summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Doucet <todd.doucet@timesys.com>2010-02-03 17:06:33 -0500
committerTodd Doucet <todd.doucet@timesys.com>2010-02-03 17:06:33 -0500
commitff238a4df84428befc55d49f58864dfc47ff853d (patch)
tree8796b828b396f5b7ed017904ff77b614dbf38677
parent4a6908a3a050aacc9c3a2f36b276b46c0629ad91 (diff)
Kernel as received from Digi for their Wi-Mx51 SoC running on their
CCWMX51JS carrier board.
-rw-r--r--Documentation/imx_nfc.txt369
-rw-r--r--Documentation/pwm.txt257
-rw-r--r--arch/arm/Kconfig61
-rw-r--r--arch/arm/Makefile12
-rw-r--r--arch/arm/boot/.gitignore2
-rw-r--r--arch/arm/boot/Makefile14
-rw-r--r--arch/arm/configs/imx233_defconfig1688
-rw-r--r--arch/arm/configs/imx233_updater_defconfig1029
-rw-r--r--arch/arm/configs/imx25_3stack_defconfig1725
-rw-r--r--arch/arm/configs/imx27ads_defconfig1172
-rw-r--r--arch/arm/configs/imx31_3stack_defconfig1835
-rw-r--r--arch/arm/configs/imx31ads_defconfig1707
-rw-r--r--arch/arm/configs/imx35_3stack_defconfig1813
-rw-r--r--arch/arm/configs/imx35evb_defconfig976
-rw-r--r--arch/arm/configs/imx37_3stack_defconfig1828
-rw-r--r--arch/arm/configs/imx51_3stack_defconfig1805
-rw-r--r--arch/arm/configs/imx51_defconfig1954
-rw-r--r--arch/arm/configs/ns9xxx_defconfig2
-rw-r--r--arch/arm/include/asm/cacheflush.h8
-rw-r--r--arch/arm/include/asm/hardware/cache-l2x0.h2
-rw-r--r--arch/arm/include/asm/ide.h5
-rw-r--r--arch/arm/include/asm/mach/keypad.h28
-rw-r--r--arch/arm/include/asm/pgtable.h15
-rw-r--r--arch/arm/include/asm/unistd.h21
-rw-r--r--arch/arm/mach-mx2/devices.c3
-rw-r--r--arch/arm/mach-mx25/Kconfig96
-rw-r--r--arch/arm/mach-mx25/Makefile18
-rw-r--r--arch/arm/mach-mx25/Makefile.boot3
-rw-r--r--arch/arm/mach-mx25/board-mx25_3stack.h173
-rw-r--r--arch/arm/mach-mx25/bus_freq.c102
-rw-r--r--arch/arm/mach-mx25/clock.c1737
-rw-r--r--arch/arm/mach-mx25/cpu.c59
-rw-r--r--arch/arm/mach-mx25/crm_regs.h215
-rw-r--r--arch/arm/mach-mx25/devices.c615
-rw-r--r--arch/arm/mach-mx25/dma.c663
-rw-r--r--arch/arm/mach-mx25/iomux.c215
-rw-r--r--arch/arm/mach-mx25/iomux.h233
-rw-r--r--arch/arm/mach-mx25/mm.c82
-rw-r--r--arch/arm/mach-mx25/mx25_3stack.c708
-rw-r--r--arch/arm/mach-mx25/mx25_3stack_cpld.c240
-rw-r--r--arch/arm/mach-mx25/mx25_3stack_gpio.c1367
-rw-r--r--arch/arm/mach-mx25/mx25_3stack_pmic_mc34704.c127
-rw-r--r--arch/arm/mach-mx25/mx25_pins.h250
-rw-r--r--arch/arm/mach-mx25/pm.c102
-rw-r--r--arch/arm/mach-mx25/sdma_script_code.h159
-rw-r--r--arch/arm/mach-mx25/serial.c263
-rw-r--r--arch/arm/mach-mx25/serial.h158
-rw-r--r--arch/arm/mach-mx25/system.c151
-rw-r--r--arch/arm/mach-mx25/usb.h103
-rw-r--r--arch/arm/mach-mx25/usb_dr.c101
-rw-r--r--arch/arm/mach-mx25/usb_h2.c87
-rw-r--r--arch/arm/mach-mx27/Kconfig43
-rw-r--r--arch/arm/mach-mx27/Makefile18
-rw-r--r--arch/arm/mach-mx27/Makefile.boot3
-rw-r--r--arch/arm/mach-mx27/board-mx27ads.h385
-rw-r--r--arch/arm/mach-mx27/clock.c1530
-rw-r--r--arch/arm/mach-mx27/cpu.c33
-rw-r--r--arch/arm/mach-mx27/crm_regs.h284
-rw-r--r--arch/arm/mach-mx27/devices.c709
-rw-r--r--arch/arm/mach-mx27/dma.c553
-rw-r--r--arch/arm/mach-mx27/dptc.c49
-rw-r--r--arch/arm/mach-mx27/gpio_mux.c308
-rw-r--r--arch/arm/mach-mx27/gpio_mux.h80
-rw-r--r--arch/arm/mach-mx27/mm.c62
-rw-r--r--arch/arm/mach-mx27/mx27_pins.h207
-rw-r--r--arch/arm/mach-mx27/mx27ads.c833
-rw-r--r--arch/arm/mach-mx27/mx27ads_gpio.c1266
-rw-r--r--arch/arm/mach-mx27/mxc_pm.c459
-rw-r--r--arch/arm/mach-mx27/pm.c102
-rw-r--r--arch/arm/mach-mx27/serial.c275
-rw-r--r--arch/arm/mach-mx27/serial.h170
-rw-r--r--arch/arm/mach-mx27/system.c60
-rw-r--r--arch/arm/mach-mx27/time.c240
-rw-r--r--arch/arm/mach-mx27/usb.h116
-rw-r--r--arch/arm/mach-mx27/usb_dr.c126
-rw-r--r--arch/arm/mach-mx27/usb_h1.c53
-rw-r--r--arch/arm/mach-mx27/usb_h2.c53
-rw-r--r--arch/arm/mach-mx3/Kconfig92
-rw-r--r--arch/arm/mach-mx3/Makefile16
-rw-r--r--arch/arm/mach-mx3/board-mx31ads.h326
-rw-r--r--arch/arm/mach-mx3/board-mx3_3stack.h152
-rw-r--r--arch/arm/mach-mx3/clock.c446
-rw-r--r--arch/arm/mach-mx3/cpu.c78
-rw-r--r--arch/arm/mach-mx3/crm_regs.h9
-rw-r--r--arch/arm/mach-mx3/devices.c978
-rw-r--r--arch/arm/mach-mx3/dma.c745
-rw-r--r--arch/arm/mach-mx3/dptc.c103
-rw-r--r--arch/arm/mach-mx3/dvfs_v2.c535
-rw-r--r--arch/arm/mach-mx3/iomux.c267
-rw-r--r--arch/arm/mach-mx3/iomux.h186
-rw-r--r--arch/arm/mach-mx3/mm.c54
-rw-r--r--arch/arm/mach-mx3/mx31_pins.h429
-rw-r--r--arch/arm/mach-mx3/mx31ads.c1117
-rw-r--r--arch/arm/mach-mx3/mx31ads_gpio.c1561
-rw-r--r--arch/arm/mach-mx3/mx3_3stack.c1069
-rw-r--r--arch/arm/mach-mx3/mx3_3stack_gpio.c1311
-rw-r--r--arch/arm/mach-mx3/mx3_3stack_pmic_mc13783.c279
-rw-r--r--arch/arm/mach-mx3/mxc_pm.c440
-rw-r--r--arch/arm/mach-mx3/pm.c103
-rw-r--r--arch/arm/mach-mx3/sdma_script_code.h581
-rw-r--r--arch/arm/mach-mx3/sdma_script_code_pass2.h434
-rw-r--r--arch/arm/mach-mx3/serial.c267
-rw-r--r--arch/arm/mach-mx3/serial.h158
-rw-r--r--arch/arm/mach-mx3/system.c103
-rw-r--r--arch/arm/mach-mx3/usb.h116
-rw-r--r--arch/arm/mach-mx3/usb_dr.c128
-rw-r--r--arch/arm/mach-mx3/usb_h1.c53
-rw-r--r--arch/arm/mach-mx3/usb_h2.c69
-rw-r--r--arch/arm/mach-mx35/Kconfig111
-rw-r--r--arch/arm/mach-mx35/Makefile20
-rw-r--r--arch/arm/mach-mx35/Makefile.boot9
-rw-r--r--arch/arm/mach-mx35/board-mx35_3stack.h200
-rw-r--r--arch/arm/mach-mx35/board-mx35evb.h144
-rw-r--r--arch/arm/mach-mx35/clock.c1927
-rw-r--r--arch/arm/mach-mx35/cpu.c82
-rw-r--r--arch/arm/mach-mx35/crm_regs.h430
-rw-r--r--arch/arm/mach-mx35/devices.c804
-rw-r--r--arch/arm/mach-mx35/dma.c1046
-rw-r--r--arch/arm/mach-mx35/dvfs.c606
-rw-r--r--arch/arm/mach-mx35/iomux.c217
-rw-r--r--arch/arm/mach-mx35/iomux.h295
-rw-r--r--arch/arm/mach-mx35/mm.c77
-rw-r--r--arch/arm/mach-mx35/mx35_3stack.c1224
-rw-r--r--arch/arm/mach-mx35/mx35_3stack_cpld.c161
-rw-r--r--arch/arm/mach-mx35/mx35_3stack_gpio.c1380
-rw-r--r--arch/arm/mach-mx35/mx35_3stack_irq.c372
-rw-r--r--arch/arm/mach-mx35/mx35_3stack_pmic_mc13892.c345
-rw-r--r--arch/arm/mach-mx35/mx35_3stack_pmic_mc9s08dz60.c105
-rw-r--r--arch/arm/mach-mx35/mx35_pins.h333
-rw-r--r--arch/arm/mach-mx35/mx35evb.c396
-rw-r--r--arch/arm/mach-mx35/mx35evb_cpld.c82
-rw-r--r--arch/arm/mach-mx35/mx35evb_gpio.c276
-rw-r--r--arch/arm/mach-mx35/pm.c79
-rw-r--r--arch/arm/mach-mx35/sdma_script_code.h254
-rw-r--r--arch/arm/mach-mx35/sdma_script_code_v2.h234
-rw-r--r--arch/arm/mach-mx35/serial.c180
-rw-r--r--arch/arm/mach-mx35/serial.h132
-rw-r--r--arch/arm/mach-mx35/system.c126
-rw-r--r--arch/arm/mach-mx35/usb.h104
-rw-r--r--arch/arm/mach-mx35/usb_dr.c109
-rw-r--r--arch/arm/mach-mx35/usb_h2.c62
-rw-r--r--arch/arm/mach-mx37/Kconfig91
-rw-r--r--arch/arm/mach-mx37/Makefile19
-rw-r--r--arch/arm/mach-mx37/Makefile.boot3
-rw-r--r--arch/arm/mach-mx37/board-mx37_3stack.h117
-rw-r--r--arch/arm/mach-mx37/bus_freq.c459
-rw-r--r--arch/arm/mach-mx37/clock.c3173
-rw-r--r--arch/arm/mach-mx37/cpu.c79
-rw-r--r--arch/arm/mach-mx37/crm_regs.h617
-rw-r--r--arch/arm/mach-mx37/devices.c1027
-rw-r--r--arch/arm/mach-mx37/dma.c666
-rw-r--r--arch/arm/mach-mx37/dptc.c69
-rw-r--r--arch/arm/mach-mx37/iomux.c205
-rw-r--r--arch/arm/mach-mx37/iomux.h228
-rw-r--r--arch/arm/mach-mx37/mm.c82
-rw-r--r--arch/arm/mach-mx37/mx37_3stack.c942
-rw-r--r--arch/arm/mach-mx37/mx37_3stack_cpld.c231
-rw-r--r--arch/arm/mach-mx37/mx37_3stack_gpio.c1028
-rw-r--r--arch/arm/mach-mx37/mx37_3stack_pmic_wm8350.c349
-rw-r--r--arch/arm/mach-mx37/mx37_pins.h256
-rw-r--r--arch/arm/mach-mx37/pm.c125
-rw-r--r--arch/arm/mach-mx37/sdma_script_code.h203
-rw-r--r--arch/arm/mach-mx37/serial.c169
-rw-r--r--arch/arm/mach-mx37/serial.h127
-rw-r--r--arch/arm/mach-mx37/system.c170
-rw-r--r--arch/arm/mach-mx37/usb.h112
-rw-r--r--arch/arm/mach-mx37/usb_dr.c164
-rw-r--r--arch/arm/mach-mx51/Kconfig146
-rw-r--r--arch/arm/mach-mx51/Makefile23
-rw-r--r--arch/arm/mach-mx51/Makefile.boot3
-rw-r--r--arch/arm/mach-mx51/board-ccwmx51.h56
-rw-r--r--arch/arm/mach-mx51/board-mx51_3stack.h125
-rw-r--r--arch/arm/mach-mx51/board-mx51_babbage.h85
-rw-r--r--arch/arm/mach-mx51/board.h24
-rw-r--r--arch/arm/mach-mx51/bus_freq.c579
-rw-r--r--arch/arm/mach-mx51/clock.c3898
-rw-r--r--arch/arm/mach-mx51/cpu.c65
-rw-r--r--arch/arm/mach-mx51/crm_regs.h690
-rw-r--r--arch/arm/mach-mx51/devices.c1284
-rw-r--r--arch/arm/mach-mx51/dma.c666
-rw-r--r--arch/arm/mach-mx51/dummy_gpio.c108
-rw-r--r--arch/arm/mach-mx51/iomux.c248
-rw-r--r--arch/arm/mach-mx51/iomux.h246
-rw-r--r--arch/arm/mach-mx51/lpmodes.c309
-rw-r--r--arch/arm/mach-mx51/mm.c84
-rw-r--r--arch/arm/mach-mx51/mx51_3stack.c1308
-rw-r--r--arch/arm/mach-mx51/mx51_3stack_gpio.c852
-rw-r--r--arch/arm/mach-mx51/mx51_3stack_pmic_mc13892.c352
-rw-r--r--arch/arm/mach-mx51/mx51_babbage.c1069
-rw-r--r--arch/arm/mach-mx51/mx51_babbage_gpio.c803
-rw-r--r--arch/arm/mach-mx51/mx51_babbage_pmic_mc13892.c363
-rw-r--r--arch/arm/mach-mx51/mx51_ccwmx51js.c566
-rw-r--r--arch/arm/mach-mx51/mx51_ccwmx51js_gpio.c528
-rw-r--r--arch/arm/mach-mx51/mx51_ccwmx51js_pmic_mc13892.c361
-rw-r--r--arch/arm/mach-mx51/mx51_pins.h361
-rw-r--r--arch/arm/mach-mx51/pm.c222
-rw-r--r--arch/arm/mach-mx51/sdma_script_code.h170
-rw-r--r--arch/arm/mach-mx51/sdram_autogating.c164
-rw-r--r--arch/arm/mach-mx51/serial.c178
-rw-r--r--arch/arm/mach-mx51/serial.h127
-rw-r--r--arch/arm/mach-mx51/suspend.S155
-rw-r--r--arch/arm/mach-mx51/system.c187
-rw-r--r--arch/arm/mach-mx51/usb.h112
-rw-r--r--arch/arm/mach-mx51/usb_dr.c160
-rw-r--r--arch/arm/mach-mx51/usb_h1.c114
-rw-r--r--arch/arm/mach-mx51/usb_h2.c91
-rw-r--r--arch/arm/mach-mx51/wfi.S427
-rw-r--r--arch/arm/mach-ns9xxx/Kconfig604
-rw-r--r--arch/arm/mach-ns9xxx/Makefile45
-rw-r--r--arch/arm/mach-ns9xxx/cc9p9215_devices.c681
-rw-r--r--arch/arm/mach-ns9xxx/cc9p9215_devices.h45
-rw-r--r--arch/arm/mach-ns9xxx/cc9p9360_devices.c232
-rw-r--r--arch/arm/mach-ns9xxx/cc9p9360_devices.h32
-rw-r--r--arch/arm/mach-ns9xxx/ccw9p9215_devices.c379
-rw-r--r--arch/arm/mach-ns9xxx/ccw9p9215_devices.h12
-rw-r--r--arch/arm/mach-ns9xxx/ccx9c_devices.c218
-rw-r--r--arch/arm/mach-ns9xxx/ccx9c_devices.h32
-rw-r--r--arch/arm/mach-ns9xxx/clock.c128
-rw-r--r--arch/arm/mach-ns9xxx/clock.h3
-rw-r--r--arch/arm/mach-ns9xxx/cme9210_devices.c251
-rw-r--r--arch/arm/mach-ns9xxx/cme9210_devices.h26
-rw-r--r--arch/arm/mach-ns9xxx/gpio-ns921x.c41
-rw-r--r--arch/arm/mach-ns9xxx/gpio-ns9360.c122
-rw-r--r--arch/arm/mach-ns9xxx/gpio.c119
-rw-r--r--arch/arm/mach-ns9xxx/gpiolib-ns921x.c76
-rw-r--r--arch/arm/mach-ns9xxx/gpiolib-ns921x.h12
-rw-r--r--arch/arm/mach-ns9xxx/gpiolib-ns9360.c59
-rw-r--r--arch/arm/mach-ns9xxx/gpiolib-ns9360.h12
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/board.h8
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/display/CUSTOM.h18
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/display/Kconfig36
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/display/LQ057Q3DC12I.h27
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/display/LQ064V3DG01.h28
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/display/VGA.h29
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/display/displays.h46
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/dma-ns921x.h140
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/fim-firmware.h101
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/fim-ns921x.h466
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/fim-uncompress.h93
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/gpio.h470
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/hardware.h8
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/irqs.h94
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/module.h10
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/ns921x-serial.h22
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/ns9360fb.h26
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/ns9xxx-pwm.h146
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/processor.h2
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/regs-bbu.h69
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/regs-io-ns921x.h41
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/regs-iohub-ns921x.h133
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/regs-lcd-ns9360.h88
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/regs-sys-common.h47
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/regs-sys-ns921x.h154
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/regs-sys-ns9360.h37
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/spi.h28
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/system.h23
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/timex.h2
-rw-r--r--arch/arm/mach-ns9xxx/include/mach/uncompress.h185
-rw-r--r--arch/arm/mach-ns9xxx/irq-ns921x.c57
-rw-r--r--arch/arm/mach-ns9xxx/irq.c326
-rw-r--r--arch/arm/mach-ns9xxx/irq.h16
-rw-r--r--arch/arm/mach-ns9xxx/leds.c64
-rw-r--r--arch/arm/mach-ns9xxx/mach-cc7ucamry.c23
-rw-r--r--arch/arm/mach-ns9xxx/mach-cc9c.c176
-rw-r--r--arch/arm/mach-ns9xxx/mach-cc9p9215.c23
-rw-r--r--arch/arm/mach-ns9xxx/mach-cc9p9215js.c192
-rw-r--r--arch/arm/mach-ns9xxx/mach-cc9p9360js.c187
-rw-r--r--arch/arm/mach-ns9xxx/mach-ccw9c.c176
-rw-r--r--arch/arm/mach-ns9xxx/mach-ccw9p9215.c23
-rw-r--r--arch/arm/mach-ns9xxx/mach-ccw9p9215js.c242
-rw-r--r--arch/arm/mach-ns9xxx/mach-cme9210.c24
-rw-r--r--arch/arm/mach-ns9xxx/mach-cme9210js.c109
-rw-r--r--arch/arm/mach-ns9xxx/mach-inc20otter.c23
-rw-r--r--arch/arm/mach-ns9xxx/mach-otter.c23
-rw-r--r--arch/arm/mach-ns9xxx/ns9215_devices.c299
-rw-r--r--arch/arm/mach-ns9xxx/ns9215_devices.h14
-rw-r--r--arch/arm/mach-ns9xxx/ns921x_devices.c798
-rw-r--r--arch/arm/mach-ns9xxx/ns921x_devices.h37
-rw-r--r--arch/arm/mach-ns9xxx/ns9360_devices.c813
-rw-r--r--arch/arm/mach-ns9xxx/ns9360_devices.h32
-rw-r--r--arch/arm/mach-ns9xxx/pm-ns921x.c173
-rw-r--r--arch/arm/mach-ns9xxx/processor-ns921x.c379
-rw-r--r--arch/arm/mach-ns9xxx/processor-ns921x.h44
-rw-r--r--arch/arm/mach-ns9xxx/processor-ns9360.c75
-rw-r--r--arch/arm/mach-ns9xxx/processor-ns9360.h30
-rw-r--r--arch/arm/mach-ns9xxx/time-ns921x.c195
-rw-r--r--arch/arm/mach-ns9xxx/time-ns9360.c5
-rw-r--r--arch/arm/mach-s3c2410/include/mach/dma.h1
-rw-r--r--arch/arm/mach-s3c2410/include/mach/fb.h50
-rw-r--r--arch/arm/mach-s3c2410/include/mach/gpio.h22
-rw-r--r--arch/arm/mach-s3c2410/include/mach/io.h138
-rw-r--r--arch/arm/mach-s3c2410/include/mach/irqs.h6
-rw-r--r--arch/arm/mach-s3c2410/include/mach/map.h32
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-bus.h29
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-cfata.h108
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-gpio.h41
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-gpioj.h118
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-hsmmc.h45
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-lcd.h83
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h55
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-s3c2443-mem.h150
-rw-r--r--arch/arm/mach-s3c2410/include/mach/s3c2443-fb.h71
-rw-r--r--arch/arm/mach-s3c2410/include/mach/usb-control.h15
-rw-r--r--arch/arm/mach-s3c2443/Kconfig83
-rw-r--r--arch/arm/mach-s3c2443/Makefile14
-rw-r--r--arch/arm/mach-s3c2443/cc9m2443-custom.h31
-rw-r--r--arch/arm/mach-s3c2443/cc9m2443js-pm.c221
-rw-r--r--arch/arm/mach-s3c2443/cc9m2443js-pm.h31
-rw-r--r--arch/arm/mach-s3c2443/cc9m2443js-sleep.S168
-rw-r--r--arch/arm/mach-s3c2443/cc9m2443js-usb.c195
-rw-r--r--arch/arm/mach-s3c2443/cc9m2443js-usb.h22
-rw-r--r--arch/arm/mach-s3c2443/displays/CUSTOM.h37
-rw-r--r--arch/arm/mach-s3c2443/displays/Kconfig32
-rw-r--r--arch/arm/mach-s3c2443/displays/LQ057Q3DC12I.h38
-rw-r--r--arch/arm/mach-s3c2443/displays/LQ064V3DG01.h38
-rw-r--r--arch/arm/mach-s3c2443/displays/VGA.h31
-rw-r--r--arch/arm/mach-s3c2443/displays/displays.h46
-rw-r--r--arch/arm/mach-s3c2443/dma.c6
-rw-r--r--arch/arm/mach-s3c2443/gpio.c356
-rw-r--r--arch/arm/mach-s3c2443/irq.c8
-rw-r--r--arch/arm/mach-s3c2443/mach-cc9m2443js.c726
-rw-r--r--arch/arm/mach-s3c2443/mach-ccw9m2443js.c774
-rw-r--r--arch/arm/mach-s3c2443/piper-ccw9m2443.c429
-rw-r--r--arch/arm/mach-s3c2443/piper-ccw9m2443.h22
-rw-r--r--arch/arm/mach-s3c2443/s3c2443.c10
-rw-r--r--arch/arm/mach-stmp3xxx/Kconfig102
-rw-r--r--arch/arm/mach-stmp3xxx/Makefile47
-rw-r--r--arch/arm/mach-stmp3xxx/Makefile.boot3
-rw-r--r--arch/arm/mach-stmp3xxx/clock.c1376
-rw-r--r--arch/arm/mach-stmp3xxx/clock.h86
-rw-r--r--arch/arm/mach-stmp3xxx/common.h65
-rw-r--r--arch/arm/mach-stmp3xxx/core.c151
-rw-r--r--arch/arm/mach-stmp3xxx/cpufreq.c476
-rw-r--r--arch/arm/mach-stmp3xxx/dcp-bootstream.c298
-rw-r--r--arch/arm/mach-stmp3xxx/devices.c592
-rw-r--r--arch/arm/mach-stmp3xxx/dma.c509
-rw-r--r--arch/arm/mach-stmp3xxx/emi.S150
-rw-r--r--arch/arm/mach-stmp3xxx/emi.inc708
-rw-r--r--arch/arm/mach-stmp3xxx/gpio.c110
-rw-r--r--arch/arm/mach-stmp3xxx/gpmi.c40
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/arc_otg.h97
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/cpu.h33
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/dcp_bootstream_ioctl.h32
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/ddi_bc.h736
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/debug-macro.S38
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/dma.h161
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/entry-macro.S35
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/fsl_usb.h60
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/fsl_usb_gadget.h40
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/gpio.h29
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/gpmi.h45
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/hardware.h32
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/i2c.h48
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/io.h29
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/irqs.h94
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/lcdif.h430
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/lradc.h60
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/memory.h65
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/ocram-malloc.h26
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/pins.h177
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/platform.h31
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/power.h67
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/pwm-led.h26
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-apbh.h300
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-apbx.h377
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-audioin.h161
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-audioout.h279
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-bch.h447
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-clkctrl.h275
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-dcp.h481
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-digctl.h451
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-dram.h454
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-dri.h160
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-ecc8.h279
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-emi.h175
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-gpmi.h329
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-i2c.h306
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-icoll.h212
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-ir.h280
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-lcdif.h497
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-lradc.h520
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-ocotp.h166
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-pinctrl.h1255
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-power.h349
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-pwm.h113
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-pxp.h406
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-rtc.h149
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-saif.h94
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-spdif.h100
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-ssp.h355
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-sydma.h117
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-timrot.h215
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-tvenc.h337
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-uartapp.h238
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-uartdbg.h287
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-usbctrl.h642
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regs-usbphy.h193
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/regulator.h23
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/rotdec.h26
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/smp.h30
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/stmp3xxx.h94
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/stmp3xxx_regs.h201
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/system.h47
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/timex.h20
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/uncompress.h54
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/unique-id.h30
-rw-r--r--arch/arm/mach-stmp3xxx/include/mach/vmalloc.h17
-rw-r--r--arch/arm/mach-stmp3xxx/irq.c86
-rw-r--r--arch/arm/mach-stmp3xxx/lcd_hx8238a.c367
-rw-r--r--arch/arm/mach-stmp3xxx/lcd_lms350.c537
-rw-r--r--arch/arm/mach-stmp3xxx/lcd_lms430.c376
-rw-r--r--arch/arm/mach-stmp3xxx/lradc.c294
-rw-r--r--arch/arm/mach-stmp3xxx/mmc.c161
-rw-r--r--arch/arm/mach-stmp3xxx/otp.c434
-rw-r--r--arch/arm/mach-stmp3xxx/persistent.c257
-rw-r--r--arch/arm/mach-stmp3xxx/pinmux.c614
-rw-r--r--arch/arm/mach-stmp3xxx/pinmux.h161
-rw-r--r--arch/arm/mach-stmp3xxx/pm.c592
-rw-r--r--arch/arm/mach-stmp3xxx/power-test.c212
-rw-r--r--arch/arm/mach-stmp3xxx/power.c531
-rw-r--r--arch/arm/mach-stmp3xxx/rotdec.c39
-rw-r--r--arch/arm/mach-stmp3xxx/sleep.S540
-rw-r--r--arch/arm/mach-stmp3xxx/sleep.h113
-rw-r--r--arch/arm/mach-stmp3xxx/spdif.c36
-rw-r--r--arch/arm/mach-stmp3xxx/spi.c106
-rw-r--r--arch/arm/mach-stmp3xxx/stmp378x.c419
-rw-r--r--arch/arm/mach-stmp3xxx/stmp378x_devb.c359
-rw-r--r--arch/arm/mach-stmp3xxx/stmp378x_devb_rotdec.c47
-rw-r--r--arch/arm/mach-stmp3xxx/stmp378x_i2c.c275
-rw-r--r--arch/arm/mach-stmp3xxx/stmp378x_lcdif.c188
-rw-r--r--arch/arm/mach-stmp3xxx/stmp378x_pwm_led.c54
-rw-r--r--arch/arm/mach-stmp3xxx/stmp37xx.c368
-rw-r--r--arch/arm/mach-stmp3xxx/timer.c188
-rw-r--r--arch/arm/mach-stmp3xxx/tvenc.c272
-rw-r--r--arch/arm/mach-stmp3xxx/unique-id.c199
-rw-r--r--arch/arm/mach-stmp3xxx/usb.h103
-rw-r--r--arch/arm/mach-stmp3xxx/usb_common.c306
-rw-r--r--arch/arm/mach-stmp3xxx/usb_dr.c112
-rw-r--r--arch/arm/mach-stmp3xxx/utmixc.c82
-rw-r--r--arch/arm/mm/Kconfig7
-rw-r--r--arch/arm/mm/cache-l2x0.c82
-rw-r--r--arch/arm/mm/cache-v6.S41
-rw-r--r--arch/arm/mm/flush.c22
-rw-r--r--arch/arm/mm/proc-macros.S4
-rw-r--r--arch/arm/mm/proc-v6.S16
-rw-r--r--arch/arm/oprofile/Makefile1
-rw-r--r--arch/arm/oprofile/evtmon_regs.h84
-rw-r--r--arch/arm/oprofile/op_model_arm11.c477
-rw-r--r--arch/arm/oprofile/op_model_arm11_core.c44
-rw-r--r--arch/arm/oprofile/op_model_arm11_core.h13
-rw-r--r--arch/arm/oprofile/op_model_arm11_evtmon.c188
-rw-r--r--arch/arm/oprofile/op_model_v6.c19
-rw-r--r--arch/arm/oprofile/op_model_v7.c3
-rw-r--r--arch/arm/plat-mxc/Kconfig155
-rw-r--r--arch/arm/plat-mxc/Makefile55
-rw-r--r--arch/arm/plat-mxc/clock.c343
-rw-r--r--arch/arm/plat-mxc/cpu_common.c85
-rw-r--r--arch/arm/plat-mxc/cpufreq.c330
-rw-r--r--arch/arm/plat-mxc/dma_mx2.c1316
-rw-r--r--arch/arm/plat-mxc/dptc.c621
-rw-r--r--arch/arm/plat-mxc/dvfs_core.c925
-rw-r--r--arch/arm/plat-mxc/entry-pm.S315
-rw-r--r--arch/arm/plat-mxc/gpio.c218
-rw-r--r--arch/arm/plat-mxc/include/mach/arc_otg.h357
-rw-r--r--arch/arm/plat-mxc/include/mach/audio_controls.h220
-rw-r--r--arch/arm/plat-mxc/include/mach/clock.h19
-rw-r--r--arch/arm/plat-mxc/include/mach/common.h6
-rw-r--r--arch/arm/plat-mxc/include/mach/dma.h279
-rw-r--r--arch/arm/plat-mxc/include/mach/dptc.h186
-rw-r--r--arch/arm/plat-mxc/include/mach/dvfs_dptc_struct.h169
-rw-r--r--arch/arm/plat-mxc/include/mach/entry-macro.S35
-rw-r--r--arch/arm/plat-mxc/include/mach/fsl_usb.h89
-rw-r--r--arch/arm/plat-mxc/include/mach/fsl_usb_gadget.h40
-rw-r--r--arch/arm/plat-mxc/include/mach/gpio.h10
-rw-r--r--arch/arm/plat-mxc/include/mach/hardware.h165
-rw-r--r--arch/arm/plat-mxc/include/mach/hw_events.h65
-rw-r--r--arch/arm/plat-mxc/include/mach/io.h45
-rw-r--r--arch/arm/plat-mxc/include/mach/irqs.h22
-rw-r--r--arch/arm/plat-mxc/include/mach/memory.h54
-rw-r--r--arch/arm/plat-mxc/include/mach/mmc.h35
-rw-r--r--arch/arm/plat-mxc/include/mach/mtd-xip.h52
-rw-r--r--arch/arm/plat-mxc/include/mach/mx21.h317
-rw-r--r--arch/arm/plat-mxc/include/mach/mx25.h510
-rw-r--r--arch/arm/plat-mxc/include/mach/mx27.h118
-rw-r--r--arch/arm/plat-mxc/include/mach/mx2_dma.h261
-rw-r--r--arch/arm/plat-mxc/include/mach/mx31.h252
-rw-r--r--arch/arm/plat-mxc/include/mach/mx35.h480
-rw-r--r--arch/arm/plat-mxc/include/mach/mx37.h525
-rw-r--r--arch/arm/plat-mxc/include/mach/mx51.h535
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc.h503
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_dptc.h111
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_dvfs.h78
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_edid.h33
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_gpc.h74
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_pm.h252
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_scc.h45
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_timer.h9
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_uart.h275
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_vpu.h94
-rw-r--r--arch/arm/plat-mxc/include/mach/pcmcia.h218
-rw-r--r--arch/arm/plat-mxc/include/mach/pmic_audio.h2315
-rw-r--r--arch/arm/plat-mxc/include/mach/pmic_convity.h873
-rw-r--r--arch/arm/plat-mxc/include/mach/pmic_power.h1358
-rw-r--r--arch/arm/plat-mxc/include/mach/sdma.h560
-rw-r--r--arch/arm/plat-mxc/include/mach/spba.h66
-rw-r--r--arch/arm/plat-mxc/include/mach/system.h23
-rw-r--r--arch/arm/plat-mxc/include/mach/uncompress.h2
-rw-r--r--arch/arm/plat-mxc/io.c41
-rw-r--r--arch/arm/plat-mxc/irq.c164
-rw-r--r--arch/arm/plat-mxc/isp1301xc.c290
-rw-r--r--arch/arm/plat-mxc/isp1504xc.c279
-rw-r--r--arch/arm/plat-mxc/leds.c111
-rw-r--r--arch/arm/plat-mxc/mc13783_xc.c299
-rw-r--r--arch/arm/plat-mxc/pwm.c300
-rw-r--r--arch/arm/plat-mxc/sdma/Makefile18
-rw-r--r--arch/arm/plat-mxc/sdma/dma_sdma.c697
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/Makefile5
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/epm.h187
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/iapi.h49
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/iapiDefaults.h128
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/iapiHigh.h136
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/iapiLow.h78
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/iapiLowDsp.h50
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/iapiLowMcu.h60
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/iapiMiddle.h52
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/iapiMiddleMcu.h41
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/iapiOS.h96
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/include/sdmaStruct.h426
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/src/Makefile18
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/src/iapiDefaults.c110
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/src/iapiHigh.c2798
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/src/iapiLow.c150
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/src/iapiLowDsp.c79
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/src/iapiLowMcu.c518
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/src/iapiMiddle.c623
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/src/iapiMiddleMcu.c52
-rw-r--r--arch/arm/plat-mxc/sdma/iapi/src/iapiOS.c64
-rw-r--r--arch/arm/plat-mxc/sdma/sdma.c1481
-rw-r--r--arch/arm/plat-mxc/sdma/sdma_malloc.c388
-rw-r--r--arch/arm/plat-mxc/serialxc.c64
-rw-r--r--arch/arm/plat-mxc/snoop.c133
-rw-r--r--arch/arm/plat-mxc/spba.c133
-rw-r--r--arch/arm/plat-mxc/tzic.c179
-rw-r--r--arch/arm/plat-mxc/usb_common.c898
-rw-r--r--arch/arm/plat-mxc/utmixc.c106
-rw-r--r--arch/arm/plat-mxc/wdog.c67
-rw-r--r--arch/arm/plat-s3c/Kconfig1
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-adc.h1
-rw-r--r--arch/arm/plat-s3c/include/plat/regs-serial.h17
-rw-r--r--arch/arm/plat-s3c/include/plat/uncompress.h32
-rw-r--r--arch/arm/plat-s3c24xx/Kconfig8
-rw-r--r--arch/arm/plat-s3c24xx/Makefile2
-rw-r--r--arch/arm/plat-s3c24xx/cpu.c2
-rw-r--r--arch/arm/plat-s3c24xx/devs.c27
-rw-r--r--arch/arm/plat-s3c24xx/dma.c111
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/hsmmc.h30
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/pcmcia.h25
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/ts.h33
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/udc.h36
-rw-r--r--arch/arm/plat-s3c24xx/irq.c52
-rw-r--r--arch/arm/plat-s3c24xx/pm.c336
-rw-r--r--arch/arm/tools/mach-types729
-rw-r--r--crypto/Kconfig10
-rw-r--r--crypto/Makefile7
-rw-r--r--crypto/cryptodev.c558
-rw-r--r--crypto/ocf/ChangeLog1960
-rw-r--r--crypto/ocf/Config.in34
-rw-r--r--crypto/ocf/Kconfig101
-rw-r--r--crypto/ocf/Makefile121
-rw-r--r--crypto/ocf/README167
-rw-r--r--crypto/ocf/criov.c215
-rw-r--r--crypto/ocf/crypto.c1748
-rw-r--r--crypto/ocf/cryptodev.c1048
-rw-r--r--crypto/ocf/cryptodev.h478
-rw-r--r--crypto/ocf/cryptosoft.c1075
-rw-r--r--crypto/ocf/ep80579/Makefile107
-rw-r--r--crypto/ocf/ep80579/environment.mk75
-rw-r--r--crypto/ocf/ep80579/icp_asym.c1375
-rw-r--r--crypto/ocf/ep80579/icp_common.c891
-rw-r--r--crypto/ocf/ep80579/icp_ocf.h363
-rw-r--r--crypto/ocf/ep80579/icp_sym.c1382
-rw-r--r--crypto/ocf/ep80579/linux_2.6_kernel_space.mk69
-rw-r--r--crypto/ocf/hifn/Makefile13
-rw-r--r--crypto/ocf/hifn/hifn7751.c2970
-rw-r--r--crypto/ocf/hifn/hifn7751reg.h540
-rw-r--r--crypto/ocf/hifn/hifn7751var.h369
-rw-r--r--crypto/ocf/hifn/hifnHIPP.c420
-rw-r--r--crypto/ocf/hifn/hifnHIPPreg.h46
-rw-r--r--crypto/ocf/hifn/hifnHIPPvar.h93
-rw-r--r--crypto/ocf/ixp4xx/Makefile104
-rw-r--r--crypto/ocf/ixp4xx/ixp4xx.c1328
-rw-r--r--crypto/ocf/ocf-bench.c436
-rw-r--r--crypto/ocf/ocf-compat.h270
-rw-r--r--crypto/ocf/ocfnull/Makefile12
-rw-r--r--crypto/ocf/ocfnull/ocfnull.c203
-rw-r--r--crypto/ocf/pasemi/Makefile12
-rw-r--r--crypto/ocf/pasemi/pasemi.c1009
-rw-r--r--crypto/ocf/pasemi/pasemi_fnu.h410
-rw-r--r--crypto/ocf/random.c317
-rw-r--r--crypto/ocf/rndtest.c300
-rw-r--r--crypto/ocf/rndtest.h54
-rw-r--r--crypto/ocf/safe/Makefile12
-rw-r--r--crypto/ocf/safe/md5.c308
-rw-r--r--crypto/ocf/safe/md5.h76
-rw-r--r--crypto/ocf/safe/safe.c2288
-rw-r--r--crypto/ocf/safe/safereg.h421
-rw-r--r--crypto/ocf/safe/safevar.h230
-rw-r--r--crypto/ocf/safe/sha1.c279
-rw-r--r--crypto/ocf/safe/sha1.h72
-rw-r--r--crypto/ocf/talitos/Makefile12
-rw-r--r--crypto/ocf/talitos/talitos.c1359
-rw-r--r--crypto/ocf/talitos/talitos_dev.h277
-rw-r--r--crypto/ocf/talitos/talitos_soft.h77
-rw-r--r--crypto/ocf/uio.h54
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile4
-rw-r--r--drivers/ata/Kconfig9
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/pata_fsl.c1042
-rw-r--r--drivers/char/Kconfig43
-rw-r--r--drivers/char/Makefile9
-rw-r--r--drivers/char/adc-ns9215.c257
-rw-r--r--drivers/char/adc-s3c2443.c323
-rw-r--r--drivers/char/hw_random/Kconfig24
-rw-r--r--drivers/char/hw_random/Makefile2
-rw-r--r--drivers/char/hw_random/fsl-rnga.c238
-rw-r--r--drivers/char/hw_random/fsl-rngc.c372
-rw-r--r--drivers/char/imx_sim.c1495
-rw-r--r--drivers/char/keyboard.c2
-rwxr-xr-xdrivers/char/mxc_iim.c161
-rw-r--r--drivers/char/mxc_si4702.c1220
-rwxr-xr-xdrivers/char/mxs_viim.c175
-rw-r--r--drivers/char/random.c65
-rw-r--r--drivers/crypto/Kconfig20
-rw-r--r--drivers/crypto/Makefile2
-rw-r--r--drivers/crypto/ns921x-aes.c636
-rw-r--r--drivers/crypto/stmp3xxx_dcp.c1401
-rw-r--r--drivers/crypto/stmp3xxx_dcp.h68
-rw-r--r--drivers/fims/Kconfig251
-rw-r--r--drivers/fims/Makefile13
-rw-r--r--drivers/fims/can/Makefile10
-rw-r--r--drivers/fims/can/README3
-rw-r--r--drivers/fims/can/fim_can.c1483
-rw-r--r--drivers/fims/can/fim_can.h28
-rw-r--r--drivers/fims/dma.h177
-rw-r--r--drivers/fims/fim.c2216
-rw-r--r--drivers/fims/fim_reg.h279
-rw-r--r--drivers/fims/sdio/Makefile10
-rw-r--r--drivers/fims/sdio/fim_sdio.c1616
-rw-r--r--drivers/fims/sdio/fim_sdio0.h225
-rw-r--r--drivers/fims/sdio/fim_sdio0_9210.h197
-rw-r--r--drivers/fims/sdio/fim_sdio1.h225
-rw-r--r--drivers/fims/sdio/fim_sdio1_9210.h197
-rw-r--r--drivers/fims/serial/Makefile10
-rw-r--r--drivers/fims/serial/fim_serial.c1654
-rw-r--r--drivers/fims/serial/fim_serial.h155
-rw-r--r--drivers/fims/usb/Makefile10
-rw-r--r--drivers/fims/usb/README12
-rw-r--r--drivers/fims/usb/fim_usb.c2030
-rw-r--r--drivers/fims/usb/fim_usb.h185
-rw-r--r--drivers/gpio/gpiolib.c119
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/isl29003.c441
-rw-r--r--drivers/hwmon/mxc_mma7450.c788
-rw-r--r--drivers/i2c-slave/Kconfig40
-rw-r--r--drivers/i2c-slave/Makefile10
-rw-r--r--drivers/i2c-slave/i2c_slave_client.c81
-rw-r--r--drivers/i2c-slave/i2c_slave_core.c358
-rw-r--r--drivers/i2c-slave/i2c_slave_device.c270
-rw-r--r--drivers/i2c-slave/i2c_slave_device.h79
-rw-r--r--drivers/i2c-slave/i2c_slave_ring_buffer.c185
-rw-r--r--drivers/i2c-slave/i2c_slave_ring_buffer.h39
-rw-r--r--drivers/i2c-slave/mxc_i2c_slave.c329
-rw-r--r--drivers/i2c-slave/mxc_i2c_slave.h44
-rw-r--r--drivers/i2c-slave/mxc_i2c_slave_reg.h41
-rw-r--r--drivers/i2c/busses/Kconfig52
-rw-r--r--drivers/i2c/busses/Makefile4
-rw-r--r--drivers/i2c/busses/i2c-ns9xxx.c558
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c9
-rw-r--r--drivers/i2c/busses/i2c-stmp378x.c337
-rw-r--r--drivers/i2c/busses/mxc_i2c.c799
-rw-r--r--drivers/i2c/busses/mxc_i2c_hs.c549
-rw-r--r--drivers/i2c/busses/mxc_i2c_hs_reg.h97
-rw-r--r--drivers/i2c/busses/mxc_i2c_reg.h40
-rw-r--r--drivers/ide/Kconfig7
-rw-r--r--drivers/ide/Makefile1
-rw-r--r--drivers/ide/s3c2443-ide.c477
-rw-r--r--drivers/input/keyboard/Kconfig29
-rw-r--r--drivers/input/keyboard/Makefile4
-rw-r--r--drivers/input/keyboard/mc9s08dz60_keyb.c248
-rw-r--r--drivers/input/keyboard/mpr084.c508
-rw-r--r--drivers/input/keyboard/mxc_keyb.c1036
-rw-r--r--drivers/input/keyboard/mxc_keyb.h191
-rw-r--r--drivers/input/keyboard/stmp3xxx-kbd.c300
-rw-r--r--drivers/input/misc/Kconfig18
-rw-r--r--drivers/input/misc/Makefile2
-rw-r--r--drivers/input/misc/mma7455l.c647
-rw-r--r--drivers/input/misc/stmp3xxx_rotdec.c171
-rw-r--r--drivers/input/touchscreen/Kconfig53
-rw-r--r--drivers/input/touchscreen/Makefile5
-rw-r--r--drivers/input/touchscreen/ads7846.c1116
-rw-r--r--drivers/input/touchscreen/imx_adc_ts.c114
-rw-r--r--drivers/input/touchscreen/mxc_ts.c118
-rw-r--r--drivers/input/touchscreen/s3c24xx_ts.c627
-rw-r--r--drivers/input/touchscreen/stmp3xxx_ts.c386
-rw-r--r--drivers/input/touchscreen/tsc2007.c443
-rw-r--r--drivers/leds/Kconfig25
-rw-r--r--drivers/leds/Makefile4
-rw-r--r--drivers/leds/leds-mc13892.c152
-rw-r--r--drivers/leds/leds-pwm.c189
-rw-r--r--drivers/leds/leds-stmp378x-pwm.c190
-rw-r--r--drivers/leds/ledtrig-dim.c95
-rw-r--r--drivers/media/radio/Kconfig2
-rw-r--r--drivers/media/radio/Makefile2
-rw-r--r--drivers/media/radio/stfm1000/Kconfig26
-rw-r--r--drivers/media/radio/stfm1000/Makefile14
-rw-r--r--drivers/media/radio/stfm1000/gen-precalc.c62
-rw-r--r--drivers/media/radio/stfm1000/stfm1000-alsa.c660
-rw-r--r--drivers/media/radio/stfm1000/stfm1000-core.c2459
-rw-r--r--drivers/media/radio/stfm1000/stfm1000-filter.c860
-rw-r--r--drivers/media/radio/stfm1000/stfm1000-filter.h185
-rw-r--r--drivers/media/radio/stfm1000/stfm1000-i2c.c453
-rw-r--r--drivers/media/radio/stfm1000/stfm1000-rds.c1529
-rw-r--r--drivers/media/radio/stfm1000/stfm1000-rds.h364
-rw-r--r--drivers/media/radio/stfm1000/stfm1000-regs.h165
-rw-r--r--drivers/media/radio/stfm1000/stfm1000.h254
-rw-r--r--drivers/media/video/Kconfig39
-rw-r--r--drivers/media/video/Makefile8
-rw-r--r--drivers/media/video/mxc/capture/Kconfig123
-rw-r--r--drivers/media/video/mxc/capture/Makefile39
-rw-r--r--drivers/media/video/mxc/capture/adv7180.c981
-rw-r--r--drivers/media/video/mxc/capture/csi_v4l2_capture.c1024
-rw-r--r--drivers/media/video/mxc/capture/emma_mt9v111.c679
-rw-r--r--drivers/media/video/mxc/capture/emma_ov2640.c444
-rw-r--r--drivers/media/video/mxc/capture/emma_v4l2_capture.c2074
-rw-r--r--drivers/media/video/mxc/capture/fsl_csi.c276
-rw-r--r--drivers/media/video/mxc/capture/fsl_csi.h197
-rw-r--r--drivers/media/video/mxc/capture/ipu_csi_enc.c277
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_enc.c455
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_sw.h38
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_vf_adc.c601
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c411
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c413
-rw-r--r--drivers/media/video/mxc/capture/ipu_still.c252
-rw-r--r--drivers/media/video/mxc/capture/mc521da.c648
-rw-r--r--drivers/media/video/mxc/capture/mt9v111.c1076
-rw-r--r--drivers/media/video/mxc/capture/mt9v111.h431
-rw-r--r--drivers/media/video/mxc/capture/mx27_csi.c333
-rw-r--r--drivers/media/video/mxc/capture/mx27_csi.h167
-rw-r--r--drivers/media/video/mxc/capture/mx27_prp.h310
-rw-r--r--drivers/media/video/mxc/capture/mx27_prphw.c1099
-rw-r--r--drivers/media/video/mxc/capture/mx27_prpsw.c1042
-rw-r--r--drivers/media/video/mxc/capture/mxc_v4l2_capture.c2593
-rw-r--r--drivers/media/video/mxc/capture/mxc_v4l2_capture.h199
-rw-r--r--drivers/media/video/mxc/capture/ov2640.c1080
-rw-r--r--drivers/media/video/mxc/capture/ov3640.c1086
-rw-r--r--drivers/media/video/mxc/capture/sensor_clock.c87
-rw-r--r--drivers/media/video/mxc/opl/Makefile5
-rw-r--r--drivers/media/video/mxc/opl/hmirror_rotate180_u16.c259
-rw-r--r--drivers/media/video/mxc/opl/opl.h162
-rw-r--r--drivers/media/video/mxc/opl/opl_mod.c30
-rw-r--r--drivers/media/video/mxc/opl/rotate270_u16.c285
-rw-r--r--drivers/media/video/mxc/opl/rotate270_u16_qcif.S70
-rw-r--r--drivers/media/video/mxc/opl/rotate90_u16.c220
-rw-r--r--drivers/media/video/mxc/opl/rotate90_u16_qcif.S71
-rw-r--r--drivers/media/video/mxc/opl/vmirror_u16.c46
-rw-r--r--drivers/media/video/mxc/output/Kconfig28
-rw-r--r--drivers/media/video/mxc/output/Makefile11
-rw-r--r--drivers/media/video/mxc/output/mx27_pp.c904
-rw-r--r--drivers/media/video/mxc/output/mx27_pp.h180
-rw-r--r--drivers/media/video/mxc/output/mx27_v4l2_output.c1442
-rw-r--r--drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c1926
-rw-r--r--drivers/media/video/mxc/output/mxc_v4l2_output.c2602
-rw-r--r--drivers/media/video/mxc/output/mxc_v4l2_output.h146
-rw-r--r--drivers/media/video/pxp.c1318
-rw-r--r--drivers/media/video/pxp.h76
-rw-r--r--drivers/media/video/videobuf-dma-contig.c4
-rw-r--r--drivers/mfd/wm8350-core.c19
-rw-r--r--drivers/mmc/card/Kconfig12
-rw-r--r--drivers/mmc/card/Makefile1
-rw-r--r--drivers/mmc/card/block.c34
-rw-r--r--drivers/mmc/card/unifi_fs/Makefile2
-rw-r--r--drivers/mmc/card/unifi_fs/fs_lx.c684
-rw-r--r--drivers/mmc/card/unifi_fs/fs_sdio_api.h68
-rw-r--r--drivers/mmc/core/mmc.c29
-rw-r--r--drivers/mmc/host/Kconfig57
-rw-r--r--drivers/mmc/host/Makefile4
-rw-r--r--drivers/mmc/host/mx_sdhci.c2153
-rw-r--r--drivers/mmc/host/mx_sdhci.h275
-rw-r--r--drivers/mmc/host/mxc_mmc.c1530
-rw-r--r--drivers/mmc/host/mxc_mmc.h126
-rw-r--r--drivers/mmc/host/s3c-hsmmc.c1457
-rw-r--r--drivers/mmc/host/s3c-hsmmc.h237
-rw-r--r--drivers/mmc/host/s3cmci.c677
-rw-r--r--drivers/mmc/host/s3cmci.h2
-rw-r--r--drivers/mmc/host/stmp3xxx_mmc.c1077
-rw-r--r--drivers/mtd/maps/Kconfig10
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/mxc_nor.c184
-rw-r--r--drivers/mtd/nand/Kconfig103
-rw-r--r--drivers/mtd/nand/Makefile7
-rw-r--r--drivers/mtd/nand/ccx9x_nand.c267
-rw-r--r--drivers/mtd/nand/gpmi/Makefile6
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-base.c1979
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-bbt.c565
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-bch.c289
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-ecc8.c382
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-hamming-13-8.c131
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c196
-rw-r--r--drivers/mtd/nand/gpmi/gpmi-hamming-22-16.h43
-rw-r--r--drivers/mtd/nand/gpmi/gpmi.h291
-rw-r--r--drivers/mtd/nand/imx_nfc.c8286
-rw-r--r--drivers/mtd/nand/lba/Makefile2
-rw-r--r--drivers/mtd/nand/lba/gpmi-transport.c828
-rw-r--r--drivers/mtd/nand/lba/gpmi.h103
-rw-r--r--drivers/mtd/nand/lba/lba-blk.c345
-rw-r--r--drivers/mtd/nand/lba/lba-core.c619
-rw-r--r--drivers/mtd/nand/lba/lba.h141
-rw-r--r--drivers/mtd/nand/mxc_nd.c1413
-rw-r--r--drivers/mtd/nand/mxc_nd.h112
-rw-r--r--drivers/mtd/nand/mxc_nd2.c1458
-rw-r--r--drivers/mtd/nand/mxc_nd2.h688
-rw-r--r--drivers/mtd/nand/nand_base.c10
-rw-r--r--drivers/mtd/nand/nand_ids.c3
-rw-r--r--drivers/mtd/nand/s3c2410.c459
-rw-r--r--drivers/mxc/Kconfig39
-rw-r--r--drivers/mxc/Makefile17
-rw-r--r--drivers/mxc/adc/Kconfig14
-rw-r--r--drivers/mxc/adc/Makefile4
-rw-r--r--drivers/mxc/adc/imx_adc.c1047
-rw-r--r--drivers/mxc/adc/imx_adc_reg.h242
-rw-r--r--drivers/mxc/asrc/Kconfig13
-rw-r--r--drivers/mxc/asrc/Makefile7
-rw-r--r--drivers/mxc/asrc/mxc_asrc.c1687
-rw-r--r--drivers/mxc/bt/Kconfig13
-rw-r--r--drivers/mxc/bt/Makefile4
-rw-r--r--drivers/mxc/bt/mxc_bt.c127
-rw-r--r--drivers/mxc/dam/Kconfig13
-rw-r--r--drivers/mxc/dam/Makefile9
-rw-r--r--drivers/mxc/dam/dam.c427
-rw-r--r--drivers/mxc/dam/dam.h258
-rw-r--r--drivers/mxc/dam/dam_v1.c617
-rw-r--r--drivers/mxc/gps_ioctrl/Kconfig13
-rw-r--r--drivers/mxc/gps_ioctrl/Makefile5
-rw-r--r--drivers/mxc/gps_ioctrl/agpsgpiodev.c329
-rw-r--r--drivers/mxc/gps_ioctrl/agpsgpiodev.h46
-rw-r--r--drivers/mxc/hmp4e/Kconfig24
-rw-r--r--drivers/mxc/hmp4e/Makefile8
-rw-r--r--drivers/mxc/hmp4e/mxc_hmp4e.c811
-rw-r--r--drivers/mxc/hmp4e/mxc_hmp4e.h70
-rw-r--r--drivers/mxc/hw_event/Kconfig11
-rw-r--r--drivers/mxc/hw_event/Makefile1
-rw-r--r--drivers/mxc/hw_event/mxc_hw_event.c265
-rw-r--r--drivers/mxc/ipu/Kconfig5
-rw-r--r--drivers/mxc/ipu/Makefile5
-rw-r--r--drivers/mxc/ipu/ipu_adc.c688
-rw-r--r--drivers/mxc/ipu/ipu_calc_stripes_sizes.c375
-rw-r--r--drivers/mxc/ipu/ipu_common.c1902
-rw-r--r--drivers/mxc/ipu/ipu_csi.c222
-rw-r--r--drivers/mxc/ipu/ipu_device.c696
-rw-r--r--drivers/mxc/ipu/ipu_ic.c592
-rw-r--r--drivers/mxc/ipu/ipu_param_mem.h176
-rw-r--r--drivers/mxc/ipu/ipu_prv.h59
-rw-r--r--drivers/mxc/ipu/ipu_regs.h396
-rw-r--r--drivers/mxc/ipu/ipu_sdc.c357
-rw-r--r--drivers/mxc/ipu/pf/Kconfig7
-rw-r--r--drivers/mxc/ipu/pf/Makefile1
-rw-r--r--drivers/mxc/ipu/pf/mxc_pf.c993
-rw-r--r--drivers/mxc/ipu3/Kconfig5
-rw-r--r--drivers/mxc/ipu3/Makefile4
-rw-r--r--drivers/mxc/ipu3/ipu_calc_stripes_sizes.c375
-rwxr-xr-xdrivers/mxc/ipu3/ipu_capture.c711
-rw-r--r--drivers/mxc/ipu3/ipu_common.c2318
-rw-r--r--drivers/mxc/ipu3/ipu_device.c446
-rw-r--r--drivers/mxc/ipu3/ipu_disp.c1515
-rw-r--r--drivers/mxc/ipu3/ipu_ic.c826
-rw-r--r--drivers/mxc/ipu3/ipu_param_mem.h494
-rw-r--r--drivers/mxc/ipu3/ipu_prv.h92
-rw-r--r--drivers/mxc/ipu3/ipu_regs.h651
-rw-r--r--drivers/mxc/mcu_pmic/Kconfig19
-rw-r--r--drivers/mxc/mcu_pmic/Makefile8
-rw-r--r--drivers/mxc/mcu_pmic/max8660.c154
-rw-r--r--drivers/mxc/mcu_pmic/max8660.h49
-rw-r--r--drivers/mxc/mcu_pmic/mc9s08dz60.c197
-rw-r--r--drivers/mxc/mcu_pmic/mc9s08dz60.h74
-rw-r--r--drivers/mxc/mcu_pmic/mcu_pmic_core.c226
-rw-r--r--drivers/mxc/mcu_pmic/mcu_pmic_core.h43
-rw-r--r--drivers/mxc/mcu_pmic/mcu_pmic_gpio.c132
-rw-r--r--drivers/mxc/mlb/Kconfig13
-rw-r--r--drivers/mxc/mlb/Makefile5
-rw-r--r--drivers/mxc/mlb/mxc_mlb.c1049
-rw-r--r--drivers/mxc/pmic/Kconfig64
-rw-r--r--drivers/mxc/pmic/Makefile8
-rw-r--r--drivers/mxc/pmic/core/Makefile22
-rw-r--r--drivers/mxc/pmic/core/mc13783.c380
-rw-r--r--drivers/mxc/pmic/core/mc13892.c333
-rw-r--r--drivers/mxc/pmic/core/mc34704.c329
-rw-r--r--drivers/mxc/pmic/core/pmic-dev.c317
-rw-r--r--drivers/mxc/pmic/core/pmic.h134
-rw-r--r--drivers/mxc/pmic/core/pmic_common.c98
-rw-r--r--drivers/mxc/pmic/core/pmic_core_i2c.c346
-rw-r--r--drivers/mxc/pmic/core/pmic_core_spi.c314
-rw-r--r--drivers/mxc/pmic/core/pmic_event.c235
-rw-r--r--drivers/mxc/pmic/core/pmic_external.c100
-rw-r--r--drivers/mxc/pmic/mc13783/Kconfig55
-rw-r--r--drivers/mxc/pmic/mc13783/Makefile18
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_adc.c1542
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_adc_defs.h321
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_audio.c5876
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_battery.c1221
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_battery_defs.h81
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_convity.c2482
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_light.c2768
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_light_defs.h144
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_power.c3146
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_power_defs.h509
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_rtc.c552
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_rtc_defs.h47
-rw-r--r--drivers/mxc/pmic/mc13892/Kconfig48
-rw-r--r--drivers/mxc/pmic/mc13892/Makefile10
-rw-r--r--drivers/mxc/pmic/mc13892/pmic_adc.c984
-rw-r--r--drivers/mxc/pmic/mc13892/pmic_battery.c634
-rw-r--r--drivers/mxc/pmic/mc13892/pmic_light.c685
-rw-r--r--drivers/mxc/security/Kconfig64
-rw-r--r--drivers/mxc/security/Makefile11
-rw-r--r--drivers/mxc/security/dryice-regs.h207
-rw-r--r--drivers/mxc/security/dryice.c1123
-rw-r--r--drivers/mxc/security/dryice.h287
-rw-r--r--drivers/mxc/security/mxc_scc.c2386
-rw-r--r--drivers/mxc/security/mxc_scc_internals.h499
-rw-r--r--drivers/mxc/security/rng/Makefile35
-rw-r--r--drivers/mxc/security/rng/des_key.c385
-rw-r--r--drivers/mxc/security/rng/fsl_shw_hash.c84
-rw-r--r--drivers/mxc/security/rng/fsl_shw_hmac.c83
-rw-r--r--drivers/mxc/security/rng/fsl_shw_rand.c122
-rw-r--r--drivers/mxc/security/rng/fsl_shw_sym.c317
-rw-r--r--drivers/mxc/security/rng/fsl_shw_wrap.c1301
-rw-r--r--drivers/mxc/security/rng/include/rng_driver.h134
-rw-r--r--drivers/mxc/security/rng/include/rng_internals.h680
-rw-r--r--drivers/mxc/security/rng/include/rng_rnga.h181
-rw-r--r--drivers/mxc/security/rng/include/rng_rngc.h235
-rw-r--r--drivers/mxc/security/rng/include/shw_driver.h2971
-rw-r--r--drivers/mxc/security/rng/include/shw_hash.h96
-rw-r--r--drivers/mxc/security/rng/include/shw_hmac.h82
-rw-r--r--drivers/mxc/security/rng/include/shw_internals.h162
-rw-r--r--drivers/mxc/security/rng/rng_driver.c1150
-rw-r--r--drivers/mxc/security/rng/shw_driver.c2335
-rw-r--r--drivers/mxc/security/rng/shw_dryice.c204
-rw-r--r--drivers/mxc/security/rng/shw_hash.c328
-rw-r--r--drivers/mxc/security/rng/shw_hmac.c145
-rw-r--r--drivers/mxc/security/rng/shw_memory_mapper.c213
-rw-r--r--drivers/mxc/security/sahara2/Kconfig35
-rw-r--r--drivers/mxc/security/sahara2/Makefile47
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_auth.c706
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_hash.c186
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_hmac.c266
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_keystore.c837
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_rand.c96
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_sym.c281
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_user.c137
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_wrap.c967
-rw-r--r--drivers/mxc/security/sahara2/include/adaptor.h113
-rw-r--r--drivers/mxc/security/sahara2/include/diagnostic.h116
-rw-r--r--drivers/mxc/security/sahara2/include/fsl_platform.h161
-rw-r--r--drivers/mxc/security/sahara2/include/fsl_shw.h2515
-rw-r--r--drivers/mxc/security/sahara2/include/fsl_shw_keystore.h475
-rw-r--r--drivers/mxc/security/sahara2/include/linux_port.h1804
-rw-r--r--drivers/mxc/security/sahara2/include/platform_abstractions.h15
-rw-r--r--drivers/mxc/security/sahara2/include/portable_os.h1453
-rw-r--r--drivers/mxc/security/sahara2/include/sah_driver_common.h102
-rw-r--r--drivers/mxc/security/sahara2/include/sah_hardware_interface.h99
-rw-r--r--drivers/mxc/security/sahara2/include/sah_interrupt_handler.h42
-rw-r--r--drivers/mxc/security/sahara2/include/sah_kernel.h113
-rw-r--r--drivers/mxc/security/sahara2/include/sah_memory_mapper.h79
-rw-r--r--drivers/mxc/security/sahara2/include/sah_queue_manager.h63
-rw-r--r--drivers/mxc/security/sahara2/include/sah_status_manager.h228
-rw-r--r--drivers/mxc/security/sahara2/include/sahara.h2266
-rw-r--r--drivers/mxc/security/sahara2/include/sahara2_kernel.h49
-rw-r--r--drivers/mxc/security/sahara2/include/sf_util.h466
-rw-r--r--drivers/mxc/security/sahara2/km_adaptor.c849
-rw-r--r--drivers/mxc/security/sahara2/sah_driver_interface.c2179
-rw-r--r--drivers/mxc/security/sahara2/sah_hardware_interface.c854
-rw-r--r--drivers/mxc/security/sahara2/sah_interrupt_handler.c216
-rw-r--r--drivers/mxc/security/sahara2/sah_memory_mapper.c2349
-rw-r--r--drivers/mxc/security/sahara2/sah_queue.c249
-rw-r--r--drivers/mxc/security/sahara2/sah_queue_manager.c1050
-rw-r--r--drivers/mxc/security/sahara2/sah_status_manager.c734
-rw-r--r--drivers/mxc/security/sahara2/sf_util.c1390
-rw-r--r--drivers/mxc/security/scc2_driver.c2306
-rw-r--r--drivers/mxc/security/scc2_internals.h527
-rw-r--r--drivers/mxc/ssi/Kconfig12
-rw-r--r--drivers/mxc/ssi/Makefile7
-rw-r--r--drivers/mxc/ssi/registers.h208
-rw-r--r--drivers/mxc/ssi/ssi.c1239
-rw-r--r--drivers/mxc/ssi/ssi.h574
-rw-r--r--drivers/mxc/ssi/ssi_types.h367
-rw-r--r--drivers/mxc/vpu/Kconfig30
-rw-r--r--drivers/mxc/vpu/Makefile10
-rw-r--r--drivers/mxc/vpu/mxc_vl2cc.c123
-rw-r--r--drivers/mxc/vpu/mxc_vpu.c847
-rw-r--r--drivers/net/Kconfig72
-rw-r--r--drivers/net/Makefile3
-rw-r--r--drivers/net/can/Kconfig167
-rw-r--r--drivers/net/can/Makefile46
-rw-r--r--drivers/net/can/dev.c528
-rw-r--r--drivers/net/can/flexcan/Makefile3
-rw-r--r--drivers/net/can/flexcan/dev.c619
-rw-r--r--drivers/net/can/flexcan/drv.c624
-rw-r--r--drivers/net/can/flexcan/flexcan.h223
-rw-r--r--drivers/net/can/flexcan/mbm.c347
-rw-r--r--drivers/net/can/mcp251x.c1239
-rw-r--r--drivers/net/can/mscan/Makefile23
-rw-r--r--drivers/net/can/mscan/mpc52xx_can.c293
-rw-r--r--drivers/net/can/mscan/mscan.c679
-rw-r--r--drivers/net/can/mscan/mscan.h237
-rw-r--r--drivers/net/can/old/Kconfig67
-rw-r--r--drivers/net/can/old/ccan/Makefile20
-rw-r--r--drivers/net/can/old/ccan/ccan.c557
-rw-r--r--drivers/net/can/old/ccan/ccan.h140
-rw-r--r--drivers/net/can/old/ccan/h7202_can.c199
-rw-r--r--drivers/net/can/old/hal/c200.c206
-rw-r--r--drivers/net/can/old/hal/esdio.c186
-rw-r--r--drivers/net/can/old/hal/gw2.c161
-rw-r--r--drivers/net/can/old/hal/hal.h99
-rw-r--r--drivers/net/can/old/hal/io.c123
-rw-r--r--drivers/net/can/old/hal/iomem.c142
-rw-r--r--drivers/net/can/old/hal/iomux.c125
-rw-r--r--drivers/net/can/old/hal/pc7io.c126
-rw-r--r--drivers/net/can/old/i82527/Makefile24
-rw-r--r--drivers/net/can/old/i82527/i82527.c1251
-rw-r--r--drivers/net/can/old/i82527/i82527.h300
-rw-r--r--drivers/net/can/old/i82527/proc.c209
-rw-r--r--drivers/net/can/old/mscan/Makefile21
-rw-r--r--drivers/net/can/old/mscan/mpc52xx_can.c248
-rw-r--r--drivers/net/can/old/mscan/mscan.c708
-rw-r--r--drivers/net/can/old/mscan/mscan.h247
-rw-r--r--drivers/net/can/old/sja1000/Makefile27
-rw-r--r--drivers/net/can/old/sja1000/proc.c230
-rw-r--r--drivers/net/can/old/sja1000/sja1000.c1140
-rw-r--r--drivers/net/can/old/sja1000/sja1000.h187
-rw-r--r--drivers/net/can/sja1000/Makefile30
-rw-r--r--drivers/net/can/sja1000/ems_pci.c328
-rw-r--r--drivers/net/can/sja1000/ems_pcmcia.c418
-rw-r--r--drivers/net/can/sja1000/ixxat_pci.c272
-rw-r--r--drivers/net/can/sja1000/kvaser_pci.c415
-rw-r--r--drivers/net/can/sja1000/peak_pci.c348
-rw-r--r--drivers/net/can/sja1000/pipcan.c201
-rw-r--r--drivers/net/can/sja1000/sja1000.c677
-rw-r--r--drivers/net/can/sja1000/sja1000.h177
-rw-r--r--drivers/net/can/sja1000/sja1000_platform.c169
-rw-r--r--drivers/net/can/slcan.c904
-rw-r--r--drivers/net/can/softing/Makefile20
-rw-r--r--drivers/net/can/softing/softing.h268
-rw-r--r--drivers/net/can/softing/softing_cs.c427
-rw-r--r--drivers/net/can/softing/softing_fw.c685
-rw-r--r--drivers/net/can/softing/softing_main.c1058
-rw-r--r--drivers/net/can/sysfs.c509
-rw-r--r--drivers/net/can/sysfs.h24
-rw-r--r--drivers/net/can/vcan.c21
-rw-r--r--drivers/net/cs89x0.c21
-rw-r--r--drivers/net/enc28j60.c186
-rw-r--r--drivers/net/fec.c744
-rw-r--r--drivers/net/fec.h18
-rw-r--r--drivers/net/irda/Kconfig4
-rw-r--r--drivers/net/irda/Makefile1
-rw-r--r--drivers/net/irda/mxc_ir.c1777
-rw-r--r--drivers/net/irda/mxc_ir.h133
-rwxr-xr-xdrivers/net/ns9xxx-eth.c1499
-rw-r--r--drivers/net/smc911x.h8
-rw-r--r--drivers/net/smsc9118/Makefile1
-rw-r--r--drivers/net/smsc9118/smsc911x.c2711
-rw-r--r--drivers/net/smsc9118/smsc911x.h393
-rw-r--r--drivers/net/smsc911x.c2253
-rw-r--r--drivers/net/smsc911x.h395
-rw-r--r--drivers/net/wireless/Kconfig39
-rw-r--r--drivers/net/wireless/Makefile2
-rw-r--r--drivers/net/wireless/digiPiper/Kconfig27
-rw-r--r--drivers/net/wireless/digiPiper/Makefile16
-rw-r--r--drivers/net/wireless/digiPiper/adc121c027.c161
-rw-r--r--drivers/net/wireless/digiPiper/adc121c027.h17
-rw-r--r--drivers/net/wireless/digiPiper/airoha.c986
-rw-r--r--drivers/net/wireless/digiPiper/airoha.h30
-rw-r--r--drivers/net/wireless/digiPiper/airohaCalibration.c974
-rw-r--r--drivers/net/wireless/digiPiper/airohaCalibration.h108
-rw-r--r--drivers/net/wireless/digiPiper/digiDebug.c205
-rw-r--r--drivers/net/wireless/digiPiper/digiIsr.c159
-rw-r--r--drivers/net/wireless/digiPiper/digiMac80211.c1000
-rw-r--r--drivers/net/wireless/digiPiper/digiPs.c1164
-rw-r--r--drivers/net/wireless/digiPiper/digiPs.h50
-rw-r--r--drivers/net/wireless/digiPiper/digiRx.c412
-rw-r--r--drivers/net/wireless/digiPiper/digiTx.c604
-rw-r--r--drivers/net/wireless/digiPiper/mac.h375
-rw-r--r--drivers/net/wireless/digiPiper/phy.c269
-rw-r--r--drivers/net/wireless/digiPiper/phy.h28
-rw-r--r--drivers/net/wireless/digiPiper/piper.c1032
-rw-r--r--drivers/net/wireless/digiPiper/piperDsp.c285
-rw-r--r--drivers/net/wireless/digiPiper/piperDsp.h8
-rw-r--r--drivers/net/wireless/digiPiper/piperMacAssist.c285
-rw-r--r--drivers/net/wireless/digiPiper/piperMacAssist.h9
-rw-r--r--drivers/net/wireless/digiPiper/pipermain.h381
-rw-r--r--drivers/net/wireless/digi_wi_g.c5021
-rw-r--r--drivers/net/wireless/digi_wi_g.h660
-rw-r--r--drivers/net/wireless/digi_wi_g_priv_handler.c345
-rw-r--r--drivers/pcmcia/Kconfig15
-rw-r--r--drivers/pcmcia/Makefile2
-rw-r--r--drivers/pcmcia/mx31ads-pcmcia.c1291
-rw-r--r--drivers/pcmcia/mx31ads-pcmcia.h155
-rwxr-xr-xdrivers/pcmcia/s3c2443_pcmcia.c491
-rw-r--r--drivers/power/Kconfig7
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/stmp37xx/Makefile9
-rw-r--r--drivers/power/stmp37xx/ddi_bc_api.c566
-rw-r--r--drivers/power/stmp37xx/ddi_bc_hw.c474
-rw-r--r--drivers/power/stmp37xx/ddi_bc_hw.h93
-rw-r--r--drivers/power/stmp37xx/ddi_bc_init.c204
-rw-r--r--drivers/power/stmp37xx/ddi_bc_internal.h52
-rw-r--r--drivers/power/stmp37xx/ddi_bc_ramp.c724
-rw-r--r--drivers/power/stmp37xx/ddi_bc_ramp.h50
-rw-r--r--drivers/power/stmp37xx/ddi_bc_sm.c1122
-rw-r--r--drivers/power/stmp37xx/ddi_bc_sm.h46
-rw-r--r--drivers/power/stmp37xx/ddi_power_battery.c1035
-rw-r--r--drivers/power/stmp37xx/ddi_power_battery.h67
-rw-r--r--drivers/power/stmp37xx/linux.c622
-rw-r--r--drivers/pwm/Kconfig35
-rw-r--r--drivers/pwm/Makefile7
-rw-r--r--drivers/pwm/atmel-pwm.c633
-rw-r--r--drivers/pwm/ns9xxx-pwm.c554
-rw-r--r--drivers/pwm/pwm.c626
-rw-r--r--drivers/regulator/Kconfig30
-rw-r--r--drivers/regulator/Makefile8
-rw-r--r--drivers/regulator/core.c5
-rw-r--r--drivers/regulator/reg-mc13783.c2661
-rw-r--r--drivers/regulator/reg-mc13892.c1849
-rw-r--r--drivers/regulator/reg-mc34704.c288
-rw-r--r--drivers/regulator/reg-mc9s08dz60.c235
-rw-r--r--drivers/regulator/stmp3xxx.c300
-rw-r--r--drivers/rtc/Kconfig50
-rw-r--r--drivers/rtc/Makefile6
-rw-r--r--drivers/rtc/rtc-ds1307.c46
-rw-r--r--drivers/rtc/rtc-imxdi.c580
-rw-r--r--drivers/rtc/rtc-mc13892.c256
-rw-r--r--drivers/rtc/rtc-mxc.c806
-rw-r--r--drivers/rtc/rtc-mxc_v2.c719
-rw-r--r--drivers/rtc/rtc-ns9xxx.c946
-rw-r--r--drivers/rtc/rtc-stmp3xxx.c277
-rw-r--r--drivers/scsi/Kconfig11
-rw-r--r--drivers/serial/8250.c10
-rw-r--r--drivers/serial/Kconfig154
-rw-r--r--drivers/serial/Makefile6
-rw-r--r--drivers/serial/mxc_uart.c1948
-rw-r--r--drivers/serial/mxc_uart_early.c253
-rw-r--r--drivers/serial/mxc_uart_reg.h128
-rw-r--r--drivers/serial/ns921x-serial.c1401
-rw-r--r--drivers/serial/ns9360-serial.c878
-rw-r--r--drivers/serial/samsung.c25
-rw-r--r--drivers/serial/stmp-app.c1064
-rw-r--r--drivers/serial/stmp-app.h81
-rw-r--r--drivers/serial/stmp-dbg.c839
-rw-r--r--drivers/serial/stmp-dbg.h180
-rw-r--r--drivers/spi/Kconfig58
-rw-r--r--drivers/spi/Makefile5
-rw-r--r--drivers/spi/mxc_spi.c1302
-rw-r--r--drivers/spi/spi.c22
-rw-r--r--drivers/spi/spi_ns921x.c852
-rw-r--r--drivers/spi/spi_ns9360.c890
-rw-r--r--drivers/spi/spi_s3c2443.c1229
-rw-r--r--drivers/spi/spi_stmp.c693
-rw-r--r--drivers/spi/spi_stmp.h49
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/Makefile3
-rw-r--r--drivers/usb/core/hcd.c17
-rw-r--r--drivers/usb/core/hub.c70
-rw-r--r--drivers/usb/gadget/Kconfig122
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/arcotg_udc.c3045
-rw-r--r--drivers/usb/gadget/arcotg_udc.h703
-rw-r--r--drivers/usb/gadget/epautoconf.c6
-rw-r--r--drivers/usb/gadget/ether.c2
-rw-r--r--drivers/usb/gadget/file_storage.c65
-rw-r--r--drivers/usb/gadget/gadget_chips.h16
-rw-r--r--drivers/usb/gadget/inode.c115
-rw-r--r--drivers/usb/gadget/s3c2443_udc.c2810
-rw-r--r--drivers/usb/gadget/s3c2443_udc.h159
-rw-r--r--drivers/usb/gadget/serial.c2
-rw-r--r--drivers/usb/gadget/stmp_updater.c473
-rw-r--r--drivers/usb/gadget/stmp_updater.h139
-rw-r--r--drivers/usb/host/Kconfig91
-rw-r--r--drivers/usb/host/ehci-arc.c635
-rw-r--r--drivers/usb/host/ehci-fsl.h14
-rw-r--r--drivers/usb/host/ehci-hcd.c18
-rw-r--r--drivers/usb/host/ehci-hub.c31
-rw-r--r--drivers/usb/host/ehci-mem-iram.c506
-rw-r--r--drivers/usb/host/ehci-q-iram.c1345
-rw-r--r--drivers/usb/host/ehci.h20
-rw-r--r--drivers/usb/host/ohci-hcd.c5
-rw-r--r--drivers/usb/host/ohci-ns9360.c263
-rw-r--r--drivers/usb/host/ohci-s3c2410.c103
-rw-r--r--drivers/usb/otg/Kconfig11
-rw-r--r--drivers/usb/otg/Makefile7
-rw-r--r--drivers/usb/otg/fsl_otg.c1200
-rw-r--r--drivers/usb/otg/fsl_otg.h413
-rw-r--r--drivers/usb/otg/otg_fsm.c371
-rw-r--r--drivers/usb/otg/otg_fsm.h151
-rw-r--r--drivers/video/Kconfig47
-rw-r--r--drivers/video/Makefile5
-rw-r--r--drivers/video/backlight/Kconfig41
-rw-r--r--drivers/video/backlight/Makefile6
-rw-r--r--drivers/video/backlight/mxc_ipu_bl.c155
-rw-r--r--drivers/video/backlight/mxc_lcdc_bl.c160
-rw-r--r--drivers/video/backlight/mxc_mc13892_bl.c177
-rw-r--r--drivers/video/backlight/mxc_pmic_bl.c197
-rw-r--r--drivers/video/backlight/pwm_bl.c4
-rw-r--r--drivers/video/backlight/stmp37xx_bl.c378
-rw-r--r--drivers/video/backlight/wm8350_bl.c298
-rw-r--r--drivers/video/hx8347fb.c482
-rw-r--r--drivers/video/modedb.c4
-rw-r--r--drivers/video/mxc/Kconfig87
-rw-r--r--drivers/video/mxc/Makefile22
-rw-r--r--drivers/video/mxc/ch7024.c866
-rw-r--r--drivers/video/mxc/fs453.c494
-rw-r--r--drivers/video/mxc/fs453.h134
-rw-r--r--drivers/video/mxc/mx2fb.c1346
-rw-r--r--drivers/video/mxc/mx2fb.h141
-rw-r--r--drivers/video/mxc/mxc_edid.c90
-rw-r--r--drivers/video/mxc/mxc_ipuv3_fb.c1599
-rw-r--r--drivers/video/mxc/mxcfb.c1502
-rw-r--r--drivers/video/mxc/mxcfb_ch7026.c369
-rw-r--r--drivers/video/mxc/mxcfb_claa_wvga.c236
-rw-r--r--drivers/video/mxc/mxcfb_epson.c1158
-rw-r--r--drivers/video/mxc/mxcfb_epson_vga.c360
-rw-r--r--drivers/video/mxc/mxcfb_modedb.c69
-rw-r--r--drivers/video/mxc/tve.c803
-rw-r--r--drivers/video/ns9360fb.c336
-rw-r--r--drivers/video/s3c2410fb_tft.c1008
-rw-r--r--drivers/video/s3c2443fb_tft.c737
-rw-r--r--drivers/video/stmp37xxfb.c844
-rw-r--r--drivers/w1/masters/Kconfig6
-rw-r--r--drivers/w1/masters/Makefile2
-rw-r--r--drivers/w1/masters/mxc_w1.c449
-rw-r--r--drivers/w1/slaves/Kconfig28
-rw-r--r--drivers/w1/slaves/Makefile3
-rw-r--r--drivers/w1/slaves/w1_ds2438.c585
-rw-r--r--drivers/w1/slaves/w1_ds2438.h119
-rw-r--r--drivers/w1/slaves/w1_ds2751.c317
-rw-r--r--drivers/w1/w1_family.h2
-rw-r--r--drivers/watchdog/Kconfig30
-rw-r--r--drivers/watchdog/Makefile3
-rw-r--r--drivers/watchdog/mxc_wdt.c385
-rw-r--r--drivers/watchdog/mxc_wdt.h37
-rw-r--r--drivers/watchdog/ns9xxx_wdt.c330
-rw-r--r--drivers/watchdog/stmp3xxx_wdt.c301
-rw-r--r--fs/Kconfig50
-rw-r--r--fs/Makefile1
-rw-r--r--fs/fcntl.c1
-rw-r--r--fs/jffs2/fs.c6
-rw-r--r--fs/jffs2/os-linux.h2
-rw-r--r--fs/jffs2/scan.c24
-rw-r--r--fs/lockd/svclock.c2
-rw-r--r--fs/nfsd/nfsxdr.c2
-rw-r--r--fs/squashfs/Makefile7
-rw-r--r--fs/squashfs/inode.c2157
-rw-r--r--fs/squashfs/squashfs.h86
-rw-r--r--fs/squashfs/squashfs2_0.c740
-rw-r--r--git_revision.txt1
-rw-r--r--include/asm-arm/plat-s3c24xx/mci.h3
-rw-r--r--include/asm-arm/plat-s3c24xx/regs-spi.h96
-rw-r--r--include/asm-arm/plat-s3c24xx/regs-udc.h184
-rw-r--r--include/asm-arm/plat-s3c24xx/spi.h55
-rw-r--r--include/asm-generic/gpio.h8
-rw-r--r--include/linux/Kbuild1
-rw-r--r--include/linux/can/dev.h151
-rw-r--r--include/linux/can/ioctl.h97
-rw-r--r--include/linux/can/platform/mcp251x.h34
-rw-r--r--include/linux/can/platform/sja1000.h32
-rw-r--r--include/linux/cryptodev.h82
-rw-r--r--include/linux/fsl_devices.h63
-rw-r--r--include/linux/i2c-ns9xxx.h22
-rw-r--r--include/linux/ieee80211.h212
-rw-r--r--include/linux/imx_adc.h275
-rw-r--r--include/linux/ipu.h1235
-rw-r--r--include/linux/major.h2
-rw-r--r--include/linux/mfd/mc13783/core.h72
-rw-r--r--include/linux/mfd/mc13892/core.h77
-rw-r--r--include/linux/mfd/mc34704/core.h84
-rw-r--r--include/linux/mfd/mc9s08dz60/core.h59
-rw-r--r--include/linux/mfd/mc9s08dz60/pmic.h82
-rw-r--r--include/linux/mfd/wm8350/audio.h37
-rw-r--r--include/linux/mfd/wm8350/bl.h32
-rw-r--r--include/linux/mfd/wm8350/core.h1
-rw-r--r--include/linux/miscdevice.h1
-rw-r--r--include/linux/mma7455l.h11
-rw-r--r--include/linux/mmc/core.h2
-rw-r--r--include/linux/mmc/host.h2
-rw-r--r--include/linux/mtd/ccx9x_nand.h11
-rw-r--r--include/linux/mtd/nand.h9
-rw-r--r--include/linux/mxc_asrc.h210
-rw-r--r--include/linux/mxc_mlb.h51
-rw-r--r--include/linux/mxc_pf.h125
-rw-r--r--include/linux/mxc_scc2_driver.h975
-rw-r--r--include/linux/mxc_scc_driver.h1031
-rw-r--r--include/linux/mxc_si4702.h39
-rw-r--r--include/linux/mxc_sim_interface.h108
-rw-r--r--include/linux/mxc_v4l2.h48
-rw-r--r--include/linux/mxcfb.h84
-rw-r--r--include/linux/nl80211.h223
-rw-r--r--include/linux/ns9xxx-eth.h21
-rw-r--r--include/linux/pmic_adc.h455
-rw-r--r--include/linux/pmic_battery.h419
-rw-r--r--include/linux/pmic_external.h1108
-rw-r--r--include/linux/pmic_light.h1082
-rw-r--r--include/linux/pmic_rtc.h153
-rw-r--r--include/linux/pmic_status.h82
-rw-r--r--include/linux/pwm-led.h35
-rw-r--r--include/linux/pwm.h169
-rw-r--r--include/linux/random.h29
-rw-r--r--include/linux/serial.h3
-rw-r--r--include/linux/serial_core.h12
-rw-r--r--include/linux/skbuff.h4
-rw-r--r--include/linux/soundcard.h7
-rw-r--r--include/linux/spi/ads7846.h34
-rw-r--r--include/linux/spi_ns9360.h22
-rw-r--r--include/linux/squashfs_fs.h935
-rw-r--r--include/linux/squashfs_fs_i.h45
-rw-r--r--include/linux/squashfs_fs_sb.h79
-rw-r--r--include/linux/usb.h3
-rw-r--r--include/linux/usb/fsl_xcvr.h44
-rw-r--r--include/mtd/mtd-abi.h3
-rw-r--r--include/net/cfg80211.h142
-rw-r--r--include/net/ieee80211.h11
-rw-r--r--include/net/ieee80211softmac.h394
-rw-r--r--include/net/ieee80211softmac_wx.h99
-rw-r--r--include/net/mac80211.h447
-rw-r--r--include/net/wireless.h90
-rw-r--r--include/sound/soc.h3
-rw-r--r--include/video/hx8347fb.h95
-rw-r--r--init/do_mounts_rd.c16
-rw-r--r--init/main.c6
-rw-r--r--lib/Kconfig.debug13
-rw-r--r--net/core/skbuff.c1
-rw-r--r--net/ieee80211/Kconfig1
-rw-r--r--net/ieee80211/Makefile1
-rw-r--r--net/ieee80211/compat.h104
-rw-r--r--net/ieee80211/ieee80211_tx.c176
-rw-r--r--net/ieee80211/ieee80211_wx.c89
-rw-r--r--net/ieee80211/softmac/Kconfig12
-rw-r--r--net/ieee80211/softmac/Makefile9
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_assoc.c542
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_auth.c413
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_event.c190
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_io.c488
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_module.c621
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_priv.h256
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_scan.c331
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_wx.c514
-rw-r--r--net/ipv4/Kconfig8
-rw-r--r--net/ipv4/ipconfig.c9
-rw-r--r--net/mac80211/Kconfig30
-rw-r--r--net/mac80211/Makefile6
-rw-r--r--net/mac80211/cfg.c194
-rw-r--r--net/mac80211/debugfs.c12
-rw-r--r--net/mac80211/debugfs_key.c4
-rw-r--r--net/mac80211/debugfs_netdev.c117
-rw-r--r--net/mac80211/debugfs_sta.c84
-rw-r--r--net/mac80211/event.c5
-rw-r--r--net/mac80211/ht.c267
-rw-r--r--net/mac80211/ieee80211_i.h92
-rw-r--r--net/mac80211/iface.c55
-rw-r--r--net/mac80211/key.c10
-rw-r--r--net/mac80211/main.c288
-rw-r--r--net/mac80211/mesh.c4
-rw-r--r--net/mac80211/mesh.h5
-rw-r--r--net/mac80211/mesh_hwmp.c7
-rw-r--r--net/mac80211/mesh_plink.c50
-rw-r--r--net/mac80211/mlme.c609
-rw-r--r--net/mac80211/rate.c52
-rw-r--r--net/mac80211/rate.h24
-rw-r--r--net/mac80211/rc80211_minstrel.c119
-rw-r--r--net/mac80211/rc80211_minstrel.h2
-rw-r--r--net/mac80211/rc80211_multirate_pid.h301
-rw-r--r--net/mac80211/rc80211_multirate_pid_algo.c569
-rw-r--r--net/mac80211/rc80211_multirate_pid_debugfs.c257
-rw-r--r--net/mac80211/rc80211_pid.h3
-rw-r--r--net/mac80211/rc80211_pid_algo.c37
-rw-r--r--net/mac80211/rc80211_pid_debugfs.c5
-rw-r--r--net/mac80211/rx.c278
-rw-r--r--net/mac80211/scan.c24
-rw-r--r--net/mac80211/sta_info.c37
-rw-r--r--net/mac80211/sta_info.h44
-rw-r--r--net/mac80211/tkip.c10
-rw-r--r--net/mac80211/tx.c472
-rw-r--r--net/mac80211/util.c107
-rw-r--r--net/mac80211/wep.c33
-rw-r--r--net/mac80211/wep.h2
-rw-r--r--net/mac80211/wext.c293
-rw-r--r--net/mac80211/wme.c32
-rw-r--r--net/mac80211/wpa.c32
-rw-r--r--net/wireless/Kconfig40
-rw-r--r--net/wireless/Makefile7
-rw-r--r--net/wireless/core.c14
-rw-r--r--net/wireless/core.h13
-rw-r--r--net/wireless/lib80211.c284
-rw-r--r--net/wireless/lib80211_crypt_ccmp.c494
-rw-r--r--net/wireless/lib80211_crypt_tkip.c788
-rw-r--r--net/wireless/lib80211_crypt_wep.c296
-rw-r--r--net/wireless/nl80211.c428
-rw-r--r--net/wireless/reg.c1146
-rw-r--r--net/wireless/reg.h33
-rw-r--r--net/wireless/sysfs.c25
-rw-r--r--net/wireless/util.c19
-rw-r--r--net/wireless/wext-compat.c139
-rw-r--r--net/wireless/wext.c2
-rw-r--r--scripts/basic/fixdep.c5
-rw-r--r--scripts/kallsyms.c3
-rw-r--r--scripts/kconfig/conf.c6
-rw-r--r--scripts/mod/modpost.c6
-rwxr-xr-xscripts/setlocalversion12
-rw-r--r--sound/arm/Kconfig49
-rw-r--r--sound/arm/Makefile11
-rw-r--r--sound/arm/mxc-alsa-common.h68
-rw-r--r--sound/arm/mxc-alsa-mixer.c410
-rw-r--r--sound/arm/mxc-alsa-pmic.c3790
-rw-r--r--sound/arm/mxc-alsa-pmic.h110
-rw-r--r--sound/arm/mxc-alsa-spdif.c2266
-rw-r--r--sound/soc/Kconfig4
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/codecs/Kconfig28
-rw-r--r--sound/soc/codecs/Makefile14
-rw-r--r--sound/soc/codecs/ak4647.c789
-rw-r--r--sound/soc/codecs/ak4647.h92
-rw-r--r--sound/soc/codecs/ak5702.c592
-rw-r--r--sound/soc/codecs/ak5702.h130
-rw-r--r--sound/soc/codecs/bluetooth.c147
-rw-r--r--sound/soc/codecs/sgtl5000.c1219
-rw-r--r--sound/soc/codecs/sgtl5000.h405
-rw-r--r--sound/soc/codecs/stmp378x_codec.c929
-rw-r--r--sound/soc/codecs/stmp378x_codec.h87
-rw-r--r--sound/soc/codecs/stmp3xxx_spdif.c412
-rw-r--r--sound/soc/codecs/stmp3xxx_spdif.h37
-rw-r--r--sound/soc/codecs/wm8350.c1546
-rw-r--r--sound/soc/codecs/wm8350.h20
-rw-r--r--sound/soc/codecs/wm8580.c178
-rw-r--r--sound/soc/codecs/wm8580.h5
-rw-r--r--sound/soc/codecs/wm8753.c27
-rw-r--r--sound/soc/imx/Kconfig72
-rw-r--r--sound/soc/imx/Makefile23
-rw-r--r--sound/soc/imx/imx-3stack-ak4647.c441
-rw-r--r--sound/soc/imx/imx-3stack-ak5702.c229
-rw-r--r--sound/soc/imx/imx-3stack-bt.c256
-rw-r--r--sound/soc/imx/imx-3stack-bt.h21
-rw-r--r--sound/soc/imx/imx-3stack-sgtl5000.c741
-rw-r--r--sound/soc/imx/imx-3stack-wm8350.c717
-rw-r--r--sound/soc/imx/imx-3stack-wm8580.c447
-rw-r--r--sound/soc/imx/imx-esai.c904
-rw-r--r--sound/soc/imx/imx-esai.h25
-rw-r--r--sound/soc/imx/imx-pcm.c689
-rw-r--r--sound/soc/imx/imx-pcm.h83
-rw-r--r--sound/soc/imx/imx-ssi.c793
-rw-r--r--sound/soc/imx/imx-ssi.h218
-rw-r--r--sound/soc/s3c24xx/Kconfig18
-rw-r--r--sound/soc/s3c24xx/Makefile2
-rw-r--r--sound/soc/s3c24xx/cc9m2443js_wm8753.c298
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.c129
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.h1
-rw-r--r--sound/soc/s3c24xx/s3c24xx-pcm.c68
-rw-r--r--sound/soc/soc-core.c17
-rw-r--r--sound/soc/stmp3xxx/Kconfig31
-rw-r--r--sound/soc/stmp3xxx/Makefile15
-rw-r--r--sound/soc/stmp3xxx/stmp3780_devb.c97
-rw-r--r--sound/soc/stmp3xxx/stmp3780_devb_spdif.c95
-rw-r--r--sound/soc/stmp3xxx/stmp3xxx_dai.c224
-rw-r--r--sound/soc/stmp3xxx/stmp3xxx_dai.h21
-rw-r--r--sound/soc/stmp3xxx/stmp3xxx_pcm.c446
-rw-r--r--sound/soc/stmp3xxx/stmp3xxx_pcm.h30
-rw-r--r--sound/soc/stmp3xxx/stmp3xxx_spdif_dai.c184
-rw-r--r--sound/soc/stmp3xxx/stmp3xxx_spdif_dai.h21
1476 files changed, 515405 insertions, 5128 deletions
diff --git a/Documentation/imx_nfc.txt b/Documentation/imx_nfc.txt
new file mode 100644
index 000000000000..7fec999c2884
--- /dev/null
+++ b/Documentation/imx_nfc.txt
@@ -0,0 +1,369 @@
+i.MX NAND Flash Controller Driver Documentation
+===============================================
+
+
+Definitions of Terms
+====================
+
+To avoid confusion, let's carefully define some of the terms we'll be using:
+
+ "NAND Flash Chip" or "Chip"
+ A NAND Flash storage array and controller (including a chip select
+ signal, ready/busy signal, data register, etc.).
+
+ "NAND Flash Package" or "Package"
+ A hardware package containing one or more NAND Flash chips that share
+ data lines and most control signals, but with separate chip select
+ and ready/busy signals. Package "boundaries" are unimportant to MTD.
+
+ "NAND Flash Medium" or "Medium"
+ A collection of one or more NAND Flash chips that the system views as
+ logically contiguous.
+
+ "Memory Technology Device" or "MTD"
+ An abstraction of underlying memory hardware, represented by a single
+ struct mtd_info.
+
+ "NAND Flash MTD"
+ An abstraction of a NAND Flash medium that is represented by a single
+ struct nand_chip (the name of this structure is misleading because it
+ has evolved to represent an entire medium, not just a single chip).
+ All the physical chips in a NAND Flash MTD medium have answered the
+ "Read ID" command with the same manufacturer code and device code and
+ are presumed to have identical characteristics.
+
+ "NAND Flash MTD Hardware-independent Layer" or "HIL"
+ The code that implements an MTD interface and drives all NAND Flash
+ MTDs.
+
+ "NAND Flash MTD Hardware Abstraction Layer" or "HAL"
+ Code that implements the internal NAND Flash hardware model and
+ matches it to specific hardware.
+
+ "NAND Flash MTD Reference Implementation"
+ A reference implementation of a HAL "stack."
+
+ "Out-of-Band" or "OOB"
+ An adjective describing information that is not part of the "data" in
+ a NAND Flash page. NAND Flash pages are generally described in terms
+ of the amount of data they can hold (e.g., "2K pages" or "4K pages").
+ The physical page size is actually larger than this and the
+ additional bytes are called "out-of-band."
+
+
+
+The Structure of the MTD NAND Flash System
+==========================================
+
+The following figure illustrates how control flows down from the system's
+MTD interface into the NAND Flash MTD system and down to the hardware-
+specific implementation (this driver):
+
+
+
+ / +---------------------------------------+
+ | | MTD |
+ | +---------------------------------------+
+ MTD | |
+ | | +----------+
+ | | | |
+ | v v |
+ \ ======================================== |
+ - struct mtd_info |
+ / ======================================== |
+ | | |
+ | v |
+ | +---------------------------------------+ |
+ | | NAND Flash MTD | |
+ NAND Flash | | Interface Functions | |
+ Hardware- | +---------------------------------------+ |
+ Independent | | | |
+ Layer | | v |
+ (HIL) | +-------------+ | +-------------+ +-------------+
+ | | Init | | | Support | | Reference |
+ | | Functions | | | Functions | |BBT Functions|
+ | +-------------+ | +-------------+ +-------------+
+ | | | | ^
+ | v v v |
+ \ ======================================== |
+ - struct nand_chip |
+ / ======================================== |
+ | | ^ | ^ | |
+ NAND Flash | | | | | | |
+ Hardware | v | v | | |
+ Abstraction | +--------------+ +---------------+ | |
+ Layer | | Hardware- | | Reference | +-----------+
+ (HAL) | | Specific | | |
+ | |Implementation| |Implementations|
+ \ +--------------+ +---------------+
+
+
+
+The function pointers and attributes in struct mtd_info embody an abstract
+model of memory technology devices.
+
+The struct nand_chip is an aggregation of two categories of function pointers
+and attributes:
+
+ - Function pointers and attributes used by the HIL. These members embody
+ an abstract model of NAND Flash media, or a hardware abstraction
+ layer (HAL).
+
+ - Function pointers and attributes used by the reference implementations.
+
+The single most confusing thing about the MTD NAND Flash system is that
+struct nand_chip contains all the main HAL members mixed up with all the
+members used only by the reference implementation, without any clear
+distinction. Recognizing the distinction is critical for understanding the
+relationship between the HIL and HAL, and can greatly simplify driver
+implementation.
+
+The fundamental operations represented by the function pointers in
+struct nand_chip fall into the following categories (from conceptually
+"low-level" to "high-level"):
+
+ - Signal Control
+ - Chip Control
+ - Low-level I/O
+ - ECC Control
+ - ECC-aware I/O
+ - Error Recovery
+ - High-level I/O
+ - Bad Block Management
+
+The HIL uses only the following "Replaceable" function pointers in
+struct nand_chip:
+
+ - Signal Control
+ - None
+ - Chip Control
+ - dev_ready
+ - select_chip
+ - cmdfunc
+ - waitfunc
+ - Low-level I/O
+ - read_byte
+ - ECC Control
+ - None
+ - ECC-aware I/O
+ - ecc.read_page
+ - ecc.read_page_raw
+ - Error Recovery
+ - None
+ - High-level I/O
+ - write_page
+ - ecc.read_oob
+ - ecc.write_oob
+ - Bad Block Management
+ - block_bad
+ - block_markbad
+ - scan_bbt
+
+Note that the HIL calls erase_cmd, but this member is marked "Internal."
+
+The HIL uses only the following commands with cmdfunc:
+
+ * NAND_CMD_STATUS
+ - nand_check_wp() - Checks if the current chip is
+ write-protected.
+ * NAND_CMD_READID
+ - nand_get_flash_type() - Gets information about the first chip.
+ - nand_scan_ident() - Scans for additional chips.
+ * NAND_CMD_RESET
+ - nand_do_write_oob() - Clears a bug observed on the
+ Toshiba TC5832DC and DiskOnChip 2000.
+ * NAND_CMD_READ0
+ - nand_do_read_ops() - Used to begin a full page read (both with
+ and without ECC).
+ * NAND_CMD_ERASE1
+ - single_erase_cmd() - Starts a block erase operation.
+ - multi_erase_cmd() - Starts a block erase operation.
+ * NAND_CMD_ERASE2
+ - single_erase_cmd() - Finishes a block erase operation.
+ - multi_erase_cmd() - Finishes a block erase operation.
+
+
+Since this is all the HIL uses, this is all a driver needs to implement.
+
+
+
+The Structure of the imx_nfc Driver
+===================================
+
+This driver supports many different versions of underlying controller, and
+also supports higher-level abstractions like interleaving. To facilitate this
+versatility, the code is layered as shown in the following diagram:
+
+
+ +--------------------------------------+
+ | MTD |
+ +--------------------------------------+
+ | NAND Flash MTD |
+ ======================================== <-- struct nand_chip
+ / | MTD Interface Layer (mil_*) |
+ | | +----------------------------------+
+ | | | Medium Abstraction Layer (mal_*) |
+ imx_nfc | | | |
+ driver | | | +------------------------+
+ | | | | NFC Utils (nfc_util_*) |
+ | ======================================== <-- struct nfc_hal
+ \ | NFC HAL (nfc_x_y_*) |
+ ======================================== <-- Hardware Interface
+ | NFC Hardware |
+ +--------------------------------------+
+
+
+MTD Interface Layer
+-------------------
+This layer includes functions that the NAND Flash MTD system calls directly.
+In a manner of speaking, this layer "parses" the function calls made by MTD
+and translates them into fundamental operations understood by the Medium
+Abstraction Layer. Some simple operations don't need any abstraction, so code
+in this layer can sometimes use the NFC HAL directly.
+
+
+Medium Abstraction Layer
+------------------------
+This layer implements the abstract model of the NAND Flash medium and hides
+details that shouldn't concern higher layers (e.g., interleaving).
+
+
+NFC Utilities
+-------------
+These functions make it easier to use the NFC HAL. Even though this layer
+is shown above the NFC HAL in the diagram, it's actually possible for the
+NFC HAL to call some of these functions.
+
+
+NFC HAL
+-------
+This layer implements the abstract model of an i.MX NAND Flash controller.
+
+
+Other Collections of Functions
+------------------------------
+
+- System Interface
+ - imx_nfc_*
+
+- sysfs Interface
+ - get_module_*
+ - set_module_*
+ - show_device_*
+ - store_device_*
+
+
+
+
+i.MX NAND Flash Controller Versions
+===================================
+
+The i.MX NAND Flash controller (NFC) has evolved over time. Both its memory
+layout and behaviors have changed. In this driver, we use major and minor
+version numbers to label these stages in the NFC's evolution. These version
+numbers are very useful, but they are entirely a figment of this driver's
+imagination -- you will never find them in Freescale hardware reference
+manuals.
+
+When the platform code instantiates an i.MX NFC device, it provides a struct
+imx_nfc_platform_data that contains platform-specific information. This
+includes the major and minor version numbers for the NFC. This driver uses
+the version numbers to select an appopriate NFC HAL structure.
+
+
+
+i.MX NFC Memory Map
+===================
+
+While many things have changed during the evolution of the NFC, much has
+remained the same. All i.MX NFCs have two or three essential memory-mapped
+regions: a set of buffers, and one or two sets of registers (one on the AXI
+bus and perhaps a second on the IP bus).
+
+The buffer area contains the captive memory to which the NFC writes data
+received from the NAND Flash medium. This area is subdivided into several
+contiguous "main" buffers that hold 512-byte chunks of data, and several
+"spare" buffers that hold varying-size chunks of out-of-band bytes. The
+number of main buffers is always the same as the number of spare buffers, but
+the exact count and the size of the spare buffers varies across NFC versions.
+
+The register areas contain the NFC's control interface. Some versions have
+only one set of registers, and some have two.
+
+The platform-specific resources passed to this driver include the starting
+and ending physical addresses of the buffer and register areas. This driver
+maps those physical addresses to virtual addresses, and then uses version-
+specific offsets and bit masks to operate the NFC.
+
+
+
+Matching the NAND Flash MTD Page Model with the i.MX NFC
+========================================================
+
+The NAND Flash MTD HAL model views a page as containing two essentially
+independent groups of bytes: "data" bytes and "out-of-band" bytes. If the
+underlying physical format has data and out-of-band bytes distributed across
+the page, they must be reassembled before being delivered to the caller
+(e.g., see the function nand_read_page_syndrome(), which is part of the
+reference implementation).
+
+The i.MX NFC hardware imposes both a physical page layout and a layout in its
+memory buffer that differ from the HAL model. The following figure shows how
+all these layouts relate to each other:
+
+
+ i.MX NFC i.MX NFC
+ Physical Memory NAND Flash
+ Page Buffers MTD Model
+
+ +--------+ +--------+ +--------+
+ |OOB[N-1]| |OOB[N-1]| |OOB[N-1]|
+ +--------+ +--------+ +--------+
+ | | <gap> |OOB[ 1 ]|
+ | Data | +--------+ +--------+
+ | [N-1] | |OOB[ 1 ]| |OOB[ 0 ]|
+ | | +--------+ +--------+
+ +--------+ <gap>
+ ... +--------+ +--------+
+ +--------+ |OOB[ 0 ]| | |
+ |OOB[ 1 ]| +--------+ | Data |
+ +--------+ | | | [N-1] |
+ | | | Data | | |
+ | Data | | [N-1] | +--------+
+ | [ 1 ] | | | | |
+ | | +--------+ | Data |
+ +--------+ ... | [ 1 ] |
+ |OOB[ 0 ]| +--------+ | |
+ +--------+ | | +--------+
+ | | | Data | | |
+ | Data | | [ 1 ] | | Data |
+ | [ 0 ] | | | | [ 0 ] |
+ | | +--------+ | |
+ +--------+ | | +--------+
+ | Data |
+ | [ 0 ] |
+ | |
+ +--------+
+
+
+The NFC memory is *almost* what we want, but not quite. The problems are:
+
+ 1) There are gaps between the out-of-band fragments.
+
+ 2) The NFC memory responds only to 16-byte or 32-byte reads and writes.
+
+To resolve these problems, we've encapsulated the NFC memory behind these
+functions:
+
+ nfc_util_copy_from_the_nfc() - Copies data to main memory from the the NFC.
+ nfc_util_copy_to_the_nfc() - Copies data from main memory to the NFC.
+
+These functions don't take pointers to locations within the NFC memory - they
+take a "column address." These functions know how to skip over the gaps and
+handle the NFC memory such that it looks like all the data and out-of-band
+bytes are completely contiguous. They also handle copying arbitrary bytes
+from/to a memory that only responds to 16- or 32-byte reads/writes. If you're
+accessing the NFC memory without these functions, you're *probably* doing
+something wrong.
+
+
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
new file mode 100644
index 000000000000..d6f87984f889
--- /dev/null
+++ b/Documentation/pwm.txt
@@ -0,0 +1,257 @@
+ Generic PWM Device API
+
+ October 8, 2008
+ Bill Gatliff
+ <bgat <at> billgatliff.com>
+
+
+
+The code in drivers/pwm and include/linux/pwm.h implements an API for
+applications involving pulse-width-modulation signals. This document
+describes how the API implementation facilitates both PWM-generating
+devices, and users of those devices.
+
+
+
+Motivation
+
+The primary goals for implementing the "generic PWM API" are to
+consolidate the various PWM implementations within a consistent and
+redundancy-reducing framework, and to facilitate the use of
+hotpluggable PWM devices.
+
+Previous PWM-related implementations within the Linux kernel achieved
+their consistency via cut-and-paste, but did not need to (and didn't)
+facilitate more than one PWM-generating device within the system---
+hotplug or otherwise. The Generic PWM Device API might be most
+appropriately viewed as an update to those implementations, rather
+than a complete rewrite.
+
+
+
+Challenges
+
+One of the difficulties in implementing a generic PWM framework is the
+fact that pulse-width-modulation applications involve real-world
+signals, which often must be carefully managed to prevent destruction
+of hardware that is linked to those signals. A DC motor that
+experiences a brief interruption in the PWM signal controlling it
+might destructively overheat; it could change speed, losing
+synchronization with a sensor; it could even suddenly change direction
+or torque, breaking the mechanical device connected to it.
+
+(A generic PWM device framework is not directly responsible for
+preventing the above scenarios: that responsibility lies with the
+hardware designer and the application and driver authors. But it must
+to the greatest extent possible make it easy to avoid such problems).
+
+A generic PWM device framework must accomodate the substantial
+differences between available PWM-generating hardware devices, without
+becoming sub-optimal for any of them.
+
+Finally, a generic PWM device framework must be relatively
+lightweight, computationally speaking. Some PWM users demand
+high-speed outputs, plus the ability to regulate those outputs
+quickly. A device framework must be able to "keep up" with such
+hardware, while still leaving time to do real work.
+
+The Generic PWM Device API is an attempt to meet all of the above
+requirements. At its initial publication, the API was already in use
+managing small DC motors through a custom-designed, optically-isolated
+H-bridge driver.
+
+
+
+Functional Overview
+
+The Generic PWM Device API framework is implemented in
+include/linux/pwm.h and drivers/pwm/pwm.c. The functions therein use
+information from pwm_device, pwm_channel and pwm_channel_config
+structures to invoke services in PWM peripheral device drivers.
+Consult drivers/pwm/atmel-pwm.c for an example driver.
+
+There are two classes of adopters of the PWM framework:
+
+ "Users" -- those wishing to employ the API merely to produce PWM
+ signals; once they have identified the appropriate physical output
+ on the platform in question, they don't care about the details of
+ the underlying hardware
+
+ "Driver authors" -- those wishing to bind devices that can generate
+ PWM signals to the Generic PWM Device API, so that the services of
+ those devices become available to users; assuming the hardware can
+ support the needs of a user, driver authors don't care about the
+ details of the user's application
+
+Generally speaking, users will first invoke pwm_request() to obtain a
+handle to a PWM device. They will then pass that handle to functions
+like pwm_duty_ns() and pwm_period_ns() to set the duty cycle and
+period of the PWM signal, respectively. They will also invoke
+pwm_start() and pwm_stop() to turn the signal on and off.
+
+The framework also provides a sysfs interface to PWM devices, which is
+adequate for basic needs and testing.
+
+Driver authors fill out a pwm_device structure, which describes the
+capabilities of the PWM hardware being driven--- including the number
+of distinct output "channels" the peripheral offers. They then invoke
+pwm_register() (usually from within their device's probe() handler) to
+make the PWM API aware of their device. The framework will call back
+to the methods described in the pwm_device structure to configure and
+use the hardware.
+
+Note that PWM signals can be produced by a variety of peripherals,
+beyond the true "PWM hardware" offered by many system-on-chip devices.
+Other possibilities include timer/counters with compare-match
+capabilities, carefully-programmed synchronous serial ports
+(e.g. SPI), and GPIO pins driven by kernel interval timers. With a
+proper pwm_device structure, these devices and pseudo-devices can all
+be accomodated by the Generic PWM Device API framework.
+
+
+
+Using the API to Generate PWM Signals -- Basic Functions for Users
+
+
+pwm_request() -- Returns a pwm_channel pointer, which is subsequently
+passed to the other user-related PWM functions. Once requested, a PWM
+channel is marked as in-use and subsequent requests prior to
+pwm_free() will fail.
+
+The names used to refer to PWM devices are defined by driver authors.
+Typically they are platform device bus identifiers, and this
+convention is encouraged for consistency.
+
+
+pwm_free() -- Marks a PWM channel as no longer in use. The PWM device
+is stopped before it is released by the API.
+
+
+pwm_period_ns() -- Specifies the PWM signal's period, in nanoseconds.
+
+
+pwm_duty_ns() -- Specifies the PWM signal's active duration, in nanoseconds.
+
+
+pwm_duty_percent() -- Specifies the PWM signal's active duration, as a
+percentage of the current period of the signal. NOTE: this value is
+not recalculated if the period of the signal is subsequently changed.
+
+
+pwm_start(), pwm_stop() -- Turns the PWM signal on and off. Except
+where stated otherwise by a driver author, signals are stopped at the
+end of the current period, at which time the output is set to its
+inactive state.
+
+
+pwm_polarity() -- Defines whether the PWM signal output's active
+region is "1" or "0". A 10% duty-cycle, polarity=1 signal will
+conventionally be at 5V (or 3.3V, or 1000V, or whatever the platform
+hardware does) for 10% of the period. The same configuration of a
+polarity=0 signal will be at 5V (or 3.3V, or ...) for 90% of the
+period.
+
+
+
+Using the API to Generate PWM Signals -- Advanced Functions
+
+
+pwm_config() -- Passes a pwm_channel_config structure to the
+associated device driver. This function is invoked by pwm_start(),
+pwm_duty_ns(), etc. and is one of two main entry points to the PWM
+driver for the hardware being used. The configuration change is
+guaranteed atomic if multiple configuration changes are specified.
+This function might sleep, depending on what the device driver has to
+do to satisfy the request. All PWM device drivers must support this
+entry point.
+
+
+pwm_config_nosleep() -- Passes a pwm_channel_config structure to the
+associated device driver. If the driver must sleep in order to
+implement the requested configuration change, -EWOULDBLOCK is
+returned. Users may call this function from interrupt handlers, for
+example. This is the other main entry point into the PWM hardware
+driver, but not all device drivers support this entry point.
+
+
+pwm_synchronize(), pwm_unsynchronize() -- "Synchronizes" two or more
+PWM channels, if the underlying hardware permits. (If it doesn't, the
+framework facilitates emulating this capability but it is not yet
+implemented). Synchronized channels will start and stop
+simultaneously when any single channel in the group is started or
+stopped. Use pwm_unsynchronize(..., NULL) to completely detach a
+channel from any other synchronized channels.
+
+
+pwm_set_handler() -- Defines an end-of-period callback. The indicated
+function will be invoked in a worker thread at the end of each PWM
+period, and can subsequently invoke pwm_config(), etc. Must be used
+with extreme care for high-speed PWM outputs. Set the handler
+function to NULL to un-set the handler.
+
+
+
+Implementing a PWM Device API Driver -- Functions for Driver Authors
+
+
+Fill out the appropriate fields in a pwm_device structure, and submit
+to pwm_register():
+
+
+bus_id -- the plaintext name of the device. Users will bind to a
+channel on the device using this name plus the channel number. For
+example, the Atmel PWMC's bus_id is "atmel_pwmc", the same as used by
+the platform device driver (recommended). The first device registered
+thereby receives bus_id "atmel_pwmc.0", which is what you put in
+pwm_device.bus_id. Channels are then named "atmel_pwmc.0:[0-3]".
+(Hint: just use pdev->dev.bus_id in your probe() method).
+
+
+nchan -- the number of distinct output channels provided by the device.
+
+
+request -- (optional) Invoked each time a user requests a channel.
+Use to turn on clocks, clean up register states, etc. The framework
+takes care of device locking/unlocking; you will see only successful
+requests.
+
+
+free -- (optional) Callback for each time a user relinquishes a
+channel. The framework will have already stopped, unsynchronized and
+un-handled the channel. Use to turn off clocks, etc. as necessary.
+
+
+synchronize, unsynchronize -- (optional) Callbacks to
+synchronize/unsynchronize channels. Some devices provide this
+capability in hardware; for others, it can be emulated (see
+atmel_pwmc.c's sync_mask for an example).
+
+
+set_callback -- (optional) Invoked when a user requests a handler. If
+the hardware supports an end-of-period interrupt, invoke the function
+indicated during your interrupt handler. The callback function itself
+is always internal to the API, and does not map directly to the user's
+callback function.
+
+
+config -- Invoked to change the device configuration, always from a
+sleep-capable context. All the changes indicated must be performed
+atomically, ideally synchronized to an end-of-period event (so that
+you avoid short or long output pulses). You may sleep, etc. as
+necessary within this function.
+
+
+config_nosleep -- (optional) Invoked to change device configuration
+from within a context that is not allowed to sleep. If you cannot
+perform the requested configuration changes without sleeping, return
+-EWOULDBLOCK.
+
+
+
+Acknowledgements
+
+
+The author expresses his gratitude to the countless developers who
+have reviewed and submitted feedback on the various versions of the
+Generic PWM Device API code, and those who have submitted drivers and
+applications that use the framework. You know who you are. ;)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9722f8bb506c..d3cb37fc7e01 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -3,7 +3,7 @@
# see Documentation/kbuild/kconfig-language.txt.
#
-mainmenu "Linux Kernel Configuration"
+#mainmenu "Linux Kernel Configuration"
config ARM
bool
@@ -421,11 +421,12 @@ config ARCH_MV78XX0
config ARCH_MXC
bool "Freescale MXC/iMX-based"
+ select ARCH_MTD_XIP
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
- select ARCH_MTD_XIP
select GENERIC_GPIO
select ARCH_REQUIRE_GPIOLIB
+ select ZONE_DMA
help
Support for Freescale MXC/iMX-based family of processors
@@ -549,6 +550,17 @@ config ARCH_MSM
interface to the ARM9 modem processor which runs the baseband stack
and controls some vital subsystems (clock and power control, etc).
+config ARCH_STMP3XXX
+ bool "SigmaTel STMP3xxx"
+ select ISA
+ select GENERIC_TIME
+ select GENERIC_CLOCKEVENTS
+ select GENERIC_GPIO
+ select USB_ARCH_HAS_EHCI
+ select ZONE_DMA
+ help
+ Support for systems based on the SigmaTel 3xxx CPUs.
+
endchoice
source "arch/arm/mach-clps711x/Kconfig"
@@ -627,6 +639,8 @@ source "arch/arm/mach-ks8695/Kconfig"
source "arch/arm/mach-msm/Kconfig"
+source "arch/arm/mach-stmp3xxx/Kconfig"
+
# Definitions to make life easier
config ARCH_ACORN
bool
@@ -657,6 +671,29 @@ if !MMU
source "arch/arm/Kconfig-nommu"
endif
+config ARM_ERRATA_364296
+ bool "Enable partial low interrupt latency mode for ARM1136"
+ depends on CPU_V6 && !SMP
+ default n
+ help
+ This options enables the workaround for the 364296 ARM1136
+ r0pX errata (possible cache data corruption with
+ hit-under-miss enabled). It sets the undocumented bit 31 in
+ the auxiliary control register and the FI bit in the control
+ register, thus disabling hit-under-miss without putting the
+ processor into full low interrupt latency mode. ARM11MPCore
+ is not affected.
+
+config ARM_ERRATA_411920
+ bool "Workaround for the global I cache invalidation on ARM1136"
+ depends on CPU_V6 && !SMP
+ default n
+ help
+ Invalidation of the Instruction Cache operation can
+ fail. This Erratum is present in 1136, 1156 and 1176. It
+ does not affect the MPCore. This option enables the ARM Ltd.
+ recommended workaround.
+
endmenu
source "arch/arm/common/Kconfig"
@@ -876,7 +913,7 @@ config LEDS
ARCH_OMAP || ARCH_P720T || ARCH_PXA_IDP || \
ARCH_SA1100 || ARCH_SHARK || ARCH_VERSATILE || \
ARCH_AT91 || ARCH_DAVINCI || \
- ARCH_KS8695 || MACH_RD88F5182
+ ARCH_KS8695 || MACH_RD88F5182 || ARCH_NS9XXX || ARCH_MXC
help
If you say Y here, the LEDs on your machine will be used
to provide useful information about your current system status.
@@ -1037,7 +1074,7 @@ endmenu
menu "CPU Power Management"
-if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA)
+if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA || ARCH_MXC || ARCH_STMP3XXX || ARCH_NS9XXX)
source "drivers/cpufreq/Kconfig"
@@ -1077,6 +1114,12 @@ config CPU_FREQ_PXA
default y
select CPU_FREQ_DEFAULT_GOV_USERSPACE
+config CPU_FREQ_IMX
+ tristate "CPUfreq driver for i.MX CPUs"
+ depends on ARCH_MXC && CPU_FREQ && REGULATOR
+ help
+ This enables the CPUfreq driver for i.MX CPUs.
+
endif
source "drivers/cpuidle/Kconfig"
@@ -1224,10 +1267,14 @@ source "drivers/char/Kconfig"
source "drivers/i2c/Kconfig"
+source "drivers/i2c-slave/Kconfig"
+
source "drivers/spi/Kconfig"
source "drivers/gpio/Kconfig"
+source "drivers/pwm/Kconfig"
+
source "drivers/w1/Kconfig"
source "drivers/power/Kconfig"
@@ -1276,6 +1323,12 @@ source "drivers/regulator/Kconfig"
source "drivers/uio/Kconfig"
+source "drivers/fims/Kconfig"
+
+if ARCH_MXC
+source "drivers/mxc/Kconfig"
+endif
+
endmenu
source "fs/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index bd6e28115ebb..fd8ab07fc234 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -139,6 +139,15 @@ endif
plat-$(CONFIG_ARCH_MXC) := mxc
machine-$(CONFIG_ARCH_MX2) := mx2
machine-$(CONFIG_ARCH_MX3) := mx3
+ machine-$(CONFIG_ARCH_MX35) := mx35
+ machine-$(CONFIG_ARCH_MX37) := mx37
+ machine-$(CONFIG_ARCH_MX51) := mx51
+ machine-$(CONFIG_ARCH_MX27) := mx27
+ machine-$(CONFIG_ARCH_MX25) := mx25
+ machine-$(CONFIG_ARCH_MX21) := mx21
+ machine-$(CONFIG_ARCH_STMP3XXX) := stmp3xxx
+ incdir-$(CONFIG_ARCH_STMP37XX) := stmp37xx
+ incdir-$(CONFIG_ARCH_STMP378X) := stmp378x
machine-$(CONFIG_ARCH_ORION5X) := orion5x
plat-$(CONFIG_PLAT_ORION) := orion
machine-$(CONFIG_ARCH_MSM) := msm
@@ -214,7 +223,7 @@ maketools: include/linux/version.h FORCE
# Convert bzImage to zImage
bzImage: zImage
-zImage Image xipImage bootpImage uImage: vmlinux
+zImage Image xipImage bootpImage uImage uImagegz: vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
zinstall install: vmlinux
@@ -237,6 +246,7 @@ define archhelp
echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
echo '* xipImage - XIP kernel image, if configured (arch/$(ARCH)/boot/xipImage)'
echo ' uImage - U-Boot wrapped zImage'
+ echo " uImagegz - U-Boot wrapped gzip'ed Image (without decompressor)"
echo ' bootpImage - Combined zImage and initial RAM disk'
echo ' (supply initrd image via make variable INITRD=<path>)'
echo ' install - Install uncompressed kernel'
diff --git a/arch/arm/boot/.gitignore b/arch/arm/boot/.gitignore
index ce1c5ff746e7..04731416ac12 100644
--- a/arch/arm/boot/.gitignore
+++ b/arch/arm/boot/.gitignore
@@ -3,3 +3,5 @@ zImage
xipImage
bootpImage
uImage
+Image.gz
+uImagegz
diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
index da226abce2d0..3f5c78a41359 100644
--- a/arch/arm/boot/Makefile
+++ b/arch/arm/boot/Makefile
@@ -27,7 +27,7 @@ INITRD_PHYS := $(initrd_phys-y)
export ZRELADDR INITRD_PHYS PARAMS_PHYS
-targets := Image zImage xipImage bootpImage uImage
+targets := Image zImage xipImage bootpImage uImage Image.gz uImagegz
ifeq ($(CONFIG_XIP_KERNEL),y)
@@ -74,6 +74,18 @@ $(obj)/uImage: $(obj)/zImage FORCE
$(call if_changed,uimage)
@echo ' Image $@ is ready'
+quiet_cmd_uimagegz = UIMAGE $@
+ cmd_uimagegz = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel \
+ -C gzip -a $(ZRELADDR) -e $(ZRELADDR) \
+ -n 'Linux-$(KERNELRELEASE)' -d $< $@
+
+$(obj)/Image.gz: $(obj)/Image FORCE
+ $(call if_changed,gzip)
+
+$(obj)/uImagegz: $(obj)/Image.gz FORCE
+ $(call if_changed,uimagegz)
+ @echo ' Image $@ is ready'
+
$(obj)/bootp/bootp: $(obj)/zImage initrd FORCE
$(Q)$(MAKE) $(build)=$(obj)/bootp $@
@:
diff --git a/arch/arm/configs/imx233_defconfig b/arch/arm/configs/imx233_defconfig
new file mode 100644
index 000000000000..e94c4076f021
--- /dev/null
+++ b/arch/arm/configs/imx233_defconfig
@@ -0,0 +1,1688 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28
+# Fri Jun 12 19:40:44 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_NO_IOPORT is not set
+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_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_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# 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=y
+# 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
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED 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 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 is not set
+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
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=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_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=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# 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_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_CLASSIC_RCU=y
+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_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE 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
+# 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_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_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+CONFIG_ARCH_STMP3XXX=y
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# SigmaTel STMP3xxx implementations
+#
+# CONFIG_ARCH_STMP37XX is not set
+CONFIG_ARCH_STMP378X=y
+# CONFIG_MACH_STMP37XX is not set
+CONFIG_MACH_STMP378X=y
+CONFIG_FB_STMP37XX_LMS350=y
+CONFIG_FB_STMP37XX_LMS430=y
+CONFIG_FB_STMP378X_TVENC=y
+CONFIG_STMP3XXX_UNIQUE_ID=y
+CONFIG_STMP3XXX_UNIQUE_ID_OTP=y
+CONFIG_STMP378X_RAM_FREQ_SCALING=y
+# CONFIG_STMP378X_RAM_MDDR is not set
+CONFIG_STMP378X_RAM_DDR=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
+
+#
+# Power management
+#
+CONFIG_UTMI_MXC=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 is not set
+
+#
+# Bus support
+#
+CONFIG_ISA=y
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+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_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL 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_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_UNEVICTABLE_LRU=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyAM0,115200 root=/dev/mmcblk0p2 rootwait lcd_panel=lms350"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+# 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_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=y
+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 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=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE 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=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_NETLABEL 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_NET_SCHED=y
+
+#
+# Queueing/Scheduling
+#
+# CONFIG_NET_SCH_CBQ is not set
+# CONFIG_NET_SCH_HTB is not set
+# CONFIG_NET_SCH_HFSC is not set
+# CONFIG_NET_SCH_PRIO is not set
+# CONFIG_NET_SCH_MULTIQ is not set
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFQ is not set
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_DSMARK is not set
+# CONFIG_NET_SCH_NETEM is not set
+
+#
+# Classification
+#
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_TCINDEX is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_FW is not set
+# CONFIG_NET_CLS_U32 is not set
+# CONFIG_NET_CLS_RSVP is not set
+# CONFIG_NET_CLS_RSVP6 is not set
+# CONFIG_NET_CLS_FLOW is not set
+# CONFIG_NET_EMATCH is not set
+# CONFIG_NET_CLS_ACT is not set
+CONFIG_NET_SCH_FIFO=y
+
+#
+# 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_PHONET is not set
+CONFIG_FIB_RULES=y
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_OLD_REGULATORY=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=y
+# CONFIG_IEEE80211_DEBUG is not set
+# CONFIG_IEEE80211_CRYPT_WEP is not set
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP 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=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+# 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 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_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# 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_NANDSIM is not set
+# CONFIG_MTD_NAND_IMX_NFC is not set
+# CONFIG_MTD_NAND_GPMI_LBA is not set
+CONFIG_MTD_NAND_GPMI=y
+CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES=y
+CONFIG_MTD_NAND_GPMI_BCH=y
+CONFIG_MTD_NAND_GPMI_TA1=y
+CONFIG_MTD_NAND_GPMI_TA3=y
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_WL_THRESHOLD=128
+CONFIG_MTD_UBI_BEB_RESERVE=2
+CONFIG_MTD_UBI_GLUEBI=y
+
+#
+# UBI debugging options
+#
+# CONFIG_MTD_UBI_DEBUG is not set
+# CONFIG_PARPORT is not set
+# CONFIG_PNP 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_UB 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=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
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# 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_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_ARCNET is not set
+# 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_ENC28J60=y
+# CONFIG_ENC28J60_WRITEVERIFY is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_NET_VENDOR_RACAL 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_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_TR is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# 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 is not set
+# CONFIG_SLIP is not set
+# 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=y
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=320
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=240
+# 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_KEYBOARD_STMP3XXX=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_STMP3XXX=y
+# CONFIG_TOUCHSCREEN_HTCPEN is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_STMP3XXX_ROTDEC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_CM109 is not set
+# CONFIG_INPUT_UINPUT is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+# CONFIG_SERIO_LIBPS2 is not set
+# 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_MXS_VIIM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_STMP_DBG=y
+CONFIG_SERIAL_STMP_DBG_CONSOLE=y
+CONFIG_SERIAL_STMP_APP=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_DEVPORT=y
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_ALGOBIT=m
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_GPIO is not set
+CONFIG_I2C_STMP378X=m
+# 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_ELEKTOR is not set
+# CONFIG_I2C_PCA_ISA is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 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_I2C_SLAVE is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+# CONFIG_SPI_BITBANG is not set
+CONFIG_SPI_STMP3XXX=y
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+# CONFIG_W1 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_BATTERY_BQ27x00 is not set
+CONFIG_BATTERY_STMP3XXX=y
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_STMP3XXX_WATCHDOG=y
+
+#
+# ISA-based Watchdog Cards
+#
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_MIXCOMWD is not set
+# CONFIG_WDT is not set
+
+#
+# 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_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_COMMON=y
+# CONFIG_VIDEO_ALLOW_V4L1 is not set
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=y
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEOBUF_GEN=y
+CONFIG_VIDEOBUF_DMA_CONTIG=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set
+
+#
+# Encoders/decoders and other helper chips
+#
+
+#
+# Audio decoders
+#
+# CONFIG_VIDEO_TVAUDIO is not set
+# CONFIG_VIDEO_TDA7432 is not set
+# CONFIG_VIDEO_TDA9840 is not set
+# CONFIG_VIDEO_TDA9875 is not set
+# CONFIG_VIDEO_TEA6415C is not set
+# CONFIG_VIDEO_TEA6420 is not set
+# CONFIG_VIDEO_MSP3400 is not set
+# CONFIG_VIDEO_CS5345 is not set
+# CONFIG_VIDEO_CS53L32A is not set
+# CONFIG_VIDEO_M52790 is not set
+# CONFIG_VIDEO_TLV320AIC23B is not set
+# CONFIG_VIDEO_WM8775 is not set
+# CONFIG_VIDEO_WM8739 is not set
+# CONFIG_VIDEO_VP27SMPX is not set
+
+#
+# Video decoders
+#
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_TCM825X is not set
+# CONFIG_VIDEO_SAA711X is not set
+# CONFIG_VIDEO_SAA717X is not set
+# CONFIG_VIDEO_TVP5150 is not set
+
+#
+# Video and audio decoders
+#
+# CONFIG_VIDEO_CX25840 is not set
+
+#
+# MPEG video encoders
+#
+# CONFIG_VIDEO_CX2341X is not set
+
+#
+# Video encoders
+#
+# CONFIG_VIDEO_SAA7127 is not set
+
+#
+# Video improvement chips
+#
+# CONFIG_VIDEO_UPD64031A is not set
+# CONFIG_VIDEO_UPD64083 is not set
+# CONFIG_VIDEO_VIVI is not set
+CONFIG_VIDEO_PXP=y
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_SOC_CAMERA is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_GSPCA=m
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_S2255 is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_SI470X is not set
+# CONFIG_USB_MR800 is not set
+CONFIG_RADIO_STFM1000=m
+CONFIG_RADIO_STFM1000_ALSA=m
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+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
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_STMP37XX=y
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+# CONFIG_LCD_PLATFORM is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_STMP37XX=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_SOUND=y
+# CONFIG_SOUND_OSS_CORE is not set
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+# CONFIG_SND_MIXER_OSS is not set
+# CONFIG_SND_PCM_OSS is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+# CONFIG_SND_SUPPORT_OLD_API is not set
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+# CONFIG_SND_DRIVERS is not set
+# CONFIG_SND_ARM is not set
+# CONFIG_SND_SPI is not set
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_STMP3XXX_SOC=y
+CONFIG_SND_STMP3XXX_SOC_DAI=y
+CONFIG_SND_STMP3XXX_SOC_SPDIF_DAI=y
+CONFIG_SND_STMP3XXX_SOC_STMP3780_DEVB=y
+CONFIG_SND_STMP3XXX_SOC_STMP3780_DEVB_SPDIF=y
+# CONFIG_SND_SOC_ALL_CODECS is not set
+CONFIG_SND_SOC_STMP378X_CODEC=y
+CONFIG_SND_SOC_STMP3XXX_SPDIF=y
+# CONFIG_SOUND_PRIME is not set
+# CONFIG_HID_SUPPORT 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=y
+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=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# 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_EHCI_HCD=m
+CONFIG_USB_EHCI_ARC=y
+CONFIG_USB_EHCI_ARC_OTG=y
+# CONFIG_USB_STATIC_IRAM is not set
+# CONFIG_USB_EHCI_FSL_MC13783 is not set
+# CONFIG_USB_EHCI_FSL_1301 is not set
+# CONFIG_USB_EHCI_FSL_1504 is not set
+CONFIG_USB_EHCI_FSL_UTMI=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD 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 information
+#
+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_DPCM 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_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA 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=m
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# 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_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_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+CONFIG_USB_GADGET_ARC=y
+CONFIG_USB_ARC=m
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+CONFIG_USB_GADGET_ARC_OTG=y
+# CONFIG_USB_GADGET_FSL_MC13783 is not set
+# CONFIG_USB_GADGET_FSL_1301 is not set
+# CONFIG_USB_GADGET_FSL_1504 is not set
+CONFIG_USB_GADGET_FSL_UTMI=y
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_GADGETFS is not set
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# 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_SPI is not set
+# CONFIG_MMC_IMX_ESDHCI_PIO_MODE is not set
+CONFIG_MMC_STMP3XXX=y
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_NEW_LEDS=y
+# CONFIG_LEDS_CLASS is not set
+
+#
+# LED drivers
+#
+
+#
+# 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_DEFAULT_ON=y
+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=y
+# 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
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+
+#
+# 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_STMP3XXX=y
+# CONFIG_DMADEVICES is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+CONFIG_REGULATOR_STMP3XXX=y
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+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_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+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
+
+#
+# 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
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_ECRYPT_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_UBIFS_FS=y
+# CONFIG_UBIFS_FS_XATTR is not set
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_ZLIB=y
+# CONFIG_UBIFS_FS_DEBUG is not set
+# CONFIG_CRAMFS 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_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_SUNRPC_REGISTER_V4 is not set
+# 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 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 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 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=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+CONFIG_KEYS=y
+CONFIG_KEYS_DEBUG_PROC_KEYS=y
+CONFIG_SECURITY=y
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_NETWORK is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_SECURITY_ROOTPLUG is not set
+CONFIG_SECURITY_DEFAULT_MMAP_MIN_ADDR=0
+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_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+CONFIG_CRYPTO_TEST=m
+CONFIG_CRYPTO_CRYPTODEV=y
+
+#
+# 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=y
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+CONFIG_CRYPTO_HMAC=y
+# 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=m
+# 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=y
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# 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=y
+CONFIG_CRYPTO_LZO=y
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+CONFIG_CRYPTO_DEV_STMP3XXX_DCP=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=m
+CONFIG_CRC16=y
+# 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_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/imx233_updater_defconfig b/arch/arm/configs/imx233_updater_defconfig
new file mode 100644
index 000000000000..2064ee1c51d6
--- /dev/null
+++ b/arch/arm/configs/imx233_updater_defconfig
@@ -0,0 +1,1029 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28
+# Fri Oct 23 09:29:11 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_NO_IOPORT is not set
+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_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_ZONE_DMA=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION="-updater"
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+CONFIG_RELAY=y
+# CONFIG_NAMESPACES is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+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 is not set
+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
+# CONFIG_VM_EVENT_COUNTERS is not set
+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_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_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=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_CLASSIC_RCU=y
+# 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_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE 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
+# 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_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_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+CONFIG_ARCH_STMP3XXX=y
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+CONFIG_DMA_ZONE_SIZE=12
+CONFIG_UTMI_MXC=y
+
+#
+# SigmaTel STMP3xxx implementations
+#
+# CONFIG_ARCH_STMP37XX is not set
+CONFIG_ARCH_STMP378X=y
+# CONFIG_MACH_STMP37XX is not set
+CONFIG_MACH_STMP378X=y
+# CONFIG_FB_STMP37XX_LMS350 is not set
+CONFIG_FB_STMP37XX_LMS430=y
+# CONFIG_FB_STMP378X_TVENC is not set
+# CONFIG_STMP3XXX_UNIQUE_ID is not set
+# CONFIG_STMP378X_RAM_FREQ_SCALING 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
+# CONFIG_OUTER_CACHE is not set
+
+#
+# Bus support
+#
+CONFIG_ISA=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 is not set
+CONFIG_HZ=100
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL 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_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_UNEVICTABLE_LRU=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyAM0,115200 rdinit=/linuxrc"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_FREQ is not set
+# 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_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_SUSPEND is not set
+# CONFIG_APM_EMULATION 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 is not set
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+# 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 is not set
+# CONFIG_MTD_BLOCK_RO is not set
+# 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_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# 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_NANDSIM is not set
+# CONFIG_MTD_NAND_IMX_NFC is not set
+# CONFIG_MTD_NAND_GPMI_LBA is not set
+CONFIG_MTD_NAND_GPMI=y
+CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES=y
+CONFIG_MTD_NAND_GPMI_BCH=y
+CONFIG_MTD_NAND_GPMI_TA1=y
+CONFIG_MTD_NAND_GPMI_TA3=y
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_RESERVE=1
+# CONFIG_MTD_UBI_GLUEBI is not set
+
+#
+# UBI debugging options
+#
+# CONFIG_MTD_UBI_DEBUG is not set
+# CONFIG_PARPORT is not set
+# CONFIG_PNP is not set
+# CONFIG_BLK_DEV 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
+
+#
+# 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=y
+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
+# CONFIG_MXS_VIIM is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_STMP_DBG=y
+CONFIG_SERIAL_STMP_DBG_CONSOLE=y
+# CONFIG_SERIAL_STMP_APP is not set
+# CONFIG_UNIX98_PTYS is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_DEVPORT=y
+# CONFIG_I2C is not set
+# CONFIG_I2C_SLAVE is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+# CONFIG_SPI_BITBANG is not set
+CONFIG_SPI_STMP3XXX=y
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 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
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+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
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_STMP37XX=y
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+CONFIG_LCD_PLATFORM=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_STMP37XX=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+# CONFIG_LOGO is not set
+# 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 is not set
+CONFIG_USB_ARCH_HAS_EHCI=y
+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_SUSPEND is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB 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_EHCI_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD 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 information
+#
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 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_PHIDGET 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 is not set
+# 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_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_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+CONFIG_USB_GADGET_ARC=y
+CONFIG_USB_ARC=y
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+CONFIG_USB_GADGET_ARC_OTG=y
+# CONFIG_USB_GADGET_FSL_MC13783 is not set
+# CONFIG_USB_GADGET_FSL_1301 is not set
+# CONFIG_USB_GADGET_FSL_1504 is not set
+CONFIG_USB_GADGET_FSL_UTMI=y
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_ETH is not set
+# CONFIG_USB_GADGETFS is not set
+CONFIG_USB_FILE_STORAGE=y
+CONFIG_STMP_UTP=y
+# CONFIG_USB_FILE_STORAGE_TEST 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
+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_SPI is not set
+# CONFIG_MMC_IMX_ESDHCI_PIO_MODE is not set
+CONFIG_MMC_STMP3XXX=y
+# 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_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+CONFIG_REGULATOR_STMP3XXX=y
+# CONFIG_UIO 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 is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_DNOTIFY 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
+
+#
+# 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 is not set
+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 is not set
+# CONFIG_PROC_PAGE_MONITOR is not set
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# 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_UBIFS_FS=y
+CONFIG_UBIFS_FS_XATTR=y
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+CONFIG_UBIFS_FS_LZO=y
+# CONFIG_UBIFS_FS_ZLIB is not set
+# CONFIG_UBIFS_FS_DEBUG is not set
+# CONFIG_CRAMFS 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
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# 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 is not set
+# 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 is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# 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 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=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG 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=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+# 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
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_CRYPTODEV 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 is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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=y
+CONFIG_CRYPTO_LZO=y
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_CRYPTO_DEV_STMP3XXX_DCP is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC16=y
+# 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_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/imx25_3stack_defconfig b/arch/arm/configs/imx25_3stack_defconfig
new file mode 100644
index 000000000000..c76198147662
--- /dev/null
+++ b/arch/arm/configs/imx25_3stack_defconfig
@@ -0,0 +1,1725 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28
+# Fri Aug 21 12:15:05 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+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
+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_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_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"
+
+#
+# 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=y
+CONFIG_SWAP=y
+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
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED 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 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 is not set
+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
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+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=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# 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_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_CLASSIC_RCU=y
+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_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE 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
+# 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_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=y
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_STMP3XXX is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Freescale MXC Implementations
+#
+# CONFIG_ARCH_MX37 is not set
+# CONFIG_ARCH_MX35 is not set
+# CONFIG_ARCH_MX51 is not set
+# CONFIG_ARCH_MX3 is not set
+# CONFIG_ARCH_MX27 is not set
+CONFIG_ARCH_MX25=y
+CONFIG_I2C_MXC_SELECT1=y
+# CONFIG_I2C_MXC_SELECT2 is not set
+
+#
+# MX25 Options
+#
+CONFIG_MX25_OPTIONS=y
+CONFIG_MACH_MX25_3DS=y
+# CONFIG_MX25_DOZE_DURING_IDLE is not set
+CONFIG_MXC_SDMA_API=y
+
+#
+# SDMA options
+#
+CONFIG_SDMA_IRAM=y
+CONFIG_SDMA_IRAM_SIZE=0x1000
+CONFIG_ARCH_MXC_HAS_NFC_V2=y
+CONFIG_ARCH_MXC_HAS_NFC_V2_1=y
+
+#
+# Device options
+#
+# CONFIG_I2C_MXC_SELECT3 is not set
+# CONFIG_FLEXCAN_MXC_SELECT1 is not set
+CONFIG_DMA_ZONE_SIZE=24
+CONFIG_UTMI_MXC=y
+CONFIG_UTMI_MXC_OTG=m
+# CONFIG_MXC_PWM is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM926T=y
+# CONFIG_CPU_V6 is not set
+# CONFIG_CPU_V7 is not set
+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 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_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+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_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL 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_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_UNEVICTABLE_LRU=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/mtdblock2 rw rootfstype=jffs2 ip=off"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_IMX=y
+# 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_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=y
+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 is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE 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=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_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+CONFIG_CAN=y
+CONFIG_CAN_RAW=y
+# CONFIG_CAN_BCM is not set
+
+#
+# CAN Device Drivers
+#
+# CONFIG_CAN_VCAN is not set
+# CONFIG_CAN_DEBUG_DEVICES is not set
+CONFIG_CAN_FLEXCAN=y
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=m
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+# CONFIG_MCS_FIR is not set
+# CONFIG_MXC_FIR is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_OLD_REGULATORY=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=y
+# CONFIG_IEEE80211_DEBUG is not set
+# CONFIG_IEEE80211_CRYPT_WEP is not set
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP 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=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_REDBOOT_PARTS=y
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY 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 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set
+# 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=y
+# 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
+# CONFIG_MTD_XIP 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
+# CONFIG_MTD_MXC is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# 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_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_IMX_NFC is not set
+CONFIG_MTD_NAND_MXC_V2=y
+# CONFIG_MTD_NAND_MXC_SWECC is not set
+# CONFIG_MTD_NAND_MXC_FORCE_CE is not set
+# CONFIG_MXC_NAND_LOW_LEVEL_ERASE is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND 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 is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT 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
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+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=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH 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 is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_SMC911X is not set
+CONFIG_SMSC911X=y
+# 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_CS89x0 is not set
+CONFIG_FEC=m
+# CONFIG_FEC2 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
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+CONFIG_USB_RTL8150=m
+CONFIG_USB_USBNET=m
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_CDCETHER=m
+# CONFIG_USB_NET_DM9601 is not set
+# CONFIG_USB_NET_SMSC95XX is not set
+# CONFIG_USB_NET_GL620A is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# 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 is not set
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# 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 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_MXC=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_IMX_ADC=y
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 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
+CONFIG_MXC_IIM=y
+CONFIG_IMX_SIM=m
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_MXC=y
+CONFIG_SERIAL_MXC_CONSOLE=y
+# CONFIG_SERIAL_IMX is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM 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 is not set
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+CONFIG_I2C_MXC=y
+# CONFIG_I2C_MXC_HS 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
+# 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_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 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_I2C_SLAVE is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_MXC=y
+# CONFIG_SPI_MXC_TEST_LOOPBACK is not set
+CONFIG_SPI_MXC_SELECT1=y
+# CONFIG_SPI_MXC_SELECT2 is not set
+# CONFIG_SPI_MXC_SELECT3 is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 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=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_MXC_WATCHDOG is not set
+
+#
+# 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_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_COMMON=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=y
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+CONFIG_VIDEO_MXC_CAMERA=m
+
+#
+# MXC Camera/V4L2 PRP Features support
+#
+CONFIG_VIDEO_MXC_CSI_CAMERA=m
+# CONFIG_MXC_CAMERA_MC521DA is not set
+# CONFIG_MXC_EMMA_CAMERA_MICRON111 is not set
+# CONFIG_MXC_CAMERA_OV2640_EMMA is not set
+# CONFIG_MXC_CAMERA_MICRON111 is not set
+CONFIG_MXC_CAMERA_OV2640=m
+# CONFIG_MXC_CAMERA_OV3640 is not set
+# CONFIG_MXC_TVIN_ADV7180 is not set
+# CONFIG_VIDEO_MXC_OUTPUT is not set
+# CONFIG_VIDEO_MXC_OPL is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_SOC_CAMERA is not set
+# CONFIG_V4L_USB_DRIVERS is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_SI470X is not set
+# CONFIG_USB_MR800 is not set
+CONFIG_DAB=y
+# CONFIG_USB_DABUSB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+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
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+CONFIG_FB_MXC=y
+CONFIG_FB_MXC_SYNC_PANEL=y
+# CONFIG_FB_MXC_EPSON_VGA_SYNC_PANEL is not set
+# CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL is not set
+# CONFIG_FB_MXC_CH7026 is not set
+# CONFIG_FB_MXC_TVOUT is not set
+# CONFIG_FB_MXC_TVOUT_CH7024 is not set
+# CONFIG_FB_MXC_ASYNC_PANEL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_MXC=y
+CONFIG_BACKLIGHT_MXC_LCDC=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_SOUND=y
+CONFIG_SOUND_OSS_CORE=y
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+CONFIG_SND_ARM=y
+# CONFIG_SND_MXC_SPDIF is not set
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_MXC_SOC=y
+CONFIG_SND_MXC_SOC_SSI=y
+CONFIG_SND_MXC_SOC_ESAI=m
+CONFIG_SND_MXC_SOC_IRAM=y
+CONFIG_SND_SOC_IMX_3STACK_SGTL5000=y
+# CONFIG_SND_SOC_IMX_3STACK_AK4647 is not set
+CONFIG_SND_SOC_IMX_3STACK_WM8580=m
+CONFIG_SND_SOC_IMX_3STACK_AK5702=m
+# CONFIG_SND_SOC_IMX_3STACK_BLUETOOTH is not set
+# CONFIG_SND_SOC_ALL_CODECS is not set
+CONFIG_SND_SOC_AK5702=m
+CONFIG_SND_SOC_WM8580=m
+CONFIG_SND_SOC_SGTL5000=y
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# Special HID drivers
+#
+CONFIG_HID_COMPAT=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_BRIGHT=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DELL=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_LOGITECH=y
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_PANTHERLORD=y
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_ZEROPLUS_FF 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=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_DEVICE_CLASS is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_OTG=y
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB 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_EHCI_HCD=m
+CONFIG_USB_EHCI_ARC=y
+CONFIG_USB_EHCI_ARC_H2=y
+CONFIG_USB_EHCI_ARC_OTG=y
+# CONFIG_USB_STATIC_IRAM is not set
+# CONFIG_USB_EHCI_FSL_MC13783 is not set
+# CONFIG_USB_EHCI_FSL_1301 is not set
+# CONFIG_USB_EHCI_FSL_1504 is not set
+CONFIG_USB_EHCI_FSL_UTMI=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD 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 information
+#
+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_DPCM 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_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+CONFIG_USB_GADGET=m
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# 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_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_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+CONFIG_USB_GADGET_ARC=y
+CONFIG_USB_STATIC_IRAM_PPH=y
+CONFIG_USB_ARC=m
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+CONFIG_USB_GADGET_ARC_OTG=y
+# CONFIG_USB_GADGET_FSL_MC13783 is not set
+# CONFIG_USB_GADGET_FSL_1301 is not set
+# CONFIG_USB_GADGET_FSL_1504 is not set
+CONFIG_USB_GADGET_FSL_UTMI=y
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_GADGETFS is not set
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# 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_SPI is not set
+# CONFIG_MMC_MXC is not set
+CONFIG_MMC_IMX_ESDHCI=y
+# CONFIG_MMC_IMX_ESDHCI_SELECT2 is not set
+# CONFIG_MMC_IMX_ESDHCI_PIO_MODE 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
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+
+#
+# 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_MXC is not set
+# CONFIG_RTC_DRV_MXC_V2 is not set
+CONFIG_RTC_DRV_IMXDI=y
+# CONFIG_DMADEVICES is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+CONFIG_REGULATOR_MC34704=y
+# CONFIG_UIO is not set
+
+#
+# MXC support drivers
+#
+
+#
+# MXC SSI support
+#
+# CONFIG_MXC_SSI is not set
+
+#
+# MXC Digital Audio Multiplexer support
+#
+# CONFIG_MXC_DAM is not set
+
+#
+# MXC PMIC support
+#
+CONFIG_MXC_PMIC=y
+# CONFIG_MXC_PMIC_MC13783 is not set
+# CONFIG_MXC_PMIC_MC13892 is not set
+CONFIG_MXC_PMIC_MC34704=y
+# CONFIG_MXC_PMIC_MC9SDZ60 is not set
+CONFIG_MXC_PMIC_CHARDEV=y
+
+#
+# MXC PMIC Client Drivers
+#
+# CONFIG_MXC_PMIC_MC9S08DZ60 is not set
+
+#
+# MXC Security Drivers
+#
+# CONFIG_MXC_SECURITY_SCC is not set
+# CONFIG_MXC_SECURITY_RNG is not set
+# CONFIG_MXC_DRYICE is not set
+
+#
+# MXC MPEG4 Encoder Kernel module support
+#
+# CONFIG_MXC_HMP4E is not set
+
+#
+# MXC HARDWARE EVENT
+#
+# CONFIG_MXC_HWEVENT is not set
+
+#
+# MXC VPU(Video Processing Unit) support
+#
+
+#
+# MXC Asynchronous Sample Rate Converter support
+#
+
+#
+# MXC Bluetooth support
+#
+
+#
+# Broadcom GPS ioctrl support
+#
+
+#
+# MXC Media Local Bus Driver
+#
+
+#
+# i.MX ADC support
+#
+CONFIG_IMX_ADC=y
+
+#
+# File systems
+#
+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_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_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+# CONFIG_FUSE_FS 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
+
+#
+# Miscellaneous filesystems
+#
+# 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=y
+# 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_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_SUNRPC_REGISTER_V4 is not set
+# 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 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=m
+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=m
+# 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_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=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG 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=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
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_CRYPTODEV 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 is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=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_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/imx27ads_defconfig b/arch/arm/configs/imx27ads_defconfig
index bcd95b8dd2df..9f5569365ebd 100644
--- a/arch/arm/configs/imx27ads_defconfig
+++ b/arch/arm/configs/imx27ads_defconfig
@@ -1,17 +1,18 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.26-rc6
-# Fri Jun 20 16:29:34 2008
+# Linux kernel version: 2.6.28
+# Wed Apr 22 10:24:31 2009
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
-CONFIG_GENERIC_GPIO=y
+# CONFIG_GENERIC_GPIO is not set
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
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_HARDIRQS_SW_RESEND=y
@@ -21,9 +22,9 @@ CONFIG_RWSEM_GENERIC_SPINLOCK=y
# 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"
@@ -36,14 +37,15 @@ CONFIG_LOCK_KERNEL=y
CONFIG_INIT_ENV_ARG_LIMIT=32
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
-# CONFIG_SWAP is not set
+CONFIG_SWAP=y
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
-CONFIG_POSIX_MQUEUE=y
+# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_TASKSTATS is not set
# CONFIG_AUDIT is not set
-# CONFIG_IKCONFIG is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
# CONFIG_CGROUPS is not set
# CONFIG_GROUP_SCHED is not set
@@ -52,14 +54,13 @@ CONFIG_SYSFS_DEPRECATED_V2=y
# CONFIG_RELAY is not set
# CONFIG_NAMESPACES is not set
# CONFIG_BLK_DEV_INITRD is not set
-# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSCTL=y
CONFIG_EMBEDDED=y
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
CONFIG_KALLSYMS=y
-CONFIG_KALLSYMS_EXTRA_PASS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
@@ -73,18 +74,20 @@ CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
+CONFIG_AIO=y
CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_SLAB=y
-# CONFIG_SLUB is not set
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
# CONFIG_SLOB is not set
-# CONFIG_PROFILING is not set
+CONFIG_PROFILING=y
# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=y
CONFIG_HAVE_OPROFILE=y
# CONFIG_KPROBES is not set
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
-# CONFIG_HAVE_DMA_ATTRS is not set
-CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
@@ -92,29 +95,31 @@ 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_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
# CONFIG_MODULE_SRCVERSION_ALL is not set
-# CONFIG_KMOD 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_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_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 is not set
-CONFIG_DEFAULT_NOOP=y
-CONFIG_DEFAULT_IOSCHED="noop"
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
CONFIG_CLASSIC_RCU=y
+CONFIG_FREEZER=y
#
# System Type
@@ -126,7 +131,6 @@ CONFIG_CLASSIC_RCU=y
# CONFIG_ARCH_AT91 is not set
# CONFIG_ARCH_CLPS7500 is not set
# CONFIG_ARCH_CLPS711X is not set
-# CONFIG_ARCH_CO285 is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
@@ -140,8 +144,11 @@ CONFIG_CLASSIC_RCU=y
# 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_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=y
# CONFIG_ARCH_ORION5X is not set
# CONFIG_ARCH_PNX4008 is not set
@@ -153,7 +160,7 @@ CONFIG_ARCH_MXC=y
# CONFIG_ARCH_LH7A40X is not set
# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
-# CONFIG_ARCH_MSM7X00A is not set
+# CONFIG_ARCH_MSM is not set
#
# Boot options
@@ -166,25 +173,40 @@ CONFIG_ARCH_MXC=y
#
# Freescale MXC Implementations
#
-CONFIG_ARCH_MX2=y
+# CONFIG_ARCH_MX37 is not set
+# CONFIG_ARCH_MX35 is not set
+# CONFIG_ARCH_MX51 is not set
# CONFIG_ARCH_MX3 is not set
+CONFIG_ARCH_MX27=y
+# CONFIG_ARCH_MX25 is not set
#
-# MX2 family CPU support
+# MX27 Options
#
-CONFIG_MACH_MX27=y
+CONFIG_MX27_OPTIONS=y
+CONFIG_MACH_MX27ADS=y
+CONFIG_ARCH_MXC_HAS_NFC_V1=y
#
-# MX2 Platforms
-#
-CONFIG_MACH_MX27ADS=y
-# CONFIG_MACH_PCM038 is not set
+# Device options
+#
+CONFIG_I2C_MXC_SELECT1=y
+# CONFIG_I2C_MXC_SELECT2 is not set
+CONFIG_MXC_EMMA=y
+CONFIG_DMA_ZONE_SIZE=24
+CONFIG_ISP1301_MXC=y
+CONFIG_MXC_USB_SU6=y
+# CONFIG_MXC_USB_SB3 is not set
+# CONFIG_MXC_USB_DU6 is not set
+# CONFIG_MXC_USB_DB4 is not set
#
# Processor Type
#
CONFIG_CPU_32=y
CONFIG_CPU_ARM926T=y
+# CONFIG_CPU_V6 is not set
+# CONFIG_CPU_V7 is not set
CONFIG_CPU_32v5=y
CONFIG_CPU_ABRT_EV5TJ=y
CONFIG_CPU_PABRT_NOIFAR=y
@@ -218,25 +240,32 @@ CONFIG_TICK_ONESHOT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
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_DISCONTIGMEM_ENABLE is not set
+CONFIG_ARCH_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL 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_SPARSEMEM_STATIC is not set
-# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
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_UNEVICTABLE_LRU=y
+# CONFIG_LEDS is not set
CONFIG_ALIGNMENT_TRAP=y
#
@@ -244,11 +273,17 @@ CONFIG_ALIGNMENT_TRAP=y
#
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE=""
+CONFIG_CMDLINE="noinitrd console=ttymxc0 root=/dev/mtdblock2 rw ip=off"
# CONFIG_XIP_KERNEL is not set
# CONFIG_KEXEC is not set
#
+# CPU Power Management
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
# Floating point emulation
#
@@ -261,18 +296,21 @@ CONFIG_CMDLINE=""
# 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_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
-
-#
-# Networking
-#
CONFIG_NET=y
#
@@ -281,14 +319,19 @@ CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_PACKET_MMAP=y
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 is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
# 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_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
# CONFIG_IP_PNP_RARP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
@@ -300,11 +343,12 @@ CONFIG_IP_PNP=y
# 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_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 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"
@@ -317,6 +361,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
# 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
@@ -334,17 +379,60 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
# 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_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=m
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
#
-# Wireless
+# FIR device drivers
#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+# CONFIG_MCS_FIR is not set
+# CONFIG_MXC_FIR is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+CONFIG_WIRELESS=y
# CONFIG_CFG80211 is not set
-# CONFIG_WIRELESS_EXT is not set
+CONFIG_WIRELESS_OLD_REGULATORY=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
# CONFIG_MAC80211 is not set
-# CONFIG_IEEE80211 is not set
+CONFIG_IEEE80211=y
+# CONFIG_IEEE80211_DEBUG is not set
+# CONFIG_IEEE80211_CRYPT_WEP is not set
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
@@ -358,14 +446,20 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
-# CONFIG_FW_LOADER is not set
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
# CONFIG_SYS_HYPERVISOR is not set
-# CONFIG_CONNECTOR is not set
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
CONFIG_MTD=y
# CONFIG_MTD_DEBUG is not set
# CONFIG_MTD_CONCAT is not set
CONFIG_MTD_PARTITIONS=y
-# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_REDBOOT_PARTS=y
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
CONFIG_MTD_CMDLINE_PARTS=y
# CONFIG_MTD_AFS_PARTS is not set
# CONFIG_MTD_AR7_PARTS is not set
@@ -405,11 +499,11 @@ CONFIG_MTD_CFI_I1=y
# CONFIG_MTD_CFI_I4 is not set
# CONFIG_MTD_CFI_I8 is not set
# CONFIG_MTD_OTP is not set
-CONFIG_MTD_CFI_INTELEXT=y
-# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
# CONFIG_MTD_CFI_STAA is not set
CONFIG_MTD_CFI_UTIL=y
-# CONFIG_MTD_RAM is not set
+CONFIG_MTD_RAM=y
# CONFIG_MTD_ROM is not set
# CONFIG_MTD_ABSENT is not set
# CONFIG_MTD_XIP is not set
@@ -418,16 +512,16 @@ CONFIG_MTD_CFI_UTIL=y
# Mapping drivers for chip access
#
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
-CONFIG_MTD_PHYSMAP=y
-CONFIG_MTD_PHYSMAP_START=0x00000000
-CONFIG_MTD_PHYSMAP_LEN=0x0
-CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_PHYSMAP is not set
# CONFIG_MTD_ARM_INTEGRATOR is not set
# CONFIG_MTD_PLATRAM is not set
+CONFIG_MTD_MXC=y
#
# Self-contained MTD device drivers
#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
# CONFIG_MTD_SLRAM is not set
# CONFIG_MTD_PHRAM is not set
# CONFIG_MTD_MTDRAM is not set
@@ -439,7 +533,20 @@ CONFIG_MTD_PHYSMAP_BANKWIDTH=2
# 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_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_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+CONFIG_MTD_NAND_MXC=y
+# CONFIG_MTD_NAND_MXC_SWECC is not set
+# CONFIG_MTD_NAND_MXC_FORCE_CE is not set
+# CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 is not set
+# CONFIG_MXC_NAND_LOW_LEVEL_ERASE is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
# CONFIG_MTD_ONENAND is not set
#
@@ -449,12 +556,21 @@ CONFIG_MTD_PHYSMAP_BANKWIDTH=2
# 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_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
# CONFIG_BLK_DEV_NBD is not set
-# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_BLK_DEV_XIP is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
-# CONFIG_MISC_DEVICES is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set
@@ -462,13 +578,52 @@ CONFIG_HAVE_IDE=y
# SCSI device support
#
# CONFIG_RAID_ATTRS is not set
-# CONFIG_SCSI is not set
-# CONFIG_SCSI_DMA is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
# CONFIG_SCSI_NETLINK is not set
-# CONFIG_ATA 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
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+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=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+CONFIG_ATA=m
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_SATA_PMP is not set
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_MV is not set
+# CONFIG_PATA_PLATFORM is not set
+CONFIG_PATA_FSL=m
# CONFIG_MD is not set
CONFIG_NETDEVICES=y
-# CONFIG_NETDEVICES_MULTIQUEUE is not set
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
@@ -477,16 +632,24 @@ CONFIG_NETDEVICES=y
# CONFIG_VETH is not set
# CONFIG_PHYLIB is not set
CONFIG_NET_ETHERNET=y
-# CONFIG_MII is not set
+CONFIG_MII=y
# CONFIG_AX88796 is not set
# CONFIG_SMC91X is not set
# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X 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_FEC_OLD is not set
+CONFIG_CS89x0=y
+CONFIG_CS89x0_NONISA_IRQ=y
+# CONFIG_FEC is not set
# CONFIG_NETDEV_1000 is not set
# CONFIG_NETDEV_10000 is not set
@@ -496,6 +659,15 @@ CONFIG_NET_ETHERNET=y
# CONFIG_WLAN_PRE80211 is not set
# CONFIG_WLAN_80211 is not set
# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# 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 is not set
# CONFIG_SLIP is not set
@@ -522,20 +694,31 @@ CONFIG_INPUT_EVDEV=y
#
# Input Device Drivers
#
-# CONFIG_INPUT_KEYBOARD is not set
+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_MXC=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
# CONFIG_TOUCHSCREEN_FUJITSU is not set
# CONFIG_TOUCHSCREEN_GUNZE is not set
# CONFIG_TOUCHSCREEN_ELO is not set
# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
# CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_MXC=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_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
# CONFIG_INPUT_MISC is not set
#
@@ -547,60 +730,166 @@ CONFIG_INPUT_TOUCHSCREEN=y
#
# Character devices
#
-# CONFIG_VT is not set
+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
+# CONFIG_MXC_IIM is not set
#
# Serial drivers
#
-# CONFIG_SERIAL_8250 is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=1
+CONFIG_SERIAL_8250_RUNTIME_UARTS=1
+CONFIG_SERIAL_8250_EXTENDED=y
+# CONFIG_SERIAL_8250_MANY_PORTS is not set
+# CONFIG_SERIAL_8250_SHARE_IRQ is not set
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+# CONFIG_SERIAL_8250_RSA is not set
#
# Non-8250 serial port support
#
+CONFIG_SERIAL_MXC=y
+CONFIG_SERIAL_MXC_CONSOLE=y
# CONFIG_SERIAL_IMX is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_UNIX98_PTYS=y
-# CONFIG_LEGACY_PTYS is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
# CONFIG_IPMI_HANDLER is not set
-# CONFIG_HW_RANDOM is not set
+CONFIG_HW_RANDOM=y
# CONFIG_NVRAM 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_HAVE_GPIO_LIB=y
+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_MXC=y
+# CONFIG_I2C_MXC_HS is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_SIMTEC is not set
#
-# GPIO Support
+# 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
#
-# I2C GPIO expanders:
+# Other I2C/SMBus bus drivers
#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
#
-# SPI GPIO expanders:
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 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_I2C_SLAVE is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
#
-# CONFIG_W1 is not set
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_MXC=y
+# CONFIG_SPI_MXC_TEST_LOOPBACK is not set
+CONFIG_SPI_MXC_SELECT1=y
+CONFIG_SPI_MXC_SELECT2=y
+# CONFIG_SPI_MXC_SELECT3 is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+CONFIG_W1=m
+CONFIG_W1_CON=y
+
+#
+# 1-wire Bus Masters
+#
+# CONFIG_W1_MASTER_DS2490 is not set
+# CONFIG_W1_MASTER_DS2482 is not set
+CONFIG_W1_MASTER_MXC=m
+
+#
+# 1-wire Slaves
+#
+# CONFIG_W1_SLAVE_THERM is not set
+# CONFIG_W1_SLAVE_SMEM is not set
+# CONFIG_W1_SLAVE_DS2751 is not set
+CONFIG_W1_SLAVE_DS2433=m
+# CONFIG_W1_SLAVE_DS2438 is not set
+# CONFIG_W1_SLAVE_DS2433_CRC is not set
+# CONFIG_W1_SLAVE_DS2760 is not set
+# CONFIG_W1_SLAVE_BQ27000 is not set
# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
-# CONFIG_WATCHDOG is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
#
-# Sonics Silicon Backplane
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_MXC_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_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
#
# Multimedia devices
@@ -609,22 +898,154 @@ CONFIG_SSB_POSSIBLE=y
#
# Multimedia core support
#
-# CONFIG_VIDEO_DEV is not set
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_COMMON=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
# CONFIG_DVB_CORE is not set
-# CONFIG_VIDEO_MEDIA is not set
+CONFIG_VIDEO_MEDIA=y
#
# Multimedia drivers
#
-# CONFIG_DAB is not set
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+CONFIG_VIDEO_MXC_CAMERA=y
+
+#
+# MXC Camera/V4L2 PRP Features support
+#
+CONFIG_VIDEO_MXC_EMMA_CAMERA=y
+# CONFIG_VIDEO_MXC_CSI_DMA is not set
+# CONFIG_MXC_CAMERA_MC521DA is not set
+# CONFIG_MXC_EMMA_CAMERA_MICRON111 is not set
+CONFIG_MXC_CAMERA_OV2640_EMMA=y
+# CONFIG_MXC_CAMERA_MICRON111 is not set
+# CONFIG_MXC_CAMERA_OV2640 is not set
+# CONFIG_MXC_CAMERA_OV3640 is not set
+# CONFIG_MXC_TVIN_ADV7180 is not set
+CONFIG_VIDEO_MXC_OUTPUT=y
+CONFIG_VIDEO_MXC_EMMA_OUTPUT=y
+CONFIG_VIDEO_MXC_OUTPUT_FBSYNC=y
+CONFIG_VIDEO_MXC_OPL=y
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_SOC_CAMERA is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_GSPCA=m
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_S2255 is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_SI470X is not set
+# CONFIG_USB_MR800 is not set
+CONFIG_DAB=y
+# CONFIG_USB_DABUSB 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
+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
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+CONFIG_FB_MXC=y
+CONFIG_FB_MXC_SYNC_PANEL=y
+# CONFIG_FB_MXC_EPSON_VGA_SYNC_PANEL is not set
+# CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL is not set
+# CONFIG_FB_MXC_CH7026 is not set
+CONFIG_FB_MXC_TVOUT=y
+# CONFIG_FB_MXC_TVOUT_CH7024 is not set
+# CONFIG_FB_MXC_ASYNC_PANEL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_MXC=y
+CONFIG_BACKLIGHT_MXC_LCDC=y
+CONFIG_BACKLIGHT_MXC_PMIC=y
#
# Display device support
@@ -632,33 +1053,440 @@ CONFIG_SSB_POSSIBLE=y
# CONFIG_DISPLAY_SUPPORT is not set
#
-# Sound
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_SOUND=y
+CONFIG_SOUND_OSS_CORE=y
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+CONFIG_SND_ARM=y
+# CONFIG_SND_MXC_SPDIF is not set
+CONFIG_SND_MXC_PMIC=y
+# CONFIG_HEADSET_DETECT_ENABLE is not set
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+# CONFIG_SND_SOC is not set
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE is not set
+
+#
+# Special HID drivers
+#
+CONFIG_HID_COMPAT=y
+CONFIG_HID_A4TECH=m
+CONFIG_HID_APPLE=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_BRIGHT=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_CHICONY=m
+CONFIG_HID_CYPRESS=m
+CONFIG_HID_DELL=m
+CONFIG_HID_EZKEY=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_LOGITECH=m
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MONTEREY=m
+CONFIG_HID_PANTHERLORD=m
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_HID_SUNPLUS=m
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_ZEROPLUS_FF 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=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# 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_EHCI_HCD=m
+CONFIG_USB_EHCI_ARC=y
+CONFIG_USB_EHCI_ARC_H1=y
+# CONFIG_USB_EHCI_ARC_H2 is not set
+CONFIG_USB_EHCI_ARC_OTG=y
+# CONFIG_USB_EHCI_ARC_OTG_WAKE_UP is not set
+# CONFIG_USB_STATIC_IRAM is not set
+# CONFIG_USB_EHCI_FSL_MC13783 is not set
+CONFIG_USB_EHCI_FSL_1301=y
+# CONFIG_USB_EHCI_FSL_1504 is not set
+# CONFIG_USB_EHCI_FSL_UTMI is not set
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD 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 information
+#
+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_DPCM 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_SOUND is not set
-# CONFIG_HID_SUPPORT is not set
-# CONFIG_USB_SUPPORT is not set
-# CONFIG_MMC is not set
+# 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_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+CONFIG_USB_GADGET=m
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# 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_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_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+CONFIG_USB_GADGET_ARC=y
+CONFIG_USB_ARC=m
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+CONFIG_USB_GADGET_ARC_OTG=y
+# CONFIG_USB_GADGET_WAKE_UP is not set
+# CONFIG_USB_GADGET_FSL_MC13783 is not set
+CONFIG_USB_GADGET_FSL_1301=y
+# CONFIG_USB_GADGET_FSL_1504 is not set
+# CONFIG_USB_GADGET_FSL_UTMI is not set
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# 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_SPI is not set
+CONFIG_MMC_MXC=y
+# CONFIG_MMC_IMX_ESDHCI is not set
+# CONFIG_MMC_IMX_ESDHCI_PIO_MODE 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_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
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+
+#
+# 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_MXC=y
+# CONFIG_RTC_DRV_MXC_V2 is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_REGULATOR is not set
# CONFIG_UIO is not set
#
+# MXC support drivers
+#
+
+#
+# MXC SSI support
+#
+CONFIG_MXC_SSI=y
+
+#
+# MXC Digital Audio Multiplexer support
+#
+CONFIG_MXC_DAM=y
+
+#
+# MXC PMIC support
+#
+CONFIG_MXC_PMIC=y
+CONFIG_MXC_PMIC_MC13783=y
+# CONFIG_MXC_PMIC_MC13892 is not set
+# CONFIG_MXC_PMIC_SPI is not set
+# CONFIG_MXC_PMIC_MC34704 is not set
+# CONFIG_MXC_PMIC_MC9SDZ60 is not set
+CONFIG_MXC_PMIC_CHARDEV=y
+
+#
+# MXC PMIC Client Drivers
+#
+CONFIG_MXC_MC13783_ADC=y
+CONFIG_MXC_MC13783_AUDIO=y
+CONFIG_MXC_MC13783_RTC=y
+CONFIG_MXC_MC13783_LIGHT=y
+CONFIG_MXC_MC13783_BATTERY=y
+CONFIG_MXC_MC13783_CONNECTIVITY=y
+CONFIG_MXC_MC13783_POWER=y
+# CONFIG_MXC_PMIC_MC9S08DZ60 is not set
+
+#
+# MXC Security Drivers
+#
+CONFIG_MXC_SECURITY_SCC=y
+# CONFIG_SCC_DEBUG is not set
+
+#
+# SAHARA2 Security Hardware Support
+#
+CONFIG_MXC_SAHARA=y
+CONFIG_MXC_SAHARA_USER_MODE=y
+# CONFIG_MXC_SAHARA_POLL_MODE is not set
+
+#
+# MXC MPEG4 Encoder Kernel module support
+#
+
+#
+# MXC HARDWARE EVENT
+#
+CONFIG_MXC_HWEVENT=y
+
+#
+# MXC VPU(Video Processing Unit) support
+#
+CONFIG_MXC_VPU=y
+# CONFIG_MXC_VPU_DEBUG is not set
+
+#
+# MXC Asynchronous Sample Rate Converter support
+#
+
+#
+# MXC Bluetooth support
+#
+
+#
+# Broadcom GPS ioctrl support
+#
+
+#
+# MXC Media Local Bus Driver
+#
+
+#
+# i.MX ADC support
+#
+# CONFIG_IMX_ADC is not set
+
+#
# File systems
#
-# CONFIG_EXT2_FS is not set
+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_FILE_LOCKING=y
# CONFIG_XFS_FS is not set
# CONFIG_OCFS2_FS is not set
-# CONFIG_DNOTIFY is not set
-# CONFIG_INOTIFY is not set
+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_AUTOFS4_FS=m
# CONFIG_FUSE_FS is not set
#
@@ -670,8 +1498,11 @@ CONFIG_RTC_LIB=y
#
# DOS/FAT/NT Filesystems
#
-# CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
+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
#
@@ -679,6 +1510,7 @@ CONFIG_RTC_LIB=y
#
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
@@ -706,9 +1538,10 @@ 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_CRAMFS=y
# 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
@@ -719,13 +1552,13 @@ CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
# CONFIG_NFS_V3_ACL is not set
# CONFIG_NFS_V4 is not set
-# CONFIG_NFSD 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_SUNRPC_BIND34 is not set
+# CONFIG_SUNRPC_REGISTER_V4 is not set
# CONFIG_RPCSEC_GSS_KRB5 is not set
# CONFIG_RPCSEC_GSS_SPKM3 is not set
# CONFIG_SMB_FS is not set
@@ -741,10 +1574,10 @@ CONFIG_SUNRPC=y
CONFIG_MSDOS_PARTITION=y
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
-CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_CODEPAGE_437=y
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
-CONFIG_NLS_CODEPAGE_850=m
+# 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
@@ -764,7 +1597,7 @@ CONFIG_NLS_CODEPAGE_850=m
# 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_ASCII=m
CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
@@ -775,10 +1608,10 @@ CONFIG_NLS_ISO8859_1=y
# 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=m
+# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
-# CONFIG_NLS_UTF8 is not set
+CONFIG_NLS_UTF8=m
# CONFIG_DLM is not set
#
@@ -793,9 +1626,22 @@ CONFIG_FRAME_WARN=1024
# 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 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=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG is not set
# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_DEBUG_USER is not set
#
@@ -803,17 +1649,101 @@ CONFIG_FRAME_POINTER=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 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
+# 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 is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
#
# Library routines
#
CONFIG_BITREVERSE=y
-# CONFIG_GENERIC_FIND_FIRST_BIT is not set
-# CONFIG_GENERIC_FIND_NEXT_BIT is not set
-# CONFIG_CRC_CCITT is not set
+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
diff --git a/arch/arm/configs/imx31_3stack_defconfig b/arch/arm/configs/imx31_3stack_defconfig
new file mode 100644
index 000000000000..d54abf1ea30b
--- /dev/null
+++ b/arch/arm/configs/imx31_3stack_defconfig
@@ -0,0 +1,1835 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28
+# Wed May 27 16:47:24 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+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
+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_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_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_OPROFILE_ARMV6=y
+CONFIG_OPROFILE_ARM11_CORE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# 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=y
+CONFIG_SWAP=y
+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
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED 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 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 is not set
+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
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+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=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# 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_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_CLASSIC_RCU=y
+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_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE 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
+# 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_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=y
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_STMP3XXX is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Freescale MXC Implementations
+#
+# CONFIG_ARCH_MX37 is not set
+# CONFIG_ARCH_MX35 is not set
+# CONFIG_ARCH_MX51 is not set
+CONFIG_ARCH_MX3=y
+# CONFIG_ARCH_MX27 is not set
+# CONFIG_ARCH_MX25 is not set
+CONFIG_ARCH_MXC_HAS_NFC_V1=y
+CONFIG_I2C_MXC_SELECT1=y
+# CONFIG_I2C_MXC_SELECT2 is not set
+CONFIG_MXC_SDMA_API=y
+CONFIG_ARCH_MXC_HAS_NFC_V2=y
+# CONFIG_I2C_MXC_SELECT3 is not set
+
+#
+# MX3 Options
+#
+CONFIG_MX3_OPTIONS=y
+# CONFIG_MACH_MX31ADS is not set
+# CONFIG_MACH_PCM037 is not set
+# CONFIG_MACH_MX31LITE is not set
+CONFIG_MACH_MX31_3DS=y
+# CONFIG_MX3_DOZE_DURING_IDLE is not set
+
+#
+# SDMA options
+#
+# CONFIG_SDMA_IRAM is not set
+
+#
+# Device options
+#
+CONFIG_ARCH_HAS_RNGA=y
+CONFIG_ARCH_HAS_EVTMON=y
+CONFIG_DMA_ZONE_SIZE=24
+CONFIG_ISP1504_MXC=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+# CONFIG_CPU_ARM926T is not set
+CONFIG_CPU_V6=y
+# CONFIG_CPU_32v6K is not set
+# CONFIG_CPU_V7 is not set
+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_OUTER_CACHE=y
+CONFIG_CACHE_L2X0=y
+CONFIG_ARM_ERRATA_364296=y
+CONFIG_ARM_ERRATA_411920=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+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_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL 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_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_UNEVICTABLE_LRU=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/mtdblock2 rw rootfstype=jffs2 ip=off"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+
+#
+# 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=y
+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 is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE 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=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_NET_SCHED 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_PHONET is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_OLD_REGULATORY=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=y
+# CONFIG_IEEE80211_DEBUG is not set
+# CONFIG_IEEE80211_CRYPT_WEP is not set
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP 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=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# 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 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
+# CONFIG_MTD_MXC is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# 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_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+CONFIG_MTD_NAND_MXC=y
+CONFIG_MTD_NAND_MXC_V2=y
+# CONFIG_MTD_NAND_MXC_SWECC is not set
+# CONFIG_MTD_NAND_MXC_FORCE_CE is not set
+# CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 is not set
+# CONFIG_MXC_NAND_LOW_LEVEL_ERASE is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND 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 is not set
+# CONFIG_BLK_DEV_UB 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=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT 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=y
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+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=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+CONFIG_ATA=m
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_SATA_PMP is not set
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_MV is not set
+# CONFIG_PATA_PLATFORM is not set
+CONFIG_PATA_FSL=m
+# 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 is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_SMC911X is not set
+CONFIG_SMSC911X=y
+# 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_CS89x0 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
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# 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=m
+# CONFIG_USB_NET_AX8817X is not set
+CONFIG_USB_NET_CDCETHER=m
+# CONFIG_USB_NET_DM9601 is not set
+# CONFIG_USB_NET_SMSC95XX is not set
+# CONFIG_USB_NET_GL620A is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# 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=y
+
+#
+# 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_MXC=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_MXC=y
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 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
+CONFIG_FM_SI4702=m
+CONFIG_MXC_IIM=y
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_MXC=y
+CONFIG_SERIAL_MXC_CONSOLE=y
+# CONFIG_SERIAL_IMX is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM 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_MXC=y
+# CONFIG_I2C_MXC_HS 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
+# 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_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 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_I2C_SLAVE is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_MXC=y
+# CONFIG_SPI_MXC_TEST_LOOPBACK is not set
+CONFIG_SPI_MXC_SELECT1=y
+CONFIG_SPI_MXC_SELECT2=y
+# CONFIG_SPI_MXC_SELECT3 is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+# 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_ADCXX 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_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_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 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_MAX1111 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_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_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_MXC_MMA7450=m
+# 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_MXC_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_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_COMMON=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=y
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set
+
+#
+# Encoders/decoders and other helper chips
+#
+
+#
+# Audio decoders
+#
+# CONFIG_VIDEO_TVAUDIO is not set
+# CONFIG_VIDEO_TDA7432 is not set
+# CONFIG_VIDEO_TDA9840 is not set
+# CONFIG_VIDEO_TDA9875 is not set
+# CONFIG_VIDEO_TEA6415C is not set
+# CONFIG_VIDEO_TEA6420 is not set
+# CONFIG_VIDEO_MSP3400 is not set
+# CONFIG_VIDEO_CS5345 is not set
+# CONFIG_VIDEO_CS53L32A is not set
+# CONFIG_VIDEO_M52790 is not set
+# CONFIG_VIDEO_TLV320AIC23B is not set
+# CONFIG_VIDEO_WM8775 is not set
+# CONFIG_VIDEO_WM8739 is not set
+# CONFIG_VIDEO_VP27SMPX is not set
+
+#
+# Video decoders
+#
+# CONFIG_VIDEO_BT819 is not set
+# CONFIG_VIDEO_BT856 is not set
+# CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_KS0127 is not set
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_TCM825X is not set
+# CONFIG_VIDEO_SAA7110 is not set
+# CONFIG_VIDEO_SAA7111 is not set
+# CONFIG_VIDEO_SAA7114 is not set
+# CONFIG_VIDEO_SAA711X is not set
+# CONFIG_VIDEO_SAA717X is not set
+# CONFIG_VIDEO_SAA7191 is not set
+# CONFIG_VIDEO_TVP5150 is not set
+# CONFIG_VIDEO_VPX3220 is not set
+
+#
+# Video and audio decoders
+#
+# CONFIG_VIDEO_CX25840 is not set
+
+#
+# MPEG video encoders
+#
+# CONFIG_VIDEO_CX2341X is not set
+
+#
+# Video encoders
+#
+# CONFIG_VIDEO_SAA7127 is not set
+# CONFIG_VIDEO_SAA7185 is not set
+# CONFIG_VIDEO_ADV7170 is not set
+# CONFIG_VIDEO_ADV7175 is not set
+
+#
+# Video improvement chips
+#
+# CONFIG_VIDEO_UPD64031A is not set
+# CONFIG_VIDEO_UPD64083 is not set
+# CONFIG_VIDEO_VIVI is not set
+CONFIG_VIDEO_MXC_CAMERA=m
+
+#
+# MXC Camera/V4L2 PRP Features support
+#
+CONFIG_VIDEO_MXC_IPU_CAMERA=y
+# CONFIG_MXC_CAMERA_MC521DA is not set
+# CONFIG_MXC_EMMA_CAMERA_MICRON111 is not set
+# CONFIG_MXC_CAMERA_OV2640_EMMA is not set
+# CONFIG_MXC_CAMERA_MICRON111 is not set
+CONFIG_MXC_CAMERA_OV2640=m
+# CONFIG_MXC_CAMERA_OV3640 is not set
+# CONFIG_MXC_TVIN_ADV7180 is not set
+CONFIG_MXC_IPU_PRP_VF_SDC=m
+CONFIG_MXC_IPU_PRP_ENC=m
+CONFIG_VIDEO_MXC_OUTPUT=y
+CONFIG_VIDEO_MXC_IPU_OUTPUT=y
+# CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT is not set
+# CONFIG_VIDEO_MXC_OPL is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_SOC_CAMERA is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_GSPCA=m
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_S2255 is not set
+# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+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
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+CONFIG_FB_MXC=y
+CONFIG_FB_MXC_SYNC_PANEL=y
+CONFIG_FB_MXC_EPSON_VGA_SYNC_PANEL=y
+# CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL is not set
+# CONFIG_FB_MXC_CH7026 is not set
+# CONFIG_FB_MXC_TVOUT is not set
+CONFIG_FB_MXC_TVOUT_CH7024=y
+# CONFIG_FB_MXC_ASYNC_PANEL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_MXC=y
+CONFIG_BACKLIGHT_MXC_IPU=y
+CONFIG_BACKLIGHT_MXC_PMIC=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_SOUND=y
+CONFIG_SOUND_OSS_CORE=y
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+CONFIG_SND_ARM=y
+# CONFIG_SND_MXC_SPDIF is not set
+CONFIG_SND_MXC_PMIC=y
+# CONFIG_SND_MXC_PLAYBACK_MIXING is not set
+# CONFIG_HEADSET_DETECT_ENABLE is not set
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+# CONFIG_SND_SOC is not set
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE is not set
+
+#
+# Special HID drivers
+#
+CONFIG_HID_COMPAT=y
+CONFIG_HID_A4TECH=m
+CONFIG_HID_APPLE=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_BRIGHT=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_CHICONY=m
+CONFIG_HID_CYPRESS=m
+CONFIG_HID_DELL=m
+CONFIG_HID_EZKEY=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_LOGITECH=m
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MONTEREY=m
+CONFIG_HID_PANTHERLORD=m
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_HID_SUNPLUS=m
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_ZEROPLUS_FF 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=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# 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_EHCI_HCD=m
+CONFIG_USB_EHCI_ARC=y
+# CONFIG_USB_EHCI_ARC_H1 is not set
+CONFIG_USB_EHCI_ARC_H2=y
+# CONFIG_USB_EHCI_ARC_H2_WAKE_UP is not set
+CONFIG_USB_EHCI_ARC_OTG=y
+# CONFIG_USB_EHCI_ARC_OTG_WAKE_UP is not set
+# CONFIG_USB_STATIC_IRAM is not set
+# CONFIG_USB_EHCI_FSL_MC13783 is not set
+# CONFIG_USB_EHCI_FSL_1301 is not set
+CONFIG_USB_EHCI_FSL_1504=y
+# CONFIG_USB_EHCI_FSL_UTMI is not set
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD 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 information
+#
+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_DPCM 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_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+CONFIG_USB_GADGET=m
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# 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_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_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+CONFIG_USB_GADGET_ARC=y
+# CONFIG_USB_STATIC_IRAM_PPH is not set
+CONFIG_USB_ARC=m
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+CONFIG_USB_GADGET_ARC_OTG=y
+# CONFIG_USB_GADGET_WAKE_UP is not set
+# CONFIG_USB_GADGET_FSL_MC13783 is not set
+# CONFIG_USB_GADGET_FSL_1301 is not set
+CONFIG_USB_GADGET_FSL_1504=y
+# CONFIG_USB_GADGET_FSL_UTMI is not set
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# 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
+CONFIG_SDIO_UNIFI_FS=m
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SPI is not set
+CONFIG_MMC_MXC=m
+# CONFIG_MMC_IMX_ESDHCI is not set
+# CONFIG_MMC_IMX_ESDHCI_PIO_MODE 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
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+
+#
+# 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_MXC=y
+# CONFIG_RTC_DRV_MXC_V2 is not set
+# CONFIG_RTC_DRV_IMXDI is not set
+# CONFIG_DMADEVICES is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+CONFIG_REGULATOR_MC13783=y
+# CONFIG_UIO is not set
+
+#
+# MXC support drivers
+#
+CONFIG_MXC_IPU=y
+CONFIG_MXC_IPU_V1=y
+CONFIG_MXC_IPU_PF=y
+
+#
+# MXC SSI support
+#
+CONFIG_MXC_SSI=y
+
+#
+# MXC Digital Audio Multiplexer support
+#
+CONFIG_MXC_DAM=y
+
+#
+# MXC PMIC support
+#
+CONFIG_MXC_PMIC=y
+CONFIG_MXC_PMIC_MC13783=y
+# CONFIG_MXC_PMIC_MC13892 is not set
+# CONFIG_MXC_PMIC_SPI is not set
+# CONFIG_MXC_PMIC_MC34704 is not set
+# CONFIG_MXC_PMIC_MC9SDZ60 is not set
+CONFIG_MXC_PMIC_CHARDEV=y
+
+#
+# MXC PMIC Client Drivers
+#
+CONFIG_MXC_MC13783_ADC=y
+CONFIG_MXC_MC13783_AUDIO=y
+CONFIG_MXC_MC13783_RTC=y
+CONFIG_MXC_MC13783_LIGHT=y
+CONFIG_MXC_MC13783_BATTERY=y
+CONFIG_MXC_MC13783_CONNECTIVITY=y
+CONFIG_MXC_MC13783_POWER=y
+# CONFIG_MXC_PMIC_MC9S08DZ60 is not set
+
+#
+# MXC Security Drivers
+#
+CONFIG_MXC_SECURITY_SCC=y
+# CONFIG_SCC_DEBUG is not set
+CONFIG_MXC_SECURITY_RNG=y
+# CONFIG_MXC_RNG_TEST_DRIVER is not set
+# CONFIG_MXC_RNG_DEBUG is not set
+
+#
+# MXC MPEG4 Encoder Kernel module support
+#
+CONFIG_MXC_HMP4E=y
+# CONFIG_MXC_HMP4E_DEBUG is not set
+
+#
+# MXC HARDWARE EVENT
+#
+CONFIG_MXC_HWEVENT=y
+
+#
+# MXC VPU(Video Processing Unit) support
+#
+# CONFIG_MXC_VPU is not set
+
+#
+# MXC Asynchronous Sample Rate Converter support
+#
+
+#
+# MXC Bluetooth support
+#
+CONFIG_MXC_BLUETOOTH=m
+
+#
+# Broadcom GPS ioctrl support
+#
+CONFIG_GPS_IOCTRL=m
+
+#
+# MXC Media Local Bus Driver
+#
+
+#
+# i.MX ADC support
+#
+# CONFIG_IMX_ADC 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 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_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+# CONFIG_FUSE_FS 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
+
+#
+# Miscellaneous filesystems
+#
+# 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=y
+# 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_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_SUNRPC_REGISTER_V4 is not set
+# 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 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=m
+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=m
+# 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 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=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG 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=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
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_CRYPTODEV 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 is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=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_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/imx31ads_defconfig b/arch/arm/configs/imx31ads_defconfig
new file mode 100644
index 000000000000..86ca17ea1648
--- /dev/null
+++ b/arch/arm/configs/imx31ads_defconfig
@@ -0,0 +1,1707 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.26
+# Tue Sep 16 10:19:21 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+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_LOCKDEP_SUPPORT=y
+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_OPROFILE_ARMV6=y
+CONFIG_OPROFILE_ARM11_CORE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# 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=y
+CONFIG_SWAP=y
+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
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED 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 is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=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_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_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_DMA_ATTRS is not set
+CONFIG_PROC_PAGE_MONITOR=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=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# 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_BLK_DEV_BSG 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_CLASSIC_RCU=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_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE 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
+# 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_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+CONFIG_ARCH_MXC=y
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM7X00A is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Freescale MXC Implementations
+#
+# CONFIG_ARCH_MX37 is not set
+# CONFIG_ARCH_MX35 is not set
+# CONFIG_ARCH_MX51 is not set
+CONFIG_ARCH_MX3=y
+# CONFIG_ARCH_MX27 is not set
+# CONFIG_ARCH_MX21 is not set
+CONFIG_ARCH_MXC_HAS_NFC_V1=y
+CONFIG_I2C_MXC_SELECT1=y
+# CONFIG_I2C_MXC_SELECT2 is not set
+
+#
+# MX3 Options
+#
+CONFIG_MX3_OPTIONS=y
+CONFIG_MACH_MX31ADS=y
+# CONFIG_MACH_MX31_3DS is not set
+# CONFIG_MX3_DOZE_DURING_IDLE is not set
+CONFIG_MXC_SDMA_API=y
+
+#
+# SDMA options
+#
+# CONFIG_SDMA_IRAM is not set
+CONFIG_ARCH_MXC_HAS_NFC_V2=y
+
+#
+# Device options
+#
+# CONFIG_I2C_MXC_SELECT3 is not set
+CONFIG_ARCH_HAS_EVTMON=y
+CONFIG_DMA_ZONE_SIZE=24
+CONFIG_ISP1504_MXC=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+# CONFIG_CPU_ARM926T is not set
+CONFIG_CPU_V6=y
+# CONFIG_CPU_32v6K is not set
+# CONFIG_CPU_V7 is not set
+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_OUTER_CACHE=y
+CONFIG_CACHE_L2X0=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCCARD=m
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=m
+CONFIG_PCMCIA_LOAD_CIS=y
+# CONFIG_PCMCIA_IOCTL is not set
+
+#
+# PC-card bridges
+#
+CONFIG_PCMCIA_MX31ADS=m
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE 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_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc0 root=/dev/mtdblock2 rw ip=off"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=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
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+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 is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE 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=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_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_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=m
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+# CONFIG_MCS_FIR is not set
+CONFIG_MXC_FIR=m
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=y
+# CONFIG_IEEE80211_DEBUG is not set
+# CONFIG_IEEE80211_CRYPT_WEP is not set
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP 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_SYS_HYPERVISOR is not set
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_REDBOOT_PARTS=y
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY 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 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set
+# 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=y
+# 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
+# CONFIG_MTD_XIP 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
+CONFIG_MTD_MXC=y
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# 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_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+CONFIG_MTD_NAND_MXC=y
+CONFIG_MTD_NAND_MXC_V2=y
+# CONFIG_MTD_NAND_MXC_SWECC is not set
+# CONFIG_MTD_NAND_MXC_FORCE_CE is not set
+# CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 is not set
+# CONFIG_MXC_NAND_LOW_LEVEL_ERASE is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+
+#
+# Voltage and Current regulators
+#
+CONFIG_REGULATOR_API=y
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+CONFIG_REGULATOR_MC13783=y
+# CONFIG_REGULATOR_WM8350 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 is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES 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
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+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=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+CONFIG_ATA=m
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_SATA_PMP is not set
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_MV is not set
+# CONFIG_PATA_PCMCIA is not set
+# CONFIG_PATA_PLATFORM is not set
+CONFIG_PATA_FSL=m
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# 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 is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X 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_B44 is not set
+CONFIG_CS89x0=y
+# 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
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# 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_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=m
+# 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
+# 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 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_MXC=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_MXC=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_USB_COMPOSITE 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_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=y
+# CONFIG_SERIAL_8250_CS is not set
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_8250_EXTENDED=y
+# CONFIG_SERIAL_8250_MANY_PORTS is not set
+# CONFIG_SERIAL_8250_SHARE_IRQ is not set
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+# CONFIG_SERIAL_8250_RSA is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_MXC=y
+CONFIG_SERIAL_MXC_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_IPWIRELESS 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
+
+#
+# I2C Hardware Bus support
+#
+CONFIG_I2C_MXC=y
+# CONFIG_I2C_MXC_HS is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 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_I2C_SLAVE is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_MXC=y
+# CONFIG_SPI_MXC_TEST_LOOPBACK is not set
+# CONFIG_SPI_MXC_SELECT1 is not set
+CONFIG_SPI_MXC_SELECT2=y
+# CONFIG_SPI_MXC_SELECT3 is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+CONFIG_W1=y
+CONFIG_W1_CON=y
+
+#
+# 1-wire Bus Masters
+#
+# CONFIG_W1_MASTER_DS2490 is not set
+# CONFIG_W1_MASTER_DS2482 is not set
+CONFIG_W1_MASTER_MXC=y
+# CONFIG_W1_MASTER_DS1WM is not set
+
+#
+# 1-wire Slaves
+#
+# CONFIG_W1_SLAVE_THERM is not set
+# CONFIG_W1_SLAVE_SMEM is not set
+# CONFIG_W1_SLAVE_DS2751 is not set
+CONFIG_W1_SLAVE_DS2433=y
+# CONFIG_W1_SLAVE_DS2433_CRC is not set
+# CONFIG_W1_SLAVE_DS2760 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_MXC_WATCHDOG=y
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_PASIC3 is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_COMMON=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=y
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+CONFIG_VIDEO_MXC_CAMERA=y
+
+#
+# MXC Camera/V4L2 PRP Features support
+#
+CONFIG_VIDEO_MXC_IPU_CAMERA=y
+# CONFIG_MXC_CAMERA_MC521DA is not set
+CONFIG_MXC_CAMERA_MICRON111=y
+# CONFIG_MXC_CAMERA_OV2640 is not set
+# CONFIG_MXC_TVIN_ADV7180 is not set
+CONFIG_MXC_IPU_PRP_VF_SDC=y
+CONFIG_MXC_IPU_PRP_ENC=y
+CONFIG_VIDEO_MXC_OUTPUT=y
+CONFIG_VIDEO_MXC_IPU_OUTPUT=y
+# CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT is not set
+# CONFIG_VIDEO_MXC_OPL is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_W9968CF is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_SOC_CAMERA is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_USB_DSBR is not set
+# CONFIG_USB_SI470X is not set
+CONFIG_DAB=y
+# CONFIG_USB_DABUSB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+CONFIG_FB_MXC=y
+CONFIG_FB_MXC_SYNC_PANEL=y
+# CONFIG_FB_MXC_EPSON_VGA_SYNC_PANEL is not set
+# CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL is not set
+CONFIG_FB_MXC_TVOUT=y
+# CONFIG_FB_MXC_TVOUT_CH7024 is not set
+# CONFIG_FB_MXC_ASYNC_PANEL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_MXC=y
+CONFIG_BACKLIGHT_MXC_IPU=y
+CONFIG_BACKLIGHT_MXC_PMIC=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+# CONFIG_SND_MXC_SPDIF is not set
+CONFIG_SND_MXC_PMIC=y
+# CONFIG_SND_MXC_PLAYBACK_MIXING is not set
+# CONFIG_HEADSET_DETECT_ENABLE is not set
+
+#
+# SPI devices
+#
+
+#
+# USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# PCMCIA devices
+#
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_PDAUDIOCF is not set
+
+#
+# System on Chip audio support
+#
+# CONFIG_SND_SOC is not set
+# CONFIG_SND_MXC_SOC is not set
+# CONFIG_SND_MXC_SOC_IRAM is not set
+# CONFIG_SND_SOC_IMX_3STACK_WM8350 is not set
+# CONFIG_SND_SOC_IMX_3STACK_AK4647 is not set
+# CONFIG_SND_SOC_IMX_3STACK_WM8580 is not set
+# CONFIG_SND_SOC_IMX_3STACK_WM8903 is not set
+
+#
+# ALSA SoC audio for Freescale SOCs
+#
+
+#
+# SoC Audio for the Texas Instruments OMAP
+#
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE 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=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+CONFIG_USB_EHCI_HCD=m
+CONFIG_USB_EHCI_ARC=y
+CONFIG_USB_EHCI_ARC_H1=y
+CONFIG_USB_EHCI_ARC_H2=y
+CONFIG_USB_EHCI_ARC_OTG=y
+# CONFIG_USB_STATIC_IRAM is not set
+# CONFIG_USB_EHCI_FSL_MC13783 is not set
+# CONFIG_USB_EHCI_FSL_1301 is not set
+CONFIG_USB_EHCI_FSL_1504=y
+# CONFIG_USB_EHCI_FSL_UTMI is not set
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD 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
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+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_DPCM 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
+CONFIG_USB_MON=y
+
+#
+# Belcarra USBLAN Networking for USB
+#
+# CONFIG_USB_USBLAN 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_AUERSWALD 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_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+CONFIG_USB_GADGET=m
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_PXA2XX is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+CONFIG_USB_GADGET_ARC=y
+CONFIG_USB_ARC=m
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+CONFIG_USB_GADGET_ARC_OTG=y
+# CONFIG_USB_GADGET_FSL_MC13783 is not set
+# CONFIG_USB_GADGET_FSL_1301 is not set
+CONFIG_USB_GADGET_FSL_1504=y
+# CONFIG_USB_GADGET_FSL_UTMI is not set
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD 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 Host Controller Drivers
+#
+# CONFIG_MMC_SPI is not set
+CONFIG_MMC_MXC=y
+# CONFIG_MMC_IMX_ESDHCI is not set
+# CONFIG_MMC_IMX_ESDHCI_PIO_MODE 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
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS 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_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_MXC=y
+# CONFIG_RTC_DRV_MXC_V2 is not set
+# CONFIG_UIO is not set
+
+#
+# MXC support drivers
+#
+CONFIG_MXC_IPU=y
+CONFIG_MXC_IPU_V1=y
+CONFIG_MXC_IPU_PF=y
+
+#
+# MXC SSI support
+#
+CONFIG_MXC_SSI=y
+
+#
+# MXC Digital Audio Multiplexer support
+#
+CONFIG_MXC_DAM=y
+
+#
+# MXC PMIC support
+#
+CONFIG_MXC_PMIC=y
+CONFIG_MXC_PMIC_MC13783=y
+# CONFIG_MXC_PMIC_MC13892 is not set
+# CONFIG_MXC_PMIC_MC9SDZ60 is not set
+CONFIG_MXC_PMIC_CHARDEV=y
+
+#
+# MXC PMIC Client Drivers
+#
+CONFIG_MXC_MC13783_ADC=y
+CONFIG_MXC_MC13783_AUDIO=y
+CONFIG_MXC_MC13783_RTC=y
+CONFIG_MXC_MC13783_LIGHT=y
+CONFIG_MXC_MC13783_BATTERY=y
+CONFIG_MXC_MC13783_CONNECTIVITY=y
+CONFIG_MXC_MC13783_POWER=y
+
+#
+# Advanced Power Management devices
+#
+
+#
+# MXC Security Drivers
+#
+CONFIG_MXC_SECURITY_SCC=y
+# CONFIG_SCC_DEBUG is not set
+CONFIG_MXC_SECURITY_RNG=y
+# CONFIG_MXC_RNG_TEST_DRIVER is not set
+# CONFIG_MXC_RNG_DEBUG is not set
+CONFIG_MXC_SECURITY_CORE=y
+
+#
+# MXC MPEG4 Encoder Kernel module support
+#
+CONFIG_MXC_HMP4E=y
+# CONFIG_MXC_HMP4E_DEBUG is not set
+
+#
+# MXC HARDWARE EVENT
+#
+CONFIG_MXC_HWEVENT=y
+
+#
+# MXC VPU(Video Processing Unit) support
+#
+CONFIG_MXC_VPU=y
+# CONFIG_MXC_VPU_DEBUG is not set
+
+#
+# MXC Asynchronous Sample Rate Converter support
+#
+
+#
+# MXC Bluetooth support
+#
+
+#
+# Broadcom GPS ioctrl support
+#
+
+#
+# MXC Media Local Bus Driver
+#
+
+#
+# File systems
+#
+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_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_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+# CONFIG_FUSE_FS 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_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# 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=y
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_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_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_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_BIND34 is not set
+# 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 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=m
+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=m
+# 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 is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# 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 is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# 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 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# 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 is not set
+# 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 is not set
+# 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_LZO is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+CONFIG_CRC_CCITT=m
+# CONFIG_CRC16 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_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/imx35_3stack_defconfig b/arch/arm/configs/imx35_3stack_defconfig
new file mode 100644
index 000000000000..08fcc91ad5cf
--- /dev/null
+++ b/arch/arm/configs/imx35_3stack_defconfig
@@ -0,0 +1,1813 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28
+# Fri Aug 21 13:59:41 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+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
+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_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_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_OPROFILE_ARMV6=y
+CONFIG_OPROFILE_ARM11_CORE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# 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=y
+CONFIG_SWAP=y
+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
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED 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 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 is not set
+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
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+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=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# 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_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_CLASSIC_RCU=y
+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_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE 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
+# 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_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=y
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_STMP3XXX is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Freescale MXC Implementations
+#
+# CONFIG_ARCH_MX37 is not set
+CONFIG_ARCH_MX35=y
+# CONFIG_ARCH_MX51 is not set
+# CONFIG_ARCH_MX3 is not set
+# CONFIG_ARCH_MX27 is not set
+# CONFIG_ARCH_MX25 is not set
+CONFIG_I2C_MXC_SELECT1=y
+# CONFIG_I2C_MXC_SELECT2 is not set
+CONFIG_MXC_SDMA_API=y
+CONFIG_SDMA_IRAM=y
+CONFIG_SDMA_IRAM_SIZE=0x1000
+CONFIG_ARCH_MXC_HAS_NFC_V2=y
+CONFIG_ARCH_MXC_HAS_NFC_V2_1=y
+# CONFIG_I2C_MXC_SELECT3 is not set
+
+#
+# MX35 Options
+#
+CONFIG_FORCE_MAX_ZONEORDER=13
+CONFIG_MX35_OPTIONS=y
+CONFIG_MACH_MX35_3DS=y
+# CONFIG_MACH_MX35EVB is not set
+# CONFIG_MX35_DOZE_DURING_IDLE is not set
+
+#
+# SDMA options
+#
+
+#
+# Device options
+#
+CONFIG_MXC_PSEUDO_IRQS=y
+CONFIG_ARCH_HAS_RNGC=y
+CONFIG_ARCH_HAS_EVTMON=y
+CONFIG_DMA_ZONE_SIZE=24
+CONFIG_UTMI_MXC=y
+# CONFIG_MXC_PWM is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+# CONFIG_CPU_ARM926T is not set
+CONFIG_CPU_V6=y
+# CONFIG_CPU_32v6K is not set
+# CONFIG_CPU_V7 is not set
+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_OUTER_CACHE=y
+CONFIG_CACHE_L2X0=y
+CONFIG_ARM_ERRATA_364296=y
+CONFIG_ARM_ERRATA_411920=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+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_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL 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_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_UNEVICTABLE_LRU=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/mtdblock8 rw rootfstype=jffs2 ip=off"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+
+#
+# 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=y
+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 is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE 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=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_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+CONFIG_CAN=y
+CONFIG_CAN_RAW=y
+CONFIG_CAN_BCM=y
+
+#
+# CAN Device Drivers
+#
+CONFIG_CAN_VCAN=y
+# CONFIG_CAN_DEBUG_DEVICES is not set
+CONFIG_CAN_FLEXCAN=m
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_OLD_REGULATORY=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=y
+# CONFIG_IEEE80211_DEBUG is not set
+# CONFIG_IEEE80211_CRYPT_WEP is not set
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP 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=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# 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 is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# 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
+# CONFIG_MTD_XIP 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
+CONFIG_MTD_MXC=y
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# 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_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_IMX_NFC is not set
+CONFIG_MTD_NAND_MXC_V2=y
+# CONFIG_MTD_NAND_MXC_SWECC is not set
+# CONFIG_MTD_NAND_MXC_FORCE_CE is not set
+# CONFIG_MXC_NAND_LOW_LEVEL_ERASE is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND 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 is not set
+# CONFIG_BLK_DEV_UB 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=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT 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=y
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+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=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+CONFIG_ATA=m
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_SATA_PMP is not set
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_MV is not set
+# CONFIG_PATA_PLATFORM is not set
+CONFIG_PATA_FSL=m
+# 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 is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_SMC911X is not set
+CONFIG_SMSC911X=y
+# 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_CS89x0 is not set
+# CONFIG_FEC 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
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# 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=m
+# CONFIG_USB_NET_AX8817X is not set
+CONFIG_USB_NET_CDCETHER=m
+# CONFIG_USB_NET_DM9601 is not set
+# CONFIG_USB_NET_SMSC95XX is not set
+# CONFIG_USB_NET_GL620A is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# 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=y
+
+#
+# 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_MXC is not set
+CONFIG_KEYBOARD_MC9S08DZ60=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_MXC=y
+CONFIG_TOUCHSCREEN_TSC2007=y
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 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
+CONFIG_FM_SI4702=m
+CONFIG_MXC_IIM=y
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_MXC=y
+CONFIG_SERIAL_MXC_CONSOLE=y
+# CONFIG_SERIAL_IMX is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM 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_MXC=y
+# CONFIG_I2C_MXC_HS 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
+# 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_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 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_I2C_SLAVE is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_MXC=y
+# CONFIG_SPI_MXC_TEST_LOOPBACK is not set
+CONFIG_SPI_MXC_SELECT1=y
+# CONFIG_SPI_MXC_SELECT2 is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 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=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_MXC_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_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_COMMON=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=y
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set
+
+#
+# Encoders/decoders and other helper chips
+#
+
+#
+# Audio decoders
+#
+# CONFIG_VIDEO_TVAUDIO is not set
+# CONFIG_VIDEO_TDA7432 is not set
+# CONFIG_VIDEO_TDA9840 is not set
+# CONFIG_VIDEO_TDA9875 is not set
+# CONFIG_VIDEO_TEA6415C is not set
+# CONFIG_VIDEO_TEA6420 is not set
+# CONFIG_VIDEO_MSP3400 is not set
+# CONFIG_VIDEO_CS5345 is not set
+# CONFIG_VIDEO_CS53L32A is not set
+# CONFIG_VIDEO_M52790 is not set
+# CONFIG_VIDEO_TLV320AIC23B is not set
+# CONFIG_VIDEO_WM8775 is not set
+# CONFIG_VIDEO_WM8739 is not set
+# CONFIG_VIDEO_VP27SMPX is not set
+
+#
+# Video decoders
+#
+# CONFIG_VIDEO_BT819 is not set
+# CONFIG_VIDEO_BT856 is not set
+# CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_KS0127 is not set
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_TCM825X is not set
+# CONFIG_VIDEO_SAA7110 is not set
+# CONFIG_VIDEO_SAA7111 is not set
+# CONFIG_VIDEO_SAA7114 is not set
+# CONFIG_VIDEO_SAA711X is not set
+# CONFIG_VIDEO_SAA717X is not set
+# CONFIG_VIDEO_SAA7191 is not set
+# CONFIG_VIDEO_TVP5150 is not set
+# CONFIG_VIDEO_VPX3220 is not set
+
+#
+# Video and audio decoders
+#
+# CONFIG_VIDEO_CX25840 is not set
+
+#
+# MPEG video encoders
+#
+# CONFIG_VIDEO_CX2341X is not set
+
+#
+# Video encoders
+#
+# CONFIG_VIDEO_SAA7127 is not set
+# CONFIG_VIDEO_SAA7185 is not set
+# CONFIG_VIDEO_ADV7170 is not set
+# CONFIG_VIDEO_ADV7175 is not set
+
+#
+# Video improvement chips
+#
+# CONFIG_VIDEO_UPD64031A is not set
+# CONFIG_VIDEO_UPD64083 is not set
+# CONFIG_VIDEO_VIVI is not set
+CONFIG_VIDEO_MXC_CAMERA=m
+
+#
+# MXC Camera/V4L2 PRP Features support
+#
+CONFIG_VIDEO_MXC_IPU_CAMERA=y
+# CONFIG_VIDEO_MXC_CSI_CAMERA is not set
+# CONFIG_MXC_CAMERA_MC521DA is not set
+# CONFIG_MXC_EMMA_CAMERA_MICRON111 is not set
+# CONFIG_MXC_CAMERA_OV2640_EMMA is not set
+# CONFIG_MXC_CAMERA_MICRON111 is not set
+CONFIG_MXC_CAMERA_OV2640=m
+# CONFIG_MXC_CAMERA_OV3640 is not set
+CONFIG_MXC_TVIN_ADV7180=m
+CONFIG_MXC_IPU_PRP_VF_SDC=m
+CONFIG_MXC_IPU_PRP_ENC=m
+CONFIG_VIDEO_MXC_OUTPUT=y
+CONFIG_VIDEO_MXC_IPU_OUTPUT=y
+# CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT is not set
+# CONFIG_VIDEO_MXC_OPL is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_SOC_CAMERA is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_GSPCA=m
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_S2255 is not set
+# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+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
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+CONFIG_FB_MXC=y
+CONFIG_FB_MXC_SYNC_PANEL=y
+# CONFIG_FB_MXC_EPSON_VGA_SYNC_PANEL is not set
+CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL=y
+# CONFIG_FB_MXC_CH7026 is not set
+# CONFIG_FB_MXC_TVOUT is not set
+# CONFIG_FB_MXC_TVOUT_CH7024 is not set
+# CONFIG_FB_MXC_ASYNC_PANEL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_MXC=y
+CONFIG_BACKLIGHT_MXC_IPU=y
+CONFIG_BACKLIGHT_MXC_MC13892=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_SOUND=y
+CONFIG_SOUND_OSS_CORE=y
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+CONFIG_SND_ARM=y
+CONFIG_SND_MXC_SPDIF=m
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_MXC_SOC=y
+CONFIG_SND_MXC_SOC_SSI=y
+CONFIG_SND_MXC_SOC_ESAI=y
+CONFIG_SND_MXC_SOC_IRAM=y
+CONFIG_SND_SOC_IMX_3STACK_SGTL5000=y
+CONFIG_SND_SOC_IMX_3STACK_AK4647=y
+CONFIG_SND_SOC_IMX_3STACK_WM8580=y
+# CONFIG_SND_SOC_IMX_3STACK_AK5702 is not set
+CONFIG_SND_SOC_IMX_3STACK_BLUETOOTH=y
+# CONFIG_SND_SOC_ALL_CODECS is not set
+CONFIG_SND_SOC_WM8580=y
+CONFIG_SND_SOC_SGTL5000=y
+CONFIG_SND_SOC_AK4647=y
+CONFIG_SND_SOC_BLUETOOTH=y
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE is not set
+
+#
+# Special HID drivers
+#
+CONFIG_HID_COMPAT=y
+CONFIG_HID_A4TECH=m
+CONFIG_HID_APPLE=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_BRIGHT=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_CHICONY=m
+CONFIG_HID_CYPRESS=m
+CONFIG_HID_DELL=m
+CONFIG_HID_EZKEY=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_LOGITECH=m
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MONTEREY=m
+CONFIG_HID_PANTHERLORD=m
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_HID_SUNPLUS=m
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_ZEROPLUS_FF 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=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# 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_EHCI_HCD=m
+CONFIG_USB_EHCI_ARC=y
+CONFIG_USB_EHCI_ARC_H2=y
+CONFIG_USB_EHCI_ARC_OTG=y
+# CONFIG_USB_STATIC_IRAM is not set
+# CONFIG_USB_EHCI_FSL_MC13783 is not set
+# CONFIG_USB_EHCI_FSL_1301 is not set
+# CONFIG_USB_EHCI_FSL_1504 is not set
+CONFIG_USB_EHCI_FSL_UTMI=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD 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 information
+#
+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_DPCM 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_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+CONFIG_USB_GADGET=m
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# 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_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_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+CONFIG_USB_GADGET_ARC=y
+CONFIG_USB_ARC=m
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+CONFIG_USB_GADGET_ARC_OTG=y
+# CONFIG_USB_GADGET_FSL_MC13783 is not set
+# CONFIG_USB_GADGET_FSL_1301 is not set
+# CONFIG_USB_GADGET_FSL_1504 is not set
+CONFIG_USB_GADGET_FSL_UTMI=y
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# 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
+CONFIG_SDIO_UNIFI_FS=m
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_MXC is not set
+CONFIG_MMC_IMX_ESDHCI=m
+# CONFIG_MMC_IMX_ESDHCI_PIO_MODE 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
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+
+#
+# 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_MXC=y
+# CONFIG_RTC_DRV_MXC_V2 is not set
+# CONFIG_RTC_DRV_IMXDI is not set
+CONFIG_RTC_MC13892=y
+# CONFIG_DMADEVICES is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+CONFIG_REGULATOR_MC13892=y
+CONFIG_REGULATOR_MC9S08DZ60=y
+# CONFIG_UIO is not set
+
+#
+# MXC support drivers
+#
+CONFIG_MXC_IPU=y
+CONFIG_MXC_IPU_V1=y
+CONFIG_MXC_IPU_PF=y
+
+#
+# MXC SSI support
+#
+# CONFIG_MXC_SSI is not set
+
+#
+# MXC Digital Audio Multiplexer support
+#
+# CONFIG_MXC_DAM is not set
+
+#
+# MXC PMIC support
+#
+CONFIG_MXC_PMIC=y
+# CONFIG_MXC_PMIC_MC13783 is not set
+CONFIG_MXC_PMIC_MC13892=y
+CONFIG_MXC_PMIC_I2C=y
+# CONFIG_MXC_PMIC_SPI is not set
+# CONFIG_MXC_PMIC_MC34704 is not set
+CONFIG_MXC_PMIC_MC9SDZ60=y
+# CONFIG_MXC_PMIC_CHARDEV is not set
+
+#
+# MXC PMIC Client Drivers
+#
+CONFIG_MXC_MC13892_ADC=y
+CONFIG_MXC_MC13892_RTC=y
+CONFIG_MXC_MC13892_LIGHT=y
+# CONFIG_MXC_MC13892_BATTERY is not set
+# CONFIG_MXC_MC13892_CONNECTIVITY is not set
+CONFIG_MXC_MC13892_POWER=y
+CONFIG_MXC_PMIC_MC9S08DZ60=y
+# CONFIG_MXC_MC9SDZ60_RTC is not set
+
+#
+# MXC Security Drivers
+#
+CONFIG_MXC_SECURITY_SCC=y
+# CONFIG_SCC_DEBUG is not set
+CONFIG_MXC_SECURITY_RNG=y
+# CONFIG_MXC_RNG_TEST_DRIVER is not set
+# CONFIG_MXC_RNG_DEBUG is not set
+
+#
+# MXC MPEG4 Encoder Kernel module support
+#
+# CONFIG_MXC_HMP4E is not set
+
+#
+# MXC HARDWARE EVENT
+#
+# CONFIG_MXC_HWEVENT is not set
+
+#
+# MXC VPU(Video Processing Unit) support
+#
+
+#
+# MXC Asynchronous Sample Rate Converter support
+#
+CONFIG_MXC_ASRC=y
+
+#
+# MXC Bluetooth support
+#
+CONFIG_MXC_BLUETOOTH=m
+
+#
+# Broadcom GPS ioctrl support
+#
+CONFIG_GPS_IOCTRL=m
+
+#
+# MXC Media Local Bus Driver
+#
+CONFIG_MXC_MLB=m
+
+#
+# i.MX ADC support
+#
+# CONFIG_IMX_ADC 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 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_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+# 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
+
+#
+# Miscellaneous filesystems
+#
+# 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=y
+# 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_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_SUNRPC_REGISTER_V4 is not set
+# 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 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=m
+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=m
+# 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 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=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG 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=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
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_CRYPTODEV 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 is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=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_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/imx35evb_defconfig b/arch/arm/configs/imx35evb_defconfig
new file mode 100644
index 000000000000..1e2ab3b76f24
--- /dev/null
+++ b/arch/arm/configs/imx35evb_defconfig
@@ -0,0 +1,976 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.24
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+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_LOCKDEP_SUPPORT=y
+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_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# 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=y
+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_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_AUDIT is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_FAIR_USER_SCHED=y
+# CONFIG_FAIR_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY 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 is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# 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_BLK_DEV_BSG 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"
+
+#
+# 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_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE 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
+# 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_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+CONFIG_ARCH_MXC=y
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Freescale MXC Implementations
+#
+# CONFIG_ARCH_MXC91321 is not set
+# CONFIG_ARCH_MX37 is not set
+CONFIG_ARCH_MX35=y
+# CONFIG_ARCH_MX3 is not set
+# CONFIG_ARCH_MX27 is not set
+# CONFIG_ARCH_MX21 is not set
+CONFIG_MXC_SDMA_API=y
+
+#
+# MX35 Options
+#
+# CONFIG_MACH_MX35_3DS is not set
+CONFIG_MACH_MX35EVB=y
+# CONFIG_MX35_DOZE_DURING_IDLE is not set
+
+#
+# Device options
+#
+CONFIG_ARCH_HAS_EVTMON=y
+CONFIG_DMA_ZONE_SIZE=24
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+# CONFIG_CPU_ARM926T is not set
+CONFIG_CPU_V6=y
+# CONFIG_CPU_32v6K is not set
+CONFIG_CPU_32v6=y
+CONFIG_CPU_ABRT_EV6=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_OUTER_CACHE=y
+CONFIG_CACHE_L2X0=y
+
+#
+# Bus support
+#
+CONFIG_ISA=y
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCCARD=m
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=m
+CONFIG_PCMCIA_LOAD_CIS=y
+# CONFIG_PCMCIA_IOCTL is not set
+
+#
+# PC-card bridges
+#
+# CONFIG_I82365 is not set
+# CONFIG_TCIC is not set
+CONFIG_PCMCIA_PROBE=y
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE 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_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/mtdblock2 rw rootfstype=jffs2 ip=off"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_SUSPEND_UP_POSSIBLE=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+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_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE 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=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_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL 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_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_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=y
+# CONFIG_IEEE80211_DEBUG is not set
+# CONFIG_IEEE80211_CRYPT_WEP is not set
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP is not set
+# CONFIG_IEEE80211_SOFTMAC 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=m
+# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+# CONFIG_MTD is not set
+
+#
+# Voltage and Current regulators
+#
+# CONFIG_REGULATOR is not set
+# CONFIG_PARPORT is not set
+# CONFIG_PNP 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 is not set
+# CONFIG_BLK_DEV_UB 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=y
+# CONFIG_EEPROM_93CX6 is not set
+# 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
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+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=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# 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_ARCNET is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# 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_SMC911X is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_NET_VENDOR_RACAL 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_NET_PCI=y
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_B44 is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_FEC is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_TR is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=m
+# 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=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 is not set
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_SHAPER is not set
+# 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 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 is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_MXC_MU is not set
+# CONFIG_MXC_SUPER_GEM is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_MXC=y
+CONFIG_SERIAL_MXC_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_DEVPORT=y
+# CONFIG_I2C is not set
+
+#
+# SPI support
+#
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_MXC=y
+# CONFIG_SPI_MXC_TEST_LOOPBACK is not set
+CONFIG_SPI_MXC_SELECT1=y
+# CONFIG_SPI_MXC_SELECT2 is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_MXC_WATCHDOG=y
+
+#
+# ISA-based Watchdog Cards
+#
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_MIXCOMWD is not set
+# CONFIG_WDT is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_DAB 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
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+CONFIG_HID_SUPPORT=y
+# CONFIG_HID 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
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# On-The-Go and USB Peripheral Support
+#
+# CONFIG_OTG is not set
+
+#
+#
+#
+
+#
+#
+#
+# CONFIG_MMC 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
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_MXC=y
+# CONFIG_RTC_DRV_MXC_V2 is not set
+
+#
+# MXC support drivers
+#
+# CONFIG_MXC_IPU is not set
+
+#
+# MXC SSI support
+#
+# CONFIG_MXC_SSI is not set
+
+#
+# MXC Digital Audio Multiplexer support
+#
+# CONFIG_MXC_DAM is not set
+
+#
+# MXC PMIC support
+#
+# CONFIG_MXC_PMIC is not set
+
+#
+# Advanced Power Management devices
+#
+
+#
+# MXC Security Drivers
+#
+# CONFIG_MXC_SECURITY_SCC is not set
+# CONFIG_MXC_SECURITY_RNG is not set
+# CONFIG_MXC_SECURITY_RTIC is not set
+
+#
+# MXC MPEG4 Encoder Kernel module support
+#
+# CONFIG_MXC_HMP4E is not set
+
+#
+# MXC HARDWARE EVENT
+#
+CONFIG_MXC_HWEVENT=y
+
+#
+# MXC VPU(Video Processing Unit) support
+#
+
+#
+# File systems
+#
+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_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_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+# CONFIG_FUSE_FS 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_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# 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=y
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_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_NFS_DIRECTIO is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_BIND34 is not set
+# 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 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=m
+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=m
+# CONFIG_DLM is not set
+CONFIG_INSTRUMENTATION=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_OPROFILE_ARMV6=y
+CONFIG_OPROFILE_ARM11_CORE=y
+CONFIG_OPROFILE_ARM11_EVTMON=y
+# CONFIG_MARKERS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+# 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_DEBUG_BUGVERBOSE is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=m
+# CONFIG_CRC16 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_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/imx37_3stack_defconfig b/arch/arm/configs/imx37_3stack_defconfig
new file mode 100644
index 000000000000..2d01ad697a57
--- /dev/null
+++ b/arch/arm/configs/imx37_3stack_defconfig
@@ -0,0 +1,1828 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28
+# Wed May 27 17:22:50 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+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
+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_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_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"
+
+#
+# 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=y
+CONFIG_SWAP=y
+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
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED 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 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 is not set
+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
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=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_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=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# 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_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_CLASSIC_RCU=y
+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_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE 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
+# 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_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=y
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_STMP3XXX is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Freescale MXC Implementations
+#
+CONFIG_ARCH_MX37=y
+# CONFIG_ARCH_MX35 is not set
+# CONFIG_ARCH_MX51 is not set
+# CONFIG_ARCH_MX3 is not set
+# CONFIG_ARCH_MX27 is not set
+# CONFIG_ARCH_MX25 is not set
+CONFIG_I2C_MXC_SELECT1=y
+CONFIG_I2C_MXC_SELECT2=y
+CONFIG_MXC_SDMA_API=y
+# CONFIG_I2C_MXC_SELECT3 is not set
+CONFIG_SDMA_IRAM=y
+CONFIG_SDMA_IRAM_SIZE=0x1000
+
+#
+# MX37 Options
+#
+CONFIG_MX37_OPTIONS=y
+CONFIG_MACH_MX37_3DS=y
+
+#
+# SDMA options
+#
+CONFIG_ARCH_MXC_HAS_NFC_V3=y
+CONFIG_ARCH_MXC_HAS_NFC_V3_1=y
+
+#
+# Device options
+#
+CONFIG_MXC_TZIC=y
+CONFIG_ARCH_HAS_RNGC=y
+CONFIG_ARCH_HAS_EVTMON=y
+CONFIG_DMA_ZONE_SIZE=32
+CONFIG_UTMI_MXC=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+# CONFIG_CPU_ARM926T is not set
+CONFIG_CPU_V6=y
+CONFIG_CPU_32v6K=y
+# CONFIG_CPU_V7 is not set
+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_OUTER_CACHE=y
+CONFIG_CACHE_L2X0=y
+# CONFIG_ARM_ERRATA_364296 is not set
+CONFIG_ARM_ERRATA_411920=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+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_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL 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_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_UNEVICTABLE_LRU=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/mtdblock2 rw rootfstype=jffs2 ip=off"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_IMX=y
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+
+#
+# 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=y
+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 is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE 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=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_NET_SCHED 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_PHONET is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_OLD_REGULATORY=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=y
+# CONFIG_IEEE80211_DEBUG is not set
+# CONFIG_IEEE80211_CRYPT_WEP is not set
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP 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=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# 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 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
+# CONFIG_MTD_MXC is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# 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_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+CONFIG_MTD_NAND_MXC_V3=y
+# CONFIG_MTD_NAND_MXC_SWECC is not set
+# CONFIG_MTD_NAND_MXC_FORCE_CE is not set
+# CONFIG_MXC_NAND_LOW_LEVEL_ERASE is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND 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 is not set
+# CONFIG_BLK_DEV_UB 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=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT 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=y
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+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=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+CONFIG_ATA=m
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_SATA_PMP is not set
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_MV is not set
+# CONFIG_PATA_PLATFORM is not set
+CONFIG_PATA_FSL=m
+# 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 is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_SMC911X is not set
+CONFIG_SMSC911X=y
+# 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_CS89x0 is not set
+# CONFIG_FEC 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
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# 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=m
+# CONFIG_USB_NET_AX8817X is not set
+CONFIG_USB_NET_CDCETHER=m
+# CONFIG_USB_NET_DM9601 is not set
+# CONFIG_USB_NET_SMSC95XX is not set
+# CONFIG_USB_NET_GL620A is not set
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# 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=y
+
+#
+# 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_MXC is not set
+CONFIG_KEYBOARD_MPR084=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_TSC2007=y
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 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
+# CONFIG_FM_SI4702 is not set
+CONFIG_MXC_IIM=y
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_MXC=y
+CONFIG_SERIAL_MXC_CONSOLE=y
+# CONFIG_SERIAL_IMX is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_HW_RANDOM_FSL_RNGC is not set
+# CONFIG_NVRAM 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_MXC=y
+# CONFIG_I2C_MXC_HS 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
+# 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_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 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_I2C_SLAVE is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_MXC=y
+# CONFIG_SPI_MXC_TEST_LOOPBACK is not set
+# CONFIG_SPI_MXC_SELECT1 is not set
+CONFIG_SPI_MXC_SELECT2=y
+# CONFIG_SPI_MXC_SELECT3 is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+# 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_ADCXX 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_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_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 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_MAX1111 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_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_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_SENSORS_ISL29003=y
+# 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_MXC_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_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+CONFIG_MFD_WM8350=y
+CONFIG_MFD_WM8350_CONFIG_MODE_0=y
+CONFIG_MFD_WM8350_I2C=y
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_COMMON=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=y
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set
+
+#
+# Encoders/decoders and other helper chips
+#
+
+#
+# Audio decoders
+#
+# CONFIG_VIDEO_TVAUDIO is not set
+# CONFIG_VIDEO_TDA7432 is not set
+# CONFIG_VIDEO_TDA9840 is not set
+# CONFIG_VIDEO_TDA9875 is not set
+# CONFIG_VIDEO_TEA6415C is not set
+# CONFIG_VIDEO_TEA6420 is not set
+# CONFIG_VIDEO_MSP3400 is not set
+# CONFIG_VIDEO_CS5345 is not set
+# CONFIG_VIDEO_CS53L32A is not set
+# CONFIG_VIDEO_M52790 is not set
+# CONFIG_VIDEO_TLV320AIC23B is not set
+# CONFIG_VIDEO_WM8775 is not set
+# CONFIG_VIDEO_WM8739 is not set
+# CONFIG_VIDEO_VP27SMPX is not set
+
+#
+# Video decoders
+#
+# CONFIG_VIDEO_BT819 is not set
+# CONFIG_VIDEO_BT856 is not set
+# CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_KS0127 is not set
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_TCM825X is not set
+# CONFIG_VIDEO_SAA7110 is not set
+# CONFIG_VIDEO_SAA7111 is not set
+# CONFIG_VIDEO_SAA7114 is not set
+# CONFIG_VIDEO_SAA711X is not set
+# CONFIG_VIDEO_SAA717X is not set
+# CONFIG_VIDEO_SAA7191 is not set
+# CONFIG_VIDEO_TVP5150 is not set
+# CONFIG_VIDEO_VPX3220 is not set
+
+#
+# Video and audio decoders
+#
+# CONFIG_VIDEO_CX25840 is not set
+
+#
+# MPEG video encoders
+#
+# CONFIG_VIDEO_CX2341X is not set
+
+#
+# Video encoders
+#
+# CONFIG_VIDEO_SAA7127 is not set
+# CONFIG_VIDEO_SAA7185 is not set
+# CONFIG_VIDEO_ADV7170 is not set
+# CONFIG_VIDEO_ADV7175 is not set
+
+#
+# Video improvement chips
+#
+# CONFIG_VIDEO_UPD64031A is not set
+# CONFIG_VIDEO_UPD64083 is not set
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_MXC_CAMERA is not set
+CONFIG_VIDEO_MXC_OUTPUT=y
+CONFIG_VIDEO_MXC_IPU_OUTPUT=y
+# CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT is not set
+# CONFIG_VIDEO_MXC_OPL is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_SOC_CAMERA is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_GSPCA=m
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_S2255 is not set
+# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+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
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+CONFIG_FB_MXC=y
+CONFIG_FB_MXC_SYNC_PANEL=y
+CONFIG_FB_MXC_EPSON_VGA_SYNC_PANEL=y
+CONFIG_FB_MXC_TVOUT_TVE=y
+# CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL is not set
+# CONFIG_FB_MXC_CH7026 is not set
+# CONFIG_FB_MXC_TVOUT is not set
+# CONFIG_FB_MXC_TVOUT_CH7024 is not set
+# CONFIG_FB_MXC_ASYNC_PANEL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_MXC=y
+CONFIG_BACKLIGHT_WM8350=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_SOUND=y
+CONFIG_SOUND_OSS_CORE=y
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+CONFIG_SND_ARM=y
+CONFIG_SND_MXC_SPDIF=m
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_MXC_SOC=y
+CONFIG_SND_MXC_SOC_SSI=y
+# CONFIG_SND_MXC_SOC_IRAM is not set
+CONFIG_SND_SOC_IMX_3STACK_WM8350=y
+# CONFIG_SND_SOC_IMX_3STACK_SGTL5000 is not set
+# CONFIG_SND_SOC_IMX_3STACK_AK4647 is not set
+# CONFIG_SND_SOC_IMX_3STACK_WM8580 is not set
+# CONFIG_SND_SOC_IMX_3STACK_BLUETOOTH is not set
+# CONFIG_SND_SOC_ALL_CODECS is not set
+CONFIG_SND_SOC_WM8350=y
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE is not set
+
+#
+# Special HID drivers
+#
+CONFIG_HID_COMPAT=y
+CONFIG_HID_A4TECH=m
+CONFIG_HID_APPLE=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_BRIGHT=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_CHICONY=m
+CONFIG_HID_CYPRESS=m
+CONFIG_HID_DELL=m
+CONFIG_HID_EZKEY=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_LOGITECH=m
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MONTEREY=m
+CONFIG_HID_PANTHERLORD=m
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_HID_SUNPLUS=m
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_ZEROPLUS_FF 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=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_DEVICE_CLASS is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB 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_EHCI_HCD=m
+CONFIG_USB_EHCI_ARC=y
+CONFIG_USB_EHCI_ARC_OTG=y
+# CONFIG_USB_EHCI_ARC_OTG_WAKE_UP is not set
+# CONFIG_USB_STATIC_IRAM is not set
+# CONFIG_USB_EHCI_FSL_MC13783 is not set
+# CONFIG_USB_EHCI_FSL_1301 is not set
+# CONFIG_USB_EHCI_FSL_1504 is not set
+CONFIG_USB_EHCI_FSL_UTMI=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD 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 information
+#
+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_DPCM 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_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+CONFIG_USB_GADGET=m
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# 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_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_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+CONFIG_USB_GADGET_ARC=y
+# CONFIG_USB_STATIC_IRAM_PPH is not set
+CONFIG_USB_ARC=m
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+CONFIG_USB_GADGET_ARC_OTG=y
+# CONFIG_USB_GADGET_WAKE_UP is not set
+# CONFIG_USB_GADGET_FSL_MC13783 is not set
+# CONFIG_USB_GADGET_FSL_1301 is not set
+# CONFIG_USB_GADGET_FSL_1504 is not set
+CONFIG_USB_GADGET_FSL_UTMI=y
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_ETH is not set
+# CONFIG_USB_GADGETFS is not set
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST 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
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# 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
+CONFIG_SDIO_UNIFI_FS=m
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_MXC is not set
+CONFIG_MMC_IMX_ESDHCI=m
+# CONFIG_MMC_IMX_ESDHCI_PIO_MODE 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=y
+# 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
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+
+#
+# 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
+# CONFIG_RTC_DRV_WM8350 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_MXC is not set
+CONFIG_RTC_DRV_MXC_V2=y
+# CONFIG_RTC_DRV_IMXDI is not set
+# CONFIG_DMADEVICES is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+CONFIG_REGULATOR_WM8350=y
+# CONFIG_UIO is not set
+
+#
+# MXC support drivers
+#
+CONFIG_MXC_IPU=y
+CONFIG_MXC_IPU_V3=y
+CONFIG_MXC_IPU_V3D=y
+
+#
+# MXC SSI support
+#
+# CONFIG_MXC_SSI is not set
+
+#
+# MXC Digital Audio Multiplexer support
+#
+# CONFIG_MXC_DAM is not set
+
+#
+# MXC PMIC support
+#
+# CONFIG_MXC_PMIC_MC13783 is not set
+# CONFIG_MXC_PMIC_MC13892 is not set
+# CONFIG_MXC_PMIC_MC34704 is not set
+# CONFIG_MXC_PMIC_MC9SDZ60 is not set
+# CONFIG_MXC_PMIC_MC9S08DZ60 is not set
+
+#
+# MXC Security Drivers
+#
+# CONFIG_MXC_SECURITY_SCC is not set
+# CONFIG_MXC_SECURITY_SCC2 is not set
+# CONFIG_MXC_SECURITY_RNG is not set
+
+#
+# SAHARA2 Security Hardware Support
+#
+# CONFIG_MXC_SAHARA is not set
+
+#
+# MXC MPEG4 Encoder Kernel module support
+#
+# CONFIG_MXC_HMP4E is not set
+
+#
+# MXC HARDWARE EVENT
+#
+# CONFIG_MXC_HWEVENT is not set
+
+#
+# MXC VPU(Video Processing Unit) support
+#
+CONFIG_MXC_VPU=y
+CONFIG_MXC_VPU_IRAM=y
+# CONFIG_MXC_VPU_DEBUG is not set
+
+#
+# MXC Asynchronous Sample Rate Converter support
+#
+
+#
+# MXC Bluetooth support
+#
+CONFIG_MXC_BLUETOOTH=m
+
+#
+# Broadcom GPS ioctrl support
+#
+CONFIG_GPS_IOCTRL=m
+
+#
+# MXC Media Local Bus Driver
+#
+
+#
+# i.MX ADC support
+#
+# CONFIG_IMX_ADC 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 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_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+# CONFIG_FUSE_FS 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
+
+#
+# Miscellaneous filesystems
+#
+# 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=y
+# 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_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_SUNRPC_REGISTER_V4 is not set
+# 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 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=m
+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=m
+# 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 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=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG 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=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
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_CRYPTODEV 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 is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=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_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/imx51_3stack_defconfig b/arch/arm/configs/imx51_3stack_defconfig
new file mode 100644
index 000000000000..a9aa7ee0bba6
--- /dev/null
+++ b/arch/arm/configs/imx51_3stack_defconfig
@@ -0,0 +1,1805 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.26
+# Wed Feb 11 14:35:12 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+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_LOCKDEP_SUPPORT=y
+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_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# 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=y
+CONFIG_SWAP=y
+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
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED 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 is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=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_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_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=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_DMA_ATTRS is not set
+CONFIG_PROC_PAGE_MONITOR=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=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# 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_BLK_DEV_BSG 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_CLASSIC_RCU=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_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE 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
+# 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_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+CONFIG_ARCH_MXC=y
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM7X00A is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Freescale MXC Implementations
+#
+# CONFIG_ARCH_MX37 is not set
+# CONFIG_ARCH_MX35 is not set
+CONFIG_ARCH_MX51=y
+# CONFIG_ARCH_MX3 is not set
+# CONFIG_ARCH_MX27 is not set
+# CONFIG_ARCH_MX25 is not set
+# CONFIG_ARCH_MX21 is not set
+# CONFIG_I2C_MXC_SELECT1 is not set
+CONFIG_I2C_MXC_SELECT2=y
+CONFIG_MXC_SDMA_API=y
+# CONFIG_I2C_MXC_SELECT3 is not set
+CONFIG_SDMA_IRAM=y
+CONFIG_SDMA_IRAM_SIZE=0x1000
+CONFIG_FORCE_MAX_ZONEORDER=13
+CONFIG_ARCH_MXC_HAS_NFC_V3=y
+
+#
+# MX51 Options
+#
+CONFIG_MX51_OPTIONS=y
+CONFIG_MACH_MX51_3DS=y
+CONFIG_ARCH_MXC_HAS_NFC_V3_2=y
+
+#
+# SDMA options
+#
+
+#
+# Device options
+#
+CONFIG_MXC_TZIC=y
+CONFIG_DMA_ZONE_SIZE=64
+CONFIG_UTMI_MXC=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+# CONFIG_CPU_ARM926T is not set
+# CONFIG_CPU_V6 is not set
+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_OUTER_CACHE 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_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE 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_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/mtdblock2 rw rootfstype=jffs2 ip=off"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_IMX=y
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_NEON=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=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
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+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 is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE 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=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_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_NET_SCHED=y
+
+#
+# Queueing/Scheduling
+#
+# CONFIG_NET_SCH_CBQ is not set
+# CONFIG_NET_SCH_HTB is not set
+# CONFIG_NET_SCH_HFSC is not set
+# CONFIG_NET_SCH_PRIO is not set
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFQ is not set
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_DSMARK is not set
+# CONFIG_NET_SCH_NETEM is not set
+
+#
+# Classification
+#
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_TCINDEX is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_FW is not set
+# CONFIG_NET_CLS_U32 is not set
+# CONFIG_NET_CLS_RSVP is not set
+# CONFIG_NET_CLS_RSVP6 is not set
+# CONFIG_NET_CLS_FLOW is not set
+# CONFIG_NET_EMATCH is not set
+# CONFIG_NET_CLS_ACT is not set
+CONFIG_NET_SCH_FIFO=y
+
+#
+# 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
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=y
+# CONFIG_IEEE80211_DEBUG is not set
+# CONFIG_IEEE80211_CRYPT_WEP is not set
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP 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_SYS_HYPERVISOR is not set
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# 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 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
+# CONFIG_MTD_MXC is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# 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_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+CONFIG_MTD_NAND_MXC_V3=y
+# CONFIG_MTD_NAND_MXC_SWECC is not set
+# CONFIG_MTD_NAND_MXC_FORCE_CE is not set
+# CONFIG_MXC_NAND_LOW_LEVEL_ERASE is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+
+#
+# Voltage and Current regulators
+#
+CONFIG_REGULATOR_API=y
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+CONFIG_REGULATOR_MC13892=y
+# CONFIG_REGULATOR_WM8350 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 is not set
+# CONFIG_BLK_DEV_UB 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=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES 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
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+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=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_ATA=m
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_SATA_PMP is not set
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_MV is not set
+# CONFIG_PATA_PLATFORM is not set
+CONFIG_PATA_FSL=m
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# 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 is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_SMC911X is not set
+CONFIG_SMSC911X=y
+# 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_B44 is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_FEC 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
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# 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 is not set
+# CONFIG_SLIP is not set
+# 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=y
+
+#
+# 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_MXC=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_MXC=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_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TSC2007 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_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_FM_SI4702=m
+CONFIG_MXC_IIM=y
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_MXC=y
+CONFIG_SERIAL_MXC_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM 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
+
+#
+# I2C Hardware Bus support
+#
+CONFIG_I2C_MXC=y
+CONFIG_I2C_MXC_HS=y
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 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_I2C_SLAVE is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_MXC=y
+# CONFIG_SPI_MXC_TEST_LOOPBACK is not set
+CONFIG_SPI_MXC_SELECT1=y
+# CONFIG_SPI_MXC_SELECT2 is not set
+# CONFIG_SPI_MXC_SELECT3 is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+CONFIG_W1=m
+CONFIG_W1_CON=y
+
+#
+# 1-wire Bus Masters
+#
+# CONFIG_W1_MASTER_DS2490 is not set
+# CONFIG_W1_MASTER_DS2482 is not set
+CONFIG_W1_MASTER_MXC=m
+# CONFIG_W1_MASTER_DS1WM is not set
+
+#
+# 1-wire Slaves
+#
+# CONFIG_W1_SLAVE_THERM is not set
+# CONFIG_W1_SLAVE_SMEM is not set
+# CONFIG_W1_SLAVE_DS2751 is not set
+# CONFIG_W1_SLAVE_DS2433 is not set
+CONFIG_W1_SLAVE_DS2438=m
+# CONFIG_W1_SLAVE_DS2760 is not set
+CONFIG_POWER_SUPPLY=m
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_BATTERY_DS2760 is not set
+CONFIG_HWMON=y
+# CONFIG_HWMON_VID 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_ADT7470 is not set
+# CONFIG_SENSORS_ADT7473 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_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 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_MAX1619 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 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_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_SENSORS_ISL29003=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_MXC_WATCHDOG=y
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_PASIC3 is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_COMMON=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=y
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_TEA5761=y
+CONFIG_MEDIA_TUNER_TEA5767=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set
+
+#
+# Encoders/decoders and other helper chips
+#
+
+#
+# Audio decoders
+#
+# CONFIG_VIDEO_TVAUDIO is not set
+# CONFIG_VIDEO_TDA7432 is not set
+# CONFIG_VIDEO_TDA9840 is not set
+# CONFIG_VIDEO_TDA9875 is not set
+# CONFIG_VIDEO_TEA6415C is not set
+# CONFIG_VIDEO_TEA6420 is not set
+# CONFIG_VIDEO_MSP3400 is not set
+# CONFIG_VIDEO_CS5345 is not set
+# CONFIG_VIDEO_CS53L32A is not set
+# CONFIG_VIDEO_M52790 is not set
+# CONFIG_VIDEO_TLV320AIC23B is not set
+# CONFIG_VIDEO_WM8775 is not set
+# CONFIG_VIDEO_WM8739 is not set
+# CONFIG_VIDEO_VP27SMPX is not set
+
+#
+# Video decoders
+#
+# CONFIG_VIDEO_BT819 is not set
+# CONFIG_VIDEO_BT856 is not set
+# CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_KS0127 is not set
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_TCM825X is not set
+# CONFIG_VIDEO_SAA7110 is not set
+# CONFIG_VIDEO_SAA7111 is not set
+# CONFIG_VIDEO_SAA7114 is not set
+# CONFIG_VIDEO_SAA711X is not set
+# CONFIG_VIDEO_SAA717X is not set
+# CONFIG_VIDEO_SAA7191 is not set
+# CONFIG_VIDEO_TVP5150 is not set
+# CONFIG_VIDEO_VPX3220 is not set
+
+#
+# Video and audio decoders
+#
+# CONFIG_VIDEO_CX25840 is not set
+
+#
+# MPEG video encoders
+#
+# CONFIG_VIDEO_CX2341X is not set
+
+#
+# Video encoders
+#
+# CONFIG_VIDEO_SAA7127 is not set
+# CONFIG_VIDEO_SAA7185 is not set
+# CONFIG_VIDEO_ADV7170 is not set
+# CONFIG_VIDEO_ADV7175 is not set
+
+#
+# Video improvement chips
+#
+# CONFIG_VIDEO_UPD64031A is not set
+# CONFIG_VIDEO_UPD64083 is not set
+# CONFIG_VIDEO_VIVI is not set
+CONFIG_VIDEO_MXC_CAMERA=m
+
+#
+# MXC Camera/V4L2 PRP Features support
+#
+CONFIG_VIDEO_MXC_IPU_CAMERA=y
+# CONFIG_MXC_CAMERA_MC521DA is not set
+# CONFIG_MXC_EMMA_CAMERA_MICRON111 is not set
+# CONFIG_MXC_CAMERA_OV2640_EMMA is not set
+# CONFIG_MXC_CAMERA_MICRON111 is not set
+# CONFIG_MXC_CAMERA_OV2640 is not set
+CONFIG_MXC_CAMERA_OV3640=m
+# CONFIG_MXC_TVIN_ADV7180 is not set
+CONFIG_MXC_IPU_PRP_VF_SDC=m
+CONFIG_MXC_IPU_PRP_ENC=m
+CONFIG_VIDEO_MXC_OUTPUT=y
+CONFIG_VIDEO_MXC_IPU_OUTPUT=y
+# CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT is not set
+# CONFIG_VIDEO_MXC_OPL is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_W9968CF is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_SOC_CAMERA is not set
+# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+CONFIG_FB_MXC=y
+CONFIG_FB_MXC_SYNC_PANEL=y
+CONFIG_FB_MXC_EPSON_VGA_SYNC_PANEL=y
+CONFIG_FB_MXC_TVOUT_TVE=y
+# CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL is not set
+# CONFIG_FB_MXC_TVOUT is not set
+# CONFIG_FB_MXC_TVOUT_CH7024 is not set
+# CONFIG_FB_MXC_ASYNC_PANEL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_MXC=y
+CONFIG_BACKLIGHT_MXC_MC13892=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+CONFIG_SND_MXC_SPDIF=m
+
+#
+# SPI devices
+#
+
+#
+# USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# System on Chip audio support
+#
+CONFIG_SND_SOC=y
+CONFIG_SND_MXC_SOC=y
+CONFIG_SND_MXC_SOC_SSI=y
+CONFIG_SND_MXC_SOC_IRAM=y
+# CONFIG_SND_SOC_IMX_3STACK_WM8350 is not set
+# CONFIG_SND_SOC_IMX_3STACK_AK4647 is not set
+# CONFIG_SND_SOC_IMX_3STACK_WM8580 is not set
+CONFIG_SND_SOC_IMX_3STACK_WM8903=y
+CONFIG_SND_SOC_IMX_3STACK_SGTL5000=y
+# CONFIG_SND_SOC_IMX_3STACK_BLUETOOTH is not set
+
+#
+# ALSA SoC audio for Freescale SOCs
+#
+
+#
+# SoC Audio for the Texas Instruments OMAP
+#
+CONFIG_SND_SOC_WM8903=y
+CONFIG_SND_SOC_SGTL5000=y
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE 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=y
+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=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+CONFIG_USB_EHCI_HCD=m
+CONFIG_USB_EHCI_ARC=y
+CONFIG_USB_EHCI_ARC_H1=y
+CONFIG_USB_EHCI_ARC_OTG=y
+# CONFIG_USB_STATIC_IRAM is not set
+# CONFIG_USB_EHCI_FSL_MC13783 is not set
+# CONFIG_USB_EHCI_FSL_1301 is not set
+# CONFIG_USB_EHCI_FSL_1504 is not set
+CONFIG_USB_EHCI_FSL_UTMI=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD 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
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+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_DPCM 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
+# CONFIG_USB_MON is not set
+
+#
+# Belcarra USBLAN Networking for USB
+#
+# CONFIG_USB_USBLAN 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_AUERSWALD 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_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA 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_GADGET=m
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_PXA2XX is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+CONFIG_USB_GADGET_ARC=y
+CONFIG_USB_ARC=m
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+CONFIG_USB_GADGET_ARC_OTG=y
+# CONFIG_USB_GADGET_FSL_MC13783 is not set
+# CONFIG_USB_GADGET_FSL_1301 is not set
+# CONFIG_USB_GADGET_FSL_1504 is not set
+CONFIG_USB_GADGET_FSL_UTMI=y
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+CONFIG_SDIO_UNIFI_FS=m
+
+#
+# MMC/SD Host Controller Drivers
+#
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_MXC is not set
+CONFIG_MMC_IMX_ESDHCI=m
+# CONFIG_MMC_IMX_ESDHCI_PIO_MODE is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_MC13892=y
+
+#
+# LED Triggers
+#
+# CONFIG_LEDS_TRIGGERS 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=y
+# 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
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS 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_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_MXC is not set
+CONFIG_RTC_DRV_MXC_V2=y
+# CONFIG_RTC_DRV_IMXDI is not set
+CONFIG_RTC_MC13892=m
+# CONFIG_UIO is not set
+
+#
+# MXC support drivers
+#
+CONFIG_MXC_IPU=y
+CONFIG_MXC_IPU_V3=y
+
+#
+# MXC SSI support
+#
+# CONFIG_MXC_SSI is not set
+
+#
+# MXC Digital Audio Multiplexer support
+#
+# CONFIG_MXC_DAM is not set
+
+#
+# MXC PMIC support
+#
+CONFIG_MXC_PMIC=y
+# CONFIG_MXC_PMIC_MC13783 is not set
+CONFIG_MXC_PMIC_MC13892=y
+CONFIG_MXC_PMIC_I2C=y
+CONFIG_MXC_PMIC_SPI=y
+# CONFIG_MXC_PMIC_MC34704 is not set
+# CONFIG_MXC_PMIC_CHARDEV is not set
+
+#
+# MXC PMIC Client Drivers
+#
+CONFIG_MXC_MC13892_ADC=y
+CONFIG_MXC_MC13892_RTC=y
+CONFIG_MXC_MC13892_LIGHT=y
+CONFIG_MXC_MC13892_BATTERY=y
+CONFIG_MXC_MC13892_CONNECTIVITY=y
+CONFIG_MXC_MC13892_POWER=y
+# CONFIG_MXC_PMIC_MC9SDZ60 is not set
+
+#
+# Advanced Power Management devices
+#
+
+#
+# MXC Security Drivers
+#
+# CONFIG_MXC_SECURITY_SCC is not set
+CONFIG_MXC_SECURITY_SCC2=y
+CONFIG_SCC_DEBUG=y
+# CONFIG_MXC_SECURITY_RNG is not set
+
+#
+# SAHARA2 Security Hardware Support
+#
+CONFIG_MXC_SAHARA=y
+CONFIG_MXC_SAHARA_USER_MODE=y
+# CONFIG_MXC_SAHARA_POLL_MODE is not set
+
+#
+# MXC MPEG4 Encoder Kernel module support
+#
+# CONFIG_MXC_HMP4E is not set
+
+#
+# MXC HARDWARE EVENT
+#
+# CONFIG_MXC_HWEVENT is not set
+
+#
+# MXC VPU(Video Processing Unit) support
+#
+CONFIG_MXC_VPU=y
+CONFIG_MXC_VPU_IRAM=y
+# CONFIG_MXC_VPU_DEBUG is not set
+
+#
+# MXC Asynchronous Sample Rate Converter support
+#
+
+#
+# MXC Bluetooth support
+#
+CONFIG_MXC_BLUETOOTH=m
+
+#
+# Broadcom GPS ioctrl support
+#
+
+#
+# MXC Media Local Bus Driver
+#
+
+#
+# i.MX ADC support
+#
+# CONFIG_IMX_ADC 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 is not set
+# CONFIG_EXT4DEV_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_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+# CONFIG_FUSE_FS 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_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# 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=y
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_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_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_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_BIND34 is not set
+# 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 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=m
+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=m
+# 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 is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# 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 is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# 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 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# 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 is not set
+# 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 is not set
+# 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_LZO is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+CONFIG_CRC_CCITT=m
+# CONFIG_CRC16 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_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/imx51_defconfig b/arch/arm/configs/imx51_defconfig
new file mode 100644
index 000000000000..afdd73553ed6
--- /dev/null
+++ b/arch/arm/configs/imx51_defconfig
@@ -0,0 +1,1954 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.28
+# Thu Sep 17 16:39:00 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_NO_IOPORT is not set
+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_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_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"
+
+#
+# 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=y
+CONFIG_SWAP=y
+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
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED 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 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 is not set
+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
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=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_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=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# 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_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_CLASSIC_RCU=y
+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_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE 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
+# 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_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=y
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_STMP3XXX is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Freescale MXC Implementations
+#
+# CONFIG_ARCH_MX37 is not set
+# CONFIG_ARCH_MX35 is not set
+CONFIG_ARCH_MX51=y
+# CONFIG_ARCH_MX3 is not set
+# CONFIG_ARCH_MX27 is not set
+# CONFIG_ARCH_MX25 is not set
+CONFIG_I2C_MXC_SELECT1=y
+CONFIG_I2C_MXC_SELECT2=y
+CONFIG_MXC_SDMA_API=y
+CONFIG_SDMA_IRAM=y
+CONFIG_SDMA_IRAM_SIZE=0x1000
+# CONFIG_I2C_MXC_SELECT3 is not set
+CONFIG_FORCE_MAX_ZONEORDER=13
+CONFIG_ARCH_MXC_HAS_NFC_V3=y
+
+#
+# MX51 Options
+#
+CONFIG_MX51_OPTIONS=y
+CONFIG_MACH_MX51_3DS=y
+CONFIG_MACH_MX51_BABBAGE=y
+CONFIG_ARCH_MXC_HAS_NFC_V3_2=y
+
+#
+# SDMA options
+#
+
+#
+# Device options
+#
+CONFIG_MXC_TZIC=y
+CONFIG_DMA_ZONE_SIZE=64
+CONFIG_UTMI_MXC=y
+# CONFIG_MXC_PWM is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+# CONFIG_CPU_ARM926T is not set
+# CONFIG_CPU_V6 is not set
+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_OUTER_CACHE 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_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+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_FLATMEM_HAS_HOLES=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL 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_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_UNEVICTABLE_LRU=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="noinitrd console=ttymxc0,115200 root=/dev/mtdblock2 rw rootfstype=jffs2 ip=off"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_IMX=y
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_NEON=y
+
+#
+# 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=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+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 is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE 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=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_NET_SCHED 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=y
+CONFIG_BT_L2CAP=y
+CONFIG_BT_SCO=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIBTUSB=y
+# CONFIG_BT_HCIBTSDIO is not set
+# CONFIG_BT_HCIUART is not set
+# CONFIG_BT_HCIBCM203X is not set
+# CONFIG_BT_HCIBPA10X is not set
+# CONFIG_BT_HCIBFUSB is not set
+CONFIG_BT_HCIVHCI=y
+# CONFIG_AF_RXRPC is not set
+# CONFIG_PHONET is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_OLD_REGULATORY=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=y
+# CONFIG_IEEE80211_DEBUG is not set
+# CONFIG_IEEE80211_CRYPT_WEP is not set
+# CONFIG_IEEE80211_CRYPT_CCMP is not set
+# CONFIG_IEEE80211_CRYPT_TKIP 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=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# 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 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
+# CONFIG_MTD_MXC is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# 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_NANDSIM is not set
+# CONFIG_MTD_NAND_IMX_NFC is not set
+CONFIG_MTD_NAND_MXC_V3=y
+# CONFIG_MTD_NAND_MXC_SWECC is not set
+# CONFIG_MTD_NAND_MXC_FORCE_CE is not set
+# CONFIG_MXC_NAND_LOW_LEVEL_ERASE is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND 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 is not set
+# CONFIG_BLK_DEV_UB 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=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT 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
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+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=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+CONFIG_ATA=m
+# CONFIG_ATA_NONSTANDARD is not set
+# CONFIG_SATA_PMP is not set
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_MV is not set
+# CONFIG_PATA_PLATFORM is not set
+CONFIG_PATA_FSL=m
+# 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 is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_SMC911X is not set
+CONFIG_SMSC911X=y
+# 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_CS89x0 is not set
+CONFIG_FEC=y
+# CONFIG_FEC2 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
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# 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 is not set
+# CONFIG_SLIP is not set
+# 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=y
+
+#
+# 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=y
+# CONFIG_INPUT_EVBUG is not set
+# CONFIG_INPUT_APMPOWER 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_MXC=y
+# CONFIG_KEYBOARD_GPIO 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_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+CONFIG_TOUCHSCREEN_MXC=y
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_CM109 is not set
+CONFIG_INPUT_UINPUT=y
+
+#
+# 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
+CONFIG_FM_SI4702=m
+CONFIG_MXC_IIM=y
+CONFIG_IMX_SIM=m
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_MXC=y
+CONFIG_SERIAL_MXC_CONSOLE=y
+# CONFIG_SERIAL_IMX is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_NVRAM 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_MXC=y
+CONFIG_I2C_MXC_HS=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_AT24 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_SENSORS_MAX6875 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_I2C_SLAVE is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_MXC=y
+# CONFIG_SPI_MXC_TEST_LOOPBACK is not set
+CONFIG_SPI_MXC_SELECT1=y
+# CONFIG_SPI_MXC_SELECT2 is not set
+# CONFIG_SPI_MXC_SELECT3 is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+# 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_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+CONFIG_W1=m
+CONFIG_W1_CON=y
+
+#
+# 1-wire Bus Masters
+#
+# CONFIG_W1_MASTER_DS2490 is not set
+# CONFIG_W1_MASTER_DS2482 is not set
+CONFIG_W1_MASTER_MXC=m
+# CONFIG_W1_MASTER_GPIO is not set
+
+#
+# 1-wire Slaves
+#
+# CONFIG_W1_SLAVE_THERM is not set
+# CONFIG_W1_SLAVE_SMEM is not set
+# CONFIG_W1_SLAVE_DS2751 is not set
+# CONFIG_W1_SLAVE_DS2433 is not set
+CONFIG_W1_SLAVE_DS2438=m
+# CONFIG_W1_SLAVE_DS2760 is not set
+# CONFIG_W1_SLAVE_BQ27000 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+CONFIG_APM_POWER=y
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_BATTERY_BQ27x00 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_ADCXX 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_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_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 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_MAX1111 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_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_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_SENSORS_ISL29003=y
+# 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_MXC_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_MFD_TMIO 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
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_V4L2_COMMON=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=y
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=y
+CONFIG_MEDIA_TUNER_CUSTOMIZE=y
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set
+
+#
+# Encoders/decoders and other helper chips
+#
+
+#
+# Audio decoders
+#
+# CONFIG_VIDEO_TVAUDIO is not set
+# CONFIG_VIDEO_TDA7432 is not set
+# CONFIG_VIDEO_TDA9840 is not set
+# CONFIG_VIDEO_TDA9875 is not set
+# CONFIG_VIDEO_TEA6415C is not set
+# CONFIG_VIDEO_TEA6420 is not set
+# CONFIG_VIDEO_MSP3400 is not set
+# CONFIG_VIDEO_CS5345 is not set
+# CONFIG_VIDEO_CS53L32A is not set
+# CONFIG_VIDEO_M52790 is not set
+# CONFIG_VIDEO_TLV320AIC23B is not set
+# CONFIG_VIDEO_WM8775 is not set
+# CONFIG_VIDEO_WM8739 is not set
+# CONFIG_VIDEO_VP27SMPX is not set
+
+#
+# Video decoders
+#
+# CONFIG_VIDEO_BT819 is not set
+# CONFIG_VIDEO_BT856 is not set
+# CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_KS0127 is not set
+# CONFIG_VIDEO_OV7670 is not set
+# CONFIG_VIDEO_TCM825X is not set
+# CONFIG_VIDEO_SAA7110 is not set
+# CONFIG_VIDEO_SAA7111 is not set
+# CONFIG_VIDEO_SAA7114 is not set
+# CONFIG_VIDEO_SAA711X is not set
+# CONFIG_VIDEO_SAA717X is not set
+# CONFIG_VIDEO_SAA7191 is not set
+# CONFIG_VIDEO_TVP5150 is not set
+# CONFIG_VIDEO_VPX3220 is not set
+
+#
+# Video and audio decoders
+#
+# CONFIG_VIDEO_CX25840 is not set
+
+#
+# MPEG video encoders
+#
+# CONFIG_VIDEO_CX2341X is not set
+
+#
+# Video encoders
+#
+# CONFIG_VIDEO_SAA7127 is not set
+# CONFIG_VIDEO_SAA7185 is not set
+# CONFIG_VIDEO_ADV7170 is not set
+# CONFIG_VIDEO_ADV7175 is not set
+
+#
+# Video improvement chips
+#
+# CONFIG_VIDEO_UPD64031A is not set
+# CONFIG_VIDEO_UPD64083 is not set
+# CONFIG_VIDEO_VIVI is not set
+CONFIG_VIDEO_MXC_CAMERA=m
+
+#
+# MXC Camera/V4L2 PRP Features support
+#
+CONFIG_VIDEO_MXC_IPU_CAMERA=y
+# CONFIG_VIDEO_MXC_CSI_CAMERA is not set
+# CONFIG_MXC_CAMERA_MC521DA is not set
+# CONFIG_MXC_EMMA_CAMERA_MICRON111 is not set
+# CONFIG_MXC_CAMERA_OV2640_EMMA is not set
+# CONFIG_MXC_CAMERA_MICRON111 is not set
+# CONFIG_MXC_CAMERA_OV2640 is not set
+CONFIG_MXC_CAMERA_OV3640=m
+# CONFIG_MXC_TVIN_ADV7180 is not set
+CONFIG_MXC_IPU_PRP_VF_SDC=m
+CONFIG_MXC_IPU_PRP_ENC=m
+CONFIG_MXC_IPU_CSI_ENC=m
+CONFIG_VIDEO_MXC_OUTPUT=y
+CONFIG_VIDEO_MXC_IPU_OUTPUT=y
+# CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT is not set
+# CONFIG_VIDEO_MXC_OPL is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_SOC_CAMERA is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+CONFIG_USB_GSPCA=m
+# CONFIG_USB_M5602 is not set
+# CONFIG_USB_GSPCA_CONEX is not set
+# CONFIG_USB_GSPCA_ETOMS is not set
+# CONFIG_USB_GSPCA_FINEPIX is not set
+# CONFIG_USB_GSPCA_MARS is not set
+# CONFIG_USB_GSPCA_OV519 is not set
+# CONFIG_USB_GSPCA_PAC207 is not set
+# CONFIG_USB_GSPCA_PAC7311 is not set
+# CONFIG_USB_GSPCA_SONIXB is not set
+# CONFIG_USB_GSPCA_SONIXJ is not set
+# CONFIG_USB_GSPCA_SPCA500 is not set
+# CONFIG_USB_GSPCA_SPCA501 is not set
+# CONFIG_USB_GSPCA_SPCA505 is not set
+# CONFIG_USB_GSPCA_SPCA506 is not set
+# CONFIG_USB_GSPCA_SPCA508 is not set
+# CONFIG_USB_GSPCA_SPCA561 is not set
+# CONFIG_USB_GSPCA_STK014 is not set
+# CONFIG_USB_GSPCA_SUNPLUS is not set
+# CONFIG_USB_GSPCA_T613 is not set
+# CONFIG_USB_GSPCA_TV8532 is not set
+# CONFIG_USB_GSPCA_VC032X is not set
+# CONFIG_USB_GSPCA_ZC3XX is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_OV511 is not set
+# CONFIG_USB_SE401 is not set
+# CONFIG_USB_SN9C102 is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_USB_S2255 is not set
+# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+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
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+CONFIG_FB_MXC=y
+CONFIG_FB_MXC_SYNC_PANEL=y
+CONFIG_FB_MXC_EPSON_VGA_SYNC_PANEL=y
+CONFIG_FB_MXC_TVOUT_TVE=y
+# CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL is not set
+CONFIG_FB_MXC_CH7026=y
+# CONFIG_FB_MXC_TVOUT is not set
+# CONFIG_FB_MXC_TVOUT_CH7024 is not set
+# CONFIG_FB_MXC_ASYNC_PANEL is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_MXC=y
+CONFIG_BACKLIGHT_MXC_MC13892=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_SOUND=y
+CONFIG_SOUND_OSS_CORE=y
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+CONFIG_SND_ARM=y
+CONFIG_SND_MXC_SPDIF=m
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_MXC_SOC=y
+CONFIG_SND_MXC_SOC_SSI=y
+CONFIG_SND_MXC_SOC_IRAM=y
+CONFIG_SND_SOC_IMX_3STACK_SGTL5000=y
+# CONFIG_SND_SOC_IMX_3STACK_AK4647 is not set
+# CONFIG_SND_SOC_IMX_3STACK_WM8580 is not set
+# CONFIG_SND_SOC_IMX_3STACK_AK5702 is not set
+# CONFIG_SND_SOC_IMX_3STACK_BLUETOOTH is not set
+# CONFIG_SND_SOC_ALL_CODECS is not set
+CONFIG_SND_SOC_SGTL5000=y
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# Special HID drivers
+#
+CONFIG_HID_COMPAT=y
+CONFIG_HID_A4TECH=m
+CONFIG_HID_APPLE=m
+CONFIG_HID_BELKIN=m
+CONFIG_HID_BRIGHT=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_CHICONY=m
+CONFIG_HID_CYPRESS=m
+CONFIG_HID_DELL=m
+CONFIG_HID_EZKEY=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_LOGITECH=m
+# CONFIG_LOGITECH_FF is not set
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MONTEREY=m
+CONFIG_HID_PANTHERLORD=m
+# CONFIG_PANTHERLORD_FF is not set
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_HID_SUNPLUS=m
+# CONFIG_THRUSTMASTER_FF is not set
+# CONFIG_ZEROPLUS_FF 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=y
+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=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB 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_EHCI_HCD=y
+CONFIG_USB_EHCI_ARC=y
+CONFIG_USB_EHCI_ARC_H1=y
+# CONFIG_USB_EHCI_ARC_H2 is not set
+# CONFIG_USB_EHCI_ARC_OTG is not set
+# CONFIG_USB_STATIC_IRAM is not set
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD 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 information
+#
+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_DPCM 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_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA 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 is not set
+# 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_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_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+CONFIG_USB_GADGET_ARC=y
+CONFIG_USB_ARC=y
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+CONFIG_USB_GADGET_ARC_OTG=y
+# CONFIG_USB_GADGET_FSL_MC13783 is not set
+# CONFIG_USB_GADGET_FSL_1301 is not set
+# CONFIG_USB_GADGET_FSL_1504 is not set
+CONFIG_USB_GADGET_FSL_UTMI=y
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# 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
+CONFIG_SDIO_UNIFI_FS=m
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_MXC is not set
+CONFIG_MMC_IMX_ESDHCI=y
+# CONFIG_MMC_IMX_ESDHCI_PIO_MODE is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_MC13892=y
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_LEDS_PCA955X is not set
+
+#
+# LED Triggers
+#
+# CONFIG_LEDS_TRIGGERS 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=y
+# 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
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+
+#
+# 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_MXC is not set
+# CONFIG_RTC_DRV_MXC_V2 is not set
+# CONFIG_RTC_DRV_IMXDI is not set
+CONFIG_RTC_MC13892=y
+# CONFIG_DMADEVICES is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+CONFIG_REGULATOR_MC13892=y
+CONFIG_UIO=y
+# CONFIG_UIO_PDRV is not set
+CONFIG_UIO_PDRV_GENIRQ=m
+# CONFIG_UIO_SMX is not set
+# CONFIG_UIO_SERCOS3 is not set
+
+#
+# MXC support drivers
+#
+CONFIG_MXC_IPU=y
+CONFIG_MXC_IPU_V3=y
+
+#
+# MXC SSI support
+#
+# CONFIG_MXC_SSI is not set
+
+#
+# MXC Digital Audio Multiplexer support
+#
+# CONFIG_MXC_DAM is not set
+
+#
+# MXC PMIC support
+#
+CONFIG_MXC_PMIC=y
+# CONFIG_MXC_PMIC_MC13783 is not set
+CONFIG_MXC_PMIC_MC13892=y
+CONFIG_MXC_PMIC_I2C=y
+CONFIG_MXC_PMIC_SPI=y
+# CONFIG_MXC_PMIC_MC34704 is not set
+# CONFIG_MXC_PMIC_MC9SDZ60 is not set
+# CONFIG_MXC_PMIC_CHARDEV is not set
+
+#
+# MXC PMIC Client Drivers
+#
+CONFIG_MXC_MC13892_ADC=y
+CONFIG_MXC_MC13892_RTC=y
+CONFIG_MXC_MC13892_LIGHT=y
+CONFIG_MXC_MC13892_BATTERY=y
+CONFIG_MXC_MC13892_CONNECTIVITY=y
+CONFIG_MXC_MC13892_POWER=y
+# CONFIG_MXC_PMIC_MC9S08DZ60 is not set
+
+#
+# MXC Security Drivers
+#
+# CONFIG_MXC_SECURITY_SCC is not set
+CONFIG_MXC_SECURITY_SCC2=y
+CONFIG_SCC_DEBUG=y
+# CONFIG_MXC_SECURITY_RNG is not set
+
+#
+# SAHARA2 Security Hardware Support
+#
+# CONFIG_MXC_SAHARA is not set
+
+#
+# MXC MPEG4 Encoder Kernel module support
+#
+# CONFIG_MXC_HMP4E is not set
+
+#
+# MXC HARDWARE EVENT
+#
+# CONFIG_MXC_HWEVENT is not set
+
+#
+# MXC VPU(Video Processing Unit) support
+#
+CONFIG_MXC_VPU=y
+CONFIG_MXC_VPU_IRAM=y
+# CONFIG_MXC_VPU_DEBUG is not set
+
+#
+# MXC Asynchronous Sample Rate Converter support
+#
+
+#
+# MXC Bluetooth support
+#
+CONFIG_MXC_BLUETOOTH=m
+
+#
+# Broadcom GPS ioctrl support
+#
+CONFIG_GPS_IOCTRL=m
+
+#
+# MXC Media Local Bus Driver
+#
+
+#
+# i.MX ADC support
+#
+# CONFIG_IMX_ADC 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_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 is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+# CONFIG_FUSE_FS 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
+
+#
+# Miscellaneous filesystems
+#
+# 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=y
+# 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_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_SUNRPC_REGISTER_V4 is not set
+# 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 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=m
+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=m
+# 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 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=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+
+#
+# Tracers
+#
+# CONFIG_DYNAMIC_PRINTK_DEBUG 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=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
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_CRYPTODEV 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 is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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 is not set
+# 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_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=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_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/configs/ns9xxx_defconfig b/arch/arm/configs/ns9xxx_defconfig
index 7dc1580e4d99..12a2a5939c0d 100644
--- a/arch/arm/configs/ns9xxx_defconfig
+++ b/arch/arm/configs/ns9xxx_defconfig
@@ -14,7 +14,9 @@ CONFIG_MACH_CC9C=y
CONFIG_MACH_CC9P9210=y
CONFIG_MACH_CC9P9210JS=y
CONFIG_MACH_CC9P9215=y
+CONFIG_MACH_CCW9P9215=y
CONFIG_MACH_CC9P9215JS=y
+CONFIG_MACH_CCW9P9215JS=y
CONFIG_MACH_CC9P9360DEV=y
CONFIG_MACH_CC9P9360JS=y
CONFIG_MACH_CC9P9360VAL=y
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index de6c59f814a1..d79121248664 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -206,6 +206,7 @@ struct outer_cache_fns {
void (*inv_range)(unsigned long, unsigned long);
void (*clean_range)(unsigned long, unsigned long);
void (*flush_range)(unsigned long, unsigned long);
+ void (*flush_all)(void);
};
/*
@@ -283,6 +284,11 @@ static inline void outer_flush_range(unsigned long start, unsigned long end)
if (outer_cache.flush_range)
outer_cache.flush_range(start, end);
}
+static inline void outer_flush_all(void)
+{
+ if (outer_cache.flush_all)
+ outer_cache.flush_all();
+}
#else
@@ -292,6 +298,8 @@ static inline void outer_clean_range(unsigned long start, unsigned long end)
{ }
static inline void outer_flush_range(unsigned long start, unsigned long end)
{ }
+static inline void outer_flush_all(void)
+{ }
#endif
diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h
index 64f2252a25cd..0024e4d49119 100644
--- a/arch/arm/include/asm/hardware/cache-l2x0.h
+++ b/arch/arm/include/asm/hardware/cache-l2x0.h
@@ -51,6 +51,8 @@
#ifndef __ASSEMBLY__
extern void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask);
+extern void l2x0_enable(void);
+extern void l2x0_disable(void);
#endif
#endif
diff --git a/arch/arm/include/asm/ide.h b/arch/arm/include/asm/ide.h
index b507ce8e5019..c1d3171bcc7a 100644
--- a/arch/arm/include/asm/ide.h
+++ b/arch/arm/include/asm/ide.h
@@ -18,6 +18,11 @@
#define __ide_mm_outsw(port,addr,len) writesw(port,addr,len)
#define __ide_mm_outsl(port,addr,len) writesl(port,addr,len)
+#ifdef CONFIG_CPU_S3C2443
+# define IDE_ARCH_ACK_INTR
+# define ide_ack_intr(hwif) ((hwif)->ack_intr ? (hwif)->ack_intr(hwif) : 1)
+#endif /* CONFIG_CPU_S3C2443 */
+
#endif /* __KERNEL__ */
#endif /* __ASMARM_IDE_H */
diff --git a/arch/arm/include/asm/mach/keypad.h b/arch/arm/include/asm/mach/keypad.h
new file mode 100644
index 000000000000..cfee65ab044a
--- /dev/null
+++ b/arch/arm/include/asm/mach/keypad.h
@@ -0,0 +1,28 @@
+/*
+ * include/asm-arm/mach/keypad.h
+ *
+ * Generic Keypad struct
+ *
+ * Author: Armin Kuster <Akuster@mvista.com>
+ *
+ * 2005 (c) MontaVista Software, Inc. 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.
+ */
+
+#ifndef __ASM_MACH_KEYPAD_H_
+#define __ASM_MACH_KEYPAD_H_
+
+#include <linux/input.h>
+
+struct keypad_data {
+ u16 rowmax;
+ u16 colmax;
+ u32 irq;
+ u16 delay;
+ u16 learning;
+ u16 *matrix;
+};
+
+#endif /* __ARM_MACH_KEYPAD_H_ */
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index 110295c5461d..d657777e75d7 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -186,6 +186,8 @@ extern void __pgd_error(const char *file, int line, unsigned long val);
#define L_PTE_MT_DEV_NONSHARED (0x0c << 2) /* 1100 */
#define L_PTE_MT_DEV_WC (0x09 << 2) /* 1001 */
#define L_PTE_MT_DEV_CACHED (0x0b << 2) /* 1011 */
+#define L_PTE_MT_OUTER_UNCACHED (0x0d << 2) /* 1101 */
+#define L_PTE_MT_OUTER_WRITETHRU (0x0e << 2) /* 1110 */
#define L_PTE_MT_MASK (0x0f << 2)
#ifndef __ASSEMBLY__
@@ -313,6 +315,19 @@ static inline pte_t pte_mkspecial(pte_t pte) { return pte; }
__pgprot((pgprot_val(prot) & ~L_PTE_MT_MASK) | L_PTE_MT_UNCACHED)
#define pgprot_writecombine(prot) \
__pgprot((pgprot_val(prot) & ~L_PTE_MT_MASK) | L_PTE_MT_BUFFERABLE)
+#define pgprot_writethru(prot) \
+ __pgprot((pgprot_val(prot) & ~L_PTE_MT_MASK) | L_PTE_MT_WRITETHROUGH)
+#define pgprot_nonshareddev(prot) \
+ __pgprot((pgprot_val(prot) & ~L_PTE_MT_MASK) | L_PTE_MT_DEV_NONSHARED)
+
+/* Extended configurations for inner writeback cacheable */
+#define pgprot_writealloc(prot) \
+ __pgprot((pgprot_val(prot) & ~L_PTE_MT_MASK) | L_PTE_MT_WRITEALLOC)
+#define pgprot_outer_wrthru(prot) \
+ __pgprot((pgprot_val(prot) & ~L_PTE_MT_MASK) | L_PTE_MT_OUTER_WRITETHRU)
+#define pgprot_outer_noncached(prot) \
+ __pgprot((pgprot_val(prot) & ~L_PTE_MT_MASK) | L_PTE_MT_OUTER_UNCACHED)
+
#define pmd_none(pmd) (!pmd_val(pmd))
#define pmd_present(pmd) (pmd_val(pmd))
diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
index 010618487cf1..8468039d2d4d 100644
--- a/arch/arm/include/asm/unistd.h
+++ b/arch/arm/include/asm/unistd.h
@@ -452,5 +452,26 @@
*/
#define __IGNORE_fadvise64_64 1
+/*
+ * > #warning syscall fadvise64 not implemented
+ * ... and probably never will be.
+ * see http://www.mail-archive.com/linux-arch@vger.kernel.org/msg02432.html
+ */
+#define __IGNORE_fadvise64 1
+
+/*
+ * Does it make sense for ARM? I have no idea.
+ * see http://www.mail-archive.com/linux-arch@vger.kernel.org/msg02432.html
+ */
+#define __IGNORE_migrate_pages 1
+
+/*
+ * Maybe sometime in the future
+ * see http://www.mail-archive.com/linux-arch@vger.kernel.org/msg02432.html
+ */
+#define __IGNORE_pselect6 1
+#define __IGNORE_ppoll 1
+#define __IGNORE_epoll_pwait 1
+
#endif /* __KERNEL__ */
#endif /* __ASM_ARM_UNISTD_H */
diff --git a/arch/arm/mach-mx2/devices.c b/arch/arm/mach-mx2/devices.c
index bd0559d5933e..96e0e1eaa751 100644
--- a/arch/arm/mach-mx2/devices.c
+++ b/arch/arm/mach-mx2/devices.c
@@ -9,7 +9,7 @@
* licensed "as is" without any warranty of any kind, whether express
* or implied.
*
- * Copyright 2006-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2006-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or
@@ -195,6 +195,7 @@ static struct mxc_gpio_port imx_gpio_ports[] = {
[0] = {
.chip.label = "gpio-0",
.irq = MXC_INT_GPIO,
+ .irq_high = 0,
.base = (void*)(AIPI_BASE_ADDR_VIRT + 0x15000 + 0x100 * 0),
.virtual_irq_start = MXC_MAX_INT_LINES,
},
diff --git a/arch/arm/mach-mx25/Kconfig b/arch/arm/mach-mx25/Kconfig
new file mode 100644
index 000000000000..3f404eeb326f
--- /dev/null
+++ b/arch/arm/mach-mx25/Kconfig
@@ -0,0 +1,96 @@
+menu "MX25 Options"
+ depends on ARCH_MX25
+
+config MX25_OPTIONS
+ bool
+ default y
+ select CPU_ARM926T
+ select USB_ARCH_HAS_EHCI
+
+config MACH_MX25_3DS
+ bool "Support MX25 3STACK platforms"
+ default y
+ help
+ Include support for MX25 3STACK platform. This includes specific
+ configurations for the board and its peripherals.
+
+config MX25_DOZE_DURING_IDLE
+ bool "Enter Doze mode during idle"
+ help
+ Turning on this option will put the CPU into Doze mode during idle.
+ The default is to enter Wait mode during idle. Doze mode during
+ idle will save additional power over Wait mode.
+
+config MXC_SDMA_API
+ bool "Use SDMA API"
+ default y
+ help
+ This selects the Freescale MXC SDMA API.
+ If unsure, say N.
+
+menu "SDMA options"
+ depends on MXC_SDMA_API
+
+config SDMA_IRAM
+ bool "Use Internal RAM for SDMA transfer"
+ default n
+ help
+ Support Internal RAM as SDMA buffer or control structures
+
+config SDMA_IRAM_SIZE
+ hex "Reserved bytes of IRAM for SDMA (0x800-0x1000)"
+ range 0x800 0x1000
+ depends on SDMA_IRAM
+ default "0x1000"
+ help
+ Set the size of IRAM for SDMA. It must be a multiple of 512bytes.
+endmenu
+
+config ARCH_MXC_HAS_NFC_V2
+ bool "MXC NFC Hardware Version 2"
+ depends on ARCH_MX25
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 2
+ If unsure, say N.
+
+config ARCH_MXC_HAS_NFC_V2_1
+ bool "MXC NFC Hardware Version 2.1"
+ depends on ARCH_MXC_HAS_NFC_V2
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 2.1
+ If unsure, say N.
+
+menu "Device options"
+
+config I2C_MXC_SELECT1
+ bool "Enable I2C1 module"
+ default y
+ depends on I2C_MXC
+ help
+ Enable MX25 I2C1 module.
+
+config I2C_MXC_SELECT2
+ bool "Enable I2C2 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX25 I2C2 module.
+
+config I2C_MXC_SELECT3
+ bool "Enable I2C3 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX25 I2C3 module.
+
+config FLEXCAN_MXC_SELECT1
+ bool "Enable FlexCAN1 module"
+ depends on CAN_FLEXCAN
+ help
+ Enable MX25 FlexCAN1 module.
+
+endmenu
+
+endmenu
diff --git a/arch/arm/mach-mx25/Makefile b/arch/arm/mach-mx25/Makefile
new file mode 100644
index 000000000000..efca65083819
--- /dev/null
+++ b/arch/arm/mach-mx25/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-y := system.o iomux.o cpu.o mm.o clock.o bus_freq.o devices.o serial.o
+obj-$(CONFIG_MXC_SDMA_API) += dma.o
+obj-$(CONFIG_SPI_MXC) += mx25_3stack_cpld.o
+obj-$(CONFIG_MACH_MX25_3DS) += mx25_3stack.o mx25_3stack_gpio.o mx25_3stack_pmic_mc34704.o
+
+obj-$(CONFIG_USB_EHCI_ARC_H2) += usb_h2.o
+
+obj-$(CONFIG_PM) += pm.o
+
+ifneq ($(strip $(CONFIG_USB_GADGET_ARC) $(CONFIG_USB_EHCI_ARC_OTG)),)
+ obj-y += usb_dr.o
+endif
diff --git a/arch/arm/mach-mx25/Makefile.boot b/arch/arm/mach-mx25/Makefile.boot
new file mode 100644
index 000000000000..e1dd366f836b
--- /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/board-mx25_3stack.h b/arch/arm/mach-mx25/board-mx25_3stack.h
new file mode 100644
index 000000000000..70c21b849499
--- /dev/null
+++ b/arch/arm/mach-mx25/board-mx25_3stack.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_BOARD_MX25_3STACK_H__
+#define __ASM_ARCH_MXC_BOARD_MX25_3STACK_H__
+
+#ifdef CONFIG_MACH_MX25_3DS
+
+/*!
+ * @defgroup BRDCFG_MX25 Board Configuration Options
+ * @ingroup MSL_MX25
+ */
+
+/*!
+ * @file mach-mx25/board-mx25_3stack.h
+ *
+ * @brief This file contains all the board level configuration options.
+ *
+ * It currently hold the options defined for MX25 3STACK Platform.
+ *
+ * @ingroup BRDCFG_MX25
+ */
+
+/*
+ * Include Files
+ */
+#include <mach/mxc_uart.h>
+
+/*!
+ * @name MXC UART board-level configurations
+ */
+/*! @{ */
+/* UART 1 configuration */
+/*!
+ * This define specifies if the UART port is configured to be in DTE or
+ * DCE mode. There exists a define like this for each UART port. Valid
+ * values that can be used are \b MODE_DTE or \b MODE_DCE.
+ */
+#define UART1_MODE MODE_DCE
+/*!
+ * This define specifies if the UART is to be used for IRDA. There exists a
+ * define like this for each UART port. Valid values that can be used are
+ * \b IRDA or \b NO_IRDA.
+ */
+#define UART1_IR NO_IRDA
+/*!
+ * This define is used to enable or disable a particular UART port. If
+ * disabled, the UART will not be registered in the file system and the user
+ * will not be able to access it. There exists a define like this for each UART
+ * port. Specify a value of 1 to enable the UART and 0 to disable it.
+ */
+#define UART1_ENABLED 1
+/*! @} */
+/* UART 2 configuration */
+#define UART2_MODE MODE_DCE
+#define UART2_IR NO_IRDA
+#define UART2_ENABLED 1
+
+/* UART 3 configuration */
+#define UART3_MODE MODE_DTE
+#define UART3_IR NO_IRDA
+#define UART3_ENABLED 0
+
+/* UART 4 configuration */
+#define UART4_MODE MODE_DTE
+#define UART4_IR NO_IRDA
+#define UART4_ENABLED 0
+
+/* UART 5 configuration */
+#define UART5_MODE MODE_DTE
+#define UART5_IR NO_IRDA
+#define UART5_ENABLED 0
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
+
+/*!
+ * @name debug board parameters
+ */
+/*! @{ */
+/*!
+ * Base address of debug board
+ */
+#define DEBUG_BASE_ADDRESS 0x78000000 /* Use a dummy base address */
+
+/* External ethernet LAN9217 base address */
+#define LAN9217_BASE_ADDR DEBUG_BASE_ADDRESS
+
+/* External UART */
+#define UARTA_BASE_ADDR (DEBUG_BASE_ADDRESS + 0x08000)
+#define UARTB_BASE_ADDR (DEBUG_BASE_ADDRESS + 0x10000)
+
+#define BOARD_IO_ADDR 0x20000
+
+/* LED switchs */
+#define LED_SWITCH_REG (BOARD_IO_ADDR + 0x00)
+/* buttons */
+#define SWITCH_BUTTON_REG (BOARD_IO_ADDR + 0x08)
+/* status, interrupt */
+#define INTR_STATUS_REG (BOARD_IO_ADDR + 0x10)
+#define INTR_RESET_REG (BOARD_IO_ADDR + 0x20)
+/*CPLD configuration*/
+#define CONFIG1_REG (BOARD_IO_ADDR + 0x28)
+#define CONFIG2_REG (BOARD_IO_ADDR + 0x30)
+/*interrupt mask */
+#define INTR_MASK_REG (BOARD_IO_ADDR + 0x38)
+
+/* magic word for debug CPLD */
+#define MAGIC_NUMBER1_REG (BOARD_IO_ADDR + 0x40)
+#define MAGIC_NUMBER2_REG (BOARD_IO_ADDR + 0x48)
+/* CPLD code version */
+#define CPLD_CODE_VER_REG (BOARD_IO_ADDR + 0x50)
+/* magic word for debug CPLD */
+#define MAGIC3_NUMBER3_REG (BOARD_IO_ADDR + 0x58)
+/* module reset register*/
+#define CONTROL_REG (BOARD_IO_ADDR + 0x60)
+/* CPU ID and Personality ID*/
+#define IDENT_REG (BOARD_IO_ADDR + 0x68)
+
+/* For interrupts like xuart, enet etc */
+#define EXPIO_PARENT_INT MX25_PIN_GPIO1_1
+
+#define EXPIO_INT_ENET_INT (MXC_EXP_IO_BASE + 0)
+#define EXPIO_INT_XUARTA_INT (MXC_EXP_IO_BASE + 1)
+#define EXPIO_INT_XUARTB_INT (MXC_EXP_IO_BASE + 2)
+
+/*! This is System IRQ used by LAN9217 for interrupt generation taken
+ * from platform.h
+ */
+#define LAN9217_IRQ EXPIO_INT_ENET_INT
+
+/*! This is base virtual address of debug board*/
+extern unsigned int mx25_3stack_board_io;
+
+#define MXC_BD_LED1 (1 << 0)
+#define MXC_BD_LED2 (1 << 1)
+#define MXC_BD_LED3 (1 << 2)
+#define MXC_BD_LED4 (1 << 3)
+#define MXC_BD_LED5 (1 << 4)
+#define MXC_BD_LED6 (1 << 5)
+#define MXC_BD_LED7 (1 << 6)
+#define MXC_BD_LED8 (1 << 7)
+#define MXC_BD_LED_ON(led)
+#define MXC_BD_LED_OFF(led)
+
+#define MXC_DEFAULT_INTENSITY 127
+#define MXC_INTENSITY_OFF 0
+
+/*! @} */
+
+extern void mx25_3stack_gpio_init(void) __init;
+extern int headphone_det_status(void);
+extern void sgtl5000_enable_amp(void);
+extern unsigned int sdhc_get_card_det_status(struct device *dev);
+extern int sdhc_write_protect(struct device *dev);
+extern void gpio_can_active(int id);
+extern void gpio_can_inactive(int id);
+extern struct flexcan_platform_data flexcan_data[];
+extern void mx2fb_set_brightness(uint8_t);
+extern int __init mx25_3stack_init_mc34704(void);
+
+#endif /* CONFIG_MACH_MX25_3DS */
+#endif /* __ASM_ARCH_MXC_BOARD_MX25_3STACK_H__ */
diff --git a/arch/arm/mach-mx25/bus_freq.c b/arch/arm/mach-mx25/bus_freq.c
new file mode 100644
index 000000000000..61bed5552a03
--- /dev/null
+++ b/arch/arm/mach-mx25/bus_freq.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file bus_freq.c
+ *
+ * @brief A common API for the Freescale Semiconductor i.MXC CPUfreq module
+ * and DVFS CORE module.
+ *
+ * The APIs are for setting bus frequency to low or high.
+ *
+ * @ingroup PM
+ */
+
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+
+int low_bus_freq_mode;
+int high_bus_freq_mode;
+char *gp_reg_id = "REG3_CORE";
+
+int set_low_bus_freq(void)
+{
+ return 0;
+}
+
+int set_high_bus_freq(int high_bus_freq)
+{
+ return 0;
+}
+
+int low_freq_bus_used(void)
+{
+ return 0;
+}
+
+/*!
+ * This is the probe routine for the bus frequency driver.
+ *
+ * @param pdev The platform device structure
+ *
+ * @return The function returns 0 on success
+ *
+ */
+static int __devinit busfreq_probe(struct platform_device *pdev)
+{
+ low_bus_freq_mode = 0;
+ high_bus_freq_mode = 0;
+
+ return 0;
+}
+
+static struct platform_driver busfreq_driver = {
+ .driver = {
+ .name = "busfreq",
+ },
+ .probe = busfreq_probe,
+};
+
+/*!
+ * Initialise the busfreq_driver.
+ *
+ * @return The function always returns 0.
+ */
+
+static int __init busfreq_init(void)
+{
+ if (platform_driver_register(&busfreq_driver) != 0) {
+ printk(KERN_ERR "busfreq_driver register failed\n");
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "Bus freq driver module loaded\n");
+ return 0;
+}
+
+static void __exit busfreq_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&busfreq_driver);
+}
+
+module_init(busfreq_init);
+module_exit(busfreq_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("BusFreq driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-mx25/clock.c b/arch/arm/mach-mx25/clock.c
new file mode 100644
index 000000000000..31ab725e1ba8
--- /dev/null
+++ b/arch/arm/mach-mx25/clock.c
@@ -0,0 +1,1737 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/* based on mach-mx27/clock.c */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/clock.h>
+#include "crm_regs.h"
+
+#define OSC24M_CLK_FREQ 24000000 /* 24M reference clk */
+#define OSC32K_CLK_FREQ 32768 /* 32.768k oscillator in */
+
+#if defined CONFIG_CPU_FREQ_IMX
+#define AHB_CLK_DEFAULT 133000000
+#define ARM_SRC_DEFAULT 532000000
+#endif
+
+static struct clk mpll_clk;
+static struct clk upll_clk;
+static struct clk ahb_clk;
+static struct clk upll_24610k_clk;
+int cpu_wp_nr;
+
+static int _clk_enable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg |= 1 << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+
+ return 0;
+}
+
+static void _clk_disable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(1 << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+}
+
+static int _clk_upll_enable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(MXC_CCM_CCTL);
+ reg &= ~MXC_CCM_CCTL_UPLL_DISABLE;
+ __raw_writel(reg, MXC_CCM_CCTL);
+
+ while ((__raw_readl(MXC_CCM_UPCTL) & MXC_CCM_UPCTL_LF) == 0) ;
+
+ return 0;
+}
+
+static void _clk_upll_disable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(MXC_CCM_CCTL);
+ reg |= MXC_CCM_CCTL_UPLL_DISABLE;
+ __raw_writel(reg, MXC_CCM_CCTL);
+}
+
+static int _perclk_enable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(MXC_CCM_CGCR0);
+ reg |= 1 << clk->id;
+ __raw_writel(reg, MXC_CCM_CGCR0);
+
+ return 0;
+}
+
+static void _perclk_disable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(MXC_CCM_CGCR0);
+ reg &= ~(1 << clk->id);
+ __raw_writel(reg, MXC_CCM_CGCR0);
+}
+
+static void _clk_pll_recalc(struct clk *clk)
+{
+ unsigned long mfi = 0, mfn = 0, mfd = 0, pdf = 0;
+ unsigned long ref_clk;
+ unsigned long reg;
+ unsigned long long temp;
+
+ ref_clk = clk->parent->rate;
+
+ if (clk == &mpll_clk) {
+ reg = __raw_readl(MXC_CCM_MPCTL);
+ pdf = (reg & MXC_CCM_MPCTL_PD_MASK) >> MXC_CCM_MPCTL_PD_OFFSET;
+ mfd =
+ (reg & MXC_CCM_MPCTL_MFD_MASK) >> MXC_CCM_MPCTL_MFD_OFFSET;
+ mfi =
+ (reg & MXC_CCM_MPCTL_MFI_MASK) >> MXC_CCM_MPCTL_MFI_OFFSET;
+ mfn =
+ (reg & MXC_CCM_MPCTL_MFN_MASK) >> MXC_CCM_MPCTL_MFN_OFFSET;
+ } else if (clk == &upll_clk) {
+ reg = __raw_readl(MXC_CCM_UPCTL);
+ pdf = (reg & MXC_CCM_UPCTL_PD_MASK) >> MXC_CCM_UPCTL_PD_OFFSET;
+ mfd =
+ (reg & MXC_CCM_UPCTL_MFD_MASK) >> MXC_CCM_UPCTL_MFD_OFFSET;
+ mfi =
+ (reg & MXC_CCM_UPCTL_MFI_MASK) >> MXC_CCM_UPCTL_MFI_OFFSET;
+ mfn =
+ (reg & MXC_CCM_UPCTL_MFN_MASK) >> MXC_CCM_UPCTL_MFN_OFFSET;
+ } else {
+ BUG(); /* oops */
+ }
+
+ mfi = (mfi <= 5) ? 5 : mfi;
+ temp = 2LL * ref_clk * mfn;
+ do_div(temp, mfd + 1);
+ temp = 2LL * ref_clk * mfi + temp;
+ do_div(temp, pdf + 1);
+
+ clk->rate = temp;
+}
+
+static unsigned long _clk_cpu_round_rate(struct clk *clk, unsigned long rate)
+{
+ int div = clk->parent->rate / rate;
+
+ if (clk->parent->rate % rate)
+ div++;
+
+ if (div > 4)
+ div = 4;
+
+ return clk->parent->rate / div;
+}
+
+static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long div = 0x0, reg = 0x0;
+ unsigned long cctl = __raw_readl(MXC_CCM_CCTL);
+
+#if defined CONFIG_CPU_FREQ_IMX
+ struct cpu_wp *cpu_wp;
+ unsigned long ahb_clk_div = 0;
+ unsigned long arm_src = 0;
+ int i;
+
+ cpu_wp = get_cpu_wp(&cpu_wp_nr);
+ for (i = 0; i < cpu_wp_nr; i++) {
+ if (cpu_wp[i].cpu_rate == rate) {
+ div = cpu_wp[i].cpu_podf;
+ ahb_clk_div = cpu_wp[i].cpu_rate / AHB_CLK_DEFAULT - 1;
+ arm_src =
+ (cpu_wp[i].pll_rate == ARM_SRC_DEFAULT) ? 0 : 1;
+ break;
+ }
+ }
+ if (i == cpu_wp_nr)
+ return -EINVAL;
+ reg = (cctl & ~MXC_CCM_CCTL_ARM_MASK) |
+ (div << MXC_CCM_CCTL_ARM_OFFSET);
+ reg = (reg & ~MXC_CCM_CCTL_AHB_MASK) |
+ (ahb_clk_div << MXC_CCM_CCTL_AHB_OFFSET);
+ reg = (reg & ~MXC_CCM_CCTL_ARM_SRC) |
+ (arm_src << MXC_CCM_CCTL_ARM_SRC_OFFSET);
+ __raw_writel(reg, MXC_CCM_CCTL);
+ clk->rate = rate;
+#else
+ div = clk->parent->rate / rate;
+
+ if (div > 4 || div < 1 || ((clk->parent->rate / div) != rate))
+ return -EINVAL;
+ div--;
+
+ reg =
+ (cctl & ~MXC_CCM_CCTL_ARM_MASK) | (div << MXC_CCM_CCTL_ARM_OFFSET);
+ __raw_writel(reg, MXC_CCM_CCTL);
+ clk->rate = rate;
+#endif
+
+ return 0;
+}
+
+static void _clk_cpu_recalc(struct clk *clk)
+{
+ unsigned long div;
+ unsigned long cctl = __raw_readl(MXC_CCM_CCTL);
+
+ div = (cctl & MXC_CCM_CCTL_ARM_MASK) >> MXC_CCM_CCTL_ARM_OFFSET;
+
+ clk->rate = clk->parent->rate / (div + 1);
+
+ if (cctl & MXC_CCM_CCTL_ARM_SRC) {
+ clk->rate *= 3;
+ clk->rate /= 4;
+ }
+}
+
+static void _clk_ahb_recalc(struct clk *clk)
+{
+ unsigned long div;
+ unsigned long cctl = __raw_readl(MXC_CCM_CCTL);
+
+ div = (cctl & MXC_CCM_CCTL_AHB_MASK) >> MXC_CCM_CCTL_AHB_OFFSET;
+
+ clk->rate = clk->parent->rate / (div + 1);
+}
+
+static void *pcdr_a[4] = {
+ MXC_CCM_PCDR0, MXC_CCM_PCDR1, MXC_CCM_PCDR2, MXC_CCM_PCDR3
+};
+static void _clk_perclkx_recalc(struct clk *clk)
+{
+ unsigned long perclk_pdf;
+ unsigned long pcdr;
+
+ if (clk->id < 0 || clk->id > 15)
+ return;
+
+ pcdr = __raw_readl(pcdr_a[clk->id >> 2]);
+
+ perclk_pdf =
+ (pcdr >> ((clk->id & 3) << 3)) & MXC_CCM_PCDR1_PERDIV1_MASK;
+
+ clk->rate = clk->parent->rate / (perclk_pdf + 1);
+}
+
+static unsigned long _clk_perclkx_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ unsigned long div;
+
+ div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ if (div > 64)
+ div = 64;
+
+ return clk->parent->rate / div;
+}
+
+static int _clk_perclkx_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long reg;
+ unsigned long div;
+
+ if (clk->id < 0 || clk->id > 15)
+ return -EINVAL;
+
+ div = clk->parent->rate / rate;
+ if (div > 64 || div < 1 || ((clk->parent->rate / div) != rate))
+ return -EINVAL;
+ div--;
+
+ reg =
+ __raw_readl(pcdr_a[clk->id >> 2]) & ~(MXC_CCM_PCDR1_PERDIV1_MASK <<
+ ((clk->id & 3) << 3));
+ reg |= div << ((clk->id & 3) << 3);
+ __raw_writel(reg, pcdr_a[clk->id >> 2]);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+static int _clk_perclkx_set_parent(struct clk *clk, struct clk *parent)
+{
+ unsigned long mcr;
+
+ if (parent != &upll_clk && parent != &ahb_clk)
+ return -EINVAL;
+
+ clk->parent = parent;
+ mcr = __raw_readl(MXC_CCM_MCR);
+ if (parent == &upll_clk)
+ mcr |= (1 << clk->id);
+ else
+ mcr &= ~(1 << clk->id);
+
+ __raw_writel(mcr, MXC_CCM_MCR);
+
+ return 0;
+}
+
+static int _clk_perclkx_set_parent3(struct clk *clk, struct clk *parent)
+{
+ unsigned long mcr = __raw_readl(MXC_CCM_MCR);
+ int bit;
+
+ if (parent != &upll_clk && parent != &ahb_clk &&
+ parent != &upll_24610k_clk)
+ return -EINVAL;
+
+ switch (clk->id) {
+ case 2:
+ bit = MXC_CCM_MCR_ESAI_CLK_MUX_OFFSET;
+ break;
+ case 13:
+ bit = MXC_CCM_MCR_SSI1_CLK_MUX_OFFSET;
+ break;
+ case 14:
+ bit = MXC_CCM_MCR_SSI2_CLK_MUX_OFFSET;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (parent == &upll_24610k_clk) {
+ mcr |= 1 << bit;
+ __raw_writel(mcr, MXC_CCM_MCR);
+ clk->parent = parent;
+ } else {
+ mcr &= ~(1 << bit);
+ __raw_writel(mcr, MXC_CCM_MCR);
+ return _clk_perclkx_set_parent(clk, parent);
+ }
+
+ return 0;
+}
+
+static void _clk_ipg_recalc(struct clk *clk)
+{
+ clk->rate = clk->parent->rate / 2; /* Always AHB / 2 */
+}
+
+static unsigned long _clk_parent_round_rate(struct clk *clk, unsigned long rate)
+{
+ return clk->parent->round_rate(clk->parent, rate);
+}
+
+static int _clk_parent_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret;
+
+ ret = clk->parent->set_rate(clk->parent, rate);
+ if (ret == 0)
+ clk->rate = rate;
+ return ret;
+}
+
+/* Top-level clocks */
+
+static struct clk osc24m_clk = {
+ .name = "osc24m",
+ .rate = OSC24M_CLK_FREQ,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk osc32k_clk = {
+ .name = "osc32k",
+ .rate = OSC32K_CLK_FREQ,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk mpll_clk = {
+ .name = "mpll",
+ .parent = &osc24m_clk,
+ .recalc = _clk_pll_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk upll_clk = {
+ .name = "upll",
+ .parent = &osc24m_clk,
+ .recalc = _clk_pll_recalc,
+ .enable = _clk_upll_enable,
+ .disable = _clk_upll_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_24610k_recalc(struct clk *clk)
+{
+ long long temp = clk->parent->rate * 2461LL;
+
+ do_div(temp, 24000);
+
+ clk->rate = temp; /* Always (UPLL * 24.61 / 240) */
+}
+
+static struct clk upll_24610k_clk = {
+ .name = "upll_24610k",
+ .parent = &upll_clk,
+ .recalc = _clk_24610k_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+/* Mid-level clocks */
+
+static struct clk cpu_clk = { /* ARM clock */
+ .name = "cpu_clk",
+ .parent = &mpll_clk,
+ .set_rate = _clk_cpu_set_rate,
+ .recalc = _clk_cpu_recalc,
+ .round_rate = _clk_cpu_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ahb_clk = { /* a.k.a. HCLK */
+ .name = "ahb_clk",
+ .parent = &cpu_clk,
+ .recalc = _clk_ahb_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ipg_clk = {
+ .name = "ipg_clk",
+ .parent = &ahb_clk,
+ .recalc = _clk_ipg_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+/* Bottom-level clocks */
+
+struct clk usb_ahb_clk = {
+ .name = "usb_ahb_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_USBOTG_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk rtic_clk = {
+ .name = "rtic_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_RTIC_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk emi_clk = {
+ .name = "emi_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_EMI_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk brom_clk = {
+ .name = "brom_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_BROM_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk per_clk[] = {
+ {
+ .name = "per_csi_clk",
+ .id = 0,
+ .parent = &upll_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_epit_clk",
+ .id = 1,
+ .parent = &ahb_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_esai_clk",
+ .id = 2,
+ .parent = &ahb_clk, /* can be AHB or UPLL or 24.61MHz */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent3,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_esdhc1_clk",
+ .id = 3,
+ .parent = &ahb_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_esdhc2_clk",
+ .id = 4,
+ .parent = &ahb_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_gpt_clk",
+ .id = 5,
+ .parent = &ahb_clk, /* Must be AHB */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_i2c_clk",
+ .id = 6,
+ .parent = &ahb_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_lcdc_clk",
+ .id = 7,
+ .parent = &upll_clk, /* Must be UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_nfc_clk",
+ .id = 8,
+ .parent = &ahb_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_owire_clk",
+ .id = 9,
+ .parent = &ahb_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_pwm_clk",
+ .id = 10,
+ .parent = &ahb_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_sim1_clk",
+ .id = 11,
+ .parent = &ahb_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_sim2_clk",
+ .id = 12,
+ .parent = &ahb_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_ssi1_clk",
+ .id = 13,
+ .parent = &ahb_clk, /* can be AHB or UPLL or 24.61MHz */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent3,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_ssi2_clk",
+ .id = 14,
+ .parent = &ahb_clk, /* can be AHB or UPLL or 24.61MHz */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent3,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_uart_clk",
+ .id = 15,
+ .parent = &ahb_clk, /* can be AHB or UPLL */
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .set_parent = _clk_perclkx_set_parent,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _perclk_enable,
+ .disable = _perclk_disable,
+ .flags = RATE_PROPAGATES,},
+};
+
+struct clk nfc_clk = {
+ .name = "nfc_clk",
+ .id = 0,
+ .parent = &per_clk[8],
+};
+
+struct clk audmux_clk = {
+ .name = "audmux_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_AUDMUX_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk ata_clk[] = {
+ {
+ .name = "ata_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_ATA_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &ata_clk[1],},
+ {
+ .name = "ata_ahb_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_ATA_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk can_clk[] = {
+ {
+ .name = "can_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_CAN1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "can_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_CAN2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk csi_clk[] = {
+ {
+ .name = "csi_clk",
+ .id = 0,
+ .parent = &per_clk[0],
+ .secondary = &csi_clk[1],},
+ {
+ .name = "csi_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_CSI_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &csi_clk[2],},
+ {
+ .name = "csi_ahb_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_CSI_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk cspi_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_CSPI1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "cspi_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_CSPI2_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "cspi_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_CSPI3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk dryice_clk = {
+ .name = "dryice_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_DRYICE_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk ect_clk = {
+ .name = "ect_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_ECT_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk epit1_clk[] = {
+ {
+ .name = "epit_clk",
+ .id = 0,
+ .parent = &per_clk[1],
+ .secondary = &epit1_clk[1],},
+ {
+ .name = "epit_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_EPIT1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk epit2_clk[] = {
+ {
+ .name = "epit_clk",
+ .id = 1,
+ .parent = &per_clk[1],
+ .secondary = &epit2_clk[1],},
+ {
+ .name = "epit_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_EPIT2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk esai_clk[] = {
+ {
+ .name = "esai_clk",
+ .id = 0,
+ .parent = &per_clk[2],
+ .secondary = &esai_clk[1],},
+ {
+ .name = "esai_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_ESAI_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esai_clk[2],},
+ {
+ .name = "esai_ahb_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_ESAI_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk esdhc1_clk[] = {
+ {
+ .name = "esdhc_clk",
+ .id = 0,
+ .parent = &per_clk[3],
+ .secondary = &esdhc1_clk[1],},
+ {
+ .name = "esdhc_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_ESDHC1_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc1_clk[2],},
+ {
+ .name = "esdhc_ahb_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_ESDHC1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk esdhc2_clk[] = {
+ {
+ .name = "esdhc_clk",
+ .id = 1,
+ .parent = &per_clk[4],
+ .secondary = &esdhc2_clk[1],},
+ {
+ .name = "esdhc_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_ESDHC2_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc2_clk[2],},
+ {
+ .name = "esdhc_ahb_clk",
+ .id = 1,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_ESDHC2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk fec_clk[] = {
+ {
+ .name = "fec_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_FEC_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &fec_clk[1],},
+ {
+ .name = "fec_ahb_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_FEC_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk gpio_clk[] = {
+ {
+ .name = "gpio_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_GPIO1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "gpio_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_GPIO2_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "gpio_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_GPIO3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk gpt1_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 0,
+ .parent = &per_clk[5],
+ .secondary = &gpt1_clk[1],},
+ {
+ .name = "gpt_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_GPT1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk gpt2_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 1,
+ .parent = &per_clk[5],
+ .secondary = &gpt1_clk[1],},
+ {
+ .name = "gpt_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_GPT2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk gpt3_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 2,
+ .parent = &per_clk[5],
+ .secondary = &gpt1_clk[1],},
+ {
+ .name = "gpt_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_GPT3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk gpt4_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 3,
+ .parent = &per_clk[5],
+ .secondary = &gpt1_clk[1],},
+ {
+ .name = "gpt_ipg_clk",
+ .id = 3,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_GPT4_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk i2c_clk[] = {
+ {
+ .name = "i2c_clk",
+ .id = 0,
+ .parent = &per_clk[6],},
+ {
+ .name = "i2c_clk",
+ .id = 1,
+ .parent = &per_clk[6],},
+ {
+ .name = "i2c_clk",
+ .id = 2,
+ .parent = &per_clk[6],},
+};
+
+struct clk iim_clk = {
+ .name = "iim_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_IIM_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk iomuxc_clk = {
+ .name = "iomuxc_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_IOMUXC_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk kpp_clk = {
+ .name = "kpp_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_KPP_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk lcdc_clk[] = {
+ {
+ .name = "lcdc_clk",
+ .id = 0,
+ .parent = &per_clk[7],
+ .secondary = &lcdc_clk[1],
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,},
+ {
+ .name = "lcdc_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_LCDC_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &lcdc_clk[2],},
+ {
+ .name = "lcdc_ahb_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_LCDC_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk owire_clk[] = {
+ {
+ .name = "owire_clk",
+ .id = 0,
+ .parent = &per_clk[9],
+ .secondary = &owire_clk[1],},
+ {
+ .name = "owire_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_OWIRE_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk pwm1_clk[] = {
+ {
+ .name = "pwm_clk",
+ .id = 0,
+ .parent = &per_clk[10],
+ .secondary = &pwm1_clk[1],},
+ {
+ .name = "pwm_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR1,
+ .enable_shift = MXC_CCM_CGCR1_PWM1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk pwm2_clk[] = {
+ {
+ .name = "pwm_clk",
+ .id = 1,
+ .parent = &per_clk[10],
+ .secondary = &pwm2_clk[1],},
+ {
+ .name = "pwm_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_PWM2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk pwm3_clk[] = {
+ {
+ .name = "pwm_clk",
+ .id = 2,
+ .parent = &per_clk[10],
+ .secondary = &pwm3_clk[1],},
+ {
+ .name = "pwm_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_PWM3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk pwm4_clk[] = {
+ {
+ .name = "pwm_clk",
+ .id = 3,
+ .parent = &per_clk[10],
+ .secondary = &pwm4_clk[1],},
+ {
+ .name = "pwm_ipg_clk",
+ .id = 3,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_PWM3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk rng_clk = {
+ .name = "rng_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_RNGB_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk scc_clk = {
+ .name = "scc_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_SCC_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk sdma_clk[] = {
+ {
+ .name = "sdma_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_SDMA_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &sdma_clk[1],},
+ {
+ .name = "sdma_ahb_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_SDMA_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk sim1_clk[] = {
+ {
+ .name = "sim1_clk",
+ .id = 0,
+ .parent = &per_clk[11],
+ .secondary = &sim1_clk[1],},
+ {
+ .name = "sim_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_SIM1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk sim2_clk[] = {
+ {
+ .name = "sim2_clk",
+ .id = 1,
+ .parent = &per_clk[12],
+ .secondary = &sim2_clk[1],},
+ {
+ .name = "sim_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_SIM2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk slcdc_clk[] = {
+ {
+ .name = "slcdc_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_SLCDC_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &slcdc_clk[1],},
+ {
+ .name = "slcdc_ahb_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR0,
+ .enable_shift = MXC_CCM_CGCR0_HCLK_SLCDC_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk spba_clk = {
+ .name = "spba_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_SPBA_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk ssi1_clk[] = {
+ {
+ .name = "ssi_clk",
+ .id = 0,
+ .parent = &per_clk[13],
+ .secondary = &ssi1_clk[1],},
+ {
+ .name = "ssi_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_SSI1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk ssi2_clk[] = {
+ {
+ .name = "ssi_clk",
+ .id = 1,
+ .parent = &per_clk[14],
+ .secondary = &ssi2_clk[1],},
+ {
+ .name = "ssi_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_SSI2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk tchscrn_clk = {
+ .name = "tchscrn_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_TCHSCRN_OFFSET,
+ .disable = _clk_disable,
+};
+
+struct clk uart1_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 0,
+ .parent = &per_clk[15],
+ .secondary = &uart1_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_UART1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk uart2_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 1,
+ .parent = &per_clk[15],
+ .secondary = &uart2_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_UART2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk uart3_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 2,
+ .parent = &per_clk[15],
+ .secondary = &uart3_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_UART3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk uart4_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 3,
+ .parent = &per_clk[15],
+ .secondary = &uart4_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 3,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_UART4_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk uart5_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 4,
+ .parent = &per_clk[15],
+ .secondary = &uart5_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 4,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_UART5_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk wdog_clk = {
+ .name = "wdog_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGCR2,
+ .enable_shift = MXC_CCM_CGCR2_WDOG_OFFSET,
+ .disable = _clk_disable,
+};
+
+static unsigned long _clk_usb_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long div;
+
+ div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ if (div > 64)
+ return -EINVAL;
+
+ return clk->parent->rate / div;
+}
+
+static int _clk_usb_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long reg;
+ unsigned long div;
+
+ div = clk->parent->rate / rate;
+
+ if (clk->parent->rate / div != rate)
+ return -EINVAL;
+ if (div > 64)
+ return -EINVAL;
+
+ reg = __raw_readl(MXC_CCM_CCTL) & ~MXC_CCM_CCTL_USB_DIV_MASK;
+ reg |= (div - 1) << MXC_CCM_CCTL_USB_DIV_OFFSET;
+ __raw_writel(reg, MXC_CCM_CCTL);
+
+ return 0;
+}
+
+static void _clk_usb_recalc(struct clk *clk)
+{
+ unsigned long div =
+ __raw_readl(MXC_CCM_CCTL) & MXC_CCM_CCTL_USB_DIV_MASK;
+
+ div >>= MXC_CCM_CCTL_USB_DIV_OFFSET;
+
+ clk->rate = clk->parent->rate / (div + 1);
+}
+
+static int _clk_usb_set_parent(struct clk *clk, struct clk *parent)
+{
+ unsigned long mcr;
+
+ if (parent != &upll_clk && parent != &ahb_clk)
+ return -EINVAL;
+
+ clk->parent = parent;
+ mcr = __raw_readl(MXC_CCM_MCR);
+ if (parent == &ahb_clk)
+ mcr |= (1 << MXC_CCM_MCR_USB_CLK_MUX_OFFSET);
+ else
+ mcr &= ~(1 << MXC_CCM_MCR_USB_CLK_MUX_OFFSET);
+
+ __raw_writel(mcr, MXC_CCM_MCR);
+
+ return 0;
+}
+
+static struct clk usb_clk = {
+ .name = "usb_clk",
+ .parent = &upll_clk,
+ .recalc = _clk_usb_recalc,
+ .set_rate = _clk_usb_set_rate,
+ .round_rate = _clk_usb_round_rate,
+ .set_parent = _clk_usb_set_parent,
+};
+
+/* CLKO */
+
+static unsigned long _clk_clko_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long div;
+
+ div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ if (div > 64)
+ return -EINVAL;
+
+ return clk->parent->rate / div;
+}
+
+static int _clk_clko_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long reg;
+ unsigned long div;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+ if (div > 64)
+ return -EINVAL;
+
+ reg = __raw_readl(MXC_CCM_MCR) & ~MXC_CCM_MCR_CLKO_DIV_MASK;
+ reg |= (div - 1) << MXC_CCM_MCR_CLKO_DIV_OFFSET;
+ __raw_writel(reg, MXC_CCM_MCR);
+
+ return 0;
+}
+
+static void _clk_clko_recalc(struct clk *clk)
+{
+ unsigned long div =
+ __raw_readl(MXC_CCM_MCR) & MXC_CCM_MCR_CLKO_DIV_MASK;
+
+ div >>= MXC_CCM_MCR_CLKO_DIV_OFFSET;
+
+ clk->rate = clk->parent->rate / (div + 1);
+}
+
+static struct clk *clko_sources[] = {
+ &osc32k_clk, /* 0x0 */
+ &osc24m_clk, /* 0x1 */
+ &cpu_clk, /* 0x2 */
+ &ahb_clk, /* 0x3 */
+ &ipg_clk, /* 0x4 */
+ NULL, /* 0x5 */
+ NULL, /* 0x6 */
+ NULL, /* 0x7 */
+ NULL, /* 0x8 */
+ NULL, /* 0x9 */
+ &per_clk[0], /* 0xA */
+ &per_clk[2], /* 0xB */
+ &per_clk[13], /* 0xC */
+ &per_clk[14], /* 0xD */
+ &usb_clk, /* 0xE */
+ NULL, /* 0xF */
+};
+
+#define NR_CLKO_SOURCES (sizeof(clko_sources) / sizeof(struct clk *))
+
+static int _clk_clko_set_parent(struct clk *clk, struct clk *parent)
+{
+ unsigned long reg =
+ __raw_readl(MXC_CCM_MCR) & ~MXC_CCM_MCR_CLKO_SEL_MASK;
+ struct clk **src;
+ int i;
+
+ for (i = 0, src = clko_sources; i < NR_CLKO_SOURCES; i++, src++)
+ if (*src == parent)
+ break;
+
+ if (i == NR_CLKO_SOURCES)
+ return -EINVAL;
+
+ clk->parent = parent;
+
+ reg |= i << MXC_CCM_MCR_CLKO_SEL_OFFSET;
+
+ __raw_writel(reg, MXC_CCM_MCR);
+
+ return 0;
+}
+
+static struct clk clko_clk = {
+ .name = "clko_clk",
+ .recalc = _clk_clko_recalc,
+ .set_rate = _clk_clko_set_rate,
+ .round_rate = _clk_clko_round_rate,
+ .set_parent = _clk_clko_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_MCR,
+ .enable_shift = MXC_CCM_MCR_CLKO_EN_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk *mxc_clks[] = {
+ &osc24m_clk,
+ &osc32k_clk,
+ &mpll_clk,
+ &upll_clk,
+ &cpu_clk,
+ &ahb_clk,
+ &ipg_clk,
+ &usb_ahb_clk,
+ &per_clk[0],
+ &per_clk[1],
+ &per_clk[2],
+ &per_clk[3],
+ &per_clk[4],
+ &per_clk[5],
+ &per_clk[6],
+ &per_clk[7],
+ &per_clk[8],
+ &per_clk[9],
+ &per_clk[10],
+ &per_clk[11],
+ &per_clk[12],
+ &per_clk[13],
+ &per_clk[14],
+ &per_clk[15],
+ &nfc_clk,
+ &audmux_clk,
+ &ata_clk[0],
+ &ata_clk[1],
+ &can_clk[0],
+ &can_clk[1],
+ &csi_clk[0],
+ &csi_clk[1],
+ &csi_clk[2],
+ &cspi_clk[0],
+ &cspi_clk[1],
+ &cspi_clk[2],
+ &dryice_clk,
+ &ect_clk,
+ &epit1_clk[0],
+ &epit1_clk[1],
+ &epit2_clk[0],
+ &epit2_clk[1],
+ &esai_clk[0],
+ &esai_clk[1],
+ &esai_clk[2],
+ &esdhc1_clk[0],
+ &esdhc1_clk[1],
+ &esdhc1_clk[2],
+ &esdhc2_clk[0],
+ &esdhc2_clk[1],
+ &esdhc2_clk[2],
+ &fec_clk[0],
+ &fec_clk[1],
+ &gpio_clk[0],
+ &gpio_clk[1],
+ &gpio_clk[2],
+ &gpt1_clk[0],
+ &gpt1_clk[1],
+ &gpt2_clk[0],
+ &gpt2_clk[1],
+ &gpt3_clk[0],
+ &gpt3_clk[1],
+ &gpt4_clk[0],
+ &gpt4_clk[1],
+ &i2c_clk[0],
+ &i2c_clk[1],
+ &i2c_clk[2],
+ &iim_clk,
+ &iomuxc_clk,
+ &kpp_clk,
+ &lcdc_clk[0],
+ &lcdc_clk[1],
+ &lcdc_clk[2],
+ &owire_clk[0],
+ &owire_clk[1],
+ &pwm1_clk[0],
+ &pwm1_clk[1],
+ &pwm2_clk[0],
+ &pwm2_clk[1],
+ &pwm3_clk[0],
+ &pwm3_clk[1],
+ &pwm4_clk[0],
+ &pwm4_clk[1],
+ &rng_clk,
+ &scc_clk,
+ &sdma_clk[0],
+ &sdma_clk[1],
+ &sim1_clk[0],
+ &sim1_clk[1],
+ &sim2_clk[0],
+ &sim2_clk[1],
+ &slcdc_clk[0],
+ &slcdc_clk[1],
+ &spba_clk,
+ &ssi1_clk[0],
+ &ssi1_clk[1],
+ &ssi2_clk[0],
+ &ssi2_clk[1],
+ &tchscrn_clk,
+ &uart1_clk[0],
+ &uart1_clk[1],
+ &uart2_clk[0],
+ &uart2_clk[1],
+ &uart3_clk[0],
+ &uart3_clk[1],
+ &uart4_clk[0],
+ &uart4_clk[1],
+ &uart5_clk[0],
+ &uart5_clk[1],
+ &wdog_clk,
+ &usb_clk,
+ &clko_clk,
+};
+
+/*!
+ * Function to get timer clock rate early in boot process before clock tree is
+ * initialized.
+ *
+ * @return Clock rate for timer
+ */
+unsigned long __init clk_early_get_timer_rate(void)
+{
+ upll_clk.recalc(&upll_clk);
+ per_clk[5].recalc(&per_clk[5]);
+ per_clk[5].enable(&per_clk[5]);
+
+ return per_clk[5].rate;
+}
+
+extern void propagate_rate(struct clk *tclk);
+
+int __init mxc_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2)
+{
+ int i;
+ struct clk **clkp;
+
+ for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++)
+ clk_register(*clkp);
+
+ /* Turn off all possible clocks */
+ __raw_writel((1 << MXC_CCM_CGCR0_HCLK_EMI_OFFSET), MXC_CCM_CGCR0);
+
+ __raw_writel((1 << MXC_CCM_CGCR1_GPT1_OFFSET) |
+ (1 << MXC_CCM_CGCR1_IIM_OFFSET), MXC_CCM_CGCR1);
+ __raw_writel(1 << MXC_CCM_CGCR2_SCC_OFFSET, MXC_CCM_CGCR2);
+
+ /* Init all perclk sources to ahb clock*/
+ for (i = 0; i < (sizeof(per_clk) / sizeof(struct clk)); i++)
+ per_clk[i].set_parent(&per_clk[i], &ahb_clk);
+
+ /* This will propagate to all children and init all the clock rates */
+ propagate_rate(&osc24m_clk);
+ propagate_rate(&osc32k_clk);
+
+ /* GPT clock must be derived from AHB clock */
+ clk_set_rate(&per_clk[5], ahb_clk.rate / 10);
+
+ /* LCDC clock must be derived from UPLL clock */
+ clk_set_parent(&per_clk[7], &upll_clk);
+ clk_set_rate(&per_clk[7], upll_clk.rate);
+
+ /* the NFC clock must be derived from AHB clock */
+ clk_set_parent(&per_clk[8], &ahb_clk);
+ clk_set_rate(&per_clk[8], ahb_clk.rate / 6);
+
+ /* sim clock */
+ clk_set_rate(&per_clk[11], ahb_clk.rate / 2);
+
+ /* the csi clock must be derived from UPLL clock */
+ clk_set_parent(&per_clk[0], &upll_clk);
+ clk_set_rate(&per_clk[0], upll_clk.rate / 5);
+
+ pr_info("Clock input source is %ld\n", osc24m_clk.rate);
+
+ clk_enable(&emi_clk);
+ clk_enable(&gpio_clk[0]);
+ clk_enable(&gpio_clk[1]);
+ clk_enable(&gpio_clk[2]);
+ clk_enable(&iim_clk);
+ clk_enable(&gpt1_clk[0]);
+ clk_enable(&iomuxc_clk);
+ clk_enable(&scc_clk);
+
+ return 0;
+}
diff --git a/arch/arm/mach-mx25/cpu.c b/arch/arm/mach-mx25/cpu.c
new file mode 100644
index 000000000000..27791da4787b
--- /dev/null
+++ b/arch/arm/mach-mx25/cpu.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mach-mx25/cpu.c
+ *
+ * @brief This file contains the CPU initialization code.
+ *
+ * @ingroup MSL_MX25
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+
+/*!
+ * CPU initialization. It is called by fixup_mxc_board()
+ */
+void __init mxc_cpu_init(void)
+{
+ if (!system_rev)
+ mxc_set_system_rev(0x25, CHIP_REV_1_0);
+}
+
+static int __init post_cpu_init(void)
+{
+ unsigned int base, reg;
+
+ base = IO_ADDRESS(AIPS1_BASE_ADDR);
+ __raw_writel(0x0, base + 0x40);
+ __raw_writel(0x0, base + 0x44);
+ __raw_writel(0x0, base + 0x48);
+ __raw_writel(0x0, base + 0x4C);
+ reg = __raw_readl(base + 0x50) & 0x00FFFFFF;
+ __raw_writel(reg, base + 0x50);
+
+ base = IO_ADDRESS(AIPS2_BASE_ADDR);
+ __raw_writel(0x0, base + 0x40);
+ __raw_writel(0x0, base + 0x44);
+ __raw_writel(0x0, base + 0x48);
+ __raw_writel(0x0, base + 0x4C);
+ reg = __raw_readl(base + 0x50) & 0x00FFFFFF;
+ __raw_writel(reg, base + 0x50);
+
+ return 0;
+}
+postcore_initcall(post_cpu_init);
diff --git a/arch/arm/mach-mx25/crm_regs.h b/arch/arm/mach-mx25/crm_regs.h
new file mode 100644
index 000000000000..a675232ab706
--- /dev/null
+++ b/arch/arm/mach-mx25/crm_regs.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX25_CRM_REGS_H__
+#define __ARCH_ARM_MACH_MX25_CRM_REGS_H__
+
+#include <mach/hardware.h>
+
+#define MXC_CCM_BASE ((char *)IO_ADDRESS(CCM_BASE_ADDR))
+
+/* Register offsets */
+#define MXC_CCM_MPCTL (MXC_CCM_BASE + 0x00)
+#define MXC_CCM_UPCTL (MXC_CCM_BASE + 0x04)
+#define MXC_CCM_CCTL (MXC_CCM_BASE + 0x08)
+#define MXC_CCM_CGCR0 (MXC_CCM_BASE + 0x0C)
+#define MXC_CCM_CGCR1 (MXC_CCM_BASE + 0x10)
+#define MXC_CCM_CGCR2 (MXC_CCM_BASE + 0x14)
+#define MXC_CCM_PCDR0 (MXC_CCM_BASE + 0x18)
+#define MXC_CCM_PCDR1 (MXC_CCM_BASE + 0x1C)
+#define MXC_CCM_PCDR2 (MXC_CCM_BASE + 0x20)
+#define MXC_CCM_PCDR3 (MXC_CCM_BASE + 0x24)
+#define MXC_CCM_RCSR (MXC_CCM_BASE + 0x28)
+#define MXC_CCM_CRDR (MXC_CCM_BASE + 0x2C)
+#define MXC_CCM_DCVR0 (MXC_CCM_BASE + 0x30)
+#define MXC_CCM_DCVR1 (MXC_CCM_BASE + 0x34)
+#define MXC_CCM_DCVR2 (MXC_CCM_BASE + 0x38)
+#define MXC_CCM_DCVR3 (MXC_CCM_BASE + 0x3C)
+#define MXC_CCM_LTR0 (MXC_CCM_BASE + 0x40)
+#define MXC_CCM_LTR1 (MXC_CCM_BASE + 0x44)
+#define MXC_CCM_LTR2 (MXC_CCM_BASE + 0x48)
+#define MXC_CCM_LTR3 (MXC_CCM_BASE + 0x4C)
+#define MXC_CCM_LTBR0 (MXC_CCM_BASE + 0x50)
+#define MXC_CCM_LTBR1 (MXC_CCM_BASE + 0x54)
+#define MXC_CCM_PMCR0 (MXC_CCM_BASE + 0x58)
+#define MXC_CCM_PMCR1 (MXC_CCM_BASE + 0x5C)
+#define MXC_CCM_PMCR2 (MXC_CCM_BASE + 0x60)
+#define MXC_CCM_MCR (MXC_CCM_BASE + 0x64)
+#define MXC_CCM_LPIMR0 (MXC_CCM_BASE + 0x68)
+#define MXC_CCM_LPIMR1 (MXC_CCM_BASE + 0x6C)
+
+#define MXC_CCM_MPCTL_BRMO (1 << 31)
+#define MXC_CCM_MPCTL_PD_OFFSET 26
+#define MXC_CCM_MPCTL_PD_MASK (0xf << 26)
+#define MXC_CCM_MPCTL_MFD_OFFSET 16
+#define MXC_CCM_MPCTL_MFD_MASK (0x3ff << 16)
+#define MXC_CCM_MPCTL_MFI_OFFSET 10
+#define MXC_CCM_MPCTL_MFI_MASK (0xf << 10)
+#define MXC_CCM_MPCTL_MFN_OFFSET 0
+#define MXC_CCM_MPCTL_MFN_MASK 0x3ff
+#define MXC_CCM_MPCTL_LF (1 << 15)
+
+#define MXC_CCM_UPCTL_BRMO (1 << 31)
+#define MXC_CCM_UPCTL_PD_OFFSET 26
+#define MXC_CCM_UPCTL_PD_MASK (0xf << 26)
+#define MXC_CCM_UPCTL_MFD_OFFSET 16
+#define MXC_CCM_UPCTL_MFD_MASK (0x3ff << 16)
+#define MXC_CCM_UPCTL_MFI_OFFSET 10
+#define MXC_CCM_UPCTL_MFI_MASK (0xf << 10)
+#define MXC_CCM_UPCTL_MFN_OFFSET 0
+#define MXC_CCM_UPCTL_MFN_MASK 0x3ff
+#define MXC_CCM_UPCTL_LF (1 << 15)
+
+#define MXC_CCM_CCTL_ARM_OFFSET 30
+#define MXC_CCM_CCTL_ARM_MASK (0x3 << 30)
+#define MXC_CCM_CCTL_AHB_OFFSET 28
+#define MXC_CCM_CCTL_AHB_MASK (0x3 << 28)
+#define MXC_CCM_CCTL_MPLL_RST (1 << 27)
+#define MXC_CCM_CCTL_UPLL_RST (1 << 26)
+#define MXC_CCM_CCTL_LP_CTL_OFFSET 24
+#define MXC_CCM_CCTL_LP_CTL_MASK (0x3 << 24)
+#define MXC_CCM_CCTL_LP_MODE_RUN (0x0 << 24)
+#define MXC_CCM_CCTL_LP_MODE_WAIT (0x1 << 24)
+#define MXC_CCM_CCTL_LP_MODE_DOZE (0x2 << 24)
+#define MXC_CCM_CCTL_LP_MODE_STOP (0x3 << 24)
+#define MXC_CCM_CCTL_UPLL_DISABLE (1 << 23)
+#define MXC_CCM_CCTL_MPLL_BYPASS (1 << 22)
+#define MXC_CCM_CCTL_USB_DIV_OFFSET 16
+#define MXC_CCM_CCTL_USB_DIV_MASK (0x3 << 16)
+#define MXC_CCM_CCTL_CG_CTRL (1 << 15)
+#define MXC_CCM_CCTL_ARM_SRC (1 << 14)
+#define MXC_CCM_CCTL_ARM_SRC_OFFSET 14
+
+#define MXC_CCM_CGCR0_HCLK_ATA_OFFSET 16
+#define MXC_CCM_CGCR0_HCLK_BROM_OFFSET 17
+#define MXC_CCM_CGCR0_HCLK_CSI_OFFSET 18
+#define MXC_CCM_CGCR0_HCLK_EMI_OFFSET 19
+#define MXC_CCM_CGCR0_HCLK_ESAI_OFFSET 20
+#define MXC_CCM_CGCR0_HCLK_ESDHC1_OFFSET 21
+#define MXC_CCM_CGCR0_HCLK_ESDHC2_OFFSET 22
+#define MXC_CCM_CGCR0_HCLK_FEC_OFFSET 23
+#define MXC_CCM_CGCR0_HCLK_LCDC_OFFSET 24
+#define MXC_CCM_CGCR0_HCLK_RTIC_OFFSET 25
+#define MXC_CCM_CGCR0_HCLK_SDMA_OFFSET 26
+#define MXC_CCM_CGCR0_HCLK_SLCDC_OFFSET 27
+#define MXC_CCM_CGCR0_HCLK_USBOTG_OFFSET 28
+
+#define MXC_CCM_CGCR0_PER_CSI_OFFSET 0
+#define MXC_CCM_CGCR0_PER_EPIT_OFFSET 1
+#define MXC_CCM_CGCR0_PER_ESAI_OFFSET 2
+#define MXC_CCM_CGCR0_PER_ESDHC1_OFFSET 3
+#define MXC_CCM_CGCR0_PER_ESDHC2_OFFSET 4
+#define MXC_CCM_CGCR0_PER_GPT_OFFSET 5
+#define MXC_CCM_CGCR0_PER_I2C_OFFSET 6
+#define MXC_CCM_CGCR0_PER_LCDC_OFFSET 7
+#define MXC_CCM_CGCR0_PER_NFC_OFFSET 8
+#define MXC_CCM_CGCR0_PER_OWIRE_OFFSET 9
+#define MXC_CCM_CGCR0_PER_PWM_OFFSET 10
+#define MXC_CCM_CGCR0_PER_SIM1_OFFSET 11
+#define MXC_CCM_CGCR0_PER_SIM2_OFFSET 12
+#define MXC_CCM_CGCR0_PER_SSI1_OFFSET 13
+#define MXC_CCM_CGCR0_PER_SSI2_OFFSET 14
+#define MXC_CCM_CGCR0_PER_UART_OFFSET 15
+
+#define MXC_CCM_CGCR1_AUDMUX_OFFSET 0
+#define MXC_CCM_CGCR1_ATA_OFFSET 1
+#define MXC_CCM_CGCR1_CAN1_OFFSET 2
+#define MXC_CCM_CGCR1_CAN2_OFFSET 3
+#define MXC_CCM_CGCR1_CSI_OFFSET 4
+#define MXC_CCM_CGCR1_CSPI1_OFFSET 5
+#define MXC_CCM_CGCR1_CSPI2_OFFSET 6
+#define MXC_CCM_CGCR1_CSPI3_OFFSET 7
+#define MXC_CCM_CGCR1_DRYICE_OFFSET 8
+#define MXC_CCM_CGCR1_ECT_OFFSET 9
+#define MXC_CCM_CGCR1_EPIT1_OFFSET 10
+#define MXC_CCM_CGCR1_EPIT2_OFFSET 11
+#define MXC_CCM_CGCR1_ESAI_OFFSET 12
+#define MXC_CCM_CGCR1_ESDHC1_OFFSET 13
+#define MXC_CCM_CGCR1_ESDHC2_OFFSET 14
+#define MXC_CCM_CGCR1_FEC_OFFSET 15
+#define MXC_CCM_CGCR1_GPIO1_OFFSET 16
+#define MXC_CCM_CGCR1_GPIO2_OFFSET 17
+#define MXC_CCM_CGCR1_GPIO3_OFFSET 18
+#define MXC_CCM_CGCR1_GPT1_OFFSET 19
+#define MXC_CCM_CGCR1_GPT2_OFFSET 20
+#define MXC_CCM_CGCR1_GPT3_OFFSET 21
+#define MXC_CCM_CGCR1_GPT4_OFFSET 22
+#define MXC_CCM_CGCR1_I2C1_OFFSET 23
+#define MXC_CCM_CGCR1_I2C2_OFFSET 24
+#define MXC_CCM_CGCR1_I2C3_OFFSET 25
+#define MXC_CCM_CGCR1_IIM_OFFSET 26
+#define MXC_CCM_CGCR1_IOMUXC_OFFSET 27
+#define MXC_CCM_CGCR1_KPP_OFFSET 28
+#define MXC_CCM_CGCR1_LCDC_OFFSET 29
+#define MXC_CCM_CGCR1_OWIRE_OFFSET 30
+#define MXC_CCM_CGCR1_PWM1_OFFSET 31
+
+#define MXC_CCM_CGCR2_PWM2_OFFSET (32-32)
+#define MXC_CCM_CGCR2_PWM3_OFFSET (33-32)
+#define MXC_CCM_CGCR2_PWM4_OFFSET (34-32)
+#define MXC_CCM_CGCR2_RNGB_OFFSET (35-32)
+#define MXC_CCM_CGCR2_RTIC_OFFSET (36-32)
+#define MXC_CCM_CGCR2_SCC_OFFSET (37-32)
+#define MXC_CCM_CGCR2_SDMA_OFFSET (38-32)
+#define MXC_CCM_CGCR2_SIM1_OFFSET (39-32)
+#define MXC_CCM_CGCR2_SIM2_OFFSET (40-32)
+#define MXC_CCM_CGCR2_SLCDC_OFFSET (41-32)
+#define MXC_CCM_CGCR2_SPBA_OFFSET (42-32)
+#define MXC_CCM_CGCR2_SSI1_OFFSET (43-32)
+#define MXC_CCM_CGCR2_SSI2_OFFSET (44-32)
+#define MXC_CCM_CGCR2_TCHSCRN_OFFSET (45-32)
+#define MXC_CCM_CGCR2_UART1_OFFSET (46-32)
+#define MXC_CCM_CGCR2_UART2_OFFSET (47-32)
+#define MXC_CCM_CGCR2_UART3_OFFSET (48-32)
+#define MXC_CCM_CGCR2_UART4_OFFSET (49-32)
+#define MXC_CCM_CGCR2_UART5_OFFSET (50-32)
+#define MXC_CCM_CGCR2_WDOG_OFFSET (51-32)
+
+#define MXC_CCM_CGCR0_STOP_MODE_MASK \
+ ((1 << MXC_CCM_CGCR0_HCLK_SLCDC_OFFSET) | \
+ (1 << MXC_CCM_CGCR0_HCLK_RTIC_OFFSET) | \
+ (1 << MXC_CCM_CGCR0_HCLK_EMI_OFFSET) | \
+ (1 << MXC_CCM_CGCR0_HCLK_BROM_OFFSET))
+
+#define MXC_CCM_CGCR1_STOP_MODE_MASK ((1 << MXC_CCM_CGCR1_IIM_OFFSET) | \
+ (1 << MXC_CCM_CGCR1_CAN2_OFFSET) | \
+ (1 << MXC_CCM_CGCR1_CAN1_OFFSET))
+
+#define MXC_CCM_CGCR2_STOP_MODE_MASK ((1 << MXC_CCM_CGCR2_SPBA_OFFSET) | \
+ (1 << MXC_CCM_CGCR2_SDMA_OFFSET) | \
+ (1 << MXC_CCM_CGCR2_RTIC_OFFSET))
+
+#define MXC_CCM_PCDR1_PERDIV1_MASK 0x3f
+
+#define MXC_CCM_RCSR_NF16B (1 << 14)
+
+#define MXC_CCM_PMCR2_VSTBY (1 << 17)
+#define MXC_CCM_PMCR2_OSC24M_DOWN (1 << 16)
+
+#define MXC_CCM_MCR_USB_XTAL_MUX_OFFSET 31
+#define MXC_CCM_MCR_CLKO_EN_OFFSET 30
+#define MXC_CCM_MCR_CLKO_DIV_OFFSET 24
+#define MXC_CCM_MCR_CLKO_DIV_MASK (0x3F << 24)
+#define MXC_CCM_MCR_CLKO_SEL_OFFSET 20
+#define MXC_CCM_MCR_CLKO_SEL_MASK (0xF << 20)
+#define MXC_CCM_MCR_ESAI_CLK_MUX_OFFSET 19
+#define MXC_CCM_MCR_SSI2_CLK_MUX_OFFSET 18
+#define MXC_CCM_MCR_SSI1_CLK_MUX_OFFSET 17
+#define MXC_CCM_MCR_USB_CLK_MUX_OFFSET 16
+
+#define MXC_CCM_MCR_PER_CLK_MUX_MASK (0xFFFF << 0)
+
+#define MXC_CCM_LPIMR0_MASK 0xFFFFFFFF
+#define MXC_CCM_LPIMR1_MASK 0xFFFFFFFF
+
+#endif /* __ARCH_ARM_MACH_MX25_CRM_REGS_H__ */
diff --git a/arch/arm/mach-mx25/devices.c b/arch/arm/mach-mx25/devices.c
new file mode 100644
index 000000000000..3424c863a2d8
--- /dev/null
+++ b/arch/arm/mach-mx25/devices.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+
+#include <mach/hardware.h>
+#include <mach/mmc.h>
+#include <mach/spba.h>
+#include <mach/sdma.h>
+
+#include "iomux.h"
+#include "sdma_script_code.h"
+#include "board-mx25_3stack.h"
+
+void mxc_sdma_get_script_info(sdma_script_start_addrs * sdma_script_addr)
+{
+ sdma_script_addr->mxc_sdma_ap_2_ap_addr = ap_2_ap_ADDR;
+ sdma_script_addr->mxc_sdma_ap_2_bp_addr = -1;
+ sdma_script_addr->mxc_sdma_bp_2_ap_addr = -1;
+ sdma_script_addr->mxc_sdma_loopback_on_dsp_side_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_interrupt_only_addr = -1;
+
+ sdma_script_addr->mxc_sdma_firi_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_firi_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_per_2_firi_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_firi_addr = -1;
+
+ sdma_script_addr->mxc_sdma_uart_2_per_addr = uart_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_uart_2_mcu_addr = uart_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_per_2_app_addr = per_2_app_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_app_addr = mcu_2_app_ADDR;
+
+ sdma_script_addr->mxc_sdma_per_2_per_addr = -1;
+
+ sdma_script_addr->mxc_sdma_uartsh_2_per_addr = uartsh_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_uartsh_2_mcu_addr = uartsh_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_per_2_shp_addr = per_2_shp_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_shp_addr = mcu_2_shp_ADDR;
+
+ sdma_script_addr->mxc_sdma_ata_2_mcu_addr = ata_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_ata_addr = mcu_2_ata_ADDR;
+
+ sdma_script_addr->mxc_sdma_app_2_per_addr = app_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_app_2_mcu_addr = app_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_shp_2_per_addr = shp_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_shp_2_mcu_addr = shp_2_mcu_ADDR;
+
+ sdma_script_addr->mxc_sdma_mshc_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_mshc_addr = -1;
+
+ sdma_script_addr->mxc_sdma_spdif_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_spdif_addr = -1;
+
+ sdma_script_addr->mxc_sdma_asrc_2_mcu_addr = -1;
+
+ sdma_script_addr->mxc_sdma_dptc_dvfs_addr = -1;
+ sdma_script_addr->mxc_sdma_ext_mem_2_ipu_addr = ext_mem__ipu_ram_ADDR;
+ sdma_script_addr->mxc_sdma_descrambler_addr = -1;
+
+ sdma_script_addr->mxc_sdma_start_addr = (unsigned short *)sdma_code;
+ sdma_script_addr->mxc_sdma_ram_code_size = RAM_CODE_SIZE;
+ sdma_script_addr->mxc_sdma_ram_code_start_addr = RAM_CODE_START_ADDR;
+}
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+#if defined(CONFIG_RTC_DRV_IMXDI) || defined(CONFIG_RTC_DRV_IMXDI_MODULE)
+static struct resource rtc_resources[] = {
+ {
+ .start = SRTC_BASE_ADDR,
+ .end = SRTC_BASE_ADDR + 0x40,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_DRYICE_NORM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device imxdi_rtc_device = {
+ .name = "imxdi_rtc",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(rtc_resources),
+ .resource = rtc_resources,
+};
+static void mxc_init_rtc(void)
+{
+ (void)platform_device_register(&imxdi_rtc_device);
+}
+#else
+static inline void mxc_init_rtc(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_WATCHDOG) || defined(CONFIG_MXC_WATCHDOG_MODULE)
+static struct resource wdt_resources[] = {
+ {
+ .start = WDOG1_BASE_ADDR,
+ .end = WDOG1_BASE_ADDR + 0x30,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_wdt_device = {
+ .name = "mxc_wdt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(wdt_resources),
+ .resource = wdt_resources,
+};
+
+static void mxc_init_wdt(void)
+{
+ (void)platform_device_register(&mxc_wdt_device);
+}
+#else
+static inline void mxc_init_wdt(void)
+{
+}
+#endif
+
+/* SPI controller and device data */
+#if defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE)
+
+#ifdef CONFIG_SPI_MXC_SELECT1
+/*!
+ * Resource definition for the CSPI1
+ */
+static struct resource mxcspi1_resources[] = {
+ [0] = {
+ .start = CSPI1_BASE_ADDR,
+ .end = CSPI1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI1,
+ .end = MXC_INT_CSPI1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI1 */
+static struct mxc_spi_master mxcspi1_data = {
+ .maxchipselect = 4,
+ .spi_version = 7,
+};
+
+/*! Device Definition for MXC CSPI1 */
+static struct platform_device mxcspi1_device = {
+ .name = "mxc_spi",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi1_resources),
+ .resource = mxcspi1_resources,
+};
+
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+
+#ifdef CONFIG_SPI_MXC_SELECT2
+/*!
+ * Resource definition for the CSPI2
+ */
+static struct resource mxcspi2_resources[] = {
+ [0] = {
+ .start = CSPI2_BASE_ADDR,
+ .end = CSPI2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI2,
+ .end = MXC_INT_CSPI2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI2 */
+static struct mxc_spi_master mxcspi2_data = {
+ .maxchipselect = 4,
+ .spi_version = 7,
+};
+
+/*! Device Definition for MXC CSPI2 */
+static struct platform_device mxcspi2_device = {
+ .name = "mxc_spi",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi2_resources),
+ .resource = mxcspi2_resources,
+};
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+
+#ifdef CONFIG_SPI_MXC_SELECT3
+/*!
+ * Resource definition for the CSPI3
+ */
+static struct resource mxcspi3_resources[] = {
+ [0] = {
+ .start = CSPI3_BASE_ADDR,
+ .end = CSPI3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI3,
+ .end = MXC_INT_CSPI3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI3 */
+static struct mxc_spi_master mxcspi3_data = {
+ .maxchipselect = 4,
+ .spi_version = 7,
+};
+
+/*! Device Definition for MXC CSPI3 */
+static struct platform_device mxcspi3_device = {
+ .name = "mxc_spi",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi3_resources),
+ .resource = mxcspi3_resources,
+};
+#endif /* CONFIG_SPI_MXC_SELECT3 */
+
+static inline void mxc_init_spi(void)
+{
+ spba_take_ownership(SPBA_CSPI2, SPBA_MASTER_A);
+ spba_take_ownership(SPBA_CSPI3, SPBA_MASTER_A);
+
+#ifdef CONFIG_SPI_MXC_SELECT1
+ if (platform_device_register(&mxcspi1_device) < 0)
+ printk(KERN_ERR "Error: Registering the SPI Controller_1\n");
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+#ifdef CONFIG_SPI_MXC_SELECT2
+ if (platform_device_register(&mxcspi2_device) < 0)
+ printk(KERN_ERR "Error: Registering the SPI Controller_2\n");
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+#ifdef CONFIG_SPI_MXC_SELECT3
+ if (platform_device_register(&mxcspi3_device) < 0)
+ printk(KERN_ERR "Error: Registering the SPI Controller_3\n");
+#endif /* CONFIG_SPI_MXC_SELECT3 */
+}
+#else
+static inline void mxc_init_spi(void)
+{
+}
+#endif
+
+/* I2C controller and device data */
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+/*!
+ * Resource definition for the I2C1
+ */
+static struct resource mxci2c1_resources[] = {
+ [0] = {
+ .start = I2C_BASE_ADDR,
+ .end = I2C_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C,
+ .end = MXC_INT_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c1_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+#ifdef CONFIG_I2C_MXC_SELECT2
+/*!
+ * Resource definition for the I2C2
+ */
+static struct resource mxci2c2_resources[] = {
+ [0] = {
+ .start = I2C2_BASE_ADDR,
+ .end = I2C2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C2,
+ .end = MXC_INT_I2C2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c2_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+#ifdef CONFIG_I2C_MXC_SELECT3
+/*!
+ * Resource definition for the I2C3
+ */
+static struct resource mxci2c3_resources[] = {
+ [0] = {
+ .start = I2C3_BASE_ADDR,
+ .end = I2C3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C3,
+ .end = MXC_INT_I2C3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c3_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+/*! Device Definition for MXC I2C1 */
+static struct platform_device mxci2c_devices[] = {
+#ifdef CONFIG_I2C_MXC_SELECT1
+ {
+ .name = "mxc_i2c",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c1_resources),
+ .resource = mxci2c1_resources,},
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT2
+ {
+ .name = "mxc_i2c",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c2_resources),
+ .resource = mxci2c2_resources,},
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT3
+ {
+ .name = "mxc_i2c",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c3_resources),
+ .resource = mxci2c3_resources,},
+#endif
+};
+
+static inline void mxc_init_i2c(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxci2c_devices); i++) {
+ if (platform_device_register(&mxci2c_devices[i]) < 0)
+ dev_err(&mxci2c_devices[i].dev,
+ "Unable to register I2C device\n");
+ }
+}
+#else
+static inline void mxc_init_i2c(void)
+{
+}
+#endif
+
+struct mxc_gpio_port mxc_gpio_ports[GPIO_PORT_NUM] = {
+ [0] = {
+ .chip.label = "gpio-0",
+ .base = IO_ADDRESS(GPIO1_BASE_ADDR),
+ .irq = MXC_INT_GPIO1,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE
+ },
+ [1] = {
+ .chip.label = "gpio-1",
+ .base = IO_ADDRESS(GPIO2_BASE_ADDR),
+ .irq = MXC_INT_GPIO2,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN
+ },
+ [2] = {
+ .chip.label = "gpio-2",
+ .base = IO_ADDRESS(GPIO3_BASE_ADDR),
+ .irq = MXC_INT_GPIO3,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 2
+ },
+ [3] = {
+ .chip.label = "gpio-3",
+ .base = IO_ADDRESS(GPIO4_BASE_ADDR),
+ .irq = MXC_INT_GPIO4,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 3
+ }
+};
+
+int __init mxc_register_gpios(void)
+{
+ return mxc_gpio_init(mxc_gpio_ports, ARRAY_SIZE(mxc_gpio_ports));
+}
+
+static inline void mxc_init_ssi(void)
+{
+ /* SPBA configuration for SSI - SDMA and MCU are set */
+ spba_take_ownership(SPBA_SSI1, SPBA_MASTER_A | SPBA_MASTER_C);
+ spba_take_ownership(SPBA_SSI2, SPBA_MASTER_A | SPBA_MASTER_C);
+}
+
+static struct platform_device mxc_dma_device = {
+ .name = "mxc_dma",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline void mxc_init_dma(void)
+{
+ (void)platform_device_register(&mxc_dma_device);
+}
+
+/* imx adc driver */
+#if defined(CONFIG_IMX_ADC) || defined(CONFIG_IMX_ADC_MODULE)
+
+static struct resource imx_adc_resources[] = {
+ [0] = {
+ .start = MXC_INT_TSC,
+ .end = MXC_INT_TSC,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TSC_BASE_ADDR,
+ .end = TSC_BASE_ADDR + PAGE_SIZE,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device imx_adc_device = {
+ .name = "imx_adc",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(imx_adc_resources),
+ .resource = imx_adc_resources,
+ .dev = {
+ .release = NULL,
+ },
+};
+static void imx_init_adc(void)
+{
+ (void)platform_device_register(&imx_adc_device);
+}
+#else
+static void imx_init_adc(void)
+{
+}
+#endif
+
+#if defined(CONFIG_CAN_FLEXCAN) || defined(CONFIG_CAN_FLEXCAN_MODULE)
+
+static struct resource flexcan1_resources[] = {
+ {
+ .start = CAN1_BASE_ADDR,
+ .end = CAN1_BASE_ADDR + 0x97F,
+ .flags = IORESOURCE_MEM,},
+ {
+ .start = MXC_INT_CAN1,
+ .end = MXC_INT_CAN1,
+ .flags = IORESOURCE_IRQ,}
+};
+static struct resource flexcan2_resources[] = {
+ {
+ .start = CAN3_BASE_ADDR,
+ .end = CAN3_BASE_ADDR + 0x97F,
+ .flags = IORESOURCE_MEM,},
+ {
+ .start = MXC_INT_CAN2,
+ .end = MXC_INT_CAN2,
+ .flags = IORESOURCE_IRQ,}
+};
+
+static struct platform_device flexcan_devices[] = {
+ {
+ .name = "FlexCAN",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &flexcan_data[0],
+ },
+ .num_resources = ARRAY_SIZE(flexcan1_resources),
+ .resource = flexcan1_resources,},
+ {
+ .name = "FlexCAN",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &flexcan_data[1],
+ },
+ .num_resources = ARRAY_SIZE(flexcan2_resources),
+ .resource = flexcan2_resources,},
+};
+
+static inline void mxc_init_flexcan(void)
+{
+#ifdef CONFIG_FLEXCAN_MXC_SELECT1
+ /* MX25 3stack doesn't use CAN1 */
+ platform_device_register(&flexcan_devices[0]);
+#endif
+ platform_device_register(&flexcan_devices[1]);
+}
+#else
+static inline void mxc_init_flexcan(void)
+{
+}
+#endif
+
+static struct platform_device mxc_alsa_surround_device = {
+ .name = "imx-3stack-wm8580",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static void mxc_init_surround_audio(void)
+{
+ platform_device_register(&mxc_alsa_surround_device);
+}
+
+#if defined(CONFIG_MXC_IIM) || defined(CONFIG_MXC_IIM_MODULE)
+static struct resource mxc_iim_resources[] = {
+ {
+ .start = IIM_BASE_ADDR,
+ .end = IIM_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_iim_device = {
+ .name = "mxc_iim",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(mxc_iim_resources),
+ .resource = mxc_iim_resources
+};
+
+static inline void mxc_init_iim(void)
+{
+ if (platform_device_register(&mxc_iim_device) < 0)
+ dev_err(&mxc_iim_device.dev,
+ "Unable to register mxc iim device\n");
+}
+#else
+static inline void mxc_init_iim(void)
+{
+}
+#endif
+
+static int __init mxc_init_devices(void)
+{
+ mxc_init_wdt();
+ mxc_init_spi();
+ mxc_init_i2c();
+ mxc_init_dma();
+ mxc_init_ssi();
+ mxc_init_surround_audio();
+ mxc_init_rtc();
+ imx_init_adc();
+ mxc_init_flexcan();
+ mxc_init_iim();
+
+ return 0;
+}
+
+arch_initcall(mxc_init_devices);
diff --git a/arch/arm/mach-mx25/dma.c b/arch/arm/mach-mx25/dma.c
new file mode 100644
index 000000000000..04d0c7aa1a0b
--- /dev/null
+++ b/arch/arm/mach-mx25/dma.c
@@ -0,0 +1,663 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+
+#include "serial.h"
+
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+#define soc_trans_type int_2_per
+#else
+#define soc_trans_type emi_2_per
+#endif
+
+#define MXC_SSI_TX0_REG 0x0
+#define MXC_SSI_TX1_REG 0x4
+#define MXC_SSI_RX0_REG 0x8
+#define MXC_SSI_RX1_REG 0xC
+#define MXC_SSI_TXFIFO_WML 0x4
+#define MXC_SSI_RXFIFO_WML 0x6
+
+#define MXC_ESAI_TX_REG 0x00
+#define MXC_ESAI_RX_REG 0x04
+#define MXC_ESAI_FIFO_WML 0x40
+
+struct mxc_sdma_info_entry_s {
+ mxc_dma_device_t device;
+ mxc_sdma_channel_params_t *chnl_info;
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart1_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART1_UFCR_RXTL,
+ .per_address = UART1_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART1_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART1_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart1_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART1_UFCR_TXTL,
+ .per_address = UART1_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART1_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART1_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart2_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART2_UFCR_RXTL,
+ .per_address = UART2_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART2_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART2_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart2_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART2_UFCR_TXTL,
+ .per_address = UART2_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART2_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART2_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart3_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART3_UFCR_RXTL,
+ .per_address = UART3_BASE_ADDR,
+ .peripheral_type = UART_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART3_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART3_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart3_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART3_UFCR_TXTL,
+ .per_address = UART3_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART3_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART3_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart4_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART4_UFCR_RXTL,
+ .per_address = UART4_BASE_ADDR,
+ .peripheral_type = UART_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART4_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART4_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart4_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART4_UFCR_TXTL,
+ .per_address = UART4_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART4_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART4_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart5_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART5_UFCR_RXTL,
+ .per_address = UART5_BASE_ADDR,
+ .peripheral_type = UART_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART5_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART5_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart5_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART5_UFCR_TXTL,
+ .per_address = UART5_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART5_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART5_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX0,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_memory_params = {
+ .chnl_params = {
+ .peripheral_type = MEMORY,
+ .transfer_type = emi_2_emi,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MEMORY,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_esai_16bit_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ESAI_FIFO_WML,
+ .per_address = ESAI_BASE_ADDR + MXC_ESAI_RX_REG,
+ .peripheral_type = ESAI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_ESAI_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ESAI_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_esai_16bit_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ESAI_FIFO_WML,
+ .per_address = ESAI_BASE_ADDR + MXC_ESAI_TX_REG,
+ .peripheral_type = ESAI,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_ESAI_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ESAI_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_esai_24bit_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ESAI_FIFO_WML,
+ .per_address = ESAI_BASE_ADDR + MXC_ESAI_RX_REG,
+ .peripheral_type = ESAI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_ESAI_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ESAI_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_esai_24bit_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ESAI_FIFO_WML,
+ .per_address = ESAI_BASE_ADDR + MXC_ESAI_TX_REG,
+ .peripheral_type = ESAI,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_ESAI_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ESAI_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static struct mxc_sdma_info_entry_s mxc_sdma_active_dma_info[] = {
+ {MXC_DMA_UART1_RX, &mxc_sdma_uart1_rx_params},
+ {MXC_DMA_UART1_TX, &mxc_sdma_uart1_tx_params},
+ {MXC_DMA_UART2_RX, &mxc_sdma_uart2_rx_params},
+ {MXC_DMA_UART2_TX, &mxc_sdma_uart2_tx_params},
+ {MXC_DMA_UART3_RX, &mxc_sdma_uart3_rx_params},
+ {MXC_DMA_UART3_TX, &mxc_sdma_uart3_tx_params},
+ {MXC_DMA_UART4_RX, &mxc_sdma_uart4_rx_params},
+ {MXC_DMA_UART4_TX, &mxc_sdma_uart4_tx_params},
+ {MXC_DMA_UART5_RX, &mxc_sdma_uart5_rx_params},
+ {MXC_DMA_UART5_TX, &mxc_sdma_uart5_tx_params},
+ {MXC_DMA_SSI1_8BIT_RX0, &mxc_sdma_ssi1_8bit_rx0_params},
+ {MXC_DMA_SSI1_8BIT_TX0, &mxc_sdma_ssi1_8bit_tx0_params},
+ {MXC_DMA_SSI1_16BIT_RX0, &mxc_sdma_ssi1_16bit_rx0_params},
+ {MXC_DMA_SSI1_16BIT_TX0, &mxc_sdma_ssi1_16bit_tx0_params},
+ {MXC_DMA_SSI1_24BIT_RX0, &mxc_sdma_ssi1_24bit_rx0_params},
+ {MXC_DMA_SSI1_24BIT_TX0, &mxc_sdma_ssi1_24bit_tx0_params},
+ {MXC_DMA_SSI1_8BIT_RX1, &mxc_sdma_ssi1_8bit_rx1_params},
+ {MXC_DMA_SSI1_8BIT_TX1, &mxc_sdma_ssi1_8bit_tx1_params},
+ {MXC_DMA_SSI1_16BIT_RX1, &mxc_sdma_ssi1_16bit_rx1_params},
+ {MXC_DMA_SSI1_16BIT_TX1, &mxc_sdma_ssi1_16bit_tx1_params},
+ {MXC_DMA_SSI1_24BIT_RX1, &mxc_sdma_ssi1_24bit_rx1_params},
+ {MXC_DMA_SSI1_24BIT_TX1, &mxc_sdma_ssi1_24bit_tx1_params},
+ {MXC_DMA_SSI2_8BIT_RX0, &mxc_sdma_ssi2_8bit_rx0_params},
+ {MXC_DMA_SSI2_8BIT_TX0, &mxc_sdma_ssi2_8bit_tx0_params},
+ {MXC_DMA_SSI2_16BIT_RX0, &mxc_sdma_ssi2_16bit_rx0_params},
+ {MXC_DMA_SSI2_16BIT_TX0, &mxc_sdma_ssi2_16bit_tx0_params},
+ {MXC_DMA_SSI2_24BIT_RX0, &mxc_sdma_ssi2_24bit_rx0_params},
+ {MXC_DMA_SSI2_24BIT_TX0, &mxc_sdma_ssi2_24bit_tx0_params},
+ {MXC_DMA_SSI2_8BIT_RX1, &mxc_sdma_ssi2_8bit_rx1_params},
+ {MXC_DMA_SSI2_8BIT_TX1, &mxc_sdma_ssi2_8bit_tx1_params},
+ {MXC_DMA_SSI2_16BIT_RX1, &mxc_sdma_ssi2_16bit_rx1_params},
+ {MXC_DMA_SSI2_16BIT_TX1, &mxc_sdma_ssi2_16bit_tx1_params},
+ {MXC_DMA_SSI2_24BIT_RX1, &mxc_sdma_ssi2_24bit_rx1_params},
+ {MXC_DMA_SSI2_24BIT_TX1, &mxc_sdma_ssi2_24bit_tx1_params},
+ {MXC_DMA_ESAI_16BIT_RX, &mxc_sdma_esai_16bit_rx_params},
+ {MXC_DMA_ESAI_16BIT_TX, &mxc_sdma_esai_16bit_tx_params},
+ {MXC_DMA_ESAI_24BIT_RX, &mxc_sdma_esai_24bit_rx_params},
+ {MXC_DMA_ESAI_24BIT_TX, &mxc_sdma_esai_24bit_tx_params},
+ {MXC_DMA_MEMORY, &mxc_sdma_memory_params},
+};
+
+static int mxc_sdma_info_entrys =
+ sizeof(mxc_sdma_active_dma_info) / sizeof(mxc_sdma_active_dma_info[0]);
+/*!
+ * This functions Returns the SDMA paramaters associated for a module
+ *
+ * @param channel_id the ID of the module requesting DMA
+ * @return returns the sdma parameters structure for the device
+ */
+mxc_sdma_channel_params_t *mxc_sdma_get_channel_params(mxc_dma_device_t
+ channel_id)
+{
+ struct mxc_sdma_info_entry_s *p = mxc_sdma_active_dma_info;
+ int i;
+
+ for (i = 0; i < mxc_sdma_info_entrys; i++, p++) {
+ if (p->device == channel_id)
+ return p->chnl_info;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(mxc_sdma_get_channel_params);
+
+/*!
+ * This functions marks the SDMA channels that are statically allocated
+ *
+ * @param chnl the channel array used to store channel information
+ */
+void mxc_get_static_channels(mxc_dma_channel_t *chnl)
+{
+#ifdef CONFIG_SDMA_IRAM
+ int i;
+ for (i = MXC_DMA_CHANNEL_IRAM; i < MAX_DMA_CHANNELS; i++)
+ chnl[i].dynamic = 0;
+#endif
+}
+EXPORT_SYMBOL(mxc_get_static_channels);
diff --git a/arch/arm/mach-mx25/iomux.c b/arch/arm/mach-mx25/iomux.c
new file mode 100644
index 000000000000..5c6141b5295b
--- /dev/null
+++ b/arch/arm/mach-mx25/iomux.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup GPIO_MX25 Board GPIO and Muxing Setup
+ * @ingroup MSL_MX25
+ */
+/*!
+ * @file mach-mx25/iomux.c
+ *
+ * @brief I/O Muxing control functions
+ *
+ * @ingroup GPIO_MX25
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include "iomux.h"
+
+/*!
+ * IOMUX register (base) addresses
+ */
+enum iomux_reg_addr {
+ IOMUXGPR = IO_ADDRESS(IOMUXC_BASE_ADDR),
+ /*!< General purpose */
+ IOMUXSW_MUX_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x008,
+ /*!< MUX control */
+ IOMUXSW_MUX_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x228,
+ /*!< last MUX control register */
+ IOMUXSW_PAD_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x22C,
+ /*!< Pad control */
+ IOMUXSW_PAD_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x414,
+ /*!< last Pad control register */
+ IOMUXSW_INPUT_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x460,
+ /*!< input select register */
+ IOMUXSW_INPUT_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x580,
+ /*!< last input select register */
+};
+
+#define MUX_PIN_NUM_MAX \
+ (((IOMUXSW_MUX_END - IOMUXSW_MUX_CTL) >> 2) + 1)
+#define MUX_INPUT_NUM_MUX \
+ (((IOMUXSW_INPUT_END - IOMUXSW_INPUT_CTL) >> 2) + 1)
+
+#define PIN_TO_IOMUX_INDEX(pin) (PIN_TO_IOMUX_MUX(pin) >> 2)
+
+static DEFINE_SPINLOCK(gpio_mux_lock);
+static u8 iomux_pin_res_table[MUX_PIN_NUM_MAX];
+#define MUX_USED 0x80
+
+/*!
+ * This function is used to configure a pin through the IOMUX module.
+ * FIXED ME: for backward compatible. Will be static function!
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param cfg an output function as defined in \b #iomux_pin_cfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+static int iomux_config_mux(iomux_pin_name_t pin, iomux_pin_cfg_t cfg)
+{
+ u32 ret = 0;
+ u32 pin_index = PIN_TO_IOMUX_INDEX(pin);
+ u32 mux_reg = PIN_TO_IOMUX_MUX(pin);
+ u8 *rp;
+
+ BUG_ON(pin_index > MUX_PIN_NUM_MAX);
+ if (mux_reg != NON_MUX_I) {
+ mux_reg += IOMUXGPR;
+ BUG_ON((mux_reg > IOMUXSW_MUX_END)
+ || (mux_reg < IOMUXSW_MUX_CTL));
+ spin_lock(&gpio_mux_lock);
+ __raw_writel(cfg, mux_reg);
+ /*
+ * Log a warning if a pin changes ownership
+ */
+ rp = iomux_pin_res_table + pin_index;
+ if (*rp && *rp != (cfg | MUX_USED)) {
+ /*Console: how to do */
+ printk(KERN_ERR "iomux_config_mux: Warning: iomux pin"
+ " config changed, index=%d register=%d, "
+ " prev=0x%x new=0x%x\n", pin_index, mux_reg,
+ *rp, cfg);
+ ret = -EINVAL;
+ }
+ *rp = cfg | MUX_USED;
+ spin_unlock(&gpio_mux_lock);
+ }
+
+ return ret;
+}
+
+/*!
+ * Request ownership for an IO pin. This function has to be the first one
+ * being called before that pin is used. The caller has to check the
+ * return value to make sure it returns 0.
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param cfg an input function as defined in \b #iomux_pin_cfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t cfg)
+{
+ int ret = iomux_config_mux(pin, cfg);
+ if (GPIO_TO_PORT(IOMUX_TO_GPIO(pin)) < GPIO_PORT_NUM) {
+ if (((cfg & (~MUX_CONFIG_SION)) == MUX_CONFIG_GPIO) ||
+ (((cfg & (~MUX_CONFIG_SION)) == MUX_CONFIG_FUNC) &&
+ ((pin == MX25_PIN_GPIO_A) || (pin == MX25_PIN_GPIO_B) ||
+ (pin == MX25_PIN_GPIO_C) || (pin == MX25_PIN_GPIO_D) ||
+ (pin == MX25_PIN_GPIO_E) || (pin == MX25_PIN_GPIO_F))))
+ ret |= gpio_request(IOMUX_TO_GPIO(pin), NULL);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(mxc_request_iomux);
+
+/*!
+ * Release ownership for an IO pin
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param cfg an input function as defined in \b #iomux_pin_cfg_t
+ */
+void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t cfg)
+{
+ u32 pin_index = PIN_TO_IOMUX_INDEX(pin);
+ u8 *rp = iomux_pin_res_table + pin_index;
+
+ BUG_ON((pin_index > MUX_PIN_NUM_MAX));
+
+ *rp = 0;
+ if (GPIO_TO_PORT(IOMUX_TO_GPIO(pin)) < GPIO_PORT_NUM) {
+ if (((cfg & (~MUX_CONFIG_SION)) == MUX_CONFIG_GPIO) ||
+ (((cfg & (~MUX_CONFIG_SION)) == MUX_CONFIG_FUNC) &&
+ ((pin == MX25_PIN_GPIO_A) || (pin == MX25_PIN_GPIO_B) ||
+ (pin == MX25_PIN_GPIO_C) || (pin == MX25_PIN_GPIO_D) ||
+ (pin == MX25_PIN_GPIO_E) || (pin == MX25_PIN_GPIO_F))))
+ gpio_free(IOMUX_TO_GPIO(pin));
+ }
+}
+EXPORT_SYMBOL(mxc_free_iomux);
+
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config the ORed value of elements defined in \b #iomux_pad_config_t
+ */
+void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config)
+{
+ u32 pad_reg = IOMUXGPR + PIN_TO_IOMUX_PAD(pin);
+
+ BUG_ON((pad_reg > IOMUXSW_PAD_END) || (pad_reg < IOMUXSW_PAD_CTL));
+
+ spin_lock(&gpio_mux_lock);
+ __raw_writel(config, pad_reg);
+ spin_unlock(&gpio_mux_lock);
+}
+EXPORT_SYMBOL(mxc_iomux_set_pad);
+
+/*!
+ * This function enables/disables the general purpose function for a particular
+ * signal.
+ *
+ * @param gp one signal as defined in \b #iomux_gp_func_t
+ * @param en \b #true to enable; \b #false to disable
+ */
+void mxc_iomux_set_gpr(iomux_gp_func_t gp, bool en)
+{
+ u32 l;
+
+ spin_lock(&gpio_mux_lock);
+ l = __raw_readl(IOMUXGPR);
+
+ if (en)
+ l |= gp;
+ else
+ l &= ~gp;
+
+ __raw_writel(l, IOMUXGPR);
+ spin_unlock(&gpio_mux_lock);
+}
+EXPORT_SYMBOL(mxc_iomux_set_gpr);
+
+/*!
+ * This function configures input path.
+ *
+ * @param input index of input select register as defined in \b
+ * #iomux_input_select_t
+ * @param config the binary value of elements defined in \b
+ * #iomux_input_config_t
+ */
+void mxc_iomux_set_input(iomux_input_select_t input, u32 config)
+{
+ u32 reg = IOMUXSW_INPUT_CTL + (input << 2);
+
+ BUG_ON(input >= MUX_INPUT_NUM_MUX);
+
+ spin_lock(&gpio_mux_lock);
+ __raw_writel(config, reg);
+ spin_unlock(&gpio_mux_lock);
+}
+EXPORT_SYMBOL(mxc_iomux_set_input);
diff --git a/arch/arm/mach-mx25/iomux.h b/arch/arm/mach-mx25/iomux.h
new file mode 100644
index 000000000000..4addb1f94557
--- /dev/null
+++ b/arch/arm/mach-mx25/iomux.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MACH_MX25_IOMUX_H__
+#define __MACH_MX25_IOMUX_H__
+
+#include <linux/types.h>
+#include <mach/gpio.h>
+#include "mx25_pins.h"
+
+/*!
+ * @file mach-mx25/iomux.h
+ *
+ * @brief I/O Muxing control definitions and functions
+ *
+ * @ingroup GPIO_MX25
+ */
+
+typedef unsigned int iomux_pin_name_t;
+
+/*!
+ * IOMUX functions
+ * SW_MUX_CTL
+ */
+typedef enum iomux_pin_config {
+ MUX_CONFIG_FUNC = 0, /*!< used as function */
+ MUX_CONFIG_ALT1, /*!< used as alternate function 1 */
+ MUX_CONFIG_ALT2, /*!< used as alternate function 2 */
+ MUX_CONFIG_ALT3, /*!< used as alternate function 3 */
+ MUX_CONFIG_ALT4, /*!< used as alternate function 4 */
+ MUX_CONFIG_ALT5, /*!< used as alternate function 5 */
+ MUX_CONFIG_ALT6, /*!< used as alternate function 6 */
+ MUX_CONFIG_ALT7, /*!< used as alternate function 7 */
+ MUX_CONFIG_SION = 0x1 << 4, /*!< used as LOOPBACK:MUX SION bit */
+ MUX_CONFIG_GPIO = MUX_CONFIG_ALT5, /*!< used as GPIO */
+} iomux_pin_cfg_t;
+
+/*!
+ * IOMUX pad functions
+ * SW_PAD_CTL
+ */
+typedef enum iomux_pad_config {
+ PAD_CTL_DRV_3_3V = 0x0 << 13,
+ PAD_CTL_DRV_1_8V = 0x1 << 13,
+ PAD_CTL_HYS_CMOS = 0x0 << 8,
+ PAD_CTL_HYS_SCHMITZ = 0x1 << 8,
+ PAD_CTL_PKE_NONE = 0x0 << 7,
+ PAD_CTL_PKE_ENABLE = 0x1 << 7,
+ PAD_CTL_PUE_KEEPER = 0x0 << 6,
+ PAD_CTL_PUE_PULL = 0x1 << 6,
+ PAD_CTL_PUE_PUD = 0x1 << 6,
+ PAD_CTL_100K_PD = 0x0 << 4,
+ PAD_CTL_47K_PU = 0x1 << 4,
+ PAD_CTL_100K_PU = 0x2 << 4,
+ PAD_CTL_22K_PU = 0x3 << 4,
+ PAD_CTL_ODE_CMOS = 0x0 << 3,
+ PAD_CTL_ODE_OpenDrain = 0x1 << 3,
+ PAD_CTL_DRV_NORMAL = 0x0 << 1,
+ PAD_CTL_DRV_HIGH = 0x1 << 1,
+ PAD_CTL_DRV_MAX = 0x2 << 1,
+ PAD_CTL_SRE_SLOW = 0x0 << 0,
+ PAD_CTL_SRE_FAST = 0x1 << 0
+} iomux_pad_config_t;
+
+/*!
+ * IOMUX general purpose functions
+ * IOMUXC_GPR1
+ */
+typedef enum iomux_gp_func {
+ MUX_SDCTL_CSD0_SEL = 0x1 << 0,
+ MUX_SDCTL_CSD1_SEL = 0x1 << 1,
+} iomux_gp_func_t;
+
+/*!
+ * IOMUX SELECT_INPUT register index
+ * Base register is IOMUXSW_INPUT_CTL in iomux.c
+ */
+typedef enum iomux_input_select {
+ MUX_IN_AUDMUX_P4_INPUT_DA_AMX = 0,
+ MUX_IN_AUDMUX_P4_INPUT_DB_AMX,
+ MUX_IN_AUDMUX_P4_INPUT_RXCLK_AMX,
+ MUX_IN_AUDMUX_P4_INPUT_RXFS_AMX,
+ MUX_IN_AUDMUX_P4_INPUT_TXCLK_AMX,
+ MUX_IN_AUDMUX_P4_INPUT_TXFS_AMX,
+ MUX_IN_AUDMUX_P7_INPUT_DA_AMX,
+ MUX_IN_AUDMUX_P7_INPUT_TXFS_AMX,
+ MUX_IN_CAN1_IPP_IND_CANRX,
+ MUX_IN_CAN2_IPP_IND_CANRX,
+ MUX_IN_CSI_IPP_CSI_D_0,
+ MUX_IN_CSI_IPP_CSI_D_1,
+ MUX_IN_CSPI1_IPP_IND_SS3_B,
+ MUX_IN_CSPI2_IPP_CSPI_CLK_IN,
+ MUX_IN_CSPI2_IPP_IND_DATAREADY_B,
+ MUX_IN_CSPI2_IPP_IND_MISO,
+ MUX_IN_CSPI2_IPP_IND_MOSI,
+ MUX_IN_CSPI2_IPP_IND_SS0_B,
+ MUX_IN_CSPI2_IPP_IND_SS1_B,
+ MUX_IN_CSPI3_IPP_CSPI_CLK_IN,
+ MUX_IN_CSPI3_IPP_IND_DATAREADY_B,
+ MUX_IN_CSPI3_IPP_IND_MISO,
+ MUX_IN_CSPI3_IPP_IND_MOSI,
+ MUX_IN_CSPI3_IPP_IND_SS0_B,
+ MUX_IN_CSPI3_IPP_IND_SS1_B,
+ MUX_IN_CSPI3_IPP_IND_SS2_B,
+ MUX_IN_CSPI3_IPP_IND_SS3_B,
+ MUX_IN_ESDHC1_IPP_DAT4_IN,
+ MUX_IN_ESDHC1_IPP_DAT5_IN,
+ MUX_IN_ESDHC1_IPP_DAT6_IN,
+ MUX_IN_ESDHC1_IPP_DAT7_IN,
+ MUX_IN_ESDHC2_IPP_CARD_CLK_IN,
+ MUX_IN_ESDHC2_IPP_CMD_IN,
+ MUX_IN_ESDHC2_IPP_DAT0_IN,
+ MUX_IN_ESDHC2_IPP_DAT1_IN,
+ MUX_IN_ESDHC2_IPP_DAT2_IN,
+ MUX_IN_ESDHC2_IPP_DAT3_IN,
+ MUX_IN_ESDHC2_IPP_DAT4_IN,
+ MUX_IN_ESDHC2_IPP_DAT5_IN,
+ MUX_IN_ESDHC2_IPP_DAT6_IN,
+ MUX_IN_ESDHC2_IPP_DAT7_IN,
+ MUX_IN_FEC_FEC_COL,
+ MUX_IN_FEC_FEC_CRS,
+ MUX_IN_FEC_FEC_RDATA_2,
+ MUX_IN_FEC_FEC_RDATA_3,
+ MUX_IN_FEC_FEC_RX_CLK,
+ MUX_IN_FEC_FEC_RX_ER,
+ MUX_IN_I2C2_IPP_SCL_IN,
+ MUX_IN_I2C2_IPP_SDA_IN,
+ MUX_IN_I2C3_IPP_SCL_IN,
+ MUX_IN_I2C3_IPP_SDA_IN,
+ MUX_IN_KPP_IPP_IND_COL_4,
+ MUX_IN_KPP_IPP_IND_COL_5,
+ MUX_IN_KPP_IPP_IND_COL_6,
+ MUX_IN_KPP_IPP_IND_COL_7,
+ MUX_IN_KPP_IPP_IND_ROW_4,
+ MUX_IN_KPP_IPP_IND_ROW_5,
+ MUX_IN_KPP_IPP_IND_ROW_6,
+ MUX_IN_KPP_IPP_IND_ROW_7,
+ MUX_IN_SIM1_PIN_SIM_RCVD1_IN,
+ MUX_IN_SIM1_PIN_SIM_SIMPD1,
+ MUX_IN_SIM1_SIM_RCVD1_IO,
+ MUX_IN_SIM2_PIN_SIM_RCVD1_IN,
+ MUX_IN_SIM2_PIN_SIM_SIMPD1,
+ MUX_IN_SIM2_SIM_RCVD1_IO,
+ MUX_IN_UART3_IPP_UART_RTS_B,
+ MUX_IN_UART3_IPP_UART_RXD_MUX,
+ MUX_IN_UART4_IPP_UART_RTS_B,
+ MUX_IN_UART4_IPP_UART_RXD_MUX,
+ MUX_IN_UART5_IPP_UART_RTS_B,
+ MUX_IN_UART5_IPP_UART_RXD_MUX,
+ MUX_IN_USB_TOP_IPP_IND_OTG_USB_OC,
+ MUX_IN_USB_TOP_IPP_IND_UH2_USB_OC,
+} iomux_input_select_t;
+
+/*!
+ * IOMUX input functions
+ * SW_SELECT_INPUT bits 2-0
+ */
+typedef enum iomux_input_config {
+ INPUT_CTL_PATH0 = 0x0,
+ INPUT_CTL_PATH1,
+ INPUT_CTL_PATH2,
+ INPUT_CTL_PATH3,
+ INPUT_CTL_PATH4,
+ INPUT_CTL_PATH5,
+ INPUT_CTL_PATH6,
+ INPUT_CTL_PATH7,
+} iomux_input_cfg_t;
+
+struct mxc_iomux_pin_cfg {
+ iomux_pin_name_t pin;
+ u8 mux_mode;
+ u16 pad_cfg;
+ u8 in_select;
+ u8 in_mode;
+};
+
+/*!
+ * Request ownership for an IO pin. This function has to be the first one
+ * being called before that pin is used. The caller has to check the
+ * return value to make sure it returns 0.
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param cfg an input function as defined in \b #iomux_pin_cfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t cfg);
+
+/*!
+ * Release ownership for an IO pin
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param cfg an input function as defined in \b #iomux_pin_cfg_t
+ */
+void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t cfg);
+
+/*!
+ * This function enables/disables the general purpose function for a particular
+ * signal.
+ *
+ * @param gp one signal as defined in \b #iomux_gp_func_t
+ * @param en \b #true to enable; \b #false to disable
+ */
+void mxc_iomux_set_gpr(iomux_gp_func_t gp, bool en);
+
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config the ORed value of elements defined in \b
+ * #iomux_pad_config_t
+ */
+void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config);
+
+/*!
+ * This function configures input path.
+ *
+ * @param input index of input select register as defined in \b
+ * #iomux_input_select_t
+ * @param config the binary value of elements defined in \b
+ * #iomux_input_cfg_t
+ */
+void mxc_iomux_set_input(iomux_input_select_t input, u32 config);
+#endif
diff --git a/arch/arm/mach-mx25/mm.c b/arch/arm/mach-mx25/mm.c
new file mode 100644
index 000000000000..1dc26d602f05
--- /dev/null
+++ b/arch/arm/mach-mx25/mm.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <asm/pgtable.h>
+#include <asm/mach/map.h>
+
+/*!
+ * @file mach-mx25/mm.c
+ *
+ * @brief This file creates static mapping between physical to virtual memory.
+ *
+ * @ingroup Memory_MX25
+ */
+
+/*!
+ * This structure defines the MX25 memory map.
+ */
+static struct map_desc mxc_io_desc[] __initdata = {
+ {
+ .virtual = IRAM_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(IRAM_BASE_ADDR),
+ .length = IRAM_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = X_MEMC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(X_MEMC_BASE_ADDR),
+ .length = X_MEMC_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = NFC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(NFC_BASE_ADDR),
+ .length = NFC_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = ROMP_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(ROMP_BASE_ADDR),
+ .length = ROMP_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = ASIC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(ASIC_BASE_ADDR),
+ .length = ASIC_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = AIPS1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS1_BASE_ADDR),
+ .length = AIPS1_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = SPBA0_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(SPBA0_BASE_ADDR),
+ .length = SPBA0_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = AIPS2_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS2_BASE_ADDR),
+ .length = 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 mxc_map_io(void)
+{
+ iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
+}
diff --git a/arch/arm/mach-mx25/mx25_3stack.c b/arch/arm/mach-mx25/mx25_3stack.c
new file mode 100644
index 000000000000..d334fbcd1205
--- /dev/null
+++ b/arch/arm/mach-mx25/mx25_3stack.c
@@ -0,0 +1,708 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/nodemask.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/mach/flash.h>
+#endif
+
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach/keypad.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+#include <mach/memory.h>
+#include <mach/gpio.h>
+#include <mach/mmc.h>
+
+#include "board-mx25_3stack.h"
+#include "crm_regs.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx25/mx25_3stack.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX25
+ */
+
+unsigned int mx25_3stack_board_io;
+
+/* working point(wp): 0 - 399MHz; 1 - 266MHz; 2 - 133MHz; */
+/* 24MHz input clock table */
+static struct cpu_wp cpu_wp_mx25[] = {
+ {
+ .pll_rate = 399000000,
+ .cpu_rate = 399000000,
+ .cpu_podf = 0x0,
+ .cpu_voltage = 1450000},
+ {
+ .pll_rate = 532000000,
+ .cpu_rate = 266000000,
+ .cpu_podf = 0x1,
+ .cpu_voltage = 1340000},
+ {
+ .pll_rate = 532000000,
+ .cpu_rate = 133000000,
+ .cpu_podf = 0x3,
+ .cpu_voltage = 1340000},
+};
+struct cpu_wp *get_cpu_wp(int *wp)
+{
+ *wp = 3;
+ return cpu_wp_mx25;
+}
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+#if defined(CONFIG_KEYBOARD_MXC) || defined(CONFIG_KEYBOARD_MXC_MODULE)
+static u16 keymapping[16] = {
+ KEY_UP, KEY_DOWN, KEY_VOLUMEDOWN, KEY_HOME,
+ KEY_RIGHT, KEY_LEFT, KEY_ENTER, KEY_VOLUMEUP,
+ KEY_F6, KEY_F8, KEY_F9, KEY_F10,
+ KEY_F1, KEY_F2, KEY_F3, KEY_POWER,
+};
+
+static struct resource mxc_kpp_resources[] = {
+ [0] = {
+ .start = MXC_INT_KPP,
+ .end = MXC_INT_KPP,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct keypad_data keypad_plat_data = {
+ .rowmax = 4,
+ .colmax = 4,
+ .irq = MXC_INT_KPP,
+ .learning = 0,
+ .delay = 2,
+ .matrix = keymapping,
+};
+
+/* mxc keypad driver */
+static struct platform_device mxc_keypad_device = {
+ .name = "mxc_keypad",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_kpp_resources),
+ .resource = mxc_kpp_resources,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &keypad_plat_data,
+ },
+};
+
+static void mxc_init_keypad(void)
+{
+ (void)platform_device_register(&mxc_keypad_device);
+}
+#else
+static inline void mxc_init_keypad(void)
+{
+}
+#endif
+
+/* MTD NAND flash */
+
+#if defined(CONFIG_MTD_NAND_MXC_V2) || defined(CONFIG_MTD_NAND_MXC_V2_MODULE)
+
+static struct mtd_partition mxc_nand_partitions[] = {
+ {
+ .name = "nand.bootloader",
+ .offset = 0,
+ .size = 1024 * 1024},
+ {
+ .name = "nand.kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 5 * 1024 * 1024},
+ {
+ .name = "nand.rootfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 256 * 1024 * 1024},
+ {
+ .name = "nand.configure",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 8 * 1024 * 1024},
+ {
+ .name = "nand.userfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL},
+};
+
+static struct flash_platform_data mxc_nand_data = {
+ .parts = mxc_nand_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_nand_partitions),
+ .width = 1,
+};
+
+static struct platform_device mxc_nand_mtd_device = {
+ .name = "mxc_nandv2_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static void mxc_init_nand_mtd(void)
+{
+ if (__raw_readl(MXC_CCM_RCSR) & MXC_CCM_RCSR_NF16B)
+ mxc_nand_data.width = 2;
+
+ platform_device_register(&mxc_nand_mtd_device);
+}
+#else
+static inline void mxc_init_nand_mtd(void)
+{
+}
+#endif
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || \
+ defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+static const char fb_default_mode[] = "CPT-VGA";
+
+/* mxc lcd driver */
+static struct platform_device mxc_fb_device = {
+ .name = "mxc_sdc_fb",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &fb_default_mode,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+};
+
+/*
+ * Power on/off CPT VGA panel.
+ */
+void board_power_lcd(int on)
+{
+ if (on)
+ mx2fb_set_brightness(MXC_DEFAULT_INTENSITY);
+ else
+ mx2fb_set_brightness(MXC_INTENSITY_OFF);
+}
+EXPORT_SYMBOL_GPL(board_power_lcd);
+
+static void mxc_init_fb(void)
+{
+ (void)platform_device_register(&mxc_fb_device);
+}
+#else
+static inline void mxc_init_fb(void)
+{
+}
+#endif
+
+#if defined(CONFIG_BACKLIGHT_MXC)
+static struct platform_device mxcbl_devices[] = {
+#if defined(CONFIG_BACKLIGHT_MXC_LCDC) || \
+ defined(CONFIG_BACKLIGHT_MXC_LCDC_MODULE)
+ {
+ .name = "mxc_lcdc_bl",
+ .id = 0,
+ },
+#endif
+};
+
+static inline void mxc_init_bl(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxcbl_devices); i++)
+ platform_device_register(&mxcbl_devices[i]);
+}
+#else
+static inline void mxc_init_bl(void)
+{
+}
+#endif
+
+/*!
+ * Power Key interrupt handler.
+ */
+static irqreturn_t power_key_int(int irq, void *dev_id)
+{
+ pr_info("on-off key pressed\n");
+ return 0;
+}
+
+/*!
+ * Power Key initialization.
+ */
+static int __init mxc_init_power_key(void)
+{
+ /*Set power key as wakeup resource */
+ int irq, ret;
+
+ mxc_request_iomux(MX25_PIN_A25, MUX_CONFIG_ALT5);
+ mxc_iomux_set_pad(MX25_PIN_A25, PAD_CTL_DRV_NORMAL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_A25), NULL);
+ gpio_direction_input(IOMUX_TO_GPIO(MX25_PIN_A25));
+
+ irq = IOMUX_TO_IRQ(MX25_PIN_A25);
+ set_irq_type(irq, IRQF_TRIGGER_RISING);
+ ret = request_irq(irq, power_key_int, 0, "power_key", 0);
+ if (ret)
+ pr_info("register on-off key interrupt failed\n");
+ else
+ enable_irq_wake(irq);
+
+ return ret;
+}
+
+late_initcall(mxc_init_power_key);
+
+static struct spi_board_info mxc_spi_board_info[] __initdata = {
+ {
+ .modalias = "cpld_spi",
+ .max_speed_hz = 18000000,
+ .bus_num = 1,
+ .chip_select = 0,
+ .mode = SPI_MODE_2,
+ },
+ {
+ .modalias = "wm8580_spi",
+ .max_speed_hz = 8000000, /* max spi SCK clock speed in HZ */
+ .bus_num = 1,
+ .chip_select = 1,
+ },
+};
+
+static struct mxc_camera_platform_data camera_data = {
+ .core_regulator = NULL,
+ .io_regulator = NULL,
+ .analog_regulator = NULL,
+ .gpo_regulator = NULL,
+ .mclk = 24000000,
+};
+
+static struct i2c_board_info mxc_i2c_board_info[] __initdata = {
+ {
+ .type = "sgtl5000-i2c",
+ .addr = 0x0a,
+ },
+ {
+ .type = "ak5702-i2c",
+ .addr = 0x13,
+ },
+ {
+ .type = "ov2640",
+ .addr = 0x30,
+ .platform_data = (void *)&camera_data,
+ },
+};
+
+#if defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000) \
+ || defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000_MODULE)
+static struct mxc_audio_platform_data sgtl5000_data = {
+ .ssi_num = 2,
+ .src_port = 1,
+ .ext_port = 4,
+ .hp_irq = IOMUX_TO_IRQ(MX25_PIN_A10),
+ .hp_status = headphone_det_status,
+ .vddio = 1800000,
+ .vdda = 3300000,
+ .vddd = 0,
+ .sysclk = 8300000,
+};
+
+static struct platform_device mxc_sgtl5000_device = {
+ .name = "imx-3stack-sgtl5000",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &sgtl5000_data,
+ },
+};
+
+static void mxc_init_sgtl5000(void)
+{
+ struct clk *cko1, *parent;
+ unsigned long rate;
+
+ /* cko1 clock */
+ mxc_request_iomux(MX25_PIN_CLKO, MUX_CONFIG_FUNC);
+
+ cko1 = clk_get(NULL, "clko_clk");
+ if (IS_ERR(cko1))
+ return;
+ parent = clk_get(NULL, "ipg_clk");
+ if (IS_ERR(parent))
+ return;
+ clk_set_parent(cko1, parent);
+ rate = clk_round_rate(cko1, 13000000);
+ if (rate < 8000000 || rate > 27000000) {
+ pr_err("Error: SGTL5000 mclk freq %ld out of range!\n", rate);
+ clk_put(parent);
+ clk_put(cko1);
+ return;
+ }
+ clk_set_rate(cko1, rate);
+ clk_enable(cko1);
+ sgtl5000_data.sysclk = rate;
+ sgtl5000_enable_amp();
+ platform_device_register(&mxc_sgtl5000_device);
+}
+#else
+static inline void mxc_init_sgtl5000(void)
+{
+}
+#endif
+
+#if defined(CONFIG_SND_SOC_IMX_3STACK_AK5702) \
+ || defined(CONFIG_SND_SOC_IMX_3STACK_AK5702_MODULE)
+static struct platform_device mxc_ak5702_device = {
+ .name = "imx-3stack-ak5702",
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static void mxc_init_ak5702(void)
+{
+ platform_device_register(&mxc_ak5702_device);
+}
+#else
+static inline void mxc_init_ak5702(void)
+{
+}
+#endif
+
+#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
+static struct resource smsc911x_resources[] = {
+ {
+ .start = LAN9217_BASE_ADDR,
+ .end = LAN9217_BASE_ADDR + 255,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_EXP_IO_BASE,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device smsc_lan9217_device = {
+ .name = "smsc911x",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(smsc911x_resources),
+ .resource = smsc911x_resources,
+};
+
+static int __init mxc_init_enet(void)
+{
+ (void)platform_device_register(&smsc_lan9217_device);
+ return 0;
+}
+#else
+static int __init mxc_init_enet(void)
+{
+ return 0;
+}
+#endif
+
+late_initcall(mxc_init_enet);
+
+#if defined(CONFIG_FEC) || defined(CONFIG_FEC_MODULE)
+unsigned int expio_intr_fec = MXC_INT_POWER_FAIL;
+EXPORT_SYMBOL(expio_intr_fec);
+#endif
+
+#if defined(CONFIG_IMX_SIM) || defined(CONFIG_IMX_SIM_MODULE)
+/* Used to configure the SIM bus */
+static struct mxc_sim_platform_data sim1_data = {
+ .clk_rate = 5000000,
+ .clock_sim = "sim1_clk",
+ .power_sim = NULL,
+ .init = NULL,
+ .exit = NULL,
+ .detect = 1,
+};
+
+/*!
+ * Resource definition for the SIM
+ */
+static struct resource mxc_sim1_resources[] = {
+ [0] = {
+ .start = SIM1_BASE_ADDR,
+ .end = SIM1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_SIM1,
+ .end = MXC_INT_SIM1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for IMX SIM */
+static struct platform_device mxc_sim1_device = {
+ .name = "mxc_sim",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &sim1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxc_sim1_resources),
+ .resource = mxc_sim1_resources,
+};
+
+static inline void mxc_init_sim(void)
+{
+ (void)platform_device_register(&mxc_sim1_device);
+}
+#else
+static inline void mxc_init_sim(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
+static struct mxc_mmc_platform_data mmc1_data = {
+ .ocr_mask = MMC_VDD_29_30 | MMC_VDD_32_33,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 400000,
+ .max_clk = 52000000,
+ .card_inserted_state = 1,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "esdhc_clk",
+};
+
+/*!
+ * Resource definition for the SDHC1
+ */
+static struct resource mxcsdhc1_resources[] = {
+ [0] = {
+ .start = MMC_SDHC1_BASE_ADDR,
+ .end = MMC_SDHC1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_SDHC1,
+ .end = MXC_INT_SDHC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IOMUX_TO_IRQ(MX25_PIN_A15),
+ .end = IOMUX_TO_IRQ(MX25_PIN_A15),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC1 */
+static struct platform_device mxcsdhc1_device = {
+ .name = "mxsdhci",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc1_resources),
+ .resource = mxcsdhc1_resources,
+};
+
+#ifdef CONFIG_MMC_IMX_ESDHCI_SELECT2
+static struct mxc_mmc_platform_data mmc2_data = {
+ .ocr_mask = MMC_VDD_29_30 | MMC_VDD_32_33,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 400000,
+ .max_clk = 52000000,
+ .card_fixed = 1,
+ .card_inserted_state = 1,
+ .status = sdhc_get_card_det_status,
+ .clock_mmc = "esdhc2_clk",
+};
+
+/*!
+ * Resource definition for the SDHC2
+ */
+static struct resource mxcsdhc2_resources[] = {
+ [0] = {
+ .start = MMC_SDHC2_BASE_ADDR,
+ .end = MMC_SDHC2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_SDHC2,
+ .end = MXC_INT_SDHC2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC2 */
+static struct platform_device mxcsdhc2_device = {
+ .name = "mxsdhci",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc2_resources),
+ .resource = mxcsdhc2_resources,
+};
+#endif
+
+static inline void mxc_init_mmc(void)
+{
+ (void)platform_device_register(&mxcsdhc1_device);
+#ifdef CONFIG_MMC_IMX_ESDHCI_SELECT2
+ (void)platform_device_register(&mxcsdhc2_device);
+#endif
+}
+#else
+static inline void mxc_init_mmc(void)
+{
+}
+#endif
+
+static void __init mx25_3stack_timer_init(void)
+{
+ mxc_clocks_init(32768, 24000000, 0, 0);
+ mxc_timer_init("gpt_clk");
+}
+
+static struct sys_timer mxc_timer = {
+ .init = mx25_3stack_timer_init,
+};
+
+#if defined(CONFIG_CAN_FLEXCAN) || defined(CONFIG_CAN_FLEXCAN_MODULE)
+static void flexcan_xcvr_enable(int id, int en)
+{
+ static int pwdn;
+
+ if (id != 1) /* MX25 3-stack uses only CAN2 */
+ return;
+
+ if (en) {
+ if (!pwdn++)
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_D14), 0);
+ } else {
+ if (!--pwdn)
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_D14), 1);
+ }
+}
+
+struct flexcan_platform_data flexcan_data[] = {
+ {
+ .core_reg = NULL,
+ .io_reg = NULL,
+ .xcvr_enable = flexcan_xcvr_enable,
+ .active = gpio_can_active,
+ .inactive = gpio_can_inactive,},
+ {
+ .core_reg = NULL,
+ .io_reg = NULL,
+ .xcvr_enable = flexcan_xcvr_enable,
+ .active = gpio_can_active,
+ .inactive = gpio_can_inactive,},
+};
+#endif
+
+/*!
+ * Board specific fixup function. It is called by \b setup_arch() in
+ * setup.c file very early on during kernel starts. It allows the user to
+ * statically fill in the proper values for the passed-in parameters. None of
+ * the parameters is used currently.
+ *
+ * @param desc pointer to \b struct \b machine_desc
+ * @param tags pointer to \b struct \b tag
+ * @param cmdline pointer to the command line
+ * @param mi pointer to \b struct \b meminfo
+ */
+static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ mxc_cpu_init();
+
+#ifdef CONFIG_DISCONTIGMEM
+ do {
+ int nid;
+ mi->nr_banks = MXC_NUMNODES;
+ for (nid = 0; nid < mi->nr_banks; nid++)
+ SET_NODE(mi, nid);
+ } while (0);
+#endif
+}
+
+/*!
+ * Board specific initialization.
+ */
+static void __init mxc_board_init(void)
+{
+ pr_info("AIPS1 VA base: 0x%x\n", IO_ADDRESS(AIPS1_BASE_ADDR));
+ mxc_cpu_common_init();
+ mxc_register_gpios();
+ mx25_3stack_gpio_init();
+ early_console_setup(saved_command_line);
+ mxc_init_keypad();
+#ifdef CONFIG_I2C
+ i2c_register_board_info(0, mxc_i2c_board_info,
+ ARRAY_SIZE(mxc_i2c_board_info));
+#endif
+ spi_register_board_info(mxc_spi_board_info,
+ ARRAY_SIZE(mxc_spi_board_info));
+ mx25_3stack_init_mc34704();
+ mxc_init_fb();
+ mxc_init_bl();
+ mxc_init_nand_mtd();
+ mxc_init_sgtl5000();
+ mxc_init_ak5702();
+ mxc_init_mmc();
+ mxc_init_sim();
+}
+
+/*
+ * The following uses standard kernel macros define in arch.h in order to
+ * initialize __mach_desc_MX25_3DS data structure.
+ */
+/* *INDENT-OFF* */
+MACHINE_START(MX25_3DS, "Freescale MX25 3-Stack Board")
+ /* Maintainer: Freescale Semiconductor, Inc. */
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .fixup = fixup_mxc_board,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mxc_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx25/mx25_3stack_cpld.c b/arch/arm/mach-mx25/mx25_3stack_cpld.c
new file mode 100644
index 000000000000..7afb82e1a062
--- /dev/null
+++ b/arch/arm/mach-mx25/mx25_3stack_cpld.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <mach/hardware.h>
+#include <asm/mach/irq.h>
+#include <mach/gpio.h>
+#include "board-mx25_3stack.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx25/mx25_3stack_cpld.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX25
+ */
+
+extern int mxc_spi_poll_transfer(struct spi_device *spi,
+ struct spi_transfer *t);
+
+struct spi_device *cpld_spi;
+
+/*!
+ * This function is used to tranfer data to CPLD regs over CSPI
+ */
+static inline int mx25_3ds_cpld_rw(u8 *buf, size_t len)
+{
+ struct spi_transfer t = {
+ .tx_buf = (const void *)buf,
+ .rx_buf = buf,
+ .len = len,
+ .cs_change = 0,
+ .delay_usecs = 0,
+ };
+ mxc_spi_poll_transfer(cpld_spi, &t);
+ return 0;
+}
+
+/*!
+ * This function is called to read a CPLD register over CSPI.
+ *
+ * @param offset number of the cpld register to be read
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+unsigned int spi_cpld_read(unsigned int offset)
+{
+ unsigned int frame[2];
+ unsigned int reg_num = offset >> 1;
+ unsigned int data = 0;
+
+ frame[0] = (1 << 13) | ((reg_num & 0x0001FFFF) >> 5) | 0x00001000;
+ frame[1] = (((reg_num & 0x0000001F) << 27) | 0x0200001f);
+ mx25_3ds_cpld_rw((u8 *) frame, 2);
+ data = (frame[1] >> 6) & 0xFFFF;
+
+ reg_num = (offset + 2) >> 1;
+ frame[0] = (1 << 13) | ((reg_num & 0x0001FFFF) >> 5) | 0x00001000;
+ frame[1] = (((reg_num & 0x0000001F) << 27) | 0x0200001f);
+ mx25_3ds_cpld_rw((u8 *) frame, 2);
+
+ data |= (((frame[1] >> 6) & 0xFFFF) << 16);
+ return data;
+}
+EXPORT_SYMBOL(spi_cpld_read);
+
+/*!
+ * This function is called to write to a CPLD register over CSPI.
+ *
+ * @param offset number of the cpld register to be written
+ * @param reg_val value to be written
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+unsigned int spi_cpld_write(unsigned int offset, unsigned int reg_val)
+{
+ unsigned int frame[2] = { 0, 0 };
+ unsigned int reg_num = offset >> 1;
+ unsigned int data = reg_val;
+
+ frame[0] = ((reg_num & 0x0001FFFF) >> 5) | 0x00001000;
+ frame[1] = (((reg_num & 0x0000001F) << 27) |
+ ((data & 0x0000FFFF) << 6) | 0x03C00027);
+ mx25_3ds_cpld_rw((u8 *) frame, 2);
+
+ reg_num = (offset + 2) >> 1;
+ data = reg_val >> 16;
+ frame[0] = 0;
+ frame[1] = 0;
+ frame[0] = ((reg_num & 0x0001FFFF) >> 5) | 0x00001000;
+ frame[1] = (((reg_num & 0x0000001F) << 27) |
+ ((data & 0x0000FFFF) << 6) | 0x03C00027);
+
+ mx25_3ds_cpld_rw((u8 *) frame, 2);
+
+ return 0;
+}
+EXPORT_SYMBOL(spi_cpld_write);
+
+static int __devinit mx25_3ds_cpld_probe(struct spi_device *spi)
+{
+ unsigned int i = 0;
+
+ spi->bits_per_word = 46;
+ cpld_spi = spi;
+
+ spi_setup(spi);
+ i = spi_cpld_read(CPLD_CODE_VER_REG);
+ pr_info("3-Stack Debug board detected, rev = 0x%04X\n", i);
+ spi_cpld_write(LED_SWITCH_REG, 0xFF);
+
+ /* disable the interrupt and clear the status */
+ spi_cpld_write(INTR_MASK_REG, 0);
+ spi_cpld_write(INTR_RESET_REG, 0xFFFF);
+ spi_cpld_write(INTR_RESET_REG, 0);
+ spi_cpld_write(INTR_MASK_REG, 0x1E);
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the CPLD callback functions.
+ */
+static struct spi_driver mx25_3ds_cpld_driver = {
+ .driver = {
+ .name = "cpld_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = mx25_3ds_cpld_probe,
+};
+
+static int __init mx25_3ds_cpld_init(void)
+{
+ pr_info("Registering the CPLD Driver\n");
+ return spi_register_driver(&mx25_3ds_cpld_driver);
+}
+device_initcall(mx25_3ds_cpld_init);
+
+static int __initdata is_dbg_removed = { 0 };
+static int __init remove_dbg_setup(char *__unused)
+{
+ is_dbg_removed = 1;
+ return 0;
+}
+__setup("remove_dbg", remove_dbg_setup);
+
+static void mxc_expio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 expio_irq;
+ struct irq_desc *d;
+
+ desc->chip->mask(irq); /* irq = gpio irq number */
+
+ expio_irq = MXC_EXP_IO_BASE;
+
+ d = irq_desc + expio_irq;
+ if (unlikely(!(d->handle_irq))) {
+ printk(KERN_ERR "\nEXPIO irq: %d unhandled\n", expio_irq);
+ BUG(); /* oops */
+ }
+ d->handle_irq(expio_irq, d);
+
+ desc->chip->ack(irq);
+ desc->chip->unmask(irq);
+}
+
+/*
+ * Disable an expio pin's interrupt by setting the bit in the imr.
+ * @param irq an expio virtual irq number
+ */
+static void expio_mask_irq(u32 irq)
+{
+}
+
+/*
+ * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr.
+ * @param irq an expanded io virtual irq number
+ */
+static void expio_ack_irq(u32 irq)
+{
+ /* clear the interrupt status */
+ spi_cpld_write(INTR_RESET_REG, 1);
+ spi_cpld_write(INTR_RESET_REG, 0);
+}
+
+/*
+ * Enable a expio pin's interrupt by clearing the bit in the imr.
+ * @param irq a expio virtual irq number
+ */
+static void expio_unmask_irq(u32 irq)
+{
+}
+
+static struct irq_chip expio_irq_chip = {
+ .ack = expio_ack_irq,
+ .mask = expio_mask_irq,
+ .unmask = expio_unmask_irq,
+};
+
+static int __init mxc_expio_init(void)
+{
+ int i;
+
+ if (is_dbg_removed)
+ return 0;
+
+ /*
+ * Configure INT line as GPIO input
+ */
+ mxc_request_iomux(MX25_PIN_PWM, MUX_CONFIG_GPIO);
+ mxc_iomux_set_pad(MX25_PIN_PWM, PAD_CTL_PUE_PUD);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_PWM), NULL);
+ gpio_direction_input(IOMUX_TO_GPIO(MX25_PIN_PWM));
+
+ for (i = MXC_EXP_IO_BASE; i < (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES);
+ i++) {
+ set_irq_chip(i, &expio_irq_chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+ set_irq_type(IOMUX_TO_IRQ(MX25_PIN_PWM), IRQF_TRIGGER_LOW);
+ set_irq_chained_handler(IOMUX_TO_IRQ(MX25_PIN_PWM),
+ mxc_expio_irq_handler);
+
+ return 0;
+}
+
+arch_initcall(mxc_expio_init);
diff --git a/arch/arm/mach-mx25/mx25_3stack_gpio.c b/arch/arm/mach-mx25/mx25_3stack_gpio.c
new file mode 100644
index 000000000000..5f7dd4f63b06
--- /dev/null
+++ b/arch/arm/mach-mx25/mx25_3stack_gpio.c
@@ -0,0 +1,1367 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include "board-mx25_3stack.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx25/mx25_3stack_gpio.c
+ *
+ * @brief This file contains all the GPIO setup functions for the board.
+ *
+ * @ingroup GPIO_MX25
+ */
+static struct mxc_iomux_pin_cfg __initdata mxc_iomux_pins[] = {
+};
+
+static struct mxc_iomux_pin_cfg __initdata sim_iomux_pins[] = {
+ /* SIM1 */
+ /* SIM1 CLK */
+ {
+ MX25_PIN_CSI_D2, MUX_CONFIG_ALT4,
+ PAD_CTL_SRE_FAST |
+ PAD_CTL_DRV_HIGH | PAD_CTL_DRV_3_3V |
+ PAD_CTL_HYS_CMOS | PAD_CTL_47K_PU |
+ PAD_CTL_PUE_PULL | PAD_CTL_ODE_CMOS | PAD_CTL_PKE_ENABLE,
+ },
+ /* SIM1 RST */
+ {
+ MX25_PIN_CSI_D3, MUX_CONFIG_ALT4,
+ PAD_CTL_DRV_HIGH | PAD_CTL_DRV_3_3V |
+ PAD_CTL_HYS_CMOS | PAD_CTL_47K_PU |
+ PAD_CTL_PUE_PULL | PAD_CTL_ODE_CMOS | PAD_CTL_PKE_ENABLE,
+ },
+ /* SIM1 VEN */
+ {
+ MX25_PIN_CSI_D4, MUX_CONFIG_ALT4,
+ PAD_CTL_DRV_HIGH | PAD_CTL_DRV_3_3V |
+ PAD_CTL_HYS_CMOS | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_PULL | PAD_CTL_ODE_CMOS | PAD_CTL_PKE_ENABLE,
+ },
+ /* SIM1 TX */
+ {
+ MX25_PIN_CSI_D5, MUX_CONFIG_ALT4,
+ PAD_CTL_SRE_FAST |
+ PAD_CTL_DRV_HIGH | PAD_CTL_DRV_3_3V |
+ PAD_CTL_HYS_CMOS | PAD_CTL_22K_PU |
+ PAD_CTL_PUE_PULL | PAD_CTL_ODE_OpenDrain | PAD_CTL_PKE_ENABLE,
+ },
+ /* SIM1 PD */
+ {
+ MX25_PIN_CSI_D6, MUX_CONFIG_ALT4,
+ PAD_CTL_DRV_HIGH | PAD_CTL_DRV_3_3V |
+ PAD_CTL_HYS_CMOS | PAD_CTL_22K_PU |
+ PAD_CTL_PUE_PULL | PAD_CTL_ODE_CMOS | PAD_CTL_PKE_ENABLE,
+ },
+ /* SIM2 */
+ /* SIM2 CLK */
+ {
+ MX25_PIN_CSI_D8, MUX_CONFIG_ALT4,
+ PAD_CTL_DRV_NORMAL | PAD_CTL_DRV_3_3V |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_ODE_CMOS | PAD_CTL_PKE_ENABLE,
+ },
+ /* SIM2 RST */
+ {
+ MX25_PIN_CSI_D9, MUX_CONFIG_ALT4,
+ PAD_CTL_DRV_NORMAL | PAD_CTL_DRV_3_3V |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_ODE_CMOS | PAD_CTL_PKE_ENABLE,
+ },
+ /* SIM2 VEN */
+ {
+ MX25_PIN_CSI_MCLK, MUX_CONFIG_ALT4,
+ PAD_CTL_DRV_NORMAL | PAD_CTL_DRV_3_3V |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_PULL | PAD_CTL_ODE_CMOS | PAD_CTL_PKE_NONE,
+ },
+ /* SIM2 TX */
+ {
+ MX25_PIN_CSI_VSYNC, MUX_CONFIG_ALT4,
+ PAD_CTL_DRV_NORMAL | PAD_CTL_DRV_3_3V |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_ODE_CMOS | PAD_CTL_PKE_ENABLE,
+ },
+ /* SIM2 PD */
+ {
+ MX25_PIN_CSI_HSYNC, MUX_CONFIG_ALT4,
+ PAD_CTL_DRV_NORMAL | PAD_CTL_DRV_3_3V |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_ODE_CMOS | PAD_CTL_PKE_ENABLE,
+ },
+};
+
+static int __initdata enable_sim = { 0 };
+static int __init sim_setup(char *__unused)
+{
+ enable_sim = 1;
+ return 1;
+}
+
+__setup("sim", sim_setup);
+
+/*!
+ * This system-wide GPIO function initializes the pins during system startup.
+ * All the statically linked device drivers should put the proper GPIO
+ * initialization code inside this function. It is called by
+ * \b fixup_mx25_3stack() during system startup. This function is board
+ * specific.
+ */
+void __init mx25_3stack_gpio_init(void)
+{
+ int i, num = 0;
+ struct mxc_iomux_pin_cfg *pin_ptr;
+
+ for (i = 0; i < ARRAY_SIZE(mxc_iomux_pins); i++) {
+ mxc_request_iomux(mxc_iomux_pins[i].pin,
+ mxc_iomux_pins[i].mux_mode);
+ if (mxc_iomux_pins[i].pad_cfg)
+ mxc_iomux_set_pad(mxc_iomux_pins[i].pin,
+ mxc_iomux_pins[i].pad_cfg);
+ if (mxc_iomux_pins[i].in_select)
+ mxc_iomux_set_input(mxc_iomux_pins[i].in_select,
+ mxc_iomux_pins[i].in_mode);
+ }
+
+ if (enable_sim) {
+ pin_ptr = sim_iomux_pins;
+ num = ARRAY_SIZE(sim_iomux_pins);
+ }
+
+ for (i = 0; i < num; i++) {
+ mxc_request_iomux(pin_ptr[i].pin, pin_ptr[i].mux_mode);
+ if (pin_ptr[i].pad_cfg)
+ mxc_iomux_set_pad(pin_ptr[i].pin, pin_ptr[i].pad_cfg);
+ if (pin_ptr[i].in_select)
+ mxc_iomux_set_input(pin_ptr[i].in_select,
+ pin_ptr[i].in_mode);
+ }
+}
+
+/*!
+ * Activate a UART port
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_active(int port, int no_irda)
+{
+ /*
+ * Configure the IOMUX control registers for the UART signals
+ */
+ switch (port) {
+ case 0:
+ /* UART 1 IOMUX Configs */
+ mxc_request_iomux(MX25_PIN_UART1_RXD, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_UART1_TXD, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_UART1_RTS, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_UART1_CTS, MUX_CONFIG_FUNC);
+ mxc_iomux_set_pad(MX25_PIN_UART1_RXD,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX25_PIN_UART1_TXD,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX25_PIN_UART1_RTS,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX25_PIN_UART1_CTS,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+
+ break;
+ case 1:
+ /* UART 2 IOMUX Configs */
+ mxc_request_iomux(MX25_PIN_UART2_RXD, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_UART2_TXD, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_UART2_RTS, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_UART2_CTS, MUX_CONFIG_FUNC);
+ mxc_iomux_set_pad(MX25_PIN_UART2_RXD,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX25_PIN_UART2_TXD, PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX25_PIN_UART2_RTS,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX25_PIN_UART2_CTS, PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+ break;
+ case 2:
+ /* UART 3 IOMUX Configs */
+ mxc_request_iomux(MX25_PIN_KPP_ROW0, MUX_CONFIG_ALT1); /*RXD*/
+ mxc_request_iomux(MX25_PIN_KPP_ROW1, MUX_CONFIG_ALT1); /*TXD*/
+ mxc_request_iomux(MX25_PIN_KPP_ROW2, MUX_CONFIG_ALT1); /*RTS*/
+ mxc_request_iomux(MX25_PIN_KPP_ROW3, MUX_CONFIG_ALT1); /*CTS*/
+
+ mxc_iomux_set_input(MUX_IN_UART3_IPP_UART_RTS_B,
+ INPUT_CTL_PATH1);
+ mxc_iomux_set_input(MUX_IN_UART3_IPP_UART_RXD_MUX,
+ INPUT_CTL_PATH1);
+ break;
+ case 3:
+ /* UART 4 IOMUX Configs */
+ mxc_request_iomux(MX25_PIN_LD8, MUX_CONFIG_ALT2); /*RXD*/
+ mxc_request_iomux(MX25_PIN_LD9, MUX_CONFIG_ALT2); /*TXD*/
+ mxc_request_iomux(MX25_PIN_LD10, MUX_CONFIG_ALT2); /*RTS*/
+ mxc_request_iomux(MX25_PIN_LD11, MUX_CONFIG_ALT2); /*CTS*/
+
+ mxc_iomux_set_input(MUX_IN_UART4_IPP_UART_RTS_B,
+ INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_UART4_IPP_UART_RXD_MUX,
+ INPUT_CTL_PATH0);
+ case 4:
+ /* UART 5 IOMUX Configs */
+ mxc_request_iomux(MX25_PIN_CSI_D2, MUX_CONFIG_ALT1); /*RXD*/
+ mxc_request_iomux(MX25_PIN_CSI_D3, MUX_CONFIG_ALT1); /*TXD*/
+ mxc_request_iomux(MX25_PIN_CSI_D4, MUX_CONFIG_ALT1); /*RTS*/
+ mxc_request_iomux(MX25_PIN_CSI_D5, MUX_CONFIG_ALT1); /*CTS*/
+
+ mxc_iomux_set_input(MUX_IN_UART5_IPP_UART_RTS_B,
+ INPUT_CTL_PATH1);
+ mxc_iomux_set_input(MUX_IN_UART5_IPP_UART_RXD_MUX,
+ INPUT_CTL_PATH1);
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(gpio_uart_active);
+
+/*!
+ * Inactivate a UART port
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_inactive(int port, int no_irda)
+{
+ switch (port) {
+ case 0:
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_UART1_RXD), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_UART1_TXD), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_UART1_RTS), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_UART1_CTS), NULL);
+
+ mxc_free_iomux(MX25_PIN_UART1_RXD, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_UART1_TXD, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_UART1_RTS, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_UART1_CTS, MUX_CONFIG_GPIO);
+ break;
+ case 1:
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_UART2_RXD), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_UART2_TXD), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_UART2_RTS), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_UART2_CTS), NULL);
+
+ mxc_free_iomux(MX25_PIN_UART2_RXD, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_UART2_TXD, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_UART2_RTS, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_UART2_CTS, MUX_CONFIG_GPIO);
+ break;
+ case 2:
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_ROW0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_ROW1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_ROW2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_ROW3), NULL);
+
+ mxc_free_iomux(MX25_PIN_KPP_ROW0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_KPP_ROW1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_KPP_ROW2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_KPP_ROW3, MUX_CONFIG_GPIO);
+ break;
+ case 3:
+ mxc_free_iomux(MX25_PIN_LD8, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_LD9, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_LD10, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_LD11, MUX_CONFIG_FUNC);
+ break;
+ case 4:
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D3), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D4), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D5), NULL);
+
+ mxc_free_iomux(MX25_PIN_CSI_D2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D3, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D4, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D5, MUX_CONFIG_GPIO);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(gpio_uart_inactive);
+
+/*!
+ * Configure the IOMUX GPR register to receive shared SDMA UART events
+ *
+ * @param port a UART port
+ */
+void config_uartdma_event(int port)
+{
+}
+EXPORT_SYMBOL(config_uartdma_event);
+
+/*!
+ * Activate Keypad
+ */
+void gpio_keypad_active(void)
+{
+ mxc_request_iomux(MX25_PIN_KPP_ROW0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_KPP_ROW1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_KPP_ROW2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_KPP_ROW3, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_KPP_COL0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_KPP_COL1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_KPP_COL2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_KPP_COL3, MUX_CONFIG_FUNC);
+
+#define KPP_PAD_CTL_ROW (PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD | \
+ PAD_CTL_100K_PU)
+#define KPP_PAD_CTL_COL (PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD | \
+ PAD_CTL_100K_PU | PAD_CTL_ODE_OpenDrain)
+
+ mxc_iomux_set_pad(MX25_PIN_KPP_ROW0, KPP_PAD_CTL_ROW);
+ mxc_iomux_set_pad(MX25_PIN_KPP_ROW1, KPP_PAD_CTL_ROW);
+ mxc_iomux_set_pad(MX25_PIN_KPP_ROW2, KPP_PAD_CTL_ROW);
+ mxc_iomux_set_pad(MX25_PIN_KPP_ROW3, KPP_PAD_CTL_ROW);
+ mxc_iomux_set_pad(MX25_PIN_KPP_COL0, KPP_PAD_CTL_COL);
+ mxc_iomux_set_pad(MX25_PIN_KPP_COL1, KPP_PAD_CTL_COL);
+ mxc_iomux_set_pad(MX25_PIN_KPP_COL2, KPP_PAD_CTL_COL);
+ mxc_iomux_set_pad(MX25_PIN_KPP_COL3, KPP_PAD_CTL_COL);
+
+#undef KPP_PAD_CTL_ROW
+#undef KPP_PAD_CTL_COL
+}
+EXPORT_SYMBOL(gpio_keypad_active);
+
+/*!
+ * Inactivate Keypad
+ */
+void gpio_keypad_inactive(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_ROW0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_ROW1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_ROW2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_ROW3), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_COL0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_COL1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_COL2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_KPP_COL3), NULL);
+
+ mxc_free_iomux(MX25_PIN_KPP_ROW0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_KPP_ROW1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_KPP_ROW2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_KPP_ROW3, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_KPP_COL0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_KPP_COL1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_KPP_COL2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_KPP_COL3, MUX_CONFIG_GPIO);
+}
+EXPORT_SYMBOL(gpio_keypad_inactive);
+
+/*!
+ * Activate FEC
+ */
+void gpio_fec_active(void)
+{
+ mxc_request_iomux(MX25_PIN_FEC_TX_CLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_FEC_RX_DV, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_FEC_RDATA0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_FEC_TDATA0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_FEC_TX_EN, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_FEC_MDC, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_FEC_MDIO, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_FEC_RDATA1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_FEC_TDATA1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_POWER_FAIL, MUX_CONFIG_FUNC); /* PHY INT */
+
+#define FEC_PAD_CTL1 (PAD_CTL_HYS_SCHMITZ | PAD_CTL_PUE_PUD | \
+ PAD_CTL_PKE_ENABLE)
+#define FEC_PAD_CTL2 (PAD_CTL_PUE_PUD)
+
+ mxc_iomux_set_pad(MX25_PIN_FEC_TX_CLK, FEC_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_FEC_RX_DV, FEC_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_FEC_RDATA0, FEC_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_FEC_TDATA0, FEC_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_FEC_TX_EN, FEC_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_FEC_MDC, FEC_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_FEC_MDIO, FEC_PAD_CTL1 | PAD_CTL_22K_PU);
+ mxc_iomux_set_pad(MX25_PIN_FEC_RDATA1, FEC_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_FEC_TDATA1, FEC_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_POWER_FAIL, FEC_PAD_CTL1);
+
+ /*
+ * Set up the FEC_RESET_B and FEC_ENABLE GPIO pins.
+ * Assert FEC_RESET_B, then power up the PHY by asserting
+ * FEC_ENABLE, at the same time lifting FEC_RESET_B.
+ *
+ * FEC_RESET_B: gpio2[3] is ALT 5 mode of pin D12
+ * FEC_ENABLE_B: gpio4[8] is ALT 5 mode of pin A17
+ */
+ mxc_request_iomux(MX25_PIN_A17, MUX_CONFIG_ALT5); /* FEC_EN */
+ mxc_request_iomux(MX25_PIN_D12, MUX_CONFIG_ALT5); /* FEC_RESET_B */
+
+ mxc_iomux_set_pad(MX25_PIN_A17, PAD_CTL_ODE_OpenDrain);
+ mxc_iomux_set_pad(MX25_PIN_D12, 0);
+
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_A17), "a17");
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_D12), "d12");
+
+ gpio_direction_output(IOMUX_TO_GPIO(MX25_PIN_A17), 0); /* FEC_EN */
+ gpio_direction_output(IOMUX_TO_GPIO(MX25_PIN_D12), 0); /* FEC_RESET_B */
+
+ /* drop PHY power */
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_A17), 0); /* FEC_EN */
+
+ /* assert reset */
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_D12), 0); /* FEC_RESET_B */
+ udelay(2); /* spec says 1us min */
+
+ /* turn on PHY power and lift reset */
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_A17), 1); /* FEC_EN */
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_D12), 1); /* FEC_RESET_B */
+
+#undef FEC_PAD_CTL_COMMON
+#undef FEC_PAD_CTL1
+#undef FEC_PAD_CTL2
+}
+EXPORT_SYMBOL(gpio_fec_active);
+
+/*!
+ * Inactivate FEC
+ */
+void gpio_fec_inactive(void)
+{
+ /*
+ * Turn off the PHY.
+ */
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_A17), 0);
+
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_TX_CLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_RX_DV), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_RDATA0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_TDATA0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_TX_EN), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_MDC), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_MDIO), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_RDATA1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_TDATA1), NULL);
+
+ mxc_free_iomux(MX25_PIN_FEC_TX_CLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_FEC_RX_DV, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_FEC_RDATA0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_FEC_TDATA0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_FEC_TX_EN, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_FEC_MDC, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_FEC_MDIO, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_FEC_RDATA1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_FEC_TDATA1, MUX_CONFIG_GPIO);
+ mxc_request_iomux(MX25_PIN_POWER_FAIL, MUX_CONFIG_FUNC); /* PHY INT */
+
+ mxc_free_iomux(MX25_PIN_A17, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_D12, MUX_CONFIG_GPIO); /* FEC_RESET_B */
+
+ /* We keep pin A17, so FEC_ENABLE doesn't float */
+}
+EXPORT_SYMBOL(gpio_fec_inactive);
+
+/*!
+ * Activate an I2C device
+ *
+ * @param i2c_num an I2C device
+ */
+void gpio_i2c_active(int i2c_num)
+{
+#define I2C_PAD_CTL (PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | \
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU | PAD_CTL_ODE_OpenDrain)
+
+ switch (i2c_num) {
+ case 0:
+ /*I2C1*/
+ mxc_request_iomux(MX25_PIN_I2C1_CLK, MUX_CONFIG_SION);
+ mxc_request_iomux(MX25_PIN_I2C1_DAT, MUX_CONFIG_SION);
+ mxc_iomux_set_pad(MX25_PIN_I2C1_CLK, I2C_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_I2C1_DAT, I2C_PAD_CTL);
+ break;
+ case 1:
+ /*I2C2*/
+ mxc_request_iomux(MX25_PIN_GPIO_C, MUX_CONFIG_ALT2); /*SCL*/
+ mxc_request_iomux(MX25_PIN_GPIO_D, MUX_CONFIG_ALT2); /*SDA*/
+ mxc_iomux_set_pad(MX25_PIN_GPIO_C, I2C_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_GPIO_D, I2C_PAD_CTL);
+ mxc_iomux_set_input(MUX_IN_I2C2_IPP_SCL_IN, INPUT_CTL_PATH1);
+ mxc_iomux_set_input(MUX_IN_I2C2_IPP_SDA_IN, INPUT_CTL_PATH1);
+
+#if 0
+ /* Or use FEC pins if it is not used */
+ mxc_request_iomux(MX25_PIN_FEC_RDATA1, MUX_CONFIG_ALT1); /*SCL*/
+ mxc_request_iomux(MX25_PIN_FEC_RX_DV, MUX_CONFIG_ALT1); /*SDA*/
+ mxc_iomux_set_pad(MX25_PIN_FEC_RDATA1, I2C_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_FEC_RX_DV, I2C_PAD_CTL);
+ mxc_iomux_set_input(MUX_IN_I2C2_IPP_SCL_IN, INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_I2C2_IPP_SDA_IN, INPUT_CTL_PATH0);
+#endif
+
+ break;
+ case 2:
+ /*I2C3*/
+ mxc_request_iomux(MX25_PIN_HSYNC, MUX_CONFIG_ALT2); /*SCL*/
+ mxc_request_iomux(MX25_PIN_VSYNC, MUX_CONFIG_ALT2); /*SDA*/
+ mxc_iomux_set_pad(MX25_PIN_HSYNC, I2C_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_VSYNC, I2C_PAD_CTL);
+ mxc_iomux_set_input(MUX_IN_I2C3_IPP_SCL_IN, INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_I2C3_IPP_SDA_IN, INPUT_CTL_PATH0);
+ break;
+ default:
+ break;
+ }
+#undef I2C_PAD_CTL
+}
+EXPORT_SYMBOL(gpio_i2c_active);
+
+/*!
+ * Inactivate an I2C device
+ *
+ * @param i2c_num an I2C device
+ */
+void gpio_i2c_inactive(int i2c_num)
+{
+ switch (i2c_num) {
+ case 0:
+ /*I2C1*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_I2C1_CLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_I2C1_DAT), NULL);
+ mxc_free_iomux(MX25_PIN_I2C1_CLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_I2C1_DAT, MUX_CONFIG_GPIO);
+ break;
+ case 1:
+ /*I2C2*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_C), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_D), NULL);
+ mxc_free_iomux(MX25_PIN_GPIO_C, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_GPIO_D, MUX_CONFIG_GPIO);
+
+#if 0
+ /* Or use FEC pins if not in use */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_RDATA1, NULL); /*SCL*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_FEC_RX_DV, NULL); /*SDA*/
+ mxc_free_iomux(MX25_PIN_FEC_RDATA1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_FEC_RX_DV, MUX_CONFIG_GPIO);
+#endif
+
+ break;
+ case 2:
+ /*I2C3*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_HSYNC), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_VSYNC), NULL);
+ mxc_free_iomux(MX25_PIN_HSYNC, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_VSYNC, MUX_CONFIG_GPIO);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(gpio_i2c_inactive);
+
+/*!
+ * Activate a CSPI device
+ *
+ * @param cspi_mod a CSPI device
+ */
+void gpio_spi_active(int cspi_mod)
+{
+#define SPI_PAD_CTL1 (PAD_CTL_HYS_SCHMITZ|PAD_CTL_PKE_ENABLE| \
+ PAD_CTL_100K_PU)
+#define SPI_PAD_CTL2 (PAD_CTL_HYS_SCHMITZ|PAD_CTL_PKE_ENABLE| \
+ PAD_CTL_PUE_PUD|PAD_CTL_100K_PU)
+
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ mxc_request_iomux(MX25_PIN_CSPI1_MOSI, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSPI1_MISO, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSPI1_SS0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSPI1_SS1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSPI1_SCLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSPI1_RDY, MUX_CONFIG_FUNC);
+#ifndef CONFIG_CAN_FLEXCAN /* MX25 3-stack uses this pin for CAN2 */
+ mxc_request_iomux(MX25_PIN_GPIO_C, MUX_CONFIG_ALT5); /*SS2*/
+#endif
+ mxc_request_iomux(MX25_PIN_VSTBY_ACK, MUX_CONFIG_ALT2); /*SS3*/
+
+ /* Or if VSTBY_ACK is being used */
+ /*mxc_request_iomux(MX25_PIN_NF_CE0, MUX_CONFIG_ALT1);*/ /*SS3*/
+
+ mxc_iomux_set_pad(MX25_PIN_CSPI1_MOSI, SPI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSPI1_MISO, SPI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSPI1_SS0, SPI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSPI1_SS1, SPI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSPI1_SCLK, SPI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSPI1_RDY, SPI_PAD_CTL1);
+#ifndef CONFIG_CAN_FLEXCAN /* MX25 3-stack uses this pin for CAN2 */
+ mxc_iomux_set_pad(MX25_PIN_GPIO_C, SPI_PAD_CTL2);
+#endif
+ mxc_iomux_set_pad(MX25_PIN_VSTBY_ACK, SPI_PAD_CTL1);
+
+ mxc_iomux_set_input(MUX_IN_CSPI1_IPP_IND_SS3_B,
+ INPUT_CTL_PATH1);
+ break;
+ case 1:
+ /* SPI2 */
+ mxc_request_iomux(MX25_PIN_LD12, MUX_CONFIG_ALT2); /*MOSI*/
+ mxc_request_iomux(MX25_PIN_LD13, MUX_CONFIG_ALT2); /*MISO*/
+ mxc_request_iomux(MX25_PIN_LD14, MUX_CONFIG_ALT2); /*SCLK*/
+ mxc_request_iomux(MX25_PIN_LD15, MUX_CONFIG_ALT2); /*RDY*/
+ mxc_request_iomux(MX25_PIN_OE_ACD, MUX_CONFIG_ALT2); /*SS0*/
+ mxc_request_iomux(MX25_PIN_CONTRAST, MUX_CONFIG_ALT2); /*SS1*/
+ mxc_request_iomux(MX25_PIN_GPIO_C, MUX_CONFIG_ALT7); /*SS2*/
+ mxc_request_iomux(MX25_PIN_UART2_RTS, MUX_CONFIG_ALT6); /*SS3*/
+
+ mxc_iomux_set_pad(MX25_PIN_LD12, SPI_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_LD13, SPI_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_LD14, SPI_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_LD15, SPI_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_OE_ACD, SPI_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_CONTRAST, SPI_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_GPIO_C, SPI_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_UART2_RTS, SPI_PAD_CTL2);
+
+ mxc_iomux_set_input(MUX_IN_CSPI2_IPP_CSPI_CLK_IN,
+ INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI2_IPP_IND_DATAREADY_B,
+ INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI2_IPP_IND_MISO, INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI2_IPP_IND_MOSI, INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI2_IPP_IND_SS0_B,
+ INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI2_IPP_IND_SS1_B,
+ INPUT_CTL_PATH0);
+ break;
+ case 2:
+ /* SPI3 */
+ mxc_request_iomux(MX25_PIN_EB0, MUX_CONFIG_ALT6); /*SS0*/
+ mxc_request_iomux(MX25_PIN_EB1, MUX_CONFIG_ALT6); /*SS1*/
+ mxc_request_iomux(MX25_PIN_CS4, MUX_CONFIG_ALT6); /*MOSI*/
+ mxc_request_iomux(MX25_PIN_CS5, MUX_CONFIG_ALT6); /*MISO*/
+ mxc_request_iomux(MX25_PIN_ECB, MUX_CONFIG_ALT6); /*SCLK*/
+ mxc_request_iomux(MX25_PIN_LBA, MUX_CONFIG_ALT6); /*RDY*/
+ mxc_request_iomux(MX25_PIN_GPIO_D, MUX_CONFIG_ALT7); /*SS2*/
+ mxc_request_iomux(MX25_PIN_CSI_D9, MUX_CONFIG_ALT7); /*SS3*/
+
+ mxc_iomux_set_pad(MX25_PIN_EB0, SPI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_EB1, SPI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CS4, SPI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CS5, SPI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_ECB, SPI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_LBA, SPI_PAD_CTL1);
+
+ mxc_iomux_set_input(MUX_IN_CSPI3_IPP_CSPI_CLK_IN,
+ INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI3_IPP_IND_DATAREADY_B,
+ INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI3_IPP_IND_MISO, INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI3_IPP_IND_MOSI, INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI3_IPP_IND_SS0_B,
+ INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI3_IPP_IND_SS1_B,
+ INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_CSPI3_IPP_IND_SS2_B,
+ INPUT_CTL_PATH1);
+ mxc_iomux_set_input(MUX_IN_CSPI3_IPP_IND_SS3_B,
+ INPUT_CTL_PATH0);
+ break;
+ default:
+ break;
+ }
+#undef SPI_PAD_CTL1
+#undef SPI_PAD_CTL2
+}
+EXPORT_SYMBOL(gpio_spi_active);
+
+/*!
+ * Inactivate a CSPI device
+ *
+ * @param cspi_mod a CSPI device
+ */
+void gpio_spi_inactive(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSPI1_MOSI), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSPI1_MISO), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSPI1_SS0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSPI1_SS1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSPI1_SCLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSPI1_RDY), NULL);
+#ifndef CONFIG_CAN_FLEXCAN /* MX25 3-stack uses this pin for CAN2 */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_C), NULL); /*SS2*/
+#endif
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_VSTBY_ACK), NULL); /*SS3*/
+
+ mxc_free_iomux(MX25_PIN_CSPI1_MOSI, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSPI1_MISO, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSPI1_SS0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSPI1_SS1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSPI1_SCLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSPI1_RDY, MUX_CONFIG_GPIO);
+#ifndef CONFIG_CAN_FLEXCAN /* MX25 3-stack uses this pin for CAN2 */
+ mxc_free_iomux(MX25_PIN_GPIO_C, MUX_CONFIG_GPIO);
+#endif
+ mxc_free_iomux(MX25_PIN_VSTBY_ACK, MUX_CONFIG_GPIO);
+ break;
+ case 1:
+ /* SPI2 */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD12), NULL); /*MOSI*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD13), NULL); /*MISO*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD14), NULL); /*SCLK*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD15), NULL); /*RDY*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_OE_ACD), NULL); /*SS0*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CONTRAST), NULL); /*SS1*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_C), NULL); /*SS2*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_UART2_RTS), NULL); /*SS3*/
+
+ mxc_free_iomux(MX25_PIN_LD12, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD13, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD14, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD15, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_OE_ACD, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CONTRAST, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_GPIO_C, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_UART2_RTS, MUX_CONFIG_GPIO);
+ break;
+ case 2:
+ /* SPI3 */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_EB0), NULL); /*SS0*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_EB1), NULL); /*SS1*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CS4), NULL); /*MOSI*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CS5), NULL); /*MISO*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_ECB), NULL); /*SCLK*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LBA), NULL); /*RDY*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_D), NULL); /*SS2*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D9), NULL); /*SS3*/
+
+ mxc_free_iomux(MX25_PIN_EB0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_EB1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CS4, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CS5, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_ECB, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LBA, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_GPIO_D, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D9, MUX_CONFIG_GPIO);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(gpio_spi_inactive);
+
+/*!
+ * Activate LCD
+ */
+void gpio_lcdc_active(void)
+{
+ mxc_request_iomux(MX25_PIN_LD0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD3, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD4, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD5, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD6, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD7, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD8, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD9, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD10, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD11, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD12, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD13, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD14, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LD15, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_GPIO_E, MUX_CONFIG_ALT2); /*D16*/
+ mxc_request_iomux(MX25_PIN_GPIO_F, MUX_CONFIG_ALT2); /*D17*/
+ mxc_request_iomux(MX25_PIN_HSYNC, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_VSYNC, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_LSCLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_OE_ACD, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CONTRAST, MUX_CONFIG_FUNC);
+
+#define LCD_PAD_CTL (PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD | PAD_CTL_100K_PU)
+ mxc_iomux_set_pad(MX25_PIN_LD0, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD1, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD2, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD3, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD4, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD5, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD6, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD7, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD8, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD9, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD10, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD11, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD12, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD13, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD14, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD15, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_GPIO_E, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_GPIO_F, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_HSYNC, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_VSYNC, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LSCLK, LCD_PAD_CTL | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX25_PIN_OE_ACD, LCD_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CONTRAST, LCD_PAD_CTL);
+}
+EXPORT_SYMBOL(gpio_lcdc_active);
+
+/*!
+ * Inactivate LCD
+ */
+void gpio_lcdc_inactive(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD3), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD4), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD5), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD6), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD7), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_E), NULL); /*D16*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_F), NULL); /*D17*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_HSYNC), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_VSYNC), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LSCLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_OE_ACD), NULL);
+
+ mxc_free_iomux(MX25_PIN_LD0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD3, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD4, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD5, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD6, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD7, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD8, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_LD9, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_LD10, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_LD11, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_LD12, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_LD13, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_LD14, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_LD15, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_GPIO_E, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_GPIO_F, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_HSYNC, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_VSYNC, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LSCLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_OE_ACD, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CONTRAST, MUX_CONFIG_FUNC);
+}
+EXPORT_SYMBOL(gpio_lcdc_inactive);
+
+/*!
+ * Activate SDHC
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_active(int module)
+{
+#define SDHC_PAD_CTL (PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD | \
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST)
+
+ switch (module) {
+ case 0:
+ /* SDHC1 */
+ mxc_request_iomux(MX25_PIN_SD1_CMD,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX25_PIN_SD1_CLK,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX25_PIN_SD1_DATA0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_SD1_DATA1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_SD1_DATA2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_SD1_DATA3, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_A14, MUX_CONFIG_ALT5); /*SD1_WP*/
+ mxc_request_iomux(MX25_PIN_A15, MUX_CONFIG_ALT5); /*SD1_DET*/
+
+ mxc_iomux_set_pad(MX25_PIN_SD1_CMD, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_SD1_CLK, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_SD1_DATA0, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_SD1_DATA1, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_SD1_DATA2, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_SD1_DATA3, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_A14, PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX25_PIN_A15, PAD_CTL_DRV_NORMAL);
+
+ /* Set write protect and card detect gpio as inputs */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_A14), "a14");
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_A15), "a15");
+ gpio_direction_input(IOMUX_TO_GPIO(MX25_PIN_A14)); /*SD1_WP*/
+ gpio_direction_input(IOMUX_TO_GPIO(MX25_PIN_A15)); /*SD1_DET*/
+
+ break;
+ case 1:
+ /* SDHC2 */
+ mxc_request_iomux(MX25_PIN_LD8,
+ MUX_CONFIG_ALT6 | MUX_CONFIG_SION); /*CMD*/
+ mxc_request_iomux(MX25_PIN_LD9,
+ MUX_CONFIG_ALT6 | MUX_CONFIG_SION); /*CLK*/
+ mxc_request_iomux(MX25_PIN_LD10, MUX_CONFIG_ALT6); /*DAT0*/
+ mxc_request_iomux(MX25_PIN_LD11, MUX_CONFIG_ALT6); /*DAT1*/
+ mxc_request_iomux(MX25_PIN_LD12, MUX_CONFIG_ALT6); /*DAT2*/
+ mxc_request_iomux(MX25_PIN_LD13, MUX_CONFIG_ALT6); /*DAT3*/
+
+ mxc_iomux_set_pad(MX25_PIN_LD8, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD9, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD10, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD11, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD12, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_LD13, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D2, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D3, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D4, SDHC_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D5, SDHC_PAD_CTL);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(gpio_sdhc_active);
+
+/*!
+ * Inactivate SDHC
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_inactive(int module)
+{
+ switch (module) {
+ case 0:
+ /* SDHC1 */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_SD1_CMD), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_SD1_CLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_SD1_DATA0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_SD1_DATA1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_SD1_DATA2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_SD1_DATA3), NULL);
+
+ mxc_free_iomux(MX25_PIN_SD1_CMD, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_SD1_CLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_SD1_DATA0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_SD1_DATA1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_SD1_DATA2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_SD1_DATA3, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_A14, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_A15, MUX_CONFIG_GPIO);
+ break;
+ case 1:
+ /* SDHC2 */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD8), NULL); /*CMD*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD9), NULL); /*CLK*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD10), NULL); /*DAT0*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD11), NULL); /*DAT1*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD12), NULL); /*DAT2*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_LD13), NULL); /*DAT3*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D2), NULL); /*DAT4*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D3), NULL); /*DAT5*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D4), NULL); /*DAT6*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D5), NULL); /*DAT7*/
+
+ mxc_free_iomux(MX25_PIN_LD8, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD9, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD10, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD11, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD12, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_LD13, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D3, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D4, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D5, MUX_CONFIG_GPIO);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(gpio_sdhc_inactive);
+
+/*
+ * Probe for the card. If present the GPIO data would be set.
+ */
+unsigned int sdhc_get_card_det_status(struct device *dev)
+{
+ unsigned int ret = 0;
+
+ ret = gpio_get_value(IOMUX_TO_GPIO(MX25_PIN_A15));
+ return ret;
+}
+EXPORT_SYMBOL(sdhc_get_card_det_status);
+
+/*!
+ * Get pin value to detect write protection
+ */
+int sdhc_write_protect(struct device *dev)
+{
+ unsigned int rc = 0;
+
+ rc = gpio_get_value(IOMUX_TO_GPIO(MX25_PIN_A14));
+ return rc;
+}
+EXPORT_SYMBOL(sdhc_write_protect);
+
+/*
+ * USB Host2
+ *
+ * This configuration uses the on-chip FS/LS serial transceiver.
+ * USBPHY2_{DP,DM} pins are not muxed.
+ * We just need to grab USBH2_PWR, USBH2_OC and the Bluetooth/USB
+ * mux control signal.
+ */
+int gpio_usbh2_active(void)
+{
+ if (mxc_request_iomux(MX25_PIN_D9, MUX_CONFIG_ALT6) || /* PWR */
+ mxc_request_iomux(MX25_PIN_D8, MUX_CONFIG_ALT6) || /* OC */
+ mxc_request_iomux(MX25_PIN_A21, MUX_CONFIG_ALT5)) { /* BT_USB_CS */
+ return -EINVAL;
+ }
+
+ /*
+ * This pin controls the mux that switches between
+ * the J18 connector and the on-board bluetooth module.
+ * dir: 0 = out
+ * pin: 0 = J18, 1 = BT
+ */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_A21), "a21");
+ gpio_direction_output(IOMUX_TO_GPIO(MX25_PIN_A21), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_A21), 0);
+
+ return 0;
+}
+EXPORT_SYMBOL(gpio_usbh2_active);
+
+void gpio_usbh2_inactive(void)
+{
+ mxc_free_iomux(MX25_PIN_D9, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_D8, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_A21, MUX_CONFIG_GPIO);
+}
+
+/*
+ * USB OTG UTMI
+ *
+ * This configuration uses the on-chip UTMI transceiver.
+ * USBPHY1_{VBUS,DP,DM,UID,RREF} pins are not muxed.
+ * We just need to grab the USBOTG_PWR and USBOTG_OC pins.
+ */
+int gpio_usbotg_utmi_active(void)
+{
+ if (mxc_request_iomux(MX25_PIN_GPIO_A, MUX_CONFIG_ALT2) || /* PWR */
+ mxc_request_iomux(MX25_PIN_GPIO_B, MUX_CONFIG_ALT2)) { /* OC */
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(gpio_usbotg_utmi_active);
+
+void gpio_usbotg_utmi_inactive(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_A), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_B), NULL);
+
+ mxc_free_iomux(MX25_PIN_GPIO_A, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_GPIO_B, MUX_CONFIG_GPIO);
+}
+EXPORT_SYMBOL(gpio_usbotg_utmi_inactive);
+
+/*!
+ * Activate camera sensor
+ */
+void gpio_sensor_active(void)
+{
+ mxc_request_iomux(MX25_PIN_CSI_D2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_D3, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_D4, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_D5, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_D6, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_D7, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_D8, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_D9, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_HSYNC, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_MCLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_PIXCLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_CSI_VSYNC, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX25_PIN_A19, MUX_CONFIG_ALT5); /*CSI_PWDN*/
+ mxc_request_iomux(MX25_PIN_A20, MUX_CONFIG_ALT5); /*CMOS_RST*/
+
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_A19), "a19");
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_A20), "a20");
+ gpio_direction_output(IOMUX_TO_GPIO(MX25_PIN_A19), 0); /*CSI_PWDN*/
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_A19), 0);
+ gpio_direction_output(IOMUX_TO_GPIO(MX25_PIN_A20), 0); /*CMOS_RST*/
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_A20), 0);
+ mdelay(20);
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_A20), 1);
+
+#define CSI_PAD_CTL1 (PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU)
+#define CSI_PAD_CTL2 (PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | \
+ PAD_CTL_100K_PU)
+
+ mxc_iomux_set_pad(MX25_PIN_CSI_D2, CSI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D3, CSI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D4, CSI_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D5, CSI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D6, CSI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D7, CSI_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D8, CSI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D9, CSI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSI_HSYNC, CSI_PAD_CTL1);
+ mxc_iomux_set_pad(MX25_PIN_CSI_MCLK, PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX25_PIN_CSI_PIXCLK, CSI_PAD_CTL2);
+ mxc_iomux_set_pad(MX25_PIN_CSI_VSYNC, CSI_PAD_CTL1);
+}
+EXPORT_SYMBOL(gpio_sensor_active);
+
+/*!
+ * Inactivate camera sensor
+ */
+void gpio_sensor_inactive(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D3), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D4), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D5), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D6), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D7), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D8), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_D9), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_HSYNC), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_MCLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_PIXCLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_CSI_VSYNC), NULL);
+
+ mxc_free_iomux(MX25_PIN_A19, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_A20, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D3, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D4, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D5, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D6, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D7, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D8, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_D9, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_HSYNC, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_MCLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_PIXCLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_CSI_VSYNC, MUX_CONFIG_GPIO);
+}
+EXPORT_SYMBOL(gpio_sensor_inactive);
+
+/*!
+ * Activate ESAI ports to enable surround sound I/O
+ */
+void gpio_activate_esai_ports(void)
+{
+ mxc_request_iomux(MX25_PIN_CSI_D2, MUX_CONFIG_ALT3); /*SCKR*/
+ mxc_request_iomux(MX25_PIN_CSI_D3, MUX_CONFIG_ALT3); /*FSR*/
+ mxc_request_iomux(MX25_PIN_CSI_D4, MUX_CONFIG_ALT3); /*HCKR*/
+ mxc_request_iomux(MX25_PIN_CSI_D5, MUX_CONFIG_ALT3); /*SCKT*/
+ mxc_request_iomux(MX25_PIN_CSI_D6, MUX_CONFIG_ALT3); /*FST*/
+ mxc_request_iomux(MX25_PIN_CSI_D7, MUX_CONFIG_ALT3); /*HCKT*/
+ mxc_request_iomux(MX25_PIN_CSI_D8, MUX_CONFIG_ALT3); /*TX5_RX0*/
+ mxc_request_iomux(MX25_PIN_CSI_D9, MUX_CONFIG_ALT3); /*TX4_RX1*/
+ mxc_request_iomux(MX25_PIN_CSI_MCLK, MUX_CONFIG_ALT3); /*TX3_RX2*/
+ mxc_request_iomux(MX25_PIN_CSI_VSYNC, MUX_CONFIG_ALT3); /*TX2_RX3*/
+ mxc_request_iomux(MX25_PIN_CSI_HSYNC, MUX_CONFIG_ALT3); /*TX1*/
+ mxc_request_iomux(MX25_PIN_CSI_PIXCLK, MUX_CONFIG_ALT3); /*TX0*/
+
+#define ESAI_PAD_CTL (PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | \
+ PAD_CTL_100K_PU | PAD_CTL_PUE_PUD)
+ mxc_iomux_set_pad(MX25_PIN_CSI_D2, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D3, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D4, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D5, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D6, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D7, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D8, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_D9, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_MCLK, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_VSYNC, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_HSYNC, ESAI_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_CSI_PIXCLK, ESAI_PAD_CTL);
+
+#undef ESAI_PAD_CTL
+}
+EXPORT_SYMBOL(gpio_activate_esai_ports);
+
+/*!
+ * Inactivate ESAI ports to disable surround sound I/O
+ */
+void gpio_deactivate_esai_ports(void)
+{
+ mxc_free_iomux(MX25_PIN_CSI_D2, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_D3, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_D4, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_D5, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_D6, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_D7, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_D8, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_D9, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_MCLK, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_VSYNC, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_HSYNC, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_CSI_PIXCLK, MUX_CONFIG_FUNC);
+}
+EXPORT_SYMBOL(gpio_deactivate_esai_ports);
+
+
+/*!
+ * Activate CAN
+ */
+void gpio_can_active(int id)
+{
+#define CAN_PAD_CTL (PAD_CTL_DRV_3_3V | PAD_CTL_PKE_NONE | PAD_CTL_ODE_CMOS | \
+ PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW)
+#define CAN_PAD_IN_CTL (PAD_CTL_HYS_CMOS | PAD_CTL_PKE_NONE)
+
+ switch (id) {
+ case 0:
+ /* CAN1 */
+ mxc_request_iomux(MX25_PIN_GPIO_A, MUX_CONFIG_ALT6); /*TXCAN*/
+ mxc_request_iomux(MX25_PIN_GPIO_B, MUX_CONFIG_ALT6); /*RXCAN*/
+
+ mxc_iomux_set_pad(MX25_PIN_GPIO_A, CAN_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_GPIO_B, CAN_PAD_IN_CTL);
+
+ mxc_iomux_set_input(MUX_IN_CAN1_IPP_IND_CANRX, INPUT_CTL_PATH1);
+ break;
+ case 1:
+ /* CAN2 */
+ mxc_request_iomux(MX25_PIN_GPIO_C, MUX_CONFIG_ALT6); /*TXCAN*/
+ mxc_request_iomux(MX25_PIN_GPIO_D, MUX_CONFIG_ALT6); /*RXCAN*/
+ mxc_request_iomux(MX25_PIN_D14, MUX_CONFIG_ALT5); /*PWDN*/
+
+ mxc_iomux_set_pad(MX25_PIN_GPIO_C, CAN_PAD_CTL);
+ mxc_iomux_set_pad(MX25_PIN_GPIO_D, CAN_PAD_IN_CTL);
+ mxc_iomux_set_pad(MX25_PIN_D14, CAN_PAD_CTL);
+
+ mxc_iomux_set_input(MUX_IN_CAN2_IPP_IND_CANRX, INPUT_CTL_PATH1);
+
+ /* Configure CAN_PWDN as output */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_D14), "d14");
+ gpio_direction_output(IOMUX_TO_GPIO(MX25_PIN_D14), 0);
+
+ /* Enable input by setting PWDN/TLE6250.INH low (gpio4 bit6) */
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_D14), 0);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(gpio_can_active);
+
+/*!
+ * Inactivate CAN
+ */
+void gpio_can_inactive(int id)
+{
+ switch (id) {
+ case 0:
+ /* CAN1 */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_A), NULL); /*TXCAN*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_B), NULL); /*RXCAN*/
+
+ mxc_free_iomux(MX25_PIN_GPIO_A, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_GPIO_B, MUX_CONFIG_FUNC);
+
+ break;
+ case 1:
+ /* CAN2 */
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_C), NULL); /*TXCAN*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_GPIO_D), NULL); /*RXCAN*/
+
+ mxc_free_iomux(MX25_PIN_GPIO_C, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX25_PIN_GPIO_D, MUX_CONFIG_FUNC);
+
+ /* Disable input by setting PWDN/TLE6250.INH high */
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_D14), 1);
+ mxc_free_iomux(MX25_PIN_D14, MUX_CONFIG_ALT5);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(gpio_can_inactive);
+
+/*!
+ * This function activates DAM port 4 to enable
+ * audio I/O.
+ */
+void gpio_activate_audio_ports(void)
+{
+ mxc_request_iomux(MX25_PIN_EB0, MUX_CONFIG_ALT4); /*SSI4_STXD*/
+ mxc_request_iomux(MX25_PIN_EB1, MUX_CONFIG_ALT4); /*SSI4_SRXD*/
+ mxc_request_iomux(MX25_PIN_RW, MUX_CONFIG_ALT4); /*SSI4_STXFS*/
+ mxc_request_iomux(MX25_PIN_OE, MUX_CONFIG_ALT4); /*SSI4_SCK*/
+ mxc_request_iomux(MX25_PIN_A10, MUX_CONFIG_ALT5); /*HP_DEC*/
+ mxc_request_iomux(MX25_PIN_D13, MUX_CONFIG_ALT5); /*AMP_SHUTDOWN*/
+
+ mxc_iomux_set_pad(MX25_PIN_EB0, PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX25_PIN_EB1, PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX25_PIN_RW, PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX25_PIN_OE, PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX25_PIN_D13, PAD_CTL_DRV_3_3V);
+
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_A10), "a10");
+ gpio_direction_input(IOMUX_TO_GPIO(MX25_PIN_A10));
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_D13), "d13");
+ gpio_direction_output(IOMUX_TO_GPIO(MX25_PIN_D13), 0);
+}
+EXPORT_SYMBOL(gpio_activate_audio_ports);
+
+/*!
+ * This function inactivates DAM port 4 for
+ * audio I/O
+ */
+void gpio_inactive_audio_ports(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_EB0), NULL); /*SSI4_STXD*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_EB1), NULL); /*SSI4_SRXD*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_RW), NULL); /*SSI4_STXFS*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_OE), NULL); /*SSI4_SCK*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_A10), NULL); /*HP_DEC*/
+ gpio_request(IOMUX_TO_GPIO(MX25_PIN_D13), NULL); /*AMP_SHUTDOWN*/
+
+ mxc_free_iomux(MX25_PIN_EB0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_EB1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_RW, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_OE, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_A10, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX25_PIN_D13, MUX_CONFIG_GPIO);
+}
+EXPORT_SYMBOL(gpio_inactive_audio_ports);
+
+int headphone_det_status(void)
+{
+ return gpio_get_value(IOMUX_TO_GPIO(MX25_PIN_A10));
+}
+EXPORT_SYMBOL(headphone_det_status);
+
+void sgtl5000_enable_amp(void)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX25_PIN_D13), 1);
+}
+EXPORT_SYMBOL(sgtl5000_enable_amp);
diff --git a/arch/arm/mach-mx25/mx25_3stack_pmic_mc34704.c b/arch/arm/mach-mx25/mx25_3stack_pmic_mc34704.c
new file mode 100644
index 000000000000..95fbf66fe564
--- /dev/null
+++ b/arch/arm/mach-mx25/mx25_3stack_pmic_mc34704.c
@@ -0,0 +1,127 @@
+/*
+ * mx25-3stack-pmic-mc34704.c -- i.MX25 3STACK Driver for MC34704 PMIC
+ */
+ /*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+ /*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/pmic_external.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/mc34704/core.h>
+#include "iomux.h"
+
+/*
+ * Convenience conversion.
+ * Here atm, maybe there is somewhere better for this.
+ */
+#define mV_to_uV(mV) (mV * 1000)
+#define uV_to_mV(uV) (uV / 1000)
+#define V_to_uV(V) (mV_to_uV(V * 1000))
+#define uV_to_V(uV) (uV_to_mV(uV) / 1000)
+
+struct mc34704;
+
+static struct regulator_init_data rbklt_init = {
+ .constraints = {
+ .name = "REG1_BKLT",
+ .min_uV =
+ mV_to_uV(REG1_V_MV * (1000 + REG1_DVS_MIN_PCT * 10) /
+ 1000),
+ .max_uV =
+ mV_to_uV(REG1_V_MV * (1000 + REG1_DVS_MAX_PCT * 10) /
+ 1000),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data rcpu_init = {
+ .constraints = {
+ .name = "REG2_CPU",
+ .min_uV =
+ mV_to_uV(REG2_V_MV * (1000 + REG2_DVS_MIN_PCT * 10) /
+ 1000),
+ .max_uV =
+ mV_to_uV(REG2_V_MV * (1000 + REG2_DVS_MAX_PCT * 10) /
+ 1000),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data rcore_init = {
+ .constraints = {
+ .name = "REG3_CORE",
+ .min_uV =
+ mV_to_uV(REG3_V_MV * (1000 + REG3_DVS_MIN_PCT * 10) /
+ 1000),
+ .max_uV =
+ mV_to_uV(REG3_V_MV * (1000 + REG3_DVS_MAX_PCT * 10) /
+ 1000),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data rddr_init = {
+ .constraints = {
+ .name = "REG4_DDR",
+ .min_uV =
+ mV_to_uV(REG4_V_MV * (1000 + REG4_DVS_MIN_PCT * 10) /
+ 1000),
+ .max_uV =
+ mV_to_uV(REG4_V_MV * (1000 + REG4_DVS_MAX_PCT * 10) /
+ 1000),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data rpers_init = {
+ .constraints = {
+ .name = "REG5_PERS",
+ .min_uV =
+ mV_to_uV(REG5_V_MV * (1000 + REG5_DVS_MIN_PCT * 10) /
+ 1000),
+ .max_uV =
+ mV_to_uV(REG5_V_MV * (1000 + REG5_DVS_MAX_PCT * 10) /
+ 1000),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static int mc34704_regulator_init(struct mc34704 *mc34704)
+{
+ mc34704_register_regulator(mc34704, MC34704_BKLT, &rbklt_init);
+ mc34704_register_regulator(mc34704, MC34704_CPU, &rcpu_init);
+ mc34704_register_regulator(mc34704, MC34704_CORE, &rcore_init);
+ mc34704_register_regulator(mc34704, MC34704_DDR, &rddr_init);
+ mc34704_register_regulator(mc34704, MC34704_PERS, &rpers_init);
+
+ return 0;
+}
+
+static struct mc34704_platform_data mc34704_plat = {
+ .init = mc34704_regulator_init,
+};
+
+static struct i2c_board_info __initdata mc34704_i2c_device = {
+ .type = "mc34704",
+ .addr = 0x54,
+ .platform_data = &mc34704_plat,
+};
+
+int __init mx25_3stack_init_mc34704(void)
+{
+ return i2c_register_board_info(0, &mc34704_i2c_device, 1);
+}
diff --git a/arch/arm/mach-mx25/mx25_pins.h b/arch/arm/mach-mx25/mx25_pins.h
new file mode 100644
index 000000000000..693eeb5918dd
--- /dev/null
+++ b/arch/arm/mach-mx25/mx25_pins.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MX25_PINS_H__
+#define __ASM_ARCH_MXC_MX25_PINS_H__
+
+/*!
+ * @file arch-mxc/mx25_pins.h
+ *
+ * @brief MX25 I/O Pin List
+ *
+ * @ingroup GPIO_MX25
+ */
+
+#ifndef __ASSEMBLY__
+
+/*!
+ * @name IOMUX/PAD Bit field definitions
+ */
+
+/*! @{ */
+
+/*!
+ * In order to identify pins more effectively, each mux-controlled pin's
+ * enumerated value is constructed in the following way:
+ *
+ * -------------------------------------------------------------------
+ * 31-29 | 28 - 24 |23 - 21| 20 - 10| 9 - 0
+ * -------------------------------------------------------------------
+ * IO_P | IO_I | RSVD | PAD_I | MUX_I
+ * -------------------------------------------------------------------
+ *
+ * Bit 0 to 7 contains MUX_I used to identify the register
+ * offset (base is IOMUX_module_base ) defined in the Section
+ * "sw_pad_ctl & sw_mux_ctl details" of the IC Spec. Similar field
+ * definitions are used for the pad control register. For example,
+ * MX25_PIN_A14 is defined in the enumeration:
+ * ( 0x10 << MUX_I) | ( 0x230 << PAD_I)
+ * So the absolute address is: IOMUX_module_base + 0x10.
+ * The pad control register offset is: 0x230.
+ */
+
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * MUX control register offset
+ */
+#define MUX_I 0
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * PAD control register offset
+ */
+#define PAD_I 10
+
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * reserved filed
+ */
+#define RSVD_I 21
+
+#define NON_GPIO_I 0x7
+#define PIN_TO_MUX_MASK ((1<<(PAD_I - MUX_I)) - 1)
+#define PIN_TO_PAD_MASK ((1<<(RSVD_I - PAD_I)) - 1)
+#define NON_MUX_I PIN_TO_MUX_MASK
+
+#define _MXC_BUILD_PIN(gp, gi, mi, pi) \
+ (((gp) << MUX_IO_P) | ((gi) << MUX_IO_I) | \
+ ((mi) << MUX_I) | ((pi) << PAD_I))
+
+#define _MXC_BUILD_GPIO_PIN(gp, gi, mi, pi) \
+ _MXC_BUILD_PIN(gp, gi, mi, pi)
+
+#define _MXC_BUILD_NON_GPIO_PIN(mi, pi) \
+ _MXC_BUILD_PIN(NON_GPIO_I, 0, mi, pi)
+
+#define PIN_TO_IOMUX_MUX(pin) ((pin >> MUX_I) & PIN_TO_MUX_MASK)
+#define PIN_TO_IOMUX_PAD(pin) ((pin >> PAD_I) & PIN_TO_PAD_MASK)
+
+/*! @} End IOMUX/PAD Bit field definitions */
+
+enum iomux_pins {
+ MX25_PIN_A10 = _MXC_BUILD_GPIO_PIN(3, 0, 0x8, 0x0),
+ MX25_PIN_A13 = _MXC_BUILD_GPIO_PIN(3, 1, 0x0c, 0x22C),
+ MX25_PIN_A14 = _MXC_BUILD_GPIO_PIN(1, 0, 0x10, 0x230),
+ MX25_PIN_A15 = _MXC_BUILD_GPIO_PIN(1, 1, 0x14, 0x234),
+ MX25_PIN_A16 = _MXC_BUILD_GPIO_PIN(1, 2, 0x18, 0x0),
+ MX25_PIN_A17 = _MXC_BUILD_GPIO_PIN(1, 3, 0x1c, 0x238),
+ MX25_PIN_A18 = _MXC_BUILD_GPIO_PIN(1, 4, 0x20, 0x23c),
+ MX25_PIN_A19 = _MXC_BUILD_GPIO_PIN(1, 5, 0x24, 0x240),
+ MX25_PIN_A20 = _MXC_BUILD_GPIO_PIN(1, 6, 0x28, 0x244),
+ MX25_PIN_A21 = _MXC_BUILD_GPIO_PIN(1, 7, 0x2c, 0x248),
+ MX25_PIN_A22 = _MXC_BUILD_GPIO_PIN(1, 8, 0x30, 0x0),
+ MX25_PIN_A23 = _MXC_BUILD_GPIO_PIN(1, 9, 0x34, 0x24c),
+ MX25_PIN_A24 = _MXC_BUILD_GPIO_PIN(1, 10, 0x38, 0x250),
+ MX25_PIN_A25 = _MXC_BUILD_GPIO_PIN(1, 11, 0x3c, 0x254),
+ MX25_PIN_EB0 = _MXC_BUILD_GPIO_PIN(1, 12, 0x40, 0x258),
+ MX25_PIN_EB1 = _MXC_BUILD_GPIO_PIN(1, 13, 0x44, 0x25c),
+ MX25_PIN_OE = _MXC_BUILD_GPIO_PIN(1, 14, 0x48, 0x260),
+ MX25_PIN_CS0 = _MXC_BUILD_GPIO_PIN(3, 2, 0x4c, 0x0),
+ MX25_PIN_CS1 = _MXC_BUILD_GPIO_PIN(3, 3, 0x50, 0x0),
+ MX25_PIN_CS4 = _MXC_BUILD_GPIO_PIN(2, 20, 0x54, 0x264),
+ MX25_PIN_CS5 = _MXC_BUILD_GPIO_PIN(2, 21, 0x58, 0x268),
+ MX25_PIN_NF_CE0 = _MXC_BUILD_GPIO_PIN(2, 22, 0x5c, 0x26c),
+ MX25_PIN_ECB = _MXC_BUILD_GPIO_PIN(2, 23, 0x60, 0x270),
+ MX25_PIN_LBA = _MXC_BUILD_GPIO_PIN(2, 24, 0x64, 0x274),
+ MX25_PIN_BCLK = _MXC_BUILD_GPIO_PIN(3, 4, 0x68, 0x0),
+ MX25_PIN_RW = _MXC_BUILD_GPIO_PIN(2, 25, 0x6c, 0x278),
+ MX25_PIN_NFWE_B = _MXC_BUILD_GPIO_PIN(2, 26, 0x70, 0x0),
+ MX25_PIN_NFRE_B = _MXC_BUILD_GPIO_PIN(2, 27, 0x74, 0x0),
+ MX25_PIN_NFALE = _MXC_BUILD_GPIO_PIN(2, 28, 0x78, 0x0),
+ MX25_PIN_NFCLE = _MXC_BUILD_GPIO_PIN(2, 29, 0x7c, 0x0),
+ MX25_PIN_NFWP_B = _MXC_BUILD_GPIO_PIN(2, 30, 0x80, 0x0),
+ MX25_PIN_NFRB = _MXC_BUILD_GPIO_PIN(2, 31, 0x84, 0x27c),
+ MX25_PIN_D15 = _MXC_BUILD_GPIO_PIN(3, 5, 0x88, 0x280),
+ MX25_PIN_D14 = _MXC_BUILD_GPIO_PIN(3, 6, 0x8c, 0x284),
+ MX25_PIN_D13 = _MXC_BUILD_GPIO_PIN(3, 7, 0x90, 0x288),
+ MX25_PIN_D12 = _MXC_BUILD_GPIO_PIN(3, 8, 0x94, 0x28c),
+ MX25_PIN_D11 = _MXC_BUILD_GPIO_PIN(3, 9, 0x98, 0x290),
+ MX25_PIN_D10 = _MXC_BUILD_GPIO_PIN(3, 10, 0x9c, 0x294),
+ MX25_PIN_D9 = _MXC_BUILD_GPIO_PIN(3, 11, 0xa0, 0x298),
+ MX25_PIN_D8 = _MXC_BUILD_GPIO_PIN(3, 12, 0xa4, 0x29c),
+ MX25_PIN_D7 = _MXC_BUILD_GPIO_PIN(3, 13, 0xa8, 0x2a0),
+ MX25_PIN_D6 = _MXC_BUILD_GPIO_PIN(3, 14, 0xac, 0x2a4),
+ MX25_PIN_D5 = _MXC_BUILD_GPIO_PIN(3, 15, 0xb0, 0x2a8),
+ MX25_PIN_D4 = _MXC_BUILD_GPIO_PIN(3, 16, 0xb4, 0x2ac),
+ MX25_PIN_D3 = _MXC_BUILD_GPIO_PIN(3, 17, 0xb8, 0x2b0),
+ MX25_PIN_D2 = _MXC_BUILD_GPIO_PIN(3, 18, 0xbc, 0x2b4),
+ MX25_PIN_D1 = _MXC_BUILD_GPIO_PIN(3, 19, 0xc0, 0x2b8),
+ MX25_PIN_D0 = _MXC_BUILD_GPIO_PIN(3, 20, 0xc4, 0x2bc),
+ MX25_PIN_LD0 = _MXC_BUILD_GPIO_PIN(1, 15, 0xc8, 0x2c0),
+ MX25_PIN_LD1 = _MXC_BUILD_GPIO_PIN(1, 16, 0xcc, 0x2c4),
+ MX25_PIN_LD2 = _MXC_BUILD_GPIO_PIN(1, 17, 0xd0, 0x2c8),
+ MX25_PIN_LD3 = _MXC_BUILD_GPIO_PIN(1, 18, 0xd4, 0x2cc),
+ MX25_PIN_LD4 = _MXC_BUILD_GPIO_PIN(1, 19, 0xd8, 0x2d0),
+ MX25_PIN_LD5 = _MXC_BUILD_GPIO_PIN(0, 19, 0xdc, 0x2d4),
+ MX25_PIN_LD6 = _MXC_BUILD_GPIO_PIN(0, 20, 0xe0, 0x2d8),
+ MX25_PIN_LD7 = _MXC_BUILD_GPIO_PIN(0, 21, 0xe4, 0x2dc),
+ MX25_PIN_LD8 = _MXC_BUILD_NON_GPIO_PIN(0xe8, 0x2e0),
+ MX25_PIN_LD9 = _MXC_BUILD_NON_GPIO_PIN(0xec, 0x2e4),
+ MX25_PIN_LD10 = _MXC_BUILD_NON_GPIO_PIN(0xf0, 0x2e8),
+ MX25_PIN_LD11 = _MXC_BUILD_NON_GPIO_PIN(0xf4, 0x2ec),
+ MX25_PIN_LD12 = _MXC_BUILD_NON_GPIO_PIN(0xf8, 0x2f0),
+ MX25_PIN_LD13 = _MXC_BUILD_NON_GPIO_PIN(0xfc, 0x2f4),
+ MX25_PIN_LD14 = _MXC_BUILD_NON_GPIO_PIN(0x100, 0x2f8),
+ MX25_PIN_LD15 = _MXC_BUILD_NON_GPIO_PIN(0x104, 0x2fc),
+ MX25_PIN_HSYNC = _MXC_BUILD_GPIO_PIN(0, 22, 0x108, 0x300),
+ MX25_PIN_VSYNC = _MXC_BUILD_GPIO_PIN(0, 23, 0x10c, 0x304),
+ MX25_PIN_LSCLK = _MXC_BUILD_GPIO_PIN(0, 24, 0x110, 0x308),
+ MX25_PIN_OE_ACD = _MXC_BUILD_GPIO_PIN(0, 25, 0x114, 0x30c),
+ MX25_PIN_CONTRAST = _MXC_BUILD_NON_GPIO_PIN(0x118, 0x310),
+ MX25_PIN_PWM = _MXC_BUILD_GPIO_PIN(0, 26, 0x11c, 0x314),
+ MX25_PIN_CSI_D2 = _MXC_BUILD_GPIO_PIN(0, 27, 0x120, 0x318),
+ MX25_PIN_CSI_D3 = _MXC_BUILD_GPIO_PIN(0, 28, 0x124, 0x31c),
+ MX25_PIN_CSI_D4 = _MXC_BUILD_GPIO_PIN(0, 29, 0x128, 0x320),
+ MX25_PIN_CSI_D5 = _MXC_BUILD_GPIO_PIN(0, 30, 0x12c, 0x324),
+ MX25_PIN_CSI_D6 = _MXC_BUILD_GPIO_PIN(0, 31, 0x130, 0x328),
+ MX25_PIN_CSI_D7 = _MXC_BUILD_GPIO_PIN(0, 6, 0x134, 0x32c),
+ MX25_PIN_CSI_D8 = _MXC_BUILD_GPIO_PIN(0, 7, 0x138, 0x330),
+ MX25_PIN_CSI_D9 = _MXC_BUILD_GPIO_PIN(3, 21, 0x13c, 0x334),
+ MX25_PIN_CSI_MCLK = _MXC_BUILD_GPIO_PIN(0, 8, 0x140, 0x338),
+ MX25_PIN_CSI_VSYNC = _MXC_BUILD_GPIO_PIN(0, 9, 0x144, 0x33c),
+ MX25_PIN_CSI_HSYNC = _MXC_BUILD_GPIO_PIN(0, 10, 0x148, 0x340),
+ MX25_PIN_CSI_PIXCLK = _MXC_BUILD_GPIO_PIN(0, 11, 0x14c, 0x344),
+ MX25_PIN_I2C1_CLK = _MXC_BUILD_GPIO_PIN(0, 12, 0x150, 0x348),
+ MX25_PIN_I2C1_DAT = _MXC_BUILD_GPIO_PIN(0, 13, 0x154, 0x34c),
+ MX25_PIN_CSPI1_MOSI = _MXC_BUILD_GPIO_PIN(0, 14, 0x158, 0x350),
+ MX25_PIN_CSPI1_MISO = _MXC_BUILD_GPIO_PIN(0, 15, 0x15c, 0x354),
+ MX25_PIN_CSPI1_SS0 = _MXC_BUILD_GPIO_PIN(0, 16, 0x160, 0x358),
+ MX25_PIN_CSPI1_SS1 = _MXC_BUILD_GPIO_PIN(0, 17, 0x164, 0x35c),
+ MX25_PIN_CSPI1_SCLK = _MXC_BUILD_GPIO_PIN(0, 18, 0x168, 0x360),
+ MX25_PIN_CSPI1_RDY = _MXC_BUILD_GPIO_PIN(1, 22, 0x16c, 0x364),
+ MX25_PIN_UART1_RXD = _MXC_BUILD_GPIO_PIN(3, 22, 0x170, 0x368),
+ MX25_PIN_UART1_TXD = _MXC_BUILD_GPIO_PIN(3, 23, 0x174, 0x36c),
+ MX25_PIN_UART1_RTS = _MXC_BUILD_GPIO_PIN(3, 24, 0x178, 0x370),
+ MX25_PIN_UART1_CTS = _MXC_BUILD_GPIO_PIN(3, 25, 0x17c, 0x374),
+ MX25_PIN_UART2_RXD = _MXC_BUILD_GPIO_PIN(3, 26, 0x180, 0x378),
+ MX25_PIN_UART2_TXD = _MXC_BUILD_GPIO_PIN(3, 27, 0x184, 0x37c),
+ MX25_PIN_UART2_RTS = _MXC_BUILD_GPIO_PIN(3, 28, 0x188, 0x380),
+ MX25_PIN_UART2_CTS = _MXC_BUILD_GPIO_PIN(3, 29, 0x18c, 0x384),
+ MX25_PIN_SD1_CMD = _MXC_BUILD_GPIO_PIN(1, 23, 0x190, 0x388),
+ MX25_PIN_SD1_CLK = _MXC_BUILD_GPIO_PIN(1, 24, 0x194, 0x38c),
+ MX25_PIN_SD1_DATA0 = _MXC_BUILD_GPIO_PIN(1, 25, 0x198, 0x390),
+ MX25_PIN_SD1_DATA1 = _MXC_BUILD_GPIO_PIN(1, 26, 0x19c, 0x394),
+ MX25_PIN_SD1_DATA2 = _MXC_BUILD_GPIO_PIN(1, 27, 0x1a0, 0x398),
+ MX25_PIN_SD1_DATA3 = _MXC_BUILD_GPIO_PIN(1, 28, 0x1a4, 0x39c),
+ MX25_PIN_KPP_ROW0 = _MXC_BUILD_GPIO_PIN(1, 29, 0x1a8, 0x3a0),
+ MX25_PIN_KPP_ROW1 = _MXC_BUILD_GPIO_PIN(1, 30, 0x1ac, 0x3a4),
+ MX25_PIN_KPP_ROW2 = _MXC_BUILD_GPIO_PIN(1, 31, 0x1b0, 0x3a8),
+ MX25_PIN_KPP_ROW3 = _MXC_BUILD_GPIO_PIN(2, 0, 0x1b4, 0x3ac),
+ MX25_PIN_KPP_COL0 = _MXC_BUILD_GPIO_PIN(2, 1, 0x1b8, 0x3b0),
+ MX25_PIN_KPP_COL1 = _MXC_BUILD_GPIO_PIN(2, 2, 0x1bc, 0x3b4),
+ MX25_PIN_KPP_COL2 = _MXC_BUILD_GPIO_PIN(2, 3, 0x1c0, 0x3b8),
+ MX25_PIN_KPP_COL3 = _MXC_BUILD_GPIO_PIN(2, 4, 0x1c4, 0x3bc),
+ MX25_PIN_FEC_MDC = _MXC_BUILD_GPIO_PIN(2, 5, 0x1c8, 0x3c0),
+ MX25_PIN_FEC_MDIO = _MXC_BUILD_GPIO_PIN(2, 6, 0x1cc, 0x3c4),
+ MX25_PIN_FEC_TDATA0 = _MXC_BUILD_GPIO_PIN(2, 7, 0x1d0, 0x3c8),
+ MX25_PIN_FEC_TDATA1 = _MXC_BUILD_GPIO_PIN(2, 8, 0x1d4, 0x3cc),
+ MX25_PIN_FEC_TX_EN = _MXC_BUILD_GPIO_PIN(2, 9, 0x1d8, 0x3d0),
+ MX25_PIN_FEC_RDATA0 = _MXC_BUILD_GPIO_PIN(2, 10, 0x1dc, 0x3d4),
+ MX25_PIN_FEC_RDATA1 = _MXC_BUILD_GPIO_PIN(2, 11, 0x1e0, 0x3d8),
+ MX25_PIN_FEC_RX_DV = _MXC_BUILD_GPIO_PIN(2, 12, 0x1e4, 0x3dc),
+ MX25_PIN_FEC_TX_CLK = _MXC_BUILD_GPIO_PIN(2, 13, 0x1e8, 0x3e0),
+ MX25_PIN_RTCK = _MXC_BUILD_GPIO_PIN(2, 14, 0x1ec, 0x3e4),
+ MX25_PIN_DE_B = _MXC_BUILD_GPIO_PIN(1, 20, 0x1f0, 0x3ec),
+ MX25_PIN_TDO = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x3e8),
+ MX25_PIN_GPIO_A = _MXC_BUILD_GPIO_PIN(0, 0, 0x1f4, 0x3f0),
+ MX25_PIN_GPIO_B = _MXC_BUILD_GPIO_PIN(0, 1, 0x1f8, 0x3f4),
+ MX25_PIN_GPIO_C = _MXC_BUILD_GPIO_PIN(0, 2, 0x1fc, 0x3f8),
+ MX25_PIN_GPIO_D = _MXC_BUILD_GPIO_PIN(0, 3, 0x200, 0x3fc),
+ MX25_PIN_GPIO_E = _MXC_BUILD_GPIO_PIN(0, 4, 0x204, 0x400),
+ MX25_PIN_GPIO_F = _MXC_BUILD_GPIO_PIN(0, 5, 0x208, 0x404),
+ MX25_PIN_EXT_ARMCLK = _MXC_BUILD_GPIO_PIN(2, 15, 0x20c, 0x0),
+ MX25_PIN_UPLL_BYPCLK = _MXC_BUILD_GPIO_PIN(2, 16, 0x210, 0x0),
+ MX25_PIN_VSTBY_REQ = _MXC_BUILD_GPIO_PIN(2, 17, 0x214, 0x408),
+ MX25_PIN_VSTBY_ACK = _MXC_BUILD_GPIO_PIN(2, 18, 0x218, 0x40c),
+ MX25_PIN_POWER_FAIL = _MXC_BUILD_GPIO_PIN(2, 19, 0x21c, 0x410),
+ MX25_PIN_CLKO = _MXC_BUILD_GPIO_PIN(1, 21, 0x220, 0x414),
+ MX25_PIN_BOOT_MODE0 = _MXC_BUILD_GPIO_PIN(3, 30, 0x224, 0x0),
+ MX25_PIN_BOOT_MODE1 = _MXC_BUILD_GPIO_PIN(3, 31, 0x228, 0x0),
+
+ MX25_PIN_CTL_GRP_DVS_MISC = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x418),
+ MX25_PIN_CTL_GRP_DSE_FEC = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x41c),
+ MX25_PIN_CTL_GRP_DVS_JTAG = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x420),
+ MX25_PIN_CTL_GRP_DSE_NFC = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x424),
+ MX25_PIN_CTL_GRP_DSE_CSI = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x428),
+ MX25_PIN_CTL_GRP_DSE_WEIM = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x42c),
+ MX25_PIN_CTL_GRP_DSE_DDR = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x430),
+ MX25_PIN_CTL_GRP_DVS_CRM = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x434),
+ MX25_PIN_CTL_GRP_DSE_KPP = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x438),
+ MX25_PIN_CTL_GRP_DSE_SDHC1 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x43c),
+ MX25_PIN_CTL_GRP_DSE_LCD = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x440),
+ MX25_PIN_CTL_GRP_DSE_UART = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x444),
+ MX25_PIN_CTL_GRP_DVS_NFC = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x448),
+ MX25_PIN_CTL_GRP_DVS_CSI = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x44c),
+ MX25_PIN_CTL_GRP_DSE_CSPI1 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x450),
+ MX25_PIN_CTL_GRP_DDRTYPE = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x454),
+ MX25_PIN_CTL_GRP_DVS_SDHC1 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x458),
+ MX25_PIN_CTL_GRP_DVS_LCD = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x45c)
+};
+
+#endif
+#endif
diff --git a/arch/arm/mach-mx25/pm.c b/arch/arm/mach-mx25/pm.c
new file mode 100644
index 000000000000..4a053d0d639b
--- /dev/null
+++ b/arch/arm/mach-mx25/pm.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+#include <linux/io.h>
+#include "crm_regs.h"
+
+/*!
+ * @defgroup MSL_MX25 i.MX25 Machine Specific Layer (MSL)
+ */
+
+/*!
+ * @file mach-mx25/pm.c
+ * @brief This file contains suspend operations
+ *
+ * @ingroup MSL_MX25
+ */
+static unsigned int cgcr0, cgcr1, cgcr2;
+
+static int mx25_suspend_enter(suspend_state_t state)
+{
+ unsigned int reg;
+
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ mxc_cpu_lp_set(STOP_POWER_OFF);
+ break;
+ case PM_SUSPEND_STANDBY:
+ mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* Executing CP15 (Wait-for-Interrupt) Instruction */
+ cpu_do_idle();
+
+ reg = (__raw_readl(MXC_CCM_CGCR0) & ~MXC_CCM_CGCR0_STOP_MODE_MASK) |
+ cgcr0;
+ __raw_writel(reg, MXC_CCM_CGCR0);
+
+ reg = (__raw_readl(MXC_CCM_CGCR1) & ~MXC_CCM_CGCR1_STOP_MODE_MASK) |
+ cgcr1;
+ __raw_writel(reg, MXC_CCM_CGCR1);
+
+ reg = (__raw_readl(MXC_CCM_CGCR2) & ~MXC_CCM_CGCR2_STOP_MODE_MASK) |
+ cgcr2;
+ __raw_writel(reg, MXC_CCM_CGCR2);
+
+ return 0;
+}
+
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+static int mx25_suspend_prepare(void)
+{
+ cgcr0 = __raw_readl(MXC_CCM_CGCR0) & MXC_CCM_CGCR0_STOP_MODE_MASK;
+ cgcr1 = __raw_readl(MXC_CCM_CGCR1) & MXC_CCM_CGCR1_STOP_MODE_MASK;
+ cgcr2 = __raw_readl(MXC_CCM_CGCR2) & MXC_CCM_CGCR2_STOP_MODE_MASK;
+
+ return 0;
+}
+
+/*
+ * Called after devices are re-setup, but before processes are thawed.
+ */
+static void mx25_suspend_finish(void)
+{
+}
+
+static int mx25_pm_valid(suspend_state_t state)
+{
+ return state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX;
+}
+
+struct platform_suspend_ops mx25_suspend_ops = {
+ .valid = mx25_pm_valid,
+ .prepare = mx25_suspend_prepare,
+ .enter = mx25_suspend_enter,
+ .finish = mx25_suspend_finish,
+};
+
+static int __init mx25_pm_init(void)
+{
+ pr_info("Static Power Management for Freescale i.MX25\n");
+ suspend_set_ops(&mx25_suspend_ops);
+
+ return 0;
+}
+
+late_initcall(mx25_pm_init);
diff --git a/arch/arm/mach-mx25/sdma_script_code.h b/arch/arm/mach-mx25/sdma_script_code.h
new file mode 100644
index 000000000000..86789dd2f353
--- /dev/null
+++ b/arch/arm/mach-mx25/sdma_script_code.h
@@ -0,0 +1,159 @@
+
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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
+ */
+
+/*!
+ * @file sdma_script_code.h
+ * @brief This file contains functions of SDMA scripts code initialization
+ *
+ * The file was generated automatically. Based on sdma scripts library.
+ *
+ * @ingroup SDMA
+ */
+/************************************************************************
+
+ SDMA RELEASE LABEL: "SS15_SENNA"
+
+************************************************************************/
+
+#ifndef SDMA_SCRIPT_CODE_H
+#define SDMA_SCRIPT_CODE_H
+
+/*!
+ * SDMA ROM scripts start addresses and sizes
+ */
+#define start_ADDR 0
+#define start_SIZE 22
+
+#define core_ADDR 80
+#define core_SIZE 233
+
+#define common_ADDR 313
+#define common_SIZE 416
+
+#define ap_2_ap_ADDR 729
+#define ap_2_ap_SIZE 41
+
+#define app_2_mcu_ADDR 770
+#define app_2_mcu_SIZE 64
+
+#define mcu_2_app_ADDR 834
+#define mcu_2_app_SIZE 70
+
+#define uart_2_mcu_ADDR 904
+#define uart_2_mcu_SIZE 75
+
+#define shp_2_mcu_ADDR 979
+#define shp_2_mcu_SIZE 69
+
+#define mcu_2_shp_ADDR 1048
+#define mcu_2_shp_SIZE 72
+
+#define uartsh_2_mcu_ADDR 1120
+#define uartsh_2_mcu_SIZE 69
+
+#define app_2_per_ADDR 1189
+#define app_2_per_SIZE 66
+
+#define per_2_app_ADDR 1255
+#define per_2_app_SIZE 74
+
+#define per_2_shp_ADDR 1329
+#define per_2_shp_SIZE 78
+
+#define shp_2_per_ADDR 1407
+#define shp_2_per_SIZE 72
+
+#define mcu_2_ata_ADDR 1479
+#define mcu_2_ata_SIZE 81
+
+#define ata_2_mcu_ADDR 1560
+#define ata_2_mcu_SIZE 96
+
+#define loop_DMAs_routines_ADDR 1656
+#define loop_DMAs_routines_SIZE 227
+
+#define test_ADDR 1883
+#define test_SIZE 63
+
+#define signature_ADDR 1022
+#define signature_SIZE 1
+
+/*!
+ * SDMA RAM scripts start addresses and sizes
+ */
+#define ext_mem__ipu_ram_ADDR 6144
+#define ext_mem__ipu_ram_SIZE 123
+
+#define uart_2_per_ADDR 6267
+#define uart_2_per_SIZE 73
+
+#define uartsh_2_per_ADDR 6340
+#define uartsh_2_per_SIZE 67
+
+/*!
+ * SDMA RAM image start address and size
+ */
+#define RAM_CODE_START_ADDR 6144
+#define RAM_CODE_SIZE 263
+
+/*!
+ * Buffer that holds the SDMA RAM image
+ */
+__attribute__ ((__aligned__(4)))
+#ifndef CONFIG_XIP_KERNEL
+const
+#endif
+static const short sdma_code[] = {
+ 0x0e70, 0x0611, 0x5616, 0xc18a, 0x7d2a, 0x5ade, 0x008e, 0xc19c,
+ 0x7c26, 0x5be0, 0x5ef0, 0x5ce8, 0x0688, 0x08ff, 0x0011, 0x28ff,
+ 0x00bc, 0x53f6, 0x05df, 0x7d0b, 0x6dc5, 0x03df, 0x7d03, 0x6bd5,
+ 0xd84f, 0x982b, 0x6b05, 0xc6d8, 0x7e27, 0x7f29, 0x982b, 0x6d01,
+ 0x03df, 0x7d05, 0x6bd5, 0xc702, 0x7e18, 0x7f1a, 0x982b, 0x6b05,
+ 0xc678, 0x7e07, 0x7f06, 0x52de, 0x53e6, 0xc1a8, 0x7dd7, 0x0200,
+ 0x9803, 0x0007, 0x6004, 0x680c, 0x53f6, 0x028e, 0x00a3, 0xc2ad,
+ 0x048b, 0x0498, 0x0454, 0x068a, 0x982b, 0x0207, 0x680c, 0x6ddf,
+ 0x0107, 0x68ff, 0x60d0, 0x9834, 0x0207, 0x68ff, 0x6d28, 0x0107,
+ 0x6004, 0x680c, 0x9834, 0x0007, 0x68ff, 0x60d0, 0x9834, 0x0288,
+ 0x03a5, 0x3b03, 0x3d03, 0x4d00, 0x7d0a, 0x0804, 0x00a5, 0x00da,
+ 0x7d1a, 0x02a0, 0x7b01, 0x65d8, 0x7eee, 0x65ff, 0x7eec, 0x0804,
+ 0x02d0, 0x7d11, 0x4b00, 0x7c0f, 0x008a, 0x3003, 0x6dcf, 0x6bdf,
+ 0x0015, 0x0015, 0x7b02, 0x65d8, 0x0000, 0x7edd, 0x63ff, 0x7edb,
+ 0x3a03, 0x6dcd, 0x6bdd, 0x008a, 0x7b02, 0x65d8, 0x0000, 0x7ed3,
+ 0x65ff, 0x7ed1, 0x0006, 0xc23a, 0x57db, 0x52f3, 0x6ad5, 0x56fb,
+ 0x028e, 0x1a94, 0x6ac3, 0x62c8, 0x0269, 0x7d1e, 0x1e94, 0x6ee3,
+ 0x62d0, 0x5aeb, 0x62c8, 0x0248, 0x6ed3, 0x6ac8, 0x2694, 0x52eb,
+ 0x6ad5, 0x6ee3, 0x62c8, 0x026e, 0x7d27, 0x6ac8, 0x7f23, 0x2501,
+ 0x4d00, 0x7d26, 0x028e, 0x1a98, 0x6ac3, 0x62c8, 0x6ec3, 0x0260,
+ 0x7df1, 0x62d0, 0xc2d1, 0x98c0, 0x6ee3, 0x008f, 0x2001, 0x00d5,
+ 0x7d01, 0x008d, 0x05a0, 0x62c8, 0x026e, 0x7d0e, 0x6ac8, 0x7f0a,
+ 0x2001, 0x7cf9, 0x6add, 0x7f06, 0x0000, 0x4d00, 0x7d09, 0xc251,
+ 0x57db, 0x987f, 0x0007, 0x6aff, 0x62d0, 0xc2d1, 0x0458, 0x0454,
+ 0x6add, 0x7ff8, 0xc261, 0x987c, 0xc230, 0xc23a, 0x57db, 0x52f3,
+ 0x6ad5, 0x56fb, 0x028e, 0x1a94, 0x5202, 0x0269, 0x7d17, 0x1e94,
+ 0x5206, 0x0248, 0x5a06, 0x2694, 0x5206, 0x026e, 0x7d26, 0x6ac8,
+ 0x7f22, 0x2501, 0x4d00, 0x7d27, 0x028e, 0x1a98, 0x5202, 0x0260,
+ 0x7df3, 0x6add, 0x7f18, 0x62d0, 0xc2d1, 0x9903, 0x008f, 0x2001,
+ 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x5206, 0x026e, 0x7d0e, 0x6ac8,
+ 0x7f0a, 0x2001, 0x7cf9, 0x6add, 0x7f06, 0x0000, 0x4d00, 0x7d0b,
+ 0xc251, 0x57db, 0x98c9, 0x0007, 0x6aff, 0x6add, 0x7ffc, 0x62d0,
+ 0xc2d1, 0x0458, 0x0454, 0x6add, 0x7ff6, 0xc261, 0x98c6
+};
+#endif
diff --git a/arch/arm/mach-mx25/serial.c b/arch/arm/mach-mx25/serial.c
new file mode 100644
index 000000000000..d71dd1082f2f
--- /dev/null
+++ b/arch/arm/mach-mx25/serial.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @file mach-mx25/serial.c
+ *
+ * @brief This file contains the UART initiliazation.
+ *
+ * @ingroup MSL_MX25
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <mach/hardware.h>
+#include <mach/mxc_uart.h>
+#include <mach/spba.h>
+#include "serial.h"
+#include "board-mx25_3stack.h"
+
+#if defined(CONFIG_SERIAL_MXC) || defined(CONFIG_SERIAL_MXC_MODULE)
+
+/*!
+ * This is an array where each element holds information about a UART port,
+ * like base address of the UART, interrupt numbers etc. This structure is
+ * passed to the serial_core.c file. Based on which UART is used, the core file
+ * passes back the appropriate port structure as an argument to the control
+ * functions.
+ */
+static uart_mxc_port mxc_ports[] = {
+ [0] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART1_BASE_ADDR),
+ .mapbase = UART1_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART1_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 0,
+ },
+ .ints_muxed = UART1_MUX_INTS,
+ .irqs = {UART1_INT2, UART1_INT3},
+ .mode = UART1_MODE,
+ .ir_mode = UART1_IR,
+ .enabled = UART1_ENABLED,
+ .hardware_flow = UART1_HW_FLOW,
+ .cts_threshold = UART1_UCR4_CTSTL,
+ .dma_enabled = UART1_DMA_ENABLE,
+ .dma_rxbuf_size = UART1_DMA_RXBUFSIZE,
+ .rx_threshold = UART1_UFCR_RXTL,
+ .tx_threshold = UART1_UFCR_TXTL,
+ .shared = UART1_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART1_TX,
+ .dma_rx_id = MXC_DMA_UART1_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ },
+ [1] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART2_BASE_ADDR),
+ .mapbase = UART2_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART2_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 1,
+ },
+ .ints_muxed = UART2_MUX_INTS,
+ .irqs = {UART2_INT2, UART2_INT3},
+ .mode = UART2_MODE,
+ .ir_mode = UART2_IR,
+ .enabled = UART2_ENABLED,
+ .hardware_flow = UART2_HW_FLOW,
+ .cts_threshold = UART2_UCR4_CTSTL,
+ .dma_enabled = UART2_DMA_ENABLE,
+ .dma_rxbuf_size = UART2_DMA_RXBUFSIZE,
+ .rx_threshold = UART2_UFCR_RXTL,
+ .tx_threshold = UART2_UFCR_TXTL,
+ .shared = UART2_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART2_TX,
+ .dma_rx_id = MXC_DMA_UART2_RX,
+ .rxd_mux = MXC_UART_IR_RXDMUX,
+ },
+#if UART3_ENABLED == 1
+ [2] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART3_BASE_ADDR),
+ .mapbase = UART3_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART3_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 2,
+ },
+ .ints_muxed = UART3_MUX_INTS,
+ .irqs = {UART3_INT2, UART3_INT3},
+ .mode = UART3_MODE,
+ .ir_mode = UART3_IR,
+ .enabled = UART3_ENABLED,
+ .hardware_flow = UART3_HW_FLOW,
+ .cts_threshold = UART3_UCR4_CTSTL,
+ .dma_enabled = UART3_DMA_ENABLE,
+ .dma_rxbuf_size = UART3_DMA_RXBUFSIZE,
+ .rx_threshold = UART3_UFCR_RXTL,
+ .tx_threshold = UART3_UFCR_TXTL,
+ .shared = UART3_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART3_TX,
+ .dma_rx_id = MXC_DMA_UART3_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ },
+#endif
+#if UART4_ENABLED == 1
+ [3] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART4_BASE_ADDR),
+ .mapbase = UART4_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART4_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 3,
+ },
+ .ints_muxed = UART4_MUX_INTS,
+ .irqs = {UART4_INT2, UART4_INT3},
+ .mode = UART4_MODE,
+ .ir_mode = UART4_IR,
+ .enabled = UART4_ENABLED,
+ .hardware_flow = UART4_HW_FLOW,
+ .cts_threshold = UART4_UCR4_CTSTL,
+ .dma_enabled = UART4_DMA_ENABLE,
+ .dma_rxbuf_size = UART4_DMA_RXBUFSIZE,
+ .rx_threshold = UART4_UFCR_RXTL,
+ .tx_threshold = UART4_UFCR_TXTL,
+ .shared = UART4_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART4_TX,
+ .dma_rx_id = MXC_DMA_UART4_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ },
+#endif
+#if UART5_ENABLED == 1
+ [4] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART5_BASE_ADDR),
+ .mapbase = UART5_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART5_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 4,
+ },
+ .ints_muxed = UART5_MUX_INTS,
+ .irqs = {UART5_INT2, UART5_INT3},
+ .mode = UART5_MODE,
+ .ir_mode = UART5_IR,
+ .enabled = UART5_ENABLED,
+ .hardware_flow = UART5_HW_FLOW,
+ .cts_threshold = UART5_UCR4_CTSTL,
+ .dma_enabled = UART5_DMA_ENABLE,
+ .dma_rxbuf_size = UART5_DMA_RXBUFSIZE,
+ .rx_threshold = UART5_UFCR_RXTL,
+ .tx_threshold = UART5_UFCR_TXTL,
+ .shared = UART5_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART5_TX,
+ .dma_rx_id = MXC_DMA_UART5_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ },
+#endif
+};
+
+static struct platform_device mxc_uart_device1 = {
+ .name = "mxcintuart",
+ .id = 0,
+ .dev = {
+ .platform_data = &mxc_ports[0],
+ },
+};
+
+static struct platform_device mxc_uart_device2 = {
+ .name = "mxcintuart",
+ .id = 1,
+ .dev = {
+ .platform_data = &mxc_ports[1],
+ },
+};
+
+#if UART3_ENABLED == 1
+static struct platform_device mxc_uart_device3 = {
+ .name = "mxcintuart",
+ .id = 2,
+ .dev = {
+ .platform_data = &mxc_ports[2],
+ },
+};
+#endif
+#if UART4_ENABLED == 1
+static struct platform_device mxc_uart_device4 = {
+ .name = "mxcintuart",
+ .id = 3,
+ .dev = {
+ .platform_data = &mxc_ports[3],
+ },
+};
+#endif
+#if UART5_ENABLED == 1
+static struct platform_device mxc_uart_device5 = {
+ .name = "mxcintuart",
+ .id = 4,
+ .dev = {
+ .platform_data = &mxc_ports[4],
+ },
+};
+#endif
+
+static int __init mxc_init_uart(void)
+{
+ /* Register all the MXC UART platform device structures */
+ platform_device_register(&mxc_uart_device1);
+ platform_device_register(&mxc_uart_device2);
+
+ /* Grab ownership of shared UARTs 3 and 4, only when enabled */
+#if UART3_ENABLED == 1
+#if UART3_DMA_ENABLE == 1
+ spba_take_ownership(UART3_SHARED_PERI, (SPBA_MASTER_A | SPBA_MASTER_C));
+#else
+ spba_take_ownership(UART3_SHARED_PERI, SPBA_MASTER_A);
+#endif /* UART3_DMA_ENABLE */
+ platform_device_register(&mxc_uart_device3);
+#endif /* UART3_ENABLED */
+#if UART4_ENABLED == 1
+#if UART4_DMA_ENABLE == 1
+ spba_take_ownership(UART4_SHARED_PERI, (SPBA_MASTER_A | SPBA_MASTER_C));
+#else
+ spba_take_ownership(UART4_SHARED_PERI, SPBA_MASTER_A);
+#endif /* UARTr_DMA_ENABLE */
+ platform_device_register(&mxc_uart_device4);
+#endif /* UART4_ENABLED */
+#if UART5_ENABLED == 1
+#if UART5_DMA_ENABLE == 1
+ spba_take_ownership(UART5_SHARED_PERI, (SPBA_MASTER_A | SPBA_MASTER_C));
+#else
+ spba_take_ownership(UART5_SHARED_PERI, SPBA_MASTER_A);
+#endif /* UART5_DMA_ENABLE */
+ platform_device_register(&mxc_uart_device5);
+#endif /* UART5_ENABLED */
+
+ return 0;
+}
+
+#else
+static int __init mxc_init_uart(void)
+{
+ return 0;
+}
+#endif
+
+arch_initcall(mxc_init_uart);
diff --git a/arch/arm/mach-mx25/serial.h b/arch/arm/mach-mx25/serial.h
new file mode 100644
index 000000000000..4f53b09db1be
--- /dev/null
+++ b/arch/arm/mach-mx25/serial.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX25_SERIAL_H__
+#define __ARCH_ARM_MACH_MX25_SERIAL_H__
+
+/*!
+ * @file mach-mx25/serial.h
+ *
+ * @ingroup MSL_MX25
+ */
+#include <mach/mxc_uart.h>
+
+/* UART 1 configuration */
+/*!
+ * This option allows to choose either an interrupt-driven software controlled
+ * hardware flow control (set this option to 0) or hardware-driven hardware
+ * flow control (set this option to 1).
+ */
+#define UART1_HW_FLOW 1
+/*!
+ * This specifies the threshold at which the CTS pin is deasserted by the
+ * RXFIFO. Set this value in Decimal to anything from 0 to 32 for
+ * hardware-driven hardware flow control. Read the HW spec while specifying
+ * this value. When using interrupt-driven software controlled hardware
+ * flow control set this option to -1.
+ */
+#define UART1_UCR4_CTSTL 16
+/*!
+ * This is option to enable (set this option to 1) or disable DMA data transfer
+ */
+#define UART1_DMA_ENABLE 0
+/*!
+ * Specify the size of the DMA receive buffer. The minimum buffer size is 512
+ * bytes. The buffer size should be a multiple of 256.
+ */
+#define UART1_DMA_RXBUFSIZE 1024
+/*!
+ * Specify the MXC UART's Receive Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the RxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_RXTL 16
+/*!
+ * Specify the MXC UART's Transmit Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the TxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_TXTL 16
+/* UART 2 configuration */
+#define UART2_HW_FLOW 0
+#define UART2_UCR4_CTSTL (-1)
+#define UART2_DMA_ENABLE 0
+#define UART2_DMA_RXBUFSIZE 512
+#define UART2_UFCR_RXTL 16
+#define UART2_UFCR_TXTL 16
+/* UART 3 configuration */
+#define UART3_HW_FLOW 1
+#define UART3_UCR4_CTSTL 16
+#define UART3_DMA_ENABLE 1
+#define UART3_DMA_RXBUFSIZE 1024
+#define UART3_UFCR_RXTL 16
+#define UART3_UFCR_TXTL 16
+/* UART 4 configuration */
+#define UART4_HW_FLOW 1
+#define UART4_UCR4_CTSTL 16
+#define UART4_DMA_ENABLE 1
+#define UART4_DMA_RXBUFSIZE 1024
+#define UART4_UFCR_RXTL 16
+#define UART4_UFCR_TXTL 16
+/* UART 5 configuration */
+#define UART5_HW_FLOW 1
+#define UART5_UCR4_CTSTL 16
+#define UART5_DMA_ENABLE 1
+#define UART5_DMA_RXBUFSIZE 1024
+#define UART5_UFCR_RXTL 16
+#define UART5_UFCR_TXTL 16
+
+/*
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+/*
+ * Is the MUXED interrupt output sent to the ARM core
+ */
+#define INTS_NOTMUXED 0
+#define INTS_MUXED 1
+/* UART 1 configuration */
+/*!
+ * This define specifies whether the muxed ANDed interrupt line or the
+ * individual interrupts from the UART port is integrated with the ARM core.
+ * There exists a define like this for each UART port. Valid values that can
+ * be used are \b INTS_NOTMUXED or \b INTS_MUXED.
+ */
+#define UART1_MUX_INTS INTS_MUXED
+/*!
+ * This define specifies the transmitter interrupt number or the interrupt
+ * number of the ANDed interrupt in case the interrupts are muxed. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_INT1 MXC_INT_UART1
+/*!
+ * This define specifies the receiver interrupt number. If the interrupts of
+ * the UART are muxed, then we specify here a dummy value -1. There exists a
+ * define like this for each UART port.
+ */
+#define UART1_INT2 (-1)
+/*!
+ * This specifies the master interrupt number. If the interrupts of the UART
+ * are muxed, then we specify here a dummy value of -1. There exists a define
+ * like this for each UART port.
+ */
+#define UART1_INT3 (-1)
+/*!
+ * This specifies if the UART is a shared peripheral. It holds the shared
+ * peripheral number if it is shared or -1 if it is not shared. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_SHARED_PERI (-1)
+/* UART 2 configuration */
+#define UART2_MUX_INTS INTS_MUXED
+#define UART2_INT1 MXC_INT_UART2
+#define UART2_INT2 (-1)
+#define UART2_INT3 (-1)
+#define UART2_SHARED_PERI (-1)
+/* UART 3 configuration */
+#define UART3_MUX_INTS INTS_MUXED
+#define UART3_INT1 MXC_INT_UART3
+#define UART3_INT2 (-1)
+#define UART3_INT3 (-1)
+#define UART3_SHARED_PERI SPBA_UART3
+/* UART 4 configuration */
+#define UART4_MUX_INTS INTS_MUXED
+#define UART4_INT1 MXC_INT_UART4
+#define UART4_INT2 (-1)
+#define UART4_INT3 (-1)
+#define UART4_SHARED_PERI SPBA_UART4
+/* UART 5 configuration */
+#define UART5_MUX_INTS INTS_MUXED
+#define UART5_INT1 MXC_INT_UART5
+#define UART5_INT2 (-1)
+#define UART5_INT3 (-1)
+#define UART5_SHARED_PERI SPBA_UART5
+
+#endif /* __ARCH_ARM_MACH_MX25_SERIAL_H__ */
diff --git a/arch/arm/mach-mx25/system.c b/arch/arm/mach-mx25/system.c
new file mode 100644
index 000000000000..d2b6fb1e8721
--- /dev/null
+++ b/arch/arm/mach-mx25/system.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <mach/hardware.h>
+#include <asm/proc-fns.h>
+#include <asm/system.h>
+#include <mach/clock.h>
+#include "crm_regs.h"
+
+/*!
+ * @defgroup MSL_MX25 i.MX25 Machine Specific Layer (MSL)
+ */
+
+/*!
+ * @file mach-mx25/system.c
+ * @brief This file contains idle and reset functions.
+ *
+ * @ingroup MSL_MX25
+ */
+
+/*!
+ * MX25 low-power mode
+ */
+enum mx25_low_pwr_mode {
+ MX25_RUN_MODE,
+ MX25_WAIT_MODE,
+ MX25_DOZE_MODE,
+ MX25_STOP_MODE
+};
+
+extern int mxc_jtag_enabled;
+
+/*!
+ * This function is used to set cpu low power mode before WFI instruction
+ *
+ * @param mode indicates different kinds of power modes
+ */
+void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
+{
+ unsigned int lpm;
+ unsigned long reg;
+ unsigned int pmcr2, lpimr;
+ unsigned int cgcr0, cgcr1, cgcr2;
+ struct irq_desc *desc;
+ int i;
+
+ /*read CCTL value */
+ reg = __raw_readl(MXC_CCM_CCTL);
+
+ switch (mode) {
+ case WAIT_UNCLOCKED_POWER_OFF:
+ lpm = MX25_DOZE_MODE;
+ break;
+
+ case STOP_POWER_ON:
+ case STOP_POWER_OFF:
+ lpm = MX25_STOP_MODE;
+ /* The clock of LCDC/SLCDC, SDMA, RTIC, RNGC, MAX, CAN
+ and EMI needs to be gated on when entering Stop mode.
+ */
+ cgcr0 = __raw_readl(MXC_CCM_CGCR0);
+ cgcr1 = __raw_readl(MXC_CCM_CGCR1);
+ cgcr2 = __raw_readl(MXC_CCM_CGCR2);
+ __raw_writel(cgcr0 | MXC_CCM_CGCR0_STOP_MODE_MASK,
+ MXC_CCM_CGCR0);
+ __raw_writel(cgcr1 | MXC_CCM_CGCR1_STOP_MODE_MASK,
+ MXC_CCM_CGCR1);
+ __raw_writel(cgcr2 | MXC_CCM_CGCR2_STOP_MODE_MASK,
+ MXC_CCM_CGCR2);
+ /* The interrupts which are not wake-up sources need
+ be mask when entering Stop mode.
+ */
+ lpimr = MXC_CCM_LPIMR0_MASK;
+ for (i = 0; i < 32; i++) {
+ desc = irq_desc + i;
+ if ((desc->status & IRQ_WAKEUP) != 0)
+ lpimr &= ~(1 << i);
+ }
+ __raw_writel(lpimr, MXC_CCM_LPIMR0);
+ lpimr = MXC_CCM_LPIMR1_MASK;
+ for (i = 32; i < 64; i++) {
+ desc = irq_desc + i;
+ if ((desc->status & IRQ_WAKEUP) != 0)
+ lpimr &= ~(1 << (i - 32));
+ }
+ __raw_writel(lpimr, MXC_CCM_LPIMR1);
+
+ if (mode == STOP_POWER_OFF) {
+ pmcr2 = __raw_readl(MXC_CCM_PMCR2);
+ pmcr2 |= (MXC_CCM_PMCR2_OSC24M_DOWN |
+ MXC_CCM_PMCR2_VSTBY);
+ __raw_writel(pmcr2, MXC_CCM_PMCR2);
+ }
+ break;
+
+ case WAIT_CLOCKED:
+ case WAIT_UNCLOCKED:
+ default:
+ /* Wait is the default mode used when idle. */
+ lpm = MX25_WAIT_MODE;
+ break;
+ }
+
+ /* program LP CTL bit */
+ reg = ((reg & (~MXC_CCM_CCTL_LP_CTL_MASK)) |
+ lpm << MXC_CCM_CCTL_LP_CTL_OFFSET);
+
+ __raw_writel(reg, MXC_CCM_CCTL);
+}
+
+/*!
+ * This function puts the CPU into idle mode. It is called by default_idle()
+ * in process.c file.
+ */
+void arch_idle(void)
+{
+ /*
+ * This should do all the clock switching
+ * and wait for interrupt tricks.
+ */
+ if (!mxc_jtag_enabled) {
+ /* set as Wait mode */
+ mxc_cpu_lp_set(WAIT_UNCLOCKED);
+ cpu_do_idle();
+ }
+}
+
+/*
+ * This function resets the system. It is called by machine_restart().
+ *
+ * @param mode indicates different kinds of resets
+ */
+void arch_reset(char mode)
+{
+ /* Assert SRS signal */
+ mxc_wd_reset();
+}
diff --git a/arch/arm/mach-mx25/usb.h b/arch/arm/mach-mx25/usb.h
new file mode 100644
index 000000000000..0ab29b5147d6
--- /dev/null
+++ b/arch/arm/mach-mx25/usb.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+extern int usbotg_init(struct platform_device *pdev);
+extern void usbotg_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbotg_utmi_active(void);
+extern void gpio_usbotg_utmi_inactive(void);
+extern struct platform_device *host_pdev_register(struct resource *res,
+ int n_res,
+ struct fsl_usb2_platform_data
+ *config);
+
+extern int fsl_usb_host_init(struct platform_device *pdev);
+extern void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbh2_active(void);
+extern void gpio_usbh2_inactive(void);
+
+/*
+ * Determine which platform_data struct to use for the DR controller,
+ * based on which transceiver is configured.
+ * PDATA is a pointer to it.
+ */
+static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config;
+#define PDATA (&dr_utmi_config)
+
+/*
+ * Used to set pdata->operating_mode before registering the platform_device.
+ * If OTG is configured, the controller operates in OTG mode,
+ * otherwise it's either host or device.
+ */
+#ifdef CONFIG_USB_OTG
+#define DR_UDC_MODE FSL_USB2_DR_OTG
+#define DR_HOST_MODE FSL_USB2_DR_OTG
+#else
+#define DR_UDC_MODE FSL_USB2_DR_DEVICE
+#define DR_HOST_MODE FSL_USB2_DR_HOST
+#endif
+
+#ifdef CONFIG_USB_EHCI_ARC_OTG
+static inline void dr_register_host(struct resource *r, int rs)
+{
+ PDATA->operating_mode = DR_HOST_MODE;
+ host_pdev_register(r, rs, PDATA);
+}
+#else
+static inline void dr_register_host(struct resource *r, int rs)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_GADGET_ARC
+static struct platform_device dr_udc_device;
+
+static inline void dr_register_udc(void)
+{
+ PDATA->operating_mode = DR_UDC_MODE;
+ dr_udc_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_udc_device))
+ printk(KERN_ERR "usb: can't register DR gadget\n");
+ else
+ printk(KERN_INFO "usb: DR gadget (%s) registered\n",
+ PDATA->transceiver);
+}
+#else
+static inline void dr_register_udc(void)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_OTG
+static struct platform_device dr_otg_device;
+
+/*
+ * set the proper operating_mode and
+ * platform_data pointer, then register the
+ * device.
+ */
+static inline void dr_register_otg(void)
+{
+ PDATA->operating_mode = FSL_USB2_DR_OTG;
+ dr_otg_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_otg_device))
+ printk(KERN_ERR "usb: can't register otg device\n");
+ else
+ printk(KERN_INFO "usb: DR OTG registered\n");
+}
+#else
+static inline void dr_register_otg(void)
+{
+}
+#endif
diff --git a/arch/arm/mach-mx25/usb_dr.c b/arch/arm/mach-mx25/usb_dr.c
new file mode 100644
index 000000000000..bae8b27d3e30
--- /dev/null
+++ b/arch/arm/mach-mx25/usb_dr.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+/*
+ * platform data structs
+ * - Which one to use is determined by CONFIG options in usb.h
+ * - operating_mode plugged at run time
+ */
+static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config = {
+ .name = "DR",
+ .platform_init = usbotg_init,
+ .platform_uninit = usbotg_uninit,
+ .phy_mode = FSL_USB2_PHY_UTMI_WIDE,
+ .power_budget = 500, /* via RT9706 */
+ .gpio_usb_active = gpio_usbotg_utmi_active,
+ .gpio_usb_inactive = gpio_usbotg_utmi_inactive,
+ .transceiver = "utmi",
+};
+
+/*
+ * resources
+ */
+static struct resource otg_resources[] = {
+ [0] = {
+ .start = (u32)(USB_OTGREGS_BASE),
+ .end = (u32)(USB_OTGREGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB_OTG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 dr_udc_dmamask = ~(u32) 0;
+static void dr_udc_release(struct device *dev)
+{
+}
+
+static u64 dr_otg_dmamask = ~(u32) 0;
+static void dr_otg_release(struct device *dev)
+{
+}
+
+/*
+ * platform device structs
+ * dev.platform_data field plugged at run time
+ */
+static struct platform_device __maybe_unused dr_udc_device = {
+ .name = "fsl-usb2-udc",
+ .id = -1,
+ .dev = {
+ .release = dr_udc_release,
+ .dma_mask = &dr_udc_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static struct platform_device __maybe_unused dr_otg_device = {
+ .name = "fsl-usb2-otg",
+ .id = -1,
+ .dev = {
+ .release = dr_otg_release,
+ .dma_mask = &dr_otg_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static int __init usb_dr_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ dr_register_otg();
+ dr_register_host(otg_resources, ARRAY_SIZE(otg_resources));
+ dr_register_udc();
+
+ return 0;
+}
+
+module_init(usb_dr_init);
diff --git a/arch/arm/mach-mx25/usb_h2.c b/arch/arm/mach-mx25/usb_h2.c
new file mode 100644
index 000000000000..9afbfef7a7d6
--- /dev/null
+++ b/arch/arm/mach-mx25/usb_h2.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <linux/regulator/consumer.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+static struct fsl_usb2_platform_data usbh2_config = {
+ .name = "Host 2",
+ .platform_init = fsl_usb_host_init,
+ .platform_uninit = fsl_usb_host_uninit,
+ .operating_mode = FSL_USB2_MPH_HOST,
+ .phy_mode = FSL_USB2_PHY_SERIAL,
+ .power_budget = 500, /* via RT9702 */
+ .gpio_usb_active = gpio_usbh2_active,
+ .gpio_usb_inactive = gpio_usbh2_inactive,
+ .transceiver = "serial", /* on-chip */
+};
+
+static struct resource usbh2_resources[] = {
+ [0] = {
+ .start = (u32) (USB_H2REGS_BASE),
+ .end = (u32) (USB_H2REGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB_HTG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+void usbh2_get_xcvr_power(struct device *dev)
+{
+ struct regulator *usbh2_regux;
+
+ usbh2_regux = regulator_get(dev, "GPO1");
+ regulator_enable(usbh2_regux);
+ ((struct fsl_usb2_platform_data *)dev->platform_data)->
+ xcvr_pwr->regu1 = usbh2_regux;
+
+ usbh2_regux = regulator_get(dev, "GPO3");
+ regulator_enable(usbh2_regux);
+ ((struct fsl_usb2_platform_data *)dev->platform_data)->
+ xcvr_pwr->regu2 = usbh2_regux;
+}
+EXPORT_SYMBOL(usbh2_get_xcvr_power);
+
+void usbh2_put_xcvr_power(struct device *dev)
+{
+ struct regulator *usbh2_regux;
+
+ usbh2_regux = ((struct fsl_usb2_platform_data *)dev->
+ platform_data)->xcvr_pwr->regu2;
+ regulator_disable(usbh2_regux);
+ regulator_put(usbh2_regux);
+
+ usbh2_regux = ((struct fsl_usb2_platform_data *)dev->
+ platform_data)->xcvr_pwr->regu1;
+ regulator_disable(usbh2_regux);
+ regulator_put(usbh2_regux);
+}
+EXPORT_SYMBOL(usbh2_put_xcvr_power);
+
+static int __init usbh2_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ host_pdev_register(usbh2_resources, ARRAY_SIZE(usbh2_resources),
+ &usbh2_config);
+ return 0;
+}
+module_init(usbh2_init);
diff --git a/arch/arm/mach-mx27/Kconfig b/arch/arm/mach-mx27/Kconfig
new file mode 100644
index 000000000000..979c557085e6
--- /dev/null
+++ b/arch/arm/mach-mx27/Kconfig
@@ -0,0 +1,43 @@
+menu "MX27 Options"
+ depends on ARCH_MX27
+
+config MX27_OPTIONS
+ bool
+ default y
+ select CPU_ARM926T
+ select MXC_EMMA
+ select USB_ARCH_HAS_EHCI
+
+config MACH_MX27ADS
+ bool "Support MX27ADS platforms"
+ default y
+ help
+ Include support for MX27ADS platform. This includes specific
+ configurations for the board and its peripherals.
+
+config ARCH_MXC_HAS_NFC_V1
+ bool "MXC NFC Hardware Version 1"
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 1
+ If unsure, say N.
+
+menu "Device options"
+
+config I2C_MXC_SELECT1
+ bool "Enable I2C1 module"
+ default y
+ depends on I2C_MXC
+ help
+ Enable MX31 I2C1 module.
+
+config I2C_MXC_SELECT2
+ bool "Enable I2C2 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX31 I2C2 module.
+
+endmenu
+
+endmenu
diff --git a/arch/arm/mach-mx27/Makefile b/arch/arm/mach-mx27/Makefile
new file mode 100644
index 000000000000..80a0ae7c4322
--- /dev/null
+++ b/arch/arm/mach-mx27/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-y := mm.o dma.o gpio_mux.o clock.o devices.o serial.o system.o cpu.o dptc.o
+obj-$(CONFIG_MACH_MX27ADS) += mx27ads.o mx27ads_gpio.o
+
+# power management
+obj-$(CONFIG_PM) += pm.o mxc_pm.o
+
+obj-$(CONFIG_USB_EHCI_ARC_H1) += usb_h1.o
+obj-$(CONFIG_USB_EHCI_ARC_H2) += usb_h2.o
+
+ifneq ($(strip $(CONFIG_USB_GADGET_ARC) $(CONFIG_USB_EHCI_ARC_OTG)),)
+ obj-y += usb_dr.o
+endif
diff --git a/arch/arm/mach-mx27/Makefile.boot b/arch/arm/mach-mx27/Makefile.boot
new file mode 100644
index 000000000000..696831dcd485
--- /dev/null
+++ b/arch/arm/mach-mx27/Makefile.boot
@@ -0,0 +1,3 @@
+ zreladdr-y := 0xA0008000
+params_phys-y := 0xA0000100
+initrd_phys-y := 0xA0800000
diff --git a/arch/arm/mach-mx27/board-mx27ads.h b/arch/arm/mach-mx27/board-mx27ads.h
new file mode 100644
index 000000000000..fd152caef71b
--- /dev/null
+++ b/arch/arm/mach-mx27/board-mx27ads.h
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_BOARD_MX27ADS_H__
+#define __ASM_ARCH_MXC_BOARD_MX27ADS_H__
+
+/*!
+ * @defgroup BRDCFG_MX27 Board Configuration Options
+ * @ingroup MSL_MX27
+ */
+
+/*!
+ * @file mach-mx27/board-mx27ads.h
+ *
+ * @brief This file contains all the board level configuration options.
+ *
+ * It currently hold the options defined for MX27 ADS Platform.
+ *
+ * @ingroup BRDCFG_MX27
+ */
+
+/*
+ * Include Files
+ */
+#include <mach/mxc_uart.h>
+
+/*!
+ * @name MXC UART EVB board level configurations
+ */
+/*! @{ */
+/*!
+ * Specifies if the Irda transmit path is inverting
+ */
+#define MXC_IRDA_TX_INV 0
+/*!
+ * Specifies if the Irda receive path is inverting
+ */
+#define MXC_IRDA_RX_INV 0
+/* UART 1 configuration */
+/*!
+ * This define specifies if the UART port is configured to be in DTE or
+ * DCE mode. There exists a define like this for each UART port. Valid
+ * values that can be used are \b MODE_DTE or \b MODE_DCE.
+ */
+#define UART1_MODE MODE_DCE
+/*!
+ * This define specifies if the UART is to be used for IRDA. There exists a
+ * define like this for each UART port. Valid values that can be used are
+ * \b IRDA or \b NO_IRDA.
+ */
+#define UART1_IR NO_IRDA
+/*!
+ * This define is used to enable or disable a particular UART port. If
+ * disabled, the UART will not be registered in the file system and the user
+ * will not be able to access it. There exists a define like this for each UART
+ * port. Specify a value of 1 to enable the UART and 0 to disable it.
+ */
+#define UART1_ENABLED 1
+/*! @} */
+/* UART 2 configuration */
+#define UART2_MODE MODE_DCE
+#define UART2_IR NO_IRDA
+#define UART2_ENABLED 1
+/* UART 3 configuration */
+#define UART3_MODE MODE_DCE
+#define UART3_IR IRDA
+#define UART3_ENABLED 1
+/* UART 4 configuration */
+#define UART4_MODE MODE_DTE
+#define UART4_IR NO_IRDA
+#define UART4_ENABLED 0 /* Disable UART 4 as its pins are shared with ATA */
+/* UART 5 configuration */
+#define UART5_MODE MODE_DTE
+#define UART5_IR NO_IRDA
+#define UART5_ENABLED 1
+/* UART 6 configuration */
+#define UART6_MODE MODE_DTE
+#define UART6_IR NO_IRDA
+#define UART6_ENABLED 1
+
+#define MXC_LL_EXTUART_PADDR (CS4_BASE_ADDR + 0x20000)
+#define MXC_LL_EXTUART_VADDR (CS4_BASE_ADDR_VIRT + 0x20000)
+#define MXC_LL_EXTUART_16BIT_BUS
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPI_IO_ADDRESS(UART1_BASE_ADDR)
+
+/*!
+ * @name PBC Controller parameters
+ */
+/*! @{ */
+/*!
+ * Base address of PBC controller, CS4
+ */
+#define PBC_BASE_ADDRESS IO_ADDRESS(CS4_BASE_ADDR)
+#define PBC_REG_ADDR(offset) (PBC_BASE_ADDRESS + (offset))
+
+/*!
+ * PBC Interupt name definitions
+ */
+#define PBC_GPIO1_0 0
+#define PBC_GPIO1_1 1
+#define PBC_GPIO1_2 2
+#define PBC_GPIO1_3 3
+#define PBC_GPIO1_4 4
+#define PBC_GPIO1_5 5
+
+#define PBC_INTR_MAX_NUM 6
+#define PBC_INTR_SHARED_MAX_NUM 8
+
+/* When the PBC address connection is fixed in h/w, defined as 1 */
+#define PBC_ADDR_SH 0
+
+/* Offsets for the PBC Controller register */
+/*!
+ * PBC Board version register offset
+ */
+#define PBC_VERSION_REG PBC_REG_ADDR(0x00000 >> PBC_ADDR_SH)
+/*!
+ * PBC Board control register 1 set address.
+ */
+#define PBC_BCTRL1_SET_REG PBC_REG_ADDR(0x00008 >> PBC_ADDR_SH)
+/*!
+ * PBC Board control register 1 clear address.
+ */
+#define PBC_BCTRL1_CLEAR_REG PBC_REG_ADDR(0x0000C >> PBC_ADDR_SH)
+/*!
+ * PBC Board control register 2 set address.
+ */
+#define PBC_BCTRL2_SET_REG PBC_REG_ADDR(0x00010 >> PBC_ADDR_SH)
+/*!
+ * PBC Board control register 2 clear address.
+ */
+#define PBC_BCTRL2_CLEAR_REG PBC_REG_ADDR(0x00014 >> PBC_ADDR_SH)
+/*!
+ * PBC Board control register 3 set address.
+ */
+#define PBC_BCTRL3_SET_REG PBC_REG_ADDR(0x00018 >> PBC_ADDR_SH)
+/*!
+ * PBC Board control register 3 clear address.
+ */
+#define PBC_BCTRL3_CLEAR_REG PBC_REG_ADDR(0x0001C >> PBC_ADDR_SH)
+/*!
+ * PBC Board control register 3 set address.
+ */
+#define PBC_BCTRL4_SET_REG PBC_REG_ADDR(0x00020 >> PBC_ADDR_SH)
+/*!
+ * PBC Board control register 4 clear address.
+ */
+#define PBC_BCTRL4_CLEAR_REG PBC_REG_ADDR(0x00024 >> PBC_ADDR_SH)
+/*!PBC_ADDR_SH
+ * PBC Board status register 1.
+ */
+#define PBC_BSTAT1_REG PBC_REG_ADDR(0x00028 >> PBC_ADDR_SH)
+/*!
+ * PBC Board interrupt status register.
+ */
+#define PBC_INTSTATUS_REG PBC_REG_ADDR(0x0002C >> PBC_ADDR_SH)
+/*!
+ * PBC Board interrupt current status register.
+ */
+#define PBC_INTCURR_STATUS_REG PBC_REG_ADDR(0x00034 >> PBC_ADDR_SH)
+/*!
+ * PBC Interrupt mask register set address.
+ */
+#define PBC_INTMASK_SET_REG PBC_REG_ADDR(0x00038 >> PBC_ADDR_SH)
+/*!
+ * PBC Interrupt mask register clear address.
+ */
+#define PBC_INTMASK_CLEAR_REG PBC_REG_ADDR(0x0003C >> PBC_ADDR_SH)
+/*!
+ * External UART A.
+ */
+#define PBC_SC16C652_UARTA_REG PBC_REG_ADDR(0x20000 >> PBC_ADDR_SH)
+/*!
+ * UART 4 Expanding Signal Status.
+ */
+#define PBC_UART_STATUS_REG PBC_REG_ADDR(0x22000 >> PBC_ADDR_SH)
+/*!
+ * UART 4 Expanding Signal Control Set.
+ */
+#define PBC_UCTRL_SET_REG PBC_REG_ADDR(0x24000 >> PBC_ADDR_SH)
+/*!
+ * UART 4 Expanding Signal Control Clear.
+ */
+#define PBC_UCTRL_CLR_REG PBC_REG_ADDR(0x26000 >> PBC_ADDR_SH)
+/*!
+ * Ethernet Controller IO base address.
+ */
+#define PBC_CS8900A_IOBASE_REG PBC_REG_ADDR(0x40000 >> PBC_ADDR_SH)
+/*!
+ * Ethernet Controller Memory base address.
+ */
+#define PBC_CS8900A_MEMBASE_REG PBC_REG_ADDR(0x42000 >> PBC_ADDR_SH)
+/*!
+ * Ethernet Controller DMA base address.
+ */
+#define PBC_CS8900A_DMABASE_REG PBC_REG_ADDR(0x44000 >> PBC_ADDR_SH)
+
+/* PBC Board Version Register bit definition */
+#define PBC_VERSION_ADS 0x8000 /* Bit15=1 means version for ads */
+#define PBC_VERSION_EVB_REVB 0x4000 /* BIT14=1 means version for evb revb */
+
+/* PBC Board Control Register 1 bit definitions */
+#define PBC_BCTRL1_ERST 0x0001 /* Ethernet Reset */
+#define PBC_BCTRL1_URST 0x0002 /* Reset External UART controller */
+#define PBC_BCTRL1_FRST 0x0004 /* FEC Reset */
+#define PBC_BCTRL1_ESLEEP 0x0010 /* Enable ethernet Sleep */
+#define PBC_BCTRL1_LCDON 0x0800 /* Enable the LCD */
+
+/* PBC Board Control Register 2 bit definitions */
+#define PBC_BCTRL2_VCC_EN 0x0004 /* Enable VCC */
+#define PBC_BCTRL2_VPP_EN 0x0008 /* Enable Vpp */
+#define PBC_BCTRL2_ATAFEC_EN 0X0010
+#define PBC_BCTRL2_ATAFEC_SEL 0X0020
+#define PBC_BCTRL2_ATA_EN 0X0040
+#define PBC_BCTRL2_IRDA_SD 0X0080
+#define PBC_BCTRL2_IRDA_EN 0X0100
+#define PBC_BCTRL2_CCTL10 0X0200
+#define PBC_BCTRL2_CCTL11 0X0400
+
+/* PBC Board Control Register 3 bit definitions */
+#define PBC_BCTRL3_HSH_EN 0X0020
+#define PBC_BCTRL3_FSH_MOD 0X0040
+#define PBC_BCTRL3_OTG_HS_EN 0X0080
+#define PBC_BCTRL3_OTG_VBUS_EN 0X0100
+#define PBC_BCTRL3_FSH_VBUS_EN 0X0200
+#define PBC_BCTRL3_USB_OTG_ON 0X0800
+#define PBC_BCTRL3_USB_FSH_ON 0X1000
+
+/* PBC Board Control Register 4 bit definitions */
+#define PBC_BCTRL4_REGEN_SEL 0X0001
+#define PBC_BCTRL4_USER_OFF 0X0002
+#define PBC_BCTRL4_VIB_EN 0X0004
+#define PBC_BCTRL4_PWRGT1_EN 0X0008
+#define PBC_BCTRL4_PWRGT2_EN 0X0010
+#define PBC_BCTRL4_STDBY_PRI 0X0020
+
+#ifndef __ASSEMBLY__
+/*!
+ * Enumerations for SD cards and memory stick card. This corresponds to
+ * the card EN bits in the IMR: SD1_EN | MS_EN | SD3_EN | SD2_EN.
+ */
+enum mxc_card_no {
+ MXC_CARD_SD2 = 0,
+ MXC_CARD_SD3,
+ MXC_CARD_MS,
+ MXC_CARD_SD1,
+ MXC_CARD_MIN = MXC_CARD_SD2,
+ MXC_CARD_MAX = MXC_CARD_SD1,
+};
+#endif
+
+#define MXC_CPLD_VER_1_50 0x01
+
+/*!
+ * PBC BSTAT Register bit definitions
+ */
+#define PBC_BSTAT_PRI_INT 0X0001
+#define PBC_BSTAT_USB_BYP 0X0002
+#define PBC_BSTAT_ATA_IOCS16 0X0004
+#define PBC_BSTAT_ATA_CBLID 0X0008
+#define PBC_BSTAT_ATA_DASP 0X0010
+#define PBC_BSTAT_PWR_RDY 0X0020
+#define PBC_BSTAT_SD3_WP 0X0100
+#define PBC_BSTAT_SD2_WP 0X0200
+#define PBC_BSTAT_SD1_WP 0X0400
+#define PBC_BSTAT_SD3_DET 0X0800
+#define PBC_BSTAT_SD2_DET 0X1000
+#define PBC_BSTAT_SD1_DET 0X2000
+#define PBC_BSTAT_MS_DET 0X4000
+#define PBC_BSTAT_SD3_DET_BIT 11
+#define PBC_BSTAT_SD2_DET_BIT 12
+#define PBC_BSTAT_SD1_DET_BIT 13
+#define PBC_BSTAT_MS_DET_BIT 14
+#define MXC_BSTAT_BIT(n) ((n == MXC_CARD_SD2) ? PBC_BSTAT_SD2_DET : \
+ ((n == MXC_CARD_SD3) ? PBC_BSTAT_SD3_DET : \
+ ((n == MXC_CARD_SD1) ? PBC_BSTAT_SD1_DET : \
+ ((n == MXC_CARD_MS) ? PBC_BSTAT_MS_DET : 0x0))))
+
+/*!
+ * PBC UART Control Register bit definitions
+ */
+#define PBC_UCTRL_DCE_DCD 0X0001
+#define PBC_UCTRL_DCE_DSR 0X0002
+#define PBC_UCTRL_DCE_RI 0X0004
+#define PBC_UCTRL_DTE_DTR 0X0100
+
+/*!
+ * PBC UART Status Register bit definitions
+ */
+#define PBC_USTAT_DTE_DCD 0X0001
+#define PBC_USTAT_DTE_DSR 0X0002
+#define PBC_USTAT_DTE_RI 0X0004
+#define PBC_USTAT_DCE_DTR 0X0100
+
+/*!
+ * PBC Interupt mask register bit definitions
+ */
+#define PBC_INTR_SD3_R_EN_BIT 4
+#define PBC_INTR_SD2_R_EN_BIT 0
+#define PBC_INTR_SD1_R_EN_BIT 6
+#define PBC_INTR_MS_R_EN_BIT 5
+#define PBC_INTR_SD3_EN_BIT 13
+#define PBC_INTR_SD2_EN_BIT 12
+#define PBC_INTR_MS_EN_BIT 14
+#define PBC_INTR_SD1_EN_BIT 15
+
+#define PBC_INTR_SD2_R_EN 0x0001
+#define PBC_INTR_LOW_BAT 0X0002
+#define PBC_INTR_OTG_FSOVER 0X0004
+#define PBC_INTR_FSH_OVER 0X0008
+#define PBC_INTR_SD3_R_EN 0x0010
+#define PBC_INTR_MS_R_EN 0x0020
+#define PBC_INTR_SD1_R_EN 0x0040
+#define PBC_INTR_FEC_INT 0X0080
+#define PBC_INTR_ENET_INT 0X0100
+#define PBC_INTR_OTGFS_INT 0X0200
+#define PBC_INTR_XUART_INT 0X0400
+#define PBC_INTR_CCTL12 0X0800
+#define PBC_INTR_SD2_EN 0x1000
+#define PBC_INTR_SD3_EN 0x2000
+#define PBC_INTR_MS_EN 0x4000
+#define PBC_INTR_SD1_EN 0x8000
+
+/*! @} */
+
+#define CKIH_27MHZ_BIT_SET (1 << 3)
+
+/* For interrupts like xuart, enet etc */
+#define EXPIO_PARENT_INT IOMUX_TO_IRQ(MX27_PIN_TIN)
+
+/*
+ * This corresponds to PBC_INTMASK_SET_REG at offset 0x38.
+ *
+ */
+#define EXPIO_INT_LOW_BAT (MXC_EXP_IO_BASE + 1)
+#define EXPIO_INT_OTG_FS_OVR (MXC_EXP_IO_BASE + 2)
+#define EXPIO_INT_FSH_OVR (MXC_EXP_IO_BASE + 3)
+#define EXPIO_INT_RES4 (MXC_EXP_IO_BASE + 4)
+#define EXPIO_INT_RES5 (MXC_EXP_IO_BASE + 5)
+#define EXPIO_INT_RES6 (MXC_EXP_IO_BASE + 6)
+#define EXPIO_INT_FEC (MXC_EXP_IO_BASE + 7)
+#define EXPIO_INT_ENET_INT (MXC_EXP_IO_BASE + 8)
+#define EXPIO_INT_OTG_FS_INT (MXC_EXP_IO_BASE + 9)
+#define EXPIO_INT_XUART_INTA (MXC_EXP_IO_BASE + 10)
+#define EXPIO_INT_CCTL12_INT (MXC_EXP_IO_BASE + 11)
+#define EXPIO_INT_SD2_EN (MXC_EXP_IO_BASE + 12)
+#define EXPIO_INT_SD3_EN (MXC_EXP_IO_BASE + 13)
+#define EXPIO_INT_MS_EN (MXC_EXP_IO_BASE + 14)
+#define EXPIO_INT_SD1_EN (MXC_EXP_IO_BASE + 15)
+
+/*! This is System IRQ used by CS8900A for interrupt generation taken from platform.h */
+#define CS8900AIRQ EXPIO_INT_ENET_INT
+/*! This is I/O Base address used to access registers of CS8900A on MXC ADS */
+#define CS8900A_BASE_ADDRESS (PBC_CS8900A_IOBASE_REG + 0x300)
+
+#define MXC_PMIC_INT_LINE IOMUX_TO_IRQ(MX27_PIN_TOUT)
+
+/*!
+* This is used to detect if the CPLD version is for mx27 evb board rev-a
+*/
+#define PBC_CPLD_VERSION_IS_REVA() \
+ ((__raw_readw(PBC_VERSION_REG) & \
+ (PBC_VERSION_ADS | PBC_VERSION_EVB_REVB))\
+ == 0)
+
+#define MXC_BD_LED1 (1 << 5)
+#define MXC_BD_LED2 (1 << 6)
+#define MXC_BD_LED_ON(led) \
+ __raw_writew(led, PBC_BCTRL1_SET_REG)
+#define MXC_BD_LED_OFF(led) \
+ __raw_writew(led, PBC_BCTRL1_CLEAR_REG)
+
+#endif /* __ASM_ARCH_MXC_BOARD_MX27ADS_H__ */
diff --git a/arch/arm/mach-mx27/clock.c b/arch/arm/mach-mx27/clock.c
new file mode 100644
index 000000000000..49444a21902a
--- /dev/null
+++ b/arch/arm/mach-mx27/clock.c
@@ -0,0 +1,1530 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/clock.h>
+#include "crm_regs.h"
+
+#define CKIH_CLK_FREQ 26000000 /* 26M reference clk */
+#define CKIH_CLK_FREQ_27MHZ 27000000
+#define CKIL_CLK_FREQ 32768 /* 32.768k oscillator in */
+
+static struct clk ckil_clk;
+static struct clk mpll_clk;
+static struct clk mpll_main_clk[];
+static struct clk spll_clk;
+
+static int _clk_enable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg |= 1 << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+
+ return 0;
+}
+
+static void _clk_disable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(1 << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+}
+
+static int _clk_spll_enable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(CCM_CSCR);
+ reg |= CCM_CSCR_SPEN;
+ __raw_writel(reg, CCM_CSCR);
+
+ while ((__raw_readl(CCM_SPCTL1) & CCM_SPCTL1_LF) == 0) ;
+
+ return 0;
+}
+
+static void _clk_spll_disable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(CCM_CSCR);
+ reg &= ~CCM_CSCR_SPEN;
+ __raw_writel(reg, CCM_CSCR);
+}
+
+static void _clk_pccr01_enable(unsigned long mask0, unsigned long mask1)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(CCM_PCCR0);
+ reg |= mask0;
+ __raw_writel(reg, CCM_PCCR0);
+
+ reg = __raw_readl(CCM_PCCR1);
+ reg |= mask1;
+ __raw_writel(reg, CCM_PCCR1);
+
+}
+
+static void _clk_pccr01_disable(unsigned long mask0, unsigned long mask1)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(CCM_PCCR0);
+ reg &= ~mask0;
+ __raw_writel(reg, CCM_PCCR0);
+
+ reg = __raw_readl(CCM_PCCR1);
+ reg &= ~mask1;
+ __raw_writel(reg, CCM_PCCR1);
+}
+
+static void _clk_pccr10_enable(unsigned long mask1, unsigned long mask0)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(CCM_PCCR1);
+ reg |= mask1;
+ __raw_writel(reg, CCM_PCCR1);
+
+ reg = __raw_readl(CCM_PCCR0);
+ reg |= mask0;
+ __raw_writel(reg, CCM_PCCR0);
+}
+
+static void _clk_pccr10_disable(unsigned long mask1, unsigned long mask0)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(CCM_PCCR1);
+ reg &= ~mask1;
+ __raw_writel(reg, CCM_PCCR1);
+
+ reg = __raw_readl(CCM_PCCR0);
+ reg &= ~mask0;
+ __raw_writel(reg, CCM_PCCR0);
+}
+
+static int _clk_dma_enable(struct clk *clk)
+{
+ _clk_pccr01_enable(CCM_PCCR0_DMA_MASK, CCM_PCCR1_HCLK_DMA_MASK);
+
+ return 0;
+}
+
+static void _clk_dma_disable(struct clk *clk)
+{
+ _clk_pccr01_disable(CCM_PCCR0_DMA_MASK, CCM_PCCR1_HCLK_DMA_MASK);
+}
+
+static int _clk_rtic_enable(struct clk *clk)
+{
+ _clk_pccr01_enable(CCM_PCCR0_RTIC_MASK, CCM_PCCR1_HCLK_RTIC_MASK);
+
+ return 0;
+}
+
+static void _clk_rtic_disable(struct clk *clk)
+{
+ _clk_pccr01_disable(CCM_PCCR0_RTIC_MASK, CCM_PCCR1_HCLK_RTIC_MASK);
+}
+
+static int _clk_emma_enable(struct clk *clk)
+{
+ _clk_pccr01_enable(CCM_PCCR0_EMMA_MASK, CCM_PCCR1_HCLK_EMMA_MASK);
+
+ return 0;
+}
+
+static void _clk_emma_disable(struct clk *clk)
+{
+ _clk_pccr01_disable(CCM_PCCR0_EMMA_MASK, CCM_PCCR1_HCLK_EMMA_MASK);
+}
+
+static int _clk_slcdc_enable(struct clk *clk)
+{
+ _clk_pccr01_enable(CCM_PCCR0_SLCDC_MASK, CCM_PCCR1_HCLK_SLCDC_MASK);
+
+ return 0;
+}
+
+static void _clk_slcdc_disable(struct clk *clk)
+{
+ _clk_pccr01_disable(CCM_PCCR0_SLCDC_MASK, CCM_PCCR1_HCLK_SLCDC_MASK);
+}
+
+static int _clk_fec_enable(struct clk *clk)
+{
+ _clk_pccr01_enable(CCM_PCCR0_FEC_MASK, CCM_PCCR1_HCLK_FEC_MASK);
+
+ return 0;
+}
+
+static void _clk_fec_disable(struct clk *clk)
+{
+ _clk_pccr01_disable(CCM_PCCR0_FEC_MASK, CCM_PCCR1_HCLK_FEC_MASK);
+}
+
+static int _clk_vpu_enable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(CCM_PCCR1);
+ reg |= CCM_PCCR1_VPU_BAUD_MASK | CCM_PCCR1_HCLK_VPU_MASK;
+ __raw_writel(reg, CCM_PCCR1);
+
+ return 0;
+}
+
+static void _clk_vpu_disable(struct clk *clk)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(CCM_PCCR1);
+ reg &= ~(CCM_PCCR1_VPU_BAUD_MASK | CCM_PCCR1_HCLK_VPU_MASK);
+ __raw_writel(reg, CCM_PCCR1);
+}
+
+static int _clk_sahara2_enable(struct clk *clk)
+{
+ _clk_pccr01_enable(CCM_PCCR0_SAHARA_MASK, CCM_PCCR1_HCLK_SAHARA_MASK);
+
+ return 0;
+}
+
+static void _clk_sahara2_disable(struct clk *clk)
+{
+ _clk_pccr01_disable(CCM_PCCR0_SAHARA_MASK, CCM_PCCR1_HCLK_SAHARA_MASK);
+}
+
+static int _clk_mstick1_enable(struct clk *clk)
+{
+ _clk_pccr10_enable(CCM_PCCR1_MSHC_BAUD_MASK, CCM_PCCR0_MSHC_MASK);
+
+ return 0;
+}
+
+static void _clk_mstick1_disable(struct clk *clk)
+{
+ _clk_pccr10_disable(CCM_PCCR1_MSHC_BAUD_MASK, CCM_PCCR0_MSHC_MASK);
+}
+
+#define CSCR() (__raw_readl(CCM_CSCR))
+#define PCDR0() (__raw_readl(CCM_PCDR0))
+#define PCDR1() (__raw_readl(CCM_PCDR1))
+
+static void _clk_pll_recalc(struct clk *clk)
+{
+ unsigned long mfi = 0, mfn = 0, mfd = 0, pdf = 0;
+ unsigned long ref_clk;
+ unsigned long reg;
+ unsigned long long temp;
+
+ ref_clk = clk->parent->rate;
+ if (clk->parent == &ckil_clk) {
+ ref_clk *= 1024;
+ }
+
+ if (clk == &mpll_clk) {
+ reg = __raw_readl(CCM_MPCTL0);
+ pdf = (reg & CCM_MPCTL0_PD_MASK) >> CCM_MPCTL0_PD_OFFSET;
+ mfd = (reg & CCM_MPCTL0_MFD_MASK) >> CCM_MPCTL0_MFD_OFFSET;
+ mfi = (reg & CCM_MPCTL0_MFI_MASK) >> CCM_MPCTL0_MFI_OFFSET;
+ mfn = (reg & CCM_MPCTL0_MFN_MASK) >> CCM_MPCTL0_MFN_OFFSET;
+ } else if (clk == &spll_clk) {
+ reg = __raw_readl(CCM_SPCTL0);
+ /*TODO: This is TO2 Bug */
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) == 1) {
+ __raw_writel(reg, CCM_SPCTL0);
+ }
+ pdf = (reg & CCM_SPCTL0_PD_MASK) >> CCM_SPCTL0_PD_OFFSET;
+ mfd = (reg & CCM_SPCTL0_MFD_MASK) >> CCM_SPCTL0_MFD_OFFSET;
+ mfi = (reg & CCM_SPCTL0_MFI_MASK) >> CCM_SPCTL0_MFI_OFFSET;
+ mfn = (reg & CCM_SPCTL0_MFN_MASK) >> CCM_SPCTL0_MFN_OFFSET;
+ } else {
+ BUG(); /* oops */
+ }
+
+ mfi = (mfi <= 5) ? 5 : mfi;
+ temp = 2LL * ref_clk * mfn;
+ do_div(temp, mfd + 1);
+ temp = 2LL * ref_clk * mfi + temp;
+ do_div(temp, pdf + 1);
+
+ clk->rate = temp;
+}
+
+static void _clk_mpll_main_recalc(struct clk *clk)
+{
+ /* i.MX27 TO2:
+ * clk->id == 0: arm clock source path 1 which is from 2*MPLL/DIV_2
+ * clk->id == 1: arm clock source path 2 which is from 2*MPLL/DIV_3
+ */
+ switch (clk->id) {
+ case 0:
+ clk->rate = clk->parent->rate;
+ break;
+ case 1:
+ clk->rate = 2 * clk->parent->rate / 3;
+ }
+}
+
+static int _clk_cpu_set_parent(struct clk *clk, struct clk *parent)
+{
+ int cscr = CSCR();
+
+ if (clk->parent == parent)
+ return 0;
+
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) {
+ if (parent == &mpll_main_clk[0]) {
+ cscr |= CCM_CSCR_ARM_SRC;
+ } else {
+ if (parent == &mpll_main_clk[1]) {
+ cscr &= ~CCM_CSCR_ARM_SRC;
+ } else {
+ return -EINVAL;
+ }
+ }
+ __raw_writel(cscr, CCM_CSCR);
+ } else {
+ return -ENODEV;
+ }
+ clk->parent = parent;
+ return 0;
+}
+
+static unsigned long _clk_cpu_round_rate(struct clk *clk, unsigned long rate)
+{
+ int div;
+ div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate) {
+ div++;
+ }
+
+ if (div > 4) {
+ div = 4;
+ }
+ return clk->parent->rate / div;
+}
+
+static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate)
+{
+ int div, reg;
+ div = clk->parent->rate / rate;
+
+ if (div > 4 || div < 1 || ((clk->parent->rate / div) != rate)) {
+ return -EINVAL;
+ }
+ div--;
+
+ reg = (CSCR() & ~CCM_CSCR_ARM_MASK) | (div << CCM_CSCR_ARM_OFFSET);
+ __raw_writel(reg, CCM_CSCR);
+ clk->rate = rate;
+ return 0;
+}
+
+static void _clk_cpu_recalc(struct clk *clk)
+{
+ unsigned long div;
+
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) {
+ div = (CSCR() & CCM_CSCR_ARM_MASK) >> CCM_CSCR_ARM_OFFSET;
+ } else {
+ div = (CSCR() & CCM_CSCR_PRESC_MASK) >> CCM_CSCR_PRESC_OFFSET;
+ }
+
+ clk->rate = clk->parent->rate / (div + 1);
+}
+
+static void _clk_ahb_recalc(struct clk *clk)
+{
+ unsigned long bclk_pdf;
+
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) {
+ bclk_pdf = (CSCR() & CCM_CSCR_AHB_MASK) >> CCM_CSCR_AHB_OFFSET;
+ } else {
+ bclk_pdf =
+ (CSCR() & CCM_CSCR_BCLK_MASK) >> CCM_CSCR_BCLK_OFFSET;
+ }
+ clk->rate = clk->parent->rate / (bclk_pdf + 1);
+}
+
+static void _clk_perclkx_recalc(struct clk *clk)
+{
+ unsigned long perclk_pdf;
+
+ if (clk->id < 0 || clk->id > 3)
+ return;
+
+ perclk_pdf = (PCDR1() >> (clk->id << 3)) & CCM_PCDR1_PERDIV1_MASK;
+
+ clk->rate = clk->parent->rate / (perclk_pdf + 1);
+}
+
+static unsigned long _clk_perclkx_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ if (div > 64) {
+ div = 64;
+ }
+
+ return clk->parent->rate / div;
+}
+
+static int _clk_perclkx_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+
+ if (clk->id < 0 || clk->id > 3)
+ return -EINVAL;
+
+ div = clk->parent->rate / rate;
+ if (div > 64 || div < 1 || ((clk->parent->rate / div) != rate)) {
+ return -EINVAL;
+ }
+ div--;
+
+ reg =
+ __raw_readl(CCM_PCDR1) & ~(CCM_PCDR1_PERDIV1_MASK <<
+ (clk->id << 3));
+ reg |= div << (clk->id << 3);
+ __raw_writel(reg, CCM_PCDR1);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+static void _clk_usb_recalc(struct clk *clk)
+{
+ unsigned long usb_pdf;
+
+ usb_pdf = (CSCR() & CCM_CSCR_USB_MASK) >> CCM_CSCR_USB_OFFSET;
+
+ clk->rate = clk->parent->rate / (usb_pdf + 1);
+}
+
+static void _clk_ssi1_recalc(struct clk *clk)
+{
+ unsigned long ssi1_pdf;
+
+ ssi1_pdf = (PCDR0() & CCM_PCDR0_SSI1BAUDDIV_MASK) >>
+ CCM_PCDR0_SSI1BAUDDIV_OFFSET;
+
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) {
+ ssi1_pdf += 4;
+ } else {
+ ssi1_pdf = (ssi1_pdf < 2) ? 124 : ssi1_pdf;
+ }
+
+ clk->rate = 2 * clk->parent->rate / ssi1_pdf;
+}
+
+static void _clk_ssi2_recalc(struct clk *clk)
+{
+ unsigned long ssi2_pdf;
+
+ ssi2_pdf = (PCDR0() & CCM_PCDR0_SSI2BAUDDIV_MASK) >>
+ CCM_PCDR0_SSI2BAUDDIV_OFFSET;
+
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) {
+ ssi2_pdf += 4;
+ } else {
+ ssi2_pdf = (ssi2_pdf < 2) ? 124 : ssi2_pdf;
+ }
+
+ clk->rate = 2 * clk->parent->rate / ssi2_pdf;
+}
+
+static void _clk_nfc_recalc(struct clk *clk)
+{
+ unsigned long nfc_pdf;
+
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) {
+ nfc_pdf =
+ (PCDR0() & CCM_PCDR0_NFCDIV2_MASK) >>
+ CCM_PCDR0_NFCDIV2_OFFSET;
+ } else {
+ nfc_pdf =
+ (PCDR0() & CCM_PCDR0_NFCDIV_MASK) >>
+ CCM_PCDR0_NFCDIV_OFFSET;
+ }
+
+ clk->rate = clk->parent->rate / (nfc_pdf + 1);
+}
+
+static void _clk_vpu_recalc(struct clk *clk)
+{
+ unsigned long vpu_pdf;
+
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) {
+ vpu_pdf =
+ (PCDR0() & CCM_PCDR0_VPUDIV2_MASK) >>
+ CCM_PCDR0_VPUDIV2_OFFSET;
+ vpu_pdf += 4;
+ } else {
+ vpu_pdf =
+ (PCDR0() & CCM_PCDR0_VPUDIV_MASK) >>
+ CCM_PCDR0_VPUDIV_OFFSET;
+ vpu_pdf = (vpu_pdf < 2) ? 124 : vpu_pdf;
+ }
+ clk->rate = 2 * clk->parent->rate / vpu_pdf;
+}
+
+static void _clk_ipg_recalc(struct clk *clk)
+{
+ unsigned long ipg_pdf;
+
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) {
+ ipg_pdf = 1;
+ } else {
+ ipg_pdf = (CSCR() & CCM_CSCR_IPDIV) >> CCM_CSCR_IPDIV_OFFSET;
+ }
+
+ clk->rate = clk->parent->rate / (ipg_pdf + 1);
+}
+
+static unsigned long _clk_parent_round_rate(struct clk *clk, unsigned long rate)
+{
+ return clk->parent->round_rate(clk->parent, rate);
+}
+
+static int _clk_parent_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret;
+ if ((ret = clk->parent->set_rate(clk->parent, rate)) == 0)
+ clk->rate = rate;
+ return ret;
+}
+
+static struct clk ckih_clk = {
+ .name = "ckih",
+ .rate = 0, /* determined at boot time (26 or 27 MHz) */
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ckil_clk = {
+ .name = "ckil",
+ .rate = CKIL_CLK_FREQ,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk mpll_clk = {
+ .name = "mpll",
+ .parent = &ckih_clk,
+ .recalc = _clk_pll_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk mpll_main_clk[] = {
+ {
+ /* For i.MX27 TO2, it is the MPLL path 1 of ARM core
+ * It provide the clock source whose rate is same as MPLL
+ */
+ .name = "mpll_main",
+ .id = 0,
+ .parent = &mpll_clk,
+ .recalc = _clk_mpll_main_recalc,},
+ {
+ /* For i.MX27 TO2, it is the MPLL path 1 of ARM core
+ * It provide the clock source whose rate is same as MPLL
+ */
+ .name = "mpll_main",
+ .id = 1,
+ .parent = &mpll_clk,
+ .recalc = _clk_mpll_main_recalc,}
+};
+
+static struct clk spll_clk = {
+ .name = "spll",
+ .parent = &ckih_clk,
+ .recalc = _clk_pll_recalc,
+ .enable = _clk_spll_enable,
+ .disable = _clk_spll_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk cpu_clk = {
+ .name = "cpu_clk",
+ .parent = &mpll_main_clk[1],
+ .set_parent = _clk_cpu_set_parent,
+ .round_rate = _clk_cpu_round_rate,
+ .set_rate = _clk_cpu_set_rate,
+ .recalc = _clk_cpu_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ahb_clk = {
+ .name = "ahb_clk",
+ .parent = &mpll_main_clk[1],
+ .recalc = _clk_ahb_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ipg_clk = {
+ .name = "ipg_clk",
+ .parent = &ahb_clk,
+ .recalc = _clk_ipg_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk per_clk[] = {
+ {
+ .name = "per_clk",
+ .id = 0,
+ .parent = &mpll_main_clk[1],
+ .recalc = _clk_perclkx_recalc,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_PERCLK1_OFFSET,
+ .disable = _clk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_clk",
+ .id = 1,
+ .parent = &mpll_main_clk[1],
+ .recalc = _clk_perclkx_recalc,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_PERCLK2_OFFSET,
+ .disable = _clk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_clk",
+ .id = 2,
+ .parent = &mpll_main_clk[1],
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_PERCLK3_OFFSET,
+ .disable = _clk_disable,
+ .flags = RATE_PROPAGATES,},
+ {
+ .name = "per_clk",
+ .id = 3,
+ .parent = &mpll_main_clk[1],
+ .round_rate = _clk_perclkx_round_rate,
+ .set_rate = _clk_perclkx_set_rate,
+ .recalc = _clk_perclkx_recalc,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_PERCLK4_OFFSET,
+ .disable = _clk_disable,
+ .flags = RATE_PROPAGATES,},
+};
+
+struct clk uart1_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 0,
+ .parent = &per_clk[0],
+ .secondary = &uart1_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_UART1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk uart2_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 1,
+ .parent = &per_clk[0],
+ .secondary = &uart2_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_UART2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk uart3_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 2,
+ .parent = &per_clk[0],
+ .secondary = &uart3_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_UART3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk uart4_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 3,
+ .parent = &per_clk[0],
+ .secondary = &uart4_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 3,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_UART4_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk uart5_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 4,
+ .parent = &per_clk[0],
+ .secondary = &uart5_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 4,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_UART5_OFFSET,
+ .disable = _clk_disable,},
+};
+
+struct clk uart6_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 5,
+ .parent = &per_clk[0],
+ .secondary = &uart6_clk[1],},
+ {
+ .name = "uart_ipg_clk",
+ .id = 5,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_UART6_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk gpt1_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 0,
+ .parent = &per_clk[0],
+ .secondary = &gpt1_clk[1],},
+ {
+ .name = "gpt_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_GPT1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk gpt2_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 1,
+ .parent = &per_clk[0],
+ .secondary = &gpt2_clk[1],},
+ {
+ .name = "gpt_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_GPT2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk gpt3_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 2,
+ .parent = &per_clk[0],
+ .secondary = &gpt3_clk[1],},
+ {
+ .name = "gpt_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_GPT3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk gpt4_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 3,
+ .parent = &per_clk[0],
+ .secondary = &gpt4_clk[1],},
+ {
+ .name = "gpt_ipg_clk",
+ .id = 3,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_GPT4_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk gpt5_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 4,
+ .parent = &per_clk[0],
+ .secondary = &gpt5_clk[1],},
+ {
+ .name = "gpt_ipg_clk",
+ .id = 4,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_GPT5_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk gpt6_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 5,
+ .parent = &per_clk[0],
+ .secondary = &gpt6_clk[1],},
+ {
+ .name = "gpt_ipg_clk",
+ .id = 5,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_GPT6_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk pwm_clk[] = {
+ {
+ .name = "pwm_clk",
+ .parent = &per_clk[0],
+ .secondary = &pwm_clk[1],},
+ {
+ .name = "pwm_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_PWM_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk sdhc1_clk[] = {
+ {
+ .name = "sdhc_clk",
+ .id = 0,
+ .parent = &per_clk[1],
+ .secondary = &sdhc1_clk[1],},
+ {
+ .name = "sdhc_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_SDHC1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk sdhc2_clk[] = {
+ {
+ .name = "sdhc_clk",
+ .id = 1,
+ .parent = &per_clk[1],
+ .secondary = &sdhc2_clk[1],},
+ {
+ .name = "sdhc_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_SDHC2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk sdhc3_clk[] = {
+ {
+ .name = "sdhc_clk",
+ .id = 2,
+ .parent = &per_clk[1],
+ .secondary = &sdhc3_clk[1],},
+ {
+ .name = "sdhc_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_SDHC3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk cspi1_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 0,
+ .parent = &per_clk[1],
+ .secondary = &cspi1_clk[1],},
+ {
+ .name = "cspi_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_CSPI1_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk cspi2_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 1,
+ .parent = &per_clk[1],
+ .secondary = &cspi2_clk[1],},
+ {
+ .name = "cspi_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_CSPI2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk cspi3_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 2,
+ .parent = &per_clk[1],
+ .secondary = &cspi3_clk[1],},
+ {
+ .name = "cspi_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_CSPI3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk lcdc_clk[] = {
+ {
+ .name = "lcdc_clk",
+ .parent = &per_clk[2],
+ .secondary = &lcdc_clk[1],
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,},
+ {
+ .name = "lcdc_ipg_clk",
+ .parent = &ipg_clk,
+ .secondary = &lcdc_clk[2],
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_LCDC_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "lcdc_ahb_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_HCLK_LCDC_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk csi_clk[] = {
+ {
+ .name = "csi_perclk",
+ .parent = &per_clk[3],
+ .secondary = &csi_clk[1],
+ .round_rate = _clk_parent_round_rate,
+ .set_rate = _clk_parent_set_rate,},
+ {
+ .name = "csi_ahb_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_HCLK_CSI_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk usb_clk[] = {
+ {
+ .name = "usb_clk",
+ .parent = &spll_clk,
+ .recalc = _clk_usb_recalc,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_USBOTG_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "usb_ahb_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_HCLK_USBOTG_OFFSET,
+ .disable = _clk_disable,}
+};
+
+static struct clk ssi1_clk[] = {
+ {
+ .name = "ssi_clk",
+ .id = 0,
+ .parent = &mpll_main_clk[1],
+ .secondary = &ssi1_clk[1],
+ .recalc = _clk_ssi1_recalc,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_SSI1_BAUD_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "ssi_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_SSI1_IPG_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk ssi2_clk[] = {
+ {
+ .name = "ssi_clk",
+ .id = 1,
+ .parent = &mpll_main_clk[1],
+ .secondary = &ssi2_clk[1],
+ .recalc = _clk_ssi2_recalc,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_SSI2_BAUD_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "ssi_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_SSI2_IPG_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk nfc_clk = {
+ .name = "nfc_clk",
+ .parent = &cpu_clk,
+ .recalc = _clk_nfc_recalc,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_NFC_BAUD_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk vpu_clk = {
+ .name = "vpu_clk",
+ .parent = &mpll_main_clk[1],
+ .recalc = _clk_vpu_recalc,
+ .enable = _clk_vpu_enable,
+ .disable = _clk_vpu_disable,
+};
+
+static struct clk dma_clk = {
+ .name = "dma_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_dma_enable,
+ .disable = _clk_dma_disable,
+};
+
+static struct clk rtic_clk = {
+ .name = "rtic_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_rtic_enable,
+ .disable = _clk_rtic_disable,
+};
+
+static struct clk brom_clk = {
+ .name = "brom_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_HCLK_BROM_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk emma_clk = {
+ .name = "emma_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_emma_enable,
+ .disable = _clk_emma_disable,
+};
+
+static struct clk slcdc_clk = {
+ .name = "slcdc_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_slcdc_enable,
+ .disable = _clk_slcdc_disable,
+};
+
+static struct clk fec_clk = {
+ .name = "fec_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_fec_enable,
+ .disable = _clk_fec_disable,
+};
+
+static struct clk emi_clk = {
+ .name = "emi_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_HCLK_EMI_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk sahara2_clk = {
+ .name = "sahara_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_sahara2_enable,
+ .disable = _clk_sahara2_disable,
+};
+
+static struct clk ata_clk = {
+ .name = "ata_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_HCLK_ATA_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk mstick1_clk = {
+ .name = "mstick1_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_mstick1_enable,
+ .disable = _clk_mstick1_disable,
+};
+
+static struct clk wdog_clk = {
+ .name = "wdog_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR1_WDT_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk gpio_clk = {
+ .name = "gpio_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR1,
+ .enable_shift = CCM_PCCR0_GPIO_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk i2c_clk[] = {
+ {
+ .name = "i2c_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_I2C1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "i2c_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_I2C2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk iim_clk = {
+ .name = "iim_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_IIM_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk kpp_clk = {
+ .name = "kpp_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_KPP_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk owire_clk = {
+ .name = "owire_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_OWIRE_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk rtc_clk = {
+ .name = "rtc_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_RTC_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk scc_clk = {
+ .name = "scc_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = CCM_PCCR0,
+ .enable_shift = CCM_PCCR0_SCC_OFFSET,
+ .disable = _clk_disable,
+};
+
+static unsigned long _clk_clko_round_rate(struct clk *clk, unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ if (div > 8) {
+ div = 8;
+ }
+
+ return clk->parent->rate / div;
+}
+
+static int _clk_clko_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+
+ div = clk->parent->rate / rate;
+
+ if (div > 8 || div < 1 || ((clk->parent->rate / div) != rate)) {
+ return -EINVAL;
+ }
+ div--;
+
+ reg = __raw_readl(CCM_PCDR0) & ~CCM_PCDR0_CLKODIV_MASK;
+ reg |= div << CCM_PCDR0_CLKODIV_OFFSET;
+ __raw_writel(reg, CCM_PCDR0);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+static void _clk_clko_recalc(struct clk *clk)
+{
+ u32 div;
+
+ div = __raw_readl(CCM_PCDR0) & CCM_PCDR0_CLKODIV_MASK >>
+ CCM_PCDR0_CLKODIV_OFFSET;
+ div++;
+
+ clk->rate = clk->parent->rate / div;
+}
+
+static int _clk_clko_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(CCM_CCSR) & ~CCM_CCSR_CLKOSEL_MASK;
+
+ if (parent == &ckil_clk) {
+ reg |= 0 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &ckih_clk) {
+ reg |= 2 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == mpll_clk.parent) {
+ reg |= 3 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == spll_clk.parent) {
+ reg |= 4 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &mpll_clk) {
+ reg |= 5 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &spll_clk) {
+ reg |= 6 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &cpu_clk) {
+ reg |= 7 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &ahb_clk) {
+ reg |= 8 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &ipg_clk) {
+ reg |= 9 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &per_clk[0]) {
+ reg |= 0xA << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &per_clk[1]) {
+ reg |= 0xB << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &per_clk[2]) {
+ reg |= 0xC << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &per_clk[3]) {
+ reg |= 0xD << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &ssi1_clk[0]) {
+ reg |= 0xE << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &ssi2_clk[0]) {
+ reg |= 0xF << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &nfc_clk) {
+ reg |= 0x10 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &mstick1_clk) {
+ reg |= 0x11 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &vpu_clk) {
+ reg |= 0x12 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else if (parent == &usb_clk[0]) {
+ reg |= 0x15 << CCM_CCSR_CLKOSEL_OFFSET;
+ } else {
+ return -EINVAL;
+ }
+
+ __raw_writel(reg, CCM_CCSR);
+
+ return 0;
+}
+
+static int _clk_clko_enable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(CCM_PCDR0) | CCM_PCDR0_CLKO_EN;
+ __raw_writel(reg, CCM_PCDR0);
+
+ return 0;
+}
+
+static void _clk_clko_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(CCM_PCDR0) & ~CCM_PCDR0_CLKO_EN;
+ __raw_writel(reg, CCM_PCDR0);
+}
+
+static struct clk clko_clk = {
+ .name = "clko_clk",
+ .recalc = _clk_clko_recalc,
+ .set_rate = _clk_clko_set_rate,
+ .round_rate = _clk_clko_round_rate,
+ .set_parent = _clk_clko_set_parent,
+ .enable = _clk_clko_enable,
+ .disable = _clk_clko_disable,
+};
+
+static struct clk *mxc_clks[] = {
+ &ckih_clk,
+ &ckil_clk,
+ &mpll_clk,
+ &mpll_main_clk[0],
+ &mpll_main_clk[1],
+ &spll_clk,
+ &cpu_clk,
+ &ahb_clk,
+ &ipg_clk,
+ &per_clk[0],
+ &per_clk[1],
+ &per_clk[2],
+ &per_clk[3],
+ &clko_clk,
+ &uart1_clk[0],
+ &uart1_clk[1],
+ &uart2_clk[0],
+ &uart2_clk[1],
+ &uart3_clk[0],
+ &uart3_clk[1],
+ &uart4_clk[0],
+ &uart4_clk[1],
+ &uart5_clk[0],
+ &uart5_clk[1],
+ &uart6_clk[0],
+ &uart6_clk[1],
+ &gpt1_clk[0],
+ &gpt1_clk[1],
+ &gpt2_clk[0],
+ &gpt2_clk[1],
+ &gpt3_clk[0],
+ &gpt3_clk[1],
+ &gpt4_clk[0],
+ &gpt4_clk[1],
+ &gpt5_clk[0],
+ &gpt5_clk[1],
+ &gpt6_clk[0],
+ &gpt6_clk[1],
+ &pwm_clk[0],
+ &pwm_clk[1],
+ &sdhc1_clk[0],
+ &sdhc1_clk[1],
+ &sdhc2_clk[0],
+ &sdhc2_clk[1],
+ &sdhc3_clk[0],
+ &sdhc3_clk[1],
+ &cspi1_clk[0],
+ &cspi1_clk[1],
+ &cspi2_clk[0],
+ &cspi2_clk[1],
+ &cspi3_clk[0],
+ &cspi3_clk[1],
+ &lcdc_clk[0],
+ &lcdc_clk[1],
+ &lcdc_clk[2],
+ &csi_clk[0],
+ &csi_clk[1],
+ &usb_clk[0],
+ &usb_clk[1],
+ &ssi1_clk[0],
+ &ssi1_clk[1],
+ &ssi2_clk[0],
+ &ssi2_clk[1],
+ &nfc_clk,
+ &vpu_clk,
+ &dma_clk,
+ &rtic_clk,
+ &brom_clk,
+ &emma_clk,
+ &slcdc_clk,
+ &fec_clk,
+ &emi_clk,
+ &sahara2_clk,
+ &ata_clk,
+ &mstick1_clk,
+ &wdog_clk,
+ &gpio_clk,
+ &i2c_clk[0],
+ &i2c_clk[1],
+ &iim_clk,
+ &kpp_clk,
+ &owire_clk,
+ &rtc_clk,
+ &scc_clk,
+};
+
+static void probe_mxc_clocks(void)
+{
+ int i;
+
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) {
+ if (CSCR() & 0x8000) {
+ cpu_clk.parent = &mpll_main_clk[0];
+ }
+
+ if (!(CSCR() & 0x00800000)) {
+ ssi2_clk[0].parent = &spll_clk;
+ }
+
+ if (!(CSCR() & 0x00400000)) {
+ ssi1_clk[0].parent = &spll_clk;
+ }
+
+ if (!(CSCR() & 0x00200000)) {
+ vpu_clk.parent = &spll_clk;
+ }
+ } else {
+ cpu_clk.parent = &mpll_clk;
+ cpu_clk.set_parent = NULL;
+ cpu_clk.round_rate = NULL;
+ cpu_clk.set_rate = NULL;
+ ahb_clk.parent = &mpll_clk;
+
+ for (i = 0; i < sizeof(per_clk) / sizeof(per_clk[0]); i++) {
+ per_clk[i].parent = &mpll_clk;
+ }
+
+ ssi1_clk[0].parent = &mpll_clk;
+ ssi2_clk[0].parent = &mpll_clk;
+
+ vpu_clk.parent = &mpll_clk;
+ }
+}
+
+extern void propagate_rate(struct clk *tclk);
+
+int __init mxc_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2)
+{
+ u32 cscr;
+ struct clk **clkp;
+
+ /* Determine which high frequency clock source is coming in */
+ ckih_clk.rate = ckih1;
+
+ if (CSCR() & CCM_CSCR_MCU) {
+ mpll_clk.parent = &ckih_clk;
+ } else {
+ mpll_clk.parent = &ckil_clk;
+ }
+
+ probe_mxc_clocks();
+
+ for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++) {
+ if (*clkp == &mpll_main_clk[0] || *clkp == &mpll_main_clk[1]) {
+ if (cpu_is_mx27_rev(CHIP_REV_1_0) == 1)
+ continue;
+ }
+ clk_register(*clkp);
+ }
+
+ /* Turn off all possible clocks */
+ __raw_writel(CCM_PCCR0_GPT1_MASK, CCM_PCCR0);
+ __raw_writel(CCM_PCCR1_PERCLK1_MASK | CCM_PCCR1_HCLK_EMI_MASK,
+ CCM_PCCR1);
+ spll_clk.disable(&spll_clk);
+
+ cscr = CSCR();
+ if (cscr & CCM_CSCR_MCU) {
+ mpll_clk.parent = &ckih_clk;
+ } else {
+ mpll_clk.parent = &ckil_clk;
+ }
+ if (cscr & CCM_CSCR_SP) {
+ spll_clk.parent = &ckih_clk;
+ } else {
+ spll_clk.parent = &ckil_clk;
+ }
+
+ pr_info("Clock input source is %ld\n", ckih_clk.rate);
+
+ /* This will propagate to all children and init all the clock rates */
+ propagate_rate(&ckih_clk);
+ propagate_rate(&ckil_clk);
+
+ clk_enable(&emi_clk);
+ clk_enable(&gpio_clk);
+ clk_enable(&iim_clk);
+ clk_enable(&gpt1_clk[0]);
+
+ return 0;
+}
diff --git a/arch/arm/mach-mx27/cpu.c b/arch/arm/mach-mx27/cpu.c
new file mode 100644
index 000000000000..55d4a4c2d8e8
--- /dev/null
+++ b/arch/arm/mach-mx27/cpu.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2001 Deep Blue Solutions Ltd.
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/*!
+ * @file mach-mx27/cpu.c
+ *
+ * @brief This file contains the CPU initialization code.
+ *
+ * @ingroup MSL_MX27
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+
+/*!
+ * CPU initialization. It is called by fixup_mxc_board()
+ */
+void __init mxc_cpu_init(void)
+{
+ if (!system_rev) {
+ mxc_set_system_rev(0x27, CHIP_REV_2_0);
+ }
+}
diff --git a/arch/arm/mach-mx27/crm_regs.h b/arch/arm/mach-mx27/crm_regs.h
new file mode 100644
index 000000000000..3a0ab35d9be7
--- /dev/null
+++ b/arch/arm/mach-mx27/crm_regs.h
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX27_CRM_REGS_H__
+#define __ARCH_ARM_MACH_MX27_CRM_REGS_H__
+
+#include <mach/hardware.h>
+
+#define SYSCTRL_BASE IO_ADDRESS(SYSCTRL_BASE_ADDR)
+#define MXC_CCM_BASE ((char *)IO_ADDRESS(CCM_BASE_ADDR))
+
+/* Register offsets */
+#define CCM_CSCR (MXC_CCM_BASE + 0x0)
+#define CCM_MPCTL0 (MXC_CCM_BASE + 0x4)
+#define CCM_MPCTL1 (MXC_CCM_BASE + 0x8)
+#define CCM_SPCTL0 (MXC_CCM_BASE + 0xC)
+#define CCM_SPCTL1 (MXC_CCM_BASE + 0x10)
+#define CCM_OSC26MCTL (MXC_CCM_BASE + 0x14)
+#define CCM_PCDR0 (MXC_CCM_BASE + 0x18)
+#define CCM_PCDR1 (MXC_CCM_BASE + 0x1c)
+#define CCM_PCCR0 (MXC_CCM_BASE + 0x20)
+#define CCM_PCCR1 (MXC_CCM_BASE + 0x24)
+#define CCM_CCSR (MXC_CCM_BASE + 0x28)
+#define CCM_PMCTL (MXC_CCM_BASE + 0x2c)
+#define CCM_PMCOUNT (MXC_CCM_BASE + 0x30)
+#define CCM_WKGDCTL (MXC_CCM_BASE + 0x34)
+#define MXC_CCM_PMCR0 (SYSCTRL_BASE + 0x60)
+#define MXC_CCM_DCVR0 (SYSCTRL_BASE + 0x64)
+#define MXC_CCM_DCVR1 (SYSCTRL_BASE + 0x68)
+#define MXC_CCM_DCVR2 (SYSCTRL_BASE + 0x72)
+#define MXC_CCM_DCVR3 (SYSCTRL_BASE + 0x76)
+#define MXC_CCM_PMCR0_DPTEN 0x00000001
+#define MXC_CCM_DIE 0x00000002
+#define MXC_CCM_DIM 0x0000000C
+#define MXC_CCM_DCR 0x00000200
+#define MXC_CCM_PMCR0_DRCE0 0x00000010
+#define MXC_CCM_PMCR0_DRCE1 0x00000020
+#define MXC_CCM_PMCR0_DRCE2 0x00000040
+#define MXC_CCM_PMCR0_DRCE3 0x00000080
+#define MXC_CCM_PMCR0_PTVAIM MXC_CCM_DIM
+
+#define CCM_CSCR_USB_OFFSET 28
+#define CCM_CSCR_USB_MASK (0x7 << 28)
+#define CCM_CSCR_SD_OFFSET 24
+#define CCM_CSCR_SD_MASK (0x3 << 24)
+#define CCM_CSCR_SSI2 (1 << 23)
+#define CCM_CSCR_SSI2_OFFSET 23
+#define CCM_CSCR_SSI1 (1 << 22)
+#define CCM_CSCR_SSI1_OFFSET 22
+#define CCM_CSCR_VPU (1 << 21)
+#define CCM_CSCR_VPU_OFFSET 21
+#define CCM_CSCR_MSHC (1 << 20)
+#define CCM_CSCR_SPLLRES (1 << 19)
+#define CCM_CSCR_MPLLRES (1 << 18)
+#define CCM_CSCR_SP (1 << 17)
+#define CCM_CSCR_MCU (1 << 16)
+/* CCM_CSCR_ARM_xxx just be avaliable on i.MX27 TO2*/
+#define CCM_CSCR_ARM_SRC (1 << 15)
+#define CCM_CSCR_ARM_OFFSET 12
+#define CCM_CSCR_ARM_MASK (0x3 << 12)
+/* CCM_CSCR_ARM_xxx just be avaliable on i.MX27 TO2*/
+#define CCM_CSCR_PRESC_OFFSET 13
+#define CCM_CSCR_PRESC_MASK (0x7 << 13)
+#define CCM_CSCR_BCLK_OFFSET 9
+#define CCM_CSCR_BCLK_MASK (0xf << 9)
+#define CCM_CSCR_IPDIV_OFFSET 8
+#define CCM_CSCR_IPDIV (1 << 8)
+/* CCM_CSCR_AHB_xxx just be avaliable on i.MX27 TO2*/
+#define CCM_CSCR_AHB_OFFSET 8
+#define CCM_CSCR_AHB_MASK (0x3 << 8)
+/* CCM_CSCR_AHB_xxx just be avaliable on i.MX27 TO2*/
+#define CCM_CSCR_OSC26MDIV (1 << 4)
+#define CCM_CSCR_OSC26M (1 << 3)
+#define CCM_CSCR_FPM (1 << 2)
+#define CCM_CSCR_SPEN (1 << 1)
+#define CCM_CSCR_MPEN 1
+
+#define CCM_MPCTL0_CPLM (1 << 31)
+#define CCM_MPCTL0_PD_OFFSET 26
+#define CCM_MPCTL0_PD_MASK (0xf << 26)
+#define CCM_MPCTL0_MFD_OFFSET 16
+#define CCM_MPCTL0_MFD_MASK (0x3ff << 16)
+#define CCM_MPCTL0_MFI_OFFSET 10
+#define CCM_MPCTL0_MFI_MASK (0xf << 10)
+#define CCM_MPCTL0_MFN_OFFSET 0
+#define CCM_MPCTL0_MFN_MASK 0x3ff
+
+#define CCM_MPCTL1_LF (1 << 15)
+#define CCM_MPCTL1_BRMO (1 << 6)
+
+#define CCM_SPCTL0_CPLM (1 << 31)
+#define CCM_SPCTL0_PD_OFFSET 26
+#define CCM_SPCTL0_PD_MASK (0xf << 26)
+#define CCM_SPCTL0_MFD_OFFSET 16
+#define CCM_SPCTL0_MFD_MASK (0x3ff << 16)
+#define CCM_SPCTL0_MFI_OFFSET 10
+#define CCM_SPCTL0_MFI_MASK (0xf << 10)
+#define CCM_SPCTL0_MFN_OFFSET 0
+#define CCM_SPCTL0_MFN_MASK 0x3ff
+
+#define CCM_SPCTL1_LF (1 << 15)
+#define CCM_SPCTL1_BRMO (1 << 6)
+
+#define CCM_OSC26MCTL_PEAK_OFFSET 16
+#define CCM_OSC26MCTL_PEAK_MASK (0x3 << 16)
+#define CCM_OSC26MCTL_AGC_OFFSET 8
+#define CCM_OSC26MCTL_AGC_MASK (0x3f << 8)
+#define CCM_OSC26MCTL_ANATEST_OFFSET 0
+#define CCM_OSC26MCTL_ANATEST_MASK 0x3f
+
+#define CCM_PCDR0_SSI2BAUDDIV_OFFSET 26
+#define CCM_PCDR0_SSI2BAUDDIV_MASK (0x3f << 26)
+#define CCM_PCDR0_CLKO_EN 25
+#define CCM_PCDR0_CLKODIV_OFFSET 22
+#define CCM_PCDR0_CLKODIV_MASK (0x7 << 22)
+#define CCM_PCDR0_SSI1BAUDDIV_OFFSET 16
+#define CCM_PCDR0_SSI1BAUDDIV_MASK (0x3f << 16)
+/*The difinition for i.MX27 TO2*/
+#define CCM_PCDR0_VPUDIV2_OFFSET 10
+#define CCM_PCDR0_VPUDIV2_MASK (0x3f << 10)
+#define CCM_PCDR0_NFCDIV2_OFFSET 6
+#define CCM_PCDR0_NFCDIV2_MASK (0xf << 6)
+#define CCM_PCDR0_MSHCDIV2_MASK 0x3f
+/*The difinition for i.MX27 TO2*/
+#define CCM_PCDR0_NFCDIV_OFFSET 12
+#define CCM_PCDR0_NFCDIV_MASK (0xf << 12)
+#define CCM_PCDR0_VPUDIV_OFFSET 8
+#define CCM_PCDR0_VPUDIV_MASK (0xf << 8)
+#define CCM_PCDR0_MSHCDIV_OFFSET 0
+#define CCM_PCDR0_MSHCDIV_MASK 0x1f
+
+#define CCM_PCDR1_PERDIV4_OFFSET 24
+#define CCM_PCDR1_PERDIV4_MASK (0x3f << 24)
+#define CCM_PCDR1_PERDIV3_OFFSET 16
+#define CCM_PCDR1_PERDIV3_MASK (0x3f << 16)
+#define CCM_PCDR1_PERDIV2_OFFSET 8
+#define CCM_PCDR1_PERDIV2_MASK (0x3f << 8)
+#define CCM_PCDR1_PERDIV1_OFFSET 0
+#define CCM_PCDR1_PERDIV1_MASK 0x3f
+
+#define CCM_PCCR0_CSPI1_OFFSET 31
+#define CCM_PCCR0_CSPI1_MASK (1 << 31)
+#define CCM_PCCR0_CSPI2_OFFSET 30
+#define CCM_PCCR0_CSPI2_MASK (1 << 30)
+#define CCM_PCCR0_CSPI3_OFFSET 29
+#define CCM_PCCR0_CSPI3_MASK (1 << 29)
+#define CCM_PCCR0_DMA_OFFSET 28
+#define CCM_PCCR0_DMA_MASK (1 << 28)
+#define CCM_PCCR0_EMMA_OFFSET 27
+#define CCM_PCCR0_EMMA_MASK (1 << 27)
+#define CCM_PCCR0_FEC_OFFSET 26
+#define CCM_PCCR0_FEC_MASK (1 << 26)
+#define CCM_PCCR0_GPIO_OFFSET 25
+#define CCM_PCCR0_GPIO_MASK (1 << 25)
+#define CCM_PCCR0_GPT1_OFFSET 24
+#define CCM_PCCR0_GPT1_MASK (1 << 24)
+#define CCM_PCCR0_GPT2_OFFSET 23
+#define CCM_PCCR0_GPT2_MASK (1 << 23)
+#define CCM_PCCR0_GPT3_OFFSET 22
+#define CCM_PCCR0_GPT3_MASK (1 << 22)
+#define CCM_PCCR0_GPT4_OFFSET 21
+#define CCM_PCCR0_GPT4_MASK (1 << 21)
+#define CCM_PCCR0_GPT5_OFFSET 20
+#define CCM_PCCR0_GPT5_MASK (1 << 20)
+#define CCM_PCCR0_GPT6_OFFSET 19
+#define CCM_PCCR0_GPT6_MASK (1 << 19)
+#define CCM_PCCR0_I2C1_OFFSET 18
+#define CCM_PCCR0_I2C1_MASK (1 << 18)
+#define CCM_PCCR0_I2C2_OFFSET 17
+#define CCM_PCCR0_I2C2_MASK (1 << 17)
+#define CCM_PCCR0_IIM_OFFSET 16
+#define CCM_PCCR0_IIM_MASK (1 << 16)
+#define CCM_PCCR0_KPP_OFFSET 15
+#define CCM_PCCR0_KPP_MASK (1 << 15)
+#define CCM_PCCR0_LCDC_OFFSET 14
+#define CCM_PCCR0_LCDC_MASK (1 << 14)
+#define CCM_PCCR0_MSHC_OFFSET 13
+#define CCM_PCCR0_MSHC_MASK (1 << 13)
+#define CCM_PCCR0_OWIRE_OFFSET 12
+#define CCM_PCCR0_OWIRE_MASK (1 << 12)
+#define CCM_PCCR0_PWM_OFFSET 11
+#define CCM_PCCR0_PWM_MASK (1 << 11)
+#define CCM_PCCR0_RTC_OFFSET 9
+#define CCM_PCCR0_RTC_MASK (1 << 9)
+#define CCM_PCCR0_RTIC_OFFSET 8
+#define CCM_PCCR0_RTIC_MASK (1 << 8)
+#define CCM_PCCR0_SAHARA_OFFSET 7
+#define CCM_PCCR0_SAHARA_MASK (1 << 7)
+#define CCM_PCCR0_SCC_OFFSET 6
+#define CCM_PCCR0_SCC_MASK (1 << 6)
+#define CCM_PCCR0_SDHC1_OFFSET 5
+#define CCM_PCCR0_SDHC1_MASK (1 << 5)
+#define CCM_PCCR0_SDHC2_OFFSET 4
+#define CCM_PCCR0_SDHC2_MASK (1 << 4)
+#define CCM_PCCR0_SDHC3_OFFSET 3
+#define CCM_PCCR0_SDHC3_MASK (1 << 3)
+#define CCM_PCCR0_SLCDC_OFFSET 2
+#define CCM_PCCR0_SLCDC_MASK (1 << 2)
+#define CCM_PCCR0_SSI1_IPG_OFFSET 1
+#define CCM_PCCR0_SSI1_IPG_MASK (1 << 1)
+#define CCM_PCCR0_SSI2_IPG_OFFSET 0
+#define CCM_PCCR0_SSI2_IPG_MASK (1 << 0)
+
+#define CCM_PCCR1_UART1_OFFSET 31
+#define CCM_PCCR1_UART1_MASK (1 << 31)
+#define CCM_PCCR1_UART2_OFFSET 30
+#define CCM_PCCR1_UART2_MASK (1 << 30)
+#define CCM_PCCR1_UART3_OFFSET 29
+#define CCM_PCCR1_UART3_MASK (1 << 29)
+#define CCM_PCCR1_UART4_OFFSET 28
+#define CCM_PCCR1_UART4_MASK (1 << 28)
+#define CCM_PCCR1_UART5_OFFSET 27
+#define CCM_PCCR1_UART5_MASK (1 << 27)
+#define CCM_PCCR1_UART6_OFFSET 26
+#define CCM_PCCR1_UART6_MASK (1 << 26)
+#define CCM_PCCR1_USBOTG_OFFSET 25
+#define CCM_PCCR1_USBOTG_MASK (1 << 25)
+#define CCM_PCCR1_WDT_OFFSET 24
+#define CCM_PCCR1_WDT_MASK (1 << 24)
+#define CCM_PCCR1_HCLK_ATA_OFFSET 23
+#define CCM_PCCR1_HCLK_ATA_MASK (1 << 23)
+#define CCM_PCCR1_HCLK_BROM_OFFSET 22
+#define CCM_PCCR1_HCLK_BROM_MASK (1 << 22)
+#define CCM_PCCR1_HCLK_CSI_OFFSET 21
+#define CCM_PCCR1_HCLK_CSI_MASK (1 << 21)
+#define CCM_PCCR1_HCLK_DMA_OFFSET 20
+#define CCM_PCCR1_HCLK_DMA_MASK (1 << 20)
+#define CCM_PCCR1_HCLK_EMI_OFFSET 19
+#define CCM_PCCR1_HCLK_EMI_MASK (1 << 19)
+#define CCM_PCCR1_HCLK_EMMA_OFFSET 18
+#define CCM_PCCR1_HCLK_EMMA_MASK (1 << 18)
+#define CCM_PCCR1_HCLK_FEC_OFFSET 17
+#define CCM_PCCR1_HCLK_FEC_MASK (1 << 17)
+#define CCM_PCCR1_HCLK_VPU_OFFSET 16
+#define CCM_PCCR1_HCLK_VPU_MASK (1 << 16)
+#define CCM_PCCR1_HCLK_LCDC_OFFSET 15
+#define CCM_PCCR1_HCLK_LCDC_MASK (1 << 15)
+#define CCM_PCCR1_HCLK_RTIC_OFFSET 14
+#define CCM_PCCR1_HCLK_RTIC_MASK (1 << 14)
+#define CCM_PCCR1_HCLK_SAHARA_OFFSET 13
+#define CCM_PCCR1_HCLK_SAHARA_MASK (1 << 13)
+#define CCM_PCCR1_HCLK_SLCDC_OFFSET 12
+#define CCM_PCCR1_HCLK_SLCDC_MASK (1 << 12)
+#define CCM_PCCR1_HCLK_USBOTG_OFFSET 11
+#define CCM_PCCR1_HCLK_USBOTG_MASK (1 << 11)
+#define CCM_PCCR1_PERCLK1_OFFSET 10
+#define CCM_PCCR1_PERCLK1_MASK (1 << 10)
+#define CCM_PCCR1_PERCLK2_OFFSET 9
+#define CCM_PCCR1_PERCLK2_MASK (1 << 9)
+#define CCM_PCCR1_PERCLK3_OFFSET 8
+#define CCM_PCCR1_PERCLK3_MASK (1 << 8)
+#define CCM_PCCR1_PERCLK4_OFFSET 7
+#define CCM_PCCR1_PERCLK4_MASK (1 << 7)
+#define CCM_PCCR1_VPU_BAUD_OFFSET 6
+#define CCM_PCCR1_VPU_BAUD_MASK (1 << 6)
+#define CCM_PCCR1_SSI1_BAUD_OFFSET 5
+#define CCM_PCCR1_SSI1_BAUD_MASK (1 << 5)
+#define CCM_PCCR1_SSI2_BAUD_OFFSET 4
+#define CCM_PCCR1_SSI2_BAUD_MASK (1 << 4)
+#define CCM_PCCR1_NFC_BAUD_OFFSET 3
+#define CCM_PCCR1_NFC_BAUD_MASK (1 << 3)
+#define CCM_PCCR1_MSHC_BAUD_OFFSET 2
+#define CCM_PCCR1_MSHC_BAUD_MASK (1 << 2)
+
+#define CCM_CCSR_32KSR (1 << 15)
+#define CCM_CCSR_CLKMODE1 (1 << 9)
+#define CCM_CCSR_CLKMODE0 (1 << 8)
+#define CCM_CCSR_CLKOSEL_OFFSET 0
+#define CCM_CCSR_CLKOSEL_MASK 0x1f
+
+#define SYS_FMCR 0x14 /* Functional Muxing Control Reg */
+#define SYS_CHIP_ID 0x00 /* The offset of CHIP ID register */
+
+#endif /* __ARCH_ARM_MACH_MX27_CRM_REGS_H__ */
diff --git a/arch/arm/mach-mx27/devices.c b/arch/arm/mach-mx27/devices.c
new file mode 100644
index 000000000000..ad933d92d8d3
--- /dev/null
+++ b/arch/arm/mach-mx27/devices.c
@@ -0,0 +1,709 @@
+/*
+ * Author: MontaVista Software, Inc.
+ * <source@mvista.com>
+ *
+ * Based on the OMAP devices.c
+ *
+ * 2005 (c) MontaVista Software, Inc. 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.
+ *
+ * Copyright 2006-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/pmic_external.h>
+
+#include <linux/spi/spi.h>
+
+#include <mach/gpio.h>
+#include <mach/hardware.h>
+#include <mach/mmc.h>
+#include <mach/mxc_dptc.h>
+
+ /*!
+ * @file mach-mx27/devices.c
+ * @brief device configurations including nor/nand/watchdog for mx27.
+ *
+ * @ingroup MSL_MX27
+ */
+
+#ifndef CONFIG_MX27_DPTC
+extern struct dptc_wp dptc_wp_allfreq[DPTC_WP_SUPPORTED];
+#endif
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+#if defined(CONFIG_W1_MASTER_MXC) || defined(CONFIG_W1_MASTER_MXC_MODULE)
+static struct mxc_w1_config mxc_w1_data = {
+ .search_rom_accelerator = 0,
+};
+
+static struct platform_device mxc_w1_devices = {
+ .name = "mxc_w1",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_w1_data,
+ },
+ .id = 0
+};
+
+static void mxc_init_owire(void)
+{
+ (void)platform_device_register(&mxc_w1_devices);
+}
+#else
+static inline void mxc_init_owire(void)
+{
+}
+#endif
+
+#if defined(CONFIG_RTC_MXC) || defined(CONFIG_RTC_MXC_MODULE)
+static struct resource rtc_resources[] = {
+ {
+ .start = RTC_BASE_ADDR,
+ .end = RTC_BASE_ADDR + 0x30,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device mxc_rtc_device = {
+ .name = "mxc_rtc",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(rtc_resources),
+ .resource = rtc_resources,
+};
+static void mxc_init_rtc(void)
+{
+ (void)platform_device_register(&mxc_rtc_device);
+}
+#else
+static inline void mxc_init_rtc(void)
+{
+}
+#endif
+#if defined(CONFIG_MXC_WATCHDOG) || defined(CONFIG_MXC_WATCHDOG_MODULE)
+
+static struct resource wdt_resources[] = {
+ {
+ .start = WDOG1_BASE_ADDR,
+ .end = WDOG1_BASE_ADDR + 0x30,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_wdt_device = {
+ .name = "mxc_wdt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(wdt_resources),
+ .resource = wdt_resources,
+};
+
+static void mxc_init_wdt(void)
+{
+ (void)platform_device_register(&mxc_wdt_device);
+}
+#else
+static inline void mxc_init_wdt(void)
+{
+}
+#endif
+/*!
+ * This is platform device structure for adding SCC
+ */
+#if defined(CONFIG_MXC_SECURITY_SCC) || defined(CONFIG_MXC_SECURITY_SCC_MODULE)
+static struct platform_device mxc_scc_device = {
+ .name = "mxc_scc",
+ .id = 0,
+};
+
+static void mxc_init_scc(void)
+{
+ platform_device_register(&mxc_scc_device);
+}
+#else
+static inline void mxc_init_scc(void)
+{
+}
+#endif
+/* MMC device data */
+
+#if defined(CONFIG_MMC_MXC) || defined(CONFIG_MMC_MXC_MODULE)
+
+extern unsigned int sdhc_get_card_det_status(struct device *dev);
+extern int sdhc_init_card_det(int id);
+
+static struct mxc_mmc_platform_data mmc_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30,
+ .min_clk = 150000,
+ .max_clk = 25000000,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+};
+
+/*!
+ * Resource definition for the SDHC1
+ */
+static struct resource mxcsdhc1_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 = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*!
+ * Resource definition for the SDHC2
+ */
+static struct resource mxcsdhc2_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 = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC1 */
+static struct platform_device mxcsdhc1_device = {
+ .name = "mxcmci",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc1_resources),
+ .resource = mxcsdhc1_resources,
+};
+
+/*! Device Definition for MXC SDHC2 */
+static struct platform_device mxcsdhc2_device = {
+ .name = "mxcmci",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc2_resources),
+ .resource = mxcsdhc2_resources,
+};
+
+#ifdef CONFIG_MXC_SDHC3
+/*!
+ * Resource definition for the SDHC3
+ */
+static struct resource mxcsdhc3_resources[] = {
+ [0] = {
+ .start = SDHC3_BASE_ADDR,
+ .end = SDHC3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_SDHC3,
+ .end = MXC_INT_SDHC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ .start = MXC_SDIO3_CARD_IRQ,
+ .end = MXC_SDIO3_CARD_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC3 */
+static struct platform_device mxcsdhc3_device = {
+ .name = "mxcmci",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc3_resources),
+ .resource = mxcsdhc3_resources,
+};
+#endif
+
+static inline void mxc_init_mmc(void)
+{
+ int cd_irq;
+
+ cd_irq = sdhc_init_card_det(0);
+ if (cd_irq) {
+ mxcsdhc1_device.resource[2].start = cd_irq;
+ mxcsdhc1_device.resource[2].end = cd_irq;
+ }
+ cd_irq = sdhc_init_card_det(1);
+ if (cd_irq) {
+ mxcsdhc2_device.resource[2].start = cd_irq;
+ mxcsdhc2_device.resource[2].end = cd_irq;
+ }
+
+ (void)platform_device_register(&mxcsdhc1_device);
+ (void)platform_device_register(&mxcsdhc2_device);
+#ifdef CONFIG_MXC_SDHC3
+ (void)platform_device_register(&mxcsdhc3_device);
+#endif
+}
+#else
+static inline void mxc_init_mmc(void)
+{
+}
+#endif
+
+/* SPI controller and device data */
+#if defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE)
+
+#ifdef CONFIG_SPI_MXC_SELECT1
+/*!
+ * Resource definition for the CSPI1
+ */
+static struct resource mxcspi1_resources[] = {
+ [0] = {
+ .start = CSPI1_BASE_ADDR,
+ .end = CSPI1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI1,
+ .end = MXC_INT_CSPI1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI1 */
+static struct mxc_spi_master mxcspi1_data = {
+ .maxchipselect = 4,
+ .spi_version = 0,
+};
+
+/*! Device Definition for MXC CSPI1 */
+static struct platform_device mxcspi1_device = {
+ .name = "mxc_spi",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi1_resources),
+ .resource = mxcspi1_resources,
+};
+
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+
+#ifdef CONFIG_SPI_MXC_SELECT2
+/*!
+ * Resource definition for the CSPI2
+ */
+static struct resource mxcspi2_resources[] = {
+ [0] = {
+ .start = CSPI2_BASE_ADDR,
+ .end = CSPI2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI2,
+ .end = MXC_INT_CSPI2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI2 */
+static struct mxc_spi_master mxcspi2_data = {
+ .maxchipselect = 4,
+ .spi_version = 0,
+};
+
+/*! Device Definition for MXC CSPI2 */
+static struct platform_device mxcspi2_device = {
+ .name = "mxc_spi",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi2_resources),
+ .resource = mxcspi2_resources,
+};
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+
+#ifdef CONFIG_SPI_MXC_SELECT3
+/*!
+ * Resource definition for the CSPI3
+ */
+static struct resource mxcspi3_resources[] = {
+ [0] = {
+ .start = CSPI3_BASE_ADDR,
+ .end = CSPI3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI3,
+ .end = MXC_INT_CSPI3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI3 */
+static struct mxc_spi_master mxcspi3_data = {
+ .maxchipselect = 4,
+ .spi_version = 0,
+};
+
+/*! Device Definition for MXC CSPI3 */
+static struct platform_device mxcspi3_device = {
+ .name = "mxc_spi",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi3_resources),
+ .resource = mxcspi3_resources,
+};
+#endif /* CONFIG_SPI_MXC_SELECT3 */
+
+static inline void mxc_init_spi(void)
+{
+#ifdef CONFIG_SPI_MXC_SELECT1
+ if (platform_device_register(&mxcspi1_device) < 0)
+ printk(KERN_ERR "Registering the SPI Controller_1\n");
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+#ifdef CONFIG_SPI_MXC_SELECT2
+ if (platform_device_register(&mxcspi2_device) < 0)
+ printk(KERN_ERR "Registering the SPI Controller_2\n");
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+#ifdef CONFIG_SPI_MXC_SELECT3
+ if (platform_device_register(&mxcspi3_device) < 0)
+ printk(KERN_ERR "Registering the SPI Controller_3\n");
+#endif /* CONFIG_SPI_MXC_SELECT3 */
+}
+#else
+static inline void mxc_init_spi(void)
+{
+}
+#endif
+
+#if defined(CONFIG_SND_MXC_PMIC) || defined(CONFIG_SND_MXC_PMIC_MODULE)
+static struct mxc_audio_platform_data mxc_audio_data;
+
+static struct platform_device mxc_alsa_device = {
+ .name = "mxc_alsa",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_audio_data,
+ },
+
+};
+
+static void mxc_init_audio(void)
+{
+ mxc_audio_data.ssi_clk[0] = clk_get(NULL, "ssi_clk.0");
+ clk_put(mxc_audio_data.ssi_clk[0]);
+ mxc_audio_data.ssi_clk[1] = clk_get(NULL, "ssi_clk.1");
+ clk_put(mxc_audio_data.ssi_clk[1]);
+ mxc_audio_data.ssi_num = 2;
+ mxc_audio_data.src_port = 0;
+ platform_device_register(&mxc_alsa_device);
+}
+#else
+
+static void mxc_init_audio(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_SSI) || defined(CONFIG_MXC_SSI_MODULE)
+/*!
+ * Resource definition for the SSI
+ */
+static struct resource mxcssi2_resources[] = {
+ [0] = {
+ .start = SSI2_BASE_ADDR,
+ .end = SSI2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource mxcssi1_resources[] = {
+ [0] = {
+ .start = SSI1_BASE_ADDR,
+ .end = SSI1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+/*! Device Definition for MXC SSI */
+static struct platform_device mxc_ssi1_device = {
+ .name = "mxc_ssi",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_audio_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcssi1_resources),
+ .resource = mxcssi1_resources,
+};
+
+static struct platform_device mxc_ssi2_device = {
+ .name = "mxc_ssi",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_audio_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcssi2_resources),
+ .resource = mxcssi2_resources,
+};
+
+static void mxc_init_ssi(void)
+{
+ platform_device_register(&mxc_ssi1_device);
+ platform_device_register(&mxc_ssi2_device);
+}
+#else
+
+static void mxc_init_ssi(void)
+{
+}
+#endif
+
+/* I2C controller and device data */
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+/*!
+ * Resource definition for the I2C1
+ */
+static struct resource mxci2c1_resources[] = {
+ [0] = {
+ .start = I2C_BASE_ADDR,
+ .end = I2C_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C,
+ .end = MXC_INT_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c1_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+#ifdef CONFIG_I2C_MXC_SELECT2
+/*!
+ * Resource definition for the I2C2
+ */
+static struct resource mxci2c2_resources[] = {
+ [0] = {
+ .start = I2C2_BASE_ADDR,
+ .end = I2C2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C2,
+ .end = MXC_INT_I2C2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c2_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+/*! Device Definition for MXC I2C */
+static struct platform_device mxci2c_devices[] = {
+#ifdef CONFIG_I2C_MXC_SELECT1
+ {
+ .name = "mxc_i2c",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c1_resources),
+ .resource = mxci2c1_resources,},
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT2
+ {
+ .name = "mxc_i2c",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c2_resources),
+ .resource = mxci2c2_resources,},
+#endif
+};
+
+static inline void mxc_init_i2c(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxci2c_devices); i++) {
+ if (platform_device_register(&mxci2c_devices[i]) < 0)
+ dev_err(&mxci2c_devices[i].dev,
+ "Unable to register I2C device\n");
+ }
+}
+#else
+static inline void mxc_init_i2c(void)
+{
+}
+#endif
+
+#ifdef CONFIG_MXC_VPU
+/*! Platform Data for MXC VPU */
+static struct platform_device mxcvpu_device = {
+ .name = "mxc_vpu",
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .id = 0,
+};
+
+static inline void mxc_init_vpu(void)
+{
+ if (platform_device_register(&mxcvpu_device) < 0)
+ printk(KERN_ERR "Error: Registering the VPU.\n");
+}
+#else
+static inline void mxc_init_vpu(void)
+{
+}
+#endif
+
+struct mxc_gpio_port mxc_gpio_ports[GPIO_PORT_NUM] = {
+ [0] = {
+ .chip.label = "gpio-0",
+ .base = IO_ADDRESS(GPIO_BASE_ADDR),
+ .irq = MXC_INT_GPIO,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE
+ },
+ [1] = {
+ .chip.label = "gpio-1",
+ .base = IO_ADDRESS(GPIO_BASE_ADDR) + 0x100,
+ .irq = MXC_INT_GPIO,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN
+ },
+ [2] = {
+ .chip.label = "gpio-2",
+ .base = IO_ADDRESS(GPIO_BASE_ADDR) + 0x200,
+ .irq = MXC_INT_GPIO,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 2
+ },
+ [3] = {
+ .chip.label = "gpio-3",
+ .base = IO_ADDRESS(GPIO_BASE_ADDR) + 0x300,
+ .irq = MXC_INT_GPIO,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 3
+ },
+ [4] = {
+ .chip.label = "gpio-4",
+ .base = IO_ADDRESS(GPIO_BASE_ADDR) + 0x400,
+ .irq = MXC_INT_GPIO,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 4
+ },
+ [5] = {
+ .chip.label = "gpio-5",
+ .base = IO_ADDRESS(GPIO_BASE_ADDR) + 0x500,
+ .irq = MXC_INT_GPIO,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 5
+ }
+};
+
+int __init mxc_register_gpios(void)
+{
+ return mxc_gpio_init(mxc_gpio_ports, ARRAY_SIZE(mxc_gpio_ports));
+}
+
+#ifndef CONFIG_MX27_DPTC
+/*! Device Definition for DPTC */
+static struct platform_device mxc_dptc_device = {
+ .name = "mxc_dptc",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &dptc_wp_allfreq,
+ },
+};
+
+static inline void mxc_init_dptc(void)
+{
+ (void)platform_device_register(&mxc_dptc_device);
+}
+#endif
+
+static int __init mxc_init_devices(void)
+{
+ mxc_init_wdt();
+ mxc_init_mmc();
+ mxc_init_spi();
+ mxc_init_i2c();
+ mxc_init_rtc();
+ mxc_init_ssi();
+ mxc_init_audio();
+ mxc_init_scc();
+ mxc_init_owire();
+ mxc_init_vpu();
+#ifndef CONFIG_MX27_DPTC
+ mxc_init_dptc();
+#endif
+
+ return 0;
+}
+
+arch_initcall(mxc_init_devices);
diff --git a/arch/arm/mach-mx27/dma.c b/arch/arm/mach-mx27/dma.c
new file mode 100644
index 000000000000..a48e070a6a44
--- /dev/null
+++ b/arch/arm/mach-mx27/dma.c
@@ -0,0 +1,553 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ *@file mach-mx27/dma.c
+ *@brief This file contains the dma parameter which is depend on the platform .
+ * @ingroup DMA_MX27
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/dma.h>
+
+#define MXC_SOUND_PLAYBACK_CHAIN_DMA 1
+#define MXC_SOUND_CAPTURE_CHAIN_DMA 1
+
+/*!
+ * @brief the structure stored device_id and dma_info pointer
+ */
+typedef struct dma_info_entry_s {
+ mxc_dma_device_t device;
+ /* if there are two dma_info , first is for reading, another is for writing */
+ mx2_dma_info_t *info;
+} dma_info_entry_t;
+
+/*!
+ * @brief dma_info from memory to memory for dma testing
+ */
+static mx2_dma_info_t ram2ram_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 0,
+ .burstLength = 4,.request = 0,.busuntils = 0,
+ .sourceType = DMA_TYPE_LINEAR,.sourcePort = TRANSFER_32BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_32BIT,
+ .M2D_Valid = 0
+};
+
+/*!
+ * @brief dma_info from 2D memory to 2D memory for dma testing
+ */
+static mx2_dma_info_t ram2d2ram2d_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .rto_en = 0,
+ .dma_chaining = 0,.ren = 0,
+ .burstLength = 4,.request = 0,.busuntils = 0,
+ .sourceType = DMA_TYPE_2D,.sourcePort = TRANSFER_32BIT,
+ .destType = DMA_TYPE_2D,.destPort = TRANSFER_32BIT,
+ .M2D_Valid = 1,.msel = 0,.W = 0x80,.X = 0x40,.Y = 0x10
+};
+
+/*!
+ * @brief dma_info from memory to 2D memory for dma testing
+ */
+static mx2_dma_info_t ram2ram2d_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 0,
+ .burstLength = 4,.request = 0,.busuntils = 0,
+ .sourceType = DMA_TYPE_LINEAR,.sourcePort = TRANSFER_32BIT,
+ .destType = DMA_TYPE_2D,.destPort = TRANSFER_32BIT,
+ .M2D_Valid = 1,.msel = 0,.W = 0x100,.X = 0x80,.Y = 0x10
+};
+
+/*!
+ * @brief dma_info from 2D memory to memory for dma testing
+ */
+static mx2_dma_info_t ram2d2ram_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 0,
+ .burstLength = 4,.request = 0,.busuntils = 0,
+ .sourceType = DMA_TYPE_2D,.sourcePort = TRANSFER_32BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_32BIT,
+ .M2D_Valid = 1,.msel = 0,.W = 0x100,.X = 0x100,.Y = 0x10
+};
+
+/*!
+ * @brief dma_info with dma chaining feature for dma testing
+ */
+static mx2_dma_info_t hw_chaining_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 1,.ren = 0,
+ .burstLength = 4,.request = 0,.busuntils = 0,
+ .sourceType = DMA_TYPE_LINEAR,.sourcePort = TRANSFER_32BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_32BIT,
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief dma_info without dma chaining feature for dma testing
+ */
+static mx2_dma_info_t sw_chaining_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 0,
+ .burstLength = 4,.request = 0,.busuntils = 0,
+ .sourceType = DMA_TYPE_LINEAR,.sourcePort = TRANSFER_32BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_32BIT,
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief dma_info for ATA recieveing
+ */
+static mx2_dma_info_t ata_rx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 32,.request = 29,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_32BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_32BIT,
+ .per_address = (ATA_BASE_ADDR + 0x18),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief: dma_info for ATA transmitting
+ */
+static mx2_dma_info_t ata_tx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 1,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 32,.request = 28,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_32BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_32BIT,
+ .per_address = (ATA_BASE_ADDR + 0x18),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief dma_info for UART1 recieveing
+ */
+static mx2_dma_info_t uart1_rx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 1,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 26,.busuntils = 8,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART1_BASE_ADDR),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief: dma_info for UART1 transmitting
+ */
+static mx2_dma_info_t uart1_tx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 1,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 27,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART1_BASE_ADDR + 0x40),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief dma_info for UART2 recieveing
+ */
+static mx2_dma_info_t uart2_rx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 24,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART2_BASE_ADDR),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief: dma_info for UART2 transmitting
+ */
+static mx2_dma_info_t uart2_tx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 1,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 25,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART2_BASE_ADDR + 0x40),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief dma_info for UART3 recieveing
+ */
+static mx2_dma_info_t uart3_rx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 22,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART3_BASE_ADDR),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief: dma_info for UART3 transmitting
+ */
+static mx2_dma_info_t uart3_tx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 1,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 23,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART3_BASE_ADDR + 0x40),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief dma_info for UART4 recieveing
+ */
+static mx2_dma_info_t uart4_rx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 20,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART4_BASE_ADDR),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief: dma_info for UART4transmitting
+ */
+static mx2_dma_info_t uart4_tx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 1,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 21,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART4_BASE_ADDR + 0x40),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief dma_info for UART5 recieveing
+ */
+static mx2_dma_info_t uart5_rx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 32,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART5_BASE_ADDR),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief: dma_info for UART5 transmitting
+ */
+static mx2_dma_info_t uart5_tx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 1,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 33,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART5_BASE_ADDR + 0x40),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief dma_info for UART6 recieveing
+ */
+static mx2_dma_info_t uart6_rx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 34,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART6_BASE_ADDR),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief: dma_info for UART6 transmitting
+ */
+static mx2_dma_info_t uart6_tx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 1,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 1,.request = 35,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = TRANSFER_8BIT,
+ .destType = DMA_TYPE_LINEAR,.destPort = TRANSFER_8BIT,
+ .per_address = (UART6_BASE_ADDR + 0x40),
+ .M2D_Valid = 0,
+};
+
+static mx2_dma_info_t ssi1_16bit_rx0_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = MXC_SOUND_CAPTURE_CHAIN_DMA,.ren = 1,
+ .burstLength = 8,.request = DMA_REQ_SSI1_RX0,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = DMA_MEM_SIZE_16,
+ .destType = DMA_TYPE_LINEAR,.destPort = DMA_MEM_SIZE_32,
+ .per_address = (SSI1_BASE_ADDR + 0x08),
+ .M2D_Valid = 0,
+};
+
+static mx2_dma_info_t ssi1_16bit_tx0_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 1,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = MXC_SOUND_PLAYBACK_CHAIN_DMA,.ren = 1,
+ .burstLength = 8,.request = DMA_REQ_SSI1_TX0,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = DMA_MEM_SIZE_16,
+ .destType = DMA_TYPE_LINEAR,.destPort = DMA_MEM_SIZE_32,
+ .per_address = (SSI1_BASE_ADDR),
+ .M2D_Valid = 0,
+};
+
+static mx2_dma_info_t ssi2_16bit_rx0_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = MXC_SOUND_CAPTURE_CHAIN_DMA,.ren = 1,
+ .burstLength = 8,.request = DMA_REQ_SSI2_RX0,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = DMA_MEM_SIZE_16,
+ .destType = DMA_TYPE_LINEAR,.destPort = DMA_MEM_SIZE_32,
+ .per_address = (SSI2_BASE_ADDR + 0x08),
+ .M2D_Valid = 0,
+};
+
+static mx2_dma_info_t ssi2_16bit_tx0_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 1,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = MXC_SOUND_PLAYBACK_CHAIN_DMA,.ren = 1,
+ .burstLength = 8,.request = DMA_REQ_SSI2_TX0,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = DMA_MEM_SIZE_16,
+ .destType = DMA_TYPE_LINEAR,.destPort = DMA_MEM_SIZE_32,
+ .per_address = (SSI2_BASE_ADDR),
+ .M2D_Valid = 0,
+};
+
+static mx2_dma_info_t mmc1_width1_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 16,.request = DMA_REQ_SDHC1,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = DMA_MEM_SIZE_32,
+ .destType = DMA_TYPE_LINEAR,.destPort = DMA_MEM_SIZE_32,
+ .per_address = (SDHC1_BASE_ADDR + 0x38),
+ .M2D_Valid = 0,
+};
+
+static mx2_dma_info_t mmc1_width4_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 0,.request = DMA_REQ_SDHC1,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = DMA_MEM_SIZE_32,
+ .destType = DMA_TYPE_LINEAR,.destPort = DMA_MEM_SIZE_32,
+ .per_address = (SDHC1_BASE_ADDR + 0x38),
+ .M2D_Valid = 0,
+};
+
+static mx2_dma_info_t mmc2_width1_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 16,.request = DMA_REQ_SDHC2,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = DMA_MEM_SIZE_32,
+ .destType = DMA_TYPE_LINEAR,.destPort = DMA_MEM_SIZE_32,
+ .per_address = (SDHC2_BASE_ADDR + 0x38),
+ .M2D_Valid = 0,
+};
+
+static mx2_dma_info_t mmc2_width4_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 0,.ren = 1,
+ .burstLength = 0,.request = DMA_REQ_SDHC2,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = DMA_MEM_SIZE_32,
+ .destType = DMA_TYPE_LINEAR,.destPort = DMA_MEM_SIZE_32,
+ .per_address = (SDHC2_BASE_ADDR + 0x38),
+ .M2D_Valid = 0,
+};
+
+static mx2_dma_info_t csi_rx_dma_info = {
+ .dma_chan = MXC_DMA_DYNAMIC_CHANNEL,
+ .mode = 0,
+ .rto_en = 0,
+ .dir = 0,
+ .dma_chaining = 1,.ren = 1,
+ .burstLength = 64,.request = DMA_REQ_CSI_RX,.busuntils = 0,
+ .sourceType = DMA_TYPE_FIFO,.sourcePort = DMA_MEM_SIZE_32,
+ .destType = DMA_TYPE_LINEAR,.destPort = DMA_MEM_SIZE_32,
+ .per_address = (CSI_BASE_ADDR + 0x10),
+ .M2D_Valid = 0,
+};
+
+/*!
+ * @brief dma info array which is actived
+ * DEVICE_ID RX/(RX&TX) TX
+ */
+static dma_info_entry_t active_dma_info[] = {
+ {MXC_DMA_TEST_RAM2RAM, &ram2ram_dma_info},
+ {MXC_DMA_TEST_RAM2D2RAM2D, &ram2d2ram2d_dma_info},
+ {MXC_DMA_TEST_RAM2RAM2D, &ram2ram2d_dma_info},
+ {MXC_DMA_TEST_RAM2D2RAM, &ram2d2ram_dma_info},
+ {MXC_DMA_TEST_HW_CHAINING, &hw_chaining_dma_info},
+ {MXC_DMA_TEST_SW_CHAINING, &sw_chaining_dma_info},
+ {MXC_DMA_ATA_RX, &ata_rx_dma_info},
+ {MXC_DMA_ATA_TX, &ata_tx_dma_info},
+ {MXC_DMA_UART1_RX, &uart1_rx_dma_info},
+ {MXC_DMA_UART1_TX, &uart1_tx_dma_info},
+ {MXC_DMA_UART2_RX, &uart2_rx_dma_info},
+ {MXC_DMA_UART2_TX, &uart2_tx_dma_info},
+ {MXC_DMA_UART3_RX, &uart3_rx_dma_info},
+ {MXC_DMA_UART3_TX, &uart3_tx_dma_info},
+ {MXC_DMA_UART4_RX, &uart4_rx_dma_info},
+ {MXC_DMA_UART4_TX, &uart4_tx_dma_info},
+ {MXC_DMA_UART5_RX, &uart5_rx_dma_info},
+ {MXC_DMA_UART5_TX, &uart5_tx_dma_info},
+ {MXC_DMA_UART6_RX, &uart6_rx_dma_info},
+ {MXC_DMA_UART6_TX, &uart6_tx_dma_info},
+ {MXC_DMA_SSI1_16BIT_RX0, &ssi1_16bit_rx0_dma_info},
+ {MXC_DMA_SSI1_16BIT_TX0, &ssi1_16bit_tx0_dma_info},
+ {MXC_DMA_SSI2_16BIT_RX0, &ssi2_16bit_rx0_dma_info},
+ {MXC_DMA_SSI2_16BIT_TX0, &ssi2_16bit_tx0_dma_info},
+ {MXC_DMA_MMC1_WIDTH_1, &mmc1_width1_dma_info},
+ {MXC_DMA_MMC1_WIDTH_4, &mmc1_width4_dma_info},
+ {MXC_DMA_MMC2_WIDTH_1, &mmc2_width1_dma_info},
+ {MXC_DMA_MMC2_WIDTH_4, &mmc2_width4_dma_info},
+ {MXC_DMA_CSI_RX, &csi_rx_dma_info},
+};
+
+/*!
+ * @brief the number of actived dma info
+ */
+static int dma_info_entrys =
+ sizeof(active_dma_info) / sizeof(active_dma_info[0]);
+
+/*!
+ * @brief get the dma info by channel_id
+ */
+mx2_dma_info_t *mxc_dma_get_info(mxc_dma_device_t channel_id)
+{
+ dma_info_entry_t *p = active_dma_info;
+ int i;
+ for (i = 0; i < dma_info_entrys; i++, p++) {
+ if (p->device == channel_id)
+ return p->info;
+ }
+ return NULL;
+}
+
+/*!
+ * @brief: scan dma parameter list . And collect information about which channels are dynamic .
+ */
+void mxc_dma_load_info(mxc_dma_channel_t * dma)
+{
+ int i, idx;
+ dma_info_entry_t *p = active_dma_info;
+
+ BUG_ON(dma == NULL);
+ BUG_ON(p == NULL);
+
+ for (i = 0; i < MXC_DMA_CHANNELS; i++) {
+ dma[i].dynamic = 1;
+ }
+
+ for (i = 0; i < dma_info_entrys; i++, p++) {
+ BUG_ON((p->info == NULL));
+
+ idx = p->info->dma_chan;
+
+ BUG_ON(((idx >= MAX_DMA_CHANNELS)
+ && (idx != MXC_DMA_DYNAMIC_CHANNEL)));
+ if ((idx < 0) || (idx == MXC_DMA_DYNAMIC_CHANNEL))
+ continue;
+ dma[idx].dynamic = 0;
+ }
+}
+
+EXPORT_SYMBOL(mxc_dma_get_info);
+EXPORT_SYMBOL(mxc_dma_load_info);
diff --git a/arch/arm/mach-mx27/dptc.c b/arch/arm/mach-mx27/dptc.c
new file mode 100644
index 000000000000..deb058c23272
--- /dev/null
+++ b/arch/arm/mach-mx27/dptc.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file dptc.c
+ *
+ * @brief DPTC table for the Freescale Semiconductor MXC DPTC module.
+ *
+ * @ingroup PM
+ */
+
+#include <mach/hardware.h>
+#include <mach/mxc_dptc.h>
+
+struct dptc_wp dptc_wp_allfreq[DPTC_WP_SUPPORTED] = {
+ /* 532MHz */
+ /* dcvr0 dcvr1 dcvr2 dcvr3 voltage */
+ /* wp0 */
+ {0xffe00000, 0x18e2e85b, 0xffe00000, 0x25c4688a, 1600},
+ {0xffe00000, 0x18e2e85b, 0xffe00000, 0x25c4688a, 1575},
+ {0xffe00000, 0x1902e85b, 0xffe00000, 0x25e4688a, 1550},
+ {0xffe00000, 0x1922e85b, 0xffe00000, 0x25e4688a, 1525},
+ {0xffe00000, 0x1942ec5b, 0xffe00000, 0x2604688a, 1500},
+ /* wp5 */
+ {0xffe00000, 0x1942ec5b, 0xffe00000, 0x26646c8a, 1475},
+ {0xffe00000, 0x1962ec5b, 0xffe00000, 0x26c4708b, 1450},
+ {0xffe00000, 0x1962ec5b, 0xffe00000, 0x26e4708b, 1425},
+ {0xffe00000, 0x1982f05c, 0xffe00000, 0x2704748b, 1400},
+ {0xffe00000, 0x19c2f05c, 0xffe00000, 0x2744748b, 1375},
+ /* wp10 */
+ {0xffe00000, 0x1a02f45c, 0xffe00000, 0x2784788b, 1350},
+ {0xffe00000, 0x1a42f45c, 0xffe00000, 0x27c47c8b, 1325},
+ {0xffe00000, 0x1a82f85c, 0xffe00000, 0x2824808c, 1300},
+ {0xffe00000, 0x1aa2f85c, 0xffe00000, 0x2884848c, 1275},
+ {0xffe00000, 0x1ac2fc5c, 0xffe00000, 0x28e4888c, 1250},
+ /* wp15 */
+ {0xffe00000, 0x1ae2fc5c, 0xffe00000, 0x2924888c, 1225},
+ {0xffe00000, 0x1b23005d, 0xffe00000, 0x29648c8c, 1200},
+};
diff --git a/arch/arm/mach-mx27/gpio_mux.c b/arch/arm/mach-mx27/gpio_mux.c
new file mode 100644
index 000000000000..500ebcc48a04
--- /dev/null
+++ b/arch/arm/mach-mx27/gpio_mux.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup GPIO_MX27 Board GPIO and Muxing Setup
+ * @ingroup MSL_MX27
+ */
+/*!
+ * @file mach-mx27/gpio_mux.c
+ *
+ * @brief I/O Muxing control functions
+ *
+ * @ingroup GPIO_MX27
+ */
+
+#include <linux/kernel.h>
+#include <linux/cache.h>
+#include <linux/spinlock.h>
+
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include "gpio_mux.h"
+
+/*!
+ * This structure defines the offset of registers in gpio module.
+ */
+enum gpio_reg {
+ GPIO_GIUS = 0x20,
+ GPIO_GPR = 0x38,
+ GPIO_PUEN = 0x40,
+ GPIO_DDIR = 0x00,
+ GPIO_OCR1 = 0x04,
+ GPIO_OCR2 = 0x08,
+ GPIO_ICONFA1 = 0x0C,
+ GPIO_ICONFA2 = 0x10,
+ GPIO_ICONFB1 = 0x14,
+ GPIO_ICONFB2 = 0x18,
+};
+
+/*!
+ * This enumeration data type defines the configuration for input mode.
+ */
+typedef enum {
+ GPIO_INPUT_GPIO = 0x00,
+ GPIO_INPUT_INTR = 0x01,
+ GPIO_INPUT_LOW = 0x02,
+ GPIO_INPUT_HIGH = 0x03
+} gpio_input_cfg_t;
+
+/*!
+ * This enumeration data type defines the configuration for output mode.
+ */
+typedef enum {
+ GPIO_OUTPUT_A = 0x00,
+ GPIO_OUTPUT_B = 0x01,
+ GPIO_OUTPUT_C = 0x02,
+ GPIO_OUTPUT_DR = 0x03
+} gpio_output_cfg_t;
+
+extern struct mxc_gpio_port mxc_gpio_ports[];
+
+/*!
+ * defines a spinlock to protected the accessing to gpio pin.
+ */
+DEFINE_SPINLOCK(gpio_mux_lock);
+
+/*!
+ * This function enable or disable the pullup feature to the pin.
+ * @param port a pointer of gpio port
+ * @param index the index of the pin in the port
+ * @param en 0 if disable pullup, otherwise enable it.
+ * @return none
+ */
+static inline void _gpio_set_puen(struct mxc_gpio_port *port, u32 index,
+ bool en)
+{
+ u32 reg;
+
+ reg = __raw_readl(port->base + GPIO_PUEN);
+ if (en) {
+ reg |= 1 << index;
+ } else {
+ reg &= ~(1 << index);
+ }
+ __raw_writel(reg, port->base + GPIO_PUEN);
+}
+
+/*!
+ * This function set the input configuration A.
+ * @param port a pointer of gpio port
+ * @param index the index of the pin in the port
+ * @param config a mode as define in \b #gpio_input_cfg_t
+ * @return none
+ */
+static inline void _gpio_set_iconfa(struct mxc_gpio_port *port, u32 index,
+ gpio_input_cfg_t config)
+{
+ u32 reg, val;
+ u32 mask;
+
+ mask = 0x3 << ((index % 16) << 1);
+
+ if (index >= 16) {
+ reg = port->base + GPIO_ICONFA2;
+ val = config << ((index - 16) * 2);
+ } else {
+ reg = port->base + GPIO_ICONFA1;
+ val = config << (index * 2);
+ }
+ val |= __raw_readl(reg) & ~(mask);
+ __raw_writel(val, reg);
+}
+
+/*!
+ * This function set the input configuration B.
+ * @param port a pointer of gpio port
+ * @param index the index of the pin in the port
+ * @param config a mode as define in \b #gpio_input_cfg_t
+ * @return none
+ */
+static inline void _gpio_set_iconfb(struct mxc_gpio_port *port, u32 index,
+ gpio_input_cfg_t config)
+{
+ u32 reg, val;
+ u32 mask;
+
+ mask = 0x3 << ((index % 16) << 1);
+
+ if (index >= 16) {
+ reg = port->base + GPIO_ICONFB2;
+ val = config << ((index - 16) * 2);
+ } else {
+ reg = port->base + GPIO_ICONFB1;
+ val = config << (index * 2);
+ }
+ val |= __raw_readl(reg) & (~mask);
+ __raw_writel(val, reg);
+}
+
+/*!
+ * This function set the output configuration.
+ * @param port a pointer of gpio port
+ * @param index the index of the pin in the port
+ * @param config a mode as define in \b #gpio_output_cfg_t
+ * @return none
+ */
+static inline void _gpio_set_ocr(struct mxc_gpio_port *port, u32 index,
+ gpio_output_cfg_t config)
+{
+ u32 reg, val;
+ u32 mask;
+
+ mask = 0x3 << ((index % 16) << 1);
+ if (index >= 16) {
+ reg = port->base + GPIO_OCR2;
+ val = config << ((index - 16) * 2);
+ } else {
+ reg = port->base + GPIO_OCR1;
+ val = config << (index * 2);
+ }
+ val |= __raw_readl(reg) & (~mask);
+ __raw_writel(val, reg);
+}
+
+/*!
+ *@brief gpio_config_mux - just configure the mode of the gpio pin.
+ *@param pin a pin number as defined in \b #iomux_pin_name_t
+ *@param mode a module as define in \b #gpio_mux_mode_t;
+ * GPIO_MUX_PRIMARY set pin to work as primary function.
+ * GPIO_MUX_ALT set pin to work as alternate function.
+ * GPIO_MUX_GPIO set pin to work as output function based the data register
+ * GPIO_MUX_INPUT1 set pin to work as input function connected with A_OUT
+ * GPIO_MUX_INPUT2 set pin to work as input function connected with B_OUT
+ * GPIO_MUX_OUTPUT1 set pin to work as output function connected with A_IN
+ * GPIO_MUX_OUTPUT2 set pin to work as output function connected with B_IN
+ * GPIO_MUX_OUTPUT3 set pin to work as output function connected with C_IN
+ *@return 0 if successful, Non-zero otherwise
+ */
+
+int gpio_config_mux(iomux_pin_name_t pin, gpio_mux_mode_t mode)
+{
+ unsigned long lock_flags;
+ u32 gius_reg, gpr_reg;
+ struct mxc_gpio_port *port;
+ u32 index, gpio = IOMUX_TO_GPIO(pin);
+
+ port = &(mxc_gpio_ports[GPIO_TO_PORT(gpio)]);
+ index = GPIO_TO_INDEX(gpio);
+
+ pr_debug("%s: Configuring PORT %c, bit %d\n",
+ __func__, GPIO_TO_PORT(gpio) + 'A', index);
+
+ spin_lock_irqsave(&gpio_mux_lock, lock_flags);
+
+ gius_reg = __raw_readl(port->base + GPIO_GIUS);
+ gpr_reg = __raw_readl(port->base + GPIO_GPR);
+
+ switch (mode) {
+ case GPIO_MUX_PRIMARY:
+ gius_reg &= ~(1L << index);
+ gpr_reg &= ~(1L << index);
+ break;
+ case GPIO_MUX_ALT:
+ gius_reg &= ~(1L << index);
+ gpr_reg |= (1L << index);
+ break;
+ case GPIO_MUX_GPIO:
+ gius_reg |= (1L << index);
+ _gpio_set_ocr(port, index, GPIO_OUTPUT_DR);
+ break;
+ case GPIO_MUX_INPUT1:
+ gius_reg |= (1L << index);
+ _gpio_set_iconfa(port, index, GPIO_INPUT_GPIO);
+ break;
+ case GPIO_MUX_INPUT2:
+ gius_reg |= (1L << index);
+ _gpio_set_iconfb(port, index, GPIO_INPUT_GPIO);
+ break;
+ case GPIO_MUX_OUTPUT1:
+ gius_reg |= (1L << index);
+ _gpio_set_ocr(port, index, GPIO_OUTPUT_A);
+ break;
+ case GPIO_MUX_OUTPUT2:
+ gius_reg |= (1L << index);
+ _gpio_set_ocr(port, index, GPIO_OUTPUT_B);
+ break;
+ case GPIO_MUX_OUTPUT3:
+ gius_reg |= (1L << index);
+ _gpio_set_ocr(port, index, GPIO_OUTPUT_C);
+ break;
+ default:
+ spin_unlock_irqrestore(&gpio_mux_lock, lock_flags);
+ return -1;
+ }
+
+ __raw_writel(gius_reg, port->base + GPIO_GIUS);
+ __raw_writel(gpr_reg, port->base + GPIO_GPR);
+
+ spin_unlock_irqrestore(&gpio_mux_lock, lock_flags);
+ return 0;
+}
+
+/*!
+ * This function is just used to enable or disable the pull up feature .
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param en 0 if disable, Non-zero enable
+ * @return 0 if successful, Non-zero otherwise
+ */
+int gpio_set_puen(iomux_pin_name_t pin, bool en)
+{
+ unsigned long lock_flags;
+
+ struct mxc_gpio_port *port;
+ u32 index, gpio = IOMUX_TO_GPIO(pin);
+
+ port = &(mxc_gpio_ports[GPIO_TO_PORT(gpio)]);
+ index = GPIO_TO_INDEX(gpio);
+
+ pr_debug("%s: Configuring output mode of PORT %c, bit %d\n",
+ __func__, GPIO_TO_PORT(gpio) + 'A', index);
+
+ spin_lock_irqsave(&gpio_mux_lock, lock_flags);
+
+ _gpio_set_puen(port, index, en);
+ spin_unlock_irqrestore(&gpio_mux_lock, lock_flags);
+ return 0;
+
+}
+
+/*!
+ * This function is just used to request a pin and configure it.
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param mode a module as define in \b #gpio_mux_mode_t;
+ * @return 0 if successful, Non-zero otherwise
+ */
+int gpio_request_mux(iomux_pin_name_t pin, gpio_mux_mode_t mode)
+{
+ int ret;
+ ret = gpio_request(IOMUX_TO_GPIO(pin), NULL);
+ if (ret == 0) {
+ ret = gpio_config_mux(pin, mode);
+ if (ret) {
+ gpio_free(IOMUX_TO_GPIO(pin));
+ }
+ }
+ return ret;
+}
+
+/*!
+ * This function is just used to release a pin.
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @return none
+ */
+void gpio_free_mux(iomux_pin_name_t pin)
+{
+ gpio_free(IOMUX_TO_GPIO(pin));
+}
diff --git a/arch/arm/mach-mx27/gpio_mux.h b/arch/arm/mach-mx27/gpio_mux.h
new file mode 100644
index 000000000000..0c8db21c766a
--- /dev/null
+++ b/arch/arm/mach-mx27/gpio_mux.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ *@file mach-mx27/gpio_mux.h
+ *@brief This file contains the private definition .
+ * @ingroup GPIO_MX27
+ */
+
+#ifndef __ARCH_ARM_MACH_MX27_GPIO_MUX_H__
+#define __ARCH_ARM_MACH_MX27_GPIO_MUX_H__
+
+#include "mx27_pins.h"
+
+typedef unsigned int iomux_pin_name_t;
+
+/*!
+ * This enumeration data type defines the modes of the pin .
+ * GPIO_MUX_PRIMARY is the primary mode.
+ * GPIO_MUX_ALT is the alternate mode.
+ * GPIO_MUX_GPIO is the output mode and the signal source is data register.
+ * GPIO_MUX_INPUT1 is the input mode and the signal destination is A_OUT.
+ * GPIO_MUX_INPUT2 is the input mode and the signal destination is B_OUT.
+ * GPIO_MUX_OUTPUT1 is the output mode and the signal destination is A_IN.
+ * GPIO_MUX_OUTPUT2 is the output mode and the signal destination is B_IN.
+ * GPIO_MUX_OUTPUT3 is the output mode and the signal destination is C_IN.
+ */
+typedef enum {
+ GPIO_MUX_PRIMARY,
+ GPIO_MUX_ALT,
+ GPIO_MUX_GPIO,
+ GPIO_MUX_INPUT1,
+ GPIO_MUX_INPUT2,
+ GPIO_MUX_OUTPUT1,
+ GPIO_MUX_OUTPUT2,
+ GPIO_MUX_OUTPUT3,
+} gpio_mux_mode_t;
+
+/*!
+ * This function is just used to request a pin and configure it.
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param mode a module as define in \b #gpio_mux_mode_t;
+ * @return 0 if successful, Non-zero otherwise
+ */
+extern int gpio_request_mux(iomux_pin_name_t pin, gpio_mux_mode_t mode);
+
+/*!
+ * This function is just used to configure a pin .
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param mode a module as define in \b #gpio_mux_mode_t;
+ * @return 0 if successful, Non-zero otherwise
+ */
+extern int gpio_config_mux(iomux_pin_name_t pin, gpio_mux_mode_t mode);
+
+/*!
+ * This function is just used to enable or disable the pull up feature .
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param en 0 if disable, Non-zero enable
+ * @return 0 if successful, Non-zero otherwise
+ */
+extern int gpio_set_puen(iomux_pin_name_t pin, bool en);
+
+/*!
+ * This function is just used to release a pin.
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @return none
+ */
+extern void gpio_free_mux(iomux_pin_name_t pin);
+
+#endif /* __ARCH_ARM_MACH_MX27_GPIO_MUX_H__ */
diff --git a/arch/arm/mach-mx27/mm.c b/arch/arm/mach-mx27/mm.c
new file mode 100644
index 000000000000..9f673b6687a2
--- /dev/null
+++ b/arch/arm/mach-mx27/mm.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <asm/pgtable.h>
+#include <asm/mach/map.h>
+
+/*!
+ * @file mach-mx27/mm.c
+ *
+ * @brief This file creates static mapping between physical to virtual memory.
+ *
+ * @ingroup Memory_MX27
+ */
+
+/*!
+ * This structure defines the MX27 memory map.
+ */
+static struct map_desc mxc_io_desc[] __initdata = {
+ {
+ .virtual = AIPI_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPI_BASE_ADDR),
+ .length = AIPI_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = SAHB1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(SAHB1_BASE_ADDR),
+ .length = SAHB1_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = X_MEMC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(X_MEMC_BASE_ADDR),
+ .length = X_MEMC_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = CS4_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(CS4_BASE_ADDR),
+ .length = CS4_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 mxc_map_io(void)
+{
+ iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
+}
diff --git a/arch/arm/mach-mx27/mx27_pins.h b/arch/arm/mach-mx27/mx27_pins.h
new file mode 100644
index 000000000000..40ff1be1febc
--- /dev/null
+++ b/arch/arm/mach-mx27/mx27_pins.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MX27_PINS_H__
+#define __ASM_ARCH_MXC_MX27_PINS_H__
+
+/*!
+ * @file arch-mxc/mx27_pins.h
+ *
+ * @brief MX27 I/O Pin List
+ *
+ * @ingroup GPIO_MX27
+ */
+
+#ifndef __ASSEMBLY__
+
+#define _MX27_BUILD_PIN(gp,gi) (((gp) << MUX_IO_P) | ((gi) << MUX_IO_I))
+
+enum iomux_pins {
+ MX27_PIN_USBH2_CLK = _MX27_BUILD_PIN(0, 0),
+ MX27_PIN_USBH2_DIR = _MX27_BUILD_PIN(0, 1),
+ MX27_PIN_USBH2_DATA7 = _MX27_BUILD_PIN(0, 2),
+ MX27_PIN_USBH2_NXT = _MX27_BUILD_PIN(0, 3),
+ MX27_PIN_USBH2_STP = _MX27_BUILD_PIN(0, 4),
+ MX27_PIN_LSCLK = _MX27_BUILD_PIN(0, 5),
+ MX27_PIN_LD0 = _MX27_BUILD_PIN(0, 6),
+ MX27_PIN_LD1 = _MX27_BUILD_PIN(0, 7),
+ MX27_PIN_LD2 = _MX27_BUILD_PIN(0, 8),
+ MX27_PIN_LD3 = _MX27_BUILD_PIN(0, 9),
+ MX27_PIN_LD4 = _MX27_BUILD_PIN(0, 10),
+ MX27_PIN_LD5 = _MX27_BUILD_PIN(0, 11),
+ MX27_PIN_LD6 = _MX27_BUILD_PIN(0, 12),
+ MX27_PIN_LD7 = _MX27_BUILD_PIN(0, 13),
+ MX27_PIN_LD8 = _MX27_BUILD_PIN(0, 14),
+ MX27_PIN_LD9 = _MX27_BUILD_PIN(0, 15),
+ MX27_PIN_LD10 = _MX27_BUILD_PIN(0, 16),
+ MX27_PIN_LD11 = _MX27_BUILD_PIN(0, 17),
+ MX27_PIN_LD12 = _MX27_BUILD_PIN(0, 18),
+ MX27_PIN_LD13 = _MX27_BUILD_PIN(0, 19),
+ MX27_PIN_LD14 = _MX27_BUILD_PIN(0, 20),
+ MX27_PIN_LD15 = _MX27_BUILD_PIN(0, 21),
+ MX27_PIN_LD16 = _MX27_BUILD_PIN(0, 22),
+ MX27_PIN_LD17 = _MX27_BUILD_PIN(0, 23),
+ MX27_PIN_REV = _MX27_BUILD_PIN(0, 24),
+ MX27_PIN_CLS = _MX27_BUILD_PIN(0, 25),
+ MX27_PIN_PS = _MX27_BUILD_PIN(0, 26),
+ MX27_PIN_SPL_SPR = _MX27_BUILD_PIN(0, 27),
+ MX27_PIN_HSYNC = _MX27_BUILD_PIN(0, 28),
+ MX27_PIN_VSYNC = _MX27_BUILD_PIN(0, 29),
+ MX27_PIN_CONTRAST = _MX27_BUILD_PIN(0, 30),
+ MX27_PIN_OE_ACD = _MX27_BUILD_PIN(0, 31),
+
+ MX27_PIN_SD2_D0 = _MX27_BUILD_PIN(1, 4),
+ MX27_PIN_SD2_D1 = _MX27_BUILD_PIN(1, 5),
+ MX27_PIN_SD2_D2 = _MX27_BUILD_PIN(1, 6),
+ MX27_PIN_SD2_D3 = _MX27_BUILD_PIN(1, 7),
+ MX27_PIN_SD2_CMD = _MX27_BUILD_PIN(1, 8),
+ MX27_PIN_SD2_CLK = _MX27_BUILD_PIN(1, 9),
+ MX27_PIN_CSI_D0 = _MX27_BUILD_PIN(1, 10),
+ MX27_PIN_CSI_D1 = _MX27_BUILD_PIN(1, 11),
+ MX27_PIN_CSI_D2 = _MX27_BUILD_PIN(1, 12),
+ MX27_PIN_CSI_D3 = _MX27_BUILD_PIN(1, 13),
+ MX27_PIN_CSI_D4 = _MX27_BUILD_PIN(1, 14),
+ MX27_PIN_CSI_MCLK = _MX27_BUILD_PIN(1, 15),
+ MX27_PIN_CSI_PIXCLK = _MX27_BUILD_PIN(1, 16),
+ MX27_PIN_CSI_D5 = _MX27_BUILD_PIN(1, 17),
+ MX27_PIN_CSI_D6 = _MX27_BUILD_PIN(1, 18),
+ MX27_PIN_CSI_D7 = _MX27_BUILD_PIN(1, 19),
+ MX27_PIN_CSI_VSYNC = _MX27_BUILD_PIN(1, 20),
+ MX27_PIN_CSI_HSYNC = _MX27_BUILD_PIN(1, 21),
+ MX27_PIN_USBH1_SUSP = _MX27_BUILD_PIN(1, 22),
+ MX27_PIN_USB_PWR = _MX27_BUILD_PIN(1, 23),
+ MX27_PIN_USB_OC_B = _MX27_BUILD_PIN(1, 24),
+ MX27_PIN_USBH1_RCV = _MX27_BUILD_PIN(1, 25),
+ MX27_PIN_USBH1_FS = _MX27_BUILD_PIN(1, 26),
+ MX27_PIN_USBH1_OE_B = _MX27_BUILD_PIN(1, 27),
+ MX27_PIN_USBH1_TXDM = _MX27_BUILD_PIN(1, 28),
+ MX27_PIN_USBH1_TXDP = _MX27_BUILD_PIN(1, 29),
+ MX27_PIN_USBH1_RXDM = _MX27_BUILD_PIN(1, 30),
+ MX27_PIN_USBH1_RXDP = _MX27_BUILD_PIN(1, 31),
+
+ MX27_PIN_I2C2_SDA = _MX27_BUILD_PIN(2, 5),
+ MX27_PIN_I2C2_SCL = _MX27_BUILD_PIN(2, 6),
+ MX27_PIN_USBOTG_DATA5 = _MX27_BUILD_PIN(2, 7),
+ MX27_PIN_USBOTG_DATA6 = _MX27_BUILD_PIN(2, 8),
+ MX27_PIN_USBOTG_DATA0 = _MX27_BUILD_PIN(2, 9),
+ MX27_PIN_USBOTG_DATA2 = _MX27_BUILD_PIN(2, 10),
+ MX27_PIN_USBOTG_DATA1 = _MX27_BUILD_PIN(2, 11),
+ MX27_PIN_USBOTG_DATA4 = _MX27_BUILD_PIN(2, 12),
+ MX27_PIN_USBOTG_DATA3 = _MX27_BUILD_PIN(2, 13),
+ MX27_PIN_TOUT = _MX27_BUILD_PIN(2, 14),
+ MX27_PIN_TIN = _MX27_BUILD_PIN(2, 15),
+ MX27_PIN_SSI4_FS = _MX27_BUILD_PIN(2, 16),
+ MX27_PIN_SSI4_RXDAT = _MX27_BUILD_PIN(2, 17),
+ MX27_PIN_SSI4_TXDAT = _MX27_BUILD_PIN(2, 18),
+ MX27_PIN_SSI4_CLK = _MX27_BUILD_PIN(2, 19),
+ MX27_PIN_SSI1_FS = _MX27_BUILD_PIN(2, 20),
+ MX27_PIN_SSI1_RXDAT = _MX27_BUILD_PIN(2, 21),
+ MX27_PIN_SSI1_TXDAT = _MX27_BUILD_PIN(2, 22),
+ MX27_PIN_SSI1_CLK = _MX27_BUILD_PIN(2, 23),
+ MX27_PIN_SSI2_FS = _MX27_BUILD_PIN(2, 24),
+ MX27_PIN_SSI2_RXDAT = _MX27_BUILD_PIN(2, 25),
+ MX27_PIN_SSI2_TXDAT = _MX27_BUILD_PIN(2, 26),
+ MX27_PIN_SSI2_CLK = _MX27_BUILD_PIN(2, 27),
+ MX27_PIN_SSI3_FS = _MX27_BUILD_PIN(2, 28),
+ MX27_PIN_SSI3_RXDAT = _MX27_BUILD_PIN(2, 29),
+ MX27_PIN_SSI3_TXDAT = _MX27_BUILD_PIN(2, 30),
+ MX27_PIN_SSI3_CLK = _MX27_BUILD_PIN(2, 31),
+
+ MX27_PIN_SD3_CMD = _MX27_BUILD_PIN(3, 0),
+ MX27_PIN_SD3_CLK = _MX27_BUILD_PIN(3, 1),
+ MX27_PIN_ATA_DATA0 = _MX27_BUILD_PIN(3, 2),
+ MX27_PIN_ATA_DATA1 = _MX27_BUILD_PIN(3, 3),
+ MX27_PIN_ATA_DATA2 = _MX27_BUILD_PIN(3, 4),
+ MX27_PIN_ATA_DATA3 = _MX27_BUILD_PIN(3, 5),
+ MX27_PIN_ATA_DATA4 = _MX27_BUILD_PIN(3, 6),
+ MX27_PIN_ATA_DATA5 = _MX27_BUILD_PIN(3, 7),
+ MX27_PIN_ATA_DATA6 = _MX27_BUILD_PIN(3, 8),
+ MX27_PIN_ATA_DATA7 = _MX27_BUILD_PIN(3, 9),
+ MX27_PIN_ATA_DATA8 = _MX27_BUILD_PIN(3, 10),
+ MX27_PIN_ATA_DATA9 = _MX27_BUILD_PIN(3, 11),
+ MX27_PIN_ATA_DATA10 = _MX27_BUILD_PIN(3, 12),
+ MX27_PIN_ATA_DATA11 = _MX27_BUILD_PIN(3, 13),
+ MX27_PIN_ATA_DATA12 = _MX27_BUILD_PIN(3, 14),
+ MX27_PIN_ATA_DATA13 = _MX27_BUILD_PIN(3, 15),
+ MX27_PIN_ATA_DATA14 = _MX27_BUILD_PIN(3, 16),
+ MX27_PIN_I2C_DATA = _MX27_BUILD_PIN(3, 17),
+ MX27_PIN_I2C_CLK = _MX27_BUILD_PIN(3, 18),
+ MX27_PIN_CSPI2_SS2 = _MX27_BUILD_PIN(3, 19),
+ MX27_PIN_CSPI2_SS1 = _MX27_BUILD_PIN(3, 20),
+ MX27_PIN_CSPI2_SS0 = _MX27_BUILD_PIN(3, 21),
+ MX27_PIN_CSPI2_SCLK = _MX27_BUILD_PIN(3, 22),
+ MX27_PIN_CSPI2_MISO = _MX27_BUILD_PIN(3, 23),
+ MX27_PIN_CSPI2_MOSI = _MX27_BUILD_PIN(3, 24),
+ MX27_PIN_CSPI1_RDY = _MX27_BUILD_PIN(3, 25),
+ MX27_PIN_CSPI1_SS2 = _MX27_BUILD_PIN(3, 26),
+ MX27_PIN_CSPI1_SS1 = _MX27_BUILD_PIN(3, 27),
+ MX27_PIN_CSPI1_SS0 = _MX27_BUILD_PIN(3, 28),
+ MX27_PIN_CSPI1_SCLK = _MX27_BUILD_PIN(3, 29),
+ MX27_PIN_CSPI1_MISO = _MX27_BUILD_PIN(3, 30),
+ MX27_PIN_CSPI1_MOSI = _MX27_BUILD_PIN(3, 31),
+
+ MX27_PIN_USBOTG_NXT = _MX27_BUILD_PIN(4, 0),
+ MX27_PIN_USBOTG_STP = _MX27_BUILD_PIN(4, 1),
+ MX27_PIN_USBOTG_DIR = _MX27_BUILD_PIN(4, 2),
+ MX27_PIN_UART2_CTS = _MX27_BUILD_PIN(4, 3),
+ MX27_PIN_UART2_RTS = _MX27_BUILD_PIN(4, 4),
+ MX27_PIN_PWMO = _MX27_BUILD_PIN(4, 5),
+ MX27_PIN_UART2_TXD = _MX27_BUILD_PIN(4, 6),
+ MX27_PIN_UART2_RXD = _MX27_BUILD_PIN(4, 7),
+ MX27_PIN_UART3_TXD = _MX27_BUILD_PIN(4, 8),
+ MX27_PIN_UART3_RXD = _MX27_BUILD_PIN(4, 9),
+ MX27_PIN_UART3_CTS = _MX27_BUILD_PIN(4, 10),
+ MX27_PIN_UART3_RTS = _MX27_BUILD_PIN(4, 11),
+ MX27_PIN_UART1_TXD = _MX27_BUILD_PIN(4, 12),
+ MX27_PIN_UART1_RXD = _MX27_BUILD_PIN(4, 13),
+ MX27_PIN_UART1_CTS = _MX27_BUILD_PIN(4, 14),
+ MX27_PIN_UART1_RTS = _MX27_BUILD_PIN(4, 15),
+ MX27_PIN_RTCK = _MX27_BUILD_PIN(4, 16),
+ MX27_PIN_RESET_OUT_B = _MX27_BUILD_PIN(4, 17),
+ MX27_PIN_SD1_D0 = _MX27_BUILD_PIN(4, 18),
+ MX27_PIN_SD1_D1 = _MX27_BUILD_PIN(4, 19),
+ MX27_PIN_SD1_D2 = _MX27_BUILD_PIN(4, 20),
+ MX27_PIN_SD1_D3 = _MX27_BUILD_PIN(4, 21),
+ MX27_PIN_SD1_CMD = _MX27_BUILD_PIN(4, 22),
+ MX27_PIN_SD1_CLK = _MX27_BUILD_PIN(4, 23),
+ MX27_PIN_USBOTG_CLK = _MX27_BUILD_PIN(4, 24),
+ MX27_PIN_USBOTG_DATA7 = _MX27_BUILD_PIN(4, 25),
+
+ MX27_PIN_NFRB = _MX27_BUILD_PIN(5, 0),
+ MX27_PIN_NFCLE = _MX27_BUILD_PIN(5, 1),
+ MX27_PIN_NFWP_B = _MX27_BUILD_PIN(5, 2),
+ MX27_PIN_NFCE_B = _MX27_BUILD_PIN(5, 3),
+ MX27_PIN_NFALE = _MX27_BUILD_PIN(5, 4),
+ MX27_PIN_NFRE_B = _MX27_BUILD_PIN(5, 5),
+ MX27_PIN_NFWE_B = _MX27_BUILD_PIN(5, 6),
+ MX27_PIN_PC_POE = _MX27_BUILD_PIN(5, 7),
+ MX27_PIN_PC_RW_B = _MX27_BUILD_PIN(5, 8),
+ MX27_PIN_IOIS16 = _MX27_BUILD_PIN(5, 9),
+ MX27_PIN_PC_RST = _MX27_BUILD_PIN(5, 10),
+ MX27_PIN_PC_BVD2 = _MX27_BUILD_PIN(5, 11),
+ MX27_PIN_PC_BVD1 = _MX27_BUILD_PIN(5, 12),
+ MX27_PIN_PC_VS2 = _MX27_BUILD_PIN(5, 13),
+ MX27_PIN_PC_VS1 = _MX27_BUILD_PIN(5, 14),
+ MX27_PIN_CLKO = _MX27_BUILD_PIN(5, 15),
+ MX27_PIN_PC_PWRON = _MX27_BUILD_PIN(5, 16),
+ MX27_PIN_PC_READY = _MX27_BUILD_PIN(5, 17),
+ MX27_PIN_PC_WAIT_B = _MX27_BUILD_PIN(5, 18),
+ MX27_PIN_PC_CD2_B = _MX27_BUILD_PIN(5, 19),
+ MX27_PIN_PC_CD1_B = _MX27_BUILD_PIN(5, 20),
+ MX27_PIN_CS4_B = _MX27_BUILD_PIN(5, 21),
+ MX27_PIN_CS5_B = _MX27_BUILD_PIN(5, 22),
+ MX27_PIN_ATA_DATA15 = _MX27_BUILD_PIN(5, 23),
+};
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ASM_ARCH_MXC_MX27_PINS_H__ */
diff --git a/arch/arm/mach-mx27/mx27ads.c b/arch/arm/mach-mx27/mx27ads.c
new file mode 100644
index 000000000000..5568fb175e49
--- /dev/null
+++ b/arch/arm/mach-mx27/mx27ads.c
@@ -0,0 +1,833 @@
+/*
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ * Copyright (C) 2002 Shane Nay (shane@minirl.com)
+ * Copyright 2006-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/nodemask.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/serial_8250.h>
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/mach/flash.h>
+#endif
+
+#include <mach/hardware.h>
+#include <mach/common.h>
+#include <mach/memory.h>
+#include <mach/gpio.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/keypad.h>
+#include <asm/mach/time.h>
+#include "gpio_mux.h"
+#include "board-mx27ads.h"
+
+/*!
+ * @file mach-mx27/mx27ads.c
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX27
+ */
+
+extern void mxc_map_io(void);
+extern void mxc_init_irq(void);
+extern void mxc_cpu_init(void) __init;
+extern void mxc_cpu_common_init(void);
+extern void __init early_console_setup(char *);
+
+static char command_line[COMMAND_LINE_SIZE];
+static int mxc_card_status;
+int mxc_board_is_ads = 1;
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+unsigned long board_get_ckih_rate(void)
+{
+ if ((__raw_readw(PBC_VERSION_REG) & CKIH_27MHZ_BIT_SET) == 0) {
+ return 27000000;
+ }
+ return 26000000;
+}
+
+#if defined(CONFIG_CS89x0) || defined(CONFIG_CS89x0_MODULE)
+/*! Null terminated portlist used to probe for the CS8900A device on ISA Bus
+ * Add 3 to reset the page window before probing (fixes eth probe when deployed
+ * using nand_boot)
+ */
+unsigned int netcard_portlist[] = { CS8900A_BASE_ADDRESS + 3, 0 };
+
+EXPORT_SYMBOL(netcard_portlist);
+/*!
+ * The CS8900A has 4 IRQ pins, which is software selectable, CS8900A interrupt
+ * pin 0 is used for interrupt generation.
+ */
+unsigned int cs8900_irq_map[] = { CS8900AIRQ, 0, 0, 0 };
+
+EXPORT_SYMBOL(cs8900_irq_map);
+#endif
+
+#if defined(CONFIG_FEC) || defined(CONFIG_FEC_MODULE)
+unsigned int expio_intr_fec = MXC_EXP_IO_BASE + 7;
+
+EXPORT_SYMBOL(expio_intr_fec);
+#endif
+
+#if defined(CONFIG_KEYBOARD_MXC) || defined(CONFIG_KEYBOARD_MXC_MODULE)
+
+/*!
+ * This array is used for mapping mx27 ADS keypad scancodes to input keyboard
+ * keycodes.
+ */
+static u16 mxckpd_keycodes[] = {
+ KEY_KP9, KEY_LEFTSHIFT, KEY_0, KEY_KPASTERISK, KEY_RECORD, KEY_POWER,
+ KEY_KP8, KEY_9, KEY_8, KEY_7, KEY_KP5, KEY_VOLUMEDOWN,
+ KEY_KP7, KEY_6, KEY_5, KEY_4, KEY_KP4, KEY_VOLUMEUP,
+ KEY_KP6, KEY_3, KEY_2, KEY_1, KEY_KP3, KEY_DOWN,
+ KEY_BACK, KEY_RIGHT, KEY_ENTER, KEY_LEFT, KEY_HOME, KEY_KP2,
+ KEY_END, KEY_F2, KEY_UP, KEY_F1, KEY_F4, KEY_KP1,
+};
+
+static struct keypad_data evb_6_by_6_keypad = {
+ .rowmax = 6,
+ .colmax = 6,
+ .irq = MXC_INT_KPP,
+ .learning = 0,
+ .delay = 2,
+ .matrix = mxckpd_keycodes,
+};
+
+static struct resource mxc_kpp_resources[] = {
+ [0] = {
+ .start = MXC_INT_KPP,
+ .end = MXC_INT_KPP,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+/* mxc keypad driver */
+static struct platform_device mxc_keypad_device = {
+ .name = "mxc_keypad",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_kpp_resources),
+ .resource = mxc_kpp_resources,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &evb_6_by_6_keypad,
+ },
+};
+
+static void mxc_init_keypad(void)
+{
+ (void)platform_device_register(&mxc_keypad_device);
+}
+#else
+static inline void mxc_init_keypad(void)
+{
+}
+#endif
+
+/* MTD NOR flash */
+
+#if defined(CONFIG_MTD_MXC) || defined(CONFIG_MTD_MXC_MODULE)
+
+static struct mtd_partition mxc_nor_partitions[] = {
+ {
+ .name = "Bootloader",
+ .size = 512 * 1024,
+ .offset = 0x00000000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "nor.Kernel",
+ .size = 2 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = 0},
+ {
+ .name = "nor.userfs",
+ .size = 14 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = 0},
+ {
+ .name = "nor.rootfs",
+ .size = 12 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE},
+ {
+ .name = "FIS directory",
+ .size = 12 * 1024,
+ .offset = 0x01FE0000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "Redboot config",
+ .size = MTDPART_SIZ_FULL,
+ .offset = 0x01FFF000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+};
+
+static struct flash_platform_data mxc_flash_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+ .parts = mxc_nor_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_nor_partitions),
+};
+
+static struct resource mxc_flash_resource = {
+ .start = 0xc0000000,
+ .end = 0xc0000000 + 0x02000000 - 1,
+ .flags = IORESOURCE_MEM,
+
+};
+
+static struct platform_device mxc_nor_mtd_device = {
+ .name = "mxc_nor_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &mxc_flash_resource,
+};
+
+static void mxc_init_nor_mtd(void)
+{
+ (void)platform_device_register(&mxc_nor_mtd_device);
+}
+#else
+static void mxc_init_nor_mtd(void)
+{
+}
+#endif
+
+/* MTD NAND flash */
+
+#if defined(CONFIG_MTD_NAND_MXC) || defined(CONFIG_MTD_NAND_MXC_MODULE)
+
+static struct mtd_partition mxc_nand_partitions[4] = {
+ {
+ .name = "nand.bootloader",
+ .offset = 0,
+ .size = 1024 * 1024},
+ {
+ .name = "nand.kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 5 * 1024 * 1024},
+ {
+ .name = "nand.rootfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 22 * 1024 * 1024},
+ {
+ .name = "nand.userfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL},
+};
+
+static struct flash_platform_data mxc_nand_data = {
+ .parts = mxc_nand_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_nand_partitions),
+ .width = 1,
+};
+
+static struct platform_device mxc_nand_mtd_device = {
+ .name = "mxc_nand_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static void mxc_init_nand_mtd(void)
+{
+ (void)platform_device_register(&mxc_nand_mtd_device);
+}
+#else
+static inline void mxc_init_nand_mtd(void)
+{
+}
+#endif
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+static const char fb_default_mode[] = "Sharp-QVGA";
+
+/* mxc lcd driver */
+static struct platform_device mxc_fb_device = {
+ .name = "mxc_sdc_fb",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &fb_default_mode,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+};
+
+static void mxc_init_fb(void)
+{
+ (void)platform_device_register(&mxc_fb_device);
+}
+#else
+static inline void mxc_init_fb(void)
+{
+}
+#endif
+
+#if defined(CONFIG_BACKLIGHT_MXC)
+static struct platform_device mxcbl_devices[] = {
+#if defined(CONFIG_BACKLIGHT_MXC_PMIC) || defined(CONFIG_BACKLIGHT_MXC_PMIC_MODULE)
+ {
+ .name = "mxc_pmic_bl",
+ .id = 0,
+ .dev = {
+ .platform_data = (void *)-1, /* DISP # for this backlight */
+ },
+ },
+ {
+ .name = "mxc_pmic_bl",
+ .id = 1,
+ .dev = {
+ .platform_data = (void *)0, /* DISP # for this backlight */
+ },
+ },
+#endif
+#if defined(CONFIG_BACKLIGHT_MXC_LCDC) || defined(CONFIG_BACKLIGHT_MXC_LCDC_MODULE)
+ {
+ .name = "mxc_lcdc_bl",
+ .id = 0,
+ .dev = {
+ .platform_data = (void *)3, /* DISP # for this backlight */
+ },
+ },
+#endif
+};
+static inline void mxc_init_bl(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mxcbl_devices); i++) {
+ platform_device_register(&mxcbl_devices[i]);
+ }
+}
+#else
+static inline void mxc_init_bl(void)
+{
+}
+#endif
+
+static struct spi_board_info mxc_spi_board_info[] __initdata = {
+ {
+ .modalias = "pmic_spi",
+ .irq = IOMUX_TO_IRQ(MX27_PIN_TOUT),
+ .max_speed_hz = 4000000,
+ .bus_num = 1,
+ .chip_select = 0,
+ },
+};
+
+#if 0
+#define MXC_CARD_DEBUG
+#endif
+
+static const int pbc_card_bit[4][3] = {
+ /* BSTAT IMR enable IMR removal */
+ {PBC_BSTAT_SD2_DET, PBC_INTR_SD2_EN, PBC_INTR_SD2_R_EN},
+ {PBC_BSTAT_SD3_DET, PBC_INTR_SD3_EN, PBC_INTR_SD3_R_EN},
+ {PBC_BSTAT_MS_DET, PBC_INTR_MS_EN, PBC_INTR_MS_R_EN},
+ {PBC_BSTAT_SD1_DET, PBC_INTR_SD1_EN, PBC_INTR_SD1_R_EN},
+};
+
+/*!
+ * Check if a SD card has been inserted or not.
+ *
+ * @param num a card number as defined in \b enum \b mxc_card_no
+ * @return 0 if a card is not present; non-zero otherwise.
+ */
+int mxc_card_detected(enum mxc_card_no num)
+{
+ u32 status;
+
+ status = __raw_readw(PBC_BSTAT1_REG);
+ return ((status & MXC_BSTAT_BIT(num)) == 0);
+}
+
+/*
+ * Check if there is any state change by reading the IMR register and the
+ * previous and current states of the board status register (offset 0x28).
+ * A state change is defined to be card insertion OR removal. So the driver
+ * may have to call the mxc_card_detected() function to see if it is card
+ * insertion or removal.
+ *
+ * @param mask current IMR value
+ * @param s0 previous status register value (offset 0x28)
+ * @param s1 current status register value (offset 0x28)
+ *
+ * @return 0 if no card status change OR the corresponding bits in the IMR
+ * (passed in as 'mask') is NOT set.
+ * A non-zero value indicates some card state changes. For example,
+ * 0b0001 means SD3 has a card state change (bit0 is set) AND its
+ * associated insertion or removal bits in IMR is SET.
+ * 0b0100 means SD1 has a card state change (bit2 is set) AND its
+ * associated insertion or removal bits in IMR is SET.
+ * 0b1001 means both MS and SD3 have state changes
+ */
+static u32 mxc_card_state_changed(u32 mask, u32 s0, u32 s1)
+{
+ u32 i, retval = 0;
+ u32 stat = (s0 ^ s1) & 0x7800;
+
+ if (stat == 0)
+ return 0;
+
+ for (i = MXC_CARD_MIN; i <= MXC_CARD_MAX; i++) {
+ if ((stat & pbc_card_bit[i][0]) != 0 &&
+ (mask & (pbc_card_bit[i][1] | pbc_card_bit[i][2])) != 0) {
+ retval |= 1 << i;
+ }
+ }
+#ifdef MXC_CARD_DEBUG
+ printk(KERN_INFO "\nmask=%x, s0=%x, s1=%x\n", mask, s0, s1);
+ printk(KERN_INFO "retval=%x, stat=%x\n", retval, stat);
+#endif
+ return retval;
+}
+
+/*!
+ * Interrupt handler for the expio (CPLD) to deal with interrupts from
+ * FEC, external UART, CS8900 Ethernet and SD cards, etc.
+ */
+static void mxc_expio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 imr, card_int, i;
+ u32 int_valid;
+ u32 expio_irq;
+ u32 stat = __raw_readw(PBC_BSTAT1_REG);
+
+ desc->chip->mask(irq); /* irq = gpio irq number */
+
+ imr = __raw_readw(PBC_INTMASK_SET_REG);
+
+ card_int = mxc_card_state_changed(imr, mxc_card_status, stat);
+ mxc_card_status = stat;
+
+ if (card_int != 0) {
+ for (i = MXC_CARD_MIN; i <= MXC_CARD_MAX; i++) {
+ if ((card_int & (1 << i)) != 0) {
+ pr_debug("card no %d state changed\n", i);
+ }
+ }
+ }
+
+ /* Bits defined in PBC_INTSTATUS_REG at 0x2C */
+ int_valid = __raw_readw(PBC_INTSTATUS_REG) & imr;
+ /* combined with the card interrupt valid information */
+ int_valid = (int_valid & 0x0F8E) | (card_int << PBC_INTR_SD2_EN_BIT);
+
+ if (unlikely(!int_valid)) {
+ pr_debug("\nEXPIO: Spurious interrupt:0x%0x\n\n", int_valid);
+ pr_debug("CPLD IMR(0x38)=0x%x, BSTAT1(0x28)=0x%x\n", imr, stat);
+ goto out;
+ }
+
+ expio_irq = MXC_EXP_IO_BASE;
+ for (; int_valid != 0; int_valid >>= 1, expio_irq++) {
+ struct irq_desc *d;
+ if ((int_valid & 1) == 0)
+ continue;
+ d = irq_desc + expio_irq;
+ if (unlikely(!(d->handle_irq))) {
+ printk(KERN_ERR "\nEXPIO irq: %d unhandeled\n",
+ expio_irq);
+ BUG(); /* oops */
+ }
+ d->handle_irq(expio_irq, d);
+ }
+
+ out:
+ desc->chip->ack(irq);
+ desc->chip->unmask(irq);
+}
+
+#ifdef MXC_CARD_DEBUG
+
+static irqreturn_t mxc_sd_test_handler(int irq, void *desc)
+{
+ int s = -1;
+
+ printk(KERN_INFO "%s(irq=%d) for ", __FUNCTION__, irq);
+ if (irq == EXPIO_INT_SD1_EN) {
+ printk(KERN_INFO "SD1");
+ s = MXC_CARD_SD1;
+ } else if (irq == EXPIO_INT_SD2_EN) {
+ printk(KERN_INFO "SD2");
+ s = MXC_CARD_SD2;
+ } else if (irq == EXPIO_INT_SD3_EN) {
+ printk(KERN_INFO "SD3");
+ s = MXC_CARD_SD3;
+ } else if (irq == EXPIO_INT_MS_EN) {
+ printk(KERN_INFO "MS");
+ s = MXC_CARD_MS;
+ } else {
+ printk(KERN_INFO "None!!!!");
+ }
+ if (mxc_card_detected(s)) {
+ printk(KERN_INFO " inserted\n");
+ } else {
+ printk(KERN_INFO " removed\n");
+ }
+
+ return IRQ_HANDLED;
+}
+#endif /* MXC_CARD_DEBUG */
+
+/*
+ * Disable an expio pin's interrupt by setting the bit in the imr.
+ * @param irq an expio virtual irq number
+ */
+static void expio_mask_irq(u32 irq)
+{
+ u32 expio = MXC_IRQ_TO_EXPIO(irq);
+
+ /* mask the interrupt */
+ if (irq < EXPIO_INT_SD2_EN) {
+ __raw_writew(1 << expio, PBC_INTMASK_CLEAR_REG);
+ } else {
+ irq -= EXPIO_INT_SD2_EN;
+ /* clear both SDx_EN and SDx_R_EN bits */
+ __raw_writew((pbc_card_bit[irq][1] | pbc_card_bit[irq][2]),
+ PBC_INTMASK_CLEAR_REG);
+ }
+}
+
+/*
+ * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr.
+ * @param irq an expanded io virtual irq number
+ */
+static void expio_ack_irq(u32 irq)
+{
+ u32 expio = MXC_IRQ_TO_EXPIO(irq);
+ /* clear the interrupt status */
+ __raw_writew(1 << expio, PBC_INTSTATUS_REG);
+ /* mask the interrupt */
+ expio_mask_irq(irq);
+}
+
+/*
+ * Enable a expio pin's interrupt by clearing the bit in the imr.
+ * @param irq an expio virtual irq number
+ */
+static void expio_unmask_irq(u32 irq)
+{
+ u32 expio = MXC_IRQ_TO_EXPIO(irq);
+
+ /* unmask the interrupt */
+ if (irq < EXPIO_INT_SD2_EN) {
+ if (irq == EXPIO_INT_XUART_INTA) {
+ /* Set 8250 MCR register bit 3 - Forces the INT (A-B
+ * outputs to the active mode and sets OP2 to logic 0.
+ * This is needed to avoid spurious int caused by the
+ * internal CPLD pull-up for the interrupt pin.
+ */
+ u16 val = __raw_readw(MXC_LL_EXTUART_VADDR + 8);
+ __raw_writew(val | 0x8, MXC_LL_EXTUART_VADDR + 8);
+ }
+ __raw_writew(1 << expio, PBC_INTMASK_SET_REG);
+ } else {
+ irq -= EXPIO_INT_SD2_EN;
+
+ if (mxc_card_detected(irq)) {
+ __raw_writew(pbc_card_bit[irq][2], PBC_INTMASK_SET_REG);
+ } else {
+ __raw_writew(pbc_card_bit[irq][1], PBC_INTMASK_SET_REG);
+ }
+ }
+}
+
+static struct irq_chip expio_irq_chip = {
+ .ack = expio_ack_irq,
+ .mask = expio_mask_irq,
+ .unmask = expio_unmask_irq,
+};
+
+static int __init mxc_expio_init(void)
+{
+ int i, ver;
+
+ ver = (__raw_readw(PBC_VERSION_REG) >> 8) & 0xFF;
+ if ((ver & 0x80) != 0) {
+ pr_info("MX27 ADS EXPIO(CPLD) hardware\n");
+ pr_info("CPLD version: 0x%x\n", ver);
+ } else {
+ mxc_board_is_ads = 0;
+ ver &= 0x0F;
+ pr_info("MX27 EVB EXPIO(CPLD) hardware\n");
+ if (ver == 0xF || ver <= MXC_CPLD_VER_1_50)
+ pr_info("Wrong CPLD version: %d\n", ver);
+ else {
+ pr_info("CPLD version: %d\n", ver);
+ }
+ }
+
+ mxc_card_status = __raw_readw(PBC_BSTAT1_REG);
+
+#ifdef MXC_CARD_DEBUG
+ for (i = MXC_CARD_MIN; i <= MXC_CARD_MAX; i++) {
+ if (mxc_card_detected(i)) {
+ pr_info("Card %d is detected\n", 3 - i);
+ }
+ }
+#endif
+ /*
+ * Configure INT line as GPIO input
+ */
+ gpio_config_mux(MX27_PIN_TIN, GPIO_MUX_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_TIN), NULL);
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_TIN));
+
+ /* disable the interrupt and clear the status */
+ __raw_writew(0xFFFF, PBC_INTMASK_CLEAR_REG);
+ __raw_writew(0xFFFF, PBC_INTSTATUS_REG);
+
+ for (i = MXC_EXP_IO_BASE; i < (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES);
+ i++) {
+ set_irq_chip(i, &expio_irq_chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+ set_irq_type(EXPIO_PARENT_INT, IRQF_TRIGGER_HIGH);
+ set_irq_chained_handler(EXPIO_PARENT_INT, mxc_expio_irq_handler);
+
+ return 0;
+}
+
+#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE)
+
+/*!
+ * The serial port definition structure. The fields contain:
+ * {UART, CLK, PORT, IRQ, FLAGS}
+ */
+static struct plat_serial8250_port serial_platform_data[] = {
+ {
+ .membase = (void __iomem *)(CS4_BASE_ADDR_VIRT + 0x20000),
+ .mapbase = (unsigned long)(CS4_BASE_ADDR + 0x20000),
+ .irq = EXPIO_INT_XUART_INTA,
+ .uartclk = 3686400,
+ .regshift = 1,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ,
+ /*.pm = serial_platform_pm, */
+ },
+ {},
+};
+
+/*!
+ * REVISIT: document me
+ */
+static struct platform_device serial_device = {
+ .name = "serial8250",
+ .id = 0,
+ .dev = {
+ .platform_data = &serial_platform_data[0],
+ },
+};
+
+/*!
+ * REVISIT: document me
+ */
+static int __init mxc_init_extuart(void)
+{
+ int value;
+ /*reset ext uart in cpld */
+ __raw_writew(PBC_BCTRL1_URST, PBC_BCTRL1_SET_REG);
+ /*delay some time for reset finish */
+ for (value = 0; value < 1000; value++) ;
+ __raw_writew(PBC_BCTRL1_URST, PBC_BCTRL1_CLEAR_REG);
+ return platform_device_register(&serial_device);
+}
+#else
+static inline int mxc_init_extuart(void)
+{
+ return 0;
+}
+#endif
+
+#if (defined(CONFIG_MXC_PMIC_MC13783) || \
+ defined(CONFIG_MXC_PMIC_MC13783_MODULE)) \
+ && (defined(CONFIG_SND_MXC_PMIC) || defined(CONFIG_SND_MXC_PMIC_MODULE))
+extern void gpio_ssi_active(int ssi_num);
+
+static void __init mxc_init_pmic_audio(void)
+{
+ struct clk *ckih_clk;
+ struct clk *cko_clk;
+
+ /* Enable 26 mhz clock on CKO1 for PMIC audio */
+ ckih_clk = clk_get(NULL, "ckih");
+ cko_clk = clk_get(NULL, "clko_clk");
+ if (IS_ERR(ckih_clk) || IS_ERR(cko_clk)) {
+ printk(KERN_ERR "Unable to set CLKO output to CKIH\n");
+ } else {
+ clk_set_parent(cko_clk, ckih_clk);
+ clk_set_rate(cko_clk, clk_get_rate(ckih_clk));
+ clk_enable(cko_clk);
+ }
+ clk_put(ckih_clk);
+ clk_put(cko_clk);
+
+ gpio_ssi_active(0);
+ gpio_ssi_active(1);
+}
+#else
+static void __inline mxc_init_pmic_audio(void)
+{
+}
+#endif
+
+/* IDE device data */
+#if defined(CONFIG_BLK_DEV_IDE_MXC) || defined(CONFIG_BLK_DEV_IDE_MXC_MODULE)
+
+/*! Platform Data for MXC IDE */
+static struct mxc_ide_platform_data mxc_ide_data = {
+ .power_drive = NULL,
+ .power_io = NULL,
+};
+
+static struct platform_device mxc_ide_device = {
+ .name = "mxc_ide",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_ide_data,
+ },
+};
+
+static inline void mxc_init_ide(void)
+{
+ if (platform_device_register(&mxc_ide_device) < 0)
+ printk(KERN_ERR "Error: Registering the ide.\n");
+}
+#else
+static inline void mxc_init_ide(void)
+{
+}
+#endif
+
+static __init void mxc_board_init(void)
+{
+ pr_info("AIPI VA base: 0x%x\n", IO_ADDRESS(AIPI_BASE_ADDR));
+ mxc_cpu_common_init();
+ early_console_setup(saved_command_line);
+ mxc_register_gpios();
+ mxc_expio_init();
+ mxc_init_keypad();
+ mxc_init_nor_mtd();
+ mxc_init_nand_mtd();
+ mxc_init_extuart();
+ mxc_init_pmic_audio();
+#ifdef MXC_CARD_DEBUG
+ request_irq(EXPIO_INT_SD1_EN, mxc_sd_test_handler, 0, "SD_card1", NULL);
+ request_irq(EXPIO_INT_SD2_EN, mxc_sd_test_handler, 0, "SD_card2", NULL);
+ request_irq(EXPIO_INT_SD3_EN, mxc_sd_test_handler, 0, "SD_card3", NULL);
+ request_irq(EXPIO_INT_MS_EN, mxc_sd_test_handler, 0, "MS_card", NULL);
+#endif
+
+ spi_register_board_info(mxc_spi_board_info,
+ ARRAY_SIZE(mxc_spi_board_info));
+
+ mxc_init_fb();
+ mxc_init_bl();
+ mxc_init_ide();
+}
+
+static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+#ifdef CONFIG_KGDB_8250
+ int i;
+ for (i = 0;
+ i <
+ (sizeof(serial_platform_data) / sizeof(serial_platform_data[0]));
+ i += 1)
+ kgdb8250_add_platform_port(i, &serial_platform_data[i]);
+#endif
+
+ mxc_cpu_init();
+ /* Store command line for use on mxc_board_init */
+ strcpy(command_line, *cmdline);
+
+#ifdef CONFIG_DISCONTIGMEM
+ do {
+ int nid;
+ mi->nr_banks = MXC_NUMNODES;
+ for (nid = 0; nid < mi->nr_banks; nid++) {
+ SET_NODE(mi, nid);
+ }
+ } while (0);
+#endif
+}
+
+EXPORT_SYMBOL(mxc_card_detected);
+EXPORT_SYMBOL(mxc_board_is_ads);
+
+static void __init mx27ads_timer_init(void)
+{
+ mxc_clocks_init(32768, 0, board_get_ckih_rate(), 0);
+ mxc_timer_init("gpt_clk.0");
+}
+
+static struct sys_timer mxc_timer = {
+ .init = mx27ads_timer_init,
+};
+
+/*
+ * The following uses standard kernel macros define in arch.h in order to
+ * initialize __mach_desc_MX27ADS data structure.
+ */
+/* *INDENT-OFF* */
+MACHINE_START(MX27ADS, "Freescale i.MX27ADS")
+ /* maintainer: Freescale Semiconductor, Inc. */
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+ .phys_io = CS4_BASE_ADDR,
+ .io_pg_offst = ((CS4_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+#else
+ .phys_io = AIPI_BASE_ADDR,
+ .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+#endif
+ .boot_params = PHYS_OFFSET + 0x100,
+ .fixup = fixup_mxc_board,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mxc_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx27/mx27ads_gpio.c b/arch/arm/mach-mx27/mx27ads_gpio.c
new file mode 100644
index 000000000000..a71d15725d0b
--- /dev/null
+++ b/arch/arm/mach-mx27/mx27ads_gpio.c
@@ -0,0 +1,1266 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+#include "board-mx27ads.h"
+#include "gpio_mux.h"
+#include "crm_regs.h"
+
+static int g_uart_activated[MXC_UART_NR] = { 0, 0, 0, 0, 0, 0 };
+
+/*!
+ * @file mach-mx27/mx27ads_gpio.c
+ *
+ * @brief This file contains all the GPIO setup functions for the board.
+ *
+ * @ingroup GPIO_MX27
+ */
+
+/*!
+ * Setup GPIO for a UART port to be active
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_active(int port, int no_irda)
+{
+ if (port < 0 || port >= MXC_UART_NR) {
+ pr_info("Wrong port number: %d\n", port);
+ BUG();
+ }
+
+ if (g_uart_activated[port]) {
+ pr_info("UART %d has been activated multi-times\n", port + 1);
+ return;
+ }
+ g_uart_activated[port] = 1;
+
+ switch (port) {
+ case 0:
+ gpio_request_mux(MX27_PIN_UART1_TXD, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_UART1_RXD, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_UART1_CTS, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_UART1_RTS, GPIO_MUX_PRIMARY);
+ break;
+ case 1:
+ gpio_request_mux(MX27_PIN_UART2_TXD, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_UART2_RXD, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_UART2_CTS, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_UART2_RTS, GPIO_MUX_PRIMARY);
+ break;
+ case 2:
+ gpio_request_mux(MX27_PIN_UART3_TXD, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_UART3_RXD, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_UART3_CTS, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_UART3_RTS, GPIO_MUX_PRIMARY);
+
+ /* Enable IRDA in CPLD */
+ __raw_writew(PBC_BCTRL2_IRDA_EN, PBC_BCTRL2_CLEAR_REG);
+ break;
+ case 3:
+ gpio_request_mux(MX27_PIN_USBH1_TXDM, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_USBH1_RXDP, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_USBH1_TXDP, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_USBH1_FS, GPIO_MUX_ALT);
+ break;
+ case 4:
+ gpio_request_mux(MX27_PIN_CSI_D6, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_CSI_D7, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_CSI_VSYNC, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_CSI_HSYNC, GPIO_MUX_ALT);
+ break;
+ case 5:
+ gpio_request_mux(MX27_PIN_CSI_D0, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_CSI_D1, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_CSI_D2, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_CSI_D3, GPIO_MUX_ALT);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup GPIO for a UART port to be inactive
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_inactive(int port, int no_irda)
+{
+ if (port < 0 || port >= MXC_UART_NR) {
+ pr_info("Wrong port number: %d\n", port);
+ BUG();
+ }
+
+ if (g_uart_activated[port] == 0) {
+ pr_info("UART %d has not been activated \n", port + 1);
+ return;
+ }
+ g_uart_activated[port] = 0;
+
+ switch (port) {
+ case 0:
+ gpio_free_mux(MX27_PIN_UART1_TXD);
+ gpio_free_mux(MX27_PIN_UART1_RXD);
+ gpio_free_mux(MX27_PIN_UART1_CTS);
+ gpio_free_mux(MX27_PIN_UART1_RTS);
+ break;
+ case 1:
+ gpio_free_mux(MX27_PIN_UART2_TXD);
+ gpio_free_mux(MX27_PIN_UART2_RXD);
+ gpio_free_mux(MX27_PIN_UART2_CTS);
+ gpio_free_mux(MX27_PIN_UART2_RTS);
+ break;
+ case 2:
+ gpio_free_mux(MX27_PIN_UART3_TXD);
+ gpio_free_mux(MX27_PIN_UART3_RXD);
+ gpio_free_mux(MX27_PIN_UART3_CTS);
+ gpio_free_mux(MX27_PIN_UART3_RTS);
+
+ /* Disable IRDA in CPLD */
+ __raw_writew(PBC_BCTRL2_IRDA_EN, PBC_BCTRL2_SET_REG);
+ break;
+ case 3:
+ gpio_free_mux(MX27_PIN_USBH1_TXDM);
+ gpio_free_mux(MX27_PIN_USBH1_RXDP);
+ gpio_free_mux(MX27_PIN_USBH1_TXDP);
+ gpio_free_mux(MX27_PIN_USBH1_FS);
+ break;
+ case 4:
+ gpio_free_mux(MX27_PIN_CSI_D6);
+ gpio_free_mux(MX27_PIN_CSI_D7);
+ gpio_free_mux(MX27_PIN_CSI_VSYNC);
+ gpio_free_mux(MX27_PIN_CSI_HSYNC);
+ break;
+ case 5:
+ gpio_free_mux(MX27_PIN_CSI_D0);
+ gpio_free_mux(MX27_PIN_CSI_D1);
+ gpio_free_mux(MX27_PIN_CSI_D2);
+ gpio_free_mux(MX27_PIN_CSI_D3);
+ break;
+ default:
+ break;
+ }
+}
+
+void gpio_power_key_active(void)
+{
+}
+EXPORT_SYMBOL(gpio_power_key_active);
+
+/*!
+ * Configure the IOMUX GPR register to receive shared SDMA UART events
+ *
+ * @param port a UART port
+ */
+void config_uartdma_event(int port)
+{
+ return;
+}
+
+static int usbh1_hs_active;
+/*!
+ * Setup GPIO for USB, Total 34 signals
+ * PIN Configuration for USBOTG: High/Full speed OTG
+ * PE2,PE1,PE0,PE24,PE25 -- PRIMARY
+ PC7 - PC13 -- PRIMARY
+ PB23,PB24 -- PRIMARY
+
+ * PIN Configuration for USBH2: : High/Full/Low speed host
+ * PA0 - PA4 -- PRIMARY
+ PD19, PD20,PD21,PD22,PD23,PD24,PD26 --Alternate (SECONDARY)
+
+ * PIN Configuration for USBH1: Full/low speed host
+ * PB25 - PB31 -- PRIMARY
+ PB22 -- PRIMARY
+ */
+int gpio_usbh1_active(void)
+{
+ if (usbh1_hs_active)
+ return 0;
+
+ if (gpio_request_mux(MX27_PIN_USBH1_SUSP, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH1_RCV, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH1_FS, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH1_OE_B, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH1_TXDM, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH1_TXDP, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH1_RXDM, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH1_RXDP, GPIO_MUX_PRIMARY))
+ return -EINVAL;
+
+ __raw_writew(PBC_BCTRL3_FSH_MOD, PBC_BCTRL3_CLEAR_REG);
+ __raw_writew(PBC_BCTRL3_FSH_VBUS_EN, PBC_BCTRL3_CLEAR_REG);
+ usbh1_hs_active = 1;
+ return 0;
+}
+void gpio_usbh1_inactive(void)
+{
+ if (usbh1_hs_active == 0)
+ return;
+
+ gpio_free_mux(MX27_PIN_USBH1_SUSP);
+ gpio_free_mux(MX27_PIN_USBH1_RCV);
+ gpio_free_mux(MX27_PIN_USBH1_FS);
+ gpio_free_mux(MX27_PIN_USBH1_OE_B);
+ gpio_free_mux(MX27_PIN_USBH1_TXDM);
+ gpio_free_mux(MX27_PIN_USBH1_TXDP);
+ gpio_free_mux(MX27_PIN_USBH1_RXDM);
+ gpio_free_mux(MX27_PIN_USBH1_RXDP);
+ __raw_writew(PBC_BCTRL3_FSH_VBUS_EN, PBC_BCTRL3_SET_REG);
+
+ usbh1_hs_active = 0;
+}
+
+static int usbh2_hs_active;
+/*
+ * conflicts with CSPI1 (MC13783) and CSPI2 (Connector)
+ */
+int gpio_usbh2_active(void)
+{
+ if (usbh2_hs_active)
+ return 0;
+
+ if (gpio_set_puen(MX27_PIN_USBH2_CLK, 0) ||
+ gpio_set_puen(MX27_PIN_USBH2_DIR, 0) ||
+ gpio_set_puen(MX27_PIN_USBH2_DATA7, 0) ||
+ gpio_set_puen(MX27_PIN_USBH2_NXT, 0) ||
+ gpio_set_puen(MX27_PIN_USBH2_STP, 0) ||
+ gpio_set_puen(MX27_PIN_CSPI2_SS2, 0) ||
+ gpio_set_puen(MX27_PIN_CSPI2_SS1, 0) ||
+ gpio_set_puen(MX27_PIN_CSPI2_SS0, 0) ||
+ gpio_set_puen(MX27_PIN_CSPI2_SCLK, 0) ||
+ gpio_set_puen(MX27_PIN_CSPI2_MISO, 0) ||
+ gpio_set_puen(MX27_PIN_CSPI2_MOSI, 0) ||
+ gpio_set_puen(MX27_PIN_CSPI1_SS2, 0))
+ return -EINVAL;
+
+ if (gpio_request_mux(MX27_PIN_USBH2_CLK, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH2_DIR, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH2_DATA7, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH2_NXT, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBH2_STP, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_CSPI2_SS2, GPIO_MUX_ALT) ||
+ gpio_request_mux(MX27_PIN_CSPI2_SS1, GPIO_MUX_ALT) ||
+ gpio_request_mux(MX27_PIN_CSPI2_SS0, GPIO_MUX_ALT) ||
+ gpio_request_mux(MX27_PIN_CSPI2_SCLK, GPIO_MUX_ALT) ||
+ gpio_request_mux(MX27_PIN_CSPI2_MISO, GPIO_MUX_ALT) ||
+ gpio_request_mux(MX27_PIN_CSPI2_MOSI, GPIO_MUX_ALT) ||
+ gpio_request_mux(MX27_PIN_CSPI1_SS2, GPIO_MUX_ALT))
+ return -EINVAL;
+
+ __raw_writew(PBC_BCTRL3_HSH_EN, PBC_BCTRL3_CLEAR_REG);
+ usbh2_hs_active = 1;
+ return 0;
+}
+void gpio_usbh2_inactive(void)
+{
+ if (usbh2_hs_active == 0)
+ return;
+
+ gpio_free_mux(MX27_PIN_USBH2_CLK);
+ gpio_free_mux(MX27_PIN_USBH2_DIR);
+ gpio_free_mux(MX27_PIN_USBH2_DATA7);
+ gpio_free_mux(MX27_PIN_USBH2_NXT);
+ gpio_free_mux(MX27_PIN_USBH2_STP);
+
+ gpio_free_mux(MX27_PIN_CSPI2_SS2);
+ gpio_free_mux(MX27_PIN_CSPI2_SS1);
+ gpio_free_mux(MX27_PIN_CSPI2_SS0);
+ gpio_free_mux(MX27_PIN_CSPI2_SCLK);
+ gpio_free_mux(MX27_PIN_CSPI2_MISO);
+ gpio_free_mux(MX27_PIN_CSPI2_MOSI);
+ gpio_free_mux(MX27_PIN_CSPI1_SS2);
+
+ gpio_set_puen(MX27_PIN_USBH2_CLK, 1);
+ gpio_set_puen(MX27_PIN_USBH2_DIR, 1);
+ gpio_set_puen(MX27_PIN_USBH2_DATA7, 1);
+ gpio_set_puen(MX27_PIN_USBH2_NXT, 1);
+ gpio_set_puen(MX27_PIN_USBH2_STP, 1);
+ gpio_set_puen(MX27_PIN_CSPI2_SS2, 1);
+ gpio_set_puen(MX27_PIN_CSPI2_SS1, 1);
+ gpio_set_puen(MX27_PIN_CSPI2_SS0, 1);
+ gpio_set_puen(MX27_PIN_CSPI2_SCLK, 1);
+ gpio_set_puen(MX27_PIN_CSPI2_MISO, 1);
+ gpio_set_puen(MX27_PIN_CSPI2_MOSI, 1);
+ gpio_set_puen(MX27_PIN_CSPI1_SS2, 1);
+ __raw_writew(PBC_BCTRL3_HSH_EN, PBC_BCTRL3_SET_REG);
+
+ usbh2_hs_active = 0;
+}
+
+static int usbotg_hs_active;
+int gpio_usbotg_hs_active(void)
+{
+ if (usbotg_hs_active)
+ return 0;
+
+ if (gpio_request_mux(MX27_PIN_USBOTG_DATA5, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBOTG_DATA6, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBOTG_DATA0, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBOTG_DATA2, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBOTG_DATA1, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBOTG_DATA3, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBOTG_DATA4, GPIO_MUX_PRIMARY) ||
+
+ gpio_request_mux(MX27_PIN_USBOTG_DIR, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBOTG_STP, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBOTG_NXT, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBOTG_CLK, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USBOTG_DATA7, GPIO_MUX_PRIMARY) ||
+
+ gpio_request_mux(MX27_PIN_USB_OC_B, GPIO_MUX_PRIMARY) ||
+ gpio_request_mux(MX27_PIN_USB_PWR, GPIO_MUX_PRIMARY))
+ return -EINVAL;
+
+ __raw_writew(PBC_BCTRL3_OTG_HS_EN, PBC_BCTRL3_CLEAR_REG);
+ __raw_writew(PBC_BCTRL3_OTG_VBUS_EN, PBC_BCTRL3_CLEAR_REG);
+
+ usbotg_hs_active = 1;
+ return 0;
+}
+
+void gpio_usbotg_hs_inactive(void)
+{
+ if (usbotg_hs_active == 0)
+ return;
+
+ gpio_free_mux(MX27_PIN_USBOTG_DATA5);
+ gpio_free_mux(MX27_PIN_USBOTG_DATA6);
+ gpio_free_mux(MX27_PIN_USBOTG_DATA0);
+ gpio_free_mux(MX27_PIN_USBOTG_DATA2);
+ gpio_free_mux(MX27_PIN_USBOTG_DATA1);
+ gpio_free_mux(MX27_PIN_USBOTG_DATA3);
+ gpio_free_mux(MX27_PIN_USBOTG_DATA4);
+
+ gpio_free_mux(MX27_PIN_USBOTG_DIR);
+ gpio_free_mux(MX27_PIN_USBOTG_STP);
+ gpio_free_mux(MX27_PIN_USBOTG_NXT);
+ gpio_free_mux(MX27_PIN_USBOTG_CLK);
+ gpio_free_mux(MX27_PIN_USBOTG_DATA7);
+
+ gpio_free_mux(MX27_PIN_USB_OC_B);
+ gpio_free_mux(MX27_PIN_USB_PWR);
+ __raw_writew(PBC_BCTRL3_OTG_HS_EN, PBC_BCTRL3_SET_REG);
+
+ usbotg_hs_active = 0;
+}
+
+int gpio_usbotg_fs_active(void)
+{
+ return gpio_usbotg_hs_active();
+}
+
+void gpio_usbotg_fs_inactive(void)
+{
+ gpio_usbotg_hs_inactive();
+}
+
+/*!
+ * end Setup GPIO for USB
+ *
+ */
+
+/************************************************************************/
+/* for i2c gpio */
+/* I2C1: PD17,PD18 -- Primary */
+/* I2C2: PC5,PC6 -- Primary */
+/************************************************************************/
+/*!
+* Setup GPIO for an I2C device to be active
+*
+* @param i2c_num an I2C device
+*/
+void gpio_i2c_active(int i2c_num)
+{
+ switch (i2c_num) {
+ case 0:
+ gpio_request_mux(MX27_PIN_I2C_CLK, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_I2C_DATA, GPIO_MUX_PRIMARY);
+ break;
+ case 1:
+ gpio_request_mux(MX27_PIN_I2C2_SCL, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_I2C2_SDA, GPIO_MUX_PRIMARY);
+ break;
+ default:
+ printk(KERN_ERR "gpio_i2c_active no compatible I2C adapter\n");
+ break;
+ }
+}
+
+/*!
+ * * Setup GPIO for an I2C device to be inactive
+ * *
+ * * @param i2c_num an I2C device
+ */
+void gpio_i2c_inactive(int i2c_num)
+{
+ switch (i2c_num) {
+ case 0:
+ gpio_free_mux(MX27_PIN_I2C_CLK);
+ gpio_free_mux(MX27_PIN_I2C_DATA);
+ break;
+ case 1:
+ gpio_free_mux(MX27_PIN_I2C2_SCL);
+ gpio_free_mux(MX27_PIN_I2C2_SDA);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup GPIO for a CSPI device to be active
+ *
+ * @param cspi_mod an CSPI device
+ */
+void gpio_spi_active(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ gpio_request_mux(MX27_PIN_CSPI1_MOSI, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI1_MISO, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI1_SCLK, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI1_RDY, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI1_SS0, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI1_SS1, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI1_SS2, GPIO_MUX_PRIMARY);
+ break;
+ case 1:
+ /*SPI2 */
+ gpio_request_mux(MX27_PIN_CSPI2_MOSI, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI2_MISO, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI2_SCLK, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI2_SS0, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI2_SS1, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSPI2_SS2, GPIO_MUX_PRIMARY);
+ break;
+ case 2:
+ /*SPI3 */
+ gpio_request_mux(MX27_PIN_SD1_D0, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_SD1_CMD, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_SD1_CLK, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_SD1_D3, GPIO_MUX_ALT);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup GPIO for a CSPI device to be inactive
+ *
+ * @param cspi_mod a CSPI device
+ */
+void gpio_spi_inactive(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ gpio_free_mux(MX27_PIN_CSPI1_MOSI);
+ gpio_free_mux(MX27_PIN_CSPI1_MISO);
+ gpio_free_mux(MX27_PIN_CSPI1_SCLK);
+ gpio_free_mux(MX27_PIN_CSPI1_RDY);
+ gpio_free_mux(MX27_PIN_CSPI1_SS0);
+ gpio_free_mux(MX27_PIN_CSPI1_SS1);
+ gpio_free_mux(MX27_PIN_CSPI1_SS2);
+ break;
+ case 1:
+ /*SPI2 */
+ gpio_free_mux(MX27_PIN_CSPI2_MOSI);
+ gpio_free_mux(MX27_PIN_CSPI2_MISO);
+ gpio_free_mux(MX27_PIN_CSPI2_SCLK);
+ gpio_free_mux(MX27_PIN_CSPI2_SS0);
+ gpio_free_mux(MX27_PIN_CSPI2_SS1);
+ gpio_free_mux(MX27_PIN_CSPI2_SS2);
+ break;
+ case 2:
+ /*SPI3 */
+ gpio_free_mux(MX27_PIN_SD1_D0);
+ gpio_free_mux(MX27_PIN_SD1_CMD);
+ gpio_free_mux(MX27_PIN_SD1_CLK);
+ gpio_free_mux(MX27_PIN_SD1_D3);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup GPIO for a nand flash device to be active
+ *
+ */
+void gpio_nand_active(void)
+{
+ unsigned long reg;
+ reg = __raw_readl(IO_ADDRESS(SYSCTRL_BASE_ADDR) + SYS_FMCR);
+ reg &= ~(1 << 4);
+ __raw_writel(reg, IO_ADDRESS(SYSCTRL_BASE_ADDR) + SYS_FMCR);
+
+ gpio_request_mux(MX27_PIN_NFRB, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_NFCE_B, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_NFWP_B, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_NFCLE, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_NFALE, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_NFRE_B, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_NFWE_B, GPIO_MUX_PRIMARY);
+}
+
+/*!
+ * Setup GPIO for a nand flash device to be inactive
+ *
+ */
+void gpio_nand_inactive(void)
+{
+ gpio_free_mux(MX27_PIN_NFRB);
+ gpio_free_mux(MX27_PIN_NFCE_B);
+ gpio_free_mux(MX27_PIN_NFWP_B);
+ gpio_free_mux(MX27_PIN_NFCLE);
+ gpio_free_mux(MX27_PIN_NFALE);
+ gpio_free_mux(MX27_PIN_NFRE_B);
+ gpio_free_mux(MX27_PIN_NFWE_B);
+}
+
+/*!
+ * Setup GPIO for CSI device to be active
+ *
+ */
+void gpio_sensor_active(void)
+{
+ gpio_request_mux(MX27_PIN_CSI_D0, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_D1, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_D2, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_D3, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_D4, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_MCLK, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_PIXCLK, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_D5, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_D6, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_D7, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_VSYNC, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CSI_HSYNC, GPIO_MUX_PRIMARY);
+
+#ifdef CONFIG_MXC_CAMERA_MC521DA
+ __raw_writew(0x100, PBC_BCTRL2_SET_REG);
+#else
+ __raw_writew(0x400, PBC_BCTRL2_SET_REG);
+#endif
+}
+
+void gpio_sensor_inactive(void)
+{
+ gpio_free_mux(MX27_PIN_CSI_D0);
+ gpio_free_mux(MX27_PIN_CSI_D1);
+ gpio_free_mux(MX27_PIN_CSI_D2);
+ gpio_free_mux(MX27_PIN_CSI_D3);
+ gpio_free_mux(MX27_PIN_CSI_D4);
+ gpio_free_mux(MX27_PIN_CSI_MCLK);
+ gpio_free_mux(MX27_PIN_CSI_PIXCLK);
+ gpio_free_mux(MX27_PIN_CSI_D5);
+ gpio_free_mux(MX27_PIN_CSI_D6);
+ gpio_free_mux(MX27_PIN_CSI_D7);
+ gpio_free_mux(MX27_PIN_CSI_VSYNC);
+ gpio_free_mux(MX27_PIN_CSI_HSYNC);
+
+#ifdef CONFIG_MXC_CAMERA_MC521DA
+ __raw_writew(0x100, PBC_BCTRL2_CLEAR_REG);
+#else
+ __raw_writew(0x400, PBC_BCTRL2_CLEAR_REG);
+#endif
+}
+
+void gpio_sensor_reset(bool flag)
+{
+ u16 temp;
+
+ if (flag) {
+ temp = 0x200;
+ __raw_writew(temp, PBC_BCTRL2_CLEAR_REG);
+ } else {
+ temp = 0x200;
+ __raw_writew(temp, PBC_BCTRL2_SET_REG);
+ }
+}
+
+/*!
+ * Setup GPIO for LCDC device to be active
+ *
+ */
+void gpio_lcdc_active(void)
+{
+ gpio_request_mux(MX27_PIN_LSCLK, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD0, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD1, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD2, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD3, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD4, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD5, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD6, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD7, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD8, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD9, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD10, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD11, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD12, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD13, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD14, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD15, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD16, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_LD17, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_REV, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CLS, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_PS, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SPL_SPR, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_HSYNC, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_VSYNC, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_CONTRAST, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_OE_ACD, GPIO_MUX_PRIMARY);
+}
+
+/*!
+ * Setup GPIO for LCDC device to be inactive
+ *
+ */
+void gpio_lcdc_inactive(void)
+{
+ gpio_free_mux(MX27_PIN_LSCLK);
+ gpio_free_mux(MX27_PIN_LD0);
+ gpio_free_mux(MX27_PIN_LD1);
+ gpio_free_mux(MX27_PIN_LD2);
+ gpio_free_mux(MX27_PIN_LD3);
+ gpio_free_mux(MX27_PIN_LD4);
+ gpio_free_mux(MX27_PIN_LD5);
+ gpio_free_mux(MX27_PIN_LD6);
+ gpio_free_mux(MX27_PIN_LD7);
+ gpio_free_mux(MX27_PIN_LD8);
+ gpio_free_mux(MX27_PIN_LD9);
+ gpio_free_mux(MX27_PIN_LD10);
+ gpio_free_mux(MX27_PIN_LD11);
+ gpio_free_mux(MX27_PIN_LD12);
+ gpio_free_mux(MX27_PIN_LD13);
+ gpio_free_mux(MX27_PIN_LD14);
+ gpio_free_mux(MX27_PIN_LD15);
+ gpio_free_mux(MX27_PIN_LD16);
+ gpio_free_mux(MX27_PIN_LD17);
+ gpio_free_mux(MX27_PIN_REV);
+ gpio_free_mux(MX27_PIN_CLS);
+ gpio_free_mux(MX27_PIN_PS);
+ gpio_free_mux(MX27_PIN_SPL_SPR);
+ gpio_free_mux(MX27_PIN_HSYNC);
+ gpio_free_mux(MX27_PIN_VSYNC);
+ gpio_free_mux(MX27_PIN_CONTRAST);
+ gpio_free_mux(MX27_PIN_OE_ACD);
+}
+
+/*!
+ * Setup GPIO PA25 low to start hard reset FS453 TV encoder
+ *
+ */
+void gpio_fs453_reset_low(void)
+{
+ gpio_free_mux(MX27_PIN_CLS);
+ if (gpio_request_mux(MX27_PIN_CLS, GPIO_MUX_GPIO)) {
+ printk(KERN_ERR "bug: request GPIO PA25 failed.\n");
+ return;
+ }
+
+ /* PA25 (CLS) as output */
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_CLS), "cls");
+ gpio_direction_output(IOMUX_TO_GPIO(MX27_PIN_CLS), 0);
+ gpio_config_mux(MX27_PIN_CLS, GPIO_MUX_GPIO);
+ gpio_set_value(IOMUX_TO_GPIO(MX27_PIN_CLS), 0);
+}
+
+/*!
+ * Setup GPIO PA25 high to end hard reset FS453 TV encoder
+ *
+ */
+void gpio_fs453_reset_high(void)
+{
+ gpio_free_mux(MX27_PIN_CLS);
+ if (gpio_request_mux(MX27_PIN_CLS, GPIO_MUX_GPIO)) {
+ printk(KERN_ERR "bug: request GPIO PA25 failed.\n");
+ return;
+ }
+
+ /* PA25 (CLS) as output */
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_CLS), "cls");
+ gpio_direction_output(IOMUX_TO_GPIO(MX27_PIN_CLS), 0);
+ gpio_config_mux(MX27_PIN_CLS, GPIO_MUX_GPIO);
+ gpio_set_value(IOMUX_TO_GPIO(MX27_PIN_CLS), 1);
+}
+
+/*!
+ * This function configures the IOMux block for PMIC standard operations.
+ *
+ */
+void gpio_pmic_active(void)
+{
+ gpio_config_mux(MX27_PIN_TOUT, GPIO_MUX_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_TOUT), "tout");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_TOUT));
+}
+
+/*!
+ * GPIO settings not required for keypad
+ *
+ */
+void gpio_keypad_active(void)
+{
+}
+
+/*!
+ * GPIO settings not required for keypad
+ *
+ */
+void gpio_keypad_inactive(void)
+{
+}
+
+/*!
+ * Setup GPIO for ATA device to be active
+ *
+ */
+void gpio_ata_active(void)
+{
+ gpio_request_mux(MX27_PIN_ATA_DATA0, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA1, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA2, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA3, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA4, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA5, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA6, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA7, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA8, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA9, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA10, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA11, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA12, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA13, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA14, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA15, GPIO_MUX_PRIMARY);
+
+ gpio_request_mux(MX27_PIN_PC_CD1_B, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_CD2_B, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_WAIT_B, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_READY, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_PWRON, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_VS1, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_VS2, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_BVD1, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_BVD2, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_RST, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_IOIS16, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_RW_B, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_PC_POE, GPIO_MUX_ALT);
+
+ __raw_writew(PBC_BCTRL2_ATAFEC_EN | PBC_BCTRL2_ATAFEC_SEL |
+ PBC_BCTRL2_ATA_EN, PBC_BCTRL2_CLEAR_REG);
+}
+
+/*!
+ * Setup GPIO for ATA device to be inactive
+ *
+ */
+void gpio_ata_inactive(void)
+{
+ __raw_writew(PBC_BCTRL2_ATAFEC_EN | PBC_BCTRL2_ATAFEC_SEL |
+ PBC_BCTRL2_ATA_EN, PBC_BCTRL2_SET_REG);
+
+ gpio_free_mux(MX27_PIN_ATA_DATA0);
+ gpio_free_mux(MX27_PIN_ATA_DATA1);
+ gpio_free_mux(MX27_PIN_ATA_DATA2);
+ gpio_free_mux(MX27_PIN_ATA_DATA3);
+ gpio_free_mux(MX27_PIN_ATA_DATA4);
+ gpio_free_mux(MX27_PIN_ATA_DATA5);
+ gpio_free_mux(MX27_PIN_ATA_DATA6);
+ gpio_free_mux(MX27_PIN_ATA_DATA7);
+ gpio_free_mux(MX27_PIN_ATA_DATA8);
+ gpio_free_mux(MX27_PIN_ATA_DATA9);
+ gpio_free_mux(MX27_PIN_ATA_DATA10);
+ gpio_free_mux(MX27_PIN_ATA_DATA11);
+ gpio_free_mux(MX27_PIN_ATA_DATA12);
+ gpio_free_mux(MX27_PIN_ATA_DATA13);
+ gpio_free_mux(MX27_PIN_ATA_DATA14);
+ gpio_free_mux(MX27_PIN_ATA_DATA15);
+
+ gpio_free_mux(MX27_PIN_PC_CD1_B);
+ gpio_free_mux(MX27_PIN_PC_CD2_B);
+ gpio_free_mux(MX27_PIN_PC_WAIT_B);
+ gpio_free_mux(MX27_PIN_PC_READY);
+ gpio_free_mux(MX27_PIN_PC_PWRON);
+ gpio_free_mux(MX27_PIN_PC_VS1);
+ gpio_free_mux(MX27_PIN_PC_VS2);
+ gpio_free_mux(MX27_PIN_PC_BVD1);
+ gpio_free_mux(MX27_PIN_PC_BVD2);
+ gpio_free_mux(MX27_PIN_PC_RST);
+ gpio_free_mux(MX27_PIN_IOIS16);
+ gpio_free_mux(MX27_PIN_PC_RW_B);
+ gpio_free_mux(MX27_PIN_PC_POE);
+}
+
+/*!
+ * Setup GPIO for FEC device to be active
+ *
+ */
+void gpio_fec_active(void)
+{
+ gpio_request_mux(MX27_PIN_ATA_DATA15, GPIO_MUX_OUTPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA15), "ata_data15");
+ gpio_direction_output(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA15), 0);
+ gpio_request_mux(MX27_PIN_ATA_DATA14, GPIO_MUX_OUTPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA14), "ata_data14");
+ gpio_direction_output(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA14), 0);
+ gpio_request_mux(MX27_PIN_ATA_DATA13, GPIO_MUX_INPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA13), "ata_data13");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA13));
+ gpio_request_mux(MX27_PIN_ATA_DATA12, GPIO_MUX_INPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA12), "ata_data12");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA12));
+ gpio_request_mux(MX27_PIN_ATA_DATA11, GPIO_MUX_INPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA11), "ata_data11");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA11));
+ gpio_request_mux(MX27_PIN_ATA_DATA10, GPIO_MUX_INPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA10), "ata_data10");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA10));
+ gpio_request_mux(MX27_PIN_ATA_DATA9, GPIO_MUX_INPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA9), "ata_data9");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA9));
+ gpio_request_mux(MX27_PIN_ATA_DATA8, GPIO_MUX_INPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA8), "ata_data8");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA8));
+ gpio_request_mux(MX27_PIN_ATA_DATA7, GPIO_MUX_OUTPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA7), "ata_data7");
+ gpio_direction_output(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA7), 0);
+
+ gpio_request_mux(MX27_PIN_ATA_DATA6, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_ATA_DATA5, GPIO_MUX_INPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA5), "ata_data5");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA5));
+ gpio_request_mux(MX27_PIN_ATA_DATA4, GPIO_MUX_INPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA4), "ata_data4");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA4));
+ gpio_request_mux(MX27_PIN_ATA_DATA3, GPIO_MUX_INPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA3), "ata_data3");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA3));
+ gpio_request_mux(MX27_PIN_ATA_DATA2, GPIO_MUX_INPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA2), "ata_data2");
+ gpio_direction_input(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA2));
+ gpio_request_mux(MX27_PIN_ATA_DATA1, GPIO_MUX_OUTPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA1), "ata_data1");
+ gpio_direction_output(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA1), 0);
+ gpio_request_mux(MX27_PIN_ATA_DATA0, GPIO_MUX_OUTPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA0), "ata_data0");
+ gpio_direction_output(IOMUX_TO_GPIO(MX27_PIN_ATA_DATA0), 0);
+ gpio_request_mux(MX27_PIN_SD3_CLK, GPIO_MUX_OUTPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_SD3_CLK), "sd3_clk");
+ gpio_direction_output(IOMUX_TO_GPIO(MX27_PIN_SD3_CLK), 0);
+ gpio_request_mux(MX27_PIN_SD3_CMD, GPIO_MUX_OUTPUT1);
+ gpio_request(IOMUX_TO_GPIO(MX27_PIN_SD3_CMD), "sd3_cmd");
+ gpio_direction_output(IOMUX_TO_GPIO(MX27_PIN_SD3_CMD), 0);
+
+ __raw_writew(PBC_BCTRL2_ATAFEC_EN, PBC_BCTRL2_CLEAR_REG);
+ __raw_writew(PBC_BCTRL2_ATAFEC_SEL, PBC_BCTRL2_SET_REG);
+}
+
+/*!
+ * Setup GPIO for FEC device to be inactive
+ *
+ */
+void gpio_fec_inactive(void)
+{
+ gpio_free(MX27_PIN_ATA_DATA0);
+ gpio_free(MX27_PIN_ATA_DATA1);
+ gpio_free(MX27_PIN_ATA_DATA2);
+ gpio_free(MX27_PIN_ATA_DATA3);
+ gpio_free(MX27_PIN_ATA_DATA4);
+ gpio_free(MX27_PIN_ATA_DATA5);
+ gpio_free(MX27_PIN_ATA_DATA6);
+ gpio_free(MX27_PIN_ATA_DATA7);
+ gpio_free(MX27_PIN_ATA_DATA8);
+ gpio_free(MX27_PIN_ATA_DATA9);
+ gpio_free(MX27_PIN_ATA_DATA10);
+ gpio_free(MX27_PIN_ATA_DATA11);
+ gpio_free(MX27_PIN_ATA_DATA12);
+ gpio_free(MX27_PIN_ATA_DATA13);
+ gpio_free(MX27_PIN_ATA_DATA14);
+ gpio_free(MX27_PIN_ATA_DATA15);
+
+ gpio_free(MX27_PIN_SD3_CMD);
+ gpio_free(MX27_PIN_SD3_CLK);
+
+ gpio_free_mux(MX27_PIN_ATA_DATA0);
+ gpio_free_mux(MX27_PIN_ATA_DATA1);
+ gpio_free_mux(MX27_PIN_ATA_DATA2);
+ gpio_free_mux(MX27_PIN_ATA_DATA3);
+ gpio_free_mux(MX27_PIN_ATA_DATA4);
+ gpio_free_mux(MX27_PIN_ATA_DATA5);
+ gpio_free_mux(MX27_PIN_ATA_DATA6);
+ gpio_free_mux(MX27_PIN_ATA_DATA7);
+ gpio_free_mux(MX27_PIN_ATA_DATA8);
+ gpio_free_mux(MX27_PIN_ATA_DATA9);
+ gpio_free_mux(MX27_PIN_ATA_DATA10);
+ gpio_free_mux(MX27_PIN_ATA_DATA11);
+ gpio_free_mux(MX27_PIN_ATA_DATA12);
+ gpio_free_mux(MX27_PIN_ATA_DATA13);
+ gpio_free_mux(MX27_PIN_ATA_DATA14);
+ gpio_free_mux(MX27_PIN_ATA_DATA15);
+
+ gpio_free_mux(MX27_PIN_SD3_CMD);
+ gpio_free_mux(MX27_PIN_SD3_CLK);
+}
+
+/*!
+ * Setup GPIO for SLCDC device to be active
+ *
+ */
+void gpio_slcdc_active(int type)
+{
+ switch (type) {
+ case 0:
+ gpio_request_mux(MX27_PIN_SSI3_CLK, GPIO_MUX_ALT); /* CLK */
+ gpio_request_mux(MX27_PIN_SSI3_TXDAT, GPIO_MUX_ALT); /* CS */
+ gpio_request_mux(MX27_PIN_SSI3_RXDAT, GPIO_MUX_ALT); /* RS */
+ gpio_request_mux(MX27_PIN_SSI3_FS, GPIO_MUX_ALT); /* D0 */
+ break;
+
+ case 1:
+ gpio_request_mux(MX27_PIN_SD2_D1, GPIO_MUX_GPIO); /* CLK */
+ gpio_request_mux(MX27_PIN_SD2_D2, GPIO_MUX_GPIO); /* D0 */
+ gpio_request_mux(MX27_PIN_SD2_D3, GPIO_MUX_GPIO); /* RS */
+ gpio_request_mux(MX27_PIN_SD2_CMD, GPIO_MUX_GPIO); /* CS */
+ break;
+
+ case 2:
+ gpio_request_mux(MX27_PIN_LD0, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD1, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD2, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD3, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD4, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD5, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD6, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD7, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD8, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD9, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD10, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD11, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD12, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD13, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD14, GPIO_MUX_GPIO);
+ gpio_request_mux(MX27_PIN_LD15, GPIO_MUX_GPIO);
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+}
+
+/*!
+ * Setup GPIO for SLCDC device to be inactive
+ *
+ */
+void gpio_slcdc_inactive(int type)
+{
+ switch (type) {
+ case 0:
+ gpio_free_mux(MX27_PIN_SSI3_CLK); /* CLK */
+ gpio_free_mux(MX27_PIN_SSI3_TXDAT); /* CS */
+ gpio_free_mux(MX27_PIN_SSI3_RXDAT); /* RS */
+ gpio_free_mux(MX27_PIN_SSI3_FS); /* D0 */
+ break;
+
+ case 1:
+ gpio_free_mux(MX27_PIN_SD2_D1); /* CLK */
+ gpio_free_mux(MX27_PIN_SD2_D2); /* D0 */
+ gpio_free_mux(MX27_PIN_SD2_D3); /* RS */
+ gpio_free_mux(MX27_PIN_SD2_CMD); /* CS */
+ break;
+
+ case 2:
+ gpio_free_mux(MX27_PIN_LD0);
+ gpio_free_mux(MX27_PIN_LD1);
+ gpio_free_mux(MX27_PIN_LD2);
+ gpio_free_mux(MX27_PIN_LD3);
+ gpio_free_mux(MX27_PIN_LD4);
+ gpio_free_mux(MX27_PIN_LD5);
+ gpio_free_mux(MX27_PIN_LD6);
+ gpio_free_mux(MX27_PIN_LD7);
+ gpio_free_mux(MX27_PIN_LD8);
+ gpio_free_mux(MX27_PIN_LD9);
+ gpio_free_mux(MX27_PIN_LD10);
+ gpio_free_mux(MX27_PIN_LD11);
+ gpio_free_mux(MX27_PIN_LD12);
+ gpio_free_mux(MX27_PIN_LD13);
+ gpio_free_mux(MX27_PIN_LD14);
+ gpio_free_mux(MX27_PIN_LD15);
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+}
+
+void gpio_ssi_active(int ssi_num)
+{
+ switch (ssi_num) {
+ case 0:
+ gpio_request_mux(MX27_PIN_SSI1_FS, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SSI1_RXDAT, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SSI1_TXDAT, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SSI1_CLK, GPIO_MUX_PRIMARY);
+ gpio_set_puen(MX27_PIN_SSI1_FS, 0);
+ gpio_set_puen(MX27_PIN_SSI1_RXDAT, 0);
+ gpio_set_puen(MX27_PIN_SSI1_TXDAT, 0);
+ gpio_set_puen(MX27_PIN_SSI1_CLK, 0);
+ break;
+ case 1:
+ gpio_request_mux(MX27_PIN_SSI2_FS, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SSI2_RXDAT, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SSI2_TXDAT, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SSI2_CLK, GPIO_MUX_PRIMARY);
+ gpio_set_puen(MX27_PIN_SSI2_FS, 0);
+ gpio_set_puen(MX27_PIN_SSI2_RXDAT, 0);
+ gpio_set_puen(MX27_PIN_SSI2_TXDAT, 0);
+ gpio_set_puen(MX27_PIN_SSI2_CLK, 0);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+/*!
+ * * Setup GPIO for a SSI port to be inactive
+ * *
+ * * @param ssi_num an SSI port num
+ */
+
+void gpio_ssi_inactive(int ssi_num)
+{
+ switch (ssi_num) {
+ case 0:
+ gpio_free_mux(MX27_PIN_SSI1_FS);
+ gpio_free_mux(MX27_PIN_SSI1_RXDAT);
+ gpio_free_mux(MX27_PIN_SSI1_TXDAT);
+ gpio_free_mux(MX27_PIN_SSI1_CLK);
+ break;
+ case 1:
+ gpio_free_mux(MX27_PIN_SSI2_FS);
+ gpio_free_mux(MX27_PIN_SSI2_RXDAT);
+ gpio_free_mux(MX27_PIN_SSI2_TXDAT);
+ gpio_free_mux(MX27_PIN_SSI2_CLK);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+/*!
+ * Setup GPIO for SDHC to be active
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_active(int module)
+{
+ u16 data;
+ switch (module) {
+ case 0:
+ gpio_request_mux(MX27_PIN_SD1_CLK, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD1_CMD, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD1_D0, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD1_D1, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD1_D2, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD1_D3, GPIO_MUX_PRIMARY);
+ /* 22k pull up for sd1 dat3 pins */
+ data = __raw_readw(IO_ADDRESS(SYSCTRL_BASE_ADDR + 0x54));
+ data |= 0x0c;
+ __raw_writew(data, IO_ADDRESS(SYSCTRL_BASE_ADDR + 0x54));
+ /*mxc_clks_enable(SDHC1_CLK);
+ mxc_clks_enable(PERCLK2); */
+ break;
+ case 1:
+ gpio_request_mux(MX27_PIN_SD2_CLK, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD2_CMD, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD2_D0, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD2_D1, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD2_D2, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD2_D3, GPIO_MUX_PRIMARY);
+ /* 22k pull up for sd2 pins */
+ data = __raw_readw(IO_ADDRESS(SYSCTRL_BASE_ADDR + 0x54));
+ data &= ~0xfff0;
+ data |= 0xfff0;
+ __raw_writew(data, IO_ADDRESS(SYSCTRL_BASE_ADDR + 0x54));
+ /*mxc_clks_enable(SDHC2_CLK);
+ mxc_clks_enable(PERCLK2); */
+ break;
+ case 2:
+ gpio_request_mux(MX27_PIN_SD3_CLK, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_SD3_CMD, GPIO_MUX_PRIMARY);
+ gpio_request_mux(MX27_PIN_ATA_DATA0, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_ATA_DATA1, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_ATA_DATA2, GPIO_MUX_ALT);
+ gpio_request_mux(MX27_PIN_ATA_DATA3, GPIO_MUX_ALT);
+ /*mxc_clks_enable(SDHC3_CLK);
+ mxc_clks_enable(PERCLK2); */
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup GPIO for SDHC1 to be inactive
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_inactive(int module)
+{
+ switch (module) {
+ case 0:
+ gpio_free_mux(MX27_PIN_SD1_CLK);
+ gpio_free_mux(MX27_PIN_SD1_CMD);
+ gpio_free_mux(MX27_PIN_SD1_D0);
+ gpio_free_mux(MX27_PIN_SD1_D1);
+ gpio_free_mux(MX27_PIN_SD1_D2);
+ gpio_free_mux(MX27_PIN_SD1_D3);
+ /*mxc_clks_disable(SDHC1_CLK); */
+ break;
+ case 1:
+ gpio_free_mux(MX27_PIN_SD2_CLK);
+ gpio_free_mux(MX27_PIN_SD2_CMD);
+ gpio_free_mux(MX27_PIN_SD2_D0);
+ gpio_free_mux(MX27_PIN_SD2_D1);
+ gpio_free_mux(MX27_PIN_SD2_D2);
+ gpio_free_mux(MX27_PIN_SD2_D3);
+ /*mxc_clks_disable(SDHC2_CLK); */
+ break;
+ case 2:
+ gpio_free_mux(MX27_PIN_SD3_CLK);
+ gpio_free_mux(MX27_PIN_SD3_CMD);
+ gpio_free_mux(MX27_PIN_ATA_DATA0);
+ gpio_free_mux(MX27_PIN_ATA_DATA1);
+ gpio_free_mux(MX27_PIN_ATA_DATA2);
+ gpio_free_mux(MX27_PIN_ATA_DATA3);
+ /*mxc_clks_disable(SDHC3_CLK); */
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Probe for the card. If present the GPIO data would be set.
+ */
+int sdhc_get_card_det_status(struct device *dev)
+{
+ return 0;
+}
+
+/*
+ * Return the card detect pin.
+ */
+int sdhc_init_card_det(int id)
+{
+ int ret = 0;
+ switch (id) {
+ case 0:
+ ret = EXPIO_INT_SD1_EN;
+ break;
+ case 1:
+ ret = EXPIO_INT_SD2_EN;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Power on/off Sharp QVGA panel.
+ */
+void board_power_lcd(int on)
+{
+ if (on)
+ __raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_SET_REG);
+ else
+ __raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_CLEAR_REG);
+}
+
+void gpio_owire_active(void)
+{
+ gpio_request_mux(MX27_PIN_RTCK, GPIO_MUX_ALT);
+}
+
+void gpio_owire_inactive(void)
+{
+ gpio_request_mux(MX27_PIN_RTCK, GPIO_MUX_PRIMARY);
+}
+
+EXPORT_SYMBOL(gpio_uart_active);
+EXPORT_SYMBOL(gpio_uart_inactive);
+EXPORT_SYMBOL(config_uartdma_event);
+EXPORT_SYMBOL(gpio_usbh1_active);
+EXPORT_SYMBOL(gpio_usbh1_inactive);
+EXPORT_SYMBOL(gpio_usbh2_active);
+EXPORT_SYMBOL(gpio_usbh2_inactive);
+EXPORT_SYMBOL(gpio_usbotg_hs_active);
+EXPORT_SYMBOL(gpio_usbotg_hs_inactive);
+EXPORT_SYMBOL(gpio_usbotg_fs_active);
+EXPORT_SYMBOL(gpio_usbotg_fs_inactive);
+EXPORT_SYMBOL(gpio_i2c_active);
+EXPORT_SYMBOL(gpio_i2c_inactive);
+EXPORT_SYMBOL(gpio_spi_active);
+EXPORT_SYMBOL(gpio_spi_inactive);
+EXPORT_SYMBOL(gpio_nand_active);
+EXPORT_SYMBOL(gpio_nand_inactive);
+EXPORT_SYMBOL(gpio_sensor_active);
+EXPORT_SYMBOL(gpio_sensor_inactive);
+EXPORT_SYMBOL(gpio_sensor_reset);
+EXPORT_SYMBOL(gpio_lcdc_active);
+EXPORT_SYMBOL(gpio_lcdc_inactive);
+EXPORT_SYMBOL(gpio_fs453_reset_low);
+EXPORT_SYMBOL(gpio_fs453_reset_high);
+EXPORT_SYMBOL(gpio_pmic_active);
+EXPORT_SYMBOL(gpio_keypad_active);
+EXPORT_SYMBOL(gpio_keypad_inactive);
+EXPORT_SYMBOL(gpio_ata_active);
+EXPORT_SYMBOL(gpio_ata_inactive);
+EXPORT_SYMBOL(gpio_fec_active);
+EXPORT_SYMBOL(gpio_fec_inactive);
+EXPORT_SYMBOL(gpio_slcdc_active);
+EXPORT_SYMBOL(gpio_slcdc_inactive);
+EXPORT_SYMBOL(gpio_ssi_active);
+EXPORT_SYMBOL(gpio_ssi_inactive);
+EXPORT_SYMBOL(gpio_sdhc_active);
+EXPORT_SYMBOL(gpio_sdhc_inactive);
+EXPORT_SYMBOL(sdhc_get_card_det_status);
+EXPORT_SYMBOL(sdhc_init_card_det);
+EXPORT_SYMBOL(board_power_lcd);
+EXPORT_SYMBOL(gpio_owire_active);
+EXPORT_SYMBOL(gpio_owire_inactive);
diff --git a/arch/arm/mach-mx27/mxc_pm.c b/arch/arm/mach-mx27/mxc_pm.c
new file mode 100644
index 000000000000..4cad42238477
--- /dev/null
+++ b/arch/arm/mach-mx27/mxc_pm.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup DPM_MX27 Power Management
+ * @ingroup MSL_MX27
+ */
+/*!
+ * @file mach-mx27/mxc_pm.c
+ *
+ * @brief This file contains the implementation of the Low-level power
+ * management driver. It modifies the registers of the PLL and clock module
+ * of the i.MX27.
+ *
+ * @ingroup DPM_MX27
+ */
+
+/*
+ * Include Files
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <mach/mxc_pm.h>
+#include <mach/mxc.h>
+#include <mach/system.h>
+#include <asm/irq.h>
+#include "crm_regs.h"
+
+/* Local defines */
+#define MAX_ARM_FREQ 400000000
+#define MAX_AHB_FREQ 133000000
+#define MAX_IPG_FREQ 66500000
+#define FREQ_COMP_TOLERANCE 100 /* tolerance percentage times 100 */
+#define MX27_LLPM_DEBUG 0
+
+/*
+ * Global variables
+ */
+#if 0
+/*!
+ * These variables hold the various clock values when the module is loaded.
+ * This is needed because these clocks are derived from MPLL and when MPLL
+ * output changes, these clocks need to be adjusted.
+ */
+static u32 perclk1, perclk2, perclk3, perclk4, nfcclk, cpuclk;
+
+/*!
+ * Compare two frequences using allowable tolerance
+ *
+ * The MX27 PLL can generate many frequencies. This function
+ * compares the generated frequency to the requested frequency
+ * and determines it they are within and acceptable tolerance.
+ *
+ * @param freq1 desired frequency
+ * @param freq2 generated frequency
+ *
+ * @return Returns 0 is frequencies are within talerance
+ * and non-zero is they are not.
+ */
+static s32 freq_equal(u32 freq1, u32 freq2)
+{
+ if (freq1 > freq2) {
+ return (freq1 - freq2) <= (freq1 / FREQ_COMP_TOLERANCE);
+ }
+ return (freq2 - freq1) <= (freq1 / FREQ_COMP_TOLERANCE);
+}
+
+/*!
+ * Select the PLL frequency based on the desired ARM frequency.
+ *
+ * The MPLL will be configured to output three frequencies, 400/333/266 MHz.
+ *
+ * @param armfreq Desired ARM frequency
+ *
+ * @return Returns one of the selected PLL frequency (400/333/266 MHz).
+ * Returns -1 on error.
+ *
+ */
+static s32 select_freq_pll(u32 armfreq)
+{
+ u32 div;
+
+ div = 266000000 / armfreq;
+ if ((div == 0) || (!freq_equal(armfreq, 266000000 / div))) {
+ div = 400000000 / armfreq;
+ if ((div == 0) || (!freq_equal(armfreq, 400000000 / div))) {
+ return -1;
+ }
+
+ return 400000000;
+ }
+
+ return 266000000;
+}
+
+/*!
+ * Check whether the desired ARM and AHB frequencies are valid.
+ *
+ * @param armfreq Desired ARM frequency
+ * @param ahbfreq Desired AHB frequency
+ *
+ * @return Returns 0 on success
+ * Return -1 on error
+ */
+static s32 mx27_pm_check_parameters(u32 armfreq, u32 ahbfreq)
+{
+ u32 ahbdiv;
+
+ /* No idea about minimum frequencies.. just a guess! */
+ if ((armfreq < 1000000) || (ahbfreq < 1000000)) {
+ printk("arm or ahb frequencies are less\n");
+ return -1;
+ }
+
+ if ((armfreq > MAX_ARM_FREQ) || (ahbfreq > MAX_AHB_FREQ)) {
+ printk("arm or ahb freq. are too much\n");
+ return -1;
+ }
+
+ /* AHB divider value is restricted to less than 8 */
+ ahbdiv = armfreq / ahbfreq;
+ if ((ahbdiv == 0) || (ahbdiv > 8)) {
+ printk("Invalid ahb frequency\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * Integer clock scaling
+ *
+ * Change the main ARM clock frequencies without changing the MPLL.
+ * The integer dividers (PRESC and BCLKDIV) are changed to obtain the
+ * desired frequency. Since NFC clock is derived from ARM frequency,
+ * NFCDIV is also adjusted.
+ *
+ * @param arm_freq Desired ARM frequency
+ * @param ahb_freq Desired AHB frequency
+ * @param pll_freq Current PLL frequency
+ *
+ * @return Returns 0
+ */
+static s32 mx27_pm_intscale(u32 arm_freq, u32 ahb_freq, s32 pll_freq)
+{
+ u32 pre_div, bclk_div, nfc_div;
+
+ /* Calculate ARM divider */
+ pre_div = pll_freq / arm_freq;
+ if (pre_div == 0)
+ pre_div = 1;
+
+ /* Calculate AHB divider */
+ bclk_div = arm_freq / ahb_freq;
+ if (bclk_div == 0)
+ bclk_div = 1;
+
+ if ((arm_freq / bclk_div) > ahb_freq)
+ bclk_div++;
+
+ /* NFC clock is dependent on ARM clock */
+ nfc_div = arm_freq / nfcclk;
+ if ((arm_freq / nfc_div) > nfcclk)
+ nfc_div++;
+
+ /* Adjust NFC divider */
+ mxc_set_clocks_div(NFC_CLK, nfc_div);
+
+#if MX27_LLPM_DEBUG
+ printk("DIVIDERS: PreDiv = %d BCLKDIV = %d \n", pre_div, bclk_div);
+ printk("Integer scaling\n");
+ printk("PLL = %d : ARM = %d: AHB = %d\n", pll_freq, arm_freq, ahb_freq);
+#endif
+
+ /*
+ * This part is tricky. What to adjust first (PRESC or BCLKDIV)?
+ * After trial and error, if current ARM frequency is greater than
+ * desired ARM frequency, then adjust PRESC first, else if current
+ * ARM frequency is less than desired ARM frequency, then adjust
+ * BCLKDIV first.
+ */
+ if (cpuclk > arm_freq) {
+ mxc_set_clocks_div(CPU_CLK, pre_div);
+ mxc_set_clocks_div(AHB_CLK, bclk_div);
+ } else {
+ mxc_set_clocks_div(AHB_CLK, bclk_div);
+ mxc_set_clocks_div(CPU_CLK, pre_div);
+ }
+
+ cpuclk = arm_freq;
+ mdelay(50);
+ return 0;
+}
+
+/*!
+ * Set dividers for various peripheral clocks.
+ *
+ * PERCLK1, PERCLK2, PERCLK3 and PERCLK4 are adjusted based on the MPLL
+ * output frequency.
+ *
+ * @param pll_freq Desired MPLL output frequency
+ */
+static void mx27_set_dividers(u32 pll_freq)
+{
+ s32 perdiv1, perdiv2, perdiv3, perdiv4;
+
+ perdiv1 = pll_freq / perclk1;
+ if ((pll_freq / perdiv1) > perclk1)
+ perdiv1++;
+
+ perdiv2 = pll_freq / perclk2;
+ if ((pll_freq / perdiv2) > perclk2)
+ perdiv2++;
+
+ perdiv3 = pll_freq / perclk3;
+ if ((pll_freq / perdiv3) > perclk3)
+ perdiv3++;
+
+ perdiv4 = pll_freq / perclk4;
+ if ((pll_freq / perdiv4) > perclk4)
+ perdiv4++;
+
+ mxc_set_clocks_div(PERCLK1, perdiv1);
+ mxc_set_clocks_div(PERCLK2, perdiv2);
+ mxc_set_clocks_div(PERCLK3, perdiv3);
+ mxc_set_clocks_div(PERCLK4, perdiv4);
+}
+
+/*!
+ * Change MPLL output frequency and adjust derived clocks to produce the
+ * desired frequencies.
+ *
+ * @param arm_freq Desired ARM frequency
+ * @param ahb_freq Desired AHB frequency
+ * @param org_pll Current PLL frequency
+ *
+ * @return Returns 0 on success
+ * Returns -1 on error
+ */
+static s32 mx27_pm_pllscale(u32 arm_freq, u32 ahb_freq, s32 org_pll)
+{
+ u32 mfi, mfn, mfd, pd = 1, cscr;
+ s32 pll_freq;
+
+ /* Obtain the PLL frequency for the desired ARM frequency */
+ pll_freq = select_freq_pll(arm_freq);
+ if (pll_freq == -1) {
+ return -1;
+ }
+
+ /* The MPCTL0 register values are programmed based on the oscillator */
+ cscr = __raw_readl(IO_ADDRESS(CCM_BASE_ADDR) + CCM_CSCR);
+ if ((cscr & CCM_CSCR_OSC26M) == 0) {
+ /* MPCTL0 register values are programmed for 400/266 MHz */
+ switch (pll_freq) {
+ case 400000000:
+ mfi = 7;
+ mfn = 9;
+ mfd = 12;
+ pd = 0;
+ break;
+
+ case 266000000:
+ mfi = 10;
+ mfn = 6;
+ mfd = 25;
+ break;
+
+ default:
+ return -1;
+ }
+ } else {
+ /* MPCTL0 register values are programmed for 400/266 MHz */
+ switch (pll_freq) {
+ case 400000000:
+ mfi = 12;
+ mfn = 2;
+ mfd = 3;
+ break;
+
+ case 266000000:
+ mfi = 8;
+ mfn = 10;
+ mfd = 31;
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+#if MX27_LLPM_DEBUG
+ printk("PLL scaling\n");
+ printk("PLL = %d : ARM = %d: AHB = %d\n", pll_freq, arm_freq, ahb_freq);
+#endif
+
+ /* Adjust the peripheral clock dividers for new PLL frequency */
+ mx27_set_dividers(pll_freq);
+
+ if (pll_freq > org_pll) {
+ /* Set the dividers first */
+ mx27_pm_intscale(arm_freq, ahb_freq, pll_freq);
+
+ /* Set the PLL */
+ mxc_pll_set(MCUPLL, mfi, pd, mfd, mfn);
+ mdelay(50);
+ } else {
+ /* Set the PLL first */
+ mxc_pll_set(MCUPLL, mfi, pd, mfd, mfn);
+ mdelay(50);
+
+ /* Set the dividers later */
+ mx27_pm_intscale(arm_freq, ahb_freq, pll_freq);
+ }
+
+ return 0;
+}
+#endif
+/*!
+ * Implement steps required to transition to low-power modes.
+ *
+ * @param mode The desired low-power mode. Possible values are,
+ * DOZE_MODE
+ * WAIT_MODE
+ * STOP_MODE
+ * DSM_MODE
+ */
+void mxc_pm_lowpower(s32 mode)
+{
+ u32 cscr;
+
+ local_irq_disable();
+
+ /* WAIT and DOZE execute WFI only */
+ switch (mode) {
+ case STOP_MODE:
+ case DSM_MODE:
+ /* Clear MPEN and SPEN to disable MPLL/SPLL */
+ cscr = __raw_readl(CCM_CSCR);
+ cscr &= 0xFFFFFFFC;
+ __raw_writel(cscr, CCM_CSCR);
+ break;
+ }
+
+ /* Executes WFI */
+ arch_idle();
+
+ local_irq_enable();
+}
+
+#if 0
+/*!
+ * Called to change the core frequency. This function internally decides
+ * whether to do integer scaling or pll scaling.
+ *
+ * @param arm_freq Desired ARM frequency
+ * @param ahb_freq Desired AHB frequency
+ * @param ipg_freq Desired IP frequency, constant AHB / 2 always.
+ *
+ * @return Returns 0 on success
+ * Returns -1 on error
+ */
+int mxc_pm_dvfs(unsigned long arm_freq, long ahb_freq, long ipg_freq)
+{
+ u32 divider;
+ s32 pll_freq, ret;
+ unsigned long flags;
+
+ if (mx27_pm_check_parameters(arm_freq, ahb_freq) != 0) {
+ return -1;
+ }
+
+ local_irq_save(flags);
+
+ /* Get the current PLL frequency */
+ pll_freq = mxc_pll_clock(MCUPLL);
+
+#if MX27_LLPM_DEBUG
+ printk("MCU PLL frequency is %d\n", pll_freq);
+#endif
+
+ /* Decide whether to do integer scaling or pll scaling */
+ if (arm_freq > pll_freq) {
+ /* Do PLL scaling */
+ ret = mx27_pm_pllscale(arm_freq, ahb_freq, pll_freq);
+ } else {
+ /* We need integer divider values */
+ divider = pll_freq / arm_freq;
+ if (!freq_equal(arm_freq, pll_freq / divider)) {
+ /* Do PLL scaling */
+ ret = mx27_pm_pllscale(arm_freq, ahb_freq, pll_freq);
+ } else {
+ /* Do integer scaling */
+ ret = mx27_pm_intscale(arm_freq, ahb_freq, pll_freq);
+ }
+ }
+
+ local_irq_restore(flags);
+ return ret;
+}
+#endif
+/*
+ * This API is not supported on i.MX27
+ */
+int mxc_pm_intscale(long armfreq, long ahbfreq, long ipfreq)
+{
+ return -MXC_PM_API_NOT_SUPPORTED;
+}
+
+/*
+ * This API is not supported on i.MX27
+ */
+int mxc_pm_pllscale(long armfreq, long ahbfreq, long ipfreq)
+{
+ return -MXC_PM_API_NOT_SUPPORTED;
+}
+
+/*!
+ * This function is used to load the module.
+ *
+ * @return Returns an Integer on success
+ */
+static int __init mxc_pm_init_module(void)
+{
+ printk(KERN_INFO "MX27: Power management module initialized\n");
+ return 0;
+}
+
+/*!
+ * This function is used to unload the module
+ */
+static void __exit mxc_pm_cleanup_module(void)
+{
+ printk(KERN_INFO "MX27: Power management module exit\n");
+}
+
+module_init(mxc_pm_init_module);
+module_exit(mxc_pm_cleanup_module);
+
+EXPORT_SYMBOL(mxc_pm_lowpower);
+//EXPORT_SYMBOL(mxc_pm_dvfs);
+EXPORT_SYMBOL(mxc_pm_pllscale);
+EXPORT_SYMBOL(mxc_pm_intscale);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("i.MX27 low level PM driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-mx27/pm.c b/arch/arm/mach-mx27/pm.c
new file mode 100644
index 000000000000..10909a72ee1b
--- /dev/null
+++ b/arch/arm/mach-mx27/pm.c
@@ -0,0 +1,102 @@
+/*
+ * linux/arch/arm/mach-mx27/pm.c
+ *
+ * MX27 Power Management Routines
+ *
+ * Original code for the SA11x0:
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * Modified for the PXA250 by Nicolas Pitre:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Modified for the OMAP1510 by David Singleton:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Cleanup 2004 for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * Modified for the MX27
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+
+#include <mach/mxc_pm.h>
+
+/*
+ * TODO: whatta save?
+ */
+
+static int mx27_suspend_enter(suspend_state_t state)
+{
+ pr_debug("Hi, from mx27_pm_enter\n");
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ mxc_pm_lowpower(STOP_MODE);
+ break;
+ case PM_SUSPEND_MEM:
+ mxc_pm_lowpower(DSM_MODE);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+static int mx27_suspend_prepare(void)
+{
+ return 0;
+}
+
+/*
+ * Called after devices are re-setup, but before processes are thawed.
+ */
+static void mx27_suspend_finish(void)
+{
+ return;
+}
+
+static int mx27_pm_valid(suspend_state_t state)
+{
+ return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
+}
+
+struct platform_suspend_ops mx27_suspend_ops = {
+ .valid = mx27_pm_valid,
+ .prepare = mx27_suspend_prepare,
+ .enter = mx27_suspend_enter,
+ .finish = mx27_suspend_finish,
+};
+
+static int __init mx27_pm_init(void)
+{
+ pr_debug("Power Management for Freescale MX27\n");
+ suspend_set_ops(&mx27_suspend_ops);
+
+ return 0;
+}
+
+late_initcall(mx27_pm_init);
diff --git a/arch/arm/mach-mx27/serial.c b/arch/arm/mach-mx27/serial.c
new file mode 100644
index 000000000000..a18cf0630c47
--- /dev/null
+++ b/arch/arm/mach-mx27/serial.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2006-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @file mach-mx27/serial.c
+ *
+ * @brief This file contains the UART initiliazation.
+ *
+ * @ingroup MSL_MX27
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <mach/hardware.h>
+#include <mach/mxc_uart.h>
+#include "serial.h"
+#include "board-mx27ads.h"
+
+#if defined(CONFIG_SERIAL_MXC) || defined(CONFIG_SERIAL_MXC_MODULE)
+
+/*!
+ * This is an array where each element holds information about a UART port,
+ * like base address of the UART, interrupt numbers etc. This structure is
+ * passed to the serial_core.c file. Based on which UART is used, the core file
+ * passes back the appropriate port structure as an argument to the control
+ * functions.
+ */
+static uart_mxc_port mxc_ports[] = {
+ [0] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART1_BASE_ADDR),
+ .mapbase = UART1_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART1_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 0,
+ },
+ .ints_muxed = UART1_MUX_INTS,
+ .irqs = {UART1_INT2, UART1_INT3},
+ .mode = UART1_MODE,
+ .ir_mode = UART1_IR,
+ .enabled = UART1_ENABLED,
+ .hardware_flow = UART1_HW_FLOW,
+ .cts_threshold = UART1_UCR4_CTSTL,
+ .dma_enabled = UART1_DMA_ENABLE,
+ .dma_rxbuf_size = UART1_DMA_RXBUFSIZE,
+ .rx_threshold = UART1_UFCR_RXTL,
+ .tx_threshold = UART1_UFCR_TXTL,
+ .shared = UART1_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART1_TX,
+ .dma_rx_id = MXC_DMA_UART1_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+ [1] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART2_BASE_ADDR),
+ .mapbase = UART2_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART2_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 1,
+ },
+ .ints_muxed = UART2_MUX_INTS,
+ .irqs = {UART2_INT2, UART2_INT3},
+ .mode = UART2_MODE,
+ .ir_mode = UART2_IR,
+ .enabled = UART2_ENABLED,
+ .hardware_flow = UART2_HW_FLOW,
+ .cts_threshold = UART2_UCR4_CTSTL,
+ .dma_enabled = UART2_DMA_ENABLE,
+ .dma_rxbuf_size = UART2_DMA_RXBUFSIZE,
+ .rx_threshold = UART2_UFCR_RXTL,
+ .tx_threshold = UART2_UFCR_TXTL,
+ .shared = UART2_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART2_TX,
+ .dma_rx_id = MXC_DMA_UART2_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+ [2] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART3_BASE_ADDR),
+ .mapbase = UART3_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART3_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 2,
+ },
+ .ints_muxed = UART3_MUX_INTS,
+ .irqs = {UART3_INT2, UART3_INT3},
+ .mode = UART3_MODE,
+ .ir_mode = UART3_IR,
+ .enabled = UART3_ENABLED,
+ .hardware_flow = UART3_HW_FLOW,
+ .cts_threshold = UART3_UCR4_CTSTL,
+ .dma_enabled = UART3_DMA_ENABLE,
+ .dma_rxbuf_size = UART3_DMA_RXBUFSIZE,
+ .rx_threshold = UART3_UFCR_RXTL,
+ .tx_threshold = UART3_UFCR_TXTL,
+ .shared = UART3_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART3_TX,
+ .dma_rx_id = MXC_DMA_UART3_RX,
+ .rxd_mux = MXC_UART_IR_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+ [3] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART4_BASE_ADDR),
+ .mapbase = UART4_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART4_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 3,
+ },
+ .ints_muxed = UART4_MUX_INTS,
+ .irqs = {UART4_INT2, UART4_INT3},
+ .mode = UART4_MODE,
+ .ir_mode = UART4_IR,
+ .enabled = UART4_ENABLED,
+ .hardware_flow = UART4_HW_FLOW,
+ .cts_threshold = UART4_UCR4_CTSTL,
+ .dma_enabled = UART4_DMA_ENABLE,
+ .dma_rxbuf_size = UART4_DMA_RXBUFSIZE,
+ .rx_threshold = UART4_UFCR_RXTL,
+ .tx_threshold = UART4_UFCR_TXTL,
+ .shared = UART4_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART4_TX,
+ .dma_rx_id = MXC_DMA_UART4_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+ [4] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART5_BASE_ADDR),
+ .mapbase = UART5_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART5_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 4,
+ },
+ .ints_muxed = UART5_MUX_INTS,
+ .irqs = {UART5_INT2, UART5_INT3},
+ .mode = UART5_MODE,
+ .ir_mode = UART5_IR,
+ .enabled = UART5_ENABLED,
+ .hardware_flow = UART5_HW_FLOW,
+ .cts_threshold = UART5_UCR4_CTSTL,
+ .dma_enabled = UART5_DMA_ENABLE,
+ .dma_rxbuf_size = UART5_DMA_RXBUFSIZE,
+ .rx_threshold = UART5_UFCR_RXTL,
+ .tx_threshold = UART5_UFCR_TXTL,
+ .shared = UART5_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART5_TX,
+ .dma_rx_id = MXC_DMA_UART5_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+ [5] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART6_BASE_ADDR),
+ .mapbase = UART6_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART6_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 5,
+ },
+ .ints_muxed = UART6_MUX_INTS,
+ .irqs = {UART6_INT2, UART6_INT3},
+ .mode = UART6_MODE,
+ .ir_mode = UART6_IR,
+ .enabled = UART6_ENABLED,
+ .hardware_flow = UART6_HW_FLOW,
+ .cts_threshold = UART6_UCR4_CTSTL,
+ .dma_enabled = UART6_DMA_ENABLE,
+ .dma_rxbuf_size = UART6_DMA_RXBUFSIZE,
+ .rx_threshold = UART6_UFCR_RXTL,
+ .tx_threshold = UART6_UFCR_TXTL,
+ .shared = UART6_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART6_TX,
+ .dma_rx_id = MXC_DMA_UART6_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+};
+
+static struct platform_device mxc_uart_device1 = {
+ .name = "mxcintuart",
+ .id = 0,
+ .dev = {
+ .platform_data = &mxc_ports[0],
+ },
+};
+
+static struct platform_device mxc_uart_device2 = {
+ .name = "mxcintuart",
+ .id = 1,
+ .dev = {
+ .platform_data = &mxc_ports[1],
+ },
+};
+
+static struct platform_device mxc_uart_device3 = {
+ .name = "mxcintuart",
+ .id = 2,
+ .dev = {
+ .platform_data = &mxc_ports[2],
+ },
+};
+
+static struct platform_device mxc_uart_device4 = {
+ .name = "mxcintuart",
+ .id = 3,
+ .dev = {
+ .platform_data = &mxc_ports[3],
+ },
+};
+static struct platform_device mxc_uart_device5 = {
+ .name = "mxcintuart",
+ .id = 4,
+ .dev = {
+ .platform_data = &mxc_ports[4],
+ },
+};
+static struct platform_device mxc_uart_device6 = {
+ .name = "mxcintuart",
+ .id = 5,
+ .dev = {
+ .platform_data = &mxc_ports[5],
+ },
+};
+
+static int __init mxc_init_uart(void)
+{
+ /* Register all the MXC UART platform device structures */
+ platform_device_register(&mxc_uart_device1);
+ platform_device_register(&mxc_uart_device2);
+ platform_device_register(&mxc_uart_device3);
+
+ platform_device_register(&mxc_uart_device4);
+
+ platform_device_register(&mxc_uart_device5);
+ platform_device_register(&mxc_uart_device6);
+ return 0;
+}
+
+#else
+static int __init mxc_init_uart(void)
+{
+ return 0;
+}
+#endif
+
+arch_initcall(mxc_init_uart);
diff --git a/arch/arm/mach-mx27/serial.h b/arch/arm/mach-mx27/serial.h
new file mode 100644
index 000000000000..11604c44cebe
--- /dev/null
+++ b/arch/arm/mach-mx27/serial.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX27_SERIAL_H__
+#define __ARCH_ARM_MACH_MX27_SERIAL_H__
+
+/*!
+ * @file mach-mx27/serial.h
+ *
+ * @ingroup MSL_MX27
+ */
+#include <mach/mxc_uart.h>
+
+/* UART 1 configuration */
+/*!
+ * This option allows to choose either an interrupt-driven software controlled
+ * hardware flow control (set this option to 0) or hardware-driven hardware
+ * flow control (set this option to 1).
+ */
+#define UART1_HW_FLOW 1
+/*!
+ * This specifies the threshold at which the CTS pin is deasserted by the
+ * RXFIFO. Set this value in Decimal to anything from 0 to 32 for
+ * hardware-driven hardware flow control. Read the HW spec while specifying
+ * this value. When using interrupt-driven software controlled hardware
+ * flow control set this option to -1.
+ */
+#define UART1_UCR4_CTSTL 16
+/*!
+ * This is option to enable (set this option to 1) or disable DMA data transfer
+ */
+#define UART1_DMA_ENABLE 0
+/*!
+ * Specify the size of the DMA receive buffer. The buffer size should be same with
+ * sub buffer size which is defined in mxc_uart.c for all data can be transfered.
+ */
+#define UART1_DMA_RXBUFSIZE 128
+/*!
+ * Specify the MXC UART's Receive Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the RxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_RXTL 16
+/*!
+ * Specify the MXC UART's Transmit Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the TxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_TXTL 16
+/* UART 2 configuration */
+#define UART2_HW_FLOW 1
+#define UART2_UCR4_CTSTL 16
+#define UART2_DMA_ENABLE 0
+#define UART2_DMA_RXBUFSIZE 512
+#define UART2_UFCR_RXTL 16
+#define UART2_UFCR_TXTL 16
+/* UART 3 configuration */
+#define UART3_HW_FLOW 0
+#define UART3_UCR4_CTSTL -1
+#define UART3_DMA_ENABLE 0
+#define UART3_DMA_RXBUFSIZE 512
+#define UART3_UFCR_RXTL 16
+#define UART3_UFCR_TXTL 16
+/* UART 4 configuration */
+#define UART4_HW_FLOW 1
+#define UART4_UCR4_CTSTL 16
+#define UART4_DMA_ENABLE 0
+#define UART4_DMA_RXBUFSIZE 512
+#define UART4_UFCR_RXTL 16
+#define UART4_UFCR_TXTL 16
+/* UART 5 configuration */
+#define UART5_HW_FLOW 1
+#define UART5_UCR4_CTSTL 16
+#define UART5_DMA_ENABLE 0
+#define UART5_DMA_RXBUFSIZE 512
+#define UART5_UFCR_RXTL 16
+#define UART5_UFCR_TXTL 16
+/* UART 6 configuration */
+#define UART6_HW_FLOW 1
+#define UART6_UCR4_CTSTL 16
+#define UART6_DMA_ENABLE 0
+#define UART6_DMA_RXBUFSIZE 512
+#define UART6_UFCR_RXTL 16
+#define UART6_UFCR_TXTL 16
+/*
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+/*
+ * Is the MUXED interrupt output sent to the ARM core
+ */
+#define INTS_NOTMUXED 0
+#define INTS_MUXED 1
+/* UART 1 configuration */
+/*!
+ * This define specifies whether the muxed ANDed interrupt line or the
+ * individual interrupts from the UART port is integrated with the ARM core.
+ * There exists a define like this for each UART port. Valid values that can
+ * be used are \b INTS_NOTMUXED or \b INTS_MUXED.
+ */
+#define UART1_MUX_INTS INTS_MUXED
+/*!
+ * This define specifies the transmitter interrupt number or the interrupt
+ * number of the ANDed interrupt in case the interrupts are muxed. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_INT1 MXC_INT_UART1
+/*!
+ * This define specifies the receiver interrupt number. If the interrupts of
+ * the UART are muxed, then we specify here a dummy value -1. There exists a
+ * define like this for each UART port.
+ */
+#define UART1_INT2 -1
+/*!
+ * This specifies the master interrupt number. If the interrupts of the UART
+ * are muxed, then we specify here a dummy value of -1. There exists a define
+ * like this for each UART port.
+ */
+#define UART1_INT3 -1
+/*!
+ * This specifies if the UART is a shared peripheral. It holds the shared
+ * peripheral number if it is shared or -1 if it is not shared. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_SHARED_PERI -1
+/* UART 2 configuration */
+#define UART2_MUX_INTS INTS_MUXED
+#define UART2_INT1 MXC_INT_UART2
+#define UART2_INT2 -1
+#define UART2_INT3 -1
+#define UART2_SHARED_PERI -1
+/* UART 3 configuration */
+#define UART3_MUX_INTS INTS_MUXED
+#define UART3_INT1 MXC_INT_UART3
+#define UART3_INT2 -1
+#define UART3_INT3 -1
+#define UART3_SHARED_PERI -1
+/* UART 4 configuration */
+#define UART4_MUX_INTS INTS_MUXED
+#define UART4_INT1 MXC_INT_UART4
+#define UART4_INT2 -1
+#define UART4_INT3 -1
+#define UART4_SHARED_PERI -1
+/* UART 5 configuration */
+#define UART5_MUX_INTS INTS_MUXED
+#define UART5_INT1 MXC_INT_UART5
+#define UART5_INT2 -1
+#define UART5_INT3 -1
+#define UART5_SHARED_PERI -1
+/* UART 6 configuration */
+#define UART6_MUX_INTS INTS_MUXED
+#define UART6_INT1 MXC_INT_UART6
+#define UART6_INT2 -1
+#define UART6_INT3 -1
+#define UART6_SHARED_PERI -1
+
+#endif /* __ARCH_ARM_MACH_MX27_SERIAL_H__ */
diff --git a/arch/arm/mach-mx27/system.c b/arch/arm/mach-mx27/system.c
new file mode 100644
index 000000000000..fb8fc6bb0f0b
--- /dev/null
+++ b/arch/arm/mach-mx27/system.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 1999 ARM Limited
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ * Copyright 2006-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/clk.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <asm/proc-fns.h>
+#include <asm/system.h>
+
+/*!
+ * @defgroup MSL_MX27 i.MX27 Machine Specific Layer (MSL)
+ */
+
+/*!
+ * @file mach-mx27/system.c
+ * @brief This file contains idle and reset functions.
+ *
+ * @ingroup MSL_MX27
+ */
+
+/*!
+ * This function puts the CPU into idle mode. It is called by default_idle()
+ * in process.c file.
+ */
+void arch_idle(void)
+{
+ /*
+ * This should do all the clock switching
+ * and wait for interrupt tricks.
+ */
+ cpu_do_idle();
+}
+
+/*
+ * This function resets the system. It is called by machine_restart().
+ *
+ * @param mode indicates different kinds of resets
+ */
+void arch_reset(char mode)
+{
+ /* Assert Watchdog Reset signal */
+ mxc_wd_reset();
+}
diff --git a/arch/arm/mach-mx27/time.c b/arch/arm/mach-mx27/time.c
new file mode 100644
index 000000000000..79a4894bc9f6
--- /dev/null
+++ b/arch/arm/mach-mx27/time.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/* System Timer Interrupt reconfigured to run in free-run mode.
+ * Author: Vitaly Wool
+ * Copyright 2004 MontaVista Software Inc.
+ */
+
+/*!
+ * @defgroup Timers_MX27 OS Tick Timer
+ * @ingroup MSL_MX27
+ */
+/*!
+ * @file mach-mx27/time.c
+ * @brief This file contains OS tick timer implementation.
+ *
+ * This file contains OS tick timer implementation.
+ *
+ * @ingroup Timers_MX27
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/mtd/xip.h>
+#include <mach/hardware.h>
+#include <asm/mach/time.h>
+
+/*!
+ * GPT register address definitions
+ */
+#define GPT_BASE_ADDR (IO_ADDRESS(GPT1_BASE_ADDR))
+#define MXC_GPT_GPTCR (GPT_BASE_ADDR + 0x00)
+#define MXC_GPT_GPTPR (GPT_BASE_ADDR + 0x04)
+#define MXC_GPT_GPTOCR1 (GPT_BASE_ADDR + 0x08)
+#define MXC_GPT_GPTICR1 (GPT_BASE_ADDR + 0x0C)
+#define MXC_GPT_GPTCNT (GPT_BASE_ADDR + 0x10)
+#define MXC_GPT_GPTSR (GPT_BASE_ADDR + 0x14)
+
+/*!
+ * GPT Control register bit definitions
+ */
+#define GPTCR_COMPEN (1 << 4)
+#define GPTCR_SWR (1 << 15)
+#define GPTCR_FRR (1 << 8)
+
+#define GPTCR_CLKSRC_SHIFT 1
+#define GPTCR_CLKSRC_MASK (7 << GPTCR_CLKSRC_SHIFT)
+#define GPTCR_CLKSRC_NOCLOCK (0 << GPTCR_CLKSRC_SHIFT)
+#define GPTCR_CLKSRC_HIGHFREQ (1 << GPTCR_CLKSRC_SHIFT)
+#define GPTCR_CLKSRC_CLKIN (3 << GPTCR_CLKSRC_SHIFT)
+#define GPTCR_CLKSRC_CLK32K (4 << GPTCR_CLKSRC_SHIFT)
+
+#define GPTCR_ENABLE (1 << 0)
+
+#define GPTSR_OF1 (1 << 0)
+
+extern unsigned long clk_early_get_timer_rate(void);
+
+static int mxc_gpt_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ unsigned long now, expires;
+ u32 reg;
+
+ now = __raw_readl(MXC_GPT_GPTCNT);
+ expires = now + cycles;
+ __raw_writel(expires, MXC_GPT_GPTOCR1);
+ __raw_writel(GPTSR_OF1, MXC_GPT_GPTSR);
+
+ /* enable interrupt */
+ reg = __raw_readl(MXC_GPT_GPTCR);
+ reg |= GPTCR_COMPEN;
+ __raw_writel(reg, MXC_GPT_GPTCR);
+
+ return 0;
+}
+
+static void mxc_gpt_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ u32 reg;
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ panic("MXC GPT: CLOCK_EVT_MODE_PERIODIC not supported\n");
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ /* Disable interrupts */
+ reg = __raw_readl(MXC_GPT_GPTCR);
+ reg &= ~GPTCR_COMPEN;
+ __raw_writel(reg, MXC_GPT_GPTCR);
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ break;
+ }
+}
+
+static struct clock_event_device gpt_clockevent = {
+ .name = "mxc_gpt",
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .rating = 300,
+ .shift = 32,
+ .set_next_event = mxc_gpt_set_next_event,
+ .set_mode = mxc_gpt_set_mode,
+};
+
+/*!
+ * This is the timer interrupt service routine to do required tasks.
+ * It also services the WDOG timer at the frequency of twice per WDOG
+ * timeout value. For example, if the WDOG's timeout value is 4 (2
+ * seconds since the WDOG runs at 0.5Hz), it will be serviced once
+ * every 2/2=1 second.
+ *
+ * @param irq GPT interrupt source number (not used)
+ * @param dev_id this parameter is not used
+ * @return always returns \b IRQ_HANDLED as defined in
+ * include/linux/interrupt.h.
+ */
+static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
+{
+ unsigned int gptsr;
+ u32 reg;
+
+ gptsr = __raw_readl(MXC_GPT_GPTSR);
+ if (gptsr & GPTSR_OF1) {
+ /* Disable interrupt */
+ reg = __raw_readl(MXC_GPT_GPTCR);
+ reg &= ~GPTCR_COMPEN;
+ __raw_writel(reg, MXC_GPT_GPTCR);
+ /* Clear interrupt */
+ __raw_writel(GPTSR_OF1, MXC_GPT_GPTSR);
+
+ gpt_clockevent.event_handler(&gpt_clockevent);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * The clockevents timer interrupt structure.
+ */
+static struct irqaction timer_irq = {
+ .name = "gpt-irq",
+ .flags = IRQF_DISABLED,
+ .handler = mxc_timer_interrupt,
+};
+
+static cycle_t __xipram mxc_gpt_read(void)
+{
+ return __raw_readl(MXC_GPT_GPTCNT);
+}
+
+static struct clocksource gpt_clocksrc = {
+ .name = "mxc_gpt",
+ .rating = 300,
+ .read = mxc_gpt_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 24,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_VALID_FOR_HRES,
+};
+
+/*!
+ * This function is used to initialize the GPT as a clocksource and clockevent.
+ * It is called by the start_kernel() during system startup.
+ */
+void __init mxc_init_time(void)
+{
+ int ret;
+ unsigned long rate;
+ u32 reg, div;
+
+ /* Reset GPT */
+ __raw_writel(GPTCR_SWR, MXC_GPT_GPTCR);
+ while ((__raw_readl(MXC_GPT_GPTCR) & GPTCR_SWR) != 0)
+ mb();
+
+ /* Normal clk api are not yet initialized, so use early verion */
+ rate = clk_early_get_timer_rate();
+ if (rate == 0)
+ panic("MXC GPT: Can't get timer clock rate\n");
+
+#ifdef CLOCK_TICK_RATE
+ div = rate / CLOCK_TICK_RATE;
+ WARN_ON((div * CLOCK_TICK_RATE) != rate);
+#else /* Hopefully CLOCK_TICK_RATE will go away soon */
+ div = 1;
+ while ((rate / div) > 20000000) {
+ div++;
+ }
+#endif
+ rate /= div;
+ __raw_writel(div - 1, MXC_GPT_GPTPR);
+
+ reg = GPTCR_FRR | GPTCR_CLKSRC_HIGHFREQ | GPTCR_ENABLE;
+ __raw_writel(reg, MXC_GPT_GPTCR);
+
+ gpt_clocksrc.mult = clocksource_hz2mult(rate, gpt_clocksrc.shift);
+ ret = clocksource_register(&gpt_clocksrc);
+ if (ret < 0) {
+ goto err;
+ }
+
+ gpt_clockevent.mult = div_sc(rate, NSEC_PER_SEC, gpt_clockevent.shift);
+ gpt_clockevent.max_delta_ns = clockevent_delta2ns(-1, &gpt_clockevent);
+ gpt_clockevent.min_delta_ns = clockevent_delta2ns(1, &gpt_clockevent);
+
+ gpt_clockevent.cpumask = cpumask_of_cpu(0);
+ clockevents_register_device(&gpt_clockevent);
+
+ ret = setup_irq(MXC_INT_GPT, &timer_irq);
+ if (ret < 0) {
+ goto err;
+ }
+
+ pr_info("MXC GPT timer initialized, rate = %lu\n", rate);
+ return;
+ err:
+ panic("Unable to initialize timer\n");
+}
+
+struct sys_timer mxc_timer = {
+ .init = mxc_init_time,
+};
diff --git a/arch/arm/mach-mx27/usb.h b/arch/arm/mach-mx27/usb.h
new file mode 100644
index 000000000000..7824814a6ecd
--- /dev/null
+++ b/arch/arm/mach-mx27/usb.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+extern int usbotg_init(struct platform_device *pdev);
+extern void usbotg_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbotg_fs_active(void);
+extern void gpio_usbotg_fs_inactive(void);
+extern int gpio_usbotg_hs_active(void);
+extern void gpio_usbotg_hs_inactive(void);
+extern struct platform_device *host_pdev_register(struct resource *res,
+ int n_res, struct fsl_usb2_platform_data *config);
+
+extern int fsl_usb_host_init(struct platform_device *pdev);
+extern void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbh1_active(void);
+extern void gpio_usbh1_inactive(void);
+extern int gpio_usbh2_active(void);
+extern void gpio_usbh2_inactive(void);
+
+/*
+ * Determine which platform_data struct to use, based on which
+ * transceiver is configured.
+ * PDATA is a pointer to it.
+ */
+#if defined(CONFIG_ISP1504_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_1504_config;
+#define PDATA (&dr_1504_config)
+#elif defined(CONFIG_ISP1301_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_1301_config;
+#define PDATA (&dr_1301_config)
+#elif defined(CONFIG_MC13783_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_13783_config;
+#define PDATA (&dr_13783_config)
+#endif
+
+
+/*
+ * Used to set pdata->operating_mode before registering the platform_device.
+ * If OTG is configured, the controller operates in OTG mode,
+ * otherwise it's either host or device.
+ */
+#ifdef CONFIG_USB_OTG
+#define DR_UDC_MODE FSL_USB2_DR_OTG
+#define DR_HOST_MODE FSL_USB2_DR_OTG
+#else
+#define DR_UDC_MODE FSL_USB2_DR_DEVICE
+#define DR_HOST_MODE FSL_USB2_DR_HOST
+#endif
+
+
+#ifdef CONFIG_USB_EHCI_ARC_OTG
+static inline void dr_register_host(struct resource *r, int rs)
+{
+ PDATA->operating_mode = DR_HOST_MODE;
+ host_pdev_register(r, rs, PDATA);
+}
+#else
+static inline void dr_register_host(struct resource *r, int rs)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_GADGET_ARC
+static struct platform_device dr_udc_device;
+
+static inline void dr_register_udc(void)
+{
+ PDATA->operating_mode = DR_UDC_MODE;
+ dr_udc_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_udc_device))
+ printk(KERN_ERR "usb: can't register DR gadget\n");
+ else
+ printk(KERN_INFO "usb: DR gadget (%s) registered\n",
+ PDATA->transceiver);
+}
+#else
+static inline void dr_register_udc(void)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_OTG
+static struct platform_device dr_otg_device;
+
+/*
+ * set the proper operating_mode and
+ * platform_data pointer, then register the
+ * device.
+ */
+static inline void dr_register_otg(void)
+{
+ PDATA->operating_mode = FSL_USB2_DR_OTG;
+ dr_otg_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_otg_device))
+ printk(KERN_ERR "usb: can't register otg device\n");
+ else
+ printk(KERN_INFO "usb: DR OTG registered\n");
+}
+#else
+static inline void dr_register_otg(void)
+{
+}
+#endif
diff --git a/arch/arm/mach-mx27/usb_dr.c b/arch/arm/mach-mx27/usb_dr.c
new file mode 100644
index 000000000000..e67bee726412
--- /dev/null
+++ b/arch/arm/mach-mx27/usb_dr.c
@@ -0,0 +1,126 @@
+#define DEBUG
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+/*
+ * platform data structs
+ * - Which one to use is determined by CONFIG options in usb.h
+ * - operating_mode plugged at run time
+ */
+static struct fsl_usb2_platform_data __maybe_unused dr_13783_config = {
+ .name = "DR",
+ .platform_init = usbotg_init,
+ .platform_uninit = usbotg_uninit,
+ .phy_mode = FSL_USB2_PHY_SERIAL,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbotg_fs_active,
+ .gpio_usb_inactive = gpio_usbotg_fs_inactive,
+ .transceiver = "mc13783",
+};
+
+static struct fsl_usb2_platform_data __maybe_unused dr_1301_config = {
+ .name = "DR",
+ .platform_init = usbotg_init,
+ .platform_uninit = usbotg_uninit,
+ .phy_mode = FSL_USB2_PHY_SERIAL,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbotg_fs_active,
+ .gpio_usb_inactive = gpio_usbotg_fs_inactive,
+ .transceiver = "isp1301",
+};
+
+static struct fsl_usb2_platform_data __maybe_unused dr_1504_config = {
+ .name = "DR",
+ .platform_init = usbotg_init,
+ .platform_uninit = usbotg_uninit,
+ .phy_mode = FSL_USB2_PHY_ULPI,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbotg_hs_active,
+ .gpio_usb_inactive = gpio_usbotg_hs_inactive,
+ .transceiver = "isp1504",
+};
+
+
+/*
+ * resources
+ */
+static struct resource otg_resources[] = {
+ [0] = {
+ .start = (u32)(USB_OTGREGS_BASE),
+ .end = (u32)(USB_OTGREGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+static u64 dr_udc_dmamask = ~(u32) 0;
+static void dr_udc_release(struct device *dev)
+{
+}
+
+static u64 dr_otg_dmamask = ~(u32) 0;
+static void dr_otg_release(struct device *dev)
+{
+}
+
+/*
+ * platform device structs
+ * dev.platform_data field plugged at run time
+ */
+static struct platform_device __maybe_unused dr_udc_device = {
+ .name = "fsl-usb2-udc",
+ .id = -1,
+ .dev = {
+ .release = dr_udc_release,
+ .dma_mask = &dr_udc_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static struct platform_device __maybe_unused dr_otg_device = {
+ .name = "fsl-usb2-otg",
+ .id = -1,
+ .dev = {
+ .release = dr_otg_release,
+ .dma_mask = &dr_otg_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static int __init usb_dr_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ dr_register_otg();
+ dr_register_host(otg_resources, ARRAY_SIZE(otg_resources));
+ dr_register_udc();
+
+ return 0;
+}
+
+module_init(usb_dr_init);
diff --git a/arch/arm/mach-mx27/usb_h1.c b/arch/arm/mach-mx27/usb_h1.c
new file mode 100644
index 000000000000..5c6c9c59fb3b
--- /dev/null
+++ b/arch/arm/mach-mx27/usb_h1.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+static struct fsl_usb2_platform_data usbh1_config = {
+ .name = "Host 1",
+ .platform_init = fsl_usb_host_init,
+ .platform_uninit = fsl_usb_host_uninit,
+ .operating_mode = FSL_USB2_MPH_HOST,
+ .phy_mode = FSL_USB2_PHY_SERIAL,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbh1_active,
+ .gpio_usb_inactive = gpio_usbh1_inactive,
+ .transceiver = "serial",
+};
+
+static struct resource usbh1_resources[] = {
+ [0] = {
+ .start = (u32) (USB_H1REGS_BASE),
+ .end = (u32) (USB_H1REGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int __init usbh1_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ host_pdev_register(usbh1_resources, ARRAY_SIZE(usbh1_resources),
+ &usbh1_config);
+ return 0;
+}
+module_init(usbh1_init);
diff --git a/arch/arm/mach-mx27/usb_h2.c b/arch/arm/mach-mx27/usb_h2.c
new file mode 100644
index 000000000000..028941a7b47c
--- /dev/null
+++ b/arch/arm/mach-mx27/usb_h2.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+static struct fsl_usb2_platform_data usbh2_config = {
+ .name = "Host 2",
+ .platform_init = fsl_usb_host_init,
+ .platform_uninit = fsl_usb_host_uninit,
+ .operating_mode = FSL_USB2_MPH_HOST,
+ .phy_mode = FSL_USB2_PHY_ULPI,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbh2_active,
+ .gpio_usb_inactive = gpio_usbh2_inactive,
+ .transceiver = "isp1504",
+};
+
+static struct resource usbh2_resources[] = {
+ [0] = {
+ .start = (u32) (USB_H2REGS_BASE),
+ .end = (u32) (USB_H2REGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int __init usbh2_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ host_pdev_register(usbh2_resources, ARRAY_SIZE(usbh2_resources),
+ &usbh2_config);
+ return 0;
+}
+module_init(usbh2_init);
diff --git a/arch/arm/mach-mx3/Kconfig b/arch/arm/mach-mx3/Kconfig
index db9431dee1b4..9b38518d1dbd 100644
--- a/arch/arm/mach-mx3/Kconfig
+++ b/arch/arm/mach-mx3/Kconfig
@@ -1,9 +1,20 @@
menu "MX3 Options"
depends on ARCH_MX3
+config MX3_OPTIONS
+ bool
+ default y
+ select CPU_V6
+ select ARM_ERRATA_364296
+ select ARM_ERRATA_411920
+ select CACHE_L2X0
+ select OUTER_CACHE
+ select USB_ARCH_HAS_EHCI
+ select ARCH_HAS_EVTMON
+ select ARCH_HAS_RNGA
+
config MACH_MX31ADS
bool "Support MX31ADS platforms"
- default y
help
Include support for MX31ADS platform. This includes specific
configurations for the board and its peripherals.
@@ -16,10 +27,85 @@ config MACH_PCM037
config MACH_MX31LITE
bool "Support MX31 LITEKIT (LogicPD)"
- default n
+
+config MACH_MX31_3DS
+ bool "Support MX31/MX32 3-Stack platforms"
help
- Include support for MX31 LITEKIT platform. This includes specific
+ Include support for MX31/MX32 3-Stack platform. This includes specific
configurations for the board and its peripherals.
+config MX3_DOZE_DURING_IDLE
+ bool "Enter Doze mode during idle"
+ help
+ Turning on this option will put the CPU into Doze mode during idle.
+ The default is to enter Wait mode during idle. Doze mode during
+ idle will save additional power over Wait mode.
+
+config MXC_SDMA_API
+ bool "Use SDMA API"
+ default y
+ help
+ This selects the Freescale MXC SDMA API.
+ If unsure, say N.
+
+menu "SDMA options"
+ depends on MXC_SDMA_API
+
+config SDMA_IRAM
+ bool "Use Internal RAM for SDMA transfer"
+ default n
+ help
+ Support Internal RAM as SDMA buffer or control structures
+
+config SDMA_IRAM_SIZE
+ hex "Reserved bytes of IRAM for SDMA (0x800-0x2000)"
+ range 0x800 0x2000
+ depends on SDMA_IRAM
+ default "0x1000"
+ help
+ Set the size of IRAM for SDMA. It must be multiple of 512bytes.
+endmenu
+
+config ARCH_MXC_HAS_NFC_V1
+ bool "MXC NFC Hardware Version 1"
+ depends on !(MACH_MX31ADS && XIP_KERNEL)
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 1
+ If unsure, say N.
+
+config ARCH_MXC_HAS_NFC_V2
+ bool "MXC NFC Hardware Version 2"
+ depends on !(MACH_MX31ADS && XIP_KERNEL)
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 2
+ If unsure, say N.
+
+menu "Device options"
+
+config I2C_MXC_SELECT1
+ bool "Enable I2C1 module"
+ default y
+ depends on I2C_MXC
+ help
+ Enable MX31 I2C1 module.
+
+config I2C_MXC_SELECT2
+ bool "Enable I2C2 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX31 I2C2 module.
+
+config I2C_MXC_SELECT3
+ bool "Enable I2C3 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX31 I2C3 module.
+
+endmenu
+
endmenu
diff --git a/arch/arm/mach-mx3/Makefile b/arch/arm/mach-mx3/Makefile
index 8b21abb71fb0..3c5f0a06f51c 100644
--- a/arch/arm/mach-mx3/Makefile
+++ b/arch/arm/mach-mx3/Makefile
@@ -4,7 +4,19 @@
# Object file lists.
-obj-y := mm.o clock.o devices.o iomux.o
-obj-$(CONFIG_MACH_MX31ADS) += mx31ads.o
+obj-y := system.o iomux.o cpu.o mm.o clock.o dptc.o devices.o serial.o dma.o mxc_pm.o dvfs_v2.o
+obj-$(CONFIG_MACH_MX31ADS) += mx31ads.o mx31ads_gpio.o
obj-$(CONFIG_MACH_MX31LITE) += mx31lite.o
obj-$(CONFIG_MACH_PCM037) += pcm037.o
+obj-$(CONFIG_MACH_MX31_3DS) += mx3_3stack.o mx3_3stack_gpio.o mx3_3stack_pmic_mc13783.o
+
+# power management
+obj-$(CONFIG_PM) += pm.o
+
+obj-$(CONFIG_USB_EHCI_ARC_H1) += usb_h1.o
+obj-$(CONFIG_USB_EHCI_ARC_H2) += usb_h2.o
+
+ifneq ($(strip $(CONFIG_USB_GADGET_ARC) $(CONFIG_USB_EHCI_ARC_OTG)),)
+ obj-y += usb_dr.o
+endif
+
diff --git a/arch/arm/mach-mx3/board-mx31ads.h b/arch/arm/mach-mx3/board-mx31ads.h
new file mode 100644
index 000000000000..ac4bbb278a48
--- /dev/null
+++ b/arch/arm/mach-mx3/board-mx31ads.h
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_BOARD_MX31ADS_H__
+#define __ASM_ARCH_MXC_BOARD_MX31ADS_H__
+
+#ifdef CONFIG_MACH_MX31ADS
+/*!
+ * @defgroup BRDCFG_MX31 Board Configuration Options
+ * @ingroup MSL_MX31
+ */
+
+/*!
+ * @file mach-mx3/board-mx31ads.h
+ *
+ * @brief This file contains all the board level configuration options.
+ *
+ * It currently hold the options defined for MX31 ADS Platform.
+ *
+ * @ingroup BRDCFG_MX31
+ */
+
+/*
+ * Include Files
+ */
+#include <mach/mxc_uart.h>
+
+/*!
+ * @name MXC UART EVB board level configurations
+ */
+/*! @{ */
+/*!
+ * Specifies if the Irda transmit path is inverting
+ */
+#define MXC_IRDA_TX_INV 0
+/*!
+ * Specifies if the Irda receive path is inverting
+ */
+#define MXC_IRDA_RX_INV 0
+
+/* UART 1 configuration */
+/*!
+ * This define specifies if the UART port is configured to be in DTE or
+ * DCE mode. There exists a define like this for each UART port. Valid
+ * values that can be used are \b MODE_DTE or \b MODE_DCE.
+ */
+#define UART1_MODE MODE_DCE
+/*!
+ * This define specifies if the UART is to be used for IRDA. There exists a
+ * define like this for each UART port. Valid values that can be used are
+ * \b IRDA or \b NO_IRDA.
+ */
+#define UART1_IR NO_IRDA
+/*!
+ * This define is used to enable or disable a particular UART port. If
+ * disabled, the UART will not be registered in the file system and the user
+ * will not be able to access it. There exists a define like this for each UART
+ * port. Specify a value of 1 to enable the UART and 0 to disable it.
+ */
+#define UART1_ENABLED 1
+/*! @} */
+/* UART 2 configuration */
+#define UART2_MODE MODE_DCE
+#define UART2_IR IRDA
+#ifdef CONFIG_MXC_FIR_MODULE
+#define UART2_ENABLED 0
+#else
+#define UART2_ENABLED 1
+#endif
+/* UART 3 configuration */
+#define UART3_MODE MODE_DTE
+#define UART3_IR NO_IRDA
+#define UART3_ENABLED 1
+/* UART 4 configuration */
+#define UART4_MODE MODE_DTE
+#define UART4_IR NO_IRDA
+#define UART4_ENABLED 0 /* Disable UART 4 as its pins are shared with ATA */
+/* UART 5 configuration */
+#define UART5_MODE MODE_DTE
+#define UART5_IR NO_IRDA
+#define UART5_ENABLED 1
+
+#define MXC_LL_EXTUART_PADDR (CS4_BASE_ADDR + 0x10000)
+#define MXC_LL_EXTUART_VADDR CS4_IO_ADDRESS(MXC_LL_EXTUART_PADDR)
+#undef MXC_LL_EXTUART_16BIT_BUS
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
+
+/*!
+ * @name PBC Controller parameters
+ */
+/*! @{ */
+/*!
+ * Base address of PBC controller
+ */
+#define PBC_BASE_ADDRESS IO_ADDRESS(CS4_BASE_ADDR)
+/* Offsets for the PBC Controller register */
+/*!
+ * PBC Board status register offset
+ */
+#define PBC_BSTAT 0x000002
+/*!
+ * PBC Board control register 1 set address.
+ */
+#define PBC_BCTRL1_SET 0x000004
+/*!
+ * PBC Board control register 1 clear address.
+ */
+#define PBC_BCTRL1_CLEAR 0x000006
+/*!
+ * PBC Board control register 2 set address.
+ */
+#define PBC_BCTRL2_SET 0x000008
+/*!
+ * PBC Board control register 2 clear address.
+ */
+#define PBC_BCTRL2_CLEAR 0x00000A
+/*!
+ * PBC Board control register 3 set address.
+ */
+#define PBC_BCTRL3_SET 0x00000C
+/*!
+ * PBC Board control register 3 clear address.
+ */
+#define PBC_BCTRL3_CLEAR 0x00000E
+/*!
+ * PBC Board control register 4 set address.
+ */
+#define PBC_BCTRL4_SET 0x000010
+/*!
+ * PBC Board control register 4 clear address.
+ */
+#define PBC_BCTRL4_CLEAR 0x000012
+/*!
+ * PBC Board status register 1.
+ */
+#define PBC_BSTAT1 0x000014
+/*!
+ * PBC Board interrupt status register.
+ */
+#define PBC_INTSTATUS 0x000016
+/*!
+ * PBC Board interrupt current status register.
+ */
+#define PBC_INTCURR_STATUS 0x000018
+/*!
+ * PBC Interrupt mask register set address.
+ */
+#define PBC_INTMASK_SET 0x00001A
+/*!
+ * PBC Interrupt mask register clear address.
+ */
+#define PBC_INTMASK_CLEAR 0x00001C
+
+/*!
+ * External UART A.
+ */
+#define PBC_SC16C652_UARTA 0x010000
+/*!
+ * External UART B.
+ */
+#define PBC_SC16C652_UARTB 0x010010
+/*!
+ * Ethernet Controller IO base address.
+ */
+#define PBC_CS8900A_IOBASE 0x020000
+/*!
+ * Ethernet Controller Memory base address.
+ */
+#define PBC_CS8900A_MEMBASE 0x021000
+/*!
+ * Ethernet Controller DMA base address.
+ */
+#define PBC_CS8900A_DMABASE 0x022000
+/*!
+ * External chip select 0.
+ */
+#define PBC_XCS0 0x040000
+/*!
+ * LCD Display enable.
+ */
+#define PBC_LCD_EN_B 0x060000
+/*!
+ * Code test debug enable.
+ */
+#define PBC_CODE_B 0x070000
+/*!
+ * PSRAM memory select.
+ */
+#define PBC_PSRAM_B 0x5000000
+
+/* PBC Board Status Register 1 bit definitions */
+#define PBC_BSTAT1_NF_DET 0x0001 /* NAND flash card. 0 = connected */
+#define PBC_BSTAT1_KP_ON 0x0002 /* KPP board. 0 = connected */
+#define PBC_BSTAT1_LS 0x0004 /* KPP:LightSense signal */
+#define PBC_BSTAT1_ATA_IOCS16 0x0008 /* ATA_IOCS16 signal */
+#define PBC_BSTAT1_ATA_CBLID 0x0010 /* ATA_CBLID signal */
+#define PBC_BSTAT1_ATA_DASP 0x0020 /* ATA_DASP signal */
+#define PBC_BSTAT1_PWR_RDY 0x0040 /* MC13783 power. 1 = ready */
+#define PBC_BSTAT1_SD1_WP 0x0080 /* 0 = SD1 card is write protected */
+#define PBC_BSTAT1_SD2_WP 0x0100 /* 0 = SD2 card is write protected */
+#define PBC_BSTAT1_FS1 0x0200 /* KPP:FlipSense1 signal */
+#define PBC_BSTAT1_FS2 0x0400 /* KPP:FlipSense2 signal */
+#define PBC_BSTAT1_PTT 0x0800 /* KPP:PTT signal */
+#define PBC_BSTAT1_MC13783_IN 0x1000 /* MC13783 board. 0 = connected. */
+
+/* PBC Board Control Register 1 bit definitions */
+#define PBC_BCTRL1_ERST 0x0001 /* Ethernet Reset */
+#define PBC_BCTRL1_URST 0x0002 /* Reset External UART controller */
+#define PBC_BCTRL1_UENA 0x0004 /* Enable UART A transceiver */
+#define PBC_BCTRL1_UENB 0x0008 /* Enable UART B transceiver */
+#define PBC_BCTRL1_UENCE 0x0010 /* Enable UART CE transceiver */
+#define PBC_BCTRL1_IREN 0x0020 /* Enable the IRDA transmitter */
+#define PBC_BCTRL1_LED0 0x0040 /* Used to control LED 0 (green) */
+#define PBC_BCTRL1_LED1 0x0080 /* Used to control LED 1 (yellow) */
+#define PBC_BCTRL1_SENSOR1_ON 0x0600 /* Enable Sensor 1 */
+#define PBC_BCTRL1_SENSOR2_ON 0x3000 /* Enable Sensor 2 */
+#define PBC_BCTRL1_BEND 0x4000 /* Big Endian Select */
+#define PBC_BCTRL1_LCDON 0x8000 /* Enable the LCD */
+
+/* PBC Board Control Register 2 bit definitions */
+#define PBC_BCTRL2_USELA 0x0001 /* UART A Select, 0 = UART1, 1 = UART5 */
+#define PBC_BCTRL2_USELB 0x0002 /* UART B Select, 0 = UART3, 1 = UART5 */
+#define PBC_BCTRL2_USELC 0x0004 /* UART C Select, 0 = UART2, 1 = UART1 */
+#define PBC_BCTRL2_UMODENA 0x0008 /* UART A Modem Signals Enable, 0 = enabled */
+#define PBC_BCTRL2_UMODENC 0x0008 /* UART C Modem Signals Enable, 0 = enabled */
+#define PBC_BCTRL2_CSI_EN 0x0020 /* Enable the CSI interface, 0 = enabled */
+#define PBC_BCTRL2_ATA_EN 0x0040 /* Enable the ATA interface, 0 = enabled */
+#define PBC_BCTRL2_ATA_SEL 0x0080 /* ATA Select, 0 = group A, 1 = group B */
+#define PBC_BCTRL2_IRDA_MOD 0x0100 /* IRDA Mode (see CPLD spec) */
+#define PBC_BCTRL2_LDC_RST0 0x0200 /* LCD 0 Reset, 1 = reset signal asserted */
+#define PBC_BCTRL2_LDC_RST1 0x0400 /* LCD 1 Reset, 1 = reset signal asserted */
+#define PBC_BCTRL2_LDC_RST2 0x0800 /* LCD 2 Reset, 1 = reset signal asserted */
+#define PBC_BCTRL2_LDCIO_EN 0x1000 /* LCD GPIO Enable, 0 = enabled */
+#define PBC_BCTRL2_CT_CS 0x2000 /* Code Test Chip Select, = Code Test selected */
+#define PBC_BCTRL2_VPP_EN 0x4000 /* PCMCIA VPP Enable, 1 = power on */
+#define PBC_BCTRL2_VCC_EN 0x8000 /* PCMCIA VCC Enable, 1 = power on */
+
+/* PBC Board Control Register 3 bit definitions */
+#define PBC_BCTRL3_OTG_FS_SEL 0x0001 /* USB OTG Full Speed Select, 0 = PMIC, 1 = CPU */
+#define PBC_BCTRL3_OTG_FS_EN 0x0002 /* USB OTG Full Speed Enable, 0 = enabled */
+#define PBC_BCTRL3_FSH_SEL 0x0004 /* USB Full Speed Host Select, 0 = Group A, 1 = Group B */
+#define PBC_BCTRL3_FSH_EN 0x0008 /* USB Full Speed Host Enable, 0 = enabled */
+#define PBC_BCTRL3_HSH_SEL 0x0010 /* USB High Speed Host Select, 0 = Group A, 1 = Group B */
+#define PBC_BCTRL3_HSH_EN 0x0020 /* USB High Speed Host Enable, 0 = enabled */
+#define PBC_BCTRL3_FSH_MOD 0x0040 /* USB Full Speed Host Mode, 0 = Differential, 1 = Single ended */
+#define PBC_BCTRL3_OTG_HS_EN 0x0080 /* USB OTG High Speed Enable, 0 = enabled */
+#define PBC_BCTRL3_OTG_VBUS_EN 0x0100 /* USB OTG VBUS Regulator Enable, 0 = enabled */
+#define PBC_BCTRL3_FSH_VBUS_EN 0x0200 /* USB Full Speed Host VBUS Regulator Enable, 0 = enabled */
+#define PBC_BCTRL3_CARD1_SEL 0x0400 /* Card1 Select, 0 = SD1, 1 = MS1 */
+#define PBC_BCTRL3_CARD2_SEL 0x0800 /* Card2 Select, 0 = PCMCIA & SD2, 1 = MS2 */
+#define PBC_BCTRL3_SYNTH_RST 0x1000 /* Audio Synthesizer Reset, 0 = reset asserted */
+#define PBC_BCTRL3_VSIM_EN 0x2000 /* VSIM Regulator Enable, 1 = enabled */
+#define PBC_BCTRL3_VESIM_EN 0x4000 /* VESIM Regulator Enable, 1 = enabled */
+#define PBC_BCTRL3_SPI3_RESET 0x8000 /* CSPI3 Connector Reset, 0 = reset asserted */
+
+/* PBC Board Control Register 4 bit definitions */
+#define PBC_BCTRL4_CSI_MSB_EN 0x0001 /* CSI MSB Enable, 0 = CSI_Data[3:0] enabled */
+#define PBC_BCTRL4_REGEN_SEL 0x0002 /* Regulator Enable Select, 0 = enabled */
+#define PBC_BCTRL4_USER_OFF 0x0004 /* User Off Indication, 1 = user off confirmation */
+#define PBC_BCTRL4_VIB_EN 0x0008 /* Vibrator Enable, 1 = enabled */
+#define PBC_BCTRL4_PCMCIA_EN 0x0010 /* PCMCIA Enable, 0 = buffer enabled */
+
+#define CKIH_27MHZ_BIT_SET (1 << 4)
+
+#define PBC_INT_CS8900A 4
+/*! @} */
+
+#define PBC_INTSTATUS_REG (PBC_INTSTATUS + PBC_BASE_ADDRESS)
+#define PBC_INTCURR_STATUS_REG (PBC_INTCURR_STATUS + PBC_BASE_ADDRESS)
+#define PBC_INTMASK_SET_REG (PBC_INTMASK_SET + PBC_BASE_ADDRESS)
+#define PBC_INTMASK_CLEAR_REG (PBC_INTMASK_CLEAR + PBC_BASE_ADDRESS)
+#define EXPIO_PARENT_INT IOMUX_TO_IRQ(MX31_PIN_GPIO1_4)
+
+#define EXPIO_INT_LOW_BAT (MXC_EXP_IO_BASE + 0)
+#define EXPIO_INT_PB_IRQ (MXC_EXP_IO_BASE + 1)
+#define EXPIO_INT_OTG_FS_OVR (MXC_EXP_IO_BASE + 2)
+#define EXPIO_INT_FSH_OVR (MXC_EXP_IO_BASE + 3)
+#define EXPIO_INT_RES4 (MXC_EXP_IO_BASE + 4)
+#define EXPIO_INT_RES5 (MXC_EXP_IO_BASE + 5)
+#define EXPIO_INT_RES6 (MXC_EXP_IO_BASE + 6)
+#define EXPIO_INT_RES7 (MXC_EXP_IO_BASE + 7)
+#define EXPIO_INT_ENET_INT (MXC_EXP_IO_BASE + 8)
+#define EXPIO_INT_OTG_FS_INT (MXC_EXP_IO_BASE + 9)
+#define EXPIO_INT_XUART_INTA (MXC_EXP_IO_BASE + 10)
+#define EXPIO_INT_XUART_INTB (MXC_EXP_IO_BASE + 11)
+#define EXPIO_INT_SYNTH_IRQ (MXC_EXP_IO_BASE + 12)
+#define EXPIO_INT_CE_INT1 (MXC_EXP_IO_BASE + 13)
+#define EXPIO_INT_CE_INT2 (MXC_EXP_IO_BASE + 14)
+#define EXPIO_INT_RES15 (MXC_EXP_IO_BASE + 15)
+
+/*!
+ * @name Defines Base address and IRQ used for CS8900A Ethernet Controller on MXC Boards
+ */
+/*! @{*/
+/*! This is System IRQ used by CS8900A for interrupt generation taken from platform.h */
+#define CS8900AIRQ EXPIO_INT_ENET_INT
+/*! This is I/O Base address used to access registers of CS8900A on MXC ADS */
+#define CS8900A_BASE_ADDRESS (PBC_BASE_ADDRESS + PBC_CS8900A_IOBASE + 0x300)
+/*! @} */
+
+#define MXC_PMIC_INT_LINE IOMUX_TO_IRQ(MX31_PIN_GPIO1_3)
+
+#define AHB_FREQ 133000000
+#define IPG_FREQ 66500000
+
+#define MXC_BD_LED1 (1 << 6)
+#define MXC_BD_LED2 (1 << 7)
+#define MXC_BD_LED_ON(led) \
+ __raw_writew(led, PBC_BASE_ADDRESS + PBC_BCTRL1_SET)
+#define MXC_BD_LED_OFF(led) \
+ __raw_writew(led, PBC_BASE_ADDRESS + PBC_BCTRL1_CLEAR)
+
+#endif /* CONFIG_MACH_MX31ADS */
+#endif /* __ASM_ARCH_MXC_BOARD_MX31ADS_H__ */
diff --git a/arch/arm/mach-mx3/board-mx3_3stack.h b/arch/arm/mach-mx3/board-mx3_3stack.h
new file mode 100644
index 000000000000..6f59ebe49a9e
--- /dev/null
+++ b/arch/arm/mach-mx3/board-mx3_3stack.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_BOARD_MX31PDK_H__
+#define __ASM_ARCH_MXC_BOARD_MX31PDK_H__
+
+#ifdef CONFIG_MACH_MX31_3DS
+/*!
+ * @defgroup BRDCFG_MX31 Board Configuration Options
+ * @ingroup MSL_MX31
+ */
+
+/*!
+ * @file mach-mx3/board-mx3_3stack.h
+ *
+ * @brief This file contains all the board level configuration options.
+ *
+ * It currently hold the options defined for MX31 3STACK Platform.
+ *
+ * @ingroup BRDCFG_MX31
+ */
+
+/*
+ * Include Files
+ */
+#include <mach/mxc_uart.h>
+
+/*!
+ * @name MXC UART EVB board level configurations
+ */
+/*! @{ */
+/*!
+ * Specifies if the Irda transmit path is inverting
+ */
+#define MXC_IRDA_TX_INV 0
+/*!
+ * Specifies if the Irda receive path is inverting
+ */
+#define MXC_IRDA_RX_INV 0
+
+/* UART 1 configuration */
+/*!
+ * This define specifies if the UART port is configured to be in DTE or
+ * DCE mode. There exists a define like this for each UART port. Valid
+ * values that can be used are \b MODE_DTE or \b MODE_DCE.
+ */
+#define UART1_MODE MODE_DCE
+/*!
+ * This define specifies if the UART is to be used for IRDA. There exists a
+ * define like this for each UART port. Valid values that can be used are
+ * \b IRDA or \b NO_IRDA.
+ */
+#define UART1_IR NO_IRDA
+/*!
+ * This define is used to enable or disable a particular UART port. If
+ * disabled, the UART will not be registered in the file system and the user
+ * will not be able to access it. There exists a define like this for each UART
+ * port. Specify a value of 1 to enable the UART and 0 to disable it.
+ */
+#define UART1_ENABLED 1
+/*! @} */
+/* UART 2 configuration */
+#define UART2_MODE MODE_DCE
+#define UART2_IR NO_IRDA
+#define UART2_ENABLED 1
+/* UART 3 configuration */
+#define UART3_MODE MODE_DTE
+#define UART3_IR NO_IRDA
+#define UART3_ENABLED 1
+/* UART 4 configuration */
+#define UART4_MODE MODE_DTE
+#define UART4_IR NO_IRDA
+#define UART4_ENABLED 0 /* Disable UART 4 as its pins are shared with ATA */
+/* UART 5 configuration */
+#define UART5_MODE MODE_DTE
+#define UART5_IR NO_IRDA
+#define UART5_ENABLED 0
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
+
+#define DEBUG_BASE_ADDRESS CS5_BASE_ADDR
+/* LAN9217 ethernet base address */
+#define LAN9217_BASE_ADDR DEBUG_BASE_ADDRESS
+/* External UART */
+#define UARTA_BASE_ADDR (DEBUG_BASE_ADDRESS + 0x8000)
+#define UARTB_BASE_ADDR (DEBUG_BASE_ADDRESS + 0x10000)
+
+#define BOARD_IO_ADDR (DEBUG_BASE_ADDRESS + 0x20000)
+/* LED switchs */
+#define LED_SWITCH_REG 0x00
+/* buttons */
+#define SWITCH_BUTTONS_REG 0x08
+/* status, interrupt */
+#define INTR_STATUS_REG 0x10
+#define INTR_MASK_REG 0x38
+#define INTR_RESET_REG 0x20
+/* magic word for debug CPLD */
+#define MAGIC_NUMBER1_REG 0x40
+#define MAGIC_NUMBER2_REG 0x48
+/* CPLD code version */
+#define CPLD_CODE_VER_REG 0x50
+/* magic word for debug CPLD */
+#define MAGIC_NUMBER3_REG 0x58
+/* module reset register*/
+#define MODULE_RESET_REG 0x60
+/* CPU ID and Personality ID */
+#define MCU_BOARD_ID_REG 0x68
+
+/* interrupts like external uart , external ethernet etc*/
+#define EXPIO_PARENT_INT IOMUX_TO_IRQ(MX31_PIN_GPIO1_1)
+
+#define EXPIO_INT_ENET (MXC_EXP_IO_BASE + 0)
+#define EXPIO_INT_XUART_A (MXC_EXP_IO_BASE + 1)
+#define EXPIO_INT_XUART_B (MXC_EXP_IO_BASE + 2)
+#define EXPIO_INT_BUTTON_A (MXC_EXP_IO_BASE + 3)
+#define EXPIO_INT_BUTTON_B (MXC_EXP_IO_BASE + 4)
+
+/*! This is System IRQ used by LAN9217 */
+#define LAN9217_IRQ EXPIO_INT_ENET
+
+/*! LED definition*/
+#define MXC_BD_LED1 (1)
+#define MXC_BD_LED2 (1 << 1)
+#define MXC_BD_LED3 (1 << 2)
+#define MXC_BD_LED4 (1 << 3)
+#define MXC_BD_LED5 (1 << 4)
+#define MXC_BD_LED6 (1 << 5)
+#define MXC_BD_LED7 (1 << 6)
+#define MXC_BD_LED8 (1 << 7)
+
+#define MXC_BD_LED_ON(led)
+#define MXC_BD_LED_OFF(led)
+
+extern unsigned int sdhc_get_card_det_status(struct device *dev);
+extern int sdhc_init_card_det(int id);
+extern int sdhc_write_protect(struct device *dev);
+
+extern int __init mx3_3stack_init_mc13783(void);
+
+#endif /* CONFIG_MACH_MX31_3DS */
+#endif /* __ASM_ARCH_MXC_BOARD_MX31PDK_H__ */
diff --git a/arch/arm/mach-mx3/clock.c b/arch/arm/mach-mx3/clock.c
index 9f14a871ee7c..b2263f9f5373 100644
--- a/arch/arm/mach-mx3/clock.c
+++ b/arch/arm/mach-mx3/clock.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2008 by Sascha Hauer <kernel@pengutronix.de>
*
* This program is free software; you can redistribute it and/or
@@ -17,6 +17,7 @@
* MA 02110-1301, USA.
*/
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
@@ -24,11 +25,17 @@
#include <linux/err.h>
#include <linux/io.h>
#include <mach/clock.h>
+#include <mach/mxc_dptc.h>
#include <asm/div64.h>
+#include <mach/mxc_dptc.h>
#include "crm_regs.h"
#define PRE_DIV_MIN_FREQ 10000000 /* Minimum Frequency after Predivider */
+#define PROPAGATE_RATE_DIS 2
+
+static int cpu_clk_set_wp(int wp);
+struct timer_list dptcen_timer;
static void __calc_pre_post_dividers(u32 div, u32 *pre, u32 *post)
{
@@ -152,10 +159,23 @@ static int _clk_pll_set_rate(struct clk *clk, unsigned long rate)
else if (clk == &serial_pll_clk)
__raw_writel(reg, MXC_CCM_SRPCTL);
+ clk->rate = rate;
return 0;
}
-static unsigned long _clk_pll_get_rate(struct clk *clk)
+static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate)
+{
+ if ((rate < ahb_clk.rate) || (rate % ahb_clk.rate != 0)) {
+ printk(KERN_ERR "Wrong rate %lu in _clk_cpu_set_rate\n", rate);
+ return -EINVAL;
+ }
+
+ cpu_clk_set_wp(rate / ahb_clk.rate - 1);
+
+ return PROPAGATE_RATE_DIS;
+}
+
+static void _clk_pll_recalc(struct clk *clk)
{
long mfi, mfn, mfd, pdf, ref_clk, mfn_abs;
unsigned long reg, ccmr;
@@ -170,10 +190,14 @@ static unsigned long _clk_pll_get_rate(struct clk *clk)
ref_clk = clk_get_rate(&ckih_clk);
if (clk == &mcu_pll_clk) {
- if ((ccmr & MXC_CCM_CCMR_MPE) == 0)
- return ref_clk;
- if ((ccmr & MXC_CCM_CCMR_MDS) != 0)
- return ref_clk;
+ if ((ccmr & MXC_CCM_CCMR_MPE) == 0) {
+ clk->rate = ref_clk;
+ return;
+ }
+ if ((ccmr & MXC_CCM_CCMR_MDS) != 0) {
+ clk->rate = ref_clk;
+ return;
+ }
reg = __raw_readl(MXC_CCM_MPCTL);
} else if (clk == &usb_pll_clk)
reg = __raw_readl(MXC_CCM_UPCTL);
@@ -181,7 +205,7 @@ static unsigned long _clk_pll_get_rate(struct clk *clk)
reg = __raw_readl(MXC_CCM_SRPCTL);
else {
BUG();
- return 0;
+ return;
}
pdf = (reg & MXC_CCM_PCTL_PD_MASK) >> MXC_CCM_PCTL_PD_OFFSET;
@@ -204,7 +228,7 @@ static unsigned long _clk_pll_get_rate(struct clk *clk)
temp = -temp;
temp = (ref_clk * mfi) + temp;
- return temp;
+ clk->rate = temp;
}
static int _clk_usb_pll_enable(struct clk *clk)
@@ -257,53 +281,103 @@ static void _clk_serial_pll_disable(struct clk *clk)
#define PDR1(mask, off) ((__raw_readl(MXC_CCM_PDR1) & mask) >> off)
#define PDR2(mask, off) ((__raw_readl(MXC_CCM_PDR2) & mask) >> off)
-static unsigned long _clk_mcu_main_get_rate(struct clk *clk)
+static void _clk_mcu_main_recalc(struct clk *clk)
{
u32 pmcr0 = __raw_readl(MXC_CCM_PMCR0);
- if ((pmcr0 & MXC_CCM_PMCR0_DFSUP1) == MXC_CCM_PMCR0_DFSUP1_SPLL)
- return clk_get_rate(&serial_pll_clk);
- else
- return clk_get_rate(&mcu_pll_clk);
+ if ((pmcr0 & MXC_CCM_PMCR0_DFSUP1) == MXC_CCM_PMCR0_DFSUP1_SPLL) {
+ serial_pll_clk.recalc(&serial_pll_clk);
+ clk->rate = serial_pll_clk.rate;
+ } else {
+ mcu_pll_clk.recalc(&mcu_pll_clk);
+ clk->rate = mcu_pll_clk.rate;
+ }
}
-static unsigned long _clk_hclk_get_rate(struct clk *clk)
+static void _clk_cpu_recalc(struct clk *clk)
+{
+ unsigned long mcu_pdf;
+
+ mcu_pdf = PDR0(MXC_CCM_PDR0_MCU_PODF_MASK,
+ MXC_CCM_PDR0_MCU_PODF_OFFSET);
+ clk->rate = clk->parent->rate / (mcu_pdf + 1);
+}
+
+static void _clk_hclk_recalc(struct clk *clk)
{
unsigned long max_pdf;
max_pdf = PDR0(MXC_CCM_PDR0_MAX_PODF_MASK,
MXC_CCM_PDR0_MAX_PODF_OFFSET);
- return clk_get_rate(clk->parent) / (max_pdf + 1);
+ clk->rate = clk->parent->rate / (max_pdf + 1);
}
-static unsigned long _clk_ipg_get_rate(struct clk *clk)
+static void _clk_ipg_recalc(struct clk *clk)
{
unsigned long ipg_pdf;
ipg_pdf = PDR0(MXC_CCM_PDR0_IPG_PODF_MASK,
MXC_CCM_PDR0_IPG_PODF_OFFSET);
- return clk_get_rate(clk->parent) / (ipg_pdf + 1);
+ clk->rate = clk->parent->rate / (ipg_pdf + 1);
}
-static unsigned long _clk_nfc_get_rate(struct clk *clk)
+static void _clk_nfc_recalc(struct clk *clk)
{
unsigned long nfc_pdf;
nfc_pdf = PDR0(MXC_CCM_PDR0_NFC_PODF_MASK,
MXC_CCM_PDR0_NFC_PODF_OFFSET);
- return clk_get_rate(clk->parent) / (nfc_pdf + 1);
+ clk->rate = clk->parent->rate / (nfc_pdf + 1);
+
+}
+
+static int _clk_nfc_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ if (div > 8)
+ return -EINVAL;
+
+ reg = __raw_readl(MXC_CCM_PDR0) & ~MXC_CCM_PDR0_NFC_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_PDR0_NFC_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_PDR0);
+
+ clk->rate = rate;
+
+ return 0;
+
+}
+
+static unsigned long _clk_nfc_round_rate(struct clk *clk, unsigned long rate)
+{
+ u32 div = clk->parent->rate / rate;
+
+ if (clk->parent->rate % rate)
+ div++;
+
+ if (div > 8)
+ return -EINVAL;
+
+ return clk->parent->rate / div;
+
}
-static unsigned long _clk_hsp_get_rate(struct clk *clk)
+static void _clk_hsp_recalc(struct clk *clk)
{
unsigned long hsp_pdf;
hsp_pdf = PDR0(MXC_CCM_PDR0_HSP_PODF_MASK,
MXC_CCM_PDR0_HSP_PODF_OFFSET);
- return clk_get_rate(clk->parent) / (hsp_pdf + 1);
+ clk->rate = clk->parent->rate / (hsp_pdf + 1);
}
-static unsigned long _clk_usb_get_rate(struct clk *clk)
+static void _clk_usb_recalc(struct clk *clk)
{
unsigned long usb_pdf, usb_prepdf;
@@ -311,10 +385,10 @@ static unsigned long _clk_usb_get_rate(struct clk *clk)
MXC_CCM_PDR1_USB_PODF_OFFSET);
usb_prepdf = PDR1(MXC_CCM_PDR1_USB_PRDF_MASK,
MXC_CCM_PDR1_USB_PRDF_OFFSET);
- return clk_get_rate(clk->parent) / (usb_prepdf + 1) / (usb_pdf + 1);
+ clk->rate = clk->parent->rate / (usb_prepdf + 1) / (usb_pdf + 1);
}
-static unsigned long _clk_csi_get_rate(struct clk *clk)
+static void _clk_csi_recalc(struct clk *clk)
{
u32 reg, pre, post;
@@ -325,7 +399,7 @@ static unsigned long _clk_csi_get_rate(struct clk *clk)
post = (reg & MXC_CCM_PDR0_CSI_PODF_MASK) >>
MXC_CCM_PDR0_CSI_PODF_OFFSET;
post++;
- return clk_get_rate(clk->parent) / (pre * post);
+ clk->rate = clk->parent->rate / (pre * post);
}
static unsigned long _clk_csi_round_rate(struct clk *clk, unsigned long rate)
@@ -359,19 +433,20 @@ static int _clk_csi_set_rate(struct clk *clk, unsigned long rate)
reg |= (pre - 1) << MXC_CCM_PDR0_CSI_PRDF_OFFSET;
__raw_writel(reg, MXC_CCM_PDR0);
+ clk->rate = rate;
return 0;
}
-static unsigned long _clk_per_get_rate(struct clk *clk)
+static void _clk_per_recalc(struct clk *clk)
{
unsigned long per_pdf;
per_pdf = PDR0(MXC_CCM_PDR0_PER_PODF_MASK,
MXC_CCM_PDR0_PER_PODF_OFFSET);
- return clk_get_rate(clk->parent) / (per_pdf + 1);
+ clk->rate = clk->parent->rate / (per_pdf + 1);
}
-static unsigned long _clk_ssi1_get_rate(struct clk *clk)
+static void _clk_ssi1_recalc(struct clk *clk)
{
unsigned long ssi1_pdf, ssi1_prepdf;
@@ -379,10 +454,10 @@ static unsigned long _clk_ssi1_get_rate(struct clk *clk)
MXC_CCM_PDR1_SSI1_PODF_OFFSET);
ssi1_prepdf = PDR1(MXC_CCM_PDR1_SSI1_PRE_PODF_MASK,
MXC_CCM_PDR1_SSI1_PRE_PODF_OFFSET);
- return clk_get_rate(clk->parent) / (ssi1_prepdf + 1) / (ssi1_pdf + 1);
+ clk->rate = clk->parent->rate / (ssi1_prepdf + 1) / (ssi1_pdf + 1);
}
-static unsigned long _clk_ssi2_get_rate(struct clk *clk)
+static void _clk_ssi2_recalc(struct clk *clk)
{
unsigned long ssi2_pdf, ssi2_prepdf;
@@ -390,10 +465,10 @@ static unsigned long _clk_ssi2_get_rate(struct clk *clk)
MXC_CCM_PDR1_SSI2_PODF_OFFSET);
ssi2_prepdf = PDR1(MXC_CCM_PDR1_SSI2_PRE_PODF_MASK,
MXC_CCM_PDR1_SSI2_PRE_PODF_OFFSET);
- return clk_get_rate(clk->parent) / (ssi2_prepdf + 1) / (ssi2_pdf + 1);
+ clk->rate = clk->parent->rate / (ssi2_prepdf + 1) / (ssi2_pdf + 1);
}
-static unsigned long _clk_firi_get_rate(struct clk *clk)
+static void _clk_firi_recalc(struct clk *clk)
{
unsigned long firi_pdf, firi_prepdf;
@@ -401,7 +476,7 @@ static unsigned long _clk_firi_get_rate(struct clk *clk)
MXC_CCM_PDR1_FIRI_PODF_OFFSET);
firi_prepdf = PDR1(MXC_CCM_PDR1_FIRI_PRE_PODF_MASK,
MXC_CCM_PDR1_FIRI_PRE_PODF_OFFSET);
- return clk_get_rate(clk->parent) / (firi_prepdf + 1) / (firi_pdf + 1);
+ clk->rate = clk->parent->rate / (firi_prepdf + 1) / (firi_pdf + 1);
}
static unsigned long _clk_firi_round_rate(struct clk *clk, unsigned long rate)
@@ -437,100 +512,104 @@ static int _clk_firi_set_rate(struct clk *clk, unsigned long rate)
reg |= (post - 1) << MXC_CCM_PDR1_FIRI_PODF_OFFSET;
__raw_writel(reg, MXC_CCM_PDR1);
+ clk->rate = rate;
return 0;
}
-static unsigned long _clk_mbx_get_rate(struct clk *clk)
+static void _clk_mbx_recalc(struct clk *clk)
{
- return clk_get_rate(clk->parent) / 2;
+ clk->rate = clk->parent->rate / 2;
}
-static unsigned long _clk_mstick1_get_rate(struct clk *clk)
+static void _clk_mstick1_recalc(struct clk *clk)
{
unsigned long msti_pdf;
msti_pdf = PDR2(MXC_CCM_PDR2_MST1_PDF_MASK,
MXC_CCM_PDR2_MST1_PDF_OFFSET);
- return clk_get_rate(clk->parent) / (msti_pdf + 1);
+ clk->rate = clk->parent->rate / (msti_pdf + 1);
}
-static unsigned long _clk_mstick2_get_rate(struct clk *clk)
+static void _clk_mstick2_recalc(struct clk *clk)
{
unsigned long msti_pdf;
msti_pdf = PDR2(MXC_CCM_PDR2_MST2_PDF_MASK,
MXC_CCM_PDR2_MST2_PDF_OFFSET);
- return clk_get_rate(clk->parent) / (msti_pdf + 1);
-}
-
-static unsigned long ckih_rate;
-
-static unsigned long clk_ckih_get_rate(struct clk *clk)
-{
- return ckih_rate;
+ clk->rate = clk->parent->rate / (msti_pdf + 1);
}
static struct clk ckih_clk = {
.name = "ckih",
- .get_rate = clk_ckih_get_rate,
+ .rate = 0, /* determined later (26 or 27 MHz) */
+ .flags = RATE_PROPAGATES,
};
-static unsigned long clk_ckil_get_rate(struct clk *clk)
-{
- return CKIL_CLK_FREQ;
-}
-
static struct clk ckil_clk = {
.name = "ckil",
- .get_rate = clk_ckil_get_rate,
+ .rate = CKIL_CLK_FREQ,
+ .flags = RATE_PROPAGATES,
};
static struct clk mcu_pll_clk = {
.name = "mcu_pll",
.parent = &ckih_clk,
.set_rate = _clk_pll_set_rate,
- .get_rate = _clk_pll_get_rate,
+ .recalc = _clk_pll_recalc,
+ .flags = RATE_PROPAGATES,
};
static struct clk mcu_main_clk = {
.name = "mcu_main_clk",
.parent = &mcu_pll_clk,
- .get_rate = _clk_mcu_main_get_rate,
+ .recalc = _clk_mcu_main_recalc,
};
static struct clk serial_pll_clk = {
.name = "serial_pll",
.parent = &ckih_clk,
.set_rate = _clk_pll_set_rate,
- .get_rate = _clk_pll_get_rate,
+ .recalc = _clk_pll_recalc,
.enable = _clk_serial_pll_enable,
.disable = _clk_serial_pll_disable,
+ .flags = RATE_PROPAGATES,
};
static struct clk usb_pll_clk = {
.name = "usb_pll",
.parent = &ckih_clk,
.set_rate = _clk_pll_set_rate,
- .get_rate = _clk_pll_get_rate,
+ .recalc = _clk_pll_recalc,
.enable = _clk_usb_pll_enable,
.disable = _clk_usb_pll_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk cpu_clk = {
+ .name = "cpu_clk",
+ .parent = &mcu_main_clk,
+ .recalc = _clk_cpu_recalc,
+ .set_rate = _clk_cpu_set_rate,
};
static struct clk ahb_clk = {
.name = "ahb_clk",
.parent = &mcu_main_clk,
- .get_rate = _clk_hclk_get_rate,
+ .recalc = _clk_hclk_recalc,
+ .flags = RATE_PROPAGATES,
};
static struct clk per_clk = {
.name = "per_clk",
.parent = &usb_pll_clk,
- .get_rate = _clk_per_get_rate,
+ .recalc = _clk_per_recalc,
+ .flags = RATE_PROPAGATES,
};
static struct clk perclk_clk = {
.name = "perclk_clk",
.parent = &ipg_clk,
+ .flags = RATE_PROPAGATES,
};
static struct clk cspi_clk[] = {
@@ -563,7 +642,8 @@ static struct clk cspi_clk[] = {
static struct clk ipg_clk = {
.name = "ipg_clk",
.parent = &ahb_clk,
- .get_rate = _clk_ipg_get_rate,
+ .recalc = _clk_ipg_recalc,
+ .flags = RATE_PROPAGATES,
};
static struct clk emi_clk = {
@@ -585,10 +665,10 @@ static struct clk gpt_clk = {
};
static struct clk pwm_clk = {
- .name = "pwm_clk",
+ .name = "pwm",
.parent = &perclk_clk,
.enable = _clk_enable,
- .enable_reg = MXC_CCM_CGR0,
+ .enable_reg = MXC_CCM_CGR1,
.enable_shift = MXC_CCM_CGR1_PWM_OFFSET,
.disable = _clk_disable,
};
@@ -615,7 +695,9 @@ static struct clk epit_clk[] = {
static struct clk nfc_clk = {
.name = "nfc_clk",
.parent = &ahb_clk,
- .get_rate = _clk_nfc_get_rate,
+ .recalc = _clk_nfc_recalc,
+ .set_rate = _clk_nfc_set_rate,
+ .round_rate = _clk_nfc_round_rate,
};
static struct clk scc_clk = {
@@ -626,7 +708,7 @@ static struct clk scc_clk = {
static struct clk ipu_clk = {
.name = "ipu_clk",
.parent = &mcu_main_clk,
- .get_rate = _clk_hsp_get_rate,
+ .recalc = _clk_hsp_recalc,
.enable = _clk_enable,
.enable_reg = MXC_CCM_CGR1,
.enable_shift = MXC_CCM_CGR1_IPU_OFFSET,
@@ -663,7 +745,7 @@ static struct clk usb_clk[] = {
{
.name = "usb_clk",
.parent = &usb_pll_clk,
- .get_rate = _clk_usb_get_rate,},
+ .recalc = _clk_usb_recalc,},
{
.name = "usb_ahb_clk",
.parent = &ahb_clk,
@@ -676,7 +758,7 @@ static struct clk usb_clk[] = {
static struct clk csi_clk = {
.name = "csi_clk",
.parent = &serial_pll_clk,
- .get_rate = _clk_csi_get_rate,
+ .recalc = _clk_csi_recalc,
.round_rate = _clk_csi_round_rate,
.set_rate = _clk_csi_set_rate,
.enable = _clk_enable,
@@ -787,7 +869,7 @@ static struct clk ssi_clk[] = {
{
.name = "ssi_clk",
.parent = &serial_pll_clk,
- .get_rate = _clk_ssi1_get_rate,
+ .recalc = _clk_ssi1_recalc,
.enable = _clk_enable,
.enable_reg = MXC_CCM_CGR0,
.enable_shift = MXC_CCM_CGR0_SSI1_OFFSET,
@@ -796,7 +878,7 @@ static struct clk ssi_clk[] = {
.name = "ssi_clk",
.id = 1,
.parent = &serial_pll_clk,
- .get_rate = _clk_ssi2_get_rate,
+ .recalc = _clk_ssi2_recalc,
.enable = _clk_enable,
.enable_reg = MXC_CCM_CGR2,
.enable_shift = MXC_CCM_CGR2_SSI2_OFFSET,
@@ -808,7 +890,7 @@ static struct clk firi_clk = {
.parent = &usb_pll_clk,
.round_rate = _clk_firi_round_rate,
.set_rate = _clk_firi_set_rate,
- .get_rate = _clk_firi_get_rate,
+ .recalc = _clk_firi_recalc,
.enable = _clk_enable,
.enable_reg = MXC_CCM_CGR2,
.enable_shift = MXC_CCM_CGR2_FIRI_OFFSET,
@@ -830,7 +912,7 @@ static struct clk mbx_clk = {
.enable = _clk_enable,
.enable_reg = MXC_CCM_CGR2,
.enable_shift = MXC_CCM_CGR2_GACC_OFFSET,
- .get_rate = _clk_mbx_get_rate,
+ .recalc = _clk_mbx_recalc,
};
static struct clk vpu_clk = {
@@ -839,7 +921,7 @@ static struct clk vpu_clk = {
.enable = _clk_enable,
.enable_reg = MXC_CCM_CGR2,
.enable_shift = MXC_CCM_CGR2_GACC_OFFSET,
- .get_rate = _clk_mbx_get_rate,
+ .recalc = _clk_mbx_recalc,
};
static struct clk rtic_clk = {
@@ -896,7 +978,7 @@ static struct clk mstick_clk[] = {
.name = "mstick_clk",
.id = 0,
.parent = &usb_pll_clk,
- .get_rate = _clk_mstick1_get_rate,
+ .recalc = _clk_mstick1_recalc,
.enable = _clk_enable,
.enable_reg = MXC_CCM_CGR1,
.enable_shift = MXC_CCM_CGR1_MEMSTICK1_OFFSET,
@@ -905,7 +987,7 @@ static struct clk mstick_clk[] = {
.name = "mstick_clk",
.id = 1,
.parent = &usb_pll_clk,
- .get_rate = _clk_mstick2_get_rate,
+ .recalc = _clk_mstick2_recalc,
.enable = _clk_enable,
.enable_reg = MXC_CCM_CGR1,
.enable_shift = MXC_CCM_CGR1_MEMSTICK2_OFFSET,
@@ -965,14 +1047,14 @@ static int _clk_cko1_set_rate(struct clk *clk, unsigned long rate)
return 0;
}
-static unsigned long _clk_cko1_get_rate(struct clk *clk)
+static void _clk_cko1_recalc(struct clk *clk)
{
u32 div;
div = __raw_readl(MXC_CCM_COSR) & MXC_CCM_COSR_CLKOUTDIV_MASK >>
MXC_CCM_COSR_CLKOUTDIV_OFFSET;
- return clk_get_rate(clk->parent) / (1 << div);
+ clk->rate = clk->parent->rate / (1 << div);
}
static int _clk_cko1_set_parent(struct clk *clk, struct clk *parent)
@@ -991,6 +1073,8 @@ static int _clk_cko1_set_parent(struct clk *clk, struct clk *parent)
reg |= 3 << MXC_CCM_COSR_CLKOSEL_OFFSET;
else if (parent == &ahb_clk)
reg |= 5 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &cpu_clk)
+ reg |= 6 << MXC_CCM_COSR_CLKOSEL_OFFSET;
else if (parent == &serial_pll_clk)
reg |= 7 << MXC_CCM_COSR_CLKOSEL_OFFSET;
else if (parent == &ckih_clk)
@@ -1031,7 +1115,7 @@ static void _clk_cko1_disable(struct clk *clk)
static struct clk cko1_clk = {
.name = "cko1_clk",
- .get_rate = _clk_cko1_get_rate,
+ .recalc = _clk_cko1_recalc,
.set_rate = _clk_cko1_set_rate,
.round_rate = _clk_cko1_round_rate,
.set_parent = _clk_cko1_set_parent,
@@ -1046,6 +1130,7 @@ static struct clk *mxc_clks[] = {
&usb_pll_clk,
&serial_pll_clk,
&mcu_main_clk,
+ &cpu_clk,
&ahb_clk,
&per_clk,
&perclk_clk,
@@ -1092,12 +1177,20 @@ static struct clk *mxc_clks[] = {
&iim_clk,
};
-int __init mxc_clocks_init(unsigned long fref)
+static int cpu_curr_wp;
+static struct cpu_wp *cpu_wp_tbl;
+
+static int cpu_wp_nr;
+
+extern void propagate_rate(struct clk *tclk);
+
+int __init mxc_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2)
{
u32 reg;
struct clk **clkp;
- ckih_rate = fref;
+ ckil_clk.rate = ckil;
+ ckih_clk.rate = ckih1;
for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++)
clk_register(*clkp);
@@ -1110,24 +1203,33 @@ int __init mxc_clocks_init(unsigned long fref)
clk_register(&vl2cc_clk);
}
+ /* CCMR stby control */
+ reg = __raw_readl(MXC_CCM_CCMR);
+ reg |= MXC_CCM_CCMR_VSTBY | MXC_CCM_CCMR_WAMO;
+ __raw_writel(reg, MXC_CCM_CCMR);
+
/* Turn off all possible clocks */
__raw_writel(MXC_CCM_CGR0_GPT_MASK, MXC_CCM_CGR0);
__raw_writel(0, MXC_CCM_CGR1);
- __raw_writel(MXC_CCM_CGR2_EMI_MASK |
- MXC_CCM_CGR2_IPMUX1_MASK |
- MXC_CCM_CGR2_IPMUX2_MASK |
- MXC_CCM_CGR2_MXCCLKENSEL_MASK | /* for MX32 */
- MXC_CCM_CGR2_CHIKCAMPEN_MASK | /* for MX32 */
- MXC_CCM_CGR2_OVRVPUBUSY_MASK | /* for MX32 */
- 1 << 27 | 1 << 28, /* Bit 27 and 28 are not defined for
- MX32, but still required to be set */
- MXC_CCM_CGR2);
+ reg = MXC_CCM_CGR2_EMI_MASK | /*For MX32 */
+ MXC_CCM_CGR2_IPMUX1_MASK | /*For MX32 */
+ MXC_CCM_CGR2_IPMUX2_MASK | /*For MX32 */
+ MXC_CCM_CGR2_MXCCLKENSEL_MASK | /*For MX32 */
+ MXC_CCM_CGR2_CHIKCAMPEN_MASK | /*For MX32 */
+ MXC_CCM_CGR2_OVRVPUBUSY_MASK | /*For MX32 */
+ 0x3 << 27 | /*Bit 27 and 28 are not defined for MX32,
+ but still requires to be set */
+ MXC_CCM_CGR2_APMSYSCLKSEL_MASK | MXC_CCM_CGR2_AOMENA_MASK;
+ __raw_writel(reg, MXC_CCM_CGR2);
clk_disable(&cko1_clk);
clk_disable(&usb_pll_clk);
- pr_info("Clock input source is %ld\n", clk_get_rate(&ckih_clk));
+ pr_info("Clock input source is %ld\n", ckih_clk.rate);
+
+ /* This will propagate to all children and init all the clock rates */
+ propagate_rate(&ckih_clk);
clk_enable(&gpt_clk);
clk_enable(&emi_clk);
@@ -1135,13 +1237,171 @@ int __init mxc_clocks_init(unsigned long fref)
clk_enable(&serial_pll_clk);
- if (mx31_revision() >= CHIP_REV_2_0) {
- reg = __raw_readl(MXC_CCM_PMCR1);
- /* No PLL restart on DVFS switch; enable auto EMI handshake */
- reg |= MXC_CCM_PMCR1_PLLRDIS | MXC_CCM_PMCR1_EMIRQ_EN;
- __raw_writel(reg, MXC_CCM_PMCR1);
+ cpu_curr_wp = cpu_clk.rate / ahb_clk.rate - 1;
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+
+ /* Init serial PLL according */
+ clk_set_rate(&serial_pll_clk, (cpu_wp_tbl[2].pll_rate));
+
+ if (cpu_is_mx31_rev(CHIP_REV_2_0) < 0) {
+ /* replace 399MHz wp with 266MHz one */
+ memcpy(&cpu_wp_tbl[2], &cpu_wp_tbl[1], sizeof(cpu_wp_tbl[0]));
}
return 0;
}
+#define MXC_PMCR0_DVFS_MASK (MXC_CCM_PMCR0_DVSUP_MASK | \
+ MXC_CCM_PMCR0_UDSC_MASK | \
+ MXC_CCM_PMCR0_VSCNT_MASK | \
+ MXC_CCM_PMCR0_DPVCR)
+
+#define MXC_PDR0_MAX_MCU_MASK (MXC_CCM_PDR0_MAX_PODF_MASK | \
+ MXC_CCM_PDR0_MCU_PODF_MASK | \
+ MXC_CCM_PDR0_HSP_PODF_MASK | \
+ MXC_CCM_PDR0_IPG_PODF_MASK | \
+ MXC_CCM_PDR0_NFC_PODF_MASK)
+
+static DEFINE_SPINLOCK(mxc_dfs_lock);
+
+static void dptcen_after_timeout(unsigned long ptr)
+{
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&mxc_dfs_lock, flags);
+
+ /*
+ * If DPTC is still active and core is running in Turbo mode
+ */
+ if (dptcen_timer.data == cpu_wp_nr - 1) {
+ dptc_resume(DPTC_GP_ID);
+ }
+ spin_unlock_irqrestore(&mxc_dfs_lock, flags);
+}
+
+/*!
+ * Setup cpu clock based on working point.
+ * @param wp cpu freq working point (0 is the slowest)
+ * @return 0 on success or error code on failure.
+ */
+static int cpu_clk_set_wp(int wp)
+{
+ struct cpu_wp *p;
+ u32 dvsup;
+ u32 pmcr0, pmcr1;
+ u32 pdr0;
+ u32 cgr2 = 0x80000000;
+ u32 vscnt = MXC_CCM_PMCR0_VSCNT_2;
+ u32 udsc = MXC_CCM_PMCR0_UDSC_DOWN;
+ u32 ipu_base = IO_ADDRESS(IPU_CTRL_BASE_ADDR);
+ u32 ipu_conf;
+
+ if (wp >= cpu_wp_nr || wp < 0) {
+ printk(KERN_ERR "Wrong wp: %d for cpu_clk_set_wp\n", wp);
+ return -EINVAL;
+ }
+ if (wp == cpu_curr_wp) {
+ return 0;
+ }
+
+ pmcr0 = __raw_readl(MXC_CCM_PMCR0);
+ pmcr1 = __raw_readl(MXC_CCM_PMCR1);
+ pdr0 = __raw_readl(MXC_CCM_PDR0);
+
+ if (!(pmcr0 & MXC_CCM_PMCR0_UPDTEN)) {
+ return -EBUSY;
+ }
+
+ if (wp > cpu_curr_wp) {
+ /* going faster */
+ if (wp == (cpu_wp_nr - 1)) {
+ /* Only update vscnt going into Turbo */
+ vscnt = MXC_CCM_PMCR0_VSCNT_8;
+ }
+ udsc = MXC_CCM_PMCR0_UDSC_UP;
+ }
+
+ p = &cpu_wp_tbl[wp];
+
+ dvsup = (cpu_wp_nr - 1 - wp) << MXC_CCM_PMCR0_DVSUP_OFFSET;
+
+ if ((mcu_main_clk.rate == 399000000) && (p->cpu_rate == 532000000)) {
+ cgr2 = __raw_readl(MXC_CCM_CGR2);
+ cgr2 &= 0x7fffffff;
+ vscnt = 0;
+ pmcr0 = (pmcr0 & ~MXC_PMCR0_DVFS_MASK) | dvsup | vscnt;
+ pr_debug("manul dvfs, dvsup = %x\n", dvsup);
+ __raw_writel(cgr2, MXC_CCM_CGR2);
+ __raw_writel(pmcr0, MXC_CCM_PMCR0);
+ udelay(100);
+ }
+
+ if (mcu_main_clk.rate == p->pll_rate) {
+ /* No pll switching and relocking needed */
+ pmcr0 |= MXC_CCM_PMCR0_DFSUP0_PDR;
+ } else {
+ /* pll switching and relocking needed */
+ pmcr0 ^= MXC_CCM_PMCR0_DFSUP1; /* flip MSB bit */
+ pmcr0 &= ~(MXC_CCM_PMCR0_DFSUP0);
+ }
+
+ pmcr0 = (pmcr0 & ~MXC_PMCR0_DVFS_MASK) | dvsup | vscnt | udsc;
+ /* also enable DVFS hardware */
+ pmcr0 |= MXC_CCM_PMCR0_DVFEN;
+
+ __raw_writel(pmcr0, MXC_CCM_PMCR0);
+
+ /* IPU and DI submodule must be on for PDR0 update to take effect */
+ if (!clk_get_usecount(&ipu_clk))
+ ipu_clk.enable(&ipu_clk);
+ ipu_conf = __raw_readl(ipu_base);
+ if (!(ipu_conf & 0x40))
+ __raw_writel(ipu_conf | 0x40, ipu_base);
+
+ __raw_writel((pdr0 & ~MXC_PDR0_MAX_MCU_MASK) | p->pdr0_reg,
+ MXC_CCM_PDR0);
+
+ if ((pmcr0 & MXC_CCM_PMCR0_DFSUP0) == MXC_CCM_PMCR0_DFSUP0_PLL) {
+ /* prevent pll restart */
+ pmcr1 |= 0x80;
+ __raw_writel(pmcr1, MXC_CCM_PMCR1);
+ /* PLL and post divider update */
+ if ((pmcr0 & MXC_CCM_PMCR0_DFSUP1) == MXC_CCM_PMCR0_DFSUP1_SPLL) {
+ __raw_writel(p->pll_reg, MXC_CCM_SRPCTL);
+ serial_pll_clk.rate = p->pll_rate;
+ mcu_main_clk.parent = &serial_pll_clk;
+ } else {
+ __raw_writel(p->pll_reg, MXC_CCM_MPCTL);
+ mcu_pll_clk.rate = p->pll_rate;
+ mcu_main_clk.parent = &mcu_pll_clk;
+ }
+ }
+
+ if ((cgr2 & 0x80000000) == 0x0) {
+ pr_debug("start auto dvfs\n");
+ cgr2 |= 0x80000000;
+ __raw_writel(cgr2, MXC_CCM_CGR2);
+ }
+
+ mcu_main_clk.rate = p->pll_rate;
+ cpu_clk.rate = p->cpu_rate;
+
+ cpu_curr_wp = wp;
+
+ /* Restore IPU_CONF setting */
+ __raw_writel(ipu_conf, ipu_base);
+ if (!clk_get_usecount(&ipu_clk))
+ ipu_clk.disable(&ipu_clk);
+
+ if (wp == cpu_wp_nr - 1) {
+ init_timer(&dptcen_timer);
+ dptcen_timer.expires = jiffies + 2;
+ dptcen_timer.function = dptcen_after_timeout;
+ dptcen_timer.data = wp;
+ add_timer(&dptcen_timer);
+ } else {
+ dptc_suspend(DPTC_GP_ID);
+ }
+
+ return 0;
+}
diff --git a/arch/arm/mach-mx3/cpu.c b/arch/arm/mach-mx3/cpu.c
new file mode 100644
index 000000000000..e481145444da
--- /dev/null
+++ b/arch/arm/mach-mx3/cpu.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2001 Deep Blue Solutions Ltd.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/*!
+ * @file mach-mx3/cpu.c
+ *
+ * @brief This file contains the CPU initialization code.
+ *
+ * @ingroup MSL_MX31
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+#include <asm/hardware/cache-l2x0.h>
+
+/*!
+ * CPU initialization. It is called by fixup_mxc_board()
+ */
+void __init mxc_cpu_init(void)
+{
+ /* Setup Peripheral Port Remap register for AVIC */
+ asm("ldr r0, =0xC0000015 \n\
+ mcr p15, 0, r0, c15, c2, 4");
+ if (!system_rev) {
+ mxc_set_system_rev(0x31, CHIP_REV_2_0);
+ }
+}
+
+/*!
+ * Post CPU init code
+ *
+ * @return 0 always
+ */
+static int __init post_cpu_init(void)
+{
+ void *l2_base;
+ volatile unsigned long aips_reg;
+
+ /* Initialize L2 cache */
+ l2_base = ioremap(L2CC_BASE_ADDR, SZ_4K);
+ if (l2_base) {
+ l2x0_init(l2_base, 0x00030024, 0x00000000);
+ }
+
+ /*
+ * S/W workaround: Clear the off platform peripheral modules
+ * Supervisor Protect bit for SDMA to access them.
+ */
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x40));
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x44));
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x48));
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x4C));
+ aips_reg = __raw_readl(IO_ADDRESS(AIPS1_BASE_ADDR + 0x50));
+ aips_reg &= 0x00FFFFFF;
+ __raw_writel(aips_reg, IO_ADDRESS(AIPS1_BASE_ADDR + 0x50));
+
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x40));
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x44));
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x48));
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x4C));
+ aips_reg = __raw_readl(IO_ADDRESS(AIPS2_BASE_ADDR + 0x50));
+ aips_reg &= 0x00FFFFFF;
+ __raw_writel(aips_reg, IO_ADDRESS(AIPS2_BASE_ADDR + 0x50));
+
+ return 0;
+}
+
+postcore_initcall(post_cpu_init);
diff --git a/arch/arm/mach-mx3/crm_regs.h b/arch/arm/mach-mx3/crm_regs.h
index 4a0e0ede23bb..04d4d7c12086 100644
--- a/arch/arm/mach-mx3/crm_regs.h
+++ b/arch/arm/mach-mx3/crm_regs.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2008 by Sascha Hauer <kernel@pengutronix.de>
*
* This program is free software; you can redistribute it and/or
@@ -24,7 +24,7 @@
#define CKIH_CLK_FREQ_27MHZ 27000000
#define CKIL_CLK_FREQ 32768
-#define MXC_CCM_BASE IO_ADDRESS(CCM_BASE_ADDR)
+#define MXC_CCM_BASE ((char *)IO_ADDRESS(CCM_BASE_ADDR))
/* Register addresses */
#define MXC_CCM_CCMR (MXC_CCM_BASE + 0x00)
@@ -55,6 +55,7 @@
#define MXC_CCM_PDR2 (MXC_CCM_BASE + 0x64)
/* Register bit definitions */
+#define MXC_CCM_CCMR_VSTBY (1 << 28)
#define MXC_CCM_CCMR_WBEN (1 << 27)
#define MXC_CCM_CCMR_CSCS (1 << 25)
#define MXC_CCM_CCMR_PERCS (1 << 24)
@@ -66,6 +67,7 @@
#define MXC_CCM_CCMR_LPM_MASK (0x3 << 14)
#define MXC_CCM_CCMR_FIRS_OFFSET 11
#define MXC_CCM_CCMR_FIRS_MASK (0x3 << 11)
+#define MXC_CCM_CCMR_WAMO (1 << 10)
#define MXC_CCM_CCMR_UPE (1 << 9)
#define MXC_CCM_CCMR_SPE (1 << 8)
#define MXC_CCM_CCMR_MDS (1 << 7)
@@ -150,7 +152,8 @@
#define MXC_CCM_PDR1_SSI1_PODF_MASK 0x3F
/* Bit definitions for RCSR */
-#define MXC_CCM_RCSR_NF16B 0x80000000
+#define MXC_CCM_RCSR_NF16B (0x1 << 31)
+#define MXC_CCM_RCSR_NFMS (0x1 << 30)
/* Bit definitions for both MCU, USB and SR PLL control registers */
#define MXC_CCM_PCTL_BRM 0x80000000
diff --git a/arch/arm/mach-mx3/devices.c b/arch/arm/mach-mx3/devices.c
index a6bdcc07f3c9..4430ba55123f 100644
--- a/arch/arm/mach-mx3/devices.c
+++ b/arch/arm/mach-mx3/devices.c
@@ -1,147 +1,901 @@
/*
- * Copyright 2006-2007 Freescale Semiconductor, Inc. All Rights Reserved.
- * Copyright 2008 Sascha Hauer, kernel@pengutronix.de
+ * Author: MontaVista Software, Inc.
+ * <source@mvista.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.
+ * Based on the OMAP devices.c
*
- * 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.
+ * 2005 (c) MontaVista Software, Inc. 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.
+ *
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*/
-
#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
#include <linux/platform_device.h>
-#include <linux/serial.h>
-#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/pmic_external.h>
+
+#include <linux/spi/spi.h>
+
+#include <asm/mach-types.h>
#include <mach/hardware.h>
-#include <mach/imx-uart.h>
+#include <mach/pmic_power.h>
+#include <mach/spba.h>
+#include <mach/sdma.h>
+#include <mach/mxc_dptc.h>
+#include <mach/gpio.h>
+
+#include "iomux.h"
+#include "crm_regs.h"
+#include "sdma_script_code.h"
+#include "sdma_script_code_pass2.h"
+
+extern struct dptc_wp dptc_wp_allfreq_26ckih[DPTC_WP_SUPPORTED];
+extern struct dptc_wp dptc_wp_allfreq_26ckih_TO_2_0[DPTC_WP_SUPPORTED];
+extern struct dptc_wp dptc_wp_allfreq_27ckih_TO_2_0[DPTC_WP_SUPPORTED];
+/*
+ * Clock structures
+ */
+static struct clk *ckih_clk;
+
+void mxc_sdma_get_script_info(sdma_script_start_addrs * sdma_script_addr)
+{
+ if (cpu_is_mx31_rev(CHIP_REV_1_0) == 1) {
+ sdma_script_addr->mxc_sdma_app_2_mcu_addr = app_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_ap_2_ap_addr = ap_2_ap_ADDR;
+ sdma_script_addr->mxc_sdma_ap_2_bp_addr = -1;
+ sdma_script_addr->mxc_sdma_bp_2_ap_addr = -1;
+ sdma_script_addr->mxc_sdma_loopback_on_dsp_side_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_app_addr = mcu_2_app_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_shp_addr = mcu_2_shp_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_interrupt_only_addr = -1;
+ sdma_script_addr->mxc_sdma_shp_2_mcu_addr = shp_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_start_addr =
+ (unsigned short *)sdma_code;
+ sdma_script_addr->mxc_sdma_uartsh_2_mcu_addr =
+ uartsh_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_uart_2_mcu_addr = uart_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_ram_code_size = RAM_CODE_SIZE;
+ sdma_script_addr->mxc_sdma_ram_code_start_addr =
+ RAM_CODE_START_ADDR;
+ sdma_script_addr->mxc_sdma_dptc_dvfs_addr = dptc_dvfs_ADDR;
+ sdma_script_addr->mxc_sdma_firi_2_mcu_addr = firi_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_firi_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_mshc_2_mcu_addr = mshc_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_per_2_app_addr = -1;
+ sdma_script_addr->mxc_sdma_per_2_firi_addr = -1;
+ sdma_script_addr->mxc_sdma_per_2_shp_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_ata_addr = mcu_2_ata_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_firi_addr = mcu_2_firi_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_mshc_addr = mcu_2_mshc_ADDR;
+ sdma_script_addr->mxc_sdma_ata_2_mcu_addr = ata_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_uartsh_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_shp_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_uart_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_app_2_per_addr = -1;
+ } else {
+ sdma_script_addr->mxc_sdma_app_2_mcu_addr = app_2_mcu_ADDR_2;
+ sdma_script_addr->mxc_sdma_ap_2_ap_addr = ap_2_ap_ADDR_2;
+ sdma_script_addr->mxc_sdma_ap_2_ap_fixed_addr =
+ ap_2_ap_fixed_addr_ADDR_2;
+ sdma_script_addr->mxc_sdma_ap_2_bp_addr = ap_2_bp_ADDR_2;
+ sdma_script_addr->mxc_sdma_ap_2_ap_fixed_addr =
+ ap_2_ap_fixed_addr_ADDR_2;
+ sdma_script_addr->mxc_sdma_bp_2_ap_addr = bp_2_ap_ADDR_2;
+ sdma_script_addr->mxc_sdma_loopback_on_dsp_side_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_app_addr = mcu_2_app_ADDR_2;
+ sdma_script_addr->mxc_sdma_mcu_2_shp_addr = mcu_2_shp_ADDR_2;
+ sdma_script_addr->mxc_sdma_mcu_interrupt_only_addr = -1;
+ sdma_script_addr->mxc_sdma_shp_2_mcu_addr = shp_2_mcu_ADDR_2;
+ sdma_script_addr->mxc_sdma_start_addr =
+ (unsigned short *)sdma_code_2;
+ sdma_script_addr->mxc_sdma_uartsh_2_mcu_addr =
+ uartsh_2_mcu_ADDR_2;
+ sdma_script_addr->mxc_sdma_uart_2_mcu_addr = uart_2_mcu_ADDR_2;
+ sdma_script_addr->mxc_sdma_ram_code_size = RAM_CODE_SIZE_2;
+ sdma_script_addr->mxc_sdma_ram_code_start_addr =
+ RAM_CODE_START_ADDR_2;
+ sdma_script_addr->mxc_sdma_dptc_dvfs_addr = -1;
+ sdma_script_addr->mxc_sdma_firi_2_mcu_addr = firi_2_mcu_ADDR_2;
+ sdma_script_addr->mxc_sdma_firi_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_mshc_2_mcu_addr = mshc_2_mcu_ADDR_2;
+ sdma_script_addr->mxc_sdma_per_2_app_addr = per_2_app_ADDR_2;
+ sdma_script_addr->mxc_sdma_per_2_firi_addr = -1;
+ sdma_script_addr->mxc_sdma_per_2_shp_addr = per_2_shp_ADDR_2;
+ sdma_script_addr->mxc_sdma_mcu_2_ata_addr = mcu_2_ata_ADDR_2;
+ sdma_script_addr->mxc_sdma_mcu_2_firi_addr = mcu_2_firi_ADDR_2;
+ sdma_script_addr->mxc_sdma_mcu_2_mshc_addr = mcu_2_mshc_ADDR_2;
+ sdma_script_addr->mxc_sdma_ata_2_mcu_addr = ata_2_mcu_ADDR_2;
+ sdma_script_addr->mxc_sdma_uartsh_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_shp_2_per_addr = shp_2_per_ADDR_2;
+ sdma_script_addr->mxc_sdma_uart_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_app_2_per_addr = app_2_per_ADDR_2;
+ }
+}
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
-static struct resource uart0[] = {
+#if defined(CONFIG_W1_MASTER_MXC) || defined(CONFIG_W1_MASTER_MXC_MODULE)
+static struct mxc_w1_config mxc_w1_data = {
+ .search_rom_accelerator = 0,
+};
+
+static struct platform_device mxc_w1_devices = {
+ .name = "mxc_w1",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_w1_data,
+ },
+ .id = 0
+};
+
+static void mxc_init_owire(void)
+{
+ (void)platform_device_register(&mxc_w1_devices);
+}
+#else
+static inline void mxc_init_owire(void)
+{
+}
+#endif
+
+#if defined(CONFIG_RTC_MXC) || defined(CONFIG_RTC_MXC_MODULE)
+static struct resource rtc_resources[] = {
+ {
+ .start = RTC_BASE_ADDR,
+ .end = RTC_BASE_ADDR + 0x30,
+ .flags = IORESOURCE_MEM,
+ },
{
- .start = UART1_BASE_ADDR,
- .end = UART1_BASE_ADDR + 0x0B5,
- .flags = IORESOURCE_MEM,
- }, {
- .start = MXC_INT_UART1,
- .end = MXC_INT_UART1,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device mxc_uart_device0 = {
- .name = "imx-uart",
+ .start = MXC_INT_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device mxc_rtc_device = {
+ .name = "mxc_rtc",
.id = 0,
- .resource = uart0,
- .num_resources = ARRAY_SIZE(uart0),
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(rtc_resources),
+ .resource = rtc_resources,
};
+static void mxc_init_rtc(void)
+{
+ (void)platform_device_register(&mxc_rtc_device);
+}
+#else
+static inline void mxc_init_rtc(void)
+{
+}
+#endif
-static struct resource uart1[] = {
+#if defined(CONFIG_MXC_WATCHDOG) || defined(CONFIG_MXC_WATCHDOG_MODULE)
+
+static struct resource wdt_resources[] = {
{
- .start = UART2_BASE_ADDR,
- .end = UART2_BASE_ADDR + 0x0B5,
- .flags = IORESOURCE_MEM,
- }, {
- .start = MXC_INT_UART2,
- .end = MXC_INT_UART2,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device mxc_uart_device1 = {
- .name = "imx-uart",
- .id = 1,
- .resource = uart1,
- .num_resources = ARRAY_SIZE(uart1),
+ .start = WDOG1_BASE_ADDR,
+ .end = WDOG1_BASE_ADDR + 0x30,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_wdt_device = {
+ .name = "mxc_wdt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(wdt_resources),
+ .resource = wdt_resources,
};
-static struct resource uart2[] = {
+static void mxc_init_wdt(void)
+{
+ (void)platform_device_register(&mxc_wdt_device);
+}
+#else
+static inline void mxc_init_wdt(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_IPU) || defined(CONFIG_MXC_IPU_MODULE)
+static struct mxc_ipu_config mxc_ipu_data = {
+ .rev = 1,
+};
+
+static struct resource ipu_resources[] = {
{
- .start = UART3_BASE_ADDR,
- .end = UART3_BASE_ADDR + 0x0B5,
- .flags = IORESOURCE_MEM,
- }, {
- .start = MXC_INT_UART3,
- .end = MXC_INT_UART3,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device mxc_uart_device2 = {
- .name = "imx-uart",
+ .start = IPU_CTRL_BASE_ADDR,
+ .end = IPU_CTRL_BASE_ADDR + SZ_4K,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_IPU_SYN,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MXC_INT_IPU_ERR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mxc_ipu_device = {
+ .name = "mxc_ipu",
+ .id = -1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_ipu_data,
+ },
+ .num_resources = ARRAY_SIZE(ipu_resources),
+ .resource = ipu_resources,
+};
+
+static void mxc_init_ipu(void)
+{
+ platform_device_register(&mxc_ipu_device);
+}
+#else
+static inline void mxc_init_ipu(void)
+{
+}
+#endif
+
+#if defined(CONFIG_SND_MXC_PMIC) || defined(CONFIG_SND_MXC_PMIC_MODULE)
+static struct mxc_audio_platform_data mxc_audio_data;
+
+static struct platform_device mxc_alsa_device = {
+ .name = "mxc_alsa",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_audio_data,
+ },
+
+};
+
+static void mxc_init_audio(void)
+{
+ struct clk *pll_clk;
+ pll_clk = clk_get(NULL, "usb_pll");
+ mxc_audio_data.ssi_clk[0] = clk_get(NULL, "ssi_clk.0");
+ clk_set_parent(mxc_audio_data.ssi_clk[0], pll_clk);
+ clk_put(mxc_audio_data.ssi_clk[0]);
+ if (machine_is_mx31_3ds()) {
+ mxc_audio_data.ssi_num = 1;
+ } else {
+ mxc_audio_data.ssi_num = 2;
+ mxc_audio_data.ssi_clk[1] = clk_get(NULL, "ssi_clk.1");
+ clk_set_parent(mxc_audio_data.ssi_clk[1], pll_clk);
+ clk_put(mxc_audio_data.ssi_clk[1]);
+ }
+ clk_put(pll_clk);
+ mxc_audio_data.src_port = 0;
+ platform_device_register(&mxc_alsa_device);
+}
+#else
+
+static void mxc_init_audio(void)
+{
+}
+
+#endif
+
+#if defined(CONFIG_MXC_SSI) || defined(CONFIG_MXC_SSI_MODULE)
+/*!
+ * Resource definition for the SSI
+ */
+static struct resource mxcssi2_resources[] = {
+ [0] = {
+ .start = SSI2_BASE_ADDR,
+ .end = SSI2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource mxcssi1_resources[] = {
+ [0] = {
+ .start = SSI1_BASE_ADDR,
+ .end = SSI1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+/*! Device Definition for MXC SSI */
+static struct platform_device mxc_ssi1_device = {
+ .name = "mxc_ssi",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_audio_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcssi1_resources),
+ .resource = mxcssi1_resources,
+};
+
+static struct platform_device mxc_ssi2_device = {
+ .name = "mxc_ssi",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_audio_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcssi2_resources),
+ .resource = mxcssi2_resources,
+};
+
+static void mxc_init_ssi(void)
+{
+ platform_device_register(&mxc_ssi1_device);
+ platform_device_register(&mxc_ssi2_device);
+}
+#else
+
+static void mxc_init_ssi(void)
+{
+}
+#endif
+
+/*!
+ * This is platform device structure for adding SCC
+ */
+#if defined(CONFIG_MXC_SECURITY_SCC) || defined(CONFIG_MXC_SECURITY_SCC_MODULE)
+static struct platform_device mxc_scc_device = {
+ .name = "mxc_scc",
+ .id = 0,
+};
+
+static void mxc_init_scc(void)
+{
+ platform_device_register(&mxc_scc_device);
+}
+#else
+static inline void mxc_init_scc(void)
+{
+}
+#endif
+
+/* SPI controller and device data */
+#if defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE)
+
+#ifdef CONFIG_SPI_MXC_SELECT1
+/*!
+ * Resource definition for the CSPI1
+ */
+static struct resource mxcspi1_resources[] = {
+ [0] = {
+ .start = CSPI1_BASE_ADDR,
+ .end = CSPI1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI1,
+ .end = MXC_INT_CSPI1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI1 */
+static struct mxc_spi_master mxcspi1_data = {
+ .maxchipselect = 4,
+ .spi_version = 4,
+};
+
+/*! Device Definition for MXC CSPI1 */
+static struct platform_device mxcspi1_device = {
+ .name = "mxc_spi",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi1_resources),
+ .resource = mxcspi1_resources,
+};
+
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+
+#ifdef CONFIG_SPI_MXC_SELECT2
+/*!
+ * Resource definition for the CSPI2
+ */
+static struct resource mxcspi2_resources[] = {
+ [0] = {
+ .start = CSPI2_BASE_ADDR,
+ .end = CSPI2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI2,
+ .end = MXC_INT_CSPI2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI2 */
+static struct mxc_spi_master mxcspi2_data = {
+ .maxchipselect = 4,
+ .spi_version = 4,
+};
+
+/*! Device Definition for MXC CSPI2 */
+static struct platform_device mxcspi2_device = {
+ .name = "mxc_spi",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi2_resources),
+ .resource = mxcspi2_resources,
+};
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+
+#ifdef CONFIG_SPI_MXC_SELECT3
+/*!
+ * Resource definition for the CSPI3
+ */
+static struct resource mxcspi3_resources[] = {
+ [0] = {
+ .start = CSPI3_BASE_ADDR,
+ .end = CSPI3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI3,
+ .end = MXC_INT_CSPI3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI3 */
+static struct mxc_spi_master mxcspi3_data = {
+ .maxchipselect = 4,
+ .spi_version = 4,
+};
+
+/*! Device Definition for MXC CSPI3 */
+static struct platform_device mxcspi3_device = {
+ .name = "mxc_spi",
.id = 2,
- .resource = uart2,
- .num_resources = ARRAY_SIZE(uart2),
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi3_resources),
+ .resource = mxcspi3_resources,
};
+#endif /* CONFIG_SPI_MXC_SELECT3 */
-static struct resource uart3[] = {
- {
- .start = UART4_BASE_ADDR,
- .end = UART4_BASE_ADDR + 0x0B5,
- .flags = IORESOURCE_MEM,
- }, {
- .start = MXC_INT_UART4,
- .end = MXC_INT_UART4,
- .flags = IORESOURCE_IRQ,
- },
+static inline void mxc_init_spi(void)
+{
+ /* SPBA configuration for CSPI2 - MCU is set */
+ spba_take_ownership(SPBA_CSPI2, SPBA_MASTER_A);
+#ifdef CONFIG_SPI_MXC_SELECT1
+ if (platform_device_register(&mxcspi1_device) < 0)
+ printk("Error: Registering the SPI Controller_1\n");
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+#ifdef CONFIG_SPI_MXC_SELECT2
+ if (platform_device_register(&mxcspi2_device) < 0)
+ printk("Error: Registering the SPI Controller_2\n");
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+#ifdef CONFIG_SPI_MXC_SELECT3
+ if (platform_device_register(&mxcspi3_device) < 0)
+ printk("Error: Registering the SPI Controller_3\n");
+#endif /* CONFIG_SPI_MXC_SELECT3 */
+}
+#else
+static inline void mxc_init_spi(void)
+{
+}
+#endif
+
+/* I2C controller and device data */
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+/*!
+ * Resource definition for the I2C1
+ */
+static struct resource mxci2c1_resources[] = {
+ [0] = {
+ .start = I2C_BASE_ADDR,
+ .end = I2C_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C,
+ .end = MXC_INT_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
};
-struct platform_device mxc_uart_device3 = {
- .name = "imx-uart",
- .id = 3,
- .resource = uart3,
- .num_resources = ARRAY_SIZE(uart3),
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c1_data = {
+ .i2c_clk = 100000,
};
+#endif
-static struct resource uart4[] = {
- {
- .start = UART5_BASE_ADDR,
- .end = UART5_BASE_ADDR + 0x0B5,
- .flags = IORESOURCE_MEM,
- }, {
- .start = MXC_INT_UART5,
- .end = MXC_INT_UART5,
- .flags = IORESOURCE_IRQ,
- },
+#ifdef CONFIG_I2C_MXC_SELECT2
+/*!
+ * Resource definition for the I2C2
+ */
+static struct resource mxci2c2_resources[] = {
+ [0] = {
+ .start = I2C2_BASE_ADDR,
+ .end = I2C2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C2,
+ .end = MXC_INT_I2C2,
+ .flags = IORESOURCE_IRQ,
+ },
};
-struct platform_device mxc_uart_device4 = {
- .name = "imx-uart",
- .id = 4,
- .resource = uart4,
- .num_resources = ARRAY_SIZE(uart4),
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c2_data = {
+ .i2c_clk = 100000,
};
+#endif
-/* GPIO port description */
-static struct mxc_gpio_port imx_gpio_ports[] = {
+#ifdef CONFIG_I2C_MXC_SELECT3
+/*!
+ * Resource definition for the I2C3
+ */
+static struct resource mxci2c3_resources[] = {
[0] = {
- .chip.label = "gpio-0",
- .base = IO_ADDRESS(GPIO1_BASE_ADDR),
- .irq = MXC_INT_GPIO1,
- .virtual_irq_start = MXC_GPIO_INT_BASE
- },
+ .start = I2C3_BASE_ADDR,
+ .end = I2C3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
[1] = {
- .chip.label = "gpio-1",
- .base = IO_ADDRESS(GPIO2_BASE_ADDR),
- .irq = MXC_INT_GPIO2,
- .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN
- },
- [2] = {
- .chip.label = "gpio-2",
- .base = IO_ADDRESS(GPIO3_BASE_ADDR),
- .irq = MXC_INT_GPIO3,
- .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 2
+ .start = MXC_INT_I2C3,
+ .end = MXC_INT_I2C3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c3_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+/*! Device Definition for MXC I2C1 */
+static struct platform_device mxci2c_devices[] = {
+#ifdef CONFIG_I2C_MXC_SELECT1
+ {
+ .name = "mxc_i2c",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c1_resources),
+ .resource = mxci2c1_resources,},
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT2
+ {
+ .name = "mxc_i2c",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c2_resources),
+ .resource = mxci2c2_resources,},
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT3
+ {
+ .name = "mxc_i2c",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c3_resources),
+ .resource = mxci2c3_resources,},
+#endif
+};
+
+static inline void mxc_init_i2c(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxci2c_devices); i++) {
+ if (platform_device_register(&mxci2c_devices[i]) < 0)
+ dev_err(&mxci2c_devices[i].dev,
+ "Unable to register I2C device\n");
}
+}
+#else
+static inline void mxc_init_i2c(void)
+{
+}
+#endif
+
+struct mxc_gpio_port mxc_gpio_ports[GPIO_PORT_NUM] = {
+ [0] = {
+ .chip.label = "gpio-0",
+ .base = IO_ADDRESS(GPIO1_BASE_ADDR),
+ .irq = MXC_INT_GPIO1,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE
+ },
+ [1] = {
+ .chip.label = "gpio-1",
+ .base = IO_ADDRESS(GPIO2_BASE_ADDR),
+ .irq = MXC_INT_GPIO2,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN
+ },
+ [2] = {
+ .chip.label = "gpio-2",
+ .base = IO_ADDRESS(GPIO3_BASE_ADDR),
+ .irq = MXC_INT_GPIO3,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 2
+ }
};
int __init mxc_register_gpios(void)
{
- return mxc_gpio_init(imx_gpio_ports, ARRAY_SIZE(imx_gpio_ports));
+ return mxc_gpio_init(mxc_gpio_ports, ARRAY_SIZE(mxc_gpio_ports));
+}
+
+#if defined(CONFIG_PCMCIA_MX31ADS) || defined(CONFIG_PCMCIA_MX31ADS_MODULE)
+
+static struct platform_device mx31ads_device = {
+ .name = "Mx31ads_pcmcia_socket",
+ .id = 0,
+ .dev.release = mxc_nop_release,
+};
+static inline void mxc_init_pcmcia(void)
+{
+ platform_device_register(&mx31ads_device);
+}
+#else
+static inline void mxc_init_pcmcia(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_HMP4E) || defined(CONFIG_MXC_HMP4E_MODULE)
+static struct platform_device hmp4e_device = {
+ .name = "mxc_hmp4e",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ }
+};
+
+static inline void mxc_init_hmp4e(void)
+{
+ u32 iim_reg = IO_ADDRESS(IIM_BASE_ADDR);
+ if (cpu_is_mx32())
+ return;
+
+ /* override fuse for Hantro HW clock */
+ if (__raw_readl(iim_reg + 0x808) == 0x4) {
+ if (!(__raw_readl(iim_reg + 0x800) & (1 << 5))) {
+ writel(__raw_readl(iim_reg + 0x808) & 0xfffffffb,
+ iim_reg + 0x808);
+ }
+ }
+
+ platform_device_register(&hmp4e_device);
+}
+#else
+static inline void mxc_init_hmp4e(void)
+{
+}
+#endif
+
+static struct platform_device mxc_dma_device = {
+ .name = "mxc_dma",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline void mxc_init_dma(void)
+{
+ (void)platform_device_register(&mxc_dma_device);
+}
+
+/*!
+ * Resource definition for the DPTC LP
+ */
+static struct resource dptc_resources[] = {
+ [0] = {
+ .start = CCM_BASE_ADDR,
+ .end = CCM_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CCM,
+ .end = MXC_INT_CCM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for DPTC */
+static struct mxc_dptc_data dptc_data = {
+ .reg_id = "SW1A",
+ .clk_id = "cpu_clk",
+ .dptccr_reg_addr = (unsigned int)MXC_CCM_PMCR0,
+ .dcvr0_reg_addr = (int)MXC_CCM_DCVR0,
+ .gpc_cntr_reg_addr = (int)MXC_CCM_PMCR0,
+ .dptccr = 0xFFFFFFFF,
+ .dptc_wp_supported = DPTC_WP_SUPPORTED,
+ .dptc_wp_allfreq = dptc_wp_allfreq_26ckih,
+ .clk_max_val = 532000000,
+ .gpc_adu = 0x0,
+ .vai_mask = MXC_CCM_PMCR0_PTVAI_MASK,
+ .vai_offset = MXC_CCM_PMCR0_PTVAI_OFFSET,
+ .dptc_enable_bit = MXC_CCM_PMCR0_DPTEN,
+ .irq_mask = MXC_CCM_PMCR0_PTVAIM,
+ .dptc_nvcr_bit = 0x0,
+ .gpc_irq_bit = 0x00000000,
+ .init_config =
+ MXC_CCM_PMCR0_PTVIS | MXC_CCM_PMCR0_DRCE3 | MXC_CCM_PMCR0_DRCE1,
+ .enable_config =
+ MXC_CCM_PMCR0_DPTEN | MXC_CCM_PMCR0_DPVCR | MXC_CCM_PMCR0_DPVV,
+ .dcr_mask = MXC_CCM_PMCR0_DCR,
+};
+
+/*! Device Definition for MXC DPTC */
+static struct platform_device mxc_dptc_device = {
+ .name = "mxc_dptc",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &dptc_data,
+ },
+ .num_resources = ARRAY_SIZE(dptc_resources),
+ .resource = dptc_resources,
+};
+
+static inline void mxc_init_dptc(void)
+{
+ if (clk_get_rate(ckih_clk) == 27000000) {
+
+ if (mxc_cpu_is_rev(CHIP_REV_2_0) < 0)
+ dptc_data.dptc_wp_allfreq = NULL;
+ else
+ dptc_data.dptc_wp_allfreq =
+ dptc_wp_allfreq_27ckih_TO_2_0;
+
+ } else if (clk_get_rate(ckih_clk) == 26000000
+ && mxc_cpu_is_rev(CHIP_REV_2_0) == 1) {
+ dptc_data.dptc_wp_allfreq = dptc_wp_allfreq_26ckih_TO_2_0;
+ }
+
+ (void)platform_device_register(&mxc_dptc_device);
+}
+
+#ifdef CONFIG_MXC_VPU
+static struct resource vpu_resources[] = {
+ {
+ .start = VL2CC_BASE_ADDR,
+ .end = VL2CC_BASE_ADDR + SZ_8K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+/*! Platform Data for MXC VPU */
+static struct platform_device mxcvpu_device = {
+ .name = "mxc_vpu",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(vpu_resources),
+ .resource = vpu_resources,
+};
+
+static inline void mxc_init_vpu(void)
+{
+ if (cpu_is_mx32()) {
+ if (platform_device_register(&mxcvpu_device) < 0)
+ printk(KERN_ERR "Error: Registering the VPU.\n");
+ }
+}
+#else
+static inline void mxc_init_vpu(void)
+{
+}
+#endif
+
+#if defined(CONFIG_HW_RANDOM_FSL_RNGA) || \
+defined(CONFIG_HW_RANDOM_FSL_RNGA_MODULE)
+static struct resource rnga_resources[] = {
+ {
+ .start = RNGA_BASE_ADDR,
+ .end = RNGA_BASE_ADDR + 0x28,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device fsl_rnga_device = {
+ .name = "fsl_rnga",
+ .id = -1,
+ .num_resources = 1,
+ .resource = rnga_resources,
+};
+
+static inline void mxc_init_rnga(void)
+{
+ platform_device_register(&fsl_rnga_device);
+}
+#else
+static inline void mxc_init_rnga(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_IIM) || defined(CONFIG_MXC_IIM_MODULE)
+static struct resource mxc_iim_resources[] = {
+ {
+ .start = IIM_BASE_ADDR,
+ .end = IIM_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_iim_device = {
+ .name = "mxc_iim",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(mxc_iim_resources),
+ .resource = mxc_iim_resources
+};
+
+static inline void mxc_init_iim(void)
+{
+ if (platform_device_register(&mxc_iim_device) < 0)
+ dev_err(&mxc_iim_device.dev,
+ "Unable to register mxc iim device\n");
+}
+#else
+static inline void mxc_init_iim(void)
+{
+}
+#endif
+
+int __init mxc_init_devices(void)
+{
+ mxc_init_wdt();
+ mxc_init_ipu();
+ mxc_init_spi();
+ mxc_init_i2c();
+ mxc_init_rtc();
+ mxc_init_owire();
+ mxc_init_pcmcia();
+ mxc_init_scc();
+ mxc_init_ssi();
+ mxc_init_hmp4e();
+ mxc_init_dma();
+ mxc_init_audio();
+ ckih_clk = clk_get(NULL, "ckih");
+ mxc_init_dptc();
+ mxc_init_vpu();
+ mxc_init_rnga();
+ mxc_init_iim();
+
+ /* SPBA configuration for SSI2 - SDMA and MCU are set */
+ spba_take_ownership(SPBA_SSI2, SPBA_MASTER_C | SPBA_MASTER_A);
+ return 0;
}
diff --git a/arch/arm/mach-mx3/dma.c b/arch/arm/mach-mx3/dma.c
new file mode 100644
index 000000000000..4d63d706cba4
--- /dev/null
+++ b/arch/arm/mach-mx3/dma.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/device.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+
+#include "serial.h"
+
+#define MXC_MMC_BUFFER_ACCESS 0x38
+#define MXC_SSI_TX0_REG 0x0
+#define MXC_SSI_TX1_REG 0x4
+#define MXC_SSI_RX0_REG 0x8
+#define MXC_SSI_RX1_REG 0xC
+#define MXC_FIRI_TXFIFO 0x14
+#define MXC_SDHC_MMC_WML 16
+#define MXC_SDHC_SD_WML 64
+#define MXC_SSI_TXFIFO_WML 0x4
+#define MXC_SSI_RXFIFO_WML 0x6
+#define MXC_FIRI_WML 16
+
+#ifdef CONFIG_SDMA_IRAM
+#define trans_type int_2_per
+#else
+#define trans_type emi_2_per
+#endif
+
+typedef struct mxc_sdma_info_entry_s {
+ mxc_dma_device_t device;
+ mxc_sdma_channel_params_t *chnl_info;
+} mxc_sdma_info_entry_t;
+
+static mxc_sdma_channel_params_t mxc_sdma_uart1_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART1_UFCR_RXTL,
+ .per_address = UART1_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART1_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART1_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart1_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART1_UFCR_TXTL,
+ .per_address = UART1_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART1_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART1_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart2_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART2_UFCR_RXTL,
+ .per_address = UART2_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART2_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART2_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart2_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART2_UFCR_TXTL,
+ .per_address = UART2_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART2_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART2_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart3_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART3_UFCR_RXTL,
+ .per_address = UART3_BASE_ADDR,
+ .peripheral_type = UART_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART3_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART3_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart3_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART3_UFCR_TXTL,
+ .per_address = UART3_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART3_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART3_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart4_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART4_UFCR_RXTL,
+ .per_address = UART4_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART4_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART4_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart4_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART4_UFCR_TXTL,
+ .per_address = UART4_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART4_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART4_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart5_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART5_UFCR_RXTL,
+ .per_address = UART5_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART5_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART5_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart5_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART5_UFCR_TXTL,
+ .per_address = UART5_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART5_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART5_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc1_width1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_MMC_WML,
+ .per_address =
+ MMC_SDHC1_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC1,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc1_width4_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_SD_WML,
+ .per_address =
+ MMC_SDHC1_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC1,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc2_width1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_MMC_WML,
+ .per_address =
+ MMC_SDHC2_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC2,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc2_width4_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_SD_WML,
+ .per_address =
+ MMC_SDHC2_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC2,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = trans_type,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = trans_type,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = trans_type,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = trans_type,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = trans_type,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_fir_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_FIRI_WML,
+ .per_address = FIRI_BASE_ADDR,
+ .peripheral_type = FIRI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_FIRI_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_FIR_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_fir_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_FIRI_WML,
+ .per_address = FIRI_BASE_ADDR + MXC_FIRI_TXFIFO,
+ .peripheral_type = FIRI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_FIRI_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_FIR_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_memory_params = {
+ .chnl_params = {
+ .peripheral_type = MEMORY,
+ .transfer_type = emi_2_emi,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MEMORY,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_fifo_memory_params = {
+ .chnl_params = {
+ .peripheral_type = FIFO_MEMORY,
+ .per_address = MXC_FIFO_MEM_DEST_FIXED,
+ .transfer_type = emi_2_emi,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .event_id = 0,
+ },
+ .channel_num = MXC_DMA_CHANNEL_FIFO_MEMORY,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ata_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_IDE_DMA_WATERMARK,
+ .per_address = ATA_DMA_BASE_ADDR,
+ .peripheral_type = ATA,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_ATA_TX_END,
+ .event_id2 = DMA_REQ_ATA_RX,
+ .bd_number = MXC_IDE_DMA_BD_NR,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ATA_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ata_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_IDE_DMA_WATERMARK,
+ .per_address = ATA_DMA_BASE_ADDR + 0x18,
+ .peripheral_type = ATA,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_ATA_TX_END,
+ .event_id2 = DMA_REQ_ATA_TX,
+ .bd_number = MXC_IDE_DMA_BD_NR,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ATA_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+static mxc_sdma_info_entry_t mxc_sdma_active_dma_info[] = {
+ {MXC_DMA_UART1_RX, &mxc_sdma_uart1_rx_params},
+ {MXC_DMA_UART1_TX, &mxc_sdma_uart1_tx_params},
+ {MXC_DMA_UART2_RX, &mxc_sdma_uart2_rx_params},
+ {MXC_DMA_UART2_TX, &mxc_sdma_uart2_tx_params},
+ {MXC_DMA_UART3_RX, &mxc_sdma_uart3_rx_params},
+ {MXC_DMA_UART3_TX, &mxc_sdma_uart3_tx_params},
+ {MXC_DMA_UART4_RX, &mxc_sdma_uart4_rx_params},
+ {MXC_DMA_UART4_TX, &mxc_sdma_uart4_tx_params},
+ {MXC_DMA_UART5_RX, &mxc_sdma_uart5_rx_params},
+ {MXC_DMA_UART5_TX, &mxc_sdma_uart5_tx_params},
+ {MXC_DMA_MMC1_WIDTH_1, &mxc_sdma_mmc1_width1_params},
+ {MXC_DMA_MMC1_WIDTH_4, &mxc_sdma_mmc1_width4_params},
+ {MXC_DMA_MMC2_WIDTH_1, &mxc_sdma_mmc2_width1_params},
+ {MXC_DMA_MMC2_WIDTH_4, &mxc_sdma_mmc2_width4_params},
+ {MXC_DMA_SSI1_8BIT_RX0, &mxc_sdma_ssi1_8bit_rx0_params},
+ {MXC_DMA_SSI1_8BIT_TX0, &mxc_sdma_ssi1_8bit_tx0_params},
+ {MXC_DMA_SSI1_16BIT_RX0, &mxc_sdma_ssi1_16bit_rx0_params},
+ {MXC_DMA_SSI1_16BIT_TX0, &mxc_sdma_ssi1_16bit_tx0_params},
+ {MXC_DMA_SSI1_24BIT_RX0, &mxc_sdma_ssi1_24bit_rx0_params},
+ {MXC_DMA_SSI1_24BIT_TX0, &mxc_sdma_ssi1_24bit_tx0_params},
+ {MXC_DMA_SSI1_8BIT_RX1, &mxc_sdma_ssi1_8bit_rx1_params},
+ {MXC_DMA_SSI1_8BIT_TX1, &mxc_sdma_ssi1_8bit_tx1_params},
+ {MXC_DMA_SSI1_16BIT_RX1, &mxc_sdma_ssi1_16bit_rx1_params},
+ {MXC_DMA_SSI1_16BIT_TX1, &mxc_sdma_ssi1_16bit_tx1_params},
+ {MXC_DMA_SSI1_24BIT_RX1, &mxc_sdma_ssi1_24bit_rx1_params},
+ {MXC_DMA_SSI1_24BIT_TX1, &mxc_sdma_ssi1_24bit_tx1_params},
+ {MXC_DMA_SSI2_8BIT_RX0, &mxc_sdma_ssi2_8bit_rx0_params},
+ {MXC_DMA_SSI2_8BIT_TX0, &mxc_sdma_ssi2_8bit_tx0_params},
+ {MXC_DMA_SSI2_16BIT_RX0, &mxc_sdma_ssi2_16bit_rx0_params},
+ {MXC_DMA_SSI2_16BIT_TX0, &mxc_sdma_ssi2_16bit_tx0_params},
+ {MXC_DMA_SSI2_24BIT_RX0, &mxc_sdma_ssi2_24bit_rx0_params},
+ {MXC_DMA_SSI2_24BIT_TX0, &mxc_sdma_ssi2_24bit_tx0_params},
+ {MXC_DMA_SSI2_8BIT_RX1, &mxc_sdma_ssi2_8bit_rx1_params},
+ {MXC_DMA_SSI2_8BIT_TX1, &mxc_sdma_ssi2_8bit_tx1_params},
+ {MXC_DMA_SSI2_16BIT_RX1, &mxc_sdma_ssi2_16bit_rx1_params},
+ {MXC_DMA_SSI2_16BIT_TX1, &mxc_sdma_ssi2_16bit_tx1_params},
+ {MXC_DMA_SSI2_24BIT_RX1, &mxc_sdma_ssi2_24bit_rx1_params},
+ {MXC_DMA_SSI2_24BIT_TX1, &mxc_sdma_ssi2_24bit_tx1_params},
+ {MXC_DMA_FIR_RX, &mxc_sdma_fir_rx_params},
+ {MXC_DMA_FIR_TX, &mxc_sdma_fir_tx_params},
+ {MXC_DMA_MEMORY, &mxc_sdma_memory_params},
+ {MXC_DMA_FIFO_MEMORY, &mxc_sdma_fifo_memory_params},
+ {MXC_DMA_ATA_RX, &mxc_sdma_ata_rx_params},
+ {MXC_DMA_ATA_TX, &mxc_sdma_ata_tx_params},
+};
+
+static int mxc_sdma_info_entrys =
+ sizeof(mxc_sdma_active_dma_info) / sizeof(mxc_sdma_active_dma_info[0]);
+
+/*!
+ * This functions Returns the SDMA paramaters associated for a module
+ *
+ * @param channel_id the ID of the module requesting DMA
+ * @return returns the sdma parameters structure for the device
+ */
+mxc_sdma_channel_params_t *mxc_sdma_get_channel_params(mxc_dma_device_t
+ channel_id)
+{
+ mxc_sdma_info_entry_t *p = mxc_sdma_active_dma_info;
+ int i;
+
+ for (i = 0; i < mxc_sdma_info_entrys; i++, p++) {
+ if (p->device == channel_id) {
+ return p->chnl_info;
+ }
+ }
+ return NULL;
+}
+
+/*!
+ * This functions marks the SDMA channels that are statically allocated
+ *
+ * @param chnl the channel array used to store channel information
+ */
+void mxc_get_static_channels(mxc_dma_channel_t * chnl)
+{
+#ifdef CONFIG_SDMA_IRAM
+ int i;
+ for (i = MXC_DMA_CHANNEL_IRAM; i < MAX_DMA_CHANNELS; i++)
+ chnl[i].dynamic = 0;
+#endif /*CONFIG_SDMA_IRAM */
+}
+
+EXPORT_SYMBOL(mxc_sdma_get_channel_params);
+EXPORT_SYMBOL(mxc_get_static_channels);
diff --git a/arch/arm/mach-mx3/dptc.c b/arch/arm/mach-mx3/dptc.c
new file mode 100644
index 000000000000..e4fb0bb74df1
--- /dev/null
+++ b/arch/arm/mach-mx3/dptc.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file dptc.c
+ *
+ * @brief DPTC table for the Freescale Semiconductor MXC DPTC module.
+ *
+ * @ingroup PM
+ */
+
+#include <mach/hardware.h>
+#include <mach/mxc_dptc.h>
+
+struct dptc_wp dptc_wp_allfreq_26ckih[DPTC_WP_SUPPORTED] = {
+ /* 532MHz */
+ /* dcvr0 dcvr1 dcvr2 dcvr3 voltage */
+ /* wp0 */
+ {0xffc00000, 0x95c00000, 0xffc00000, 0xe5800000, 1625},
+ {0xffc00000, 0x95e3e8e4, 0xffc00000, 0xe5b6fda0, 1600},
+ {0xffc00000, 0x95e3e8e4, 0xffc00000, 0xe5b6fda0, 1575},
+ {0xffc00000, 0x95e3e8e8, 0xffc00000, 0xe5f70da4, 1550},
+ {0xffc00000, 0x9623f8e8, 0xffc00000, 0xe6371da8, 1525},
+ /* wp5 */
+ {0xffc00000, 0x966408f0, 0xffc00000, 0xe6b73db0, 1500},
+ {0xffc00000, 0x96e428f4, 0xffc00000, 0xe7776dbc, 1475},
+ {0xffc00000, 0x976448fc, 0xffc00000, 0xe8379dc8, 1450},
+ {0xffc00000, 0x97e46904, 0xffc00000, 0xe977ddd8, 1425},
+ {0xffc00000, 0x98a48910, 0xffc00000, 0xeab81de8, 1400},
+ /* wp10 */
+ {0xffc00000, 0x9964b918, 0xffc00000, 0xebf86df8, 1375},
+ {0xffc00000, 0xffe4e924, 0xffc00000, 0xfff8ae08, 1350},
+ {0xffc00000, 0xffe5192c, 0xffc00000, 0xfff8fe1c, 1350},
+ {0xffc00000, 0xffe54938, 0xffc00000, 0xfff95e2c, 1350},
+ {0xffc00000, 0xffe57944, 0xffc00000, 0xfff9ae44, 1350},
+ /* wp15 */
+ {0xffc00000, 0xffe5b954, 0xffc00000, 0xfffa0e58, 1350},
+ {0xffc00000, 0xffe5e960, 0xffc00000, 0xfffa6e70, 1350},
+};
+
+struct dptc_wp dptc_wp_allfreq_26ckih_TO_2_0[DPTC_WP_SUPPORTED] = {
+ /* Mx31 TO 2.0 Offset table */
+ /* 532MHz */
+ /* dcvr0 dcvr1 dcvr2 dcvr3 voltage */
+ /* wp0 */
+ {0xffc00000, 0x9E265978, 0xffc00000, 0xE4371D9C, 1625},
+ {0xffc00000, 0x9E665978, 0xffc00000, 0xE4772D9C, 1600},
+ {0xffc00000, 0x9EA65978, 0xffc00000, 0xE4772DA0, 1575},
+ {0xffc00000, 0x9EE66978, 0xffc00000, 0xE4B73DA0, 1550},
+ {0xffc00000, 0x9F26697C, 0xffc00000, 0xE4F73DA0, 1525},
+ /* wp5 */
+ {0xffc00000, 0x9F66797C, 0xffc00000, 0xE5774DA4, 1500},
+ {0xffc00000, 0x9FE6797C, 0xffc00000, 0xE5F75DA4, 1475},
+ {0xffc00000, 0xA026897C, 0xffc00000, 0xE6776DA4, 1450},
+ {0xffc00000, 0xA0A6897C, 0xffc00000, 0xE6F77DA8, 1425},
+ {0xffc00000, 0xA0E69980, 0xffc00000, 0xE7B78DAC, 1400},
+ /* wp10 */
+ {0xffc00000, 0xA1669980, 0xffc00000, 0xE8379DAC, 1375},
+ {0xffc00000, 0xA1A6A980, 0xffc00000, 0xE8F7ADB0, 1350},
+ {0xffc00000, 0xA226B984, 0xffc00000, 0xE9F7CDB0, 1325},
+ {0xffc00000, 0xA2A6C984, 0xffc00000, 0xEAB7DDB4, 1300},
+ {0xffc00000, 0xA326C988, 0xffc00000, 0xEBB7FDB8, 1275},
+ /* wp15 */
+ {0xffc00000, 0xA3A6D988, 0xffc00000, 0xECB80DBC, 1250},
+ {0xffc00000, 0xA426E988, 0xffc00000, 0xEDB82DC0, 1225},
+};
+
+struct dptc_wp dptc_wp_allfreq_27ckih_TO_2_0[DPTC_WP_SUPPORTED] = {
+ /* Mx31 TO 2.0 Offset table */
+ /* 532MHz */
+ /* dcvr0 dcvr1 dcvr2 dcvr3 voltage */
+ /* wp0 */
+ {0xffc00000, 0x9864E920, 0xffc00000, 0xDBB50D1C, 1625},
+ {0xffc00000, 0x98A4E920, 0xffc00000, 0xDBF51D1C, 1600},
+ {0xffc00000, 0x98E4E920, 0xffc00000, 0xDBF51D20, 1575},
+ {0xffc00000, 0x9924F920, 0xffc00000, 0xDC352D20, 1550},
+ {0xffc00000, 0x9924F924, 0xffc00000, 0xDC752D20, 1525},
+ /* wp5 */
+ {0xffc00000, 0x99650924, 0xffc00000, 0xDCF53D24, 1500},
+ {0xffc00000, 0x99E50924, 0xffc00000, 0xDD754D24, 1475},
+ {0xffc00000, 0x9A251924, 0xffc00000, 0xDDF55D24, 1450},
+ {0xffc00000, 0x9AA51924, 0xffc00000, 0xDE756D28, 1425},
+ {0xffc00000, 0x9AE52928, 0xffc00000, 0xDF357D2C, 1400},
+ /* wp10 */
+ {0xffc00000, 0x9B652928, 0xffc00000, 0xDFB58D2C, 1375},
+ {0xffc00000, 0x9BA53928, 0xffc00000, 0xE0759D30, 1350},
+ {0xffc00000, 0x9C254928, 0xffc00000, 0xE135BD30, 1325},
+ {0xffc00000, 0x9CA55928, 0xffc00000, 0xE1F5CD34, 1300},
+ {0xffc00000, 0x9D25592C, 0xffc00000, 0xE2F5ED38, 1275},
+ /* wp15 */
+ {0xffc00000, 0x9DA5692C, 0xffc00000, 0xE3F5FD38, 1250},
+ {0xffc00000, 0x9E25792C, 0xffc00000, 0xE4F61D3C, 1225},
+};
diff --git a/arch/arm/mach-mx3/dvfs_v2.c b/arch/arm/mach-mx3/dvfs_v2.c
new file mode 100644
index 000000000000..0d22e5fd8318
--- /dev/null
+++ b/arch/arm/mach-mx3/dvfs_v2.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file dvfs_v2.c
+ *
+ * @brief A simplied driver for the Freescale Semiconductor MXC DVFS module.
+ *
+ * Upon initialization, the DVFS driver initializes the DVFS hardware
+ * sets up driver nodes attaches to the DVFS interrupt and initializes internal
+ * data structures. When the DVFS interrupt occurs the driver checks the cause
+ * of the interrupt (lower frequency, increase frequency or emergency) and changes
+ * the CPU voltage according to translation table that is loaded into the driver.
+ *
+ * @ingroup PM
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/pmic_external.h>
+#include <mach/pmic_power.h>
+
+#include "iomux.h"
+#include "crm_regs.h"
+
+static int dvfs_is_active;
+
+/* Used for tracking the number of interrupts */
+static u32 dvfs_nr_up[4];
+static u32 dvfs_nr_dn[4];
+
+/*
+ * Clock structures
+ */
+static struct clk *cpu_clk;
+static struct clk *ahb_clk;
+
+enum {
+ FSVAI_FREQ_NOCHANGE = 0x0,
+ FSVAI_FREQ_INCREASE,
+ FSVAI_FREQ_DECREASE,
+ FSVAI_FREQ_EMERG,
+};
+
+/*
+ * Frequency increase threshold. Increase frequency change request
+ * will be sent if DVFS counter value will be more than this value.
+ */
+#define DVFS_UPTHR (30 << MXC_CCM_LTR0_UPTHR_OFFSET)
+
+/*
+ * Frequency decrease threshold. Decrease frequency change request
+ * will be sent if DVFS counter value will be less than this value.
+ */
+#define DVFS_DNTHR (18 << MXC_CCM_LTR0_DNTHR_OFFSET)
+
+/*
+ * With the ARM clocked at 532, this setting yields a DIV_3_CLK of 2.03 kHz.
+ */
+#define DVFS_DIV3CK (3 << MXC_CCM_LTR0_DIV3CK_OFFSET)
+
+/*
+ * DNCNT defines the amount of times the down threshold should be exceeded
+ * before DVFS will trigger frequency decrease request.
+ */
+#define DVFS_DNCNT (0x33 << MXC_CCM_LTR1_DNCNT_OFFSET)
+
+/*
+ * UPCNT defines the amount of times the up threshold should be exceeded
+ * before DVFS will trigger frequency increase request.
+ */
+#define DVFS_UPCNT (0x33 << MXC_CCM_LTR1_UPCNT_OFFSET)
+
+/*
+ * Panic threshold. Panic frequency change request
+ * will be sent if DVFS counter value will be more than this value.
+ */
+#define DVFS_PNCTHR (63 << MXC_CCM_LTR1_PNCTHR_OFFSET)
+
+/*
+ * Load tracking buffer source: 1 for ld_add; 0 for pre_ld_add
+ */
+#define DVFS_LTBRSR (1 << MXC_CCM_LTR1_LTBRSR_OFFSET)
+
+/* EMAC defines how many samples are included in EMA calculation */
+#define DVFS_EMAC (0x20 << MXC_CCM_LTR2_EMAC_OFFSET)
+
+const static u8 ltr_gp_weight[] = {
+ 0, /* 0 */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0, /* 5 */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0, /* 10 */
+ 0,
+ 7,
+ 7,
+ 7,
+ 7, /* 15 */
+};
+
+DEFINE_SPINLOCK(mxc_dvfs_lock);
+
+/*!
+ * This function sets the weight of general purpose signals
+ * @param gp_id number of general purpose bit
+ * @param weight the weight of the general purpose bit
+ */
+static void set_gp_weight(int gp_id, u8 weight)
+{
+ u32 reg;
+
+ if (gp_id < 9) {
+ reg = __raw_readl(MXC_CCM_LTR3);
+ reg = (reg & ~(MXC_CCM_LTR3_WSW_MASK(gp_id))) |
+ (weight << MXC_CCM_LTR3_WSW_OFFSET(gp_id));
+ __raw_writel(reg, MXC_CCM_LTR3);
+ } else if (gp_id < 16) {
+ reg = __raw_readl(MXC_CCM_LTR2);
+ reg = (reg & ~(MXC_CCM_LTR2_WSW_MASK(gp_id))) |
+ (weight << MXC_CCM_LTR2_WSW_OFFSET(gp_id));
+ __raw_writel(reg, MXC_CCM_LTR2);
+ }
+}
+
+static int start_dvfs(void)
+{
+ u32 reg;
+ unsigned long flags;
+
+ if (dvfs_is_active) {
+ return 0;
+ }
+
+ spin_lock_irqsave(&mxc_dvfs_lock, flags);
+
+ reg = __raw_readl(MXC_CCM_PMCR0);
+
+ /* enable dvfs and interrupt */
+ reg = (reg & ~MXC_CCM_PMCR0_FSVAIM) | MXC_CCM_PMCR0_DVFEN;
+
+ __raw_writel(reg, MXC_CCM_PMCR0);
+
+ dvfs_is_active = 1;
+
+ spin_unlock_irqrestore(&mxc_dvfs_lock, flags);
+
+ pr_info("DVFS is started\n");
+
+ return 0;
+}
+
+#define MXC_CCM_LTR0_CONFIG_MASK (MXC_CCM_LTR0_UPTHR_MASK | \
+ MXC_CCM_LTR0_DNTHR_MASK | \
+ MXC_CCM_LTR0_DIV3CK_MASK)
+#define MXC_CCM_LTR0_CONFIG_VAL (DVFS_UPTHR | DVFS_DNTHR | DVFS_DIV3CK)
+
+#define MXC_CCM_LTR1_CONFIG_MASK (MXC_CCM_LTR1_UPCNT_MASK | \
+ MXC_CCM_LTR1_DNCNT_MASK | \
+ MXC_CCM_LTR1_PNCTHR_MASK | \
+ MXC_CCM_LTR1_LTBRSR_MASK)
+#define MXC_CCM_LTR1_CONFIG_VAL (DVFS_UPCNT | DVFS_DNCNT | \
+ DVFS_PNCTHR | DVFS_LTBRSR)
+
+/*!
+ * This function is called for module initialization.
+ * It sets up the DVFS hardware.
+ * It sets default values for DVFS thresholds and counters. The default
+ * values was chosen from a set of different reasonable values. They was tested
+ * and the default values in the driver gave the best results.
+ * More work should be done to find optimal values.
+ *
+ * @return 0 if successful; non-zero otherwise.
+ *
+ */
+static int init_dvfs_controller(void)
+{
+ u32 i, reg;
+
+ /* Configure 2 MC13783 DVFS pins */
+ mxc_request_iomux(MX31_PIN_DVFS0, OUTPUTCONFIG_FUNC, INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_DVFS1, OUTPUTCONFIG_FUNC, INPUTCONFIG_NONE);
+
+ /* Configure MC13783 voltage ready input pin */
+ mxc_request_iomux(MX31_PIN_GPIO1_5, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_FUNC);
+
+ /* setup LTR0 */
+ reg = __raw_readl(MXC_CCM_LTR0);
+ reg = (reg & ~(MXC_CCM_LTR0_CONFIG_MASK)) | MXC_CCM_LTR0_CONFIG_VAL;
+ __raw_writel(reg, MXC_CCM_LTR0);
+
+ /* set up LTR1 */
+ reg = __raw_readl(MXC_CCM_LTR1);
+ reg = (reg & ~(MXC_CCM_LTR1_CONFIG_MASK)) | MXC_CCM_LTR1_CONFIG_VAL;
+ __raw_writel(reg, MXC_CCM_LTR1);
+
+ /* setup LTR2 */
+ reg = __raw_readl(MXC_CCM_LTR2);
+ reg = (reg & ~(MXC_CCM_LTR2_EMAC_MASK)) | DVFS_EMAC;
+ __raw_writel(reg, MXC_CCM_LTR2);
+
+ /* Set general purpose weights to 0 */
+ for (i = 0; i < 16; i++) {
+ set_gp_weight(i, ltr_gp_weight[i]);
+ }
+
+ /* ARM interrupt, mask load buf full interrupt */
+ reg = __raw_readl(MXC_CCM_PMCR0);
+ reg |= MXC_CCM_PMCR0_DVFIS | MXC_CCM_PMCR0_LBMI;
+ __raw_writel(reg, MXC_CCM_PMCR0);
+
+ /* configuring EMI Handshake and PLL relock disable */
+ reg = __raw_readl(MXC_CCM_PMCR1);
+ reg |= MXC_CCM_PMCR1_PLLRDIS;
+ reg |= MXC_CCM_PMCR1_EMIRQ_EN;
+ __raw_writel(reg, MXC_CCM_PMCR1);
+
+ return 0;
+}
+
+static irqreturn_t dvfs_irq(int irq, void *dev_id)
+{
+ u32 pmcr0 = __raw_readl(MXC_CCM_PMCR0);
+ u32 fsvai = (pmcr0 & MXC_CCM_PMCR0_FSVAI_MASK) >>
+ MXC_CCM_PMCR0_FSVAI_OFFSET;
+ u32 dvsup = (pmcr0 & MXC_CCM_PMCR0_DVSUP_MASK) >>
+ MXC_CCM_PMCR0_DVSUP_OFFSET;
+ u32 curr_ahb, curr_cpu, rate;
+
+ /* Should not be here if FSVAIM is set */
+ BUG_ON(pmcr0 & MXC_CCM_PMCR0_FSVAIM);
+
+ if (fsvai == FSVAI_FREQ_NOCHANGE) {
+ /* Do nothing. Freq change is not required */
+ printk(KERN_WARNING "fsvai should not be 0\n");
+ return IRQ_HANDLED;
+ }
+
+ if (!(pmcr0 & MXC_CCM_PMCR0_UPDTEN)) {
+ /* Do nothing. DVFS didn't finish previous flow update */
+ return IRQ_HANDLED;
+ }
+
+ if (((dvsup == DVSUP_LOW) && (fsvai == FSVAI_FREQ_DECREASE)) ||
+ ((dvsup == DVSUP_TURBO) && ((fsvai == FSVAI_FREQ_INCREASE) ||
+ (fsvai == FSVAI_FREQ_EMERG)))) {
+ /* Interrupt should be disabled in these cases according to
+ * the spec since DVFS is already at lowest (highest) state */
+ printk(KERN_WARNING "Something is wrong?\n");
+ return IRQ_HANDLED;
+ }
+
+ curr_ahb = clk_get_rate(ahb_clk);
+ if (fsvai == FSVAI_FREQ_DECREASE) {
+ curr_cpu = clk_get_rate(cpu_clk);
+ rate = ((curr_cpu / curr_ahb) - 1) * curr_ahb;
+ if ((cpu_is_mx31_rev(CHIP_REV_2_0) < 0) &&
+ ((curr_cpu / curr_ahb) == 4)) {
+ rate = ((curr_cpu / curr_ahb) - 2) * curr_ahb;
+ }
+ dvfs_nr_dn[dvsup]++;
+ } else {
+ rate = 4 * curr_ahb;
+ dvfs_nr_up[dvsup]++;
+ }
+
+ clk_set_rate(cpu_clk, rate);
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function disables the DVFS module.
+ */
+static void stop_dvfs(void)
+{
+ u32 pmcr0, dvsup;
+ unsigned long flags;
+ u32 curr_ahb = clk_get_rate(ahb_clk);
+
+ if (dvfs_is_active) {
+ spin_lock_irqsave(&mxc_dvfs_lock, flags);
+
+ pmcr0 = __raw_readl(MXC_CCM_PMCR0);
+ dvsup = (pmcr0 & MXC_CCM_PMCR0_DVSUP_MASK) >>
+ MXC_CCM_PMCR0_DVSUP_OFFSET;
+ if (dvsup != DVSUP_TURBO) {
+ /* Use sw delay to insure volt/freq change */
+ clk_set_rate(cpu_clk, (4 * curr_ahb));
+ udelay(200);
+ }
+
+ pmcr0 = __raw_readl(MXC_CCM_PMCR0);
+ /* disable dvfs and its interrupt */
+ pmcr0 = (pmcr0 & ~MXC_CCM_PMCR0_DVFEN) | MXC_CCM_PMCR0_FSVAIM;
+ __raw_writel(pmcr0, MXC_CCM_PMCR0);
+
+ dvfs_is_active = 0;
+
+ spin_unlock_irqrestore(&mxc_dvfs_lock, flags);
+ }
+
+ pr_info("DVFS is stopped\n");
+}
+
+void pmic_voltage_init(void)
+{
+ t_regulator_voltage volt;
+
+ /* Enable 4 mc13783 output voltages */
+ pmic_write_reg(REG_ARBITRATION_SWITCHERS, (1 << 5), (1 << 5));
+
+ /* Set mc13783 DVS speed 25mV each 4us */
+ pmic_write_reg(REG_SWITCHERS_4, (0 << 6), (3 << 6));
+
+ if (cpu_is_mx31())
+ volt.sw1a = SW1A_1_625V;
+ else
+ volt.sw1a = SW1A_1_425V;
+
+ pmic_power_regulator_set_voltage(SW_SW1A, volt);
+
+ volt.sw1a = SW1A_1_25V;
+ pmic_power_switcher_set_dvs(SW_SW1A, volt);
+
+ if (cpu_is_mx32()) {
+ volt.sw1a = SW1A_0_975V;
+ pmic_power_switcher_set_stby(SW_SW1A, volt);
+ }
+
+ volt.sw1b = SW1A_1_25V;
+ pmic_power_switcher_set_dvs(SW_SW1B, volt);
+
+ volt.sw1b = SW1A_1_25V;
+ pmic_power_switcher_set_stby(SW_SW1B, volt);
+}
+
+static ssize_t dvfs_enable_store(struct sys_device *dev, struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (strstr(buf, "1") != NULL) {
+ if (start_dvfs() != 0) {
+ printk(KERN_ERR "Failed to start DVFS\n");
+ }
+ } else if (strstr(buf, "0") != NULL) {
+ stop_dvfs();
+ }
+
+ return size;
+}
+
+static ssize_t dvfs_status_show(struct sys_device *dev, struct sysdev_attribute *attr,
+ char *buf)
+{
+ int size = 0;
+
+ if (dvfs_is_active) {
+ size = sprintf(buf, "DVFS is enabled\n");
+ } else {
+ size = sprintf(buf, "DVFS is disabled\n");
+ }
+ size +=
+ sprintf((buf + size), "UP:\t%d\t%d\t%d\t%d\n", dvfs_nr_up[0],
+ dvfs_nr_up[1], dvfs_nr_up[2], dvfs_nr_up[3]);
+ size +=
+ sprintf((buf + size), "DOWN:\t%d\t%d\t%d\t%d\n\n", dvfs_nr_dn[0],
+ dvfs_nr_dn[1], dvfs_nr_dn[2], dvfs_nr_dn[3]);
+
+ return size;
+}
+
+static ssize_t dvfs_status_store(struct sys_device *dev, struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (strstr(buf, "reset") != NULL) {
+ int i;
+ for (i = 0; i < 4; i++) {
+ dvfs_nr_up[i] = 0;
+ dvfs_nr_dn[i] = 0;
+ }
+ }
+
+ return size;
+}
+
+static ssize_t dvfs_debug_show(struct sys_device *dev, struct sysdev_attribute *attr,
+ char *buf)
+{
+ int size = 0;
+ u32 curr_ahb, curr_cpu;
+
+ curr_ahb = clk_get_rate(ahb_clk);
+ curr_cpu = clk_get_rate(cpu_clk);
+
+ pr_debug("ahb %d, cpu %d\n", curr_ahb, curr_cpu);
+
+ return size;
+}
+
+static ssize_t dvfs_debug_store(struct sys_device *dev, struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ u32 curr_ahb, curr_cpu, rate = 0;
+
+ curr_ahb = clk_get_rate(ahb_clk);
+ curr_cpu = clk_get_rate(cpu_clk);
+
+ if (strstr(buf, "inc") != NULL) {
+ rate = 4 * curr_ahb;
+ pr_debug("inc to %d\n", rate);
+ }
+
+ if (strstr(buf, "dec") != NULL) {
+ rate = ((curr_cpu / curr_ahb) - 1) * curr_ahb;
+ if ((cpu_is_mx31_rev(CHIP_REV_2_0) < 0) &&
+ ((curr_cpu / curr_ahb) == 4))
+ rate = ((curr_cpu / curr_ahb) - 2) * curr_ahb;
+
+ pr_debug("dec to %d\n", rate);
+ }
+
+ clk_set_rate(cpu_clk, rate);
+
+ return size;
+}
+
+static SYSDEV_ATTR(enable, 0200, NULL, dvfs_enable_store);
+static SYSDEV_ATTR(status, 0644, dvfs_status_show, dvfs_status_store);
+static SYSDEV_ATTR(debug, 0644, dvfs_debug_show, dvfs_debug_store);
+
+static struct sysdev_class dvfs_sysclass = {
+ .name = "dvfs",
+};
+
+static struct sys_device dvfs_device = {
+ .id = 0,
+ .cls = &dvfs_sysclass,
+};
+
+static int dvfs_sysdev_ctrl_init(void)
+{
+ int err;
+
+ err = sysdev_class_register(&dvfs_sysclass);
+ if (!err)
+ err = sysdev_register(&dvfs_device);
+ if (!err) {
+ err = sysdev_create_file(&dvfs_device, &attr_enable);
+ err = sysdev_create_file(&dvfs_device, &attr_status);
+ err = sysdev_create_file(&dvfs_device, &attr_debug);
+ }
+
+ return err;
+}
+
+static void dvfs_sysdev_ctrl_exit(void)
+{
+ sysdev_remove_file(&dvfs_device, &attr_enable);
+ sysdev_remove_file(&dvfs_device, &attr_status);
+ sysdev_unregister(&dvfs_device);
+ sysdev_class_unregister(&dvfs_sysclass);
+}
+
+static int __init dvfs_init(void)
+{
+ int err = 0;
+ pmic_voltage_init();
+
+ cpu_clk = clk_get(NULL, "cpu_clk");
+ ahb_clk = clk_get(NULL, "ahb_clk");
+ err = init_dvfs_controller();
+ if (err) {
+ printk(KERN_ERR "DVFS: Unable to initialize DVFS");
+ return err;
+ }
+
+ /* request the DVFS interrupt */
+ err = request_irq(MXC_INT_DVFS, dvfs_irq, IRQF_DISABLED, "dvfs", NULL);
+ if (err) {
+ printk(KERN_ERR "DVFS: Unable to attach to DVFS interrupt");
+ }
+
+ err = dvfs_sysdev_ctrl_init();
+ if (err) {
+ printk(KERN_ERR
+ "DVFS: Unable to register sysdev entry for dvfs");
+ return err;
+ }
+
+ return err;
+}
+
+static void __exit dvfs_cleanup(void)
+{
+ stop_dvfs();
+
+ /* release the DVFS interrupt */
+ free_irq(MXC_INT_DVFS, NULL);
+
+ dvfs_sysdev_ctrl_exit();
+
+ clk_put(cpu_clk);
+ clk_put(ahb_clk);
+}
+
+module_init(dvfs_init);
+module_exit(dvfs_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("DVFS driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-mx3/iomux.c b/arch/arm/mach-mx3/iomux.c
index 6e664be8cc13..dbaba1e31062 100644
--- a/arch/arm/mach-mx3/iomux.c
+++ b/arch/arm/mach-mx3/iomux.c
@@ -1,113 +1,260 @@
/*
- * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved.
- * Copyright (C) 2008 by Sascha Hauer <kernel@pengutronix.de>
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup GPIO_MX31 Board GPIO and Muxing Setup
+ * @ingroup MSL_MX31
+ */
+/*!
+ * @file mach-mx3/iomux.c
*
- * 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.
+ * @brief I/O Muxing control functions
*
- * 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.
+ * @ingroup GPIO_MX31
*/
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/spinlock.h>
-#include <linux/io.h>
-#include <linux/gpio.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
-#include <mach/iomux-mx3.h>
+#include "iomux.h"
-/*
+/*!
+ * 4 control fields per MUX register
+ */
+#define MUX_CTL_FIELDS 4
+
+/*!
+ * 3 control fields per PAD register
+ */
+#define PAD_CTL_FIELDS 3
+
+/*!
+ * Maximum number of MUX pins
+ * Number of pins = (highest iomux reg - lowest iomux reg + 1) * (4 pins/reg)
+ */
+#define MUX_PIN_NUM_MAX \
+ (((u32 *)IOMUXSW_MUX_END - (u32 *)IOMUXSW_MUX_CTL + 1) * MUX_CTL_FIELDS)
+
+/*!
+ * Number of pad controls =
+ * (highest pad ctl reg - lowest pad ctl reg + 1) * (3 pins/reg)
+ */
+#define PAD_CTL_NUM_MAX \
+ (((u32 *)IOMUXSW_PAD_END - (u32 *)IOMUXSW_PAD_CTL + 1) * PAD_CTL_FIELDS)
+
+#define PIN_TO_IOMUX_INDEX(pin) ((pin >> MUX_I) & ((1 << (MUX_F - MUX_I)) - 1))
+#define PIN_TO_IOMUX_FIELD(pin) ((pin >> MUX_F) & ((1 << (PAD_I - MUX_F)) - 1))
+
+/*!
+ * 8 bits for each MUX control field
+ */
+#define MUX_CTL_BIT_LEN 8
+
+/*!
+ * 10 bits for each PAD control field
+ */
+#define MUX_PAD_BIT_LEN 10
+
+/*!
* IOMUX register (base) addresses
*/
-#define IOMUX_BASE IO_ADDRESS(IOMUXC_BASE_ADDR)
-#define IOMUXINT_OBS1 (IOMUX_BASE + 0x000)
-#define IOMUXINT_OBS2 (IOMUX_BASE + 0x004)
-#define IOMUXGPR (IOMUX_BASE + 0x008)
-#define IOMUXSW_MUX_CTL (IOMUX_BASE + 0x00C)
-#define IOMUXSW_PAD_CTL (IOMUX_BASE + 0x154)
+enum iomux_reg_addr {
+ IOMUXGPR = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x008, /*!< General purpose */
+ IOMUXSW_MUX_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x00C, /*!< MUX control */
+ IOMUXSW_MUX_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x150, /*!< last MUX control register */
+ IOMUXSW_PAD_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x154, /*!< Pad control */
+ IOMUXSW_PAD_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x308, /*!< last Pad control register */
+ IOMUXINT_OBS1 = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x000, /*!< Observe interrupts 1 */
+ IOMUXINT_OBS2 = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x004, /*!< Observe interrupts 2 */
+};
+/* len - mask bit length; fld - mask bit field. Example, to have the mask:
+ * 0xFF000000, use GET_FIELD_MASK(8, 3). Translate in plain language:
+ * "set the 3rd (0-based) 8-bit-long field to all 1's */
+#define GET_FIELD_MASK(len, fld) (((1 << len) - 1) << (len * fld))
static DEFINE_SPINLOCK(gpio_mux_lock);
+static u8 iomux_pin_res_table[MUX_PIN_NUM_MAX];
-#define IOMUX_REG_MASK (IOMUX_PADNUM_MASK & ~0x3)
-/*
- * set the mode for a IOMUX pin.
+/*!
+ * This function is used to configure a pin through the IOMUX module.
+ * FIXED ME: for backward compatible. Will be static function!
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param out an output function as defined in \b #iomux_pin_ocfg_t
+ * @param in an input function as defined in \b #iomux_pin_icfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
*/
-int mxc_iomux_mode(unsigned int pin_mode)
+int iomux_config_mux(iomux_pin_name_t pin, iomux_pin_ocfg_t out,
+ iomux_pin_icfg_t in)
{
- u32 field, l, mode, ret = 0;
- void __iomem *reg;
-
- reg = IOMUXSW_MUX_CTL + (pin_mode & IOMUX_REG_MASK);
- field = pin_mode & 0x3;
- mode = (pin_mode & IOMUX_MODE_MASK) >> IOMUX_MODE_SHIFT;
+ u32 reg, l, ret = 0;
+ u32 mux_index = PIN_TO_IOMUX_INDEX(pin);
+ u32 mux_field = PIN_TO_IOMUX_FIELD(pin);
+ u32 mux_mask = GET_FIELD_MASK(MUX_CTL_BIT_LEN, mux_field);
+ u8 *rp;
- pr_debug("%s: reg offset = 0x%x field = %d mode = 0x%02x\n",
- __func__, (pin_mode & IOMUX_REG_MASK), field, mode);
+ BUG_ON((mux_index > (MUX_PIN_NUM_MAX / MUX_CTL_FIELDS - 1)) ||
+ (mux_field >= MUX_CTL_FIELDS));
+ reg = IOMUXSW_MUX_CTL + (mux_index * 4);
spin_lock(&gpio_mux_lock);
-
l = __raw_readl(reg);
- l &= ~(0xff << (field * 8));
- l |= mode << (field * 8);
+ l = (l & (~mux_mask)) |
+ (((out << 4) | in) << (mux_field * MUX_CTL_BIT_LEN));
__raw_writel(l, reg);
-
+ /*
+ * Log a warning if a pin changes ownership
+ */
+ rp = iomux_pin_res_table + mux_index * MUX_CTL_FIELDS + mux_field;
+ if (out & *rp && *rp != ((out << 4) | in)) {
+ /*
+ * Don't call printk if we're tweaking the console uart or
+ * we'll deadlock.
+ */
+ if (pin != MX31_PIN_CTS1 &&
+ pin != MX31_PIN_RTS1 &&
+ pin != MX31_PIN_DCD_DCE1 &&
+ pin != MX31_PIN_DSR_DTE1 &&
+ pin != MX31_PIN_DTR_DTE1 &&
+ pin != MX31_PIN_RI_DCE1 &&
+ pin != MX31_PIN_DSR_DCE1 &&
+ pin != MX31_PIN_DTR_DCE1 &&
+ pin != MX31_PIN_RXD1 && pin != MX31_PIN_TXD1) {
+ printk(KERN_ERR "iomux_config_mux: Warning: iomux pin"
+ " config changed, index=%d field=%d, "
+ " prev=0x%x new=0x%x\n", mux_index, mux_field,
+ *rp, (out << 4) | in);
+ }
+ ret = -EINVAL;
+ }
+ *rp = (out << 4) | in;
spin_unlock(&gpio_mux_lock);
return ret;
}
-EXPORT_SYMBOL(mxc_iomux_mode);
-/*
- * This function configures the pad value for a IOMUX pin.
+/*!
+ * Request ownership for an IO pin. This function has to be the first one
+ * being called before that pin is used. The caller has to check the
+ * return value to make sure it returns 0.
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param out an output function as defined in \b #iomux_pin_ocfg_t
+ * @param in an input function as defined in \b #iomux_pin_icfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
*/
-void mxc_iomux_set_pad(enum iomux_pins pin, u32 config)
+int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_ocfg_t out,
+ iomux_pin_icfg_t in)
{
- u32 field, l;
- void __iomem *reg;
+ int ret = iomux_config_mux(pin, out, in);
+ if (out == OUTPUTCONFIG_GPIO && in == INPUTCONFIG_GPIO) {
+ ret |= gpio_request(IOMUX_TO_GPIO(pin), NULL);
+ }
+ return ret;
+}
- reg = IOMUXSW_PAD_CTL + (pin + 2) / 3;
- field = (pin + 2) % 3;
+/*!
+ * Release ownership for an IO pin
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param out an output function as defined in \b #iomux_pin_ocfg_t
+ * @param in an input function as defined in \b #iomux_pin_icfg_t
+ */
+void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_ocfg_t out,
+ iomux_pin_icfg_t in)
+{
+ u32 mux_index = PIN_TO_IOMUX_INDEX(pin);
+ u32 mux_field = PIN_TO_IOMUX_FIELD(pin);
+ u8 *rp = iomux_pin_res_table + mux_index * MUX_CTL_FIELDS + mux_field;
- pr_debug("%s: reg offset = 0x%x field = %d\n",
- __func__, (pin + 2) / 3, field);
+ BUG_ON((mux_index > (MUX_PIN_NUM_MAX / MUX_CTL_FIELDS - 1)) ||
+ (mux_field >= MUX_CTL_FIELDS));
- spin_lock(&gpio_mux_lock);
+ *rp = 0;
+ if (out == OUTPUTCONFIG_GPIO && in == INPUTCONFIG_GPIO) {
+ gpio_free(IOMUX_TO_GPIO(pin));
+ }
+}
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config the ORed value of elements defined in \b #iomux_pad_config_t
+ */
+void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config)
+{
+ u32 reg, l;
+ u32 pad_index = (pin >> PAD_I) & ((1 << (PAD_F - PAD_I)) - 1);
+ u32 pad_field = (pin >> PAD_F) & ((1 << (MUX_IO_I - PAD_F)) - 1);
+ u32 pad_mask = GET_FIELD_MASK(MUX_PAD_BIT_LEN, pad_field);
+
+ BUG_ON((pad_index > (PAD_CTL_NUM_MAX / PAD_CTL_FIELDS - 1)) ||
+ (pad_field >= PAD_CTL_FIELDS));
+
+ reg = IOMUXSW_PAD_CTL + (pad_index * 4);
+ spin_lock(&gpio_mux_lock);
l = __raw_readl(reg);
- l &= ~(0x1ff << (field * 9));
- l |= config << (field * 9);
+ l = (l & (~pad_mask)) | (config << (pad_field * MUX_PAD_BIT_LEN));
__raw_writel(l, reg);
-
spin_unlock(&gpio_mux_lock);
}
-EXPORT_SYMBOL(mxc_iomux_set_pad);
/*
+ * FIXED ME: for backward compatible. to be removed!
+ */
+void iomux_config_pad(iomux_pin_name_t pin, u32 config)
+{
+ mxc_iomux_set_pad(pin, config);
+}
+
+/*!
* This function enables/disables the general purpose function for a particular
* signal.
+ *
+ * @param gp one signal as defined in \b #iomux_gp_func_t
+ * @param en \b #true to enable; \b #false to disable
*/
-void mxc_iomux_set_gpr(enum iomux_gp_func gp, bool en)
+void mxc_iomux_set_gpr(iomux_gp_func_t gp, bool en)
{
u32 l;
spin_lock(&gpio_mux_lock);
l = __raw_readl(IOMUXGPR);
- if (en)
+ if (en) {
l |= gp;
- else
+ } else {
l &= ~gp;
-
+ }
__raw_writel(l, IOMUXGPR);
spin_unlock(&gpio_mux_lock);
}
-EXPORT_SYMBOL(mxc_iomux_set_gpr);
+/*!
+ * FIXED ME: for backward compatible. to be removed!
+ */
+void iomux_config_gpr(iomux_gp_func_t gp, bool en)
+{
+ mxc_iomux_set_gpr(gp, en);
+}
+
+EXPORT_SYMBOL(mxc_request_iomux);
+EXPORT_SYMBOL(mxc_free_iomux);
+EXPORT_SYMBOL(mxc_iomux_set_pad);
+EXPORT_SYMBOL(mxc_iomux_set_gpr);
+EXPORT_SYMBOL(iomux_config_pad);
+EXPORT_SYMBOL(iomux_config_gpr);
+EXPORT_SYMBOL(iomux_config_mux);
diff --git a/arch/arm/mach-mx3/iomux.h b/arch/arm/mach-mx3/iomux.h
new file mode 100644
index 000000000000..194e091ec656
--- /dev/null
+++ b/arch/arm/mach-mx3/iomux.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MACH_MX31_IOMUX_H__
+#define __MACH_MX31_IOMUX_H__
+
+#include <linux/types.h>
+#include <mach/gpio.h>
+#include "mx31_pins.h"
+
+typedef unsigned int iomux_pin_name_t;
+/*!
+ * @file mach-mx3/iomux.h
+ *
+ * @brief I/O Muxing control definitions and functions
+ *
+ * @ingroup GPIO_MX31
+ */
+
+/*!
+ * various IOMUX output functions
+ */
+typedef enum iomux_output_config {
+ OUTPUTCONFIG_GPIO = 0, /*!< used as GPIO */
+ OUTPUTCONFIG_FUNC, /*!< used as function */
+ OUTPUTCONFIG_ALT1, /*!< used as alternate function 1 */
+ OUTPUTCONFIG_ALT2, /*!< used as alternate function 2 */
+ OUTPUTCONFIG_ALT3, /*!< used as alternate function 3 */
+ OUTPUTCONFIG_ALT4, /*!< used as alternate function 4 */
+ OUTPUTCONFIG_ALT5, /*!< used as alternate function 5 */
+ OUTPUTCONFIG_ALT6 /*!< used as alternate function 6 */
+} iomux_pin_ocfg_t;
+
+/*!
+ * various IOMUX input functions
+ */
+typedef enum iomux_input_config {
+ INPUTCONFIG_NONE = 0, /*!< not configured for input */
+ INPUTCONFIG_GPIO = 1 << 0, /*!< used as GPIO */
+ INPUTCONFIG_FUNC = 1 << 1, /*!< used as function */
+ INPUTCONFIG_ALT1 = 1 << 2, /*!< used as alternate function 1 */
+ INPUTCONFIG_ALT2 = 1 << 3 /*!< used as alternate function 2 */
+} iomux_pin_icfg_t;
+
+/*!
+ * various IOMUX pad functions
+ */
+typedef enum iomux_pad_config {
+ PAD_CTL_NOLOOPBACK = 0x0 << 9,
+ PAD_CTL_LOOPBACK = 0x1 << 9,
+ PAD_CTL_PKE_NONE = 0x0 << 8,
+ PAD_CTL_PKE_ENABLE = 0x1 << 8,
+ PAD_CTL_PUE_KEEPER = 0x0 << 7,
+ PAD_CTL_PUE_PUD = 0x1 << 7,
+ PAD_CTL_100K_PD = 0x0 << 5,
+ PAD_CTL_100K_PU = 0x1 << 5,
+ PAD_CTL_47K_PU = 0x2 << 5,
+ PAD_CTL_22K_PU = 0x3 << 5,
+ PAD_CTL_HYS_CMOS = 0x0 << 4,
+ PAD_CTL_HYS_SCHMITZ = 0x1 << 4,
+ PAD_CTL_ODE_CMOS = 0x0 << 3,
+ PAD_CTL_ODE_OpenDrain = 0x1 << 3,
+ PAD_CTL_DRV_NORMAL = 0x0 << 1,
+ PAD_CTL_DRV_HIGH = 0x1 << 1,
+ PAD_CTL_DRV_MAX = 0x2 << 1,
+ PAD_CTL_SRE_SLOW = 0x0 << 0,
+ PAD_CTL_SRE_FAST = 0x1 << 0
+} iomux_pad_config_t;
+
+/*!
+ * various IOMUX general purpose functions
+ */
+typedef enum iomux_gp_func {
+ MUX_PGP_FIRI = 0x1 << 0,
+ MUX_DDR_MODE = 0x1 << 1,
+ MUX_PGP_CSPI_BB = 0x1 << 2,
+ MUX_PGP_ATA_1 = 0x1 << 3,
+ MUX_PGP_ATA_2 = 0x1 << 4,
+ MUX_PGP_ATA_3 = 0x1 << 5,
+ MUX_PGP_ATA_4 = 0x1 << 6,
+ MUX_PGP_ATA_5 = 0x1 << 7,
+ MUX_PGP_ATA_6 = 0x1 << 8,
+ MUX_PGP_ATA_7 = 0x1 << 9,
+ MUX_PGP_ATA_8 = 0x1 << 10,
+ MUX_PGP_UH2 = 0x1 << 11,
+ MUX_SDCTL_CSD0_SEL = 0x1 << 12,
+ MUX_SDCTL_CSD1_SEL = 0x1 << 13,
+ MUX_CSPI1_UART3 = 0x1 << 14,
+ MUX_EXTDMAREQ2_MBX_SEL = 0x1 << 15,
+ MUX_TAMPER_DETECT_EN = 0x1 << 16,
+ MUX_PGP_USB_4WIRE = 0x1 << 17,
+ MUX_PGB_USB_COMMON = 0x1 << 18,
+ MUX_SDHC_MEMSTICK1 = 0x1 << 19,
+ MUX_SDHC_MEMSTICK2 = 0x1 << 20,
+ MUX_PGP_SPLL_BYP = 0x1 << 21,
+ MUX_PGP_UPLL_BYP = 0x1 << 22,
+ MUX_PGP_MSHC1_CLK_SEL = 0x1 << 23,
+ MUX_PGP_MSHC2_CLK_SEL = 0x1 << 24,
+ MUX_CSPI3_UART5_SEL = 0x1 << 25,
+ MUX_PGP_ATA_9 = 0x1 << 26,
+ MUX_PGP_USB_SUSPEND = 0x1 << 27,
+ MUX_PGP_USB_OTG_LOOPBACK = 0x1 << 28,
+ MUX_PGP_USB_HS1_LOOPBACK = 0x1 << 29,
+ MUX_PGP_USB_HS2_LOOPBACK = 0x1 << 30,
+ MUX_CLKO_DDR_MODE = 0x1 << 31,
+} iomux_gp_func_t;
+
+/*!
+ * This function is used to configure a pin through the IOMUX module.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param out an output function as defined in \b #iomux_pin_ocfg_t
+ * @param in an input function as defined in \b #iomux_pin_icfg_t
+ * @return 0 if successful; Non-zero otherwise
+ */
+int iomux_config_mux(iomux_pin_name_t pin, iomux_pin_ocfg_t out,
+ iomux_pin_icfg_t in);
+
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pins
+ * @param config ORed value of elements defined in \b #iomux_pad_config_t
+ */
+void iomux_config_pad(iomux_pin_name_t pin, __u32 config);
+
+/*!
+ * This function enables/disables the general purpose function for a particular
+ * signal.
+ *
+ * @param gp one signal as defined in \b #iomux_gp_func_t
+ * @param en \b #true to enable; \b #false to disable
+ */
+void iomux_config_gpr(iomux_gp_func_t gp, bool en);
+
+/*!
+ * Request ownership for an IO pin. This function has to be the first one
+ * being called before that pin is used. The caller has to check the
+ * return value to make sure it returns 0.
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param out an output function as defined in \b #iomux_pin_ocfg_t
+ * @param in an input function as defined in \b #iomux_pin_icfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_ocfg_t out,
+ iomux_pin_icfg_t in);
+
+/*!
+ * Release ownership for an IO pin
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param out an output function as defined in \b #iomux_pin_ocfg_t
+ * @param in an input function as defined in \b #iomux_pin_icfg_t
+ */
+void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_ocfg_t out,
+ iomux_pin_icfg_t in);
+
+/*!
+ * This function enables/disables the general purpose function for a particular
+ * signal.
+ *
+ * @param gp one signal as defined in \b #iomux_gp_func_t
+ * @param en \b #true to enable; \b #false to disable
+ */
+void mxc_iomux_set_gpr(iomux_gp_func_t gp, bool en);
+
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config the ORed value of elements defined in \b #iomux_pad_config_t
+ */
+void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config);
+
+#endif
diff --git a/arch/arm/mach-mx3/mm.c b/arch/arm/mach-mx3/mm.c
index 0589b5cd33c7..40379fa5abdf 100644
--- a/arch/arm/mach-mx3/mm.c
+++ b/arch/arm/mach-mx3/mm.c
@@ -22,18 +22,10 @@
#include <linux/mm.h>
#include <linux/init.h>
-#include <mach/hardware.h>
#include <asm/pgtable.h>
#include <asm/mach/map.h>
#include <mach/common.h>
-
-/*!
- * @file mm.c
- *
- * @brief This file creates static virtual to physical mappings, common to all MX3 boards.
- *
- * @ingroup Memory
- */
+#include <mach/hardware.h>
/*!
* This table defines static virtual address mappings for I/O regions.
@@ -41,16 +33,40 @@
*/
static struct map_desc mxc_io_desc[] __initdata = {
{
- .virtual = X_MEMC_BASE_ADDR_VIRT,
- .pfn = __phys_to_pfn(X_MEMC_BASE_ADDR),
- .length = X_MEMC_SIZE,
- .type = MT_DEVICE
- }, {
- .virtual = AVIC_BASE_ADDR_VIRT,
- .pfn = __phys_to_pfn(AVIC_BASE_ADDR),
- .length = AVIC_SIZE,
- .type = MT_DEVICE_NONSHARED
- },
+ .virtual = IRAM_BASE_ADDR_VIRT & 0xFFF00000,
+ .pfn = __phys_to_pfn(IRAM_BASE_ADDR & 0xFFF00000),
+ .length = SZ_1M,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = X_MEMC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(X_MEMC_BASE_ADDR),
+ .length = X_MEMC_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = AVIC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AVIC_BASE_ADDR),
+ .length = AVIC_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = AIPS1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS1_BASE_ADDR),
+ .length = AIPS1_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = SPBA0_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(SPBA0_BASE_ADDR),
+ .length = SPBA0_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = AIPS2_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS2_BASE_ADDR),
+ .length = AIPS2_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = CS4_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(CS4_BASE_ADDR),
+ .length = CS4_SIZE,
+ .type = MT_DEVICE},
};
/*!
diff --git a/arch/arm/mach-mx3/mx31_pins.h b/arch/arm/mach-mx3/mx31_pins.h
new file mode 100644
index 000000000000..2e2274922069
--- /dev/null
+++ b/arch/arm/mach-mx3/mx31_pins.h
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MX31_PINS_H__
+#define __ASM_ARCH_MXC_MX31_PINS_H__
+
+/*!
+ * @file arch-mxc/mx31_pins.h
+ *
+ * @brief MX31 I/O Pin List
+ *
+ * @ingroup GPIO_MX31
+ */
+
+#ifndef __ASSEMBLY__
+
+/*!
+ * @name IOMUX/PAD Bit field definitions
+ */
+
+/*! @{ */
+
+/*!
+ * In order to identify pins more effectively, each mux-controlled pin's
+ * enumerated value is constructed in the following way:
+ *
+ * -------------------------------------------------------------------
+ * 31-29 | 28 - 24 |23 - 21| 20 | 19 - 18 | 17 - 10| 9 - 8 | 7 - 0
+ * -------------------------------------------------------------------
+ * IO_P | IO_I | RSVD | PAD_F | PAD_I | MUX_F | MUX_I
+ * -------------------------------------------------------------------
+ *
+ * Bit 0 to 7 contains MUX_I used to identify the register
+ * offset (0-based. base is IOMUX_module_base + 0xC) defined in the Section
+ * "sw_pad_ctl & sw_mux_ctl details" of the IC Spec. Bit 8 to 9 is MUX_F which
+ * contains the offset value defined WITHIN the same register (each IOMUX
+ * control register contains four 8-bit fields for four different pins). The
+ * similar field definitions are used for the pad control register.
+ * For example, the MX31_PIN_A0 is defined in the enumeration:
+ * ( 73 << MUX_I) | (0 << MUX_F)|( 98 << PAD_I) | (0 << PAD_F)
+ * It means the mux control register is at register offset 73. So the absolute
+ * address is: 0xC+73*4=0x130 0 << MUX_F means the control bits are at the
+ * least significant bits within the register. The pad control register offset
+ * is: 0x154+98*4=0x2DC and also occupy the least significant bits within the
+ * register.
+ */
+
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * MUX control register index (0-based)
+ */
+#define MUX_I 0
+
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * field within IOMUX control register for control bits
+ * (legal values are 0, 1, 2, 3)
+ */
+#define MUX_F 8
+
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * PAD control register index (0-based)
+ */
+#define PAD_I 10
+
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * field within PAD control register for control bits
+ * (legal values are 0, 1, 2)
+ */
+#define PAD_F 18
+
+#define _MXC_BUILD_PIN(gp,gi,mi,mf,pi,pf) \
+ ((gp) << MUX_IO_P) | ((gi) << MUX_IO_I) | ((mi) << MUX_I) | \
+ ((mf) << MUX_F) | ((pi) << PAD_I) | ((pf) << PAD_F)
+
+#define _MXC_BUILD_GPIO_PIN(gp,gi,mi,mf,pi,pf) \
+ _MXC_BUILD_PIN(gp,gi,mi,mf,pi,pf)
+#define _MXC_BUILD_NON_GPIO_PIN(mi,mf,pi,pf) \
+ _MXC_BUILD_PIN(7,0,mi,mf,pi,pf)
+
+/*!
+ * This enumeration is constructed based on the Section
+ * "sw_pad_ctl & sw_mux_ctl details" of the MX31 IC Spec. Each enumerated
+ * value is constructed based on the rules described above.
+ */
+enum iomux_pins {
+ MX31_PIN_CSPI3_MISO = _MXC_BUILD_NON_GPIO_PIN(0, 3, 1, 2),
+ MX31_PIN_CSPI3_SCLK = _MXC_BUILD_NON_GPIO_PIN(0, 2, 1, 1),
+ MX31_PIN_CSPI3_SPI_RDY = _MXC_BUILD_NON_GPIO_PIN(0, 1, 1, 0),
+ MX31_PIN_TTM_PAD = _MXC_BUILD_NON_GPIO_PIN(0, 0, 0, 2),
+ MX31_PIN_ATA_RESET_B = _MXC_BUILD_GPIO_PIN(2, 31, 1, 3, 3, 0),
+ MX31_PIN_CE_CONTROL = _MXC_BUILD_NON_GPIO_PIN(1, 2, 2, 2),
+ MX31_PIN_CLKSS = _MXC_BUILD_NON_GPIO_PIN(1, 1, 2, 1),
+ MX31_PIN_CSPI3_MOSI = _MXC_BUILD_NON_GPIO_PIN(1, 0, 2, 0),
+ MX31_PIN_ATA_CS1 = _MXC_BUILD_GPIO_PIN(2, 27, 2, 3, 4, 1),
+ MX31_PIN_ATA_DIOR = _MXC_BUILD_GPIO_PIN(2, 28, 2, 2, 4, 0),
+ MX31_PIN_ATA_DIOW = _MXC_BUILD_GPIO_PIN(2, 29, 2, 1, 3, 2),
+ MX31_PIN_ATA_DMACK = _MXC_BUILD_GPIO_PIN(2, 30, 2, 0, 3, 1),
+ MX31_PIN_SD1_DATA1 = _MXC_BUILD_GPIO_PIN(1, 29, 3, 3, 5, 2),
+ MX31_PIN_SD1_DATA2 = _MXC_BUILD_GPIO_PIN(1, 30, 3, 2, 5, 1),
+ MX31_PIN_SD1_DATA3 = _MXC_BUILD_GPIO_PIN(1, 31, 3, 1, 5, 0),
+ MX31_PIN_ATA_CS0 = _MXC_BUILD_GPIO_PIN(2, 26, 3, 0, 4, 2),
+ MX31_PIN_D3_SPL = _MXC_BUILD_NON_GPIO_PIN(4, 3, 7, 0),
+ MX31_PIN_SD1_CMD = _MXC_BUILD_GPIO_PIN(1, 26, 4, 2, 6, 2),
+ MX31_PIN_SD1_CLK = _MXC_BUILD_GPIO_PIN(1, 27, 4, 1, 6, 1),
+ MX31_PIN_SD1_DATA0 = _MXC_BUILD_GPIO_PIN(1, 28, 4, 0, 6, 0),
+ MX31_PIN_VSYNC3 = _MXC_BUILD_NON_GPIO_PIN(5, 3, 8, 1),
+ MX31_PIN_CONTRAST = _MXC_BUILD_NON_GPIO_PIN(5, 2, 8, 0),
+ MX31_PIN_D3_REV = _MXC_BUILD_NON_GPIO_PIN(5, 1, 7, 2),
+ MX31_PIN_D3_CLS = _MXC_BUILD_NON_GPIO_PIN(5, 0, 7, 1),
+ MX31_PIN_SER_RS = _MXC_BUILD_GPIO_PIN(2, 25, 6, 3, 9, 2),
+ MX31_PIN_PAR_RS = _MXC_BUILD_NON_GPIO_PIN(6, 2, 9, 1),
+ MX31_PIN_WRITE = _MXC_BUILD_NON_GPIO_PIN(6, 1, 9, 0),
+ MX31_PIN_READ = _MXC_BUILD_NON_GPIO_PIN(6, 0, 8, 2),
+ MX31_PIN_SD_D_IO = _MXC_BUILD_GPIO_PIN(2, 21, 7, 3, 11, 0),
+ MX31_PIN_SD_D_CLK = _MXC_BUILD_GPIO_PIN(2, 22, 7, 2, 10, 2),
+ MX31_PIN_LCS0 = _MXC_BUILD_GPIO_PIN(2, 23, 7, 1, 10, 1),
+ MX31_PIN_LCS1 = _MXC_BUILD_GPIO_PIN(2, 24, 7, 0, 10, 0),
+ MX31_PIN_HSYNC = _MXC_BUILD_NON_GPIO_PIN(8, 3, 12, 1),
+ MX31_PIN_FPSHIFT = _MXC_BUILD_NON_GPIO_PIN(8, 2, 12, 0),
+ MX31_PIN_DRDY0 = _MXC_BUILD_NON_GPIO_PIN(8, 1, 11, 2),
+ MX31_PIN_SD_D_I = _MXC_BUILD_GPIO_PIN(2, 20, 8, 0, 11, 1),
+ MX31_PIN_LD15 = _MXC_BUILD_NON_GPIO_PIN(9, 3, 13, 2),
+ MX31_PIN_LD16 = _MXC_BUILD_NON_GPIO_PIN(9, 2, 13, 1),
+ MX31_PIN_LD17 = _MXC_BUILD_NON_GPIO_PIN(9, 1, 13, 0),
+ MX31_PIN_VSYNC0 = _MXC_BUILD_NON_GPIO_PIN(9, 0, 12, 2),
+ MX31_PIN_LD11 = _MXC_BUILD_NON_GPIO_PIN(10, 3, 15, 0),
+ MX31_PIN_LD12 = _MXC_BUILD_NON_GPIO_PIN(10, 2, 14, 2),
+ MX31_PIN_LD13 = _MXC_BUILD_NON_GPIO_PIN(10, 1, 14, 1),
+ MX31_PIN_LD14 = _MXC_BUILD_NON_GPIO_PIN(10, 0, 14, 0),
+ MX31_PIN_LD7 = _MXC_BUILD_NON_GPIO_PIN(11, 3, 16, 1),
+ MX31_PIN_LD8 = _MXC_BUILD_NON_GPIO_PIN(11, 2, 16, 0),
+ MX31_PIN_LD9 = _MXC_BUILD_NON_GPIO_PIN(11, 1, 15, 2),
+ MX31_PIN_LD10 = _MXC_BUILD_NON_GPIO_PIN(11, 0, 15, 1),
+ MX31_PIN_LD3 = _MXC_BUILD_NON_GPIO_PIN(12, 3, 17, 2),
+ MX31_PIN_LD4 = _MXC_BUILD_NON_GPIO_PIN(12, 2, 17, 1),
+ MX31_PIN_LD5 = _MXC_BUILD_NON_GPIO_PIN(12, 1, 17, 0),
+ MX31_PIN_LD6 = _MXC_BUILD_NON_GPIO_PIN(12, 0, 16, 2),
+ MX31_PIN_USBH2_DATA1 = _MXC_BUILD_NON_GPIO_PIN(13, 3, 19, 0),
+ MX31_PIN_LD0 = _MXC_BUILD_NON_GPIO_PIN(13, 2, 18, 2),
+ MX31_PIN_LD1 = _MXC_BUILD_NON_GPIO_PIN(13, 1, 18, 1),
+ MX31_PIN_LD2 = _MXC_BUILD_NON_GPIO_PIN(13, 0, 18, 0),
+ MX31_PIN_USBH2_DIR = _MXC_BUILD_NON_GPIO_PIN(14, 3, 20, 1),
+ MX31_PIN_USBH2_STP = _MXC_BUILD_NON_GPIO_PIN(14, 2, 20, 0),
+ MX31_PIN_USBH2_NXT = _MXC_BUILD_NON_GPIO_PIN(14, 1, 19, 2),
+ MX31_PIN_USBH2_DATA0 = _MXC_BUILD_NON_GPIO_PIN(14, 0, 19, 1),
+ MX31_PIN_USBOTG_DATA5 = _MXC_BUILD_NON_GPIO_PIN(15, 3, 21, 2),
+ MX31_PIN_USBOTG_DATA6 = _MXC_BUILD_NON_GPIO_PIN(15, 2, 21, 1),
+ MX31_PIN_USBOTG_DATA7 = _MXC_BUILD_NON_GPIO_PIN(15, 1, 21, 0),
+ MX31_PIN_USBH2_CLK = _MXC_BUILD_NON_GPIO_PIN(15, 0, 20, 2),
+ MX31_PIN_USBOTG_DATA1 = _MXC_BUILD_NON_GPIO_PIN(16, 3, 23, 0),
+ MX31_PIN_USBOTG_DATA2 = _MXC_BUILD_NON_GPIO_PIN(16, 2, 22, 2),
+ MX31_PIN_USBOTG_DATA3 = _MXC_BUILD_NON_GPIO_PIN(16, 1, 22, 1),
+ MX31_PIN_USBOTG_DATA4 = _MXC_BUILD_NON_GPIO_PIN(16, 0, 22, 0),
+ MX31_PIN_USBOTG_DIR = _MXC_BUILD_NON_GPIO_PIN(17, 3, 24, 1),
+ MX31_PIN_USBOTG_STP = _MXC_BUILD_NON_GPIO_PIN(17, 2, 24, 0),
+ MX31_PIN_USBOTG_NXT = _MXC_BUILD_NON_GPIO_PIN(17, 1, 23, 2),
+ MX31_PIN_USBOTG_DATA0 = _MXC_BUILD_NON_GPIO_PIN(17, 0, 23, 1),
+ MX31_PIN_USB_PWR = _MXC_BUILD_GPIO_PIN(0, 29, 18, 3, 25, 2),
+ MX31_PIN_USB_OC = _MXC_BUILD_GPIO_PIN(0, 30, 18, 2, 25, 1),
+ MX31_PIN_USB_BYP = _MXC_BUILD_GPIO_PIN(0, 31, 18, 1, 25, 0),
+ MX31_PIN_USBOTG_CLK = _MXC_BUILD_NON_GPIO_PIN(18, 0, 24, 2),
+ MX31_PIN_TDO = _MXC_BUILD_NON_GPIO_PIN(19, 3, 27, 0),
+ MX31_PIN_TRSTB = _MXC_BUILD_NON_GPIO_PIN(19, 2, 26, 2),
+ MX31_PIN_DE_B = _MXC_BUILD_NON_GPIO_PIN(19, 1, 26, 1),
+ MX31_PIN_SJC_MOD = _MXC_BUILD_NON_GPIO_PIN(19, 0, 26, 0),
+ MX31_PIN_RTCK = _MXC_BUILD_NON_GPIO_PIN(20, 3, 28, 1),
+ MX31_PIN_TCK = _MXC_BUILD_NON_GPIO_PIN(20, 2, 28, 0),
+ MX31_PIN_TMS = _MXC_BUILD_NON_GPIO_PIN(20, 1, 27, 2),
+ MX31_PIN_TDI = _MXC_BUILD_NON_GPIO_PIN(20, 0, 27, 1),
+ MX31_PIN_KEY_COL4 = _MXC_BUILD_GPIO_PIN(1, 22, 21, 3, 29, 2),
+ MX31_PIN_KEY_COL5 = _MXC_BUILD_GPIO_PIN(1, 23, 21, 2, 29, 1),
+ MX31_PIN_KEY_COL6 = _MXC_BUILD_GPIO_PIN(1, 24, 21, 1, 29, 0),
+ MX31_PIN_KEY_COL7 = _MXC_BUILD_GPIO_PIN(1, 25, 21, 0, 28, 2),
+ MX31_PIN_KEY_COL0 = _MXC_BUILD_NON_GPIO_PIN(22, 3, 31, 0),
+ MX31_PIN_KEY_COL1 = _MXC_BUILD_NON_GPIO_PIN(22, 2, 30, 2),
+ MX31_PIN_KEY_COL2 = _MXC_BUILD_NON_GPIO_PIN(22, 1, 30, 1),
+ MX31_PIN_KEY_COL3 = _MXC_BUILD_NON_GPIO_PIN(22, 0, 30, 0),
+ MX31_PIN_KEY_ROW4 = _MXC_BUILD_GPIO_PIN(1, 18, 23, 3, 32, 1),
+ MX31_PIN_KEY_ROW5 = _MXC_BUILD_GPIO_PIN(1, 19, 23, 2, 32, 0),
+ MX31_PIN_KEY_ROW6 = _MXC_BUILD_GPIO_PIN(1, 20, 23, 1, 31, 2),
+ MX31_PIN_KEY_ROW7 = _MXC_BUILD_GPIO_PIN(1, 21, 23, 0, 31, 1),
+ MX31_PIN_KEY_ROW0 = _MXC_BUILD_NON_GPIO_PIN(24, 3, 33, 2),
+ MX31_PIN_KEY_ROW1 = _MXC_BUILD_NON_GPIO_PIN(24, 2, 33, 1),
+ MX31_PIN_KEY_ROW2 = _MXC_BUILD_NON_GPIO_PIN(24, 1, 33, 0),
+ MX31_PIN_KEY_ROW3 = _MXC_BUILD_NON_GPIO_PIN(24, 0, 32, 2),
+ MX31_PIN_TXD2 = _MXC_BUILD_GPIO_PIN(0, 28, 25, 3, 35, 0),
+ MX31_PIN_RTS2 = _MXC_BUILD_NON_GPIO_PIN(25, 2, 34, 2),
+ MX31_PIN_CTS2 = _MXC_BUILD_NON_GPIO_PIN(25, 1, 34, 1),
+ MX31_PIN_BATT_LINE = _MXC_BUILD_GPIO_PIN(1, 17, 25, 0, 34, 0),
+ MX31_PIN_RI_DTE1 = _MXC_BUILD_GPIO_PIN(1, 14, 26, 3, 36, 1),
+ MX31_PIN_DCD_DTE1 = _MXC_BUILD_GPIO_PIN(1, 15, 26, 2, 36, 0),
+ MX31_PIN_DTR_DCE2 = _MXC_BUILD_GPIO_PIN(1, 16, 26, 1, 35, 2),
+ MX31_PIN_RXD2 = _MXC_BUILD_GPIO_PIN(0, 27, 26, 0, 35, 1),
+ MX31_PIN_RI_DCE1 = _MXC_BUILD_GPIO_PIN(1, 10, 27, 3, 37, 2),
+ MX31_PIN_DCD_DCE1 = _MXC_BUILD_GPIO_PIN(1, 11, 27, 2, 37, 1),
+ MX31_PIN_DTR_DTE1 = _MXC_BUILD_GPIO_PIN(1, 12, 27, 1, 37, 0),
+ MX31_PIN_DSR_DTE1 = _MXC_BUILD_GPIO_PIN(1, 13, 27, 0, 36, 2),
+ MX31_PIN_RTS1 = _MXC_BUILD_GPIO_PIN(1, 6, 28, 3, 39, 0),
+ MX31_PIN_CTS1 = _MXC_BUILD_GPIO_PIN(1, 7, 28, 2, 38, 2),
+ MX31_PIN_DTR_DCE1 = _MXC_BUILD_GPIO_PIN(1, 8, 28, 1, 38, 1),
+ MX31_PIN_DSR_DCE1 = _MXC_BUILD_GPIO_PIN(1, 9, 28, 0, 38, 0),
+ MX31_PIN_CSPI2_SCLK = _MXC_BUILD_NON_GPIO_PIN(29, 3, 40, 1),
+ MX31_PIN_CSPI2_SPI_RDY = _MXC_BUILD_NON_GPIO_PIN(29, 2, 40, 0),
+ MX31_PIN_RXD1 = _MXC_BUILD_GPIO_PIN(1, 4, 29, 1, 39, 2),
+ MX31_PIN_TXD1 = _MXC_BUILD_GPIO_PIN(1, 5, 29, 0, 39, 1),
+ MX31_PIN_CSPI2_MISO = _MXC_BUILD_NON_GPIO_PIN(30, 3, 41, 2),
+ MX31_PIN_CSPI2_SS0 = _MXC_BUILD_NON_GPIO_PIN(30, 2, 41, 1),
+ MX31_PIN_CSPI2_SS1 = _MXC_BUILD_NON_GPIO_PIN(30, 1, 41, 0),
+ MX31_PIN_CSPI2_SS2 = _MXC_BUILD_NON_GPIO_PIN(30, 0, 40, 2),
+ MX31_PIN_CSPI1_SS2 = _MXC_BUILD_NON_GPIO_PIN(31, 3, 43, 0),
+ MX31_PIN_CSPI1_SCLK = _MXC_BUILD_NON_GPIO_PIN(31, 2, 42, 2),
+ MX31_PIN_CSPI1_SPI_RDY = _MXC_BUILD_NON_GPIO_PIN(31, 1, 42, 1),
+ MX31_PIN_CSPI2_MOSI = _MXC_BUILD_NON_GPIO_PIN(31, 0, 42, 0),
+ MX31_PIN_CSPI1_MOSI = _MXC_BUILD_NON_GPIO_PIN(32, 3, 44, 1),
+ MX31_PIN_CSPI1_MISO = _MXC_BUILD_NON_GPIO_PIN(32, 2, 44, 0),
+ MX31_PIN_CSPI1_SS0 = _MXC_BUILD_NON_GPIO_PIN(32, 1, 43, 2),
+ MX31_PIN_CSPI1_SS1 = _MXC_BUILD_NON_GPIO_PIN(32, 0, 43, 1),
+ MX31_PIN_STXD6 = _MXC_BUILD_GPIO_PIN(0, 23, 33, 3, 45, 2),
+ MX31_PIN_SRXD6 = _MXC_BUILD_GPIO_PIN(0, 24, 33, 2, 45, 1),
+ MX31_PIN_SCK6 = _MXC_BUILD_GPIO_PIN(0, 25, 33, 1, 45, 0),
+ MX31_PIN_SFS6 = _MXC_BUILD_GPIO_PIN(0, 26, 33, 0, 44, 2),
+ MX31_PIN_STXD5 = _MXC_BUILD_GPIO_PIN(0, 21, 34, 3, 47, 0),
+ MX31_PIN_SRXD5 = _MXC_BUILD_GPIO_PIN(0, 22, 34, 2, 46, 2),
+ MX31_PIN_SCK5 = _MXC_BUILD_NON_GPIO_PIN(34, 1, 46, 1),
+ MX31_PIN_SFS5 = _MXC_BUILD_NON_GPIO_PIN(34, 0, 46, 0),
+ MX31_PIN_STXD4 = _MXC_BUILD_GPIO_PIN(0, 19, 35, 3, 48, 1),
+ MX31_PIN_SRXD4 = _MXC_BUILD_GPIO_PIN(0, 20, 35, 2, 48, 0),
+ MX31_PIN_SCK4 = _MXC_BUILD_NON_GPIO_PIN(35, 1, 47, 2),
+ MX31_PIN_SFS4 = _MXC_BUILD_NON_GPIO_PIN(35, 0, 47, 1),
+ MX31_PIN_STXD3 = _MXC_BUILD_GPIO_PIN(0, 17, 36, 3, 49, 2),
+ MX31_PIN_SRXD3 = _MXC_BUILD_GPIO_PIN(0, 18, 36, 2, 49, 1),
+ MX31_PIN_SCK3 = _MXC_BUILD_NON_GPIO_PIN(36, 1, 49, 0),
+ MX31_PIN_SFS3 = _MXC_BUILD_NON_GPIO_PIN(36, 0, 48, 2),
+ MX31_PIN_CSI_HSYNC = _MXC_BUILD_GPIO_PIN(2, 18, 37, 3, 51, 0),
+ MX31_PIN_CSI_PIXCLK = _MXC_BUILD_GPIO_PIN(2, 19, 37, 2, 50, 2),
+ MX31_PIN_I2C_CLK = _MXC_BUILD_NON_GPIO_PIN(37, 1, 50, 1),
+ MX31_PIN_I2C_DAT = _MXC_BUILD_NON_GPIO_PIN(37, 0, 50, 0),
+ MX31_PIN_CSI_D14 = _MXC_BUILD_GPIO_PIN(2, 14, 38, 3, 52, 1),
+ MX31_PIN_CSI_D15 = _MXC_BUILD_GPIO_PIN(2, 15, 38, 2, 52, 0),
+ MX31_PIN_CSI_MCLK = _MXC_BUILD_GPIO_PIN(2, 16, 38, 1, 51, 2),
+ MX31_PIN_CSI_VSYNC = _MXC_BUILD_GPIO_PIN(2, 17, 38, 0, 51, 1),
+ MX31_PIN_CSI_D10 = _MXC_BUILD_GPIO_PIN(2, 10, 39, 3, 53, 2),
+ MX31_PIN_CSI_D11 = _MXC_BUILD_GPIO_PIN(2, 11, 39, 2, 53, 1),
+ MX31_PIN_CSI_D12 = _MXC_BUILD_GPIO_PIN(2, 12, 39, 1, 53, 0),
+ MX31_PIN_CSI_D13 = _MXC_BUILD_GPIO_PIN(2, 13, 39, 0, 52, 2),
+ MX31_PIN_CSI_D6 = _MXC_BUILD_GPIO_PIN(2, 6, 40, 3, 55, 0),
+ MX31_PIN_CSI_D7 = _MXC_BUILD_GPIO_PIN(2, 7, 40, 2, 54, 2),
+ MX31_PIN_CSI_D8 = _MXC_BUILD_GPIO_PIN(2, 8, 40, 1, 54, 1),
+ MX31_PIN_CSI_D9 = _MXC_BUILD_GPIO_PIN(2, 9, 40, 0, 54, 0),
+ MX31_PIN_M_REQUEST = _MXC_BUILD_NON_GPIO_PIN(41, 3, 56, 1),
+ MX31_PIN_M_GRANT = _MXC_BUILD_NON_GPIO_PIN(41, 2, 56, 0),
+ MX31_PIN_CSI_D4 = _MXC_BUILD_GPIO_PIN(2, 4, 41, 1, 55, 2),
+ MX31_PIN_CSI_D5 = _MXC_BUILD_GPIO_PIN(2, 5, 41, 0, 55, 1),
+ MX31_PIN_PC_RST = _MXC_BUILD_NON_GPIO_PIN(42, 3, 57, 2),
+ MX31_PIN_IOIS16 = _MXC_BUILD_NON_GPIO_PIN(42, 2, 57, 1),
+ MX31_PIN_PC_RW_B = _MXC_BUILD_NON_GPIO_PIN(42, 1, 57, 0),
+ MX31_PIN_PC_POE = _MXC_BUILD_NON_GPIO_PIN(42, 0, 56, 2),
+ MX31_PIN_PC_VS1 = _MXC_BUILD_NON_GPIO_PIN(43, 3, 59, 0),
+ MX31_PIN_PC_VS2 = _MXC_BUILD_NON_GPIO_PIN(43, 2, 58, 2),
+ MX31_PIN_PC_BVD1 = _MXC_BUILD_NON_GPIO_PIN(43, 1, 58, 1),
+ MX31_PIN_PC_BVD2 = _MXC_BUILD_NON_GPIO_PIN(43, 0, 58, 0),
+ MX31_PIN_PC_CD2_B = _MXC_BUILD_NON_GPIO_PIN(44, 3, 60, 1),
+ MX31_PIN_PC_WAIT_B = _MXC_BUILD_NON_GPIO_PIN(44, 2, 60, 0),
+ MX31_PIN_PC_READY = _MXC_BUILD_NON_GPIO_PIN(44, 1, 59, 2),
+ MX31_PIN_PC_PWRON = _MXC_BUILD_NON_GPIO_PIN(44, 0, 59, 1),
+ MX31_PIN_D2 = _MXC_BUILD_NON_GPIO_PIN(45, 3, 61, 2),
+ MX31_PIN_D1 = _MXC_BUILD_NON_GPIO_PIN(45, 2, 61, 1),
+ MX31_PIN_D0 = _MXC_BUILD_NON_GPIO_PIN(45, 1, 61, 0),
+ MX31_PIN_PC_CD1_B = _MXC_BUILD_NON_GPIO_PIN(45, 0, 60, 2),
+ MX31_PIN_D6 = _MXC_BUILD_NON_GPIO_PIN(46, 3, 63, 0),
+ MX31_PIN_D5 = _MXC_BUILD_NON_GPIO_PIN(46, 2, 62, 2),
+ MX31_PIN_D4 = _MXC_BUILD_NON_GPIO_PIN(46, 1, 62, 1),
+ MX31_PIN_D3 = _MXC_BUILD_NON_GPIO_PIN(46, 0, 62, 0),
+ MX31_PIN_D10 = _MXC_BUILD_NON_GPIO_PIN(47, 3, 64, 1),
+ MX31_PIN_D9 = _MXC_BUILD_NON_GPIO_PIN(47, 2, 64, 0),
+ MX31_PIN_D8 = _MXC_BUILD_NON_GPIO_PIN(47, 1, 63, 2),
+ MX31_PIN_D7 = _MXC_BUILD_NON_GPIO_PIN(47, 0, 63, 1),
+ MX31_PIN_D14 = _MXC_BUILD_NON_GPIO_PIN(48, 3, 65, 2),
+ MX31_PIN_D13 = _MXC_BUILD_NON_GPIO_PIN(48, 2, 65, 1),
+ MX31_PIN_D12 = _MXC_BUILD_NON_GPIO_PIN(48, 1, 65, 0),
+ MX31_PIN_D11 = _MXC_BUILD_NON_GPIO_PIN(48, 0, 64, 2),
+ MX31_PIN_NFWP_B = _MXC_BUILD_GPIO_PIN(0, 14, 49, 3, 67, 0),
+ MX31_PIN_NFCE_B = _MXC_BUILD_GPIO_PIN(0, 15, 49, 2, 66, 2),
+ MX31_PIN_NFRB = _MXC_BUILD_GPIO_PIN(0, 16, 49, 1, 66, 1),
+ MX31_PIN_D15 = _MXC_BUILD_NON_GPIO_PIN(49, 0, 66, 0),
+ MX31_PIN_NFWE_B = _MXC_BUILD_GPIO_PIN(0, 10, 50, 3, 68, 1),
+ MX31_PIN_NFRE_B = _MXC_BUILD_GPIO_PIN(0, 11, 50, 2, 68, 0),
+ MX31_PIN_NFALE = _MXC_BUILD_GPIO_PIN(0, 12, 50, 1, 67, 2),
+ MX31_PIN_NFCLE = _MXC_BUILD_GPIO_PIN(0, 13, 50, 0, 67, 1),
+ MX31_PIN_SDQS0 = _MXC_BUILD_NON_GPIO_PIN(51, 3, 69, 2),
+ MX31_PIN_SDQS1 = _MXC_BUILD_NON_GPIO_PIN(51, 2, 69, 1),
+ MX31_PIN_SDQS2 = _MXC_BUILD_NON_GPIO_PIN(51, 1, 69, 0),
+ MX31_PIN_SDQS3 = _MXC_BUILD_NON_GPIO_PIN(51, 0, 68, 2),
+ MX31_PIN_SDCKE0 = _MXC_BUILD_NON_GPIO_PIN(52, 3, 71, 0),
+ MX31_PIN_SDCKE1 = _MXC_BUILD_NON_GPIO_PIN(52, 2, 70, 2),
+ MX31_PIN_SDCLK = _MXC_BUILD_NON_GPIO_PIN(52, 1, 70, 1),
+ MX31_PIN_SDCLK_B = _MXC_BUILD_NON_GPIO_PIN(52, 0, 70, 0),
+ MX31_PIN_RW = _MXC_BUILD_NON_GPIO_PIN(53, 3, 72, 1),
+ MX31_PIN_RAS = _MXC_BUILD_NON_GPIO_PIN(53, 2, 72, 0),
+ MX31_PIN_CAS = _MXC_BUILD_NON_GPIO_PIN(53, 1, 71, 2),
+ MX31_PIN_SDWE = _MXC_BUILD_NON_GPIO_PIN(53, 0, 71, 1),
+ MX31_PIN_CS5 = _MXC_BUILD_NON_GPIO_PIN(54, 3, 73, 2),
+ MX31_PIN_ECB = _MXC_BUILD_NON_GPIO_PIN(54, 2, 73, 1),
+ MX31_PIN_LBA = _MXC_BUILD_NON_GPIO_PIN(54, 1, 73, 0),
+ MX31_PIN_BCLK = _MXC_BUILD_NON_GPIO_PIN(54, 0, 72, 2),
+ MX31_PIN_CS1 = _MXC_BUILD_NON_GPIO_PIN(55, 3, 75, 0),
+ MX31_PIN_CS2 = _MXC_BUILD_NON_GPIO_PIN(55, 2, 74, 2),
+ MX31_PIN_CS3 = _MXC_BUILD_NON_GPIO_PIN(55, 1, 74, 1),
+ MX31_PIN_CS4 = _MXC_BUILD_NON_GPIO_PIN(55, 0, 74, 0),
+ MX31_PIN_EB0 = _MXC_BUILD_NON_GPIO_PIN(56, 3, 76, 1),
+ MX31_PIN_EB1 = _MXC_BUILD_NON_GPIO_PIN(56, 2, 76, 0),
+ MX31_PIN_OE = _MXC_BUILD_NON_GPIO_PIN(56, 1, 75, 2),
+ MX31_PIN_CS0 = _MXC_BUILD_NON_GPIO_PIN(56, 0, 75, 1),
+ MX31_PIN_DQM0 = _MXC_BUILD_NON_GPIO_PIN(57, 3, 77, 2),
+ MX31_PIN_DQM1 = _MXC_BUILD_NON_GPIO_PIN(57, 2, 77, 1),
+ MX31_PIN_DQM2 = _MXC_BUILD_NON_GPIO_PIN(57, 1, 77, 0),
+ MX31_PIN_DQM3 = _MXC_BUILD_NON_GPIO_PIN(57, 0, 76, 2),
+ MX31_PIN_SD28 = _MXC_BUILD_NON_GPIO_PIN(58, 3, 79, 0),
+ MX31_PIN_SD29 = _MXC_BUILD_NON_GPIO_PIN(58, 2, 78, 2),
+ MX31_PIN_SD30 = _MXC_BUILD_NON_GPIO_PIN(58, 1, 78, 1),
+ MX31_PIN_SD31 = _MXC_BUILD_NON_GPIO_PIN(58, 0, 78, 0),
+ MX31_PIN_SD24 = _MXC_BUILD_NON_GPIO_PIN(59, 3, 80, 1),
+ MX31_PIN_SD25 = _MXC_BUILD_NON_GPIO_PIN(59, 2, 80, 0),
+ MX31_PIN_SD26 = _MXC_BUILD_NON_GPIO_PIN(59, 1, 79, 2),
+ MX31_PIN_SD27 = _MXC_BUILD_NON_GPIO_PIN(59, 0, 79, 1),
+ MX31_PIN_SD20 = _MXC_BUILD_NON_GPIO_PIN(60, 3, 81, 2),
+ MX31_PIN_SD21 = _MXC_BUILD_NON_GPIO_PIN(60, 2, 81, 1),
+ MX31_PIN_SD22 = _MXC_BUILD_NON_GPIO_PIN(60, 1, 81, 0),
+ MX31_PIN_SD23 = _MXC_BUILD_NON_GPIO_PIN(60, 0, 80, 2),
+ MX31_PIN_SD16 = _MXC_BUILD_NON_GPIO_PIN(61, 3, 83, 0),
+ MX31_PIN_SD17 = _MXC_BUILD_NON_GPIO_PIN(61, 2, 82, 2),
+ MX31_PIN_SD18 = _MXC_BUILD_NON_GPIO_PIN(61, 1, 82, 1),
+ MX31_PIN_SD19 = _MXC_BUILD_NON_GPIO_PIN(61, 0, 82, 0),
+ MX31_PIN_SD12 = _MXC_BUILD_NON_GPIO_PIN(62, 3, 84, 1),
+ MX31_PIN_SD13 = _MXC_BUILD_NON_GPIO_PIN(62, 2, 84, 0),
+ MX31_PIN_SD14 = _MXC_BUILD_NON_GPIO_PIN(62, 1, 83, 2),
+ MX31_PIN_SD15 = _MXC_BUILD_NON_GPIO_PIN(62, 0, 83, 1),
+ MX31_PIN_SD8 = _MXC_BUILD_NON_GPIO_PIN(63, 3, 85, 2),
+ MX31_PIN_SD9 = _MXC_BUILD_NON_GPIO_PIN(63, 2, 85, 1),
+ MX31_PIN_SD10 = _MXC_BUILD_NON_GPIO_PIN(63, 1, 85, 0),
+ MX31_PIN_SD11 = _MXC_BUILD_NON_GPIO_PIN(63, 0, 84, 2),
+ MX31_PIN_SD4 = _MXC_BUILD_NON_GPIO_PIN(64, 3, 87, 0),
+ MX31_PIN_SD5 = _MXC_BUILD_NON_GPIO_PIN(64, 2, 86, 2),
+ MX31_PIN_SD6 = _MXC_BUILD_NON_GPIO_PIN(64, 1, 86, 1),
+ MX31_PIN_SD7 = _MXC_BUILD_NON_GPIO_PIN(64, 0, 86, 0),
+ MX31_PIN_SD0 = _MXC_BUILD_NON_GPIO_PIN(65, 3, 88, 1),
+ MX31_PIN_SD1 = _MXC_BUILD_NON_GPIO_PIN(65, 2, 88, 0),
+ MX31_PIN_SD2 = _MXC_BUILD_NON_GPIO_PIN(65, 1, 87, 2),
+ MX31_PIN_SD3 = _MXC_BUILD_NON_GPIO_PIN(65, 0, 87, 1),
+ MX31_PIN_A24 = _MXC_BUILD_NON_GPIO_PIN(66, 3, 89, 2),
+ MX31_PIN_A25 = _MXC_BUILD_NON_GPIO_PIN(66, 2, 89, 1),
+ MX31_PIN_SDBA1 = _MXC_BUILD_NON_GPIO_PIN(66, 1, 89, 0),
+ MX31_PIN_SDBA0 = _MXC_BUILD_NON_GPIO_PIN(66, 0, 88, 2),
+ MX31_PIN_A20 = _MXC_BUILD_NON_GPIO_PIN(67, 3, 91, 0),
+ MX31_PIN_A21 = _MXC_BUILD_NON_GPIO_PIN(67, 2, 90, 2),
+ MX31_PIN_A22 = _MXC_BUILD_NON_GPIO_PIN(67, 1, 90, 1),
+ MX31_PIN_A23 = _MXC_BUILD_NON_GPIO_PIN(67, 0, 90, 0),
+ MX31_PIN_A16 = _MXC_BUILD_NON_GPIO_PIN(68, 3, 92, 1),
+ MX31_PIN_A17 = _MXC_BUILD_NON_GPIO_PIN(68, 2, 92, 0),
+ MX31_PIN_A18 = _MXC_BUILD_NON_GPIO_PIN(68, 1, 91, 2),
+ MX31_PIN_A19 = _MXC_BUILD_NON_GPIO_PIN(68, 0, 91, 1),
+ MX31_PIN_A12 = _MXC_BUILD_NON_GPIO_PIN(69, 3, 93, 2),
+ MX31_PIN_A13 = _MXC_BUILD_NON_GPIO_PIN(69, 2, 93, 1),
+ MX31_PIN_A14 = _MXC_BUILD_NON_GPIO_PIN(69, 1, 93, 0),
+ MX31_PIN_A15 = _MXC_BUILD_NON_GPIO_PIN(69, 0, 92, 2),
+ MX31_PIN_A9 = _MXC_BUILD_NON_GPIO_PIN(70, 3, 95, 0),
+ MX31_PIN_A10 = _MXC_BUILD_NON_GPIO_PIN(70, 2, 94, 2),
+ MX31_PIN_MA10 = _MXC_BUILD_NON_GPIO_PIN(70, 1, 94, 1),
+ MX31_PIN_A11 = _MXC_BUILD_NON_GPIO_PIN(70, 0, 94, 0),
+ MX31_PIN_A5 = _MXC_BUILD_NON_GPIO_PIN(71, 3, 96, 1),
+ MX31_PIN_A6 = _MXC_BUILD_NON_GPIO_PIN(71, 2, 96, 0),
+ MX31_PIN_A7 = _MXC_BUILD_NON_GPIO_PIN(71, 1, 95, 2),
+ MX31_PIN_A8 = _MXC_BUILD_NON_GPIO_PIN(71, 0, 95, 1),
+ MX31_PIN_A1 = _MXC_BUILD_NON_GPIO_PIN(72, 3, 97, 2),
+ MX31_PIN_A2 = _MXC_BUILD_NON_GPIO_PIN(72, 2, 97, 1),
+ MX31_PIN_A3 = _MXC_BUILD_NON_GPIO_PIN(72, 1, 97, 0),
+ MX31_PIN_A4 = _MXC_BUILD_NON_GPIO_PIN(72, 0, 96, 2),
+ MX31_PIN_DVFS1 = _MXC_BUILD_NON_GPIO_PIN(73, 3, 99, 0),
+ MX31_PIN_VPG0 = _MXC_BUILD_NON_GPIO_PIN(73, 2, 98, 2),
+ MX31_PIN_VPG1 = _MXC_BUILD_NON_GPIO_PIN(73, 1, 98, 1),
+ MX31_PIN_A0 = _MXC_BUILD_NON_GPIO_PIN(73, 0, 98, 0),
+ MX31_PIN_CKIL = _MXC_BUILD_NON_GPIO_PIN(74, 3, 100, 1),
+ MX31_PIN_POWER_FAIL = _MXC_BUILD_NON_GPIO_PIN(74, 2, 100, 0),
+ MX31_PIN_VSTBY = _MXC_BUILD_NON_GPIO_PIN(74, 1, 99, 2),
+ MX31_PIN_DVFS0 = _MXC_BUILD_NON_GPIO_PIN(74, 0, 99, 1),
+ MX31_PIN_BOOT_MODE1 = _MXC_BUILD_NON_GPIO_PIN(75, 3, 101, 2),
+ MX31_PIN_BOOT_MODE2 = _MXC_BUILD_NON_GPIO_PIN(75, 2, 101, 1),
+ MX31_PIN_BOOT_MODE3 = _MXC_BUILD_NON_GPIO_PIN(75, 1, 101, 0),
+ MX31_PIN_BOOT_MODE4 = _MXC_BUILD_NON_GPIO_PIN(75, 0, 100, 2),
+ MX31_PIN_RESET_IN_B = _MXC_BUILD_NON_GPIO_PIN(76, 3, 103, 0),
+ MX31_PIN_POR_B = _MXC_BUILD_NON_GPIO_PIN(76, 2, 102, 2),
+ MX31_PIN_CLKO = _MXC_BUILD_NON_GPIO_PIN(76, 1, 102, 1),
+ MX31_PIN_BOOT_MODE0 = _MXC_BUILD_NON_GPIO_PIN(76, 0, 102, 0),
+ MX31_PIN_STX0 = _MXC_BUILD_GPIO_PIN(1, 1, 77, 3, 104, 1),
+ MX31_PIN_SRX0 = _MXC_BUILD_GPIO_PIN(1, 2, 77, 2, 104, 0),
+ MX31_PIN_SIMPD0 = _MXC_BUILD_GPIO_PIN(1, 3, 77, 1, 103, 2),
+ MX31_PIN_CKIH = _MXC_BUILD_NON_GPIO_PIN(77, 0, 103, 1),
+ MX31_PIN_GPIO3_1 = _MXC_BUILD_GPIO_PIN(2, 1, 78, 3, 105, 2),
+ MX31_PIN_SCLK0 = _MXC_BUILD_GPIO_PIN(2, 2, 78, 2, 105, 1),
+ MX31_PIN_SRST0 = _MXC_BUILD_GPIO_PIN(2, 3, 78, 1, 105, 0),
+ MX31_PIN_SVEN0 = _MXC_BUILD_GPIO_PIN(1, 0, 78, 0, 104, 2),
+ MX31_PIN_GPIO1_4 = _MXC_BUILD_GPIO_PIN(0, 4, 79, 3, 107, 0),
+ MX31_PIN_GPIO1_5 = _MXC_BUILD_GPIO_PIN(0, 5, 79, 2, 106, 2),
+ MX31_PIN_GPIO1_6 = _MXC_BUILD_GPIO_PIN(0, 6, 79, 1, 106, 1),
+ MX31_PIN_GPIO3_0 = _MXC_BUILD_GPIO_PIN(2, 0, 79, 0, 106, 0),
+ MX31_PIN_GPIO1_0 = _MXC_BUILD_GPIO_PIN(0, 0, 80, 3, 108, 1),
+ MX31_PIN_GPIO1_1 = _MXC_BUILD_GPIO_PIN(0, 1, 80, 2, 108, 0),
+ MX31_PIN_GPIO1_2 = _MXC_BUILD_GPIO_PIN(0, 2, 80, 1, 107, 2),
+ MX31_PIN_GPIO1_3 = _MXC_BUILD_GPIO_PIN(0, 3, 80, 0, 107, 1),
+ MX31_PIN_CAPTURE = _MXC_BUILD_GPIO_PIN(0, 7, 81, 3, 109, 2),
+ MX31_PIN_COMPARE = _MXC_BUILD_GPIO_PIN(0, 8, 81, 2, 109, 1),
+ MX31_PIN_WATCHDOG_RST = _MXC_BUILD_NON_GPIO_PIN(81, 1, 109, 0),
+ MX31_PIN_PWMO = _MXC_BUILD_GPIO_PIN(0, 9, 81, 0, 108, 2),
+};
+
+#endif
+#endif
diff --git a/arch/arm/mach-mx3/mx31ads.c b/arch/arm/mach-mx3/mx31ads.c
index f902a7c37c31..f9d9b1ea802c 100644
--- a/arch/arm/mach-mx3/mx31ads.c
+++ b/arch/arm/mach-mx3/mx31ads.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2000 Deep Blue Solutions Ltd
* Copyright (C) 2002 Shane Nay (shane@minirl.com)
- * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,31 +19,139 @@
*/
#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
#include <linux/init.h>
-#include <linux/clk.h>
#include <linux/serial_8250.h>
-#include <linux/irq.h>
+#include <linux/input.h>
+#include <linux/nodemask.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/ata.h>
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/mach/flash.h>
+#endif
#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/keypad.h>
#include <asm/mach/time.h>
-#include <asm/memory.h>
-#include <asm/mach/map.h>
#include <mach/common.h>
-#include <mach/board-mx31ads.h>
-#include <mach/imx-uart.h>
-#include <mach/iomux-mx3.h>
-
-#include "devices.h"
+#include <mach/memory.h>
+#include <mach/gpio.h>
+#include <mach/mmc.h>
+#include <mach/spba.h>
+#include "board-mx31ads.h"
+#include "crm_regs.h"
+#include "iomux.h"
/*!
- * @file mx31ads.c
+ * @file mach-mx3/mx31ads.c
*
* @brief This file contains the board-specific initialization routines.
*
- * @ingroup System
+ * @ingroup MSL_MX31
+ */
+
+extern void mx31ads_gpio_init(void) __init;
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+#if defined(CONFIG_CS89x0) || defined(CONFIG_CS89x0_MODULE)
+/*! Null terminated portlist used to probe for the CS8900A device on ISA Bus
+ * Add 3 to reset the page window before probing (fixes eth probe when deployed
+ * using nand_boot)
+ */
+unsigned int netcard_portlist[] = { CS8900A_BASE_ADDRESS + 3, 0 };
+
+EXPORT_SYMBOL(netcard_portlist);
+/*!
+ * The CS8900A has 4 IRQ pins, which is software selectable, CS8900A interrupt
+ * pin 0 is used for interrupt generation.
+ */
+unsigned int cs8900_irq_map[] = { CS8900AIRQ, 0, 0, 0 };
+
+EXPORT_SYMBOL(cs8900_irq_map);
+#endif
+
+#if defined(CONFIG_KEYBOARD_MXC) || defined(CONFIG_KEYBOARD_MXC_MODULE)
+
+/* Keypad keycodes for the EVB 8x8
+ * keypad. POWER and PTT keys don't generate
+ * any interrupts via this driver so they are
+ * not support. Change any keys as u like!
*/
+static u16 keymapping[64] = {
+ KEY_SELECT, KEY_LEFT, KEY_DOWN, KEY_RIGHT,
+ KEY_UP, KEY_F12, KEY_END, KEY_BACK,
+ KEY_F1, KEY_SENDFILE, KEY_HOME, KEY_F6,
+ KEY_VOLUMEUP, KEY_F8, KEY_F9, KEY_F10,
+ KEY_3, KEY_2, KEY_1, KEY_4,
+ KEY_VOLUMEDOWN, KEY_7, KEY_5, KEY_6,
+ KEY_9, KEY_LEFTSHIFT, KEY_8, KEY_0,
+ KEY_KPASTERISK, KEY_RECORD, KEY_Q, KEY_W,
+ KEY_A, KEY_S, KEY_D, KEY_E,
+ KEY_F, KEY_R, KEY_T, KEY_Y,
+ KEY_TAB, KEY_F7, KEY_CAPSLOCK, KEY_Z,
+ KEY_X, KEY_C, KEY_V, KEY_G,
+ KEY_B, KEY_H, KEY_N, KEY_M,
+ KEY_J, KEY_K, KEY_U, KEY_I,
+ KEY_SPACE, KEY_F2, KEY_DOT, KEY_ENTER,
+ KEY_L, KEY_BACKSPACE, KEY_P, KEY_O,
+};
+
+static struct resource mxc_kpp_resources[] = {
+ [0] = {
+ .start = MXC_INT_KPP,
+ .end = MXC_INT_KPP,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct keypad_data evb_8_by_8_keypad = {
+ .rowmax = 8,
+ .colmax = 8,
+ .irq = MXC_INT_KPP,
+ .learning = 0,
+ .delay = 2,
+ .matrix = keymapping,
+};
+
+/* mxc keypad driver */
+static struct platform_device mxc_keypad_device = {
+ .name = "mxc_keypad",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_kpp_resources),
+ .resource = mxc_kpp_resources,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &evb_8_by_8_keypad,
+ },
+};
+
+static void mxc_init_keypad(void)
+{
+ (void)platform_device_register(&mxc_keypad_device);
+}
+#else
+static inline void mxc_init_keypad(void)
+{
+}
+#endif
#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE)
/*!
@@ -51,31 +159,30 @@
*/
static struct plat_serial8250_port serial_platform_data[] = {
{
- .membase = (void *)(PBC_BASE_ADDRESS + PBC_SC16C652_UARTA),
- .mapbase = (unsigned long)(CS4_BASE_ADDR + PBC_SC16C652_UARTA),
- .irq = EXPIO_INT_XUART_INTA,
- .uartclk = 14745600,
- .regshift = 0,
- .iotype = UPIO_MEM,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ,
- }, {
- .membase = (void *)(PBC_BASE_ADDRESS + PBC_SC16C652_UARTB),
- .mapbase = (unsigned long)(CS4_BASE_ADDR + PBC_SC16C652_UARTB),
- .irq = EXPIO_INT_XUART_INTB,
- .uartclk = 14745600,
- .regshift = 0,
- .iotype = UPIO_MEM,
- .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ,
- },
+ .membase = (void *)(PBC_BASE_ADDRESS + PBC_SC16C652_UARTA),
+ .mapbase = (unsigned long)(CS4_BASE_ADDR + PBC_SC16C652_UARTA),
+ .irq = EXPIO_INT_XUART_INTA,
+ .uartclk = 14745600,
+ .regshift = 0,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ,},
+ {
+ .membase = (void *)(PBC_BASE_ADDRESS + PBC_SC16C652_UARTB),
+ .mapbase = (unsigned long)(CS4_BASE_ADDR + PBC_SC16C652_UARTB),
+ .irq = EXPIO_INT_XUART_INTB,
+ .uartclk = 14745600,
+ .regshift = 0,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ,},
{},
};
static struct platform_device serial_device = {
- .name = "serial8250",
- .id = 0,
- .dev = {
+ .name = "serial8250",
+ .id = 0,
+ .dev = {
.platform_data = serial_platform_data,
- },
+ },
};
static int __init mxc_init_extuart(void)
@@ -88,71 +195,534 @@ static inline int mxc_init_extuart(void)
return 0;
}
#endif
+/* MTD NOR flash */
+
+#if defined(CONFIG_MTD_MXC) || defined(CONFIG_MTD_MXC_MODULE)
+
+static struct mtd_partition mxc_nor_partitions[] = {
+ {
+ .name = "Bootloader",
+ .size = 512 * 1024,
+ .offset = 0x00000000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "nor.Kernel",
+ .size = 2 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = 0},
+ {
+ .name = "nor.userfs",
+ .size = 14 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = 0},
+ {
+ .name = "nor.rootfs",
+ .size = 12 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE},
+ {
+ .name = "FIS directory",
+ .size = 12 * 1024,
+ .offset = 0x01FE0000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "Redboot config",
+ .size = MTDPART_SIZ_FULL,
+ .offset = 0x01FFF000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+};
+
+static struct flash_platform_data mxc_flash_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+ .parts = mxc_nor_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_nor_partitions),
+};
+
+static struct resource mxc_flash_resource = {
+ .start = 0xa0000000,
+ .end = 0xa0000000 + 0x02000000 - 1,
+ .flags = IORESOURCE_MEM,
+
+};
+
+static struct platform_device mxc_nor_mtd_device = {
+ .name = "mxc_nor_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &mxc_flash_resource,
+};
+
+static void mxc_init_nor_mtd(void)
+{
+ (void)platform_device_register(&mxc_nor_mtd_device);
+}
+#else
+static void mxc_init_nor_mtd(void)
+{
+}
+#endif
-#if defined(CONFIG_SERIAL_IMX) || defined(CONFIG_SERIAL_IMX_MODULE)
-static struct imxuart_platform_data uart_pdata = {
- .flags = IMXUART_HAVE_RTSCTS,
+/* NAND Flash Partitions */
+#ifdef CONFIG_MTD_PARTITIONS
+
+static struct mtd_partition nand_flash_partitions[4] = {
+ {
+ .name = "nand.bootloader",
+ .offset = 0,
+ .size = 1024 * 1024},
+ {
+ .name = "nand.kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 5 * 1024 * 1024},
+ {
+ .name = "nand.rootfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 22 * 1024 * 1024},
+ {
+ .name = "nand.userfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL},
};
-static inline void mxc_init_imx_uart(void)
+#endif
+
+/* MTD NAND flash */
+
+#if defined(CONFIG_MTD_NAND_MXC) \
+ || defined(CONFIG_MTD_NAND_MXC_MODULE) \
+ || defined(CONFIG_MTD_NAND_MXC_V2) \
+ || defined(CONFIG_MTD_NAND_MXC_V2_MODULE)
+
+static struct flash_platform_data mxc_nand_data = {
+ #ifdef CONFIG_MTD_PARTITIONS
+ .parts = nand_flash_partitions,
+ .nr_parts = ARRAY_SIZE(nand_flash_partitions),
+ #endif
+ .width = 1,
+};
+
+static struct platform_device mxc_nand_mtd_device = {
+ .name = "mxc_nand_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static struct platform_device mxc_nandv2_mtd_device = {
+ .name = "mxc_nandv2_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static void mxc_init_nand_mtd(void)
+{
+ if (__raw_readl(MXC_CCM_RCSR) & MXC_CCM_RCSR_NF16B) {
+ mxc_nand_data.width = 2;
+ }
+ if (cpu_is_mx31()) {
+ (void)platform_device_register(&mxc_nand_mtd_device);
+ }
+ if (cpu_is_mx32()) {
+ (void)platform_device_register(&mxc_nandv2_mtd_device);
+ }
+}
+#else
+static inline void mxc_init_nand_mtd(void)
+{
+}
+#endif
+
+/* i.MX MTD NAND Flash Controller */
+
+#if defined(CONFIG_MTD_NAND_IMX_NFC) || defined(CONFIG_MTD_NAND_IMX_NFC_MODULE)
+
+/* Resources for this device. */
+
+static struct resource imx_nfc_resources[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ .start = NFC_BASE_ADDR + 0x000,
+ .end = NFC_BASE_ADDR + 0x840 - 1,
+ .name = IMX_NFC_BUFFERS_ADDR_RES_NAME,
+ },
+ {
+ .flags = IORESOURCE_MEM,
+ .start = NFC_BASE_ADDR + 0xE00,
+ .end = NFC_BASE_ADDR + 0xE20 - 1,
+ .name = IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME,
+ },
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = MXC_INT_NANDFC,
+ .end = MXC_INT_NANDFC,
+ .name = IMX_NFC_INTERRUPT_RES_NAME,
+ },
+};
+
+/**
+ * imx_nfc_set_page_size() - Tells the hardware the page size.
+ *
+ * @data_size_in_bytes: The page size in bytes (e.g., 512, 2048, etc.). This
+ * size refers specifically to the the data bytes in the
+ * page, *not* including out-of-band bytes. The return
+ * value is zero if the operation succeeded. Do not
+ * interpret a non-zero value as an error code - it only
+ * indicates failure. The driver will decide what error
+ * code to return to its caller.
+ */
+static int imx_nfc_set_page_size(unsigned int data_size_in_bytes)
+{
+
+ unsigned long x = __raw_readl(MXC_CCM_RCSR);
+
+ switch (data_size_in_bytes) {
+
+ case 512:
+ x &= ~MXC_CCM_RCSR_NFMS;
+ break;
+
+ case 2048:
+ x |= MXC_CCM_RCSR_NFMS;
+ break;
+
+ default:
+ return !0;
+ break;
+
+ }
+
+ __raw_writel(x, MXC_CCM_RCSR);
+
+ return 0;
+
+}
+
+/*
+ * Platform-specific information about this device. Some of the details depend
+ * on the SoC. See imx_init_nfc() below for code that fills in the rest.
+ */
+
+static struct imx_nfc_platform_data imx_nfc_platform_data = {
+ .force_ce = false,
+ .target_cycle_in_ns = 50,
+ .clock_name = "nfc_clk",
+ .set_page_size = imx_nfc_set_page_size,
+ .interleave = false,
+ #ifdef CONFIG_MTD_PARTITIONS
+ .partitions = nand_flash_partitions,
+ .partition_count = ARRAY_SIZE(nand_flash_partitions),
+ #endif
+};
+
+/* The structure that represents the NFC device. */
+
+static struct platform_device imx_nfc_device = {
+ .name = IMX_NFC_DRIVER_NAME,
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &imx_nfc_platform_data,
+ },
+ .resource = imx_nfc_resources,
+ .num_resources = ARRAY_SIZE(imx_nfc_resources),
+};
+
+/**
+ * imx_init_nfc() - Sets up the NFC for this platform.
+ *
+ * This function sets up data structures representing the NFC device on this
+ * platform and registers the device with the platform management system.
+ */
+
+static void imx_nfc_init(void)
+{
+
+ /*
+ * A field in the Reset Control and Source Register register tells us
+ * the bus width.
+ */
+
+ if (__raw_readl(MXC_CCM_RCSR) & MXC_CCM_RCSR_NF16B)
+ imx_nfc_platform_data.bus_width_in_bits = 16;
+ else
+ imx_nfc_platform_data.bus_width_in_bits = 8;
+
+ /*
+ * Discover the type of SoC we're running on and, based on that, fill in
+ * some details about the NFC.
+ */
+
+ if (cpu_is_mx31()) {
+ imx_nfc_platform_data.major_version = 1;
+ imx_nfc_platform_data.minor_version = 0;
+ } else if (cpu_is_mx32()) {
+ imx_nfc_platform_data.major_version = 2;
+ imx_nfc_platform_data.minor_version = 0;
+ } else {
+ pr_err("imx_nfc: Can't identify the SoC\n");
+ BUG();
+ }
+
+ /* Register the NFC device. */
+
+ (void)platform_device_register(&imx_nfc_device);
+
+}
+
+#else
+
+static inline void imx_nfc_init(void)
+{
+}
+
+#endif /* i.MX MTD NAND Flash Controller */
+
+static struct spi_board_info mxc_spi_board_info[] __initdata = {
+ {
+ .modalias = "pmic_spi",
+ .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_3),
+ .max_speed_hz = 4000000,
+ .bus_num = 2,
+ .chip_select = 0,
+ },
+};
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+static const char fb_default_mode[] = "Sharp-QVGA";
+
+/* mxc lcd driver */
+static struct platform_device mxc_fb_device = {
+ .name = "mxc_sdc_fb",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &fb_default_mode,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+};
+
+static void mxc_init_fb(void)
+{
+ (void)platform_device_register(&mxc_fb_device);
+}
+#else
+static inline void mxc_init_fb(void)
+{
+}
+#endif
+
+#if defined(CONFIG_BACKLIGHT_MXC)
+static struct platform_device mxcbl_devices[] = {
+#if defined(CONFIG_BACKLIGHT_MXC_PMIC) || defined(CONFIG_BACKLIGHT_MXC_PMIC_MODULE)
+ {
+ .name = "mxc_pmic_bl",
+ .id = 0,
+ .dev = {
+ .platform_data = (void *)-1, /* DISP # for this backlight */
+ },
+ },
+ {
+ .name = "mxc_pmic_bl",
+ .id = 1,
+ .dev = {
+ .platform_data = (void *)0, /* DISP # for this backlight */
+ },
+ },
+#endif
+#if defined(CONFIG_BACKLIGHT_MXC_IPU) || defined(CONFIG_BACKLIGHT_MXC_IPU_MODULE)
+ {
+ .name = "mxc_ipu_bl",
+ .id = 0,
+ .dev = {
+ .platform_data = (void *)3, /* DISP # for this backlight */
+ },
+ },
+#endif
+};
+static inline void mxc_init_bl(void)
{
- mxc_iomux_mode(MX31_PIN_CTS1__CTS1);
- mxc_iomux_mode(MX31_PIN_RTS1__RTS1);
- mxc_iomux_mode(MX31_PIN_TXD1__TXD1);
- mxc_iomux_mode(MX31_PIN_RXD1__RXD1);
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mxcbl_devices); i++) {
+ platform_device_register(&mxcbl_devices[i]);
+ }
+}
+#else
+static inline void mxc_init_bl(void)
+{
+}
+#endif
+
+/*!
+ * Data structures and data for mt9v111 camera.
+ */
+static struct mxc_camera_platform_data camera_mt9v111_data = {
+ .mclk = 27000000,
+};
+
+/*!
+ * Data structures and data for ov2640 camera.
+ */
+static struct mxc_camera_platform_data camera_ov2640_data = {
+ .core_regulator = NULL,
+ .io_regulator = NULL,
+ .analog_regulator = NULL,
+ .gpo_regulator = NULL,
+ .mclk = 24000000,
+};
+
+/*!
+ * Info to register i2c devices.
+ */
+static struct i2c_board_info mxc_i2c_info[] __initdata = {
+ {
+ .type = "mt9v111",
+ .addr = 0x48,
+ .platform_data = (void *)&camera_mt9v111_data,
+ },
+ {
+ .type = "ov2640",
+ .addr = 0x30,
+ .platform_data = (void *)&camera_ov2640_data,
+ },
+};
- mxc_register_device(&mxc_uart_device0, &uart_pdata);
+#if defined(CONFIG_MXC_FIR) || defined(CONFIG_MXC_FIR_MODULE)
+/*!
+ * Resource definition for the FIR
+ */
+static struct resource mxcir_resources[] = {
+ [0] = {
+ .start = UART2_BASE_ADDR,
+ .end = UART2_BASE_ADDR + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_UART2,
+ .end = MXC_INT_UART2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = FIRI_BASE_ADDR,
+ .end = FIRI_BASE_ADDR + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [3] = {
+ .start = MXC_INT_FIRI,
+ .end = MXC_INT_FIRI,
+ .flags = IORESOURCE_IRQ,
+ },
+ [4] = {
+ .start = MXC_INT_UART2,
+ .end = MXC_INT_UART2,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct mxc_ir_platform_data ir_data = {
+ .uart_ir_mux = 1,
+ .ir_rx_invert = MXC_IRDA_RX_INV,
+ .ir_tx_invert = MXC_IRDA_TX_INV,
+};
+
+/*! Device Definition for MXC FIR */
+static struct platform_device mxcir_device = {
+ .name = "mxcir",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &ir_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcir_resources),
+ .resource = mxcir_resources,
+};
+
+static inline void mxc_init_ir(void)
+{
+ ir_data.uart_clk = clk_get(NULL, "uart_clk.1");;
+ (void)platform_device_register(&mxcir_device);
}
-#else /* !SERIAL_IMX */
-static inline void mxc_init_imx_uart(void)
+#else
+static inline void mxc_init_ir(void)
{
}
-#endif /* !SERIAL_IMX */
+#endif
-static void mx31ads_expio_irq_handler(u32 irq, struct irq_desc *desc)
+static void mxc_expio_irq_handler(u32 irq, struct irq_desc *desc)
{
u32 imr_val;
u32 int_valid;
u32 expio_irq;
+ desc->chip->mask(irq); /* irq = gpio irq number */
+
imr_val = __raw_readw(PBC_INTMASK_SET_REG);
int_valid = __raw_readw(PBC_INTSTATUS_REG) & imr_val;
+ if (unlikely(!int_valid)) {
+ printk(KERN_ERR "\nEXPIO: Spurious interrupt:0x%0x\n\n",
+ int_valid);
+ goto out;
+ }
+
expio_irq = MXC_EXP_IO_BASE;
for (; int_valid != 0; int_valid >>= 1, expio_irq++) {
+ struct irq_desc *d;
if ((int_valid & 1) == 0)
continue;
-
- generic_handle_irq(expio_irq);
+ d = irq_desc + expio_irq;
+ if (unlikely(!(d->handle_irq))) {
+ printk(KERN_ERR "\nEXPIO irq: %d unhandeled\n",
+ expio_irq);
+ BUG(); /* oops */
+ }
+ d->handle_irq(expio_irq, d);
}
+
+ out:
+ desc->chip->ack(irq);
+ desc->chip->unmask(irq);
}
/*
* Disable an expio pin's interrupt by setting the bit in the imr.
- * @param irq an expio virtual irq number
+ * @param irq an expio virtual irq number
*/
static void expio_mask_irq(u32 irq)
{
u32 expio = MXC_IRQ_TO_EXPIO(irq);
/* mask the interrupt */
__raw_writew(1 << expio, PBC_INTMASK_CLEAR_REG);
- __raw_readw(PBC_INTMASK_CLEAR_REG);
}
/*
* Acknowledge an expanded io pin's interrupt by clearing the bit in the isr.
- * @param irq an expanded io virtual irq number
+ * @param irq an expanded io virtual irq number
*/
static void expio_ack_irq(u32 irq)
{
u32 expio = MXC_IRQ_TO_EXPIO(irq);
/* clear the interrupt status */
__raw_writew(1 << expio, PBC_INTSTATUS_REG);
+ /* mask the interrupt */
+ expio_mask_irq(irq);
}
/*
* Enable a expio pin's interrupt by clearing the bit in the imr.
- * @param irq a expio virtual irq number
+ * @param irq a expio virtual irq number
*/
static void expio_unmask_irq(u32 irq)
{
@@ -167,16 +737,22 @@ static struct irq_chip expio_irq_chip = {
.unmask = expio_unmask_irq,
};
-static void __init mx31ads_init_expio(void)
+static int initialized = 0;
+
+static int __init _mxc_expio_init(void)
{
int i;
- printk(KERN_INFO "MX31ADS EXPIO(CPLD) hardware\n");
+ initialized = 1;
+ printk(KERN_INFO "MX31ADS EXPIO(CPLD) hardware\n");
/*
* Configure INT line as GPIO input
*/
- mxc_iomux_mode(IOMUX_MODE(MX31_PIN_GPIO1_4, IOMUX_CONFIG_GPIO));
+ mxc_request_iomux(MX31_PIN_GPIO1_4, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_4), NULL);
+ gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_4));
/* disable the interrupt and clear the status */
__raw_writew(0xFFFF, PBC_INTMASK_CLEAR_REG);
@@ -187,82 +763,427 @@ static void __init mx31ads_init_expio(void)
set_irq_handler(i, handle_level_irq);
set_irq_flags(i, IRQF_VALID);
}
- set_irq_type(EXPIO_PARENT_INT, IRQ_TYPE_LEVEL_HIGH);
- set_irq_chained_handler(EXPIO_PARENT_INT, mx31ads_expio_irq_handler);
+ set_irq_type(EXPIO_PARENT_INT, IRQF_TRIGGER_HIGH);
+ set_irq_chained_handler(EXPIO_PARENT_INT, mxc_expio_irq_handler);
+
+ return 0;
}
+/*
+ * This may get called early from board specific init
+ */
+int mxc_expio_init(void)
+{
+ if (!initialized)
+ return _mxc_expio_init();
+ else
+ return 0;
+}
+
+/* MMC device data */
+
+#if defined(CONFIG_MMC_MXC) || defined(CONFIG_MMC_MXC_MODULE)
+extern unsigned int sdhc_get_card_det_status(struct device *dev);
+extern int sdhc_init_card_det(int id);
+
+static struct mxc_mmc_platform_data mmc1_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30,
+ .min_clk = 150000,
+ .max_clk = 25000000,
+ .card_inserted_state = 1,
+ .status = sdhc_get_card_det_status,
+ .power_mmc = "VMMC1",
+};
+static struct mxc_mmc_platform_data mmc2_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30,
+ .min_clk = 150000,
+ .max_clk = 25000000,
+ .card_inserted_state = 1,
+ .status = sdhc_get_card_det_status,
+ .power_mmc = "VMMC2",
+};
+
/*!
- * This structure defines static mappings for the i.MX31ADS board.
+ * Resource definition for the SDHC1
*/
-static struct map_desc mx31ads_io_desc[] __initdata = {
- {
- .virtual = AIPS1_BASE_ADDR_VIRT,
- .pfn = __phys_to_pfn(AIPS1_BASE_ADDR),
- .length = AIPS1_SIZE,
- .type = MT_DEVICE_NONSHARED
- }, {
- .virtual = SPBA0_BASE_ADDR_VIRT,
- .pfn = __phys_to_pfn(SPBA0_BASE_ADDR),
- .length = SPBA0_SIZE,
- .type = MT_DEVICE_NONSHARED
- }, {
- .virtual = AIPS2_BASE_ADDR_VIRT,
- .pfn = __phys_to_pfn(AIPS2_BASE_ADDR),
- .length = AIPS2_SIZE,
- .type = MT_DEVICE_NONSHARED
- }, {
- .virtual = CS4_BASE_ADDR_VIRT,
- .pfn = __phys_to_pfn(CS4_BASE_ADDR),
- .length = CS4_SIZE / 2,
- .type = MT_DEVICE
- },
+static struct resource mxcsdhc1_resources[] = {
+ [0] = {
+ .start = MMC_SDHC1_BASE_ADDR,
+ .end = MMC_SDHC1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC1,
+ .end = MXC_INT_MMC_SDHC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
};
/*!
- * Set up static virtual mappings.
+ * Resource definition for the SDHC2
*/
-void __init mx31ads_map_io(void)
+static struct resource mxcsdhc2_resources[] = {
+ [0] = {
+ .start = MMC_SDHC2_BASE_ADDR,
+ .end = MMC_SDHC2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC2,
+ .end = MXC_INT_MMC_SDHC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC1 */
+static struct platform_device mxcsdhc1_device = {
+ .name = "mxcmci",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc1_resources),
+ .resource = mxcsdhc1_resources,
+};
+
+/*! Device Definition for MXC SDHC2 */
+static struct platform_device mxcsdhc2_device = {
+ .name = "mxcmci",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc2_resources),
+ .resource = mxcsdhc2_resources,
+};
+
+static inline void mxc_init_mmc(void)
{
- mxc_map_io();
- iotable_init(mx31ads_io_desc, ARRAY_SIZE(mx31ads_io_desc));
+ int cd_irq;
+
+ cd_irq = sdhc_init_card_det(0);
+ if (cd_irq) {
+ mxcsdhc1_device.resource[2].start = cd_irq;
+ mxcsdhc1_device.resource[2].end = cd_irq;
+ }
+
+ cd_irq = sdhc_init_card_det(1);
+ if (cd_irq) {
+ mxcsdhc2_device.resource[2].start = cd_irq;
+ mxcsdhc2_device.resource[2].end = cd_irq;
+ }
+
+ spba_take_ownership(SPBA_SDHC1, SPBA_MASTER_A | SPBA_MASTER_C);
+ (void)platform_device_register(&mxcsdhc1_device);
+ spba_take_ownership(SPBA_SDHC2, SPBA_MASTER_A | SPBA_MASTER_C);
+ (void)platform_device_register(&mxcsdhc2_device);
}
+#else
+static inline void mxc_init_mmc(void)
+{
+}
+#endif
-void __init mx31ads_init_irq(void)
+/*!
+ * Board specific fixup function. It is called by \b setup_arch() in
+ * setup.c file very early on during kernel starts. It allows the user to
+ * statically fill in the proper values for the passed-in parameters. None of
+ * the parameters is used currently.
+ *
+ * @param desc pointer to \b struct \b machine_desc
+ * @param tags pointer to \b struct \b tag
+ * @param cmdline pointer to the command line
+ * @param mi pointer to \b struct \b meminfo
+ */
+static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ mxc_cpu_init();
+}
+
+#if (defined(CONFIG_MXC_PMIC_MC13783) || \
+ defined(CONFIG_MXC_PMIC_MC13783_MODULE)) \
+ && (defined(CONFIG_SND_MXC_PMIC) || defined(CONFIG_SND_MXC_PMIC_MODULE))
+extern void gpio_activate_audio_ports(void);
+
+static void __init mxc_init_pmic_audio(void)
+{
+ struct clk *ckih_clk;
+ struct clk *cko_clk;
+
+ /* Enable 26 mhz clock on CKO1 for PMIC audio */
+ ckih_clk = clk_get(NULL, "ckih");
+ cko_clk = clk_get(NULL, "cko1_clk");
+ if (IS_ERR(ckih_clk) || IS_ERR(cko_clk)) {
+ printk(KERN_ERR "Unable to set CKO1 output to CKIH\n");
+ } else {
+ clk_set_parent(cko_clk, ckih_clk);
+ clk_set_rate(cko_clk, clk_get_rate(ckih_clk));
+ clk_enable(cko_clk);
+ }
+ clk_put(ckih_clk);
+ clk_put(cko_clk);
+
+ gpio_activate_audio_ports();
+}
+#else
+static void __inline mxc_init_pmic_audio(void)
+{
+}
+#endif
+
+/* IDE device data */
+#if defined(CONFIG_BLK_DEV_IDE_MXC) || defined(CONFIG_BLK_DEV_IDE_MXC_MODULE)
+
+/*! Platform Data for MXC IDE */
+static struct mxc_ide_platform_data mxc_ide_data = {
+ .power_drive = NULL,
+ .power_io = NULL,
+};
+
+static struct platform_device mxc_ide_device = {
+ .name = "mxc_ide",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_ide_data,
+ },
+};
+
+static inline void mxc_init_ide(void)
+{
+ if (platform_device_register(&mxc_ide_device) < 0)
+ printk(KERN_ERR "Error: Registering the ide.\n");
+}
+#else
+static inline void mxc_init_ide(void)
{
- mxc_init_irq();
- mx31ads_init_expio();
}
+#endif
+
+#if defined(CONFIG_PATA_FSL) || defined(CONFIG_PATA_FSL_MODULE)
+extern void gpio_ata_active(void);
+extern void gpio_ata_inactive(void);
+
+static int ata_init(struct platform_device *pdev)
+{
+ /* Configure the pins */
+ gpio_ata_active();
+
+ return 0;
+}
+
+static void ata_exit(void)
+{
+ /* Free the pins */
+ gpio_ata_inactive();
+}
+
+static struct fsl_ata_platform_data ata_data = {
+ .udma_mask = ATA_UDMA3, /* board can handle up to UDMA3 */
+ .mwdma_mask = ATA_MWDMA2,
+ .pio_mask = ATA_PIO4,
+ .fifo_alarm = MXC_IDE_DMA_WATERMARK / 2,
+ .max_sg = MXC_IDE_DMA_BD_NR,
+ .init = ata_init,
+ .exit = ata_exit,
+ .core_reg = NULL, /*"LDO2", */
+ .io_reg = NULL, /*"LDO3", */
+};
+
+static struct resource pata_fsl_resources[] = {
+ [0] = { /* I/O */
+ .start = ATA_BASE_ADDR + 0x00,
+ .end = ATA_BASE_ADDR + 0xD8,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = { /* IRQ */
+ .start = MXC_INT_ATA,
+ .end = MXC_INT_ATA,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device pata_fsl_device = {
+ .name = "pata_fsl",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(pata_fsl_resources),
+ .resource = pata_fsl_resources,
+ .dev = {
+ .platform_data = &ata_data,
+ .coherent_dma_mask = ~0,
+ },
+};
+
+static void __init mxc_init_pata(void)
+{
+ (void)platform_device_register(&pata_fsl_device);
+}
+#else /* CONFIG_PATA_FSL */
+static void __init mxc_init_pata(void)
+{
+}
+#endif /* CONFIG_PATA_FSL */
/*!
* Board specific initialization.
*/
static void __init mxc_board_init(void)
{
+ mxc_cpu_common_init();
+ early_console_setup(saved_command_line);
+ mxc_init_devices();
+ mxc_init_pmic_audio();
+ mxc_register_gpios();
+ mx31ads_gpio_init();
+ mxc_expio_init();
+ mxc_init_keypad();
mxc_init_extuart();
- mxc_init_imx_uart();
+ mxc_init_nor_mtd();
+ mxc_init_nand_mtd();
+ imx_nfc_init();
+
+ i2c_register_board_info(0, mxc_i2c_info, ARRAY_SIZE(mxc_i2c_info));
+ spi_register_board_info(mxc_spi_board_info,
+ ARRAY_SIZE(mxc_spi_board_info));
+
+ mxc_init_fb();
+ mxc_init_bl();
+ mxc_init_ir();
+ mxc_init_mmc();
+ mxc_init_ide();
+ mxc_init_pata();
}
+unsigned long board_get_ckih_rate(void)
+{
+}
static void __init mx31ads_timer_init(void)
{
- mxc_clocks_init(26000000);
- mxc_timer_init("ipg_clk.0");
+ unsigned long ckih = 26000000;
+
+ if ((__raw_readw(PBC_BASE_ADDRESS + PBC_BSTAT) &
+ CKIH_27MHZ_BIT_SET) != 0) {
+ ckih = 27000000;
+ }
+
+ mxc_clocks_init(32768, 0, ckih, 0);
+ mxc_timer_init("gpt_clk");
}
-struct sys_timer mx31ads_timer = {
+static struct sys_timer mx31ads_timer = {
.init = mx31ads_timer_init,
};
+
+#define PLL_PCTL_REG(pd, mfd, mfi, mfn) \
+ ((((pd) - 1) << 26) + (((mfd) - 1) << 16) + ((mfi) << 10) + mfn)
+
+/* For 26MHz input clock */
+#define PLL_532MHZ PLL_PCTL_REG(1, 13, 10, 3)
+#define PLL_399MHZ PLL_PCTL_REG(1, 52, 7, 35)
+#define PLL_133MHZ PLL_PCTL_REG(2, 26, 5, 3)
+
+/* For 27MHz input clock */
+#define PLL_532_8MHZ PLL_PCTL_REG(1, 15, 9, 13)
+#define PLL_399_6MHZ PLL_PCTL_REG(1, 18, 7, 7)
+#define PLL_133_2MHZ PLL_PCTL_REG(3, 5, 7, 2)
+
+#define PDR0_REG(mcu, max, hsp, ipg, nfc) \
+ (MXC_CCM_PDR0_MCU_DIV_##mcu | MXC_CCM_PDR0_MAX_DIV_##max | \
+ MXC_CCM_PDR0_HSP_DIV_##hsp | MXC_CCM_PDR0_IPG_DIV_##ipg | \
+ MXC_CCM_PDR0_NFC_DIV_##nfc)
+
+/* working point(wp): 0 - 133MHz; 1 - 266MHz; 2 - 399MHz; 3 - 532MHz */
+/* 26MHz input clock table */
+static struct cpu_wp cpu_wp_26[] = {
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 133000000,
+ .pdr0_reg = PDR0_REG(4, 4, 4, 2, 6),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 266000000,
+ .pdr0_reg = PDR0_REG(2, 4, 4, 2, 6),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 399000000,
+ .pdr0_reg = PDR0_REG(1, 3, 3, 2, 6),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 532000000,
+ .pdr0_reg = PDR0_REG(1, 4, 4, 2, 6),},
+};
+
+/* 27MHz input clock table */
+static struct cpu_wp cpu_wp_27[] = {
+ {
+ .pll_reg = PLL_532_8MHZ,
+ .pll_rate = 532800000,
+ .cpu_rate = 133200000,
+ .pdr0_reg = PDR0_REG(4, 4, 4, 2, 6),},
+ {
+ .pll_reg = PLL_532_8MHZ,
+ .pll_rate = 532800000,
+ .cpu_rate = 266400000,
+ .pdr0_reg = PDR0_REG(2, 4, 4, 2, 6),},
+ {
+ .pll_reg = PLL_399_6MHZ,
+ .pll_rate = 399600000,
+ .cpu_rate = 399600000,
+ .pdr0_reg = PDR0_REG(1, 3, 3, 2, 6),},
+ {
+ .pll_reg = PLL_532_8MHZ,
+ .pll_rate = 532800000,
+ .cpu_rate = 532800000,
+ .pdr0_reg = PDR0_REG(1, 4, 4, 2, 6),},
+};
+
+struct cpu_wp *get_cpu_wp(int *wp)
+{
+ *wp = 4;
+ if ((__raw_readw(PBC_BASE_ADDRESS + PBC_BSTAT) &
+ CKIH_27MHZ_BIT_SET) != 0) {
+ return cpu_wp_27;
+ } else {
+ return cpu_wp_26;
+ }
+}
+
/*
* The following uses standard kernel macros defined in arch.h in order to
* initialize __mach_desc_MX31ADS data structure.
*/
-MACHINE_START(MX31ADS, "Freescale MX31ADS")
+/* *INDENT-OFF* */
+MACHINE_START(MX31ADS, "Freescale MX31/MX32 ADS")
/* Maintainer: Freescale Semiconductor, Inc. */
- .phys_io = AIPS1_BASE_ADDR,
- .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
- .boot_params = PHYS_OFFSET + 0x100,
- .map_io = mx31ads_map_io,
- .init_irq = mx31ads_init_irq,
- .init_machine = mxc_board_init,
- .timer = &mx31ads_timer,
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+ .phys_io = CS4_BASE_ADDR,
+ .io_pg_offst = ((CS4_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+#else
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+#endif
+ .boot_params = PHYS_OFFSET + 0x100,
+ .fixup = fixup_mxc_board,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mx31ads_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx3/mx31ads_gpio.c b/arch/arm/mach-mx3/mx31ads_gpio.c
new file mode 100644
index 000000000000..4d63db812804
--- /dev/null
+++ b/arch/arm/mach-mx3/mx31ads_gpio.c
@@ -0,0 +1,1561 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include "board-mx31ads.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx3/mx31ads_gpio.c
+ *
+ * @brief This file contains all the GPIO setup functions for the board.
+ *
+ * @ingroup GPIO_MX31
+ */
+
+void gpio_activate_audio_ports(void);
+
+/*!
+ * This system-wise GPIO function initializes the pins during system startup.
+ * All the statically linked device drivers should put the proper GPIO initialization
+ * code inside this function. It is called by \b fixup_mx31ads() during
+ * system startup. This function is board specific.
+ */
+void mx31ads_gpio_init(void)
+{
+ /* config CS4 */
+ mxc_request_iomux(MX31_PIN_CS4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ /*Connect DAM ports 4 & 5 to enable audio I/O */
+ gpio_activate_audio_ports();
+}
+
+/*!
+ * Setup GPIO for a UART port to be active
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_active(int port, int no_irda)
+{
+ unsigned int pbc_bctrl1_clr = 0, pbc_bctrl2_set = 0, pbc_bctrl2_clr = 0;
+ /*
+ * Configure the IOMUX control registers for the UART signals
+ */
+ switch (port) {
+ /* UART 1 IOMUX Configs */
+ case 0:
+ mxc_request_iomux(MX31_PIN_RXD1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_TXD1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_RTS1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CTS1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_DTR_DCE1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_DSR_DCE1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_RI_DCE1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_DCD_DCE1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+
+ /* Enable the transceiver */
+ pbc_bctrl1_clr |= PBC_BCTRL1_UENCE;
+ pbc_bctrl2_set |= PBC_BCTRL2_USELC;
+ break;
+ /* UART 2 IOMUX Configs */
+ case 1:
+ mxc_request_iomux(MX31_PIN_TXD2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_RXD2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+
+ if (no_irda == 1) {
+ mxc_request_iomux(MX31_PIN_RTS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CTS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_DTR_DCE2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ pbc_bctrl1_clr |= PBC_BCTRL1_UENCE;
+ pbc_bctrl2_clr |= PBC_BCTRL2_USELC;
+ } else {
+ pbc_bctrl1_clr |= PBC_BCTRL1_IREN;
+ pbc_bctrl2_clr |= PBC_BCTRL2_IRDA_MOD;
+ }
+ break;
+ /* UART 3 IOMUX Configs */
+ case 2:
+ mxc_request_iomux(MX31_PIN_CSPI3_MOSI, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI3_MISO, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI3_SCLK, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI3_SPI_RDY, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+
+ pbc_bctrl1_clr |= PBC_BCTRL1_UENB;
+ pbc_bctrl2_clr |= PBC_BCTRL2_USELB;
+ break;
+ /* UART 4 IOMUX Configs */
+ case 3:
+ mxc_request_iomux(MX31_PIN_ATA_CS0, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_ATA_CS1, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_ATA_DIOR, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_ATA_DIOW, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+
+ pbc_bctrl1_clr |= PBC_BCTRL1_UENB;
+ pbc_bctrl2_set |= PBC_BCTRL2_USELB;
+ break;
+ /* UART 5 IOMUX Configs */
+ case 4:
+ mxc_request_iomux(MX31_PIN_PC_VS2, OUTPUTCONFIG_ALT2,
+ INPUTCONFIG_ALT2);
+ mxc_request_iomux(MX31_PIN_PC_RST, OUTPUTCONFIG_ALT2,
+ INPUTCONFIG_ALT2);
+ mxc_request_iomux(MX31_PIN_PC_BVD1, OUTPUTCONFIG_ALT2,
+ INPUTCONFIG_ALT2);
+ mxc_request_iomux(MX31_PIN_PC_BVD2, OUTPUTCONFIG_ALT2,
+ INPUTCONFIG_ALT2);
+
+ pbc_bctrl1_clr |= PBC_BCTRL1_UENA;
+ pbc_bctrl2_set |= PBC_BCTRL2_USELA;
+ break;
+ default:
+ break;
+ }
+
+ __raw_writew(pbc_bctrl1_clr, PBC_BASE_ADDRESS + PBC_BCTRL1_CLEAR);
+ __raw_writew(pbc_bctrl2_set, PBC_BASE_ADDRESS + PBC_BCTRL2_SET);
+ __raw_writew(pbc_bctrl2_clr, PBC_BASE_ADDRESS + PBC_BCTRL2_CLEAR);
+ /*
+ * TODO: Configure the Pad registers for the UART pins
+ */
+}
+
+/*!
+ * Setup GPIO for a UART port to be inactive
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_inactive(int port, int no_irda)
+{
+ unsigned int pbc_bctrl1_set = 0;
+
+ switch (port) {
+ case 0:
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_RXD1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_TXD1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_RTS1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_CTS1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_DTR_DCE1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_DSR_DCE1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_RI_DCE1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_DCD_DCE1), NULL);
+
+ mxc_free_iomux(MX31_PIN_RXD1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_TXD1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_RTS1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_CTS1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_DTR_DCE1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_DSR_DCE1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_RI_DCE1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_DCD_DCE1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+
+ pbc_bctrl1_set |= PBC_BCTRL1_UENCE;
+ break;
+ case 1:
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_TXD2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_RXD2), NULL);
+
+ mxc_free_iomux(MX31_PIN_TXD2, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_RXD2, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+
+ if (no_irda == 1) {
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_DTR_DCE2), NULL);
+ mxc_free_iomux(MX31_PIN_DTR_DCE2, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+
+ pbc_bctrl1_set |= PBC_BCTRL1_UENCE;
+ } else {
+ pbc_bctrl1_set |= PBC_BCTRL1_IREN;
+ }
+ break;
+ case 2:
+ pbc_bctrl1_set |= PBC_BCTRL1_UENB;
+ break;
+ case 3:
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_ATA_CS0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_ATA_CS1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_ATA_DIOR), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_ATA_DIOW), NULL);
+
+ mxc_free_iomux(MX31_PIN_ATA_CS0, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_ATA_CS1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_ATA_DIOR, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_ATA_DIOW, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+
+ pbc_bctrl1_set |= PBC_BCTRL1_UENB;
+ break;
+ case 4:
+ pbc_bctrl1_set |= PBC_BCTRL1_UENA;
+ break;
+ default:
+ break;
+ }
+ __raw_writew(pbc_bctrl1_set, PBC_BASE_ADDRESS + PBC_BCTRL1_SET);
+}
+
+/*!
+ * Configure the IOMUX GPR register to receive shared SDMA UART events
+ *
+ * @param port a UART port
+ */
+void config_uartdma_event(int port)
+{
+ switch (port) {
+ case 1:
+ /* Configure to receive UART 2 SDMA events */
+ mxc_iomux_set_gpr(MUX_PGP_FIRI, false);
+ break;
+ case 2:
+ /* Configure to receive UART 3 SDMA events */
+ mxc_iomux_set_gpr(MUX_CSPI1_UART3, true);
+ break;
+ case 4:
+ /* Configure to receive UART 5 SDMA events */
+ mxc_iomux_set_gpr(MUX_CSPI3_UART5_SEL, true);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_uart_active);
+EXPORT_SYMBOL(gpio_uart_inactive);
+EXPORT_SYMBOL(config_uartdma_event);
+
+/*!
+ * Setup GPIO for Keypad to be active
+ *
+ */
+void gpio_keypad_active(void)
+{
+ /*
+ * Configure the IOMUX control register for keypad signals.
+ */
+ mxc_request_iomux(MX31_PIN_KEY_COL0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_COL1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_COL2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_COL3, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_COL4, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_COL5, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_COL6, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_COL7, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW3, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW4, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW5, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW6, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW7, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_keypad_active);
+
+/*!
+ * Setup GPIO for Keypad to be inactive
+ *
+ */
+void gpio_keypad_inactive(void)
+{
+ mxc_request_iomux(MX31_PIN_KEY_COL4, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_request_iomux(MX31_PIN_KEY_COL5, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_request_iomux(MX31_PIN_KEY_COL6, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_request_iomux(MX31_PIN_KEY_COL7, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+
+ mxc_request_iomux(MX31_PIN_KEY_ROW4, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_request_iomux(MX31_PIN_KEY_ROW5, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_request_iomux(MX31_PIN_KEY_ROW6, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_request_iomux(MX31_PIN_KEY_ROW7, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+}
+
+EXPORT_SYMBOL(gpio_keypad_inactive);
+
+void gpio_power_key_active(void)
+{
+}
+EXPORT_SYMBOL(gpio_power_key_active);
+
+/*!
+ * Setup GPIO for a CSPI device to be active
+ *
+ * @param cspi_mod an CSPI device
+ */
+void gpio_spi_active(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ mxc_request_iomux(MX31_PIN_CSPI1_MISO, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI1_MOSI, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI1_SCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI1_SPI_RDY, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI1_SS0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI1_SS1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI1_SS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ case 1:
+ /* SPI2 */
+ mxc_request_iomux(MX31_PIN_CSPI2_MISO, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_MOSI, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SPI_RDY, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ case 2:
+ /* SPI3 */
+ /*
+ mxc_request_iomux(MX31_PIN_CSPI2_MISO, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_MOSI, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SCLK, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SPI_RDY, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ */
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup 1-Wire to be active
+ */
+void gpio_owire_active(void)
+{
+ /*
+ * Configure the IOMUX control register for 1-wire signals.
+ */
+ iomux_config_mux(MX31_PIN_BATT_LINE, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ iomux_config_pad(MX31_PIN_BATT_LINE, PAD_CTL_LOOPBACK);
+}
+
+/*!
+ * Setup 1-Wire to be active
+ */
+void gpio_owire_inactive(void)
+{
+ /*
+ * Configure the IOMUX control register for 1-wire signals.
+ */
+ iomux_config_mux(MX31_PIN_BATT_LINE, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+}
+
+EXPORT_SYMBOL(gpio_owire_active);
+EXPORT_SYMBOL(gpio_owire_inactive);
+
+/*!
+ * Setup GPIO for a CSPI device to be inactive
+ *
+ * @param cspi_mod a CSPI device
+ */
+void gpio_spi_inactive(int cspi_mod)
+{
+ /* Do nothing as CSPI pins doesn't have/support GPIO mode */
+}
+
+/*!
+ * Setup GPIO for an I2C device to be active
+ *
+ * @param i2c_num an I2C device
+ */
+void gpio_i2c_active(int i2c_num)
+{
+ switch (i2c_num) {
+ case 0:
+ mxc_request_iomux(MX31_PIN_I2C_CLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_I2C_DAT, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ case 1:
+ mxc_request_iomux(MX31_PIN_CSPI2_MOSI, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI2_MISO, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ break;
+ case 2:
+ mxc_request_iomux(MX31_PIN_CSPI2_SS2, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI2_SCLK, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ break;
+ default:
+ break;
+ }
+
+}
+
+/*!
+ * Setup GPIO for an I2C device to be inactive
+ *
+ * @param i2c_num an I2C device
+ */
+void gpio_i2c_inactive(int i2c_num)
+{
+ switch (i2c_num) {
+ case 0:
+ break;
+ case 1:
+ break;
+ case 2:
+ mxc_request_iomux(MX31_PIN_CSPI2_SS2, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI2_SCLK, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_ALT1);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * This function configures the IOMux block for PMIC standard operations.
+ *
+ */
+void gpio_pmic_active(void)
+{
+ mxc_request_iomux(MX31_PIN_GPIO1_3, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_3), NULL);
+ gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_3));
+}
+
+EXPORT_SYMBOL(gpio_pmic_active);
+
+/*!
+ * This function activates DAM ports 4 & 5 to enable
+ * audio I/O. Thsi function is called from mx31ads_gpio_init
+ * function, which is board-specific.
+ */
+void gpio_activate_audio_ports(void)
+{
+ /* config Audio ports (4 & 5) */
+ mxc_request_iomux(MX31_PIN_SCK4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SRXD4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_STXD4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SFS4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SCK5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SRXD5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_STXD5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SFS5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+}
+
+/*!
+ * Setup GPIO for SDHC to be active
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_active(int module)
+{
+ switch (module) {
+ case 0:
+ mxc_request_iomux(MX31_PIN_SD1_CLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SD1_CMD, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SD1_DATA0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SD1_DATA1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SD1_DATA2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SD1_DATA3, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+
+ mxc_iomux_set_pad(MX31_PIN_SD1_CLK,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_SD1_CMD,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA1,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA2,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA3,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ break;
+ case 1:
+ mxc_request_iomux(MX31_PIN_PC_CD2_B, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_PC_CD1_B, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_PC_WAIT_B, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_PC_READY, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_PC_VS1, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_PC_PWRON, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_sdhc_active);
+
+/*!
+ * Setup GPIO for SDHC1 to be inactive
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_inactive(int module)
+{
+ switch (module) {
+ case 0:
+ mxc_request_iomux(MX31_PIN_SD1_CLK, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_SD1_CMD, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_SD1_DATA0, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_SD1_DATA1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_SD1_DATA2, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_SD1_DATA3, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+
+ mxc_iomux_set_pad(MX31_PIN_SD1_CLK,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX31_PIN_SD1_CMD,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA0,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA1,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA2,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA3,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ break;
+ case 1:
+ /* TODO:what are the pins for SDHC2? */
+ mxc_request_iomux(MX31_PIN_PC_CD2_B, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_PC_CD1_B, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_PC_WAIT_B, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_PC_READY, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_PC_VS1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_PC_PWRON, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_sdhc_inactive);
+
+/*
+ * Probe for the card. If present the GPIO data would be set.
+ */
+int sdhc_get_card_det_status(struct device *dev)
+{
+ if (to_platform_device(dev)->id == 0) {
+ return gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1));
+ } else {
+ return gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_GPIO1_2));
+ }
+}
+
+EXPORT_SYMBOL(sdhc_get_card_det_status);
+
+/*
+ * Return the card detect pin.
+ */
+int sdhc_init_card_det(int id)
+{
+ if (id == 0) {
+ iomux_config_mux(MX31_PIN_GPIO1_1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ return IOMUX_TO_IRQ(MX31_PIN_GPIO1_1);
+ } else {
+ iomux_config_mux(MX31_PIN_GPIO1_2, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ return IOMUX_TO_IRQ(MX31_PIN_GPIO1_2);
+
+ }
+}
+
+EXPORT_SYMBOL(sdhc_init_card_det);
+
+/*!
+ * Setup GPIO for LCD to be active
+ *
+ */
+void gpio_lcd_active(void)
+{
+ u16 temp;
+
+ mxc_request_iomux(MX31_PIN_LD0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD7, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD8, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD9, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD10, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD11, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD12, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD13, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD14, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD15, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD16, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // LD16
+ mxc_request_iomux(MX31_PIN_LD17, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // LD17
+ mxc_request_iomux(MX31_PIN_VSYNC3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // VSYNC
+ mxc_request_iomux(MX31_PIN_HSYNC, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // HSYNC
+ mxc_request_iomux(MX31_PIN_FPSHIFT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // CLK
+ mxc_request_iomux(MX31_PIN_DRDY0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // DRDY
+ mxc_request_iomux(MX31_PIN_D3_REV, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // REV
+ mxc_request_iomux(MX31_PIN_CONTRAST, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // CONTR
+ mxc_request_iomux(MX31_PIN_D3_SPL, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // SPL
+ mxc_request_iomux(MX31_PIN_D3_CLS, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // CLS
+
+ temp = PBC_BCTRL1_LCDON;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL1_SET);
+}
+
+/*!
+ * Setup GPIO for LCD to be inactive
+ *
+ */
+void gpio_lcd_inactive(void)
+{
+ u16 pbc_bctrl1_set = 0;
+
+ pbc_bctrl1_set = (u16) PBC_BCTRL1_LCDON;
+ __raw_writew(pbc_bctrl1_set, PBC_BASE_ADDRESS + PBC_BCTRL1_SET + 2);
+}
+
+/*!
+ * Setup pins for SLCD to be active
+ *
+ */
+void slcd_gpio_config(void)
+{
+ u16 temp;
+
+ /* Reset smart lcd */
+ temp = PBC_BCTRL2_LDC_RST0;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL2_CLEAR);
+ msleep(2);
+ /* Bring out of reset */
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL2_SET);
+ msleep(2);
+
+ mxc_request_iomux(MX31_PIN_LD0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD7, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD8, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD9, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD10, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD11, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD12, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD13, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD14, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD15, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD16, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD17, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ mxc_request_iomux(MX31_PIN_READ, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); /* read */
+ mxc_request_iomux(MX31_PIN_WRITE, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); /* write */
+ mxc_request_iomux(MX31_PIN_PAR_RS, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); /* RS */
+ mxc_request_iomux(MX31_PIN_LCS0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); /* chip select */
+
+ /* Enable smart lcd interface */
+ temp = PBC_BCTRL2_LDCIO_EN;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL2_CLEAR);
+}
+
+/*!
+ * Switch to the specified sensor - MX31 ADS has two
+ *
+ */
+void gpio_sensor_select(int sensor)
+{
+ u16 temp;
+
+ switch (sensor) {
+ case 0:
+#ifdef CONFIG_MXC_CAMERA_MC521DA
+ temp = 0x100;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL1_SET);
+#else
+ temp = PBC_BCTRL1_SENSOR2_ON;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL1_CLEAR);
+ temp = PBC_BCTRL1_SENSOR1_ON;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL1_SET);
+#endif
+ break;
+ case 1:
+ temp = PBC_BCTRL1_SENSOR1_ON;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL1_CLEAR);
+ temp = PBC_BCTRL1_SENSOR2_ON;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL1_SET);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup GPIO for sensor to be active
+ *
+ */
+void gpio_sensor_active(void)
+{
+ gpio_sensor_select(0);
+
+ /*
+ * Configure the iomuxen for the CSI.
+ */
+
+ mxc_request_iomux(MX31_PIN_CSI_D4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D7, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D8, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D9, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D10, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D11, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D12, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D13, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D14, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D15, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_HSYNC, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_MCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_PIXCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_VSYNC, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+
+#ifdef CONFIG_MXC_IPU_CAMERA_16BIT
+ /*
+ * The other 4 data bits are multiplexed on MX31.
+ */
+ mxc_request_iomux(MX31_PIN_ATA_CS0, OUTPUTCONFIG_ALT2,
+ INPUTCONFIG_ALT2);
+ mxc_request_iomux(MX31_PIN_ATA_CS1, OUTPUTCONFIG_ALT2,
+ INPUTCONFIG_ALT2);
+ mxc_request_iomux(MX31_PIN_ATA_DIOR, OUTPUTCONFIG_ALT2,
+ INPUTCONFIG_ALT2);
+ mxc_request_iomux(MX31_PIN_ATA_DIOW, OUTPUTCONFIG_ALT2,
+ INPUTCONFIG_ALT2);
+#endif
+
+ /*
+ * Now enable the CSI buffers
+ */
+
+ __raw_writew(PBC_BCTRL2_CSI_EN, PBC_BASE_ADDRESS + PBC_BCTRL2_CLEAR);
+
+#ifdef CONFIG_MXC_IPU_CAMERA_16BIT
+ /*
+ * Enable the other buffer for the additional 4 data bits.
+ */
+ __raw_writew(PBC_BCTRL4_CSI_MSB_EN,
+ PBC_BASE_ADDRESS + PBC_BCTRL4_CLEAR);
+#endif
+}
+
+EXPORT_SYMBOL(gpio_sensor_active);
+
+void gpio_sensor_reset(bool flag)
+{
+ u16 temp;
+
+ if (flag) {
+ temp = 0x200;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL1_CLEAR);
+ } else {
+ temp = 0x200;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL1_SET);
+ }
+}
+
+EXPORT_SYMBOL(gpio_sensor_reset);
+
+/*!
+ * Setup GPIO for sensor to be inactive
+ *
+ */
+void gpio_sensor_inactive(void)
+{
+ mxc_free_iomux(MX31_PIN_CSI_D4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D7, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D8, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D9, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D10, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D11, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D12, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D13, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D14, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D15, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_HSYNC, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_MCLK, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_PIXCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_VSYNC, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_sensor_inactive);
+
+/*!
+ * Setup GPIO for ATA interface
+ *
+ */
+void gpio_ata_active(void)
+{
+ /*
+ * Configure the GPR for ATA group B signals
+ */
+ __raw_writew(PBC_BCTRL2_ATA_SEL, PBC_BASE_ADDRESS + PBC_BCTRL2_SET);
+ __raw_writew(PBC_BCTRL2_ATA_EN, PBC_BASE_ADDRESS + PBC_BCTRL2_CLEAR);
+
+ mxc_iomux_set_gpr(MUX_PGP_ATA_7 | MUX_PGP_ATA_6 | MUX_PGP_ATA_2 |
+ MUX_PGP_ATA_1, true);
+
+ /*
+ * Configure the IOMUX for ATA group B signals
+ */
+
+ mxc_request_iomux(MX31_PIN_CSPI1_MOSI, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D0
+ mxc_request_iomux(MX31_PIN_CSPI1_MISO, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D1
+ mxc_request_iomux(MX31_PIN_CSPI1_SS0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D2
+ mxc_request_iomux(MX31_PIN_CSPI1_SS1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D3
+ mxc_request_iomux(MX31_PIN_CSPI1_SS2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D4
+ mxc_request_iomux(MX31_PIN_CSPI1_SCLK, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D5
+ mxc_request_iomux(MX31_PIN_CSPI1_SPI_RDY, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D6
+ mxc_request_iomux(MX31_PIN_STXD3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D7
+ mxc_request_iomux(MX31_PIN_SRXD3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D8
+ mxc_request_iomux(MX31_PIN_SCK3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D9
+ mxc_request_iomux(MX31_PIN_SFS3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D10
+ mxc_request_iomux(MX31_PIN_STXD6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D11
+ mxc_request_iomux(MX31_PIN_SRXD6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D12
+ mxc_request_iomux(MX31_PIN_SCK6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D13
+ mxc_request_iomux(MX31_PIN_CAPTURE, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D14
+ mxc_request_iomux(MX31_PIN_COMPARE, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D15
+
+ mxc_request_iomux(MX31_PIN_USBH2_STP, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_DMARQ_B
+ mxc_request_iomux(MX31_PIN_USBH2_CLK, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_INTRQ_B
+ mxc_request_iomux(MX31_PIN_USBH2_NXT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_DA0
+ mxc_request_iomux(MX31_PIN_USBH2_DATA0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_DA1
+ mxc_request_iomux(MX31_PIN_USBH2_DATA1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_DA2
+ mxc_request_iomux(MX31_PIN_USBH2_DIR, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_BUFFER_DIR
+
+ /* These ATA pins are common to Group A and Group B */
+
+ mxc_request_iomux(MX31_PIN_ATA_CS0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_ATA_CS1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_ATA_DIOR, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_ATA_DIOW, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_ATA_DMACK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_ATA_RESET_B, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_PWMO, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ /* Need fast slew rate for UDMA mode */
+
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_MISO, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 0
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_MOSI, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 1
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS0, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 2
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS1, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 3
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS2, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 4
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SCLK, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 5
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SPI_RDY, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 6
+ mxc_iomux_set_pad(MX31_PIN_STXD3, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 7
+ mxc_iomux_set_pad(MX31_PIN_SRXD3, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 8
+ mxc_iomux_set_pad(MX31_PIN_SCK3, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 9
+ mxc_iomux_set_pad(MX31_PIN_SFS3, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 10
+ mxc_iomux_set_pad(MX31_PIN_STXD6, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 11
+ mxc_iomux_set_pad(MX31_PIN_SRXD6, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 12
+ mxc_iomux_set_pad(MX31_PIN_SCK6, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 13
+ mxc_iomux_set_pad(MX31_PIN_CAPTURE, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 14
+ mxc_iomux_set_pad(MX31_PIN_COMPARE, PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE); // data 12
+
+ /*
+ * Turn off default pullups on high asserted control signals.
+ * These are pulled down externally, so it will just waste
+ * power and create voltage divider action to pull them up
+ * on chip.
+ */
+ mxc_iomux_set_pad(MX31_PIN_USBH2_STP, PAD_CTL_PKE_NONE); // ATA_DMARQ
+ mxc_iomux_set_pad(MX31_PIN_USBH2_CLK, PAD_CTL_PKE_NONE); // ATA_INTRQ
+}
+
+EXPORT_SYMBOL(gpio_ata_active);
+
+/*!
+ * Restore ATA interface pins to reset values
+ *
+ */
+void gpio_ata_inactive(void)
+{
+ __raw_writew(PBC_BCTRL2_ATA_EN, PBC_BASE_ADDRESS + PBC_BCTRL2_SET);
+ /*
+ * Turn off ATA group B signals
+ */
+ mxc_request_iomux(MX31_PIN_STXD3, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D7
+ mxc_request_iomux(MX31_PIN_SRXD3, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D8
+ mxc_request_iomux(MX31_PIN_STXD6, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D11
+ mxc_request_iomux(MX31_PIN_SRXD6, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D12
+ mxc_request_iomux(MX31_PIN_SCK6, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D13
+ mxc_request_iomux(MX31_PIN_CAPTURE, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D14
+ mxc_request_iomux(MX31_PIN_COMPARE, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D15
+
+ /* These ATA pins are common to Group A and Group B */
+
+ mxc_request_iomux(MX31_PIN_ATA_CS0, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_ATA_CS1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_ATA_DIOR, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_ATA_DIOW, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_ATA_DMACK, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_ATA_RESET_B, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+
+ /* Needed fast slew rate for UDMA mode */
+
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_MISO, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 0
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_MOSI, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 1
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS0, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 2
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS1, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 3
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS2, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 4
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SCLK, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 5
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SPI_RDY, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 6
+ mxc_iomux_set_pad(MX31_PIN_STXD3, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 7
+ mxc_iomux_set_pad(MX31_PIN_SRXD3, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 8
+ mxc_iomux_set_pad(MX31_PIN_SCK3, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 9
+ mxc_iomux_set_pad(MX31_PIN_SFS3, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 10
+ mxc_iomux_set_pad(MX31_PIN_STXD3, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 11
+ mxc_iomux_set_pad(MX31_PIN_SRXD6, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 12
+ mxc_iomux_set_pad(MX31_PIN_SCK6, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 13
+ mxc_iomux_set_pad(MX31_PIN_CAPTURE, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 14
+ mxc_iomux_set_pad(MX31_PIN_COMPARE, PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE); // data 12
+}
+
+EXPORT_SYMBOL(gpio_ata_inactive);
+
+/*!
+ * Setup EDIO/IOMUX for external UART.
+ *
+ * @param port UART port
+ * @param irq Interrupt line to allocate
+ * @param handler Function to be called when the IRQ occurs
+ * @param irq_flags Interrupt type flags
+ * @param devname An ascii name for the claiming device
+ * @param dev_id A cookie passed back to the handler function
+ * @return Returns 0 if the interrupt was successfully requested,
+ * otherwise returns an error code.
+ */
+int extuart_intr_setup(unsigned int port, unsigned int irq,
+ irqreturn_t(*handler) (int, void *),
+ unsigned long irq_flags, const char *devname,
+ void *dev_id)
+{
+ return 0;
+}
+
+/*!
+ * Get the EDIO interrupt, clear if set.
+ *
+ * @param port UART port
+ */
+void extuart_intr_clear(unsigned int port)
+{
+}
+
+/*!
+ * Do IOMUX configs required to put the
+ * pin back in low power mode.
+ *
+ * @param port UART port
+ * @param irq Interrupt line to free
+ * @param dev_id Device identity to free
+ * @return Returns 0 if the interrupt was successfully freed,
+ * otherwise returns an error code.
+ */
+int extuart_intr_cleanup(unsigned int port, unsigned int irq, void *dev_id)
+{
+ return 0;
+}
+
+/* *INDENT-OFF* */
+/*
+ * USB Host 1
+ * pins conflict with SPI1, ATA, UART3
+ */
+int gpio_usbh1_active(void)
+{
+ if (mxc_request_iomux(MX31_PIN_CSPI1_MOSI, /* USBH1_RXDM */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_CSPI1_MISO, /* USBH1_RXDP */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_CSPI1_SS0, /* USBH1_TXDM */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_CSPI1_SS1, /* USBH1_TXDP */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_CSPI1_SS2, /* USBH1_RCV */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_CSPI1_SCLK, /* USBH1_OEB (_TXOE) */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_CSPI1_SPI_RDY, /* USBH1_FS */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1)) {
+ return -EINVAL;
+ }
+
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_MOSI, /* USBH1_RXDM */
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_MISO, /* USBH1_RXDP */
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS0, /* USBH1_TXDM */
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS1, /* USBH1_TXDP */
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS2, /* USBH1_RCV */
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SCLK, /* USBH1_OEB (_TXOE) */
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SPI_RDY, /* USBH1_FS */
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+
+ mxc_iomux_set_gpr(MUX_PGP_USB_SUSPEND, true);
+
+ __raw_writew(PBC_BCTRL3_FSH_EN, PBC_BASE_ADDRESS + PBC_BCTRL3_CLEAR); /* enable FSH */
+ __raw_writew(PBC_BCTRL3_FSH_SEL, PBC_BASE_ADDRESS + PBC_BCTRL3_SET); /* Group B */
+ __raw_writew(PBC_BCTRL3_FSH_MOD, PBC_BASE_ADDRESS + PBC_BCTRL3_CLEAR); /* single ended */
+ __raw_writew(PBC_BCTRL3_FSH_VBUS_EN, PBC_BASE_ADDRESS + PBC_BCTRL3_CLEAR); /* enable FSH VBUS */
+
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_usbh1_active);
+
+void gpio_usbh1_inactive(void)
+{
+ /* Do nothing as pins don't have/support GPIO mode */
+
+}
+
+EXPORT_SYMBOL(gpio_usbh1_inactive);
+
+/*
+ * USB Host 2
+ * pins conflict with UART5, PCMCIA
+ */
+int gpio_usbh2_active(void)
+{
+ if (mxc_request_iomux(MX31_PIN_USBH2_CLK,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBH2_DIR,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBH2_NXT,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBH2_STP,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBH2_DATA0,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBH2_DATA1,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_PC_VS2, /* USBH2_DATA2 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_PC_BVD1, /* USBH2_DATA3 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_PC_BVD2, /* USBH2_DATA4 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_PC_RST, /* USBH2_DATA5 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_IOIS16, /* USBH2_DATA6 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_PC_RW_B, /* USBH2_DATA7 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_NFWE_B,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_NFRE_B,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_NFALE,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_NFCLE,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_NFWP_B,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE) ||
+ mxc_request_iomux(MX31_PIN_NFCE_B,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE)) {
+ return -EINVAL;
+ }
+
+#define H2_PAD_CFG (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST | PAD_CTL_HYS_CMOS | PAD_CTL_ODE_CMOS | PAD_CTL_100K_PU)
+ mxc_iomux_set_pad(MX31_PIN_USBH2_CLK, H2_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBH2_DIR, H2_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBH2_NXT, H2_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBH2_STP, H2_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBH2_DATA0, H2_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBH2_DATA1, H2_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_SRXD6, H2_PAD_CFG); /* USBH2_DATA2 */
+ mxc_iomux_set_pad(MX31_PIN_STXD6, H2_PAD_CFG); /* USBH2_DATA3 */
+ mxc_iomux_set_pad(MX31_PIN_SFS3, H2_PAD_CFG); /* USBH2_DATA4 */
+ mxc_iomux_set_pad(MX31_PIN_SCK3, H2_PAD_CFG); /* USBH2_DATA5 */
+ mxc_iomux_set_pad(MX31_PIN_SRXD3, H2_PAD_CFG); /* USBH2_DATA6 */
+ mxc_iomux_set_pad(MX31_PIN_STXD3, H2_PAD_CFG); /* USBH2_DATA7 */
+#undef H2_PAD_CFG
+
+ mxc_iomux_set_gpr(MUX_PGP_UH2, true);
+
+ __raw_writew(PBC_BCTRL3_HSH_SEL, PBC_BASE_ADDRESS + PBC_BCTRL3_SET); /* enable HSH select */
+ __raw_writew(PBC_BCTRL3_HSH_EN, PBC_BASE_ADDRESS + PBC_BCTRL3_CLEAR); /* enable HSH */
+
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_usbh2_active);
+
+void gpio_usbh2_inactive(void)
+{
+ iomux_config_gpr(MUX_PGP_UH2, false);
+
+ iomux_config_pad(MX31_PIN_USBH2_CLK,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_USBH2_DIR,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_USBH2_NXT,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_USBH2_STP,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_USBH2_DATA0,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_USBH2_DATA1,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_SRXD6, /* USBH2_DATA2 */
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_STXD6, /* USBH2_DATA3 */
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_SFS3, /* USBH2_DATA4 */
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_SCK3, /* USBH2_DATA5 */
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_SRXD3, /* USBH2_DATA6 */
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ iomux_config_pad(MX31_PIN_STXD3, /* USBH2_DATA7 */
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+
+ mxc_free_iomux(MX31_PIN_NFWE_B,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_NFRE_B,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_NFALE,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_NFCLE,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_NFWP_B,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_NFCE_B,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+
+ __raw_writew(PBC_BCTRL3_HSH_SEL, PBC_BASE_ADDRESS + PBC_BCTRL3_CLEAR);
+ __raw_writew(PBC_BCTRL3_HSH_EN, PBC_BASE_ADDRESS + PBC_BCTRL3_SET);
+}
+
+EXPORT_SYMBOL(gpio_usbh2_inactive);
+
+/*
+ * USB OTG HS port
+ */
+int gpio_usbotg_hs_active(void)
+{
+ if (mxc_request_iomux(MX31_PIN_USBOTG_DATA0,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA1,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA2,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA3,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA4,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA5,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA6,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA7,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_CLK,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DIR,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_NXT,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_STP,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC)) {
+ return -EINVAL;
+ }
+
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA1,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA2,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA3,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA4,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA5,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA6,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA7,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_CLK,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DIR,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_NXT,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_STP,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+
+ /* enable OTG/HS */
+ __raw_writew(PBC_BCTRL3_OTG_HS_EN, PBC_BASE_ADDRESS + PBC_BCTRL3_CLEAR);
+ /* disable OTG/FS */
+ __raw_writew(PBC_BCTRL3_OTG_FS_EN, PBC_BASE_ADDRESS + PBC_BCTRL3_SET);
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_usbotg_hs_active);
+
+void gpio_usbotg_hs_inactive(void)
+{
+ /* Do nothing as pins doesn't have/support GPIO mode */
+
+}
+
+EXPORT_SYMBOL(gpio_usbotg_hs_inactive);
+
+/*
+ * USB OTG FS port
+ */
+int gpio_usbotg_fs_active(void)
+{
+ if (mxc_request_iomux(MX31_PIN_USBOTG_DATA0,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA1,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA2,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA3,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA4,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA5,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA6,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA7,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_CLK,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DIR,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_NXT,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_STP,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USB_PWR,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC)) {
+ return -EINVAL;
+ }
+
+ /* disable OTG/HS */
+ __raw_writew(PBC_BCTRL3_OTG_HS_EN, PBC_BASE_ADDRESS + PBC_BCTRL3_SET);
+ /* enable OTG/FS */
+ __raw_writew(PBC_BCTRL3_OTG_FS_EN, PBC_BASE_ADDRESS + PBC_BCTRL3_CLEAR);
+
+#if defined(CONFIG_MC13783_MXC)
+ /* Select PMIC transceiver */
+ __raw_writew(PBC_BCTRL3_OTG_FS_SEL, PBC_BASE_ADDRESS + PBC_BCTRL3_CLEAR);
+#endif
+ return 0;
+
+}
+
+EXPORT_SYMBOL(gpio_usbotg_fs_active);
+
+void gpio_usbotg_fs_inactive(void)
+{
+ /* Do nothing as pins doesn't have/support GPIO mode */
+
+}
+
+EXPORT_SYMBOL(gpio_usbotg_fs_inactive);
+/* *INDENT-ON* */
+
+/*!
+ * Setup GPIO for PCMCIA interface
+ *
+ */
+void gpio_pcmcia_active(void)
+{
+ u16 temp;
+
+ mxc_request_iomux(MX31_PIN_SDBA0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SDBA1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ iomux_config_mux(MX31_PIN_LBA, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_RW, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_EB0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_EB1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_OE, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ iomux_config_mux(MX31_PIN_IOIS16, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_BVD1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_BVD2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_CD1_B, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_CD2_B, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_POE, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_PWRON, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_READY, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_RST, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_RW_B, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_VS1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_VS2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(MX31_PIN_PC_WAIT_B, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+
+ /* PCMCIA VPP, VCC Enable, 1 = power on */
+ temp = PBC_BCTRL2_VPP_EN | PBC_BCTRL2_VCC_EN;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL2_SET);
+
+ /* Set up Card2 Select pin for PCMCIA, 0 = PCMCIA & SD2 */
+ temp = PBC_BCTRL3_CARD2_SEL;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL3_CLEAR);
+
+ /* PCMCIA Enable, 0 = enable */
+ temp = PBC_BCTRL4_PCMCIA_EN;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL4_CLEAR);
+ mdelay(1);
+}
+
+EXPORT_SYMBOL(gpio_pcmcia_active);
+
+/*!
+ * Setup GPIO for pcmcia to be inactive
+ */
+void gpio_pcmcia_inactive(void)
+{
+ u16 temp;
+
+ /* PCMCIA Enable, 0 = enable */
+ temp = PBC_BCTRL4_PCMCIA_EN;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL4_SET);
+
+ /* Set up Card2 Select pin for PCMCIA, 0 = PCMCIA & SD2 */
+ temp = PBC_BCTRL3_CARD2_SEL;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL3_SET);
+
+ /* PCMCIA VPP, VCC Enable, 1 = power on */
+ temp = PBC_BCTRL2_VPP_EN | PBC_BCTRL2_VCC_EN;
+ __raw_writew(temp, PBC_BASE_ADDRESS + PBC_BCTRL2_CLEAR);
+}
+
+EXPORT_SYMBOL(gpio_pcmcia_inactive);
+/*!
+ * Setup IR to be used by UART and FIRI
+ */
+void gpio_firi_init(void)
+{
+ gpio_uart_active(1, 0);
+}
+
+EXPORT_SYMBOL(gpio_firi_init);
+
+/*!
+ * Setup IR to be used by UART
+ */
+void gpio_firi_inactive(void)
+{
+ unsigned int pbc_bctrl2_set = 0, pbc_bctrl2_clr = 0;
+
+ iomux_config_gpr(MUX_PGP_FIRI, false);
+ mxc_request_iomux(MX31_PIN_TXD2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_RXD2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ pbc_bctrl2_set |= PBC_BCTRL2_IRDA_MOD;
+ __raw_writew(pbc_bctrl2_set, PBC_BASE_ADDRESS + PBC_BCTRL2_SET);
+
+ pbc_bctrl2_clr |= PBC_BCTRL2_IRDA_MOD;
+ __raw_writew(pbc_bctrl2_clr, PBC_BASE_ADDRESS + PBC_BCTRL2_CLEAR);
+}
+
+EXPORT_SYMBOL(gpio_firi_inactive);
+
+/*!
+ * Setup IR to be used by FIRI
+ */
+void gpio_firi_active(void *fir_cong_reg_base, unsigned int tpp_mask)
+{
+ unsigned int pbc_bctrl2_set = 0, pbc_bctrl2_clr = 0;
+ unsigned int cr;
+
+ iomux_config_gpr(MUX_PGP_FIRI, true);
+
+ cr = readl(fir_cong_reg_base);
+ cr &= ~tpp_mask;
+ writel(cr, fir_cong_reg_base);
+
+ pbc_bctrl2_clr |= PBC_BCTRL2_IRDA_MOD;
+ __raw_writew(pbc_bctrl2_clr, PBC_BASE_ADDRESS + PBC_BCTRL2_CLEAR);
+
+ pbc_bctrl2_set |= PBC_BCTRL2_IRDA_MOD;
+ __raw_writew(pbc_bctrl2_set, PBC_BASE_ADDRESS + PBC_BCTRL2_SET);
+
+ cr = readl(fir_cong_reg_base);
+ cr |= tpp_mask;
+ writel(cr, fir_cong_reg_base);
+
+ __raw_writew(pbc_bctrl2_clr, PBC_BASE_ADDRESS + PBC_BCTRL2_CLEAR);
+
+ cr = readl(fir_cong_reg_base);
+ cr &= ~tpp_mask;
+ writel(cr, fir_cong_reg_base);
+}
+
+EXPORT_SYMBOL(gpio_firi_active);
diff --git a/arch/arm/mach-mx3/mx3_3stack.c b/arch/arm/mach-mx3/mx3_3stack.c
new file mode 100644
index 000000000000..a78aa5e430c9
--- /dev/null
+++ b/arch/arm/mach-mx3/mx3_3stack.c
@@ -0,0 +1,1069 @@
+/*
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ * Copyright (C) 2002 Shane Nay (shane@minirl.com)
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/nodemask.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/consumer.h>
+#include <linux/ata.h>
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/mach/flash.h>
+#endif
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/keypad.h>
+#include <asm/mach/time.h>
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/memory.h>
+#include <mach/gpio.h>
+#include <mach/mmc.h>
+#include <mach/spba.h>
+#include <mach/pmic_power.h>
+
+#include "board-mx3_3stack.h"
+#include "crm_regs.h"
+#include "iomux.h"
+/*!
+ * @file mach-mx3/mx3_3stack.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX31
+ */
+
+extern void mxc_map_io(void);
+extern void mxc_init_irq(void);
+extern void mxc_cpu_init(void) __init;
+extern void mxc_cpu_common_init(void);
+extern void __init early_console_setup(char *);
+extern int mxc_init_devices(void);
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+#if defined(CONFIG_KEYBOARD_MXC) || defined(CONFIG_KEYBOARD_MXC_MODULE)
+static u16 keymapping[12] = {
+ KEY_UP, KEY_DOWN, 0, 0,
+ KEY_RIGHT, KEY_LEFT, KEY_ENTER, 0,
+ KEY_F6, KEY_F8, KEY_F9, KEY_F10,
+};
+
+static struct resource mxc_kpp_resources[] = {
+ [0] = {
+ .start = MXC_INT_KPP,
+ .end = MXC_INT_KPP,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct keypad_data keypad_plat_data = {
+ .rowmax = 3,
+ .colmax = 4,
+ .irq = MXC_INT_KPP,
+ .learning = 0,
+ .delay = 2,
+ .matrix = keymapping,
+};
+
+/* mxc keypad driver */
+static struct platform_device mxc_keypad_device = {
+ .name = "mxc_keypad",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_kpp_resources),
+ .resource = mxc_kpp_resources,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &keypad_plat_data,
+ },
+};
+
+static void mxc_init_keypad(void)
+{
+ (void)platform_device_register(&mxc_keypad_device);
+}
+#else
+static inline void mxc_init_keypad(void)
+{
+}
+#endif
+
+/* MTD NAND flash */
+#if defined(CONFIG_MTD_NAND_MXC) || defined(CONFIG_MTD_NAND_MXC_MODULE) \
+ || defined(CONFIG_MTD_NAND_MXC_V2) || defined(CONFIG_MTD_NAND_MXC_V2_MODULE)
+
+static struct mtd_partition mxc_nand_partitions[] = {
+ {
+ .name = "nand.bootloader",
+ .offset = 0,
+ .size = 1024 * 1024},
+ {
+ .name = "nand.kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 5 * 1024 * 1024},
+ {
+ .name = "nand.rootfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 180 * 1024 * 1024},
+ {
+ .name = "nand.configure",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 8 * 1024 * 1024},
+ {
+ .name = "nand.userfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL},
+};
+
+static struct flash_platform_data mxc_nand_data = {
+ .parts = mxc_nand_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_nand_partitions),
+ .width = 1,
+};
+
+static struct platform_device mxc_nand_mtd_device = {
+ .name = "mxc_nand_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static struct platform_device mxc_nandv2_mtd_device = {
+ .name = "mxc_nandv2_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static void mxc_init_nand_mtd(void)
+{
+ if (__raw_readl(MXC_CCM_RCSR) & MXC_CCM_RCSR_NF16B) {
+ mxc_nand_data.width = 2;
+ }
+ if (cpu_is_mx31()) {
+ (void)platform_device_register(&mxc_nand_mtd_device);
+ }
+ if (cpu_is_mx32()) {
+ (void)platform_device_register(&mxc_nandv2_mtd_device);
+ }
+}
+#else
+static inline void mxc_init_nand_mtd(void)
+{
+}
+#endif
+
+static void lcd_reset(void)
+{
+ /* ensure that LCDIO(1.8V) has been turn on */
+ /* active reset line GPIO */
+ mxc_request_iomux(MX31_PIN_LCS1, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_LCS1), "lcs1");
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_LCS1), 0);
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_LCS1), 0);
+ /* do reset */
+ msleep(10); /* tRES >= 100us */
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_LCS1), 1);
+ msleep(60);
+#ifdef CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_LCS1), 0);
+#endif
+}
+
+static struct mxc_lcd_platform_data lcd_data = {
+ .io_reg = "VGEN",
+#ifdef CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL
+ .core_reg = "GPO1",
+#else
+ .core_reg = "VMMC1",
+#endif
+ .reset = lcd_reset,
+};
+
+static struct mxc_camera_platform_data camera_data = {
+ .core_regulator = "VVIB",
+ .io_regulator = "VMMC1",
+ .analog_regulator = "SW2B",
+ .gpo_regulator = "GPO3",
+ .mclk = 27000000,
+};
+
+struct mxc_tvout_platform_data tvout_data = {
+ .io_reg = "VGEN",
+ .core_reg = "GPO3",
+ .analog_reg = "GPO1",
+ .detect_line = 49,
+};
+
+void si4702_reset(void)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_SRST0), 0);
+ msleep(100);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_SRST0), 1);
+ msleep(100);
+}
+
+void si4702_clock_ctl(int flag)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_SIMPD0), flag);
+}
+
+static void si4702_gpio_get(void)
+{
+ /* reset pin */
+ mxc_request_iomux(MX31_PIN_SRST0, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_SRST0), "srst0");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_SRST0), 0);
+
+ mxc_request_iomux(MX31_PIN_SIMPD0, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_SIMPD0), "simpd0");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_SIMPD0), 0);
+}
+
+static void si4702_gpio_put(void)
+{
+ mxc_free_iomux(MX31_PIN_SRST0, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_SIMPD0, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+}
+
+static struct mxc_fm_platform_data si4702_data = {
+ .reg_vio = "GPO3",
+ .reg_vdd = "VMMC1",
+ .gpio_get = si4702_gpio_get,
+ .gpio_put = si4702_gpio_put,
+ .reset = si4702_reset,
+ .clock_ctl = si4702_clock_ctl,
+ .sksnr = 0,
+ .skcnt = 0,
+ .band = 0,
+ .space = 100,
+ .seekth = 0xa,
+};
+
+/* setup GPIO for mma7450 */
+static void gpio_mma7450_get(void)
+{
+ mxc_request_iomux(MX31_PIN_STX0, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ mxc_iomux_set_pad(MX31_PIN_STX0, PAD_CTL_PKE_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_STX0), "stx0");
+ gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_STX0));
+
+ mxc_request_iomux(MX31_PIN_SRX0, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ mxc_iomux_set_pad(MX31_PIN_SRX0, PAD_CTL_PKE_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_SRX0), "srx0");
+ gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_SRX0));
+}
+
+static void gpio_mma7450_put(void)
+{
+ mxc_free_iomux(MX31_PIN_STX0, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_SRX0, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+}
+
+static struct mxc_mma7450_platform_data mma7450_data = {
+ .reg_dvdd_io = "GPO3",
+ .reg_avdd = "VMMC1",
+ .gpio_pin_get = gpio_mma7450_get,
+ .gpio_pin_put = gpio_mma7450_put,
+ .int1 = IOMUX_TO_IRQ(MX31_PIN_STX0),
+ .int2 = IOMUX_TO_IRQ(MX31_PIN_SRX0),
+};
+
+static struct i2c_board_info mxc_i2c_board_info[] __initdata = {
+ {
+ .type = "ov2640",
+ .addr = 0x30,
+ .platform_data = (void *)&camera_data,},
+ {
+ .type = "ch7024",
+ .addr = 0x76,
+ .platform_data = (void *)&tvout_data,
+ .irq = IOMUX_TO_IRQ(MX31_PIN_BATT_LINE),},
+ {
+ .type = "si4702",
+ .addr = 0x10,
+ .platform_data = (void *)&si4702_data,
+ },
+ {
+ .type = "mma7450",
+ .addr = 0x1d,
+ .platform_data = (void *)&mma7450_data,
+ },
+};
+
+static struct spi_board_info mxc_spi_board_info[] __initdata = {
+ {
+ .modalias = "pmic_spi",
+ .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_3),
+ .max_speed_hz = 4000000,
+ .bus_num = 2,
+ .chip_select = 2,
+ },
+ {
+ .modalias = "lcd_spi",
+ .platform_data = (void *)&lcd_data,
+ .max_speed_hz = 5000000,
+ .bus_num = 1,
+ .chip_select = 2,
+ },
+};
+
+/*lan9217 device*/
+#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
+static struct resource smsc911x_resources[] = {
+ {
+ .start = LAN9217_BASE_ADDR,
+ .end = LAN9217_BASE_ADDR + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = LAN9217_IRQ,
+ .end = LAN9217_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device smsc_lan9217_device = {
+ .name = "smsc911x",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(smsc911x_resources),
+ .resource = smsc911x_resources,
+};
+static void mxc_init_enet(void)
+{
+ (void)platform_device_register(&smsc_lan9217_device);
+}
+#else
+static inline void mxc_init_enet(void)
+{
+}
+#endif
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+static const char fb_default_mode[] = "Epson-VGA";
+
+/* mxc lcd driver */
+static struct platform_device mxc_fb_device = {
+ .name = "mxc_sdc_fb",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &fb_default_mode,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+};
+
+static struct platform_device mxc_fb_wvga_device = {
+ .name = "lcd_claa",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &lcd_data,
+ },
+};
+
+static void mxc_init_fb(void)
+{
+ (void)platform_device_register(&mxc_fb_device);
+ (void)platform_device_register(&mxc_fb_wvga_device);
+}
+#else
+static inline void mxc_init_fb(void)
+{
+}
+#endif
+
+#if defined(CONFIG_BACKLIGHT_MXC)
+static struct platform_device mxcbl_devices[] = {
+#if defined(CONFIG_BACKLIGHT_MXC_IPU) || defined(CONFIG_BACKLIGHT_MXC_IPU_MODULE)
+ {
+ .name = "mxc_ipu_bl",
+ .id = 0,
+ .dev = {
+ .platform_data = (void *)3, /* DISP # for this backlight */
+ },
+ },
+#endif
+};
+static inline void mxc_init_bl(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mxcbl_devices); i++) {
+ platform_device_register(&mxcbl_devices[i]);
+ }
+}
+#else
+static inline void mxc_init_bl(void)
+{
+}
+#endif
+
+#if defined(CONFIG_FB_MXC_TVOUT_CH7024) || \
+ defined(CONFIG_FB_MXC_TVOUT_CH7024_MODULE)
+static int mxc_init_ch7024(void)
+{
+ /* request gpio for phone jack detect */
+ mxc_request_iomux(MX31_PIN_BATT_LINE, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_iomux_set_pad(MX31_PIN_BATT_LINE, PAD_CTL_PKE_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_BATT_LINE), "batt_line");
+ gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_BATT_LINE));
+
+ return 0;
+}
+#else
+static inline int mxc_init_ch7024(void)
+{
+ return 0;
+}
+#endif
+
+static u32 brd_io;
+
+static void mxc_expio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 imr_val;
+ u32 int_valid;
+ u32 expio_irq;
+
+ desc->chip->mask(irq); /* irq = gpio irq number */
+
+ imr_val = __raw_readw(brd_io + INTR_MASK_REG);
+ int_valid = __raw_readw(brd_io + INTR_STATUS_REG) & ~imr_val;
+
+ if (unlikely(!int_valid)) {
+ goto out;
+ }
+
+ expio_irq = MXC_EXP_IO_BASE;
+ for (; int_valid != 0; int_valid >>= 1, expio_irq++) {
+ struct irq_desc *d;
+ if ((int_valid & 1) == 0)
+ continue;
+ d = irq_desc + expio_irq;
+ if (unlikely(!(d->handle_irq))) {
+ printk(KERN_ERR "\nEXPIO irq: %d unhandled\n",
+ expio_irq);
+ BUG(); /* oops */
+ }
+ d->handle_irq(expio_irq, d);
+ }
+
+ out:
+ desc->chip->ack(irq);
+ desc->chip->unmask(irq);
+}
+
+/*
+ * Disable an expio pin's interrupt by setting the bit in the imr.
+ * @param irq an expio virtual irq number
+ */
+static void expio_mask_irq(u32 irq)
+{
+ u16 reg;
+ u32 expio = MXC_IRQ_TO_EXPIO(irq);
+ /* mask the interrupt */
+ reg = __raw_readw(brd_io + INTR_MASK_REG);
+ reg |= (1 << expio);
+ __raw_writew(reg, brd_io + INTR_MASK_REG);
+}
+
+/*
+ * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr.
+ * @param irq an expanded io virtual irq number
+ */
+static void expio_ack_irq(u32 irq)
+{
+ u32 expio = MXC_IRQ_TO_EXPIO(irq);
+ /* clear the interrupt status */
+ __raw_writew(1 << expio, brd_io + INTR_RESET_REG);
+ __raw_writew(0, brd_io + INTR_RESET_REG);
+ /* mask the interrupt */
+ expio_mask_irq(irq);
+}
+
+/*
+ * Enable a expio pin's interrupt by clearing the bit in the imr.
+ * @param irq a expio virtual irq number
+ */
+static void expio_unmask_irq(u32 irq)
+{
+ u16 reg;
+ u32 expio = MXC_IRQ_TO_EXPIO(irq);
+ /* unmask the interrupt */
+ reg = __raw_readw(brd_io + INTR_MASK_REG);
+ reg &= ~(1 << expio);
+ __raw_writew(reg, brd_io + INTR_MASK_REG);
+}
+
+static struct irq_chip expio_irq_chip = {
+ .ack = expio_ack_irq,
+ .mask = expio_mask_irq,
+ .unmask = expio_unmask_irq,
+};
+
+static int __init mxc_expio_init(void)
+{
+ int i;
+
+ brd_io = (u32) ioremap(BOARD_IO_ADDR, SZ_4K);
+ if (brd_io == 0)
+ return -ENOMEM;
+
+ if ((__raw_readw(brd_io + MAGIC_NUMBER1_REG) != 0xAAAA) ||
+ (__raw_readw(brd_io + MAGIC_NUMBER2_REG) != 0x5555) ||
+ (__raw_readw(brd_io + MAGIC_NUMBER3_REG) != 0xCAFE)) {
+ iounmap((void *)brd_io);
+ brd_io = 0;
+ return -ENODEV;
+ }
+
+ pr_info("3-Stack Debug board detected, rev = 0x%04X\n",
+ readw(brd_io + CPLD_CODE_VER_REG));
+
+ /*
+ * Configure INT line as GPIO input
+ */
+ mxc_request_iomux(MX31_PIN_GPIO1_1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1), "gpio1_1");
+ gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_1));
+
+ /* disable the interrupt and clear the status */
+ __raw_writew(0, brd_io + INTR_MASK_REG);
+ __raw_writew(0xFFFF, brd_io + INTR_RESET_REG);
+ __raw_writew(0, brd_io + INTR_RESET_REG);
+ __raw_writew(0x1F, brd_io + INTR_MASK_REG);
+ for (i = MXC_EXP_IO_BASE; i < (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES);
+ i++) {
+ set_irq_chip(i, &expio_irq_chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+ set_irq_type(EXPIO_PARENT_INT, IRQF_TRIGGER_LOW);
+ set_irq_chained_handler(EXPIO_PARENT_INT, mxc_expio_irq_handler);
+
+ return 0;
+}
+
+#if (defined(CONFIG_MXC_PMIC_MC13783) || \
+ defined(CONFIG_MXC_PMIC_MC13783_MODULE)) \
+ && (defined(CONFIG_SND_MXC_PMIC) || defined(CONFIG_SND_MXC_PMIC_MODULE))
+static void __init mxc_init_pmic_audio(void)
+{
+ struct clk *ckih_clk;
+ struct clk *cko_clk;
+
+ /* Enable 26 mhz clock on CKO1 for PMIC audio */
+ ckih_clk = clk_get(NULL, "ckih");
+ cko_clk = clk_get(NULL, "cko1_clk");
+ if (IS_ERR(ckih_clk) || IS_ERR(cko_clk)) {
+ printk(KERN_ERR "Unable to set CKO1 output to CKIH\n");
+ } else {
+ clk_set_parent(cko_clk, ckih_clk);
+ clk_set_rate(cko_clk, clk_get_rate(ckih_clk));
+ clk_enable(cko_clk);
+ }
+ clk_put(ckih_clk);
+ clk_put(cko_clk);
+
+ /* config Audio ports (4 & 5) */
+ mxc_request_iomux(MX31_PIN_SCK4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SRXD4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_STXD4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SFS4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SCK5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SRXD5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_STXD5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SFS5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+}
+#else
+static void __inline mxc_init_pmic_audio(void)
+{
+}
+#endif
+
+/* MMC device data */
+
+#if defined(CONFIG_MMC_MXC) || defined(CONFIG_MMC_MXC_MODULE)
+static struct mxc_mmc_platform_data mmc1_data = {
+ .ocr_mask = MMC_VDD_32_33,
+ .min_clk = 150000,
+ .max_clk = 25000000,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .power_mmc = "GPO1",
+};
+
+/*!
+ * Resource definition for the SDHC1
+ */
+static struct resource mxcsdhc1_resources[] = {
+ [0] = {
+ .start = MMC_SDHC1_BASE_ADDR,
+ .end = MMC_SDHC1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC1,
+ .end = MXC_INT_MMC_SDHC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mxc_mmc_platform_data mmc2_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 |
+ MMC_VDD_31_32,
+ .min_clk = 150000,
+ .max_clk = 25000000,
+ .card_fixed = 1,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+ .power_mmc = "VMMC2",
+};
+
+/*!
+ * Resource definition for the SDHC1
+ */
+static struct resource mxcsdhc2_resources[] = {
+ [0] = {
+ .start = MMC_SDHC2_BASE_ADDR,
+ .end = MMC_SDHC2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC2,
+ .end = MXC_INT_MMC_SDHC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC1 */
+static struct platform_device mxcsdhc1_device = {
+ .name = "mxcmci",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc1_resources),
+ .resource = mxcsdhc1_resources,
+};
+
+/*! Device Definition for MXC SDHC2 */
+static struct platform_device mxcsdhc2_device = {
+ .name = "mxcmci",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc2_resources),
+ .resource = mxcsdhc2_resources,
+};
+
+static inline void mxc_init_mmc(void)
+{
+ int cd_irq;
+
+ cd_irq = sdhc_init_card_det(0);
+ if (cd_irq) {
+ mxcsdhc1_device.resource[2].start = cd_irq;
+ mxcsdhc1_device.resource[2].end = cd_irq;
+ }
+
+ cd_irq = sdhc_init_card_det(1);
+ if (cd_irq) {
+ mxcsdhc2_device.resource[2].start = cd_irq;
+ mxcsdhc2_device.resource[2].end = cd_irq;
+ }
+
+ spba_take_ownership(SPBA_SDHC1, SPBA_MASTER_A | SPBA_MASTER_C);
+ (void)platform_device_register(&mxcsdhc1_device);
+
+ spba_take_ownership(SPBA_SDHC2, SPBA_MASTER_A | SPBA_MASTER_C);
+ (void)platform_device_register(&mxcsdhc2_device);
+}
+#else
+static inline void mxc_init_mmc(void)
+{
+}
+#endif
+
+/*!
+ * Board specific fixup function. It is called by \b setup_arch() in
+ * setup.c file very early on during kernel starts. It allows the user to
+ * statically fill in the proper values for the passed-in parameters. None of
+ * the parameters is used currently.
+ *
+ * @param desc pointer to \b struct \b machine_desc
+ * @param tags pointer to \b struct \b tag
+ * @param cmdline pointer to the command line
+ * @param mi pointer to \b struct \b meminfo
+ */
+static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ mxc_cpu_init();
+
+#ifdef CONFIG_DISCONTIGMEM
+ do {
+ int nid;
+ mi->nr_banks = MXC_NUMNODES;
+ for (nid = 0; nid < mi->nr_banks; nid++) {
+ SET_NODE(mi, nid);
+ }
+ } while (0);
+#endif
+}
+
+/* IDE device data */
+#if defined(CONFIG_BLK_DEV_IDE_MXC) || defined(CONFIG_BLK_DEV_IDE_MXC_MODULE)
+
+/*! Platform Data for MXC IDE */
+static struct mxc_ide_platform_data mxc_ide_data = {
+ .power_drive = "GPO2",
+ .power_io = "GPO3",
+};
+
+static struct platform_device mxc_ide_device = {
+ .name = "mxc_ide",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_ide_data,
+ },
+};
+
+static inline void mxc_init_ide(void)
+{
+ if (platform_device_register(&mxc_ide_device) < 0)
+ printk(KERN_ERR "Error: Registering the ide.\n");
+}
+#else
+static inline void mxc_init_ide(void)
+{
+}
+#endif
+
+#if defined(CONFIG_PATA_FSL) || defined(CONFIG_PATA_FSL_MODULE)
+extern void gpio_ata_active(void);
+extern void gpio_ata_inactive(void);
+
+static int ata_init(struct platform_device *pdev)
+{
+ /* Configure the pins */
+ gpio_ata_active();
+
+ return 0;
+}
+
+static void ata_exit(void)
+{
+ /* Free the pins */
+ gpio_ata_inactive();
+}
+
+static struct fsl_ata_platform_data ata_data = {
+ .udma_mask = ATA_UDMA3, /* board can handle up to UDMA3 */
+ .mwdma_mask = ATA_MWDMA2,
+ .pio_mask = ATA_PIO4,
+ .fifo_alarm = MXC_IDE_DMA_WATERMARK / 2,
+ .max_sg = MXC_IDE_DMA_BD_NR,
+ .init = ata_init,
+ .exit = ata_exit,
+ .core_reg = "GPO2", /*"LDO2", */
+ .io_reg = "GPO3", /*"LDO3", */
+};
+
+static struct resource pata_fsl_resources[] = {
+ [0] = { /* I/O */
+ .start = ATA_BASE_ADDR + 0x00,
+ .end = ATA_BASE_ADDR + 0xD8,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = { /* IRQ */
+ .start = MXC_INT_ATA,
+ .end = MXC_INT_ATA,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device pata_fsl_device = {
+ .name = "pata_fsl",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(pata_fsl_resources),
+ .resource = pata_fsl_resources,
+ .dev = {
+ .platform_data = &ata_data,
+ .coherent_dma_mask = ~0,
+ },
+};
+
+static void __init mxc_init_pata(void)
+{
+ (void)platform_device_register(&pata_fsl_device);
+}
+#else /* CONFIG_PATA_FSL */
+static void __init mxc_init_pata(void)
+{
+}
+#endif /* CONFIG_PATA_FSL */
+
+static void bt_reset(void)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_DCD_DCE1), 1);
+}
+
+static struct mxc_bt_platform_data mxc_bt_data = {
+ .bt_vdd = "VMMC2",
+ .bt_vdd_parent = "GPO1",
+ .bt_vusb = NULL,
+ .bt_vusb_parent = "GPO3",
+ .bt_reset = bt_reset,
+};
+
+static struct platform_device mxc_bt_device = {
+ .name = "mxc_bt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_bt_data,
+ },
+};
+
+static void mxc_init_bluetooth(void)
+{
+ (void)platform_device_register(&mxc_bt_device);
+}
+
+static void mxc_unifi_hardreset(int pin_level)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_DCD_DCE1), pin_level & 0x01);
+}
+
+static struct mxc_unifi_platform_data unifi_data = {
+ .hardreset = mxc_unifi_hardreset,
+
+ /* GPO3 -> enables SW2B 1.8V out - this becomes 1V8 on personality
+ * board, then 1V8_EXT, then BT_VUSB
+ */
+ .reg_gpo1 = "GPO3",
+
+ /* GPO4 -> WiFi_PWEN, but this signal is not used on current boards */
+ .reg_gpo2 = "GPO4",
+
+ .reg_1v5_ana_bb = "VRF1", /* VRF1 -> WL_1V5ANA and WL_1V5BB */
+ .reg_vdd_vpa = "VMMC2", /* VMMC2 -> WL_VDD and WL_VPA */
+ .reg_1v5_dd = "VRF2", /* VRF2 -> WL_1V5DD */
+
+ .host_id = 1,
+};
+
+struct mxc_unifi_platform_data *get_unifi_plat_data(void)
+{
+ return &unifi_data;
+}
+
+EXPORT_SYMBOL(get_unifi_plat_data);
+
+#if defined(CONFIG_GPS_IOCTRL) || defined(CONFIG_GPS_IOCTRL_MODULE)
+static struct mxc_gps_platform_data gps_data = {
+ .core_reg = "GPO3",
+ .analog_reg = "GPO1",
+};
+
+static struct platform_device mxc_gps_device = {
+ .name = "gps_ioctrl",
+ .id = -1,
+ .dev = {
+ .platform_data = &gps_data,
+ },
+};
+
+static void __init mxc_init_gps(void)
+{
+ (void)platform_device_register(&mxc_gps_device);
+}
+#else
+static void __init mxc_init_gps(void)
+{
+}
+#endif
+
+static void __init mx3_3stack_timer_init(void)
+{
+ mxc_clocks_init(32768, 0, 26000000, 0);
+ mxc_timer_init("gpt_clk");
+}
+
+static struct sys_timer mxc_timer = {
+ .init = mx3_3stack_timer_init,
+};
+
+/*!
+ * Board specific initialization.
+ */
+static void __init mxc_board_init(void)
+{
+ /* config CS5 for debug board */
+ mxc_request_iomux(MX31_PIN_CS5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ mxc_cpu_common_init();
+ mxc_register_gpios();
+ early_console_setup(saved_command_line);
+ mxc_init_devices();
+
+ /*Pull down MX31_PIN_USB_BYP to reset USB3317 */
+ mxc_request_iomux(MX31_PIN_USB_BYP, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_USB_BYP), "usb_byp");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_USB_BYP), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_USB_BYP), 0);
+ mxc_free_iomux(MX31_PIN_USB_BYP, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+
+ /* Reset BT/WiFi chip */
+ mxc_request_iomux(MX31_PIN_DCD_DCE1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_DCD_DCE1), "dcd_dce1");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_DCD_DCE1), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_DCD_DCE1), 0);
+
+ mxc_init_pmic_audio();
+ mxc_expio_init();
+ mxc_init_keypad();
+ mxc_init_enet();
+ mxc_init_nand_mtd();
+ mxc_init_ch7024();
+ mx3_3stack_init_mc13783();
+
+ i2c_register_board_info(0, mxc_i2c_board_info,
+ ARRAY_SIZE(mxc_i2c_board_info));
+ spi_register_board_info(mxc_spi_board_info,
+ ARRAY_SIZE(mxc_spi_board_info));
+
+ mxc_init_fb();
+ mxc_init_bl();
+ mxc_init_mmc();
+ mxc_init_ide();
+ mxc_init_pata();
+ mxc_init_bluetooth();
+ mxc_init_gps();
+}
+
+#define PLL_PCTL_REG(pd, mfd, mfi, mfn) \
+ ((((pd) - 1) << 26) + (((mfd) - 1) << 16) + ((mfi) << 10) + mfn)
+
+/* For 26MHz input clock */
+#define PLL_532MHZ PLL_PCTL_REG(1, 13, 10, 3)
+#define PLL_399MHZ PLL_PCTL_REG(1, 52, 7, 35)
+#define PLL_133MHZ PLL_PCTL_REG(2, 26, 5, 3)
+
+#define PDR0_REG(mcu, max, hsp, ipg, nfc) \
+ (MXC_CCM_PDR0_MCU_DIV_##mcu | MXC_CCM_PDR0_MAX_DIV_##max | \
+ MXC_CCM_PDR0_HSP_DIV_##hsp | MXC_CCM_PDR0_IPG_DIV_##ipg | \
+ MXC_CCM_PDR0_NFC_DIV_##nfc)
+
+/* working point(wp): 0 - 133MHz; 1 - 266MHz; 2 - 399MHz; 3 - 532MHz */
+/* 26MHz input clock table */
+static struct cpu_wp cpu_wp_26[] = {
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 133000000,
+ .pdr0_reg = PDR0_REG(4, 4, 4, 2, 6),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 266000000,
+ .pdr0_reg = PDR0_REG(2, 4, 4, 2, 6),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 399000000,
+ .pdr0_reg = PDR0_REG(1, 3, 3, 2, 6),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 532000000,
+ .pdr0_reg = PDR0_REG(1, 4, 4, 2, 6),},
+};
+
+struct cpu_wp *get_cpu_wp(int *wp)
+{
+ *wp = 4;
+ return cpu_wp_26;
+}
+
+/*
+ * The following uses standard kernel macros define in arch.h in order to
+ * initialize __mach_desc_MX3_3STACK data structure.
+ */
+/* *INDENT-OFF* */
+MACHINE_START(MX31_3DS, "Freescale MX31/MX32 3-Stack Board")
+ /* Maintainer: Freescale Semiconductor, Inc. */
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .fixup = fixup_mxc_board,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mxc_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx3/mx3_3stack_gpio.c b/arch/arm/mach-mx3/mx3_3stack_gpio.c
new file mode 100644
index 000000000000..41e0b692bf37
--- /dev/null
+++ b/arch/arm/mach-mx3/mx3_3stack_gpio.c
@@ -0,0 +1,1311 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/pmic_adc.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include "board-mx3_3stack.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx3/mx3_3stack_gpio.c
+ *
+ * @brief This file contains all the GPIO setup functions for the board.
+ *
+ * @ingroup GPIO_MX31
+ */
+
+/*!
+ * Setup GPIO for a UART port to be active
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_active(int port, int no_irda)
+{
+ /*
+ * Configure the IOMUX control registers for the UART signals
+ */
+ switch (port) {
+ /* UART 1 IOMUX Configs */
+ case 0:
+ mxc_request_iomux(MX31_PIN_RXD1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_TXD1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_RTS1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CTS1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ /* UART 2 IOMUX Configs */
+ case 1:
+ mxc_request_iomux(MX31_PIN_TXD2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_RXD2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+
+ mxc_request_iomux(MX31_PIN_RTS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CTS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ /* UART 3 IOMUX Configs */
+ case 2:
+ mxc_request_iomux(MX31_PIN_CSPI3_MOSI, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI3_MISO, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI3_SCLK, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI3_SPI_RDY, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * TODO: Configure the Pad registers for the UART pins
+ */
+}
+
+/*!
+ * Setup GPIO for a UART port to be inactive
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_inactive(int port, int no_irda)
+{
+ switch (port) {
+ case 0:
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_RXD1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_TXD1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_RTS1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_CTS1), NULL);
+
+ mxc_free_iomux(MX31_PIN_RXD1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_TXD1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_RTS1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_CTS1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ break;
+ case 1:
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_TXD2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_RXD2), NULL);
+
+ mxc_free_iomux(MX31_PIN_TXD2, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_RXD2, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_RTS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CTS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Configure the IOMUX GPR register to receive shared SDMA UART events
+ *
+ * @param port a UART port
+ */
+void config_uartdma_event(int port)
+{
+ switch (port) {
+ case 1:
+ /* Configure to receive UART 2 SDMA events */
+ mxc_iomux_set_gpr(MUX_PGP_FIRI, false);
+ break;
+ case 2:
+ /* Configure to receive UART 3 SDMA events */
+ mxc_iomux_set_gpr(MUX_CSPI1_UART3, true);
+ break;
+ case 4:
+ /* Configure to receive UART 5 SDMA events */
+ mxc_iomux_set_gpr(MUX_CSPI3_UART5_SEL, true);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_uart_active);
+EXPORT_SYMBOL(gpio_uart_inactive);
+EXPORT_SYMBOL(config_uartdma_event);
+
+/*!
+ * Setup GPIO for Keypad to be active
+ *
+ */
+void gpio_keypad_active(void)
+{
+ /*
+ * Configure the IOMUX control register for keypad signals.
+ */
+ mxc_request_iomux(MX31_PIN_KEY_COL0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_COL1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_COL2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_COL3, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_KEY_ROW2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_keypad_active);
+
+/*!
+ * Setup GPIO for Keypad to be inactive
+ *
+ */
+void gpio_keypad_inactive(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_KEY_COL0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_KEY_COL1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_KEY_COL2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_KEY_COL3), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_KEY_ROW0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_KEY_ROW1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_KEY_ROW2), NULL);
+
+ mxc_free_iomux(MX31_PIN_KEY_COL0, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_KEY_COL1, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_KEY_COL2, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_KEY_COL3, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_KEY_ROW0, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_KEY_ROW1, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_KEY_ROW2, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+}
+
+EXPORT_SYMBOL(gpio_keypad_inactive);
+
+void gpio_power_key_active(void)
+{
+ mxc_request_iomux(MX31_PIN_GPIO1_2, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_2), NULL);
+ gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_2));
+ mxc_iomux_set_pad(MX31_PIN_GPIO1_2, PAD_CTL_PKE_NONE);
+}
+
+EXPORT_SYMBOL(gpio_power_key_active);
+
+/*!
+ * Setup GPIO for a CSPI device to be active
+ *
+ * @param cspi_mod an CSPI device
+ */
+void gpio_spi_active(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ /* setup GPR for CSPI BB */
+ iomux_config_gpr(MUX_PGP_CSPI_BB, true);
+ /* CSPI1 clock and RDY use full UART ALT1 mode */
+ mxc_request_iomux(MX31_PIN_DSR_DCE1, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_RI_DCE1, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ break;
+ case 1:
+ /* SPI2 */
+ mxc_request_iomux(MX31_PIN_CSPI2_MISO, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_MOSI, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SPI_RDY, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ case 2:
+ /* SPI3 */
+ /*
+ mxc_request_iomux(MX31_PIN_CSPI2_MISO, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_MOSI, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SCLK, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SPI_RDY, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSPI2_SS2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ */
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup 1-Wire to be active
+ */
+void gpio_owire_active(void)
+{
+ /*
+ * Configure the IOMUX control register for 1-wire signals.
+ */
+ iomux_config_mux(MX31_PIN_BATT_LINE, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ iomux_config_pad(MX31_PIN_BATT_LINE, PAD_CTL_LOOPBACK);
+}
+
+/*!
+ * Setup 1-Wire to be active
+ */
+void gpio_owire_inactive(void)
+{
+ /*
+ * Configure the IOMUX control register for 1-wire signals.
+ */
+ iomux_config_mux(MX31_PIN_BATT_LINE, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+}
+
+EXPORT_SYMBOL(gpio_owire_active);
+EXPORT_SYMBOL(gpio_owire_inactive);
+
+/*!
+ * Setup GPIO for a CSPI device to be inactive
+ *
+ * @param cspi_mod a CSPI device
+ */
+void gpio_spi_inactive(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ /* setup GPR for CSPI BB */
+ iomux_config_gpr(MUX_PGP_CSPI_BB, false);
+ /* CSPI1 clock and RDY use full UART ALT1 mode */
+ mxc_free_iomux(MX31_PIN_DSR_DCE1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_RI_DCE1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ case 1:
+ /* SPI2 */
+ mxc_free_iomux(MX31_PIN_CSPI2_MISO, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSPI2_MOSI, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSPI2_SCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSPI2_SPI_RDY, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSPI2_SS0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSPI2_SS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ case 2:
+ /* SPI3 */
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup GPIO for an I2C device to be active
+ *
+ * @param i2c_num an I2C device
+ */
+void gpio_i2c_active(int i2c_num)
+{
+ switch (i2c_num) {
+ case 0:
+ mxc_request_iomux(MX31_PIN_I2C_CLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_I2C_DAT, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ case 1:
+ mxc_request_iomux(MX31_PIN_CSPI2_MOSI, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI2_MISO, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ break;
+ case 2:
+ mxc_request_iomux(MX31_PIN_CSPI2_SS2, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI2_SCLK, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ break;
+ default:
+ break;
+ }
+
+}
+
+/*!
+ * Setup GPIO for an I2C device to be inactive
+ *
+ * @param i2c_num an I2C device
+ */
+void gpio_i2c_inactive(int i2c_num)
+{
+ switch (i2c_num) {
+ case 0:
+ mxc_free_iomux(MX31_PIN_I2C_CLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_I2C_DAT, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ break;
+ case 1:
+ mxc_free_iomux(MX31_PIN_CSPI2_MOSI, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_ALT1);
+ mxc_free_iomux(MX31_PIN_CSPI2_MISO, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_ALT1);
+ break;
+ case 2:
+ mxc_request_iomux(MX31_PIN_CSPI2_SS2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_CSPI2_SCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_ALT1);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * This function configures the IOMux block for PMIC standard operations.
+ *
+ */
+void gpio_pmic_active(void)
+{
+ mxc_request_iomux(MX31_PIN_GPIO1_3, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO1_3), NULL);
+ gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO1_3));
+}
+
+EXPORT_SYMBOL(gpio_pmic_active);
+
+/*!
+ * Setup GPIO for SDHC to be active
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_active(int module)
+{
+ switch (module) {
+ case 0:
+ mxc_request_iomux(MX31_PIN_SD1_CLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SD1_CMD, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SD1_DATA0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SD1_DATA1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SD1_DATA2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_SD1_DATA3, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+
+ mxc_iomux_set_pad(MX31_PIN_SD1_CLK,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST
+ | PAD_CTL_100K_PU));
+ mxc_iomux_set_pad(MX31_PIN_SD1_CMD,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST
+ | PAD_CTL_100K_PU));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA0,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST
+ | PAD_CTL_100K_PU));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA1,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST
+ | PAD_CTL_100K_PU));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA2,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST
+ | PAD_CTL_100K_PU));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA3,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST
+ | PAD_CTL_100K_PU));
+
+ /*
+ * Active the Buffer Enable Pin only if there is
+ * a card in slot.
+ * To fix the card voltage issue caused by
+ * bi-directional chip TXB0108 on 3Stack
+ */
+ if (gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_GPIO3_1)))
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_GPIO3_0), 0);
+ else
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_GPIO3_0), 1);
+ break;
+ case 1:
+ mxc_request_iomux(MX31_PIN_PC_CD2_B, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_PC_CD1_B, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_PC_WAIT_B, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_PC_READY, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_PC_VS1, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ mxc_request_iomux(MX31_PIN_PC_PWRON, OUTPUTCONFIG_ALT1,
+ INPUTCONFIG_ALT1);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_sdhc_active);
+
+/*!
+ * Setup GPIO for SDHC1 to be inactive
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_inactive(int module)
+{
+ switch (module) {
+ case 0:
+ mxc_free_iomux(MX31_PIN_SD1_CLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_SD1_CMD, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_SD1_DATA0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_SD1_DATA1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_SD1_DATA2, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_SD1_DATA3, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+
+ mxc_iomux_set_pad(MX31_PIN_SD1_CLK,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX31_PIN_SD1_CMD,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA0,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA1,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA2,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX31_PIN_SD1_DATA3,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+
+ /* Buffer Enable Pin of SD, Active HI */
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_GPIO3_0), 0);
+ break;
+ case 1:
+ /* TODO:what are the pins for SDHC2? */
+ mxc_free_iomux(MX31_PIN_PC_CD2_B, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_PC_CD1_B, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_PC_WAIT_B, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_PC_READY, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_PC_VS1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_PC_PWRON, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_NONE);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_sdhc_inactive);
+
+/*
+ * Probe for the card. If present the GPIO data would be set.
+ */
+unsigned int sdhc_get_card_det_status(struct device *dev)
+{
+ int ret;
+
+ if (to_platform_device(dev)->id == 0) {
+ ret = gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_GPIO3_1));
+ /*
+ * Active the Buffer Enable Pin only if there is
+ * a card in slot.
+ * To fix the card voltage issue caused by
+ * bi-directional chip TXB0108 on 3Stack
+ */
+ if (ret)
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_GPIO3_0), 0);
+ else
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_GPIO3_0), 1);
+ return ret;
+ } else
+ return gpio_get_value(IOMUX_TO_GPIO(MX31_PIN_GPIO1_2));
+}
+
+EXPORT_SYMBOL(sdhc_get_card_det_status);
+
+/*
+ * Return the card detect pin.
+ */
+int sdhc_init_card_det(int id)
+{
+ if (id == 0) {
+ /* Buffer Enable Pin, Active HI */
+ mxc_request_iomux(MX31_PIN_GPIO3_0, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO3_0), "gpio3_0");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_GPIO3_0), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_GPIO3_0), 0);
+
+ /* CD Pin */
+ mxc_request_iomux(MX31_PIN_GPIO3_1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ mxc_iomux_set_pad(MX31_PIN_GPIO3_1, PAD_CTL_PKE_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_GPIO3_1), "gpio3_1");
+ gpio_direction_input(IOMUX_TO_GPIO(MX31_PIN_GPIO3_1));
+ return IOMUX_TO_IRQ(MX31_PIN_GPIO3_1);
+ } else {
+ iomux_config_mux(MX31_PIN_GPIO1_2, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ return IOMUX_TO_IRQ(MX31_PIN_GPIO1_2);
+
+ }
+}
+
+EXPORT_SYMBOL(sdhc_init_card_det);
+
+/*!
+ * Get SD1_WP ADIN7 of ATLAS pin value to detect write protection
+ */
+int sdhc_write_protect(struct device *dev)
+{
+ unsigned short rc = 0;
+
+ pmic_adc_convert(GEN_PURPOSE_AD7, &rc);
+ if (rc > 0)
+ return 1;
+ else
+ return 0;
+}
+
+EXPORT_SYMBOL(sdhc_write_protect);
+
+/*!
+ * Setup GPIO for LCD to be active
+ *
+ */
+void gpio_lcd_active(void)
+{
+
+ mxc_request_iomux(MX31_PIN_LD0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD7, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD8, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD9, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD10, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD11, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD12, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD13, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD14, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD15, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_LD16, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // LD16
+ mxc_request_iomux(MX31_PIN_LD17, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // LD17
+ mxc_request_iomux(MX31_PIN_VSYNC3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // VSYNC
+ mxc_request_iomux(MX31_PIN_HSYNC, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // HSYNC
+ mxc_request_iomux(MX31_PIN_FPSHIFT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // CLK
+ mxc_request_iomux(MX31_PIN_CONTRAST, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // CONTR
+
+#ifdef CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL
+ mxc_request_iomux(MX31_PIN_DRDY0,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); /* DRDY */
+ mxc_request_iomux(MX31_PIN_D3_REV,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); /* REV */
+ mxc_request_iomux(MX31_PIN_D3_SPL,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); /* SPL */
+ mxc_request_iomux(MX31_PIN_D3_CLS,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); /* CLS */
+#else
+ /* ensure that LCDIO(1.8V) has been turn on */
+ /* active reset line GPIO */
+ mxc_request_iomux(MX31_PIN_LCS1, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_LCS1), "lcs1");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_LCS1), 0);
+ /* do reset */
+ mdelay(10); /* tRES >= 100us */
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_LCS1), 1);
+
+ /* enable data */
+ mxc_request_iomux(MX31_PIN_SER_RS, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_SER_RS), "ser_rs");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_SER_RS), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_SER_RS), 1);
+#endif
+}
+
+/*!
+ * Setup GPIO for LCD to be inactive
+ *
+ */
+void gpio_lcd_inactive(void)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_SER_RS), 0);
+}
+
+/*!
+ * Switch to the specified sensor - MX31 ADS has two
+ *
+ */
+void gpio_sensor_select(int sensor)
+{
+}
+
+/*!
+ * Setup GPIO for sensor to be active
+ *
+ */
+void gpio_sensor_active(void)
+{
+ gpio_sensor_select(0);
+
+ /*
+ * Configure the iomuxen for the CSI.
+ */
+
+ mxc_request_iomux(MX31_PIN_CSI_D5, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_CSI_D5), "csi_d5");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_CSI_D5), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_CSI_D5), 0);
+
+ mxc_request_iomux(MX31_PIN_CSI_D6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D7, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D8, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D9, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D10, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D11, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D12, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D13, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D14, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_D15, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_HSYNC, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_MCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_PIXCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_CSI_VSYNC, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+
+ if (mxc_request_iomux(MX31_PIN_A23, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE)
+ == 0) {
+ printk(KERN_ERR "%s:REGEN set request gpio ok\n", __func__);
+ } else {
+ printk(KERN_ERR "%s:REGEN set error, request gpio error\n",
+ __func__);
+ return;
+ }
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_SD_D_IO), "sd_d_io");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_SD_D_IO), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_SD_D_IO), 1);
+}
+
+EXPORT_SYMBOL(gpio_sensor_active);
+
+void gpio_sensor_reset(bool flag)
+{
+}
+
+EXPORT_SYMBOL(gpio_sensor_reset);
+
+/*!
+ * Setup GPIO for sensor to be inactive
+ *
+ */
+void gpio_sensor_inactive(void)
+{
+ mxc_free_iomux(MX31_PIN_CSI_D5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D7, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D8, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D9, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D10, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D11, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D12, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D13, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D14, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_D15, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_HSYNC, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_MCLK, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_PIXCLK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_free_iomux(MX31_PIN_CSI_VSYNC, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_sensor_inactive);
+
+/*!
+ * Setup GPIO for ATA interface
+ *
+ */
+void gpio_ata_active(void)
+{
+ /*
+ * Configure the GPR for ATA group B signals
+ */
+ mxc_iomux_set_gpr(MUX_PGP_ATA_8 | MUX_PGP_ATA_5 | MUX_PGP_ATA_4 |
+ MUX_PGP_ATA_3 | MUX_PGP_ATA_2, false);
+
+ mxc_iomux_set_gpr(MUX_PGP_ATA_9 | MUX_PGP_ATA_7 | MUX_PGP_ATA_6 |
+ MUX_PGP_ATA_1, true);
+
+ /*
+ * Configure the IOMUX for ATA group B signals
+ */
+
+ mxc_request_iomux(MX31_PIN_CSPI1_MOSI, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D0
+ mxc_request_iomux(MX31_PIN_CSPI1_MISO, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D1
+ mxc_request_iomux(MX31_PIN_CSPI1_SS0, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D2
+ mxc_request_iomux(MX31_PIN_CSPI1_SS1, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D3
+ mxc_request_iomux(MX31_PIN_CSPI1_SS2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D4
+ mxc_request_iomux(MX31_PIN_CSPI1_SCLK, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D5
+ mxc_request_iomux(MX31_PIN_CSPI1_SPI_RDY, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D6
+ mxc_request_iomux(MX31_PIN_STXD3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D7
+ mxc_request_iomux(MX31_PIN_SRXD3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D8
+ mxc_request_iomux(MX31_PIN_SCK3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D9
+ mxc_request_iomux(MX31_PIN_SFS3, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D10
+ mxc_request_iomux(MX31_PIN_STXD6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D11
+ mxc_request_iomux(MX31_PIN_SRXD6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D12
+ mxc_request_iomux(MX31_PIN_SCK6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D13
+ mxc_request_iomux(MX31_PIN_CAPTURE, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D14
+ mxc_request_iomux(MX31_PIN_COMPARE, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_D15
+
+ /* Config the multiplex pin of ATA interface DIR, DA0-2, INTRQ, DMARQ */
+ mxc_request_iomux(MX31_PIN_KEY_COL4, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_DMARQ_B
+ mxc_request_iomux(MX31_PIN_KEY_ROW6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_INTRQ_B
+ mxc_request_iomux(MX31_PIN_KEY_COL5, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_DA0
+ mxc_request_iomux(MX31_PIN_KEY_COL6, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_DA1
+ mxc_request_iomux(MX31_PIN_KEY_COL7, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_DA2
+ mxc_request_iomux(MX31_PIN_KEY_ROW7, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC); // ATA_BUFFER_DIR
+
+ /* HDD_ENABLE_B(H:Disable,L:Enable) */
+ mxc_request_iomux(MX31_PIN_CSI_D4, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO); // HDD_ENABLE_B
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_CSI_D4), "csi_d4");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_CSI_D4), 0);
+ mdelay(10);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_CSI_D4), 0);
+ mdelay(10);
+
+ /* These ATA pins are common to Group A and Group B */
+
+ mxc_request_iomux(MX31_PIN_ATA_CS0, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_ATA_CS1, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_ATA_DIOR, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_ATA_DIOW, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_ATA_DMACK, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_ATA_RESET_B, OUTPUTCONFIG_FUNC,
+ INPUTCONFIG_FUNC);
+ mxc_request_iomux(MX31_PIN_PWMO, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ /* Need fast slew rate for UDMA mode */
+
+#define ATA_DAT_PAD_CFG (PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE | PAD_CTL_100K_PU)
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_MISO, ATA_DAT_PAD_CFG); // data 0
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_MOSI, ATA_DAT_PAD_CFG); // data 1
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS0, ATA_DAT_PAD_CFG); // data 2
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS1, ATA_DAT_PAD_CFG); // data 3
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS2, ATA_DAT_PAD_CFG); // data 4
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SCLK, ATA_DAT_PAD_CFG); // data 5
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SPI_RDY, ATA_DAT_PAD_CFG); // data 6
+ mxc_iomux_set_pad(MX31_PIN_STXD3, ATA_DAT_PAD_CFG); // data 7
+ mxc_iomux_set_pad(MX31_PIN_SRXD3, ATA_DAT_PAD_CFG); // data 8
+ mxc_iomux_set_pad(MX31_PIN_SCK3, ATA_DAT_PAD_CFG); // data 9
+ mxc_iomux_set_pad(MX31_PIN_SFS3, ATA_DAT_PAD_CFG); // data 10
+ mxc_iomux_set_pad(MX31_PIN_STXD6, ATA_DAT_PAD_CFG); // data 11
+ mxc_iomux_set_pad(MX31_PIN_SRXD6, ATA_DAT_PAD_CFG); // data 12
+ mxc_iomux_set_pad(MX31_PIN_SCK6, ATA_DAT_PAD_CFG); // data 13
+ mxc_iomux_set_pad(MX31_PIN_CAPTURE, ATA_DAT_PAD_CFG); // data 14
+ mxc_iomux_set_pad(MX31_PIN_COMPARE, ATA_DAT_PAD_CFG); // data 15
+#undef ATA_DAT_PAD_CFG
+
+#define ATA_CTL_PAD_CFG (PAD_CTL_SRE_SLOW | PAD_CTL_PKE_NONE)
+ mxc_iomux_set_pad(MX31_PIN_KEY_COL4, ATA_CTL_PAD_CFG); // ATA_DMARQ);
+ mxc_iomux_set_pad(MX31_PIN_KEY_ROW6, ATA_CTL_PAD_CFG); // ATA_INTRQ);
+ mxc_iomux_set_pad(MX31_PIN_KEY_COL5, ATA_CTL_PAD_CFG); //
+ mxc_iomux_set_pad(MX31_PIN_KEY_COL6, ATA_CTL_PAD_CFG); //
+ mxc_iomux_set_pad(MX31_PIN_KEY_COL7, ATA_CTL_PAD_CFG); //
+ mxc_iomux_set_pad(MX31_PIN_KEY_ROW7, ATA_CTL_PAD_CFG); //
+
+ mxc_iomux_set_pad(MX31_PIN_ATA_CS0, ATA_CTL_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_ATA_CS1, ATA_CTL_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_ATA_DIOR, ATA_CTL_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_ATA_DIOW, ATA_CTL_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_ATA_DMACK, ATA_CTL_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_ATA_RESET_B, ATA_CTL_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_PWMO, ATA_CTL_PAD_CFG);
+#undef ATA_CTL_PAD_CFG
+}
+
+EXPORT_SYMBOL(gpio_ata_active);
+
+/*!
+ * Restore ATA interface pins to reset values
+ *
+ */
+void gpio_ata_inactive(void)
+{
+ /*
+ * Turn off ATA group B signals
+ */
+ mxc_request_iomux(MX31_PIN_CSPI1_MOSI, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D0
+ mxc_request_iomux(MX31_PIN_CSPI1_MISO, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D1
+ mxc_request_iomux(MX31_PIN_CSPI1_SS0, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D2
+ mxc_request_iomux(MX31_PIN_CSPI1_SS1, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D3
+ mxc_request_iomux(MX31_PIN_CSPI1_SS2, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D4
+ mxc_request_iomux(MX31_PIN_CSPI1_SCLK, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D5
+ mxc_request_iomux(MX31_PIN_CSPI1_SPI_RDY, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D6
+ mxc_request_iomux(MX31_PIN_STXD3, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D7
+ mxc_request_iomux(MX31_PIN_SRXD3, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D8
+ mxc_request_iomux(MX31_PIN_SCK3, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D9
+ mxc_request_iomux(MX31_PIN_SFS3, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D10
+ mxc_request_iomux(MX31_PIN_STXD6, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D11
+ mxc_request_iomux(MX31_PIN_SRXD6, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D12
+ mxc_request_iomux(MX31_PIN_SCK6, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D13
+ mxc_request_iomux(MX31_PIN_CAPTURE, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D14
+ mxc_request_iomux(MX31_PIN_COMPARE, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_D15
+
+ /* Config the multiplex pin of ATA interface DIR, DA0-2, INTRQ, DMARQ */
+ mxc_request_iomux(MX31_PIN_KEY_COL4, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_DMARQ_B
+ mxc_request_iomux(MX31_PIN_KEY_ROW6, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_INTRQ_B
+ mxc_request_iomux(MX31_PIN_KEY_COL5, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_DA0
+ mxc_request_iomux(MX31_PIN_KEY_COL6, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_DA1
+ mxc_request_iomux(MX31_PIN_KEY_COL7, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_DA2
+ mxc_request_iomux(MX31_PIN_KEY_ROW7, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE); // ATA_BUFFER_DIR
+
+ /* HDD_BUFF_EN (H:A->B, L:B->A) and HDD_ENABLE_B(H:Disable,L:Enable) */
+ mxc_free_iomux(MX31_PIN_CSI_D4, OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+
+ /* These ATA pins are common to Group A and Group B */
+
+ mxc_request_iomux(MX31_PIN_ATA_CS0, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_ATA_CS1, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_ATA_DIOR, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_ATA_DIOW, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_ATA_DMACK, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_ATA_RESET_B, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ mxc_request_iomux(MX31_PIN_PWMO, OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+
+ /* Needed fast slew rate for UDMA mode */
+
+#define ATA_DAT_PAD_CFG (PAD_CTL_SRE_SLOW | PAD_CTL_DRV_NORMAL | PAD_CTL_PKE_NONE)
+ mxc_iomux_set_pad(MX31_PIN_KEY_COL4, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_KEY_ROW6, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_KEY_COL5, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_KEY_COL6, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_KEY_COL7, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_KEY_ROW7, ATA_DAT_PAD_CFG);
+
+ mxc_iomux_set_pad(MX31_PIN_ATA_CS0, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_ATA_CS1, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_ATA_DIOR, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_ATA_DIOW, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_ATA_DMACK, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_ATA_RESET_B, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_PWMO, ATA_DAT_PAD_CFG);
+
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_MISO, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_MOSI, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS0, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS1, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SS2, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SCLK, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_CSPI1_SPI_RDY, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_STXD3, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_SRXD3, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_SCK3, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_SFS3, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_STXD6, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_SRXD6, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_SCK6, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_CAPTURE, ATA_DAT_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_COMPARE, ATA_DAT_PAD_CFG);
+#undef ATA_DAT_PAD_CFG
+}
+
+EXPORT_SYMBOL(gpio_ata_inactive);
+
+/* *INDENT-OFF* */
+/*
+ * USB Host 1
+ * pins conflict with SPI1, ATA, UART3
+ */
+int gpio_usbh1_active(void)
+{
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_usbh1_active);
+
+void gpio_usbh1_inactive(void)
+{
+ /* Do nothing as pins don't have/support GPIO mode */
+
+}
+
+EXPORT_SYMBOL(gpio_usbh1_inactive);
+
+/*
+ * USB Host 2
+ * pins conflict with UART5, PCMCIA
+ */
+int gpio_usbh2_active(void)
+{
+ if (mxc_request_iomux(MX31_PIN_USBH2_CLK,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBH2_DIR,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBH2_NXT,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBH2_STP,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBH2_DATA0,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBH2_DATA1,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_PC_VS2, /* USBH2_DATA2 */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_PC_BVD1, /* USBH2_DATA3 */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_PC_BVD2, /* USBH2_DATA4 */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_PC_RST, /* USBH2_DATA5 */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_IOIS16, /* USBH2_DATA6 */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1) ||
+ mxc_request_iomux(MX31_PIN_PC_RW_B, /* USBH2_DATA7 */
+ OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1)) {
+ return -EINVAL;
+ }
+ mxc_iomux_set_pad(MX31_PIN_USBH2_CLK,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST |
+ PAD_CTL_PKE_NONE));
+ mxc_iomux_set_pad(MX31_PIN_USBH2_DIR,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBH2_NXT,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBH2_STP,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBH2_DATA0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBH2_DATA1,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_PC_VS2,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_PC_BVD1,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_PC_BVD2,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_PC_RST,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_IOIS16,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_PC_RW_B,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+
+ mxc_request_iomux(MX31_PIN_USB_BYP, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_USB_BYP), "usb_byp");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_USB_BYP), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_USB_BYP), 0);
+ mdelay(1);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_USB_BYP), 1);
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_usbh2_active);
+
+void gpio_usbh2_inactive(void)
+{
+ mxc_iomux_set_pad(MX31_PIN_USBH2_CLK,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBH2_DIR,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBH2_NXT,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBH2_STP,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBH2_DATA0,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBH2_DATA1,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_PC_VS2,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_PC_BVD1,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_PC_BVD2,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_PC_RST,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_IOIS16,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_PC_RW_B,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_FAST));
+
+ mxc_free_iomux(MX31_PIN_USBH2_CLK,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_USBH2_DIR,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_USBH2_NXT,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_USBH2_STP,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_USBH2_DATA0,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_USBH2_DATA1,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+
+ mxc_free_iomux(MX31_PIN_PC_VS2, /* USBH2_DATA2 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_PC_BVD1, /* USBH2_DATA3 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_PC_BVD2, /* USBH2_DATA4 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_PC_RST, /* USBH2_DATA5 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_IOIS16, /* USBH2_DATA6 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+ mxc_free_iomux(MX31_PIN_PC_RW_B, /* USBH2_DATA7 */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+
+ mxc_free_iomux(MX31_PIN_USB_BYP, /* USBH2 PHY reset */
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_NONE);
+}
+
+EXPORT_SYMBOL(gpio_usbh2_inactive);
+
+/*
+ * USB OTG HS port
+ */
+int gpio_usbotg_hs_active(void)
+{
+ if (mxc_request_iomux(MX31_PIN_USBOTG_DATA0,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA1,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA2,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA3,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA4,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA5,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA6,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA7,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_CLK,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DIR,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_NXT,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_STP,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC)) {
+ return -EINVAL;
+ }
+
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA1,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA2,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA3,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA4,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA5,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA6,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA7,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_CLK,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DIR,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_NXT,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_STP,
+ (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST));
+
+ /* reset transceiver */
+ mxc_request_iomux(MX31_PIN_USB_PWR, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_USB_PWR), "usb_pwr");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_USB_PWR), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_USB_PWR), 0);
+ mdelay(1);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_USB_PWR), 1);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_usbotg_hs_active);
+
+void gpio_usbotg_hs_inactive(void)
+{
+ mxc_free_iomux(MX31_PIN_USB_PWR, OUTPUTCONFIG_GPIO,
+ INPUTCONFIG_GPIO);
+}
+
+EXPORT_SYMBOL(gpio_usbotg_hs_inactive);
+
+/*!
+ * USB OTG FS port
+ */
+int gpio_usbotg_fs_active(void)
+{
+ if (mxc_request_iomux(MX31_PIN_USBOTG_DATA0,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA1,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA2,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA3,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA4,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA5,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA6,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DATA7,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_CLK,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_DIR,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_NXT,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USBOTG_STP,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC) ||
+ mxc_request_iomux(MX31_PIN_USB_PWR,
+ OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC))
+ return -EINVAL;
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_usbotg_fs_active);
+
+void gpio_usbotg_fs_inactive(void)
+{
+ /* Do nothing as pins doesn't have/support GPIO mode */
+
+}
+
+EXPORT_SYMBOL(gpio_usbotg_fs_inactive);
+
+/*!
+ * GPS GPIO
+ */
+void gpio_gps_active(void)
+{
+ /* POWER_EN */
+ mxc_request_iomux(MX31_PIN_SCLK0,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_SCLK0), "sclk0");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_SCLK0), 0);
+ /* Reset Pin */
+ mxc_request_iomux(MX31_PIN_DCD_DTE1,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX31_PIN_DCD_DTE1), "dcd_dte1");
+ gpio_direction_output(IOMUX_TO_GPIO(MX31_PIN_DCD_DTE1), 0);
+
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_SCLK0), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_DCD_DTE1), 0);
+
+ msleep(5);
+ gpio_set_value(IOMUX_TO_GPIO(MX31_PIN_DCD_DTE1), 1);
+ msleep(5);
+}
+
+EXPORT_SYMBOL(gpio_gps_active);
+
+int gpio_gps_access(int para)
+{
+ iomux_pin_name_t pin;
+ pin = (para & 0x1) ? MX31_PIN_SCLK0 : MX31_PIN_DCD_DTE1;
+
+ if (para & 0x4) /* Read GPIO */
+ return gpio_get_value(IOMUX_TO_GPIO(pin));
+ else if (para & 0x2) /* Write GPIO */
+ gpio_set_value(IOMUX_TO_GPIO(pin), 1);
+ else
+ gpio_set_value(IOMUX_TO_GPIO(pin), 0);
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_gps_access);
+
+void gpio_gps_inactive(void)
+{
+ mxc_free_iomux(MX31_PIN_DCD_DTE1,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+ mxc_free_iomux(MX31_PIN_SCLK0,
+ OUTPUTCONFIG_GPIO, INPUTCONFIG_GPIO);
+}
+
+EXPORT_SYMBOL(gpio_gps_inactive);
diff --git a/arch/arm/mach-mx3/mx3_3stack_pmic_mc13783.c b/arch/arm/mach-mx3/mx3_3stack_pmic_mc13783.c
new file mode 100644
index 000000000000..fa1dcea8d09e
--- /dev/null
+++ b/arch/arm/mach-mx3/mx3_3stack_pmic_mc13783.c
@@ -0,0 +1,279 @@
+/*
+ * mx3-3stack-pmic-mc13783.c -- i.MX3 3STACK Driver for Atlas MC13783 PMIC
+ */
+ /*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+ /*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/pmic_external.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/mc13783/core.h>
+#include "iomux.h"
+
+/*
+ * Convenience conversion.
+ * Here atm, maybe there is somewhere better for this.
+ */
+#define mV_to_uV(mV) (mV * 1000)
+
+static struct regulator_consumer_supply gpo_consumers[] = {
+ {
+ .supply = "GPO1",
+ }
+};
+
+struct mc13783;
+
+static struct regulator_init_data violo_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1200), /* mc13783 allows min of 1200. */
+ .max_uV = mV_to_uV(1800), /* mc13783 allows max of 1800. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vdig_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1200), /* mc13783 allows min of 1200. */
+ .max_uV = mV_to_uV(1800), /* mc13783 allows max of 1800. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vgen_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1100), /* mc13783 allows min of 1100. */
+ .max_uV = mV_to_uV(2775), /* mc13783 allows max of 2775. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vrfdig_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1200), /* mc13783 allows min of 1200. */
+ .max_uV = mV_to_uV(1875), /* mc13783 allows max of 1875. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vrfref_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(2475), /* mc13783 allows min of 2475. */
+ .max_uV = mV_to_uV(2775), /* mc13783 allows max of 2775. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vrfcp_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(2700), /* mc13783 allows min of 2700. */
+ .max_uV = mV_to_uV(2775), /* mc13783 allows max of 2775. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vsim_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1800), /* mc13783 allows min of 1800. */
+ .max_uV = mV_to_uV(2900), /* mc13783 allows max of 2900. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vesim_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1800), /* mc13783 allows min of 1800. */
+ .max_uV = mV_to_uV(2900), /* mc13783 allows max of 2900. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vcam_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1500), /* mc13783 allows min of 1500. */
+ .max_uV = mV_to_uV(3000), /* mc13783 allows max of 3000. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vvib_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1300), /* mc13783 allows min of 1300. */
+ .max_uV = mV_to_uV(3000), /* mc13783 allows max of 3000. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vrf_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1500), /* mc13783 allows min of 1500. */
+ .max_uV = mV_to_uV(2775), /* mc13783 allows max of 2775. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vmmc_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1600), /* mc13783 allows min of 1600. */
+ .max_uV = mV_to_uV(3000), /* mc13783 allows max of 3000. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data sw3_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(5000), /* mc13783 allows min of 5000. */
+ .max_uV = mV_to_uV(5500), /* mc13783 allows max of 5500. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data sw1_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1200), /* mc13783 allows min of 900. */
+ .max_uV = mV_to_uV(1600), /* mc13783 allows max of 2200. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_FAST
+ | REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_IDLE
+ | REGULATOR_MODE_STANDBY,
+ .always_on = 1,
+ .boot_on = 1,
+ .initial_state = PM_SUSPEND_MEM,
+ .state_mem = {
+ .uV = mV_to_uV(1250),
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ }
+};
+
+static struct regulator_init_data sw_init = {
+ .constraints = {
+ .min_uV = mV_to_uV(1200), /* mc13783 allows min of 900. */
+ .max_uV = mV_to_uV(2200), /* mc13783 allows max of 2200. */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vaudio_init = {
+ .constraints = {
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data viohi_init = {
+ .constraints = {
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vrfbg_init = {
+ .constraints = {
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data gpo1_init = {
+ .constraints = {
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data gpo_init = {
+ .constraints = {
+ },
+ .num_consumer_supplies = ARRAY_SIZE(gpo_consumers),
+ .consumer_supplies = gpo_consumers,
+};
+
+static int mc13783_regulator_init(void *data)
+{
+ struct mc13783 *mc13783 = data;
+ unsigned int value;
+
+ /*most regulators are controled by standby signal*/
+ /*except violo*/
+ pmic_read_reg(REG_REGULATOR_MODE_0, &value, 0xffffff);
+ value |= 0x492412;
+ pmic_write_reg(REG_REGULATOR_MODE_0, value, 0xffffff);
+ pmic_read_reg(REG_REGULATOR_MODE_1, &value, 0xffffff);
+ value |= 0x492492;
+ pmic_write_reg(REG_REGULATOR_MODE_1, value, 0xffffff);
+ /*also sw3 is controled by standby signal*/
+ pmic_read_reg(REG_SWITCHERS_5, &value, 0xffffff);
+ value |= 0x200000;
+ pmic_write_reg(REG_SWITCHERS_5, value, 0xffffff);
+
+ mc13783_register_regulator(mc13783, MC13783_SW1A, &sw1_init);
+ mc13783_register_regulator(mc13783, MC13783_SW1B, &sw_init);
+ mc13783_register_regulator(mc13783, MC13783_SW2A, &sw_init);
+ mc13783_register_regulator(mc13783, MC13783_SW2B, &sw_init);
+ mc13783_register_regulator(mc13783, MC13783_SW3, &sw3_init);
+ mc13783_register_regulator(mc13783, MC13783_VMMC1, &vmmc_init);
+ mc13783_register_regulator(mc13783, MC13783_VMMC2, &vmmc_init);
+ mc13783_register_regulator(mc13783, MC13783_VVIB, &vvib_init);
+ mc13783_register_regulator(mc13783, MC13783_VIOHI, &viohi_init);
+ mc13783_register_regulator(mc13783, MC13783_VIOLO, &violo_init);
+ mc13783_register_regulator(mc13783, MC13783_VDIG, &vdig_init);
+ mc13783_register_regulator(mc13783, MC13783_VRFDIG, &vrfdig_init);
+ mc13783_register_regulator(mc13783, MC13783_VRFREF, &vrfref_init);
+ mc13783_register_regulator(mc13783, MC13783_VRFCP, &vrfcp_init);
+ mc13783_register_regulator(mc13783, MC13783_VRF1, &vrf_init);
+ mc13783_register_regulator(mc13783, MC13783_VRF2, &vrf_init);
+ mc13783_register_regulator(mc13783, MC13783_VAUDIO, &vaudio_init);
+ mc13783_register_regulator(mc13783, MC13783_VCAM, &vcam_init);
+ mc13783_register_regulator(mc13783, MC13783_VGEN, &vgen_init);
+ mc13783_register_regulator(mc13783, MC13783_VSIM, &vsim_init);
+ mc13783_register_regulator(mc13783, MC13783_VESIM, &vesim_init);
+ mc13783_register_regulator(mc13783, MC13783_GPO1, &gpo1_init);
+ mc13783_register_regulator(mc13783, MC13783_GPO2, &gpo_init);
+ mc13783_register_regulator(mc13783, MC13783_GPO3, &gpo_init);
+ mc13783_register_regulator(mc13783, MC13783_GPO4, &gpo_init);
+
+ return 0;
+}
+
+static struct pmic_platform_data mc13783_plat = {
+ .init = mc13783_regulator_init,
+ .power_key_irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_2),
+};
+
+static struct spi_board_info __initdata mc13783_spi_device = {
+ .modalias = "pmic_spi",
+ .irq = IOMUX_TO_IRQ(MX31_PIN_GPIO1_3),
+ .max_speed_hz = 4000000,
+ .bus_num = 2,
+ .platform_data = &mc13783_plat,
+ .chip_select = 2,
+};
+
+int __init mx3_3stack_init_mc13783(void)
+{
+ return spi_register_board_info(&mc13783_spi_device, 1);
+}
+
diff --git a/arch/arm/mach-mx3/mxc_pm.c b/arch/arm/mach-mx3/mxc_pm.c
new file mode 100644
index 000000000000..3fcce2472983
--- /dev/null
+++ b/arch/arm/mach-mx3/mxc_pm.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup DPM_MX31 Power Management
+ * @ingroup MSL_MX31
+ */
+/*!
+ * @file mach-mx3/mxc_pm.c
+ *
+ * @brief This file provides all the kernel level and user level API
+ * definitions for the CRM_MCU and DPLL in mx3.
+ *
+ * @ingroup DPM_MX31
+ */
+
+/*
+ * Include Files
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <mach/hardware.h>
+#include <mach/system.h>
+#include <mach/mxc_pm.h>
+#include <asm/cacheflush.h>
+#include <asm/irq.h>
+#include <mach/common.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include "crm_regs.h"
+
+/* Local defines */
+#define FREQ_COMP_TOLERANCE 200 /* tolerance percentage times 100 */
+#define MCU_PLL_MAX_FREQ 600000000 /* Maximum frequency MCU PLL clock */
+#define MCU_PLL_MIN_FREQ 160000000 /* Minimum frequency MCU PLL clock */
+#define NFC_MAX_FREQ 20000000 /* Maximum frequency NFC clock */
+#define PRE_DIV_MIN_FREQ 10000000 /* Minimum Frequency after Predivider */
+
+static struct clk *mcu_pll_clk;
+static struct clk *cpu_clk;
+static struct clk *ahb_clk;
+static struct clk *ipg_clk;
+
+/*!
+ * Spinlock to protect CRM register accesses
+ */
+static DEFINE_SPINLOCK(mxc_crm_lock);
+
+/*!
+ * This function is called to modify the contents of a CCM_MCU register
+ *
+ * @param reg_offset the CCM_MCU register that will read
+ * @param mask the mask to be used to clear the bits that are to be modified
+ * @param data the data that should be written to the register
+ */
+void mxc_ccm_modify_reg(void *reg_offset, unsigned int mask,
+ unsigned int data)
+{
+ unsigned long flags;
+ unsigned long reg;
+
+ spin_lock_irqsave(&mxc_crm_lock, flags);
+ reg = __raw_readl(reg_offset);
+ reg = (reg & (~mask)) | data;
+ __raw_writel(reg, reg_offset);
+ spin_unlock_irqrestore(&mxc_crm_lock, flags);
+}
+
+/*!
+ * Compare two frequences using allowable tolerance
+ *
+ * The MX3 PLL can generate many frequencies. This function
+ * compares the generated frequency to the requested frequency
+ * and determines it they are within and acceptable tolerance.
+ *
+ * @param freq1 desired frequency
+ * @param freq2 generated frequency
+ *
+ * @return Returns 0 is frequencies are within talerance
+ * and non-zero is they are not.
+ */
+static int freq_equal(unsigned long freq1, unsigned long freq2)
+{
+ if (freq1 > freq2) {
+ return (freq1 - freq2) <= (freq1 / FREQ_COMP_TOLERANCE);
+ }
+ return (freq2 - freq1) <= (freq1 / FREQ_COMP_TOLERANCE);
+}
+
+/*!
+ * Calculate new MCU clock dividers for the PDR0 regiser.
+ *
+ * @param mcu_main_clk PLL output frequency (Hz)
+ * @param arm_freq desired ARM frequency (Hz)
+ * @param max_freq desired MAX frequency (Hz)
+ * @param ip_freq desired IP frequency (Hz)
+ * @param mask were to return PDR0 mask
+ * @param value were to return PDR0 value
+ *
+ * @return Returns 0 on success or
+ * Returns non zero if error
+ * PLL_LESS_ARM_ERR if pll frequency is less than
+ * desired core frequency
+ * FREQ_OUT_OF_RANGE if desided frequencies ar not
+ * possible with the current mcu pll frequency.
+ */
+static int
+cal_pdr0_value(unsigned long mcu_main_clk,
+ long arm_freq,
+ long max_freq,
+ long ip_freq, unsigned long *mask, unsigned long *value)
+{
+ unsigned long arm_div; /* ARM core clock divider */
+ unsigned long max_div; /* MAX clock divider */
+ unsigned long ipg_div; /* IPG clock divider */
+ unsigned long nfc_div; /* NFC (Nand Flash Controller) clock divider */
+ unsigned long hsp_div; /* HSP clock divider */
+
+ if (arm_freq > mcu_main_clk) {
+ return -PLL_LESS_ARM_ERR;
+ }
+
+ arm_div = mcu_main_clk / arm_freq;
+ if ((arm_div == 0) || !freq_equal(arm_freq, mcu_main_clk / arm_div)) {
+ return FREQ_OUT_OF_RANGE;
+ }
+ max_div = mcu_main_clk / max_freq;
+ if ((max_div == 0) || !freq_equal(max_freq, mcu_main_clk / max_div)) {
+ return FREQ_OUT_OF_RANGE;
+ }
+ hsp_div = max_div;
+
+ ipg_div = max_freq / ip_freq;
+ if ((ipg_div == 0) || !freq_equal(ip_freq, max_freq / ipg_div)) {
+ return FREQ_OUT_OF_RANGE;
+ }
+
+ nfc_div = ((max_freq - 1000000) / NFC_MAX_FREQ) + 1;
+
+ /* All of the divider values have been calculated.
+ * Now change the hardware register. */
+
+ *mask = MXC_CCM_PDR0_HSP_PODF_MASK |
+ MXC_CCM_PDR0_NFC_PODF_MASK |
+ MXC_CCM_PDR0_IPG_PODF_MASK |
+ MXC_CCM_PDR0_MAX_PODF_MASK | MXC_CCM_PDR0_MCU_PODF_MASK;
+
+ *value = ((hsp_div - 1) << MXC_CCM_PDR0_HSP_PODF_OFFSET) |
+ ((nfc_div - 1) << MXC_CCM_PDR0_NFC_PODF_OFFSET) |
+ ((ipg_div - 1) << MXC_CCM_PDR0_IPG_PODF_OFFSET) |
+ ((max_div - 1) << MXC_CCM_PDR0_MAX_PODF_OFFSET) |
+ ((arm_div - 1) << MXC_CCM_PDR0_MCU_PODF_OFFSET);
+
+ return 0;
+}
+
+/*!
+ * Integer clock scaling
+ *
+ * Change main arm clock frequencies without changing the PLL.
+ * The integer dividers are changed to produce the desired
+ * frequencies. The number of valid frequency are limited and
+ * are determined by the current MCU PLL frequency
+ *
+ * @param arm_freq desired ARM frequency (Hz)
+ * @param max_freq desired MAX frequency (Hz)
+ * @param ip_freq desired IP frequency (Hz)
+ *
+ * @return Returns 0 on success or
+ * Returns non zero if error
+ * PLL_LESS_ARM_ERR if pll frequency is less than
+ * desired core frequency
+ * FREQ_OUT_OF_RANGE if desided frequencies ar not
+ * possible with the current mcu pll frequency.
+ */
+int mxc_pm_intscale(long arm_freq, long max_freq, long ip_freq)
+{
+ unsigned long mcu_main_clk; /* mcu clock domain main clock */
+ unsigned long mask;
+ unsigned long value;
+ int ret_value;
+
+ printk(KERN_INFO "arm_freq=%ld, max_freq=%ld, ip_freq=%ld\n",
+ arm_freq, max_freq, ip_freq);
+ //print_frequencies(); /* debug */
+
+ mcu_main_clk = clk_get_rate(mcu_pll_clk);
+ ret_value = cal_pdr0_value(mcu_main_clk, arm_freq, max_freq, ip_freq,
+ &mask, &value);
+ if ((arm_freq != clk_round_rate(cpu_clk, arm_freq)) ||
+ (max_freq != clk_round_rate(ahb_clk, max_freq)) ||
+ (ip_freq != clk_round_rate(ipg_clk, ip_freq))) {
+ return -EINVAL;
+ }
+
+ if ((max_freq != clk_get_rate(ahb_clk)) ||
+ (ip_freq != clk_get_rate(ipg_clk))) {
+ return -EINVAL;
+ }
+
+ if (arm_freq != clk_get_rate(cpu_clk)) {
+ ret_value = clk_set_rate(cpu_clk, arm_freq);
+ }
+ return ret_value;
+}
+
+/*!
+ * PLL clock scaling
+ *
+ * Change MCU PLL frequency and adjust derived clocks. Integer
+ * dividers are used generate the derived clocks so changed to produce
+ * the desired the valid frequencies are limited by the desired ARM
+ * frequency.
+ *
+ * The clock source for the MCU is set to the MCU PLL.
+ *
+ * @param arm_freq desired ARM frequency (Hz)
+ * @param max_freq desired MAX frequency (Hz)
+ * @param ip_freq desired IP frequency (Hz)
+ *
+ * @return Returns 0 on success or
+ * Returns non zero if error
+ * PLL_LESS_ARM_ERR if pll frequency is less than
+ * desired core frequency
+ * FREQ_OUT_OF_RANGE if desided frequencies ar not
+ * possible with the current mcu pll frequency.
+ */
+int mxc_pm_pllscale(long arm_freq, long max_freq, long ip_freq)
+{
+ signed long pll_freq = 0; /* target pll frequency */
+ unsigned long old_pll;
+ unsigned long mask;
+ unsigned long value;
+ int ret_value;
+
+ printk(KERN_INFO "arm_freq=%ld, max_freq=%ld, ip_freq=%ld\n",
+ arm_freq, max_freq, ip_freq);
+ //print_frequencies();
+
+ do {
+ pll_freq += arm_freq;
+ if ((pll_freq > MCU_PLL_MAX_FREQ) || (pll_freq / 8 > arm_freq)) {
+ return FREQ_OUT_OF_RANGE;
+ }
+ if (pll_freq < MCU_PLL_MIN_FREQ) {
+ ret_value = 111;
+ } else {
+ ret_value =
+ cal_pdr0_value(pll_freq, arm_freq, max_freq,
+ ip_freq, &mask, &value);
+ }
+ } while (ret_value != 0);
+
+ old_pll = clk_get_rate(mcu_pll_clk);
+ if (pll_freq > old_pll) {
+ /* if pll freq is increasing then change dividers first */
+ mxc_ccm_modify_reg(MXC_CCM_PDR0, mask, value);
+ ret_value = clk_set_rate(mcu_pll_clk, pll_freq);
+ } else {
+ /* if pll freq is decreasing then change pll first */
+ ret_value = clk_set_rate(mcu_pll_clk, pll_freq);
+ mxc_ccm_modify_reg(MXC_CCM_PDR0, mask, value);
+ }
+ //print_frequencies();
+ return ret_value;
+}
+
+/*!
+ * Implementing steps required to transition to low-power modes
+ *
+ * @param mode The desired low-power mode. Possible values are,
+ * WAIT_MODE, DOZE_MODE, STOP_MODE or DSM_MODE
+ *
+ */
+void mxc_pm_lowpower(int mode)
+{
+ unsigned int lpm;
+ int enable_flag;
+ unsigned long reg;
+
+ local_irq_disable();
+ enable_flag = 0;
+
+ switch (mode) {
+ case STOP_MODE:
+ /* State Retention mode */
+ lpm = 2;
+ /* Disable timer interrupt */
+ disable_irq(MXC_INT_GPT);
+ enable_flag = 1;
+
+ /* Enable Well Bias and set VSTBY
+ * VSTBY pin will be asserted during SR mode. This asks the
+ * PM IC to set the core voltage to the standby voltage
+ * Must clear the MXC_CCM_CCMR_SBYCS bit as well */
+ mxc_ccm_modify_reg(MXC_CCM_CCMR,
+ MXC_CCM_CCMR_WBEN | MXC_CCM_CCMR_VSTBY |
+ MXC_CCM_CCMR_SBYCS,
+ MXC_CCM_CCMR_WBEN | MXC_CCM_CCMR_VSTBY |
+ MXC_CCM_CCMR_SBYCS);
+
+ mxc_ccm_modify_reg(MXC_CCM_CCMR,
+ MXC_CCM_CCMR_LPM_MASK,
+ lpm << MXC_CCM_CCMR_LPM_OFFSET);
+ cpu_do_idle();
+ break;
+
+ case DSM_MODE:
+ /* Deep Sleep Mode */
+ lpm = 3;
+ /* Disable timer interrupt */
+ disable_irq(MXC_INT_GPT);
+ enable_flag = 1;
+ /* Enabled Well Bias
+ * SBYCS = 0, MCU clock source is disabled*/
+ mxc_ccm_modify_reg(MXC_CCM_CCMR,
+ MXC_CCM_CCMR_WBEN | MXC_CCM_CCMR_VSTBY |
+ MXC_CCM_CCMR_SBYCS | MXC_CCM_CCMR_LPM_MASK,
+ MXC_CCM_CCMR_WBEN | MXC_CCM_CCMR_VSTBY |
+ MXC_CCM_CCMR_SBYCS |
+ (lpm << MXC_CCM_CCMR_LPM_OFFSET));
+
+ /* wake up by keypad */
+ reg = __raw_readl(MXC_CCM_WIMR);
+ reg &= ~(1 << 18);
+ __raw_writel(reg, MXC_CCM_WIMR);
+
+ flush_cache_all();
+ l2x0_disable();
+
+ mxc_pm_arch_entry(IO_ADDRESS(NFC_BASE_ADDR), 2048);
+ printk(KERN_INFO "Resume from DSM\n");
+
+ l2x0_enable();
+ mxc_init_irq();
+
+ break;
+ default:
+ case WAIT_MODE:
+ /* Wait is the default mode used when idle. */
+ reg = __raw_readl(MXC_CCM_CCMR);
+ reg &= ~MXC_CCM_CCMR_LPM_MASK;
+ __raw_writel(reg, MXC_CCM_CCMR);
+ break;
+ }
+
+ if (enable_flag) {
+ /* Enable timer interrupt */
+ enable_irq(MXC_INT_GPT);
+ }
+ local_irq_enable();
+}
+
+#ifdef CONFIG_MXC_DVFS
+/*!
+ * Changes MCU frequencies using dvfs.
+ *
+ * @param armfreq desired ARM frequency in Hz
+ * @param ahbfreq desired AHB frequency in Hz
+ * @param ipfreq desired IP frequency in Hz
+ *
+ * @return Returns 0 on success, non-zero on error
+ */
+int mxc_pm_dvfs(unsigned long armfreq, long ahbfreq, long ipfreq)
+{
+ int ret_value;
+ int i;
+
+ if (ahbfreq != 133000000) {
+ return FREQ_OUT_OF_RANGE;
+ }
+ if (ipfreq != 66500000) {
+ return FREQ_OUT_OF_RANGE;
+ }
+ ret_value = FREQ_OUT_OF_RANGE;
+ for (i = 0; i < dvfs_states_tbl->num_of_states; i++) {
+ if (dvfs_states_tbl->freqs[i] == armfreq) {
+ ret_value = dvfs_set_state(i);
+ break;
+ }
+ }
+
+ return ret_value;
+}
+#endif /* CONFIG_MXC_DVFS */
+
+/*!
+ * This function is used to load the module.
+ *
+ * @return Returns an Integer on success
+ */
+static int __init mxc_pm_init_module(void)
+{
+ printk(KERN_INFO "Low-Level PM Driver module loaded\n");
+
+ mcu_pll_clk = clk_get(NULL, "mcu_pll");
+ cpu_clk = clk_get(NULL, "cpu_clk");
+ ahb_clk = clk_get(NULL, "ahb_clk");
+ ipg_clk = clk_get(NULL, "ipg_clk");
+ return 0;
+}
+
+/*!
+ * This function is used to unload the module
+ */
+static void __exit mxc_pm_cleanup_module(void)
+{
+ clk_put(mcu_pll_clk);
+ clk_put(cpu_clk);
+ clk_put(ahb_clk);
+ clk_put(ipg_clk);
+ printk(KERN_INFO "Low-Level PM Driver module Unloaded\n");
+}
+
+module_init(mxc_pm_init_module);
+module_exit(mxc_pm_cleanup_module);
+
+EXPORT_SYMBOL(mxc_pm_intscale);
+EXPORT_SYMBOL(mxc_pm_pllscale);
+EXPORT_SYMBOL(mxc_pm_lowpower);
+#ifdef CONFIG_MXC_DVFS
+EXPORT_SYMBOL(mxc_pm_dvfs);
+#endif
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MX3 Low-level Power Management Driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-mx3/pm.c b/arch/arm/mach-mx3/pm.c
new file mode 100644
index 000000000000..68b692283070
--- /dev/null
+++ b/arch/arm/mach-mx3/pm.c
@@ -0,0 +1,103 @@
+/*
+ * linux/arch/arm/mach-mx3/pm.c
+ *
+ * MX3 Power Management Routines
+ *
+ * Original code for the SA11x0:
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * Modified for the PXA250 by Nicolas Pitre:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Modified for the OMAP1510 by David Singleton:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Cleanup 2004 for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * Modified for the MX31
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/suspend.h>
+#include <linux/regulator/machine.h>
+#include <mach/mxc_pm.h>
+
+/*
+ * TODO: whatta save?
+ */
+
+static int mx31_suspend_enter(suspend_state_t state)
+{
+ printk(KERN_INFO "Hi, from mx31_pm_enter\n");
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ mxc_pm_lowpower(DSM_MODE);
+ break;
+ case PM_SUSPEND_STANDBY:
+ mxc_pm_lowpower(STOP_MODE);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+static int mx31_suspend_prepare(void)
+{
+ return 0;
+}
+
+/*
+ * Called after devices are re-setup, but before processes are thawed.
+ */
+static void mx31_suspend_finish(void)
+{
+ return;
+}
+
+static int mx31_pm_valid(suspend_state_t state)
+{
+ return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
+}
+
+struct platform_suspend_ops mx31_suspend_ops = {
+ .valid = mx31_pm_valid,
+ .prepare = mx31_suspend_prepare,
+ .enter = mx31_suspend_enter,
+ .finish = mx31_suspend_finish,
+};
+
+static int __init mx31_pm_init(void)
+{
+ printk(KERN_INFO "Power Management for Freescale MX31\n");
+ suspend_set_ops(&mx31_suspend_ops);
+
+ return 0;
+}
+
+late_initcall(mx31_pm_init);
diff --git a/arch/arm/mach-mx3/sdma_script_code.h b/arch/arm/mach-mx3/sdma_script_code.h
new file mode 100644
index 000000000000..627b896f91a1
--- /dev/null
+++ b/arch/arm/mach-mx3/sdma_script_code.h
@@ -0,0 +1,581 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __SDMA_SCRIPT_CODE_H__
+#define __SDMA_SCRIPT_CODE_H__
+
+/*!
+* Following define start address of start script
+*/
+#define start_ADDR 0
+/*!
+* Following define size of start script
+*/
+#define start_SIZE 21
+
+/*!
+* Following define start address of core script
+*/
+#define core_ADDR 80
+/*!
+* Following define size of core script
+*/
+#define core_SIZE 152
+
+/*!
+* Following define start address of common script
+*/
+#define common_ADDR 232
+/*!
+* Following define size of common script
+*/
+#define common_SIZE 191
+
+/*!
+* Following define start address of burst_copy script
+*/
+#define burst_copy_ADDR 423
+/*!
+* Following define size of burst_copy script
+*/
+#define burst_copy_SIZE 87
+
+/*!
+* Following define start address of dsp_2_burst script
+*/
+#define dsp_2_burst_ADDR 510
+/*!
+* Following define size of dsp_2_burst script
+*/
+#define dsp_2_burst_SIZE 24
+
+/*!
+* Following define start address of burst_2_dsp script
+*/
+#define burst_2_dsp_ADDR 534
+/*!
+* Following define size of burst_2_dsp script
+*/
+#define burst_2_dsp_SIZE 24
+
+/*!
+* Following define start address of dsp_copy script
+*/
+#define dsp_copy_ADDR 558
+/*!
+* Following define size of dsp_copy script
+*/
+#define dsp_copy_SIZE 86
+
+/*!
+* Following define start address of mcu_2_mcu script
+*/
+#define mcu_2_mcu_ADDR 644
+/*!
+* Following define size of mcu_2_mcu script
+*/
+#define mcu_2_mcu_SIZE 79
+
+/*!
+* Following define start address of mcu_2_per script
+*/
+#define mcu_2_per_ADDR 723
+/*!
+* Following define size of mcu_2_per script
+*/
+#define mcu_2_per_SIZE 88
+
+/*!
+* Following define start address of test script
+*/
+#define test_ADDR 811
+/*!
+* Following define size of test script
+*/
+#define test_SIZE 63
+
+/*!
+* Following define start address of mcu_2_dsp script
+*/
+#define mcu_2_dsp_ADDR 874
+/*!
+* Following define size of mcu_2_dsp script
+*/
+#define mcu_2_dsp_SIZE 30
+
+/*!
+* Following define start address of mcu_2_dsp_2buf script
+*/
+#define mcu_2_dsp_2buf_ADDR 904
+/*!
+* Following define size of mcu_2_dsp_2buf script
+*/
+#define mcu_2_dsp_2buf_SIZE 113
+
+/*!
+* Following define start address of dsp_2_mcu script
+*/
+#define dsp_2_mcu_ADDR 1017
+/*!
+* Following define size of dsp_2_mcu script
+*/
+#define dsp_2_mcu_SIZE 30
+
+/*!
+* Following define start address of dsp_2_mcu_2buf script
+*/
+#define dsp_2_mcu_2buf_ADDR 1047
+/*!
+* Following define size of dsp_2_mcu_2buf script
+*/
+#define dsp_2_mcu_2buf_SIZE 113
+
+/*!
+* Following define start address of dsp_2_dsp script
+*/
+#define dsp_2_dsp_ADDR 1160
+/*!
+* Following define size of dsp_2_dsp script
+*/
+#define dsp_2_dsp_SIZE 64
+
+/*!
+* Following define start address of per_2_mcu script
+*/
+#define per_2_mcu_ADDR 1224
+/*!
+* Following define size of per_2_mcu script
+*/
+#define per_2_mcu_SIZE 121
+
+/*!
+* Following define start address of dsp_2_per_2buf script
+*/
+#define dsp_2_per_2buf_ADDR 1345
+/*!
+* Following define size of dsp_2_per_2buf script
+*/
+#define dsp_2_per_2buf_SIZE 164
+
+/*!
+* Following define start address of per_2_dsp_2buf script
+*/
+#define per_2_dsp_2buf_ADDR 1509
+/*!
+* Following define size of per_2_dsp_2buf script
+*/
+#define per_2_dsp_2buf_SIZE 168
+
+/*!
+* Following define start address of per_2_per script
+*/
+#define per_2_per_ADDR 1677
+/*!
+* Following define size of per_2_per script
+*/
+#define per_2_per_SIZE 67
+
+/*!
+* Following define start address of error_dsp script
+*/
+#define error_dsp_ADDR 1744
+/*!
+* Following define size of error_dsp script
+*/
+#define error_dsp_SIZE 34
+
+/*!
+* Following define start address of ap_2_ap script
+*/
+#define ap_2_ap_ADDR 6144
+/*!
+* Following define size of ap_2_ap script
+*/
+#define ap_2_ap_SIZE 294
+
+/*!
+* Following define start address of app_2_mcu script
+*/
+#define app_2_mcu_ADDR 6438
+/*!
+* Following define size of app_2_mcu script
+*/
+#define app_2_mcu_SIZE 101
+
+/*!
+* Following define start address of ata_2_mcu script
+*/
+#define ata_2_mcu_ADDR 6539
+/*!
+* Following define size of ata_2_mcu script
+*/
+#define ata_2_mcu_SIZE 110
+
+/*!
+* Following define start address of dptc_dvfs script
+*/
+#define dptc_dvfs_ADDR 6649
+/*!
+* Following define size of dptc_dvfs script
+*/
+#define dptc_dvfs_SIZE 274
+
+/*!
+* Following define start address of error script
+*/
+#define error_ADDR 6923
+/*!
+* Following define size of error script
+*/
+#define error_SIZE 73
+
+/*!
+* Following define start address of firi_2_mcu script
+*/
+#define firi_2_mcu_ADDR 6996
+/*!
+* Following define size of firi_2_mcu script
+*/
+#define firi_2_mcu_SIZE 114
+
+/*!
+* Following define start address of mcu_2_app script
+*/
+#define mcu_2_app_ADDR 7110
+/*!
+* Following define size of mcu_2_app script
+*/
+#define mcu_2_app_SIZE 127
+
+/*!
+* Following define start address of mcu_2_ata script
+*/
+#define mcu_2_ata_ADDR 7237
+/*!
+* Following define size of mcu_2_ata script
+*/
+#define mcu_2_ata_SIZE 87
+
+/*!
+* Following define start address of mcu_2_firi script
+*/
+#define mcu_2_firi_ADDR 7324
+/*!
+* Following define size of mcu_2_firi script
+*/
+#define mcu_2_firi_SIZE 77
+
+/*!
+* Following define start address of mcu_2_mshc script
+*/
+#define mcu_2_mshc_ADDR 7401
+/*!
+* Following define size of mcu_2_mshc script
+*/
+#define mcu_2_mshc_SIZE 48
+
+/*!
+* Following define start address of mcu_2_shp script
+*/
+#define mcu_2_shp_ADDR 7449
+/*!
+* Following define size of mcu_2_shp script
+*/
+#define mcu_2_shp_SIZE 123
+
+/*!
+* Following define start address of mshc_2_mcu script
+*/
+#define mshc_2_mcu_ADDR 7572
+/*!
+* Following define size of mshc_2_mcu script
+*/
+#define mshc_2_mcu_SIZE 60
+
+/*!
+* Following define start address of shp_2_mcu script
+*/
+#define shp_2_mcu_ADDR 7632
+/*!
+* Following define size of shp_2_mcu script
+*/
+#define shp_2_mcu_SIZE 101
+
+/*!
+* Following define start address of uart_2_mcu script
+*/
+#define uart_2_mcu_ADDR 7733
+/*!
+* Following define size of uart_2_mcu script
+*/
+#define uart_2_mcu_SIZE 105
+
+/*!
+* Following define start address of uartsh_2_mcu script
+*/
+#define uartsh_2_mcu_ADDR 7838
+/*!
+* Following define size of uartsh_2_mcu script
+*/
+#define uartsh_2_mcu_SIZE 98
+
+/*!
+* Following define the start address of sdma ram
+*/
+
+#define RAM_CODE_START_ADDR 6144
+/*!
+* Following define the size of sdma ram
+*/
+#define RAM_CODE_SIZE 1792
+
+/*!
+* This function returns buffer that holds the image of SDMA RAM.
+* This is required to start on a 4-byte aligned boundary on some platforms
+* for SDMA to work properly.
+*
+* @return pointer to buffer that holds the image of SDMA RAM
+*/
+
+__attribute__ ((__aligned__(4)))
+#ifndef CONFIG_XIP_KERNEL
+const
+#endif
+static short sdma_code[] = {
+ 0xc0ec, 0x7d59, 0x0970, 0x0111, 0x5111, 0x5ad1, 0x5bd9, 0xc0fe,
+ 0x5ce1, 0x7d02, 0x0200, 0x9806, 0x08ff, 0x0011, 0x28ff, 0x00bc,
+ 0x05df, 0x7d4b, 0x06df, 0x7d2f, 0x6dc5, 0x6ed5, 0x5ef1, 0x0288,
+ 0xd81a, 0x9854, 0x0b04, 0x00d3, 0x7d20, 0x06a5, 0x3e03, 0x3d03,
+ 0x03a5, 0x3b03, 0x008b, 0x058b, 0x7802, 0x63d8, 0x0000, 0x7e72,
+ 0x63ff, 0x7e70, 0x02a5, 0x008a, 0x4e00, 0x7d01, 0x983d, 0x6dcf,
+ 0x6edf, 0x0015, 0x0015, 0x7802, 0x63d8, 0x0000, 0x7e63, 0x63ff,
+ 0x7e61, 0x3a03, 0x008a, 0x6dcd, 0x6edd, 0x7801, 0x63d8, 0x7e5a,
+ 0x63ff, 0x7e58, 0x0006, 0x6dc5, 0x6e07, 0x5ef1, 0x0288, 0xd8f7,
+ 0x7e02, 0x7f04, 0x9854, 0x0007, 0x68cc, 0x6b28, 0x54e1, 0x0089,
+ 0xdb13, 0x0188, 0x5ce1, 0x9854, 0x52d1, 0x53d9, 0x54e1, 0xc10d,
+ 0x7dad, 0x0200, 0x9800, 0x0200, 0x9800, 0x06df, 0x7d06, 0x6d23,
+ 0x6ed5, 0x5ef1, 0x0288, 0xd8cd, 0x9854, 0x5ef1, 0x6e07, 0x6d03,
+ 0x0b04, 0x00d3, 0x7d59, 0x06a5, 0x3e03, 0x3d03, 0x4d00, 0x7d09,
+ 0x03a5, 0x00a3, 0x0588, 0x008b, 0xd8c9, 0x7ed8, 0x620c, 0x7ed6,
+ 0x008d, 0x4e00, 0x7c25, 0x0a20, 0x00da, 0x7c22, 0x6503, 0x3d1f,
+ 0x02a5, 0x00a2, 0x0215, 0x0215, 0x6a18, 0x6a28, 0x7fc7, 0x0a20,
+ 0x0b08, 0x00da, 0x7c06, 0x6b18, 0x6b28, 0x7fc0, 0x0000, 0x2020,
+ 0x9889, 0x0688, 0x0015, 0x0015, 0x6818, 0x6828, 0x7fb7, 0x98c2,
+ 0x0007, 0x6a0c, 0x54e1, 0x0089, 0xdb0f, 0x0188, 0x5ce1, 0x9854,
+ 0x0b04, 0x00d3, 0x7d21, 0x0389, 0x1b12, 0x048b, 0x0688, 0x0015,
+ 0x0015, 0x0588, 0x038c, 0x0a08, 0x05da, 0x008d, 0x7c01, 0x008a,
+ 0x05a0, 0x7803, 0x620b, 0x5a03, 0x1b01, 0x7e98, 0x008b, 0x00a4,
+ 0x038c, 0x7803, 0x5203, 0x6a0b, 0x1b01, 0x6a28, 0x7f8f, 0x0000,
+ 0x4d00, 0x7ce8, 0x008e, 0x3803, 0xd8c9, 0x7e88, 0x620c, 0x7e86,
+ 0x9854, 0x7802, 0x6209, 0x6a29, 0x0006, 0x3e03, 0x4e00, 0x7d11,
+ 0x0b04, 0x03a6, 0x02db, 0x7d01, 0x038a, 0x02a3, 0x048a, 0x008b,
+ 0x7802, 0x6329, 0x6bc8, 0x7ebc, 0x63c8, 0x7ebc, 0x008c, 0x4800,
+ 0x7d15, 0x0488, 0x0015, 0x0015, 0x6edf, 0x7803, 0x632b, 0x6bc8,
+ 0x0000, 0x7eae, 0x63c8, 0x7eae, 0x008c, 0x3803, 0x6edd, 0x7803,
+ 0x6329, 0x6bc8, 0x0000, 0x7ea4, 0x63c8, 0x7ea4, 0x0006, 0x3d03,
+ 0x4d00, 0x7d0e, 0x0b04, 0x03a5, 0x02db, 0x7d01, 0x038a, 0x02a3,
+ 0x048a, 0x008b, 0x7802, 0x63c8, 0x6b09, 0x7e1e, 0x7f1e, 0x008c,
+ 0x0488, 0x0015, 0x0015, 0x6dcf, 0x0288, 0x008a, 0x0d08, 0x02dd,
+ 0x7c01, 0x008d, 0x7802, 0x63c8, 0x6b0b, 0x7e0e, 0x6b28, 0x7f0d,
+ 0x0000, 0x02dd, 0x7c02, 0x2208, 0x990d, 0x008c, 0x3803, 0x65c0,
+ 0x6dc5, 0x7802, 0x63c8, 0x6b09, 0x6b28, 0x0006, 0x0870, 0x0011,
+ 0x5010, 0xc0ec, 0x7d5e, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8,
+ 0x7d02, 0x0200, 0x992c, 0x6ec3, 0x6d07, 0x5df0, 0x0dff, 0x0511,
+ 0x1dff, 0x05bc, 0x4d00, 0x7d44, 0x0b70, 0x0311, 0x522b, 0x5313,
+ 0x02b9, 0x4a00, 0x7c04, 0x6a28, 0x7f3a, 0x0400, 0x993c, 0x008f,
+ 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x0a03, 0x0212, 0x02bc, 0x0210,
+ 0x4a00, 0x7d1c, 0x4a02, 0x7d20, 0x4a01, 0x7d23, 0x0b70, 0x0311,
+ 0x53eb, 0x62c8, 0x7e24, 0x0360, 0x7d02, 0x0210, 0x0212, 0x6a09,
+ 0x7f1e, 0x0212, 0x6a09, 0x7f1b, 0x0212, 0x6a09, 0x7f18, 0x2003,
+ 0x4800, 0x7cef, 0x0b70, 0x0311, 0x5313, 0x997d, 0x0015, 0x0015,
+ 0x7802, 0x62c8, 0x6a0b, 0x997c, 0x0015, 0x7802, 0x62c8, 0x6a0a,
+ 0x997c, 0x7802, 0x62c8, 0x6a09, 0x7c02, 0x0000, 0x993a, 0xdb13,
+ 0x6a28, 0x7ffd, 0x008b, 0x52c3, 0x53cb, 0xc10d, 0x7da5, 0x0200,
+ 0x992c, 0x0200, 0x9929, 0xc19d, 0xc0ec, 0x7d69, 0x0c70, 0x0411,
+ 0x5414, 0x5ac4, 0x028c, 0x58da, 0x5efa, 0xc0fe, 0x56fa, 0x7d02,
+ 0x0200, 0x9994, 0x6d07, 0x5bca, 0x5cd2, 0x0bff, 0x0311, 0x1bff,
+ 0x04bb, 0x0415, 0x53da, 0x4c00, 0x7d47, 0x0a70, 0x0211, 0x552a,
+ 0x5212, 0x008d, 0x00bb, 0x4800, 0x7c07, 0x05b9, 0x4d00, 0x7c13,
+ 0x6928, 0x7f2d, 0x0400, 0x99a5, 0x008f, 0x0015, 0x04d8, 0x7d01,
+ 0x008c, 0x04a0, 0x0015, 0x7802, 0x55c6, 0x6d0b, 0x7e29, 0x6d28,
+ 0x7f1e, 0x0000, 0x99a3, 0x1e20, 0x5506, 0x2620, 0x008d, 0x0560,
+ 0x7c08, 0x065f, 0x55c6, 0x063f, 0x7e1b, 0x6d0a, 0x7f10, 0x4c00,
+ 0x7d1b, 0x04d8, 0x7d02, 0x008c, 0x0020, 0x04a0, 0x0015, 0x7802,
+ 0x55c6, 0x6d0b, 0x7e0d, 0x6d28, 0x7f02, 0x0000, 0x99ec, 0x0007,
+ 0x680c, 0x6d0c, 0x6507, 0x6d07, 0x6d2b, 0x6d28, 0x0007, 0x680c,
+ 0x0007, 0x54d2, 0x0454, 0x99ef, 0x6928, 0x7ff1, 0x54d2, 0x008a,
+ 0x52c0, 0x53c8, 0xc10d, 0x0288, 0x7d9f, 0x0200, 0x9994, 0x0200,
+ 0x998c, 0xc0ec, 0x7d72, 0x0800, 0x0970, 0x0111, 0x5111, 0x5ac1,
+ 0x5bc9, 0x028e, 0xc0fe, 0x068a, 0x7c6a, 0x5dd9, 0x5ce1, 0x0bff,
+ 0x0311, 0x1bff, 0x03bc, 0x5bd1, 0x1a5c, 0x6ac3, 0x63c8, 0x0363,
+ 0x7c05, 0x036f, 0x7d27, 0x0374, 0x7c7a, 0x9a71, 0xdb04, 0x3c06,
+ 0x4c00, 0x7df7, 0x028f, 0x1a04, 0x6a23, 0x620b, 0x6f23, 0x301f,
+ 0x00aa, 0x0462, 0x7c04, 0x4a00, 0x7d0b, 0x2001, 0x9a30, 0x048a,
+ 0x620b, 0x2201, 0x1c01, 0x1801, 0x02dc, 0x7d02, 0x301f, 0x00aa,
+ 0x048f, 0x1c04, 0x6c07, 0x0488, 0x3c1f, 0x6c2b, 0x0045, 0x028e,
+ 0x1a5c, 0x9a11, 0x058f, 0x1d0c, 0x6d23, 0x650b, 0x007d, 0x7c01,
+ 0x1d08, 0x007c, 0x7c01, 0x1d04, 0x6d23, 0x650b, 0x0488, 0x3c1f,
+ 0x0417, 0x0417, 0x0417, 0x0417, 0x059c, 0x6d23, 0x028e, 0x1a34,
+ 0x6ad7, 0x0488, 0x0804, 0x7802, 0x650b, 0x6dc8, 0x008c, 0x1a28,
+ 0x6ad7, 0x63c8, 0x034c, 0x6bc8, 0x54d1, 0x4c00, 0x7d06, 0x0065,
+ 0x7c02, 0x0101, 0x0025, 0x0400, 0x9a0d, 0x52c1, 0x53c9, 0x54e1,
+ 0x0453, 0xc10d, 0x7d95, 0x0200, 0x9a00, 0x0200, 0x99f9, 0x0200,
+ 0x9a00, 0x55d9, 0x6d07, 0x54d1, 0x058a, 0x2508, 0x6dc7, 0x0373,
+ 0x7c03, 0x65c8, 0x6d0b, 0x2408, 0x0372, 0x7c04, 0x65c8, 0x6d0b,
+ 0x2408, 0x9a86, 0x6cce, 0x65c8, 0x6d0a, 0x2404, 0x6d28, 0x6507,
+ 0x5dd9, 0x5cd1, 0x6ad7, 0x6ae3, 0x63c8, 0x0334, 0x6bc8, 0x0370,
+ 0x7ca9, 0x0c60, 0x0411, 0x04bb, 0x4c00, 0x7da4, 0x0410, 0x1c30,
+ 0x0410, 0x04bb, 0x046d, 0x7d0a, 0x047d, 0x7c03, 0x047c, 0x7c01,
+ 0x9a3a, 0x003b, 0x003a, 0x0039, 0x0058, 0x9ab5, 0x047d, 0x7d03,
+ 0x047c, 0x7d01, 0x9a3a, 0x005b, 0xdaf9, 0x1d18, 0x6d23, 0x650b,
+ 0x0510, 0x003a, 0x0039, 0x0038, 0x00ad, 0xdb04, 0x0c30, 0x0410,
+ 0x04bb, 0x003c, 0x003d, 0x00ac, 0xdaf9, 0x007b, 0x7c04, 0x003d,
+ 0x003c, 0x1d0c, 0x9ad6, 0x048f, 0x1c14, 0x6c23, 0x640b, 0x4401,
+ 0x7d04, 0x005d, 0x005c, 0x1d0c, 0x9ad6, 0x0310, 0x3b30, 0x4b30,
+ 0x7d01, 0x1b10, 0x0310, 0x003d, 0x003c, 0x00ab, 0x6ad7, 0x63c8,
+ 0x6d23, 0x650b, 0x0560, 0x7d03, 0x005e, 0xdaed, 0x9a3a, 0x003e,
+ 0x0c80, 0x0410, 0x0394, 0xdaed, 0x640b, 0x037f, 0x7d02, 0x1a14,
+ 0x9aea, 0x1a0c, 0x6ad7, 0x6cc8, 0x9a3a, 0x0c7f, 0x0410, 0x03b4,
+ 0x04b8, 0x03ac, 0x640b, 0x6bc8, 0x028e, 0x1a04, 0x6ad7, 0x6cc8,
+ 0x0006, 0x058f, 0x1d08, 0x6d23, 0x650b, 0x007d, 0x7c01, 0x1d38,
+ 0x007c, 0x7c01, 0x1d1c, 0x0006, 0x048b, 0x042c, 0x0454, 0x042b,
+ 0x6ad7, 0x6cc8, 0x0006, 0x0007, 0x684c, 0x6144, 0x9b1c, 0x0007,
+ 0x68cc, 0x61d0, 0x9b1c, 0x0007, 0x680c, 0x680c, 0x6107, 0x6907,
+ 0x692b, 0x6928, 0x0007, 0x680c, 0x0d70, 0x0511, 0x5515, 0x55f5,
+ 0x01a5, 0x0dff, 0x0512, 0x1dff, 0x0512, 0x04bd, 0x0499, 0x0454,
+ 0x0006, 0x08ff, 0x0011, 0x28ff, 0x0006, 0x038c, 0x0eff, 0x0611,
+ 0x2eff, 0x03b6, 0x0006, 0x53d6, 0x0398, 0x5bd6, 0x53ee, 0x0398,
+ 0x5bee, 0x0006, 0x52de, 0x53e6, 0x54ee, 0x0498, 0x0454, 0x0006,
+ 0x50f6, 0x52c6, 0x53ce, 0x54d6, 0x0498, 0x0454, 0x0006, 0x6207,
+ 0x0b70, 0x0311, 0x5013, 0x55f0, 0x02a5, 0x0bff, 0x0312, 0x1bff,
+ 0x0312, 0x04bb, 0x049a, 0x0006, 0x1e10, 0x0870, 0x0011, 0x5010,
+ 0xc0ec, 0x7d39, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02,
+ 0x0200, 0x9b5b, 0x6d07, 0x5df0, 0x0dff, 0x0511, 0x1dff, 0x05bc,
+ 0x4d00, 0x7d17, 0x6ec3, 0x62c8, 0x7e28, 0x0264, 0x7d08, 0x0b70,
+ 0x0311, 0x522b, 0x02b9, 0x4a00, 0x7c18, 0x0400, 0x9b6a, 0x0212,
+ 0x3aff, 0x008a, 0x05d8, 0x7d01, 0x008d, 0x0a10, 0x6ed3, 0x6ac8,
+ 0xdba5, 0x6a28, 0x7f17, 0x0b70, 0x0311, 0x5013, 0xdbbd, 0x52c0,
+ 0x53c8, 0xc10d, 0x7dd0, 0x0200, 0x9b5b, 0x008f, 0x00d5, 0x7d01,
+ 0x008d, 0xdba5, 0x9b68, 0x0200, 0x9b58, 0x0007, 0x68cc, 0x6a28,
+ 0x7f01, 0x9ba3, 0x0007, 0x6a0c, 0x6a0c, 0x6207, 0x6a07, 0x6a2b,
+ 0x6a28, 0x0007, 0x680c, 0x0454, 0x9b81, 0x05a0, 0x1e08, 0x6ec3,
+ 0x0388, 0x3b03, 0x0015, 0x0015, 0x7802, 0x62c8, 0x6a0b, 0x7ee5,
+ 0x6a28, 0x7fe8, 0x0000, 0x6ec1, 0x008b, 0x7802, 0x62c8, 0x6a09,
+ 0x7edc, 0x6a28, 0x7fdf, 0x2608, 0x0006, 0x55f0, 0x6207, 0x02a5,
+ 0x0dff, 0x0511, 0x1dff, 0x04b5, 0x049a, 0x0006, 0x0870, 0x0011,
+ 0x5010, 0xc0ec, 0x7d78, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8,
+ 0x7d02, 0x0200, 0x9bcc, 0x6d03, 0x6ed3, 0x0dff, 0x0511, 0x1dff,
+ 0x05bc, 0x5df8, 0x4d00, 0x7d5e, 0x0b70, 0x0311, 0x522b, 0x5313,
+ 0x02b9, 0x4a00, 0x7c04, 0x62ff, 0x7e3f, 0x0400, 0x9bdc, 0x008f,
+ 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x5ddb, 0x0d03, 0x0512, 0x05bc,
+ 0x0510, 0x5dd3, 0x4d00, 0x7d27, 0x4d02, 0x7d20, 0x4d01, 0x7d1a,
+ 0x0b70, 0x0311, 0x53eb, 0x0360, 0x7d05, 0x6509, 0x7e25, 0x620a,
+ 0x7e23, 0x9c06, 0x620a, 0x7e20, 0x6509, 0x7e1e, 0x0512, 0x0512,
+ 0x02ad, 0x6ac8, 0x7f19, 0x2003, 0x4800, 0x7ced, 0x0b70, 0x0311,
+ 0x5313, 0x9c21, 0x7802, 0x6209, 0x6ac8, 0x9c20, 0x0015, 0x7802,
+ 0x620a, 0x6ac8, 0x9c20, 0x0015, 0x0015, 0x7802, 0x620b, 0x6ac8,
+ 0x7c03, 0x0000, 0x55db, 0x9bda, 0x0007, 0x68cc, 0x680c, 0x55d3,
+ 0x4d00, 0x7d03, 0x4d02, 0x7d02, 0x9c2f, 0x0017, 0x0017, 0x55db,
+ 0x009d, 0x55fb, 0x05a0, 0x08ff, 0x0011, 0x18ff, 0x0010, 0x04b8,
+ 0x04ad, 0x0454, 0x62ff, 0x7ee8, 0x008b, 0x52c0, 0x53c8, 0xc10d,
+ 0x7d8b, 0x0200, 0x9bcc, 0x0200, 0x9bc9, 0xc19d, 0xc0ec, 0x7d52,
+ 0x0c70, 0x0411, 0x5414, 0x5ac4, 0x028c, 0x58da, 0x5efa, 0xc0fe,
+ 0x56fa, 0x7d02, 0x0200, 0x9c4e, 0x6d03, 0x5bca, 0x5cd2, 0x0bff,
+ 0x0311, 0x1bff, 0x04bb, 0x0415, 0x53da, 0x0a70, 0x0211, 0x4c00,
+ 0x7d28, 0x552a, 0x05bb, 0x4d00, 0x7c02, 0x0400, 0x9c61, 0x4c01,
+ 0x7d0f, 0x008f, 0x0015, 0x04d8, 0x7d01, 0x008c, 0x0020, 0x04a0,
+ 0x0015, 0x7802, 0x650b, 0x5d06, 0x0000, 0x7e0c, 0x7f0d, 0x9c5f,
+ 0x650a, 0x7e08, 0x008d, 0x0011, 0x0010, 0x05a8, 0x065f, 0x5d06,
+ 0x063f, 0x7f02, 0x0007, 0x680c, 0x0007, 0x5012, 0x54d0, 0x0454,
+ 0x9c8b, 0x5012, 0x54d0, 0x0473, 0x7c06, 0x552a, 0x05b9, 0x4d00,
+ 0x7c02, 0x0400, 0x9c8d, 0x52c0, 0x53c8, 0xc10d, 0x0288, 0x7db6,
+ 0x0200, 0x9c4e, 0x0200, 0x9c46, 0x0870, 0x0011, 0x5010, 0xc0ec,
+ 0x7d46, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200,
+ 0x9ca2, 0x0b70, 0x0311, 0x6ed3, 0x6d03, 0x0dff, 0x0511, 0x1dff,
+ 0x05bc, 0x4d00, 0x7d2b, 0x522b, 0x02b9, 0x4a00, 0x7c04, 0x62c8,
+ 0x7e1f, 0x0400, 0x9cb3, 0x008f, 0x00d5, 0x7d01, 0x008d, 0x05a0,
+ 0x0060, 0x7c05, 0x6edd, 0x6209, 0x7e16, 0x6ac8, 0x7f11, 0x0015,
+ 0x0060, 0x7c05, 0x6ede, 0x620a, 0x7e0e, 0x6ac8, 0x7f09, 0x6edf,
+ 0x0015, 0x7802, 0x620b, 0x6ac8, 0x0000, 0x7e05, 0x7f01, 0x9cb1,
+ 0x0007, 0x68cc, 0x9cdd, 0x0007, 0x6a0c, 0x0454, 0x62c8, 0x7ef8,
+ 0x5013, 0x52c0, 0x53c8, 0xc10d, 0x7dbd, 0x0200, 0x9ca2, 0x0200,
+ 0x9c9f, 0xc19d, 0x0870, 0x0011, 0xc0ec, 0x7d29, 0x5010, 0x5ac0,
+ 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200, 0x9cf0, 0x0870, 0x0011,
+ 0x6d03, 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x4d00, 0x7d12, 0x5228,
+ 0x02b9, 0x4a00, 0x7c02, 0x0400, 0x9cff, 0x620b, 0x7e06, 0x5a06,
+ 0x7f06, 0x0000, 0x2504, 0x7d05, 0x9cff, 0x0007, 0x680c, 0x0007,
+ 0x0454, 0x5010, 0x52c0, 0xc10d, 0x7ddb, 0x0200, 0x9cf0, 0x0200,
+ 0x9cec, 0xc19d, 0x0870, 0x0011, 0xc0ec, 0x7d74, 0x5010, 0x5ac0,
+ 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200, 0x9d20, 0x6d03,
+ 0x0d03, 0x0512, 0x05bc, 0x0510, 0x5dd0, 0x0dff, 0x0511, 0x1dff,
+ 0x05bc, 0x5df8, 0x4d00, 0x7d57, 0x0a70, 0x0211, 0x532a, 0x5212,
+ 0x03b9, 0x4b00, 0x7c02, 0x0400, 0x9d34, 0x008f, 0x05d8, 0x7d01,
+ 0x008d, 0x05a0, 0x5dda, 0x55d2, 0x4d00, 0x7d27, 0x4d02, 0x7d20,
+ 0x4d01, 0x7d1a, 0x0a70, 0x0211, 0x52ea, 0x0260, 0x7d05, 0x6509,
+ 0x7e25, 0x630a, 0x7e23, 0x9d58, 0x630a, 0x7e20, 0x6509, 0x7e1e,
+ 0x0512, 0x0512, 0x03ad, 0x5b06, 0x7f19, 0x2003, 0x4800, 0x7ced,
+ 0x0a70, 0x0211, 0x5212, 0x9d73, 0x7802, 0x6309, 0x5b06, 0x9d72,
+ 0x0015, 0x7802, 0x630a, 0x5b06, 0x9d72, 0x0015, 0x0015, 0x7802,
+ 0x630b, 0x5b06, 0x7c03, 0x55da, 0x0000, 0x9d32, 0x0007, 0x680c,
+ 0x55d2, 0x4d00, 0x7d03, 0x4d02, 0x7d02, 0x9d80, 0x0017, 0x0017,
+ 0x55da, 0x009d, 0x55fa, 0x05a0, 0x08ff, 0x0011, 0x18ff, 0x0010,
+ 0x04b8, 0x04ad, 0x0454, 0x008a, 0x52c0, 0x53c8, 0xc10d, 0x7d90,
+ 0x0200, 0x9d20, 0x0200, 0x9d1c, 0xc19d, 0x0870, 0x0011, 0xc0ec,
+ 0x7d35, 0x5010, 0x5ac0, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200,
+ 0x9d9b, 0x0870, 0x0011, 0x6d07, 0x0dff, 0x0511, 0x1dff, 0x05bc,
+ 0x4d00, 0x7d1c, 0x5228, 0x02b9, 0x4a00, 0x7c04, 0x6928, 0x7f0b,
+ 0x0400, 0x9daa, 0x5206, 0x7e10, 0x6a0b, 0x6928, 0x7f04, 0x0000,
+ 0x2504, 0x7d0c, 0x9daa, 0x0007, 0x680c, 0x680c, 0x6207, 0x6a07,
+ 0x6a2b, 0x6a28, 0x0007, 0x680c, 0x0007, 0x0454, 0x6928, 0x7ff3,
+ 0x5010, 0x52c0, 0xc10d, 0x7dcf, 0x0200, 0x9d9b, 0x0200, 0x9d97,
+ 0xc19d, 0x0870, 0x0011, 0xc0ec, 0x7d5e, 0x5010, 0x5ac0, 0x5bc8,
+ 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200, 0x9dd7, 0x6d07, 0x5df0,
+ 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x4d00, 0x7d44, 0x0a70, 0x0211,
+ 0x532a, 0x5212, 0x03b9, 0x4b00, 0x7c04, 0x6a28, 0x7f3a, 0x0400,
+ 0x9de6, 0x008f, 0x05d8, 0x7d01, 0x008d, 0x05a0, 0x0b03, 0x0312,
+ 0x03bc, 0x0310, 0x4b00, 0x7d1c, 0x4b02, 0x7d20, 0x4b01, 0x7d23,
+ 0x0a70, 0x0211, 0x52ea, 0x5306, 0x7e24, 0x0260, 0x7d02, 0x0310,
+ 0x0312, 0x6b09, 0x7f1e, 0x0312, 0x6b09, 0x7f1b, 0x0312, 0x6b09,
+ 0x7f18, 0x2003, 0x4800, 0x7cef, 0x0a70, 0x0211, 0x5212, 0x9e27,
+ 0x0015, 0x0015, 0x7802, 0x5306, 0x6b0b, 0x9e26, 0x0015, 0x7802,
+ 0x5306, 0x6b0a, 0x9e26, 0x7802, 0x5306, 0x6b09, 0x7c02, 0x0000,
+ 0x9de4, 0xdb13, 0x6928, 0x7ffd, 0x008a, 0x52c0, 0x53c8, 0xc10d,
+ 0x7da6, 0x0200, 0x9dd7, 0x0200, 0x9dd3, 0x0870, 0x0011, 0x5010,
+ 0xc0ec, 0x7d5b, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02,
+ 0x0200, 0x9e3b, 0x0b70, 0x0311, 0x6ec3, 0x6d07, 0x5df0, 0x0dff,
+ 0x0511, 0x1dff, 0x05bc, 0x4d00, 0x7d3d, 0x522b, 0x02b9, 0x4a00,
+ 0x7c04, 0x6a28, 0x7f33, 0x0400, 0x9e4d, 0x028e, 0x1a94, 0x6ac3,
+ 0x62c8, 0x0269, 0x7d1b, 0x1e94, 0x6ec3, 0x6ed3, 0x62c8, 0x0248,
+ 0x6ac8, 0x2694, 0x6ec3, 0x62c8, 0x026e, 0x7d31, 0x6a09, 0x7f1e,
+ 0x2501, 0x4d00, 0x7d1f, 0x028e, 0x1a98, 0x6ac3, 0x62c8, 0x6ec3,
+ 0x0260, 0x7df1, 0x6a28, 0x7f12, 0xdb47, 0x9e8c, 0x6ee3, 0x008f,
+ 0x2001, 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x62c8, 0x026e, 0x7d17,
+ 0x6a09, 0x7f04, 0x2001, 0x7cf9, 0x0000, 0x9e4b, 0x0289, 0xdb13,
+ 0x018a, 0x9e9b, 0x6a28, 0x7ffa, 0x0b70, 0x0311, 0x5013, 0x52c0,
+ 0x53c8, 0xc10d, 0x7da8, 0x0200, 0x9e3b, 0x0200, 0x9e38, 0x6a28,
+ 0x7fed, 0xdb47, 0x9e9b, 0x0458, 0x0454, 0x9e8c, 0xc19d, 0x0870,
+ 0x0011, 0xc0ec, 0x7d54, 0x5010, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe,
+ 0x56f8, 0x7d02, 0x0200, 0x9ea5, 0x0b70, 0x0311, 0x6d07, 0x5df0,
+ 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x4d00, 0x7d36, 0x522b, 0x02b9,
+ 0x4a00, 0x7c04, 0x6928, 0x7f2c, 0x0400, 0x9eb6, 0x028e, 0x1a94,
+ 0x5202, 0x0269, 0x7d16, 0x1e94, 0x5206, 0x0248, 0x5a06, 0x2694,
+ 0x5206, 0x026e, 0x7d2e, 0x6a09, 0x7f1b, 0x2501, 0x4d00, 0x7d1c,
+ 0x028e, 0x1a98, 0x5202, 0x0260, 0x7df3, 0x6a28, 0x7f11, 0xdb47,
+ 0x9eee, 0x008f, 0x2001, 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x5206,
+ 0x026e, 0x7d17, 0x6a09, 0x7f04, 0x2001, 0x7cf9, 0x0000, 0x9eb4,
+ 0x0289, 0xdb13, 0x018a, 0x9efd, 0x6928, 0x7ffa, 0x0b70, 0x0311,
+ 0x5013, 0x52c0, 0x53c8, 0xc10d, 0x7db0, 0x0200, 0x9ea5, 0x0200,
+ 0x9ea1, 0x6a28, 0x7fed, 0xdb47, 0x9efd, 0x0458, 0x0454, 0x9eee,
+ 0x9eee
+};
+#endif
diff --git a/arch/arm/mach-mx3/sdma_script_code_pass2.h b/arch/arm/mach-mx3/sdma_script_code_pass2.h
new file mode 100644
index 000000000000..85de716c45f9
--- /dev/null
+++ b/arch/arm/mach-mx3/sdma_script_code_pass2.h
@@ -0,0 +1,434 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 */
+
+/*!
+ * @file sdma_script_code.h
+ * @brief This file contains functions of SDMA scripts code initialization
+ *
+ * The file was generated automatically. Based on sdma scripts library.
+ *
+ * @ingroup SDMA
+ */
+/*******************************************************************************
+
+ SDMA RELEASE LABEL: "SS15_MX31"
+
+*******************************************************************************/
+
+#ifndef __SDMA_SCRIPT_CODE_PASS2_H__
+#define __SDMA_SCRIPT_CODE_PASS2_H__
+
+/*!
+* SDMA ROM scripts start addresses and sizes
+*/
+
+#define start_ADDR_2 0
+#define start_SIZE_2 20
+
+#define core_ADDR_2 80
+#define core_SIZE_2 152
+
+#define common_ADDR_2 232
+#define common_SIZE_2 191
+
+#define ap_2_ap_ADDR_2 423
+#define ap_2_ap_SIZE_2 294
+
+#define bp_2_bp_ADDR_2 717
+#define bp_2_bp_SIZE_2 112
+
+#define ap_2_bp_ADDR_2 829
+#define ap_2_bp_SIZE_2 200
+
+#define bp_2_ap_ADDR_2 1029
+#define bp_2_ap_SIZE_2 223
+
+#define app_2_mcu_ADDR_2 1252
+#define app_2_mcu_SIZE_2 101
+
+#define mcu_2_app_ADDR_2 1353
+#define mcu_2_app_SIZE_2 127
+
+#define uart_2_mcu_ADDR_2 1480
+#define uart_2_mcu_SIZE_2 105
+
+#define uartsh_2_mcu_ADDR_2 1585
+#define uartsh_2_mcu_SIZE_2 98
+
+#define mcu_2_shp_ADDR_2 1683
+#define mcu_2_shp_SIZE_2 123
+
+#define shp_2_mcu_ADDR_2 1806
+#define shp_2_mcu_SIZE_2 101
+
+#define error_ADDR_2 1907
+#define error_SIZE_2 73
+
+#define test_ADDR_2 1980
+#define test_SIZE_2 63
+
+#define signature_ADDR_2 1023
+#define signature_SIZE_2 1
+
+/*!
+* SDMA RAM scripts start addresses and sizes
+*/
+
+#define ap_2_ap_fixed_addr_ADDR_2 6144
+#define ap_2_ap_fixed_addr_SIZE_2 68
+
+#define app_2_mcu_patched_ADDR_2 6212
+#define app_2_mcu_patched_SIZE_2 104
+
+#undef app_2_mcu_ADDR_2
+#undef app_2_mcu_SIZE_2
+
+/*mapping the app_2_mcu start address to the patched(RAM)script start address*/
+#define app_2_mcu_ADDR_2 app_2_mcu_patched_ADDR_2
+#define app_2_mcu_SIZE_2 app_2_mcu_patched_SIZE_2
+
+#define app_2_per_ADDR_2 6316
+#define app_2_per_SIZE_2 105
+
+#define ata_2_mcu_ADDR_2 6421
+#define ata_2_mcu_SIZE_2 110
+
+#define firi_2_mcu_ADDR_2 6531
+#define firi_2_mcu_SIZE_2 114
+
+#define loop_DMAs_fixed_addr_ADDR_2 6645
+#define loop_DMAs_fixed_addr_SIZE_2 90
+
+#define mcu_2_app_patched_ADDR_2 6735
+#define mcu_2_app_patched_SIZE_2 129
+
+#undef mcu_2_app_ADDR_2
+#undef mcu_2_app_SIZE_2
+
+/*mapping the mcu_2_app start address to the patched(RAM)script start address*/
+#define mcu_2_app_ADDR_2 mcu_2_app_patched_ADDR_2
+#define mcu_2_app_SIZE_2 mcu_2_app_patched_SIZE_2
+
+#define mcu_2_ata_ADDR_2 6864
+#define mcu_2_ata_SIZE_2 87
+
+#define mcu_2_firi_ADDR_2 6951
+#define mcu_2_firi_SIZE_2 77
+
+#define mcu_2_mshc_ADDR_2 7028
+#define mcu_2_mshc_SIZE_2 48
+
+#define mcu_2_shp_patched_ADDR_2 7076
+#define mcu_2_shp_patched_SIZE_2 125
+
+#undef mcu_2_shp_ADDR_2
+#undef mcu_2_shp_SIZE_2
+
+/*mapping the mcu_2_shp start address to the patched(RAM)script start address*/
+#define mcu_2_shp_ADDR_2 mcu_2_shp_patched_ADDR_2
+#define mcu_2_shp_SIZE_2 mcu_2_shp_patched_SIZE_2
+
+#define mshc_2_mcu_ADDR_2 7201
+#define mshc_2_mcu_SIZE_2 60
+
+#define per_2_app_ADDR_2 7261
+#define per_2_app_SIZE_2 131
+
+#define per_2_shp_ADDR_2 7392
+#define per_2_shp_SIZE_2 131
+
+#define shp_2_mcu_patched_ADDR_2 7523
+#define shp_2_mcu_patched_SIZE_2 104
+
+#undef shp_2_mcu_ADDR_2
+#undef shp_2_mcu_SIZE_2
+
+/*mapping the shp_2_mcu start address to the patched(RAM)script start address*/
+#define shp_2_mcu_ADDR_2 shp_2_mcu_patched_ADDR_2
+#define shp_2_mcu_SIZE_2 shp_2_mcu_patched_SIZE_2
+
+#define shp_2_per_ADDR_2 7627
+#define shp_2_per_SIZE_2 109
+
+#define uart_2_mcu_patched_ADDR_2 7736
+#define uart_2_mcu_patched_SIZE_2 106
+
+#undef uart_2_mcu_ADDR_2
+#undef uart_2_mcu_SIZE_2
+
+/*mapping the uart_2_mcu start address to the patched(RAM)script start address*/
+#define uart_2_mcu_ADDR_2 uart_2_mcu_patched_ADDR_2
+#define uart_2_mcu_SIZE_2 uart_2_mcu_patched_SIZE_2
+
+#define uartsh_2_mcu_patched_ADDR_2 7842
+#define uartsh_2_mcu_patched_SIZE_2 99
+
+#undef uartsh_2_mcu_ADDR_2
+#undef uartsh_2_mcu_SIZE_2
+
+/*
+ * mapping the uartsh_2_mcu start address to the patched(RAM)script
+ * start address
+ */
+#define uartsh_2_mcu_ADDR_2 uartsh_2_mcu_patched_ADDR_2
+#define uartsh_2_mcu_SIZE_2 uartsh_2_mcu_patched_SIZE_2
+
+/*!
+* SDMA RAM image start address and size
+*/
+
+#define RAM_CODE_START_ADDR_2 6144
+#define RAM_CODE_SIZE_2 1797
+
+/*!
+* Buffer that holds the SDMA RAM image
+*/
+
+__attribute__ ((__aligned__(4)))
+#ifndef CONFIG_XIP_KERNEL
+const
+#endif
+static const short sdma_code_2[] = {
+ 0x0970, 0x0111, 0x5111, 0x5ef9, 0xc0ec, 0x7d23, 0x5ad1, 0x5bd9,
+ 0xc0fe, 0x7c1f, 0x5ce1, 0x5de9, 0x5ef1, 0x08ff, 0x0011, 0x28ff,
+ 0x00bc, 0x048e, 0x56f9, 0x0660, 0x7d05, 0x0661, 0x7c2b, 0x6c07,
+ 0x6d13, 0x9821, 0x0661, 0x7d26, 0x6c17, 0x6d03, 0x028d, 0x058c,
+ 0x048a, 0xd9f5, 0x7e08, 0x7f07, 0x54e1, 0x52d1, 0x53d9, 0xc10d,
+ 0x7dde, 0x0200, 0x9804, 0x0660, 0x7d03, 0x6007, 0x52f1, 0x9832,
+ 0x6003, 0x52e9, 0x00a2, 0x0007, 0x6a0c, 0x6a0c, 0x6207, 0x6a07,
+ 0x6a2b, 0x6a28, 0x0007, 0x6a0c, 0x54e1, 0xc795, 0x048b, 0x0498,
+ 0x0454, 0x9825, 0x0800, 0x983c, 0x0870, 0x0011, 0x5010, 0xc0ec,
+ 0x7d61, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200,
+ 0x984a, 0x6ec3, 0x6d07, 0x5df0, 0x0dff, 0x0511, 0x1dff, 0x05bc,
+ 0x4d00, 0x7d45, 0x0b70, 0x0311, 0x522b, 0x5313, 0x02b9, 0x4a00,
+ 0x7c04, 0x6a28, 0x7f3b, 0x0400, 0x985a, 0x008f, 0x00d5, 0x7d01,
+ 0x008d, 0x05a0, 0x0a03, 0x0212, 0x02bc, 0x0210, 0x4a00, 0x7d1c,
+ 0x4a02, 0x7d20, 0x4a01, 0x7d23, 0x0b70, 0x0311, 0x53eb, 0x62c8,
+ 0x7e25, 0x0360, 0x7d02, 0x0210, 0x0212, 0x6a09, 0x7f1f, 0x0212,
+ 0x6a09, 0x7f1c, 0x0212, 0x6a09, 0x7f19, 0x2003, 0x4800, 0x7cef,
+ 0x0b70, 0x0311, 0x5313, 0x989b, 0x0015, 0x0015, 0x7802, 0x62c8,
+ 0x6a0b, 0x989a, 0x0015, 0x7802, 0x62c8, 0x6a0a, 0x989a, 0x7802,
+ 0x62c8, 0x6a09, 0x7c03, 0x6a28, 0x0000, 0x9858, 0xc77b, 0x6a28,
+ 0x7ffd, 0x0870, 0x0011, 0x5010, 0x52c0, 0x53c8, 0xc10d, 0x7da2,
+ 0x0200, 0x984a, 0x0200, 0x9847, 0x0870, 0x0011, 0x5010, 0xc0ec,
+ 0x7d62, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200,
+ 0x98b2, 0x6ec3, 0x6dd7, 0x5df0, 0x0dff, 0x0511, 0x1dff, 0x05bc,
+ 0x4d00, 0x7d46, 0x0b70, 0x0311, 0x522b, 0x5313, 0x02b9, 0x4a00,
+ 0x7c04, 0x62ff, 0x7e3c, 0x0400, 0x98c2, 0x008f, 0x00d5, 0x7d01,
+ 0x008d, 0x05a0, 0x0a03, 0x0212, 0x02bc, 0x0210, 0x4a00, 0x7d28,
+ 0x4a02, 0x7d20, 0x4a01, 0x7d19, 0x6ddd, 0x0b70, 0x0311, 0x53eb,
+ 0x62c8, 0x7e25, 0x0360, 0x7d02, 0x0210, 0x0212, 0x6ac8, 0x7f1f,
+ 0x0212, 0x6ac8, 0x7f1c, 0x0212, 0x6ac8, 0x7f19, 0x2003, 0x4800,
+ 0x7cef, 0x0b70, 0x0311, 0x5313, 0x9905, 0x6ddd, 0x7802, 0x62c8,
+ 0x6ac8, 0x9904, 0x6dde, 0x0015, 0x7802, 0x62c8, 0x6ac8, 0x9904,
+ 0x0015, 0x0015, 0x7801, 0x62d8, 0x7c02, 0x0000, 0x98c0, 0xc777,
+ 0x62ff, 0x7efd, 0x0870, 0x0011, 0x5010, 0x52c0, 0x53c8, 0xc10d,
+ 0x7da1, 0x0200, 0x98b2, 0x0200, 0x98af, 0xc19d, 0xc0ec, 0x7d69,
+ 0x0c70, 0x0411, 0x5414, 0x5ac4, 0x028c, 0x58da, 0x5efa, 0xc0fe,
+ 0x56fa, 0x7d02, 0x0200, 0x991e, 0x6d07, 0x5bca, 0x5cd2, 0x0bff,
+ 0x0311, 0x1bff, 0x04bb, 0x0415, 0x53da, 0x4c00, 0x7d47, 0x0a70,
+ 0x0211, 0x552a, 0x5212, 0x008d, 0x00bb, 0x4800, 0x7c07, 0x05b9,
+ 0x4d00, 0x7c13, 0x6928, 0x7f2d, 0x0400, 0x992f, 0x008f, 0x0015,
+ 0x04d8, 0x7d01, 0x008c, 0x04a0, 0x0015, 0x7802, 0x55c6, 0x6d0b,
+ 0x7e29, 0x6d28, 0x7f1e, 0x0000, 0x992d, 0x1e20, 0x5506, 0x2620,
+ 0x008d, 0x0560, 0x7c08, 0x065f, 0x55c6, 0x063f, 0x7e1b, 0x6d0a,
+ 0x7f10, 0x4c00, 0x7d1b, 0x04d8, 0x7d02, 0x008c, 0x0020, 0x04a0,
+ 0x0015, 0x7802, 0x55c6, 0x6d0b, 0x7e0d, 0x6d28, 0x7f02, 0x0000,
+ 0x9976, 0x0007, 0x680c, 0x6d0c, 0x6507, 0x6d07, 0x6d2b, 0x6d28,
+ 0x0007, 0x680c, 0x0007, 0x54d2, 0x0454, 0x9979, 0x6928, 0x7ff1,
+ 0x54d2, 0x008a, 0x52c0, 0x53c8, 0xc10d, 0x0288, 0x7d9f, 0x0200,
+ 0x991e, 0x0200, 0x9916, 0x1e10, 0x0870, 0x0011, 0x5010, 0xc0ec,
+ 0x7d39, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200,
+ 0x998a, 0x6d07, 0x5df0, 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x4d00,
+ 0x7d17, 0x6ec3, 0x62c8, 0x7e28, 0x0264, 0x7d08, 0x0b70, 0x0311,
+ 0x522b, 0x02b9, 0x4a00, 0x7c18, 0x0400, 0x9999, 0x0212, 0x3aff,
+ 0x008a, 0x05d8, 0x7d01, 0x008d, 0x0a10, 0x6ed3, 0x6ac8, 0xd9d4,
+ 0x6a28, 0x7f17, 0x0b70, 0x0311, 0x5013, 0xd9ec, 0x52c0, 0x53c8,
+ 0xc10d, 0x7dd0, 0x0200, 0x998a, 0x008f, 0x00d5, 0x7d01, 0x008d,
+ 0xd9d4, 0x9997, 0x0200, 0x9987, 0x0007, 0x68cc, 0x6a28, 0x7f01,
+ 0x99d2, 0x0007, 0x6a0c, 0x6a0c, 0x6207, 0x6a07, 0x6a2b, 0x6a28,
+ 0x0007, 0x680c, 0x0454, 0x99b0, 0x05a0, 0x1e08, 0x6ec3, 0x0388,
+ 0x3b03, 0x0015, 0x0015, 0x7802, 0x62c8, 0x6a0b, 0x7ee5, 0x6a28,
+ 0x7fe8, 0x0000, 0x6ec1, 0x008b, 0x7802, 0x62c8, 0x6a09, 0x7edc,
+ 0x6a28, 0x7fdf, 0x2608, 0x0006, 0x55f0, 0x6207, 0x02a5, 0x0dff,
+ 0x0511, 0x1dff, 0x04b5, 0x049a, 0x0006, 0x0388, 0x028d, 0x3a03,
+ 0x4a00, 0x7c33, 0x028c, 0x3a03, 0x4a00, 0x7d0c, 0x0804, 0x00a2,
+ 0x00db, 0x7d24, 0x03a0, 0x0498, 0x7802, 0x6209, 0x6a29, 0x7e24,
+ 0x620c, 0x7e22, 0x0804, 0x03d0, 0x7d19, 0x0820, 0x028c, 0x3a1f,
+ 0x00a2, 0x03d0, 0x7c02, 0x008b, 0x3003, 0x03a0, 0x0015, 0x0015,
+ 0x6818, 0x7e12, 0x6828, 0x7f10, 0x0000, 0x0820, 0x03d8, 0x7df5,
+ 0x0804, 0x03d0, 0x7d03, 0x008b, 0x3003, 0x9a15, 0x008b, 0x7802,
+ 0x6209, 0x6a29, 0x7e01, 0x620c, 0x0006, 0x0804, 0x03d0, 0x7df6,
+ 0x048b, 0x3403, 0x03a4, 0x0415, 0x0415, 0x0d0f, 0x0511, 0x1df0,
+ 0x0808, 0x04d0, 0x7c01, 0x008c, 0x58c1, 0x04a0, 0x7803, 0x620b,
+ 0x5a05, 0x1d01, 0x7ee9, 0x50c1, 0x05a0, 0x7803, 0x5205, 0x6a0b,
+ 0x1d01, 0x6a28, 0x7fe1, 0x0000, 0x4c00, 0x7ce7, 0x9a26, 0x0870,
+ 0x0011, 0x5010, 0xc0ec, 0x7d7a, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe,
+ 0x56f8, 0x7d02, 0x0200, 0x9a55, 0x6d03, 0x6ed3, 0x0dff, 0x0511,
+ 0x1dff, 0x05bc, 0x5df8, 0x4d00, 0x7d5e, 0x0b70, 0x0311, 0x522b,
+ 0x5313, 0x02b9, 0x4a00, 0x7c04, 0x62ff, 0x7e3f, 0x0400, 0x9a65,
+ 0x008f, 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x5ddb, 0x0d03, 0x0512,
+ 0x05bc, 0x0510, 0x5dd3, 0x4d00, 0x7d27, 0x4d02, 0x7d20, 0x4d01,
+ 0x7d1a, 0x0b70, 0x0311, 0x53eb, 0x0360, 0x7d05, 0x6509, 0x7e25,
+ 0x620a, 0x7e23, 0x9a8f, 0x620a, 0x7e20, 0x6509, 0x7e1e, 0x0512,
+ 0x0512, 0x02ad, 0x6ac8, 0x7f19, 0x2003, 0x4800, 0x7ced, 0x0b70,
+ 0x0311, 0x5313, 0x9aaa, 0x7802, 0x6209, 0x6ac8, 0x9aa9, 0x0015,
+ 0x7802, 0x620a, 0x6ac8, 0x9aa9, 0x0015, 0x0015, 0x7802, 0x620b,
+ 0x6ac8, 0x7c03, 0x0000, 0x55db, 0x9a63, 0x0007, 0x68cc, 0x680c,
+ 0x55d3, 0x4d00, 0x7d03, 0x4d02, 0x7d02, 0x9ab8, 0x0017, 0x0017,
+ 0x55db, 0x009d, 0x55fb, 0x05a0, 0x08ff, 0x0011, 0x18ff, 0x0010,
+ 0x04b8, 0x04ad, 0x0454, 0x62ff, 0x7ee8, 0x0870, 0x0011, 0x5010,
+ 0x52c0, 0x53c8, 0xc10d, 0x7d89, 0x0200, 0x9a55, 0x0200, 0x9a52,
+ 0xc19d, 0xc0ec, 0x7d52, 0x0c70, 0x0411, 0x5414, 0x5ac4, 0x028c,
+ 0x58da, 0x5efa, 0xc0fe, 0x56fa, 0x7d02, 0x0200, 0x9ad9, 0x6d03,
+ 0x5bca, 0x5cd2, 0x0bff, 0x0311, 0x1bff, 0x04bb, 0x0415, 0x53da,
+ 0x0a70, 0x0211, 0x4c00, 0x7d28, 0x552a, 0x05bb, 0x4d00, 0x7c02,
+ 0x0400, 0x9aec, 0x4c01, 0x7d0f, 0x008f, 0x0015, 0x04d8, 0x7d01,
+ 0x008c, 0x0020, 0x04a0, 0x0015, 0x7802, 0x650b, 0x5d06, 0x0000,
+ 0x7e0c, 0x7f0d, 0x9aea, 0x650a, 0x7e08, 0x008d, 0x0011, 0x0010,
+ 0x05a8, 0x065f, 0x5d06, 0x063f, 0x7f02, 0x0007, 0x680c, 0x0007,
+ 0x5012, 0x54d0, 0x0454, 0x9b16, 0x5012, 0x54d0, 0x0473, 0x7c06,
+ 0x552a, 0x05b9, 0x4d00, 0x7c02, 0x0400, 0x9b18, 0x52c0, 0x53c8,
+ 0xc10d, 0x0288, 0x7db6, 0x0200, 0x9ad9, 0x0200, 0x9ad1, 0x0870,
+ 0x0011, 0x5010, 0xc0ec, 0x7d46, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe,
+ 0x56f8, 0x7d02, 0x0200, 0x9b2d, 0x0b70, 0x0311, 0x6ed3, 0x6d03,
+ 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x4d00, 0x7d2b, 0x522b, 0x02b9,
+ 0x4a00, 0x7c04, 0x62c8, 0x7e1f, 0x0400, 0x9b3e, 0x008f, 0x00d5,
+ 0x7d01, 0x008d, 0x05a0, 0x0060, 0x7c05, 0x6edd, 0x6209, 0x7e16,
+ 0x6ac8, 0x7f11, 0x0015, 0x0060, 0x7c05, 0x6ede, 0x620a, 0x7e0e,
+ 0x6ac8, 0x7f09, 0x6edf, 0x0015, 0x7802, 0x620b, 0x6ac8, 0x0000,
+ 0x7e05, 0x7f01, 0x9b3c, 0x0007, 0x68cc, 0x9b68, 0x0007, 0x6a0c,
+ 0x0454, 0x62c8, 0x7ef8, 0x5013, 0x52c0, 0x53c8, 0xc10d, 0x7dbd,
+ 0x0200, 0x9b2d, 0x0200, 0x9b2a, 0xc19d, 0x0870, 0x0011, 0xc0ec,
+ 0x7d29, 0x5010, 0x5ac0, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200,
+ 0x9b7b, 0x0870, 0x0011, 0x6d03, 0x0dff, 0x0511, 0x1dff, 0x05bc,
+ 0x4d00, 0x7d12, 0x5228, 0x02b9, 0x4a00, 0x7c02, 0x0400, 0x9b8a,
+ 0x620b, 0x7e06, 0x5a06, 0x7f06, 0x0000, 0x2504, 0x7d05, 0x9b8a,
+ 0x0007, 0x680c, 0x0007, 0x0454, 0x5010, 0x52c0, 0xc10d, 0x7ddb,
+ 0x0200, 0x9b7b, 0x0200, 0x9b77, 0xc19d, 0x0870, 0x0011, 0xc0ec,
+ 0x7d76, 0x5010, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02,
+ 0x0200, 0x9bab, 0x6d03, 0x0d03, 0x0512, 0x05bc, 0x0510, 0x5dd0,
+ 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x5df8, 0x4d00, 0x7d57, 0x0a70,
+ 0x0211, 0x532a, 0x5212, 0x03b9, 0x4b00, 0x7c02, 0x0400, 0x9bbf,
+ 0x008f, 0x05d8, 0x7d01, 0x008d, 0x05a0, 0x5dda, 0x55d2, 0x4d00,
+ 0x7d27, 0x4d02, 0x7d20, 0x4d01, 0x7d1a, 0x0a70, 0x0211, 0x52ea,
+ 0x0260, 0x7d05, 0x6509, 0x7e25, 0x630a, 0x7e23, 0x9be3, 0x630a,
+ 0x7e20, 0x6509, 0x7e1e, 0x0512, 0x0512, 0x03ad, 0x5b06, 0x7f19,
+ 0x2003, 0x4800, 0x7ced, 0x0a70, 0x0211, 0x5212, 0x9bfe, 0x7802,
+ 0x6309, 0x5b06, 0x9bfd, 0x0015, 0x7802, 0x630a, 0x5b06, 0x9bfd,
+ 0x0015, 0x0015, 0x7802, 0x630b, 0x5b06, 0x7c03, 0x55da, 0x0000,
+ 0x9bbd, 0x0007, 0x680c, 0x55d2, 0x4d00, 0x7d03, 0x4d02, 0x7d02,
+ 0x9c0b, 0x0017, 0x0017, 0x55da, 0x009d, 0x55fa, 0x05a0, 0x08ff,
+ 0x0011, 0x18ff, 0x0010, 0x04b8, 0x04ad, 0x0454, 0x0870, 0x0011,
+ 0x5010, 0x52c0, 0x53c8, 0xc10d, 0x7d8e, 0x0200, 0x9bab, 0x0200,
+ 0x9ba7, 0xc19d, 0x0870, 0x0011, 0xc0ec, 0x7d35, 0x5010, 0x5ac0,
+ 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200, 0x9c28, 0x0870, 0x0011,
+ 0x6d07, 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x4d00, 0x7d1c, 0x5228,
+ 0x02b9, 0x4a00, 0x7c04, 0x6928, 0x7f0b, 0x0400, 0x9c37, 0x5206,
+ 0x7e10, 0x6a0b, 0x6928, 0x7f04, 0x0000, 0x2504, 0x7d0c, 0x9c37,
+ 0x0007, 0x680c, 0x680c, 0x6207, 0x6a07, 0x6a2b, 0x6a28, 0x0007,
+ 0x680c, 0x0007, 0x0454, 0x6928, 0x7ff3, 0x5010, 0x52c0, 0xc10d,
+ 0x7dcf, 0x0200, 0x9c28, 0x0200, 0x9c24, 0x0870, 0x0011, 0x5010,
+ 0xc0ec, 0x7d7c, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02,
+ 0x0200, 0x9c63, 0x6ed3, 0x6dc5, 0x0dff, 0x0511, 0x1dff, 0x05bc,
+ 0x5df8, 0x4d00, 0x7d60, 0x0b70, 0x0311, 0x522b, 0x5313, 0x02b9,
+ 0x4a00, 0x7c02, 0x0400, 0x9c73, 0x008f, 0x00d5, 0x7d01, 0x008d,
+ 0x05a0, 0x5ddb, 0x0d03, 0x0512, 0x05bc, 0x0510, 0x5dd3, 0x4d00,
+ 0x7d2c, 0x4d02, 0x7d24, 0x4d01, 0x7d1e, 0x59e3, 0x0b70, 0x0311,
+ 0x53eb, 0x61c8, 0x7e2b, 0x62c8, 0x7e29, 0x65c8, 0x7e27, 0x0360,
+ 0x7d03, 0x0112, 0x0112, 0x9c9e, 0x0512, 0x0512, 0x0211, 0x02a9,
+ 0x02ad, 0x6ac8, 0x7f1b, 0x2003, 0x4800, 0x7ceb, 0x0b70, 0x0311,
+ 0x5313, 0x51e3, 0x9cbb, 0x7802, 0x62c8, 0x6ac8, 0x9cba, 0x6dce,
+ 0x0015, 0x7802, 0x62c8, 0x6ac8, 0x9cba, 0x6dcf, 0x0015, 0x0015,
+ 0x7801, 0x62d8, 0x7c03, 0x0000, 0x55db, 0x9c71, 0x0007, 0x68ff,
+ 0x55d3, 0x4d00, 0x7d03, 0x4d02, 0x7d02, 0x9cc8, 0x0017, 0x0017,
+ 0x55db, 0x009d, 0x55fb, 0x05a0, 0x08ff, 0x0011, 0x18ff, 0x0010,
+ 0x04b8, 0x04ad, 0x0454, 0x62c8, 0x7ee9, 0x0870, 0x0011, 0x5010,
+ 0x52c0, 0x53c8, 0xc10d, 0x7d87, 0x0200, 0x9c63, 0x0200, 0x9c60,
+ 0xc19d, 0x0870, 0x0011, 0xc0ec, 0x7d7c, 0x5010, 0x5ac0, 0x5bc8,
+ 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200, 0x9ce7, 0x6dc5, 0x0d03,
+ 0x0512, 0x05bc, 0x0510, 0x5dd0, 0x0dff, 0x0511, 0x1dff, 0x05bc,
+ 0x5df8, 0x4d00, 0x7d5d, 0x0a70, 0x0211, 0x532a, 0x5212, 0x03b9,
+ 0x4b00, 0x7c02, 0x0400, 0x9cfb, 0x008f, 0x05d8, 0x7d01, 0x008d,
+ 0x05a0, 0x5dda, 0x55d2, 0x4d00, 0x7d2c, 0x4d02, 0x7d24, 0x4d01,
+ 0x7d1e, 0x59e2, 0x0a70, 0x0211, 0x52ea, 0x61c8, 0x7e2c, 0x63c8,
+ 0x7e2a, 0x65c8, 0x7e28, 0x0260, 0x7d03, 0x0112, 0x0112, 0x9d22,
+ 0x0512, 0x0512, 0x0311, 0x03a9, 0x03ad, 0x5b06, 0x7f1c, 0x2003,
+ 0x4800, 0x7ceb, 0x0a70, 0x0211, 0x5212, 0x51e2, 0x9d40, 0x7802,
+ 0x63c8, 0x5b06, 0x9d3f, 0x6dce, 0x0015, 0x7802, 0x63c8, 0x5b06,
+ 0x9d3f, 0x6dcf, 0x0015, 0x0015, 0x7802, 0x63c8, 0x5b06, 0x7c03,
+ 0x55da, 0x0000, 0x9cf9, 0x0007, 0x68ff, 0x55d2, 0x4d00, 0x7d03,
+ 0x4d02, 0x7d02, 0x9d4d, 0x0017, 0x0017, 0x55da, 0x009d, 0x55fa,
+ 0x05a0, 0x08ff, 0x0011, 0x18ff, 0x0010, 0x04b8, 0x04ad, 0x0454,
+ 0x0870, 0x0011, 0x5010, 0x52c0, 0x53c8, 0xc10d, 0x7d88, 0x0200,
+ 0x9ce7, 0x0200, 0x9ce3, 0xc19d, 0x0870, 0x0011, 0xc0ec, 0x7d61,
+ 0x5010, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200,
+ 0x9d6a, 0x6d07, 0x5df0, 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x4d00,
+ 0x7d45, 0x0a70, 0x0211, 0x532a, 0x5212, 0x03b9, 0x4b00, 0x7c04,
+ 0x6a28, 0x7f3b, 0x0400, 0x9d79, 0x008f, 0x05d8, 0x7d01, 0x008d,
+ 0x05a0, 0x0b03, 0x0312, 0x03bc, 0x0310, 0x4b00, 0x7d1c, 0x4b02,
+ 0x7d20, 0x4b01, 0x7d23, 0x0a70, 0x0211, 0x52ea, 0x5306, 0x7e25,
+ 0x0260, 0x7d02, 0x0310, 0x0312, 0x6b09, 0x7f1f, 0x0312, 0x6b09,
+ 0x7f1c, 0x0312, 0x6b09, 0x7f19, 0x2003, 0x4800, 0x7cef, 0x0a70,
+ 0x0211, 0x5212, 0x9dba, 0x0015, 0x0015, 0x7802, 0x5306, 0x6b0b,
+ 0x9db9, 0x0015, 0x7802, 0x5306, 0x6b0a, 0x9db9, 0x7802, 0x5306,
+ 0x6b09, 0x7c03, 0x6b28, 0x0000, 0x9d77, 0xc77b, 0x6928, 0x7ffd,
+ 0x0870, 0x0011, 0x5010, 0x52c0, 0x53c8, 0xc10d, 0x7da3, 0x0200,
+ 0x9d6a, 0x0200, 0x9d66, 0xc19d, 0x0870, 0x0011, 0xc0ec, 0x7d60,
+ 0x5010, 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200,
+ 0x9dd2, 0x6dd7, 0x5df0, 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x4d00,
+ 0x7d46, 0x0a70, 0x0211, 0x532a, 0x5212, 0x03b9, 0x4b00, 0x7c02,
+ 0x0400, 0x9de1, 0x008f, 0x05d8, 0x7d01, 0x008d, 0x05a0, 0x0b03,
+ 0x0312, 0x03bc, 0x0310, 0x4b00, 0x7d28, 0x4b02, 0x7d20, 0x4b01,
+ 0x7d19, 0x6ddd, 0x0a70, 0x0211, 0x52ea, 0x5306, 0x7e27, 0x0260,
+ 0x7d02, 0x0310, 0x0312, 0x6bc8, 0x7f21, 0x0312, 0x6bc8, 0x7f1e,
+ 0x0312, 0x6bc8, 0x7f1b, 0x2003, 0x4800, 0x7cef, 0x0a70, 0x0211,
+ 0x5212, 0x9e23, 0x6ddd, 0x7802, 0x5306, 0x6bc8, 0x9e22, 0x6dde,
+ 0x0015, 0x7802, 0x5306, 0x6bc8, 0x9e22, 0x0015, 0x0015, 0x7802,
+ 0x5306, 0x6bc8, 0x7c03, 0x0000, 0xde32, 0x9ddf, 0xc777, 0x0870,
+ 0x0011, 0x5010, 0x52c0, 0x53c8, 0xc10d, 0x7da4, 0x0200, 0x9dd2,
+ 0x0200, 0x9dce, 0x63ff, 0x0368, 0x7d02, 0x0369, 0x7def, 0x0006,
+ 0x0870, 0x0011, 0x5010, 0xc0ec, 0x7d5c, 0x5ac0, 0x5bc8, 0x5ef8,
+ 0xc0fe, 0x56f8, 0x7d02, 0x0200, 0x9e3e, 0x0b70, 0x0311, 0x6ec3,
+ 0x6d07, 0x5df0, 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x4d00, 0x7d3e,
+ 0x522b, 0x02b9, 0x4a00, 0x7c04, 0x6a28, 0x7f34, 0x0400, 0x9e50,
+ 0x028e, 0x1a94, 0x6ac3, 0x62c8, 0x0269, 0x7d1b, 0x1e94, 0x6ec3,
+ 0x6ed3, 0x62c8, 0x0248, 0x6ac8, 0x2694, 0x6ec3, 0x62c8, 0x026e,
+ 0x7d32, 0x6a09, 0x7f1f, 0x2501, 0x4d00, 0x7d20, 0x028e, 0x1a98,
+ 0x6ac3, 0x62c8, 0x6ec3, 0x0260, 0x7df1, 0x6a28, 0x7f13, 0xc7af,
+ 0x9e90, 0x6ee3, 0x008f, 0x2001, 0x00d5, 0x7d01, 0x008d, 0x05a0,
+ 0x62c8, 0x026e, 0x7d18, 0x6a09, 0x7f05, 0x2001, 0x7cf9, 0x6a28,
+ 0x0000, 0x9e4e, 0x0289, 0xc77b, 0x018a, 0x9e9f, 0x6a28, 0x7ffa,
+ 0x0b70, 0x0311, 0x5013, 0x52c0, 0x53c8, 0xc10d, 0x7da7, 0x0200,
+ 0x9e3e, 0x0200, 0x9e3b, 0x6a28, 0x7fed, 0xc7af, 0x9e9f, 0x0458,
+ 0x0454, 0x9e90, 0xc19d, 0x0870, 0x0011, 0xc0ec, 0x7d55, 0x5010,
+ 0x5ac0, 0x5bc8, 0x5ef8, 0xc0fe, 0x56f8, 0x7d02, 0x0200, 0x9ea9,
+ 0x0b70, 0x0311, 0x6d07, 0x5df0, 0x0dff, 0x0511, 0x1dff, 0x05bc,
+ 0x4d00, 0x7d37, 0x522b, 0x02b9, 0x4a00, 0x7c04, 0x6928, 0x7f2d,
+ 0x0400, 0x9eba, 0x028e, 0x1a94, 0x5202, 0x0269, 0x7d16, 0x1e94,
+ 0x5206, 0x0248, 0x5a06, 0x2694, 0x5206, 0x026e, 0x7d2f, 0x6a09,
+ 0x7f1c, 0x2501, 0x4d00, 0x7d1d, 0x028e, 0x1a98, 0x5202, 0x0260,
+ 0x7df3, 0x6a28, 0x7f12, 0xc7af, 0x9ef3, 0x008f, 0x2001, 0x00d5,
+ 0x7d01, 0x008d, 0x05a0, 0x5206, 0x026e, 0x7d18, 0x6a09, 0x7f05,
+ 0x2001, 0x7cf9, 0x6a28, 0x0000, 0x9eb8, 0x0289, 0xc77b, 0x018a,
+ 0x9f02, 0x6928, 0x7ffa, 0x0b70, 0x0311, 0x5013, 0x52c0, 0x53c8,
+ 0xc10d, 0x7daf, 0x0200, 0x9ea9, 0x0200, 0x9ea5, 0x6a28, 0x7fed,
+ 0xc7af, 0x9f02, 0x0458, 0x0454, 0x9ef3
+};
+#endif
diff --git a/arch/arm/mach-mx3/serial.c b/arch/arm/mach-mx3/serial.c
new file mode 100644
index 000000000000..fca1b5b8a38f
--- /dev/null
+++ b/arch/arm/mach-mx3/serial.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2006-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @file mach-mx3/serial.c
+ *
+ * @brief This file contains the UART initiliazation.
+ *
+ * @ingroup MSL_MX31
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <mach/hardware.h>
+#include <mach/mxc_uart.h>
+#include <mach/spba.h>
+#include "serial.h"
+#include "board-mx31ads.h"
+#include "board-mx3_3stack.h"
+
+#if defined(CONFIG_SERIAL_MXC) || defined(CONFIG_SERIAL_MXC_MODULE)
+
+/*!
+ * This is an array where each element holds information about a UART port,
+ * like base address of the UART, interrupt numbers etc. This structure is
+ * passed to the serial_core.c file. Based on which UART is used, the core file
+ * passes back the appropriate port structure as an argument to the control
+ * functions.
+ */
+static uart_mxc_port mxc_ports[MXC_UART_NR] = {
+ [0] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART1_BASE_ADDR),
+ .mapbase = UART1_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART1_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 0,
+ },
+ .ints_muxed = UART1_MUX_INTS,
+ .irqs = {UART1_INT2, UART1_INT3},
+ .mode = UART1_MODE,
+ .ir_mode = UART1_IR,
+ .enabled = UART1_ENABLED,
+ .hardware_flow = UART1_HW_FLOW,
+ .cts_threshold = UART1_UCR4_CTSTL,
+ .dma_enabled = UART1_DMA_ENABLE,
+ .dma_rxbuf_size = UART1_DMA_RXBUFSIZE,
+ .rx_threshold = UART1_UFCR_RXTL,
+ .tx_threshold = UART1_UFCR_TXTL,
+ .shared = UART1_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART1_TX,
+ .dma_rx_id = MXC_DMA_UART1_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+ [1] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART2_BASE_ADDR),
+ .mapbase = UART2_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART2_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 1,
+ },
+ .ints_muxed = UART2_MUX_INTS,
+ .irqs = {UART2_INT2, UART2_INT3},
+ .mode = UART2_MODE,
+ .ir_mode = UART2_IR,
+ .enabled = UART2_ENABLED,
+ .hardware_flow = UART2_HW_FLOW,
+ .cts_threshold = UART2_UCR4_CTSTL,
+ .dma_enabled = UART2_DMA_ENABLE,
+ .dma_rxbuf_size = UART2_DMA_RXBUFSIZE,
+ .rx_threshold = UART2_UFCR_RXTL,
+ .tx_threshold = UART2_UFCR_TXTL,
+ .shared = UART2_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART2_TX,
+ .dma_rx_id = MXC_DMA_UART2_RX,
+ .rxd_mux = MXC_UART_IR_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+#if UART3_ENABLED == 1
+ [2] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART3_BASE_ADDR),
+ .mapbase = UART3_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART3_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 2,
+ },
+ .ints_muxed = UART3_MUX_INTS,
+ .irqs = {UART3_INT2, UART3_INT3},
+ .mode = UART3_MODE,
+ .ir_mode = UART3_IR,
+ .enabled = UART3_ENABLED,
+ .hardware_flow = UART3_HW_FLOW,
+ .cts_threshold = UART3_UCR4_CTSTL,
+ .dma_enabled = UART3_DMA_ENABLE,
+ .dma_rxbuf_size = UART3_DMA_RXBUFSIZE,
+ .rx_threshold = UART3_UFCR_RXTL,
+ .tx_threshold = UART3_UFCR_TXTL,
+ .shared = UART3_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART3_TX,
+ .dma_rx_id = MXC_DMA_UART3_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+#endif
+#if UART4_ENABLED == 1
+ [3] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART4_BASE_ADDR),
+ .mapbase = UART4_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART4_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 3,
+ },
+ .ints_muxed = UART4_MUX_INTS,
+ .irqs = {UART4_INT2, UART4_INT3},
+ .mode = UART4_MODE,
+ .ir_mode = UART4_IR,
+ .enabled = UART4_ENABLED,
+ .hardware_flow = UART4_HW_FLOW,
+ .cts_threshold = UART4_UCR4_CTSTL,
+ .dma_enabled = UART4_DMA_ENABLE,
+ .dma_rxbuf_size = UART4_DMA_RXBUFSIZE,
+ .rx_threshold = UART4_UFCR_RXTL,
+ .tx_threshold = UART4_UFCR_TXTL,
+ .shared = UART4_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART4_TX,
+ .dma_rx_id = MXC_DMA_UART4_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+#endif
+#if UART5_ENABLED == 1
+ [4] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART5_BASE_ADDR),
+ .mapbase = UART5_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART5_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 4,
+ },
+ .ints_muxed = UART5_MUX_INTS,
+ .irqs = {UART5_INT2, UART5_INT3},
+ .mode = UART5_MODE,
+ .ir_mode = UART5_IR,
+ .enabled = UART5_ENABLED,
+ .hardware_flow = UART5_HW_FLOW,
+ .cts_threshold = UART5_UCR4_CTSTL,
+ .dma_enabled = UART5_DMA_ENABLE,
+ .dma_rxbuf_size = UART5_DMA_RXBUFSIZE,
+ .rx_threshold = UART5_UFCR_RXTL,
+ .tx_threshold = UART5_UFCR_TXTL,
+ .shared = UART5_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART5_TX,
+ .dma_rx_id = MXC_DMA_UART5_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+#endif
+};
+
+static struct platform_device mxc_uart_device1 = {
+ .name = "mxcintuart",
+ .id = 0,
+ .dev = {
+ .platform_data = &mxc_ports[0],
+ },
+};
+
+static struct platform_device mxc_uart_device2 = {
+ .name = "mxcintuart",
+ .id = 1,
+ .dev = {
+ .platform_data = &mxc_ports[1],
+ },
+};
+
+#if UART3_ENABLED == 1
+static struct platform_device mxc_uart_device3 = {
+ .name = "mxcintuart",
+ .id = 2,
+ .dev = {
+ .platform_data = &mxc_ports[2],
+ },
+};
+#endif
+
+#if UART4_ENABLED == 1
+static struct platform_device mxc_uart_device4 = {
+ .name = "mxcintuart",
+ .id = 3,
+ .dev = {
+ .platform_data = &mxc_ports[3],
+ },
+};
+#endif
+
+#if UART5_ENABLED == 1
+static struct platform_device mxc_uart_device5 = {
+ .name = "mxcintuart",
+ .id = 4,
+ .dev = {
+ .platform_data = &mxc_ports[4],
+ },
+};
+#endif
+
+static int __init mxc_init_uart(void)
+{
+ /* Register all the MXC UART platform device structures */
+ platform_device_register(&mxc_uart_device1);
+ platform_device_register(&mxc_uart_device2);
+
+ /* Grab ownership of shared UARTs 3 and 4, only when enabled */
+#if UART3_ENABLED == 1
+#if UART3_DMA_ENABLE == 1
+ spba_take_ownership(UART3_SHARED_PERI, (SPBA_MASTER_A | SPBA_MASTER_C));
+#else
+ spba_take_ownership(UART3_SHARED_PERI, SPBA_MASTER_A);
+#endif /* UART3_DMA_ENABLE */
+ platform_device_register(&mxc_uart_device3);
+#endif /* UART3_ENABLED */
+
+#if UART4_ENABLED == 1
+ platform_device_register(&mxc_uart_device4);
+#endif /* UART4_ENABLED */
+
+#if UART5_ENABLED == 1
+ platform_device_register(&mxc_uart_device5);
+#endif /* UART5_ENABLED */
+ return 0;
+}
+
+#else
+static int __init mxc_init_uart(void)
+{
+ return 0;
+}
+#endif
+
+arch_initcall(mxc_init_uart);
diff --git a/arch/arm/mach-mx3/serial.h b/arch/arm/mach-mx3/serial.h
new file mode 100644
index 000000000000..03cce3bf5f24
--- /dev/null
+++ b/arch/arm/mach-mx3/serial.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX3_SERIAL_H__
+#define __ARCH_ARM_MACH_MX3_SERIAL_H__
+
+/*!
+ * @file mach-mx3/serial.h
+ *
+ * @ingroup MSL_MX31
+ */
+#include <mach/mxc_uart.h>
+
+/* UART 1 configuration */
+/*!
+ * This option allows to choose either an interrupt-driven software controlled
+ * hardware flow control (set this option to 0) or hardware-driven hardware
+ * flow control (set this option to 1).
+ */
+/* UART used as wakeup source */
+#define UART1_HW_FLOW 0
+/*!
+ * This specifies the threshold at which the CTS pin is deasserted by the
+ * RXFIFO. Set this value in Decimal to anything from 0 to 32 for
+ * hardware-driven hardware flow control. Read the HW spec while specifying
+ * this value. When using interrupt-driven software controlled hardware
+ * flow control set this option to -1.
+ */
+#define UART1_UCR4_CTSTL 16
+/*!
+ * This is option to enable (set this option to 1) or disable DMA data transfer
+ */
+#define UART1_DMA_ENABLE 0
+/*!
+ * Specify the size of the DMA receive buffer. The minimum buffer size is 512
+ * bytes. The buffer size should be a multiple of 256.
+ */
+#define UART1_DMA_RXBUFSIZE 1024
+/*!
+ * Specify the MXC UART's Receive Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the RxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_RXTL 16
+/*!
+ * Specify the MXC UART's Transmit Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the TxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_TXTL 16
+/* UART 2 configuration */
+#define UART2_HW_FLOW 0
+#define UART2_UCR4_CTSTL -1
+#define UART2_DMA_ENABLE 0
+#define UART2_DMA_RXBUFSIZE 512
+#define UART2_UFCR_RXTL 16
+#define UART2_UFCR_TXTL 16
+/* UART 3 configuration */
+#define UART3_HW_FLOW 1
+#define UART3_UCR4_CTSTL 16
+#define UART3_DMA_ENABLE 1
+#define UART3_DMA_RXBUFSIZE 1024
+#define UART3_UFCR_RXTL 16
+#define UART3_UFCR_TXTL 16
+/* UART 4 configuration */
+#define UART4_HW_FLOW 1
+#define UART4_UCR4_CTSTL 16
+#define UART4_DMA_ENABLE 0
+#define UART4_DMA_RXBUFSIZE 512
+#define UART4_UFCR_RXTL 16
+#define UART4_UFCR_TXTL 16
+/* UART 5 configuration */
+#define UART5_HW_FLOW 1
+#define UART5_UCR4_CTSTL 16
+#define UART5_DMA_ENABLE 0
+#define UART5_DMA_RXBUFSIZE 512
+#define UART5_UFCR_RXTL 16
+#define UART5_UFCR_TXTL 16
+/*
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+/*
+ * Is the MUXED interrupt output sent to the ARM core
+ */
+#define INTS_NOTMUXED 0
+#define INTS_MUXED 1
+/* UART 1 configuration */
+/*!
+ * This define specifies whether the muxed ANDed interrupt line or the
+ * individual interrupts from the UART port is integrated with the ARM core.
+ * There exists a define like this for each UART port. Valid values that can
+ * be used are \b INTS_NOTMUXED or \b INTS_MUXED.
+ */
+#define UART1_MUX_INTS INTS_MUXED
+/*!
+ * This define specifies the transmitter interrupt number or the interrupt
+ * number of the ANDed interrupt in case the interrupts are muxed. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_INT1 MXC_INT_UART1
+/*!
+ * This define specifies the receiver interrupt number. If the interrupts of
+ * the UART are muxed, then we specify here a dummy value -1. There exists a
+ * define like this for each UART port.
+ */
+#define UART1_INT2 -1
+/*!
+ * This specifies the master interrupt number. If the interrupts of the UART
+ * are muxed, then we specify here a dummy value of -1. There exists a define
+ * like this for each UART port.
+ */
+#define UART1_INT3 -1
+/*!
+ * This specifies if the UART is a shared peripheral. It holds the shared
+ * peripheral number if it is shared or -1 if it is not shared. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_SHARED_PERI -1
+/* UART 2 configuration */
+#define UART2_MUX_INTS INTS_MUXED
+#define UART2_INT1 MXC_INT_UART2
+#define UART2_INT2 -1
+#define UART2_INT3 -1
+#define UART2_SHARED_PERI -1
+/* UART 3 configuration */
+#define UART3_MUX_INTS INTS_MUXED
+#define UART3_INT1 MXC_INT_UART3
+#define UART3_INT2 -1
+#define UART3_INT3 -1
+#define UART3_SHARED_PERI SPBA_UART3
+/* UART 4 configuration */
+#define UART4_MUX_INTS INTS_MUXED
+#define UART4_INT1 MXC_INT_UART4
+#define UART4_INT2 -1
+#define UART4_INT3 -1
+#define UART4_SHARED_PERI -1
+/* UART 5 configuration */
+#define UART5_MUX_INTS INTS_MUXED
+#define UART5_INT1 MXC_INT_UART5
+#define UART5_INT2 -1
+#define UART5_INT3 -1
+#define UART5_SHARED_PERI -1
+
+#endif /* __ARCH_ARM_MACH_MX3_SERIAL_H__ */
diff --git a/arch/arm/mach-mx3/system.c b/arch/arm/mach-mx3/system.c
new file mode 100644
index 000000000000..932bf99322d9
--- /dev/null
+++ b/arch/arm/mach-mx3/system.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 1999 ARM Limited
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/clk.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <asm/proc-fns.h>
+#include <asm/system.h>
+#include <mach/clock.h>
+#include "crm_regs.h"
+
+/*!
+ * @defgroup MSL_MX31 i.MX31 Machine Specific Layer (MSL)
+ */
+
+/*!
+ * @file mach-mx3/system.c
+ * @brief This file contains idle and reset functions.
+ *
+ * @ingroup MSL_MX31
+ */
+
+static int clks_initialized = 0;
+static struct clk *sdma_clk, *mbx_clk, *ipu_clk, *mpeg_clk, *vpu_clk, *usb_clk,
+ *rtic_clk, *nfc_clk, *emi_clk;
+
+extern int mxc_jtag_enabled;
+
+/*!
+ * This function puts the CPU into idle mode. It is called by default_idle()
+ * in process.c file.
+ */
+void arch_idle(void)
+{
+ int emi_gated_off = 0;
+
+ /*
+ * This should do all the clock switching
+ * and wait for interrupt tricks.
+ */
+ if (!mxc_jtag_enabled) {
+ if (clks_initialized == 0) {
+ clks_initialized = 1;
+ sdma_clk = clk_get(NULL, "sdma_ahb_clk");
+ ipu_clk = clk_get(NULL, "ipu_clk");
+ if (cpu_is_mx31()) {
+ mpeg_clk = clk_get(NULL, "mpeg4_clk");
+ mbx_clk = clk_get(NULL, "mbx_clk");
+ } else {
+ vpu_clk = clk_get(NULL, "vpu_clk");
+ }
+ usb_clk = clk_get(NULL, "usb_ahb_clk");
+ rtic_clk = clk_get(NULL, "rtic_clk");
+ nfc_clk = clk_get(NULL, "nfc_clk");
+ emi_clk = clk_get(NULL, "emi_clk");
+ }
+
+ if ((clk_get_usecount(sdma_clk) == 0)
+ && (clk_get_usecount(ipu_clk) <= 1)
+ && (clk_get_usecount(usb_clk) == 0)
+ && (clk_get_usecount(rtic_clk) == 0)
+ && (clk_get_usecount(mpeg_clk) == 0)
+ && (clk_get_usecount(mbx_clk) == 0)
+ && (clk_get_usecount(nfc_clk) == 0)
+ && (clk_get_usecount(vpu_clk) == 0)) {
+ emi_gated_off = 1;
+ clk_disable(emi_clk);
+ }
+
+ cpu_do_idle();
+ if (emi_gated_off == 1) {
+ clk_enable(emi_clk);
+ }
+ }
+}
+
+/*
+ * This function resets the system. It is called by machine_restart().
+ *
+ * @param mode indicates different kinds of resets
+ */
+void arch_reset(char mode)
+{
+ /* Assert SRS signal */
+ mxc_wd_reset();
+}
diff --git a/arch/arm/mach-mx3/usb.h b/arch/arm/mach-mx3/usb.h
new file mode 100644
index 000000000000..5a7170367773
--- /dev/null
+++ b/arch/arm/mach-mx3/usb.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+extern int usbotg_init(struct platform_device *pdev);
+extern void usbotg_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbotg_fs_active(void);
+extern void gpio_usbotg_fs_inactive(void);
+extern int gpio_usbotg_hs_active(void);
+extern void gpio_usbotg_hs_inactive(void);
+extern struct platform_device *host_pdev_register(struct resource *res,
+ int n_res, struct fsl_usb2_platform_data *config);
+
+extern int fsl_usb_host_init(struct platform_device *pdev);
+extern void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbh1_active(void);
+extern void gpio_usbh1_inactive(void);
+extern int gpio_usbh2_active(void);
+extern void gpio_usbh2_inactive(void);
+
+/*
+ * Determine which platform_data struct to use for the DR controller,
+ * based on which transceiver is configured.
+ * PDATA is a pointer to it.
+ */
+#if defined(CONFIG_ISP1504_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_1504_config;
+#define PDATA (&dr_1504_config)
+#elif defined(CONFIG_ISP1301_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_1301_config;
+#define PDATA (&dr_1301_config)
+#elif defined(CONFIG_MC13783_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_13783_config;
+#define PDATA (&dr_13783_config)
+#endif
+
+
+/*
+ * Used to set pdata->operating_mode before registering the platform_device.
+ * If OTG is configured, the controller operates in OTG mode,
+ * otherwise it's either host or device.
+ */
+#ifdef CONFIG_USB_OTG
+#define DR_UDC_MODE FSL_USB2_DR_OTG
+#define DR_HOST_MODE FSL_USB2_DR_OTG
+#else
+#define DR_UDC_MODE FSL_USB2_DR_DEVICE
+#define DR_HOST_MODE FSL_USB2_DR_HOST
+#endif
+
+
+#ifdef CONFIG_USB_EHCI_ARC_OTG
+static inline void dr_register_host(struct resource *r, int rs)
+{
+ PDATA->operating_mode = DR_HOST_MODE;
+ host_pdev_register(r, rs, PDATA);
+}
+#else
+static inline void dr_register_host(struct resource *r, int rs)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_GADGET_ARC
+static struct platform_device dr_udc_device;
+
+static inline void dr_register_udc(void)
+{
+ PDATA->operating_mode = DR_UDC_MODE;
+ dr_udc_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_udc_device))
+ printk(KERN_ERR "usb: can't register DR gadget\n");
+ else
+ printk(KERN_INFO "usb: DR gadget (%s) registered\n",
+ PDATA->transceiver);
+}
+#else
+static inline void dr_register_udc(void)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_OTG
+static struct platform_device dr_otg_device;
+
+/*
+ * set the proper operating_mode and
+ * platform_data pointer, then register the
+ * device.
+ */
+static inline void dr_register_otg(void)
+{
+ PDATA->operating_mode = FSL_USB2_DR_OTG;
+ dr_otg_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_otg_device))
+ printk(KERN_ERR "usb: can't register otg device\n");
+ else
+ printk(KERN_INFO "usb: DR OTG registered\n");
+}
+#else
+static inline void dr_register_otg(void)
+{
+}
+#endif
diff --git a/arch/arm/mach-mx3/usb_dr.c b/arch/arm/mach-mx3/usb_dr.c
new file mode 100644
index 000000000000..b37ecd6eab56
--- /dev/null
+++ b/arch/arm/mach-mx3/usb_dr.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+/*
+ * platform data structs
+ * - Which one to use is determined by CONFIG options in usb.h
+ * - operating_mode plugged at run time
+ */
+static struct fsl_usb2_platform_data __maybe_unused dr_13783_config = {
+ .name = "DR",
+ .platform_init = usbotg_init,
+ .platform_uninit = usbotg_uninit,
+ .phy_mode = FSL_USB2_PHY_SERIAL,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbotg_fs_active,
+ .gpio_usb_inactive = gpio_usbotg_fs_inactive,
+ .transceiver = "mc13783",
+};
+
+static struct fsl_usb2_platform_data __maybe_unused dr_1301_config = {
+ .name = "DR",
+ .platform_init = usbotg_init,
+ .platform_uninit = usbotg_uninit,
+ .phy_mode = FSL_USB2_PHY_SERIAL,
+ .power_budget = 150, /* 150 mA max power */
+ .gpio_usb_active = gpio_usbotg_fs_active,
+ .gpio_usb_inactive = gpio_usbotg_fs_inactive,
+ .transceiver = "isp1301",
+};
+
+static struct fsl_usb2_platform_data __maybe_unused dr_1504_config = {
+ .name = "DR",
+ .platform_init = usbotg_init,
+ .platform_uninit = usbotg_uninit,
+ .phy_mode = FSL_USB2_PHY_ULPI,
+ .power_budget = 150, /* 150 mA max power */
+ .gpio_usb_active = gpio_usbotg_hs_active,
+ .gpio_usb_inactive = gpio_usbotg_hs_inactive,
+ .transceiver = "isp1504",
+};
+
+
+/*
+ * resources
+ */
+static struct resource otg_resources[] = {
+ [0] = {
+ .start = (u32)(USB_OTGREGS_BASE),
+ .end = (u32)(USB_OTGREGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+static u64 dr_udc_dmamask = ~(u32) 0;
+static void dr_udc_release(struct device *dev)
+{
+}
+
+static u64 dr_otg_dmamask = ~(u32) 0;
+static void dr_otg_release(struct device *dev)
+{
+}
+
+/*
+ * platform device structs
+ * dev.platform_data field plugged at run time
+ */
+static struct platform_device __maybe_unused dr_udc_device = {
+ .name = "fsl-usb2-udc",
+ .id = -1,
+ .dev = {
+ .release = dr_udc_release,
+ .dma_mask = &dr_udc_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static struct platform_device __maybe_unused dr_otg_device = {
+ .name = "fsl-usb2-otg",
+ .id = -1,
+ .dev = {
+ .release = dr_otg_release,
+ .dma_mask = &dr_otg_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static int __init usb_dr_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ dr_register_otg();
+ dr_register_host(otg_resources, ARRAY_SIZE(otg_resources));
+ dr_register_udc();
+#ifdef CONFIG_USB_GADGET_WAKE_UP
+ /* set udc may and should wakeup */
+ device_init_wakeup(&(dr_udc_device.dev), 1);
+#endif
+ return 0;
+}
+
+module_init(usb_dr_init);
diff --git a/arch/arm/mach-mx3/usb_h1.c b/arch/arm/mach-mx3/usb_h1.c
new file mode 100644
index 000000000000..5c6c9c59fb3b
--- /dev/null
+++ b/arch/arm/mach-mx3/usb_h1.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+static struct fsl_usb2_platform_data usbh1_config = {
+ .name = "Host 1",
+ .platform_init = fsl_usb_host_init,
+ .platform_uninit = fsl_usb_host_uninit,
+ .operating_mode = FSL_USB2_MPH_HOST,
+ .phy_mode = FSL_USB2_PHY_SERIAL,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbh1_active,
+ .gpio_usb_inactive = gpio_usbh1_inactive,
+ .transceiver = "serial",
+};
+
+static struct resource usbh1_resources[] = {
+ [0] = {
+ .start = (u32) (USB_H1REGS_BASE),
+ .end = (u32) (USB_H1REGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int __init usbh1_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ host_pdev_register(usbh1_resources, ARRAY_SIZE(usbh1_resources),
+ &usbh1_config);
+ return 0;
+}
+module_init(usbh1_init);
diff --git a/arch/arm/mach-mx3/usb_h2.c b/arch/arm/mach-mx3/usb_h2.c
new file mode 100644
index 000000000000..d47f26b18a93
--- /dev/null
+++ b/arch/arm/mach-mx3/usb_h2.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/mach-types.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <linux/regulator/consumer.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+static struct fsl_usb2_platform_data usbh2_config = {
+ .name = "Host 2",
+ .platform_init = fsl_usb_host_init,
+ .platform_uninit = fsl_usb_host_uninit,
+ .operating_mode = FSL_USB2_MPH_HOST,
+ .phy_mode = FSL_USB2_PHY_ULPI,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbh2_active,
+ .gpio_usb_inactive = gpio_usbh2_inactive,
+ .transceiver = "isp1504",
+};
+
+static struct resource usbh2_resources[] = {
+ [0] = {
+ .start = (u32) (USB_H2REGS_BASE),
+ .end = (u32) (USB_H2REGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int __init usbh2_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ if (machine_is_mx31_3ds()) {
+ struct regulator *usbh2_regux;
+ usbh2_config.xcvr_pwr =
+ kmalloc(sizeof(struct fsl_xcvr_power), GFP_KERNEL);
+ if (!(usbh2_config.xcvr_pwr))
+ return -ENOMEM;
+
+ usbh2_regux = regulator_get(NULL, "GPO1");
+ usbh2_config.xcvr_pwr->regu1 = usbh2_regux;
+ usbh2_regux = regulator_get(NULL, "GPO3");
+ usbh2_config.xcvr_pwr->regu2 = usbh2_regux;
+ }
+
+ host_pdev_register(usbh2_resources, ARRAY_SIZE(usbh2_resources),
+ &usbh2_config);
+ return 0;
+}
+module_init(usbh2_init);
diff --git a/arch/arm/mach-mx35/Kconfig b/arch/arm/mach-mx35/Kconfig
new file mode 100644
index 000000000000..6fdc1dd45919
--- /dev/null
+++ b/arch/arm/mach-mx35/Kconfig
@@ -0,0 +1,111 @@
+menu "MX35 Options"
+ depends on ARCH_MX35
+
+config FORCE_MAX_ZONEORDER
+ int "MAX_ORDER"
+ default "13"
+
+config MX35_OPTIONS
+ bool
+ default y
+ select CPU_V6
+ select ARM_ERRATA_364296
+ select ARM_ERRATA_411920
+ select CACHE_L2X0
+ select OUTER_CACHE
+ select USB_ARCH_HAS_EHCI
+ select ARCH_HAS_EVTMON
+ select ARCH_HAS_RNGC
+
+config MACH_MX35_3DS
+ bool "Support MX35 3STACK platforms"
+ default y
+ select MXC_PSEUDO_IRQS if MXC_PMIC_MC9SDZ60
+ help
+ Include support for MX35 3STACK platform. This includes specific
+ configurations for the board and its peripherals.
+
+config MACH_MX35EVB
+ bool "Support MX35EVB platforms"
+ default n
+ help
+ Include support for MX35EVB platform. This includes specific
+ configurations for the board and its peripherals.
+
+config MX35_DOZE_DURING_IDLE
+ bool "Enter Doze mode during idle"
+ help
+ Turning on this option will put the CPU into Doze mode during idle.
+ The default is to enter Wait mode during idle. Doze mode during
+ idle will save additional power over Wait mode.
+
+config MXC_SDMA_API
+ bool "Use SDMA API"
+ default y
+ help
+ This selects the Freescale MXC SDMA API.
+ If unsure, say N.
+
+menu "SDMA options"
+ depends on MXC_SDMA_API
+
+config SDMA_IRAM
+ bool "Use Internal RAM for SDMA transfer"
+ default n
+ help
+ Support Internal RAM as SDMA buffer or control structures
+
+config SDMA_IRAM_SIZE
+ hex "Reserved bytes of IRAM for SDMA (0x800-0x1000)"
+ range 0x800 0x1000
+ depends on SDMA_IRAM
+ default "0x1000"
+ help
+ Set the size of IRAM for SDMA. It must be a multiple of 512bytes.
+endmenu
+
+config ARCH_MXC_HAS_NFC_V2
+ bool "MXC NFC Hardware Version 2"
+ depends on ARCH_MX35
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 3
+ If unsure, say N.
+
+config ARCH_MXC_HAS_NFC_V2_1
+ bool "MXC NFC Hardware Version 2.1"
+ depends on ARCH_MXC_HAS_NFC_V2
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 2.1
+ If unsure, say N.
+
+menu "Device options"
+
+config I2C_MXC_SELECT1
+ bool "Enable I2C1 module"
+ default y
+ depends on I2C_MXC
+ help
+ Enable MX35 I2C1 module.
+
+config I2C_MXC_SELECT2
+ bool "Enable I2C2 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX35 I2C2 module.
+
+config I2C_MXC_SELECT3
+ bool "Enable I2C3 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX35 I2C3 module.
+
+endmenu
+
+config MXC_PSEUDO_IRQS
+ bool
+
+endmenu
diff --git a/arch/arm/mach-mx35/Makefile b/arch/arm/mach-mx35/Makefile
new file mode 100644
index 000000000000..6029bf200a3f
--- /dev/null
+++ b/arch/arm/mach-mx35/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-y := system.o iomux.o cpu.o mm.o clock.o devices.o serial.o
+obj-$(CONFIG_MXC_SDMA_API) += dma.o
+obj-$(CONFIG_MACH_MX35_3DS) += mx35_3stack.o mx35_3stack_gpio.o mx35_3stack_cpld.o dvfs.o mx35_3stack_pmic_mc13892.o mx35_3stack_pmic_mc9s08dz60.o
+obj-$(CONFIG_MACH_MX35EVB) += mx35evb.o mx35evb_cpld.o mx35evb_gpio.o
+
+obj-$(CONFIG_MXC_PSEUDO_IRQS) += mx35_3stack_irq.o
+obj-$(CONFIG_PM) += pm.o
+
+obj-$(CONFIG_USB_EHCI_ARC_H2) += usb_h2.o
+
+ifneq ($(strip $(CONFIG_USB_GADGET_ARC) $(CONFIG_USB_EHCI_ARC_OTG)),)
+ obj-y += usb_dr.o
+endif
+
diff --git a/arch/arm/mach-mx35/Makefile.boot b/arch/arm/mach-mx35/Makefile.boot
new file mode 100644
index 000000000000..198d92d5e463
--- /dev/null
+++ b/arch/arm/mach-mx35/Makefile.boot
@@ -0,0 +1,9 @@
+ifeq ($(CONFIG_MACH_MX35EVB), y)
+ zreladdr-y := 0x90008000
+params_phys-y := 0x90000100
+initrd_phys-y := 0x90800000
+else
+ zreladdr-y := 0x80008000
+params_phys-y := 0x80000100
+initrd_phys-y := 0x80800000
+endif
diff --git a/arch/arm/mach-mx35/board-mx35_3stack.h b/arch/arm/mach-mx35/board-mx35_3stack.h
new file mode 100644
index 000000000000..ea3eace64ae7
--- /dev/null
+++ b/arch/arm/mach-mx35/board-mx35_3stack.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_BOARD_MX35_3STACK_H__
+#define __ASM_ARCH_MXC_BOARD_MX35_3STACK_H__
+
+#ifdef CONFIG_MACH_MX35_3DS
+
+/*!
+ * @defgroup BRDCFG_MX35 Board Configuration Options
+ * @ingroup MSL_MX35
+ */
+
+/*!
+ * @file mach-mx35/board-mx35_3stack.h
+ *
+ * @brief This file contains all the board level configuration options.
+ *
+ * It currently hold the options defined for MX35 3STACK Platform.
+ *
+ * @ingroup BRDCFG_MX35
+ */
+
+/*
+ * Include Files
+ */
+#include <mach/mxc_uart.h>
+
+/*!
+ * @name MXC UART EVB board level configurations
+ */
+/*! @{ */
+/*!
+ * Specifies if the Irda transmit path is inverting
+ */
+#define MXC_IRDA_TX_INV 0
+/*!
+ * Specifies if the Irda receive path is inverting
+ */
+#define MXC_IRDA_RX_INV 0
+
+/* UART 1 configuration */
+/*!
+ * This define specifies if the UART port is configured to be in DTE or
+ * DCE mode. There exists a define like this for each UART port. Valid
+ * values that can be used are \b MODE_DTE or \b MODE_DCE.
+ */
+#define UART1_MODE MODE_DCE
+/*!
+ * This define specifies if the UART is to be used for IRDA. There exists a
+ * define like this for each UART port. Valid values that can be used are
+ * \b IRDA or \b NO_IRDA.
+ */
+#define UART1_IR NO_IRDA
+/*!
+ * This define is used to enable or disable a particular UART port. If
+ * disabled, the UART will not be registered in the file system and the user
+ * will not be able to access it. There exists a define like this for each UART
+ * port. Specify a value of 1 to enable the UART and 0 to disable it.
+ */
+#define UART1_ENABLED 1
+/*! @} */
+/* UART 2 configuration */
+#define UART2_MODE MODE_DTE
+#define UART2_IR NO_IRDA
+#define UART2_ENABLED 1
+
+/* UART 3 configuration */
+#define UART3_MODE MODE_DTE
+#define UART3_IR NO_IRDA
+#define UART3_ENABLED 1
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
+
+#define MXC_PSEUDO_PARENT MXC_INT_RESV0
+
+enum {
+ MCU_INT_HEADPHONE = 0,
+ MCU_INT_GPS,
+ MCU_INT_SD1_CD,
+ MCU_INT_SD1_WP,
+ MCU_INT_SD2_CD,
+ MCU_INT_SD2_WP,
+ MCU_INT_POWER_KEY,
+ MCU_INT_RTC,
+ MCU_INT_TS_ADC,
+ MCU_INT_KEYPAD,
+};
+
+#define MXC_PSEUDO_IRQ_HEADPHONE (MXC_PSEUDO_IO_BASE + MCU_INT_HEADPHONE)
+#define MXC_PSEUDO_IRQ_GPS (MXC_PSEUDO_IO_BASE + MCU_INT_GPS)
+#define MXC_PSEUDO_IRQ_SD1_CD (MXC_PSEUDO_IO_BASE + MCU_INT_SD1_CD)
+#define MXC_PSEUDO_IRQ_SD1_WP (MXC_PSEUDO_IO_BASE + MCU_INT_SD1_WP)
+#define MXC_PSEUDO_IRQ_SD2_CD (MXC_PSEUDO_IO_BASE + MCU_INT_SD2_CD)
+#define MXC_PSEUDO_IRQ_SD2_WP (MXC_PSEUDO_IO_BASE + MCU_INT_SD2_WP)
+#define MXC_PSEUDO_IRQ_POWER_KEY (MXC_PSEUDO_IO_BASE + MCU_INT_POWER_KEY)
+#define MXC_PSEUDO_IRQ_KEYPAD (MXC_PSEUDO_IO_BASE + MCU_INT_KEYPAD)
+#define MXC_PSEUDO_IRQ_RTC (MXC_PSEUDO_IO_BASE + MCU_INT_RTC)
+#define MXC_PSEUDO_IRQ_TS_ADC (MXC_PSEUDO_IO_BASE + MCU_INT_TS_ADC)
+
+/*!
+ * @name debug board parameters
+ */
+/*! @{ */
+/*!
+ * Base address of debug board
+ */
+#define DEBUG_BASE_ADDRESS CS5_BASE_ADDR
+
+/* External ethernet LAN9217 base address */
+#define LAN9217_BASE_ADDR DEBUG_BASE_ADDRESS
+
+/* External UART */
+#define UARTA_BASE_ADDR (DEBUG_BASE_ADDRESS + 0x08000)
+#define UARTB_BASE_ADDR (DEBUG_BASE_ADDRESS + 0x10000)
+
+#define BOARD_IO_ADDR (DEBUG_BASE_ADDRESS + 0x20000)
+
+/* LED switchs */
+#define LED_SWITCH_REG 0x00
+/* buttons */
+#define SWITCH_BUTTON_REG 0x08
+/* status, interrupt */
+#define INTR_STATUS_REG 0x10
+#define INTR_RESET_REG 0x20
+/*CPLD configuration*/
+#define CONFIG1_REG 0x28
+#define CONFIG2_REG 0x30
+/*interrupt mask */
+#define INTR_MASK_REG 0x38
+
+/* magic word for debug CPLD */
+#define MAGIC_NUMBER1_REG 0x40
+#define MAGIC_NUMBER2_REG 0x48
+/* CPLD code version */
+#define CPLD_CODE_VER_REG 0x50
+/* magic word for debug CPLD */
+#define MAGIC3_NUMBER3_REG 0x58
+/* module reset register*/
+#define CONTROL_REG 0x60
+/* CPU ID and Personality ID*/
+#define IDENT_REG 0x68
+
+/* For interrupts like xuart, enet etc */
+#define EXPIO_PARENT_INT MX35_PIN_GPIO1_1
+
+#define EXPIO_INT_ENET_INT (MXC_EXP_IO_BASE + 0)
+#define EXPIO_INT_XUARTA_INT (MXC_EXP_IO_BASE + 1)
+#define EXPIO_INT_XUARTB_INT (MXC_EXP_IO_BASE + 2)
+#define EXPIO_INT_BUTTONA_INT (MXC_EXP_IO_BASE + 3)
+#define EXPIO_INT_BUTTONB_INT (MXC_EXP_IO_BASE + 4)
+
+/*! This is System IRQ used by LAN9217 for interrupt generation taken
+ * from platform.h
+ */
+#define LAN9217_IRQ EXPIO_INT_ENET_INT
+
+/*! This is base virtual address of debug board*/
+extern unsigned int mx35_3stack_board_io;
+
+#define MXC_BD_LED1 (1)
+#define MXC_BD_LED2 (1 << 1)
+#define MXC_BD_LED3 (1 << 2)
+#define MXC_BD_LED4 (1 << 3)
+#define MXC_BD_LED5 (1 << 4)
+#define MXC_BD_LED6 (1 << 5)
+#define MXC_BD_LED7 (1 << 6)
+#define MXC_BD_LED8 (1 << 7)
+#define MXC_BD_LED_ON(led)
+#define MXC_BD_LED_OFF(led)
+
+/*! @} */
+
+#define AHB_FREQ 133000000
+#define IPG_FREQ 66500000
+
+extern void mx35_3stack_gpio_init(void) __init;
+extern void gpio_tsc_active(void);
+extern void gpio_tsc_inactive(void);
+extern unsigned int sdhc_get_card_det_status(struct device *dev);
+extern int sdhc_write_protect(struct device *dev);
+extern void gpio_can_active(int id);
+extern void gpio_can_inactive(int id);
+extern struct flexcan_platform_data flexcan_data[];
+extern int __init mx35_3stack_init_mc13892(void);
+extern int __init mx35_3stack_init_mc9s08dz60(void);
+
+#endif /* CONFIG_MACH_MX35_3DS */
+#endif /* __ASM_ARCH_MXC_BOARD_MX35_3STACK_H__ */
diff --git a/arch/arm/mach-mx35/board-mx35evb.h b/arch/arm/mach-mx35/board-mx35evb.h
new file mode 100644
index 000000000000..3eb124c1b1ff
--- /dev/null
+++ b/arch/arm/mach-mx35/board-mx35evb.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_BOARD_MX35EVB_H__
+#define __ASM_ARCH_MXC_BOARD_MX35EVB_H__
+
+#ifdef CONFIG_MACH_MX35EVB
+/*!
+ * @defgroup BRDCFG_MX35 Board Configuration Options
+ * @ingroup MSL_MX35
+ */
+
+/*!
+ * @file mach-mx35/board-mx35evb.h
+ *
+ * @brief This file contains all the board level configuration options.
+ *
+ * It currently hold the options defined for MX35 EVB Platform.
+ *
+ * @ingroup BRDCFG_MX35
+ */
+
+/*
+ * Include Files
+ */
+#include <mach/mxc_uart.h>
+
+/*!
+ * @name MXC UART EVB board level configurations
+ */
+/*! @{ */
+/*!
+ * Specifies if the Irda transmit path is inverting
+ */
+#define MXC_IRDA_TX_INV 0
+/*!
+ * Specifies if the Irda receive path is inverting
+ */
+#define MXC_IRDA_RX_INV 0
+
+/* UART 1 configuration */
+/*!
+ * This define specifies if the UART port is configured to be in DTE or
+ * DCE mode. There exists a define like this for each UART port. Valid
+ * values that can be used are \b MODE_DTE or \b MODE_DCE.
+ */
+#define UART1_MODE MODE_DCE
+/*!
+ * This define specifies if the UART is to be used for IRDA. There exists a
+ * define like this for each UART port. Valid values that can be used are
+ * \b IRDA or \b NO_IRDA.
+ */
+#define UART1_IR NO_IRDA
+/*!
+ * This define is used to enable or disable a particular UART port. If
+ * disabled, the UART will not be registered in the file system and the user
+ * will not be able to access it. There exists a define like this for each UART
+ * port. Specify a value of 1 to enable the UART and 0 to disable it.
+ */
+#define UART1_ENABLED 1
+/*! @} */
+/* UART 2 configuration */
+#define UART2_MODE MODE_DCE
+#define UART2_IR NO_IRDA
+#define UART2_ENABLED 1
+
+/* UART 3 configuration */
+#define UART3_MODE MODE_DTE
+#define UART3_IR NO_IRDA
+#define UART3_ENABLED 1
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
+
+/*!
+ * @name PBC Controller parameters
+ */
+/*!
+ * Base address of PBC controller
+ */
+/*! @{ */
+#define PBC_BASE_ADDRESS CS4_BASE_ADDR
+
+#define PBC_VERSION 0x0000
+#define PBC_BCTRL1_SET 0x0008
+#define PBC_BCTRL1_CLR 0x000C
+#define PBC_BCTRL2_SET 0x0010
+#define PBC_BCTRL2_CLR 0x0014
+#define PBC_IMR1_SET 0x0018
+#define PBC_IMR1_CLR 0x001C
+#define PBC_IMR2_SET 0x0020
+#define PBC_IMR2_CLR 0x0024
+#define PBC_BSTAT1 0x0028
+#define PBC_ISR 0x002C
+
+#define ENET_BASE_ADDRESS (PBC_BASE_ADDRESS + 0x20000)
+
+/*! Definitions in Board Control Register 1 */
+#define PBC_BCTRL1_ENET_RESET_B (1)
+#define PBC_BCTRL1_FEC_RESET_B (1<<2)
+
+/*! @} */
+
+#define EXPIO_PARENT_INT_0 IOMUX_TO_IRQ(MX35_PIN_GPIO1_0)
+#define EXPIO_PARENT_INT_1 IOMUX_TO_IRQ(MX35_PIN_GPIO1_1)
+
+#define EXPIO_PARENT_INT EXPIO_PARENT_INT_1
+#define EXPIO_PARENT_INT_PIN MX35_PIN_GPIO1_1
+
+#define EXPIO_INT_FEC (EXPIO_PARENT_INT + 0)
+#define EXPIO_INT_ENET (EXPIO_PARENT_INT + 1)
+
+extern unsigned int mx35evb_board_io;
+
+#define MXC_BD_LED1 (1)
+#define MXC_BD_LED2 (1 << 1)
+#define MXC_BD_LED3 (1 << 2)
+#define MXC_BD_LED4 (1 << 3)
+#define MXC_BD_LED_ON(led) \
+ __raw_writew(led, mx35evb_bard_io + PBC_BCTRL2_SET)
+#define MXC_BD_LED_OFF(led) \
+ __raw_writew(led, mx35evb_bard_io + PBC_BCTRL2_CLR)
+
+#define AHB_FREQ 133000000
+#define IPG_FREQ 66500000
+/*!
+ * Specifies if the MXC Write Protectect function is suppport or not.
+ */
+#define MXC_MMC_WRITE_PROTECT 1
+
+extern void mx35evb_gpio_init(void) __init;
+
+#endif /* CONFIG_MACH_MX35EVB */
+#endif /* __ASM_ARCH_MXC_BOARD_MX35EVB_H__ */
diff --git a/arch/arm/mach-mx35/clock.c b/arch/arm/mach-mx35/clock.c
new file mode 100644
index 000000000000..f54021cfd95b
--- /dev/null
+++ b/arch/arm/mach-mx35/clock.c
@@ -0,0 +1,1927 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/clock.h>
+#include <asm/div64.h>
+
+#include "crm_regs.h"
+
+#define PRE_DIV_MIN_FREQ 10000000 /* Minimum Frequency after Predivider */
+#define PROPAGATE_RATE_DIS 2
+
+struct timer_list dptcen_timer;
+static int cpu_curr_wp;
+static struct cpu_wp *cpu_wp_tbl;
+static int cpu_wp_nr;
+static int cpu_wp_offset;
+
+static struct clk mcu_pll_clk;
+static struct clk peri_pll_clk;
+static struct clk ipg_clk;
+static struct clk ckih_clk;
+static struct clk ckie_clk;
+static struct clk ahb_clk;
+static struct clk cpu_clk;
+
+#define CLK_CODE(arm, ahb, sel) (((arm) << 16) + ((ahb) << 8) + (sel))
+#define CLK_CODE_ARM(c) (((c) >> 16) & 0xFF)
+#define CLK_CODE_AHB(c) (((c) >> 8) & 0xFF)
+#define CLK_CODE_PATH(c) ((c) & 0xFF)
+
+static int __get_arm_div(unsigned long pdr0, int *fi, int *fd);
+
+static int g_clk_mux_auto[8] = {
+ CLK_CODE(1, 3, 0), CLK_CODE(1, 2, 1), CLK_CODE(2, 1, 1), -1,
+ CLK_CODE(1, 6, 0), CLK_CODE(1, 4, 1), CLK_CODE(2, 2, 1), -1,
+};
+
+static int g_clk_mux_consumer[16] = {
+ CLK_CODE(1, 4, 0), CLK_CODE(1, 3, 1), CLK_CODE(2, 2, 0), -1,
+ -1, -1, CLK_CODE(4, 1, 0), CLK_CODE(1, 5, 0),
+ CLK_CODE(1, 8, 0), CLK_CODE(1, 6, 1), CLK_CODE(2, 4, 0), -1,
+ -1, -1, CLK_CODE(4, 2, 0), -1,
+};
+
+static int g_hsp_div_table[3][16] = {
+ {4, 3, 2, -1, -1, -1, 1, 5, 4, 3, 2, -1, -1, -1, 1, -1},
+ {-1, -1, -1, -1, -1, -1, -1, -1, 8, 6, 4, -1, -1, -1, 2, -1},
+ {3, -1, -1, -1, -1, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1},
+};
+
+static void __calc_dividers(u32 div, u32 *pre, u32 *post, u32 base)
+{
+ u32 min_pre, temp_pre, old_err, err;
+ min_pre = (div - 1) / base + 1;
+ old_err = 8;
+ for (temp_pre = 8; temp_pre >= min_pre; temp_pre--) {
+ if (div > (temp_pre * base))
+ break;
+ if (div < (temp_pre * temp_pre))
+ continue;
+ err = div % temp_pre;
+ if (err == 0) {
+ *pre = temp_pre;
+ break;
+ }
+ err = temp_pre - err;
+ if (err < old_err) {
+ old_err = err;
+ *pre = temp_pre;
+ }
+ }
+ *post = (div + *pre - 1) / *pre;
+}
+
+static void __calc_pre_post_dividers(u32 div, u32 *pre, u32 *post)
+{
+ if (div >= 512) {
+ *pre = 8;
+ *post = 64;
+ } else if (div >= 64) {
+ __calc_dividers(div, pre, post, 64);
+ } else if (div <= 8) {
+ *pre = div;
+ *post = 1;
+ } else {
+ *pre = 1;
+ *post = div;
+ }
+}
+
+static void __calc_two_dividers(u32 div, u32 *pre, u32 *post)
+{
+ if (div >= 64) {
+ *pre = *post = 8;
+ } else if (div > 8) {
+ __calc_dividers(div, pre, post, 8);
+ } else {
+ *pre = 1;
+ *post = div;
+ }
+}
+
+static unsigned long _clk_per_post_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 pre, post;
+ u32 div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ __calc_pre_post_dividers(div, &pre, &post);
+
+ return clk->parent->rate / (pre * post);
+}
+
+static unsigned long _clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ u32 pre, post;
+ u32 div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ __calc_two_dividers(div, &pre, &post);
+ return clk->parent->rate / (pre * post);
+ } else
+ return clk->parent->rate / div;
+}
+
+static int __switch_cpu_wp(struct clk *clk, unsigned long rate)
+{
+ int i;
+ u32 reg_value;
+ if (cpu_wp_tbl[cpu_curr_wp].cpu_rate < rate) {
+ for (i = cpu_curr_wp + 2; i < cpu_wp_nr; i += 2) {
+ if (rate == cpu_wp_tbl[i].cpu_rate)
+ goto found;
+ }
+ return -EINVAL;
+ } else {
+ for (i = cpu_curr_wp - 2; i >= 0; i -= 2) {
+ if (rate == cpu_wp_tbl[i].cpu_rate)
+ goto found;
+ }
+ return -EINVAL;
+ }
+ found:
+ reg_value = __raw_readl(MXC_CCM_PDR0);
+ reg_value = (reg_value & ~(MXC_CCM_PDR0_CON_MUX_DIV_MASK |
+ MXC_CCM_PDR0_AUTO_MUX_DIV_MASK)) |
+ cpu_wp_tbl[i].pdr0_reg;
+ __raw_writel(reg_value, MXC_CCM_PDR0);
+
+ if (cpu_wp_tbl[i].pll_rate != cpu_wp_tbl[cpu_curr_wp].pll_rate)
+ clk_set_rate(clk->parent, cpu_wp_tbl[i].pll_rate);
+ cpu_curr_wp = i;
+ clk->rate = rate;
+ return 0;
+}
+
+static int __switch_cpu_rate(struct clk *clk, unsigned long rate)
+{
+ int prev;
+ unsigned long tmp;
+ int arm_div, fi, fd, start, end;
+ u32 reg_value;
+
+ if (cpu_wp_tbl[cpu_curr_wp].cpu_rate < rate) {
+ start = cpu_curr_wp + 2;
+ end = cpu_wp_nr;
+ prev = cpu_curr_wp;
+ } else {
+ start = cpu_wp_offset + 2;
+ end = cpu_curr_wp;
+ prev = cpu_wp_offset;
+ }
+ while (start < end) {
+ arm_div = __get_arm_div(cpu_wp_tbl[start].pdr0_reg, &fi, &fd);
+ tmp = (mcu_pll_clk.rate * fi) / (arm_div * fd);
+ if (tmp == rate) {
+ prev = start;
+ break;
+ }
+ if (tmp < rate) {
+ if (prev < start)
+ prev = start;
+ } else {
+ break;
+ }
+ start += 2;
+ }
+ if (start >= end)
+ return -EINVAL;
+
+ if (prev == cpu_curr_wp)
+ return 0;
+
+ reg_value = __raw_readl(MXC_CCM_PDR0);
+ reg_value = (reg_value & ~(MXC_CCM_PDR0_CON_MUX_DIV_MASK |
+ MXC_CCM_PDR0_AUTO_MUX_DIV_MASK)) |
+ cpu_wp_tbl[prev].pdr0_reg;
+ __raw_writel(reg_value, MXC_CCM_PDR0);
+
+ cpu_curr_wp = prev;
+ clk->rate = rate;
+ return 0;
+}
+
+static int __get_arm_div(unsigned long pdr0, int *fi, int *fd)
+{
+ int *pclk_mux;
+ if ((pdr0 & MXC_CCM_PDR0_AUTO_CON)
+ || (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1))
+ pclk_mux =
+ g_clk_mux_consumer +
+ ((pdr0 & MXC_CCM_PDR0_CON_MUX_DIV_MASK) >>
+ MXC_CCM_PDR0_CON_MUX_DIV_OFFSET);
+ else {
+ pclk_mux = g_clk_mux_auto +
+ ((pdr0 & MXC_CCM_PDR0_AUTO_MUX_DIV_MASK) >>
+ MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET);
+ }
+
+ if ((*pclk_mux) == -1) {
+ BUG();
+ return -EINVAL;
+ }
+
+ if (fi && fd) {
+ if (!CLK_CODE_PATH(*pclk_mux)) {
+ *fi = *fd = 1;
+ return CLK_CODE_ARM(*pclk_mux);
+ }
+ if ((pdr0 & MXC_CCM_PDR0_AUTO_CON)
+ || (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) {
+ *fi = 3;
+ *fd = 4;
+ } else {
+ *fi = 2;
+ *fd = 3;
+ }
+ }
+ return CLK_CODE_ARM(*pclk_mux);
+}
+
+static int __get_ahb_div(unsigned long pdr0)
+{
+ int *pclk_mux;
+ if ((pdr0 & MXC_CCM_PDR0_AUTO_CON)
+ || (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) {
+ pclk_mux =
+ g_clk_mux_consumer +
+ ((pdr0 & MXC_CCM_PDR0_CON_MUX_DIV_MASK) >>
+ MXC_CCM_PDR0_CON_MUX_DIV_OFFSET);
+ } else {
+ pclk_mux = g_clk_mux_auto +
+ ((pdr0 & MXC_CCM_PDR0_AUTO_MUX_DIV_MASK) >>
+ MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET);
+ }
+
+ if ((*pclk_mux) == -1) {
+ BUG();
+ return -EINVAL;
+ }
+ return CLK_CODE_AHB(*pclk_mux);
+}
+
+static void sync_cpu_wb(void)
+{
+ int i;
+ struct cpu_wp *p;
+ unsigned long reg = __raw_readl(MXC_CCM_PDR0);
+ if ((reg & MXC_CCM_PDR0_AUTO_CON)
+ || (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) {
+ reg &= MXC_CCM_PDR0_CON_MUX_DIV_MASK;
+ } else {
+ reg &= MXC_CCM_PDR0_AUTO_MUX_DIV_MASK;
+ }
+ for (i = 0; i < cpu_wp_nr; i++) {
+ p = cpu_wp_tbl + cpu_curr_wp;
+ if (p->pdr0_reg == (reg & 0xF0E00))
+ break;
+ cpu_curr_wp = (cpu_curr_wp + 1) % cpu_wp_nr;
+ }
+ cpu_wp_offset = cpu_curr_wp & 1;
+}
+
+static int _clk_enable(struct clk *clk)
+{
+ u32 reg;
+ reg = __raw_readl(clk->enable_reg);
+ reg |= 3 << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+
+ return 0;
+}
+
+static void _clk_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(3 << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+}
+
+static void _clk_emi_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(3 << clk->enable_shift);
+ reg |= (1 << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+}
+
+static int _clk_asrc_enable(struct clk *clk)
+{
+ u32 reg;
+ reg = __raw_readl(MXC_CCM_COSR);
+ __raw_writel(reg | MXC_CCM_COSR_ASRC_AUDIO_EN, MXC_CCM_COSR);
+ return 0;
+}
+
+static void _clk_asrc_disable(struct clk *clk)
+{
+ u32 reg;
+ reg = __raw_readl(MXC_CCM_COSR);
+ __raw_writel(reg & (~MXC_CCM_COSR_ASRC_AUDIO_EN), MXC_CCM_COSR);
+}
+
+static int _clk_pll_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ signed long pd = 1; /* Pre-divider */
+ signed long mfi; /* Multiplication Factor (Integer part) */
+ signed long mfn; /* Multiplication Factor (Integer part) */
+ signed long mfd; /* Multiplication Factor (Denominator Part) */
+ signed long tmp;
+ u32 ref_freq = clk->parent->rate;
+
+ if ((clk == &mcu_pll_clk)
+ && (clk->parent->rate == cpu_wp_tbl[cpu_curr_wp].pll_rate)) {
+ __raw_writel(cpu_wp_tbl[cpu_curr_wp].pll_reg, MXC_CCM_MPCTL);
+ clk->rate = rate;
+ return 0;
+ }
+
+ while (((ref_freq / pd) * 10) > rate)
+ pd++;
+
+ if ((ref_freq / pd) < PRE_DIV_MIN_FREQ)
+ return -EINVAL;
+
+ /* the ref_freq/2 in the following is to round up */
+ mfi = (((rate / 2) * pd) + (ref_freq / 2)) / ref_freq;
+ if (mfi < 5 || mfi > 15)
+ return -EINVAL;
+
+ /* pick a mfd value that will work
+ * then solve for mfn */
+ mfd = ref_freq / 50000;
+
+ /*
+ * pll_freq * pd * mfd
+ * mfn = -------------------- - (mfi * mfd)
+ * 2 * ref_freq
+ */
+ /* the tmp/2 is for rounding */
+ tmp = ref_freq / 10000;
+ mfn =
+ ((((((rate / 2) + (tmp / 2)) / tmp) * pd) * mfd) / 10000) -
+ (mfi * mfd);
+
+ mfn = mfn & 0x3ff;
+ pd--;
+ mfd--;
+
+ /* Change the Pll value */
+ reg = (mfi << MXC_CCM_PCTL_MFI_OFFSET) |
+ (mfn << MXC_CCM_PCTL_MFN_OFFSET) |
+ (mfd << MXC_CCM_PCTL_MFD_OFFSET) | (pd << MXC_CCM_PCTL_PD_OFFSET);
+
+ if (clk == &mcu_pll_clk)
+ __raw_writel(reg, MXC_CCM_MPCTL);
+ else if (clk == &peri_pll_clk)
+ __raw_writel(reg, MXC_CCM_PPCTL);
+
+ clk->rate = rate;
+ return 0;
+}
+
+static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate)
+{
+ if ((rate < ahb_clk.rate) || (rate % ahb_clk.rate != 0)) {
+ printk(KERN_ERR "Wrong rate %lu in _clk_cpu_set_rate\n", rate);
+ return -EINVAL;
+ }
+
+ if (clk->rate == rate)
+ return 0;
+
+ if (clk->parent->rate == cpu_wp_tbl[cpu_curr_wp].pll_rate)
+ return __switch_cpu_wp(clk, rate);
+ return __switch_cpu_rate(clk, rate);
+}
+
+static void _clk_pll_recalc(struct clk *clk)
+{
+ long mfi, mfn, mfd, pdf, ref_clk, mfn_abs;
+ unsigned long reg = 0;
+ s64 temp;
+
+ ref_clk = ckih_clk.rate;
+
+ if (clk == &mcu_pll_clk)
+ reg = __raw_readl(MXC_CCM_MPCTL);
+ else if (clk == &peri_pll_clk)
+ reg = __raw_readl(MXC_CCM_PPCTL);
+ else
+ BUG();
+
+ pdf = (reg & MXC_CCM_PCTL_PD_MASK) >> MXC_CCM_PCTL_PD_OFFSET;
+ mfd = (reg & MXC_CCM_PCTL_MFD_MASK) >> MXC_CCM_PCTL_MFD_OFFSET;
+ mfi = (reg & MXC_CCM_PCTL_MFI_MASK) >> MXC_CCM_PCTL_MFI_OFFSET;
+ mfi = (mfi <= 5) ? 5 : mfi;
+ mfn = mfn_abs = reg & MXC_CCM_PCTL_MFN_MASK;
+
+ if (mfn >= 0x200) {
+ mfn |= 0xFFFFFE00;
+ mfn_abs = -mfn;
+ }
+
+ ref_clk *= 2;
+ ref_clk /= pdf + 1;
+
+ temp = (u64) ref_clk * mfn_abs;
+ do_div(temp, mfd + 1);
+ if (mfn < 0)
+ temp = -temp;
+ temp = (ref_clk * mfi) + temp;
+
+ clk->rate = temp;
+}
+
+static int _clk_peri_pll_enable(struct clk *clk)
+{
+ u32 reg;
+ reg = __raw_readl(MXC_CCM_CCMR);
+ reg |= MXC_CCM_CCMR_UPE;
+ __raw_writel(reg, MXC_CCM_CCMR);
+
+ /* No lock bit on MX31, so using max time from spec */
+ udelay(80);
+
+ return 0;
+}
+
+static void _clk_peri_pll_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CCMR);
+ reg &= ~MXC_CCM_CCMR_UPE;
+ __raw_writel(reg, MXC_CCM_CCMR);
+}
+
+#define PDR0(mask, off) ((__raw_readl(MXC_CCM_PDR0) & mask) >> off)
+#define PDR1(mask, off) ((__raw_readl(MXC_CCM_PDR1) & mask) >> off)
+#define PDR2(mask, off) ((__raw_readl(MXC_CCM_PDR2) & mask) >> off)
+#define PDR3(mask, off) ((__raw_readl(MXC_CCM_PDR3) & mask) >> off)
+#define PDR4(mask, off) ((__raw_readl(MXC_CCM_PDR4) & mask) >> off)
+
+static void _clk_cpu_recalc(struct clk *clk)
+{
+ unsigned long pdr0 = __raw_readl(MXC_CCM_PDR0);
+ int arm_div, fi, fd;
+ if (clk->parent->rate == cpu_wp_tbl[cpu_curr_wp].pll_rate) {
+ clk->rate = cpu_wp_tbl[cpu_curr_wp].cpu_rate;
+ } else {
+ arm_div = __get_arm_div(pdr0, &fi, &fd);
+ clk->rate = (clk->parent->rate * fi) / (arm_div * fd);
+ }
+}
+
+static void _clk_hclk_recalc(struct clk *clk)
+{
+ unsigned long ahb_div, pdr0 = __raw_readl(MXC_CCM_PDR0);
+ ahb_div = __get_ahb_div(pdr0);
+ clk->rate = clk->parent->rate / ahb_div;
+}
+
+static void _clk_ipg_recalc(struct clk *clk)
+{
+ clk->rate = clk->parent->rate / 2;
+}
+
+static void _clk_nfc_recalc(struct clk *clk)
+{
+ unsigned long nfc_pdf;
+
+ nfc_pdf = PDR4(MXC_CCM_PDR4_NFC_PODF_MASK,
+ MXC_CCM_PDR4_NFC_PODF_OFFSET);
+ clk->rate = clk->parent->rate / (nfc_pdf + 1);
+}
+
+static void _clk_hsp_recalc(struct clk *clk)
+{
+ int hsp_pdf;
+ unsigned long reg;
+ reg = __raw_readl(MXC_CCM_PDR0);
+
+ if ((reg & MXC_CCM_PDR0_AUTO_CON)
+ || (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) {
+ hsp_pdf =
+ (reg & MXC_CCM_PDR0_HSP_PODF_MASK) >>
+ MXC_CCM_PDR0_HSP_PODF_OFFSET;
+ reg =
+ (reg & MXC_CCM_PDR0_CON_MUX_DIV_MASK) >>
+ MXC_CCM_PDR0_CON_MUX_DIV_OFFSET;
+ if (hsp_pdf < 3) {
+ hsp_pdf = g_hsp_div_table[hsp_pdf][reg];
+ if (hsp_pdf > 0)
+ clk->rate = clk->parent->rate / hsp_pdf;
+ }
+ } else {
+ clk->rate = clk->parent->rate;
+ }
+}
+
+static void _clk_mlb_recalc(struct clk *clk)
+{
+ clk->rate = clk->parent->rate * 2;
+}
+
+static void _clk_usb_recalc(struct clk *clk)
+{
+ unsigned long usb_podf, usb_prdf;
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ usb_podf = PDR4(MXC_CCM_PDR4_USB_PODF_MASK,
+ MXC_CCM_PDR4_USB_PODF_OFFSET);
+ usb_prdf = PDR4(MXC_CCM_PDR4_USB_PRDF_MASK,
+ MXC_CCM_PDR4_USB_PRDF_OFFSET);
+ clk->rate =
+ clk->parent->rate / ((usb_prdf + 1) * (usb_podf + 1));
+ } else {
+ usb_podf = PDR4(MXC_CCM_PDR4_USB_PODF_MASK_V2,
+ MXC_CCM_PDR4_USB_PODF_OFFSET);
+ clk->rate = clk->parent->rate / (usb_podf + 1);
+ }
+}
+
+static int _clk_usb_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+ u32 podf, prdf;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ __calc_two_dividers(div, &prdf, &podf);
+ reg = __raw_readl(MXC_CCM_PDR4) &
+ ~(MXC_CCM_PDR4_USB_PODF_MASK | MXC_CCM_PDR4_USB_PRDF_MASK);
+ reg |= (podf - 1) << MXC_CCM_PDR4_USB_PODF_OFFSET;
+ reg |= (prdf - 1) << MXC_CCM_PDR4_USB_PRDF_OFFSET;
+ } else {
+ podf = div - 1;
+ reg =
+ __raw_readl(MXC_CCM_PDR4) & ~MXC_CCM_PDR4_USB_PODF_MASK_V2;
+ reg |= (podf - 1) << MXC_CCM_PDR4_USB_PODF_OFFSET;
+ }
+ __raw_writel(reg, MXC_CCM_PDR4);
+ clk->rate = rate;
+ return 0;
+}
+
+static void _clk_csi_recalc(struct clk *clk)
+{
+ u32 podf, prdf;
+
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ prdf = PDR2(MXC_CCM_PDR2_CSI_PRDF_MASK,
+ MXC_CCM_PDR2_CSI_PRDF_OFFSET);
+ podf =
+ PDR2(MXC_CCM_PDR2_CSI_PODF_MASK,
+ MXC_CCM_PDR2_CSI_PODF_OFFSET);
+ clk->rate = clk->parent->rate / ((prdf + 1) * (podf + 1));
+ } else {
+ podf =
+ PDR2(MXC_CCM_PDR2_CSI_PODF_MASK_V2,
+ MXC_CCM_PDR2_CSI_PODF_OFFSET);
+ clk->rate = clk->parent->rate / (podf + 1);
+ }
+}
+
+static int _clk_csi_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+ u32 prdf, podf;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ __calc_two_dividers(div, &prdf, &podf);
+ reg = __raw_readl(MXC_CCM_PDR2) &
+ ~(MXC_CCM_PDR2_CSI_PRDF_MASK | MXC_CCM_PDR2_CSI_PODF_MASK);
+ reg |= (podf - 1) << MXC_CCM_PDR2_CSI_PODF_OFFSET;
+ reg |= (prdf - 1) << MXC_CCM_PDR2_CSI_PRDF_OFFSET;
+ } else {
+ reg =
+ __raw_readl(MXC_CCM_PDR2) & ~MXC_CCM_PDR2_CSI_PODF_MASK_V2;
+ reg |= (div - 1) << MXC_CCM_PDR2_CSI_PODF_OFFSET;
+ }
+
+ /* Set CSI clock divider */
+ __raw_writel(reg, MXC_CCM_PDR2);
+ clk->rate = rate;
+ return 0;
+}
+
+static int _clk_csi_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+ if (parent == &cpu_clk)
+ reg = __raw_readl(MXC_CCM_PDR2) | MXC_CCM_PDR2_CSI_M_U;
+ else if (parent == &peri_pll_clk)
+ reg = __raw_readl(MXC_CCM_PDR2) & (~MXC_CCM_PDR2_CSI_M_U);
+ else
+ return -EINVAL;
+ __raw_writel(reg, MXC_CCM_PDR2);
+ return 0;
+}
+
+static void _clk_per_recalc(struct clk *clk)
+{
+ u32 podf = 0, prdf = 0;
+
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ if (clk->parent == &cpu_clk) {
+ prdf = PDR4(MXC_CCM_PDR4_PER0_PRDF_MASK,
+ MXC_CCM_PDR4_PER0_PRDF_OFFSET);
+ podf = PDR4(MXC_CCM_PDR4_PER0_PODF_MASK,
+ MXC_CCM_PDR4_PER0_PODF_OFFSET);
+ } else {
+ podf = PDR0(MXC_CCM_PDR0_PER_PODF_MASK,
+ MXC_CCM_PDR0_PER_PODF_OFFSET);
+ }
+ clk->rate = clk->parent->rate / ((podf + 1) * (prdf + 1));
+ } else {
+ if (clk->parent == &ahb_clk)
+ podf = PDR0(MXC_CCM_PDR0_PER_PODF_MASK,
+ MXC_CCM_PDR0_PER_PODF_OFFSET);
+ else if (clk->parent == &cpu_clk) {
+ podf = PDR4(MXC_CCM_PDR4_PER0_PODF_MASK_V2,
+ MXC_CCM_PDR4_PER0_PODF_OFFSET);
+ }
+ clk->rate = clk->parent->rate / (podf + 1);
+ }
+}
+
+static void _clk_uart_per_recalc(struct clk *clk)
+{
+ unsigned long podf, prdf;
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ prdf = PDR4(MXC_CCM_PDR4_UART_PRDF_MASK,
+ MXC_CCM_PDR4_UART_PRDF_OFFSET);
+ podf = PDR4(MXC_CCM_PDR4_UART_PODF_MASK,
+ MXC_CCM_PDR4_UART_PODF_OFFSET);
+ clk->rate = clk->parent->rate / ((prdf + 1) * (podf + 1));
+ } else {
+ podf =
+ PDR4(MXC_CCM_PDR4_UART_PODF_MASK_V2,
+ MXC_CCM_PDR4_UART_PODF_OFFSET);
+ clk->rate = clk->parent->rate / (podf + 1);
+ }
+
+}
+
+static int _clk_uart_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+ u32 prdf, podf;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ /* Set UART clock divider */
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ __calc_two_dividers(div, &prdf, &podf);
+ reg = __raw_readl(MXC_CCM_PDR4) &
+ ~(MXC_CCM_PDR4_UART_PRDF_MASK |
+ MXC_CCM_PDR4_UART_PODF_MASK);
+ reg |= (podf - 1) << MXC_CCM_PDR4_UART_PODF_OFFSET;
+ reg |= (prdf - 1) << MXC_CCM_PDR4_UART_PRDF_OFFSET;
+ } else {
+ reg =
+ __raw_readl(MXC_CCM_PDR4) & ~MXC_CCM_PDR4_UART_PODF_MASK_V2;
+ reg |= (div - 1) << MXC_CCM_PDR4_UART_PODF_OFFSET;
+ }
+ __raw_writel(reg, MXC_CCM_PDR4);
+ clk->rate = rate;
+ return 0;
+}
+
+static void _clk_ssi_recalc(struct clk *clk)
+{
+ unsigned long ssi_pdf, ssi_prepdf;
+
+ if (clk->id == 1) {
+ ssi_pdf = PDR2(MXC_CCM_PDR2_SSI2_PODF_MASK,
+ MXC_CCM_PDR2_SSI2_PODF_OFFSET);
+ ssi_prepdf = PDR2(MXC_CCM_PDR2_SSI2_PRDF_MASK,
+ MXC_CCM_PDR2_SSI2_PRDF_OFFSET);
+ } else {
+ ssi_pdf = PDR2(MXC_CCM_PDR2_SSI1_PODF_MASK,
+ MXC_CCM_PDR2_SSI1_PODF_OFFSET);
+ ssi_prepdf = PDR2(MXC_CCM_PDR2_SSI1_PRDF_MASK,
+ MXC_CCM_PDR2_SSI1_PRDF_OFFSET);
+ }
+ clk->rate = clk->parent->rate / ((ssi_prepdf + 1) * (ssi_pdf + 1));
+}
+
+static int _clk_ssi_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+ u32 pre, post;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ __calc_pre_post_dividers(div, &pre, &post);
+
+ if (clk->id == 1) {
+ reg = __raw_readl(MXC_CCM_PDR2) &
+ ~(MXC_CCM_PDR2_SSI2_PRDF_MASK |
+ MXC_CCM_PDR2_SSI2_PODF_MASK);
+ reg |= (post - 1) << MXC_CCM_PDR2_SSI2_PODF_OFFSET;
+ reg |= (pre - 1) << MXC_CCM_PDR2_SSI2_PRDF_OFFSET;
+ } else {
+ reg = __raw_readl(MXC_CCM_PDR2) &
+ ~(MXC_CCM_PDR2_SSI1_PRDF_MASK |
+ MXC_CCM_PDR2_SSI1_PODF_MASK);
+ reg |= (post - 1) << MXC_CCM_PDR2_SSI1_PODF_OFFSET;
+ reg |= (pre - 1) << MXC_CCM_PDR2_SSI1_PRDF_OFFSET;
+ }
+ __raw_writel(reg, MXC_CCM_PDR2);
+
+ clk->rate = rate;
+ return 0;
+}
+
+static void _clk_mstick1_recalc(struct clk *clk)
+{
+ unsigned long prdf, podf;
+ prdf = PDR1(MXC_CCM_PDR1_MSHC_PRDF_MASK, MXC_CCM_PDR1_MSHC_PRDF_OFFSET);
+ podf = PDR1(MXC_CCM_PDR1_MSHC_PODF_MASK, MXC_CCM_PDR1_MSHC_PODF_OFFSET);
+ clk->rate = clk->parent->rate / ((prdf + 1) * (podf + 1));
+}
+
+static int _clk_mstick1_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+ u32 pre, post;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ __calc_pre_post_dividers(div, &pre, &post);
+
+ reg = __raw_readl(MXC_CCM_PDR1) &
+ ~(MXC_CCM_PDR1_MSHC_PRDF_MASK | MXC_CCM_PDR1_MSHC_PODF_MASK);
+ reg |= (post - 1) << MXC_CCM_PDR1_MSHC_PODF_OFFSET;
+ reg |= (pre - 1) << MXC_CCM_PDR1_MSHC_PRDF_OFFSET;
+ __raw_writel(reg, MXC_CCM_PDR1);
+
+ clk->rate = rate;
+ return 0;
+}
+
+static int _clk_mstick1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+ if (parent == &cpu_clk)
+ reg = __raw_readl(MXC_CCM_PDR1) | MXC_CCM_PDR1_MSHC_M_U;
+ else if (parent == &peri_pll_clk)
+ reg = __raw_readl(MXC_CCM_PDR1) & (~MXC_CCM_PDR1_MSHC_M_U);
+ else
+ return -EINVAL;
+ __raw_writel(reg, MXC_CCM_PDR1);
+ return 0;
+}
+
+static void _clk_spdif_recalc(struct clk *clk)
+{
+ unsigned long prdf, podf;
+ prdf =
+ PDR3(MXC_CCM_PDR3_SPDIF_PRDF_MASK, MXC_CCM_PDR3_SPDIF_PRDF_OFFSET);
+ podf =
+ PDR3(MXC_CCM_PDR3_SPDIF_PODF_MASK, MXC_CCM_PDR3_SPDIF_PODF_OFFSET);
+ clk->rate = clk->parent->rate / ((prdf + 1) * (podf + 1));
+}
+
+static int _clk_spdif_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+ u32 pre, post;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ __calc_pre_post_dividers(div, &pre, &post);
+
+ reg = __raw_readl(MXC_CCM_PDR3) &
+ ~(MXC_CCM_PDR3_SPDIF_PRDF_MASK | MXC_CCM_PDR3_SPDIF_PODF_MASK);
+ reg |= (post - 1) << MXC_CCM_PDR3_SPDIF_PODF_OFFSET;
+ reg |= (pre - 1) << MXC_CCM_PDR3_SPDIF_PRDF_OFFSET;
+ __raw_writel(reg, MXC_CCM_PDR3);
+
+ clk->rate = rate;
+ return 0;
+}
+
+static int _clk_spdif_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+ if (parent == &cpu_clk)
+ reg = __raw_readl(MXC_CCM_PDR3) | MXC_CCM_PDR3_SPDIF_M_U;
+ else if (parent == &peri_pll_clk)
+ reg = __raw_readl(MXC_CCM_PDR3) & (~MXC_CCM_PDR3_SPDIF_M_U);
+ else
+ return -EINVAL;
+ __raw_writel(reg, MXC_CCM_PDR3);
+ return 0;
+}
+
+static void _clk_asrc_recalc(struct clk *clk)
+{
+ unsigned long div;
+ div = __raw_readl(MXC_CCM_COSR) & MXC_CCM_COSR_ASRC_AUDIO_PODF_MASK;
+ div = div >> MXC_CCM_COSR_ASRC_AUDIO_PODF_OFFSET;
+ clk->rate = clk->parent->rate / (div + 1);
+}
+
+static int _clk_asrc_set_rate(struct clk *clk, unsigned long rate)
+{
+ int div;
+ unsigned long reg;
+ if (clk->parent->rate % rate)
+ return -EINVAL;
+
+ div = clk->parent->rate / rate;
+ reg = __raw_readl(MXC_CCM_COSR) & (~MXC_CCM_COSR_ASRC_AUDIO_PODF_MASK);
+ reg |= (div - 1) << MXC_CCM_COSR_ASRC_AUDIO_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_COSR);
+ clk->rate = rate;
+ return 0;
+}
+
+static void _clk_sdhc_recalc(struct clk *clk)
+{
+ u32 podf = 0, prdf = 0;
+
+ switch (clk->id) {
+ case 0:
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ prdf = PDR3(MXC_CCM_PDR3_ESDHC1_PRDF_MASK,
+ MXC_CCM_PDR3_ESDHC1_PRDF_OFFSET);
+ podf = PDR3(MXC_CCM_PDR3_ESDHC1_PODF_MASK,
+ MXC_CCM_PDR3_ESDHC1_PODF_OFFSET);
+ } else
+ podf = PDR3(MXC_CCM_PDR3_ESDHC1_PODF_MASK_V2,
+ MXC_CCM_PDR3_ESDHC1_PODF_OFFSET);
+ break;
+ case 1:
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ prdf = PDR3(MXC_CCM_PDR3_ESDHC2_PRDF_MASK,
+ MXC_CCM_PDR3_ESDHC2_PRDF_OFFSET);
+ podf = PDR3(MXC_CCM_PDR3_ESDHC2_PODF_MASK,
+ MXC_CCM_PDR3_ESDHC2_PODF_OFFSET);
+ } else
+ podf = PDR3(MXC_CCM_PDR3_ESDHC2_PODF_MASK_V2,
+ MXC_CCM_PDR3_ESDHC2_PODF_OFFSET);
+ break;
+ case 2:
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ prdf = PDR3(MXC_CCM_PDR3_ESDHC3_PRDF_MASK,
+ MXC_CCM_PDR3_ESDHC3_PRDF_OFFSET);
+ podf = PDR3(MXC_CCM_PDR3_ESDHC3_PODF_MASK,
+ MXC_CCM_PDR3_ESDHC3_PODF_OFFSET);
+ } else
+ podf = PDR3(MXC_CCM_PDR3_ESDHC3_PODF_MASK_V2,
+ MXC_CCM_PDR3_ESDHC3_PODF_OFFSET);
+ break;
+ default:
+ return;
+ }
+ clk->rate = clk->parent->rate / ((podf + 1) * (prdf + 1));
+}
+
+static int _clk_sdhc_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+ u32 prdf, podf;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1)
+ __calc_pre_post_dividers(div, &prdf, &podf);
+
+ switch (clk->id) {
+ case 0:
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ reg = __raw_readl(MXC_CCM_PDR3) &
+ ~(MXC_CCM_PDR3_ESDHC1_PRDF_MASK |
+ MXC_CCM_PDR3_ESDHC1_PODF_MASK);
+ reg |= (podf - 1) << MXC_CCM_PDR3_ESDHC1_PODF_OFFSET;
+ reg |= (prdf - 1) << MXC_CCM_PDR3_ESDHC1_PRDF_OFFSET;
+ } else {
+ reg = __raw_readl(MXC_CCM_PDR3) &
+ ~MXC_CCM_PDR3_ESDHC1_PODF_MASK_V2;
+ reg |= (div - 1) << MXC_CCM_PDR3_ESDHC1_PODF_OFFSET;
+ }
+ break;
+ case 1:
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ reg = __raw_readl(MXC_CCM_PDR3) &
+ ~(MXC_CCM_PDR3_ESDHC2_PRDF_MASK |
+ MXC_CCM_PDR3_ESDHC2_PODF_MASK);
+ reg |= (podf - 1) << MXC_CCM_PDR3_ESDHC2_PODF_OFFSET;
+ reg |= (prdf - 1) << MXC_CCM_PDR3_ESDHC2_PRDF_OFFSET;
+ } else {
+ reg = __raw_readl(MXC_CCM_PDR3) &
+ ~MXC_CCM_PDR3_ESDHC2_PODF_MASK_V2;
+ reg |= (div - 1) << MXC_CCM_PDR3_ESDHC2_PODF_OFFSET;
+ }
+ break;
+ case 2:
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ reg = __raw_readl(MXC_CCM_PDR3) &
+ ~(MXC_CCM_PDR3_ESDHC3_PRDF_MASK |
+ MXC_CCM_PDR3_ESDHC3_PODF_MASK);
+ reg |= (podf - 1) << MXC_CCM_PDR3_ESDHC3_PODF_OFFSET;
+ reg |= (prdf - 1) << MXC_CCM_PDR3_ESDHC3_PRDF_OFFSET;
+ } else {
+ reg = __raw_readl(MXC_CCM_PDR3) &
+ ~MXC_CCM_PDR3_ESDHC3_PODF_MASK_V2;
+ reg |= (div - 1) << MXC_CCM_PDR3_ESDHC3_PODF_OFFSET;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ __raw_writel(reg, MXC_CCM_PDR3);
+
+ clk->rate = rate;
+ return 0;
+}
+
+static struct clk ckih_clk = {
+ .name = "ckih",
+ .rate = CKIH_CLK_FREQ,
+ .flags = RATE_FIXED,
+};
+
+static struct clk int_32k_clk = {
+ .name = "int_32k",
+ .rate = CKIL_CLK_FREQ,
+ .flags = RATE_FIXED,
+};
+
+static struct clk ext_32k_clk = {
+ .name = "ext_32k",
+ .rate = CKIL_EXT_FREQ,
+ .flags = RATE_FIXED,
+};
+
+static int _clk_ckil_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+ if (parent == &int_32k_clk) {
+ reg = __raw_readl(MXC_CCM_PDR0) & (~MXC_CCM_PDR0_CKIL_SEL);
+ clk->rate = parent->rate;
+ } else if (parent == &ext_32k_clk) {
+ reg = __raw_readl(MXC_CCM_PDR0) | MXC_CCM_PDR0_CKIL_SEL;
+ clk->rate = parent->rate;
+ } else
+ return -EINVAL;
+ __raw_writel(reg, MXC_CCM_PDR0);
+ return 0;
+}
+
+static int _clk_ckil_set_rate(struct clk *clk, unsigned long rate)
+{
+ clk->rate = clk->parent->rate;
+ return 0;
+}
+
+static struct clk ckil_clk = {
+ .name = "ckil",
+ .parent = &ext_32k_clk,
+ .set_parent = _clk_ckil_set_parent,
+ .set_rate = _clk_ckil_set_rate,
+};
+
+static int _clk_ckie_enable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_PMCR2) & ~MXC_CCM_PMCR2_OSC_AUDIO_DOWN;
+ __raw_writel(reg, MXC_CCM_PMCR2);
+
+ return 0;
+}
+
+static void _clk_ckie_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_PMCR2) | MXC_CCM_PMCR2_OSC_AUDIO_DOWN;
+ __raw_writel(reg, MXC_CCM_PMCR2);
+}
+
+static struct clk ckie_clk = {
+ .name = "ckie",
+ .rate = CKIE_CLK_FREQ,
+ .flags = RATE_FIXED,
+ .enable = _clk_ckie_enable,
+ .disable = _clk_ckie_disable,
+};
+
+static struct clk mcu_pll_clk = {
+ .name = "mcu_pll",
+ .parent = &ckih_clk,
+ .set_rate = _clk_pll_set_rate,
+ .recalc = _clk_pll_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk peri_pll_clk = {
+ .name = "peri_pll",
+ .parent = &ckih_clk,
+ .set_rate = _clk_pll_set_rate,
+ .recalc = _clk_pll_recalc,
+ .enable = _clk_peri_pll_enable,
+ .disable = _clk_peri_pll_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk cpu_clk = {
+ .name = "cpu_clk",
+ .parent = &mcu_pll_clk,
+ .recalc = _clk_cpu_recalc,
+ .set_rate = _clk_cpu_set_rate,
+};
+
+static struct clk ahb_clk = {
+ .name = "ahb_clk",
+ .parent = &cpu_clk,
+ .recalc = _clk_hclk_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ipg_clk = {
+ .name = "ipg_clk",
+ .parent = &ahb_clk,
+ .recalc = _clk_ipg_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk perclk_clk = {
+ .name = "perclk_clk",
+ .parent = &ahb_clk,
+ .recalc = _clk_per_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk uart_per_clk = {
+ .name = "uart_per_clk",
+ .parent = &peri_pll_clk,
+ .recalc = _clk_uart_per_recalc,
+ .round_rate = _clk_round_rate,
+ .set_rate = _clk_uart_set_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk asrc_clk[] = {
+ {
+ .name = "asrc_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_ASRC_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "asrc_audio_clk",
+ .parent = &ckie_clk,
+ .recalc = _clk_asrc_recalc,
+ .round_rate = _clk_round_rate,
+ .set_rate = _clk_asrc_set_rate,
+ .enable = _clk_asrc_enable,
+ .disable = _clk_asrc_disable,},
+};
+
+static struct clk ata_clk = {
+ .name = "ata_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_ATA_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk can_clk[] = {
+ {
+ .name = "can_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_CAN1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "can_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_CAN2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk cspi_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_CSPI1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "cspi_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_CSPI2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk ect_clk = {
+ .name = "ect_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_ECT_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk emi_clk = {
+ .name = "emi_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_EMI_OFFSET,
+ .disable = _clk_emi_disable,
+};
+
+static struct clk epit_clk[] = {
+ {
+ .name = "epit_clk",
+ .id = 0,
+ .parent = &perclk_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_EPIT1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "epit_clk",
+ .id = 1,
+ .parent = &perclk_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_EPIT2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk esai_clk = {
+ .name = "esai_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_ESAI_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk sdhc_clk[] = {
+ {
+ .name = "sdhc_clk",
+ .id = 0,
+ .parent = &peri_pll_clk,
+ .recalc = _clk_sdhc_recalc,
+ .set_rate = _clk_sdhc_set_rate,
+ .round_rate = _clk_round_rate,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_ESDHC1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "sdhc_clk",
+ .id = 1,
+ .parent = &peri_pll_clk,
+ .recalc = _clk_sdhc_recalc,
+ .set_rate = _clk_sdhc_set_rate,
+ .round_rate = _clk_round_rate,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_ESDHC2_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "sdhc_clk",
+ .id = 2,
+ .parent = &peri_pll_clk,
+ .recalc = _clk_sdhc_recalc,
+ .set_rate = _clk_sdhc_set_rate,
+ .round_rate = _clk_round_rate,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR0,
+ .enable_shift = MXC_CCM_CGR0_ESDHC3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk fec_clk = {
+ .name = "fec_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_FEC_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk gpt_clk = {
+ .name = "gpt_clk",
+ .parent = &perclk_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_GPT_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk i2c_clk[] = {
+ {
+ .name = "i2c_clk",
+ .id = 0,
+ .parent = &perclk_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_I2C1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "i2c_clk",
+ .id = 1,
+ .parent = &perclk_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_I2C2_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "i2c_clk",
+ .id = 2,
+ .parent = &perclk_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_I2C3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk ipu_clk = {
+ .name = "ipu_clk",
+ .parent = &cpu_clk,
+ .recalc = _clk_hsp_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_IPU_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk kpp_clk = {
+ .name = "kpp_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_KPP_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk mlb_clk = {
+ .name = "mlb_clk",
+ .parent = &ahb_clk,
+ .recalc = _clk_mlb_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_MLB_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk mstick_clk = {
+ .name = "mstick_clk",
+ .id = 0,
+ .parent = &peri_pll_clk,
+ .recalc = _clk_mstick1_recalc,
+ .set_rate = _clk_mstick1_set_rate,
+ .round_rate = _clk_per_post_round_rate,
+ .set_parent = _clk_mstick1_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_MSHC_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk owire_clk = {
+ .name = "owire_clk",
+ .parent = &perclk_clk,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_OWIRE_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static struct clk pwm_clk = {
+ .name = "pwm_clk",
+ .parent = &perclk_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_PWM_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk rng_clk = {
+ .name = "rng_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR1,
+ .enable_shift = MXC_CCM_CGR1_RNGC_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk rtc_clk = {
+ .name = "rtc_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_RTC_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk rtic_clk = {
+ .name = "rtic_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_RTIC_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk scc_clk = {
+ .name = "scc_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_SCC_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk sdma_clk[] = {
+ {
+ .name = "sdma_ahb_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_SDMA_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "sdma_ipg_clk",
+ .parent = &ipg_clk,}
+};
+
+static struct clk spba_clk = {
+ .name = "spba_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_SPBA_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk spdif_clk[] = {
+ {
+ .name = "spdif_clk",
+ .parent = &peri_pll_clk,
+ .recalc = _clk_spdif_recalc,
+ .set_rate = _clk_spdif_set_rate,
+ .round_rate = _clk_per_post_round_rate,
+ .set_parent = _clk_spdif_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_SPDIF_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "spdif_audio_clk",
+ .parent = &ckie_clk,},
+ {
+ .name = "spdif_ipg_clk",
+ .parent = &ipg_clk,},
+};
+
+static struct clk ssi_clk[] = {
+ {
+ .name = "ssi_clk",
+ .parent = &peri_pll_clk,
+ .recalc = _clk_ssi_recalc,
+ .set_rate = _clk_ssi_set_rate,
+ .round_rate = _clk_per_post_round_rate,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_SSI1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "ssi_clk",
+ .id = 1,
+ .parent = &peri_pll_clk,
+ .recalc = _clk_ssi_recalc,
+ .set_rate = _clk_ssi_set_rate,
+ .round_rate = _clk_per_post_round_rate,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_SSI2_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk uart_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 0,
+ .parent = &uart_per_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_UART1_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "uart_clk",
+ .id = 1,
+ .parent = &uart_per_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_UART2_OFFSET,
+ .disable = _clk_disable,},
+ {
+ .name = "uart_clk",
+ .id = 2,
+ .parent = &uart_per_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_UART3_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk usb_clk[] = {
+ {
+ .name = "usb_clk",
+ .parent = &peri_pll_clk,
+ .recalc = _clk_usb_recalc,
+ .round_rate = _clk_round_rate,
+ .set_rate = _clk_usb_set_rate,},
+ {
+ .name = "usb_ahb_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_USBOTG_OFFSET,
+ .disable = _clk_disable,},
+};
+
+static struct clk wdog_clk = {
+ .name = "wdog_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR2,
+ .enable_shift = MXC_CCM_CGR2_WDOG_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk csi_clk = {
+ .name = "csi_clk",
+ .parent = &peri_pll_clk,
+ .recalc = _clk_csi_recalc,
+ .round_rate = _clk_round_rate,
+ .set_rate = _clk_csi_set_rate,
+ .set_parent = _clk_csi_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR3,
+ .enable_shift = MXC_CCM_CGR3_CSI_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk iim_clk = {
+ .name = "iim_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR3,
+ .enable_shift = MXC_CCM_CGR3_IIM_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk nfc_clk = {
+ .name = "nfc_clk",
+ .parent = &ahb_clk,
+ .recalc = _clk_nfc_recalc,
+};
+
+static unsigned long _clk_cko1_round_rate(struct clk *clk, unsigned long rate)
+{
+ u32 div = 0, div1 = 1;
+
+ div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ if (div > 64) {
+ div = (div + 1) >> 1;
+ div1++;
+ }
+
+ if (div > 128)
+ div = 64;
+ return clk->parent->rate / (div * div1);
+}
+
+static int _clk_cko1_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div, div1 = 0;
+ u32 prdf, podf;
+
+ div = clk->parent->rate / rate;
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+ if (div > 64) {
+ div1 = MXC_CCM_COSR_CLKOUTDIV_1;
+ div >>= 1;
+ } else {
+ div1 = 0;
+ }
+
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ __calc_two_dividers(div, &prdf, &podf);
+ reg = __raw_readl(MXC_CCM_COSR) &
+ ~(MXC_CCM_COSR_CLKOUT_PREDIV_MASK |
+ MXC_CCM_COSR_CLKOUT_PRODIV_MASK |
+ MXC_CCM_COSR_CLKOUTDIV_1);
+ reg |= ((prdf - 1) << MXC_CCM_COSR_CLKOUT_PREDIV_OFFSET)
+ | ((podf - 1) << MXC_CCM_COSR_CLKOUT_PRODIV_OFFSET)
+ | div1;
+ } else {
+ reg = __raw_readl(MXC_CCM_COSR) &
+ ~(MXC_CCM_COSR_CLKOUT_PRODIV_MASK_V2 |
+ MXC_CCM_COSR_CLKOUTDIV_1);
+ reg |= ((div - 1) << MXC_CCM_COSR_CLKOUT_PRODIV_OFFSET) | div1;
+ }
+ __raw_writel(reg, MXC_CCM_COSR);
+
+ return 0;
+}
+
+static void _clk_cko1_recalc(struct clk *clk)
+{
+ u32 prdf = 1;
+ u32 podf, div1;
+ u32 reg = __raw_readl(MXC_CCM_COSR);
+
+ div1 = 1 << ((reg & MXC_CCM_COSR_CLKOUTDIV_1) != 0);
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ prdf = (reg & MXC_CCM_COSR_CLKOUT_PREDIV_MASK) >>
+ MXC_CCM_COSR_CLKOUT_PREDIV_OFFSET;
+ podf = (reg & MXC_CCM_COSR_CLKOUT_PRODIV_MASK) >>
+ MXC_CCM_COSR_CLKOUT_PRODIV_OFFSET;
+ } else
+ podf = (reg & MXC_CCM_COSR_CLKOUT_PRODIV_MASK_V2) >>
+ MXC_CCM_COSR_CLKOUT_PRODIV_OFFSET;
+
+ clk->rate = clk->parent->rate / (div1 * (podf + 1) * (prdf + 1));
+}
+
+static int _clk_cko1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+ reg = __raw_readl(MXC_CCM_COSR) & ~MXC_CCM_COSR_CLKOSEL_MASK;
+
+ if (parent == &ckil_clk) {
+ reg &= ~MXC_CCM_COSR_CKIL_CKIH_MASK;
+ reg |= 0 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ } else if (parent == &ckih_clk) {
+ reg |= 1 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ } else if (parent == &ckie_clk)
+ reg |= 2 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &peri_pll_clk)
+ reg |= 6 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &cpu_clk)
+ reg |= 7 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &ahb_clk)
+ reg |= 8 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &ipg_clk)
+ reg |= 9 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &usb_clk[1])
+ reg |= 0xB << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &sdhc_clk[1])
+ reg |= 0xC << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &ssi_clk[1])
+ reg |= 0xD << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &mlb_clk)
+ reg |= 0xE << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &csi_clk)
+ reg |= 0x11 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &spdif_clk[0])
+ reg |= 0x12 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &uart_clk[0])
+ reg |= 0x13 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if (parent == &asrc_clk[1])
+ reg |= 0x14 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if ((parent == &nfc_clk) && (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1))
+ reg |= 0x17 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else if ((parent == &ipu_clk) && (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1))
+ reg |= 0x18 << MXC_CCM_COSR_CLKOSEL_OFFSET;
+ else
+ return -EINVAL;
+
+ __raw_writel(reg, MXC_CCM_COSR);
+ return 0;
+}
+
+static int _clk_cko1_enable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_COSR) | MXC_CCM_COSR_CLKOEN;
+ __raw_writel(reg, MXC_CCM_COSR);
+
+ return 0;
+}
+
+static void _clk_cko1_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_COSR) & ~MXC_CCM_COSR_CLKOEN;
+ __raw_writel(reg, MXC_CCM_COSR);
+}
+
+static struct clk cko1_clk = {
+ .name = "cko1_clk",
+ .recalc = _clk_cko1_recalc,
+ .set_rate = _clk_cko1_set_rate,
+ .round_rate = _clk_cko1_round_rate,
+ .set_parent = _clk_cko1_set_parent,
+ .enable = _clk_cko1_enable,
+ .disable = _clk_cko1_disable,
+};
+
+static struct clk gpu2d_clk = {
+ .name = "gpu2d_clk",
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CGR3,
+ .enable_shift = MXC_CCM_CGR3_GPU2D_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk *mxc_clks[] = {
+ &int_32k_clk,
+ &ext_32k_clk,
+ &ckih_clk,
+ &ckil_clk,
+ &ckie_clk,
+ &mcu_pll_clk,
+ &peri_pll_clk,
+ &cpu_clk,
+ &ahb_clk,
+ &ipg_clk,
+ &perclk_clk,
+ &uart_per_clk,
+ &asrc_clk[0],
+ &asrc_clk[1],
+ &ata_clk,
+ &can_clk[0],
+ &can_clk[1],
+ &cspi_clk[0],
+ &cspi_clk[1],
+ &ect_clk,
+ &emi_clk,
+ &epit_clk[0],
+ &epit_clk[1],
+ &esai_clk,
+ &sdhc_clk[0],
+ &sdhc_clk[1],
+ &sdhc_clk[2],
+ &fec_clk,
+ &gpt_clk,
+ &i2c_clk[0],
+ &i2c_clk[1],
+ &i2c_clk[2],
+ &ipu_clk,
+ &kpp_clk,
+ &mlb_clk,
+ &mstick_clk,
+ &owire_clk,
+ &rng_clk,
+ &pwm_clk,
+ &rtc_clk,
+ &rtic_clk,
+ &scc_clk,
+ &sdma_clk[0],
+ &sdma_clk[1],
+ &spba_clk,
+ &spdif_clk[0],
+ &spdif_clk[1],
+ &spdif_clk[2],
+ &ssi_clk[0],
+ &ssi_clk[1],
+ &uart_clk[0],
+ &uart_clk[1],
+ &uart_clk[2],
+ &usb_clk[0],
+ &usb_clk[1],
+ &wdog_clk,
+ &csi_clk,
+ &iim_clk,
+ &nfc_clk,
+ &cko1_clk,
+ &gpu2d_clk,
+};
+
+extern void propagate_rate(struct clk *tclk);
+
+static void mxc_clockout_scan(void)
+{
+ u32 reg = __raw_readl(MXC_CCM_COSR) & MXC_CCM_COSR_CLKOSEL_MASK;
+ reg >>= MXC_CCM_COSR_CLKOSEL_OFFSET;
+ switch (reg) {
+ case 0:
+ cko1_clk.parent = &ckil_clk;
+ break;
+ case 1:
+ cko1_clk.parent = &ckih_clk;
+ break;
+ case 2:
+ cko1_clk.parent = &ckie_clk;
+ break;
+ case 6:
+ cko1_clk.parent = &peri_pll_clk;
+ break;
+ case 7:
+ cko1_clk.parent = &cpu_clk;
+ break;
+ case 8:
+ cko1_clk.parent = &ahb_clk;
+ break;
+ case 9:
+ cko1_clk.parent = &ipg_clk;
+ break;
+ case 0xB:
+ cko1_clk.parent = &usb_clk[1];
+ break;
+ case 0xC:
+ cko1_clk.parent = &sdhc_clk[1];
+ break;
+ case 0xD:
+ cko1_clk.parent = &ssi_clk[1];
+ break;
+ case 0xE:
+ cko1_clk.parent = &mlb_clk;
+ break;
+ case 0x11:
+ cko1_clk.parent = &csi_clk;
+ break;
+ case 0x12:
+ cko1_clk.parent = &spdif_clk[0];
+ break;
+ case 0x13:
+ cko1_clk.parent = &uart_clk[0];
+ break;
+ case 0x14:
+ cko1_clk.parent = &asrc_clk[1];
+ break;
+ case 0x17:
+ cko1_clk.parent = &nfc_clk;
+ break;
+ case 0x18:
+ cko1_clk.parent = &ipu_clk;
+ break;
+ }
+}
+
+static void mxc_update_clocks(void)
+{
+ unsigned long reg;
+ reg = __raw_readl(MXC_CCM_PDR0);
+ if ((!(reg & MXC_CCM_PDR0_AUTO_CON))
+ && (cpu_is_mx35_rev(CHIP_REV_2_0) < 1))
+ ipu_clk.parent = &ahb_clk;
+
+ if (reg & MXC_CCM_PDR0_PER_SEL)
+ perclk_clk.parent = &cpu_clk;
+
+ reg = __raw_readl(MXC_CCM_PDR1);
+ if (reg & MXC_CCM_PDR1_MSHC_M_U)
+ mstick_clk.parent = &cpu_clk;
+
+ reg = __raw_readl(MXC_CCM_PDR2);
+ if (reg & MXC_CCM_PDR2_CSI_M_U)
+ csi_clk.parent = &cpu_clk;
+ if (reg & MXC_CCM_PDR2_SSI_M_U) {
+ ssi_clk[0].parent = &cpu_clk;
+ ssi_clk[1].parent = &cpu_clk;
+ }
+
+ reg = __raw_readl(MXC_CCM_PDR3);
+ if (reg & MXC_CCM_PDR3_SPDIF_M_U)
+ spdif_clk[0].parent = &cpu_clk;
+
+ if (reg & MXC_CCM_PDR3_UART_M_U)
+ uart_per_clk.parent = &cpu_clk;
+
+ if (reg & MXC_CCM_PDR3_ESDHC_M_U) {
+ sdhc_clk[0].parent = &cpu_clk;
+ sdhc_clk[1].parent = &cpu_clk;
+ sdhc_clk[2].parent = &cpu_clk;
+ }
+
+ reg = __raw_readl(MXC_CCM_PDR4);
+ if (reg & MXC_CCM_PDR4_USB_M_U)
+ usb_clk[0].parent = &cpu_clk;
+
+ mxc_clockout_scan();
+}
+
+int __init mxc_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2)
+{
+ struct clk **clkp;
+ for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++)
+ clk_register(*clkp);
+
+ /* Turn off all possible clocks */
+ __raw_writel(MXC_CCM_CGR0_ECT_MASK | MXC_CCM_CGR0_EMI_MASK,
+ MXC_CCM_CGR0);
+ __raw_writel(MXC_CCM_CGR1_GPIO1_MASK | MXC_CCM_CGR1_GPIO2_MASK |
+ MXC_CCM_CGR1_GPIO3_MASK | MXC_CCM_CGR1_GPT_MASK |
+ MXC_CCM_CGR1_IOMUXC_MASK, MXC_CCM_CGR1);
+ __raw_writel(MXC_CCM_CGR2_MAX_MASK | MXC_CCM_CGR2_SPBA_MASK |
+ MXC_CCM_CGR2_AUDMUX_MASK | MXC_CCM_CGR2_MAX_ENABLE,
+ MXC_CCM_CGR2);
+ __raw_writel(MXC_CCM_CGR3_IIM_MASK, MXC_CCM_CGR3);
+ __raw_writel((__raw_readl(MXC_CCM_PMCR2) |
+ MXC_CCM_PMCR2_OSC24M_DOWN |
+ MXC_CCM_PMCR2_OSC_AUDIO_DOWN), MXC_CCM_PMCR2);
+ mxc_update_clocks();
+ pr_info("Clock input source is %ld\n", ckih_clk.rate);
+
+ /* Determine which high frequency clock source is coming in */
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+ sync_cpu_wb();
+
+ /* This will propagate to all children and init all the clock rates */
+ propagate_rate(&ckih_clk);
+ propagate_rate(&ext_32k_clk);
+ propagate_rate(&ckie_clk);
+
+ clk_enable(&mcu_pll_clk);
+ clk_enable(&gpt_clk);
+ clk_enable(&emi_clk);
+ clk_enable(&iim_clk);
+ clk_enable(&spba_clk);
+
+ /* Init serial PLL according */
+ clk_set_rate(&peri_pll_clk, 300000000);
+
+ clk_enable(&peri_pll_clk);
+ return 0;
+}
diff --git a/arch/arm/mach-mx35/cpu.c b/arch/arm/mach-mx35/cpu.c
new file mode 100644
index 000000000000..90848a55e0ad
--- /dev/null
+++ b/arch/arm/mach-mx35/cpu.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mach-mx35/cpu.c
+ *
+ * @brief This file contains the CPU initialization code.
+ *
+ * @ingroup MSL_MX35
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+#include <asm/hardware/cache-l2x0.h>
+
+/*!
+ * CPU initialization. It is called by fixup_mxc_board()
+ */
+void __init mxc_cpu_init(void)
+{
+
+ /* Setup Peripheral Port Remap register for AVIC */
+ asm("ldr r0, =0xC0000015 \n\
+ mcr p15, 0, r0, c15, c2, 4");
+ /*TODO:Add code to check chip version */
+
+ if (!system_rev)
+ mxc_set_system_rev(0x35, CHIP_REV_1_0);
+
+}
+
+/*!
+ * Post CPU init code
+ *
+ * @return 0 always
+ */
+static int __init post_cpu_init(void)
+{
+ void *l2_base;
+ unsigned long aips_reg;
+
+ /* Initialize L2 cache */
+ l2_base = ioremap(L2CC_BASE_ADDR, SZ_4K);
+ if (l2_base)
+ l2x0_init(l2_base, 0x00030024, 0x00000000);
+
+ /*
+ * S/W workaround: Clear the off platform peripheral modules
+ * Supervisor Protect bit for SDMA to access them.
+ */
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x40));
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x44));
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x48));
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x4C));
+ aips_reg = __raw_readl(IO_ADDRESS(AIPS1_BASE_ADDR + 0x50));
+ aips_reg &= 0x00FFFFFF;
+ __raw_writel(aips_reg, IO_ADDRESS(AIPS1_BASE_ADDR + 0x50));
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x40));
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x44));
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x48));
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x4C));
+ aips_reg = __raw_readl(IO_ADDRESS(AIPS2_BASE_ADDR + 0x50));
+ aips_reg &= 0x00FFFFFF;
+ __raw_writel(aips_reg, IO_ADDRESS(AIPS2_BASE_ADDR + 0x50));
+
+ return 0;
+}
+
+postcore_initcall(post_cpu_init);
diff --git a/arch/arm/mach-mx35/crm_regs.h b/arch/arm/mach-mx35/crm_regs.h
new file mode 100644
index 000000000000..b770b4841e8e
--- /dev/null
+++ b/arch/arm/mach-mx35/crm_regs.h
@@ -0,0 +1,430 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX35_CRM_REGS_H__
+#define __ARCH_ARM_MACH_MX35_CRM_REGS_H__
+
+#define CKIH_CLK_FREQ 24000000
+#define CKIE_CLK_FREQ 24576000
+#define CKIL_CLK_FREQ 32000
+#define CKIL_EXT_FREQ 32768
+
+#define MXC_CCM_BASE ((char *)IO_ADDRESS(CCM_BASE_ADDR))
+
+/* Register addresses */
+#define MXC_CCM_CCMR (MXC_CCM_BASE + 0x00)
+#define MXC_CCM_PDR0 (MXC_CCM_BASE + 0x04)
+#define MXC_CCM_PDR1 (MXC_CCM_BASE + 0x08)
+#define MXC_CCM_PDR2 (MXC_CCM_BASE + 0x0C)
+#define MXC_CCM_PDR3 (MXC_CCM_BASE + 0x10)
+#define MXC_CCM_PDR4 (MXC_CCM_BASE + 0x14)
+#define MXC_CCM_RCSR (MXC_CCM_BASE + 0x18)
+#define MXC_CCM_MPCTL (MXC_CCM_BASE + 0x1C)
+#define MXC_CCM_PPCTL (MXC_CCM_BASE + 0x20)
+#define MXC_CCM_ACMR (MXC_CCM_BASE + 0x24)
+#define MXC_CCM_COSR (MXC_CCM_BASE + 0x28)
+#define MXC_CCM_CGR0 (MXC_CCM_BASE + 0x2C)
+#define MXC_CCM_CGR1 (MXC_CCM_BASE + 0x30)
+#define MXC_CCM_CGR2 (MXC_CCM_BASE + 0x34)
+#define MXC_CCM_CGR3 (MXC_CCM_BASE + 0x38)
+#define MXC_CCM_RESV (MXC_CCM_BASE + 0x3C)
+#define MXC_CCM_DCVR0 (MXC_CCM_BASE + 0x40)
+#define MXC_CCM_DCVR1 (MXC_CCM_BASE + 0x44)
+#define MXC_CCM_DCVR2 (MXC_CCM_BASE + 0x48)
+#define MXC_CCM_DCVR3 (MXC_CCM_BASE + 0x4C)
+#define MXC_CCM_LTR0 (MXC_CCM_BASE + 0x50)
+#define MXC_CCM_LTR1 (MXC_CCM_BASE + 0x54)
+#define MXC_CCM_LTR2 (MXC_CCM_BASE + 0x58)
+#define MXC_CCM_LTR3 (MXC_CCM_BASE + 0x5C)
+#define MXC_CCM_LTBR0 (MXC_CCM_BASE + 0x60)
+#define MXC_CCM_LTBR1 (MXC_CCM_BASE + 0x64)
+#define MXC_CCM_PMCR0 (MXC_CCM_BASE + 0x68)
+#define MXC_CCM_PMCR1 (MXC_CCM_BASE + 0x6C)
+#define MXC_CCM_PMCR2 (MXC_CCM_BASE + 0x70)
+
+/* Register bit definitions */
+#define MXC_CCM_CCMR_WFI (1 << 30)
+#define MXC_CCM_CCMR_STBY_EXIT_SRC (1 << 29)
+#define MXC_CCM_CCMR_VSTBY (1 << 28)
+#define MXC_CCM_CCMR_WBEN (1 << 27)
+#define MXC_CCM_CCMR_VOL_RDY_CNT_OFFSET 20
+#define MXC_CCM_CCMR_VOL_RDY_CNT_MASK (0xF << 20)
+#define MXC_CCM_CCMR_ROMW_OFFSET 18
+#define MXC_CCM_CCMR_ROMW_MASK (0x3 << 18)
+#define MXC_CCM_CCMR_RAMW_OFFSET 21
+#define MXC_CCM_CCMR_RAMW_MASK (0x3 << 21)
+#define MXC_CCM_CCMR_LPM_OFFSET 14
+#define MXC_CCM_CCMR_LPM_MASK (0x3 << 14)
+#define MXC_CCM_CCMR_UPE (1 << 9)
+#define MXC_CCM_CCMR_MPE (1 << 3)
+
+#define MXC_CCM_PDR0_PER_SEL (1 << 26)
+#define MXC_CCM_PDR0_IPU_HND_BYP (1 << 23)
+#define MXC_CCM_PDR0_HSP_PODF_OFFSET 20
+#define MXC_CCM_PDR0_HSP_PODF_MASK (0x3 << 20)
+#define MXC_CCM_PDR0_CON_MUX_DIV_OFFSET 16
+#define MXC_CCM_PDR0_CON_MUX_DIV_MASK (0xF << 16)
+#define MXC_CCM_PDR0_CKIL_SEL (1 << 15)
+#define MXC_CCM_PDR0_PER_PODF_OFFSET 12
+#define MXC_CCM_PDR0_PER_PODF_MASK (0xF << 12)
+#define MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET 9
+#define MXC_CCM_PDR0_AUTO_MUX_DIV_MASK (0x7 << 9)
+#define MXC_CCM_PDR0_AUTO_CON 0x1
+
+#define MXC_CCM_PDR1_MSHC_PRDF_OFFSET 28
+#define MXC_CCM_PDR1_MSHC_PRDF_MASK (0x7 << 28)
+#define MXC_CCM_PDR1_MSHC_PODF_OFFSET 22
+#define MXC_CCM_PDR1_MSHC_PODF_MASK (0x3F << 22)
+#define MXC_CCM_PDR1_MSHC_M_U (1 << 7)
+
+#define MXC_CCM_PDR2_SSI2_PRDF_OFFSET 27
+#define MXC_CCM_PDR2_SSI2_PRDF_MASK (0x7 << 27)
+#define MXC_CCM_PDR2_SSI1_PRDF_OFFSET 24
+#define MXC_CCM_PDR2_SSI1_PRDF_MASK (0x7 << 24)
+#define MXC_CCM_PDR2_CSI_PRDF_OFFSET 19
+#define MXC_CCM_PDR2_CSI_PRDF_MASK (0x7 << 19)
+#define MXC_CCM_PDR2_CSI_PODF_OFFSET 16
+#define MXC_CCM_PDR2_CSI_PODF_MASK (0x7 << 16)
+#define MXC_CCM_PDR2_SSI2_PODF_OFFSET 8
+#define MXC_CCM_PDR2_SSI2_PODF_MASK (0x3F << 8)
+#define MXC_CCM_PDR2_CSI_M_U (1 << 7)
+#define MXC_CCM_PDR2_SSI_M_U (1 << 6)
+#define MXC_CCM_PDR2_SSI1_PODF_OFFSET 0
+#define MXC_CCM_PDR2_SSI1_PODF_MASK (0x3F)
+
+/* Extra definitions for Chip Version 2*/
+#define MXC_CCM_PDR2_CSI_PODF_MASK_V2 (0x3F << 16)
+
+#define MXC_CCM_PDR3_SPDIF_PRDF_OFFSET 29
+#define MXC_CCM_PDR3_SPDIF_PRDF_MASK (0x7 << 29)
+#define MXC_CCM_PDR3_SPDIF_PODF_OFFSET 23
+#define MXC_CCM_PDR3_SPDIF_PODF_MASK (0x3F << 23)
+#define MXC_CCM_PDR3_SPDIF_M_U (1 << 22)
+#define MXC_CCM_PDR3_ESDHC3_PRDF_OFFSET 19
+#define MXC_CCM_PDR3_ESDHC3_PRDF_MASK (0x7 << 19)
+#define MXC_CCM_PDR3_ESDHC3_PODF_OFFSET 16
+#define MXC_CCM_PDR3_ESDHC3_PODF_MASK (0x7 << 16)
+#define MXC_CCM_PDR3_UART_M_U (1 << 15)
+#define MXC_CCM_PDR3_ESDHC2_PRDF_OFFSET 11
+#define MXC_CCM_PDR3_ESDHC2_PRDF_MASK (0x7 << 11)
+#define MXC_CCM_PDR3_ESDHC2_PODF_OFFSET 8
+#define MXC_CCM_PDR3_ESDHC2_PODF_MASK (0x7 << 8)
+#define MXC_CCM_PDR3_ESDHC_M_U (1 << 6)
+#define MXC_CCM_PDR3_ESDHC1_PRDF_OFFSET 3
+#define MXC_CCM_PDR3_ESDHC1_PRDF_MASK (0x7 << 3)
+#define MXC_CCM_PDR3_ESDHC1_PODF_OFFSET 0
+#define MXC_CCM_PDR3_ESDHC1_PODF_MASK (0x7)
+
+/* Extra definitions for Chip Version 2 */
+#define MXC_CCM_PDR3_ESDHC3_PODF_MASK_V2 (0x3F << 16)
+#define MXC_CCM_PDR3_ESDHC2_PODF_MASK_V2 (0x3F << 8)
+#define MXC_CCM_PDR3_ESDHC1_PODF_MASK_V2 0x3F
+
+#define MXC_CCM_PDR4_NFC_PODF_OFFSET 28
+#define MXC_CCM_PDR4_NFC_PODF_MASK (0xF << 28)
+#define MXC_CCM_PDR4_USB_PRDF_OFFSET 25
+#define MXC_CCM_PDR4_USB_PRDF_MASK (0x7 << 25)
+#define MXC_CCM_PDR4_USB_PODF_OFFSET 22
+#define MXC_CCM_PDR4_USB_PODF_MASK (0x7 << 22)
+#define MXC_CCM_PDR4_PER0_PRDF_OFFSET 19
+#define MXC_CCM_PDR4_PER0_PRDF_MASK (0x7 << 19)
+#define MXC_CCM_PDR4_PER0_PODF_OFFSET 16
+#define MXC_CCM_PDR4_PER0_PODF_MASK (0x7 << 16)
+#define MXC_CCM_PDR4_UART_PRDF_OFFSET 13
+#define MXC_CCM_PDR4_UART_PRDF_MASK (0x7 << 13)
+#define MXC_CCM_PDR4_UART_PODF_OFFSET 10
+#define MXC_CCM_PDR4_UART_PODF_MASK (0x7 << 10)
+#define MXC_CCM_PDR4_USB_M_U (1 << 9)
+
+/* Extra definitions for Chip Version 2 */
+#define MXC_CCM_PDR4_USB_PODF_MASK_V2 (0x3F << 22)
+#define MXC_CCM_PDR4_PER0_PODF_MASK_V2 (0x3F << 16)
+#define MXC_CCM_PDR4_UART_PODF_MASK_V2 (0x3F << 10)
+
+/* Bit definitions for RCSR */
+#define MXC_CCM_RCSR_BUS_WIDTH (1 << 29)
+#define MXC_CCM_RCSR_BUS_16BIT (1 << 29)
+#define MXC_CCM_RCSR_PAGE_SIZE (3 << 27)
+#define MXC_CCM_RCSR_PAGE_512 (0 << 27)
+#define MXC_CCM_RCSR_PAGE_2K (1 << 27)
+#define MXC_CCM_RCSR_PAGE_4K1 (2 << 27)
+#define MXC_CCM_RCSR_PAGE_4K2 (3 << 27)
+#define MXC_CCM_RCSR_SOFT_RESET (1 << 15)
+#define MXC_CCM_RCSR_NF16B (1 << 14)
+#define MXC_CCM_RCSR_NFC_4K (1 << 9)
+#define MXC_CCM_RCSR_NFC_FMS (1 << 8)
+
+/* Bit definitions for both MCU, PERIPHERAL PLL control registers */
+#define MXC_CCM_PCTL_BRM 0x80000000
+#define MXC_CCM_PCTL_PD_OFFSET 26
+#define MXC_CCM_PCTL_PD_MASK (0xF << 26)
+#define MXC_CCM_PCTL_MFD_OFFSET 16
+#define MXC_CCM_PCTL_MFD_MASK (0x3FF << 16)
+#define MXC_CCM_PCTL_MFI_OFFSET 10
+#define MXC_CCM_PCTL_MFI_MASK (0xF << 10)
+#define MXC_CCM_PCTL_MFN_OFFSET 0
+#define MXC_CCM_PCTL_MFN_MASK 0x3FF
+
+/* Bit definitions for Audio clock mux register*/
+#define MXC_CCM_ACMR_ESAI_CLK_SEL_OFFSET 12
+#define MXC_CCM_ACMR_ESAI_CLK_SEL_MASK (0xF << 12)
+#define MXC_CCM_ACMR_SPDIF_CLK_SEL_OFFSET 8
+#define MXC_CCM_ACMR_SPDIF_CLK_SEL_MASK (0xF << 8)
+#define MXC_CCM_ACMR_SSI1_CLK_SEL_OFFSET 4
+#define MXC_CCM_ACMR_SSI1_CLK_SEL_MASK (0xF << 4)
+#define MXC_CCM_ACMR_SSI2_CLK_SEL_OFFSET 0
+#define MXC_CCM_ACMR_SSI2_CLK_SEL_MASK (0xF << 0)
+
+/* Extra definitions for Version 2 */
+#define MXC_CCM_ACMR_CKILH_PODF0_OFFSET 16
+#define MXC_CCM_ACMR_CKILH_PODF1_OFFSET 19
+#define MXC_CCM_ACMR_CKILH_PODF2_OFFSET 22
+#define MXC_CCM_ACMR_CKILH_PODF3_OFFSET 25
+#define MXC_CCM_ACMR_CKILH_PODF_MASK 0x7
+
+/* Bit definitions for Clock gating Register*/
+#define MXC_CCM_CGR0_ASRC_OFFSET 0
+#define MXC_CCM_CGR0_ASRC_MASK (0x3 << 0)
+#define MXC_CCM_CGR0_ATA_OFFSET 2
+#define MXC_CCM_CGR0_ATA_MASK (0x3 << 2)
+#define MXC_CCM_CGR0_CAN1_OFFSET 6
+#define MXC_CCM_CGR0_CAN1_MASK (0x3 << 6)
+#define MXC_CCM_CGR0_CAN2_OFFSET 8
+#define MXC_CCM_CGR0_CAN2_MASK (0x3 << 8)
+#define MXC_CCM_CGR0_CSPI1_OFFSET 10
+#define MXC_CCM_CGR0_CSPI1_MASK (0x3 << 10)
+#define MXC_CCM_CGR0_CSPI2_OFFSET 12
+#define MXC_CCM_CGR0_CSPI2_MASK (0x3 << 12)
+#define MXC_CCM_CGR0_ECT_OFFSET 14
+#define MXC_CCM_CGR0_ECT_MASK (0x3 << 14)
+#define MXC_CCM_CGR0_EMI_OFFSET 18
+#define MXC_CCM_CGR0_EMI_MASK (0x3 << 18)
+#define MXC_CCM_CGR0_EPIT1_OFFSET 20
+#define MXC_CCM_CGR0_EPIT1_MASK (0x3 << 20)
+#define MXC_CCM_CGR0_EPIT2_OFFSET 22
+#define MXC_CCM_CGR0_EPIT2_MASK (0x3 << 22)
+#define MXC_CCM_CGR0_ESAI_OFFSET 24
+#define MXC_CCM_CGR0_ESAI_MASK (0x3 << 24)
+#define MXC_CCM_CGR0_ESDHC1_OFFSET 26
+#define MXC_CCM_CGR0_ESDHC1_MASK (0x3 << 26)
+#define MXC_CCM_CGR0_ESDHC2_OFFSET 28
+#define MXC_CCM_CGR0_ESDHC2_MASK (0x3 << 28)
+#define MXC_CCM_CGR0_ESDHC3_OFFSET 30
+#define MXC_CCM_CGR0_ESDHC3_MASK (0x3 << 30)
+
+#define MXC_CCM_CGR1_FEC_OFFSET 0
+#define MXC_CCM_CGR1_FEC_MASK (0x3 << 0)
+#define MXC_CCM_CGR1_GPIO1_OFFSET 2
+#define MXC_CCM_CGR1_GPIO1_MASK (0x3 << 2)
+#define MXC_CCM_CGR1_GPIO2_OFFSET 4
+#define MXC_CCM_CGR1_GPIO2_MASK (0x3 << 4)
+#define MXC_CCM_CGR1_GPIO3_OFFSET 6
+#define MXC_CCM_CGR1_GPIO3_MASK (0x3 << 6)
+#define MXC_CCM_CGR1_GPT_OFFSET 8
+#define MXC_CCM_CGR1_GPT_MASK (0x3 << 8)
+#define MXC_CCM_CGR1_I2C1_OFFSET 10
+#define MXC_CCM_CGR1_I2C1_MASK (0x3 << 10)
+#define MXC_CCM_CGR1_I2C2_OFFSET 12
+#define MXC_CCM_CGR1_I2C2_MASK (0x3 << 12)
+#define MXC_CCM_CGR1_I2C3_OFFSET 14
+#define MXC_CCM_CGR1_I2C3_MASK (0x3 << 14)
+#define MXC_CCM_CGR1_IOMUXC_OFFSET 16
+#define MXC_CCM_CGR1_IOMUXC_MASK (0x3 << 16)
+#define MXC_CCM_CGR1_IPU_OFFSET 18
+#define MXC_CCM_CGR1_IPU_MASK (0x3 << 18)
+#define MXC_CCM_CGR1_KPP_OFFSET 20
+#define MXC_CCM_CGR1_KPP_MASK (0x3 << 20)
+#define MXC_CCM_CGR1_MLB_OFFSET 22
+#define MXC_CCM_CGR1_MLB_MASK (0x3 << 22)
+#define MXC_CCM_CGR1_MSHC_OFFSET 24
+#define MXC_CCM_CGR1_MSHC_MASK (0x3 << 24)
+#define MXC_CCM_CGR1_OWIRE_OFFSET 26
+#define MXC_CCM_CGR1_OWIRE_MASK (0x3 << 26)
+#define MXC_CCM_CGR1_PWM_OFFSET 28
+#define MXC_CCM_CGR1_PWM_MASK (0x3 << 28)
+#define MXC_CCM_CGR1_RNGC_OFFSET 30
+#define MXC_CCM_CGR1_RNGC_MASK (0x3 << 30)
+
+#define MXC_CCM_CGR2_RTC_OFFSET 0
+#define MXC_CCM_CGR2_RTC_MASK (0x3 << 0)
+#define MXC_CCM_CGR2_RTIC_OFFSET 2
+#define MXC_CCM_CGR2_RTIC_MASK (0x3 << 2)
+#define MXC_CCM_CGR2_SCC_OFFSET 4
+#define MXC_CCM_CGR2_SCC_MASK (0x3 << 4)
+#define MXC_CCM_CGR2_SDMA_OFFSET 6
+#define MXC_CCM_CGR2_SDMA_MASK (0x3 << 6)
+#define MXC_CCM_CGR2_SPBA_OFFSET 8
+#define MXC_CCM_CGR2_SPBA_MASK (0x3 << 8)
+#define MXC_CCM_CGR2_SPDIF_OFFSET 10
+#define MXC_CCM_CGR2_SPDIF_MASK (0x3 << 10)
+#define MXC_CCM_CGR2_SSI1_OFFSET 12
+#define MXC_CCM_CGR2_SSI1_MASK (0x3 << 12)
+#define MXC_CCM_CGR2_SSI2_OFFSET 14
+#define MXC_CCM_CGR2_SSI2_MASK (0x3 << 14)
+#define MXC_CCM_CGR2_UART1_OFFSET 16
+#define MXC_CCM_CGR2_UART1_MASK (0x3 << 16)
+#define MXC_CCM_CGR2_UART2_OFFSET 18
+#define MXC_CCM_CGR2_UART2_MASK (0x3 << 18)
+#define MXC_CCM_CGR2_UART3_OFFSET 20
+#define MXC_CCM_CGR2_UART3_MASK (0x3 << 20)
+#define MXC_CCM_CGR2_USBOTG_OFFSET 22
+#define MXC_CCM_CGR2_USBOTG_MASK (0x3 << 22)
+#define MXC_CCM_CGR2_WDOG_OFFSET 24
+#define MXC_CCM_CGR2_WDOG_MASK (0x3 << 24)
+#define MXC_CCM_CGR2_MAX_OFFSET 26
+#define MXC_CCM_CGR2_MAX_MASK (0x3 << 26)
+#define MXC_CCM_CGR2_MAX_ENABLE (0x2 << 26)
+#define MXC_CCM_CGR2_AUDMUX_OFFSET 30
+#define MXC_CCM_CGR2_AUDMUX_MASK (0x3 << 30)
+
+#define MXC_CCM_CGR3_CSI_OFFSET 0
+#define MXC_CCM_CGR3_CSI_MASK (0x3 << 0)
+#define MXC_CCM_CGR3_IIM_OFFSET 2
+#define MXC_CCM_CGR3_IIM_MASK (0x3 << 2)
+#define MXC_CCM_CGR3_GPU2D_OFFSET 4
+#define MXC_CCM_CGR3_GPU2D_MASK (0x3 << 4)
+/*
+ * LTR0 register offsets
+ */
+#define MXC_CCM_LTR0_DNTHR_OFFSET 16
+#define MXC_CCM_LTR0_DNTHR_MASK (0x3F << 16)
+#define MXC_CCM_LTR0_UPTHR_OFFSET 22
+#define MXC_CCM_LTR0_UPTHR_MASK (0x3F << 22)
+#define MXC_CCM_LTR0_DIV3CK_OFFSET 1
+#define MXC_CCM_LTR0_DIV3CK_MASK (0x3 << 1)
+
+/*
+ * LTR1 register offsets
+ */
+#define MXC_CCM_LTR1_PNCTHR_OFFSET 0
+#define MXC_CCM_LTR1_PNCTHR_MASK 0x3F
+#define MXC_CCM_LTR1_UPCNT_OFFSET 6
+#define MXC_CCM_LTR1_UPCNT_MASK (0xFF << 6)
+#define MXC_CCM_LTR1_DNCNT_OFFSET 14
+#define MXC_CCM_LTR1_DNCNT_MASK (0xFF << 14)
+#define MXC_CCM_LTR1_LTBRSR_MASK 0x400000
+#define MXC_CCM_LTR1_LTBRSR_OFFSET 22
+#define MXC_CCM_LTR1_LTBRSR 0x400000
+#define MXC_CCM_LTR1_LTBRSH 0x800000
+
+/*
+ * LTR2 bit definitions. x ranges from 0 for WSW9 to 6 for WSW15
+ */
+#define MXC_CCM_LTR2_WSW_OFFSET(x) (11 + (x) * 3)
+#define MXC_CCM_LTR2_WSW_MASK(x) (0x7 << MXC_CCM_LTR2_WSW_OFFSET((x)))
+#define MXC_CCM_LTR2_EMAC_OFFSET 0
+#define MXC_CCM_LTR2_EMAC_MASK 0x1FF
+
+/*
+ * LTR3 bit definitions. x ranges from 0 for WSW0 to 8 for WSW8
+ */
+#define MXC_CCM_LTR3_WSW_OFFSET(x) (5 + (x) * 3)
+#define MXC_CCM_LTR3_WSW_MASK(x) (0x7 << MXC_CCM_LTR3_WSW_OFFSET((x)))
+
+#define DVSUP_TURBO 0
+#define DVSUP_HIGH 1
+#define DVSUP_MEDIUM 2
+#define DVSUP_LOW 3
+#define MXC_CCM_PMCR0_DVSUP_TURBO (DVSUP_TURBO << 28)
+#define MXC_CCM_PMCR0_DVSUP_HIGH (DVSUP_HIGH << 28)
+#define MXC_CCM_PMCR0_DVSUP_MEDIUM (DVSUP_MEDIUM << 28)
+#define MXC_CCM_PMCR0_DVSUP_LOW (DVSUP_LOW << 28)
+#define MXC_CCM_PMCR0_DVSUP_OFFSET 28
+#define MXC_CCM_PMCR0_DVSUP_MASK (0x3 << 28)
+#define MXC_CCM_PMCR0_DVFS_UPDATE_FINISH 0x01000000
+#define MXC_CCM_PMCR0_DVFEV 0x00800000
+#define MXC_CCM_PMCR0_DVFIS 0x00400000
+#define MXC_CCM_PMCR0_LBMI 0x00200000
+#define MXC_CCM_PMCR0_LBFL 0x00100000
+#define MXC_CCM_PMCR0_LBCF_4 (0x0 << 18)
+#define MXC_CCM_PMCR0_LBCF_8 (0x1 << 18)
+#define MXC_CCM_PMCR0_LBCF_12 (0x2 << 18)
+#define MXC_CCM_PMCR0_LBCF_16 (0x3 << 18)
+#define MXC_CCM_PMCR0_LBCF_OFFSET 18
+#define MXC_CCM_PMCR0_LBCF_MASK (0x3 << 18)
+#define MXC_CCM_PMCR0_PTVIS 0x00020000
+#define MXC_CCM_PMCR0_DVFS_START 0x00010000
+#define MXC_CCM_PMCR0_DVFS_START_MASK 0x1 << 16)
+#define MXC_CCM_PMCR0_FSVAIM 0x00008000
+#define MXC_CCM_PMCR0_FSVAI_OFFSET 13
+#define MXC_CCM_PMCR0_FSVAI_MASK (0x3 << 13)
+#define MXC_CCM_PMCR0_DPVCR 0x00001000
+#define MXC_CCM_PMCR0_DPVV 0x00000800
+#define MXC_CCM_PMCR0_WFIM 0x00000400
+#define MXC_CCM_PMCR0_DRCE3 0x00000200
+#define MXC_CCM_PMCR0_DRCE2 0x00000100
+#define MXC_CCM_PMCR0_DRCE1 0x00000080
+#define MXC_CCM_PMCR0_DRCE0 0x00000040
+#define MXC_CCM_PMCR0_DCR 0x00000020
+#define MXC_CCM_PMCR0_DVFEN 0x00000010
+#define MXC_CCM_PMCR0_PTVAIM 0x00000008
+#define MXC_CCM_PMCR0_PTVAI_OFFSET 1
+#define MXC_CCM_PMCR0_PTVAI_MASK (0x3 << 1)
+#define MXC_CCM_PMCR0_DPTEN 0x00000001
+
+#define MXC_CCM_PMCR1_DVGP_OFFSET 0
+#define MXC_CCM_PMCR1_DVGP_MASK (0xF)
+
+#define MXC_CCM_PMCR1_PLLRDIS (0x1 << 7)
+#define MXC_CCM_PMCR1_EMIRQ_EN (0x1 << 8)
+
+#define MXC_CCM_DCVR_ULV_MASK (0x3FF << 22)
+#define MXC_CCM_DCVR_ULV_OFFSET 22
+#define MXC_CCM_DCVR_LLV_MASK (0x3FF << 12)
+#define MXC_CCM_DCVR_LLV_OFFSET 12
+#define MXC_CCM_DCVR_ELV_MASK (0x3FF << 2)
+#define MXC_CCM_DCVR_ELV_OFFSET 2
+
+#define MXC_CCM_PDR2_MST2_PDF_MASK (0x3F << 7)
+#define MXC_CCM_PDR2_MST2_PDF_OFFSET 7
+#define MXC_CCM_PDR2_MST1_PDF_MASK 0x3F
+#define MXC_CCM_PDR2_MST1_PDF_OFFSET 0
+
+#define MXC_CCM_COSR_CLKOSEL_MASK 0x1F
+#define MXC_CCM_COSR_CLKOSEL_OFFSET 0
+#define MXC_CCM_COSR_CLKOEN (1 << 5)
+#define MXC_CCM_COSR_CLKOUTDIV_1 (1 << 6)
+#define MXC_CCM_COSR_CLKOUT_PREDIV_MASK (0x7 << 13)
+#define MXC_CCM_COSR_CLKOUT_PREDIV_OFFSET 13
+#define MXC_CCM_COSR_CLKOUT_PRODIV_MASK (0x7 << 10)
+#define MXC_CCM_COSR_CLKOUT_PRODIV_OFFSET 10
+#define MXC_CCM_COSR_SSI1_RX_SRC_SEL_MASK (0x3 << 16)
+#define MXC_CCM_COSR_SSI1_RX_SRC_SEL_OFFSET 16
+#define MXC_CCM_COSR_SSI1_TX_SRC_SEL_MASK (0x3 << 18)
+#define MXC_CCM_COSR_SSI1_TX_SRC_SEL_OFFSET 18
+#define MXC_CCM_COSR_SSI2_RX_SRC_SEL_MASK (0x3 << 20)
+#define MXC_CCM_COSR_SSI2_RX_SRC_SEL_OFFSET 20
+#define MXC_CCM_COSR_SSI2_TX_SRC_SEL_MASK (0x3 << 22)
+#define MXC_CCM_COSR_SSI2_TX_SRC_SEL_OFFSET 22
+#define MXC_CCM_COSR_ASRC_AUDIO_EN (1 << 24)
+#define MXC_CCM_COSR_ASRC_AUDIO_PODF_MASK (0x3F << 26)
+#define MXC_CCM_COSR_ASRC_AUDIO_PODF_OFFSET 26
+
+/* extra definitions for Version 2 */
+#define MXC_CCM_COSR_CKIL_CKIH_MASK (1 << 7)
+#define MXC_CCM_COSR_CKIL_CKIH_OFFSET 7
+#define MXC_CCM_COSR_CLKOUT_PRODIV_MASK_V2 (0x3F << 10)
+
+/*
+ * PMCR0 register offsets
+ */
+#define MXC_CCM_PMCR0_LBFL_OFFSET 20
+#define MXC_CCM_PMCR0_DFSUP0_OFFSET 30
+#define MXC_CCM_PMCR0_DFSUP1_OFFSET 31
+
+/*
+ * PMCR2 register definitions
+ */
+#define MXC_CCM_PMCR2_OSC24M_DOWN (1 << 16)
+#define MXC_CCM_PMCR2_OSC_AUDIO_DOWN (1 << 17)
+
+#endif /* __ARCH_ARM_MACH_MX3_CRM_REGS_H__ */
diff --git a/arch/arm/mach-mx35/devices.c b/arch/arm/mach-mx35/devices.c
new file mode 100644
index 000000000000..457feb7759a6
--- /dev/null
+++ b/arch/arm/mach-mx35/devices.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+
+#include <mach/hardware.h>
+#include <mach/mmc.h>
+#include <mach/spba.h>
+#include <mach/sdma.h>
+
+#include "iomux.h"
+#include "sdma_script_code.h"
+#include "sdma_script_code_v2.h"
+#include "board-mx35_3stack.h"
+
+void mxc_sdma_get_script_info(sdma_script_start_addrs * sdma_script_addr)
+{
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) {
+ sdma_script_addr->mxc_sdma_ap_2_ap_addr = ap_2_ap_ADDR;
+ sdma_script_addr->mxc_sdma_ap_2_bp_addr = -1;
+ sdma_script_addr->mxc_sdma_bp_2_ap_addr = -1;
+ sdma_script_addr->mxc_sdma_loopback_on_dsp_side_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_interrupt_only_addr = -1;
+
+ sdma_script_addr->mxc_sdma_firi_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_firi_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_per_2_firi_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_firi_addr = -1;
+
+ sdma_script_addr->mxc_sdma_uart_2_per_addr = uart_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_uart_2_mcu_addr = uart_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_per_2_app_addr = per_2_app_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_app_addr = mcu_2_app_ADDR;
+
+ sdma_script_addr->mxc_sdma_per_2_per_addr = p_2_p_ADDR;
+
+ sdma_script_addr->mxc_sdma_uartsh_2_per_addr =
+ uartsh_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_uartsh_2_mcu_addr =
+ uartsh_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_per_2_shp_addr = per_2_shp_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_shp_addr = mcu_2_shp_ADDR;
+
+ sdma_script_addr->mxc_sdma_ata_2_mcu_addr = ata_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_ata_addr = mcu_2_ata_ADDR;
+
+ sdma_script_addr->mxc_sdma_app_2_per_addr = app_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_app_2_mcu_addr = app_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_shp_2_per_addr = shp_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_shp_2_mcu_addr = shp_2_mcu_ADDR;
+
+ sdma_script_addr->mxc_sdma_mshc_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_mshc_addr = -1;
+
+ sdma_script_addr->mxc_sdma_spdif_2_mcu_addr = spdif_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_spdif_addr = mcu_2_spdif_ADDR;
+
+ sdma_script_addr->mxc_sdma_asrc_2_mcu_addr = asrc__mcu_ADDR;
+
+ sdma_script_addr->mxc_sdma_dptc_dvfs_addr = -1;
+ sdma_script_addr->mxc_sdma_ext_mem_2_ipu_addr =
+ ext_mem__ipu_ram_ADDR;
+ sdma_script_addr->mxc_sdma_descrambler_addr = -1;
+
+ sdma_script_addr->mxc_sdma_start_addr =
+ (unsigned short *)sdma_code;
+ sdma_script_addr->mxc_sdma_ram_code_size = RAM_CODE_SIZE;
+ sdma_script_addr->mxc_sdma_ram_code_start_addr =
+ RAM_CODE_START_ADDR;
+ } else {
+ sdma_script_addr->mxc_sdma_ap_2_ap_addr = ap_2_ap_ADDR_V2;
+ sdma_script_addr->mxc_sdma_ap_2_bp_addr = -1;
+ sdma_script_addr->mxc_sdma_bp_2_ap_addr = -1;
+ sdma_script_addr->mxc_sdma_loopback_on_dsp_side_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_interrupt_only_addr = -1;
+
+ sdma_script_addr->mxc_sdma_firi_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_firi_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_per_2_firi_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_firi_addr = -1;
+
+ sdma_script_addr->mxc_sdma_uart_2_per_addr = uart_2_per_ADDR_V2;
+ sdma_script_addr->mxc_sdma_uart_2_mcu_addr = uart_2_mcu_ADDR_V2;
+ sdma_script_addr->mxc_sdma_per_2_app_addr = per_2_app_ADDR_V2;
+ sdma_script_addr->mxc_sdma_mcu_2_app_addr = mcu_2_app_ADDR_V2;
+
+ sdma_script_addr->mxc_sdma_per_2_per_addr = p_2_p_ADDR_V2;
+
+ sdma_script_addr->mxc_sdma_uartsh_2_per_addr =
+ uartsh_2_per_ADDR_V2;
+ sdma_script_addr->mxc_sdma_uartsh_2_mcu_addr =
+ uartsh_2_mcu_ADDR_V2;
+ sdma_script_addr->mxc_sdma_per_2_shp_addr = per_2_shp_ADDR_V2;
+ sdma_script_addr->mxc_sdma_mcu_2_shp_addr = mcu_2_shp_ADDR_V2;
+
+ sdma_script_addr->mxc_sdma_ata_2_mcu_addr = ata_2_mcu_ADDR_V2;
+ sdma_script_addr->mxc_sdma_mcu_2_ata_addr = mcu_2_ata_ADDR_V2;
+
+ sdma_script_addr->mxc_sdma_app_2_per_addr = app_2_per_ADDR_V2;
+ sdma_script_addr->mxc_sdma_app_2_mcu_addr = app_2_mcu_ADDR_V2;
+ sdma_script_addr->mxc_sdma_shp_2_per_addr = shp_2_per_ADDR_V2;
+ sdma_script_addr->mxc_sdma_shp_2_mcu_addr = shp_2_mcu_ADDR_V2;
+
+ sdma_script_addr->mxc_sdma_mshc_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_mshc_addr = -1;
+
+ sdma_script_addr->mxc_sdma_spdif_2_mcu_addr =
+ spdif_2_mcu_ADDR_V2;
+ sdma_script_addr->mxc_sdma_mcu_2_spdif_addr =
+ mcu_2_spdif_ADDR_V2;
+
+ sdma_script_addr->mxc_sdma_asrc_2_mcu_addr = asrc__mcu_ADDR_V2;
+
+ sdma_script_addr->mxc_sdma_dptc_dvfs_addr = -1;
+ sdma_script_addr->mxc_sdma_ext_mem_2_ipu_addr =
+ ext_mem__ipu_ram_ADDR_V2;
+ sdma_script_addr->mxc_sdma_descrambler_addr = -1;
+
+ sdma_script_addr->mxc_sdma_start_addr =
+ (unsigned short *)sdma_code_v2;
+ sdma_script_addr->mxc_sdma_ram_code_size = RAM_CODE_SIZE;
+ sdma_script_addr->mxc_sdma_ram_code_start_addr =
+ RAM_CODE_START_ADDR_V2;
+ }
+}
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+#if defined(CONFIG_RTC_MXC) || defined(CONFIG_RTC_MXC_MODULE)
+static struct resource rtc_resources[] = {
+ {
+ .start = RTC_BASE_ADDR,
+ .end = RTC_BASE_ADDR + 0x30,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device mxc_rtc_device = {
+ .name = "mxc_rtc",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(rtc_resources),
+ .resource = rtc_resources,
+};
+static void mxc_init_rtc(void)
+{
+ (void)platform_device_register(&mxc_rtc_device);
+}
+#else
+static inline void mxc_init_rtc(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_MC9SDZ60_RTC) || defined(CONFIG_MXC_MC9SDZ60_RTC_MODULE)
+static struct resource pmic_rtc_resources[] = {
+ {
+ .start = MXC_PSEUDO_IRQ_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device pmic_rtc_device = {
+ .name = "pmic_rtc",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(pmic_rtc_resources),
+ .resource = pmic_rtc_resources,
+};
+static void pmic_init_rtc(void)
+{
+ platform_device_register(&pmic_rtc_device);
+}
+#else
+static void pmic_init_rtc(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_WATCHDOG) || defined(CONFIG_MXC_WATCHDOG_MODULE)
+static struct resource wdt_resources[] = {
+ {
+ .start = WDOG1_BASE_ADDR,
+ .end = WDOG1_BASE_ADDR + 0x30,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_wdt_device = {
+ .name = "mxc_wdt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(wdt_resources),
+ .resource = wdt_resources,
+};
+
+static void mxc_init_wdt(void)
+{
+ (void)platform_device_register(&mxc_wdt_device);
+}
+#else
+static inline void mxc_init_wdt(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_IPU) || defined(CONFIG_MXC_IPU_MODULE)
+static struct mxc_ipu_config mxc_ipu_data = {
+ .rev = 2,
+};
+
+static struct resource ipu_resources[] = {
+ {
+ .start = IPU_CTRL_BASE_ADDR,
+ .end = IPU_CTRL_BASE_ADDR + SZ_4K,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_IPU_SYN,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MXC_INT_IPU_ERR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mxc_ipu_device = {
+ .name = "mxc_ipu",
+ .id = -1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_ipu_data,
+ },
+ .num_resources = ARRAY_SIZE(ipu_resources),
+ .resource = ipu_resources,
+};
+
+static void mxc_init_ipu(void)
+{
+ platform_device_register(&mxc_ipu_device);
+}
+#else
+static inline void mxc_init_ipu(void)
+{
+}
+#endif
+
+/* SPI controller and device data */
+#if defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE)
+
+#ifdef CONFIG_SPI_MXC_SELECT1
+/*!
+ * Resource definition for the CSPI1
+ */
+static struct resource mxcspi1_resources[] = {
+ [0] = {
+ .start = CSPI1_BASE_ADDR,
+ .end = CSPI1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI1,
+ .end = MXC_INT_CSPI1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI1 */
+static struct mxc_spi_master mxcspi1_data = {
+ .maxchipselect = 4,
+ .spi_version = 7,
+};
+
+/*! Device Definition for MXC CSPI1 */
+static struct platform_device mxcspi1_device = {
+ .name = "mxc_spi",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi1_resources),
+ .resource = mxcspi1_resources,
+};
+
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+
+#ifdef CONFIG_SPI_MXC_SELECT2
+/*!
+ * Resource definition for the CSPI2
+ */
+static struct resource mxcspi2_resources[] = {
+ [0] = {
+ .start = CSPI2_BASE_ADDR,
+ .end = CSPI2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI2,
+ .end = MXC_INT_CSPI2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI2 */
+static struct mxc_spi_master mxcspi2_data = {
+ .maxchipselect = 4,
+ .spi_version = 7,
+};
+
+/*! Device Definition for MXC CSPI2 */
+static struct platform_device mxcspi2_device = {
+ .name = "mxc_spi",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi2_resources),
+ .resource = mxcspi2_resources,
+};
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+
+static inline void mxc_init_spi(void)
+{
+ /* SPBA configuration for CSPI2 - MCU is set */
+ spba_take_ownership(SPBA_CSPI2, SPBA_MASTER_A);
+#ifdef CONFIG_SPI_MXC_SELECT1
+ if (platform_device_register(&mxcspi1_device) < 0)
+ printk(KERN_ERR "Error: Registering the SPI Controller_1\n");
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+#ifdef CONFIG_SPI_MXC_SELECT2
+ if (platform_device_register(&mxcspi2_device) < 0)
+ printk(KERN_ERR "Error: Registering the SPI Controller_2\n");
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+}
+#else
+static inline void mxc_init_spi(void)
+{
+}
+#endif
+
+/* I2C controller and device data */
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+/*!
+ * Resource definition for the I2C1
+ */
+static struct resource mxci2c1_resources[] = {
+ [0] = {
+ .start = I2C_BASE_ADDR,
+ .end = I2C_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C,
+ .end = MXC_INT_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c1_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+/*!
+ * Resource definition for the I2C2
+ */
+static struct resource mxci2c2_resources[] = {
+ [0] = {
+ .start = I2C2_BASE_ADDR,
+ .end = I2C2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C2,
+ .end = MXC_INT_I2C2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c2_data = {
+ .i2c_clk = 100000,
+};
+
+#ifdef CONFIG_I2C_MXC_SELECT3
+/*!
+ * Resource definition for the I2C3
+ */
+static struct resource mxci2c3_resources[] = {
+ [0] = {
+ .start = I2C3_BASE_ADDR,
+ .end = I2C3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C3,
+ .end = MXC_INT_I2C3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c3_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+/*! Device Definition for MXC I2C1 */
+static struct platform_device mxci2c_devices[] = {
+#ifdef CONFIG_I2C_MXC_SELECT1
+ {
+ .name = "mxc_i2c",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c1_resources),
+ .resource = mxci2c1_resources,},
+#endif
+ {
+ .name = "mxc_i2c_slave",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c2_resources),
+ .resource = mxci2c2_resources,},
+#ifdef CONFIG_I2C_MXC_SELECT3
+ {
+ .name = "mxc_i2c",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c3_resources),
+ .resource = mxci2c3_resources,},
+#endif
+};
+
+static inline void mxc_init_i2c(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxci2c_devices); i++) {
+ if (platform_device_register(&mxci2c_devices[i]) < 0)
+ dev_err(&mxci2c_devices[i].dev,
+ "Unable to register I2C device\n");
+ }
+}
+#else
+static inline void mxc_init_i2c(void)
+{
+}
+#endif
+
+struct mxc_gpio_port mxc_gpio_ports[GPIO_PORT_NUM] = {
+ [0] = {
+ .chip.label = "gpio-0",
+ .base = IO_ADDRESS(GPIO1_BASE_ADDR),
+ .irq = MXC_INT_GPIO1,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE
+ },
+ [1] = {
+ .chip.label = "gpio-1",
+ .base = IO_ADDRESS(GPIO2_BASE_ADDR),
+ .irq = MXC_INT_GPIO2,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN
+ },
+ [2] = {
+ .chip.label = "gpio-2",
+ .base = IO_ADDRESS(GPIO3_BASE_ADDR),
+ .irq = MXC_INT_GPIO3,
+ .irq_high = 0,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 2
+ }
+};
+
+int __init mxc_register_gpios(void)
+{
+ return mxc_gpio_init(mxc_gpio_ports, ARRAY_SIZE(mxc_gpio_ports));
+}
+
+static struct platform_device mxc_dma_device = {
+ .name = "mxc_dma",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline void mxc_init_dma(void)
+{
+ (void)platform_device_register(&mxc_dma_device);
+}
+
+static struct resource spdif_resources[] = {
+ {
+ .start = SPDIF_BASE_ADDR,
+ .end = SPDIF_BASE_ADDR + 0x50,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct mxc_spdif_platform_data mxc_spdif_data = {
+ .spdif_tx = 1,
+ .spdif_rx = 1,
+ .spdif_clk_44100 = 3, /* spdif_ext_clk source for 44.1KHz */
+ .spdif_clk_48000 = 0, /* audio osc source */
+ .spdif_clkid = 0,
+ .spdif_clk = NULL, /* spdif bus clk */
+};
+
+static struct platform_device mxc_alsa_spdif_device = {
+ .name = "mxc_alsa_spdif",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_spdif_data,
+ },
+ .num_resources = ARRAY_SIZE(spdif_resources),
+ .resource = spdif_resources,
+};
+
+static inline void mxc_init_spdif(void)
+{
+ mxc_spdif_data.spdif_clk = clk_get(NULL, "spdif_ipg_clk");
+ clk_put(mxc_spdif_data.spdif_clk);
+ mxc_spdif_data.spdif_core_clk = clk_get(NULL, "spdif_clk");
+ clk_put(mxc_spdif_data.spdif_core_clk);
+ mxc_spdif_data.spdif_audio_clk = clk_get(NULL, "spdif_audio_clk");
+ clk_put(mxc_spdif_data.spdif_audio_clk);
+ platform_device_register(&mxc_alsa_spdif_device);
+}
+
+static struct platform_device mxc_alsa_surround_device = {
+ .name = "imx-3stack-wm8580",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static void mxc_init_surround_audio(void)
+{
+ platform_device_register(&mxc_alsa_surround_device);
+}
+
+static struct mxc_audio_platform_data mxc_bt_audio_data;
+
+static struct platform_device mxc_bt_alsa_device = {
+ .name = "imx-3stack-bt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_bt_audio_data,
+ },
+
+};
+
+static void mxc_init_bt_audio(void)
+{
+ mxc_bt_audio_data.src_port = 2;
+ mxc_bt_audio_data.ext_port = 5;
+ mxc_bt_audio_data.ext_ram = 1;
+ platform_device_register(&mxc_bt_alsa_device);
+}
+
+static struct resource asrc_resources[] = {
+ {
+ .start = ASRC_BASE_ADDR,
+ .end = ASRC_BASE_ADDR + 0x9C,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct mxc_asrc_platform_data mxc_asrc_data;
+
+static struct platform_device mxc_asrc_device = {
+ .name = "mxc_asrc",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_asrc_data,
+ },
+ .num_resources = ARRAY_SIZE(asrc_resources),
+ .resource = asrc_resources,
+};
+
+static inline void mxc_init_asrc(void)
+{
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1)
+ mxc_asrc_data.channel_bits = 3;
+ else
+ mxc_asrc_data.channel_bits = 4;
+
+ mxc_asrc_data.asrc_core_clk = clk_get(NULL, "asrc_clk");
+ clk_put(mxc_asrc_data.asrc_core_clk);
+ mxc_asrc_data.asrc_audio_clk = clk_get(NULL, "asrc_audio_clk");
+ clk_set_rate(mxc_asrc_data.asrc_audio_clk, 768000);
+ clk_put(mxc_asrc_data.asrc_audio_clk);
+ platform_device_register(&mxc_asrc_device);
+}
+
+#if defined(CONFIG_CAN_FLEXCAN) || defined(CONFIG_CAN_FLEXCAN_MODULE)
+
+static struct resource flexcan1_resources[] = {
+ {
+ .start = CAN1_BASE_ADDR,
+ .end = CAN1_BASE_ADDR + 0x97F,
+ .flags = IORESOURCE_MEM,},
+ {
+ .start = MXC_INT_CAN1,
+ .end = MXC_INT_CAN1,
+ .flags = IORESOURCE_IRQ,}
+};
+
+static struct resource flexcan2_resources[] = {
+ {
+ .start = CAN2_BASE_ADDR,
+ .end = CAN2_BASE_ADDR + 0x97F,
+ .flags = IORESOURCE_MEM,},
+ {
+ .start = MXC_INT_CAN2,
+ .end = MXC_INT_CAN2,
+ .flags = IORESOURCE_IRQ,}
+};
+
+static struct platform_device flexcan_devices[] = {
+ {
+ .name = "FlexCAN",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &flexcan_data[0],
+ },
+ .num_resources = ARRAY_SIZE(flexcan1_resources),
+ .resource = flexcan1_resources,},
+ {
+ .name = "FlexCAN",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &flexcan_data[1],
+ },
+ .num_resources = ARRAY_SIZE(flexcan2_resources),
+ .resource = flexcan2_resources,},
+};
+
+static inline void mxc_init_flexcan(void)
+{
+ platform_device_register(&flexcan_devices[0]);
+ platform_device_register(&flexcan_devices[1]);
+}
+#else
+static inline void mxc_init_flexcan(void)
+{
+}
+#endif
+
+#if defined(CONFIG_HW_RANDOM_FSL_RNGC) || \
+defined(CONFIG_HW_RANDOM_FSL_RNGC_MODULE)
+static struct resource rngc_resources[] = {
+ {
+ .start = RNGC_BASE_ADDR,
+ .end = RNGC_BASE_ADDR + 0x34,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_RNG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device fsl_rngc_device = {
+ .name = "fsl_rngc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(rngc_resources),
+ .resource = rngc_resources,
+};
+
+static inline void mxc_init_rngc(void)
+{
+ platform_device_register(&fsl_rngc_device);
+}
+#else
+static inline void mxc_init_rngc(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_IIM) || defined(CONFIG_MXC_IIM_MODULE)
+static struct resource mxc_iim_resources[] = {
+ {
+ .start = IIM_BASE_ADDR,
+ .end = IIM_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_iim_device = {
+ .name = "mxc_iim",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(mxc_iim_resources),
+ .resource = mxc_iim_resources
+};
+
+static inline void mxc_init_iim(void)
+{
+ if (platform_device_register(&mxc_iim_device) < 0)
+ dev_err(&mxc_iim_device.dev,
+ "Unable to register mxc iim device\n");
+}
+#else
+static inline void mxc_init_iim(void)
+{
+}
+#endif
+
+static struct resource mxc_gpu_resources[] = {
+ {
+ .start = MXC_INT_GPU2D,
+ .end = MXC_INT_GPU2D,
+ .name = "gpu_2d_irq",
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device gpu_device = {
+ .name = "mxc_gpu",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(mxc_gpu_resources),
+ .resource = mxc_gpu_resources,
+};
+
+static void __init mxc_init_gpu(void)
+{
+ platform_device_register(&gpu_device);
+}
+
+int __init mxc_init_devices(void)
+{
+ mxc_init_wdt();
+ mxc_init_ipu();
+ mxc_init_spi();
+ mxc_init_i2c();
+ pmic_init_rtc();
+ mxc_init_rtc();
+ mxc_init_dma();
+ mxc_init_bt_audio();
+ mxc_init_spdif();
+ mxc_init_surround_audio();
+ mxc_init_asrc();
+ mxc_init_flexcan();
+ mxc_init_rngc();
+ mxc_init_iim();
+ mxc_init_gpu();
+
+ /* SPBA configuration for SSI2 - SDMA and MCU are set */
+ spba_take_ownership(SPBA_SSI2, SPBA_MASTER_C | SPBA_MASTER_A);
+ return 0;
+}
diff --git a/arch/arm/mach-mx35/dma.c b/arch/arm/mach-mx35/dma.c
new file mode 100644
index 000000000000..ef7f557d1cc8
--- /dev/null
+++ b/arch/arm/mach-mx35/dma.c
@@ -0,0 +1,1046 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/device.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+
+#include "serial.h"
+
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+#define soc_trans_type int_2_per
+#else
+#define soc_trans_type emi_2_per
+#endif
+
+#define MXC_SPDIF_TXFIFO_WML 8
+#define MXC_SPDIF_RXFIFO_WML 8
+#define MXC_SPDIF_TX_REG 0x2C
+#define MXC_SPDIF_RX_REG 0x14
+
+#define MXC_SSI_TX0_REG 0x0
+#define MXC_SSI_TX1_REG 0x4
+#define MXC_SSI_RX0_REG 0x8
+#define MXC_SSI_RX1_REG 0xC
+#define MXC_SSI_TXFIFO_WML 0x4
+#define MXC_SSI_RXFIFO_WML 0x6
+
+#define MXC_ASRC_FIFO_WML 0x40
+#define MXC_ASRCA_RX_REG 0x60
+#define MXC_ASRCA_TX_REG 0x64
+#define MXC_ASRCB_RX_REG 0x68
+#define MXC_ASRCB_TX_REG 0x6C
+#define MXC_ASRCC_RX_REG 0x70
+#define MXC_ASRCC_TX_REG 0x74
+
+#define MXC_ESAI_TX_REG 0x00
+#define MXC_ESAI_RX_REG 0x04
+#define MXC_ESAI_FIFO_WML 0x40
+
+struct mxc_sdma_info_entry_s {
+ mxc_dma_device_t device;
+ void *chnl_info;
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart1_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART1_UFCR_RXTL,
+ .per_address = UART1_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART1_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART1_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart1_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART1_UFCR_TXTL,
+ .per_address = UART1_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART1_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART1_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart2_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART2_UFCR_RXTL,
+ .per_address = UART2_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART2_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART2_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart2_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART2_UFCR_TXTL,
+ .per_address = UART2_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART2_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART2_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart3_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART3_UFCR_RXTL,
+ .per_address = UART3_BASE_ADDR,
+ .peripheral_type = UART_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART3_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART3_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart3_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART3_UFCR_TXTL,
+ .per_address = UART3_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART3_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART3_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_spdif_16bit_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SPDIF_TXFIFO_WML,
+ .per_address = SPDIF_BASE_ADDR + MXC_SPDIF_TX_REG,
+ .peripheral_type = SPDIF,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SPDIF_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SPDIF_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_spdif_32bit_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SPDIF_TXFIFO_WML,
+ .per_address = SPDIF_BASE_ADDR + MXC_SPDIF_TX_REG,
+ .peripheral_type = SPDIF,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SPDIF_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SPDIF_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_spdif_32bit_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SPDIF_RXFIFO_WML,
+ .per_address = SPDIF_BASE_ADDR + MXC_SPDIF_RX_REG,
+ .peripheral_type = SPDIF,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SPDIF_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SPDIF_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_memory_params = {
+ .chnl_params = {
+ .peripheral_type = MEMORY,
+ .transfer_type = emi_2_emi,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MEMORY,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_asrca_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ASRC_FIFO_WML,
+ .per_address = ASRC_BASE_ADDR + MXC_ASRCA_RX_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_ASRC_DMA1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCA_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_asrca_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ASRC_FIFO_WML,
+ .per_address = ASRC_BASE_ADDR + MXC_ASRCA_TX_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_ASRC_DMA4,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCA_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_asrcb_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ASRC_FIFO_WML,
+ .per_address = ASRC_BASE_ADDR + MXC_ASRCB_RX_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_ASRC_DMA2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCB_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_asrcb_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ASRC_FIFO_WML,
+ .per_address = ASRC_BASE_ADDR + MXC_ASRCB_TX_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_ASRC_DMA5,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCB_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_asrcc_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ASRC_FIFO_WML * 3,
+ .per_address = ASRC_BASE_ADDR + MXC_ASRCC_RX_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_ASRC_DMA3,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCC_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_asrcc_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ASRC_FIFO_WML * 3,
+ .per_address = ASRC_BASE_ADDR + MXC_ASRCC_TX_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_ASRC_DMA6,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCC_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrca_ssi1_tx0_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .event_id2 = DMA_REQ_ASRC_DMA4,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP,
+ .watermark_level2 = MXC_SSI_TXFIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCA_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCA_SSI1_TX0,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrca_ssi1_tx1_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .event_id2 = DMA_REQ_ASRC_DMA4,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP,
+ .watermark_level2 = MXC_SSI_TXFIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCA_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCA_SSI1_TX1,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrca_ssi2_tx0_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .event_id2 = DMA_REQ_ASRC_DMA4,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP |
+ SDMA_ASRC_P2P_INFO_DP,
+ .watermark_level2 = MXC_SSI_TXFIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCA_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCA_SSI2_TX0,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrca_ssi2_tx1_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .event_id2 = DMA_REQ_ASRC_DMA4,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP |
+ SDMA_ASRC_P2P_INFO_DP,
+ .watermark_level2 = MXC_SSI_TXFIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCA_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCA_SSI2_TX1,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrcb_ssi1_tx0_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .event_id2 = DMA_REQ_ASRC_DMA5,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP,
+ .watermark_level2 = MXC_SSI_TXFIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCB_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCB_SSI1_TX0,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrcb_ssi1_tx1_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .event_id2 = DMA_REQ_ASRC_DMA5,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP,
+ .watermark_level2 = MXC_SSI_TXFIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCB_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCB_SSI1_TX1,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrcb_ssi2_tx0_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .event_id2 = DMA_REQ_ASRC_DMA5,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP |
+ SDMA_ASRC_P2P_INFO_DP,
+ .watermark_level2 = MXC_SSI_TXFIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCB_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCB_SSI2_TX0,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrcb_ssi2_tx1_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .event_id2 = DMA_REQ_ASRC_DMA5,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP |
+ SDMA_ASRC_P2P_INFO_DP,
+ .watermark_level2 = MXC_SSI_TXFIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCB_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCB_SSI2_TX1,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrca_esai_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ ESAI_BASE_ADDR + MXC_ESAI_TX_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_ESAI_TX,
+ .event_id2 = DMA_REQ_ASRC_DMA4,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP |
+ SDMA_ASRC_P2P_INFO_DP,
+ .watermark_level2 = MXC_ESAI_FIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCA_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCA_ESAI,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrcb_esai_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ ESAI_BASE_ADDR + MXC_ESAI_TX_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_ESAI_TX,
+ .event_id2 = DMA_REQ_ASRC_DMA5,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP |
+ SDMA_ASRC_P2P_INFO_DP,
+ .watermark_level2 = MXC_ESAI_FIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCB_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCB_ESAI,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_ext_params_t mxc_sdma_asrcc_esai_params = {
+ .chnl_ext_params = {
+ .common = {
+ .watermark_level =
+ MXC_ASRC_FIFO_WML >> 1,
+ .per_address =
+ ESAI_BASE_ADDR + MXC_ESAI_TX_REG,
+ .peripheral_type = ASRC,
+ .transfer_type = per_2_per,
+ .event_id = DMA_REQ_ESAI_TX,
+ .event_id2 = DMA_REQ_ASRC_DMA6,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ .ext = 1,
+ },
+ .p2p_dir = 0,
+ .info_bits =
+ SDMA_ASRC_P2P_INFO_CONT | SDMA_ASRC_P2P_INFO_SP |
+ SDMA_ASRC_P2P_INFO_DP,
+ .watermark_level2 = MXC_ASRC_FIFO_WML,
+ .per_address2 = ASRC_BASE_ADDR + MXC_ASRCC_TX_REG,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ASRCC_ESAI,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_esai_16bit_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ESAI_FIFO_WML,
+ .per_address = ESAI_BASE_ADDR + MXC_ESAI_RX_REG,
+ .peripheral_type = ESAI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_ESAI_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ESAI_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_esai_16bit_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ESAI_FIFO_WML,
+ .per_address = ESAI_BASE_ADDR + MXC_ESAI_TX_REG,
+ .peripheral_type = ESAI,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_ESAI_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ESAI_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_esai_24bit_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ESAI_FIFO_WML,
+ .per_address = ESAI_BASE_ADDR + MXC_ESAI_RX_REG,
+ .peripheral_type = ESAI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_ESAI_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ESAI_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_esai_24bit_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_ESAI_FIFO_WML,
+ .per_address = ESAI_BASE_ADDR + MXC_ESAI_TX_REG,
+ .peripheral_type = ESAI,
+ .transfer_type = soc_trans_type,
+ .event_id = DMA_REQ_ESAI_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ESAI_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static struct mxc_sdma_info_entry_s mxc_sdma_active_dma_info[] = {
+ {MXC_DMA_UART1_RX, &mxc_sdma_uart1_rx_params},
+ {MXC_DMA_UART1_TX, &mxc_sdma_uart1_tx_params},
+ {MXC_DMA_UART2_RX, &mxc_sdma_uart2_rx_params},
+ {MXC_DMA_UART2_TX, &mxc_sdma_uart2_tx_params},
+ {MXC_DMA_UART3_RX, &mxc_sdma_uart3_rx_params},
+ {MXC_DMA_UART3_TX, &mxc_sdma_uart3_tx_params},
+ {MXC_DMA_SPDIF_16BIT_TX, &mxc_sdma_spdif_16bit_tx_params},
+ {MXC_DMA_SPDIF_32BIT_TX, &mxc_sdma_spdif_32bit_tx_params},
+ {MXC_DMA_SPDIF_32BIT_RX, &mxc_sdma_spdif_32bit_rx_params},
+ {MXC_DMA_SSI1_8BIT_RX0, &mxc_sdma_ssi1_8bit_rx0_params},
+ {MXC_DMA_SSI1_8BIT_TX0, &mxc_sdma_ssi1_8bit_tx0_params},
+ {MXC_DMA_SSI1_16BIT_RX0, &mxc_sdma_ssi1_16bit_rx0_params},
+ {MXC_DMA_SSI1_16BIT_TX0, &mxc_sdma_ssi1_16bit_tx0_params},
+ {MXC_DMA_SSI1_24BIT_RX0, &mxc_sdma_ssi1_24bit_rx0_params},
+ {MXC_DMA_SSI1_24BIT_TX0, &mxc_sdma_ssi1_24bit_tx0_params},
+ {MXC_DMA_SSI1_8BIT_RX1, &mxc_sdma_ssi1_8bit_rx1_params},
+ {MXC_DMA_SSI1_8BIT_TX1, &mxc_sdma_ssi1_8bit_tx1_params},
+ {MXC_DMA_SSI1_16BIT_RX1, &mxc_sdma_ssi1_16bit_rx1_params},
+ {MXC_DMA_SSI1_16BIT_TX1, &mxc_sdma_ssi1_16bit_tx1_params},
+ {MXC_DMA_SSI1_24BIT_RX1, &mxc_sdma_ssi1_24bit_rx1_params},
+ {MXC_DMA_SSI1_24BIT_TX1, &mxc_sdma_ssi1_24bit_tx1_params},
+ {MXC_DMA_SSI2_8BIT_RX0, &mxc_sdma_ssi2_8bit_rx0_params},
+ {MXC_DMA_SSI2_8BIT_TX0, &mxc_sdma_ssi2_8bit_tx0_params},
+ {MXC_DMA_SSI2_16BIT_RX0, &mxc_sdma_ssi2_16bit_rx0_params},
+ {MXC_DMA_SSI2_16BIT_TX0, &mxc_sdma_ssi2_16bit_tx0_params},
+ {MXC_DMA_SSI2_24BIT_RX0, &mxc_sdma_ssi2_24bit_rx0_params},
+ {MXC_DMA_SSI2_24BIT_TX0, &mxc_sdma_ssi2_24bit_tx0_params},
+ {MXC_DMA_SSI2_8BIT_RX1, &mxc_sdma_ssi2_8bit_rx1_params},
+ {MXC_DMA_SSI2_8BIT_TX1, &mxc_sdma_ssi2_8bit_tx1_params},
+ {MXC_DMA_SSI2_16BIT_RX1, &mxc_sdma_ssi2_16bit_rx1_params},
+ {MXC_DMA_SSI2_16BIT_TX1, &mxc_sdma_ssi2_16bit_tx1_params},
+ {MXC_DMA_SSI2_24BIT_RX1, &mxc_sdma_ssi2_24bit_rx1_params},
+ {MXC_DMA_SSI2_24BIT_TX1, &mxc_sdma_ssi2_24bit_tx1_params},
+ {MXC_DMA_ASRC_A_RX, &mxc_sdma_asrca_rx_params},
+ {MXC_DMA_ASRC_A_TX, &mxc_sdma_asrca_tx_params},
+ {MXC_DMA_ASRC_B_RX, &mxc_sdma_asrcb_rx_params},
+ {MXC_DMA_ASRC_B_TX, &mxc_sdma_asrcb_tx_params},
+ {MXC_DMA_ASRC_C_RX, &mxc_sdma_asrcc_rx_params},
+ {MXC_DMA_ASRC_C_TX, &mxc_sdma_asrcc_tx_params},
+ {MXC_DMA_ASRCA_SSI1_TX0, &mxc_sdma_asrca_ssi1_tx0_params},
+ {MXC_DMA_ASRCA_SSI1_TX1, &mxc_sdma_asrca_ssi1_tx1_params},
+ {MXC_DMA_ASRCA_SSI2_TX0, &mxc_sdma_asrca_ssi2_tx0_params},
+ {MXC_DMA_ASRCA_SSI2_TX1, &mxc_sdma_asrca_ssi2_tx1_params},
+ {MXC_DMA_ASRCB_SSI1_TX0, &mxc_sdma_asrcb_ssi1_tx0_params},
+ {MXC_DMA_ASRCB_SSI1_TX1, &mxc_sdma_asrcb_ssi1_tx1_params},
+ {MXC_DMA_ASRCB_SSI2_TX0, &mxc_sdma_asrcb_ssi2_tx0_params},
+ {MXC_DMA_ASRCB_SSI2_TX1, &mxc_sdma_asrcb_ssi2_tx1_params},
+ {MXC_DMA_ASRCA_ESAI, &mxc_sdma_asrca_esai_params},
+ {MXC_DMA_ASRCB_ESAI, &mxc_sdma_asrcb_esai_params},
+ {MXC_DMA_ASRCC_ESAI, &mxc_sdma_asrcc_esai_params},
+ {MXC_DMA_ESAI_16BIT_RX, &mxc_sdma_esai_16bit_rx_params},
+ {MXC_DMA_ESAI_16BIT_TX, &mxc_sdma_esai_16bit_tx_params},
+ {MXC_DMA_ESAI_24BIT_RX, &mxc_sdma_esai_24bit_rx_params},
+ {MXC_DMA_ESAI_24BIT_TX, &mxc_sdma_esai_24bit_tx_params},
+ {MXC_DMA_MEMORY, &mxc_sdma_memory_params},
+};
+
+static int mxc_sdma_info_entrys =
+ sizeof(mxc_sdma_active_dma_info) / sizeof(mxc_sdma_active_dma_info[0]);
+/*!
+ * This functions Returns the SDMA paramaters associated for a module
+ *
+ * @param channel_id the ID of the module requesting DMA
+ * @return returns the sdma parameters structure for the device
+ */
+mxc_sdma_channel_params_t *mxc_sdma_get_channel_params(mxc_dma_device_t
+ channel_id)
+{
+ struct mxc_sdma_info_entry_s *p = mxc_sdma_active_dma_info;
+ int i;
+
+ for (i = 0; i < mxc_sdma_info_entrys; i++, p++) {
+ if (p->device == channel_id)
+ return p->chnl_info;
+ }
+ return NULL;
+}
+
+EXPORT_SYMBOL(mxc_sdma_get_channel_params);
+
+/*!
+ * This functions marks the SDMA channels that are statically allocated
+ *
+ * @param chnl the channel array used to store channel information
+ */
+void mxc_get_static_channels(mxc_dma_channel_t *chnl)
+{
+ /* No channels statically allocated for MX35 */
+#ifdef CONFIG_SDMA_IRAM
+ int i;
+ for (i = MXC_DMA_CHANNEL_IRAM; i < MAX_DMA_CHANNELS; i++)
+ chnl[i].dynamic = 0;
+#endif
+}
+
+EXPORT_SYMBOL(mxc_get_static_channels);
diff --git a/arch/arm/mach-mx35/dvfs.c b/arch/arm/mach-mx35/dvfs.c
new file mode 100644
index 000000000000..5ad2292b296b
--- /dev/null
+++ b/arch/arm/mach-mx35/dvfs.c
@@ -0,0 +1,606 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * @file dvfs.c
+ *
+ * @brief A simplied driver for the Freescale Semiconductor MXC DVFS module.
+ *
+ * Upon initialization, the DVFS driver initializes the DVFS hardware
+ * sets up driver nodes attaches to the DVFS interrupt and initializes internal
+ * data structures. When the DVFS interrupt occurs the driver checks the cause
+ * of the interrupt (lower frequency, increase frequency or emergency) and
+ * changes the CPU voltage according to translation table that is loaded into
+ * the driver.
+ *
+ * @ingroup PM
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <mach/hardware.h>
+#include "crm_regs.h"
+
+/*
+ * The frequency of div_3_clk will affect the dvfs sample rate..
+ */
+#define DVFS_DIV3CK (3 << MXC_CCM_LTR0_DIV3CK_OFFSET)
+
+/*
+ * Panic threshold. Panic frequency change request
+ * will be sent if DVFS counter value will be more than this value.
+ */
+#define DVFS_PNCTHR (63 << MXC_CCM_LTR1_PNCTHR_OFFSET)
+
+/*
+ * Load tracking buffer source: 1 for ld_add; 0 for pre_ld_add
+ */
+#define DVFS_LTBRSR (1 << MXC_CCM_LTR1_LTBRSR_OFFSET)
+
+/* EMAC defines how many samples are included in EMA calculation */
+#define DVFS_EMAC (0x20 << MXC_CCM_LTR2_EMAC_OFFSET)
+
+/*
+ * Frequency increase threshold. Increase frequency change request
+ * will be sent if DVFS counter value will be more than this value.
+ */
+#define DVFS_UPTHR(val) (val << MXC_CCM_LTR0_UPTHR_OFFSET)
+
+/*
+ * Frequency decrease threshold. Decrease frequency change request
+ * will be sent if DVFS counter value will be less than this value.
+ */
+#define DVFS_DNTHR(val) (val << MXC_CCM_LTR0_DNTHR_OFFSET)
+
+/*
+ * DNCNT defines the amount of times the down threshold should be exceeded
+ * before DVFS will trigger frequency decrease request.
+ */
+#define DVFS_DNCNT(val) (val << MXC_CCM_LTR1_DNCNT_OFFSET)
+
+/*
+ * UPCNT defines the amount of times the up threshold should be exceeded
+ * before DVFS will trigger frequency increase request.
+ */
+#define DVFS_UPCNT(val) (val << MXC_CCM_LTR1_UPCNT_OFFSET)
+
+#define DVFS_DVSUP(val) (val << MXC_CCM_PMCR0_DVSUP_OFFSET)
+
+#define MXC_DVFS_MAX_WP_NUM 2
+
+enum {
+ FSVAI_FREQ_NOCHANGE = 0x0,
+ FSVAI_FREQ_INCREASE,
+ FSVAI_FREQ_DECREASE,
+ FSVAI_FREQ_EMERG,
+};
+
+struct dvfs_wp {
+ unsigned long cpu_rate;
+ u32 core_voltage;
+ u32 dvsup;
+ u32 dnthr;
+ u32 upthr;
+ u32 dncnt;
+ u32 upcnt;
+};
+
+/* the default working points for MX35 TO2 DVFS. */
+static struct dvfs_wp dvfs_wp_tbl[MXC_DVFS_MAX_WP_NUM] = {
+ {399000000, 1200000, DVFS_DVSUP(DVSUP_LOW), DVFS_DNTHR(18),
+ DVFS_UPTHR(31), DVFS_DNCNT(0x33),
+ DVFS_UPCNT(0x33)},
+/* TBD: Need to set default voltage according to published data sheet */
+ {532000000, 1350000, DVFS_DVSUP(DVSUP_TURBO), DVFS_DNTHR(18),
+ DVFS_UPTHR(30), DVFS_DNCNT(0x33),
+ DVFS_UPCNT(0x33)}
+};
+
+static u8 dvfs_wp_num = MXC_DVFS_MAX_WP_NUM;
+
+ /* Used for tracking the number of interrupts */
+static u32 dvfs_nr_up[MXC_DVFS_MAX_WP_NUM];
+static u32 dvfs_nr_dn[MXC_DVFS_MAX_WP_NUM];
+static unsigned long stored_cpu_rate; /* cpu rate before DVFS starts */
+static u32 stored_pmcr0;
+static int dvfs_is_active; /* indicate DVFS is active or not */
+
+static struct delayed_work dvfs_work;
+
+/*
+ * Clock structures
+ */
+static struct clk *cpu_clk;
+static struct regulator *core_reg;
+
+const static u8 ltr_gp_weight[] = {
+ 0, /* 0 */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0, /* 5 */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0, /* 10 */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0, /* 15 */
+};
+
+DEFINE_SPINLOCK(mxc_dvfs_lock);
+
+/*!
+ * This function sets the weight of general purpose signals
+ * @param gp_id number of general purpose bit
+ * @param weight the weight of the general purpose bit
+ */
+static void set_gp_weight(int gp_id, u8 weight)
+{
+ u32 reg;
+
+ if (gp_id < 9) {
+ reg = __raw_readl(MXC_CCM_LTR3);
+ reg = (reg & ~(MXC_CCM_LTR3_WSW_MASK(gp_id))) |
+ (weight << MXC_CCM_LTR3_WSW_OFFSET(gp_id));
+ __raw_writel(reg, MXC_CCM_LTR3);
+ } else if (gp_id < 16) {
+ reg = __raw_readl(MXC_CCM_LTR2);
+ reg = (reg & ~(MXC_CCM_LTR2_WSW_MASK(gp_id))) |
+ (weight << MXC_CCM_LTR2_WSW_OFFSET(gp_id));
+ __raw_writel(reg, MXC_CCM_LTR2);
+ }
+}
+
+/*!
+ * This function sets upper threshold, lower threshold,
+ * up-counter, down-counter for load tracking.
+ * @param upthr upper threshold
+ * @param dnthr lower threshold
+ * @param upcnt up counter
+ * @param dncnt down counter
+ */
+static void set_ltr_thres_counter(u32 upthr, u32 dnthr, u32 upcnt, u32 dncnt)
+{
+ u32 reg;
+ reg = __raw_readl(MXC_CCM_LTR0);
+ reg =
+ (reg &
+ ~(MXC_CCM_LTR0_UPTHR_MASK |
+ MXC_CCM_LTR0_DNTHR_MASK)) | upthr | dnthr;
+ __raw_writel(reg, MXC_CCM_LTR0);
+
+ reg = __raw_readl(MXC_CCM_LTR1);
+ reg =
+ (reg &
+ ~(MXC_CCM_LTR1_UPCNT_MASK |
+ MXC_CCM_LTR1_DNCNT_MASK)) | upcnt | dncnt;
+ __raw_writel(reg, MXC_CCM_LTR1);
+}
+
+/*!
+ * This function is called for module initialization.
+ * It sets up the DVFS hardware.
+ * It sets default values for DVFS thresholds and counters. The default
+ * values was chosen from a set of different reasonable values. They was tested
+ * and the default values in the driver gave the best results.
+ * More work should be done to find optimal values.
+ *
+ * @return 0 if successful; non-zero otherwise.
+ *
+ */
+static int init_dvfs_controller(void)
+{
+ u32 i, reg;
+
+ /* setup LTR0 */
+ reg = __raw_readl(MXC_CCM_LTR0);
+ reg = (reg & ~(MXC_CCM_LTR0_DIV3CK_MASK)) | DVFS_DIV3CK;
+ __raw_writel(reg, MXC_CCM_LTR0);
+
+ /* set up LTR1 */
+ reg = __raw_readl(MXC_CCM_LTR1);
+ reg = (reg & ~(MXC_CCM_LTR1_PNCTHR_MASK | MXC_CCM_LTR1_LTBRSR_MASK));
+ reg = reg | DVFS_PNCTHR | DVFS_LTBRSR;
+ __raw_writel(reg, MXC_CCM_LTR1);
+
+ /* setup LTR2 */
+ reg = __raw_readl(MXC_CCM_LTR2);
+ reg = (reg & ~(MXC_CCM_LTR2_EMAC_MASK)) | DVFS_EMAC;
+ __raw_writel(reg, MXC_CCM_LTR2);
+
+ /* Set general purpose weights to 0 */
+ for (i = 0; i < 16; i++)
+ set_gp_weight(i, ltr_gp_weight[i]);
+
+ /* ARM interrupt, mask load buf full interrupt */
+ reg = __raw_readl(MXC_CCM_PMCR0);
+ reg |= MXC_CCM_PMCR0_DVFIS | MXC_CCM_PMCR0_LBMI;
+ __raw_writel(reg, MXC_CCM_PMCR0);
+
+ return 0;
+}
+
+static void dvfs_workqueue_handler(struct work_struct *work)
+{
+ u32 pmcr0 = stored_pmcr0;
+ u32 fsvai = (pmcr0 & MXC_CCM_PMCR0_FSVAI_MASK) >>
+ MXC_CCM_PMCR0_FSVAI_OFFSET;
+ u32 dvsup = (pmcr0 & MXC_CCM_PMCR0_DVSUP_MASK) >>
+ MXC_CCM_PMCR0_DVSUP_OFFSET;
+ u32 curr_cpu;
+ u8 curr_dvfs;
+
+ if (!dvfs_is_active)
+ return;
+
+ if (fsvai == FSVAI_FREQ_NOCHANGE) {
+ /* Do nothing. Freq change is not required */
+ printk(KERN_WARNING "fsvai should not be 0\n");
+ goto exit;
+ }
+
+ if (((dvsup == DVSUP_LOW) && (fsvai == FSVAI_FREQ_DECREASE)) ||
+ ((dvsup == DVSUP_TURBO) && ((fsvai == FSVAI_FREQ_INCREASE) ||
+ (fsvai == FSVAI_FREQ_EMERG)))) {
+ /* Interrupt should be disabled in these cases according to
+ * the spec since DVFS is already at lowest (highest) state */
+ printk(KERN_WARNING "Something is wrong?\n");
+ goto exit;
+ }
+
+ /*Disable DPTC voltage update */
+ pmcr0 = pmcr0 & ~MXC_CCM_PMCR0_DPVCR;
+ __raw_writel(pmcr0, MXC_CCM_PMCR0);
+
+ curr_cpu = clk_get_rate(cpu_clk);
+ for (curr_dvfs = 0; curr_dvfs < dvfs_wp_num; curr_dvfs++) {
+ if (dvfs_wp_tbl[curr_dvfs].cpu_rate == curr_cpu) {
+ if (fsvai == FSVAI_FREQ_DECREASE) {
+ curr_dvfs--;
+ dvfs_nr_dn[dvsup]++;
+ /*reduce frequency and then voltage */
+ clk_set_rate(cpu_clk,
+ dvfs_wp_tbl[curr_dvfs].cpu_rate);
+ regulator_set_voltage(core_reg,
+ dvfs_wp_tbl[curr_dvfs].
+ core_voltage,
+ dvfs_wp_tbl[curr_dvfs].
+ core_voltage);
+ pr_info("Decrease frequency to: %ld \n",
+ dvfs_wp_tbl[curr_dvfs].cpu_rate);
+ } else {
+ /*increase freq to the highest one */
+ curr_dvfs = dvfs_wp_num - 1;
+ dvfs_nr_up[dvsup]++;
+ /*Increase voltage and then frequency */
+ regulator_set_voltage(core_reg,
+ dvfs_wp_tbl[curr_dvfs].
+ core_voltage,
+ dvfs_wp_tbl[curr_dvfs].
+ core_voltage);
+ clk_set_rate(cpu_clk,
+ dvfs_wp_tbl[curr_dvfs].cpu_rate);
+ pr_info("Increase frequency to: %ld \n",
+ dvfs_wp_tbl[curr_dvfs].cpu_rate);
+ }
+ pmcr0 = (pmcr0 & ~MXC_CCM_PMCR0_DVSUP_MASK)
+ | (dvfs_wp_tbl[curr_dvfs].dvsup);
+ __raw_writel(pmcr0, MXC_CCM_PMCR0);
+
+ set_ltr_thres_counter(dvfs_wp_tbl[curr_dvfs].upthr,
+ dvfs_wp_tbl[curr_dvfs].dnthr,
+ dvfs_wp_tbl[curr_dvfs].upcnt,
+ dvfs_wp_tbl[curr_dvfs].dncnt);
+ break;
+ }
+ }
+
+ exit:
+ /* unmask interrupt */
+ pmcr0 = pmcr0 & ~MXC_CCM_PMCR0_FSVAIM;
+ __raw_writel(pmcr0, MXC_CCM_PMCR0);
+ /*DVFS update finish */
+ pmcr0 = (pmcr0 | MXC_CCM_PMCR0_DVFS_UPDATE_FINISH);
+ __raw_writel(pmcr0, MXC_CCM_PMCR0);
+}
+
+static irqreturn_t dvfs_irq(int irq, void *dev_id)
+{
+
+ u32 pmcr0 = __raw_readl(MXC_CCM_PMCR0);
+
+ /* Config dvfs_start bit */
+ pmcr0 = pmcr0 | MXC_CCM_PMCR0_DVFS_START;
+ /*Mask interrupt */
+ pmcr0 = pmcr0 | MXC_CCM_PMCR0_FSVAIM;
+ __raw_writel(pmcr0, MXC_CCM_PMCR0);
+
+ stored_pmcr0 = pmcr0;
+ schedule_delayed_work(&dvfs_work, 0);
+
+ return IRQ_RETVAL(1);
+}
+
+/*!
+ * This function enables the DVFS module.
+ */
+static int start_dvfs(void)
+{
+ u32 reg = 0;
+ unsigned long flags;
+ u8 i;
+
+ if (dvfs_is_active) {
+ pr_info("DVFS is already started\n");
+ return 0;
+ }
+
+ spin_lock_irqsave(&mxc_dvfs_lock, flags);
+
+ stored_cpu_rate = clk_get_rate(cpu_clk);
+ for (i = 0; i < dvfs_wp_num; i++) {
+ if (dvfs_wp_tbl[i].cpu_rate == stored_cpu_rate) {
+ /*Set LTR0 and LTR1 */
+ set_ltr_thres_counter(dvfs_wp_tbl[i].upthr,
+ dvfs_wp_tbl[i].dnthr,
+ dvfs_wp_tbl[i].upcnt,
+ dvfs_wp_tbl[i].dncnt);
+
+ reg = __raw_readl(MXC_CCM_PMCR0);
+ reg =
+ (reg & ~MXC_CCM_PMCR0_DVSUP_MASK) | (dvfs_wp_tbl[i].
+ dvsup);
+ /* enable dvfs and interrupt */
+ reg =
+ (reg & ~MXC_CCM_PMCR0_FSVAIM) | MXC_CCM_PMCR0_DVFEN;
+
+ __raw_writel(reg, MXC_CCM_PMCR0);
+
+ dvfs_is_active = 1;
+ pr_info("DVFS Starts\n");
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&mxc_dvfs_lock, flags);
+ if (dvfs_is_active)
+ return 0;
+ else
+ return 1;
+}
+
+/*!
+ * This function disables the DVFS module.
+ */
+static void stop_dvfs(void)
+{
+ u32 pmcr0;
+ unsigned long curr_cpu = clk_get_rate(cpu_clk);
+ u8 index;
+
+ if (dvfs_is_active) {
+
+ pmcr0 = __raw_readl(MXC_CCM_PMCR0);
+ /* disable dvfs and its interrupt */
+ pmcr0 = (pmcr0 & ~MXC_CCM_PMCR0_DVFEN) | MXC_CCM_PMCR0_FSVAIM;
+ __raw_writel(pmcr0, MXC_CCM_PMCR0);
+
+ if (stored_cpu_rate < curr_cpu) {
+ for (index = 0; index < dvfs_wp_num; index++) {
+ if (dvfs_wp_tbl[index].cpu_rate ==
+ stored_cpu_rate)
+ break;
+ }
+ clk_set_rate(cpu_clk, stored_cpu_rate);
+ regulator_set_voltage(core_reg,
+ dvfs_wp_tbl[index].core_voltage,
+ dvfs_wp_tbl[index].core_voltage);
+ } else if (stored_cpu_rate > curr_cpu) {
+ for (index = 0; index < dvfs_wp_num; index++) {
+ if (dvfs_wp_tbl[index].cpu_rate ==
+ stored_cpu_rate)
+ break;
+ }
+ regulator_set_voltage(core_reg,
+ dvfs_wp_tbl[index].core_voltage,
+ dvfs_wp_tbl[index].core_voltage);
+ clk_set_rate(cpu_clk, stored_cpu_rate);
+ }
+
+ dvfs_is_active = 0;
+ }
+
+ pr_info("DVFS is stopped\n");
+}
+
+static ssize_t dvfs_enable_store(struct sys_device *dev, struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (strstr(buf, "1") != NULL) {
+ if (start_dvfs() != 0)
+ printk(KERN_ERR "Failed to start DVFS\n");
+ } else if (strstr(buf, "0") != NULL) {
+ stop_dvfs();
+ }
+
+ return size;
+}
+
+static ssize_t dvfs_status_show(struct sys_device *dev, struct sysdev_attribute *attr,
+ char *buf)
+{
+ int size = 0, i;
+
+ if (dvfs_is_active)
+ size = sprintf(buf, "DVFS is enabled\n");
+ else
+ size = sprintf(buf, "DVFS is disabled\n");
+
+ size += sprintf((buf + size), "UP:\t");
+ for (i = 0; i < MXC_DVFS_MAX_WP_NUM; i++)
+ size += sprintf((buf + size), "%d\t", dvfs_nr_up[i]);
+ size += sprintf((buf + size), "\n");
+
+ size += sprintf((buf + size), "DOWN:\t");
+ for (i = 0; i < MXC_DVFS_MAX_WP_NUM; i++)
+ size += sprintf((buf + size), "%d\t", dvfs_nr_dn[i]);
+ size += sprintf((buf + size), "\n");
+
+ return size;
+}
+
+static ssize_t dvfs_status_store(struct sys_device *dev, struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (strstr(buf, "reset") != NULL) {
+ int i;
+ for (i = 0; i < MXC_DVFS_MAX_WP_NUM; i++) {
+ dvfs_nr_up[i] = 0;
+ dvfs_nr_dn[i] = 0;
+ }
+ }
+
+ return size;
+}
+
+static SYSDEV_ATTR(enable, 0200, NULL, dvfs_enable_store);
+static SYSDEV_ATTR(status, 0644, dvfs_status_show, dvfs_status_store);
+
+static struct sysdev_class dvfs_sysclass = {
+ .name = "dvfs",
+};
+
+static struct sys_device dvfs_device = {
+ .id = 0,
+ .cls = &dvfs_sysclass,
+};
+
+static int dvfs_sysdev_ctrl_init(void)
+{
+ int err;
+
+ err = sysdev_class_register(&dvfs_sysclass);
+ if (!err)
+ err = sysdev_register(&dvfs_device);
+ if (!err) {
+ err = sysdev_create_file(&dvfs_device, &attr_enable);
+ err = sysdev_create_file(&dvfs_device, &attr_status);
+ }
+
+ return err;
+}
+
+static void dvfs_sysdev_ctrl_exit(void)
+{
+ sysdev_remove_file(&dvfs_device, &attr_enable);
+ sysdev_remove_file(&dvfs_device, &attr_status);
+ sysdev_unregister(&dvfs_device);
+ sysdev_class_unregister(&dvfs_sysclass);
+}
+
+static int __init dvfs_init(void)
+{
+ int err = 0;
+ u8 index;
+ unsigned long curr_cpu;
+
+ if (cpu_is_mx35_rev(CHIP_REV_1_0) == 1) {
+ /*
+ * Don't support DVFS for auto path in TO1 because
+ * the voltages under 399M are all 1.2v
+ */
+ if (!(__raw_readl(MXC_CCM_PDR0) & MXC_CCM_PDR0_AUTO_CON)) {
+ pr_info("MX35 TO1 auto path, no need to use DVFS \n");
+ return -1;
+ }
+ }
+
+ cpu_clk = clk_get(NULL, "cpu_clk");
+ curr_cpu = clk_get_rate(cpu_clk);
+
+ if (board_is_mx35(BOARD_REV_2))
+ core_reg = regulator_get(NULL, "SW2");
+ else
+ core_reg = regulator_get(NULL, "SW3");
+
+ dvfs_is_active = 0;
+
+ /*Set voltage */
+ for (index = 0; index < dvfs_wp_num; index++) {
+ if (dvfs_wp_tbl[index].cpu_rate == curr_cpu
+ && !IS_ERR(core_reg)) {
+ regulator_set_voltage(core_reg,
+ dvfs_wp_tbl[index].core_voltage,
+ dvfs_wp_tbl[index].core_voltage);
+ break;
+ }
+ }
+
+ err = init_dvfs_controller();
+ if (err) {
+ printk(KERN_ERR "DVFS: Unable to initialize DVFS");
+ return err;
+ }
+
+ INIT_DELAYED_WORK(&dvfs_work, dvfs_workqueue_handler);
+
+ /* request the DVFS interrupt */
+ err = request_irq(MXC_INT_DVFS, dvfs_irq, IRQF_DISABLED, "dvfs", NULL);
+ if (err) {
+ printk(KERN_ERR "DVFS: Unable to attach to DVFS interrupt");
+ return err;
+ }
+
+ err = dvfs_sysdev_ctrl_init();
+ if (err) {
+ printk(KERN_ERR
+ "DVFS: Unable to register sysdev entry for dvfs");
+ return err;
+ }
+
+ return err;
+}
+
+static void __exit dvfs_cleanup(void)
+{
+ stop_dvfs();
+
+ /* release the DVFS interrupt */
+ free_irq(MXC_INT_DVFS, NULL);
+
+ dvfs_sysdev_ctrl_exit();
+
+ clk_put(cpu_clk);
+ regulator_put(core_reg);
+}
+
+module_init(dvfs_init);
+module_exit(dvfs_cleanup);
+
+MODULE_AUTHOR("Freescale Seminconductor, Inc.");
+MODULE_DESCRIPTION("DVFS driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-mx35/iomux.c b/arch/arm/mach-mx35/iomux.c
new file mode 100644
index 000000000000..3a23c04f3eee
--- /dev/null
+++ b/arch/arm/mach-mx35/iomux.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup GPIO_MX35 Board GPIO and Muxing Setup
+ * @ingroup MSL_MX35
+ */
+/*!
+ * @file mach-mx35/iomux.c
+ *
+ * @brief I/O Muxing control functions
+ *
+ * @ingroup GPIO_MX35
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include "iomux.h"
+
+/*!
+ * IOMUX register (base) addresses
+ */
+enum iomux_reg_addr {
+ IOMUXGPR = IO_ADDRESS(IOMUXC_BASE_ADDR),
+ /*!< General purpose */
+ IOMUXSW_MUX_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + 4,
+ /*!< MUX control */
+ IOMUXSW_MUX_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x324,
+ /*!< last MUX control register */
+ IOMUXSW_PAD_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x328,
+ /*!< Pad control */
+ IOMUXSW_PAD_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x790,
+ /*!< last Pad control register */
+ IOMUXSW_INPUT_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x7A8,
+ /*!< input select register */
+ IOMUXSW_INPUT_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x9F4,
+ /*!< last input select register */
+};
+
+#define MUX_PIN_NUM_MAX \
+ (((IOMUXSW_PAD_END - IOMUXSW_PAD_CTL) >> 2) + 1)
+#define MUX_INPUT_NUM_MUX \
+ (((IOMUXSW_INPUT_END - IOMUXSW_INPUT_CTL) >> 2) + 1)
+
+#define PIN_TO_IOMUX_INDEX(pin) ((PIN_TO_IOMUX_PAD(pin) - 0x328) >> 2)
+
+static DEFINE_SPINLOCK(gpio_mux_lock);
+static u8 iomux_pin_res_table[MUX_PIN_NUM_MAX];
+
+/*!
+ * This function is used to configure a pin through the IOMUX module.
+ * FIXED ME: for backward compatible. Will be static function!
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param cfg an output function as defined in \b #iomux_pin_cfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+static int iomux_config_mux(iomux_pin_name_t pin, iomux_pin_cfg_t cfg)
+{
+ u32 ret = 0;
+ u32 pin_index = PIN_TO_IOMUX_INDEX(pin);
+ u32 mux_reg = PIN_TO_IOMUX_MUX(pin);
+ u8 *rp;
+
+ BUG_ON(pin_index > MUX_PIN_NUM_MAX);
+ if (mux_reg != NON_MUX_I) {
+ mux_reg += IOMUXGPR;
+ BUG_ON((mux_reg > IOMUXSW_MUX_END)
+ || (mux_reg < IOMUXSW_MUX_CTL));
+ spin_lock(&gpio_mux_lock);
+ __raw_writel(cfg, mux_reg);
+ /*
+ * Log a warning if a pin changes ownership
+ */
+ rp = iomux_pin_res_table + pin_index;
+ if ((cfg & *rp) && (*rp != cfg)) {
+ /*Console: how to do */
+ printk(KERN_ERR "iomux_config_mux: Warning: iomux pin"
+ " config changed, index=%d register=%d, "
+ " prev=0x%x new=0x%x\n", pin_index, mux_reg,
+ *rp, cfg);
+ ret = -EINVAL;
+ }
+ *rp = cfg;
+ spin_unlock(&gpio_mux_lock);
+ }
+
+ return ret;
+}
+
+/*!
+ * Request ownership for an IO pin. This function has to be the first one
+ * being called before that pin is used. The caller has to check the
+ * return value to make sure it returns 0.
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param cfg an input function as defined in \b #iomux_pin_cfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t cfg)
+{
+ int ret = iomux_config_mux(pin, cfg);
+ if (GPIO_TO_PORT(IOMUX_TO_GPIO(pin)) < GPIO_PORT_NUM) {
+ if (((cfg & (~MUX_CONFIG_SION)) == MUX_CONFIG_GPIO) ||
+ (((cfg & (~MUX_CONFIG_SION)) == MUX_CONFIG_FUNC) &&
+ ((pin == MX35_PIN_GPIO1_0) || (pin == MX35_PIN_GPIO1_1) ||
+ (pin == MX35_PIN_GPIO2_0) || (pin == MX35_PIN_GPIO3_0))))
+ ret |= gpio_request(IOMUX_TO_GPIO(pin), NULL);
+ }
+ return ret;
+}
+
+EXPORT_SYMBOL(mxc_request_iomux);
+
+/*!
+ * Release ownership for an IO pin
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param cfg an input function as defined in \b #iomux_pin_cfg_t
+ */
+void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t cfg)
+{
+ u32 pin_index = PIN_TO_IOMUX_INDEX(pin);
+ u8 *rp = iomux_pin_res_table + pin_index;
+
+ BUG_ON((pin_index > MUX_PIN_NUM_MAX));
+
+ *rp = 0;
+ if (GPIO_TO_PORT(IOMUX_TO_GPIO(pin)) < GPIO_PORT_NUM) {
+ if (((cfg & (~MUX_CONFIG_SION)) == MUX_CONFIG_GPIO) ||
+ (((cfg & (~MUX_CONFIG_SION)) == MUX_CONFIG_FUNC) &&
+ ((pin == MX35_PIN_GPIO1_0) || (pin == MX35_PIN_GPIO1_1) ||
+ (pin == MX35_PIN_GPIO2_0) || (pin == MX35_PIN_GPIO3_0))))
+ gpio_free(IOMUX_TO_GPIO(pin));
+ }
+}
+
+EXPORT_SYMBOL(mxc_free_iomux);
+
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config the ORed value of elements defined in \b #iomux_pad_config_t
+ */
+void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config)
+{
+ u32 pad_reg = IOMUXGPR + PIN_TO_IOMUX_PAD(pin);
+
+ BUG_ON((pad_reg > IOMUXSW_PAD_END) || (pad_reg < IOMUXSW_PAD_CTL));
+
+ spin_lock(&gpio_mux_lock);
+ __raw_writel(config, pad_reg);
+ spin_unlock(&gpio_mux_lock);
+}
+
+EXPORT_SYMBOL(mxc_iomux_set_pad);
+
+/*!
+ * This function enables/disables the general purpose function for a particular
+ * signal.
+ *
+ * @param gp one signal as defined in \b #iomux_gp_func_t
+ * @param en \b #true to enable; \b #false to disable
+ */
+void mxc_iomux_set_gpr(iomux_gp_func_t gp, bool en)
+{
+ u32 l;
+
+ spin_lock(&gpio_mux_lock);
+ l = __raw_readl(IOMUXGPR);
+
+ if (en)
+ l |= gp;
+ else
+ l &= ~gp;
+
+ __raw_writel(l, IOMUXGPR);
+ spin_unlock(&gpio_mux_lock);
+}
+
+EXPORT_SYMBOL(mxc_iomux_set_gpr);
+
+/*!
+ * This function configures input path.
+ *
+ * @param input index of input select register as defined in \b
+ * #iomux_input_select_t
+ * @param config the binary value of elements defined in \b
+ * #iomux_input_config_t
+ */
+void mxc_iomux_set_input(iomux_input_select_t input, u32 config)
+{
+ u32 reg = IOMUXSW_INPUT_CTL + (input << 2);
+
+ BUG_ON(input >= MUX_INPUT_NUM_MUX);
+
+ spin_lock(&gpio_mux_lock);
+ __raw_writel(config, reg);
+ spin_unlock(&gpio_mux_lock);
+}
+
+EXPORT_SYMBOL(mxc_iomux_set_input);
diff --git a/arch/arm/mach-mx35/iomux.h b/arch/arm/mach-mx35/iomux.h
new file mode 100644
index 000000000000..dc8de152c5a5
--- /dev/null
+++ b/arch/arm/mach-mx35/iomux.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MACH_MX35_IOMUX_H__
+#define __MACH_MX35_IOMUX_H__
+
+#include <linux/types.h>
+#include <mach/gpio.h>
+#include "mx35_pins.h"
+
+/*!
+ * @file mach-mx35/iomux.h
+ *
+ * @brief I/O Muxing control definitions and functions
+ *
+ * @ingroup GPIO_MX35
+ */
+
+typedef unsigned int iomux_pin_name_t;
+
+/*!
+ * various IOMUX functions
+ */
+typedef enum iomux_pin_config {
+ MUX_CONFIG_FUNC = 0, /*!< used as function */
+ MUX_CONFIG_ALT1, /*!< used as alternate function 1 */
+ MUX_CONFIG_ALT2, /*!< used as alternate function 2 */
+ MUX_CONFIG_ALT3, /*!< used as alternate function 3 */
+ MUX_CONFIG_ALT4, /*!< used as alternate function 4 */
+ MUX_CONFIG_ALT5, /*!< used as alternate function 5 */
+ MUX_CONFIG_ALT6, /*!< used as alternate function 6 */
+ MUX_CONFIG_ALT7, /*!< used as alternate function 7 */
+ MUX_CONFIG_SION = 0x1 << 4, /*!< used as LOOPBACK:MUX SION bit */
+ MUX_CONFIG_GPIO = MUX_CONFIG_ALT5, /*!< used as GPIO */
+} iomux_pin_cfg_t;
+
+/*!
+ * various IOMUX pad functions
+ */
+typedef enum iomux_pad_config {
+ PAD_CTL_DRV_3_3V = 0x0 << 13,
+ PAD_CTL_DRV_1_8V = 0x1 << 13,
+ PAD_CTL_HYS_CMOS = 0x0 << 8,
+ PAD_CTL_HYS_SCHMITZ = 0x1 << 8,
+ PAD_CTL_PKE_NONE = 0x0 << 7,
+ PAD_CTL_PKE_ENABLE = 0x1 << 7,
+ PAD_CTL_PUE_KEEPER = 0x0 << 6,
+ PAD_CTL_PUE_PUD = 0x1 << 6,
+ PAD_CTL_100K_PD = 0x0 << 4,
+ PAD_CTL_47K_PU = 0x1 << 4,
+ PAD_CTL_100K_PU = 0x2 << 4,
+ PAD_CTL_22K_PU = 0x3 << 4,
+ PAD_CTL_ODE_CMOS = 0x0 << 3,
+ PAD_CTL_ODE_OpenDrain = 0x1 << 3,
+ PAD_CTL_DRV_NORMAL = 0x0 << 1,
+ PAD_CTL_DRV_HIGH = 0x1 << 1,
+ PAD_CTL_DRV_MAX = 0x2 << 1,
+ PAD_CTL_SRE_SLOW = 0x0 << 0,
+ PAD_CTL_SRE_FAST = 0x1 << 0
+} iomux_pad_config_t;
+
+/*!
+ * various IOMUX general purpose functions
+ */
+typedef enum iomux_gp_func {
+ MUX_SDCTL_CSD0_SEL = 0x1 << 0,
+ MUX_SDCTL_CSD1_SEL = 0x1 << 1,
+ MUX_TAMPER_DETECT_EN = 0x1 << 2,
+} iomux_gp_func_t;
+
+/*!
+ * various IOMUX input select register index
+ */
+typedef enum iomux_input_select {
+ MUX_IN_AMX_P5_RXCLK = 0,
+ MUX_IN_AMX_P5_RXFS,
+ MUX_IN_AMX_P6_DA,
+ MUX_IN_AMX_P6_DB,
+ MUX_IN_AMX_P6_RXCLK,
+ MUX_IN_AMX_P6_RXFS,
+ MUX_IN_AMX_P6_TXCLK,
+ MUX_IN_AMX_P6_TXFS,
+ MUX_IN_CAN1_CANRX,
+ MUX_IN_CAN2_CANRX,
+ MUX_IN_CCM_32K_MUXED,
+ MUX_IN_CCM_PMIC_RDY,
+ MUX_IN_CSPI1_SS2_B,
+ MUX_IN_CSPI1_SS3_B,
+ MUX_IN_CSPI2_CLK_IN,
+ MUX_IN_CSPI2_DATAREADY_B,
+ MUX_IN_CSPI2_MISO,
+ MUX_IN_CSPI2_MOSI,
+ MUX_IN_CSPI2_SS0_B,
+ MUX_IN_CSPI2_SS1_B,
+ MUX_IN_CSPI2_SS2_B,
+ MUX_IN_CSPI2_SS3_B,
+ MUX_IN_EMI_WEIM_DTACK_B,
+ MUX_IN_ESDHC1_DAT4_IN,
+ MUX_IN_ESDHC1_DAT5_IN,
+ MUX_IN_ESDHC1_DAT6_IN,
+ MUX_IN_ESDHC1_DAT7_IN,
+ MUX_IN_ESDHC3_CARD_CLK_IN,
+ MUX_IN_ESDHC3_CMD_IN,
+ MUX_IN_ESDHC3_DAT0,
+ MUX_IN_ESDHC3_DAT1,
+ MUX_IN_ESDHC3_DAT2,
+ MUX_IN_ESDHC3_DAT3,
+ MUX_IN_GPIO1_IN_0,
+ MUX_IN_GPIO1_IN_10,
+ MUX_IN_GPIO1_IN_11,
+ MUX_IN_GPIO1_IN_1,
+ MUX_IN_GPIO1_IN_20,
+ MUX_IN_GPIO1_IN_21,
+ MUX_IN_GPIO1_IN_22,
+ MUX_IN_GPIO1_IN_2,
+ MUX_IN_GPIO1_IN_3,
+ MUX_IN_GPIO1_IN_4,
+ MUX_IN_GPIO1_IN_5,
+ MUX_IN_GPIO1_IN_6,
+ MUX_IN_GPIO1_IN_7,
+ MUX_IN_GPIO1_IN_8,
+ MUX_IN_GPIO1_IN_9,
+ MUX_IN_GPIO2_IN_0,
+ MUX_IN_GPIO2_IN_10,
+ MUX_IN_GPIO2_IN_11,
+ MUX_IN_GPIO2_IN_12,
+ MUX_IN_GPIO2_IN_13,
+ MUX_IN_GPIO2_IN_14,
+ MUX_IN_GPIO2_IN_15,
+ MUX_IN_GPIO2_IN_16,
+ MUX_IN_GPIO2_IN_17,
+ MUX_IN_GPIO2_IN_18,
+ MUX_IN_GPIO2_IN_19,
+ MUX_IN_GPIO2_IN_1,
+ MUX_IN_GPIO2_IN_20,
+ MUX_IN_GPIO2_IN_21,
+ MUX_IN_GPIO2_IN_22,
+ MUX_IN_GPIO2_IN_23,
+ MUX_IN_GPIO2_IN_24,
+ MUX_IN_GPIO2_IN_25,
+ MUX_IN_GPIO2_IN_26,
+ MUX_IN_GPIO2_IN_27,
+ MUX_IN_GPIO2_IN_28,
+ MUX_IN_GPIO2_IN_29,
+ MUX_IN_GPIO2_IN_2,
+ MUX_IN_GPIO2_IN_30,
+ MUX_IN_GPIO2_IN_31,
+ MUX_IN_GPIO2_IN_3,
+ MUX_IN_GPIO2_IN_4,
+ MUX_IN_GPIO2_IN_5,
+ MUX_IN_GPIO2_IN_6,
+ MUX_IN_GPIO2_IN_7,
+ MUX_IN_GPIO2_IN_8,
+ MUX_IN_GPIO2_IN_9,
+ MUX_IN_GPIO3_IN_0,
+ MUX_IN_GPIO3_IN_10,
+ MUX_IN_GPIO3_IN_11,
+ MUX_IN_GPIO3_IN_12,
+ MUX_IN_GPIO3_IN_13,
+ MUX_IN_GPIO3_IN_14,
+ MUX_IN_GPIO3_IN_15,
+ MUX_IN_GPIO3_IN_4,
+ MUX_IN_GPIO3_IN_5,
+ MUX_IN_GPIO3_IN_6,
+ MUX_IN_GPIO3_IN_7,
+ MUX_IN_GPIO3_IN_8,
+ MUX_IN_GPIO3_IN_9,
+ MUX_IN_I2C3_SCL_IN,
+ MUX_IN_I2C3_SDA_IN,
+ MUX_IN_IPU_DISPB_D0_VSYNC,
+ MUX_IN_IPU_DISPB_D12_VSYNC,
+ MUX_IN_IPU_DISPB_SD_D,
+ MUX_IN_IPU_SENSB_DATA_0,
+ MUX_IN_IPU_SENSB_DATA_1,
+ MUX_IN_IPU_SENSB_DATA_2,
+ MUX_IN_IPU_SENSB_DATA_3,
+ MUX_IN_IPU_SENSB_DATA_4,
+ MUX_IN_IPU_SENSB_DATA_5,
+ MUX_IN_IPU_SENSB_DATA_6,
+ MUX_IN_IPU_SENSB_DATA_7,
+ MUX_IN_KPP_COL_0,
+ MUX_IN_KPP_COL_1,
+ MUX_IN_KPP_COL_2,
+ MUX_IN_KPP_COL_3,
+ MUX_IN_KPP_COL_4,
+ MUX_IN_KPP_COL_5,
+ MUX_IN_KPP_COL_6,
+ MUX_IN_KPP_COL_7,
+ MUX_IN_KPP_ROW_0,
+ MUX_IN_KPP_ROW_1,
+ MUX_IN_KPP_ROW_2,
+ MUX_IN_KPP_ROW_3,
+ MUX_IN_KPP_ROW_4,
+ MUX_IN_KPP_ROW_5,
+ MUX_IN_KPP_ROW_6,
+ MUX_IN_KPP_ROW_7,
+ MUX_IN_OWIRE_BATTERY_LINE,
+ MUX_IN_SPDIF_HCKT_CLK2,
+ MUX_IN_SPDIF_SPDIF_IN1,
+ MUX_IN_UART3_UART_RTS_B,
+ MUX_IN_UART3_UART_RXD_MUX,
+ MUX_IN_USB_OTG_DATA_0,
+ MUX_IN_USB_OTG_DATA_1,
+ MUX_IN_USB_OTG_DATA_2,
+ MUX_IN_USB_OTG_DATA_3,
+ MUX_IN_USB_OTG_DATA_4,
+ MUX_IN_USB_OTG_DATA_5,
+ MUX_IN_USB_OTG_DATA_6,
+ MUX_IN_USB_OTG_DATA_7,
+ MUX_IN_USB_OTG_DIR,
+ MUX_IN_USB_OTG_NXT,
+ MUX_IN_USB_UH2_DATA_0,
+ MUX_IN_USB_UH2_DATA_1,
+ MUX_IN_USB_UH2_DATA_2,
+ MUX_IN_USB_UH2_DATA_3,
+ MUX_IN_USB_UH2_DATA_4,
+ MUX_IN_USB_UH2_DATA_5,
+ MUX_IN_USB_UH2_DATA_6,
+ MUX_IN_USB_UH2_DATA_7,
+ MUX_IN_USB_UH2_DIR,
+ MUX_IN_USB_UH2_NXT,
+ MUX_IN_USB_UH2_USB_OC,
+} iomux_input_select_t;
+
+/*!
+ * various IOMUX input functions
+ */
+typedef enum iomux_input_config {
+ INPUT_CTL_PATH0 = 0x0,
+ INPUT_CTL_PATH1,
+ INPUT_CTL_PATH2,
+ INPUT_CTL_PATH3,
+ INPUT_CTL_PATH4,
+ INPUT_CTL_PATH5,
+ INPUT_CTL_PATH6,
+ INPUT_CTL_PATH7,
+} iomux_input_cfg_t;
+
+/*!
+ * Request ownership for an IO pin. This function has to be the first one
+ * being called before that pin is used. The caller has to check the
+ * return value to make sure it returns 0.
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param cfg an input function as defined in \b #iomux_pin_cfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t cfg);
+
+/*!
+ * Release ownership for an IO pin
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param cfg an input function as defined in \b #iomux_pin_cfg_t
+ */
+void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t cfg);
+
+/*!
+ * This function enables/disables the general purpose function for a particular
+ * signal.
+ *
+ * @param gp one signal as defined in \b #iomux_gp_func_t
+ * @param en \b #true to enable; \b #false to disable
+ */
+void mxc_iomux_set_gpr(iomux_gp_func_t gp, bool en);
+
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config the ORed value of elements defined in \b
+ * #iomux_pad_config_t
+ */
+void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config);
+
+/*!
+ * This function configures input path.
+ *
+ * @param input index of input select register as defined in \b
+ * #iomux_input_select_t
+ * @param config the binary value of elements defined in \b
+ * #iomux_input_cfg_t
+ */
+void mxc_iomux_set_input(iomux_input_select_t input, u32 config);
+#endif
diff --git a/arch/arm/mach-mx35/mm.c b/arch/arm/mach-mx35/mm.c
new file mode 100644
index 000000000000..81ca3eb797ee
--- /dev/null
+++ b/arch/arm/mach-mx35/mm.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <asm/pgtable.h>
+#include <asm/mach/map.h>
+
+/*!
+ * @file mach-mx35/mm.c
+ *
+ * @brief This file creates static mapping between physical to virtual memory.
+ *
+ * @ingroup Memory_MX35
+ */
+
+/*!
+ * This structure defines the MX35 memory map.
+ */
+static struct map_desc mxc_io_desc[] __initdata = {
+ {
+ .virtual = IRAM_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(IRAM_BASE_ADDR),
+ .length = IRAM_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = X_MEMC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(X_MEMC_BASE_ADDR),
+ .length = X_MEMC_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = NFC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(NFC_BASE_ADDR),
+ .length = NFC_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = AVIC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AVIC_BASE_ADDR),
+ .length = AVIC_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = AIPS1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS1_BASE_ADDR),
+ .length = AIPS1_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = SPBA0_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(SPBA0_BASE_ADDR),
+ .length = SPBA0_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = AIPS2_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS2_BASE_ADDR),
+ .length = 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 map for
+ * the IO modules.
+ */
+void __init mxc_map_io(void)
+{
+ iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
+}
diff --git a/arch/arm/mach-mx35/mx35_3stack.c b/arch/arm/mach-mx35/mx35_3stack.c
new file mode 100644
index 000000000000..494e236bf73a
--- /dev/null
+++ b/arch/arm/mach-mx35/mx35_3stack.c
@@ -0,0 +1,1224 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/nodemask.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/ata.h>
+#include <linux/pmic_external.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include <linux/regulator/consumer.h>
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/delay.h>
+
+#include <asm/mach/flash.h>
+#endif
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+#include <mach/common.h>
+#include <mach/memory.h>
+#include <mach/gpio.h>
+#include <mach/mmc.h>
+
+#include "board-mx35_3stack.h"
+#include "crm_regs.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx35/mx35_3stack.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX35
+ */
+
+unsigned int mx35_3stack_board_io;
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+/* MTD NOR flash */
+
+#if defined(CONFIG_MTD_MXC) || defined(CONFIG_MTD_MXC_MODULE)
+
+static struct mtd_partition mxc_nor_partitions[] = {
+ {
+ .name = "Bootloader",
+ .size = 512 * 1024,
+ .offset = 0x00000000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "nor.Kernel",
+ .size = 4 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = 0},
+ {
+ .name = "nor.userfs",
+ .size = 30 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = 0},
+ {
+ .name = "nor.rootfs",
+ .size = 28 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE},
+ {
+ .name = "FIS directory",
+ .size = 12 * 1024,
+ .offset = 0x01FE0000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "Redboot config",
+ .size = MTDPART_SIZ_FULL,
+ .offset = 0x01FFF000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+};
+
+static struct flash_platform_data mxc_flash_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+ .parts = mxc_nor_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_nor_partitions),
+};
+
+static struct resource mxc_flash_resource = {
+ .start = 0xa0000000,
+ .end = 0xa0000000 + 0x04000000 - 1,
+ .flags = IORESOURCE_MEM,
+
+};
+
+static struct platform_device mxc_nor_mtd_device = {
+ .name = "mxc_nor_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &mxc_flash_resource,
+};
+
+static void mxc_init_nor_mtd(void)
+{
+ (void)platform_device_register(&mxc_nor_mtd_device);
+}
+#else
+static void mxc_init_nor_mtd(void)
+{
+}
+#endif
+
+/* MTD NAND flash */
+
+#if defined(CONFIG_MTD_NAND_MXC) || defined(CONFIG_MTD_NAND_MXC_MODULE) \
+|| defined(CONFIG_MTD_NAND_MXC_V2) || defined(CONFIG_MTD_NAND_MXC_V2_MODULE)
+
+static struct mtd_partition mxc_nand_partitions[] = {
+ {
+ .name = "nand.bootloader",
+ .offset = 0,
+ .size = 1024 * 1024},
+ {
+ .name = "nand.kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 5 * 1024 * 1024},
+ {
+ .name = "nand.rootfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 256 * 1024 * 1024},
+ {
+ .name = "nand.configure",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 8 * 1024 * 1024},
+ {
+ .name = "nand.userfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL},
+};
+
+static struct flash_platform_data mxc_nand_data = {
+ .parts = mxc_nand_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_nand_partitions),
+ .width = 1,
+};
+
+static struct platform_device mxc_nand_mtd_device = {
+ .name = "mxc_nandv2_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static void mxc_init_nand_mtd(void)
+{
+ if (__raw_readl(MXC_CCM_RCSR) & MXC_CCM_RCSR_NF16B)
+ mxc_nand_data.width = 2;
+
+ platform_device_register(&mxc_nand_mtd_device);
+}
+#else
+static inline void mxc_init_nand_mtd(void)
+{
+}
+#endif
+
+static struct mxc_lcd_platform_data lcd_data = {
+ .io_reg = "LCD"
+};
+
+static struct platform_device lcd_dev = {
+ .name = "lcd_claa",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = (void *)&lcd_data,
+ },
+};
+
+static void mxc_init_lcd(void)
+{
+ platform_device_register(&lcd_dev);
+}
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+/* mxc lcd driver */
+static struct platform_device mxc_fb_device = {
+ .name = "mxc_sdc_fb",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+};
+
+static void mxc_init_fb(void)
+{
+ (void)platform_device_register(&mxc_fb_device);
+}
+#else
+static inline void mxc_init_fb(void)
+{
+}
+#endif
+
+#if defined(CONFIG_BACKLIGHT_MXC)
+static struct platform_device mxcbl_devices[] = {
+#if defined(CONFIG_BACKLIGHT_MXC_IPU) || defined(CONFIG_BACKLIGHT_MXC_IPU_MODULE)
+ {
+ .name = "mxc_ipu_bl",
+ .id = 0,
+ .dev = {
+ .platform_data = (void *)3, /* DISP # for this backlight */
+ },
+ }
+#endif
+};
+
+static inline void mxc_init_bl(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mxcbl_devices); i++) {
+ platform_device_register(&mxcbl_devices[i]);
+ }
+}
+#else
+static inline void mxc_init_bl(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_MLB) || defined(CONFIG_MXC_MLB_MODULE)
+static struct resource mlb_resource[] = {
+ [0] = {
+ .start = MLB_BASE_ADDR,
+ .end = MLB_BASE_ADDR + 0x300,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MLB,
+ .end = MXC_INT_MLB,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mxc_mlb_platform_data mlb_data = {
+ .buf_address = IRAM_BASE_ADDR_VIRT + MLB_IRAM_ADDR_OFFSET,
+ .phy_address = IRAM_BASE_ADDR + MLB_IRAM_ADDR_OFFSET,
+ .reg_nvcc = "VVIDEO",
+ .mlb_clk = "mlb_clk",
+};
+
+static struct platform_device mlb_dev = {
+ .name = "mxc_mlb",
+ .id = 0,
+ .dev = {
+ .platform_data = &mlb_data,
+ },
+ .num_resources = ARRAY_SIZE(mlb_resource),
+ .resource = mlb_resource,
+};
+
+static inline void mxc_init_mlb(void)
+{
+ platform_device_register(&mlb_dev);
+}
+#else
+static inline void mxc_init_mlb(void)
+{
+}
+#endif
+
+static void mxc_unifi_hardreset(int pin_level)
+{
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 1, pin_level & 0x01);
+}
+
+static void mxc_unifi_enable(int en)
+{
+ if (en) {
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 5, 1);
+ msleep(10);
+ } else
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 5, 0);
+}
+
+static struct mxc_unifi_platform_data unifi_data = {
+ .hardreset = mxc_unifi_hardreset,
+ .enable = mxc_unifi_enable,
+ .reg_gpo1 = "GPO2",
+ .reg_gpo2 = "GPO3",
+ .reg_1v5_ana_bb = "PWGT1",
+ .reg_vdd_vpa = "VAUDIO",
+ .reg_1v5_dd = "SW1",
+ .host_id = 1,
+};
+
+struct mxc_unifi_platform_data *get_unifi_plat_data(void)
+{
+ return &unifi_data;
+}
+
+EXPORT_SYMBOL(get_unifi_plat_data);
+
+static struct mxc_tsc_platform_data tsc2007_data = {
+ .vdd_reg = NULL,
+ .penup_threshold = 30,
+ .active = NULL,
+ .inactive = NULL,
+};
+
+static struct mxc_camera_platform_data camera_data = {
+ .core_regulator = "SW1",
+ .io_regulator = "VAUDIO",
+ .analog_regulator = NULL,
+ .gpo_regulator = "PWGT1",
+ .mclk = 27000000,
+};
+
+void si4702_reset(void)
+{
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 4, 0);
+ msleep(100);
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 4, 1);
+ msleep(100);
+}
+
+void si4702_clock_ctl(int flag)
+{
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 7, flag);
+}
+
+static void si4702_gpio_get(void)
+{
+}
+
+static void si4702_gpio_put(void)
+{
+}
+
+static struct mxc_fm_platform_data si4702_data = {
+ .reg_vio = "VSD",
+ .reg_vdd = NULL,
+ .gpio_get = si4702_gpio_get,
+ .gpio_put = si4702_gpio_put,
+ .reset = si4702_reset,
+ .clock_ctl = si4702_clock_ctl,
+ .sksnr = 0,
+ .skcnt = 0,
+ .band = 0,
+ .space = 100,
+ .seekth = 0xa,
+};
+
+static void adv7180_pwdn(int pwdn)
+{
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 1, pwdn);
+}
+
+static void adv7180_reset(void)
+{
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 6, 0);
+ msleep(5);
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 6, 1);
+ msleep(5);
+}
+
+static struct mxc_tvin_platform_data adv7180_data = {
+ .dvddio_reg = NULL,
+ .dvdd_reg = "SW3",
+ .avdd_reg = "PWGT2",
+ .pvdd_reg = NULL,
+ .pwdn = adv7180_pwdn,
+ .reset = adv7180_reset,
+};
+
+static struct i2c_board_info mxc_i2c_board_info[] __initdata = {
+ {
+ .type = "max8660",
+ .addr = 0x34,
+ },
+ {
+ .type = "tsc2007",
+ .addr = 0x48,
+ .platform_data = &tsc2007_data,
+ .irq = IOMUX_TO_IRQ(MX35_PIN_CAPTURE),
+ },
+ {
+ .type = "ov2640",
+ .addr = 0x30,
+ .platform_data = (void *)&camera_data,
+ },
+ {
+ .type = "sgtl5000-i2c",
+ .addr = 0x0a,
+ },
+ {
+ .type = "ak4647-i2c",
+ .addr = 0x12,
+ },
+#if defined(CONFIG_I2C_SLAVE_CLIENT)
+ {
+ .type = "i2c-slave-client",
+ .addr = 0x55,
+ },
+#endif
+ {
+ .type = "si4702",
+ .addr = 0x10,
+ .platform_data = (void *)&si4702_data,
+ },
+ {
+ .type = "adv7180",
+ .addr = 0x21,
+ .platform_data = (void *)&adv7180_data,
+ },
+};
+
+static struct spi_board_info mxc_spi_board_info[] __initdata = {
+ {
+ .modalias = "wm8580_spi",
+ .max_speed_hz = 8000000, /* max spi SCK clock speed in HZ */
+ .bus_num = 1,
+ .chip_select = 1,
+ },
+};
+
+#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
+static struct resource smsc911x_resources[] = {
+ {
+ .start = LAN9217_BASE_ADDR,
+ .end = LAN9217_BASE_ADDR + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = LAN9217_IRQ,
+ .end = LAN9217_IRQ,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device mxc_smsc911x_device = {
+ .name = "smsc911x",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(smsc911x_resources),
+ .resource = smsc911x_resources,
+};
+
+static void mxc_init_enet(void)
+{
+ platform_device_register(&mxc_smsc911x_device);
+}
+#else
+static inline void mxc_init_enet(void)
+{
+}
+#endif
+
+#if defined(CONFIG_FEC) || defined(CONFIG_FEC_MODULE)
+unsigned int expio_intr_fec;
+
+EXPORT_SYMBOL(expio_intr_fec);
+#endif
+
+#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
+static struct mxc_mmc_platform_data mmc1_data = {
+ .ocr_mask = MMC_VDD_32_33,
+#if defined(CONFIG_SDIO_UNIFI_FS) || defined(CONFIG_SDIO_UNIFI_FS_MODULE)
+ .caps = MMC_CAP_4_BIT_DATA,
+#else
+ .caps = MMC_CAP_8_BIT_DATA,
+#endif
+ .min_clk = 150000,
+ .max_clk = 52000000,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "sdhc_clk",
+};
+
+/*!
+ * Resource definition for the SDHC1
+ */
+static struct resource mxcsdhc1_resources[] = {
+ [0] = {
+ .start = MMC_SDHC1_BASE_ADDR,
+ .end = MMC_SDHC1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC1,
+ .end = MXC_INT_MMC_SDHC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = MXC_PSEUDO_IRQ_SD1_CD,
+ .end = MXC_PSEUDO_IRQ_SD1_CD,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC1 */
+static struct platform_device mxcsdhc1_device = {
+ .name = "mxsdhci",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc1_resources),
+ .resource = mxcsdhc1_resources,
+};
+
+#if defined(CONFIG_SDIO_UNIFI_FS) || defined(CONFIG_SDIO_UNIFI_FS_MODULE)
+static struct mxc_mmc_platform_data mmc2_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 |
+ MMC_VDD_31_32,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 150000,
+ .max_clk = 50000000,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "sdhc_clk",
+};
+
+static struct resource mxcsdhc2_resources[] = {
+ [0] = {
+ .start = MMC_SDHC2_BASE_ADDR,
+ .end = MMC_SDHC2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC2,
+ .end = MXC_INT_MMC_SDHC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mxcsdhc2_device = {
+ .name = "mxsdhci",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc2_resources),
+ .resource = mxcsdhc2_resources,
+};
+#endif
+
+static inline void mxc_init_mmc(void)
+{
+ (void)platform_device_register(&mxcsdhc1_device);
+#if defined(CONFIG_SDIO_UNIFI_FS) || defined(CONFIG_SDIO_UNIFI_FS_MODULE)
+ (void)platform_device_register(&mxcsdhc2_device);
+#endif
+}
+#else
+static inline void mxc_init_mmc(void)
+{
+}
+#endif
+
+#ifdef CONFIG_MXC_PSEUDO_IRQS
+/*! Device Definition for MXC SDHC1 */
+static struct platform_device mxc_pseudo_irq_device = {
+ .name = "mxc_pseudo_irq",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline int mxc_init_pseudo_irq(void)
+{
+ return platform_device_register(&mxc_pseudo_irq_device);
+}
+
+late_initcall(mxc_init_pseudo_irq);
+
+/*!
+ * Power Key interrupt handler.
+ */
+static irqreturn_t power_key_int(int irq, void *dev_id)
+{
+ pr_info(KERN_INFO "on-off key pressed\n");
+ return 0;
+}
+
+/*!
+ * Power Key initialization.
+ */
+static int __init mxc_init_power_key(void)
+{
+ if (!board_is_mx35(BOARD_REV_2)) {
+ /*Set power key as wakeup resource */
+ int irq, ret;
+ irq = MXC_PSEUDO_IRQ_POWER_KEY;
+ set_irq_type(irq, IRQF_TRIGGER_RISING);
+ ret = request_irq(irq, power_key_int, 0, "power_key", 0);
+ if (ret)
+ pr_info("register on-off key interrupt failed\n");
+ else
+ enable_irq_wake(irq);
+ return ret;
+ }
+ return 0;
+}
+
+late_initcall(mxc_init_power_key);
+#endif
+
+/*!
+ * the event handler for power on event
+ */
+static void power_on_evt_handler(void)
+{
+ pr_info("pwr on event1 is received \n");
+}
+
+#if defined(CONFIG_PATA_FSL) || defined(CONFIG_PATA_FSL_MODULE)
+extern void gpio_ata_active(void);
+extern void gpio_ata_inactive(void);
+
+static int ata_init(struct platform_device *pdev)
+{
+ /* Configure the pins */
+ gpio_ata_active();
+
+ return 0;
+}
+
+static void ata_exit(void)
+{
+ /* Free the pins */
+ gpio_ata_inactive();
+}
+
+static struct fsl_ata_platform_data ata_data = {
+ .adma_flag = 1, /* 0:smart dma, 1:ADMA */
+ .udma_mask = 0x3F,
+ .mwdma_mask = 0x1F,
+ .pio_mask = ATA_PIO4,
+ .fifo_alarm = MXC_IDE_DMA_WATERMARK / 2,
+ .max_sg = MXC_IDE_DMA_BD_NR,
+ .init = ata_init,
+ .exit = ata_exit,
+ .core_reg = NULL, /*"LDO2", */
+ .io_reg = NULL, /*"LDO3", */
+};
+
+static struct resource pata_fsl_resources[] = {
+ [0] = { /* I/O */
+ .start = ATA_BASE_ADDR,
+ .end = ATA_BASE_ADDR + 0x000000C8,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = { /* IRQ */
+ .start = MXC_INT_ATA,
+ .end = MXC_INT_ATA,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device pata_fsl_device = {
+ .name = "pata_fsl",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(pata_fsl_resources),
+ .resource = pata_fsl_resources,
+ .dev = {
+ .platform_data = &ata_data,
+ .coherent_dma_mask = ~0,
+ },
+};
+
+static void __init mxc_init_pata(void)
+{
+ (void)platform_device_register(&pata_fsl_device);
+}
+#else /* CONFIG_PATA_FSL */
+static void __init mxc_init_pata(void)
+{
+}
+#endif /* CONFIG_PATA_FSL */
+
+#if defined(CONFIG_GPS_IOCTRL) || defined(CONFIG_GPS_IOCTRL_MODULE)
+static struct mxc_gps_platform_data gps_data = {
+ .core_reg = "SW3",
+ .analog_reg = "PWGT2",
+};
+
+static struct platform_device mxc_gps_device = {
+ .name = "gps_ioctrl",
+ .id = 0,
+ .dev = {
+ .platform_data = &gps_data,
+ },
+};
+
+static void __init mxc_init_gps(void)
+{
+ (void)platform_device_register(&mxc_gps_device);
+}
+#else
+static void __init mxc_init_gps(void)
+{
+}
+#endif
+
+/*!
+ * Board specific fixup function. It is called by \b setup_arch() in
+ * setup.c file very early on during kernel starts. It allows the user to
+ * statically fill in the proper values for the passed-in parameters. None of
+ * the parameters is used currently.
+ *
+ * @param desc pointer to \b struct \b machine_desc
+ * @param tags pointer to \b struct \b tag
+ * @param cmdline pointer to the command line
+ * @param mi pointer to \b struct \b meminfo
+ */
+static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ mxc_cpu_init();
+
+#ifdef CONFIG_DISCONTIGMEM
+ do {
+ int nid;
+ mi->nr_banks = MXC_NUMNODES;
+ for (nid = 0; nid < mi->nr_banks; nid++)
+ SET_NODE(mi, nid);
+ } while (0);
+#endif
+}
+
+static void bt_reset(void)
+{
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 2, 0);
+ msleep(5);
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 2, 1);
+}
+
+static struct mxc_bt_platform_data mxc_bt_data = {
+ .bt_vdd = "GPO2",
+ .bt_vdd_parent = NULL,
+ .bt_vusb = NULL,
+ .bt_vusb_parent = NULL,
+ .bt_reset = bt_reset,
+};
+
+static struct platform_device mxc_bt_device = {
+ .name = "mxc_bt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_bt_data,
+ },
+};
+
+static void mxc_init_bluetooth(void)
+{
+ (void)platform_device_register(&mxc_bt_device);
+}
+
+#if defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000) \
+ || defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000_MODULE)
+
+static int sgtl5000_headphone_det_status(void)
+{
+ int ret = 0;
+ if (0 != pmic_gpio_get_designation_bit_val(0, &ret))
+ printk(KERN_ERR "Get headphone status error.");
+ return ret;
+}
+
+static int mxc_sgtl5000_plat_init(void);
+static int mxc_sgtl5000_plat_finit(void);
+static int mxc_sgtl5000_amp_enable(int enable);
+
+static struct mxc_audio_platform_data sgtl5000_data = {
+ .ssi_num = 1,
+ .src_port = 1,
+ .ext_port = 4,
+ .hp_irq = MXC_PSEUDO_IRQ_HEADPHONE,
+ .hp_status = sgtl5000_headphone_det_status,
+ .vddio_reg = NULL,
+ .vdda_reg = "VCAM",
+ .amp_enable = mxc_sgtl5000_amp_enable,
+ .vddio = 0,
+ .vdda = 3000000,
+ .vddd = 0,
+ .sysclk = 12000000,
+ .init = mxc_sgtl5000_plat_init,
+ .finit = mxc_sgtl5000_plat_finit,
+};
+
+static struct platform_device mxc_sgtl5000_device = {
+ .name = "imx-3stack-sgtl5000",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &sgtl5000_data,
+ },
+};
+
+static int mxc_sgtl5000_plat_init(void)
+{
+ struct regulator *reg;
+ reg = regulator_get(&mxc_sgtl5000_device.dev, "SPKR");
+ if (IS_ERR(reg))
+ return -EINVAL;
+ sgtl5000_data.priv = reg;
+ return 0;
+}
+
+static int mxc_sgtl5000_plat_finit(void)
+{
+ struct regulator *reg;
+ reg = sgtl5000_data.priv;
+ if (reg) {
+ regulator_put(reg);
+ sgtl5000_data.priv = NULL;
+ }
+ return 0;
+}
+
+static int mxc_sgtl5000_amp_enable(int enable)
+{
+ struct regulator *reg;
+ reg = sgtl5000_data.priv;
+
+ if (!reg)
+ return -EINVAL;
+ if (enable)
+ regulator_enable(reg);
+ else
+ regulator_disable(reg);
+ return 0;
+}
+
+static void mxc_init_sgtl5000(void)
+{
+ struct clk *cko1, *parent;
+ unsigned long rate;
+
+ /* for board v1.1 do nothing */
+ if (!board_is_mx35(BOARD_REV_2))
+ return;
+
+ cko1 = clk_get(NULL, "cko1_clk");
+ if (IS_ERR(cko1))
+ return;
+ parent = clk_get(NULL, "ckih");
+ if (IS_ERR(parent))
+ return;
+ clk_set_parent(cko1, parent);
+ rate = clk_round_rate(cko1, 12000000);
+ if (rate < 8000000 || rate > 27000000) {
+ printk(KERN_ERR "Error: SGTL5000 mclk freq %d out of range!\n",
+ (unsigned int)rate);
+ clk_put(parent);
+ clk_put(cko1);
+ return;
+ }
+ clk_set_rate(cko1, rate);
+ clk_enable(cko1);
+ sgtl5000_data.sysclk = rate;
+ platform_device_register(&mxc_sgtl5000_device);
+}
+#else
+static void mxc_init_sgtl5000(void)
+{
+}
+#endif
+
+#if defined(CONFIG_SND_SOC_IMX_3STACK_AK4647) \
+ || defined(CONFIG_SND_SOC_IMX_3STACK_AK4647_MODULE)
+static int mxc_ak4647_amp_enable(int enable)
+{
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 0, enable);
+ return 0;
+}
+
+static int mxc_ak4647_plat_init(void)
+{
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_2, 1, 0);
+ msleep(1);
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_2, 1, 1);
+ return 0;
+}
+
+static int ak4647_headphone_det_status(void)
+{
+ int ret = 0;
+ if (0 != pmic_gpio_get_designation_bit_val(0, &ret))
+ printk(KERN_ERR "Get headphone status error.");
+ return ret;
+}
+
+static struct mxc_audio_platform_data mxc_ak4647_data = {
+ .ssi_num = 1,
+ .src_port = 1,
+ .ext_port = 4,
+ .amp_enable = mxc_ak4647_amp_enable,
+ .init = mxc_ak4647_plat_init,
+ .hp_status = ak4647_headphone_det_status,
+ .intr_id_hp = MXC_PSEUDO_IRQ_HEADPHONE,
+};
+
+static struct platform_device mxc_alsa_device = {
+ .name = "imx-3stack-ak4647",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_ak4647_data,
+ },
+
+};
+
+static void mxc_init_ak4647(void)
+{
+ platform_device_register(&mxc_alsa_device);
+}
+#else
+static void mxc_init_ak4647(void)
+{
+}
+#endif
+
+#if defined(CONFIG_CAN_FLEXCAN) || defined(CONFIG_CAN_FLEXCAN_MODULE)
+static void flexcan_xcvr_enable(int id, int en)
+{
+ static int pwdn;
+
+ if (id < 0 || id > 1)
+ return;
+
+ if (en) {
+ if (!(pwdn++))
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2,
+ 1, 0);
+ } else {
+ if (!(--pwdn))
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2,
+ 1, 1);
+ }
+}
+
+struct flexcan_platform_data flexcan_data[] = {
+ {
+ .core_reg = "GPO2",
+ .io_reg = NULL,
+ .xcvr_enable = flexcan_xcvr_enable,
+ .active = gpio_can_active,
+ .inactive = gpio_can_inactive,},
+ {
+ .core_reg = "GPO2",
+ .io_reg = NULL,
+ .xcvr_enable = flexcan_xcvr_enable,
+ .active = gpio_can_active,
+ .inactive = gpio_can_inactive,},
+};
+#endif
+
+/*!
+ * fixup for mx35 3stack board v1.0 (MAX8660)
+ */
+static void mx35_3stack_fixup_for_board_v1(void)
+{
+#if defined(CONFIG_MXC_MLB) || defined(CONFIG_MXC_MLB_MODULE)
+ mlb_data.reg_nvcc = "LDO6";
+#endif
+
+#if defined(CONFIG_SDIO_UNIFI_FS) || defined(CONFIG_SDIO_UNIFI_FS_MODULE)
+ unifi_data.reg_gpo1 = NULL;
+ unifi_data.reg_gpo2 = NULL;
+ unifi_data.reg_1v5_ana_bb = "SW4";
+ unifi_data.reg_vdd_vpa = "SW1";
+ unifi_data.reg_1v5_dd = "SW4";
+#endif
+ camera_data.analog_regulator = "LDO7";
+ camera_data.core_regulator = NULL;
+ camera_data.io_regulator = NULL;
+ camera_data.gpo_regulator = NULL;
+ camera_data.mclk = 20000000;
+
+ adv7180_data.dvddio_reg = NULL;
+ adv7180_data.dvdd_reg = NULL;
+ adv7180_data.avdd_reg = NULL;
+ adv7180_data.pvdd_reg = NULL;
+
+ si4702_data.reg_vio = "SW1";
+ si4702_data.reg_vdd = NULL;
+
+#if defined(CONFIG_GPS_IOCTRL) || defined(CONFIG_GPS_IOCTRL_MODULE)
+ gps_data.core_reg = "SW1";
+ gps_data.analog_reg = "SW2";
+#endif
+
+ mxc_bt_data.bt_vdd = "SW1";
+
+#if defined(CONFIG_CAN_FLEXCAN) || defined(CONFIG_CAN_FLEXCAN_MODULE)
+ flexcan_data[0].core_reg = "SW1";
+ flexcan_data[1].core_reg = "SW1";
+#endif
+
+ tsc2007_data.vdd_reg = "SW1";
+ tsc2007_data.active = gpio_tsc_active;
+ tsc2007_data.inactive = gpio_tsc_inactive;
+}
+
+/*!
+ * Board specific initialization.
+ */
+static void __init mxc_board_init(void)
+{
+ mxc_cpu_common_init();
+
+ early_console_setup(saved_command_line);
+ mxc_register_gpios();
+ mxc_init_devices();
+ if (!board_is_mx35(BOARD_REV_2))
+ mx35_3stack_fixup_for_board_v1();
+ mx35_3stack_gpio_init();
+ mxc_init_enet();
+ mxc_init_nor_mtd();
+ mxc_init_nand_mtd();
+
+ mx35_3stack_init_mc13892();
+ mx35_3stack_init_mc9s08dz60();
+ mxc_init_lcd();
+ mxc_init_fb();
+ mxc_init_bl();
+ mxc_init_sgtl5000();
+ mxc_init_ak4647();
+
+ i2c_register_board_info(0, mxc_i2c_board_info,
+ ARRAY_SIZE(mxc_i2c_board_info));
+
+ spi_register_board_info(mxc_spi_board_info,
+ ARRAY_SIZE(mxc_spi_board_info));
+ mxc_init_mmc();
+ mxc_init_pata();
+ mxc_init_bluetooth();
+ mxc_init_gps();
+ mxc_init_mlb();
+}
+
+#define PLL_PCTL_REG(brmo, pd, mfd, mfi, mfn) \
+ (((brmo) << 31) + (((pd) - 1) << 26) + (((mfd) - 1) << 16) + \
+ ((mfi) << 10) + mfn)
+
+/* For 24MHz input clock */
+#define PLL_665MHZ PLL_PCTL_REG(1, 1, 48, 13, 41)
+#define PLL_532MHZ PLL_PCTL_REG(1, 1, 12, 11, 1)
+#define PLL_399MHZ PLL_PCTL_REG(0, 1, 16, 8, 5)
+
+/* working point(wp): 0,1 - 133MHz; 2,3 - 266MHz; 4,5 - 399MHz;*/
+/* auto input clock table */
+static struct cpu_wp cpu_wp_auto[] = {
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 133000000,
+ .pdr0_reg = (0x2 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 133000000,
+ .pdr0_reg = (0x6 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 266000000,
+ .pdr0_reg = (0x1 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 266000000,
+ .pdr0_reg = (0x5 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 399000000,
+ .pdr0_reg = (0x0 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 399000000,
+ .pdr0_reg = (0x6 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+};
+
+/* consumer input clock table */
+static struct cpu_wp cpu_wp_con[] = {
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 133000000,
+ .pdr0_reg = (0x6 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 133000000,
+ .pdr0_reg = (0xE << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 266000000,
+ .pdr0_reg = (0x2 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 266000000,
+ .pdr0_reg = (0xA << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 399000000,
+ .pdr0_reg = (0x1 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 399000000,
+ .pdr0_reg = (0x9 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 532000000,
+ .pdr0_reg = (0x0 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 532000000,
+ .pdr0_reg = (0x8 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_665MHZ,
+ .pll_rate = 665000000,
+ .cpu_rate = 665000000,
+ .pdr0_reg = (0x7 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+};
+
+struct cpu_wp *get_cpu_wp(int *wp)
+{
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1) {
+ *wp = 9;
+ return cpu_wp_con;
+ } else {
+ if (__raw_readl(MXC_CCM_PDR0) & MXC_CCM_PDR0_AUTO_CON) {
+ *wp = 9;
+ return cpu_wp_con;
+ } else {
+ *wp = 6;
+ return cpu_wp_auto;
+ }
+ }
+}
+
+static void __init mx35_3stack_timer_init(void)
+{
+ mxc_clocks_init(0, 0, 0, 0);
+ mxc_timer_init("gpt_clk");
+}
+
+static struct sys_timer mxc_timer = {
+ .init = mx35_3stack_timer_init,
+};
+
+/*
+ * The following uses standard kernel macros define in arch.h in order to
+ * initialize __mach_desc_MX35_3DS data structure.
+ */
+/* *INDENT-OFF* */
+MACHINE_START(MX35_3DS, "Freescale MX35 3-Stack Board")
+ /* Maintainer: Freescale Semiconductor, Inc. */
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .fixup = fixup_mxc_board,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mxc_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx35/mx35_3stack_cpld.c b/arch/arm/mach-mx35/mx35_3stack_cpld.c
new file mode 100644
index 000000000000..d62e24eaf12e
--- /dev/null
+++ b/arch/arm/mach-mx35/mx35_3stack_cpld.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+#include "board-mx35_3stack.h"
+#include "crm_regs.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx35/mx35_3stack_cpld.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX35
+ */
+static void mxc_expio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 expio_irq;
+ u32 index, mask;
+ desc->chip->mask(irq); /* irq = gpio irq number */
+
+ index = __raw_readw(mx35_3stack_board_io + INTR_STATUS_REG);
+ mask = __raw_readw(mx35_3stack_board_io + INTR_MASK_REG);
+
+ if (unlikely(!(index & (~mask)))) {
+ printk(KERN_ERR "\nEXPIO: Spurious interrupt:0x%0x\n\n", index);
+ pr_info("CPLD IMR(0x38)=0x%x, PENDING(0x28)=0x%x\n", mask,
+ index);
+ goto out;
+ }
+ index = index & (~mask);
+ expio_irq = MXC_EXP_IO_BASE;
+ for (; index != 0; index >>= 1, expio_irq++) {
+ struct irq_desc *d;
+ if ((index & 1) == 0)
+ continue;
+ d = irq_desc + expio_irq;
+ if (unlikely(!(d->handle_irq))) {
+ printk(KERN_ERR "\nEXPIO irq: %d unhandeled\n",
+ expio_irq);
+ BUG(); /* oops */
+ }
+ d->handle_irq(expio_irq, d);
+ }
+
+ out:
+ desc->chip->ack(irq);
+ desc->chip->unmask(irq);
+}
+
+/*
+ * Disable an expio pin's interrupt by setting the bit in the imr.
+ * @param irq an expio virtual irq number
+ */
+static void expio_mask_irq(u32 irq)
+{
+ u16 reg, expio = MXC_IRQ_TO_EXPIO(irq);
+
+ reg = __raw_readw(mx35_3stack_board_io + INTR_MASK_REG);
+ /* mask the interrupt */
+ __raw_writew(reg | (1 << expio), mx35_3stack_board_io + INTR_MASK_REG);
+}
+
+/*
+ * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr.
+ * @param irq an expanded io virtual irq number
+ */
+static void expio_ack_irq(u32 irq)
+{
+ u32 expio = MXC_IRQ_TO_EXPIO(irq);
+ /* clear the interrupt status */
+ __raw_writew(1 << expio, mx35_3stack_board_io + INTR_RESET_REG);
+ __raw_writew(0, mx35_3stack_board_io + INTR_RESET_REG);
+ /* mask the interrupt */
+ expio_mask_irq(irq);
+}
+
+/*
+ * Enable a expio pin's interrupt by clearing the bit in the imr.
+ * @param irq a expio virtual irq number
+ */
+static void expio_unmask_irq(u32 irq)
+{
+ u16 reg, expio = MXC_IRQ_TO_EXPIO(irq);
+
+ reg = __raw_readw(mx35_3stack_board_io + INTR_MASK_REG);
+ /* unmask the interrupt */
+ __raw_writew(reg & (~(1 << expio)),
+ mx35_3stack_board_io + INTR_MASK_REG);
+}
+
+static struct irq_chip expio_irq_chip = {
+ .ack = expio_ack_irq,
+ .mask = expio_mask_irq,
+ .unmask = expio_unmask_irq,
+};
+
+static int __init mxc_expio_init(void)
+{
+ int i;
+
+ mx35_3stack_board_io = (u32) ioremap(BOARD_IO_ADDR, SZ_4K);
+ if (mx35_3stack_board_io == 0)
+ return -ENOMEM;
+
+ if ((__raw_readw(mx35_3stack_board_io + MAGIC_NUMBER1_REG) != 0xAAAA) ||
+ (__raw_readw(mx35_3stack_board_io + MAGIC_NUMBER2_REG) != 0x5555))
+ return -ENODEV;
+
+ pr_info("3-Stack Debug board detected, rev = 0x%04X\n",
+ readw(mx35_3stack_board_io + CPLD_CODE_VER_REG));
+
+ /*
+ * Configure INT line as GPIO input
+ */
+ mxc_request_iomux(EXPIO_PARENT_INT, MUX_CONFIG_FUNC);
+ gpio_request(IOMUX_TO_GPIO(EXPIO_PARENT_INT), NULL);
+ gpio_direction_input(IOMUX_TO_GPIO(EXPIO_PARENT_INT));
+
+ /* disable the interrupt and clear the status */
+ __raw_writew(0, mx35_3stack_board_io + INTR_MASK_REG);
+ __raw_writew(0xFFFF, mx35_3stack_board_io + INTR_RESET_REG);
+ __raw_writew(0, mx35_3stack_board_io + INTR_RESET_REG);
+ __raw_writew(0x1F, mx35_3stack_board_io + INTR_MASK_REG);
+ for (i = MXC_EXP_IO_BASE; i < (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES);
+ i++) {
+ set_irq_chip(i, &expio_irq_chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+ set_irq_type(IOMUX_TO_IRQ(EXPIO_PARENT_INT), IRQF_TRIGGER_LOW);
+ set_irq_chained_handler(IOMUX_TO_IRQ(EXPIO_PARENT_INT),
+ mxc_expio_irq_handler);
+ return 0;
+}
+
+arch_initcall(mxc_expio_init);
diff --git a/arch/arm/mach-mx35/mx35_3stack_gpio.c b/arch/arm/mach-mx35/mx35_3stack_gpio.c
new file mode 100644
index 000000000000..422e2cd6dcd9
--- /dev/null
+++ b/arch/arm/mach-mx35/mx35_3stack_gpio.c
@@ -0,0 +1,1380 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include "board-mx35_3stack.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx35/mx35_3stack_gpio.c
+ *
+ * @brief This file contains all the GPIO setup functions for the board.
+ *
+ * @ingroup GPIO_MX35
+ */
+
+/*!
+ * This system-wise GPIO function initializes the pins during system startup.
+ * All the statically linked device drivers should put the proper GPIO
+ * initialization code inside this function. It is called by \b fixup_mx31ads()
+ * during system startup. This function is board specific.
+ */
+void mx35_3stack_gpio_init(void)
+{
+ /* config CS5 */
+ mxc_request_iomux(MX35_PIN_CS5, MUX_CONFIG_FUNC);
+
+ /* configure capture pin for ckil input */
+ mxc_request_iomux(MX35_PIN_CAPTURE, MUX_CONFIG_ALT4);
+ mxc_iomux_set_pad(MX35_PIN_CAPTURE,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_100K_PU | PAD_CTL_PUE_PUD);
+ mxc_iomux_set_input(MUX_IN_CCM_32K_MUXED, INPUT_CTL_PATH0);
+
+}
+
+/*!
+ * Setup GPIO for a UART port to be active
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_active(int port, int no_irda)
+{
+ /*
+ * Configure the IOMUX control registers for the UART signals
+ */
+ switch (port) {
+ /* UART 1 IOMUX Configs */
+ case 0:
+ mxc_request_iomux(MX35_PIN_RXD1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_TXD1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_RTS1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CTS1, MUX_CONFIG_FUNC);
+
+ mxc_iomux_set_pad(MX35_PIN_RXD1,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX35_PIN_TXD1,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_RTS1,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX35_PIN_CTS1,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+
+ break;
+ /* UART 2 IOMUX Configs */
+ case 1:
+ mxc_request_iomux(MX35_PIN_TXD2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_RXD2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_RTS2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CTS2, MUX_CONFIG_FUNC);
+ mxc_iomux_set_pad(MX35_PIN_RXD2,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX35_PIN_TXD2,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_RTS2,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX35_PIN_CTS2,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+ break;
+ /* UART 3 IOMUX Configs */
+ case 2:
+ mxc_request_iomux(MX35_PIN_FEC_TX_CLK, MUX_CONFIG_ALT2);
+ mxc_request_iomux(MX35_PIN_FEC_RX_CLK, MUX_CONFIG_ALT2);
+ mxc_request_iomux(MX35_PIN_FEC_COL, MUX_CONFIG_ALT2);
+ mxc_request_iomux(MX35_PIN_FEC_RX_DV, MUX_CONFIG_ALT2);
+
+ mxc_iomux_set_pad(MX35_PIN_FEC_TX_CLK,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX35_PIN_FEC_RX_CLK,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_RX_DV,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX35_PIN_FEC_COL,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+
+ mxc_iomux_set_input(MUX_IN_UART3_UART_RTS_B, INPUT_CTL_PATH2);
+ mxc_iomux_set_input(MUX_IN_UART3_UART_RXD_MUX, INPUT_CTL_PATH3);
+ break;
+ default:
+ break;
+ }
+
+}
+
+EXPORT_SYMBOL(gpio_uart_active);
+
+/*!
+ * Setup GPIO for a UART port to be inactive
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_inactive(int port, int no_irda)
+{
+ switch (port) {
+ case 0:
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_RXD1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_TXD1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_RTS1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CTS1), NULL);
+
+ mxc_free_iomux(MX35_PIN_RXD1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_TXD1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_RTS1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CTS1, MUX_CONFIG_GPIO);
+ break;
+ case 1:
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_RXD2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_TXD2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_RTS2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CTS2), NULL);
+
+ mxc_free_iomux(MX35_PIN_RXD2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_TXD2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_RTS2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CTS2, MUX_CONFIG_GPIO);
+ break;
+ case 2:
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_TX_CLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RX_CLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_COL), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RX_DV), NULL);
+
+ mxc_free_iomux(MX35_PIN_FEC_TX_CLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RX_CLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_COL, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RX_DV, MUX_CONFIG_GPIO);
+
+ mxc_iomux_set_input(MUX_IN_UART3_UART_RTS_B, INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_UART3_UART_RXD_MUX, INPUT_CTL_PATH0);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_uart_inactive);
+
+/*!
+ * Configure the IOMUX GPR register to receive shared SDMA UART events
+ *
+ * @param port a UART port
+ */
+void config_uartdma_event(int port)
+{
+}
+
+EXPORT_SYMBOL(config_uartdma_event);
+
+void gpio_fec_active(void)
+{
+ mxc_request_iomux(MX35_PIN_FEC_TX_CLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_RX_CLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_RX_DV, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_COL, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_RDATA0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_TDATA0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_TX_EN, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_MDC, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_MDIO, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_TX_ERR, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_RX_ERR, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_CRS, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_RDATA1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_TDATA1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_RDATA2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_TDATA2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_RDATA3, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FEC_TDATA3, MUX_CONFIG_FUNC);
+
+#define FEC_PAD_CTL_COMMON (PAD_CTL_DRV_3_3V|PAD_CTL_PUE_PUD| \
+ PAD_CTL_ODE_CMOS|PAD_CTL_DRV_NORMAL|PAD_CTL_SRE_SLOW)
+ mxc_iomux_set_pad(MX35_PIN_FEC_TX_CLK, FEC_PAD_CTL_COMMON |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_RX_CLK,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_RX_DV,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_COL,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_RDATA0,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_TDATA0,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_CMOS |
+ PAD_CTL_PKE_NONE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_TX_EN,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_CMOS |
+ PAD_CTL_PKE_NONE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_MDC,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_CMOS |
+ PAD_CTL_PKE_NONE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_MDIO,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_22K_PU);
+ mxc_iomux_set_pad(MX35_PIN_FEC_TX_ERR,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_CMOS |
+ PAD_CTL_PKE_NONE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_RX_ERR,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_CRS,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_RDATA1,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_TDATA1,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_CMOS |
+ PAD_CTL_PKE_NONE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_RDATA2,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_TDATA2,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_CMOS |
+ PAD_CTL_PKE_NONE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_RDATA3,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_FEC_TDATA3,
+ FEC_PAD_CTL_COMMON | PAD_CTL_HYS_CMOS |
+ PAD_CTL_PKE_NONE | PAD_CTL_100K_PD);
+#undef FEC_PAD_CTL_COMMON
+ /* Pull GPIO1_5 to be high for routing signal to FEC */
+ if (board_is_mx35(BOARD_REV_2)) {
+ mxc_request_iomux(MX35_PIN_COMPARE, MUX_CONFIG_GPIO);
+ mxc_iomux_set_pad(MX35_PIN_COMPARE, PAD_CTL_DRV_NORMAL |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU |
+ PAD_CTL_DRV_3_3V | PAD_CTL_PUE_PUD |
+ PAD_CTL_SRE_SLOW);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_COMPARE), "compare");
+ gpio_direction_output(IOMUX_TO_GPIO(MX35_PIN_COMPARE), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX35_PIN_COMPARE), 1);
+ }
+
+ /* FEC enable */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 2, 1);
+ /* FEC reset */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 7, 0);
+ msleep(10);
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 7, 1);
+ msleep(100);
+}
+
+EXPORT_SYMBOL(gpio_fec_active);
+
+void gpio_fec_inactive(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_TX_CLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RX_CLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RX_DV), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_COL), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RDATA0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_TDATA0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_TX_EN), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_MDC), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_MDIO), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_TX_ERR), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RX_ERR), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_CRS), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RDATA1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_TDATA1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RDATA2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_TDATA2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RDATA3), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_TDATA3), NULL);
+
+ mxc_free_iomux(MX35_PIN_FEC_TX_CLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RX_CLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RX_DV, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_COL, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RDATA0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_TDATA0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_TX_EN, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_MDC, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_MDIO, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_TX_ERR, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RX_ERR, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_CRS, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RDATA1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_TDATA1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RDATA2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_TDATA2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RDATA3, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_TDATA3, MUX_CONFIG_GPIO);
+
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 2, 0);
+
+ /* Free GPIO1_5 */
+ if (board_is_mx35(BOARD_REV_2)) {
+ gpio_free(IOMUX_TO_GPIO(MX35_PIN_COMPARE));
+ mxc_free_iomux(MX35_PIN_COMPARE, MUX_CONFIG_GPIO);
+ }
+}
+
+EXPORT_SYMBOL(gpio_fec_inactive);
+
+/*!
+ * Setup GPIO for an I2C device to be active
+ *
+ * @param i2c_num an I2C device
+ */
+void gpio_i2c_active(int i2c_num)
+{
+
+#define PAD_CONFIG (PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD | PAD_CTL_ODE_OpenDrain)
+
+ switch (i2c_num) {
+ case 0:
+ mxc_request_iomux(MX35_PIN_I2C1_CLK, MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_I2C1_DAT, MUX_CONFIG_SION);
+
+ mxc_iomux_set_pad(MX35_PIN_I2C1_CLK, PAD_CONFIG);
+ mxc_iomux_set_pad(MX35_PIN_I2C1_DAT, PAD_CONFIG);
+ break;
+ case 1:
+ mxc_request_iomux(MX35_PIN_I2C2_CLK, MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_I2C2_DAT, MUX_CONFIG_SION);
+
+ mxc_iomux_set_pad(MX35_PIN_I2C2_CLK, PAD_CONFIG);
+ mxc_iomux_set_pad(MX35_PIN_I2C2_DAT, PAD_CONFIG);
+
+ break;
+ case 2:
+ mxc_request_iomux(MX35_PIN_TX3_RX2, MUX_CONFIG_ALT1);
+ mxc_request_iomux(MX35_PIN_TX2_RX3, MUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX35_PIN_TX3_RX2, PAD_CONFIG);
+ mxc_iomux_set_pad(MX35_PIN_TX2_RX3, PAD_CONFIG);
+ break;
+ default:
+ break;
+ }
+
+#undef PAD_CONFIG
+
+}
+
+EXPORT_SYMBOL(gpio_i2c_active);
+
+/*!
+ * Setup GPIO for an I2C device to be inactive
+ *
+ * @param i2c_num an I2C device
+ */
+void gpio_i2c_inactive(int i2c_num)
+{
+ switch (i2c_num) {
+ case 0:
+ break;
+ case 1:
+ break;
+ case 2:
+ mxc_request_iomux(MX35_PIN_TX3_RX2, MUX_CONFIG_GPIO);
+ mxc_request_iomux(MX35_PIN_TX2_RX3, MUX_CONFIG_GPIO);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_i2c_inactive);
+
+/*!
+ * Setup GPIO for a CSPI device to be active
+ *
+ * @param cspi_mod an CSPI device
+ */
+void gpio_spi_active(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ mxc_request_iomux(MX35_PIN_CSPI1_MOSI, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSPI1_MISO, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSPI1_SS0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSPI1_SS1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSPI1_SCLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSPI1_SPI_RDY, MUX_CONFIG_FUNC);
+
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_MOSI,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PD | PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_MISO,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PD | PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_SS0,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PU | PAD_CTL_ODE_CMOS |
+ PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_SS1,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PU | PAD_CTL_ODE_CMOS |
+ PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_SCLK,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PD | PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_SPI_RDY,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PU | PAD_CTL_DRV_NORMAL);
+ break;
+ case 1:
+ /* SPI2 */
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_spi_active);
+
+/*!
+ * Setup GPIO for a CSPI device to be inactive
+ *
+ * @param cspi_mod a CSPI device
+ */
+void gpio_spi_inactive(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_MOSI), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_MISO), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_SS0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_SS1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_SCLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_SPI_RDY), NULL);
+
+ mxc_free_iomux(MX35_PIN_CSPI1_MOSI, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CSPI1_MISO, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CSPI1_SS0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CSPI1_SS1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CSPI1_SCLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CSPI1_SPI_RDY, MUX_CONFIG_GPIO);
+ break;
+ case 1:
+ /* SPI2 */
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_spi_inactive);
+
+/*!
+ * Setup GPIO for LCD to be active
+ */
+void gpio_lcd_active(void)
+{
+ mxc_request_iomux(MX35_PIN_LD0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD3, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD4, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD5, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD6, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD7, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD8, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD9, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD10, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD11, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD12, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD13, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD14, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD15, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD16, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_LD17, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_D3_VSYNC, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_D3_HSYNC, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_D3_FPSHIFT, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_D3_DRDY, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CONTRAST, MUX_CONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_lcd_active);
+
+/*!
+ * Setup GPIO for LCD to be inactive
+ */
+void gpio_lcd_inactive(void)
+{
+}
+
+EXPORT_SYMBOL(gpio_lcd_inactive);
+
+/*!
+ * Setup pin for touchscreen
+ */
+void gpio_tsc_active(void)
+{
+ unsigned int pad_val = PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU;
+ mxc_request_iomux(MX35_PIN_CAPTURE, MUX_CONFIG_GPIO);
+ mxc_iomux_set_pad(MX35_PIN_CAPTURE, pad_val);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CAPTURE), "capture");
+ gpio_direction_input(IOMUX_TO_GPIO(MX35_PIN_CAPTURE));
+}
+
+/*!
+ * Release pin for touchscreen
+ */
+void gpio_tsc_inactive(void)
+{
+ gpio_free(IOMUX_TO_GPIO(MX35_PIN_CAPTURE));
+ mxc_free_iomux(MX35_PIN_CAPTURE, MUX_CONFIG_GPIO);
+}
+
+/*!
+ * Setup GPIO for SDHC to be active
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_active(int module)
+{
+ unsigned int pad_val;
+
+ switch (module) {
+ case 0:
+ mxc_request_iomux(MX35_PIN_SD1_CLK,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD1_CMD,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD1_DATA0,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD1_DATA1,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD1_DATA2,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD1_DATA3,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+#if defined(CONFIG_SDIO_UNIFI_FS) || defined(CONFIG_SDIO_UNIFI_FS_MODULE)
+#else
+ /* MUX4_CTR , 0: SD2 to WIFI, 1:SD2 to SD1 8bit */
+ if (board_is_mx35(BOARD_REV_2))
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2,
+ 7, 1);
+ mxc_request_iomux(MX35_PIN_SD2_CMD,
+ MUX_CONFIG_ALT2 | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD2_CLK,
+ MUX_CONFIG_ALT2 | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD2_DATA0,
+ MUX_CONFIG_ALT2 | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD2_DATA1,
+ MUX_CONFIG_ALT2 | MUX_CONFIG_SION);
+#endif
+
+ pad_val = PAD_CTL_PUE_PUD | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_DRV_MAX |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST;
+ mxc_iomux_set_pad(MX35_PIN_SD1_CMD, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SD1_DATA0, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SD1_DATA1, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SD1_DATA2, pad_val);
+ pad_val = PAD_CTL_PUE_PUD | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_DRV_MAX | PAD_CTL_47K_PU | PAD_CTL_SRE_FAST;
+ mxc_iomux_set_pad(MX35_PIN_SD1_CLK, pad_val);
+ pad_val = PAD_CTL_PUE_PUD | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_DRV_MAX |
+ PAD_CTL_100K_PU | PAD_CTL_SRE_FAST;
+ mxc_iomux_set_pad(MX35_PIN_SD1_DATA3, pad_val);
+#if defined(CONFIG_SDIO_UNIFI_FS) || defined(CONFIG_SDIO_UNIFI_FS_MODULE)
+#else
+ pad_val = PAD_CTL_PUE_PUD | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_DRV_MAX |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST;
+ mxc_iomux_set_pad(MX35_PIN_SD2_CMD, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA0, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA1, pad_val);
+ pad_val = PAD_CTL_PUE_PUD | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_DRV_MAX | PAD_CTL_47K_PU | PAD_CTL_SRE_FAST;
+ mxc_iomux_set_pad(MX35_PIN_SD2_CLK, pad_val);
+#endif
+ break;
+ case 1:
+ /* MUX4_CTR , 0: SD2 to WIFI, 1:SD2 to SD1 8bit */
+ if (board_is_mx35(BOARD_REV_2))
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2,
+ 7, 0);
+ mxc_request_iomux(MX35_PIN_SD2_CLK,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD2_CMD,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD2_DATA0,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD2_DATA1,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD2_DATA2,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_request_iomux(MX35_PIN_SD2_DATA3,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+
+ pad_val = PAD_CTL_PUE_PUD | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_DRV_MAX |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST;
+
+ mxc_iomux_set_pad(MX35_PIN_SD2_CLK, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SD2_CMD, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA0, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA1, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA2, pad_val);
+
+ pad_val = PAD_CTL_PUE_PUD | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_DRV_MAX |
+ PAD_CTL_100K_PU | PAD_CTL_SRE_FAST;
+
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA3, pad_val);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_sdhc_active);
+
+/*!
+ * Setup GPIO for SDHC1 to be inactive
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_inactive(int module)
+{
+ switch (module) {
+ case 0:
+ mxc_free_iomux(MX35_PIN_SD1_CLK,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD1_CMD,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD1_DATA0,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD1_DATA1,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD1_DATA2,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD1_DATA3,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD2_CMD,
+ MUX_CONFIG_ALT2 | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD2_CLK,
+ MUX_CONFIG_ALT2 | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD2_DATA0,
+ MUX_CONFIG_ALT2 | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD2_DATA1,
+ MUX_CONFIG_ALT2 | MUX_CONFIG_SION);
+
+ mxc_iomux_set_pad(MX35_PIN_SD1_CLK,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD1_CMD,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD1_DATA0,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD1_DATA1,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD1_DATA2,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD1_DATA3,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD2_CMD,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD2_CLK,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA0,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA1,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ break;
+ case 1:
+ mxc_free_iomux(MX35_PIN_SD2_CLK,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD2_CMD,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD2_DATA0,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD2_DATA1,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD2_DATA2,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+ mxc_free_iomux(MX35_PIN_SD2_DATA3,
+ MUX_CONFIG_FUNC | MUX_CONFIG_SION);
+
+ mxc_iomux_set_pad(MX35_PIN_SD2_CLK,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD2_CMD,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA0,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA1,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA2,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX35_PIN_SD2_DATA3,
+ (PAD_CTL_DRV_NORMAL | PAD_CTL_SRE_SLOW));
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_sdhc_inactive);
+
+/*
+ * Probe for the card. If present the GPIO data would be set.
+ */
+unsigned int sdhc_get_card_det_status(struct device *dev)
+{
+ unsigned int ret;
+
+ if (to_platform_device(dev)->id == 0) {
+ if (0 != pmic_gpio_get_designation_bit_val(2, &ret))
+ printk(KERN_ERR "Get cd status error.");
+ return ret;
+ } else { /* config the det pin for SDHC2 */
+ return 0;
+ }
+}
+
+EXPORT_SYMBOL(sdhc_get_card_det_status);
+
+/*!
+ * Get pin value to detect write protection
+ */
+int sdhc_write_protect(struct device *dev)
+{
+ unsigned int rc = 0;
+
+ if (0 != pmic_gpio_get_designation_bit_val(3, &rc))
+ printk(KERN_ERR "Get wp status error.");
+ return rc;
+}
+
+EXPORT_SYMBOL(sdhc_write_protect);
+
+/*
+ * USB Host2
+ */
+int gpio_usbh2_active(void)
+{
+ if (board_is_mx35(BOARD_REV_2)) {
+ /* MUX3_CTR to be low for USB Host2 DP&DM */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 6, 0);
+ /* CAN_PWDN to be high for USB Host2 Power&OC */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 1, 1);
+ }
+
+ mxc_request_iomux(MX35_PIN_I2C2_CLK, MUX_CONFIG_ALT2);
+ mxc_iomux_set_pad(MX35_PIN_I2C2_CLK, 0x0040);
+
+ mxc_request_iomux(MX35_PIN_I2C2_DAT, MUX_CONFIG_ALT2);
+ mxc_iomux_set_input(MUX_IN_USB_UH2_USB_OC, INPUT_CTL_PATH0);
+ mxc_iomux_set_pad(MX35_PIN_I2C2_DAT, 0x01c0);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_usbh2_active);
+
+void gpio_usbh2_inactive(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_I2C2_DAT), NULL);
+ mxc_free_iomux(MX35_PIN_I2C2_DAT, MUX_CONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_I2C2_CLK), NULL);
+ mxc_free_iomux(MX35_PIN_I2C2_CLK, MUX_CONFIG_GPIO);
+}
+
+EXPORT_SYMBOL(gpio_usbh2_inactive);
+
+/*
+ * USB OTG UTMI
+ */
+int gpio_usbotg_utmi_active(void)
+{
+ mxc_request_iomux(MX35_PIN_USBOTG_PWR, MUX_CONFIG_FUNC);
+ mxc_iomux_set_pad(MX35_PIN_USBOTG_PWR, 0x0040);
+ mxc_request_iomux(MX35_PIN_USBOTG_OC, MUX_CONFIG_FUNC);
+ mxc_iomux_set_pad(MX35_PIN_USBOTG_OC, 0x01c0);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_usbotg_utmi_active);
+
+void gpio_usbotg_utmi_inactive(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_USBOTG_PWR), NULL);
+ mxc_free_iomux(MX35_PIN_USBOTG_PWR, MUX_CONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_USBOTG_OC), NULL);
+ mxc_free_iomux(MX35_PIN_USBOTG_OC, MUX_CONFIG_GPIO);
+}
+
+EXPORT_SYMBOL(gpio_usbotg_utmi_inactive);
+
+void gpio_sensor_active(void)
+{
+ /*CSI D6 */
+ mxc_request_iomux(MX35_PIN_TX1, MUX_CONFIG_ALT6);
+ /*CSI D7 */
+ mxc_request_iomux(MX35_PIN_TX0, MUX_CONFIG_ALT6);
+ mxc_request_iomux(MX35_PIN_CSI_D8, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_D9, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_D10, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_D11, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_D12, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_D13, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_D14, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_D15, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_HSYNC, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_MCLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_PIXCLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSI_VSYNC, MUX_CONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_sensor_active);
+
+void gpio_sensor_inactive(void)
+{
+ mxc_request_iomux(MX35_PIN_TX1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_TX0, MUX_CONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_sensor_inactive);
+
+/*!
+ * Setup GPIO for spdif tx/rx to be active
+ */
+void gpio_spdif_active(void)
+{
+ /* SPDIF OUT */
+ mxc_request_iomux(MX35_PIN_STXD5, MUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX35_PIN_STXD5, PAD_CTL_PKE_NONE | PAD_CTL_PUE_PUD);
+ /* SPDIF IN */
+ mxc_request_iomux(MX35_PIN_SRXD5, MUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX35_PIN_SRXD5, PAD_CTL_PKE_ENABLE
+ | PAD_CTL_100K_PU | PAD_CTL_HYS_SCHMITZ);
+ /* SPDIF ext clock */
+ mxc_request_iomux(MX35_PIN_SCK5, MUX_CONFIG_ALT1);
+ if (board_is_mx35(BOARD_REV_2))
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 5, 1);
+ else
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_2, 0, 1);
+}
+
+EXPORT_SYMBOL(gpio_spdif_active);
+
+/*!
+ * Setup GPIO for spdif tx/rx to be inactive
+ */
+void gpio_spdif_inactive(void)
+{
+ /* SPDIF OUT */
+ mxc_free_iomux(MX35_PIN_STXD5, MUX_CONFIG_ALT1);
+ /* SPDIF IN */
+ mxc_free_iomux(MX35_PIN_SRXD5, MUX_CONFIG_ALT1);
+ /* SPDIF ext clock */
+ mxc_free_iomux(MX35_PIN_SCK5, MUX_CONFIG_ALT1);
+}
+
+EXPORT_SYMBOL(gpio_spdif_inactive);
+
+/*!
+ * This function activates DAM ports 3 to enable
+ * audio I/O.
+ */
+void gpio_activate_audio_ports(void)
+{
+ unsigned int pad_val;
+
+ mxc_request_iomux(MX35_PIN_STXD4, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_SRXD4, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_SCK4, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_STXFS4, MUX_CONFIG_FUNC);
+
+ pad_val = PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_PUD;
+ mxc_iomux_set_pad(MX35_PIN_STXD4, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SRXD4, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SCK4, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_STXFS4, pad_val);
+}
+
+EXPORT_SYMBOL(gpio_activate_audio_ports);
+
+/*!
+ * This function deactivates DAM ports 3 to disable
+ * audio I/O.
+ */
+void gpio_inactivate_audio_ports(void)
+{
+ mxc_free_iomux(MX35_PIN_STXD4, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_SRXD4, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_SCK4, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_STXFS4, MUX_CONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_inactivate_audio_ports);
+
+/*!
+ * This function activates DAM ports 5 to enable
+ * audio I/O.
+ */
+void gpio_activate_bt_audio_port(void)
+{
+ unsigned int pad_val;
+
+ mxc_request_iomux(MX35_PIN_STXD5, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_SRXD5, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_SCK5, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_STXFS5, MUX_CONFIG_FUNC);
+
+ pad_val = PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_PUD;
+ mxc_iomux_set_pad(MX35_PIN_STXD5, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SRXD5, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_SCK5, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_STXFS5, pad_val);
+ if (board_is_mx35(BOARD_REV_2))
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 5, 0);
+ else
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_2, 0, 0);
+}
+
+EXPORT_SYMBOL(gpio_activate_bt_audio_port);
+
+/*!
+ * Setup GPIO for bluetooth audio to be inactive
+ */
+void gpio_inactivate_bt_audio_port(void)
+{
+ mxc_free_iomux(MX35_PIN_STXD5, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_SRXD5, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_SCK5, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_STXFS5, MUX_CONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_inactivate_bt_audio_port);
+
+/*!
+ * Setup GPIO for ATA interface
+ *
+ */
+void gpio_ata_active(void)
+{
+ unsigned int ata_ctl_pad_cfg, ata_dat_pad_cfg;
+
+ /* HDD_ENBALE */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 3, 0);
+ /* Power On the HDD */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 4, 1);
+ msleep(300);
+
+ /*IOMUX Settings */
+ /*PATA_DIOR */
+ mxc_request_iomux(MX35_PIN_ATA_DIOR, MUX_CONFIG_FUNC);
+ /*PATA_DIOW */
+ mxc_request_iomux(MX35_PIN_ATA_DIOW, MUX_CONFIG_FUNC);
+ /*PATA_DMARQ_B */
+ mxc_request_iomux(MX35_PIN_ATA_DMARQ, MUX_CONFIG_FUNC);
+ /*PATA_DMACK */
+ mxc_request_iomux(MX35_PIN_ATA_DMACK, MUX_CONFIG_FUNC);
+ /*PATA_RESET_B */
+ mxc_request_iomux(MX35_PIN_ATA_RESET_B, MUX_CONFIG_FUNC);
+ /*PATA_IORDY */
+ mxc_request_iomux(MX35_PIN_ATA_IORDY, MUX_CONFIG_FUNC);
+ /*PATA_INTRQ_B */
+ mxc_request_iomux(MX35_PIN_ATA_INTRQ, MUX_CONFIG_FUNC);
+ /*PATA_CS_0 */
+ mxc_request_iomux(MX35_PIN_ATA_CS0, MUX_CONFIG_FUNC);
+ /*PATA_CS_1 */
+ mxc_request_iomux(MX35_PIN_ATA_CS1, MUX_CONFIG_FUNC);
+ /*PATA_DA0 */
+ mxc_request_iomux(MX35_PIN_ATA_DA0, MUX_CONFIG_FUNC);
+ /*PATA_DA1 */
+ mxc_request_iomux(MX35_PIN_ATA_DA1, MUX_CONFIG_FUNC);
+ /*PATA_DA2 */
+ mxc_request_iomux(MX35_PIN_ATA_DA2, MUX_CONFIG_FUNC);
+ /* BUFFER_ENABLE - HDD_ENABLE_B */
+ mxc_request_iomux(MX35_PIN_ATA_BUFF_EN, MUX_CONFIG_FUNC);
+
+ /*PATA_D0 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA0, MUX_CONFIG_FUNC);
+ /*PATA_D1 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA1, MUX_CONFIG_FUNC);
+ /*PATA_D2 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA2, MUX_CONFIG_FUNC);
+ /*PATA_D3 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA3, MUX_CONFIG_FUNC);
+ /*PATA_D4 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA4, MUX_CONFIG_FUNC);
+ /*PATA_D5 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA5, MUX_CONFIG_FUNC);
+ /*PATA_D6 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA6, MUX_CONFIG_FUNC);
+ /*PATA_D7 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA7, MUX_CONFIG_FUNC);
+ /*PATA_D8 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA8, MUX_CONFIG_FUNC);
+ /*PATA_D9 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA9, MUX_CONFIG_FUNC);
+ /*PATA_D10 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA10, MUX_CONFIG_FUNC);
+ /*PATA_D11 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA11, MUX_CONFIG_FUNC);
+ /*PATA_D12 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA12, MUX_CONFIG_FUNC);
+ /*PATA_D13 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA13, MUX_CONFIG_FUNC);
+ /*PATA_D14 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA14, MUX_CONFIG_FUNC);
+ /*PATA_D15 */
+ mxc_request_iomux(MX35_PIN_ATA_DATA15, MUX_CONFIG_FUNC);
+
+ /* IOMUX Pad Settings */
+ ata_ctl_pad_cfg = PAD_CTL_SRE_SLOW | PAD_CTL_DRV_NORMAL |
+ PAD_CTL_ODE_CMOS | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD |
+ PAD_CTL_HYS_CMOS | PAD_CTL_DRV_3_3V;
+ ata_dat_pad_cfg = PAD_CTL_SRE_FAST | PAD_CTL_DRV_MAX |
+ PAD_CTL_ODE_CMOS | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD |
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_DRV_3_3V;
+
+ mxc_iomux_set_pad(MX35_PIN_ATA_DMARQ, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DIOR, ata_ctl_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DIOW, ata_ctl_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DMACK, ata_ctl_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_RESET_B, PAD_CTL_SRE_SLOW |
+ PAD_CTL_DRV_NORMAL | PAD_CTL_ODE_CMOS |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PU | PAD_CTL_HYS_CMOS |
+ PAD_CTL_DRV_3_3V);
+ mxc_iomux_set_pad(MX35_PIN_ATA_IORDY, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_INTRQ, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_CS0, ata_ctl_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_CS1, ata_ctl_pad_cfg);
+
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA0, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA1, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA2, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA3, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA4, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA5, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA6, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA7, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA8, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA9, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA10, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA11, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA12, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA13, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA14, ata_dat_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DATA15, ata_dat_pad_cfg);
+
+ mxc_iomux_set_pad(MX35_PIN_ATA_DA0, ata_ctl_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DA1, ata_ctl_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_DA2, ata_ctl_pad_cfg);
+ mxc_iomux_set_pad(MX35_PIN_ATA_BUFF_EN, ata_ctl_pad_cfg);
+}
+
+EXPORT_SYMBOL(gpio_ata_active);
+
+/*!
+ * Restore ATA interface pins to reset values
+ *
+ */
+void gpio_ata_inactive(void)
+{
+ /*Turn off the IOMUX for ATA group B signals */
+ mxc_free_iomux(MX35_PIN_ATA_DATA0, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA1, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA2, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA3, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA4, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA5, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA6, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA7, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA8, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA9, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA10, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA11, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA12, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA13, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA14, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DATA15, MUX_CONFIG_FUNC);
+
+ /* Config the multiplex pin of ATA interface DIR, DA0-2, INTRQ, DMARQ */
+ mxc_free_iomux(MX35_PIN_ATA_DMARQ, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DIOR, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DIOW, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DMACK, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_RESET_B, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_IORDY, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_INTRQ, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_CS0, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_CS1, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DA0, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DA1, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_DA2, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_ATA_BUFF_EN, MUX_CONFIG_FUNC);
+
+ /* Power Off the HDD */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 4, 0);
+ /* HDD_ENBALE */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 3, 1);
+}
+
+EXPORT_SYMBOL(gpio_ata_inactive);
+
+/*!
+ * This function activates ESAI ports to enable
+ * surround sound I/O
+ */
+void gpio_activate_esai_ports(void)
+{
+ unsigned int pad_val;
+ /* ESAI TX - WM8580 */
+ mxc_request_iomux(MX35_PIN_HCKT, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_SCKT, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FST, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_TX0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_TX1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_TX2_RX3, MUX_CONFIG_FUNC);
+
+ /* ESAI RX - AK5702 */
+ /*mxc_request_iomux(MX35_PIN_HCKR, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_SCKR, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_FSR, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_TX3_RX2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_TX4_RX1, MUX_CONFIG_FUNC);*/
+
+ pad_val = PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_PUD;
+ /* ESAI TX - WM8580 */
+ mxc_iomux_set_pad(MX35_PIN_SCKT, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_FST, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_TX0, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_TX1, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_TX2_RX3, pad_val);
+
+ /* ESAI RX - AK5702 */
+ /*mxc_iomux_set_pad(MX35_PIN_SCKR, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_FSR, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_TX3_RX2, pad_val);
+ mxc_iomux_set_pad(MX35_PIN_TX4_RX1, pad_val);*/
+
+ pad_val =
+ PAD_CTL_DRV_HIGH | PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_PUD;
+
+ /* ESAI TX - WM8580 */
+ mxc_iomux_set_pad(MX35_PIN_HCKT, pad_val);
+ /* ESAI RX - AK5702 */
+ /*mxc_iomux_set_pad(MX35_PIN_HCKR, pad_val);*/
+}
+
+EXPORT_SYMBOL(gpio_activate_esai_ports);
+
+/*!
+ * This function deactivates ESAI ports to disable
+ * surround sound I/O
+ */
+void gpio_deactivate_esai_ports(void)
+{
+
+ mxc_free_iomux(MX35_PIN_HCKT, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_SCKT, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_FST, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_TX0, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_TX1, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_TX2_RX3, MUX_CONFIG_FUNC);
+ /*mxc_free_iomux(MX35_PIN_HCKR, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_SCKR, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FSR, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_TX3_RX2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_TX4_RX1, MUX_CONFIG_GPIO);*/
+}
+
+EXPORT_SYMBOL(gpio_deactivate_esai_ports);
+
+/*!
+ * This function enable and reset GPS GPIO
+ */
+void gpio_gps_active(void)
+{
+ /* Pull GPIO1_5 to be low for routing signal to UART3/GPS */
+ if (board_is_mx35(BOARD_REV_2)) {
+ mxc_request_iomux(MX35_PIN_COMPARE, MUX_CONFIG_GPIO);
+ mxc_iomux_set_pad(MX35_PIN_COMPARE, PAD_CTL_DRV_NORMAL |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU |
+ PAD_CTL_DRV_3_3V | PAD_CTL_PUE_PUD |
+ PAD_CTL_SRE_SLOW);
+ gpio_direction_output(IOMUX_TO_GPIO(MX35_PIN_COMPARE), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX35_PIN_COMPARE), 0);
+ }
+
+ /* PWR_EN_GPS is set to be 0, will be toggled on in app by ioctl */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 0, 0);
+
+ /* GPS 32KHz clock enbale */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 7, 1);
+
+ /* GPS reset */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 5, 0);
+ msleep(5);
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 5, 1);
+ msleep(5);
+}
+
+EXPORT_SYMBOL(gpio_gps_active);
+
+/*!
+ * This function get GPS GPIO status.
+ */
+int gpio_gps_access(int para)
+{
+ unsigned int gps_val;
+
+ if (para & 0x4) { /* Read GPIO */
+ if (para & 0x1) /* Read PWR_EN */
+ pmic_gpio_get_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 0,
+ &gps_val);
+ else /* Read nReset */
+ pmic_gpio_get_bit_val(MCU_GPIO_REG_RESET_1, 5,
+ &gps_val);
+ return gps_val;
+ } else { /* Write GPIO */
+ gps_val = (para & 0x2) ? 1 : 0;
+ if (para & 0x1)
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 0,
+ gps_val);
+ else
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_RESET_1, 5, gps_val);
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_gps_access);
+
+/*!
+ * This function disable GPS GPIO
+ */
+void gpio_gps_inactive(void)
+{
+ /* GPS disable */
+ pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 0, 0);
+ /* Free GPIO1_5 */
+ if (board_is_mx35(BOARD_REV_2))
+ mxc_free_iomux(MX35_PIN_COMPARE, MUX_CONFIG_GPIO);
+}
+
+EXPORT_SYMBOL(gpio_gps_inactive);
+
+/*!
+ * The MLB gpio configuration routine
+ */
+void gpio_mlb_active(void)
+{
+ mxc_request_iomux(MX35_PIN_MLB_CLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_MLB_SIG, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_MLB_DAT, MUX_CONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_mlb_active);
+
+void gpio_mlb_inactive(void)
+{
+ mxc_free_iomux(MX35_PIN_MLB_CLK, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_MLB_SIG, MUX_CONFIG_FUNC);
+ mxc_free_iomux(MX35_PIN_MLB_DAT, MUX_CONFIG_FUNC);
+}
+
+EXPORT_SYMBOL(gpio_mlb_inactive);
+
+void gpio_can_active(int id)
+{
+ int pad;
+
+ switch (id) {
+ case 0:
+ pad = PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | \
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU | PAD_CTL_DRV_HIGH;
+ mxc_request_iomux(MX35_PIN_I2C2_CLK, MUX_CONFIG_ALT1);
+ mxc_request_iomux(MX35_PIN_I2C2_DAT, MUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX35_PIN_I2C2_CLK, pad);
+ mxc_iomux_set_pad(MX35_PIN_I2C2_DAT, pad);
+ mxc_iomux_set_input(MUX_IN_CAN1_CANRX, INPUT_CTL_PATH0);
+ break;
+ case 1:
+ pad = PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD | PAD_CTL_100K_PU;
+ mxc_request_iomux(MX35_PIN_FEC_MDC, MUX_CONFIG_ALT1);
+ mxc_request_iomux(MX35_PIN_FEC_MDIO, MUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX35_PIN_FEC_MDC, pad);
+ mxc_iomux_set_pad(MX35_PIN_FEC_MDIO, pad);
+ mxc_iomux_set_input(MUX_IN_CAN2_CANRX, INPUT_CTL_PATH2);
+ break;
+ default:
+ printk(KERN_ERR "NO such device\n");
+ }
+}
+
+void gpio_can_inactive(int id)
+{
+ switch (id) {
+ case 0:
+ mxc_free_iomux(MX35_PIN_I2C2_CLK, MUX_CONFIG_ALT1);
+ mxc_free_iomux(MX35_PIN_I2C2_DAT, MUX_CONFIG_ALT1);
+ mxc_iomux_set_input(MUX_IN_CAN1_CANRX, INPUT_CTL_PATH0);
+ break;
+ case 1:
+ mxc_free_iomux(MX35_PIN_FEC_MDC, MUX_CONFIG_ALT1);
+ mxc_free_iomux(MX35_PIN_FEC_MDIO, MUX_CONFIG_ALT1);
+ mxc_iomux_set_input(MUX_IN_CAN2_CANRX, INPUT_CTL_PATH0);
+ break;
+ default:
+ printk(KERN_ERR "NO such device\n");
+ }
+}
+
+void gpio_pmic_active(void)
+{
+ unsigned int pad_val = PAD_CTL_SRE_SLOW | PAD_CTL_DRV_NORMAL
+ | PAD_CTL_HYS_CMOS | PAD_CTL_100K_PU | PAD_CTL_DRV_3_3V;
+ mxc_request_iomux(MX35_PIN_GPIO2_0, MUX_CONFIG_FUNC);
+ mxc_iomux_set_pad(MX35_PIN_GPIO2_0, pad_val);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_GPIO2_0), NULL);
+ gpio_direction_input(IOMUX_TO_GPIO(MX35_PIN_GPIO2_0));
+}
+
+EXPORT_SYMBOL(gpio_pmic_active);
+
+
diff --git a/arch/arm/mach-mx35/mx35_3stack_irq.c b/arch/arm/mach-mx35/mx35_3stack_irq.c
new file mode 100644
index 000000000000..ad772040c19e
--- /dev/null
+++ b/arch/arm/mach-mx35/mx35_3stack_irq.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/bitops.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+
+#include <mach/gpio.h>
+
+#include "board-mx35_3stack.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx35/mx35_3stack_irq.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX35
+ */
+#ifdef CONFIG_MXC_PSEUDO_IRQS
+
+/*
+ * The interrupt status and mask variables.
+ */
+static unsigned long pseudo_irq_pending;
+static unsigned long pseudo_irq_enable;
+static unsigned long pseudo_irq_wakeup;
+static unsigned long pseudo_suspend;
+static atomic_t pseudo_irq_state = ATOMIC_INIT(0);
+
+/*
+ * The declaration of handler of two work queue.
+ * The one is the work queue to indentify the events from MCU.
+ * The another is the work queue to change the events mask.
+ */
+static void mcu_event_handler(struct work_struct *work);
+static void mcu_state_handler(struct work_struct *work);
+static void mcu_event_delay(unsigned long data);
+
+/*!
+ * The work structure for mcu events.
+ */
+static DECLARE_WORK(mcu_event_ws, mcu_event_handler);
+static DECLARE_WORK(mcu_state_ws, mcu_state_handler);
+static DEFINE_TIMER(mcu_delay_timer, mcu_event_delay, HZ, 0);
+
+static inline void mxc_pseudo_irq_ack(void)
+{
+ disable_irq(MXC_PSEUDO_PARENT);
+ atomic_set(&pseudo_irq_state, 0);
+}
+
+static inline void mxc_pseudo_irq_trigger(void)
+{
+ if (!atomic_xchg(&pseudo_irq_state, 1))
+ enable_irq(MXC_PSEUDO_PARENT);
+}
+
+/*
+ * mask a pseudo interrupt by setting the bit in the mask variable.
+ * @param irq a pseudo virtual irq number
+ */
+static void pseudo_mask_irq(u32 irq)
+{
+ int index = irq - MXC_PSEUDO_IO_BASE;
+ clear_bit(index, &pseudo_irq_enable);
+}
+
+/*
+ * disable a pseudo interrupt by triggerring a work queue
+ * @param irq a pseudo virtual irq number
+ */
+static void pseudo_disable_irq(u32 irq)
+{
+ struct irq_desc *desc = irq_desc + irq;
+ desc->chip->mask(irq);
+ desc->status |= IRQ_MASKED;
+ schedule_work(&mcu_state_ws);
+}
+
+/*
+ * Acknowledge a pseudo interrupt by clearing the bit in the isr variable.
+ * @param irq a pseudo virtual irq number
+ */
+static void pseudo_ack_irq(u32 irq)
+{
+ int index = irq - MXC_PSEUDO_IO_BASE;
+ /* clear the interrupt status */
+ clear_bit(index, &pseudo_irq_pending);
+}
+
+/*
+ * unmask a pseudo interrupt by clearing the bit in the imr.
+ * @param irq a pseudo virtual irq number
+ */
+static void pseudo_unmask_irq(u32 irq)
+{
+ int index = irq - MXC_PSEUDO_IO_BASE;
+
+ set_bit(index, &pseudo_irq_enable);
+
+ if (test_bit(index, &pseudo_irq_pending))
+ mxc_pseudo_irq_trigger();
+}
+
+/*
+ * Enable a pseudo interrupt by triggerring a work queue
+ * @param irq a pseudo virtual irq number
+ */
+static void pseudo_enable_irq(u32 irq)
+{
+ struct irq_desc *desc = irq_desc + irq;
+ desc->chip->unmask(irq);
+ desc->status &= ~IRQ_MASKED;
+ schedule_work(&mcu_state_ws);
+}
+
+/*
+ * set pseudo irq as a wake-up source.
+ * @param irq a pseudo virtual irq number
+ * @param enable enable as wake-up if equal to non-ero
+ * @return This function return 0 on success
+ */
+static int pseudo_set_wake_irq(u32 irq, u32 enable)
+{
+ int index = irq - MXC_PSEUDO_IO_BASE;
+
+ if (index >= MXC_MAX_PSEUDO_IO_LINES)
+ return -ENODEV;
+
+ if (enable) {
+ if (!pseudo_irq_wakeup)
+ enable_irq_wake(IOMUX_TO_IRQ(MX35_PIN_GPIO1_0));
+ pseudo_irq_wakeup |= (1 << index);
+ } else {
+ pseudo_irq_wakeup &= ~(1 << index);
+ if (!pseudo_irq_wakeup)
+ disable_irq_wake(IOMUX_TO_IRQ(MX35_PIN_GPIO1_0));
+ }
+ return 0;
+}
+
+static struct irq_chip pseudo_irq_chip = {
+ .ack = pseudo_ack_irq,
+ .mask = pseudo_mask_irq,
+ .disable = pseudo_disable_irq,
+ .unmask = pseudo_unmask_irq,
+ .enable = pseudo_enable_irq,
+ .set_wake = pseudo_set_wake_irq,
+};
+
+static void mxc_pseudo_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 pseudo_irq;
+ u32 index, mask;
+
+ desc->chip->mask(irq);
+ mxc_pseudo_irq_ack();
+
+ mask = pseudo_irq_enable;
+ index = pseudo_irq_pending;
+
+ if (unlikely(!(index & mask))) {
+ printk(KERN_ERR "\nPseudo IRQ: Spurious interrupt:0x%0x\n\n",
+ index);
+ pr_info("IEN=0x%x, PENDING=0x%x\n", mask, index);
+ return;
+ }
+
+ index = index & mask;
+ pseudo_irq = MXC_PSEUDO_IO_BASE;
+ for (; index != 0; index >>= 1, pseudo_irq++) {
+ struct irq_desc *d;
+ if ((index & 1) == 0)
+ continue;
+ d = irq_desc + pseudo_irq;
+ if (unlikely(!(d->handle_irq))) {
+ printk(KERN_ERR "\nPseudo irq: %d unhandeled\n",
+ pseudo_irq);
+ BUG(); /* oops */
+ }
+ d->handle_irq(pseudo_irq, d);
+ d->chip->ack(pseudo_irq);
+ }
+}
+
+static void mcu_event_delay(unsigned long data)
+{
+ schedule_work(&mcu_event_ws);
+}
+
+/*!
+ * This function is called when mcu interrupt occurs on the processor.
+ * It is the interrupt handler for the mcu.
+ *
+ * @param irq the irq number
+ * @param dev_id the pointer on the device
+ *
+ * @return The function returns IRQ_HANDLED when handled.
+ */
+static irqreturn_t mcu_irq_handler(int irq, void *dev_id)
+{
+ disable_irq(IOMUX_TO_IRQ(MX35_PIN_GPIO1_0));
+ if (pseudo_suspend)
+ mod_timer(&mcu_delay_timer, jiffies + HZ);
+ else
+ schedule_work(&mcu_event_ws);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function is the work handler of mcu interrupt.
+ * It reads the events status and trigger the pseudo irq.
+ */
+static void mcu_event_handler(struct work_struct *work)
+{
+ int i, err;
+ unsigned int flag1, flag2;
+
+ /* read int flags and ack int */
+ for (i = 0; i < 3; i++) {
+ err = mcu_pmic_read_reg(REG_MCU_INT_FLAG_1, &flag1, 0xFFFFFFFF);
+ err |= mcu_pmic_read_reg(REG_MCU_INT_FLAG_2,
+ &flag2, 0xFFFFFFFF);
+ err |= mcu_pmic_write_reg(REG_MCU_INT_FLAG_1, 0, 0xFFFFFFFF);
+ err |= mcu_pmic_write_reg(REG_MCU_INT_FLAG_2, 0, 0xFFFFFFFF);
+ if (err == 0)
+ break;
+ }
+
+ if (i >= 3) {
+ printk(KERN_ERR "Reads MCU event fail\n");
+ goto no_new_events;
+ }
+
+ for (i = 0; flag1 && (i < MCU_INT_RTC); i++, flag1 >>= 1)
+ if (flag1 & 1)
+ set_bit(i, &pseudo_irq_pending);
+
+ for (i = MCU_INT_RTC; flag2 && (i <= MCU_INT_KEYPAD); i++, flag2 >>= 1)
+ if (flag2 & 1)
+ set_bit(i, &pseudo_irq_pending);
+ no_new_events:
+ if (pseudo_irq_pending & pseudo_irq_enable)
+ mxc_pseudo_irq_trigger();
+ enable_irq(IOMUX_TO_IRQ(MX35_PIN_GPIO1_0));
+}
+
+static void mcu_state_handler(struct work_struct *work)
+{
+ int err, i;
+ unsigned int event1, event2;
+ event1 = pseudo_irq_enable & ((1 << MCU_INT_RTC) - 1);
+ event2 = pseudo_irq_enable >> MCU_INT_RTC;
+
+ for (i = 0; i < 3; i++) {
+ err = mcu_pmic_write_reg(REG_MCU_INT_ENABLE_1, event1, 0xFF);
+ err |= mcu_pmic_write_reg(REG_MCU_INT_ENABLE_2, event2, 0xFF);
+ if (err == 0)
+ break;
+ }
+ if (i >= 3)
+ printk(KERN_ERR "Change MCU event mask fail\n");
+}
+
+static int __init mxc_pseudo_init(void)
+{
+ int i;
+
+ /* disable the interrupt and clear the status */
+ pseudo_irq_pending = 0;
+ pseudo_irq_enable = 0;
+
+ pr_info("3-Stack Pseudo interrupt rev=0.1v\n");
+
+ for (i = MXC_PSEUDO_IO_BASE;
+ i < (MXC_PSEUDO_IO_BASE + MXC_MAX_PSEUDO_IO_LINES); i++) {
+ set_irq_chip(i, &pseudo_irq_chip);
+ set_irq_handler(i, handle_simple_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+
+ set_irq_flags(MXC_PSEUDO_PARENT, IRQF_NOAUTOEN);
+ set_irq_handler(MXC_PSEUDO_PARENT, mxc_pseudo_irq_handler);
+
+ /* Set and install PMIC IRQ handler */
+ mxc_request_iomux(MX35_PIN_GPIO1_0, MUX_CONFIG_FUNC);
+ mxc_iomux_set_pad(MX35_PIN_GPIO1_0, PAD_CTL_PKE_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_GPIO1_0), NULL);
+ gpio_direction_input(IOMUX_TO_GPIO(MX35_PIN_GPIO1_0));
+
+ set_irq_type(IOMUX_TO_IRQ(MX35_PIN_GPIO1_0), IRQF_TRIGGER_RISING);
+ if (request_irq(IOMUX_TO_IRQ(MX35_PIN_GPIO1_0), mcu_irq_handler,
+ 0, "MCU_IRQ", 0)) {
+ printk(KERN_ERR "mcu request irq failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+fs_initcall_sync(mxc_pseudo_init);
+
+static int mxc_pseudo_irq_suspend(struct platform_device *dev,
+ pm_message_t mesg)
+{
+ int err, i;
+ unsigned int event1, event2;
+
+ if (!pseudo_irq_wakeup)
+ return 0;
+
+ event1 = pseudo_irq_wakeup & ((1 << MCU_INT_RTC) - 1);
+ event2 = pseudo_irq_wakeup >> MCU_INT_RTC;
+
+ for (i = 0; i < 3; i++) {
+ err = mcu_pmic_write_reg(REG_MCU_INT_ENABLE_1, event1, 0xFF);
+ err |= mcu_pmic_write_reg(REG_MCU_INT_ENABLE_2, event2, 0xFF);
+ if (err == 0)
+ break;
+ }
+ pseudo_suspend = 1;
+ return err;
+}
+
+static int mxc_pseudo_irq_resume(struct platform_device *dev)
+{
+ if (!pseudo_irq_wakeup)
+ return 0;
+
+ schedule_work(&mcu_state_ws);
+ pseudo_suspend = 0;
+ return 0;
+}
+
+static struct platform_driver mxc_pseudo_irq_driver = {
+ .driver = {
+ .name = "mxc_pseudo_irq",
+ },
+ .suspend = mxc_pseudo_irq_suspend,
+ .resume = mxc_pseudo_irq_resume,
+};
+
+static int __init mxc_pseudo_sysinit(void)
+{
+ return platform_driver_register(&mxc_pseudo_irq_driver);
+}
+
+late_initcall(mxc_pseudo_sysinit);
+#endif /* CONFIG_MXC_PSEUDO_IRQS */
diff --git a/arch/arm/mach-mx35/mx35_3stack_pmic_mc13892.c b/arch/arm/mach-mx35/mx35_3stack_pmic_mc13892.c
new file mode 100644
index 000000000000..deb5e4a809de
--- /dev/null
+++ b/arch/arm/mach-mx35/mx35_3stack_pmic_mc13892.c
@@ -0,0 +1,345 @@
+/*
+ * mx35-3stack-pmic-mc13892.c -- i.MX35 3STACK Driver for Atlas MC13892 PMIC
+ */
+ /*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+ /*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/pmic_external.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/mc13892/core.h>
+#include "iomux.h"
+
+/*
+ * Convenience conversion.
+ * Here atm, maybe there is somewhere better for this.
+ */
+#define mV_to_uV(mV) (mV * 1000)
+#define uV_to_mV(uV) (uV / 1000)
+#define V_to_uV(V) (mV_to_uV(V * 1000))
+#define uV_to_V(uV) (uV_to_mV(uV) / 1000)
+
+#define STANDBYSECINV_LSH 11
+#define STANDBYSECINV_WID 1
+
+/* CPU */
+static struct regulator_consumer_supply sw1_consumers[] = {
+ {
+ .supply = "cpu_vcc",
+ }
+};
+
+struct mc13892;
+
+static struct regulator_init_data sw1_init = {
+ .constraints = {
+ .name = "SW1",
+ .min_uV = mV_to_uV(600),
+ .max_uV = mV_to_uV(1375),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .valid_modes_mask = 0,
+ .always_on = 1,
+ .boot_on = 1,
+ .initial_state = PM_SUSPEND_MEM,
+ .state_mem = {
+ .uV = 700000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ },
+ .num_consumer_supplies = ARRAY_SIZE(sw1_consumers),
+ .consumer_supplies = sw1_consumers,
+};
+
+static struct regulator_init_data sw2_init = {
+ .constraints = {
+ .name = "SW2",
+ .min_uV = mV_to_uV(900),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ .initial_state = PM_SUSPEND_MEM,
+ .state_mem = {
+ .uV = 1200000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ .state_standby = {
+ .uV = 1000000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ }
+};
+
+static struct regulator_init_data sw3_init = {
+ .constraints = {
+ .name = "SW3",
+ .min_uV = mV_to_uV(1100),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data sw4_init = {
+ .constraints = {
+ .name = "SW4",
+ .min_uV = mV_to_uV(1100),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data viohi_init = {
+ .constraints = {
+ .name = "VIOHI",
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vusb_init = {
+ .constraints = {
+ .name = "VUSB",
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data swbst_init = {
+ .constraints = {
+ .name = "SWBST",
+ }
+};
+
+static struct regulator_init_data vdig_init = {
+ .constraints = {
+ .name = "VDIG",
+ .min_uV = mV_to_uV(1050),
+ .max_uV = mV_to_uV(1800),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vpll_init = {
+ .constraints = {
+ .name = "VPLL",
+ .min_uV = mV_to_uV(1050),
+ .max_uV = mV_to_uV(1800),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vusb2_init = {
+ .constraints = {
+ .name = "VUSB2",
+ .min_uV = mV_to_uV(2400),
+ .max_uV = mV_to_uV(2775),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vvideo_init = {
+ .constraints = {
+ .name = "VVIDEO",
+ .min_uV = mV_to_uV(2500),
+ .max_uV = mV_to_uV(2775),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vaudio_init = {
+ .constraints = {
+ .name = "VAUDIO",
+ .min_uV = mV_to_uV(2300),
+ .max_uV = mV_to_uV(3000),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vsd_init = {
+ .constraints = {
+ .name = "VSD",
+ .min_uV = mV_to_uV(1800),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vcam_init = {
+ .constraints = {
+ .name = "VCAM",
+ .min_uV = mV_to_uV(2500),
+ .max_uV = mV_to_uV(3000),
+ .valid_ops_mask =
+ REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL,
+ }
+};
+
+static struct regulator_init_data vgen1_init = {
+ .constraints = {
+ .name = "VGEN1",
+ .min_uV = mV_to_uV(1200),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vgen2_init = {
+ .constraints = {
+ .name = "VGEN2",
+ .min_uV = mV_to_uV(1200),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vgen3_init = {
+ .constraints = {
+ .name = "VGEN3",
+ .min_uV = mV_to_uV(1800),
+ .max_uV = mV_to_uV(2900),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data gpo1_init = {
+ .constraints = {
+ .name = "GPO1",
+ }
+};
+
+static struct regulator_init_data gpo2_init = {
+ .constraints = {
+ .name = "GPO2",
+ }
+};
+
+static struct regulator_init_data gpo3_init = {
+ .constraints = {
+ .name = "GPO3",
+ }
+};
+
+static struct regulator_init_data gpo4_init = {
+ .constraints = {
+ .name = "GPO4",
+ }
+};
+
+static struct regulator_init_data pwg1_init = {
+ .constraints = {
+ .name = "PWG1",
+ }
+};
+
+static struct regulator_init_data pwg2_init = {
+ .constraints = {
+ .name = "PWG2",
+ }
+};
+
+/*!
+ * the event handler for power on event
+ */
+static void power_on_evt_handler(void)
+{
+ pr_info("pwr on event1 is received \n");
+}
+
+/*!
+ * pmic board initialization code
+ */
+static int init_mc13892(void)
+{
+ if (board_is_mx35(BOARD_REV_2)) {
+ unsigned int value;
+ pmic_event_callback_t power_key_event;
+
+ /* subscribe PWRON1 event. */
+ power_key_event.param = NULL;
+ power_key_event.func = (void *)power_on_evt_handler;
+ pmic_event_subscribe(EVENT_PWRONI, power_key_event);
+
+ pmic_read_reg(REG_POWER_CTL2, &value, 0xffffff);
+ /* Bit 11 (STANDBYSECINV): Active Low */
+ value |= 0x00800;
+ /* Bit 12 (WDIRESET): enable */
+ value |= 0x01000;
+ pmic_write_reg(REG_POWER_CTL2, value, 0xffffff);
+
+ /* Battery charger default settings */
+ /* current limit = 1200mA, PLIM = 1000mw, disable auto charge */
+ value = 0x210068;
+ pmic_write_reg(REG_CHARGE, value, 0x018078);
+ }
+ return 0;
+}
+
+static int mc13892_regulator_init(struct mc13892 *mc13892)
+{
+ mc13892_register_regulator(mc13892, MC13892_SW1, &sw1_init);
+ mc13892_register_regulator(mc13892, MC13892_SW2, &sw2_init);
+ mc13892_register_regulator(mc13892, MC13892_SW3, &sw3_init);
+ mc13892_register_regulator(mc13892, MC13892_SW4, &sw4_init);
+ mc13892_register_regulator(mc13892, MC13892_SWBST, &swbst_init);
+ mc13892_register_regulator(mc13892, MC13892_VIOHI, &viohi_init);
+ mc13892_register_regulator(mc13892, MC13892_VPLL, &vpll_init);
+ mc13892_register_regulator(mc13892, MC13892_VDIG, &vdig_init);
+ mc13892_register_regulator(mc13892, MC13892_VSD, &vsd_init);
+ mc13892_register_regulator(mc13892, MC13892_VUSB2, &vusb2_init);
+ mc13892_register_regulator(mc13892, MC13892_VVIDEO, &vvideo_init);
+ mc13892_register_regulator(mc13892, MC13892_VAUDIO, &vaudio_init);
+ mc13892_register_regulator(mc13892, MC13892_VCAM, &vcam_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN1, &vgen1_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN2, &vgen2_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN3, &vgen3_init);
+ mc13892_register_regulator(mc13892, MC13892_VUSB, &vusb_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO1, &gpo1_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO2, &gpo2_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO3, &gpo3_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO4, &gpo4_init);
+ mc13892_register_regulator(mc13892, MC13892_PWGT1, &pwg1_init);
+ mc13892_register_regulator(mc13892, MC13892_PWGT2, &pwg2_init);
+
+ init_mc13892();
+ return 0;
+}
+
+static struct mc13892_platform_data mc13892_plat = {
+ .init = mc13892_regulator_init,
+};
+
+static struct i2c_board_info __initdata mc13892_i2c_device = {
+ I2C_BOARD_INFO("mc13892", 0x08),
+ .irq = MX35_PIN_GPIO2_0,
+ .platform_data = &mc13892_plat,
+};
+
+int __init mx35_3stack_init_mc13892(void)
+{
+ return i2c_register_board_info(0, &mc13892_i2c_device, 1);
+}
+
diff --git a/arch/arm/mach-mx35/mx35_3stack_pmic_mc9s08dz60.c b/arch/arm/mach-mx35/mx35_3stack_pmic_mc9s08dz60.c
new file mode 100644
index 000000000000..a10346340b03
--- /dev/null
+++ b/arch/arm/mach-mx35/mx35_3stack_pmic_mc9s08dz60.c
@@ -0,0 +1,105 @@
+/*
+ * mx35-3stack-pmic-mc9s08dz60.c -- i.MX35 3STACK Driver for MCU PMIC
+ */
+ /*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+ /*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/pmic_external.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/mc9s08dz60/core.h>
+#include "iomux.h"
+#include "board-mx35_3stack.h"
+
+static struct regulator_init_data lcd_init = {
+ .constraints = {
+ .name = "LCD",
+ }
+};
+
+static struct regulator_init_data wifi_init = {
+ .constraints = {
+ .name = "WIFI",
+ }
+};
+
+static struct regulator_init_data hdd_init = {
+ .constraints = {
+ .name = "HDD",
+ }
+};
+
+static struct regulator_init_data gps_init = {
+ .constraints = {
+ .name = "GPS",
+ }
+};
+
+static struct regulator_init_data spkr_init = {
+ .constraints = {
+ .name = "SPKR",
+ }
+};
+
+static int mc9s08dz60_regulator_init(struct mc9s08dz60 *mc9s08dz60)
+{
+ pr_info("mc9s08dz60 regulator init\n");
+ if (board_is_mx35(BOARD_REV_2)) {
+ mc9s08dz60_register_regulator(
+ mc9s08dz60, MC9S08DZ60_LCD, &lcd_init);
+ mc9s08dz60_register_regulator(mc9s08dz60,
+ MC9S08DZ60_WIFI, &wifi_init);
+ mc9s08dz60_register_regulator(
+ mc9s08dz60, MC9S08DZ60_HDD, &hdd_init);
+ mc9s08dz60_register_regulator(
+ mc9s08dz60, MC9S08DZ60_GPS, &gps_init);
+ mc9s08dz60_register_regulator(mc9s08dz60,
+ MC9S08DZ60_SPKR, &spkr_init);
+ }
+ return 0;
+}
+
+static struct mc9s08dz60_platform_data mc9s08dz60_plat = {
+ .init = mc9s08dz60_regulator_init,
+};
+
+static struct i2c_board_info __initdata mc9s08dz60_i2c_device = {
+ I2C_BOARD_INFO("mc9s08dz60", 0x69),
+ .platform_data = &mc9s08dz60_plat,
+};
+
+static struct resource mc9s08dz60_keypad_resource = {
+ .start = MXC_PSEUDO_IRQ_KEYPAD,
+ .end = MXC_PSEUDO_IRQ_KEYPAD,
+ .flags = IORESOURCE_IRQ,
+};
+
+static struct platform_device mc9s08dz60_keypad_dev = {
+ .name = "mc9s08dz60keypad",
+ .num_resources = 1,
+ .resource = &mc9s08dz60_keypad_resource,
+};
+
+int __init mx35_3stack_init_mc9s08dz60(void)
+{
+ int retval = 0;
+ retval = i2c_register_board_info(0, &mc9s08dz60_i2c_device, 1);
+ if (retval == 0)
+ platform_device_register(&mc9s08dz60_keypad_dev);
+ return retval;
+}
+
diff --git a/arch/arm/mach-mx35/mx35_pins.h b/arch/arm/mach-mx35/mx35_pins.h
new file mode 100644
index 000000000000..082384231370
--- /dev/null
+++ b/arch/arm/mach-mx35/mx35_pins.h
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MX35_PINS_H__
+#define __ASM_ARCH_MXC_MX35_PINS_H__
+
+/*!
+ * @file arch-mxc/mx35_pins.h
+ *
+ * @brief MX35 I/O Pin List
+ *
+ * @ingroup GPIO_MX35
+ */
+
+#ifndef __ASSEMBLY__
+
+/*!
+ * @name IOMUX/PAD Bit field definitions
+ */
+
+/*! @{ */
+
+/*!
+ * In order to identify pins more effectively, each mux-controlled pin's
+ * enumerated value is constructed in the following way:
+ *
+ * -------------------------------------------------------------------
+ * 31-29 | 28 - 24 |23 - 21| 20 - 10| 9 - 0
+ * -------------------------------------------------------------------
+ * IO_P | IO_I | RSVD | PAD_I | MUX_I
+ * -------------------------------------------------------------------
+ *
+ * Bit 0 to 7 contains MUX_I used to identify the register
+ * offset (base is IOMUX_module_base ) defined in the Section
+ * "sw_pad_ctl & sw_mux_ctl details" of the IC Spec. The similar field
+ * definitions are used for the pad control register.the MX35_PIN_A0 is
+ * defined in the enumeration: ( 0x28 << MUX_I) |( 0x368 << PAD_I)
+ * So the absolute address is: IOMUX_module_base + 0x28.
+ * The pad control register offset is: 0x368.
+ */
+
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * MUX control register offset
+ */
+#define MUX_I 0
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * PAD control register offset
+ */
+#define PAD_I 10
+
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * reserved filed
+ */
+#define RSVD_I 21
+
+#define NON_GPIO_I 0x7
+#define PIN_TO_MUX_MASK ((1<<(PAD_I - MUX_I)) - 1)
+#define PIN_TO_PAD_MASK ((1<<(RSVD_I - PAD_I)) - 1)
+#define NON_MUX_I PIN_TO_MUX_MASK
+
+#define _MXC_BUILD_PIN(gp, gi, mi, pi) \
+ (((gp) << MUX_IO_P) | ((gi) << MUX_IO_I) | \
+ ((mi) << MUX_I) | ((pi) << PAD_I))
+
+#define _MXC_BUILD_GPIO_PIN(gp, gi, mi, pi) \
+ _MXC_BUILD_PIN(gp, gi, mi, pi)
+
+#define _MXC_BUILD_NON_GPIO_PIN(mi, pi) \
+ _MXC_BUILD_PIN(NON_GPIO_I, 0, mi, pi)
+
+#define PIN_TO_IOMUX_MUX(pin) ((pin >> MUX_I) & PIN_TO_MUX_MASK)
+#define PIN_TO_IOMUX_PAD(pin) ((pin >> PAD_I) & PIN_TO_PAD_MASK)
+
+/*! @} End IOMUX/PAD Bit field definitions */
+
+/*!
+ * This enumeration is constructed based on the Section
+ * "sw_pad_ctl & sw_mux_ctl details" of the MX35 IC Spec. Each enumerated
+ * value is constructed based on the rules described above.
+ */
+enum iomux_pins {
+ MX35_PIN_CAPTURE = _MXC_BUILD_GPIO_PIN(0, 4, 0x4, 0x328),
+ MX35_PIN_COMPARE = _MXC_BUILD_GPIO_PIN(0, 5, 0x8, 0x32C),
+ MX35_PIN_WATCHDOG_RST = _MXC_BUILD_GPIO_PIN(0, 6, 0xC, 0x330),
+ MX35_PIN_GPIO1_0 = _MXC_BUILD_GPIO_PIN(0, 0, 0x10, 0x334),
+ MX35_PIN_GPIO1_1 = _MXC_BUILD_GPIO_PIN(0, 1, 0x14, 0x338),
+ MX35_PIN_GPIO2_0 = _MXC_BUILD_GPIO_PIN(1, 0, 0x18, 0x33C),
+ MX35_PIN_GPIO3_0 = _MXC_BUILD_GPIO_PIN(2, 0, 0x1C, 0x340),
+ MX35_PIN_CLKO = _MXC_BUILD_GPIO_PIN(0, 8, 0x20, 0x34C),
+
+ MX35_PIN_POWER_FAIL = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x360),
+ MX35_PIN_VSTBY = _MXC_BUILD_GPIO_PIN(0, 7, 0x24, 0x364),
+ MX35_PIN_A0 = _MXC_BUILD_NON_GPIO_PIN(0x28, 0x368),
+ MX35_PIN_A1 = _MXC_BUILD_NON_GPIO_PIN(0x2C, 0x36C),
+ MX35_PIN_A2 = _MXC_BUILD_NON_GPIO_PIN(0x30, 0x370),
+ MX35_PIN_A3 = _MXC_BUILD_NON_GPIO_PIN(0x34, 0x374),
+ MX35_PIN_A4 = _MXC_BUILD_NON_GPIO_PIN(0x38, 0x378),
+ MX35_PIN_A5 = _MXC_BUILD_NON_GPIO_PIN(0x3C, 0x37C),
+ MX35_PIN_A6 = _MXC_BUILD_NON_GPIO_PIN(0x40, 0x380),
+ MX35_PIN_A7 = _MXC_BUILD_NON_GPIO_PIN(0x44, 0x384),
+ MX35_PIN_A8 = _MXC_BUILD_NON_GPIO_PIN(0x48, 0x388),
+ MX35_PIN_A9 = _MXC_BUILD_NON_GPIO_PIN(0x4C, 0x38C),
+ MX35_PIN_A10 = _MXC_BUILD_NON_GPIO_PIN(0x50, 0x390),
+ MX35_PIN_MA10 = _MXC_BUILD_NON_GPIO_PIN(0x54, 0x394),
+ MX35_PIN_A11 = _MXC_BUILD_NON_GPIO_PIN(0x58, 0x398),
+ MX35_PIN_A12 = _MXC_BUILD_NON_GPIO_PIN(0x5C, 0x39C),
+ MX35_PIN_A13 = _MXC_BUILD_NON_GPIO_PIN(0x60, 0x3A0),
+ MX35_PIN_A14 = _MXC_BUILD_NON_GPIO_PIN(0x64, 0x3A4),
+ MX35_PIN_A15 = _MXC_BUILD_NON_GPIO_PIN(0x68, 0x3A8),
+ MX35_PIN_A16 = _MXC_BUILD_NON_GPIO_PIN(0x6C, 0x3AC),
+ MX35_PIN_A17 = _MXC_BUILD_NON_GPIO_PIN(0x70, 0x3B0),
+ MX35_PIN_A18 = _MXC_BUILD_NON_GPIO_PIN(0x74, 0x3B4),
+ MX35_PIN_A19 = _MXC_BUILD_NON_GPIO_PIN(0x78, 0x3B8),
+ MX35_PIN_A20 = _MXC_BUILD_NON_GPIO_PIN(0x7C, 0x3BC),
+ MX35_PIN_A21 = _MXC_BUILD_NON_GPIO_PIN(0x80, 0x3C0),
+ MX35_PIN_A22 = _MXC_BUILD_NON_GPIO_PIN(0x84, 0x3C4),
+ MX35_PIN_A23 = _MXC_BUILD_NON_GPIO_PIN(0x88, 0x3C8),
+ MX35_PIN_A24 = _MXC_BUILD_NON_GPIO_PIN(0x8C, 0x3CC),
+ MX35_PIN_A25 = _MXC_BUILD_NON_GPIO_PIN(0x90, 0x3D0),
+
+ MX35_PIN_EB0 = _MXC_BUILD_NON_GPIO_PIN(0x94, 0x46C),
+ MX35_PIN_EB1 = _MXC_BUILD_NON_GPIO_PIN(0x98, 0x470),
+ MX35_PIN_OE = _MXC_BUILD_NON_GPIO_PIN(0x9C, 0x474),
+ MX35_PIN_CS0 = _MXC_BUILD_NON_GPIO_PIN(0xA0, 0x478),
+ MX35_PIN_CS1 = _MXC_BUILD_NON_GPIO_PIN(0xA4, 0x47C),
+ MX35_PIN_CS2 = _MXC_BUILD_NON_GPIO_PIN(0xA8, 0x480),
+ MX35_PIN_CS3 = _MXC_BUILD_NON_GPIO_PIN(0xAC, 0x484),
+ MX35_PIN_CS4 = _MXC_BUILD_GPIO_PIN(0, 20, 0xB0, 0x488),
+ MX35_PIN_CS5 = _MXC_BUILD_GPIO_PIN(0, 21, 0xB4, 0x48C),
+ MX35_PIN_NFCE_B = _MXC_BUILD_GPIO_PIN(0, 22, 0xB8, 0x490),
+
+ MX35_PIN_LBA = _MXC_BUILD_NON_GPIO_PIN(0xBC, 0x498),
+ MX35_PIN_BCLK = _MXC_BUILD_NON_GPIO_PIN(0xC0, 0x49C),
+ MX35_PIN_RW = _MXC_BUILD_NON_GPIO_PIN(0xC4, 0x4A0),
+
+ MX35_PIN_NFWE_B = _MXC_BUILD_GPIO_PIN(1, 18, 0xC8, 0x4CC),
+ MX35_PIN_NFRE_B = _MXC_BUILD_GPIO_PIN(1, 19, 0xCC, 0x4D0),
+ MX35_PIN_NFALE = _MXC_BUILD_GPIO_PIN(1, 20, 0xD0, 0x4D4),
+ MX35_PIN_NFCLE = _MXC_BUILD_GPIO_PIN(1, 21, 0xD4, 0x4D8),
+ MX35_PIN_NFWP_B = _MXC_BUILD_GPIO_PIN(1, 22, 0xD8, 0x4DC),
+ MX35_PIN_NFRB = _MXC_BUILD_GPIO_PIN(1, 23, 0xDC, 0x4E0),
+
+ MX35_PIN_D15 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x4E4),
+ MX35_PIN_D14 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x4E8),
+ MX35_PIN_D13 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x4EC),
+ MX35_PIN_D12 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x4F0),
+ MX35_PIN_D11 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x4F4),
+ MX35_PIN_D10 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x4F8),
+ MX35_PIN_D9 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x4FC),
+ MX35_PIN_D8 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x500),
+ MX35_PIN_D7 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x504),
+ MX35_PIN_D6 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x508),
+ MX35_PIN_D5 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x50C),
+ MX35_PIN_D4 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x510),
+ MX35_PIN_D3 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x514),
+ MX35_PIN_D2 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x518),
+ MX35_PIN_D1 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x51C),
+ MX35_PIN_D0 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x520),
+
+ MX35_PIN_CSI_D8 = _MXC_BUILD_GPIO_PIN(0, 20, 0xE0, 0x524),
+ MX35_PIN_CSI_D9 = _MXC_BUILD_GPIO_PIN(0, 21, 0xE4, 0x528),
+ MX35_PIN_CSI_D10 = _MXC_BUILD_GPIO_PIN(0, 22, 0xE8, 0x52C),
+ MX35_PIN_CSI_D11 = _MXC_BUILD_GPIO_PIN(0, 23, 0xEC, 0x530),
+ MX35_PIN_CSI_D12 = _MXC_BUILD_GPIO_PIN(0, 24, 0xF0, 0x534),
+ MX35_PIN_CSI_D13 = _MXC_BUILD_GPIO_PIN(0, 25, 0xF4, 0x538),
+ MX35_PIN_CSI_D14 = _MXC_BUILD_GPIO_PIN(0, 26, 0xF8, 0x53C),
+ MX35_PIN_CSI_D15 = _MXC_BUILD_GPIO_PIN(0, 27, 0xFC, 0x540),
+ MX35_PIN_CSI_MCLK = _MXC_BUILD_GPIO_PIN(0, 28, 0x100, 0x544),
+ MX35_PIN_CSI_VSYNC = _MXC_BUILD_GPIO_PIN(0, 29, 0x104, 0x548),
+ MX35_PIN_CSI_HSYNC = _MXC_BUILD_GPIO_PIN(0, 30, 0x108, 0x54C),
+ MX35_PIN_CSI_PIXCLK = _MXC_BUILD_GPIO_PIN(0, 31, 0x10C, 0x550),
+
+ MX35_PIN_I2C1_CLK = _MXC_BUILD_GPIO_PIN(1, 24, 0x110, 0x554),
+ MX35_PIN_I2C1_DAT = _MXC_BUILD_GPIO_PIN(1, 25, 0x114, 0x558),
+ MX35_PIN_I2C2_CLK = _MXC_BUILD_GPIO_PIN(1, 26, 0x118, 0x55C),
+ MX35_PIN_I2C2_DAT = _MXC_BUILD_GPIO_PIN(1, 27, 0x11C, 0x560),
+
+ MX35_PIN_STXD4 = _MXC_BUILD_GPIO_PIN(1, 28, 0x120, 0x564),
+ MX35_PIN_SRXD4 = _MXC_BUILD_GPIO_PIN(1, 29, 0x124, 0x568),
+ MX35_PIN_SCK4 = _MXC_BUILD_GPIO_PIN(1, 30, 0x128, 0x56C),
+ MX35_PIN_STXFS4 = _MXC_BUILD_GPIO_PIN(1, 31, 0x12C, 0x570),
+ MX35_PIN_STXD5 = _MXC_BUILD_GPIO_PIN(0, 0, 0x130, 0x574),
+ MX35_PIN_SRXD5 = _MXC_BUILD_GPIO_PIN(0, 1, 0x134, 0x578),
+ MX35_PIN_SCK5 = _MXC_BUILD_GPIO_PIN(0, 2, 0x138, 0x57C),
+ MX35_PIN_STXFS5 = _MXC_BUILD_GPIO_PIN(0, 3, 0x13C, 0x580),
+
+ MX35_PIN_SCKR = _MXC_BUILD_GPIO_PIN(0, 4, 0x140, 0x584),
+ MX35_PIN_FSR = _MXC_BUILD_GPIO_PIN(0, 5, 0x144, 0x588),
+ MX35_PIN_HCKR = _MXC_BUILD_GPIO_PIN(0, 6, 0x148, 0x58C),
+ MX35_PIN_SCKT = _MXC_BUILD_GPIO_PIN(0, 7, 0x14C, 0x590),
+ MX35_PIN_FST = _MXC_BUILD_GPIO_PIN(0, 8, 0x150, 0x594),
+ MX35_PIN_HCKT = _MXC_BUILD_GPIO_PIN(0, 9, 0x154, 0x598),
+ MX35_PIN_TX5_RX0 = _MXC_BUILD_GPIO_PIN(0, 10, 0x158, 0x59C),
+ MX35_PIN_TX4_RX1 = _MXC_BUILD_GPIO_PIN(0, 11, 0x15C, 0x5A0),
+ MX35_PIN_TX3_RX2 = _MXC_BUILD_GPIO_PIN(0, 12, 0x160, 0x5A4),
+ MX35_PIN_TX2_RX3 = _MXC_BUILD_GPIO_PIN(0, 13, 0x164, 0x5A8),
+ MX35_PIN_TX1 = _MXC_BUILD_GPIO_PIN(0, 14, 0x168, 0x5AC),
+ MX35_PIN_TX0 = _MXC_BUILD_GPIO_PIN(0, 15, 0x16C, 0x5B0),
+
+ MX35_PIN_CSPI1_MOSI = _MXC_BUILD_GPIO_PIN(0, 16, 0x170, 0x5B4),
+ MX35_PIN_CSPI1_MISO = _MXC_BUILD_GPIO_PIN(0, 17, 0x174, 0x5B8),
+ MX35_PIN_CSPI1_SS0 = _MXC_BUILD_GPIO_PIN(0, 18, 0x178, 0x5BC),
+ MX35_PIN_CSPI1_SS1 = _MXC_BUILD_GPIO_PIN(0, 19, 0x17C, 0x5C0),
+ MX35_PIN_CSPI1_SCLK = _MXC_BUILD_GPIO_PIN(2, 4, 0x180, 0x5C4),
+ MX35_PIN_CSPI1_SPI_RDY = _MXC_BUILD_GPIO_PIN(2, 5, 0x184, 0x5C8),
+
+ MX35_PIN_RXD1 = _MXC_BUILD_GPIO_PIN(2, 6, 0x188, 0x5CC),
+ MX35_PIN_TXD1 = _MXC_BUILD_GPIO_PIN(2, 7, 0x18C, 0x5D0),
+ MX35_PIN_RTS1 = _MXC_BUILD_GPIO_PIN(2, 8, 0x190, 0x5D4),
+ MX35_PIN_CTS1 = _MXC_BUILD_GPIO_PIN(2, 9, 0x194, 0x5D8),
+ MX35_PIN_RXD2 = _MXC_BUILD_GPIO_PIN(2, 10, 0x198, 0x5DC),
+ MX35_PIN_TXD2 = _MXC_BUILD_GPIO_PIN(2, 11, 0x19C, 0x5E0),
+ MX35_PIN_RTS2 = _MXC_BUILD_GPIO_PIN(2, 12, 0x1A0, 0x5E4),
+ MX35_PIN_CTS2 = _MXC_BUILD_GPIO_PIN(2, 13, 0x1A4, 0x5E8),
+
+ MX35_PIN_USBOTG_PWR = _MXC_BUILD_GPIO_PIN(2, 14, 0x1A8, 0x60C),
+ MX35_PIN_USBOTG_OC = _MXC_BUILD_GPIO_PIN(2, 15, 0x1AC, 0x610),
+
+ MX35_PIN_LD0 = _MXC_BUILD_GPIO_PIN(1, 0, 0x1B0, 0x614),
+ MX35_PIN_LD1 = _MXC_BUILD_GPIO_PIN(1, 1, 0x1B4, 0x618),
+ MX35_PIN_LD2 = _MXC_BUILD_GPIO_PIN(1, 2, 0x1B8, 0x61C),
+ MX35_PIN_LD3 = _MXC_BUILD_GPIO_PIN(1, 3, 0x1BC, 0x620),
+ MX35_PIN_LD4 = _MXC_BUILD_GPIO_PIN(1, 4, 0x1C0, 0x624),
+ MX35_PIN_LD5 = _MXC_BUILD_GPIO_PIN(1, 5, 0x1C4, 0x628),
+ MX35_PIN_LD6 = _MXC_BUILD_GPIO_PIN(1, 6, 0x1C8, 0x62C),
+ MX35_PIN_LD7 = _MXC_BUILD_GPIO_PIN(1, 7, 0x1CC, 0x630),
+ MX35_PIN_LD8 = _MXC_BUILD_GPIO_PIN(1, 8, 0x1D0, 0x634),
+ MX35_PIN_LD9 = _MXC_BUILD_GPIO_PIN(1, 9, 0x1D4, 0x638),
+ MX35_PIN_LD10 = _MXC_BUILD_GPIO_PIN(1, 10, 0x1D8, 0x63C),
+ MX35_PIN_LD11 = _MXC_BUILD_GPIO_PIN(1, 11, 0x1DC, 0x640),
+ MX35_PIN_LD12 = _MXC_BUILD_GPIO_PIN(1, 12, 0x1E0, 0x644),
+ MX35_PIN_LD13 = _MXC_BUILD_GPIO_PIN(1, 13, 0x1E4, 0x648),
+ MX35_PIN_LD14 = _MXC_BUILD_GPIO_PIN(1, 14, 0x1E8, 0x64C),
+ MX35_PIN_LD15 = _MXC_BUILD_GPIO_PIN(1, 15, 0x1EC, 0x650),
+ MX35_PIN_LD16 = _MXC_BUILD_GPIO_PIN(1, 16, 0x1F0, 0x654),
+ MX35_PIN_LD17 = _MXC_BUILD_GPIO_PIN(1, 17, 0x1F4, 0x658),
+ MX35_PIN_LD18 = _MXC_BUILD_GPIO_PIN(2, 24, 0x1F8, 0x65C),
+ MX35_PIN_LD19 = _MXC_BUILD_GPIO_PIN(2, 25, 0x1FC, 0x660),
+ MX35_PIN_LD20 = _MXC_BUILD_GPIO_PIN(2, 26, 0x200, 0x664),
+ MX35_PIN_LD21 = _MXC_BUILD_GPIO_PIN(2, 27, 0x204, 0x668),
+ MX35_PIN_LD22 = _MXC_BUILD_GPIO_PIN(2, 28, 0x208, 0x66C),
+ MX35_PIN_LD23 = _MXC_BUILD_GPIO_PIN(2, 29, 0x20C, 0x670),
+
+ MX35_PIN_D3_HSYNC = _MXC_BUILD_GPIO_PIN(2, 30, 0x210, 0x674),
+ MX35_PIN_D3_FPSHIFT = _MXC_BUILD_GPIO_PIN(2, 31, 0x214, 0x678),
+ MX35_PIN_D3_DRDY = _MXC_BUILD_GPIO_PIN(0, 0, 0x218, 0x67C),
+ MX35_PIN_CONTRAST = _MXC_BUILD_GPIO_PIN(0, 1, 0x21C, 0x680),
+ MX35_PIN_D3_VSYNC = _MXC_BUILD_GPIO_PIN(0, 2, 0x220, 0x684),
+ MX35_PIN_D3_REV = _MXC_BUILD_GPIO_PIN(0, 3, 0x224, 0x688),
+ MX35_PIN_D3_CLS = _MXC_BUILD_GPIO_PIN(0, 4, 0x228, 0x68C),
+ MX35_PIN_D3_SPL = _MXC_BUILD_GPIO_PIN(0, 5, 0x22C, 0x690),
+
+ MX35_PIN_SD1_CMD = _MXC_BUILD_GPIO_PIN(0, 6, 0x230, 0x694),
+ MX35_PIN_SD1_CLK = _MXC_BUILD_GPIO_PIN(0, 7, 0x234, 0x698),
+ MX35_PIN_SD1_DATA0 = _MXC_BUILD_GPIO_PIN(0, 8, 0x238, 0x69C),
+ MX35_PIN_SD1_DATA1 = _MXC_BUILD_GPIO_PIN(0, 9, 0x23C, 0x6A0),
+ MX35_PIN_SD1_DATA2 = _MXC_BUILD_GPIO_PIN(0, 10, 0x240, 0x6A4),
+ MX35_PIN_SD1_DATA3 = _MXC_BUILD_GPIO_PIN(0, 11, 0x244, 0x6A8),
+ MX35_PIN_SD2_CMD = _MXC_BUILD_GPIO_PIN(1, 0, 0x248, 0x6AC),
+ MX35_PIN_SD2_CLK = _MXC_BUILD_GPIO_PIN(1, 1, 0x24C, 0x6B0),
+ MX35_PIN_SD2_DATA0 = _MXC_BUILD_GPIO_PIN(1, 2, 0x250, 0x6B4),
+ MX35_PIN_SD2_DATA1 = _MXC_BUILD_GPIO_PIN(1, 3, 0x254, 0x6B8),
+ MX35_PIN_SD2_DATA2 = _MXC_BUILD_GPIO_PIN(1, 4, 0x258, 0x6BC),
+ MX35_PIN_SD2_DATA3 = _MXC_BUILD_GPIO_PIN(1, 5, 0x25C, 0x6C0),
+
+ MX35_PIN_ATA_CS0 = _MXC_BUILD_GPIO_PIN(1, 6, 0x260, 0x6C4),
+ MX35_PIN_ATA_CS1 = _MXC_BUILD_GPIO_PIN(1, 7, 0x264, 0x6C8),
+ MX35_PIN_ATA_DIOR = _MXC_BUILD_GPIO_PIN(1, 8, 0x268, 0x6CC),
+ MX35_PIN_ATA_DIOW = _MXC_BUILD_GPIO_PIN(1, 9, 0x26C, 0x6D0),
+ MX35_PIN_ATA_DMACK = _MXC_BUILD_GPIO_PIN(1, 10, 0x270, 0x6D4),
+ MX35_PIN_ATA_RESET_B = _MXC_BUILD_GPIO_PIN(1, 11, 0x274, 0x6D8),
+ MX35_PIN_ATA_IORDY = _MXC_BUILD_GPIO_PIN(1, 12, 0x278, 0x6DC),
+ MX35_PIN_ATA_DATA0 = _MXC_BUILD_GPIO_PIN(1, 13, 0x27C, 0x6E0),
+ MX35_PIN_ATA_DATA1 = _MXC_BUILD_GPIO_PIN(1, 14, 0x280, 0x6E4),
+ MX35_PIN_ATA_DATA2 = _MXC_BUILD_GPIO_PIN(1, 15, 0x284, 0x6E8),
+ MX35_PIN_ATA_DATA3 = _MXC_BUILD_GPIO_PIN(1, 16, 0x288, 0x6EC),
+ MX35_PIN_ATA_DATA4 = _MXC_BUILD_GPIO_PIN(1, 17, 0x28C, 0x6F0),
+ MX35_PIN_ATA_DATA5 = _MXC_BUILD_GPIO_PIN(1, 18, 0x290, 0x6F4),
+ MX35_PIN_ATA_DATA6 = _MXC_BUILD_GPIO_PIN(1, 19, 0x294, 0x6F8),
+ MX35_PIN_ATA_DATA7 = _MXC_BUILD_GPIO_PIN(1, 20, 0x298, 0x6FC),
+ MX35_PIN_ATA_DATA8 = _MXC_BUILD_GPIO_PIN(1, 21, 0x29C, 0x700),
+ MX35_PIN_ATA_DATA9 = _MXC_BUILD_GPIO_PIN(1, 22, 0x2A0, 0x704),
+ MX35_PIN_ATA_DATA10 = _MXC_BUILD_GPIO_PIN(1, 23, 0x2A4, 0x708),
+ MX35_PIN_ATA_DATA11 = _MXC_BUILD_GPIO_PIN(1, 24, 0x2A8, 0x70C),
+ MX35_PIN_ATA_DATA12 = _MXC_BUILD_GPIO_PIN(1, 25, 0x2AC, 0x710),
+ MX35_PIN_ATA_DATA13 = _MXC_BUILD_GPIO_PIN(1, 26, 0x2B0, 0x714),
+ MX35_PIN_ATA_DATA14 = _MXC_BUILD_GPIO_PIN(1, 27, 0x2B4, 0x718),
+ MX35_PIN_ATA_DATA15 = _MXC_BUILD_GPIO_PIN(1, 28, 0x2B8, 0x71C),
+ MX35_PIN_ATA_INTRQ = _MXC_BUILD_GPIO_PIN(1, 29, 0x2BC, 0x720),
+ MX35_PIN_ATA_BUFF_EN = _MXC_BUILD_GPIO_PIN(1, 30, 0x2C0, 0x724),
+ MX35_PIN_ATA_DMARQ = _MXC_BUILD_GPIO_PIN(1, 31, 0x2C4, 0x728),
+ MX35_PIN_ATA_DA0 = _MXC_BUILD_GPIO_PIN(2, 0, 0x2C8, 0x72C),
+ MX35_PIN_ATA_DA1 = _MXC_BUILD_GPIO_PIN(2, 1, 0x2CC, 0x730),
+ MX35_PIN_ATA_DA2 = _MXC_BUILD_GPIO_PIN(2, 2, 0x2D0, 0x734),
+
+ MX35_PIN_MLB_CLK = _MXC_BUILD_GPIO_PIN(2, 3, 0x2D4, 0x738),
+ MX35_PIN_MLB_DAT = _MXC_BUILD_GPIO_PIN(2, 4, 0x2D8, 0x73C),
+ MX35_PIN_MLB_SIG = _MXC_BUILD_GPIO_PIN(2, 5, 0x2DC, 0x740),
+
+ MX35_PIN_FEC_TX_CLK = _MXC_BUILD_GPIO_PIN(2, 6, 0x2E0, 0x744),
+ MX35_PIN_FEC_RX_CLK = _MXC_BUILD_GPIO_PIN(2, 7, 0x2E4, 0x748),
+ MX35_PIN_FEC_RX_DV = _MXC_BUILD_GPIO_PIN(2, 8, 0x2E8, 0x74C),
+ MX35_PIN_FEC_COL = _MXC_BUILD_GPIO_PIN(2, 9, 0x2EC, 0x750),
+ MX35_PIN_FEC_RDATA0 = _MXC_BUILD_GPIO_PIN(2, 10, 0x2F0, 0x754),
+ MX35_PIN_FEC_TDATA0 = _MXC_BUILD_GPIO_PIN(2, 11, 0x2F4, 0x758),
+ MX35_PIN_FEC_TX_EN = _MXC_BUILD_GPIO_PIN(2, 12, 0x2F8, 0x75C),
+ MX35_PIN_FEC_MDC = _MXC_BUILD_GPIO_PIN(2, 13, 0x2FC, 0x760),
+ MX35_PIN_FEC_MDIO = _MXC_BUILD_GPIO_PIN(2, 14, 0x300, 0x764),
+ MX35_PIN_FEC_TX_ERR = _MXC_BUILD_GPIO_PIN(2, 15, 0x304, 0x768),
+ MX35_PIN_FEC_RX_ERR = _MXC_BUILD_GPIO_PIN(2, 16, 0x308, 0x76C),
+ MX35_PIN_FEC_CRS = _MXC_BUILD_GPIO_PIN(2, 17, 0x30C, 0x770),
+ MX35_PIN_FEC_RDATA1 = _MXC_BUILD_GPIO_PIN(2, 18, 0x310, 0x774),
+ MX35_PIN_FEC_TDATA1 = _MXC_BUILD_GPIO_PIN(2, 19, 0x314, 0x778),
+ MX35_PIN_FEC_RDATA2 = _MXC_BUILD_GPIO_PIN(2, 20, 0x318, 0x77C),
+ MX35_PIN_FEC_TDATA2 = _MXC_BUILD_GPIO_PIN(2, 21, 0x31C, 0x780),
+ MX35_PIN_FEC_RDATA3 = _MXC_BUILD_GPIO_PIN(2, 22, 0x320, 0x784),
+ MX35_PIN_FEC_TDATA3 = _MXC_BUILD_GPIO_PIN(2, 23, 0x324, 0x788),
+};
+
+#endif
+#endif
diff --git a/arch/arm/mach-mx35/mx35evb.c b/arch/arm/mach-mx35/mx35evb.c
new file mode 100644
index 000000000000..527ed464fdc6
--- /dev/null
+++ b/arch/arm/mach-mx35/mx35evb.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/nodemask.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/mach/flash.h>
+#endif
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/keypad.h>
+#include <mach/memory.h>
+#include <mach/gpio.h>
+
+#include "board-mx35evb.h"
+#include "crm_regs.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx35/mx35evb.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX35
+ */
+
+unsigned int mx35evb_board_io;
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+/* MTD NOR flash */
+
+#if defined(CONFIG_MTD_MXC) || defined(CONFIG_MTD_MXC_MODULE)
+
+static struct mtd_partition mxc_nor_partitions[] = {
+ {
+ .name = "Bootloader",
+ .size = 512 * 1024,
+ .offset = 0x00000000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "nor.Kernel",
+ .size = 4 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = 0},
+ {
+ .name = "nor.userfs",
+ .size = 30 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = 0},
+ {
+ .name = "nor.rootfs",
+ .size = 28 * 1024 * 1024,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE},
+ {
+ .name = "FIS directory",
+ .size = 12 * 1024,
+ .offset = 0x01FE0000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+ {
+ .name = "Redboot config",
+ .size = MTDPART_SIZ_FULL,
+ .offset = 0x01FFF000,
+ .mask_flags = MTD_WRITEABLE /* force read-only */
+ },
+};
+
+static struct flash_platform_data mxc_flash_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+ .parts = mxc_nor_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_nor_partitions),
+};
+
+static struct resource mxc_flash_resource = {
+ .start = 0xa0000000,
+ .end = 0xa0000000 + 0x04000000 - 1,
+ .flags = IORESOURCE_MEM,
+
+};
+
+static struct platform_device mxc_nor_mtd_device = {
+ .name = "mxc_nor_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &mxc_flash_resource,
+};
+
+static void mxc_init_nor_mtd(void)
+{
+ (void)platform_device_register(&mxc_nor_mtd_device);
+}
+#else
+static void mxc_init_nor_mtd(void)
+{
+}
+#endif
+
+/* MTD NAND flash */
+
+#if defined(CONFIG_MTD_NAND_MXC) || defined(CONFIG_MTD_NAND_MXC_MODULE)
+
+static struct mtd_partition mxc_nand_partitions[] = {
+ {
+ .name = "IPL-SPL",
+ .offset = 0,
+ .size = 256 * 1024},
+ {
+ .name = "nand.kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 4 * 1024 * 1024},
+ {
+ .name = "nand.rootfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 96 * 1024 * 1024},
+ {
+ .name = "nand.configure",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 8 * 1024 * 1024},
+ {
+ .name = "nand.userfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL},
+};
+
+static struct flash_platform_data mxc_nand_data = {
+ .parts = mxc_nand_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_nand_partitions),
+ .width = 1,
+};
+
+static struct platform_device mxc_nand_mtd_device = {
+ .name = "mxc_nand_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static void mxc_init_nand_mtd(void)
+{
+ if (__raw_readl(MXC_CCM_RCSR) & MXC_CCM_RCSR_NF16B)
+ mxc_nand_data.width = 2;
+
+ platform_device_register(&mxc_nand_mtd_device);
+}
+#else
+static inline void mxc_init_nand_mtd(void)
+{
+}
+#endif
+
+#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
+static struct resource smsc911x_resources[] = {
+ {
+ .start = ENET_BASE_ADDRESS,
+ .end = ENET_BASE_ADDRESS + 0x100,
+ .flags = IORESOURCE_MEM,},
+ {
+ .start = EXPIO_INT_ENET,
+ .end = EXPIO_INT_ENET,
+ .flags = IORESOURCE_IRQ,}
+};
+
+static struct platform_device mxc_smsc911x_device = {
+ .name = "smsc911x",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(smsc911x_resources),
+ .resource = smsc911x_resources,
+};
+
+static void mxc_init_enet(void)
+{
+ int i;
+ /*reset ext uart in cpld */
+ __raw_writew(PBC_BCTRL1_ENET_RST_B, PBC_BCTRL1_SET);
+ /*delay some time for reset finish */
+ for (i = 0; i < 10000; i++) ;
+ __raw_writew(PBC_BCTRL1_ENET_RST_B, PBC_BCTRL1_CLR);
+
+ platform_device_register(&mxc_smsc911x_device);
+}
+#else
+static inline void mxc_init_enet(void)
+{
+}
+#endif
+
+#if defined(CONFIG_FEC) || defined(CONFIG_FEC_MODULE)
+unsigned int expio_intr_fec;
+
+EXPORT_SYMBOL(expio_intr_fec);
+#endif
+
+/*!
+ * Board specific fixup function. It is called by \b setup_arch() in
+ * setup.c file very early on during kernel starts. It allows the user to
+ * statically fill in the proper values for the passed-in parameters. None of
+ * the parameters is used currently.
+ *
+ * @param desc pointer to \b struct \b machine_desc
+ * @param tags pointer to \b struct \b tag
+ * @param cmdline pointer to the command line
+ * @param mi pointer to \b struct \b meminfo
+ */
+static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ mxc_cpu_init();
+
+#ifdef CONFIG_DISCONTIGMEM
+ do {
+ int nid;
+ mi->nr_banks = MXC_NUMNODES;
+ for (nid = 0; nid < mi->nr_banks; nid++)
+ SET_NODE(mi, nid);
+ } while (0);
+#endif
+}
+
+/*!
+ * Board specific initialization.
+ */
+static void __init mxc_board_init(void)
+{
+ mxc_cpu_common_init();
+ mxc_clocks_init();
+ early_console_setup(saved_command_line);
+ mxc_register_gpios();
+ mx35evb_gpio_init();
+ mxc_init_enet();
+ mxc_init_nor_mtd();
+ mxc_init_nand_mtd();
+
+}
+
+#define PLL_PCTL_REG(brmo, pd, mfd, mfi, mfn) \
+ (((brmo) << 31) + (((pd) - 1) << 26) + (((mfd) - 1) << 16) + \
+ ((mfi) << 10) + mfn)
+
+/* For 24MHz input clock */
+#define PLL_665MHZ PLL_PCTL_REG(1, 1, 48, 13, 41)
+#define PLL_532MHZ PLL_PCTL_REG(1, 1, 12, 11, 1)
+#define PLL_399MHZ PLL_PCTL_REG(0, 1, 16, 8, 5)
+
+/* working point(wp): 0,1 - 133MHz; 2,3 - 266MHz; 4,5 - 399MHz;*/
+/* auto input clock table */
+static struct cpu_wp cpu_wp_auto[] = {
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 133000000,
+ .pdr0_reg = (0x2 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 133000000,
+ .pdr0_reg = (0x6 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 266000000,
+ .pdr0_reg = (0x1 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 266000000,
+ .pdr0_reg = (0x5 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 399000000,
+ .pdr0_reg = (0x0 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_399MHZ,
+ .pll_rate = 399000000,
+ .cpu_rate = 399000000,
+ .pdr0_reg = (0x6 << MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET),},
+};
+
+/* consumer input clock table */
+static struct cpu_wp cpu_wp_con[] = {
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 133000000,
+ .pdr0_reg = (0x6 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 133000000,
+ .pdr0_reg = (0xE << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 266000000,
+ .pdr0_reg = (0x2 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 266000000,
+ .pdr0_reg = (0xA << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 399000000,
+ .pdr0_reg = (0x1 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 399000000,
+ .pdr0_reg = (0x9 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 532000000,
+ .pdr0_reg = (0x0 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_532MHZ,
+ .pll_rate = 532000000,
+ .cpu_rate = 532000000,
+ .pdr0_reg = (0x8 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+ {
+ .pll_reg = PLL_665MHZ,
+ .pll_rate = 665000000,
+ .cpu_rate = 665000000,
+ .pdr0_reg = (0x7 << MXC_CCM_PDR0_CON_MUX_DIV_OFFSET),},
+};
+
+struct cpu_wp *get_cpu_wp(int *wp)
+{
+ if (__raw_readl(MXC_CCM_PDR0) & MXC_CCM_PDR0_AUTO_CON) {
+ *wp = 9;
+ return cpu_wp_con;
+ } else {
+ *wp = 6;
+ return cpu_wp_auto;
+ }
+}
+
+/*
+ * The following uses standard kernel macros define in arch.h in order to
+ * initialize __mach_desc_MX35EVB data structure.
+ */
+/* *INDENT-OFF* */
+MACHINE_START(MX35EVB, "Freescale MX35 EVB")
+ /* Maintainer: Freescale Semiconductor, Inc. */
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .fixup = fixup_mxc_board,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mxc_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx35/mx35evb_cpld.c b/arch/arm/mach-mx35/mx35evb_cpld.c
new file mode 100644
index 000000000000..fbf9188f3671
--- /dev/null
+++ b/arch/arm/mach-mx35/mx35evb_cpld.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <mach/gpio.h>
+
+#include "board-mx35evb.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx35/mx35evb_cpld.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX35
+ */
+
+static void mxc_expio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ /*TODO:virtual interrupt dispatcher */
+}
+
+/*
+ * Disable an expio pin's interrupt by setting the bit in the imr.
+ * @param irq an expio virtual irq number
+ */
+static void expio_mask_irq(u32 irq)
+{
+ /*TODO:mask virtual interrupt #irq */
+}
+
+/*
+ * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr.
+ * @param irq an expanded io virtual irq number
+ */
+static void expio_ack_irq(u32 irq)
+{
+ /*TODO:ack & mask virtual interrupt #irq */
+}
+
+/*
+ * Enable a expio pin's interrupt by clearing the bit in the imr.
+ * @param irq a expio virtual irq number
+ */
+static void expio_unmask_irq(u32 irq)
+{
+ /*TODO:enable virtual interrupt #irq */
+}
+
+static struct irq_chip expio_irq_chip = {
+ .ack = expio_ack_irq,
+ .mask = expio_mask_irq,
+ .unmask = expio_unmask_irq,
+};
+
+static int __init mxc_expio_init(void)
+{
+ /*TODO:enable virtual interrupts generated by CPLD */
+ return 0;
+}
+
+arch_initcall(mxc_expio_init);
diff --git a/arch/arm/mach-mx35/mx35evb_gpio.c b/arch/arm/mach-mx35/mx35evb_gpio.c
new file mode 100644
index 000000000000..e326699d6d80
--- /dev/null
+++ b/arch/arm/mach-mx35/mx35evb_gpio.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include "board-mx35evb.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx35/mx35evb_gpio.c
+ *
+ * @brief This file contains all the GPIO setup functions for the board.
+ *
+ * @ingroup GPIO_MX35
+ */
+
+/*!
+ * This system-wise GPIO function initializes the pins during system startup.
+ * All the statically linked device drivers should put the proper GPIO
+ * initialization code inside this function. It is called by \b fixup_mx31ads()
+ * during system startup. This function is board specific.
+ */
+void mx35evb_gpio_init(void)
+{
+ /* config CS4 */
+ mxc_request_iomux(MX35_PIN_CS4, MUX_CONFIG_FUNC);
+
+}
+
+/*!
+ * Setup GPIO for a UART port to be active
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_active(int port, int no_irda)
+{
+ /*
+ * Configure the IOMUX control registers for the UART signals
+ */
+ switch (port) {
+ /* UART 1 IOMUX Configs */
+ case 0:
+ mxc_request_iomux(MX35_PIN_RXD1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_TXD1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_RTS1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CTS1, MUX_CONFIG_FUNC);
+
+ mxc_iomux_set_pad(MX35_PIN_RXD1,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX35_PIN_TXD1,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_RTS1,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX35_PIN_CTS1,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+
+ break;
+ /* UART 2 IOMUX Configs */
+ case 1:
+ mxc_request_iomux(MX35_PIN_TXD2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_RXD2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_RTS2, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CTS2, MUX_CONFIG_FUNC);
+
+ mxc_iomux_set_pad(MX35_PIN_RXD2,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX35_PIN_TXD2,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+ mxc_iomux_set_pad(MX35_PIN_RTS2,
+ PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PU);
+ mxc_iomux_set_pad(MX35_PIN_CTS2,
+ PAD_CTL_PUE_PUD | PAD_CTL_100K_PD);
+
+ break;
+ /* UART 3 IOMUX Configs */
+ case 2:
+ mxc_request_iomux(MX35_PIN_FEC_TX_CLK, MUX_CONFIG_ALT2);
+ mxc_request_iomux(MX35_PIN_FEC_RX_CLK, MUX_CONFIG_ALT2);
+ mxc_request_iomux(MX35_PIN_FEC_COL, MUX_CONFIG_ALT2);
+ mxc_request_iomux(MX35_PIN_FEC_RX_DV, MUX_CONFIG_ALT2);
+
+ mxc_iomux_set_input(MUX_IN_UART3_UART_RTS_B, INPUT_CTL_PATH2);
+ mxc_iomux_set_input(MUX_IN_UART3_UART_RXD_MUX, INPUT_CTL_PATH3);
+ break;
+ default:
+ break;
+ }
+
+}
+
+EXPORT_SYMBOL(gpio_uart_active);
+
+/*!
+ * Setup GPIO for a UART port to be inactive
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_inactive(int port, int no_irda)
+{
+ switch (port) {
+ case 0:
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_RXD1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_TXD1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_RTS1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CTS1), NULL);
+
+ mxc_free_iomux(MX35_PIN_RXD1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_TXD1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_RTS1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CTS1, MUX_CONFIG_GPIO);
+ break;
+ case 1:
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_RXD2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_TXD2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_RTS2), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CTS2), NULL);
+
+ mxc_free_iomux(MX35_PIN_RXD2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_TXD2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_RTS2, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CTS2, MUX_CONFIG_GPIO);
+ break;
+ case 2:
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_TX_CLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RX_CLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_COL), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_FEC_RX_DV), NULL);
+
+ mxc_free_iomux(MX35_PIN_FEC_TX_CLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RX_CLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_COL, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_FEC_RX_DV, MUX_CONFIG_GPIO);
+
+ mxc_iomux_set_input(MUX_IN_UART3_UART_RTS_B, INPUT_CTL_PATH0);
+ mxc_iomux_set_input(MUX_IN_UART3_UART_RXD_MUX, INPUT_CTL_PATH0);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_uart_inactive);
+
+/*!
+ * Configure the IOMUX GPR register to receive shared SDMA UART events
+ *
+ * @param port a UART port
+ */
+void config_uartdma_event(int port)
+{
+}
+
+EXPORT_SYMBOL(config_uartdma_event);
+
+void gpio_fec_active(void)
+{
+ /*TODO:require the pins related with FEC */
+}
+
+EXPORT_SYMBOL(gpio_fec_active);
+
+void gpio_fec_inactive(void)
+{
+ /*TODO:release the pins related with FEC */
+}
+
+EXPORT_SYMBOL(gpio_fec_inactive);
+
+/*!
+ * Setup GPIO for a CSPI device to be active
+ *
+ * @param cspi_mod an CSPI device
+ */
+void gpio_spi_active(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ mxc_request_iomux(MX35_PIN_CSPI1_MOSI, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSPI1_MISO, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSPI1_SS0, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSPI1_SS1, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSPI1_SCLK, MUX_CONFIG_FUNC);
+ mxc_request_iomux(MX35_PIN_CSPI1_SPI_RDY, MUX_CONFIG_FUNC);
+
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_MOSI,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PD | PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_MISO,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PD | PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_SS0,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PU | PAD_CTL_ODE_CMOS |
+ PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_SS1,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PU | PAD_CTL_ODE_CMOS |
+ PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_SCLK,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PD | PAD_CTL_DRV_NORMAL);
+ mxc_iomux_set_pad(MX35_PIN_CSPI1_SPI_RDY,
+ PAD_CTL_DRV_3_3V | PAD_CTL_HYS_SCHMITZ |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PUD |
+ PAD_CTL_100K_PU | PAD_CTL_DRV_NORMAL);
+ break;
+ case 1:
+ /* SPI2 */
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_spi_active);
+
+/*!
+ * Setup GPIO for a CSPI device to be inactive
+ *
+ * @param cspi_mod a CSPI device
+ */
+void gpio_spi_inactive(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ /* SPI1 */
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_MOSI), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_MISO), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_SS0), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_SS1), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_SCLK), NULL);
+ gpio_request(IOMUX_TO_GPIO(MX35_PIN_CSPI1_SPI_RDY), NULL);
+
+ mxc_free_iomux(MX35_PIN_CSPI1_MOSI, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CSPI1_MISO, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CSPI1_SS0, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CSPI1_SS1, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CSPI1_SCLK, MUX_CONFIG_GPIO);
+ mxc_free_iomux(MX35_PIN_CSPI1_SPI_RDY, MUX_CONFIG_GPIO);
+ break;
+ case 1:
+ /* SPI2 */
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_spi_inactive);
diff --git a/arch/arm/mach-mx35/pm.c b/arch/arm/mach-mx35/pm.c
new file mode 100644
index 000000000000..ad375ce2fb78
--- /dev/null
+++ b/arch/arm/mach-mx35/pm.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+
+/*!
+ * @defgroup MSL_MX35 i.MX35 Machine Specific Layer (MSL)
+ */
+
+/*!
+ * @file mach-mx35/pm.c
+ * @brief This file contains suspend operations
+ *
+ * @ingroup MSL_MX35
+ */
+static int mx35_suspend_enter(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ mxc_cpu_lp_set(STOP_POWER_OFF);
+ break;
+ case PM_SUSPEND_STANDBY:
+ mxc_cpu_lp_set(STOP_POWER_ON);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* Executing CP15 (Wait-for-Interrupt) Instruction */
+ cpu_do_idle();
+ return 0;
+}
+
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+static int mx35_suspend_prepare(void)
+{
+ return 0;
+}
+
+/*
+ * Called after devices are re-setup, but before processes are thawed.
+ */
+static void mx35_suspend_finish(void)
+{
+}
+
+static int mx35_pm_valid(suspend_state_t state)
+{
+ return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
+}
+
+struct platform_suspend_ops mx35_suspend_ops = {
+ .valid = mx35_pm_valid,
+ .prepare = mx35_suspend_prepare,
+ .enter = mx35_suspend_enter,
+ .finish = mx35_suspend_finish,
+};
+
+static int __init mx35_pm_init(void)
+{
+ pr_info("Static Power Management for Freescale i.MX35\n");
+ suspend_set_ops(&mx35_suspend_ops);
+
+ return 0;
+}
+
+late_initcall(mx35_pm_init);
diff --git a/arch/arm/mach-mx35/sdma_script_code.h b/arch/arm/mach-mx35/sdma_script_code.h
new file mode 100644
index 000000000000..e1b6b59bc31e
--- /dev/null
+++ b/arch/arm/mach-mx35/sdma_script_code.h
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 */
+
+/*!
+ * @file sdma_script_code.h
+ * @brief This file contains functions of SDMA scripts code initialization
+ *
+ * The file was generated automatically. Based on sdma scripts library.
+ *
+ * @ingroup SDMA
+ */
+/*******************************************************************************
+
+ SDMA RELEASE LABEL: "SS15_RINGO"
+
+*******************************************************************************/
+
+#ifndef __SDMA_SCRIPT_CODE_H__
+#define __SDMA_SCRIPT_CODE_H__
+
+/*!
+* SDMA ROM scripts start addresses and sizes
+*/
+
+#define start_ADDR 0
+#define start_SIZE 22
+
+#define core_ADDR 80
+#define core_SIZE 232
+
+#define common_ADDR 312
+#define common_SIZE 330
+
+#define ap_2_ap_ADDR 642
+#define ap_2_ap_SIZE 41
+
+#define app_2_mcu_ADDR 683
+#define app_2_mcu_SIZE 64
+
+#define mcu_2_app_ADDR 747
+#define mcu_2_app_SIZE 70
+
+#define uart_2_mcu_ADDR 817
+#define uart_2_mcu_SIZE 75
+
+#define shp_2_mcu_ADDR 892
+#define shp_2_mcu_SIZE 69
+
+#define mcu_2_shp_ADDR 961
+#define mcu_2_shp_SIZE 72
+
+#define per_2_shp_ADDR 1033
+#define per_2_shp_SIZE 78
+
+#define shp_2_per_ADDR 1111
+#define shp_2_per_SIZE 72
+
+#define uartsh_2_mcu_ADDR 1183
+#define uartsh_2_mcu_SIZE 69
+
+#define mcu_2_ata_ADDR 1252
+#define mcu_2_ata_SIZE 81
+
+#define ata_2_mcu_ADDR 1333
+#define ata_2_mcu_SIZE 96
+
+#define loop_DMAs_routines_ADDR 1429
+#define loop_DMAs_routines_SIZE 227
+
+#define test_ADDR 1656
+#define test_SIZE 63
+
+#define signature_ADDR 1023
+#define signature_SIZE 1
+
+/*!
+* SDMA RAM scripts start addresses and sizes
+*/
+
+#define app_2_per_ADDR 6144
+#define app_2_per_SIZE 66
+
+#define asrc__mcu_ADDR 6210
+#define asrc__mcu_SIZE 114
+
+#define ext_mem__ipu_ram_ADDR 6324
+#define ext_mem__ipu_ram_SIZE 123
+
+#define mcu_2_spdif_ADDR 6447
+#define mcu_2_spdif_SIZE 103
+
+#define p_2_p_ADDR 6550
+#define p_2_p_SIZE 254
+
+#define per_2_app_ADDR 6804
+#define per_2_app_SIZE 74
+
+#define spdif_2_mcu_ADDR 6878
+#define spdif_2_mcu_SIZE 47
+
+#define uart_2_per_ADDR 6925
+#define uart_2_per_SIZE 73
+
+#define uartsh_2_per_ADDR 6998
+#define uartsh_2_per_SIZE 67
+
+/*!
+* SDMA RAM image start address and size
+*/
+
+#define RAM_CODE_START_ADDR 6144
+#define RAM_CODE_SIZE 921
+
+/*!
+* Buffer that holds the SDMA RAM image
+*/
+__attribute__ ((__aligned__(4)))
+#ifndef CONFIG_XIP_KERNEL
+const
+#endif
+static const short sdma_code[] = {
+ 0xc1e3, 0x57db, 0x52fb, 0x6ac3, 0x52f3, 0x6ad7, 0x008f, 0x00d5,
+ 0x7d01, 0x008d, 0x05a0, 0x0478, 0x7d03, 0x0479, 0x7d1c, 0x7c21,
+ 0x0479, 0x7c14, 0x6ddd, 0x56ee, 0x62c8, 0x7e28, 0x0660, 0x7d02,
+ 0x0210, 0x0212, 0x6ac8, 0x7f22, 0x0212, 0x6ac8, 0x7f1f, 0x0212,
+ 0x6ac8, 0x7f1c, 0x2003, 0x4800, 0x7cef, 0x9836, 0x6ddd, 0x7802,
+ 0x62c8, 0x6ac8, 0x9835, 0x6dde, 0x0015, 0x7802, 0x62c8, 0x6ac8,
+ 0x9835, 0x0015, 0x0015, 0x7801, 0x62d8, 0x7c08, 0x6ddf, 0x7f06,
+ 0x0000, 0x4d00, 0x7d05, 0xc1fa, 0x57db, 0x9806, 0xc273, 0x0454,
+ 0xc20a, 0x9801, 0xc1d9, 0xc1e3, 0x56f3, 0x57db, 0x047a, 0x7d07,
+ 0x072f, 0x076e, 0x7d02, 0x6ec7, 0x9855, 0x6ed7, 0x9855, 0x074f,
+ 0x076e, 0x7d02, 0x6e01, 0x9855, 0x6e05, 0x5ce3, 0x048f, 0x0410,
+ 0x3c0f, 0x5c93, 0x0eff, 0x06bf, 0x06d5, 0x7d01, 0x068d, 0x05a6,
+ 0x5deb, 0x55fb, 0x008e, 0x0768, 0x7d02, 0x0769, 0x7c04, 0x06d4,
+ 0x7d01, 0x008c, 0x04a0, 0x06a0, 0x076f, 0x7d0c, 0x076e, 0x7d05,
+ 0x7802, 0x62c8, 0x5a05, 0x7c2b, 0x9887, 0x7802, 0x5205, 0x6ac8,
+ 0x7c26, 0x9887, 0x076e, 0x7d05, 0x7802, 0x620b, 0x5a05, 0x7c21,
+ 0x9887, 0x7802, 0x5205, 0x6a0b, 0x7c1c, 0x6a28, 0x7f1a, 0x0768,
+ 0x7d02, 0x0769, 0x7c0a, 0x4c00, 0x7c08, 0x0768, 0x7d03, 0x5a05,
+ 0x7f11, 0x9894, 0x5205, 0x7e0e, 0x5493, 0x4e00, 0x7ccb, 0x0000,
+ 0x54e3, 0x55eb, 0x4d00, 0x7d0a, 0xc1fa, 0x57db, 0x9856, 0x68cc,
+ 0x98a2, 0x680c, 0x009e, 0x0007, 0x54e3, 0xd8a8, 0xc20a, 0x9844,
+ 0x55eb, 0x009d, 0x058c, 0x0aff, 0x0211, 0x1aff, 0x05ba, 0x05a0,
+ 0x04b2, 0x04ad, 0x0454, 0x0006, 0x0e70, 0x0611, 0x5616, 0xc13c,
+ 0x7d2a, 0x5ade, 0x008e, 0xc14e, 0x7c26, 0x5be0, 0x5ef0, 0x5ce8,
+ 0x0688, 0x08ff, 0x0011, 0x28ff, 0x00bc, 0x53f6, 0x05df, 0x7d0b,
+ 0x6dc5, 0x03df, 0x7d03, 0x6bd5, 0xd903, 0x98df, 0x6b05, 0xc5f5,
+ 0x7e27, 0x7f29, 0x98df, 0x6d01, 0x03df, 0x7d05, 0x6bd5, 0xc61f,
+ 0x7e18, 0x7f1a, 0x98df, 0x6b05, 0xc595, 0x7e07, 0x7f06, 0x52de,
+ 0x53e6, 0xc159, 0x7dd7, 0x0200, 0x98b7, 0x0007, 0x6004, 0x680c,
+ 0x53f6, 0x028e, 0x00a3, 0xc256, 0x048b, 0x0498, 0x0454, 0x068a,
+ 0x98df, 0x0207, 0x680c, 0x6ddf, 0x0107, 0x68ff, 0x60d0, 0x98e8,
+ 0x0207, 0x68ff, 0x6d28, 0x0107, 0x6004, 0x680c, 0x98e8, 0x0007,
+ 0x68ff, 0x60d0, 0x98e8, 0x0288, 0x03a5, 0x3b03, 0x3d03, 0x4d00,
+ 0x7d0a, 0x0804, 0x00a5, 0x00da, 0x7d1a, 0x02a0, 0x7b01, 0x65d8,
+ 0x7eee, 0x65ff, 0x7eec, 0x0804, 0x02d0, 0x7d11, 0x4b00, 0x7c0f,
+ 0x008a, 0x3003, 0x6dcf, 0x6bdf, 0x0015, 0x0015, 0x7b02, 0x65d8,
+ 0x0000, 0x7edd, 0x63ff, 0x7edb, 0x3a03, 0x6dcd, 0x6bdd, 0x008a,
+ 0x7b02, 0x65d8, 0x0000, 0x7ed3, 0x65ff, 0x7ed1, 0x0006, 0xc1d9,
+ 0xc1e3, 0x57db, 0x52f3, 0x047a, 0x7d06, 0x0479, 0x7c02, 0x6ac6,
+ 0x993c, 0x6ac7, 0x993c, 0x6a01, 0x008f, 0x00d5, 0x7d01, 0x008d,
+ 0x05a0, 0x5deb, 0x56fb, 0x0478, 0x7d4e, 0x0479, 0x7c1f, 0x0015,
+ 0x0388, 0x047a, 0x7d03, 0x62c8, 0x7e39, 0x9950, 0x620a, 0x7e38,
+ 0x0808, 0x7801, 0x0217, 0x5a06, 0x7f34, 0x2301, 0x047a, 0x7d03,
+ 0x62c8, 0x7e2c, 0x995d, 0x620a, 0x7e2b, 0x0808, 0x7801, 0x0217,
+ 0x5a26, 0x7f27, 0x2301, 0x4b00, 0x7ce4, 0x997c, 0x0015, 0x0015,
+ 0x0015, 0x047a, 0x7d09, 0x7806, 0x0b00, 0x62c8, 0x5a06, 0x0b01,
+ 0x62c8, 0x5a26, 0x7c13, 0x997c, 0x7806, 0x0b00, 0x620b, 0x5a06,
+ 0x0b01, 0x620b, 0x5a26, 0x7c0c, 0x0b70, 0x0311, 0x5313, 0x0000,
+ 0x55eb, 0x4d00, 0x7d11, 0xc1fa, 0x57db, 0x993c, 0x68cc, 0x9989,
+ 0x680c, 0x0007, 0x0479, 0x7c02, 0x008b, 0x9990, 0x0017, 0x00a3,
+ 0x0b70, 0x0311, 0x5313, 0xc213, 0xc20a, 0x9931, 0x0b70, 0x0311,
+ 0x5313, 0x076c, 0x7c01, 0xc1d9, 0x5efb, 0x068a, 0x076b, 0x7c01,
+ 0xc1d9, 0x5ef3, 0x59db, 0x58d3, 0x018f, 0x0110, 0x390f, 0x008b,
+ 0xc13c, 0x7d2b, 0x5ac0, 0x5bc8, 0xc14e, 0x7c27, 0x0388, 0x0689,
+ 0x5ce3, 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x073e, 0x4d00, 0x7d18,
+ 0x0870, 0x0011, 0x077e, 0x7d09, 0x077d, 0x7d02, 0x5228, 0x99c1,
+ 0x52f8, 0x54db, 0x02bc, 0x02cc, 0x7c09, 0x077c, 0x7d02, 0x5228,
+ 0x99ca, 0x52f8, 0x54d3, 0x02bc, 0x02cc, 0x7d09, 0x0400, 0x99b8,
+ 0x008b, 0x52c0, 0x53c8, 0xc159, 0x7dd6, 0x0200, 0x99a8, 0x08ff,
+ 0x00bf, 0x077f, 0x7d15, 0x0488, 0x00d5, 0x7d01, 0x008d, 0x05a0,
+ 0x5deb, 0x028f, 0x0212, 0x0212, 0x3aff, 0x05da, 0x7c02, 0x073e,
+ 0x99f3, 0x02a4, 0x02dd, 0x7d02, 0x073e, 0x99f3, 0x075e, 0x99f3,
+ 0x55eb, 0x0598, 0x5deb, 0x52f3, 0x54fb, 0x076a, 0x7d26, 0x076c,
+ 0x7d01, 0x9a30, 0x076b, 0x7c57, 0x0769, 0x7d04, 0x0768, 0x7d02,
+ 0x0e01, 0x9a0a, 0x5893, 0x00d6, 0x7d01, 0x008e, 0x5593, 0x05a0,
+ 0x5d93, 0x06a0, 0x7802, 0x5502, 0x5d04, 0x7c1d, 0x4e00, 0x7c08,
+ 0x0769, 0x7d03, 0x5502, 0x7e17, 0x9a17, 0x5d04, 0x7f14, 0x0689,
+ 0x5093, 0x4800, 0x7d01, 0x9a02, 0x9a7b, 0x0015, 0x7806, 0x5502,
+ 0x5d04, 0x074f, 0x5502, 0x5d24, 0x072f, 0x7c01, 0x9a7b, 0x0017,
+ 0x076f, 0x7c01, 0x2001, 0x5593, 0x009d, 0x0007, 0xda82, 0x99d0,
+ 0x6cd3, 0x0769, 0x7d04, 0x0768, 0x7d02, 0x0e01, 0x9a3f, 0x5893,
+ 0x00d6, 0x7d01, 0x008e, 0x5593, 0x05a0, 0x5d93, 0x06a0, 0x7802,
+ 0x5502, 0x6dc8, 0x7c0f, 0x4e00, 0x7c08, 0x0769, 0x7d03, 0x5502,
+ 0x7e09, 0x9a4c, 0x6dc8, 0x7f06, 0x0689, 0x5093, 0x4800, 0x7d01,
+ 0x9a37, 0x9a7b, 0x9a75, 0x6ac3, 0x0769, 0x7d04, 0x0768, 0x7d02,
+ 0x0e01, 0x9a62, 0x5893, 0x00d6, 0x7d01, 0x008e, 0x5593, 0x05a0,
+ 0x5d93, 0x06a0, 0x7802, 0x65c8, 0x5d04, 0x7c0f, 0x4e00, 0x7c08,
+ 0x0769, 0x7d03, 0x65c8, 0x7e09, 0x9a6f, 0x5d04, 0x7f06, 0x0689,
+ 0x5093, 0x4800, 0x7d01, 0x9a5a, 0x9a7b, 0x5593, 0x009d, 0x0007,
+ 0x6cff, 0xda82, 0x99d0, 0x0000, 0x54e3, 0x55eb, 0x4d00, 0x7c01,
+ 0x99d0, 0x99b8, 0x54e3, 0x55eb, 0x0aff, 0x0211, 0x1aff, 0x077f,
+ 0x7c02, 0x05a0, 0x9a8f, 0x009d, 0x058c, 0x05ba, 0x05a0, 0x0210,
+ 0x04ba, 0x04ad, 0x0454, 0x0006, 0xc1e3, 0x57db, 0x52f3, 0x6ac5,
+ 0x52fb, 0x6ad3, 0x008f, 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x5deb,
+ 0x0478, 0x7d03, 0x0479, 0x7d20, 0x7c25, 0x0479, 0x7c19, 0x59e3,
+ 0x56ee, 0x61c8, 0x7e2e, 0x62c8, 0x7e2c, 0x65c8, 0x7e2a, 0x0660,
+ 0x7d03, 0x0112, 0x0112, 0x9ab6, 0x0512, 0x0512, 0x0211, 0x02a9,
+ 0x02ad, 0x6ac8, 0x7f1e, 0x2003, 0x4800, 0x7ceb, 0x51e3, 0x9ad0,
+ 0x7802, 0x62c8, 0x6ac8, 0x9acf, 0x6dce, 0x0015, 0x7802, 0x62c8,
+ 0x6ac8, 0x9acf, 0x6dcf, 0x0015, 0x0015, 0x7801, 0x62d8, 0x7c09,
+ 0x6ddf, 0x7f07, 0x0000, 0x55eb, 0x4d00, 0x7d06, 0xc1fa, 0x57db,
+ 0x9a9a, 0x0007, 0x68ff, 0xc213, 0xc20a, 0x9a95, 0xc1d9, 0xc1e3,
+ 0x57db, 0x52f3, 0x047a, 0x7d02, 0x6ad7, 0x9ae7, 0x6a05, 0x008f,
+ 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x56fb, 0x0015, 0x0015, 0x0015,
+ 0x047a, 0x7d07, 0x7804, 0x5206, 0x6ac8, 0x5226, 0x6ac8, 0x7c0f,
+ 0x9b01, 0x7804, 0x5206, 0x6a0b, 0x5226, 0x6a0b, 0x7c0a, 0x6a28,
+ 0x7f08, 0x0000, 0x4d00, 0x7d07, 0xc1fa, 0x57db, 0x9ae7, 0xc273,
+ 0x9b0a, 0xc277, 0x0454, 0xc20a, 0x9ae0, 0xc1e3, 0x57db, 0x52f3,
+ 0x6ad5, 0x56fb, 0x028e, 0x1a94, 0x6ac3, 0x62c8, 0x0269, 0x7d1e,
+ 0x1e94, 0x6ee3, 0x62d0, 0x5aeb, 0x62c8, 0x0248, 0x6ed3, 0x6ac8,
+ 0x2694, 0x52eb, 0x6ad5, 0x6ee3, 0x62c8, 0x026e, 0x7d27, 0x6ac8,
+ 0x7f23, 0x2501, 0x4d00, 0x7d26, 0x028e, 0x1a98, 0x6ac3, 0x62c8,
+ 0x6ec3, 0x0260, 0x7df1, 0x62d0, 0xc27a, 0x9b52, 0x6ee3, 0x008f,
+ 0x2001, 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x62c8, 0x026e, 0x7d0e,
+ 0x6ac8, 0x7f0a, 0x2001, 0x7cf9, 0x6add, 0x7f06, 0x0000, 0x4d00,
+ 0x7d09, 0xc1fa, 0x57db, 0x9b11, 0x0007, 0x6aff, 0x62d0, 0xc27a,
+ 0x0458, 0x0454, 0x6add, 0x7ff8, 0xc20a, 0x9b0e, 0xc1d9, 0xc1e3,
+ 0x57db, 0x52f3, 0x6ad5, 0x56fb, 0x028e, 0x1a94, 0x5202, 0x0269,
+ 0x7d17, 0x1e94, 0x5206, 0x0248, 0x5a06, 0x2694, 0x5206, 0x026e,
+ 0x7d26, 0x6ac8, 0x7f22, 0x2501, 0x4d00, 0x7d27, 0x028e, 0x1a98,
+ 0x5202, 0x0260, 0x7df3, 0x6add, 0x7f18, 0x62d0, 0xc27a, 0x9b95,
+ 0x008f, 0x2001, 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x5206, 0x026e,
+ 0x7d0e, 0x6ac8, 0x7f0a, 0x2001, 0x7cf9, 0x6add, 0x7f06, 0x0000,
+ 0x4d00, 0x7d0b, 0xc1fa, 0x57db, 0x9b5b, 0x0007, 0x6aff, 0x6add,
+ 0x7ffc, 0x62d0, 0xc27a, 0x0458, 0x0454, 0x6add, 0x7ff6, 0xc20a,
+ 0x9b58
+};
+#endif
diff --git a/arch/arm/mach-mx35/sdma_script_code_v2.h b/arch/arm/mach-mx35/sdma_script_code_v2.h
new file mode 100644
index 000000000000..1bd949e6c992
--- /dev/null
+++ b/arch/arm/mach-mx35/sdma_script_code_v2.h
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 */
+
+/*!
+ * @file sdma_script_code.h
+ * @brief This file contains functions of SDMA scripts code initialization
+ *
+ * The file was generated automatically. Based on sdma scripts library.
+ *
+ * @ingroup SDMA
+ */
+/*******************************************************************************
+
+ SDMA RELEASE LABEL: "SDMA_RINGO.03.00.00"
+
+*******************************************************************************/
+
+#ifndef SDMA_SCRIPT_CODE_V2_H
+#define SDMA_SCRIPT_CODE_V2_H
+
+/*!
+* SDMA ROM scripts start addresses and sizes
+*/
+
+#define start_ADDR_V2 0
+#define start_SIZE_V2 24
+
+#define core_ADDR_V2 80
+#define core_SIZE_V2 233
+
+#define common_ADDR_V2 313
+#define common_SIZE_V2 416
+
+#define ap_2_ap_ADDR_V2 729
+#define ap_2_ap_SIZE_V2 41
+
+#define app_2_mcu_ADDR_V2 770
+#define app_2_mcu_SIZE_V2 64
+
+#define mcu_2_app_ADDR_V2 834
+#define mcu_2_app_SIZE_V2 70
+
+#define uart_2_mcu_ADDR_V2 904
+#define uart_2_mcu_SIZE_V2 75
+
+#define shp_2_mcu_ADDR_V2 979
+#define shp_2_mcu_SIZE_V2 69
+
+#define mcu_2_shp_ADDR_V2 1048
+#define mcu_2_shp_SIZE_V2 72
+
+#define per_2_shp_ADDR_V2 1120
+#define per_2_shp_SIZE_V2 78
+
+#define shp_2_per_ADDR_V2 1198
+#define shp_2_per_SIZE_V2 72
+
+#define uartsh_2_mcu_ADDR_V2 1270
+#define uartsh_2_mcu_SIZE_V2 69
+
+#define mcu_2_ata_ADDR_V2 1339
+#define mcu_2_ata_SIZE_V2 90
+
+#define ata_2_mcu_ADDR_V2 1429
+#define ata_2_mcu_SIZE_V2 102
+
+#define app_2_per_ADDR_V2 1531
+#define app_2_per_SIZE_V2 66
+
+#define per_2_app_ADDR_V2 1597
+#define per_2_app_SIZE_V2 74
+
+#define loop_DMAs_routines_ADDR_V2 1671
+#define loop_DMAs_routines_SIZE_V2 240
+
+#define test_ADDR_V2 1911
+#define test_SIZE_V2 63
+
+#define signature_ADDR_V2 1022
+#define signature_SIZE_V2 1
+
+/*!
+* SDMA RAM scripts start addresses and sizes
+*/
+
+#define asrc__mcu_ADDR_V2 6144
+#define asrc__mcu_SIZE_V2 116
+
+#define ext_mem__ipu_ram_ADDR_V2 6260
+#define ext_mem__ipu_ram_SIZE_V2 123
+
+#define mcu_2_spdif_ADDR_V2 6383
+#define mcu_2_spdif_SIZE_V2 103
+
+#define p_2_p_ADDR_V2 6486
+#define p_2_p_SIZE_V2 260
+
+#define spdif_2_mcu_ADDR_V2 6746
+#define spdif_2_mcu_SIZE_V2 47
+
+#define uart_2_per_ADDR_V2 6793
+#define uart_2_per_SIZE_V2 73
+
+#define uartsh_2_per_ADDR_V2 6866
+#define uartsh_2_per_SIZE_V2 67
+
+/*!
+* SDMA RAM image start address and size
+*/
+
+#define RAM_CODE_START_ADDR_V2 6144
+#define RAM_CODE_SIZE_V2 789
+
+/*!
+* Buffer that holds the SDMA RAM image
+*/
+
+static const short sdma_code_v2[] = {
+ 0xc230, 0xc23a, 0x56f3, 0x57db, 0x047a, 0x7d07, 0x072f, 0x076e,
+ 0x7d02, 0x6ec7, 0x9813, 0x6ed7, 0x9813, 0x074f, 0x076e, 0x7d02,
+ 0x6e01, 0x9813, 0x6e05, 0x5ce3, 0x048f, 0x0410, 0x3c0f, 0x5c93,
+ 0x0e03, 0x0611, 0x1eff, 0x06bf, 0x06d5, 0x7d01, 0x068d, 0x05a6,
+ 0x5deb, 0x55fb, 0x008e, 0x076a, 0x7d02, 0x076b, 0x7c04, 0x06d4,
+ 0x7d01, 0x008c, 0x04a0, 0x06a0, 0x076f, 0x7d0c, 0x076e, 0x7d05,
+ 0x7802, 0x62c8, 0x5a05, 0x7c2b, 0x9847, 0x7802, 0x5205, 0x6ac8,
+ 0x7c26, 0x9847, 0x076e, 0x7d05, 0x7802, 0x620b, 0x5a05, 0x7c21,
+ 0x9847, 0x7802, 0x5205, 0x6a0b, 0x7c1c, 0x6a28, 0x7f1a, 0x076a,
+ 0x7d02, 0x076b, 0x7c0a, 0x4c00, 0x7c08, 0x076a, 0x7d03, 0x5a05,
+ 0x7f11, 0x9854, 0x5205, 0x7e0e, 0x5493, 0x4e00, 0x7ccb, 0x0000,
+ 0x54e3, 0x55eb, 0x4d00, 0x7d0a, 0xc251, 0x57db, 0x9814, 0x68cc,
+ 0x9862, 0x680c, 0x009e, 0x0007, 0x54e3, 0xd868, 0xc261, 0x9802,
+ 0x55eb, 0x009d, 0x058c, 0x0aff, 0x0211, 0x1aff, 0x05ba, 0x05a0,
+ 0x04b2, 0x04ad, 0x0454, 0x0006, 0x0e70, 0x0611, 0x5616, 0xc18a,
+ 0x7d2a, 0x5ade, 0x008e, 0xc19c, 0x7c26, 0x5be0, 0x5ef0, 0x5ce8,
+ 0x0688, 0x08ff, 0x0011, 0x28ff, 0x00bc, 0x53f6, 0x05df, 0x7d0b,
+ 0x6dc5, 0x03df, 0x7d03, 0x6bd5, 0xd8c3, 0x989f, 0x6b05, 0xc6e7,
+ 0x7e27, 0x7f29, 0x989f, 0x6d01, 0x03df, 0x7d05, 0x6bd5, 0xc711,
+ 0x7e18, 0x7f1a, 0x989f, 0x6b05, 0xc687, 0x7e07, 0x7f06, 0x52de,
+ 0x53e6, 0xc1a8, 0x7dd7, 0x0200, 0x9877, 0x0007, 0x6004, 0x680c,
+ 0x53f6, 0x028e, 0x00a3, 0xc2ad, 0x048b, 0x0498, 0x0454, 0x068a,
+ 0x989f, 0x0207, 0x680c, 0x6ddf, 0x0107, 0x68ff, 0x60d0, 0x98a8,
+ 0x0207, 0x68ff, 0x6d28, 0x0107, 0x6004, 0x680c, 0x98a8, 0x0007,
+ 0x68ff, 0x60d0, 0x98a8, 0x0288, 0x03a5, 0x3b03, 0x3d03, 0x4d00,
+ 0x7d0a, 0x0804, 0x00a5, 0x00da, 0x7d1a, 0x02a0, 0x7b01, 0x65d8,
+ 0x7eee, 0x65ff, 0x7eec, 0x0804, 0x02d0, 0x7d11, 0x4b00, 0x7c0f,
+ 0x008a, 0x3003, 0x6dcf, 0x6bdf, 0x0015, 0x0015, 0x7b02, 0x65d8,
+ 0x0000, 0x7edd, 0x63ff, 0x7edb, 0x3a03, 0x6dcd, 0x6bdd, 0x008a,
+ 0x7b02, 0x65d8, 0x0000, 0x7ed3, 0x65ff, 0x7ed1, 0x0006, 0xc230,
+ 0xc23a, 0x57db, 0x52f3, 0x047a, 0x7d06, 0x0479, 0x7c02, 0x6ac6,
+ 0x98fc, 0x6ac7, 0x98fc, 0x6a01, 0x008f, 0x00d5, 0x7d01, 0x008d,
+ 0x05a0, 0x5deb, 0x56fb, 0x0478, 0x7d4e, 0x0479, 0x7c1f, 0x0015,
+ 0x0388, 0x047a, 0x7d03, 0x62c8, 0x7e39, 0x9910, 0x620a, 0x7e38,
+ 0x0808, 0x7801, 0x0217, 0x5a06, 0x7f34, 0x2301, 0x047a, 0x7d03,
+ 0x62c8, 0x7e2c, 0x991d, 0x620a, 0x7e2b, 0x0808, 0x7801, 0x0217,
+ 0x5a26, 0x7f27, 0x2301, 0x4b00, 0x7ce4, 0x993c, 0x0015, 0x0015,
+ 0x0015, 0x047a, 0x7d09, 0x7806, 0x0b00, 0x62c8, 0x5a06, 0x0b01,
+ 0x62c8, 0x5a26, 0x7c13, 0x993c, 0x7806, 0x0b00, 0x620b, 0x5a06,
+ 0x0b01, 0x620b, 0x5a26, 0x7c0c, 0x0b70, 0x0311, 0x5313, 0x0000,
+ 0x55eb, 0x4d00, 0x7d11, 0xc251, 0x57db, 0x98fc, 0x68cc, 0x9949,
+ 0x680c, 0x0007, 0x0479, 0x7c02, 0x008b, 0x9950, 0x0017, 0x00a3,
+ 0x0b70, 0x0311, 0x5313, 0xc26a, 0xc261, 0x98f1, 0x0b70, 0x0311,
+ 0x5313, 0x076c, 0x7c01, 0xc230, 0x5efb, 0x068a, 0x076b, 0x7c01,
+ 0xc230, 0x5ef3, 0x59db, 0x58d3, 0x018f, 0x0110, 0x390f, 0x008b,
+ 0xc18a, 0x7d2b, 0x5ac0, 0x5bc8, 0xc19c, 0x7c27, 0x0388, 0x0689,
+ 0x5ce3, 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x073e, 0x4d00, 0x7d18,
+ 0x0870, 0x0011, 0x077e, 0x7d09, 0x077d, 0x7d02, 0x5228, 0x9981,
+ 0x52f8, 0x54db, 0x02bc, 0x02cc, 0x7c09, 0x077c, 0x7d02, 0x5228,
+ 0x998a, 0x52f8, 0x54d3, 0x02bc, 0x02cc, 0x7d09, 0x0400, 0x9978,
+ 0x008b, 0x52c0, 0x53c8, 0xc1a8, 0x7dd6, 0x0200, 0x9968, 0x08ff,
+ 0x00bf, 0x077f, 0x7d1b, 0x0488, 0x00d5, 0x7d01, 0x008d, 0x05a0,
+ 0x5deb, 0x028f, 0x32ff, 0x0210, 0x32ff, 0x0210, 0x0212, 0x0217,
+ 0x0217, 0x32ff, 0x0212, 0x05da, 0x7c02, 0x073e, 0x99b9, 0x02a4,
+ 0x02dd, 0x7d02, 0x073e, 0x99b9, 0x075e, 0x99b9, 0x55eb, 0x0598,
+ 0x5deb, 0x52f3, 0x54fb, 0x076a, 0x7d26, 0x076c, 0x7d01, 0x99f6,
+ 0x076b, 0x7c57, 0x0769, 0x7d04, 0x0768, 0x7d02, 0x0e01, 0x99d0,
+ 0x5893, 0x00d6, 0x7d01, 0x008e, 0x5593, 0x05a0, 0x5d93, 0x06a0,
+ 0x7802, 0x5502, 0x5d04, 0x7c1d, 0x4e00, 0x7c08, 0x0769, 0x7d03,
+ 0x5502, 0x7e17, 0x99dd, 0x5d04, 0x7f14, 0x0689, 0x5093, 0x4800,
+ 0x7d01, 0x99c8, 0x9a41, 0x0015, 0x7806, 0x5502, 0x5d04, 0x074d,
+ 0x5502, 0x5d24, 0x072d, 0x7c01, 0x9a41, 0x0017, 0x076d, 0x7c01,
+ 0x2001, 0x5593, 0x009d, 0x0007, 0xda48, 0x9990, 0x6cd3, 0x0769,
+ 0x7d04, 0x0768, 0x7d02, 0x0e01, 0x9a05, 0x5893, 0x00d6, 0x7d01,
+ 0x008e, 0x5593, 0x05a0, 0x5d93, 0x06a0, 0x7802, 0x5502, 0x6dc8,
+ 0x7c0f, 0x4e00, 0x7c08, 0x0769, 0x7d03, 0x5502, 0x7e09, 0x9a12,
+ 0x6dc8, 0x7f06, 0x0689, 0x5093, 0x4800, 0x7d01, 0x99fd, 0x9a41,
+ 0x9a3b, 0x6ac3, 0x0769, 0x7d04, 0x0768, 0x7d02, 0x0e01, 0x9a28,
+ 0x5893, 0x00d6, 0x7d01, 0x008e, 0x5593, 0x05a0, 0x5d93, 0x06a0,
+ 0x7802, 0x65c8, 0x5d04, 0x7c0f, 0x4e00, 0x7c08, 0x0769, 0x7d03,
+ 0x65c8, 0x7e09, 0x9a35, 0x5d04, 0x7f06, 0x0689, 0x5093, 0x4800,
+ 0x7d01, 0x9a20, 0x9a41, 0x5593, 0x009d, 0x0007, 0x6cff, 0xda48,
+ 0x9990, 0x0000, 0x54e3, 0x55eb, 0x4d00, 0x7c01, 0x9990, 0x9978,
+ 0x54e3, 0x55eb, 0x0aff, 0x0211, 0x1aff, 0x077f, 0x7c02, 0x05a0,
+ 0x9a55, 0x009d, 0x058c, 0x05ba, 0x05a0, 0x0210, 0x04ba, 0x04ad,
+ 0x0454, 0x0006, 0xc230, 0xc23a, 0x57db, 0x52f3, 0x047a, 0x7d02,
+ 0x6ad7, 0x9a63, 0x6a05, 0x008f, 0x00d5, 0x7d01, 0x008d, 0x05a0,
+ 0x56fb, 0x0015, 0x0015, 0x0015, 0x047a, 0x7d07, 0x7804, 0x5206,
+ 0x6ac8, 0x5226, 0x6ac8, 0x7c0f, 0x9a7d, 0x7804, 0x5206, 0x6a0b,
+ 0x5226, 0x6a0b, 0x7c0a, 0x6a28, 0x7f08, 0x0000, 0x4d00, 0x7d07,
+ 0xc251, 0x57db, 0x9a63, 0xc2ca, 0x9a86, 0xc2ce, 0x0454, 0xc261,
+ 0x9a5c, 0xc23a, 0x57db, 0x52f3, 0x6ad5, 0x56fb, 0x028e, 0x1a94,
+ 0x6ac3, 0x62c8, 0x0269, 0x7d1e, 0x1e94, 0x6ee3, 0x62d0, 0x5aeb,
+ 0x62c8, 0x0248, 0x6ed3, 0x6ac8, 0x2694, 0x52eb, 0x6ad5, 0x6ee3,
+ 0x62c8, 0x026e, 0x7d27, 0x6ac8, 0x7f23, 0x2501, 0x4d00, 0x7d26,
+ 0x028e, 0x1a98, 0x6ac3, 0x62c8, 0x6ec3, 0x0260, 0x7df1, 0x62d0,
+ 0xc2d1, 0x9ace, 0x6ee3, 0x008f, 0x2001, 0x00d5, 0x7d01, 0x008d,
+ 0x05a0, 0x62c8, 0x026e, 0x7d0e, 0x6ac8, 0x7f0a, 0x2001, 0x7cf9,
+ 0x6add, 0x7f06, 0x0000, 0x4d00, 0x7d09, 0xc251, 0x57db, 0x9a8d,
+ 0x0007, 0x6aff, 0x62d0, 0xc2d1, 0x0458, 0x0454, 0x6add, 0x7ff8,
+ 0xc261, 0x9a8a, 0xc230, 0xc23a, 0x57db, 0x52f3, 0x6ad5, 0x56fb,
+ 0x028e, 0x1a94, 0x5202, 0x0269, 0x7d17, 0x1e94, 0x5206, 0x0248,
+ 0x5a06, 0x2694, 0x5206, 0x026e, 0x7d26, 0x6ac8, 0x7f22, 0x2501,
+ 0x4d00, 0x7d27, 0x028e, 0x1a98, 0x5202, 0x0260, 0x7df3, 0x6add,
+ 0x7f18, 0x62d0, 0xc2d1, 0x9b11, 0x008f, 0x2001, 0x00d5, 0x7d01,
+ 0x008d, 0x05a0, 0x5206, 0x026e, 0x7d0e, 0x6ac8, 0x7f0a, 0x2001,
+ 0x7cf9, 0x6add, 0x7f06, 0x0000, 0x4d00, 0x7d0b, 0xc251, 0x57db,
+ 0x9ad7, 0x0007, 0x6aff, 0x6add, 0x7ffc, 0x62d0, 0xc2d1, 0x0458,
+ 0x0454, 0x6add, 0x7ff6, 0xc261, 0x9ad4
+};
+#endif
diff --git a/arch/arm/mach-mx35/serial.c b/arch/arm/mach-mx35/serial.c
new file mode 100644
index 000000000000..37dafba6177e
--- /dev/null
+++ b/arch/arm/mach-mx35/serial.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @file mach-mx35/serial.c
+ *
+ * @brief This file contains the UART initiliazation.
+ *
+ * @ingroup MSL_MX35
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <mach/hardware.h>
+#include <mach/mxc_uart.h>
+#include <mach/spba.h>
+#include "serial.h"
+#include "board-mx35evb.h"
+#include "board-mx35_3stack.h"
+
+#if defined(CONFIG_SERIAL_MXC) || defined(CONFIG_SERIAL_MXC_MODULE)
+
+/*!
+ * This is an array where each element holds information about a UART port,
+ * like base address of the UART, interrupt numbers etc. This structure is
+ * passed to the serial_core.c file. Based on which UART is used, the core file
+ * passes back the appropriate port structure as an argument to the control
+ * functions.
+ */
+static uart_mxc_port mxc_ports[] = {
+ [0] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART1_BASE_ADDR),
+ .mapbase = UART1_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART1_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 0,
+ },
+ .ints_muxed = UART1_MUX_INTS,
+ .irqs = {UART1_INT2, UART1_INT3},
+ .mode = UART1_MODE,
+ .ir_mode = UART1_IR,
+ .enabled = UART1_ENABLED,
+ .hardware_flow = UART1_HW_FLOW,
+ .cts_threshold = UART1_UCR4_CTSTL,
+ .dma_enabled = UART1_DMA_ENABLE,
+ .dma_rxbuf_size = UART1_DMA_RXBUFSIZE,
+ .rx_threshold = UART1_UFCR_RXTL,
+ .tx_threshold = UART1_UFCR_TXTL,
+ .shared = UART1_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART1_TX,
+ .dma_rx_id = MXC_DMA_UART1_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+ [1] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART2_BASE_ADDR),
+ .mapbase = UART2_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART2_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 1,
+ },
+ .ints_muxed = UART2_MUX_INTS,
+ .irqs = {UART2_INT2, UART2_INT3},
+ .mode = UART2_MODE,
+ .ir_mode = UART2_IR,
+ .enabled = UART2_ENABLED,
+ .hardware_flow = UART2_HW_FLOW,
+ .cts_threshold = UART2_UCR4_CTSTL,
+ .dma_enabled = UART2_DMA_ENABLE,
+ .dma_rxbuf_size = UART2_DMA_RXBUFSIZE,
+ .rx_threshold = UART2_UFCR_RXTL,
+ .tx_threshold = UART2_UFCR_TXTL,
+ .shared = UART2_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART2_TX,
+ .dma_rx_id = MXC_DMA_UART2_RX,
+ .rxd_mux = MXC_UART_IR_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+#if UART3_ENABLED == 1
+ [2] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART3_BASE_ADDR),
+ .mapbase = UART3_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART3_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 2,
+ },
+ .ints_muxed = UART3_MUX_INTS,
+ .irqs = {UART3_INT2, UART3_INT3},
+ .mode = UART3_MODE,
+ .ir_mode = UART3_IR,
+ .enabled = UART3_ENABLED,
+ .hardware_flow = UART3_HW_FLOW,
+ .cts_threshold = UART3_UCR4_CTSTL,
+ .dma_enabled = UART3_DMA_ENABLE,
+ .dma_rxbuf_size = UART3_DMA_RXBUFSIZE,
+ .rx_threshold = UART3_UFCR_RXTL,
+ .tx_threshold = UART3_UFCR_TXTL,
+ .shared = UART3_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART3_TX,
+ .dma_rx_id = MXC_DMA_UART3_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ .ir_tx_inv = MXC_IRDA_TX_INV,
+ .ir_rx_inv = MXC_IRDA_RX_INV,
+ },
+#endif
+};
+
+static struct platform_device mxc_uart_device1 = {
+ .name = "mxcintuart",
+ .id = 0,
+ .dev = {
+ .platform_data = &mxc_ports[0],
+ },
+};
+
+static struct platform_device mxc_uart_device2 = {
+ .name = "mxcintuart",
+ .id = 1,
+ .dev = {
+ .platform_data = &mxc_ports[1],
+ },
+};
+
+#if UART3_ENABLED == 1
+static struct platform_device mxc_uart_device3 = {
+ .name = "mxcintuart",
+ .id = 2,
+ .dev = {
+ .platform_data = &mxc_ports[2],
+ },
+};
+#endif
+
+static int __init mxc_init_uart(void)
+{
+ /* Register all the MXC UART platform device structures */
+ platform_device_register(&mxc_uart_device1);
+ platform_device_register(&mxc_uart_device2);
+
+ /* Grab ownership of shared UARTs 3 and 4, only when enabled */
+#if UART3_ENABLED == 1
+#if UART3_DMA_ENABLE == 1
+ spba_take_ownership(UART3_SHARED_PERI, (SPBA_MASTER_A | SPBA_MASTER_C));
+#else
+ spba_take_ownership(UART3_SHARED_PERI, SPBA_MASTER_A);
+#endif /* UART3_DMA_ENABLE */
+ platform_device_register(&mxc_uart_device3);
+#endif /* UART3_ENABLED */
+
+ return 0;
+}
+
+#else
+static int __init mxc_init_uart(void)
+{
+ return 0;
+}
+#endif
+
+arch_initcall(mxc_init_uart);
diff --git a/arch/arm/mach-mx35/serial.h b/arch/arm/mach-mx35/serial.h
new file mode 100644
index 000000000000..467a4a437525
--- /dev/null
+++ b/arch/arm/mach-mx35/serial.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX35_SERIAL_H__
+#define __ARCH_ARM_MACH_MX35_SERIAL_H__
+
+/*!
+ * @file mach-mx35/serial.h
+ *
+ * @ingroup MSL_MX35
+ */
+#include <mach/mxc_uart.h>
+
+/* UART 1 configuration */
+/*!
+ * This option allows to choose either an interrupt-driven software controlled
+ * hardware flow control (set this option to 0) or hardware-driven hardware
+ * flow control (set this option to 1).
+ */
+#define UART1_HW_FLOW 1
+/*!
+ * This specifies the threshold at which the CTS pin is deasserted by the
+ * RXFIFO. Set this value in Decimal to anything from 0 to 32 for
+ * hardware-driven hardware flow control. Read the HW spec while specifying
+ * this value. When using interrupt-driven software controlled hardware
+ * flow control set this option to -1.
+ */
+#define UART1_UCR4_CTSTL 16
+/*!
+ * This is option to enable (set this option to 1) or disable DMA data transfer
+ */
+#define UART1_DMA_ENABLE 0
+/*!
+ * Specify the size of the DMA receive buffer. The minimum buffer size is 512
+ * bytes. The buffer size should be a multiple of 256.
+ */
+#define UART1_DMA_RXBUFSIZE 1024
+/*!
+ * Specify the MXC UART's Receive Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the RxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_RXTL 16
+/*!
+ * Specify the MXC UART's Transmit Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the TxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_TXTL 16
+/* UART 2 configuration */
+#define UART2_HW_FLOW 1
+#define UART2_UCR4_CTSTL 16
+#define UART2_DMA_ENABLE 0
+#define UART2_DMA_RXBUFSIZE 1024
+#define UART2_UFCR_RXTL 16
+#define UART2_UFCR_TXTL 16
+/* UART 3 configuration */
+#define UART3_HW_FLOW 1
+#define UART3_UCR4_CTSTL 16
+#define UART3_DMA_ENABLE 1
+#define UART3_DMA_RXBUFSIZE 1024
+#define UART3_UFCR_RXTL 16
+#define UART3_UFCR_TXTL 16
+
+/*
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+/*
+ * Is the MUXED interrupt output sent to the ARM core
+ */
+#define INTS_NOTMUXED 0
+#define INTS_MUXED 1
+/* UART 1 configuration */
+/*!
+ * This define specifies whether the muxed ANDed interrupt line or the
+ * individual interrupts from the UART port is integrated with the ARM core.
+ * There exists a define like this for each UART port. Valid values that can
+ * be used are \b INTS_NOTMUXED or \b INTS_MUXED.
+ */
+#define UART1_MUX_INTS INTS_MUXED
+/*!
+ * This define specifies the transmitter interrupt number or the interrupt
+ * number of the ANDed interrupt in case the interrupts are muxed. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_INT1 MXC_INT_UART1
+/*!
+ * This define specifies the receiver interrupt number. If the interrupts of
+ * the UART are muxed, then we specify here a dummy value -1. There exists a
+ * define like this for each UART port.
+ */
+#define UART1_INT2 -1
+/*!
+ * This specifies the master interrupt number. If the interrupts of the UART
+ * are muxed, then we specify here a dummy value of -1. There exists a define
+ * like this for each UART port.
+ */
+#define UART1_INT3 -1
+/*!
+ * This specifies if the UART is a shared peripheral. It holds the shared
+ * peripheral number if it is shared or -1 if it is not shared. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_SHARED_PERI -1
+/* UART 2 configuration */
+#define UART2_MUX_INTS INTS_MUXED
+#define UART2_INT1 MXC_INT_UART2
+#define UART2_INT2 -1
+#define UART2_INT3 -1
+#define UART2_SHARED_PERI -1
+/* UART 3 configuration */
+#define UART3_MUX_INTS INTS_MUXED
+#define UART3_INT1 MXC_INT_UART3
+#define UART3_INT2 -1
+#define UART3_INT3 -1
+#define UART3_SHARED_PERI SPBA_UART3
+
+#endif /* __ARCH_ARM_MACH_MX35_SERIAL_H__ */
diff --git a/arch/arm/mach-mx35/system.c b/arch/arm/mach-mx35/system.c
new file mode 100644
index 000000000000..22daf58817bd
--- /dev/null
+++ b/arch/arm/mach-mx35/system.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <asm/proc-fns.h>
+#include <asm/system.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+#include "crm_regs.h"
+
+/*!
+ * @defgroup MSL_MX35 i.MX35 Machine Specific Layer (MSL)
+ */
+
+/*!
+ * @file mach-mx35/system.c
+ * @brief This file contains idle and reset functions.
+ *
+ * @ingroup MSL_MX35
+ */
+
+/*!
+* MX35 low-power mode
+*/
+enum mx35_low_pwr_mode {
+ MX35_RUN_MODE,
+ MX35_WAIT_MODE,
+ MX35_DOZE_MODE,
+ MX35_STOP_MODE
+};
+
+extern int mxc_jtag_enabled;
+
+/*!
+ * This function is used to set cpu low power mode before WFI instruction
+ *
+ * @param mode indicates different kinds of power modes
+ */
+void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
+{
+ unsigned int lpm;
+ unsigned long reg;
+
+ /*read CCMR value */
+ reg = __raw_readl(MXC_CCM_CCMR);
+
+ switch (mode) {
+ case WAIT_UNCLOCKED_POWER_OFF:
+ lpm = MX35_DOZE_MODE;
+ break;
+
+ case STOP_POWER_ON:
+ case STOP_POWER_OFF:
+ lpm = MX35_STOP_MODE;
+ /* Enabled Well Bias */
+ reg |= MXC_CCM_CCMR_WBEN;
+ if (board_is_mx35(BOARD_REV_2))
+ reg |= MXC_CCM_CCMR_VSTBY;
+ break;
+
+ case WAIT_CLOCKED:
+ case WAIT_UNCLOCKED:
+ default:
+ /* Wait is the default mode used when idle. */
+ lpm = MX35_WAIT_MODE;
+ break;
+ }
+
+ /* program LPM bit */
+ reg = (reg & (~MXC_CCM_CCMR_LPM_MASK)) | lpm << MXC_CCM_CCMR_LPM_OFFSET;
+ /* program Interrupt holdoff bit */
+ reg = reg | MXC_CCM_CCMR_WFI;
+ /* TBD: PMIC has put the voltage back to Normal if the voltage ready */
+ /* counter finished */
+ reg = reg | MXC_CCM_CCMR_STBY_EXIT_SRC;
+
+ __raw_writel(reg, MXC_CCM_CCMR);
+}
+
+EXPORT_SYMBOL(mxc_cpu_lp_set);
+
+/*!
+ * This function puts the CPU into idle mode. It is called by default_idle()
+ * in process.c file.
+ */
+void arch_idle(void)
+{
+ /*
+ * This should do all the clock switching
+ * and wait for interrupt tricks.
+ */
+ if (!mxc_jtag_enabled) {
+#ifdef CONFIG_MX35_DOZE_DURING_IDLE
+ /*set as Doze mode */
+ mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
+#else
+ /* set as Wait mode */
+ mxc_cpu_lp_set(WAIT_UNCLOCKED);
+#endif
+ cpu_do_idle();
+ }
+}
+
+/*
+ * This function resets the system. It is called by machine_restart().
+ *
+ * @param mode indicates different kinds of resets
+ */
+void arch_reset(char mode)
+{
+ /* Assert SRS signal */
+ mxc_wd_reset();
+}
diff --git a/arch/arm/mach-mx35/usb.h b/arch/arm/mach-mx35/usb.h
new file mode 100644
index 000000000000..52b6f803bb50
--- /dev/null
+++ b/arch/arm/mach-mx35/usb.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+extern int usbotg_init(struct platform_device *pdev);
+extern void usbotg_uninit(struct fsl_usb2_platform_data *pdata);
+extern struct platform_device *host_pdev_register(struct resource *res,
+ int n_res, struct fsl_usb2_platform_data *config);
+
+extern int fsl_usb_host_init(struct platform_device *pdev);
+extern void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbh2_active(void);
+extern void gpio_usbh2_inactive(void);
+extern int gpio_usbotg_utmi_active(void);
+extern void gpio_usbotg_utmi_inactive(void);
+
+/*
+ * Determine which platform_data struct to use for the DR controller,
+ * based on which transceiver is configured.
+ * PDATA is a pointer to it.
+ */
+static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config;
+#define PDATA (&dr_utmi_config)
+
+
+/*
+ * Used to set pdata->operating_mode before registering the platform_device.
+ * If OTG is configured, the controller operates in OTG mode,
+ * otherwise it's either host or device.
+ */
+#ifdef CONFIG_USB_OTG
+#define DR_UDC_MODE FSL_USB2_DR_OTG
+#define DR_HOST_MODE FSL_USB2_DR_OTG
+#else
+#define DR_UDC_MODE FSL_USB2_DR_DEVICE
+#define DR_HOST_MODE FSL_USB2_DR_HOST
+#endif
+
+
+#ifdef CONFIG_USB_EHCI_ARC_OTG
+static inline void dr_register_host(struct resource *r, int rs)
+{
+ PDATA->operating_mode = DR_HOST_MODE;
+ host_pdev_register(r, rs, PDATA);
+}
+#else
+static inline void dr_register_host(struct resource *r, int rs)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_GADGET_ARC
+static struct platform_device dr_udc_device;
+
+static inline void dr_register_udc(void)
+{
+ PDATA->operating_mode = DR_UDC_MODE;
+ dr_udc_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_udc_device))
+ printk(KERN_ERR "usb: can't register DR gadget\n");
+ else
+ printk(KERN_INFO "usb: DR gadget (%s) registered\n",
+ PDATA->transceiver);
+}
+#else
+static inline void dr_register_udc(void)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_OTG
+static struct platform_device dr_otg_device;
+
+/*
+ * set the proper operating_mode and
+ * platform_data pointer, then register the
+ * device.
+ */
+static inline void dr_register_otg(void)
+{
+ PDATA->operating_mode = FSL_USB2_DR_OTG;
+ dr_otg_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_otg_device))
+ printk(KERN_ERR "usb: can't register otg device\n");
+ else
+ printk(KERN_INFO "usb: DR OTG registered\n");
+}
+#else
+static inline void dr_register_otg(void)
+{
+}
+#endif
diff --git a/arch/arm/mach-mx35/usb_dr.c b/arch/arm/mach-mx35/usb_dr.c
new file mode 100644
index 000000000000..72ba040d9a62
--- /dev/null
+++ b/arch/arm/mach-mx35/usb_dr.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+/*
+ * platform data structs
+ * - Which one to use is determined by CONFIG options in usb.h
+ * - operating_mode plugged at run time
+ */
+static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config = {
+ .name = "DR",
+ .platform_init = usbotg_init,
+ .platform_uninit = usbotg_uninit,
+ .phy_mode = FSL_USB2_PHY_UTMI_WIDE,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbotg_utmi_active,
+ .gpio_usb_inactive = gpio_usbotg_utmi_inactive,
+ .transceiver = "utmi",
+};
+
+
+/*
+ * resources
+ */
+static struct resource otg_resources[] = {
+ [0] = {
+ .start = (u32)(USB_OTGREGS_BASE),
+ .end = (u32)(USB_OTGREGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB_OTG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+static u64 dr_udc_dmamask = ~(u32) 0;
+static void dr_udc_release(struct device *dev)
+{
+}
+
+static u64 dr_otg_dmamask = ~(u32) 0;
+static void dr_otg_release(struct device *dev)
+{
+}
+
+/*
+ * platform device structs
+ * dev.platform_data field plugged at run time
+ */
+static struct platform_device __maybe_unused dr_udc_device = {
+ .name = "fsl-usb2-udc",
+ .id = -1,
+ .dev = {
+ .release = dr_udc_release,
+ .dma_mask = &dr_udc_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static struct platform_device __maybe_unused dr_otg_device = {
+ .name = "fsl-usb2-otg",
+ .id = -1,
+ .dev = {
+ .release = dr_otg_release,
+ .dma_mask = &dr_otg_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static int __init usb_dr_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ /* i.MX35 1.0 should work in INCR mode */
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 0) {
+ PDATA->change_ahb_burst = 1;
+ PDATA->ahb_burst_mode = 0;
+ }
+
+ dr_register_otg();
+ dr_register_host(otg_resources, ARRAY_SIZE(otg_resources));
+ dr_register_udc();
+
+ return 0;
+}
+
+module_init(usb_dr_init);
diff --git a/arch/arm/mach-mx35/usb_h2.c b/arch/arm/mach-mx35/usb_h2.c
new file mode 100644
index 000000000000..d78deb09bf2a
--- /dev/null
+++ b/arch/arm/mach-mx35/usb_h2.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/fsl_xcvr.h>
+
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+static struct fsl_usb2_platform_data usbh2_config = {
+ .name = "Host 2",
+ .platform_init = fsl_usb_host_init,
+ .platform_uninit = fsl_usb_host_uninit,
+ .operating_mode = FSL_USB2_MPH_HOST,
+ .phy_mode = FSL_USB2_PHY_SERIAL,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbh2_active,
+ .gpio_usb_inactive = gpio_usbh2_inactive,
+ .transceiver = "serial",
+};
+
+static struct resource usbh2_resources[] = {
+ [0] = {
+ .start = (u32) (USB_H2REGS_BASE),
+ .end = (u32) (USB_H2REGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB_HS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+static int __init usbh2_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ /* i.MX35 1.0 should work in INCR mode */
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 0) {
+ usbh2_config.change_ahb_burst = 1;
+ usbh2_config.ahb_burst_mode = 0;
+ }
+
+ host_pdev_register(usbh2_resources, ARRAY_SIZE(usbh2_resources),
+ &usbh2_config);
+ return 0;
+}
+module_init(usbh2_init);
diff --git a/arch/arm/mach-mx37/Kconfig b/arch/arm/mach-mx37/Kconfig
new file mode 100644
index 000000000000..6e4976ed41d0
--- /dev/null
+++ b/arch/arm/mach-mx37/Kconfig
@@ -0,0 +1,91 @@
+menu "MX37 Options"
+ depends on ARCH_MX37
+
+config MX37_OPTIONS
+ bool
+ default y
+ select CPU_V6
+ select ARM_ERRATA_411920
+ select CACHE_L2X0
+ select OUTER_CACHE
+ select USB_ARCH_HAS_EHCI
+ select ARCH_HAS_EVTMON
+ select MXC_TZIC
+ select ARCH_HAS_RNGC
+
+config MACH_MX37_3DS
+ bool "Support MX37 3-Stack platforms"
+ default y
+ select MFD_WM8350_CONFIG_MODE_0
+ help
+ Include support for MX37 3-Stack platform. This includes specific
+ configurations for the board and its peripherals.
+
+config MXC_SDMA_API
+ bool "Use SDMA API"
+ default y
+ help
+ This selects the Freescale MXC SDMA API.
+ If unsure, say N.
+
+menu "SDMA options"
+ depends on MXC_SDMA_API
+
+config SDMA_IRAM
+ bool "Use Internal RAM for SDMA transfer"
+ default n
+ help
+ Support Internal RAM as SDMA buffer or control structures
+
+config SDMA_IRAM_SIZE
+ hex "Reserved bytes of IRAM for SDMA (0x800-0x1000)"
+ range 0x800 0x1000
+ depends on SDMA_IRAM
+ default "0x1000"
+ help
+ Set the size of IRAM for SDMA. It must be a multiple of 512bytes.
+endmenu
+
+config ARCH_MXC_HAS_NFC_V3
+ bool "MXC NFC Hardware Version 3"
+ depends on ARCH_MX37
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 3
+ If unsure, say N.
+
+config ARCH_MXC_HAS_NFC_V3_1
+ bool "MXC NFC Hardware Version 3.1"
+ depends on ARCH_MXC_HAS_NFC_V3
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 3.1
+ If unsure, say N.
+
+menu "Device options"
+
+config I2C_MXC_SELECT1
+ bool "Enable I2C1 module"
+ default y
+ depends on I2C_MXC
+ help
+ Enable MX37 I2C1 module.
+
+config I2C_MXC_SELECT2
+ bool "Enable I2C2 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX37 I2C2 module.
+
+config I2C_MXC_SELECT3
+ bool "Enable I2C3 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX37 I2C3 module.
+
+endmenu
+
+endmenu
+
diff --git a/arch/arm/mach-mx37/Makefile b/arch/arm/mach-mx37/Makefile
new file mode 100644
index 000000000000..5a484de73ebd
--- /dev/null
+++ b/arch/arm/mach-mx37/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-y := system.o iomux.o cpu.o mm.o clock.o devices.o serial.o dma.o dptc.o bus_freq.o
+
+
+obj-$(CONFIG_MACH_MX37_3DS) += mx37_3stack.o mx37_3stack_gpio.o
+obj-$(CONFIG_SPI_MXC) += mx37_3stack_cpld.o
+obj-$(CONFIG_REGULATOR_WM8350) += mx37_3stack_pmic_wm8350.o
+
+# power management
+obj-$(CONFIG_PM) += pm.o
+
+ifneq ($(strip $(CONFIG_USB_GADGET_ARC) $(CONFIG_USB_EHCI_ARC_OTG)),)
+ obj-y += usb_dr.o
+endif
diff --git a/arch/arm/mach-mx37/Makefile.boot b/arch/arm/mach-mx37/Makefile.boot
new file mode 100644
index 000000000000..1568ad404d59
--- /dev/null
+++ b/arch/arm/mach-mx37/Makefile.boot
@@ -0,0 +1,3 @@
+ zreladdr-y := 0x40008000
+params_phys-y := 0x40000100
+initrd_phys-y := 0x40800000
diff --git a/arch/arm/mach-mx37/board-mx37_3stack.h b/arch/arm/mach-mx37/board-mx37_3stack.h
new file mode 100644
index 000000000000..82eee4d47476
--- /dev/null
+++ b/arch/arm/mach-mx37/board-mx37_3stack.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_BOARD_MX37_3STACK_H__
+#define __ASM_ARCH_MXC_BOARD_MX37_3STACK_H__
+
+/*!
+ * @defgroup BRDCFG_MX37 Board Configuration Options
+ * @ingroup MSL_MX37
+ */
+
+/*!
+ * @file mach-mx37/board-mx37_3stack.h
+ *
+ * @brief This file contains all the board level configuration options.
+ *
+ * It currently hold the options defined for MX31 ADS Platform.
+ *
+ * @ingroup BRDCFG_MX37
+ */
+
+/*
+ * Include Files
+ */
+#include <mach/mxc_uart.h>
+#include <mach/mxc_dptc.h>
+
+/*!
+ * @name MXC UART EVB board level configurations
+ */
+/*! @{ */
+/*!
+ * Specifies if the Irda transmit path is inverting
+ */
+#define MXC_IRDA_TX_INV 0
+/*!
+ * Specifies if the Irda receive path is inverting
+ */
+#define MXC_IRDA_RX_INV 0
+
+/* UART 1 configuration */
+/*!
+ * This define specifies if the UART port is configured to be in DTE or
+ * DCE mode. There exists a define like this for each UART port. Valid
+ * values that can be used are \b MODE_DTE or \b MODE_DCE.
+ */
+#define UART1_MODE MODE_DCE
+/*!
+ * This define specifies if the UART is to be used for IRDA. There exists a
+ * define like this for each UART port. Valid values that can be used are
+ * \b IRDA or \b NO_IRDA.
+ */
+#define UART1_IR NO_IRDA
+/*!
+ * This define is used to enable or disable a particular UART port. If
+ * disabled, the UART will not be registered in the file system and the user
+ * will not be able to access it. There exists a define like this for each UART
+ * port. Specify a value of 1 to enable the UART and 0 to disable it.
+ */
+#define UART1_ENABLED 1
+/*! @} */
+/* UART 2 configuration */
+#define UART2_MODE MODE_DCE
+#define UART2_IR NO_IRDA
+#define UART2_ENABLED 1
+/* UART 3 configuration */
+#define UART3_MODE MODE_DCE
+#define UART3_IR NO_IRDA
+#define UART3_ENABLED 1
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
+
+#define DEBUG_BASE_ADDRESS 0x78000000 /* Use a Dummy base address */
+/* LAN9217 ethernet base address */
+#define LAN9217_BASE_ADDR DEBUG_BASE_ADDRESS
+/* External UART */
+#define UARTA_BASE_ADDR (DEBUG_BASE_ADDRESS + 0x8000)
+#define UARTB_BASE_ADDR (DEBUG_BASE_ADDRESS + 0x10000)
+
+#define BOARD_IO_ADDR 0x20000
+/* LED switchs */
+#define LED_SWITCH_REG BOARD_IO_ADDR + 0x00
+/* buttons */
+#define SWITCH_BUTTONS_REG BOARD_IO_ADDR + 0x08
+/* status, interrupt */
+#define INTR_STATUS_REG BOARD_IO_ADDR + 0x10
+#define INTR_MASK_REG BOARD_IO_ADDR + 0x38
+#define INTR_RESET_REG BOARD_IO_ADDR + 0x20
+/* magic word for debug CPLD */
+#define MAGIC_NUMBER1_REG BOARD_IO_ADDR + 0x40
+#define MAGIC_NUMBER2_REG BOARD_IO_ADDR + 0x48
+/* CPLD code version */
+#define CPLD_CODE_VER_REG BOARD_IO_ADDR + 0x50
+
+extern unsigned int sdhc_get_card_det_status(struct device *dev);
+extern int sdhc_write_protect(struct device *dev);
+extern int sdhc_init_card_det(int id);
+extern struct tve_platform_data tve_data;
+extern struct mxc_dptc_data dptc_lp_data;
+extern struct mxc_dptc_data dptc_gp_data;
+extern struct mxc_dvfs_platform_data dvfs_core_data;
+extern char *gp_reg_id;
+extern char *lp_reg_id;
+
+extern int headphone_det_status(void);
+#endif /* __ASM_ARCH_MXC_BOARD_MX37_3STACK_H__ */
diff --git a/arch/arm/mach-mx37/bus_freq.c b/arch/arm/mach-mx37/bus_freq.c
new file mode 100644
index 000000000000..302f59a17881
--- /dev/null
+++ b/arch/arm/mach-mx37/bus_freq.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file bus_freq.c
+ *
+ * @brief A common API for the Freescale Semiconductor i.MXC CPUfreq module
+ * and DVFS CORE module.
+ *
+ * The APIs are for setting bus frequency to low or high.
+ *
+ * @ingroup PM
+ */
+
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+#include <linux/regulator/consumer.h>
+#include <mach/mxc_dvfs.h>
+
+#include "iomux.h"
+#include "crm_regs.h"
+
+#define GP_LPM_VOLTAGE 850000
+#define LP_LPM_VOLTAGE 1050000
+#define LP_LOWFREQ_VOLTAGE 1050000
+#define LP_NORMAL_VOLTAGE 1200000
+#define GP_LPAPM_FREQ 200000000
+
+DEFINE_SPINLOCK(bus_freq_lock);
+
+struct clk *main_bus_clk;
+struct clk *pll2;
+struct clk *pll1;
+struct clk *axi_a_clk;
+struct clk *axi_b_clk;
+struct clk *axi_c_clk;
+struct clk *emi_core_clk;
+struct clk *emi_intr_clk;
+struct clk *nfc_clk;
+struct clk *ahb_clk;
+struct clk *vpu_clk;
+struct clk *vpu_core_clk;
+struct clk *arm_axi_clk;
+struct clk *ddr_clk;
+struct clk *ipu_clk;
+struct clk *periph_apm_clk;
+struct clk *lp_apm;
+struct clk *cpu_clk;
+struct clk *osc;
+struct clk *uart_clk;
+struct regulator *lp_regulator;
+int low_bus_freq_mode;
+int high_bus_freq_mode;
+char *gp_reg_id = "SW1";
+char *lp_reg_id = "SW2";
+static struct cpu_wp *cpu_wp_tbl;
+static int busfreq_suspended;
+
+struct dvfs_wp dvfs_core_setpoint[] = {
+ {33, 8, 33, 10, 10, 0x08},
+ {26, 0, 33, 20, 10, 0x08},
+ {28, 8, 33, 20, 30, 0x08},
+ {26, 0, 33, 20, 10, 0x08},};
+
+int set_low_bus_freq(void)
+{
+ int ret = 0;
+ unsigned long flags;
+ int reg;
+ unsigned long lp_lpm_clk;
+
+ if (busfreq_suspended)
+ return ret;
+
+ spin_lock_irqsave(&bus_freq_lock, flags);
+
+ if (low_bus_freq_mode || (clk_get_rate(cpu_clk) != GP_LPAPM_FREQ)) {
+ spin_unlock_irqrestore(&bus_freq_lock, flags);
+ return ret;
+ }
+
+ if (clk_get_rate(cpu_clk) != GP_LPAPM_FREQ)
+ return ret;
+
+ lp_lpm_clk = clk_get_rate(periph_apm_clk) / 8;
+
+ /* Set the parent of peripheral_apm_clk to be lpapm */
+ clk_set_parent(periph_apm_clk, pll1);
+ /* Set the LP clocks */
+ clk_set_parent(main_bus_clk, periph_apm_clk);
+
+ clk_set_rate(axi_a_clk, clk_round_rate(axi_a_clk, lp_lpm_clk));
+ clk_set_rate(axi_b_clk, clk_round_rate(axi_b_clk, lp_lpm_clk));
+ clk_set_rate(axi_c_clk, clk_round_rate(axi_c_clk, lp_lpm_clk));
+ clk_set_rate(emi_core_clk, clk_round_rate(emi_core_clk, lp_lpm_clk));
+ clk_set_rate(ahb_clk, clk_round_rate(ahb_clk, lp_lpm_clk));
+ /* Set the emi_intr_clk to be at 24MHz. */
+ clk_set_rate(emi_intr_clk, clk_round_rate(emi_intr_clk, lp_lpm_clk));
+
+ low_bus_freq_mode = 1;
+ high_bus_freq_mode = 0;
+
+ spin_unlock_irqrestore(&bus_freq_lock, flags);
+
+ /* Set the voltage to 1.05V for the LP domain. */
+ ret = regulator_set_voltage(lp_regulator, 1050000, 1050000);
+ udelay(100);
+ if (ret < 0) {
+ printk(KERN_ERR "COULD NOT SET LP VOLTAGE!!!!!!\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+int set_high_bus_freq(int high_bus_freq)
+{
+ int ret = 0;
+ unsigned long flags;
+ unsigned long lp_lpm_clk;
+
+ if (!low_bus_freq_mode)
+ return ret;
+
+ /* Set the voltage to 1.25V for the LP domain. */
+ ret = regulator_set_voltage(lp_regulator, 1250000, 1250000);
+ udelay(100);
+ if (ret < 0) {
+ printk(KERN_ERR "COULD NOT SET LP VOLTAGE!!!!!!\n");
+ return ret;
+ }
+
+ spin_lock_irqsave(&bus_freq_lock, flags);
+
+ low_bus_freq_mode = 0;
+
+ /* Set the LP clocks. */
+ lp_lpm_clk = clk_get_rate(periph_apm_clk);
+ clk_set_rate(axi_a_clk, clk_round_rate(axi_a_clk, lp_lpm_clk/5));
+ clk_set_rate(axi_b_clk, clk_round_rate(axi_b_clk, lp_lpm_clk/5));
+ clk_set_rate(axi_c_clk, clk_round_rate(axi_c_clk, lp_lpm_clk/5));
+ clk_set_rate(emi_core_clk, clk_round_rate(emi_core_clk, lp_lpm_clk/5));
+ clk_set_rate(ahb_clk, clk_round_rate(ahb_clk, lp_lpm_clk/5));
+ /* Set emi_intr clock back to divide by 2. */
+ clk_set_rate(emi_intr_clk, clk_round_rate(emi_intr_clk, lp_lpm_clk/10));
+
+ /* Set the parent of main_bus_clk to be pll2 */
+ clk_set_parent(main_bus_clk, pll2);
+
+ high_bus_freq_mode = 1;
+
+ spin_unlock_irqrestore(&bus_freq_lock, flags);
+
+ return ret;
+}
+
+int low_freq_bus_used(void)
+{
+ if ((clk_get_usecount(ipu_clk) == 0)
+ && (clk_get_usecount(vpu_clk) == 0))
+ return 1;
+ else
+ return 0;
+}
+
+void setup_pll(void)
+{
+ u32 reg;
+ u32 hfsm;
+ struct cpu_wp *p;
+
+ /* Setup the DPLL registers */
+ hfsm = __raw_readl(MXC_DPLL1_BASE + MXC_PLL_DP_CTL) &
+ MXC_PLL_DP_CTL_HFSM;
+ reg = __raw_readl(MXC_DPLL1_BASE + MXC_PLL_DP_CONFIG);
+ reg &= ~MXC_PLL_DP_CONFIG_AREN;
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_CONFIG);
+
+ if (hfsm) {
+ /* Running at lower frequency, need to bump up. */
+ p = &cpu_wp_tbl[0];
+ /* PDF and MFI */
+ reg = p->pdf | p->mfi << MXC_PLL_DP_OP_MFI_OFFSET;
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_OP);
+
+ /* MFD */
+ __raw_writel(p->mfd, MXC_DPLL1_BASE + MXC_PLL_DP_MFD);
+
+ /* MFI */
+ __raw_writel(p->mfn, MXC_DPLL1_BASE + MXC_PLL_DP_MFN);
+ } else {
+ /* Running at high frequency, need to lower it. */
+ p = &cpu_wp_tbl[1];
+ /* PDF and MFI */
+ reg = p->pdf | p->mfi << MXC_PLL_DP_OP_MFI_OFFSET;
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_HFS_OP);
+
+ /* MFD */
+ __raw_writel(p->mfd, MXC_DPLL1_BASE + MXC_PLL_DP_HFS_MFD);
+
+ /* MFN */
+ __raw_writel(p->mfn, MXC_DPLL1_BASE + MXC_PLL_DP_HFS_MFN);
+ }
+ if (clk_get_usecount(pll2) != 0) {
+ /* Set the temporal frequency to be PLL2 */
+ /* Set PLL2_PODF to be 3. */
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg |= 2 << MXC_CCM_CCSR_PLL2_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CCSR);
+ /* Set the parent of STEP_CLK to be PLL2 */
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) |
+ (2 << MXC_CCM_CCSR_STEP_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CCSR);
+ } else {
+ /* Set the temporal frequency to be lp-apm */
+ /* Set the parent of STEP_CLK to be lp-apm */
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) |
+ (0 << MXC_CCM_CCSR_STEP_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CCSR);
+ }
+}
+
+static int busfreq_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ if (low_bus_freq_mode)
+ set_high_bus_freq(1);
+ busfreq_suspended = 1;
+ return 0;
+}
+
+static int busfreq_resume(struct platform_device *pdev)
+{
+ busfreq_suspended = 0;
+ return 0;
+}
+
+/*!
+ * This is the probe routine for the bus frequency driver.
+ *
+ * @param pdev The platform device structure
+ *
+ * @return The function returns 0 on success
+ *
+ */
+static int __devinit busfreq_probe(struct platform_device *pdev)
+{
+ int cpu_wp_nr;
+
+ main_bus_clk = clk_get(NULL, "main_bus_clk");
+ if (IS_ERR(main_bus_clk)) {
+ printk(KERN_DEBUG "%s: failed to get main_bus_clk\n", __func__);
+ return PTR_ERR(main_bus_clk);
+ }
+
+ pll2 = clk_get(NULL, "pll2");
+ if (IS_ERR(pll2)) {
+ printk(KERN_DEBUG "%s: failed to get pll2\n", __func__);
+ return PTR_ERR(pll2);
+ }
+
+ axi_a_clk = clk_get(NULL, "axi_a_clk");
+ if (IS_ERR(axi_a_clk)) {
+ printk(KERN_DEBUG "%s: failed to get axi_a_clk\n", __func__);
+ return PTR_ERR(axi_a_clk);
+ }
+
+ axi_b_clk = clk_get(NULL, "axi_b_clk");
+ if (IS_ERR(axi_b_clk)) {
+ printk(KERN_DEBUG "%s: failed to get axi_b_clk\n", __func__);
+ return PTR_ERR(axi_b_clk);
+ }
+
+ axi_c_clk = clk_get(NULL, "axi_c_clk");
+ if (IS_ERR(axi_c_clk)) {
+ printk(KERN_DEBUG "%s: failed to get axi_c_clk\n", __func__);
+ return PTR_ERR(axi_c_clk);
+ }
+
+ emi_core_clk = clk_get(NULL, "emi_core_clk");
+ if (IS_ERR(emi_core_clk)) {
+ printk(KERN_DEBUG "%s: failed to get emi_core_clk\n", __func__);
+ return PTR_ERR(emi_core_clk);
+ }
+
+ emi_intr_clk = clk_get(NULL, "emi_intr_clk");
+ if (IS_ERR(emi_intr_clk)) {
+ printk(KERN_DEBUG "%s: failed to get emi_intr_clk\n", __func__);
+ return PTR_ERR(emi_intr_clk);
+ }
+
+ nfc_clk = clk_get(NULL, "nfc_clk");
+ if (IS_ERR(nfc_clk)) {
+ printk(KERN_DEBUG "%s: failed to get nfc_clk\n", __func__);
+ return PTR_ERR(nfc_clk);
+ }
+
+ ahb_clk = clk_get(NULL, "ahb_clk");
+ if (IS_ERR(ahb_clk)) {
+ printk(KERN_DEBUG "%s: failed to get ahb_clk\n", __func__);
+ return PTR_ERR(ahb_clk);
+ }
+
+ vpu_core_clk = clk_get(NULL, "vpu_core_clk");
+ if (IS_ERR(vpu_core_clk)) {
+ printk(KERN_DEBUG "%s: failed to get vpu_core_clk\n", __func__);
+ return PTR_ERR(vpu_core_clk);
+ }
+
+ arm_axi_clk = clk_get(NULL, "arm_axi_clk");
+ if (IS_ERR(arm_axi_clk)) {
+ printk(KERN_DEBUG "%s: failed to get arm_axi_clk\n", __func__);
+ return PTR_ERR(arm_axi_clk);
+ }
+
+ ddr_clk = clk_get(NULL, "ddr_clk");
+ if (IS_ERR(ddr_clk)) {
+ printk(KERN_DEBUG "%s: failed to get ddr_clk\n", __func__);
+ return PTR_ERR(ddr_clk);
+ }
+
+ ipu_clk = clk_get(NULL, "ipu_clk");
+ if (IS_ERR(ipu_clk)) {
+ printk(KERN_DEBUG "%s: failed to get ipu_clk\n", __func__);
+ return PTR_ERR(ipu_clk);
+ }
+
+ vpu_clk = clk_get(NULL, "vpu_clk");
+ if (IS_ERR(vpu_clk)) {
+ printk(KERN_DEBUG "%s: failed to get vpu_clk\n", __func__);
+ return PTR_ERR(vpu_clk);
+ }
+
+ periph_apm_clk = clk_get(NULL, "periph_apm_clk");
+ if (IS_ERR(periph_apm_clk)) {
+ printk(KERN_DEBUG "%s: failed to get periph_apm_clk\n",
+ __func__);
+ return PTR_ERR(periph_apm_clk);
+ }
+
+ lp_apm = clk_get(NULL, "lp_apm");
+ if (IS_ERR(lp_apm)) {
+ printk(KERN_DEBUG "%s: failed to get lp_apm\n", __func__);
+ return PTR_ERR(lp_apm);
+ }
+
+ cpu_clk = clk_get(NULL, "cpu_clk");
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_DEBUG "%s: failed to get cpu_clk\n", __func__);
+ return PTR_ERR(cpu_clk);
+ }
+
+ osc = clk_get(NULL, "osc");
+ if (IS_ERR(osc)) {
+ printk(KERN_DEBUG "%s: failed to get osc\n", __func__);
+ return PTR_ERR(osc);
+ }
+
+ uart_clk = clk_get(NULL, "uart_clk.0");
+ if (IS_ERR(uart_clk)) {
+ printk(KERN_DEBUG "%s: failed to get uart_clk-0\n", __func__);
+ return PTR_ERR(uart_clk);
+ }
+
+ pll1 = clk_get(NULL, "pll1_sw_clk");
+ if (IS_ERR(pll1)) {
+ printk(KERN_DEBUG "%s: failed to get pll1_sw_clk\n", __func__);
+ return PTR_ERR(pll1);
+ }
+
+ lp_regulator = regulator_get(NULL, lp_reg_id);
+ if (IS_ERR(lp_regulator)) {
+ clk_put(ahb_clk);
+ printk(KERN_DEBUG "%s: failed to get lp regulator\n", __func__);
+ return PTR_ERR(lp_regulator);
+ }
+
+ low_bus_freq_mode = 0;
+ high_bus_freq_mode = 1;
+
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+
+ return 0;
+}
+
+static struct platform_driver busfreq_driver = {
+ .driver = {
+ .name = "busfreq",
+ },
+ .probe = busfreq_probe,
+ .suspend = busfreq_suspend,
+ .resume = busfreq_resume,
+};
+
+/*!
+ * Initialise the busfreq_driver.
+ *
+ * @return The function always returns 0.
+ */
+static int __init busfreq_init(void)
+{
+ if (platform_driver_register(&busfreq_driver) != 0) {
+ printk(KERN_ERR "busfreq_driver register failed\n");
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "Bus freq driver module loaded\n");
+ return 0;
+}
+
+static void __exit busfreq_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&busfreq_driver);
+
+ clk_put(main_bus_clk);
+ clk_put(pll2);
+ clk_put(axi_a_clk);
+ clk_put(axi_b_clk);
+ clk_put(axi_c_clk);
+ clk_put(emi_core_clk);
+ clk_put(emi_intr_clk);
+ clk_put(nfc_clk);
+ clk_put(ahb_clk);
+ clk_put(vpu_core_clk);
+ clk_put(arm_axi_clk);
+ clk_put(ddr_clk);
+ clk_put(ipu_clk);
+ clk_put(periph_apm_clk);
+ clk_put(lp_apm);
+ clk_put(osc);
+ clk_put(pll1);
+ clk_put(pll2);
+ regulator_put(lp_regulator);
+
+}
+
+module_init(busfreq_init);
+module_exit(busfreq_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("BusFreq driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-mx37/clock.c b/arch/arm/mach-mx37/clock.c
new file mode 100644
index 000000000000..c6fb7afb5568
--- /dev/null
+++ b/arch/arm/mach-mx37/clock.c
@@ -0,0 +1,3173 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <asm/div64.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/mxc_dptc.h>
+#include <mach/spba.h>
+#include <mach/mxc_uart.h>
+
+#include "crm_regs.h"
+#include "iomux.h"
+
+extern int mxc_jtag_enabled;
+extern int cpufreq_trig_needed;
+extern int dvfs_core_is_active;
+
+static unsigned long pll_base[] = {
+ (unsigned long)MXC_DPLL1_BASE,
+ (unsigned long)MXC_DPLL2_BASE,
+ (unsigned long)MXC_DPLL3_BASE,
+};
+
+static struct clk pll1_main_clk;
+static struct clk pll1_sw_clk;
+static struct clk pll2_sw_clk;
+static struct clk pll3_sw_clk;
+static struct clk lp_apm_clk;
+static struct clk emi_core_clk;
+static struct clk emi_fast_clk;
+static struct clk emi_slow_clk;
+static struct clk emi_intr_clk;
+static struct clk ddr_clk;
+static struct clk ipu_clk[];
+static struct clk axi_a_clk;
+static struct clk axi_b_clk;
+static struct clk axi_c_clk;
+static struct clk ahb_clk;
+
+int cpu_wp_nr;
+int lp_high_freq;
+int lp_med_freq;
+static int cpu_curr_wp;
+static struct cpu_wp *cpu_wp_tbl;
+
+extern void propagate_rate(struct clk *tclk);
+extern void board_ref_clk_rate(unsigned long *ckil, unsigned long *osc,
+ unsigned long *ckih);
+static int cpu_clk_set_wp(int wp);
+
+static int _clk_enable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg |= MXC_CCM_CCGR_CG_MASK << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+
+ return 0;
+}
+
+static void _clk_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+}
+
+static void _clk_disable_inwait(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift);
+ reg |= 1 << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+}
+
+/*
+ * For the 4-to-1 muxed input clock
+ */
+static inline u32 _get_mux(struct clk *parent, struct clk *m0,
+ struct clk *m1, struct clk *m2, struct clk *m3)
+{
+ if (parent == m0) {
+ return 0;
+ } else if (parent == m1) {
+ return 1;
+ } else if (parent == m2) {
+ return 2;
+ } else if (parent == m3) {
+ return 3;
+ } else {
+ BUG();
+ }
+ return 0;
+}
+
+static inline unsigned long _get_pll_base(struct clk *pll)
+{
+ if (pll == &pll1_main_clk) {
+ return pll_base[0];
+ } else if (pll == &pll2_sw_clk) {
+ return pll_base[1];
+ } else if (pll == &pll3_sw_clk) {
+ return pll_base[2];
+ } else {
+ BUG();
+ }
+ return 0;
+}
+
+static struct clk ckih_clk = {
+ .name = "ckih",
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk osc_clk = {
+ .name = "osc",
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ckil_clk = {
+ .name = "ckil",
+ .flags = RATE_PROPAGATES,
+};
+
+static void _fpm_recalc(struct clk *clk)
+{
+ clk->rate = ckil_clk.rate * 512;
+ if ((__raw_readl(MXC_CCM_CCR) & MXC_CCM_CCR_FPM_MULT_MASK) != 0) {
+ clk->rate *= 2;
+ }
+}
+
+static int _fpm_enable(struct clk *clk)
+{
+ u32 reg = __raw_readl(MXC_CCM_CCR);
+ reg |= MXC_CCM_CCR_FPM_EN;
+ __raw_writel(reg, MXC_CCM_CCR);
+ return 0;
+}
+
+static void _fpm_disable(struct clk *clk)
+{
+ u32 reg = __raw_readl(MXC_CCM_CCR);
+ reg &= ~MXC_CCM_CCR_FPM_EN;
+ __raw_writel(reg, MXC_CCM_CCR);
+}
+
+static struct clk fpm_clk = {
+ .name = "fpm_clk",
+ .parent = &ckil_clk,
+ .recalc = _fpm_recalc,
+ .enable = _fpm_enable,
+ .disable = _fpm_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _fpm_div2_recalc(struct clk *clk)
+{
+ clk->rate = clk->parent->rate / 2;
+}
+
+static struct clk fpm_div2_clk = {
+ .name = "fpm_div2_clk",
+ .parent = &fpm_clk,
+ .recalc = _fpm_div2_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_pll_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 pllbase;
+
+ long mfi, pdf, mfn, mfd = 999999;
+ s64 temp64;
+ unsigned long quad_parent_rate;
+ unsigned long pll_hfsm, dp_ctl;
+
+ pllbase = _get_pll_base(clk);
+
+ quad_parent_rate = 4*clk->parent->rate;
+ pdf = mfi = -1;
+ while (++pdf < 16 && mfi < 5)
+ mfi = rate * (pdf+1) / quad_parent_rate;
+ if (mfi > 15)
+ return -1;
+ pdf--;
+
+ temp64 = rate*(pdf+1) - quad_parent_rate*mfi;
+ do_div(temp64, quad_parent_rate/1000000);
+ mfn = (long)temp64;
+
+ dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
+ /* use dpdck0_2 */
+ __raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL);
+ pll_hfsm = dp_ctl & MXC_PLL_DP_CTL_HFSM;
+ if (pll_hfsm == 0) {
+ reg = mfi<<4 | pdf;
+ __raw_writel(reg, pllbase + MXC_PLL_DP_OP);
+ __raw_writel(mfd, pllbase + MXC_PLL_DP_MFD);
+ __raw_writel(mfn, pllbase + MXC_PLL_DP_MFN);
+ } else {
+ reg = mfi<<4 | pdf;
+ __raw_writel(reg, pllbase + MXC_PLL_DP_HFS_OP);
+ __raw_writel(mfd, pllbase + MXC_PLL_DP_HFS_MFD);
+ __raw_writel(mfn, pllbase + MXC_PLL_DP_HFS_MFN);
+ }
+
+ return 0;
+}
+
+static void _clk_pll_recalc(struct clk *clk)
+{
+ long mfi, mfn, mfd, pdf, ref_clk, mfn_abs;
+ unsigned long dp_op, dp_mfd, dp_mfn, dp_ctl, pll_hfsm, dbl;
+ unsigned long pllbase;
+ s64 temp;
+
+ pllbase = _get_pll_base(clk);
+
+ dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
+ pll_hfsm = dp_ctl & MXC_PLL_DP_CTL_HFSM;
+ dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN;
+
+ 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_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET;
+ mfi = (mfi <= 5) ? 5 : mfi;
+ mfd = dp_mfd & MXC_PLL_DP_MFD_MASK;
+ mfn = mfn_abs = dp_mfn & MXC_PLL_DP_MFN_MASK;
+ /* Sign extend to 32-bits */
+ if (mfn >= 0x04000000) {
+ mfn |= 0xFC000000;
+ mfn_abs = -mfn;
+ }
+
+ ref_clk = 2 * clk->parent->rate;
+ if (dbl != 0) {
+ ref_clk *= 2;
+ }
+ ref_clk /= (pdf + 1);
+ temp = (u64) ref_clk *mfn_abs;
+ do_div(temp, mfd + 1);
+ if (mfn < 0)
+ temp = -temp;
+ temp = (ref_clk * mfi) + temp;
+
+ clk->rate = temp;
+}
+
+static int _clk_pll_enable(struct clk *clk)
+{
+ u32 reg;
+ u32 pllbase;
+
+ pllbase = _get_pll_base(clk);
+ reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) | MXC_PLL_DP_CTL_UPEN;
+ __raw_writel(reg, pllbase + MXC_PLL_DP_CTL);
+
+ /* Wait for lock */
+ while (!(__raw_readl(pllbase + MXC_PLL_DP_CTL) & MXC_PLL_DP_CTL_LRF)) ;
+
+ return 0;
+}
+
+static void _clk_pll_disable(struct clk *clk)
+{
+ u32 reg;
+ u32 pllbase;
+
+ pllbase = _get_pll_base(clk);
+ reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) & ~MXC_PLL_DP_CTL_UPEN;
+ __raw_writel(reg, pllbase + MXC_PLL_DP_CTL);
+}
+
+static struct clk pll1_main_clk = {
+ .name = "pll1_main_clk",
+ .parent = &osc_clk,
+ .recalc = _clk_pll_recalc,
+ .set_rate = _clk_pll_set_rate,
+ .enable = _clk_pll_enable,
+ .disable = _clk_pll_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_pll1_sw_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CCSR);
+
+ if (parent == &pll1_main_clk) {
+ reg &= ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ } else {
+ mux = _get_mux(parent, &lp_apm_clk, NULL, &pll2_sw_clk,
+ &pll3_sw_clk);
+ reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) |
+ (mux << MXC_CCM_CCSR_STEP_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CCSR);
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ }
+ __raw_writel(reg, MXC_CCM_CCSR);
+ return 0;
+}
+
+static void _clk_pll1_sw_recalc(struct clk *clk)
+{
+ u32 reg, div;
+ div = 1;
+ reg = __raw_readl(MXC_CCM_CCSR);
+
+ if (clk->parent == &pll2_sw_clk) {
+ div = ((reg & MXC_CCM_CCSR_PLL2_PODF_MASK) >>
+ MXC_CCM_CCSR_PLL2_PODF_OFFSET) + 1;
+ } else if (clk->parent == &pll3_sw_clk) {
+ div = ((reg & MXC_CCM_CCSR_PLL3_PODF_MASK) >>
+ MXC_CCM_CCSR_PLL3_PODF_OFFSET) + 1;
+ }
+ clk->rate = clk->parent->rate / div;
+}
+
+/* pll1 switch clock */
+static struct clk pll1_sw_clk = {
+ .name = "pll1_sw_clk",
+ .parent = &pll1_main_clk,
+ .set_parent = _clk_pll1_sw_set_parent,
+ .recalc = _clk_pll1_sw_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+/* same as pll2_main_clk. These two clocks should always be the same */
+static struct clk pll2_sw_clk = {
+ .name = "pll2",
+ .parent = &osc_clk,
+ .set_rate = _clk_pll_set_rate,
+ .recalc = _clk_pll_recalc,
+ .enable = _clk_pll_enable,
+ .disable = _clk_pll_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+/* same as pll3_main_clk. These two clocks should always be the same */
+static struct clk pll3_sw_clk = {
+ .name = "pll3",
+ .parent = &osc_clk,
+ .recalc = _clk_pll_recalc,
+ .enable = _clk_pll_enable,
+ .disable = _clk_pll_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_lp_apm_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ if (parent == &osc_clk) {
+ reg = __raw_readl(MXC_CCM_CCSR) & ~MXC_CCM_CCSR_LP_APM_SEL;
+ } else if (parent == &fpm_clk) {
+ reg = __raw_readl(MXC_CCM_CCSR) | MXC_CCM_CCSR_LP_APM_SEL;
+ } else {
+ return -EINVAL;
+ }
+ __raw_writel(reg, MXC_CCM_CCSR);
+
+ return 0;
+}
+
+static struct clk lp_apm_clk = {
+ .name = "lp_apm",
+ .parent = &osc_clk,
+ .set_parent = _clk_lp_apm_set_parent,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_arm_recalc(struct clk *clk)
+{
+ u32 cacrr, div;
+
+ cacrr = __raw_readl(MXC_CCM_CACRR);
+ div = (cacrr & MXC_CCM_CACRR_ARM_PODF_MASK) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 i;
+ for (i = 0; i < cpu_wp_nr; i++) {
+ if (rate == cpu_wp_tbl[i].cpu_rate)
+ break;
+ }
+ if (i > cpu_wp_nr)
+ return -EINVAL;
+ cpu_clk_set_wp(i);
+
+ return 0;
+}
+
+static unsigned long _clk_cpu_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 i;
+ u32 wp;
+
+ for (i = 0; i < cpu_wp_nr; i++) {
+ if (rate == cpu_wp_tbl[i].cpu_rate)
+ break;
+ }
+
+ if (i > cpu_wp_nr)
+ wp = 0;
+
+ return cpu_wp_tbl[wp].cpu_rate;
+}
+
+static struct clk cpu_clk = {
+ .name = "cpu_clk",
+ .parent = &pll1_sw_clk,
+ .recalc = _clk_arm_recalc,
+ .set_rate = _clk_cpu_set_rate,
+ .round_rate = _clk_cpu_round_rate,
+};
+
+static int _clk_periph_apm_set_parent(struct clk *clk,
+ struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll3_sw_clk, &lp_apm_clk, NULL);
+
+ reg = __raw_readl(MXC_CCM_CAMR) & ~MXC_CCM_CAMR_PERIPH_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CAMR_PERIPH_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CAMR);
+
+ return 0;
+}
+
+static struct clk periph_apm_clk = {
+ .name = "periph_apm_clk",
+ .parent = &pll1_sw_clk,
+ .set_parent = _clk_periph_apm_set_parent,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_main_bus_recalc(struct clk *clk)
+{
+ clk->rate = clk->parent->rate;
+}
+
+static int _clk_main_bus_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 div = 0;
+
+ clk->rate = clk->parent->rate/(div + 1);
+ return 0;
+}
+static int _clk_main_bus_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, stat;
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+ if (emi_intr_clk.usecount == 0)
+ emi_intr_clk.enable(&emi_intr_clk);
+
+ if (parent == &pll2_sw_clk) {
+ reg = __raw_readl(MXC_CCM_CBCDR6) &
+ ~MXC_CCM_CBCDR6_PERIPH_CLK_SEL;
+ } else if (parent == &periph_apm_clk) {
+ reg = __raw_readl(MXC_CCM_CBCDR6) |
+ MXC_CCM_CBCDR6_PERIPH_CLK_SEL;
+ } else {
+ return -EINVAL;
+ }
+ __raw_writel(reg, MXC_CCM_CBCDR6);
+
+ /* Set the Load-dividers bit in CCM */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_LOAD_DIVIDERS;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ do {
+ stat = __raw_readl(MXC_CCM_CCDR) & MXC_CCM_CCDR_LOAD_DIVIDERS;
+ } while (stat);
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+ if (emi_intr_clk.usecount == 0)
+ emi_intr_clk.disable(&emi_intr_clk);
+
+ return 0;
+}
+
+static struct clk main_bus_clk = {
+ .name = "main_bus_clk",
+ .parent = &pll2_sw_clk,
+ .set_parent = _clk_main_bus_set_parent,
+ .set_rate = _clk_main_bus_set_rate,
+ .recalc = _clk_main_bus_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_axi_a_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div, stat;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+
+ if (ddr_clk.parent == &axi_a_clk && emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR3);
+ reg &= ~MXC_CCM_CBCDR3_AXI_A_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR3_AXI_A_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR3);
+
+ /* Set the Load-dividers bit in CCM */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_LOAD_DIVIDERS;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ do {
+ stat = __raw_readl(MXC_CCM_CCDR) & MXC_CCM_CCDR_LOAD_DIVIDERS;
+ } while (stat);
+ clk->rate = rate;
+
+ if (ddr_clk.parent == &axi_a_clk && emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+
+ return 0;
+}
+
+static void _clk_axi_a_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR3);
+ div = ((reg & MXC_CCM_CBCDR3_AXI_A_PODF_MASK) >>
+ MXC_CCM_CBCDR3_AXI_A_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static unsigned long _clk_axi_a_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static struct clk axi_a_clk = {
+ .name = "axi_a_clk",
+ .parent = &main_bus_clk,
+ .recalc = _clk_axi_a_recalc,
+ .set_rate = _clk_axi_a_set_rate,
+ .round_rate = _clk_axi_a_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_axi_b_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div, stat;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+
+ if (ddr_clk.parent == &axi_b_clk && emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR4);
+ reg &= ~MXC_CCM_CBCDR4_AXI_B_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR4_AXI_B_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR4);
+
+ /* Set the Load-dividers bit in CCM */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_LOAD_DIVIDERS;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ do {
+ stat = __raw_readl(MXC_CCM_CCDR) & MXC_CCM_CCDR_LOAD_DIVIDERS;
+ } while (stat);
+ clk->rate = rate;
+
+ if (ddr_clk.parent == &axi_c_clk && emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+
+ return 0;
+}
+
+static void _clk_axi_b_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR4);
+ div = ((reg & MXC_CCM_CBCDR4_AXI_B_PODF_MASK) >>
+ MXC_CCM_CBCDR4_AXI_B_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static unsigned long _clk_axi_b_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static struct clk axi_b_clk = {
+ .name = "axi_b_clk",
+ .parent = &main_bus_clk,
+ .recalc = _clk_axi_b_recalc,
+ .set_rate = _clk_axi_b_set_rate,
+ .round_rate = _clk_axi_b_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_axi_c_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div, stat;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+
+ if (ddr_clk.parent == &axi_c_clk && emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR5);
+ reg &= ~MXC_CCM_CBCDR5_AXI_C_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR5_AXI_C_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR5);
+
+ /* Set the Load-dividers bit in CCM */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_LOAD_DIVIDERS;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ do {
+ stat = __raw_readl(MXC_CCM_CCDR) & MXC_CCM_CCDR_LOAD_DIVIDERS;
+ } while (stat);
+ clk->rate = rate;
+
+ if (ddr_clk.parent == &axi_c_clk && emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+
+ return 0;
+}
+
+static void _clk_axi_c_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR5);
+ div = ((reg & MXC_CCM_CBCDR5_AXI_C_PODF_MASK) >>
+ MXC_CCM_CBCDR5_AXI_C_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static unsigned long _clk_axi_c_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static struct clk axi_c_clk = {
+ .name = "axi_c_clk",
+ .parent = &main_bus_clk,
+ .recalc = _clk_axi_c_recalc,
+ .set_rate = _clk_axi_c_set_rate,
+ .round_rate = _clk_axi_c_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_ahb_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR2);
+ div = ((reg & MXC_CCM_CBCDR2_AHB_PODF_MASK) >>
+ MXC_CCM_CBCDR2_AHB_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static int _clk_ahb_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+
+ reg = __raw_readl(MXC_CCM_CBCDR2);
+ reg &= ~MXC_CCM_CBCDR2_AHB_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR2_AHB_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR2);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+static unsigned long _clk_ahb_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static struct clk ahb_clk = {
+ .name = "ahb_clk",
+ .parent = &main_bus_clk,
+ .recalc = _clk_ahb_recalc,
+ .set_rate = _clk_ahb_set_rate,
+ .round_rate = _clk_ahb_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ahb_max_clk = {
+ .name = "max_clk",
+ .parent = &ahb_clk,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG12_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable_inwait,
+};
+
+static int _clk_emi_core_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div, stat;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+ if (emi_intr_clk.usecount == 0)
+ emi_intr_clk.enable(&emi_intr_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR6);
+ reg &= ~MXC_CCM_CBCDR6_EMI_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR6_EMI_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR6);
+
+ /* Set the Load-dividers bit in CCM */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_LOAD_DIVIDERS;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ do {
+ stat = __raw_readl(MXC_CCM_CCDR) & MXC_CCM_CCDR_LOAD_DIVIDERS;
+ } while (stat);
+
+ clk->rate = rate;
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+ if (emi_intr_clk.usecount == 0)
+ emi_intr_clk.disable(&emi_intr_clk);
+
+ return 0;
+}
+
+static void _clk_emi_core_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR6);
+ div = ((reg & MXC_CCM_CBCDR6_EMI_PODF_MASK) >>
+ MXC_CCM_CBCDR6_EMI_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static int _clk_emi_core_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+ u32 stat;
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+ if (emi_intr_clk.usecount == 0)
+ emi_intr_clk.enable(&emi_intr_clk);
+
+ if ((ipu_clk[0].parent == &emi_core_clk) &&
+ (ipu_clk[0].usecount == 0))
+ ipu_clk[0].enable(&ipu_clk[0]);
+
+ reg = __raw_readl(MXC_CCM_CBCDR6);
+ if (parent == &ahb_clk) {
+ reg |= MXC_CCM_CBCDR6_EMI_CLK_SEL;
+ } else if (parent == &main_bus_clk) {
+ reg &= ~MXC_CCM_CBCDR6_EMI_CLK_SEL;
+ } else {
+ BUG();
+ }
+ __raw_writel(reg, MXC_CCM_CBCDR6);
+
+ do {
+ stat = __raw_readl(MXC_CCM_CCDR) & MXC_CCM_CCDR_LOAD_DIVIDERS;
+ } while (stat);
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+ if (emi_intr_clk.usecount == 0)
+ emi_intr_clk.disable(&emi_intr_clk);
+ if ((ipu_clk[0].parent == &emi_core_clk) &&
+ (ipu_clk[0].usecount == 0))
+ ipu_clk[0].disable(&ipu_clk[0]);
+
+ return 0;
+}
+
+static unsigned long _clk_emi_core_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static struct clk emi_core_clk = {
+ .name = "emi_core_clk",
+ .set_parent = _clk_emi_core_set_parent,
+ .recalc = _clk_emi_core_recalc,
+ .set_rate = _clk_emi_core_set_rate,
+ .round_rate = _clk_emi_core_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ahbmux1_clk = {
+ .name = "ahbmux1_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG4_OFFSET,
+ .disable = _clk_disable_inwait,
+};
+
+static struct clk ahbmux2_clk = {
+ .name = "ahbmux2_clk",
+ .id = 0,
+ .parent = &emi_core_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG7_OFFSET,
+ .disable = _clk_disable_inwait,
+};
+
+static struct clk emi_fast_clk = {
+ .name = "emi_fast_clk",
+ .parent = &emi_core_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG12_OFFSET,
+ .disable = _clk_disable_inwait,
+};
+
+static struct clk emi_slow_clk = {
+ .name = "emi_slow_clk",
+ .parent = &emi_core_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG13_OFFSET,
+ .disable = _clk_disable_inwait,
+};
+
+static int _clk_emi_intr_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div, stat;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 4))
+ return -EINVAL;
+
+ reg = __raw_readl(MXC_CCM_CBCDR7);
+ reg &= ~MXC_CCM_CBCDR7_IPG_INIT_MEM_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR7_IPG_INT_MEM_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR7);
+
+ /* Set the Load-dividers bit in CCM */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_LOAD_DIVIDERS;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ do {
+ stat = __raw_readl(MXC_CCM_CCDR) & MXC_CCM_CCDR_LOAD_DIVIDERS;
+ } while (stat);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+static void _clk_emi_intr_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR7);
+ div = ((reg & MXC_CCM_CBCDR7_IPG_INIT_MEM_PODF_MASK) >>
+ MXC_CCM_CBCDR7_IPG_INT_MEM_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static unsigned long _clk_emi_intr_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 4)
+ div = 4;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static struct clk emi_intr_clk = {
+ .name = "emi_intr_clk",
+ .parent = &emi_core_clk,
+ .secondary = &ahbmux2_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG14_OFFSET,
+ .disable = _clk_disable_inwait,
+ .recalc = _clk_emi_intr_recalc,
+ .set_rate = _clk_emi_intr_set_rate,
+ .round_rate = _clk_emi_intr_round_rate,
+};
+
+static void _clk_ipg_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR2);
+ div = ((reg & MXC_CCM_CBCDR2_IPG_PODF_MASK) >>
+ MXC_CCM_CBCDR2_IPG_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static struct clk ipg_clk = {
+ .name = "ipg_clk",
+ .parent = &ahb_clk,
+ .recalc = _clk_ipg_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_ipg_per_recalc(struct clk *clk)
+{
+ u32 reg, prediv1, prediv2, podf;
+
+ if (clk->parent == &main_bus_clk || clk->parent == &lp_apm_clk) {
+ /* the main_bus_clk is the one before the DVFS engine */
+ reg = __raw_readl(MXC_CCM_CBCDR2);
+ prediv1 = ((reg & MXC_CCM_CBCDR2_PERCLK_PRED1_MASK) >>
+ MXC_CCM_CBCDR2_PERCLK_PRED1_OFFSET) + 1;
+ prediv2 = ((reg & MXC_CCM_CBCDR2_PERCLK_PRED2_MASK) >>
+ MXC_CCM_CBCDR2_PERCLK_PRED2_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CBCDR2_PERCLK_PODF_MASK) >>
+ MXC_CCM_CBCDR2_PERCLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (prediv1 * prediv2 * podf);
+ } else if (clk->parent == &ipg_clk) {
+ clk->rate = ipg_clk.rate;
+ } else {
+ BUG();
+ }
+}
+
+static int _clk_ipg_per_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ mux = _get_mux(parent, &main_bus_clk, &lp_apm_clk, &ipg_clk, NULL);
+ if (mux == 2) {
+ reg |= MXC_CCM_CSCMR1_PERCLK_IPG_CLK_SEL;
+ } else {
+ reg &= ~MXC_CCM_CSCMR1_PERCLK_IPG_CLK_SEL;
+ if (mux == 0) {
+ reg &= ~MXC_CCM_CSCMR1_PERCLK_LP_APM_CLK_SEL;
+ } else {
+ reg |= MXC_CCM_CSCMR1_PERCLK_LP_APM_CLK_SEL;
+ }
+ }
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk ipg_perclk = {
+ .name = "ipg_perclk",
+ .parent = &ipg_clk,
+ .recalc = _clk_ipg_per_recalc,
+ .set_parent = _clk_ipg_per_set_parent,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk aips_tz1_clk = {
+ .name = "aips_tz1_clk",
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG13_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable_inwait,
+};
+
+static struct clk aips_tz2_clk = {
+ .name = "aips_tz2_clk",
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG14_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable_inwait,
+};
+
+static struct clk gpc_dvfs_clk = {
+ .name = "gpc_dvfs_clk",
+ .parent = &aips_tz2_clk,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG15_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static struct clk sdma_clk[] = {
+ {
+ .name = "sdma_ahb_clk",
+ .parent = &ahb_clk,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG0_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "sdma_ipg_clk",
+ .parent = &ipg_clk,
+#ifdef CONFIG_SDMA_IRAM
+ .secondary = &emi_intr_clk,
+#endif
+ },
+};
+
+static int _clk_tve_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+
+ if (parent == &pll3_sw_clk) {
+ reg &= ~(MXC_CCM_CSCMR1_TVE_CLK_SEL);
+ } else if (parent == &osc_clk) {
+ reg |= MXC_CCM_CSCMR1_TVE_CLK_SEL;
+ reg &= MXC_CCM_CSCMR1_TVE_EXT_CLK_SEL;
+ } else if (parent == &ckih_clk) {
+ reg |= MXC_CCM_CSCMR1_TVE_CLK_SEL;
+ reg |= MXC_CCM_CSCMR1_TVE_EXT_CLK_SEL;
+ } else {
+ BUG();
+ }
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+ return 0;
+}
+
+static void _clk_tve_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if ((reg & MXC_CCM_CSCMR1_TVE_CLK_SEL) == 0) {
+ reg = __raw_readl(MXC_CCM_CDCDR) &
+ MXC_CCM_CDCDR_TVE_CLK_PRED_MASK;
+ div = (reg >> MXC_CCM_CDCDR_TVE_CLK_PRED_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+ } else {
+ clk->rate = clk->parent->rate;
+ }
+}
+
+static unsigned long _clk_tve_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (reg & MXC_CCM_CSCMR1_TVE_CLK_SEL) {
+ return -EINVAL;
+ }
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static int _clk_tve_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (reg & MXC_CCM_CSCMR1_TVE_CLK_SEL) {
+ return -EINVAL;
+ }
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8)) {
+ return -EINVAL;
+ }
+
+ div--;
+ reg = __raw_readl(MXC_CCM_CDCDR) & ~MXC_CCM_CDCDR_TVE_CLK_PRED_MASK;
+ reg |= div << MXC_CCM_CDCDR_TVE_CLK_PRED_OFFSET;
+ __raw_writel(reg, MXC_CCM_CDCDR);
+ clk->rate = rate;
+ return 0;
+}
+
+static struct clk tve_clk = {
+ .name = "tve_clk",
+ .parent = &pll3_sw_clk,
+ .secondary = &aips_tz1_clk,
+ .set_parent = _clk_tve_set_parent,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG10_OFFSET,
+ .recalc = _clk_tve_recalc,
+ .round_rate = _clk_tve_round_rate,
+ .set_rate = _clk_tve_set_rate,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk spba_clk = {
+ .name = "spba_clk",
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG1_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static void _clk_uart_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ prediv = ((reg & MXC_CCM_CSCDR1_UART_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CSCDR1_UART_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_uart_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_UART_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_UART_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static int _clk_uart_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div, post_div = 1;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 64) || (div == 1))
+ return -EINVAL;
+
+ if (div > 8) {
+ int i = 1;
+ while ((div / (2 * i)) > 8)
+ i++;
+ post_div = i * 2;
+ div = div / post_div;
+ }
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ reg &= ~MXC_CCM_CSCDR1_UART_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CSCDR1_UART_CLK_PRED_MASK;
+ reg |= (div - 1) << MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET |
+ (post_div - 1) << MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCDR1);
+ clk->rate = rate;
+
+ return 0;
+}
+
+static unsigned long _clk_uart_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 64)
+ div = 64;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static struct clk uart_main_clk = {
+ .name = "uart_main_clk",
+ .parent = &pll2_sw_clk,
+ .secondary = &emi_fast_clk,
+ .recalc = _clk_uart_recalc,
+ .set_parent = _clk_uart_set_parent,
+ .set_rate = _clk_uart_set_rate,
+ .round_rate = _clk_uart_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk uart1_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 0,
+ .parent = &uart_main_clk,
+ .secondary = &uart1_clk[1],
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG5_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "uart_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .secondary = &aips_tz2_clk,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG4_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static struct clk uart2_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 1,
+ .parent = &uart_main_clk,
+ .secondary = &uart2_clk[1],
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG7_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "uart_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .secondary = &aips_tz2_clk,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG6_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static struct clk uart3_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 2,
+ .parent = &uart_main_clk,
+ .secondary = &uart3_clk[1],
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG9_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "uart_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .secondary = &spba_clk,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG8_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static struct clk gpt_clk[] = {
+ {
+ .name = "gpt_clk",
+ .id = 0,
+ .parent = &ipg_perclk,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG8_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ .secondary = &gpt_clk[1],
+ },
+ {
+ .name = "gpt_ipg_clk",
+ .parent = &ipg_clk,
+ .id = 0,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG7_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "gpt_32k_clk",
+ .id = 0,
+ .parent = &ckil_clk,
+ },
+};
+
+static struct clk i2c_clk[] = {
+ {
+ .name = "i2c_clk",
+ .id = 0,
+ .parent = &ipg_perclk,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG14_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "i2c_clk",
+ .id = 1,
+ .parent = &ipg_perclk,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG15_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "i2c_clk",
+ .id = 2,
+ .parent = &ipg_perclk,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG0_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static void _clk_cspi_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR2);
+ prediv = ((reg & MXC_CCM_CSCDR2_CSPI_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR2_CSPI_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CSCDR2_CSPI_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR2_CSPI_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_cspi_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_CSPI_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_CSPI_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static int _clk_cspi_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div, post_div = 1;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 512) || (div == 1))
+ return -EINVAL;
+
+ if (div > 8) {
+ int i = 1;
+ while ((div / (2 * i)) > 8)
+ i++;
+ post_div = i * 2;
+ div = div / post_div;
+ }
+
+ reg = __raw_readl(MXC_CCM_CSCDR2);
+ reg &= ~MXC_CCM_CSCDR2_CSPI_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CSCDR2_CSPI_CLK_PRED_MASK;
+ reg |= (div - 1) << MXC_CCM_CSCDR2_CSPI_CLK_PRED_OFFSET;
+ reg |= (post_div - 1) << MXC_CCM_CSCDR2_CSPI_CLK_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCDR2);
+ clk->rate = rate;
+
+ return 0;
+}
+
+static unsigned long _clk_cspi_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 512)
+ div = 8;
+ else if (div == 1)
+ div = 2;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static struct clk cspi_main_clk = {
+ .name = "cspi_main_clk",
+ .parent = &pll3_sw_clk,
+ .recalc = _clk_cspi_recalc,
+ .set_parent = _clk_cspi_set_parent,
+ .set_rate = _clk_cspi_set_rate,
+ .round_rate = _clk_cspi_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk cspi1_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 0,
+ .parent = &cspi_main_clk,
+ .secondary = &cspi1_clk[1],
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG8_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "cspi_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .secondary = &aips_tz2_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG7_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static struct clk cspi2_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 1,
+ .parent = &cspi_main_clk,
+ .secondary = &cspi2_clk[1],
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG10_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "cspi_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .secondary = &spba_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG9_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static struct clk cspi3_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 2,
+ .parent = &cspi_main_clk,
+ .secondary = &cspi3_clk[1],
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG12_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "cspi_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .secondary = &aips_tz2_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG11_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static int _clk_ssi_lp_apm_set_parent(struct clk *clk,
+ struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+
+ if (parent == &ckih_clk) {
+ reg &= ~MXC_CCM_CSCMR1_SSI_APM_CLK_SEL;
+ } else if (parent == &lp_apm_clk) {
+ reg |= MXC_CCM_CSCMR1_SSI_APM_CLK_SEL;
+ } else {
+ BUG();
+ }
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+ return 0;
+}
+
+static struct clk ssi_lp_apm_clk = {
+ .name = "ssi_lp_apm_clk",
+ .parent = &ckih_clk,
+ .set_parent = _clk_ssi_lp_apm_set_parent,
+};
+
+static void _clk_ssi1_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CS1CDR);
+ prediv = ((reg & MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK) >>
+ MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK) >>
+ MXC_CCM_CS1CDR_SSI1_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+static int _clk_ssi1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk,
+ &pll3_sw_clk, &ssi_lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_SSI1_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_SSI1_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk ssi1_clk[] = {
+ {
+ .name = "ssi_clk",
+ .id = 0,
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_ssi1_set_parent,
+ .secondary = &ssi1_clk[1],
+ .recalc = _clk_ssi1_recalc,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG8_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "ssi_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .secondary = &ssi1_clk[2],
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG7_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "ssi_dep_clk",
+ .id = 0,
+ .parent = &aips_tz2_clk,
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+ .secondary = &emi_intr_clk,
+#else
+ .secondary = &emi_fast_clk,
+#endif
+ },
+};
+
+static void _clk_ssi2_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CS2CDR);
+ prediv = ((reg & MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK) >>
+ MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK) >>
+ MXC_CCM_CS2CDR_SSI2_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_ssi2_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk,
+ &pll3_sw_clk, &ssi_lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_SSI2_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_SSI2_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk ssi2_clk[] = {
+ {
+ .name = "ssi_clk",
+ .id = 1,
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_ssi2_set_parent,
+ .secondary = &ssi2_clk[1],
+ .recalc = _clk_ssi2_recalc,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG10_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "ssi_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .secondary = &ssi2_clk[2],
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG9_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "ssi_dep_clk",
+ .id = 1,
+ .parent = &spba_clk,
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+ .secondary = &emi_intr_clk,
+#else
+ .secondary = &emi_fast_clk,
+#endif
+ },
+};
+
+static void _clk_ssi_ext1_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ clk->rate = clk->parent->rate;
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if ((reg & MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL) == 0) {
+ reg = __raw_readl(MXC_CCM_CSECDR1);
+ prediv = ((reg & MXC_CCM_CSECDR1_SSI_EXT1_CLK_PRED_MASK) >>
+ MXC_CCM_CSECDR1_SSI_EXT1_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CSECDR1_SSI_EXT1_CLK_PODF_MASK) >>
+ MXC_CCM_CSECDR1_SSI_EXT1_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (prediv * podf);
+ }
+}
+
+static int _clk_ssi_ext1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (parent == &ssi1_clk[0]) {
+ reg |= MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL;
+ } else {
+ reg &= ~MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL;
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &ssi_lp_apm_clk);
+ reg = (reg & ~MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_OFFSET);
+ }
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk ssi_ext1_clk = {
+ .name = "ssi_ext1_clk",
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_ssi_ext1_set_parent,
+ .recalc = _clk_ssi_ext1_recalc,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG11_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static void _clk_ssi_ext2_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ clk->rate = clk->parent->rate;
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if ((reg & MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL) == 0) {
+ reg = __raw_readl(MXC_CCM_CSECDR2);
+ prediv = ((reg & MXC_CCM_CSECDR2_SSI_EXT2_CLK_PRED_MASK) >>
+ MXC_CCM_CSECDR2_SSI_EXT2_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CSECDR2_SSI_EXT2_CLK_PODF_MASK) >>
+ MXC_CCM_CSECDR2_SSI_EXT2_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (prediv * podf);
+ }
+}
+
+static int _clk_ssi_ext2_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (parent == &ssi2_clk[0]) {
+ reg |= MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL;
+ } else {
+ reg &= ~MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL;
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &ssi_lp_apm_clk);
+ reg = (reg & ~MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_OFFSET);
+ }
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk ssi_ext2_clk = {
+ .name = "ssi_ext2_clk",
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_ssi_ext2_set_parent,
+ .recalc = _clk_ssi_ext2_recalc,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG12_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static struct clk iim_clk = {
+ .name = "iim_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG15_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk tmax1_clk = {
+ .name = "tmax1_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG0_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk tmax2_clk = {
+ .name = "tmax2_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG1_OFFSET,
+ .disable = _clk_disable,
+};
+
+static void _clk_usboh2_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ prediv = ((reg & MXC_CCM_CSCDR1_USBOH2_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR1_USBOH2_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CSCDR1_USBOH2_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR1_USBOH2_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_usboh2_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_USBOH2_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_USBOH2_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+/*
+ * This is USB core clock.
+ ** need access DDR/iram, TMAX
+ */
+static struct clk usb_core_clk[] = {
+ {
+ .name = "usb_ahb_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG11_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &usb_core_clk[1],
+ },
+ {
+ .name = "usb_tmax_clk",
+ .parent = &tmax1_clk,
+ .secondary = &usb_core_clk[2],
+ },
+ {
+ .name = "usb_ddr_clk",
+ .parent = &emi_fast_clk,
+#if defined CONFIG_USB_STATIC_IRAM_PPH || defined CONFIG_USB_STATIC_IRAM
+ .secondary = &usb_core_clk[3],
+#endif
+ },
+ /* iram patch, need access internal ram */
+ {
+ .name = "usb_iram_clk",
+ .parent = &emi_intr_clk,
+ },
+};
+
+/* used for connecting external PHY */
+static struct clk usboh2_clk = {
+ .name = "usboh2_clk",
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_usboh2_set_parent,
+ .recalc = _clk_usboh2_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG12_OFFSET,
+ .disable = _clk_disable,
+};
+
+static void _clk_usb_phy_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ if (clk->parent == &pll3_sw_clk) {
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ prediv = ((reg & MXC_CCM_CDCDR_USB_PHY_PRED_MASK) >>
+ MXC_CCM_CDCDR_USB_PHY_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CDCDR_USB_PHY_PODF_MASK) >>
+ MXC_CCM_CDCDR_USB_PHY_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+ } else
+ clk->rate = clk->parent->rate;
+}
+
+static int _clk_usb_phy_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (parent == &osc_clk) {
+ reg &= ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL;
+ } else if (parent == &pll3_sw_clk) {
+ reg |= MXC_CCM_CSCMR1_USB_PHY_CLK_SEL;
+ } else {
+ BUG();
+ }
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+ return 0;
+}
+
+static struct clk usb_phy_clk = {
+ .name = "usb_phy_clk",
+ .parent = &osc_clk,
+ .set_parent = _clk_usb_phy_set_parent,
+ .recalc = _clk_usb_phy_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG6_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk esdhc_dep_clks = {
+ .name = "sd_dep_clk",
+ .parent = &spba_clk,
+ .secondary = &emi_fast_clk,
+};
+
+
+static void _clk_esdhc1_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ prediv = ((reg & MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_esdhc1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) &
+ ~MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk esdhc1_clk[] = {
+ {
+ .name = "esdhc_clk",
+ .id = 0,
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_esdhc1_set_parent,
+ .recalc = _clk_esdhc1_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG14_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc1_clk[1],
+ },
+ {
+ .name = "esdhc_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG13_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc1_clk[2],
+ },
+ {
+ .name = "esdhc1_sec_clk",
+ .parent = &tmax2_clk,
+ .secondary = &esdhc_dep_clks,
+ },
+};
+
+static void _clk_esdhc2_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ prediv = ((reg & MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_esdhc2_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) &
+ ~MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk esdhc2_clk[] = {
+ {
+ .name = "esdhc_clk",
+ .id = 1,
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_esdhc2_set_parent,
+ .recalc = _clk_esdhc2_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG0_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc2_clk[1],
+ },
+ {
+ .name = "esdhc_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG15_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc2_clk[2],
+ },
+ {
+ .name = "esdhc2_sec_clk",
+ .parent = &ahb_max_clk,
+ .secondary = &esdhc_dep_clks,
+ },
+};
+
+static int _clk_esdhc3_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (parent == &esdhc1_clk[0]) {
+ reg &= ~MXC_CCM_CSCMR1_ESDHC3_CLK_SEL;
+ } else if (parent == &esdhc2_clk[0]) {
+ reg |= MXC_CCM_CSCMR1_ESDHC3_CLK_SEL;
+ } else {
+ BUG();
+ }
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk esdhc3_clk[] = {
+ {
+ .name = "esdhc_clk",
+ .id = 2,
+ .parent = &esdhc1_clk[0],
+ .set_parent = _clk_esdhc3_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG2_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc3_clk[1],
+ },
+ {
+ .name = "esdhc_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG1_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc3_clk[2],
+ },
+ {
+ .name = "esdhc3_sec_clk",
+ .parent = &ahb_max_clk,
+ .secondary = &esdhc_dep_clks,
+ },
+
+};
+
+static void _clk_nfc_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR7);
+ div = ((reg & MXC_CCM_CBCDR7_NFC_PODF_MASK) >>
+ MXC_CCM_CBCDR7_NFC_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static int _clk_nfc_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div, stat;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+ if (emi_intr_clk.usecount == 0)
+ emi_intr_clk.enable(&emi_intr_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR7);
+ reg &= ~MXC_CCM_CBCDR7_NFC_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR7_NFC_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR7);
+
+ /* Set the Load-dividers bit in CCM */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_LOAD_DIVIDERS;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ do {
+ stat = __raw_readl(MXC_CCM_CCDR) & MXC_CCM_CCDR_LOAD_DIVIDERS;
+ } while (stat);
+ clk->rate = rate;
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+ if (emi_intr_clk.usecount == 0)
+ emi_intr_clk.disable(&emi_intr_clk);
+
+ return 0;
+}
+
+static unsigned long _clk_nfc_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static struct clk nfc_clk = {
+ .name = "nfc_clk",
+ .parent = &emi_core_clk,
+ .secondary = &emi_slow_clk,
+ .recalc = _clk_nfc_recalc,
+ .set_rate = _clk_nfc_set_rate,
+ .round_rate = _clk_nfc_round_rate,
+};
+
+static int _clk_spdif_xtal_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (parent == &osc_clk) {
+ reg &= ~MXC_CCM_CSCMR1_SPDIF_CLK_SEL;
+ } else if (parent == &ckih_clk) {
+ reg |= MXC_CCM_CSCMR1_SPDIF_CLK_SEL;
+ } else {
+ BUG();
+ }
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk spdif_xtal_clk = {
+ .name = "spdif_xtal_clk",
+ .parent = &osc_clk,
+ .set_parent = _clk_spdif_xtal_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG10_OFFSET,
+ .disable = _clk_disable,
+};
+
+static int _clk_spdif0_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR2);
+ reg |= MXC_CCM_CSCMR2_SPDIF0_COM;
+ if (parent != &ssi1_clk[0]) {
+ reg &= ~MXC_CCM_CSCMR2_SPDIF0_COM;
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &spdif_xtal_clk);
+ reg = (reg & ~MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_OFFSET);
+ }
+ __raw_writel(reg, MXC_CCM_CSCMR2);
+
+ return 0;
+}
+
+static void _clk_spdif0_recalc(struct clk *clk)
+{
+ u32 reg, pred, podf;
+
+ if (clk->parent == &ssi1_clk[0]) {
+ clk->rate = clk->parent->rate;
+ } else {
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ pred = ((reg & MXC_CCM_CDCDR_SPDIF0_CLK_PRED_MASK) >>
+ MXC_CCM_CDCDR_SPDIF0_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK) >>
+ MXC_CCM_CDCDR_SPDIF0_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (pred * podf);
+ }
+}
+
+static struct clk spdif0_clk[] = {
+ {
+ .name = "spdif_clk",
+ .id = 0,
+ .parent = &pll3_sw_clk,
+ .secondary = &spdif0_clk[1],
+ .set_parent = _clk_spdif0_set_parent,
+ .recalc = _clk_spdif0_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG11_OFFSET,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "spdif_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .secondary = &spba_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG10_OFFSET,
+ .disable = _clk_disable,
+ },
+};
+
+static int _clk_spdif1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR2);
+ reg |= MXC_CCM_CSCMR2_SPDIF1_COM;
+ if (parent != &ssi2_clk[0]) {
+ reg &= ~MXC_CCM_CSCMR2_SPDIF1_COM;
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &spdif_xtal_clk);
+ reg = (reg & ~MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_OFFSET);
+ }
+ __raw_writel(reg, MXC_CCM_CSCMR2);
+
+ return 0;
+}
+
+static void _clk_spdif1_recalc(struct clk *clk)
+{
+ u32 reg, pred, podf;
+
+ if (clk->parent == &ssi2_clk[0]) {
+ clk->rate = clk->parent->rate;
+ } else {
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ pred = ((reg & MXC_CCM_CDCDR_SPDIF1_CLK_PRED_MASK) >>
+ MXC_CCM_CDCDR_SPDIF1_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CDCDR_SPDIF1_CLK_PODF_MASK) >>
+ MXC_CCM_CDCDR_SPDIF1_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (pred * podf);
+ }
+}
+
+static struct clk spdif1_clk[] = {
+ {
+ .name = "spdif_clk",
+ .id = 1,
+ .parent = &pll3_sw_clk,
+ .secondary = &spdif1_clk[1],
+ .set_parent = _clk_spdif1_set_parent,
+ .recalc = _clk_spdif1_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG12_OFFSET,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "spdif_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .secondary = &spba_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG10_OFFSET,
+ .disable = _clk_disable,
+ },
+};
+
+static int _clk_ipu_enable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_enable(clk);
+ /* Handshake with IPU when certain clock rates are changed. */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg &= ~MXC_CCM_CCDR_IPU_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+ return 0;
+}
+
+static void _clk_ipu_disable(struct clk *clk)
+{
+ u32 reg;
+ _clk_disable(clk);
+
+ /* No handshake with IPU as its not enabled. */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_IPU_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+}
+
+static int _clk_ipu_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ reg = __raw_readl(MXC_CCM_CAMR);
+ mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &axi_c_clk,
+ &emi_core_clk);
+ reg = (reg & ~MXC_CCM_CAMR_IPU_HSP_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CAMR_IPU_HSP_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CAMR);
+
+ return 0;
+}
+
+static struct clk ipu_clk[] = {
+ {
+ .name = "ipu_clk",
+ .parent = &axi_a_clk,
+ .secondary = &ipu_clk[1],
+ .set_parent = _clk_ipu_set_parent,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG15_OFFSET,
+ .enable = _clk_ipu_enable,
+ .disable = _clk_ipu_disable,
+ .flags = CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "ipu_sec_clk",
+ .parent = &emi_fast_clk,
+ .secondary = &ahbmux1_clk,
+ }
+};
+
+static int _clk_ipu_di_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ if (parent == &tve_clk) {
+ mxc_iomux_set_gpr(MUX_IPUv3D_CAMP, false, 0);
+ } else if (parent == &ckih_clk) {
+ mxc_iomux_set_gpr(MUX_IPUv3D_CAMP, true, 0);
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ reg |= MXC_CCM_CSCMR1_DI_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+ } else if (parent == &osc_clk) {
+ mxc_iomux_set_gpr(MUX_IPUv3D_CAMP, true, 0);
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ reg &= ~MXC_CCM_CSCMR1_DI_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void _clk_ipu_di_recalc(struct clk *clk)
+{
+ if (clk->parent == &tve_clk) {
+ clk->rate = clk->parent->rate / 8;
+ } else {
+ clk->rate = clk->parent->rate;
+ }
+}
+
+static struct clk ipu_di_clk = {
+ .name = "ipu_di_clk",
+ .parent = &tve_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG14_OFFSET,
+ .recalc = _clk_ipu_di_recalc,
+ .set_parent = _clk_ipu_di_set_parent,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static int _clk_ddr_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ reg = __raw_readl(MXC_CCM_CAMR);
+ mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &axi_c_clk,
+ &emi_core_clk);
+ reg = (reg & ~MXC_CCM_CAMR_DDR_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CAMR_DDR_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CAMR);
+
+ return 0;
+}
+
+static struct clk ddr_clk = {
+ .name = "ddr_clk",
+ .parent = &axi_c_clk,
+ .set_parent = _clk_ddr_set_parent,
+};
+
+static int _clk_arm_axi_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ reg = __raw_readl(MXC_CCM_CAMR);
+ mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &axi_c_clk,
+ &emi_core_clk);
+ reg = (reg & ~MXC_CCM_CAMR_ARM_AXI_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CAMR_ARM_AXI_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CAMR);
+
+ return 0;
+}
+
+static struct clk arm_axi_clk = {
+ .name = "arm_axi_clk",
+ .parent = &axi_a_clk,
+ .set_parent = _clk_arm_axi_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG1_OFFSET,
+ .disable = _clk_disable,
+};
+
+static int _clk_vpu_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ reg = __raw_readl(MXC_CCM_CAMR);
+ mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &axi_c_clk,
+ &emi_core_clk);
+ reg = (reg & ~MXC_CCM_CAMR_VPU_AXI_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CAMR_VPU_AXI_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CAMR);
+
+ return 0;
+}
+
+static int _clk_vpu_core_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ reg = __raw_readl(MXC_CCM_CAMR);
+ mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &axi_c_clk,
+ &emi_core_clk);
+ reg = (reg & ~MXC_CCM_CAMR_VPU_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CAMR_VPU_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CAMR);
+
+ return 0;
+}
+
+static struct clk vpu_clk[] = {
+ {
+ .name = "vpu_clk",
+ .parent = &axi_a_clk,
+ .set_parent = _clk_vpu_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG7_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &vpu_clk[1],
+ .flags = CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "vpu_core_clk",
+ .parent = &axi_a_clk,
+ .secondary = &vpu_clk[2],
+ .set_parent = _clk_vpu_core_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG6_OFFSET,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "vpu_emi_clk",
+ .parent = &emi_fast_clk,
+#ifdef CONFIG_MXC_VPU_IRAM
+ .secondary = &emi_intr_clk,
+#endif
+ }
+};
+
+static int _clk_lpsr_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ mux = _get_mux(parent, &ckil_clk, &fpm_clk, &fpm_div2_clk, NULL);
+ reg = (reg & ~MXC_CCM_CLPCR_LPSR_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CLPCR_LPSR_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CLPCR);
+
+ return 0;
+}
+
+static struct clk lpsr_clk = {
+ .name = "lpsr_clk",
+ .parent = &ckil_clk,
+ .set_parent = _clk_lpsr_set_parent,
+};
+
+static void _clk_pgc_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ div = (reg & MXC_CCM_CSCDR1_PGC_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR1_PGC_CLK_PODF_OFFSET;
+ div = 1 >> div;
+ clk->rate = clk->parent->rate / div;
+}
+
+static struct clk pgc_clk = {
+ .name = "pgc_clk",
+ .parent = &ipg_clk,
+ .recalc = _clk_pgc_recalc,
+};
+
+/*usb OTG clock */
+/*Notes: in mx37, usb clock get from UTMI PHY, always 60MHz*/
+
+static struct clk usb_clk = {
+ .name = "usb_clk",
+ .rate = 60000000,
+};
+
+static struct clk rtc_clk = {
+ .name = "rtc_clk",
+ .parent = &ckil_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG13_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk ata_clk = {
+ .name = "ata_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG14_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk rng_clk = {
+ .name = "rng_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG5_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk scc_clk = {
+ .name = "scc_clk",
+ .parent = &ipg_clk,
+ .secondary = &emi_fast_clk,
+};
+
+static void cko1_recalc(struct clk *clk)
+{
+ unsigned long rate;
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CCOSR);
+ reg &= MXC_CCM_CCOSR_CKOL_DIV_MASK;
+ reg = reg >> MXC_CCM_CCOSR_CKOL_DIV_OFFSET;
+ rate = clk->parent->rate;
+ clk->rate = rate / (reg + 1);
+}
+
+static int cko1_enable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CCOSR);
+ reg |= MXC_CCM_CCOSR_CKOL_EN;
+ __raw_writel(reg, MXC_CCM_CCOSR);
+ return 0;
+}
+
+static void cko1_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CCOSR);
+ reg &= ~MXC_CCM_CCOSR_CKOL_EN;
+ __raw_writel(reg, MXC_CCM_CCOSR);
+}
+
+static int cko1_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+
+ div = (clk->parent->rate/rate - 1) & 0x7;
+ reg = __raw_readl(MXC_CCM_CCOSR);
+ reg &= ~MXC_CCM_CCOSR_CKOL_DIV_MASK;
+ reg |= div << MXC_CCM_CCOSR_CKOL_DIV_OFFSET;
+ __raw_writel(reg, MXC_CCM_CCOSR);
+ return 0;
+}
+
+static unsigned long cko1_round_rate(struct clk *clk, unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ div = div < 1 ? 1 : div;
+ div = div > 8 ? 8 : div;
+ return clk->parent->rate / div;
+}
+
+static int cko1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 sel, reg;
+
+ if (parent == &cpu_clk)
+ sel = 0;
+ else if (parent == &pll1_sw_clk)
+ sel = 1;
+ else if (parent == &pll2_sw_clk)
+ sel = 2;
+ else if (parent == &pll3_sw_clk)
+ sel = 3;
+ else if (parent == &emi_core_clk)
+ sel = 4;
+ else if (parent == &nfc_clk)
+ sel = 6;
+ else if (parent == &vpu_clk[1])
+ sel = 7;
+ else if (parent == &ipu_di_clk)
+ sel = 8;
+ else if (parent == &ahb_clk)
+ sel = 11;
+ else if (parent == &ipg_clk)
+ sel = 12;
+ else if (parent == &ipg_perclk)
+ sel = 13;
+ else
+ return -EINVAL;
+
+ reg = __raw_readl(MXC_CCM_CCOSR);
+ reg &= ~MXC_CCM_CCOSR_CKOL_SEL_MASK;
+ reg |= sel << MXC_CCM_CCOSR_CKOL_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CCOSR);
+ return 0;
+}
+static struct clk cko1_clk = {
+ .name = "cko1_clk",
+ .recalc = cko1_recalc,
+ .enable = cko1_enable,
+ .disable = cko1_disable,
+ .set_rate = cko1_set_rate,
+ .round_rate = cko1_round_rate,
+ .set_parent = cko1_set_parent,
+};
+
+static struct clk *mxc_clks[] = {
+ &osc_clk,
+ &ckih_clk,
+ &ckil_clk,
+ &fpm_clk,
+ &fpm_div2_clk,
+ &pll1_main_clk,
+ &pll1_sw_clk,
+ &pll2_sw_clk,
+ &pll3_sw_clk,
+ &gpc_dvfs_clk,
+ &lp_apm_clk,
+ &cpu_clk,
+ &periph_apm_clk,
+ &main_bus_clk,
+ &axi_a_clk,
+ &axi_b_clk,
+ &axi_c_clk,
+ &ahb_clk,
+ &ahb_max_clk,
+ &aips_tz1_clk,
+ &aips_tz2_clk,
+ &ipg_clk,
+ &ipg_perclk,
+ &sdma_clk[0],
+ &sdma_clk[1],
+ &ipu_clk[0],
+ &ipu_clk[1],
+ &ipu_di_clk,
+ &tve_clk,
+ &uart_main_clk,
+ &uart1_clk[0],
+ &uart1_clk[1],
+ &uart2_clk[0],
+ &uart2_clk[1],
+ &uart3_clk[0],
+ &uart3_clk[1],
+ &spba_clk,
+ &i2c_clk[0],
+ &i2c_clk[1],
+ &i2c_clk[2],
+ &gpt_clk[0],
+ &gpt_clk[1],
+ &gpt_clk[2],
+ &cspi_main_clk,
+ &cspi1_clk[0],
+ &cspi1_clk[1],
+ &cspi2_clk[0],
+ &cspi2_clk[1],
+ &cspi3_clk[0],
+ &cspi3_clk[1],
+ &ssi_lp_apm_clk,
+ &ssi1_clk[0],
+ &ssi1_clk[1],
+ &ssi2_clk[0],
+ &ssi2_clk[1],
+ &ssi_ext1_clk,
+ &ssi_ext2_clk,
+ &iim_clk,
+ &tmax1_clk,
+ &tmax2_clk,
+ &ahbmux1_clk,
+ &ahbmux2_clk,
+ &usb_core_clk[0],
+ &usb_core_clk[1],
+ &usb_core_clk[2],
+ &usb_core_clk[3],
+ &usboh2_clk,
+ &usb_phy_clk,
+ &usb_clk,
+ &esdhc1_clk[0],
+ &esdhc1_clk[1],
+ &esdhc1_clk[2],
+ &esdhc2_clk[0],
+ &esdhc2_clk[1],
+ &esdhc2_clk[2],
+ &esdhc3_clk[0],
+ &esdhc3_clk[1],
+ &esdhc3_clk[2],
+ &esdhc_dep_clks,
+ &emi_core_clk,
+ &emi_fast_clk,
+ &emi_slow_clk,
+ &emi_intr_clk,
+ &nfc_clk,
+ &spdif_xtal_clk,
+ &spdif0_clk[0],
+ &spdif0_clk[1],
+ &spdif1_clk[0],
+ &spdif1_clk[1],
+ &ddr_clk,
+ &arm_axi_clk,
+ &vpu_clk[0],
+ &vpu_clk[1],
+ &vpu_clk[2],
+ &lpsr_clk,
+ &pgc_clk,
+ &rtc_clk,
+ &ata_clk,
+ &rng_clk,
+ &scc_clk,
+ &cko1_clk,
+};
+
+static void clk_tree_init(void)
+{
+ u32 reg, dp_ctl;
+
+ ipg_perclk.set_parent(&ipg_perclk, &lp_apm_clk);
+
+ /*
+ *Initialise the IPG PER CLK dividers to 3. IPG_PER_CLK should be at
+ * 8MHz, its derived from lp_apm.
+ */
+ reg = __raw_readl(MXC_CCM_CBCDR2);
+ reg &= ~MXC_CCM_CBCDR2_PERCLK_PRED1_MASK;
+ reg &= ~MXC_CCM_CBCDR2_PERCLK_PRED2_MASK;
+ reg &= ~MXC_CCM_CBCDR2_PERCLK_PODF_MASK;
+ reg |= (2 << MXC_CCM_CBCDR2_PERCLK_PRED1_OFFSET);
+ __raw_writel(reg, MXC_CCM_CBCDR2);
+
+ /* set pll1_main_clk parent */
+ pll1_main_clk.parent = &osc_clk;
+ dp_ctl = __raw_readl(pll_base[0] + MXC_PLL_DP_CTL);
+ if ((dp_ctl & MXC_PLL_DP_CTL_REF_CLK_SEL_MASK) == 0)
+ pll1_main_clk.parent = &fpm_clk;
+ /* set pll2_sw_clk parent */
+ pll2_sw_clk.parent = &osc_clk;
+ dp_ctl = __raw_readl(pll_base[1] + MXC_PLL_DP_CTL);
+ if ((dp_ctl & MXC_PLL_DP_CTL_REF_CLK_SEL_MASK) == 0)
+ pll2_sw_clk.parent = &fpm_clk;
+ /* set pll3_clk parent */
+ pll3_sw_clk.parent = &osc_clk;
+ dp_ctl = __raw_readl(pll_base[2] + MXC_PLL_DP_CTL);
+ if ((dp_ctl & MXC_PLL_DP_CTL_REF_CLK_SEL_MASK) == 0)
+ pll3_sw_clk.parent = &fpm_clk;
+
+ /* set emi_core_clk parent */
+ emi_core_clk.parent = &main_bus_clk;
+ reg = __raw_readl(MXC_CCM_CBCDR6);
+ if ((reg & MXC_CCM_CBCDR6_EMI_CLK_SEL) != 0) {
+ emi_core_clk.parent = &ahb_clk;
+ }
+
+ /* set ipg_perclk parent */
+ ipg_perclk.parent = &lp_apm_clk;
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if ((reg & MXC_CCM_CSCMR1_PERCLK_IPG_CLK_SEL) != 0) {
+ ipg_perclk.parent = &ipg_clk;
+ } else {
+ if ((reg & MXC_CCM_CSCMR1_PERCLK_LP_APM_CLK_SEL) == 0)
+ ipg_perclk.parent = &main_bus_clk;
+ }
+
+ /* set DDR clock parent */
+ reg = __raw_readl(MXC_CCM_CAMR) & MXC_CCM_CAMR_DDR_CLK_SEL_MASK;
+ reg >>= MXC_CCM_CAMR_DDR_CLK_SEL_OFFSET;
+ if (reg == 0) {
+ ddr_clk.parent = &axi_a_clk;
+ } else if (reg == 1) {
+ ddr_clk.parent = &axi_b_clk;
+ } else if (reg == 2) {
+ ddr_clk.parent = &axi_c_clk;
+ } else {
+ ddr_clk.parent = &emi_core_clk;
+ }
+}
+
+int __init mxc_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2)
+{
+ struct clk **clkp;
+ u32 reg;
+ int i;
+
+ /* Turn off all possible clocks */
+ if (mxc_jtag_enabled) {
+ __raw_writel((1 << MXC_CCM_CCGR0_CG0_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG1_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG2_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG12_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG13_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG7_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG14_OFFSET), MXC_CCM_CCGR0);
+ } else {
+ __raw_writel((1 << MXC_CCM_CCGR0_CG0_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG1_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG12_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG13_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG7_OFFSET) |
+ (1 << MXC_CCM_CCGR0_CG14_OFFSET), MXC_CCM_CCGR0);
+ }
+ __raw_writel(0, MXC_CCM_CCGR1);
+
+ /* TMAX clocks. */
+ reg = __raw_readl(MXC_CCM_CCGR1);
+ reg |= 1 << MXC_CCM_CCGR1_CG0_OFFSET;
+ reg |= 1 << MXC_CCM_CCGR1_CG1_OFFSET;
+ __raw_writel(reg, MXC_CCM_CCGR1);
+ __raw_writel(0, MXC_CCM_CCGR2);
+ __raw_writel(0, MXC_CCM_CCGR3);
+ __raw_writel(0, MXC_CCM_CCGR4);
+ /* Initialise the EMI clocks to be OFF when ARM is in WAIT mode. */
+ __raw_writel((1 << MXC_CCM_CCGR5_CG4_OFFSET) |
+ (1 << MXC_CCM_CCGR5_CG12_OFFSET) |
+ (1 << MXC_CCM_CCGR5_CG13_OFFSET) |
+ (1 << MXC_CCM_CCGR5_CG14_OFFSET) |
+ MXC_CCM_CCGR5_CG11_MASK, MXC_CCM_CCGR5);
+
+ ckil_clk.rate = ckil;
+ ckih_clk.rate = ckih1;
+ osc_clk.rate = osc;
+
+ clk_tree_init();
+
+ for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++)
+ clk_register(*clkp);
+
+ reg = __raw_readl(MXC_CCM_CCSR);
+ /*STEP_CLK - make sure its source is lp_apm */
+ reg &= ~MXC_CCM_CCSR_STEP_SEL_MASK;
+ __raw_writel(reg, MXC_CCM_CCSR);
+
+ /* This will propagate to all children and init all the clock rates */
+ propagate_rate(&osc_clk);
+ propagate_rate(&ckih_clk);
+ propagate_rate(&ckil_clk);
+
+ _clk_pll_disable(&pll3_sw_clk);
+
+ clk_enable(&cpu_clk);
+ clk_enable(&main_bus_clk);
+
+ /* Move UART to run from pll2_sw_clk */
+ clk_set_parent(&uart_main_clk, &pll2_sw_clk);
+
+ /* Set the UART dividers to divide by 10, so the UART_CLK is 66.5MHz. */
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ reg &= ~MXC_CCM_CSCDR1_UART_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CSCDR1_UART_CLK_PRED_MASK;
+ reg |= (4 << MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET) |
+ (1 << MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET);
+ __raw_writel(reg, MXC_CCM_CSCDR1);
+
+ /* move cspi to 24MHz */
+ clk_set_parent(&cspi_main_clk, &lp_apm_clk);
+ clk_set_rate(&cspi_main_clk, 12000000);
+
+ /*move the spdif0 to spdif_xtal_ckl */
+ clk_set_parent(&spdif0_clk[0], &spdif_xtal_clk);
+ /*set the SPDIF dividers to 1 */
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ reg &= ~MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CDCDR_SPDIF0_CLK_PRED_MASK;
+ __raw_writel(reg, MXC_CCM_CDCDR);
+
+ /* move the spdif1 to 24MHz */
+ clk_set_parent(&spdif1_clk[0], &spdif_xtal_clk);
+ /* set the spdif1 dividers to 1 */
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ reg &= ~MXC_CCM_CDCDR_SPDIF1_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CDCDR_SPDIF1_CLK_PRED_MASK;
+ __raw_writel(reg, MXC_CCM_CDCDR);
+
+ /* Move SSI clocks to SSI_LP_APM clock */
+ clk_set_parent(&ssi_lp_apm_clk, &lp_apm_clk);
+
+ clk_set_parent(&ssi1_clk[0], &ssi_lp_apm_clk);
+ /* set the SSI dividers to divide by 2 */
+ reg = __raw_readl(MXC_CCM_CS1CDR);
+ reg &= ~MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK;
+ reg |= 1 << MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET;
+ __raw_writel(reg, MXC_CCM_CS1CDR);
+
+ clk_set_parent(&ssi2_clk[0], &ssi_lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CS2CDR);
+ reg &= ~MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK;
+ reg |= 1 << MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET;
+ __raw_writel(reg, MXC_CCM_CS2CDR);
+
+ /* Change the SSI_EXT1_CLK to be sourced from SSI1_CLK_ROOT */
+ clk_set_parent(&ssi_ext1_clk, &ssi1_clk[0]);
+ clk_set_parent(&ssi_ext2_clk, &ssi2_clk[0]);
+
+ propagate_rate(&ssi_lp_apm_clk);
+
+ clk_set_parent(&arm_axi_clk, &emi_core_clk);
+
+ clk_set_parent(&ipu_clk[0], &axi_a_clk);
+ clk_set_parent(&vpu_clk[0], &axi_a_clk);
+ clk_set_parent(&vpu_clk[1], &axi_a_clk);
+
+ clk_set_rate(&emi_intr_clk, clk_round_rate(&emi_intr_clk, 66000000));
+ /* Change the NFC clock rate to be 1:3 ratio with emi clock. */
+ clk_set_rate(&nfc_clk, clk_round_rate(&nfc_clk,
+ (clk_get_rate(&emi_slow_clk))/3));
+
+ clk_set_parent(&usb_phy_clk, &osc_clk);
+
+ clk_set_parent(&periph_apm_clk, &lp_apm_clk);
+
+ clk_set_parent(&cko1_clk, &ipg_perclk);
+ clk_set_rate(&cko1_clk, 8000000);
+
+ /* Set the current working point. */
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+ for (i = 0; i < cpu_wp_nr; i++) {
+ if (clk_get_rate(&cpu_clk) == cpu_wp_tbl[i].cpu_rate) {
+ cpu_curr_wp = i;
+ break;
+ }
+ }
+ if (i > cpu_wp_nr)
+ BUG();
+
+ propagate_rate(&osc_clk);
+ propagate_rate(&pll1_sw_clk);
+ propagate_rate(&pll2_sw_clk);
+
+#ifdef DVFS_SW_WORKAROUND
+ clk_set_parent(&periph_apm_clk, &pll3_sw_clk);
+
+ clk_set_parent(&main_bus_clk, &periph_apm_clk);
+ clk_disable(&pll2_sw_clk);
+ clk_set_rate(&pll2_sw_clk, 266000000);
+ pll2_sw_clk.recalc(&pll2_sw_clk);
+ clk_enable(&pll2_sw_clk);
+ clk_set_parent(&main_bus_clk, &pll2_sw_clk);
+
+ clk_set_rate(&ahb_clk, clk_round_rate(&ahb_clk, 130000000));
+ clk_set_rate(&axi_b_clk, clk_round_rate(&axi_b_clk, 110000000));
+ clk_set_rate(&axi_c_clk, clk_round_rate(&axi_c_clk, 166000000));
+ clk_set_rate(&axi_a_clk, clk_round_rate(&axi_a_clk, 130000000));
+
+ clk_set_parent(&emi_core_clk, &ahb_clk);
+ clk_set_rate(&emi_core_clk, clk_round_rate(&emi_core_clk, 130000000));
+ clk_set_rate(&emi_intr_clk, clk_round_rate(&emi_intr_clk, 66000000));
+
+ clk_set_rate(&axi_c_clk, clk_round_rate(&axi_c_clk, 130000000));
+ clk_set_parent(&ddr_clk, &axi_c_clk);
+ /* Set the UART dividers to divide by 6, so the UART_CLK is 66.5MHz. */
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ reg &= ~MXC_CCM_CSCDR1_UART_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CSCDR1_UART_CLK_PRED_MASK;
+ reg |= (3 << MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET) |
+ (0 << MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET);
+ __raw_writel(reg, MXC_CCM_CSCDR1);
+#endif
+
+ return 0;
+}
+
+/*!
+ * Setup cpu clock based on working point.
+ * @param wp cpu freq working point
+ * @return 0 on success or error code on failure.
+ */
+static int cpu_clk_set_wp(int wp)
+{
+ struct cpu_wp *p;
+ u32 reg;
+ u32 stat;
+
+ if (wp == cpu_curr_wp)
+ return 0;
+
+ p = &cpu_wp_tbl[wp];
+
+ if (!dvfs_core_is_active) {
+ /* Change the ARM clock to requested frequency */
+ /* First move the ARM clock to step clock */
+ /* which is running at 24MHz. */
+
+ /* Change the source of pll1_sw_clk to be the step_clk */
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+
+ /* Stop the PLL */
+ reg = __raw_readl(MXC_DPLL1_BASE + MXC_PLL_DP_CTL);
+ reg &= ~MXC_PLL_DP_CTL_UPEN;
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_CTL);
+
+ /* PDF and MFI */
+ reg = p->pdf | p->mfi << MXC_PLL_DP_OP_MFI_OFFSET;
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_OP);
+
+ /* MFD */
+ __raw_writel(p->mfd, MXC_DPLL1_BASE + MXC_PLL_DP_MFD);
+
+ /* MFI */
+ __raw_writel(p->mfn, MXC_DPLL1_BASE + MXC_PLL_DP_MFN);
+
+ reg = __raw_readl(MXC_DPLL1_BASE + MXC_PLL_DP_CTL);
+ reg |= MXC_PLL_DP_CTL_UPEN;
+ /* Set the UPEN bits */
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_CTL);
+ /* Forcefully restart the PLL */
+ reg |= MXC_PLL_DP_CTL_RST;
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_CTL);
+
+ /* Wait for the PLL to lock */
+ do {
+ stat = __raw_readl(MXC_DPLL1_BASE + MXC_PLL_DP_CTL) &
+ MXC_PLL_DP_CTL_LRF;
+ } while (!stat);
+
+ reg = __raw_readl(MXC_CCM_CCSR);
+ /* Move the PLL1 back to the pll1_main_clk */
+ reg &= ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+ }
+ cpu_curr_wp = wp;
+
+ pll1_sw_clk.rate = cpu_wp_tbl[wp].cpu_rate;
+ pll1_main_clk.rate = pll1_sw_clk.rate;
+ cpu_clk.rate = pll1_sw_clk.rate;
+
+#if defined(CONFIG_CPU_FREQ)
+ cpufreq_trig_needed = 1;
+#endif
+
+ if (wp == 0)
+ dptc_resume(DPTC_GP_ID);
+ else
+ dptc_suspend(DPTC_GP_ID);
+
+ return 0;
+}
diff --git a/arch/arm/mach-mx37/cpu.c b/arch/arm/mach-mx37/cpu.c
new file mode 100644
index 000000000000..e059e4735d59
--- /dev/null
+++ b/arch/arm/mach-mx37/cpu.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2001 Deep Blue Solutions Ltd.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/*!
+ * @file mach-mx37/cpu.c
+ *
+ * @brief This file contains the CPU initialization code.
+ *
+ * @ingroup MSL_MX37
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+#include <asm/hardware/cache-l2x0.h>
+
+/*!
+ * CPU initialization. It is called by fixup_mxc_board()
+ */
+void __init mxc_cpu_init(void)
+{
+ if (!system_rev) {
+ mxc_set_system_rev(0x37, CHIP_REV_1_0);
+ }
+}
+
+#define MXC_ARM1176_BASE IO_ADDRESS(ARM1176_BASE_ADDR)
+
+/*!
+ * Post CPU init code
+ *
+ * @return 0 always
+ */
+static int __init post_cpu_init(void)
+{
+ u32 reg;
+ void *l2_base;
+ volatile unsigned long aips_reg;
+
+ /* Set ALP bits to 000. Set ALP_EN bit in Arm Memory Controller reg. */
+ reg = __raw_readl(MXC_ARM1176_BASE + 0x1C);
+ reg = 0x8;
+ __raw_writel(reg, MXC_ARM1176_BASE + 0x1C);
+
+ /* Initialize L2 cache */
+ l2_base = ioremap(L2CC_BASE_ADDR, SZ_4K);
+ if (l2_base) {
+ l2x0_init(l2_base, 0x0003001B, 0x00000000);
+ }
+
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x40));
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x44));
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x48));
+ __raw_writel(0x0, IO_ADDRESS(AIPS1_BASE_ADDR + 0x4C));
+ aips_reg = __raw_readl(IO_ADDRESS(AIPS1_BASE_ADDR + 0x50));
+ aips_reg &= 0x00FFFFFF;
+ __raw_writel(aips_reg, IO_ADDRESS(AIPS1_BASE_ADDR + 0x50));
+
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x40));
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x44));
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x48));
+ __raw_writel(0x0, IO_ADDRESS(AIPS2_BASE_ADDR + 0x4C));
+ aips_reg = __raw_readl(IO_ADDRESS(AIPS2_BASE_ADDR + 0x50));
+ aips_reg &= 0x00FFFFFF;
+ __raw_writel(aips_reg, IO_ADDRESS(AIPS2_BASE_ADDR + 0x50));
+
+ return 0;
+}
+
+postcore_initcall(post_cpu_init);
diff --git a/arch/arm/mach-mx37/crm_regs.h b/arch/arm/mach-mx37/crm_regs.h
new file mode 100644
index 000000000000..345746a789f9
--- /dev/null
+++ b/arch/arm/mach-mx37/crm_regs.h
@@ -0,0 +1,617 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX37_CRM_REGS_H__
+#define __ARCH_ARM_MACH_MX37_CRM_REGS_H__
+
+#define MXC_CCM_BASE ((char *)IO_ADDRESS(CCM_BASE_ADDR))
+#define MXC_DPLL1_BASE IO_ADDRESS(PLL0_BASE_ADDR)
+#define MXC_DPLL2_BASE IO_ADDRESS(PLL1_BASE_ADDR)
+#define MXC_DPLL3_BASE IO_ADDRESS(PLL2_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_MFNMINUS 0x14
+#define MXC_PLL_DP_MFNPLUS 0x18
+#define MXC_PLL_DP_HFS_OP 0x1C
+#define MXC_PLL_DP_HFS_MFD 0x20
+#define MXC_PLL_DP_HFS_MFN 0x24
+#define MXC_PLL_DP_MFN_TOGC 0x28
+#define MXC_PLL_DP_DESTAT 0x2c
+
+/* PLL Register Bit definitions */
+#define MXC_PLL_DP_CTL_MUL_CTRL 0x2000
+#define MXC_PLL_DP_CTL_DPDCK0_2_EN 0x1000
+#define MXC_PLL_DP_CTL_DPDCK0_2_OFFSET 12
+#define MXC_PLL_DP_CTL_ADE 0x800
+#define MXC_PLL_DP_CTL_REF_CLK_DIV 0x400
+#define MXC_PLL_DP_CTL_REF_CLK_SEL_MASK (3 << 8)
+#define MXC_PLL_DP_CTL_REF_CLK_SEL_OFFSET 8
+#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_CONFIG_BIST 0x8
+#define MXC_PLL_DP_CONFIG_SJC_CE 0x4
+#define MXC_PLL_DP_CONFIG_AREN 0x2
+#define MXC_PLL_DP_CONFIG_LDREQ 0x1
+
+#define MXC_PLL_DP_OP_MFI_OFFSET 4
+#define MXC_PLL_DP_OP_MFI_MASK (0xF << 4)
+#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 0x07FFFFFF
+
+#define MXC_PLL_DP_MFN_OFFSET 0x0
+#define MXC_PLL_DP_MFN_MASK 0x07FFFFFF
+
+#define MXC_PLL_DP_MFN_TOGC_TOG_DIS (1 << 17)
+#define MXC_PLL_DP_MFN_TOGC_TOG_EN (1 << 16)
+#define MXC_PLL_DP_MFN_TOGC_CNT_OFFSET 0x0
+#define MXC_PLL_DP_MFN_TOGC_CNT_MASK 0xFFFF
+
+#define MXC_PLL_DP_DESTAT_TOG_SEL (1 << 31)
+#define MXC_PLL_DP_DESTAT_MFN 0x07FFFFFF
+
+/* Register addresses of CCM*/
+#define MXC_CCM_CCR (MXC_CCM_BASE + 0x00)
+#define MXC_CCM_CCDR (MXC_CCM_BASE + 0x04)
+#define MXC_CCM_CSR (MXC_CCM_BASE + 0x08)
+#define MXC_CCM_CCSR (MXC_CCM_BASE + 0x0C)
+#define MXC_CCM_CACRR (MXC_CCM_BASE + 0x10)
+#define MXC_CCM_CBCDR2 (MXC_CCM_BASE + 0x18)
+#define MXC_CCM_CBCDR3 (MXC_CCM_BASE + 0x1C)
+#define MXC_CCM_CBCDR4 (MXC_CCM_BASE + 0x20)
+#define MXC_CCM_CBCDR5 (MXC_CCM_BASE + 0x24)
+#define MXC_CCM_CBCDR6 (MXC_CCM_BASE + 0x28)
+#define MXC_CCM_CBCDR7 (MXC_CCM_BASE + 0x2C)
+#define MXC_CCM_CAMR (MXC_CCM_BASE + 0x30)
+#define MXC_CCM_CSCMR1 (MXC_CCM_BASE + 0x34)
+#define MXC_CCM_CSCMR2 (MXC_CCM_BASE + 0x38)
+#define MXC_CCM_CSCDR1 (MXC_CCM_BASE + 0x3C)
+#define MXC_CCM_CS1CDR (MXC_CCM_BASE + 0x40)
+#define MXC_CCM_CS2CDR (MXC_CCM_BASE + 0x44)
+#define MXC_CCM_CSECDR1 (MXC_CCM_BASE + 0x48)
+#define MXC_CCM_CSECDR2 (MXC_CCM_BASE + 0x4C)
+#define MXC_CCM_CECDR (MXC_CCM_BASE + 0x50)
+#define MXC_CCM_CDCDR (MXC_CCM_BASE + 0x54)
+#define MXC_CCM_CH1CDR (MXC_CCM_BASE + 0x58)
+#define MXC_CCM_CH2CDR (MXC_CCM_BASE + 0x5C)
+#define MXC_CCM_CSCDR2 (MXC_CCM_BASE + 0x60)
+#define MXC_CCM_CR2 (MXC_CCM_BASE + 0x64)
+#define MXC_CCM_CDHIPR (MXC_CCM_BASE + 0x68)
+#define MXC_CCM_CDCR (MXC_CCM_BASE + 0x6C)
+#define MXC_CCM_CTOR (MXC_CCM_BASE + 0x70)
+#define MXC_CCM_CLPCR (MXC_CCM_BASE + 0x74)
+#define MXC_CCM_CISR (MXC_CCM_BASE + 0x78)
+#define MXC_CCM_CIMR (MXC_CCM_BASE + 0x7C)
+#define MXC_CCM_CCOSR (MXC_CCM_BASE + 0x80)
+#define MXC_CCM_CGPR (MXC_CCM_BASE + 0x84)
+#define MXC_CCM_CCGR0 (MXC_CCM_BASE + 0x88)
+#define MXC_CCM_CCGR1 (MXC_CCM_BASE + 0x8C)
+#define MXC_CCM_CCGR2 (MXC_CCM_BASE + 0x90)
+#define MXC_CCM_CCGR3 (MXC_CCM_BASE + 0x94)
+#define MXC_CCM_CCGR4 (MXC_CCM_BASE + 0x98)
+#define MXC_CCM_CCGR5 (MXC_CCM_BASE + 0x9C)
+#define MXC_CCM_CMEOR (MXC_CCM_BASE + 0xA0)
+
+/* Define the bits in register CCR */
+#define MXC_CCM_CCR_COSC_EN (1 << 11)
+#define MXC_CCM_CCR_FPM_MULT_MASK (1 << 10)
+#define MXC_CCM_CCR_CAMP_EN (1 << 9)
+#define MXC_CCM_CCR_FPM_EN (1 << 8)
+#define MXC_CCM_CCR_OSCNT_OFFSET (0)
+#define MXC_CCM_CCR_OSCNT_MASK (0xFF)
+
+/* Define the bits in register CCDR */
+#define MXC_CCM_CCDR_IPU_HS_MASK (0x1 << 17)
+#define MXC_CCM_CCDR_EMI_HS_MASK (0x1 << 16)
+#define MXC_CCM_CCDR_LOAD_DIVIDERS (0x1 << 0)
+
+/* Define the bits in register CSR */
+#define MXC_CCM_CSR_COSR_READY (1 << 4)
+#define MXC_CCM_CSR_LVS_VALUE (1 << 3)
+#define MXC_CCM_CSR_CAMP_READY (1 << 2)
+#define MXC_CCM_CSR_FPM_READY (1 << 1)
+#define MXC_CCM_CSR_REF_EN_B (1 << 0)
+
+/* Define the bits in register CCSR */
+#define MXC_CCM_CCSR_LP_APM_SEL (0x1 << 9)
+#define MXC_CCM_CCSR_STEP_SEL_OFFSET (7)
+#define MXC_CCM_CCSR_STEP_SEL_MASK (0x3 << 7)
+#define MXC_CCM_CCSR_PLL2_PODF_OFFSET (5)
+#define MXC_CCM_CCSR_PLL2_PODF_MASK (0x3 << 5)
+#define MXC_CCM_CCSR_PLL3_PODF_OFFSET (3)
+#define MXC_CCM_CCSR_PLL3_PODF_MASK (0x3 << 3)
+#define MXC_CCM_CCSR_PLL1_SW_CLK_SEL (1 << 2)
+#define MXC_CCM_CCSR_PLL2_SW_CLK_SEL (1 << 1)
+#define MXC_CCM_CCSR_PLL3_SW_CLK_SEL (1 << 0)
+
+/* Define the bits in register CACRR */
+#define MXC_CCM_CACRR_ARM_PODF_OFFSET (0)
+#define MXC_CCM_CACRR_ARM_PODF_MASK (0x7)
+
+/* Define the bits in register CBCDR2 */
+#define MXC_CCM_CBCDR2_AHB_PODF_OFFSET (10)
+#define MXC_CCM_CBCDR2_AHB_PODF_MASK (0x7 << 10)
+#define MXC_CCM_CBCDR2_IPG_PODF_OFFSET (8)
+#define MXC_CCM_CBCDR2_IPG_PODF_MASK (0x3 << 8)
+#define MXC_CCM_CBCDR2_PERCLK_PRED1_OFFSET (6)
+#define MXC_CCM_CBCDR2_PERCLK_PRED1_MASK (0x3 << 6)
+#define MXC_CCM_CBCDR2_PERCLK_PRED2_OFFSET (3)
+#define MXC_CCM_CBCDR2_PERCLK_PRED2_MASK (0x7 << 3)
+#define MXC_CCM_CBCDR2_PERCLK_PODF_OFFSET (0)
+#define MXC_CCM_CBCDR2_PERCLK_PODF_MASK (0x7)
+
+/* Define the bits in register CBCDR3 */
+#define MXC_CCM_CBCDR3_AXI_A_PODF_OFFSET (0)
+#define MXC_CCM_CBCDR3_AXI_A_PODF_MASK (0x7)
+
+/* Define the bits in register CBCDR4 */
+#define MXC_CCM_CBCDR4_AXI_B_PODF_OFFSET (0)
+#define MXC_CCM_CBCDR4_AXI_B_PODF_MASK (0x7)
+
+/* Define the bits in register CBCDR5 */
+#define MXC_CCM_CBCDR5_AXI_C_PODF_OFFSET (0)
+#define MXC_CCM_CBCDR5_AXI_C_PODF_MASK (0x7)
+
+/* Define the bits in register CBCDR6 */
+#define MXC_CCM_CBCDR6_EMI_PODF_OFFSET (0)
+#define MXC_CCM_CBCDR6_EMI_PODF_MASK (0x7)
+#define MXC_CCM_CBCDR6_EMI_CLK_SEL (0x1 << 3)
+#define MXC_CCM_CBCDR6_PERIPH_CLK_SEL (0x1 << 4)
+
+/* Define the bits in register CBCDR7 */
+#define MXC_CCM_CBCDR7_IPG_INT_MEM_PODF_OFFSET (3)
+#define MXC_CCM_CBCDR7_IPG_INIT_MEM_PODF_MASK (0x3 << 3)
+#define MXC_CCM_CBCDR7_NFC_PODF_OFFSET (0)
+#define MXC_CCM_CBCDR7_NFC_PODF_MASK (0x7)
+
+/* Define the bits in register CAMR */
+#define MXC_CCM_CAMR_PERIPH_CLK_SEL_OFFSET (12)
+#define MXC_CCM_CAMR_PERIPH_CLK_SEL_MASK (0x3 << 12)
+#define MXC_CCM_CAMR_DDR_CLK_SEL_OFFSET (10)
+#define MXC_CCM_CAMR_DDR_CLK_SEL_MASK (0x3 << 10)
+#define MXC_CCM_CAMR_ARM_AXI_CLK_SEL_OFFSET (8)
+#define MXC_CCM_CAMR_ARM_AXI_CLK_SEL_MASK (0x3 << 8)
+#define MXC_CCM_CAMR_VPU_CLK_SEL_OFFSET (6)
+#define MXC_CCM_CAMR_VPU_CLK_SEL_MASK (0x3 << 6)
+#define MXC_CCM_CAMR_VPU_AXI_CLK_SEL_OFFSET (4)
+#define MXC_CCM_CAMR_VPU_AXI_CLK_SEL_MASK (0x3 << 4)
+#define MXC_CCM_CAMR_IPU_HSP_CLK_SEL_OFFSET (2)
+#define MXC_CCM_CAMR_IPU_HSP_CLK_SEL_MASK (0x3 << 2)
+
+/* Define the bits in register CSCMR1 */
+#define MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_OFFSET (30)
+#define MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_MASK (0x3 << 30)
+#define MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_OFFSET (28)
+#define MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_MASK (0x3 << 28)
+#define MXC_CCM_CSCMR1_DI_CLK_SEL (0x1 << 27)
+#define MXC_CCM_CSCMR1_USB_PHY_CLK_SEL_OFFSET (26)
+#define MXC_CCM_CSCMR1_USB_PHY_CLK_SEL (0x1 << 26)
+#define MXC_CCM_CSCMR1_UART_CLK_SEL_OFFSET (24)
+#define MXC_CCM_CSCMR1_UART_CLK_SEL_MASK (0x3 << 24)
+#define MXC_CCM_CSCMR1_USBOH2_CLK_SEL_OFFSET (22)
+#define MXC_CCM_CSCMR1_USBOH2_CLK_SEL_MASK (0x3 << 22)
+#define MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_OFFSET (20)
+#define MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_MASK (0x3 << 20)
+#define MXC_CCM_CSCMR1_ESDHC3_CLK_SEL (0x1 << 19)
+#define MXC_CCM_CSCMR1_PERCLK_IPG_CLK_SEL (0x1 << 18)
+#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_OFFSET (16)
+#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_MASK (0x3 << 16)
+#define MXC_CCM_CSCMR1_SSI1_CLK_SEL_OFFSET (14)
+#define MXC_CCM_CSCMR1_SSI1_CLK_SEL_MASK (0x3 << 14)
+#define MXC_CCM_CSCMR1_SSI2_CLK_SEL_OFFSET (12)
+#define MXC_CCM_CSCMR1_SSI2_CLK_SEL_MASK (0x3 << 12)
+#define MXC_CCM_CSCMR1_SSI_APM_CLK_SEL (0x1 << 9)
+#define MXC_CCM_CSCMR1_SPDIF_CLK_SEL (0x1 << 8)
+#define MXC_CCM_CSCMR1_TVE_CLK_SEL (0x1 << 7)
+#define MXC_CCM_CSCMR1_TVE_EXT_CLK_SEL (0x1 << 6)
+#define MXC_CCM_CSCMR1_CSPI_CLK_SEL_OFFSET (4)
+#define MXC_CCM_CSCMR1_CSPI_CLK_SEL_MASK (0x3 << 4)
+#define MXC_CCM_CSCMR1_PERCLK_LP_APM_CLK_SEL (0x1 << 2)
+#define MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL (0x1 << 1)
+#define MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL (0x1)
+
+/* Define the bits in register CSCMR2 */
+#define MXC_CCM_CSCMR2_SPDIF1_COM (1 << 5)
+#define MXC_CCM_CSCMR2_SPDIF0_COM (1 << 4)
+#define MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_OFFSET (2)
+#define MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_MASK (0x3 << 2)
+#define MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_OFFSET (0)
+#define MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_MASK (0x3)
+
+/* Define the bits in register CSCDR1 */
+#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_OFFSET (22)
+#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_MASK (0x7 << 22)
+#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_OFFSET (19)
+#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_MASK (0x7 << 19)
+#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_OFFSET (16)
+#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_MASK (0x7 << 16)
+#define MXC_CCM_CSCDR1_PGC_CLK_PODF_OFFSET (14)
+#define MXC_CCM_CSCDR1_PGC_CLK_PODF_MASK (0x3 << 14)
+#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_OFFSET (11)
+#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_MASK (0x7 << 11)
+#define MXC_CCM_CSCDR1_USBOH2_CLK_PRED_OFFSET (8)
+#define MXC_CCM_CSCDR1_USBOH2_CLK_PRED_MASK (0x7 << 8)
+#define MXC_CCM_CSCDR1_USBOH2_CLK_PODF_OFFSET (6)
+#define MXC_CCM_CSCDR1_USBOH2_CLK_PODF_MASK (0x3 << 6)
+#define MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET (3)
+#define MXC_CCM_CSCDR1_UART_CLK_PRED_MASK (0x7 << 3)
+#define MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CSCDR1_UART_CLK_PODF_MASK (0x7)
+
+/* Define the bits in register CS1CDR and CS2CDR */
+#define MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET (6)
+#define MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK (0x7 << 6)
+#define MXC_CCM_CS1CDR_SSI1_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK (0x3F)
+
+#define MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET (6)
+#define MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK (0x7 << 6)
+#define MXC_CCM_CS2CDR_SSI2_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK (0x3F)
+
+/* Define the bits in register CSECDR1 and CSECDR2 */
+#define MXC_CCM_CSECDR1_SSI_EXT1_CLK_PRED_OFFSET (6)
+#define MXC_CCM_CSECDR1_SSI_EXT1_CLK_PRED_MASK (0x7 << 6)
+#define MXC_CCM_CSECDR1_SSI_EXT1_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CSECDR1_SSI_EXT1_CLK_PODF_MASK (0x3F)
+
+#define MXC_CCM_CSECDR2_SSI_EXT2_CLK_PRED_OFFSET (6)
+#define MXC_CCM_CSECDR2_SSI_EXT2_CLK_PRED_MASK (0x7 << 6)
+#define MXC_CCM_CSECDR2_SSI_EXT2_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CSECDR2_SSI_EXT2_CLK_PODF_MASK (0x3F)
+
+/* Define the bits in register CDCDR */
+#define MXC_CCM_CDCDR_TVE_CLK_PRED_OFFSET (28)
+#define MXC_CCM_CDCDR_TVE_CLK_PRED_MASK (0x7 << 28)
+#define MXC_CCM_CDCDR_SPDIF0_CLK_PRED_OFFSET (25)
+#define MXC_CCM_CDCDR_SPDIF0_CLK_PRED_MASK (0x7 << 25)
+#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_OFFSET (19)
+#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK (0x3F << 19)
+#define MXC_CCM_CDCDR_SPDIF1_CLK_PRED_OFFSET (16)
+#define MXC_CCM_CDCDR_SPDIF1_CLK_PRED_MASK (0x7 << 16)
+#define MXC_CCM_CDCDR_SPDIF1_CLK_PODF_OFFSET (9)
+#define MXC_CCM_CDCDR_SPDIF1_CLK_PODF_MASK (0x3F << 9)
+#define MXC_CCM_CDCDR_USB_PHY_PRED_OFFSET (4)
+#define MXC_CCM_CDCDR_USB_PHY_PRED_MASK (0x7 << 4)
+#define MXC_CCM_CDCDR_USB_PHY_PODF_OFFSET (1)
+#define MXC_CCM_CDCDR_USB_PHY_PODF_MASK (0x7 << 1)
+
+/* Define the bits in register CSCDR2 */
+#define MXC_CCM_CSCDR2_CSPI_CLK_PRED_OFFSET (25)
+#define MXC_CCM_CSCDR2_CSPI_CLK_PRED_MASK (0x7 << 25)
+#define MXC_CCM_CSCDR2_CSPI_CLK_PODF_OFFSET (19)
+#define MXC_CCM_CSCDR2_CSPI_CLK_PODF_MASK (0x3F << 19)
+
+/* Define the bits in register CDHIPR */
+#define MXC_CCM_CDHIPR_ARM_PODF_BUSY (1 << 16)
+#define MXC_CCM_CDHIPR_NFC_IPG_INT_MEM_PODF_BUSY (1 << 4)
+#define MXC_CCM_CDHIPR_EMI_PODF_BUSY (1 << 3)
+#define MXC_CCM_CDHIPR_AXI_C_PODF_BUSY (1 << 2)
+#define MXC_CCM_CDHIPR_AXI_B_PODF_BUSY (1 << 1)
+#define MXC_CCM_CDHIPR_AXI_A_PODF_BUSY (1 << 0)
+
+/* Define the bits in register CDCR */
+#define MXC_CCM_CDCR_ARM_FREQ_SHIFT_DIVIDER (0x1 << 2)
+#define MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_OFFSET (0)
+#define MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_MASK (0x3)
+
+/* Define the bits in register CLPCR */
+#define MXC_CCM_CLPCR_BYPASS_SCC_LPM_HS (0x1 << 22)
+#define MXC_CCM_CLPCR_BYPASS_MAX_LPM_HS (0x1 << 21)
+#define MXC_CCM_CLPCR_BYPASS_SDMA_LPM_HS (0x1 << 20)
+#define MXC_CCM_CLPCR_BYPASS_EMI_LPM_HS (0x1 << 19)
+#define MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS (0x1 << 18)
+#define MXC_CCM_CLPCR_BYPASS_RTIC_LPM_HS (0x1 << 17)
+#define MXC_CCM_CLPCR_BYPASS_RNGC_LPM_HS (0x1 << 16)
+#define MXC_CCM_CLPCR_COSC_PWRDOWN (0x1 << 11)
+#define MXC_CCM_CLPCR_STBY_COUNT_OFFSET (9)
+#define MXC_CCM_CLPCR_STBY_COUNT_MASK (0x3 << 9)
+#define MXC_CCM_CLPCR_VSTBY (0x1 << 8)
+#define MXC_CCM_CLPCR_DIS_REF_OSC (0x1 << 7)
+#define MXC_CCM_CLPCR_SBYOS (0x1 << 6)
+#define MXC_CCM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
+#define MXC_CCM_CLPCR_LPSR_CLK_SEL_OFFSET (3)
+#define MXC_CCM_CLPCR_LPSR_CLK_SEL_MASK (0x3 << 3)
+#define MXC_CCM_CLPCR_LPM_OFFSET (0)
+#define MXC_CCM_CLPCR_LPM_MASK (0x3)
+
+/* Define the bits in register CISR */
+#define MXC_CCM_CISR_ARM_PODF_LOADED (0x1 << 25)
+#define MXC_CCM_CISR_NFC_IPG_INT_MEM_PODF_LOADED (0x1 << 21)
+#define MXC_CCM_CISR_EMI_PODF_LOADED (0x1 << 20)
+#define MXC_CCM_CISR_AXI_C_PODF_LOADED (0x1 << 19)
+#define MXC_CCM_CISR_AXI_B_PODF_LOADED (0x1 << 18)
+#define MXC_CCM_CISR_AXI_A_PODF_LOADED (0x1 << 17)
+#define MXC_CCM_CISR_DIVIDER_LOADED (0x1 << 16)
+#define MXC_CCM_CISR_COSC_READY (0x1 << 5)
+#define MXC_CCM_CISR_CKIH_READY (0x1 << 4)
+#define MXC_CCM_CISR_FPM_READY (0x1 << 3)
+#define MXC_CCM_CISR_LRF_PLL3 (0x1 << 2)
+#define MXC_CCM_CISR_LRF_PLL2 (0x1 << 1)
+#define MXC_CCM_CISR_LRF_PLL1 (0x1)
+
+/* Define the bits in register CIMR */
+#define MXC_CCM_CIMR_MASK_ARM_PODF_LOADED (0x1 << 25)
+#define MXC_CCM_CIMR_MASK_NFC_IPG_INT_MEM_PODF_LOADED (0x1 << 21)
+#define MXC_CCM_CIMR_MASK_EMI_PODF_LOADED (0x1 << 20)
+#define MXC_CCM_CIMR_MASK_AXI_C_PODF_LOADED (0x1 << 19)
+#define MXC_CCM_CIMR_MASK_AXI_B_PODF_LOADED (0x1 << 18)
+#define MXC_CCM_CIMR_MASK_AXI_A_PODF_LOADED (0x1 << 17)
+#define MXC_CCM_CIMR_MASK_DIVIDER_LOADED (0x1 << 16)
+#define MXC_CCM_CIMR_MASK_COSC_READY (0x1 << 5)
+#define MXC_CCM_CIMR_MASK_CKIH_READY (0x1 << 4)
+#define MXC_CCM_CIMR_MASK_FPM_READY (0x1 << 3)
+#define MXC_CCM_CIMR_MASK_LRF_PLL3 (0x1 << 2)
+#define MXC_CCM_CIMR_MASK_LRF_PLL2 (0x1 << 1)
+#define MXC_CCM_CIMR_MASK_LRF_PLL1 (0x1)
+
+/* Define the bits in register CCOSR */
+#define MXC_CCM_CCOSR_CKO2_EN_OFFSET (0x1 << 24)
+#define MXC_CCM_CCOSR_CKO2_DIV_OFFSET (21)
+#define MXC_CCM_CCOSR_CKO2_DIV_MASK (0x7 << 21)
+#define MXC_CCM_CCOSR_CKO2_SEL_OFFSET (16)
+#define MXC_CCM_CCOSR_CKO2_SEL_MASK (0x1F << 16)
+#define MXC_CCM_CCOSR_CKOL_EN (0x1 << 7)
+#define MXC_CCM_CCOSR_CKOL_DIV_OFFSET (4)
+#define MXC_CCM_CCOSR_CKOL_DIV_MASK (0x7 << 4)
+#define MXC_CCM_CCOSR_CKOL_SEL_OFFSET (0)
+#define MXC_CCM_CCOSR_CKOL_SEL_MASK (0xF)
+
+/* Define the bits in registers CGPR */
+#define MXC_CCM_CGPR_EFUSE_PROG_SUPPLY_GATE (0x1 << 4)
+#define MXC_CCM_CGPR_FPM_SEL (0x1 << 3)
+#define MXC_CCM_CGPR_VL_L2BIST_CLKDIV_OFFSET (0)
+#define MXC_CCM_CGPR_VL_L2BIST_CLKDIV_MASK (0x7)
+
+/* Define the bits in registers CCGRx */
+#define MXC_CCM_CCGR_CG_MASK 0x3
+
+#define MXC_CCM_CCGR0_CG15_OFFSET 30
+#define MXC_CCM_CCGR0_CG14_OFFSET 28
+#define MXC_CCM_CCGR0_CG14_MASK (0x3 << 28)
+#define MXC_CCM_CCGR0_CG13_OFFSET 26
+#define MXC_CCM_CCGR0_CG13_MASK (0x3 << 26)
+#define MXC_CCM_CCGR0_CG12_OFFSET 24
+#define MXC_CCM_CCGR0_CG12_MASK (0x3 << 24)
+#define MXC_CCM_CCGR0_CG11_OFFSET 22
+#define MXC_CCM_CCGR0_CG10_OFFSET 20
+#define MXC_CCM_CCGR0_CG9_OFFSET 18
+#define MXC_CCM_CCGR0_CG8_OFFSET 16
+#define MXC_CCM_CCGR0_CG7_OFFSET 14
+#define MXC_CCM_CCGR0_CG6_OFFSET 12
+#define MXC_CCM_CCGR0_CG5_OFFSET 10
+#define MXC_CCM_CCGR0_CG4_OFFSET 8
+#define MXC_CCM_CCGR0_CG3_OFFSET 6
+#define MXC_CCM_CCGR0_CG2_OFFSET 4
+#define MXC_CCM_CCGR0_CG2_MASK (0x3 << 4)
+#define MXC_CCM_CCGR0_CG1_OFFSET 2
+#define MXC_CCM_CCGR0_CG1_MASK (0x3 << 2)
+#define MXC_CCM_CCGR0_CG0_OFFSET 0
+#define MXC_CCM_CCGR0_CG0_MASK 0x3
+
+#define MXC_CCM_CCGR1_CG15_OFFSET 30
+#define MXC_CCM_CCGR1_CG14_OFFSET 28
+#define MXC_CCM_CCGR1_CG13_OFFSET 26
+#define MXC_CCM_CCGR1_CG12_OFFSET 24
+#define MXC_CCM_CCGR1_CG11_OFFSET 22
+#define MXC_CCM_CCGR1_CG10_OFFSET 20
+#define MXC_CCM_CCGR1_CG9_OFFSET 18
+#define MXC_CCM_CCGR1_CG8_OFFSET 16
+#define MXC_CCM_CCGR1_CG7_OFFSET 14
+#define MXC_CCM_CCGR1_CG6_OFFSET 12
+#define MXC_CCM_CCGR1_CG5_OFFSET 10
+#define MXC_CCM_CCGR1_CG4_OFFSET 8
+#define MXC_CCM_CCGR1_CG3_OFFSET 6
+#define MXC_CCM_CCGR1_CG2_OFFSET 4
+#define MXC_CCM_CCGR1_CG1_OFFSET 2
+#define MXC_CCM_CCGR1_CG0_OFFSET 0
+
+#define MXC_CCM_CCGR2_CG15_OFFSET 30
+#define MXC_CCM_CCGR2_CG14_OFFSET 28
+#define MXC_CCM_CCGR2_CG13_OFFSET 26
+#define MXC_CCM_CCGR2_CG12_OFFSET 24
+#define MXC_CCM_CCGR2_CG11_OFFSET 22
+#define MXC_CCM_CCGR2_CG10_OFFSET 20
+#define MXC_CCM_CCGR2_CG9_OFFSET 18
+#define MXC_CCM_CCGR2_CG8_OFFSET 16
+#define MXC_CCM_CCGR2_CG7_OFFSET 14
+#define MXC_CCM_CCGR2_CG6_OFFSET 12
+#define MXC_CCM_CCGR2_CG5_OFFSET 10
+#define MXC_CCM_CCGR2_CG4_OFFSET 8
+#define MXC_CCM_CCGR2_CG3_OFFSET 6
+#define MXC_CCM_CCGR2_CG2_OFFSET 4
+#define MXC_CCM_CCGR2_CG1_OFFSET 2
+#define MXC_CCM_CCGR2_CG0_OFFSET 0
+
+#define MXC_CCM_CCGR3_CG15_OFFSET 30
+#define MXC_CCM_CCGR3_CG14_OFFSET 28
+#define MXC_CCM_CCGR3_CG13_OFFSET 26
+#define MXC_CCM_CCGR3_CG12_OFFSET 24
+#define MXC_CCM_CCGR3_CG11_OFFSET 22
+#define MXC_CCM_CCGR3_CG10_OFFSET 20
+#define MXC_CCM_CCGR3_CG9_OFFSET 18
+#define MXC_CCM_CCGR3_CG8_OFFSET 16
+#define MXC_CCM_CCGR3_CG7_OFFSET 14
+#define MXC_CCM_CCGR3_CG6_OFFSET 12
+#define MXC_CCM_CCGR3_CG5_OFFSET 10
+#define MXC_CCM_CCGR3_CG4_OFFSET 8
+#define MXC_CCM_CCGR3_CG3_OFFSET 6
+#define MXC_CCM_CCGR3_CG2_OFFSET 4
+#define MXC_CCM_CCGR3_CG1_OFFSET 2
+#define MXC_CCM_CCGR3_CG0_OFFSET 0
+
+#define MXC_CCM_CCGR4_CG15_OFFSET 30
+#define MXC_CCM_CCGR4_CG14_OFFSET 28
+#define MXC_CCM_CCGR4_CG13_OFFSET 26
+#define MXC_CCM_CCGR4_CG12_OFFSET 24
+#define MXC_CCM_CCGR4_CG11_OFFSET 22
+#define MXC_CCM_CCGR4_CG10_OFFSET 20
+#define MXC_CCM_CCGR4_CG9_OFFSET 18
+#define MXC_CCM_CCGR4_CG8_OFFSET 16
+#define MXC_CCM_CCGR4_CG7_OFFSET 14
+#define MXC_CCM_CCGR4_CG6_OFFSET 12
+#define MXC_CCM_CCGR4_CG5_OFFSET 10
+#define MXC_CCM_CCGR4_CG4_OFFSET 8
+#define MXC_CCM_CCGR4_CG3_OFFSET 6
+#define MXC_CCM_CCGR4_CG2_OFFSET 4
+#define MXC_CCM_CCGR4_CG1_OFFSET 2
+#define MXC_CCM_CCGR4_CG0_OFFSET 0
+
+#define MXC_CCM_CCGR5_CG15_OFFSET 30
+#define MXC_CCM_CCGR5_CG14_OFFSET 28
+#define MXC_CCM_CCGR5_CG14_MASK (0x3 << 28)
+#define MXC_CCM_CCGR5_CG13_OFFSET 26
+#define MXC_CCM_CCGR5_CG13_MASK (0x3 << 26)
+#define MXC_CCM_CCGR5_CG12_OFFSET 24
+#define MXC_CCM_CCGR5_CG12_MASK (0x3 << 24)
+#define MXC_CCM_CCGR5_CG11_OFFSET 22
+#define MXC_CCM_CCGR5_CG11_MASK (0x3 << 22)
+#define MXC_CCM_CCGR5_CG10_OFFSET 20
+#define MXC_CCM_CCGR5_CG9_OFFSET 18
+#define MXC_CCM_CCGR5_CG8_OFFSET 16
+#define MXC_CCM_CCGR5_CG7_OFFSET 14
+#define MXC_CCM_CCGR5_CG6_OFFSET 12
+#define MXC_CCM_CCGR5_CG5_OFFSET 10
+#define MXC_CCM_CCGR5_CG4_OFFSET 8
+#define MXC_CCM_CCGR5_CG3_OFFSET 6
+#define MXC_CCM_CCGR5_CG2_OFFSET 4
+#define MXC_CCM_CCGR5_CG1_OFFSET 2
+#define MXC_CCM_CCGR5_CG0_OFFSET 0
+
+#define MXC_ARM1176_BASE IO_ADDRESS(ARM1176_BASE_ADDR)
+#define MXC_GPC_BASE IO_ADDRESS(GPC_BASE_ADDR)
+#define MXC_DPTC_LP_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x80)
+#define MXC_DPTC_GP_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x100)
+#define MXC_DVFS_CORE_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x180)
+#define MXC_DPTC_PER_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x1C0)
+#define MXC_PGC_IPU_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x220)
+#define MXC_PGC_VPU_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x240)
+#define MXC_SRPGC_EMI_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x280)
+#define MXC_SRPGC_ARM_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x2A0)
+#define MXC_EMPGC0_ARM_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x2C0)
+#define MXC_EMPGC1_ARM_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x2D0)
+
+/* ARM1176 platform */
+#define MXC_ARM1176_PLAT_PVID (MXC_ARM1176_BASE + 0x0)
+#define MXC_ARM1176_PLAT_GPC (MXC_ARM1176_BASE + 0x4)
+#define MXC_ARM1176_PLAT_PIC (MXC_ARM1176_BASE + 0x8)
+#define MXC_ARM1176_PLAT_L2SO (MXC_ARM1176_BASE + 0xC)
+#define MXC_ARM1176_PLAT_EMSO (MXC_ARM1176_BASE + 0x10)
+#define MXC_ARM1176_PLAT_LPC (MXC_ARM1176_BASE + 0x14)
+#define MXC_ARM1176_PLAT_ICGC (MXC_ARM1176_BASE + 0x18)
+#define MXC_ARM1176_PLAT_AMC (MXC_ARM1176_BASE + 0x1C)
+
+/* GPC */
+#define MXC_GPC_CNTR (MXC_GPC_BASE + 0x0)
+#define MXC_GPC_PGR (MXC_GPC_BASE + 0x4)
+#define MXC_GPC_VCR (MXC_GPC_BASE + 0x8)
+
+/* DVFS CORE */
+#define MXC_DVFSTHRS (MXC_DVFS_CORE_BASE + 0x00)
+#define MXC_DVFSCOUN (MXC_DVFS_CORE_BASE + 0x04)
+#define MXC_DVFSSIG1 (MXC_DVFS_CORE_BASE + 0x08)
+#define MXC_DVFSSIG0 (MXC_DVFS_CORE_BASE + 0x0C)
+#define MXC_DVFSGPC0 (MXC_DVFS_CORE_BASE + 0x10)
+#define MXC_DVFSGPC1 (MXC_DVFS_CORE_BASE + 0x14)
+#define MXC_DVFSGPBT (MXC_DVFS_CORE_BASE + 0x18)
+#define MXC_DVFSEMAC (MXC_DVFS_CORE_BASE + 0x1C)
+#define MXC_DVFSCNTR (MXC_DVFS_CORE_BASE + 0x20)
+#define MXC_DVFSLTR0_0 (MXC_DVFS_CORE_BASE + 0x24)
+#define MXC_DVFSLTR0_1 (MXC_DVFS_CORE_BASE + 0x28)
+#define MXC_DVFSLTR1_0 (MXC_DVFS_CORE_BASE + 0x2C)
+#define MXC_DVFSLTR1_1 (MXC_DVFS_CORE_BASE + 0x30)
+#define MXC_DVFSPT0 (MXC_DVFS_CORE_BASE + 0x34)
+#define MXC_DVFSPT1 (MXC_DVFS_CORE_BASE + 0x38)
+#define MXC_DVFSPT2 (MXC_DVFS_CORE_BASE + 0x3C)
+#define MXC_DVFSPT3 (MXC_DVFS_CORE_BASE + 0x40)
+
+/* DPTC GP */
+#define MXC_GP_DPTCCR (MXC_DPTC_GP_BASE + 0x00)
+#define MXC_GP_DPTCDBG (MXC_DPTC_GP_BASE + 0x04)
+#define MXC_GP_DCVR0 (MXC_DPTC_GP_BASE + 0x08)
+#define MXC_GP_DCVR1 (MXC_DPTC_GP_BASE + 0x0C)
+#define MXC_GP_DCVR2 (MXC_DPTC_GP_BASE + 0x10)
+#define MXC_GP_DCVR3 (MXC_DPTC_GP_BASE + 0x14)
+
+/* DPTC LP */
+#define MXC_LP_DPTCCR (MXC_DPTC_LP_BASE + 0x00)
+#define MXC_LP_DPTCDBG (MXC_DPTC_LP_BASE + 0x04)
+#define MXC_LP_DCVR0 (MXC_DPTC_LP_BASE + 0x08)
+#define MXC_LP_DCVR1 (MXC_DPTC_LP_BASE + 0x0C)
+#define MXC_LP_DCVR2 (MXC_DPTC_LP_BASE + 0x10)
+#define MXC_LP_DCVR3 (MXC_DPTC_LP_BASE + 0x14)
+
+#define MXC_DPTCCR_DRCE3 0x00400000
+#define MXC_DPTCCR_DRCE2 0x00200000
+#define MXC_DPTCCR_DRCE1 0x00100000
+#define MXC_DPTCCR_DRCE0 0x00080000
+#define MXC_DPTCCR_DCR_256 0x00060000
+#define MXC_DPTCCR_DCR_128 0x00040000
+#define MXC_DPTCCR_DCR_64 0x00020000
+#define MXC_DPTCCR_DCR_32 0x00000000
+#define MXC_DPTCCR_DSMM 0x00000040
+#define MXC_DPTCCR_DPNVCR 0x00000020
+#define MXC_DPTCCR_DPVV 0x00000010
+#define MXC_DPTCCR_VAIM 0x00000008
+#define MXC_DPTCCR_VAI_OFFSET 1
+#define MXC_DPTCCR_VAI_MASK 0x00000006
+#define MXC_DPTCCR_DEN 0x00000001
+
+#define MXC_GPCCNTR_GPCIRQ 0x00100000
+#define MXC_GPCCNTR_DPTC0CR 0x00040000
+#define MXC_GPCCNTR_DPTC1CR 0x00080000
+#define MXC_GPCCNTR_ADU 0x00008000
+
+/* SRPG */
+#define MXC_SRPGC_EMI_SRPGCR (MXC_SRPGC_EMI_BASE + 0x0)
+#define MXC_SRPGC_ARM_SRPGCR (MXC_SRPGC_ARM_BASE + 0x0)
+#define MXC_EMPGC0_ARM_PUPSCR (MXC_EMPGC0_ARM_BASE + 0x4)
+#define MXC_EMPGC0_ARM_PDNSCR (MXC_EMPGC0_ARM_BASE + 0x8)
+
+
+#define MXC_EMPGC0_ARM_EMPGCR (MXC_EMPGC0_ARM_BASE + 0x0)
+#define MXC_EMPGC1_ARM_EMPGCR (MXC_EMPGC1_ARM_BASE + 0x0)
+#define MXC_SRPGC_ARM_PUPSCR (MXC_SRPGC_ARM_BASE + 0x4)
+#define MXC_SRPGC_ARM_PDNSCR (MXC_SRPGC_ARM_BASE + 0x8)
+
+#define MXC_PGC_IPU_PGCR (MXC_PGC_IPU_BASE + 0x0)
+#define MXC_PGC_IPU_PGSR (MXC_PGC_IPU_BASE + 0xC)
+#define MXC_PGC_VPU_PGCR (MXC_PGC_VPU_BASE + 0x0)
+#define MXC_PGC_VPU_PGSR (MXC_PGC_VPU_BASE + 0xC)
+
+#define MXC_ARM1176_PLAT_LPC_DSM (1 << 16)
+#define MXC_ARM1176_PLAT_LPC_DBG_DSM (1 << 17)
+
+#define MXC_GPC_PGR_ARMPG_OFFSET 8
+#define MXC_GPC_PGR_ARMPG_MASK (3 << 8)
+
+#define MXC_PGCR_PCR 1
+#define MXC_SRPGCR_PCR 1
+#define MXC_EMPGCR_PCR 1
+
+#define MXC_PGSR_PSR 1
+
+#endif /* __ARCH_ARM_MACH_MX37_CRM_REGS_H__ */
diff --git a/arch/arm/mach-mx37/devices.c b/arch/arm/mach-mx37/devices.c
new file mode 100644
index 000000000000..b81d0be00e17
--- /dev/null
+++ b/arch/arm/mach-mx37/devices.c
@@ -0,0 +1,1027 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mxc_scc2_driver.h>
+#include <linux/spi/spi.h>
+
+#include <mach/gpio.h>
+#include <mach/hardware.h>
+#include <mach/mxc_dptc.h>
+#include <mach/mxc_dvfs.h>
+#include <mach/sdma.h>
+#include <mach/spba.h>
+
+#include "sdma_script_code.h"
+#include "crm_regs.h"
+
+/* Flag used to indicate if dvfs_core is active. */
+int dvfs_core_is_active;
+
+extern struct dptc_wp dptc_gp_wp_allfreq[DPTC_GP_WP_SUPPORTED];
+extern struct dptc_wp dptc_lp_wp_allfreq[DPTC_LP_WP_SUPPORTED];
+
+void mxc_sdma_get_script_info(sdma_script_start_addrs * sdma_script_addr)
+{
+ sdma_script_addr->mxc_sdma_app_2_mcu_addr = app_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_ap_2_ap_addr = ap_2_ap_ADDR;
+ sdma_script_addr->mxc_sdma_ap_2_bp_addr = -1;
+ sdma_script_addr->mxc_sdma_bp_2_ap_addr = -1;
+ sdma_script_addr->mxc_sdma_loopback_on_dsp_side_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_app_addr = mcu_2_app_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_shp_addr = mcu_2_shp_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_interrupt_only_addr = -1;
+ sdma_script_addr->mxc_sdma_shp_2_mcu_addr = shp_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_start_addr = (unsigned short *)sdma_code;
+ sdma_script_addr->mxc_sdma_uartsh_2_mcu_addr = uartsh_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_uart_2_mcu_addr = uart_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_ram_code_size = RAM_CODE_SIZE;
+ sdma_script_addr->mxc_sdma_ram_code_start_addr = RAM_CODE_START_ADDR;
+ sdma_script_addr->mxc_sdma_dptc_dvfs_addr = dptc_dvfs_ADDR;
+ sdma_script_addr->mxc_sdma_firi_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_firi_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_mshc_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_per_2_app_addr = -1;
+ sdma_script_addr->mxc_sdma_per_2_firi_addr = -1;
+ sdma_script_addr->mxc_sdma_per_2_shp_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_ata_addr = mcu_2_ata_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_firi_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_mshc_addr = -1;
+ sdma_script_addr->mxc_sdma_ata_2_mcu_addr = ata_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_uartsh_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_shp_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_uart_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_app_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_spdif_addr = mcu_2_spdif_marley_ADDR;
+}
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+#if defined(CONFIG_W1_MASTER_MXC) || defined(CONFIG_W1_MASTER_MXC_MODULE)
+static struct mxc_w1_config mxc_w1_data = {
+ .search_rom_accelerator = 0,
+};
+
+static struct platform_device mxc_w1_devices = {
+ .name = "mxc_w1",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_w1_data,
+ },
+ .id = 0
+};
+
+static void mxc_init_owire(void)
+{
+ (void)platform_device_register(&mxc_w1_devices);
+}
+#else
+static inline void mxc_init_owire(void)
+{
+}
+#endif
+
+#if defined(CONFIG_RTC_DRV_MXC_V2) || defined(CONFIG_RTC_DRV_MXC_V2_MODULE)
+static struct mxc_srtc_platform_data srtc_data = {
+ .srtc_sec_mode_addr = 0xC3FAC80C,
+};
+
+static struct resource rtc_resources[] = {
+ {
+ .start = SRTC_BASE_ADDR,
+ .end = SRTC_BASE_ADDR + 0x40,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_SRTC_NTZ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device mxc_rtc_device = {
+ .name = "mxc_rtc",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &srtc_data,
+ },
+ .num_resources = ARRAY_SIZE(rtc_resources),
+ .resource = rtc_resources,
+};
+static void mxc_init_rtc(void)
+{
+ (void)platform_device_register(&mxc_rtc_device);
+}
+#else
+static inline void mxc_init_rtc(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_WATCHDOG) || defined(CONFIG_MXC_WATCHDOG_MODULE)
+
+static struct resource wdt_resources[] = {
+ {
+ .start = WDOG1_BASE_ADDR,
+ .end = WDOG1_BASE_ADDR + 0x30,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_wdt_device = {
+ .name = "mxc_wdt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(wdt_resources),
+ .resource = wdt_resources,
+};
+
+static void mxc_init_wdt(void)
+{
+ (void)platform_device_register(&mxc_wdt_device);
+}
+#else
+static inline void mxc_init_wdt(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_IPU_V3) || defined(CONFIG_MXC_IPU_V3_MODULE)
+static struct mxc_ipu_config mxc_ipu_data = {
+ .rev = 1,
+};
+
+static struct resource ipu_resources[] = {
+ {
+ .start = IPU_CTRL_BASE_ADDR,
+ .end = IPU_CTRL_BASE_ADDR + SZ_512M,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_IPU_SYN,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MXC_INT_IPU_ERR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mxc_ipu_device = {
+ .name = "mxc_ipu",
+ .id = -1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_ipu_data,
+ },
+ .num_resources = ARRAY_SIZE(ipu_resources),
+ .resource = ipu_resources,
+};
+
+static void mxc_init_ipu(void)
+{
+ mxc_ipu_data.di_clk[1] = clk_get(NULL, "ipu_di_clk");
+
+ platform_device_register(&mxc_ipu_device);
+}
+#else
+static inline void mxc_init_ipu(void)
+{
+}
+#endif
+
+/*!
+ * This is platform device structure for adding SCC
+ */
+#if defined(CONFIG_MXC_SECURITY_SCC) || defined(CONFIG_MXC_SECURITY_SCC_MODULE)
+static struct platform_device mxc_scc_device = {
+ .name = "mxc_scc",
+ .id = 0,
+};
+
+static void mxc_init_scc(void)
+{
+ platform_device_register(&mxc_scc_device);
+}
+#else
+static inline void mxc_init_scc(void)
+{
+ uint32_t reg_value;
+ uint8_t *UMID_base;
+ uint32_t *MAP_base;
+ uint8_t i;
+ uint32_t partition_no;
+ uint32_t scc_partno;
+ void *scm_ram_base;
+ void *scc_base;
+
+ scc_base = ioremap((uint32_t) SCC_BASE_ADDR, 0x140);
+ if (scc_base == NULL) {
+ printk(KERN_ERR "FAILED TO MAP IRAM REGS\n");
+ return;
+ }
+ scm_ram_base = ioremap((uint32_t) IRAM_BASE_ADDR, IRAM_SIZE);
+ if (scm_ram_base == NULL) {
+ printk(KERN_ERR "FAILED TO MAP IRAM\n");
+ return;
+ }
+
+ for (partition_no = 0; partition_no < 9; partition_no++) {
+ reg_value = ((partition_no << SCM_ZCMD_PART_SHIFT) &
+ SCM_ZCMD_PART_MASK) | ((0x03 <<
+ SCM_ZCMD_CCMD_SHIFT)
+ & SCM_ZCMD_CCMD_MASK);
+ __raw_writel(reg_value, scc_base + SCM_ZCMD_REG);
+
+ while ((__raw_readl(scc_base + SCM_STATUS_REG) &
+ SCM_STATUS_SRS_READY) != SCM_STATUS_SRS_READY) ;
+
+ __raw_writel(0, scc_base + (SCM_SMID0_REG + 8 * partition_no));
+
+ reg_value = __raw_readl(scc_base + SCM_PART_OWNERS_REG);
+
+ if (((reg_value >> (2 * (partition_no))) & 3) != 3) {
+ printk(KERN_ERR "FAILED TO ACQUIRE IRAM PARTITION\n");
+ iounmap(scm_ram_base);
+ return;
+ }
+
+ MAP_base = scm_ram_base + (partition_no * 0x2000);
+ UMID_base = (uint8_t *) MAP_base + 0x10;
+
+ for (i = 0; i < 16; i++)
+ UMID_base[i] = 0;
+
+ MAP_base[0] = SCM_PERM_NO_ZEROIZE | SCM_PERM_HD_SUP_DISABLE |
+ SCM_PERM_HD_READ | SCM_PERM_HD_WRITE |
+ SCM_PERM_TH_READ | SCM_PERM_TH_WRITE;
+
+ }
+
+ /*Freeing 2 partitions for SCC2 */
+ scc_partno = 9 - (SCC_IRAM_SIZE / SZ_8K);
+ for (partition_no = scc_partno; partition_no < 9; partition_no++) {
+ reg_value = ((partition_no << SCM_ZCMD_PART_SHIFT) &
+ SCM_ZCMD_PART_MASK) | ((0x03 <<
+ SCM_ZCMD_CCMD_SHIFT)
+ & SCM_ZCMD_CCMD_MASK);
+ __raw_writel(reg_value, scc_base + SCM_ZCMD_REG);
+
+ while ((__raw_readl(scc_base + SCM_STATUS_REG) &
+ SCM_STATUS_SRS_READY) != SCM_STATUS_SRS_READY) ;
+ }
+ iounmap(scm_ram_base);
+ iounmap(scc_base);
+ printk(KERN_INFO "IRAM READY\n");
+
+}
+#endif
+
+/* SPI controller and device data */
+#if defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE)
+
+#ifdef CONFIG_SPI_MXC_SELECT1
+/*!
+ * Resource definition for the CSPI1
+ */
+static struct resource mxcspi1_resources[] = {
+ [0] = {
+ .start = CSPI1_BASE_ADDR,
+ .end = CSPI1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI1,
+ .end = MXC_INT_CSPI1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI1 */
+static struct mxc_spi_master mxcspi1_data = {
+ .maxchipselect = 4,
+ .spi_version = 23,
+};
+
+/*! Device Definition for MXC CSPI1 */
+static struct platform_device mxcspi1_device = {
+ .name = "mxc_spi",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi1_resources),
+ .resource = mxcspi1_resources,
+};
+
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+
+#ifdef CONFIG_SPI_MXC_SELECT2
+/*!
+ * Resource definition for the CSPI2
+ */
+static struct resource mxcspi2_resources[] = {
+ [0] = {
+ .start = CSPI2_BASE_ADDR,
+ .end = CSPI2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI2,
+ .end = MXC_INT_CSPI2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI2 */
+static struct mxc_spi_master mxcspi2_data = {
+ .maxchipselect = 4,
+ .spi_version = 23,
+};
+
+/*! Device Definition for MXC CSPI2 */
+static struct platform_device mxcspi2_device = {
+ .name = "mxc_spi",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi2_resources),
+ .resource = mxcspi2_resources,
+};
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+
+#ifdef CONFIG_SPI_MXC_SELECT3
+/*!
+ * Resource definition for the CSPI3
+ */
+static struct resource mxcspi3_resources[] = {
+ [0] = {
+ .start = CSPI3_BASE_ADDR,
+ .end = CSPI3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI3,
+ .end = MXC_INT_CSPI3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI3 */
+static struct mxc_spi_master mxcspi3_data = {
+ .maxchipselect = 4,
+ .spi_version = 23,
+};
+
+/*! Device Definition for MXC CSPI3 */
+static struct platform_device mxcspi3_device = {
+ .name = "mxc_spi",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi3_resources),
+ .resource = mxcspi3_resources,
+};
+#endif /* CONFIG_SPI_MXC_SELECT3 */
+
+void __init mxc_init_spi(void)
+{
+ /* SPBA configuration for CSPI2 - MCU is set */
+ spba_take_ownership(SPBA_CSPI2, SPBA_MASTER_A);
+#ifdef CONFIG_SPI_MXC_SELECT1
+ if (platform_device_register(&mxcspi1_device) < 0)
+ printk("Error: Registering the SPI Controller_1\n");
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+#ifdef CONFIG_SPI_MXC_SELECT2
+ if (platform_device_register(&mxcspi2_device) < 0)
+ printk("Error: Registering the SPI Controller_2\n");
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+#ifdef CONFIG_SPI_MXC_SELECT3
+ if (platform_device_register(&mxcspi3_device) < 0)
+ printk("Error: Registering the SPI Controller_3\n");
+#endif /* CONFIG_SPI_MXC_SELECT3 */
+}
+#else
+void __init mxc_init_spi(void)
+{
+}
+#endif
+
+/* I2C controller and device data */
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+/*!
+ * Resource definition for the I2C1
+ */
+static struct resource mxci2c1_resources[] = {
+ [0] = {
+ .start = I2C_BASE_ADDR,
+ .end = I2C_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C,
+ .end = MXC_INT_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c1_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+#ifdef CONFIG_I2C_MXC_SELECT2
+/*!
+ * Resource definition for the I2C2
+ */
+static struct resource mxci2c2_resources[] = {
+ [0] = {
+ .start = I2C2_BASE_ADDR,
+ .end = I2C2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C2,
+ .end = MXC_INT_I2C2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c2_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+#ifdef CONFIG_I2C_MXC_SELECT3
+/*!
+ * Resource definition for the I2C3
+ */
+static struct resource mxci2c3_resources[] = {
+ [0] = {
+ .start = I2C3_BASE_ADDR,
+ .end = I2C3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C3,
+ .end = MXC_INT_I2C3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c3_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+/*! Device Definition for MXC I2C1 */
+static struct platform_device mxci2c_devices[] = {
+#ifdef CONFIG_I2C_MXC_SELECT1
+ {
+ .name = "mxc_i2c",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c1_resources),
+ .resource = mxci2c1_resources,},
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT2
+ {
+ .name = "mxc_i2c",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c2_resources),
+ .resource = mxci2c2_resources,},
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT3
+ {
+ .name = "mxc_i2c",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c3_resources),
+ .resource = mxci2c3_resources,},
+#endif
+};
+
+static inline void mxc_init_i2c(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxci2c_devices); i++) {
+ if (platform_device_register(&mxci2c_devices[i]) < 0)
+ dev_err(&mxci2c_devices[i].dev,
+ "Unable to register I2C device\n");
+ }
+}
+#else
+static inline void mxc_init_i2c(void)
+{
+}
+#endif
+
+struct tve_platform_data tve_data = {
+ .dac_reg = "VVIDEO",
+ .dig_reg = "VDIG",
+};
+
+static struct resource tve_resources[] = {
+ {
+ .start = TVE_BASE_ADDR,
+ .end = TVE_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_TVOUT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mxc_tve_device = {
+ .name = "tve",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &tve_data,
+ },
+ .num_resources = ARRAY_SIZE(tve_resources),
+ .resource = tve_resources,
+};
+
+void __init mxc_init_tve(void)
+{
+ platform_device_register(&mxc_tve_device);
+}
+
+/*!
+ * Resource definition for the DVFS CORE
+ */
+static struct resource dvfs_core_resources[] = {
+ [0] = {
+ .start = MXC_DVFS_CORE_BASE,
+ .end = MXC_DVFS_CORE_BASE + 4 * SZ_16 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_GPC1,
+ .end = MXC_INT_GPC1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for DVFS CORE */
+struct mxc_dvfs_platform_data dvfs_core_data = {
+ .reg_id = "SW1",
+ .clk1_id = "cpu_clk",
+ .clk2_id = "gpc_dvfs_clk",
+ .gpc_cntr_reg_addr = MXC_GPC_CNTR,
+ .gpc_vcr_reg_addr = MXC_GPC_VCR,
+ .ccm_cdcr_reg_addr = MXC_CCM_CDCR,
+ .ccm_cacrr_reg_addr = MXC_CCM_CACRR,
+ .ccm_cdhipr_reg_addr = MXC_CCM_CDHIPR,
+ .dvfs_thrs_reg_addr = MXC_DVFSTHRS,
+ .dvfs_coun_reg_addr = MXC_DVFSCOUN,
+ .dvfs_emac_reg_addr = MXC_DVFSEMAC,
+ .dvfs_cntr_reg_addr = MXC_DVFSCNTR,
+ .prediv_mask = 0x3800,
+ .prediv_offset = 11,
+ .prediv_val = 1,
+ .div3ck_mask = 0x00000006,
+ .div3ck_offset = 1,
+ .div3ck_val = 3,
+ .emac_val = 0x08,
+ .upthr_val = 30,
+ .dnthr_val = 10,
+ .pncthr_val = 33,
+ .upcnt_val = 5,
+ .dncnt_val = 5,
+ .delay_time = 100,
+ .num_wp = 3,
+};
+
+/*! Device Definition for MXC DVFS core */
+static struct platform_device mxc_dvfs_core_device = {
+ .name = "mxc_dvfs_core",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &dvfs_core_data,
+ },
+ .num_resources = ARRAY_SIZE(dvfs_core_resources),
+ .resource = dvfs_core_resources,
+};
+
+static inline void mxc_init_dvfs(void)
+{
+ if (platform_device_register(&mxc_dvfs_core_device) < 0)
+ dev_err(&mxc_dvfs_core_device.dev,
+ "Unable to register DVFS core device\n");
+}
+
+/*!
+ * Resource definition for the DPTC GP
+ */
+static struct resource dptc_gp_resources[] = {
+ [0] = {
+ .start = MXC_DPTC_GP_BASE,
+ .end = MXC_DPTC_GP_BASE + 8 * SZ_16 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_GPC1,
+ .end = MXC_INT_GPC1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for DPTC GP */
+struct mxc_dptc_data dptc_gp_data = {
+ .reg_id = "SW1",
+ .clk_id = "cpu_clk",
+ .dptccr_reg_addr = MXC_GP_DPTCCR,
+ .dcvr0_reg_addr = MXC_GP_DCVR0,
+ .gpc_cntr_reg_addr = MXC_GPC_CNTR,
+ .dptccr = MXC_GPCCNTR_DPTC0CR,
+ .dptc_wp_supported = DPTC_GP_WP_SUPPORTED,
+ .dptc_wp_allfreq = dptc_gp_wp_allfreq,
+ .clk_max_val = 532000000,
+ .gpc_adu = MXC_GPCCNTR_ADU,
+ .vai_mask = MXC_DPTCCR_VAI_MASK,
+ .vai_offset = MXC_DPTCCR_VAI_OFFSET,
+ .dptc_enable_bit = MXC_DPTCCR_DEN,
+ .irq_mask = MXC_DPTCCR_VAIM,
+ .dptc_nvcr_bit = MXC_DPTCCR_DPNVCR,
+ .gpc_irq_bit = MXC_GPCCNTR_GPCIRQ,
+ .init_config =
+ MXC_DPTCCR_DRCE0 | MXC_DPTCCR_DRCE1 | MXC_DPTCCR_DRCE2 |
+ MXC_DPTCCR_DRCE3 | MXC_DPTCCR_DCR_128 | MXC_DPTCCR_DPNVCR |
+ MXC_DPTCCR_DPVV,
+ .enable_config =
+ MXC_DPTCCR_DEN | MXC_DPTCCR_DPNVCR | MXC_DPTCCR_DPVV |
+ MXC_DPTCCR_DSMM,
+ .dcr_mask = MXC_DPTCCR_DCR_256,
+};
+
+/*!
+ * Resource definition for the DPTC LP
+ */
+static struct resource dptc_lp_resources[] = {
+ [0] = {
+ .start = MXC_DPTC_LP_BASE,
+ .end = MXC_DPTC_LP_BASE + 8 * SZ_16 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_GPC1,
+ .end = MXC_INT_GPC1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC DPTC LP */
+struct mxc_dptc_data dptc_lp_data = {
+ .reg_id = "SW2",
+ .clk_id = "ahb_clk",
+ .dptccr_reg_addr = MXC_LP_DPTCCR,
+ .dcvr0_reg_addr = MXC_LP_DCVR0,
+ .gpc_cntr_reg_addr = MXC_GPC_CNTR,
+ .dptccr = MXC_GPCCNTR_DPTC1CR,
+ .dptc_wp_supported = DPTC_LP_WP_SUPPORTED,
+ .dptc_wp_allfreq = dptc_lp_wp_allfreq,
+ .clk_max_val = 133000000,
+ .gpc_adu = 0x0,
+ .vai_mask = MXC_DPTCCR_VAI_MASK,
+ .vai_offset = MXC_DPTCCR_VAI_OFFSET,
+ .dptc_enable_bit = MXC_DPTCCR_DEN,
+ .irq_mask = MXC_DPTCCR_VAIM,
+ .dptc_nvcr_bit = MXC_DPTCCR_DPNVCR,
+ .gpc_irq_bit = MXC_GPCCNTR_GPCIRQ,
+ .init_config =
+ MXC_DPTCCR_DRCE0 | MXC_DPTCCR_DRCE1 | MXC_DPTCCR_DRCE2 |
+ MXC_DPTCCR_DRCE3 | MXC_DPTCCR_DCR_128 | MXC_DPTCCR_DPNVCR |
+ MXC_DPTCCR_DPVV,
+ .enable_config =
+ MXC_DPTCCR_DEN | MXC_DPTCCR_DPNVCR | MXC_DPTCCR_DPVV |
+ MXC_DPTCCR_DSMM,
+ .dcr_mask = MXC_DPTCCR_DCR_256,
+};
+
+/*! Device Definition for MXC DPTC */
+static struct platform_device mxc_dptc_devices[] = {
+ {
+ .name = "mxc_dptc",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &dptc_gp_data,
+ },
+ .num_resources = ARRAY_SIZE(dptc_gp_resources),
+ .resource = dptc_gp_resources,
+ },
+ {
+ .name = "mxc_dptc",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &dptc_lp_data,
+ },
+ .num_resources = ARRAY_SIZE(dptc_lp_resources),
+ .resource = dptc_lp_resources,
+ },
+};
+
+static inline void mxc_init_dptc(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxc_dptc_devices); i++) {
+ if (platform_device_register(&mxc_dptc_devices[i]) < 0)
+ dev_err(&mxc_dptc_devices[i].dev,
+ "Unable to register DPTC device\n");
+ }
+}
+
+struct mxc_gpio_port mxc_gpio_ports[GPIO_PORT_NUM] = {
+ [0] = {
+ .chip.label = "gpio-0",
+ .base = IO_ADDRESS(GPIO1_BASE_ADDR),
+ .irq = MXC_INT_GPIO1_LOW,
+ .irq_high = MXC_INT_GPIO1_HIGH,
+ .virtual_irq_start = MXC_GPIO_INT_BASE
+ },
+ [1] = {
+ .chip.label = "gpio-1",
+ .base = IO_ADDRESS(GPIO2_BASE_ADDR),
+ .irq = MXC_INT_GPIO2_LOW,
+ .irq_high = MXC_INT_GPIO2_HIGH,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN
+ },
+ [2] = {
+ .chip.label = "gpio-2",
+ .base = IO_ADDRESS(GPIO3_BASE_ADDR),
+ .irq = MXC_INT_GPIO3_LOW,
+ .irq_high = MXC_INT_GPIO3_HIGH,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 2
+ }
+};
+
+int __init mxc_register_gpios(void)
+{
+ return mxc_gpio_init(mxc_gpio_ports, ARRAY_SIZE(mxc_gpio_ports));
+}
+
+#if defined(CONFIG_MXC_VPU) || defined(CONFIG_MXC_VPU_MODULE)
+static struct resource vpu_resources[] = {
+ [0] = {
+ .start = VPU_IRAM_BASE_ADDR,
+ .end = VPU_IRAM_BASE_ADDR + VPU_IRAM_SIZE,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IO_ADDRESS(SRC_BASE_ADDR),
+ .end = IO_ADDRESS(SRC_BASE_ADDR),
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+/*! Platform Data for MXC VPU */
+static struct platform_device mxcvpu_device = {
+ .name = "mxc_vpu",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(vpu_resources),
+ .resource = vpu_resources,
+};
+
+static inline void mxc_init_vpu(void)
+{
+ if (platform_device_register(&mxcvpu_device) < 0)
+ printk(KERN_ERR "Error: Registering the VPU.\n");
+}
+#else
+static inline void mxc_init_vpu(void)
+{
+}
+#endif
+
+static struct platform_device mxc_dma_device = {
+ .name = "mxc_dma",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline void mxc_init_dma(void)
+{
+ (void)platform_device_register(&mxc_dma_device);
+}
+
+static struct resource spdif_resources[] = {
+ {
+ .start = SPDIF_BASE_ADDR,
+ .end = SPDIF_BASE_ADDR + 0x50,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct mxc_spdif_platform_data mxc_spdif_data = {
+ .spdif_tx = 1,
+ .spdif_rx = 0,
+ .spdif_clk_44100 = 0,
+ .spdif_clk_48000 = 3,
+ .spdif_clk = NULL,
+ .spdif_core_clk = NULL,
+};
+
+static struct platform_device mxc_alsa_spdif_device = {
+ .name = "mxc_alsa_spdif",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_spdif_data,
+ },
+ .num_resources = ARRAY_SIZE(spdif_resources),
+ .resource = spdif_resources,
+};
+
+static inline void mxc_init_spdif(void)
+{
+ struct clk *ckih_clk;
+ ckih_clk = clk_get(NULL, "ckih");
+ mxc_spdif_data.spdif_core_clk = clk_get(NULL, "spdif_xtal_clk");
+ clk_set_parent(mxc_spdif_data.spdif_core_clk, ckih_clk);
+ clk_put(ckih_clk);
+ clk_put(mxc_spdif_data.spdif_core_clk);
+ platform_device_register(&mxc_alsa_spdif_device);
+}
+
+static struct platform_device mx37_lpmode_device = {
+ .name = "mx37_lpmode",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline void mx37_init_lpmode(void)
+{
+ (void)platform_device_register(&mx37_lpmode_device);
+}
+
+static struct platform_device busfreq_device = {
+ .name = "busfreq",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline void mxc_init_busfreq(void)
+{
+ (void)platform_device_register(&busfreq_device);
+}
+
+#if defined(CONFIG_HW_RANDOM_FSL_RNGC) || \
+defined(CONFIG_HW_RANDOM_FSL_RNGC_MODULE)
+static struct resource rngc_resources[] = {
+ {
+ .start = RNGC_BASE_ADDR,
+ .end = RNGC_BASE_ADDR + 0x34,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_RNG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device fsl_rngc_device = {
+ .name = "fsl_rngc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(rngc_resources),
+ .resource = rngc_resources,
+};
+
+static inline void mxc_init_rngc(void)
+{
+ platform_device_register(&fsl_rngc_device);
+}
+#else
+static inline void mxc_init_rngc(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_IIM) || defined(CONFIG_MXC_IIM_MODULE)
+static struct resource mxc_iim_resources[] = {
+ {
+ .start = IIM_BASE_ADDR,
+ .end = IIM_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_iim_device = {
+ .name = "mxc_iim",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(mxc_iim_resources),
+ .resource = mxc_iim_resources
+};
+
+static inline void mxc_init_iim(void)
+{
+ if (platform_device_register(&mxc_iim_device) < 0)
+ dev_err(&mxc_iim_device.dev,
+ "Unable to register mxc iim device\n");
+}
+#else
+static inline void mxc_init_iim(void)
+{
+}
+#endif
+
+int __init mxc_init_srpgconfig(void)
+{
+ struct clk *gpcclk = clk_get(NULL, "gpc_dvfs_clk");
+ clk_enable(gpcclk);
+
+ /* Setup the number of clock cycles to wait for SRPG
+ * power up and power down requests.
+ */
+ __raw_writel(0x03023030, MXC_SRPGC_ARM_PUPSCR);
+ __raw_writel(0x50, MXC_EMPGC0_ARM_PUPSCR);
+
+ __raw_writel(0x30033030, MXC_SRPGC_ARM_PDNSCR);
+ __raw_writel(0x50, MXC_EMPGC0_ARM_PDNSCR);
+
+ clk_disable(gpcclk);
+ clk_put(gpcclk);
+
+ return 0;
+}
+
+int __init mxc_init_devices(void)
+{
+ mxc_init_wdt();
+ mxc_init_ipu();
+ mxc_init_spi();
+ mxc_init_i2c();
+ mxc_init_rtc();
+ mxc_init_owire();
+ mxc_init_scc();
+ mxc_init_dma();
+ mxc_init_vpu();
+ mxc_init_spdif();
+ mxc_init_tve();
+ mx37_init_lpmode();
+ mxc_init_busfreq();
+ mxc_init_dvfs();
+ mxc_init_dptc();
+ mxc_init_rngc();
+ mxc_init_iim();
+
+ /* SPBA configuration for SSI2 - SDMA and MCU are set */
+ spba_take_ownership(SPBA_SSI2, SPBA_MASTER_C | SPBA_MASTER_A);
+ return 0;
+}
diff --git a/arch/arm/mach-mx37/dma.c b/arch/arm/mach-mx37/dma.c
new file mode 100644
index 000000000000..5732c5803888
--- /dev/null
+++ b/arch/arm/mach-mx37/dma.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/device.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+
+#include "serial.h"
+
+#define MXC_MMC_BUFFER_ACCESS 0x20
+#define MXC_SDHC_MMC_WML 512
+#define MXC_SDHC_SD_WML 512
+#define MXC_SSI_TX0_REG 0x0
+#define MXC_SSI_TX1_REG 0x4
+#define MXC_SSI_RX0_REG 0x8
+#define MXC_SSI_RX1_REG 0xC
+#define MXC_SSI_TXFIFO_WML 0x4
+#define MXC_SSI_RXFIFO_WML 0x6
+#define MXC_SPDIF_TXFIFO_WML 0x0
+#define MXC_SPDIF_TX_REG 0x2C
+
+typedef struct mxc_sdma_info_entry_s {
+ mxc_dma_device_t device;
+ mxc_sdma_channel_params_t *chnl_info;
+} mxc_sdma_info_entry_t;
+
+static mxc_sdma_channel_params_t mxc_sdma_uart1_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART1_UFCR_RXTL,
+ .per_address = UART1_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART1_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART1_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart1_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART1_UFCR_TXTL,
+ .per_address = UART1_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART1_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART1_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart2_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART2_UFCR_RXTL,
+ .per_address = UART2_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART2_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART2_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart2_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART2_UFCR_TXTL,
+ .per_address = UART2_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART2_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART2_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart3_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART3_UFCR_RXTL,
+ .per_address = UART3_BASE_ADDR,
+ .peripheral_type = UART_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART3_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART3_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart3_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART3_UFCR_TXTL,
+ .per_address = UART3_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART3_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART3_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc1_width1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_MMC_WML,
+ .per_address =
+ MMC_SDHC1_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC1,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc1_width4_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_SD_WML,
+ .per_address =
+ MMC_SDHC1_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC1,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc2_width1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_MMC_WML / 32,
+ .per_address =
+ MMC_SDHC2_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC2,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc2_width4_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_SD_WML / 8,
+ .per_address =
+ MMC_SDHC2_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC2,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_memory_params = {
+ .chnl_params = {
+ .peripheral_type = MEMORY,
+ .transfer_type = emi_2_emi,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MEMORY,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ata_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_IDE_DMA_WATERMARK,
+ .per_address = ATA_DMA_BASE_ADDR,
+ .peripheral_type = ATA,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_ATA_TX_END,
+ .event_id2 = DMA_REQ_ATA_RX,
+ .bd_number = MXC_IDE_DMA_BD_NR,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ATA_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ata_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_IDE_DMA_WATERMARK,
+ .per_address = ATA_DMA_BASE_ADDR + 0x18,
+ .peripheral_type = ATA,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_ATA_TX_END,
+ .event_id2 = DMA_REQ_ATA_TX,
+ .bd_number = MXC_IDE_DMA_BD_NR,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ATA_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_spdif_16bit_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SPDIF_TXFIFO_WML,
+ .per_address = SPDIF_BASE_ADDR + MXC_SPDIF_TX_REG,
+ .peripheral_type = SPDIF,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SPDIF_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SPDIF_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_spdif_32bit_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SPDIF_TXFIFO_WML,
+ .per_address = SPDIF_BASE_ADDR + MXC_SPDIF_TX_REG,
+ .peripheral_type = SPDIF,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SPDIF_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SPDIF_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_info_entry_t mxc_sdma_active_dma_info[] = {
+ {MXC_DMA_UART1_RX, &mxc_sdma_uart1_rx_params},
+ {MXC_DMA_UART1_TX, &mxc_sdma_uart1_tx_params},
+ {MXC_DMA_UART2_RX, &mxc_sdma_uart2_rx_params},
+ {MXC_DMA_UART2_TX, &mxc_sdma_uart2_tx_params},
+ {MXC_DMA_UART3_RX, &mxc_sdma_uart3_rx_params},
+ {MXC_DMA_UART3_TX, &mxc_sdma_uart3_tx_params},
+ {MXC_DMA_MMC1_WIDTH_1, &mxc_sdma_mmc1_width1_params},
+ {MXC_DMA_MMC1_WIDTH_4, &mxc_sdma_mmc1_width4_params},
+ {MXC_DMA_MMC2_WIDTH_1, &mxc_sdma_mmc2_width1_params},
+ {MXC_DMA_MMC2_WIDTH_4, &mxc_sdma_mmc2_width4_params},
+ {MXC_DMA_SSI1_8BIT_RX0, &mxc_sdma_ssi1_8bit_rx0_params},
+ {MXC_DMA_SSI1_8BIT_TX0, &mxc_sdma_ssi1_8bit_tx0_params},
+ {MXC_DMA_SSI1_16BIT_RX0, &mxc_sdma_ssi1_16bit_rx0_params},
+ {MXC_DMA_SSI1_16BIT_TX0, &mxc_sdma_ssi1_16bit_tx0_params},
+ {MXC_DMA_SSI1_24BIT_RX0, &mxc_sdma_ssi1_24bit_rx0_params},
+ {MXC_DMA_SSI1_24BIT_TX0, &mxc_sdma_ssi1_24bit_tx0_params},
+ {MXC_DMA_SSI1_8BIT_RX1, &mxc_sdma_ssi1_8bit_rx1_params},
+ {MXC_DMA_SSI1_8BIT_TX1, &mxc_sdma_ssi1_8bit_tx1_params},
+ {MXC_DMA_SSI1_16BIT_RX1, &mxc_sdma_ssi1_16bit_rx1_params},
+ {MXC_DMA_SSI1_16BIT_TX1, &mxc_sdma_ssi1_16bit_tx1_params},
+ {MXC_DMA_SSI1_24BIT_RX1, &mxc_sdma_ssi1_24bit_rx1_params},
+ {MXC_DMA_SSI1_24BIT_TX1, &mxc_sdma_ssi1_24bit_tx1_params},
+ {MXC_DMA_SSI2_8BIT_RX0, &mxc_sdma_ssi2_8bit_rx0_params},
+ {MXC_DMA_SSI2_8BIT_TX0, &mxc_sdma_ssi2_8bit_tx0_params},
+ {MXC_DMA_SSI2_16BIT_RX0, &mxc_sdma_ssi2_16bit_rx0_params},
+ {MXC_DMA_SSI2_16BIT_TX0, &mxc_sdma_ssi2_16bit_tx0_params},
+ {MXC_DMA_SSI2_24BIT_RX0, &mxc_sdma_ssi2_24bit_rx0_params},
+ {MXC_DMA_SSI2_24BIT_TX0, &mxc_sdma_ssi2_24bit_tx0_params},
+ {MXC_DMA_SSI2_8BIT_RX1, &mxc_sdma_ssi2_8bit_rx1_params},
+ {MXC_DMA_SSI2_8BIT_TX1, &mxc_sdma_ssi2_8bit_tx1_params},
+ {MXC_DMA_SSI2_16BIT_RX1, &mxc_sdma_ssi2_16bit_rx1_params},
+ {MXC_DMA_SSI2_16BIT_TX1, &mxc_sdma_ssi2_16bit_tx1_params},
+ {MXC_DMA_SSI2_24BIT_RX1, &mxc_sdma_ssi2_24bit_rx1_params},
+ {MXC_DMA_SSI2_24BIT_TX1, &mxc_sdma_ssi2_24bit_tx1_params},
+ {MXC_DMA_MEMORY, &mxc_sdma_memory_params},
+ {MXC_DMA_ATA_RX, &mxc_sdma_ata_rx_params},
+ {MXC_DMA_ATA_TX, &mxc_sdma_ata_tx_params},
+ {MXC_DMA_SPDIF_16BIT_TX, &mxc_sdma_spdif_16bit_tx_params},
+ {MXC_DMA_SPDIF_32BIT_TX, &mxc_sdma_spdif_32bit_tx_params},
+};
+
+static int mxc_sdma_info_entrys =
+ sizeof(mxc_sdma_active_dma_info) / sizeof(mxc_sdma_active_dma_info[0]);
+
+/*!
+ * This functions Returns the SDMA paramaters associated for a module
+ *
+ * @param channel_id the ID of the module requesting DMA
+ * @return returns the sdma parameters structure for the device
+ */
+mxc_sdma_channel_params_t *mxc_sdma_get_channel_params(mxc_dma_device_t
+ channel_id)
+{
+ mxc_sdma_info_entry_t *p = mxc_sdma_active_dma_info;
+ int i;
+
+ for (i = 0; i < mxc_sdma_info_entrys; i++, p++) {
+ if (p->device == channel_id) {
+ return p->chnl_info;
+ }
+ }
+ return NULL;
+}
+
+/*!
+ * This functions marks the SDMA channels that are statically allocated
+ *
+ * @param chnl the channel array used to store channel information
+ */
+void mxc_get_static_channels(mxc_dma_channel_t * chnl)
+{
+#ifdef CONFIG_SDMA_IRAM
+ int i;
+ for (i = MXC_DMA_CHANNEL_IRAM; i < MAX_DMA_CHANNELS; i++)
+ chnl[i].dynamic = 0;
+#endif
+}
+
+EXPORT_SYMBOL(mxc_sdma_get_channel_params);
+EXPORT_SYMBOL(mxc_get_static_channels);
diff --git a/arch/arm/mach-mx37/dptc.c b/arch/arm/mach-mx37/dptc.c
new file mode 100644
index 000000000000..4585423c0036
--- /dev/null
+++ b/arch/arm/mach-mx37/dptc.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file dptc_gp.c
+ *
+ * @brief DPTC table for the Freescale Semiconductor MXC DPTC module.
+ *
+ * @ingroup PM
+ */
+
+#include <mach/hardware.h>
+#include <mach/mxc_dptc.h>
+
+struct dptc_wp dptc_gp_wp_allfreq[DPTC_GP_WP_SUPPORTED] = {
+ /* 532MHz */
+ /* dcvr0 dcvr1 dcvr2
+ dcvr3 voltage */
+ /* wp0 */
+ {DCVR(107, 108, 112), DCVR(122, 123, 127), DCVR(133, 134, 139),
+ DCVR(115, 116, 121), 1000},
+ {DCVR(107, 108, 113), DCVR(122, 123, 127), DCVR(133, 134, 139),
+ DCVR(115, 117, 122), 975},
+ {DCVR(107, 109, 113), DCVR(122, 123, 127), DCVR(133, 134, 139),
+ DCVR(115, 117, 122), 950},
+ {DCVR(107, 109, 114), DCVR(122, 123, 127), DCVR(133, 135, 140),
+ DCVR(115, 117, 122), 925},
+ {DCVR(108, 109, 115), DCVR(122, 123, 127), DCVR(133, 136, 142),
+ DCVR(115, 117, 123), 900},
+ {DCVR(108, 110, 115), DCVR(122, 123, 127), DCVR(133, 136, 142),
+ DCVR(115, 117, 123), 875},
+ {DCVR(108, 110, 115), DCVR(122, 124, 128), DCVR(133, 136, 143),
+ DCVR(115, 118, 124), 850},
+};
+
+struct dptc_wp dptc_lp_wp_allfreq[DPTC_LP_WP_SUPPORTED] = {
+ /* 532MHz */
+ /* dcvr0 dcvr1 dcvr2
+ dcvr3 regulator voltage */
+ /* wp0 */
+ {DCVR(141, 143, 149), DCVR(155, 157, 162), DCVR(106, 108, 112),
+ DCVR(124, 126, 130), 1200},
+ {DCVR(141, 143, 149), DCVR(155, 157, 162), DCVR(106, 108, 113),
+ DCVR(124, 126, 131), 1175},
+ {DCVR(141, 144, 150), DCVR(155, 157, 163), DCVR(106, 108, 113),
+ DCVR(124, 126, 131), 1150},
+ {DCVR(141, 144, 151), DCVR(155, 157, 163), DCVR(106, 108, 114),
+ DCVR(124, 126, 131), 1125},
+ {DCVR(142, 144, 152), DCVR(155, 157, 163), DCVR(107, 109, 114),
+ DCVR(125, 127, 132), 1100},
+ {DCVR(142, 145, 153), DCVR(155, 157, 164), DCVR(107, 109, 115),
+ DCVR(125, 127, 133), 1075},
+ {DCVR(142, 145, 153), DCVR(155, 158, 164), DCVR(107, 109, 116),
+ DCVR(125, 127, 133), 1050},
+ {DCVR(142, 145, 154), DCVR(155, 158, 165), DCVR(107, 110, 117),
+ DCVR(125, 127, 134), 1025},
+ {DCVR(142, 146, 156), DCVR(155, 158, 165), DCVR(107, 110, 117),
+ DCVR(125, 128, 135), 1000},
+};
diff --git a/arch/arm/mach-mx37/iomux.c b/arch/arm/mach-mx37/iomux.c
new file mode 100644
index 000000000000..3d768bb9c9e3
--- /dev/null
+++ b/arch/arm/mach-mx37/iomux.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup GPIO_MX37 Board GPIO and Muxing Setup
+ * @ingroup MSL_MX37
+ */
+/*!
+ * @file mach-mx37/iomux.c
+ *
+ * @brief I/O Muxing control functions
+ *
+ * @ingroup GPIO_MX37
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include "iomux.h"
+
+/*!
+ * IOMUX register (base) addresses
+ */
+enum iomux_reg_addr {
+ IOMUXGPR0 = IO_ADDRESS(IOMUXC_BASE_ADDR), /*!< General purpose 0 */
+ IOMUXGPR1 = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x004, /*!< General purpose 1 */
+ IOMUXSW_MUX_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + MUX_I_START, /*!< MUX control */
+ IOMUXSW_MUX_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + PAD_I_END, /*!< last MUX control register */
+ IOMUXSW_PAD_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + PAD_I_START, /*!< Pad control */
+ IOMUXSW_PAD_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + PAD_I_END, /*!< last Pad control register */
+ IOMUXSW_INPUT_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + INPUT_CTL_START, /*!< input select register */
+ IOMUXSW_INPUT_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + INPUT_CTL_END, /*!< last input select register */
+};
+
+#define MUX_PIN_NUM_MAX (((IOMUXSW_MUX_END - IOMUXSW_MUX_CTL) >> 2) + 1)
+#define MUX_INPUT_NUM_MUX (((IOMUXSW_INPUT_END - IOMUXSW_INPUT_CTL) >> 2) + 1)
+
+static u8 iomux_pin_res_table[MUX_PIN_NUM_MAX];
+static DEFINE_SPINLOCK(gpio_mux_lock);
+
+/*!
+ * This function is used to configure a pin through the IOMUX module.
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config a configuration as defined in \b #iomux_pin_cfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+static int iomux_config_mux(iomux_pin_name_t pin, iomux_pin_cfg_t config)
+{
+ u32 ret = 0;
+ u32 pin_index = PIN_TO_IOMUX_INDEX(pin);
+ u32 mux_reg = IOMUXSW_MUX_CTL + PIN_TO_IOMUX_MUX(pin);
+ u32 mux_data = 0;
+ u8 *rp;
+
+ BUG_ON((mux_reg > IOMUXSW_MUX_END) || (mux_reg < IOMUXSW_MUX_CTL));
+ spin_lock(&gpio_mux_lock);
+
+ if (config == IOMUX_CONFIG_GPIO) {
+ mux_data = PIN_TO_ALT_GPIO(pin);
+ } else {
+ mux_data = config;
+ }
+
+ __raw_writel(mux_data, mux_reg);
+
+ /*
+ * Log a warning if a pin changes ownership
+ */
+ rp = iomux_pin_res_table + pin_index;
+ if ((mux_data & *rp) && (*rp != mux_data)) {
+ /*
+ * Don't call printk if we're tweaking the console uart or
+ * we'll deadlock.
+ */
+ printk(KERN_ERR "iomux_config_mux: Warning: iomux pin"
+ " config changed, pin=%d, "
+ " prev=0x%x new=0x%x\n", mux_reg, *rp, mux_data);
+ ret = -EINVAL;
+ }
+ *rp = mux_data;
+ spin_unlock(&gpio_mux_lock);
+ return ret;
+}
+
+/*!
+ * Request ownership for an IO pin. This function has to be the first one
+ * being called before that pin is used. The caller has to check the
+ * return value to make sure it returns 0.
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param config a configuration as defined in \b #iomux_pin_cfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t config)
+{
+ int ret = iomux_config_mux(pin, config);
+ int gpio_port = GPIO_TO_PORT(IOMUX_TO_GPIO(pin));
+
+ if (!ret && (gpio_port != NON_GPIO_PORT)
+ && ((config == IOMUX_CONFIG_GPIO)
+ || (config == PIN_TO_ALT_GPIO(pin)))) {
+ ret |= gpio_request(IOMUX_TO_GPIO(pin), NULL);
+ }
+ return ret;
+}
+
+/*!
+ * Release ownership for an IO pin
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param config config as defined in \b #iomux_pin_ocfg_t
+ */
+void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t config)
+{
+ u32 pin_index = PIN_TO_IOMUX_INDEX(pin);
+ u8 *rp = iomux_pin_res_table + pin_index;
+ int gpio_port = GPIO_TO_PORT(IOMUX_TO_GPIO(pin));
+
+ BUG_ON(pin_index > MUX_PIN_NUM_MAX);
+ *rp = 0;
+ if ((gpio_port != NON_GPIO_PORT)
+ && ((config == IOMUX_CONFIG_GPIO)
+ || (config == PIN_TO_ALT_GPIO(pin)))) {
+ gpio_free(IOMUX_TO_GPIO(pin));
+ }
+}
+
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config the ORed value of elements defined in \b #iomux_pad_config_t
+ */
+void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config)
+{
+ u32 pad_reg = IOMUXSW_PAD_CTL + PIN_TO_IOMUX_PAD(pin);
+
+ BUG_ON((pad_reg > IOMUXSW_PAD_END) || (pad_reg < IOMUXSW_PAD_CTL));
+ spin_lock(&gpio_mux_lock);
+ __raw_writel(config, pad_reg);
+ spin_unlock(&gpio_mux_lock);
+}
+
+unsigned int mxc_iomux_get_pad(iomux_pin_name_t pin)
+{
+ volatile u32 pad_reg = IOMUXSW_PAD_CTL + PIN_TO_IOMUX_PAD(pin);
+ return __raw_readl(pad_reg);
+}
+
+/*!
+ * This function enables/disables the general purpose function for a particular
+ * signal.
+ *
+ * @param gp one signal as defined in \b #iomux_gp_func_t
+ * @param en \b #true to enable; \b #false to disable
+ * @param index 0 for GPR0 and 1 for GPR1
+ */
+void mxc_iomux_set_gpr(iomux_gp_func_t gp, bool en, u8 index)
+{
+ volatile u32 l;
+
+ spin_lock(&gpio_mux_lock);
+ l = __raw_readl(IOMUXGPR0 + (index << 2));
+ if (en) {
+ l |= gp;
+ } else {
+ l &= ~gp;
+ }
+ __raw_writel(l, IOMUXGPR0 + (index << 2));
+ spin_unlock(&gpio_mux_lock);
+}
+
+/*!
+ * This function configures input path.
+ *
+ * @param input index of input select register as defined in \b #iomux_input_select_t
+ * @param config the binary value of elements defined in \b #iomux_input_config_t
+ * */
+void mxc_iomux_set_input(iomux_input_select_t input, u32 config)
+{
+ u32 reg = IOMUXSW_INPUT_CTL + (input << 2);
+
+ BUG_ON(input >= MUX_INPUT_NUM_MUX);
+ __raw_writel(config, reg);
+}
+
+EXPORT_SYMBOL(mxc_request_iomux);
+EXPORT_SYMBOL(mxc_free_iomux);
+EXPORT_SYMBOL(mxc_iomux_set_input);
+EXPORT_SYMBOL(mxc_iomux_set_pad);
+EXPORT_SYMBOL(mxc_iomux_set_gpr);
diff --git a/arch/arm/mach-mx37/iomux.h b/arch/arm/mach-mx37/iomux.h
new file mode 100644
index 000000000000..b8f38212da18
--- /dev/null
+++ b/arch/arm/mach-mx37/iomux.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MACH_MX37_IOMUX_H__
+#define __MACH_MX37_IOMUX_H__
+
+#include <linux/types.h>
+#include <mach/gpio.h>
+#include "mx37_pins.h"
+
+/*!
+ * @file mach-mx37/iomux.h
+ *
+ * @brief I/O Muxing control definitions and functions
+ *
+ * @ingroup GPIO_MX37
+ */
+
+typedef unsigned int iomux_pin_name_t;
+
+/*!
+ * various IOMUX output functions
+ */
+typedef enum iomux_config {
+ IOMUX_CONFIG_ALT0, /*!< used as alternate function 0 */
+ IOMUX_CONFIG_ALT1, /*!< used as alternate function 1 */
+ IOMUX_CONFIG_ALT2, /*!< used as alternate function 2 */
+ IOMUX_CONFIG_ALT3, /*!< used as alternate function 3 */
+ IOMUX_CONFIG_ALT4, /*!< used as alternate function 4 */
+ IOMUX_CONFIG_ALT5, /*!< used as alternate function 5 */
+ IOMUX_CONFIG_ALT6, /*!< used as alternate function 6 */
+ IOMUX_CONFIG_ALT7, /*!< used as alternate function 7 */
+ IOMUX_CONFIG_GPIO, /*!< added to help user use GPIO mode */
+ IOMUX_CONFIG_SION = 0x1 << 4, /*!< used as LOOPBACK:MUX SION bit */
+} iomux_pin_cfg_t;
+
+/*!
+ * various IOMUX pad functions
+ */
+typedef enum iomux_pad_config {
+ PAD_CTL_SRE_SLOW = 0x0 << 0,
+ PAD_CTL_SRE_FAST = 0x1 << 0,
+ PAD_CTL_DRV_LOW = 0x0 << 1,
+ PAD_CTL_DRV_MEDIUM = 0x1 << 1,
+ PAD_CTL_DRV_HIGH = 0x2 << 1,
+ PAD_CTL_DRV_MAX = 0x3 << 1,
+ PAD_CTL_ODE_OPENDRAIN_NONE = 0x0 << 3,
+ PAD_CTL_ODE_OPENDRAIN_ENABLE = 0x1 << 3,
+ PAD_CTL_100K_PD = 0x0 << 4,
+ PAD_CTL_47K_PU = 0x1 << 4,
+ PAD_CTL_100K_PU = 0x2 << 4,
+ PAD_CTL_22K_PU = 0x3 << 4,
+ PAD_CTL_PUE_KEEPER = 0x0 << 6,
+ PAD_CTL_PUE_PULL = 0x1 << 6,
+ PAD_CTL_PKE_NONE = 0x0 << 7,
+ PAD_CTL_PKE_ENABLE = 0x1 << 7,
+ PAD_CTL_HYS_NONE = 0x0 << 8,
+ PAD_CTL_HYS_ENABLE = 0x1 << 8,
+ PAD_CTL_DDR_INPUT_CMOS = 0x0 << 9,
+ PAD_CTL_DDR_INPUT_DDR = 0x1 << 9,
+ PAD_CTL_DRV_VOT_LOW = 0x0 << 13,
+ PAD_CTL_DRV_VOT_HIGH = 0x1 << 13,
+} iomux_pad_config_t;
+
+/*!
+ * various IOMUX general purpose functions
+ */
+typedef enum iomux_gp_func {
+ MUX_IPD_ESDHC_DREQ_B = 0x0 << 0,
+ MUX_XDRQ = 0x1 << 0,
+ MUX_EMI_DMA_ACCESS_1 = 0x0 << 4,
+ MUX_KEY_COL2 = 0x1 << 4,
+ MUX_TAMPER_DETECT_EN = 0x1 << 8,
+ MUX_IPUv3D_TVE = 0x0 << 12,
+ MUX_IPUv3D_CAMP = 0x1 << 12,
+} iomux_gp_func_t;
+
+/*!
+ * various IOMUX input select register index
+ */
+typedef enum iomux_input_select {
+ MUX_IN_CCM_PLL1_BYPASS_CLK = 0,
+ MUX_IN_CCM_PLL2_BYPASS_CLK,
+ MUX_IN_CCM_PLL3_BYPASS_CLK,
+ MUX_IN_CSPI3_CSPI_CLK,
+ MUX_IN_CSPI3_MISO,
+ MUX_IN_CSPI3_MOSI,
+ MUX_IN_EMI_READ_MADDR_DATA_0,
+ MUX_IN_EMI_READ_MADDR_DATA_10,
+ MUX_IN_EMI_READ_MADDR_DATA_11,
+ MUX_IN_EMI_READ_MADDR_DATA_12,
+ MUX_IN_EMI_READ_MADDR_DATA_13,
+ MUX_IN_EMI_READ_MADDR_DATA_14,
+ MUX_IN_EMI_READ_MADDR_DATA_15,
+ MUX_IN_EMI_READ_MADDR_DATA_1,
+ MUX_IN_EMI_READ_MADDR_DATA_2,
+ MUX_IN_EMI_READ_MADDR_DATA_3,
+ MUX_IN_EMI_READ_MADDR_DATA_4,
+ MUX_IN_EMI_READ_MADDR_DATA_5,
+ MUX_IN_EMI_READ_MADDR_DATA_6,
+ MUX_IN_EMI_READ_MADDR_DATA_7,
+ MUX_IN_EMI_READ_MADDR_DATA_8,
+ MUX_IN_EMI_READ_MADDR_DATA_9,
+ MUX_IN_EMI_NFC_READ_DATA_IN_0,
+ MUX_IN_EMI_NFC_READ_DATA_IN_10,
+ MUX_IN_EMI_NFC_READ_DATA_IN_11,
+ MUX_IN_EMI_NFC_READ_DATA_IN_12,
+ MUX_IN_EMI_NFC_READ_DATA_IN_13,
+ MUX_IN_EMI_NFC_READ_DATA_IN_14,
+ MUX_IN_EMI_NFC_READ_DATA_IN_15,
+ MUX_IN_EMI_NFC_READ_DATA_IN_1,
+ MUX_IN_EMI_NFC_READ_DATA_IN_2,
+ MUX_IN_EMI_NFC_READ_DATA_IN_3,
+ MUX_IN_EMI_NFC_READ_DATA_IN_4,
+ MUX_IN_EMI_NFC_READ_DATA_IN_5,
+ MUX_IN_EMI_NFC_READ_DATA_IN_6,
+ MUX_IN_EMI_NFC_READ_DATA_IN_7,
+ MUX_IN_EMI_NFC_READ_DATA_IN_8,
+ MUX_IN_EMI_NFC_READ_DATA_IN_9,
+ MUX_IN_FEC_FEC_COL,
+ MUX_IN_FEC_FEC_CRS, MUX_IN_FEC_FEC_MDI,
+ MUX_IN_FEC_FEC_RDATA_0,
+ MUX_IN_FEC_FEC_RX_CLK,
+ MUX_IN_FEC_FEC_RX_DV,
+ MUX_IN_FEC_FEC_RX_ER,
+ MUX_IN_FEC_FEC_TX_CLK,
+ MUX_IN_I2C1_SCL,
+ MUX_IN_I2C1_SDA,
+ MUX_IN_I2C2_SCL,
+ MUX_IN_I2C2_SDA,
+ MUX_IN_I2C3_SCL,
+ MUX_IN_I2C3_SDA,
+ MUX_IN_IPU_DI_0_IND_DISPB_D0_VSYNC,
+ MUX_IN__IPU_DI_0_IND_DISPB_SD_D,
+ MUX_IN_KPP_ROW_0,
+ MUX_IN_KPP_ROW_1,
+ MUX_IN_KPP_ROW_2,
+ MUX_IN_KPP_ROW_3,
+ MUX_IN_KPP_ROW_4,
+ MUX_IN_KPP_ROW_5,
+ MUX_IN_KPP_ROW_6,
+ MUX_IN_KPP_ROW_7,
+ MUX_IN_UART1_UART_RTS_B,
+ MUX_IN_UART1_UART_RXD_MUX,
+ MUX_IN_UART2_UART_RTS_B,
+ MUX_IN_UART2_UART_RXD_MUX,
+ MUX_IN_UART3_UART_RTS_B,
+ MUX_IN_UART3_UART_RXD_MUX,
+} iomux_input_select_t;
+
+/*!
+ * various IOMUX input functions
+ */
+typedef enum iomux_input_config {
+ INPUT_CTL_PATH0 = 0x0,
+ INPUT_CTL_PATH1,
+ INPUT_CTL_PATH2,
+ INPUT_CTL_PATH3,
+ INPUT_CTL_PATH4,
+ INPUT_CTL_PATH5,
+ INPUT_CTL_PATH6,
+ INPUT_CTL_PATH7,
+} iomux_input_config_t;
+
+/*!
+ * Request ownership for an IO pin. This function has to be the first one
+ * being called before that pin is used. The caller has to check the
+ * return value to make sure it returns 0.
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param config config as defined in \b #iomux_pin_ocfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t config);
+
+/*!
+ * Release ownership for an IO pin
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param config config as defined in \b #iomux_pin_ocfg_t
+ */
+void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t config);
+
+/*!
+ * This function enables/disables the general purpose function for a particular
+ * signal.
+ *
+ * @param gp one signal as defined in \b #iomux_gp_func_t
+ * @param en \b #true to enable; \b #false to disable
+ * @param index 0 for GPR0 and 1 for GPR1
+ */
+void mxc_iomux_set_gpr(iomux_gp_func_t gp, bool en, u8 index);
+
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config the ORed value of elements defined in \b #iomux_pad_config_t
+ */
+void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config);
+
+/*!
+ * This function gets the current pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @return current pad value
+ */
+unsigned int mxc_iomux_get_pad(iomux_pin_name_t pin);
+
+/*!
+ * This function configures input path.
+ *
+ * @param input index of input select register as defined in \b #iomux_input_select_t
+ * @param config the binary value of elements defined in \b #iomux_input_config_t
+ */
+void mxc_iomux_set_input(iomux_input_select_t input, u32 config);
+
+#endif /* __MACH_MX37_IOMUX_H__ */
diff --git a/arch/arm/mach-mx37/mm.c b/arch/arm/mach-mx37/mm.c
new file mode 100644
index 000000000000..64a4df695219
--- /dev/null
+++ b/arch/arm/mach-mx37/mm.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <asm/pgtable.h>
+#include <asm/mach/map.h>
+
+/*!
+ * @file mach-mx37/mm.c
+ *
+ * @brief This file creates static mapping between physical to virtual memory.
+ *
+ * @ingroup Memory_MX37
+ */
+
+/*!
+ * This structure defines the MX37 memory map.
+ */
+static struct map_desc mxc_io_desc[] __initdata = {
+ {
+ .virtual = IRAM_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(IRAM_BASE_ADDR),
+ .length = IRAM_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = PLATFORM_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(PLATFORM_BASE_ADDR),
+ .length = PLATFORM_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = DEBUG_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(DEBUG_BASE_ADDR),
+ .length = DEBUG_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = TZIC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(TZIC_BASE_ADDR),
+ .length = TZIC_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = AIPS1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS1_BASE_ADDR),
+ .length = AIPS1_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = SPBA0_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(SPBA0_BASE_ADDR),
+ .length = SPBA0_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = AIPS2_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS2_BASE_ADDR),
+ .length = AIPS2_SIZE,
+ .type = MT_DEVICE_NONSHARED},
+ {
+ .virtual = NFC_BASE_ADDR_AXI_VIRT,
+ .pfn = __phys_to_pfn(NFC_BASE_ADDR_AXI),
+ .length = NFC_AXI_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 mxc_map_io(void)
+{
+ iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
+}
diff --git a/arch/arm/mach-mx37/mx37_3stack.c b/arch/arm/mach-mx37/mx37_3stack.c
new file mode 100644
index 000000000000..4ccacad04691
--- /dev/null
+++ b/arch/arm/mach-mx37/mx37_3stack.c
@@ -0,0 +1,942 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/nodemask.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/ata.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pmic_external.h>
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/mach/flash.h>
+#endif
+
+#include <mach/hardware.h>
+#include <mach/spba.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/common.h>
+#include <mach/memory.h>
+#include <mach/gpio.h>
+#include <mach/mmc.h>
+#include "board-mx37_3stack.h"
+#include "iomux.h"
+#include "crm_regs.h"
+
+/*!
+ * @file mach-mx37/mx37_3stack.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX37
+ */
+extern void gpio_lcd_active(void);
+extern void mxc_init_srpgconfig(void);
+
+/* working point(wp): 0 - 532MHz; 1 - 200MHz; */
+static struct cpu_wp cpu_wp_auto[] = {
+ {
+ .pll_rate = 532000000,
+ .cpu_rate = 532000000,
+ .pdf = 0,
+ .mfi = 5,
+ .mfd = 23,
+ .mfn = 13,
+ .cpu_voltage = 1050000,},
+ {
+ .pll_rate = 200000000,
+ .cpu_rate = 200000000,
+ .pdf = 3,
+ .mfi = 8,
+ .mfd = 2,
+ .mfn = 1,
+ .cpu_voltage = 850000,},
+};
+
+struct cpu_wp *get_cpu_wp(int *wp)
+{
+ *wp = 2;
+ return cpu_wp_auto;
+}
+
+#if defined(CONFIG_REGULATOR_MC13892) \
+ || defined(CONFIG_REGULATOR_MC13892_MODULE)
+static int mc13892_reg_int(void)
+{
+ int i = 0;
+ unsigned int value;
+ struct regulator *regulator;
+ struct cpu_wp *cpu_wp_tbl1;
+ int cpu_wp_nr1;
+ char *reg_name[] = {
+ "SW1",
+ "SW2",
+ "SW3",
+ "SW4",
+ "SW1_STBY",
+ "SW2_STBY",
+ "SW3_STBY",
+ "SW4_STBY",
+ "SW1_DVS",
+ "SW2_DVS",
+ "SWBST",
+ "VIOHI",
+ "VPLL",
+ "VDIG",
+ "VSD",
+ "VUSB2",
+ "VVIDEO",
+ "VAUDIO",
+ "VCAM",
+ "VGEN1",
+ "VGEN2",
+ "VGEN3",
+ "USB",
+ "GPO1",
+ "GPO2",
+ "GPO3",
+ "GPO4",
+ };
+
+ /* for board v1.1 do nothing */
+ if (!board_is_mx37(BOARD_REV_2))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(reg_name); i++) {
+ regulator = regulator_get(NULL, reg_name[i]);
+ if (regulator != ERR_PTR(-ENOENT)) {
+ regulator_enable(regulator);
+ regulator_put(regulator);
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(reg_name); i++) {
+ if ((strcmp(reg_name[i], "VIOHI") == 0) ||
+ (strcmp(reg_name[i], "VPLL") == 0) ||
+ (strcmp(reg_name[i], "VDIG") == 0) ||
+ (strcmp(reg_name[i], "VGEN2") == 0))
+ continue;
+ regulator = regulator_get(NULL, reg_name[i]);
+ if (regulator != ERR_PTR(-ENOENT)) {
+ regulator_disable(regulator);
+ regulator_put(regulator);
+ }
+ }
+
+ /* Set the current working point. */
+ cpu_wp_tbl1 = get_cpu_wp(&cpu_wp_nr1);
+ for (i = 0; i < cpu_wp_nr1; i++)
+ cpu_wp_tbl1[i].cpu_voltage += 50000;
+
+ /* Bit 4 DRM: keep VSRTC and CLK32KMCU on for all states */
+ pmic_read_reg(REG_POWER_CTL0, &value, 0xffffff);
+ value |= 0x000010;
+ pmic_write_reg(REG_POWER_CTL0, value, 0xffffff);
+
+ return 0;
+}
+
+late_initcall(mc13892_reg_int);
+#endif
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+/* MTD NAND flash */
+#if defined(CONFIG_MTD_NAND_MXC) || defined(CONFIG_MTD_NAND_MXC_MODULE) \
+ || defined(CONFIG_MTD_NAND_MXC_V2) || defined(CONFIG_MTD_NAND_MXC_V2_MODULE) \
+ || defined(CONFIG_MTD_NAND_MXC_V3)
+
+static struct mtd_partition mxc_nand_partitions[] = {
+ {
+ .name = "nand.bootloader",
+ .offset = 0,
+ .size = 2 * 1024 * 1024},
+ {
+ .name = "nand.kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 4 * 1024 * 1024},
+ {
+ .name = "nand.rootfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 256 * 1024 * 1024},
+ {
+ .name = "nand.userfs1",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 256 * 1024 * 1024},
+ {
+ .name = "nand.userfs2",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 512 * 1024 * 1024},
+ {
+ .name = "nand.userfs3",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL},
+};
+
+static struct flash_platform_data mxc_nand_data = {
+ .parts = mxc_nand_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_nand_partitions),
+ .width = 1,
+};
+
+static struct platform_device mxc_nandv2_mtd_device = {
+ .name = "mxc_nandv2_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static void mxc_init_nand_mtd(void)
+{
+// if (__raw_readl(MXC_CCM_RCSR) & MXC_CCM_RCSR_NF16B) {
+// mxc_nand_data.width = 2;
+// }
+ (void)platform_device_register(&mxc_nandv2_mtd_device);
+}
+#else
+static inline void mxc_init_nand_mtd(void)
+{
+}
+#endif
+
+static void lcd_reset(void)
+{
+ static int first;
+
+ /* ensure that LCDIO(1.8V) has been turn on */
+ /* active reset line GPIO */
+ if (!first) {
+ mxc_request_iomux(MX37_PIN_GPIO1_5, IOMUX_CONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX37_PIN_GPIO1_5), "gpio1_5");
+ first = 1;
+ }
+ gpio_set_value(IOMUX_TO_GPIO(MX37_PIN_GPIO1_5), 0);
+ gpio_direction_output(IOMUX_TO_GPIO(MX37_PIN_GPIO1_5), 0);
+ /* do reset */
+ msleep(10); /* tRES >= 100us */
+ gpio_set_value(IOMUX_TO_GPIO(MX37_PIN_GPIO1_5), 1);
+ msleep(60);
+}
+
+static struct mxc_lcd_platform_data lcd_data = {
+ .core_reg = "VVIDEO",
+ .io_reg = "SW4",
+ .reset = lcd_reset,
+};
+
+#if defined(CONFIG_KEYBOARD_MPR084) || defined(CONFIG_KEYBOARD_MPR084_MODULE)
+/*!
+ * These functions are used to configure and the GPIO pins for keypad to
+ * activate and deactivate it.
+ */
+extern void gpio_keypad_active(void);
+
+extern void gpio_keypad_inactive(void);
+
+static u16 keymap[] = {
+ KEY_DOWN, KEY_LEFT, KEY_ENTER,
+ KEY_RIGHT, KEY_UP, KEY_LEFTALT,
+ KEY_TAB, KEY_ESC,
+};
+
+static struct mxc_keyp_platform_data keypad_data = {
+ .matrix = keymap,
+ .active = gpio_keypad_active,
+ .inactive = gpio_keypad_inactive,
+ .vdd_reg = "VGEN2",
+};
+#else
+
+static struct mxc_keyp_platform_data keypad_data = {};
+
+#endif
+
+static struct mxc_lightsensor_platform_data ls_data = {
+ .vdd_reg = "VGEN2",
+ .rext = 100,
+};
+static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {
+ {
+ .type = "tsc2007",
+ .addr = 0x48,
+ .irq = IOMUX_TO_IRQ(MX37_PIN_AUD5_RXFS),
+ },
+ {
+ .type = "mpr084",
+ .addr = 0x5D,
+ .platform_data = &keypad_data,
+ .irq = IOMUX_TO_IRQ(MX37_PIN_GPIO1_3),
+ },
+ {
+ .type = "isl29003",
+ .addr = 0x44,
+ .platform_data = &ls_data,
+ },
+};
+
+static struct i2c_board_info mxc_i2c1_board_info[] __initdata = {
+ {
+ .type = "mc13892",
+ .addr = 0x08,
+ .platform_data = (void *)MX37_PIN_OWIRE_LINE,
+ },
+ {
+ .type = "sgtl5000-i2c",
+ .addr = 0x0a,
+ },
+};
+
+static struct spi_board_info mxc_spi_board_info[] __initdata = {
+ {
+ .modalias = "cpld_spi",
+ .max_speed_hz = 27000000,
+ .bus_num = 2,
+ .chip_select = 0,
+ },
+ {
+ .modalias = "lcd_spi",
+ .max_speed_hz = 5000000,
+ .bus_num = 2,
+ .platform_data = &lcd_data,
+ .chip_select = 1,},
+};
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+static struct platform_device mxc_fb_device[] = {
+ {
+ .name = "mxc_sdc_fb",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+ },
+ {
+ .name = "mxc_sdc_fb",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+ },
+ {
+ .name = "mxc_sdc_fb",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+ },
+};
+
+static void mxc_init_fb(void)
+{
+ (void)platform_device_register(&mxc_fb_device[0]);
+ (void)platform_device_register(&mxc_fb_device[1]);
+ (void)platform_device_register(&mxc_fb_device[2]);
+ gpio_lcd_active();
+}
+#else
+static inline void mxc_init_fb(void)
+{
+}
+#endif
+
+static struct platform_device mxcbl_device = {
+ .name = "mxc_mc13892_bl",
+};
+
+static inline void mxc_init_bl(void)
+{
+ platform_device_register(&mxcbl_device);
+}
+
+#if defined(CONFIG_TOUCHSCREEN_TSC2007) || defined(CONFIG_TOUCHSCREEN_TSC2007_MODULE)
+
+static int __init mxc_init_touchscreen(void)
+{
+ int pad_val;
+
+ mxc_request_iomux(MX37_PIN_AUD5_RXFS, IOMUX_CONFIG_GPIO);
+ pad_val = PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU;
+ mxc_iomux_set_pad(MX37_PIN_AUD5_RXFS, pad_val);
+ gpio_request(IOMUX_TO_GPIO(MX37_PIN_AUD5_RXFS), "aud5_rxfs");
+ gpio_direction_input(IOMUX_TO_GPIO(MX37_PIN_AUD5_RXFS));
+
+ return 0;
+}
+#else
+static int __init mxc_init_touchscreen(void)
+{
+ return 0;
+}
+#endif
+
+/*lan9217 device*/
+#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
+static struct resource smsc911x_resources[] = {
+ {
+ .start = LAN9217_BASE_ADDR,
+ .end = LAN9217_BASE_ADDR + 255,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_EXP_IO_BASE,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device smsc_lan9217_device = {
+ .name = "smsc911x",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(smsc911x_resources),
+ .resource = smsc911x_resources,
+};
+
+static int __init mxc_init_enet(void)
+{
+ (void)platform_device_register(&smsc_lan9217_device);
+ return 0;
+}
+#else
+static int __init mxc_init_enet(void)
+{
+ return 0;
+}
+#endif
+
+late_initcall(mxc_init_enet);
+
+#if defined(CONFIG_PATA_FSL) || defined(CONFIG_PATA_FSL_MODULE)
+extern void gpio_ata_active(void);
+extern void gpio_ata_inactive(void);
+
+static int ata_init(struct platform_device *pdev)
+{
+ /* Configure the pins */
+ gpio_ata_active();
+
+ return 0;
+}
+
+static void ata_exit(void)
+{
+ /* Free the pins */
+ gpio_ata_inactive();
+}
+
+static struct fsl_ata_platform_data ata_data = {
+ .udma_mask = ATA_UDMA3, /* board can handle up to UDMA3 */
+ .mwdma_mask = ATA_MWDMA2,
+ .pio_mask = ATA_PIO4,
+ .fifo_alarm = MXC_IDE_DMA_WATERMARK / 2,
+ .max_sg = MXC_IDE_DMA_BD_NR,
+ .init = ata_init,
+ .exit = ata_exit,
+ .core_reg = NULL, /*"LDO2", */
+ .io_reg = NULL, /*"LDO3", */
+};
+
+static struct resource pata_fsl_resources[] = {
+ [0] = { /* I/O */
+ .start = ATA_BASE_ADDR,
+ .end = ATA_BASE_ADDR + 0x000000C8,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = { /* IRQ */
+ .start = MXC_INT_ATA,
+ .end = MXC_INT_ATA,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device pata_fsl_device = {
+ .name = "pata_fsl",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(pata_fsl_resources),
+ .resource = pata_fsl_resources,
+ .dev = {
+ .platform_data = &ata_data,
+ .coherent_dma_mask = ~0,
+ },
+};
+
+static void __init mxc_init_pata(void)
+{
+ (void)platform_device_register(&pata_fsl_device);
+}
+#else /* CONFIG_PATA_FSL */
+static void __init mxc_init_pata(void)
+{
+}
+#endif /* CONFIG_PATA_FSL */
+
+/*!
+ * Board specific fixup function. It is called by \b setup_arch() in
+ * setup.c file very early on during kernel starts. It allows the user to
+ * statically fill in the proper values for the passed-in parameters. None of
+ * the parameters is used currently.
+ *
+ * @param desc pointer to \b struct \b machine_desc
+ * @param tags pointer to \b struct \b tag
+ * @param cmdline pointer to the command line
+ * @param mi pointer to \b struct \b meminfo
+ */
+static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ mxc_cpu_init();
+
+#ifdef CONFIG_DISCONTIGMEM
+ do {
+ int nid;
+ mi->nr_banks = MXC_NUMNODES;
+ for (nid = 0; nid < mi->nr_banks; nid++) {
+ SET_NODE(mi, nid);
+ }
+ } while (0);
+#endif
+}
+
+static void mxc_unifi_hardreset(int pin_level)
+{
+ struct regulator *gpo4;
+
+ if (board_is_mx37(BOARD_REV_2)) {
+ gpo4 = regulator_get(NULL, "GPO4");
+ if (!IS_ERR(gpo4)) {
+ if (pin_level & 0x01)
+ regulator_enable(gpo4);
+ else
+ regulator_disable(gpo4);
+ }
+ regulator_put(gpo4);
+ } else {
+ mxc_request_iomux(MX37_PIN_AUD5_RXC, IOMUX_CONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX37_PIN_AUD5_RXC), "aud5_rxc");
+ gpio_set_value(IOMUX_TO_GPIO(MX37_PIN_AUD5_RXC),
+ pin_level & 0x01);
+ gpio_direction_output(IOMUX_TO_GPIO(MX37_PIN_AUD5_RXC), 0);
+ mxc_free_iomux(MX37_PIN_AUD5_RXC, IOMUX_CONFIG_GPIO);
+ }
+}
+
+static struct mxc_unifi_platform_data unifi_data = {
+ .hardreset = mxc_unifi_hardreset,
+ .enable = NULL,
+ .reg_1v5_ana_bb = "VGEN1",
+ .reg_vdd_vpa = "VCAM",
+ .reg_1v5_dd = "VGEN1",
+ .host_id = 1,
+};
+
+struct mxc_unifi_platform_data *get_unifi_plat_data(void)
+{
+ return &unifi_data;
+}
+
+EXPORT_SYMBOL(get_unifi_plat_data);
+
+#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
+static struct mxc_mmc_platform_data mmc1_data = {
+ .ocr_mask = MMC_VDD_32_33,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 150000,
+ .max_clk = 52000000,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "esdhc_clk",
+};
+
+/*!
+ * Resource definition for the SDHC1
+ */
+static struct resource mxcsdhc1_resources[] = {
+ [0] = {
+ .start = MMC_SDHC1_BASE_ADDR,
+ .end = MMC_SDHC1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC1,
+ .end = MXC_INT_MMC_SDHC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC1 */
+static struct platform_device mxcsdhc1_device = {
+ .name = "mxsdhci",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc1_resources),
+ .resource = mxcsdhc1_resources,
+};
+
+static struct mxc_mmc_platform_data mmc2_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 |
+ MMC_VDD_31_32,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 150000,
+ .max_clk = 50000000,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "esdhc_clk",
+};
+
+static struct resource mxcsdhc2_resources[] = {
+ [0] = {
+ .start = MMC_SDHC2_BASE_ADDR,
+ .end = MMC_SDHC2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC2,
+ .end = MXC_INT_MMC_SDHC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mxcsdhc2_device = {
+ .name = "mxsdhci",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc2_resources),
+ .resource = mxcsdhc2_resources,
+};
+
+static inline void mxc_init_mmc(void)
+{
+ int cd_irq;
+
+ cd_irq = sdhc_init_card_det(0);
+ if (cd_irq) {
+ mxcsdhc1_device.resource[2].start = cd_irq;
+ mxcsdhc1_device.resource[2].end = cd_irq;
+ }
+
+ spba_take_ownership(SPBA_SDHC1, SPBA_MASTER_A | SPBA_MASTER_C);
+ (void)platform_device_register(&mxcsdhc1_device);
+ cd_irq = sdhc_init_card_det(1);
+ if (cd_irq) {
+ mxcsdhc2_device.resource[2].start = cd_irq;
+ mxcsdhc2_device.resource[2].end = cd_irq;
+ }
+ spba_take_ownership(SPBA_SDHC2, SPBA_MASTER_A | SPBA_MASTER_C);
+ (void)platform_device_register(&mxcsdhc2_device);
+}
+#else
+static inline void mxc_init_mmc(void)
+{
+}
+#endif
+
+static void bt_reset(void)
+{
+ struct regulator *gpo4;
+ if (board_is_mx37(BOARD_REV_2)) {
+ gpo4 = regulator_get(NULL, "GPO4");
+ if (!IS_ERR(gpo4))
+ regulator_enable(gpo4);
+ regulator_put(gpo4);
+ } else {
+ mxc_request_iomux(MX37_PIN_AUD5_RXC, IOMUX_CONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX37_PIN_AUD5_RXC), "aud5_rxc");
+ gpio_set_value(IOMUX_TO_GPIO(MX37_PIN_AUD5_RXC), 1);
+ gpio_direction_output(IOMUX_TO_GPIO(MX37_PIN_AUD5_RXC), 0);
+ }
+}
+
+static struct mxc_bt_platform_data mxc_bt_data = {
+ .bt_vdd = "VGEN2",
+ .bt_vdd_parent = NULL,
+ .bt_vusb = "SW4",
+ .bt_vusb_parent = NULL,
+ .bt_reset = bt_reset,
+};
+
+static struct platform_device mxc_bt_device = {
+ .name = "mxc_bt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_bt_data,
+ },
+};
+
+static void mxc_init_bluetooth(void)
+{
+ (void)platform_device_register(&mxc_bt_device);
+}
+
+#if defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000) \
+ || defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000_MODULE)
+static int mxc_sgtl5000_plat_init(void);
+static int mxc_sgtl5000_plat_finit(void);
+static int mxc_sgtl5000_amp_enable(int enable);
+
+static struct mxc_audio_platform_data sgtl5000_data = {
+ .ssi_num = 1,
+ .src_port = 2,
+ .ext_port = 5,
+ .hp_irq = IOMUX_TO_IRQ(MX37_PIN_AUD5_RXFS),
+ .hp_status = headphone_det_status,
+ .vddio_reg = "SW3",
+ .vdda_reg = "VAUDIO",
+ .amp_enable = mxc_sgtl5000_amp_enable,
+ .vddio = 1850000,
+ .vdda = 2775000,
+ .vddd = 0,
+ .init = mxc_sgtl5000_plat_init,
+ .finit = mxc_sgtl5000_plat_finit,
+};
+
+static struct platform_device mxc_sgtl5000_device = {
+ .name = "imx-3stack-sgtl5000",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &sgtl5000_data,
+ },
+};
+
+static int mxc_sgtl5000_plat_init(void)
+{
+ struct regulator *reg;
+ reg = regulator_get(&mxc_sgtl5000_device.dev, "GPO2");
+ if (IS_ERR(reg))
+ return -EINVAL;
+ sgtl5000_data.priv = reg;
+ return 0;
+}
+
+static int mxc_sgtl5000_plat_finit(void)
+{
+ struct regulator *reg;
+ reg = sgtl5000_data.priv;
+ if (reg) {
+ regulator_put(reg);
+ sgtl5000_data.priv = NULL;
+ }
+ return 0;
+}
+
+static int mxc_sgtl5000_amp_enable(int enable)
+{
+ struct regulator *reg;
+ reg = sgtl5000_data.priv;
+
+ if (!reg)
+ return -EINVAL;
+ if (enable)
+ regulator_enable(reg);
+ else
+ regulator_disable(reg);
+ return 0;
+}
+
+static void mxc_init_sgtl5000(void)
+{
+ int err, pin;
+ struct clk *cko1, *parent;
+ unsigned long rate;
+
+ /* for board v1.1 do nothing */
+ if (!board_is_mx37(BOARD_REV_2))
+ return;
+
+ pin = MX37_PIN_AUD5_RXFS;
+ err = mxc_request_iomux(pin, IOMUX_CONFIG_GPIO);
+ if (err) {
+ sgtl5000_data.hp_irq = -1;
+ printk(KERN_ERR "Error: sgtl5000_init request gpio failed!\n");
+ return;
+ }
+ mxc_iomux_set_pad(pin, PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU);
+ gpio_request(IOMUX_TO_GPIO(pin), "aud5_rxfs");
+ gpio_direction_input(IOMUX_TO_GPIO(pin));
+
+ /* cko1 clock */
+ mxc_request_iomux(MX37_PIN_GPIO1_6, IOMUX_CONFIG_ALT2);
+
+ cko1 = clk_get(NULL, "cko1_clk");
+ if (IS_ERR(cko1))
+ return;
+ parent = clk_get(NULL, "ipg_perclk");
+ if (IS_ERR(parent))
+ return;
+ clk_set_parent(cko1, parent);
+ rate = clk_round_rate(cko1, 13000000);
+ if (rate < 8000000 || rate > 27000000) {
+ printk(KERN_ERR "Error: SGTL5000 mclk freq %d out of range!\n",
+ rate);
+ clk_put(parent);
+ clk_put(cko1);
+ return;
+ }
+ clk_set_rate(cko1, rate);
+ clk_enable(cko1);
+ sgtl5000_data.sysclk = rate;
+ platform_device_register(&mxc_sgtl5000_device);
+}
+#else
+static inline void mxc_init_sgtl5000(void)
+{
+}
+#endif
+
+/*!
+ * fixup for mx37 3stack board v1.1(wm8350)
+ */
+static void mx37_3stack_fixup_for_board_v1(void)
+{
+ dptc_gp_data.reg_id = "DCDC1";
+ dptc_lp_data.reg_id = "DCDC4";
+ gp_reg_id = "DCDC1";
+ lp_reg_id = "DCDC4";
+ tve_data.dac_reg = "LDO2";
+ tve_data.dig_reg = "LDO3";
+ lcd_data.core_reg = "LDO1";
+ lcd_data.io_reg = "DCDC6";
+ dvfs_core_data.reg_id = "DCDC1";
+ ls_data.vdd_reg = "DCDC3";
+ mxc_bt_data.bt_vdd = "DCDC3";
+ mxc_bt_data.bt_vusb = "DCDC6";
+ mxc_init_touchscreen();
+ unifi_data.reg_1v5_ana_bb = NULL; /* VMAIN is used on v1 board */
+ unifi_data.reg_vdd_vpa = NULL;
+ unifi_data.reg_1v5_dd = NULL;
+#if defined(CONFIG_KEYBOARD_MPR084) || defined(CONFIG_KEYBOARD_MPR084_MODULE)
+ keypad_data.vdd_reg = "DCDC3";
+#endif
+}
+
+#if defined(CONFIG_GPS_IOCTRL) || defined(CONFIG_GPS_IOCTRL_MODULE)
+static struct mxc_gps_platform_data gps_data = {
+ .core_reg = "VIOHI",
+ .analog_reg = "SW3",
+};
+
+static struct platform_device mxc_gps_device = {
+ .name = "gps_ioctrl",
+ .id = 0,
+ .dev = {
+ .platform_data = &gps_data,
+ },
+};
+
+static void __init mxc_init_gps(void)
+{
+ (void)platform_device_register(&mxc_gps_device);
+}
+#else
+static void __init mxc_init_gps(void)
+{
+}
+#endif
+
+/*!
+ * Board specific initialization.
+ */
+static void __init mxc_board_init(void)
+{
+ mxc_cpu_common_init();
+ mxc_init_srpgconfig();
+ mxc_register_gpios();
+ early_console_setup(saved_command_line);
+ mxc_init_devices();
+ if (!board_is_mx37(BOARD_REV_2))
+ mx37_3stack_fixup_for_board_v1();
+ i2c_register_board_info(0, mxc_i2c0_board_info,
+ ARRAY_SIZE(mxc_i2c0_board_info));
+ i2c_register_board_info(1, mxc_i2c1_board_info,
+ ARRAY_SIZE(mxc_i2c1_board_info));
+
+ spi_register_board_info(mxc_spi_board_info,
+ ARRAY_SIZE(mxc_spi_board_info));
+ mxc_init_nand_mtd();
+ mxc_init_mmc();
+ mxc_init_pata();
+ mxc_init_fb();
+ mxc_init_bl();
+ mxc_init_bluetooth();
+ mxc_init_gps();
+ mxc_init_sgtl5000();
+}
+
+static void __init mx37_3stack_timer_init(void)
+{
+ mxc_clocks_init(32768, 24000000, 22579200, 0);
+ mxc_timer_init("gpt_clk");
+}
+
+static struct sys_timer mxc_timer = {
+ .init = mx37_3stack_timer_init,
+};
+
+/*
+ * The following uses standard kernel macros define in arch.h in order to
+ * initialize __mach_desc_MX37_3STACK data structure.
+ */
+/* *INDENT-OFF* */
+MACHINE_START(MX37_3DS, "Freescale MX37 3-Stack Board")
+ /* Maintainer: Freescale Semiconductor, Inc. */
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .fixup = fixup_mxc_board,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mxc_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx37/mx37_3stack_cpld.c b/arch/arm/mach-mx37/mx37_3stack_cpld.c
new file mode 100644
index 000000000000..a94165215d52
--- /dev/null
+++ b/arch/arm/mach-mx37/mx37_3stack_cpld.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <mach/hardware.h>
+#include <asm/mach/irq.h>
+#include <mach/gpio.h>
+#include "board-mx37_3stack.h"
+#include "iomux.h"
+
+/*!
+ * @file mach-mx37/mx37_3stack_cpld.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX37
+ */
+
+extern int mxc_spi_poll_transfer(struct spi_device *spi,
+ struct spi_transfer *t);
+
+struct spi_device *cpld_spi;
+
+/*!
+ * This function is used to tranfer data to CPLD regs over CSPI
+ */
+static inline int mx37_3ds_cpld_rw(u8 * buf, size_t len)
+{
+ struct spi_transfer t = {
+ .tx_buf = (const void *)buf,
+ .rx_buf = buf,
+ .len = len,
+ .cs_change = 0,
+ .delay_usecs = 0,
+ };
+ mxc_spi_poll_transfer(cpld_spi, &t);
+ return 0;
+}
+
+/*!
+ * This function is called to read a CPLD register over CSPI.
+ *
+ * @param offset number of the cpld register to be read
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+unsigned int spi_cpld_read(unsigned int offset)
+{
+ unsigned int frame[2];
+ unsigned int reg_num = offset >> 1;
+ unsigned int data = 0;
+
+ frame[0] = (1 << 13) | ((reg_num & 0x0001FFFF) >> 5) | 0x00001000;
+ frame[1] = (((reg_num & 0x0000001F) << 27) | 0x0200001f);
+ mx37_3ds_cpld_rw((u8 *) frame, 2);
+ data = (frame[1] >> 6) & 0xFFFF;
+
+ reg_num = (offset + 2) >> 1;
+ frame[0] = (1 << 13) | ((reg_num & 0x0001FFFF) >> 5) | 0x00001000;
+ frame[1] = (((reg_num & 0x0000001F) << 27) | 0x0200001f);
+ mx37_3ds_cpld_rw((u8 *) frame, 2);
+
+ data |= (((frame[1] >> 6) & 0xFFFF) << 16);
+ return data;
+}
+EXPORT_SYMBOL(spi_cpld_read);
+
+/*!
+ * This function is called to write to a CPLD register over CSPI.
+ *
+ * @param offset number of the cpld register to be written
+ * @param reg_val value to be written
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+unsigned int spi_cpld_write(unsigned int offset, unsigned int reg_val)
+{
+ unsigned int frame[2] = { 0, 0 };
+ unsigned int reg_num = offset >> 1;
+ unsigned int data = reg_val;
+
+ frame[0] = ((reg_num & 0x0001FFFF) >> 5) | 0x00001000;
+ frame[1] = (((reg_num & 0x0000001F) << 27) |
+ ((data & 0x0000FFFF) << 6) | 0x03C00027);
+ mx37_3ds_cpld_rw((u8 *) frame, 2);
+
+ reg_num = (offset + 2) >> 1;
+ data = reg_val >> 16;
+ frame[0] = 0;
+ frame[1] = 0;
+ frame[0] = ((reg_num & 0x0001FFFF) >> 5) | 0x00001000;
+ frame[1] = (((reg_num & 0x0000001F) << 27) |
+ ((data & 0x0000FFFF) << 6) | 0x03C00027);
+
+ mx37_3ds_cpld_rw((u8 *) frame, 2);
+
+ return 0;
+}
+EXPORT_SYMBOL(spi_cpld_write);
+
+static int __devinit mx37_3ds_cpld_probe(struct spi_device *spi)
+{
+ unsigned int i = 0;
+
+ spi->bits_per_word = 46;
+ cpld_spi = spi;
+
+ spi_setup(spi);
+ i = spi_cpld_read(CPLD_CODE_VER_REG);
+ pr_info("3-Stack Debug board detected, rev = 0x%04X\n", i);
+ spi_cpld_write(LED_SWITCH_REG, 0xFF);
+
+ /* disable the interrupt and clear the status */
+ spi_cpld_write(INTR_MASK_REG, 0);
+ spi_cpld_write(INTR_RESET_REG, 0xFFFF);
+ spi_cpld_write(INTR_RESET_REG, 0);
+ spi_cpld_write(INTR_MASK_REG, 0x1E);
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the CPLD callback functions.
+ */
+static struct spi_driver mx37_3ds_cpld_driver = {
+ .driver = {
+ .name = "cpld_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = mx37_3ds_cpld_probe,
+};
+
+static int __init mx37_3ds_cpld_init(void)
+{
+ pr_debug("Registering the CPLD Driver\n");
+ return spi_register_driver(&mx37_3ds_cpld_driver);
+}
+
+device_initcall(mx37_3ds_cpld_init);
+
+static void mxc_expio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 expio_irq;
+ struct irq_desc *d;
+
+ desc->chip->mask(irq); /* irq = gpio irq number */
+
+ expio_irq = MXC_EXP_IO_BASE;
+
+ d = irq_desc + expio_irq;
+ if (unlikely(!(d->handle_irq))) {
+ printk(KERN_ERR "\nEXPIO irq: %d unhandled\n", expio_irq);
+ BUG(); /* oops */
+ }
+ d->handle_irq(expio_irq, d);
+
+ desc->chip->ack(irq);
+ desc->chip->unmask(irq);
+}
+
+/*
+ * Disable an expio pin's interrupt by setting the bit in the imr.
+ * @param irq an expio virtual irq number
+ */
+static void expio_mask_irq(u32 irq)
+{
+}
+
+/*
+ * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr.
+ * @param irq an expanded io virtual irq number
+ */
+static void expio_ack_irq(u32 irq)
+{
+ /* clear the interrupt status */
+ spi_cpld_write(INTR_RESET_REG, 1);
+ spi_cpld_write(INTR_RESET_REG, 0);
+}
+
+/*
+ * Enable a expio pin's interrupt by clearing the bit in the imr.
+ * @param irq a expio virtual irq number
+ */
+static void expio_unmask_irq(u32 irq)
+{
+}
+
+static struct irq_chip expio_irq_chip = {
+ .ack = expio_ack_irq,
+ .mask = expio_mask_irq,
+ .unmask = expio_unmask_irq,
+};
+
+static int __init mxc_expio_init(void)
+{
+ int i;
+ int pad_val;
+
+ /*
+ * Configure INT line as GPIO input
+ */
+ mxc_request_iomux(MX37_PIN_GPIO1_2, IOMUX_CONFIG_GPIO);
+ pad_val = mxc_iomux_get_pad(MX37_PIN_GPIO1_2);
+ pad_val |= PAD_CTL_PUE_PULL;
+ mxc_iomux_set_pad(MX37_PIN_GPIO1_2, pad_val);
+ gpio_direction_input(IOMUX_TO_GPIO(MX37_PIN_GPIO1_2));
+
+ for (i = MXC_EXP_IO_BASE; i < (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES);
+ i++) {
+ set_irq_chip(i, &expio_irq_chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+ set_irq_type(IOMUX_TO_IRQ(MX37_PIN_GPIO1_2), IRQF_TRIGGER_LOW);
+ set_irq_chained_handler(IOMUX_TO_IRQ(MX37_PIN_GPIO1_2),
+ mxc_expio_irq_handler);
+ return 0;
+}
+
+arch_initcall(mxc_expio_init);
diff --git a/arch/arm/mach-mx37/mx37_3stack_gpio.c b/arch/arm/mach-mx37/mx37_3stack_gpio.c
new file mode 100644
index 000000000000..647c53503a80
--- /dev/null
+++ b/arch/arm/mach-mx37/mx37_3stack_gpio.c
@@ -0,0 +1,1028 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/gpio.h>
+
+#include "iomux.h"
+
+/*!
+ * @file mx37_3stack_gpio.c
+ *
+ * @brief This file contains all the GPIO setup functions for the board.
+ *
+ * @ingroup GPIO
+ */
+
+void gpio_activate_audio_ports(void);
+
+/*!
+ * Setup GPIO for a UART port to be active
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_active(int port, int no_irda)
+{
+ /*
+ * Configure the IOMUX control registers for the UART signals
+ * and enable the UART transceivers
+ */
+ switch (port) {
+ /* UART 1 IOMUX Configs */
+ case 0:
+ mxc_request_iomux(MX37_PIN_UART1_RXD, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_UART1_RXD, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_input(MUX_IN_UART1_UART_RXD_MUX, INPUT_CTL_PATH4);
+ mxc_request_iomux(MX37_PIN_UART1_TXD, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_UART1_TXD, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST);
+ mxc_request_iomux(MX37_PIN_UART1_RTS, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_UART1_RTS, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH);
+ mxc_iomux_set_input(MUX_IN_UART1_UART_RTS_B, INPUT_CTL_PATH4);
+ mxc_request_iomux(MX37_PIN_UART1_CTS, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_UART1_CTS, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH);
+ break;
+ case 1:
+ mxc_request_iomux(MX37_PIN_UART1_DCD, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX37_PIN_UART1_DCD, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_input(MUX_IN_UART2_UART_RXD_MUX, INPUT_CTL_PATH1);
+ mxc_request_iomux(MX37_PIN_UART1_RI, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX37_PIN_UART1_RI, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST);
+ mxc_request_iomux(MX37_PIN_UART1_DSR, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX37_PIN_UART1_DSR, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH);
+ mxc_iomux_set_input(MUX_IN_UART2_UART_RTS_B, INPUT_CTL_PATH1);
+ mxc_request_iomux(MX37_PIN_UART1_DTR, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX37_PIN_UART1_DTR, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH);
+ break;
+ case 2:
+ mxc_request_iomux(MX37_PIN_AUD3_BB_TXD, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX37_PIN_AUD3_BB_TXD, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_input(MUX_IN_UART3_UART_RXD_MUX, INPUT_CTL_PATH0);
+ mxc_request_iomux(MX37_PIN_AUD3_BB_RXD, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX37_PIN_AUD3_BB_RXD, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST);
+ mxc_request_iomux(MX37_PIN_AUD3_BB_CK, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX37_PIN_AUD3_BB_CK, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH);
+ mxc_iomux_set_input(MUX_IN_UART3_UART_RTS_B, INPUT_CTL_PATH0);
+ mxc_request_iomux(MX37_PIN_AUD3_BB_FS, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX37_PIN_AUD3_BB_FS, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup GPIO for a UART port to be inactive
+ *
+ * @param port a UART port
+ * @param no_irda indicates if the port is used for SIR
+ */
+void gpio_uart_inactive(int port, int no_irda)
+{
+ /*
+ * Configure the IOMUX control registers for the UART signals
+ * and disable the UART transceivers
+ */
+ switch (port) {
+ case 0:
+ mxc_request_iomux(MX37_PIN_UART1_RXD, IOMUX_CONFIG_GPIO);
+ mxc_request_iomux(MX37_PIN_UART1_TXD, IOMUX_CONFIG_GPIO);
+ mxc_request_iomux(MX37_PIN_UART1_RTS, IOMUX_CONFIG_GPIO);
+ mxc_request_iomux(MX37_PIN_UART1_CTS, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_UART1_RXD, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_UART1_TXD, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_UART1_RTS, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_UART1_CTS, IOMUX_CONFIG_GPIO);
+ break;
+ case 1:
+ mxc_request_iomux(MX37_PIN_UART1_DCD, IOMUX_CONFIG_GPIO);
+ mxc_request_iomux(MX37_PIN_UART1_RI, IOMUX_CONFIG_GPIO);
+ mxc_request_iomux(MX37_PIN_UART1_DSR, IOMUX_CONFIG_GPIO);
+ mxc_request_iomux(MX37_PIN_UART1_DTR, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_UART1_DCD, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_UART1_RI, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_UART1_DSR, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_UART1_DTR, IOMUX_CONFIG_GPIO);
+ break;
+ /* UART 3 IOMUX Configs */
+ case 2:
+ mxc_request_iomux(MX37_PIN_AUD3_BB_TXD, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_AUD3_BB_TXD, IOMUX_CONFIG_GPIO);
+ mxc_request_iomux(MX37_PIN_AUD3_BB_RXD, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_AUD3_BB_RXD, IOMUX_CONFIG_GPIO);
+ mxc_request_iomux(MX37_PIN_AUD3_BB_CK, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_AUD3_BB_CK, IOMUX_CONFIG_GPIO);
+ mxc_request_iomux(MX37_PIN_AUD3_BB_FS, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_AUD3_BB_FS, IOMUX_CONFIG_GPIO);
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Configure the IOMUX GPR register to receive shared SDMA UART events
+ *
+ * @param port a UART port
+ */
+void config_uartdma_event(int port)
+{
+
+}
+
+EXPORT_SYMBOL(gpio_uart_active);
+EXPORT_SYMBOL(gpio_uart_inactive);
+EXPORT_SYMBOL(config_uartdma_event);
+
+/*!
+ * Setup GPIO for a CSPI device to be active
+ *
+ * @param cspi_mod an CSPI device
+ */
+void gpio_spi_active(int cspi_mod)
+{
+ switch (cspi_mod) {
+ case 0:
+ break;
+ case 1:
+ /* SPI2 */
+ mxc_request_iomux(MX37_PIN_CSPI2_MISO, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_CSPI2_MISO, PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_GRP_H9, PAD_CTL_HYS_ENABLE);
+
+ mxc_request_iomux(MX37_PIN_CSPI2_MOSI, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_CSPI2_MOSI, PAD_CTL_SRE_FAST);
+
+ mxc_request_iomux(MX37_PIN_UART1_CTS, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX37_PIN_UART1_CTS, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE);
+
+ mxc_request_iomux(MX37_PIN_CSPI2_SCLK, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_CSPI2_SCLK, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_SRE_FAST);
+
+ mxc_request_iomux(MX37_PIN_CSPI2_SS1, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_CSPI2_SS1, PAD_CTL_SRE_FAST);
+
+ mxc_request_iomux(MX37_PIN_CSPI2_SS0, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_CSPI2_SS0, PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_GRP_H10, PAD_CTL_HYS_ENABLE);
+ break;
+ case 2:
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup GPIO for a CSPI device to be inactive
+ *
+ * @param cspi_mod a CSPI device
+ */
+void gpio_spi_inactive(int cspi_mod)
+{
+}
+
+/*!
+ * Setup 1-Wire to be active
+ */
+void gpio_owire_active(void)
+{
+ /*TODO*/}
+
+/*!
+ * Setup 1-Wire to be active
+ */
+void gpio_owire_inactive(void)
+{
+ /*TODO*/}
+
+EXPORT_SYMBOL(gpio_owire_active);
+EXPORT_SYMBOL(gpio_owire_inactive);
+
+/*!
+ * Setup GPIO for an I2C device to be active
+ *
+ * @param i2c_num an I2C device
+ */
+void gpio_i2c_active(int i2c_num)
+{
+ iomux_pad_config_t regval = 0;
+
+ switch (i2c_num) {
+ case 0:
+ /* Touch */
+ /* select I2C1_SCK as daisy chain input */
+ mxc_request_iomux(MX37_PIN_I2C1_CLK, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_input(MUX_IN_I2C1_SCL, INPUT_CTL_PATH1);
+ /* OpenDrain enabled, 100k PU enabled */
+ regval =
+ PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_100K_PU |
+ PAD_CTL_PKE_ENABLE;
+ mxc_iomux_set_pad(MX37_PIN_I2C1_CLK, regval);
+
+ /*select I2C1_SDA as daisy chain input */
+ mxc_request_iomux(MX37_PIN_I2C1_DAT, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_input(MUX_IN_I2C1_SDA, INPUT_CTL_PATH1);
+ mxc_iomux_set_pad(MX37_PIN_I2C1_DAT, regval);
+ mxc_iomux_set_pad(MX37_PIN_GRP_H3, PAD_CTL_HYS_ENABLE);
+ break;
+ case 1:
+ /* PMIC */
+ /*select I2C2_SCL as daisy chain input */
+ mxc_iomux_set_input(MUX_IN_I2C2_SCL, INPUT_CTL_PATH1);
+ regval = PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PULL | PAD_CTL_100K_PU |
+ PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_DRV_HIGH;
+ mxc_iomux_set_pad(MX37_PIN_GPIO1_0, regval);
+ mxc_request_iomux(MX37_PIN_GPIO1_0,
+ (IOMUX_CONFIG_SION | IOMUX_CONFIG_ALT2));
+
+ /*select I2C2_SDA as daisy chain input */
+ mxc_iomux_set_input(MUX_IN_I2C2_SDA, INPUT_CTL_PATH1);
+ mxc_iomux_set_pad(MX37_PIN_GPIO1_1, regval);
+ mxc_request_iomux(MX37_PIN_GPIO1_1,
+ (IOMUX_CONFIG_SION | IOMUX_CONFIG_ALT2));
+ break;
+ case 2:
+ break;
+ default:
+ break;
+ }
+}
+
+/*!
+ * Setup GPIO for an I2C device to be inactive
+ *
+ * @param i2c_num an I2C device
+ */
+void gpio_i2c_inactive(int i2c_num)
+{
+ /*TODO*/}
+
+/*!
+ * This function activates DAM ports 4 & 5 to enable
+ * audio I/O.
+ */
+void gpio_activate_audio_ports(void)
+{
+ unsigned int pad_val;
+
+ /* AUD4_TXD */
+ mxc_request_iomux(MX37_PIN_DISP1_DAT20, IOMUX_CONFIG_ALT5);
+ /* AUD4_RXD */
+ mxc_request_iomux(MX37_PIN_DISP1_DAT21, IOMUX_CONFIG_ALT5);
+ /* AUD4_TXC */
+ mxc_request_iomux(MX37_PIN_DISP1_DAT22, IOMUX_CONFIG_ALT5);
+ /* AUD4_TXFS */
+ mxc_request_iomux(MX37_PIN_DISP1_DAT23, IOMUX_CONFIG_ALT5);
+
+ pad_val = PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST;
+ mxc_iomux_set_pad(MX37_PIN_AUD5_WB_CK, PAD_CTL_100K_PU | pad_val);
+ mxc_request_iomux(MX37_PIN_AUD5_WB_CK, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_AUD5_WB_RXD, pad_val);
+ mxc_request_iomux(MX37_PIN_AUD5_WB_RXD, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_AUD5_WB_TXD, pad_val);
+ mxc_request_iomux(MX37_PIN_AUD5_WB_TXD, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_AUD5_WB_FS, PAD_CTL_100K_PU | pad_val);
+ mxc_request_iomux(MX37_PIN_AUD5_WB_FS, IOMUX_CONFIG_ALT0);
+
+ /* Enable hysteresis for AUD5_WB_CK, AUD5_WB_RXD, AUD5_WB_TXD, AUD5_WB_FS */
+ mxc_iomux_set_pad(MX37_PIN_GRP_H5, PAD_CTL_HYS_ENABLE);
+}
+
+EXPORT_SYMBOL(gpio_activate_audio_ports);
+
+/*!
+ * Setup GPIO for SDHC to be active
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_active(int module)
+{
+ switch (module) {
+ case 0:
+ mxc_request_iomux(MX37_PIN_SD1_CLK,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_request_iomux(MX37_PIN_SD1_CMD,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_request_iomux(MX37_PIN_SD1_DATA0,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_request_iomux(MX37_PIN_SD1_DATA1,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_request_iomux(MX37_PIN_SD1_DATA2,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_request_iomux(MX37_PIN_SD1_DATA3,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+
+ mxc_iomux_set_pad(MX37_PIN_SD1_CMD,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_SD1_CLK,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_SD1_DATA0,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_SD1_DATA1,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_SD1_DATA2,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_SD1_DATA3,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+
+ /* Write Protected Pin */
+ mxc_request_iomux(MX37_PIN_CSPI1_SS0,
+ IOMUX_CONFIG_SION | IOMUX_CONFIG_ALT4);
+ mxc_iomux_set_pad(MX37_PIN_CSPI1_SS0,
+ PAD_CTL_DRV_HIGH | PAD_CTL_HYS_NONE |
+ PAD_CTL_SRE_FAST);
+ /*
+ * SW workaround for the eSDHC1 Write Protected feature
+ * The PSR of CSPI1_SS0 (GPIO3_2) should be read.
+ */
+ gpio_request(IOMUX_TO_GPIO(MX37_PIN_CSPI1_SS0), "cspi1_ss0");
+ gpio_direction_output(IOMUX_TO_GPIO(MX37_PIN_CSPI1_SS0), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX37_PIN_CSPI1_SS0), 1);
+ break;
+ case 1:
+ mxc_request_iomux(MX37_PIN_SD2_CLK,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_request_iomux(MX37_PIN_SD2_CMD,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_request_iomux(MX37_PIN_SD2_DATA0,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_request_iomux(MX37_PIN_SD2_DATA1,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_request_iomux(MX37_PIN_SD2_DATA2,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_request_iomux(MX37_PIN_SD2_DATA3,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+
+ mxc_iomux_set_pad(MX37_PIN_SD2_CMD,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_SD2_CLK,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA0,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA1,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA2,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA3,
+ PAD_CTL_DRV_MAX | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_PUE_PULL |
+ PAD_CTL_47K_PU |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_sdhc_active);
+
+/*!
+ * Setup GPIO for SDHC1 to be inactive
+ *
+ * @param module SDHC module number
+ */
+void gpio_sdhc_inactive(int module)
+{
+ switch (module) {
+ case 0:
+ mxc_free_iomux(MX37_PIN_SD1_CLK,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_free_iomux(MX37_PIN_SD1_CMD,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_free_iomux(MX37_PIN_SD1_DATA0,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_free_iomux(MX37_PIN_SD1_DATA1,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_free_iomux(MX37_PIN_SD1_DATA2,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_free_iomux(MX37_PIN_SD1_DATA3,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+
+ mxc_iomux_set_pad(MX37_PIN_SD1_CLK,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX37_PIN_SD1_CMD,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX37_PIN_SD1_DATA0,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX37_PIN_SD1_DATA1,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX37_PIN_SD1_DATA2,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX37_PIN_SD1_DATA3,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+
+ /* Free Write Protected Pin */
+ mxc_free_iomux(MX37_PIN_CSPI1_SS0,
+ IOMUX_CONFIG_SION | IOMUX_CONFIG_ALT4);
+ mxc_iomux_set_pad(MX37_PIN_CSPI1_SS0,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ break;
+ case 1:
+ mxc_free_iomux(MX37_PIN_SD2_CLK,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_free_iomux(MX37_PIN_SD2_CMD,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_free_iomux(MX37_PIN_SD2_DATA0,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_free_iomux(MX37_PIN_SD2_DATA1,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_free_iomux(MX37_PIN_SD2_DATA2,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+ mxc_free_iomux(MX37_PIN_SD2_DATA3,
+ IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION);
+
+ mxc_iomux_set_pad(MX37_PIN_SD2_CLK,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX37_PIN_SD2_CMD,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA0,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA1,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA2,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA3,
+ (PAD_CTL_DRV_LOW | PAD_CTL_SRE_SLOW));
+ break;
+ default:
+ break;
+ }
+}
+
+EXPORT_SYMBOL(gpio_sdhc_inactive);
+
+/*
+ * Probe for the card. If present the GPIO data would be set.
+ */
+int sdhc_get_card_det_status(struct device *dev)
+{
+ int ret;
+ u32 gpio = IOMUX_TO_GPIO(MX37_PIN_OWIRE_LINE);
+
+ if (to_platform_device(dev)->id == 0) {
+ if (board_is_mx37(BOARD_REV_2))
+ ret = gpio_get_value(IOMUX_TO_GPIO(MX37_PIN_GPIO1_4));
+ else
+ ret = gpio_get_value(gpio);
+ return ret;
+ } else { /* config the det pin for SDHC2 */
+ return 0;
+ }
+}
+
+EXPORT_SYMBOL(sdhc_get_card_det_status);
+
+/*
+ * Return the card detect pin.
+ */
+int sdhc_init_card_det(int id)
+{
+ u32 gpio;
+
+ if (id == 0) {
+ if (board_is_mx37(BOARD_REV_2)) {
+ gpio = IOMUX_TO_GPIO(MX37_PIN_GPIO1_4);
+ mxc_request_iomux(MX37_PIN_GPIO1_4, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX37_PIN_GPIO1_4,
+ PAD_CTL_DRV_HIGH |
+ PAD_CTL_HYS_NONE |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_NONE | PAD_CTL_SRE_FAST);
+ gpio_request(gpio, "gpio1_4");
+ gpio_direction_input(gpio);
+ return IOMUX_TO_IRQ(MX37_PIN_GPIO1_4);
+ } else {
+ gpio = IOMUX_TO_GPIO(MX37_PIN_OWIRE_LINE);
+ mxc_request_iomux(MX37_PIN_OWIRE_LINE,
+ IOMUX_CONFIG_ALT4);
+ mxc_iomux_set_pad(MX37_PIN_OWIRE_LINE,
+ PAD_CTL_DRV_HIGH |
+ PAD_CTL_HYS_NONE |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_NONE | PAD_CTL_SRE_FAST);
+ gpio_request(gpio, "owire_line");
+ gpio_direction_input(gpio);
+ return IOMUX_TO_IRQ(MX37_PIN_OWIRE_LINE);
+ }
+ } else { /* config the det pin for SDHC2 */
+ return 0;
+
+ }
+}
+
+EXPORT_SYMBOL(sdhc_init_card_det);
+
+/*!
+ * Get CSPI1_SS0 pin value to detect write protection
+ */
+int sdhc_write_protect(struct device *dev)
+{
+ unsigned short rc = 0;
+
+ rc = gpio_get_value(IOMUX_TO_GPIO(MX37_PIN_CSPI1_SS0));
+ if (rc > 0)
+ return 1;
+ else
+ return 0;
+}
+
+EXPORT_SYMBOL(sdhc_write_protect);
+
+/*!
+ * Setup GPIO for LCD to be active
+ *
+ */
+void gpio_lcd_active(void)
+{
+ mxc_request_iomux(MX37_PIN_DI1_PIN2, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_PAD_DI1_PIN3, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_DISP_CLK, IOMUX_CONFIG_ALT0);
+}
+
+/*!
+ * Setup GPIO for LCD to be inactive
+ *
+ */
+void gpio_lcd_inactive(void)
+{
+ /*TODO*/}
+
+/*!
+ * Setup pins for SLCD to be active
+ *
+ */
+void slcd_gpio_config(void)
+{
+ /*TODO*/}
+
+/*!
+ * Switch to the specified sensor - MX33 ADS has two
+ *
+ */
+void gpio_sensor_select(int sensor)
+{
+ /*TODO*/}
+
+/*!
+ * Setup GPIO for sensor to be active
+ *
+ */
+void gpio_sensor_active(void)
+{
+ /*TODO*/}
+
+EXPORT_SYMBOL(gpio_sensor_active);
+
+/*!
+ * Setup GPIO for sensor to be inactive
+ *
+ */
+void gpio_sensor_inactive(void)
+{
+ /*TODO*/}
+
+EXPORT_SYMBOL(gpio_sensor_inactive);
+
+/*!
+ * Setup GPIO for ATA interface
+ *
+ */
+void gpio_ata_active(void)
+{
+ /*IOMUX Settings */
+ /*PATA_DMARQ_B */
+ mxc_request_iomux(MX37_PIN_NANDF_RE_B, IOMUX_CONFIG_ALT1);
+ /*PATA_DIOR */
+ mxc_request_iomux(MX37_PIN_NANDF_ALE, IOMUX_CONFIG_ALT1);
+ /*PATA_DIOW */
+ mxc_request_iomux(MX37_PIN_NANDF_CLE, IOMUX_CONFIG_ALT1);
+ /*PATA_DMACK */
+ mxc_request_iomux(MX37_PIN_NANDF_WP_B, IOMUX_CONFIG_ALT1);
+ /*PATA_RESET_B */
+ mxc_request_iomux(MX37_PIN_NANDF_RB, IOMUX_CONFIG_ALT1);
+ /*PATA_IORDY */
+ mxc_request_iomux(MX37_PIN_NANDF_CS0, IOMUX_CONFIG_ALT1);
+ /*PATA_INTRQ_B */
+ mxc_request_iomux(MX37_PIN_NANDF_CS1, IOMUX_CONFIG_ALT1);
+ /*PATA_CS_0 */
+ mxc_request_iomux(MX37_PIN_NANDF_CS2, IOMUX_CONFIG_ALT1);
+ /*PATA_CS_1 */
+ mxc_request_iomux(MX37_PIN_NANDF_CS3, IOMUX_CONFIG_ALT1);
+
+ /*PATA_D0 */
+ mxc_request_iomux(MX37_PIN_EIM_D0, IOMUX_CONFIG_ALT1);
+ /*PATA_D1 */
+ mxc_request_iomux(MX37_PIN_EIM_D1, IOMUX_CONFIG_ALT1);
+ /*PATA_D2 */
+ mxc_request_iomux(MX37_PIN_EIM_D2, IOMUX_CONFIG_ALT1);
+ /*PATA_D3 */
+ mxc_request_iomux(MX37_PIN_EIM_D3, IOMUX_CONFIG_ALT1);
+ /*PATA_D4 */
+ mxc_request_iomux(MX37_PIN_EIM_D4, IOMUX_CONFIG_ALT1);
+ /*PATA_D5 */
+ mxc_request_iomux(MX37_PIN_EIM_D5, IOMUX_CONFIG_ALT1);
+ /*PATA_D6 */
+ mxc_request_iomux(MX37_PIN_EIM_D6, IOMUX_CONFIG_ALT1);
+ /*PATA_D7 */
+ mxc_request_iomux(MX37_PIN_EIM_D7, IOMUX_CONFIG_ALT1);
+ /*PATA_D8 */
+ mxc_request_iomux(MX37_PIN_EIM_D8, IOMUX_CONFIG_ALT1);
+ /*PATA_D9 */
+ mxc_request_iomux(MX37_PIN_EIM_D9, IOMUX_CONFIG_ALT1);
+ /*PATA_D10 */
+ mxc_request_iomux(MX37_PIN_EIM_D10, IOMUX_CONFIG_ALT1);
+ /*PATA_D11 */
+ mxc_request_iomux(MX37_PIN_EIM_D11, IOMUX_CONFIG_ALT1);
+ /*PATA_D12 */
+ mxc_request_iomux(MX37_PIN_EIM_D12, IOMUX_CONFIG_ALT1);
+ /*PATA_D13 */
+ mxc_request_iomux(MX37_PIN_EIM_D13, IOMUX_CONFIG_ALT1);
+ /*PATA_D14 */
+ mxc_request_iomux(MX37_PIN_EIM_D14, IOMUX_CONFIG_ALT1);
+ /*PATA_D15 */
+ mxc_request_iomux(MX37_PIN_EIM_D15, IOMUX_CONFIG_ALT1);
+ /*PATA_DA0 */
+ mxc_request_iomux(MX37_PIN_SD2_DATA3, IOMUX_CONFIG_ALT1);
+ /*PATA_DA1 */
+ mxc_request_iomux(MX37_PIN_SD2_DATA2, IOMUX_CONFIG_ALT1);
+ /*PATA_DA2 */
+ mxc_request_iomux(MX37_PIN_SD2_DATA1, IOMUX_CONFIG_ALT1);
+
+ /* BUFFER_ENABLE - HDD_ENABLE_B */
+ mxc_request_iomux(MX37_PIN_NANDF_WE_B, IOMUX_CONFIG_ALT1);
+
+ /* IOMUX Pad Settings */
+ mxc_iomux_set_pad(MX37_PIN_EIM_D0, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D1, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D2, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D3, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D4, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D5, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D6, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D7, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D8, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D9, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D10, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D11, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D12, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D13, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D14, 0xc0);
+ mxc_iomux_set_pad(MX37_PIN_EIM_D15, 0xc0);
+
+ mxc_iomux_set_pad(MX37_PIN_NANDF_RE_B, 0x0080);
+ mxc_iomux_set_pad(MX37_PIN_NANDF_CS1, 0x0020);
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA3, 0x00);
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA2, 0x00);
+ mxc_iomux_set_pad(MX37_PIN_SD2_DATA1, 0x00);
+ mxc_iomux_set_pad(MX37_PIN_NANDF_ALE, 0x00);
+ mxc_iomux_set_pad(MX37_PIN_NANDF_CS2, 0x00);
+ mxc_iomux_set_pad(MX37_PIN_NANDF_CS3, 0x00);
+ mxc_iomux_set_pad(MX37_PIN_NANDF_WE_B, 0x00);
+ mxc_iomux_set_pad(MX37_PIN_NANDF_CLE, 0x00);
+ mxc_iomux_set_pad(MX37_PIN_NANDF_WP_B, 0x00);
+ mxc_iomux_set_pad(MX37_PIN_NANDF_RB, 0x00);
+ mxc_iomux_set_pad(MX37_PIN_NANDF_CS0, 0x0020);
+
+}
+
+EXPORT_SYMBOL(gpio_ata_active);
+
+/*!
+ * Restore ATA interface pins to reset values
+ *
+ */
+void gpio_ata_inactive(void)
+{
+ /*Turn off the IOMUX for ATA group B signals */
+ mxc_request_iomux(MX37_PIN_EIM_D0, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D1, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D2, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D3, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D4, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D5, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D6, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D7, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D8, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D9, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D10, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D11, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D12, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D13, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D14, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_EIM_D15, IOMUX_CONFIG_ALT0);
+
+ /* Config the multiplex pin of ATA interface DIR, DA0-2, INTRQ, DMARQ */
+ mxc_request_iomux(MX37_PIN_NANDF_RE_B, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_NANDF_CS1, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_SD2_DATA3, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_SD2_DATA2, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_SD2_DATA1, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_NANDF_ALE, IOMUX_CONFIG_ALT0);
+
+ /* HDD_BUFF_EN (H:A->B, L:B->A) and HDD_ENABLE_B(H:Disable,L:Enable) */
+ mxc_free_iomux(MX37_PIN_NANDF_WE_B, IOMUX_CONFIG_ALT0);
+
+ /* These ATA pins are common to Group A and Group B */
+ mxc_request_iomux(MX37_PIN_NANDF_CS2, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_NANDF_CS3, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_NANDF_ALE, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_NANDF_CLE, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_NANDF_WP_B, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_NANDF_RB, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX37_PIN_NANDF_CS0, IOMUX_CONFIG_ALT0);
+
+}
+
+EXPORT_SYMBOL(gpio_ata_inactive);
+
+/*!
+ * Setup GPIO for Keypad to be active
+ *
+ */
+void gpio_keypad_active(void)
+{
+ int pad_val;
+
+ /*
+ * Configure the IOMUX control register for keypad signals.
+ */
+ /*KEY_INT */
+ mxc_request_iomux(MX37_PIN_GPIO1_3, IOMUX_CONFIG_ALT0);
+ /*KEY_WAKE */
+ mxc_request_iomux(MX37_PIN_DISP1_DAT18, IOMUX_CONFIG_ALT4);
+
+ /* fast slew rate */
+ pad_val = (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_22K_PU | \
+ PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_HYS_NONE | \
+ PAD_CTL_DDR_INPUT_CMOS | PAD_CTL_DRV_VOT_LOW);
+ /*KEY_INT */
+ mxc_iomux_set_pad(MX37_PIN_GPIO1_3, pad_val);
+
+ /* fast slew rate */
+ pad_val = (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | \
+ PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_HYS_NONE | \
+ PAD_CTL_DDR_INPUT_CMOS | PAD_CTL_DRV_VOT_LOW);
+ /*KEY_WAKE */
+ mxc_iomux_set_pad(MX37_PIN_DISP1_DAT18, pad_val);
+ gpio_request(IOMUX_TO_GPIO(MX37_PIN_DISP1_DAT18), "disp1_dat18");
+ gpio_direction_output(IOMUX_TO_GPIO(MX37_PIN_DISP1_DAT18), 0);
+ gpio_request(IOMUX_TO_GPIO(MX37_PIN_GPIO1_3), "gpio1_3");
+ gpio_direction_input(IOMUX_TO_GPIO(MX37_PIN_GPIO1_3));
+
+ /* drive initial value */
+ gpio_set_value(IOMUX_TO_GPIO(MX37_PIN_DISP1_DAT18), 1);
+}
+
+EXPORT_SYMBOL(gpio_keypad_active);
+
+/*!
+ * Setup GPIO for Keypad to be inactive
+ *
+ */
+void gpio_keypad_inactive(void)
+{
+ /*KEY_INT */
+ mxc_request_iomux(MX37_PIN_GPIO1_3, IOMUX_CONFIG_ALT0);
+ /*KEY_WAKE */
+ mxc_request_iomux(MX37_PIN_DISP1_DAT18, IOMUX_CONFIG_ALT0);
+}
+
+EXPORT_SYMBOL(gpio_keypad_inactive);
+
+/*
+ * USB OTG HS port
+ */
+int gpio_usbotg_hs_active(void)
+{
+ /*TODO*/ return 0;
+}
+
+EXPORT_SYMBOL(gpio_usbotg_hs_active);
+
+void gpio_usbotg_hs_inactive(void)
+{
+ /*TODO*/}
+
+EXPORT_SYMBOL(gpio_usbotg_hs_inactive);
+
+/*!
+ * Setup GPIO for PCMCIA interface
+ *
+ */
+void gpio_pcmcia_active(void)
+{
+ /*TODO*/}
+
+EXPORT_SYMBOL(gpio_pcmcia_active);
+
+/*!
+ * Setup GPIO for pcmcia to be inactive
+ */
+void gpio_pcmcia_inactive(void)
+{
+ /*TODO*/}
+
+EXPORT_SYMBOL(gpio_pcmcia_inactive);
+
+/*!
+ * Setup GPIO for fec to be active
+ */
+void gpio_fec_active(void)
+{
+ /*TODO*/}
+
+EXPORT_SYMBOL(gpio_fec_active);
+/*!
+ * Setup GPIO for fec to be inactive
+ */
+void gpio_fec_inactive(void)
+{
+ /*TODO*/}
+
+EXPORT_SYMBOL(gpio_fec_inactive);
+
+void gpio_spdif_active(void)
+{
+ iomux_pad_config_t regval = 0;
+ regval =
+ PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_100K_PU;
+ mxc_iomux_set_pad(MX37_PIN_AUD3_BB_RXD, regval);
+ mxc_request_iomux(MX37_PIN_AUD3_BB_RXD, IOMUX_CONFIG_ALT1);
+}
+
+EXPORT_SYMBOL(gpio_spdif_active);
+
+void gpio_spdif_inactive(void)
+{
+ mxc_free_iomux(MX37_PIN_AUD3_BB_RXD, IOMUX_CONFIG_ALT1);
+}
+
+EXPORT_SYMBOL(gpio_spdif_inactive);
+
+void gpio_pmic_active(void)
+{
+ mxc_request_iomux(MX37_PIN_OWIRE_LINE, IOMUX_CONFIG_GPIO);
+ mxc_iomux_set_pad(MX37_PIN_OWIRE_LINE, PAD_CTL_SRE_SLOW |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_DRV_MEDIUM |
+ PAD_CTL_100K_PU |
+ PAD_CTL_HYS_ENABLE |
+ PAD_CTL_DRV_VOT_HIGH | PAD_CTL_DDR_INPUT_CMOS);
+ gpio_request(IOMUX_TO_GPIO(MX37_PIN_OWIRE_LINE), "owire_line");
+ gpio_direction_input(IOMUX_TO_GPIO(MX37_PIN_OWIRE_LINE));
+}
+
+EXPORT_SYMBOL(gpio_pmic_active);
+
+void gpio_gps_active(void)
+{
+ /* PWR_EN */
+ mxc_request_iomux(MX37_PIN_EIM_OE, IOMUX_CONFIG_GPIO);
+ mxc_iomux_set_pad(MX37_PIN_EIM_OE, PAD_CTL_100K_PU |
+ PAD_CTL_DRV_HIGH | PAD_CTL_HYS_NONE |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX37_PIN_EIM_OE), "eim_oe");
+ gpio_direction_output(IOMUX_TO_GPIO(MX37_PIN_EIM_OE), 0);
+
+ /* RESET */
+ mxc_request_iomux(MX37_PIN_EIM_BCLK, IOMUX_CONFIG_GPIO);
+ mxc_iomux_set_pad(MX37_PIN_EIM_BCLK, PAD_CTL_DRV_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX37_PIN_EIM_BCLK), "eim_bclk");
+ gpio_direction_output(IOMUX_TO_GPIO(MX37_PIN_EIM_BCLK), 0);
+
+ gpio_set_value(IOMUX_TO_GPIO(MX37_PIN_EIM_OE), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX37_PIN_EIM_BCLK), 0);
+
+ msleep(5);
+ gpio_set_value(IOMUX_TO_GPIO(MX37_PIN_EIM_BCLK), 1);
+
+ msleep(5);
+}
+
+EXPORT_SYMBOL(gpio_gps_active);
+
+int gpio_gps_access(int para)
+{
+ iomux_pin_name_t pin;
+ pin = (para & 0x1) ? MX37_PIN_EIM_OE : MX37_PIN_EIM_BCLK;
+
+ if (para & 0x4)
+ return gpio_get_value(IOMUX_TO_GPIO(pin));
+ else if (para & 0x2)
+ gpio_set_value(IOMUX_TO_GPIO(pin), 1);
+ else
+ gpio_set_value(IOMUX_TO_GPIO(pin), 0);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(gpio_gps_access);
+
+void gpio_gps_inactive(void)
+{
+ mxc_free_iomux(MX37_PIN_EIM_BCLK, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX37_PIN_EIM_OE, IOMUX_CONFIG_GPIO);
+}
+
+EXPORT_SYMBOL(gpio_gps_inactive);
+
+int headphone_det_status(void)
+{
+ return gpio_get_value(IOMUX_TO_GPIO(MX37_PIN_AUD5_RXFS));
+}
+
+EXPORT_SYMBOL(headphone_det_status);
diff --git a/arch/arm/mach-mx37/mx37_3stack_pmic_wm8350.c b/arch/arm/mach-mx37/mx37_3stack_pmic_wm8350.c
new file mode 100644
index 000000000000..9586a9959bd6
--- /dev/null
+++ b/arch/arm/mach-mx37/mx37_3stack_pmic_wm8350.c
@@ -0,0 +1,349 @@
+/*
+ * mx37-3stack-pmic-wm8350.c -- i.MX37 3STACK Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Copyright 2008-2009 Freescale Semiconductor Inc.
+ *
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/mfd/wm8350/gpio.h>
+#include <linux/mfd/wm8350/bl.h>
+
+#include "iomux.h"
+
+/* CPU */
+static struct regulator_consumer_supply dcdc1_consumers[] = {
+ {
+ .supply = "cpu_vcc",
+ }
+};
+
+static struct regulator_init_data dcdc1_data = {
+ .constraints = {
+ .name = "DCDC1",
+ .min_uV = 850000,
+ .max_uV = 1200000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_FAST,
+ .state_mem = {
+ .uV = 1050000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ .initial_state = PM_SUSPEND_MEM,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(dcdc1_consumers),
+ .consumer_supplies = dcdc1_consumers,
+};
+
+/* MX37 LP */
+static struct regulator_init_data dcdc4_data = {
+ .constraints = {
+ .name = "DCDC4",
+ .min_uV = 1000000,
+ .max_uV = 1250000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_FAST,
+ .state_mem = {
+ .uV = 1250000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ .initial_state = PM_SUSPEND_MEM,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+};
+
+/* DDR RAM */
+static struct regulator_init_data dcdc6_data = {
+ .constraints = {
+ .name = "DCDC6",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .state_mem = {
+ .uV = 1800000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ .state_disk = {
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 0,
+ },
+ .always_on = 1,
+ .boot_on = 1,
+ .initial_state = PM_SUSPEND_MEM,
+ },
+};
+
+static struct regulator_init_data dcdc3_data = {
+ .constraints = {
+ .name = "DCDC3",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .apply_uV = 1,
+ },
+};
+
+static struct regulator_init_data ldo1_data = {
+ .constraints = {
+ .name = "LDO1",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .apply_uV = 1,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+};
+
+static struct regulator_init_data ldo2_data = {
+ .constraints = {
+ .name = "LDO2",
+ .min_uV = 2500000,
+ .max_uV = 2500000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .apply_uV = 1,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+};
+
+static struct regulator_init_data ldo3_data = {
+ .constraints = {
+ .name = "LDO3",
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .apply_uV = 1,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+};
+
+static struct regulator_init_data ldo4_data = {
+ .constraints = {
+ .name = "LDO4",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ .apply_uV = 1,
+ .always_on = 1,
+ .boot_on = 1,
+ },
+};
+
+static struct regulator_init_data isinka_data = {
+ .constraints = {
+ .name = "ISINKA",
+ .min_uA = 0,
+ .max_uA = 225000,
+ .valid_ops_mask = REGULATOR_CHANGE_CURRENT,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ },
+};
+
+static struct regulator_init_data dcdc5_data = {
+ .constraints = {
+ .name = "DCDC5",
+ .min_uV = 0,
+ .max_uV = 5000000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ },
+};
+
+static struct regulator_init_data dcdc2_data = {
+ .constraints = {
+ .name = "DCDC2",
+ .min_uV = 0,
+ .max_uV = 5000000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL,
+ },
+};
+
+static void wm8350_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+static int wm8350_check_fb(struct fb_info *info)
+{
+ return (to_platform_device(info->device)->id == 0);
+}
+
+struct wm8350_bl_platform_data wm8350_bl_data = {
+ .isink = WM8350_ISINK_A,
+ .dcdc = WM8350_DCDC_5,
+ .voltage_ramp = WM8350_DC5_RMP_20V,
+ .retries = 5,
+ .max_brightness = 63,
+ .power = FB_BLANK_UNBLANK,
+ .brightness = 50,
+ .check_fb = wm8350_check_fb,
+};
+
+static struct platform_device mxc_wm8350_devices[] = {
+ {
+ .name = "wm8350-bl",
+ .id = 2,
+ .dev = {
+ .release = wm8350_nop_release,
+ .platform_data = &wm8350_bl_data,
+ },
+ },
+};
+
+struct mxc_audio_platform_data imx_3stack_audio_platform_data = {
+ .ssi_num = 2,
+ .src_port = 2,
+ .ext_port = 5,
+ .regulator1 = "DCDC6",
+ .regulator2 = "DCDC3",
+};
+
+static struct platform_device *imx_snd_device;
+
+static int mx37_wm8350_init(struct wm8350 *wm8350)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(mxc_wm8350_devices); i++) {
+ if (platform_device_register(&mxc_wm8350_devices[i]) < 0)
+ dev_err(&mxc_wm8350_devices[i].dev,
+ "Unable to register WM8350 device\n");
+ }
+
+ wm8350->pmic.isink_A_dcdc = WM8350_DCDC_5;
+
+ /*Note: Needs to be moved into a regulator function. */
+ /* Configuring -- GPIO 7 pin */
+ if (wm8350_gpio_config(wm8350, 7, WM8350_GPIO_DIR_OUT, 0,
+ WM8350_GPIO_ACTIVE_LOW, WM8350_GPIO_PULL_NONE,
+ WM8350_GPIO_INVERT_OFF,
+ WM8350_GPIO_DEBOUNCE_OFF) == 0)
+ wm8350_set_bits(wm8350, WM8350_GPIO_PIN_STATUS, 1 << 7);
+ else
+ printk(KERN_ERR "Error in setting Wolfson GPIO pin 7 \n");
+ /* enable gpio4:USB_VBUS_EN */
+ ret =
+ wm8350_gpio_config(wm8350, 4, WM8350_GPIO_DIR_IN,
+ WM8350_GPIO4_MR_IN, WM8350_GPIO_ACTIVE_HIGH,
+ WM8350_GPIO_PULL_UP, WM8350_GPIO_INVERT_OFF,
+ WM8350_GPIO_DEBOUNCE_OFF);
+ if (ret)
+ printk(KERN_ERR "Error in setting USB VBUS enable pin\n");
+
+ wm8350_register_regulator(wm8350, WM8350_DCDC_1, &dcdc1_data);
+ wm8350_register_regulator(wm8350, WM8350_DCDC_2, &dcdc2_data);
+ wm8350_register_regulator(wm8350, WM8350_DCDC_3, &dcdc3_data);
+ wm8350_register_regulator(wm8350, WM8350_DCDC_4, &dcdc4_data);
+ wm8350_register_regulator(wm8350, WM8350_DCDC_5, &dcdc5_data);
+ wm8350_register_regulator(wm8350, WM8350_DCDC_6, &dcdc6_data);
+ wm8350_register_regulator(wm8350, WM8350_LDO_1, &ldo1_data);
+ wm8350_register_regulator(wm8350, WM8350_LDO_2, &ldo2_data);
+ wm8350_register_regulator(wm8350, WM8350_LDO_3, &ldo3_data);
+ wm8350_register_regulator(wm8350, WM8350_LDO_4, &ldo4_data);
+ wm8350_register_regulator(wm8350, WM8350_ISINK_A, &isinka_data);
+
+ /* register sound */
+ pr_info("Registering imx37_snd_device");
+ imx_snd_device = platform_device_alloc("wm8350-imx-3stack-audio", -1);
+ if (!imx_snd_device) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ imx_3stack_audio_platform_data.priv = wm8350;
+
+ imx_snd_device->dev.platform_data = &imx_3stack_audio_platform_data;
+ ret = platform_device_add(imx_snd_device);
+ if (ret)
+ goto snd_err;
+
+ return 0;
+
+snd_err:
+ platform_device_put(imx_snd_device);
+
+err:
+ kfree(wm8350->reg_cache);
+ return ret;
+}
+
+struct wm8350_platform_data __initdata mx37_wm8350_pdata = {
+ .init = mx37_wm8350_init,
+};
+
+static struct i2c_board_info __initdata wm8350_i2c_device = {
+ I2C_BOARD_INFO("wm8350", 0x1a),
+ .platform_data = &mx37_wm8350_pdata,
+ .irq = IOMUX_TO_IRQ(MX37_PIN_GPIO1_4),
+};
+
+static __init int mxc_init_i2c(void)
+{
+ i2c_register_board_info(1, &wm8350_i2c_device, 1);
+ return 0;
+}
+
+subsys_initcall(mxc_init_i2c);
+
+static __init int wm8350_regulator_init(void)
+{
+ int i = 0;
+ int ret = 0;
+ struct regulator *regulator;
+ char *wm8350_global_regulator[] = {
+ "DCDC1",
+ "DCDC3",
+ "DCDC4",
+ "DCDC6",
+ "LDO3",
+ };
+
+ /* for board v2.0 later, do nothing here */
+ if (board_is_mx37(BOARD_REV_2))
+ return 0;
+ while ((i < ARRAY_SIZE(wm8350_global_regulator)) &&
+ !IS_ERR_VALUE(
+ (unsigned long)(regulator =
+ regulator_get(NULL,
+ wm8350_global_regulator
+ [i])))) {
+ regulator_enable(regulator);
+ if (wm8350_global_regulator[i] == "DCDC4")
+ ret =
+ regulator_set_voltage(regulator, 1250000, 1250000);
+ else if (wm8350_global_regulator[i] == "DCDC1") {
+ ret =
+ regulator_set_voltage(regulator, 1050000, 1050000);
+ regulator_set_mode(regulator, REGULATOR_MODE_FAST);
+ }
+ i++;
+ }
+ return ret;
+}
+
+late_initcall(wm8350_regulator_init);
diff --git a/arch/arm/mach-mx37/mx37_pins.h b/arch/arm/mach-mx37/mx37_pins.h
new file mode 100644
index 000000000000..10dcbf3c8d8b
--- /dev/null
+++ b/arch/arm/mach-mx37/mx37_pins.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MX37_PINS_H__
+#define __ASM_ARCH_MXC_MX37_PINS_H__
+
+/*!
+ * @file arch-mxc/mx37_pins.h
+ *
+ * @brief MX37 I/O Pin List
+ *
+ * @ingroup GPIO_MX37
+ */
+
+#ifndef __ASSEMBLY__
+
+/*!
+ * @name IOMUX/PAD Bit field definitions
+ */
+
+/*! @{ */
+
+/*!
+ * In order to identify pins more effectively, each mux-controlled pin's
+ * enumerated value is constructed in the following way:
+ *
+ * -------------------------------------------------------------------
+ * 31-29 | 28 - 24 | 23 | 22 - 20 | 19 - 10| 9 - 0
+ * -------------------------------------------------------------------
+ * IO_P | IO_I | RSVD_I | GPIO_I | PAD_I | MUX_I
+ * -------------------------------------------------------------------
+ *
+ * Bit 0 to 9 contains MUX_I used to identify the register
+ * offset (0-based. base is IOMUX_module_base) defined in the Section
+ * "sw_pad_ctl & sw_mux_ctl details" of the IC Spec. The
+ * similar field definitions are used for the pad control register.
+ * For example, the MX37_PIN_ETM_D0 is defined in the enumeration:
+ * ( (0x28 - MUX_I_START) << MUX_I)|( (0x250 - PAD_I_START) << PAD_I)
+ * It means the mux control register is at register offset 0x28. The pad control
+ * register offset is: 0x250 and also occupy the least significant bits
+ * within the register.
+ */
+
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * MUX control register offset
+ */
+#define MUX_I 0
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * PAD control register offset
+ */
+#define PAD_I 10
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent which
+ * mux mode is for GPIO (0-based)
+ */
+#define GPIO_I 20
+/*!
+ * Starting bit position which is reserved.
+ */
+#define RSVD_I 23
+
+#define NON_GPIO_PORT 0x7
+#define PIN_TO_MUX_MASK ((1 << (PAD_I - MUX_I)) -1)
+#define PIN_TO_PAD_MASK ((1 << (GPIO_I - PAD_I)) - 1)
+#define PIN_TO_ALT_GPIO_MASK ((1 << (RSVD_I - GPIO_I)) - 1)
+
+#define NON_MUX_I PIN_TO_MUX_MASK
+#define MUX_I_START 0x0008
+#define MUX_I_END (PAD_I_START - 4)
+#define PAD_I_START 0x230
+#define PAD_I_END (INPUT_CTL_START - 4)
+#define INPUT_CTL_START 0x508
+#define INPUT_CTL_END 0x614
+
+#define _MXC_BUILD_PIN(gp, gi, ga, mi, pi) \
+ (((gp) << MUX_IO_P) | ((gi) << MUX_IO_I) | \
+ ((mi - MUX_I_START) << MUX_I) | \
+ ((pi - PAD_I_START) << PAD_I) | \
+ ((ga) << GPIO_I))
+
+#define _MXC_BUILD_GPIO_PIN(gp, gi, ga, mi, pi) \
+ _MXC_BUILD_PIN(gp, gi, ga, mi, pi)
+
+#define _MXC_BUILD_NON_GPIO_PIN(mi, pi) \
+ _MXC_BUILD_PIN(NON_GPIO_PORT, 0, 0, mi, pi)
+
+#define PIN_TO_IOMUX_MUX(pin) ((pin >> MUX_I) & PIN_TO_MUX_MASK)
+#define PIN_TO_IOMUX_PAD(pin) ((pin >> PAD_I) & PIN_TO_PAD_MASK)
+#define PIN_TO_ALT_GPIO(pin) ((pin >> GPIO_I) & PIN_TO_ALT_GPIO_MASK)
+#define PIN_TO_IOMUX_INDEX(pin) (PIN_TO_IOMUX_MUX(pin) >> 2)
+
+/*! @} End IOMUX/PAD Bit field definitions */
+
+/*!
+ * This enumeration is constructed based on the Section
+ * "sw_pad_ctl & sw_mux_ctl details" of the MX37 IC Spec. Each enumerated
+ * value is constructed based on the rules described above.
+ */
+enum iomux_pins {
+ MX37_PIN_KEY_ROW0 = _MXC_BUILD_NON_GPIO_PIN(0x8, 0x230),
+ MX37_PIN_KEY_ROW1 = _MXC_BUILD_NON_GPIO_PIN(0xC, 0x234),
+ MX37_PIN_KEY_ROW2 = _MXC_BUILD_NON_GPIO_PIN(0x10, 0x238),
+ MX37_PIN_KEY_ROW3 = _MXC_BUILD_NON_GPIO_PIN(0x14, 0x23C),
+ MX37_PIN_KEY_ROW4 = _MXC_BUILD_NON_GPIO_PIN(0x18, 0x240),
+ MX37_PIN_KEY_ROW5 = _MXC_BUILD_NON_GPIO_PIN(0x1C, 0x244),
+ MX37_PIN_KEY_ROW6 = _MXC_BUILD_NON_GPIO_PIN(0x20, 0x248),
+ MX37_PIN_KEY_ROW7 = _MXC_BUILD_NON_GPIO_PIN(0x24, 0x24C),
+ MX37_PIN_ETM_D0 = _MXC_BUILD_NON_GPIO_PIN(0x28, 0x250),
+ MX37_PIN_ETM_D1 = _MXC_BUILD_NON_GPIO_PIN(0x2C, 0x254),
+ MX37_PIN_ETM_D2 = _MXC_BUILD_NON_GPIO_PIN(0x30, 0x258),
+ MX37_PIN_ETM_D3 = _MXC_BUILD_NON_GPIO_PIN(0x34, 0x25C),
+ MX37_PIN_ETM_D4 = _MXC_BUILD_NON_GPIO_PIN(0x38, 0x260),
+ MX37_PIN_ETM_D5 = _MXC_BUILD_NON_GPIO_PIN(0x3C, 0x264),
+ MX37_PIN_ETM_D6 = _MXC_BUILD_NON_GPIO_PIN(0x40, 0x268),
+ MX37_PIN_ETM_D7 = _MXC_BUILD_NON_GPIO_PIN(0x44, 0x26C),
+ MX37_PIN_EIM_EB0 = _MXC_BUILD_GPIO_PIN(0, 15, 3, 0x48, 0x2A8),
+ MX37_PIN_EIM_EB1 = _MXC_BUILD_GPIO_PIN(0, 14, 3, 0x4C, 0x2AC),
+ MX37_PIN_EIM_OE = _MXC_BUILD_GPIO_PIN(0, 13, 3, 0x50, 0x2B0),
+ MX37_PIN_EIM_CS0 = _MXC_BUILD_GPIO_PIN(1, 0, 1, 0x54, 0x2B4),
+ MX37_PIN_EIM_CS1 = _MXC_BUILD_GPIO_PIN(1, 1, 1, 0x58, 0x2B8),
+ MX37_PIN_EIM_ECB = _MXC_BUILD_GPIO_PIN(0, 12, 3, 0x5C, 0x2BC),
+ MX37_PIN_EIM_LBA = _MXC_BUILD_GPIO_PIN(0, 11, 3, 0x60, 0x2C0),
+ MX37_PIN_EIM_BCLK = _MXC_BUILD_GPIO_PIN(0, 10, 3, 0x64, 0x2C4),
+ MX37_PIN_EIM_RW = _MXC_BUILD_GPIO_PIN(0, 9, 3, 0x68, 0x2C8),
+ MX37_PIN_NANDF_WE_B = _MXC_BUILD_GPIO_PIN(1, 2, 4, 0x6C, 0x2CC),
+ MX37_PIN_NANDF_RE_B = _MXC_BUILD_GPIO_PIN(1, 3, 4, 0x70, 0x2D0),
+ MX37_PIN_NANDF_ALE = _MXC_BUILD_GPIO_PIN(1, 4, 4, 0x74, 0x2D4),
+ MX37_PIN_NANDF_CLE = _MXC_BUILD_GPIO_PIN(1, 5, 4, 0x78, 0x2D8),
+ MX37_PIN_NANDF_WP_B = _MXC_BUILD_GPIO_PIN(1, 6, 4, 0x7C, 0x2DC),
+ MX37_PIN_NANDF_RB = _MXC_BUILD_GPIO_PIN(1, 7, 4, 0x80, 0x2E0),
+ MX37_PIN_NANDF_CS0 = _MXC_BUILD_GPIO_PIN(1, 8, 4, 0x84, 0x2E4),
+ MX37_PIN_NANDF_CS1 = _MXC_BUILD_GPIO_PIN(1, 9, 4, 0x88, 0x2E8),
+ MX37_PIN_NANDF_CS2 = _MXC_BUILD_GPIO_PIN(1, 10, 4, 0x8C, 0x2EC),
+ MX37_PIN_NANDF_CS3 = _MXC_BUILD_GPIO_PIN(1, 11, 4, 0x90, 0x2F0),
+ MX37_PIN_EIM_D15 = _MXC_BUILD_NON_GPIO_PIN(0x94, 0x2F4),
+ MX37_PIN_EIM_D14 = _MXC_BUILD_NON_GPIO_PIN(0x98, 0x2F8),
+ MX37_PIN_EIM_D13 = _MXC_BUILD_NON_GPIO_PIN(0x9C, 0x2FC),
+ MX37_PIN_EIM_D12 = _MXC_BUILD_NON_GPIO_PIN(0xA0, 0x300),
+ MX37_PIN_EIM_D11 = _MXC_BUILD_NON_GPIO_PIN(0xA4, 0x304),
+ MX37_PIN_EIM_D10 = _MXC_BUILD_NON_GPIO_PIN(0xA8, 0x308),
+ MX37_PIN_EIM_D9 = _MXC_BUILD_NON_GPIO_PIN(0xAC, 0x30C),
+ MX37_PIN_EIM_D8 = _MXC_BUILD_GPIO_PIN(0, 8, 3, 0xB0, 0x310),
+ MX37_PIN_EIM_D7 = _MXC_BUILD_GPIO_PIN(2, 27, 3, 0xB4, 0x314),
+ MX37_PIN_EIM_D6 = _MXC_BUILD_GPIO_PIN(2, 26, 3, 0xB8, 0x318),
+ MX37_PIN_EIM_D5 = _MXC_BUILD_GPIO_PIN(2, 25, 3, 0xBC, 0x31C),
+ MX37_PIN_EIM_D4 = _MXC_BUILD_GPIO_PIN(2, 24, 3, 0xC0, 0x320),
+ MX37_PIN_EIM_D3 = _MXC_BUILD_GPIO_PIN(2, 23, 3, 0xC4, 0x324),
+ MX37_PIN_EIM_D2 = _MXC_BUILD_GPIO_PIN(2, 22, 3, 0xC8, 0x328),
+ MX37_PIN_EIM_D1 = _MXC_BUILD_GPIO_PIN(2, 21, 3, 0xCC, 0x32C),
+ MX37_PIN_EIM_D0 = _MXC_BUILD_GPIO_PIN(2, 20, 3, 0xD0, 0x330),
+ MX37_PIN_SD1_CMD = _MXC_BUILD_GPIO_PIN(0, 16, 3, 0xD4, 0x334),
+ MX37_PIN_SD1_CLK = _MXC_BUILD_GPIO_PIN(0, 17, 3, 0xD8, 0x338),
+ MX37_PIN_SD1_DATA0 = _MXC_BUILD_GPIO_PIN(0, 18, 3, 0xDC, 0x33C),
+ MX37_PIN_SD1_DATA1 = _MXC_BUILD_GPIO_PIN(0, 19, 3, 0xE0, 0x340),
+ MX37_PIN_SD1_DATA2 = _MXC_BUILD_GPIO_PIN(0, 20, 3, 0xE4, 0x344),
+ MX37_PIN_SD1_DATA3 = _MXC_BUILD_GPIO_PIN(0, 21, 3, 0xE8, 0x348),
+ MX37_PIN_SD2_CMD = _MXC_BUILD_GPIO_PIN(0, 22, 3, 0xEC, 0x34C),
+ MX37_PIN_SD2_CLK = _MXC_BUILD_GPIO_PIN(0, 23, 3, 0xF0, 0x350),
+ MX37_PIN_SD2_DATA0 = _MXC_BUILD_GPIO_PIN(0, 24, 3, 0xF4, 0x354),
+ MX37_PIN_SD2_DATA1 = _MXC_BUILD_GPIO_PIN(0, 25, 3, 0xF8, 0x358),
+ MX37_PIN_SD2_DATA2 = _MXC_BUILD_GPIO_PIN(0, 26, 3, 0xFC, 0x35C),
+ MX37_PIN_SD2_DATA3 = _MXC_BUILD_GPIO_PIN(0, 27, 3, 0x100, 0x360),
+ MX37_PIN_I2C1_CLK = _MXC_BUILD_GPIO_PIN(1, 12, 4, 0x104, 0x364),
+ MX37_PIN_I2C1_DAT = _MXC_BUILD_GPIO_PIN(1, 13, 4, 0x108, 0x368),
+ MX37_PIN_AUD3_BB_TXD = _MXC_BUILD_GPIO_PIN(1, 14, 4, 0x10C, 0x36C),
+ MX37_PIN_AUD3_BB_RXD = _MXC_BUILD_GPIO_PIN(1, 15, 4, 0x110, 0x370),
+ MX37_PIN_AUD3_BB_CK = _MXC_BUILD_GPIO_PIN(1, 16, 4, 0x114, 0x374),
+ MX37_PIN_AUD3_BB_FS = _MXC_BUILD_GPIO_PIN(1, 17, 4, 0x118, 0x378),
+ MX37_PIN_AUD5_RXFS = _MXC_BUILD_GPIO_PIN(1, 18, 4, 0x11C, 0x37C),
+ MX37_PIN_AUD5_RXC = _MXC_BUILD_GPIO_PIN(1, 19, 4, 0x120, 0x380),
+ MX37_PIN_AUD5_WB_TXD = _MXC_BUILD_GPIO_PIN(1, 20, 4, 0x124, 0x384),
+ MX37_PIN_AUD5_WB_RXD = _MXC_BUILD_GPIO_PIN(1, 21, 4, 0x128, 0x388),
+ MX37_PIN_AUD5_WB_CK = _MXC_BUILD_GPIO_PIN(1, 22, 4, 0x12C, 0x38C),
+ MX37_PIN_AUD5_WB_FS = _MXC_BUILD_GPIO_PIN(1, 23, 4, 0x130, 0x390),
+ MX37_PIN_CSPI1_MOSI = _MXC_BUILD_GPIO_PIN(2, 0, 4, 0x134, 0x394),
+ MX37_PIN_CSPI1_MISO = _MXC_BUILD_GPIO_PIN(2, 1, 4, 0x138, 0x398),
+ MX37_PIN_CSPI1_SS0 = _MXC_BUILD_GPIO_PIN(2, 2, 4, 0x13C, 0x39C),
+ MX37_PIN_CSPI1_SS1 = _MXC_BUILD_GPIO_PIN(2, 3, 4, 0x140, 0x3A0),
+ MX37_PIN_CSPI1_SCLK = _MXC_BUILD_GPIO_PIN(2, 4, 4, 0x144, 0x3A4),
+ MX37_PIN_CSPI2_MOSI = _MXC_BUILD_GPIO_PIN(2, 5, 4, 0x148, 0x3A8),
+ MX37_PIN_CSPI2_MISO = _MXC_BUILD_GPIO_PIN(2, 6, 4, 0x14C, 0x3AC),
+ MX37_PIN_CSPI2_SS0 = _MXC_BUILD_GPIO_PIN(2, 7, 4, 0x150, 0x3B0),
+ MX37_PIN_CSPI2_SS1 = _MXC_BUILD_GPIO_PIN(2, 8, 4, 0x154, 0x3B4),
+ MX37_PIN_CSPI2_SCLK = _MXC_BUILD_GPIO_PIN(2, 9, 4, 0x158, 0x3B8),
+ MX37_PIN_UART1_RXD = _MXC_BUILD_GPIO_PIN(1, 24, 4, 0x15C, 0x3BC),
+ MX37_PIN_UART1_TXD = _MXC_BUILD_GPIO_PIN(1, 25, 4, 0x160, 0x3C0),
+ MX37_PIN_UART1_RTS = _MXC_BUILD_GPIO_PIN(1, 26, 4, 0x164, 0x3C4),
+ MX37_PIN_UART1_CTS = _MXC_BUILD_GPIO_PIN(1, 27, 4, 0x168, 0x3C8),
+ MX37_PIN_UART1_DTR = _MXC_BUILD_GPIO_PIN(1, 28, 4, 0x16C, 0x3CC),
+ MX37_PIN_UART1_DSR = _MXC_BUILD_GPIO_PIN(1, 29, 4, 0x170, 0x3D0),
+ MX37_PIN_UART1_RI = _MXC_BUILD_GPIO_PIN(1, 30, 4, 0x174, 0x3D4),
+ MX37_PIN_UART1_DCD = _MXC_BUILD_GPIO_PIN(1, 31, 4, 0x178, 0x3D8),
+ MX37_PIN_OWIRE_LINE = _MXC_BUILD_GPIO_PIN(0, 31, 4, 0x17C, 0x3DC),
+ MX37_PIN_JTAG_DE_B = _MXC_BUILD_NON_GPIO_PIN(0x180, 0x3E0),
+ MX37_PIN_DI1_PIN11 = _MXC_BUILD_GPIO_PIN(2, 10, 4, 0x184, 0x3E4),
+ MX37_PIN_DI1_PIN12 = _MXC_BUILD_GPIO_PIN(2, 11, 4, 0x188, 0x3E8),
+ MX37_PIN_DI1_PIN13 = _MXC_BUILD_GPIO_PIN(2, 12, 4, 0x18C, 0x3EC),
+ MX37_PIN_DI1_D0_CS = _MXC_BUILD_GPIO_PIN(2, 13, 4, 0x190, 0x3F0),
+ MX37_PIN_DI1_PIN15 = _MXC_BUILD_GPIO_PIN(0, 30, 4, 0x194, 0),
+ MX37_PIN_DISP1_DAT0 = _MXC_BUILD_NON_GPIO_PIN(0x198, 0x3F4),
+ MX37_PIN_DISP1_DAT1 = _MXC_BUILD_NON_GPIO_PIN(0x19C, 0x3F8),
+ MX37_PIN_DISP1_DAT2 = _MXC_BUILD_NON_GPIO_PIN(0x1A0, 0x3FC),
+ MX37_PIN_DISP1_DAT3 = _MXC_BUILD_NON_GPIO_PIN(0x1A4, 0x400),
+ MX37_PIN_DISP1_DAT4 = _MXC_BUILD_NON_GPIO_PIN(0x1A8, 0x404),
+ MX37_PIN_DISP1_DAT5 = _MXC_BUILD_NON_GPIO_PIN(0x1AC, 0x408),
+ MX37_PIN_DISP1_DAT6 = _MXC_BUILD_NON_GPIO_PIN(0x1B0, 0x40C),
+ MX37_PIN_DISP1_DAT7 = _MXC_BUILD_NON_GPIO_PIN(0x1B4, 0x410),
+ MX37_PIN_DISP1_DAT8 = _MXC_BUILD_NON_GPIO_PIN(0x1B8, 0x414),
+ MX37_PIN_DISP1_DAT9 = _MXC_BUILD_NON_GPIO_PIN(0x1BC, 0x418),
+ MX37_PIN_DISP1_DAT10 = _MXC_BUILD_NON_GPIO_PIN(0x1C0, 0x41C),
+ MX37_PIN_DISP1_DAT11 = _MXC_BUILD_NON_GPIO_PIN(0x1C4, 0x420),
+ MX37_PIN_DISP1_DAT12 = _MXC_BUILD_NON_GPIO_PIN(0x1C8, 0x424),
+ MX37_PIN_DISP1_DAT13 = _MXC_BUILD_NON_GPIO_PIN(0x1CC, 0x428),
+ MX37_PIN_DISP1_DAT14 = _MXC_BUILD_NON_GPIO_PIN(0x1D0, 0x42C),
+ MX37_PIN_DISP1_DAT15 = _MXC_BUILD_NON_GPIO_PIN(0x1D4, 0x430),
+ MX37_PIN_DISP1_DAT16 = _MXC_BUILD_GPIO_PIN(0, 28, 4, 0x1D8, 0x434),
+ MX37_PIN_DISP1_DAT17 = _MXC_BUILD_GPIO_PIN(0, 29, 4, 0x1DC, 0x438),
+ MX37_PIN_DISP1_DAT18 = _MXC_BUILD_GPIO_PIN(2, 14, 4, 0x1E0, 0x43C),
+ MX37_PIN_DISP1_DAT19 = _MXC_BUILD_GPIO_PIN(2, 15, 4, 0x1E4, 0x440),
+ MX37_PIN_DISP1_DAT20 = _MXC_BUILD_GPIO_PIN(2, 16, 4, 0x1E8, 0x444),
+ MX37_PIN_DISP1_DAT21 = _MXC_BUILD_GPIO_PIN(2, 17, 4, 0x1EC, 0x448),
+ MX37_PIN_DISP1_DAT22 = _MXC_BUILD_GPIO_PIN(2, 18, 4, 0x1F0, 0x44C),
+ MX37_PIN_DISP1_DAT23 = _MXC_BUILD_GPIO_PIN(2, 18, 4, 0x1F4, 0x450),
+ MX37_PIN_PAD_DI1_PIN3 = _MXC_BUILD_GPIO_PIN(2, 29, 4, 0x1F8, 0),
+ MX37_PIN_DISP_CLK = _MXC_BUILD_GPIO_PIN(2, 30, 4, 0x1FC, 0),
+ MX37_PIN_DI1_PIN2 = _MXC_BUILD_GPIO_PIN(2, 31, 4, 0x200, 0),
+ MX37_PIN_BOOT_MODE1 = _MXC_BUILD_NON_GPIO_PIN(0x204, 0x454),
+ MX37_PIN_BOOT_MODE0 = _MXC_BUILD_NON_GPIO_PIN(0x208, 0x458),
+ MX37_PIN_WDOG_RST = _MXC_BUILD_GPIO_PIN(2, 28, 1, 0x20C, 0x464),
+ MX37_PIN_GPIO1_0 = _MXC_BUILD_GPIO_PIN(0, 0, 0, 0x210, 0x468),
+ MX37_PIN_GPIO1_1 = _MXC_BUILD_GPIO_PIN(0, 1, 0, 0x214, 0x46C),
+ MX37_PIN_GPIO1_2 = _MXC_BUILD_GPIO_PIN(0, 2, 1, 0x218, 0x470),
+ MX37_PIN_GPIO1_3 = _MXC_BUILD_GPIO_PIN(0, 3, 0, 0x21C, 0x474),
+ MX37_PIN_GPIO1_4 = _MXC_BUILD_GPIO_PIN(0, 4, 0, 0x220, 0x478),
+ MX37_PIN_GPIO1_5 = _MXC_BUILD_GPIO_PIN(0, 5, 0, 0x224, 0x47C),
+ MX37_PIN_GPIO1_6 = _MXC_BUILD_GPIO_PIN(0, 6, 0, 0x228, 0x480),
+ MX37_PIN_GPIO1_7 = _MXC_BUILD_GPIO_PIN(0, 7, 0, 0x22C, 0x484),
+ MX37_PIN_GRP_H10 = _MXC_BUILD_NON_GPIO_PIN(0x230, 0x490),
+ MX37_PIN_GRP_H9 = _MXC_BUILD_NON_GPIO_PIN(0x230, 0x494),
+ MX37_PIN_GRP_H3 = _MXC_BUILD_NON_GPIO_PIN(0x230, 0x4D0),
+ MX37_PIN_GRP_H5 = _MXC_BUILD_NON_GPIO_PIN(0x230, 0x4EC),
+};
+
+#endif /* */
+#endif /* */
diff --git a/arch/arm/mach-mx37/pm.c b/arch/arm/mach-mx37/pm.c
new file mode 100644
index 000000000000..ebd6607d5fdb
--- /dev/null
+++ b/arch/arm/mach-mx37/pm.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/proc_fs.h>
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+
+static struct cpu_wp *cpu_wp_tbl;
+static struct clk *cpu_clk;
+
+#if defined(CONFIG_CPU_FREQ)
+static int org_freq;
+extern int cpufreq_suspended;
+extern int set_cpu_freq(int wp);
+#endif
+
+static int mx37_suspend_enter(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ mxc_cpu_lp_set(STOP_POWER_OFF);
+ break;
+ case PM_SUSPEND_STANDBY:
+ mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (tzic_enable_wake(0) != 0)
+ return -EAGAIN;
+
+ cpu_do_idle();
+
+ return 0;
+}
+
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+static int mx37_suspend_prepare(void)
+{
+#if defined(CONFIG_CPU_FREQ)
+ struct cpufreq_freqs freqs;
+ org_freq = clk_get_rate(cpu_clk);
+ freqs.old = org_freq / 1000;
+ freqs.new = cpu_wp_tbl[0].cpu_rate / 1000;
+ freqs.cpu = 0;
+ freqs.flags = 0;
+
+ cpufreq_suspended = 1;
+ if (clk_get_rate(cpu_clk) != cpu_wp_tbl[0].cpu_rate) {
+ set_cpu_freq(cpu_wp_tbl[0].cpu_rate);
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Called after devices are re-setup, but before processes are thawed.
+ */
+static void mx37_suspend_finish(void)
+{
+#if defined(CONFIG_CPU_FREQ)
+ struct cpufreq_freqs freqs;
+
+ freqs.old = clk_get_rate(cpu_clk) / 1000;
+ freqs.new = org_freq / 1000;
+ freqs.cpu = 0;
+ freqs.flags = 0;
+
+ cpufreq_suspended = 0;
+
+ if (org_freq != clk_get_rate(cpu_clk)) {
+ set_cpu_freq(org_freq);
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+#endif
+}
+
+static int mx37_pm_valid(suspend_state_t state)
+{
+ return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
+}
+
+struct platform_suspend_ops mx37_suspend_ops = {
+ .valid = mx37_pm_valid,
+ .prepare = mx37_suspend_prepare,
+ .enter = mx37_suspend_enter,
+ .finish = mx37_suspend_finish,
+};
+
+static int __init mx37_pm_init(void)
+{
+ int cpu_wp_nr;
+
+ pr_info("Static Power Management for Freescale i.MX37\n");
+ suspend_set_ops(&mx37_suspend_ops);
+
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+
+ cpu_clk = clk_get(NULL, "cpu_clk");
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_DEBUG "%s: failed to get cpu_clk\n", __func__);
+ return PTR_ERR(cpu_clk);
+ }
+ return 0;
+}
+
+late_initcall(mx37_pm_init);
diff --git a/arch/arm/mach-mx37/sdma_script_code.h b/arch/arm/mach-mx37/sdma_script_code.h
new file mode 100644
index 000000000000..f3b7d509ffb5
--- /dev/null
+++ b/arch/arm/mach-mx37/sdma_script_code.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 */
+
+/*!
+ * @file sdma_script_code.h
+ * @brief This file contains functions of SDMA scripts code initialization
+ *
+ * The file was generated automatically. Based on sdma scripts library.
+ *
+ * @ingroup SDMA
+ */
+/*******************************************************************************
+
+ SDMA RELEASE LABEL: "SS15_MARLEY"
+
+*******************************************************************************/
+
+#ifndef __SDMA_SCRIPT_CODE_H__
+#define __SDMA_SCRIPT_CODE_H__
+
+/*!
+* SDMA ROM scripts start addresses and sizes
+*/
+
+#define start_ADDR 0
+#define start_SIZE 20
+
+#define core_ADDR 80
+#define core_SIZE 232
+
+#define common_ADDR 312
+#define common_SIZE 330
+
+#define ap_2_ap_ADDR 642
+#define ap_2_ap_SIZE 41
+
+#define app_2_mcu_ADDR 683
+#define app_2_mcu_SIZE 64
+
+#define mcu_2_app_ADDR 747
+#define mcu_2_app_SIZE 70
+
+#define uart_2_mcu_ADDR 817
+#define uart_2_mcu_SIZE 75
+
+#define shp_2_mcu_ADDR 892
+#define shp_2_mcu_SIZE 69
+
+#define mcu_2_shp_ADDR 961
+#define mcu_2_shp_SIZE 72
+
+#define uartsh_2_mcu_ADDR 1033
+#define uartsh_2_mcu_SIZE 69
+
+#define mcu_2_ata_ADDR 1102
+#define mcu_2_ata_SIZE 81
+
+#define ata_2_mcu_ADDR 1183
+#define ata_2_mcu_SIZE 96
+
+#define burstDMA__2__burstDMA_routine_ADDR 1279
+#define burstDMA__2__burstDMA_routine_SIZE 227
+
+#define test_ADDR 1506
+#define test_SIZE 63
+
+#define signature_ADDR 1023
+#define signature_SIZE 1
+
+/*!
+* SDMA RAM scripts start addresses and sizes
+*/
+
+#define dptc_dvfs_ADDR 6144
+#define dptc_dvfs_SIZE 270
+
+#define ext_mem__ipu_ram_ADDR 6414
+#define ext_mem__ipu_ram_SIZE 123
+
+#define mcu_2_mshc_ADDR 6537
+#define mcu_2_mshc_SIZE 54
+
+#define mcu_2_spdif_marley_ADDR 6591
+#define mcu_2_spdif_marley_SIZE 161
+
+#define mshc_2_mcu_ADDR 6752
+#define mshc_2_mcu_SIZE 54
+
+/*!
+* SDMA RAM image start address and size
+*/
+
+#define RAM_CODE_START_ADDR 6144
+#define RAM_CODE_SIZE 662
+
+/*!
+* Buffer that holds the SDMA RAM image
+*/
+__attribute__ ((__aligned__(4)))
+#ifndef CONFIG_XIP_KERNEL
+const
+#endif
+static const short sdma_code[] = {
+ 0xc13c, 0x7d70, 0x0800, 0x0970, 0x0111, 0x5111, 0x5ac1, 0x5bc9,
+ 0x028e, 0xc14e, 0x068a, 0x7c66, 0x5dd9, 0x5ce1, 0x0bff, 0x0311,
+ 0x1bff, 0x03bc, 0x5bd1, 0x1a5c, 0x6ac3, 0x63c8, 0x0363, 0x7c05,
+ 0x036f, 0x7d27, 0x0374, 0x7c76, 0x9874, 0xd907, 0x3c06, 0x4c00,
+ 0x7df7, 0x028f, 0x1a04, 0x6a20, 0x620b, 0x6f20, 0x301f, 0x00aa,
+ 0x0462, 0x7c04, 0x4a00, 0x7d0b, 0x2001, 0x9837, 0x048a, 0x620b,
+ 0x2201, 0x1c01, 0x1801, 0x02dc, 0x7d02, 0x301f, 0x00aa, 0x048f,
+ 0x1c04, 0x6c04, 0x0488, 0x3c1f, 0x6c2b, 0x0045, 0x028e, 0x1a5c,
+ 0x9818, 0x058f, 0x1d0c, 0x6d20, 0x650b, 0x007d, 0x7c01, 0x1d08,
+ 0x007c, 0x7c01, 0x1d04, 0x6d20, 0x650b, 0x0488, 0x3c1f, 0x0417,
+ 0x0417, 0x0417, 0x0417, 0x059c, 0x6d20, 0x028e, 0x1a34, 0x6ad7,
+ 0x0488, 0x0804, 0x7802, 0x650b, 0x6dc8, 0x008c, 0x1a28, 0x6ad7,
+ 0x63c8, 0x034c, 0x6bc8, 0x54d1, 0x4c00, 0x7d06, 0x0065, 0x7c02,
+ 0x0101, 0x0025, 0x0400, 0x9814, 0x52c1, 0x53c9, 0x54e1, 0x0453,
+ 0xc159, 0x7d95, 0x0200, 0x9800, 0x55d9, 0x6d04, 0x54d1, 0x058a,
+ 0x2508, 0x6dc7, 0x0373, 0x7c03, 0x65c8, 0x6d0b, 0x2408, 0x0372,
+ 0x7c04, 0x65c8, 0x6d0b, 0x2408, 0x9889, 0x6cce, 0x65c8, 0x6d0a,
+ 0x2404, 0x6d28, 0x6504, 0x5dd9, 0x5cd1, 0x6ad7, 0x6ae3, 0x63c8,
+ 0x0334, 0x6bc8, 0x0370, 0x7cad, 0x0c60, 0x0411, 0x04bb, 0x4c00,
+ 0x7da8, 0x0410, 0x1c30, 0x0410, 0x04bb, 0x046d, 0x7d0a, 0x047d,
+ 0x7c03, 0x047c, 0x7c01, 0x9841, 0x003b, 0x003a, 0x0039, 0x0058,
+ 0x98b8, 0x047d, 0x7d03, 0x047c, 0x7d01, 0x9841, 0x005b, 0xd8fc,
+ 0x1d18, 0x6d20, 0x650b, 0x0510, 0x003a, 0x0039, 0x0038, 0x00ad,
+ 0xd907, 0x0c30, 0x0410, 0x04bb, 0x003c, 0x003d, 0x00ac, 0xd8fc,
+ 0x007b, 0x7c04, 0x003d, 0x003c, 0x1d0c, 0x98d9, 0x048f, 0x1c14,
+ 0x6c20, 0x640b, 0x4401, 0x7d04, 0x005d, 0x005c, 0x1d0c, 0x98d9,
+ 0x0310, 0x3b30, 0x4b30, 0x7d01, 0x1b10, 0x0310, 0x003d, 0x003c,
+ 0x00ab, 0x6ad7, 0x63c8, 0x6d20, 0x650b, 0x0560, 0x7d03, 0x005e,
+ 0xd8f0, 0x9841, 0x003e, 0x0c80, 0x0410, 0x0394, 0xd8f0, 0x640b,
+ 0x037f, 0x7d02, 0x1a14, 0x98ed, 0x1a0c, 0x6ad7, 0x6cc8, 0x9841,
+ 0x0c7f, 0x0410, 0x03b4, 0x04b8, 0x03ac, 0x640b, 0x6bc8, 0x028e,
+ 0x1a04, 0x6ad7, 0x6cc8, 0x0006, 0x058f, 0x1d08, 0x6d20, 0x650b,
+ 0x007d, 0x7c01, 0x1d38, 0x007c, 0x7c01, 0x1d1c, 0x0006, 0x048b,
+ 0x042c, 0x0454, 0x042b, 0x6ad7, 0x6cc8, 0x0006, 0x0e70, 0x0611,
+ 0x5616, 0xc13c, 0x7d2a, 0x5ade, 0x008e, 0xc14e, 0x7c26, 0x5be0,
+ 0x5ef0, 0x5ce8, 0x0688, 0x08ff, 0x0011, 0x28ff, 0x00bc, 0x53f6,
+ 0x05df, 0x7d0b, 0x6dc5, 0x03df, 0x7d03, 0x6bd5, 0xd95d, 0x9939,
+ 0x6b05, 0xc55f, 0x7e27, 0x7f29, 0x9939, 0x6d01, 0x03df, 0x7d05,
+ 0x6bd5, 0xc589, 0x7e18, 0x7f1a, 0x9939, 0x6b05, 0xc4ff, 0x7e07,
+ 0x7f06, 0x52de, 0x53e6, 0xc159, 0x7dd7, 0x0200, 0x9911, 0x0007,
+ 0x6004, 0x680c, 0x53f6, 0x028e, 0x00a3, 0xc256, 0x048b, 0x0498,
+ 0x0454, 0x068a, 0x9939, 0x0207, 0x680c, 0x6ddf, 0x0107, 0x68ff,
+ 0x60d0, 0x9942, 0x0207, 0x68ff, 0x6d28, 0x0107, 0x6004, 0x680c,
+ 0x9942, 0x0007, 0x68ff, 0x60d0, 0x9942, 0x0288, 0x03a5, 0x3b03,
+ 0x3d03, 0x4d00, 0x7d0a, 0x0804, 0x00a5, 0x00da, 0x7d1a, 0x02a0,
+ 0x7b01, 0x65d8, 0x7eee, 0x65ff, 0x7eec, 0x0804, 0x02d0, 0x7d11,
+ 0x4b00, 0x7c0f, 0x008a, 0x3003, 0x6dcf, 0x6bdf, 0x0015, 0x0015,
+ 0x7b02, 0x65d8, 0x0000, 0x7edd, 0x63ff, 0x7edb, 0x3a03, 0x6dcd,
+ 0x6bdd, 0x008a, 0x7b02, 0x65d8, 0x0000, 0x7ed3, 0x65ff, 0x7ed1,
+ 0x0006, 0xc1d9, 0x0b70, 0x0311, 0x5313, 0x58d3, 0x008b, 0x5efb,
+ 0xc13c, 0x7d2b, 0x5ac0, 0x5bc8, 0xc14e, 0x7c27, 0x6d01, 0x0388,
+ 0x0dff, 0x0511, 0x1dff, 0x05bc, 0x4d00, 0x7d1a, 0x0e70, 0x0611,
+ 0x522e, 0x02b9, 0x4a00, 0x7c07, 0x52fe, 0x50d3, 0x02b8, 0x4a00,
+ 0x7c02, 0x0400, 0x999e, 0x56fb, 0x620b, 0x7e06, 0x5a06, 0x7f06,
+ 0x0000, 0x2504, 0x7d05, 0x999e, 0x0007, 0x680c, 0x0007, 0x0454,
+ 0x008b, 0x52c0, 0x53c8, 0xc159, 0x7dd6, 0x0200, 0x9990, 0xc1d9,
+ 0xc1e3, 0x0800, 0x005f, 0x00ac, 0x58e3, 0x0478, 0x7d5c, 0x0479,
+ 0x7d01, 0x0515, 0x0515, 0xda38, 0xda57, 0x0479, 0x7d26, 0x54e3,
+ 0x047f, 0x7d12, 0x50eb, 0x56fb, 0x0015, 0x52db, 0x7806, 0x5402,
+ 0x5c06, 0x1a01, 0x5402, 0x5c26, 0x1a01, 0x54e3, 0x043f, 0x5ce3,
+ 0x4d00, 0x7d4e, 0x0479, 0x7d14, 0x047f, 0x7d01, 0xda57, 0x52f3,
+ 0x6a21, 0x56db, 0x7803, 0x620b, 0x5a06, 0x1e01, 0x7f34, 0x7e33,
+ 0x6200, 0x5af3, 0x047f, 0x7dde, 0x9a20, 0x54e3, 0x047f, 0x7cda,
+ 0x54e3, 0x047f, 0x7d01, 0xda57, 0x54eb, 0x0fff, 0x0711, 0x1fff,
+ 0x56db, 0x52f3, 0x6a21, 0x630b, 0x028b, 0x03bf, 0xda32, 0x5b06,
+ 0x2401, 0x4c00, 0x7d0b, 0x1e01, 0x038a, 0x03b7, 0x0312, 0x0312,
+ 0xda32, 0x5b06, 0x1e01, 0x2401, 0x4c00, 0x7ced, 0x0b70, 0x0311,
+ 0x5313, 0x7f09, 0x7e08, 0x6200, 0x5af3, 0x54e3, 0x047f, 0x7db2,
+ 0x57db, 0xc1fa, 0x99cd, 0x0007, 0x680c, 0x54e3, 0x0478, 0x7c02,
+ 0x0800, 0x9a2e, 0x0479, 0x7d01, 0x0517, 0x0517, 0x5deb, 0xc213,
+ 0xc20a, 0x99c1, 0x0808, 0x7801, 0x0317, 0x0006, 0x020a, 0x0006,
+ 0x070a, 0xda36, 0x1a05, 0x0215, 0x5adb, 0x0708, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x080c,
+ 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x4800, 0x7dd2, 0x58eb, 0x0006,
+ 0xc1d9, 0x0b70, 0x0311, 0x5313, 0x58d3, 0x008b, 0x5efb, 0xc13c,
+ 0x7d2b, 0x5ac0, 0x5bc8, 0xc14e, 0x7c27, 0x0388, 0x6d05, 0x0dff,
+ 0x0511, 0x1dff, 0x05bc, 0x4d00, 0x7d1a, 0x0e70, 0x0611, 0x522e,
+ 0x02b9, 0x4a00, 0x7c07, 0x52fe, 0x50d3, 0x02b8, 0x4a00, 0x7c02,
+ 0x0400, 0x9a75, 0x56fb, 0x5206, 0x7e08, 0x6a0b, 0x6a28, 0x7f04,
+ 0x0000, 0x2504, 0x7d04, 0x9a75, 0x680c, 0x0007, 0x0454, 0x008b,
+ 0x52c0, 0x53c8, 0xc159, 0x7dd6, 0x0200, 0x9a67
+};
+#endif
diff --git a/arch/arm/mach-mx37/serial.c b/arch/arm/mach-mx37/serial.c
new file mode 100644
index 000000000000..aaa5171eb177
--- /dev/null
+++ b/arch/arm/mach-mx37/serial.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @file mach-mx37/serial.c
+ *
+ * @brief This file contains the UART initiliazation.
+ *
+ * @ingroup MSL_MX37
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <mach/hardware.h>
+#include <mach/mxc_uart.h>
+#include <mach/spba.h>
+#include "serial.h"
+#include "board-mx37_3stack.h"
+
+#if defined(CONFIG_SERIAL_MXC) || defined(CONFIG_SERIAL_MXC_MODULE)
+
+/*!
+ * This is an array where each element holds information about a UART port,
+ * like base address of the UART, interrupt numbers etc. This structure is
+ * passed to the serial_core.c file. Based on which UART is used, the core file
+ * passes back the appropriate port structure as an argument to the control
+ * functions.
+ */
+static uart_mxc_port mxc_ports[] = {
+ [0] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART1_BASE_ADDR),
+ .mapbase = UART1_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART1_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 0,
+ },
+ .ints_muxed = UART1_MUX_INTS,
+ .irqs = {UART1_INT2, UART1_INT3},
+ .mode = UART1_MODE,
+ .ir_mode = UART1_IR,
+ .enabled = UART1_ENABLED,
+ .hardware_flow = UART1_HW_FLOW,
+ .cts_threshold = UART1_UCR4_CTSTL,
+ .dma_enabled = UART1_DMA_ENABLE,
+ .dma_rxbuf_size = UART1_DMA_RXBUFSIZE,
+ .rx_threshold = UART1_UFCR_RXTL,
+ .tx_threshold = UART1_UFCR_TXTL,
+ .shared = UART1_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART1_TX,
+ .dma_rx_id = MXC_DMA_UART1_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ },
+ [1] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART2_BASE_ADDR),
+ .mapbase = UART2_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART2_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 1,
+ },
+ .ints_muxed = UART2_MUX_INTS,
+ .irqs = {UART2_INT2, UART2_INT3},
+ .mode = UART2_MODE,
+ .ir_mode = UART2_IR,
+ .enabled = UART2_ENABLED,
+ .hardware_flow = UART2_HW_FLOW,
+ .cts_threshold = UART2_UCR4_CTSTL,
+ .dma_enabled = UART2_DMA_ENABLE,
+ .dma_rxbuf_size = UART2_DMA_RXBUFSIZE,
+ .rx_threshold = UART2_UFCR_RXTL,
+ .tx_threshold = UART2_UFCR_TXTL,
+ .shared = UART2_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART2_TX,
+ .dma_rx_id = MXC_DMA_UART2_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ },
+ [2] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART3_BASE_ADDR),
+ .mapbase = UART3_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART3_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 2,
+ },
+ .ints_muxed = UART3_MUX_INTS,
+ .irqs = {UART3_INT2, UART3_INT3},
+ .mode = UART3_MODE,
+ .ir_mode = UART3_IR,
+ .enabled = UART3_ENABLED,
+ .hardware_flow = UART3_HW_FLOW,
+ .cts_threshold = UART3_UCR4_CTSTL,
+ .dma_enabled = UART3_DMA_ENABLE,
+ .dma_rxbuf_size = UART3_DMA_RXBUFSIZE,
+ .rx_threshold = UART3_UFCR_RXTL,
+ .tx_threshold = UART3_UFCR_TXTL,
+ .shared = UART3_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART3_TX,
+ .dma_rx_id = MXC_DMA_UART3_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ },
+};
+
+static struct platform_device mxc_uart_device1 = {
+ .name = "mxcintuart",
+ .id = 0,
+ .dev = {
+ .platform_data = &mxc_ports[0],
+ },
+};
+
+static struct platform_device mxc_uart_device2 = {
+ .name = "mxcintuart",
+ .id = 1,
+ .dev = {
+ .platform_data = &mxc_ports[1],
+ },
+};
+
+static struct platform_device mxc_uart_device3 = {
+ .name = "mxcintuart",
+ .id = 2,
+ .dev = {
+ .platform_data = &mxc_ports[2],
+ },
+};
+
+static int __init mxc_init_uart(void)
+{
+ /* Register all the MXC UART platform device structures */
+ platform_device_register(&mxc_uart_device1);
+ platform_device_register(&mxc_uart_device2);
+
+ /* Grab ownership of shared UARTs 3 and 4, only when enabled */
+#if UART3_ENABLED == 1
+#if UART3_DMA_ENABLE == 1
+ spba_take_ownership(UART3_SHARED_PERI, (SPBA_MASTER_A | SPBA_MASTER_C));
+#else
+ spba_take_ownership(UART3_SHARED_PERI, SPBA_MASTER_A);
+#endif /* UART3_DMA_ENABLE */
+ platform_device_register(&mxc_uart_device3);
+#endif /* UART3_ENABLED */
+
+ return 0;
+}
+
+#else
+static int __init mxc_init_uart(void)
+{
+ return 0;
+}
+#endif
+
+arch_initcall(mxc_init_uart);
diff --git a/arch/arm/mach-mx37/serial.h b/arch/arm/mach-mx37/serial.h
new file mode 100644
index 000000000000..b8b8403e2698
--- /dev/null
+++ b/arch/arm/mach-mx37/serial.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX37_SERIAL_H__
+#define __ARCH_ARM_MACH_MX37_SERIAL_H__
+
+#include <mach/mxc_uart.h>
+
+/* UART 1 configuration */
+/*!
+ * This option allows to choose either an interrupt-driven software controlled
+ * hardware flow control (set this option to 0) or hardware-driven hardware
+ * flow control (set this option to 1).
+ */
+/* UART used as wakeup source */
+#define UART1_HW_FLOW 0
+/*!
+ * This specifies the threshold at which the CTS pin is deasserted by the
+ * RXFIFO. Set this value in Decimal to anything from 0 to 32 for
+ * hardware-driven hardware flow control. Read the HW spec while specifying
+ * this value. When using interrupt-driven software controlled hardware
+ * flow control set this option to -1.
+ */
+#define UART1_UCR4_CTSTL 16
+/*!
+ * This is option to enable (set this option to 1) or disable DMA data transfer
+ */
+#define UART1_DMA_ENABLE 0
+/*!
+ * Specify the size of the DMA receive buffer. The minimum buffer size is 512
+ * bytes. The buffer size should be a multiple of 256.
+ */
+#define UART1_DMA_RXBUFSIZE 1024
+/*!
+ * Specify the MXC UART's Receive Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the RxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_RXTL 16
+/*!
+ * Specify the MXC UART's Transmit Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the TxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_TXTL 16
+/* UART 2 configuration */
+#define UART2_HW_FLOW 0
+#define UART2_UCR4_CTSTL -1
+#define UART2_DMA_ENABLE 0
+#define UART2_DMA_RXBUFSIZE 512
+#define UART2_UFCR_RXTL 16
+#define UART2_UFCR_TXTL 16
+/* UART 3 configuration */
+#define UART3_HW_FLOW 1
+#define UART3_UCR4_CTSTL 16
+#define UART3_DMA_ENABLE 0
+#define UART3_DMA_RXBUFSIZE 1024
+#define UART3_UFCR_RXTL 16
+#define UART3_UFCR_TXTL 16
+/*
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+/*
+ * Is the MUXED interrupt output sent to the ARM core
+ */
+#define INTS_NOTMUXED 0
+#define INTS_MUXED 1
+/* UART 1 configuration */
+/*!
+ * This define specifies whether the muxed ANDed interrupt line or the
+ * individual interrupts from the UART port is integrated with the ARM core.
+ * There exists a define like this for each UART port. Valid values that can
+ * be used are \b INTS_NOTMUXED or \b INTS_MUXED.
+ */
+#define UART1_MUX_INTS INTS_MUXED
+/*!
+ * This define specifies the transmitter interrupt number or the interrupt
+ * number of the ANDed interrupt in case the interrupts are muxed. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_INT1 MXC_INT_UART1
+/*!
+ * This define specifies the receiver interrupt number. If the interrupts of
+ * the UART are muxed, then we specify here a dummy value -1. There exists a
+ * define like this for each UART port.
+ */
+#define UART1_INT2 -1
+/*!
+ * This specifies the master interrupt number. If the interrupts of the UART
+ * are muxed, then we specify here a dummy value of -1. There exists a define
+ * like this for each UART port.
+ */
+#define UART1_INT3 -1
+/*!
+ * This specifies if the UART is a shared peripheral. It holds the shared
+ * peripheral number if it is shared or -1 if it is not shared. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_SHARED_PERI -1
+/* UART 2 configuration */
+#define UART2_MUX_INTS INTS_MUXED
+#define UART2_INT1 MXC_INT_UART2
+#define UART2_INT2 -1
+#define UART2_INT3 -1
+#define UART2_SHARED_PERI -1
+/* UART 3 configuration */
+#define UART3_MUX_INTS INTS_MUXED
+#define UART3_INT1 MXC_INT_UART3
+#define UART3_INT2 -1
+#define UART3_INT3 -1
+#define UART3_SHARED_PERI SPBA_UART3
+
+#endif /* __ARCH_ARM_MACH_MX37_SERIAL_H__ */
diff --git a/arch/arm/mach-mx37/system.c b/arch/arm/mach-mx37/system.c
new file mode 100644
index 000000000000..78685f625813
--- /dev/null
+++ b/arch/arm/mach-mx37/system.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#define DEBUG
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <asm/proc-fns.h>
+#include <asm/system.h>
+#include <asm/cacheflush.h>
+#include <mach/clock.h>
+#include "crm_regs.h"
+
+/*!
+ * @defgroup MSL_MX37 i.MX37 Machine Specific Layer (MSL)
+ */
+
+/*!
+ * @file mach-mx37/system.c
+ * @brief This file contains idle and reset functions.
+ *
+ * @ingroup MSL_MX37
+ */
+
+extern int mxc_jtag_enabled;
+extern int low_bus_freq_mode;
+
+static struct clk *gpc_dvfs_clk;
+
+/* set cpu low power mode before WFI instruction */
+void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
+{
+ u32 plat_lpc, gpc_pgr, arm_srpgcr, empgcr0, empgcr1, ccm_clpcr;
+ /* always allow platform to issue a deep sleep mode request */
+ plat_lpc = __raw_readl(MXC_ARM1176_PLAT_LPC) &
+ ~(MXC_ARM1176_PLAT_LPC_DSM);
+
+ ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK);
+ gpc_pgr = __raw_readl(MXC_GPC_PGR) & ~(MXC_GPC_PGR_ARMPG_MASK);
+ arm_srpgcr = __raw_readl(MXC_SRPGC_ARM_SRPGCR) & ~(MXC_SRPGCR_PCR);
+ empgcr0 = __raw_readl(MXC_EMPGC0_ARM_EMPGCR) & ~(MXC_EMPGCR_PCR);
+ empgcr1 = __raw_readl(MXC_EMPGC1_ARM_EMPGCR) & ~(MXC_EMPGCR_PCR);
+
+ switch (mode) {
+ case WAIT_CLOCKED:
+ break;
+ case WAIT_UNCLOCKED:
+ ccm_clpcr |= (0x1 << MXC_CCM_CLPCR_LPM_OFFSET);
+ break;
+ case WAIT_UNCLOCKED_POWER_OFF:
+ case STOP_POWER_OFF:
+ plat_lpc |= MXC_ARM1176_PLAT_LPC_DSM;
+ if (mode == WAIT_UNCLOCKED_POWER_OFF)
+ ccm_clpcr |= (0x1 << MXC_CCM_CLPCR_LPM_OFFSET);
+ else {
+ ccm_clpcr |= (0x2 << MXC_CCM_CLPCR_LPM_OFFSET);
+ ccm_clpcr |= (0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET);
+ ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
+ ccm_clpcr |= MXC_CCM_CLPCR_SBYOS;
+ }
+
+ gpc_pgr |= (0x1 << MXC_GPC_PGR_ARMPG_OFFSET);
+ arm_srpgcr |= MXC_SRPGCR_PCR;
+ empgcr0 |= MXC_EMPGCR_PCR;
+ empgcr1 |= MXC_EMPGCR_PCR;
+
+ if (tzic_enable_wake(1) != 0)
+ return;
+ break;
+ case STOP_POWER_ON:
+ ccm_clpcr |= (0x2 << MXC_CCM_CLPCR_LPM_OFFSET);
+ break;
+ default:
+ printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode);
+ return;
+ }
+
+ __raw_writel(plat_lpc, MXC_ARM1176_PLAT_LPC);
+ __raw_writel(ccm_clpcr, MXC_CCM_CLPCR);
+ __raw_writel(gpc_pgr, MXC_GPC_PGR);
+ __raw_writel(arm_srpgcr, MXC_SRPGC_ARM_SRPGCR);
+ if ((mxc_cpu_is_rev(CHIP_REV_1_0)) != 1)
+ __raw_writel(empgcr0, MXC_EMPGC0_ARM_EMPGCR);
+
+ flush_cache_all();
+
+ if (gpc_dvfs_clk == NULL)
+ gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs_clk");
+
+ /* gpc clock is needed for SRPG */
+ clk_enable(gpc_dvfs_clk);
+}
+
+void mxc_pg_enable(struct platform_device *pdev)
+{
+ if (pdev == NULL)
+ return;
+
+ if (strcmp(pdev->name, "mxc_ipu") == 0) {
+ __raw_writel(MXC_PGCR_PCR, MXC_PGC_IPU_PGCR);
+ __raw_writel(MXC_PGSR_PSR, MXC_PGC_IPU_PGSR);
+ } else if (strcmp(pdev->name, "mxc_vpu") == 0) {
+ __raw_writel(MXC_PGCR_PCR, MXC_PGC_VPU_PGCR);
+ __raw_writel(MXC_PGSR_PSR, MXC_PGC_VPU_PGSR);
+ }
+}
+
+EXPORT_SYMBOL(mxc_pg_enable);
+
+void mxc_pg_disable(struct platform_device *pdev)
+{
+ if (pdev == NULL)
+ return;
+
+ if (strcmp(pdev->name, "mxc_ipu") == 0) {
+ __raw_writel(0x0, MXC_PGC_IPU_PGCR);
+ if (__raw_readl(MXC_PGC_IPU_PGSR) & MXC_PGSR_PSR)
+ dev_dbg(&pdev->dev, "power gating successful\n");
+ __raw_writel(MXC_PGSR_PSR, MXC_PGC_IPU_PGSR);
+ } else if (strcmp(pdev->name, "mxc_vpu") == 0) {
+ __raw_writel(0x0, MXC_PGC_VPU_PGCR);
+ if (__raw_readl(MXC_PGC_VPU_PGSR) & MXC_PGSR_PSR)
+ dev_dbg(&pdev->dev, "power gating successful\n");
+ __raw_writel(MXC_PGSR_PSR, MXC_PGC_VPU_PGSR);
+ }
+}
+
+EXPORT_SYMBOL(mxc_pg_disable);
+
+/* To change the idle power mode, need to set arch_idle_mode to a different
+ * power mode as in enum mxc_cpu_pwr_mode.
+ * May allow dynamically changing the idle mode.
+ */
+static int arch_idle_mode = WAIT_UNCLOCKED_POWER_OFF;
+
+/*!
+ * This function puts the CPU into idle mode. It is called by default_idle()
+ * in process.c file.
+ */
+void arch_idle(void)
+{
+ if (likely(!mxc_jtag_enabled)) {
+ mxc_cpu_lp_set(arch_idle_mode);
+ cpu_do_idle();
+ /* gpc clock is needed for SRPG */
+ clk_disable(gpc_dvfs_clk);
+ }
+}
+
+/*
+ * This function resets the system. It is called by machine_restart().
+ *
+ * @param mode indicates different kinds of resets
+ */
+void arch_reset(char mode)
+{
+ /* Assert SRS signal */
+ mxc_wd_reset();
+}
diff --git a/arch/arm/mach-mx37/usb.h b/arch/arm/mach-mx37/usb.h
new file mode 100644
index 000000000000..3542866ada0b
--- /dev/null
+++ b/arch/arm/mach-mx37/usb.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+extern int usbotg_init(struct platform_device *pdev);
+extern void usbotg_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbotg_hs_active(void);
+extern void gpio_usbotg_hs_inactive(void);
+extern struct platform_device *host_pdev_register(struct resource *res,
+ int n_res, struct fsl_usb2_platform_data *config);
+
+extern int gpio_usbotg_utmi_active(void);
+extern void gpio_usbotg_utmi_inactive(void);
+static int usbotg_init_ext(struct platform_device *pdev);
+static void usbotg_uninit_ext(struct fsl_usb2_platform_data *pdata);
+
+/*
+ * Determine which platform_data struct to use for the DR controller,
+ * based on which transceiver is configured.
+ * PDATA is a pointer to it.
+ */
+#if defined(CONFIG_ISP1301_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_1301_config;
+#define PDATA (&dr_1301_config)
+#elif defined(CONFIG_MC13783_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_13783_config;
+#define PDATA (&dr_13783_config)
+#elif defined(CONFIG_UTMI_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config;
+#define PDATA (&dr_utmi_config)
+#endif
+
+
+/*
+ * Used to set pdata->operating_mode before registering the platform_device.
+ * If OTG is configured, the controller operates in OTG mode,
+ * otherwise it's either host or device.
+ */
+#ifdef CONFIG_USB_OTG
+#define DR_UDC_MODE FSL_USB2_DR_OTG
+#define DR_HOST_MODE FSL_USB2_DR_OTG
+#else
+#define DR_UDC_MODE FSL_USB2_DR_DEVICE
+#define DR_HOST_MODE FSL_USB2_DR_HOST
+#endif
+
+
+#ifdef CONFIG_USB_EHCI_ARC_OTG
+static inline void dr_register_host(struct resource *r, int rs)
+{
+ PDATA->operating_mode = DR_HOST_MODE;
+ host_pdev_register(r, rs, PDATA);
+}
+#else
+static inline void dr_register_host(struct resource *r, int rs)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_GADGET_ARC
+static struct platform_device dr_udc_device;
+
+static inline void dr_register_udc(void)
+{
+ PDATA->operating_mode = DR_UDC_MODE;
+ dr_udc_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_udc_device))
+ printk(KERN_ERR "usb: can't register DR gadget\n");
+ else
+ printk(KERN_INFO "usb: DR gadget (%s) registered\n",
+ PDATA->transceiver);
+}
+#else
+static inline void dr_register_udc(void)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_OTG
+static struct platform_device dr_otg_device;
+
+/*
+ * set the proper operating_mode and
+ * platform_data pointer, then register the
+ * device.
+ */
+static inline void dr_register_otg(void)
+{
+ PDATA->operating_mode = FSL_USB2_DR_OTG;
+ dr_otg_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_otg_device))
+ printk(KERN_ERR "usb: can't register otg device\n");
+ else
+ printk(KERN_INFO "usb: DR OTG registered\n");
+}
+#else
+static inline void dr_register_otg(void)
+{
+}
+#endif
diff --git a/arch/arm/mach-mx37/usb_dr.c b/arch/arm/mach-mx37/usb_dr.c
new file mode 100644
index 000000000000..5c102eb246ac
--- /dev/null
+++ b/arch/arm/mach-mx37/usb_dr.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+static void usbotg_pm_clock(bool on);
+static void _wake_up_enable(struct fsl_usb2_platform_data *pdata, bool enable);
+
+/*
+ * platform data structs
+ * - Which one to use is determined by CONFIG options in usb.h
+ * - operating_mode plugged at run time
+ */
+static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config = {
+ .name = "DR",
+ .platform_init = usbotg_init_ext,
+ .platform_uninit = usbotg_uninit_ext,
+ .phy_mode = FSL_USB2_PHY_UTMI_WIDE,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbotg_hs_active,
+ .gpio_usb_inactive = gpio_usbotg_hs_inactive,
+ .usb_clock_for_pm = usbotg_pm_clock,
+ .wake_up_enable = _wake_up_enable,
+ .transceiver = "utmi",
+};
+
+
+/*
+ * resources
+ */
+static struct resource otg_resources[] = {
+ [0] = {
+ .start = (u32)(OTG_BASE_ADDR),
+ .end = (u32)(OTG_BASE_ADDR + 0x620),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB_OTG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+static u64 dr_udc_dmamask = ~(u32) 0;
+static void dr_udc_release(struct device *dev)
+{
+}
+
+/*
+ * platform device structs
+ * dev.platform_data field plugged at run time
+ */
+static struct platform_device dr_udc_device = {
+ .name = "fsl-usb2-udc",
+ .id = -1,
+ .dev = {
+ .release = dr_udc_release,
+ .dma_mask = &dr_udc_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static u64 dr_otg_dmamask = ~(u32) 0;
+static void dr_otg_release(struct device *dev)
+{}
+
+static struct platform_device __maybe_unused dr_otg_device = {
+ .name = "fsl-usb2-otg",
+ .id = -1,
+ .dev = {
+ .release = dr_otg_release,
+ .dma_mask = &dr_otg_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+/* Notes: configure USB clock*/
+static int usbotg_init_ext(struct platform_device *pdev)
+{
+ struct clk *usb_clk, *usboh2_clk;
+ int ret;
+
+ usboh2_clk = clk_get(NULL, "usboh2_clk");
+ clk_enable(usboh2_clk);
+
+ usb_clk = clk_get(NULL, "usb_phy_clk");
+ clk_enable(usb_clk);
+ clk_put(usb_clk);
+
+ ret = usbotg_init(pdev);
+
+ /* this clock is no use after set portsc PTS bit */
+ clk_disable(usboh2_clk);
+ clk_put(usboh2_clk);
+
+ return ret;
+}
+
+static void usbotg_pm_clock(bool on)
+{
+ struct clk *usb_clk;
+
+ usb_clk = clk_get(NULL, "usb_phy_clk");
+ /* close and open usb phy clock for suspend and resume */
+ if (on) {
+ clk_enable(usb_clk);
+ } else {
+ clk_disable(usb_clk);
+ }
+ clk_put(usb_clk);
+}
+
+static void usbotg_uninit_ext(struct fsl_usb2_platform_data *pdata)
+{
+ usbotg_pm_clock(false);
+ usbotg_uninit(pdata);
+}
+
+static void _wake_up_enable(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ if (get_usb_mode(pdata) == FSL_USB_DR_DEVICE) {
+ if (enable) {
+ USBCTRL |= (UCTRL_OWIE | UCTRL_VBUS_WKUP_EN);
+ USB_PHY_CTR_FUNC |= USB_UTMI_PHYCTRL_CONF2;
+ } else {
+ USBCTRL &= ~UCTRL_OWIE;
+ USBCTRL &= ~UCTRL_VBUS_WKUP_EN;
+ USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_CONF2;
+ }
+ }
+}
+
+static int __init usb_dr_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ dr_register_otg();
+ dr_register_host(otg_resources, ARRAY_SIZE(otg_resources));
+ dr_register_udc();
+ device_init_wakeup(&(dr_udc_device.dev), 1);
+ return 0;
+}
+
+module_init(usb_dr_init);
diff --git a/arch/arm/mach-mx51/Kconfig b/arch/arm/mach-mx51/Kconfig
new file mode 100644
index 000000000000..5510ec5d5638
--- /dev/null
+++ b/arch/arm/mach-mx51/Kconfig
@@ -0,0 +1,146 @@
+menu "MX51 Options"
+ depends on ARCH_MX51
+
+config FORCE_MAX_ZONEORDER
+ int "MAX_ORDER"
+ default "13"
+
+config MX51_OPTIONS
+ bool
+ default y
+ select CPU_V7
+ select USB_ARCH_HAS_EHCI
+ select MXC_TZIC
+
+config MACH_MX51_3DS
+ bool "Support MX51 3-Stack platforms"
+ default y
+ help
+ Include support for MX51 3-Stack platform. This includes specific
+ configurations for the board and its peripherals.
+
+config MACH_MX51_BABBAGE
+ bool "Support MX51 BABBAGE platforms"
+ help
+ Include support for MX51 Babbage platform. This includes specific
+ configurations for the board and its peripherals.
+
+config MACH_CCWMX51JS
+ bool "Support for the ConnectCore Wi-MX51 module, on the JSK base board"
+ help
+ Include support for the Digi ConnectCore Wi-MX51 Embedded Module, on the
+ JumpStart Kit base board. This includes specific configurations for the
+ peripherals on that base board.
+
+config MACH_CCMX51JS
+ bool "Support for the ConnectCore MX51 module, on the JSK base board"
+ help
+ Include support for the Digi ConnectCore MX51 Embedded Module, on the
+ JumpStart Kit base board. This includes specific configurations for the
+ peripherals on that base board.
+
+config MACH_CCWMX51
+ bool "Support for the ConnectCore Wi-MX51 module"
+ help
+ Include support for the Digi ConnectCore Wi-MX51 Embedded Module, on a
+ custom board. The machine file should be modified to include support for
+ the interfaces available in that board.
+
+config MACH_CCMX51
+ bool "Support for the ConnectCore MX51 module"
+ help
+ Include support for the Digi ConnectCore Wi-MX51 Embedded Module, on a
+ custom board. The machine file should be modified to include support for
+ the interfaces available in that board.
+
+config MXC_SDMA_API
+ bool "Use SDMA API"
+ default y
+ help
+ This selects the Freescale MXC SDMA API.
+ If unsure, say N.
+
+config ARCH_MXC_HAS_NFC_V3
+ bool "MXC NFC Hardware Version 3"
+ depends on ARCH_MX51
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 3
+ If unsure, say N.
+
+config ARCH_MXC_HAS_NFC_V3_2
+ bool "MXC NFC Hardware Version 3.2"
+ depends on ARCH_MXC_HAS_NFC_V3
+ default y
+ help
+ This selects the Freescale MXC Nand Flash Controller Hardware Version 3.2
+ If unsure, say N.
+
+menu "SDMA options"
+ depends on MXC_SDMA_API
+
+config SDMA_IRAM
+ bool "Use Internal RAM for SDMA transfer"
+ default n
+ help
+ Support Internal RAM as SDMA buffer or control structures
+
+config SDMA_IRAM_SIZE
+ hex "Reserved bytes of IRAM for SDMA (0x800-0x1000)"
+ range 0x800 0x1000
+ depends on SDMA_IRAM
+ default "0x1000"
+ help
+ Set the size of IRAM for SDMA. It must be a multiple of 512bytes.
+endmenu
+
+menu "I2C Options"
+
+config I2C_MXC_SELECT1
+ bool "Enable I2C1 module"
+ default y
+ depends on I2C_MXC
+ help
+ Enable MX51 I2C1 module.
+
+config I2C_MXC_SELECT2
+ bool "Enable I2C2 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX51 I2C2 module.
+
+config I2C_MXC_SELECT3
+ bool "Enable I2C3 module"
+ default n
+ depends on I2C_MXC
+ help
+ Enable MX51 I2C3 module.
+
+endmenu
+
+menu "Serial Port Options"
+config UART1_ENABLED
+ bool "Enable UART1"
+ default y
+ depends on SERIAL_MXC
+ help
+ Enable the MX51 UART1 interface
+
+config UART2_ENABLED
+ bool "Enable UART2"
+ default y
+ depends on SERIAL_MXC
+ help
+ Enable the MX51 UART2 interface
+
+config UART3_ENABLED
+ bool "Enable UART3"
+ default y
+ depends on SERIAL_MXC
+ help
+ Enable the MX51 UART3 interface
+endmenu
+
+endmenu
+
diff --git a/arch/arm/mach-mx51/Makefile b/arch/arm/mach-mx51/Makefile
new file mode 100644
index 000000000000..f0a4070bb26b
--- /dev/null
+++ b/arch/arm/mach-mx51/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+
+obj-y := system.o iomux.o cpu.o mm.o clock.o devices.o serial.o dma.o lpmodes.o pm.o sdram_autogating.o bus_freq.o
+
+obj-y += dummy_gpio.o
+
+obj-$(CONFIG_CPU_V7) += wfi.o suspend.o
+obj-$(CONFIG_MACH_MX51_3DS) += mx51_3stack.o mx51_3stack_gpio.o mx51_3stack_pmic_mc13892.o
+obj-$(CONFIG_MACH_MX51_BABBAGE) += mx51_babbage.o mx51_babbage_gpio.o mx51_babbage_pmic_mc13892.o
+obj-$(CONFIG_MACH_CCWMX51JS) += mx51_ccwmx51js.o mx51_ccwmx51js_gpio.o mx51_ccwmx51js_pmic_mc13892.o
+
+obj-$(CONFIG_USB_EHCI_ARC_H1) += usb_h1.o
+obj-$(CONFIG_USB_EHCI_ARC_H2) += usb_h2.o
+
+ifneq ($(strip $(CONFIG_USB_GADGET_ARC) $(CONFIG_USB_EHCI_ARC_OTG)),)
+ obj-y += usb_dr.o
+endif
+
diff --git a/arch/arm/mach-mx51/Makefile.boot b/arch/arm/mach-mx51/Makefile.boot
new file mode 100644
index 000000000000..9939a19d99a1
--- /dev/null
+++ b/arch/arm/mach-mx51/Makefile.boot
@@ -0,0 +1,3 @@
+ zreladdr-y := 0x90008000
+params_phys-y := 0x90000100
+initrd_phys-y := 0x90800000
diff --git a/arch/arm/mach-mx51/board-ccwmx51.h b/arch/arm/mach-mx51/board-ccwmx51.h
new file mode 100644
index 000000000000..6696c27c5c36
--- /dev/null
+++ b/arch/arm/mach-mx51/board-ccwmx51.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010 Digi International, Inc. All Rights Reserved.
+ *
+ * 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 __ASM_ARCH_MXC_BOARD_CCWMX51_H__
+#define __ASM_ARCH_MXC_BOARD_CCWMX51_H__
+
+#include <mach/mxc_uart.h>
+
+/* UART 1 configuration */
+#if defined CONFIG_UART1_ENABLED
+#define UART1_ENABLED 1
+#else
+#define UART1_ENABLED 0
+#endif
+#define UART1_MODE MODE_DCE
+#define UART1_IR NO_IRDA
+
+/* UART 2 configuration */
+#if defined CONFIG_UART2_ENABLED
+#define UART2_ENABLED 1
+#else
+#define UART2_ENABLED 0
+#endif
+#define UART2_MODE MODE_DCE
+#define UART2_IR NO_IRDA
+
+/* UART 3 configuration */
+#if defined CONFIG_UART3_ENABLED
+#define UART3_ENABLED 1
+#else
+#define UART3_ENABLED 0
+#endif
+#define UART3_MODE MODE_DCE
+#define UART3_IR NO_IRDA
+
+/*!
+ * Specifies if the Irda transmit path is inverting
+ */
+#define MXC_IRDA_TX_INV 0
+/*!
+ * Specifies if the Irda receive path is inverting
+ */
+#define MXC_IRDA_RX_INV 0
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
+
+#endif /* __ASM_ARCH_MXC_BOARD_CCWMX51_H__ */
diff --git a/arch/arm/mach-mx51/board-mx51_3stack.h b/arch/arm/mach-mx51/board-mx51_3stack.h
new file mode 100644
index 000000000000..52f99e01f40d
--- /dev/null
+++ b/arch/arm/mach-mx51/board-mx51_3stack.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_BOARD_MX51_3STACK_H__
+#define __ASM_ARCH_MXC_BOARD_MX51_3STACK_H__
+
+/*!
+ * @defgroup BRDCFG_MX51 Board Configuration Options
+ * @ingroup MSL_MX51
+ */
+
+/*!
+ * @file mach-mx51/board-mx51_3stack.h
+ *
+ * @brief This file contains all the board level configuration options.
+ *
+ * It currently hold the options defined for MX51 3Stack Platform.
+ *
+ * @ingroup BRDCFG_MX51
+ */
+
+/*
+ * Include Files
+ */
+#include <mach/mxc_uart.h>
+
+/*!
+ * @name MXC UART board level configurations
+ */
+/*! @{ */
+/*!
+ * Specifies if the Irda transmit path is inverting
+ */
+#define MXC_IRDA_TX_INV 0
+/*!
+ * Specifies if the Irda receive path is inverting
+ */
+#define MXC_IRDA_RX_INV 0
+
+/* UART 1 configuration */
+/*!
+ * This define specifies if the UART port is configured to be in DTE or
+ * DCE mode. There exists a define like this for each UART port. Valid
+ * values that can be used are \b MODE_DTE or \b MODE_DCE.
+ */
+#define UART1_MODE MODE_DCE
+/*!
+ * This define specifies if the UART is to be used for IRDA. There exists a
+ * define like this for each UART port. Valid values that can be used are
+ * \b IRDA or \b NO_IRDA.
+ */
+#define UART1_IR NO_IRDA
+/*!
+ * This define is used to enable or disable a particular UART port. If
+ * disabled, the UART will not be registered in the file system and the user
+ * will not be able to access it. There exists a define like this for each UART
+ * port. Specify a value of 1 to enable the UART and 0 to disable it.
+ */
+#define UART1_ENABLED 1
+/*! @} */
+/* UART 2 configuration */
+#define UART2_MODE MODE_DCE
+#define UART2_IR NO_IRDA
+#define UART2_ENABLED 1
+/* UART 3 configuration */
+#define UART3_MODE MODE_DCE
+#define UART3_IR NO_IRDA
+#define UART3_ENABLED 1
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
+
+#define DEBUG_BOARD_BASE_ADDRESS(n) (n)
+/* LAN9217 ethernet base address */
+#define LAN9217_BASE_ADDR(n) (DEBUG_BOARD_BASE_ADDRESS(n))
+/* External UART */
+#define UARTA_BASE_ADDR(n) (DEBUG_BOARD_BASE_ADDRESS(n) + 0x8000)
+#define UARTB_BASE_ADDR(n) (DEBUG_BOARD_BASE_ADDRESS(n) + 0x10000)
+
+#define BOARD_IO_ADDR(n) (DEBUG_BOARD_BASE_ADDRESS(n) + 0x20000)
+/* LED switchs */
+#define LED_SWITCH_REG 0x00
+/* buttons */
+#define SWITCH_BUTTONS_REG 0x08
+/* status, interrupt */
+#define INTR_STATUS_REG 0x10
+#define INTR_MASK_REG 0x38
+#define INTR_RESET_REG 0x20
+/* magic word for debug CPLD */
+#define MAGIC_NUMBER1_REG 0x40
+#define MAGIC_NUMBER2_REG 0x48
+/* CPLD code version */
+#define CPLD_CODE_VER_REG 0x50
+/* magic word for debug CPLD */
+#define MAGIC_NUMBER3_REG 0x58
+/* module reset register*/
+#define MODULE_RESET_REG 0x60
+/* CPU ID and Personality ID */
+#define MCU_BOARD_ID_REG 0x68
+
+/* interrupts like external uart , external ethernet etc*/
+#define EXPIO_PARENT_INT IOMUX_TO_IRQ(MX51_PIN_GPIO1_6)
+
+#define EXPIO_INT_ENET (MXC_EXP_IO_BASE + 0)
+#define EXPIO_INT_XUART_A (MXC_EXP_IO_BASE + 1)
+#define EXPIO_INT_XUART_B (MXC_EXP_IO_BASE + 2)
+#define EXPIO_INT_BUTTON_A (MXC_EXP_IO_BASE + 3)
+#define EXPIO_INT_BUTTON_B (MXC_EXP_IO_BASE + 4)
+
+/*! This is System IRQ used by LAN9217 */
+#define LAN9217_IRQ EXPIO_INT_ENET
+
+extern int __init mx51_3stack_init_mc13892(void);
+
+#endif /* __ASM_ARCH_MXC_BOARD_MX51_3STACK_H__ */
diff --git a/arch/arm/mach-mx51/board-mx51_babbage.h b/arch/arm/mach-mx51/board-mx51_babbage.h
new file mode 100644
index 000000000000..1e6c7c8e849e
--- /dev/null
+++ b/arch/arm/mach-mx51/board-mx51_babbage.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_BOARD_MX51_BABBAGE_H__
+#define __ASM_ARCH_MXC_BOARD_MX51_BABBAGE_H__
+
+/*!
+ * @defgroup BRDCFG_MX51 Board Configuration Options
+ * @ingroup MSL_MX51
+ */
+
+/*!
+ * @file mach-mx51/board-mx51_babbage.h
+ *
+ * @brief This file contains all the board level configuration options.
+ *
+ * It currently hold the options defined for MX51 Babbage Platform.
+ *
+ * @ingroup BRDCFG_MX51
+ */
+
+/*
+ * Include Files
+ */
+#include <mach/mxc_uart.h>
+
+/*!
+ * @name MXC UART board level configurations
+ */
+/*! @{ */
+/*!
+ * Specifies if the Irda transmit path is inverting
+ */
+#define MXC_IRDA_TX_INV 0
+/*!
+ * Specifies if the Irda receive path is inverting
+ */
+#define MXC_IRDA_RX_INV 0
+
+/* UART 1 configuration */
+/*!
+ * This define specifies if the UART port is configured to be in DTE or
+ * DCE mode. There exists a define like this for each UART port. Valid
+ * values that can be used are \b MODE_DTE or \b MODE_DCE.
+ */
+#define UART1_MODE MODE_DCE
+/*!
+ * This define specifies if the UART is to be used for IRDA. There exists a
+ * define like this for each UART port. Valid values that can be used are
+ * \b IRDA or \b NO_IRDA.
+ */
+#define UART1_IR NO_IRDA
+/*!
+ * This define is used to enable or disable a particular UART port. If
+ * disabled, the UART will not be registered in the file system and the user
+ * will not be able to access it. There exists a define like this for each UART
+ * port. Specify a value of 1 to enable the UART and 0 to disable it.
+ */
+#define UART1_ENABLED 1
+/*! @} */
+/* UART 2 configuration */
+#define UART2_MODE MODE_DCE
+#define UART2_IR IRDA
+#define UART2_ENABLED 1
+/* UART 3 configuration */
+#define UART3_MODE MODE_DTE
+#define UART3_IR NO_IRDA
+#define UART3_ENABLED 1
+
+#define MXC_LL_UART_PADDR UART1_BASE_ADDR
+#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
+
+extern int __init mx51_babbage_init_mc13892(void);
+
+#endif /* __ASM_ARCH_MXC_BOARD_MX51_BABBAGE_H__ */
diff --git a/arch/arm/mach-mx51/board.h b/arch/arm/mach-mx51/board.h
new file mode 100644
index 000000000000..e269ab46fb5a
--- /dev/null
+++ b/arch/arm/mach-mx51/board.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010 Digi International, Inc. All Rights Reserved.
+ *
+ * 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 __ASM_ARCH_MXC_BOARD_H__
+#define __ASM_ARCH_MXC_BOARD_H__
+
+#if defined(CONFIG_MACH_CCWMX51JS) || defined(CONFIG_MACH_CCMX51JS) || \
+ defined(CONFIG_MACH_CCWMX51) || defined(CONFIG_MACH_CCMX51)
+#include "board-ccwmx51.h"
+#elif defined(MACH_MX51_3DS)
+#include "board-3stack.h"
+#elif defined(MACH_MX51_BABBAGE)
+#include "board-mx51_babbage.h"
+#endif
+
+#endif /* __ASM_ARCH_MXC_BOARD_H__ */
diff --git a/arch/arm/mach-mx51/bus_freq.c b/arch/arm/mach-mx51/bus_freq.c
new file mode 100644
index 000000000000..a1c4777ede6a
--- /dev/null
+++ b/arch/arm/mach-mx51/bus_freq.c
@@ -0,0 +1,579 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file bus_freq.c
+ *
+ * @brief A common API for the Freescale Semiconductor i.MXC CPUfreq module
+ * and DVFS CORE module.
+ *
+ * The APIs are for setting bus frequency to low or high.
+ *
+ * @ingroup PM
+ */
+#include <asm/io.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/mxc_dvfs.h>
+#include "crm_regs.h"
+
+#define LP_NORMAL_CLK 133000000
+#define LP_MED_CLK 83125000
+#define LP_APM_CLK 24000000
+#define NAND_LP_APM_CLK 12000000
+#define DDR_LOW_FREQ_CLK 133000000
+#define DDR_NORMAL_CLK 200000000
+#define AXI_A_NORMAL_CLK 166250000
+#define AXI_A_CLK_NORMAL_DIV 4
+#define AXI_B_CLK_NORMAL_DIV 5
+#define AHB_CLK_NORMAL_DIV AXI_B_CLK_NORMAL_DIV
+#define EMI_SLOW_CLK_NORMAL_DIV AXI_B_CLK_NORMAL_DIV
+#define NFC_CLK_NORMAL_DIV 4
+
+static struct clk *ddr_clk;
+static struct clk *pll2;
+static struct clk *main_bus_clk;
+static struct clk *axi_a_clk;
+static struct clk *axi_b_clk;
+static struct clk *cpu_clk;
+static struct clk *ddr_hf_clk;
+static struct clk *nfc_clk;
+static struct clk *ahb_clk;
+static struct clk *vpu_clk;
+static struct clk *vpu_core_clk;
+static struct clk *emi_slow_clk;
+static struct clk *ddr_clk;
+static struct clk *ipu_clk;
+static struct clk *periph_apm_clk;
+static struct clk *lp_apm;
+static struct clk *osc;
+static struct clk *gpc_dvfs_clk;
+static struct clk *mipi_hsp_clk;
+struct regulator *lp_regulator;
+int low_bus_freq_mode;
+int high_bus_freq_mode;
+int bus_freq_scaling_is_active;
+char *gp_reg_id = "SW1";
+char *lp_reg_id = "SW2";
+
+static struct cpu_wp *cpu_wp_tbl;
+static struct device *busfreq_dev;
+static int busfreq_suspended;
+int sdram_autogating_paused;
+
+extern int lp_high_freq;
+extern int lp_med_freq;
+extern int dvfs_core_is_active;
+extern struct cpu_wp *(*get_cpu_wp)(int *wp);
+extern int cpu_wp_nr;
+extern int sdram_autogating_is_active;
+extern void enable_sdram_autogating(void);
+extern void disable_sdram_autogating(void);
+
+struct dvfs_wp dvfs_core_setpoint[] = {
+ {33, 8, 33, 10, 10, 0x08},
+ {26, 0, 33, 20, 10, 0x08},
+ {28, 8, 33, 20, 30, 0x08},
+ {29, 0, 33, 20, 10, 0x08},};
+
+#define DISABLE_PLL1
+
+int set_low_bus_freq(void)
+{
+ struct clk *tclk;
+ u32 reg;
+
+ if (bus_freq_scaling_is_active) {
+
+ if (busfreq_suspended)
+ return 0;
+
+ if (clk_get_rate(cpu_clk) != cpu_wp_tbl[cpu_wp_nr - 1].cpu_rate)
+ return 0;
+
+ if (sdram_autogating_is_active) {
+ disable_sdram_autogating();
+ sdram_autogating_paused = 1;
+ }
+#ifdef DISABLE_PLL1
+ tclk = clk_get(NULL, "ddr_clk");
+ clk_set_parent(tclk, clk_get(NULL, "axi_a_clk"));
+
+ /* Set CPU clock to be derived from PLL2 instead of PLL1 */
+ tclk = clk_get(NULL, "pll1_sw_clk");
+ clk_set_parent(tclk, clk_get(NULL, "pll2"));
+ clk_enable(tclk);
+
+ tclk = clk_get(NULL, "ddr_clk");
+ clk_set_parent(tclk, clk_get(NULL, "ddr_hf_clk"));
+#endif
+
+ /*Change the DDR freq to 133Mhz. */
+ clk_set_rate(ddr_hf_clk,
+ clk_round_rate(ddr_hf_clk, DDR_LOW_FREQ_CLK));
+
+ clk_enable(gpc_dvfs_clk);
+
+ /* Setup the GPC. */
+ reg = __raw_readl(MXC_GPC_VCR);
+ reg &= ~(MXC_GPCVCR_VINC_MASK | MXC_GPCVCR_VCNTU_MASK |
+ MXC_GPCVCR_VCNT_MASK);
+
+ reg |= (1 << MXC_GPCVCR_VCNTU_OFFSET) |
+ (0 << MXC_GPCVCR_VCNT_OFFSET);
+ __raw_writel(reg, MXC_GPC_VCR);
+
+ reg = __raw_readl(MXC_GPC_CNTR);
+ reg &= ~(MXC_GPCCNTR_ADU_MASK | MXC_GPCCNTR_FUPD_MASK);
+ reg |= MXC_GPCCNTR_FUPD;
+ __raw_writel(reg, MXC_GPC_CNTR);
+
+ /* Enable DVFS-PER */
+ reg = __raw_readl(MXC_DVFSPER_PMCR0);
+ reg &= ~(MXC_DVFSPER_PMCR0_UDCS_MASK |
+ MXC_DVFSPER_PMCR0_ENABLE_MASK);
+ reg |= MXC_DVFSPER_PMCR0_ENABLE;
+ __raw_writel(reg, MXC_DVFSPER_PMCR0);
+
+ /* Set the dvfs-podf to divide by 4. */
+ reg = __raw_readl(MXC_CCM_CDCR);
+ reg &= ~MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_MASK;
+ reg |= 3 << MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CDCR);
+
+ /* Setup the GPC */
+ reg = __raw_readl(MXC_GPC_VCR) & ~MXC_GPCVCR_VINC_MASK;
+ /* Set VINC to 0. */
+ reg |= 0 << MXC_GPCVCR_VINC_OFFSET;
+ __raw_writel(reg, MXC_GPC_VCR);
+
+ reg = __raw_readl(MXC_GPC_CNTR);
+ reg |= MXC_GPCCNTR_STRT;
+ __raw_writel(reg, MXC_GPC_CNTR);
+ while (__raw_readl(MXC_GPC_CNTR) & MXC_GPCCNTR_STRT)
+ udelay(10);
+
+ /* Disable DVFS-PER */
+ reg = __raw_readl(MXC_DVFSPER_PMCR0);
+ reg &= ~MXC_DVFSPER_PMCR0_ENABLE;
+ __raw_writel(reg, MXC_DVFSPER_PMCR0);
+
+ /* Set the dividers to be close to 24Mhz from 166.25MHz*/
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ reg &= ~(MXC_CCM_CBCDR_AXI_A_PODF_MASK
+ | MXC_CCM_CBCDR_AXI_B_PODF_MASK
+ | MXC_CCM_CBCDR_AHB_PODF_MASK
+ | MXC_CCM_CBCDR_EMI_PODF_MASK
+ | MXC_CCM_CBCDR_NFC_PODF_OFFSET);
+ reg |= (6 << MXC_CCM_CBCDR_AXI_A_PODF_OFFSET
+ | 6 << MXC_CCM_CBCDR_AXI_B_PODF_OFFSET
+ | 6 << MXC_CCM_CBCDR_AHB_PODF_OFFSET
+ | 6 << MXC_CCM_CBCDR_EMI_PODF_OFFSET
+ | 3 << MXC_CCM_CBCDR_NFC_PODF_OFFSET);
+ __raw_writel(reg, MXC_CCM_CBCDR);
+
+ while (__raw_readl(MXC_CCM_CDHIPR) & 0x1F)
+ udelay(10);
+ clk_set_parent(main_bus_clk, pll2);
+
+ low_bus_freq_mode = 1;
+ high_bus_freq_mode = 0;
+ }
+ return 0;
+}
+
+int set_high_bus_freq(int high_bus_freq)
+{
+ u32 dvfs_podf = __raw_readl(MXC_CCM_CDCR) & 0x3;
+ u32 reg;
+ struct clk *tclk;
+
+ if (bus_freq_scaling_is_active) {
+ if (sdram_autogating_is_active) {
+ disable_sdram_autogating();
+ sdram_autogating_paused = 1;
+ }
+
+ if (dvfs_podf > 1) {
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ reg &= ~(MXC_CCM_CBCDR_AXI_A_PODF_MASK
+ | MXC_CCM_CBCDR_AXI_B_PODF_MASK
+ | MXC_CCM_CBCDR_AHB_PODF_MASK
+ | MXC_CCM_CBCDR_EMI_PODF_MASK
+ | MXC_CCM_CBCDR_NFC_PODF_OFFSET);
+ reg |= (3 << MXC_CCM_CBCDR_AXI_A_PODF_OFFSET
+ | 4 << MXC_CCM_CBCDR_AXI_B_PODF_OFFSET
+ | 4 << MXC_CCM_CBCDR_AHB_PODF_OFFSET
+ | 4 << MXC_CCM_CBCDR_EMI_PODF_OFFSET
+ | 3 << MXC_CCM_CBCDR_NFC_PODF_OFFSET);
+ __raw_writel(reg, MXC_CCM_CBCDR);
+ while (__raw_readl(MXC_CCM_CDHIPR) & 0x1F)
+ udelay(10);
+
+ /* Setup the GPC. */
+ reg = __raw_readl(MXC_GPC_VCR);
+ reg &= ~(MXC_GPCVCR_VINC_MASK | MXC_GPCVCR_VCNTU_MASK |
+ MXC_GPCVCR_VCNT_MASK);
+
+ reg |= (1 << MXC_GPCVCR_VCNTU_OFFSET) |
+ (0 << MXC_GPCVCR_VCNT_OFFSET);
+ __raw_writel(reg, MXC_GPC_VCR);
+
+ reg = __raw_readl(MXC_GPC_CNTR);
+ reg &= ~(MXC_GPCCNTR_ADU_MASK | MXC_GPCCNTR_FUPD_MASK);
+ reg |= MXC_GPCCNTR_FUPD;
+ __raw_writel(reg, MXC_GPC_CNTR);
+
+ /* Enable DVFS-PER */
+ reg = __raw_readl(MXC_DVFSPER_PMCR0);
+ reg |= MXC_DVFSPER_PMCR0_UDCS;
+ reg |= MXC_DVFSPER_PMCR0_ENABLE;
+ __raw_writel(reg, MXC_DVFSPER_PMCR0);
+
+ /* Set the dvfs-podf to divide by 1. */
+ reg = __raw_readl(MXC_CCM_CDCR);
+ reg &= ~MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_MASK;
+ reg |= 0 << MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CDCR);
+
+ /* Setup the GPC */
+ reg = __raw_readl(MXC_GPC_VCR) & ~MXC_GPCVCR_VINC_MASK;
+ /* Set VINC to 1. */
+ reg |= 1 << MXC_GPCVCR_VINC_OFFSET;
+ __raw_writel(reg, MXC_GPC_VCR);
+
+ reg = __raw_readl(MXC_GPC_CNTR);
+ reg |= MXC_GPCCNTR_STRT;
+ __raw_writel(reg, MXC_GPC_CNTR);
+ while (__raw_readl(MXC_GPC_CNTR) & MXC_GPCCNTR_STRT)
+ udelay(10);
+
+ /* Disable DVFS-PER */
+ reg = __raw_readl(MXC_DVFSPER_PMCR0);
+ reg &= ~MXC_DVFSPER_PMCR0_ENABLE;
+ __raw_writel(reg, MXC_DVFSPER_PMCR0);
+
+ clk_set_parent(main_bus_clk, pll2);
+ clk_disable(gpc_dvfs_clk);
+#ifdef DISABLE_PLL1
+ tclk = clk_get(NULL, "ddr_clk");
+ clk_set_parent(tclk, clk_get(NULL, "axi_a_clk"));
+
+ /* Set CPU clock to be derived from PLL1 instead of PLL2 */
+ tclk = clk_get(NULL, "pll1_sw_clk");
+ clk_set_parent(tclk, clk_get(NULL, "pll1_main_clk"));
+ clk_disable(tclk);
+
+ tclk = clk_get(NULL, "ddr_clk");
+ clk_set_parent(tclk, clk_get(NULL, "ddr_hf_clk"));
+#endif
+ /*Change the DDR freq to 200MHz*/
+ clk_set_rate(ddr_hf_clk,
+ clk_round_rate(ddr_hf_clk, DDR_NORMAL_CLK));
+
+ low_bus_freq_mode = 0;
+ }
+
+ /*
+ * If the CPU freq is 800MHz, set the bus to the high setpoint
+ * (133MHz) and DDR to 200MHz.
+ */
+ if (clk_get_rate(cpu_clk) != cpu_wp_tbl[cpu_wp_nr - 1].cpu_rate)
+ high_bus_freq = 1;
+
+ if (((clk_get_rate(ahb_clk) == LP_MED_CLK) && lp_high_freq) ||
+ high_bus_freq) {
+ /* Set to the high setpoint. */
+ high_bus_freq_mode = 1;
+ clk_set_rate(ahb_clk,
+ clk_round_rate(ahb_clk, LP_NORMAL_CLK));
+ clk_set_rate(ddr_hf_clk,
+ clk_round_rate(ddr_hf_clk, DDR_NORMAL_CLK));
+ }
+ if (!lp_high_freq && !high_bus_freq) {
+ /* Set to the medium setpoint. */
+ high_bus_freq_mode = 0;
+ low_bus_freq_mode = 0;
+ clk_set_rate(ddr_hf_clk,
+ clk_round_rate(ddr_hf_clk, DDR_LOW_FREQ_CLK));
+ clk_set_rate(ahb_clk,
+ clk_round_rate(ahb_clk, LP_MED_CLK));
+ }
+ if (sdram_autogating_paused) {
+ enable_sdram_autogating();
+ sdram_autogating_paused = 0;
+ }
+ }
+ return 0;
+}
+
+int low_freq_bus_used(void)
+{
+ if ((clk_get_usecount(ipu_clk) == 0)
+ && (clk_get_usecount(vpu_clk) == 0)
+ && (lp_high_freq == 0)
+ && (lp_med_freq == 0))
+ return 1;
+ else
+ return 0;
+}
+
+void setup_pll(void)
+{
+}
+
+static ssize_t bus_freq_scaling_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (bus_freq_scaling_is_active)
+ return sprintf(buf, "Bus frequency scaling is enabled\n");
+ else
+ return sprintf(buf, "Bus frequency scaling is disabled\n");
+}
+
+static ssize_t bus_freq_scaling_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (strstr(buf, "1") != NULL)
+ bus_freq_scaling_is_active = 1;
+ else if (strstr(buf, "0") != NULL) {
+ if (bus_freq_scaling_is_active)
+ set_high_bus_freq(1);
+ bus_freq_scaling_is_active = 0;
+ }
+ return size;
+}
+
+static int busfreq_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ if (low_bus_freq_mode)
+ set_high_bus_freq(1);
+ busfreq_suspended = 1;
+ return 0;
+}
+
+static int busfreq_resume(struct platform_device *pdev)
+{
+ busfreq_suspended = 0;
+ return 0;
+}
+
+static DEVICE_ATTR(enable, 0644, bus_freq_scaling_enable_show,
+ bus_freq_scaling_enable_store);
+
+/*!
+ * This is the probe routine for the bus frequency driver.
+ *
+ * @param pdev The platform device structure
+ *
+ * @return The function returns 0 on success
+ *
+ */
+static int __devinit busfreq_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ u32 reg;
+
+ busfreq_dev = &pdev->dev;
+
+ main_bus_clk = clk_get(NULL, "main_bus_clk");
+ if (IS_ERR(main_bus_clk)) {
+ printk(KERN_DEBUG "%s: failed to get main_bus_clk\n",
+ __func__);
+ return PTR_ERR(main_bus_clk);
+ }
+
+ pll2 = clk_get(NULL, "pll2");
+ if (IS_ERR(pll2)) {
+ printk(KERN_DEBUG "%s: failed to get pll2\n", __func__);
+ return PTR_ERR(pll2);
+ }
+
+ axi_a_clk = clk_get(NULL, "axi_a_clk");
+ if (IS_ERR(axi_a_clk)) {
+ printk(KERN_DEBUG "%s: failed to get axi_a_clk\n",
+ __func__);
+ return PTR_ERR(axi_a_clk);
+ }
+
+ axi_b_clk = clk_get(NULL, "axi_b_clk");
+ if (IS_ERR(axi_b_clk)) {
+ printk(KERN_DEBUG "%s: failed to get axi_b_clk\n",
+ __func__);
+ return PTR_ERR(axi_b_clk);
+ }
+
+ ddr_hf_clk = clk_get(NULL, "ddr_hf_clk");
+ if (IS_ERR(ddr_hf_clk)) {
+ printk(KERN_DEBUG "%s: failed to get ddr_hf_clk\n",
+ __func__);
+ return PTR_ERR(ddr_hf_clk);
+ }
+
+ emi_slow_clk = clk_get(NULL, "emi_slow_clk");
+ if (IS_ERR(emi_slow_clk)) {
+ printk(KERN_DEBUG "%s: failed to get emi_slow_clk\n",
+ __func__);
+ return PTR_ERR(emi_slow_clk);
+ }
+
+ nfc_clk = clk_get(NULL, "nfc_clk");
+ if (IS_ERR(nfc_clk)) {
+ printk(KERN_DEBUG "%s: failed to get nfc_clk\n",
+ __func__);
+ return PTR_ERR(nfc_clk);
+ }
+
+ ahb_clk = clk_get(NULL, "ahb_clk");
+ if (IS_ERR(ahb_clk)) {
+ printk(KERN_DEBUG "%s: failed to get ahb_clk\n",
+ __func__);
+ return PTR_ERR(ahb_clk);
+ }
+
+ vpu_core_clk = clk_get(NULL, "vpu_core_clk");
+ if (IS_ERR(vpu_core_clk)) {
+ printk(KERN_DEBUG "%s: failed to get vpu_core_clk\n",
+ __func__);
+ return PTR_ERR(vpu_core_clk);
+ }
+
+ ddr_clk = clk_get(NULL, "ddr_clk");
+ if (IS_ERR(ddr_clk)) {
+ printk(KERN_DEBUG "%s: failed to get ddr_clk\n",
+ __func__);
+ return PTR_ERR(ddr_clk);
+ }
+
+ cpu_clk = clk_get(NULL, "cpu_clk");
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_DEBUG "%s: failed to get cpu_clk\n",
+ __func__);
+ return PTR_ERR(cpu_clk);
+ }
+
+ ipu_clk = clk_get(NULL, "ipu_clk");
+ if (IS_ERR(ipu_clk)) {
+ printk(KERN_DEBUG "%s: failed to get ipu_clk\n",
+ __func__);
+ return PTR_ERR(ipu_clk);
+ }
+
+ mipi_hsp_clk = clk_get(NULL, "mipi_hsp_clk");
+ if (IS_ERR(mipi_hsp_clk)) {
+ printk(KERN_DEBUG "%s: failed to get mipi_hsp_clk\n",
+ __func__);
+ return PTR_ERR(mipi_hsp_clk);
+ }
+
+ vpu_clk = clk_get(NULL, "vpu_clk");
+ if (IS_ERR(vpu_clk)) {
+ printk(KERN_DEBUG "%s: failed to get vpu_clk\n",
+ __func__);
+ return PTR_ERR(vpu_clk);
+ }
+
+ periph_apm_clk = clk_get(NULL, "periph_apm_clk");
+ if (IS_ERR(periph_apm_clk)) {
+ printk(KERN_DEBUG "%s: failed to get periph_apm_clk\n",
+ __func__);
+ return PTR_ERR(periph_apm_clk);
+ }
+
+ lp_apm = clk_get(NULL, "lp_apm");
+ if (IS_ERR(lp_apm)) {
+ printk(KERN_DEBUG "%s: failed to get lp_apm\n",
+ __func__);
+ return PTR_ERR(lp_apm);
+ }
+
+ osc = clk_get(NULL, "osc");
+ if (IS_ERR(osc)) {
+ printk(KERN_DEBUG "%s: failed to get osc\n", __func__);
+ return PTR_ERR(osc);
+ }
+
+ gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs_clk");
+ if (IS_ERR(gpc_dvfs_clk)) {
+ printk(KERN_DEBUG "%s: failed to get gpc_dvfs_clk\n", __func__);
+ return PTR_ERR(gpc_dvfs_clk);
+ }
+
+ err = sysfs_create_file(&busfreq_dev->kobj, &dev_attr_enable.attr);
+ if (err) {
+ printk(KERN_ERR
+ "Unable to register sysdev entry for BUSFREQ");
+ return err;
+ }
+
+ /* Initialize DVFS-PODF to 0. */
+ reg = __raw_readl(MXC_CCM_CDCR);
+ reg &= ~MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_MASK;
+ __raw_writel(reg, MXC_CCM_CDCR);
+ clk_set_parent(main_bus_clk, pll2);
+
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+ low_bus_freq_mode = 0;
+ high_bus_freq_mode = 1;
+ bus_freq_scaling_is_active = 0;
+
+ return 0;
+}
+
+static struct platform_driver busfreq_driver = {
+ .driver = {
+ .name = "busfreq",
+ },
+ .probe = busfreq_probe,
+ .suspend = busfreq_suspend,
+ .resume = busfreq_resume,
+};
+
+/*!
+ * Initialise the busfreq_driver.
+ *
+ * @return The function always returns 0.
+ */
+
+static int __init busfreq_init(void)
+{
+ if (platform_driver_register(&busfreq_driver) != 0) {
+ printk(KERN_ERR "busfreq_driver register failed\n");
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "Bus freq driver module loaded\n");
+ return 0;
+}
+
+static void __exit busfreq_cleanup(void)
+{
+ sysfs_remove_file(&busfreq_dev->kobj, &dev_attr_enable.attr);
+
+ /* Unregister the device structure */
+ platform_driver_unregister(&busfreq_driver);
+}
+
+module_init(busfreq_init);
+module_exit(busfreq_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("BusFreq driver");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/arm/mach-mx51/clock.c b/arch/arm/mach-mx51/clock.c
new file mode 100644
index 000000000000..7de58e62778d
--- /dev/null
+++ b/arch/arm/mach-mx51/clock.c
@@ -0,0 +1,3898 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/hrtimer.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <asm/io.h>
+#include <asm/div64.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/spba.h>
+
+#include "crm_regs.h"
+
+static unsigned long pll_base[] = {
+ (unsigned long)MXC_DPLL1_BASE,
+ (unsigned long)MXC_DPLL2_BASE,
+ (unsigned long)MXC_DPLL3_BASE,
+};
+
+static struct clk pll1_main_clk;
+static struct clk pll1_sw_clk;
+static struct clk pll2_sw_clk;
+static struct clk pll3_sw_clk;
+static struct clk lp_apm_clk;
+static struct clk tve_clk;
+static struct clk emi_fast_clk;
+static struct clk emi_slow_clk;
+static struct clk emi_intr_clk;
+static struct clk ddr_clk;
+static struct clk ipu_clk[];
+static struct clk axi_a_clk;
+static struct clk axi_b_clk;
+static struct clk ddr_hf_clk;
+static struct clk mipi_hsp_clk;
+static struct clk gpu3d_clk;
+static struct clk gpu2d_clk;
+static struct clk vpu_clk[];
+static int cpu_curr_wp;
+static struct cpu_wp *cpu_wp_tbl;
+
+int cpu_wp_nr;
+int lp_high_freq;
+int lp_med_freq;
+
+#define SPIN_DELAY 1000000 /* in nanoseconds */
+
+extern int mxc_jtag_enabled;
+extern int cpufreq_trig_needed;
+
+static int cpu_clk_set_wp(int wp);
+extern void propagate_rate(struct clk *tclk);
+struct cpu_wp *(*get_cpu_wp)(int *wp);
+void (*set_num_cpu_wp)(int num);
+
+static void __calc_pre_post_dividers(u32 div, u32 *pre, u32 *post)
+{
+ u32 min_pre, temp_pre, old_err, err;
+
+ if (div >= 512) {
+ *pre = 8;
+ *post = 64;
+ } else if (div >= 8) {
+ min_pre = (div - 1) / 64 + 1;
+ old_err = 8;
+ for (temp_pre = 8; temp_pre >= min_pre; temp_pre--) {
+ err = div % temp_pre;
+ if (err == 0) {
+ *pre = temp_pre;
+ break;
+ }
+ err = temp_pre - err;
+ if (err < old_err) {
+ old_err = err;
+ *pre = temp_pre;
+ }
+ }
+ *post = (div + *pre - 1) / *pre;
+ } else if (div < 8) {
+ *pre = div;
+ *post = 1;
+ }
+}
+
+static int _clk_enable(struct clk *clk)
+{
+ u32 reg;
+ reg = __raw_readl(clk->enable_reg);
+ reg |= MXC_CCM_CCGR_CG_MASK << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+
+ if (clk->flags & AHB_HIGH_SET_POINT)
+ lp_high_freq++;
+ else if (clk->flags & AHB_MED_SET_POINT)
+ lp_med_freq++;
+
+
+ return 0;
+}
+
+static void _clk_disable(struct clk *clk)
+{
+ u32 reg;
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+
+ if (clk->flags & AHB_HIGH_SET_POINT)
+ lp_high_freq--;
+ else if (clk->flags & AHB_MED_SET_POINT)
+ lp_med_freq--;
+
+}
+
+static void _clk_disable_inwait(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift);
+ reg |= 1 << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+}
+
+static int _clk_enable_inwait(struct clk *clk)
+{
+ _clk_disable_inwait(clk);
+ return 0;
+}
+
+/*
+ * For the 4-to-1 muxed input clock
+ */
+static inline u32 _get_mux(struct clk *parent, struct clk *m0,
+ struct clk *m1, struct clk *m2, struct clk *m3)
+{
+ if (parent == m0)
+ return 0;
+ else if (parent == m1)
+ return 1;
+ else if (parent == m2)
+ return 2;
+ else if (parent == m3)
+ return 3;
+ else
+ BUG();
+
+ return 0;
+}
+
+/*
+ * For the ddr muxed input clock
+ */
+static inline u32 _get_mux_ddr(struct clk *parent, struct clk *m0,
+ struct clk *m1, struct clk *m2, struct clk *m3, struct clk *m4)
+{
+ if (parent == m0)
+ return 0;
+ else if (parent == m1)
+ return 1;
+ else if (parent == m2)
+ return 2;
+ else if (parent == m3)
+ return 3;
+ else if (parent == m4)
+ return 4;
+ else
+ BUG();
+
+ return 0;
+}
+
+static inline unsigned long _get_pll_base(struct clk *pll)
+{
+ if (pll == &pll1_main_clk)
+ return pll_base[0];
+ else if (pll == &pll2_sw_clk)
+ return pll_base[1];
+ else if (pll == &pll3_sw_clk)
+ return pll_base[2];
+ else
+ BUG();
+
+ return 0;
+}
+
+static struct clk ckih_clk = {
+ .name = "ckih",
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ckih2_clk = {
+ .name = "ckih2",
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk osc_clk = {
+ .name = "osc",
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ckil_clk = {
+ .name = "ckil",
+ .flags = RATE_PROPAGATES,
+};
+
+static void _fpm_recalc(struct clk *clk)
+{
+ clk->rate = ckil_clk.rate * 512;
+ if ((__raw_readl(MXC_CCM_CCR) & MXC_CCM_CCR_FPM_MULT_MASK) != 0)
+ clk->rate *= 2;
+
+}
+
+static int _fpm_enable(struct clk *clk)
+{
+ u32 reg = __raw_readl(MXC_CCM_CCR);
+ reg |= MXC_CCM_CCR_FPM_EN;
+ __raw_writel(reg, MXC_CCM_CCR);
+ return 0;
+}
+
+static void _fpm_disable(struct clk *clk)
+{
+ u32 reg = __raw_readl(MXC_CCM_CCR);
+ reg &= ~MXC_CCM_CCR_FPM_EN;
+ __raw_writel(reg, MXC_CCM_CCR);
+}
+
+static struct clk fpm_clk = {
+ .name = "fpm_clk",
+ .parent = &ckil_clk,
+ .recalc = _fpm_recalc,
+ .enable = _fpm_enable,
+ .disable = _fpm_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _fpm_div2_recalc(struct clk *clk)
+{
+ clk->rate = clk->parent->rate / 2;
+}
+
+static struct clk fpm_div2_clk = {
+ .name = "fpm_div2_clk",
+ .parent = &fpm_clk,
+ .recalc = _fpm_div2_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_pll_recalc(struct clk *clk)
+{
+ long mfi, mfn, mfd, pdf, ref_clk, mfn_abs;
+ unsigned long dp_op, dp_mfd, dp_mfn, dp_ctl, pll_hfsm, dbl;
+ unsigned long pllbase;
+ s64 temp;
+
+ pllbase = _get_pll_base(clk);
+
+ dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
+ pll_hfsm = dp_ctl & MXC_PLL_DP_CTL_HFSM;
+ dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN;
+
+ 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_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET;
+ mfi = (mfi <= 5) ? 5 : mfi;
+ mfd = dp_mfd & MXC_PLL_DP_MFD_MASK;
+ mfn = mfn_abs = dp_mfn & MXC_PLL_DP_MFN_MASK;
+ /* Sign extend to 32-bits */
+ if (mfn >= 0x04000000) {
+ mfn |= 0xFC000000;
+ mfn_abs = -mfn;
+ }
+
+ ref_clk = 2 * clk->parent->rate;
+ if (dbl != 0)
+ ref_clk *= 2;
+
+ ref_clk /= (pdf + 1);
+ temp = (u64) ref_clk * mfn_abs;
+ do_div(temp, mfd + 1);
+ if (mfn < 0)
+ temp = -temp;
+ temp = (ref_clk * mfi) + temp;
+
+ clk->rate = temp;
+}
+
+static int _clk_pll_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 pllbase;
+
+ long mfi, pdf, mfn, mfd = 999999;
+ s64 temp64;
+ unsigned long quad_parent_rate;
+ unsigned long pll_hfsm, dp_ctl;
+
+ pllbase = _get_pll_base(clk);
+
+ quad_parent_rate = 4*clk->parent->rate;
+ pdf = mfi = -1;
+ while (++pdf < 16 && mfi < 5)
+ mfi = rate * (pdf+1) / quad_parent_rate;
+ if (mfi > 15)
+ return -1;
+ pdf--;
+
+ temp64 = rate*(pdf+1) - quad_parent_rate*mfi;
+ do_div(temp64, quad_parent_rate/1000000);
+ mfn = (long)temp64;
+
+ dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
+ /* use dpdck0_2 */
+ __raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL);
+ pll_hfsm = dp_ctl & MXC_PLL_DP_CTL_HFSM;
+ if (pll_hfsm == 0) {
+ reg = mfi<<4 | pdf;
+ __raw_writel(reg, pllbase + MXC_PLL_DP_OP);
+ __raw_writel(mfd, pllbase + MXC_PLL_DP_MFD);
+ __raw_writel(mfn, pllbase + MXC_PLL_DP_MFN);
+ } else {
+ reg = mfi<<4 | pdf;
+ __raw_writel(reg, pllbase + MXC_PLL_DP_HFS_OP);
+ __raw_writel(mfd, pllbase + MXC_PLL_DP_HFS_MFD);
+ __raw_writel(mfn, pllbase + MXC_PLL_DP_HFS_MFN);
+ }
+
+ clk->rate = rate;
+ return 0;
+}
+
+static int _clk_pll_enable(struct clk *clk)
+{
+ u32 reg;
+ u32 pllbase;
+ struct timespec nstimeofday;
+ struct timespec curtime;
+
+ pllbase = _get_pll_base(clk);
+ reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) | MXC_PLL_DP_CTL_UPEN;
+ __raw_writel(reg, pllbase + MXC_PLL_DP_CTL);
+
+ /* Wait for lock */
+ getnstimeofday(&nstimeofday);
+ while (!(__raw_readl(pllbase + MXC_PLL_DP_CTL) & MXC_PLL_DP_CTL_LRF)) {
+ getnstimeofday(&curtime);
+ if (curtime.tv_nsec - nstimeofday.tv_nsec > SPIN_DELAY)
+ panic("pll relock failed\n");
+ }
+ return 0;
+}
+
+static void _clk_pll_disable(struct clk *clk)
+{
+ u32 reg;
+ u32 pllbase;
+
+ pllbase = _get_pll_base(clk);
+ reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) & ~MXC_PLL_DP_CTL_UPEN;
+ __raw_writel(reg, pllbase + MXC_PLL_DP_CTL);
+}
+
+static struct clk pll1_main_clk = {
+ .name = "pll1_main_clk",
+ .parent = &osc_clk,
+ .recalc = _clk_pll_recalc,
+ .enable = _clk_pll_enable,
+ .disable = _clk_pll_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_pll1_sw_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CCSR);
+
+ if (parent == &pll1_main_clk) {
+ reg &= ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+ /* Set the step_clk parent to be lp_apm, to save power. */
+ mux = _get_mux(&lp_apm_clk, &lp_apm_clk, NULL, &pll2_sw_clk,
+ &pll3_sw_clk);
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) |
+ (mux << MXC_CCM_CCSR_STEP_SEL_OFFSET);
+ } else {
+ if (parent == &lp_apm_clk) {
+ reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ reg = __raw_readl(MXC_CCM_CCSR);
+ mux = _get_mux(parent, &lp_apm_clk, NULL, &pll2_sw_clk,
+ &pll3_sw_clk);
+ reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) |
+ (mux << MXC_CCM_CCSR_STEP_SEL_OFFSET);
+ } else {
+ mux = _get_mux(parent, &lp_apm_clk, NULL, &pll2_sw_clk,
+ &pll3_sw_clk);
+ reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) |
+ (mux << MXC_CCM_CCSR_STEP_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CCSR);
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+
+ }
+ }
+ __raw_writel(reg, MXC_CCM_CCSR);
+
+ return 0;
+}
+
+static void _clk_pll1_sw_recalc(struct clk *clk)
+{
+ u32 reg, div;
+ div = 1;
+ reg = __raw_readl(MXC_CCM_CCSR);
+
+ if (clk->parent == &pll2_sw_clk) {
+ div = ((reg & MXC_CCM_CCSR_PLL2_PODF_MASK) >>
+ MXC_CCM_CCSR_PLL2_PODF_OFFSET) + 1;
+ } else if (clk->parent == &pll3_sw_clk) {
+ div = ((reg & MXC_CCM_CCSR_PLL3_PODF_MASK) >>
+ MXC_CCM_CCSR_PLL3_PODF_OFFSET) + 1;
+ }
+ clk->rate = clk->parent->rate / div;
+}
+
+/* pll1 switch clock */
+static struct clk pll1_sw_clk = {
+ .name = "pll1_sw_clk",
+ .parent = &pll1_main_clk,
+ .set_parent = _clk_pll1_sw_set_parent,
+ .recalc = _clk_pll1_sw_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_pll2_sw_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CCSR);
+
+ if (parent == &pll2_sw_clk) {
+ reg &= ~MXC_CCM_CCSR_PLL2_SW_CLK_SEL;
+ } else {
+ reg = (reg & ~MXC_CCM_CCSR_PLL2_SW_CLK_SEL);
+ reg |= MXC_CCM_CCSR_PLL2_SW_CLK_SEL;
+ }
+ __raw_writel(reg, MXC_CCM_CCSR);
+ return 0;
+}
+
+/* same as pll2_main_clk. These two clocks should always be the same */
+static struct clk pll2_sw_clk = {
+ .name = "pll2",
+ .parent = &osc_clk,
+ .recalc = _clk_pll_recalc,
+ .enable = _clk_pll_enable,
+ .disable = _clk_pll_disable,
+ .set_rate = _clk_pll_set_rate,
+ .set_parent = _clk_pll2_sw_set_parent,
+ .flags = RATE_PROPAGATES,
+};
+
+/* same as pll3_main_clk. These two clocks should always be the same */
+static struct clk pll3_sw_clk = {
+ .name = "pll3",
+ .parent = &osc_clk,
+ .set_rate = _clk_pll_set_rate,
+ .recalc = _clk_pll_recalc,
+ .enable = _clk_pll_enable,
+ .disable = _clk_pll_disable,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_lp_apm_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ if (parent == &osc_clk)
+ reg = __raw_readl(MXC_CCM_CCSR) & ~MXC_CCM_CCSR_LP_APM_SEL;
+ else if (parent == &fpm_clk)
+ reg = __raw_readl(MXC_CCM_CCSR) | MXC_CCM_CCSR_LP_APM_SEL;
+ else
+ return -EINVAL;
+
+ __raw_writel(reg, MXC_CCM_CCSR);
+
+ return 0;
+}
+
+static struct clk lp_apm_clk = {
+ .name = "lp_apm",
+ .parent = &osc_clk,
+ .set_parent = _clk_lp_apm_set_parent,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_arm_recalc(struct clk *clk)
+{
+ u32 cacrr, div;
+
+ cacrr = __raw_readl(MXC_CCM_CACRR);
+ div = (cacrr & MXC_CCM_CACRR_ARM_PODF_MASK) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 i;
+ for (i = 0; i < cpu_wp_nr; i++) {
+ if (rate == cpu_wp_tbl[i].cpu_rate)
+ break;
+ }
+ if (i >= cpu_wp_nr)
+ return -EINVAL;
+ cpu_clk_set_wp(i);
+
+ return 0;
+}
+
+static unsigned long _clk_cpu_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 i;
+ u32 wp;
+
+ for (i = 0; i < cpu_wp_nr; i++) {
+ if (rate == cpu_wp_tbl[i].cpu_rate)
+ break;
+ }
+
+ if (i > cpu_wp_nr)
+ wp = 0;
+
+ return cpu_wp_tbl[wp].cpu_rate;
+}
+
+
+static struct clk cpu_clk = {
+ .name = "cpu_clk",
+ .parent = &pll1_sw_clk,
+ .recalc = _clk_arm_recalc,
+ .set_rate = _clk_cpu_set_rate,
+ .round_rate = _clk_cpu_round_rate,
+};
+
+static int _clk_periph_apm_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ struct timespec nstimeofday;
+ struct timespec curtime;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll3_sw_clk, &lp_apm_clk, NULL);
+
+ reg = __raw_readl(MXC_CCM_CBCMR) & ~MXC_CCM_CBCMR_PERIPH_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CBCMR_PERIPH_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCMR);
+
+ getnstimeofday(&nstimeofday);
+ while (__raw_readl(MXC_CCM_CDHIPR) &
+ MXC_CCM_CDHIPR_PERIPH_CLK_SEL_BUSY) {
+ getnstimeofday(&curtime);
+ if (curtime.tv_nsec - nstimeofday.tv_nsec > SPIN_DELAY)
+ panic("pll _clk_periph_apm_set_parent failed\n");
+ }
+ return 0;
+}
+
+static struct clk periph_apm_clk = {
+ .name = "periph_apm_clk",
+ .parent = &pll1_sw_clk,
+ .set_parent = _clk_periph_apm_set_parent,
+ .flags = RATE_PROPAGATES,
+};
+
+/* TODO: Need to sync with GPC to determine if DVFS is in place so that
+ * the DVFS_PODF divider can be applied in CDCR register.
+ */
+static void _clk_main_bus_recalc(struct clk *clk)
+{
+ u32 div;
+
+ div = (__raw_readl(MXC_CCM_CDCR) & 0x3) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static int _clk_main_bus_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+
+ if (parent == &pll2_sw_clk) {
+ reg = __raw_readl(MXC_CCM_CBCDR) &
+ ~MXC_CCM_CBCDR_PERIPH_CLK_SEL;
+ } else if (parent == &periph_apm_clk) {
+ reg = __raw_readl(MXC_CCM_CBCDR) | MXC_CCM_CBCDR_PERIPH_CLK_SEL;
+ } else {
+ return -EINVAL;
+ }
+ __raw_writel(reg, MXC_CCM_CBCDR);
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+
+ return 0;
+}
+
+static struct clk main_bus_clk = {
+ .name = "main_bus_clk",
+ .parent = &pll2_sw_clk,
+ .set_parent = _clk_main_bus_set_parent,
+ .recalc = _clk_main_bus_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_axi_a_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ div = ((reg & MXC_CCM_CBCDR_AXI_A_PODF_MASK) >>
+ MXC_CCM_CBCDR_AXI_A_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static int _clk_axi_a_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+ struct timespec nstimeofday;
+ struct timespec curtime;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ reg &= ~MXC_CCM_CBCDR_AXI_A_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR_AXI_A_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR);
+
+ getnstimeofday(&nstimeofday);
+ while (__raw_readl(MXC_CCM_CDHIPR) & MXC_CCM_CDHIPR_AXI_A_PODF_BUSY) {
+ getnstimeofday(&curtime);
+ if (curtime.tv_nsec - nstimeofday.tv_nsec > SPIN_DELAY)
+ panic("pll _clk_axi_a_set_rate failed\n");
+ }
+ clk->rate = rate;
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+
+ return 0;
+}
+
+static unsigned long _clk_axi_a_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+
+static struct clk axi_a_clk = {
+ .name = "axi_a_clk",
+ .parent = &main_bus_clk,
+ .recalc = _clk_axi_a_recalc,
+ .set_rate = _clk_axi_a_set_rate,
+ .round_rate = _clk_axi_a_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_ddr_hf_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ div = ((reg & MXC_CCM_CBCDR_DDR_PODF_MASK) >>
+ MXC_CCM_CBCDR_DDR_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static unsigned long _clk_ddr_hf_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static int _clk_ddr_hf_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+ struct timespec nstimeofday;
+ struct timespec curtime;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ reg &= ~MXC_CCM_CBCDR_DDR_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR_DDR_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR);
+
+ getnstimeofday(&nstimeofday);
+ while (__raw_readl(MXC_CCM_CDHIPR) & MXC_CCM_CDHIPR_DDR_PODF_BUSY) {
+ getnstimeofday(&curtime);
+ if (curtime.tv_nsec - nstimeofday.tv_nsec > SPIN_DELAY)
+ panic("clk_ddr_hf_set_rate failed\n");
+ }
+ clk->rate = rate;
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+
+ return 0;
+}
+
+static struct clk ddr_hf_clk = {
+ .name = "ddr_hf_clk",
+ .parent = &pll1_sw_clk,
+ .recalc = _clk_ddr_hf_recalc,
+ .round_rate = _clk_ddr_hf_round_rate,
+ .set_rate = _clk_ddr_hf_set_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_axi_b_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ div = ((reg & MXC_CCM_CBCDR_AXI_B_PODF_MASK) >>
+ MXC_CCM_CBCDR_AXI_B_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static int _clk_axi_b_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+ struct timespec nstimeofday;
+ struct timespec curtime;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ reg &= ~MXC_CCM_CBCDR_AXI_B_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR_AXI_B_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR);
+
+ getnstimeofday(&nstimeofday);
+ while (__raw_readl(MXC_CCM_CDHIPR) & MXC_CCM_CDHIPR_AXI_B_PODF_BUSY) {
+ getnstimeofday(&curtime);
+ if (curtime.tv_nsec - nstimeofday.tv_nsec > SPIN_DELAY)
+ panic("_clk_axi_b_set_rate failed\n");
+ }
+
+ clk->rate = rate;
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+
+ return 0;
+}
+
+static unsigned long _clk_axi_b_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+
+static struct clk axi_b_clk = {
+ .name = "axi_b_clk",
+ .parent = &main_bus_clk,
+ .recalc = _clk_axi_b_recalc,
+ .set_rate = _clk_axi_b_set_rate,
+ .round_rate = _clk_axi_b_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_ahb_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >>
+ MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+
+static int _clk_ahb_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+ struct timespec nstimeofday;
+ struct timespec curtime;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ reg &= ~MXC_CCM_CBCDR_AHB_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR_AHB_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR);
+
+ getnstimeofday(&nstimeofday);
+ while (__raw_readl(MXC_CCM_CDHIPR) & MXC_CCM_CDHIPR_AHB_PODF_BUSY) {
+ getnstimeofday(&curtime);
+ if (curtime.tv_nsec - nstimeofday.tv_nsec > SPIN_DELAY)
+ panic("_clk_ahb_set_rate failed\n");
+ }
+ clk->rate = rate;
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+
+ return 0;
+}
+
+static unsigned long _clk_ahb_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+
+static struct clk ahb_clk = {
+ .name = "ahb_clk",
+ .parent = &main_bus_clk,
+ .recalc = _clk_ahb_recalc,
+ .set_rate = _clk_ahb_set_rate,
+ .round_rate = _clk_ahb_round_rate,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_max_enable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_enable(clk);
+
+ /* Handshake with MAX when LPM is entered. */
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg &= ~MXC_CCM_CLPCR_BYPASS_MAX_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+
+ return 0;
+}
+
+
+static void _clk_max_disable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_disable_inwait(clk);
+
+ /* No Handshake with MAX when LPM is entered as its disabled. */
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg |= MXC_CCM_CLPCR_BYPASS_MAX_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+}
+
+
+static struct clk ahb_max_clk = {
+ .name = "max_clk",
+ .parent = &ahb_clk,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG14_OFFSET,
+ .enable = _clk_max_enable,
+ .disable = _clk_max_disable,
+};
+
+static int _clk_emi_slow_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ if (parent == &ahb_clk) {
+ reg |= MXC_CCM_CBCDR_EMI_CLK_SEL;
+ } else if (parent == &main_bus_clk) {
+ reg &= ~MXC_CCM_CBCDR_EMI_CLK_SEL;
+ } else {
+ BUG();
+ }
+ __raw_writel(reg, MXC_CCM_CBCDR);
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+
+ return 0;
+}
+
+static void _clk_emi_slow_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ div = ((reg & MXC_CCM_CBCDR_EMI_PODF_MASK) >>
+ MXC_CCM_CBCDR_EMI_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static int _clk_emi_slow_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+ struct timespec nstimeofday;
+ struct timespec curtime;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ reg &= ~MXC_CCM_CBCDR_EMI_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR_EMI_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR);
+ getnstimeofday(&nstimeofday);
+ while (__raw_readl(MXC_CCM_CDHIPR) & MXC_CCM_CDHIPR_EMI_PODF_BUSY) {
+ getnstimeofday(&curtime);
+ if ((curtime.tv_nsec - nstimeofday.tv_nsec) > SPIN_DELAY)
+ panic("_clk_emi_slow_set_rate failed\n");
+ }
+ clk->rate = rate;
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ return 0;
+}
+
+static unsigned long _clk_emi_slow_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+
+static struct clk emi_slow_clk = {
+ .name = "emi_slow_clk",
+ .parent = &main_bus_clk,
+ .set_parent = _clk_emi_slow_set_parent,
+ .recalc = _clk_emi_slow_recalc,
+ .set_rate = _clk_emi_slow_set_rate,
+ .round_rate = _clk_emi_slow_round_rate,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG8_OFFSET,
+ .disable = _clk_disable_inwait,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk ahbmux1_clk = {
+ .name = "ahbmux1_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG8_OFFSET,
+ .disable = _clk_disable_inwait,
+};
+
+static struct clk ahbmux2_clk = {
+ .name = "ahbmux2_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG9_OFFSET,
+ .disable = _clk_disable_inwait,
+};
+
+
+static struct clk emi_fast_clk = {
+ .name = "emi_fast_clk",
+ .parent = &ddr_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG7_OFFSET,
+ .disable = _clk_disable_inwait,
+};
+
+static struct clk emi_intr_clk = {
+ .name = "emi_intr_clk",
+ .parent = &ahb_clk,
+ .secondary = &ahbmux2_clk,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG9_OFFSET,
+};
+
+static void _clk_ipg_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ div = ((reg & MXC_CCM_CBCDR_IPG_PODF_MASK) >>
+ MXC_CCM_CBCDR_IPG_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static struct clk ipg_clk = {
+ .name = "ipg_clk",
+ .parent = &ahb_clk,
+ .recalc = _clk_ipg_recalc,
+ .flags = RATE_PROPAGATES,
+};
+
+static void _clk_ipg_per_recalc(struct clk *clk)
+{
+ u32 reg, prediv1, prediv2, podf;
+
+ if (clk->parent == &main_bus_clk || clk->parent == &lp_apm_clk) {
+ /* the main_bus_clk is the one before the DVFS engine */
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ prediv1 = ((reg & MXC_CCM_CBCDR_PERCLK_PRED1_MASK) >>
+ MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET) + 1;
+ prediv2 = ((reg & MXC_CCM_CBCDR_PERCLK_PRED2_MASK) >>
+ MXC_CCM_CBCDR_PERCLK_PRED2_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CBCDR_PERCLK_PODF_MASK) >>
+ MXC_CCM_CBCDR_PERCLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (prediv1 * prediv2 * podf);
+ } else if (clk->parent == &ipg_clk) {
+ clk->rate = ipg_clk.rate;
+ } else {
+ BUG();
+ }
+}
+
+static int _clk_ipg_per_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CBCMR);
+ mux = _get_mux(parent, &main_bus_clk, &lp_apm_clk, &ipg_clk, NULL);
+ if (mux == 2) {
+ reg |= MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL;
+ } else {
+ reg &= ~MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL;
+ if (mux == 0)
+ reg &= ~MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL;
+ else
+ reg |= MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL;
+ }
+ __raw_writel(reg, MXC_CCM_CBCMR);
+
+ return 0;
+}
+
+static struct clk ipg_perclk = {
+ .name = "ipg_perclk",
+ .parent = &lp_apm_clk,
+ .recalc = _clk_ipg_per_recalc,
+ .set_parent = _clk_ipg_per_set_parent,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk aips_tz1_clk = {
+ .name = "aips_tz1_clk",
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG12_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable_inwait,
+};
+
+static struct clk aips_tz2_clk = {
+ .name = "aips_tz2_clk",
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG13_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable_inwait,
+};
+
+static struct clk gpc_dvfs_clk = {
+ .name = "gpc_dvfs_clk",
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG12_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static int _clk_sdma_enable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_enable(clk);
+
+ /* Handshake with SDMA when LPM is entered. */
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg &= ~MXC_CCM_CLPCR_BYPASS_SDMA_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+
+ return 0;
+}
+
+static void _clk_sdma_disable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_disable(clk);
+ /* No handshake with SDMA as its not enabled. */
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg |= MXC_CCM_CLPCR_BYPASS_SDMA_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+}
+
+
+static struct clk sdma_clk[] = {
+ {
+ .name = "sdma_ahb_clk",
+ .parent = &ahb_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG15_OFFSET,
+ .enable = _clk_sdma_enable,
+ .disable = _clk_sdma_disable,
+ },
+ {
+ .name = "sdma_ipg_clk",
+ .parent = &ipg_clk,
+#ifdef CONFIG_SDMA_IRAM
+ .secondary = &emi_intr_clk,
+#endif
+ },
+};
+
+static int _clk_ipu_enable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_enable(clk);
+ /* Handshake with IPU when certain clock rates are changed. */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg &= ~MXC_CCM_CCDR_IPU_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ /* Handshake with IPU when LPM is entered as its enabled. */
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg &= ~MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+
+
+ return 0;
+}
+
+static void _clk_ipu_disable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_disable(clk);
+
+ /* No handshake with IPU whe dividers are changed
+ * as its not enabled. */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_IPU_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ /* No handshake with IPU when LPM is entered as its not enabled. */
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg |= MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+}
+
+static int _clk_ipu_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ reg = __raw_readl(MXC_CCM_CBCMR);
+ mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &ahb_clk,
+ &emi_slow_clk);
+ reg = (reg & ~MXC_CCM_CBCMR_IPU_HSP_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CBCMR_IPU_HSP_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CBCMR);
+
+ return 0;
+}
+
+
+static struct clk ipu_clk[] = {
+ {
+ .name = "ipu_clk",
+ .parent = &ahb_clk,
+ .secondary = &ipu_clk[1],
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG5_OFFSET,
+ .enable = _clk_ipu_enable,
+ .disable = _clk_ipu_disable,
+ .set_parent = _clk_ipu_set_parent,
+ .flags = CPU_FREQ_TRIG_UPDATE | AHB_MED_SET_POINT,
+ },
+ {
+ .name = "ipu_sec_clk",
+ .parent = &emi_fast_clk,
+ .secondary = &ahbmux1_clk,
+ }
+};
+
+static int _clk_ipu_di_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CSCMR2);
+ reg &= ~MXC_CCM_CSCMR2_DI_CLK_SEL_MASK(clk->id);
+ if (parent == &pll3_sw_clk)
+ ;
+ else if (parent == &osc_clk)
+ reg |= 1 << MXC_CCM_CSCMR2_DI_CLK_SEL_OFFSET(clk->id);
+ else if (parent == &ckih_clk)
+ reg |= 2 << MXC_CCM_CSCMR2_DI_CLK_SEL_OFFSET(clk->id);
+ else if (parent == &tve_clk)
+ reg |= 3 << MXC_CCM_CSCMR2_DI_CLK_SEL_OFFSET(clk->id);
+ else /* Assume any other clock is external clock pin */
+ reg |= 4 << MXC_CCM_CSCMR2_DI_CLK_SEL_OFFSET(clk->id);
+ __raw_writel(reg, MXC_CCM_CSCMR2);
+
+ return 0;
+}
+
+static void _clk_ipu_di_recalc(struct clk *clk)
+{
+ u32 reg, div, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR2);
+ mux = (reg & MXC_CCM_CSCMR2_DI_CLK_SEL_MASK(clk->id)) >>
+ MXC_CCM_CSCMR2_DI_CLK_SEL_OFFSET(clk->id);
+ if (mux == 0) {
+ reg = __raw_readl(MXC_CCM_CDCDR) &
+ MXC_CCM_CDCDR_DI_CLK_PRED_MASK;
+ div = (reg >> MXC_CCM_CDCDR_DI_CLK_PRED_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+ } else if (mux == 3) {
+ clk->rate = clk->parent->rate / 8;
+ } else {
+ clk->rate = clk->parent->rate;
+ }
+}
+
+static int _clk_ipu_di_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ reg &= ~MXC_CCM_CDCDR_DI_CLK_PRED_MASK;
+ reg |= (div - 1) << MXC_CCM_CDCDR_DI_CLK_PRED_OFFSET;
+ __raw_writel(reg, MXC_CCM_CDCDR);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+static unsigned long _clk_ipu_di_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static struct clk ipu_di_clk[] = {
+ {
+ .name = "ipu_di0_clk",
+ .id = 0,
+ .parent = &pll3_sw_clk,
+ .enable_reg = MXC_CCM_CCGR6,
+ .enable_shift = MXC_CCM_CCGR6_CG5_OFFSET,
+ .recalc = _clk_ipu_di_recalc,
+ .set_parent = _clk_ipu_di_set_parent,
+ .round_rate = _clk_ipu_di_round_rate,
+ .set_rate = _clk_ipu_di_set_rate,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "ipu_di1_clk",
+ .id = 1,
+ .parent = &pll3_sw_clk,
+ .enable_reg = MXC_CCM_CCGR6,
+ .enable_shift = MXC_CCM_CCGR6_CG6_OFFSET,
+ .recalc = _clk_ipu_di_recalc,
+ .set_parent = _clk_ipu_di_set_parent,
+ .round_rate = _clk_ipu_di_round_rate,
+ .set_rate = _clk_ipu_di_set_rate,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static int _clk_csi0_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR2);
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, NULL);
+ reg = (reg & ~MXC_CCM_CSCMR2_CSI_MCLK1_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CSCMR2_CSI_MCLK1_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CSCMR2);
+
+ return 0;
+}
+
+static void _clk_csi0_recalc(struct clk *clk)
+{
+ u32 reg, pred, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR4);
+ pred = ((reg & MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (pred * podf);
+}
+
+static unsigned long _clk_csi0_round_rate(struct clk *clk, unsigned long rate)
+{
+ u32 pre, post;
+ u32 div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ __calc_pre_post_dividers(div, &pre, &post);
+
+ return clk->parent->rate / (pre * post);
+}
+
+static int _clk_csi0_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+ u32 pre, post;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ __calc_pre_post_dividers(div, &pre, &post);
+
+ /* Set CSI clock divider */
+ reg = __raw_readl(MXC_CCM_CSCDR4) &
+ ~(MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PRED_MASK |
+ MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PODF_MASK);
+ reg |= (post - 1) << MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PODF_OFFSET;
+ reg |= (pre - 1) << MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PRED_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCDR4);
+
+ clk->rate = rate;
+ return 0;
+}
+
+static struct clk csi0_clk = {
+ .name = "csi_mclk1",
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_csi0_set_parent,
+ .recalc = _clk_csi0_recalc,
+ .round_rate = _clk_csi0_round_rate,
+ .set_rate = _clk_csi0_set_rate,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR6,
+ .enable_shift = MXC_CCM_CCGR6_CG2_OFFSET,
+ .disable = _clk_disable,
+};
+
+static int _clk_csi1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR2);
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, NULL);
+ reg = (reg & ~MXC_CCM_CSCMR2_CSI_MCLK2_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CSCMR2_CSI_MCLK2_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CSCMR2);
+
+ return 0;
+}
+
+static void _clk_csi1_recalc(struct clk *clk)
+{
+ u32 reg, pred, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR4);
+ pred = ((reg & MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (pred * podf);
+}
+
+static unsigned long _clk_csi1_round_rate(struct clk *clk, unsigned long rate)
+{
+ u32 pre, post;
+ u32 div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ __calc_pre_post_dividers(div, &pre, &post);
+
+ return clk->parent->rate / (pre * post);
+}
+
+static int _clk_csi1_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+ u32 pre, post;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ __calc_pre_post_dividers(div, &pre, &post);
+
+ /* Set CSI clock divider */
+ reg = __raw_readl(MXC_CCM_CSCDR4) &
+ ~(MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PRED_MASK |
+ MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PODF_MASK);
+ reg |= (post - 1) << MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PODF_OFFSET;
+ reg |= (pre - 1) << MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PRED_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCDR4);
+
+ clk->rate = rate;
+ return 0;
+}
+
+static struct clk csi1_clk = {
+ .name = "csi_mclk2",
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_csi1_set_parent,
+ .recalc = _clk_csi1_recalc,
+ .round_rate = _clk_csi1_round_rate,
+ .set_rate = _clk_csi1_set_rate,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR6,
+ .enable_shift = MXC_CCM_CCGR6_CG3_OFFSET,
+ .disable = _clk_disable,
+};
+
+
+static int _clk_hsc_enable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_enable(clk);
+ /* Handshake with IPU when certain clock rates are changed. */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg &= ~MXC_CCM_CCDR_HSC_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg &= ~MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+
+ return 0;
+}
+
+static void _clk_hsc_disable(struct clk *clk)
+{
+ u32 reg;
+
+ _clk_disable(clk);
+ /* No handshake with HSC as its not enabled. */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_HSC_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg |= MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+}
+
+static struct clk mipi_esc_clk = {
+ .name = "mipi_esc_clk",
+ .parent = &pll2_sw_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG5_OFFSET,
+};
+
+static struct clk mipi_hsc2_clk = {
+ .name = "mipi_hsc2_clk",
+ .parent = &pll2_sw_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG4_OFFSET,
+ .secondary = &mipi_esc_clk,
+};
+
+static struct clk mipi_hsc1_clk = {
+ .name = "mipi_hsc1_clk",
+ .parent = &pll2_sw_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG3_OFFSET,
+ .secondary = &mipi_hsc2_clk,
+};
+
+static struct clk mipi_hsp_clk = {
+ .name = "mipi_hsp_clk",
+ .parent = &ipu_clk[0],
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG6_OFFSET,
+ .enable = _clk_hsc_enable,
+ .disable = _clk_hsc_disable,
+ .secondary = &mipi_hsc1_clk,
+};
+
+static int _clk_tve_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+
+ if (parent == &pll3_sw_clk) {
+ reg &= ~(MXC_CCM_CSCMR1_TVE_CLK_SEL);
+ } else if (parent == &osc_clk) {
+ reg |= MXC_CCM_CSCMR1_TVE_CLK_SEL;
+ reg &= MXC_CCM_CSCMR1_TVE_EXT_CLK_SEL;
+ } else if (parent == &ckih_clk) {
+ reg |= MXC_CCM_CSCMR1_TVE_CLK_SEL;
+ reg |= MXC_CCM_CSCMR1_TVE_EXT_CLK_SEL;
+ } else {
+ BUG();
+ }
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+ return 0;
+}
+
+static void _clk_tve_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if ((reg & MXC_CCM_CSCMR1_TVE_CLK_SEL) == 0) {
+ reg = __raw_readl(MXC_CCM_CDCDR) &
+ MXC_CCM_CDCDR_TVE_CLK_PRED_MASK;
+ div = (reg >> MXC_CCM_CDCDR_TVE_CLK_PRED_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+ } else {
+ clk->rate = clk->parent->rate;
+ }
+}
+
+static unsigned long _clk_tve_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (reg & MXC_CCM_CSCMR1_TVE_CLK_SEL)
+ return -EINVAL;
+
+ div = clk->parent->rate / rate;
+ if (div > 8)
+ div = 8;
+ else if (div == 0)
+ div++;
+ return clk->parent->rate / div;
+}
+
+static int _clk_tve_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (reg & MXC_CCM_CSCMR1_TVE_CLK_SEL)
+ return -EINVAL;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+
+ div--;
+ reg = __raw_readl(MXC_CCM_CDCDR) & ~MXC_CCM_CDCDR_TVE_CLK_PRED_MASK;
+ reg |= div << MXC_CCM_CDCDR_TVE_CLK_PRED_OFFSET;
+ __raw_writel(reg, MXC_CCM_CDCDR);
+ clk->rate = rate;
+ return 0;
+}
+
+static struct clk tve_clk = {
+ .name = "tve_clk",
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_tve_set_parent,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG15_OFFSET,
+ .recalc = _clk_tve_recalc,
+ .round_rate = _clk_tve_round_rate,
+ .set_rate = _clk_tve_set_rate,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+};
+
+static struct clk spba_clk = {
+ .name = "spba_clk",
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG0_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static void _clk_uart_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ prediv = ((reg & MXC_CCM_CSCDR1_UART_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CSCDR1_UART_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_uart_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_UART_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_UART_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk uart_main_clk = {
+ .name = "uart_main_clk",
+ .parent = &pll2_sw_clk,
+ .recalc = _clk_uart_recalc,
+ .set_parent = _clk_uart_set_parent,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk uart1_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 0,
+ .parent = &uart_main_clk,
+ .secondary = &uart1_clk[1],
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG4_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+#ifdef UART1_DMA_ENABLE
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+#endif
+ },
+ {
+ .name = "uart_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+#ifdef UART1_DMA_ENABLE
+ .secondary = &aips_tz1_clk,
+#endif
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG3_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static struct clk uart2_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 1,
+ .parent = &uart_main_clk,
+ .secondary = &uart2_clk[1],
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG6_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+#ifdef UART2_DMA_ENABLE
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+#endif
+ },
+ {
+ .name = "uart_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+#ifdef UART2_DMA_ENABLE
+ .secondary = &aips_tz1_clk,
+#endif
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG5_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static struct clk uart3_clk[] = {
+ {
+ .name = "uart_clk",
+ .id = 2,
+ .parent = &uart_main_clk,
+ .secondary = &uart3_clk[1],
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG8_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+#ifdef UART3_DMA_ENABLE
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+#endif
+ },
+ {
+ .name = "uart_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .secondary = &spba_clk,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG7_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static struct clk gpt_clk[] = {
+ {
+ .name = "gpt_clk",
+ .parent = &ipg_perclk,
+ .id = 0,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG9_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ .secondary = &gpt_clk[1],
+ },
+ {
+ .name = "gpt_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG10_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "gpt_32k_clk",
+ .id = 0,
+ .parent = &ckil_clk,
+ },
+};
+
+static struct clk pwm1_clk[] = {
+ {
+ .name = "pwm",
+ .parent = &ipg_perclk,
+ .id = 0,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG6_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ .secondary = &pwm1_clk[1],
+ },
+ {
+ .name = "pwm_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG5_OFFSET,
+ .enable = _clk_enable_inwait, /*Active only when ARM is running. */
+ .disable = _clk_disable,
+ },
+ {
+ .name = "pwm_32k_clk",
+ .id = 0,
+ .parent = &ckil_clk,
+ },
+};
+
+static struct clk pwm2_clk[] = {
+ {
+ .name = "pwm",
+ .parent = &ipg_perclk,
+ .id = 1,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG8_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ .secondary = &pwm2_clk[1],
+ },
+ {
+ .name = "pwm_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG7_OFFSET,
+ .enable = _clk_enable_inwait, /*Active only when ARM is running. */
+ .disable = _clk_disable,
+ },
+ {
+ .name = "pwm_32k_clk",
+ .id = 1,
+ .parent = &ckil_clk,
+ },
+};
+
+static struct clk i2c_clk[] = {
+ {
+ .name = "i2c_clk",
+ .id = 0,
+ .parent = &ipg_perclk,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG9_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "i2c_clk",
+ .id = 1,
+ .parent = &ipg_perclk,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG10_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+};
+
+static void _clk_hsi2c_serial_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR3);
+ prediv = ((reg & MXC_CCM_CSCDR3_HSI2C_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR3_HSI2C_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CSCDR3_HSI2C_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR3_HSI2C_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static struct clk hsi2c_serial_clk = {
+ .name = "hsi2c_serial_clk",
+ .id = 0,
+ .parent = &pll3_sw_clk,
+ .secondary = &spba_clk,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG11_OFFSET,
+ .recalc = _clk_hsi2c_serial_recalc,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static struct clk hsi2c_clk = {
+ .name = "hsi2c_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG12_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+};
+
+static void _clk_cspi_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR2);
+ prediv = ((reg & MXC_CCM_CSCDR2_CSPI_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR2_CSPI_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CSCDR2_CSPI_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR2_CSPI_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_cspi_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_CSPI_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_CSPI_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk cspi_main_clk = {
+ .name = "cspi_main_clk",
+ .parent = &pll3_sw_clk,
+ .recalc = _clk_cspi_recalc,
+ .set_parent = _clk_cspi_set_parent,
+ .flags = RATE_PROPAGATES,
+};
+
+static struct clk cspi1_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 0,
+ .parent = &cspi_main_clk,
+ .secondary = &cspi1_clk[1],
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG10_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "cspi_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .secondary = &spba_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG9_OFFSET,
+ .enable = _clk_enable_inwait, /*Active only when ARM is running. */
+ .disable = _clk_disable,
+ },
+};
+
+static struct clk cspi2_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 1,
+ .parent = &cspi_main_clk,
+ .secondary = &cspi2_clk[1],
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG12_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "cspi_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .secondary = &aips_tz2_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG11_OFFSET,
+ .enable = _clk_enable_inwait, /*Active only when ARM is running. */
+ .disable = _clk_disable,
+ },
+};
+
+static struct clk cspi3_clk[] = {
+ {
+ .name = "cspi_clk",
+ .id = 2,
+ .parent = &cspi_main_clk,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG13_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ .secondary = &cspi3_clk[1],
+ },
+ {
+ .name = "cspi_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .secondary = &aips_tz2_clk,
+ },
+};
+
+static int _clk_ssi_lp_apm_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &ckih_clk, &lp_apm_clk, &ckih2_clk, NULL);
+ reg = __raw_readl(MXC_CCM_CSCMR1) &
+ ~MXC_CCM_CSCMR1_SSI_APM_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_SSI_APM_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk ssi_lp_apm_clk = {
+ .name = "ssi_lp_apm_clk",
+ .parent = &ckih_clk,
+ .set_parent = _clk_ssi_lp_apm_set_parent,
+};
+
+static void _clk_ssi1_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CS1CDR);
+ prediv = ((reg & MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK) >>
+ MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK) >>
+ MXC_CCM_CS1CDR_SSI1_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+static int _clk_ssi1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk,
+ &pll3_sw_clk, &ssi_lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_SSI1_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_SSI1_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk ssi1_clk[] = {
+ {
+ .name = "ssi_clk",
+ .id = 0,
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_ssi1_set_parent,
+ .secondary = &ssi1_clk[1],
+ .recalc = _clk_ssi1_recalc,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG9_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "ssi_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .secondary = &ssi1_clk[2],
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG8_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "ssi_dep_clk",
+ .id = 0,
+ .parent = &aips_tz2_clk,
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+ .secondary = &emi_intr_clk,
+#else
+ .secondary = &emi_fast_clk,
+#endif
+ },
+};
+
+static void _clk_ssi2_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CS2CDR);
+ prediv = ((reg & MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK) >>
+ MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK) >>
+ MXC_CCM_CS2CDR_SSI2_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_ssi2_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk,
+ &pll3_sw_clk, &ssi_lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_SSI2_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_SSI2_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk ssi2_clk[] = {
+ {
+ .name = "ssi_clk",
+ .id = 1,
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_ssi2_set_parent,
+ .secondary = &ssi2_clk[1],
+ .recalc = _clk_ssi2_recalc,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG11_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "ssi_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .secondary = &ssi2_clk[2],
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG10_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "ssi_dep_clk",
+ .id = 1,
+ .parent = &spba_clk,
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+ .secondary = &emi_intr_clk,
+#else
+ .secondary = &emi_fast_clk,
+#endif
+ },
+};
+
+static void _clk_ssi_ext1_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ clk->rate = clk->parent->rate;
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if ((reg & MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL) == 0) {
+ reg = __raw_readl(MXC_CCM_CS1CDR);
+ prediv = ((reg & MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_MASK) >>
+ MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_MASK) >>
+ MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (prediv * podf);
+ }
+}
+
+static int _clk_ssi_ext1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (parent == &ssi1_clk[0]) {
+ reg |= MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL;
+ } else {
+ reg &= ~MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL;
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &ssi_lp_apm_clk);
+ reg = (reg & ~MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_OFFSET);
+ }
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk ssi_ext1_clk = {
+ .name = "ssi_ext1_clk",
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_ssi_ext1_set_parent,
+ .recalc = _clk_ssi_ext1_recalc,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG14_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static void _clk_ssi_ext2_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ clk->rate = clk->parent->rate;
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if ((reg & MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL) == 0) {
+ reg = __raw_readl(MXC_CCM_CS2CDR);
+ prediv = ((reg & MXC_CCM_CS2CDR_SSI_EXT2_CLK_PRED_MASK) >>
+ MXC_CCM_CS2CDR_SSI_EXT2_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CS2CDR_SSI_EXT2_CLK_PODF_MASK) >>
+ MXC_CCM_CS2CDR_SSI_EXT2_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (prediv * podf);
+ }
+}
+
+static int _clk_ssi_ext2_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (parent == &ssi2_clk[0]) {
+ reg |= MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL;
+ } else {
+ reg &= ~MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL;
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &ssi_lp_apm_clk);
+ reg = (reg & ~MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_OFFSET);
+ }
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk ssi_ext2_clk = {
+ .name = "ssi_ext2_clk",
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_ssi_ext2_set_parent,
+ .recalc = _clk_ssi_ext2_recalc,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG15_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+};
+
+static struct clk iim_clk = {
+ .name = "iim_clk",
+ .parent = &ipg_clk,
+ .secondary = &aips_tz2_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG15_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk tmax1_clk = {
+ .name = "tmax1_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG0_OFFSET,
+ .disable = _clk_disable,
+ };
+
+static struct clk tmax2_clk = {
+ .name = "tmax2_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG1_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk tmax3_clk = {
+ .name = "tmax3_clk",
+ .id = 0,
+ .parent = &ahb_clk,
+ .secondary = &ahb_max_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG2_OFFSET,
+ .disable = _clk_disable,
+};
+
+static void _clk_usboh3_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ prediv = ((reg & MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET) + 1;
+ if (prediv == 1)
+ BUG();
+ podf = ((reg & MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_usboh3_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_USBOH3_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_USBOH3_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk usboh3_clk[] = {
+ {
+ .name = "usboh3_clk",
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_usboh3_set_parent,
+ .recalc = _clk_usboh3_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG14_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &usboh3_clk[1],
+ .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "usb_sec_clk",
+ .parent = &tmax2_clk,
+ .secondary = &emi_fast_clk,
+ },
+};
+static struct clk usb_ahb_clk = {
+ .name = "usb_ahb_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG13_OFFSET,
+ .disable = _clk_disable,
+};
+
+static void _clk_usb_phy_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ if (clk->parent == &pll3_sw_clk) {
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ prediv = ((reg & MXC_CCM_CDCDR_USB_PHY_PRED_MASK) >>
+ MXC_CCM_CDCDR_USB_PHY_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CDCDR_USB_PHY_PODF_MASK) >>
+ MXC_CCM_CDCDR_USB_PHY_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+ } else
+ clk->rate = clk->parent->rate;
+}
+
+static int _clk_usb_phy_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (parent == &osc_clk)
+ reg &= ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL;
+ else if (parent == &pll3_sw_clk)
+ reg |= MXC_CCM_CSCMR1_USB_PHY_CLK_SEL;
+ else
+ BUG();
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+ return 0;
+}
+
+static struct clk usb_phy_clk = {
+ .name = "usb_phy_clk",
+ .parent = &pll3_sw_clk,
+ .secondary = &tmax3_clk,
+ .set_parent = _clk_usb_phy_set_parent,
+ .recalc = _clk_usb_phy_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG0_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk esdhc_dep_clks = {
+ .name = "sd_dep_clk",
+ .parent = &spba_clk,
+ .secondary = &emi_fast_clk,
+};
+
+
+static void _clk_esdhc1_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ prediv = ((reg & MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_esdhc1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) &
+ ~MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk esdhc1_clk[] = {
+ {
+ .name = "esdhc_clk",
+ .id = 0,
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_esdhc1_set_parent,
+ .recalc = _clk_esdhc1_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG1_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc1_clk[1],
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "esdhc_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .secondary = &esdhc1_clk[2],
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG0_OFFSET,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "esdhc_sec_clk",
+ .id = 0,
+ .parent = &tmax3_clk,
+ .secondary = &esdhc_dep_clks,
+ },
+
+};
+
+static void _clk_esdhc2_recalc(struct clk *clk)
+{
+ u32 reg, prediv, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ prediv = ((reg & MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_OFFSET) + 1;
+
+ clk->rate = clk->parent->rate / (prediv * podf);
+}
+
+static int _clk_esdhc2_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CSCMR1) &
+ ~MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk esdhc2_clk[] = {
+ {
+ .name = "esdhc_clk",
+ .id = 1,
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_esdhc2_set_parent,
+ .recalc = _clk_esdhc2_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG3_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc2_clk[1],
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "esdhc_ipg_clk",
+ .id = 1,
+ .parent = &ipg_clk,
+ .secondary = &esdhc2_clk[2],
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG2_OFFSET,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "esdhc_sec_clk",
+ .id = 0,
+ .parent = &tmax2_clk,
+ .secondary = &esdhc_dep_clks,
+ },
+};
+
+static int _clk_esdhc3_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (parent == &esdhc1_clk[0])
+ reg &= ~MXC_CCM_CSCMR1_ESDHC3_CLK_SEL;
+ else if (parent == &esdhc2_clk[0])
+ reg |= MXC_CCM_CSCMR1_ESDHC3_CLK_SEL;
+ else
+ BUG();
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk esdhc3_clk[] = {
+ {
+ .name = "esdhc_clk",
+ .id = 2,
+ .parent = &esdhc1_clk[0],
+ .set_parent = _clk_esdhc3_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG5_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc3_clk[1],
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "esdhc_ipg_clk",
+ .id = 2,
+ .parent = &ipg_clk,
+ .secondary = &esdhc3_clk[2],
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG4_OFFSET,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "esdhc_sec_clk",
+ .id = 0,
+ .parent = &ahb_max_clk,
+ .secondary = &esdhc_dep_clks,
+ },
+};
+
+
+static int _clk_esdhc4_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg;
+
+ reg = __raw_readl(MXC_CCM_CSCMR1);
+ if (parent == &esdhc1_clk[0])
+ reg &= ~MXC_CCM_CSCMR1_ESDHC4_CLK_SEL;
+ else if (parent == &esdhc2_clk[0])
+ reg |= MXC_CCM_CSCMR1_ESDHC4_CLK_SEL;
+ else
+ BUG();
+
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk esdhc4_clk[] = {
+ {
+ .name = "esdhc_clk",
+ .id = 3,
+ .parent = &esdhc1_clk[0],
+ .set_parent = _clk_esdhc4_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG7_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &esdhc3_clk[1],
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "esdhc_ipg_clk",
+ .id = 3,
+ .parent = &ipg_clk,
+ .secondary = &esdhc3_clk[2],
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR3,
+ .enable_shift = MXC_CCM_CCGR3_CG6_OFFSET,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "esdhc_sec_clk",
+ .id = 0,
+ .parent = &tmax3_clk,
+ .secondary = &esdhc_dep_clks,
+ },
+};
+
+static int _clk_sim_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, NULL);
+ reg = __raw_readl(MXC_CCM_CSCMR2) & ~MXC_CCM_CSCMR2_SIM_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR2_SIM_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR2);
+
+ return 0;
+}
+
+static void _clk_sim_recalc(struct clk *clk)
+{
+ u32 reg, pred, podf;
+
+ reg = __raw_readl(MXC_CCM_CSCDR2);
+ pred = ((reg & MXC_CCM_CSCDR2_SIM_CLK_PRED_MASK) >>
+ MXC_CCM_CSCDR2_SIM_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CSCDR2_SIM_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR2_SIM_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (pred * podf);
+}
+
+static unsigned long _clk_sim_round_rate(struct clk *clk, unsigned long rate)
+{
+ u32 pre, post;
+ u32 div = clk->parent->rate / rate;
+ if (clk->parent->rate % rate)
+ div++;
+
+ __calc_pre_post_dividers(div, &pre, &post);
+
+ return clk->parent->rate / (pre * post);
+}
+
+static int _clk_sim_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg;
+ u32 div;
+ u32 pre, post;
+
+ div = clk->parent->rate / rate;
+
+ if ((clk->parent->rate / div) != rate)
+ return -EINVAL;
+
+ __calc_pre_post_dividers(div, &pre, &post);
+
+ /* Set SIM clock divider */
+ reg = __raw_readl(MXC_CCM_CSCDR2) &
+ ~(MXC_CCM_CSCDR2_SIM_CLK_PRED_MASK |
+ MXC_CCM_CSCDR2_SIM_CLK_PODF_MASK);
+ reg |= (post - 1) << MXC_CCM_CSCDR2_SIM_CLK_PODF_OFFSET;
+ reg |= (pre - 1) << MXC_CCM_CSCDR2_SIM_CLK_PRED_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCDR2);
+
+ clk->rate = rate;
+ return 0;
+
+}
+
+static struct clk sim_clk[] = {
+ {
+ .name = "sim_clk",
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_sim_set_parent,
+ .secondary = &sim_clk[1],
+ .recalc = _clk_sim_recalc,
+ .round_rate = _clk_sim_round_rate,
+ .set_rate = _clk_sim_set_rate,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG2_OFFSET,
+ .disable = _clk_disable,
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "sim_ipg_clk",
+ .parent = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG1_OFFSET,
+ .disable = _clk_disable,
+ },
+};
+
+static void _clk_nfc_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ div = ((reg & MXC_CCM_CBCDR_NFC_PODF_MASK) >>
+ MXC_CCM_CBCDR_NFC_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / div;
+}
+
+static unsigned long _clk_nfc_round_rate(struct clk *clk,
+ unsigned long rate)
+{
+ u32 div;
+
+ /*
+ * Compute the divider we'd have to use to reach the target rate.
+ */
+
+ div = clk->parent->rate / rate;
+
+ /*
+ * If there's a remainder after the division, then we have to increment
+ * the divider. There are two reasons for this:
+ *
+ * 1) The frequency we round to must be LESS THAN OR EQUAL to the
+ * target. We aren't allowed to round to a frequency that is higher
+ * than the target.
+ *
+ * 2) This also catches the case where target rate is less than the
+ * parent rate, which implies a divider of zero. We can't allow a
+ * divider of zero.
+ */
+
+ if (clk->parent->rate % rate)
+ div++;
+
+ /*
+ * The divider for this clock is 3 bits wide, so we can't possibly
+ * divide the parent by more than eight.
+ */
+
+ if (div > 8)
+ return -EINVAL;
+
+ return clk->parent->rate / div;
+
+}
+
+static int _clk_nfc_set_rate(struct clk *clk, unsigned long rate)
+{
+ u32 reg, div;
+ struct timespec nstimeofday;
+ struct timespec curtime;
+
+ div = clk->parent->rate / rate;
+ if (div == 0)
+ div++;
+ if (((clk->parent->rate / div) != rate) || (div > 8))
+ return -EINVAL;
+
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.enable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.enable(&emi_slow_clk);
+
+
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ reg &= ~MXC_CCM_CBCDR_NFC_PODF_MASK;
+ reg |= (div - 1) << MXC_CCM_CBCDR_NFC_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CBCDR);
+ getnstimeofday(&nstimeofday);
+ while (__raw_readl(MXC_CCM_CDHIPR) &
+ MXC_CCM_CDHIPR_NFC_IPG_INT_MEM_PODF_BUSY){
+ getnstimeofday(&curtime);
+ if ((curtime.tv_nsec - nstimeofday.tv_nsec) > SPIN_DELAY)
+ panic("_clk_nfc_set_rate failed\n");
+ }
+ clk->rate = rate;
+ if (emi_fast_clk.usecount == 0)
+ emi_fast_clk.disable(&emi_fast_clk);
+ if (emi_slow_clk.usecount == 0)
+ emi_slow_clk.disable(&emi_slow_clk);
+
+ return 0;
+}
+
+static struct clk emi_enfc_clk = {
+ .name = "nfc_clk",
+ .parent = &emi_slow_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG10_OFFSET,
+ .disable = _clk_disable_inwait,
+ .recalc = _clk_nfc_recalc,
+ .round_rate = _clk_nfc_round_rate,
+ .set_rate = _clk_nfc_set_rate,
+};
+
+static int _clk_spdif_xtal_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ mux = _get_mux(parent, &osc_clk, &ckih_clk, &ckih2_clk, NULL);
+ reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_SPDIF_CLK_SEL_MASK;
+ reg |= mux << MXC_CCM_CSCMR1_SPDIF_CLK_SEL_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCMR1);
+
+ return 0;
+}
+
+static struct clk spdif_xtal_clk = {
+ .name = "spdif_xtal_clk",
+ .parent = &osc_clk,
+ .set_parent = _clk_spdif_xtal_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG15_OFFSET,
+ .disable = _clk_disable,
+};
+
+static int _clk_spdif0_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR2);
+ reg |= MXC_CCM_CSCMR2_SPDIF0_COM;
+ if (parent != &ssi1_clk[0]) {
+ reg &= ~MXC_CCM_CSCMR2_SPDIF0_COM;
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &spdif_xtal_clk);
+ reg = (reg & ~MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_OFFSET);
+ }
+ __raw_writel(reg, MXC_CCM_CSCMR2);
+
+ return 0;
+}
+
+static void _clk_spdif0_recalc(struct clk *clk)
+{
+ u32 reg, pred, podf;
+
+ if (clk->parent == &ssi1_clk[0]) {
+ clk->rate = clk->parent->rate;
+ } else {
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ pred = ((reg & MXC_CCM_CDCDR_SPDIF0_CLK_PRED_MASK) >>
+ MXC_CCM_CDCDR_SPDIF0_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK) >>
+ MXC_CCM_CDCDR_SPDIF0_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (pred * podf);
+ }
+}
+
+static struct clk spdif0_clk[] = {
+ {
+ .name = "spdif_clk",
+ .id = 0,
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_spdif0_set_parent,
+ .recalc = _clk_spdif0_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG13_OFFSET,
+ .disable = _clk_disable,
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "spdif_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .secondary = &spba_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG15_OFFSET,
+ .disable = _clk_disable,
+ },
+};
+
+static int _clk_spdif1_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CSCMR2);
+ reg |= MXC_CCM_CSCMR2_SPDIF1_COM;
+ if (parent != &ssi2_clk[0]) {
+ reg &= ~MXC_CCM_CSCMR2_SPDIF1_COM;
+ mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk,
+ &spdif_xtal_clk);
+ reg = (reg & ~MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_OFFSET);
+ }
+ __raw_writel(reg, MXC_CCM_CSCMR2);
+
+ return 0;
+}
+
+static void _clk_spdif1_recalc(struct clk *clk)
+{
+ u32 reg, pred, podf;
+
+ if (clk->parent == &ssi2_clk[0]) {
+ clk->rate = clk->parent->rate;
+ } else {
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ pred = ((reg & MXC_CCM_CDCDR_SPDIF1_CLK_PRED_MASK) >>
+ MXC_CCM_CDCDR_SPDIF1_CLK_PRED_OFFSET) + 1;
+ podf = ((reg & MXC_CCM_CDCDR_SPDIF1_CLK_PODF_MASK) >>
+ MXC_CCM_CDCDR_SPDIF1_CLK_PODF_OFFSET) + 1;
+ clk->rate = clk->parent->rate / (pred * podf);
+ }
+}
+
+static struct clk spdif1_clk[] = {
+ {
+ .name = "spdif_clk",
+ .id = 1,
+ .parent = &pll3_sw_clk,
+ .set_parent = _clk_spdif1_set_parent,
+ .recalc = _clk_spdif1_recalc,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG14_OFFSET,
+ .disable = _clk_disable,
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "spdif_ipg_clk",
+ .id = 0,
+ .parent = &ipg_clk,
+ .secondary = &spba_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG15_OFFSET,
+ .disable = _clk_disable,
+ },
+};
+
+static int _clk_ddr_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, reg2, mux;
+ struct timespec nstimeofday;
+ struct timespec curtime;
+
+ reg = __raw_readl(MXC_CCM_CBCMR);
+ reg2 = __raw_readl(MXC_CCM_CBCDR);
+ mux = _get_mux_ddr(parent, &axi_a_clk, &axi_b_clk, &emi_slow_clk, &ahb_clk, &ddr_hf_clk);
+ if (mux < 4) {
+ reg = (reg & ~MXC_CCM_CBCMR_DDR_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CBCMR_DDR_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CBCMR);
+ reg2 = (reg2 & ~MXC_CCM_CBCDR_DDR_HF_SEL);
+ } else {
+ reg2 = (reg2 & ~MXC_CCM_CBCDR_DDR_HF_SEL) |
+ (MXC_CCM_CBCDR_DDR_HF_SEL);
+ }
+ __raw_writel(reg2, MXC_CCM_CBCDR);
+ getnstimeofday(&nstimeofday);
+ while (__raw_readl(MXC_CCM_CDHIPR) &
+ MXC_CCM_CDHIPR_DDR_HF_CLK_SEL_BUSY){
+ getnstimeofday(&curtime);
+ if ((curtime.tv_nsec - nstimeofday.tv_nsec) > SPIN_DELAY)
+ panic("_clk_ddr_set_parent failed\n");
+ }
+ return 0;
+}
+
+static struct clk ddr_clk = {
+ .name = "ddr_clk",
+ .parent = &ddr_hf_clk,
+ .set_parent = _clk_ddr_set_parent,
+ .flags = RATE_PROPAGATES,
+};
+
+static int _clk_arm_axi_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ reg = __raw_readl(MXC_CCM_CBCMR);
+ mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &emi_slow_clk, &ahb_clk);
+ reg = (reg & ~MXC_CCM_CBCMR_ARM_AXI_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CBCMR_ARM_AXI_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CBCMR);
+
+ return 0;
+}
+
+static struct clk arm_axi_clk = {
+ .name = "arm_axi_clk",
+ .parent = &axi_a_clk,
+ .set_parent = _clk_arm_axi_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR0,
+ .enable_shift = MXC_CCM_CCGR0_CG1_OFFSET,
+ .disable = _clk_disable,
+};
+
+static int _clk_vpu_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ reg = __raw_readl(MXC_CCM_CBCMR);
+ mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &emi_slow_clk, &ahb_clk);
+ reg = (reg & ~MXC_CCM_CBCMR_VPU_AXI_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CBCMR_VPU_AXI_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CBCMR);
+
+ return 0;
+}
+
+static int _clk_vpu_enable(struct clk *clk)
+{
+ /* Set VPU's parent to be axi_a or ahb when its enabled. */
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) < 0) {
+ clk_set_parent(&vpu_clk[0], &ahb_clk);
+ clk_set_parent(&vpu_clk[1], &ahb_clk);
+ } else {
+ clk_set_parent(&vpu_clk[0], &axi_a_clk);
+ clk_set_parent(&vpu_clk[1], &axi_a_clk);
+ }
+
+ _clk_enable(clk);
+ return 0;
+
+}
+
+static void _clk_vpu_disable(struct clk *clk)
+{
+ _clk_disable(clk);
+
+ /* Set VPU's parent to be axi_b when its disabled. */
+ clk_set_parent(&vpu_clk[0], &axi_b_clk);
+ clk_set_parent(&vpu_clk[1], &axi_b_clk);
+}
+
+static struct clk vpu_clk[] = {
+ {
+ .name = "vpu_clk",
+ .set_parent = _clk_vpu_set_parent,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG4_OFFSET,
+ .disable = _clk_disable,
+ .secondary = &vpu_clk[1],
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "vpu_core_clk",
+ .set_parent = _clk_vpu_set_parent,
+ .enable = _clk_vpu_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG3_OFFSET,
+ .disable = _clk_vpu_disable,
+ .secondary = &vpu_clk[2],
+ },
+ {
+ .name = "vpu_emi_clk",
+ .parent = &emi_fast_clk,
+#ifdef CONFIG_MXC_VPU_IRAM
+ .secondary = &emi_intr_clk,
+#endif
+ }
+};
+
+static int _clk_lpsr_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ mux = _get_mux(parent, &ckil_clk, &fpm_clk, &fpm_div2_clk, NULL);
+ reg = (reg & ~MXC_CCM_CLPCR_LPSR_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CLPCR_LPSR_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CLPCR);
+
+ return 0;
+}
+
+static struct clk lpsr_clk = {
+ .name = "lpsr_clk",
+ .parent = &ckil_clk,
+ .set_parent = _clk_lpsr_set_parent,
+};
+
+static void _clk_pgc_recalc(struct clk *clk)
+{
+ u32 reg, div;
+
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ div = (reg & MXC_CCM_CSCDR1_PGC_CLK_PODF_MASK) >>
+ MXC_CCM_CSCDR1_PGC_CLK_PODF_OFFSET;
+ div = 1 >> div;
+ clk->rate = clk->parent->rate / div;
+}
+
+static struct clk pgc_clk = {
+ .name = "pgc_clk",
+ .parent = &ipg_clk,
+ .recalc = _clk_pgc_recalc,
+};
+
+/*usb OTG clock */
+
+static struct clk usb_clk = {
+ .name = "usb_clk",
+ .rate = 60000000,
+};
+
+static struct clk usb_utmi_clk = {
+ .name = "usb_utmi_clk",
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CSCMR1,
+ .enable_shift = MXC_CCM_CSCMR1_USB_PHY_CLK_SEL_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk rtc_clk = {
+ .name = "rtc_clk",
+ .parent = &ckil_clk,
+ .secondary = &ipg_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG14_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk ata_clk = {
+ .name = "ata_clk",
+ .parent = &ipg_clk,
+ .secondary = &spba_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG0_OFFSET,
+ .disable = _clk_disable,
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+};
+
+static struct clk owire_clk = {
+ .name = "owire_clk",
+ .parent = &ipg_perclk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG11_OFFSET,
+ .disable = _clk_disable,
+};
+
+
+static struct clk fec_clk[] = {
+ {
+ .name = "fec_clk",
+ .parent = &ipg_clk,
+ .secondary = &fec_clk[1],
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR2,
+ .enable_shift = MXC_CCM_CCGR2_CG12_OFFSET,
+ .disable = _clk_disable,
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+ },
+ {
+ .name = "fec_sec1_clk",
+ .parent = &tmax2_clk,
+ .secondary = &fec_clk[2],
+ },
+ {
+ .name = "fec_sec2_clk",
+ .parent = &aips_tz2_clk,
+ .secondary = &emi_fast_clk,
+ },
+};
+
+static struct clk sahara_clk[] = {
+ {
+ .name = "sahara_clk",
+ .parent = &ahb_clk,
+ .secondary = &sahara_clk[1],
+ .enable_reg = MXC_CCM_CCGR4,
+ .enable_shift = MXC_CCM_CCGR4_CG7_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "sahara_sec_clk",
+ .parent = &tmax1_clk,
+ .secondary = &emi_fast_clk,
+ }
+};
+
+static struct clk scc_clk[] = {
+ {
+ .name = "scc_clk",
+ .parent = &ahb_clk,
+ .secondary = &scc_clk[1],
+ .enable_reg = MXC_CCM_CCGR1,
+ .enable_shift = MXC_CCM_CCGR1_CG15_OFFSET,
+ .enable = _clk_enable,
+ .disable = _clk_disable,
+ },
+ {
+ .name = "scc_sec_clk",
+ .parent = &tmax1_clk,
+ .secondary = &emi_fast_clk,
+ }
+};
+
+
+static int _clk_gpu3d_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CBCMR);
+ mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &emi_slow_clk, &ahb_clk);
+ reg = (reg & ~MXC_CCM_CBCMR_GPU_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CBCMR_GPU_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CBCMR);
+
+ return 0;
+}
+
+static int _clk_gpu3d_enable(struct clk *clk)
+{
+ /* Set gPU's parent to be axi_a or ahb when its enabled. */
+ clk_set_parent(&gpu3d_clk, &axi_a_clk);
+ _clk_enable(clk);
+ return 0;
+}
+
+static void _clk_gpu3d_disable(struct clk *clk)
+{
+ _clk_disable(clk);
+
+ /* Set GPU's parent to be axi_b when its disabled. */
+ clk_set_parent(&gpu3d_clk, &axi_b_clk);
+}
+
+
+static struct clk gpu3d_clk = {
+ .name = "gpu3d_clk",
+ .parent = &axi_a_clk,
+ .set_parent = _clk_gpu3d_set_parent,
+ .enable = _clk_gpu3d_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG1_OFFSET,
+ .disable = _clk_gpu3d_disable,
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+};
+
+static struct clk garb_clk = {
+ .name = "garb_clk",
+ .parent = &axi_a_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR5,
+ .enable_shift = MXC_CCM_CCGR5_CG2_OFFSET,
+ .disable = _clk_disable,
+};
+
+static struct clk emi_garb_clk = {
+ .name = "emi_garb_clk",
+ .parent = &axi_a_clk,
+ .enable = _clk_enable,
+ .enable_reg = MXC_CCM_CCGR6,
+ .enable_shift = MXC_CCM_CCGR6_CG4_OFFSET,
+ .disable = _clk_disable,
+};
+
+static int _clk_gpu2d_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 reg, mux;
+
+ reg = __raw_readl(MXC_CCM_CBCMR);
+ mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &emi_slow_clk, &ahb_clk);
+ reg = (reg & ~MXC_CCM_CBCMR_GPU2D_CLK_SEL_MASK) |
+ (mux << MXC_CCM_CBCMR_GPU2D_CLK_SEL_OFFSET);
+ __raw_writel(reg, MXC_CCM_CBCMR);
+
+ return 0;
+}
+
+static int _clk_gpu2d_enable(struct clk *clk)
+{
+ /* Set GPU2D's parent to be axi_a or ahb when its enabled. */
+ clk_set_parent(&gpu2d_clk, &axi_a_clk);
+ _clk_enable(clk);
+ return 0;
+}
+
+static void _clk_gpu2d_disable(struct clk *clk)
+{
+ _clk_disable(clk);
+
+ /* Set GPU2d's parent to be axi_b when its disabled. */
+ clk_set_parent(&gpu2d_clk, &axi_b_clk);
+}
+
+static struct clk gpu2d_clk = {
+ .name = "gpu2d_clk",
+ .parent = &axi_a_clk,
+ .set_parent = _clk_gpu2d_set_parent,
+ .enable = _clk_gpu2d_enable,
+ .enable_reg = MXC_CCM_CCGR6,
+ .enable_shift = MXC_CCM_CCGR6_CG7_OFFSET,
+ .disable = _clk_gpu2d_disable,
+ .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
+};
+
+static struct clk *mxc_clks[] = {
+ &osc_clk,
+ &ckih_clk,
+ &ckih2_clk,
+ &ckil_clk,
+ &fpm_clk,
+ &fpm_div2_clk,
+ &pll1_main_clk,
+ &pll1_sw_clk,
+ &pll2_sw_clk,
+ &pll3_sw_clk,
+ &gpc_dvfs_clk,
+ &lp_apm_clk,
+ &cpu_clk,
+ &periph_apm_clk,
+ &main_bus_clk,
+ &axi_a_clk,
+ &axi_b_clk,
+ &ahb_clk,
+ &ahb_max_clk,
+ &ipg_clk,
+ &ipg_perclk,
+ &ahbmux1_clk,
+ &ahbmux2_clk,
+ &aips_tz1_clk,
+ &aips_tz2_clk,
+ &sdma_clk[0],
+ &sdma_clk[1],
+ &ipu_clk[0],
+ &ipu_clk[1],
+ &ipu_di_clk[0],
+ &ipu_di_clk[1],
+ &tve_clk,
+ &csi0_clk,
+ &csi1_clk,
+ &uart_main_clk,
+ &uart1_clk[0],
+ &uart1_clk[1],
+ &uart2_clk[0],
+ &uart2_clk[1],
+ &uart3_clk[0],
+ &uart3_clk[1],
+ &spba_clk,
+ &i2c_clk[0],
+ &i2c_clk[1],
+ &hsi2c_clk,
+ &hsi2c_serial_clk,
+ &gpt_clk[0],
+ &gpt_clk[1],
+ &gpt_clk[2],
+ &pwm1_clk[0],
+ &pwm1_clk[1],
+ &pwm1_clk[2],
+ &pwm2_clk[0],
+ &pwm2_clk[1],
+ &pwm2_clk[2],
+ &cspi_main_clk,
+ &cspi1_clk[0],
+ &cspi1_clk[1],
+ &cspi2_clk[0],
+ &cspi2_clk[1],
+ &cspi3_clk[0],
+ &cspi3_clk[1],
+ &ssi_lp_apm_clk,
+ &ssi1_clk[0],
+ &ssi1_clk[1],
+ &ssi1_clk[2],
+ &ssi2_clk[0],
+ &ssi2_clk[1],
+ &ssi2_clk[2],
+ &ssi_ext1_clk,
+ &ssi_ext2_clk,
+ &iim_clk,
+ &tmax1_clk,
+ &tmax2_clk,
+ &tmax3_clk,
+ &usboh3_clk[0],
+ &usboh3_clk[1],
+ &usb_ahb_clk,
+ &usb_phy_clk,
+ &usb_utmi_clk,
+ &usb_clk,
+ &esdhc1_clk[0],
+ &esdhc1_clk[1],
+ &esdhc2_clk[0],
+ &esdhc2_clk[1],
+ &esdhc3_clk[0],
+ &esdhc3_clk[1],
+ &esdhc4_clk[0],
+ &esdhc4_clk[1],
+ &esdhc_dep_clks,
+ &sim_clk[0],
+ &sim_clk[1],
+ &emi_slow_clk,
+ &ddr_clk,
+ &emi_enfc_clk,
+ &emi_fast_clk,
+ &emi_intr_clk,
+ &spdif_xtal_clk,
+ &spdif0_clk[0],
+ &spdif0_clk[1],
+ &spdif1_clk[0],
+ &spdif1_clk[1],
+ &arm_axi_clk,
+ &vpu_clk[0],
+ &vpu_clk[1],
+ &vpu_clk[2],
+ &lpsr_clk,
+ &pgc_clk,
+ &rtc_clk,
+ &ata_clk,
+ &owire_clk,
+ &fec_clk[0],
+ &fec_clk[1],
+ &fec_clk[2],
+ &mipi_hsc1_clk,
+ &mipi_hsc2_clk,
+ &mipi_esc_clk,
+ &mipi_hsp_clk,
+ &sahara_clk[0],
+ &sahara_clk[1],
+ &gpu3d_clk,
+ &garb_clk,
+ &emi_garb_clk,
+ &ddr_hf_clk,
+ &gpu2d_clk,
+ &scc_clk[0],
+};
+
+static void clk_tree_init(void)
+{
+ u32 reg, reg2, dp_ctl;
+
+ ipg_perclk.set_parent(&ipg_perclk, &lp_apm_clk);
+
+ /*
+ *Initialise the IPG PER CLK dividers to 3. IPG_PER_CLK should be at
+ * 8MHz, its derived from lp_apm.
+ */
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ reg &= ~MXC_CCM_CBCDR_PERCLK_PRED1_MASK;
+ reg &= ~MXC_CCM_CBCDR_PERCLK_PRED2_MASK;
+ reg &= ~MXC_CCM_CBCDR_PERCLK_PODF_MASK;
+ reg |= (2 << MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET);
+ __raw_writel(reg, MXC_CCM_CBCDR);
+
+ /* set pll1_main_clk parent */
+ pll1_main_clk.parent = &osc_clk;
+ dp_ctl = __raw_readl(pll_base[0] + MXC_PLL_DP_CTL);
+ if ((dp_ctl & MXC_PLL_DP_CTL_REF_CLK_SEL_MASK) == 0)
+ pll1_main_clk.parent = &fpm_clk;
+ /* set pll2_sw_clk parent */
+ pll2_sw_clk.parent = &osc_clk;
+ dp_ctl = __raw_readl(pll_base[1] + MXC_PLL_DP_CTL);
+ if ((dp_ctl & MXC_PLL_DP_CTL_REF_CLK_SEL_MASK) == 0)
+ pll2_sw_clk.parent = &fpm_clk;
+ /* set pll3_clk parent */
+ pll3_sw_clk.parent = &osc_clk;
+ dp_ctl = __raw_readl(pll_base[2] + MXC_PLL_DP_CTL);
+ if ((dp_ctl & MXC_PLL_DP_CTL_REF_CLK_SEL_MASK) == 0)
+ pll3_sw_clk.parent = &fpm_clk;
+
+ /* set emi_slow_clk parent */
+ emi_slow_clk.parent = &main_bus_clk;
+ reg = __raw_readl(MXC_CCM_CBCDR);
+ if ((reg & MXC_CCM_CBCDR_EMI_CLK_SEL) != 0)
+ emi_slow_clk.parent = &ahb_clk;
+
+ /* set ipg_perclk parent */
+ ipg_perclk.parent = &lp_apm_clk;
+ reg = __raw_readl(MXC_CCM_CBCMR);
+ if ((reg & MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL) != 0) {
+ ipg_perclk.parent = &ipg_clk;
+ } else {
+ if ((reg & MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL) == 0)
+ ipg_perclk.parent = &main_bus_clk;
+ }
+
+ /* set DDR clock parent */
+ reg = __raw_readl(MXC_CCM_CBCMR) & MXC_CCM_CBCMR_DDR_CLK_SEL_MASK;
+ reg >>= MXC_CCM_CBCMR_DDR_CLK_SEL_OFFSET;
+ reg2 = __raw_readl(MXC_CCM_CBCDR) & MXC_CCM_CBCDR_DDR_HF_SEL;
+ reg2 >>= MXC_CCM_CBCDR_DDR_HF_SEL_OFFSET;
+
+ if (reg2) {
+ ddr_clk.parent = &ddr_hf_clk;
+ } else {
+ if (reg == 0) {
+ ddr_clk.parent = &axi_a_clk;
+ } else if (reg == 1) {
+ ddr_clk.parent = &axi_b_clk;
+ } else if (reg == 2) {
+ ddr_clk.parent = &emi_slow_clk;
+ } else {
+ ddr_clk.parent = &ahb_clk;
+ }
+ }
+}
+
+int __init mxc_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2)
+{
+ struct clk **clkp;
+ int i = 0, j = 0, reg;
+ int wp_cnt = 0;
+
+ /* Turn off all possible clocks */
+ if (mxc_jtag_enabled) {
+ __raw_writel(1 << MXC_CCM_CCGR0_CG0_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG1_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG2_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG3_OFFSET |
+ 3 << MXC_CCM_CCGR0_CG4_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG8_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG9_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG12_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG13_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG14_OFFSET, MXC_CCM_CCGR0);
+ } else {
+ __raw_writel(1 << MXC_CCM_CCGR0_CG0_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG1_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG2_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG3_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG8_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG9_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG12_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG13_OFFSET |
+ 1 << MXC_CCM_CCGR0_CG14_OFFSET, MXC_CCM_CCGR0);
+ }
+ __raw_writel(0, MXC_CCM_CCGR1);
+ __raw_writel(0, MXC_CCM_CCGR2);
+ __raw_writel(0, MXC_CCM_CCGR3);
+ __raw_writel(1 << MXC_CCM_CCGR4_CG8_OFFSET, MXC_CCM_CCGR4);
+
+ __raw_writel(1 << MXC_CCM_CCGR5_CG2_OFFSET |
+ 3 << MXC_CCM_CCGR5_CG6_OFFSET |
+ 3 << MXC_CCM_CCGR5_CG7_OFFSET |
+ 1 << MXC_CCM_CCGR5_CG8_OFFSET |
+ 3 << MXC_CCM_CCGR5_CG9_OFFSET |
+ 1 << MXC_CCM_CCGR5_CG10_OFFSET |
+ 3 << MXC_CCM_CCGR5_CG11_OFFSET, MXC_CCM_CCGR5);
+
+ __raw_writel(1 << MXC_CCM_CCGR6_CG4_OFFSET, MXC_CCM_CCGR6);
+
+ ckil_clk.rate = ckil;
+ osc_clk.rate = osc;
+ ckih_clk.rate = ckih1;
+ ckih2_clk.rate = ckih2;
+
+ clk_tree_init();
+
+ for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++)
+ clk_register(*clkp);
+
+ /*Setup the LPM bypass bits */
+ reg = __raw_readl(MXC_CCM_CLPCR);
+ reg |= MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS
+ | MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS
+ | MXC_CCM_CLPCR_BYPASS_RTIC_LPM_HS
+ | MXC_CCM_CLPCR_BYPASS_SCC_LPM_HS
+ | MXC_CCM_CLPCR_BYPASS_SDMA_LPM_HS;
+ __raw_writel(reg, MXC_CCM_CLPCR);
+
+ /* Disable the handshake with HSC block as its not
+ * initialised right now.
+ */
+ reg = __raw_readl(MXC_CCM_CCDR);
+ reg |= MXC_CCM_CCDR_HSC_HS_MASK;
+ __raw_writel(reg, MXC_CCM_CCDR);
+
+ /* This will propagate to all children and init all the clock rates */
+ propagate_rate(&osc_clk);
+ propagate_rate(&ckih_clk);
+ propagate_rate(&ckih2_clk);
+ propagate_rate(&ckil_clk);
+ propagate_rate(&pll1_sw_clk);
+ propagate_rate(&pll2_sw_clk);
+
+ clk_enable(&cpu_clk);
+ clk_enable(&main_bus_clk);
+
+ reg = __raw_readl(MXC_CCM_CBCDR) & MXC_CCM_CBCDR_DDR_HF_SEL;
+ reg >>= MXC_CCM_CBCDR_DDR_HF_SEL_OFFSET;
+
+ if (reg)
+ clk_set_parent(&ddr_clk, &ddr_hf_clk);
+ else
+ clk_set_parent(&ddr_clk, &axi_a_clk);
+
+ /* Initialise the parents to be axi_b, parents are set to
+ * axi_a when the clocks are enabled.
+ */
+ clk_set_parent(&vpu_clk[0], &axi_b_clk);
+ clk_set_parent(&vpu_clk[1], &axi_b_clk);
+ clk_set_parent(&gpu3d_clk, &axi_b_clk);
+ clk_set_parent(&gpu2d_clk, &axi_b_clk);
+
+ /* move cspi to 24MHz */
+ clk_set_parent(&cspi_main_clk, &lp_apm_clk);
+ clk_set_rate(&cspi_main_clk, 12000000);
+ /*move the spdif0 to spdif_xtal_ckl */
+ clk_set_parent(&spdif0_clk[0], &spdif_xtal_clk);
+ /*set the SPDIF dividers to 1 */
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ reg &= ~MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CDCDR_SPDIF0_CLK_PRED_MASK;
+ __raw_writel(reg, MXC_CCM_CDCDR);
+
+ /* move the spdif1 to 24MHz */
+ clk_set_parent(&spdif1_clk[0], &spdif_xtal_clk);
+ /* set the spdif1 dividers to 1 */
+ reg = __raw_readl(MXC_CCM_CDCDR);
+ reg &= ~MXC_CCM_CDCDR_SPDIF1_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CDCDR_SPDIF1_CLK_PRED_MASK;
+ __raw_writel(reg, MXC_CCM_CDCDR);
+
+ /* Move SSI clocks to SSI_LP_APM clock */
+ clk_set_parent(&ssi_lp_apm_clk, &lp_apm_clk);
+
+ clk_set_parent(&ssi1_clk[0], &ssi_lp_apm_clk);
+ /* set the SSI dividers to divide by 2 */
+ reg = __raw_readl(MXC_CCM_CS1CDR);
+ reg &= ~MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK;
+ reg |= 1 << MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET;
+ __raw_writel(reg, MXC_CCM_CS1CDR);
+
+ clk_set_parent(&ssi2_clk[0], &ssi_lp_apm_clk);
+ reg = __raw_readl(MXC_CCM_CS2CDR);
+ reg &= ~MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK;
+ reg |= 1 << MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET;
+ __raw_writel(reg, MXC_CCM_CS2CDR);
+
+ /* Change the SSI_EXT1_CLK to be sourced from SSI1_CLK_ROOT */
+ clk_set_parent(&ssi_ext1_clk, &ssi1_clk[0]);
+ clk_set_parent(&ssi_ext2_clk, &ssi2_clk[0]);
+
+ /* move usb_phy_clk to 24MHz */
+ clk_set_parent(&usb_phy_clk, &osc_clk);
+
+ /* set usboh3_clk to pll2 */
+ clk_set_parent(&usboh3_clk[0], &pll2_sw_clk);
+ reg = __raw_readl(MXC_CCM_CSCDR1);
+ reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK;
+ reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK;
+ reg |= 4 << MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET;
+ reg |= 1 << MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CSCDR1);
+
+ /* Set the current working point. */
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+ /* Update the cpu working point table based on the PLL1 freq
+ * at boot time
+ */
+ if (pll1_main_clk.rate <= cpu_wp_tbl[cpu_wp_nr - 1].cpu_rate)
+ wp_cnt = 1;
+ else if (pll1_main_clk.rate <= cpu_wp_tbl[1].cpu_rate &&
+ pll1_main_clk.rate > cpu_wp_tbl[2].cpu_rate)
+ wp_cnt = cpu_wp_nr - 1;
+ else
+ wp_cnt = cpu_wp_nr;
+
+ cpu_wp_tbl[0].cpu_rate = pll1_main_clk.rate;
+
+ if (wp_cnt == 1) {
+ cpu_wp_tbl[0] = cpu_wp_tbl[cpu_wp_nr - 1];
+ memset(&cpu_wp_tbl[cpu_wp_nr - 1], 0, sizeof(struct cpu_wp));
+ memset(&cpu_wp_tbl[cpu_wp_nr - 2], 0, sizeof(struct cpu_wp));
+ } else if (wp_cnt < cpu_wp_nr) {
+ for (i = 0; i < wp_cnt; i++)
+ cpu_wp_tbl[i] = cpu_wp_tbl[i+1];
+ memset(&cpu_wp_tbl[i], 0, sizeof(struct cpu_wp));
+ }
+
+ if (wp_cnt < cpu_wp_nr) {
+ set_num_cpu_wp(wp_cnt);
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+ }
+
+
+ for (j = 0; j < cpu_wp_nr; j++) {
+ if ((ddr_clk.parent == &ddr_hf_clk)) {
+ /* Change the CPU podf divider based on the boot up
+ * pll1 rate.
+ */
+ cpu_wp_tbl[j].cpu_podf =
+ (pll1_main_clk.rate / cpu_wp_tbl[j].cpu_rate)
+ - 1;
+ if (pll1_main_clk.rate/(cpu_wp_tbl[j].cpu_podf + 1) >
+ cpu_wp_tbl[j].cpu_rate) {
+ cpu_wp_tbl[j].cpu_podf++;
+ cpu_wp_tbl[j].cpu_rate =
+ pll1_main_clk.rate/
+ (1000 * (cpu_wp_tbl[j].cpu_podf + 1));
+ cpu_wp_tbl[j].cpu_rate *= 1000;
+ }
+ if (pll1_main_clk.rate/(cpu_wp_tbl[j].cpu_podf + 1) <
+ cpu_wp_tbl[j].cpu_rate) {
+ cpu_wp_tbl[j].cpu_rate = pll1_main_clk.rate;
+ }
+ }
+ cpu_wp_tbl[j].pll_rate = pll1_main_clk.rate;
+ }
+ /* Set the current working point. */
+ for (i = 0; i < cpu_wp_nr; i++) {
+ if (clk_get_rate(&cpu_clk) == cpu_wp_tbl[i].cpu_rate) {
+ cpu_curr_wp = i;
+ break;
+ }
+ }
+ if (i > cpu_wp_nr)
+ BUG();
+
+ propagate_rate(&osc_clk);
+ propagate_rate(&pll1_sw_clk);
+ propagate_rate(&pll2_sw_clk);
+
+ /*Allow for automatic gating of the EMI internal clock.
+ * If this is done, emi_intr CCGR bits should be set to 11.
+ */
+ reg = __raw_readl((IO_ADDRESS(M4IF_BASE_ADDR) + 0x8c));
+ reg &= ~0x1;
+ __raw_writel(reg, (IO_ADDRESS(M4IF_BASE_ADDR) + 0x8c));
+
+ clk_set_parent(&arm_axi_clk, &axi_a_clk);
+ clk_set_parent(&ipu_clk[0], &axi_b_clk);
+ clk_set_parent(&uart_main_clk, &pll2_sw_clk);
+
+ clk_set_parent(&emi_slow_clk, &ahb_clk);
+ clk_set_rate(&emi_slow_clk, clk_round_rate(&emi_slow_clk, 130000000));
+
+ /* Change the NFC clock rate to be 1:4 ratio with emi clock. */
+ clk_set_rate(&emi_enfc_clk, clk_round_rate(&emi_enfc_clk,
+ (clk_get_rate(&emi_slow_clk))/4));
+ return 0;
+}
+
+/*!
+ * Setup cpu clock based on working point.
+ * @param wp cpu freq working point
+ * @return 0 on success or error code on failure.
+ */
+static int cpu_clk_set_wp(int wp)
+{
+ struct cpu_wp *p;
+ u32 reg;
+ u32 stat;
+
+ if (wp == cpu_curr_wp)
+ return 0;
+
+ p = &cpu_wp_tbl[wp];
+
+ /*
+ * If DDR clock is sourced from PLL1, we cannot drop PLL1 freq.
+ * Use the ARM_PODF to change the freq of the core, leave the PLL1
+ * freq unchanged.
+ */
+ if (ddr_clk.parent == &ddr_hf_clk) {
+ reg = __raw_readl(MXC_CCM_CACRR);
+ reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK;
+ reg |= cpu_wp_tbl[wp].cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET;
+ __raw_writel(reg, MXC_CCM_CACRR);
+ cpu_curr_wp = wp;
+ cpu_clk.rate = cpu_wp_tbl[wp].cpu_rate;
+ } else {
+ struct timespec nstimeofday;
+ struct timespec curtime;
+
+ /* Change the ARM clock to requested frequency */
+ /* First move the ARM clock to step clock which is running
+ * at 24MHz.
+ */
+
+ /* Change the source of pll1_sw_clk to be the step_clk */
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+
+ /* Stop the PLL */
+ reg = __raw_readl(MXC_DPLL1_BASE + MXC_PLL_DP_CTL);
+ reg &= ~MXC_PLL_DP_CTL_UPEN;
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_CTL);
+
+ /* PDF and MFI */
+ reg = p->pdf | p->mfi << MXC_PLL_DP_OP_MFI_OFFSET;
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_OP);
+
+ /* MFD */
+ __raw_writel(p->mfd, MXC_DPLL1_BASE + MXC_PLL_DP_MFD);
+
+ /* MFI */
+ __raw_writel(p->mfn, MXC_DPLL1_BASE + MXC_PLL_DP_MFN);
+
+ reg = __raw_readl(MXC_DPLL1_BASE + MXC_PLL_DP_CTL);
+ reg |= MXC_PLL_DP_CTL_UPEN;
+ /* Set the UPEN bits */
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_CTL);
+ /* Forcefully restart the PLL */
+ reg |= MXC_PLL_DP_CTL_RST;
+ __raw_writel(reg, MXC_DPLL1_BASE + MXC_PLL_DP_CTL);
+
+ /* Wait for the PLL to lock */
+ getnstimeofday(&nstimeofday);
+ do {
+ getnstimeofday(&curtime);
+ if ((curtime.tv_nsec - nstimeofday.tv_nsec) > SPIN_DELAY)
+ panic("pll1 relock failed\n");
+ stat = __raw_readl(MXC_DPLL1_BASE + MXC_PLL_DP_CTL) &
+ MXC_PLL_DP_CTL_LRF;
+ } while (!stat);
+
+ reg = __raw_readl(MXC_CCM_CCSR);
+ /* Move the PLL1 back to the pll1_main_clk */
+ reg &= ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+
+ cpu_curr_wp = wp;
+
+ pll1_sw_clk.rate = cpu_wp_tbl[wp].cpu_rate;
+ pll1_main_clk.rate = pll1_sw_clk.rate;
+ cpu_clk.rate = pll1_sw_clk.rate;
+ }
+
+#if defined(CONFIG_CPU_FREQ)
+ cpufreq_trig_needed = 1;
+#endif
+ return 0;
+}
+
diff --git a/arch/arm/mach-mx51/cpu.c b/arch/arm/mach-mx51/cpu.c
new file mode 100644
index 000000000000..00a4bb8bbfe7
--- /dev/null
+++ b/arch/arm/mach-mx51/cpu.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mach-mx51/cpu.c
+ *
+ * @brief This file contains the CPU initialization code.
+ *
+ * @ingroup MSL_MX51
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+#include "crm_regs.h"
+
+/*!
+ * CPU initialization. It is called by fixup_mxc_board()
+ */
+void __init mxc_cpu_init(void)
+{
+ if (!system_rev)
+ mxc_set_system_rev(0x51, CHIP_REV_1_0);
+}
+
+static int __init post_cpu_init(void)
+{
+ unsigned int base, reg;
+
+ /* Set ALP bits to 000. Set ALP_EN bit in Arm Memory Controller reg. */
+ reg = 0x8;
+ __raw_writel(reg, MXC_CORTEXA8_PLAT_AMC);
+
+ base = IO_ADDRESS(AIPS1_BASE_ADDR);
+ __raw_writel(0x0, base + 0x40);
+ __raw_writel(0x0, base + 0x44);
+ __raw_writel(0x0, base + 0x48);
+ __raw_writel(0x0, base + 0x4C);
+ reg = __raw_readl(base + 0x50) & 0x00FFFFFF;
+ __raw_writel(reg, base + 0x50);
+
+ base = IO_ADDRESS(AIPS2_BASE_ADDR);
+ __raw_writel(0x0, base + 0x40);
+ __raw_writel(0x0, base + 0x44);
+ __raw_writel(0x0, base + 0x48);
+ __raw_writel(0x0, base + 0x4C);
+ reg = __raw_readl(base + 0x50) & 0x00FFFFFF;
+ __raw_writel(reg, base + 0x50);
+
+ return 0;
+}
+
+postcore_initcall(post_cpu_init);
diff --git a/arch/arm/mach-mx51/crm_regs.h b/arch/arm/mach-mx51/crm_regs.h
new file mode 100644
index 000000000000..37689877e57e
--- /dev/null
+++ b/arch/arm/mach-mx51/crm_regs.h
@@ -0,0 +1,690 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX51_CRM_REGS_H__
+#define __ARCH_ARM_MACH_MX51_CRM_REGS_H__
+
+#define MXC_CCM_BASE ((char *)IO_ADDRESS(CCM_BASE_ADDR))
+#define MXC_DPLL1_BASE IO_ADDRESS(PLL1_BASE_ADDR)
+#define MXC_DPLL2_BASE IO_ADDRESS(PLL2_BASE_ADDR)
+#define MXC_DPLL3_BASE IO_ADDRESS(PLL3_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_MFNMINUS 0x14
+#define MXC_PLL_DP_MFNPLUS 0x18
+#define MXC_PLL_DP_HFS_OP 0x1C
+#define MXC_PLL_DP_HFS_MFD 0x20
+#define MXC_PLL_DP_HFS_MFN 0x24
+#define MXC_PLL_DP_MFN_TOGC 0x28
+#define MXC_PLL_DP_DESTAT 0x2c
+
+/* PLL Register Bit definitions */
+#define MXC_PLL_DP_CTL_MUL_CTRL 0x2000
+#define MXC_PLL_DP_CTL_DPDCK0_2_EN 0x1000
+#define MXC_PLL_DP_CTL_DPDCK0_2_OFFSET 12
+#define MXC_PLL_DP_CTL_ADE 0x800
+#define MXC_PLL_DP_CTL_REF_CLK_DIV 0x400
+#define MXC_PLL_DP_CTL_REF_CLK_SEL_MASK (3 << 8)
+#define MXC_PLL_DP_CTL_REF_CLK_SEL_OFFSET 8
+#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_CONFIG_BIST 0x8
+#define MXC_PLL_DP_CONFIG_SJC_CE 0x4
+#define MXC_PLL_DP_CONFIG_AREN 0x2
+#define MXC_PLL_DP_CONFIG_LDREQ 0x1
+
+#define MXC_PLL_DP_OP_MFI_OFFSET 4
+#define MXC_PLL_DP_OP_MFI_MASK (0xF << 4)
+#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 0x07FFFFFF
+
+#define MXC_PLL_DP_MFN_OFFSET 0x0
+#define MXC_PLL_DP_MFN_MASK 0x07FFFFFF
+
+#define MXC_PLL_DP_MFN_TOGC_TOG_DIS (1 << 17)
+#define MXC_PLL_DP_MFN_TOGC_TOG_EN (1 << 16)
+#define MXC_PLL_DP_MFN_TOGC_CNT_OFFSET 0x0
+#define MXC_PLL_DP_MFN_TOGC_CNT_MASK 0xFFFF
+
+#define MXC_PLL_DP_DESTAT_TOG_SEL (1 << 31)
+#define MXC_PLL_DP_DESTAT_MFN 0x07FFFFFF
+
+/* Register addresses of CCM*/
+#define MXC_CCM_CCR (MXC_CCM_BASE + 0x00)
+#define MXC_CCM_CCDR (MXC_CCM_BASE + 0x04)
+#define MXC_CCM_CSR (MXC_CCM_BASE + 0x08)
+#define MXC_CCM_CCSR (MXC_CCM_BASE + 0x0C)
+#define MXC_CCM_CACRR (MXC_CCM_BASE + 0x10)
+#define MXC_CCM_CBCDR (MXC_CCM_BASE + 0x14)
+#define MXC_CCM_CBCMR (MXC_CCM_BASE + 0x18)
+#define MXC_CCM_CSCMR1 (MXC_CCM_BASE + 0x1C)
+#define MXC_CCM_CSCMR2 (MXC_CCM_BASE + 0x20)
+#define MXC_CCM_CSCDR1 (MXC_CCM_BASE + 0x24)
+#define MXC_CCM_CS1CDR (MXC_CCM_BASE + 0x28)
+#define MXC_CCM_CS2CDR (MXC_CCM_BASE + 0x2C)
+#define MXC_CCM_CDCDR (MXC_CCM_BASE + 0x30)
+#define MXC_CCM_CHSCDR (MXC_CCM_BASE + 0x34)
+#define MXC_CCM_CSCDR2 (MXC_CCM_BASE + 0x38)
+#define MXC_CCM_CSCDR3 (MXC_CCM_BASE + 0x3C)
+#define MXC_CCM_CSCDR4 (MXC_CCM_BASE + 0x40)
+#define MXC_CCM_CWDR (MXC_CCM_BASE + 0x44)
+#define MXC_CCM_CDHIPR (MXC_CCM_BASE + 0x48)
+#define MXC_CCM_CDCR (MXC_CCM_BASE + 0x4C)
+#define MXC_CCM_CTOR (MXC_CCM_BASE + 0x50)
+#define MXC_CCM_CLPCR (MXC_CCM_BASE + 0x54)
+#define MXC_CCM_CISR (MXC_CCM_BASE + 0x58)
+#define MXC_CCM_CIMR (MXC_CCM_BASE + 0x5C)
+#define MXC_CCM_CCOSR (MXC_CCM_BASE + 0x60)
+#define MXC_CCM_CGPR (MXC_CCM_BASE + 0x64)
+#define MXC_CCM_CCGR0 (MXC_CCM_BASE + 0x68)
+#define MXC_CCM_CCGR1 (MXC_CCM_BASE + 0x6C)
+#define MXC_CCM_CCGR2 (MXC_CCM_BASE + 0x70)
+#define MXC_CCM_CCGR3 (MXC_CCM_BASE + 0x74)
+#define MXC_CCM_CCGR4 (MXC_CCM_BASE + 0x78)
+#define MXC_CCM_CCGR5 (MXC_CCM_BASE + 0x7C)
+#define MXC_CCM_CCGR6 (MXC_CCM_BASE + 0x80)
+#define MXC_CCM_CMEOR (MXC_CCM_BASE + 0x84)
+
+/* Define the bits in register CCR */
+#define MXC_CCM_CCR_COSC_EN (1 << 12)
+#define MXC_CCM_CCR_FPM_MULT_MASK (1 << 11)
+#define MXC_CCM_CCR_CAMP2_EN (1 << 10)
+#define MXC_CCM_CCR_CAMP1_EN (1 << 9)
+#define MXC_CCM_CCR_FPM_EN (1 << 8)
+#define MXC_CCM_CCR_OSCNT_OFFSET (0)
+#define MXC_CCM_CCR_OSCNT_MASK (0xFF)
+
+/* Define the bits in register CCDR */
+#define MXC_CCM_CCDR_HSC_HS_MASK (0x1 << 18)
+#define MXC_CCM_CCDR_IPU_HS_MASK (0x1 << 17)
+#define MXC_CCM_CCDR_EMI_HS_MASK (0x1 << 16)
+
+/* Define the bits in register CSR */
+#define MXC_CCM_CSR_COSR_READY (1 << 5)
+#define MXC_CCM_CSR_LVS_VALUE (1 << 4)
+#define MXC_CCM_CSR_CAMP2_READY (1 << 3)
+#define MXC_CCM_CSR_CAMP1_READY (1 << 2)
+#define MXC_CCM_CSR_FPM_READY (1 << 1)
+#define MXC_CCM_CSR_REF_EN_B (1 << 0)
+
+/* Define the bits in register CCSR */
+#define MXC_CCM_CCSR_LP_APM_SEL (0x1 << 9)
+#define MXC_CCM_CCSR_STEP_SEL_OFFSET (7)
+#define MXC_CCM_CCSR_STEP_SEL_MASK (0x3 << 7)
+#define MXC_CCM_CCSR_PLL2_PODF_OFFSET (5)
+#define MXC_CCM_CCSR_PLL2_PODF_MASK (0x3 << 5)
+#define MXC_CCM_CCSR_PLL3_PODF_OFFSET (3)
+#define MXC_CCM_CCSR_PLL3_PODF_MASK (0x3 << 3)
+#define MXC_CCM_CCSR_PLL1_SW_CLK_SEL (1 << 2)
+#define MXC_CCM_CCSR_PLL2_SW_CLK_SEL (1 << 1)
+#define MXC_CCM_CCSR_PLL3_SW_CLK_SEL (1 << 0)
+
+/* Define the bits in register CACRR */
+#define MXC_CCM_CACRR_ARM_PODF_OFFSET (0)
+#define MXC_CCM_CACRR_ARM_PODF_MASK (0x7)
+
+/* Define the bits in register CBCDR */
+#define MXC_CCM_CBCDR_EMI_CLK_SEL (0x1 << 26)
+#define MXC_CCM_CBCDR_PERIPH_CLK_SEL (0x1 << 25)
+#define MXC_CCM_CBCDR_DDR_HF_SEL_OFFSET (30)
+#define MXC_CCM_CBCDR_DDR_HF_SEL (0x1 << 30)
+#define MXC_CCM_CBCDR_DDR_PODF_OFFSET (27)
+#define MXC_CCM_CBCDR_DDR_PODF_MASK (0x7 << 27)
+#define MXC_CCM_CBCDR_EMI_PODF_OFFSET (22)
+#define MXC_CCM_CBCDR_EMI_PODF_MASK (0x7 << 22)
+#define MXC_CCM_CBCDR_AXI_B_PODF_OFFSET (19)
+#define MXC_CCM_CBCDR_AXI_B_PODF_MASK (0x7 << 19)
+#define MXC_CCM_CBCDR_AXI_A_PODF_OFFSET (16)
+#define MXC_CCM_CBCDR_AXI_A_PODF_MASK (0x7 << 16)
+#define MXC_CCM_CBCDR_NFC_PODF_OFFSET (13)
+#define MXC_CCM_CBCDR_NFC_PODF_MASK (0x7 << 13)
+#define MXC_CCM_CBCDR_AHB_PODF_OFFSET (10)
+#define MXC_CCM_CBCDR_AHB_PODF_MASK (0x7 << 10)
+#define MXC_CCM_CBCDR_IPG_PODF_OFFSET (8)
+#define MXC_CCM_CBCDR_IPG_PODF_MASK (0x3 << 8)
+#define MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET (6)
+#define MXC_CCM_CBCDR_PERCLK_PRED1_MASK (0x3 << 6)
+#define MXC_CCM_CBCDR_PERCLK_PRED2_OFFSET (3)
+#define MXC_CCM_CBCDR_PERCLK_PRED2_MASK (0x7 << 3)
+#define MXC_CCM_CBCDR_PERCLK_PODF_OFFSET (0)
+#define MXC_CCM_CBCDR_PERCLK_PODF_MASK (0x7)
+
+/* Define the bits in register CBCMR */
+#define MXC_CCM_CBCMR_VPU_AXI_CLK_SEL_OFFSET (14)
+#define MXC_CCM_CBCMR_VPU_AXI_CLK_SEL_MASK (0x3 << 14)
+#define MXC_CCM_CBCMR_PERIPH_CLK_SEL_OFFSET (12)
+#define MXC_CCM_CBCMR_PERIPH_CLK_SEL_MASK (0x3 << 12)
+#define MXC_CCM_CBCMR_DDR_CLK_SEL_OFFSET (10)
+#define MXC_CCM_CBCMR_DDR_CLK_SEL_MASK (0x3 << 10)
+#define MXC_CCM_CBCMR_ARM_AXI_CLK_SEL_OFFSET (8)
+#define MXC_CCM_CBCMR_ARM_AXI_CLK_SEL_MASK (0x3 << 8)
+#define MXC_CCM_CBCMR_IPU_HSP_CLK_SEL_OFFSET (6)
+#define MXC_CCM_CBCMR_IPU_HSP_CLK_SEL_MASK (0x3 << 6)
+#define MXC_CCM_CBCMR_GPU_CLK_SEL_OFFSET (4)
+#define MXC_CCM_CBCMR_GPU_CLK_SEL_MASK (0x3 << 4)
+#define MXC_CCM_CBCMR_GPU2D_CLK_SEL_OFFSET (14)
+#define MXC_CCM_CBCMR_GPU2D_CLK_SEL_MASK (0x3 << 14)
+#define MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL (0x1 << 1)
+#define MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL (0x1 << 0)
+
+/* Define the bits in register CSCMR1 */
+#define MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_OFFSET (30)
+#define MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_MASK (0x3 << 30)
+#define MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_OFFSET (28)
+#define MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_MASK (0x3 << 28)
+#define MXC_CCM_CSCMR1_USB_PHY_CLK_SEL_OFFSET (26)
+#define MXC_CCM_CSCMR1_USB_PHY_CLK_SEL (0x1 << 26)
+#define MXC_CCM_CSCMR1_UART_CLK_SEL_OFFSET (24)
+#define MXC_CCM_CSCMR1_UART_CLK_SEL_MASK (0x3 << 24)
+#define MXC_CCM_CSCMR1_USBOH3_CLK_SEL_OFFSET (22)
+#define MXC_CCM_CSCMR1_USBOH3_CLK_SEL_MASK (0x3 << 22)
+#define MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_OFFSET (20)
+#define MXC_CCM_CSCMR1_ESDHC1_MSHC1_CLK_SEL_MASK (0x3 << 20)
+#define MXC_CCM_CSCMR1_ESDHC3_CLK_SEL (0x1 << 19)
+#define MXC_CCM_CSCMR1_ESDHC4_CLK_SEL (0x1 << 18)
+#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_OFFSET (16)
+#define MXC_CCM_CSCMR1_ESDHC2_MSHC2_CLK_SEL_MASK (0x3 << 16)
+#define MXC_CCM_CSCMR1_SSI1_CLK_SEL_OFFSET (14)
+#define MXC_CCM_CSCMR1_SSI1_CLK_SEL_MASK (0x3 << 14)
+#define MXC_CCM_CSCMR1_SSI2_CLK_SEL_OFFSET (12)
+#define MXC_CCM_CSCMR1_SSI2_CLK_SEL_MASK (0x3 << 12)
+#define MXC_CCM_CSCMR1_SSI3_CLK_SEL (0x1 << 11)
+#define MXC_CCM_CSCMR1_VPU_RCLK_SEL (0x1 << 10)
+#define MXC_CCM_CSCMR1_SSI_APM_CLK_SEL_OFFSET (8)
+#define MXC_CCM_CSCMR1_SSI_APM_CLK_SEL_MASK (0x3 << 8)
+#define MXC_CCM_CSCMR1_TVE_CLK_SEL (0x1 << 7)
+#define MXC_CCM_CSCMR1_TVE_EXT_CLK_SEL (0x1 << 6)
+#define MXC_CCM_CSCMR1_CSPI_CLK_SEL_OFFSET (4)
+#define MXC_CCM_CSCMR1_CSPI_CLK_SEL_MASK (0x3 << 4)
+#define MXC_CCM_CSCMR1_SPDIF_CLK_SEL_OFFSET (2)
+#define MXC_CCM_CSCMR1_SPDIF_CLK_SEL_MASK (0x3 << 2)
+#define MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL (0x1 << 1)
+#define MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL (0x1)
+
+/* Define the bits in register CSCMR2 */
+#define MXC_CCM_CSCMR2_DI_CLK_SEL_OFFSET(n) (26+n*3)
+#define MXC_CCM_CSCMR2_DI_CLK_SEL_MASK(n) (0x7 << (26+n*3))
+#define MXC_CCM_CSCMR2_CSI_MCLK2_CLK_SEL_OFFSET (24)
+#define MXC_CCM_CSCMR2_CSI_MCLK2_CLK_SEL_MASK (0x3 << 24)
+#define MXC_CCM_CSCMR2_CSI_MCLK1_CLK_SEL_OFFSET (22)
+#define MXC_CCM_CSCMR2_CSI_MCLK1_CLK_SEL_MASK (0x3 << 22)
+#define MXC_CCM_CSCMR2_ESC_CLK_SEL_OFFSET (20)
+#define MXC_CCM_CSCMR2_ESC_CLK_SEL_MASK (0x3 << 20)
+#define MXC_CCM_CSCMR2_HSC2_CLK_SEL_OFFSET (18)
+#define MXC_CCM_CSCMR2_HSC2_CLK_SEL_MASK (0x3 << 18)
+#define MXC_CCM_CSCMR2_HSC1_CLK_SEL_OFFSET (16)
+#define MXC_CCM_CSCMR2_HSC1_CLK_SEL_MASK (0x3 << 16)
+#define MXC_CCM_CSCMR2_HSI2C_CLK_SEL_OFFSET (14)
+#define MXC_CCM_CSCMR2_HSI2C_CLK_SEL_MASK (0x3 << 14)
+#define MXC_CCM_CSCMR2_FIRI_CLK_SEL_OFFSET (12)
+#define MXC_CCM_CSCMR2_FIRI_CLK_SEL_MASK (0x3 << 12)
+#define MXC_CCM_CSCMR2_SIM_CLK_SEL_OFFSET (10)
+#define MXC_CCM_CSCMR2_SIM_CLK_SEL_MASK (0x3 << 10)
+#define MXC_CCM_CSCMR2_SLIMBUS_COM (0x1 << 9)
+#define MXC_CCM_CSCMR2_SLIMBUS_CLK_SEL_OFFSET (6)
+#define MXC_CCM_CSCMR2_SLIMBUS_CLK_SEL_MASK (0x7 << 6)
+#define MXC_CCM_CSCMR2_SPDIF1_COM (1 << 5)
+#define MXC_CCM_CSCMR2_SPDIF0_COM (1 << 4)
+#define MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_OFFSET (2)
+#define MXC_CCM_CSCMR2_SPDIF1_CLK_SEL_MASK (0x3 << 2)
+#define MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_OFFSET (0)
+#define MXC_CCM_CSCMR2_SPDIF0_CLK_SEL_MASK (0x3)
+
+/* Define the bits in register CSCDR1 */
+#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_OFFSET (22)
+#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PRED_MASK (0x7 << 22)
+#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_OFFSET (19)
+#define MXC_CCM_CSCDR1_ESDHC2_MSHC2_CLK_PODF_MASK (0x7 << 19)
+#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_OFFSET (16)
+#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PRED_MASK (0x7 << 16)
+#define MXC_CCM_CSCDR1_PGC_CLK_PODF_OFFSET (14)
+#define MXC_CCM_CSCDR1_PGC_CLK_PODF_MASK (0x3 << 14)
+#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_OFFSET (11)
+#define MXC_CCM_CSCDR1_ESDHC1_MSHC1_CLK_PODF_MASK (0x7 << 11)
+#define MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET (8)
+#define MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK (0x7 << 8)
+#define MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET (6)
+#define MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK (0x3 << 6)
+#define MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET (3)
+#define MXC_CCM_CSCDR1_UART_CLK_PRED_MASK (0x7 << 3)
+#define MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CSCDR1_UART_CLK_PODF_MASK (0x7)
+
+/* Define the bits in register CS1CDR and CS2CDR */
+#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_OFFSET (22)
+#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_MASK (0x7 << 22)
+#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_OFFSET (16)
+#define MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_MASK (0x3F << 16)
+#define MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET (6)
+#define MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK (0x7 << 6)
+#define MXC_CCM_CS1CDR_SSI1_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK (0x3F)
+
+#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PRED_OFFSET (22)
+#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PRED_MASK (0x7 << 22)
+#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PODF_OFFSET (16)
+#define MXC_CCM_CS2CDR_SSI_EXT2_CLK_PODF_MASK (0x3F << 16)
+#define MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET (6)
+#define MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK (0x7 << 6)
+#define MXC_CCM_CS2CDR_SSI2_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK (0x3F)
+
+/* Define the bits in register CDCDR */
+#define MXC_CCM_CDCDR_TVE_CLK_PRED_OFFSET (28)
+#define MXC_CCM_CDCDR_TVE_CLK_PRED_MASK (0x7 << 28)
+#define MXC_CCM_CDCDR_SPDIF0_CLK_PRED_OFFSET (25)
+#define MXC_CCM_CDCDR_SPDIF0_CLK_PRED_MASK (0x7 << 25)
+#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_OFFSET (19)
+#define MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK (0x3F << 19)
+#define MXC_CCM_CDCDR_SPDIF1_CLK_PRED_OFFSET (16)
+#define MXC_CCM_CDCDR_SPDIF1_CLK_PRED_MASK (0x7 << 16)
+#define MXC_CCM_CDCDR_SPDIF1_CLK_PODF_OFFSET (9)
+#define MXC_CCM_CDCDR_SPDIF1_CLK_PODF_MASK (0x3F << 9)
+#define MXC_CCM_CDCDR_DI_CLK_PRED_OFFSET (6)
+#define MXC_CCM_CDCDR_DI_CLK_PRED_MASK (0x7 << 6)
+#define MXC_CCM_CDCDR_USB_PHY_PRED_OFFSET (3)
+#define MXC_CCM_CDCDR_USB_PHY_PRED_MASK (0x7 << 3)
+#define MXC_CCM_CDCDR_USB_PHY_PODF_OFFSET (0)
+#define MXC_CCM_CDCDR_USB_PHY_PODF_MASK (0x7)
+
+/* Define the bits in register CHSCCDR */
+#define MXC_CCM_CHSCCDR_ESC_CLK_PRED_OFFSET (12)
+#define MXC_CCM_CHSCCDR_ESC_CLK_PRED_MASK (0x7 << 12)
+#define MXC_CCM_CHSCCDR_ESC_CLK_PODF_OFFSET (6)
+#define MXC_CCM_CHSCCDR_ESC_CLK_PODF_MASK (0x3F << 6)
+#define MXC_CCM_CHSCCDR_HSC2_CLK_PODF_OFFSET (3)
+#define MXC_CCM_CHSCCDR_HSC2_CLK_PODF_MASK (0x7 << 3)
+#define MXC_CCM_CHSCCDR_HSC1_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CHSCCDR_HSC1_CLK_PODF_MASK (0x7)
+
+/* Define the bits in register CSCDR2 */
+#define MXC_CCM_CSCDR2_CSPI_CLK_PRED_OFFSET (25)
+#define MXC_CCM_CSCDR2_CSPI_CLK_PRED_MASK (0x7 << 25)
+#define MXC_CCM_CSCDR2_CSPI_CLK_PODF_OFFSET (19)
+#define MXC_CCM_CSCDR2_CSPI_CLK_PODF_MASK (0x3F << 19)
+#define MXC_CCM_CSCDR2_SIM_CLK_PRED_OFFSET (16)
+#define MXC_CCM_CSCDR2_SIM_CLK_PRED_MASK (0x7 << 16)
+#define MXC_CCM_CSCDR2_SIM_CLK_PODF_OFFSET (9)
+#define MXC_CCM_CSCDR2_SIM_CLK_PODF_MASK (0x3F << 9)
+#define MXC_CCM_CSCDR2_SLIMBUS_CLK_PRED_OFFSET (6)
+#define MXC_CCM_CSCDR2_SLIMBUS_PRED_MASK (0x7 << 6)
+#define MXC_CCM_CSCDR2_SLIMBUS_PODF_OFFSET (0)
+#define MXC_CCM_CSCDR2_SLIMBUS_PODF_MASK (0x3F)
+
+/* Define the bits in register CSCDR3 */
+#define MXC_CCM_CSCDR3_HSI2C_CLK_PRED_OFFSET (16)
+#define MXC_CCM_CSCDR3_HSI2C_CLK_PRED_MASK (0x7 << 16)
+#define MXC_CCM_CSCDR3_HSI2C_CLK_PODF_OFFSET (9)
+#define MXC_CCM_CSCDR3_HSI2C_CLK_PODF_MASK (0x3F << 9)
+#define MXC_CCM_CSCDR3_FIRI_CLK_PRED_OFFSET (6)
+#define MXC_CCM_CSCDR3_FIRI_CLK_PRED_MASK (0x7 << 6)
+#define MXC_CCM_CSCDR3_FIRI_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CSCDR3_FIRI_CLK_PODF_MASK (0x3F)
+
+/* Define the bits in register CSCDR4 */
+#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PRED_OFFSET (16)
+#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PRED_MASK (0x7 << 16)
+#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PODF_OFFSET (9)
+#define MXC_CCM_CSCDR4_CSI_MCLK2_CLK_PODF_MASK (0x3F << 9)
+#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PRED_OFFSET (6)
+#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PRED_MASK (0x7 << 6)
+#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PODF_OFFSET (0)
+#define MXC_CCM_CSCDR4_CSI_MCLK1_CLK_PODF_MASK (0x3F)
+
+/* Define the bits in register CDHIPR */
+#define MXC_CCM_CDHIPR_ARM_PODF_BUSY (1 << 16)
+#define MXC_CCM_CDHIPR_DDR_HF_CLK_SEL_BUSY (1 << 8)
+#define MXC_CCM_CDHIPR_DDR_PODF_BUSY (1 << 7)
+#define MXC_CCM_CDHIPR_EMI_CLK_SEL_BUSY (1 << 6)
+#define MXC_CCM_CDHIPR_PERIPH_CLK_SEL_BUSY (1 << 5)
+#define MXC_CCM_CDHIPR_NFC_IPG_INT_MEM_PODF_BUSY (1 << 4)
+#define MXC_CCM_CDHIPR_AHB_PODF_BUSY (1 << 3)
+#define MXC_CCM_CDHIPR_EMI_PODF_BUSY (1 << 2)
+#define MXC_CCM_CDHIPR_AXI_B_PODF_BUSY (1 << 1)
+#define MXC_CCM_CDHIPR_AXI_A_PODF_BUSY (1 << 0)
+
+/* Define the bits in register CDCR */
+#define MXC_CCM_CDCR_ARM_FREQ_SHIFT_DIVIDER (0x1 << 2)
+#define MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_OFFSET (0)
+#define MXC_CCM_CDCR_PERIPH_CLK_DVFS_PODF_MASK (0x3)
+
+/* Define the bits in register CLPCR */
+#define MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS (0x1 << 23)
+#define MXC_CCM_CLPCR_BYPASS_SCC_LPM_HS (0x1 << 22)
+#define MXC_CCM_CLPCR_BYPASS_MAX_LPM_HS (0x1 << 21)
+#define MXC_CCM_CLPCR_BYPASS_SDMA_LPM_HS (0x1 << 20)
+#define MXC_CCM_CLPCR_BYPASS_EMI_LPM_HS (0x1 << 19)
+#define MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS (0x1 << 18)
+#define MXC_CCM_CLPCR_BYPASS_RTIC_LPM_HS (0x1 << 17)
+#define MXC_CCM_CLPCR_BYPASS_RNGC_LPM_HS (0x1 << 16)
+#define MXC_CCM_CLPCR_COSC_PWRDOWN (0x1 << 11)
+#define MXC_CCM_CLPCR_STBY_COUNT_OFFSET (9)
+#define MXC_CCM_CLPCR_STBY_COUNT_MASK (0x3 << 9)
+#define MXC_CCM_CLPCR_VSTBY (0x1 << 8)
+#define MXC_CCM_CLPCR_DIS_REF_OSC (0x1 << 7)
+#define MXC_CCM_CLPCR_SBYOS (0x1 << 6)
+#define MXC_CCM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
+#define MXC_CCM_CLPCR_LPSR_CLK_SEL_OFFSET (3)
+#define MXC_CCM_CLPCR_LPSR_CLK_SEL_MASK (0x3 << 3)
+#define MXC_CCM_CLPCR_LPM_OFFSET (0)
+#define MXC_CCM_CLPCR_LPM_MASK (0x3)
+
+/* Define the bits in register CISR */
+#define MXC_CCM_CISR_ARM_PODF_LOADED (0x1 << 25)
+#define MXC_CCM_CISR_NFC_IPG_INT_MEM_PODF_LOADED (0x1 << 21)
+#define MXC_CCM_CISR_AHB_PODF_LOADED (0x1 << 20)
+#define MXC_CCM_CISR_EMI_PODF_LOADED (0x1 << 19)
+#define MXC_CCM_CISR_AXI_B_PODF_LOADED (0x1 << 18)
+#define MXC_CCM_CISR_AXI_A_PODF_LOADED (0x1 << 17)
+#define MXC_CCM_CISR_DIVIDER_LOADED (0x1 << 16)
+#define MXC_CCM_CISR_COSC_READY (0x1 << 6)
+#define MXC_CCM_CISR_CKIH2_READY (0x1 << 5)
+#define MXC_CCM_CISR_CKIH_READY (0x1 << 4)
+#define MXC_CCM_CISR_FPM_READY (0x1 << 3)
+#define MXC_CCM_CISR_LRF_PLL3 (0x1 << 2)
+#define MXC_CCM_CISR_LRF_PLL2 (0x1 << 1)
+#define MXC_CCM_CISR_LRF_PLL1 (0x1)
+
+/* Define the bits in register CIMR */
+#define MXC_CCM_CIMR_MASK_ARM_PODF_LOADED (0x1 << 25)
+#define MXC_CCM_CIMR_MASK_NFC_IPG_INT_MEM_PODF_LOADED (0x1 << 21)
+#define MXC_CCM_CIMR_MASK_EMI_PODF_LOADED (0x1 << 20)
+#define MXC_CCM_CIMR_MASK_AXI_C_PODF_LOADED (0x1 << 19)
+#define MXC_CCM_CIMR_MASK_AXI_B_PODF_LOADED (0x1 << 18)
+#define MXC_CCM_CIMR_MASK_AXI_A_PODF_LOADED (0x1 << 17)
+#define MXC_CCM_CIMR_MASK_DIVIDER_LOADED (0x1 << 16)
+#define MXC_CCM_CIMR_MASK_COSC_READY (0x1 << 5)
+#define MXC_CCM_CIMR_MASK_CKIH_READY (0x1 << 4)
+#define MXC_CCM_CIMR_MASK_FPM_READY (0x1 << 3)
+#define MXC_CCM_CIMR_MASK_LRF_PLL3 (0x1 << 2)
+#define MXC_CCM_CIMR_MASK_LRF_PLL2 (0x1 << 1)
+#define MXC_CCM_CIMR_MASK_LRF_PLL1 (0x1)
+
+/* Define the bits in register CCOSR */
+#define MXC_CCM_CCOSR_CKO2_EN_OFFSET (0x1 << 24)
+#define MXC_CCM_CCOSR_CKO2_DIV_OFFSET (21)
+#define MXC_CCM_CCOSR_CKO2_DIV_MASK (0x7 << 21)
+#define MXC_CCM_CCOSR_CKO2_SEL_OFFSET (16)
+#define MXC_CCM_CCOSR_CKO2_SEL_MASK (0x1F << 16)
+#define MXC_CCM_CCOSR_CKOL_EN (0x1 << 7)
+#define MXC_CCM_CCOSR_CKOL_DIV_OFFSET (4)
+#define MXC_CCM_CCOSR_CKOL_DIV_MASK (0x7 << 4)
+#define MXC_CCM_CCOSR_CKOL_SEL_OFFSET (0)
+#define MXC_CCM_CCOSR_CKOL_SEL_MASK (0xF)
+
+/* Define the bits in registers CGPR */
+#define MXC_CCM_CGPR_EFUSE_PROG_SUPPLY_GATE (0x1 << 4)
+#define MXC_CCM_CGPR_FPM_SEL (0x1 << 3)
+#define MXC_CCM_CGPR_VL_L2BIST_CLKDIV_OFFSET (0)
+#define MXC_CCM_CGPR_VL_L2BIST_CLKDIV_MASK (0x7)
+
+/* Define the bits in registers CCGRx */
+#define MXC_CCM_CCGR_CG_MASK 0x3
+
+#define MXC_CCM_CCGR0_CG15_OFFSET 30
+#define MXC_CCM_CCGR0_CG15_MASK (0x3 << 30)
+#define MXC_CCM_CCGR0_CG14_OFFSET 28
+#define MXC_CCM_CCGR0_CG14_MASK (0x3 << 28)
+#define MXC_CCM_CCGR0_CG13_OFFSET 26
+#define MXC_CCM_CCGR0_CG13_MASK (0x3 << 26)
+#define MXC_CCM_CCGR0_CG12_OFFSET 24
+#define MXC_CCM_CCGR0_CG12_MASK (0x3 << 24)
+#define MXC_CCM_CCGR0_CG11_OFFSET 22
+#define MXC_CCM_CCGR0_CG11_MASK (0x3 << 22)
+#define MXC_CCM_CCGR0_CG10_OFFSET 20
+#define MXC_CCM_CCGR0_CG10_MASK (0x3 << 20)
+#define MXC_CCM_CCGR0_CG9_OFFSET 18
+#define MXC_CCM_CCGR0_CG9_MASK (0x3 << 18)
+#define MXC_CCM_CCGR0_CG8_OFFSET 16
+#define MXC_CCM_CCGR0_CG8_MASK (0x3 << 16)
+#define MXC_CCM_CCGR0_CG7_OFFSET 14
+#define MXC_CCM_CCGR0_CG6_OFFSET 12
+#define MXC_CCM_CCGR0_CG5_OFFSET 10
+#define MXC_CCM_CCGR0_CG5_MASK (0x3 << 10)
+#define MXC_CCM_CCGR0_CG4_OFFSET 8
+#define MXC_CCM_CCGR0_CG4_MASK (0x3 << 8)
+#define MXC_CCM_CCGR0_CG3_OFFSET 6
+#define MXC_CCM_CCGR0_CG3_MASK (0x3 << 6)
+#define MXC_CCM_CCGR0_CG2_OFFSET 4
+#define MXC_CCM_CCGR0_CG2_MASK (0x3 << 4)
+#define MXC_CCM_CCGR0_CG1_OFFSET 2
+#define MXC_CCM_CCGR0_CG1_MASK (0x3 << 2)
+#define MXC_CCM_CCGR0_CG0_OFFSET 0
+#define MXC_CCM_CCGR0_CG0_MASK 0x3
+
+#define MXC_CCM_CCGR1_CG15_OFFSET 30
+#define MXC_CCM_CCGR1_CG14_OFFSET 28
+#define MXC_CCM_CCGR1_CG13_OFFSET 26
+#define MXC_CCM_CCGR1_CG12_OFFSET 24
+#define MXC_CCM_CCGR1_CG11_OFFSET 22
+#define MXC_CCM_CCGR1_CG10_OFFSET 20
+#define MXC_CCM_CCGR1_CG9_OFFSET 18
+#define MXC_CCM_CCGR1_CG8_OFFSET 16
+#define MXC_CCM_CCGR1_CG7_OFFSET 14
+#define MXC_CCM_CCGR1_CG6_OFFSET 12
+#define MXC_CCM_CCGR1_CG5_OFFSET 10
+#define MXC_CCM_CCGR1_CG4_OFFSET 8
+#define MXC_CCM_CCGR1_CG3_OFFSET 6
+#define MXC_CCM_CCGR1_CG2_OFFSET 4
+#define MXC_CCM_CCGR1_CG1_OFFSET 2
+#define MXC_CCM_CCGR1_CG0_OFFSET 0
+
+#define MXC_CCM_CCGR2_CG15_OFFSET 30
+#define MXC_CCM_CCGR2_CG14_OFFSET 28
+#define MXC_CCM_CCGR2_CG13_OFFSET 26
+#define MXC_CCM_CCGR2_CG12_OFFSET 24
+#define MXC_CCM_CCGR2_CG11_OFFSET 22
+#define MXC_CCM_CCGR2_CG10_OFFSET 20
+#define MXC_CCM_CCGR2_CG9_OFFSET 18
+#define MXC_CCM_CCGR2_CG8_OFFSET 16
+#define MXC_CCM_CCGR2_CG7_OFFSET 14
+#define MXC_CCM_CCGR2_CG6_OFFSET 12
+#define MXC_CCM_CCGR2_CG5_OFFSET 10
+#define MXC_CCM_CCGR2_CG4_OFFSET 8
+#define MXC_CCM_CCGR2_CG3_OFFSET 6
+#define MXC_CCM_CCGR2_CG2_OFFSET 4
+#define MXC_CCM_CCGR2_CG1_OFFSET 2
+#define MXC_CCM_CCGR2_CG0_OFFSET 0
+
+#define MXC_CCM_CCGR3_CG15_OFFSET 30
+#define MXC_CCM_CCGR3_CG14_OFFSET 28
+#define MXC_CCM_CCGR3_CG13_OFFSET 26
+#define MXC_CCM_CCGR3_CG12_OFFSET 24
+#define MXC_CCM_CCGR3_CG11_OFFSET 22
+#define MXC_CCM_CCGR3_CG10_OFFSET 20
+#define MXC_CCM_CCGR3_CG9_OFFSET 18
+#define MXC_CCM_CCGR3_CG8_OFFSET 16
+#define MXC_CCM_CCGR3_CG7_OFFSET 14
+#define MXC_CCM_CCGR3_CG6_OFFSET 12
+#define MXC_CCM_CCGR3_CG5_OFFSET 10
+#define MXC_CCM_CCGR3_CG4_OFFSET 8
+#define MXC_CCM_CCGR3_CG3_OFFSET 6
+#define MXC_CCM_CCGR3_CG2_OFFSET 4
+#define MXC_CCM_CCGR3_CG1_OFFSET 2
+#define MXC_CCM_CCGR3_CG0_OFFSET 0
+
+#define MXC_CCM_CCGR4_CG15_OFFSET 30
+#define MXC_CCM_CCGR4_CG14_OFFSET 28
+#define MXC_CCM_CCGR4_CG13_OFFSET 26
+#define MXC_CCM_CCGR4_CG12_OFFSET 24
+#define MXC_CCM_CCGR4_CG11_OFFSET 22
+#define MXC_CCM_CCGR4_CG10_OFFSET 20
+#define MXC_CCM_CCGR4_CG9_OFFSET 18
+#define MXC_CCM_CCGR4_CG8_OFFSET 16
+#define MXC_CCM_CCGR4_CG7_OFFSET 14
+#define MXC_CCM_CCGR4_CG6_OFFSET 12
+#define MXC_CCM_CCGR4_CG5_OFFSET 10
+#define MXC_CCM_CCGR4_CG4_OFFSET 8
+#define MXC_CCM_CCGR4_CG3_OFFSET 6
+#define MXC_CCM_CCGR4_CG2_OFFSET 4
+#define MXC_CCM_CCGR4_CG1_OFFSET 2
+#define MXC_CCM_CCGR4_CG0_OFFSET 0
+
+#define MXC_CCM_CCGR5_CG15_OFFSET 30
+#define MXC_CCM_CCGR5_CG14_OFFSET 28
+#define MXC_CCM_CCGR5_CG14_MASK (0x3 << 28)
+#define MXC_CCM_CCGR5_CG13_OFFSET 26
+#define MXC_CCM_CCGR5_CG13_MASK (0x3 << 26)
+#define MXC_CCM_CCGR5_CG12_OFFSET 24
+#define MXC_CCM_CCGR5_CG12_MASK (0x3 << 24)
+#define MXC_CCM_CCGR5_CG11_OFFSET 22
+#define MXC_CCM_CCGR5_CG11_MASK (0x3 << 22)
+#define MXC_CCM_CCGR5_CG10_OFFSET 20
+#define MXC_CCM_CCGR5_CG10_MASK (0x3 << 20)
+#define MXC_CCM_CCGR5_CG9_OFFSET 18
+#define MXC_CCM_CCGR5_CG9_MASK (0x3 << 18)
+#define MXC_CCM_CCGR5_CG8_OFFSET 16
+#define MXC_CCM_CCGR5_CG8_MASK (0x3 << 16)
+#define MXC_CCM_CCGR5_CG7_OFFSET 14
+#define MXC_CCM_CCGR5_CG7_MASK (0x3 << 14)
+#define MXC_CCM_CCGR5_CG6_OFFSET 12
+#define MXC_CCM_CCGR5_CG5_OFFSET 10
+#define MXC_CCM_CCGR5_CG4_OFFSET 8
+#define MXC_CCM_CCGR5_CG3_OFFSET 6
+#define MXC_CCM_CCGR5_CG2_OFFSET 4
+#define MXC_CCM_CCGR5_CG2_MASK (0x3 << 4)
+#define MXC_CCM_CCGR5_CG1_OFFSET 2
+#define MXC_CCM_CCGR5_CG0_OFFSET 0
+#define MXC_CCM_CCGR6_CG7_OFFSET 14
+#define MXC_CCM_CCGR6_CG7_MASK (0x3 << 14)
+#define MXC_CCM_CCGR6_CG6_OFFSET 12
+#define MXC_CCM_CCGR6_CG6_MASK (0x3 << 12)
+#define MXC_CCM_CCGR6_CG5_OFFSET 10
+#define MXC_CCM_CCGR6_CG5_MASK (0x3 << 10)
+#define MXC_CCM_CCGR6_CG4_OFFSET 8
+#define MXC_CCM_CCGR6_CG4_MASK (0x3 << 8)
+#define MXC_CCM_CCGR6_CG3_OFFSET 6
+#define MXC_CCM_CCGR6_CG2_OFFSET 4
+#define MXC_CCM_CCGR6_CG1_OFFSET 2
+#define MXC_CCM_CCGR6_CG0_OFFSET 0
+
+#define MXC_CORTEXA8_BASE IO_ADDRESS(ARM_BASE_ADDR)
+#define MXC_GPC_BASE IO_ADDRESS(GPC_BASE_ADDR)
+#define MXC_DPTC_LP_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x80)
+#define MXC_DPTC_GP_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x100)
+#define MXC_DVFS_CORE_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x180)
+#define MXC_DVFS_PER_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x1C4)
+#define MXC_PGC_IPU_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x220)
+#define MXC_PGC_VPU_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x240)
+#define MXC_PGC_GPU_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x260)
+#define MXC_SRPG_NEON_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x280)
+#define MXC_SRPG_ARM_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x2A0)
+#define MXC_SRPG_EMPGC0_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x2C0)
+#define MXC_SRPG_EMPGC1_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x2D0)
+#define MXC_SRPG_MEGAMIX_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x2E0)
+#define MXC_SRPG_EMI_BASE IO_ADDRESS(GPC_BASE_ADDR + 0x300)
+
+/* CORTEXA8 platform */
+#define MXC_CORTEXA8_PLAT_PVID (MXC_CORTEXA8_BASE + 0x0)
+#define MXC_CORTEXA8_PLAT_GPC (MXC_CORTEXA8_BASE + 0x4)
+#define MXC_CORTEXA8_PLAT_PIC (MXC_CORTEXA8_BASE + 0x8)
+#define MXC_CORTEXA8_PLAT_LPC (MXC_CORTEXA8_BASE + 0xC)
+#define MXC_CORTEXA8_PLAT_NEON_LPC (MXC_CORTEXA8_BASE + 0x10)
+#define MXC_CORTEXA8_PLAT_ICGC (MXC_CORTEXA8_BASE + 0x14)
+#define MXC_CORTEXA8_PLAT_AMC (MXC_CORTEXA8_BASE + 0x18)
+#define MXC_CORTEXA8_PLAT_NMC (MXC_CORTEXA8_BASE + 0x20)
+#define MXC_CORTEXA8_PLAT_NMS (MXC_CORTEXA8_BASE + 0x24)
+
+/* DVFS CORE */
+#define MXC_DVFSTHRS (MXC_DVFS_CORE_BASE + 0x00)
+#define MXC_DVFSCOUN (MXC_DVFS_CORE_BASE + 0x04)
+#define MXC_DVFSSIG1 (MXC_DVFS_CORE_BASE + 0x08)
+#define MXC_DVFSSIG0 (MXC_DVFS_CORE_BASE + 0x0C)
+#define MXC_DVFSGPC0 (MXC_DVFS_CORE_BASE + 0x10)
+#define MXC_DVFSGPC1 (MXC_DVFS_CORE_BASE + 0x14)
+#define MXC_DVFSGPBT (MXC_DVFS_CORE_BASE + 0x18)
+#define MXC_DVFSEMAC (MXC_DVFS_CORE_BASE + 0x1C)
+#define MXC_DVFSCNTR (MXC_DVFS_CORE_BASE + 0x20)
+#define MXC_DVFSLTR0_0 (MXC_DVFS_CORE_BASE + 0x24)
+#define MXC_DVFSLTR0_1 (MXC_DVFS_CORE_BASE + 0x28)
+#define MXC_DVFSLTR1_0 (MXC_DVFS_CORE_BASE + 0x2C)
+#define MXC_DVFSLTR1_1 (MXC_DVFS_CORE_BASE + 0x30)
+#define MXC_DVFSPT0 (MXC_DVFS_CORE_BASE + 0x34)
+#define MXC_DVFSPT1 (MXC_DVFS_CORE_BASE + 0x38)
+#define MXC_DVFSPT2 (MXC_DVFS_CORE_BASE + 0x3C)
+#define MXC_DVFSPT3 (MXC_DVFS_CORE_BASE + 0x40)
+
+/* DVFS PER */
+#define MXC_DVFSPER_LTR0 (MXC_DVFS_PER_BASE)
+#define MXC_DVFSPER_LTR1 (MXC_DVFS_PER_BASE + 0x04)
+#define MXC_DVFSPER_LTR2 (MXC_DVFS_PER_BASE + 0x08)
+#define MXC_DVFSPER_LTR3 (MXC_DVFS_PER_BASE + 0x0C)
+#define MXC_DVFSPER_LTBR0 (MXC_DVFS_PER_BASE + 0x10)
+#define MXC_DVFSPER_LTBR1 (MXC_DVFS_PER_BASE + 0x14)
+#define MXC_DVFSPER_PMCR0 (MXC_DVFS_PER_BASE + 0x18)
+#define MXC_DVFSPER_PMCR1 (MXC_DVFS_PER_BASE + 0x1C)
+
+/* GPC */
+#define MXC_GPC_CNTR (MXC_GPC_BASE + 0x0)
+#define MXC_GPC_PGR (MXC_GPC_BASE + 0x4)
+#define MXC_GPC_VCR (MXC_GPC_BASE + 0x8)
+#define MXC_GPC_ALL_PU (MXC_GPC_BASE + 0xC)
+#define MXC_GPC_NEON (MXC_GPC_BASE + 0x10)
+
+/* PGC */
+#define MXC_PGC_IPU_PGCR (MXC_PGC_IPU_BASE + 0x0)
+#define MXC_PGC_IPU_PGSR (MXC_PGC_IPU_BASE + 0xC)
+#define MXC_PGC_VPU_PGCR (MXC_PGC_VPU_BASE + 0x0)
+#define MXC_PGC_VPU_PGSR (MXC_PGC_VPU_BASE + 0xC)
+#define MXC_PGC_GPU_PGCR (MXC_PGC_GPU_BASE + 0x0)
+#define MXC_PGC_GPU_PGSR (MXC_PGC_GPU_BASE + 0xC)
+
+#define MXC_PGCR_PCR 1
+#define MXC_SRPGCR_PCR 1
+#define MXC_EMPGCR_PCR 1
+#define MXC_PGSR_PSR 1
+
+
+#define MXC_CORTEXA8_PLAT_LPC_DSM (1 << 0)
+#define MXC_CORTEXA8_PLAT_LPC_DBG_DSM (1 << 1)
+
+/* SRPG */
+#define MXC_SRPG_NEON_SRPGCR (MXC_SRPG_NEON_BASE + 0x0)
+#define MXC_SRPG_NEON_PUPSCR (MXC_SRPG_NEON_BASE + 0x4)
+#define MXC_SRPG_NEON_PDNSCR (MXC_SRPG_NEON_BASE + 0x8)
+
+#define MXC_SRPG_ARM_SRPGCR (MXC_SRPG_ARM_BASE + 0x0)
+#define MXC_SRPG_ARM_PUPSCR (MXC_SRPG_ARM_BASE + 0x4)
+#define MXC_SRPG_ARM_PDNSCR (MXC_SRPG_ARM_BASE + 0x8)
+
+#define MXC_SRPG_EMPGC0_SRPGCR (MXC_SRPG_EMPGC0_BASE + 0x0)
+#define MXC_SRPG_EMPGC0_PUPSCR (MXC_SRPG_EMPGC0_BASE + 0x4)
+#define MXC_SRPG_EMPGC0_PDNSCR (MXC_SRPG_EMPGC0_BASE + 0x8)
+
+#define MXC_SRPG_EMPGC1_SRPGCR (MXC_SRPG_EMPGC1_BASE + 0x0)
+#define MXC_SRPG_EMPGC1_PUPSCR (MXC_SRPG_EMPGC1_BASE + 0x4)
+#define MXC_SRPG_EMPGC1_PDNSCR (MXC_SRPG_EMPGC1_BASE + 0x8)
+
+#define MXC_SRPG_MEGAMIX_SRPGCR (MXC_SRPG_MEGAMIX_BASE + 0x0)
+#define MXC_SRPG_MEGAMIX_PUPSCR (MXC_SRPG_MEGAMIX_BASE + 0x4)
+#define MXC_SRPG_MEGAMIX_PDNSCR (MXC_SRPG_MEGAMIX_BASE + 0x8)
+
+#define MXC_SRPGC_EMI_SRPGCR (MXC_SRPGC_EMI_BASE + 0x0)
+#define MXC_SRPGC_EMI_PUPSCR (MXC_SRPGC_EMI_BASE + 0x4)
+#define MXC_SRPGC_EMI_PDNSCR (MXC_SRPGC_EMI_BASE + 0x8)
+
+#endif /* __ARCH_ARM_MACH_MX51_CRM_REGS_H__ */
diff --git a/arch/arm/mach-mx51/devices.c b/arch/arm/mach-mx51/devices.c
new file mode 100644
index 000000000000..3496580d807f
--- /dev/null
+++ b/arch/arm/mach-mx51/devices.c
@@ -0,0 +1,1284 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/uio_driver.h>
+#include <linux/mxc_scc2_driver.h>
+#include <linux/pwm_backlight.h>
+#include <mach/hardware.h>
+#include <mach/spba.h>
+#include <asm/mach-types.h>
+#include "iomux.h"
+#include "crm_regs.h"
+#include <mach/sdma.h>
+#include "sdma_script_code.h"
+
+/* Flag used to indicate when IRAM has been initialized */
+int iram_ready;
+/* Flag used to indicate if dvfs_core is active. */
+int dvfs_core_is_active;
+
+void mxc_sdma_get_script_info(sdma_script_start_addrs * sdma_script_addr)
+{
+ /* AP<->BP */
+ sdma_script_addr->mxc_sdma_ap_2_ap_addr = ap_2_ap_ADDR;
+ sdma_script_addr->mxc_sdma_ap_2_bp_addr = -1;
+ sdma_script_addr->mxc_sdma_bp_2_ap_addr = -1;
+ sdma_script_addr->mxc_sdma_ap_2_ap_fixed_addr = -1;
+
+ /*misc */
+ sdma_script_addr->mxc_sdma_loopback_on_dsp_side_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_interrupt_only_addr = -1;
+
+ /* firi */
+ sdma_script_addr->mxc_sdma_firi_2_per_addr = -1;
+ sdma_script_addr->mxc_sdma_firi_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_per_2_firi_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_firi_addr = -1;
+
+ /* uart */
+ sdma_script_addr->mxc_sdma_uart_2_per_addr = uart_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_uart_2_mcu_addr = uart_2_mcu_ADDR;
+
+ /* UART SH */
+ sdma_script_addr->mxc_sdma_uartsh_2_per_addr = uartsh_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_uartsh_2_mcu_addr = uartsh_2_mcu_ADDR;
+
+ /* SHP */
+ sdma_script_addr->mxc_sdma_per_2_shp_addr = per_2_shp_ADDR;
+ sdma_script_addr->mxc_sdma_shp_2_per_addr = shp_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_shp_addr = mcu_2_shp_ADDR;
+ sdma_script_addr->mxc_sdma_shp_2_mcu_addr = shp_2_mcu_ADDR;
+
+ /* ATA */
+ sdma_script_addr->mxc_sdma_mcu_2_ata_addr = mcu_2_ata_ADDR;
+ sdma_script_addr->mxc_sdma_ata_2_mcu_addr = ata_2_mcu_ADDR;
+
+ /* app */
+ sdma_script_addr->mxc_sdma_app_2_per_addr = app_2_per_ADDR;
+ sdma_script_addr->mxc_sdma_app_2_mcu_addr = app_2_mcu_ADDR;
+ sdma_script_addr->mxc_sdma_per_2_app_addr = per_2_app_ADDR;
+ sdma_script_addr->mxc_sdma_mcu_2_app_addr = mcu_2_app_ADDR;
+
+ /* MSHC */
+ sdma_script_addr->mxc_sdma_mshc_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_mshc_addr = -1;
+
+ /* spdif */
+ sdma_script_addr->mxc_sdma_spdif_2_mcu_addr = -1;
+ sdma_script_addr->mxc_sdma_mcu_2_spdif_addr = mcu_2_spdif_ADDR;
+
+ /* IPU */
+ sdma_script_addr->mxc_sdma_ext_mem_2_ipu_addr = ext_mem__ipu_ram_ADDR;
+
+ /* DVFS */
+ sdma_script_addr->mxc_sdma_dptc_dvfs_addr = -1;
+
+ /* core */
+ sdma_script_addr->mxc_sdma_start_addr = (unsigned short *)sdma_code;
+ sdma_script_addr->mxc_sdma_ram_code_start_addr = RAM_CODE_START_ADDR;
+ sdma_script_addr->mxc_sdma_ram_code_size = RAM_CODE_SIZE;
+}
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+#if defined(CONFIG_W1_MASTER_MXC) || defined(CONFIG_W1_MASTER_MXC_MODULE)
+static struct resource w1_resources[] = {
+ {
+ .start = MXC_INT_OWIRE,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct mxc_w1_config mxc_w1_data = {
+ .search_rom_accelerator = 1,
+};
+
+static struct platform_device mxc_w1_devices = {
+ .name = "mxc_w1",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_w1_data,
+ },
+ .num_resources = ARRAY_SIZE(w1_resources),
+ .resource = w1_resources,
+ .id = 0
+};
+
+static void mxc_init_owire(void)
+{
+ (void)platform_device_register(&mxc_w1_devices);
+}
+#else
+static inline void mxc_init_owire(void)
+{
+}
+#endif
+
+#if defined(CONFIG_RTC_DRV_MXC_V2) || defined(CONFIG_RTC_DRV_MXC_V2_MODULE)
+static struct mxc_srtc_platform_data srtc_data = {
+ .srtc_sec_mode_addr = 0x83F98840,
+};
+
+static struct resource rtc_resources[] = {
+ {
+ .start = SRTC_BASE_ADDR,
+ .end = SRTC_BASE_ADDR + 0x40,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_SRTC_NTZ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device mxc_rtc_device = {
+ .name = "mxc_rtc",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &srtc_data,
+ },
+ .num_resources = ARRAY_SIZE(rtc_resources),
+ .resource = rtc_resources,
+};
+static void mxc_init_rtc(void)
+{
+ (void)platform_device_register(&mxc_rtc_device);
+}
+#else
+static inline void mxc_init_rtc(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_WATCHDOG) || defined(CONFIG_MXC_WATCHDOG_MODULE)
+
+static struct resource wdt_resources[] = {
+ {
+ .start = WDOG1_BASE_ADDR,
+ .end = WDOG1_BASE_ADDR + 0x30,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_wdt_device = {
+ .name = "mxc_wdt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(wdt_resources),
+ .resource = wdt_resources,
+};
+
+static void mxc_init_wdt(void)
+{
+ (void)platform_device_register(&mxc_wdt_device);
+}
+#else
+static inline void mxc_init_wdt(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_PWM)
+static struct resource pwm_resources[] = {
+ {
+ .start = PWM1_BASE_ADDR,
+ .end = PWM1_BASE_ADDR + 0x14,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_pwm_device = {
+ .name = "mxc_pwm",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(pwm_resources),
+ .resource = pwm_resources,
+};
+
+static void mxc_init_pwm(void)
+{
+ printk(KERN_INFO "mxc_pwm_device registered\n");
+ if (platform_device_register(&mxc_pwm_device) < 0)
+ printk(KERN_ERR "registration of mxc_pwm device failed\n");
+}
+#else
+static void mxc_init_pwm(void)
+{
+
+}
+#endif
+
+#if defined(CONFIG_BACKLIGHT_PWM)
+static struct platform_pwm_backlight_data mxc_pwm_backlight_data = {
+ .pwm_id = 0,
+ .max_brightness = 255,
+ .dft_brightness = 128,
+ .pwm_period_ns = 78770,
+};
+
+static struct platform_device mxc_pwm_backlight_device = {
+ .name = "pwm-backlight",
+ .id = -1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_pwm_backlight_data,
+ },
+};
+
+static void mxc_init_pwm_backlight(void)
+{
+ printk(KERN_INFO "pwm-backlight device registered\n");
+ if (platform_device_register(&mxc_pwm_backlight_device) < 0)
+ printk(KERN_ERR
+ "registration of pwm-backlight device failed\n");
+}
+#else
+static void mxc_init_pwm_backlight(void)
+{
+
+}
+#endif
+
+#if defined(CONFIG_MXC_IPU_V3) || defined(CONFIG_MXC_IPU_V3_MODULE)
+static struct mxc_ipu_config mxc_ipu_data = {
+ .rev = 1,
+};
+
+static struct resource ipu_resources[] = {
+ {
+ .start = IPU_CTRL_BASE_ADDR,
+ .end = IPU_CTRL_BASE_ADDR + SZ_512M,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_IPU_SYN,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MXC_INT_IPU_ERR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mxc_ipu_device = {
+ .name = "mxc_ipu",
+ .id = -1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_ipu_data,
+ },
+ .num_resources = ARRAY_SIZE(ipu_resources),
+ .resource = ipu_resources,
+};
+
+static void mxc_init_ipu(void)
+{
+ u32 reg_hsc_mcd = IO_ADDRESS(MIPI_HSC_BASE_ADDR);
+ u32 reg_hsc_mxt_conf = IO_ADDRESS(MIPI_HSC_BASE_ADDR + 0x800);
+ struct clk *clk;
+ uint32_t temp;
+
+ /* Select IPUv3 h/w version */
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) > 0)
+ mxc_ipu_data.rev = 2;
+
+ mxc_ipu_data.di_clk[1] = clk_get(NULL, "ipu_di1_clk");
+ clk = clk_get(NULL, "tve_clk");
+ clk_set_parent(mxc_ipu_data.di_clk[1], clk);
+ clk_put(clk);
+
+ /* Temporarily setup MIPI module to legacy mode */
+ clk = clk_get(NULL, "mipi_hsp_clk");
+ if (!IS_ERR(clk)) {
+ clk_enable(clk);
+
+ /* Temporarily setup MIPI module to legacy mode */
+ __raw_writel(0xF00, reg_hsc_mcd);
+
+ /* CSI mode reserved*/
+ temp = __raw_readl(reg_hsc_mxt_conf);
+ __raw_writel(temp | 0x0FF, reg_hsc_mxt_conf);
+
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) > 0) {
+ temp = __raw_readl(reg_hsc_mxt_conf);
+ __raw_writel(temp | 0x10000, reg_hsc_mxt_conf);
+ }
+
+ clk_disable(clk);
+ clk_put(clk);
+ }
+ platform_device_register(&mxc_ipu_device);
+}
+#else
+static inline void mxc_init_ipu(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MXC_VPU) || defined(CONFIG_MXC_VPU_MODULE)
+static struct resource vpu_resources[] = {
+ [0] = {
+ .start = VPU_IRAM_BASE_ADDR,
+ .end = VPU_IRAM_BASE_ADDR + VPU_IRAM_SIZE,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IO_ADDRESS(SRC_BASE_ADDR),
+ .end = IO_ADDRESS(SRC_BASE_ADDR),
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+/*! Platform Data for MXC VPU */
+static struct platform_device mxcvpu_device = {
+ .name = "mxc_vpu",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(vpu_resources),
+ .resource = vpu_resources,
+};
+
+static inline void mxc_init_vpu(void)
+{
+ if (platform_device_register(&mxcvpu_device) < 0)
+ printk(KERN_ERR "Error: Registering the VPU.\n");
+}
+#else
+static inline void mxc_init_vpu(void)
+{
+}
+#endif
+
+/*!
+ * This is platform device structure for adding SCC
+ */
+#if defined(CONFIG_MXC_SECURITY_SCC) || defined(CONFIG_MXC_SECURITY_SCC_MODULE)
+static struct platform_device mxc_scc_device = {
+ .name = "mxc_scc",
+ .id = 0,
+};
+static void mxc_init_scc(void)
+{
+ platform_device_register(&mxc_scc_device);
+}
+#else
+#define SCM_RD_DELAY 1000000 /* in nanoseconds */
+#define SEC_TO_NANOSEC 1000000000 /*Second to nanoseconds */
+static inline void mxc_init_scc(void)
+{
+ uint32_t reg_value;
+ uint32_t reg_mask = 0;
+ uint8_t *UMID_base;
+ uint32_t *MAP_base;
+ uint8_t i;
+ uint32_t partition_no;
+ uint32_t scc_partno;
+ void *scm_ram_base;
+ void *scc_base;
+ uint8_t iram_partitions = 16;
+ struct timespec stime;
+ struct timespec curtime;
+ long scm_rd_timeout = 0;
+ long cur_ns = 0;
+ long start_ns = 0;
+
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) < 0)
+ iram_partitions = 12;
+
+ scc_base = ioremap((uint32_t) SCC_BASE_ADDR, 0x140);
+ if (scc_base == NULL) {
+ printk(KERN_ERR "FAILED TO MAP IRAM REGS\n");
+ return;
+ }
+ scm_ram_base = ioremap((uint32_t) IRAM_BASE_ADDR, IRAM_SIZE);
+ if (scm_ram_base == NULL) {
+ printk(KERN_ERR "FAILED TO MAP IRAM\n");
+ return;
+ }
+
+ /* Wait for any running SCC operations to finish or fail */
+ getnstimeofday(&stime);
+ do {
+ reg_value = __raw_readl(scc_base + SCM_STATUS_REG);
+ getnstimeofday(&curtime);
+ if (curtime.tv_nsec > stime.tv_nsec)
+ scm_rd_timeout = curtime.tv_nsec - stime.tv_nsec;
+ else{
+ /*Converted second to nanosecond and add to
+ nsec when current nanosec is less than
+ start time nanosec.*/
+ cur_ns = (curtime.tv_sec * SEC_TO_NANOSEC) +
+ curtime.tv_nsec;
+ start_ns = (stime.tv_sec * SEC_TO_NANOSEC) +
+ stime.tv_nsec;
+ scm_rd_timeout = cur_ns - start_ns;
+ }
+ } while (((reg_value & SCM_STATUS_SRS_MASK) != SCM_STATUS_SRS_READY)
+ && ((reg_value & SCM_STATUS_SRS_MASK) != SCM_STATUS_SRS_FAIL));
+
+ /* Check for failures */
+ if ((reg_value & SCM_STATUS_SRS_MASK) != SCM_STATUS_SRS_READY) {
+ /* Special message for bad secret key fuses */
+ if (reg_value & SCM_STATUS_KST_BAD_KEY)
+ printk(KERN_ERR "INVALID SCC KEY FUSE PATTERN\n");
+ else
+ printk(KERN_ERR "SECURE RAM FAILURE\n");
+
+ iounmap(scm_ram_base);
+ iounmap(scc_base);
+ return;
+ }
+
+ scm_rd_timeout = 0;
+ /* Release final two partitions for SCC2 driver */
+ scc_partno = iram_partitions - (SCC_IRAM_SIZE / SZ_8K);
+ for (partition_no = scc_partno; partition_no < iram_partitions;
+ partition_no++) {
+ reg_value = (((partition_no << SCM_ZCMD_PART_SHIFT) &
+ SCM_ZCMD_PART_MASK) | ((0x03 << SCM_ZCMD_CCMD_SHIFT) &
+ SCM_ZCMD_CCMD_MASK));
+ __raw_writel(reg_value, scc_base + SCM_ZCMD_REG);
+ udelay(1);
+ /* Wait for zeroization to complete */
+ getnstimeofday(&stime);
+ do {
+ reg_value = __raw_readl(scc_base + SCM_STATUS_REG);
+ getnstimeofday(&curtime);
+ if (curtime.tv_nsec > stime.tv_nsec)
+ scm_rd_timeout = curtime.tv_nsec -
+ stime.tv_nsec;
+ else {
+ /*Converted second to nanosecond and add to
+ nsec when current nanosec is less than
+ start time nanosec.*/
+ cur_ns = (curtime.tv_sec * SEC_TO_NANOSEC) +
+ curtime.tv_nsec;
+ start_ns = (stime.tv_sec * SEC_TO_NANOSEC) +
+ stime.tv_nsec;
+ scm_rd_timeout = cur_ns - start_ns;
+ }
+ } while (((reg_value & SCM_STATUS_SRS_MASK) !=
+ SCM_STATUS_SRS_READY) && ((reg_value & SCM_STATUS_SRS_MASK) !=
+ SCM_STATUS_SRS_FAIL) && (scm_rd_timeout <= SCM_RD_DELAY));
+
+ if (scm_rd_timeout > SCM_RD_DELAY)
+ printk(KERN_ERR "SCM Status Register Read timeout"
+ "for Partition No:%d", partition_no);
+
+ if ((reg_value & SCM_STATUS_SRS_MASK) != SCM_STATUS_SRS_READY)
+ break;
+ }
+
+ /*Check all expected partitions released */
+ reg_value = __raw_readl(scc_base + SCM_PART_OWNERS_REG);
+ if ((reg_value & reg_mask) != 0) {
+ printk(KERN_ERR "FAILED TO RELEASE IRAM PARTITION\n");
+ iounmap(scm_ram_base);
+ iounmap(scc_base);
+ return;
+ }
+ reg_mask = 0;
+ scm_rd_timeout = 0;
+ /* Allocate remaining partitions for general use */
+ for (partition_no = 0; partition_no < scc_partno; partition_no++) {
+ /* Supervisor mode claims a partition for it's own use
+ by writing zero to SMID register.*/
+ __raw_writel(0, scc_base + (SCM_SMID0_REG + 8 * partition_no));
+
+ /* Wait for any zeroization to complete */
+ getnstimeofday(&stime);
+ do {
+ reg_value = __raw_readl(scc_base + SCM_STATUS_REG);
+ getnstimeofday(&curtime);
+ if (curtime.tv_nsec > stime.tv_nsec)
+ scm_rd_timeout = curtime.tv_nsec -
+ stime.tv_nsec;
+ else{
+ /*Converted second to nanosecond and add to
+ nsec when current nanosec is less than
+ start time nanosec.*/
+ cur_ns = (curtime.tv_sec * SEC_TO_NANOSEC) +
+ curtime.tv_nsec;
+ start_ns = (stime.tv_sec * SEC_TO_NANOSEC) +
+ stime.tv_nsec;
+ scm_rd_timeout = cur_ns - start_ns;
+ }
+ } while (((reg_value & SCM_STATUS_SRS_MASK) !=
+ SCM_STATUS_SRS_READY) && ((reg_value & SCM_STATUS_SRS_MASK) !=
+ SCM_STATUS_SRS_FAIL) && (scm_rd_timeout <= SCM_RD_DELAY));
+
+ if (scm_rd_timeout > SCM_RD_DELAY)
+ printk(KERN_ERR "SCM Status Register Read timeout"
+ "for Partition No:%d", partition_no);
+
+ if ((reg_value & SCM_STATUS_SRS_MASK) != SCM_STATUS_SRS_READY)
+ break;
+ /* Set UMID=0 and permissions for universal data
+ read/write access */
+ MAP_base = scm_ram_base + (partition_no * 0x2000);
+ UMID_base = (uint8_t *) MAP_base + 0x10;
+ for (i = 0; i < 16; i++)
+ UMID_base[i] = 0;
+
+ MAP_base[0] = (SCM_PERM_NO_ZEROIZE | SCM_PERM_HD_SUP_DISABLE |
+ SCM_PERM_HD_READ | SCM_PERM_HD_WRITE |
+ SCM_PERM_HD_EXECUTE | SCM_PERM_TH_READ |
+ SCM_PERM_TH_WRITE);
+ reg_mask |= (3 << (2 * (partition_no)));
+ }
+
+ /* Check all expected partitions allocated */
+ reg_value = __raw_readl(scc_base + SCM_PART_OWNERS_REG);
+ if ((reg_value & reg_mask) != reg_mask) {
+ printk(KERN_ERR "FAILED TO ACQUIRE IRAM PARTITION\n");
+ iounmap(scm_ram_base);
+ iounmap(scc_base);
+ return;
+ }
+
+ iounmap(scm_ram_base);
+ iounmap(scc_base);
+ printk(KERN_INFO "IRAM READY\n");
+ iram_ready = 1;
+}
+#endif
+
+/* SPI controller and device data */
+#if defined(CONFIG_SPI_MXC) || defined(CONFIG_SPI_MXC_MODULE)
+
+#ifdef CONFIG_SPI_MXC_SELECT1
+/*!
+ * Resource definition for the CSPI1
+ */
+static struct resource mxcspi1_resources[] = {
+ [0] = {
+ .start = CSPI1_BASE_ADDR,
+ .end = CSPI1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI1,
+ .end = MXC_INT_CSPI1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+extern void mx51_babbage_gpio_spi_chipselect_active(int cspi_mode, int status,
+ int chipselect);
+extern void mx51_babbage_gpio_spi_chipselect_inactive(int cspi_mode, int status,
+ int chipselect);
+/*! Platform Data for MXC CSPI1 */
+static struct mxc_spi_master mxcspi1_data = {
+ .maxchipselect = 4,
+ .spi_version = 23,
+};
+
+/*! Device Definition for MXC CSPI1 */
+static struct platform_device mxcspi1_device = {
+ .name = "mxc_spi",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi1_resources),
+ .resource = mxcspi1_resources,
+};
+
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+
+#ifdef CONFIG_SPI_MXC_SELECT2
+/*!
+ * Resource definition for the CSPI2
+ */
+static struct resource mxcspi2_resources[] = {
+ [0] = {
+ .start = CSPI2_BASE_ADDR,
+ .end = CSPI2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI2,
+ .end = MXC_INT_CSPI2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI2 */
+static struct mxc_spi_master mxcspi2_data = {
+ .maxchipselect = 4,
+ .spi_version = 23,
+};
+
+/*! Device Definition for MXC CSPI2 */
+static struct platform_device mxcspi2_device = {
+ .name = "mxc_spi",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi2_resources),
+ .resource = mxcspi2_resources,
+};
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+
+#ifdef CONFIG_SPI_MXC_SELECT3
+/*!
+ * Resource definition for the CSPI3
+ */
+static struct resource mxcspi3_resources[] = {
+ [0] = {
+ .start = CSPI3_BASE_ADDR,
+ .end = CSPI3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_CSPI,
+ .end = MXC_INT_CSPI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC CSPI3 */
+static struct mxc_spi_master mxcspi3_data = {
+ .maxchipselect = 4,
+ .spi_version = 7,
+};
+
+/*! Device Definition for MXC CSPI3 */
+static struct platform_device mxcspi3_device = {
+ .name = "mxc_spi",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxcspi3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcspi3_resources),
+ .resource = mxcspi3_resources,
+};
+#endif /* CONFIG_SPI_MXC_SELECT3 */
+
+void __init mxc_init_spi(void)
+{
+ /* SPBA configuration for CSPI2 - MCU is set */
+ spba_take_ownership(SPBA_CSPI1, SPBA_MASTER_A);
+#ifdef CONFIG_SPI_MXC_SELECT1
+ if (machine_is_mx51_babbage()) {
+ mxcspi1_data.chipselect_active =
+ mx51_babbage_gpio_spi_chipselect_active;
+ mxcspi1_data.chipselect_inactive =
+ mx51_babbage_gpio_spi_chipselect_inactive;
+ }
+ if (platform_device_register(&mxcspi1_device) < 0)
+ printk(KERN_ERR "Error: Registering the SPI Controller_1\n");
+#endif /* CONFIG_SPI_MXC_SELECT1 */
+#ifdef CONFIG_SPI_MXC_SELECT2
+ if (platform_device_register(&mxcspi2_device) < 0)
+ printk(KERN_ERR "Error: Registering the SPI Controller_2\n");
+#endif /* CONFIG_SPI_MXC_SELECT2 */
+#ifdef CONFIG_SPI_MXC_SELECT3
+ if (platform_device_register(&mxcspi3_device) < 0)
+ printk(KERN_ERR "Error: Registering the SPI Controller_3\n");
+#endif /* CONFIG_SPI_MXC_SELECT3 */
+}
+#else
+void __init mxc_init_spi(void)
+{
+}
+#endif
+
+/* I2C controller and device data */
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+/*!
+ * Resource definition for the I2C1
+ */
+static struct resource mxci2c1_resources[] = {
+ [0] = {
+ .start = I2C1_BASE_ADDR,
+ .end = I2C1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C1,
+ .end = MXC_INT_I2C1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c1_data = {
+ .i2c_clk = 400000,
+};
+#endif
+
+#ifdef CONFIG_I2C_MXC_SELECT2
+/*!
+ * Resource definition for the I2C2
+ */
+static struct resource mxci2c2_resources[] = {
+ [0] = {
+ .start = I2C2_BASE_ADDR,
+ .end = I2C2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C2,
+ .end = MXC_INT_I2C2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c2_data = {
+ .i2c_clk = 400000,
+};
+#endif
+
+#ifdef CONFIG_I2C_MXC_SELECT3
+/*!
+ * Resource definition for the I2C3
+ */
+static struct resource mxci2c3_resources[] = {
+ [0] = {
+ .start = I2C3_BASE_ADDR,
+ .end = I2C3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_I2C3,
+ .end = MXC_INT_I2C3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c3_data = {
+ .i2c_clk = 100000,
+};
+#endif
+
+/*! Device Definition for MXC I2C1 */
+static struct platform_device mxci2c_devices[] = {
+#ifdef CONFIG_I2C_MXC_SELECT1
+ {
+ .name = "mxc_i2c",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c1_resources),
+ .resource = mxci2c1_resources,},
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT2
+ {
+ .name = "mxc_i2c",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c2_resources),
+ .resource = mxci2c2_resources,},
+#endif
+// Note: AG: The CCWMX51 has not got a third standard I2C port.
+#ifdef CONFIG_I2C_MXC_SELECT3
+ {
+ .name = "mxc_i2c",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c3_resources),
+ .resource = mxci2c3_resources,},
+#endif
+};
+
+static inline void mxc_init_i2c(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxci2c_devices); i++) {
+ if (platform_device_register(&mxci2c_devices[i]) < 0)
+ dev_err(&mxci2c_devices[i].dev,
+ "Unable to register I2C device\n");
+ }
+}
+#else
+static inline void mxc_init_i2c(void)
+{
+}
+#endif
+
+#if defined(CONFIG_I2C_MXC_HS) || defined(CONFIG_I2C_MXC_HS_MODULE)
+static struct resource mxci2c_hs_resources[] = {
+ [0] = {
+ .start = HSI2C_DMA_BASE_ADDR,
+ .end = HSI2C_DMA_BASE_ADDR + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_HS_I2C,
+ .end = MXC_INT_HS_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for MXC I2C */
+static struct mxc_i2c_platform_data mxci2c_hs_data = {
+ .i2c_clk = 400000,
+};
+
+static struct platform_device mxci2c_hs_device = {
+ .name = "mxc_i2c_hs",
+ .id = 3,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxci2c_hs_data,
+ },
+ .num_resources = ARRAY_SIZE(mxci2c_hs_resources),
+ .resource = mxci2c_hs_resources
+};
+
+static inline void mxc_init_i2c_hs(void)
+{
+ if (platform_device_register(&mxci2c_hs_device) < 0)
+ dev_err(&mxci2c_hs_device.dev,
+ "Unable to register High Speed I2C device\n");
+}
+#else
+static inline void mxc_init_i2c_hs(void)
+{
+}
+#endif
+
+#if defined(CONFIG_FB_MXC_TVOUT_TVE) || defined(CONFIG_FB_MXC_TVOUT_TVE_MODULE)
+static struct resource tve_resources[] = {
+ {
+ .start = TVE_BASE_ADDR,
+ .end = TVE_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MXC_INT_TVE,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct tve_platform_data tve_data = {
+ .dac_reg = "VVIDEO",
+ .dig_reg = "VDIG",
+};
+
+static struct platform_device mxc_tve_device = {
+ .name = "tve",
+ .dev = {
+ .platform_data = &tve_data,
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(tve_resources),
+ .resource = tve_resources,
+};
+
+void __init mxc_init_tve(void)
+{
+ platform_device_register(&mxc_tve_device);
+}
+#else
+static inline void mxc_init_tve(void)
+{
+}
+#endif
+
+/*!
+ * Resource definition for the DVFS CORE
+ */
+static struct resource dvfs_core_resources[] = {
+ [0] = {
+ .start = MXC_DVFS_CORE_BASE,
+ .end = MXC_DVFS_CORE_BASE + 4 * SZ_16 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_GPC1,
+ .end = MXC_INT_GPC1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Platform Data for DVFS CORE */
+struct mxc_dvfs_platform_data dvfs_core_data = {
+ .reg_id = "SW1",
+ .clk1_id = "cpu_clk",
+ .clk2_id = "gpc_dvfs_clk",
+ .gpc_cntr_reg_addr = MXC_GPC_CNTR,
+ .gpc_vcr_reg_addr = MXC_GPC_VCR,
+ .ccm_cdcr_reg_addr = (unsigned int)MXC_CCM_CDCR,
+ .ccm_cacrr_reg_addr = (unsigned int)MXC_CCM_CACRR,
+ .ccm_cdhipr_reg_addr = (unsigned int)MXC_CCM_CDHIPR,
+ .dvfs_thrs_reg_addr = MXC_DVFSTHRS,
+ .dvfs_coun_reg_addr = MXC_DVFSCOUN,
+ .dvfs_emac_reg_addr = MXC_DVFSEMAC,
+ .dvfs_cntr_reg_addr = MXC_DVFSCNTR,
+ .prediv_mask = 0x1F800,
+ .prediv_offset = 11,
+ .prediv_val = 3,
+ .div3ck_mask = 0xE0000000,
+ .div3ck_offset = 29,
+ .div3ck_val = 2,
+ .emac_val = 0x08,
+ .upthr_val = 25,
+ .dnthr_val = 9,
+ .pncthr_val = 33,
+ .upcnt_val = 10,
+ .dncnt_val = 10,
+ .delay_time = 30,
+ .num_wp = 3,
+};
+
+/*! Device Definition for MXC DVFS core */
+static struct platform_device mxc_dvfs_core_device = {
+ .name = "mxc_dvfs_core",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &dvfs_core_data,
+ },
+ .num_resources = ARRAY_SIZE(dvfs_core_resources),
+ .resource = dvfs_core_resources,
+};
+
+static inline void mxc_init_dvfs(void)
+{
+ if (platform_device_register(&mxc_dvfs_core_device) < 0)
+ dev_err(&mxc_dvfs_core_device.dev,
+ "Unable to register DVFS core device\n");
+}
+
+struct mxc_gpio_port mxc_gpio_ports[GPIO_PORT_NUM] = {
+ [0] = {
+ .chip.label = "gpio-0",
+ .base = IO_ADDRESS(GPIO1_BASE_ADDR),
+ .irq = MXC_INT_GPIO1_LOW,
+ .irq_high = MXC_INT_GPIO1_HIGH,
+ .virtual_irq_start = MXC_GPIO_INT_BASE
+ },
+ [1] = {
+ .chip.label = "gpio-1",
+ .base = IO_ADDRESS(GPIO2_BASE_ADDR),
+ .irq = MXC_INT_GPIO2_LOW,
+ .irq_high = MXC_INT_GPIO2_HIGH,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 1
+ },
+ [2] = {
+ .chip.label = "gpio-2",
+ .base = IO_ADDRESS(GPIO3_BASE_ADDR),
+ .irq = MXC_INT_GPIO3_LOW,
+ .irq_high = MXC_INT_GPIO3_HIGH,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 2
+ },
+ [3] = {
+ .chip.label = "gpio-3",
+ .base = IO_ADDRESS(GPIO4_BASE_ADDR),
+ .irq = MXC_INT_GPIO4_LOW,
+ .irq_high = MXC_INT_GPIO4_HIGH,
+ .virtual_irq_start = MXC_GPIO_INT_BASE + GPIO_NUM_PIN * 3
+ }
+};
+
+int __init mxc_register_gpios(void)
+{
+ return mxc_gpio_init(mxc_gpio_ports, ARRAY_SIZE(mxc_gpio_ports));
+}
+
+static struct platform_device mxc_dma_device = {
+ .name = "mxc_dma",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline void mxc_init_dma(void)
+{
+ (void)platform_device_register(&mxc_dma_device);
+}
+
+static struct resource spdif_resources[] = {
+ {
+ .start = SPDIF_BASE_ADDR,
+ .end = SPDIF_BASE_ADDR + 0x50,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct mxc_spdif_platform_data mxc_spdif_data = {
+ .spdif_tx = 1,
+ .spdif_rx = 0,
+ .spdif_clk_44100 = 0, /* spdif_ext_clk source for 44.1KHz */
+ .spdif_clk_48000 = 7, /* audio osc source */
+ .spdif_clkid = 0,
+ .spdif_clk = NULL, /* spdif bus clk */
+};
+
+static struct platform_device mxc_alsa_spdif_device = {
+ .name = "mxc_alsa_spdif",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_spdif_data,
+ },
+ .num_resources = ARRAY_SIZE(spdif_resources),
+ .resource = spdif_resources,
+};
+
+static inline void mxc_init_spdif(void)
+{
+ mxc_spdif_data.spdif_core_clk = clk_get(NULL, "spdif_xtal_clk");
+ clk_put(mxc_spdif_data.spdif_core_clk);
+ platform_device_register(&mxc_alsa_spdif_device);
+}
+
+static struct platform_device mx51_lpmode_device = {
+ .name = "mx51_lpmode",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline void mx51_init_lpmode(void)
+{
+ (void)platform_device_register(&mx51_lpmode_device);
+}
+
+static struct platform_device busfreq_device = {
+ .name = "busfreq",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline void mxc_init_busfreq(void)
+{
+ (void)platform_device_register(&busfreq_device);
+}
+
+static struct platform_device sdram_autogating_device = {
+ .name = "sdram_autogating",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+};
+
+static inline void mxc_init_sdram_autogating(void)
+{
+ (void)platform_device_register(&sdram_autogating_device);
+}
+
+#if defined(CONFIG_MXC_IIM) || defined(CONFIG_MXC_IIM_MODULE)
+static struct resource mxc_iim_resources[] = {
+ {
+ .start = IIM_BASE_ADDR,
+ .end = IIM_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mxc_iim_device = {
+ .name = "mxc_iim",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(mxc_iim_resources),
+ .resource = mxc_iim_resources
+};
+
+static inline void mxc_init_iim(void)
+{
+ if (platform_device_register(&mxc_iim_device) < 0)
+ dev_err(&mxc_iim_device.dev,
+ "Unable to register mxc iim device\n");
+}
+#else
+static inline void mxc_init_iim(void)
+{
+}
+#endif
+
+static struct resource mxc_gpu_resources[] = {
+ [0] = {
+ .start = MXC_INT_GPU2_IRQ,
+ .end = MXC_INT_GPU2_IRQ,
+ .name = "gpu_2d_irq",
+ .flags = IORESOURCE_IRQ,},
+ [1] = {
+ .start = MXC_INT_GPU,
+ .end = MXC_INT_GPU,
+ .name = "gpu_3d_irq",
+ .flags = IORESOURCE_IRQ,},
+};
+
+static struct platform_device gpu_device = {
+ .name = "mxc_gpu",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(mxc_gpu_resources),
+ .resource = mxc_gpu_resources,
+};
+
+static void __init mxc_init_gpu(void)
+{
+ platform_device_register(&gpu_device);
+}
+
+static struct resource mxc_gpu2d_resources[] = {
+ {
+ .start = GPU2D_BASE_ADDR,
+ .end = GPU2D_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+#if defined(CONFIG_UIO_PDRV_GENIRQ) || defined(CONFIG_UIO_PDRV_GENIRQ_MODULE)
+static struct clk *gpu_clk;
+
+int gpu2d_open(struct uio_info *info, struct inode *inode)
+{
+ gpu_clk = clk_get(NULL, "gpu2d_clk");
+ if (IS_ERR(gpu_clk))
+ return PTR_ERR(gpu_clk);
+
+ return clk_enable(gpu_clk);
+}
+
+int gpu2d_release(struct uio_info *info, struct inode *inode)
+{
+ if (IS_ERR(gpu_clk))
+ return PTR_ERR(gpu_clk);
+
+ clk_disable(gpu_clk);
+ clk_put(gpu_clk);
+ return 0;
+}
+
+static int gpu2d_mmap(struct uio_info *info, struct vm_area_struct *vma)
+{
+ int mi = vma->vm_pgoff;
+ if (mi < 0)
+ return -EINVAL;
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ return remap_pfn_range(vma,
+ vma->vm_start,
+ info->mem[mi].addr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+}
+
+static struct uio_info gpu2d_info = {
+ .name = "imx_gpu2d",
+ .version = "1",
+ .irq = MXC_INT_GPU2_IRQ,
+ .open = gpu2d_open,
+ .release = gpu2d_release,
+ .mmap = gpu2d_mmap,
+};
+
+static struct platform_device mxc_gpu2d_device = {
+ .name = "uio_pdrv_genirq",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &gpu2d_info,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+ .num_resources = ARRAY_SIZE(mxc_gpu2d_resources),
+ .resource = mxc_gpu2d_resources,
+};
+
+static inline void mxc_init_gpu2d(void)
+{
+ dma_alloc_coherent(&mxc_gpu2d_device.dev, SZ_8K, &mxc_gpu2d_resources[1].start, GFP_DMA);
+ mxc_gpu2d_resources[1].end = mxc_gpu2d_resources[1].start + SZ_8K - 1;
+
+ dma_alloc_coherent(&mxc_gpu2d_device.dev, 88 * SZ_1K, &mxc_gpu2d_resources[2].start, GFP_DMA);
+ mxc_gpu2d_resources[2].end = mxc_gpu2d_resources[2].start + (88 * SZ_1K) - 1;
+
+ platform_device_register(&mxc_gpu2d_device);
+}
+#else
+static inline void mxc_init_gpu2d(void)
+{
+}
+#endif
+
+int __init mxc_init_devices(void)
+{
+ mxc_init_wdt();
+ mxc_init_spi();
+ mxc_init_i2c();
+ mxc_init_i2c_hs();
+ mxc_init_rtc();
+ mxc_init_scc();
+ mxc_init_dma();
+ mxc_init_owire();
+ mxc_init_ipu();
+ mxc_init_vpu();
+ mxc_init_spdif();
+ mxc_init_tve();
+ mx51_init_lpmode();
+ mxc_init_busfreq();
+ mxc_init_sdram_autogating();
+ mxc_init_dvfs();
+ mxc_init_iim();
+ mxc_init_gpu();
+ mxc_init_gpu2d();
+ mxc_init_pwm();
+ mxc_init_pwm_backlight();
+ return 0;
+}
diff --git a/arch/arm/mach-mx51/dma.c b/arch/arm/mach-mx51/dma.c
new file mode 100644
index 000000000000..a27d7e2a2349
--- /dev/null
+++ b/arch/arm/mach-mx51/dma.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/device.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+
+#include "serial.h"
+
+#define MXC_MMC_BUFFER_ACCESS 0x20
+#define MXC_SDHC_MMC_WML 64
+#define MXC_SDHC_SD_WML 256
+#define MXC_SSI_TX0_REG 0x0
+#define MXC_SSI_TX1_REG 0x4
+#define MXC_SSI_RX0_REG 0x8
+#define MXC_SSI_RX1_REG 0xC
+#define MXC_SSI_TXFIFO_WML 0x4
+#define MXC_SSI_RXFIFO_WML 0x6
+#define MXC_SPDIF_TXFIFO_WML 0x8
+#define MXC_SPDIF_TX_REG 0x2C
+
+typedef struct mxc_sdma_info_entry_s {
+ mxc_dma_device_t device;
+ mxc_sdma_channel_params_t *chnl_info;
+} mxc_sdma_info_entry_t;
+
+static mxc_sdma_channel_params_t mxc_sdma_uart1_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART1_UFCR_RXTL,
+ .per_address = UART1_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART1_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART1_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart1_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART1_UFCR_TXTL,
+ .per_address = UART1_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART1_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART1_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart2_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART2_UFCR_RXTL,
+ .per_address = UART2_BASE_ADDR,
+ .peripheral_type = UART,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART2_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART2_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart2_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART2_UFCR_TXTL,
+ .per_address = UART2_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART2_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART2_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart3_rx_params = {
+ .chnl_params = {
+ .watermark_level = UART3_UFCR_RXTL,
+ .per_address = UART3_BASE_ADDR,
+ .peripheral_type = UART_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_UART3_RX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART3_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_uart3_tx_params = {
+ .chnl_params = {
+ .watermark_level = UART3_UFCR_TXTL,
+ .per_address = UART3_BASE_ADDR + MXC_UARTUTXD,
+ .peripheral_type = UART_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_UART3_TX,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_UART3_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc1_width1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_MMC_WML,
+ .per_address =
+ MMC_SDHC1_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC1,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc1_width4_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_SD_WML,
+ .per_address =
+ MMC_SDHC1_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC1,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc2_width1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_MMC_WML,
+ .per_address =
+ MMC_SDHC2_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC2,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_mmc2_width4_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SDHC_SD_WML,
+ .per_address =
+ MMC_SDHC2_BASE_ADDR + MXC_MMC_BUFFER_ACCESS,
+ .peripheral_type = MMC,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SDHC2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MMC2,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_8bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_16bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI1_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi1_24bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI1_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI1_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI1_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_rx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_tx0_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX0_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX1,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_8bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_8BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_16bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_rx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_RXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_RX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_SSI2_RX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_RX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ssi2_24bit_tx1_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SSI_TXFIFO_WML,
+ .per_address = SSI2_BASE_ADDR + MXC_SSI_TX1_REG,
+ .peripheral_type = SSI_SP,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SSI2_TX2,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SSI2_TX,
+ .chnl_priority = 2,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_memory_params = {
+ .chnl_params = {
+ .peripheral_type = MEMORY,
+ .transfer_type = emi_2_emi,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_MEMORY,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ata_rx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_IDE_DMA_WATERMARK,
+ .per_address = ATA_DMA_BASE_ADDR,
+ .peripheral_type = ATA,
+ .transfer_type = per_2_emi,
+ .event_id = DMA_REQ_ATA_TX_END,
+ .event_id2 = DMA_REQ_ATA_RX,
+ .bd_number = MXC_IDE_DMA_BD_NR,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ATA_RX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_ata_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_IDE_DMA_WATERMARK,
+ .per_address = ATA_DMA_BASE_ADDR + 0x18,
+ .peripheral_type = ATA,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_ATA_TX_END,
+ .event_id2 = DMA_REQ_ATA_TX,
+ .bd_number = MXC_IDE_DMA_BD_NR,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_ATA_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_spdif_16bit_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SPDIF_TXFIFO_WML,
+ .per_address = SPDIF_BASE_ADDR + MXC_SPDIF_TX_REG,
+ .peripheral_type = SPDIF,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SPDIF,
+ .bd_number = 32,
+ .word_size = TRANSFER_16BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SPDIF_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_channel_params_t mxc_sdma_spdif_32bit_tx_params = {
+ .chnl_params = {
+ .watermark_level = MXC_SPDIF_TXFIFO_WML,
+ .per_address = SPDIF_BASE_ADDR + MXC_SPDIF_TX_REG,
+ .peripheral_type = SPDIF,
+ .transfer_type = emi_2_per,
+ .event_id = DMA_REQ_SPDIF,
+ .bd_number = 32,
+ .word_size = TRANSFER_32BIT,
+ },
+ .channel_num = MXC_DMA_CHANNEL_SPDIF_TX,
+ .chnl_priority = MXC_SDMA_DEFAULT_PRIORITY,
+};
+
+static mxc_sdma_info_entry_t mxc_sdma_active_dma_info[] = {
+ {MXC_DMA_UART1_RX, &mxc_sdma_uart1_rx_params},
+ {MXC_DMA_UART1_TX, &mxc_sdma_uart1_tx_params},
+ {MXC_DMA_UART2_RX, &mxc_sdma_uart2_rx_params},
+ {MXC_DMA_UART2_TX, &mxc_sdma_uart2_tx_params},
+ {MXC_DMA_UART3_RX, &mxc_sdma_uart3_rx_params},
+ {MXC_DMA_UART3_TX, &mxc_sdma_uart3_tx_params},
+ {MXC_DMA_MMC1_WIDTH_1, &mxc_sdma_mmc1_width1_params},
+ {MXC_DMA_MMC1_WIDTH_4, &mxc_sdma_mmc1_width4_params},
+ {MXC_DMA_MMC2_WIDTH_1, &mxc_sdma_mmc2_width1_params},
+ {MXC_DMA_MMC2_WIDTH_4, &mxc_sdma_mmc2_width4_params},
+ {MXC_DMA_SSI1_8BIT_RX0, &mxc_sdma_ssi1_8bit_rx0_params},
+ {MXC_DMA_SSI1_8BIT_TX0, &mxc_sdma_ssi1_8bit_tx0_params},
+ {MXC_DMA_SSI1_16BIT_RX0, &mxc_sdma_ssi1_16bit_rx0_params},
+ {MXC_DMA_SSI1_16BIT_TX0, &mxc_sdma_ssi1_16bit_tx0_params},
+ {MXC_DMA_SSI1_24BIT_RX0, &mxc_sdma_ssi1_24bit_rx0_params},
+ {MXC_DMA_SSI1_24BIT_TX0, &mxc_sdma_ssi1_24bit_tx0_params},
+ {MXC_DMA_SSI1_8BIT_RX1, &mxc_sdma_ssi1_8bit_rx1_params},
+ {MXC_DMA_SSI1_8BIT_TX1, &mxc_sdma_ssi1_8bit_tx1_params},
+ {MXC_DMA_SSI1_16BIT_RX1, &mxc_sdma_ssi1_16bit_rx1_params},
+ {MXC_DMA_SSI1_16BIT_TX1, &mxc_sdma_ssi1_16bit_tx1_params},
+ {MXC_DMA_SSI1_24BIT_RX1, &mxc_sdma_ssi1_24bit_rx1_params},
+ {MXC_DMA_SSI1_24BIT_TX1, &mxc_sdma_ssi1_24bit_tx1_params},
+ {MXC_DMA_SSI2_8BIT_RX0, &mxc_sdma_ssi2_8bit_rx0_params},
+ {MXC_DMA_SSI2_8BIT_TX0, &mxc_sdma_ssi2_8bit_tx0_params},
+ {MXC_DMA_SSI2_16BIT_RX0, &mxc_sdma_ssi2_16bit_rx0_params},
+ {MXC_DMA_SSI2_16BIT_TX0, &mxc_sdma_ssi2_16bit_tx0_params},
+ {MXC_DMA_SSI2_24BIT_RX0, &mxc_sdma_ssi2_24bit_rx0_params},
+ {MXC_DMA_SSI2_24BIT_TX0, &mxc_sdma_ssi2_24bit_tx0_params},
+ {MXC_DMA_SSI2_8BIT_RX1, &mxc_sdma_ssi2_8bit_rx1_params},
+ {MXC_DMA_SSI2_8BIT_TX1, &mxc_sdma_ssi2_8bit_tx1_params},
+ {MXC_DMA_SSI2_16BIT_RX1, &mxc_sdma_ssi2_16bit_rx1_params},
+ {MXC_DMA_SSI2_16BIT_TX1, &mxc_sdma_ssi2_16bit_tx1_params},
+ {MXC_DMA_SSI2_24BIT_RX1, &mxc_sdma_ssi2_24bit_rx1_params},
+ {MXC_DMA_SSI2_24BIT_TX1, &mxc_sdma_ssi2_24bit_tx1_params},
+ {MXC_DMA_MEMORY, &mxc_sdma_memory_params},
+ {MXC_DMA_ATA_RX, &mxc_sdma_ata_rx_params},
+ {MXC_DMA_ATA_TX, &mxc_sdma_ata_tx_params},
+ {MXC_DMA_SPDIF_16BIT_TX, &mxc_sdma_spdif_16bit_tx_params},
+ {MXC_DMA_SPDIF_32BIT_TX, &mxc_sdma_spdif_32bit_tx_params},
+};
+
+static int mxc_sdma_info_entrys =
+ sizeof(mxc_sdma_active_dma_info) / sizeof(mxc_sdma_active_dma_info[0]);
+
+/*!
+ * This functions Returns the SDMA paramaters associated for a module
+ *
+ * @param channel_id the ID of the module requesting DMA
+ * @return returns the sdma parameters structure for the device
+ */
+mxc_sdma_channel_params_t *mxc_sdma_get_channel_params(mxc_dma_device_t
+ channel_id)
+{
+ mxc_sdma_info_entry_t *p = mxc_sdma_active_dma_info;
+ int i;
+
+ for (i = 0; i < mxc_sdma_info_entrys; i++, p++) {
+ if (p->device == channel_id)
+ return p->chnl_info;
+
+ }
+ return NULL;
+}
+
+/*!
+ * This functions marks the SDMA channels that are statically allocated
+ *
+ * @param chnl the channel array used to store channel information
+ */
+void mxc_get_static_channels(mxc_dma_channel_t *chnl)
+{
+#ifdef CONFIG_SDMA_IRAM
+ int i;
+ for (i = MXC_DMA_CHANNEL_IRAM; i < MAX_DMA_CHANNELS; i++)
+ chnl[i].dynamic = 0;
+#endif
+}
+
+EXPORT_SYMBOL(mxc_sdma_get_channel_params);
+EXPORT_SYMBOL(mxc_get_static_channels);
diff --git a/arch/arm/mach-mx51/dummy_gpio.c b/arch/arm/mach-mx51/dummy_gpio.c
new file mode 100644
index 000000000000..bf21862908ac
--- /dev/null
+++ b/arch/arm/mach-mx51/dummy_gpio.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+
+void gpio_gps_active(void) {}
+EXPORT_SYMBOL(gpio_gps_active);
+
+void gpio_gps_inactive(void) {}
+EXPORT_SYMBOL(gpio_gps_inactive);
+
+void config_uartdma_event(int port) {}
+EXPORT_SYMBOL(config_uartdma_event);
+
+void gpio_spi_active(int cspi_mod) {}
+EXPORT_SYMBOL(gpio_spi_active);
+
+void gpio_spi_inactive(int cspi_mod) {}
+EXPORT_SYMBOL(gpio_spi_inactive);
+
+void gpio_owire_active(void) {}
+EXPORT_SYMBOL(gpio_owire_active);
+
+void gpio_owire_inactive(void) {}
+EXPORT_SYMBOL(gpio_owire_inactive);
+
+void gpio_i2c_active(int i2c_num) {}
+EXPORT_SYMBOL(gpio_i2c_active);
+
+void gpio_i2c_inactive(int i2c_num) {}
+EXPORT_SYMBOL(gpio_i2c_inactive);
+
+void gpio_i2c_hs_active(void) {}
+EXPORT_SYMBOL(gpio_i2c_hs_active);
+
+void gpio_i2c_hs_inactive(void) {}
+EXPORT_SYMBOL(gpio_i2c_hs_inactive);
+
+void gpio_pmic_active(void) {}
+EXPORT_SYMBOL(gpio_pmic_active);
+
+void gpio_activate_audio_ports(void) {}
+EXPORT_SYMBOL(gpio_activate_audio_ports);
+
+void gpio_sdhc_active(int module) {}
+EXPORT_SYMBOL(gpio_sdhc_active);
+
+void gpio_sdhc_inactive(int module) {}
+EXPORT_SYMBOL(gpio_sdhc_inactive);
+
+void gpio_sensor_select(int sensor) {}
+
+void gpio_sensor_active(unsigned int csi) {}
+EXPORT_SYMBOL(gpio_sensor_active);
+
+void gpio_sensor_inactive(unsigned int csi) {}
+EXPORT_SYMBOL(gpio_sensor_inactive);
+
+void gpio_ata_active(void) {}
+EXPORT_SYMBOL(gpio_ata_active);
+
+void gpio_ata_inactive(void) {}
+EXPORT_SYMBOL(gpio_ata_inactive);
+
+void gpio_nand_active(void) {}
+EXPORT_SYMBOL(gpio_nand_active);
+
+void gpio_nand_inactive(void) {}
+EXPORT_SYMBOL(gpio_nand_inactive);
+
+void gpio_keypad_active(void) {}
+EXPORT_SYMBOL(gpio_keypad_active);
+
+void gpio_keypad_inactive(void) {}
+EXPORT_SYMBOL(gpio_keypad_inactive);
+
+int gpio_usbotg_hs_active(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(gpio_usbotg_hs_active);
+
+void gpio_usbotg_hs_inactive(void) {}
+EXPORT_SYMBOL(gpio_usbotg_hs_inactive);
+
+void gpio_fec_active(void) {}
+EXPORT_SYMBOL(gpio_fec_active);
+
+void gpio_fec_inactive(void) {}
+EXPORT_SYMBOL(gpio_fec_inactive);
+
+void gpio_spdif_active(void) {}
+EXPORT_SYMBOL(gpio_spdif_active);
+
+void gpio_spdif_inactive(void) {}
+EXPORT_SYMBOL(gpio_spdif_inactive);
+
diff --git a/arch/arm/mach-mx51/iomux.c b/arch/arm/mach-mx51/iomux.c
new file mode 100644
index 000000000000..7655ea4a04d4
--- /dev/null
+++ b/arch/arm/mach-mx51/iomux.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup GPIO_MX51 Board GPIO and Muxing Setup
+ * @ingroup MSL_MX51
+ */
+/*!
+ * @file mach-mx51/iomux.c
+ *
+ * @brief I/O Muxing control functions
+ *
+ * @ingroup GPIO_MX51
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include "iomux.h"
+
+/*!
+ * IOMUX register (base) addresses
+ */
+enum iomux_reg_addr {
+ IOMUXGPR0 = IO_ADDRESS(IOMUXC_BASE_ADDR),
+ IOMUXGPR1 = IO_ADDRESS(IOMUXC_BASE_ADDR) + 0x004,
+ IOMUXSW_MUX_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR),
+ IOMUXSW_MUX_END = IO_ADDRESS(IOMUXC_BASE_ADDR) + MUX_I_END,
+ IOMUXSW_PAD_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR) + PAD_I_START,
+ IOMUXSW_INPUT_CTL = IO_ADDRESS(IOMUXC_BASE_ADDR),
+};
+
+#define MUX_PIN_NUM_MAX ((MUX_I_END >> 2) + 1)
+
+static u8 iomux_pin_res_table[MUX_PIN_NUM_MAX];
+static DEFINE_SPINLOCK(gpio_mux_lock);
+
+static inline u32 _get_mux_reg(iomux_pin_name_t pin)
+{
+ u32 mux_reg = PIN_TO_IOMUX_MUX(pin);
+
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) < 0) {
+ if ((pin == MX51_PIN_NANDF_RB5) ||
+ (pin == MX51_PIN_NANDF_RB6) ||
+ (pin == MX51_PIN_NANDF_RB7))
+ ; /* Do nothing */
+ else if (mux_reg >= 0x2FC)
+ mux_reg += 8;
+ else if (mux_reg >= 0x130)
+ mux_reg += 0xC;
+ }
+ mux_reg += IOMUXSW_MUX_CTL;
+ return mux_reg;
+}
+
+static inline u32 _get_pad_reg(iomux_pin_name_t pin)
+{
+ u32 pad_reg = PIN_TO_IOMUX_PAD(pin);
+
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) < 0) {
+ if ((pin == MX51_PIN_NANDF_RB5) ||
+ (pin == MX51_PIN_NANDF_RB6) ||
+ (pin == MX51_PIN_NANDF_RB7))
+ ; /* Do nothing */
+ else if (pad_reg == 0x4D0 - PAD_I_START)
+ pad_reg += 0x4C;
+ else if (pad_reg == 0x860 - PAD_I_START)
+ pad_reg += 0x9C;
+ else if (pad_reg >= 0x804 - PAD_I_START)
+ pad_reg += 0xB0;
+ else if (pad_reg >= 0x7FC - PAD_I_START)
+ pad_reg += 0xB4;
+ else if (pad_reg >= 0x4E4 - PAD_I_START)
+ pad_reg += 0xCC;
+ else
+ pad_reg += 8;
+ }
+ pad_reg += IOMUXSW_PAD_CTL;
+ return pad_reg;
+}
+
+static inline u32 _get_mux_end(void)
+{
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) < 0)
+ return(IO_ADDRESS(IOMUXC_BASE_ADDR) + (0x3F8 - 4));
+ else
+ return(IO_ADDRESS(IOMUXC_BASE_ADDR) + (0x3F0 - 4));
+}
+
+/*!
+ * This function is used to configure a pin through the IOMUX module.
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config a configuration as defined in \b #iomux_pin_cfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+static int iomux_config_mux(iomux_pin_name_t pin, iomux_pin_cfg_t config)
+{
+ u32 ret = 0;
+ u32 pin_index = PIN_TO_IOMUX_INDEX(pin);
+ u32 mux_reg = _get_mux_reg(pin);
+ u32 mux_data = 0;
+ u8 *rp;
+
+ BUG_ON((mux_reg > _get_mux_end()) || (mux_reg < IOMUXSW_MUX_CTL));
+ spin_lock(&gpio_mux_lock);
+
+ if (config == IOMUX_CONFIG_GPIO)
+ mux_data = PIN_TO_ALT_GPIO(pin);
+ else
+ mux_data = config;
+
+ __raw_writel(mux_data, mux_reg);
+
+ /*
+ * Log a warning if a pin changes ownership
+ */
+ rp = iomux_pin_res_table + pin_index;
+ if ((mux_data & *rp) && (*rp != mux_data)) {
+ /*
+ * Don't call printk if we're tweaking the console uart or
+ * we'll deadlock.
+ */
+ printk(KERN_ERR "iomux_config_mux: Warning: iomux pin"
+ " config changed, pin=%d, "
+ " prev=0x%x new=0x%x\n", mux_reg, *rp, mux_data);
+ ret = -EINVAL;
+ }
+ *rp = mux_data;
+ spin_unlock(&gpio_mux_lock);
+ return ret;
+}
+
+/*!
+ * Request ownership for an IO pin. This function has to be the first one
+ * being called before that pin is used. The caller has to check the
+ * return value to make sure it returns 0.
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param config a configuration as defined in \b #iomux_pin_cfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t config)
+{
+ int ret = iomux_config_mux(pin, config);
+ int gpio_port = GPIO_TO_PORT(IOMUX_TO_GPIO(pin));
+
+ if (!ret && (gpio_port != NON_GPIO_PORT)
+ && ((config == IOMUX_CONFIG_GPIO)
+ || (config == PIN_TO_ALT_GPIO(pin))))
+ ret |= gpio_request(IOMUX_TO_GPIO(pin), NULL);
+
+ return ret;
+}
+EXPORT_SYMBOL(mxc_request_iomux);
+
+/*!
+ * Release ownership for an IO pin
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param config config as defined in \b #iomux_pin_ocfg_t
+ */
+void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t config)
+{
+ u32 pin_index = PIN_TO_IOMUX_INDEX(pin);
+ u8 *rp = iomux_pin_res_table + pin_index;
+ int gpio_port = GPIO_TO_PORT(IOMUX_TO_GPIO(pin));
+
+ *rp = 0;
+ if ((gpio_port != NON_GPIO_PORT)
+ && ((config == IOMUX_CONFIG_GPIO)
+ || (config == PIN_TO_ALT_GPIO(pin))))
+ gpio_free(IOMUX_TO_GPIO(pin));
+
+}
+EXPORT_SYMBOL(mxc_free_iomux);
+
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config the ORed value of elements defined in \b #iomux_pad_config_t
+ */
+void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config)
+{
+ u32 pad_reg = _get_pad_reg(pin);
+
+ BUG_ON(pad_reg < IOMUXSW_PAD_CTL);
+ __raw_writel(config, pad_reg);
+}
+EXPORT_SYMBOL(mxc_iomux_set_pad);
+
+unsigned int mxc_iomux_get_pad(iomux_pin_name_t pin)
+{
+ u32 pad_reg = _get_pad_reg(pin);
+
+ return __raw_readl(pad_reg);
+}
+EXPORT_SYMBOL(mxc_iomux_get_pad);
+
+/*!
+ * This function configures input path.
+ *
+ * @param input index of input select register as defined in \b #iomux_input_select_t
+ * @param config the binary value of elements defined in \b #iomux_input_config_t
+ * */
+void mxc_iomux_set_input(iomux_input_select_t input, u32 config)
+{
+ u32 reg;
+
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) < 0) {
+ if (input == MUX_IN_IPU_IPP_DI_0_IND_DISPB_SD_D_SELECT_INPUT)
+ input -= 4;
+ else if (input == MUX_IN_IPU_IPP_DI_1_IND_DISPB_SD_D_SELECT_INPUT)
+ input -= 3;
+ else if (input >= MUX_IN_KPP_IPP_IND_COL_6_SELECT_INPUT)
+ input -= 2;
+ else if (input >= MUX_IN_HSC_MIPI_MIX_PAR_SISG_TRIG_SELECT_INPUT)
+ input -= 5;
+ else if (input >= MUX_IN_HSC_MIPI_MIX_IPP_IND_SENS1_DATA_EN_SELECT_INPUT)
+ input -= 3;
+ else if (input >= MUX_IN_ECSPI2_IPP_IND_SS_B_3_SELECT_INPUT)
+ input -= 2;
+ else if (input >= MUX_IN_CCM_PLL1_BYPASS_CLK_SELECT_INPUT)
+ input -= 1;
+
+ reg = IOMUXSW_INPUT_CTL + (input << 2) + INPUT_CTL_START_TO1;
+ } else {
+ reg = IOMUXSW_INPUT_CTL + (input << 2) + INPUT_CTL_START;
+ }
+
+ BUG_ON(input >= MUX_INPUT_NUM_MUX);
+ __raw_writel(config, reg);
+}
+EXPORT_SYMBOL(mxc_iomux_set_input);
diff --git a/arch/arm/mach-mx51/iomux.h b/arch/arm/mach-mx51/iomux.h
new file mode 100644
index 000000000000..e314ab26ac9b
--- /dev/null
+++ b/arch/arm/mach-mx51/iomux.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MACH_MX51_IOMUX_H__
+#define __MACH_MX51_IOMUX_H__
+
+#include <linux/types.h>
+#include <mach/gpio.h>
+#include "mx51_pins.h"
+
+/*!
+ * @file mach-mx51/iomux.h
+ *
+ * @brief I/O Muxing control definitions and functions
+ *
+ * @ingroup GPIO_MX51
+ */
+
+typedef unsigned int iomux_pin_name_t;
+
+/*!
+ * various IOMUX output functions
+ */
+typedef enum iomux_config {
+ IOMUX_CONFIG_ALT0, /*!< used as alternate function 0 */
+ IOMUX_CONFIG_ALT1, /*!< used as alternate function 1 */
+ IOMUX_CONFIG_ALT2, /*!< used as alternate function 2 */
+ IOMUX_CONFIG_ALT3, /*!< used as alternate function 3 */
+ IOMUX_CONFIG_ALT4, /*!< used as alternate function 4 */
+ IOMUX_CONFIG_ALT5, /*!< used as alternate function 5 */
+ IOMUX_CONFIG_ALT6, /*!< used as alternate function 6 */
+ IOMUX_CONFIG_ALT7, /*!< used as alternate function 7 */
+ IOMUX_CONFIG_GPIO, /*!< added to help user use GPIO mode */
+ IOMUX_CONFIG_SION = 0x1 << 4, /*!< used as LOOPBACK:MUX SION bit */
+} iomux_pin_cfg_t;
+
+/*!
+ * various IOMUX pad functions
+ */
+typedef enum iomux_pad_config {
+ PAD_CTL_SRE_SLOW = 0x0 << 0,
+ PAD_CTL_SRE_FAST = 0x1 << 0,
+ PAD_CTL_DRV_LOW = 0x0 << 1,
+ PAD_CTL_DRV_MEDIUM = 0x1 << 1,
+ PAD_CTL_DRV_HIGH = 0x2 << 1,
+ PAD_CTL_DRV_MAX = 0x3 << 1,
+ PAD_CTL_ODE_OPENDRAIN_NONE = 0x0 << 3,
+ PAD_CTL_ODE_OPENDRAIN_ENABLE = 0x1 << 3,
+ PAD_CTL_100K_PD = 0x0 << 4,
+ PAD_CTL_47K_PU = 0x1 << 4,
+ PAD_CTL_100K_PU = 0x2 << 4,
+ PAD_CTL_22K_PU = 0x3 << 4,
+ PAD_CTL_PUE_KEEPER = 0x0 << 6,
+ PAD_CTL_PUE_PULL = 0x1 << 6,
+ PAD_CTL_PKE_NONE = 0x0 << 7,
+ PAD_CTL_PKE_ENABLE = 0x1 << 7,
+ PAD_CTL_HYS_NONE = 0x0 << 8,
+ PAD_CTL_HYS_ENABLE = 0x1 << 8,
+ PAD_CTL_DDR_INPUT_CMOS = 0x0 << 9,
+ PAD_CTL_DDR_INPUT_DDR = 0x1 << 9,
+ PAD_CTL_DRV_VOT_LOW = 0x0 << 13,
+ PAD_CTL_DRV_VOT_HIGH = 0x1 << 13,
+} iomux_pad_config_t;
+
+/*!
+ * various IOMUX input select register index
+ */
+typedef enum iomux_input_select {
+ MUX_IN_AUDMUX_P4_INPUT_DA_AMX_SELECT_I = 0,
+ MUX_IN_AUDMUX_P4_INPUT_DB_AMX_SELECT_I,
+ MUX_IN_AUDMUX_P4_INPUT_TXCLK_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P4_INPUT_TXFS_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P5_INPUT_DA_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P5_INPUT_DB_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P5_INPUT_RXCLK_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P5_INPUT_RXFS_AMX_SELECT,
+ MUX_IN_AUDMUX_P5_INPUT_TXCLK_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P5_INPUT_TXFS_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P6_INPUT_DA_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P6_INPUT_DB_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P6_INPUT_RXCLK_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P6_INPUT_RXFS_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P6_INPUT_TXCLK_AMX_SELECT_INPUT,
+ MUX_IN_AUDMUX_P6_INPUT_TXFS_AMX_SELECT_INPUT,
+ MUX_IN_CCM_IPP_DI_CLK_SELECT_INPUT,
+ /* TO2 */
+ MUX_IN_CCM_IPP_DI1_CLK_SELECT_INPUT,
+ MUX_IN_CCM_PLL1_BYPASS_CLK_SELECT_INPUT,
+ MUX_IN_CCM_PLL2_BYPASS_CLK_SELECT_INPUT,
+ MUX_IN_CSPI_IPP_CSPI_CLK_IN_SELECT_INPUT,
+ MUX_IN_CSPI_IPP_IND_MISO_SELECT_INPUT,
+ MUX_IN_CSPI_IPP_IND_MOSI_SELECT_INPUT,
+ MUX_IN_CSPI_IPP_IND_SS_B_1_SELECT_INPUT,
+ MUX_IN_CSPI_IPP_IND_SS_B_2_SELECT_INPUT,
+ MUX_IN_CSPI_IPP_IND_SS_B_3_SELECT_INPUT,
+ MUX_IN_DPLLIP1_L1T_TOG_EN_SELECT_INPUT,
+ /* TO2 */
+ MUX_IN_ECSPI2_IPP_IND_SS_B_1_SELECT_INPUT,
+ MUX_IN_ECSPI2_IPP_IND_SS_B_3_SELECT_INPUT,
+ MUX_IN_EMI_IPP_IND_RDY_INT_SELECT_INPUT,
+ MUX_IN_ESDHC3_IPP_DAT0_IN_SELECT_INPUT,
+ MUX_IN_ESDHC3_IPP_DAT1_IN_SELECT_INPUT,
+ MUX_IN_ESDHC3_IPP_DAT2_IN_SELECT_INPUT,
+ MUX_IN_ESDHC3_IPP_DAT3_IN_SELECT_INPUT,
+ MUX_IN_FEC_FEC_COL_SELECT_INPUT,
+ MUX_IN_FEC_FEC_CRS_SELECT_INPUT,
+ MUX_IN_FEC_FEC_MDI_SELECT_INPUT,
+ MUX_IN_FEC_FEC_RDATA_0_SELECT_INPUT,
+ MUX_IN_FEC_FEC_RDATA_1_SELECT_INPUT,
+ MUX_IN_FEC_FEC_RDATA_2_SELECT_INPUT,
+ MUX_IN_FEC_FEC_RDATA_3_SELECT_INPUT,
+ MUX_IN_FEC_FEC_RX_CLK_SELECT_INPUT,
+ MUX_IN_FEC_FEC_RX_DV_SELECT_INPUT,
+ MUX_IN_FEC_FEC_RX_ER_SELECT_INPUT,
+ MUX_IN_FEC_FEC_TX_CLK_SELECT_INPUT,
+ MUX_IN_GPIO3_IPP_IND_G_IN_1_SELECT_INPUT,
+ MUX_IN_GPIO3_IPP_IND_G_IN_2_SELECT_INPUT,
+ MUX_IN_GPIO3_IPP_IND_G_IN_3_SELECT_INPUT,
+ MUX_IN_GPIO3_IPP_IND_G_IN_4_SELECT_INPUT,
+ MUX_IN_GPIO3_IPP_IND_G_IN_5_SELECT_INPUT,
+ MUX_IN_GPIO3_IPP_IND_G_IN_6_SELECT_INPUT,
+ MUX_IN_GPIO3_IPP_IND_G_IN_7_SELECT_INPUT,
+ MUX_IN_GPIO3_IPP_IND_G_IN_8_SELECT_INPUT,
+ /* TO2 */
+ MUX_IN_GPIO3_IPP_IND_G_IN_12_SELECT_INPUT,
+ MUX_IN_HSC_MIPI_MIX_IPP_IND_SENS1_DATA_EN_SELECT_INPUT,
+ MUX_IN_HSC_MIPI_MIX_IPP_IND_SENS2_DATA_EN_SELECT_INPUT,
+ /* TO2 */
+ MUX_IN_HSC_MIPI_MIX_PAR_VSYNC_SELECT_INPUT,
+ /* TO2 */
+ MUX_IN_HSC_MIPI_MIX_PAR_DI_WAIT_SELECT_INPUT,
+ MUX_IN_HSC_MIPI_MIX_PAR_SISG_TRIG_SELECT_INPUT,
+ MUX_IN_I2C1_IPP_SCL_IN_SELECT_INPUT,
+ MUX_IN_I2C1_IPP_SDA_IN_SELECT_INPUT,
+ MUX_IN_I2C2_IPP_SCL_IN_SELECT_INPUT,
+ MUX_IN_I2C2_IPP_SDA_IN_SELECT_INPUT,
+
+ MUX_IN_IPU_IPP_DI_0_IND_DISPB_SD_D_SELECT_INPUT,
+
+ MUX_IN_IPU_IPP_DI_1_IND_DISPB_SD_D_SELECT_INPUT,
+
+ MUX_IN_KPP_IPP_IND_COL_6_SELECT_INPUT,
+ MUX_IN_KPP_IPP_IND_COL_7_SELECT_INPUT,
+ MUX_IN_KPP_IPP_IND_ROW_4_SELECT_INPUT,
+ MUX_IN_KPP_IPP_IND_ROW_5_SELECT_INPUT,
+ MUX_IN_KPP_IPP_IND_ROW_6_SELECT_INPUT,
+ MUX_IN_KPP_IPP_IND_ROW_7_SELECT_INPUT,
+ MUX_IN_UART1_IPP_UART_RTS_B_SELECT_INPUT,
+ MUX_IN_UART1_IPP_UART_RXD_MUX_SELECT_INPUT,
+ MUX_IN_UART2_IPP_UART_RTS_B_SELECT_INPUT,
+ MUX_IN_UART2_IPP_UART_RXD_MUX_SELECT_INPUT,
+ MUX_IN_UART3_IPP_UART_RTS_B_SELECT_INPUT,
+ MUX_IN_UART3_IPP_UART_RXD_MUX_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_CLK_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_DATA_0_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_DATA_1_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_DATA_2_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_DATA_3_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_DATA_4_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_DATA_5_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_DATA_6_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_DATA_7_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_DIR_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_NXT_SELECT_INPUT,
+ MUX_IN_USBOH3_IPP_IND_UH3_STP_SELECT_INPUT,
+ MUX_INPUT_NUM_MUX,
+} iomux_input_select_t;
+
+/*!
+ * various IOMUX input functions
+ */
+typedef enum iomux_input_config {
+ INPUT_CTL_PATH0 = 0x0,
+ INPUT_CTL_PATH1,
+ INPUT_CTL_PATH2,
+ INPUT_CTL_PATH3,
+ INPUT_CTL_PATH4,
+ INPUT_CTL_PATH5,
+ INPUT_CTL_PATH6,
+ INPUT_CTL_PATH7,
+} iomux_input_config_t;
+
+struct mxc_iomux_pin_cfg {
+ iomux_pin_name_t pin;
+ u8 mux_mode;
+ u16 pad_cfg;
+ u8 in_select;
+ u8 in_mode;
+};
+
+/*!
+ * Request ownership for an IO pin. This function has to be the first one
+ * being called before that pin is used. The caller has to check the
+ * return value to make sure it returns 0.
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param config config as defined in \b #iomux_pin_ocfg_t
+ *
+ * @return 0 if successful; Non-zero otherwise
+ */
+int mxc_request_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t config);
+
+/*!
+ * Release ownership for an IO pin
+ *
+ * @param pin a name defined by \b iomux_pin_name_t
+ * @param config config as defined in \b #iomux_pin_ocfg_t
+ */
+void mxc_free_iomux(iomux_pin_name_t pin, iomux_pin_cfg_t config);
+
+/*!
+ * This function configures the pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @param config the ORed value of elements defined in
+ * \b #iomux_pad_config_t
+ */
+void mxc_iomux_set_pad(iomux_pin_name_t pin, u32 config);
+
+/*!
+ * This function gets the current pad value for a IOMUX pin.
+ *
+ * @param pin a pin number as defined in \b #iomux_pin_name_t
+ * @return current pad value
+ */
+unsigned int mxc_iomux_get_pad(iomux_pin_name_t pin);
+
+/*!
+ * This function configures input path.
+ *
+ * @param input index of input select register as defined in
+ * \b #iomux_input_select_t
+ * @param config the binary value of elements defined in \b #iomux_input_config_t
+ */
+void mxc_iomux_set_input(iomux_input_select_t input, u32 config);
+
+#endif /* __MACH_MX51_IOMUX_H__ */
diff --git a/arch/arm/mach-mx51/lpmodes.c b/arch/arm/mach-mx51/lpmodes.c
new file mode 100644
index 000000000000..32af9ccc4f6a
--- /dev/null
+++ b/arch/arm/mach-mx51/lpmodes.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx51_lpmodes.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC low power modes setup.
+ *
+ * MX51 is designed to play and video with minimal power consumption.
+ * This driver enables the platform to enter and exit audio and video low
+ * power modes.
+ *
+ * @ingroup PM
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+#include <linux/regulator/machine.h>
+#include "crm_regs.h"
+
+#define ARM_LP_CLK 166250000
+#define GP_LPM_VOLTAGE 775000
+#define GP_NORMAL_VOLTAGE 1050000
+
+static int org_cpu_rate;
+int lp_video_mode;
+int lp_audio_mode;
+static struct device *lpmode_dev;
+struct regulator *gp_core;
+
+void enter_lp_video_mode(void)
+{
+}
+
+void exit_lp_video_mode(void)
+{
+}
+
+void enter_lp_audio_mode(void)
+{
+ struct clk *tclk;
+ int ret;
+
+ struct clk *p_clk;
+ struct clk *amode_parent_clk;
+
+
+ tclk = clk_get(NULL, "ipu_clk");
+ if (clk_get_usecount(tclk) != 0) {
+ printk(KERN_INFO
+ "Cannot enter AUDIO LPM mode - display is still active\n");
+ return;
+ }
+
+ tclk = clk_get(NULL, "cpu_clk");
+ org_cpu_rate = clk_get_rate(tclk);
+
+#ifdef CHANGE_DDR2_TO_PLL2
+ tclk = clk_get(NULL, "ddr_clk");
+ clk_set_parent(tclk, clk_get(NULL, "axi_a_clk"));
+
+ /* Set CPU clock to be derived from PLL2 instead of PLL1 */
+ tclk = clk_get(NULL, "pll1_sw_clk");
+ clk_set_parent(tclk, clk_get(NULL, "pll2"));
+ clk_enable(tclk);
+
+ tclk = clk_get(NULL, "ddr_clk");
+ clk_set_parent(tclk, clk_get(NULL, "ddr_hf_clk"));
+#endif
+
+ /*Change the DDR freq to 133Mhz. */
+ tclk = clk_get(NULL, "ddr_hf_clk");
+ clk_set_rate(tclk, clk_round_rate(tclk, 133000000));
+
+ tclk = clk_get(NULL, "cpu_clk");
+ ret = clk_set_rate(tclk, ARM_LP_CLK);
+ if (ret != 0)
+ printk(KERN_DEBUG "cannot set CPU clock rate\n");
+ clk_put(tclk);
+ /* Set the voltage to 0.775v for the GP domain. */
+ ret = regulator_set_voltage(gp_core, GP_LPM_VOLTAGE, GP_LPM_VOLTAGE);
+ if (ret < 0)
+ printk(KERN_DEBUG "COULD NOT SET GP VOLTAGE!!!\n");
+
+ tclk = clk_get(NULL, "periph_apm_clk");
+ amode_parent_clk = clk_get(NULL, "lp_apm");
+ p_clk = clk_get_parent(tclk);
+ /* Make sure osc_clk is the parent of lp_apm. */
+ clk_set_parent(amode_parent_clk, clk_get(NULL, "osc"));
+ /* Set the parent of periph_apm_clk to be lp_apm */
+ clk_set_parent(tclk, amode_parent_clk);
+
+ amode_parent_clk = tclk;
+
+ tclk = clk_get(NULL, "main_bus_clk");
+ p_clk = clk_get_parent(tclk);
+ /* Set the parent of main_bus_clk to be periph_apm_clk */
+ clk_set_parent(tclk, amode_parent_clk);
+
+ clk_set_rate(clk_get(NULL, "axi_a_clk"), 24000000);
+ clk_set_rate(clk_get(NULL, "axi_b_clk"), 24000000);
+ clk_set_rate(clk_get(NULL, "ahb_clk"), 24000000);
+ clk_set_rate(clk_get(NULL, "emi_slow_clk"), 24000000);
+ clk_set_rate(clk_get(NULL, "nfc_clk"), 12000000);
+
+ /* disable PLL3 */
+ tclk = clk_get(NULL, "pll3");
+ if (tclk->usecount == 1)
+ clk_disable(tclk);
+
+ /* disable PLL2 */
+ tclk = clk_get(NULL, "pll2");
+ if (tclk->usecount == 1)
+ clk_disable(tclk);
+
+ /* disable PLL1 */
+ tclk = clk_get(NULL, "pll1_main_clk");
+ if (tclk->usecount == 1)
+ clk_disable(tclk);
+
+ lp_audio_mode = 1;
+}
+
+void exit_lp_audio_mode(void)
+{
+ struct clk *tclk;
+ struct clk *p_clk;
+ struct clk *rmode_parent_clk;
+ int ret;
+
+ /* Set the voltage to 1.05v for the GP domain. */
+ ret = regulator_set_voltage(gp_core,
+ GP_NORMAL_VOLTAGE, GP_NORMAL_VOLTAGE);
+ if (ret < 0)
+ printk(KERN_DEBUG "COULD NOT SET GP VOLTAGE!!!\n");
+
+ rmode_parent_clk = clk_get(NULL, "pll2");
+ clk_enable(rmode_parent_clk);
+
+ tclk = clk_get(NULL, "main_bus_clk");
+ p_clk = clk_get_parent(tclk);
+
+ /* Set the dividers before setting the parent clock. */
+ clk_set_rate(clk_get(NULL, "axi_a_clk"), 6000000);
+ clk_set_rate(clk_get(NULL, "axi_b_clk"), 4800000);
+ clk_set_rate(clk_get(NULL, "ahb_clk"), 4800000);
+ clk_set_rate(clk_get(NULL, "emi_slow_clk"), 4800000);
+ clk_set_rate(clk_get(NULL, "nfc_clk"), 1200000);
+ /* Set the parent of main_bus_clk to be pll2 */
+ clk_set_parent(tclk, rmode_parent_clk);
+
+#ifdef CHANGE_DDR2_TO_PLL2
+ tclk = clk_get(NULL, "ddr_clk");
+ clk_set_parent(tclk, clk_get(NULL, "axi_a_clk"));
+
+ /* Set CPU clock to be derived from PLL1 instead of PLL2 */
+ tclk = clk_get(NULL, "pll1_sw_clk");
+ clk_set_parent(tclk, clk_get(NULL, "pll1_main_clk"));
+ clk_disable(tclk);
+
+ tclk = clk_get(NULL, "ddr_clk");
+ clk_set_parent(tclk, clk_get(NULL, "ddr_hf_clk"));
+#endif
+
+ tclk = clk_get(NULL, "cpu_clk");
+ ret = clk_set_rate(tclk, org_cpu_rate);
+ if (ret != 0)
+ printk(KERN_DEBUG "cannot set CPU clock rate\n");
+ clk_put(tclk);
+
+ tclk = clk_get(NULL, "cpu_clk");
+
+ ret = clk_set_rate(tclk, org_cpu_rate);
+ if (ret != 0)
+ printk(KERN_DEBUG "cannot set CPU clock rate\n");
+
+
+ /*Change the DDR freq to 200MHz*/
+ tclk = clk_get(NULL, "ddr_hf_clk");
+ clk_set_rate(tclk, clk_round_rate(tclk, 200000000));
+ lp_audio_mode = 0;
+
+}
+
+static ssize_t lp_curr_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (lp_video_mode)
+ return sprintf(buf, "in lp_video_mode\n");
+ else if (lp_audio_mode)
+ return sprintf(buf, "in lp_audio_mode\n");
+ else
+ return sprintf(buf, "in normal mode\n");
+}
+
+static ssize_t set_lp_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ printk(KERN_DEBUG "In set_lp_mode() \n");
+
+ if (strstr(buf, "enable_lp_video") != NULL) {
+ if (!lp_video_mode)
+ enter_lp_video_mode();
+ } else if (strstr(buf, "disable_lp_video") != NULL) {
+ if (lp_video_mode)
+ exit_lp_video_mode();
+ } else if (strstr(buf, "enable_lp_audio") != NULL) {
+ if (!lp_audio_mode)
+ enter_lp_audio_mode();
+ } else if (strstr(buf, "disable_lp_audio") != NULL) {
+ if (lp_audio_mode)
+ exit_lp_audio_mode();
+ }
+ return size;
+}
+
+static DEVICE_ATTR(lp_modes, 0644, lp_curr_mode, set_lp_mode);
+
+/*!
+ * This is the probe routine for the lp_mode driver.
+ *
+ * @param pdev The platform device structure
+ *
+ * @return The function returns 0 on success
+ *
+ */
+static int __devinit mx51_lpmode_probe(struct platform_device *pdev)
+{
+ u32 res = 0;
+ lpmode_dev = &pdev->dev;
+
+ res = sysfs_create_file(&lpmode_dev->kobj, &dev_attr_lp_modes.attr);
+ if (res) {
+ printk(KERN_ERR
+ "lpmode_dev: Unable to register sysdev entry for lpmode_dev");
+ return res;
+ }
+
+ if (res != 0) {
+ printk(KERN_ERR "lpmode_dev: Unable to start");
+ return res;
+ }
+ gp_core = regulator_get(NULL, "SW1");
+ lp_video_mode = 0;
+ lp_audio_mode = 0;
+
+ return 0;
+}
+
+static struct platform_driver mx51_lpmode_driver = {
+ .driver = {
+ .name = "mx51_lpmode",
+ },
+ .probe = mx51_lpmode_probe,
+};
+
+/*!
+ * Initialise the mx51_lpmode_driver.
+ *
+ * @return The function always returns 0.
+ */
+
+static int __init lpmode_init(void)
+{
+ if (platform_driver_register(&mx51_lpmode_driver) != 0) {
+ printk(KERN_ERR "mx37_lpmode_driver register failed\n");
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "LPMode driver module loaded\n");
+ return 0;
+}
+
+static void __exit lpmode_cleanup(void)
+{
+ sysfs_remove_file(&lpmode_dev->kobj, &dev_attr_lp_modes.attr);
+
+ /* Unregister the device structure */
+ platform_driver_unregister(&mx51_lpmode_driver);
+}
+
+module_init(lpmode_init);
+module_exit(lpmode_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("LPMode driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-mx51/mm.c b/arch/arm/mach-mx51/mm.c
new file mode 100644
index 000000000000..7834d02b386a
--- /dev/null
+++ b/arch/arm/mach-mx51/mm.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <asm/pgtable.h>
+#include <asm/mach/map.h>
+
+/*!
+ * @file mach-mx51/mm.c
+ *
+ * @brief This file creates static mapping between physical to virtual memory.
+ *
+ * @ingroup Memory_MX51
+ */
+
+/*!
+ * This structure defines the MX51 memory map.
+ */
+static struct map_desc mxc_io_desc[] __initdata = {
+ {
+ .virtual = IRAM_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(IRAM_BASE_ADDR),
+ .length = IRAM_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = DEBUG_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(DEBUG_BASE_ADDR),
+ .length = DEBUG_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = TZIC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(TZIC_BASE_ADDR),
+ .length = TZIC_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = AIPS1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS1_BASE_ADDR),
+ .length = AIPS1_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = SPBA0_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(SPBA0_BASE_ADDR),
+ .length = SPBA0_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = AIPS2_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(AIPS2_BASE_ADDR),
+ .length = AIPS2_SIZE,
+ .type = MT_DEVICE},
+ {
+ .virtual = NFC_BASE_ADDR_AXI_VIRT,
+ .pfn = __phys_to_pfn(NFC_BASE_ADDR_AXI),
+ .length = NFC_AXI_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 mxc_map_io(void)
+{
+ u32 tzic_addr;
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) < 0)
+ tzic_addr = 0x8FFFC000;
+ else
+ tzic_addr = 0xE0003000;
+
+ mxc_io_desc[2].pfn = __phys_to_pfn(tzic_addr);
+ iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
+}
diff --git a/arch/arm/mach-mx51/mx51_3stack.c b/arch/arm/mach-mx51/mx51_3stack.c
new file mode 100644
index 000000000000..12e20bf95096
--- /dev/null
+++ b/arch/arm/mach-mx51/mx51_3stack.c
@@ -0,0 +1,1308 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <asm/mach/keypad.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/nodemask.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/ata.h>
+#include <linux/pmic_external.h>
+#include <linux/ipu.h>
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/mach/flash.h>
+#endif
+
+#include <linux/regulator/consumer.h>
+#include <mach/hardware.h>
+#include <mach/spba.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/common.h>
+#include <mach/memory.h>
+#include <mach/gpio.h>
+#include <mach/mmc.h>
+
+#include "board-mx51_3stack.h"
+#include "iomux.h"
+#include "crm_regs.h"
+
+/*!
+ * @file mach-mx51/mx51_3stack.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX51
+ */
+extern void __init mx51_3stack_io_init(void);
+extern struct cpu_wp *(*get_cpu_wp)(int *wp);
+extern void (*set_num_cpu_wp)(int num);
+static int num_cpu_wp = 3;
+
+/* working point(wp): 0 - 800MHz; 1 - 166.25MHz; */
+static struct cpu_wp cpu_wp_auto[] = {
+ {
+ .pll_rate = 1000000000,
+ .cpu_rate = 1000000000,
+ .pdf = 0,
+ .mfi = 10,
+ .mfd = 11,
+ .mfn = 5,
+ .cpu_podf = 0,
+ .cpu_voltage = 1175000,},
+ {
+ .pll_rate = 800000000,
+ .cpu_rate = 800000000,
+ .pdf = 0,
+ .mfi = 8,
+ .mfd = 2,
+ .mfn = 1,
+ .cpu_podf = 0,
+ .cpu_voltage = 1100000,},
+ {
+ .pll_rate = 800000000,
+ .cpu_rate = 166250000,
+ .pdf = 4,
+ .mfi = 8,
+ .mfd = 2,
+ .mfn = 1,
+ .cpu_podf = 4,
+ .cpu_voltage = 850000,},
+};
+
+struct cpu_wp *mx51_3stack_get_cpu_wp(int *wp)
+{
+ *wp = num_cpu_wp;
+ return cpu_wp_auto;
+}
+
+void mx51_3stack_set_num_cpu_wp(int num)
+{
+ num_cpu_wp = num;
+ return;
+}
+
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+#if defined(CONFIG_KEYBOARD_MXC) || defined(CONFIG_KEYBOARD_MXC_MODULE)
+static u16 keymapping[24] = {
+ KEY_1, KEY_2, KEY_3, KEY_F1, KEY_UP, KEY_F2,
+ KEY_4, KEY_5, KEY_6, KEY_LEFT, KEY_SELECT, KEY_RIGHT,
+ KEY_7, KEY_8, KEY_9, KEY_F3, KEY_DOWN, KEY_F4,
+ KEY_0, KEY_OK, KEY_ESC, KEY_ENTER, KEY_MENU, KEY_BACK,
+};
+
+static struct resource mxc_kpp_resources[] = {
+ [0] = {
+ .start = MXC_INT_KPP,
+ .end = MXC_INT_KPP,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct keypad_data keypad_plat_data = {
+ .rowmax = 4,
+ .colmax = 6,
+ .irq = MXC_INT_KPP,
+ .learning = 0,
+ .delay = 2,
+ .matrix = keymapping,
+};
+
+/* mxc keypad driver */
+static struct platform_device mxc_keypad_device = {
+ .name = "mxc_keypad",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_kpp_resources),
+ .resource = mxc_kpp_resources,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &keypad_plat_data,
+ },
+};
+
+static void mxc_init_keypad(void)
+{
+ (void)platform_device_register(&mxc_keypad_device);
+}
+#else
+static inline void mxc_init_keypad(void)
+{
+}
+#endif
+
+/* NAND Flash Partitions */
+#ifdef CONFIG_MTD_PARTITIONS
+
+static struct mtd_partition nand_flash_partitions[] = {
+ {
+ .name = "bootloader",
+ .offset = 0,
+ .size = 3 * 1024 * 1024},
+ {
+ .name = "nand.kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 5 * 1024 * 1024},
+ {
+ .name = "nand.rootfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 256 * 1024 * 1024},
+ {
+ .name = "nand.userfs1",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 256 * 1024 * 1024},
+ {
+ .name = "nand.userfs2",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL},
+};
+
+#endif
+
+/* MTD NAND flash */
+#if defined(CONFIG_MTD_NAND_MXC) \
+ || defined(CONFIG_MTD_NAND_MXC_MODULE) \
+ || defined(CONFIG_MTD_NAND_MXC_V2) \
+ || defined(CONFIG_MTD_NAND_MXC_V2_MODULE) \
+ || defined(CONFIG_MTD_NAND_MXC_V3) \
+ || defined(CONFIG_MTD_NAND_MXC_V3_MODULE)
+
+extern void gpio_nand_active(void);
+extern void gpio_nand_inactive(void);
+
+static int nand_init(void)
+{
+ /* Configure the pins */
+ gpio_nand_active();
+ return 0;
+}
+
+static void nand_exit(void)
+{
+ /* Free the pins */
+ gpio_nand_inactive();
+}
+
+static struct flash_platform_data mxc_nand_data = {
+ #ifdef CONFIG_MTD_PARTITIONS
+ .parts = nand_flash_partitions,
+ .nr_parts = ARRAY_SIZE(nand_flash_partitions),
+ #endif
+ .width = 1,
+ .init = nand_init,
+ .exit = nand_exit,
+};
+
+static struct platform_device mxc_nandv2_mtd_device = {
+ .name = "mxc_nandv2_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static void mxc_init_nand_mtd(void)
+{
+ (void)platform_device_register(&mxc_nandv2_mtd_device);
+}
+#else
+static inline void mxc_init_nand_mtd(void)
+{
+}
+#endif
+
+/* i.MX MTD NAND Flash Controller */
+
+#if defined(CONFIG_MTD_NAND_IMX_NFC) || defined(CONFIG_MTD_NAND_IMX_NFC_MODULE)
+
+/* Resources for this device. */
+
+static struct resource imx_nfc_resources[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ .start = NFC_BASE_ADDR_AXI + 0x0000,
+ .end = NFC_BASE_ADDR_AXI + 0x1200 - 1,
+ .name = IMX_NFC_BUFFERS_ADDR_RES_NAME,
+ },
+ {
+ .flags = IORESOURCE_MEM,
+ .start = NFC_BASE_ADDR_AXI + 0x1E00,
+ .end = NFC_BASE_ADDR_AXI + 0x1E44 - 1,
+ .name = IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME,
+ },
+ {
+ .flags = IORESOURCE_MEM,
+ .start = NFC_BASE_ADDR + 0x00,
+ .end = NFC_BASE_ADDR + 0x34 - 1,
+ .name = IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME,
+ },
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = MXC_INT_NFC,
+ .end = MXC_INT_NFC,
+ .name = IMX_NFC_INTERRUPT_RES_NAME,
+ },
+};
+
+/*
+ * Platform-specific information about this device. Some of the details depend
+ * on the SoC. See imx_init_nfc() below for code that fills in the rest.
+ */
+
+static struct imx_nfc_platform_data imx_nfc_platform_data = {
+ .nfc_major_version = 3,
+ .nfc_minor_version = 2,
+ .force_ce = false,
+ .target_cycle_in_ns = 30,
+ .clock_name = "nfc_clk",
+ .set_page_size = 0,
+ .interleave = false,
+ #ifdef CONFIG_MTD_PARTITIONS
+ .partitions = nand_flash_partitions,
+ .partition_count = ARRAY_SIZE(nand_flash_partitions),
+ #endif
+};
+
+/* The structure that represents the NFC device. */
+
+static struct platform_device imx_nfc_device = {
+ .name = IMX_NFC_DRIVER_NAME,
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &imx_nfc_platform_data,
+ },
+ .resource = imx_nfc_resources,
+ .num_resources = ARRAY_SIZE(imx_nfc_resources),
+};
+
+/**
+ * imx_init_nfc() - Sets up the NFC for this platform.
+ *
+ * This function sets up data structures representing the NFC device on this
+ * platform and registers the device with the platform management system.
+ */
+
+static void imx_nfc_init(void)
+{
+ (void)platform_device_register(&imx_nfc_device);
+}
+
+#else
+
+static inline void imx_nfc_init(void)
+{
+}
+
+#endif /* i.MX MTD NAND Flash Controller */
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || \
+ defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+
+static struct mxc_fb_platform_data fb_data[] = {
+ {
+ .interface_pix_fmt = IPU_PIX_FMT_RGB666,
+ },
+ {
+ .interface_pix_fmt = IPU_PIX_FMT_YUV444,
+ },
+};
+
+static struct platform_device mxc_fb_device[] = {
+ {
+ .name = "mxc_sdc_fb",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ .platform_data = &fb_data[0],
+ },
+ },
+ {
+ .name = "mxc_sdc_fb",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ .platform_data = &fb_data[1],
+ },
+ },
+ {
+ .name = "mxc_sdc_fb",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+ },
+};
+
+static void lcd_reset_to2(void)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DI1_D1_CS), 0);
+ ipu_reset_disp_panel();
+
+ return;
+}
+
+static void lcd_reset(void)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_RS), 0);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_RS), "ser_rs");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_RS), 0);
+ /* do reset */
+ msleep(10); /* tRES >= 100us */
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_RS), 1);
+ msleep(60);
+}
+
+static struct mxc_lcd_platform_data lcd_data = {
+ .core_reg = "VIOHI",
+ .io_reg = "SW4",
+ .reset = lcd_reset,
+};
+
+static struct platform_device mxc_lcd_device = {
+ .name = "lcd_spi",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &lcd_data,
+ },
+};
+
+static void wvga_reset(void)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DI1_D1_CS), 1);
+}
+
+static struct mxc_lcd_platform_data lcd_wvga_data = {
+ .reset = wvga_reset,
+};
+
+static struct platform_device lcd_wvga_device = {
+ .name = "lcd_claa",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &lcd_wvga_data,
+ },
+};
+
+static void mxc_init_fb(void)
+{
+
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) > 0)
+ lcd_data.reset = lcd_reset_to2;
+
+ (void)platform_device_register(&mxc_lcd_device);
+ (void)platform_device_register(&lcd_wvga_device);
+
+ (void)platform_device_register(&mxc_fb_device[0]);
+ (void)platform_device_register(&mxc_fb_device[1]);
+ (void)platform_device_register(&mxc_fb_device[2]);
+}
+#else
+static inline void mxc_init_fb(void)
+{
+}
+#endif
+
+static struct platform_device mxcbl_device = {
+ .name = "mxc_mc13892_bl",
+};
+
+static inline void mxc_init_bl(void)
+{
+ platform_device_register(&mxcbl_device);
+}
+
+void si4702_reset(void)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_DTACK), 0);
+ msleep(100);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_DTACK), 1);
+ msleep(100);
+}
+
+void si4702_clock_ctl(int flag)
+{
+}
+
+static void si4702_gpio_get(void)
+{
+ /* reset pin */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_DTACK), "eim_dtack");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_DTACK), 0);
+}
+
+static void si4702_gpio_put(void)
+{
+}
+
+static struct mxc_fm_platform_data si4702_data = {
+ .reg_vio = "SW4",
+ .reg_vdd = "VIOHI",
+ .gpio_get = si4702_gpio_get,
+ .gpio_put = si4702_gpio_put,
+ .reset = si4702_reset,
+ .clock_ctl = si4702_clock_ctl,
+ .sksnr = 0,
+ .skcnt = 0,
+ .band = 0,
+ .space = 100,
+ .seekth = 0xa,
+};
+
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {
+};
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT2
+static struct i2c_board_info mxc_i2c1_board_info[] __initdata = {
+ {
+ .type = "wm8903-i2c",
+ .addr = 0x1a,
+ },
+ {
+ .type = "sgtl5000-i2c",
+ .addr = 0x0a,
+ },
+ {
+ .type = "tsc2007",
+ .addr = 0x48,
+ .irq = IOMUX_TO_IRQ(MX51_PIN_GPIO1_5),
+ },
+ {
+ .type = "si4702",
+ .addr = 0x10,
+ .platform_data = (void *)&si4702_data,
+ },
+};
+#endif
+#if defined(CONFIG_I2C_MXC_HS) || defined(CONFIG_I2C_MXC_HS_MODULE)
+static struct mxc_camera_platform_data camera_data = {
+ .io_regulator = "SW4",
+ .analog_regulator = "VIOHI",
+ .mclk = 24000000,
+ .csi = 0,
+};
+static struct mxc_lightsensor_platform_data ls_data = {
+ .vdd_reg = NULL,
+ .rext = 100,
+};
+
+static struct i2c_board_info mxc_i2c_hs_board_info[] __initdata = {
+ {
+ .type = "ov3640",
+ .addr = 0x3C,
+ .platform_data = (void *)&camera_data,
+ },
+ {
+ .type = "isl29003",
+ .addr = 0x44,
+ .platform_data = &ls_data,
+ },
+};
+#endif
+
+#endif
+
+static u32 cpld_base_addr;
+
+/*lan9217 device*/
+#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
+static struct resource smsc911x_resources[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = LAN9217_IRQ,
+ .end = LAN9217_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+static struct platform_device smsc_lan9217_device = {
+ .name = "smsc911x",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ },
+ .num_resources = ARRAY_SIZE(smsc911x_resources),
+ .resource = smsc911x_resources,
+};
+static void mxc_init_enet(void)
+{
+ if (cpld_base_addr) {
+ smsc_lan9217_device.resource[0].start =
+ LAN9217_BASE_ADDR(cpld_base_addr);
+ smsc_lan9217_device.resource[0].end =
+ LAN9217_BASE_ADDR(cpld_base_addr) + 0x100;
+ (void)platform_device_register(&smsc_lan9217_device);
+ }
+}
+#else
+static inline void mxc_init_enet(void)
+{
+}
+#endif
+
+#if defined(CONFIG_IMX_SIM) || defined(CONFIG_IMX_SIM_MODULE)
+/* Used to configure the SIM bus */
+static struct mxc_sim_platform_data sim_data = {
+ .clk_rate = 4000000,
+ .clock_sim = "sim_clk",
+ .power_sim = NULL,
+ .init = NULL,
+ .exit = NULL,
+ .detect = 0,
+};
+
+/*!
+ * Resource definition for the SIM
+ */
+static struct resource mxc_sim_resources[] = {
+ [0] = {
+ .start = SIM_BASE_ADDR,
+ .end = SIM_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_SIM_IPB,
+ .end = MXC_INT_SIM_IPB,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = MXC_INT_SIM_DAT,
+ .end = MXC_INT_SIM_DAT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for IMX SIM */
+static struct platform_device mxc_sim_device = {
+ .name = "mxc_sim",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &sim_data,
+ },
+ .num_resources = ARRAY_SIZE(mxc_sim_resources),
+ .resource = mxc_sim_resources,
+};
+
+static inline void mxc_init_sim(void)
+{
+ (void)platform_device_register(&mxc_sim_device);
+}
+#else
+static inline void mxc_init_sim(void)
+{
+}
+#endif
+
+#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
+/*!
+ * Get WP pin value to detect write protection
+ */
+int sdhc_write_protect(struct device *dev)
+{
+ unsigned short rc = 0;
+
+ if (to_platform_device(dev)->id == 0)
+ rc = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_1));
+ else
+ rc = 0;
+ return rc;
+}
+
+/*
+ * Probe for the card. If present the GPIO data would be set.
+ */
+unsigned int sdhc_get_card_det_status(struct device *dev)
+{
+ int ret;
+
+ if (to_platform_device(dev)->id == 0) {
+ ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_0));
+ return ret;
+ } else { /* config the det pin for SDHC2 */
+ return 0;
+ }
+}
+
+static struct mxc_mmc_platform_data mmc1_data = {
+ .ocr_mask = MMC_VDD_32_33,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 150000,
+ .max_clk = 52000000,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "esdhc_clk",
+ .power_mmc = NULL,
+};
+static struct mxc_mmc_platform_data mmc2_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 |
+ MMC_VDD_31_32,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 150000,
+ .max_clk = 50000000,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "esdhc_clk",
+};
+
+/*!
+ * Resource definition for the SDHC1
+ */
+static struct resource mxcsdhc1_resources[] = {
+ [0] = {
+ .start = MMC_SDHC1_BASE_ADDR,
+ .end = MMC_SDHC1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC1,
+ .end = MXC_INT_MMC_SDHC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IOMUX_TO_IRQ(MX51_PIN_GPIO1_0),
+ .end = IOMUX_TO_IRQ(MX51_PIN_GPIO1_0),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*!
+ * Resource definition for the SDHC2
+ */
+static struct resource mxcsdhc2_resources[] = {
+ [0] = {
+ .start = MMC_SDHC2_BASE_ADDR,
+ .end = MMC_SDHC2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC2,
+ .end = MXC_INT_MMC_SDHC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC1 */
+static struct platform_device mxcsdhc1_device = {
+ .name = "mxsdhci",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc1_resources),
+ .resource = mxcsdhc1_resources,
+};
+
+/*! Device Definition for MXC SDHC2 */
+static struct platform_device mxcsdhc2_device = {
+ .name = "mxsdhci",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc2_resources),
+ .resource = mxcsdhc2_resources,
+};
+
+static inline void mxc_init_mmc(void)
+{
+ (void)platform_device_register(&mxcsdhc1_device);
+ (void)platform_device_register(&mxcsdhc2_device);
+}
+#else
+static inline void mxc_init_mmc(void)
+{
+}
+#endif
+
+static u32 brd_io;
+static void expio_ack_irq(u32 irq);
+
+static void mxc_expio_irq_handler(u32 irq, struct irq_desc *desc)
+{
+ u32 imr_val;
+ u32 int_valid;
+ u32 expio_irq;
+
+ desc->chip->mask(irq); /* irq = gpio irq number */
+
+ imr_val = __raw_readw(brd_io + INTR_MASK_REG);
+ int_valid = __raw_readw(brd_io + INTR_STATUS_REG) & ~imr_val;
+
+ if (unlikely(!int_valid))
+ goto out;
+
+ expio_irq = MXC_EXP_IO_BASE;
+ for (; int_valid != 0; int_valid >>= 1, expio_irq++) {
+ struct irq_desc *d;
+ if ((int_valid & 1) == 0)
+ continue;
+ d = irq_desc + expio_irq;
+ if (unlikely(!(d->handle_irq))) {
+ printk(KERN_ERR "\nEXPIO irq: %d unhandled\n",
+ expio_irq);
+ BUG(); /* oops */
+ }
+ d->handle_irq(expio_irq, d);
+ }
+
+ out:
+ desc->chip->ack(irq);
+ desc->chip->unmask(irq);
+}
+
+/*
+ * Disable an expio pin's interrupt by setting the bit in the imr.
+ * @param irq an expio virtual irq number
+ */
+static void expio_mask_irq(u32 irq)
+{
+ u16 reg;
+ u32 expio = MXC_IRQ_TO_EXPIO(irq);
+ /* mask the interrupt */
+ reg = __raw_readw(brd_io + INTR_MASK_REG);
+ reg |= (1 << expio);
+ __raw_writew(reg, brd_io + INTR_MASK_REG);
+}
+
+/*
+ * Acknowledge an expanded io pin's interrupt by clearing the bit in the isr.
+ * @param irq an expanded io virtual irq number
+ */
+static void expio_ack_irq(u32 irq)
+{
+ u32 expio = MXC_IRQ_TO_EXPIO(irq);
+ /* clear the interrupt status */
+ __raw_writew(1 << expio, brd_io + INTR_RESET_REG);
+ __raw_writew(0, brd_io + INTR_RESET_REG);
+ /* mask the interrupt */
+ expio_mask_irq(irq);
+}
+
+/*
+ * Enable a expio pin's interrupt by clearing the bit in the imr.
+ * @param irq a expio virtual irq number
+ */
+static void expio_unmask_irq(u32 irq)
+{
+ u16 reg;
+ u32 expio = MXC_IRQ_TO_EXPIO(irq);
+ /* unmask the interrupt */
+ reg = __raw_readw(brd_io + INTR_MASK_REG);
+ reg &= ~(1 << expio);
+ __raw_writew(reg, brd_io + INTR_MASK_REG);
+}
+
+static struct irq_chip expio_irq_chip = {
+ .ack = expio_ack_irq,
+ .mask = expio_mask_irq,
+ .unmask = expio_unmask_irq,
+};
+
+static int __init mxc_expio_init(void)
+{
+ int i;
+
+ brd_io = (u32) ioremap(BOARD_IO_ADDR(CS5_BASE_ADDR), SZ_4K);
+ if (brd_io == 0)
+ return -ENOMEM;
+
+ if ((__raw_readw(brd_io + MAGIC_NUMBER1_REG) != 0xAAAA) ||
+ (__raw_readw(brd_io + MAGIC_NUMBER2_REG) != 0x5555) ||
+ (__raw_readw(brd_io + MAGIC_NUMBER3_REG) != 0xCAFE)) {
+ pr_info("3-Stack Debug board not detected \n");
+ cpld_base_addr = 0;
+ return -ENODEV;
+ } else {
+ cpld_base_addr = CS5_BASE_ADDR;
+ }
+
+ pr_info("3-Stack Debug board detected, rev = 0x%04X\n",
+ readw(brd_io + CPLD_CODE_VER_REG));
+
+ /*
+ * Configure INT line as GPIO input
+ */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_6), "gpio1_6");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_6));
+
+ /* disable the interrupt and clear the status */
+ __raw_writew(0, brd_io + INTR_MASK_REG);
+ __raw_writew(0xFFFF, brd_io + INTR_RESET_REG);
+ __raw_writew(0, brd_io + INTR_RESET_REG);
+ __raw_writew(0x1F, brd_io + INTR_MASK_REG);
+ for (i = MXC_EXP_IO_BASE; i < (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES);
+ i++) {
+ set_irq_chip(i, &expio_irq_chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+ set_irq_type(EXPIO_PARENT_INT, IRQF_TRIGGER_LOW);
+ set_irq_chained_handler(EXPIO_PARENT_INT, mxc_expio_irq_handler);
+
+ return 0;
+}
+
+#if defined(CONFIG_PATA_FSL) || defined(CONFIG_PATA_FSL_MODULE)
+extern void gpio_ata_active(void);
+extern void gpio_ata_inactive(void);
+
+static int ata_init(struct platform_device *pdev)
+{
+ /* Configure the pins */
+ gpio_ata_active();
+ return 0;
+}
+
+static void ata_exit(void)
+{
+ /* Free the pins */
+ gpio_ata_inactive();
+}
+
+static struct fsl_ata_platform_data ata_data = {
+ .udma_mask = ATA_UDMA3,
+ .mwdma_mask = ATA_MWDMA2,
+ .pio_mask = ATA_PIO4,
+ .fifo_alarm = MXC_IDE_DMA_WATERMARK / 2,
+ .max_sg = MXC_IDE_DMA_BD_NR,
+ .init = ata_init,
+ .exit = ata_exit,
+ .core_reg = NULL,
+ .io_reg = NULL,
+};
+
+static struct resource pata_fsl_resources[] = {
+ [0] = {
+ .start = ATA_BASE_ADDR,
+ .end = ATA_BASE_ADDR + 0x000000C8,
+ .flags = IORESOURCE_MEM,},
+ [2] = {
+ .start = MXC_INT_ATA,
+ .end = MXC_INT_ATA,
+ .flags = IORESOURCE_IRQ,},
+};
+
+static struct platform_device pata_fsl_device = {
+ .name = "pata_fsl",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(pata_fsl_resources),
+ .resource = pata_fsl_resources,
+ .dev = {
+ .platform_data = &ata_data,
+ .coherent_dma_mask = ~0,},
+};
+
+static void __init mxc_init_pata(void)
+{
+ (void)platform_device_register(&pata_fsl_device);
+}
+#else /* CONFIG_PATA_FSL */
+static void __init mxc_init_pata(void)
+{
+}
+#endif /* CONFIG_PATA_FSL */
+
+#if defined(CONFIG_TOUCHSCREEN_TSC2007) \
+ || defined(CONFIG_TOUCHSCREEN_TSC2007_MODULE)
+
+static int __init mxc_init_touchscreen(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), "gpio1_5");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5));
+
+ return 0;
+}
+#else
+static int __init mxc_init_touchscreen(void)
+{
+ return 0;
+}
+#endif
+
+static int __init mxc_init_srpgconfig(void)
+{
+ struct clk *gpcclk = clk_get(NULL, "gpc_dvfs_clk");
+ clk_enable(gpcclk);
+
+ /* Setup the number of clock cycles to wait for SRPG
+ * power up and power down requests.
+ */
+ __raw_writel(0x010F0201, MXC_SRPG_ARM_PUPSCR);
+ __raw_writel(0x010F0201, MXC_SRPG_NEON_PUPSCR);
+ __raw_writel(0x00000008, MXC_SRPG_EMPGC0_PUPSCR);
+ __raw_writel(0x00000008, MXC_SRPG_EMPGC1_PUPSCR);
+
+ __raw_writel(0x01010101, MXC_SRPG_ARM_PDNSCR);
+ __raw_writel(0x01010101, MXC_SRPG_NEON_PDNSCR);
+ __raw_writel(0x00000018, MXC_SRPG_EMPGC0_PDNSCR);
+ __raw_writel(0x00000018, MXC_SRPG_EMPGC1_PDNSCR);
+
+ clk_disable(gpcclk);
+ clk_put(gpcclk);
+
+ return 0;
+}
+
+#if defined(CONFIG_SND_SOC_IMX_3STACK_WM8903) \
+ || defined(CONFIG_SND_SOC_IMX_3STACK_WM8903_MODULE)
+static struct mxc_audio_platform_data wm8903_data;
+
+static struct platform_device mxc_wm8903_device = {
+ .name = "imx-3stack-wm8903",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &wm8903_data,
+ },
+};
+
+static void __init mxc_init_wm8903(void)
+{
+ wm8903_data.ssi_clk[0] = clk_get(NULL, "ssi_clk.0");
+ clk_put(wm8903_data.ssi_clk[0]);
+
+ wm8903_data.ssi_clk[1] = clk_get(NULL, "ssi_clk.1");
+ clk_put(wm8903_data.ssi_clk[1]);
+
+ wm8903_data.ssi_num = 1;
+ wm8903_data.src_port = 2;
+ wm8903_data.ext_port = 3;
+
+ (void)platform_device_register(&mxc_wm8903_device);
+}
+#else
+static void __init mxc_init_wm8903(void)
+{
+}
+#endif
+
+#if defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000) \
+ || defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000_MODULE)
+static int mxc_sgtl5000_plat_init(void);
+static int mxc_sgtl5000_plat_finit(void);
+static int mxc_sgtl5000_amp_enable(int enable);
+
+int headphone_det_status(void)
+{
+ return gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A26));
+}
+
+static struct mxc_audio_platform_data sgtl5000_data = {
+ .ssi_num = 1,
+ .src_port = 2,
+ .ext_port = 3,
+ .hp_irq = IOMUX_TO_IRQ(MX51_PIN_EIM_A26),
+ .hp_status = headphone_det_status,
+ .amp_enable = mxc_sgtl5000_amp_enable,
+ .vddio = 1800000,
+ .vdda = 1800000,
+ .vddd = 1200000,
+ .sysclk = 12000000,
+ .init = mxc_sgtl5000_plat_init,
+ .finit = mxc_sgtl5000_plat_finit,
+};
+
+static struct platform_device mxc_sgtl5000_device = {
+ .name = "imx-3stack-sgtl5000",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &sgtl5000_data,
+ },
+};
+
+static int mxc_sgtl5000_plat_init(void)
+{
+ struct regulator *reg;
+ reg = regulator_get(&mxc_sgtl5000_device.dev, "GPO2");
+ if (IS_ERR(reg))
+ return -EINVAL;
+ sgtl5000_data.priv = reg;
+ return 0;
+}
+
+static int mxc_sgtl5000_plat_finit(void)
+{
+ struct regulator *reg;
+ reg = sgtl5000_data.priv;
+ if (reg) {
+ regulator_put(reg);
+ sgtl5000_data.priv = NULL;
+ }
+ return 0;
+}
+
+static int mxc_sgtl5000_amp_enable(int enable)
+{
+ struct regulator *reg;
+ reg = sgtl5000_data.priv;
+
+ if (!reg)
+ return -EINVAL;
+ if (enable)
+ regulator_enable(reg);
+ else
+ regulator_disable(reg);
+ return 0;
+}
+
+static void mxc_init_sgtl5000(void)
+{
+ platform_device_register(&mxc_sgtl5000_device);
+}
+#else
+static inline void mxc_init_sgtl5000(void)
+{
+}
+#endif
+
+static void bt_reset(void)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D19), 1);
+}
+
+static struct mxc_bt_platform_data mxc_bt_data = {
+ .bt_vdd = NULL,
+ .bt_vdd_parent = NULL,
+ .bt_vusb = "SW4",
+ .bt_vusb_parent = NULL,
+ .bt_reset = bt_reset,
+};
+
+static struct platform_device mxc_bt_device = {
+ .name = "mxc_bt",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_bt_data,
+ },
+};
+
+static void mxc_init_bluetooth(void)
+{
+ (void)platform_device_register(&mxc_bt_device);
+}
+
+static void mxc_unifi_hardreset(int pin_level)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D19), pin_level & 0x01);
+}
+
+static struct mxc_unifi_platform_data unifi_data = {
+ .hardreset = mxc_unifi_hardreset,
+ .reg_vdd_vpa = "VSD",
+ .reg_1v5_dd = "VGEN1",
+ .host_id = 1,
+};
+
+struct mxc_unifi_platform_data *get_unifi_plat_data(void)
+{
+ return &unifi_data;
+}
+
+EXPORT_SYMBOL(get_unifi_plat_data);
+
+/*!
+ * Board specific fixup function. It is called by \b setup_arch() in
+ * setup.c file very early on during kernel starts. It allows the user to
+ * statically fill in the proper values for the passed-in parameters. None of
+ * the parameters is used currently.
+ *
+ * @param desc pointer to \b struct \b machine_desc
+ * @param tags pointer to \b struct \b tag
+ * @param cmdline pointer to the command line
+ * @param mi pointer to \b struct \b meminfo
+ */
+static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ mxc_cpu_init();
+
+ get_cpu_wp = mx51_3stack_get_cpu_wp;
+ set_num_cpu_wp = mx51_3stack_set_num_cpu_wp;
+#ifdef CONFIG_DISCONTIGMEM
+ do {
+ int nid;
+ mi->nr_banks = MXC_NUMNODES;
+ for (nid = 0; nid < mi->nr_banks; nid++)
+ SET_NODE(mi, nid);
+
+ } while (0);
+#endif
+}
+
+#if defined(CONFIG_GPS_IOCTRL) || defined(CONFIG_GPS_IOCTRL_MODULE)
+static struct mxc_gps_platform_data gps_data = {
+ .core_reg = "VIOHI",
+ .analog_reg = "SW4",
+};
+
+static struct platform_device mxc_gps_device = {
+ .name = "gps_ioctrl",
+ .id = -1,
+ .dev = {
+ .platform_data = &gps_data,
+ },
+};
+
+static void __init mxc_init_gps(void)
+{
+ (void)platform_device_register(&mxc_gps_device);
+}
+
+int gpio_gps_access(int para)
+{
+ iomux_pin_name_t pin;
+ pin = (para & 0x1) ? MX51_PIN_EIM_CS2 : MX51_PIN_EIM_CRE;
+
+ if (para & 0x4) /* Read GPIO */
+ return gpio_get_value(IOMUX_TO_GPIO(pin));
+ else if (para & 0x2) /* Write GPIO */
+ gpio_set_value(IOMUX_TO_GPIO(pin), 1);
+ else
+ gpio_set_value(IOMUX_TO_GPIO(pin), 0);
+ return 0;
+}
+EXPORT_SYMBOL(gpio_gps_access);
+#else
+static void __init mxc_init_gps(void)
+{
+}
+#endif
+
+/*!
+ * Board specific initialization.
+ */
+static void __init mxc_board_init(void)
+{
+ int err;
+
+ mxc_cpu_common_init();
+ mxc_register_gpios();
+ mx51_3stack_io_init();
+ early_console_setup(saved_command_line);
+ mxc_init_devices();
+
+ mxc_expio_init();
+ mxc_init_enet();
+ mxc_init_pata();
+ mxc_init_fb();
+ mxc_init_bl();
+ mxc_init_keypad();
+ mxc_init_nand_mtd();
+ imx_nfc_init();
+ mxc_init_mmc();
+ mxc_init_sim();
+ mxc_init_srpgconfig();
+ mx51_3stack_init_mc13892();
+
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+ i2c_register_board_info(0, mxc_i2c0_board_info,
+ ARRAY_SIZE(mxc_i2c0_board_info));
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT2
+ i2c_register_board_info(1, mxc_i2c1_board_info,
+ ARRAY_SIZE(mxc_i2c1_board_info));
+#endif
+#if defined(CONFIG_I2C_MXC_HS) || defined(CONFIG_I2C_MXC_HS_MODULE)
+ i2c_register_board_info(3, mxc_i2c_hs_board_info,
+ ARRAY_SIZE(mxc_i2c_hs_board_info));
+#endif
+
+#endif
+ mxc_init_touchscreen();
+ mxc_init_wm8903();
+ mxc_init_sgtl5000();
+ mxc_init_bluetooth();
+ mxc_init_gps();
+
+ err = mxc_request_iomux(MX51_PIN_EIM_D19, IOMUX_CONFIG_GPIO);
+ if (err)
+ printk(KERN_ERR "Error: bt reset request gpio failed!\n");
+ else {
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_D19), "eim_d19");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D19), 0);
+ }
+}
+
+static void __init mx51_3stack_timer_init(void)
+{
+ /* Change the CPU voltages for TO2*/
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) <= 1) {
+ cpu_wp_auto[0].cpu_voltage = 1175000;
+ cpu_wp_auto[1].cpu_voltage = 1100000;
+ cpu_wp_auto[2].cpu_voltage = 1000000;
+ }
+
+ mxc_clocks_init(32768, 24000000, 22579200, 24576000);
+ mxc_timer_init("gpt_clk.0");
+}
+
+static struct sys_timer mxc_timer = {
+ .init = mx51_3stack_timer_init,
+};
+
+/*
+ * The following uses standard kernel macros define in arch.h in order to
+ * initialize __mach_desc_MX51_3STACK data structure.
+ */
+/* *INDENT-OFF* */
+MACHINE_START(MX51_3DS, "Freescale MX51 3-Stack Board")
+ /* Maintainer: Freescale Semiconductor, Inc. */
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .fixup = fixup_mxc_board,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mxc_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx51/mx51_3stack_gpio.c b/arch/arm/mach-mx51/mx51_3stack_gpio.c
new file mode 100644
index 000000000000..dfa57920b07b
--- /dev/null
+++ b/arch/arm/mach-mx51/mx51_3stack_gpio.c
@@ -0,0 +1,852 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/gpio.h>
+
+#include "iomux.h"
+
+/*!
+ * @file mach-mx51/mx51_3stack_gpio.c
+ *
+ * @brief This file contains all the GPIO setup functions for the board.
+ *
+ * @ingroup GPIO
+ */
+#define ATA_PAD_CONFIG (PAD_CTL_DRV_HIGH | PAD_CTL_DRV_VOT_HIGH)
+
+static struct mxc_iomux_pin_cfg __initdata mxc_iomux_pins[] = {
+ /* CSI0 */
+ {
+ MX51_PIN_CSI1_D8, IOMUX_CONFIG_ALT3,
+ PAD_CTL_PKE_ENABLE,
+ MUX_IN_GPIO3_IPP_IND_G_IN_12_SELECT_INPUT,
+ INPUT_CTL_PATH1,
+ },
+ {
+ MX51_PIN_CSI1_D9, IOMUX_CONFIG_ALT3,
+ PAD_CTL_PKE_ENABLE,
+ },
+ {
+ MX51_PIN_CSI1_D10, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D11, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D12, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D13, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D14, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D15, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D16, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D17, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D18, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D19, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_VSYNC, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_SRE_SLOW),
+ },
+ {
+ MX51_PIN_CSI1_HSYNC, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_SRE_SLOW),
+ },
+ {
+ MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT5 | IOMUX_CONFIG_SION,
+ (PAD_CTL_SRE_SLOW | PAD_CTL_DRV_MEDIUM | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_ENABLE | PAD_CTL_DRV_VOT_HIGH),
+ MUX_IN_HSC_MIPI_MIX_IPP_IND_SENS2_DATA_EN_SELECT_INPUT,
+ INPUT_CTL_PATH0,
+ },
+ { /* SPI1 */
+ MX51_PIN_CSPI1_MISO, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_CSPI1_MOSI, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_CSPI1_RDY, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_CSPI1_SCLK, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_OWIRE_LINE, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP2_DAT15, IOMUX_CONFIG_ALT5,
+ },
+ {
+ MX51_PIN_DI_GP2, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_DI_GP3, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_KEY_COL0, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_KEY_COL1, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_KEY_COL2, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_KEY_COL3, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_KEY_COL4, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_KEY_COL5, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_KEY_ROW0, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_KEY_ROW1, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_KEY_ROW2, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_KEY_ROW3, IOMUX_CONFIG_ALT0,
+ },
+ { /* AUD3_TXD */
+ MX51_PIN_AUD3_BB_TXD, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH |
+ PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_NONE | PAD_CTL_DDR_INPUT_CMOS | PAD_CTL_DRV_VOT_LOW),
+ },
+ { /* AUD3_RXD */
+ MX51_PIN_AUD3_BB_RXD, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH |
+ PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_NONE | PAD_CTL_DDR_INPUT_CMOS | PAD_CTL_DRV_VOT_LOW),
+ },
+ { /* AUD3_CLK */
+ MX51_PIN_AUD3_BB_CK, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH |
+ PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_NONE | PAD_CTL_DDR_INPUT_CMOS | PAD_CTL_DRV_VOT_LOW),
+ },
+ { /* AUD3_FS */
+ MX51_PIN_AUD3_BB_FS, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH |
+ PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_NONE | PAD_CTL_DDR_INPUT_CMOS | PAD_CTL_DRV_VOT_LOW),
+ },
+ {
+ MX51_PIN_EIM_D16, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_EIM_A27, IOMUX_CONFIG_ALT2,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ {
+ MX51_PIN_EIM_EB2, IOMUX_CONFIG_ALT1,
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_100K_PD,
+ },
+ {
+ MX51_PIN_EIM_DTACK, IOMUX_CONFIG_GPIO,
+ (PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU),
+ },
+ {
+ MX51_PIN_EIM_CS2, IOMUX_CONFIG_GPIO,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_HYS_NONE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_100K_PU | PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_EIM_CRE, IOMUX_CONFIG_GPIO,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_HYS_NONE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_100K_PU | PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DI_GP4, IOMUX_CONFIG_ALT2,
+ },
+ {
+ MX51_PIN_DISPB2_SER_DIN, IOMUX_CONFIG_GPIO,
+ 0,
+ MUX_IN_GPIO3_IPP_IND_G_IN_5_SELECT_INPUT,
+ INPUT_CTL_PATH1,
+ },
+ {
+ MX51_PIN_DISPB2_SER_RS, IOMUX_CONFIG_GPIO,
+ },
+ {
+ MX51_PIN_DISPB2_SER_DIO, IOMUX_CONFIG_GPIO,
+ },
+ { /* TO2 */
+ MX51_PIN_DI1_D1_CS, IOMUX_CONFIG_ALT4,
+ },
+ { /* TO2 */
+ MX51_PIN_DI1_D0_CS, IOMUX_CONFIG_ALT1,
+ },
+ { /* TO2 */
+ MX51_PIN_DI1_PIN11, IOMUX_CONFIG_ALT1,
+ },
+ { /* TO2 */
+ MX51_PIN_DI1_PIN12, IOMUX_CONFIG_ALT1,
+ },
+ { /* TO2 */
+ MX51_PIN_DI1_PIN13, IOMUX_CONFIG_ALT1,
+ },
+#ifdef CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL
+ {
+ MX51_PIN_DISP1_DAT0, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT2, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT3, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT4, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT5, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT6, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT7, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT8, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT9, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT10, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT11, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT12, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT13, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT14, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT15, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT16, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT17, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT18, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT19, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT20, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT21, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT22, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_DISP1_DAT23, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+#endif
+ {
+ MX51_PIN_I2C1_CLK, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ 0x1E4,
+ },
+ {
+ MX51_PIN_I2C1_DAT, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ 0x1E4,
+ },
+ {
+ MX51_PIN_GPIO1_6, IOMUX_CONFIG_GPIO,
+ },
+ {
+ MX51_PIN_GPIO1_7, IOMUX_CONFIG_ALT2,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_PUE_PULL |
+ PAD_CTL_100K_PU | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_GPIO1_2, IOMUX_CONFIG_ALT2 | IOMUX_CONFIG_SION,
+ (PAD_CTL_SRE_FAST | PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_100K_PU | PAD_CTL_HYS_ENABLE),
+ MUX_IN_I2C2_IPP_SCL_IN_SELECT_INPUT, INPUT_CTL_PATH3,
+ },
+ {
+ MX51_PIN_GPIO1_3, IOMUX_CONFIG_ALT2 | IOMUX_CONFIG_SION,
+ (PAD_CTL_SRE_FAST | PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_100K_PU | PAD_CTL_HYS_ENABLE),
+ MUX_IN_I2C2_IPP_SDA_IN_SELECT_INPUT, INPUT_CTL_PATH3,
+ },
+ {
+ MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_CLK */
+ MX51_PIN_USBH1_CLK, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS),
+ },
+ { /* USBH1_DIR */
+ MX51_PIN_USBH1_DIR, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS),
+ },
+ { /* USBH1_NXT */
+ MX51_PIN_USBH1_NXT, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS),
+ },
+ { /* USBH1_DATA0 */
+ MX51_PIN_USBH1_DATA0, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA1 */
+ MX51_PIN_USBH1_DATA1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA2 */
+ MX51_PIN_USBH1_DATA2, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA3 */
+ MX51_PIN_USBH1_DATA3, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA4 */
+ MX51_PIN_USBH1_DATA4, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA5 */
+ MX51_PIN_USBH1_DATA5, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA6 */
+ MX51_PIN_USBH1_DATA6, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA7 */
+ MX51_PIN_USBH1_DATA7, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USB1_OC */
+ MX51_PIN_GPIO1_9, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_SRE_SLOW | PAD_CTL_DRV_LOW | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_HYS_ENABLE),
+ },
+ { /* USB1_PWR */
+ MX51_PIN_GPIO1_8, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_NONE | PAD_CTL_HYS_ENABLE),
+ },
+ {
+ MX51_PIN_SD1_CMD, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_CLK, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA0, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA2, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA3, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_GPIO1_0, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU),
+ },
+ {
+ MX51_PIN_GPIO1_1, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU),
+ },
+ {
+ MX51_PIN_SD2_CMD, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD2_CLK, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD2_DATA0, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD2_DATA1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD2_DATA2, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD2_DATA3, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_UART1_RXD, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ MUX_IN_UART1_IPP_UART_RXD_MUX_SELECT_INPUT,
+ INPUT_CTL_PATH0,
+ },
+ {
+ MX51_PIN_UART1_TXD, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_UART1_RTS, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH),
+ MUX_IN_UART1_IPP_UART_RTS_B_SELECT_INPUT,
+ INPUT_CTL_PATH0,
+ },
+ {
+ MX51_PIN_UART1_CTS, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH),
+ },
+ {
+ MX51_PIN_UART2_RXD, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ MUX_IN_UART2_IPP_UART_RXD_MUX_SELECT_INPUT,
+ INPUT_CTL_PATH2,
+ },
+ {
+ MX51_PIN_UART2_TXD, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_EIM_D26, IOMUX_CONFIG_ALT4,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ MUX_IN_UART2_IPP_UART_RTS_B_SELECT_INPUT,
+ INPUT_CTL_PATH3,
+ },
+ {
+ MX51_PIN_EIM_D25, IOMUX_CONFIG_ALT4,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_UART3_RXD, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ MUX_IN_UART3_IPP_UART_RXD_MUX_SELECT_INPUT,
+ INPUT_CTL_PATH4,
+ },
+ {
+ MX51_PIN_UART3_TXD, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_EIM_D27, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ MUX_IN_UART3_IPP_UART_RTS_B_SELECT_INPUT,
+ INPUT_CTL_PATH3,
+ },
+ {
+ MX51_PIN_EIM_D24, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ },
+};
+
+static struct mxc_iomux_pin_cfg __initdata ata_iomux_pins[] = {
+ {
+ MX51_PIN_NANDF_ALE, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_CS2, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_CS3, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_CS4, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_CS5, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_CS6, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_RE_B, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_WE_B, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_CLE, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_RB0, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_WP_B, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ /* TO 2.0 */
+ {
+ MX51_PIN_GPIO_NAND, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ /* TO 1.0 */
+ {
+ MX51_PIN_NANDF_RB5, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_RB1, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D0, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D1, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D2, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D3, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D4, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D5, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D6, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D7, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D8, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D9, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D10, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D11, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D12, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D13, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D14, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+ {
+ MX51_PIN_NANDF_D15, IOMUX_CONFIG_ALT1,
+ ATA_PAD_CONFIG,
+ },
+};
+
+static struct mxc_iomux_pin_cfg __initdata nand_iomux_pins[] = {
+ {
+ MX51_PIN_NANDF_CS0, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_NANDF_CS1, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_NANDF_CS2, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_NANDF_CS3, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_NANDF_CS4, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_NANDF_CS5, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_NANDF_CS6, IOMUX_CONFIG_ALT0,
+ },
+ {
+ MX51_PIN_NANDF_CS7, IOMUX_CONFIG_ALT0,
+ },
+ /* TO2 */
+ {
+ MX51_PIN_GPIO_NAND, IOMUX_CONFIG_ALT0,
+ },
+ /* TO1 */
+ {
+ MX51_PIN_NANDF_RB5, IOMUX_CONFIG_ALT0,
+ },
+};
+
+static struct mxc_iomux_pin_cfg __initdata sim_iomux_pins[] = {
+ {
+ MX51_PIN_NANDF_CS4, IOMUX_CONFIG_ALT6,
+ PAD_CTL_DRV_HIGH | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_47K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_PKE_ENABLE,
+ },
+ {
+ MX51_PIN_NANDF_CS5, IOMUX_CONFIG_ALT6,
+ PAD_CTL_DRV_HIGH | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_47K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_PKE_ENABLE,
+ },
+ {
+ MX51_PIN_NANDF_CS6, IOMUX_CONFIG_ALT6,
+ PAD_CTL_DRV_HIGH | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_100K_PD |
+ PAD_CTL_PUE_PULL | PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_PKE_ENABLE,
+ },
+ {
+ MX51_PIN_NANDF_CS7, IOMUX_CONFIG_ALT6,
+ PAD_CTL_DRV_HIGH | PAD_CTL_DRV_VOT_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_22K_PU |
+ PAD_CTL_PUE_PULL | PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_PKE_ENABLE,
+ },
+};
+
+static int __initdata enable_ata = { 0 };
+static int __init ata_setup(char *__unused)
+{
+ enable_ata = 1;
+ return 1;
+}
+
+__setup("ata", ata_setup);
+
+static int __initdata enable_sim = { 0 };
+static int __init sim_setup(char *__unused)
+{
+ enable_sim = 1;
+ return 1;
+}
+
+__setup("sim", sim_setup);
+
+void __init mx51_3stack_io_init(void)
+{
+ int i, num;
+ struct mxc_iomux_pin_cfg *pin_ptr;
+
+ for (i = 0; i < ARRAY_SIZE(mxc_iomux_pins); i++) {
+ mxc_request_iomux(mxc_iomux_pins[i].pin,
+ mxc_iomux_pins[i].mux_mode);
+ if (mxc_iomux_pins[i].pad_cfg)
+ mxc_iomux_set_pad(mxc_iomux_pins[i].pin,
+ mxc_iomux_pins[i].pad_cfg);
+ if (mxc_iomux_pins[i].in_select)
+ mxc_iomux_set_input(mxc_iomux_pins[i].in_select,
+ mxc_iomux_pins[i].in_mode);
+ }
+
+ if (enable_ata) {
+ pin_ptr = ata_iomux_pins;
+ num = ARRAY_SIZE(ata_iomux_pins);
+ } else if (enable_sim) {
+ pin_ptr = sim_iomux_pins;
+ num = ARRAY_SIZE(sim_iomux_pins);
+ } else {
+ pin_ptr = nand_iomux_pins;
+ num = ARRAY_SIZE(nand_iomux_pins);
+ }
+
+ for (i = 0; i < num; i++) {
+ mxc_request_iomux(pin_ptr[i].pin, pin_ptr[i].mux_mode);
+ if (pin_ptr[i].pad_cfg)
+ mxc_iomux_set_pad(pin_ptr[i].pin, pin_ptr[i].pad_cfg);
+ if (pin_ptr[i].in_select)
+ mxc_iomux_set_input(pin_ptr[i].in_select,
+ pin_ptr[i].in_mode);
+ }
+
+ /* TO3 doesn't need pad to drive CSI_DATA_EN[0] high */
+ if (cpu_is_mx51_rev(CHIP_REV_3_0) > 0)
+ mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT0);
+
+ /* Camera low power */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_CSI1_D8), "csi1_d8");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_CSI1_D8), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI1_D8), 0);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_EB2), "eim_eb2");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_EB2), 0); /* TO1 */
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_EB2), 0); /* TO1 */
+
+ /* Camera reset */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_CSI1_D9), "csi1_d9");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_CSI1_D9), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI1_D9), 1);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_DI1_D1_CS), "di1_d1_cs");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DI1_D1_CS), 0);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_0), "gpio1_0");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_0)); /* SD1 CD */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_1), "gpio1_1");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_1)); /* SD1 WP */
+
+ /* EIM_D16 */
+ /* osc_en is shared by SPDIF */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_D16), "eim_d16");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D16), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D16), 1);
+
+ /* LCD related gpio */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_DI1_D1_CS), "di1_d1_cs");
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIO), "dispb2_ser_di0");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DI1_D1_CS), 0);
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIO), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIO), 0);
+
+ /* GPS related gpio */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_CS2), "eim_cs2");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_CS2), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_CS2), 0);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_CRE), "eim_cre");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_CRE), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_CRE), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_CRE), 1);
+}
+
diff --git a/arch/arm/mach-mx51/mx51_3stack_pmic_mc13892.c b/arch/arm/mach-mx51/mx51_3stack_pmic_mc13892.c
new file mode 100644
index 000000000000..b84bb1ee05d4
--- /dev/null
+++ b/arch/arm/mach-mx51/mx51_3stack_pmic_mc13892.c
@@ -0,0 +1,352 @@
+/*
+ * mx51-3stack-pmic-mc13892.c -- i.MX51 3STACK Driver for Atlas MC13892 PMIC
+ */
+ /*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+ /*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/pmic_external.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/mc13892/core.h>
+#include "iomux.h"
+
+/*
+ * Convenience conversion.
+ * Here atm, maybe there is somewhere better for this.
+ */
+#define mV_to_uV(mV) (mV * 1000)
+#define uV_to_mV(uV) (uV / 1000)
+#define V_to_uV(V) (mV_to_uV(V * 1000))
+#define uV_to_V(uV) (uV_to_mV(uV) / 1000)
+
+#define STANDBYSECINV_LSH 11
+#define STANDBYSECINV_WID 1
+
+/* Coin cell charger enable */
+#define CIONCHEN_LSH 23
+#define CIONCHEN_WID 1
+/* Coin cell charger voltage setting */
+#define VCOIN_LSH 20
+#define VCOIN_WID 3
+
+/* Coin Charger voltage */
+#define VCOIN_2_5V 0x0
+#define VCOIN_2_7V 0x1
+#define VCOIN_2_8V 0x2
+#define VCOIN_2_9V 0x3
+#define VCOIN_3_0V 0x4
+#define VCOIN_3_1V 0x5
+#define VCOIN_3_2V 0x6
+#define VCOIN_3_3V 0x7
+
+/* Keeps VSRTC and CLK32KMCU on for all states */
+#define DRM_LSH 4
+#define DRM_WID 1
+
+/* CPU */
+static struct regulator_consumer_supply sw1_consumers[] = {
+ {
+ .supply = "cpu_vcc",
+ }
+};
+
+struct mc13892;
+
+static struct regulator_init_data sw1_init = {
+ .constraints = {
+ .name = "SW1",
+ .min_uV = mV_to_uV(600),
+ .max_uV = mV_to_uV(1375),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .valid_modes_mask = 0,
+ .always_on = 1,
+ .boot_on = 1,
+ .initial_state = PM_SUSPEND_MEM,
+ .state_mem = {
+ .uV = 850000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ },
+ .num_consumer_supplies = ARRAY_SIZE(sw1_consumers),
+ .consumer_supplies = sw1_consumers,
+};
+
+static struct regulator_init_data sw2_init = {
+ .constraints = {
+ .name = "SW2",
+ .min_uV = mV_to_uV(900),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ .initial_state = PM_SUSPEND_MEM,
+ .state_mem = {
+ .uV = 950000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ }
+};
+
+static struct regulator_init_data sw3_init = {
+ .constraints = {
+ .name = "SW3",
+ .min_uV = mV_to_uV(1100),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data sw4_init = {
+ .constraints = {
+ .name = "SW4",
+ .min_uV = mV_to_uV(1100),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data viohi_init = {
+ .constraints = {
+ .name = "VIOHI",
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vusb_init = {
+ .constraints = {
+ .name = "VUSB",
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data swbst_init = {
+ .constraints = {
+ .name = "SWBST",
+ }
+};
+
+static struct regulator_init_data vdig_init = {
+ .constraints = {
+ .name = "VDIG",
+ .min_uV = mV_to_uV(1050),
+ .max_uV = mV_to_uV(1800),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vpll_init = {
+ .constraints = {
+ .name = "VPLL",
+ .min_uV = mV_to_uV(1050),
+ .max_uV = mV_to_uV(1800),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vusb2_init = {
+ .constraints = {
+ .name = "VUSB2",
+ .min_uV = mV_to_uV(2400),
+ .max_uV = mV_to_uV(2775),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vvideo_init = {
+ .constraints = {
+ .name = "VVIDEO",
+ .min_uV = mV_to_uV(2500),
+ .max_uV = mV_to_uV(2775),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vaudio_init = {
+ .constraints = {
+ .name = "VAUDIO",
+ .min_uV = mV_to_uV(2300),
+ .max_uV = mV_to_uV(3000),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vsd_init = {
+ .constraints = {
+ .name = "VSD",
+ .min_uV = mV_to_uV(1800),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vcam_init = {
+ .constraints = {
+ .name = "VCAM",
+ .min_uV = mV_to_uV(2500),
+ .max_uV = mV_to_uV(3000),
+ .valid_ops_mask =
+ REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL,
+ }
+};
+
+static struct regulator_init_data vgen1_init = {
+ .constraints = {
+ .name = "VGEN1",
+ .min_uV = mV_to_uV(1200),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vgen2_init = {
+ .constraints = {
+ .name = "VGEN2",
+ .min_uV = mV_to_uV(1200),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vgen3_init = {
+ .constraints = {
+ .name = "VGEN3",
+ .min_uV = mV_to_uV(1800),
+ .max_uV = mV_to_uV(2900),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data gpo1_init = {
+ .constraints = {
+ .name = "GPO1",
+ }
+};
+
+static struct regulator_init_data gpo2_init = {
+ .constraints = {
+ .name = "GPO2",
+ }
+};
+
+static struct regulator_init_data gpo3_init = {
+ .constraints = {
+ .name = "GPO3",
+ }
+};
+
+static struct regulator_init_data gpo4_init = {
+ .constraints = {
+ .name = "GPO4",
+ }
+};
+
+/*!
+ * the event handler for power on event
+ */
+static void power_on_evt_handler(void)
+{
+ pr_info("pwr on event1 is received \n");
+}
+
+static int mc13892_regulator_init(struct mc13892 *mc13892)
+{
+ unsigned int value;
+ pmic_event_callback_t power_key_event;
+ int register_mask;
+
+ printk("Initializing regulators for 3-stack.\n");
+ if (mxc_cpu_is_rev(CHIP_REV_2_0) < 0)
+ sw2_init.constraints.state_mem.uV = 1100000;
+ else if (mxc_cpu_is_rev(CHIP_REV_2_0) == 1) {
+ sw2_init.constraints.state_mem.uV = 1250000;
+ sw1_init.constraints.state_mem.uV = 1000000;
+ }
+
+ /* subscribe PWRON1 event to enable ON_OFF key */
+ power_key_event.param = NULL;
+ power_key_event.func = (void *)power_on_evt_handler;
+ pmic_event_subscribe(EVENT_PWRONI, power_key_event);
+
+ /* Bit 4 DRM: keep VSRTC and CLK32KMCU on for all states */
+#if defined(CONFIG_RTC_DRV_MXC_V2) || defined(CONFIG_RTC_DRV_MXC_V2_MODULE)
+ value = BITFVAL(DRM, 1);
+ register_mask = BITFMASK(DRM);
+ pmic_write_reg(REG_POWER_CTL0, value, register_mask);
+#endif
+ /* Set the STANDBYSECINV bit, so that STANDBY pin is
+ * interpreted as active low.
+ */
+ value = BITFVAL(STANDBYSECINV, 1);
+ register_mask = BITFMASK(STANDBYSECINV);
+ pmic_write_reg(REG_POWER_CTL2, value, register_mask);
+
+ /* Enable coin cell charger */
+ value = BITFVAL(CIONCHEN, 1) | BITFVAL(VCOIN, VCOIN_3_0V);
+ register_mask = BITFMASK(CIONCHEN) | BITFMASK(VCOIN);
+ pmic_write_reg(REG_POWER_CTL0, value, register_mask);
+
+ mc13892_register_regulator(mc13892, MC13892_SW1, &sw1_init);
+ mc13892_register_regulator(mc13892, MC13892_SW2, &sw2_init);
+ mc13892_register_regulator(mc13892, MC13892_SW3, &sw3_init);
+ mc13892_register_regulator(mc13892, MC13892_SW4, &sw4_init);
+ mc13892_register_regulator(mc13892, MC13892_SWBST, &swbst_init);
+ mc13892_register_regulator(mc13892, MC13892_VIOHI, &viohi_init);
+ mc13892_register_regulator(mc13892, MC13892_VPLL, &vpll_init);
+ mc13892_register_regulator(mc13892, MC13892_VDIG, &vdig_init);
+ mc13892_register_regulator(mc13892, MC13892_VSD, &vsd_init);
+ mc13892_register_regulator(mc13892, MC13892_VUSB2, &vusb2_init);
+ mc13892_register_regulator(mc13892, MC13892_VVIDEO, &vvideo_init);
+ mc13892_register_regulator(mc13892, MC13892_VAUDIO, &vaudio_init);
+ mc13892_register_regulator(mc13892, MC13892_VCAM, &vcam_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN1, &vgen1_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN2, &vgen2_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN3, &vgen3_init);
+ mc13892_register_regulator(mc13892, MC13892_VUSB, &vusb_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO1, &gpo1_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO2, &gpo2_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO3, &gpo3_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO4, &gpo4_init);
+
+ return 0;
+}
+
+static struct mc13892_platform_data mc13892_plat = {
+ .init = mc13892_regulator_init,
+};
+
+static struct i2c_board_info __initdata mc13892_i2c_device = {
+ I2C_BOARD_INFO("mc13892", 0x08),
+ .irq = MX51_PIN_GPIO1_5,
+ .platform_data = &mc13892_plat,
+};
+
+int __init mx51_3stack_init_mc13892(void)
+{
+ return i2c_register_board_info(1, &mc13892_i2c_device, 1);
+}
+
diff --git a/arch/arm/mach-mx51/mx51_babbage.c b/arch/arm/mach-mx51/mx51_babbage.c
new file mode 100644
index 000000000000..9b28b90ce3ed
--- /dev/null
+++ b/arch/arm/mach-mx51/mx51_babbage.c
@@ -0,0 +1,1069 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/nodemask.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/ata.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/spi/flash.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/spba.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <asm/mach/keypad.h>
+#include <mach/memory.h>
+#include <mach/gpio.h>
+#include <mach/mmc.h>
+#include "board-mx51_babbage.h"
+#include "iomux.h"
+#include "crm_regs.h"
+#include <mach/mxc_edid.h>
+
+/*!
+ * @file mach-mx51/mx51_babbage.c
+ *
+ * @brief This file contains the board specific initialization routines.
+ *
+ * @ingroup MSL_MX51
+ */
+extern void __init mx51_babbage_io_init(void);
+extern struct cpu_wp *(*get_cpu_wp)(int *wp);
+extern void (*set_num_cpu_wp)(int num);
+static int num_cpu_wp = 3;
+
+/* working point(wp): 0 - 800MHz; 1 - 166.25MHz; */
+static struct cpu_wp cpu_wp_auto[] = {
+ {
+ .pll_rate = 1000000000,
+ .cpu_rate = 1000000000,
+ .pdf = 0,
+ .mfi = 10,
+ .mfd = 11,
+ .mfn = 5,
+ .cpu_podf = 0,
+ .cpu_voltage = 1175000,},
+ {
+ .pll_rate = 800000000,
+ .cpu_rate = 800000000,
+ .pdf = 0,
+ .mfi = 8,
+ .mfd = 2,
+ .mfn = 1,
+ .cpu_podf = 0,
+ .cpu_voltage = 1100000,},
+ {
+ .pll_rate = 800000000,
+ .cpu_rate = 166250000,
+ .pdf = 4,
+ .mfi = 8,
+ .mfd = 2,
+ .mfn = 1,
+ .cpu_podf = 4,
+ .cpu_voltage = 850000,},
+};
+
+static struct fb_videomode video_modes[] = {
+ {
+ /* 720p60 TV output */
+ "720P60", 60, 1280, 720, 7418,
+ 220, 110,
+ 20, 5,
+ 40, 5,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_EXT,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* MITSUBISHI LVDS panel */
+ "XGA", 60, 1024, 768, 15385,
+ 220, 40,
+ 21, 7,
+ 60, 10,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+};
+
+struct cpu_wp *mx51_babbage_get_cpu_wp(int *wp)
+{
+ *wp = num_cpu_wp;
+ return cpu_wp_auto;
+}
+
+void mx51_babbage_set_num_cpu_wp(int num)
+{
+ num_cpu_wp = num;
+ return;
+}
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+#if defined(CONFIG_KEYBOARD_MXC) || defined(CONFIG_KEYBOARD_MXC_MODULE)
+static u16 keymapping[24] = {
+ KEY_1, KEY_2, KEY_3, KEY_F1, KEY_UP, KEY_F2,
+ KEY_4, KEY_5, KEY_6, KEY_LEFT, KEY_SELECT, KEY_RIGHT,
+ KEY_7, KEY_8, KEY_9, KEY_F3, KEY_DOWN, KEY_F4,
+ KEY_0, KEY_OK, KEY_ESC, KEY_ENTER, KEY_MENU, KEY_BACK,
+};
+
+static struct resource mxc_kpp_resources[] = {
+ [0] = {
+ .start = MXC_INT_KPP,
+ .end = MXC_INT_KPP,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct keypad_data keypad_plat_data = {
+ .rowmax = 4,
+ .colmax = 6,
+ .irq = MXC_INT_KPP,
+ .learning = 0,
+ .delay = 2,
+ .matrix = keymapping,
+};
+
+/* mxc keypad driver */
+static struct platform_device mxc_keypad_device = {
+ .name = "mxc_keypad",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_kpp_resources),
+ .resource = mxc_kpp_resources,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &keypad_plat_data,
+ },
+};
+
+static void mxc_init_keypad(void)
+{
+ (void)platform_device_register(&mxc_keypad_device);
+}
+#else
+static inline void mxc_init_keypad(void)
+{
+}
+#endif
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || \
+ defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+static struct resource mxcfb_resources[] = {
+ [0] = {
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct mxc_fb_platform_data fb_data[] = {
+ {
+ .interface_pix_fmt = IPU_PIX_FMT_RGB24,
+ .mode_str = "1024x768M-16@60",
+ },
+ {
+ .interface_pix_fmt = IPU_PIX_FMT_RGB565,
+ .mode_str = "1024x768M-16@60",
+ },
+};
+
+static struct platform_device mxc_fb_device[] = {
+ {
+ .name = "mxc_sdc_fb",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ .platform_data = &fb_data[0],
+ },
+ .num_resources = ARRAY_SIZE(mxcfb_resources),
+ .resource = mxcfb_resources,
+ },
+ {
+ .name = "mxc_sdc_fb",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ .platform_data = &fb_data[1],
+ },
+ },
+ {
+ .name = "mxc_sdc_fb",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+ },
+};
+
+static int __initdata enable_vga = { 0 };
+static int __initdata enable_wvga = { 0 };
+static int __initdata enable_tv = { 0 };
+static int __initdata enable_mitsubishi_xga = { 0 };
+
+static void wvga_reset(void)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DI1_D1_CS), 1);
+}
+
+static struct mxc_lcd_platform_data lcd_wvga_data = {
+ .reset = wvga_reset,
+};
+
+static struct platform_device lcd_wvga_device = {
+ .name = "lcd_claa",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &lcd_wvga_data,
+ },
+};
+
+static int handle_edid(int *pixclk)
+{
+ int err = 0;
+ int dvi = 0;
+ int fb0 = 0;
+ int fb1 = 1;
+ struct fb_var_screeninfo screeninfo;
+ struct i2c_adapter *adp;
+
+ memset(&screeninfo, 0, sizeof(screeninfo));
+
+ adp = i2c_get_adapter(1);
+
+ if (cpu_is_mx51_rev(CHIP_REV_3_0) > 0) {
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI2_HSYNC), 1);
+ msleep(1);
+ }
+ err = read_edid(adp, &screeninfo, &dvi);
+ if (cpu_is_mx51_rev(CHIP_REV_3_0) > 0)
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI2_HSYNC), 0);
+
+ if (!err) {
+ printk(KERN_INFO " EDID read\n");
+ if (!dvi) {
+ enable_vga = 1;
+ fb0 = 1; /* fb0 will be VGA */
+ fb1 = 0; /* fb1 will be DVI or TV */
+ }
+
+ /* Handle TV modes */
+ /* This logic is fairly complex yet still doesn't handle all
+ possibilities. Once a customer knows the platform
+ configuration, this should be simplified to what is desired.
+ */
+ if (screeninfo.xres == 1920 && screeninfo.yres != 1200) {
+ /* MX51 can't handle clock speeds for anything larger.*/
+ if (!enable_tv)
+ enable_tv = 1;
+ if (enable_vga || enable_wvga || enable_tv == 2)
+ enable_tv = 2;
+ fb_data[0].mode = &(video_modes[0]);
+ if (!enable_wvga)
+ fb_data[1].mode_str = "800x600M-16@60";
+ } else if (screeninfo.xres > 1280 && screeninfo.yres > 1024) {
+ if (!enable_wvga) {
+ fb_data[fb0].mode_str = "1280x1024M-16@60";
+ fb_data[fb1].mode_str = NULL;
+ } else {
+ /* WVGA is preset so the DVI can't be > this. */
+ fb_data[0].mode_str = "1024x768M-16@60";
+ }
+ } else if (screeninfo.xres > 0 && screeninfo.yres > 0) {
+ if (!enable_wvga) {
+ fb_data[fb0].mode =
+ kzalloc(sizeof(struct fb_videomode),
+ GFP_KERNEL);
+ fb_var_to_videomode(fb_data[fb0].mode,
+ &screeninfo);
+ fb_data[fb0].mode_str = NULL;
+ if (screeninfo.xres >= 1280 &&
+ screeninfo.yres > 720)
+ fb_data[fb1].mode_str = NULL;
+ else if (screeninfo.xres > 1024 &&
+ screeninfo.yres > 768)
+ fb_data[fb1].mode_str =
+ "800x600M-16@60";
+ else if (screeninfo.xres > 800 &&
+ screeninfo.yres > 600)
+ fb_data[fb1].mode_str =
+ "1024x768M-16@60";
+ } else {
+ /* A WVGA panel was specified and an EDID was
+ read thus there is a DVI monitor attached. */
+ if (screeninfo.xres >= 1024)
+ fb_data[0].mode_str = "1024x768M-16@60";
+ else if (screeninfo.xres >= 800)
+ fb_data[0].mode_str = "800x600M-16@60";
+ else
+ fb_data[0].mode_str = "640x480M-16@60";
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int __init mxc_init_fb(void)
+{
+ int pixclk = 0;
+
+ if (!machine_is_mx51_babbage())
+ return 0;
+
+ if (cpu_is_mx51_rev(CHIP_REV_1_1) == 1) {
+ enable_vga = 1;
+ fb_data[0].mode_str = NULL;
+ fb_data[1].mode_str = NULL;
+ }
+
+ if (enable_wvga) {
+ fb_data[1].interface_pix_fmt = IPU_PIX_FMT_RGB565;
+ fb_data[1].mode_str = "800x480M-16@55";
+ }
+
+ if (enable_mitsubishi_xga) {
+ fb_data[0].interface_pix_fmt = IPU_PIX_FMT_LVDS666;
+ fb_data[0].mode = &(video_modes[1]);
+
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DI1_D0_CS), 0);
+ msleep(1);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DI1_D0_CS), 1);
+
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI2_D12), 1);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI2_D13), 1);
+ }
+
+ /* DVI Detect */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_NANDF_D12), "nandf_d12");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_NANDF_D12));
+ /* DVI Reset - Assert for i2c disabled mode */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIN), "dispb2_ser_din");
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIN), 0);
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIN), 0);
+ /* DVI Power-down */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIO), "dispb2_ser_di0");
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIO), 1);
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIO), 0);
+
+ (void)platform_device_register(&lcd_wvga_device);
+
+#if 0
+ if (cpu_is_mx51_rev(CHIP_REV_1_1) == 2)
+ handle_edid(&pixclk);
+#endif
+
+ if (enable_vga)
+ printk(KERN_INFO "VGA monitor is primary\n");
+ else if (enable_wvga)
+ printk(KERN_INFO "WVGA LCD panel is primary\n");
+ else if (!enable_tv)
+ printk(KERN_INFO "DVI monitor is primary\n");
+
+ if (enable_tv) {
+ printk(KERN_INFO "TV is specified as %d\n", enable_tv);
+ if (!fb_data[0].mode) {
+ fb_data[0].mode = &(video_modes[0]);
+ if (!enable_wvga)
+ fb_data[1].mode_str = "800x600M-16@60";
+ }
+ }
+
+ if (enable_tv) {
+ struct clk *clk, *di_clk;
+ clk = clk_get(NULL, "pll3");
+ di_clk = clk_get(NULL, "ipu_di0_clk");
+ clk_disable(clk);
+ clk_disable(di_clk);
+ clk_set_rate(clk, 297000000);
+ clk_set_rate(di_clk, 297000000 / 4);
+ clk_enable(clk);
+ clk_enable(di_clk);
+ clk_put(di_clk);
+ clk_put(clk);
+ }
+
+ /* Once a customer knows the platform configuration,
+ this should be simplified to what is desired.
+ */
+ if (enable_vga || enable_wvga || enable_tv == 2) {
+ (void)platform_device_register(&mxc_fb_device[1]); /* VGA */
+ if (fb_data[0].mode_str || fb_data[0].mode)
+ (void)platform_device_register(&mxc_fb_device[0]);
+ } else {
+ (void)platform_device_register(&mxc_fb_device[0]); /* DVI */
+ if (fb_data[1].mode_str || fb_data[1].mode)
+ (void)platform_device_register(&mxc_fb_device[1]);
+ }
+
+ (void)platform_device_register(&mxc_fb_device[2]);
+
+ return 0;
+}
+device_initcall(mxc_init_fb);
+
+static int __init vga_setup(char *__unused)
+{
+ enable_vga = 1;
+ return 1;
+}
+
+__setup("vga", vga_setup);
+
+static int __init wvga_setup(char *__unused)
+{
+ enable_wvga = 1;
+ return 1;
+}
+
+__setup("wvga", wvga_setup);
+
+static int __init mitsubishi_xga_setup(char *__unused)
+{
+ enable_mitsubishi_xga = 1;
+ return 1;
+}
+
+__setup("mitsubishi_xga", mitsubishi_xga_setup);
+
+static int __init tv_setup(char *s)
+{
+ enable_tv = 1;
+ if (strcmp(s, "2") == 0 || strcmp(s, "=2") == 0)
+ enable_tv = 2;
+ return 1;
+}
+
+__setup("tv", tv_setup);
+#else
+static inline void mxc_init_fb(void)
+{
+}
+#endif
+
+static void dvi_reset(void)
+{
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIN), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIN), 0);
+ msleep(50);
+
+ /* do reset */
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIN), 1);
+ msleep(20); /* tRES >= 50us */
+
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_DIN), 0);
+}
+
+static struct mxc_lcd_platform_data dvi_data = {
+ .core_reg = "VGEN1",
+ .io_reg = "VGEN3",
+ .reset = dvi_reset,
+};
+
+static void vga_reset(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_A19), "eim_a19");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A19), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A19), 0);
+ msleep(50);
+ /* do reset */
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A19), 1);
+ msleep(10); /* tRES >= 50us */
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A19), 0);
+}
+
+static struct mxc_lcd_platform_data vga_data = {
+ .core_reg = "VCAM",
+ .io_reg = "VGEN3",
+ .analog_reg = "VAUDIO",
+ .reset = vga_reset,
+};
+
+static void si4702_reset(void)
+{
+ return;
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A21), 0);
+ msleep(100);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A21), 1);
+ msleep(100);
+}
+
+static void si4702_clock_ctl(int flag)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A18), flag);
+ msleep(100);
+}
+
+static void si4702_gpio_get(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_A18), "eim_a18");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A18), 0);
+}
+
+static void si4702_gpio_put(void)
+{
+}
+
+static struct mxc_fm_platform_data si4702_data = {
+ .reg_vio = "SW4",
+ .reg_vdd = "VIOHI",
+ .gpio_get = si4702_gpio_get,
+ .gpio_put = si4702_gpio_put,
+ .reset = si4702_reset,
+ .clock_ctl = si4702_clock_ctl,
+};
+
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+static struct mxc_camera_platform_data camera_data = {
+ .io_regulator = "SW4",
+ .analog_regulator = "VIOHI",
+ .mclk = 24000000,
+ .csi = 0,
+};
+
+static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {
+ {
+ .type = "ov3640",
+ .addr = 0x3C,
+ .platform_data = (void *)&camera_data,
+ },
+};
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT2
+static struct i2c_board_info mxc_i2c1_board_info[] __initdata = {
+ {
+ .type = "sgtl5000-i2c",
+ .addr = 0x0a,
+ },
+};
+#endif
+
+#if defined(CONFIG_I2C_MXC_HS) || defined(CONFIG_I2C_MXC_HS_MODULE)
+static struct i2c_board_info mxc_i2c_hs_board_info[] __initdata = {
+ {
+ .type = "sii9022",
+ .addr = 0x39,
+ .platform_data = &dvi_data,
+ },
+ {
+ .type = "ch7026",
+ .addr = 0x75,
+ .platform_data = &vga_data,
+ },
+ {
+ .type = "si4702",
+ .addr = 0x10,
+ .platform_data = (void *)&si4702_data,
+ },
+};
+#endif
+
+#endif
+
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+static struct mtd_partition mxc_spi_nor_partitions[] = {
+ {
+ .name = "bootloader",
+ .offset = 0,
+ .size = 0x00040000,},
+ {
+ .name = "kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,},
+
+};
+
+static struct mtd_partition mxc_dataflash_partitions[] = {
+ {
+ .name = "bootloader",
+ .offset = 0,
+ .size = 0x000100000,},
+ {
+ .name = "kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,},
+};
+
+static struct flash_platform_data mxc_spi_flash_data[] = {
+ {
+ .name = "mxc_spi_nor",
+ .parts = mxc_spi_nor_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_spi_nor_partitions),
+ .type = "sst25vf016b",},
+ {
+ .name = "mxc_dataflash",
+ .parts = mxc_dataflash_partitions,
+ .nr_parts = ARRAY_SIZE(mxc_dataflash_partitions),
+ .type = "at45db321d",}
+};
+#endif
+
+static struct spi_board_info mxc_spi_nor_device[] __initdata = {
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+ {
+ .modalias = "mxc_spi_nor",
+ .max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 1,
+ .chip_select = 1,
+ .platform_data = &mxc_spi_flash_data[0],},
+#endif
+};
+
+static struct spi_board_info mxc_dataflash_device[] __initdata = {
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+ {
+ .modalias = "mxc_dataflash",
+ .max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 1,
+ .chip_select = 1,
+ .platform_data = &mxc_spi_flash_data[1],},
+#endif
+};
+
+#if defined(CONFIG_FEC) || defined(CONFIG_FEC_MODULE)
+unsigned int expio_intr_fec;
+
+EXPORT_SYMBOL(expio_intr_fec);
+#endif
+
+#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
+static int sdhc_write_protect(struct device *dev)
+{
+ unsigned short rc = 0;
+
+ if (to_platform_device(dev)->id == 0)
+ rc = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_1));
+ else
+ rc = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5));
+
+ return rc;
+}
+
+static unsigned int sdhc_get_card_det_status(struct device *dev)
+{
+ int ret;
+
+ if (to_platform_device(dev)->id == 0) {
+ ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_0));
+ return ret;
+ } else { /* config the det pin for SDHC2 */
+ if (board_is_babbage_2_5() == 1)
+ /* BB2.5 */
+ ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_6));
+ else
+ /* BB2.0 */
+ ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_4));
+ return ret;
+ }
+}
+
+static struct mxc_mmc_platform_data mmc1_data = {
+ .ocr_mask = MMC_VDD_31_32,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 400000,
+ .max_clk = 52000000,
+ .card_inserted_state = 1,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "esdhc_clk",
+ .power_mmc = NULL,
+};
+
+static struct mxc_mmc_platform_data mmc2_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 |
+ MMC_VDD_31_32,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 150000,
+ .max_clk = 50000000,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "esdhc_clk",
+};
+
+/*!
+ * Resource definition for the SDHC1
+ */
+static struct resource mxcsdhc1_resources[] = {
+ [0] = {
+ .start = MMC_SDHC1_BASE_ADDR,
+ .end = MMC_SDHC1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC1,
+ .end = MXC_INT_MMC_SDHC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IOMUX_TO_IRQ(MX51_PIN_GPIO1_0),
+ .end = IOMUX_TO_IRQ(MX51_PIN_GPIO1_0),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*!
+ * Resource definition for the SDHC2
+ */
+static struct resource mxcsdhc2_resources[] = {
+ [0] = {
+ .start = MMC_SDHC2_BASE_ADDR,
+ .end = MMC_SDHC2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC2,
+ .end = MXC_INT_MMC_SDHC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IOMUX_TO_IRQ(MX51_PIN_GPIO1_6),
+ .end = IOMUX_TO_IRQ(MX51_PIN_GPIO1_6),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC1 */
+static struct platform_device mxcsdhc1_device = {
+ .name = "mxsdhci",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc1_resources),
+ .resource = mxcsdhc1_resources,
+};
+
+/*! Device Definition for MXC SDHC2 */
+static struct platform_device mxcsdhc2_device = {
+ .name = "mxsdhci",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc2_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc2_resources),
+ .resource = mxcsdhc2_resources,
+};
+
+static inline void mxc_init_mmc(void)
+{
+ if (board_is_babbage_2_5() == 1) {
+ /* BB2.5 */
+ mxcsdhc2_resources[2].start =
+ IOMUX_TO_IRQ(MX51_PIN_GPIO1_6); /* SD2 CD */
+ mxcsdhc2_resources[2].end =
+ IOMUX_TO_IRQ(MX51_PIN_GPIO1_6); /* SD2 CD */
+ }
+
+ (void)platform_device_register(&mxcsdhc1_device);
+ (void)platform_device_register(&mxcsdhc2_device);
+}
+#else
+static inline void mxc_init_mmc(void)
+{
+}
+#endif
+
+#if defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000) \
+ || defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000_MODULE)
+static int mxc_sgtl5000_amp_enable(int enable);
+
+static int headphone_det_status(void)
+{
+ if (cpu_is_mx51_rev(CHIP_REV_1_1) == 2)
+ return (gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_NANDF_D14)) == 0);
+
+ return gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_NANDF_CS0));
+}
+
+static struct mxc_audio_platform_data sgtl5000_data = {
+ .ssi_num = 1,
+ .src_port = 2,
+ .ext_port = 3,
+ .hp_irq = IOMUX_TO_IRQ(MX51_PIN_NANDF_CS0),
+ .hp_status = headphone_det_status,
+ .vddio_reg = "VVIDEO",
+ .vdda_reg = "VDIG",
+ .vddd_reg = "VGEN1",
+ .amp_enable = mxc_sgtl5000_amp_enable,
+ .vddio = 2775000,
+ .vdda = 1650000,
+ .vddd = 1200000,
+ .sysclk = 12288000,
+};
+
+static struct platform_device mxc_sgtl5000_device = {
+ .name = "imx-3stack-sgtl5000",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &sgtl5000_data,
+ },
+};
+
+static int mxc_sgtl5000_amp_enable(int enable)
+{
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A23), enable ? 1 : 0);
+ return 0;
+}
+
+static void mxc_init_sgtl5000(void)
+{
+ if (cpu_is_mx51_rev(CHIP_REV_1_1) == 2) {
+ sgtl5000_data.sysclk = 26000000;
+ sgtl5000_data.vddd_reg = NULL;
+ sgtl5000_data.vddd = 0;
+ }
+
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_A23), "eim_a23");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A23), 0);
+
+ platform_device_register(&mxc_sgtl5000_device);
+}
+#else
+static inline void mxc_init_sgtl5000(void)
+{
+}
+#endif
+
+#if defined(CONFIG_GPIO_BUTTON_MXC) || \
+ defined(CONFIG_GPIO_BUTTON_MXC_MODULE)
+
+#define MXC_BUTTON_GPIO_PIN MX51_PIN_EIM_DTACK
+
+static struct mxc_gpio_button_data gpio_button_data = {
+ .name = "Power Button (CM)",
+ .gpio = MXC_BUTTON_GPIO_PIN,
+ .irq = IOMUX_TO_IRQ(MXC_BUTTON_GPIO_PIN),
+ .key = KEY_POWER,
+};
+
+static struct platform_device gpio_button_device = {
+ .name = "gpio_button",
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &gpio_button_data,
+ },
+};
+
+static inline void mxc_init_gpio_button(void)
+{
+ gpio_request(IOMUX_TO_GPIO(MXC_BUTTON_GPIO_PIN), "button");
+ gpio_direction_input(IOMUX_TO_GPIO(MXC_BUTTON_GPIO_PIN));
+ platform_device_register(&gpio_button_device);
+}
+#else
+static inline void mxc_init_gpio_button(void)
+{
+}
+#endif
+
+/*!
+ * Board specific fixup function. It is called by \b setup_arch() in
+ * setup.c file very early on during kernel starts. It allows the user to
+ * statically fill in the proper values for the passed-in parameters. None of
+ * the parameters is used currently.
+ *
+ * @param desc pointer to \b struct \b machine_desc
+ * @param tags pointer to \b struct \b tag
+ * @param cmdline pointer to the command line
+ * @param mi pointer to \b struct \b meminfo
+ */
+static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ char *str;
+ int size = SZ_512M - SZ_32M;
+ struct tag *t;
+
+ mxc_cpu_init();
+
+ get_cpu_wp = mx51_babbage_get_cpu_wp;
+ set_num_cpu_wp = mx51_babbage_set_num_cpu_wp;
+
+ for_each_tag(t, tags) {
+ if (t->hdr.tag != ATAG_CMDLINE)
+ continue;
+ str = t->u.cmdline.cmdline;
+ str = strstr(str, "mem=");
+ if (str != NULL) {
+ str += 4;
+ size = memparse(str, &str);
+ if (size == 0 || size == SZ_512M)
+ return;
+ }
+ }
+
+ for_each_tag(t, tags) {
+ if (t->hdr.tag != ATAG_MEM)
+ continue;
+
+ t->u.mem.size = size;
+ mxcfb_resources[0].start = t->u.mem.start + size;
+ mxcfb_resources[0].end = t->u.mem.start + SZ_512M - 1;
+ }
+}
+
+#define PWGT1SPIEN (1<<15)
+#define PWGT2SPIEN (1<<16)
+#define USEROFFSPI (1<<3)
+
+static void mxc_power_off(void)
+{
+ /* We can do power down one of two ways:
+ Set the power gating
+ Set USEROFFSPI */
+
+ /* Set the power gate bits to power down */
+ pmic_write_reg(REG_POWER_MISC, (PWGT1SPIEN|PWGT2SPIEN),
+ (PWGT1SPIEN|PWGT2SPIEN));
+}
+
+/*!
+ * Power Key interrupt handler.
+ */
+static irqreturn_t power_key_int(int irq, void *dev_id)
+{
+ pr_info(KERN_INFO "PWR key pressed\n");
+ return 0;
+}
+
+/*!
+ * Power Key initialization.
+ */
+static int __init mxc_init_power_key(void)
+{
+ /* Set power key as wakeup resource */
+ int irq, ret;
+ irq = IOMUX_TO_IRQ(MX51_PIN_EIM_A27);
+ set_irq_type(irq, IRQF_TRIGGER_RISING);
+ ret = request_irq(irq, power_key_int, 0, "power_key", 0);
+ if (ret)
+ pr_info("register on-off key interrupt failed\n");
+ else
+ enable_irq_wake(irq);
+ return ret;
+}
+
+late_initcall(mxc_init_power_key);
+
+/*!
+ * Board specific initialization.
+ */
+static void __init mxc_board_init(void)
+{
+ mxc_cpu_common_init();
+ mxc_register_gpios();
+ mx51_babbage_io_init();
+ early_console_setup(saved_command_line);
+
+ mxc_init_devices();
+
+ mxc_init_keypad();
+ mxc_init_mmc();
+ mxc_init_gpio_button();
+ mx51_babbage_init_mc13892();
+
+ if (board_is_babbage_2_5() == 1)
+ /* BB2.5 */
+ spi_register_board_info(mxc_dataflash_device,
+ ARRAY_SIZE(mxc_dataflash_device));
+ else
+ /* BB2.0 */
+ spi_register_board_info(mxc_spi_nor_device,
+ ARRAY_SIZE(mxc_spi_nor_device));
+
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+ i2c_register_board_info(0, mxc_i2c0_board_info,
+ ARRAY_SIZE(mxc_i2c0_board_info));
+#endif
+#ifdef CONFIG_I2C_MXC_SELECT2
+ i2c_register_board_info(1, mxc_i2c1_board_info,
+ ARRAY_SIZE(mxc_i2c1_board_info));
+#endif
+#if defined(CONFIG_I2C_MXC_HS) || defined(CONFIG_I2C_MXC_HS_MODULE)
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) >= 1) {
+ vga_data.core_reg = NULL;
+ vga_data.io_reg = NULL;
+ vga_data.analog_reg = NULL;
+ }
+ i2c_register_board_info(3, mxc_i2c_hs_board_info,
+ ARRAY_SIZE(mxc_i2c_hs_board_info));
+#endif
+
+#endif
+ pm_power_off = mxc_power_off;
+ mxc_init_sgtl5000();
+}
+
+static void __init mx51_babbage_timer_init(void)
+{
+ /* Change the CPU voltages for TO2*/
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) <= 1) {
+ cpu_wp_auto[0].cpu_voltage = 1175000;
+ cpu_wp_auto[1].cpu_voltage = 1100000;
+ cpu_wp_auto[2].cpu_voltage = 1000000;
+ }
+
+ mxc_clocks_init(32768, 24000000, 22579200, 24576000);
+ mxc_timer_init("gpt_clk.0");
+}
+
+static struct sys_timer mxc_timer = {
+ .init = mx51_babbage_timer_init,
+};
+
+/*
+ * The following uses standard kernel macros define in arch.h in order to
+ * initialize __mach_desc_MX51_BABBAGE data structure.
+ */
+/* *INDENT-OFF* */
+MACHINE_START(MX51_BABBAGE, "Freescale MX51 Babbage Board")
+ /* Maintainer: Freescale Semiconductor, Inc. */
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .fixup = fixup_mxc_board,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mxc_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx51/mx51_babbage_gpio.c b/arch/arm/mach-mx51/mx51_babbage_gpio.c
new file mode 100644
index 000000000000..8e4b9968fef5
--- /dev/null
+++ b/arch/arm/mach-mx51/mx51_babbage_gpio.c
@@ -0,0 +1,803 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+#include "iomux.h"
+
+/*!
+ * @file mach-mx51/mx51_babbage_gpio.c
+ *
+ * @brief This file contains all the GPIO setup functions for the board.
+ *
+ * @ingroup GPIO
+ */
+
+static struct mxc_iomux_pin_cfg __initdata mxc_iomux_pins[] = {
+ {
+ MX51_PIN_EIM_A16, IOMUX_CONFIG_GPIO,
+ },
+ {
+ MX51_PIN_EIM_A17, IOMUX_CONFIG_GPIO,
+ },
+ {
+ MX51_PIN_EIM_A18, IOMUX_CONFIG_GPIO,
+ },
+ {
+ MX51_PIN_EIM_A19, IOMUX_CONFIG_GPIO,
+ },
+ {
+ MX51_PIN_EIM_A20, IOMUX_CONFIG_GPIO,
+ (PAD_CTL_PKE_ENABLE),
+ },
+ {
+ MX51_PIN_EIM_A21, IOMUX_CONFIG_GPIO,
+ },
+ {
+ MX51_PIN_EIM_A22, IOMUX_CONFIG_GPIO,
+ },
+ {
+ MX51_PIN_EIM_A23, IOMUX_CONFIG_GPIO,
+ },
+ { /*MDIO */
+ MX51_PIN_EIM_EB2, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_ODE_OPENDRAIN_ENABLE |
+ PAD_CTL_22K_PU | PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_PULL),
+ },
+ { /*RDATA[1] */
+
+ MX51_PIN_EIM_EB3, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE),
+ },
+ { /*RDATA[2] */
+ MX51_PIN_EIM_CS2, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE),
+ },
+ { /*RDATA[3] */
+ MX51_PIN_EIM_CS3, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE),
+ },
+ { /*RX_ER */
+ MX51_PIN_EIM_CS4, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE),
+ },
+ { /*CRS */
+ MX51_PIN_EIM_CS5, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE),
+ },
+ {
+ MX51_PIN_EIM_DTACK, IOMUX_CONFIG_GPIO,
+ (PAD_CTL_PKE_ENABLE | PAD_CTL_100K_PU),
+ },
+ {
+ MX51_PIN_EIM_LBA, IOMUX_CONFIG_GPIO,
+ },
+ {
+ MX51_PIN_NANDF_RB2, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE),
+ },
+ {
+ MX51_PIN_NANDF_RB3, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE),
+ },
+ {
+ MX51_PIN_NANDF_RB4, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE),
+ },
+ { /*RDATA[0] */
+ MX51_PIN_NANDF_RB6, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_DRV_VOT_HIGH | PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE),
+ },
+ { /*TDATA[0] */
+ MX51_PIN_NANDF_RB7, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_DRV_VOT_HIGH | PAD_CTL_DRV_HIGH),
+ },
+ {
+ MX51_PIN_NANDF_CS0, IOMUX_CONFIG_GPIO,
+ PAD_CTL_100K_PU,
+ },
+ {
+ MX51_PIN_NANDF_CS1, IOMUX_CONFIG_GPIO,
+ },
+ { /*TX_ER */
+ MX51_PIN_NANDF_CS2, IOMUX_CONFIG_ALT2,
+ (PAD_CTL_DRV_VOT_HIGH | PAD_CTL_DRV_HIGH),
+ },
+ {
+ MX51_PIN_NANDF_CS3, IOMUX_CONFIG_ALT2,
+ (PAD_CTL_DRV_VOT_HIGH | PAD_CTL_DRV_HIGH),
+ },
+ { /*TDATA[1] */
+ MX51_PIN_NANDF_CS4, IOMUX_CONFIG_ALT2,
+ (PAD_CTL_DRV_VOT_HIGH | PAD_CTL_DRV_HIGH),
+ },
+ { /*TDATA[2] */
+ MX51_PIN_NANDF_CS5, IOMUX_CONFIG_ALT2,
+ (PAD_CTL_DRV_VOT_HIGH | PAD_CTL_DRV_HIGH),
+ },
+ { /*TDATA[3] */
+ MX51_PIN_NANDF_CS6, IOMUX_CONFIG_ALT2,
+ (PAD_CTL_DRV_VOT_HIGH | PAD_CTL_DRV_HIGH),
+ },
+ { /*TX_EN */
+ MX51_PIN_NANDF_CS7, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_DRV_VOT_HIGH | PAD_CTL_DRV_HIGH),
+ },
+ { /*TX_CLK */
+ MX51_PIN_NANDF_RDY_INT, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_DRV_VOT_HIGH | PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE),
+ },
+ {
+ MX51_PIN_GPIO1_8, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_SRE_SLOW | PAD_CTL_DRV_MEDIUM | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_ENABLE | PAD_CTL_DRV_VOT_HIGH),
+ },
+ {
+ MX51_PIN_DI_GP4, IOMUX_CONFIG_ALT4,
+ },
+ {
+ MX51_PIN_DISPB2_SER_DIN, IOMUX_CONFIG_GPIO,
+ 0,
+ MUX_IN_GPIO3_IPP_IND_G_IN_5_SELECT_INPUT,
+ INPUT_CTL_PATH1,
+ },
+#ifdef CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL
+ { /* DISP2_DAT16 */
+ MX51_PIN_DISP1_DAT22, IOMUX_CONFIG_ALT5,
+ },
+ { /* DISP2_DAT17 */
+ MX51_PIN_DISP1_DAT23, IOMUX_CONFIG_ALT5,
+ },
+ {
+ MX51_PIN_DI1_D1_CS, IOMUX_CONFIG_ALT4,
+ (PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER | PAD_CTL_DRV_HIGH |
+ PAD_CTL_SRE_FAST),
+ MUX_IN_GPIO3_IPP_IND_G_IN_4_SELECT_INPUT, INPUT_CTL_PATH1,
+ },
+#endif
+ /* LVDS GPIO control */
+ {
+ MX51_PIN_DI1_D0_CS, IOMUX_CONFIG_ALT4,
+ (PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER | PAD_CTL_DRV_HIGH |
+ PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_CSI2_D12, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER | PAD_CTL_DRV_HIGH |
+ PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_CSI2_D13, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER | PAD_CTL_DRV_HIGH |
+ PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_NANDF_D12, IOMUX_CONFIG_GPIO,
+ 0,
+ },
+ {
+ MX51_PIN_I2C1_CLK, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ 0x1E4,
+ },
+ {
+ MX51_PIN_I2C1_DAT, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ 0x1E4,
+ },
+ {
+ MX51_PIN_GPIO1_2, IOMUX_CONFIG_ALT1,
+ },
+ {
+ MX51_PIN_GPIO1_3, IOMUX_CONFIG_ALT2 | IOMUX_CONFIG_SION,
+ (PAD_CTL_SRE_FAST | PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_100K_PU | PAD_CTL_HYS_ENABLE),
+ MUX_IN_I2C2_IPP_SDA_IN_SELECT_INPUT, INPUT_CTL_PATH3,
+ },
+ {
+ MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_CLK */
+ MX51_PIN_USBH1_CLK, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS),
+ },
+ { /* USBH1_DIR */
+ MX51_PIN_USBH1_DIR, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS),
+ },
+ { /* USBH1_NXT */
+ MX51_PIN_USBH1_NXT, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS),
+ },
+ { /* USBH1_DATA0 */
+ MX51_PIN_USBH1_DATA0, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA1 */
+ MX51_PIN_USBH1_DATA1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA2 */
+ MX51_PIN_USBH1_DATA2, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA3 */
+ MX51_PIN_USBH1_DATA3, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA4 */
+ MX51_PIN_USBH1_DATA4, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA5 */
+ MX51_PIN_USBH1_DATA5, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA6 */
+ MX51_PIN_USBH1_DATA6, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA7 */
+ MX51_PIN_USBH1_DATA7, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ {
+ MX51_PIN_SD1_CMD, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_CLK, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA0, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA2, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA3, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_GPIO1_0, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU),
+ },
+ {
+ MX51_PIN_GPIO1_1, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU),
+ },
+ {
+ MX51_PIN_SD2_CMD, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD2_CLK, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD2_DATA0, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD2_DATA1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD2_DATA2, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD2_DATA3, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_GPIO1_4, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU),
+ },
+ {
+ MX51_PIN_GPIO1_5, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU),
+ },
+ {
+ MX51_PIN_GPIO1_6, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU),
+ },
+ { /* Detect pin GPIO BB2.0 and BB2.5 */
+ MX51_PIN_UART3_RXD, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_UART1_RXD, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ MUX_IN_UART1_IPP_UART_RXD_MUX_SELECT_INPUT,
+ INPUT_CTL_PATH0,
+ },
+ {
+ MX51_PIN_UART1_TXD, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_UART1_RTS, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH),
+ MUX_IN_UART1_IPP_UART_RTS_B_SELECT_INPUT,
+ INPUT_CTL_PATH0,
+ },
+ {
+ MX51_PIN_UART1_CTS, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH),
+ },
+ {
+ MX51_PIN_AUD3_BB_TXD, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_100K_PU | PAD_CTL_HYS_NONE | PAD_CTL_DDR_INPUT_CMOS |
+ PAD_CTL_DRV_VOT_LOW),
+ },
+ {
+ MX51_PIN_AUD3_BB_RXD, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_100K_PU | PAD_CTL_HYS_NONE | PAD_CTL_DDR_INPUT_CMOS |
+ PAD_CTL_DRV_VOT_LOW),
+ },
+ {
+ MX51_PIN_AUD3_BB_CK, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_100K_PU | PAD_CTL_HYS_NONE | PAD_CTL_DDR_INPUT_CMOS |
+ PAD_CTL_DRV_VOT_LOW),
+ },
+ {
+ MX51_PIN_AUD3_BB_FS, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_100K_PU | PAD_CTL_HYS_NONE | PAD_CTL_DDR_INPUT_CMOS |
+ PAD_CTL_DRV_VOT_LOW),
+ },
+ {
+ MX51_PIN_CSPI1_SS1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_SRE_FAST),
+ },
+ /* Camera on expansion board */
+ { /* camera reset */
+ MX51_PIN_EIM_D23, IOMUX_CONFIG_ALT1,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ },
+ { /* camera low power */
+ MX51_PIN_CSI2_D19, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_LOW |
+ PAD_CTL_SRE_SLOW),
+ },
+ { /* CSI1_DATA_EN need to be pulled up */
+ MX51_PIN_DI_GP3, IOMUX_CONFIG_ALT3,
+ (PAD_CTL_HYS_NONE | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST),
+ MUX_IN_HSC_MIPI_MIX_IPP_IND_SENS2_DATA_EN_SELECT_INPUT,
+ INPUT_CTL_PATH1,
+ },
+ {
+ MX51_PIN_CSI1_D10, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D11, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D12, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D13, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D14, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D15, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D16, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D17, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D18, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_D19, IOMUX_CONFIG_ALT0, PAD_CTL_HYS_NONE,
+ },
+ {
+ MX51_PIN_CSI1_VSYNC, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_SRE_SLOW),
+ },
+ {
+ MX51_PIN_CSI1_HSYNC, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_SRE_SLOW),
+ },
+ {
+ MX51_PIN_EIM_D18, IOMUX_CONFIG_GPIO,
+ (PAD_CTL_DRV_HIGH | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_100K_PU | PAD_CTL_SRE_FAST),
+ },
+};
+
+static int __initdata enable_w1 = { 0 };
+static int __init w1_setup(char *__unused)
+{
+ enable_w1 = 1;
+ return 1;
+}
+
+__setup("w1", w1_setup);
+
+void __init mx51_babbage_io_init(void)
+{
+ int i;
+
+ /* Work-around For external USB HUB chip to use default configuration
+ by reseting hub with i2c lines pulled low */
+ mxc_request_iomux(MX51_PIN_GPIO1_7, IOMUX_CONFIG_GPIO);
+ mxc_iomux_set_pad(MX51_PIN_GPIO1_7, PAD_CTL_DRV_HIGH |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_7), "gpio1_7");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_GPIO1_7), 0);
+
+ if (cpu_is_mx51_rev(CHIP_REV_1_1) == 1) {
+ /* Drive I2C1 SDA line low */
+ mxc_request_iomux(MX51_PIN_GPIO1_3, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX51_PIN_GPIO1_3, PAD_CTL_DRV_HIGH |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_3), "gpio1_3");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_GPIO1_3), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_3), 0);
+
+ /* Drive I2C1 SCL line low */
+ mxc_request_iomux(MX51_PIN_GPIO1_2, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX51_PIN_GPIO1_2, PAD_CTL_DRV_HIGH |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_2), "gpio1_2");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_GPIO1_2), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_2), 0);
+
+ msleep(5);
+ mxc_free_iomux(MX51_PIN_GPIO1_2, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_GPIO1_3, IOMUX_CONFIG_ALT2);
+ }
+
+ /* USB HUB RESET - De-assert USB HUB RESET_N */
+ msleep(1);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_7), 0);
+ msleep(1);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_7), 1);
+
+ for (i = 0; i < ARRAY_SIZE(mxc_iomux_pins); i++) {
+ mxc_request_iomux(mxc_iomux_pins[i].pin,
+ mxc_iomux_pins[i].mux_mode);
+ if (mxc_iomux_pins[i].pad_cfg)
+ mxc_iomux_set_pad(mxc_iomux_pins[i].pin,
+ mxc_iomux_pins[i].pad_cfg);
+ if (mxc_iomux_pins[i].in_select)
+ mxc_iomux_set_input(mxc_iomux_pins[i].in_select,
+ mxc_iomux_pins[i].in_mode);
+ }
+
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_8), "gpio1_8");
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_0), "gpio1_0");
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_1), "gpio1_1");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_8));
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_0)); /* SD1 CD */
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_1)); /* SD1 WP */
+ if (board_is_babbage_2_5() == 1) {
+ /* SD2 CD for BB2.5 */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_6), "gpio1_6");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_6));
+ } else {
+ /* SD2 CD for BB2.0 */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_4), "gpio1_4");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_4));
+ }
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), "gpio1_5");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5)); /* SD2 WP */
+
+ /* reset FEC PHY */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_A20), "eim_a20");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A20), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A20), 0);
+ msleep(10);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A20), 1);
+
+ /* reset FM */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_A21), "eim_a21");
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A21), 0);
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A21), 0);
+ msleep(10);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A21), 1);
+
+ if (cpu_is_mx51_rev(CHIP_REV_1_1) == 1) {
+ /* MX51_PIN_EIM_CRE - De-assert USB PHY RESETB */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_CRE), "eim_cre");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_CRE), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_CRE), 1);
+
+ /* hphone_det_b */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_NANDF_CS0), "nandf_cs0");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_NANDF_CS0));
+ } else {
+ mxc_free_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_EIM_A24, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_EIM_A25, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_EIM_D18, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_EIM_D20, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_EIM_D16, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_EIM_D19, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_GPIO1_2, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_GPIO1_3, IOMUX_CONFIG_ALT2);
+ mxc_free_iomux(MX51_PIN_EIM_LBA, IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX51_PIN_NANDF_CS0, IOMUX_CONFIG_GPIO);
+
+ /* i2c1 SDA */
+ mxc_request_iomux(MX51_PIN_EIM_D16,
+ IOMUX_CONFIG_ALT4 | IOMUX_CONFIG_SION);
+ mxc_iomux_set_input(MUX_IN_I2C1_IPP_SDA_IN_SELECT_INPUT,
+ INPUT_CTL_PATH0);
+ mxc_iomux_set_pad(MX51_PIN_EIM_D16, PAD_CTL_SRE_FAST |
+ PAD_CTL_ODE_OPENDRAIN_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_ENABLE);
+
+ /* i2c1 SCL */
+ mxc_request_iomux(MX51_PIN_EIM_D19,
+ IOMUX_CONFIG_ALT4 | IOMUX_CONFIG_SION);
+ mxc_iomux_set_input(MUX_IN_I2C1_IPP_SCL_IN_SELECT_INPUT,
+ INPUT_CTL_PATH0);
+ mxc_iomux_set_pad(MX51_PIN_EIM_D19, PAD_CTL_SRE_FAST |
+ PAD_CTL_ODE_OPENDRAIN_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_ENABLE);
+
+ /* i2c2 SDA */
+ mxc_request_iomux(MX51_PIN_KEY_COL5,
+ IOMUX_CONFIG_ALT3 | IOMUX_CONFIG_SION);
+ mxc_iomux_set_input(MUX_IN_I2C2_IPP_SDA_IN_SELECT_INPUT,
+ INPUT_CTL_PATH1);
+ mxc_iomux_set_pad(MX51_PIN_KEY_COL5,
+ PAD_CTL_SRE_FAST |
+ PAD_CTL_ODE_OPENDRAIN_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_ENABLE);
+
+ /* i2c2 SCL */
+ mxc_request_iomux(MX51_PIN_KEY_COL4,
+ IOMUX_CONFIG_ALT3 | IOMUX_CONFIG_SION);
+ mxc_iomux_set_input(MUX_IN_I2C2_IPP_SCL_IN_SELECT_INPUT,
+ INPUT_CTL_PATH1);
+ mxc_iomux_set_pad(MX51_PIN_KEY_COL4,
+ PAD_CTL_SRE_FAST |
+ PAD_CTL_ODE_OPENDRAIN_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_ENABLE);
+
+ /* Drive 26M_OSC_EN line high */
+ mxc_request_iomux(MX51_PIN_DI1_PIN12, IOMUX_CONFIG_ALT4);
+ mxc_iomux_set_pad(MX51_PIN_DI1_PIN12, PAD_CTL_DRV_HIGH |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_DI1_PIN12), "di1_pin12");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DI1_PIN12), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DI1_PIN12), 1);
+
+ /* Drive USB_CLK_EN_B line low */
+ mxc_request_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX51_PIN_EIM_D17, PAD_CTL_DRV_HIGH |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_D17), "eim_d17");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D17), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D17), 0);
+
+ /* MX51_PIN_EIM_D21 - De-assert USB PHY RESETB */
+ mxc_request_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX51_PIN_EIM_D21, PAD_CTL_DRV_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_100K_PU | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_D21), "eim_d21");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D21), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D21), 1);
+
+ /* hphone_det_b */
+ mxc_request_iomux(MX51_PIN_NANDF_D14, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX51_PIN_NANDF_D14, PAD_CTL_100K_PU);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_NANDF_D14), "nandf_d14");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_NANDF_D14));
+
+ /* audio_clk_en_b */
+ mxc_request_iomux(MX51_PIN_CSPI1_RDY, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX51_PIN_CSPI1_RDY, PAD_CTL_DRV_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_100K_PU | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_CSPI1_RDY), "cspi1_rdy");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_CSPI1_RDY), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSPI1_RDY), 0);
+
+ /* power key */
+ mxc_request_iomux(MX51_PIN_EIM_A27, IOMUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX51_PIN_EIM_A27, PAD_CTL_SRE_FAST |
+ PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_NONE);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_A27), "eim_a27");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_EIM_A27));
+ }
+
+ if (cpu_is_mx51_rev(CHIP_REV_3_0) > 0) {
+ /* DVI_I2C_ENB = 0 tristates the DVI I2C level shifter */
+ mxc_request_iomux(MX51_PIN_CSI2_HSYNC, IOMUX_CONFIG_ALT3);
+ mxc_iomux_set_pad(MX51_PIN_CSI2_HSYNC, PAD_CTL_DRV_HIGH |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_CSI2_HSYNC), "csi2_hsync");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_CSI2_HSYNC), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI2_HSYNC), 0);
+
+ /* TO3 doesn't need pad to drive CSI_DATA_EN[0] high */
+ mxc_request_iomux(MX51_PIN_DI_GP3, IOMUX_CONFIG_ALT0);
+ }
+
+ /* Deassert VGA reset to free i2c bus */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_A19), "eim_a19");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A19), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A19), 1);
+
+ /* LCD related gpio */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_DI1_D1_CS), "di1_d1_cs");
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_DI1_D0_CS), "di1_d0_cs");
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_CSI2_D12), "csi2_d12");
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_CSI2_D13), "csi2_d13");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DI1_D1_CS), 0);
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DI1_D0_CS), 0);
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_CSI2_D12), 0);
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_CSI2_D13), 0);
+
+ /* Camera reset */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_D23), "eim_d23");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D23), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D23), 1);
+
+ /* Camera low power */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_CSI2_D19), "csi2_d19");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_CSI2_D19), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_CSI2_D19), 0);
+
+ /* OSC_EN */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_D18), "eim_d18");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D18), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D18), 1);
+
+ if (enable_w1) {
+ /* OneWire */
+ mxc_request_iomux(MX51_PIN_OWIRE_LINE, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX51_PIN_OWIRE_LINE, PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE |
+ PAD_CTL_ODE_OPENDRAIN_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST |
+ PAD_CTL_100K_PU | PAD_CTL_PUE_PULL);
+ } else {
+ /* SPDIF Out */
+ mxc_request_iomux(MX51_PIN_OWIRE_LINE, IOMUX_CONFIG_ALT6);
+ mxc_iomux_set_pad(MX51_PIN_OWIRE_LINE, PAD_CTL_DRV_HIGH |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL |
+ PAD_CTL_100K_PU | PAD_CTL_SRE_FAST);
+ }
+}
+
+/* workaround for ecspi chipselect pin may not keep correct level when idle */
+void mx51_babbage_gpio_spi_chipselect_active(int cspi_mode, int status,
+ int chipselect)
+{
+ u32 gpio;
+
+ switch (cspi_mode) {
+ case 1:
+ switch (chipselect) {
+ case 0x1:
+ mxc_request_iomux(MX51_PIN_CSPI1_SS0,
+ IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX51_PIN_CSPI1_SS0,
+ PAD_CTL_HYS_ENABLE |
+ PAD_CTL_PKE_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_SRE_FAST);
+ break;
+ case 0x2:
+ gpio = IOMUX_TO_GPIO(MX51_PIN_CSPI1_SS0);
+ mxc_request_iomux(MX51_PIN_CSPI1_SS0,
+ IOMUX_CONFIG_GPIO);
+ gpio_request(gpio, "cspi1_ss0");
+ gpio_direction_output(gpio, 0);
+ gpio_set_value(gpio, 1 & (~status));
+ break;
+ default:
+ break;
+ }
+ break;
+ case 2:
+ break;
+ case 3:
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(mx51_babbage_gpio_spi_chipselect_active);
+
+void mx51_babbage_gpio_spi_chipselect_inactive(int cspi_mode, int status,
+ int chipselect)
+{
+ switch (cspi_mode) {
+ case 1:
+ switch (chipselect) {
+ case 0x1:
+ mxc_free_iomux(MX51_PIN_CSPI1_SS0, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX51_PIN_CSPI1_SS0,
+ IOMUX_CONFIG_GPIO);
+ mxc_free_iomux(MX51_PIN_CSPI1_SS0, IOMUX_CONFIG_GPIO);
+ break;
+ case 0x2:
+ mxc_free_iomux(MX51_PIN_CSPI1_SS0, IOMUX_CONFIG_GPIO);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 2:
+ break;
+ case 3:
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(mx51_babbage_gpio_spi_chipselect_inactive);
+
diff --git a/arch/arm/mach-mx51/mx51_babbage_pmic_mc13892.c b/arch/arm/mach-mx51/mx51_babbage_pmic_mc13892.c
new file mode 100644
index 000000000000..7a8e2216aff1
--- /dev/null
+++ b/arch/arm/mach-mx51/mx51_babbage_pmic_mc13892.c
@@ -0,0 +1,363 @@
+/*
+ * mx51-3stack-pmic-mc13892.c -- i.MX51 3STACK Driver for Atlas MC13892 PMIC
+ */
+ /*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+ /*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/pmic_external.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/mc13892/core.h>
+#include "iomux.h"
+
+/*
+ * Convenience conversion.
+ * Here atm, maybe there is somewhere better for this.
+ */
+#define mV_to_uV(mV) (mV * 1000)
+#define uV_to_mV(uV) (uV / 1000)
+#define V_to_uV(V) (mV_to_uV(V * 1000))
+#define uV_to_V(uV) (uV_to_mV(uV) / 1000)
+
+/* Coin cell charger enable */
+#define CIONCHEN_LSH 23
+#define CIONCHEN_WID 1
+/* Coin cell charger voltage setting */
+#define VCOIN_LSH 20
+#define VCOIN_WID 3
+
+/* Coin Charger voltage */
+#define VCOIN_2_5V 0x0
+#define VCOIN_2_7V 0x1
+#define VCOIN_2_8V 0x2
+#define VCOIN_2_9V 0x3
+#define VCOIN_3_0V 0x4
+#define VCOIN_3_1V 0x5
+#define VCOIN_3_2V 0x6
+#define VCOIN_3_3V 0x7
+
+/* Keeps VSRTC and CLK32KMCU on for all states */
+#define DRM_LSH 4
+#define DRM_WID 1
+
+/* regulator standby mask */
+#define GEN1_STBY_MASK (1 << 1)
+#define IOHI_STBY_MASK (1 << 4)
+#define DIG_STBY_MASK (1 << 10)
+#define GEN2_STBY_MASK (1 << 13)
+#define PLL_STBY_MASK (1 << 16)
+#define USB2_STBY_MASK (1 << 19)
+
+#define GEN3_STBY_MASK (1 << 1)
+#define CAM_STBY_MASK (1 << 7)
+#define VIDEO_STBY_MASK (1 << 13)
+#define AUDIO_STBY_MASK (1 << 16)
+#define SD_STBY_MASK (1 << 19)
+
+/* 0x92412 */
+#define REG_MODE_0_ALL_MASK (GEN1_STBY_MASK |\
+ DIG_STBY_MASK | GEN2_STBY_MASK |\
+ PLL_STBY_MASK | USB2_STBY_MASK)
+/* 0x92082 */
+#define REG_MODE_1_ALL_MASK (GEN3_STBY_MASK | CAM_STBY_MASK |\
+ VIDEO_STBY_MASK | AUDIO_STBY_MASK |\
+ SD_STBY_MASK)
+
+/* CPU */
+static struct regulator_consumer_supply sw1_consumers[] = {
+ {
+ .supply = "cpu_vcc",
+ }
+};
+
+struct mc13892;
+
+static struct regulator_init_data sw1_init = {
+ .constraints = {
+ .name = "SW1",
+ .min_uV = mV_to_uV(600),
+ .max_uV = mV_to_uV(1375),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .valid_modes_mask = 0,
+ .always_on = 1,
+ .boot_on = 1,
+ .initial_state = PM_SUSPEND_MEM,
+ .state_mem = {
+ .uV = 850000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ },
+ .num_consumer_supplies = ARRAY_SIZE(sw1_consumers),
+ .consumer_supplies = sw1_consumers,
+};
+
+static struct regulator_init_data sw2_init = {
+ .constraints = {
+ .name = "SW2",
+ .min_uV = mV_to_uV(900),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ .initial_state = PM_SUSPEND_MEM,
+ .state_mem = {
+ .uV = 950000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ }
+};
+
+static struct regulator_init_data sw3_init = {
+ .constraints = {
+ .name = "SW3",
+ .min_uV = mV_to_uV(1100),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data sw4_init = {
+ .constraints = {
+ .name = "SW4",
+ .min_uV = mV_to_uV(1100),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data viohi_init = {
+ .constraints = {
+ .name = "VIOHI",
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vusb_init = {
+ .constraints = {
+ .name = "VUSB",
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data swbst_init = {
+ .constraints = {
+ .name = "SWBST",
+ }
+};
+
+static struct regulator_init_data vdig_init = {
+ .constraints = {
+ .name = "VDIG",
+ .min_uV = mV_to_uV(1050),
+ .max_uV = mV_to_uV(1800),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vpll_init = {
+ .constraints = {
+ .name = "VPLL",
+ .min_uV = mV_to_uV(1050),
+ .max_uV = mV_to_uV(1800),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vusb2_init = {
+ .constraints = {
+ .name = "VUSB2",
+ .min_uV = mV_to_uV(2400),
+ .max_uV = mV_to_uV(2775),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vvideo_init = {
+ .constraints = {
+ .name = "VVIDEO",
+ .min_uV = mV_to_uV(2775),
+ .max_uV = mV_to_uV(2775),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .apply_uV =1,
+ }
+};
+
+static struct regulator_init_data vaudio_init = {
+ .constraints = {
+ .name = "VAUDIO",
+ .min_uV = mV_to_uV(2300),
+ .max_uV = mV_to_uV(3000),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vsd_init = {
+ .constraints = {
+ .name = "VSD",
+ .min_uV = mV_to_uV(1800),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vcam_init = {
+ .constraints = {
+ .name = "VCAM",
+ .min_uV = mV_to_uV(2500),
+ .max_uV = mV_to_uV(3000),
+ .valid_ops_mask =
+ REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL,
+ }
+};
+
+static struct regulator_init_data vgen1_init = {
+ .constraints = {
+ .name = "VGEN1",
+ .min_uV = mV_to_uV(1200),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vgen2_init = {
+ .constraints = {
+ .name = "VGEN2",
+ .min_uV = mV_to_uV(1200),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vgen3_init = {
+ .constraints = {
+ .name = "VGEN3",
+ .min_uV = mV_to_uV(1800),
+ .max_uV = mV_to_uV(2900),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data gpo1_init = {
+ .constraints = {
+ .name = "GPO1",
+ }
+};
+
+static struct regulator_init_data gpo2_init = {
+ .constraints = {
+ .name = "GPO2",
+ }
+};
+
+static struct regulator_init_data gpo3_init = {
+ .constraints = {
+ .name = "GPO3",
+ }
+};
+
+static struct regulator_init_data gpo4_init = {
+ .constraints = {
+ .name = "GPO4",
+ }
+};
+
+static int mc13892_regulator_init(struct mc13892 *mc13892)
+{
+ unsigned int value, register_mask;
+ printk("Initializing regulators for Babbage.\n");
+ if (mxc_cpu_is_rev(CHIP_REV_2_0) < 0)
+ sw2_init.constraints.state_mem.uV = 1100000;
+ else if (mxc_cpu_is_rev(CHIP_REV_2_0) == 1) {
+ sw2_init.constraints.state_mem.uV = 1250000;
+ sw1_init.constraints.state_mem.uV = 1000000;
+ }
+
+ /* enable standby controll for all regulators */
+ pmic_read_reg(REG_MODE_0, &value, 0xffffff);
+ value |= REG_MODE_0_ALL_MASK;
+ pmic_write_reg(REG_MODE_0, value, 0xffffff);
+
+ pmic_read_reg(REG_MODE_1, &value, 0xffffff);
+ value |= REG_MODE_1_ALL_MASK;
+ pmic_write_reg(REG_MODE_1, value, 0xffffff);
+
+ /* Enable coin cell charger */
+ value = BITFVAL(CIONCHEN, 1) | BITFVAL(VCOIN, VCOIN_3_0V);
+ register_mask = BITFMASK(CIONCHEN) | BITFMASK(VCOIN);
+ pmic_write_reg(REG_POWER_CTL0, value, register_mask);
+
+#if defined(CONFIG_RTC_DRV_MXC_V2) || defined(CONFIG_RTC_DRV_MXC_V2_MODULE)
+ value = BITFVAL(DRM, 1);
+ register_mask = BITFMASK(DRM);
+ pmic_write_reg(REG_POWER_CTL0, value, register_mask);
+#endif
+
+ mc13892_register_regulator(mc13892, MC13892_SW1, &sw1_init);
+ mc13892_register_regulator(mc13892, MC13892_SW2, &sw2_init);
+ mc13892_register_regulator(mc13892, MC13892_SW3, &sw3_init);
+ mc13892_register_regulator(mc13892, MC13892_SW4, &sw4_init);
+ mc13892_register_regulator(mc13892, MC13892_SWBST, &swbst_init);
+ mc13892_register_regulator(mc13892, MC13892_VIOHI, &viohi_init);
+ mc13892_register_regulator(mc13892, MC13892_VPLL, &vpll_init);
+ mc13892_register_regulator(mc13892, MC13892_VDIG, &vdig_init);
+ mc13892_register_regulator(mc13892, MC13892_VSD, &vsd_init);
+ mc13892_register_regulator(mc13892, MC13892_VUSB2, &vusb2_init);
+ mc13892_register_regulator(mc13892, MC13892_VVIDEO, &vvideo_init);
+ mc13892_register_regulator(mc13892, MC13892_VAUDIO, &vaudio_init);
+ mc13892_register_regulator(mc13892, MC13892_VCAM, &vcam_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN1, &vgen1_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN2, &vgen2_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN3, &vgen3_init);
+ mc13892_register_regulator(mc13892, MC13892_VUSB, &vusb_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO1, &gpo1_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO2, &gpo2_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO3, &gpo3_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO4, &gpo4_init);
+
+ return 0;
+}
+
+static struct mc13892_platform_data mc13892_plat = {
+ .init = mc13892_regulator_init,
+};
+
+static struct spi_board_info __initdata mc13892_spi_device = {
+ .modalias = "pmic_spi",
+ .irq = IOMUX_TO_IRQ(MX51_PIN_GPIO1_8),
+ .max_speed_hz = 1000000, /* max spi SCK clock speed in HZ */
+ .bus_num = 1,
+ .chip_select = 0,
+ .platform_data = &mc13892_plat,
+};
+
+
+int __init mx51_babbage_init_mc13892(void)
+{
+ return spi_register_board_info(&mc13892_spi_device, 1);
+}
+
diff --git a/arch/arm/mach-mx51/mx51_ccwmx51js.c b/arch/arm/mach-mx51/mx51_ccwmx51js.c
new file mode 100644
index 000000000000..2d603bb3655e
--- /dev/null
+++ b/arch/arm/mach-mx51/mx51_ccwmx51js.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Digi International, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/nodemask.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/ata.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/spba.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <mach/memory.h>
+#include <mach/gpio.h>
+#include <mach/mmc.h>
+#include "board.h"
+#include "iomux.h"
+#include "crm_regs.h"
+
+
+#include <linux/smc911x.h>
+
+
+#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <asm/mach/flash.h>
+#endif
+
+extern void __init ccwmx51_io_init(void);
+extern int __init ccwmx51_init_mc13892(void);
+extern struct cpu_wp *(*get_cpu_wp)(int *wp);
+extern void (*set_num_cpu_wp)(int num);
+static int num_cpu_wp = 3;
+
+/* working point(wp): 0 - 800MHz; 1 - 166.25MHz; */
+static struct cpu_wp cpu_wp_auto[] = {
+ {
+ .pll_rate = 1000000000,
+ .cpu_rate = 1000000000,
+ .pdf = 0,
+ .mfi = 10,
+ .mfd = 11,
+ .mfn = 5,
+ .cpu_podf = 0,
+ .cpu_voltage = 1175000,},
+ {
+ .pll_rate = 800000000,
+ .cpu_rate = 800000000,
+ .pdf = 0,
+ .mfi = 8,
+ .mfd = 2,
+ .mfn = 1,
+ .cpu_podf = 0,
+ .cpu_voltage = 1100000,},
+ {
+ .pll_rate = 800000000,
+ .cpu_rate = 166250000,
+ .pdf = 4,
+ .mfi = 8,
+ .mfd = 2,
+ .mfn = 1,
+ .cpu_podf = 4,
+ .cpu_voltage = 850000,},
+};
+
+struct cpu_wp *mx51_get_cpu_wp(int *wp)
+{
+ *wp = num_cpu_wp;
+ return cpu_wp_auto;
+}
+
+void mx51_set_num_cpu_wp(int num)
+{
+ num_cpu_wp = num;
+ return;
+}
+static void mxc_nop_release(struct device *dev)
+{
+ /* Nothing */
+}
+
+/* MTD NAND flash */
+#if defined(CONFIG_MTD_NAND_MXC) \
+ || defined(CONFIG_MTD_NAND_MXC_MODULE) \
+ || defined(CONFIG_MTD_NAND_MXC_V2) \
+ || defined(CONFIG_MTD_NAND_MXC_V2_MODULE) \
+ || defined(CONFIG_MTD_NAND_MXC_V3) \
+ || defined(CONFIG_MTD_NAND_MXC_V3_MODULE)
+
+extern void gpio_nand_active(void);
+extern void gpio_nand_inactive(void);
+
+static int nand_init(void)
+{
+ /* Configure the pins */
+ gpio_nand_active();
+ return 0;
+}
+
+static void nand_exit(void)
+{
+ /* Free the pins */
+ gpio_nand_inactive();
+}
+
+static struct flash_platform_data mxc_nand_data = {
+ .width = 1,
+ .init = nand_init,
+ .exit = nand_exit,
+};
+
+static struct platform_device mxc_nandv2_mtd_device = {
+ .name = "mxc_nandv2_flash",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mxc_nand_data,
+ },
+};
+
+static void ccwmx51_init_nand_mtd(void)
+{
+ (void)platform_device_register(&mxc_nandv2_mtd_device);
+}
+#else
+static inline void ccwmx51_init_nand_mtd(void) { }
+#endif
+
+#if defined(CONFIG_SMSC9118)
+static struct resource smsc911x_device_resources[] = {
+ [0] = {
+ .name = "smsc911x-memory",
+ .start = CS5_BASE_ADDR,
+ .end = CS5_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IOMUX_TO_IRQ(MX51_PIN_GPIO1_9),
+ .end = IOMUX_TO_IRQ(MX51_PIN_GPIO1_9),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct smc911x_platdata ccwmx51_smsc9118 = {
+ .flags = SMC911X_USE_16BIT,
+ .irq_flags = IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+ .irq_polarity = 0,
+};
+
+static struct platform_device smsc911x_device = {
+ .name = "smsc911x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(smsc911x_device_resources),
+ .resource = smsc911x_device_resources,
+ .dev = {
+ .platform_data = &ccwmx51_smsc9118,
+ }
+};
+
+/* WEIM registers */
+#define CSGCR1 0x00
+#define CSGCR2 0x04
+#define CSRCR1 0x08
+#define CSRCR2 0x0C
+#define CSWCR1 0x10
+
+static void ccwmx51_init_ext_eth_mac(void)
+{
+ __iomem u32 *weim_vbaddr;
+
+ weim_vbaddr = ioremap(WEIM_BASE_ADDR, SZ_4K);
+ if (weim_vbaddr == 0) {
+ printk(KERN_ERR "Unable to ioremap 0x%08x in %s\n", WEIM_BASE_ADDR, __func__);
+ return;
+ }
+
+ /** Configure the CS timming, bus width, etc.
+ * 16 bit on DATA[31..16], not multiplexed, async
+ * RWSC=50, RADVA=2, RADVN=6, OEA=0, OEN=0, RCSA=0, RCSN=0, APR=0
+ * WAL=0, WBED=1, WWSC=50, WADVA=2, WADVN=6, WEA=0, WEN=0, WCSA=0
+ */
+ __raw_writel(0x00420081, weim_vbaddr + 0x78 + CSGCR1);
+ __raw_writel(0, weim_vbaddr + 0x78 + CSGCR2);
+ __raw_writel(0x32260000, weim_vbaddr + 0x78 + CSRCR1);
+ __raw_writel(0, weim_vbaddr + 0x78 + CSRCR2);
+ __raw_writel(0x72080f00, weim_vbaddr + 0x78 + CSWCR1);
+
+ iounmap(weim_vbaddr);
+
+ /* Configure interrupt line as GPIO input, the iomux should be already setup */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_9), "LAN2-irq");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_9));
+
+ (void)platform_device_register(&smsc911x_device);
+}
+#else
+static void ccwmx51_init_ext_eth_mac(void) { }
+#endif
+
+
+#if defined(CONFIG_FEC) || defined(CONFIG_FEC_MODULE)
+unsigned int expio_intr_fec;
+
+EXPORT_SYMBOL(expio_intr_fec);
+#endif
+
+#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
+static int sdhc_write_protect(struct device *dev)
+{
+ unsigned short rc = 0;
+
+ if (to_platform_device(dev)->id == 0)
+ rc = 1; /* Not supported WP on JSK board */
+ else if (to_platform_device(dev)->id == 2)
+ rc = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_NANDF_CS1));
+
+ return rc;
+}
+
+static unsigned int sdhc_get_card_det_status(struct device *dev)
+{
+ int ret = 0;
+
+ if (to_platform_device(dev)->id == 0)
+ ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_0));
+ else if (to_platform_device(dev)->id == 2)
+ ret = gpio_get_value(IOMUX_TO_GPIO(MX51_PIN_GPIO_NAND));
+
+ return ret;
+}
+
+static struct mxc_mmc_platform_data mmc1_data = {
+ .ocr_mask = MMC_VDD_31_32,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 400000,
+ .max_clk = 52000000,
+ .card_inserted_state = 1,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "esdhc_clk",
+ .power_mmc = NULL,
+};
+
+static struct mxc_mmc_platform_data mmc3_data = {
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 |
+ MMC_VDD_31_32,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .min_clk = 150000,
+ .max_clk = 50000000,
+ .card_inserted_state = 0,
+ .status = sdhc_get_card_det_status,
+ .wp_status = sdhc_write_protect,
+ .clock_mmc = "esdhc_clk",
+ .power_mmc = NULL,
+};
+
+/**
+ * Resource definition for the SDHC1
+ */
+static struct resource mxcsdhc1_resources[] = {
+ [0] = {
+ .start = MMC_SDHC1_BASE_ADDR,
+ .end = MMC_SDHC1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC1,
+ .end = MXC_INT_MMC_SDHC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IOMUX_TO_IRQ(MX51_PIN_GPIO1_0),
+ .end = IOMUX_TO_IRQ(MX51_PIN_GPIO1_0),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/**
+ * Resource definition for the SDHC3
+ */
+static struct resource mxcsdhc3_resources[] = {
+ [0] = {
+ .start = MMC_SDHC3_BASE_ADDR,
+ .end = MMC_SDHC3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_MMC_SDHC3,
+ .end = MXC_INT_MMC_SDHC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IOMUX_TO_IRQ(MX51_PIN_GPIO_NAND),
+ .end = IOMUX_TO_IRQ(MX51_PIN_GPIO_NAND),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/*! Device Definition for MXC SDHC1 */
+static struct platform_device mxcsdhc1_device = {
+ .name = "mxsdhci",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc1_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc1_resources),
+ .resource = mxcsdhc1_resources,
+};
+
+/*! Device Definition for MXC SDHC3 */
+static struct platform_device mxcsdhc3_device = {
+ .name = "mxsdhci",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .platform_data = &mmc3_data,
+ },
+ .num_resources = ARRAY_SIZE(mxcsdhc3_resources),
+ .resource = mxcsdhc3_resources,
+};
+
+static inline void ccwmx51_init_mmc(void)
+{
+ (void)platform_device_register(&mxcsdhc1_device);
+ (void)platform_device_register(&mxcsdhc3_device);
+}
+#else
+static inline void ccwmx51_init_mmc(void) {}
+#endif
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || \
+ defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+
+static struct mxc_fb_platform_data fb_data_vga = {
+ .interface_pix_fmt = IPU_PIX_FMT_RGB24,
+ .mode_str = "1024x768M-16@60", /* Default */
+};
+
+static struct platform_device mxc_fb_device[] = {
+ {
+ .name = "mxc_sdc_fb",
+ .id = 0,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ .platform_data = &fb_data_vga,
+ },
+ }, {
+ .name = "mxc_sdc_fb",
+ .id = 1,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ .platform_data = NULL,
+ },
+ }, {
+ .name = "mxc_sdc_fb",
+ .id = 2,
+ .dev = {
+ .release = mxc_nop_release,
+ .coherent_dma_mask = 0xFFFFFFFF,
+ },
+ },
+};
+
+static int __init ccwmx51_init_fb(void)
+{
+ char *options = NULL, *p;
+
+ if (fb_get_options("displayfb", &options))
+ pr_warning("no display information available in commnad line\n");
+
+ if (!options)
+ return -ENODEV;
+
+ if (!strncasecmp(options, "VGA", 3)) {
+ pr_info("VGA interface is primary\n");
+
+ /* Get the desired configuration provided by the bootloader */
+ if (options[3] != '@') {
+ pr_info("Video resolution for VGA interface not provided, using default\n");
+ /* TODO set default video here */
+ } else {
+ options = &options[4];
+ if (((p = strsep (&options, "@")) != NULL) && *p) {
+ if (!strcmp(p, "640x480x16")) {
+ strcpy(fb_data_vga.mode_str, "640x480M-16@60");
+ } else if (!strcmp(p, "800x600x16")) {
+ strcpy(fb_data_vga.mode_str, "800x600M-16@60");
+ } else if (!strcmp(p, "1024x768x16")) {
+ strcpy(fb_data_vga.mode_str, "1024x768M-16@60");
+ } else if (!strcmp(p, "1280x1024x16")) {
+ strcpy(fb_data_vga.mode_str, "1280x1024M-16@60");
+ } else if (!strcmp(p, "1280x1024x16")) {
+ strcpy(fb_data_vga.mode_str, "1280x1024M-16@60");
+ } else
+ pr_warning("Unsuported video resolution: %s, using default\n", p);
+ }
+ }
+ (void)platform_device_register(&mxc_fb_device[0]); /* VGA */
+ }
+
+ return 0;
+}
+device_initcall(ccwmx51_init_fb);
+#else
+static inline void ccwmx51_init_fb(void) { }
+#endif
+
+
+
+/*!
+ * Board specific fixup function. It is called by \b setup_arch() in
+ * setup.c file very early on during kernel starts. It allows the user to
+ * statically fill in the proper values for the passed-in parameters. None of
+ * the parameters is used currently.
+ *
+ * @param desc pointer to \b struct \b machine_desc
+ * @param tags pointer to \b struct \b tag
+ * @param cmdline pointer to the command line
+ * @param mi pointer to \b struct \b meminfo
+ */
+static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
+ char **cmdline, struct meminfo *mi)
+{
+ char *str;
+ int size = SZ_512M - SZ_32M;
+ struct tag *t;
+
+ mxc_cpu_init();
+
+ get_cpu_wp = mx51_get_cpu_wp;
+ set_num_cpu_wp = mx51_set_num_cpu_wp;
+
+ for_each_tag(t, tags) {
+ if (t->hdr.tag != ATAG_CMDLINE)
+ continue;
+ str = t->u.cmdline.cmdline;
+ str = strstr(str, "mem=");
+ if (str != NULL) {
+ str += 4;
+ size = memparse(str, &str);
+ if (size == 0 || size == SZ_512M)
+ return;
+ }
+ }
+
+ for_each_tag(t, tags) {
+ if (t->hdr.tag != ATAG_MEM)
+ continue;
+
+ t->u.mem.size = size;
+ }
+}
+
+#define PWGT1SPIEN (1<<15)
+#define PWGT2SPIEN (1<<16)
+#define USEROFFSPI (1<<3)
+
+static void mxc_power_off(void)
+{
+ /* We can do power down one of two ways:
+ Set the power gating
+ Set USEROFFSPI */
+
+ /* Set the power gate bits to power down */
+ pmic_write_reg(REG_POWER_MISC, (PWGT1SPIEN|PWGT2SPIEN),
+ (PWGT1SPIEN|PWGT2SPIEN));
+}
+
+static struct i2c_board_info ccwmx51_i2c_devices[] __initdata = {
+#if defined(CONFIG_INPUT_MMA7455L)
+ {
+ I2C_BOARD_INFO("mma7455l", 0x1d),
+ .irq = IOMUX_TO_IRQ(MX51_PIN_GPIO1_7),
+ },
+#endif
+};
+
+int __init ccwmx51_init_mma7455l(void)
+{
+
+ return i2c_register_board_info(1, ccwmx51_i2c_devices , ARRAY_SIZE(ccwmx51_i2c_devices) );
+}
+
+/*!
+ * Board specific initialization.
+ */
+static void __init mxc_board_init(void)
+{
+ mxc_cpu_common_init();
+ mxc_register_gpios();
+ ccwmx51_io_init();
+ early_console_setup(saved_command_line);
+
+ mxc_init_devices();
+ ccwmx51_init_mmc();
+ ccwmx51_init_nand_mtd();
+// ccwmx51_init_ext_eth_mac();
+ ccwmx51_init_mma7455l();
+ ccwmx51_init_mc13892();
+
+ pm_power_off = mxc_power_off;
+}
+
+static void __init ccwmx51_timer_init(void)
+{
+ /* Change the CPU voltages for TO2*/
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) <= 1) {
+ cpu_wp_auto[0].cpu_voltage = 1175000;
+ cpu_wp_auto[1].cpu_voltage = 1100000;
+ cpu_wp_auto[2].cpu_voltage = 1000000;
+ }
+
+ mxc_clocks_init(32768, 24000000, 22579200, 24576000);
+ mxc_timer_init("gpt_clk.0");
+}
+
+static struct sys_timer mxc_timer = {
+ .init = ccwmx51_timer_init,
+};
+
+MACHINE_START(CCWMX51JS, "ConnectCore Wi-MX51 on a JSK board")
+ /* Maintainer: Digi International, Inc. */
+ .phys_io = AIPS1_BASE_ADDR,
+ .io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .fixup = fixup_mxc_board,
+ .map_io = mxc_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = mxc_board_init,
+ .timer = &mxc_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx51/mx51_ccwmx51js_gpio.c b/arch/arm/mach-mx51/mx51_ccwmx51js_gpio.c
new file mode 100644
index 000000000000..3458935cd8cf
--- /dev/null
+++ b/arch/arm/mach-mx51/mx51_ccwmx51js_gpio.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Digi International, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+#include "iomux.h"
+
+/**
+ * iomux settings for the external ethernet mac
+ */
+#if defined(CONFIG_SMSC9118)
+static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_ext_eth_pins[] = {
+ {MX51_PIN_EIM_CS5, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_KEEPER | PAD_CTL_DRV_MEDIUM), },
+ {MX51_PIN_EIM_OE, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_DA0, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_DA1, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_DA2, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_DA3, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_DA4, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_DA5, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_DA6, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_DA7, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D16, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D18, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D19, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D20, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D22, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D23, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D24, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D25, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D26, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D27, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D28, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D29, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D30, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_EIM_D31, IOMUX_CONFIG_ALT0,},
+ {MX51_PIN_GPIO1_9, IOMUX_CONFIG_ALT0, (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU), },
+};
+#endif
+
+#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
+static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_mmc_pins[] = {
+ /* SDHC1*/
+ {
+ MX51_PIN_SD1_CMD, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_CLK, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA0, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA2, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_SD1_DATA3, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_47K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_GPIO1_0, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU),
+ },
+
+ /* SDHC3*/
+ {
+ MX51_PIN_NANDF_RDY_INT, IOMUX_CONFIG_ALT5 | IOMUX_CONFIG_SION,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ {
+ MX51_PIN_NANDF_CS7, IOMUX_CONFIG_ALT5,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ },
+ { /* SD3 DATA0 */
+ MX51_PIN_NANDF_D8, IOMUX_CONFIG_ALT5,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ MUX_IN_ESDHC3_IPP_DAT0_IN_SELECT_INPUT, INPUT_CTL_PATH1
+ },
+ { /* SD3 DATA1 */
+ MX51_PIN_NANDF_D9, IOMUX_CONFIG_ALT5,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ MUX_IN_ESDHC3_IPP_DAT1_IN_SELECT_INPUT, INPUT_CTL_PATH1
+ },
+ { /* SD3 DATA2 */
+ MX51_PIN_NANDF_D10, IOMUX_CONFIG_ALT5,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ MUX_IN_ESDHC3_IPP_DAT2_IN_SELECT_INPUT, INPUT_CTL_PATH1
+ },
+ { /* SD3 DATA3 */
+ MX51_PIN_NANDF_D11, IOMUX_CONFIG_ALT5,
+ (PAD_CTL_DRV_MAX | PAD_CTL_22K_PU | PAD_CTL_SRE_FAST),
+ MUX_IN_ESDHC3_IPP_DAT3_IN_SELECT_INPUT, INPUT_CTL_PATH1
+ },
+ { /* SD3 Card detect */
+ MX51_PIN_GPIO_NAND, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU),
+ },
+ { /* SD3 Write protect */
+ MX51_PIN_NANDF_CS1, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_HYS_ENABLE | PAD_CTL_100K_PU),
+ },
+};
+#endif
+
+#if defined(CONFIG_USB_EHCI_ARC_H1) || defined(CONFIG_USB_EHCI_ARC_H1_MODULE)
+static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_usbh1_pins[] = {
+ { /* USBH1_STP */
+ MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_CLK */
+ MX51_PIN_USBH1_CLK, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS),
+ },
+ { /* USBH1_DIR */
+ MX51_PIN_USBH1_DIR, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS),
+ },
+ { /* USBH1_NXT */
+ MX51_PIN_USBH1_NXT, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS),
+ },
+ { /* USBH1_DATA0 */
+ MX51_PIN_USBH1_DATA0, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA1 */
+ MX51_PIN_USBH1_DATA1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA2 */
+ MX51_PIN_USBH1_DATA2, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA3 */
+ MX51_PIN_USBH1_DATA3, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA4 */
+ MX51_PIN_USBH1_DATA4, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA5 */
+ MX51_PIN_USBH1_DATA5, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA6 */
+ MX51_PIN_USBH1_DATA6, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH1_DATA7 */
+ MX51_PIN_USBH1_DATA7, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+ { /* USBH PHY RESET */
+ MX51_PIN_DISPB2_SER_RS, IOMUX_CONFIG_GPIO,
+ (PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH | PAD_CTL_47K_PU |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE),
+ },
+};
+#endif
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_video1_pins[] = {
+ { /* DISP1 DAT0 */
+ MX51_PIN_DISP1_DAT0, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT1 */
+ MX51_PIN_DISP1_DAT1, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT2 */
+ MX51_PIN_DISP1_DAT2, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT3 */
+ MX51_PIN_DISP1_DAT3, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT4 */
+ MX51_PIN_DISP1_DAT4, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT5 */
+ MX51_PIN_DISP1_DAT5, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT6 */
+ MX51_PIN_DISP1_DAT6, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT7 */
+ MX51_PIN_DISP1_DAT7, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT8 */
+ MX51_PIN_DISP1_DAT8, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT9 */
+ MX51_PIN_DISP1_DAT9, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT10 */
+ MX51_PIN_DISP1_DAT10, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT11 */
+ MX51_PIN_DISP1_DAT11, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT12 */
+ MX51_PIN_DISP1_DAT12, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT13 */
+ MX51_PIN_DISP1_DAT13, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT14 */
+ MX51_PIN_DISP1_DAT14, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT15 */
+ MX51_PIN_DISP1_DAT15, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT16 */
+ MX51_PIN_DISP1_DAT16, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT17 */
+ MX51_PIN_DISP1_DAT17, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT18 */
+ MX51_PIN_DISP1_DAT18, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT19 */
+ MX51_PIN_DISP1_DAT19, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT20 */
+ MX51_PIN_DISP1_DAT20, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT21 */
+ MX51_PIN_DISP1_DAT21, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT22 */
+ MX51_PIN_DISP1_DAT22, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+ { /* DISP1 DAT23 */
+ MX51_PIN_DISP1_DAT23, IOMUX_CONFIG_ALT0,
+ (PAD_CTL_HYS_NONE | PAD_CTL_DRV_LOW | PAD_CTL_SRE_FAST),
+ },
+};
+#endif
+
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_mma7455l_pins[] = {
+
+// AG - Could use gpio_i2c_active() which is called from mxci2c_probe() and mxci2c_resume()
+
+#ifdef CONFIG_I2C_MXC_SELECT1
+ {
+ MX51_PIN_SD2_CMD, IOMUX_CONFIG_ALT1 | IOMUX_CONFIG_SION,
+ (PAD_CTL_SRE_FAST | PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_HYS_ENABLE |
+ PAD_CTL_100K_PU | PAD_CTL_DRV_HIGH),
+ MUX_IN_I2C1_IPP_SDA_IN_SELECT_INPUT, INPUT_CTL_PATH2,
+ },
+ {
+ MX51_PIN_SD2_CLK, IOMUX_CONFIG_ALT1 | IOMUX_CONFIG_SION,
+ (PAD_CTL_SRE_FAST | PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_HYS_ENABLE |
+ PAD_CTL_100K_PU | PAD_CTL_DRV_HIGH),
+ MUX_IN_I2C1_IPP_SCL_IN_SELECT_INPUT, INPUT_CTL_PATH2,
+ },
+#endif
+
+#ifdef CONFIG_I2C_MXC_SELECT2
+ {
+ MX51_PIN_GPIO1_2, IOMUX_CONFIG_ALT2 | IOMUX_CONFIG_SION,
+ (PAD_CTL_SRE_FAST | PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_100K_PU | PAD_CTL_HYS_ENABLE),
+ MUX_IN_I2C2_IPP_SCL_IN_SELECT_INPUT, INPUT_CTL_PATH3,
+ },
+ {
+ MX51_PIN_GPIO1_3, IOMUX_CONFIG_ALT2 | IOMUX_CONFIG_SION,
+ (PAD_CTL_SRE_FAST | PAD_CTL_ODE_OPENDRAIN_ENABLE | PAD_CTL_DRV_HIGH |
+ PAD_CTL_100K_PU | PAD_CTL_HYS_ENABLE),
+ MUX_IN_I2C2_IPP_SDA_IN_SELECT_INPUT, INPUT_CTL_PATH3,
+ },
+#endif
+
+#ifdef CONFIG_I2C_MXC_SELECT3
+ {
+ MX51_PIN_I2C1_CLK, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ ( PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_HYS_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | PAD_CTL_SRE_SLOW),
+ },
+ {
+ MX51_PIN_I2C1_DAT, IOMUX_CONFIG_ALT0 | IOMUX_CONFIG_SION,
+ ( PAD_CTL_ODE_OPENDRAIN_NONE | PAD_CTL_HYS_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | PAD_CTL_SRE_SLOW),
+ }
+#endif
+};
+
+#endif //defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+
+static struct mxc_iomux_pin_cfg __initdata ccwmx51_iomux_devices_pins[] = {
+ { /* PMIC interrupt line */
+ MX51_PIN_GPIO1_5, IOMUX_CONFIG_GPIO | IOMUX_CONFIG_SION,
+ (PAD_CTL_SRE_SLOW | PAD_CTL_DRV_MEDIUM | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_ENABLE | PAD_CTL_DRV_VOT_HIGH),
+ },
+// { /* MMA7455L interrupt line */
+// MX51_PIN_GPIO1_6, IOMUX_CONFIG_GPIO,
+// },
+// {
+// MX51_PIN_GPIO1_7, IOMUX_CONFIG_ALT2,
+// (PAD_CTL_DRV_HIGH | PAD_CTL_PUE_PULL |
+// PAD_CTL_100K_PU | PAD_CTL_PKE_ENABLE |
+// PAD_CTL_SRE_FAST),
+// },
+};
+
+
+void __init ccwmx51_io_init(void)
+{
+ int i;
+
+#if defined(CONFIG_SMSC9118)
+ for (i = 0; i < ARRAY_SIZE(ccwmx51_iomux_ext_eth_pins); i++) {
+ mxc_request_iomux(ccwmx51_iomux_ext_eth_pins[i].pin,
+ ccwmx51_iomux_ext_eth_pins[i].mux_mode);
+ if (ccwmx51_iomux_ext_eth_pins[i].pad_cfg)
+ mxc_iomux_set_pad(ccwmx51_iomux_ext_eth_pins[i].pin,
+ ccwmx51_iomux_ext_eth_pins[i].pad_cfg);
+ if (ccwmx51_iomux_ext_eth_pins[i].in_select)
+ mxc_iomux_set_input(ccwmx51_iomux_ext_eth_pins[i].in_select,
+ ccwmx51_iomux_ext_eth_pins[i].in_mode);
+ }
+#endif
+
+#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
+ for (i = 0; i < ARRAY_SIZE(ccwmx51_iomux_mmc_pins); i++) {
+ mxc_request_iomux(ccwmx51_iomux_mmc_pins[i].pin,
+ ccwmx51_iomux_mmc_pins[i].mux_mode);
+ if (ccwmx51_iomux_mmc_pins[i].pad_cfg)
+ mxc_iomux_set_pad(ccwmx51_iomux_mmc_pins[i].pin,
+ ccwmx51_iomux_mmc_pins[i].pad_cfg);
+ if (ccwmx51_iomux_mmc_pins[i].in_select)
+ mxc_iomux_set_input(ccwmx51_iomux_mmc_pins[i].in_select,
+ ccwmx51_iomux_mmc_pins[i].in_mode);
+ }
+#endif
+
+#if defined(CONFIG_USB_EHCI_ARC_H1) || defined(CONFIG_USB_EHCI_ARC_H1_MODULE)
+ for (i = 0; i < ARRAY_SIZE(ccwmx51_iomux_usbh1_pins); i++) {
+ mxc_request_iomux(ccwmx51_iomux_usbh1_pins[i].pin,
+ ccwmx51_iomux_usbh1_pins[i].mux_mode);
+ if (ccwmx51_iomux_usbh1_pins[i].pad_cfg)
+ mxc_iomux_set_pad(ccwmx51_iomux_usbh1_pins[i].pin,
+ ccwmx51_iomux_usbh1_pins[i].pad_cfg);
+ if (ccwmx51_iomux_usbh1_pins[i].in_select)
+ mxc_iomux_set_input(ccwmx51_iomux_usbh1_pins[i].in_select,
+ ccwmx51_iomux_usbh1_pins[i].in_mode);
+ }
+#endif
+
+#if defined(CONFIG_FB_MXC_SYNC_PANEL) || defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+ for (i = 0; i < ARRAY_SIZE(ccwmx51_iomux_video1_pins); i++) {
+ mxc_request_iomux(ccwmx51_iomux_video1_pins[i].pin,
+ ccwmx51_iomux_video1_pins[i].mux_mode);
+ if (ccwmx51_iomux_video1_pins[i].pad_cfg)
+ mxc_iomux_set_pad(ccwmx51_iomux_video1_pins[i].pin,
+ ccwmx51_iomux_video1_pins[i].pad_cfg);
+ if (ccwmx51_iomux_video1_pins[i].in_select)
+ mxc_iomux_set_input(ccwmx51_iomux_video1_pins[i].in_select,
+ ccwmx51_iomux_video1_pins[i].in_mode);
+ }
+#endif
+
+#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
+ for (i = 0; i < ARRAY_SIZE(ccwmx51_iomux_mma7455l_pins); i++) {
+ mxc_request_iomux(ccwmx51_iomux_mma7455l_pins[i].pin,
+ ccwmx51_iomux_mma7455l_pins[i].mux_mode);
+ if (ccwmx51_iomux_mma7455l_pins[i].pad_cfg)
+ mxc_iomux_set_pad(ccwmx51_iomux_mma7455l_pins[i].pin,
+ ccwmx51_iomux_mma7455l_pins[i].pad_cfg);
+ if (ccwmx51_iomux_mma7455l_pins[i].in_select)
+ mxc_iomux_set_input(ccwmx51_iomux_mma7455l_pins[i].in_select,
+ ccwmx51_iomux_mma7455l_pins[i].in_mode);
+ }
+#endif
+
+ for (i = 0; i < ARRAY_SIZE(ccwmx51_iomux_devices_pins); i++) {
+ mxc_request_iomux(ccwmx51_iomux_devices_pins[i].pin,
+ ccwmx51_iomux_devices_pins[i].mux_mode);
+ if (ccwmx51_iomux_devices_pins[i].pad_cfg)
+ mxc_iomux_set_pad(ccwmx51_iomux_devices_pins[i].pin,
+ ccwmx51_iomux_devices_pins[i].pad_cfg);
+ if (ccwmx51_iomux_devices_pins[i].in_select)
+ mxc_iomux_set_input(ccwmx51_iomux_devices_pins[i].in_select,
+ ccwmx51_iomux_devices_pins[i].in_mode);
+ }
+
+ /* PMIC interrupt line */
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), "gpio1_5");
+ gpio_direction_input(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5));
+
+#if defined(CONFIG_USB_EHCI_ARC_H1) || defined(CONFIG_USB_EHCI_ARC_H1_MODULE)
+ /* USB PHY/HUB reset*/
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_RS), "gpio3_8");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_RS), 0);
+ msleep(1);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_DISPB2_SER_RS), 1);
+#endif
+}
+
+
+#if defined(CONFIG_SERIAL_MXC) || defined(CONFIG_SERIAL_MXC_MODULE)
+#define SERIAL_PORT_PAD (PAD_CTL_HYS_ENABLE | PAD_CTL_PKE_ENABLE | \
+ PAD_CTL_PUE_PULL | PAD_CTL_DRV_HIGH | \
+ PAD_CTL_SRE_FAST)
+
+void gpio_uart_active(int port, int no_irda)
+{
+ /* Configure the IOMUX control registers for the UART signals */
+ switch (port) {
+
+ case 0: /* UART 1 IOMUX Configs */
+#ifdef CONFIG_UART1_ENABLED
+ mxc_request_iomux(MX51_PIN_UART1_RXD, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX51_PIN_UART1_TXD, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX51_PIN_UART1_RXD, SERIAL_PORT_PAD);
+ mxc_iomux_set_pad(MX51_PIN_UART1_TXD, SERIAL_PORT_PAD);
+ mxc_iomux_set_input(MUX_IN_UART1_IPP_UART_RXD_MUX_SELECT_INPUT, INPUT_CTL_PATH0);
+
+ /* TODO enable CTS/RTS if selected */
+#endif
+ break;
+
+ case 1: /* UART 2 IOMUX Configs */
+#ifdef CONFIG_UART2_ENABLED
+ mxc_request_iomux(MX51_PIN_UART2_RXD, IOMUX_CONFIG_ALT0);
+ mxc_request_iomux(MX51_PIN_UART2_TXD, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX51_PIN_UART2_RXD, SERIAL_PORT_PAD);
+ mxc_iomux_set_pad(MX51_PIN_UART2_TXD, SERIAL_PORT_PAD);
+ mxc_iomux_set_input(MUX_IN_UART2_IPP_UART_RXD_MUX_SELECT_INPUT, INPUT_CTL_PATH2);
+
+ /* TODO enable CTS/RTS if selected */
+#endif
+ break;
+ case 2: /* UART 3 IOMUX Configs */
+#ifdef CONFIG_UART3_ENABLED
+ mxc_request_iomux(MX51_PIN_UART3_RXD, IOMUX_CONFIG_ALT1);
+ mxc_request_iomux(MX51_PIN_UART3_TXD, IOMUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX51_PIN_UART3_RXD, SERIAL_PORT_PAD);
+ mxc_iomux_set_pad(MX51_PIN_UART3_TXD, SERIAL_PORT_PAD);
+ mxc_iomux_set_input(MUX_IN_UART3_IPP_UART_RXD_MUX_SELECT_INPUT, INPUT_CTL_PATH4);
+
+ /* TODO enable CTS/RTS if selected */
+#endif
+ break;
+ default:
+ break;
+ }
+
+}
+
+#else
+void gpio_uart_active(int port, int no_irda) {}
+#endif
+void gpio_uart_inactive(int port, int no_irda) {}
+EXPORT_SYMBOL(gpio_uart_active);
+EXPORT_SYMBOL(gpio_uart_inactive);
diff --git a/arch/arm/mach-mx51/mx51_ccwmx51js_pmic_mc13892.c b/arch/arm/mach-mx51/mx51_ccwmx51js_pmic_mc13892.c
new file mode 100644
index 000000000000..f650724871ea
--- /dev/null
+++ b/arch/arm/mach-mx51/mx51_ccwmx51js_pmic_mc13892.c
@@ -0,0 +1,361 @@
+ /*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Digi International, Inc. All Rights Reserved.
+ */
+
+ /*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/pmic_external.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/mc13892/core.h>
+#include "iomux.h"
+
+/*
+ * Convenience conversion.
+ * Here atm, maybe there is somewhere better for this.
+ */
+#define mV_to_uV(mV) (mV * 1000)
+#define uV_to_mV(uV) (uV / 1000)
+#define V_to_uV(V) (mV_to_uV(V * 1000))
+#define uV_to_V(uV) (uV_to_mV(uV) / 1000)
+
+/* Coin cell charger enable */
+#define CIONCHEN_LSH 23
+#define CIONCHEN_WID 1
+/* Coin cell charger voltage setting */
+#define VCOIN_LSH 20
+#define VCOIN_WID 3
+
+/* Coin Charger voltage */
+#define VCOIN_2_5V 0x0
+#define VCOIN_2_7V 0x1
+#define VCOIN_2_8V 0x2
+#define VCOIN_2_9V 0x3
+#define VCOIN_3_0V 0x4
+#define VCOIN_3_1V 0x5
+#define VCOIN_3_2V 0x6
+#define VCOIN_3_3V 0x7
+
+/* Keeps VSRTC and CLK32KMCU on for all states */
+#define DRM_LSH 4
+#define DRM_WID 1
+
+/* regulator standby mask */
+#define GEN1_STBY_MASK (1 << 1)
+#define IOHI_STBY_MASK (1 << 4)
+#define DIG_STBY_MASK (1 << 10)
+#define GEN2_STBY_MASK (1 << 13)
+#define PLL_STBY_MASK (1 << 16)
+#define USB2_STBY_MASK (1 << 19)
+
+#define GEN3_STBY_MASK (1 << 1)
+#define CAM_STBY_MASK (1 << 7)
+#define VIDEO_STBY_MASK (1 << 13)
+#define AUDIO_STBY_MASK (1 << 16)
+#define SD_STBY_MASK (1 << 19)
+
+/* 0x92412 */
+#define REG_MODE_0_ALL_MASK (GEN1_STBY_MASK |\
+ DIG_STBY_MASK | GEN2_STBY_MASK |\
+ PLL_STBY_MASK | USB2_STBY_MASK)
+/* 0x92082 */
+#define REG_MODE_1_ALL_MASK (GEN3_STBY_MASK | CAM_STBY_MASK |\
+ VIDEO_STBY_MASK | AUDIO_STBY_MASK |\
+ SD_STBY_MASK)
+
+/* CPU */
+static struct regulator_consumer_supply sw1_consumers[] = {
+ {
+ .supply = "cpu_vcc",
+ }
+};
+
+struct mc13892;
+
+static struct regulator_init_data sw1_init = {
+ .constraints = {
+ .name = "SW1",
+ .min_uV = mV_to_uV(600),
+ .max_uV = mV_to_uV(1375),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .valid_modes_mask = 0,
+ .always_on = 1,
+ .boot_on = 1,
+ .initial_state = PM_SUSPEND_MEM,
+ .state_mem = {
+ .uV = 850000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ },
+ .num_consumer_supplies = ARRAY_SIZE(sw1_consumers),
+ .consumer_supplies = sw1_consumers,
+};
+
+static struct regulator_init_data sw2_init = {
+ .constraints = {
+ .name = "SW2",
+ .min_uV = mV_to_uV(900),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ .initial_state = PM_SUSPEND_MEM,
+ .state_mem = {
+ .uV = 950000,
+ .mode = REGULATOR_MODE_NORMAL,
+ .enabled = 1,
+ },
+ }
+};
+
+static struct regulator_init_data sw3_init = {
+ .constraints = {
+ .name = "SW3",
+ .min_uV = mV_to_uV(1100),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data sw4_init = {
+ .constraints = {
+ .name = "SW4",
+ .min_uV = mV_to_uV(1100),
+ .max_uV = mV_to_uV(1850),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data viohi_init = {
+ .constraints = {
+ .name = "VIOHI",
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vusb_init = {
+ .constraints = {
+ .name = "VUSB",
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data swbst_init = {
+ .constraints = {
+ .name = "SWBST",
+ }
+};
+
+static struct regulator_init_data vdig_init = {
+ .constraints = {
+ .name = "VDIG",
+ .min_uV = mV_to_uV(1050),
+ .max_uV = mV_to_uV(1800),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vpll_init = {
+ .constraints = {
+ .name = "VPLL",
+ .min_uV = mV_to_uV(1050),
+ .max_uV = mV_to_uV(1800),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vusb2_init = {
+ .constraints = {
+ .name = "VUSB2",
+ .min_uV = mV_to_uV(2400),
+ .max_uV = mV_to_uV(2775),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .boot_on = 1,
+ }
+};
+
+static struct regulator_init_data vvideo_init = {
+ .constraints = {
+ .name = "VVIDEO",
+ .min_uV = mV_to_uV(2775),
+ .max_uV = mV_to_uV(2775),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .always_on = 1,
+ .apply_uV =1,
+ }
+};
+
+static struct regulator_init_data vaudio_init = {
+ .constraints = {
+ .name = "VAUDIO",
+ .min_uV = mV_to_uV(2300),
+ .max_uV = mV_to_uV(3000),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vsd_init = {
+ .constraints = {
+ .name = "VSD",
+ .min_uV = mV_to_uV(1800),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vcam_init = {
+ .constraints = {
+ .name = "VCAM",
+ .min_uV = mV_to_uV(2500),
+ .max_uV = mV_to_uV(3000),
+ .valid_ops_mask =
+ REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL,
+ }
+};
+
+static struct regulator_init_data vgen1_init = {
+ .constraints = {
+ .name = "VGEN1",
+ .min_uV = mV_to_uV(1200),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vgen2_init = {
+ .constraints = {
+ .name = "VGEN2",
+ .min_uV = mV_to_uV(1200),
+ .max_uV = mV_to_uV(3150),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data vgen3_init = {
+ .constraints = {
+ .name = "VGEN3",
+ .min_uV = mV_to_uV(1800),
+ .max_uV = mV_to_uV(2900),
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ }
+};
+
+static struct regulator_init_data gpo1_init = {
+ .constraints = {
+ .name = "GPO1",
+ }
+};
+
+static struct regulator_init_data gpo2_init = {
+ .constraints = {
+ .name = "GPO2",
+ }
+};
+
+static struct regulator_init_data gpo3_init = {
+ .constraints = {
+ .name = "GPO3",
+ }
+};
+
+static struct regulator_init_data gpo4_init = {
+ .constraints = {
+ .name = "GPO4",
+ }
+};
+
+static int mc13892_regulator_init(struct mc13892 *mc13892)
+{
+ unsigned int value, register_mask;
+ printk("Initializing regulators for Babbage.\n");
+ if (mxc_cpu_is_rev(CHIP_REV_2_0) < 0)
+ sw2_init.constraints.state_mem.uV = 1100000;
+ else if (mxc_cpu_is_rev(CHIP_REV_2_0) == 1) {
+ sw2_init.constraints.state_mem.uV = 1250000;
+ sw1_init.constraints.state_mem.uV = 1000000;
+ }
+
+ /* enable standby controll for all regulators */
+ pmic_read_reg(REG_MODE_0, &value, 0xffffff);
+ value |= REG_MODE_0_ALL_MASK;
+ pmic_write_reg(REG_MODE_0, value, 0xffffff);
+
+ pmic_read_reg(REG_MODE_1, &value, 0xffffff);
+ value |= REG_MODE_1_ALL_MASK;
+ pmic_write_reg(REG_MODE_1, value, 0xffffff);
+
+ /* Enable coin cell charger */
+ value = BITFVAL(CIONCHEN, 1) | BITFVAL(VCOIN, VCOIN_3_0V);
+ register_mask = BITFMASK(CIONCHEN) | BITFMASK(VCOIN);
+ pmic_write_reg(REG_POWER_CTL0, value, register_mask);
+
+#if defined(CONFIG_RTC_DRV_MXC_V2) || defined(CONFIG_RTC_DRV_MXC_V2_MODULE)
+ value = BITFVAL(DRM, 1);
+ register_mask = BITFMASK(DRM);
+ pmic_write_reg(REG_POWER_CTL0, value, register_mask);
+#endif
+
+ mc13892_register_regulator(mc13892, MC13892_SW1, &sw1_init);
+ mc13892_register_regulator(mc13892, MC13892_SW2, &sw2_init);
+ mc13892_register_regulator(mc13892, MC13892_SW3, &sw3_init);
+ mc13892_register_regulator(mc13892, MC13892_SW4, &sw4_init);
+ mc13892_register_regulator(mc13892, MC13892_SWBST, &swbst_init);
+ mc13892_register_regulator(mc13892, MC13892_VIOHI, &viohi_init);
+ mc13892_register_regulator(mc13892, MC13892_VPLL, &vpll_init);
+ mc13892_register_regulator(mc13892, MC13892_VDIG, &vdig_init);
+ mc13892_register_regulator(mc13892, MC13892_VSD, &vsd_init);
+ mc13892_register_regulator(mc13892, MC13892_VUSB2, &vusb2_init);
+ mc13892_register_regulator(mc13892, MC13892_VVIDEO, &vvideo_init);
+ mc13892_register_regulator(mc13892, MC13892_VAUDIO, &vaudio_init);
+ mc13892_register_regulator(mc13892, MC13892_VCAM, &vcam_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN1, &vgen1_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN2, &vgen2_init);
+ mc13892_register_regulator(mc13892, MC13892_VGEN3, &vgen3_init);
+ mc13892_register_regulator(mc13892, MC13892_VUSB, &vusb_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO1, &gpo1_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO2, &gpo2_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO3, &gpo3_init);
+ mc13892_register_regulator(mc13892, MC13892_GPO4, &gpo4_init);
+
+ return 0;
+}
+
+static struct mc13892_platform_data mc13892_plat = {
+ .init = mc13892_regulator_init,
+};
+
+static struct spi_board_info __initdata mc13892_spi_device = {
+ .modalias = "pmic_spi",
+ .irq = IOMUX_TO_IRQ(MX51_PIN_GPIO1_5),
+ .max_speed_hz = 1000000, /* max spi SCK clock speed in HZ */
+ .bus_num = 1,
+ .chip_select = 0,
+ .platform_data = &mc13892_plat,
+};
+
+
+int __init ccwmx51_init_mc13892(void)
+{
+ return spi_register_board_info(&mc13892_spi_device, 1);
+}
+
diff --git a/arch/arm/mach-mx51/mx51_pins.h b/arch/arm/mach-mx51/mx51_pins.h
new file mode 100644
index 000000000000..c0905a025556
--- /dev/null
+++ b/arch/arm/mach-mx51/mx51_pins.h
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MX51_PINS_H__
+#define __ASM_ARCH_MXC_MX51_PINS_H__
+
+/*!
+ * @file arch-mxc/mx51_pins.h
+ *
+ * @brief MX51 I/O Pin List
+ *
+ * @ingroup GPIO_MX51
+ */
+
+#ifndef __ASSEMBLY__
+
+/*!
+ * @name IOMUX/PAD Bit field definitions
+ */
+
+/*! @{ */
+
+/*!
+ * In order to identify pins more effectively, each mux-controlled pin's
+ * enumerated value is constructed in the following way:
+ *
+ * -------------------------------------------------------------------
+ * 31-29 | 28 - 24 | 23 - 21 | 20 - 10| 9 - 0
+ * -------------------------------------------------------------------
+ * IO_P | IO_I | GPIO_I | PAD_I | MUX_I
+ * -------------------------------------------------------------------
+ *
+ * Bit 0 to 9 contains MUX_I used to identify the register
+ * offset (0-based. base is IOMUX_module_base) defined in the Section
+ * "sw_pad_ctl & sw_mux_ctl details" of the IC Spec. The
+ * similar field definitions are used for the pad control register.
+ * For example, the MX51_PIN_ETM_D0 is defined in the enumeration:
+ * ( (0x28 - MUX_I_START) << MUX_I)|( (0x250 - PAD_I_START) << PAD_I)
+ * It means the mux control register is at register offset 0x28. The pad control
+ * register offset is: 0x250 and also occupy the least significant bits
+ * within the register.
+ */
+
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * MUX control register offset
+ */
+#define MUX_I 0
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent the
+ * PAD control register offset
+ */
+#define PAD_I 10
+/*!
+ * Starting bit position within each entry of \b iomux_pins to represent which
+ * mux mode is for GPIO (0-based)
+ */
+#define GPIO_I 21
+
+#define NON_GPIO_PORT 0x7
+#define PIN_TO_MUX_MASK ((1 << (PAD_I - MUX_I)) - 1)
+#define PIN_TO_PAD_MASK ((1 << (GPIO_I - PAD_I)) - 1)
+#define PIN_TO_ALT_GPIO_MASK ((1 << (MUX_IO_I - GPIO_I)) - 1)
+
+#define NON_MUX_I PIN_TO_MUX_MASK
+#define MUX_I_START 0x001C
+#define PAD_I_START 0x3F0
+#define INPUT_CTL_START 0x8C4
+#define INPUT_CTL_START_TO1 0x928
+#define MUX_I_END (PAD_I_START - 4)
+
+#define _MXC_BUILD_PIN(gp, gi, ga, mi, pi) \
+ (((gp) << MUX_IO_P) | ((gi) << MUX_IO_I) | \
+ ((mi) << MUX_I) | \
+ ((pi - PAD_I_START) << PAD_I) | \
+ ((ga) << GPIO_I))
+
+#define _MXC_BUILD_GPIO_PIN(gp, gi, ga, mi, pi) \
+ _MXC_BUILD_PIN(gp, gi, ga, mi, pi)
+
+#define _MXC_BUILD_NON_GPIO_PIN(mi, pi) \
+ _MXC_BUILD_PIN(NON_GPIO_PORT, 0, 0, mi, pi)
+
+#define PIN_TO_IOMUX_MUX(pin) ((pin >> MUX_I) & PIN_TO_MUX_MASK)
+#define PIN_TO_IOMUX_PAD(pin) ((pin >> PAD_I) & PIN_TO_PAD_MASK)
+#define PIN_TO_ALT_GPIO(pin) ((pin >> GPIO_I) & PIN_TO_ALT_GPIO_MASK)
+#define PIN_TO_IOMUX_INDEX(pin) (PIN_TO_IOMUX_MUX(pin) >> 2)
+
+/*! @} End IOMUX/PAD Bit field definitions */
+
+/*!
+ * This enumeration is constructed based on the Section
+ * "sw_pad_ctl & sw_mux_ctl details" of the MX51 IC Spec. Each enumerated
+ * value is constructed based on the rules described above.
+ */
+enum iomux_pins {
+ MX51_PIN_EIM_DA0 = _MXC_BUILD_NON_GPIO_PIN(0x1C, 0x7A8),
+ MX51_PIN_EIM_DA1 = _MXC_BUILD_NON_GPIO_PIN(0x20, 0x7A8),
+ MX51_PIN_EIM_DA2 = _MXC_BUILD_NON_GPIO_PIN(0x24, 0x7A8),
+ MX51_PIN_EIM_DA3 = _MXC_BUILD_NON_GPIO_PIN(0x28, 0x7A8),
+ MX51_PIN_EIM_DA4 = _MXC_BUILD_NON_GPIO_PIN(0x2C, 0x7AC),
+ MX51_PIN_EIM_DA5 = _MXC_BUILD_NON_GPIO_PIN(0x30, 0x7AC),
+ MX51_PIN_EIM_DA6 = _MXC_BUILD_NON_GPIO_PIN(0x34, 0x7AC),
+ MX51_PIN_EIM_DA7 = _MXC_BUILD_NON_GPIO_PIN(0x38, 0x7AC),
+ MX51_PIN_EIM_DA8 = _MXC_BUILD_NON_GPIO_PIN(0x3C, 0x7B0),
+ MX51_PIN_EIM_DA9 = _MXC_BUILD_NON_GPIO_PIN(0x40, 0x7B0),
+ MX51_PIN_EIM_DA10 = _MXC_BUILD_NON_GPIO_PIN(0x44, 0x7B0),
+ MX51_PIN_EIM_DA11 = _MXC_BUILD_NON_GPIO_PIN(0x48, 0x7B0),
+ MX51_PIN_EIM_DA12 = _MXC_BUILD_NON_GPIO_PIN(0x4C, 0x7BC),
+ MX51_PIN_EIM_DA13 = _MXC_BUILD_NON_GPIO_PIN(0x50, 0x7BC),
+ MX51_PIN_EIM_DA14 = _MXC_BUILD_NON_GPIO_PIN(0x54, 0x7BC),
+ MX51_PIN_EIM_DA15 = _MXC_BUILD_NON_GPIO_PIN(0x58, 0x7BC),
+ MX51_PIN_EIM_D16 = _MXC_BUILD_GPIO_PIN(1, 0, 1, 0x5C, 0x3F0),
+ MX51_PIN_EIM_D17 = _MXC_BUILD_GPIO_PIN(1, 1, 1, 0x60, 0x3F4),
+ MX51_PIN_EIM_D18 = _MXC_BUILD_GPIO_PIN(1, 2, 1, 0x64, 0x3F8),
+ MX51_PIN_EIM_D19 = _MXC_BUILD_GPIO_PIN(1, 3, 1, 0x68, 0x3FC),
+ MX51_PIN_EIM_D20 = _MXC_BUILD_GPIO_PIN(1, 4, 1, 0x6C, 0x400),
+ MX51_PIN_EIM_D21 = _MXC_BUILD_GPIO_PIN(1, 5, 1, 0x70, 0x404),
+ MX51_PIN_EIM_D22 = _MXC_BUILD_GPIO_PIN(1, 6, 1, 0x74, 0x408),
+ MX51_PIN_EIM_D23 = _MXC_BUILD_GPIO_PIN(1, 7, 1, 0x78, 0x40C),
+ MX51_PIN_EIM_D24 = _MXC_BUILD_GPIO_PIN(1, 8, 1, 0x7C, 0x410),
+ MX51_PIN_EIM_D25 = _MXC_BUILD_NON_GPIO_PIN(0x80, 0x414),
+ MX51_PIN_EIM_D26 = _MXC_BUILD_NON_GPIO_PIN(0x84, 0x418),
+ MX51_PIN_EIM_D27 = _MXC_BUILD_GPIO_PIN(1, 9, 1, 0x88, 0x41C),
+ MX51_PIN_EIM_D28 = _MXC_BUILD_NON_GPIO_PIN(0x8C, 0x420),
+ MX51_PIN_EIM_D29 = _MXC_BUILD_NON_GPIO_PIN(0x90, 0x424),
+ MX51_PIN_EIM_D30 = _MXC_BUILD_NON_GPIO_PIN(0x94, 0x428),
+ MX51_PIN_EIM_D31 = _MXC_BUILD_NON_GPIO_PIN(0x98, 0x42C),
+ MX51_PIN_EIM_A16 = _MXC_BUILD_GPIO_PIN(1, 10, 1, 0x9C, 0x430),
+ MX51_PIN_EIM_A17 = _MXC_BUILD_GPIO_PIN(1, 11, 1, 0xA0, 0x434),
+ MX51_PIN_EIM_A18 = _MXC_BUILD_GPIO_PIN(1, 12, 1, 0xA4, 0x438),
+ MX51_PIN_EIM_A19 = _MXC_BUILD_GPIO_PIN(1, 13, 1, 0xA8, 0x43C),
+ MX51_PIN_EIM_A20 = _MXC_BUILD_GPIO_PIN(1, 14, 1, 0xAC, 0x440),
+ MX51_PIN_EIM_A21 = _MXC_BUILD_GPIO_PIN(1, 15, 1, 0xB0, 0x444),
+ MX51_PIN_EIM_A22 = _MXC_BUILD_GPIO_PIN(1, 16, 1, 0xB4, 0x448),
+ MX51_PIN_EIM_A23 = _MXC_BUILD_GPIO_PIN(1, 17, 1, 0xB8, 0x44C),
+ MX51_PIN_EIM_A24 = _MXC_BUILD_GPIO_PIN(1, 18, 1, 0xBC, 0x450),
+ MX51_PIN_EIM_A25 = _MXC_BUILD_GPIO_PIN(1, 19, 1, 0xC0, 0x454),
+ MX51_PIN_EIM_A26 = _MXC_BUILD_GPIO_PIN(1, 20, 1, 0xC4, 0x458),
+ MX51_PIN_EIM_A27 = _MXC_BUILD_GPIO_PIN(1, 21, 1, 0xC8, 0x45C),
+ MX51_PIN_EIM_EB0 = _MXC_BUILD_NON_GPIO_PIN(0xCC, 0x460),
+ MX51_PIN_EIM_EB1 = _MXC_BUILD_NON_GPIO_PIN(0xD0, 0x464),
+ MX51_PIN_EIM_EB2 = _MXC_BUILD_GPIO_PIN(1, 22, 1, 0xD4, 0x468),
+ MX51_PIN_EIM_EB3 = _MXC_BUILD_GPIO_PIN(1, 23, 1, 0xD8, 0x46C),
+ MX51_PIN_EIM_OE = _MXC_BUILD_GPIO_PIN(1, 24, 1, 0xDC, 0x470),
+ MX51_PIN_EIM_CS0 = _MXC_BUILD_GPIO_PIN(1, 25, 1, 0xE0, 0x474),
+ MX51_PIN_EIM_CS1 = _MXC_BUILD_GPIO_PIN(1, 26, 1, 0xE4, 0x478),
+ MX51_PIN_EIM_CS2 = _MXC_BUILD_GPIO_PIN(1, 27, 1, 0xE8, 0x47C),
+ MX51_PIN_EIM_CS3 = _MXC_BUILD_GPIO_PIN(1, 28, 1, 0xEC, 0x480),
+ MX51_PIN_EIM_CS4 = _MXC_BUILD_GPIO_PIN(1, 29, 1, 0xF0, 0x484),
+ MX51_PIN_EIM_CS5 = _MXC_BUILD_GPIO_PIN(1, 30, 1, 0xF4, 0x488),
+ MX51_PIN_EIM_DTACK = _MXC_BUILD_GPIO_PIN(1, 31, 1, 0xF8, 0x48C),
+ MX51_PIN_EIM_LBA = _MXC_BUILD_GPIO_PIN(2, 1, 1, 0xFC, 0x494),
+ MX51_PIN_EIM_CRE = _MXC_BUILD_GPIO_PIN(2, 2, 1, 0x100, 0x4A0),
+ MX51_PIN_DRAM_CS1 = _MXC_BUILD_NON_GPIO_PIN(0x104, 0x4D0),
+ MX51_PIN_NANDF_WE_B = _MXC_BUILD_GPIO_PIN(2, 3, 3, 0x108, 0x4E4),
+ MX51_PIN_NANDF_RE_B = _MXC_BUILD_GPIO_PIN(2, 4, 3, 0x10C, 0x4E8),
+ MX51_PIN_NANDF_ALE = _MXC_BUILD_GPIO_PIN(2, 5, 3, 0x110, 0x4EC),
+ MX51_PIN_NANDF_CLE = _MXC_BUILD_GPIO_PIN(2, 6, 3, 0x114, 0x4F0),
+ MX51_PIN_NANDF_WP_B = _MXC_BUILD_GPIO_PIN(2, 7, 3, 0x118, 0x4F4),
+ MX51_PIN_NANDF_RB0 = _MXC_BUILD_GPIO_PIN(2, 8, 3, 0x11C, 0x4F8),
+ MX51_PIN_NANDF_RB1 = _MXC_BUILD_GPIO_PIN(2, 9, 3, 0x120, 0x4FC),
+ MX51_PIN_NANDF_RB2 = _MXC_BUILD_GPIO_PIN(2, 10, 3, 0x124, 0x500),
+ MX51_PIN_NANDF_RB3 = _MXC_BUILD_GPIO_PIN(2, 11, 3, 0x128, 0x504),
+ MX51_PIN_GPIO_NAND = _MXC_BUILD_GPIO_PIN(2, 12, 3, 0x12C, 0x514),
+ MX51_PIN_NANDF_RB4 = MX51_PIN_GPIO_NAND,
+ MX51_PIN_NANDF_RB5 = _MXC_BUILD_GPIO_PIN(2, 13, 3, 0x130, 0x5D8),
+ MX51_PIN_NANDF_RB6 = _MXC_BUILD_GPIO_PIN(2, 14, 3, 0x134, 0x5DC),
+ MX51_PIN_NANDF_RB7 = _MXC_BUILD_GPIO_PIN(2, 15, 3, 0x138, 0x5E0),
+ MX51_PIN_NANDF_CS0 = _MXC_BUILD_GPIO_PIN(2, 16, 3, 0x130, 0x518),
+ MX51_PIN_NANDF_CS1 = _MXC_BUILD_GPIO_PIN(2, 17, 3, 0x134, 0x51C),
+ MX51_PIN_NANDF_CS2 = _MXC_BUILD_GPIO_PIN(2, 18, 3, 0x138, 0x520),
+ MX51_PIN_NANDF_CS3 = _MXC_BUILD_GPIO_PIN(2, 19, 3, 0x13C, 0x524),
+ MX51_PIN_NANDF_CS4 = _MXC_BUILD_GPIO_PIN(2, 20, 3, 0x140, 0x528),
+ MX51_PIN_NANDF_CS5 = _MXC_BUILD_GPIO_PIN(2, 21, 3, 0x144, 0x52C),
+ MX51_PIN_NANDF_CS6 = _MXC_BUILD_GPIO_PIN(2, 22, 3, 0x148, 0x530),
+ MX51_PIN_NANDF_CS7 = _MXC_BUILD_GPIO_PIN(2, 23, 3, 0x14C, 0x534),
+ MX51_PIN_NANDF_RDY_INT = _MXC_BUILD_GPIO_PIN(2, 24, 3, 0x150, 0x538),
+ MX51_PIN_NANDF_D15 = _MXC_BUILD_GPIO_PIN(2, 25, 3, 0x154, 0x53C),
+ MX51_PIN_NANDF_D14 = _MXC_BUILD_GPIO_PIN(2, 26, 3, 0x158, 0x540),
+ MX51_PIN_NANDF_D13 = _MXC_BUILD_GPIO_PIN(2, 27, 3, 0x15C, 0x544),
+ MX51_PIN_NANDF_D12 = _MXC_BUILD_GPIO_PIN(2, 28, 3, 0x160, 0x548),
+ MX51_PIN_NANDF_D11 = _MXC_BUILD_GPIO_PIN(2, 29, 3, 0x164, 0x54C),
+ MX51_PIN_NANDF_D10 = _MXC_BUILD_GPIO_PIN(2, 30, 3, 0x168, 0x550),
+ MX51_PIN_NANDF_D9 = _MXC_BUILD_GPIO_PIN(2, 31, 3, 0x16C, 0x554),
+ MX51_PIN_NANDF_D8 = _MXC_BUILD_GPIO_PIN(3, 0, 3, 0x170, 0x558),
+ MX51_PIN_NANDF_D7 = _MXC_BUILD_GPIO_PIN(3, 1, 3, 0x174, 0x55C),
+ MX51_PIN_NANDF_D6 = _MXC_BUILD_GPIO_PIN(3, 2, 3, 0x178, 0x560),
+ MX51_PIN_NANDF_D5 = _MXC_BUILD_GPIO_PIN(3, 3, 3, 0x17C, 0x564),
+ MX51_PIN_NANDF_D4 = _MXC_BUILD_GPIO_PIN(3, 4, 3, 0x180, 0x568),
+ MX51_PIN_NANDF_D3 = _MXC_BUILD_GPIO_PIN(3, 5, 3, 0x184, 0x56C),
+ MX51_PIN_NANDF_D2 = _MXC_BUILD_GPIO_PIN(3, 6, 3, 0x188, 0x570),
+ MX51_PIN_NANDF_D1 = _MXC_BUILD_GPIO_PIN(3, 7, 3, 0x18C, 0x574),
+ MX51_PIN_NANDF_D0 = _MXC_BUILD_GPIO_PIN(3, 8, 3, 0x190, 0x578),
+ MX51_PIN_CSI1_D8 = _MXC_BUILD_GPIO_PIN(2, 12, 3, 0x194, 0x57C),
+ MX51_PIN_CSI1_D9 = _MXC_BUILD_GPIO_PIN(2, 13, 3, 0x198, 0x580),
+ MX51_PIN_CSI1_D10 = _MXC_BUILD_NON_GPIO_PIN(0x19C, 0x584),
+ MX51_PIN_CSI1_D11 = _MXC_BUILD_NON_GPIO_PIN(0x1A0, 0x588),
+ MX51_PIN_CSI1_D12 = _MXC_BUILD_NON_GPIO_PIN(0x1A4, 0x58C),
+ MX51_PIN_CSI1_D13 = _MXC_BUILD_NON_GPIO_PIN(0x1A8, 0x590),
+ MX51_PIN_CSI1_D14 = _MXC_BUILD_NON_GPIO_PIN(0x1AC, 0x594),
+ MX51_PIN_CSI1_D15 = _MXC_BUILD_NON_GPIO_PIN(0x1B0, 0x598),
+ MX51_PIN_CSI1_D16 = _MXC_BUILD_NON_GPIO_PIN(0x1B4, 0x59C),
+ MX51_PIN_CSI1_D17 = _MXC_BUILD_NON_GPIO_PIN(0x1B8, 0x5A0),
+ MX51_PIN_CSI1_D18 = _MXC_BUILD_NON_GPIO_PIN(0x1BC, 0x5A4),
+ MX51_PIN_CSI1_D19 = _MXC_BUILD_NON_GPIO_PIN(0x1C0, 0x5A8),
+ MX51_PIN_CSI1_VSYNC = _MXC_BUILD_GPIO_PIN(2, 14, 3, 0x1C4, 0x5AC),
+ MX51_PIN_CSI1_HSYNC = _MXC_BUILD_GPIO_PIN(2, 15, 3, 0x1C8, 0x5B0),
+ MX51_PIN_CSI1_PIXCLK = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x5B4),
+ MX51_PIN_CSI1_MCLK = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x5B8),
+ MX51_PIN_CSI1_PKE0 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x860),
+ MX51_PIN_CSI2_D12 = _MXC_BUILD_GPIO_PIN(3, 9, 3, 0x1CC, 0x5BC),
+ MX51_PIN_CSI2_D13 = _MXC_BUILD_GPIO_PIN(3, 10, 3, 0x1D0, 0x5C0),
+ MX51_PIN_CSI2_D14 = _MXC_BUILD_GPIO_PIN(3, 11, 3, 0x1D4, 0x5C4),
+ MX51_PIN_CSI2_D15 = _MXC_BUILD_GPIO_PIN(3, 12, 3, 0x1D8, 0x5C8),
+ MX51_PIN_CSI2_D16 = _MXC_BUILD_GPIO_PIN(3, 11, 3, 0x1DC, 0x5CC),
+ MX51_PIN_CSI2_D17 = _MXC_BUILD_GPIO_PIN(3, 12, 3, 0x1E0, 0x5D0),
+ MX51_PIN_CSI2_D18 = _MXC_BUILD_GPIO_PIN(3, 11, 3, 0x1E4, 0x5D4),
+ MX51_PIN_CSI2_D19 = _MXC_BUILD_GPIO_PIN(3, 12, 3, 0x1E8, 0x5D8),
+ MX51_PIN_CSI2_VSYNC = _MXC_BUILD_GPIO_PIN(3, 13, 3, 0x1EC, 0x5DC),
+ MX51_PIN_CSI2_HSYNC = _MXC_BUILD_GPIO_PIN(3, 14, 3, 0x1F0, 0x5E0),
+ MX51_PIN_CSI2_PIXCLK = _MXC_BUILD_GPIO_PIN(3, 15, 3, 0x1F4, 0x5E4),
+ MX51_PIN_CSI2_PKE0 = _MXC_BUILD_NON_GPIO_PIN(NON_MUX_I, 0x81C),
+ MX51_PIN_I2C1_CLK = _MXC_BUILD_GPIO_PIN(3, 16, 3, 0x1F8, 0x5E8),
+ MX51_PIN_I2C1_DAT = _MXC_BUILD_GPIO_PIN(3, 17, 3, 0x1FC, 0x5EC),
+ MX51_PIN_AUD3_BB_TXD = _MXC_BUILD_GPIO_PIN(3, 18, 3, 0x200, 0x5F0),
+ MX51_PIN_AUD3_BB_RXD = _MXC_BUILD_GPIO_PIN(3, 19, 3, 0x204, 0x5F4),
+ MX51_PIN_AUD3_BB_CK = _MXC_BUILD_GPIO_PIN(3, 20, 3, 0x208, 0x5F8),
+ MX51_PIN_AUD3_BB_FS = _MXC_BUILD_GPIO_PIN(3, 21, 3, 0x20C, 0x5FC),
+ MX51_PIN_CSPI1_MOSI = _MXC_BUILD_GPIO_PIN(3, 22, 3, 0x210, 0x600),
+ MX51_PIN_CSPI1_MISO = _MXC_BUILD_GPIO_PIN(3, 23, 3, 0x214, 0x604),
+ MX51_PIN_CSPI1_SS0 = _MXC_BUILD_GPIO_PIN(3, 24, 3, 0x218, 0x608),
+ MX51_PIN_CSPI1_SS1 = _MXC_BUILD_GPIO_PIN(3, 25, 3, 0x21C, 0x60C),
+ MX51_PIN_CSPI1_RDY = _MXC_BUILD_GPIO_PIN(3, 26, 3, 0x220, 0x610),
+ MX51_PIN_CSPI1_SCLK = _MXC_BUILD_GPIO_PIN(3, 27, 3, 0x224, 0x614),
+ MX51_PIN_UART1_RXD = _MXC_BUILD_GPIO_PIN(3, 28, 3, 0x228, 0x618),
+ MX51_PIN_UART1_TXD = _MXC_BUILD_GPIO_PIN(3, 29, 3, 0x22C, 0x61C),
+ MX51_PIN_UART1_RTS = _MXC_BUILD_GPIO_PIN(3, 30, 3, 0x230, 0x620),
+ MX51_PIN_UART1_CTS = _MXC_BUILD_GPIO_PIN(3, 31, 3, 0x234, 0x624),
+ MX51_PIN_UART2_RXD = _MXC_BUILD_GPIO_PIN(0, 20, 3, 0x238, 0x628),
+ MX51_PIN_UART2_TXD = _MXC_BUILD_GPIO_PIN(0, 21, 3, 0x23C, 0x62C),
+ MX51_PIN_UART3_RXD = _MXC_BUILD_GPIO_PIN(0, 22, 3, 0x240, 0x630),
+ MX51_PIN_UART3_TXD = _MXC_BUILD_GPIO_PIN(0, 23, 3, 0x244, 0x634),
+ MX51_PIN_OWIRE_LINE = _MXC_BUILD_GPIO_PIN(0, 24, 3, 0x248, 0x638),
+ MX51_PIN_KEY_ROW0 = _MXC_BUILD_NON_GPIO_PIN(0x24C, 0x63C),
+ MX51_PIN_KEY_ROW1 = _MXC_BUILD_NON_GPIO_PIN(0x250, 0x640),
+ MX51_PIN_KEY_ROW2 = _MXC_BUILD_NON_GPIO_PIN(0x254, 0x644),
+ MX51_PIN_KEY_ROW3 = _MXC_BUILD_NON_GPIO_PIN(0x258, 0x648),
+ MX51_PIN_KEY_COL0 = _MXC_BUILD_NON_GPIO_PIN(0x25C, 0x64C),
+ MX51_PIN_KEY_COL1 = _MXC_BUILD_NON_GPIO_PIN(0x260, 0x650),
+ MX51_PIN_KEY_COL2 = _MXC_BUILD_NON_GPIO_PIN(0x264, 0x654),
+ MX51_PIN_KEY_COL3 = _MXC_BUILD_NON_GPIO_PIN(0x268, 0x658),
+ MX51_PIN_KEY_COL4 = _MXC_BUILD_NON_GPIO_PIN(0x26C, 0x65C),
+ MX51_PIN_KEY_COL5 = _MXC_BUILD_NON_GPIO_PIN(0x270, 0x660),
+ MX51_PIN_USBH1_CLK = _MXC_BUILD_GPIO_PIN(0, 25, 2, 0x278, 0x678),
+ MX51_PIN_USBH1_DIR = _MXC_BUILD_GPIO_PIN(0, 26, 2, 0x27C, 0x67C),
+ MX51_PIN_USBH1_STP = _MXC_BUILD_GPIO_PIN(0, 27, 2, 0x280, 0x680),
+ MX51_PIN_USBH1_NXT = _MXC_BUILD_GPIO_PIN(0, 28, 2, 0x284, 0x684),
+ MX51_PIN_USBH1_DATA0 = _MXC_BUILD_GPIO_PIN(0, 11, 2, 0x288, 0x688),
+ MX51_PIN_USBH1_DATA1 = _MXC_BUILD_GPIO_PIN(0, 12, 2, 0x28C, 0x68C),
+ MX51_PIN_USBH1_DATA2 = _MXC_BUILD_GPIO_PIN(0, 13, 2, 0x290, 0x690),
+ MX51_PIN_USBH1_DATA3 = _MXC_BUILD_GPIO_PIN(0, 14, 2, 0x294, 0x694),
+ MX51_PIN_USBH1_DATA4 = _MXC_BUILD_GPIO_PIN(0, 15, 2, 0x298, 0x698),
+ MX51_PIN_USBH1_DATA5 = _MXC_BUILD_GPIO_PIN(0, 16, 2, 0x29C, 0x69C),
+ MX51_PIN_USBH1_DATA6 = _MXC_BUILD_GPIO_PIN(0, 17, 2, 0x2A0, 0x6A0),
+ MX51_PIN_USBH1_DATA7 = _MXC_BUILD_GPIO_PIN(0, 18, 2, 0x2A4, 0x6A4),
+ MX51_PIN_DI1_PIN11 = _MXC_BUILD_GPIO_PIN(2, 0, 4, 0x2A8, 0x6A8),
+ MX51_PIN_DI1_PIN12 = _MXC_BUILD_GPIO_PIN(2, 1, 4, 0x2AC, 0x6AC),
+ MX51_PIN_DI1_PIN13 = _MXC_BUILD_GPIO_PIN(2, 2, 4, 0x2B0, 0x6B0),
+ MX51_PIN_DI1_D0_CS = _MXC_BUILD_GPIO_PIN(2, 3, 4, 0x2B4, 0x6B4),
+ MX51_PIN_DI1_D1_CS = _MXC_BUILD_GPIO_PIN(2, 4, 4, 0x2B8, 0x6B8),
+ MX51_PIN_DISPB2_SER_DIN = _MXC_BUILD_GPIO_PIN(2, 5, 4, 0x2BC, 0x6BC),
+ MX51_PIN_DISPB2_SER_DIO = _MXC_BUILD_GPIO_PIN(2, 6, 4, 0x2C0, 0x6C0),
+ MX51_PIN_DISPB2_SER_CLK = _MXC_BUILD_GPIO_PIN(2, 7, 4, 0x2C4, 0x6C4),
+ MX51_PIN_DISPB2_SER_RS = _MXC_BUILD_GPIO_PIN(2, 8, 4, 0x2C8, 0x6C8),
+ MX51_PIN_DISP1_DAT0 = _MXC_BUILD_NON_GPIO_PIN(0x2CC, 0x6CC),
+ MX51_PIN_DISP1_DAT1 = _MXC_BUILD_NON_GPIO_PIN(0x2D0, 0x6D0),
+ MX51_PIN_DISP1_DAT2 = _MXC_BUILD_NON_GPIO_PIN(0x2D4, 0x6D4),
+ MX51_PIN_DISP1_DAT3 = _MXC_BUILD_NON_GPIO_PIN(0x2D8, 0x6D8),
+ MX51_PIN_DISP1_DAT4 = _MXC_BUILD_NON_GPIO_PIN(0x2DC, 0x6DC),
+ MX51_PIN_DISP1_DAT5 = _MXC_BUILD_NON_GPIO_PIN(0x2E0, 0x6E0),
+ MX51_PIN_DISP1_DAT6 = _MXC_BUILD_NON_GPIO_PIN(0x2E4, 0x6E4),
+ MX51_PIN_DISP1_DAT7 = _MXC_BUILD_NON_GPIO_PIN(0x2E8, 0x6E8),
+ MX51_PIN_DISP1_DAT8 = _MXC_BUILD_NON_GPIO_PIN(0x2EC, 0x6EC),
+ MX51_PIN_DISP1_DAT9 = _MXC_BUILD_NON_GPIO_PIN(0x2F0, 0x6F0),
+ MX51_PIN_DISP1_DAT10 = _MXC_BUILD_NON_GPIO_PIN(0x2F4, 0x6F4),
+ MX51_PIN_DISP1_DAT11 = _MXC_BUILD_NON_GPIO_PIN(0x2F8, 0x6F8),
+ MX51_PIN_DISP1_DAT12 = _MXC_BUILD_NON_GPIO_PIN(0x2FC, 0x6FC),
+ MX51_PIN_DISP1_DAT13 = _MXC_BUILD_NON_GPIO_PIN(0x300, 0x700),
+ MX51_PIN_DISP1_DAT14 = _MXC_BUILD_NON_GPIO_PIN(0x304, 0x704),
+ MX51_PIN_DISP1_DAT15 = _MXC_BUILD_NON_GPIO_PIN(0x308, 0x708),
+ MX51_PIN_DISP1_DAT16 = _MXC_BUILD_NON_GPIO_PIN(0x30C, 0x70C),
+ MX51_PIN_DISP1_DAT17 = _MXC_BUILD_NON_GPIO_PIN(0x310, 0x710),
+ MX51_PIN_DISP1_DAT18 = _MXC_BUILD_NON_GPIO_PIN(0x314, 0x714),
+ MX51_PIN_DISP1_DAT19 = _MXC_BUILD_NON_GPIO_PIN(0x318, 0x718),
+ MX51_PIN_DISP1_DAT20 = _MXC_BUILD_NON_GPIO_PIN(0x31C, 0x71C),
+ MX51_PIN_DISP1_DAT21 = _MXC_BUILD_NON_GPIO_PIN(0x320, 0x720),
+ MX51_PIN_DISP1_DAT22 = _MXC_BUILD_NON_GPIO_PIN(0x324, 0x724),
+ MX51_PIN_DISP1_DAT23 = _MXC_BUILD_NON_GPIO_PIN(0x328, 0x728),
+ MX51_PIN_DI1_PIN3 = _MXC_BUILD_NON_GPIO_PIN(0x32C, 0x72C),
+ MX51_PIN_DI1_PIN2 = _MXC_BUILD_NON_GPIO_PIN(0x330, 0x734),
+ MX51_PIN_DI_GP1 = _MXC_BUILD_NON_GPIO_PIN(0x334, 0x73C),
+ MX51_PIN_DI_GP2 = _MXC_BUILD_NON_GPIO_PIN(0x338, 0x740),
+ MX51_PIN_DI_GP3 = _MXC_BUILD_NON_GPIO_PIN(0x33C, 0x744),
+ MX51_PIN_DI2_PIN4 = _MXC_BUILD_NON_GPIO_PIN(0x340, 0x748),
+ MX51_PIN_DI2_PIN2 = _MXC_BUILD_NON_GPIO_PIN(0x344, 0x74C),
+ MX51_PIN_DI2_PIN3 = _MXC_BUILD_NON_GPIO_PIN(0x348, 0x750),
+ MX51_PIN_DI2_DISP_CLK = _MXC_BUILD_NON_GPIO_PIN(0x34C, 0x754),
+ MX51_PIN_DI_GP4 = _MXC_BUILD_NON_GPIO_PIN(0x350, 0x758),
+ MX51_PIN_DISP2_DAT0 = _MXC_BUILD_NON_GPIO_PIN(0x354, 0x75C),
+ MX51_PIN_DISP2_DAT1 = _MXC_BUILD_NON_GPIO_PIN(0x358, 0x760),
+ MX51_PIN_DISP2_DAT2 = _MXC_BUILD_NON_GPIO_PIN(0x35C, 0x764),
+ MX51_PIN_DISP2_DAT3 = _MXC_BUILD_NON_GPIO_PIN(0x360, 0x768),
+ MX51_PIN_DISP2_DAT4 = _MXC_BUILD_NON_GPIO_PIN(0x364, 0x76C),
+ MX51_PIN_DISP2_DAT5 = _MXC_BUILD_NON_GPIO_PIN(0x368, 0x770),
+ MX51_PIN_DISP2_DAT6 = _MXC_BUILD_GPIO_PIN(0, 19, 5, 0x36C, 0x774),
+ MX51_PIN_DISP2_DAT7 = _MXC_BUILD_GPIO_PIN(0, 29, 5, 0x370, 0x778),
+ MX51_PIN_DISP2_DAT8 = _MXC_BUILD_GPIO_PIN(0, 30, 5, 0x374, 0x77C),
+ MX51_PIN_DISP2_DAT9 = _MXC_BUILD_GPIO_PIN(0, 31, 5, 0x378, 0x780),
+ MX51_PIN_DISP2_DAT10 = _MXC_BUILD_NON_GPIO_PIN(0x37C, 0x784),
+ MX51_PIN_DISP2_DAT11 = _MXC_BUILD_NON_GPIO_PIN(0x380, 0x788),
+ MX51_PIN_DISP2_DAT12 = _MXC_BUILD_NON_GPIO_PIN(0x384, 0x78C),
+ MX51_PIN_DISP2_DAT13 = _MXC_BUILD_NON_GPIO_PIN(0x388, 0x790),
+ MX51_PIN_DISP2_DAT14 = _MXC_BUILD_NON_GPIO_PIN(0x38C, 0x794),
+ MX51_PIN_DISP2_DAT15 = _MXC_BUILD_NON_GPIO_PIN(0x390, 0x798),
+ MX51_PIN_SD1_CMD = _MXC_BUILD_NON_GPIO_PIN(0x394, 0x79C),
+ MX51_PIN_SD1_CLK = _MXC_BUILD_NON_GPIO_PIN(0x398, 0x7A0),
+ MX51_PIN_SD1_DATA0 = _MXC_BUILD_NON_GPIO_PIN(0x39C, 0x7A4),
+ MX51_PIN_SD1_DATA1 = _MXC_BUILD_NON_GPIO_PIN(0x3A0, 0x7A8),
+ MX51_PIN_SD1_DATA2 = _MXC_BUILD_NON_GPIO_PIN(0x3A4, 0x7AC),
+ MX51_PIN_SD1_DATA3 = _MXC_BUILD_NON_GPIO_PIN(0x3A8, 0x7B0),
+ MX51_PIN_GPIO1_0 = _MXC_BUILD_GPIO_PIN(0, 0, 1, 0x3AC, 0x7B4),
+ MX51_PIN_GPIO1_1 = _MXC_BUILD_GPIO_PIN(0, 1, 1, 0x3B0, 0x7B8),
+ MX51_PIN_SD2_CMD = _MXC_BUILD_NON_GPIO_PIN(0x3B4, 0x7BC),
+ MX51_PIN_SD2_CLK = _MXC_BUILD_NON_GPIO_PIN(0x3B8, 0x7C0),
+ MX51_PIN_SD2_DATA0 = _MXC_BUILD_NON_GPIO_PIN(0x3BC, 0x7C4),
+ MX51_PIN_SD2_DATA1 = _MXC_BUILD_NON_GPIO_PIN(0x3C0, 0x7C8),
+ MX51_PIN_SD2_DATA2 = _MXC_BUILD_NON_GPIO_PIN(0x3C4, 0x7CC),
+ MX51_PIN_SD2_DATA3 = _MXC_BUILD_NON_GPIO_PIN(0x3C8, 0x7D0),
+ MX51_PIN_GPIO1_2 = _MXC_BUILD_GPIO_PIN(0, 2, 0, 0x3CC, 0x7D4),
+ MX51_PIN_GPIO1_3 = _MXC_BUILD_GPIO_PIN(0, 3, 0, 0x3D0, 0x7D8),
+ MX51_PIN_PMIC_INT_REQ = _MXC_BUILD_NON_GPIO_PIN(0x3D4, 0x7FC),
+ MX51_PIN_GPIO1_4 = _MXC_BUILD_GPIO_PIN(0, 4, 0, 0x3D8, 0x804),
+ MX51_PIN_GPIO1_5 = _MXC_BUILD_GPIO_PIN(0, 5, 0, 0x3DC, 0x808),
+ MX51_PIN_GPIO1_6 = _MXC_BUILD_GPIO_PIN(0, 6, 0, 0x3E0, 0x80C),
+ MX51_PIN_GPIO1_7 = _MXC_BUILD_GPIO_PIN(0, 7, 0, 0x3E4, 0x810),
+ MX51_PIN_GPIO1_8 = _MXC_BUILD_GPIO_PIN(0, 8, 0, 0x3E8, 0x814),
+ MX51_PIN_GPIO1_9 = _MXC_BUILD_GPIO_PIN(0, 9, 0, 0x3EC, 0x818),
+};
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ASM_ARCH_MXC_MX51_PINS_H__ */
diff --git a/arch/arm/mach-mx51/pm.c b/arch/arm/mach-mx51/pm.c
new file mode 100644
index 000000000000..1a6a5b1d6025
--- /dev/null
+++ b/arch/arm/mach-mx51/pm.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/suspend.h>
+#include <linux/proc_fs.h>
+#include <linux/cpufreq.h>
+#include <mach/hardware.h>
+#include <asm/cacheflush.h>
+#include <asm/tlb.h>
+#include <asm/mach/map.h>
+#include "crm_regs.h"
+
+static struct cpu_wp *cpu_wp_tbl;
+static struct clk *cpu_clk;
+
+#if defined(CONFIG_CPU_FREQ)
+static int org_freq;
+extern int cpufreq_suspended;
+extern int set_cpu_freq(int wp);
+#endif
+
+
+static struct device *pm_dev;
+struct clk *gpc_dvfs_clk;
+extern void cpu_do_suspend_workaround(u32 sdclk_iomux_addr);
+extern void cpu_cortexa8_do_idle(u32);
+extern struct cpu_wp *(*get_cpu_wp)(int *wp);
+
+extern int iram_ready;
+void *suspend_iram_base;
+void (*suspend_in_iram)(u32 sdclk_iomux_addr) = NULL;
+
+static int mx51_suspend_enter(suspend_state_t state)
+{
+ u32 sdclk_iomux_addr = IO_ADDRESS(IOMUXC_BASE_ADDR + 0x4b8);
+
+ if (gpc_dvfs_clk == NULL)
+ gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs_clk");
+ /* gpc clock is needed for SRPG */
+ clk_enable(gpc_dvfs_clk);
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ mxc_cpu_lp_set(STOP_POWER_OFF);
+ break;
+ case PM_SUSPEND_STANDBY:
+ mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (tzic_enable_wake(0) != 0)
+ return -EAGAIN;
+
+ if (state == PM_SUSPEND_MEM) {
+ local_flush_tlb_all();
+ flush_cache_all();
+
+ /* Run the suspend code from iRAM. */
+ suspend_in_iram(sdclk_iomux_addr);
+
+ /*clear the EMPGC0/1 bits */
+ __raw_writel(0, MXC_SRPG_EMPGC0_SRPGCR);
+ __raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR);
+ } else {
+ if ((mxc_cpu_is_rev(CHIP_REV_2_0)) < 0) {
+ /* do cpu_idle_workaround */
+ u32 l2_iram_addr = IDLE_IRAM_BASE_ADDR;
+ if (!iram_ready)
+ return 0;
+ if (l2_iram_addr > 0x1FFE8000)
+ cpu_cortexa8_do_idle(IO_ADDRESS(l2_iram_addr));
+ } else {
+ cpu_do_idle();
+ }
+ }
+ clk_disable(gpc_dvfs_clk);
+
+ return 0;
+}
+
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+static int mx51_suspend_prepare(void)
+{
+#if defined(CONFIG_CPU_FREQ)
+ struct cpufreq_freqs freqs;
+ org_freq = clk_get_rate(cpu_clk);
+ freqs.old = org_freq / 1000;
+ freqs.new = cpu_wp_tbl[0].cpu_rate / 1000;
+ freqs.cpu = 0;
+ freqs.flags = 0;
+
+ cpufreq_suspended = 1;
+ if (clk_get_rate(cpu_clk) != cpu_wp_tbl[0].cpu_rate) {
+ set_cpu_freq(cpu_wp_tbl[0].cpu_rate);
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Called before devices are re-setup.
+ */
+static void mx51_suspend_finish(void)
+{
+#if defined(CONFIG_CPU_FREQ)
+ struct cpufreq_freqs freqs;
+
+ freqs.old = clk_get_rate(cpu_clk) / 1000;
+ freqs.new = org_freq / 1000;
+ freqs.cpu = 0;
+ freqs.flags = 0;
+
+ cpufreq_suspended = 0;
+
+ if (org_freq != clk_get_rate(cpu_clk)) {
+ set_cpu_freq(org_freq);
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+#endif
+}
+
+/*
+ * Called after devices are re-setup, but before processes are thawed.
+ */
+static void mx51_suspend_end(void)
+{
+}
+
+static int mx51_pm_valid(suspend_state_t state)
+{
+ return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
+}
+
+struct platform_suspend_ops mx51_suspend_ops = {
+ .valid = mx51_pm_valid,
+ .prepare = mx51_suspend_prepare,
+ .enter = mx51_suspend_enter,
+ .finish = mx51_suspend_finish,
+ .end = mx51_suspend_end,
+};
+
+
+static int __devinit mx51_pm_probe(struct platform_device *pdev)
+{
+ pm_dev = &pdev->dev;
+ return 0;
+}
+
+static struct platform_driver mx51_pm_driver = {
+ .driver = {
+ .name = "mx51_pm",
+ },
+ .probe = mx51_pm_probe,
+};
+
+static int __init pm_init(void)
+{
+ int cpu_wp_nr;
+
+ pr_info("Static Power Management for Freescale i.MX51\n");
+ if (platform_driver_register(&mx51_pm_driver) != 0) {
+ printk(KERN_ERR "mx51_pm_driver register failed\n");
+ return -ENODEV;
+ }
+ suspend_set_ops(&mx51_suspend_ops);
+ /* Move suspend routine into iRAM */
+ suspend_iram_base = IO_ADDRESS(SUSPEND_IRAM_BASE_ADDR);
+ memcpy(suspend_iram_base, cpu_do_suspend_workaround, SZ_4K);
+ /* Need to remap the area here since we want the memory region
+ to be executable. */
+ suspend_iram_base = __arm_ioremap(SUSPEND_IRAM_BASE_ADDR, SZ_4K,
+ MT_HIGH_VECTORS);
+ suspend_in_iram = (void *)suspend_iram_base;
+
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+
+ cpu_clk = clk_get(NULL, "cpu_clk");
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_DEBUG "%s: failed to get cpu_clk\n", __func__);
+ return PTR_ERR(cpu_clk);
+ }
+ printk(KERN_INFO "PM driver module loaded\n");
+
+ return 0;
+}
+
+
+static void __exit pm_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&mx51_pm_driver);
+}
+
+module_init(pm_init);
+module_exit(pm_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("PM driver");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/arm/mach-mx51/sdma_script_code.h b/arch/arm/mach-mx51/sdma_script_code.h
new file mode 100644
index 000000000000..9b8de26ab74a
--- /dev/null
+++ b/arch/arm/mach-mx51/sdma_script_code.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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 */
+
+/*!
+ * @file sdma_script_code.h
+ * @brief This file contains functions of SDMA scripts code initialization
+ *
+ * The file was generated automatically. Based on sdma scripts library.
+ *
+ * @ingroup SDMA
+ */
+/*******************************************************************************
+
+ SDMA RELEASE LABEL: "SS15_ELVIS"
+
+*******************************************************************************/
+
+#ifndef __SDMA_SCRIPT_CODE_H__
+#define __SDMA_SCRIPT_CODE_H__
+
+/*!
+* SDMA ROM scripts start addresses and sizes
+*/
+
+#define start_ADDR 0
+#define start_SIZE 24
+
+#define core_ADDR 80
+#define core_SIZE 232
+
+#define common_ADDR 312
+#define common_SIZE 330
+
+#define ap_2_ap_ADDR 642
+#define ap_2_ap_SIZE 41
+
+#define app_2_mcu_ADDR 683
+#define app_2_mcu_SIZE 64
+
+#define mcu_2_app_ADDR 747
+#define mcu_2_app_SIZE 70
+
+#define uart_2_mcu_ADDR 817
+#define uart_2_mcu_SIZE 75
+
+#define shp_2_mcu_ADDR 892
+#define shp_2_mcu_SIZE 69
+
+#define mcu_2_shp_ADDR 961
+#define mcu_2_shp_SIZE 72
+
+#define app_2_per_ADDR 1033
+#define app_2_per_SIZE 66
+
+#define per_2_app_ADDR 1099
+#define per_2_app_SIZE 74
+
+#define per_2_shp_ADDR 1173
+#define per_2_shp_SIZE 78
+
+#define shp_2_per_ADDR 1251
+#define shp_2_per_SIZE 72
+
+#define uartsh_2_mcu_ADDR 1323
+#define uartsh_2_mcu_SIZE 69
+
+#define mcu_2_ata_ADDR 1392
+#define mcu_2_ata_SIZE 81
+
+#define ata_2_mcu_ADDR 1473
+#define ata_2_mcu_SIZE 96
+
+#define loop_DMAs_routines_ADDR 1569
+#define loop_DMAs_routines_SIZE 227
+
+#define test_ADDR 1796
+#define test_SIZE 63
+
+#define signature_ADDR 1023
+#define signature_SIZE 1
+
+/*!
+* SDMA RAM scripts start addresses and sizes
+*/
+
+#define ext_mem__ipu_ram_ADDR 6144
+#define ext_mem__ipu_ram_SIZE 123
+
+#define mcu_2_spdif_ADDR 6267
+#define mcu_2_spdif_SIZE 59
+
+#define uart_2_per_ADDR 6326
+#define uart_2_per_SIZE 73
+
+#define uartsh_2_per_ADDR 6399
+#define uartsh_2_per_SIZE 67
+
+/*!
+* SDMA RAM image start address and size
+*/
+
+#define RAM_CODE_START_ADDR 6144
+#define RAM_CODE_SIZE 322
+
+/*!
+* Buffer that holds the SDMA RAM image
+*/
+__attribute__ ((__aligned__(4)))
+#ifndef CONFIG_XIP_KERNEL
+const
+#endif
+static const short sdma_code[] = {
+ 0x0e70, 0x0611, 0x5616, 0xc13c, 0x7d2a, 0x5ade, 0x008e, 0xc14e,
+ 0x7c26, 0x5be0, 0x5ef0, 0x5ce8, 0x0688, 0x08ff, 0x0011, 0x28ff,
+ 0x00bc, 0x53f6, 0x05df, 0x7d0b, 0x6dc5, 0x03df, 0x7d03, 0x6bd5,
+ 0xd84f, 0x982b, 0x6b05, 0xc681, 0x7e27, 0x7f29, 0x982b, 0x6d01,
+ 0x03df, 0x7d05, 0x6bd5, 0xc6ab, 0x7e18, 0x7f1a, 0x982b, 0x6b05,
+ 0xc621, 0x7e07, 0x7f06, 0x52de, 0x53e6, 0xc159, 0x7dd7, 0x0200,
+ 0x9803, 0x0007, 0x6004, 0x680c, 0x53f6, 0x028e, 0x00a3, 0xc256,
+ 0x048b, 0x0498, 0x0454, 0x068a, 0x982b, 0x0207, 0x680c, 0x6ddf,
+ 0x0107, 0x68ff, 0x60d0, 0x9834, 0x0207, 0x68ff, 0x6d28, 0x0107,
+ 0x6004, 0x680c, 0x9834, 0x0007, 0x68ff, 0x60d0, 0x9834, 0x0288,
+ 0x03a5, 0x3b03, 0x3d03, 0x4d00, 0x7d0a, 0x0804, 0x00a5, 0x00da,
+ 0x7d1a, 0x02a0, 0x7b01, 0x65d8, 0x7eee, 0x65ff, 0x7eec, 0x0804,
+ 0x02d0, 0x7d11, 0x4b00, 0x7c0f, 0x008a, 0x3003, 0x6dcf, 0x6bdf,
+ 0x0015, 0x0015, 0x7b02, 0x65d8, 0x0000, 0x7edd, 0x63ff, 0x7edb,
+ 0x3a03, 0x6dcd, 0x6bdd, 0x008a, 0x7b02, 0x65d8, 0x0000, 0x7ed3,
+ 0x65ff, 0x7ed1, 0x0006, 0xc1d9, 0xc1e3, 0x57db, 0x52f3, 0x6a01,
+ 0x008f, 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x5deb, 0x56fb, 0x0478,
+ 0x7d28, 0x0479, 0x7c16, 0x0015, 0x0015, 0x0388, 0x620a, 0x0808,
+ 0x7801, 0x0217, 0x5a06, 0x7f1d, 0x620a, 0x0808, 0x7801, 0x0217,
+ 0x5a26, 0x7f17, 0x2301, 0x4b00, 0x7cf1, 0x0b70, 0x0311, 0x5313,
+ 0x98aa, 0x0015, 0x0015, 0x0015, 0x7804, 0x620b, 0x5a06, 0x620b,
+ 0x5a26, 0x7c07, 0x0000, 0x55eb, 0x4d00, 0x7d06, 0xc1fa, 0x57db,
+ 0x9880, 0x0007, 0x680c, 0xc213, 0xc20a, 0x987d, 0xc1e3, 0x57db,
+ 0x52f3, 0x6ad5, 0x56fb, 0x028e, 0x1a94, 0x6ac3, 0x62c8, 0x0269,
+ 0x7d1e, 0x1e94, 0x6ee3, 0x62d0, 0x5aeb, 0x62c8, 0x0248, 0x6ed3,
+ 0x6ac8, 0x2694, 0x52eb, 0x6ad5, 0x6ee3, 0x62c8, 0x026e, 0x7d27,
+ 0x6ac8, 0x7f23, 0x2501, 0x4d00, 0x7d26, 0x028e, 0x1a98, 0x6ac3,
+ 0x62c8, 0x6ec3, 0x0260, 0x7df1, 0x62d0, 0xc27a, 0x98fb, 0x6ee3,
+ 0x008f, 0x2001, 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x62c8, 0x026e,
+ 0x7d0e, 0x6ac8, 0x7f0a, 0x2001, 0x7cf9, 0x6add, 0x7f06, 0x0000,
+ 0x4d00, 0x7d09, 0xc1fa, 0x57db, 0x98ba, 0x0007, 0x6aff, 0x62d0,
+ 0xc27a, 0x0458, 0x0454, 0x6add, 0x7ff8, 0xc20a, 0x98b7, 0xc1d9,
+ 0xc1e3, 0x57db, 0x52f3, 0x6ad5, 0x56fb, 0x028e, 0x1a94, 0x5202,
+ 0x0269, 0x7d17, 0x1e94, 0x5206, 0x0248, 0x5a06, 0x2694, 0x5206,
+ 0x026e, 0x7d26, 0x6ac8, 0x7f22, 0x2501, 0x4d00, 0x7d27, 0x028e,
+ 0x1a98, 0x5202, 0x0260, 0x7df3, 0x6add, 0x7f18, 0x62d0, 0xc27a,
+ 0x993e, 0x008f, 0x2001, 0x00d5, 0x7d01, 0x008d, 0x05a0, 0x5206,
+ 0x026e, 0x7d0e, 0x6ac8, 0x7f0a, 0x2001, 0x7cf9, 0x6add, 0x7f06,
+ 0x0000, 0x4d00, 0x7d0b, 0xc1fa, 0x57db, 0x9904, 0x0007, 0x6aff,
+ 0x6add, 0x7ffc, 0x62d0, 0xc27a, 0x0458, 0x0454, 0x6add, 0x7ff6,
+ 0xc20a, 0x9901
+};
+#endif
diff --git a/arch/arm/mach-mx51/sdram_autogating.c b/arch/arm/mach-mx51/sdram_autogating.c
new file mode 100644
index 000000000000..1d47e5aaf606
--- /dev/null
+++ b/arch/arm/mach-mx51/sdram_autogating.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file sdram_autogating.c
+ *
+ * @brief Enable auto clock gating of the EMI_FAST clock using M4IF.
+ *
+ * The APIs are for enabling and disabling automatic clock gating of EMI_FAST.
+ *
+ * @ingroup PM
+ */
+#include <asm/io.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/mxc_dvfs.h>
+#include "crm_regs.h"
+
+int sdram_autogating_is_active;
+static struct device *sdram_autogating_dev;
+#define M4IF_CNTL_REG0 0x8c
+#define M4IF_CNTL_REG1 0x90
+
+void enable_sdram_autogating(void);
+void disable_sdram_autogating(void);
+
+void enable_sdram_autogating()
+{
+ u32 reg;
+
+ /* Set the Fast arbitration Power saving timer */
+ reg = __raw_readl((IO_ADDRESS(M4IF_BASE_ADDR) + M4IF_CNTL_REG1));
+ reg &= ~0xFF;
+ reg |= 0x09;
+ __raw_writel(reg, (IO_ADDRESS(M4IF_BASE_ADDR) + M4IF_CNTL_REG1));
+ /*Allow for automatic gating of the EMI internal clock.
+ * If this is done, emi_intr CCGR bits should be set to 11.
+ */
+ reg = __raw_readl((IO_ADDRESS(M4IF_BASE_ADDR) + M4IF_CNTL_REG0));
+ reg &= ~0x5;
+ __raw_writel(reg, (IO_ADDRESS(M4IF_BASE_ADDR) + M4IF_CNTL_REG0));
+
+ sdram_autogating_is_active = 1;
+}
+
+void disable_sdram_autogating()
+{
+ u32 reg;
+
+ reg = __raw_readl((IO_ADDRESS(M4IF_BASE_ADDR) + M4IF_CNTL_REG0));
+ reg |= 0x4;
+ __raw_writel(reg, (IO_ADDRESS(M4IF_BASE_ADDR) + M4IF_CNTL_REG0));
+ sdram_autogating_is_active = 0;
+}
+
+static ssize_t sdram_autogating_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (sdram_autogating_is_active)
+ return sprintf(buf,
+ "M4IF autoclock gating for EMI_FAST enabled\n");
+ else
+ return sprintf(buf,
+ "M4IF autoclock gating for EMI_FAST disabled\n");
+}
+
+static ssize_t sdram_autogating_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (strstr(buf, "1") != NULL)
+ enable_sdram_autogating();
+ else if (strstr(buf, "0") != NULL) {
+ if (sdram_autogating_is_active)
+ disable_sdram_autogating();
+ }
+ return size;
+}
+
+static DEVICE_ATTR(enable, 0644, sdram_autogating_enable_show,
+ sdram_autogating_enable_store);
+
+/*!
+ * This is the probe routine for the auto clockgating of sdram driver.
+ *
+ * @param pdev The platform device structure
+ *
+ * @return The function returns 0 on success
+ *
+ */
+static int __devinit sdram_autogating_probe(struct platform_device *pdev)
+{
+ int err = 0;
+
+ sdram_autogating_dev = &pdev->dev;
+
+ err = sysfs_create_file(&sdram_autogating_dev->kobj,
+ &dev_attr_enable.attr);
+ if (err) {
+ printk(KERN_ERR
+ "Unable to register sysdev entry for sdram_autogating");
+ return err;
+ }
+
+ sdram_autogating_is_active = 0;
+ if (mxc_cpu_is_rev(CHIP_REV_3_0) >= 1)
+ enable_sdram_autogating();
+ return 0;
+}
+
+static struct platform_driver sdram_autogating_driver = {
+ .driver = {
+ .name = "sdram_autogating",
+ },
+ .probe = sdram_autogating_probe,
+};
+
+/*!
+ * Initialise the sdram_autogating_driver.
+ *
+ * @return The function always returns 0.
+ */
+
+static int __init sdram_autogating_init(void)
+{
+ if (platform_driver_register(&sdram_autogating_driver) != 0) {
+ printk(KERN_ERR "sdram_autogating_driver register failed\n");
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "sdram autogating driver module loaded\n");
+ return 0;
+}
+
+static void __exit sdram_autogating_cleanup(void)
+{
+ sysfs_remove_file(&sdram_autogating_dev->kobj, &dev_attr_enable.attr);
+
+ /* Unregister the device structure */
+ platform_driver_unregister(&sdram_autogating_driver);
+}
+
+module_init(sdram_autogating_init);
+module_exit(sdram_autogating_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("sdram_autogating driver");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/arm/mach-mx51/serial.c b/arch/arm/mach-mx51/serial.c
new file mode 100644
index 000000000000..7d1f94c26b85
--- /dev/null
+++ b/arch/arm/mach-mx51/serial.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @file mach-mx51/serial.c
+ *
+ * @brief This file contains the UART initiliazation.
+ *
+ * @ingroup MSL_MX51
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <mach/hardware.h>
+#include <mach/mxc_uart.h>
+#include <mach/spba.h>
+#include "serial.h"
+#include "board.h"
+
+#if defined(CONFIG_SERIAL_MXC) || defined(CONFIG_SERIAL_MXC_MODULE)
+
+/*!
+ * This is an array where each element holds information about a UART port,
+ * like base address of the UART, interrupt numbers etc. This structure is
+ * passed to the serial_core.c file. Based on which UART is used, the core file
+ * passes back the appropriate port structure as an argument to the control
+ * functions.
+ */
+static uart_mxc_port mxc_ports[] = {
+ [0] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART1_BASE_ADDR),
+ .mapbase = UART1_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART1_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 0,
+ },
+ .ints_muxed = UART1_MUX_INTS,
+ .irqs = {UART1_INT2, UART1_INT3},
+ .mode = UART1_MODE,
+ .ir_mode = UART1_IR,
+ .enabled = UART1_ENABLED,
+ .hardware_flow = UART1_HW_FLOW,
+ .cts_threshold = UART1_UCR4_CTSTL,
+ .dma_enabled = UART1_DMA_ENABLE,
+ .dma_rxbuf_size = UART1_DMA_RXBUFSIZE,
+ .rx_threshold = UART1_UFCR_RXTL,
+ .tx_threshold = UART1_UFCR_TXTL,
+ .shared = UART1_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART1_TX,
+ .dma_rx_id = MXC_DMA_UART1_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ },
+ [1] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART2_BASE_ADDR),
+ .mapbase = UART2_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART2_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 1,
+ },
+ .ints_muxed = UART2_MUX_INTS,
+ .irqs = {UART2_INT2, UART2_INT3},
+ .mode = UART2_MODE,
+ .ir_mode = UART2_IR,
+ .enabled = UART2_ENABLED,
+ .hardware_flow = UART2_HW_FLOW,
+ .cts_threshold = UART2_UCR4_CTSTL,
+ .dma_enabled = UART2_DMA_ENABLE,
+ .dma_rxbuf_size = UART2_DMA_RXBUFSIZE,
+ .rx_threshold = UART2_UFCR_RXTL,
+ .tx_threshold = UART2_UFCR_TXTL,
+ .shared = UART2_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART2_TX,
+ .dma_rx_id = MXC_DMA_UART2_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ },
+ [2] = {
+ .port = {
+ .membase = (void *)IO_ADDRESS(UART3_BASE_ADDR),
+ .mapbase = UART3_BASE_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = UART3_INT1,
+ .fifosize = 32,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 2,
+ },
+ .ints_muxed = UART3_MUX_INTS,
+ .irqs = {UART3_INT2, UART3_INT3},
+ .mode = UART3_MODE,
+ .ir_mode = UART3_IR,
+ .enabled = UART3_ENABLED,
+ .hardware_flow = UART3_HW_FLOW,
+ .cts_threshold = UART3_UCR4_CTSTL,
+ .dma_enabled = UART3_DMA_ENABLE,
+ .dma_rxbuf_size = UART3_DMA_RXBUFSIZE,
+ .rx_threshold = UART3_UFCR_RXTL,
+ .tx_threshold = UART3_UFCR_TXTL,
+ .shared = UART3_SHARED_PERI,
+ .dma_tx_id = MXC_DMA_UART3_TX,
+ .dma_rx_id = MXC_DMA_UART3_RX,
+ .rxd_mux = MXC_UART_RXDMUX,
+ },
+};
+
+#if defined CONFIG_UART1_ENABLED
+static struct platform_device mxc_uart_device1 = {
+ .name = "mxcintuart",
+ .id = 0,
+ .dev = {
+ .platform_data = &mxc_ports[0],
+ },
+};
+#endif
+
+#if defined CONFIG_UART2_ENABLED
+static struct platform_device mxc_uart_device2 = {
+ .name = "mxcintuart",
+ .id = 1,
+ .dev = {
+ .platform_data = &mxc_ports[1],
+ },
+};
+#endif
+
+#if defined CONFIG_UART3_ENABLED
+static struct platform_device mxc_uart_device3 = {
+ .name = "mxcintuart",
+ .id = 2,
+ .dev = {
+ .platform_data = &mxc_ports[2],
+ },
+};
+#endif
+
+static int __init mxc_init_uart(void)
+{
+ /* Register all the MXC UART platform device structures */
+#if defined CONFIG_UART1_ENABLED
+ platform_device_register(&mxc_uart_device1);
+#endif
+#if defined CONFIG_UART2_ENABLED
+ platform_device_register(&mxc_uart_device2);
+#endif
+
+ /* Grab ownership of shared UARTs 3 and 4, only when enabled */
+#if defined CONFIG_UART3_ENABLED
+#if UART3_DMA_ENABLE == 1
+ spba_take_ownership(UART3_SHARED_PERI, (SPBA_MASTER_A | SPBA_MASTER_C));
+#else
+ spba_take_ownership(UART3_SHARED_PERI, SPBA_MASTER_A);
+#endif /* UART3_DMA_ENABLE */
+ platform_device_register(&mxc_uart_device3);
+#endif /* UART3_ENABLED */
+ return 0;
+}
+
+#else
+static int __init mxc_init_uart(void)
+{
+ return 0;
+}
+#endif
+
+arch_initcall(mxc_init_uart);
diff --git a/arch/arm/mach-mx51/serial.h b/arch/arm/mach-mx51/serial.h
new file mode 100644
index 000000000000..ff4928c3b002
--- /dev/null
+++ b/arch/arm/mach-mx51/serial.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_MACH_MX51_SERIAL_H__
+#define __ARCH_ARM_MACH_MX51_SERIAL_H__
+
+#include <mach/mxc_uart.h>
+
+/* UART 1 configuration */
+/*!
+ * This option allows to choose either an interrupt-driven software controlled
+ * hardware flow control (set this option to 0) or hardware-driven hardware
+ * flow control (set this option to 1).
+ */
+/* UART used as wakeup source */
+#define UART1_HW_FLOW 0
+/*!
+ * This specifies the threshold at which the CTS pin is deasserted by the
+ * RXFIFO. Set this value in Decimal to anything from 0 to 32 for
+ * hardware-driven hardware flow control. Read the HW spec while specifying
+ * this value. When using interrupt-driven software controlled hardware
+ * flow control set this option to -1.
+ */
+#define UART1_UCR4_CTSTL 16
+/*!
+ * This is option to enable (set this option to 1) or disable DMA data transfer
+ */
+#define UART1_DMA_ENABLE 0
+/*!
+ * Specify the size of the DMA receive buffer. The minimum buffer size is 512
+ * bytes. The buffer size should be a multiple of 256.
+ */
+#define UART1_DMA_RXBUFSIZE 1024
+/*!
+ * Specify the MXC UART's Receive Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the RxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_RXTL 16
+/*!
+ * Specify the MXC UART's Transmit Trigger Level. This controls the threshold at
+ * which a maskable interrupt is generated by the TxFIFO. Set this value in
+ * Decimal to anything from 0 to 32. Read the HW spec while specifying this
+ * value.
+ */
+#define UART1_UFCR_TXTL 16
+/* UART 2 configuration */
+#define UART2_HW_FLOW 0
+#define UART2_UCR4_CTSTL -1
+#define UART2_DMA_ENABLE 0
+#define UART2_DMA_RXBUFSIZE 512
+#define UART2_UFCR_RXTL 16
+#define UART2_UFCR_TXTL 16
+/* UART 3 configuration */
+#define UART3_HW_FLOW 1
+#define UART3_UCR4_CTSTL 16
+#define UART3_DMA_ENABLE 1
+#define UART3_DMA_RXBUFSIZE 1024
+#define UART3_UFCR_RXTL 16
+#define UART3_UFCR_TXTL 16
+/*
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+/*
+ * Is the MUXED interrupt output sent to the ARM core
+ */
+#define INTS_NOTMUXED 0
+#define INTS_MUXED 1
+/* UART 1 configuration */
+/*!
+ * This define specifies whether the muxed ANDed interrupt line or the
+ * individual interrupts from the UART port is integrated with the ARM core.
+ * There exists a define like this for each UART port. Valid values that can
+ * be used are \b INTS_NOTMUXED or \b INTS_MUXED.
+ */
+#define UART1_MUX_INTS INTS_MUXED
+/*!
+ * This define specifies the transmitter interrupt number or the interrupt
+ * number of the ANDed interrupt in case the interrupts are muxed. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_INT1 MXC_INT_UART1
+/*!
+ * This define specifies the receiver interrupt number. If the interrupts of
+ * the UART are muxed, then we specify here a dummy value -1. There exists a
+ * define like this for each UART port.
+ */
+#define UART1_INT2 -1
+/*!
+ * This specifies the master interrupt number. If the interrupts of the UART
+ * are muxed, then we specify here a dummy value of -1. There exists a define
+ * like this for each UART port.
+ */
+#define UART1_INT3 -1
+/*!
+ * This specifies if the UART is a shared peripheral. It holds the shared
+ * peripheral number if it is shared or -1 if it is not shared. There exists
+ * a define like this for each UART port.
+ */
+#define UART1_SHARED_PERI -1
+/* UART 2 configuration */
+#define UART2_MUX_INTS INTS_MUXED
+#define UART2_INT1 MXC_INT_UART2
+#define UART2_INT2 -1
+#define UART2_INT3 -1
+#define UART2_SHARED_PERI -1
+/* UART 3 configuration */
+#define UART3_MUX_INTS INTS_MUXED
+#define UART3_INT1 MXC_INT_UART3
+#define UART3_INT2 -1
+#define UART3_INT3 -1
+#define UART3_SHARED_PERI SPBA_UART3
+
+#endif /* __ARCH_ARM_MACH_MX51_SERIAL_H__ */
diff --git a/arch/arm/mach-mx51/suspend.S b/arch/arm/mach-mx51/suspend.S
new file mode 100644
index 000000000000..a7f585aa9525
--- /dev/null
+++ b/arch/arm/mach-mx51/suspend.S
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+/*
+ * 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
+ */
+
+#include <linux/linkage.h>
+
+#define ARM_CTRL_DCACHE 1 << 2
+#define ARM_CTRL_ICACHE 1 << 12
+#define ARM_AUXCR_L2EN 1 << 1
+
+
+/*
+ * cpu_do_suspend_workaround()
+ *
+ * Suspend the processor (eg, wait for interrupt).
+ *
+ * IRQs are already disabled.
+ */
+ENTRY(cpu_do_suspend_workaround)
+ stmfd sp!, {r4,r5,r6,r7,r9,r10,r11} @ Save registers
+
+ mov r6, r0 @save iomux address
+ /* Disable L1 caches */
+ mrc p15, 0, r0, c1, c0, 0 @ R0 = system control reg
+ bic r0, r0, #ARM_CTRL_ICACHE @ Disable ICache
+ bic r0, r0, #ARM_CTRL_DCACHE @ Disable DCache
+ mcr p15, 0, r0, c1, c0, 0 @ Update system control reg
+
+ mrc p15, 1, r0, c0, c0, 1 @ Read CLIDR
+ ands r3, r0, #0x7000000 @ Isolate level of coherency
+ mov r3, r3, lsr #23 @ Cache level value (naturally aligned)
+ beq FinishedClean
+ mov r10, #0
+Loop1Clean:
+ add r2, r10, r10, lsr #1 @ Work out cache level
+ mov r1, r0, lsr r2 @ R0 bottom 3 bits = Cache Type for this level
+ and r1, r1, #7 @ Get those 3 bits alone
+ cmp r1, #2
+ blt SkipClean @ No cache or only instruction cache at this level
+ mcr p15, 2, r10, c0, c0, 0 @ Write the Cache Size selection register
+ mov r1, #0
+ .long 0xF57FF06F @ ISB
+ mrc p15, 1, r1, c0, c0, 0 @ Reads current Cache Size ID register
+ and r2, r1, #7 @ Extract the line length field
+ add r2, r2, #4 @ Add 4 for the line length offset (log2 16 bytes)
+ ldr r4, =0x3FF
+ ands r4, r4, r1, lsr #3 @ R4 is the max number on the way size (right aligned)
+ clz r5, r4 @ R5 is the bit position of the way size increment
+ ldr r7, =0x00007FFF
+ ands r7, r7, r1, lsr #13 @ R7 is the max number of the index size (right aligned)
+Loop2Clean:
+ mov r9, r4 @ R9 working copy of the max way size (right aligned)
+Loop3Clean:
+ orr r11, r10, r9, lsl r5 @ Factor in the way number and cache number into R11
+ orr r11, r11, r7, lsl r2 @ Factor in the index number
+ mcr p15, 0, r11, c7, c14, 2 @ Clean and invalidate by set/way
+ subs r9, r9, #1 @ Decrement the way number
+ bge Loop3Clean
+ subs r7, r7, #1 @ Decrement the index
+ bge Loop2Clean
+SkipClean:
+ add r10, r10, #2 @ Increment the cache number
+ cmp r3, r10
+ bgt Loop1Clean
+
+FinishedClean:
+
+ /* Disable L2 cache */
+ mrc p15, 0, r0, c1, c0, 1 @ R0 = auxiliary control reg
+ bic r0, r0, #ARM_AUXCR_L2EN @ Disable L2 cache
+ mcr p15, 0, r0, c1, c0, 1 @ Update aux control reg
+
+ /*Set the DDR drive strength to low */
+ ldr r10, [r6]
+ and r10, r10, #0xF1 @ clear bits 2-1
+ str r10, [r6]
+
+ .long 0xe320f003 @ Opcode for WFI
+
+ /*Set the DDR drive strength to max */
+ orr r10, r10, #0x06 @ set bits 2-1
+ str r10, [r6]
+
+ mov r0, #0
+ mcr p15, 0, r0, c7, c5, 0 @ Invalidate inst cache
+
+ /* Invalidate data caches */
+ mrc p15, 1, r0, c0, c0, 1 @ Read CLIDR
+ ands r3, r0, #0x7000000 @ Isolate level of coherency
+ mov r3, r3, lsr #23 @ Cache level value (naturally aligned)
+ beq FinishedInvalidate
+ mov r10, #0
+Loop1Invalidate:
+ add r2, r10, r10, lsr #1 @ Work out cache level
+ mov r1, r0, lsr r2 @ R0 bottom 3 bits = Cache Type for this level
+ and r1, r1, #7 @ Get those 3 bits alone
+ cmp r1, #2
+ blt SkipInvalidate @ No cache or only instruction cache at this level
+ mcr p15, 2, r10, c0, c0, 0 @ Write the Cache Size selection register
+ mov r1, #0
+ .long 0xF57FF06F @ ISB
+ mrc p15, 1, r1, c0, c0, 0 @ Reads current Cache Size ID register
+ and r2, r1, #7 @ Extract the line length field
+ add r2, r2, #4 @ Add 4 for the line length offset (log2 16 bytes)
+ ldr r4, =0x3FF
+ ands r4, r4, r1, lsr #3 @ R4 is the max number on the way size (right aligned)
+ clz r5, r4 @ R5 is the bit position of the way size increment
+ ldr r7, =0x00007FFF
+ ands r7, r7, r1, lsr #13 @ R7 is the max number of the index size (right aligned)
+Loop2Invalidate:
+ mov r9, r4 @ R9 working copy of the max way size (right aligned)
+Loop3Invalidate:
+ orr r11, r10, r9, lsl r5 @ Factor in the way number and cache number into R11
+ orr r11, r11, r7, lsl r2 @ Factor in the index number
+ mcr p15, 0, r11, c7, c6, 2 @ Invalidate by set/way
+ subs r9, r9, #1 @ Decrement the way number
+ bge Loop3Invalidate
+ subs r7, r7, #1 @ Decrement the index
+ bge Loop2Invalidate
+SkipInvalidate:
+ add r10, r10, #2 @ Increment the cache number
+ cmp r3, r10
+ bgt Loop1Invalidate
+
+FinishedInvalidate:
+
+ /* Enable L2 cache */
+ mrc p15, 0, r0, c1, c0, 1 @ R0 = auxiliary control reg
+ orr r0, r0, #ARM_AUXCR_L2EN @ Enable L2 cache
+ mcr p15, 0, r0, c1, c0, 1 @ Update aux control reg
+
+ /* Enable L1 caches */
+ mrc p15, 0, r0, c1, c0, 0 @ R0 = system control reg
+ orr r0, r0, #ARM_CTRL_ICACHE @ Enable ICache
+ orr r0, r0, #ARM_CTRL_DCACHE @ Enable DCache
+ mcr p15, 0, r0, c1, c0, 0 @ Update system control reg
+
+ /* Restore registers */
+ ldmfd sp!, {r4,r5,r6,r7,r9,r10,r11}
+ mov pc, lr
+
+ .type cpu_do_suspend, #object
+ENTRY(cpu_do_suspend)
+ .word cpu_do_suspend_workaround
+ .size cpu_do_suspend_workaround, . - cpu_do_suspend_workaround
+
+
diff --git a/arch/arm/mach-mx51/system.c b/arch/arm/mach-mx51/system.c
new file mode 100644
index 000000000000..749282d5a633
--- /dev/null
+++ b/arch/arm/mach-mx51/system.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <asm/proc-fns.h>
+#include <asm/system.h>
+#include "crm_regs.h"
+
+/*!
+ * @defgroup MSL_MX51 i.MX51 Machine Specific Layer (MSL)
+ */
+
+/*!
+ * @file mach-mx51/system.c
+ * @brief This file contains idle and reset functions.
+ *
+ * @ingroup MSL_MX51
+ */
+
+extern int mxc_jtag_enabled;
+extern int iram_ready;
+static struct clk *gpc_dvfs_clk;
+
+extern void cpu_cortexa8_do_idle(u32 addr);
+
+
+/* set cpu low power mode before WFI instruction */
+void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
+{
+ u32 plat_lpc, arm_srpgcr, ccm_clpcr;
+ u32 empgc0, empgc1;
+ int stop_mode = 0;
+
+ /* always allow platform to issue a deep sleep mode request */
+ plat_lpc = __raw_readl(MXC_CORTEXA8_PLAT_LPC) &
+ ~(MXC_CORTEXA8_PLAT_LPC_DSM);
+ ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK);
+ arm_srpgcr = __raw_readl(MXC_SRPG_ARM_SRPGCR) & ~(MXC_SRPGCR_PCR);
+ empgc0 = __raw_readl(MXC_SRPG_EMPGC0_SRPGCR) & ~(MXC_SRPGCR_PCR);
+ empgc1 = __raw_readl(MXC_SRPG_EMPGC1_SRPGCR) & ~(MXC_SRPGCR_PCR);
+
+ switch (mode) {
+ case WAIT_CLOCKED:
+ break;
+ case WAIT_UNCLOCKED:
+ ccm_clpcr |= (0x1 << MXC_CCM_CLPCR_LPM_OFFSET);
+ break;
+ case WAIT_UNCLOCKED_POWER_OFF:
+ case STOP_POWER_OFF:
+ plat_lpc |= MXC_CORTEXA8_PLAT_LPC_DSM
+ | MXC_CORTEXA8_PLAT_LPC_DBG_DSM;
+ if (mode == WAIT_UNCLOCKED_POWER_OFF) {
+ ccm_clpcr |= (0x1 << MXC_CCM_CLPCR_LPM_OFFSET);
+ ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY;
+ stop_mode = 0;
+ } else {
+ ccm_clpcr |= (0x2 << MXC_CCM_CLPCR_LPM_OFFSET);
+ ccm_clpcr |= (0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET);
+ ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
+ ccm_clpcr |= MXC_CCM_CLPCR_SBYOS;
+ stop_mode = 1;
+ }
+
+ arm_srpgcr |= MXC_SRPGCR_PCR;
+ if (stop_mode) {
+ empgc0 |= MXC_SRPGCR_PCR;
+ empgc1 |= MXC_SRPGCR_PCR;
+ }
+
+ if (tzic_enable_wake(1) != 0)
+ return;
+ break;
+ case STOP_POWER_ON:
+ ccm_clpcr |= (0x2 << MXC_CCM_CLPCR_LPM_OFFSET);
+ break;
+ default:
+ printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode);
+ return;
+ }
+
+ __raw_writel(plat_lpc, MXC_CORTEXA8_PLAT_LPC);
+ __raw_writel(ccm_clpcr, MXC_CCM_CLPCR);
+ __raw_writel(arm_srpgcr, MXC_SRPG_ARM_SRPGCR);
+ __raw_writel(arm_srpgcr, MXC_SRPG_NEON_SRPGCR);
+ if (stop_mode) {
+ __raw_writel(empgc0, MXC_SRPG_EMPGC0_SRPGCR);
+ __raw_writel(empgc1, MXC_SRPG_EMPGC1_SRPGCR);
+ }
+}
+
+void mxc_pg_enable(struct platform_device *pdev)
+{
+ if (pdev == NULL)
+ return;
+
+ if (strcmp(pdev->name, "mxc_ipu") == 0) {
+ __raw_writel(MXC_PGCR_PCR, MXC_PGC_IPU_PGCR);
+ __raw_writel(MXC_PGSR_PSR, MXC_PGC_IPU_PGSR);
+ } else if (strcmp(pdev->name, "mxc_vpu") == 0) {
+ __raw_writel(MXC_PGCR_PCR, MXC_PGC_VPU_PGCR);
+ __raw_writel(MXC_PGSR_PSR, MXC_PGC_VPU_PGSR);
+ }
+}
+
+EXPORT_SYMBOL(mxc_pg_enable);
+
+void mxc_pg_disable(struct platform_device *pdev)
+{
+ if (pdev == NULL)
+ return;
+
+ if (strcmp(pdev->name, "mxc_ipu") == 0) {
+ __raw_writel(0x0, MXC_PGC_IPU_PGCR);
+ if (__raw_readl(MXC_PGC_IPU_PGSR) & MXC_PGSR_PSR)
+ dev_dbg(&pdev->dev, "power gating successful\n");
+ __raw_writel(MXC_PGSR_PSR, MXC_PGC_IPU_PGSR);
+ } else if (strcmp(pdev->name, "mxc_vpu") == 0) {
+ __raw_writel(0x0, MXC_PGC_VPU_PGCR);
+ if (__raw_readl(MXC_PGC_VPU_PGSR) & MXC_PGSR_PSR)
+ dev_dbg(&pdev->dev, "power gating successful\n");
+ __raw_writel(MXC_PGSR_PSR, MXC_PGC_VPU_PGSR);
+ }
+}
+
+EXPORT_SYMBOL(mxc_pg_disable);
+
+/* To change the idle power mode, need to set arch_idle_mode to a different
+ * power mode as in enum mxc_cpu_pwr_mode.
+ * May allow dynamically changing the idle mode.
+ */
+static int arch_idle_mode = WAIT_UNCLOCKED_POWER_OFF;
+/*!
+ * This function puts the CPU into idle mode. It is called by default_idle()
+ * in process.c file.
+ */
+void arch_idle(void)
+{
+ if (likely(!mxc_jtag_enabled)) {
+ if (gpc_dvfs_clk == NULL)
+ gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs_clk");
+ /* gpc clock is needed for SRPG */
+ clk_enable(gpc_dvfs_clk);
+ mxc_cpu_lp_set(arch_idle_mode);
+ if ((mxc_cpu_is_rev(CHIP_REV_2_0)) < 0) {
+ u32 l2_iram_addr = IDLE_IRAM_BASE_ADDR;
+
+ if (!iram_ready)
+ return;
+
+ if (l2_iram_addr > 0x1FFE8000)
+ cpu_cortexa8_do_idle(IO_ADDRESS(l2_iram_addr));
+ } else {
+ cpu_do_idle();
+ }
+ clk_disable(gpc_dvfs_clk);
+ }
+}
+
+/*
+ * This function resets the system. It is called by machine_restart().
+ *
+ * @param mode indicates different kinds of resets
+ */
+void arch_reset(char mode)
+{
+ /* Workaround to reset NFC_CONFIG3 register
+ * due to the chip warm reset does not reset it
+ */
+ __raw_writel(0x20600, IO_ADDRESS(NFC_BASE_ADDR) + 0x28);
+
+ /* Assert SRS signal */
+ mxc_wd_reset();
+}
diff --git a/arch/arm/mach-mx51/usb.h b/arch/arm/mach-mx51/usb.h
new file mode 100644
index 000000000000..7cc14d9119d1
--- /dev/null
+++ b/arch/arm/mach-mx51/usb.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+extern int usbotg_init(struct platform_device *pdev);
+extern void usbotg_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbotg_hs_active(void);
+extern void gpio_usbotg_hs_inactive(void);
+extern struct platform_device *host_pdev_register(struct resource *res,
+ int n_res, struct fsl_usb2_platform_data *config);
+
+extern int fsl_usb_host_init(struct platform_device *pdev);
+extern void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbotg_utmi_active(void);
+extern void gpio_usbotg_utmi_inactive(void);
+
+/*
+ * Determine which platform_data struct to use for the DR controller,
+ * based on which transceiver is configured.
+ * PDATA is a pointer to it.
+ */
+#if defined(CONFIG_ISP1301_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_1301_config;
+#define PDATA (&dr_1301_config)
+#elif defined(CONFIG_MC13783_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_13783_config;
+#define PDATA (&dr_13783_config)
+#elif defined(CONFIG_UTMI_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config;
+#define PDATA (&dr_utmi_config)
+#endif
+
+
+/*
+ * Used to set pdata->operating_mode before registering the platform_device.
+ * If OTG is configured, the controller operates in OTG mode,
+ * otherwise it's either host or device.
+ */
+#ifdef CONFIG_USB_OTG
+#define DR_UDC_MODE FSL_USB2_DR_OTG
+#define DR_HOST_MODE FSL_USB2_DR_OTG
+#else
+#define DR_UDC_MODE FSL_USB2_DR_DEVICE
+#define DR_HOST_MODE FSL_USB2_DR_HOST
+#endif
+
+
+#ifdef CONFIG_USB_EHCI_ARC_OTG
+static inline void dr_register_host(struct resource *r, int rs)
+{
+ PDATA->operating_mode = DR_HOST_MODE;
+ host_pdev_register(r, rs, PDATA);
+}
+#else
+static inline void dr_register_host(struct resource *r, int rs)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_GADGET_ARC
+static struct platform_device dr_udc_device;
+
+static inline void dr_register_udc(void)
+{
+ PDATA->operating_mode = DR_UDC_MODE;
+ dr_udc_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_udc_device))
+ printk(KERN_ERR "usb: can't register DR gadget\n");
+ else
+ printk(KERN_INFO "usb: DR gadget (%s) registered\n",
+ PDATA->transceiver);
+}
+#else
+static inline void dr_register_udc(void)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_OTG
+static struct platform_device dr_otg_device;
+
+/*
+ * set the proper operating_mode and
+ * platform_data pointer, then register the
+ * device.
+ */
+static inline void dr_register_otg(void)
+{
+ PDATA->operating_mode = FSL_USB2_DR_OTG;
+ dr_otg_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_otg_device))
+ printk(KERN_ERR "usb: can't register otg device\n");
+ else
+ printk(KERN_INFO "usb: DR OTG registered\n");
+}
+#else
+static inline void dr_register_otg(void)
+{
+}
+#endif
diff --git a/arch/arm/mach-mx51/usb_dr.c b/arch/arm/mach-mx51/usb_dr.c
new file mode 100644
index 000000000000..b3eb7d645e34
--- /dev/null
+++ b/arch/arm/mach-mx51/usb_dr.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+
+static int usbotg_init_ext(struct platform_device *pdev);
+static void usbotg_uninit_ext(struct fsl_usb2_platform_data *pdata);
+static void _wake_up_enable(struct fsl_usb2_platform_data *pdata, bool enable);
+
+/*
+ * platform data structs
+ * - Which one to use is determined by CONFIG options in usb.h
+ * - operating_mode plugged at run time
+ */
+static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config = {
+ .name = "DR",
+ .platform_init = usbotg_init_ext,
+ .platform_uninit = usbotg_uninit_ext,
+ .phy_mode = FSL_USB2_PHY_UTMI_WIDE,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbotg_hs_active,
+ .gpio_usb_inactive = gpio_usbotg_hs_inactive,
+ .wake_up_enable = _wake_up_enable,
+ .transceiver = "utmi",
+};
+
+
+/*
+ * resources
+ */
+static struct resource otg_resources[] = {
+ [0] = {
+ .start = (u32)(USB_OTGREGS_BASE),
+ .end = (u32)(USB_OTGREGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB_OTG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+static u64 dr_udc_dmamask = ~(u32) 0;
+static void dr_udc_release(struct device *dev)
+{
+}
+
+static u64 dr_otg_dmamask = ~(u32) 0;
+static void dr_otg_release(struct device *dev)
+{
+}
+
+/*
+ * platform device structs
+ * dev.platform_data field plugged at run time
+ */
+static struct platform_device dr_udc_device = {
+ .name = "fsl-usb2-udc",
+ .id = -1,
+ .dev = {
+ .release = dr_udc_release,
+ .dma_mask = &dr_udc_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static struct platform_device __maybe_unused dr_otg_device = {
+ .name = "fsl-usb2-otg",
+ .id = -1,
+ .dev = {
+ .release = dr_otg_release,
+ .dma_mask = &dr_otg_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+/* Notes: configure USB clock*/
+static int usbotg_init_ext(struct platform_device *pdev)
+{
+ struct clk *usb_clk;
+
+ usb_clk = clk_get(NULL, "usboh3_clk");
+ clk_enable(usb_clk);
+ clk_put(usb_clk);
+
+ usb_clk = clk_get(NULL, "usb_phy_clk");
+ clk_enable(usb_clk);
+ clk_put(usb_clk);
+
+ /*derive clock from oscillator */
+ usb_clk = clk_get(NULL, "usb_utmi_clk");
+ clk_disable(usb_clk);
+ clk_put(usb_clk);
+
+ return usbotg_init(pdev);
+}
+
+static void usbotg_uninit_ext(struct fsl_usb2_platform_data *pdata)
+{
+ struct clk *usb_clk;
+
+ usb_clk = clk_get(NULL, "usboh3_clk");
+ clk_disable(usb_clk);
+ clk_put(usb_clk);
+
+ usb_clk = clk_get(NULL, "usb_phy_clk");
+ clk_disable(usb_clk);
+ clk_put(usb_clk);
+
+ usbotg_uninit(pdata);
+}
+
+static void _wake_up_enable(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ if (get_usb_mode(pdata) == FSL_USB_DR_DEVICE) {
+ if (enable) {
+ USBCTRL |= UCTRL_OWIE;
+ USBCTRL_HOST2 |= UCTRL_H2OVBWK_EN;
+ USB_PHY_CTR_FUNC |= USB_UTMI_PHYCTRL_CONF2;
+ } else {
+ USBCTRL &= ~UCTRL_OWIE;
+ USBCTRL_HOST2 &= ~UCTRL_H2OVBWK_EN;
+ USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_CONF2;
+ }
+ }
+}
+
+static int __init usb_dr_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ dr_register_otg();
+ dr_register_host(otg_resources, ARRAY_SIZE(otg_resources));
+ dr_register_udc();
+
+ return 0;
+}
+
+module_init(usb_dr_init);
diff --git a/arch/arm/mach-mx51/usb_h1.c b/arch/arm/mach-mx51/usb_h1.c
new file mode 100644
index 000000000000..869ec45f0726
--- /dev/null
+++ b/arch/arm/mach-mx51/usb_h1.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include "usb.h"
+#include "iomux.h"
+
+
+/*
+ * USB Host1 HS port
+ */
+static int gpio_usbh1_active(void)
+{
+ /* Set USBH1_STP to GPIO and toggle it */
+ mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_GPIO |
+ IOMUX_CONFIG_SION);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_USBH1_STP), "usbh1_stp");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_USBH1_STP), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_USBH1_STP), 1);
+
+ /* Signal only used on MX51-3DS for reset to PHY.*/
+ if (machine_is_mx51_3ds()) {
+ mxc_request_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX51_PIN_EIM_D17, PAD_CTL_DRV_HIGH |
+ PAD_CTL_HYS_NONE | PAD_CTL_PUE_KEEPER |
+ PAD_CTL_100K_PU | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_D17), "eim_d17");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D17), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D17), 1);
+ }
+
+ msleep(100);
+
+ return 0;
+}
+
+void gpio_usbh1_setback_stp(void)
+{
+ /* setback USBH1_STP to be function */
+ mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX51_PIN_USBH1_STP, PAD_CTL_SRE_FAST |
+ PAD_CTL_DRV_HIGH | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS |
+ PAD_CTL_DRV_VOT_LOW);
+ gpio_free(IOMUX_TO_GPIO(MX51_PIN_USBH1_STP));
+}
+EXPORT_SYMBOL(gpio_usbh1_setback_stp);
+
+static void gpio_usbh1_inactive(void)
+{
+ /* Signal only used on MX51-3DS for reset to PHY.*/
+ if (machine_is_mx51_3ds()) {
+ gpio_free(IOMUX_TO_GPIO(MX51_PIN_EIM_D17));
+ mxc_free_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_GPIO);
+ }
+
+ mxc_free_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_GPIO);
+ gpio_free(IOMUX_TO_GPIO(MX51_PIN_USBH1_STP));
+}
+
+static struct fsl_usb2_platform_data usbh1_config = {
+ .name = "Host 1",
+ .platform_init = fsl_usb_host_init,
+ .platform_uninit = fsl_usb_host_uninit,
+ .operating_mode = FSL_USB2_MPH_HOST,
+ .phy_mode = FSL_USB2_PHY_ULPI,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbh1_active,
+ .gpio_usb_inactive = gpio_usbh1_inactive,
+ .transceiver = "isp1504",
+};
+
+static struct resource usbh1_resources[] = {
+ [0] = {
+ .start = (u32) (USB_H1REGS_BASE),
+ .end = (u32) (USB_H1REGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB_H1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int __init usbh1_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ host_pdev_register(usbh1_resources,
+ ARRAY_SIZE(usbh1_resources), &usbh1_config);
+
+ return 0;
+}
+
+module_init(usbh1_init);
diff --git a/arch/arm/mach-mx51/usb_h2.c b/arch/arm/mach-mx51/usb_h2.c
new file mode 100644
index 000000000000..90de5a28733f
--- /dev/null
+++ b/arch/arm/mach-mx51/usb_h2.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <asm/mach-types.h>
+#include <mach/arc_otg.h>
+#include "usb.h"
+#include "iomux.h"
+
+/*
+ * USB Host2 HS port
+ */
+static int gpio_usbh2_active(void)
+{
+ /* Set USBH2_STP to GPIO and toggle it */
+ mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_GPIO);
+ gpio_request(IOMUX_TO_GPIO(MX51_PIN_EIM_A26), "eim_a26");
+ gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A26), 0);
+ gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A26), 1);
+
+ msleep(100);
+
+ return 0;
+}
+
+void gpio_usbh2_setback_stp(void)
+{
+ /* setback USBH2_STP to be function */
+ mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT2);
+}
+EXPORT_SYMBOL(gpio_usbh2_setback_stp);
+
+static void gpio_usbh2_inactive(void)
+{
+ gpio_free(IOMUX_TO_GPIO(MX51_PIN_EIM_A26));
+ mxc_free_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_GPIO);
+}
+
+static struct fsl_usb2_platform_data usbh2_config = {
+ .name = "Host 2",
+ .platform_init = fsl_usb_host_init,
+ .platform_uninit = fsl_usb_host_uninit,
+ .operating_mode = FSL_USB2_MPH_HOST,
+ .phy_mode = FSL_USB2_PHY_ULPI,
+ .power_budget = 500, /* 500 mA max power */
+ .gpio_usb_active = gpio_usbh2_active,
+ .gpio_usb_inactive = gpio_usbh2_inactive,
+ .transceiver = "isp1504",
+};
+
+static struct resource usbh2_resources[] = {
+ [0] = {
+ .start = (u32) (USB_H2REGS_BASE),
+ .end = (u32) (USB_H2REGS_BASE + 0x1ff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = MXC_INT_USB_H2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int __init usbh2_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ if (machine_is_mx51_3ds() ||
+ (machine_is_mx51_babbage() && (cpu_is_mx51_rev(CHIP_REV_2_0) >= 1)))
+ return 0;
+
+ host_pdev_register(usbh2_resources, ARRAY_SIZE(usbh2_resources),
+ &usbh2_config);
+ return 0;
+}
+
+module_init(usbh2_init);
+
diff --git a/arch/arm/mach-mx51/wfi.S b/arch/arm/mach-mx51/wfi.S
new file mode 100644
index 000000000000..559669611b6a
--- /dev/null
+++ b/arch/arm/mach-mx51/wfi.S
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+/*
+ * 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
+ */
+
+#include <linux/linkage.h>
+
+#define ARM_CTRL_DCACHE 1 << 2
+#define ARM_AUXCR_L2EN 1 << 1
+/*
+ * cpu_cortexa8_do_idle()
+ *
+ * Idle the processor (eg, wait for interrupt).
+ *
+ * IRQs are already disabled.
+ */
+ENTRY(cpu_cortexa8_do_idle)
+
+ mrc p15, 0, r1, c1, c0, 1 @ R1 = auxiliary control reg
+ ands r2, r1, #ARM_AUXCR_L2EN @ Check if L2 is disabled
+ beq SkipL2Access
+
+ mrc p15, 0, r2, c1, c0, 0 @ R2 = system control reg
+ bic r2, r2, #ARM_CTRL_DCACHE @ Disable DCache
+ mcr p15, 0, r2, c1, c0, 0 @ Update system control reg
+
+ bic r1, r1, #ARM_AUXCR_L2EN @ Disable L2 cache
+ mcr p15, 0, r1, c1, c0, 1 @ Update aux control reg
+
+ ldr r1, =(0x0 << 6) @ A[6] = 0
+ mcr p15, 0, r1, c15, c9, 2 @ Read L2 tag RAM into L2 data 0 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x00] @ Save tag info
+
+ ldr r1, =(0x1 << 6) @ A[6] = 1
+ mcr p15, 0, r1, c15, c9, 2 @ Read L2 tag RAM into L2 data 0 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x04] @ Save tag info
+
+ ldr r1, =(0x0 << 3) @ A[6:3] = b0000
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x08] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x0C] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x10] @ Store data info
+
+ ldr r1, =(0x1 << 3) @ A[6:3] = b0001
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x14] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x18] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x1C] @ Store data info
+
+ ldr r1, =(0x2 << 3) @ A[6:3] = b0010
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x20] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x24] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x28] @ Store data info
+
+ ldr r1, =(0x3 << 3) @ A[6:3] = b0011
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x2C] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x30] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x34] @ Store data info
+
+ ldr r1, =(0x4 << 3) @ A[6:3] = b0100
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x38] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x3C] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x40] @ Store data info
+
+ ldr r1, =(0x5 << 3) @ A[6:3] = b0101
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x44] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x48] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x4C] @ Store data info
+
+ ldr r1, =(0x6 << 3) @ A[6:3] = b0110
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x50] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x54] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x58] @ Store data info
+
+ ldr r1, =(0x7 << 3) @ A[6:3] = b0111
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x5C] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x60] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x64] @ Store data info
+
+ ldr r1, =(0x8 << 3) @ A[6:3] = b1000
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x68] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x6C] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x70] @ Store data info
+
+ ldr r1, =(0x9 << 3) @ A[6:3] = b1001
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x74] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x78] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x7C] @ Store data info
+
+ ldr r1, =(0xA << 3) @ A[6:3] = b1010
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x80] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x84] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x88] @ Store data info
+
+ ldr r1, =(0xB << 3) @ A[6:3] = b1011
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x8C] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x90] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0x94] @ Store data info
+
+ ldr r1, =(0xC << 3) @ A[6:3] = b1100
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0x98] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0x9C] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0xA0] @ Store data info
+
+ ldr r1, =(0xD << 3) @ A[6:3] = b1101
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0xA4] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0xA8] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0xAC] @ Store data info
+
+ ldr r1, =(0xE << 3) @ A[6:3] = b1110
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0xB0] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0xB4] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0xB8] @ Store data info
+
+ ldr r1, =(0xF << 3) @ A[6:3] = b1111
+ mcr p15, 0, r1, c15, c9, 3 @ Read L2 Data RAM into L2 data 0-2 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0xBC] @ Store data info
+ mrc p15, 0, r2, c15, c8, 1 @ Move L2 data 1 register to R2
+ str r2, [r0, #0xC0] @ Store data info
+ mrc p15, 0, r2, c15, c8, 5 @ Move L2 data 2 register to R2
+ str r2, [r0, #0xC4] @ Store data info
+
+ ldr r1, =(0x2 << 29) | (0x0 << 6) @ WAY = A[31:29] = 2, A[6] = 0
+ mcr p15, 0, r1, c15, c9, 2 @ Read L2 tag RAM into L2 data 0 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0xC8] @ Save tag info
+
+ ldr r1, =(0x2 << 29) | (0x1 << 6) @ WAY = A[31:29] = 2, A[6] = 1
+ mcr p15, 0, r1, c15, c9, 2 @ Read L2 tag RAM into L2 data 0 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0xCC] @ Save tag info
+
+ ldr r1, =(0x4 << 29) | (0x0 << 6) @ WAY = A[31:29] = 4, A[6] = 0
+ mcr p15, 0, r1, c15, c9, 2 @ Read L2 tag RAM into L2 data 0 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0xD0] @ Save tag info
+
+ ldr r1, =(0x4 << 29) | (0x1 << 6) @ WAY = A[31:29] = 4, A[6] = 1
+ mcr p15, 0, r1, c15, c9, 2 @ Read L2 tag RAM into L2 data 0 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0xD4] @ Save tag info
+
+ ldr r1, =(0x6 << 29) | (0x0 << 6) @ WAY = A[31:29] = 6, A[6] = 0
+ mcr p15, 0, r1, c15, c9, 2 @ Read L2 tag RAM into L2 data 0 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0xD8] @ Save tag info
+
+ ldr r1, =(0x6 << 29) | (0x1 << 6) @ WAY = A[31:29] = 6, A[6] = 1
+ mcr p15, 0, r1, c15, c9, 2 @ Read L2 tag RAM into L2 data 0 register
+ mrc p15, 0, r2, c15, c8, 0 @ Move L2 data 0 register to R2
+ str r2, [r0, #0xDC] @ Save tag info
+
+ .long 0xe320f003 @ Opcode for WFI
+
+ ldr r1, =(0x0 << 6) @ A[6] = 0
+ ldr r2, [r0, #0x00] @ Load tag info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ mcr p15, 0, r1, c15, c8, 2 @ Write L2 data 0 register to L2 tag RAM
+
+ ldr r1, =(0x1 << 6) @ A[6] = 1
+ ldr r2, [r0, #0x04] @ Load tag info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ mcr p15, 0, r1, c15, c8, 2 @ Write L2 data 0 register to L2 tag RAM
+
+ ldr r1, =(0x0 << 3) @ A[6:3] = b0000
+ ldr r2, [r0, #0x08] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x0C] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x10] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0x1 << 3) @ A[6:3] = b0001
+ ldr r2, [r0, #0x14] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x18] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x1C] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0x2 << 3) @ A[6:3] = b0010
+ ldr r2, [r0, #0x20] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x24] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x28] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0x3 << 3) @ A[6:3] = b0011
+ ldr r2, [r0, #0x2C] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x30] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x34] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0x4 << 3) @ A[6:3] = b0100
+ ldr r2, [r0, #0x38] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x3C] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x40] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0x5 << 3) @ A[6:3] = b0101
+ ldr r2, [r0, #0x44] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x48] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x4C] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0x6 << 3) @ A[6:3] = b0110
+ ldr r2, [r0, #0x50] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x54] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x58] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0x7 << 3) @ A[6:3] = b0111
+ ldr r2, [r0, #0x5C] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x60] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x64] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0x8 << 3) @ A[6:3] = b1000
+ ldr r2, [r0, #0x68] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x6C] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x70] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0x9 << 3) @ A[6:3] = b1001
+ ldr r2, [r0, #0x74] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x78] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x7C] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0xA << 3) @ A[6:3] = b1010
+ ldr r2, [r0, #0x80] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x84] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x88] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0xB << 3) @ A[6:3] = b1011
+ ldr r2, [r0, #0x8C] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x90] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0x94] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0xC << 3) @ A[6:3] = b1100
+ ldr r2, [r0, #0x98] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0x9C] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0xA0] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0xD << 3) @ A[6:3] = b1101
+ ldr r2, [r0, #0xA4] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0xA8] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0xAC] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0xE << 3) @ A[6:3] = b1110
+ ldr r2, [r0, #0xB0] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0xB4] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0xB8] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0xF << 3) @ A[6:3] = b1111
+ ldr r2, [r0, #0xBC] @ Load data info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ ldr r2, [r0, #0xC0] @ Load data info
+ mcr p15, 0, r2, c15, c8, 1 @ Move R2 to L2 data 1 register
+ ldr r2, [r0, #0xC4] @ Load data info
+ mcr p15, 0, r2, c15, c8, 5 @ Move R2 to L2 data 2 register
+ mcr p15, 0, r1, c15, c8, 3 @ Write L2 data 0-2 registers to L2 data RAM
+
+ ldr r1, =(0x2 << 29) | (0x0 << 6) @ WAY = A[31:29] = 2, A[6] = 0
+ ldr r2, [r0, #0xC8] @ Load tag info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ mcr p15, 0, r1, c15, c8, 2 @ Write L2 data 0 register to L2 tag RAM
+
+ ldr r1, =(0x2 << 29) | (0x1 << 6) @ WAY = A[31:29] = 2, A[6] = 1
+ ldr r2, [r0, #0xCC] @ Load tag info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ mcr p15, 0, r1, c15, c8, 2 @ Write L2 data 0 register to L2 tag RAM
+
+ ldr r1, =(0x4 << 29) | (0x0 << 6) @ WAY = A[31:29] = 4, A[6] = 0
+ ldr r2, [r0, #0xD0] @ Load tag info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ mcr p15, 0, r1, c15, c8, 2 @ Write L2 data 0 register to L2 tag RAM
+
+ ldr r1, =(0x4 << 29) | (0x1 << 6) @ WAY = A[31:29] = 4, A[6] = 1
+ ldr r2, [r0, #0xD4] @ Load tag info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ mcr p15, 0, r1, c15, c8, 2 @ Write L2 data 0 register to L2 tag RAM
+
+ ldr r1, =(0x6 << 29) | (0x0 << 6) @ WAY = A[31:29] = 6, A[6] = 0
+ ldr r2, [r0, #0xD8] @ Load tag info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ mcr p15, 0, r1, c15, c8, 2 @ Write L2 data 0 register to L2 tag RAM
+
+ ldr r1, =(0x6 << 29) | (0x1 << 6) @ WAY = A[31:29] = 6, A[6] = 1
+ ldr r2, [r0, #0xDC] @ Load tag info
+ mcr p15, 0, r2, c15, c8, 0 @ Move R2 to L2 data 0 register
+ mcr p15, 0, r1, c15, c8, 2 @ Write L2 data 0 register to L2 tag RAM
+
+ mrc p15, 0, r1, c1, c0, 1 @ R1 = auxiliary control reg
+ orr r1, r1, #ARM_AUXCR_L2EN @ Enable L2 cache
+ mcr p15, 0, r1, c1, c0, 1 @ Update aux control reg
+
+ mrc p15, 0, r2, c1, c0, 0 @ R2 = system control reg
+ orr r2, r2, #ARM_CTRL_DCACHE @ Enable DCache
+ mcr p15, 0, r2, c1, c0, 0 @ Update system control reg
+
+ b Done
+
+SkipL2Access:
+ .long 0xe320f003 @ Opcode for WFI
+
+Done:
+ mov pc, lr
+
+ .type cortexa8_idle_workaround, #object
+ENTRY(cortexa8_idle_workaround)
+ .word cpu_cortexa8_do_idle
+ .size cortexa8_idle_workaround, . - cortexa8_idle_workaround
+
diff --git a/arch/arm/mach-ns9xxx/Kconfig b/arch/arm/mach-ns9xxx/Kconfig
index dd0cd5ac4b8b..7edebdcf1c59 100644
--- a/arch/arm/mach-ns9xxx/Kconfig
+++ b/arch/arm/mach-ns9xxx/Kconfig
@@ -2,29 +2,149 @@ if ARCH_NS9XXX
menu "NS9xxx Implementations"
-config NS9XXX_HAVE_SERIAL8250
+config DIGI_UNSUPPORTED
+ def_bool n
+ help
+ This symbol is used to disable some options in the kernel
+ configuration that are not supported by Digi Embedded Linux.
+
+config PROCESSOR_NS921X
+ bool
+
+config RESET_DISABLED_MODULES
+ bool "reset disabled modules"
+ depends on BROKEN && PROCESSOR_NS921X
+ help
+ If you say Y here, cpu components that are not used are not only
+ disabled in the SYS_CLOCK register but also reset using the SYS_RESET
+
+ Currently this breaks the serial console.
+
+config PROCESSOR_NS9210
+ bool
+ select PROCESSOR_NS921X
+
+config PROCESSOR_NS9215
bool
+ select PROCESSOR_NS921X
config PROCESSOR_NS9360
bool
+ select USB_ARCH_HAS_OHCI
+
+config MODULE_CC9C
+ bool
+ select PROCESSOR_NS9360
+
+config MODULE_CC7UCAMRY
+ bool
+ select PROCESSOR_NS9210
+
+config MODULE_CC9P9215
+ bool
+ select PROCESSOR_NS9215
+
+config MODULE_CCW9P9215
+ bool
+ select PROCESSOR_NS9215
config MODULE_CC9P9360
bool
select PROCESSOR_NS9360
+config MODULE_CME9210
+ bool
+ select PROCESSOR_NS9210
+
+config MODULE_CCW9C
+ bool
+ select PROCESSOR_NS9360
+
+config MODULE_INC20OTTER
+ bool
+ select PROCESSOR_NS9210
+
config BOARD_A9M9750DEV
- select NS9XXX_HAVE_SERIAL8250
+ bool
+
+config BOARD_JSCC9P9215
+ bool
+
+config BOARD_JSCCW9P9215
bool
config BOARD_JSCC9P9360
bool
+config BOARD_JSCME9210
+ bool
+
+config BOARD_UNCBAS
+ bool
+
+choice
+ prompt "NS9xxx Implementations"
+
+config MACH_CC7UCAMRY
+ bool "ConnectCore 7U Camry"
+ depends on DIGI_UNSUPPORTED
+ select MODULE_CC7UCAMRY
+ select BOARD_UNCBAS
+ help
+ Say Y here if you are using the CC7U Camry module.
+
+ This is a modified ConnectCore 7U with an NS9210 processor
+ instead of an NS7520.
+
+config MACH_CC9C
+ bool "ConnectCore 9C"
+ select MODULE_CC9C
+ help
+ Say Y here if you are using the ConnectCore 9C.
+
+config MACH_CCW9C
+ bool "ConnectCore Wi-9C"
+ select MODULE_CCW9C
+ help
+ Say Y here if you are using the ConnectCore Wi-9C.
+
+config MACH_CC9P9215
+ bool "ConnectCore 9P 9215"
+ select MODULE_CC9P9215
+ help
+ Say Y here if you are using the ConnectCore 9P 9215 module on an
+ currently unsupported board.
+
+config MACH_CC9P9215JS
+ bool "ConnectCore 9P 9215 on a JSCC9P9215 Devboard"
+ select MODULE_CC9P9215
+ select BOARD_JSCC9P9215
+ help
+ Say Y here if you are using the ConnectCore 9P 9215 module
+ on a JSCC9P9215 Development Board.
+
+config MACH_CCW9P9215
+ bool "ConnectCore Wi-9P 9215"
+ select MODULE_CCW9P9215
+ help
+ Say Y here if you are using the ConnectCore Wi-9P 9215 module on an
+ currently unsupported board.
+
+config MACH_CCW9P9215JS
+ bool "ConnectCore Wi-9P 9215 on a JSCCW9P9215 Devboard"
+ select MODULE_CCW9P9215
+ select BOARD_JSCCW9P9215
+ help
+ Say Y here if you are using the ConnectCore Wi-9P 9215 module
+ on a JSCCW9P9215 Development Board.
+
config MACH_CC9P9360DEV
bool "ConnectCore 9P 9360 on an A9M9750 Devboard"
+ depends on DIGI_UNSUPPORTED
select MODULE_CC9P9360
select BOARD_A9M9750DEV
help
- Say Y here if you are using the Digi ConnectCore 9P 9360
+ Say Y here if you are using the ConnectCore 9P 9360
on an A9M9750 Development Board.
config MACH_CC9P9360JS
@@ -32,8 +152,482 @@ config MACH_CC9P9360JS
select MODULE_CC9P9360
select BOARD_JSCC9P9360
help
- Say Y here if you are using the Digi ConnectCore 9P 9360
- on an JSCC9P9360 Development Board.
+ Say Y here if you are using the ConnectCore 9P 9360
+ on a JSCC9P9360 Development Board.
+
+config MACH_CME9210
+ bool "Digi Connect ME 9210"
+ select MODULE_CME9210
+
+config MACH_CME9210JS
+ bool "Digi Connect ME 9210 on Devboard"
+ select MODULE_CME9210
+ select BOARD_JSCME9210
+
+config MACH_INC20OTTER
+ bool "Inc20-Otter"
+ depends on DIGI_UNSUPPORTED
+ select MODULE_INC20OTTER
+
+config MACH_OTTER
+ bool "Otter"
+ depends on DIGI_UNSUPPORTED
+ select PROCESSOR_NS9210
+
+endchoice
+
+config NS9XXX_HAVE_GPIO_LIB
+ bool "Use GPIO_LIB" if DIGI_UNSUPPORTED
+ default y
+ select ARCH_REQUIRE_GPIOLIB
+
+if (MACH_CC9C || MACH_CCW9C)
+
+comment "ConnectCore 9C/Wi-9C configuration"
+
+config CCX9C_SERIAL_PORTA
+ bool "Serial port A"
+ default y
+ depends on SERIAL_NS9360 && !CCX9C_SPI_PORTA
+
+choice
+ prompt "port configuration"
+ depends on CCX9C_SERIAL_PORTA
+ default CCX9C_SERIAL_PORTA_FULL
+
+config CCX9C_SERIAL_PORTA_RXTX
+ bool "RX/TX only"
+
+config CCX9C_SERIAL_PORTA_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CCX9C_SERIAL_PORTA_FULL
+ bool "Full port"
+
+endchoice
+
+config CCX9C_SPI_PORTA
+ bool "SPI port A"
+ default n
+ depends on SPI_NS9360
+
+config CCX9C_SERIAL_PORTB
+ bool "Serial port B"
+ default y
+ depends on SERIAL_NS9360 && !CCX9C_SPI_PORTB
+
+choice
+ prompt "port configuration"
+ depends on CCX9C_SERIAL_PORTB
+ default CCX9C_SERIAL_PORTB_FULL
+
+config CCX9C_SERIAL_PORTB_RXTX
+ bool "RX/TX only"
+
+config CCX9C_SERIAL_PORTB_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CCX9C_SERIAL_PORTB_FULL
+ bool "Full port"
+
+endchoice
+
+config CCX9C_SPI_PORTB
+ bool "SPI port B"
+ default y
+ depends on SPI_NS9360
+
+config CCX9C_SERIAL_PORTC
+ bool "Serial port C"
+ default y
+ depends on SERIAL_NS9360 && !CCX9C_SPI_PORTC && !CCX9C_FB
+
+choice
+ prompt "port configuration"
+ depends on CCX9C_SERIAL_PORTC
+ default CCX9C_SERIAL_PORTC_FULL
+
+config CCX9C_SERIAL_PORTC_RXTX
+ bool "RX/TX only"
+
+config CCX9C_SERIAL_PORTC_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CCX9C_SERIAL_PORTC_FULL
+ bool "Full port"
+
+endchoice
+
+config CCX9C_SPI_PORTC
+ bool "SPI port C"
+ default n
+ depends on SPI_NS9360 && !CCX9C_FB
+
+config CCX9C_SERIAL_PORTD
+ bool "Serial port D"
+ default y
+ depends on SERIAL_NS9360 && !CCX9C_SPI_PORTD && !CCX9C_FB
+
+choice
+ prompt "port configuration"
+ depends on CCX9C_SERIAL_PORTD
+ default CCX9C_SERIAL_PORTD_FULL
+
+config CCX9C_SERIAL_PORTD_RXTX
+ bool "RX/TX only"
+
+config CCX9C_SERIAL_PORTD_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CCX9C_SERIAL_PORTD_FULL
+ bool "Full port"
+
+endchoice
+
+config CCX9C_SPI_PORTD
+ bool "SPI port D"
+ default n
+ depends on SPI_NS9360 && !CCX9C_FB
+
+config CCX9C_FB
+ bool "Framebuffer"
+ default y
+ depends on FB_NS9360
+
+config CCX9C_TOUCH
+ bool "Touchscreen"
+ default y
+ depends on TOUCHSCREEN_ADS7846 && CCX9C_FB && CCX9C_SPI_PORTB
+
+endif ## if (MACH_CC9C || MACH_CCW9C)
+
+
+if (MACH_CC9P9215JS || MACH_CCW9P9215JS)
+
+comment "ConnectCore 9P/Wi-9P 9215 configuration"
+
+config CC9P9215JS_SERIAL_PORTA
+ bool "Serial port A"
+ default y
+ depends on SERIAL_NS921X && !CC9P9215JS_SPI
+
+choice
+ prompt "port configuration"
+ depends on CC9P9215JS_SERIAL_PORTA
+ default CC9P9215JS_SERIAL_PORTA_FULL
+
+config CC9P9215JS_SERIAL_PORTA_RXTX
+ bool "RX/TX only"
+
+config CC9P9215JS_SERIAL_PORTA_RXTX485
+ bool "RX/TX/RTS (RTS for RS-485 transceiver control)"
+ help
+ This mode enables the RS-485 transceiver control functionality
+ of the RTS line in the serial port.
+
+config CC9P9215JS_SERIAL_PORTA_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CC9P9215JS_SERIAL_PORTA_FULL
+ bool "Full port"
+
+endchoice
+
+config CC9P9215JS_SERIAL_PORTB
+ bool "Serial port B"
+ default y
+ depends on SERIAL_NS921X
+
+choice
+ prompt "port configuration"
+ depends on CC9P9215JS_SERIAL_PORTB
+ default CC9P9215JS_SERIAL_PORTB_FULL
+
+config CC9P9215JS_SERIAL_PORTB_RXTX
+ bool "RX/TX only"
+
+config CC9P9215JS_SERIAL_PORTB_RXTX485
+ bool "RX/TX/RTS (RTS for RS-485 transceiver control)"
+ help
+ This mode enables the RS-485 transceiver control functionality
+ of the RTS line in the serial port.
+
+config CC9P9215JS_SERIAL_PORTB_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CC9P9215JS_SERIAL_PORTB_FULL
+ bool "Full port"
+
+endchoice
+
+config CC9P9215JS_SERIAL_PORTC
+ bool "Serial port C"
+ default y
+ depends on SERIAL_NS921X
+
+choice
+ prompt "port configuration"
+ depends on CC9P9215JS_SERIAL_PORTC
+ default CC9P9215JS_SERIAL_PORTC_FULL
+
+config CC9P9215JS_SERIAL_PORTC_RXTX
+ bool "RX/TX only"
+
+config CC9P9215JS_SERIAL_PORTC_RXTX485
+ bool "RX/TX/RTS (RTS for RS-485 transceiver control)"
+ help
+ This mode enables the RS-485 transceiver control functionality
+ of the RTS line in the serial port.
+
+config CC9P9215JS_SERIAL_PORTC_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CC9P9215JS_SERIAL_PORTC_FULL
+ bool "Full port"
+
+endchoice
+
+config CC9P9215JS_SERIAL_PORTD
+ bool "Serial port D"
+ default y
+ depends on SERIAL_NS921X
+
+choice
+ prompt "port configuration"
+ depends on CC9P9215JS_SERIAL_PORTD
+ default CC9P9215JS_SERIAL_PORTD_FULL
+
+config CC9P9215JS_SERIAL_PORTD_RXTX
+ bool "RX/TX only"
+
+config CC9P9215JS_SERIAL_PORTD_RXTX485
+ bool "RX/TX/RTS (RTS for RS-485 transceiver control)"
+ help
+ This mode enables the RS-485 transceiver control functionality
+ of the RTS line in the serial port.
+
+config CC9P9215JS_SERIAL_PORTD_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CC9P9215JS_SERIAL_PORTD_FULL
+ bool "Full port"
+
+endchoice
+
+config CC9P9215JS_SPI
+ bool "SPI port"
+ default y
+ depends on SPI_NS921X
+
+config CC9P9215JS_EDT_DISPLAY_QVGA
+ bool "Support for the EDT QVGA TFT Display"
+ default n
+ select FB_HX8347
+
+config CC9P9215JS_TOUCH
+ bool "Touchscreen"
+ default y
+ depends on TOUCHSCREEN_ADS7846 && CC9P9215JS_EDT_DISPLAY_QVGA && CC9P9215JS_SPI
+
+endif ## if (MACH_CC9P9215JS || MACH_CCW9P9215JS)
+
+
+if MACH_CC9P9360JS
+
+comment "ConnectCore 9P 9360 configuration"
+
+config CC9P9360JS_SERIAL_PORTA
+ bool "Serial port A"
+ default y
+ depends on SERIAL_NS9360 && !CC9P9360JS_SPI_PORTA
+
+choice
+ prompt "port configuration"
+ depends on CC9P9360JS_SERIAL_PORTA
+ default CC9P9360JS_SERIAL_PORTA_FULL
+
+config CC9P9360JS_SERIAL_PORTA_RXTX
+ bool "RX/TX only"
+
+config CC9P9360JS_SERIAL_PORTA_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CC9P9360JS_SERIAL_PORTA_FULL
+ bool "Full port"
+
+endchoice
+
+config CC9P9360JS_SPI_PORTA
+ bool "SPI port A"
+ default n
+ depends on SPI_NS9360
+
+config CC9P9360JS_SERIAL_PORTB
+ bool "Serial port B"
+ default y
+ depends on SERIAL_NS9360 && !CC9P9360JS_SPI_PORTB
+
+choice
+ prompt "port configuration"
+ depends on CC9P9360JS_SERIAL_PORTB
+ default CC9P9360JS_SERIAL_PORTB_FULL
+
+config CC9P9360JS_SERIAL_PORTB_RXTX
+ bool "RX/TX only"
+
+config CC9P9360JS_SERIAL_PORTB_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CC9P9360JS_SERIAL_PORTB_FULL
+ bool "Full port"
+
+endchoice
+
+config CC9P9360JS_SPI_PORTB
+ bool "SPI port B"
+ default y
+ depends on SPI_NS9360
+
+config CC9P9360JS_SERIAL_PORTC
+ bool "Serial port C"
+ default y
+ depends on SERIAL_NS9360 && !CC9P9360JS_SPI_PORTC && !CC9P9360JS_FB
+
+choice
+ prompt "port configuration"
+ depends on CC9P9360JS_SERIAL_PORTC
+ default CC9P9360JS_SERIAL_PORTC_FULL
+
+config CC9P9360JS_SERIAL_PORTC_RXTX
+ bool "RX/TX only"
+
+config CC9P9360JS_SERIAL_PORTC_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CC9P9360JS_SERIAL_PORTC_FULL
+ bool "Full port"
+
+endchoice
+
+config CC9P9360JS_SPI_PORTC
+ bool "SPI port C"
+ default n
+ depends on SPI_NS9360 && !CC9P9360JS_FB
+
+config CC9P9360JS_SERIAL_PORTD
+ bool "Serial port D"
+ default y
+ depends on SERIAL_NS9360 && !CC9P9360JS_SPI_PORTD && !CC9P9360JS_FB
+
+choice
+ prompt "port configuration"
+ depends on CC9P9360JS_SERIAL_PORTD
+ default CC9P9360JS_SERIAL_PORTD_FULL
+
+config CC9P9360JS_SERIAL_PORTD_RXTX
+ bool "RX/TX only"
+
+config CC9P9360JS_SERIAL_PORTD_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CC9P9360JS_SERIAL_PORTD_FULL
+ bool "Full port"
+
+endchoice
+
+config CC9P9360JS_SPI_PORTD
+ bool "SPI port D"
+ default n
+ depends on SPI_NS9360 && !CC9P9360JS_FB
+
+config CC9P9360JS_FB
+ bool "Framebuffer"
+ default y
+ depends on FB_NS9360
+
+config CC9P9360JS_TOUCH
+ bool "Touchscreen"
+ default y
+ depends on TOUCHSCREEN_ADS7846 && CC9P9360JS_FB && CC9P9360JS_SPI_PORTB
+
+# Commented out as cc9p9360 does not assemble 0 ohm R2
+# resistor which routes the RTC alarm to the CPU
+#config EXTERNAL_RTC_ALARM
+# bool "External RTC Alarm interrupt"
+# default n
+# depends on !CC9P9360JS_SERIAL_PORTA_FULL
+# help
+# This enables the interrupt coming from the external
+# I2C DS1337 Real Time Clock.
+#comment "--- External RTC Alarm interrupt (disable Serial port A Full)"
+# depends on CC9P9360JS_SERIAL_PORTA_FULL
+
+endif ## if MACH_CC9P9360JS
+
+
+if MACH_CME9210JS
+
+comment "Digi Connect ME 9210 configuration"
+
+config CME9210JS_SERIAL_PORTA
+ bool "Serial port A"
+ default y
+ depends on SERIAL_NS921X
+
+choice
+ prompt "port configuration"
+ depends on CME9210JS_SERIAL_PORTA
+ default CME9210JS_SERIAL_PORTA_RXTX
+
+config CME9210JS_SERIAL_PORTA_RXTX
+ bool "RX/TX only"
+
+config CME9210JS_SERIAL_PORTA_RXTX485
+ bool "RX/TX/RTS (RTS for RS-485 transceiver control)"
+ help
+ This mode enables the RS-485 transceiver control functionality
+ of the RTS line in the serial port.
+
+config CME9210JS_SERIAL_PORTA_CTSRTSRXTX
+ bool "CTS/RTS/RX/TX"
+
+config CME9210JS_SERIAL_PORTA_FULL
+ bool "Full port"
+
+endchoice
+
+config CME9210JS_SERIAL_PORTC
+ bool "Serial port C (only modules with JTAG-header)"
+ depends on SERIAL_NS921X && !I2C_NS9XXX
+ help
+ Enable serial port C on Connect ME9210 module. You can only
+ use this port if your module has the JTAG-header!
+ This port shares GPIOs with I2C bus, so I2C must be disabled.
+
+choice
+ prompt "port configuration"
+ depends on CME9210JS_SERIAL_PORTC
+ default CME9210JS_SERIAL_PORTC_RXTX
+
+config CME9210JS_SERIAL_PORTC_RXTX
+ bool "RX/TX only"
+
+endchoice
+
+config CME9210JS_SPI
+ bool "SPI port"
+ depends on !CME9210JS_SERIAL_PORTA
+
+config GPIO_ETH_ACTIVITY_LED
+ bool "Ethernet activity LED"
+ depends on NS9XXX_ETH
+ default y
+ help
+ Enables the use of GPIO14 as Ethernet RX/TX activity LED
+
+endif ## if MACH_CME9210JS
+
+source "arch/arm/mach-ns9xxx/include/mach/display/Kconfig"
endmenu
diff --git a/arch/arm/mach-ns9xxx/Makefile b/arch/arm/mach-ns9xxx/Makefile
index 41efaf9ad50b..63aae2d39ef3 100644
--- a/arch/arm/mach-ns9xxx/Makefile
+++ b/arch/arm/mach-ns9xxx/Makefile
@@ -1,12 +1,45 @@
-obj-y := clock.o generic.o gpio.o irq.o
+obj-y := clock.o gpio.o irq.o
+ifeq ($(CONFIG_GPIOLIB),y)
+gpio-$(CONFIG_PROCESSOR_NS921X) += gpiolib-ns921x.o
+gpio-$(CONFIG_PROCESSOR_NS9360) += gpiolib-ns9360.o
+endif
+
+obj-$(CONFIG_MACH_CC7UCAMRY) += mach-cc7ucamry.o
+obj-$(CONFIG_MACH_CC9C) += mach-cc9c.o
+obj-$(CONFIG_MACH_CC9P9215) += mach-cc9p9215.o
+obj-$(CONFIG_MACH_CC9P9215JS) += mach-cc9p9215js.o
obj-$(CONFIG_MACH_CC9P9360DEV) += mach-cc9p9360dev.o
obj-$(CONFIG_MACH_CC9P9360JS) += mach-cc9p9360js.o
+obj-$(CONFIG_MACH_CME9210) += mach-cme9210.o
+obj-$(CONFIG_MACH_CME9210JS) += mach-cme9210js.o
+obj-$(CONFIG_MACH_CCW9P9215) += mach-ccw9p9215.o
+obj-$(CONFIG_MACH_CCW9P9215JS) += mach-ccw9p9215js.o
+obj-$(CONFIG_MACH_CCW9C) += mach-ccw9c.o
+obj-$(CONFIG_MACH_INC20OTTER) += mach-inc20otter.o
+obj-$(CONFIG_MACH_OTTER) += mach-otter.o
+
+obj-$(CONFIG_PROCESSOR_NS921X) += gpio-ns921x.o irq-ns921x.o processor-ns921x.o time-ns921x.o ns921x_devices.o
+obj-$(CONFIG_PROCESSOR_NS9215) += ns9215_devices.o
+obj-$(CONFIG_PROCESSOR_NS9360) += gpio-ns9360.o processor-ns9360.o time-ns9360.o ns9360_devices.o
+
+obj-$(CONFIG_MODULE_CC9P9215) += cc9p9215_devices.o
+ifeq ($(CONFIG_MODULE_CCW9P9215),y)
+CFLAGS_ccw9p9215_devices.o += -I$(srctree)/drivers/net/wireless/digiPiper/
+CFLAGS_mach-ccw9p9215js.o += -I$(srctree)/drivers/net/wireless/digiPiper/
+endif
+obj-$(CONFIG_MODULE_CCW9P9215) += cc9p9215_devices.o ccw9p9215_devices.o
+obj-$(CONFIG_MODULE_CC9P9360) += cc9p9360_devices.o
+obj-$(CONFIG_MODULE_CC9C) += ccx9c_devices.o
+obj-$(CONFIG_MODULE_CCW9C) += ccx9c_devices.o
+obj-$(CONFIG_MODULE_CME9210) += cme9210_devices.o
-obj-$(CONFIG_PROCESSOR_NS9360) += gpio-ns9360.o processor-ns9360.o time-ns9360.o
+obj-$(CONFIG_LEDS_CPU) += leds.o
-obj-$(CONFIG_BOARD_A9M9750DEV) += board-a9m9750dev.o
-obj-$(CONFIG_BOARD_JSCC9P9360) += board-jscc9p9360.o
+# Power Management
+obj-$(CONFIG_PM) += pm.o
+pm-$(CONFIG_PROCESSOR_NS921X) += pm-ns921x.o
+ifeq ($(CONFIG_PM_VERBOSE),y)
+CFLAGS_pm-ns921x.o += -DDEBUG
+endif
-# platform devices
-obj-$(CONFIG_NS9XXX_HAVE_SERIAL8250) += plat-serial8250.o
diff --git a/arch/arm/mach-ns9xxx/cc9p9215_devices.c b/arch/arm/mach-ns9xxx/cc9p9215_devices.c
new file mode 100644
index 000000000000..9de10e6e9666
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/cc9p9215_devices.c
@@ -0,0 +1,681 @@
+/*
+ * arch/arm/mach-ns9xxx/cc9p9215_devices.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/mtd/physmap.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <mach/fim-ns921x.h>
+#include <mach/hardware.h>
+#include <mach/regs-sys-common.h>
+#include <mach/regs-sys-ns921x.h>
+#include <mach/regs-mem.h>
+
+#include <video/hx8347fb.h>
+#include <linux/fb.h>
+#include <linux/mmc/host.h>
+#include <linux/w1-gpio.h>
+
+#include "ns921x_devices.h"
+#include "cc9p9215_devices.h"
+#include "clock.h"
+
+/*
+ * Pick Digi's internal FIM board
+ * Use internal board, defined to 1
+ * Use newer boards, defined to 0
+ */
+#if 0
+#define INT_FIM_BOARD
+#endif
+
+#if defined(CONFIG_NS9XXX_ETH) || defined(CONFIG_NS9XXX_ETH_MODULE)
+static int cc9p9215_phy_endisable(struct clk *clk, int enable)
+{
+ int ret;
+
+ if (enable) {
+ ret = gpio_request(90, "ns9xxx-eth-phy");
+ if (ret)
+ return ret;
+
+ gpio_direction_output(90, 1);
+ } else {
+ gpio_set_value(90, 0);
+ gpio_free(90);
+ }
+
+ return 0;
+}
+
+static struct clk phyclk = {
+ .name = "ns9xxx-eth-phy",
+ .id = -1,
+ .owner = THIS_MODULE,
+ .endisable = cc9p9215_phy_endisable,
+};
+
+void __init ns9xxx_add_device_cc9p9215_eth(void)
+{
+ int gpio[] = {32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49};
+ int func[] = {0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int dir[] = {0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ if (clk_register(&phyclk))
+ return;
+
+ ns9xxx_add_device_ns921x_eth(&phyclk, 0, gpio, func,
+ dir, ARRAY_SIZE(gpio));
+}
+#else
+void __init ns9xxx_add_device_cc9p9215_eth(void) {}
+#endif
+
+#if defined(CONFIG_I2C_NS9XXX) || defined(CONFIG_I2C_NS9XXX_MODULE)
+static void cc9p9215_i2c_gpio_reconfigure(void)
+{
+ gpio_configure_ns921x(102, 0, 0, 2, 0);
+ gpio_configure_ns921x(103, 0, 0, 2, 0);
+}
+
+static struct plat_ns9xxx_i2c ns9xxx_device_cc9p9215_i2c_data = {
+ .gpio_scl = 102,
+ .gpio_sda = 103,
+ .speed = 100000,
+ .gpio_configuration_func = cc9p9215_i2c_gpio_reconfigure,
+};
+
+void __init ns9xxx_add_device_cc9p9215_i2c(void)
+{
+ ns9xxx_add_device_ns921x_i2c(&ns9xxx_device_cc9p9215_i2c_data);
+}
+#else
+void __init ns9xxx_add_device_cc9p9215_i2c(void) {}
+#endif
+
+#if defined(CONFIG_SERIAL_NS921X) || defined(CONFIG_SERIAL_NS921X_MODULE)
+void __init ns9xxx_add_device_cc9p9215_uarta(int gpio_nr)
+{
+ ns9xxx_add_device_ns921x_uarta(0, gpio_nr, 0);
+}
+
+void __init ns9xxx_add_device_cc9p9215_uartb(int gpio_nr)
+{
+ ns9xxx_add_device_ns921x_uartb(51, gpio_nr, 0);
+}
+
+void __init ns9xxx_add_device_cc9p9215_uartc(int gpio_nr)
+{
+ ns9xxx_add_device_ns921x_uartc(8, gpio_nr, 0);
+}
+
+void __init ns9xxx_add_device_cc9p9215_uartd(int gpio_nr)
+{
+ ns9xxx_add_device_ns921x_uartd(59, gpio_nr, 0);
+}
+#else
+void __init ns9xxx_add_device_cc9p9215_uarta(int gpio_nr) {}
+void __init ns9xxx_add_device_cc9p9215_uartb(int gpio_nr) {}
+void __init ns9xxx_add_device_cc9p9215_uartc(int gpio_nr) {}
+void __init ns9xxx_add_device_cc9p9215_uartd(int gpio_nr) {}
+#endif
+
+#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP)
+static struct physmap_flash_data ns9xxx_device_cc9p9215_flash_data = {
+ .width = 2,
+};
+
+void __init ns9xxx_add_device_cc9p9215_flash(void)
+{
+ ns9xxx_add_device_ns921x_flash(&ns9xxx_device_cc9p9215_flash_data);
+}
+#else
+void __init ns9xxx_add_device_cc9p9215_flash(void) {}
+#endif
+
+#if defined(CONFIG_SPI_NS921X) || defined(CONFIG_SPI_NS921X_MODULE)
+/* SPI ports and their related GPIOs */
+static struct spi_ns9xxx_data ns9xxx_device_cc9p9215_spi_data = {
+ .gpios = {7, 3, 5, 0},
+ .gpio_funcs = { NS921X_GPIO_FUNC_4,
+ NS921X_GPIO_FUNC_4,
+ NS921X_GPIO_FUNC_4,
+ NS921X_GPIO_FUNC_4 },
+ .nr_gpios = 4,
+};
+
+void __init ns9xxx_add_device_cc9p9215_spi(void) {
+ ns9xxx_add_device_ns921x_spi(&ns9xxx_device_cc9p9215_spi_data);
+}
+#else
+void __init ns9xxx_add_device_cc9p9215_spi(void) {}
+#endif
+
+
+#if defined(CONFIG_FIM_ZERO_SERIAL)
+static struct fim_serial_platform_data fim_serial_data0 = {
+ .fim_nr = 0,
+#if defined(CONFIG_FIM_ZERO_SERIAL_CTSRTS)
+ NS921X_FIM_SERIAL_GPIOS(69, 68, /* RX + TX */
+ 70, 71, /* RTS + CTS */
+ NS921X_GPIO_FUNC_0),
+#else
+ NS921X_FIM_SERIAL_GPIOS(69, 68, /* RX + TX */
+ FIM_GPIO_DONT_USE, /* RTS */
+ FIM_GPIO_DONT_USE, /* CTS */
+ NS921X_GPIO_FUNC_0),
+#endif
+};
+struct platform_device ns921x_fim_serial0 = {
+ .name = "fim-serial",
+ .id = 0,
+ .dev.platform_data = &fim_serial_data0,
+};
+EXPORT_SYMBOL(ns921x_fim_serial0);
+#endif /* CONFIG_FIM_ZERO_SERIAL */
+
+#if defined(CONFIG_FIM_ONE_SERIAL)
+static struct fim_serial_platform_data fim_serial_data1 = {
+ .fim_nr = 1,
+#if defined(CONFIG_FIM_ONE_SERIAL_CTSRTS)
+ NS921X_FIM_SERIAL_GPIOS(73, 72, /* RX + TX */
+ 74, 75, /* RTS + CTS */
+ NS921X_GPIO_FUNC_1),
+#else
+ NS921X_FIM_SERIAL_GPIOS(73, 72, /* RX + TX */
+ FIM_GPIO_DONT_USE, /* RTS */
+ FIM_GPIO_DONT_USE, /* CTS */
+ NS921X_GPIO_FUNC_1),
+#endif
+};
+struct platform_device ns921x_fim_serial1 = {
+ .name = "fim-serial",
+ .id = 1,
+ .dev.platform_data = &fim_serial_data1,
+};
+EXPORT_SYMBOL(ns921x_fim_serial1);
+#endif /* CONFIG_FIM_ONE_SERIAL */
+
+
+#if defined(CONFIG_FIM_ZERO_SDIO)
+static struct fim_sdio_platform_data fim_sdio_data0 = {
+ .fim_nr = 0,
+ .host_caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ,
+#if defined(INT_FIM_BOARD)
+ NS921X_FIM_SDIO_GPIOS(68, 69, 70, 71, /* D0 to D3 */
+ 72, 73, /* WP + CD */
+ 76, 77, /* CLK + CMD */
+ NS921X_GPIO_FUNC_0),
+#else
+ NS921X_FIM_SDIO_GPIOS_FIM(68, 69, 70, 71, /* D0 to D3 */
+ 76, 77, /* CLK + CMD */
+ NS921X_GPIO_FUNC_0),
+ .cd_gpio_nr = 101, /* CD as external intrrupt */
+ .cd_gpio_func = NS921X_GPIO_FUNC_2,
+
+ .wp_gpio_nr = 100, /* WP as normal GPIO */
+ .wp_gpio_func = NS921X_GPIO_FUNC_3,
+#endif
+};
+struct platform_device ns921x_fim_sdio0 = {
+ .name = "fim-sdio",
+ .id = 0,
+ .dev.platform_data = &fim_sdio_data0,
+};
+EXPORT_SYMBOL(ns921x_fim_sdio0);
+#endif /* CONFIG_FIM_ZERO_SDIO */
+
+
+#if defined(CONFIG_FIM_ONE_SDIO)
+static struct fim_sdio_platform_data fim_sdio_data1 = {
+ .fim_nr = 1,
+ .host_caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ,
+#if defined(INT_FIM_BOARD)
+ NS921X_FIM_SDIO_GPIOS(68, 69, 70, 71, /* D0 to D3 */
+ 72, 73, /* WP + CD */
+ 76, 77, /* CLK + CMD */
+ NS921X_GPIO_FUNC_1),
+#else
+ NS921X_FIM_SDIO_GPIOS_FIM(72, 73, 74, 75, /* D0 to D3 */
+ 78, 79, /* CLK + CMD */
+ NS921X_GPIO_FUNC_1),
+ .cd_gpio_nr = 101, /* CD as external intrrupt */
+ .cd_gpio_func = NS921X_GPIO_FUNC_2,
+
+ .wp_gpio_nr = 100, /* WP as normal GPIO */
+ .wp_gpio_func = NS921X_GPIO_FUNC_3,
+#endif
+};
+struct platform_device ns921x_fim_sdio1 = {
+ .name = "fim-sdio",
+ .id = 1,
+ .dev.platform_data = &fim_sdio_data1,
+};
+EXPORT_SYMBOL(ns921x_fim_sdio1);
+#endif /* CONFIG_FIM_ONE_SDIO */
+
+
+#if defined(CONFIG_FIM_ZERO_CAN) || defined(CONFIG_PROCESSOR_NS9215)
+static struct fim_can_platform_data fim_can_data0 = {
+ .fim_nr = 0,
+ .fim_can_bitrate = 500000,
+ NS921X_FIM_CAN_GPIOS( 96, 97, /* RX + TX */
+ NS921X_GPIO_FUNC_2),
+};
+struct platform_device ns921x_fim_can0 = {
+ .name = "fim-can",
+ .id = 0,
+ .dev.platform_data = &fim_can_data0,
+};
+EXPORT_SYMBOL(ns921x_fim_can0);
+#endif /* CONFIG_FIM_ZERO_CAN */
+
+#if defined(CONFIG_FIM_ONE_CAN)
+static struct fim_can_platform_data fim_can_data1 = {
+ .fim_nr = 1,
+ .fim_can_bitrate = 500000,
+#if defined(INT_FIM_BOARD)
+ NS921X_FIM_CAN_GPIOS( 68, 69, /* RX + TX */
+ NS921X_GPIO_FUNC_2),
+#else
+ NS921X_FIM_CAN_GPIOS( 98, 99, /* RX + TX */
+ NS921X_GPIO_FUNC_2),
+#endif
+};
+struct platform_device ns921x_fim_can1 = {
+ .name = "fim-can",
+ .id = 1,
+ .dev.platform_data = &fim_can_data1,
+};
+EXPORT_SYMBOL(ns921x_fim_can1);
+#endif /* CONFIG_FIM_ONE_CAN */
+
+#if defined(CONFIG_FIM_ZERO_W1)
+static struct w1_gpio_platform_data fim_w1_data0 = {
+ .pin = 68,
+ .is_open_drain = 0,
+};
+struct platform_device ns921x_fim0_w1 = {
+ .name = "w1-gpio",
+ .id = 0,
+ .dev = {
+ .platform_data = &fim_w1_data0,
+ },
+};
+EXPORT_SYMBOL(ns921x_fim0_w1);
+#endif /* CONFIG_FIM_ZERO_W1 */
+
+#if defined(CONFIG_FIM_ONE_W1)
+static struct w1_gpio_platform_data fim_w1_data1 = {
+ .pin = 72,
+ .is_open_drain = 0,
+};
+struct platform_device ns921x_fim1_w1 = {
+ .name = "w1-gpio",
+ .id = 1,
+ .dev = {
+ .platform_data = &fim_w1_data1,
+ },
+};
+EXPORT_SYMBOL(ns921x_fim1_w1);
+#endif /* CONFIG_FIM_ONE_W1 */
+
+#if defined(CONFIG_FIM_ZERO_USB)
+static struct fim_usb_platform_data fim_usb_data0 = {
+ .fim_nr = 0,
+
+ /* The setup of the pull-ups must be fixed in the driver, however print a warning */
+#if defined(INT_FIM_BOARD)
+#warning "Internal FIM board has a different pull-ups configuration"
+ NS921X_FIM_USB_GPIOS(68, 69, 70, 71, /* VP + VM + RCV + OE_L */
+ 72, 73, /* ENUM + SPND */
+ NS921X_GPIO_FUNC_0,
+ NS921X_GPIO_FUNC_GPIO),
+#else
+ NS921X_FIM_USB_GPIOS(68, 69, 70, 71, /* VP + VM + RCV + OE_L */
+ 76, 77, /* ENUM + SPND */
+ NS921X_GPIO_FUNC_0,
+ NS921X_GPIO_FUNC_GPIO),
+#endif
+};
+struct platform_device ns921x_fim_usb0 = {
+ .name = "fim-usb",
+ .id = 0,
+ .dev.platform_data = &fim_usb_data0,
+};
+EXPORT_SYMBOL(ns921x_fim_usb0);
+#endif /* CONFIG_FIM_ZERO_USB */
+
+#if defined(CONFIG_FIM_ONE_USB)
+static struct fim_usb_platform_data fim_usb_data1 = {
+ .fim_nr = 1,
+ NS921X_FIM_USB_GPIOS(72, 73, 74, 75, /* VP + VM + RCV + OE_L */
+ 78, 79, /* ENUM + SPND */
+ NS921X_GPIO_FUNC_1,
+ NS921X_GPIO_FUNC_GPIO),
+};
+struct platform_device ns921x_fim_usb1 = {
+ .name = "fim-usb",
+ .id = 1,
+ .dev.platform_data = &fim_usb_data1,
+};
+EXPORT_SYMBOL(ns921x_fim_usb1);
+#endif /* CONFIG_FIM_ONE_USB */
+
+#if defined(CONFIG_CC9P9215JS_EDT_DISPLAY_QVGA)
+#ifdef CONFIG_CC9P9215JS_DISPLAY_USES_DMA
+#include <mach/dma-ns921x.h>
+struct ext_dma_desc_t dmadesc[3];
+#endif
+
+void __init cc9p9215_edt_qvga_lcd_setup_cs(void)
+{
+ /* LCD required the extended wait register to be set */
+ writel(3, MEM_SMEW);
+ /* 16 bit bus width and enable exteneded wait */
+ writel(MEM_SMC_PB_1 | MEM_SMC_EW_ON | MEM_SMC_MW_16, MEM_SMC(0));
+ /* Static Memory Write Enable Delay x */
+ writel(4, MEM_SMWED(0));
+ /* Static Memory Output Enable Delay x */
+ writel(0, MEM_SMOED(0));
+ /* Static Memory Read Delay x */
+ writel(0, MEM_SMRD(0));
+ /* Static Memory Page Mode Read Delay 0 */
+ writel(0, MEM_SMPMRD(0));
+ /* Static Memory Write Delay */
+ writel(0, MEM_SMWD(0));
+ /* Static Memory Turn Round Delay x */
+ writel(0, MEM_SWT(0));
+ /* Enable the CS0 access */
+ writel(readl(SYS_SMCSSMM(0)) | SYS_SMCSSMM_CSEx_EN, SYS_SMCSSMM(0));
+}
+
+
+int cc9p9215_edt_qvga_lcd_register_gpios(struct hx8347fb_pdata *pdata)
+{
+ if (gpio_request(pdata->rst_gpio, "lcd-rst"))
+ goto err;
+
+ gpio_direction_output(pdata->rst_gpio, 0);
+
+ if (gpio_request(pdata->enable_gpio, "lcd-enable"))
+ goto err1;
+
+ gpio_direction_output(pdata->enable_gpio, 0);
+
+ return 0;
+err1:
+ gpio_free(pdata->rst_gpio);
+err:
+ return -EBUSY;
+}
+
+/* Configuration for the EDT QVGA display, most of the settings have
+ * been taken from a Himax application note */
+unsigned char edt_qvga_lcd_init[][3] = {
+ /* Index, value, delay to write next register in ms*/
+ {0x46, 0x94, 0},
+ {0x47, 0x41, 0},
+ {0x48, 0x00, 0},
+ {0x49, 0x33, 0},
+ {0x4a, 0x23, 0},
+ {0x4b, 0x45, 0},
+ {0x4c, 0x44, 0},
+ {0x4d, 0x77, 0},
+ {0x4e, 0x12, 0},
+ {0x4f, 0xcc, 0},
+ {0x50, 0x46, 0},
+ {0x51, 0x82, 0},
+ {0x02, 0x00, 0}, /* Column address start 2 */
+ {0x03, 0x00, 0}, /* Column address start 1 */
+ {0x04, 0x01, 0}, /* Column address end 2 */
+ {0x05, 0x3f, 0}, /* Column address end 1 */
+ {0x06, 0x00, 0}, /* Row address start 2 */
+ {0x07, 0x00, 0}, /* Row address start 1 */
+ {0x08, 0x00, 0}, /* Row address end 2 */
+ {0x09, 0xef, 0}, /* Row address end 1 */
+ {0x01, 0x06, 0},
+ {0x16, 0x68, 0},
+ {0x23, 0x95, 0},
+ {0x24, 0x95, 0},
+ {0x25, 0xff, 0},
+ {0x27, 0x02, 0},
+ {0x28, 0x02, 0},
+ {0x29, 0x02, 0},
+ {0x2a, 0x02, 0},
+ {0x2c, 0x02, 0},
+ {0x2d, 0x02, 0},
+ {0x3a, 0x01, 0},
+ {0x3b, 0x01, 0},
+ {0x3c, 0xf0, 0},
+ {0x3d, 0x00, 20},
+ {0x35, 0x38, 0},
+ {0x36, 0x78, 0},
+ {0x3e, 0x38, 0},
+ {0x40, 0x0f, 0},
+ {0x41, 0xf0, 0},
+ {0x19, 0x49, 0},
+ {0x93, 0x0f, 10},
+ {0x20, 0x40, 0},
+ {0x1d, 0x07, 0},
+ {0x1e, 0x00, 0},
+ {0x1f, 0x04, 0},
+ {0x44, 0x40, 0},
+ {0x45, 0x12, 10},
+ {0x1c, 0x04, 20},
+ {0x43, 0x80, 5},
+ {0x1b, 0x08, 40},
+ {0x1b, 0x10, 40},
+ {0x90, 0x7f, 0},
+ {0x26, 0x04, 40},
+ {0x26, 0x24, 0},
+ {0x26, 0x2c, 40},
+ {0x26, 0x3c, 0},
+ {0x57, 0x02, 0},
+ {0x55, 0x00, 0},
+ {0x57, 0x00, 0}
+};
+
+
+static void cc9p9215_lcd_reset(struct hx8347fb_par *par)
+{
+ gpio_set_value(par->pdata->rst_gpio, 0);
+ mdelay(100);
+ gpio_set_value(par->pdata->rst_gpio, 1);
+}
+
+static void cc9p9215_lcd_enable(struct hx8347fb_par *par, int state)
+{
+ gpio_set_value(par->pdata->enable_gpio, state);
+}
+
+static void cc9p9215_lcd_set_idx(struct hx8347fb_par *par, u8 idx)
+{
+ writeb(idx, par->mmio_cmd);
+}
+
+static void cc9p9215_lcd_wr_reg(struct hx8347fb_par *par, u8 reg, u16 data)
+{
+ writeb(reg, par->mmio_cmd);
+ writew(data, par->mmio_data);
+}
+
+#ifdef CONFIG_CC9P9215JS_DISPLAY_USES_DMA
+static irqreturn_t fb_extdma(int irq, void *dev_id)
+{
+ u32 status;
+
+ status = readl(NS921X_DMA_STIE(1));
+
+ if (status & (NS921X_DMA_STIE_NCIP | NS921X_DMA_STIE_NRIP)) {
+ writel(status, NS921X_DMA_STIE(1));
+ }
+
+ return IRQ_HANDLED;
+}
+#endif
+
+static void cc9p9215_lcd_wr_data(struct hx8347fb_par *par, u16 *buf, int len)
+{
+#ifdef CONFIG_CC9P9215JS_DISPLAY_USES_DMA
+#if 0
+ /* CE=0, CG=1, SW=16bit, DW=16bit, SB=16 bytes, DB=1 unit, SAI=0, DAI=1 */
+ writel(NS921X_DMA_CR_CG | NS921X_DMA_CR_SW_16b | \
+ NS921X_DMA_CR_DW_16b | NS921X_DMA_CR_SB_16B | \
+ NS921X_DMA_CR_DB_1B | NS921X_DMA_CR_DINC_N, NS921X_DMA_CR(1));
+ /* CE=1, CG=1, SW=16bit, DW=16bit, SB=16 bytes, DB=1 unit, SAI=0, DAI=1 */
+ writel(NS921X_DMA_CR_CE | readl(NS921X_DMA_CR(1)), NS921X_DMA_CR(1));*/*/*/
+#endif
+#else
+ int i, wcycles;
+ u16 *data = buf;
+
+ wcycles = len >> 1;
+
+ for (i = 0; i < wcycles; i++)
+ writew(*data++, par->mmio_data);
+#endif
+}
+
+static int cc9p9215_lcd_init(struct hx8347fb_par *par)
+{
+ int i, ret = 0;
+ u8 reg;
+
+ if (cc9p9215_edt_qvga_lcd_register_gpios(par->pdata))
+ return -EINVAL;
+
+ cc9p9215_lcd_enable(par, 0);
+ cc9p9215_lcd_reset(par);
+ cc9p9215_edt_qvga_lcd_setup_cs();
+
+ mdelay(50);
+
+ writeb(HIMAX_ID_CODE, par->mmio_cmd);
+ if ((reg = readb(par->mmio_data)) != 0x47) {
+ pr_debug("%s: HX8347 controller not detected REG[0x67] = 0x%02x\n", __func__, reg);
+ ret = -EIO;
+ goto err_detect;
+ }
+
+#ifdef CONFIG_CC9P9215JS_DISPLAY_USES_DMA
+ writel(SYS_CLOCK_EXTDMA | readl(SYS_RESET), SYS_RESET);
+ writel(SYS_CLOCK_EXTDMA | readl(SYS_CLOCK), SYS_CLOCK);
+
+ dmadesc[0].src = (u32)par->info->screen_base;
+ dmadesc[0].dest = (u32)par->info->fix.smem_start;
+ dmadesc[0].length = (u16)(par->info->fix.smem_len / 3);
+ dmadesc[0].control = EXT_DMA_DESC_CTRL_FULL;
+
+ dmadesc[1].src = (u32)(par->info->screen_base + par->info->fix.smem_len / 3);
+ dmadesc[1].dest = (u32)par->info->fix.smem_start;
+ dmadesc[1].length = (u16)(par->info->fix.smem_len / 3);
+ dmadesc[1].control = EXT_DMA_DESC_CTRL_FULL;
+
+ dmadesc[2].src = (u32)(dmadesc[1].src + par->info->fix.smem_len / 3);
+ dmadesc[2].dest = (u32)par->info->fix.smem_start;
+ dmadesc[2].length = (u16)(par->info->fix.smem_len / 3);
+ dmadesc[2].control = EXT_DMA_DESC_CTRL_WRAP | \
+ EXT_DMA_DESC_CTRL_LAST | \
+ EXT_DMA_DESC_CTRL_FULL;
+
+ /* Setup the DMA Controllers */
+ writel(&dmadesc[0], NS921X_DMA_BDP(1));
+ writel(NS921X_DMA_STIE_NCIE | NS921X_DMA_STIE_ECIE | \
+ NS921X_DMA_STIE_NRIE | NS921X_DMA_STIE_CAIE, NS921X_DMA_STIE(1));
+
+ ret = request_irq(IRQ_NS921X_EXTDMA, fb_extdma, 0, "fb_extdma", NULL);
+ if (ret) {
+ pr_debug("%s: err_request_irq_extdmairq %d -> %d\n", __func__, IRQ_NS921X_EXTDMA, ret);
+ return ret;
+ }
+#endif
+
+ for (i=0; i < (sizeof(edt_qvga_lcd_init)/3); i++) {
+
+ cc9p9215_lcd_wr_reg(par, edt_qvga_lcd_init[i][0],
+ edt_qvga_lcd_init[i][1]);
+ mdelay(edt_qvga_lcd_init[i][2]);
+ }
+
+ return 0;
+
+err_detect:
+ gpio_free(par->pdata->rst_gpio);
+ gpio_free(par->pdata->enable_gpio);
+
+ return ret;
+}
+
+static void cc9p9215_lcd_cleanup(struct hx8347fb_par *par)
+{
+ gpio_direction_input(par->pdata->rst_gpio);
+ gpio_direction_input(par->pdata->enable_gpio);
+ gpio_free(par->pdata->rst_gpio);
+ gpio_free(par->pdata->enable_gpio);
+}
+
+static struct resource cc9p9215js_lcd_res[] = {
+ {
+ .start = 0x40000000,
+ .end = 0x40000001,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 0x40000002,
+ .end = 0x40000003,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS921X_EXTDMA,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct hx8347fb_pdata c9p9215js_lcd_pdata = {
+ .owner = THIS_MODULE,
+ .reset = cc9p9215_lcd_reset,
+ .bl_enable = cc9p9215_lcd_enable,
+ .init = cc9p9215_lcd_init,
+ .cleanup = cc9p9215_lcd_cleanup,
+ .set_idx = cc9p9215_lcd_set_idx,
+ .wr_reg = cc9p9215_lcd_wr_reg,
+ .wr_data = cc9p9215_lcd_wr_data,
+ .rst_gpio = 86,
+ .enable_gpio = 87,
+ .usedma = 0,
+ .xres = 320,
+ .yres = 240,
+ .bits_per_pixel = 16,
+};
+
+static struct platform_device edt_qvga_lcd = {
+ .name = "hx8347",
+ .id = 0,
+ .dev = {
+ .platform_data = &c9p9215js_lcd_pdata,
+ },
+ .resource = cc9p9215js_lcd_res,
+ .num_resources = ARRAY_SIZE(cc9p9215js_lcd_res),
+};
+
+void __init ns9xxx_add_device_cc9p9215_edt_diplay(void)
+{
+ platform_device_register(&edt_qvga_lcd);
+}
+#else
+void __init ns9xxx_add_device_cc9p9215_edt_diplay(void) {}
+#endif
+
diff --git a/arch/arm/mach-ns9xxx/cc9p9215_devices.h b/arch/arm/mach-ns9xxx/cc9p9215_devices.h
new file mode 100644
index 000000000000..916815b3a484
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/cc9p9215_devices.h
@@ -0,0 +1,45 @@
+/*
+ * arch/arm/mach-ns9xxx/cc9p9215_devices.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+void __init ns9xxx_add_device_cc9p9215_eth(void);
+void __init ns9xxx_add_device_cc9p9215_i2c(void);
+void __init ns9xxx_add_device_cc9p9215_uarta(int gpio_start);
+void __init ns9xxx_add_device_cc9p9215_uartb(int gpio_start);
+void __init ns9xxx_add_device_cc9p9215_uartc(int gpio_start);
+void __init ns9xxx_add_device_cc9p9215_uartd(int gpio_start);
+void __init ns9xxx_add_device_cc9p9215_flash(void);
+void __init ns9xxx_add_device_cc9p9215_spi(void);
+void __init ns9xxx_add_device_cc9p9215_edt_diplay(void);
+
+#define ns9xxx_add_device_cc9p9215_uarta_rxtx() \
+ ns9xxx_add_device_cc9p9215_uarta(2)
+#define ns9xxx_add_device_cc9p9215_uarta_ctsrtsrxtx() \
+ ns9xxx_add_device_cc9p9215_uarta(4)
+#define ns9xxx_add_device_cc9p9215_uarta_full() \
+ ns9xxx_add_device_cc9p9215_uarta(8)
+#define ns9xxx_add_device_cc9p9215_uartb_rxtx() \
+ ns9xxx_add_device_cc9p9215_uartb(2)
+#define ns9xxx_add_device_cc9p9215_uartb_ctsrtsrxtx() \
+ ns9xxx_add_device_cc9p9215_uartb(4)
+#define ns9xxx_add_device_cc9p9215_uartb_full() \
+ ns9xxx_add_device_cc9p9215_uartb(8)
+#define ns9xxx_add_device_cc9p9215_uartc_rxtx() \
+ ns9xxx_add_device_cc9p9215_uartc(2)
+#define ns9xxx_add_device_cc9p9215_uartc_ctsrtsrxtx() \
+ ns9xxx_add_device_cc9p9215_uartc(4)
+#define ns9xxx_add_device_cc9p9215_uartc_full() \
+ ns9xxx_add_device_cc9p9215_uartc(8)
+#define ns9xxx_add_device_cc9p9215_uartd_rxtx() \
+ ns9xxx_add_device_cc9p9215_uartd(2)
+#define ns9xxx_add_device_cc9p9215_uartd_ctsrtsrxtx() \
+ ns9xxx_add_device_cc9p9215_uartd(4)
+#define ns9xxx_add_device_cc9p9215_uartd_full() \
+ ns9xxx_add_device_cc9p9215_uartd(8)
diff --git a/arch/arm/mach-ns9xxx/cc9p9360_devices.c b/arch/arm/mach-ns9xxx/cc9p9360_devices.c
new file mode 100644
index 000000000000..12ebf16818f8
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/cc9p9360_devices.c
@@ -0,0 +1,232 @@
+/*
+ * arch/arm/mach-ns9xxx/cc9p9360_devices.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+
+#include "ns9360_devices.h"
+#include "cc9p9360_devices.h"
+
+
+#if defined(CONFIG_MTD_NAND_CCX9X) || defined(CONFIG_MTD_NAND_CCX9X_MODULE)
+static struct ccx9x_nand_info ns9xxx_device_cc9p9360_nand_data = {
+ .addr_offset = 0x2000,
+ .cmd_offset = 0x4000,
+ .delay = 25,
+ .busy_pin = 49,
+};
+void __init ns9xxx_add_device_cc9p9360_nand(void)
+{
+ ns9xxx_add_device_ns9360_nand(&ns9xxx_device_cc9p9360_nand_data);
+}
+#else
+void __init ns9xxx_add_device_cc9p9360_nand(void) {}
+#endif
+
+#if defined(CONFIG_I2C_NS9XXX) || defined(CONFIG_I2C_NS9XXX_MODULE)
+static void cc9p9360_i2c_gpio_reconfigure(void)
+{
+ gpio_configure_ns9360(70, 0, 0, 2);
+ gpio_configure_ns9360(71, 0, 0, 2);
+}
+
+static struct plat_ns9xxx_i2c ns9xxx_device_cc9p9360_i2c_data = {
+ .gpio_scl = 70,
+ .gpio_sda = 71,
+ .speed = 100000,
+ .gpio_configuration_func = cc9p9360_i2c_gpio_reconfigure,
+};
+
+void __init ns9xxx_add_device_cc9p9360_i2c(void)
+{
+ ns9xxx_add_device_ns9360_i2c(&ns9xxx_device_cc9p9360_i2c_data);
+}
+#else
+void __init ns9xxx_add_device_cc9p9360_i2c(void) {}
+#endif
+
+#if defined(CONFIG_SERIAL_NS9360) || defined(CONFIG_SERIAL_NS9360_MODULE)
+void __init ns9xxx_add_device_cc9p9360_uarta_rxtx(void)
+{
+ int uarta_gpio[] = {8, 9};
+ ns9xxx_add_device_ns9360_uarta(uarta_gpio, ARRAY_SIZE(uarta_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uarta_ctsrtsrxtx(void)
+{
+ int uarta_gpio[] = {8, 9, 10, 11};
+ ns9xxx_add_device_ns9360_uarta(uarta_gpio, ARRAY_SIZE(uarta_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uarta_full(void)
+{
+ int uarta_gpio[] = {8, 9, 10, 11, 12, 13, 14, 15};
+ ns9xxx_add_device_ns9360_uarta(uarta_gpio, ARRAY_SIZE(uarta_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uartb_rxtx(void)
+{
+ int uartb_gpio[] = {0, 1};
+ ns9xxx_add_device_ns9360_uartb(uartb_gpio, ARRAY_SIZE(uartb_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uartb_ctsrtsrxtx(void)
+{
+ int uartb_gpio[] = {0, 1, 2, 3};
+ ns9xxx_add_device_ns9360_uartb(uartb_gpio, ARRAY_SIZE(uartb_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uartb_full(void)
+{
+ int uartb_gpio[] = {0, 1, 2, 3, 4, 5, 6, 7};
+ ns9xxx_add_device_ns9360_uartb(uartb_gpio, ARRAY_SIZE(uartb_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uartc_rxtx(void)
+{
+ int uartc_gpio[] = {40, 41};
+ ns9xxx_add_device_ns9360_uartc(uartc_gpio, ARRAY_SIZE(uartc_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uartc_ctsrtsrxtx(void)
+{
+ int uartc_gpio[] = {40, 41, 42, 43};
+ ns9xxx_add_device_ns9360_uartc(uartc_gpio, ARRAY_SIZE(uartc_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uartc_full(void)
+{
+ int uartc_gpio[] = {40, 41, 42, 43, 20, 21, 22, 23};
+ ns9xxx_add_device_ns9360_uartc(uartc_gpio, ARRAY_SIZE(uartc_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uartd_rxtx(void)
+{
+ int uartd_gpio[] = {44, 45};
+ ns9xxx_add_device_ns9360_uartd(uartd_gpio, ARRAY_SIZE(uartd_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uartd_ctsrtsrxtx(void)
+{
+ int uartd_gpio[] = {44, 45, 46, 47};
+ ns9xxx_add_device_ns9360_uartd(uartd_gpio, ARRAY_SIZE(uartd_gpio), 0);
+}
+
+void __init ns9xxx_add_device_cc9p9360_uartd_full(void)
+{
+ int uartd_gpio[] = {44, 45, 46, 47, 24, 25, 26, 27};
+ ns9xxx_add_device_ns9360_uartd(uartd_gpio, ARRAY_SIZE(uartd_gpio), 0);
+}
+#else
+void __init ns9xxx_add_device_cc9p9360_uarta_rxtx(void) {}
+void __init ns9xxx_add_device_cc9p9360_uarta_ctsrtsrxtx(void) {}
+void __init ns9xxx_add_device_cc9p9360_uarta_full(void) {}
+void __init ns9xxx_add_device_cc9p9360_uartb_rxtx(void) {}
+void __init ns9xxx_add_device_cc9p9360_uartb_ctsrtsrxtx(void) {}
+void __init ns9xxx_add_device_cc9p9360_uartb_full(void) {}
+void __init ns9xxx_add_device_cc9p9360_uartc_rxtx(void) {}
+void __init ns9xxx_add_device_cc9p9360_uartc_ctsrtsrxtx(void) {}
+void __init ns9xxx_add_device_cc9p9360_uartc_full(void) {}
+void __init ns9xxx_add_device_cc9p9360_uartd_rxtx(void) {}
+void __init ns9xxx_add_device_cc9p9360_uartd_ctsrtsrxtx(void) {}
+void __init ns9xxx_add_device_cc9p9360_uartd_full(void) {}
+#endif
+
+#if defined(CONFIG_FB_NS9360) || defined(CONFIG_FB_NS9360_MODULE)
+void __init ns9xxx_add_device_cc9p9360_fb(int power)
+{
+ int gpio[] = {19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41};
+ int func[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
+
+ ns9xxx_add_device_ns9360_fb(gpio, ARRAY_SIZE(gpio), func, power);
+}
+#else
+void __init ns9xxx_add_device_cc9p9360_fb(int power) {}
+#endif
+
+#if defined(CONFIG_SPI_NS9360) || defined(CONFIG_SPI_NS9360_MODULE)
+/* SPI ports and their related GPIOs */
+static struct spi_ns9xxx_data ns9xxx_device_cc9p9360_spi_porta_data = {
+ .gpios = {8, 9, 14, 15},
+ .gpio_funcs = { NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0 },
+ .nr_gpios = 4,
+};
+
+static struct spi_ns9xxx_data ns9xxx_device_cc9p9360_spi_portb_data = {
+ .gpios = {0, 1, 6, 7},
+ .gpio_funcs = { NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0 },
+ .nr_gpios = 4,
+};
+
+static struct spi_ns9xxx_data ns9xxx_device_cc9p9360_spi_portc_data = {
+ .gpios = {40, 41, 22, 23},
+ .gpio_funcs = { NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0 },
+ .nr_gpios = 4,
+};
+
+static struct spi_ns9xxx_data ns9xxx_device_cc9p9360_spi_portd_data = {
+ .gpios = {44, 45, 26, 27},
+ .gpio_funcs = { NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0,
+ NS9360_GPIO_FUNC_0 },
+ .nr_gpios = 4,
+};
+
+void __init ns9xxx_add_device_cc9p9360_spi_porta(void)
+{
+ ns9xxx_add_device_ns9360_spi_porta( &ns9xxx_device_cc9p9360_spi_porta_data );
+}
+
+void __init ns9xxx_add_device_cc9p9360_spi_portb(void)
+{
+ ns9xxx_add_device_ns9360_spi_portb( &ns9xxx_device_cc9p9360_spi_portb_data );
+}
+
+void __init ns9xxx_add_device_cc9p9360_spi_portc(void)
+{
+ ns9xxx_add_device_ns9360_spi_portc( &ns9xxx_device_cc9p9360_spi_portc_data );
+}
+
+void __init ns9xxx_add_device_cc9p9360_spi_portd(void)
+{
+ ns9xxx_add_device_ns9360_spi_portd( &ns9xxx_device_cc9p9360_spi_portd_data );
+}
+
+#else
+void __init ns9xxx_add_device_cc9p9360_spi_porta(void) {}
+void __init ns9xxx_add_device_cc9p9360_spi_portb(void) {}
+void __init ns9xxx_add_device_cc9p9360_spi_portc(void) {}
+void __init ns9xxx_add_device_cc9p9360_spi_portd(void) {}
+#endif
+
+#if defined(CONFIG_NS9XXX_ETH) || defined(CONFIG_NS9XXX_ETH_MODULE)
+void __init ns9xxx_add_device_cc9p9360_eth(void)
+{
+ int gpio[] = {50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65};
+
+ ns9xxx_add_device_ns9360_eth(gpio, 16, 0, 0xfffffffd);
+}
+#else
+void __init ns9xxx_add_device_cc9p9360_eth(void) {}
+#endif
diff --git a/arch/arm/mach-ns9xxx/cc9p9360_devices.h b/arch/arm/mach-ns9xxx/cc9p9360_devices.h
new file mode 100644
index 000000000000..2c87238e2f4b
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/cc9p9360_devices.h
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-ns9xxx/cc9p9360_devices.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+void __init ns9xxx_add_device_cc9p9360_nand(void);
+void __init ns9xxx_add_device_cc9p9360_i2c(void);
+void __init ns9xxx_add_device_cc9p9360_uarta_rxtx(void);
+void __init ns9xxx_add_device_cc9p9360_uartb_rxtx(void);
+void __init ns9xxx_add_device_cc9p9360_uartc_rxtx(void);
+void __init ns9xxx_add_device_cc9p9360_uartd_rxtx(void);
+void __init ns9xxx_add_device_cc9p9360_uarta_ctsrtsrxtx(void);
+void __init ns9xxx_add_device_cc9p9360_uartb_ctsrtsrxtx(void);
+void __init ns9xxx_add_device_cc9p9360_uartc_ctsrtsrxtx(void);
+void __init ns9xxx_add_device_cc9p9360_uartd_ctsrtsrxtx(void);
+void __init ns9xxx_add_device_cc9p9360_uarta_full(void);
+void __init ns9xxx_add_device_cc9p9360_uartb_full(void);
+void __init ns9xxx_add_device_cc9p9360_uartc_full(void);
+void __init ns9xxx_add_device_cc9p9360_uartd_full(void);
+void __init ns9xxx_add_device_cc9p9360_fb(int power);
+void __init ns9xxx_add_device_cc9p9360_spi_porta(void);
+void __init ns9xxx_add_device_cc9p9360_spi_portb(void);
+void __init ns9xxx_add_device_cc9p9360_spi_portc(void);
+void __init ns9xxx_add_device_cc9p9360_spi_portd(void);
+void __init ns9xxx_add_device_cc9p9360_eth(void);
+void __init ns9xxx_add_device_cc9p9360_touch(void);
diff --git a/arch/arm/mach-ns9xxx/ccw9p9215_devices.c b/arch/arm/mach-ns9xxx/ccw9p9215_devices.c
new file mode 100644
index 000000000000..d5630571d59d
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/ccw9p9215_devices.c
@@ -0,0 +1,379 @@
+/*
+ * arch/arm/mach-ns9xxx/cc9p9215_devices.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/mtd/physmap.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <mach/fim-ns921x.h>
+#include <mach/hardware.h>
+
+#include <mach/regs-sys-common.h>
+#include <mach/regs-sys-ns921x.h>
+#include <mach/regs-mem.h>
+
+#include <asm/leds.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airoha.h"
+
+/*
+ * Pick Digi's internal FIM board
+ * Use internal board, defined to 1
+ * Use newer boards, defined to 0
+ */
+#if defined(CONFIG_DIGI_PIPER_WIFI)
+
+/* Low level functions to access piper chip */
+static u32 read_reg(struct piper_priv *piperp, u8 reg)
+{
+ return ioread32(piperp->vbase + reg);
+}
+
+static int write_reg(struct piper_priv *piperp, u8 reg, u32 val, reg_op_t op)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->ac->reg_lock, flags);
+ switch (op) {
+ case op_write:
+ iowrite32(val, piperp->vbase + reg);
+ break;
+ case op_or:
+ iowrite32(val | ioread32(piperp->vbase + reg), piperp->vbase + reg);
+ break;
+ case op_and:
+ iowrite32(val & ioread32(piperp->vbase + reg), piperp->vbase + reg);
+ break;
+ default:
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": Invalid write register operation (%d)\n", op);
+ WARN_ON(1);
+ break;
+ }
+ spin_unlock_irqrestore(&piperp->ac->reg_lock, flags);
+
+ return 0;
+}
+
+/*
+ * This macro waits for the AES busy bit to clear if we are writing to the
+ * AES FIFO.
+ */
+#define wait_for_aes_ready() while ((addr == BB_AES_FIFO) \
+ && ((ioread32(piperp->vbase + BB_RSSI) \
+ & BB_RSSI_EAS_FIFO_FULL) != 0)) { \
+ udelay(1); \
+ }
+
+static int write_fifo(struct piper_priv *piperp, u8 addr, u8 *buf, int len)
+{
+ int wordIndex;
+ int wordLength = len / sizeof(unsigned int);
+ unsigned long flags;
+ bool loadingBeacon = (addr == BEACON_FIFO);
+
+ spin_lock_irqsave(&piperp->ac->reg_lock, flags);
+
+ if (loadingBeacon) {
+ /*
+ * If we are loading a new beacon, then adjust the address to point
+ * to the data FIFO, and set the beacon enable bit which tells piper
+ * to put this data into the beacon buffer.
+ */
+ addr = BB_DATA_FIFO;
+ iowrite32(ioread32(piperp->vbase + BB_GENERAL_CTL) | BB_GENERAL_CTL_BEACON_EN,
+ piperp->vbase + BB_GENERAL_CTL);
+ }
+
+ if (((unsigned)(buf) & 0x3) == 0) {
+ /*
+ * We come here if the data is 32-bit aligned. We can dispense
+ * with memcpys
+ */
+ if (wordLength == 1) {
+ /*
+ * Only 1 word of data, so just one write.
+ */
+ unsigned int *word = (unsigned int *)buf;
+
+ wait_for_aes_ready();
+ iowrite32(cpu_to_be32(*word), piperp->vbase + addr);
+ len -= 4;
+ } else {
+ /*
+ * More than one word of data, so set up a for loop.
+ */
+ for (wordIndex = 0; wordIndex < wordLength; wordIndex++) {
+ unsigned int *word = (unsigned int *)buf;
+
+ wait_for_aes_ready();
+ iowrite32(cpu_to_be32(word[wordIndex]), piperp->vbase + addr);
+ len -= 4;
+ }
+ }
+ } else {
+ /*
+ * Ugh! Data is not 32-bit aligned. We have to memcpy it!
+ */
+ for (wordIndex = 0; wordIndex < wordLength; wordIndex++) {
+ unsigned int word;
+
+ memcpy(&word, &buf[wordIndex * sizeof(unsigned int)],
+ sizeof(unsigned int));
+
+ wait_for_aes_ready();
+ iowrite32(cpu_to_be32(word), piperp->vbase + addr);
+ len -= 4;
+ }
+ }
+
+ if (len) {
+ /*
+ * Double Ugh! There was left over data at the end. We have to write
+ * the leftover data into the upper bytes of the last word, making
+ * sure the unused bytes are set to zero.
+ */
+ unsigned int word;
+
+ memcpy(&word, &buf[wordLength * sizeof(unsigned int)], sizeof(unsigned int));
+ word = cpu_to_be32(word);
+ switch (len) {
+ case 1:
+ word &= 0xff000000;
+ break;
+ case 2:
+ word &= 0xffff0000;
+ break;
+ case 3:
+ word &= 0xffffff00;
+ break;
+ default:
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": len = %d at end of piper_write\n", len);
+ break;
+ }
+ wait_for_aes_ready();
+ iowrite32(word, piperp->vbase + addr);
+ }
+
+ if (loadingBeacon) {
+ /*
+ * If we just loaded a beacon, then don't forget to turn off the
+ * load beacon bit.
+ */
+ iowrite32(ioread32(piperp->vbase + BB_GENERAL_CTL) & ~BB_GENERAL_CTL_BEACON_EN,
+ piperp->vbase + BB_GENERAL_CTL);
+ piperp->beacon.loaded = true;
+ }
+
+ spin_unlock_irqrestore(&piperp->ac->reg_lock, flags);
+
+ return 0;
+}
+
+/*
+ * This routine waits for the empty flag to clear when we are reading from
+ * the AES FIFO.
+ */
+#define wait_for_aes_not_empty() \
+ while ((addr == BB_AES_FIFO) \
+ && ((ioread32(piperp->vbase + BB_RSSI) & BB_RSSI_EAS_FIFO_EMPTY) != 0)) { \
+ udelay(1); \
+ if (--timeout == 0) { \
+ timeout = 10000; \
+ } \
+ }
+
+static int read_fifo(struct piper_priv *piperp, u8 addr, u8 *buf, int len)
+{
+ int wordIndex;
+ unsigned long flags;
+ int timeout = 10000;
+
+ spin_lock_irqsave(&piperp->ac->reg_lock, flags);
+
+ /*
+ * We can only read 32-bit words, so round the length up to an even multiple of
+ * 4 if necessary.
+ */
+ len += 3;
+ len &= 0xfffffc;
+
+ if ((len == 4) && ((((unsigned)buf) & 0x3) == 0)) {
+ unsigned *word = (unsigned *)buf;
+
+ wait_for_aes_not_empty();
+ *word = be32_to_cpu(ioread32(piperp->vbase + addr));
+ } else if ((((unsigned)buf) & 0x3) == 0) {
+ unsigned *word = (unsigned *)buf;
+
+ for (wordIndex = 0; wordIndex < (len / sizeof(unsigned)); wordIndex++) {
+ wait_for_aes_not_empty();
+ word[wordIndex] = be32_to_cpu(ioread32(piperp->vbase + addr));
+ }
+ } else {
+ /*
+ * If we come here, then the buffer is not aligned and we have to
+ * memcpy the data.
+ */
+ for (wordIndex = 0; wordIndex < (len / sizeof(unsigned)); wordIndex++) {
+ unsigned word;
+
+ wait_for_aes_not_empty();
+ word = be32_to_cpu(ioread32(piperp->vbase + addr));
+ memcpy(&buf[wordIndex * sizeof(unsigned)], &word, sizeof(word));
+ }
+ }
+ spin_unlock_irqrestore(&piperp->ac->reg_lock, flags);
+
+ return 0;
+}
+
+/* Initialize piper hardware, mac and dsp firmwares and mac address */
+static int piper_init_chip_hw(struct piper_priv *piperp)
+{
+ int ret;
+
+ piper_load_mac_firmware(piperp);
+ piper_load_dsp_firmware(piperp);
+
+ ret = piper_spike_suppression(piperp, true);
+ if (ret) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": spike suppresion error\n");
+ return ret;
+ }
+ piper_reset_mac(piperp);
+ piper_set_macaddr(piperp);
+
+ return ret;
+}
+
+static void ccw9p9215_piper_set_led(struct piper_priv *piperp, enum wireless_led led, int val)
+{
+ if(led == STATUS_LED)
+ leds_event(val ? led_green_on : led_green_off);
+}
+
+static void ccw9p9215_piper_reset(struct piper_priv *piperp, int reset)
+{
+ gpio_set_value(piperp->pdata->rst_gpio, !reset);
+}
+
+static int ccw9p9215_piper_init(struct piper_priv *piperp)
+{
+ ccw9p9215_piper_reset(piperp, 1);
+ mdelay(1);
+ ccw9p9215_piper_reset(piperp, 0);
+ mdelay(1);
+
+ /* Initialize functions to access register */
+ piperp->ac->wr_reg = write_reg;
+ piperp->ac->rd_reg = read_reg;
+ piperp->ac->wr_fifo = write_fifo;
+ piperp->ac->rd_fifo = read_fifo;
+
+ mdelay(1);
+
+ return piper_init_chip_hw(piperp);
+}
+
+static int ccw9p9215_piper_late_init(struct piper_priv *piperp)
+{
+ /* Configure irq gpio line */
+ gpio_configure_ns921x(piperp->pdata->irq_gpio, NS921X_GPIO_INPUT,
+ NS921X_GPIO_DONT_INVERT, NS921X_GPIO_FUNC_2,
+ NS921X_GPIO_ENABLE_PULLUP);
+
+ return 0;
+}
+
+static struct resource piper_resources[] = {
+ {
+ .start = 0x70000000,
+ .end = 0x70000000 + 0x100,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS9XXX_EXT0,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+/* describes the device */
+static struct platform_device piper_device = {
+ .id = 0,
+ .name = PIPER_DRIVER_NAME,
+ .num_resources = ARRAY_SIZE(piper_resources),
+ .resource = piper_resources,
+};
+
+void __init ns9xxx_add_device_ccw9p9215_wifi(struct piper_pdata *pdata)
+{
+ int ret;
+
+ if (!pdata)
+ return;
+
+ if (pdata->rst_gpio >= 0) {
+ ret = gpio_request(pdata->rst_gpio, PIPER_DRIVER_NAME "-reset-gpio");
+ if (ret != 0)
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": failed to request reset gpio %d\n", pdata->rst_gpio);
+ else {
+ /* Configure reset line and hold the chip in reset */
+ gpio_direction_output(pdata->rst_gpio, 0);
+ pdata->reset = ccw9p9215_piper_reset;
+ }
+ }
+
+ if (pdata->irq_gpio >= 0) {
+ ret = gpio_request(pdata->irq_gpio, PIPER_DRIVER_NAME "-irq-gpio");
+ if (ret != 0)
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": failed to request irq gpio %d\n", pdata->irq_gpio);
+ }
+
+ /* Configure the memory controller (CS3) with the appropriate settings */
+ /* 32 bit bus width */
+ writel(MEM_SMC_PB_1 | MEM_SMC_MW_32, MEM_SMC(3));
+ /* Static Memory Write Enable Delay x */
+ writel(0, MEM_SMWED(3));
+ /* Static Memory Output Enable Delay x */
+ writel(2, MEM_SMOED(3));
+ /* Static Memory Read Delay x */
+ writel(8, MEM_SMRD(3));
+ /* Static Memory Page Mode Read Delay 0 */
+ writel(0, MEM_SMPMRD(3));
+ /* Static Memory Write Delay */
+ writel(4, MEM_SMWD(3));
+ /* Static Memory Turn Round Delay x */
+ writel(2, MEM_SWT(3));
+ /* Enable the CS0 access */
+ writel(readl(SYS_SMCSSMM(3)) | SYS_SMCSSMM_CSEx_EN, SYS_SMCSSMM(3));
+
+ pdata->rf_transceiver = RF_AIROHA_7230;
+ pdata->init = ccw9p9215_piper_init;
+ pdata->late_init = ccw9p9215_piper_late_init;
+ pdata->set_led = ccw9p9215_piper_set_led;
+ piper_device.dev.platform_data = pdata;
+
+ platform_device_register(&piper_device);
+}
+
+#else
+void __init ns9xxx_add_device_ccw9p9215_wifi(struct piper_pdata *pdata) {}
+#endif
+
diff --git a/arch/arm/mach-ns9xxx/ccw9p9215_devices.h b/arch/arm/mach-ns9xxx/ccw9p9215_devices.h
new file mode 100644
index 000000000000..3e27884cd0d8
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/ccw9p9215_devices.h
@@ -0,0 +1,12 @@
+/*
+ * arch/arm/mach-ns9xxx/ccw9p9215_devices.h
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+void __init ns9xxx_add_device_ccw9p9215_wifi(struct piper_pdata *pdata);
diff --git a/arch/arm/mach-ns9xxx/ccx9c_devices.c b/arch/arm/mach-ns9xxx/ccx9c_devices.c
new file mode 100644
index 000000000000..6ec20a4971f0
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/ccx9c_devices.c
@@ -0,0 +1,218 @@
+/*
+ * arch/arm/mach-ns9xxx/ccx9c_devices.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+
+#include "ns9360_devices.h"
+#include "ccx9c_devices.h"
+
+#if defined(CONFIG_SPI_NS9360) || defined(CONFIG_SPI_NS9360_MODULE)
+/* SPI ports and their related GPIOs */
+static struct spi_ns9xxx_data ns9xxx_device_ccx9c_spi_porta_data = {
+ .gpios = {8, 9, 14, 15},
+ .nr_gpios = 4,
+};
+
+static struct spi_ns9xxx_data ns9xxx_device_ccx9c_spi_portb_data = {
+ .gpios = {0, 1, 6, 7},
+ .nr_gpios = 4,
+};
+
+static struct spi_ns9xxx_data ns9xxx_device_ccx9c_spi_portc_data = {
+ .gpios = {40, 41, 22, 23},
+ .nr_gpios = 4,
+};
+
+static struct spi_ns9xxx_data ns9xxx_device_ccx9c_spi_portd_data = {
+ .gpios = {44, 45, 26, 27},
+ .nr_gpios = 4,
+};
+#endif
+
+#if defined(CONFIG_MTD_NAND_CCX9X) || defined(CONFIG_MTD_NAND_CCX9X_MODULE)
+static struct ccx9x_nand_info ns9xxx_device_ccx9c_nand_data = {
+ .addr_offset = 0x8000,
+ .cmd_offset = 0x10000,
+ .delay = 25,
+};
+void __init ns9xxx_add_device_ccx9c_nand(void)
+{
+ ns9xxx_add_device_ns9360_nand(&ns9xxx_device_ccx9c_nand_data);
+}
+#else
+void __init ns9xxx_add_device_ccx9c_nand(void) {}
+#endif
+
+#if defined(CONFIG_I2C_NS9XXX) || defined(CONFIG_I2C_NS9XXX_MODULE)
+static void ccx9c_i2c_gpio_reconfigure(void)
+{
+ gpio_configure_ns9360(70, 0, 0, 2);
+ gpio_configure_ns9360(71, 0, 0, 2);
+}
+
+static struct plat_ns9xxx_i2c ns9xxx_device_ccx9c_i2c_data = {
+ .gpio_scl = 70,
+ .gpio_sda = 71,
+ .speed = 100000,
+ .gpio_configuration_func = ccx9c_i2c_gpio_reconfigure,
+};
+
+void __init ns9xxx_add_device_ccx9c_i2c(void)
+{
+ ns9xxx_add_device_ns9360_i2c(&ns9xxx_device_ccx9c_i2c_data);
+}
+#else
+void __init ns9xxx_add_device_ccx9c_i2c(void) {}
+#endif
+
+#if defined(CONFIG_SERIAL_NS9360) || defined(CONFIG_SERIAL_NS9360_MODULE)
+void __init ns9xxx_add_device_ccx9c_uarta_rxtx(void)
+{
+ int uarta_gpio[] = {8, 9};
+ ns9xxx_add_device_ns9360_uarta(uarta_gpio, ARRAY_SIZE(uarta_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uarta_ctsrtsrxtx(void)
+{
+ int uarta_gpio[] = {8, 9, 10, 11};
+ ns9xxx_add_device_ns9360_uarta(uarta_gpio, ARRAY_SIZE(uarta_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uarta_full(void)
+{
+ int uarta_gpio[] = { 8, 9, 10, 11, 12, 13, 14, 15};
+ ns9xxx_add_device_ns9360_uarta(uarta_gpio, ARRAY_SIZE(uarta_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uartb_rxtx(void)
+{
+ int uartb_gpio[] = {0, 1};
+ ns9xxx_add_device_ns9360_uartb(uartb_gpio, ARRAY_SIZE(uartb_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uartb_ctsrtsrxtx(void)
+{
+ int uartb_gpio[] = {0, 1, 2, 3};
+ ns9xxx_add_device_ns9360_uartb(uartb_gpio, ARRAY_SIZE(uartb_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uartb_full(void)
+{
+ int uartb_gpio[] = { 0, 1, 2, 3, 4, 5, 6, 7};
+ ns9xxx_add_device_ns9360_uartb(uartb_gpio, ARRAY_SIZE(uartb_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uartc_rxtx(void)
+{
+ int uartc_gpio[] = {40, 41};
+ ns9xxx_add_device_ns9360_uartc(uartc_gpio, ARRAY_SIZE(uartc_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uartc_ctsrtsrxtx(void)
+{
+ int uartc_gpio[] = {40, 41, 42, 43};
+ ns9xxx_add_device_ns9360_uartc(uartc_gpio, ARRAY_SIZE(uartc_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uartd_rxtx(void)
+{
+ int uartd_gpio[] = {44, 45};
+ ns9xxx_add_device_ns9360_uartd(uartd_gpio, ARRAY_SIZE(uartd_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uartd_ctsrtsrxtx(void)
+{
+ int uartd_gpio[] = {44, 45, 46, 47};
+ ns9xxx_add_device_ns9360_uartd(uartd_gpio, ARRAY_SIZE(uartd_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uartc_full(void)
+{
+ int uartc_gpio[] = {40, 41, 42, 43, 20, 21, 22, 23};
+ ns9xxx_add_device_ns9360_uartc(uartc_gpio, ARRAY_SIZE(uartc_gpio), 0);
+}
+
+void __init ns9xxx_add_device_ccx9c_uartd_full(void)
+{
+ int uartd_gpio[] = {44, 45, 46, 47, 24, 25, 26, 27};
+ ns9xxx_add_device_ns9360_uartd(uartd_gpio, ARRAY_SIZE(uartd_gpio), 0);
+}
+#else
+void __init ns9xxx_add_device_ccx9c_uarta_rxtx(void) {}
+void __init ns9xxx_add_device_ccx9c_uarta_ctsrtsrxtx(void) {}
+void __init ns9xxx_add_device_ccx9c_uarta_full(void) {}
+void __init ns9xxx_add_device_ccx9c_uartb_rxtx(void) {}
+void __init ns9xxx_add_device_ccx9c_uartb_ctsrtsrxtx(void) {}
+void __init ns9xxx_add_device_ccx9c_uartb_full(void) {}
+void __init ns9xxx_add_device_ccx9c_uartc_rxtx(void) {}
+void __init ns9xxx_add_device_ccx9c_uartc_ctsrtsrxtx(void) {}
+void __init ns9xxx_add_device_ccx9c_uartc_full(void) {}
+void __init ns9xxx_add_device_ccx9c_uartd_rxtx(void) {}
+void __init ns9xxx_add_device_ccx9c_uartd_ctsrtsrxtx(void) {}
+void __init ns9xxx_add_device_ccx9c_uartd_full(void) {}
+#endif
+
+#if defined(CONFIG_FB_NS9360) || defined(CONFIG_FB_NS9360_MODULE)
+void __init ns9xxx_add_device_ccx9c_fb(int power)
+{
+ int gpio[] = {19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41};
+ int func[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
+
+ ns9xxx_add_device_ns9360_fb(gpio, ARRAY_SIZE(gpio), func, power);
+}
+#else
+void __init ns9xxx_add_device_ccx9c_fb(int power) {}
+#endif
+
+#if defined(CONFIG_SPI_NS9360) || defined(CONFIG_SPI_NS9360_MODULE)
+void __init ns9xxx_add_device_ccx9c_spi_porta(void)
+{
+ ns9xxx_add_device_ns9360_spi_porta( &ns9xxx_device_ccx9c_spi_porta_data );
+}
+
+void __init ns9xxx_add_device_ccx9c_spi_portb(void)
+{
+ ns9xxx_add_device_ns9360_spi_portb( &ns9xxx_device_ccx9c_spi_portb_data );
+}
+
+void __init ns9xxx_add_device_ccx9c_spi_portc(void)
+{
+ ns9xxx_add_device_ns9360_spi_portc( &ns9xxx_device_ccx9c_spi_portc_data );
+}
+
+void __init ns9xxx_add_device_ccx9c_spi_portd(void)
+{
+ ns9xxx_add_device_ns9360_spi_portd( &ns9xxx_device_ccx9c_spi_portd_data );
+}
+
+#else
+void __init ns9xxx_add_device_ccx9c_spi_porta(void) {}
+void __init ns9xxx_add_device_ccx9c_spi_portb(void) {}
+void __init ns9xxx_add_device_ccx9c_spi_portc(void) {}
+void __init ns9xxx_add_device_ccx9c_spi_portd(void) {}
+#endif
+
+#if defined(CONFIG_NS9XXX_ETH) || defined(CONFIG_NS9XXX_ETH_MODULE)
+void __init ns9xxx_add_device_ccx9c_eth(void)
+{
+ int gpio[] = {50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64};
+
+ ns9xxx_add_device_ns9360_eth(gpio, 15, 0, 0xfffffffd);
+}
+#else
+void __init ns9xxx_add_device_ccx9c_eth(void) {}
+#endif
diff --git a/arch/arm/mach-ns9xxx/ccx9c_devices.h b/arch/arm/mach-ns9xxx/ccx9c_devices.h
new file mode 100644
index 000000000000..074197e29aba
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/ccx9c_devices.h
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-ns9xxx/ccx9c_devices.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+void __init ns9xxx_add_device_ccx9c_nand(void);
+void __init ns9xxx_add_device_ccx9c_i2c(void);
+void __init ns9xxx_add_device_ccx9c_uarta_rxtx(void);
+void __init ns9xxx_add_device_ccx9c_uartb_rxtx(void);
+void __init ns9xxx_add_device_ccx9c_uartc_rxtx(void);
+void __init ns9xxx_add_device_ccx9c_uartd_rxtx(void);
+void __init ns9xxx_add_device_ccx9c_uarta_ctsrtsrxtx(void);
+void __init ns9xxx_add_device_ccx9c_uartb_ctsrtsrxtx(void);
+void __init ns9xxx_add_device_ccx9c_uartc_ctsrtsrxtx(void);
+void __init ns9xxx_add_device_ccx9c_uartd_ctsrtsrxtx(void);
+void __init ns9xxx_add_device_ccx9c_uarta_full(void);
+void __init ns9xxx_add_device_ccx9c_uartb_full(void);
+void __init ns9xxx_add_device_ccx9c_uartc_full(void);
+void __init ns9xxx_add_device_ccx9c_uartd_full(void);
+void __init ns9xxx_add_device_ccx9c_fb(int power);
+void __init ns9xxx_add_device_ccx9c_spi_porta(void);
+void __init ns9xxx_add_device_ccx9c_spi_portb(void);
+void __init ns9xxx_add_device_ccx9c_spi_portc(void);
+void __init ns9xxx_add_device_ccx9c_spi_portd(void);
+void __init ns9xxx_add_device_ccx9c_eth(void);
+void __init ns9xxx_add_device_ccx9c_touch(void);
diff --git a/arch/arm/mach-ns9xxx/clock.c b/arch/arm/mach-ns9xxx/clock.c
index 44ed20d4a388..0344a519707b 100644
--- a/arch/arm/mach-ns9xxx/clock.c
+++ b/arch/arm/mach-ns9xxx/clock.c
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-ns9xxx/clock.c
*
- * Copyright (C) 2007 by Digi International Inc.
+ * Copyright (C) 2007-2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -61,22 +61,77 @@ EXPORT_SYMBOL(clk_get);
void clk_put(struct clk *clk)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clk_lock, flags);
+
module_put(clk->owner);
--clk->refcount;
+
+ spin_unlock_irqrestore(&clk_lock, flags);
}
EXPORT_SYMBOL(clk_put);
-static int clk_enable_unlocked(struct clk *clk)
+static int clk_enable_haslock(struct clk *clk);
+static int clk_disable_haslock(struct clk *clk);
+
+static void clk_disable_parent_haslock(struct clk *clk)
{
+ struct clk *parent = clk->parent;
int ret = 0;
- if (clk->parent) {
- ret = clk_enable_unlocked(clk->parent);
- if (ret)
- return ret;
+
+ if (parent)
+ ret = clk_disable_haslock(parent);
+
+ if (unlikely(ret))
+ pr_warning("failed to disable %s.%d clk -> %d\n",
+ parent->name, parent->id, ret);
+}
+
+static int clk_enable_haslocknchange(struct clk *clk)
+{
+ int ret = 0;
+
+ assert_spin_locked(&clk_lock);
+ BUG_ON(!test_bit(CLK_FLAG_CHANGESTATE, &clk->flags));
+
+ if (clk->usage++ == 0) {
+ if (clk->parent) {
+ ret = clk_enable_haslock(clk->parent);
+ if (ret)
+ goto err_enable_parent;
+ }
+
+ spin_unlock(&clk_lock);
+
+ if (clk->endisable)
+ ret = clk->endisable(clk, 1);
+
+ spin_lock(&clk_lock);
+
+ if (ret) {
+ clk_disable_parent_haslock(clk);
+err_enable_parent:
+
+ clk->usage = 0;
+ }
+
}
- if (clk->usage++ == 0 && clk->endisable)
- ret = clk->endisable(clk, 1);
+ return ret;
+}
+
+static int clk_enable_haslock(struct clk *clk)
+{
+ int ret;
+
+ assert_spin_locked(&clk_lock);
+ if (__test_and_set_bit(CLK_FLAG_CHANGESTATE, &clk->flags))
+ return -EBUSY;
+
+ ret = clk_enable_haslocknchange(clk);
+
+ clear_bit(CLK_FLAG_CHANGESTATE, &clk->flags);
return ret;
}
@@ -88,7 +143,7 @@ int clk_enable(struct clk *clk)
spin_lock_irqsave(&clk_lock, flags);
- ret = clk_enable_unlocked(clk);
+ ret = clk_enable_haslock(clk);
spin_unlock_irqrestore(&clk_lock, flags);
@@ -96,24 +151,60 @@ int clk_enable(struct clk *clk)
}
EXPORT_SYMBOL(clk_enable);
-static void clk_disable_unlocked(struct clk *clk)
+static int clk_disable_haslocknchange(struct clk *clk)
{
- if (--clk->usage == 0 && clk->endisable)
- clk->endisable(clk, 0);
+ int ret = 0;
- if (clk->parent)
- clk_disable_unlocked(clk->parent);
+ assert_spin_locked(&clk_lock);
+ BUG_ON(!test_bit(CLK_FLAG_CHANGESTATE, &clk->flags));
+
+ BUG_ON(clk->usage == 0);
+
+ if (--clk->usage == 0) {
+ spin_unlock(&clk_lock);
+
+ if (clk->endisable)
+ ret = clk->endisable(clk, 0);
+
+ spin_lock(&clk_lock);
+
+ if (ret == 0)
+ clk_disable_parent_haslock(clk);
+ else
+ clk->usage = 1;
+ }
+
+ return ret;
+}
+
+static int clk_disable_haslock(struct clk *clk)
+{
+ int ret;
+
+ if (__test_and_set_bit(CLK_FLAG_CHANGESTATE, &clk->flags))
+ return -EBUSY;
+
+ ret = clk_disable_haslocknchange(clk);
+
+ clear_bit(CLK_FLAG_CHANGESTATE, &clk->flags);
+
+ return ret;
}
void clk_disable(struct clk *clk)
{
+ int ret;
unsigned long flags;
spin_lock_irqsave(&clk_lock, flags);
- clk_disable_unlocked(clk);
+ ret = clk_disable_haslock(clk);
spin_unlock_irqrestore(&clk_lock, flags);
+
+ if (unlikely(ret))
+ pr_warning("failed to disable %s.%d clk -> %d\n",
+ clk->name, clk->id, ret);
}
EXPORT_SYMBOL(clk_disable);
@@ -138,6 +229,8 @@ int clk_register(struct clk *clk)
spin_lock_irqsave(&clk_lock, flags);
+ BUG_ON(clk->flags);
+
list_add(&clk->node, &clocks);
if (clk->parent)
@@ -181,9 +274,10 @@ static int clk_debugfs_show(struct seq_file *s, void *null)
spin_lock_irqsave(&clk_lock, flags);
list_for_each_entry(p, &clocks, node)
- seq_printf(s, "%s.%d: usage=%lu refcount=%lu rate=%lu\n",
+ seq_printf(s, "%s.%d: usage=%lu refcount=%lu rate=%lu parent=%s\n",
p->name, p->id, p->usage, p->refcount,
- p->usage ? clk_get_rate(p) : 0);
+ p->usage ? clk_get_rate(p) : 0,
+ p->parent ? p->parent->name : "<nil>");
spin_unlock_irqrestore(&clk_lock, flags);
diff --git a/arch/arm/mach-ns9xxx/clock.h b/arch/arm/mach-ns9xxx/clock.h
index b86c30dd79eb..9097fe971dfc 100644
--- a/arch/arm/mach-ns9xxx/clock.h
+++ b/arch/arm/mach-ns9xxx/clock.h
@@ -27,6 +27,9 @@ struct clk {
struct list_head node;
unsigned long refcount;
unsigned long usage;
+
+#define CLK_FLAG_CHANGESTATE 0
+ unsigned long flags;
};
int clk_register(struct clk *clk);
diff --git a/arch/arm/mach-ns9xxx/cme9210_devices.c b/arch/arm/mach-ns9xxx/cme9210_devices.c
new file mode 100644
index 000000000000..c3b3ff05b999
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/cme9210_devices.c
@@ -0,0 +1,251 @@
+ /*
+ * arch/arm/mach-ns9xxx/cme9210_devices.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/mtd/physmap.h>
+#include <mach/fim-ns921x.h>
+#include <linux/gpio.h>
+#include <linux/mmc/host.h>
+#include <linux/w1-gpio.h>
+
+#include "ns921x_devices.h"
+#include "cme9210_devices.h"
+
+#if defined(CONFIG_NS9XXX_ETH) || defined(CONFIG_NS9XXX_ETH_MODULE)
+void __init ns9xxx_add_device_cme9210_eth(void)
+{
+ int gpio[] = {
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ 14,
+#endif
+ 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49};
+ int func[] = {
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ 3,
+#endif
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int dir[] = {
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ 1,
+#endif
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ ns9xxx_add_device_ns921x_eth(NULL, 0, gpio,
+ func, dir, ARRAY_SIZE(gpio));
+}
+#else
+void __init ns9xxx_add_device_cme9210_eth(void) {}
+#endif
+
+#if defined(CONFIG_I2C_NS9XXX) || defined(CONFIG_I2C_NS9XXX_MODULE)
+static void cme9210_i2c_gpio_reconfigure(void)
+{
+ gpio_configure_ns921x(9, 0, 0, 1, 0);
+ gpio_configure_ns921x(12, 0, 0, 1, 0);
+}
+
+static struct plat_ns9xxx_i2c ns9xxx_device_cme9210_i2c_data = {
+ .gpio_scl = 9,
+ .gpio_sda = 12,
+ .speed = 100000,
+ .gpio_configuration_func = cme9210_i2c_gpio_reconfigure,
+};
+
+void __init ns9xxx_add_device_cme9210_i2c(void)
+{
+ ns9xxx_add_device_ns921x_i2c(&ns9xxx_device_cme9210_i2c_data);
+}
+#else
+void __init ns9xxx_add_device_cme9210_i2c(void) {}
+#endif
+
+#if defined(CONFIG_SERIAL_NS921X) || defined(CONFIG_SERIAL_NS921X_MODULE)
+void __init ns9xxx_add_device_cme9210_uarta(int gpio_nr)
+{
+ ns9xxx_add_device_ns921x_uarta(0, gpio_nr, 0);
+}
+void __init ns9xxx_add_device_cme9210_uartc(int gpio_nr)
+{
+ ns9xxx_add_device_ns921x_uartc(8, gpio_nr, 0);
+}
+#else
+void __init ns9xxx_add_device_cme9210_uarta(int gpio_nr) {}
+void __init ns9xxx_add_device_cme9210_uartc(int gpio_nr) {}
+#endif
+
+#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP)
+static struct physmap_flash_data ns9xxx_device_cme9210_flash_data = {
+ .width = 2,
+};
+
+void __init ns9xxx_add_device_cme9210_flash(void)
+{
+ ns9xxx_add_device_ns921x_flash(&ns9xxx_device_cme9210_flash_data);
+}
+#else
+void __init ns9xxx_add_device_cme9210_flash(void) {}
+#endif
+
+#if defined(CONFIG_SPI_NS921X) || defined(CONFIG_SPI_NS921X_MODULE)
+/* SPI ports and their related GPIOs */
+static struct spi_ns9xxx_data ns9xxx_device_cme9210_spi_data = {
+ .gpios = {7, 3, 5, 0},
+ .gpio_funcs = { NS921X_GPIO_FUNC_4,
+ NS921X_GPIO_FUNC_4,
+ NS921X_GPIO_FUNC_4,
+ NS921X_GPIO_FUNC_4 },
+ .nr_gpios = 4,
+};
+
+void __init ns9xxx_add_device_cme9210_spi(void)
+{
+ ns9xxx_add_device_ns921x_spi(&ns9xxx_device_cme9210_spi_data);
+}
+#else
+void __init ns9xxx_add_device_cme9210_spi(void) {}
+#endif
+
+/*
+ * XXX Need testing and it might be incomplete add_device()?
+ */
+
+#if defined(CONFIG_FIM_ZERO_SERIAL)
+static struct fim_serial_platform_data fim_serial_data0 = {
+ .fim_nr = 0,
+#if defined(CONFIG_FIM_ZERO_SERIAL_CTSRTS)
+ NS921X_FIM_SERIAL_GPIOS(2, 1, /* RX(2,23) + TX(1) */
+ 0, 3, /* RTS(0) + CTS(3) */
+ NS921X_GPIO_FUNC_2),
+#else
+ NS921X_FIM_SERIAL_GPIOS(2, 1, /* RX(2,23) + TX(1) */
+ FIM_GPIO_DONT_USE, /* RTS */
+ FIM_GPIO_DONT_USE, /* CTS */
+ NS921X_GPIO_FUNC_2),
+#endif
+};
+struct platform_device ns921x_fim_serial0 = {
+ .name = "fim-serial",
+ .id = 0,
+ .dev.platform_data = &fim_serial_data0,
+};
+EXPORT_SYMBOL(ns921x_fim_serial0);
+#endif /* CONFIG_FIM_ZERO_SERIAL */
+
+#if defined(CONFIG_FIM_ONE_CAN)
+static struct fim_can_platform_data fim_can_data1 = {
+ .fim_nr = 1,
+ .fim_can_bitrate = 500000,
+ NS921X_FIM_CAN_GPIOS( 23, 24, /* RX(2,23) + TX(6,24) */
+ NS921X_GPIO_FUNC_2),
+};
+struct platform_device ns921x_fim_can1 = {
+ .name = "fim-can",
+ .id = 1,
+ .dev.platform_data = &fim_can_data1,
+};
+EXPORT_SYMBOL(ns921x_fim_can1);
+#endif /* CONFIG_FIM_ONE_CAN */
+
+#if defined(CONFIG_FIM_ZERO_SDIO)
+static struct fim_sdio_platform_data fim_sdio_data0 = {
+ .fim_nr = 0,
+ .host_caps = 0,
+
+ .d0_gpio_nr = 0,
+ .d0_gpio_func = NS921X_GPIO_FUNC_2,
+ .d1_gpio_nr = FIM_GPIO_DONT_USE,
+ .d2_gpio_nr = FIM_GPIO_DONT_USE,
+ .d3_gpio_nr = FIM_GPIO_DONT_USE,
+
+ .clk_gpio_nr = 1,
+ .clk_gpio_func = NS921X_GPIO_FUNC_2,
+
+ .cmd_gpio_nr = 2,
+ .cmd_gpio_func = NS921X_GPIO_FUNC_2,
+
+ .cd_gpio_nr = 9,
+ .cd_gpio_func = NS921X_GPIO_FUNC_2,
+
+ .wp_gpio_nr = 6,
+ .wp_gpio_func = NS921X_GPIO_FUNC_3,
+};
+struct platform_device ns921x_fim_sdio0 = {
+ .name = "fim-sdio",
+ .id = 0,
+ .dev.platform_data = &fim_sdio_data0,
+};
+EXPORT_SYMBOL(ns921x_fim_sdio0);
+#endif /* CONFIG_FIM_ZERO_SDIO */
+
+/* The second SDIO-port will be available by the CC9P9210JS first */
+#if defined(CONFIG_FIM_ONE_SDIO)
+static struct fim_sdio_platform_data fim_sdio_data1 = {
+ .fim_nr = 1,
+ .host_caps = 0,
+
+ .d0_gpio_nr = 26,
+ .d0_gpio_func = NS921X_GPIO_FUNC_2,
+ .d1_gpio_nr = FIM_GPIO_DONT_USE,
+ .d2_gpio_nr = FIM_GPIO_DONT_USE,
+ .d3_gpio_nr = FIM_GPIO_DONT_USE,
+
+ .clk_gpio_nr = 27,
+ .clk_gpio_func = NS921X_GPIO_FUNC_2,
+
+ .cmd_gpio_nr = 28,
+ .cmd_gpio_func = NS921X_GPIO_FUNC_2,
+
+ .cd_gpio_nr = 9,
+ .cd_gpio_func = NS921X_GPIO_FUNC_2,
+
+ .wp_gpio_nr = 6,
+ .wp_gpio_func = NS921X_GPIO_FUNC_3,
+};
+struct platform_device ns921x_fim_sdio1 = {
+ .name = "fim-sdio",
+ .id = 1,
+ .dev.platform_data = &fim_sdio_data1,
+};
+EXPORT_SYMBOL(ns921x_fim_sdio1);
+#endif /* CONFIG_FIM_ONE_SDIO */
+
+#if defined(CONFIG_FIM_ZERO_W1)
+static struct w1_gpio_platform_data fim_w1_data0 = {
+ .pin = 1,
+ .is_open_drain = 0,
+};
+struct platform_device ns921x_fim0_w1 = {
+ .name = "w1-gpio",
+ .id = 0,
+ .dev = {
+ .platform_data = &fim_w1_data0,
+ },
+};
+EXPORT_SYMBOL(ns921x_fim0_w1);
+#endif /* CONFIG_FIM_ZERO_W1 */
+
+#if defined(CONFIG_FIM_ZERO_USB)
+static struct fim_usb_platform_data fim_usb_data0 = {
+ .fim_nr = 0,
+ NS921X_FIM_USB_GPIOS(0, 1, 2, /* VP + VM + RCV */
+ 3, 6, 9, /* OE_L + ENUM + SPND */
+ NS921X_GPIO_FUNC_2,
+ NS921X_GPIO_FUNC_GPIO),
+};
+struct platform_device ns921x_fim_usb0 = {
+ .name = "fim-usb",
+ .id = 0,
+ .dev.platform_data = &fim_usb_data0,
+};
+EXPORT_SYMBOL(ns921x_fim_usb0);
+#endif /* CONFIG_FIM_ZERO_USB */
diff --git a/arch/arm/mach-ns9xxx/cme9210_devices.h b/arch/arm/mach-ns9xxx/cme9210_devices.h
new file mode 100644
index 000000000000..606ed71a244f
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/cme9210_devices.h
@@ -0,0 +1,26 @@
+/*
+ * arch/arm/mach-ns9xxx/cme9210_devices.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+void __init ns9xxx_add_device_cme9210_eth(void);
+void __init ns9xxx_add_device_cme9210_uarta(int gpio_nr);
+void __init ns9xxx_add_device_cme9210_uartc(int gpio_nr);
+void __init ns9xxx_add_device_cme9210_flash(void);
+void __init ns9xxx_add_device_cme9210_spi(void);
+void __init ns9xxx_add_device_cme9210_i2c(void);
+
+#define ns9xxx_add_device_cme9210_uarta_rxtx() \
+ ns9xxx_add_device_cme9210_uarta(2)
+#define ns9xxx_add_device_cme9210_uarta_ctsrtsrxtx() \
+ ns9xxx_add_device_cme9210_uarta(4)
+#define ns9xxx_add_device_cme9210_uarta_full() \
+ ns9xxx_add_device_cme9210_uarta(8)
+#define ns9xxx_add_device_cme9210_uartc_rxtx() \
+ ns9xxx_add_device_cme9210_uartc(2)
diff --git a/arch/arm/mach-ns9xxx/gpio-ns921x.c b/arch/arm/mach-ns9xxx/gpio-ns921x.c
new file mode 100644
index 000000000000..fff11e82c862
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/gpio-ns921x.c
@@ -0,0 +1,41 @@
+/*
+ * arch/arm/mach-ns9xxx/gpio-ns921x.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/module.h>
+
+#include <asm/gpio.h>
+#include <mach/irqs.h>
+
+static const struct gpio_to_irq_map gpio_to_irq_map_ns921x[] = {
+ { .gpio = 1, .irq = IRQ_NS9XXX_EXT0, .func = 1, },
+ { .gpio = 2, .irq = IRQ_NS9XXX_EXT1, .func = 1, },
+ { .gpio = 4, .irq = IRQ_NS9XXX_EXT2, .func = 1, },
+ { .gpio = 5, .irq = IRQ_NS9XXX_EXT3, .func = 1, },
+ { .gpio = 9, .irq = IRQ_NS9XXX_EXT0, .func = 2, },
+ { .gpio = 10, .irq = IRQ_NS9XXX_EXT1, .func = 2, },
+ { .gpio = 16, .irq = IRQ_NS9XXX_EXT0, .func = 2, },
+ { .gpio = 17, .irq = IRQ_NS9XXX_EXT1, .func = 2, },
+ { .gpio = 18, .irq = IRQ_NS9XXX_EXT2, .func = 2, },
+ { .gpio = 19, .irq = IRQ_NS9XXX_EXT3, .func = 2, },
+#if defined(CONFIG_PROCESSOR_NS9215)
+ { .gpio = 67, .irq = IRQ_NS9XXX_EXT3, .func = 2, },
+ { .gpio = 101, .irq = IRQ_NS9XXX_EXT3, .func = 2, },
+#endif
+ { .gpio = 104, .irq = IRQ_NS9XXX_EXT0, .func = 2, },
+ { .gpio = 105, .irq = IRQ_NS9XXX_EXT1, .func = 2, },
+ { .gpio = 106, .irq = IRQ_NS9XXX_EXT2, .func = 2, },
+};
+
+const struct gpio_to_irq_map *gpio_get_map_ns921x(unsigned gpio)
+{
+ return gpio_get_map(gpio, gpio_to_irq_map_ns921x,
+ ARRAY_SIZE(gpio_to_irq_map_ns921x));
+}
+EXPORT_SYMBOL(gpio_get_map_ns921x);
diff --git a/arch/arm/mach-ns9xxx/gpio-ns9360.c b/arch/arm/mach-ns9xxx/gpio-ns9360.c
index 377330c1b250..6bc3e74207d9 100644
--- a/arch/arm/mach-ns9xxx/gpio-ns9360.c
+++ b/arch/arm/mach-ns9xxx/gpio-ns9360.c
@@ -1,118 +1,34 @@
/*
* arch/arm/mach-ns9xxx/gpio-ns9360.c
*
- * Copyright (C) 2006,2007 by Digi International Inc.
+ * Copyright (C) 2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
-#include <linux/bug.h>
-#include <linux/errno.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
#include <linux/module.h>
-#include <mach/regs-bbu.h>
-#include <mach/processor-ns9360.h>
+#include <asm/gpio.h>
+#include <mach/irqs.h>
-#include "gpio-ns9360.h"
+static const struct gpio_to_irq_map gpio_to_irq_map_ns9360[] = {
+ { .gpio = 1, .irq = IRQ_NS9XXX_EXT0, .func = 2, },
+ { .gpio = 7, .irq = IRQ_NS9XXX_EXT1, .func = 2, },
+ { .gpio = 11, .irq = IRQ_NS9XXX_EXT2, .func = 1, },
+ { .gpio = 13, .irq = IRQ_NS9XXX_EXT0, .func = 1, },
+ { .gpio = 18, .irq = IRQ_NS9XXX_EXT3, .func = 2, },
+ { .gpio = 28, .irq = IRQ_NS9XXX_EXT1, .func = 0, },
+ { .gpio = 32, .irq = IRQ_NS9XXX_EXT2, .func = 0, },
+ { .gpio = 40, .irq = IRQ_NS9XXX_EXT3, .func = 1, },
+ { .gpio = 68, .irq = IRQ_NS9XXX_EXT0, .func = 2, },
+ { .gpio = 69, .irq = IRQ_NS9XXX_EXT1, .func = 2, },
+};
-static inline int ns9360_valid_gpio(unsigned gpio)
+const struct gpio_to_irq_map *gpio_get_map_ns9360(unsigned gpio)
{
- return gpio <= 72;
-}
-
-static inline void __iomem *ns9360_gpio_get_gconfaddr(unsigned gpio)
-{
- if (gpio < 56)
- return BBU_GCONFb1(gpio / 8);
- else
- /*
- * this could be optimised away on
- * ns9750 only builds, but it isn't ...
- */
- return BBU_GCONFb2((gpio - 56) / 8);
-}
-
-static inline void __iomem *ns9360_gpio_get_gctrladdr(unsigned gpio)
-{
- if (gpio < 32)
- return BBU_GCTRL1;
- else if (gpio < 64)
- return BBU_GCTRL2;
- else
- /* this could be optimised away on ns9750 only builds */
- return BBU_GCTRL3;
-}
-
-static inline void __iomem *ns9360_gpio_get_gstataddr(unsigned gpio)
-{
- if (gpio < 32)
- return BBU_GSTAT1;
- else if (gpio < 64)
- return BBU_GSTAT2;
- else
- /* this could be optimised away on ns9750 only builds */
- return BBU_GSTAT3;
-}
-
-/*
- * each gpio can serve for 4 different purposes [0..3]. These are called
- * "functions" and passed in the parameter func. Functions 0-2 are always some
- * special things, function 3 is GPIO. If func == 3 dir specifies input or
- * output, and with inv you can enable an inverter (independent of func).
- */
-int __ns9360_gpio_configure(unsigned gpio, int dir, int inv, int func)
-{
- void __iomem *conf = ns9360_gpio_get_gconfaddr(gpio);
- u32 confval;
-
- confval = __raw_readl(conf);
- REGSETIM_IDX(confval, BBU_GCONFx, DIR, gpio & 7, dir);
- REGSETIM_IDX(confval, BBU_GCONFx, INV, gpio & 7, inv);
- REGSETIM_IDX(confval, BBU_GCONFx, FUNC, gpio & 7, func);
- __raw_writel(confval, conf);
-
- return 0;
-}
-
-int ns9360_gpio_configure(unsigned gpio, int inv, int func)
-{
- if (likely(ns9360_valid_gpio(gpio))) {
- if (func == 3) {
- printk(KERN_WARNING "use gpio_direction_input "
- "or gpio_direction_output\n");
- return -EINVAL;
- } else
- return __ns9360_gpio_configure(gpio, 0, inv, func);
- } else
- return -EINVAL;
-}
-EXPORT_SYMBOL(ns9360_gpio_configure);
-
-int ns9360_gpio_get_value(unsigned gpio)
-{
- void __iomem *stat = ns9360_gpio_get_gstataddr(gpio);
- int ret;
-
- ret = 1 & (__raw_readl(stat) >> (gpio & 31));
-
- return ret;
-}
-
-void ns9360_gpio_set_value(unsigned gpio, int value)
-{
- void __iomem *ctrl = ns9360_gpio_get_gctrladdr(gpio);
- u32 ctrlval;
-
- ctrlval = __raw_readl(ctrl);
-
- if (value)
- ctrlval |= 1 << (gpio & 31);
- else
- ctrlval &= ~(1 << (gpio & 31));
-
- __raw_writel(ctrlval, ctrl);
+ return gpio_get_map(gpio, gpio_to_irq_map_ns9360,
+ ARRAY_SIZE(gpio_to_irq_map_ns9360));
}
+EXPORT_SYMBOL(gpio_get_map_ns9360);
diff --git a/arch/arm/mach-ns9xxx/gpio.c b/arch/arm/mach-ns9xxx/gpio.c
index 5503ca09c4ae..374e312bd7dd 100644
--- a/arch/arm/mach-ns9xxx/gpio.c
+++ b/arch/arm/mach-ns9xxx/gpio.c
@@ -1,61 +1,33 @@
/*
* arch/arm/mach-ns9xxx/gpio.c
*
- * Copyright (C) 2006,2007 by Digi International Inc.
+ * Copyright (C) 2006-2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
-#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/module.h>
-#include <linux/bitops.h>
-#include <mach/gpio.h>
-#include <mach/processor.h>
-#include <mach/processor-ns9360.h>
#include <asm/bug.h>
#include <asm/types.h>
+#include <asm/bitops.h>
-#include "gpio-ns9360.h"
-
-#if defined(CONFIG_PROCESSOR_NS9360)
-#define GPIO_MAX 72
-#elif defined(CONFIG_PROCESSOR_NS9750)
-#define GPIO_MAX 49
-#endif
+#include <mach/gpio.h>
+#include <mach/processor.h>
-/* protects BBU_GCONFx and BBU_GCTRLx */
-static spinlock_t gpio_lock = __SPIN_LOCK_UNLOCKED(gpio_lock);
+DEFINE_SPINLOCK(gpio_lock);
/* only access gpiores with atomic ops */
-static DECLARE_BITMAP(gpiores, GPIO_MAX + 1);
-
-static inline int ns9xxx_valid_gpio(unsigned gpio)
-{
-#if defined(CONFIG_PROCESSOR_NS9360)
- if (processor_is_ns9360())
- return gpio <= 72;
- else
-#endif
-#if defined(CONFIG_PROCESSOR_NS9750)
- if (processor_is_ns9750())
- return gpio <= 49;
- else
-#endif
- {
- BUG();
- return 0;
- }
-}
+static DECLARE_BITMAP(gpiores, NS9XXX_NUM_GPIO);
int gpio_request(unsigned gpio, const char *label)
{
- if (likely(ns9xxx_valid_gpio(gpio)))
+ if (likely(gpio_issocgpio(gpio)))
return test_and_set_bit(gpio, gpiores) ? -EBUSY : 0;
else
return -EINVAL;
@@ -64,84 +36,7 @@ EXPORT_SYMBOL(gpio_request);
void gpio_free(unsigned gpio)
{
- might_sleep();
clear_bit(gpio, gpiores);
return;
}
EXPORT_SYMBOL(gpio_free);
-
-int gpio_direction_input(unsigned gpio)
-{
- if (likely(ns9xxx_valid_gpio(gpio))) {
- int ret = -EINVAL;
- unsigned long flags;
-
- spin_lock_irqsave(&gpio_lock, flags);
-#if defined(CONFIG_PROCESSOR_NS9360)
- if (processor_is_ns9360())
- ret = __ns9360_gpio_configure(gpio, 0, 0, 3);
- else
-#endif
- BUG();
-
- spin_unlock_irqrestore(&gpio_lock, flags);
-
- return ret;
-
- } else
- return -EINVAL;
-}
-EXPORT_SYMBOL(gpio_direction_input);
-
-int gpio_direction_output(unsigned gpio, int value)
-{
- if (likely(ns9xxx_valid_gpio(gpio))) {
- int ret = -EINVAL;
- unsigned long flags;
-
- gpio_set_value(gpio, value);
-
- spin_lock_irqsave(&gpio_lock, flags);
-#if defined(CONFIG_PROCESSOR_NS9360)
- if (processor_is_ns9360())
- ret = __ns9360_gpio_configure(gpio, 1, 0, 3);
- else
-#endif
- BUG();
-
- spin_unlock_irqrestore(&gpio_lock, flags);
-
- return ret;
- } else
- return -EINVAL;
-}
-EXPORT_SYMBOL(gpio_direction_output);
-
-int gpio_get_value(unsigned gpio)
-{
-#if defined(CONFIG_PROCESSOR_NS9360)
- if (processor_is_ns9360())
- return ns9360_gpio_get_value(gpio);
- else
-#endif
- {
- BUG();
- return -EINVAL;
- }
-}
-EXPORT_SYMBOL(gpio_get_value);
-
-void gpio_set_value(unsigned gpio, int value)
-{
- unsigned long flags;
- spin_lock_irqsave(&gpio_lock, flags);
-#if defined(CONFIG_PROCESSOR_NS9360)
- if (processor_is_ns9360())
- ns9360_gpio_set_value(gpio, value);
- else
-#endif
- BUG();
-
- spin_unlock_irqrestore(&gpio_lock, flags);
-}
-EXPORT_SYMBOL(gpio_set_value);
diff --git a/arch/arm/mach-ns9xxx/gpiolib-ns921x.c b/arch/arm/mach-ns9xxx/gpiolib-ns921x.c
new file mode 100644
index 000000000000..e8a513171d26
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/gpiolib-ns921x.c
@@ -0,0 +1,76 @@
+/*
+ * arch/arm/mach-ns9xxx/gpiolib-ns921x.c
+ *
+ * Copyright (C) 2007-2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/errno.h>
+
+#include <asm/gpio.h>
+
+#include <mach/processor.h>
+
+static int ns921x_gpio_chip_get(struct gpio_chip *chip, unsigned offset)
+{
+ return gpio_get_value_ns921x(chip->base + offset);
+}
+
+static void ns921x_gpio_chip_set(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ gpio_set_value_ns921x_unlocked(chip->base + offset, value);
+}
+
+static int ns921x_gpio_chip_dir_input(struct gpio_chip *chip, unsigned offset)
+{
+ return gpio_direction_irqinput_ns921x_unlocked(chip->base + offset);
+}
+
+static int ns921x_gpio_chip_dir_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ return gpio_direction_output_ns921x_unlocked(chip->base + offset,
+ value);
+}
+
+static struct gpio_chip ns921x_gpio_chip = {
+ .label = "ns921x-system-gpio",
+ .direction_input = ns921x_gpio_chip_dir_input,
+ .direction_output = ns921x_gpio_chip_dir_output,
+ .get = ns921x_gpio_chip_get,
+ .set = ns921x_gpio_chip_set,
+ .base = 0,
+};
+
+static struct gpio_chip ns921x_gpio_a_chip = {
+ .label = "ns921x-system-gpio",
+ .direction_input = ns921x_gpio_chip_dir_input,
+ .direction_output = ns921x_gpio_chip_dir_output,
+ .get = ns921x_gpio_chip_get,
+ .set = ns921x_gpio_chip_set,
+ .base = 104,
+ .ngpio = 4,
+};
+
+int __init ns921x_gpio_init(void)
+{
+ int ret;
+
+ if (processor_is_ns9210())
+ ns921x_gpio_chip.ngpio = 50;
+ else if (processor_is_ns9215())
+ ns921x_gpio_chip.ngpio = 104;
+ else
+ return -EINVAL;
+
+ ret = gpiochip_add(&ns921x_gpio_chip);
+ if (ret)
+ return ret;
+
+ return gpiochip_add(&ns921x_gpio_a_chip);
+}
diff --git a/arch/arm/mach-ns9xxx/gpiolib-ns921x.h b/arch/arm/mach-ns9xxx/gpiolib-ns921x.h
new file mode 100644
index 000000000000..73b3a754b411
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/gpiolib-ns921x.h
@@ -0,0 +1,12 @@
+/*
+ * arch/arm/mach-ns9xxx/gpiolib-ns921x.h
+ *
+ * Copyright (C) 2006-2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+int __init ns921x_gpio_init(void);
diff --git a/arch/arm/mach-ns9xxx/gpiolib-ns9360.c b/arch/arm/mach-ns9xxx/gpiolib-ns9360.c
new file mode 100644
index 000000000000..310dde70fea6
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/gpiolib-ns9360.c
@@ -0,0 +1,59 @@
+/*
+ * arch/arm/mach-ns9xxx/gpiolib-ns9360.c
+ *
+ * Copyright (C) 2006-2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <asm/gpio.h>
+
+static int ns9360_gpio_chip_get(struct gpio_chip *chip, unsigned offset)
+{
+ return gpio_get_value_ns9360(chip->base + offset);
+}
+
+static void ns9360_gpio_chip_set(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ gpio_set_value_ns9360_unlocked(chip->base + offset, value);
+}
+
+static int ns9360_gpio_chip_dir_input(struct gpio_chip *chip, unsigned offset)
+{
+ return gpio_direction_irqinput_ns9360_unlocked(chip->base + offset);
+}
+
+static int ns9360_gpio_chip_dir_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ return gpio_direction_output_ns9360_unlocked(chip->base + offset,
+ value);
+}
+
+static struct gpio_chip ns9360_gpio_chip = {
+ .label = "ns9360-system-gpio",
+ .direction_input = ns9360_gpio_chip_dir_input,
+ .direction_output = ns9360_gpio_chip_dir_output,
+ .get = ns9360_gpio_chip_get,
+ .set = ns9360_gpio_chip_set,
+ .base = 0,
+ .ngpio = 72,
+};
+
+int __init ns9360_gpio_init(void)
+{
+ if (!processor_is_ns9360())
+ return -EINVAL;
+
+ return gpiochip_add(&ns9360_gpio_chip);
+}
+
diff --git a/arch/arm/mach-ns9xxx/gpiolib-ns9360.h b/arch/arm/mach-ns9xxx/gpiolib-ns9360.h
new file mode 100644
index 000000000000..ea37e0b4dd8c
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/gpiolib-ns9360.h
@@ -0,0 +1,12 @@
+/*
+ * arch/arm/mach-ns9xxx/gpiolib-ns9360.h
+ *
+ * Copyright (C) 2006-2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+int __init ns9360_gpio_init(void);
diff --git a/arch/arm/mach-ns9xxx/include/mach/board.h b/arch/arm/mach-ns9xxx/include/mach/board.h
index f7e9196eb9ab..247c0034e573 100644
--- a/arch/arm/mach-ns9xxx/include/mach/board.h
+++ b/arch/arm/mach-ns9xxx/include/mach/board.h
@@ -31,10 +31,18 @@
|| machine_is_cc9p9215js() \
)
+#define board_is_jsccw9p9215() (0 \
+ || machine_is_ccw9p9215js() \
+ )
+
#define board_is_jscc9p9360() (0 \
|| machine_is_cc9p9360js() \
)
+#define board_is_jscme9210() (0 \
+ || machine_is_cme9210js() \
+ )
+
#define board_is_uncbas() (0 \
|| machine_is_cc7ucamry() \
)
diff --git a/arch/arm/mach-ns9xxx/include/mach/display/CUSTOM.h b/arch/arm/mach-ns9xxx/include/mach/display/CUSTOM.h
new file mode 100644
index 000000000000..2eceb4b94494
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/display/CUSTOM.h
@@ -0,0 +1,18 @@
+
+
+
+/*
+#define CUSTOM_DISPLAY \
+{ \
+ .display_name = "CUSTOM", \
+ .width = , \
+ .height = , \
+ .control = NS9360_DISPLAY_CONTROL, \
+ .timing = { \
+ NS9360_DISPLAY_TIMING0, \
+ NS9360_DISPLAY_TIMING1, \
+ NS9360_DISPLAY_TIMING2, \
+ }, \
+ .clock = NS9360_DISPLAY_CLOCK, \
+}
+*/ \ No newline at end of file
diff --git a/arch/arm/mach-ns9xxx/include/mach/display/Kconfig b/arch/arm/mach-ns9xxx/include/mach/display/Kconfig
new file mode 100644
index 000000000000..842eb775185e
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/display/Kconfig
@@ -0,0 +1,36 @@
+# arch/arm/mach-ns9xxx/displays/Kconfig
+#
+# Copyright 2009 Digi International Inc
+#
+
+if (CC9P9360JS_FB || CCX9C_FB)
+
+comment "Display selection"
+
+config NS9XXX_FB_VGA
+ bool "CRT VGA video support"
+ depends on ! (CC9P9360JS_SERIAL_PORTA_FULL || CCX9C_SERIAL_PORTA_FULL || CCW9C_SERIAL_PORTA_FULL)
+ def_bool y
+ help
+ This enables the video support for the VGA intrface.
+
+config NS9XXX_FB_LQ057Q3DC12I
+ bool "LQ057Q3DC12I TFT LCD support"
+ depends on ! (CC9P9360JS_SERIAL_PORTA_FULL || CCX9C_SERIAL_PORTA_FULL || CCW9C_SERIAL_PORTA_FULL)
+ help
+ This enables the support for the LQ057Q3DC12I TFT display.
+
+config NS9XXX_FB_LQ064V3DG01
+ bool "LQ064V3DG01 TFT LCD support"
+ depends on ! (CC9P9360JS_SERIAL_PORTA_FULL || CCX9C_SERIAL_PORTA_FULL || CCW9C_SERIAL_PORTA_FULL)
+ help
+ This enables the support for the LQ064V3DG01 TFT display.
+
+config NS9XXX_FB_CUSTOM
+ bool "Custom display support"
+ depends on ! (CC9P9360JS_SERIAL_PORTA_FULL || CCX9C_SERIAL_PORTA_FULL || CCW9C_SERIAL_PORTA_FULL)
+ help
+ This enables the support for a customer specific display.
+ displays/CUSTOM.h has to be modified when selecting this.
+
+endif
diff --git a/arch/arm/mach-ns9xxx/include/mach/display/LQ057Q3DC12I.h b/arch/arm/mach-ns9xxx/include/mach/display/LQ057Q3DC12I.h
new file mode 100644
index 000000000000..afaf151cab2b
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/display/LQ057Q3DC12I.h
@@ -0,0 +1,27 @@
+
+#define LQ057Q3DC12I_DISPLAY \
+{ \
+ .display_name = "LQ057Q3DC12I", \
+ .width = 320, \
+ .height = 240, \
+ .control = LCD_CONTROL_WATERMARK | \
+ LCD_CONTROL_PWR | \
+ LCD_CONTROL_TFT | \
+ LCD_CONTROL_BGR | \
+ LCD_CONTROL_BPP_16 | \
+ LCD_CONTROL_EN, \
+ .timing = { \
+ LCD_TIMING0_HBP(8) | \
+ LCD_TIMING0_HFP(39) | \
+ LCD_TIMING0_HSW(2) | \
+ LCD_TIMING0_PPL(320 / 16 - 1),\
+ LCD_TIMING1_VBP(6) | \
+ LCD_TIMING1_VFP(5) | \
+ LCD_TIMING1_VSW(0) | \
+ LCD_TIMING1_LPP(240 - 1), \
+ LCD_TIMING2_CPL(320 - 1) | \
+ LCD_TIMING2_IHS | \
+ LCD_TIMING2_IVS, \
+ }, \
+ .clock = 7000000, \
+}
diff --git a/arch/arm/mach-ns9xxx/include/mach/display/LQ064V3DG01.h b/arch/arm/mach-ns9xxx/include/mach/display/LQ064V3DG01.h
new file mode 100644
index 000000000000..e801e5f0766e
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/display/LQ064V3DG01.h
@@ -0,0 +1,28 @@
+
+#define LQ064V3DG01_DISPLAY \
+{ \
+ .display_name = "LQ064V3DG01", \
+ .width = 640, \
+ .height = 480, \
+ .control = LCD_CONTROL_WATERMARK | \
+ LCD_CONTROL_PWR | \
+ LCD_CONTROL_TFT | \
+ LCD_CONTROL_BGR | \
+ LCD_CONTROL_BPP_16 | \
+ LCD_CONTROL_EN, \
+ .timing = { \
+ LCD_TIMING0_HBP(40) | \
+ LCD_TIMING0_HFP(8) | \
+ LCD_TIMING0_HSW(96) | \
+ LCD_TIMING0_PPL(640 / 16 - 1),\
+ LCD_TIMING1_VBP(31) | \
+ LCD_TIMING1_VFP(2) | \
+ LCD_TIMING1_VSW(2) | \
+ LCD_TIMING1_LPP(480 - 1), \
+ LCD_TIMING2_CPL(640 - 1) | \
+ LCD_TIMING2_IPC | \
+ LCD_TIMING2_IHS | \
+ LCD_TIMING2_IVS, \
+ }, \
+ .clock = 28000000, \
+}
diff --git a/arch/arm/mach-ns9xxx/include/mach/display/VGA.h b/arch/arm/mach-ns9xxx/include/mach/display/VGA.h
new file mode 100644
index 000000000000..63f79f2440a6
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/display/VGA.h
@@ -0,0 +1,29 @@
+
+#define VGA_DISPLAY \
+{ \
+ .display_name = "VGA", \
+ .width = 640, \
+ .height = 480, \
+ .control = LCD_CONTROL_WATERMARK | \
+ LCD_CONTROL_PWR | \
+ LCD_CONTROL_TFT | \
+ LCD_CONTROL_BGR | \
+ LCD_CONTROL_BPP_16 | \
+ LCD_CONTROL_EN, \
+ .timing = { \
+ LCD_TIMING0_HBP(40) | \
+ LCD_TIMING0_HFP(8) | \
+ LCD_TIMING0_HSW(96) | \
+ LCD_TIMING0_PPL(640 / 16 - 1),\
+ LCD_TIMING1_VBP(25) | \
+ LCD_TIMING1_VFP(2) | \
+ LCD_TIMING1_VSW(2) | \
+ LCD_TIMING1_LPP(480 - 1), \
+ LCD_TIMING2_CPL(640 - 1) | \
+ LCD_TIMING2_BCD | \
+ LCD_TIMING2_IPC | \
+ LCD_TIMING2_IHS | \
+ LCD_TIMING2_IVS, \
+ }, \
+ .clock = 0, /* external */ \
+}
diff --git a/arch/arm/mach-ns9xxx/include/mach/display/displays.h b/arch/arm/mach-ns9xxx/include/mach/display/displays.h
new file mode 100644
index 000000000000..5fa5ca1de4d6
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/display/displays.h
@@ -0,0 +1,46 @@
+/*
+ * arch/arm/mach-ns9xxx/displays/displays.h
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <mach/ns9360fb.h>
+
+#if defined(CONFIG_NS9XXX_FB_VGA)
+#include <mach/display/VGA.h>
+#endif
+
+#if defined(CONFIG_NS9XXX_FB_LQ057Q3DC12I)
+#include <mach/display/LQ057Q3DC12I.h>
+#endif
+
+#if defined(CONFIG_NS9XXX_FB_LQ064V3DG01)
+#include <mach/display/LQ064V3DG01.h>
+#endif
+
+#if defined(CONFIG_NS9XXX_FB_CUSTOM)
+#include <mach/display/CUSTOM.h>
+#endif
+
+/* List of supported displays */
+struct ns9360fb_display display_list[] = {
+#if defined(CONFIG_NS9XXX_FB_VGA)
+ VGA_DISPLAY,
+#endif
+#if defined(CONFIG_NS9XXX_FB_LQ057Q3DC12I)
+ LQ057Q3DC12I_DISPLAY,
+#endif
+#if defined(CONFIG_NS9XXX_FB_LQ064V3DG01)
+ LQ064V3DG01_DISPLAY,
+#endif
+#if defined(CONFIG_NS9XXX_FB_CUSTOM)
+ CUSTOM_DISPLAY,
+#endif
+};
+
+
diff --git a/arch/arm/mach-ns9xxx/include/mach/dma-ns921x.h b/arch/arm/mach-ns9xxx/include/mach/dma-ns921x.h
new file mode 100644
index 000000000000..9fcbee4caa39
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/dma-ns921x.h
@@ -0,0 +1,140 @@
+/*
+ * arch/arm/mach-ns9xxx/include/mach/dma-ns921x.h
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+
+#ifndef __ASM_ARCH_NS921X_DMA_H
+#define __ASM_ARCH_NS921X_DMA_H
+
+#include <mach/hardware.h>
+
+/* External DMA control nad status registers. Valid channels are 1 and 2 */
+#define NS921X_DMA_BDP(x) __REG(0xa0800000 + ((x-1) & 0x1) * 0x10)
+#define NS921X_DMA_CR(x) __REG(0xa0800004 + ((x-1) & 0x1) * 0x10)
+#define NS921X_DMA_STIE(x) __REG(0xa0800008 + ((x-1) & 0x1) * 0x10)
+#define NS921X_DMA_PCS(x) __REG(0xa080000c + ((x-1) & 0x1) * 0x10)
+
+/* Control register masks */
+#define NS921X_DMA_CR_CE (0x1 << 31)
+#define NS921X_DMA_CR_CA (0x1 << 30)
+#define NS921X_DMA_CR_CG (0x1 << 29)
+#define NS921X_DMA_CR_SW_MA (0x3 << 27)
+#define NS921X_DMA_CR_SW_8b (0x0 << 27)
+#define NS921X_DMA_CR_SW_16b (0x1 << 27)
+#define NS921X_DMA_CR_SW_32b (0x2 << 27)
+#define NS921X_DMA_CR_DW_MA (0x3 << 25)
+#define NS921X_DMA_CR_DW_8b (0x0 << 25)
+#define NS921X_DMA_CR_DW_16b (0x1 << 25)
+#define NS921X_DMA_CR_DW_32b (0x2 << 25)
+#define NS921X_DMA_CR_SB_MA (0x3 << 23)
+#define NS921X_DMA_CR_SB_1B (0x0 << 23)
+#define NS921X_DMA_CR_SB_4B (0x1 << 23)
+#define NS921X_DMA_CR_SB_16B (0x2 << 23)
+#define NS921X_DMA_CR_SB_32B (0x3 << 23)
+#define NS921X_DMA_CR_DB_MA (0x3 << 21)
+#define NS921X_DMA_CR_DB_1B (0x0 << 21)
+#define NS921X_DMA_CR_DB_4B (0x1 << 21)
+#define NS921X_DMA_CR_DB_16B (0x2 << 21)
+#define NS921X_DMA_CR_DB_32B (0x3 << 21)
+#define NS921X_DMA_CR_SINC_N (0x1 << 20)
+#define NS921X_DMA_CR_DINC_N (0x1 << 19)
+#define NS921X_DMA_CR_POL (0x1 << 18)
+#define NS921X_DMA_CR_MODE (0x1 << 17)
+#define NS921X_DMA_CR_MODE_FBW (0x0 << 17)
+#define NS921X_DMA_CR_MODE_FBR (0x1 << 17)
+#define NS921X_DMA_CR_RESET (0x1 << 16)
+#define NS921X_DMA_CR_STATE_MA (0x3f << 10)
+#define NS921X_DMA_CR_INDEX_MA (0x3ff << 0)
+
+/* Status and interrupt enable masks */
+#define NS921X_DMA_STIE_NCIP (0x1 << 31)
+#define NS921X_DMA_STIE_ECIP (0x1 << 30)
+#define NS921X_DMA_STIE_NRIP (0x1 << 29)
+#define NS921X_DMA_STIE_CAIP (0x1 << 28)
+#define NS921X_DMA_STIE_PCIP (0x1 << 27)
+#define NS921X_DMA_STIE_NCIE (0x1 << 24)
+#define NS921X_DMA_STIE_ECIE (0x1 << 23)
+#define NS921X_DMA_STIE_NRIE (0x1 << 22)
+#define NS921X_DMA_STIE_CAIE (0x1 << 21)
+#define NS921X_DMA_STIE_PCIE (0x1 << 20)
+#define NS921X_DMA_STIE_IE_ALL (0x1f << 20)
+#define NS921X_DMA_STIE_WRAP (0x1 << 19)
+#define NS921X_DMA_STIE_DONE (0x1 << 18)
+#define NS921X_DMA_STIE_LAST (0x1 << 17)
+#define NS921X_DMA_STIE_FULL (0x1 << 16)
+#define NS921X_DMA_STIE_BLEN_MA (0xffff << 0)
+
+/* Peripheral CS register */
+#define NS921X_DMA_PCS_SEL_MA (0x3 << 0)
+#define NS921X_DMA_PCS_CS0 (0x0 << 0)
+#define NS921X_DMA_PCS_CS1 (0x1 << 0)
+#define NS921X_DMA_PCS_CS2 (0x2 << 0)
+#define NS921X_DMA_PCS_CS3 (0x3 << 0)
+
+#define EXT_DMA_DESC_CTRL_WRAP (0x1 << 15)
+#define EXT_DMA_DESC_CTRL_INT (0x1 << 14)
+#define EXT_DMA_DESC_CTRL_LAST (0x1 << 13)
+#define EXT_DMA_DESC_CTRL_FULL (0x1 << 12)
+#define EXT_DMA_DESC_CTRL_ALL (EXT_DMA_DESC_CTRL_FULL | \
+ EXT_DMA_DESC_CTRL_INT | \
+ EXT_DMA_DESC_CTRL_LAST | \
+ EXT_DMA_DESC_CTRL_WRAP)
+
+struct ext_dma_desc_t {
+ unsigned int src;
+ unsigned int length;
+ unsigned int dest;
+ unsigned short status;
+ unsigned short control;
+}__attribute__((__packed__));
+
+
+/*
+ * IO-HUB constans and macros
+ * The maximal number of DMA-buffer descriptors comes from the NET+OS
+ * distribution (iop_private.h)
+ */
+#define IOHUB_MAX_DMA_BUFFERS (64)
+#define IOHUB_MAX_DMA_LENGTH (65535)
+
+#define IOHUB_DMA_DESC_CTRL_WRAP EXT_DMA_DESC_CTRL_WRAP
+#define IOHUB_DMA_DESC_CTRL_INT EXT_DMA_DESC_CTRL_INT
+#define IOHUB_DMA_DESC_CTRL_LAST EXT_DMA_DESC_CTRL_LAST
+#define IOHUB_DMA_DESC_CTRL_FULL EXT_DMA_DESC_CTRL_FULL
+#define IOHUB_DMA_DESC_CTRL_ALL EXT_DMA_DESC_CTRL_ALL
+
+struct iohub_dma_desc_t {
+ unsigned int src;
+ unsigned int length;
+ unsigned int reserved;
+ unsigned short status;
+ unsigned short control;
+}__attribute__((packed, aligned));
+
+
+#define IOHUB_DMA_DESC_LENGTH sizeof(struct iohub_dma_desc_t)
+
+/* This is the FIFO used for the DMA-transfers of the IOHUB (e.g. FIMs) */
+struct iohub_dma_fifo_t {
+ int length;
+ struct iohub_dma_desc_t **descs;
+ dma_addr_t phys_descs;
+ struct iohub_dma_desc_t *first;
+ struct iohub_dma_desc_t *last;
+ struct iohub_dma_desc_t *dma_first;
+ struct iohub_dma_desc_t *dma_last;
+ struct iohub_dma_desc_t *dma_next;
+ struct iohub_dma_desc_t *next_free;
+ unsigned long rx_error, tx_error;
+ unsigned long rx_error1, tx_error2;
+}__attribute__((__packed__));
+
+#endif /* ifndef __ASM_ARCH_NS912X_DMA_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/fim-firmware.h b/arch/arm/mach-ns9xxx/include/mach/fim-firmware.h
new file mode 100644
index 000000000000..6bdd0e05333d
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/fim-firmware.h
@@ -0,0 +1,101 @@
+/*
+ * arch/arm/mach-ns9xxx/include/mach/fim-firmware.h
+ *
+ * Copyright (C) 2006 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * !Revision: $Revision: 1.0 $
+ * !Author: Luis Galdos
+ * !Desc:
+ * !References:
+ */
+
+
+#ifndef _FIM_FIRMWARE_H
+#define _FIM_FIRMWARE_H
+
+
+
+#define FIM_NUM_HWA_CONF_REGS (14)
+#define FIM_NS9215_MAX_INSTRUCTIONS (1024)
+
+
+typedef enum {
+ FIM_NS9215 = 0,
+} fim_processors_t;
+
+#define PROCESSOR_TYPE_VALID(t) (t == FIM_NS9215) /* || (t == ADD_NEW_ONE_HERE) */
+
+
+typedef enum {
+ FIM_FORMAT_0 = 0,
+} fim_formats_t;
+
+#define FORMAT_TYPE_VALID(t) (t == FIM_FORMAT_0) /* || (t == ADD_NEW_ONE_HERE) */
+
+typedef enum {
+ FIM_PROCESSOR_PIC0,
+ FIM_PROCESSOR_PIC1
+} fim_processor_index_t;
+
+typedef enum {
+ FIM_HW_ASSIST_MODE_NONE,
+ FIM_HW_ASSIST_MODE_GENERIC,
+ FIM_HW_ASSIST_MODE_CAN,
+} fim_hw_assit_t;
+
+typedef enum {
+ FIM_CLK_DIV_2,
+ FIM_CLK_DIV_4,
+ FIM_CLK_DIV_8,
+ FIM_CLK_DIV_16,
+ FIM_CLK_DIV_32,
+ FIM_CLK_DIV_64,
+ FIM_CLK_DIV_128,
+ FIM_CLK_DIV_256
+} fim_output_clk_div_t;
+
+typedef enum {
+ FIM_SIGBUS_SIGNAL_0,
+ FIM_SIGBUS_SIGNAL_1,
+ FIM_SIGBUS_SIGNAL_2,
+ FIM_SIGBUS_SIGNAL_3,
+ FIM_SIGBUS_SIGNAL_4,
+ FIM_SIGBUS_SIGNAL_5,
+ FIM_SIGBUS_SIGNAL_6,
+ FIM_SIGBUS_SIGNAL_7,
+ FIM_SIGBUS_CONTROL_0,
+ FIM_SIGBUS_CONTROL_1,
+ FIM_SIGBUS_CONTROL_2,
+ FIM_SIGBUS_CONTROL_3,
+ FIM_SIGBUS_16_BIT_BUS,
+ FIM_SIGBUS_24_BIT_BUS
+} fim_signal_bus_t;
+
+
+
+/*
+ * The macro FIM_FIRMWARE_BUILDER is set by the build-system of the FIM-firmware
+ * See the Makefile under ../firmware/Makefile for more infos.
+ */
+struct fim_program_t {
+ fim_processors_t processor;
+ fim_formats_t format;
+ fim_hw_assit_t hw_mode;
+ fim_output_clk_div_t clkdiv;
+ unsigned int hwa_cfg[FIM_NUM_HWA_CONF_REGS];
+ unsigned int length;
+#ifndef FIM_FIRMWARE_BUILDER
+ unsigned short data[FIM_NS9215_MAX_INSTRUCTIONS];
+#endif
+} __attribute__((packed, aligned));
+
+
+#endif /* _FIM_FIRMWARE_H */
+
+
+
diff --git a/arch/arm/mach-ns9xxx/include/mach/fim-ns921x.h b/arch/arm/mach-ns9xxx/include/mach/fim-ns921x.h
new file mode 100644
index 000000000000..6fbc61488f32
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/fim-ns921x.h
@@ -0,0 +1,466 @@
+/* -*- linux-c -*-
+ * arch/arm/mach-ns9xxx/include/mach/fim-ns921x.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * !Revision: $Revision: 1.25 $
+ * !Author: Silvano Najera, Luis Galdos
+ * !Descr:
+ * !References:
+ */
+
+
+#ifndef _NS921X_FIM_CORE_H
+#define _NS921X_FIM_CORE_H
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+#include <linux/kfifo.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#include <mach/dma-ns921x.h>
+
+
+#define FIM_MAX_FIRMWARE_NAME 32
+
+
+/* For DMA handling... */
+#define FIM_DMA_NCIP (0x1)
+#define FIM_DMA_NRIP (0x2)
+#define FIM_DMA_ECIP (0x4)
+#define FIM_DMA_CAIP (0x8)
+#define FIM_DMA_CANCELLED FIM_DMA_CAIP
+#define FIM_DMA_FLUSHED (0x10)
+#define FIM_DMA_SUCCESS (FIM_DMA_NCIP | FIM_DMA_NRIP)
+
+
+#define FIM_MAX_PIC_INDEX (1)
+#define FIM_MIN_PIC_INDEX (0)
+#define FIM_NR_PICS (FIM_MAX_PIC_INDEX-FIM_MIN_PIC_INDEX+1)
+
+
+/* @XXX: Place this macros in another place? */
+#define NS92XX_FIM_GEN_CTRL_STOP_PIC ~NS92XX_FIM_GEN_CTRL_PROGMEM
+#define NS92XX_FIM_GEN_CTRL_START_PIC NS92XX_FIM_GEN_CTRL_PROGMEM
+
+
+
+/* Please note that the maximal DMA-buffer size is 64kB */
+/* @FIXME: Check that the maximal size of the descriptors is littler than one page */
+#define PIC_DMA_RX_BUFFERS (10)
+#define PIC_DMA_TX_BUFFERS (10)
+#define PIC_DMA_BUFFER_SIZE (1 * PAGE_SIZE)
+
+/*
+ * Internal structure for handling with the DMA-buffer descriptors
+ * p_desc : Physical descriptors address
+ * v_desc : Virtual access address for the descriptors
+ * v_buf : Virtual address of the memory buffers
+ * length : Configured length of this buffer
+ * tasked : Used for locking the descriptor
+ */
+struct pic_dma_desc_t {
+ dma_addr_t src;
+ size_t length;
+ atomic_t tasked;
+ void *private;
+ int total_length;
+};
+
+
+/*
+ * Structure used by the FIM-API to configure the DMA-buffer and buffer-descriptors.
+ * rxnr : Number of RX-DMA-buffers
+ * rxsz : Size of each DMA-buffer (in Bytes)
+ * txnr : Number of TX-DMA-buffers
+ * txsz : Size for each TX-buffer (in Bytes)
+ */
+struct fim_dma_cfg_t {
+ int rxnr;
+ int rxsz;
+ int txnr;
+ int txsz;
+};
+
+
+/*
+ * This structure should be used for transferring data with the API
+ * length : Date length to transfer
+ * data : Data buffer
+ * private : The API will not touch this pointer
+ * sent : The external driver can use it for waking up sleeping processes
+ */
+struct fim_buffer_t {
+ int length;
+ unsigned char *data;
+ void *private;
+ int sent;
+};
+
+
+/* @TODO: We need perhaps another PIC-structure for the U-Boot */
+struct pic_t {
+ int irq;
+ struct device *dev;
+ struct fim_driver *driver;
+ void __iomem *reg_addr;
+ void __iomem *instr_addr;
+ void __iomem *hwa_addr;
+ void __iomem *iohub_addr;
+ spinlock_t lock;
+ int index;
+ atomic_t irq_enabled;
+ atomic_t requested;
+
+ /* RX-DMA structures */
+ struct iohub_dma_fifo_t rx_fifo;
+ spinlock_t rx_lock;
+ struct fim_dma_cfg_t dma_cfg;
+
+ /* Variables for the DMA-memory buffers */
+ dma_addr_t dma_phys;
+ void __iomem *dma_virt;
+ size_t dma_size;
+
+ /* Data for the handling of the TX-DMA buffers */
+ spinlock_t tx_lock;
+ struct pic_dma_desc_t *tx_desc;
+ struct iohub_dma_fifo_t tx_fifo;
+ atomic_t tx_tasked;
+ atomic_t tx_aborted;
+ struct tasklet_struct rx_tasklet;
+
+ /* Info data for the sysfs */
+ char fw_name[FIM_MAX_FIRMWARE_NAME];
+ int fw_length;
+
+ /* Functions for a low level access to the PICs */
+ int (* is_running)(struct pic_t *);
+ int (* start_at_zero)(struct pic_t *);
+ int (* stop_and_reset)(struct pic_t *);
+ int (* download_firmware)(struct pic_t *, const unsigned char *);
+ int (* get_ctrl_reg)(struct pic_t *, int , unsigned int *);
+ void (* set_ctrl_reg)(struct pic_t *, int , unsigned int );
+ int (* send_interrupt)(struct pic_t *, u32 );
+ void (* ack_interrupt)(struct pic_t * , int );
+};
+
+
+/*
+ * Structure with the GPIOs to use for the driver to be initialized
+ * nr : GPIO number
+ * name : Name to use for the GPIO
+ * picval : Value to pass to the PIC-firmware
+ * func : Function to be configured for the GPIO
+ */
+struct fim_gpio_t {
+ int nr;
+ char *name;
+ unsigned char picval; /* Value to pass to firmware */
+ unsigned int func;
+};
+
+#define FIM_LAST_GPIO -2
+#define FIM_GPIO_DONT_USE -1
+
+/*
+ * Internal structure for allocating a FIM driver
+ * picnr : Number of the PIC to use for this driver
+ * fw_code : Firmware code that should be used as firmware
+ * fw_name : Name of the firmware to get over the firmware layer
+ * driver : Driver structure
+ * dev : Device that should be set by the FIM-API
+ * driver_data : The API will not touch this member
+ * fim_isr : Called when the PIC generates an interrupt
+ * dma_tx_isr : TX-callback function. Called in interrupt context
+ * dma_rx_isr : RX-callback. Called inside the interrupt context
+ * dma_cfg : If NULL then the API will use the default config
+ * verbose : Used by the FIM-core for printing sys messages (debug, infos, etc.)
+ */
+struct fim_driver {
+ int picnr;
+ const unsigned char *fw_code;
+ const char *fw_name;
+ struct device_driver driver;
+ struct device *dev;
+ void (*fim_isr)(struct fim_driver *, int, unsigned char, unsigned int);
+ void (*dma_tx_isr)(struct fim_driver *, int, struct fim_buffer_t *);
+ void (*dma_rx_isr)(struct fim_driver *, int, struct fim_buffer_t *);
+ void (*dma_error_isr)(struct fim_driver *, ulong rx_err, ulong tx_err);
+ void *driver_data;
+ struct fim_dma_cfg_t *dma_cfg;
+ int verbose;
+};
+
+
+
+/*
+ * Structure for the FIM-devices with UART-support
+ * If a GPIO should not be used, then it's required to disable it by using the
+ * above macro 'FIM_GPIO_DONT_USE'
+ *
+ * fim_nr : Number of the FIM to use for the device
+ * gpio_nr : GPIO to use for the interface line
+ * fim_cfg : Currently not used
+ */
+struct fim_serial_platform_data {
+ int fim_nr;
+
+ int rx_gpio_nr;
+ unsigned int rx_gpio_func;
+ unsigned int rx_fim_cfg;
+
+ int tx_gpio_nr;
+ unsigned int tx_gpio_func;
+ unsigned int tx_fim_cfg;
+
+ int cts_gpio_nr;
+ unsigned int cts_gpio_func;
+ unsigned int cts_fim_cfg;
+
+ int rts_gpio_nr;
+ unsigned int rts_gpio_func;
+ unsigned int rts_fim_cfg;
+};
+
+
+/* Macro for the configuration of the GPIOs for the FIM-serial driver */
+#define NS921X_FIM_SERIAL_GPIOS(rx, tx, rts, cts, func) \
+ .rx_gpio_nr = rx, \
+ .rx_gpio_func = func, \
+ .tx_gpio_nr = tx, \
+ .tx_gpio_func = func, \
+ .rts_gpio_nr = rts, \
+ .rts_gpio_func = func, \
+ .cts_gpio_nr = cts, \
+ .cts_gpio_func = func
+
+
+/*
+ * Structure for the FIM-devices with SDIO-support
+ * If a GPIO should not be used, then it's required to disable it by using the
+ * macro 'FIM_GPIO_DONT_USE'
+ *
+ * fim_nr : Number of the FIM to use for the device
+ * host_caps : Specific host capabilities (see: linux/mmc/host.h)
+ */
+struct fim_sdio_platform_data {
+ int fim_nr;
+ unsigned int host_caps; /* Host capabilities */
+
+ int d0_gpio_nr; /* data 0 */
+ unsigned int d0_gpio_func;
+ int d1_gpio_nr; /* data 1 */
+ unsigned int d1_gpio_func;
+ int d2_gpio_nr; /* data 2 */
+ unsigned int d2_gpio_func;
+ int d3_gpio_nr; /* data 3 */
+ unsigned int d3_gpio_func;
+ int wp_gpio_nr; /* write protect */
+ unsigned int wp_gpio_func;
+ int cd_gpio_nr; /* card detect */
+ unsigned int cd_gpio_func;
+ int clk_gpio_nr; /* clock */
+ unsigned int clk_gpio_func;
+ int cmd_gpio_nr; /* command */
+ unsigned int cmd_gpio_func;
+};
+
+
+/*
+ * Use the below macro if all the GPIOs can be configured with the same function
+ * number (this is the normal case)
+ */
+#define NS921X_FIM_SDIO_GPIOS(d0, d1, d2, d3, wp, cd, clk, cmd, func) \
+ .d0_gpio_nr = d0, \
+ .d0_gpio_func = func, \
+ .d1_gpio_nr = d1, \
+ .d1_gpio_func = func, \
+ .d2_gpio_nr = d2, \
+ .d2_gpio_func = func, \
+ .d3_gpio_nr = d3, \
+ .d3_gpio_func = func, \
+ .wp_gpio_nr = wp, \
+ .wp_gpio_func = func, \
+ .cd_gpio_nr = cd, \
+ .cd_gpio_func = func, \
+ .clk_gpio_nr = clk, \
+ .clk_gpio_func = func, \
+ .cmd_gpio_nr = cmd, \
+ .cmd_gpio_func = func
+
+/*
+ * The new FIM board doesn't connect all the lines to the FIM. The CMD
+ * and CD are not connected to the FIM.
+ */
+#define NS921X_FIM_SDIO_GPIOS_FIM(d0, d1, d2, d3, clk, cmd, func) \
+ .d0_gpio_nr = d0, \
+ .d0_gpio_func = func, \
+ .d1_gpio_nr = d1, \
+ .d1_gpio_func = func, \
+ .d2_gpio_nr = d2, \
+ .d2_gpio_func = func, \
+ .d3_gpio_nr = d3, \
+ .d3_gpio_func = func, \
+ .clk_gpio_nr = clk, \
+ .clk_gpio_func = func, \
+ .cmd_gpio_nr = cmd, \
+ .cmd_gpio_func = func
+
+/*
+ * Structure for the FIM-devices with CAN-support
+ * If a GPIO should not be used, then it's required to disable it by using the
+ * above macro 'FIM_GPIO_DONT_USE'
+ *
+ * fim_nr : Number of the FIM to use for the device
+ * gpio_nr : GPIO to use for the interface line
+ */
+struct fim_can_platform_data {
+
+ int fim_nr;
+ int fim_can_bitrate;
+
+ int rx_gpio_nr;
+ unsigned int rx_gpio_func;
+ int tx_gpio_nr;
+ unsigned int tx_gpio_func;
+};
+
+/* Macro for the configuration of the GPIOs for the FIM CAN driver */
+#define NS921X_FIM_CAN_GPIOS(rx, tx, func) \
+ .rx_gpio_nr = rx, \
+ .rx_gpio_func = func, \
+ .tx_gpio_nr = tx, \
+ .tx_gpio_func = func
+
+/*
+ * Structure for the FIM-devices with USB support
+ * If a GPIO should not be used, then it's required to disable it by using the
+ * above macro 'FIM_GPIO_DONT_USE'
+ *
+ * fim_nr : Number of the FIM to use for the device
+ * gpio_nr : GPIO to use for the interface line
+ */
+struct fim_usb_platform_data {
+
+ int fim_nr;
+
+ int (*init)(struct device *);
+ int (*exit)(struct device *);
+
+ int vp_gpio_nr;
+ unsigned int vp_gpio_func;
+ int vm_gpio_nr;
+ unsigned int vm_gpio_func;
+ int rcv_gpio_nr;
+ unsigned int rcv_gpio_func;
+ int oe_l_gpio_nr;
+ unsigned int oe_l_gpio_func;
+ int enum_gpio_nr;
+ unsigned int enum_gpio_func;
+ int spnd_gpio_nr;
+ unsigned int spnd_gpio_func;
+};
+
+/*
+ * Macro for the configuration of the GPIOs for the FIM USB driver
+ * IMPORTANT: The FIM-firmware is able to control the DP, DM, OE and RCV pins, but NOT
+ * the lines for the enumeration (ENUM) and suspend (SPND). The 'func_out' defines the
+ * function for the output GPIOs
+ */
+#define NS921X_FIM_USB_GPIOS(vp, vm, rcv, oe_l, enume, spnd, func, func_out) \
+ .vp_gpio_nr = vp, \
+ .vp_gpio_func = func, \
+ .vm_gpio_nr = vm, \
+ .vm_gpio_func = func, \
+ .rcv_gpio_nr = rcv, \
+ .rcv_gpio_func = func, \
+ .oe_l_gpio_nr = oe_l, \
+ .oe_l_gpio_func = func, \
+ .enum_gpio_nr = enume, \
+ .enum_gpio_func = func_out, \
+ .spnd_gpio_nr = spnd, \
+ .spnd_gpio_func = func_out
+
+/* Macros for building the FIM-drivers as loadable modules */
+#if defined(MODULE)
+# define NS921X_FIM_NUMBERS_PARAM(number) \
+ static int number = -1; \
+ module_param_named(fims, number, int, 0644);
+#else
+# define NS921X_FIM_NUMBERS_PARAM(number) \
+ static int number = FIM_NR_PICS;
+#endif
+
+/* Call the function for checking the FIM module parameter */
+#if defined(MODULE)
+inline int fim_check_numbers_param(int number) \
+{ \
+ if (number < 0 || number > FIM_NR_PICS) \
+ return -1; \
+ else \
+ return 0; \
+}
+inline int fim_check_device_id(int number, uint id) { \
+ int ret; \
+ if (id < 0) \
+ ret = 1; \
+ else if (number == FIM_NR_PICS && id < number) \
+ ret = 0; \
+ else if (number < FIM_NR_PICS && id == number) \
+ ret = 0; \
+ else \
+ ret = 1; \
+ return ret; \
+}
+#else
+# define fim_check_numbers_param(number) (0)
+# define fim_check_device_id(number, id) (id < 0 || id >= number)
+#endif
+
+/* These are the functions of the FIM-API */
+int fim_register_driver(struct fim_driver *driver);
+int fim_unregister_driver(struct fim_driver *driver);
+int fim_send_interrupt2(struct fim_driver *driver, unsigned int code);
+int fim_get_exp_reg(struct fim_driver *driver, int nr, unsigned int *value);
+int fim_enable_irq(struct fim_driver *driver);
+int fim_disable_irq(struct fim_driver *driver);
+int fim_send_buffer(struct fim_driver *driver, const struct fim_buffer_t *bufdesc);
+int fim_tx_buffers_room(struct fim_driver *driver);
+int fim_tx_buffers_level(struct fim_driver *driver);
+int fim_send_reset(struct fim_driver *driver);
+int fim_send_start(struct fim_driver *driver);
+int fim_send_stop(struct fim_driver *driver);
+void fim_flush_rx(struct fim_driver *driver);
+void fim_flush_tx(struct fim_driver *driver);
+struct fim_buffer_t *fim_alloc_buffer(struct fim_driver *driver, int length,
+ unsigned int gfp_flags);
+void fim_free_buffer(struct fim_driver *driver, struct fim_buffer_t *buffer);
+void fim_set_ctrl_reg(struct fim_driver *driver, int reg, unsigned int val);
+void fim_set_exp_reg(struct fim_driver *driver, int reg, unsigned int val);
+int fim_get_ctrl_reg(struct fim_driver *driver, int reg, unsigned int *val);
+int fim_get_stat_reg(struct fim_driver *driver, int reg, unsigned int *val);
+struct pic_t *fim_request_pic(int picnr);
+void fim_free_pic(struct pic_t *pic);
+void fim_print_fifo_status(struct fim_driver *driver);
+int fim_number_pics(void);
+int fim_download_firmware(struct fim_driver *driver);
+int fim_is_running(struct fim_driver *driver);
+
+int fim_dma_stop(struct fim_driver *fim);
+int fim_dma_start(struct fim_driver *fim, struct fim_dma_cfg_t *cfg);
+
+#endif /* ifndef _NS921X_FIM_CORE_H */
+
+
+
diff --git a/arch/arm/mach-ns9xxx/include/mach/fim-uncompress.h b/arch/arm/mach-ns9xxx/include/mach/fim-uncompress.h
new file mode 100644
index 000000000000..0f77d72bbc5d
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/fim-uncompress.h
@@ -0,0 +1,93 @@
+/* -*- linux-c -*-
+ *
+ * arch/arm/mach-ns9xxx/include/mach/fim-uncompress.h
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * !Authors: Hector Palacios, Luis Galdos
+ * !Desc:
+ * !References:
+ */
+
+#ifndef __FIM_UNCROMPRESS_H
+#define __FIM_UNCROMPRESS_H
+
+#include <asm/io.h>
+
+#define NS921X_FIM0 __REG(0x90000000)
+#define NS921X_FIM1 __REG(0x90008000)
+
+#define FIM_SERIAL_DATA_BITS (8)
+
+#define NS92XX_FIM_CTRL0_REG (0x10)
+#define NS92XX_FIM_EXP0_REG (0x50)
+#define NS92XX_FIM_CTRL_REG(i) (NS92XX_FIM_CTRL0_REG + 4*i)
+#define NS92XX_FIM_EXP_REG(i) (NS92XX_FIM_EXP0_REG + 4*i)
+
+#define NS92XX_FIM_REG_BASE_PA (0x90001000)
+#define NS92XX_FIM_REG_BASE_PA (0x90001000)
+#define NS92XX_FIM_REG_OFFSET (0x8000)
+#define FIM_REG_ADDR(x) (NS92XX_FIM_REG_BASE_PA + \
+ (x * NS92XX_FIM_REG_OFFSET))
+#define NS921X_FIM_ENABLED(base) (__raw_readl((base) + 0x1000) \
+ & (1 << 31))
+#define NS92XX_FIM_GEN_CTRL_REG (0x00)
+#define NS92XX_FIM_GEN_CTRL_INTTOPIC (0x00007f00)
+#define NS92XX_FIM_GEN_CTRL_INTACKRD (0x00000080)
+#define NS92XX_FIM_INT_MASK(code) (code<<8)
+
+#define FIM_SERIAL_INT_INSERT_CHAR (0x01)
+
+static int fim_send_interrupt(int pic_num, unsigned int code)
+{
+ unsigned int stopcnt;
+ u32 status;
+
+ code = NS92XX_FIM_INT_MASK(code);
+ status = readl(FIM_REG_ADDR(pic_num) + NS92XX_FIM_GEN_CTRL_REG);
+ writel(status | code, FIM_REG_ADDR(pic_num) + NS92XX_FIM_GEN_CTRL_REG);
+
+ /* This loop is perhaps problematic, exit with a timeout */
+ stopcnt = 0xFFFF;
+ do {
+ status = readl(FIM_REG_ADDR(pic_num) + NS92XX_FIM_GEN_CTRL_REG);
+ stopcnt--;
+ } while (!(status & NS92XX_FIM_GEN_CTRL_INTACKRD) && stopcnt);
+
+ if (!stopcnt) {
+ return 1;
+ }
+
+ /* Reset the interrupt bits for the PIC acknowledge */
+ status &= ~NS92XX_FIM_GEN_CTRL_INTTOPIC;
+ writel(status, FIM_REG_ADDR(pic_num) + NS92XX_FIM_GEN_CTRL_REG);
+
+ stopcnt = 0xFFFF;
+ do {
+ status = readl(FIM_REG_ADDR(pic_num) + NS92XX_FIM_GEN_CTRL_REG);
+ stopcnt--;
+ } while ((status & NS92XX_FIM_GEN_CTRL_INTACKRD) && stopcnt);
+
+ if (!stopcnt) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void fim_set_ctrl_reg(int pic_num, int reg, unsigned int val)
+{
+ writel(val, FIM_REG_ADDR(pic_num) + NS92XX_FIM_CTRL_REG(reg));
+}
+
+static int fim_get_exp_reg(int pic_num, int nr)
+{
+ return readl(FIM_REG_ADDR(pic_num) + NS92XX_FIM_EXP_REG(nr));
+}
+
+#endif /* __FIM_UNCROMPRESS_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/gpio.h b/arch/arm/mach-ns9xxx/include/mach/gpio.h
index 5eb349032579..40e246dabdcc 100644
--- a/arch/arm/mach-ns9xxx/include/mach/gpio.h
+++ b/arch/arm/mach-ns9xxx/include/mach/gpio.h
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-ns9xxx/include/mach/gpio.h
*
- * Copyright (C) 2007 by Digi International Inc.
+ * Copyright (C) 2007,2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -11,21 +11,457 @@
#ifndef __ASM_ARCH_GPIO_H
#define __ASM_ARCH_GPIO_H
+#include <linux/spinlock.h>
#include <asm/errno.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <mach/processor.h>
-int gpio_request(unsigned gpio, const char *label);
+#include <mach/regs-io-ns921x.h>
+#include <mach/regs-bbu.h>
+
+
+/* Macros for configuring the GPIO-functions */
+#if defined(CONFIG_PROCESSOR_NS9210) || defined(CONFIG_PROCESSOR_NS9215)
+# define NS921X_GPIO_FUNC_0 (0x00)
+# define NS921X_GPIO_FUNC_1 (0x01)
+# define NS921X_GPIO_FUNC_2 (0x02)
+# define NS921X_GPIO_FUNC_3 (0x03)
+# define NS921X_GPIO_FUNC_4 (0x04)
+
+# define NS921X_GPIO_FUNC_GPIO NS921X_GPIO_FUNC_3
+
+# define NS921X_GPIO_INPUT (0x00)
+# define NS921X_GPIO_OUTPUT (0x01)
+
+# define NS921X_GPIO_INVERT (0x01)
+# define NS921X_GPIO_DONT_INVERT (0x00)
+
+# define NS921X_GPIO_ENABLE_PULLUP (0x00)
+# define NS921X_GPIO_DISABLE_PULLUP (0x01)
+#endif
+
+/* Macros for configuring the GPIO-functions */
+#if defined(CONFIG_PROCESSOR_NS9360)
+# define NS9360_GPIO_FUNC_0 (0x00)
+# define NS9360_GPIO_FUNC_1 (0x01)
+# define NS9360_GPIO_FUNC_2 (0x02)
+# define NS9360_GPIO_FUNC_3 (0x03)
+# define NS9360_GPIO_FUNC_4 (0x04)
+
+# define NS9360_GPIO_FUNC_GPIO NS9360_GPIO_FUNC_3
+
+# define NS9360_GPIO_INPUT (0x00)
+# define NS9360_GPIO_OUTPUT (0x01)
+
+# define NS9360_GPIO_INVERT (0x01)
+# define NS9360_GPIO_DONT_INVERT (0x00)
+#endif
+
+
+struct gpio_to_irq_map {
+ unsigned gpio;
+ unsigned irq;
+ int func;
+};
+
+#define NS9XXX_NUM_GPIO 0
+
+#if defined(CONFIG_PROCESSOR_NS9210)
+# define NS9XXX_NUM_GPIO_NS9210 108
+# if NS9XXX_NUM_GPIO < NS9XXX_NUM_GPIO_NS9210
+# undef NS9XXX_NUM_GPIO
+# define NS9XXX_NUM_GPIO NS9XXX_NUM_GPIO_NS9210
+# endif
+static inline int gpio_issocgpio_ns9210(unsigned gpio)
+{
+ return gpio < NS9XXX_NUM_GPIO_NS9210 && !(gpio >= 50 && gpio < 105);
+}
+#endif
+
+#if defined(CONFIG_PROCESSOR_NS9215)
+# define NS9XXX_NUM_GPIO_NS9215 108
+# if NS9XXX_NUM_GPIO < NS9XXX_NUM_GPIO_NS9215
+# undef NS9XXX_NUM_GPIO
+# define NS9XXX_NUM_GPIO NS9XXX_NUM_GPIO_NS9215
+# endif
+static inline int gpio_issocgpio_ns9215(unsigned gpio)
+{
+ return gpio < NS9XXX_NUM_GPIO_NS9215;
+}
+#endif
+
+static const inline struct gpio_to_irq_map *gpio_get_map(unsigned gpio,
+ const struct gpio_to_irq_map map[], size_t array_size)
+{
+ /* TODO: check if a binary search yields some performance advantage */
+ int i;
+
+ for (i = 0; i < array_size; ++i) {
+ if (map[i].gpio == gpio)
+ return &map[i];
+ }
+
+ return NULL;
+}
+
+#if defined(CONFIG_PROCESSOR_NS921X)
+const struct gpio_to_irq_map *gpio_get_map_ns921x(unsigned gpio) __attribute__((const));
+
+static inline void gpio_configure_ns921x_unlocked(unsigned gpio,
+ int dir, int inv, int func, int dispullup)
+{
+ void __iomem *conf = NS921X_IO_GPIOCONFx(gpio / 4);
+ u32 confval;
+
+ confval = __raw_readl(conf);
+ REGSETIM_IDX(confval, NS921X_IO_GPIOCONFx, DIR, gpio & 3, dir);
+ REGSETIM_IDX(confval, NS921X_IO_GPIOCONFx, INV, gpio & 3, inv);
+ REGSETIM_IDX(confval, NS921X_IO_GPIOCONFx, FUNC, gpio & 3, func);
+ if (gpio != 9 && gpio != 12 && (gpio < 102 || gpio > 105))
+ REGSETIM_IDX(confval, NS921X_IO_GPIOCONFx, PUEN, gpio & 3, dispullup);
+ else {
+ if (dispullup)
+ pr_warning("cannot disable pullup for gpio %u\n", gpio);
+
+ REGSETIM_IDX(confval, NS921X_IO_GPIOCONFx, PUEN, gpio & 3, 0);
+ }
+
+ __raw_writel(confval, conf);
+}
+
+static inline void gpio_configure_ns921x(unsigned gpio,
+ int dir, int inv, int func, int dispullup)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ gpio_configure_ns921x_unlocked(gpio, dir, inv, func, dispullup);
+
+ local_irq_restore(flags);
+}
+
+static inline int gpio_get_value_ns921x(unsigned gpio)
+{
+ void __iomem *stat = NS921X_IO_GPIOSTATx(gpio / 32);
+
+ return (__raw_readl(stat) >> (gpio & 31)) & 1;
+}
+
+static inline void gpio_set_value_ns921x_unlocked(unsigned gpio, int value)
+{
+ void __iomem *ctrl = NS921X_IO_GPIOCTRLx(gpio / 32);
+ u32 ctrlval;
+
+ ctrlval = __raw_readl(ctrl);
+
+ if (value)
+ ctrlval |= 1 << (gpio & 31);
+ else
+ ctrlval &= ~(1 << (gpio & 31));
+
+ __raw_writel(ctrlval, ctrl);
+}
+
+static inline int gpio_direction_input_ns921x_unlocked(unsigned gpio)
+{
+ gpio_configure_ns921x_unlocked(gpio, 0, 0, 3, 0);
+ return 0;
+}
+
+static inline int gpio_direction_irqinput_ns921x_unlocked(unsigned gpio)
+{
+ const struct gpio_to_irq_map *map = gpio_get_map_ns921x(gpio);
+
+ if (map) {
+ gpio_configure_ns921x_unlocked(gpio, 0, 0, map->func, 0);
+ return 0;
+ } else
+ return gpio_direction_input_ns921x_unlocked(gpio);
+}
+
+static inline int gpio_direction_output_ns921x_unlocked(unsigned gpio,
+ int value)
+{
+ gpio_set_value_ns921x_unlocked(gpio, value);
+ gpio_configure_ns921x_unlocked(gpio, 1, 0, 3, 0);
+ return 0;
+}
+
+#endif /* if defined(CONFIG_PROCESSOR_NS921X) */
+
+#if defined(CONFIG_PROCESSOR_NS9360)
+# define NS9XXX_NUM_GPIO_NS9360 73
+# if NS9XXX_NUM_GPIO < NS9XXX_NUM_GPIO_NS9360
+# undef NS9XXX_NUM_GPIO
+# define NS9XXX_NUM_GPIO NS9XXX_NUM_GPIO_NS9360
+# endif
+static inline void __iomem *gpio_ns9360_gstataddr(unsigned gpio)
+{
+ if (gpio < 32)
+ return NS9360_BBU_GSTAT1;
+ else if (gpio < 64)
+ return NS9360_BBU_GSTAT2;
+ else
+ return NS9360_BBU_GSTAT3;
+}
+
+static inline void __iomem *gpio_ns9360_gctrladdr(unsigned gpio)
+{
+ if (gpio < 32)
+ return NS9360_BBU_GCTRL1;
+ else if (gpio < 64)
+ return NS9360_BBU_GCTRL2;
+ else
+ return NS9360_BBU_GCTRL3;
+}
+
+static inline void __iomem *gpio_ns9360_gconfaddr(unsigned gpio)
+{
+ if (gpio < 56)
+ return NS9360_BBU_GCONFb1(gpio / 8);
+ else
+ /* this could be optimised away on
+ * ns9750 only builds, but it isn't ...
+ */
+ return NS9360_BBU_GCONFb2((gpio - 56) / 8);
+}
+
+static inline int gpio_issocgpio_ns9360(unsigned gpio)
+{
+ return gpio <= 72;
+}
+
+const struct gpio_to_irq_map *gpio_get_map_ns9360(unsigned gpio) __attribute__((const));
+
+static inline void gpio_configure_ns9360_unlocked(unsigned gpio,
+ int dir, int inv, int func)
+{
+ void __iomem *conf = gpio_ns9360_gconfaddr(gpio);
+ u32 confval;
+
+ confval = __raw_readl(conf);
+ REGSETIM_IDX(confval, NS9360_BBU_GCONFx, DIR, gpio & 7, dir);
+ REGSETIM_IDX(confval, NS9360_BBU_GCONFx, INV, gpio & 7, inv);
+ REGSETIM_IDX(confval, NS9360_BBU_GCONFx, FUNC, gpio & 7, func);
+ __raw_writel(confval, conf);
+}
+
+static inline void gpio_configure_ns9360(unsigned gpio,
+ int dir, int inv, int func)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ gpio_configure_ns9360_unlocked(gpio, dir, inv, func);
+
+ local_irq_restore(flags);
+}
+
+static inline int gpio_get_value_ns9360(unsigned gpio)
+{
+ void __iomem *stat = gpio_ns9360_gstataddr(gpio);
+
+ return (__raw_readl(stat) >> (gpio & 31)) & 1;
+}
+
+static inline void gpio_set_value_ns9360_unlocked(unsigned gpio, int value)
+{
+ void __iomem *ctrl = gpio_ns9360_gctrladdr(gpio);
+ u32 ctrlval;
+
+ ctrlval = __raw_readl(ctrl);
+
+ if (value)
+ ctrlval |= 1 << (gpio & 31);
+ else
+ ctrlval &= ~(1 << (gpio & 31));
+
+ __raw_writel(ctrlval, ctrl);
+}
+
+static inline int gpio_direction_input_ns9360_unlocked(unsigned gpio)
+{
+ gpio_configure_ns9360_unlocked(gpio, 0, 0, 3);
+ return 0;
+}
+
+static inline int gpio_direction_irqinput_ns9360_unlocked(unsigned gpio)
+{
+ const struct gpio_to_irq_map *map = gpio_get_map_ns9360(gpio);
+ if (map) {
+ gpio_configure_ns9360_unlocked(gpio, 0, 0, map->func);
+ return 0;
+ } else
+ return gpio_direction_input_ns9360_unlocked(gpio);
+}
+
+static inline int gpio_direction_output_ns9360_unlocked(unsigned gpio,
+ int value)
+{
+ gpio_set_value_ns9360_unlocked(gpio, value);
+ gpio_configure_ns9360_unlocked(gpio, 1, 0, 3);
+ return 0;
+}
+
+#endif /* if defined(CONFIG_PROCESSOR_NS9360) */
+
+static inline int gpio_issocgpio(unsigned gpio)
+{
+#if defined(CONFIG_PROCESSOR_NS9210)
+ if (processor_is_ns9210())
+ return gpio_issocgpio_ns9210(gpio);
+ else
+#endif
+#if defined(CONFIG_PROCESSOR_NS9215)
+ if (processor_is_ns9215())
+ return gpio_issocgpio_ns9215(gpio);
+ else
+#endif
+#if defined(CONFIG_PROCESSOR_NS9360)
+ if (processor_is_ns9360())
+ return gpio_issocgpio_ns9360(gpio);
+ else
+#endif
+ BUG();
+
+ BUG();
+
+ return 0;
+}
+
+#if defined(CONFIG_GPIOLIB)
+
+#include <asm-generic/gpio.h>
+
+static inline int gpio_get_value(unsigned gpio)
+{
+ return __gpio_get_value(gpio);
+}
+
+static inline void gpio_set_value(unsigned gpio, int value)
+{
+ return __gpio_set_value(gpio, value);
+}
+
+static inline int gpio_cansleep(unsigned gpio)
+{
+ return __gpio_cansleep(gpio);
+}
+
+#else /* if defined(CONFIG_GPIOLIB) */
+
+extern spinlock_t gpio_lock;
+
+int gpio_request(unsigned gpio, const char *label);
void gpio_free(unsigned gpio);
-int ns9xxx_gpio_configure(unsigned gpio, int inv, int func);
+static inline int gpio_get_value(unsigned gpio)
+{
+#if defined(CONFIG_PROCESSOR_NS921X)
+ if (processor_is_ns921x())
+ return gpio_get_value_ns921x(gpio);
+ else
+#endif
+#if defined(CONFIG_PROCESSOR_NS9360)
+ if (processor_is_ns9360())
+ return gpio_get_value_ns9360(gpio);
+ else
+#endif
+ BUG();
+}
+
+static inline void gpio_set_value_unlocked(unsigned gpio, int value)
+{
+#if defined(CONFIG_PROCESSOR_NS921X)
+ if (processor_is_ns921x())
+ gpio_set_value_ns921x_unlocked(gpio, value);
+ else
+#endif
+#if defined(CONFIG_PROCESSOR_NS9360)
+ if (processor_is_ns9360())
+ gpio_set_value_ns9360_unlocked(gpio, value);
+ else
+#endif
+ BUG();
+}
+
+static inline void gpio_set_value(unsigned gpio, int value)
+{
+ unsigned long flags;
+
+ BUG_ON(!gpio_issocgpio(gpio));
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ gpio_set_value_unlocked(gpio, value);
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static inline int gpio_direction_input(unsigned gpio)
+{
+ if (likely(gpio_issocgpio(gpio))) {
+ int ret = -EINVAL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+#if defined(CONFIG_PROCESSOR_NS921X)
+ if (processor_is_ns921x())
+ ret = gpio_direction_irqinput_ns921x_unlocked(gpio);
+ else
+#endif
+#if defined(CONFIG_PROCESSOR_NS9360)
+ if (processor_is_ns9360())
+ ret = gpio_direction_irqinput_ns9360_unlocked(gpio);
+ else
+#endif
+ BUG();
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
-int gpio_direction_input(unsigned gpio);
+ return ret;
+ } else
+ return -EINVAL;
+}
+
+static inline int gpio_direction_output(unsigned gpio, int value)
+{
+ if (likely(gpio_issocgpio(gpio))) {
+ int ret = -EINVAL;
+ unsigned long flags;
-int gpio_direction_output(unsigned gpio, int value);
+ spin_lock_irqsave(&gpio_lock, flags);
-int gpio_get_value(unsigned gpio);
+#if defined(CONFIG_PROCESSOR_NS921X)
+ if (processor_is_ns921x())
+ ret = gpio_direction_output_ns921x_unlocked(gpio,
+ value);
+ else
+#endif
+#if defined(CONFIG_PROCESSOR_NS9360)
+ if (processor_is_ns9360())
+ ret = gpio_direction_output_ns9360_unlocked(gpio,
+ value);
+ else
+#endif
+ BUG();
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ return ret;
+ } else
+ return -EINVAL;
+}
+
+#include <asm-generic/gpio.h>
+
+#endif /* if defined(CONFIG_GPIOLIB) / else */
-void gpio_set_value(unsigned gpio, int value);
/*
* ns9xxx can use gpio pins to trigger an irq, but it's not generic
@@ -33,6 +469,23 @@ void gpio_set_value(unsigned gpio, int value);
*/
static inline int gpio_to_irq(unsigned gpio)
{
+#if defined(CONFIG_PROCESSOR_NS921X)
+ if (processor_is_ns921x()) {
+ const struct gpio_to_irq_map *map = gpio_get_map_ns921x(gpio);
+
+ if (map)
+ return map->irq;
+ }
+#endif
+#if defined(CONFIG_PROCESSOR_NS9360)
+ if (processor_is_ns9360()) {
+ const struct gpio_to_irq_map *map = gpio_get_map_ns9360(gpio);
+
+ if (map)
+ return map->irq;
+ }
+#endif
+
return -EINVAL;
}
@@ -41,7 +494,4 @@ static inline int irq_to_gpio(unsigned irq)
return -EINVAL;
}
-/* get the cansleep() stubs */
-#include <asm-generic/gpio.h>
-
#endif /* ifndef __ASM_ARCH_GPIO_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/hardware.h b/arch/arm/mach-ns9xxx/include/mach/hardware.h
index 6dbb2030f563..46299b18433e 100644
--- a/arch/arm/mach-ns9xxx/include/mach/hardware.h
+++ b/arch/arm/mach-ns9xxx/include/mach/hardware.h
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-ns9xxx/include/mach/hardware.h
*
- * Copyright (C) 2006 by Digi International Inc.
+ * Copyright (C) 2006-2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -30,7 +30,9 @@
#define __REGSHIFT(mask) ((mask) & (-(mask)))
#define __REGBIT(bit) ((u32)1 << (bit))
+#define __REGBIT_SHIFT(bit, shift) ((u32)1 << ((bit) + (shift)))
#define __REGBITS(hbit, lbit) ((((u32)1 << ((hbit) - (lbit) + 1)) - 1) << (lbit))
+#define __REGBITS_SHIFT(hbit, lbit, shift) __REGBITS((hbit) + (shift), (lbit) + (shift))
#define __REGVAL(mask, value) (((value) * __REGSHIFT(mask)) & (mask))
#ifndef __ASSEMBLY__
@@ -66,13 +68,13 @@
__REGGET(var, reg ## _ ## field) / __REGSHIFT(reg ## _ ## field)
# define REGGETIM_IDX(var, reg, field, idx) \
- __REGGET(var, reg ## _ ## field((idx))) / \
+ __REGGET(var, reg ## _ ## field((idx))) / \
__REGSHIFT(reg ## _ ## field((idx)))
#else
# define __REG(x) io_p2v(x)
-# define __REG2(x, y) io_p2v((x) + 4 * (y))
+# define __REG2(x, y) io_p2v((x) + (y))
#endif
diff --git a/arch/arm/mach-ns9xxx/include/mach/irqs.h b/arch/arm/mach-ns9xxx/include/mach/irqs.h
index 13483949e210..f72bd08e5de9 100644
--- a/arch/arm/mach-ns9xxx/include/mach/irqs.h
+++ b/arch/arm/mach-ns9xxx/include/mach/irqs.h
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-ns9xxx/include/mach/irqs.h
*
- * Copyright (C) 2006,2007 by Digi International Inc.
+ * Copyright (C) 2006-2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -11,14 +11,48 @@
#ifndef __ASM_ARCH_IRQS_H
#define __ASM_ARCH_IRQS_H
-/* NetSilicon 9360 */
+/* Digi ns921x */
#define IRQ_NS9XXX_WATCHDOG 0
#define IRQ_NS9XXX_AHBBUSERR 1
-#define IRQ_NS9360_BBUSAGG 2
-/* irq 3 is reserved for NS9360 */
+#define IRQ_NS921X_EXTDMA 2
+#define IRQ_NS921X_CPUWAKE 3
#define IRQ_NS9XXX_ETHRX 4
#define IRQ_NS9XXX_ETHTX 5
-#define IRQ_NS9XXX_ETHPHY 6
+#define IRQ_NS9XXX_ETHPHY 6 /* reserved for ns9210 */
+#define IRQ_NS921X_UARTA 7
+#define IRQ_NS921X_UARTB 8
+#define IRQ_NS921X_UARTC 9
+#define IRQ_NS921X_UARTD 10
+#define IRQ_NS921X_SPI 11
+#define IRQ_NS921X_PIC0 12
+#define IRQ_NS921X_PIC1 13
+#define IRQ_NS9215_ADC 14
+#define IRQ_NS9215_EPL 15
+#define IRQ_NS921X_I2C 16
+#define IRQ_NS9215_RTC 17
+#define IRQ_NS921X_TIMER0 18
+#define IRQ_NS921X_TIMER1 19
+#define IRQ_NS921X_TIMER2 20
+#define IRQ_NS921X_TIMER3 21
+#define IRQ_NS921X_TIMER4 22
+#define IRQ_NS921X_TIMER5 23
+#define IRQ_NS921X_TIMER6 24
+#define IRQ_NS921X_TIMER7 25
+#define IRQ_NS921X_TIMER8 26
+#define IRQ_NS921X_TIMER9 27
+#define IRQ_NS9XXX_EXT0 28
+#define IRQ_NS9XXX_EXT1 29
+#define IRQ_NS9XXX_EXT2 30
+#define IRQ_NS9XXX_EXT3 31
+
+/* NetSilicon 9360 */
+/* IRQ_NS9XXX_WATCHDOG 0 */
+/* IRQ_NS9XXX_AHBBUSERR 1 */
+#define IRQ_NS9360_BBUSAGG 2
+/* irq 3 is reserved for NS9360 */
+/* IRQ_NS9XXX_ETHRX 3 */
+/* IRQ_NS9XXX_ETHTX 4 */
+/* IRQ_NS9XXX_ETHPHY 5 */
#define IRQ_NS9360_LCD 7
#define IRQ_NS9360_SERBRX 8
#define IRQ_NS9360_SERBTX 9
@@ -40,37 +74,39 @@
#define IRQ_NS9360_USBHOST 25
#define IRQ_NS9360_USBDEVICE 26
#define IRQ_NS9360_IEEE1284 27
-#define IRQ_NS9XXX_EXT0 28
-#define IRQ_NS9XXX_EXT1 29
-#define IRQ_NS9XXX_EXT2 30
-#define IRQ_NS9XXX_EXT3 31
+/* IRQ_NS9XXX_EXT0 28 */
+/* IRQ_NS9XXX_EXT1 29 */
+/* IRQ_NS9XXX_EXT2 30 */
+/* IRQ_NS9XXX_EXT3 31 */
+
+#define IRQ_NS9360_BBUS(irq) (32 + irq)
-#define BBUS_IRQ(irq) (32 + irq)
+#define IRQ_NS9360_BBUS_DMA IRQ_NS9360_BBUS(0)
+#define IRQ_NS9360_BBUS_SERBRX IRQ_NS9360_BBUS(2)
+#define IRQ_NS9360_BBUS_SERBTX IRQ_NS9360_BBUS(3)
+#define IRQ_NS9360_BBUS_SERARX IRQ_NS9360_BBUS(4)
+#define IRQ_NS9360_BBUS_SERATX IRQ_NS9360_BBUS(5)
+#define IRQ_NS9360_BBUS_SERCRX IRQ_NS9360_BBUS(6)
+#define IRQ_NS9360_BBUS_SERCTX IRQ_NS9360_BBUS(7)
+#define IRQ_NS9360_BBUS_SERDRX IRQ_NS9360_BBUS(8)
+#define IRQ_NS9360_BBUS_SERDTX IRQ_NS9360_BBUS(9)
+#define IRQ_NS9360_BBUS_I2C IRQ_NS9360_BBUS(10)
+#define IRQ_NS9360_BBUS_1284 IRQ_NS9360_BBUS(11)
+#define IRQ_NS9360_BBUS_UTIL IRQ_NS9360_BBUS(12)
+#define IRQ_NS9360_BBUS_RTC IRQ_NS9360_BBUS(13)
+#define IRQ_NS9360_BBUS_USBHST IRQ_NS9360_BBUS(14)
+#define IRQ_NS9360_BBUS_USBDEV IRQ_NS9360_BBUS(15)
+#define IRQ_NS9360_BBUS_AHBDMA1 IRQ_NS9360_BBUS(24)
+#define IRQ_NS9360_BBUS_AHBDMA2 IRQ_NS9360_BBUS(25)
-#define IRQ_BBUS_DMA BBUS_IRQ(0)
-#define IRQ_BBUS_SERBRX BBUS_IRQ(2)
-#define IRQ_BBUS_SERBTX BBUS_IRQ(3)
-#define IRQ_BBUS_SERARX BBUS_IRQ(4)
-#define IRQ_BBUS_SERATX BBUS_IRQ(5)
-#define IRQ_BBUS_SERCRX BBUS_IRQ(6)
-#define IRQ_BBUS_SERCTX BBUS_IRQ(7)
-#define IRQ_BBUS_SERDRX BBUS_IRQ(8)
-#define IRQ_BBUS_SERDTX BBUS_IRQ(9)
-#define IRQ_BBUS_I2C BBUS_IRQ(10)
-#define IRQ_BBUS_1284 BBUS_IRQ(11)
-#define IRQ_BBUS_UTIL BBUS_IRQ(12)
-#define IRQ_BBUS_RTC BBUS_IRQ(13)
-#define IRQ_BBUS_USBHST BBUS_IRQ(14)
-#define IRQ_BBUS_USBDEV BBUS_IRQ(15)
-#define IRQ_BBUS_AHBDMA1 BBUS_IRQ(24)
-#define IRQ_BBUS_AHBDMA2 BBUS_IRQ(25)
+#define IRQ_NS9360_BBUDMA(irq) (58 + irq)
/*
* these Interrupts are specific for the a9m9750dev board.
* They are generated by an FPGA that interrupts the CPU on
* IRQ_NS9360_EXT2
*/
-#define FPGA_IRQ(irq) (64 + irq)
+#define FPGA_IRQ(irq) (74 + irq)
#define IRQ_FPGA_UARTA FPGA_IRQ(0)
#define IRQ_FPGA_UARTB FPGA_IRQ(1)
@@ -81,6 +117,6 @@
#define IRQ_FPGA_CAN0 FPGA_IRQ(6)
#define IRQ_FPGA_CAN1 FPGA_IRQ(7)
-#define NR_IRQS 72
+#define NR_IRQS 82
#endif /* __ASM_ARCH_IRQS_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/module.h b/arch/arm/mach-ns9xxx/include/mach/module.h
index f851a6b7da6c..6c497bf87084 100644
--- a/arch/arm/mach-ns9xxx/include/mach/module.h
+++ b/arch/arm/mach-ns9xxx/include/mach/module.h
@@ -31,6 +31,11 @@
|| machine_is_cc9p9215js() \
)
+#define module_is_ccw9p9215() (0 \
+ || machine_is_ccw9p9215() \
+ || machine_is_ccw9p9215js() \
+ )
+
#define module_is_cc9p9360() (0 \
|| machine_is_a9m9360() \
|| machine_is_cc9p9360dev() \
@@ -49,6 +54,11 @@
|| machine_is_ccw9c() \
)
+#define module_is_cme9210() (0 \
+ || machine_is_cme9210() \
+ || machine_is_cme9210js() \
+ )
+
#define module_is_inc20otter() (0 \
|| machine_is_inc20otter() \
)
diff --git a/arch/arm/mach-ns9xxx/include/mach/ns921x-serial.h b/arch/arm/mach-ns9xxx/include/mach/ns921x-serial.h
new file mode 100644
index 000000000000..e879492fe10a
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/ns921x-serial.h
@@ -0,0 +1,22 @@
+/*
+ * include/linux/ns9360.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __NS921X_SERIAL_H__
+#define __NS921X_SERIAL_H__
+
+
+struct ns921x_uart_data {
+ unsigned int gpios[8];
+ unsigned int nr_gpios;
+ unsigned int rtsen; /* RTS for 485 transceiver control */
+};
+
+#endif
diff --git a/arch/arm/mach-ns9xxx/include/mach/ns9360fb.h b/arch/arm/mach-ns9xxx/include/mach/ns9360fb.h
new file mode 100644
index 000000000000..cacb56c17997
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/ns9360fb.h
@@ -0,0 +1,26 @@
+#ifndef __NS9360FB_H__
+#define __NS9360FB_H__
+
+
+struct ns9360fb_display {
+
+ /* Display name */
+ char *display_name;
+
+ unsigned height;
+ unsigned width;
+ unsigned clock;
+
+ u32 timing[4];
+ u32 control;
+
+ void (*display_power_enable)(int);
+};
+
+struct ns9360fb_pdata {
+ unsigned num_displays; /* number of defined displays */
+ struct ns9360fb_display *displays; /* attached diplays info */
+ struct ns9360fb_display *display; /* attached diplays info */
+};
+
+#endif
diff --git a/arch/arm/mach-ns9xxx/include/mach/ns9xxx-pwm.h b/arch/arm/mach-ns9xxx/include/mach/ns9xxx-pwm.h
new file mode 100644
index 000000000000..b48527482cd3
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/ns9xxx-pwm.h
@@ -0,0 +1,146 @@
+/*
+ * drivers/pwm/ns9xxx-pwm.h
+ *
+ * Copyright (C) 2009 Digi International 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 __NS9XXX_PWM_HEADER_H
+#define __NS9XXX_PWM_HEADER_H
+
+/* Maximal number of available channels */
+#define NS9XXX_PWM_CHANNEL_MAX 10
+
+/* This is the data for the PWM channels */
+struct ns9xxx_pwm_channel {
+ int timer;
+ int gpio;
+
+ /* Additional channel configuration variables ... */
+};
+
+/* */
+struct ns9xxx_pwm_pdata {
+ unsigned int number_channels;
+ struct ns9xxx_pwm_channel *channels;
+};
+
+
+
+
+#define NS921X_TMC_T0E (1 << 0) /* Timer enable */
+#define NS921X_TMC_T1E (1 << 1)
+#define NS921X_TMC_T2E (1 << 2)
+#define NS921X_TMC_T3E (1 << 3)
+#define NS921X_TMC_T4E (1 << 4)
+#define NS921X_TMC_T5E (1 << 5)
+#define NS921X_TMC_T6E (1 << 6)
+#define NS921X_TMC_T7E (1 << 7)
+#define NS921X_TMC_T8E (1 << 8)
+#define NS921X_TMC_T9E (1 << 9)
+#define NS921X_TMC_T6HSE (1 << 10) /* High step enable */
+#define NS921X_TMC_T6LSE (1 << 11) /* Low step enable */
+#define NS921X_TMC_T6RSE (1 << 12) /* Reload step enable */
+#define NS921X_TMC_T7HSE (1 << 13)
+#define NS921X_TMC_T7LSE (1 << 14)
+#define NS921X_TMC_T7RSE (1 << 15)
+#define NS921X_TMC_T8HSE (1 << 16)
+#define NS921X_TMC_T8LSE (1 << 17)
+#define NS921X_TMC_T8RSE (1 << 18)
+#define NS921X_TMC_T9HSE (1 << 19)
+#define NS921X_TMC_T9LSE (1 << 20)
+#define NS921X_TMC_T9RSE (1 << 21)
+
+#define NS921X_TCR_RELOADEN (1 << 0) /* Reload enable */
+#define NS921X_TCR_BITTIMER (1 << 1) /* 32 or 16 bit timer */
+#define NS921X_TCR_UPDOWN (1 << 2) /* Up/Down select */
+#define NS921X_TCR_INTSEL (1 << 3) /* Interrupt select */
+
+#define NS921X_TCR_TMODE(x) ((x) & (0x3 << 4)) /* Timer mode */
+#define NS921X_TCR_TMODE_MASK (0x3 << 4)
+#define NS921X_TCR_TMODE_INT (0 << 4)
+#define NS921X_TCR_TMODE_EXTLOW (1 << 4)
+#define NS921X_TCR_TMODE_EXTHIGH (2 << 4)
+/* @TODO: Complete the remaining macros */
+//#define NS921X_TCR_TMODE (2 << 4) /* Timer mode */
+
+#define NS921X_TCR_TCLKSEL (0xf << 6) /* Timer clock set */
+#define NS921X_TCR_TCLKSEL_MASK (0xf << 6)
+#define NS921X_TCR_TCLKSEL_AHB (0x1 << 6)
+#define NS921X_TCR_INTCLR (1 << 10) /* Interrupt clear */
+#define NS921X_TCR_DEBUGMODE (1 << 11) /* Debug mode */
+#define NS921X_TCR_CAPCOMP (7 << 12) /* Capture and compare mode functions */
+#define NS921X_TCR_TE (1 << 15) /* Timer enable */
+#define NS921X_TCR_TMODE2 (3 << 16) /* Timer mode 2 */
+#define NS921X_TCR_TMODE2_PWM (1 << 16) /* Timer mode 2 */
+
+#define NS921X_TCR_RELOADMODE2 (1 << 18) /* Reload mode */
+
+#define NS921X_THIGHREG (32 << 0) /* PWM output toggles high when timer counter reaches this value */
+#define NS921X_TLOWREG (32 << 0) /* PWM toggles low when counter reaches this value */
+
+#define NS921X_HISTEP_DIR (1 << 31) /* High step direction */
+#define NS921X_HISTEP (15 << 16) /* High step */
+#define NS921X_LOWSTEP_DIR (1 << 15) /* Low step direction */
+#define NS921X_LOWSTEP (15 << 0) /* Low step */
+
+#define NS921X_RELOADSTEPDIR (1 << 15) /* Reload step direction */
+#define NS921X_RELOADSTEP (15 << 0) /* Reload step */
+
+#define NS921X_TIMECOMP (16 << 16) /* Timer compare register or timer reload bits 31:16 count register */
+#define NS921X_TIMERELOADBITS (16 << 0) /* Timer reload bits 15:00 count register */
+
+#define NS921X_TIMERCAP (16 << 16) /* Timer capture register or timer read bits 31:16 register */
+#define NS921X_TIMERREAD (16 << 0) /* Timer read bits 15:00 register */
+
+
+
+#if 0
+struct pwm_channel {
+ void __iomem *regs;
+ unsigned index;
+ unsigned long mck;
+};
+
+extern int pwm_channel_alloc(int index, struct pwm_channel *ch);
+extern int pwm_channel_free(struct pwm_channel *ch);
+
+extern int pwm_clk_alloc(unsigned prescale, unsigned div);
+extern void pwm_clk_free(unsigned clk);
+
+extern int __pwm_channel_onoff(struct pwm_channel *ch, int enabled);
+
+#define pwm_channel_enable(ch) __pwm_channel_onoff((ch), 1)
+#define pwm_channel_disable(ch) __pwm_channel_onoff((ch), 0)
+
+/* periodic interrupts, mostly for CUPD changes to period or cycle */
+extern int pwm_channel_handler(struct pwm_channel *ch,
+ void (*handler)(struct pwm_channel *ch));
+
+static inline void
+pwm_channel_writel(struct pwm_channel *pwmc, unsigned offset, u32 val)
+{
+ __raw_writel(val, pwmc->regs + offset);
+}
+
+static inline u32 pwm_channel_readl(struct pwm_channel *pwmc, unsigned offset)
+{
+ return __raw_readl(pwmc->regs + offset);
+}
+
+#endif /* __LINUX_NS9XXX_PWM_H */
+
+#endif /* __NS9XXX_PWM_HEADER_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/processor.h b/arch/arm/mach-ns9xxx/include/mach/processor.h
index 9f77f746a386..a36ce171b4e3 100644
--- a/arch/arm/mach-ns9xxx/include/mach/processor.h
+++ b/arch/arm/mach-ns9xxx/include/mach/processor.h
@@ -16,12 +16,14 @@
#define processor_is_ns9210() (0 \
|| module_is_cc7ucamry() \
|| module_is_cc9p9210() \
+ || module_is_cme9210() \
|| module_is_inc20otter() \
|| module_is_otter() \
)
#define processor_is_ns9215() (0 \
|| module_is_cc9p9215() \
+ || module_is_ccw9p9215() \
)
#define processor_is_ns9360() (0 \
diff --git a/arch/arm/mach-ns9xxx/include/mach/regs-bbu.h b/arch/arm/mach-ns9xxx/include/mach/regs-bbu.h
index af227c058fb9..564498990063 100644
--- a/arch/arm/mach-ns9xxx/include/mach/regs-bbu.h
+++ b/arch/arm/mach-ns9xxx/include/mach/regs-bbu.h
@@ -8,38 +8,51 @@
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
-#ifndef __ASM_ARCH_REGSBBU_H
-#define __ASM_ARCH_REGSBBU_H
+#ifndef __ASM_ARCH_REGS_BBU_H
+#define __ASM_ARCH_REGS_BBU_H
#include <mach/hardware.h>
/* BBus Utility */
+#define NS9360_BBU_MSR __REG(0x90600000)
+
/* GPIO Configuration Registers block 1 */
/* NOTE: the HRM starts counting at 1 for the GPIO registers, here the start is
- * at 0 for each block. That is, BBU_GCONFb1(0) is GPIO Configuration Register
- * #1, BBU_GCONFb2(0) is GPIO Configuration Register #8. */
-#define BBU_GCONFb1(x) __REG2(0x90600010, (x))
-#define BBU_GCONFb2(x) __REG2(0x90600100, (x))
-
-#define BBU_GCONFx_DIR(m) __REGBIT(3 + (((m) & 7) << 2))
-#define BBU_GCONFx_DIR_INPUT(m) __REGVAL(BBU_GCONFx_DIR(m), 0)
-#define BBU_GCONFx_DIR_OUTPUT(m) __REGVAL(BBU_GCONFx_DIR(m), 1)
-#define BBU_GCONFx_INV(m) __REGBIT(2 + (((m) & 7) << 2))
-#define BBU_GCONFx_INV_NO(m) __REGVAL(BBU_GCONFx_INV(m), 0)
-#define BBU_GCONFx_INV_YES(m) __REGVAL(BBU_GCONFx_INV(m), 1)
-#define BBU_GCONFx_FUNC(m) __REGBITS(1 + (((m) & 7) << 2), ((m) & 7) << 2)
-#define BBU_GCONFx_FUNC_0(m) __REGVAL(BBU_GCONFx_FUNC(m), 0)
-#define BBU_GCONFx_FUNC_1(m) __REGVAL(BBU_GCONFx_FUNC(m), 1)
-#define BBU_GCONFx_FUNC_2(m) __REGVAL(BBU_GCONFx_FUNC(m), 2)
-#define BBU_GCONFx_FUNC_3(m) __REGVAL(BBU_GCONFx_FUNC(m), 3)
-
-#define BBU_GCTRL1 __REG(0x90600030)
-#define BBU_GCTRL2 __REG(0x90600034)
-#define BBU_GCTRL3 __REG(0x90600120)
-
-#define BBU_GSTAT1 __REG(0x90600040)
-#define BBU_GSTAT2 __REG(0x90600044)
-#define BBU_GSTAT3 __REG(0x90600130)
-
-#endif /* ifndef __ASM_ARCH_REGSBBU_H */
+ * at 0 for each block. That is, NS9360_BBU_GCONFb1(0) is GPIO Configuration
+ * Register #1, NS9360_BBU_GCONFb2(0) is GPIO Configuration Register #8. */
+#define NS9360_BBU_GCONFb1(x) __REG2(0x90600010, (x))
+#define NS9360_BBU_GCONFb2(x) __REG2(0x90600100, (x))
+
+#define __NS9360_BBU_GCONFx_SHIFT(m) (((m) & 7) << 2)
+
+#define NS9360_BBU_GCONFx_DIR(m) __REGBIT_SHIFT(3, __NS9360_BBU_GCONFx_SHIFT(m))
+#define NS9360_BBU_GCONFx_DIR_INPUT(m) __REGVAL(NS9360_BBU_GCONFx_DIR(m), 0)
+#define NS9360_BBU_GCONFx_DIR_OUTPUT(m) __REGVAL(NS9360_BBU_GCONFx_DIR(m), 1)
+#define NS9360_BBU_GCONFx_INV(m) __REGBIT_SHIFT(2, __NS9360_BBU_GCONFx_SHIFT(m))
+#define NS9360_BBU_GCONFx_INV_NO(m) __REGVAL(NS9360_BBU_GCONFx_INV(m), 0)
+#define NS9360_BBU_GCONFx_INV_YES(m) __REGVAL(NS9360_BBU_GCONFx_INV(m), 1)
+#define NS9360_BBU_GCONFx_FUNC(m) __REGBITS_SHIFT(1, 0, __NS9360_BBU_GCONFx_SHIFT(m))
+#define NS9360_BBU_GCONFx_FUNC_0(m) __REGVAL(NS9360_BBU_GCONFx_FUNC(m), 0)
+#define NS9360_BBU_GCONFx_FUNC_1(m) __REGVAL(NS9360_BBU_GCONFx_FUNC(m), 1)
+#define NS9360_BBU_GCONFx_FUNC_2(m) __REGVAL(NS9360_BBU_GCONFx_FUNC(m), 2)
+#define NS9360_BBU_GCONFx_FUNC_3(m) __REGVAL(NS9360_BBU_GCONFx_FUNC(m), 3)
+
+#define NS9360_BBU_GCTRL1 __REG(0x90600030)
+#define NS9360_BBU_GCTRL2 __REG(0x90600034)
+#define NS9360_BBU_GCTRL3 __REG(0x90600120)
+
+#define NS9360_BBU_GSTAT1 __REG(0x90600040)
+#define NS9360_BBU_GSTAT2 __REG(0x90600044)
+#define NS9360_BBU_GSTAT3 __REG(0x90600130)
+
+#define NS9360_BBUS_USB __REG(0x90600070)
+
+#define NS9360_BBUS_DMA_ISTAT __REG(0x90600060)
+#define NS9360_BBUS_DMA_IEN __REG(0x90600064)
+
+#define NS9360_BBUS_ISTAT __REG(0xa0401000)
+#define NS9360_BBUS_IEN __REG(0xa0401004)
+#define NS9360_BBUS_IEN_GLBL (1 << 31)
+
+#endif /* ifndef __ASM_ARCH_REGS_BBU_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/regs-io-ns921x.h b/arch/arm/mach-ns9xxx/include/mach/regs-io-ns921x.h
new file mode 100644
index 000000000000..948a273a9621
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/regs-io-ns921x.h
@@ -0,0 +1,41 @@
+/*
+ * arch/arm/mach-ns9xxx/include/mach/regs-io-ns921x.h
+ *
+ * Copyright (C) 2007-2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef __ASM_ARCH_REGSIONS921X_H
+#define __ASM_ARCH_REGSIONS921X_H
+
+#include <mach/hardware.h>
+
+#define __NS921X_IO_GPIOCONF_SHIFT(m) (((m) & 3) << 3)
+
+/* I/O Control Module */
+
+/* NOTE: the first GPIO has number #0, this is different from NS9360 which
+ * starts at #1 */
+
+/* GPIO Configuration Register */
+#define NS921X_IO_GPIOCONFx(x) __REG2(0xa0902000, (x))
+
+#define NS921X_IO_GPIOCONFx_FUNC(m) __REGBITS_SHIFT(5, 3, __NS921X_IO_GPIOCONF_SHIFT(m))
+#define NS921X_IO_GPIOCONFx_DIR(m) __REGBIT(2 + __NS921X_IO_GPIOCONF_SHIFT(m))
+#define NS921X_IO_GPIOCONFx_DIR_IN(m) __REGVAL(NS921X_IO_GPIOCONF_DIR(m), 0)
+#define NS921X_IO_GPIOCONFx_DIR_OUT(m) __REGVAL(NS921X_IO_GPIOCONF_DIR(m), 1)
+#define NS921X_IO_GPIOCONFx_INV(m) __REGBIT(1 + __NS921X_IO_GPIOCONF_SHIFT(m))
+#define NS921X_IO_GPIOCONFx_INV_OFF(m) __REGVAL(NS921X_IO_GPIOCONF_INV(m), 0)
+#define NS921X_IO_GPIOCONFx_INV_ON(m) __REGVAL(NS921X_IO_GPIOCONF_INV(m), 1)
+#define NS921X_IO_GPIOCONFx_PUEN(m) __REGBIT(__NS921X_IO_GPIOCONF_SHIFT(m))
+#define NS921X_IO_GPIOCONFx_PUEN_EN(m) __REGVAL(NS921X_IO_GPIOCONF_PUEN(m), 0)
+#define NS921X_IO_GPIOCONFx_PUEN_DIS(m) __REGVAL(NS921X_IO_GPIOCONF_PUEN(m), 1)
+
+#define NS921X_IO_GPIOCTRLx(x) __REG2(0xa090206c, (x))
+
+#define NS921X_IO_GPIOSTATx(x) __REG2(0xa090207c, (x))
+
+#endif /* ifndef __ASM_ARCH_REGSSYSNS921X_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/regs-iohub-ns921x.h b/arch/arm/mach-ns9xxx/include/mach/regs-iohub-ns921x.h
new file mode 100644
index 000000000000..8b274bb8bcd8
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/regs-iohub-ns921x.h
@@ -0,0 +1,133 @@
+/*
+ * arch/arm/mach-ns9xxx/include/mach/regs-sys-ns921x.h
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * !Revision: $Revision: 1.0 $
+ * !Author: Luis Galdos
+ * !Desc:
+ * !References:
+ */
+
+#ifndef __ASM_ARCH_REGSIOHUB_NS921X_H
+#define __ASM_ARCH_REGSIOHUB_NS921X_H
+
+#include <mach/hardware.h>
+#include <mach/regs-sys-common.h>
+
+#define IOHUB_REG_BASE_PA (0x90000000)
+#define IOHUB_REG_BASE_VA io_p2v(IOHUB_REG_BASE_PA)
+#define IOHUB_REG_OFFSET (0x8000)
+#define IOHUB_FIM0_BASE_PA IOHUB_REG_BASE_PA
+#define IOHUB_FIM1_BASE_PA (IOHUB_REG_BASE_PA + IOHUB_REG_OFFSET)
+#define IOHUB_FIM0_BASE_VA io_p2v(IOHUB_FIM0_BASE_PA)
+#define IOHUB_FIM1_BASE_VA io_p2v(IOHUB_FIM1_BASE_PA)
+
+/* Register offsets for the IOHUB-components */
+#define IOHUB_IFS_REG (0x00)
+
+#define IOHUB_RX_DMA_CTRL_REG (0x04)
+#define IOHUB_RX_DMA_BUFPTR_REG (0x08)
+#define IOHUB_RX_DMA_ICTRL_REG (0x0C)
+#define IOHUB_RX_DIR_REG (0x10)
+#define IOHUB_RX_DIR_FIFO_REG (0x14)
+#define IOHUB_TX_DMA_CTRL_REG (0x18)
+#define IOHUB_TX_DMA_BUFPTR_REG (0x1C)
+#define IOHUB_TX_ICTRL_REG (0x20)
+#define IOHUB_TX_FIFO_REG (0x28)
+#define IOHUB_TX_DIR_REG (0x2C)
+
+/* DMA RX control bits */
+#define IOHUB_RX_DMA_CTRL_CE (1<<31)
+#define IOHUB_RX_DMA_CTRL_CA (1<<30)
+#define IOHUB_RX_DMA_CTRL_FLEXIO (1<<29)
+#define IOHUB_RX_DMA_CTRL_DIRECT (1<<28)
+#define IOHUB_RX_DMA_CTRL_STATE (0xFC00)
+#define IOHUB_RX_DMA_CTRL_INDEX (0x03FF)
+
+
+/* DMA TX control bits */
+#define IOHUB_TX_DMA_CTRL_CE (1<<31)
+#define IOHUB_TX_DMA_CTRL_CA (1<<30)
+#define IOHUB_TX_DMA_CTRL_FLEXIO (1<<29)
+#define IOHUB_TX_DMA_CTRL_DIRECT (1<<28)
+#define IOHUB_TX_DMA_CTRL_STATE (0xFC00)
+#define IOHUB_TX_DMA_CTRL_INDEXEN (1<<27)
+#define IOHUB_TX_DMA_CTRL_INDEX(i) (i & 0x03FF)
+
+
+
+/* Interrupt and FIFO status register */
+#define IOHUB_IFS_RXNCIP (1<<31)
+#define IOHUB_IFS_RXECIP (1<<30)
+#define IOHUB_IFS_RXNRIP (1<<29)
+#define IOHUB_IFS_RXCAIP (1<<28)
+#define IOHUB_IFS_TXNCIP (1<<24)
+#define IOHUB_IFS_TXECIP (1<<23)
+#define IOHUB_IFS_TXNRIP (1<<22)
+#define IOHUB_IFS_TXCAIP (1<<21)
+#define IOHUB_IFS_TXFUFIP (1<<20)
+#define IOHUB_IFS_TXFSRIP (1<<19)
+#define IOHUB_IFS_MODIP (1<<18)
+#define IOHUB_IFS_DMA_TX (IOHUB_IFS_TXNCIP | \
+ IOHUB_IFS_TXNRIP | \
+ IOHUB_IFS_TXECIP | \
+ IOHUB_IFS_TXCAIP)
+#define IOHUB_IFS_DMA_RX (IOHUB_IFS_RXNCIP | \
+ IOHUB_IFS_RXNRIP | \
+ IOHUB_IFS_RXECIP | \
+ IOHUB_IFS_RXCAIP)
+
+/* Interrupt configuration register */
+#define IOHUB_ICTRL_RXTHRS(val) (val<<28)
+#define IOHUB_ICTRL_RXFOFIE (1<<26)
+#define IOHUB_ICTRL_RXFSRIE (1<<25)
+#define IOHUB_ICTRL_RXNCIE (1<<24)
+#define IOHUB_ICTRL_RXECIE (1<<23)
+#define IOHUB_ICTRL_RXNRIE (1<<22)
+#define IOHUB_ICTRL_RXCAIE (1<<21)
+#define IOHUB_ICTRL_RXPCIE (1<<20)
+#define IOHUB_ICTRL_WSTAT (1<<19)
+#define IOHUB_ICTRL_ISTAT (1<<18)
+#define IOHUB_ICTRL_LSTAT (1<<17)
+#define IOHUB_ICTRL_FSTAT (1<<16)
+#define IOHUB_ICTRL_BLENSTAT (0xFF)
+#define IOHUB_ICTRL_RXALLE (IOHUB_ICTRL_RXFOFIE | \
+ IOHUB_ICTRL_RXFSRIE | \
+ IOHUB_ICTRL_RXNCIE | \
+ IOHUB_ICTRL_RXECIE | \
+ IOHUB_ICTRL_RXNRIE | \
+ IOHUB_ICTRL_RXCAIE | \
+ IOHUB_ICTRL_RXPCIE)
+
+
+#define IOHUB_ICTRL_TXTHRS(val) (val<<28)
+#define IOHUB_ICTRL_TXFUFIE (1<<26)
+#define IOHUB_ICTRL_TXFSRIE (1<<25)
+#define IOHUB_ICTRL_TXNCIE (1<<24)
+#define IOHUB_ICTRL_TXECIE (1<<23)
+#define IOHUB_ICTRL_TXNRIE (1<<22)
+#define IOHUB_ICTRL_TXCAIE (1<<21)
+#define IOHUB_ICTRL_WSTAT (1<<19)
+#define IOHUB_ICTRL_ISTAT (1<<18)
+#define IOHUB_ICTRL_LSTAT (1<<17)
+#define IOHUB_ICTRL_FSTAT (1<<16)
+#define IOHUB_ICTRL_BLENSTAT (0xFF)
+#define IOHUB_ICTRL_TXALLE (IOHUB_ICTRL_TXFUFIE | \
+ IOHUB_ICTRL_TXFSRIE | \
+ IOHUB_ICTRL_TXNCIE | \
+ IOHUB_ICTRL_TXECIE | \
+ IOHUB_ICTRL_TXNRIE | \
+ IOHUB_ICTRL_TXCAIE)
+
+
+
+#endif /* ifndef __ASM_ARCH_REGSIOHUB_NS921X_H */
+
+
+
diff --git a/arch/arm/mach-ns9xxx/include/mach/regs-lcd-ns9360.h b/arch/arm/mach-ns9xxx/include/mach/regs-lcd-ns9360.h
new file mode 100644
index 000000000000..7beabc4729bc
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/regs-lcd-ns9360.h
@@ -0,0 +1,88 @@
+/*
+ * arch/arm/mach-ns9xxx/include/mach/regs-lcd-ns9360.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __ASM_ARCH_REGSLCDNS9360_H
+#define __ASM_ARCH_REGSLCDNS9360_H
+
+#define LCD_TIMING0 0x00
+#define LCD_TIMING0_HBP(x) (((x) & 0xFF) << 24)
+#define LCD_TIMING0_HFP(x) (((x) & 0xFF) << 16)
+#define LCD_TIMING0_HSW(x) (((x) & 0xFF) << 8)
+#define LCD_TIMING0_PPL(x) (((x) & 0x3F) << 2)
+
+#define LCD_TIMING1 0x04
+#define LCD_TIMING1_VBP(x) (((x) & 0xFF) << 24)
+#define LCD_TIMING1_VFP(x) (((x) & 0xFF) << 16)
+#define LCD_TIMING1_VSW(x) (((x) & 0x3F) << 10)
+#define LCD_TIMING1_LPP(x) (((x) & 0x3FF))
+
+#define LCD_TIMING2 0x08
+#define LCD_TIMING2_BCD (1 << 26)
+#define LCD_TIMING2_CPL(x) (((x) & 0x3FF) << 16)
+#define LCD_TIMING2_IOE (1 << 14)
+#define LCD_TIMING2_IPC (1 << 13)
+#define LCD_TIMING2_IHS (1 << 12)
+#define LCD_TIMING2_IVS (1 << 11)
+#define LCD_TIMING2_ACB(x) (((x) & 0x1F) << 6)
+#define LCD_TIMING2_PCD(x) (((x) & 0x1F))
+
+#define LCD_TIMING3 0x0c
+#define LCD_TIMING3_LEE (1 << 16)
+#define LCD_TIMING3_LED(x) (((x) & 0x7F))
+
+#define LCD_UPBASE 0x10
+#define LCD_UPBASE_V(x) (((x) & 0x3FFFFFFF)<<2)
+
+#define LCD_LPBASE 0x14
+#define LCD_PBASE_V(x) (((x) & 0x3FFFFFFF)<<2)
+
+#define LCD_IRENABLE 0x18
+#define LCD_IRENABLE_MBERR (1 << 3)
+#define LCD_IRENABLE_VCOMP (1 << 2)
+#define LCD_IRENABLE_LNBU (1 << 1)
+
+#define LCD_CONTROL 0x1c
+#define LCD_CONTROL_WATERMARK (1 << 16)
+#define LCD_CONTROL_VCOMP_MA (3 << 12)
+#define LCD_CONTROL_VCOMP_VS (0 << 12)
+#define LCD_CONTROL_VCOMP_BP (1 << 12)
+#define LCD_CONTROL_VCOMP_AV (2 << 12)
+#define LCD_CONTROL_VCOMP_FP (3 << 12)
+#define LCD_CONTROL_PWR (1 << 11)
+#define LCD_CONTROL_BEPO (1 << 10)
+#define LCD_CONTROL_BEBO (1 << 9)
+#define LCD_CONTROL_BGR (1 << 8)
+#define LCD_CONTROL_DUAL (1 << 7)
+#define LCD_CONTROL_MONO8 (1 << 6)
+#define LCD_CONTROL_TFT (1 << 5)
+#define LCD_CONTROL_BW (1 << 4)
+#define LCD_CONTROL_BPP_MA (7 << 1)
+#define LCD_CONTROL_BPP_1 (0 << 1)
+#define LCD_CONTROL_BPP_2 (1 << 1)
+#define LCD_CONTROL_BPP_4 (2 << 1)
+#define LCD_CONTROL_BPP_8 (3 << 1)
+#define LCD_CONTROL_BPP_16 (4 << 1)
+#define LCD_CONTROL_EN (1 << 0)
+
+#define LCD_STATUS 0x20
+#define LCD_STATUS_MBERROR (4 << 1)
+#define LCD_STATUS_VCOMP (3 << 1)
+#define LCD_STATUS_LNBU (2 << 1)
+
+#define LCD_INTR 0x24
+#define LCD_INTR_MBERROR (4 << 1)
+#define LCD_INTR_VCOMP (3 << 1)
+#define LCD_INTR_LNBUINTR (2 << 1)
+
+#define LCD_UPCURRENT 0x28
+#define LCD_LPCURRENT 0x2c
+
+#endif
diff --git a/arch/arm/mach-ns9xxx/include/mach/regs-sys-common.h b/arch/arm/mach-ns9xxx/include/mach/regs-sys-common.h
index 14f91dfd5736..3041d3f6f44a 100644
--- a/arch/arm/mach-ns9xxx/include/mach/regs-sys-common.h
+++ b/arch/arm/mach-ns9xxx/include/mach/regs-sys-common.h
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-ns9xxx/include/mach/regs-sys-common.h
*
- * Copyright (C) 2007 by Digi International Inc.
+ * Copyright (C) 2007,2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -18,6 +18,12 @@
/* Interrupt Configuration registers */
#define SYS_IC(x) __REG2(0xa0900144, (x))
+#define __SYS_IC_FIELDNUM(i) (3 - ((i) & 3))
+#define __SYS_IC_SHIFT(i) (((i) & 3) << 3)
+#define SYS_IC_IE(i) __REGBIT(7 + __SYS_IC_SHIFT(i))
+#define SYS_IC_IE_EN(i) __REGVAL(SYS_IC_IE(i), 1)
+#define SYS_IC_IE_DIS(i) __REGVAL(SYS_IC_IE(i), 0)
+#define SYS_IC_ISD(i) __REGBITS_SHIFT(4, 0, __SYS_IC_SHIFT(i))
/* ISRADDR */
#define SYS_ISRADDR __REG(0xa0900164)
@@ -28,4 +34,43 @@
/* Interrupt Status Raw */
#define SYS_ISR __REG(0xa090016c)
+/* Active Interrupt Level ID Status register */
+#define SYS_AILID __REG(0xa090018c)
+
+/* System Memory Chip Select x Dynamic Memory Base */
+#define SYS_SMCSDMB(x) __REG2(0xa09001d0, (x) << 1)
+
+/* System Memory Chip Select x Dynamic Memory Mask */
+#define SYS_SMCSDMM(x) __REG2(0xa09001d4, (x) << 1)
+
+/* System Memory Chip Select x Static Memory Base */
+#define SYS_SMCSSMB(x) __REG2(0xa09001f0, (x) << 1)
+
+/* System Memory Chip Select x Static Memory Base: Chip select x base */
+#define SYS_SMCSSMB_CSxB __REGBITS(31, 12)
+
+/* System Memory Chip Select x Static Memory Mask */
+#define SYS_SMCSSMM(x) __REG2(0xa09001f4, (x) << 1)
+
+/* System Memory Chip Select x Static Memory Mask: Chip select x mask */
+#define SYS_SMCSSMM_CSxM __REGBITS(31, 12)
+
+/* System Memory Chip Select x Static Memory Mask: Chip select x enable */
+#define SYS_SMCSSMM_CSEx __REGBIT(0)
+#define SYS_SMCSSMM_CSEx_DIS __REGVAL(SYS_SMCSSMM_CSEx, 0)
+#define SYS_SMCSSMM_CSEx_EN __REGVAL(SYS_SMCSSMM_CSEx, 1)
+
+/* General purpose, user-defined ID register */
+#define SYS_GENID __REG(0xa0900210)
+
+
+#define SYS_EIxCTRL(i) __REG2(0xa0900214, (i))
+#define SYS_EIxCTRL_CLEAR __REGBIT(2)
+#define SYS_EIxCTRL_PLTY __REGBIT(1)
+#define SYS_EIxCTRL_PLTY_HIGH __REGVAL(SYS_EIxCTRL_PLTY, 0)
+#define SYS_EIxCTRL_PLTY_LOW __REGVAL(SYS_EIxCTRL_PLTY, 1)
+#define SYS_EIxCTRL_TYPE __REGBIT(0)
+#define SYS_EIxCTRL_TYPE_LEVEL __REGVAL(SYS_EIxCTRL_TYPE, 0)
+#define SYS_EIxCTRL_TYPE_EDGE __REGVAL(SYS_EIxCTRL_TYPE, 1)
+
#endif /* ifndef __ASM_ARCH_REGSSYSCOMMON_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/regs-sys-ns921x.h b/arch/arm/mach-ns9xxx/include/mach/regs-sys-ns921x.h
new file mode 100644
index 000000000000..a8e4410806c6
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/regs-sys-ns921x.h
@@ -0,0 +1,154 @@
+/*
+ * arch/arm/mach-ns9xxx/include/mach/regs-sys-ns921x.h
+ *
+ * Copyright (C) 2007,2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef __ASM_ARCH_REGSSYSNS921X_H
+#define __ASM_ARCH_REGSSYSNS921X_H
+
+#include <mach/hardware.h>
+#include <mach/regs-sys-common.h>
+
+/* System Control Module */
+
+/* Timer Master Control */
+#define SYS_TMC __REG(0xa0900024)
+#define SYS_TMC_T9RSE __REGBIT(21)
+#define SYS_TMC_T9LSE __REGBIT(20)
+#define SYS_TMC_T9HSE __REGBIT(19)
+#define SYS_TMC_T8RSE __REGBIT(18)
+#define SYS_TMC_T8LSE __REGBIT(17)
+#define SYS_TMC_T8HSE __REGBIT(16)
+#define SYS_TMC_T7RSE __REGBIT(15)
+#define SYS_TMC_T7LSE __REGBIT(14)
+#define SYS_TMC_T7HSE __REGBIT(13)
+#define SYS_TMC_T6RSE __REGBIT(12)
+#define SYS_TMC_T6LSE __REGBIT(11)
+#define SYS_TMC_T6HSE __REGBIT(10)
+#define SYS_TMC_T9E __REGBIT(9)
+#define SYS_TMC_T8E __REGBIT(8)
+#define SYS_TMC_T7E __REGBIT(7)
+#define SYS_TMC_T6E __REGBIT(6)
+#define SYS_TMC_T5E __REGBIT(5)
+#define SYS_TMC_T4E __REGBIT(4)
+#define SYS_TMC_T3E __REGBIT(3)
+#define SYS_TMC_T2E __REGBIT(2)
+#define SYS_TMC_T1E __REGBIT(1)
+#define SYS_TMC_T0E __REGBIT(0)
+
+/* Timer x Reload Count and Compare register */
+#define SYS_TRCC(x) __REG2(0xa0900028, (x))
+
+/* Timer x Read and Capture register */
+#define SYS_TRC(x) __REG2(0xa0900050, (x))
+
+/* Interrupt Vector Address Register Level x */
+#define SYS_IVA(x) __REG2(0xa09000c4, (x))
+
+/* Clock Configuration register */
+#define SYS_CLOCK __REG(0xa090017c)
+#define SYS_CLOCK_CSC __REGBITS(31, 29)
+#define SYS_CLOCK_CSSEL __REGBIT(25)
+#define SYS_CLOCK_EXTDMA __REGBIT(14)
+#define SYS_CLOCK_RTC __REGBIT(12)
+#define SYS_CLOCK_I2C __REGBIT(11)
+#define SYS_CLOCK_AES __REGBIT(9)
+#define SYS_CLOCK_ADC __REGBIT(8)
+#define SYS_CLOCK_SPI __REGBIT(5)
+#define SYS_CLOCK_UARTx(i) __REGBIT(1 + (i))
+#define SYS_CLOCK_UARTD SYS_CLOCK_UARTx(3)
+#define SYS_CLOCK_UARTC SYS_CLOCK_UARTx(2)
+#define SYS_CLOCK_UARTB SYS_CLOCK_UARTx(1)
+#define SYS_CLOCK_UARTA SYS_CLOCK_UARTx(0)
+#define SYS_CLOCK_ETH __REGBIT(0)
+
+#define SYS_RESET __REG(0xa0900180)
+
+/* PLL Configuration register */
+#define SYS_PLL __REG(0xa0900188)
+#define SYS_PLL_NF __REGBITS(16, 8)
+#define SYS_PLL_BP __REGBIT(7)
+#define SYS_PLL_OD __REGBITS(6, 5)
+#define SYS_PLL_NR __REGBITS(4, 0)
+
+/* Timer x Control register */
+#define SYS_TC(x) __REG2(0xa0900190, (x))
+#define SYS_TCx_RELMODE __REGBIT(18)
+#define SYS_TCx_RELMODE_FULL __REGVAL(SYS_TCx_RELMODE, 0)
+#define SYS_TCx_RELMODE_HALF __REGVAL(SYS_TCx_RELMODE, 1)
+#define SYS_TCx_MODE2 __REGBITS(17, 16)
+#define SYS_TCx_MODE2_00 __REGVAL(SYS_TCx_MODE2, 0)
+#define SYS_TCx_MODE2_01 __REGVAL(SYS_TCx_MODE2, 1)
+#define SYS_TCx_MODE2_10 __REGVAL(SYS_TCx_MODE2, 2)
+#define SYS_TCx_MODE2_11 __REGVAL(SYS_TCx_MODE2, 3)
+#define SYS_TCx_TE __REGBIT(15)
+#define SYS_TCx_TE_DIS __REGVAL(SYS_TCx_TE, 0)
+#define SYS_TCx_TE_EN __REGVAL(SYS_TCx_TE, 1)
+#define SYS_TCx_CAPCOMP __REGBITS(14,12)
+#define SYS_TCx_CAPCOMP_000 __REGVAL(SYS_TCx_CAPCOMP, 0)
+#define SYS_TCx_DEBUG __REGBIT(11)
+#define SYS_TCx_DEBUG_CONT __REGVAL(SYS_TCx_DEBUG, 0)
+#define SYS_TCx_DEBUG_STOP __REGVAL(SYS_TCx_DEBUG, 1)
+#define SYS_TCx_INTCLR __REGBIT(10)
+#define SYS_TCx_TCS __REGBITS(9, 6)
+#define SYS_TCx_TCS_2AHB __REGVAL(SYS_TCx_TCS, 0)
+#define SYS_TCx_TCS_AHB __REGVAL(SYS_TCx_TCS, 1)
+#define SYS_TCx_MODE __REGBITS(5, 4)
+#define SYS_TCx_MODE_INTERNAL __REGVAL(SYS_TCx_MODE, 0)
+#define SYS_TCx_MODE_CONCA __REGVAL(SYS_TCx_MODE, 3)
+#define SYS_TCx_INTSEL __REGBIT(3)
+#define SYS_TCx_INTSEL_DIS __REGVAL(SYS_TCx_INTSEL, 0)
+#define SYS_TCx_INTSEL_EN __REGVAL(SYS_TCx_INTSEL, 1)
+#define SYS_TCx_UPDOWN __REGBIT(2)
+#define SYS_TCx_UPDOWN_UP __REGVAL(SYS_TCx_UPDOWN, 0)
+#define SYS_TCx_UPDOWN_DOWN __REGVAL(SYS_TCx_UPDOWN, 1)
+#define SYS_TCx_BITTIMER __REGBIT(1)
+#define SYS_TCx_BITTIMER_16 __REGVAL(SYS_TCx_BITTIMER, 0)
+#define SYS_TCx_BITTIMER_32 __REGVAL(SYS_TCx_BITTIMER, 1)
+#define SYS_TCx_RELENBL __REGBIT(0)
+#define SYS_TCx_RELENBL_DIS __REGVAL(SYS_TCx_RELENBL, 0)
+#define SYS_TCx_RELENBL_EN __REGVAL(SYS_TCx_RELENBL, 1)
+
+/* Timer Registers */
+#define SYS_THR(x) __REG2(0xa0900078, (x)) /* Timer 6-9 High Registers */
+#define SYS_TLR(x) __REG2(0xa0900088, (x)) /* Timer 6-9 Low Registers */
+#define SYS_THLSR(x) __REG2(0xa0900098, (x)) /* Timer 6-9 High Low Step Register */
+#define SYS_TRELSR(x) __REG2(0xa09000a8, (x)) /* Timer 6-9 Reload Step Register */
+#define SYS_TRELCCR(x) __REG2(0xa0900028, (x)) /* Timer 0-9 Reload Count and Compare Register */
+#define SYS_TRCR(x) __REG2(0xa0900050, (x)) /* Timer 0-9 Read and Capture Register */
+
+/* */
+#define SYS_RTCMC __REG(0xa0900224)
+#define SYS_RTCMC_SS __REGBIT(4)
+#define SYS_RTCMC_RIS __REGBIT(3)
+#define SYS_RTCMC_MIS __REGBIT(2)
+#define SYS_RTCMC_MODE __REGBIT(1)
+#define SYS_RTCMC_MODE_STANDBY __REGVAL(SYS_RTCMC_MODE, 0)
+#define SYS_RTCMC_MODE_NORMAL __REGVAL(SYS_RTCMC_MODE, 1)
+#define SYS_RTCMC_RIC __REGBIT(0)
+
+/* */
+#define SYS_POWER __REG(0xa0900228)
+#define SYS_POWER_SLFRFSH __REGBIT(21)
+#define SYS_POWER_INTCLR __REGBIT(20)
+#define SYS_POWER_EXTIRQx(i) __REGBIT(16 + (i))
+#define SYS_POWER_EXTIRQ3 SYS_POWER_EXTIRQx(3)
+#define SYS_POWER_EXTIRQ2 SYS_POWER_EXTIRQx(2)
+#define SYS_POWER_EXTIRQ1 SYS_POWER_EXTIRQx(1)
+#define SYS_POWER_EXTIRQ0 SYS_POWER_EXTIRQx(0)
+#define SYS_POWER_RTC SYS_CLOCK_RTC
+#define SYS_POWER_I2C SYS_CLOCK_I2C
+#define SYS_POWER_SPI SYS_CLOCK_SPI
+#define SYS_POWER_UARTx(i) SYS_CLOCK_UARTx(i)
+#define SYS_POWER_UARTD SYS_POWER_UARTx(3)
+#define SYS_POWER_UARTC SYS_POWER_UARTx(2)
+#define SYS_POWER_UARTB SYS_POWER_UARTx(1)
+#define SYS_POWER_UARTA SYS_POWER_UARTx(0)
+#define SYS_POWER_ETH SYS_CLOCK_ETH
+
+#endif /* ifndef __ASM_ARCH_REGSSYSNS921X_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/regs-sys-ns9360.h b/arch/arm/mach-ns9xxx/include/mach/regs-sys-ns9360.h
index 8ff254d9901c..30236e6fbc49 100644
--- a/arch/arm/mach-ns9xxx/include/mach/regs-sys-ns9360.h
+++ b/arch/arm/mach-ns9xxx/include/mach/regs-sys-ns9360.h
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-ns9xxx/include/mach/regs-sys-ns9360.h
*
- * Copyright (C) 2006,2007 by Digi International Inc.
+ * Copyright (C) 2006-2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -30,6 +30,12 @@
/* Timer Interrupt Status register */
#define SYS_TIS __REG(0xa0900170)
+#define SYS_CLOCK __REG(0xa090017c)
+#define SYS_CLOCK_LPCSEXT __REGBIT(9)
+
+#define SYS_RESET __REG(0xa0900180)
+#define SYS_RESET_LCDC __REGBIT(5)
+
/* PLL Configuration register */
#define SYS_PLL __REG(0xa0900188)
@@ -100,32 +106,6 @@
#define SYS_TCx_REN_DIS __REGVAL(SYS_TCx_REN, 0)
#define SYS_TCx_REN_EN __REGVAL(SYS_TCx_REN, 1)
-/* System Memory Chip Select x Dynamic Memory Base */
-#define SYS_SMCSDMB(x) __REG2(0xa09001d0, (x) << 1)
-
-/* System Memory Chip Select x Dynamic Memory Mask */
-#define SYS_SMCSDMM(x) __REG2(0xa09001d4, (x) << 1)
-
-/* System Memory Chip Select x Static Memory Base */
-#define SYS_SMCSSMB(x) __REG2(0xa09001f0, (x) << 1)
-
-/* System Memory Chip Select x Static Memory Base: Chip select x base */
-#define SYS_SMCSSMB_CSxB __REGBITS(31, 12)
-
-/* System Memory Chip Select x Static Memory Mask */
-#define SYS_SMCSSMM(x) __REG2(0xa09001f4, (x) << 1)
-
-/* System Memory Chip Select x Static Memory Mask: Chip select x mask */
-#define SYS_SMCSSMM_CSxM __REGBITS(31, 12)
-
-/* System Memory Chip Select x Static Memory Mask: Chip select x enable */
-#define SYS_SMCSSMM_CSEx __REGBIT(0)
-#define SYS_SMCSSMM_CSEx_DIS __REGVAL(SYS_SMCSSMM_CSEx, 0)
-#define SYS_SMCSSMM_CSEx_EN __REGVAL(SYS_SMCSSMM_CSEx, 1)
-
-/* General purpose, user-defined ID register */
-#define SYS_GENID __REG(0xa0900210)
-
/* External Interrupt x Control register */
#define SYS_EIC(x) __REG2(0xa0900214, (x))
@@ -145,4 +125,7 @@
#define SYS_EIC_LVEDG_LEVEL __REGVAL(SYS_EIC_LVEDG, 0)
#define SYS_EIC_LVEDG_EDGE __REGVAL(SYS_EIC_LVEDG, 1)
+/* RTC Clock Control register */
+#define SYS_RTCCC __REG(0xa0900224)
+
#endif /* ifndef __ASM_ARCH_REGSSYSNS9360_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/spi.h b/arch/arm/mach-ns9xxx/include/mach/spi.h
new file mode 100644
index 000000000000..801d01b838d6
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/include/mach/spi.h
@@ -0,0 +1,28 @@
+/*
+ * arch/arm/mach-ns9xxx/include/mach/spi.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+*/
+#ifndef __ASM_ARCH_SPI_H
+#define __ASM_ARCH_SPI_H
+
+#define SPI_MOSI_GPIO_OFFSET 0
+#define SPI_MISO_GPIO_OFFSET 1
+#define SPI_CLK_GPIO_OFFSET 2
+#define SPI_EN_GPIO_OFFSET 3
+
+
+#define SPI_MAX_GPIO 10
+
+struct spi_ns9xxx_data {
+ char gpios[SPI_MAX_GPIO];
+ char gpio_funcs[SPI_MAX_GPIO];
+ char nr_gpios;
+};
+
+#endif /* ifndef __ASM_ARCH_SPI_H */
diff --git a/arch/arm/mach-ns9xxx/include/mach/system.h b/arch/arm/mach-ns9xxx/include/mach/system.h
index e2068c57415f..e4ae479dc5b2 100644
--- a/arch/arm/mach-ns9xxx/include/mach/system.h
+++ b/arch/arm/mach-ns9xxx/include/mach/system.h
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-ns9xxx/include/mach/system.h
*
- * Copyright (C) 2006,2007 by Digi International Inc.
+ * Copyright (C) 2006-2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -13,15 +13,32 @@
#include <asm/proc-fns.h>
#include <mach/processor.h>
-#include <mach/processor-ns9360.h>
static inline void arch_idle(void)
{
- cpu_do_idle();
+ /*
+ * When a Camry (i.e. ns921x) executes
+ *
+ * mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt
+ *
+ * the CPU is stopped, so are all timers. This is not something I want
+ * to handle. As the "wait for interrupt" instruction is part of
+ * cpu_arm926_do_idle, it's not called for it.
+ */
+ if (!processor_is_ns921x())
+ cpu_do_idle();
}
+void ns921x_reset(char);
+void ns9360_reset(char);
+
static inline void arch_reset(char mode)
{
+#ifdef CONFIG_PROCESSOR_NS921X
+ if (processor_is_ns921x())
+ ns921x_reset(mode);
+ else
+#endif
#ifdef CONFIG_PROCESSOR_NS9360
if (processor_is_ns9360())
ns9360_reset(mode);
diff --git a/arch/arm/mach-ns9xxx/include/mach/timex.h b/arch/arm/mach-ns9xxx/include/mach/timex.h
index 734a8d8bd578..e901b4e10610 100644
--- a/arch/arm/mach-ns9xxx/include/mach/timex.h
+++ b/arch/arm/mach-ns9xxx/include/mach/timex.h
@@ -12,7 +12,7 @@
#define __ASM_ARCH_TIMEX_H
/*
- * value for CLOCK_TICK_RATE stolen from arch/arm/mach-s3c2410/include/mach/timex.h.
+ * value for CLOCK_TICK_RATE stolen from include/asm-arm/arch-s3c2410/timex.h.
* See there for an explanation.
*/
#define CLOCK_TICK_RATE 12000000
diff --git a/arch/arm/mach-ns9xxx/include/mach/uncompress.h b/arch/arm/mach-ns9xxx/include/mach/uncompress.h
index 1b12d324b087..fc9b0a424230 100644
--- a/arch/arm/mach-ns9xxx/include/mach/uncompress.h
+++ b/arch/arm/mach-ns9xxx/include/mach/uncompress.h
@@ -11,15 +11,21 @@
#ifndef __ASM_ARCH_UNCOMPRESS_H
#define __ASM_ARCH_UNCOMPRESS_H
-#include <linux/io.h>
-
-#define __REG(x) ((void __iomem __force *)(x))
+#include <asm/io.h>
+#define __REG(x) ((void __iomem __force *)(x))
static void putc_dummy(char c, void __iomem *base)
{
/* nothing */
}
+#if defined(CONFIG_PROCESSOR_NS9360)
+# define NS9360_UARTA __REG(0x90200040)
+# define NS9360_UARTB __REG(0x90200000)
+# define NS9360_UARTC __REG(0x90300000)
+# define NS9360_UARTD __REG(0x90300040)
+# define NS9360_UART_ENABLED(base) \
+ (__raw_readl(NS9360_UARTA) & (1 << 31))
static void putc_ns9360(char c, void __iomem *base)
{
static int t = 0x10000;
@@ -34,22 +40,16 @@ static void putc_ns9360(char c, void __iomem *base)
}
} while (t);
}
-
-static void putc_a9m9750dev(char c, void __iomem *base)
-{
- static int t = 0x10000;
- do {
- if (t)
- --t;
-
- if (__raw_readb(base + 5) & (1 << 5)) {
- __raw_writeb(c, base);
- t = 0x10000;
- break;
- }
- } while (t);
-
-}
+#endif
+
+#if defined(CONFIG_PROCESSOR_NS921X)
+# define NS921XSYS_CLOCK __REG(0xa090017c)
+# define NS921X_UARTA __REG(0x90010000)
+# define NS921X_UARTB __REG(0x90018000)
+# define NS921X_UARTC __REG(0x90020000)
+# define NS921X_UARTD __REG(0x90028000)
+# define NS921X_UART_ENABLED(base) \
+ (__raw_readl((base) + 0x1000) & (1 << 29))
static void putc_ns921x(char c, void __iomem *base)
{
@@ -66,79 +66,96 @@ static void putc_ns921x(char c, void __iomem *base)
} while (t);
}
-#define MSCS __REG(0xA0900184)
+#include "fim-uncompress.h"
-#define NS9360_UARTA __REG(0x90200040)
-#define NS9360_UARTB __REG(0x90200000)
-#define NS9360_UARTC __REG(0x90300000)
-#define NS9360_UARTD __REG(0x90300040)
+static void putc_ns921x_fim(char ch, void __iomem *base)
+{
+ unsigned int status;
+ unsigned short data = 1;
+ int pic = 0;
-#define NS9360_UART_ENABLED(base) \
- (__raw_readl(NS9360_UARTA) & (1 << 31))
+ if ('\n' == ch)
+ ch = '\r';
-#define A9M9750DEV_UARTA __REG(0x40000000)
+ data = (data << FIM_SERIAL_DATA_BITS) | (ch & ((1 << FIM_SERIAL_DATA_BITS) - 1));
-#define NS921XSYS_CLOCK __REG(0xa090017c)
-#define NS921X_UARTA __REG(0x90010000)
-#define NS921X_UARTB __REG(0x90018000)
-#define NS921X_UARTC __REG(0x90020000)
-#define NS921X_UARTD __REG(0x90028000)
+ /* Check if the PIC is tasked with another send-char request */
+ do {
+ status = fim_get_exp_reg(pic, 0);
+ } while (status & FIM_SERIAL_INT_INSERT_CHAR);
-#define NS921X_UART_ENABLED(base) \
- (__raw_readl((base) + 0x1000) & (1 << 29))
+ /* And send the char using the interrupt function */
+ fim_set_ctrl_reg(pic, 0, data & 0xFF);
+ fim_set_ctrl_reg(pic, 1, (data >> 8) & 0xFF);
+ fim_send_interrupt(pic, FIM_SERIAL_INT_INSERT_CHAR);
+}
+#endif
static void autodetect(void (**putc)(char, void __iomem *), void __iomem **base)
{
- if (((__raw_readl(MSCS) >> 16) & 0xfe) == 0x00) {
- /* ns9360 or ns9750 */
- if (NS9360_UART_ENABLED(NS9360_UARTA)) {
- *putc = putc_ns9360;
- *base = NS9360_UARTA;
- return;
- } else if (NS9360_UART_ENABLED(NS9360_UARTB)) {
- *putc = putc_ns9360;
- *base = NS9360_UARTB;
- return;
- } else if (NS9360_UART_ENABLED(NS9360_UARTC)) {
- *putc = putc_ns9360;
- *base = NS9360_UARTC;
- return;
- } else if (NS9360_UART_ENABLED(NS9360_UARTD)) {
- *putc = putc_ns9360;
- *base = NS9360_UARTD;
- return;
- } else if (__raw_readl(__REG(0xa09001f4)) == 0xfffff001) {
- *putc = putc_a9m9750dev;
- *base = A9M9750DEV_UARTA;
- return;
- }
- } else if (((__raw_readl(MSCS) >> 16) & 0xfe) == 0x02) {
- /* ns921x */
- u32 clock = __raw_readl(NS921XSYS_CLOCK);
-
- if ((clock & (1 << 1)) &&
- NS921X_UART_ENABLED(NS921X_UARTA)) {
- *putc = putc_ns921x;
- *base = NS921X_UARTA;
- return;
- } else if ((clock & (1 << 2)) &&
- NS921X_UART_ENABLED(NS921X_UARTB)) {
- *putc = putc_ns921x;
- *base = NS921X_UARTB;
- return;
- } else if ((clock & (1 << 3)) &&
- NS921X_UART_ENABLED(NS921X_UARTC)) {
- *putc = putc_ns921x;
- *base = NS921X_UARTC;
- return;
- } else if ((clock & (1 << 4)) &&
- NS921X_UART_ENABLED(NS921X_UARTD)) {
- *putc = putc_ns921x;
- *base = NS921X_UARTD;
- return;
- }
+#if defined(CONFIG_PROCESSOR_NS9360)
+ /* ns9360 */
+ if (NS9360_UART_ENABLED(NS9360_UARTA)) {
+ *putc = putc_ns9360;
+ *base = NS9360_UARTA;
+ return;
+ } else if (NS9360_UART_ENABLED(NS9360_UARTB)) {
+ *putc = putc_ns9360;
+ *base = NS9360_UARTB;
+ return;
+ } else if (NS9360_UART_ENABLED(NS9360_UARTC)) {
+ *putc = putc_ns9360;
+ *base = NS9360_UARTC;
+ return;
+ } else if (NS9360_UART_ENABLED(NS9360_UARTD)) {
+ *putc = putc_ns9360;
+ *base = NS9360_UARTD;
+ return;
}
-
+#elif defined(CONFIG_PROCESSOR_NS921X)
+ /* ns921x */
+ u32 clock = __raw_readl(NS921XSYS_CLOCK);
+
+ if ((clock & (1 << 1)) &&
+ NS921X_UART_ENABLED(NS921X_UARTA)) {
+ *putc = putc_ns921x;
+ *base = NS921X_UARTA;
+ return;
+ } else if ((clock & (1 << 2)) &&
+ NS921X_UART_ENABLED(NS921X_UARTB)) {
+ *putc = putc_ns921x;
+ *base = NS921X_UARTB;
+ return;
+ } else if ((clock & (1 << 3)) &&
+ NS921X_UART_ENABLED(NS921X_UARTC)) {
+ *putc = putc_ns921x;
+ *base = NS921X_UARTC;
+ return;
+ } else if ((clock & (1 << 4)) &&
+ NS921X_UART_ENABLED(NS921X_UARTD)) {
+ *putc = putc_ns921x;
+ *base = NS921X_UARTD;
+ return;
+ }
+# if defined(CONFIG_SERIAL_FIM_CONSOLE)
+ /* None of the standard UARTs is enabled. Try with the FIMs */
+# if defined(CONFIG_FIM_ZERO_SERIAL_SELECTED) && !defined(CONFIG_FIM_ONE_SERIAL_SELECTED)
+ /* Try with UART on FIM0 */
+ else if (NS921X_FIM_ENABLED(NS921X_FIM0)) {
+ *putc = putc_ns921x_fim;
+ *base = NS921X_FIM0;
+ return;
+ }
+# elif !defined(CONFIG_FIM_ZERO_SERIAL_SELECTED) && defined(CONFIG_FIM_ONE_SERIAL_SELECTED)
+ /* Try with UART on FIM1 */
+ else if (NS921X_FIM_ENABLED(NS921X_FIM1)) {
+ *putc = putc_ns921x_fim;
+ *base = NS921X_FIM1;
+ return;
+ }
+# endif /* FIM_x */
+# endif /* SERIAL_FIM_CONSOLE */
+#endif /* PROCESSOR_x */
*putc = putc_dummy;
}
@@ -147,7 +164,7 @@ void __iomem *base;
static void putc(char c)
{
- myputc(c, base);
+ myputc(c, base);
}
static void arch_decomp_setup(void)
diff --git a/arch/arm/mach-ns9xxx/irq-ns921x.c b/arch/arm/mach-ns9xxx/irq-ns921x.c
new file mode 100644
index 000000000000..1937d574a0e3
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/irq-ns921x.c
@@ -0,0 +1,57 @@
+/*
+ * arch/arm/mach-ns9xxx/irq-ns921x.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <mach/regs-sys-ns921x.h>
+
+#include "processor-ns921x.h"
+
+static const u32 ns921x_irq2powermask[] = {
+ [IRQ_NS9XXX_ETHRX] = SYS_POWER_ETH,
+ [IRQ_NS921X_UARTA] = SYS_POWER_UARTA,
+ [IRQ_NS921X_UARTB] = SYS_POWER_UARTB,
+ [IRQ_NS921X_UARTC] = SYS_POWER_UARTC,
+ [IRQ_NS921X_UARTD] = SYS_POWER_UARTD,
+ [IRQ_NS921X_SPI] = SYS_POWER_SPI,
+ [IRQ_NS921X_I2C] = SYS_POWER_I2C,
+ [IRQ_NS9215_RTC] = SYS_POWER_RTC,
+ [IRQ_NS9XXX_EXT0] = SYS_POWER_EXTIRQ0,
+ [IRQ_NS9XXX_EXT1] = SYS_POWER_EXTIRQ1,
+ [IRQ_NS9XXX_EXT2] = SYS_POWER_EXTIRQ2,
+ [IRQ_NS9XXX_EXT3] = SYS_POWER_EXTIRQ3,
+};
+
+int ns921x_set_wake_irq(unsigned int irq, unsigned int on)
+{
+ unsigned long flags;
+ u32 power;
+
+ if (irq > ARRAY_SIZE(ns921x_irq2powermask) ||
+ !ns921x_irq2powermask[irq])
+ return -ENOENT;
+
+ local_irq_save(flags);
+
+ power = __raw_readl(SYS_POWER);
+
+ if (on)
+ power |= ns921x_irq2powermask[irq];
+ else
+ power &= ~ns921x_irq2powermask[irq];
+
+ __raw_writel(power, SYS_POWER);
+
+ local_irq_restore(flags);
+
+ return 0;
+}
diff --git a/arch/arm/mach-ns9xxx/irq.c b/arch/arm/mach-ns9xxx/irq.c
index 22e0eb6e9ec4..e678f53cca61 100644
--- a/arch/arm/mach-ns9xxx/irq.c
+++ b/arch/arm/mach-ns9xxx/irq.c
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-ns9xxx/irq.c
*
- * Copyright (C) 2006,2007 by Digi International Inc.
+ * Copyright (C) 2006-2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -10,30 +10,105 @@
*/
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
-#include <linux/io.h>
+#include <asm/io.h>
#include <asm/mach/irq.h>
+#include <asm/mach-types.h>
+#include <mach/regs-bbu.h>
#include <mach/regs-sys-common.h>
#include <mach/irqs.h>
#include <mach/board.h>
+#include <mach/processor.h>
-#include "generic.h"
+#include "processor-ns921x.h"
+#include "irq.h"
-/* simple interrupt prio table: prio(x) < prio(y) <=> x < y */
-#define irq2prio(i) (i)
-#define prio2irq(p) (p)
+static unsigned prio2irq(unsigned prio)
+{
+ u32 ic = __raw_readl(SYS_IC(prio / 4));
+ return REGGETIM_IDX(ic, SYS_IC, ISD, __SYS_IC_FIELDNUM(prio));
+}
+
+static unsigned irq2prio_map[32];
+
+static unsigned irq2prio(unsigned irq)
+{
+ unsigned cachedprio = irq2prio_map[irq];
+ int timeout;
+
+ if (likely(irq == prio2irq(cachedprio)))
+ return cachedprio;
+
+ for (timeout = 32; timeout; --timeout) {
+ static unsigned i;
+ unsigned iirq;
+
+ i = (i + 1) % 32;
+
+ iirq = prio2irq(i);
+
+ irq2prio_map[iirq] = i;
+
+ pr_debug("%s: update %u -> %u\n", __func__, iirq, i);
+
+ if (iirq == irq)
+ return i;
+ }
+
+ BUG();
+
+ return 0; /* not reached, clean compiler warning */
+}
+
+#define prio2irq_init(p) (p)
static void ns9xxx_mask_irq(unsigned int irq)
{
- /* XXX: better use cpp symbols */
int prio = irq2prio(irq);
u32 ic = __raw_readl(SYS_IC(prio / 4));
- ic &= ~(1 << (7 + 8 * (3 - (prio & 3))));
+ REGSET_IDX(ic, SYS_IC, IE, __SYS_IC_FIELDNUM(prio), DIS);
__raw_writel(ic, SYS_IC(prio / 4));
}
+int ns9xxx_is_enabled_irq(unsigned int irq)
+{
+ int prio = irq2prio(irq);
+ u32 ic, en_bit_mask;
+
+ en_bit_mask = 0x80 << ((3 - (prio & 3)) * 8);
+ ic = __raw_readl(SYS_IC(prio / 4));
+
+ return ic & en_bit_mask;
+}
+EXPORT_SYMBOL(ns9xxx_is_enabled_irq);
+
+static void ns9xxx_disable_irq(unsigned int irq)
+{
+ struct irq_desc *desc = irq_desc + irq;
+
+ ns9xxx_mask_irq(irq);
+ desc->status &= IRQ_MASKED;
+}
+
static void ns9xxx_ack_irq(unsigned int irq)
{
- __raw_writel(0, SYS_ISRADDR);
+ if (irq >= IRQ_NS9XXX_EXT0) {
+ u32 eixctl;
+
+ BUG_ON((unsigned)(irq - IRQ_NS9XXX_EXT0) > 4);
+
+ eixctl = __raw_readl(SYS_EIxCTRL(irq - IRQ_NS9XXX_EXT0));
+
+ if (REGGET(eixctl, SYS_EIxCTRL, TYPE) ==
+ SYS_EIxCTRL_TYPE_EDGE) {
+ REGSETIM(eixctl, SYS_EIxCTRL, CLEAR, 1);
+ __raw_writel(eixctl,
+ SYS_EIxCTRL(irq - IRQ_NS9XXX_EXT0));
+
+ REGSETIM(eixctl, SYS_EIxCTRL, CLEAR, 0);
+ __raw_writel(eixctl,
+ SYS_EIxCTRL(irq - IRQ_NS9XXX_EXT0));
+ }
+ }
}
static void ns9xxx_maskack_irq(unsigned int irq)
@@ -44,23 +119,175 @@ static void ns9xxx_maskack_irq(unsigned int irq)
static void ns9xxx_unmask_irq(unsigned int irq)
{
- /* XXX: better use cpp symbols */
int prio = irq2prio(irq);
u32 ic = __raw_readl(SYS_IC(prio / 4));
- ic |= 1 << (7 + 8 * (3 - (prio & 3)));
+ REGSET_IDX(ic, SYS_IC, IE, __SYS_IC_FIELDNUM(prio), EN);
__raw_writel(ic, SYS_IC(prio / 4));
}
+static void ns9xxx_eoi_irq(unsigned int irq)
+{
+ __raw_writel(irq2prio(irq), SYS_ISRADDR);
+}
+
+static int ns9xxx_set_type_irq(unsigned int irq, unsigned int flow_type)
+{
+ void __iomem *regeixctrl = SYS_EIxCTRL(irq - IRQ_NS9XXX_EXT0);
+ u32 eixctrl = 0;
+
+ REGSETIM(eixctrl, SYS_EIxCTRL, CLEAR, 1);
+
+ if (irq < IRQ_NS9XXX_EXT0 || irq > IRQ_NS9XXX_EXT3)
+ return -EINVAL;
+
+ switch (flow_type) {
+ case IRQF_TRIGGER_HIGH:
+ REGSET(eixctrl, SYS_EIxCTRL, PLTY, HIGH);
+ REGSET(eixctrl, SYS_EIxCTRL, TYPE, LEVEL);
+ break;
+
+ case IRQF_TRIGGER_LOW:
+ REGSET(eixctrl, SYS_EIxCTRL, PLTY, LOW);
+ REGSET(eixctrl, SYS_EIxCTRL, TYPE, LEVEL);
+ break;
+
+ case IRQF_TRIGGER_RISING:
+ REGSET(eixctrl, SYS_EIxCTRL, PLTY, HIGH);
+ REGSET(eixctrl, SYS_EIxCTRL, TYPE, EDGE);
+ break;
+
+ case IRQF_TRIGGER_FALLING:
+ REGSET(eixctrl, SYS_EIxCTRL, PLTY, LOW);
+ REGSET(eixctrl, SYS_EIxCTRL, TYPE, EDGE);
+ break;
+
+ default:
+ pr_warning("%s: cannot configure for flow type %u\n",
+ __func__, flow_type);
+ return -ENODEV;
+ }
+
+ __raw_writel(eixctrl, regeixctrl);
+ REGSETIM(eixctrl, SYS_EIxCTRL, CLEAR, 0);
+ __raw_writel(eixctrl, regeixctrl);
+
+ return 0;
+}
+
+static int ns9xxx_set_wake_irq(unsigned int irq, unsigned int on)
+{
+#if defined(CONFIG_PROCESSOR_NS921X)
+ if (processor_is_ns921x())
+ return ns921x_set_wake_irq(irq, on);
+#endif
+
+ return -EINVAL;
+}
+
static struct irq_chip ns9xxx_chip = {
+ .name = "ns9xxx",
+ .disable = ns9xxx_disable_irq,
.ack = ns9xxx_ack_irq,
.mask = ns9xxx_mask_irq,
.mask_ack = ns9xxx_maskack_irq,
.unmask = ns9xxx_unmask_irq,
+ .eoi = ns9xxx_eoi_irq,
+ .set_type = ns9xxx_set_type_irq,
+ .set_wake = ns9xxx_set_wake_irq,
+};
+
+#if defined(CONFIG_PROCESSOR_NS9360)
+static void ns9360_mask_bbus_irq(unsigned int irq)
+{
+ u32 ier = __raw_readl(NS9360_BBUS_IEN);
+ ier &= ~(1 << (irq - IRQ_NS9360_BBUS(0)));
+ __raw_writel(ier, NS9360_BBUS_IEN);
+}
+
+static void ns9360_unmask_bbus_irq(unsigned int irq)
+{
+ u32 ier = __raw_readl(NS9360_BBUS_IEN);
+ ier |= 1 << (irq - IRQ_NS9360_BBUS(0));
+ __raw_writel(ier, NS9360_BBUS_IEN);
+}
+
+static void ns9360_demux_bbus_irq(unsigned int irq, struct irq_desc *desc)
+{
+ unsigned bbus_irq_plus1;
+ u32 stat = __raw_readl(NS9360_BBUS_ISTAT);
+
+ while ((bbus_irq_plus1 = fls(stat))) {
+ unsigned bbus_irq = bbus_irq_plus1 - 1;
+ stat &= ~(1 << bbus_irq);
+
+ desc_handle_irq(IRQ_NS9360_BBUS(bbus_irq),
+ irq_desc + IRQ_NS9360_BBUS(bbus_irq));
+ }
+
+ /* unmask parent irq */
+ desc->chip->unmask(irq);
+ desc->chip->eoi(irq);
+}
+
+static struct irq_chip ns9360_bbus_chip = {
+ .name = "ns9xxx_bbus",
+ .ack = ns9360_mask_bbus_irq,
+ .mask = ns9360_mask_bbus_irq,
+ .mask_ack = ns9360_mask_bbus_irq,
+ .unmask = ns9360_unmask_bbus_irq,
+};
+
+static void ns9360_mask_bbus_dma_irq(unsigned int irq)
+{
+ u32 ien = __raw_readl(NS9360_BBUS_DMA_IEN);
+ ien &= ~(1 << (irq - IRQ_NS9360_BBUDMA(0)));
+ __raw_writel(ien, NS9360_BBUS_DMA_IEN);
+}
+
+static void ns9360_unmask_bbus_dma_irq( unsigned int irq )
+{
+ u32 ien = __raw_readl(NS9360_BBUS_DMA_IEN);
+ ien |= 1 << (irq - IRQ_NS9360_BBUDMA(0));
+ __raw_writel(ien, NS9360_BBUS_DMA_IEN);
+}
+
+static void ns9360_demux_bbus_dma_irq(unsigned int irq, struct irq_desc *desc)
+{
+ unsigned dma_irq_plus1;
+ u32 stat = __raw_readl(NS9360_BBUS_DMA_ISTAT);
+
+ /* mask parent irq */
+ desc->chip->mask_ack(irq);
+
+ while ((dma_irq_plus1 = fls(stat))) {
+ unsigned dma_irq = dma_irq_plus1 - 1;
+ stat &= ~(1 << dma_irq);
+
+ desc_handle_irq(IRQ_NS9360_BBUDMA(dma_irq),
+ irq_desc + IRQ_NS9360_BBUDMA(dma_irq));
+ }
+
+ /* unmask parent */
+ desc->chip->unmask(irq);
+}
+
+static struct irq_chip ns9360_bbus_dma_chip = {
+ .name = "ns9xxx_bbus_dma",
+ .ack = ns9360_mask_bbus_dma_irq,
+ .mask = ns9360_mask_bbus_dma_irq,
+ .mask_ack = ns9360_mask_bbus_dma_irq,
+ .unmask = ns9360_unmask_bbus_dma_irq,
};
+#endif /* if defined(CONFIG_PROCESSOR_NS9360) */
+
+extern int noirqdebug;
-#if 0
-#define handle_irq handle_level_irq
-#else
+/* this is similar to handle_fasteoi_irq. The differences are:
+ * - handle_prio_irq calls desc->chip->ack at the beginning;
+ * - handle_prio_irq disables an irq directly after handle_IRQ_event to work
+ * around the bug in the ns9xxx' irq priority encoder;
+ * - currently some debug code;
+ */
static void handle_prio_irq(unsigned int irq, struct irq_desc *desc)
{
unsigned int cpu = smp_processor_id();
@@ -69,24 +296,29 @@ static void handle_prio_irq(unsigned int irq, struct irq_desc *desc)
spin_lock(&desc->lock);
- BUG_ON(desc->status & IRQ_INPROGRESS);
+ desc->chip->ack(irq);
+
+ if (unlikely(desc->status & IRQ_INPROGRESS)) {
+ desc->status |= IRQ_PENDING;
+ goto out_unlock;
+ }
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
kstat_cpu(cpu).irqs[irq]++;
action = desc->action;
- if (unlikely(!action || (desc->status & IRQ_DISABLED)))
+ if (unlikely(!action || (desc->status & IRQ_DISABLED))) {
+ desc->status |= IRQ_PENDING;
goto out_mask;
+ }
desc->status |= IRQ_INPROGRESS;
+ desc->status &= ~IRQ_PENDING;
spin_unlock(&desc->lock);
action_ret = handle_IRQ_event(irq, action);
-
- /* XXX: There is no direct way to access noirqdebug, so check
- * unconditionally for spurious irqs...
- * Maybe this function should go to kernel/irq/chip.c? */
- note_interrupt(irq, desc, action_ret);
+ if (!noirqdebug)
+ note_interrupt(irq, desc, action_ret);
spin_lock(&desc->lock);
desc->status &= ~IRQ_INPROGRESS;
@@ -95,32 +327,62 @@ static void handle_prio_irq(unsigned int irq, struct irq_desc *desc)
out_mask:
desc->chip->mask(irq);
- /* ack unconditionally to unmask lower prio irqs */
- desc->chip->ack(irq);
+ desc->chip->eoi(irq);
+out_unlock:
spin_unlock(&desc->lock);
}
-#define handle_irq handle_prio_irq
-#endif
void __init ns9xxx_init_irq(void)
{
int i;
/* disable all IRQs */
- for (i = 0; i < 8; ++i)
- __raw_writel(prio2irq(4 * i) << 24 |
- prio2irq(4 * i + 1) << 16 |
- prio2irq(4 * i + 2) << 8 |
- prio2irq(4 * i + 3),
- SYS_IC(i));
+ for (i = 0; i < 8; ++i) {
+ u32 ic = 0;
+
+ REGSETIM_IDX(ic, SYS_IC, ISD, __SYS_IC_FIELDNUM(0),
+ prio2irq_init(4 * i));
+ REGSETIM_IDX(ic, SYS_IC, ISD, __SYS_IC_FIELDNUM(1),
+ prio2irq_init(4 * i + 1));
+ REGSETIM_IDX(ic, SYS_IC, ISD, __SYS_IC_FIELDNUM(2),
+ prio2irq_init(4 * i + 2));
+ REGSETIM_IDX(ic, SYS_IC, ISD, __SYS_IC_FIELDNUM(3),
+ prio2irq_init(4 * i + 3));
+
+ __raw_writel(ic, SYS_IC(i));
+ }
for (i = 0; i < 32; ++i)
__raw_writel(prio2irq(i), SYS_IVA(i));
for (i = 0; i <= 31; ++i) {
set_irq_chip(i, &ns9xxx_chip);
- set_irq_handler(i, handle_irq);
+ set_irq_handler(i, handle_prio_irq);
set_irq_flags(i, IRQF_VALID);
}
+
+#ifdef CONFIG_PROCESSOR_NS9360
+ if (processor_is_ns9360()) {
+ /* set up the BBUS interrupt handlers */
+ __raw_writel(NS9360_BBUS_IEN_GLBL, NS9360_BBUS_IEN);
+ for (i = IRQ_NS9360_BBUS(0); i <= IRQ_NS9360_BBUS(25); i++) {
+ set_irq_chip(i, &ns9360_bbus_chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+ set_irq_chained_handler(IRQ_NS9360_BBUSAGG,
+ ns9360_demux_bbus_irq);
+
+ /* set up the BBUS DMA interrupt handlers */
+ __raw_writel(0, NS9360_BBUS_DMA_IEN);
+ for (i = IRQ_NS9360_BBUDMA(0); i <= IRQ_NS9360_BBUDMA(15); i++) {
+ set_irq_chip(i, &ns9360_bbus_dma_chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+ set_irq_chained_handler(IRQ_NS9360_BBUS_DMA,
+ ns9360_demux_bbus_dma_irq);
+ }
+#endif
}
diff --git a/arch/arm/mach-ns9xxx/irq.h b/arch/arm/mach-ns9xxx/irq.h
new file mode 100644
index 000000000000..81f4487a8cd2
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/irq.h
@@ -0,0 +1,16 @@
+/*
+ * arch/arm/mach-ns9xxx/irq.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/time.h>
+#include <asm/mach/time.h>
+#include <linux/init.h>
+
+void __init ns9xxx_init_irq(void);
diff --git a/arch/arm/mach-ns9xxx/leds.c b/arch/arm/mach-ns9xxx/leds.c
new file mode 100644
index 000000000000..99414dce0437
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/leds.c
@@ -0,0 +1,64 @@
+/*
+ * arch/arm/mach-ns9xxx/leds.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+
+#include <asm/leds.h>
+
+#include <mach/module.h>
+
+static void cc9p9215_leds_event(led_event_t evt)
+{
+ switch(evt) {
+ case led_idle_start:
+ gpio_set_value(89, 1);
+ break;
+ case led_idle_end:
+ gpio_set_value(89, 0);
+ break;
+ case led_green_on:
+ if (module_is_ccw9p9215())
+ gpio_set_value(88, 0);
+ break;
+ case led_green_off:
+ if (module_is_ccw9p9215())
+ gpio_set_value(88, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+static int __init ns9xxx_init_leds(void)
+{
+ int ret;
+
+ if (!module_is_cc9p9215() && !module_is_ccw9p9215())
+ return -ENODEV;
+
+ ret = gpio_request(89, "idle led");
+ if (ret)
+ return ret;
+
+ if (module_is_ccw9p9215()) {
+ ret = gpio_request(88, "wifi led");
+ if (ret)
+ return ret;
+ /* switch the led, just in case it were on */
+ gpio_set_value(88, 1);
+ }
+
+ leds_event = cc9p9215_leds_event;
+
+ return 0;
+}
+device_initcall(ns9xxx_init_leds);
diff --git a/arch/arm/mach-ns9xxx/mach-cc7ucamry.c b/arch/arm/mach-ns9xxx/mach-cc7ucamry.c
new file mode 100644
index 000000000000..741cc8974d7b
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-cc7ucamry.c
@@ -0,0 +1,23 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-cc7ucamry.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include "irq.h"
+#include "processor-ns921x.h"
+
+MACHINE_START(CC7UCAMRY, "ConnectCore 7U Camry")
+ .map_io = ns921x_map_io,
+ .init_irq = ns9xxx_init_irq,
+ .init_machine = ns921x_init_machine,
+ .timer = &ns921x_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/mach-cc9c.c b/arch/arm/mach-ns9xxx/mach-cc9c.c
new file mode 100644
index 000000000000..08561d0441b6
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-cc9c.c
@@ -0,0 +1,176 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-cc9c.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/i2c/pca953x.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/gpio.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include "irq.h"
+#include "processor-ns9360.h"
+#include "ns9360_devices.h"
+#include "ccx9c_devices.h"
+
+/* I2C devices */
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+static struct pca953x_platform_data pca9554_data = {
+ .gpio_base = 108,
+};
+#endif
+
+static struct i2c_board_info i2c_devices[] __initdata = {
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+ {
+ I2C_BOARD_INFO("pca9554", 0x20),
+ .platform_data = &pca9554_data,
+ },
+#endif
+};
+
+#ifdef CONFIG_CCX9C_TOUCH
+static int touch_pendown_state(void)
+{
+ return gpio_get_value(18) ? 0 : 1;
+}
+
+static struct ads7846_platform_data ccx9c_touch_data = {
+ .model = 7843,
+ .get_pendown_state = touch_pendown_state,
+ .x_min = 100,
+ .y_min = 100,
+ .x_max = 4000,
+ .y_max = 4000,
+ .rotate = 180,
+ .buflen = 20,
+ .skip_samples = 0,
+};
+
+void __init ccx9c_add_device_touch(void)
+{
+ if (gpio_request(18, "ads7846"))
+ return;
+
+ gpio_configure_ns9360(18, 0, 0, 2);
+}
+
+#define CCX9C_SPI_TOUCH \
+ { \
+ .modalias = "ads7846", \
+ .max_speed_hz = 200000, \
+ .irq = IRQ_NS9XXX_EXT3, \
+ .bus_num = 0, \
+ .chip_select = 0, \
+ .platform_data = &ccx9c_touch_data, \
+ },
+
+#else
+#define CCX9C_SPI_TOUCH
+void __init ccx9c_add_device_touch(void) {}
+#endif
+
+static struct spi_board_info spi_devices[] __initdata = {
+ CCX9C_SPI_TOUCH
+ /* Add here other SPI devices, if any... */
+};
+
+static void __init mach_cc9cjs_init_machine(void)
+{
+ /* register several system clocks */
+ ns9360_init_machine();
+
+ /* UARTs */
+#if defined(CONFIG_CCX9C_SERIAL_PORTA_RXTX)
+ ns9xxx_add_device_ccx9c_uarta_rxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTA_CTSRTSRXTX)
+ ns9xxx_add_device_ccx9c_uarta_ctsrtsrxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTA_FULL)
+ ns9xxx_add_device_ccx9c_uarta_full();
+#endif
+#if defined(CONFIG_CCX9C_SERIAL_PORTB_RXTX)
+ ns9xxx_add_device_ccx9c_uartb_rxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTB_CTSRTSRXTX)
+ ns9xxx_add_device_ccx9c_uartb_ctsrtsrxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTB_FULL)
+ ns9xxx_add_device_ccx9c_uartb_full();
+#endif
+#if defined(CONFIG_CCX9C_SERIAL_PORTC_RXTX)
+ ns9xxx_add_device_ccx9c_uartc_rxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTC_CTSRTSRXTX)
+ ns9xxx_add_device_ccx9c_uartc_ctsrtsrxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTC_FULL)
+ ns9xxx_add_device_ccx9c_uartc_full();
+#endif
+#if defined(CONFIG_CCX9C_SERIAL_PORTD_RXTX)
+ ns9xxx_add_device_ccx9c_uartd_rxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTD_CTSRTSRXTX)
+ ns9xxx_add_device_ccx9c_uartd_ctsrtsrxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTD_FULL)
+ ns9xxx_add_device_ccx9c_uartd_full();
+#endif
+
+ /* SPI */
+#if defined(CONFIG_CCX9C_SPI_PORTA)
+ ns9xxx_add_device_ccx9c_spi_porta();
+#endif
+#if defined(CONFIG_CCX9C_SPI_PORTB)
+ ns9xxx_add_device_ccx9c_spi_portb();
+#endif
+#if defined(CONFIG_CCX9C_SPI_PORTC)
+ ns9xxx_add_device_ccx9c_spi_portc();
+#endif
+#if defined(CONFIG_CCX9C_SPI_PORTD)
+ ns9xxx_add_device_ccx9c_spi_portd();
+#endif
+
+ /* Ethernet */
+ ns9xxx_add_device_ccx9c_eth();
+
+ /* NAND flash */
+ ns9xxx_add_device_ccx9c_nand();
+
+ /* Watchdog timer */
+ ns9xxx_add_device_ns9360_wdt();
+
+ /* USB host */
+ ns9xxx_add_device_ns9360_usbh();
+
+ /* I2C controller */
+ ns9xxx_add_device_ccx9c_i2c();
+
+ /* I2C devices */
+ i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
+
+ /* Framebuffer */
+#if defined(CONFIG_CCX9C_FB)
+ ns9xxx_add_device_ccx9c_fb(23);
+#endif
+
+ /* Touchscreen */
+ ccx9c_add_device_touch();
+
+ /* SPI devices */
+ spi_register_board_info(spi_devices, ARRAY_SIZE(spi_devices));
+
+ /* RTC */
+ ns9xxx_add_device_ns9360_rtc();
+}
+
+MACHINE_START(CC9C, "ConnectCore 9C")
+ .map_io = ns9360_map_io,
+ .init_irq = ns9xxx_init_irq,
+ .init_machine = mach_cc9cjs_init_machine,
+ .timer = &ns9360_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/mach-cc9p9215.c b/arch/arm/mach-ns9xxx/mach-cc9p9215.c
new file mode 100644
index 000000000000..6e223e70078c
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-cc9p9215.c
@@ -0,0 +1,23 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-cc9p9215.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include "irq.h"
+#include "processor-ns921x.h"
+
+MACHINE_START(CC9P9215, "ConnectCore 9P 9215")
+ .map_io = ns921x_map_io,
+ .init_irq = ns9xxx_init_irq,
+ .init_machine = ns921x_init_machine,
+ .timer = &ns921x_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/mach-cc9p9215js.c b/arch/arm/mach-ns9xxx/mach-cc9p9215js.c
new file mode 100644
index 000000000000..96f8220f0eec
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-cc9p9215js.c
@@ -0,0 +1,192 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-cc9p9215.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/i2c/pca953x.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/gpio.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include "irq.h"
+#include "processor-ns921x.h"
+#include "ns921x_devices.h"
+#include "ns9215_devices.h"
+#include "cc9p9215_devices.h"
+
+/* I2C devices */
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+static struct pca953x_platform_data pca9554_data = {
+ .gpio_base = 108,
+};
+#endif
+
+static struct i2c_board_info i2c_devices[] __initdata = {
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+ {
+ I2C_BOARD_INFO("pca9554", 0x20),
+ .platform_data = &pca9554_data,
+ },
+#endif
+};
+
+#ifdef CONFIG_CC9P9215JS_TOUCH
+static int touch_pendown_state(void)
+{
+ return gpio_get_value(101) ? 0 : 1;
+}
+
+static struct ads7846_platform_data cc9p9215js_touch_data = {
+ .model = 7843,
+ .get_pendown_state = touch_pendown_state,
+ .x_min = 100,
+ .y_min = 100,
+ .x_max = 4000,
+ .y_max = 4000,
+ .rotate = 180,
+ .buflen = 20,
+ .skip_samples = 0,
+};
+
+void __init cc9p9215js_add_device_touch(void)
+{
+ if (gpio_request(101, "ads7846"))
+ return;
+ gpio_configure_ns921x(101, 0, 0, 2, 0);
+}
+
+#define CC9P9215JS_TOUCH \
+ { \
+ .modalias = "ads7846", \
+ .max_speed_hz = 300000, \
+ .irq = IRQ_NS9XXX_EXT3, \
+ .bus_num = 1, \
+ .chip_select = 0, \
+ .platform_data = &cc9p9215js_touch_data, \
+ },
+
+#else
+#define CC9P9215JS_TOUCH
+void __init cc9p9215js_add_device_touch(void) {}
+#endif
+
+/* SPI devices */
+static struct spi_board_info spi_devices[] __initdata = {
+ CC9P9215JS_TOUCH
+#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
+ {
+ .modalias = "spidev",
+ .max_speed_hz = 10000000,
+ .bus_num = 1,
+ .chip_select = 0,
+ },
+#endif
+};
+
+static void __init mach_cc9p9215js_init_machine(void)
+{
+ /* register several system clocks */
+ ns921x_init_machine();
+
+ /* UARTs */
+#if defined(CONFIG_CC9P9215JS_SERIAL_PORTA_RXTX)
+ ns9xxx_add_device_cc9p9215_uarta_rxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTA_CTSRTSRXTX) || \
+ defined(CONFIG_CC9P9215JS_SERIAL_PORTA_RXTX485)
+ ns9xxx_add_device_cc9p9215_uarta_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTA_FULL)
+ ns9xxx_add_device_cc9p9215_uarta_full();
+#endif
+#if defined(CONFIG_CC9P9215JS_SERIAL_PORTB_RXTX)
+ ns9xxx_add_device_cc9p9215_uartb_rxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTB_CTSRTSRXTX) || \
+ defined(CONFIG_CC9P9215JS_SERIAL_PORTB_RXTX485)
+ ns9xxx_add_device_cc9p9215_uartb_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTB_FULL)
+ ns9xxx_add_device_cc9p9215_uartb_full();
+#endif
+#if defined(CONFIG_CC9P9215JS_SERIAL_PORTC_RXTX)
+ ns9xxx_add_device_cc9p9215_uartc_rxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTC_CTSRTSRXTX) || \
+ defined(CONFIG_CC9P9215JS_SERIAL_PORTC_RXTX485)
+ ns9xxx_add_device_cc9p9215_uartc_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTC_FULL)
+ ns9xxx_add_device_cc9p9215_uartc_full();
+#endif
+#if defined(CONFIG_CC9P9215JS_SERIAL_PORTD_RXTX)
+ ns9xxx_add_device_cc9p9215_uartd_rxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTD_CTSRTSRXTX) || \
+ defined(CONFIG_CC9P9215JS_SERIAL_PORTD_RXTX485)
+ ns9xxx_add_device_cc9p9215_uartd_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTD_FULL)
+ ns9xxx_add_device_cc9p9215_uartd_full();
+#endif
+
+ /* Ethernet */
+ ns9xxx_add_device_cc9p9215_eth();
+
+ /* SPI */
+#ifdef CONFIG_CC9P9215JS_SPI
+ ns9xxx_add_device_cc9p9215_spi();
+#endif
+
+ /* SPI devices */
+ spi_register_board_info(spi_devices, ARRAY_SIZE(spi_devices));
+
+ /* Watchdog timer */
+ ns9xxx_add_device_ns921x_wdt();
+
+ /* NOR Flash */
+ ns9xxx_add_device_cc9p9215_flash();
+
+ /* I2C controller */
+ ns9xxx_add_device_cc9p9215_i2c();
+
+ /* I2C devices */
+ i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
+
+ /* Leds */
+ ns9xxx_add_device_ns9215_leds();
+
+ /* Analog Digital Converter */
+ ns9xxx_add_device_ns9215_adc();
+
+ /* AES HW Encryption module */
+#if defined(CONFIG_CRYPTO_DEV_NS921X_AES) || \
+ defined(CONFIG_CRYPTO_DEV_NS921X_AES_MODULE)
+ ns9xxx_add_device_ns921x_aes();
+#endif
+
+ /* Real Time Clock */
+ ns9xxx_add_device_ns9215_rtc();
+
+ /* GPIO 4 is used as wake up gpio */
+ (void)ns921x_extgpio_pm_wakeup_init(4);
+
+ /* Init the FIM devices */
+ ns9xxx_add_device_ns921x_fims();
+
+ /* Video */
+ ns9xxx_add_device_cc9p9215_edt_diplay();
+
+ /* Touchscreen */
+ cc9p9215js_add_device_touch();
+}
+
+MACHINE_START(CC9P9215JS, "ConnectCore 9P 9215 on a JSCC9P9215 Devboard")
+ .map_io = ns921x_map_io,
+ .init_irq = ns9xxx_init_irq,
+ .init_machine = mach_cc9p9215js_init_machine,
+ .timer = &ns921x_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/mach-cc9p9360js.c b/arch/arm/mach-ns9xxx/mach-cc9p9360js.c
index 729f68da4293..cebf99f49a84 100644
--- a/arch/arm/mach-ns9xxx/mach-cc9p9360js.c
+++ b/arch/arm/mach-ns9xxx/mach-cc9p9360js.c
@@ -1,28 +1,201 @@
/*
* arch/arm/mach-ns9xxx/mach-cc9p9360js.c
*
- * Copyright (C) 2006,2007 by Digi International Inc.
+ * Copyright (C) 2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
+
+#include <linux/i2c.h>
+#include <linux/i2c/pca953x.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
#include <asm/mach/arch.h>
#include <asm/mach-types.h>
+#include <mach/irqs.h>
+
+#include "irq.h"
+#include "processor-ns9360.h"
+#include "ns9360_devices.h"
+#include "cc9p9360_devices.h"
+
+/* I2C devices */
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+static struct pca953x_platform_data pca9554_data = {
+ .gpio_base = 108,
+};
+#endif
+
+static struct i2c_board_info i2c_devices[] __initdata = {
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+ {
+ I2C_BOARD_INFO("pca9554", 0x20),
+ .platform_data = &pca9554_data,
+ },
+#endif
+#if defined(CONFIG_RTC_DRV_DS1307) || defined(CONFIG_RTC_DRV_DS1307_MODULE)
+ {
+ I2C_BOARD_INFO("ds1337", 0x68),
+#if defined(CONFIG_EXTERNAL_RTC_ALARM)
+ .irq = IRQ_NS9XXX_EXT0,
+#endif
+ },
+#endif
+};
+
+#ifdef CONFIG_CC9P9360JS_TOUCH
+static int touch_pendown_state(void)
+{
+ return gpio_get_value(69) ? 0 : 1;
+}
+
+static struct ads7846_platform_data cc9p9360js_touch_data = {
+ .model = 7843,
+ .get_pendown_state = touch_pendown_state,
+ .x_min = 100,
+ .y_min = 100,
+ .x_max = 4000,
+ .y_max = 4000,
+ .rotate = 180,
+ .buflen = 20,
+ .skip_samples = 0,
+};
+
+void __init cc9p9360js_add_device_touch(void)
+{
+ if (gpio_request(69, "ads7846"))
+ return;
+
+ gpio_configure_ns9360(69, 0, 0, 2);
+}
+
+#define CC9P9360JS_TOUCH \
+ { \
+ .modalias = "ads7846", \
+ .max_speed_hz = 200000, \
+ .irq = IRQ_NS9XXX_EXT1, \
+ .bus_num = 0, \
+ .chip_select = 0, \
+ .platform_data = &cc9p9360js_touch_data, \
+ },
+
+#else
+#define CC9P9360JS_TOUCH
+void __init cc9p9360js_add_device_touch(void) {}
+#endif
-#include <mach/processor-ns9360.h>
+static struct spi_board_info spi_devices[] __initdata = {
+ CC9P9360JS_TOUCH
+ /* Add here other SPI devices, if any... */
+};
-#include "board-jscc9p9360.h"
-#include "generic.h"
+#if defined(CONFIG_EXTERNAL_RTC_ALARM)
+void __init cc9p9360js_external_rtc_alarm(void)
+{
+ /* Request and configure GPIO */
+ if (gpio_request(13, "ds1337"))
+ return;
+ gpio_configure_ns9360(13, 0, 0, 1);
+ /* Configure interrupt */
+ set_irq_type( IRQ_NS9XXX_EXT0, IRQF_TRIGGER_FALLING );
+}
+#endif
static void __init mach_cc9p9360js_init_machine(void)
{
- ns9xxx_init_machine();
- board_jscc9p9360_init_machine();
+ /* register several system clocks */
+ ns9360_init_machine();
+
+ /* UARTs */
+#if defined(CONFIG_CC9P9360JS_SERIAL_PORTA_RXTX)
+ ns9xxx_add_device_cc9p9360_uarta_rxtx();
+#elif defined(CONFIG_CC9P9360JS_SERIAL_PORTA_CTSRTSRXTX)
+ ns9xxx_add_device_cc9p9360_uarta_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9360JS_SERIAL_PORTA_FULL)
+ ns9xxx_add_device_cc9p9360_uarta_full();
+#endif
+#if defined(CONFIG_CC9P9360JS_SERIAL_PORTB_RXTX)
+ ns9xxx_add_device_cc9p9360_uartb_rxtx();
+#elif defined(CONFIG_CC9P9360JS_SERIAL_PORTB_CTSRTSRXTX)
+ ns9xxx_add_device_cc9p9360_uartb_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9360JS_SERIAL_PORTB_FULL)
+ ns9xxx_add_device_cc9p9360_uartb_full();
+#endif
+#if defined(CONFIG_CC9P9360JS_SERIAL_PORTC_RXTX)
+ ns9xxx_add_device_cc9p9360_uartc_rxtx();
+#elif defined(CONFIG_CC9P9360JS_SERIAL_PORTC_CTSRTSRXTX)
+ ns9xxx_add_device_cc9p9360_uartc_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9360JS_SERIAL_PORTC_FULL)
+ ns9xxx_add_device_cc9p9360_uartc_full();
+#endif
+#if defined(CONFIG_CC9P9360JS_SERIAL_PORTD_RXTX)
+ ns9xxx_add_device_cc9p9360_uartd_rxtx();
+#elif defined(CONFIG_CC9P9360JS_SERIAL_PORTD_CTSRTSRXTX)
+ ns9xxx_add_device_cc9p9360_uartd_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9360JS_SERIAL_PORTD_FULL)
+ ns9xxx_add_device_cc9p9360_uartd_full();
+#endif
+
+ /* SPI */
+#if defined(CONFIG_CC9P9360JS_SPI_PORTA)
+ ns9xxx_add_device_cc9p9360_spi_porta();
+#endif
+#if defined(CONFIG_CC9P9360JS_SPI_PORTB)
+ ns9xxx_add_device_cc9p9360_spi_portb();
+#endif
+#if defined(CONFIG_CC9P9360JS_SPI_PORTC)
+ ns9xxx_add_device_cc9p9360_spi_portc();
+#endif
+#if defined(CONFIG_CC9P9360JS_SPI_PORTD)
+ ns9xxx_add_device_cc9p9360_spi_portd();
+#endif
+
+ /* Ethernet */
+ ns9xxx_add_device_cc9p9360_eth();
+
+ /* NAND flash */
+ ns9xxx_add_device_cc9p9360_nand();
+
+ /* Watchdog timer */
+ ns9xxx_add_device_ns9360_wdt();
+
+ /* USB host */
+ ns9xxx_add_device_ns9360_usbh();
+
+ /* I2C controller */
+ ns9xxx_add_device_cc9p9360_i2c();
+
+ /* I2C devices */
+ i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
+
+ /* Framebuffer */
+#if defined(CONFIG_CC9P9360JS_FB)
+ ns9xxx_add_device_cc9p9360_fb(18);
+#endif
+
+ /* Touchscreen */
+ cc9p9360js_add_device_touch();
+
+ /* SPI devices */
+ spi_register_board_info(spi_devices, ARRAY_SIZE(spi_devices));
+
+ /* RTC (internal) */
+ ns9xxx_add_device_ns9360_rtc();
+
+ /* RTC (external) */
+#if defined(CONFIG_EXTERNAL_RTC_ALARM)
+ cc9p9360js_external_rtc_alarm();
+#endif
}
-MACHINE_START(CC9P9360JS, "Digi ConnectCore 9P 9360 on an JSCC9P9360 Devboard")
+MACHINE_START(CC9P9360JS, "ConnectCore 9P 9360 on a JSCC9P9360 Devboard")
.map_io = ns9360_map_io,
.init_irq = ns9xxx_init_irq,
.init_machine = mach_cc9p9360js_init_machine,
diff --git a/arch/arm/mach-ns9xxx/mach-ccw9c.c b/arch/arm/mach-ns9xxx/mach-ccw9c.c
new file mode 100644
index 000000000000..3bb098c92b1e
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-ccw9c.c
@@ -0,0 +1,176 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-ccw9c.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/i2c/pca953x.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/gpio.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include "irq.h"
+#include "processor-ns9360.h"
+#include "ns9360_devices.h"
+#include "ccx9c_devices.h"
+
+/* I2C devices */
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+static struct pca953x_platform_data pca9554_data = {
+ .gpio_base = 108,
+};
+#endif
+
+static struct i2c_board_info i2c_devices[] __initdata = {
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+ {
+ I2C_BOARD_INFO("pca9554", 0x20),
+ .platform_data = &pca9554_data,
+ },
+#endif
+};
+
+#ifdef CONFIG_CCX9C_TOUCH
+static int touch_pendown_state(void)
+{
+ return gpio_get_value(18) ? 0 : 1;
+}
+
+static struct ads7846_platform_data ccx9c_touch_data = {
+ .model = 7843,
+ .get_pendown_state = touch_pendown_state,
+ .x_min = 100,
+ .y_min = 100,
+ .x_max = 4000,
+ .y_max = 4000,
+ .rotate = 180,
+ .buflen = 20,
+ .skip_samples = 0,
+};
+
+void __init ccx9c_add_device_touch(void)
+{
+ if (gpio_request(18, "ads7846"))
+ return;
+
+ gpio_configure_ns9360(18, 0, 0, 2);
+}
+
+#define CCX9C_SPI_TOUCH \
+ { \
+ .modalias = "ads7846", \
+ .max_speed_hz = 200000, \
+ .irq = IRQ_NS9XXX_EXT3, \
+ .bus_num = 0, \
+ .chip_select = 0, \
+ .platform_data = &ccx9c_touch_data, \
+ },
+
+#else
+#define CCX9C_SPI_TOUCH
+void __init ccx9c_add_device_touch(void) {}
+#endif
+
+static struct spi_board_info spi_devices[] __initdata = {
+ CCX9C_SPI_TOUCH
+ /* Add here other SPI devices, if any... */
+};
+
+static void __init mach_ccw9cjs_init_machine(void)
+{
+ /* register several system clocks */
+ ns9360_init_machine();
+
+ /* UARTs */
+#if defined(CONFIG_CCX9C_SERIAL_PORTA_RXTX)
+ ns9xxx_add_device_ccx9c_uarta_rxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTA_CTSRTSRXTX)
+ ns9xxx_add_device_ccx9c_uarta_ctsrtsrxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTA_FULL)
+ ns9xxx_add_device_ccx9c_uarta_full();
+#endif
+#if defined(CONFIG_CCX9C_SERIAL_PORTB_RXTX)
+ ns9xxx_add_device_ccx9c_uartb_rxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTB_CTSRTSRXTX)
+ ns9xxx_add_device_ccx9c_uartb_ctsrtsrxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTB_FULL)
+ ns9xxx_add_device_ccx9c_uartb_full();
+#endif
+#if defined(CONFIG_CCX9C_SERIAL_PORTC_RXTX)
+ ns9xxx_add_device_ccx9c_uartc_rxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTC_CTSRTSRXTX)
+ ns9xxx_add_device_ccx9c_uartc_ctsrtsrxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTC_FULL)
+ ns9xxx_add_device_ccx9c_uartc_full();
+#endif
+#if defined(CONFIG_CCX9C_SERIAL_PORTD_RXTX)
+ ns9xxx_add_device_ccx9c_uartd_rxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTD_CTSRTSRXTX)
+ ns9xxx_add_device_ccx9c_uartd_ctsrtsrxtx();
+#elif defined(CONFIG_CCX9C_SERIAL_PORTD_FULL)
+ ns9xxx_add_device_ccx9c_uartd_full();
+#endif
+
+ /* SPI */
+#if defined(CONFIG_CCX9C_SPI_PORTA)
+ ns9xxx_add_device_ccx9c_spi_porta();
+#endif
+#if defined(CONFIG_CCX9C_SPI_PORTB)
+ ns9xxx_add_device_ccx9c_spi_portb();
+#endif
+#if defined(CONFIG_CCX9C_SPI_PORTC)
+ ns9xxx_add_device_ccx9c_spi_portc();
+#endif
+#if defined(CONFIG_CCX9C_SPI_PORTD)
+ ns9xxx_add_device_ccx9c_spi_portd();
+#endif
+
+ /* Ethernet */
+ ns9xxx_add_device_ccx9c_eth();
+
+ /* NAND flash */
+ ns9xxx_add_device_ccx9c_nand();
+
+ /* Watchdog timer */
+ ns9xxx_add_device_ns9360_wdt();
+
+ /* USB host */
+ ns9xxx_add_device_ns9360_usbh();
+
+ /* I2C controller */
+ ns9xxx_add_device_ccx9c_i2c();
+
+ /* I2C devices */
+ i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
+
+ /* Framebuffer */
+#if defined(CONFIG_CCX9C_FB)
+ ns9xxx_add_device_ccx9c_fb(23);
+#endif
+
+ /* Touchscreen */
+ ccx9c_add_device_touch();
+
+ /* SPI devices */
+ spi_register_board_info(spi_devices, ARRAY_SIZE(spi_devices));
+
+ /* RTC */
+ ns9xxx_add_device_ns9360_rtc();
+}
+
+MACHINE_START(CCW9C, "ConnectCore Wi-9C")
+ .map_io = ns9360_map_io,
+ .init_irq = ns9xxx_init_irq,
+ .init_machine = mach_ccw9cjs_init_machine,
+ .timer = &ns9360_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/mach-ccw9p9215.c b/arch/arm/mach-ns9xxx/mach-ccw9p9215.c
new file mode 100644
index 000000000000..db86575a120e
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-ccw9p9215.c
@@ -0,0 +1,23 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-cc9p9215.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include "irq.h"
+#include "processor-ns921x.h"
+
+MACHINE_START(CCW9P9215, "ConnectCore Wi-9P 9215")
+ .map_io = ns921x_map_io,
+ .init_irq = ns9xxx_init_irq,
+ .init_machine = ns921x_init_machine,
+ .timer = &ns921x_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/mach-ccw9p9215js.c b/arch/arm/mach-ns9xxx/mach-ccw9p9215js.c
new file mode 100644
index 000000000000..4a5ddcb33dd8
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-ccw9p9215js.c
@@ -0,0 +1,242 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-cc9p9215.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/i2c/pca953x.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/gpio.h>
+#include <linux/crc32.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+#include <asm/leds.h>
+
+#include "irq.h"
+#include "pipermain.h"
+#include "processor-ns921x.h"
+#include "ns921x_devices.h"
+#include "ns9215_devices.h"
+#include "cc9p9215_devices.h"
+#include "ccw9p9215_devices.h"
+
+
+/* I2C devices */
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+static struct pca953x_platform_data pca9554_data = {
+ .gpio_base = 108,
+};
+#endif
+
+static struct i2c_board_info i2c_devices[] __initdata = {
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+ {
+ I2C_BOARD_INFO("pca9554", 0x20),
+ .platform_data = &pca9554_data,
+ },
+#endif
+};
+
+#ifdef CONFIG_CC9P9215JS_TOUCH
+static int touch_pendown_state(void)
+{
+ return gpio_get_value(101) ? 0 : 1;
+}
+
+static struct ads7846_platform_data cc9p9215js_touch_data = {
+ .model = 7843,
+ .get_pendown_state = touch_pendown_state,
+ .x_min = 100,
+ .y_min = 100,
+ .x_max = 4000,
+ .y_max = 4000,
+ .rotate = 180,
+ .buflen = 20,
+ .skip_samples = 0,
+};
+
+void __init cc9p9215js_add_device_touch(void)
+{
+ if (gpio_request(101, "ads7846"))
+ return;
+ gpio_configure_ns921x(101, 0, 0, 2, 0);
+}
+
+#define CC9P9215JS_TOUCH \
+ { \
+ .modalias = "ads7846", \
+ .max_speed_hz = 300000, \
+ .irq = IRQ_NS9XXX_EXT3, \
+ .bus_num = 1, \
+ .chip_select = 0, \
+ .platform_data = &cc9p9215js_touch_data, \
+ },
+
+#else
+#define CC9P9215JS_TOUCH
+void __init cc9p9215js_add_device_touch(void) {}
+#endif
+
+/* SPI devices */
+static struct spi_board_info spi_devices[] __initdata = {
+#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
+ {
+ .modalias = "spidev",
+ .max_speed_hz = 10000000,
+ .bus_num = 1,
+ .chip_select = 0,
+ },
+#endif
+ CC9P9215JS_TOUCH
+};
+
+
+#if defined(CONFIG_DIGI_PIPER_WIFI)
+static struct piper_pdata ccw9p9215_piper_pdata = {
+ .rst_gpio = 92,
+ .irq_gpio = 104,
+ .i2c_adapter_num = 0,
+};
+
+static void __init ccw9p9215js_fixup(struct machine_desc *desc,
+ struct tag *tags, char **cmdline,
+ struct meminfo *mi)
+{
+ unsigned char *mac = phys_to_virt(desc->boot_params) + 0xf00;
+ wcd_data_t *pwcal;
+ u32 crc;
+
+ /* 8 bytes after the mac address, its located the calibration data */
+ pwcal = (wcd_data_t *)(mac + 8);
+ memcpy(&ccw9p9215_piper_pdata.macaddr[0], mac, 6);
+
+ if (!strncmp(pwcal->header.magic_string, WCD_MAGIC,
+ sizeof(pwcal->header.magic_string))) {
+ /* check version */
+ if (((pwcal->header.ver_major >= '1') && (pwcal->header.ver_major <= '9')) &&
+ ((pwcal->header.ver_minor >= '0') && (pwcal->header.ver_minor <= '9'))) {
+ crc = ~crc32_le(~0, (unsigned char const *)pwcal->cal_curves_bg,
+ pwcal->header.wcd_len);
+ if (crc == pwcal->header.wcd_crc) {
+ memcpy(&ccw9p9215_piper_pdata.wcd, pwcal, sizeof(wcd_data_t));
+ return;
+ }
+ }
+ }
+
+ memset(&ccw9p9215_piper_pdata.wcd, 0, sizeof(wcd_data_t));
+}
+#endif
+
+static void __init mach_ccw9p9215js_init_machine(void)
+{
+ /* register several system clocks */
+ ns921x_init_machine();
+
+ /* UARTs */
+#if defined(CONFIG_CC9P9215JS_SERIAL_PORTA_RXTX)
+ ns9xxx_add_device_cc9p9215_uarta_rxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTA_CTSRTSRXTX) || \
+ defined(CONFIG_CC9P9215JS_SERIAL_PORTA_RXTX485)
+ ns9xxx_add_device_cc9p9215_uarta_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTA_FULL)
+ ns9xxx_add_device_cc9p9215_uarta_full();
+#endif
+#if defined(CONFIG_CC9P9215JS_SERIAL_PORTB_RXTX)
+ ns9xxx_add_device_cc9p9215_uartb_rxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTB_CTSRTSRXTX) || \
+ defined(CONFIG_CC9P9215JS_SERIAL_PORTB_RXTX485)
+ ns9xxx_add_device_cc9p9215_uartb_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTB_FULL)
+ ns9xxx_add_device_cc9p9215_uartb_full();
+#endif
+#if defined(CONFIG_CC9P9215JS_SERIAL_PORTC_RXTX)
+ ns9xxx_add_device_cc9p9215_uartc_rxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTC_CTSRTSRXTX) || \
+ defined(CONFIG_CC9P9215JS_SERIAL_PORTC_RXTX485)
+ ns9xxx_add_device_cc9p9215_uartc_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTC_FULL)
+ ns9xxx_add_device_cc9p9215_uartc_full();
+#endif
+#if defined(CONFIG_CC9P9215JS_SERIAL_PORTD_RXTX)
+ ns9xxx_add_device_cc9p9215_uartd_rxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTD_CTSRTSRXTX) || \
+ defined(CONFIG_CC9P9215JS_SERIAL_PORTD_RXTX485)
+ ns9xxx_add_device_cc9p9215_uartd_ctsrtsrxtx();
+#elif defined(CONFIG_CC9P9215JS_SERIAL_PORTD_FULL)
+ ns9xxx_add_device_cc9p9215_uartd_full();
+#endif
+
+ /* Ethernet */
+ ns9xxx_add_device_cc9p9215_eth();
+
+ /* 802.11 */
+#if defined(CONFIG_DIGI_PIPER_WIFI)
+ ns9xxx_add_device_ccw9p9215_wifi(&ccw9p9215_piper_pdata);
+#endif
+
+ /* SPI */
+#ifdef CONFIG_CC9P9215JS_SPI
+ ns9xxx_add_device_cc9p9215_spi();
+#endif
+
+ /* SPI devices */
+ spi_register_board_info(spi_devices, ARRAY_SIZE(spi_devices));
+
+ /* Watchdog timer */
+ ns9xxx_add_device_ns921x_wdt();
+
+ /* NOR Flash */
+ ns9xxx_add_device_cc9p9215_flash();
+
+ /* I2C controller */
+ ns9xxx_add_device_cc9p9215_i2c();
+
+ /* I2C devices */
+ i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
+
+ /* Leds */
+ ns9xxx_add_device_ns9215_leds();
+
+ /* Analog Digital Converter */
+ ns9xxx_add_device_ns9215_adc();
+
+ /* AES HW Encryption module */
+#if defined(CONFIG_CRYPTO_DEV_NS921X_AES) || \
+ defined(CONFIG_CRYPTO_DEV_NS921X_AES_MODULE)
+ ns9xxx_add_device_ns921x_aes();
+#endif
+
+ /* Real Time Clock */
+ ns9xxx_add_device_ns9215_rtc();
+
+ /* GPIO 4 is used as wake up gpio */
+ (void)ns921x_extgpio_pm_wakeup_init(4);
+
+ /* Init the FIM devices */
+ ns9xxx_add_device_ns921x_fims();
+
+ /* Video */
+ ns9xxx_add_device_cc9p9215_edt_diplay();
+
+ /* Touchscreen */
+ cc9p9215js_add_device_touch();
+}
+MACHINE_START(CCW9P9215JS, "ConnectCore Wi-9P 9215 on a JSCCW9P9215 Devboard")
+ .map_io = ns921x_map_io,
+ .init_irq = ns9xxx_init_irq,
+#if defined(CONFIG_DIGI_PIPER_WIFI)
+ .fixup = ccw9p9215js_fixup,
+#endif
+ .init_machine = mach_ccw9p9215js_init_machine,
+ .timer = &ns921x_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/mach-cme9210.c b/arch/arm/mach-ns9xxx/mach-cme9210.c
new file mode 100644
index 000000000000..29f71ce0acf1
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-cme9210.c
@@ -0,0 +1,24 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-cme9210.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include "irq.h"
+#include "processor-ns921x.h"
+
+MACHINE_START(CME9210JS, "Digi Connect ME 9210")
+ .map_io = ns921x_map_io,
+ .init_irq = ns9xxx_init_irq,
+ .init_machine = ns921x_init_machine,
+ .timer = &ns921x_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/mach-cme9210js.c b/arch/arm/mach-ns9xxx/mach-cme9210js.c
new file mode 100644
index 000000000000..23853f86a778
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-cme9210js.c
@@ -0,0 +1,109 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-cme9210js.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/spi/spi.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include "irq.h"
+#include "processor-ns921x.h"
+#include "ns921x_devices.h"
+#include "cme9210_devices.h"
+
+/* SPI devices */
+static struct spi_board_info spi_devices[] __initdata = {
+#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
+ {
+ .modalias = "spidev",
+ .max_speed_hz = 10000000,
+ .bus_num = 1,
+ .chip_select = 0,
+ },
+#endif
+};
+
+/* I2C devices */
+/* Array to add I2C devices
+static struct i2c_board_info i2c_devices[] __initdata = {
+ {
+ I2C_BOARD_INFO("device_name", address),
+ },
+};
+*/
+
+static void __init mach_cme9210js_init_machine(void)
+{
+ /* register several system clocks */
+ ns921x_init_machine();
+
+ /* UART */
+#if defined(CONFIG_CME9210JS_SERIAL_PORTA_RXTX)
+ ns9xxx_add_device_cme9210_uarta_rxtx();
+#elif defined(CONFIG_CME9210JS_SERIAL_PORTA_CTSRTSRXTX) || \
+ defined(CONFIG_CME9210JS_SERIAL_PORTA_RXTX485)
+ ns9xxx_add_device_cme9210_uarta_ctsrtsrxtx();
+#elif defined(CONFIG_CME9210JS_SERIAL_PORTA_FULL)
+ ns9xxx_add_device_cme9210_uarta_full();
+#endif
+#if defined(CONFIG_CME9210JS_SERIAL_PORTC_RXTX)
+ ns9xxx_add_device_cme9210_uartc_rxtx();
+#endif
+
+ /* Ethernet */
+ /* Adjust the netdev_max_backlog to a lower value. A big queue
+ * of incoming packets in the kernel can exhaust the kernel memory
+ * and trigger the OOM killer. This value can be tuned later in
+ * user space throught proc fs and with sysctl */
+ netdev_max_backlog = 300;
+ ns9xxx_add_device_cme9210_eth();
+
+ /* Watchdog timer */
+ ns9xxx_add_device_ns921x_wdt();
+
+ /* NOR Flash */
+ ns9xxx_add_device_cme9210_flash();
+
+ /* SPI */
+#ifdef CONFIG_CME9210JS_SPI
+ ns9xxx_add_device_cme9210_spi();
+#endif
+
+ /* SPI devices */
+ spi_register_board_info(spi_devices, ARRAY_SIZE(spi_devices));
+
+ /* I2C controller */
+ ns9xxx_add_device_cme9210_i2c();
+
+ /* I2C devices */
+ /*
+ i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
+ */
+
+ /* Init the FIM devices */
+ ns9xxx_add_device_ns921x_fims();
+
+ /* AES HW Encryption module */
+#if defined(CONFIG_CRYPTO_DEV_NS921X_AES) || \
+ defined(CONFIG_CRYPTO_DEV_NS921X_AES_MODULE)
+ ns9xxx_add_device_ns921x_aes();
+#endif
+}
+
+MACHINE_START(CME9210JS, "Digi Connect ME 9210 on Devboard")
+ .map_io = ns921x_map_io,
+ .init_irq = ns9xxx_init_irq,
+ .init_machine = mach_cme9210js_init_machine,
+ .timer = &ns921x_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/mach-inc20otter.c b/arch/arm/mach-ns9xxx/mach-inc20otter.c
new file mode 100644
index 000000000000..477553fe4411
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-inc20otter.c
@@ -0,0 +1,23 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-inc20otter.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include "irq.h"
+#include "processor-ns921x.h"
+
+MACHINE_START(INC20OTTER, "Inc20-Otter")
+ .map_io = ns921x_map_io,
+ .init_irq = ns9xxx_init_irq,
+ .init_machine = ns921x_init_machine,
+ .timer = &ns921x_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/mach-otter.c b/arch/arm/mach-ns9xxx/mach-otter.c
new file mode 100644
index 000000000000..5ab5cf13e0d2
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/mach-otter.c
@@ -0,0 +1,23 @@
+/*
+ * arch/arm/mach-ns9xxx/mach-otter.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include "irq.h"
+#include "processor-ns921x.h"
+
+MACHINE_START(OTTER, "Otter")
+ .map_io = ns921x_map_io,
+ .init_irq = ns9xxx_init_irq,
+ .init_machine = ns921x_init_machine,
+ .timer = &ns921x_timer,
+ .boot_params = 0x100,
+MACHINE_END
diff --git a/arch/arm/mach-ns9xxx/ns9215_devices.c b/arch/arm/mach-ns9xxx/ns9215_devices.c
new file mode 100644
index 000000000000..0a6f473b68f1
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/ns9215_devices.c
@@ -0,0 +1,299 @@
+/*
+ * arch/arm/mach-ns9xxx/ns9215_devices.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#include <mach/ns9xxx-pwm.h>
+#include <linux/leds.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/pwm-led.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-sys-ns921x.h>
+
+#include "clock.h"
+#include "processor-ns921x.h"
+
+#if defined(CONFIG_ARCH_NS9XXX)
+static struct led_info ns9xxx_leds_pdata_info = {
+ .name = "leds-pwm",
+ .default_trigger = "ledtrig-dim",
+ .flags = 0,
+};
+
+static struct pwm_channel_config ns9xxx_leds_pdata_config = {
+ .duty_ns = 0,
+ .period_ns = 0,
+
+};
+
+static struct pwm_led_platform_data ns9xxx_leds_pdata = {
+ .bus_id = "ns9xxx_pwmc.0",
+ .chan = 0,
+ .led_info = &ns9xxx_leds_pdata_info,
+ .config = &ns9xxx_leds_pdata_config,
+};
+
+static struct platform_device ns9xxx_device_ns9215_leds = {
+ .name = "leds-pwm",
+ .id = 0,
+ .dev = {
+ .platform_data = &ns9xxx_leds_pdata,
+ }
+};
+
+static struct ns9xxx_pwm_channel ns9215_pwm_channels[] = {
+ [0] = {
+ .timer = 6,
+ .gpio = 5,
+ },
+ [1] = {
+ .timer = 7,
+ .gpio = 7,
+ },
+ [2] = {
+ .timer = 8,
+ .gpio = 8,
+ },
+ [3] = {
+ .timer = 9,
+ .gpio = 13,
+ }
+};
+
+
+/* This structure will be initialized in the init function (see below) */
+static struct ns9xxx_pwm_pdata ns9215_pwm_pdata;
+
+
+static struct platform_device ns9xxx_device_ns9215_pwm = {
+ .name = "ns9xxx_pwmc",
+ .id = 0,
+ .dev = {
+ .platform_data = &ns9215_pwm_pdata,
+ }
+};
+
+void __init ns9xxx_add_device_ns9215_leds(void)
+{
+ platform_device_register(&ns9xxx_device_ns9215_leds);
+
+#if 1 /* Enabled PWM by the user configuration */
+ ns9215_pwm_pdata.channels = ns9215_pwm_channels;
+ ns9215_pwm_pdata.number_channels = ARRAY_SIZE(ns9215_pwm_channels);
+ platform_device_register(&ns9xxx_device_ns9215_pwm);
+#endif
+
+}
+#else
+void __init ns9xxx_add_device_ns9215_leds(void) {}
+#endif
+
+#if defined(CONFIG_ADC_NS9215) || defined(CONFIG_ADC_NS9215_MODULE)
+static struct ns921x_sysclk adc_clk = {
+ .clk = {
+ .name = "adc-ns9215",
+ .id = -1,
+ .owner = THIS_MODULE,
+ },
+ .mask = SYS_CLOCK_ADC,
+};
+
+static struct resource adc_resources[] = {
+ {
+ .start = 0x90039000,
+ .end = 0x90039027,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device ns9xxx_device_ns9215_adc = {
+ .name = "adc-ns9215",
+ .id = -1,
+ .resource = adc_resources,
+ .num_resources = ARRAY_SIZE(adc_resources),
+};
+
+void __init ns9xxx_add_device_ns9215_adc(void)
+{
+ if (clk_register(&adc_clk.clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns9215_adc);
+}
+#else
+void __init ns9xxx_add_device_ns9215_adc(void) {}
+#endif
+
+#if defined(CONFIG_RTC_DRV_NS9XXX) || defined(CONFIG_RTC_DRV_NS9XXX_MODULE)
+static irqreturn_t ns9xxx_plat_rtc_irq(int irq, void *data)
+{
+ u32 sysrtcmc = __raw_readl(SYS_RTCMC);
+
+ if (sysrtcmc & SYS_RTCMC_RIS) {
+ REGSETIM(sysrtcmc, SYS_RTCMC, RIC, 1);
+ __raw_writel(sysrtcmc, SYS_RTCMC);
+ REGSETIM(sysrtcmc, SYS_RTCMC, RIC, 0);
+ __raw_writel(sysrtcmc, SYS_RTCMC);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static inline int wait_for_clkrdy(void)
+{
+ u32 sysrtcmc;
+ unsigned int timeout = 0x20;
+
+wait:
+ sysrtcmc = __raw_readl(SYS_RTCMC);
+ if (!(sysrtcmc & SYS_RTCMC_RIS)) {
+ if (unlikely(!--timeout))
+ return -ETIMEDOUT;
+ udelay(1);
+ goto wait;
+ }
+ pr_debug("%s: SYS_RTCMC = 0x%x\n", __func__, sysrtcmc);
+
+ REGSETIM(sysrtcmc, SYS_RTCMC, RIC, 1);
+ __raw_writel(sysrtcmc, SYS_RTCMC);
+ REGSETIM(sysrtcmc, SYS_RTCMC, RIC, 0);
+ __raw_writel(sysrtcmc, SYS_RTCMC);
+
+ return sysrtcmc & SYS_RTCMC_SS ? 0 : -EIO;
+}
+
+static int ns9215_rtc_endisable(struct clk *clk, int enable)
+{
+ u32 sysrtcmc = __raw_readl(SYS_RTCMC);
+
+ pr_debug("%s: enable=%d, SYS_RTCMC=%08x\n", __func__, enable, sysrtcmc);
+
+ if (enable && (sysrtcmc & (SYS_RTCMC_SS | SYS_RTCMC_MODE)) ==
+ SYS_RTCMC_SS) {
+ /* the clock was disabled but is still active */
+
+ unsigned int timeout = 0x20;
+
+ pr_debug("%s: wait until disabled clock becomes inactive\n",
+ __func__);
+wait:
+ sysrtcmc = __raw_readl(SYS_RTCMC);
+ if (sysrtcmc & SYS_RTCMC_SS) {
+ if (unlikely(!--timeout))
+ return -ETIMEDOUT;
+ udelay(1);
+ goto wait;
+ }
+ }
+
+ if (enable && (sysrtcmc & SYS_RTCMC_SS)) {
+ pr_debug("%s: RTC clock already on\n", __func__);
+ return 0;
+ }
+
+ if (enable) {
+ int ret;
+
+ /* disable rtc irq because the irq is acked in wait_for_clkrdy
+ * and this might stuck the irq controller. (It doesn't like an
+ * irq to become active and then inactive before it is serviced.
+ *
+ * Not acking doesn't do the trick. Then the following might
+ * happen in an endless loop:
+ * - clk_enable + clk_disable with irqs off. This makes
+ * IRQ_NS9215_RTC pending.
+ * - reenable irqs
+ * - service IRQ_NS9215_RTC
+ * - platform handler runs and acks irq.
+ * - driver handler enables clk, sees that there is nothing to
+ * do, and disables clk again. All this with IRQ_NS9215_RTC
+ * being masked.
+ */
+ disable_irq(IRQ_NS9215_RTC);
+
+ __raw_writel(SYS_RTCMC_MODE_NORMAL, SYS_RTCMC);
+
+ ret = wait_for_clkrdy();
+
+ enable_irq(IRQ_NS9215_RTC);
+
+ return ret;
+ } else {
+ __raw_writel(SYS_RTCMC_MODE_STANDBY, SYS_RTCMC);
+ sysrtcmc = __raw_readl(SYS_RTCMC);
+ pr_debug("%s: disable done, SYS_RTCMC=%08x\n",
+ __func__, sysrtcmc);
+
+ return 0;
+ }
+}
+
+static struct ns921x_sysclk rtc_pm_clk = {
+ .clk = {
+ .name = "rtc-ns9xxx-pm",
+ .id = -1,
+ .owner = THIS_MODULE,
+ .endisable = ns921x_endisable_sysclock,
+ },
+ .mask = SYS_CLOCK_RTC,
+ };
+
+static struct clk rtc_clk = {
+ .name = "rtc-ns9xxx",
+ .id = 0,
+ .owner = THIS_MODULE,
+ .endisable = ns9215_rtc_endisable,
+};
+
+static struct resource rtc_resources[] = {
+ {
+ .start = 0x90060000,
+ .end = 0x900600ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS9215_RTC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device ns9xxx_device_ns9215_rtc = {
+ .name = "rtc-ns9xxx",
+ .id = 0,
+ .resource = rtc_resources,
+ .num_resources = ARRAY_SIZE(rtc_resources),
+};
+
+void __init ns9xxx_add_device_ns9215_rtc(void)
+{
+ if (clk_register(&rtc_pm_clk.clk))
+ return;
+
+ rtc_clk.parent = &rtc_pm_clk.clk;
+ if (clk_register(&rtc_clk))
+ return;
+
+ if (request_irq(IRQ_NS9215_RTC, ns9xxx_plat_rtc_irq, IRQF_SHARED,
+ "plat-rtc-ns9xxx", ns9xxx_plat_rtc_irq))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns9215_rtc);
+}
+#else
+void __init ns9xxx_add_device_ns9215_rtc(void) {}
+#endif
diff --git a/arch/arm/mach-ns9xxx/ns9215_devices.h b/arch/arm/mach-ns9xxx/ns9215_devices.h
new file mode 100644
index 000000000000..065c270b9350
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/ns9215_devices.h
@@ -0,0 +1,14 @@
+/*
+ * arch/arm/mach-ns9xxx/ns9215_devices.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+void __init ns9xxx_add_device_ns9215_leds(void);
+void __init ns9xxx_add_device_ns9215_adc(void);
+void __init ns9xxx_add_device_ns9215_rtc(void);
diff --git a/arch/arm/mach-ns9xxx/ns921x_devices.c b/arch/arm/mach-ns9xxx/ns921x_devices.c
new file mode 100644
index 000000000000..1814cb95782d
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/ns921x_devices.c
@@ -0,0 +1,798 @@
+/*
+ * arch/arm/mach-ns9xxx/ns921x_devices.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/ns9xxx-eth.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/regs-sys-ns921x.h>
+#include <mach/ns921x-serial.h>
+#include <mach/fim-ns921x.h>
+#include <mach/gpio.h>
+
+#include "clock.h"
+#include "ns921x_devices.h"
+#include "processor-ns921x.h"
+
+/* Watchdog timer */
+#if defined(CONFIG_NS9XXX_WATCHDOG) || defined(CONFIG_NS9XXX_WATCHDOG_MODULE)
+static struct clk wdt_clk = {
+ .name = "ns9xxx-wdt",
+ .id = -1,
+ .owner = THIS_MODULE,
+};
+
+static struct resource wdt_resources[] = {
+ {
+ .start = 0xa0900174,
+ .end = 0xa0900177,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device ns9xxx_device_ns921x_wdt = {
+ .name = "ns9xxx-wdt",
+ .id = -1,
+ .resource = wdt_resources,
+ .num_resources = ARRAY_SIZE(wdt_resources),
+};
+
+void __init ns9xxx_add_device_ns921x_wdt(void)
+{
+ wdt_clk.parent = clk_get(NULL, "cpuclock");
+ if (IS_ERR(wdt_clk.parent))
+ return;
+
+ if (clk_register(&wdt_clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns921x_wdt);
+}
+#else
+void __init ns9xxx_add_device_ns921x_wdt(void) {}
+#endif
+
+/* Ethernet */
+#if defined(CONFIG_NS9XXX_ETH) || defined(CONFIG_NS9XXX_ETH_MODULE)
+int __init eth_register_gpios(int gpio[], int func[], int dir[], int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ if (gpio_request(gpio[i], "ns9xxx-eth"))
+ goto err;
+ gpio_configure_ns921x(gpio[i], dir[i], 0, func[i], 0);
+ }
+
+ return 0;
+err:
+ for (; i >= 0; i--)
+ gpio_free(gpio[i]);
+
+ return -EBUSY;
+}
+
+static struct ns921x_sysclk eth_clk = {
+ .clk = {
+ .name = "ns9xxx-eth",
+ .id = -1,
+ .owner = THIS_MODULE,
+ .endisable = ns921x_endisable_sysclock,
+ },
+ .mask = SYS_CLOCK_ETH,
+};
+
+static struct plat_ns9xxx_eth ns9xxx_device_ns921x_eth_data = {
+ .irqrx = IRQ_NS9XXX_ETHRX,
+ .irqtx = IRQ_NS9XXX_ETHTX,
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ .activityled = 14,
+#endif
+};
+
+static struct resource eth_resources[] = {
+ {
+ .start = 0xa0600000,
+ .end = 0xa06027ff,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device ns9xxx_device_ns921x_eth = {
+ .name = "ns9xxx-eth",
+ .id = -1,
+ .resource = eth_resources,
+ .num_resources = ARRAY_SIZE(eth_resources),
+ .dev = {
+ .platform_data = &ns9xxx_device_ns921x_eth_data,
+ },
+};
+
+void __init ns9xxx_add_device_ns921x_eth(struct clk *phyclk, u32 phy_mask,
+ int gpio[], int func[], int dir[], int num)
+{
+ if (eth_register_gpios(gpio, func, dir, num))
+ return;
+
+ eth_clk.clk.parent = phyclk;
+ ns9xxx_device_ns921x_eth_data.phy_mask = phy_mask;
+
+ if (clk_register(&eth_clk.clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns921x_eth);
+}
+#else
+void __init ns9xxx_add_device_ns921x_eth(struct clk *phyclk, u32 phy_mask,
+ int gpio[], int func[], int dir[], int num) {}
+#endif
+
+/* I2C controller */
+#if defined(CONFIG_I2C_NS9XXX) || defined(CONFIG_I2C_NS9XXX_MODULE)
+static struct ns921x_sysclk i2c_clk = {
+ .clk = {
+ .name = "i2c-ns9xxx",
+ .id = -1,
+ .owner = THIS_MODULE,
+ .endisable = ns921x_endisable_sysclock,
+ },
+ .mask = SYS_CLOCK_I2C,
+};
+
+static struct resource i2c_resources[] = {
+ {
+ .start = 0x90050000,
+ .end = 0x9005000f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS921X_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device ns9xxx_device_ns921x_i2c = {
+ .name = "i2c-ns9xxx",
+ .id = -1,
+ .resource = i2c_resources,
+ .num_resources = ARRAY_SIZE(i2c_resources),
+};
+
+void __init ns9xxx_add_device_ns921x_i2c(struct plat_ns9xxx_i2c *i2c_data)
+{
+ i2c_clk.clk.parent = clk_get(NULL, "systemclock");
+ if (IS_ERR(i2c_clk.clk.parent))
+ return;
+
+ if (clk_register(&i2c_clk.clk))
+ return;
+
+ ns9xxx_device_ns921x_i2c.dev.platform_data = i2c_data;
+ platform_device_register(&ns9xxx_device_ns921x_i2c);
+}
+#else
+void __init ns9xxx_add_device_ns921x_i2c(struct plat_ns9xxx_i2c *i2c_data) {}
+#endif
+
+/* UARTs */
+#if defined(CONFIG_SERIAL_NS921X) || defined(CONFIG_SERIAL_NS921X_MODULE)
+static struct ns921x_sysclk uarta_clk = {
+ .clk = {
+ .name = "ns921x-serial",
+ .id = 0,
+ .owner = THIS_MODULE,
+ .rate = NS921X_REFCLOCK,
+ .endisable = ns921x_endisable_sysclock,
+ },
+ .mask = SYS_CLOCK_UARTA,
+};
+
+static struct ns921x_sysclk uartb_clk = {
+ .clk = {
+ .name = "ns921x-serial",
+ .id = 1,
+ .owner = THIS_MODULE,
+ .rate = NS921X_REFCLOCK,
+ .endisable = ns921x_endisable_sysclock,
+ },
+ .mask = SYS_CLOCK_UARTB,
+};
+
+static struct ns921x_sysclk uartc_clk = {
+ .clk = {
+ .name = "ns921x-serial",
+ .id = 2,
+ .owner = THIS_MODULE,
+ .rate = NS921X_REFCLOCK,
+ .endisable = ns921x_endisable_sysclock,
+ },
+ .mask = SYS_CLOCK_UARTC,
+};
+
+static struct ns921x_sysclk uartd_clk = {
+ .clk = {
+ .name = "ns921x-serial",
+ .id = 3,
+ .owner = THIS_MODULE,
+ .rate = NS921X_REFCLOCK,
+ .endisable = ns921x_endisable_sysclock,
+ },
+ .mask = SYS_CLOCK_UARTD,
+};
+
+static struct resource uarta_resources[] = {
+ {
+ .start = 0x90010000,
+ .end = 0x90017fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS921X_UARTA,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource uartb_resources[] = {
+ {
+ .start = 0x90018000,
+ .end = 0x9001ffff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS921X_UARTB,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource uartc_resources[] = {
+ {
+ .start = 0x90020000,
+ .end = 0x90027fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS921X_UARTC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource uartd_resources[] = {
+ {
+ .start = 0x90028000,
+ .end = 0x9002ffff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS921X_UARTD,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct ns921x_uart_data uarta_data;
+static struct ns921x_uart_data uartb_data;
+static struct ns921x_uart_data uartc_data;
+static struct ns921x_uart_data uartd_data;
+
+static struct platform_device ns9xxx_device_ns921x_uarta = {
+ .name = "ns921x-serial",
+ .id = 0,
+ .dev = {
+ .platform_data = &uarta_data,
+ },
+ .resource = uarta_resources,
+ .num_resources = ARRAY_SIZE(uarta_resources),
+};
+
+static struct platform_device ns9xxx_device_ns921x_uartb = {
+ .name = "ns921x-serial",
+ .id = 1,
+ .dev = {
+ .platform_data = &uartb_data,
+ },
+ .resource = uartb_resources,
+ .num_resources = ARRAY_SIZE(uartb_resources),
+};
+
+static struct platform_device ns9xxx_device_ns921x_uartc = {
+ .name = "ns921x-serial",
+ .id = 2,
+ .dev = {
+ .platform_data = &uartc_data,
+ },
+ .resource = uartc_resources,
+ .num_resources = ARRAY_SIZE(uartc_resources),
+};
+
+static struct platform_device ns9xxx_device_ns921x_uartd = {
+ .name = "ns921x-serial",
+ .id = 3,
+ .dev = {
+ .platform_data = &uartd_data,
+ },
+ .resource = uartd_resources,
+ .num_resources = ARRAY_SIZE(uartd_resources),
+};
+
+int __init uart_register_gpios(int gpio_start,
+ int gpio_nr,
+ int func,
+ struct ns921x_uart_data *data)
+{
+ int i;
+ int gpio[] = { 3, 7, 1, 5, 0, 2, 4, 6 };
+
+ for (i = 0; i < gpio_nr; i++) {
+ if (i!=2 || !data->rtsen) /* Skip request of CTS line */
+ {
+ if (gpio_request(gpio_start + gpio[i], "ns921x-serial"))
+ goto err;
+#ifdef CONFIG_NS921X_SERIAL_RTS_AS_GPIO
+ if (i==3) /* RTS needs to be used as gpio to allow using AFE */
+ gpio_configure_ns921x(gpio_start + gpio[i], 0, 0, 3, 0);
+ else
+#endif
+ gpio_configure_ns921x(gpio_start + gpio[i], 0, 0, func, 0);
+ }
+ data->gpios[i] = gpio_start + gpio[i];
+ }
+
+ return 0;
+err:
+ for (; i >= 0; i--)
+ gpio_free(gpio_start + gpio[i]);
+
+ return -EBUSY;
+}
+
+void __init ns9xxx_add_device_ns921x_uarta(int gpio_start,
+ int gpio_nr, int func)
+{
+ uarta_data.nr_gpios = gpio_nr;
+#if defined(CONFIG_CC9P9215JS_SERIAL_PORTA_RXTX485) || \
+ defined(CONFIG_CME9210JS_SERIAL_PORTA_RXTX485)
+ uarta_data.rtsen = 1;
+#else
+ uarta_data.rtsen = 0;
+#endif
+ if (uart_register_gpios(gpio_start, gpio_nr, func, &uarta_data))
+ return;
+
+ if (clk_register(&uarta_clk.clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns921x_uarta);
+}
+
+void __init ns9xxx_add_device_ns921x_uartb(int gpio_start,
+ int gpio_nr, int func)
+{
+ uartb_data.nr_gpios = gpio_nr;
+#ifdef CONFIG_CC9P9215JS_SERIAL_PORTB_RXTX485
+ uartb_data.rtsen = 1;
+#else
+ uartb_data.rtsen = 0;
+#endif
+
+ if (uart_register_gpios(gpio_start, gpio_nr, func, &uartb_data))
+ return;
+
+ if (clk_register(&uartb_clk.clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns921x_uartb);
+}
+
+void __init ns9xxx_add_device_ns921x_uartc(int gpio_start,
+ int gpio_nr, int func)
+{
+ uartc_data.nr_gpios = gpio_nr;
+#ifdef CONFIG_CC9P9215JS_SERIAL_PORTC_RXTX485
+ uartc_data.rtsen = 1;
+#else
+ uartc_data.rtsen = 0;
+#endif
+
+ if (uart_register_gpios(gpio_start, gpio_nr, func, &uartc_data))
+ return;
+
+ if (clk_register(&uartc_clk.clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns921x_uartc);
+}
+
+void __init ns9xxx_add_device_ns921x_uartd(int gpio_start,
+ int gpio_nr, int func)
+{
+ uartd_data.nr_gpios = gpio_nr;
+#ifdef CONFIG_CC9P9215JS_SERIAL_PORTD_RXTX485
+ uartd_data.rtsen = 1;
+#else
+ uartd_data.rtsen = 0;
+#endif
+
+ if (uart_register_gpios(gpio_start, gpio_nr, func, &uartd_data))
+ return;
+
+ if (clk_register(&uartd_clk.clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns921x_uartd);
+}
+#else
+void __init ns9xxx_add_device_ns921x_uarta(int gpio_start,
+ int gpio_nr, int func) {}
+void __init ns9xxx_add_device_ns921x_uartb(int gpio_start,
+ int gpio_nr, int func) {}
+void __init ns9xxx_add_device_ns921x_uartc(int gpio_start,
+ int gpio_nr, int func) {}
+void __init ns9xxx_add_device_ns921x_uartd(int gpio_start,
+ int gpio_nr, int func) {}
+#endif
+
+/* Flash */
+#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP)
+static struct resource flash_resources[] = {
+ {
+ .start = 0x50000000,
+ .end = 0x5fffffff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device ns9xxx_device_ns921x_flash = {
+ .name = "physmap-flash",
+ .id = 0,
+ .resource = flash_resources,
+ .num_resources = ARRAY_SIZE(flash_resources),
+};
+
+void __init ns9xxx_add_device_ns921x_flash(
+ struct physmap_flash_data *flash_data)
+{
+
+ ns9xxx_device_ns921x_flash.dev.platform_data = flash_data;
+ platform_device_register(&ns9xxx_device_ns921x_flash);
+}
+#else
+void __init ns9xxx_add_device_ns921x_flash(
+ struct physmap_flash_data *flash_data) {}
+#endif
+
+/* SPI port */
+#if defined(CONFIG_SPI_NS921X) || defined(CONFIG_SPI_NS921X_MODULE)
+int __init spi_register_gpios(struct spi_ns9xxx_data *data)
+{
+ int i;
+
+ for (i = 0; i < data->nr_gpios; i++) {
+ if (gpio_request(data->gpios[i], "spi_ns921x"))
+ goto err;
+ gpio_configure_ns921x(data->gpios[i],
+ 0,
+ 0,
+ data->gpio_funcs[i],
+ 0 );
+ }
+
+ return 0;
+err:
+ for (; i >= 0; i--)
+ gpio_free(data->gpios[i]);
+
+ return -EBUSY;
+}
+
+static struct ns921x_sysclk spi_clk = {
+ .clk = {
+ .name = "spi_ns921x",
+ .id = 1,
+ .owner = THIS_MODULE,
+ .endisable = ns921x_endisable_sysclock,
+ },
+ .mask = SYS_CLOCK_SPI,
+};
+
+static struct resource spi_resources[] = {
+ {
+ .start = 0x90030000,
+ .end = 0x90037fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS921X_SPI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 spi_dmamask = DMA_BIT_MASK(32);
+
+static struct platform_device ns9xxx_device_ns921x_spi = {
+ .name = "spi_ns921x",
+ .id = 1,
+ .resource = spi_resources,
+ .num_resources = ARRAY_SIZE(spi_resources),
+ .dev = {
+ .dma_mask = &spi_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+void __init ns9xxx_add_device_ns921x_spi(struct spi_ns9xxx_data *data)
+{
+ if (spi_register_gpios(data))
+ return;
+
+ spi_clk.clk.parent = clk_get(NULL, "systemclock");
+ if (IS_ERR(spi_clk.clk.parent))
+ return;
+
+ if (clk_register(&spi_clk.clk))
+ return;
+
+ ns9xxx_device_ns921x_spi.dev.platform_data = data;
+ platform_device_register(&ns9xxx_device_ns921x_spi);
+}
+#else
+void __init ns9xxx_add_device_ns921x_spi(struct spi_ns9xxx_data *data) {}
+#endif
+
+/* AES HW Encryption module */
+#if defined(CONFIG_CRYPTO_DEV_NS921X_AES) || \
+ defined(CONFIG_CRYPTO_DEV_NS921X_AES_MODULE)
+static struct ns921x_sysclk aes_clk = {
+ .clk = {
+ .name = "ns921x-aes",
+ .id = -1,
+ .owner = THIS_MODULE,
+ .endisable = ns921x_endisable_sysclock,
+ },
+ .mask = SYS_CLOCK_AES,
+};
+
+static struct resource aes_resources[] = {
+ {
+ .start = 0xa0800000,
+ .end = 0xa080000f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS921X_EXTDMA,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 aes_dmamask = DMA_BIT_MASK(32);
+
+static struct platform_device ns9xxx_device_ns921x_aes = {
+ .name = "ns921x-aes",
+ .id = 1,
+ .resource = aes_resources,
+ .num_resources = ARRAY_SIZE(aes_resources),
+ .dev = {
+ .dma_mask = &aes_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+void __init ns9xxx_add_device_ns921x_aes(void)
+{
+ aes_clk.clk.parent = clk_get(NULL, "dmaclock");
+ if (IS_ERR(aes_clk.clk.parent))
+ return;
+
+ if (clk_register(&aes_clk.clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns921x_aes);
+}
+#else
+void __init ns9xxx_add_device_ns921x_aes(void);
+#endif
+
+
+#if defined(CONFIG_PM)
+static irqreturn_t ns921x_ack_extirq(int irq, void *dev_id)
+{
+ u32 eixctl;
+
+ BUG_ON((unsigned)(irq - IRQ_NS9XXX_EXT0) > 4);
+
+ eixctl = __raw_readl(SYS_EIxCTRL(irq - IRQ_NS9XXX_EXT0));
+
+ /* ack ext irq */
+ REGSETIM(eixctl, SYS_EIxCTRL, CLEAR, 1);
+ __raw_writel(eixctl, SYS_EIxCTRL(irq - IRQ_NS9XXX_EXT0));
+ REGSETIM(eixctl, SYS_EIxCTRL, CLEAR, 0);
+ __raw_writel(eixctl, SYS_EIxCTRL(irq - IRQ_NS9XXX_EXT0));
+
+ return IRQ_HANDLED;
+}
+
+int __init ns921x_extgpio_pm_wakeup_init(unsigned int gpio)
+{
+ int ret;
+ const struct gpio_to_irq_map *map;
+
+ map = gpio_get_map_ns921x(gpio);
+ if (!map) {
+ pr_debug("%s: selected gpio %d not irq capable\n", __func__, gpio);
+ return -EINVAL;
+ }
+
+ ret = gpio_request(gpio, "cpuwake");
+ if (ret) {
+ pr_debug("%s: err_gpio_request -> %d\n", __func__, ret);
+ goto err_gpio_request;
+ }
+
+ gpio_configure_ns921x(gpio, 0, 0, map->func, 0);
+
+ ret = request_irq(map->irq, ns921x_ack_extirq, IRQF_TRIGGER_FALLING,
+ "extwakeirq", NULL);
+ if (ret) {
+ pr_debug("%s: err_request_irq_extwakeirq %d -> %d\n", __func__, map->irq, ret);
+ goto err_irq_request;
+ }
+
+ ret = enable_irq_wake(map->irq);
+ if (ret) {
+ pr_debug("%s: err_enable_irq_extwakeirq %d -> %d\n", __func__, map->irq, ret);
+ goto err_enable_wake;
+ }
+ return 0;
+
+err_enable_wake:
+ free_irq(map->irq, NULL);
+err_irq_request:
+ gpio_free(gpio);
+err_gpio_request:
+ return ret;
+}
+#else /* CONFIG_PM */
+int __init ns921x_extgpio_pm_wakeup_init(unsigned int gpio)
+{
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+#if defined(CONFIG_FIM_ZERO_SDIO)
+void __init ns9xxx_add_device_ns921x_fim_sdio0(void)
+{
+ extern struct platform_device ns921x_fim_sdio0;
+ platform_device_register(&ns921x_fim_sdio0);
+}
+#else
+void __init ns9xxx_add_device_ns921x_fim_sdio0(void) {}
+#endif
+
+#if defined(CONFIG_FIM_ONE_SDIO)
+void __init ns9xxx_add_device_ns921x_fim_sdio1(void)
+{
+ extern struct platform_device ns921x_fim_sdio1;
+ platform_device_register(&ns921x_fim_sdio1);
+}
+#else
+void __init ns9xxx_add_device_ns921x_fim_sdio1(void) {}
+#endif
+
+
+#if defined(CONFIG_FIM_ZERO_SERIAL)
+void __init ns9xxx_add_device_ns921x_fim_serial0(void)
+{
+ extern struct platform_device ns921x_fim_serial0;
+ platform_device_register(&ns921x_fim_serial0);
+}
+#else
+void __init ns9xxx_add_device_ns921x_fim_serial0(void) {}
+#endif /* CONFIG_FIM_SERIAL */
+
+#if defined(CONFIG_FIM_ONE_SERIAL)
+void __init ns9xxx_add_device_ns921x_fim_serial1(void)
+{
+ extern struct platform_device ns921x_fim_serial1;
+ platform_device_register(&ns921x_fim_serial1);
+}
+#else
+void __init ns9xxx_add_device_ns921x_fim_serial1(void) {}
+#endif /* CONFIG_FIM_SERIAL */
+
+#if defined(CONFIG_FIM_ZERO_CAN)
+void __init ns9xxx_add_device_ns921x_fim_can0(void)
+{
+ extern struct platform_device ns921x_fim_can0;
+ platform_device_register(&ns921x_fim_can0);
+}
+#else
+void __init ns9xxx_add_device_ns921x_fim_can0(void) {}
+#endif
+
+#if defined(CONFIG_FIM_ONE_CAN)
+void __init ns9xxx_add_device_ns921x_fim_can1(void)
+{
+ extern struct platform_device ns921x_fim_can1;
+ platform_device_register(&ns921x_fim_can1);
+}
+#else
+void __init ns9xxx_add_device_ns921x_fim_can1(void) {}
+#endif
+
+#if defined(CONFIG_FIM_ZERO_W1)
+static void __init ns9xxx_add_device_ns921x_fim0_w1(void)
+{
+ extern struct platform_device ns921x_fim0_w1;
+ platform_device_register(&ns921x_fim0_w1);
+}
+#else
+static inline void __init ns9xxx_add_device_ns921x_fim0_w1(void) { }
+#endif
+
+#if defined(CONFIG_FIM_ONE_W1)
+static void __init ns9xxx_add_device_ns921x_fim1_w1(void)
+{
+ extern struct platform_device ns921x_fim1_w1;
+ platform_device_register(&ns921x_fim1_w1);
+}
+#else
+static inline void __init ns9xxx_add_device_ns921x_fim1_w1(void) { }
+#endif
+
+#if defined(CONFIG_FIM_ZERO_USB)
+void __init ns9xxx_add_device_ns921x_fim_usb0(void)
+{
+ extern struct platform_device ns921x_fim_usb0;
+ platform_device_register(&ns921x_fim_usb0);
+}
+#else
+static inline void __init ns9xxx_add_device_ns921x_fim_usb0(void) {}
+#endif
+
+#if defined(CONFIG_FIM_ONE_USB)
+void __init ns9xxx_add_device_ns921x_fim_usb1(void)
+{
+ extern struct platform_device ns921x_fim_usb1;
+ platform_device_register(&ns921x_fim_usb1);
+}
+#else
+static inline void __init ns9xxx_add_device_ns921x_fim_usb1(void) {}
+#endif
+
+#if defined(CONFIG_FIM_CORE) || defined(CONFIG_FIM_MODULES)
+void __init ns9xxx_add_device_ns921x_fims(void)
+{
+ /*
+ * By the CME9210s with the CAN-support the GPIOs 2 and 23 are inter-connected.
+ * This is just problematic when these modules should use the FIM-serial
+ * interface, then the GPIO23 is not configured as input GPIO after the resets.
+ * IMPORTANT: The below code will disappear in one of the next U-Boot releases
+ * with the FIMs-support.
+ * (Luis Galdos)
+ */
+#if defined(CONFIG_FIM_CORE) && \
+ (defined(CONFIG_MACH_CME9210) || defined(CONFIG_MACH_CME9210JS))
+ gpio_direction_input_ns921x_unlocked(23);
+#endif
+
+ /* FIM 0 */
+ ns9xxx_add_device_ns921x_fim_serial0();
+ ns9xxx_add_device_ns921x_fim_sdio0();
+ ns9xxx_add_device_ns921x_fim_can0();
+ ns9xxx_add_device_ns921x_fim0_w1();
+ ns9xxx_add_device_ns921x_fim_usb0();
+
+ /* FIM 1 */
+ ns9xxx_add_device_ns921x_fim_serial1();
+ ns9xxx_add_device_ns921x_fim_sdio1();
+ ns9xxx_add_device_ns921x_fim_can1();
+ ns9xxx_add_device_ns921x_fim1_w1();
+ ns9xxx_add_device_ns921x_fim_usb1();
+}
+#else
+void __init ns9xxx_add_device_ns921x_fims(void) {}
+#endif /* CONFIG_FIM_CORE */
+
diff --git a/arch/arm/mach-ns9xxx/ns921x_devices.h b/arch/arm/mach-ns9xxx/ns921x_devices.h
new file mode 100644
index 000000000000..d1e7e7153cdf
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/ns921x_devices.h
@@ -0,0 +1,37 @@
+/*
+ * arch/arm/mach-ns9xxx/ns921x_devices.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/i2c-ns9xxx.h>
+#include <linux/init.h>
+#include <linux/mtd/physmap.h>
+#include <mach/spi.h>
+
+#include "clock.h"
+
+void __init ns9xxx_add_device_ns921x_wdt(void);
+void __init ns9xxx_add_device_ns921x_eth(struct clk *phyclk, u32 phy_mask,
+ int gpio[], int func[], int dir[], int num);
+void __init ns9xxx_add_device_ns921x_i2c(struct plat_ns9xxx_i2c *);
+void __init ns9xxx_add_device_ns921x_uarta(int gpio_start,
+ int gpio_nr, int func);
+void __init ns9xxx_add_device_ns921x_uartb(int gpio_start,
+ int gpio_nr, int func);
+void __init ns9xxx_add_device_ns921x_uartc(int gpio_start,
+ int gpio_nr, int func);
+void __init ns9xxx_add_device_ns921x_uartd(int gpio_start,
+ int gpio_nr, int func);
+void __init ns9xxx_add_device_ns921x_flash(
+ struct physmap_flash_data *flash_data);
+void __init ns9xxx_add_device_ns921x_spi(struct spi_ns9xxx_data *data);
+int __init ns921x_extgpio_pm_wakeup_init(unsigned int gpio);
+void __init ns9xxx_add_device_ns921x_fims(void);
+void __init ns9xxx_add_device_ns921x_aes(void);
diff --git a/arch/arm/mach-ns9xxx/ns9360_devices.c b/arch/arm/mach-ns9xxx/ns9360_devices.c
new file mode 100644
index 000000000000..63d2d1205c0c
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/ns9360_devices.c
@@ -0,0 +1,813 @@
+/*
+ * arch/arm/mach-ns9xxx/ns9360_devices.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
+#include <linux/ns9xxx-eth.h>
+#include <linux/platform_device.h>
+
+#include <mach/regs-lcd-ns9360.h>
+#include <mach/regs-sys-ns9360.h>
+#include <mach/gpio.h>
+#include <mach/ns9360fb.h>
+#include <mach/display/displays.h>
+
+#include "clock.h"
+#include "ns9360_devices.h"
+
+
+/* USB host */
+#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+static int usbh_endisable(struct clk *clk, int enable)
+{
+ if (enable) {
+ u32 mrst = __raw_readl(NS9360_BBU_MSR);
+ mrst |= 1 << 11;
+ __raw_writel(mrst, NS9360_BBU_MSR);
+
+ __raw_writel(0 << 2, NS9360_BBUS_USB);
+
+ mrst = __raw_readl(NS9360_BBU_MSR);
+ mrst &= ~(1 << 11);
+ __raw_writel(mrst, NS9360_BBU_MSR);
+ }
+
+ return 0;
+}
+
+static struct clk usbh_clk = {
+ .name = "ns9360-ohci",
+ .id = -1,
+ .owner = THIS_MODULE,
+ .endisable = usbh_endisable,
+};
+
+static u64 ohci_dmamask = DMA_BIT_MASK(32);
+static struct resource usbh_resources[] = {
+ {
+ .start = 0x90801000,
+ .end = 0x90801fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 0x90800000,
+ .end = 0x90800fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS9360_USBHOST,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device ns9xxx_device_ns9360_usbh = {
+ .name = "ns9360-ohci",
+ .id = -1,
+ .resource = usbh_resources,
+ .num_resources = ARRAY_SIZE(usbh_resources),
+ .dev = {
+ .dma_mask = &ohci_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+void __init ns9xxx_add_device_ns9360_usbh(void)
+{
+ if (clk_register(&usbh_clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns9360_usbh);
+}
+#else
+void __init ns9xxx_add_device_ns9360_usbh(void) {}
+#endif
+
+/* Watchdog timer */
+#if defined(CONFIG_NS9XXX_WATCHDOG) || defined(CONFIG_NS9XXX_WATCHDOG_MODULE)
+static struct clk wdt_clk = {
+ .name = "ns9xxx-wdt",
+ .id = -1,
+ .owner = THIS_MODULE,
+};
+
+static struct resource wdt_resources[] = {
+ {
+ .start = 0xa0900174,
+ .end = 0xa0900177,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device ns9xxx_device_ns9360_wdt = {
+ .name = "ns9xxx-wdt",
+ .id = -1,
+ .resource = wdt_resources,
+ .num_resources = ARRAY_SIZE(wdt_resources),
+};
+
+void __init ns9xxx_add_device_ns9360_wdt(void)
+{
+ wdt_clk.parent = clk_get(NULL, "cpuclock");
+ if (IS_ERR(wdt_clk.parent))
+ return;
+
+ if (clk_register(&wdt_clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns9360_wdt);
+}
+#else
+void __init ns9xxx_add_device_ns9360_wdt(void) {}
+#endif
+
+/* RTC */
+#if defined(CONFIG_RTC_DRV_NS9XXX) || defined(CONFIG_RTC_DRV_NS9XXX_MODULE)
+static struct clk rtc_clk = {
+ .name = "rtc-ns9xxx",
+ .id = -1,
+ .owner = THIS_MODULE,
+};
+
+static struct resource rtc_resources[] = {
+ {
+ .start = 0x90700000,
+ .end = 0x907000ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS9360_RTC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device ns9xxx_device_ns9360_rtc = {
+ .name = "rtc-ns9xxx",
+ .id = 0,
+ .resource = rtc_resources,
+ .num_resources = ARRAY_SIZE(rtc_resources),
+};
+
+void __init ns9xxx_add_device_ns9360_rtc(void)
+{
+ struct clk *sysclk = clk_get(NULL, "systemclock");
+ if (IS_ERR(sysclk))
+ return;
+
+ __raw_writel(clk_get_rate(sysclk) / 200, SYS_RTCCC);
+ clk_put(sysclk);
+
+ if (clk_register(&rtc_clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns9360_rtc);
+}
+#else
+void __init ns9xxx_add_device_ns9360_rtc(void) {}
+#endif
+
+/* Ethernet */
+#if defined(CONFIG_NS9XXX_ETH) || defined(CONFIG_NS9XXX_ETH_MODULE)
+int __init eth_register_gpios(int gpio[], int gpio_nr, int func)
+{
+ int i;
+
+ for (i = 0; i < gpio_nr; i++) {
+ if (gpio_request(gpio[i], "ns9xxx-eth"))
+ goto err;
+ gpio_configure_ns9360(gpio[i], 0, 0, func);
+ }
+
+ return 0;
+err:
+ for (; i >= 0; i--)
+ gpio_free(gpio[i]);
+
+ return -EBUSY;
+}
+
+static struct clk eth_clk = {
+ .name = "ns9xxx-eth",
+ .id = -1,
+ .owner = THIS_MODULE,
+};
+
+static struct plat_ns9xxx_eth ns9xxx_device_ns9360_eth_data = {
+ .irqrx = IRQ_NS9XXX_ETHRX,
+ .irqtx = IRQ_NS9XXX_ETHTX,
+};
+
+static struct resource eth_resources[] = {
+ {
+ .start = 0xa0600000,
+ .end = 0xa06027ff,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device ns9xxx_device_ns9360_eth = {
+ .name = "ns9xxx-eth",
+ .id = -1,
+ .resource = eth_resources,
+ .num_resources = ARRAY_SIZE(eth_resources),
+ .dev = {
+ .platform_data = &ns9xxx_device_ns9360_eth_data,
+ },
+};
+
+void __init ns9xxx_add_device_ns9360_eth(int gpio[], int gpio_nr, int func, u32 phy_mask)
+{
+ if (eth_register_gpios(gpio, gpio_nr, func))
+ return;
+
+ if (clk_register(&eth_clk))
+ return;
+
+ ns9xxx_device_ns9360_eth_data.phy_mask = phy_mask;
+
+ platform_device_register(&ns9xxx_device_ns9360_eth);
+}
+#else
+void __init ns9xxx_add_device_ns9360_eth(int gpio[], int gpio_nr, int func, u32 phy_mask) {}
+#endif
+
+/* UARTs */
+#if defined(CONFIG_SERIAL_NS9360) || defined(CONFIG_SERIAL_NS9360_MODULE)
+static struct clk uarta_clk = {
+ .name = "ns9360-serial",
+ .id = 1,
+ .owner = THIS_MODULE,
+};
+
+static struct clk uartb_clk = {
+ .name = "ns9360-serial",
+ .id = 0,
+ .owner = THIS_MODULE,
+};
+
+static struct clk uartc_clk = {
+ .name = "ns9360-serial",
+ .id = 2,
+ .owner = THIS_MODULE,
+};
+
+static struct clk uartd_clk = {
+ .name = "ns9360-serial",
+ .id = 3,
+ .owner = THIS_MODULE,
+};
+
+static struct resource uarta_resources[] = {
+ {
+ .start = 0x90200040,
+ .end = 0x9020007f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS9360_SERARX,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource uartb_resources[] = {
+ {
+ .start = 0x90200000,
+ .end = 0x9020003f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS9360_SERBRX,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource uartc_resources[] = {
+ {
+ .start = 0x90300000,
+ .end = 0x9030003f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS9360_SERCRX,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource uartd_resources[] = {
+ {
+ .start = 0x90300040,
+ .end = 0x9030007f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS9360_BBUS_SERDRX,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device ns9xxx_device_ns9360_uarta = {
+ .name = "ns9360-serial",
+ .id = 1,
+ .resource = uarta_resources,
+ .num_resources = ARRAY_SIZE(uarta_resources),
+};
+
+static struct platform_device ns9xxx_device_ns9360_uartb = {
+ .name = "ns9360-serial",
+ .id = 0,
+ .resource = uartb_resources,
+ .num_resources = ARRAY_SIZE(uartb_resources),
+};
+
+static struct platform_device ns9xxx_device_ns9360_uartc = {
+ .name = "ns9360-serial",
+ .id = 2,
+ .resource = uartc_resources,
+ .num_resources = ARRAY_SIZE(uartc_resources),
+};
+
+static struct platform_device ns9xxx_device_ns9360_uartd = {
+ .name = "ns9360-serial",
+ .id = 3,
+ .resource = uartd_resources,
+ .num_resources = ARRAY_SIZE(uartd_resources),
+};
+
+int __init uart_register_gpios(int gpio[], int gpio_nr, int func)
+{
+ int i;
+
+ for (i = 0; i < gpio_nr; i++) {
+ if (gpio_request(gpio[i], "ns9360-serial"))
+ goto err;
+ gpio_configure_ns9360(gpio[i], 0, 0, func);
+ }
+
+ return 0;
+err:
+ for (; i >= 0; i--)
+ gpio_free(gpio[i]);
+
+ return -EBUSY;
+}
+
+void __init ns9xxx_add_device_ns9360_uarta(int gpio[], int gpio_nr, int func)
+{
+ if (uart_register_gpios(gpio, gpio_nr, func))
+ return;
+
+ uarta_clk.parent = clk_get(NULL, "bbusclock");
+ if (IS_ERR(uarta_clk.parent))
+ return;
+
+ if (clk_register(&uarta_clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns9360_uarta);
+}
+
+void __init ns9xxx_add_device_ns9360_uartb(int gpio[], int gpio_nr, int func)
+{
+ if (uart_register_gpios(gpio, gpio_nr, func))
+ return;
+
+ uartb_clk.parent = clk_get(NULL, "bbusclock");
+ if (IS_ERR(uartb_clk.parent))
+ return;
+
+ if (clk_register(&uartb_clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns9360_uartb);
+}
+
+void __init ns9xxx_add_device_ns9360_uartc(int gpio[], int gpio_nr, int func)
+{
+ if (uart_register_gpios(gpio, gpio_nr, func))
+ return;
+
+ uartc_clk.parent = clk_get(NULL, "bbusclock");
+ if (IS_ERR(uartc_clk.parent))
+ return;
+
+ if (clk_register(&uartc_clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns9360_uartc);
+}
+
+void __init ns9xxx_add_device_ns9360_uartd(int gpio[], int gpio_nr, int func)
+{
+ if (uart_register_gpios(gpio, gpio_nr, func))
+ return;
+
+ uartd_clk.parent = clk_get(NULL, "bbusclock");
+ if (IS_ERR(uartd_clk.parent))
+ return;
+
+ if (clk_register(&uartd_clk))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns9360_uartd);
+}
+#else
+void __init ns9xxx_add_device_ns9360_uarta(int gpio[], int gpio_nr, int func) {}
+void __init ns9xxx_add_device_ns9360_uartb(int gpio[], int gpio_nr, int func) {}
+void __init ns9xxx_add_device_ns9360_uartc(int gpio[], int gpio_nr, int func) {}
+void __init ns9xxx_add_device_ns9360_uartd(int gpio[], int gpio_nr, int func) {}
+#endif
+
+/* NAND flash */
+#if defined(CONFIG_MTD_NAND_CCX9X) || defined(CONFIG_MTD_NAND_CCX9X_MODULE)
+static struct resource nand_resources[] = {
+ {
+ .start = 0x50000000,
+ .end = 0x5fffffff,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device ns9xxx_device_ns9360_nand = {
+ .name = "ccx9x_nand",
+ .id = -1,
+ .resource = nand_resources,
+ .num_resources = ARRAY_SIZE(nand_resources),
+};
+
+void __init ns9xxx_add_device_ns9360_nand(struct ccx9x_nand_info *nand_data)
+{
+ ns9xxx_device_ns9360_nand.dev.platform_data = nand_data;
+ platform_device_register(&ns9xxx_device_ns9360_nand);
+}
+#else
+void __init ns9xxx_add_device_ns9360_nand(struct ccx9x_nand_info *nand_data) {}
+#endif
+
+/* I2C controller */
+#if defined(CONFIG_I2C_NS9XXX) || defined(CONFIG_I2C_NS9XXX_MODULE)
+static struct clk i2c_clk = {
+ .name = "i2c-ns9xxx",
+ .id = -1,
+ .owner = THIS_MODULE,
+};
+
+static struct resource i2c_resources[] = {
+ {
+ .start = 0x90500000,
+ .end = 0x9050000f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_NS9360_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device ns9xxx_device_ns9360_i2c = {
+ .name = "i2c-ns9xxx",
+ .id = -1,
+ .resource = i2c_resources,
+ .num_resources = ARRAY_SIZE(i2c_resources),
+};
+
+void __init ns9xxx_add_device_ns9360_i2c(struct plat_ns9xxx_i2c *i2c_data)
+{
+ i2c_clk.parent = clk_get(NULL, "systemclock");
+ if (IS_ERR(i2c_clk.parent))
+ return;
+
+ if (clk_register(&i2c_clk))
+ return;
+
+ ns9xxx_device_ns9360_i2c.dev.platform_data = i2c_data;
+ platform_device_register(&ns9xxx_device_ns9360_i2c);
+}
+#else
+void __init ns9xxx_add_device_ns9360_i2c(struct plat_ns9xxx_i2c *i2c_data) {}
+#endif
+
+/* Framebuffer controller */
+#if (defined(CONFIG_FB_NS9360) || defined(CONFIG_FB_NS9360_MODULE)) && !defined(NS9XXX_NODISPLAY)
+int __init fb_register_gpios(int gpio[], int gpio_nr, int func[], int power)
+{
+ int i;
+
+ for (i = 0; i < gpio_nr; i++) {
+ if (gpio_request(gpio[i], "ns9360fb"))
+ goto err;
+ gpio_configure_ns9360(gpio[i], 0, 0, func[i]);
+ }
+
+ if (gpio_request(power, "ns9360fb")) {
+ i--;
+ goto err;
+ }
+ gpio_direction_output(power, 0);
+
+ return 0;
+err:
+ for (; i >= 0; i--)
+ gpio_free(gpio[i]);
+
+ return -EBUSY;
+}
+
+static int fb_endisable(struct clk *clk, int enable)
+{
+ /* Currently, nothing to do */
+ return 0;
+}
+
+static struct clk fb_clk = {
+ .name = "ns9360fb",
+ .id = -1,
+ .owner = THIS_MODULE,
+ .endisable = fb_endisable,
+
+};
+static struct ns9360fb_pdata ns9xxx_device_ns9360_fb_data = {
+ .displays = display_list,
+ .num_displays = ARRAY_SIZE(display_list),
+};
+
+static struct resource fb_resources[] = {
+ {
+ .start = 0xa0800000,
+ .end = 0xa080002f,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device ns9xxx_device_ns9360_fb = {
+ .name = "ns9360fb",
+ .id = -1,
+ .resource = fb_resources,
+ .num_resources = ARRAY_SIZE(fb_resources),
+ .dev = {
+ .platform_data = &ns9xxx_device_ns9360_fb_data,
+ },
+};
+
+void __init ns9xxx_add_device_ns9360_fb(int gpio[], int gpio_nr,
+ int func[], int power)
+{
+ fb_clk.parent = clk_get(NULL, "ahbclock");
+ if (IS_ERR(fb_clk.parent))
+ return;
+
+ if (clk_register(&fb_clk))
+ return;
+
+ if (fb_register_gpios(gpio, gpio_nr, func, power))
+ return;
+
+ platform_device_register(&ns9xxx_device_ns9360_fb);
+}
+#else
+void __init ns9xxx_add_device_ns9360_fb(int gpio[], int gpio_nr,
+ int func[], int power) {}
+#endif
+
+#if defined(CONFIG_SPI_NS9360) || defined(CONFIG_SPI_NS9360_MODULE)
+int __init spi_register_gpios(struct spi_ns9xxx_data *data)
+{
+ int i;
+
+ for (i = 0; i < data->nr_gpios; i++) {
+ if (gpio_request(data->gpios[i], "spi_ns9360"))
+ goto err;
+ gpio_configure_ns9360( data->gpios[i],
+ 0,
+ 0,
+ data->gpio_funcs[i] );
+ }
+
+ return 0;
+err:
+ for (; i >= 0; i--)
+ gpio_free(data->gpios[i]);
+
+ return -EBUSY;
+}
+
+static struct clk spi_porta_clk = {
+ .name = "spi_ns9360",
+ .id = 1,
+ .owner = THIS_MODULE,
+};
+
+static struct clk spi_portb_clk = {
+ .name = "spi_ns9360",
+ .id = 0,
+ .owner = THIS_MODULE,
+};
+
+static struct clk spi_portc_clk = {
+ .name = "spi_ns9360",
+ .id = 2,
+ .owner = THIS_MODULE,
+};
+
+static struct clk spi_portd_clk = {
+ .name = "spi_ns9360",
+ .id = 3,
+ .owner = THIS_MODULE,
+};
+
+static struct resource spi_porta_resources[] = {
+ {
+ .start = 0x90200040,
+ .end = 0x9020007f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 0x90000040,
+ .end = 0x9000007f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 60,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = 61,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource spi_portb_resources[] = {
+ {
+ .start = 0x90200000,
+ .end = 0x9020003f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 0x90000000,
+ .end = 0x9000003f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 58,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = 59,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource spi_portc_resources[] = {
+ {
+ .start = 0x90300000,
+ .end = 0x9020003f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 0x90000080,
+ .end = 0x900000bf,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 62,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = 63,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource spi_portd_resources[] = {
+ {
+ .start = 0x90300040,
+ .end = 0x9030007f,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 0x900000c0,
+ .end = 0x900000ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 64,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = 65,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 spi_dmamask = DMA_BIT_MASK(32);
+
+static struct platform_device ns9xxx_device_ns9360_spi_porta = {
+ .name = "spi_ns9360",
+ .id = 1,
+ .resource = spi_porta_resources,
+ .num_resources = ARRAY_SIZE(spi_porta_resources),
+ .dev = {
+ .dma_mask = &spi_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct platform_device ns9xxx_device_ns9360_spi_portb = {
+ .name = "spi_ns9360",
+ .id = 0,
+ .resource = spi_portb_resources,
+ .num_resources = ARRAY_SIZE(spi_portb_resources),
+ .dev = {
+ .dma_mask = &spi_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct platform_device ns9xxx_device_ns9360_spi_portc = {
+ .name = "spi_ns9360",
+ .id = 2,
+ .resource = spi_portc_resources,
+ .num_resources = ARRAY_SIZE(spi_portc_resources),
+ .dev = {
+ .dma_mask = &spi_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct platform_device ns9xxx_device_ns9360_spi_portd = {
+ .name = "spi_ns9360",
+ .id = 3,
+ .resource = spi_portd_resources,
+ .num_resources = ARRAY_SIZE(spi_portd_resources),
+ .dev = {
+ .dma_mask = &spi_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+void __init ns9xxx_add_device_ns9360_spi_porta(struct spi_ns9xxx_data *data)
+{
+ if (spi_register_gpios(data))
+ return;
+
+ spi_porta_clk.parent = clk_get(NULL, "bbusclock");
+ if (IS_ERR(spi_porta_clk.parent))
+ return;
+
+ if (clk_register(&spi_porta_clk))
+ return;
+
+ /* Set platform data */
+ ns9xxx_device_ns9360_spi_porta.dev.platform_data = data;
+
+ platform_device_register(&ns9xxx_device_ns9360_spi_porta);
+}
+
+void __init ns9xxx_add_device_ns9360_spi_portb( struct spi_ns9xxx_data *data )
+{
+ if (spi_register_gpios(data))
+ return;
+
+ spi_portb_clk.parent = clk_get(NULL, "bbusclock");
+ if (IS_ERR(spi_portb_clk.parent))
+ return;
+
+ if (clk_register(&spi_portb_clk))
+ return;
+
+ /* Set platform data */
+ ns9xxx_device_ns9360_spi_portb.dev.platform_data = data;
+
+ platform_device_register(&ns9xxx_device_ns9360_spi_portb);
+}
+
+
+void __init ns9xxx_add_device_ns9360_spi_portc(struct spi_ns9xxx_data *data)
+{
+ if (spi_register_gpios(data))
+ return;
+
+ spi_portc_clk.parent = clk_get(NULL, "bbusclock");
+ if (IS_ERR(spi_portc_clk.parent))
+ return;
+
+ if (clk_register(&spi_portc_clk))
+ return;
+
+ /* Set platform data */
+ ns9xxx_device_ns9360_spi_portc.dev.platform_data = data;
+
+ platform_device_register(&ns9xxx_device_ns9360_spi_portc);
+}
+
+void __init ns9xxx_add_device_ns9360_spi_portd(struct spi_ns9xxx_data *data)
+{
+ if (spi_register_gpios(data))
+ return;
+
+ spi_portd_clk.parent = clk_get(NULL, "bbusclock");
+ if (IS_ERR(spi_portd_clk.parent))
+ return;
+
+ if (clk_register(&spi_portd_clk))
+ return;
+
+ /* Set platform data */
+ ns9xxx_device_ns9360_spi_portd.dev.platform_data = data;
+
+ platform_device_register(&ns9xxx_device_ns9360_spi_portd);
+}
+#else
+void __init ns9xxx_add_device_ns9360_spi_porta(struct spi_ns9xxx_data *data) {}
+void __init ns9xxx_add_device_ns9360_spi_portb(struct spi_ns9xxx_data *data) {}
+void __init ns9xxx_add_device_ns9360_spi_portc(struct spi_ns9xxx_data *data) {}
+void __init ns9xxx_add_device_ns9360_spi_portd(struct spi_ns9xxx_data *data) {}
+#endif
diff --git a/arch/arm/mach-ns9xxx/ns9360_devices.h b/arch/arm/mach-ns9xxx/ns9360_devices.h
new file mode 100644
index 000000000000..1994532aa3a1
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/ns9360_devices.h
@@ -0,0 +1,32 @@
+/*
+ * arch/arm/mach-ns9xxx/ns9360_devices.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/i2c-ns9xxx.h>
+#include <linux/init.h>
+#include <linux/mtd/ccx9x_nand.h>
+#include <mach/spi.h>
+
+void __init ns9xxx_add_device_ns9360_usbh(void);
+void __init ns9xxx_add_device_ns9360_wdt(void);
+void __init ns9xxx_add_device_ns9360_rtc(void);
+void __init ns9xxx_add_device_ns9360_eth(int gpio[], int gpio_nr, int func, u32 phy_mask);
+void __init ns9xxx_add_device_ns9360_uarta(int gpio[], int gpio_nr, int func);
+void __init ns9xxx_add_device_ns9360_uartb(int gpio[], int gpio_nr, int func);
+void __init ns9xxx_add_device_ns9360_uartc(int gpio[], int gpio_nr, int func);
+void __init ns9xxx_add_device_ns9360_uartd(int gpio[], int gpio_nr, int func);
+void __init ns9xxx_add_device_ns9360_nand(struct ccx9x_nand_info *);
+void __init ns9xxx_add_device_ns9360_i2c(struct plat_ns9xxx_i2c *);
+void __init ns9xxx_add_device_ns9360_fb(int gpio[], int gpio_nr,
+ int func[], int power);
+void __init ns9xxx_add_device_ns9360_spi_porta(struct spi_ns9xxx_data *data);
+void __init ns9xxx_add_device_ns9360_spi_portb(struct spi_ns9xxx_data *data);
+void __init ns9xxx_add_device_ns9360_spi_portc(struct spi_ns9xxx_data *data);
+void __init ns9xxx_add_device_ns9360_spi_portd(struct spi_ns9xxx_data *data);
diff --git a/arch/arm/mach-ns9xxx/pm-ns921x.c b/arch/arm/mach-ns9xxx/pm-ns921x.c
new file mode 100644
index 000000000000..5ecd5bb5b1bf
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/pm-ns921x.c
@@ -0,0 +1,173 @@
+/*
+ * arch/arm/mach-ns9xxx/pm-ns921x.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/gpio.h>
+
+#include <asm/io.h>
+#include <asm/leds.h>
+
+#include <mach/processor.h>
+#include <mach/regs-sys-ns921x.h>
+#include <mach/irqs.h>
+
+#include "processor-ns921x.h"
+
+#define NS921X_PM_ENET (1 << 0)
+#define NS921X_PM_UART (0xf << 1)
+#define NS921X_PM_SPI (1 << 5)
+#define NS921X_PM_I2C (1 << 11)
+#define NS921X_PM_RTC (1 << 12)
+#define NS921X_PM_I2C (1 << 11)
+#define NS921X_PM_EXT0 (1 << 16)
+#define NS921X_PM_EXT1 (1 << 17)
+#define NS921X_PM_EXT2 (1 << 18)
+#define NS921X_PM_EXT3 (1 << 19)
+#define NS921X_PM_ALL_WK 0xf183f
+
+#if defined(DEBUG)
+#define REGMAP(reg) { .name = #reg, .addr = reg, }
+static const struct {
+ const char *name;
+ void __iomem *addr;
+} regmap[] = {
+ REGMAP(SYS_ISA),
+ REGMAP(SYS_ISR),
+ REGMAP(SYS_CLOCK),
+ REGMAP(SYS_POWER),
+};
+
+static void dump_regs(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(regmap); ++i)
+ pr_debug("%s = 0x%08x\n", regmap[i].name,
+ __raw_readl(regmap[i].addr));
+}
+#else
+#define dump_regs() ((void)0)
+#endif
+
+static irqreturn_t ns921x_cpuwake_irq(int irq, void *dev_id)
+{
+ u32 power = __raw_readl(SYS_POWER);
+
+ /* ack wake irq */
+ __raw_writel(power | SYS_POWER_INTCLR, SYS_POWER);
+ __raw_writel(power, SYS_POWER);
+
+ return IRQ_HANDLED;
+}
+
+static int ns921x_pm_prepare(void)
+{
+ int ret;
+
+ ret = request_irq(IRQ_NS921X_CPUWAKE, ns921x_cpuwake_irq, 0, "cpuwake",
+ NULL);
+ if (ret) {
+ pr_debug("%s: err_%s -> %d\n",
+ __func__, "request_irq_cpuwake", ret);
+ }
+
+ return ret;
+}
+
+extern int ns9xxx_is_enabled_irq(unsigned int irq);
+
+static int ns921x_pm_enter(suspend_state_t state)
+{
+ u32 power = __raw_readl(SYS_POWER) | SYS_POWER_SLFRFSH;
+
+ /* ack any possible wake up irq before going to sleep... */
+ __raw_writel(power | SYS_POWER_INTCLR, SYS_POWER);
+ __raw_writel(power, SYS_POWER);
+
+ power &= NS921X_PM_ALL_WK;
+ if (!power)
+ goto pm_enter_err;
+
+ /* Verify that, if only one source is enabled that at least
+ * that irq is enabled */
+ if (!(power & ~NS921X_PM_ENET)) {
+ if (!ns9xxx_is_enabled_irq(IRQ_NS9XXX_ETHRX))
+ goto pm_enter_err;
+ }
+
+ if (!(power & ~NS921X_PM_UART)) {
+ if (!ns9xxx_is_enabled_irq(IRQ_NS921X_UARTA) |
+ !ns9xxx_is_enabled_irq(IRQ_NS921X_UARTB) |
+ !ns9xxx_is_enabled_irq(IRQ_NS921X_UARTC) |
+ !ns9xxx_is_enabled_irq(IRQ_NS921X_UARTD))
+ goto pm_enter_err;
+ }
+
+ if (!(power & ~NS921X_PM_RTC)) {
+ if (!ns9xxx_is_enabled_irq(IRQ_NS9215_RTC))
+ goto pm_enter_err;
+ }
+
+ if (!(power & ~NS921X_PM_EXT0)) {
+ if (!ns9xxx_is_enabled_irq(IRQ_NS9XXX_EXT0))
+ goto pm_enter_err;
+ }
+
+ if (!(power & ~NS921X_PM_EXT1)) {
+ if (!ns9xxx_is_enabled_irq(IRQ_NS9XXX_EXT1))
+ goto pm_enter_err;
+ }
+
+ if (!(power & ~NS921X_PM_EXT2)) {
+ if (!ns9xxx_is_enabled_irq(IRQ_NS9XXX_EXT2))
+ goto pm_enter_err;
+ }
+
+ if (!(power & ~NS921X_PM_EXT3)) {
+ if (!ns9xxx_is_enabled_irq(IRQ_NS9XXX_EXT3))
+ goto pm_enter_err;
+ }
+
+ leds_event(led_idle_start);
+ asm volatile("mcr p15, 0, %0, c7, c0, 4" : : "r" (0));
+ leds_event(led_idle_end);
+
+ return 0;
+
+pm_enter_err:
+ pr_warning("No wakeup source or interface disabled, not going to sleep (0x%08x)\n",
+ __raw_readl(SYS_POWER));
+ return -EDEADLK;
+
+}
+
+static void ns921x_pm_finish(void)
+{
+ free_irq(IRQ_NS921X_CPUWAKE, NULL);
+}
+
+static struct platform_suspend_ops ns921x_pm_ops = {
+ .valid = suspend_valid_only_mem,
+ .prepare = ns921x_pm_prepare,
+ .enter = ns921x_pm_enter,
+ .finish = ns921x_pm_finish,
+};
+
+static int __init ns921x_pm_init(void)
+{
+ if (!processor_is_ns921x())
+ return -ENODEV;
+
+ suspend_set_ops(&ns921x_pm_ops);
+ return 0;
+}
+arch_initcall(ns921x_pm_init);
diff --git a/arch/arm/mach-ns9xxx/processor-ns921x.c b/arch/arm/mach-ns9xxx/processor-ns921x.c
new file mode 100644
index 000000000000..cc0a49ccbc87
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/processor-ns921x.c
@@ -0,0 +1,379 @@
+/*
+ * arch/arm/mach-ns9xxx/processor-ns921x.c
+ *
+ * Copyright (C) 2007,2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/cpufreq.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <asm/page.h>
+#include <asm/mach/map.h>
+
+#include <mach/regs-mem.h>
+#include <mach/regs-sys-ns921x.h>
+
+#include "clock.h"
+
+#include "processor-ns921x.h"
+#include "gpiolib-ns921x.h"
+
+void ns921x_reset(char mode)
+{
+ unsigned long pll = __raw_readl(SYS_PLL);
+ __raw_writel(pll, SYS_PLL);
+}
+
+#define DIVMASK(mask) ((mask) & (-(mask)))
+#define MASKVALUE(value, mask) (((value) & (mask)) / DIVMASK(mask))
+
+unsigned long ns921x_systemclock(void)
+{
+ unsigned long pll = __raw_readl(SYS_PLL);
+ unsigned pll_nr = MASKVALUE(pll, SYS_PLL_NR);
+ unsigned pll_nf = MASKVALUE(pll, SYS_PLL_NF);
+ unsigned pll_od = MASKVALUE(pll, SYS_PLL_OD);
+
+ return NS921X_REFCLOCK / (pll_nr + 1) * (pll_nf + 1) / (pll_od + 1);
+}
+static unsigned long ns921x_get_systemclock_rate(struct clk *clk)
+{
+ return ns921x_systemclock();
+}
+
+static inline unsigned long ahbclock(u32 clock)
+{
+ return ns921x_systemclock() >> (MASKVALUE(clock, SYS_CLOCK_CSC) + 2);
+}
+
+unsigned long ns921x_ahbclock(void)
+{
+ unsigned long clock = __raw_readl(SYS_CLOCK);
+ return ahbclock(clock);
+}
+
+static unsigned long ns921x_get_ahbclock_rate(struct clk *clk)
+{
+ return ns921x_ahbclock();
+}
+
+static inline unsigned long cpuclock(u32 clock)
+{
+ return ahbclock(clock) << MASKVALUE(clock, SYS_CLOCK_CSSEL);
+}
+
+static unsigned long ns921x_cpuclock(void)
+{
+ unsigned long clock = __raw_readl(SYS_CLOCK);
+ return cpuclock(clock);
+}
+
+static unsigned long ns921x_get_cpuclock_rate(struct clk *clk)
+{
+ return ns921x_cpuclock();
+}
+
+#if defined(CONFIG_CPU_FREQ)
+static inline int ns921x_freq2clock(unsigned int freq /* [kHz] */,
+ u32 cssel)
+{
+ /* valid range for CSC = [0...4] */
+ int i, found = 0;
+
+ BUG_ON(cssel && (cssel != SYS_CLOCK_CSSEL));
+ pr_debug("%s: freq = %u\n", __func__, freq);
+
+ for (i = 4; i >= 0; --i) {
+ u32 clock = i << 29 | cssel;
+ found = cpuclock(clock) >= 1000UL * freq;
+ if (found)
+ break;
+ }
+ if (!found) {
+ pr_debug("%s(%u, 0x%x) failed\n", __func__, freq, cssel);
+ BUG();
+ }
+
+ return i << 29 | cssel;
+}
+
+/* refresh_timer hold the value of the MEM_DMRT register for full speed
+ * operation (SYS_CLOCK_SYS = 0b000)
+ */
+static u32 refresh_timer;
+
+static int ns921x_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ unsigned int freq;
+ u32 cssel = 0;
+
+ pr_debug("%s\n", __func__);
+ /* This is an UP machine */
+ if (policy->cpu)
+ return -EINVAL;
+
+ if (policy->policy == CPUFREQ_POLICY_PERFORMANCE)
+ cssel = SYS_CLOCK_CSSEL;
+
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+
+ /* XXX: make sure there is at least one frequency within the policy */
+ freq = cpuclock(ns921x_freq2clock(policy->min, cssel)) / 1000;
+ if (freq > policy->max)
+ policy->max = freq;
+
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+
+ pr_debug("/%s\n", __func__);
+ return 0;
+}
+
+static inline void update_refresh_timer(u32 clock)
+{
+ u32 rt = (refresh_timer >> (clock >> 29));
+
+ if (unlikely(!rt)) {
+ pr_warning("Cannot adapt the Dynamic Memory Refresh Timer: "
+ "clock=0x%08x, rftimer=0x%08x",
+ clock, refresh_timer);
+ rt = 1;
+ } else {
+ if (unlikely(rt > 0x7ff))
+ rt = 0x7ff;
+ pr_debug("%s: MEM_DMRT <- 0x%08x\n", __func__, rt);
+ }
+ __raw_writel(rt, MEM_DMRT);
+}
+
+static int ns921x_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct cpufreq_freqs freqs;
+ unsigned long flags;
+ u32 cssel = 0;
+ u32 clock;
+
+ pr_debug("%s\n", __func__);
+ if (policy->policy == CPUFREQ_POLICY_PERFORMANCE)
+ cssel = SYS_CLOCK_CSSEL;
+
+ clock = ns921x_freq2clock(target_freq, cssel);
+
+ pr_debug("%s: targetfreq = %u, relation = %u, new clock = %lx\n",
+ __func__, target_freq, relation, (unsigned long)clock);
+
+ if (relation == CPUFREQ_RELATION_H &&
+ cpuclock(clock) / 1000 != target_freq) {
+ clock += 1 << 29;
+ BUG_ON((clock >> 29) > 4);
+ }
+
+ freqs.cpu = policy->cpu;
+ freqs.old = policy->cur;
+
+ freqs.new = cpuclock(clock) / 1000;
+
+ pr_debug("%s: new = %u kHz, CLOCK=%lx\n", __func__, freqs.new,
+ (unsigned long)clock);
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ local_irq_save(flags);
+
+ if (freqs.old > freqs.new)
+ update_refresh_timer(clock);
+
+ clock |= __raw_readl(SYS_CLOCK) & ~(SYS_CLOCK_CSC | SYS_CLOCK_CSSEL);
+
+ pr_debug("%s: SYS_CLOCK <- %x (%lu)\n", __func__, clock, cpuclock(clock));
+ __raw_writel(clock, SYS_CLOCK);
+
+ if (freqs.old < freqs.new)
+ update_refresh_timer(clock);
+
+ local_irq_restore(flags);
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ pr_debug("/%s\n", __func__);
+
+ return 0;
+}
+
+static unsigned int ns921x_cpufreq_get(unsigned int cpu)
+{
+ BUG_ON(cpu);
+
+ return ns921x_cpuclock() / 1000;
+}
+
+static int ns921x_cpufreq_init(struct cpufreq_policy *policy)
+{
+ u32 clock = __raw_readl(SYS_CLOCK);
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+ policy->policy = CPUFREQ_POLICY_PERFORMANCE;
+ policy->cpuinfo.min_freq = cpuclock(4 << 29 | SYS_CLOCK_CSSEL) / 1000;
+ policy->cpuinfo.max_freq = cpuclock(SYS_CLOCK_CSSEL) / 1000;
+ policy->cpuinfo.transition_latency = 1; /* XXX ??? */
+ policy->cur = ns921x_cpufreq_get(0);
+ policy->min = policy->cpuinfo.min_freq;
+ policy->max = policy->cpuinfo.max_freq;
+
+ refresh_timer = __raw_readl(MEM_DMRT) << (clock >> 29);
+
+ pr_info("ns921x CPU frequency change support initialized\n");
+ pr_debug("%s: cur = %u, min = %d, max = %d\n", __func__, policy->cur,
+ policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
+
+ return 0;
+}
+
+static struct cpufreq_driver ns921x_cpufreq_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = ns921x_cpufreq_verify,
+ .target = ns921x_cpufreq_target,
+ .get = ns921x_cpufreq_get,
+ .init = ns921x_cpufreq_init,
+ .name = "ns921x",
+};
+#endif
+
+static struct map_desc ns921x_io_desc[] __initdata = {
+ { /* Memory Controller */
+ .virtual = io_p2v(0xa0700000),
+ .pfn = __phys_to_pfn(0xa0700000),
+ .length = 0x27c,
+ .type = MT_DEVICE,
+ },
+ { /* External DMA Module */
+ .virtual = io_p2v(0xa0800000),
+ .pfn = __phys_to_pfn(0xa0800000),
+ .length = 0x20,
+ .type = MT_DEVICE,
+ },
+ { /* System Control Module */
+ .virtual = io_p2v(0xa0900000),
+ .pfn = __phys_to_pfn(0xa0900000),
+ .length = 0x1000,
+ .type = MT_DEVICE,
+ },
+ { /* I/O Control Module */
+ .virtual = io_p2v(0xa0902000),
+ .pfn = __phys_to_pfn(0xa0902000),
+ .length = 0x90,
+ .type = MT_DEVICE,
+ },
+};
+
+void __init ns921x_map_io(void)
+{
+ iotable_init(ns921x_io_desc, ARRAY_SIZE(ns921x_io_desc));
+}
+
+static inline void ns921x_setclock(u32 mask, u32 value)
+{
+ u32 oldclock = __raw_readl(SYS_CLOCK), clock;
+
+ BUG_ON(value & ~mask);
+
+ clock = (oldclock & ~mask) | value;
+
+ __raw_writel(clock, SYS_CLOCK);
+}
+
+static inline void ns921x_setreset(u32 mask, u32 value)
+{
+ u32 oldreset = __raw_readl(SYS_RESET), reset;
+
+ BUG_ON(value & ~mask);
+
+ reset = (oldreset & ~mask) | value;
+
+ if (reset != oldreset)
+ __raw_writel(reset, SYS_RESET);
+}
+
+int ns921x_endisable_sysclock(struct clk *clk, int enable)
+{
+ struct ns921x_sysclk *sysclk =
+ container_of(clk, struct ns921x_sysclk, clk);
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ if (enable) {
+ ns921x_setreset(sysclk->mask, sysclk->mask);
+ ns921x_setclock(sysclk->mask, sysclk->mask);
+ } else {
+ ns921x_setclock(sysclk->mask, 0);
+#if defined(CONFIG_RESET_DISABLED_MODULES)
+ ns921x_setreset(sysclk->mask, 0);
+#endif
+ }
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+void ns921x_init_machine(void)
+{
+ /* register system, ahb and cpu clocks */
+ struct clk *clk = kzalloc(3 * sizeof(*clk), GFP_KERNEL);
+ struct ns921x_sysclk *dmaclk;
+ int ret, i;
+
+ if (!clk) {
+ pr_warning("%s: could not register system clocks\n", __func__);
+ return;
+ }
+
+ clk[0].name = "systemclock";
+ clk[0].get_rate = ns921x_get_systemclock_rate;
+
+ clk[1].name = "ahbclock";
+ clk[1].get_rate = ns921x_get_ahbclock_rate;
+
+ clk[2].name = "cpuclock";
+ clk[2].get_rate = ns921x_get_cpuclock_rate;
+
+ for (i = 0; i < 3; ++i) {
+ clk[i].id = -1;
+ ret = clk_register(&clk[i]);
+ if (ret)
+ pr_warning("%s: could not register %s\n",
+ __func__, clk[i].name);
+ }
+
+ dmaclk = kzalloc(sizeof(*dmaclk), GFP_KERNEL);
+ if (!dmaclk) {
+ pr_warning("%s: could not register dma clock\n", __func__);
+ return;
+ }
+
+ dmaclk->clk.name = "dmaclock";
+ dmaclk->clk.id = -1;
+ dmaclk->clk.endisable = ns921x_endisable_sysclock;
+ dmaclk->clk.owner = THIS_MODULE;
+ dmaclk->mask = 1 << 14;
+
+ ret = clk_register(&dmaclk->clk);
+ if (ret)
+ pr_warning("%s: could not register dma clock\n", __func__);
+
+#if defined(CONFIG_CPU_FREQ)
+ cpufreq_register_driver(&ns921x_cpufreq_driver);
+#endif
+
+ /* add system GPIOs */
+ ns921x_gpio_init();
+}
diff --git a/arch/arm/mach-ns9xxx/processor-ns921x.h b/arch/arm/mach-ns9xxx/processor-ns921x.h
new file mode 100644
index 000000000000..a5c91a63c911
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/processor-ns921x.h
@@ -0,0 +1,44 @@
+/*
+ * arch/arm/mach-ns9xxx/processor-ns921x.h
+ *
+ * Copyright (C) 2007,2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef __NS9XXX_PROCESSOR_NS921X_H
+#define __NS9XXX_PROCESSOR_NS921X_H
+#include <linux/init.h>
+
+#include "clock.h"
+
+#define NS921X_REFCLOCK 29491200
+
+void ns921x_reset(char mode);
+
+unsigned long ns921x_systemclock(void) __attribute__((const));
+
+unsigned long ns921x_ahbclock(void) __attribute__((const));
+
+void __init ns921x_map_io(void);
+
+extern struct sys_timer ns921x_timer;
+
+struct ns921x_sysclk {
+ struct clk clk;
+ u32 mask;
+};
+/*
+ * only use ns921x_endisable_sysclock as callback for struct ns921x_sysclk's
+ * because there is no additional locking to serialize access to SYS_CLOCK.
+ */
+int ns921x_endisable_sysclock(struct clk *clk, int enable);
+
+void __init ns921x_init_machine(void);
+
+/* irq-ns921x.c */
+int ns921x_set_wake_irq(unsigned int irq, unsigned int on);
+
+#endif /* ifndef __NS9XXX_PROCESSOR_NS921X_H */
diff --git a/arch/arm/mach-ns9xxx/processor-ns9360.c b/arch/arm/mach-ns9xxx/processor-ns9360.c
index abee8338735d..ca9475ed1b62 100644
--- a/arch/arm/mach-ns9xxx/processor-ns9360.c
+++ b/arch/arm/mach-ns9xxx/processor-ns9360.c
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-ns9xxx/processor-ns9360.c
*
- * Copyright (C) 2007 by Digi International Inc.
+ * Copyright (C) 2007,2008 by Digi International Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -14,9 +14,14 @@
#include <asm/page.h>
#include <asm/mach/map.h>
-#include <mach/processor-ns9360.h>
+
#include <mach/regs-sys-ns9360.h>
+#include "clock.h"
+
+#include "processor-ns9360.h"
+#include "gpiolib-ns9360.h"
+
void ns9360_reset(char mode)
{
u32 reg;
@@ -34,6 +39,36 @@ unsigned long ns9360_systemclock(void)
>> REGGETIM(pll, SYS_PLL, FS);
}
+unsigned long ns9360_cpuclock(void)
+{
+ return ns9360_systemclock() / 2;
+}
+
+unsigned long ns9360_ahbclock(void)
+{
+ return ns9360_systemclock() / 4;
+}
+
+static unsigned long ns9360_get_systemclock_rate(struct clk *clk)
+{
+ return ns9360_systemclock();
+}
+
+static unsigned long ns9360_get_bbusclock_rate(struct clk *clk)
+{
+ return ns9360_systemclock() / 8;
+}
+
+static unsigned long ns9360_get_cpuclock_rate(struct clk *clk)
+{
+ return ns9360_cpuclock();
+}
+
+static unsigned long ns9360_get_ahbclock_rate(struct clk *clk)
+{
+ return ns9360_ahbclock();
+}
+
static struct map_desc ns9360_io_desc[] __initdata = {
{ /* BBus */
.virtual = io_p2v(0x90000000),
@@ -52,3 +87,39 @@ void __init ns9360_map_io(void)
{
iotable_init(ns9360_io_desc, ARRAY_SIZE(ns9360_io_desc));
}
+
+void ns9360_init_machine(void)
+{
+ /* register system, bbus and cpu clocks */
+ struct clk *clk = kzalloc(4 * sizeof(*clk), GFP_KERNEL);
+ int ret, i;
+
+ if (!clk) {
+ pr_warning("%s: could not register system clocks\n", __func__);
+ return;
+ }
+
+ clk[0].name = "systemclock";
+ clk[0].get_rate = ns9360_get_systemclock_rate;
+
+ clk[1].name = "bbusclock";
+ clk[1].get_rate = ns9360_get_bbusclock_rate;
+
+ clk[2].name = "cpuclock";
+ clk[2].get_rate = ns9360_get_cpuclock_rate;
+
+ clk[3].name = "ahbclock";
+ clk[3].get_rate = ns9360_get_ahbclock_rate;
+
+ for (i = 0; i < 4; ++i) {
+ clk[i].id = -1;
+ ret = clk_register(&clk[i]);
+ if (ret)
+ pr_warning("%s: could not register %s\n",
+ __func__, clk[i].name);
+
+ }
+
+ /* add system GPIOs */
+ ns9360_gpio_init();
+}
diff --git a/arch/arm/mach-ns9xxx/processor-ns9360.h b/arch/arm/mach-ns9xxx/processor-ns9360.h
new file mode 100644
index 000000000000..7fbb79e0cbb1
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/processor-ns9360.h
@@ -0,0 +1,30 @@
+/*
+ * arch/arm/mach-ns9xxx/processor-ns9360.h
+ *
+ * Copyright (C) 2007,2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef __NS9XXX_PROCESSOR_NS9360_H
+#define __NS9XXX_PROCESSOR_NS9360_H
+
+#include <linux/init.h>
+
+#include "clock.h"
+
+void ns9360_reset(char mode);
+
+unsigned long ns9360_systemclock(void) __attribute__((const));
+
+unsigned long ns9360_cpuclock(void) __attribute__((const));
+
+void __init ns9360_map_io(void);
+
+extern struct sys_timer ns9360_timer;
+
+void __init ns9360_init_machine(void);
+
+#endif /* ifndef __NS9XXX_PROCESSOR_NS9360_H */
diff --git a/arch/arm/mach-ns9xxx/time-ns921x.c b/arch/arm/mach-ns9xxx/time-ns921x.c
new file mode 100644
index 000000000000..5e5d54608e00
--- /dev/null
+++ b/arch/arm/mach-ns9xxx/time-ns921x.c
@@ -0,0 +1,195 @@
+/*
+ * arch/arm/mach-ns9xxx/time-ns921x.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/stringify.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+
+#include <mach/regs-sys-ns921x.h>
+#include <mach/irqs.h>
+
+#include "irq.h"
+#include "processor-ns921x.h"
+
+#define TIMER_CLOCKSOURCE 0
+#define TIMER_CLOCKEVENT 1
+static u32 latch;
+
+static cycle_t ns921x_clocksource_read(void)
+{
+ return __raw_readl(SYS_TRC(TIMER_CLOCKSOURCE));
+}
+
+static struct clocksource ns921x_clocksource = {
+ .name = "ns921x-timer" __stringify(TIMER_CLOCKSOURCE),
+ .rating = 300,
+ .read = ns921x_clocksource_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 20,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static void ns921x_clockevent_setmode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ u32 oldtc, tc = __raw_readl(SYS_TC(TIMER_CLOCKEVENT));
+ oldtc = tc;
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ __raw_writel(latch, SYS_TRCC(TIMER_CLOCKEVENT));
+ REGSET(tc, SYS_TCx, RELENBL, EN);
+ REGSET(tc, SYS_TCx, INTSEL, EN);
+ REGSET(tc, SYS_TCx, TE, EN);
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ REGSET(tc, SYS_TCx, RELENBL, DIS);
+ REGSET(tc, SYS_TCx, INTSEL, EN);
+ REGSET(tc, SYS_TCx, TE, DIS);
+ break;
+
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ default:
+ REGSET(tc, SYS_TCx, TE, DIS);
+ break;
+ }
+
+ /* ack an possibly pending irq
+ * This might stuck the irq priority encoder, but only until the next
+ * timer irq ... */
+ if (REGGET(tc, SYS_TCx, TE) == SYS_TCx_TE_DIS &&
+ REGGET(oldtc, SYS_TCx, TE) == SYS_TCx_TE_EN) {
+ REGSETIM(tc, SYS_TCx, INTCLR, 1);
+ __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT));
+ REGSETIM(tc, SYS_TCx, INTCLR, 0);
+ }
+
+ __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT));
+}
+
+static int ns921x_clockevent_setnextevent(unsigned long evt,
+ struct clock_event_device *clk)
+{
+ u32 tc = __raw_readl(SYS_TC(TIMER_CLOCKEVENT));
+
+ if (REGGET(tc, SYS_TCx, TE)) {
+ REGSET(tc, SYS_TCx, TE, DIS);
+ __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT));
+ }
+
+ REGSET(tc, SYS_TCx, TE, EN);
+
+ __raw_writel(evt, SYS_TRCC(TIMER_CLOCKEVENT));
+
+ __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT));
+
+ return 0;
+}
+
+static struct clock_event_device ns921x_clockevent_device = {
+ .name = "ns921x-timer" __stringify(TIMER_CLOCKEVENT),
+ .shift = 20,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = ns921x_clockevent_setmode,
+ .set_next_event = ns921x_clockevent_setnextevent,
+};
+
+static irqreturn_t ns921x_clockevent_handler(int irq, void *dev_id)
+{
+ int timerno = irq - IRQ_NS921X_TIMER0;
+ u32 tc;
+
+ struct clock_event_device *evt = &ns921x_clockevent_device;
+
+ /* clear irq */
+ tc = __raw_readl(SYS_TC(timerno));
+ if (REGGET(tc, SYS_TCx, RELENBL) == SYS_TCx_RELENBL_DIS) {
+ REGSET(tc, SYS_TCx, TE, DIS);
+ __raw_writel(tc, SYS_TC(timerno));
+ }
+ REGSETIM(tc, SYS_TCx, INTCLR, 1);
+ __raw_writel(tc, SYS_TC(timerno));
+ REGSETIM(tc, SYS_TCx, INTCLR, 0);
+ __raw_writel(tc, SYS_TC(timerno));
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction ns921x_clockevent_action = {
+ .name = "ns921x-timer" __stringify(TIMER_CLOCKEVENT),
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = ns921x_clockevent_handler,
+};
+
+static void __init ns921x_timer_init(void)
+{
+ int tc;
+
+ tc = __raw_readl(SYS_TC(TIMER_CLOCKSOURCE));
+ if (REGGET(tc, SYS_TCx, TE)) {
+ REGSET(tc, SYS_TCx, TE, DIS);
+ __raw_writel(tc, SYS_TC(TIMER_CLOCKSOURCE));
+ }
+
+ __raw_writel(0, SYS_TRC(TIMER_CLOCKSOURCE));
+
+ REGSET(tc, SYS_TCx, TE, EN);
+ REGSET(tc, SYS_TCx, DEBUG, STOP);
+ REGSET(tc, SYS_TCx, TCS, 2AHB);
+ REGSET(tc, SYS_TCx, MODE, INTERNAL);
+ REGSET(tc, SYS_TCx, INTSEL, DIS);
+ REGSET(tc, SYS_TCx, UPDOWN, UP);
+ REGSET(tc, SYS_TCx, BITTIMER, 32);
+ REGSET(tc, SYS_TCx, RELENBL, EN);
+
+ __raw_writel(tc, SYS_TC(TIMER_CLOCKSOURCE));
+
+ ns921x_clocksource.mult = clocksource_hz2mult(ns921x_ahbclock() * 2,
+ ns921x_clocksource.shift);
+
+ clocksource_register(&ns921x_clocksource);
+
+ latch = SH_DIV(ns921x_ahbclock() * 2, HZ, 0);
+
+ tc = __raw_readl(SYS_TC(TIMER_CLOCKEVENT));
+ REGSET(tc, SYS_TCx, TE, DIS);
+ REGSET(tc, SYS_TCx, DEBUG, STOP);
+ REGSET(tc, SYS_TCx, TCS, 2AHB);
+ REGSET(tc, SYS_TCx, MODE, INTERNAL);
+ REGSET(tc, SYS_TCx, INTSEL, DIS);
+ REGSET(tc, SYS_TCx, UPDOWN, DOWN);
+ REGSET(tc, SYS_TCx, BITTIMER, 32);
+ REGSET(tc, SYS_TCx, RELENBL, EN);
+ __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT));
+
+ ns921x_clockevent_device.mult = div_sc(ns921x_ahbclock() * 2,
+ NSEC_PER_SEC, ns921x_clockevent_device.shift);
+ ns921x_clockevent_device.max_delta_ns =
+ clockevent_delta2ns(-1, &ns921x_clockevent_device);
+ ns921x_clockevent_device.min_delta_ns =
+ clockevent_delta2ns(1, &ns921x_clockevent_device);
+
+ ns921x_clockevent_device.cpumask = cpumask_of_cpu(0);
+ clockevents_register_device(&ns921x_clockevent_device);
+
+ setup_irq(IRQ_NS921X_TIMER0 + TIMER_CLOCKEVENT,
+ &ns921x_clockevent_action);
+}
+
+struct sys_timer ns921x_timer = {
+ .init = ns921x_timer_init,
+};
diff --git a/arch/arm/mach-ns9xxx/time-ns9360.c b/arch/arm/mach-ns9xxx/time-ns9360.c
index a63424d083d9..553617eba15a 100644
--- a/arch/arm/mach-ns9xxx/time-ns9360.c
+++ b/arch/arm/mach-ns9xxx/time-ns9360.c
@@ -15,11 +15,12 @@
#include <linux/clocksource.h>
#include <linux/clockchips.h>
-#include <mach/processor-ns9360.h>
#include <mach/regs-sys-ns9360.h>
#include <mach/irqs.h>
#include <mach/system.h>
-#include "generic.h"
+
+#include "irq.h"
+#include "processor-ns9360.h"
#define TIMER_CLOCKSOURCE 0
#define TIMER_CLOCKEVENT 1
diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-s3c2410/include/mach/dma.h
index 891b53cd69b8..856b80bdee1f 100644
--- a/arch/arm/mach-s3c2410/include/mach/dma.h
+++ b/arch/arm/mach-s3c2410/include/mach/dma.h
@@ -34,6 +34,7 @@ enum dma_ch {
DMACH_XD1,
DMACH_SDI,
DMACH_SPI0,
+ DMACH_SPI0_RX,
DMACH_SPI1,
DMACH_UART0,
DMACH_UART1,
diff --git a/arch/arm/mach-s3c2410/include/mach/fb.h b/arch/arm/mach-s3c2410/include/mach/fb.h
index eee0654eb8fb..b7774bb52b72 100644
--- a/arch/arm/mach-s3c2410/include/mach/fb.h
+++ b/arch/arm/mach-s3c2410/include/mach/fb.h
@@ -46,6 +46,56 @@ struct s3c2410fb_display {
/* lcd configuration registers */
unsigned long lcdcon5;
+
+ /* Additional registers for the TFT-LCD (Luis Galdos) */
+ int frame_rate;
+ unsigned long vidcon0;
+ unsigned long vidcon1;
+ unsigned long vidtcon0;
+ unsigned long vidtcon1;
+ unsigned long vidtcon2;
+ unsigned long wincon0;
+ unsigned long wincon1;
+ unsigned long vidosd0a;
+ unsigned long vidosd0b;
+ unsigned long vidosd0c;
+ unsigned long vidosd1a;
+ unsigned long vidosd1b;
+ unsigned long vidosd1c;
+
+ unsigned long vidw00add0b0;
+ unsigned long vidw00add0b1;
+ unsigned long vidw01add0;
+
+ unsigned long vidw00add1b0;
+ unsigned long vidw00add1b1;
+ unsigned long vidw01add1;
+
+ unsigned long vidw00add2b0;
+ unsigned long vidw00add2b1;
+ unsigned long vidw01add2;
+
+ unsigned long vidintcon;
+ unsigned long w1keycon0;
+ unsigned long w1keycon1;
+ unsigned long w2keycon0;
+ unsigned long w2keycon1;
+ unsigned long w3keycon0;
+ unsigned long w3keycon1;
+ unsigned long w4keycon0;
+ unsigned long w4keycon1;
+
+ unsigned long win0map;
+ unsigned long win1map;
+
+ unsigned long wpalcon;
+ unsigned long dithmode;
+ unsigned long intclr0;
+ unsigned long intclr1;
+ unsigned long intclr2;
+
+ unsigned long win0pal;
+ unsigned long win1pal;
};
struct s3c2410fb_mach_info {
diff --git a/arch/arm/mach-s3c2410/include/mach/gpio.h b/arch/arm/mach-s3c2410/include/mach/gpio.h
index 3b52b86498a6..77d345e1ff2b 100644
--- a/arch/arm/mach-s3c2410/include/mach/gpio.h
+++ b/arch/arm/mach-s3c2410/include/mach/gpio.h
@@ -1,8 +1,8 @@
/* arch/arm/mach-s3c2410/include/mach/gpio.h
*
* Copyright (c) 2008 Simtec Electronics
- * http://armlinux.simtec.co.uk/
- * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
*
* S3C2410 - GPIO lib support
*
@@ -11,8 +11,20 @@
* 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_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
+#define gpio_set_pullupdown __gpio_set_pullupdown
#include <asm-generic/gpio.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-gpioj.h>
+
+void s3c2443_gpio_cfgpin(unsigned int pin, unsigned int function);
+int s3c2443_gpio_extpull(unsigned int pin, int pullup);
+int s3c2443_gpio_getirq(unsigned int gpio);
+unsigned int s3c2443_gpio_read_porta(unsigned int pin);
+void s3c2443_gpio_set_udp(unsigned int pin, int val);
+void s3c2443_gpio_setpin(unsigned int pin, unsigned int to);
+unsigned int s3c2443_gpio_num(unsigned gpio);
diff --git a/arch/arm/mach-s3c2410/include/mach/io.h b/arch/arm/mach-s3c2410/include/mach/io.h
index 9813dbf2ae4f..0781e41a7713 100644
--- a/arch/arm/mach-s3c2410/include/mach/io.h
+++ b/arch/arm/mach-s3c2410/include/mach/io.h
@@ -75,144 +75,8 @@ DECLARE_IO(char,b,"b")
DECLARE_IO(short,w,"h")
DECLARE_IO(int,l,"")
-#undef DECLARE_IO
-#undef DECLARE_DYN_IN
-/*
- * Constant address IO functions
- *
- * These have to be macros for the 'J' constraint to work -
- * +/-4096 immediate operand.
- */
-#define __outbc(value,port) \
-({ \
- if (__PORT_PCIO((port))) \
- __asm__ __volatile__( \
- "strb %0, [%1, %2] @ outbc" \
- : : "r" (value), "r" (PCIO_BASE), "Jr" ((port))); \
- else \
- __asm__ __volatile__( \
- "strb %0, [%1, #0] @ outbc" \
- : : "r" (value), "r" ((port))); \
-})
-
-#define __inbc(port) \
-({ \
- unsigned char result; \
- if (__PORT_PCIO((port))) \
- __asm__ __volatile__( \
- "ldrb %0, [%1, %2] @ inbc" \
- : "=r" (result) : "r" (PCIO_BASE), "Jr" ((port))); \
- else \
- __asm__ __volatile__( \
- "ldrb %0, [%1, #0] @ inbc" \
- : "=r" (result) : "r" ((port))); \
- result; \
-})
-
-#define __outwc(value,port) \
-({ \
- unsigned long v = value; \
- if (__PORT_PCIO((port))) { \
- if ((port) < 256 && (port) > -256) \
- __asm__ __volatile__( \
- "strh %0, [%1, %2] @ outwc" \
- : : "r" (v), "r" (PCIO_BASE), "Jr" ((port))); \
- else if ((port) > 0) \
- __asm__ __volatile__( \
- "strh %0, [%1, %2] @ outwc" \
- : : "r" (v), \
- "r" (PCIO_BASE + ((port) & ~0xff)), \
- "Jr" (((port) & 0xff))); \
- else \
- __asm__ __volatile__( \
- "strh %0, [%1, #0] @ outwc" \
- : : "r" (v), \
- "r" (PCIO_BASE + (port))); \
- } else \
- __asm__ __volatile__( \
- "strh %0, [%1, #0] @ outwc" \
- : : "r" (v), "r" ((port))); \
-})
-
-#define __inwc(port) \
-({ \
- unsigned short result; \
- if (__PORT_PCIO((port))) { \
- if ((port) < 256 && (port) > -256 ) \
- __asm__ __volatile__( \
- "ldrh %0, [%1, %2] @ inwc" \
- : "=r" (result) \
- : "r" (PCIO_BASE), \
- "Jr" ((port))); \
- else if ((port) > 0) \
- __asm__ __volatile__( \
- "ldrh %0, [%1, %2] @ inwc" \
- : "=r" (result) \
- : "r" (PCIO_BASE + ((port) & ~0xff)), \
- "Jr" (((port) & 0xff))); \
- else \
- __asm__ __volatile__( \
- "ldrh %0, [%1, #0] @ inwc" \
- : "=r" (result) \
- : "r" (PCIO_BASE + ((port)))); \
- } else \
- __asm__ __volatile__( \
- "ldrh %0, [%1, #0] @ inwc" \
- : "=r" (result) : "r" ((port))); \
- result; \
-})
-
-#define __outlc(value,port) \
-({ \
- unsigned long v = value; \
- if (__PORT_PCIO((port))) \
- __asm__ __volatile__( \
- "str %0, [%1, %2] @ outlc" \
- : : "r" (v), "r" (PCIO_BASE), "Jr" ((port))); \
- else \
- __asm__ __volatile__( \
- "str %0, [%1, #0] @ outlc" \
- : : "r" (v), "r" ((port))); \
-})
-
-#define __inlc(port) \
-({ \
- unsigned long result; \
- if (__PORT_PCIO((port))) \
- __asm__ __volatile__( \
- "ldr %0, [%1, %2] @ inlc" \
- : "=r" (result) : "r" (PCIO_BASE), "Jr" ((port))); \
- else \
- __asm__ __volatile__( \
- "ldr %0, [%1, #0] @ inlc" \
- : "=r" (result) : "r" ((port))); \
- result; \
-})
-
-#define __ioaddrc(port) ((__PORT_PCIO(port) ? PCIO_BASE + (port) : (void __iomem *)(port)))
-
-#define inb(p) (__builtin_constant_p((p)) ? __inbc(p) : __inb(p))
-#define inw(p) (__builtin_constant_p((p)) ? __inwc(p) : __inw(p))
-#define inl(p) (__builtin_constant_p((p)) ? __inlc(p) : __inl(p))
-#define outb(v,p) (__builtin_constant_p((p)) ? __outbc(v,p) : __outb(v,p))
-#define outw(v,p) (__builtin_constant_p((p)) ? __outwc(v,p) : __outw(v,p))
-#define outl(v,p) (__builtin_constant_p((p)) ? __outlc(v,p) : __outl(v,p))
-#define __ioaddr(p) (__builtin_constant_p((p)) ? __ioaddr(p) : __ioaddrc(p))
-/* the following macro is deprecated */
-#define ioaddr(port) __ioaddr((port))
-
-#define insb(p,d,l) __raw_readsb(__ioaddr(p),d,l)
-#define insw(p,d,l) __raw_readsw(__ioaddr(p),d,l)
-#define insl(p,d,l) __raw_readsl(__ioaddr(p),d,l)
-
-#define outsb(p,d,l) __raw_writesb(__ioaddr(p),d,l)
-#define outsw(p,d,l) __raw_writesw(__ioaddr(p),d,l)
-#define outsl(p,d,l) __raw_writesl(__ioaddr(p),d,l)
-
-/*
- * 1:1 mapping for ioremapped regions.
- */
#define __mem_pci(x) (x)
+#define __io(x) ((void __iomem *)(x))
#endif
diff --git a/arch/arm/mach-s3c2410/include/mach/irqs.h b/arch/arm/mach-s3c2410/include/mach/irqs.h
index 950c71bf1489..184720ae80b2 100644
--- a/arch/arm/mach-s3c2410/include/mach/irqs.h
+++ b/arch/arm/mach-s3c2410/include/mach/irqs.h
@@ -65,17 +65,17 @@
/* interrupts generated from the external interrupts sources */
#define IRQ_EINT4 S3C2410_IRQ(32) /* 48 */
#define IRQ_EINT5 S3C2410_IRQ(33)
-#define IRQ_EINT6 S3C2410_IRQ(34)
+#define IRQ_EINT6 S3C2410_IRQ(34) /* 50 */
#define IRQ_EINT7 S3C2410_IRQ(35)
#define IRQ_EINT8 S3C2410_IRQ(36)
#define IRQ_EINT9 S3C2410_IRQ(37)
#define IRQ_EINT10 S3C2410_IRQ(38)
-#define IRQ_EINT11 S3C2410_IRQ(39)
+#define IRQ_EINT11 S3C2410_IRQ(39) /* 55 */
#define IRQ_EINT12 S3C2410_IRQ(40)
#define IRQ_EINT13 S3C2410_IRQ(41)
#define IRQ_EINT14 S3C2410_IRQ(42)
#define IRQ_EINT15 S3C2410_IRQ(43)
-#define IRQ_EINT16 S3C2410_IRQ(44)
+#define IRQ_EINT16 S3C2410_IRQ(44) /* 60 */
#define IRQ_EINT17 S3C2410_IRQ(45)
#define IRQ_EINT18 S3C2410_IRQ(46)
#define IRQ_EINT19 S3C2410_IRQ(47)
diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h
index 23c470c2e5b1..100266280693 100644
--- a/arch/arm/mach-s3c2410/include/mach/map.h
+++ b/arch/arm/mach-s3c2410/include/mach/map.h
@@ -46,10 +46,23 @@
#define S3C2410_PA_LCD (0x4D000000)
#define S3C24XX_SZ_LCD SZ_1M
+/* TFT-LCD controller */
+#define S3C2410_PA_TFTLCD (0x4C800000)
+#define S3C24XX_SZ_TFTLCD SZ_1M
+
/* NAND flash controller */
#define S3C2410_PA_NAND (0x4E000000)
#define S3C24XX_SZ_NAND SZ_1M
+/* EBI registers for 2443 */
+#define S3C2443_PA_EBI (0x4E800000)
+#define S3C2443_VA_EBI S3C_VA_EBI
+#define S3C2443_SZ_EBI SZ_1M
+
+/* SSMC Memory controller */
+#define S3C2443_PA_SSMC (0x4F000000)
+#define S3C2443_SZ_SSMC SZ_1M
+
/* UARTs */
#define S3C24XX_VA_UART S3C_VA_UART
#define S3C2410_PA_UART (0x50000000)
@@ -103,6 +116,10 @@
#define S3C2410_PA_SPI (0x59000000)
#define S3C24XX_SZ_SPI SZ_1M
+/* High Speed SPI */
+#define S3C2443_PA_HSSPI (0x52000000)
+#define S3C2443_SZ_HSSPI SZ_1M
+
/* SDI */
#define S3C2410_PA_SDI (0x5A000000)
#define S3C24XX_SZ_SDI SZ_1M
@@ -112,7 +129,6 @@
#define S3C2440_SZ_CAMIF SZ_1M
/* AC97 */
-
#define S3C2440_PA_AC97 (0x5B000000)
#define S3C2440_SZ_AC97 SZ_1M
@@ -120,6 +136,10 @@
#define S3C2443_PA_HSMMC (0x4A800000)
#define S3C2443_SZ_HSMMC (256)
+/* CF/ATA */
+#define S3C2443_PA_CFATA (0x4B800000)
+#define S3C2443_SZ_CFATA SZ_1M
+
/* ISA style IO, for each machine to sort out mappings for, if it
* implements it. We reserve two 16M regions for ISA.
*/
@@ -127,6 +147,15 @@
#define S3C24XX_VA_ISA_WORD S3C2410_ADDR(0x02000000)
#define S3C24XX_VA_ISA_BYTE S3C2410_ADDR(0x03000000)
+/* LAN Mapping */
+#define CC9M2443_VA_LAN S3C2410_ADDR(0x04000000)
+#define CC9M2443_PA_LAN (S3C2410_CS5)
+#define CC9M2443_SZ_LAN SZ_1M
+
+/* USB device Mapping */
+#define S3C2443_PA_USBDEV (0x49800000)
+#define S3C2443_SZ_USBDEV SZ_1M
+
/* physical addresses of all the chip-select areas */
#define S3C2410_CS0 (0x00000000)
@@ -148,6 +177,7 @@
#define S3C24XX_PA_DMA S3C2410_PA_DMA
#define S3C24XX_PA_CLKPWR S3C2410_PA_CLKPWR
#define S3C24XX_PA_LCD S3C2410_PA_LCD
+#define S3C24XX_PA_TFTLCD S3C2410_PA_TFTLCD
#define S3C24XX_PA_UART S3C2410_PA_UART
#define S3C24XX_PA_TIMER S3C2410_PA_TIMER
#define S3C24XX_PA_USBDEV S3C2410_PA_USBDEV
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-bus.h b/arch/arm/mach-s3c2410/include/mach/regs-bus.h
new file mode 100644
index 000000000000..c47ef822842b
--- /dev/null
+++ b/arch/arm/mach-s3c2410/include/mach/regs-bus.h
@@ -0,0 +1,29 @@
+/* linux/include/asm-arm/arch-s3c2410/regs-bus.h
+ *
+ * Copyright (c) 2009 Digi International
+ *
+ * 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.
+ *
+ * S3C2443 bus matrix and EBI registers
+ */
+
+#ifndef __ASM_ARM_REGS_BUS
+#define __ASM_ARM_REGS_BUS
+
+#define S3C2443_BUSREG(x) ((x) + S3C2443_VA_EBI)
+
+#define S3C2443_BPRIORITY0 S3C2443_BUSREG(0x00)
+#define S3C2443_BPRIORITY1 S3C2443_BUSREG(0x04)
+
+#define S3C2443_EBICON S3C2443_BUSREG(0x08)
+
+#define S3C2443_EBICON_FIXPRTYPE (3<<0)
+#define S3C2443_EBICON_PR_TYPE (1<<2)
+#define S3C2443_EBICON_BANK1_CFG (1<<8)
+#define S3C2443_EBICON_BANK2_CFG (1<<9)
+#define S3C2443_EBICON_BANK3_CFG (1<<10)
+
+
+#endif /* __ASM_ARM_REGS_BUS */
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-cfata.h b/arch/arm/mach-s3c2410/include/mach/regs-cfata.h
new file mode 100644
index 000000000000..c730ef2c72f5
--- /dev/null
+++ b/arch/arm/mach-s3c2410/include/mach/regs-cfata.h
@@ -0,0 +1,108 @@
+/* linux/arch/arm/mach-s3c2410/include/mach/regs-cfata.h
+ *
+ * Copyright (c) 2009 Digi Internationa Inc.
+ * http://www.digi.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+
+#ifndef ___ASM_ARCH_REGS_CFATA_H
+#define ___ASM_ARCH_REGS_CFATA_H
+
+/* PCMCIA / ATA controller registers */
+#define S3C2443_ATTR_BASE (0x0000)
+#define S3C2443_IO_BASE (0x0800)
+#define S3C2443_COMM_BASE (0x1000)
+#define S3C2443_SFR_BASE (0x1800)
+#define S3C2443_MUX_REG (0x1800)
+#define S3C2443_PCCARD_BASE (0x1820)
+#define S3C2443_PCCARD_CFG (0x1820)
+#define S3C2443_PCCARD_INT (0x1824)
+#define S3C2443_PCCARD_ATTR (0x1828)
+#define S3C2443_PCCARD_IO (0x182C)
+#define S3C2443_PCCARD_COMM (0x1830)
+
+#define S3C2443_ATA_BASE (0x1900)
+#define S3C2443_ATA_CONTROL (0x1900)
+#define S3C2443_ATA_STATUS (0x1904)
+#define S3C2443_ATA_COMMAND (0x1908)
+#define S3C2443_ATA_SWRST (0x190c)
+#define S3C2443_ATA_IRQ (0x1910)
+#define S3C2443_ATA_IRQ_MASK (0x1914)
+#define S3C2443_ATA_CFG (0x1918)
+
+#define S3C2443_ATA_PIO_TIME (0x192c)
+#define S3C2443_ATA_UDMA_TIME (0x1930)
+
+#define S3C2443_ATA_PIO_DTR (0x1954)
+#define S3C2443_ATA_PIO_FED (0x1958)
+#define S3C2443_ATA_PIO_SCR (0x195c)
+#define S3C2443_ATA_PIO_LLR (0x1960)
+#define S3C2443_ATA_PIO_LMR (0x1964)
+#define S3C2443_ATA_PIO_LHR (0x1968)
+#define S3C2443_ATA_PIO_DVR (0x196c)
+#define S3C2443_ATA_PIO_CSD (0x1970)
+#define S3C2443_ATA_PIO_DAD (0x1974)
+#define S3C2443_ATA_PIO_RDATA (0x197c)
+#define S3C2443_BUS_FIFO_STATUS (0x1990)
+#define S3C2443_ATA_FIFO_STATUS (0x1994)
+
+
+/* PCCARD / ATA register masks */
+
+/* MUX_REG masks */
+#define S3C2443_MUX_OUTPUT_DISABLE (1 << 2)
+#define S3C2443_MUX_OUTPUT_ENABLE (0 << 2)
+#define S3C2443_MUX_PWREN_PWOFF (1 << 1)
+#define S3C2443_MUX_PWREN_PWON (0 << 1)
+#define S3C2443_MUX_MODE_PCCARD (0 << 0)
+#define S3C2443_MUX_MODE_IDE (1 << 0)
+
+/* PCCARD_CFG */
+#define S3C2443_PCC_CARD_RESET (1 << 13)
+#define S3C2443_PCC_INT_SEL (1 << 12)
+#define S3C2443_PCC_nWAIT_EN (1 << 11)
+#define S3C2443_PCC_DEVICE_ATT_8 (0 << 10)
+#define S3C2443_PCC_DEVICE_ATT_16 (1 << 10)
+#define S3C2443_PCC_DEVICE_COMM_8 (0 << 9)
+#define S3C2443_PCC_DEVICE_COMM_16 (1 << 9)
+#define S3C2443_PCC_DEVICE_IO_8 (0 << 8)
+#define S3C2443_PCC_DEVICE_IO_16 (1 << 8)
+#define S3C2443_PCC_NOCARD_ERR (1 << 3)
+#define S3C2443_PCC_nWAIT (1 << 2)
+#define S3C2443_PCC_nIREQ (1 << 1)
+#define S3C2443_PCC_nCD (1 << 0)
+
+/* PCCARD_INT */
+#define S3C2443_PCC_INTMSK_ERR_N (1 << 10)
+#define S3C2443_PCC_INTMSK_IREQ (1 << 9)
+#define S3C2443_PCC_INTMSK_CD (1 << 8)
+#define S3C2443_PCC_INTMSK_ALL (S3C2443_PCC_INTMSK_ERR_N | \
+ S3C2443_PCC_INTMSK_IREQ | \
+ S3C2443_PCC_INTMSK_CD)
+#define S3C2443_PCC_INTSRC_ERR_N (1 << 2)
+#define S3C2443_PCC_INTSRC_IREQ (1 << 1)
+#define S3C2443_PCC_INTSRC_CD (1 << 0)
+#define S3C2443_PCC_INTSRC_ALL (S3C2443_PCC_INTSRC_ERR_N | \
+ S3C2443_PCC_INTSRC_IREQ | \
+ S3C2443_PCC_INTSRC_CD)
+
+/* PCCARD_ATTR */
+#define S3C2443_PCC_HOLD_ATTR (0x7f << 16)
+#define S3C2443_PCC_CMND_ATTR (0x7f << 8)
+#define S3C2443_PCC_SETUP_ATTR (0x7f << 0)
+
+/* PCCARD_IO */
+#define S3C2443_PCC_HOLD_IO (0x7f << 16)
+#define S3C2443_PCC_CMND_IO (0x7f << 8)
+#define S3C2443_PCC_SETUP_IO (0x7f << 0)
+
+/* PCCARD_COMM */
+#define S3C2443_PCC_HOLD_COMM (0x7f << 16)
+#define S3C2443_PCC_CMND_COMM (0x7f << 8)
+#define S3C2443_PCC_SETUP_COMM (0x7f << 0)
+
+#endif /* ___ASM_ARCH_REGS_CFATA_H */
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-gpio.h b/arch/arm/mach-s3c2410/include/mach/regs-gpio.h
index 528080ceac44..c431fbeff892 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-gpio.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-gpio.h
@@ -45,7 +45,7 @@
S3C2400_BASEA2B(pin)+S3C24XX_VA_GPIO : \
S3C2400_BASEC2H(pin)+S3C24XX_VA_GPIO)
-
+#define S3C2410_BANKBASE(pin) ((pin) & ~31)
#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
@@ -78,6 +78,10 @@
#define S3C2400_GPACON S3C2410_GPIOREG(0x00)
#define S3C2400_GPADAT S3C2410_GPIOREG(0x04)
+/* Special registers for the S3C2443 */
+#define S3C2443_GPACDL S3C2410_GPIOREG(0x00)
+#define S3C2443_GPACDH S3C2410_GPIOREG(0x04)
+
#define S3C2410_GPA0 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 0)
#define S3C2410_GPA0_OUT (0<<0)
#define S3C2410_GPA0_ADDR0 (1<<0)
@@ -920,59 +924,92 @@
#define S3C2410_GPH0_INP (0x00 << 0)
#define S3C2410_GPH0_OUTP (0x01 << 0)
#define S3C2410_GPH0_nCTS0 (0x02 << 0)
+#define S3C2443_GPH0_TXD0 (0x02 << 0)
#define S3C2410_GPH1 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 1)
#define S3C2410_GPH1_INP (0x00 << 2)
#define S3C2410_GPH1_OUTP (0x01 << 2)
#define S3C2410_GPH1_nRTS0 (0x02 << 2)
+#define S3C2443_GPH1_RXD0 (0x02 << 2)
#define S3C2410_GPH2 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 2)
#define S3C2410_GPH2_INP (0x00 << 4)
#define S3C2410_GPH2_OUTP (0x01 << 4)
#define S3C2410_GPH2_TXD0 (0x02 << 4)
+#define S3C2443_GPH2_TXD1 (0x02 << 4)
#define S3C2410_GPH3 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 3)
#define S3C2410_GPH3_INP (0x00 << 6)
#define S3C2410_GPH3_OUTP (0x01 << 6)
#define S3C2410_GPH3_RXD0 (0x02 << 6)
+#define S3C2443_GPH3_RXD1 (0x02 << 6)
#define S3C2410_GPH4 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 4)
#define S3C2410_GPH4_INP (0x00 << 8)
#define S3C2410_GPH4_OUTP (0x01 << 8)
#define S3C2410_GPH4_TXD1 (0x02 << 8)
+#define S3C2443_GPH4_TXD2 (0x02 << 8)
#define S3C2410_GPH5 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 5)
#define S3C2410_GPH5_INP (0x00 << 10)
#define S3C2410_GPH5_OUTP (0x01 << 10)
#define S3C2410_GPH5_RXD1 (0x02 << 10)
+#define S3C2443_GPH5_RXD2 (0x02 << 10)
#define S3C2410_GPH6 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 6)
#define S3C2410_GPH6_INP (0x00 << 12)
#define S3C2410_GPH6_OUTP (0x01 << 12)
#define S3C2410_GPH6_TXD2 (0x02 << 12)
#define S3C2410_GPH6_nRTS1 (0x03 << 12)
+#define S3C2443_GPH6_TXD3 (0x02 << 12)
+#define S3C2443_GPH6_nRTS2 (0x03 << 12)
#define S3C2410_GPH7 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 7)
#define S3C2410_GPH7_INP (0x00 << 14)
#define S3C2410_GPH7_OUTP (0x01 << 14)
#define S3C2410_GPH7_RXD2 (0x02 << 14)
#define S3C2410_GPH7_nCTS1 (0x03 << 14)
+#define S3C2443_GPH7_RXD3 (0x02 << 14)
+#define S3C2443_GPH7_nCTS2 (0x03 << 14)
#define S3C2410_GPH8 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 8)
#define S3C2410_GPH8_INP (0x00 << 16)
#define S3C2410_GPH8_OUTP (0x01 << 16)
#define S3C2410_GPH8_UCLK (0x02 << 16)
+#define S3C2443_GPH8_nCTS0 (0x02 << 16)
#define S3C2410_GPH9 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 9)
#define S3C2410_GPH9_INP (0x00 << 18)
#define S3C2410_GPH9_OUTP (0x01 << 18)
#define S3C2410_GPH9_CLKOUT0 (0x02 << 18)
#define S3C2442_GPH9_nSPICS0 (0x03 << 18)
+#define S3C2443_GPH9_nRTS0 (0x02 << 18)
#define S3C2410_GPH10 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 10)
#define S3C2410_GPH10_INP (0x00 << 20)
#define S3C2410_GPH10_OUTP (0x01 << 20)
#define S3C2410_GPH10_CLKOUT1 (0x02 << 20)
+#define S3C2443_GPH10_nCTS1 (0x02 << 20)
+
+#define S3C2443_GPH11 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 11)
+#define S3C2443_GPH11_INP (0x00 << 22)
+#define S3C2443_GPH11_OUTP (0x01 << 22)
+#define S3C2443_GPH11_nRTS1 (0x02 << 22)
+
+#define S3C2443_GPH12 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 12)
+#define S3C2443_GPH12_INP (0x00 << 24)
+#define S3C2443_GPH12_OUTP (0x01 << 24)
+#define S3C2443_GPH12_EXTUARCLK (0x02 << 24)
+
+#define S3C2443_GPH13 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 13)
+#define S3C2443_GPH13_INP (0x00 << 26)
+#define S3C2443_GPH13_OUTP (0x01 << 26)
+#define S3C2443_GPH13_CLKOUT0 (0x02 << 26)
+
+#define S3C2443_GPH14 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 14)
+#define S3C2443_GPH14_INP (0x00 << 28)
+#define S3C2443_GPH14_OUTP (0x01 << 28)
+#define S3C2443_GPH14_CLKOUT1 (0x02 << 28)
/* The S3C2412 and S3C2413 move the GPJ register set to after
* GPH, which means all registers after 0x80 are now offset by 0x10
@@ -1035,6 +1072,8 @@
#define S3C2410_MISCCR_nEN_SCLKE (1<<19) /* not 2412 */
#define S3C2410_MISCCR_SDSLEEP (7<<17)
+#define S3C2443_MISCCR_nCD_CF (1<<30)
+
/* external interrupt control... */
/* S3C2410_EXTINT0 -> irq sense control for EINT0..EINT7
* S3C2410_EXTINT1 -> irq sense control for EINT8..EINT15
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-gpioj.h b/arch/arm/mach-s3c2410/include/mach/regs-gpioj.h
index 1202ca5e99f6..7283e774b7fd 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-gpioj.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-gpioj.h
@@ -1,4 +1,4 @@
-/* arch/arm/mach-s3c2410/include/mach/regs-gpioj.h
+/* linux/include/asm-arm/arch-s3c2410/regs-gpioj.h
*
* Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk>
* http://www.simtec.co.uk/products/SWLINUX/
@@ -22,11 +22,14 @@
* pull up works like all other ports.
*/
-#define S3C2440_GPIO_BANKJ (416)
+#define S3C2440_GPIO_BANKJ (416)
+#define S3C2410_GPIO_BANKL (32*15)
+#define S3C2410_GPIO_BANKM (32*16)
-#define S3C2440_GPJCON S3C2410_GPIOREG(0xd0)
-#define S3C2440_GPJDAT S3C2410_GPIOREG(0xd4)
-#define S3C2440_GPJUP S3C2410_GPIOREG(0xd8)
+
+#define S3C2440_GPJCON S3C2410_GPIOREG(0xd0)
+#define S3C2440_GPJDAT S3C2410_GPIOREG(0xd4)
+#define S3C2440_GPJUP S3C2410_GPIOREG(0xd8)
#define S3C2413_GPJCON S3C2410_GPIOREG(0x80)
#define S3C2413_GPJDAT S3C2410_GPIOREG(0x84)
@@ -99,8 +102,113 @@
#define S3C2440_GPJ12_CAMRESET (0x02 << 24)
#define S3C2443_GPJ13 S3C2410_GPIONO(S3C2440_GPIO_BANKJ, 13)
+#define S3C2440_GPJ13_INP (0x00 << 26)
+#define S3C2440_GPJ13_OUTP (0x01 << 26)
+#define S3C2440_GPJ13_SD0LED (0x02 << 26)
+
#define S3C2443_GPJ14 S3C2410_GPIONO(S3C2440_GPIO_BANKJ, 14)
+#define S3C2440_GPJ14_INP (0x00 << 28)
+#define S3C2440_GPJ14_OUTP (0x01 << 28)
+#define S3C2440_GPJ14_SD0CD (0x02 << 28)
+
#define S3C2443_GPJ15 S3C2410_GPIONO(S3C2440_GPIO_BANKJ, 15)
+#define S3C2440_GPJ15_INP (0x00 << 30)
+#define S3C2440_GPJ15_OUTP (0x01 << 30)
+#define S3C2440_GPJ15_SD0WP (0x02 << 30)
+
+
+/* Port L consists of 14 GPIO
+ *
+ * GPLCON has 2 bits for each of the input pins on port K
+ * 00 = 0 input, 1 output, 2 SD
+ *
+ * pull down works like all other ports.
+*/
+#define S3C2443_GPLCON S3C2410_GPIOREG(0xf0)
+#define S3C2443_GPLDAT S3C2410_GPIOREG(0xf4)
+#define S3C2443_GPLUDP S3C2410_GPIOREG(0xf8)
+#define S3C2443_GPLDN S3C2410_GPIOREG(0xf8)
+
+#define S3C2443_GPL0 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 0)
+#define S3C2443_GPL0_INP (0x0 << 0)
+#define S3C2443_GPL0_OUTP (0x1 << 0)
+#define S3C2443_GPL0_SD0DAT0 (0x2 << 0)
+
+#define S3C2443_GPL1 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 1)
+#define S3C2443_GPL1_INP (0x0 << 2)
+#define S3C2443_GPL1_OUTP (0x1 << 2)
+#define S3C2443_GPL1_SD0DAT1 (0x2 << 2)
+
+#define S3C2443_GPL2 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 2)
+#define S3C2443_GPL2_INP (0x0 << 4)
+#define S3C2443_GPL2_OUTP (0x1 << 4)
+#define S3C2443_GPL2_SD0DAT2 (0x2 << 4)
+
+#define S3C2443_GPL3 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 3)
+#define S3C2443_GPL3_INP (0x0 << 6)
+#define S3C2443_GPL3_OUTP (0x1 << 6)
+#define S3C2443_GPL3_SD0DAT3 (0x2 << 6)
+
+#define S3C2443_GPL4 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 4)
+#define S3C2443_GPL4_INP (0x0 << 8)
+#define S3C2443_GPL4_OUTP (0x1 << 8)
+#define S3C2443_GPL4_SD0DAT4 (0x2 << 8)
+
+#define S3C2443_GPL5 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 5)
+#define S3C2443_GPL5_INP (0x0 << 10)
+#define S3C2443_GPL5_OUTP (0x1 << 10)
+#define S3C2443_GPL5_SD0DAT5 (0x2 << 10)
+
+#define S3C2443_GPL6 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 6)
+#define S3C2443_GPL6_INP (0x0 << 12)
+#define S3C2443_GPL6_OUTP (0x1 << 12)
+#define S3C2443_GPL6_SD0DAT6 (0x2 << 12)
+
+#define S3C2443_GPL7 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 7)
+#define S3C2443_GPL7_INP (0x0 << 14)
+#define S3C2443_GPL7_OUTP (0x1 << 14)
+#define S3C2443_GPL7_SD0DAT7 (0x2 << 14)
+
+#define S3C2443_GPL8 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 8)
+#define S3C2443_GPL8_INP (0x0 << 16)
+#define S3C2443_GPL8_OUTP (0x1 << 16)
+#define S3C2443_GPL8_SD0CMD (0x2 << 16)
+
+#define S3C2443_GPL9 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 9)
+#define S3C2443_GPL9_INP (0x0 << 18)
+#define S3C2443_GPL9_OUTP (0x1 << 18)
+#define S3C2443_GPL9_SD0CLK (0x2 << 18)
+
+#define S3C2443_GPL10 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 10)
+#define S3C2443_GPL10_INP (0x0 << 20)
+#define S3C2443_GPL10_OUTP (0x1 << 20)
+#define S3C2443_GPL10_SPICLK1 (0x2 << 20)
+
+#define S3C2443_GPL11 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 11)
+#define S3C2443_GPL11_INP (0x0 << 22)
+#define S3C2443_GPL11_OUTP (0x1 << 22)
+#define S3C2443_GPL11_SPIMOSI1 (0x2 << 22)
+
+#define S3C2443_GPL12 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 12)
+#define S3C2443_GPL12_INP (0x0 << 24)
+#define S3C2443_GPL12_OUTP (0x1 << 24)
+#define S3C2443_GPL12_SPIMISO1 (0x2 << 24)
+
+#define S3C2443_GPL13 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 13)
+#define S3C2443_GPL13_INP (0x0 << 26)
+#define S3C2443_GPL13_OUTP (0x1 << 26)
+#define S3C2443_GPL13_nSS0 (0x2 << 26)
+
+#define S3C2443_GPL14 S3C2410_GPIONO(S3C2410_GPIO_BANKL, 14)
+#define S3C2443_GPL14_INP (0x0 << 28)
+#define S3C2443_GPL14_OUTP (0x1 << 28)
+#define S3C2443_GPL14_nSS1 (0x2 << 28)
+
+/* Port M */
+#define S3C2443_GPMCON S3C2410_GPIOREG(0x100)
+#define S3C2443_GPMDAT S3C2410_GPIOREG(0x104)
+#define S3C2443_GPMUDP S3C2410_GPIOREG(0x108)
+#define S3C2443_GPMDN S3C2410_GPIOREG(0x108)
#endif /* __ASM_ARCH_REGS_GPIOJ_H */
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-hsmmc.h b/arch/arm/mach-s3c2410/include/mach/regs-hsmmc.h
new file mode 100644
index 000000000000..523dd5f8f586
--- /dev/null
+++ b/arch/arm/mach-s3c2410/include/mach/regs-hsmmc.h
@@ -0,0 +1,45 @@
+
+
+/*
+ * HS MMC Interface (chapter28)
+ */
+
+#ifndef __ASM_ARM_REGS_HSMMC
+#define __ASM_ARM_REGS_HSMMC "regs-hsmmc.h"
+
+
+#define S3C2410_HSMMC_SYSAD (0x00)
+#define S3C2410_HSMMC_BLKSIZE (0x04)
+#define S3C2410_HSMMC_BLKCNT (0x06)
+#define S3C2410_HSMMC_ARGUMENT (0x08)
+#define S3C2410_HSMMC_TRNMOD (0x0c)
+#define S3C2410_HSMMC_CMDREG (0x0e)
+#define S3C2410_HSMMC_RSPREG0 (0x10)
+#define S3C2410_HSMMC_RSPREG1 (0x14)
+#define S3C2410_HSMMC_RSPREG2 (0x18)
+#define S3C2410_HSMMC_RSPREG3 (0x1c)
+#define S3C2410_HSMMC_BDATA (0x20)
+#define S3C2410_HSMMC_PRNSTS (0x24)
+#define S3C2410_HSMMC_HOSTCTL (0x28)
+#define S3C2410_HSMMC_PWRCON (0x29)
+#define S3C2410_HSMMC_BLKGAP (0x2a)
+#define S3C2410_HSMMC_WAKCON (0x2b)
+#define S3C2410_HSMMC_CLKCON (0x2c)
+#define S3C2410_HSMMC_TIMEOUTCON (0x2e)
+#define S3C2410_HSMMC_SWRST (0x2f)
+#define S3C2410_HSMMC_NORINTSTS (0x30)
+#define S3C2410_HSMMC_ERRINTSTS (0x32)
+#define S3C2410_HSMMC_NORINTSTSEN (0x34)
+#define S3C2410_HSMMC_ERRINTSTSEN (0x36)
+#define S3C2410_HSMMC_NORINTSIGEN (0x38)
+#define S3C2410_HSMMC_ERRINTSIGEN (0x3a)
+#define S3C2410_HSMMC_ACMD12ERRSTS (0x3c)
+#define S3C2410_HSMMC_CAPAREG (0x40)
+#define S3C2410_HSMMC_MAXCURR (0x48)
+#define S3C2410_HSMMC_CONTROL2 (0x80)
+#define S3C2410_HSMMC_CONTROL3 (0x84)
+#define S3C2410_HSMMC_HCVER (0xfe)
+
+
+
+#endif /* __ASM_ARM_REGS_HSMMC */
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-lcd.h b/arch/arm/mach-s3c2410/include/mach/regs-lcd.h
index ee8f040aff5f..f7ba898eef9e 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-lcd.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-lcd.h
@@ -159,4 +159,87 @@
#define S3C24XX_LCDSRCPND (0x04)
#define S3C24XX_LCDINTMSK (0x08)
+/* Registers for the TFT-LCD controller (Luis G.) */
+#define S3C24XX_LCD_VIDCON0 S3C2410_LCDREG(0x00)
+#define S3C24XX_LCD_VIDCON0_ENVID_F (0x1 << 0)
+#define S3C24XX_LCD_VIDCON0_ENVID (0x1 << 1)
+#define S3C24XX_LCD_VIDCON0_CLKSEL_HCLK (0<<2)
+#define S3C24XX_LCD_VIDCON0_CLKSEL_LCD (1<<2)
+#define S3C24XX_LCD_VIDCON0_CLKSEL_MASK (3<<2)
+#define S3C24XX_LCD_VIDCON0_CLKDIR_DIVIDED (0x1 << 4)
+#define S3C24XX_LCD_VIDCON0_CLKDIR_DIRECT (0x0 << 4)
+#define S3C24XX_LCD_VIDCON0_VCLK_ON (0x0 << 5)
+#define S3C24XX_LCD_VIDCON0_VCLK_OFF (0x1 << 5)
+#define S3C24XX_LCD_VIDCON0_CLKVAL(x) ((0x3F & (x)) << 6)
+#define S3C24XX_LCD_VIDCON0_CLKVAL_MASK (0x3F << 6)
+#define S3C24XX_LCD_VIDCON0_RGB_PAR (0x0 << 13)
+#define S3C24XX_LCD_VIDCON0_BGR_PAR (0x1 << 13)
+#define S3C24XX_LCD_VIDCON0_VIDOUT_RGB_IF (0x0 << 22)
+#define S3C24XX_LCD_VIDCON0_VIDOUT_MLDI (0x2 << 22) /* IF for Main LDI */
+#define S3C24XX_LCD_VIDCON0_VIDOUT_SLDI (0x3 << 22) /* IF for Sub LDI */
+#define S3C24XX_LCD_VIDCON0_VIDOUT(x) (x << 22)
+
+#define S3C24XX_LCD_VIDCON1 S3C2410_LCDREG(0x04)
+#define S3C24XX_LCD_VIDCON1_IVDEN (1 << 4)
+#define S3C24XX_LCD_VIDCON1_IVSYNC (1 << 5)
+#define S3C24XX_LCD_VIDCON1_IHSYNC (1 << 6)
+#define S3C24XX_LCD_VIDCON1_IVCLK (1 << 7)
+
+/* Video time control 1 register */
+#define S3C24XX_LCD_VIDTCON0 S3C2410_LCDREG(0x08)
+#define S3C24XX_LCD_VIDTCON0_VBPDE(x) ((0xFF & (x)) << 24)
+#define S3C24XX_LCD_VIDTCON0_VBPD(x) ((0xFF & (x)) << 16)
+#define S3C24XX_LCD_VIDTCON0_VFPD(x) ((0xFF & (x)) << 8)
+#define S3C24XX_LCD_VIDTCON0_VSPW(x) ((0xFF & (x)) << 0)
+
+/* Video time control 2 register */
+#define S3C24XX_LCD_VIDTCON1 S3C2410_LCDREG(0x0C)
+#define S3C24XX_LCD_VIDTCON1_VFPDE(x) ((0xFF & (x)) << 24)
+#define S3C24XX_LCD_VIDTCON1_HBPD(x) ((0xFF & (x)) << 16)
+#define S3C24XX_LCD_VIDTCON1_HFPD(x) ((0xFF & (x)) << 8)
+#define S3C24XX_LCD_VIDTCON1_HSPW(x) ((0xFF & (x)) << 0)
+
+/* Video time control 3 register */
+#define S3C24XX_LCD_VIDTCON2 S3C2410_LCDREG(0x10)
+#define S3C24XX_LCD_VIDTCON2_HOZVAL(x) ((0x7FF & (x)) << 0)
+#define S3C24XX_LCD_VIDTCON2_LINEVAL(x) ((0x7FF & (x)) << 11)
+
+/* Window 0 control register */
+#define S3C24XX_LCD_WINCON0 S3C2410_LCDREG(0x14)
+#define S3C24XX_LCD_WINCON0_ENWIN_F (1 << 0)
+#define S3C24XX_LCD_WINCON0_BPPMODE_F(x) ((0xF & (x)) << 2)
+#define S3C24XX_LCD_WINCON0_16WBURST (0x0 << 9)
+#define S3C24XX_LCD_WINCON0_8WBURST (0x1 << 9)
+#define S3C24XX_LCD_WINCON0_4WBURST (0x2 << 9)
+#define S3C24XX_LCD_WINCON0_HAWSWP (0x1 << 16)
+
+#define S3C24XX_LCD_WINCON1 S3C2410_LCDREG(0x18)
+
+#define S3C24XX_LCD_WINCON_BPP(x) ((x) & 0x3C)
+#define S3C24XX_LCD_WINCON_16BPP_565 (0x5 << 2)
+#define S3C24XX_LCD_WINCON_16BPP_1555 (0x7 << 2)
+
+#define S3C24XX_LCD_VIDOSD0A S3C2410_LCDREG(0x28)
+#define S3C24XX_LCD_VIDOSD0A_TOP_X(x) (x << 11)
+#define S3C24XX_LCD_VIDOSD0A_TOP_Y(x) (x << 0)
+
+#define S3C24XX_LCD_VIDOSD0B S3C2410_LCDREG(0x2C)
+#define S3C24XX_LCD_VIDOSD0B_RIGHT_X(x) ((0x3FF & (x)) << 11)
+#define S3C24XX_LCD_VIDOSD0B_RIGHT_Y(x) ((0x3FF & (x)) << 0)
+
+#define S3C24XX_LCD_VIDINTCON S3C2410_LCDREG(0xAC)
+
+#define S3C24XX_LCD_VIDW00ADD0B0 S3C2410_LCDREG(0x64)
+#define S3C24XX_LCD_VIDW00ADD0B1 S3C2410_LCDREG(0x68)
+
+#define S3C24XX_LCD_VIDW00ADD1B0 S3C2410_LCDREG(0x7C)
+#define S3C24XX_LCD_VIDW00ADD1B1 S3C2410_LCDREG(0x80)
+
+#define S3C24XX_LCD_VIDW00ADD2B0 S3C2410_LCDREG(0x94)
+#define S3C24XX_LCD_VIDW00ADD2B1 S3C2410_LCDREG(0x98)
+
+#define S3C24XX_LCD_SYSIFCON0 S3C2410_LCDREG(0x130)
+#define S3C24XX_LCD_SYSIFCON1 S3C2410_LCDREG(0x134)
+
#endif /* ___ASM_ARCH_REGS_LCD_H */
+
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
index 6026d091a2fe..75f452255db6 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
@@ -23,6 +23,11 @@
#define S3C2443_PLLCON_PDIVMASK ((1<<(1+(9-8)))-1)
#define S3C2443_PLLCON_SDIVMASK (3)
+#define S3C2443_LOCKTIME0 S3C2443_CLKREG(0x00)
+#define S3C2443_LOCKTIME1 S3C2443_CLKREG(0x04)
+#define S3C2443_LOCKCON0 S3C2443_CLKREG(0x00)
+#define S3C2443_LOCKCON1 S3C2443_CLKREG(0x04)
+#define S3C2443_OSCSET S3C2443_CLKREG(0x08)
#define S3C2443_MPLLCON S3C2443_CLKREG(0x10)
#define S3C2443_EPLLCON S3C2443_CLKREG(0x18)
#define S3C2443_CLKSRC S3C2443_CLKREG(0x20)
@@ -37,6 +42,55 @@
#define S3C2443_SYSID S3C2443_CLKREG(0x5C)
#define S3C2443_PWRCFG S3C2443_CLKREG(0x60)
#define S3C2443_RSTCON S3C2443_CLKREG(0x64)
+#define S3C2443_RSTSTAT S3C2443_CLKREG(0x68)
+#define S3C2443_PHYCTRL S3C2443_CLKREG(0x80)
+#define S3C2443_PHYPWR S3C2443_CLKREG(0x84)
+#define S3C2443_URSTCON S3C2443_CLKREG(0x88)
+#define S3C2443_UCLKCON S3C2443_CLKREG(0x8C)
+
+/* Control bits of the power management configuration register */
+#define S3C2443_PWRCFG_USBPHY_ON (1 << 4)
+
+/* Control bits for the USB reset control register */
+#define S3C2443_URSTCON_PHY (1 << 0)
+#define S3C2443_URSTCON_HOST (1 << 1)
+#define S3C2443_URSTCON_FUNC (1 << 2)
+
+/* Control bits for the USB clock configuration register */
+#define S3C2443_UCLKCON_VBUS_PULLUP (1 << 31)
+#define S3C2443_UCLKCON_THOST_ENABLE (0 << 4)
+#define S3C2443_UCLKCON_THOST_DISABLE (1 << 4)
+#define S3C2443_UCLKCON_FUNC_ENABLE (1 << 2)
+#define S3C2443_UCLKCON_FUNC_DISABLE (0 << 2)
+#define S3C2443_UCLKCON_HOST_ENABLE (1 << 1)
+#define S3C2443_UCLKCON_HOST_DISABLE (0 << 1)
+#define S3C2443_UCLKCON_TFUNC_ENABLE (1 << 0)
+#define S3C2443_UCLKCON_TFUNC_DISABLE (0 << 0)
+
+/* Control bits for the USB phy control */
+#define S3C2443_PHYCTRL_CLKSEL(x) (((x) & 0x3) << 3)
+#define S3C2443_PHYCTRL_CLKSEL_24MHZ (0x3 << 3)
+#define S3C2443_PHYCTRL_CLKSEL_12MHZ (0x2 << 3)
+#define S3C2443_PHYCTRL_CLKSEL_48MHZ (0x0 << 3)
+#define S3C2443_PHYCTRL_EXTCLK_OSCI (1 << 2)
+#define S3C2443_PHYCTRL_EXTCLK_CRYSTAL (0 << 2)
+#define S3C2443_PHYCTRL_INTPLL_SYS (0 << 1)
+#define S3C2443_PHYCTRL_INTPLL_USB (1 << 1)
+#define S3C2443_PHYCTRL_DOWN_DEV (1 << 0)
+#define S3C2443_PHYCTRL_DOWN_HOST (0 << 0)
+
+/* Control bits for the USB phy power control */
+#define S3C2443_PHYPWR_COMMON_ON (1 << 31)
+#define S3C2443_PHYPWR_ANALOG_DOWN (1 << 4)
+#define S3C2443_PHYPWR_ANALOG_UP (0 << 4)
+#define S3C2443_PHYPWR_PLLREF_EXT (0 << 3)
+#define S3C2443_PHYPWR_PLLREF_INT (1 << 3)
+#define S3C2443_PHYPWR_XO_UP (1 << 2)
+#define S3C2443_PHYPWR_XO_DOWN (0 << 2)
+#define S3C2443_PHYPWR_PLL_UP (0 << 1)
+#define S3C2443_PHYPWR_PLL_DOWN (1 << 1)
+#define S3C2443_PHYPWR_SUSP_ENABLE (1 << 0)
+#define S3C2443_PHYPWR_SUSP_DISABLE (0 << 0)
#define S3C2443_SWRST_RESET (0x533c2443)
@@ -128,6 +182,7 @@
#define S3C2443_PCLKCON_UART3 (1<<3)
#define S3C2443_PCLKCON_IIC (1<<4)
#define S3C2443_PCLKCON_SDI (1<<5)
+#define S3C2443_PCLKCON_HSSPI (1<<6)
#define S3C2443_PCLKCON_ADC (1<<7)
#define S3C2443_PCLKCON_AC97 (1<<8)
#define S3C2443_PCLKCON_IIS (1<<9)
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-mem.h b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-mem.h
new file mode 100644
index 000000000000..6e6ca1f3d326
--- /dev/null
+++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-mem.h
@@ -0,0 +1,150 @@
+/* -*- linux-c -*-
+ *
+ * arch/arm/mach-s3c2410/include/mach/regs-s3c2443-mem.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.
+ *
+ * S3C2443 memory register definitions
+ *
+ * Copyright (c) 2009 Digi International
+ *
+ * Author: Luis Galdos <luis.galdos@digi.com>
+ *
+ */
+
+#ifndef __ASM_ARM_REGS_S3C2443_MEM
+#define __ASM_ARM_REGS_S3C2443_MEM
+
+#include <plat/map.h>
+#include <mach/map.h>
+
+#define S3C2443_MEMREG(x) (S3C24XX_VA_MEMCTRL + (x))
+
+/*
+ * @XXX: Add the SSMC registers to the minimal IO mapping table of [1]
+ * [1] arch/arm/plat-s3c24xx/cpu.c
+ *
+ * Luis Galdos
+ */
+#if 0
+#define S3C2443_SSMCREG(x) (S3C2443_VA_SSMC + (x))
+#else
+#define S3C2443_SSMCREG(x) (x)
+#endif
+
+#define S3C2443_BANKCFG S3C2443_MEMREG(0x0000)
+#define S3C2443_BANKCON1 S3C2443_MEMREG(0x0004)
+#define S3C2443_BANKCON2 S3C2443_MEMREG(0x0008)
+#define S3C2443_BANKCON3 S3C2443_MEMREG(0x000C)
+#define S3C2443_REFRESH S3C2443_MEMREG(0x0010)
+#define S3C2443_TIMEOUT S3C2443_MEMREG(0x0014)
+
+/* Bank Idle Cycle Control Registers 0-5 */
+#define S3C2443_SSMC_SMBIDCYR0 S3C2443_SSMCREG(0x0000)
+#define S3C2443_SSMC_SMBIDCYR1 S3C2443_SSMCREG(0x0020)
+#define S3C2443_SSMC_SMBIDCYR2 S3C2443_SSMCREG(0x0040)
+#define S3C2443_SSMC_SMBIDCYR3 S3C2443_SSMCREG(0x0060)
+#define S3C2443_SSMC_SMBIDCYR4 S3C2443_SSMCREG(0x0080)
+#define S3C2443_SSMC_SMBIDCYR5 S3C2443_SSMCREG(0x00A0)
+#define S3C2443_SSMC_SMBIDCYR6 S3C2443_SSMCREG(0x00C0)
+#define S3C2443_SSMC_SMBIDCYR7 S3C2443_SSMCREG(0x00E0)
+
+/* Bank Read Wait State Contro, Registers 0-5 */
+#define S3C2443_SSMC_SMBWSTRDR0 S3C2443_SSMCREG(0x0004)
+#define S3C2443_SSMC_SMBWSTRDR1 S3C2443_SSMCREG(0x0024)
+#define S3C2443_SSMC_SMBWSTRDR2 S3C2443_SSMCREG(0x0044)
+#define S3C2443_SSMC_SMBWSTRDR3 S3C2443_SSMCREG(0x0064)
+#define S3C2443_SSMC_SMBWSTRDR4 S3C2443_SSMCREG(0x0084)
+#define S3C2443_SSMC_SMBWSTRDR5 S3C2443_SSMCREG(0x00A4)
+#define S3C2443_SSMC_SMBWSTRDR6 S3C2443_SSMCREG(0x00C4)
+#define S3C2443_SSMC_SMBWSTRDR7 S3C2443_SSMCREG(0x00E4)
+
+/* Bank Write Wait State Control, Registers 0-5 */
+#define S3C2443_SSMC_SMBWSTWRR0 S3C2443_SSMCREG(0x0008)
+#define S3C2443_SSMC_SMBWSTWRR1 S3C2443_SSMCREG(0x0028)
+#define S3C2443_SSMC_SMBWSTWRR2 S3C2443_SSMCREG(0x0048)
+#define S3C2443_SSMC_SMBWSTWRR3 S3C2443_SSMCREG(0x0068)
+#define S3C2443_SSMC_SMBWSTWRR4 S3C2443_SSMCREG(0x0088)
+#define S3C2443_SSMC_SMBWSTWRR5 S3C2443_SSMCREG(0x00A8)
+#define S3C2443_SSMC_SMBWSTWRR6 S3C2443_SSMCREG(0x00C8)
+#define S3C2443_SSMC_SMBWSTWRR7 S3C2443_SSMCREG(0x00E8)
+
+/* Bank OutPut enable Assertion Delay Control Registers 0-5 */
+#define S3C2443_SSMC_SMBWSTOENR0 S3C2443_SSMCREG(0x000C)
+#define S3C2443_SSMC_SMBWSTOENR1 S3C2443_SSMCREG(0x002C)
+#define S3C2443_SSMC_SMBWSTOENR2 S3C2443_SSMCREG(0x004C)
+#define S3C2443_SSMC_SMBWSTOENR3 S3C2443_SSMCREG(0x006C)
+#define S3C2443_SSMC_SMBWSTOENR4 S3C2443_SSMCREG(0x008C)
+#define S3C2443_SSMC_SMBWSTOENR5 S3C2443_SSMCREG(0x00AC)
+#define S3C2443_SSMC_SMBWSTOENR6 S3C2443_SSMCREG(0x00CC)
+#define S3C2443_SSMC_SMBWSTOENR7 S3C2443_SSMCREG(0x00EC)
+
+/* Bank Write enable Assertion Delay Control Registers 0-5 */
+#define S3C2443_SSMC_SMBWSTWENR0 S3C2443_SSMCREG(0x0010)
+#define S3C2443_SSMC_SMBWSTWENR1 S3C2443_SSMCREG(0x0030)
+#define S3C2443_SSMC_SMBWSTWENR2 S3C2443_SSMCREG(0x0050)
+#define S3C2443_SSMC_SMBWSTWENR3 S3C2443_SSMCREG(0x0070)
+#define S3C2443_SSMC_SMBWSTWENR4 S3C2443_SSMCREG(0x0090)
+#define S3C2443_SSMC_SMBWSTWENR5 S3C2443_SSMCREG(0x00B0)
+#define S3C2443_SSMC_SMBWSTWENR6 S3C2443_SSMCREG(0x00D0)
+#define S3C2443_SSMC_SMBWSTWENR7 S3C2443_SSMCREG(0x00F0)
+
+/* Bank Control Registers 0-5 */
+#define S3C2443_SSMC_SMBCR0 S3C2443_SSMCREG(0x0014)
+#define S3C2443_SSMC_SMBCR1 S3C2443_SSMCREG(0x0034)
+#define S3C2443_SSMC_SMBCR2 S3C2443_SSMCREG(0x0054)
+#define S3C2443_SSMC_SMBCR3 S3C2443_SSMCREG(0x0074)
+#define S3C2443_SSMC_SMBCR4 S3C2443_SSMCREG(0x0094)
+#define S3C2443_SSMC_SMBCR5 S3C2443_SSMCREG(0x00B4)
+#define S3C2443_SSMC_SMBCR6 S3C2443_SSMCREG(0x00D4)
+#define S3C2443_SSMC_SMBCR7 S3C2443_SSMCREG(0x00F4)
+
+/* Bank Control bitfields */
+#define S3C2443_SSMC_SMBC_AVWE 1 << 20
+#define S3C2443_SSMC_SMBC_BLW_8 1 << 18
+#define S3C2443_SSMC_SMBC_BLW_CONT 3 << 18
+#define S3C2443_SSMC_SMBC_BMW 1 << 16
+#define S3C2443_SSMC_SMBC_DROWE 1 << 15
+#define S3C2443_SSMC_SMBC_WR 1 << 14
+#define S3C2443_SSMC_SMBC_AVRE 1 << 12
+#define S3C2443_SSMC_SMBC_BLR_8 1 << 10
+#define S3C2443_SSMC_SMBC_BLR_16 2 << 10
+#define S3C2443_SSMC_SMBC_BLR_CONT 3 << 10
+#define S3C2443_SSMC_SMBC_SRD 1 << 9
+#define S3C2443_SSMC_SMBC_BMR 1 << 8
+#define S3C2443_SSMC_SMBC_DRCS 1 << 7
+#define S3C2443_SSMC_SMBC_SMBLSPOL 1 << 6
+#define S3C2443_SSMC_SMBC_MW_16 1 << 4
+#define S3C2443_SSMC_SMBC_WP 1 << 3
+#define S3C2443_SSMC_SMBC_WAIT 1 << 2
+#define S3C2443_SSMC_SMBC_WPOL 1 << 1
+#define S3C2443_SSMC_SMBC_RBLE 1 << 0
+
+/* Bank Status Registers 0-5 */
+#define S3C2443_SSMC_SMBSR0 S3C2443_SSMCREG(0x0018)
+#define S3C2443_SSMC_SMBSR1 S3C2443_SSMCREG(0x0038)
+#define S3C2443_SSMC_SMBSR2 S3C2443_SSMCREG(0x0058)
+#define S3C2443_SSMC_SMBSR3 S3C2443_SSMCREG(0x0078)
+#define S3C2443_SSMC_SMBSR4 S3C2443_SSMCREG(0x0098)
+#define S3C2443_SSMC_SMBSR5 S3C2443_SSMCREG(0x00B8)
+#define S3C2443_SSMC_SMBSR6 S3C2443_SSMCREG(0x00D8)
+#define S3C2443_SSMC_SMBSR7 S3C2443_SSMCREG(0x00F8)
+
+/* Bank Burst Read Wait delay Control Registers 0-5 */
+#define S3C2443_SSMC_SMBWSTBRDR0 S3C2443_SSMCREG(0x001C)
+#define S3C2443_SSMC_SMBWSTBRDR1 S3C2443_SSMCREG(0x003C)
+#define S3C2443_SSMC_SMBWSTBRDR2 S3C2443_SSMCREG(0x005C)
+#define S3C2443_SSMC_SMBWSTBRDR3 S3C2443_SSMCREG(0x007C)
+#define S3C2443_SSMC_SMBWSTBRDR4 S3C2443_SSMCREG(0x009C)
+#define S3C2443_SSMC_SMBWSTBRDR5 S3C2443_SSMCREG(0x00BC)
+#define S3C2443_SSMC_SMBWSTBRDR6 S3C2443_SSMCREG(0x00DC)
+#define S3C2443_SSMC_SMBWSTBRDR7 S3C2443_SSMCREG(0x00FC)
+
+/* SROMC status register */
+#define S3C2443_SSMC_SSMCSR S3C2443_SSMCREG(0x0200)
+/* SROMC control register */
+#define S3C2443_SSMC_SSMCCR S3C2443_SSMCREG(0x0204)
+
+#endif /* __ASM_ARM_REGS_S3C2443_MEM */
diff --git a/arch/arm/mach-s3c2410/include/mach/s3c2443-fb.h b/arch/arm/mach-s3c2410/include/mach/s3c2443-fb.h
new file mode 100644
index 000000000000..4deb4e24dd33
--- /dev/null
+++ b/arch/arm/mach-s3c2410/include/mach/s3c2443-fb.h
@@ -0,0 +1,71 @@
+/* arch/arm/mach-s3c2410/include/mach/s3c2443-fb.h
+ *
+ * Copyright (c) 2009 Luis Galdos & Pedro Perez de Heredia
+ *
+ * Inspired by fb.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_ARM_S3C2443_FB_H
+#define __ASM_ARM_S3C2443_FB_H
+
+#include <mach/regs-lcd.h>
+
+/* LCD description */
+struct s3c2443fb_display {
+ /* LCD type */
+ unsigned type;
+
+ /* Display name */
+ char *display_name;
+
+ /* Clock parent in ascii: "lcd" -> hclck, "display-if" -> epll */
+ char *clock_source;
+
+ /* Screen size */
+ unsigned short width;
+ unsigned short height;
+
+ /* Screen info */
+ unsigned short xres;
+ unsigned short yres;
+ unsigned short bpp;
+
+ unsigned pixclock; /* pixclock in picoseconds */
+ unsigned short left_margin; /* value in pixels (TFT) or HCLKs (STN) */
+ unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */
+ unsigned short hsync_len; /* value in pixels (TFT) or HCLKs (STN) */
+ unsigned short upper_margin; /* value in lines (TFT) or 0 (STN) */
+ unsigned short lower_margin; /* value in lines (TFT) or 0 (STN) */
+ unsigned short vsync_len; /* value in lines (TFT) or 0 (STN) */
+
+ unsigned int bpp_mode;
+ unsigned int frame_rate;
+
+ /* lcd configuration registers */
+ unsigned long vidcon0;
+ unsigned long vidcon1;
+
+ void (*display_power_enable)(int);
+};
+
+struct s3c2443fb_mach_info {
+
+ struct s3c2443fb_display *displays; /* attached diplays info */
+ struct s3c2443fb_display *display; /* attached diplays info */
+ unsigned num_displays; /* number of defined displays */
+ /* GPIOs */
+ unsigned long gpcup;
+ unsigned long gpcup_mask;
+ unsigned long gpccon;
+ unsigned long gpccon_mask;
+ unsigned long gpdup;
+ unsigned long gpdup_mask;
+ unsigned long gpdcon;
+ unsigned long gpdcon_mask;
+};
+
+#endif /* __ASM_ARM_S3C2443_FB_H */
diff --git a/arch/arm/mach-s3c2410/include/mach/usb-control.h b/arch/arm/mach-s3c2410/include/mach/usb-control.h
index cd91d1591f31..17eb0ac25926 100644
--- a/arch/arm/mach-s3c2410/include/mach/usb-control.h
+++ b/arch/arm/mach-s3c2410/include/mach/usb-control.h
@@ -26,9 +26,22 @@ struct s3c2410_hcd_info {
struct usb_hcd *hcd;
struct s3c2410_hcd_port port[2];
- void (*power_control)(int port, int to);
+ void (*power_control)(struct s3c2410_hcd_info *, int port, int to);
void (*enable_oc)(struct s3c2410_hcd_info *, int on);
void (*report_oc)(struct s3c2410_hcd_info *, int ports);
+
+ /* GPIO that should be used as interrupt source */
+ unsigned long oc_gpio;
+ unsigned long oc_gpio_cfg;
+ int oc_irq;
+
+ /* GPIO with the power enable */
+ unsigned long pw_gpio;
+ unsigned long pw_gpio_cfg;
+ unsigned long pw_gpio_inv;
+
+ /* Timer used for the secure power-on of the VBUS */
+ struct timer_list timer;
};
static void inline s3c2410_usb_report_oc(struct s3c2410_hcd_info *info, int ports)
diff --git a/arch/arm/mach-s3c2443/Kconfig b/arch/arm/mach-s3c2443/Kconfig
index 14252f573754..a37a84d65a13 100644
--- a/arch/arm/mach-s3c2443/Kconfig
+++ b/arch/arm/mach-s3c2443/Kconfig
@@ -20,6 +20,9 @@ config S3C2443_DMA
menu "S3C2443 Machines"
+choice
+ prompt "S3C2443 Machines"
+
config MACH_SMDK2443
bool "SMDK2443"
select CPU_S3C2443
@@ -27,4 +30,84 @@ config MACH_SMDK2443
help
Say Y here if you are using an SMDK2443
+config MACH_CC9M2443JS
+ bool "ConnectCore 9M 2443 on a JSCC9M2443 Devboard"
+ select CPU_S3C2443
+ select MACH_CC9M2443JS_PM if PM
+ help
+ Say Y here if you are using the ConnectCore 9M 2443 module
+ on a JSCC9M2443 Development Board
+
+config MACH_CCW9M2443JS
+ bool "ConnectCore Wi-9M 2443 on a JSCC9M2443 Devboard"
+ select CPU_S3C2443
+ select MACH_CC9M2443JS_PM if PM
+ help
+ Say Y here if you are using the ConnectCore Wi-9M 2443 module
+ on a JSCC9M2443 Development Board
+
+endchoice
+
+config MACH_CC9M2443JS_GPIOLIB
+ bool "Enables the machine's GPIO-Lib"
+ depends on (MACH_CC9M2443JS || MACH_CCW9M2443JS)
+ help
+ Say Y to enable the GPIO-Lib support for the ConnectCore 9M 2443 module
+ on a JSCC9M2443 Development Board
+
+config MACH_CC9M2443JS_PM
+ bool
+ depends on (MACH_CC9M2443JS || MACH_CCW9M2443JS) && PM
+ help
+ Internal config for the Power Management support on the CC9M2443JS
+
+config MACH_CC9M2443JS_USB
+ bool "Enable the USB overload protection"
+ depends on (MACH_CC9M2443JS || MACH_CCW9M2443JS) && USB_OHCI_HCD
+ default y
+ help
+ Enable the overload protection for the ConnectCore 9M 2443 module on a
+ JSCC9M2443 Development Board.
+
+config S3C2443_USB_PHY
+ bool "Enable the USB PHY for the S3C2443"
+ depends on CPU_S3C2443
+ depends on USB_SUPPORT
+ default y
+ help
+ This option enables the USB PHY on the S3C2443.
+
+ IMPORTANT: After enabling/disabling the functionality of the USB PHY
+ please verify that the UDC controller is correctly configured. When
+ disabling the S3C2443-UDC the configuration menu selects automatically
+ another one. The reconfiguration can be done under:
+
+ Device Drivers > USB support > USB Gadget Support
+
+choice
+ prompt "Functionality"
+ depends on S3C2443_USB_PHY
+ bool
+
+config S3C2443_USB_PHY_UDC
+ bool "Device Controller"
+ select USB_GADGET
+ select USB_GADGET_SELECTED
+ select USB_GADGET_DUALSPEED
+ select USB_GADGET_S3C2443
+ select USB_S3C2443
+ help
+ This option enables the USB PHY to work as device controller. Additionally
+ it enables the USB Gadget framework.
+
+config S3C2443_USB_PHY_PORT
+ bool "Second root hub port"
+ select USB_OHCI_HCD
+ help
+ This options enables the USB PHY to work as second USB root hub port.
+
+endchoice # S3C2443_USB_PHY
+
+source "arch/arm/mach-s3c2443/displays/Kconfig"
+
endmenu
diff --git a/arch/arm/mach-s3c2443/Makefile b/arch/arm/mach-s3c2443/Makefile
index d1843c9eb8bd..5ad95c798a54 100644
--- a/arch/arm/mach-s3c2443/Makefile
+++ b/arch/arm/mach-s3c2443/Makefile
@@ -18,3 +18,17 @@ obj-$(CONFIG_S3C2443_DMA) += dma.o
# Machine support
obj-$(CONFIG_MACH_SMDK2443) += mach-smdk2443.o
+obj-$(CONFIG_MACH_CC9M2443JS) += mach-cc9m2443js.o gpio.o
+obj-$(CONFIG_MACH_CCW9M2443JS) += mach-ccw9m2443js.o piper-ccw9m2443.o gpio.o
+
+ifeq ($(CONFIG_MACH_CCW9M2443JS),y)
+CFLAGS_piper-ccw9m2443.o += -I$(srctree)/drivers/net/wireless/digiPiper/
+CFLAGS_mach-ccw9m2443js.o += -I$(srctree)/drivers/net/wireless/digiPiper/
+endif
+
+
+# Support for the USB overload protection
+obj-$(CONFIG_MACH_CC9M2443JS_USB) += cc9m2443js-usb.o
+
+# Power Management support
+obj-$(CONFIG_MACH_CC9M2443JS_PM) += cc9m2443js-pm.o cc9m2443js-sleep.o
diff --git a/arch/arm/mach-s3c2443/cc9m2443-custom.h b/arch/arm/mach-s3c2443/cc9m2443-custom.h
new file mode 100644
index 000000000000..cfa5b8a3c418
--- /dev/null
+++ b/arch/arm/mach-s3c2443/cc9m2443-custom.h
@@ -0,0 +1,31 @@
+/* -*- linux-c -*-
+ *
+ * linux/arch/arm/mach-s3c2443/cc9m2443-custom.h
+ *
+ * Copyright (c) 2008 Digi International Spain
+ *
+ * http://www.fluff.org/ben/smdk2443/
+ * http://www.digi.com/products/embeddedsolutions/connectcore9m.jsp
+ *
+ * Thanks to Samsung for the loan of an SMDK2443
+ *
+ * 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 __CCM92443_CUSTOM_H
+#define __CCM92443_CUSTOM_H
+
+/* Header file that will include customized hardware devices */
+
+
+
+
+
+
+
+
+#endif /* __CCM92443_CUSTOM_H */
diff --git a/arch/arm/mach-s3c2443/cc9m2443js-pm.c b/arch/arm/mach-s3c2443/cc9m2443js-pm.c
new file mode 100644
index 000000000000..2d56de817894
--- /dev/null
+++ b/arch/arm/mach-s3c2443/cc9m2443js-pm.c
@@ -0,0 +1,221 @@
+/* -*- linux-c -*-
+ *
+ * linux/arch/arm/mach-s3c2443/cc9m2443js-pm.c
+ *
+ * Copyright (c) 2009 Digi International Spain
+ *
+ * http://www.digi.com/products/embeddedsolutions/connectcore9m.jsp
+ *
+ * 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.
+ *
+ * Author : Luis Galdos <luis.galdos@digi.com>
+ *
+ */
+
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <mach/memory.h>
+#include <asm/memory.h>
+#include <mach/map.h>
+#include <mach/regs-s3c2443-mem.h>
+#include <mach/hardware.h>
+
+#include <mach/regs-gpio.h>
+#include <mach/regs-gpioj.h>
+#include <mach/regs-lcd.h>
+#include <mach/regs-s3c2443-clock.h>
+#include <mach/regs-power.h>
+
+#include <linux/interrupt.h>
+#include <asm/mach/irq.h>
+#include <mach/gpio.h>
+
+#include "cc9m2443js-pm.h"
+
+
+#define CC9M2443_PM_ITEM_OFFSET(off) { .offset = off }
+#define CC9M2443_PM_ITEM_BASE(bas) { .base = bas }
+
+struct cc9m2443js_pm_gpio {
+ int gpio;
+ unsigned long config;
+ int irq;
+ int state;
+ unsigned long flags;
+ irqreturn_t (* irq_handler)(int irq, void *gpio);
+};
+
+struct cc9m2443_pm_reg {
+ void __iomem *base;
+ unsigned long offset;
+ unsigned long val;
+};
+
+/*
+ * The SSMC registers are not part of the minimal IO mapping of the CPU
+ */
+static void __iomem *ssmc_vptr = NULL;
+
+/* Converts the virtual to physical address of the stack pointer */
+unsigned long sleep_phys_sp(void *sp)
+{
+ return virt_to_phys(sp);
+}
+
+/* These are the register for the external Ethernet controller (CS5) */
+static struct cc9m2443_pm_reg s3c2443_regs_ssmc[] = {
+ CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBIDCYR5),
+ CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBWSTRDR5),
+ CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBWSTWRR5),
+ CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBWSTOENR5),
+ CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBWSTWENR5),
+ CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBCR5),
+ CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBWSTBRDR5),
+};
+
+/* We MUST save the reset configuration register */
+static struct cc9m2443_pm_reg s3c2443_regs_syscon[] = {
+ CC9M2443_PM_ITEM_BASE(S3C2443_RSTCON),
+ CC9M2443_PM_ITEM_BASE(S3C2443_PWRCFG),
+ CC9M2443_PM_ITEM_BASE(S3C2443_OSCSET),
+};
+
+/* If the passed register doesn't have a base register, the offset will be used */
+static void cc9m2443_pm_restore_regs(struct cc9m2443_pm_reg *regs, int count,
+ void __iomem *offptr)
+{
+ int cnt;
+ void __iomem *reg_ptr;
+
+ if (!regs) {
+ printk(KERN_ERR "[ RESUME ] NULL pointer passed.\n");
+ return;
+ }
+
+ for (cnt = 0; cnt < count; cnt++, regs++) {
+ reg_ptr = (regs->base) ? (regs->base) : (offptr + regs->offset);
+ __raw_writel(regs->val, reg_ptr);
+ }
+}
+
+/* If the passed register doesn't have a base register, the offset will be used */
+static void cc9m2443_pm_save_regs(struct cc9m2443_pm_reg *regs, int count,
+ void __iomem *offptr)
+{
+ int cnt;
+ void __iomem *reg_ptr;
+
+ if (!regs) {
+ printk(KERN_ERR "[ SUSPEND ] NULL pointer passed!\n");
+ return;
+ }
+
+ for (cnt = 0; cnt < count; cnt++, regs++) {
+ reg_ptr = (regs->base) ? (regs->base) : (offptr + regs->offset);
+ regs->val = __raw_readl(reg_ptr);
+ }
+}
+
+/* Save all the registers that are part of the CC9M2443 */
+static int cc9m2443_pm_save(void)
+{
+ /* First map to our virtual memory if required */
+ if (!ssmc_vptr)
+ ssmc_vptr = ioremap_nocache(S3C2443_PA_SSMC, S3C2443_SZ_SSMC);
+
+ if (!ssmc_vptr)
+ printk(KERN_ERR "Couldn't save the regs of the SSMC\n");
+ else
+ cc9m2443_pm_save_regs(s3c2443_regs_ssmc, ARRAY_SIZE(s3c2443_regs_ssmc),
+ ssmc_vptr);
+
+ cc9m2443_pm_save_regs(s3c2443_regs_syscon, ARRAY_SIZE(s3c2443_regs_syscon),
+ NULL);
+
+ return 0;
+}
+
+static int cc9m2443_pm_restore(void)
+{
+ if (ssmc_vptr)
+ cc9m2443_pm_restore_regs(s3c2443_regs_ssmc,
+ ARRAY_SIZE(s3c2443_regs_ssmc), ssmc_vptr);
+
+ cc9m2443_pm_restore_regs(s3c2443_regs_syscon,
+ ARRAY_SIZE(s3c2443_regs_syscon), NULL);
+
+ return 0;
+}
+
+/*
+ * This is the CPU-specific prepare function (Code coming from the WinCE world)
+ * (Luis Galdos)
+ */
+static void s3c2443_pm_prepare(void)
+{
+ __raw_writel(0xff80, S3C2443_RSTCON);
+ __raw_writel(0x8000, S3C2443_OSCSET);
+ __raw_writel(0x8008, S3C2443_PWRCFG);
+}
+
+static int cc9m2443js_pm_suspend(struct sys_device *sd, pm_message_t state)
+{
+ /* Write the magic value u-boot uses to check for resume into
+ * the INFORM0 register, and ensure INFORM1 is set to the
+ * correct address to resume from. */
+
+ printk(KERN_DEBUG "[ SUSPEND ] Setting resume address to %p [0x%lx]\n",
+ s3c2443_cpu_resume,
+ virt_to_phys(s3c2443_cpu_resume));
+ __raw_writel(0x2BED, S3C2412_INFORM0);
+ __raw_writel(virt_to_phys(s3c2443_cpu_resume), S3C2412_INFORM1);
+
+ /*
+ * This two function pointers are used by the platform driver (see [1])
+ * for preparing and entering in the sleep mode.
+ * [1] arch/arm/plat-s3c24xx/pm.c
+ */
+ pm_cpu_prep = s3c2443_pm_prepare;
+ pm_cpu_sleep = s3c2443_cpu_suspend;
+
+ /* Save the registers */
+ cc9m2443_pm_save();
+
+ return 0;
+}
+
+static int cc9m2443js_pm_resume(struct sys_device *sd)
+{
+ __raw_writel(0x0, S3C2412_INFORM0);
+ __raw_writel(0x0, S3C2412_INFORM1);
+ __raw_writel(0x0, S3C2412_INFORM2);
+
+ cc9m2443_pm_restore();
+
+ return 0;
+}
+
+/* Internal system class/device for accessing to the PM-functions */
+static struct sysdev_class cc9m2443js_pm_sysclass = {
+ .name = "cc9m2443js-pm",
+ .suspend = cc9m2443js_pm_suspend,
+ .resume = cc9m2443js_pm_resume,
+};
+
+static struct sys_device cc9m2443js_pm_sysdev = {
+ .cls = &cc9m2443js_pm_sysclass,
+};
+
+/* This function generates the required sysclass for our platform */
+void __init cc9m2443js_pm_init(void)
+{
+ sysdev_class_register(&cc9m2443js_pm_sysclass);
+ sysdev_register(&cc9m2443js_pm_sysdev);
+
+ /* This is the main function for the PM of the Samsung-platforms */
+ s3c2410_pm_init();
+}
diff --git a/arch/arm/mach-s3c2443/cc9m2443js-pm.h b/arch/arm/mach-s3c2443/cc9m2443js-pm.h
new file mode 100644
index 000000000000..904e6afb826a
--- /dev/null
+++ b/arch/arm/mach-s3c2443/cc9m2443js-pm.h
@@ -0,0 +1,31 @@
+/* -*- linux-c -*-
+ *
+ * linux/arch/arm/mach-s3c2443/cc9m2443js-pm.h
+ *
+ * Copyright (c) 2009 Digi International Spain
+ *
+ * http://www.digi.com/products/embeddedsolutions/connectcore9m.jsp
+ *
+ * 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.
+ *
+ * Author : Luis Galdos <luis.galdos@digi.com>
+ *
+ */
+
+#ifndef _CC9M2443JS_PM_H
+#define _CC9M2443JS_PM_H
+
+#include <linux/sysdev.h>
+#include <plat/pm.h>
+
+unsigned long sleep_phys_sp(void *sp);
+
+/* These are coming from the assembler code */
+extern void s3c2443_cpu_suspend(void);
+extern void s3c2443_cpu_resume(void);
+
+void cc9m2443js_pm_init(void);
+
+#endif /* _CC9M2443JS_PM_H */
diff --git a/arch/arm/mach-s3c2443/cc9m2443js-sleep.S b/arch/arm/mach-s3c2443/cc9m2443js-sleep.S
new file mode 100644
index 000000000000..c66ca42ac38e
--- /dev/null
+++ b/arch/arm/mach-s3c2443/cc9m2443js-sleep.S
@@ -0,0 +1,168 @@
+/* linux/arch/arm/mach-s3c24xx/sleep-s3c2443.S
+ *
+ * $Id: sleep-s3c2443.S,v 1.3 2006/11/08 01:07:53 eyryu Exp $
+ *
+ * Copyright (c) 2006 Samsung Electronics
+ * Ryu Euiyoul <steven.ryu@samsung.com>
+ *
+ * S3C Power Manager (Suspend-To-RAM) 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; 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/config.h>
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <mach/hardware.h>
+
+
+#if 0
+#define ENABLE_GPIO_TOGGLE
+#endif
+
+ .text
+
+/* s3c_cpu_suspend
+ *
+ * put the cpu into sleep mode
+ *
+ * entry:
+ * r0 = sleep save block
+*/
+
+ENTRY(s3c2443_cpu_suspend)
+ stmfd sp!, { r4 - r12, lr } @ save registers on stack
+
+ @@ get co-processor registers
+ mrc p15, 0, r4, c3, c0, 0 @ Domain ID
+ mrc p15, 0, r5, c2, c0, 0 @ translation table base address
+ mrc p15, 0, r6, c13, c0, 0 @ PID
+ mrc p15, 0, r7, c1, c0, 0 @ control register
+
+ @ store them plus current virtual stact ptr on stack
+ mov r8, sp
+ stmfd sp!, {r4 - r8}
+
+
+ mov r1, #0
+ mcr p15, 0, r1, c8, c7, 0 @ flush I+D TLBs
+ mcr p15, 0, r1, c7, c7, 0 @ flush I & D caches
+ mcr p15, 0, r1, c9, c0, 0 @ invalidate RB
+
+ mov r0, sp
+ bl sleep_phys_sp
+ ldr r1, =sleep_save_sp
+ str r0, [r1]
+
+ @@ flush the caches to ensure everything is back out to
+ @@ SDRAM before the core powers down
+
+ bl arm920_flush_kern_cache_all
+
+ b s3c_goto_sleep
+
+ .align 5
+
+
+ /*
+ * Before entering the sleep mode we must store the jump address in the
+ * configuration register INFORM1
+ */
+s3c_goto_sleep:
+ ldr r0, =0xf4100040 @ =S3C_PWRMODE
+ ldr r1, =0x2BED
+ str r1, [r0]
+
+@ _sleeping:
+@ nop
+@ nop
+@ nop
+@ b _sleeping
+ .ltorg
+
+ /* s3c_cpu_resume
+ *
+ * resume code entry for bootloader to call
+ *
+ * we must put this code here in the data segment as we have no
+ * other way of restoring the stack pointer after sleep, and we
+ * must not write to the code segment (code is read-only)
+ */
+ .data
+ .align 5
+ENTRY(s3c2443_cpu_resume)
+
+ @@@@@@@@@ The U-Boot set the registers: r10 -> GPLDAT
+#if defined(ENABLE_GPIO_TOGGLE)
+ adr r11, _gplcon_virtual_address
+ ldr r10, [r11]
+ mov r5, #50
+_toggle_gpio:
+ ldr r6, [r10]
+ eor r6, r6, #0x800
+ str r6, [r10]
+ mov r4, #0x800000
+_dec_timer:
+ sub r4, r4, #1
+ cmp r4, #0
+ bne _dec_timer
+
+ sub r5, r5, #1
+ cmp r5, #0
+ bne _toggle_gpio
+#endif /* ENABLE_GPIO_TOGGLE */
+ @@@@@@@@@ The U-Boot set the registers: r5 -> GPLDAT
+
+
+ mov r0, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
+ msr cpsr_c, r0
+
+ ldr r0, sleep_save_sp @ address of restore block
+ ldr r2, =resume_after_mmu
+ ldmfd r0, {r4 - r7, sp}
+
+ mov r1, #0
+ mcr p15, 0, r1, c8, c7, 0 @ flush I+D TLBs
+ mcr p15, 0, r1, c7, c7, 0 @ flush I & D caches
+ mcr p15, 0, r1, c9, c0, 0 @ invalidate RB
+ mcr p15, 0, r1, c9, c0, 5 @ allow user space to use RB
+
+ mcr p15, 0, r4, c3, c0, 0 @ Domain ID
+ mcr p15, 0, r5, c2, c0, 0 @ translation table base
+ mcr p15, 0, r6, c13, c0, 0 @ PID
+ b resume_turn_on_mmu @ cache align execution
+
+ .align 5
+resume_turn_on_mmu:
+ mcr p15, 0, r7, c1, c0, 0 @ turn on MMU, caches, etc.
+ nop
+ mov pc, r2
+ nop
+ nop
+ nop
+
+ .ltorg
+sleep_save_sp:
+ .word 0
+
+ @@ DEBUG: Virtual address of the register GPLDAT
+_gplcon_virtual_address:
+ @ .word 0xfb0000f4
+ .word 0x560000f4
+
+ .text
+resume_after_mmu:
+ ldmfd sp!, { r4 - r12, pc }
+
diff --git a/arch/arm/mach-s3c2443/cc9m2443js-usb.c b/arch/arm/mach-s3c2443/cc9m2443js-usb.c
new file mode 100644
index 000000000000..bcd240e7dc5d
--- /dev/null
+++ b/arch/arm/mach-s3c2443/cc9m2443js-usb.c
@@ -0,0 +1,195 @@
+/* -*- linux-c -*-
+ *
+ * linux/arch/arm/mach-s3c2443/cc9m2443js-usb.c
+ *
+ * Copyright (c) 2004,2005 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * http://www.simtec.co.uk/products/EB2410ITX/
+ *
+ * Based on the Simtec platform code
+ *
+ * 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/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/bast-map.h>
+#include <mach/bast-irq.h>
+#include <mach/usb-control.h>
+#include <mach/regs-gpio.h>
+#include <mach/gpio.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+
+#include <plat/devs.h>
+
+#include "cc9m2443js-usb.h"
+
+/* control power and monitor over-current events on various Simtec
+ * designed boards.
+ */
+
+static unsigned int power_state[2];
+
+static void cc9m2443js_usb_timer_func(unsigned long _info)
+{
+ struct s3c2410_hcd_info *info;
+
+ info = (struct s3c2410_hcd_info *)_info;
+
+ s3c2410_usb_report_oc(info, 0);
+
+ /*
+ * IMPORTANT: Some power switches, like the MAX890, reports an overload
+ * when they are powered on. For avoiding endless overload reports it's
+ * required to wait for some time (> 400usecs.) before reenabling the
+ * interrupt line of the overload detection.
+ * (Luis Galdos)
+ */
+ mdelay(1);
+
+ enable_irq(info->oc_irq);
+}
+
+/*
+ * If the USB-PHY is used as second root hub port, then we must take care of its
+ * current state, otherwise only the first port is considered.
+ */
+#if defined(CONFIG_S3C2443_USB_PHY_PORT)
+# define CC9M2443JS_USB_PORT_ON(state) (state[0] && state[1])
+#else
+# define CC9M2443JS_USB_PORT_ON(state) (state[0])
+#endif /* CONFIG_S3C2443_USB_PHY_PORT */
+
+static void cc9m2443js_usb_powercontrol(struct s3c2410_hcd_info * info, int port, int to)
+{
+ pr_debug("USB power: port %d | to %d [%d %d]\n",
+ port, to, power_state[0], power_state[1]);
+
+ power_state[port] = to;
+
+ /* @XXX: Power control without a configured GPIO? */
+ if (!info->pw_gpio)
+ return;
+
+ if (CC9M2443JS_USB_PORT_ON(power_state))
+ s3c2443_gpio_setpin(info->pw_gpio, 1 ^ info->pw_gpio_inv);
+ else
+ s3c2443_gpio_setpin(info->pw_gpio, 0 ^ info->pw_gpio_inv);
+}
+
+static irqreturn_t cc9m2443js_usb_ocirq(int irq, void *_info)
+{
+ struct s3c2410_hcd_info *info = _info;
+
+ if (s3c2410_gpio_getpin(info->oc_gpio) == 0) {
+ s3c2410_usb_report_oc(info, 3);
+ pr_info("USB IRQ: Overcurrent Detected\n");
+ } else {
+
+ disable_irq(info->oc_irq);
+ pr_debug("USB IRQ: Scheduling timer for clearing OC\n");
+ mod_timer(&info->timer, jiffies + jiffies_to_msecs(100));
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * The S3C2443 doesn't have a dedicated interrupt for the OC detection.
+ * We use the external interrupt line connected to the GPG8
+ */
+static void cc9m2443js_usb_enableoc(struct s3c2410_hcd_info *info, int on)
+{
+ int ret;
+
+ pr_debug("Command to %s OC detection\n", on ? "enable" : "disable");
+
+ info->oc_irq = s3c2443_gpio_getirq(info->oc_gpio);
+ if (info->oc_irq < 0) {
+ printk(KERN_ERR "[ ERROR ] Couldn't get a GPIO interrupt\n");
+ return;
+ }
+
+ if (on) {
+ ret = request_irq(info->oc_irq, cc9m2443js_usb_ocirq,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "s3c2443 USB OC", info);
+ if (ret != 0)
+ printk(KERN_ERR "[ ERROR ] Failed to request usb oc irq\n");
+ } else
+ free_irq(info->oc_irq, info);
+}
+
+static struct s3c2410_hcd_info cc9m2443js_usb_info = {
+ .port[0] = {
+ .flags = S3C_HCDFLG_USED
+ },
+
+ /* Enable the second port if required */
+#if defined(CONFIG_S3C2443_USB_PHY_PORT)
+ .port[1] = {
+ .flags = S3C_HCDFLG_USED
+ },
+#endif /* CONFIG_S3C2443_USB_PHY_PORT */
+
+ .power_control = cc9m2443js_usb_powercontrol,
+ .enable_oc = cc9m2443js_usb_enableoc,
+
+ /* Over current GPIO */
+ .oc_gpio = S3C2410_GPG8,
+ .oc_gpio_cfg = S3C2410_GPG8_EINT16,
+
+ /* Power switch GPIO (inverted by the CC9M2443JS) */
+ .pw_gpio = S3C2410_GPA14,
+ .pw_gpio_cfg = S3C2410_GPA14_OUT,
+ .pw_gpio_inv = 1,
+};
+
+int __init cc9m2443js_usb_init(void)
+{
+ struct s3c2410_hcd_info *info = &cc9m2443js_usb_info;
+
+ s3c_device_usb.dev.platform_data = &cc9m2443js_usb_info;
+
+ /* Configure the GPIO and disable the pullups */
+ if (info->oc_gpio) {
+ s3c2443_gpio_cfgpin(info->oc_gpio, info->oc_gpio_cfg);
+ s3c2443_gpio_extpull(info->oc_gpio, 0);
+ s3c2443_gpio_set_udp(info->oc_gpio, 1);
+ }
+
+ if (info->pw_gpio) {
+ s3c2443_gpio_cfgpin(info->pw_gpio, info->pw_gpio_cfg);
+
+ /* At first power off the ports */
+ s3c2443_gpio_setpin(info->pw_gpio, 0 ^ info->pw_gpio_inv);
+ }
+
+ /* Init the internal timer */
+ init_timer(&info->timer);
+ info->timer.expires = 0;
+ info->timer.data = (unsigned long)info;
+ info->timer.function = cc9m2443js_usb_timer_func;
+ add_timer(&info->timer);
+
+ return 0;
+}
diff --git a/arch/arm/mach-s3c2443/cc9m2443js-usb.h b/arch/arm/mach-s3c2443/cc9m2443js-usb.h
new file mode 100644
index 000000000000..f349dbfba3e7
--- /dev/null
+++ b/arch/arm/mach-s3c2443/cc9m2443js-usb.h
@@ -0,0 +1,22 @@
+/* -*- linux-c -*-
+ *
+ * linux/arch/arm/mach-s3c2443/cc9m2443js-usb.h
+ *
+ * Copyright (c) 2009 Digi International Spain
+ *
+ * http://www.digi.com/products/embeddedsolutions/connectcore9m.jsp
+ *
+ * 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.
+ *
+ * Author : Luis Galdos <luis.galdos@digi.com>
+ *
+ */
+
+#ifndef _CC9M2443JS_USB_H
+#define _CC9M2443JS_USB_H
+
+extern int cc9m2443js_usb_init(void);
+
+#endif /* _CC9M2443JS_USB_H */
diff --git a/arch/arm/mach-s3c2443/displays/CUSTOM.h b/arch/arm/mach-s3c2443/displays/CUSTOM.h
new file mode 100644
index 000000000000..342bd85645d6
--- /dev/null
+++ b/arch/arm/mach-s3c2443/displays/CUSTOM.h
@@ -0,0 +1,37 @@
+
+// void lcd_power_enable(int enable)
+// {
+// /* Customize me pelase */
+// }
+//
+// #define CUSTOM_DISPLAY \
+// { \
+// .display_name = "CUSTOM", \
+// /* epll clock, 96MHz */ \
+// .clock_source = "display-if", \
+// .width = , \
+// .height = , \
+// .xres = , \
+// .yres = , \
+// .bpp = , \
+// .frame_rate = , \
+// /* horizontal front porch */ \
+// .left_margin = , \
+// /* vertical front porch */ \
+// .upper_margin = , \
+// /* horizontal back porch */ \
+// .right_margin = , \
+// /* vertical back porch */ \
+// .lower_margin = , \
+// .hsync_len = , \
+// .vsync_len = , \
+// .vidcon1 = S3C24XX_LCD_VIDCON1_IHSYNC | \
+// S3C24XX_LCD_VIDCON1_IVSYNC, \
+// .vidcon0 = S3C24XX_LCD_VIDCON0_VIDOUT_RGB_IF | \
+// S3C24XX_LCD_VIDCON0_RGB_PAR | \
+// S3C24XX_LCD_VIDCON0_VCLK_OFF | \
+// S3C24XX_LCD_VIDCON0_CLKDIR_DIVIDED, \
+// .bpp_mode = S3C24XX_LCD_WINCON_16BPP_565, \
+// .display_power_enable = &lcd_power_enable, \
+// }
+
diff --git a/arch/arm/mach-s3c2443/displays/Kconfig b/arch/arm/mach-s3c2443/displays/Kconfig
new file mode 100644
index 000000000000..74466430cc03
--- /dev/null
+++ b/arch/arm/mach-s3c2443/displays/Kconfig
@@ -0,0 +1,32 @@
+# arch/arm/mach-s3c2443/displays/Kconfig
+#
+# Copyright 2009 Digi International Inc
+#
+
+if FB_S3C2443_TFT
+
+comment "Display selection"
+
+config CC9M2443_VGA
+ bool "CRT VGA video support"
+ def_bool y
+ help
+ This enables the video support for the VGA intrface.
+
+config CC9M2443_LQ057Q3DC12I
+ bool "LQ057Q3DC12I TFT LCD support"
+ help
+ This enables the support for the LQ057Q3DC12I TFT display.
+
+config CC9M2443_LQ064V3DG01
+ bool "LQ064V3DG01 TFT LCD support"
+ help
+ This enables the support for the LQ064V3DG01 TFT display.
+
+config CC9M2443_CUSTOM
+ bool "Custom display support"
+ help
+ This enables the support for a customer specific display.
+ displays/CUSTOM.h has to be modified when selecting this.
+
+endif
diff --git a/arch/arm/mach-s3c2443/displays/LQ057Q3DC12I.h b/arch/arm/mach-s3c2443/displays/LQ057Q3DC12I.h
new file mode 100644
index 000000000000..b0f4d367bed3
--- /dev/null
+++ b/arch/arm/mach-s3c2443/displays/LQ057Q3DC12I.h
@@ -0,0 +1,38 @@
+
+static void lcd_power_enable_lq57(int enable)
+{
+ s3c2443_gpio_cfgpin(S3C2410_GPG4, S3C2410_GPG4_OUTP);
+ s3c2410_gpio_setpin(S3C2410_GPG4, enable ? 0 : 1);
+}
+
+#define LQ057Q3DC12I_DISPLAY \
+{ \
+ .display_name = "LQ057Q3DC12I", \
+ /* epll clock, 96MHz */ \
+ .clock_source = "display-if", \
+ .width = 320, \
+ .height = 240, \
+ .xres = 320, \
+ .yres = 240, \
+ .bpp = 16, \
+ .frame_rate = 60, \
+ /* horizontal front porch */ \
+ .left_margin = 3, \
+ /* vertical front porch */ \
+ .upper_margin = 3, \
+ /* horizontal back porch */ \
+ .right_margin = 5, \
+ /* vertical back porch */ \
+ .lower_margin = 3, \
+ .hsync_len = 12, \
+ .vsync_len = 4, \
+ .vidcon1 = S3C24XX_LCD_VIDCON1_IHSYNC | \
+ S3C24XX_LCD_VIDCON1_IVSYNC, \
+ .vidcon0 = S3C24XX_LCD_VIDCON0_VIDOUT_RGB_IF | \
+ S3C24XX_LCD_VIDCON0_RGB_PAR | \
+ S3C24XX_LCD_VIDCON0_VCLK_OFF | \
+ S3C24XX_LCD_VIDCON0_CLKDIR_DIVIDED, \
+ .bpp_mode = S3C24XX_LCD_WINCON_16BPP_565, \
+ .display_power_enable = &lcd_power_enable_lq57, \
+}
+
diff --git a/arch/arm/mach-s3c2443/displays/LQ064V3DG01.h b/arch/arm/mach-s3c2443/displays/LQ064V3DG01.h
new file mode 100644
index 000000000000..9ceedf617b45
--- /dev/null
+++ b/arch/arm/mach-s3c2443/displays/LQ064V3DG01.h
@@ -0,0 +1,38 @@
+
+static void lcd_power_enable_lq64(int enable)
+{
+ s3c2443_gpio_cfgpin(S3C2410_GPG4, S3C2410_GPG4_OUTP);
+ s3c2410_gpio_setpin(S3C2410_GPG4, enable ? 0 : 1);
+}
+
+#define LQ064V3DG01_DISPLAY \
+{ \
+ .display_name = "LQ064V3DG01", \
+ /* epll clock, 96MHz */ \
+ .clock_source = "display-if", \
+ .width = 640, \
+ .height = 480, \
+ .xres = 640, \
+ .yres = 480, \
+ .bpp = 16, \
+ .frame_rate = 60, \
+ /* horizontal front porch */ \
+ .left_margin = 12, \
+ /* vertical front porch */ \
+ .upper_margin = 3, \
+ /* horizontal back porch */ \
+ .right_margin = 40, \
+ /* vertical back porch */ \
+ .lower_margin = 31, \
+ .hsync_len = 95, \
+ .vsync_len = 3, \
+ .vidcon1 = S3C24XX_LCD_VIDCON1_IHSYNC | \
+ S3C24XX_LCD_VIDCON1_IVSYNC, \
+ .vidcon0 = S3C24XX_LCD_VIDCON0_VIDOUT_RGB_IF | \
+ S3C24XX_LCD_VIDCON0_RGB_PAR | \
+ S3C24XX_LCD_VIDCON0_VCLK_OFF | \
+ S3C24XX_LCD_VIDCON0_CLKDIR_DIVIDED, \
+ .bpp_mode = S3C24XX_LCD_WINCON_16BPP_565, \
+ .display_power_enable = &lcd_power_enable_lq64, \
+}
+
diff --git a/arch/arm/mach-s3c2443/displays/VGA.h b/arch/arm/mach-s3c2443/displays/VGA.h
new file mode 100644
index 000000000000..03c45a9efbbb
--- /dev/null
+++ b/arch/arm/mach-s3c2443/displays/VGA.h
@@ -0,0 +1,31 @@
+
+#define VGA_DISPLAY \
+{ \
+ .display_name = "VGA", \
+ /* epll clock, 96MHz */ \
+ .clock_source = "display-if", \
+ .width = 640, \
+ .height = 480, \
+ .xres = 640, \
+ .yres = 480, \
+ .bpp = 16, \
+ .frame_rate = 60, \
+ /* horizontal front porch */ \
+ .left_margin = 8, \
+ /* vertical front porch */ \
+ .upper_margin = 2, \
+ /* horizontal back porch */ \
+ .right_margin = 40, \
+ /* vertical back porch */ \
+ .lower_margin = 25, \
+ .hsync_len = 96, \
+ .vsync_len = 2, \
+ .vidcon1 = S3C24XX_LCD_VIDCON1_IHSYNC | \
+ S3C24XX_LCD_VIDCON1_IVSYNC, \
+ .vidcon0 = S3C24XX_LCD_VIDCON0_VIDOUT_RGB_IF | \
+ S3C24XX_LCD_VIDCON0_RGB_PAR | \
+ S3C24XX_LCD_VIDCON0_VCLK_OFF | \
+ S3C24XX_LCD_VIDCON0_CLKDIR_DIVIDED, \
+ .bpp_mode = S3C24XX_LCD_WINCON_16BPP_565, \
+}
+
diff --git a/arch/arm/mach-s3c2443/displays/displays.h b/arch/arm/mach-s3c2443/displays/displays.h
new file mode 100644
index 000000000000..250e060114a7
--- /dev/null
+++ b/arch/arm/mach-s3c2443/displays/displays.h
@@ -0,0 +1,46 @@
+/*
+ * arch/arm/mach-s3c2443/displays/displays.h
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <mach/s3c2443-fb.h>
+
+#if defined(CONFIG_CC9M2443_VGA)
+#include "VGA.h"
+#endif
+
+#if defined(CONFIG_CC9M2443_LQ057Q3DC12I)
+#include "LQ057Q3DC12I.h"
+#endif
+
+#if defined(CONFIG_CC9M2443_LQ064V3DG01)
+#include "LQ064V3DG01.h"
+#endif
+
+#if defined(CONFIG_CC9M2443_CUSTOM)
+#include "CUSTOM.h"
+#endif
+
+/* List of supported displays */
+struct s3c2443fb_display display_list[] = {
+#if defined(CONFIG_CC9M2443_VGA)
+ VGA_DISPLAY,
+#endif
+#if defined(CONFIG_CC9M2443_LQ057Q3DC12I)
+ LQ057Q3DC12I_DISPLAY,
+#endif
+#if defined(CONFIG_CC9M2443_LQ064V3DG01)
+ LQ064V3DG01_DISPLAY,
+#endif
+#if defined(CONFIG_CC9M2443_CUSTOM)
+ CUSTOM_DISPLAY,
+#endif
+};
+
+
diff --git a/arch/arm/mach-s3c2443/dma.c b/arch/arm/mach-s3c2443/dma.c
index f73ccb25ff94..daddd1322115 100644
--- a/arch/arm/mach-s3c2443/dma.c
+++ b/arch/arm/mach-s3c2443/dma.c
@@ -63,6 +63,12 @@ static struct s3c24xx_dma_map __initdata s3c2443_dma_mappings[] = {
.hw_addr.to = S3C2410_PA_SPI + S3C2410_SPTDAT,
.hw_addr.from = S3C2410_PA_SPI + S3C2410_SPRDAT,
},
+ [DMACH_SPI0_RX] = {
+ .name = "spi0-rx",
+ .channels = MAP(S3C2443_DMAREQSEL_SPI0RX),
+ .hw_addr.to = S3C2410_PA_SPI + S3C2410_SPTDAT,
+ .hw_addr.from = S3C2410_PA_SPI + S3C2410_SPRDAT,
+ },
[DMACH_SPI1] = {
.name = "spi1",
.channels = MAP(S3C2443_DMAREQSEL_SPI1TX),
diff --git a/arch/arm/mach-s3c2443/gpio.c b/arch/arm/mach-s3c2443/gpio.c
new file mode 100644
index 000000000000..3dd170b28716
--- /dev/null
+++ b/arch/arm/mach-s3c2443/gpio.c
@@ -0,0 +1,356 @@
+/* -*- linux-c -*-
+ *
+ * linux/arch/arm/mach-s3c2443/gpio.c
+ *
+ * Copyright (c) 2009 Digi International Spain
+ * Luis Galdos <luis.galdos@digi.com>
+ *
+ * http://www.digi.com/products/embeddedsolutions/connectcore9m.jsp
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <mach/gpio.h>
+
+struct s3c2443_gpio_chip {
+ struct gpio_chip chip;
+ u32 oldbase;
+ u32 in_mask;
+ u32 out_mask;
+ u32 wakeup_mask;
+};
+
+#define to_s3c2443_gpio_chip(c) container_of(c, struct s3c2443_gpio_chip, chip)
+
+unsigned int s3c2443_gpio_read_porta(unsigned int pin)
+{
+ unsigned long gpacdh, gpacdl;
+ unsigned long flags;
+ unsigned long res, mask;
+ int i;
+
+ /* Port A requieres special handling... */
+ local_irq_save(flags);
+ gpacdl = __raw_readl(S3C2410_GPACON);
+ gpacdh = __raw_readl(S3C2410_GPACON + 0x4);
+ local_irq_restore(flags);
+
+ if (pin > S3C2410_GPA7) {
+ gpacdl >>= 8;
+ gpacdh >>= 8;
+ }
+
+ for (i=0, res = 0, mask = 0x1; i < 8; i++) {
+ res |= (((gpacdh & mask) | ((gpacdl & mask) << 1)) << i);
+ mask = mask << 1;
+ }
+
+ if (pin > S3C2410_GPA7)
+ res |= (gpacdh & mask) << i;
+
+ return res;
+}
+
+/* Code coming from the U-Boot 1.1.6 */
+void s3c2443_gpio_setpin(unsigned int pin, unsigned int to)
+{
+ unsigned long dat;
+ void __iomem *base = S3C24XX_GPIO_BASE(pin);
+ unsigned long offs = S3C2410_GPIO_OFFSET(pin);
+
+ if (pin < S3C2410_GPIO_BANKB) {
+ dat = s3c2443_gpio_read_porta(pin);
+ if (pin > S3C2410_GPA7) {
+ dat &= ~(1 << ((offs - S3C2410_GPA7 - 1) * 2));
+ dat |= to << ((offs - S3C2410_GPA7 - 1) * 2);
+ writel(dat, base + 0x04);
+ } else {
+ dat &= ~(1 << (offs * 2));
+ dat |= to << (offs * 2);
+ writel(dat, base);
+ }
+ } else
+ s3c2410_gpio_setpin(pin, to);
+}
+
+void s3c2443_gpio_cfgpin(unsigned int pin, unsigned int function)
+{
+ void __iomem *base;
+ unsigned long mask;
+ unsigned long con;
+ unsigned long offs;
+ unsigned long flags;
+
+ if (pin >= S3C2410_GPIO_BANKB) {
+ s3c2410_gpio_cfgpin(pin, function);
+ return;
+ }
+
+ /* Port A requieres special handling... */
+ con = s3c2443_gpio_read_porta(pin);
+
+ offs = S3C2410_GPIO_OFFSET(pin);
+ if (pin > S3C2410_GPA7)
+ offs = offs - S3C2410_GPA7 - 1;
+
+ mask = 1 << ((offs * 2) + 1);
+ con &= ~mask;
+ con |= (function << ((offs * 2) + 1));
+
+ base = S3C24XX_GPIO_BASE(pin);
+ if (pin > S3C2410_GPA7 )
+ base += 0x4;
+
+ local_irq_save(flags);
+ __raw_writel(con, base);
+ local_irq_restore(flags);
+}
+
+/* Setup the UDP register of the S3C2443 ports */
+void s3c2443_gpio_set_udp(unsigned int pin, int val)
+{
+ void __iomem *base = S3C24XX_GPIO_BASE(pin);
+ unsigned long offs = S3C2410_GPIO_OFFSET(pin) * 2;
+ unsigned long flags;
+ unsigned long up;
+
+ if (pin < S3C2410_GPIO_BANKB)
+ return;
+
+ local_irq_save(flags);
+
+ up = __raw_readl(base + 0x08);
+ up &= ~(3L << offs);
+ up |= val << offs;
+ __raw_writel(up, base + 0x08);
+
+ local_irq_restore(flags);
+}
+
+/* Enable the pull-down of an external interrupt GPIO */
+int s3c2443_gpio_extpull(unsigned int pin, int pullup)
+{
+ void __iomem *base;
+ unsigned long regval;
+ unsigned int offs;
+
+ if (pin < S3C2410_GPF0 || pin > S3C2410_GPG15)
+ return -ENODEV;
+
+ if (pin >= S3C2410_GPG0)
+ base = S3C24XX_EXTINT1;
+ else
+ base = S3C24XX_EXTINT0;
+
+ offs = (S3C2410_GPIO_OFFSET(pin) * 4) + 3;
+
+ /*
+ * First clear the control bit with the corresponding offset and then
+ * use the passed configuration value for setting the control bit
+ */
+ pullup &= 0x1;
+
+ /* There is a HW bug in the EXTINT0 register (code coming from the SMDK) */
+ regval = __raw_readl(base);
+ if (base == S3C24XX_EXTINT0) {
+ unsigned int t;
+ t = (regval & 0x0000000f) << 28;
+ t |= (regval & 0x000000f0) << 20;
+ t |= (regval & 0x00000f00) << 12;
+ t |= (regval & 0x0000f000) << 4;
+ t |= (regval & 0x000f0000) >> 4;
+ t |= (regval & 0x00f00000) >> 12;
+ t |= (regval & 0x0f000000) >> 20;
+ t |= (regval & 0xf0000000) >> 28;
+ regval = t;
+ }
+
+ regval &= (~0x1UL << offs);
+ regval |= (pullup << offs);
+ __raw_writel(regval, base);
+ return 0;
+}
+
+/* Gets the s3c2410 GPIO number from a s3c2443 GPIO number */
+static inline unsigned int s3c2410_gpio_num(struct gpio_chip *chip, unsigned gpio)
+{
+ struct s3c2443_gpio_chip *port = to_s3c2443_gpio_chip(chip);
+
+ return (gpio + port->oldbase);
+}
+
+static int s3c2443_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+ return s3c2410_gpio_getirq(s3c2410_gpio_num(chip, gpio));
+}
+
+int s3c2443_gpio_getirq(unsigned gpio)
+{
+ return s3c2410_gpio_getirq(gpio);
+}
+
+static int s3c2443_gpio_dir_input(struct gpio_chip *chip, unsigned gpio)
+{
+ struct s3c2443_gpio_chip *port = to_s3c2443_gpio_chip(chip);
+
+ if ((1 << gpio) & port->in_mask) {
+ s3c2410_gpio_cfgpin(s3c2410_gpio_num(chip, gpio),
+ S3C2410_GPIO_INPUT);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int s3c2443_gpio_dir_output(struct gpio_chip *chip, unsigned gpio, int value)
+{
+ struct s3c2443_gpio_chip *port = to_s3c2443_gpio_chip(chip);
+
+ if ((1 << gpio) & port->out_mask) {
+ s3c2410_gpio_cfgpin(s3c2410_gpio_num(chip, gpio),
+ S3C2410_GPIO_OUTPUT);
+ s3c2410_gpio_setpin(s3c2410_gpio_num(chip, gpio), value);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int s3c2443_gpio_get(struct gpio_chip *chip, unsigned gpio)
+{
+ return s3c2410_gpio_getpin(s3c2410_gpio_num(chip, gpio)) ? 1 : 0;
+}
+
+static void s3c2443_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
+{
+ s3c2410_gpio_setpin(s3c2410_gpio_num(chip, gpio), value);
+}
+
+static irqreturn_t s3c2443_wakeup_irq(int irq, void *data)
+{
+ return IRQ_HANDLED;
+}
+
+static int s3c2443_gpio_wakeup_conf(struct gpio_chip *chip, unsigned gpio, int enable)
+{
+ struct s3c2443_gpio_chip *port = to_s3c2443_gpio_chip(chip);
+ int irq, ret = 0;
+
+ /* Configure as wakeup interrupt if capable */
+ if (!((1 << gpio) & port->wakeup_mask)) {
+ pr_err("[ ERROR ] GPIO %i not wakeup capable\n", gpio);
+ return -ENODEV;
+ }
+
+ irq = s3c2443_gpio_to_irq(chip, gpio);
+ if (irq < 0) {
+ pr_err("[ ERROR ] Couldn't get an IRQ for gpio %i\n", gpio);
+ return -ENODEV;
+ }
+
+ if (enable) {
+ ret = request_irq(irq, s3c2443_wakeup_irq,
+ IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+ "s3c2443-wio", NULL);
+ if (ret) {
+ pr_err("[ ERROR ] Couldn't request IRQ %i\n", irq);
+ goto exit_wakeup;
+ }
+ enable_irq_wake(irq);
+ } else {
+ disable_irq_wake(irq);
+ free_irq(irq, NULL);
+ s3c2443_gpio_dir_input(chip, gpio);
+ }
+
+ exit_wakeup:
+ return ret;
+}
+
+static void s3c2443_set_pullupdown(struct gpio_chip *chip, unsigned gpio, int value)
+{
+ s3c2443_gpio_set_udp( s3c2410_gpio_num(chip, gpio), value);
+}
+
+#define S3C2443_GPIO_PORT(name,num,start,obase, \
+ inmsk,outmsk,wakemsk) \
+ { \
+ .chip = { \
+ .label = "Port" #name, \
+ .direction_input = s3c2443_gpio_dir_input, \
+ .direction_output = s3c2443_gpio_dir_output, \
+ .get = s3c2443_gpio_get, \
+ .set = s3c2443_gpio_set, \
+ .to_irq = s3c2443_gpio_to_irq, \
+ .wakeup_configure = s3c2443_gpio_wakeup_conf, \
+ .pullupdown = s3c2443_set_pullupdown, \
+ .base = start, \
+ .ngpio = num, \
+ }, \
+ .oldbase = obase, \
+ .in_mask = inmsk, \
+ .out_mask = outmsk, \
+ .wakeup_mask = wakemsk, \
+ }
+
+
+static struct s3c2443_gpio_chip s3c2443_gpio_ports[] = {
+ S3C2443_GPIO_PORT(A, 16, 0, S3C2410_GPIO_BANKA, 0x00000000, 0x0000ffff, 0),
+ S3C2443_GPIO_PORT(B, 11, 16, S3C2410_GPIO_BANKB, 0x000003ff, 0x000003ff, 0),
+ S3C2443_GPIO_PORT(C, 16, 27, S3C2410_GPIO_BANKC, 0x0000ffff, 0x0000ffff, 0),
+ S3C2443_GPIO_PORT(D, 16, 43, S3C2410_GPIO_BANKD, 0x0000ffff, 0x0000ffff, 0),
+ S3C2443_GPIO_PORT(E, 16, 59, S3C2410_GPIO_BANKE, 0x0000ffff, 0x0000ffff, 0),
+ S3C2443_GPIO_PORT(F, 8, 75, S3C2410_GPIO_BANKF, 0x000000ff, 0x000000ff, 0xff),
+ S3C2443_GPIO_PORT(G, 16, 83, S3C2410_GPIO_BANKG, 0x0000ffff, 0x0000ffff, 0xff),
+ S3C2443_GPIO_PORT(H, 15, 99, S3C2410_GPIO_BANKH, 0x00007fff, 0x00007fff, 0),
+ S3C2443_GPIO_PORT(J, 16, 114, S3C2440_GPIO_BANKJ, 0x0000ffff, 0x0000ffff, 0),
+ S3C2443_GPIO_PORT(L, 15, 130, S3C2410_GPIO_BANKL, 0x00007fff, 0x00007fff, 0),
+ S3C2443_GPIO_PORT(M, 2, 145, S3C2410_GPIO_BANKM, 0x00000003, 0x00000000, 0),
+};
+
+/* Gets the s3c2443 GPIO number from a s3c2410 GPIO number */
+unsigned int s3c2443_gpio_num(unsigned gpio)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s3c2443_gpio_ports); i++) {
+ if (S3C2410_BANKBASE(gpio) == s3c2443_gpio_ports[i].oldbase) {
+ return (s3c2443_gpio_ports[i].chip.base + S3C2410_GPIO_OFFSET(gpio));
+ }
+ }
+
+ return -1;
+}
+
+static int __init s3c2443_gpio_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s3c2443_gpio_ports); i++)
+ gpiochip_add(&s3c2443_gpio_ports[i].chip);
+
+ return 0;
+}
+pure_initcall(s3c2443_gpio_init);
+
+EXPORT_SYMBOL(s3c2443_gpio_getirq);
+EXPORT_SYMBOL(s3c2443_gpio_extpull);
+EXPORT_SYMBOL(s3c2443_gpio_cfgpin);
+EXPORT_SYMBOL(s3c2443_gpio_set_udp);
+EXPORT_SYMBOL(s3c2443_gpio_setpin);
+EXPORT_SYMBOL(s3c2443_gpio_num);
diff --git a/arch/arm/mach-s3c2443/irq.c b/arch/arm/mach-s3c2443/irq.c
index 0e0d693f3974..71b084ef40d2 100644
--- a/arch/arm/mach-s3c2443/irq.c
+++ b/arch/arm/mach-s3c2443/irq.c
@@ -137,8 +137,7 @@ static void s3c2443_irq_demux_dma(unsigned int irq, struct irq_desc *desc)
}
#define INTMSK_DMA (1UL << (IRQ_S3C2443_DMA - IRQ_EINT0))
-#define SUBMSK_DMA INTMSK(IRQ_S3C2443_DMA0, IRQ_S3C2443_DMA5)
-
+#define SUBMSK_DMA (0x3F << (IRQ_S3C2443_DMA0 - S3C2410_IRQSUB(0)))
static void s3c2443_irq_dma_mask(unsigned int irqno)
{
@@ -166,12 +165,11 @@ static struct irq_chip s3c2443_irq_dma = {
static void s3c2443_irq_demux_uart3(unsigned int irq, struct irq_desc *desc)
{
- s3c2443_irq_demux(IRQ_S3C2443_UART3, 3);
+ s3c2443_irq_demux(IRQ_S3C2443_RX3, 3);
}
#define INTMSK_UART3 (1UL << (IRQ_S3C2443_UART3 - IRQ_EINT0))
-#define SUBMSK_UART3 (0xf << (IRQ_S3C2443_RX3 - S3C2410_IRQSUB(0)))
-
+#define SUBMSK_UART3 (0x7 << (IRQ_S3C2443_RX3 - S3C2410_IRQSUB(0)))
static void s3c2443_irq_uart3_mask(unsigned int irqno)
{
diff --git a/arch/arm/mach-s3c2443/mach-cc9m2443js.c b/arch/arm/mach-s3c2443/mach-cc9m2443js.c
new file mode 100644
index 000000000000..c487958f02a4
--- /dev/null
+++ b/arch/arm/mach-s3c2443/mach-cc9m2443js.c
@@ -0,0 +1,726 @@
+/* -*- linux-c -*-
+ *
+ * linux/arch/arm/mach-s3c2443/mach-cc9m2443js.c
+ *
+ * Copyright (c) 2007 Simtec Electronics
+ * Copyright (c) 2008 Digi International Spain
+ * Ben Dooks <ben@simtec.co.uk>
+ * Miriam Ruiz <Miriam.Ruiz@digi.com>
+ * Luis Galdos <luis.galdos@digi.com>
+ *
+ * http://www.fluff.org/ben/smdk2443/
+ * http://www.digi.com/products/embeddedsolutions/connectcore9m.jsp
+ *
+ * Thanks to Samsung for the loan of an SMDK2443
+ *
+ * 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/i2c.h>
+#include <linux/dma-mapping.h>
+#include <linux/smc911x.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+#include <asm/delay.h>
+
+#include <plat/regs-serial.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-gpioj.h>
+#include <mach/regs-lcd.h>
+#include <mach/regs-s3c2443-clock.h>
+
+#include <mach/idle.h>
+#include <mach/fb.h>
+#include <asm/plat-s3c24xx/spi.h>
+#include <mach/gpio.h>
+#include <linux/spi/spi.h>
+
+#include <plat/s3c2410.h>
+#include <plat/s3c2440.h>
+#include <plat/clock.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+#include <plat/hsmmc.h>
+#include <plat/ts.h>
+#include <plat/udc.h>
+#include <plat/pcmcia.h>
+#include <mach/irqs.h>
+#include <mach/regs-irq.h>
+#include <plat/irq.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <plat/common-smdk.h>
+#include <asm/plat-s3c24xx/mci.h>
+#include <asm/plat-s3c/nand.h>
+#include <asm/plat-s3c/iic.h>
+
+#include "displays/displays.h"
+#include "cc9m2443js-pm.h"
+#include "cc9m2443js-usb.h"
+
+/*
+ * IMPORTANT: We are using the Ethernet-driver of the Blackfin-project because:
+ * - The driver from the Vanilla-kernel is not working out of the box
+ * - After some modifications (for accessing to the FIFO correctly) the driver
+ * has some performance-problems (lost some packets)
+ * - The driver doesn't use the new NAPI for reading from the controller
+ * (Luis Galdos)
+ */
+static struct resource smsc911x_device_resources[] = {
+ [0] = {
+ .name = "smsc911x-memory",
+ .start = S3C2410_CS5,
+ .end = S3C2410_CS5 + SZ_1M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_EINT9,
+ .end = IRQ_EINT9,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct smc911x_platdata cc9m2443_smsc9118 = {
+ .flags = SMC911X_USE_16BIT,
+ .irq_flags = IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+ .irq_polarity = 0,
+};
+
+static struct platform_device smsc911x_device = {
+ .name = "smsc911x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(smsc911x_device_resources),
+ .resource = smsc911x_device_resources,
+ .dev = {
+ .platform_data = &cc9m2443_smsc9118,
+ }
+};
+
+/* @XXX: The macro will be used for external board-devices under cc9m2443-custom.h */
+#if !defined(CC9M2443_I2C_BASE_BOARD_DEVICES)
+#define CC9M2443_I2C_BASE_BOARD_DEVICES
+/*#define CC9M2443_I2C_BASE_BOARD_DEVICES \
+ { \
+ .driver_name = "rtc-ds1307", \
+ .addr = 0x68, \
+ .type = "ds1337", \
+ } \
+#endif */
+#endif
+
+/*
+ * This is the external IRQ for the RTC. Since the GPIO for the RTC-IRQ is not
+ * available on the connect of the module, the GPIO can be used only with the RTC.
+ */
+#define CC9M2443_RTC_IRQ (IRQ_EINT7)
+#define CC9M2443_RTC_IRQ_GPIO (S3C2410_GPF7)
+#define CC9M2443_RTC_IRQ_CFG (S3C2410_GPF7_EINT7)
+
+/* I2C devices */
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+#include <linux/i2c/pca953x.h>
+static struct pca953x_platform_data pca9554_data = {
+ .gpio_base = 147,
+};
+#endif
+
+static struct i2c_board_info cc9m2443_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("ds1337", 0x68),
+ .irq = CC9M2443_RTC_IRQ
+ },
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+ {
+ I2C_BOARD_INFO("pca9554", 0x20),
+ .platform_data = &pca9554_data,
+ },
+#endif
+ CC9M2443_I2C_BASE_BOARD_DEVICES
+};
+
+static struct map_desc cc9m2443_iodesc[] __initdata = {
+ /* ISA IO Space map (memory space selected by A24) */
+ {
+ .virtual = (u32)S3C24XX_VA_ISA_WORD,
+ .pfn = __phys_to_pfn(S3C2410_CS2),
+ .length = 0x10000,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (u32)S3C24XX_VA_ISA_WORD + 0x10000,
+ .pfn = __phys_to_pfn(S3C2410_CS2 + (1<<24)),
+ .length = SZ_4M,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (u32)S3C24XX_VA_ISA_BYTE,
+ .pfn = __phys_to_pfn(S3C2410_CS2),
+ .length = 0x10000,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (u32)S3C24XX_VA_ISA_BYTE + 0x10000,
+ .pfn = __phys_to_pfn(S3C2410_CS2 + (1<<24)),
+ .length = SZ_4M,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (u32)CC9M2443_VA_LAN,
+ .pfn = __phys_to_pfn(CC9M2443_PA_LAN),
+ .length = CC9M2443_SZ_LAN,
+ .type = MT_DEVICE,
+ }
+};
+
+
+/* Macros for the default port configuration on the CC9M2443 */
+#define CC9M2443_PORT_UCON (S3C2412_UCON_PCLK | \
+ S3C2410_UCON_RXIRQMODE | \
+ S3C2410_UCON_TXIRQMODE | \
+ S3C2410_UCON_RXFIFO_TOI | \
+ S3C2443_UCON_RXERR_IRQEN | \
+ S3C2410_UCON_TXILEVEL | \
+ S3C2410_UCON_RXILEVEL)
+#define CC9M2443_PORT_UFCON (S3C2410_UFCON_FIFOMODE | \
+ S3C2440_UFCON_RXTRIG8 | \
+ S3C2440_UFCON_TXTRIG16)
+#define CC9M2443_PORT_LCON (S3C2410_LCON_CS8)
+
+
+static struct s3c2410_uartcfg cc9m2443_uartcfgs[] __initdata = {
+#ifdef CONFIG_SERIAL_S3C2410_ENABLE_PORTA
+ {
+ .hwport = 0,
+ .flags = 0,
+ .ucon = CC9M2443_PORT_UCON,
+ .ulcon = S3C2410_LCON_CS8,
+ .ufcon = CC9M2443_PORT_UFCON,
+ .rx_gpio = S3C2410_GPH1,
+ .rx_cfg = S3C2443_GPH1_RXD0,
+ .tx_gpio = S3C2410_GPH0,
+ .tx_cfg = S3C2443_GPH0_TXD0,
+#if defined(CONFIG_SERIAL_S3C2410_PORTA_HWCTRL)
+ .rts_gpio = S3C2410_GPH9,
+ .rts_cfg = S3C2443_GPH9_nRTS0,
+ .cts_gpio = S3C2410_GPH8,
+ .cts_cfg = S3C2443_GPH8_nCTS0,
+#endif /* CONFIG_SERIAL_S3C2410_PORTA_HWCTRL */
+ },
+#endif /* CONFIG_SERIAL_S3C2410_ENABLE_PORTA */
+#ifdef CONFIG_SERIAL_S3C2410_ENABLE_PORTB
+ {
+ .hwport = 1,
+ .flags = 0,
+ .ucon = CC9M2443_PORT_UCON,
+ .ulcon = CC9M2443_PORT_LCON,
+ .ufcon = CC9M2443_PORT_UFCON,
+ .rx_gpio = S3C2410_GPH3,
+ .rx_cfg = S3C2443_GPH3_RXD1,
+ .tx_gpio = S3C2410_GPH2,
+ .tx_cfg = S3C2443_GPH2_TXD1,
+#if defined(CONFIG_SERIAL_S3C2410_PORTB_HWCTRL)
+ .rts_gpio = S3C2443_GPH11,
+ .rts_cfg = S3C2443_GPH11_nRTS1,
+ .cts_gpio = S3C2410_GPH10,
+ .cts_cfg = S3C2443_GPH10_nCTS1,
+#endif /* CONFIG_SERIAL_S3C2410_PORTB_HWCTRL */
+ },
+#endif /* CONFIG_SERIAL_S3C2410_ENABLE_PORTB */
+#ifdef CONFIG_SERIAL_S3C2410_ENABLE_PORTC
+ {
+ .hwport = 2,
+ .flags = 0,
+ .ucon = CC9M2443_PORT_UCON,
+ .ulcon = CC9M2443_PORT_LCON,
+ .ufcon = CC9M2443_PORT_UFCON,
+ .rx_gpio = S3C2410_GPH5 ,
+ .rx_cfg = S3C2443_GPH5_RXD2,
+ .tx_gpio = S3C2410_GPH4 ,
+ .tx_cfg = S3C2443_GPH4_TXD2,
+#if defined(CONFIG_SERIAL_S3C2410_PORTC_HWCTRL)
+ .rts_gpio = S3C2410_GPH6,
+ .rts_cfg = S3C2443_GPH6_nRTS2,
+ .cts_gpio = S3C2410_GPH7,
+ .cts_cfg = S3C2443_GPH7_nCTS2,
+#endif /* CONFIG_SERIAL_S3C2410_PORTC_HWCTRL */
+
+ },
+#endif /* CONFIG_SERIAL_S3C2410_ENABLE_PORTC */
+#ifdef CONFIG_SERIAL_S3C2410_ENABLE_PORTD
+ {
+ .hwport = 3,
+ .flags = 0,
+ .ucon = CC9M2443_PORT_UCON,
+ .ulcon = CC9M2443_PORT_LCON,
+ .ufcon = CC9M2443_PORT_UFCON,
+ .rx_gpio = S3C2410_GPH7,
+ .rx_cfg = S3C2443_GPH7_RXD3,
+ .tx_gpio = S3C2410_GPH6,
+ .tx_cfg = S3C2443_GPH6_TXD3,
+ }
+#endif /* CONFIG_SERIAL_S3C2410_ENABLE_PORTD */
+};
+
+/* This is required for the LCD-display */
+static struct s3c2443fb_mach_info cc9m2443fb_mach = {
+ .displays = display_list,
+ .num_displays = ARRAY_SIZE(display_list),
+
+ /* Set the GPIO-configuration */
+ .gpccon = 0xaaa002a8,
+ .gpccon_mask = 0xfff003fc,
+
+ .gpcup = 0xfff003fc,
+ .gpcup_mask = 0xfff003fc,
+
+ .gpdcon = 0xaaa0aaa0,
+ .gpdcon_mask = 0xfff0fff0,
+
+ .gpdup = 0xfff0fff0,
+ .gpdup_mask = 0xfff0fff0,
+
+};
+
+/* This is for the LCD platform device */
+static struct resource s3c2443_tft_resource[] = {
+ [0] = {
+ .start = S3C24XX_PA_TFTLCD,
+ .end = S3C24XX_PA_TFTLCD + S3C24XX_SZ_LCD - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ /* No irq for s3c2443 tft driver */
+};
+
+static u64 s3c2443_lcd_dmamask = 0xffffffffUL;
+
+struct platform_device s3c_device_tft_lcd = {
+ .name = "s3c2443fb-tft",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(s3c2443_tft_resource),
+ .resource = s3c2443_tft_resource,
+ .dev = {
+ .dma_mask = &s3c2443_lcd_dmamask,
+ .coherent_dma_mask = 0xffffffffUL,
+ .platform_data = &cc9m2443fb_mach,
+ }
+};
+
+/*
+ * SDIO device for the CC9M2443JS
+ * The correct driver name is "s3c2440-sdi" and NOT "s3c2410-sdi"
+ */
+static struct resource cc9m2443_sdi_resource[] = {
+ [0] = {
+ .start = S3C2410_PA_SDI,
+ .end = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_SDI,
+ .end = IRQ_SDI,
+ .flags = IORESOURCE_IRQ,
+ }
+
+};
+
+static struct s3c24xx_mci_pdata cc9m2443_sdi_pdata = {
+ .gpio_detect = S3C2410_GPF1,
+ .gpio_wprotect = S3C2410_GPG9,
+ .dma_enable = 1,
+};
+
+struct platform_device cc9m2443_device_sdi = {
+ .name = "s3c2440-sdi",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(cc9m2443_sdi_resource),
+ .resource = cc9m2443_sdi_resource,
+ .dev = {
+ .platform_data = &cc9m2443_sdi_pdata,
+ }
+};
+
+/* High-speed MMC/SD */
+static struct resource cc9m2443_hsmmc_resource[] = {
+ [0] = {
+ .start = S3C2443_PA_HSMMC,
+ .end = S3C2443_PA_HSMMC + S3C2443_SZ_HSMMC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3C2443_HSMMC,
+ .end = IRQ_S3C2443_HSMMC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 cc9m2443_device_hsmmc_dmamask = 0xffffffffUL;
+
+#define HOST_CAPS (MMC_CAP_4_BIT_DATA | \
+ MMC_CAP_8_BIT_DATA | \
+ MMC_CAP_MMC_HIGHSPEED | \
+ MMC_CAP_SD_HIGHSPEED)
+
+struct s3c_hsmmc_cfg cc9m2443_hsmmc_platform = {
+ .hwport = 0,
+ .host_caps = HOST_CAPS,
+ .ctrl3[0] = 0x00800080,
+ .ctrl3[1] = 0x00800080,
+ .clk_name[0] = "hclk", /* 1st clock HCLK */
+ .clk_name[1] = "hsmmc-div", /* 2nd clock hsmmc-epll */
+ .clk_name[2] = "hsmmc-ext", /* 3rd clock external */
+ .gpio_wprotect = S3C2443_GPJ15,
+ .gpio_detect = S3C2443_GPJ14,
+ .gpio_led = S3C2443_GPJ13,
+};
+
+struct platform_device cc9m2443_device_hsmmc = {
+ .name = "s3c-hsmmc",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(cc9m2443_hsmmc_resource),
+ .resource = cc9m2443_hsmmc_resource,
+ .dev = {
+ .dma_mask = &cc9m2443_device_hsmmc_dmamask,
+ .coherent_dma_mask = 0xffffffffUL,
+ .platform_data = &cc9m2443_hsmmc_platform,
+ }
+};
+
+/* Touch device */
+static struct resource cc9m2443_ts_resource[] = {
+ [0] = {
+ .start = S3C2410_PA_ADC,
+ .end = S3C2410_PA_ADC + S3C24XX_SZ_ADC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_ADC,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_TC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+/* @XXX: Set the correct values */
+static struct s3c_ts_mach_info cc9m2443_ts_info = {
+ .delay = 1,
+ .presc = 255,
+ .oversampling_shift = 1,
+ .xmin = 0x3ff,
+ .ymin = 0x3ff,
+ .trigger_ms = 1,
+ .probes = 9,
+};
+
+struct platform_device cc9m2443_device_ts = {
+ .name = "s3c24xx-ts",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(cc9m2443_ts_resource),
+ .resource = cc9m2443_ts_resource,
+ .dev = {
+ .platform_data = &cc9m2443_ts_info,
+ }
+};
+
+/* This is the High Speed SPI-master of the S3C2443 */
+static struct s3c2443_spi_info cc9m2443_hsspi_info = {
+ .bus_num = 0,
+ .num_chipselect = 1,
+ .input_clk = S3C2443_HSSPI_INCLK_PCLK,
+ .miso = {
+ .nr = S3C2410_GPE11,
+ .cfg = S3C2410_GPE11_SPIMISO0,
+ },
+ .mosi = {
+ .nr = S3C2410_GPE12,
+ .cfg = S3C2410_GPE12_SPIMOSI0,
+ },
+ .clk = {
+ .nr = S3C2410_GPE13,
+ .cfg = S3C2410_GPE13_SPICLK0,
+ },
+ .cs[0] = {
+ .nr = S3C2443_GPL13,
+ .cfg = S3C2443_GPL13_nSS0,
+ }
+};
+
+static struct resource cc9m2443_hsspi_resources[] = {
+ {
+ .start = S3C2443_PA_HSSPI,
+ .end = S3C2443_PA_HSSPI + S3C2443_SZ_HSSPI - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_SPI0,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device cc9m2443_device_hsspi = {
+ .name = "spi-s3c2443",
+ .id = 0,
+ .resource = cc9m2443_hsspi_resources,
+ .num_resources = ARRAY_SIZE(cc9m2443_hsspi_resources),
+ .dev = {
+ .platform_data = &cc9m2443_hsspi_info,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ }
+};
+
+/* This is the USB-device of the S3C2443 */
+static struct resource cc9m2443_udc_resources[] = {
+ [0] = {
+ .start = S3C2443_PA_USBDEV,
+ .end = S3C2443_PA_USBDEV+ S3C2443_SZ_USBDEV - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_USBD,
+ .end = IRQ_USBD,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct s3c2410_udc_mach_info cc9m2443_udc_pdata = {
+ .vbus_pin = S3C2410_GPF5,
+ .vbus_pin_inverted = 0,
+ .udc_command = NULL,
+};
+
+static struct platform_device cc9m2443_device_udc = {
+ .name = "s3c2443_udc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(cc9m2443_udc_resources),
+ .resource = cc9m2443_udc_resources,
+ .dev = {
+ .platform_data = &cc9m2443_udc_pdata,
+ }
+};
+
+static struct resource s3c2443_ide_resource[] = {
+ [0] = {
+ .start = S3C2443_PA_CFATA,
+ .end = S3C2443_PA_CFATA + S3C2443_SZ_CFATA,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3C2443_CFCON,
+ .end = IRQ_S3C2443_CFCON,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device s3c443_device_ide = {
+ .name = "s3c2443-ide",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(s3c2443_ide_resource),
+ .resource = s3c2443_ide_resource,
+};
+
+static struct resource s3c2443_pcmcia_resource[] = {
+ [0] = {
+ .start = S3C2443_PA_CFATA,
+ .end = S3C2443_PA_CFATA + S3C2443_SZ_CFATA,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3C2443_CFCON,
+ .end = IRQ_S3C2443_CFCON,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct s3c2443_pcmcia_pdata cc9m2443js_pcmcia_pdata = {
+ .gpio_detect = S3C2410_GPG10,
+};
+
+struct platform_device s3c443_device_pcmcia = {
+ .name = "s3c2443-pcmcia",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(s3c2443_pcmcia_resource),
+ .resource = s3c2443_pcmcia_resource,
+ .dev = {
+ .platform_data = &cc9m2443js_pcmcia_pdata,
+ }
+};
+
+static struct resource s3c2443_adc_resources[] = {
+ [0] = {
+ .name = "s3c2443-adc",
+ .start = S3C2410_PA_ADC,
+ .end = S3C2410_PA_ADC + S3C24XX_SZ_ADC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device s3c2443_device_adc = {
+ .name = "s3c2443-adc",
+ .id = -1,
+ .resource = s3c2443_adc_resources,
+ .num_resources = ARRAY_SIZE(s3c2443_adc_resources),
+ .dev = {
+ .platform_data = &cc9m2443_ts_info, /* shared between touch and adc*/
+ }
+};
+
+
+/* Platform devices for the CC9M2443 */
+static struct platform_device *cc9m2443_devices[] __initdata = {
+ &s3c_device_usb,
+ &s3c_device_tft_lcd,
+ &s3c_device_wdt,
+ &s3c_device_i2c,
+ &s3c_device_nand,
+ &smsc911x_device,
+ &cc9m2443_device_sdi,
+ &cc9m2443_device_hsmmc,
+ &cc9m2443_device_ts,
+ &cc9m2443_device_hsspi,
+ &cc9m2443_device_udc,
+ &s3c443_device_ide,
+ &s3c443_device_pcmcia,
+ &s3c2443_device_adc,
+};
+
+static void __init cc9m2443_map_io(void)
+{
+ struct s3c2410_uartcfg *cfg;
+ int cnt;
+
+ s3c24xx_init_io(cc9m2443_iodesc, ARRAY_SIZE(cc9m2443_iodesc));
+ s3c24xx_init_clocks(12000000);
+
+
+ /*
+ * Init the GPIOs for the UART-ports
+ * @XXX: We can probably move this part to the UART-driver, but for this
+ * moment let's initialize the GPIOs at this place
+ * Luis Galdos
+ */
+ for (cnt = 0; cnt < ARRAY_SIZE(cc9m2443_uartcfgs); cnt++) {
+
+ cfg = &cc9m2443_uartcfgs[cnt];
+
+ s3c2410_gpio_cfgpin(cfg->rx_gpio, cfg->rx_cfg);
+ s3c2410_gpio_cfgpin(cfg->tx_gpio, cfg->tx_cfg);
+
+ if (cfg->cts_gpio && cfg->rts_gpio) {
+ s3c2410_gpio_cfgpin(cfg->cts_gpio, cfg->cts_cfg);
+ s3c2410_gpio_cfgpin(cfg->rts_gpio, cfg->rts_cfg);
+ }
+ }
+
+ s3c24xx_init_uarts(cc9m2443_uartcfgs, ARRAY_SIZE(cc9m2443_uartcfgs));
+}
+
+/*
+ * These are the NAND-timing parameters
+ * @TODO: Set the correct timing parameters for the NAND-device
+ */
+static struct s3c2410_platform_nand cc9m2443_nand_info = {
+ .tacls = 20,
+ .twrph0 = 50,
+ .twrph1 = 20,
+ .nr_sets = 1,
+ .sets = NULL,
+};
+
+/*
+ * Platform data for the I2C-bus driver
+ * @XXX: Verify the correct bus clocks for this device
+ */
+#if !defined(CONFIG_I2C_S3C2410_ADAPTER_NR)
+#define CONFIG_I2C_S3C2410_ADAPTER_NR -1
+#endif
+static struct s3c2410_platform_i2c cc9m2443_i2c_info = {
+ .flags = 0,
+ .bus_freq = 400 * 1000,
+ .max_freq = 500 * 1000,
+ .bus_num = CONFIG_I2C_S3C2410_ADAPTER_NR,
+};
+
+/* SPI devices */
+static struct spi_board_info spi_devices[] __initdata = {
+#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
+ {
+ .modalias = "spidev",
+ .max_speed_hz = 500000,
+ .bus_num = 0,
+ .chip_select = 0,
+ },
+#endif
+};
+
+static void __init cc9m2443_machine_init(void)
+{
+ printk("Starting Linux on ARM ConnectCore 9M 2443\n");
+
+ s3c_device_nand.dev.platform_data = &cc9m2443_nand_info;
+
+ s3c_device_i2c.dev.platform_data = &cc9m2443_i2c_info;
+
+ /* Enable the pullup for the write protect GPIO of the SD-port */
+ s3c2410_gpio_pullup(S3C2410_GPG9, 0x0);
+
+ /* For the bus detection we MUST activate the pull-down */
+ s3c2443_gpio_extpull(S3C2410_GPF5, 1);
+
+ /* Though this call the probes of the different drivers will be called */
+ platform_add_devices(cc9m2443_devices, ARRAY_SIZE(cc9m2443_devices));
+
+ /*
+ * Configure the IO for the external IRQ of the RTC
+ * @XXX: This is probably not the best place for configuring the trigget-type,
+ * but the RTC-driver doesn't touch this configuration (hope)
+ * (Luis Galdos)
+ */
+ s3c2443_gpio_cfgpin(CC9M2443_RTC_IRQ_GPIO, CC9M2443_RTC_IRQ_CFG);
+ set_irq_type(CC9M2443_RTC_IRQ, IRQF_TRIGGER_FALLING);
+
+ /* The first argument is the bus or adapter number to attach the devices */
+ i2c_register_board_info(CONFIG_I2C_S3C2410_ADAPTER_NR,
+ cc9m2443_i2c_devices,
+ ARRAY_SIZE(cc9m2443_i2c_devices));
+
+ /* SPI devices */
+ spi_register_board_info(spi_devices, ARRAY_SIZE(spi_devices));
+
+ /* Start the platform dependent part of the USB host controller */
+#if defined(CONFIG_MACH_CC9M2443JS_USB)
+ cc9m2443js_usb_init();
+#endif
+
+ /* Call the function for enabling the PM support */
+#if defined(CONFIG_MACH_CC9M2443JS_PM)
+ cc9m2443js_pm_init();
+#endif
+}
+
+MACHINE_START(CC9M2443JS, "ConnectCore 9M 2443 on a JSCC9M2443 Devboard")
+ .phys_io = S3C2410_PA_UART,
+ .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
+ .boot_params = S3C2410_SDRAM_PA + 0x100,
+ .init_irq = s3c24xx_init_irq,
+ .map_io = cc9m2443_map_io,
+ .init_machine = cc9m2443_machine_init,
+ .timer = &s3c24xx_timer,
+MACHINE_END
diff --git a/arch/arm/mach-s3c2443/mach-ccw9m2443js.c b/arch/arm/mach-s3c2443/mach-ccw9m2443js.c
new file mode 100644
index 000000000000..a02f098f52a6
--- /dev/null
+++ b/arch/arm/mach-s3c2443/mach-ccw9m2443js.c
@@ -0,0 +1,774 @@
+/* -*- linux-c -*-
+ *
+ * linux/arch/arm/mach-s3c2443/mach-cc9m2443js.c
+ *
+ * Copyright (c) 2007 Simtec Electronics
+ * Copyright (c) 2008 Digi International Spain
+ * Ben Dooks <ben@simtec.co.uk>
+ * Miriam Ruiz <Miriam.Ruiz@digi.com>
+ * Luis Galdos <luis.galdos@digi.com>
+ *
+ * http://www.fluff.org/ben/smdk2443/
+ * http://www.digi.com/products/embeddedsolutions/connectcore9m.jsp
+ *
+ * Thanks to Samsung for the loan of an SMDK2443
+ *
+ * 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/i2c.h>
+#include <linux/dma-mapping.h>
+#include <linux/smc911x.h>
+#include <linux/crc32.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+#include <asm/delay.h>
+
+#include <plat/regs-serial.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-gpioj.h>
+#include <mach/regs-lcd.h>
+#include <mach/regs-s3c2443-clock.h>
+
+#include <mach/idle.h>
+#include <mach/fb.h>
+#include <asm/plat-s3c24xx/spi.h>
+#include <mach/gpio.h>
+#include <linux/spi/spi.h>
+
+#include <plat/s3c2410.h>
+#include <plat/s3c2440.h>
+#include <plat/clock.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+#include <plat/hsmmc.h>
+#include <plat/ts.h>
+#include <plat/udc.h>
+#include <plat/pcmcia.h>
+#include <mach/irqs.h>
+#include <mach/regs-irq.h>
+#include <plat/irq.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <plat/common-smdk.h>
+#include <asm/plat-s3c24xx/mci.h>
+#include <asm/plat-s3c/nand.h>
+#include <asm/plat-s3c/iic.h>
+
+#include "displays/displays.h"
+#include "cc9m2443js-pm.h"
+#include "pipermain.h"
+#include "piper-ccw9m2443.h"
+
+
+/*
+ * IMPORTANT: We are using the Ethernet-driver of the Blackfin-project because:
+ * - The driver from the Vanilla-kernel is not working out of the box
+ * - After some modifications (for accessing to the FIFO correctly) the driver
+ * has some performance-problems (lost some packets)
+ * - The driver doesn't use the new NAPI for reading from the controller
+ * (Luis Galdos)
+ */
+static struct resource smsc911x_device_resources[] = {
+ [0] = {
+ .name = "smsc911x-memory",
+ .start = S3C2410_CS5,
+ .end = S3C2410_CS5 + SZ_1M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_EINT9,
+ .end = IRQ_EINT9,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct smc911x_platdata cc9m2443_smsc9118 = {
+ .flags = SMC911X_USE_16BIT,
+ .irq_flags = IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+ .irq_polarity = 0,
+};
+
+static struct platform_device smsc911x_device = {
+ .name = "smsc911x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(smsc911x_device_resources),
+ .resource = smsc911x_device_resources,
+ .dev = {
+ .platform_data = &cc9m2443_smsc9118,
+ }
+};
+
+/* @XXX: The macro will be used for external board-devices under cc9m2443-custom.h */
+#if !defined(CC9M2443_I2C_BASE_BOARD_DEVICES)
+#define CC9M2443_I2C_BASE_BOARD_DEVICES
+/*#define CC9M2443_I2C_BASE_BOARD_DEVICES \
+ { \
+ .driver_name = "rtc-ds1307", \
+ .addr = 0x68, \
+ .type = "ds1337", \
+ } \
+#endif */
+#endif
+
+/*
+ * This is the external IRQ for the RTC. Since the GPIO for the RTC-IRQ is not
+ * available on the connect of the module, the GPIO can be used only with the RTC.
+ */
+#define CC9M2443_RTC_IRQ (IRQ_EINT7)
+#define CC9M2443_RTC_IRQ_GPIO (S3C2410_GPF7)
+#define CC9M2443_RTC_IRQ_CFG (S3C2410_GPF7_EINT7)
+
+/* I2C devices */
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+#include <linux/i2c/pca953x.h>
+static struct pca953x_platform_data pca9554_data = {
+ .gpio_base = 147,
+};
+#endif
+
+static struct i2c_board_info cc9m2443_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("ds1337", 0x68),
+ .irq = CC9M2443_RTC_IRQ
+ },
+#if defined(CONFIG_GPIO_PCA953X) || defined(CONFIG_GPIO_PCA953X_MODULE)
+ {
+ I2C_BOARD_INFO("pca9554", 0x20),
+ .platform_data = &pca9554_data,
+ },
+#endif
+ CC9M2443_I2C_BASE_BOARD_DEVICES
+};
+
+static struct map_desc cc9m2443_iodesc[] __initdata = {
+ /* ISA IO Space map (memory space selected by A24) */
+ {
+ .virtual = (u32)S3C24XX_VA_ISA_WORD,
+ .pfn = __phys_to_pfn(S3C2410_CS2),
+ .length = 0x10000,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (u32)S3C24XX_VA_ISA_WORD + 0x10000,
+ .pfn = __phys_to_pfn(S3C2410_CS2 + (1<<24)),
+ .length = SZ_4M,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (u32)S3C24XX_VA_ISA_BYTE,
+ .pfn = __phys_to_pfn(S3C2410_CS2),
+ .length = 0x10000,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (u32)S3C24XX_VA_ISA_BYTE + 0x10000,
+ .pfn = __phys_to_pfn(S3C2410_CS2 + (1<<24)),
+ .length = SZ_4M,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (u32)CC9M2443_VA_LAN,
+ .pfn = __phys_to_pfn(CC9M2443_PA_LAN),
+ .length = CC9M2443_SZ_LAN,
+ .type = MT_DEVICE,
+ }
+};
+
+
+/* Macros for the default port configuration on the CC9M2443 */
+#define CC9M2443_PORT_UCON (S3C2412_UCON_PCLK | \
+ S3C2410_UCON_RXIRQMODE | \
+ S3C2410_UCON_TXIRQMODE | \
+ S3C2410_UCON_RXFIFO_TOI | \
+ S3C2443_UCON_RXERR_IRQEN | \
+ S3C2410_UCON_TXILEVEL | \
+ S3C2410_UCON_RXILEVEL)
+#define CC9M2443_PORT_UFCON (S3C2410_UFCON_FIFOMODE | \
+ S3C2440_UFCON_RXTRIG8 | \
+ S3C2440_UFCON_TXTRIG16)
+#define CC9M2443_PORT_LCON (S3C2410_LCON_CS8)
+
+
+static struct s3c2410_uartcfg cc9m2443_uartcfgs[] __initdata = {
+#ifdef CONFIG_SERIAL_S3C2410_ENABLE_PORTA
+ {
+ .hwport = 0,
+ .flags = 0,
+ .ucon = CC9M2443_PORT_UCON,
+ .ulcon = S3C2410_LCON_CS8,
+ .ufcon = CC9M2443_PORT_UFCON,
+ .rx_gpio = S3C2410_GPH1,
+ .rx_cfg = S3C2443_GPH1_RXD0,
+ .tx_gpio = S3C2410_GPH0,
+ .tx_cfg = S3C2443_GPH0_TXD0,
+#if defined(CONFIG_SERIAL_S3C2410_PORTA_HWCTRL)
+ .rts_gpio = S3C2410_GPH9,
+ .rts_cfg = S3C2443_GPH9_nRTS0,
+ .cts_gpio = S3C2410_GPH8,
+ .cts_cfg = S3C2443_GPH8_nCTS0,
+#endif /* CONFIG_SERIAL_S3C2410_PORTA_HWCTRL */
+ },
+#endif /* CONFIG_SERIAL_S3C2410_ENABLE_PORTA */
+#ifdef CONFIG_SERIAL_S3C2410_ENABLE_PORTB
+ {
+ .hwport = 1,
+ .flags = 0,
+ .ucon = CC9M2443_PORT_UCON,
+ .ulcon = CC9M2443_PORT_LCON,
+ .ufcon = CC9M2443_PORT_UFCON,
+ .rx_gpio = S3C2410_GPH3,
+ .rx_cfg = S3C2443_GPH3_RXD1,
+ .tx_gpio = S3C2410_GPH2,
+ .tx_cfg = S3C2443_GPH2_TXD1,
+#if defined(CONFIG_SERIAL_S3C2410_PORTB_HWCTRL)
+ .rts_gpio = S3C2443_GPH11,
+ .rts_cfg = S3C2443_GPH11_nRTS1,
+ .cts_gpio = S3C2410_GPH10,
+ .cts_cfg = S3C2443_GPH10_nCTS1,
+#endif /* CONFIG_SERIAL_S3C2410_PORTB_HWCTRL */
+ },
+#endif /* CONFIG_SERIAL_S3C2410_ENABLE_PORTB */
+#ifdef CONFIG_SERIAL_S3C2410_ENABLE_PORTC
+ {
+ .hwport = 2,
+ .flags = 0,
+ .ucon = CC9M2443_PORT_UCON,
+ .ulcon = CC9M2443_PORT_LCON,
+ .ufcon = CC9M2443_PORT_UFCON,
+ .rx_gpio = S3C2410_GPH5 ,
+ .rx_cfg = S3C2443_GPH5_RXD2,
+ .tx_gpio = S3C2410_GPH4 ,
+ .tx_cfg = S3C2443_GPH4_TXD2,
+#if defined(CONFIG_SERIAL_S3C2410_PORTC_HWCTRL)
+ .rts_gpio = S3C2410_GPH6,
+ .rts_cfg = S3C2443_GPH6_nRTS2,
+ .cts_gpio = S3C2410_GPH7,
+ .cts_cfg = S3C2443_GPH7_nCTS2,
+#endif /* CONFIG_SERIAL_S3C2410_PORTC_HWCTRL */
+
+ },
+#endif /* CONFIG_SERIAL_S3C2410_ENABLE_PORTC */
+#ifdef CONFIG_SERIAL_S3C2410_ENABLE_PORTD
+ {
+ .hwport = 3,
+ .flags = 0,
+ .ucon = CC9M2443_PORT_UCON,
+ .ulcon = CC9M2443_PORT_LCON,
+ .ufcon = CC9M2443_PORT_UFCON,
+ .rx_gpio = S3C2410_GPH7,
+ .rx_cfg = S3C2443_GPH7_RXD3,
+ .tx_gpio = S3C2410_GPH6,
+ .tx_cfg = S3C2443_GPH6_TXD3,
+ }
+#endif /* CONFIG_SERIAL_S3C2410_ENABLE_PORTD */
+};
+
+/* This is required for the LCD-display */
+static struct s3c2443fb_mach_info cc9m2443fb_mach = {
+ .displays = display_list,
+ .num_displays = ARRAY_SIZE(display_list),
+
+ /* Set the GPIO-configuration */
+ .gpccon = 0xaaa002a8,
+ .gpccon_mask = 0xfff003fc,
+
+ .gpcup = 0xfff003fc,
+ .gpcup_mask = 0xfff003fc,
+
+ .gpdcon = 0xaaa0aaa0,
+ .gpdcon_mask = 0xfff0fff0,
+
+ .gpdup = 0xfff0fff0,
+ .gpdup_mask = 0xfff0fff0,
+
+};
+
+/* This is for the LCD platform device */
+static struct resource s3c2443_tft_resource[] = {
+ [0] = {
+ .start = S3C24XX_PA_TFTLCD,
+ .end = S3C24XX_PA_TFTLCD + S3C24XX_SZ_LCD - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ /* No irq for s3c2443 tft driver */
+};
+
+static u64 s3c2443_lcd_dmamask = 0xffffffffUL;
+
+struct platform_device s3c_device_tft_lcd = {
+ .name = "s3c2443fb-tft",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(s3c2443_tft_resource),
+ .resource = s3c2443_tft_resource,
+ .dev = {
+ .dma_mask = &s3c2443_lcd_dmamask,
+ .coherent_dma_mask = 0xffffffffUL,
+ .platform_data = &cc9m2443fb_mach,
+ }
+};
+
+/*
+ * SDIO device for the CC9M2443JS
+ * The correct driver name is "s3c2440-sdi" and NOT "s3c2410-sdi"
+ */
+static struct resource cc9m2443_sdi_resource[] = {
+ [0] = {
+ .start = S3C2410_PA_SDI,
+ .end = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_SDI,
+ .end = IRQ_SDI,
+ .flags = IORESOURCE_IRQ,
+ }
+
+};
+
+static struct s3c24xx_mci_pdata cc9m2443_sdi_pdata = {
+ .gpio_detect = S3C2410_GPF1,
+ .gpio_wprotect = S3C2410_GPG9,
+ .dma_enable = 1,
+};
+
+struct platform_device cc9m2443_device_sdi = {
+ .name = "s3c2440-sdi",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(cc9m2443_sdi_resource),
+ .resource = cc9m2443_sdi_resource,
+ .dev = {
+ .platform_data = &cc9m2443_sdi_pdata,
+ }
+};
+
+/* High-speed MMC/SD */
+static struct resource cc9m2443_hsmmc_resource[] = {
+ [0] = {
+ .start = S3C2443_PA_HSMMC,
+ .end = S3C2443_PA_HSMMC + S3C2443_SZ_HSMMC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3C2443_HSMMC,
+ .end = IRQ_S3C2443_HSMMC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 cc9m2443_device_hsmmc_dmamask = 0xffffffffUL;
+
+#define HOST_CAPS (MMC_CAP_4_BIT_DATA | \
+ MMC_CAP_8_BIT_DATA | \
+ MMC_CAP_MMC_HIGHSPEED | \
+ MMC_CAP_SD_HIGHSPEED)
+
+struct s3c_hsmmc_cfg cc9m2443_hsmmc_platform = {
+ .hwport = 0,
+ .host_caps = HOST_CAPS,
+ .ctrl3[0] = 0x00800080,
+ .ctrl3[1] = 0x00800080,
+ .clk_name[0] = "hclk", /* 1st clock HCLK */
+ .clk_name[1] = "hsmmc-div", /* 2nd clock hsmmc-epll */
+ .clk_name[2] = "hsmmc-ext", /* 3rd clock external */
+ .gpio_wprotect = S3C2443_GPJ15,
+ .gpio_detect = S3C2443_GPJ14,
+ .gpio_led = S3C2443_GPJ13,
+};
+
+struct platform_device cc9m2443_device_hsmmc = {
+ .name = "s3c-hsmmc",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(cc9m2443_hsmmc_resource),
+ .resource = cc9m2443_hsmmc_resource,
+ .dev = {
+ .dma_mask = &cc9m2443_device_hsmmc_dmamask,
+ .coherent_dma_mask = 0xffffffffUL,
+ .platform_data = &cc9m2443_hsmmc_platform,
+ }
+};
+
+/* Touch device */
+static struct resource cc9m2443_ts_resource[] = {
+ [0] = {
+ .start = S3C2410_PA_ADC,
+ .end = S3C2410_PA_ADC + S3C24XX_SZ_ADC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_ADC,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_TC,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+/* @XXX: Set the correct values */
+static struct s3c_ts_mach_info cc9m2443_ts_info = {
+ .delay = 1,
+ .presc = 255,
+ .oversampling_shift = 1,
+ .xmin = 0x3ff,
+ .ymin = 0x3ff,
+ .trigger_ms = 1,
+ .probes = 9,
+};
+
+struct platform_device cc9m2443_device_ts = {
+ .name = "s3c24xx-ts",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(cc9m2443_ts_resource),
+ .resource = cc9m2443_ts_resource,
+ .dev = {
+ .platform_data = &cc9m2443_ts_info,
+ }
+};
+
+/* This is the High Speed SPI-master of the S3C2443 */
+static struct s3c2443_spi_info cc9m2443_hsspi_info = {
+ .bus_num = 0,
+ .num_chipselect = 1,
+ .input_clk = S3C2443_HSSPI_INCLK_PCLK,
+ .miso = {
+ .nr = S3C2410_GPE11,
+ .cfg = S3C2410_GPE11_SPIMISO0,
+ },
+ .mosi = {
+ .nr = S3C2410_GPE12,
+ .cfg = S3C2410_GPE12_SPIMOSI0,
+ },
+ .clk = {
+ .nr = S3C2410_GPE13,
+ .cfg = S3C2410_GPE13_SPICLK0,
+ },
+ .cs[0] = {
+ .nr = S3C2443_GPL13,
+ .cfg = S3C2443_GPL13_nSS0,
+ }
+};
+
+static struct resource cc9m2443_hsspi_resources[] = {
+ {
+ .start = S3C2443_PA_HSSPI,
+ .end = S3C2443_PA_HSSPI + S3C2443_SZ_HSSPI - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_SPI0,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device cc9m2443_device_hsspi = {
+ .name = "spi-s3c2443",
+ .id = 0,
+ .resource = cc9m2443_hsspi_resources,
+ .num_resources = ARRAY_SIZE(cc9m2443_hsspi_resources),
+ .dev = {
+ .platform_data = &cc9m2443_hsspi_info,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ }
+};
+
+/* This is the USB-device of the S3C2443 */
+static struct resource cc9m2443_udc_resources[] = {
+ [0] = {
+ .start = S3C2443_PA_USBDEV,
+ .end = S3C2443_PA_USBDEV+ S3C2443_SZ_USBDEV - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_USBD,
+ .end = IRQ_USBD,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct s3c2410_udc_mach_info cc9m2443_udc_pdata = {
+ .vbus_pin = S3C2410_GPF5,
+ .vbus_pin_inverted = 0,
+ .udc_command = NULL,
+};
+
+static struct platform_device cc9m2443_device_udc = {
+ .name = "s3c2443_udc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(cc9m2443_udc_resources),
+ .resource = cc9m2443_udc_resources,
+ .dev = {
+ .platform_data = &cc9m2443_udc_pdata,
+ }
+};
+
+static struct resource s3c2443_ide_resource[] = {
+ [0] = {
+ .start = S3C2443_PA_CFATA,
+ .end = S3C2443_PA_CFATA + S3C2443_SZ_CFATA,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3C2443_CFCON,
+ .end = IRQ_S3C2443_CFCON,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device s3c443_device_ide = {
+ .name = "s3c2443-ide",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(s3c2443_ide_resource),
+ .resource = s3c2443_ide_resource,
+};
+
+static struct resource s3c2443_pcmcia_resource[] = {
+ [0] = {
+ .start = S3C2443_PA_CFATA,
+ .end = S3C2443_PA_CFATA + S3C2443_SZ_CFATA,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3C2443_CFCON,
+ .end = IRQ_S3C2443_CFCON,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct s3c2443_pcmcia_pdata cc9m2443js_pcmcia_pdata = {
+ .gpio_detect = S3C2410_GPG10,
+};
+
+struct platform_device s3c443_device_pcmcia = {
+ .name = "s3c2443-pcmcia",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(s3c2443_pcmcia_resource),
+ .resource = s3c2443_pcmcia_resource,
+ .dev = {
+ .platform_data = &cc9m2443js_pcmcia_pdata,
+ }
+};
+
+static struct resource s3c2443_adc_resources[] = {
+ [0] = {
+ .name = "s3c2443-adc",
+ .start = S3C2410_PA_ADC,
+ .end = S3C2410_PA_ADC + S3C24XX_SZ_ADC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device s3c2443_device_adc = {
+ .name = "s3c2443-adc",
+ .id = -1,
+ .resource = s3c2443_adc_resources,
+ .num_resources = ARRAY_SIZE(s3c2443_adc_resources),
+ .dev = {
+ .platform_data = &cc9m2443_ts_info, /* shared between touch and adc*/
+ }
+};
+
+/* Platform devices for the CC9M2443 */
+static struct platform_device *cc9m2443_devices[] __initdata = {
+ &s3c_device_usb,
+ &s3c_device_tft_lcd,
+ &s3c_device_wdt,
+ &s3c_device_i2c,
+ &s3c_device_nand,
+ &smsc911x_device,
+ &cc9m2443_device_sdi,
+ &cc9m2443_device_hsmmc,
+ &cc9m2443_device_ts,
+ &cc9m2443_device_hsspi,
+ &cc9m2443_device_udc,
+ &s3c443_device_ide,
+ &s3c443_device_pcmcia,
+ &s3c2443_device_adc,
+};
+
+static void __init cc9m2443_map_io(void)
+{
+ struct s3c2410_uartcfg *cfg;
+ int cnt;
+
+ s3c24xx_init_io(cc9m2443_iodesc, ARRAY_SIZE(cc9m2443_iodesc));
+ s3c24xx_init_clocks(12000000);
+
+
+ /*
+ * Init the GPIOs for the UART-ports
+ * @XXX: We can probably move this part to the UART-driver, but for this
+ * moment let's initialize the GPIOs at this place
+ * Luis Galdos
+ */
+ for (cnt = 0; cnt < ARRAY_SIZE(cc9m2443_uartcfgs); cnt++) {
+
+ cfg = &cc9m2443_uartcfgs[cnt];
+
+ s3c2410_gpio_cfgpin(cfg->rx_gpio, cfg->rx_cfg);
+ s3c2410_gpio_cfgpin(cfg->tx_gpio, cfg->tx_cfg);
+
+ if (cfg->cts_gpio && cfg->rts_gpio) {
+ s3c2410_gpio_cfgpin(cfg->cts_gpio, cfg->cts_cfg);
+ s3c2410_gpio_cfgpin(cfg->rts_gpio, cfg->rts_cfg);
+ }
+ }
+
+ s3c24xx_init_uarts(cc9m2443_uartcfgs, ARRAY_SIZE(cc9m2443_uartcfgs));
+}
+
+/*
+ * These are the NAND-timing parameters
+ * @TODO: Set the correct timing parameters for the NAND-device
+ */
+static struct s3c2410_platform_nand cc9m2443_nand_info = {
+ .tacls = 20,
+ .twrph0 = 50,
+ .twrph1 = 20,
+ .nr_sets = 1,
+ .sets = NULL,
+};
+
+/*
+ * Platform data for the I2C-bus driver
+ * @XXX: Verify the correct bus clocks for this device
+ */
+#if !defined(CONFIG_I2C_S3C2410_ADAPTER_NR)
+#define CONFIG_I2C_S3C2410_ADAPTER_NR -1
+#endif
+static struct s3c2410_platform_i2c cc9m2443_i2c_info = {
+ .flags = 0,
+ .bus_freq = 400 * 1000,
+ .max_freq = 500 * 1000,
+ .bus_num = CONFIG_I2C_S3C2410_ADAPTER_NR,
+};
+
+/* SPI devices */
+static struct spi_board_info spi_devices[] __initdata = {
+#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
+ {
+ .modalias = "spidev",
+ .max_speed_hz = 500000,
+ .bus_num = 0,
+ .chip_select = 0,
+ },
+#endif
+};
+
+#if defined(CONFIG_DIGI_PIPER_WIFI)
+#if !defined(CONFIG_PIPER_STATUS_LED_GPIO)
+#define CONFIG_PIPER_STATUS_LED_GPIO -1
+#endif
+static struct piper_pdata ccw9m2443_piper_pdata = {
+ .rst_gpio = 19,
+ .irq_gpio = 90,
+ .status_led_gpio = CONFIG_PIPER_STATUS_LED_GPIO,
+ .i2c_adapter_num = CONFIG_I2C_S3C2410_ADAPTER_NR,
+};
+
+static void __init ccw9m2443js_fixup(struct machine_desc *desc,
+ struct tag *tags, char **cmdline,
+ struct meminfo *mi)
+{
+ unsigned char *mac = phys_to_virt(desc->boot_params) + 0xf00;
+ wcd_data_t *pwcal;
+ u32 crc;
+
+ /* 8 bytes after the mac address, its located the calibration data */
+ pwcal = (wcd_data_t *)(mac + 8);
+
+ memcpy(&ccw9m2443_piper_pdata.macaddr[0], mac, 6);
+
+ if (!strncmp(pwcal->header.magic_string, WCD_MAGIC,
+ sizeof(pwcal->header.magic_string))) {
+ /* check version */
+ if (((pwcal->header.ver_major >= '1') && (pwcal->header.ver_major <= '9')) &&
+ ((pwcal->header.ver_minor >= '0') && (pwcal->header.ver_minor <= '9'))) {
+ crc = ~crc32_le(~0, (unsigned char const *)pwcal->cal_curves_bg,
+ pwcal->header.wcd_len);
+ if (crc == pwcal->header.wcd_crc) {
+ memcpy(&ccw9m2443_piper_pdata.wcd, pwcal, sizeof(wcd_data_t));
+ return;
+ }
+ }
+ }
+
+ memset(&ccw9m2443_piper_pdata.wcd, 0, sizeof(wcd_data_t));
+}
+#endif
+
+static void __init cc9m2443_machine_init(void)
+{
+ printk("Starting Linux on ARM ConnectCore 9M 2443\n");
+
+ s3c_device_nand.dev.platform_data = &cc9m2443_nand_info;
+
+ s3c_device_i2c.dev.platform_data = &cc9m2443_i2c_info;
+
+ /* Enable the pullup for the write protect GPIO of the SD-port */
+ s3c2410_gpio_pullup(S3C2410_GPG9, 0x0);
+
+ /* For the bus detection we MUST activate the pull-down */
+ s3c2443_gpio_extpull(S3C2410_GPF5, 1);
+
+ /* Though this call the probes of the different drivers will be called */
+ platform_add_devices(cc9m2443_devices, ARRAY_SIZE(cc9m2443_devices));
+
+ /*
+ * Configure the IO for the external IRQ of the RTC
+ * @XXX: This is probably not the best place for configuring the trigget-type,
+ * but the RTC-driver doesn't touch this configuration (hope)
+ * (Luis Galdos)
+ */
+ s3c2443_gpio_cfgpin(CC9M2443_RTC_IRQ_GPIO, CC9M2443_RTC_IRQ_CFG);
+ set_irq_type(CC9M2443_RTC_IRQ, IRQF_TRIGGER_FALLING);
+
+ /* The first argument is the bus or adapter number to attach the devices */
+ i2c_register_board_info(CONFIG_I2C_S3C2410_ADAPTER_NR,
+ cc9m2443_i2c_devices,
+ ARRAY_SIZE(cc9m2443_i2c_devices));
+
+ /* SPI devices */
+ spi_register_board_info(spi_devices, ARRAY_SIZE(spi_devices));
+
+
+#if defined(CONFIG_DIGI_PIPER_WIFI)
+ /* 802.11 */
+ add_device_ccw9m2443_piper(&ccw9m2443_piper_pdata);
+#endif
+
+ /* Call the function for enabling the PM support */
+#if defined(CONFIG_MACH_CC9M2443JS_PM)
+ cc9m2443js_pm_init();
+#endif
+}
+
+MACHINE_START(CCW9M2443JS, "ConnectCore Wi-9M 2443 on a JSCC9M2443 Devboard")
+ .phys_io = S3C2410_PA_UART,
+ .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
+ .boot_params = S3C2410_SDRAM_PA + 0x100,
+#if defined(CONFIG_DIGI_PIPER_WIFI)
+ .fixup = ccw9m2443js_fixup,
+#endif
+ .init_irq = s3c24xx_init_irq,
+ .map_io = cc9m2443_map_io,
+ .init_machine = cc9m2443_machine_init,
+ .timer = &s3c24xx_timer,
+MACHINE_END
diff --git a/arch/arm/mach-s3c2443/piper-ccw9m2443.c b/arch/arm/mach-s3c2443/piper-ccw9m2443.c
new file mode 100644
index 000000000000..d740888f113b
--- /dev/null
+++ b/arch/arm/mach-s3c2443/piper-ccw9m2443.c
@@ -0,0 +1,429 @@
+/*
+ * arch/arm/mach-ns9xxx/cc9p9215_devices.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+#include <linux/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.
+ */
+
+
+#include <linux/clk.h>
+#include <linux/mtd/physmap.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+
+#include <mach/regs-s3c2443-mem.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airoha.h"
+
+#if defined(CONFIG_DIGI_PIPER_WIFI)
+/* Low level functions to access piper chip */
+static u32 read_reg(struct piper_priv *piperp, u8 reg)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&piperp->ac->reg_lock, flags);
+ val = ioread32(piperp->vbase + reg);
+ spin_unlock_irqrestore(&piperp->ac->reg_lock, flags);
+
+ return val;
+}
+
+static int write_reg(struct piper_priv *piperp, u8 reg, u32 val, reg_op_t op)
+{
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&piperp->ac->reg_lock, flags);
+ switch (op) {
+ case op_write:
+ iowrite16((u16)(val >> 16), piperp->vbase + reg + 2);
+ iowrite16((u16)val, piperp->vbase + reg);
+ break;
+ case op_or:
+ tmp = ioread32(piperp->vbase + reg) | val;
+ iowrite16((u16)(tmp >> 16), piperp->vbase + reg + 2);
+ iowrite16((u16)tmp, piperp->vbase + reg);
+ break;
+ case op_and:
+ tmp = ioread32(piperp->vbase + reg) & val;
+ iowrite16((u16)(tmp >> 16), piperp->vbase + reg + 2);
+ iowrite16((u16)tmp, piperp->vbase + reg);
+ break;
+ default:
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": Invalid write register operation (%d)\n", op);
+ WARN_ON(1);
+ break;
+ }
+ spin_unlock_irqrestore(&piperp->ac->reg_lock, flags);
+
+ return 0;
+}
+
+/*
+ * This macro waits for the AES busy bit to clear if we are writing to the
+ * AES FIFO.
+ */
+#define wait_for_aes_ready() while ((addr == BB_AES_FIFO) \
+ && ((ioread32(piperp->vbase + BB_RSSI) \
+ & BB_RSSI_EAS_FIFO_FULL) != 0)) { \
+ udelay(1); \
+ }
+
+static int write_fifo(struct piper_priv *piperp, u8 addr, u8 *buf, int len)
+{
+ int wordIndex, wordLength = len / sizeof(unsigned int);
+ bool loadingBeacon = (addr == BEACON_FIFO);
+ unsigned long flags;
+ u32 tmp;
+
+ spin_lock_irqsave(&piperp->ac->reg_lock, flags);
+
+ if (loadingBeacon) {
+ /*
+ * If we are loading a new beacon, then adjust the address to point
+ * to the data FIFO, and set the beacon enable bit which tells piper
+ * to put this data into the beacon buffer.
+ */
+ addr = BB_DATA_FIFO;
+ tmp = ioread32(piperp->vbase + BB_GENERAL_CTL) | BB_GENERAL_CTL_BEACON_EN;
+ iowrite16((u16)(tmp >> 16), piperp->vbase + BB_GENERAL_CTL + 2);
+ iowrite16((u16)tmp, piperp->vbase + BB_GENERAL_CTL);
+ }
+
+ if (((unsigned)(buf) & 0x3) == 0) {
+ /*
+ * We come here if the data is 32-bit aligned. We can dispense
+ * with memcpys
+ */
+ if (wordLength == 1) {
+ /*
+ * Only 1 word of data, so just one write.
+ */
+ unsigned int *word = (unsigned int *)buf;
+ tmp = cpu_to_be32(*word);
+
+ wait_for_aes_ready();
+
+ iowrite16((u16)(tmp >> 16), piperp->vbase + addr + 2);
+ iowrite16((u16)tmp, piperp->vbase + addr);
+ len -= 4;
+ } else {
+ /*
+ * More than one word of data, so set up a for loop.
+ */
+ for (wordIndex = 0; wordIndex < wordLength; wordIndex++) {
+ unsigned int *word = (unsigned int *)buf;
+
+ tmp = cpu_to_be32(word[wordIndex]);
+
+ wait_for_aes_ready();
+ iowrite16((u16)(tmp >> 16), piperp->vbase + addr + 2);
+ iowrite16((u16)tmp, piperp->vbase + addr);
+ len -= 4;
+ }
+ }
+ } else {
+ /*
+ * Ugh! Data is not 32-bit aligned. We have to memcpy it!
+ */
+ for (wordIndex = 0; wordIndex < wordLength; wordIndex++) {
+
+ memcpy(&tmp, &buf[wordIndex * sizeof(u32)], sizeof(u32));
+
+ tmp = cpu_to_be32(tmp);
+
+ wait_for_aes_ready();
+ iowrite16((u16)(tmp >> 16), piperp->vbase + addr + 2);
+ iowrite16((u16)tmp, piperp->vbase + addr);
+ len -= 4;
+ }
+ }
+
+ if (len) {
+ /*
+ * Double Ugh! There was left over data at the end. We have to write
+ * the leftover data into the upper bytes of the last word, making
+ * sure the unused bytes are set to zero.
+ */
+ memcpy(&tmp, &buf[wordLength * sizeof(u32)], sizeof(u32));
+ tmp = cpu_to_be32(tmp);
+ switch (len) {
+ case 1:
+ tmp &= 0xff000000;
+ break;
+ case 2:
+ tmp &= 0xffff0000;
+ break;
+ case 3:
+ tmp &= 0xffffff00;
+ break;
+ default:
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": len = %d at end of piper_write\n", len);
+ break;
+ }
+ wait_for_aes_ready();
+ iowrite16((u16)(tmp >> 16), piperp->vbase + addr + 2);
+ iowrite16((u16)tmp, piperp->vbase + addr);
+ }
+
+ if (loadingBeacon) {
+ /*
+ * If we just loaded a beacon, then don't forget to turn off the
+ * load beacon bit.
+ */
+ tmp = ioread32(piperp->vbase + BB_GENERAL_CTL) & ~BB_GENERAL_CTL_BEACON_EN;
+ iowrite16((u16)(tmp >> 16), piperp->vbase + BB_GENERAL_CTL + 2);
+ iowrite16((u16)tmp, piperp->vbase + BB_GENERAL_CTL);
+
+ piperp->beacon.loaded = true;
+ }
+
+ spin_unlock_irqrestore(&piperp->ac->reg_lock, flags);
+
+ return 0;
+}
+
+/*
+ * This routine waits for the empty flag to clear when we are reading from
+ * the AES FIFO.
+ */
+#define wait_for_aes_not_empty() \
+ while ((addr == BB_AES_FIFO) \
+ && ((ioread32(piperp->vbase + BB_RSSI) & BB_RSSI_EAS_FIFO_EMPTY) != 0)) { \
+ udelay(1); \
+ if (--timeout == 0) { \
+ timeout = 10000; \
+ } \
+ }
+
+
+static int read_fifo(struct piper_priv *piperp, u8 addr, u8 *buf, int len)
+{
+ int wordIndex;
+ unsigned long flags;
+ int timeout = 10000;
+
+ spin_lock_irqsave(&piperp->ac->reg_lock, flags);
+
+ /*
+ * We can only read 32-bit words, so round the length up to an even multiple of
+ * 4 if necessary.
+ */
+ len += 3;
+ len &= 0xfffffc;
+
+ if ((len == 4) && ((((unsigned)buf) & 0x3) == 0)) {
+ unsigned *word = (unsigned *)buf;
+
+ wait_for_aes_not_empty();
+ *word = be32_to_cpu(ioread32(piperp->vbase + addr));
+ } else if ((((unsigned)buf) & 0x3) == 0) {
+ unsigned *word = (unsigned *)buf;
+
+ for (wordIndex = 0; wordIndex < (len / sizeof(unsigned)); wordIndex++) {
+ wait_for_aes_not_empty();
+ word[wordIndex] = be32_to_cpu(ioread32(piperp->vbase + addr));
+ }
+ } else {
+ /*
+ * If we come here, then the buffer is not aligned and we have to
+ * memcpy the data.
+ */
+ for (wordIndex = 0; wordIndex < (len / sizeof(unsigned)); wordIndex++) {
+ unsigned word;
+
+ wait_for_aes_not_empty();
+ word = be32_to_cpu(ioread32(piperp->vbase + addr));
+ memcpy(&buf[wordIndex * sizeof(unsigned)], &word, sizeof(word));
+ }
+ }
+ spin_unlock_irqrestore(&piperp->ac->reg_lock, flags);
+
+ return 0;
+}
+
+/* Initialize piper hardware, mac and dsp firmwares and mac address */
+static int piper_init_chip_hw(struct piper_priv *piperp)
+{
+ int ret;
+
+ piper_load_mac_firmware(piperp);
+ piper_load_dsp_firmware(piperp);
+
+ ret = piper_spike_suppression(piperp, true);
+ if (ret) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": spike suppresion error\n");
+ return ret;
+ }
+ piper_reset_mac(piperp);
+ piper_set_macaddr(piperp);
+
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xffffffff, op_write);
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);
+
+ /* Enable host interface output control, to ensure a correct
+ * level on the interrupt line */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x04000000, op_or);
+
+ return ret;
+}
+
+static void ccw9m2443_piper_set_led(struct piper_priv *piperp, enum wireless_led led, int val)
+{
+ if(led == STATUS_LED)
+ gpio_set_value(piperp->pdata->status_led_gpio, (val == 0));
+}
+
+static void ccw9m2443_piper_reset(struct piper_priv *piperp, int reset)
+{
+ gpio_set_value(piperp->pdata->rst_gpio, !reset);
+}
+
+#define LINIX_CONFIG_WIFI_CS
+#ifdef LINIX_CONFIG_WIFI_CS
+static int ccw9m2443_wifi_cs_config(void)
+{
+ void __iomem *ssmc;
+ u32 reg;
+
+ /* Configure the memory controller (CS3) with the appropriate settings */
+ if ((ssmc = ioremap(S3C2443_PA_SSMC, 0x100)) == NULL)
+ return -EBUSY;
+
+ writel(10, ssmc + S3C2443_SSMC_SMBIDCYR4); /* Idle cycle ctrl */
+ writel(14, ssmc + S3C2443_SSMC_SMBWSTWRR4); /* Write Wait State ctrl */
+ writel(3, ssmc + S3C2443_SSMC_SMBWSTOENR4); /* Output Enable Assertion Delay */
+ writel(3, ssmc + S3C2443_SSMC_SMBWSTWENR4); /* Write Enable Assertion Delay */
+ writel(14 , ssmc + S3C2443_SSMC_SMBWSTRDR4); /* Read Wait State control */
+ /* Read Byte Lane Enable and configure memory width to 16 bit */
+ reg = (readl(ssmc + S3C2443_SSMC_SMBCR4) & ~(3 << 4)) | (1 << 4) | 0x1;
+ writel(reg, ssmc + S3C2443_SSMC_SMBCR4);
+
+ iounmap(ssmc);
+
+ return 0;
+}
+#else
+static int ccw9m2443_wifi_cs_config(void) {return 0;}
+#endif
+
+static int ccw9m2443_piper_init(struct piper_priv *piperp)
+{
+ ccw9m2443_piper_reset(piperp, 1);
+ mdelay(1);
+ ccw9m2443_piper_reset(piperp, 0);
+ mdelay(1);
+
+ ccw9m2443_wifi_cs_config();
+
+ /* Initialize functions to access register */
+ piperp->ac->wr_reg = write_reg;
+ piperp->ac->rd_reg = read_reg;
+ piperp->ac->wr_fifo = write_fifo;
+ piperp->ac->rd_fifo = read_fifo;
+
+ mdelay(1);
+
+ return piper_init_chip_hw(piperp);
+}
+
+static void ccw9m2443_piper_early_resume(struct piper_priv *piperp)
+{
+ /*
+ * Reconfigure the CS line and the interrupt line. This should be
+ * done by the PM subsystem but its not done at the moment. In future
+ * when it is solved, this lines could be removed.
+ */
+ ccw9m2443_wifi_cs_config();
+ set_irq_type(piperp->irq, IRQ_TYPE_LEVEL_HIGH);
+}
+
+static int ccw9m2443_piper_late_init(struct piper_priv *piperp)
+{
+ return 0;
+}
+
+static struct resource piper_resources[] = {
+ {
+ .start = S3C2410_CS4,
+ .end = S3C2410_CS4 + 0x100,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_EINT15,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+/* describes the device */
+static struct platform_device piper_device = {
+ .id = 0,
+ .name = PIPER_DRIVER_NAME,
+ .num_resources = ARRAY_SIZE(piper_resources),
+ .resource = piper_resources,
+};
+
+void __init add_device_ccw9m2443_piper(struct piper_pdata *pdata)
+{
+ int ret;
+
+ if (!pdata)
+ return;
+
+ if (pdata->rst_gpio >= 0) {
+ ret = gpio_request(pdata->rst_gpio, PIPER_DRIVER_NAME "-reset-gpio");
+ if (ret != 0)
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": failed to request reset gpio %d\n", pdata->rst_gpio);
+ else {
+ /* Configure reset line and hold the chip in reset */
+ gpio_direction_output(pdata->rst_gpio, 0);
+ pdata->reset = ccw9m2443_piper_reset;
+ }
+ }
+
+ if (pdata->irq_gpio >= 0) {
+ ret = gpio_request(pdata->irq_gpio, PIPER_DRIVER_NAME "-irq-gpio");
+ if (ret != 0)
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": failed to request irq gpio %d\n", pdata->irq_gpio);
+ }
+
+ if (pdata->status_led_gpio >= 0) {
+ ret = gpio_request(pdata->status_led_gpio, PIPER_DRIVER_NAME "-stat-led");
+ if (ret != 0)
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": failed to request status led gpio %d\n",
+ pdata->status_led_gpio);
+ else {
+ gpio_direction_output(pdata->status_led_gpio, 0);
+ pdata->set_led = ccw9m2443_piper_set_led;
+ }
+ }
+
+ pdata->rf_transceiver = RF_AIROHA_7230;
+ pdata->init = ccw9m2443_piper_init;
+ pdata->late_init = ccw9m2443_piper_late_init;
+ pdata->early_resume = ccw9m2443_piper_early_resume;
+
+ piper_device.dev.platform_data = pdata;
+ platform_device_register(&piper_device);
+}
+
+#else
+void __init add_device_ccw9m2443_piper(struct piper_pdata *pdata) {}
+#endif
+
diff --git a/arch/arm/mach-s3c2443/piper-ccw9m2443.h b/arch/arm/mach-s3c2443/piper-ccw9m2443.h
new file mode 100644
index 000000000000..9baeab1dee23
--- /dev/null
+++ b/arch/arm/mach-s3c2443/piper-ccw9m2443.h
@@ -0,0 +1,22 @@
+/* -*- linux-c -*-
+ *
+ * linux/arch/arm/mach-s3c2443/cc9m2443js-pm.h
+ *
+ * Copyright (c) 2009 Digi International Spain
+ *
+ * http://www.digi.com/products/embeddedsolutions/connectcore9m.jsp
+ *
+ * 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.
+ *
+ * Author : Luis Galdos <luis.galdos@digi.com>
+ *
+ */
+
+#ifndef __PIPER_CCW9M2443JS_H
+#define __PIPER_CCW9M2443JS_H
+
+void __init add_device_ccw9m2443_piper(struct piper_pdata *pdata);
+
+#endif /* __PIPER_CCW9M2443JS_H */
diff --git a/arch/arm/mach-s3c2443/s3c2443.c b/arch/arm/mach-s3c2443/s3c2443.c
index bbeddf9ddcb1..17efa7736a6f 100644
--- a/arch/arm/mach-s3c2443/s3c2443.c
+++ b/arch/arm/mach-s3c2443/s3c2443.c
@@ -21,6 +21,7 @@
#include <linux/sysdev.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/syscalls.h> /* For accessing to the syslog */
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
@@ -36,6 +37,9 @@
#include <plat/devs.h>
#include <plat/cpu.h>
+#include <asm/plat-s3c/regs-watchdog.h>
+#include <linux/delay.h>
+
static struct map_desc s3c2443_iodesc[] __initdata = {
IODESC_ENT(WATCHDOG),
IODESC_ENT(CLKPWR),
@@ -52,7 +56,11 @@ static struct sys_device s3c2443_sysdev = {
static void s3c2443_hard_reset(void)
{
- __raw_writel(S3C2443_SWRST_RESET, S3C2443_SWRST);
+ /* Disable the logs and flush the printk buffers before the reboot command */
+ sys_syslog(5, NULL, 0);
+ sys_syslog(6, NULL, 0);
+ mdelay(100);
+ __raw_writel(0x21, S3C2410_WTCON);
}
int __init s3c2443_init(void)
diff --git a/arch/arm/mach-stmp3xxx/Kconfig b/arch/arm/mach-stmp3xxx/Kconfig
new file mode 100644
index 000000000000..7b87f8006e56
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/Kconfig
@@ -0,0 +1,102 @@
+if ARCH_STMP3XXX
+
+menu "SigmaTel STMP3xxx implementations"
+
+choice
+ prompt "Select STMP3xxx chip family"
+
+config ARCH_STMP37XX
+ bool "SigmaTel SMTP37xx"
+ ---help---
+ STMP37xx refers to 3700 through 3769 chips
+
+config ARCH_STMP378X
+ bool "SigmaTel STMP378x"
+ ---help---
+ STMP378x refers to 3780 through 3789 chips
+
+endchoice
+
+choice
+ prompt "Select STMP3xxx board type"
+
+config MACH_STMP37XX
+ depends on ARCH_STMP37XX
+ bool "SigmaTel STMP37xx development board"
+
+config MACH_STMP378X
+ depends on ARCH_STMP378X
+ bool "SigmaTel STMP378x development board"
+
+endchoice
+
+endmenu
+
+config FB_STMP37XX_HX8238A
+ depends on ARCH_STMP37XX
+ bool "HX8238A"
+ ---help---
+ Use HX8238A dotclock LCD panel for STMP37XX
+
+config FB_STMP37XX_LMS350
+ depends on ARCH_STMP378X
+ bool "LMS350"
+ ---help---
+ Use LMS350 dotclock LCD panel for STMP378X
+
+config FB_STMP37XX_LMS430
+ depends on ARCH_STMP378X
+ bool "LMS430"
+ ---help---
+ Use LMS430 dotclock LCD panel for STMP378X
+
+config FB_STMP378X_TVENC
+ depends on ARCH_STMP378X
+ bool "TVENC"
+ ---help---
+ Use TVOUT encoder for STMP378X
+
+config STMP3XXX_UNIQUE_ID
+ bool "Support for UniqueID on boot media"
+ default y
+
+config STMP3XXX_UNIQUE_ID_OTP
+ bool "UniqueID on OTP"
+ depends on STMP3XXX_UNIQUE_ID
+ default y
+
+config STMP378X_RAM_FREQ_SCALING
+ bool "RAM frequency scaling support"
+ depends on ARCH_STMP378X
+ default y
+
+choice
+ prompt "Select STMP378x RAM chip"
+ depends on STMP378X_RAM_FREQ_SCALING
+
+config STMP378X_RAM_MDDR
+ bool "mDDR SDRAM"
+config STMP378X_RAM_DDR
+ bool "DDR SDRAM"
+
+endchoice
+
+config DMA_ZONE_SIZE
+ int "DMA memory zone size"
+ range 0 32
+ default 12
+ help
+ This is the size in MB for the DMA zone. The DMA zone is used for
+ dedicated memory for large contiguous video buffers
+
+# set if we need the UTMI transceiver code
+config UTMI_MXC
+ bool
+ select UTMI_MXC_OTG if USB_GADGET && USB_EHCI_HCD && USB_OTG
+ default y if USB_EHCI_FSL_UTMI || USB_GADGET_FSL_UTMI
+
+config UTMI_MXC_OTG
+ tristate
+ help
+ Support for USB OTG pin detect using the UTMI transceiver on STMP378xxx platforms.
+endif
diff --git a/arch/arm/mach-stmp3xxx/Makefile b/arch/arm/mach-stmp3xxx/Makefile
new file mode 100644
index 000000000000..65d46d1e6f1a
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/Makefile
@@ -0,0 +1,47 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+obj-y += core.o timer.o irq.o dma.o clock.o pinmux.o \
+ gpio.o lradc.o devices.o gpmi.o spi.o mmc.o \
+ power.o \
+ persistent.o power-test.o
+
+# charging/current limitation testing
+obj-m += power-test.o
+
+obj-$(CONFIG_ARCH_STMP378X) += dcp-bootstream.o usb_common.o utmixc.o
+
+# Chip family select
+obj-$(CONFIG_ARCH_STMP37XX) += stmp37xx.o stmp37xx_lcdif.o
+obj-$(CONFIG_ARCH_STMP378X) += stmp378x.o stmp378x_lcdif.o
+
+obj-$(CONFIG_MACH_STMP37XX) += stmp37xx_devb.o
+obj-$(CONFIG_MACH_STMP378X) += stmp378x_devb.o stmp378x_i2c.o stmp378x_pwm_led.o
+
+# Power Management
+obj-$(CONFIG_PM) += pm.o sleep.o
+obj-$(CONFIG_CPU_FREQ) += cpufreq.o
+
+# LCD panels support
+obj-$(CONFIG_FB_STMP37XX_HX8238A) += lcd_hx8238a.o
+obj-$(CONFIG_FB_STMP37XX_LMS350) += lcd_lms350.o
+obj-$(CONFIG_FB_STMP37XX_LMS430) += lcd_lms430.o
+
+# TVOUT support
+obj-$(CONFIG_FB_STMP378X_TVENC) += tvenc.o
+
+obj-$(CONFIG_STMP3XXX_UNIQUE_ID) += unique-id.o
+obj-$(CONFIG_STMP3XXX_UNIQUE_ID_OTP) += otp.o
+obj-$(CONFIG_STMP3XXX_UNIQUE_ID_FILE) += unique-id-file.o
+
+obj-$(CONFIG_INPUT_STMP3XXX_ROTDEC) += stmp378x_devb_rotdec.o
+obj-$(CONFIG_STMP378X_RAM_FREQ_SCALING) += emi.o
+
+# SPDIF transmitter
+obj-$(CONFIG_SND_SOC_STMP3XXX_SPDIF) += spdif.o
+
+ifneq ($(strip $(CONFIG_USB_GADGET_ARC) $(CONFIG_USB_EHCI_ARC_OTG)),)
+ obj-y += usb_dr.o
+endif
diff --git a/arch/arm/mach-stmp3xxx/Makefile.boot b/arch/arm/mach-stmp3xxx/Makefile.boot
new file mode 100644
index 000000000000..1568ad404d59
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/Makefile.boot
@@ -0,0 +1,3 @@
+ zreladdr-y := 0x40008000
+params_phys-y := 0x40000100
+initrd_phys-y := 0x40800000
diff --git a/arch/arm/mach-stmp3xxx/clock.c b/arch/arm/mach-stmp3xxx/clock.c
new file mode 100644
index 000000000000..746ee5c459a9
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/clock.c
@@ -0,0 +1,1376 @@
+/*
+ * Clock manipulation routines for Freescale STMP37XX/STMP378X
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/* #define DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <mach/cpu.h>
+#include <mach/regs-clkctrl.h>
+
+#include "clock.h"
+
+static DEFINE_SPINLOCK(clocks_lock);
+
+static struct clk osc_24M;
+static struct clk pll_clk;
+static struct clk cpu_clk;
+static struct clk hclk;
+
+static int std_propagate_rate(struct clk *);
+
+static inline int clk_is_busy(struct clk *clk)
+{
+ return __raw_readl(clk->busy_reg) & (1 << clk->busy_bit);
+}
+
+static int std_clk_enable(struct clk *clk)
+{
+ if (clk->enable_reg) {
+ u32 clk_reg = __raw_readl(clk->enable_reg);
+ pr_debug("%s: clock '%s'\n", __func__, clk->name);
+ if (clk->enable_negate)
+ clk_reg &= ~(1 << clk->enable_shift);
+ else
+ clk_reg |= (1 << clk->enable_shift);
+ __raw_writel(clk_reg, clk->enable_reg);
+ if (clk->enable_wait)
+ udelay(clk->enable_wait);
+ return 0;
+ } else
+ return -EINVAL;
+}
+
+static int std_clk_disable(struct clk *clk)
+{
+ if (clk->enable_reg) {
+ u32 clk_reg = __raw_readl(clk->enable_reg);
+ pr_debug("%s: clock '%s'\n", __func__, clk->name);
+ if (clk->enable_negate)
+ clk_reg |= (1 << clk->enable_shift);
+ else
+ clk_reg &= ~(1 << clk->enable_shift);
+ __raw_writel(clk_reg, clk->enable_reg);
+ return 0;
+ } else
+ return -EINVAL;
+}
+
+static int io_set_rate(struct clk *clk, u32 rate)
+{
+ u32 reg_frac, clkctrl_frac;
+ int i, ret = 0, mask = 0x1f;
+
+ clkctrl_frac = (clk->parent->rate * 18 + rate - 1) / rate;
+
+ if (clkctrl_frac < 18 || clkctrl_frac > 35) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ reg_frac = __raw_readl(clk->scale_reg);
+ reg_frac &= ~(mask << clk->scale_shift);
+ __raw_writel(reg_frac | (clkctrl_frac << clk->scale_shift),
+ clk->scale_reg);
+ if (clk->busy_reg) {
+ for (i = 10000; i; i--)
+ if (!clk_is_busy(clk))
+ break;
+ if (!i)
+ ret = -ETIMEDOUT;
+ else
+ ret = 0;
+ }
+out:
+ return ret;
+}
+
+static long io_get_rate(struct clk *clk)
+{
+ long rate = clk->parent->rate * 18;
+ int mask = 0x1f;
+
+ rate /= (__raw_readl(clk->scale_reg) >> clk->scale_shift) & mask;
+ pr_debug("rate now %ld\n", rate);
+ clk->rate = rate;
+
+ return rate;
+}
+
+static long per_get_rate(struct clk *clk)
+{
+ long rate = clk->parent->rate;
+ long div;
+ const int mask = 0xff;
+
+ pr_debug("%s: clock name %s\n", __func__, clk->name);
+ if (clk->enable_reg &&
+ !(__raw_readl(clk->enable_reg) & clk->enable_shift))
+ clk->rate = 0;
+ else {
+ div = (__raw_readl(clk->scale_reg) >> clk->scale_shift) & mask;
+ if (div)
+ rate /= div;
+ else
+ printk(KERN_WARNING "clock '%s' has divisor 0!\n",
+ clk->name);
+ clk->rate = rate;
+ }
+
+ return 0;
+}
+
+static int per_set_rate(struct clk *clk, u32 rate)
+{
+ int ret = -EINVAL;
+ int div = (clk->parent->rate + rate - 1) / rate;
+ u32 reg_frac;
+ const int mask = 0xff;
+ int try = 10;
+ int i = -1;
+
+ if (div == 0 || div > mask)
+ goto out;
+
+ reg_frac = __raw_readl(clk->scale_reg);
+ reg_frac &= ~(mask << clk->scale_shift);
+
+ while (try--) {
+ __raw_writel(reg_frac | (div << clk->scale_shift),
+ clk->scale_reg);
+
+ if (clk->busy_reg) {
+ for (i = 10000; i; i--)
+ if (!clk_is_busy(clk))
+ break;
+ }
+ if (i)
+ break;
+ }
+
+ if (!i)
+ ret = -ETIMEDOUT;
+ else
+ ret = 0;
+
+out:
+ BUG_ON(ret != 0);
+ return ret;
+}
+
+static long lcdif_get_rate(struct clk *clk)
+{
+ long rate = clk->parent->rate;
+ long div;
+ const int mask = 0xff;
+
+ pr_debug("%s: clock name %s\n", __func__, clk->name);
+ div = (__raw_readl(clk->scale_reg) >> clk->scale_shift) & mask;
+ if (div) {
+ rate /= div;
+ div = (HW_CLKCTRL_FRAC_RD() & BM_CLKCTRL_FRAC_PIXFRAC) >>
+ BP_CLKCTRL_FRAC_PIXFRAC;
+ rate /= div;
+ } else
+ printk(KERN_WARNING "clock '%s' has divisor 0!\n", clk->name);
+ clk->rate = rate;
+
+ return 0;
+}
+
+static int lcdif_set_rate(struct clk *clk, u32 rate)
+{
+ int ret = 0;
+ /*
+ * On 3700, we can get most timings exact by modifying ref_pix
+ * and the divider, but keeping the phase timings at 1 (2
+ * phases per cycle).
+ *
+ * ref_pix can be between 480e6*18/35=246.9MHz and 480e6*18/18=480MHz,
+ * which is between 18/(18*480e6)=2.084ns and 35/(18*480e6)=4.050ns.
+ *
+ * ns_cycle >= 2*18e3/(18*480) = 25/6
+ * ns_cycle <= 2*35e3/(18*480) = 875/108
+ *
+ * Multiply the ns_cycle by 'div' to lengthen it until it fits the
+ * bounds. This is the divider we'll use after ref_pix.
+ *
+ * 6 * ns_cycle >= 25 * div
+ * 108 * ns_cycle <= 875 * div
+ */
+ u32 ns_cycle = 1000000 / rate;
+ ns_cycle *= 2; /* Fix calculate double frequency */
+ u32 div, reg_val;
+ u32 lowest_result = (u32) -1;
+ u32 lowest_div = 0, lowest_fracdiv = 0;
+
+ for (div = 1; div < 256; ++div) {
+ u32 fracdiv;
+ u32 ps_result;
+ int lower_bound = 6 * ns_cycle >= 25 * div;
+ int upper_bound = 108 * ns_cycle <= 875 * div;
+ if (!lower_bound)
+ break;
+ if (!upper_bound)
+ continue;
+ /*
+ * Found a matching div. Calculate fractional divider needed,
+ * rounded up.
+ */
+ fracdiv = ((clk->parent->rate / 1000 * 18 / 2) *
+ ns_cycle + 1000 * div - 1) /
+ (1000 * div);
+ if (fracdiv < 18 || fracdiv > 35) {
+ ret = -EINVAL;
+ goto out;
+ }
+ /* Calculate the actual cycle time this results in */
+ ps_result = 6250 * div * fracdiv / 27;
+
+ /* Use the fastest result that doesn't break ns_cycle */
+ if (ps_result <= lowest_result) {
+ lowest_result = ps_result;
+ lowest_div = div;
+ lowest_fracdiv = fracdiv;
+ }
+ }
+
+ if (div >= 256 || lowest_result == (u32) -1) {
+ ret = -EINVAL;
+ goto out;
+ }
+ pr_debug("Programming PFD=%u,DIV=%u ref_pix=%uMHz "
+ "PIXCLK=%uMHz cycle=%u.%03uns\n",
+ lowest_fracdiv, lowest_div,
+ 480*18/lowest_fracdiv, 480*18/lowest_fracdiv/lowest_div,
+ lowest_result / 1000, lowest_result % 1000);
+
+ /* Program ref_pix phase fractional divider */
+ HW_CLKCTRL_FRAC_WR((HW_CLKCTRL_FRAC_RD() & ~BM_CLKCTRL_FRAC_PIXFRAC) |
+ BF_CLKCTRL_FRAC_PIXFRAC(lowest_fracdiv));
+ /* Ungate PFD */
+ HW_CLKCTRL_FRAC_CLR(BM_CLKCTRL_FRAC_CLKGATEPIX);
+
+ /* Program pix divider */
+ reg_val = __raw_readl(clk->scale_reg);
+ reg_val &= ~(BM_CLKCTRL_PIX_DIV | BM_CLKCTRL_PIX_CLKGATE);
+ reg_val |= BF_CLKCTRL_PIX_DIV(lowest_div);
+ __raw_writel(reg_val, clk->scale_reg);
+
+ /* Wait for divider update */
+ if (clk->busy_reg) {
+ int i;
+ for (i = 10000; i; i--)
+ if (!clk_is_busy(clk))
+ break;
+ if (!i) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+ }
+
+ /* Switch to ref_pix source */
+ HW_CLKCTRL_CLKSEQ_CLR(BM_CLKCTRL_CLKSEQ_BYPASS_PIX);
+
+out:
+ return ret;
+}
+
+
+static int cpu_set_rate(struct clk *clk, u32 rate)
+{
+ if (rate < 24000)
+ return -EINVAL;
+ else if (rate == 24000) {
+ /* switch to the 24M source */
+ clk_set_parent(clk, &osc_24M);
+ } else {
+ int i;
+ u32 clkctrl_cpu = 1;
+ u32 c = clkctrl_cpu;
+ u32 clkctrl_frac = 1;
+ u32 val;
+ for ( ; c < 0x40; c++) {
+ u32 f = (pll_clk.rate*18/c + rate/2) / rate;
+ int s1, s2;
+
+ if (f < 18 || f > 35)
+ continue;
+ s1 = pll_clk.rate*18/clkctrl_frac/clkctrl_cpu - rate;
+ s2 = pll_clk.rate*18/c/f - rate;
+ pr_debug("%s: s1 %d, s2 %d\n", __func__, s1, s2);
+ if (abs(s1) > abs(s2)) {
+ clkctrl_cpu = c;
+ clkctrl_frac = f;
+ }
+ if (s2 == 0)
+ break;
+ };
+ pr_debug("%s: clkctrl_cpu %d, clkctrl_frac %d\n", __func__,
+ clkctrl_cpu, clkctrl_frac);
+ if (c == 0x40) {
+ int d = pll_clk.rate*18/clkctrl_frac/clkctrl_cpu -
+ rate;
+ if (abs(d) > 100 ||
+ clkctrl_frac < 18 || clkctrl_frac > 35)
+ return -EINVAL;
+ }
+
+ /* 4.6.2 */
+ val = __raw_readl(clk->scale_reg);
+ val &= ~(0x3f << clk->scale_shift);
+ val |= clkctrl_frac;
+ clk_set_parent(clk, &osc_24M);
+ udelay(10);
+ __raw_writel(val, clk->scale_reg);
+ /* ungate */
+ __raw_writel(1<<7, clk->scale_reg + 8);
+ /* write clkctrl_cpu */
+ clk->saved_div = clkctrl_cpu;
+ HW_CLKCTRL_CPU_WR((HW_CLKCTRL_CPU_RD() & ~0x3f) | clkctrl_cpu);
+ for (i = 10000; i; i--)
+ if (!clk_is_busy(clk))
+ break;
+ if (!i) {
+ printk(KERN_ERR "couldn't set up CPU divisor\n");
+ return -ETIMEDOUT;
+ }
+ clk_set_parent(clk, &pll_clk);
+ clk->saved_div = 0;
+ udelay(10);
+ }
+ return 0;
+}
+
+static long cpu_get_rate(struct clk *clk)
+{
+ long rate = clk->parent->rate * 18;
+
+ rate /= (__raw_readl(clk->scale_reg) >> clk->scale_shift) & 0x3f;
+ rate /= HW_CLKCTRL_CPU_RD() & 0x3f;
+ rate = ((rate + 9) / 10) * 10;
+ clk->rate = rate;
+
+ return rate;
+}
+
+static long cpu_round_rate(struct clk *clk, u32 rate)
+{
+ unsigned long r = 0;
+
+ if (rate <= 24000)
+ r = 24000;
+ else {
+ u32 clkctrl_cpu = 1;
+ u32 clkctrl_frac;
+ do {
+ clkctrl_frac =
+ (pll_clk.rate*18 / clkctrl_cpu + rate/2) / rate;
+ if (clkctrl_frac > 35)
+ continue;
+ if (pll_clk.rate*18 / clkctrl_frac / clkctrl_cpu/10 ==
+ rate / 10)
+ break;
+ } while (pll_clk.rate / 2 >= clkctrl_cpu++ * rate);
+ if (pll_clk.rate / 2 < (clkctrl_cpu - 1) * rate)
+ clkctrl_cpu--;
+ pr_debug("%s: clkctrl_cpu %d, clkctrl_frac %d\n", __func__,
+ clkctrl_cpu, clkctrl_frac);
+ if (clkctrl_frac < 18)
+ clkctrl_frac = 18;
+ if (clkctrl_frac > 35)
+ clkctrl_frac = 35;
+
+ r = pll_clk.rate * 18;
+ r /= clkctrl_frac;
+ r /= clkctrl_cpu;
+ r = 10 * ((r + 9) / 10);
+ }
+ return r;
+}
+
+static int emi_set_rate(struct clk *clk, u32 rate)
+{
+ int ret = 0;
+
+ if (rate < 24000)
+ return -EINVAL;
+ else {
+ int i;
+ struct stmp3xxx_emi_scaling_data sc_data;
+ int (*scale)(struct stmp3xxx_emi_scaling_data *) =
+ (void *)STMP3XXX_OCRAM_VA_BASE;
+ void *saved_ocram;
+ u32 clkctrl_emi;
+ u32 clkctrl_frac;
+ int div = 1;
+ /*
+ * We've been setting div to HW_CLKCTRL_CPU_RD() & 0x3f so far.
+ * TODO: verify 1 is still valid.
+ */
+
+ if (!stmp3xxx_ram_funcs_sz)
+ goto out;
+
+ for (clkctrl_emi = div; clkctrl_emi < 0x3f;
+ clkctrl_emi += div) {
+ clkctrl_frac =
+ (pll_clk.rate * 18 + rate * clkctrl_emi / 2) /
+ (rate * clkctrl_emi);
+ if (clkctrl_frac >= 18 && clkctrl_frac <= 35) {
+ pr_debug("%s: clkctrl_frac found %d for %d\n",
+ __func__, clkctrl_frac, clkctrl_emi);
+ if (pll_clk.rate * 18 /
+ clkctrl_frac / clkctrl_emi / 100 ==
+ rate / 100)
+ break;
+ }
+ }
+ if (clkctrl_emi >= 0x3f)
+ return -EINVAL;
+ pr_debug("%s: clkctrl_emi %d, clkctrl_frac %d\n",
+ __func__, clkctrl_emi, clkctrl_frac);
+
+ saved_ocram = kmalloc(stmp3xxx_ram_funcs_sz, GFP_KERNEL);
+ if (!saved_ocram)
+ return -ENOMEM;
+ memcpy(saved_ocram, scale, stmp3xxx_ram_funcs_sz);
+ memcpy(scale, stmp3xxx_ram_freq_scale, stmp3xxx_ram_funcs_sz);
+
+ sc_data.emi_div = clkctrl_emi;
+ sc_data.frac_div = clkctrl_frac;
+ sc_data.cur_freq = clk->rate / 1000;
+ sc_data.new_freq = rate / 1000;
+
+ local_irq_disable();
+ local_fiq_disable();
+
+ scale(&sc_data);
+
+ local_fiq_enable();
+ local_irq_enable();
+
+ for (i = 10000; i; i--)
+ if (!clk_is_busy(clk))
+ break;
+ memcpy(scale, saved_ocram, stmp3xxx_ram_funcs_sz);
+ kfree(saved_ocram);
+
+ if (!i) {
+ printk(KERN_ERR "couldn't set up EMI divisor\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+static long emi_get_rate(struct clk *clk)
+{
+ long rate = clk->parent->rate * 18;
+
+ rate /= (__raw_readl(clk->scale_reg) >> clk->scale_shift) & 0x3f;
+ rate /= HW_CLKCTRL_EMI_RD() & 0x3f;
+ clk->rate = rate;
+
+ return rate;
+}
+
+static int clkseq_set_parent(struct clk *clk, struct clk *parent)
+{
+ int ret = -EINVAL;
+ int shift = 8;
+
+ /* bypass? */
+ if (parent == &osc_24M)
+ shift = 4;
+
+ if (clk->bypass_reg) {
+ u32 hbus_mask = BM_CLKCTRL_HBUS_DIV_FRAC_EN |
+ BM_CLKCTRL_HBUS_DIV;
+
+ if (clk == &cpu_clk && shift == 4) {
+ u32 hbus_val = HW_CLKCTRL_HBUS_RD();
+ u32 cpu_val = HW_CLKCTRL_CPU_RD();
+ hbus_val &= ~hbus_mask;
+ hbus_val |= 1;
+ clk->saved_div = cpu_val & BM_CLKCTRL_CPU_DIV_CPU;
+ cpu_val &= ~BM_CLKCTRL_CPU_DIV_CPU;
+ cpu_val |= 1;
+ __raw_writel(1 << clk->bypass_shift,
+ clk->bypass_reg + shift);
+ if (machine_is_stmp378x()) {
+ HW_CLKCTRL_HBUS_WR(hbus_val);
+ HW_CLKCTRL_CPU_WR(cpu_val);
+ hclk.rate = 0;
+ }
+ } else if (clk == &cpu_clk && shift == 8) {
+ u32 hbus_val = HW_CLKCTRL_HBUS_RD();
+ u32 cpu_val = HW_CLKCTRL_CPU_RD();
+ hbus_val &= ~hbus_mask;
+ hbus_val |= 2;
+ cpu_val &= ~BM_CLKCTRL_CPU_DIV_CPU;
+ if (clk->saved_div)
+ cpu_val |= clk->saved_div;
+ else
+ cpu_val |= 2;
+ if (machine_is_stmp378x()) {
+ HW_CLKCTRL_HBUS_WR(hbus_val);
+ HW_CLKCTRL_CPU_WR(cpu_val);
+ hclk.rate = 0;
+ }
+ __raw_writel(1 << clk->bypass_shift,
+ clk->bypass_reg + shift);
+ } else
+ __raw_writel(1 << clk->bypass_shift,
+ clk->bypass_reg + shift);
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int hbus_set_rate(struct clk *clk, u32 rate)
+{
+ u8 div = 0;
+ int is_frac = 0;
+ u32 clkctrl_hbus;
+ struct clk *parent = clk->parent;
+
+ pr_debug("%s: rate %d, parent rate %d\n", __func__, rate,
+ parent->rate);
+
+ if (rate > parent->rate)
+ return -EINVAL;
+
+ if (((parent->rate + rate/2) / rate) * rate != parent->rate &&
+ parent->rate / rate < 32) {
+ pr_debug("%s: switching to fractional mode\n", __func__);
+ is_frac = 1;
+ }
+
+ if (is_frac)
+ div = (32 * rate + parent->rate / 2) / parent->rate;
+ else
+ div = (parent->rate + rate - 1) / rate;
+ pr_debug("%s: div calculated is %d\n", __func__, div);
+ if (!div || div > 0x1f)
+ return -EINVAL;
+
+ clk_set_parent(&cpu_clk, &osc_24M);
+ udelay(10);
+ clkctrl_hbus = __raw_readl(clk->scale_reg);
+ clkctrl_hbus &= ~0x3f;
+ clkctrl_hbus |= div;
+ clkctrl_hbus |= (is_frac << 5);
+
+ __raw_writel(clkctrl_hbus, clk->scale_reg);
+ if (clk->busy_reg) {
+ int i;
+ for (i = 10000; i; i--)
+ if (!clk_is_busy(clk))
+ break;
+ if (!i) {
+ printk(KERN_ERR "couldn't set up CPU divisor\n");
+ return -ETIMEDOUT;
+ }
+ }
+ clk_set_parent(&cpu_clk, &pll_clk);
+ __raw_writel(clkctrl_hbus, clk->scale_reg);
+ udelay(10);
+ return 0;
+}
+
+static long hbus_get_rate(struct clk *clk)
+{
+ long rate = clk->parent->rate;
+
+ if (__raw_readl(clk->scale_reg) & 0x20) {
+ rate *= __raw_readl(clk->scale_reg) & 0x1f;
+ rate /= 32;
+ } else
+ rate /= __raw_readl(clk->scale_reg) & 0x1f;
+ clk->rate = rate;
+
+ return rate;
+}
+
+static int xbus_set_rate(struct clk *clk, u32 rate)
+{
+ u16 div = 0;
+ u32 clkctrl_xbus;
+
+ pr_debug("%s: rate %d, parent rate %d\n", __func__, rate,
+ clk->parent->rate);
+
+ div = (clk->parent->rate + rate - 1) / rate;
+ pr_debug("%s: div calculated is %d\n", __func__, div);
+ if (!div || div > 0x3ff)
+ return -EINVAL;
+
+ clkctrl_xbus = __raw_readl(clk->scale_reg);
+ clkctrl_xbus &= ~0x3ff;
+ clkctrl_xbus |= div;
+ __raw_writel(clkctrl_xbus, clk->scale_reg);
+ if (clk->busy_reg) {
+ int i;
+ for (i = 10000; i; i--)
+ if (!clk_is_busy(clk))
+ break;
+ if (!i) {
+ printk(KERN_ERR "couldn't set up xbus divisor\n");
+ return -ETIMEDOUT;
+ }
+ }
+ return 0;
+}
+
+static long xbus_get_rate(struct clk *clk)
+{
+ long rate = clk->parent->rate;
+
+ rate /= __raw_readl(clk->scale_reg) & 0x3ff;
+ clk->rate = rate;
+
+ return rate;
+}
+
+
+static int etm_set_rate(struct clk *clk, u32 rate)
+{
+ u16 div = 0;
+ u32 clkctrl_etm;
+
+ pr_debug("%s: rate %d, parent rate %d\n", __func__, rate,
+ clk->parent->rate);
+
+ div = (clk->parent->rate + rate - 1) / rate;
+ pr_debug("%s: div calculated is %d\n", __func__, div);
+ if (!div || div > 0x3f)
+ return -EINVAL;
+
+ clkctrl_etm = __raw_readl(clk->scale_reg);
+ clkctrl_etm &= ~0x3f;
+ clkctrl_etm |= div;
+ __raw_writel(clkctrl_etm, clk->scale_reg);
+ if (clk->busy_reg) {
+ int i;
+ for (i = 10000; i; i--)
+ if (!clk_is_busy(clk))
+ break;
+ if (!i) {
+ printk(KERN_ERR "couldn't set up ETM clock divisor\n");
+ return -ETIMEDOUT;
+ }
+ }
+ return 0;
+}
+
+static long etm_get_rate(struct clk *clk)
+{
+ long rate = clk->parent->rate;
+ rate /= (__raw_readl(clk->scale_reg) >> clk->scale_shift) & 0x3f;
+ clk->rate = rate;
+
+ return rate;
+}
+
+/* List of on-chip clocks */
+
+static struct clk osc_24M = {
+ .name = "osc_24M",
+ .flags = FIXED_RATE | ENABLED |
+ PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+ .rate = 24000,
+ .propagate_rate = std_propagate_rate,
+};
+
+static struct clk pll_clk = {
+ .name = "pll",
+ .parent = &osc_24M,
+ .enable_reg = HW_CLKCTRL_PLLCTRL0_ADDR,
+ .enable_shift = 16,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_wait = 10,
+ .flags = FIXED_RATE | ENABLED |
+ PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+ .rate = 480000,
+ .propagate_rate = std_propagate_rate,
+};
+
+static struct clk cpu_clk = {
+ .name = "cpu",
+ .parent = &pll_clk,
+ .get_rate = cpu_get_rate,
+ .set_rate = cpu_set_rate,
+ .round_rate = cpu_round_rate,
+ .scale_reg = HW_CLKCTRL_FRAC_ADDR,
+ .scale_shift = 0,
+ .bypass_reg = HW_CLKCTRL_CLKSEQ_ADDR,
+ .bypass_shift = 7,
+ .busy_reg = HW_CLKCTRL_CPU_ADDR,
+ .busy_bit = 28,
+ .set_parent = clkseq_set_parent,
+ .flags = RATE_PROPAGATES | ENABLED |
+ PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+ .propagate_rate = std_propagate_rate,
+};
+
+static struct clk io_clk = {
+ .name = "io",
+ .parent = &pll_clk,
+ .enable_reg = HW_CLKCTRL_FRAC_ADDR,
+ .enable_shift = 31,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .get_rate = io_get_rate,
+ .set_rate = io_set_rate,
+ .scale_reg = HW_CLKCTRL_FRAC_ADDR,
+ .scale_shift = 24,
+ .flags = RATE_PROPAGATES | ENABLED |
+ PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+ .propagate_rate = std_propagate_rate,
+};
+
+static struct clk hclk = {
+ .name = "hclk",
+ .parent = &cpu_clk,
+ .get_rate = hbus_get_rate,
+ .set_rate = hbus_set_rate,
+ .scale_reg = HW_CLKCTRL_HBUS_ADDR,
+ .bypass_reg = HW_CLKCTRL_CLKSEQ_ADDR,
+ .bypass_shift = 7,
+ .busy_reg = HW_CLKCTRL_HBUS_ADDR,
+ .busy_bit = 29,
+ .flags = RATE_PROPAGATES | ENABLED |
+ PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+ .propagate_rate = std_propagate_rate,
+};
+
+static struct clk xclk = {
+ .name = "xclk",
+ .parent = &osc_24M,
+ .get_rate = xbus_get_rate,
+ .set_rate = xbus_set_rate,
+ .scale_reg = HW_CLKCTRL_XBUS_ADDR,
+ .busy_reg = HW_CLKCTRL_XBUS_ADDR,
+ .busy_bit = 31,
+ .flags = RATE_PROPAGATES | ENABLED |
+ PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+ .propagate_rate = std_propagate_rate,
+};
+
+static struct clk uart_clk = {
+ .name = "uart",
+ .parent = &xclk,
+ .enable_reg = HW_CLKCTRL_XTAL_ADDR,
+ .enable_shift = 31,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .flags = ENABLED | PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk audio_clk = {
+ .name = "audio",
+ .parent = &xclk,
+ .enable_reg = HW_CLKCTRL_XTAL_ADDR,
+ .enable_shift = 30,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .flags = PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk pwm_clk = {
+ .name = "pwm",
+ .parent = &xclk,
+ .enable_reg = HW_CLKCTRL_XTAL_ADDR,
+ .enable_shift = 29,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .flags = PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk dri_clk = {
+ .name = "dri",
+ .parent = &xclk,
+ .enable_reg = HW_CLKCTRL_XTAL_ADDR,
+ .enable_shift = 28,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .flags = PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk digctl_clk = {
+ .name = "digctl",
+ .parent = &xclk, /* XXX? */
+ .enable_reg = HW_CLKCTRL_XTAL_ADDR,
+ .enable_shift = 27,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .flags = PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk timer_clk = {
+ .name = "timer",
+ .parent = &xclk, /* XXX? */
+ .enable_reg = HW_CLKCTRL_XTAL_ADDR,
+ .enable_shift = 26,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .flags = ENABLED | PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk lcdif_clk = {
+ .name = "lcdif",
+ .parent = &pll_clk,
+ .get_rate = lcdif_get_rate,
+ .set_rate = lcdif_set_rate,
+ .scale_reg = HW_CLKCTRL_PIX_ADDR,
+ .busy_reg = HW_CLKCTRL_PIX_ADDR,
+ .busy_bit = 29,
+ .enable_reg = HW_CLKCTRL_PIX_ADDR,
+ .enable_shift = 31,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .bypass_reg = HW_CLKCTRL_CLKSEQ_ADDR,
+ .bypass_shift = 1,
+ .set_parent = clkseq_set_parent,
+ .flags = PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X |
+ NEEDS_SET_PARENT,
+};
+
+static struct clk ssp_clk = {
+ .name = "ssp",
+ .parent = &io_clk,
+ .get_rate = per_get_rate,
+ .set_rate = per_set_rate,
+ .scale_reg = HW_CLKCTRL_SSP_ADDR,
+ .busy_reg = HW_CLKCTRL_SSP_ADDR,
+ .busy_bit = 29,
+ .enable_reg = HW_CLKCTRL_SSP_ADDR,
+ .enable_shift = 31,
+ .bypass_reg = HW_CLKCTRL_CLKSEQ_ADDR,
+ .bypass_shift = 5,
+ .set_parent = clkseq_set_parent,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .flags = NEEDS_SET_PARENT |
+ PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk gpmi_clk = {
+ .name = "gpmi",
+ .parent = &io_clk,
+ .get_rate = per_get_rate,
+ .set_rate = per_set_rate,
+ .scale_reg = HW_CLKCTRL_GPMI_ADDR,
+ .busy_reg = HW_CLKCTRL_GPMI_ADDR,
+ .busy_bit = 29,
+ .enable_reg = HW_CLKCTRL_GPMI_ADDR,
+ .enable_shift = 31,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .bypass_reg = HW_CLKCTRL_CLKSEQ_ADDR,
+ .bypass_shift = 4,
+ .set_parent = clkseq_set_parent,
+ .flags = NEEDS_SET_PARENT |
+ PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk spdif_clk = {
+ .name = "spdif",
+ .parent = &pll_clk,
+ .enable_reg = HW_CLKCTRL_SPDIF_ADDR,
+ .enable_shift = 31,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .flags = PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk emi_clk = {
+ .name = "emi",
+ .parent = &pll_clk,
+ .enable_reg = HW_CLKCTRL_EMI_ADDR,
+ .enable_shift = 31,
+ .enable_negate = 1,
+ .scale_reg = HW_CLKCTRL_FRAC_ADDR,
+ .scale_shift = 8,
+ .get_rate = emi_get_rate,
+ .set_rate = emi_set_rate,
+ .busy_reg = HW_CLKCTRL_EMI_ADDR,
+ .busy_bit = 28,
+ .bypass_reg = HW_CLKCTRL_CLKSEQ_ADDR,
+ .bypass_shift = 6,
+ .set_parent = clkseq_set_parent,
+ .flags = ENABLED | PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk ir_clk = {
+ .name = "ir",
+ .parent = &io_clk,
+ .enable_reg = HW_CLKCTRL_IR_ADDR,
+ .enable_shift = 31,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .bypass_reg = HW_CLKCTRL_CLKSEQ_ADDR,
+ .bypass_shift = 3,
+ .set_parent = clkseq_set_parent,
+ .flags = NEEDS_SET_PARENT |
+ PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk saif_clk = {
+ .name = "saif",
+ .parent = &pll_clk,
+ .get_rate = per_get_rate,
+ .set_rate = per_set_rate,
+ .scale_reg = HW_CLKCTRL_SAIF_ADDR,
+ .busy_reg = HW_CLKCTRL_SAIF_ADDR,
+ .busy_bit = 29,
+ .enable_reg = HW_CLKCTRL_SAIF_ADDR,
+ .enable_shift = 31,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .bypass_reg = HW_CLKCTRL_CLKSEQ_ADDR,
+ .bypass_shift = 0,
+ .set_parent = clkseq_set_parent,
+ .flags = NEEDS_SET_PARENT |
+ PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk usb_clk = {
+ .name = "usb",
+ .parent = &pll_clk,
+ .enable_reg = HW_CLKCTRL_PLLCTRL0_ADDR,
+ .enable_shift = 18,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .flags = PRESENT_ON_STMP37XX | PRESENT_ON_STMP378X,
+};
+
+static struct clk vid_clk = {
+ .name = "ref_vid",
+ .parent = &osc_24M,
+#ifdef CONFIG_MACH_STMP378X
+ .enable_reg = HW_CLKCTRL_FRAC1_ADDR,
+ .enable_shift = 31,
+ .enable_negate = 1,
+#endif
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .rate = 432000,
+ .flags = FIXED_RATE | PRESENT_ON_STMP378X,
+};
+
+static struct clk clk_tv108M_ng = {
+ .name = "tv108M_ng",
+ .parent = &vid_clk,
+#ifdef CONFIG_MACH_STMP378X
+ .enable_reg = HW_CLKCTRL_TV_ADDR,
+ .enable_shift = 31,
+ .enable_negate = 1,
+#endif
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .rate = 108000,
+ .flags = FIXED_RATE | PRESENT_ON_STMP378X,
+};
+
+static struct clk clk_tv54M = {
+ .name = "tv54M",
+ .parent = &vid_clk,
+#ifdef CONFIG_MACH_STMP378X
+ .enable_reg = HW_CLKCTRL_TV_ADDR,
+ .enable_shift = 30,
+ .enable_negate = 1,
+#endif
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .rate = 54000,
+ .flags = FIXED_RATE | PRESENT_ON_STMP378X,
+};
+
+static struct clk clk_tv27M = {
+ .name = "tv27M",
+ .parent = &vid_clk,
+#ifdef CONFIG_MACH_STMP378X
+ .enable_reg = HW_CLKCTRL_TV_ADDR,
+ .enable_shift = 30,
+ .enable_negate = 1,
+#endif
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .rate = 27000,
+ .flags = FIXED_RATE | PRESENT_ON_STMP378X,
+};
+
+static struct clk clk_tvenc_fifo = {
+ .name = "tvenc_fifo",
+ .parent = &vid_clk,
+ .flags = PRESENT_ON_STMP378X,
+};
+
+static struct clk clk_etm = {
+ .name = "etm",
+ .parent = &pll_clk,
+#ifdef CONFIG_MACH_STMP378X
+ .enable_reg = HW_CLKCTRL_ETM_ADDR,
+ .enable_shift = 31,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .enable_negate = 1,
+ .scale_reg = HW_CLKCTRL_ETM_ADDR,
+ .scale_shift = 0,
+ .get_rate = etm_get_rate,
+ .set_rate = etm_set_rate,
+ .bypass_reg = HW_CLKCTRL_CLKSEQ_ADDR,
+ .bypass_shift = 8,
+ .busy_reg = HW_CLKCTRL_ETM_ADDR,
+ .busy_bit = 29,
+#endif
+ .set_parent = clkseq_set_parent,
+ .flags = PRESENT_ON_STMP378X,
+};
+
+
+/* list of all the clocks */
+static struct clk *onchip_clks[] = {
+ &osc_24M,
+ &pll_clk,
+ &cpu_clk,
+ &hclk,
+ &xclk,
+ &io_clk,
+ &uart_clk,
+ &audio_clk,
+ &pwm_clk,
+ &dri_clk,
+ &digctl_clk,
+ &timer_clk,
+ &lcdif_clk,
+ &ssp_clk,
+ &gpmi_clk,
+ &spdif_clk,
+ &emi_clk,
+ &ir_clk,
+ &saif_clk,
+ &usb_clk,
+ &vid_clk,
+ &clk_tv108M_ng,
+ &clk_tv54M,
+ &clk_tv27M,
+ &clk_etm,
+};
+
+static int std_propagate_rate(struct clk *clk)
+{
+ struct clk **clkp = onchip_clks;
+
+ for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks);
+ clkp++) {
+ if (!((*clkp)->flags & PRESENT_ON_STMP37XX &&
+ cpu_is_stmp37xx()) &&
+ !((*clkp)->flags & PRESENT_ON_STMP378X &&
+ cpu_is_stmp378x()))
+ continue;
+
+ if ((*clkp)->parent == clk && (*clkp)->get_rate) {
+ ((*clkp)->get_rate)(*clkp);
+ if ((*clkp)->flags & RATE_PROPAGATES)
+ ((*clkp)->propagate_rate)(*clkp);
+ }
+ }
+
+ return 0;
+}
+
+/* Exported API */
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (IS_ERR(clk))
+ return 0;
+
+ if (clk->rate != 0)
+ return clk->rate;
+
+ if (clk->get_rate != NULL)
+ return clk->get_rate(clk);
+
+ return clk->parent ? clk->parent->rate : 0;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ if (!IS_ERR(clk) && clk->round_rate)
+ return clk->round_rate(clk, rate);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+static inline int close_enough(long rate1, long rate2)
+{
+ return rate1 && !((rate2 - rate1) * 1000 / rate1);
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret = -EINVAL;
+
+ pr_debug("%s: clock %s rate %ld:%ld\n", __func__, clk->name,
+ (unsigned long)clk->rate, rate);
+ if (clk->flags & FIXED_RATE || !clk->set_rate)
+ goto out;
+ else if (!close_enough(clk->rate, rate)) {
+ ret = clk->set_rate(clk, rate);
+ if (ret < 0) {
+ printk(KERN_ERR "couldn't set rate for clock '%s': "
+ "error %d\n", clk->name, ret);
+ goto out;
+ }
+ clk->rate = rate;
+ if (clk->flags & RATE_PROPAGATES)
+ clk->propagate_rate(clk);
+ } else
+ ret = 0;
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+ struct clk **p;
+ struct clk *clk = ERR_PTR(-ENOENT);
+ int idno;
+ unsigned long clocks_flags;
+
+ if (dev == NULL || dev->bus != &platform_bus_type)
+ idno = -1;
+ else
+ idno = to_platform_device(dev)->id;
+
+ spin_lock_irqsave(&clocks_lock, clocks_flags);
+ for (p = onchip_clks; p < onchip_clks + ARRAY_SIZE(onchip_clks); p++) {
+ if (!((*p)->flags & PRESENT_ON_STMP37XX &&
+ cpu_is_stmp37xx()) &&
+ !((*p)->flags & PRESENT_ON_STMP378X &&
+ cpu_is_stmp378x()))
+ continue;
+
+ if (strcmp(id, (*p)->name) == 0 &&
+ try_module_get((*p)->owner)) {
+ clk = *p;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&clocks_lock, clocks_flags);
+ return clk;
+}
+EXPORT_SYMBOL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+ if (clk->owner)
+ module_put(clk->owner);
+}
+EXPORT_SYMBOL(clk_put);
+
+int clk_enable(struct clk *clk)
+{
+ unsigned long clocks_flags;
+
+ if (IS_ERR(clk) || clk == NULL)
+ return -EINVAL;
+ pr_debug("%s: clock '%s'\n", __func__, clk->name);
+
+ if (clk->parent)
+ clk_enable(clk->parent);
+
+ spin_lock_irqsave(&clocks_lock, clocks_flags);
+
+ clk->usage++;
+ pr_debug("clock '%s': use count increased to %d\n",
+ clk->name, clk->usage);
+ if (clk->enable)
+ (clk->enable)(clk);
+
+ spin_unlock_irqrestore(&clocks_lock, clocks_flags);
+ return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void local_clk_disable(struct clk *clk)
+{
+ unsigned long clocks_flags;
+
+ if (IS_ERR(clk) || clk == NULL)
+ return;
+
+ spin_lock_irqsave(&clocks_lock, clocks_flags);
+
+ if (clk->usage == 0 && clk->disable) {
+ pr_debug("%s: disabling clock '%s'\n", __func__, clk->name);
+ (clk->disable)(clk);
+ }
+
+ spin_unlock_irqrestore(&clocks_lock, clocks_flags);
+}
+
+void clk_disable(struct clk *clk)
+{
+ unsigned long clocks_flags;
+
+ if (IS_ERR(clk) || clk == NULL)
+ return;
+ pr_debug("%s: clock '%s'\n", __func__, clk->name);
+
+ spin_lock_irqsave(&clocks_lock, clocks_flags);
+
+ if ((--clk->usage) == 0 && clk->disable)
+ (clk->disable)(clk);
+ pr_debug("clock '%s': use count decreased to %d\n",
+ clk->name, clk->usage);
+
+ spin_unlock_irqrestore(&clocks_lock, clocks_flags);
+ if (clk->parent)
+ clk_disable(clk->parent);
+}
+EXPORT_SYMBOL(clk_disable);
+
+/* Some additional API */
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int ret = -ENODEV;
+ unsigned long clocks_flags;
+
+ if (!clk->set_parent)
+ goto out;
+
+ spin_lock_irqsave(&clocks_lock, clocks_flags);
+
+ if (clk->set_parent)
+ ret = clk->set_parent(clk, parent);
+ if (!ret) {
+ pr_debug("%s: '%s': parent usage %d, clk->parent usage %d, "
+ "clk usage %d\n", __func__, clk->name,
+ parent->usage, clk->parent->usage, clk->usage);
+ if (parent->usage == 0 && parent->disable)
+ (parent->disable)(parent);
+ parent->usage += clk->usage;
+ clk->parent->usage -= clk->usage;
+ if (clk->parent->usage == 0 && clk->parent->disable)
+ (clk->parent->disable)(clk->parent);
+ clk->parent = parent;
+ }
+ spin_unlock_irqrestore(&clocks_lock, clocks_flags);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+static void clkctrl_enable_powersavings(void)
+{
+ HW_CLKCTRL_HBUS_SET(BM_CLKCTRL_HBUS_APBHDMA_AS_ENABLE);
+ HW_CLKCTRL_HBUS_SET(BM_CLKCTRL_HBUS_APBXDMA_AS_ENABLE);
+ HW_CLKCTRL_HBUS_SET(BM_CLKCTRL_HBUS_TRAFFIC_AS_ENABLE);
+ HW_CLKCTRL_HBUS_SET(BM_CLKCTRL_HBUS_TRAFFIC_JAM_AS_ENABLE);
+ HW_CLKCTRL_HBUS_SET(BM_CLKCTRL_HBUS_CPU_DATA_AS_ENABLE);
+ HW_CLKCTRL_HBUS_SET(BM_CLKCTRL_HBUS_CPU_INSTR_AS_ENABLE);
+ HW_CLKCTRL_HBUS_SET(BM_CLKCTRL_HBUS_DCP_AS_ENABLE);
+ HW_CLKCTRL_HBUS_SET(BM_CLKCTRL_HBUS_PXP_AS_ENABLE);
+
+ HW_CLKCTRL_HBUS_SET(BF_CLKCTRL_HBUS_SLOW_DIV(
+ BV_CLKCTRL_HBUS_SLOW_DIV__BY32));
+ HW_CLKCTRL_HBUS_SET(BM_CLKCTRL_HBUS_AUTO_SLOW_MODE);
+}
+
+static int __init clk_init(void)
+{
+ struct clk **clkp;
+
+ spin_lock_init(&clocks_lock);
+ pr_debug("%s!\n", __func__);
+ for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks);
+ clkp++) {
+ if (!((*clkp)->flags & PRESENT_ON_STMP37XX &&
+ cpu_is_stmp37xx()) &&
+ !((*clkp)->flags & PRESENT_ON_STMP378X &&
+ cpu_is_stmp378x()))
+ continue;
+
+ (*clkp)->owner = THIS_MODULE;
+ if ((*clkp)->flags & ENABLED)
+ clk_enable(*clkp);
+ else
+ local_clk_disable(*clkp);
+ if (((*clkp)->flags & NEEDS_INITIALIZATION) &&
+ (*clkp)->set_rate)
+ (*clkp)->set_rate((*clkp), (*clkp)->rate);
+ if (!((*clkp)->flags & FIXED_RATE) && ((*clkp)->get_rate))
+ (*clkp)->get_rate(*clkp);
+ if (((*clkp)->flags & FIXED_RATE) &&
+ ((*clkp)->flags & RATE_PROPAGATES))
+ (*clkp)->propagate_rate(*clkp);
+ if ((*clkp)->flags & NEEDS_SET_PARENT)
+ (*clkp)->set_parent(*clkp, (*clkp)->parent);
+ pr_debug("%s: clock %s, rate %d\n",
+ __func__, (*clkp)->name, (*clkp)->rate);
+ }
+ clkctrl_enable_powersavings();
+
+ return 0;
+}
+
+arch_initcall(clk_init);
diff --git a/arch/arm/mach-stmp3xxx/clock.h b/arch/arm/mach-stmp3xxx/clock.h
new file mode 100644
index 000000000000..08fc733357b4
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/clock.h
@@ -0,0 +1,86 @@
+/*
+ * Clock control driver for Freescale STMP37XX/STMP378X - internal header file
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ARCH_ARM_STMX3XXX_CLOCK_H__
+#define __ARCH_ARM_STMX3XXX_CLOCK_H__
+
+#ifndef __ASSEMBLER__
+#include <linux/list.h>
+
+struct clk {
+ struct list_head node;
+ struct module *owner;
+ const char *name;
+ struct clk *parent;
+ u32 rate;
+ s8 usage;
+ u32 flags;
+ u32 scale_reg;
+ u8 scale_shift;
+ u8 enable_shift;
+ u32 enable_reg;
+ int enable_wait;
+ int enable_negate;
+ u8 bypass_shift;
+ u32 bypass_reg;
+ u8 busy_bit;
+ u32 busy_reg;
+ u32 saved_div;
+ int (*enable) (struct clk *);
+ int (*disable) (struct clk *);
+ long (*get_rate) (struct clk *);
+ long (*round_rate) (struct clk *, u32);
+ int (*set_rate) (struct clk *, u32);
+ int (*set_parent) (struct clk *, struct clk *);
+ int (*propagate_rate) (struct clk *);
+};
+
+struct stmp3xxx_emi_scaling_data {
+ u32 emi_div;
+ u32 frac_div;
+ u32 cur_freq;
+ u32 new_freq;
+};
+
+#ifdef CONFIG_STMP378X_RAM_FREQ_SCALING
+extern void stmp3xxx_ram_freq_scale(struct stmp3xxx_emi_scaling_data *);
+extern u32 stmp3xxx_ram_funcs_sz;
+#else
+static inline void stmp3xxx_ram_freq_scale(struct stmp3xxx_emi_scaling_data *p)
+{
+}
+static u32 stmp3xxx_ram_funcs_sz;
+#endif
+
+#endif /* __ASSEMBLER__ */
+
+/* Flags */
+#define RATE_PROPAGATES (1<<0)
+#define NEEDS_INITIALIZATION (1<<1)
+#define PARENT_SET_RATE (1<<2)
+#define FIXED_RATE (1<<3)
+#define ENABLED (1<<4)
+#define NEEDS_SET_PARENT (1<<5)
+#define PRESENT_ON_STMP37XX (1<<8)
+#define PRESENT_ON_STMP378X (1<<9)
+
+#define SCALING_DATA_EMI_DIV_OFFSET 0
+#define SCALING_DATA_FRAC_DIV_OFFSET 4
+#define SCALING_DATA_CUR_FREQ_OFFSET 8
+#define SCALING_DATA_NEW_FREQ_OFFSET 12
+
+#endif
diff --git a/arch/arm/mach-stmp3xxx/common.h b/arch/arm/mach-stmp3xxx/common.h
new file mode 100644
index 000000000000..1d662dbc5376
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/common.h
@@ -0,0 +1,65 @@
+/*
+ * Freescale STMP37XX/STMP378X internal functions and data declarations
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MACH_STMP3XXX_COMMON_H
+#define __ASM_ARCH_MACH_STMP3XXX_COMMON_H
+
+#include <linux/irq.h>
+#include <asm/mach/time.h>
+#include "pinmux.h"
+
+extern struct sys_timer stmp3xxx_timer;
+int stmp3xxx_add_devices(void);
+void stmp3xxx_init_irq(struct irq_chip *chip1,
+ struct irq_chip *chip2,
+ int (*is2)(int));
+void stmp3xxx_init(void);
+void stmp3xxx_set_mmc_data(struct device *dev);
+
+void stmp37xx_map_io(void);
+void stmp37xx_init_irq(void);
+int stmp37xx_add_devices(void);
+
+void stmp378x_map_io(void);
+void stmp378x_init_irq(void);
+void stmp378x_set_mmc_data(struct device *dev);
+
+extern struct pin_group i2c_pins;
+extern struct pin_group appuart_pins[];
+extern struct pin_group dbguart_pins[];
+extern struct pin_group gpmi_pins;
+extern struct pin_group stmp37xx_lcd_pins;
+extern struct pin_group stmp378x_lcd_pins;
+extern unsigned stmp37xx_lcd_spi_pins[];
+extern unsigned stmp378x_lcd_spi_pins[];
+extern struct pin_group usb_mux_pins;
+extern struct pin_group spdif_pins;
+
+/* pm.c */
+extern int stmp_s2ram_alloc_sz;
+void stmp37xx_cpu_suspend(void);
+extern int stmp_standby_alloc_sz;
+void stmp37xx_cpu_standby(void);
+void stmp3xxx_suspend_timer(void);
+void stmp3xxx_resume_timer(void);
+
+/* SPI */
+extern int stmp37xx_spi_enc_init(void *);
+extern int stmp37xx_spi_enc_release(void *);
+
+
+#endif /* __ASM_ARCH_MACH_STMP3XXXL_COMMON_H */
diff --git a/arch/arm/mach-stmp3xxx/core.c b/arch/arm/mach-stmp3xxx/core.c
new file mode 100644
index 000000000000..cfe68dec9c9a
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/core.c
@@ -0,0 +1,151 @@
+/*
+ * Freescale STMP37XX/STMP378X core routines
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+#include <linux/irq.h>
+
+#include <mach/hardware.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+#include <mach/system.h>
+
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-rtc.h>
+
+#include "common.h"
+
+int __stmp3xxx_reset_block(u32 hwreg, int just_enable)
+{
+ u32 c;
+ int timeout;
+
+ /* the process of software reset of IP block is done
+ in several steps:
+
+ - clear SFTRST and wait for block is enabled;
+ - clear clock gating (CLKGATE bit);
+ - set the SFTRST again and wait for block is in reset;
+ - clear SFTRST and wait for reset completion.
+ */
+ c = __raw_readl(hwreg);
+ c &= ~(1<<31); /* clear SFTRST */
+ __raw_writel(c, hwreg);
+ for (timeout = 1000000; timeout > 0; timeout--)
+ /* still in SFTRST state ? */
+ if ((__raw_readl(hwreg) & (1<<31)) == 0)
+ break;
+ if (timeout <= 0) {
+ printk(KERN_ERR"%s(%x): timeout when enabling\n",
+ __func__, hwreg);
+ return -ETIME;
+ }
+
+ c = __raw_readl(hwreg);
+ c &= ~(1<<30); /* clear CLKGATE */
+ __raw_writel(c, hwreg);
+
+ if (!just_enable) {
+ c = __raw_readl(hwreg);
+ c |= (1<<31); /* now again set SFTRST */
+ __raw_writel(c, hwreg);
+ for (timeout = 1000000; timeout > 0; timeout--)
+ /* poll until CLKGATE set */
+ if (__raw_readl(hwreg) & (1<<30))
+ break;
+ if (timeout <= 0) {
+ printk(KERN_ERR"%s(%x): timeout when resetting\n",
+ __func__, hwreg);
+ return -ETIME;
+ }
+
+ c = __raw_readl(hwreg);
+ c &= ~(1<<31); /* clear SFTRST */
+ __raw_writel(c, hwreg);
+ for (timeout = 1000000; timeout > 0; timeout--)
+ /* still in SFTRST state ? */
+ if ((__raw_readl(hwreg) & (1<<31)) == 0)
+ break;
+ if (timeout <= 0) {
+ printk(KERN_ERR"%s(%x): timeout when enabling "
+ "after reset\n", __func__, hwreg);
+ return -ETIME;
+ }
+
+ c = __raw_readl(hwreg);
+ c &= ~(1<<30); /* clear CLKGATE */
+ __raw_writel(c, hwreg);
+ }
+ for (timeout = 1000000; timeout > 0; timeout--)
+ /* still in SFTRST state ? */
+ if ((__raw_readl(hwreg) & (1<<30)) == 0)
+ break;
+
+ if (timeout <= 0) {
+ printk(KERN_ERR"%s(%x): timeout when unclockgating\n",
+ __func__, hwreg);
+ return -ETIME;
+ }
+
+ return 0;
+}
+int stmp3xxx_reset_block(u32 hwreg, int just_enable)
+{
+ int try = 10;
+ int r;
+
+ while (try--) {
+ r = __stmp3xxx_reset_block(hwreg, just_enable);
+ if (!r)
+ break;
+ pr_debug("%s: try %d failed\n", __func__, 10 - try);
+ }
+ return r;
+}
+EXPORT_SYMBOL(stmp3xxx_reset_block);
+
+static void stmp3xxx_machine_restart(char mode)
+{
+ arch_reset(mode);
+ printk(KERN_ERR"stmp3xxx_machine_restart failed -- System halted\n");
+ for (;;)
+ continue;
+}
+
+void __init stmp3xxx_init(void)
+{
+
+ /* Re-route machine restart to our own handler */
+ arm_pm_restart = stmp3xxx_machine_restart;
+
+ stmp3xxx_dma_init();
+
+ HW_RTC_PERSISTENT0_SET(BM_RTC_PERSISTENT0_XTAL32KHZ_PWRUP |
+ BM_RTC_PERSISTENT0_XTAL24MHZ_PWRUP);
+}
diff --git a/arch/arm/mach-stmp3xxx/cpufreq.c b/arch/arm/mach-stmp3xxx/cpufreq.c
new file mode 100644
index 000000000000..3ec0ec8e553f
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/cpufreq.c
@@ -0,0 +1,476 @@
+/*
+ * CPU frequency scaling for Freescale STMP37XX/STMP378X
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/* #define DEBUG */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+//#include <linux/regulator/regulator-drv.h>
+#include <linux/notifier.h>
+
+#include <mach/hardware.h>
+#include <linux/io.h>
+#include <asm/system.h>
+#include <mach/regulator.h>
+#include <mach/power.h>
+#include <mach/regs-digctl.h>
+#include <mach/regs-clkctrl.h>
+
+#define VERY_HI_RATE 2000000000
+#define CLKCTRL_PLL_PWD_BIT 16
+#define CLKCTRL_PLL_BYPASS 0x1ff
+
+static struct profile {
+ int cpu;
+ int ahb;
+ int emi;
+ int ss;
+ int vddd;
+ int vddd_bo;
+ int cur;
+ int vddio;
+ int vdda;
+ int pll_off;
+} profiles[] = {
+#ifdef CONFIG_STMP378X_RAM_MDDR
+ { 24000, 24000, 24000, 3, 1050000,
+ 975000, 150000, 3075000, 1725000, 1 },
+#endif
+ { 64000, 64000, 48000, 3, 1050000,
+ 925000, 150000, 3300000, 1750000, 0 },
+ { 261820, 130910, 130910, 0, 1275000,
+ 1175000, 173000, 3300000, 1750000, 0 },
+ { 360000, 120000, 120000, 0, 13750000,
+ 1275000, 200000, 3300000, 1750000, 0 },
+ { 392730, 130910, 130910, 0, 1475000,
+ 1375000, 225000, 3300000, 1750000, 0 },
+ { 454740, 151580, 130910, 0, 1550000,
+ 1450000, 355000, 3300000, 1750000, 0 },
+};
+
+static struct stmp3xxx_cpufreq {
+ struct cpufreq_policy policy;
+ struct regulator *regulator;
+ struct notifier_block nb;
+ struct notifier_block init_nb;
+ int freq_id;
+ int next_freq_id;
+ spinlock_t lock;
+} cpufreq_bdata;
+
+static u32 clkseq_setting;
+
+static int reg_callback(struct notifier_block *, unsigned long, void *);
+static int init_reg_callback(struct notifier_block *, unsigned long, void *);
+
+static inline void __set_new_policy(struct cpufreq_policy *policy)
+{
+ spin_lock(&cpufreq_bdata.lock);
+
+ if (policy)
+ cpufreq_bdata.policy = *policy;
+ else
+ memzero(&cpufreq_bdata.policy, sizeof(cpufreq_bdata.policy));
+
+ if (cpufreq_bdata.regulator)
+ goto out;
+
+ cpufreq_bdata.regulator = regulator_get(NULL, "cpufreq-1");
+ if (!cpufreq_bdata.regulator || IS_ERR(cpufreq_bdata.regulator))
+ cpufreq_bdata.regulator = NULL;
+ else {
+ regulator_set_mode(cpufreq_bdata.regulator,
+ REGULATOR_MODE_FAST);
+ if (cpufreq_bdata.regulator)
+ regulator_register_notifier(
+ cpufreq_bdata.regulator,
+ &cpufreq_bdata.nb);
+ }
+
+out:
+ spin_unlock(&cpufreq_bdata.lock);
+}
+
+static int stmp3xxx_verify_speed(struct cpufreq_policy *policy)
+{
+ struct clk *cpu_clk;
+
+ pr_debug("%s: entered, policy %p\n", __func__, policy);
+
+ __set_new_policy(policy);
+
+ if (policy->cpu)
+ return -EINVAL;
+
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+ cpu_clk = clk_get(NULL, "cpu");
+ if (IS_ERR(cpu_clk))
+ return PTR_ERR(cpu_clk);
+
+ pr_debug("%s: policy->min %d, policy->max %d\n",
+ __func__, policy->min, policy->max);
+ policy->min = clk_round_rate(cpu_clk, policy->min);
+ policy->max = clk_round_rate(cpu_clk, policy->max);
+ pr_debug("%s: after rounding rate: policy->min %d, policy->max %d\n",
+ __func__, policy->min, policy->max);
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+ clk_put(cpu_clk);
+
+ return 0;
+}
+
+static unsigned int stmp3xxx_getspeed(unsigned int cpu)
+{
+ struct clk *cpu_clk;
+ unsigned long rate;
+
+ pr_debug("%s: entered\n", __func__);
+ if (cpu)
+ return 0;
+
+ cpu_clk = clk_get(NULL, "cpu");
+ if (IS_ERR(cpu_clk))
+ return 0;
+ rate = clk_get_rate(cpu_clk);
+ pr_debug("%s: got cpu speed %ld\n", __func__, rate);
+ clk_put(cpu_clk);
+
+ return rate;
+}
+
+static int set_op(unsigned int target_freq)
+{
+ struct clk *cpu_clk, *ahb_clk, *emi_clk;
+ struct regulator *vddd, *vdddbo, *cur_limit, *vddio, *vdda;
+ struct cpufreq_freqs freqs;
+ int ret = 0, i;
+
+ cur_limit = cpufreq_bdata.regulator;
+ pr_debug("%s: entered\n", __func__);
+ cpu_clk = clk_get(NULL, "cpu");
+ if (IS_ERR(cpu_clk)) {
+ ret = PTR_ERR(cpu_clk);
+ goto out_cpu;
+ }
+ ahb_clk = clk_get(NULL, "hclk");
+ if (IS_ERR(ahb_clk)) {
+ ret = PTR_ERR(ahb_clk);
+ goto out_ahb;
+ }
+ emi_clk = clk_get(NULL, "emi");
+ if (IS_ERR(emi_clk)) {
+ ret = PTR_ERR(emi_clk);
+ goto out_emi;
+ }
+
+ vddd = regulator_get(NULL, "vddd");
+ vdddbo = regulator_get(NULL, "vddd_bo");
+ if (IS_ERR(vdddbo))
+ vdddbo = NULL;
+ vddio = regulator_get(NULL, "vddio");
+ if (IS_ERR(vddio)) {
+ vddio = NULL;
+ pr_warning("unable to get vddio");
+ }
+ vdda = regulator_get(NULL, "vdda");
+ if (IS_ERR(vdda)) {
+ vdda = NULL;
+ pr_warning("unable to get vddio");
+ }
+
+ freqs.old = clk_get_rate(cpu_clk);
+ freqs.cpu = 0;
+ for (i = 0; i < ARRAY_SIZE(profiles) - 1; i++) {
+ if (profiles[i].cpu <= target_freq &&
+ target_freq < profiles[i + 1].cpu) {
+ freqs.new = profiles[i].cpu;
+ cpufreq_bdata.next_freq_id = i;
+ break;
+ }
+ if (!vddd && profiles[i].cpu > freqs.old) {
+ /* can't safely set more than now */
+ freqs.new = profiles[i - 1].cpu;
+ break;
+ }
+ }
+
+ pr_debug("target_freq %d, new %d\n", target_freq, freqs.new);
+ if (i == ARRAY_SIZE(profiles) - 1) {
+ freqs.new = profiles[i].cpu;
+ cpufreq_bdata.next_freq_id = i;
+ }
+
+ if (IS_ERR(vddd)) {
+ ret = PTR_ERR(vddd);
+ if (!cpufreq_bdata.init_nb.notifier_call) {
+ /* we only register once */
+ cpufreq_bdata.init_nb.notifier_call = init_reg_callback;
+ bus_register_notifier(&platform_bus_type,
+ &cpufreq_bdata.init_nb);
+ }
+ goto out_vddd;
+ }
+
+ pr_debug("i %d: freqs.old %d, freqs.new %d\n",
+ i, freqs.old, freqs.new);
+
+ spin_lock(&cpufreq_bdata.lock);
+
+ if (freqs.old == 24000 && freqs.new > 24000) {
+ /* turn pll on */
+ HW_CLKCTRL_PLLCTRL0_SET(CLKCTRL_PLL_PWD_BIT);
+ udelay(10);
+ } else if (freqs.old > 24000 && freqs.new == 24000)
+ clkseq_setting = HW_CLKCTRL_CLKSEQ_RD();
+
+ if (cur_limit && (freqs.old < freqs.new)) {
+ ret = regulator_set_current_limit(cur_limit, profiles[i].cur, profiles[i].cur);
+ if (ret)
+ goto out_cur;
+ }
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ if (freqs.old > freqs.new) {
+ int ss = profiles[i].ss;
+ clk_set_rate(cpu_clk, profiles[i].cpu);
+ clk_set_rate(ahb_clk, profiles[i].ahb);
+ clk_set_rate(emi_clk, profiles[i].emi);
+ HW_DIGCTL_ARMCACHE_WR(BF_DIGCTL_ARMCACHE_VALID_SS(ss) |
+ BF_DIGCTL_ARMCACHE_DRTY_SS(ss) |
+ BF_DIGCTL_ARMCACHE_CACHE_SS(ss) |
+ BF_DIGCTL_ARMCACHE_DTAG_SS(ss) |
+ BF_DIGCTL_ARMCACHE_ITAG_SS(ss));
+ if (vddd && vdddbo && vddio && vdda) {
+ ret = regulator_set_voltage(vddd, profiles[i].vddd, profiles[i].vddd);
+ if (ret)
+ ret = regulator_set_voltage(vddd,
+ profiles[i].vddd,
+ profiles[i].vddd);
+ regulator_set_voltage(vdddbo, profiles[i].vddd_bo, profiles[i].vddd_bo);
+
+ ret = regulator_set_voltage(vddio, profiles[i].vddio,
+ profiles[i].vddio);
+ if (ret)
+ ret = regulator_set_voltage(vddio,
+ profiles[i].vddio,
+ profiles[i].vddio);
+ ret = regulator_set_voltage(vdda, profiles[i].vdda,
+ profiles[i].vdda);
+ if (ret)
+ ret = regulator_set_voltage(vdda,
+ profiles[i].vdda,
+ profiles[i].vdda);
+ }
+ } else {
+ int ss = profiles[i].ss;
+ if (vddd && vdddbo && vddio && vdda) {
+ ret = regulator_set_voltage(vddd, profiles[i].vddd, profiles[i].vddd);
+ if (ret)
+ ret = regulator_set_voltage(vddd,
+ profiles[i].vddd,
+ profiles[i].vddd);
+ regulator_set_voltage(vdddbo, profiles[i].vddd_bo, profiles[i].vddd_bo);
+ ret = regulator_set_voltage(vddio, profiles[i].vddio,
+ profiles[i].vddio);
+ if (ret)
+ ret = regulator_set_voltage(vddio,
+ profiles[i].vddio,
+ profiles[i].vddio);
+ ret = regulator_set_voltage(vdda, profiles[i].vdda,
+ profiles[i].vdda);
+ if (ret)
+ ret = regulator_set_voltage(vdda,
+ profiles[i].vdda,
+ profiles[i].vdda);
+ }
+ HW_DIGCTL_ARMCACHE_WR(BF_DIGCTL_ARMCACHE_VALID_SS(ss) |
+ BF_DIGCTL_ARMCACHE_DRTY_SS(ss) |
+ BF_DIGCTL_ARMCACHE_CACHE_SS(ss) |
+ BF_DIGCTL_ARMCACHE_DTAG_SS(ss) |
+ BF_DIGCTL_ARMCACHE_ITAG_SS(ss));
+ clk_set_rate(cpu_clk, profiles[i].cpu);
+ clk_set_rate(ahb_clk, profiles[i].ahb);
+ clk_set_rate(emi_clk, profiles[i].emi);
+ }
+ udelay(100);
+
+ if (freqs.old > 24000 && freqs.new == 24000) {
+ /* turn pll off */
+ HW_CLKCTRL_PLLCTRL0_CLR(CLKCTRL_PLL_PWD_BIT);
+ HW_CLKCTRL_CLKSEQ_WR(CLKCTRL_PLL_BYPASS);
+ } else if (freqs.old == 24000 && freqs.new > 24000)
+ HW_CLKCTRL_CLKSEQ_WR(clkseq_setting);
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ if (cur_limit && (freqs.old > freqs.new)) /* will not fail */
+ regulator_set_current_limit(cur_limit, profiles[i].cur, profiles[i].cur);
+
+ cpufreq_bdata.freq_id = i;
+
+out_cur:
+ spin_unlock(&cpufreq_bdata.lock);
+ if (vddd)
+ regulator_put(vddd);
+ if (vddio)
+ regulator_put(vddio);
+ if (vdda)
+ regulator_put(vdda);
+out_vddd:
+ clk_put(emi_clk);
+out_emi:
+ clk_put(ahb_clk);
+out_ahb:
+ clk_put(cpu_clk);
+out_cpu:
+
+ return ret;
+}
+
+static int stmp3xxx_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ return set_op(target_freq);
+}
+
+static int reg_callback(struct notifier_block *self, unsigned long event,
+ void *data)
+{
+ struct stmp3xxx_cpufreq *_temp =
+ container_of(self, struct stmp3xxx_cpufreq, nb);
+ struct cpufreq_policy *policy = &_temp->policy;
+ int max_prof = ARRAY_SIZE(profiles) - 1;
+ int ret = -EINVAL;
+
+ pr_debug("%s: entered, _temp %p, policy %p, cpu %d, freq_id %d\n",
+ __func__, _temp, policy, policy->cpu, _temp->freq_id);
+
+ if (policy)
+ policy = cpufreq_cpu_get(policy->cpu);
+ if (!policy) {
+ printk(KERN_ERR "%s: couldn't get cpufreq policy\n", __func__);
+ goto out;
+ }
+
+ /* FIXME: Need a lock: set policy by user VS async USB event */
+ switch (event) {
+ case STMP3XXX_REG5V_IS_USB:
+ pr_debug("%s: limiting max_freq to %d\n", __func__,
+ profiles[max_prof - 1].cpu);
+ policy->user_policy.min = profiles[0].cpu;
+ policy->user_policy.max = profiles[max_prof - 1].cpu;
+ if (_temp->freq_id > max_prof - 1)
+ set_op(profiles[max_prof - 1].cpu);
+ break;
+
+ case STMP3XXX_REG5V_NOT_USB:
+ pr_debug("%s: undo limiting max_freq to %d\n", __func__,
+ profiles[max_prof - 1].cpu);
+ policy->user_policy.min = profiles[0].cpu;
+ policy->user_policy.max = profiles[max_prof].cpu;
+ break;
+
+ default:
+ pr_info("%s: unknown event %ld\n", __func__, event);
+ break;
+ }
+ cpufreq_cpu_put(policy);
+ ret = cpufreq_update_policy(policy->cpu);
+out:
+ return ret;
+}
+
+static int init_reg_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ int ret;
+ struct stmp3xxx_cpufreq *_temp =
+ container_of(self, struct stmp3xxx_cpufreq, init_nb);
+
+ ret = set_op(profiles[_temp->next_freq_id].cpu);
+ if (ret == 0)
+ bus_unregister_notifier(&platform_bus_type,
+ &cpufreq_bdata.init_nb);
+ return ret;
+}
+
+static int __init stmp3xxx_cpu_init(struct cpufreq_policy *policy)
+{
+ struct clk *cpu_clk = clk_get(NULL, "cpu");
+
+ pr_debug("%s: entered\n", __func__);
+ if (IS_ERR(cpu_clk))
+ return PTR_ERR(cpu_clk);
+
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ policy->cur = policy->min = policy->max = clk_get_rate(cpu_clk);
+
+ pr_debug("got CPU clock rate %d\n", policy->cur);
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+ policy->cpuinfo.min_freq = profiles[0].cpu;
+ policy->cpuinfo.max_freq = profiles[ARRAY_SIZE(profiles) - 1].cpu;
+ policy->cpuinfo.transition_latency = 1000000; /* 1 ms, assumed */
+ clk_put(cpu_clk);
+
+ return 0;
+}
+
+static struct cpufreq_driver stmp3xxx_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = stmp3xxx_verify_speed,
+ .target = stmp3xxx_target,
+ .get = stmp3xxx_getspeed,
+ .init = stmp3xxx_cpu_init,
+ .name = "stmp3xxx",
+};
+
+static int __init stmp3xxx_cpufreq_init(void)
+{
+ spin_lock_init(&cpufreq_bdata.lock);
+ cpufreq_bdata.nb.notifier_call = reg_callback;
+ return cpufreq_register_driver(&stmp3xxx_driver);
+}
+
+static int __init stmp3xxx_reg_init(void)
+{
+ pr_debug("%s: enter\n", __func__);
+ if (!cpufreq_bdata.regulator)
+ __set_new_policy(&cpufreq_bdata.policy);
+
+ if (cpufreq_bdata.regulator)
+ regulator_set_current_limit(cpufreq_bdata.regulator,
+ profiles[cpufreq_bdata.freq_id].cur,
+ profiles[cpufreq_bdata.freq_id].cur);
+ return 0 ;
+}
+
+arch_initcall(stmp3xxx_cpufreq_init);
+late_initcall(stmp3xxx_reg_init);
diff --git a/arch/arm/mach-stmp3xxx/dcp-bootstream.c b/arch/arm/mach-stmp3xxx/dcp-bootstream.c
new file mode 100644
index 000000000000..08776ad0e2d9
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/dcp-bootstream.c
@@ -0,0 +1,298 @@
+/*
+ * Freescale STMP378X DCP driver for bootstream update. Only handles the OTP KEY
+ * case and can only encrypt/decrypt.
+ *
+ * Author: Pantelis Antoniou <pantelis@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sysdev.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <mach/stmp3xxx_regs.h>
+#include <mach/stmp3xxx.h>
+#include <mach/regs-dcp.h>
+#include <mach/dcp_bootstream_ioctl.h>
+
+/* use this channel (same as the ROM) */
+#define ROM_DCP_CHAN 3
+
+/* Defines the channel mask for the rom dcp channel */
+#define ROM_DCP_CHAN_MASK (1 << ROM_DCP_CHAN)
+
+/* Defines the initialization value for the dcp control register */
+#define DCP_CTRL_INIT \
+ (BM_DCP_CTRL_GATHER_RESIDUAL_WRITES | \
+ BM_DCP_CTRL_ENABLE_CONTEXT_CACHING)
+
+/* Defines the initialization value for the dcp channel control register */
+#define DCP_CHANNELCTRL_INIT \
+ BF_DCP_CHANNELCTRL_ENABLE_CHANNEL(ROM_DCP_CHAN_MASK)
+
+/* DCP work packet 1 value used to calculate CBC-MAC over the image header */
+#define DCP_PKT1_ENCRYPT \
+ (BM_DCP_PACKET1_DECR_SEMAPHORE | \
+ BM_DCP_PACKET1_ENABLE_CIPHER | \
+ BM_DCP_PACKET1_CIPHER_ENCRYPT | \
+ BM_DCP_PACKET1_CIPHER_INIT | \
+ BM_DCP_PACKET1_OTP_KEY)
+
+/* DCP work packet 1 value used to decrypt DEK in key dictionary */
+#define DCP_PKT1_DECRYPT \
+ (BM_DCP_PACKET1_DECR_SEMAPHORE | \
+ BM_DCP_PACKET1_ENABLE_CIPHER | \
+ BM_DCP_PACKET1_CIPHER_INIT | \
+ BM_DCP_PACKET1_OTP_KEY)
+
+/* DCP (decryption) work packet definition */
+struct hw_dcp_packet {
+ uint32_t pNext; /* next dcp work packet address */
+ uint32_t pkt1; /* dcp work packet 1 (control 0) */
+ uint32_t pkt2; /* dcp work packet 2 (control 1) */
+ uint32_t pSrc; /* source buffer address */
+ uint32_t pDst; /* destination buffer address */
+ uint32_t size; /* buffer size in bytes */
+ uint32_t pPayload; /* payload buffer address */
+ uint32_t stat; /* dcp status (written by dcp) */
+};
+
+struct dma_area {
+ struct hw_dcp_packet hw_packet;
+ uint16_t block[16];
+};
+
+struct stmp3xxx_dcp_bootstream_data {
+ struct device *dev;
+ struct dma_area *dma_area;
+ dma_addr_t dma_area_phys;
+};
+
+/* Only one instance allowed, so this is OK */
+static struct stmp3xxx_dcp_bootstream_data *global_dbd;
+
+static int stmp3xxx_dcp_bootstream_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct stmp3xxx_dcp_bootstream_data *dbd = global_dbd;
+ struct dma_area *da = dbd->dma_area;
+ void __user *argp = (void __user *)arg;
+ unsigned long timeout;
+
+ /* be paranoid */
+ if (dbd == NULL)
+ return -EBADF;
+
+ if (cmd != DBS_ENC && cmd != DBS_DEC)
+ return -EINVAL;
+
+ /* copy to (aligned) block */
+ if (copy_from_user(da->block, argp, 16))
+ return -EFAULT;
+
+ /* Soft reset and remove the clock gate */
+ HW_DCP_CTRL_SET(BM_DCP_CTRL_SFTRST);
+
+ /* At 24Mhz, it takes no more than 4 clocks (160 ns) Maximum for
+ * the part to reset, reading the register twice should
+ * be sufficient to get 4 clks delay.
+ */
+ HW_DCP_CTRL_RD();
+ HW_DCP_CTRL_RD();
+
+ HW_DCP_CTRL_CLR(BM_DCP_CTRL_SFTRST | BM_DCP_CTRL_CLKGATE);
+
+ /* Initialize control registers */
+ HW_DCP_CTRL_WR(DCP_CTRL_INIT);
+ HW_DCP_CHANNELCTRL_WR(DCP_CHANNELCTRL_INIT);
+
+ /* The loader does not enable context switching. Give the context
+ * buffer pointer an illegal address so if context switching is
+ * inadvertantly enabled, the dcp will return an error instead of
+ * trashing good memory. The dcp dma cannot access rom, so any rom
+ * address will do.
+ */
+ HW_DCP_CONTEXT_WR(0xFFFF0000);
+
+ HW_DCP_CHnSTAT_CLR(ROM_DCP_CHAN, -1);
+ HW_DCP_STAT_CLR(-1);
+
+ da->hw_packet.pNext = 0;
+ da->hw_packet.pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE |
+ BM_DCP_PACKET1_ENABLE_CIPHER | BM_DCP_PACKET1_OTP_KEY |
+ BM_DCP_PACKET1_INTERRUPT |
+ (cmd == DBS_ENC ? BM_DCP_PACKET1_CIPHER_ENCRYPT : 0);
+ da->hw_packet.pkt2 = BF_DCP_PACKET2_CIPHER_CFG(0) |
+ BF_DCP_PACKET2_KEY_SELECT(0) |
+ BF_DCP_PACKET2_CIPHER_MODE(BV_DCP_PACKET2_CIPHER_MODE__ECB) |
+ BF_DCP_PACKET2_CIPHER_SELECT(
+ BV_DCP_PACKET2_CIPHER_SELECT__AES128);
+ da->hw_packet.pSrc = dbd->dma_area_phys +
+ offsetof(struct dma_area, block);
+ da->hw_packet.pDst = da->hw_packet.pSrc; /* in-place */
+ da->hw_packet.size = 16;
+ da->hw_packet.pPayload = 0;
+ da->hw_packet.stat = 0;
+
+ /* Load the work packet pointer and bump the channel semaphore */
+ HW_DCP_CHnCMDPTR_WR(ROM_DCP_CHAN, dbd->dma_area_phys +
+ offsetof(struct dma_area, hw_packet));
+ HW_DCP_CHnSEMA_WR(ROM_DCP_CHAN, BF_DCP_CHnSEMA_INCREMENT(1));
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout) &&
+ (HW_DCP_STAT_RD() & BF_DCP_STAT_IRQ(ROM_DCP_CHAN_MASK)) == 0)
+ cpu_relax();
+
+ if (!time_before(jiffies, timeout)) {
+ dev_err(dbd->dev, "Timeout while waiting STAT\n");
+ return -ETIMEDOUT;
+ }
+
+ if ((HW_DCP_CHnSTAT_RD(ROM_DCP_CHAN) & 0xff) != 0) {
+ dev_err(dbd->dev, "Channel stat error 0x%02x\n",
+ HW_DCP_CHnSTAT_RD(ROM_DCP_CHAN) & 0xff);
+ return -EFAULT;
+ }
+
+ if (copy_to_user(argp, da->block, 16))
+ return -EFAULT;
+
+ return 0;
+}
+
+static struct file_operations stmp3xxx_dcp_bootstream_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = stmp3xxx_dcp_bootstream_ioctl,
+};
+
+static struct miscdevice stmp3xxx_dcp_bootstream_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "dcpboot",
+ .fops = &stmp3xxx_dcp_bootstream_fops,
+};
+
+static int __devinit stmp3xxx_dcp_bootstream_probe(struct platform_device *pdev)
+{
+ struct stmp3xxx_dcp_bootstream_data *dbd;
+ int err;
+
+ /* we only allow a single device */
+ if (global_dbd != NULL)
+ return -ENODEV;
+
+ dbd = kzalloc(sizeof(*dbd), GFP_KERNEL);
+ if (dbd == NULL)
+ return -ENOMEM;
+ memset(dbd, 0, sizeof(*dbd));
+
+ dbd->dev = &pdev->dev;
+ platform_set_drvdata(pdev, dbd);
+
+ err = misc_register(&stmp3xxx_dcp_bootstream_misc);
+ if (err != 0) {
+ dev_err(&pdev->dev, "Unable to register misc device\n");
+ goto err_done;
+ }
+
+ dbd->dma_area = dma_alloc_coherent(&pdev->dev, sizeof(*dbd->dma_area),
+ &dbd->dma_area_phys, GFP_KERNEL);
+ if (dbd->dma_area == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate DMAable memory\n");
+ goto err_dereg;
+ }
+
+ global_dbd = dbd;
+
+ return 0;
+
+err_dereg:
+ misc_deregister(&stmp3xxx_dcp_bootstream_misc);
+err_done:
+ kfree(dbd);
+ return err;
+}
+
+static int stmp3xxx_dcp_bootstream_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_dcp_bootstream_data *dbd;
+
+ dbd = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, NULL);
+
+ dma_free_coherent(&pdev->dev, sizeof(*dbd->dma_area),
+ dbd->dma_area, dbd->dma_area_phys);
+ misc_deregister(&stmp3xxx_dcp_bootstream_misc);
+
+ kfree(dbd);
+
+ global_dbd = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stmp3xxx_dcp_bootstream_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int stmp3xxx_dcp_bootstream_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define stmp3xxx_dcp_bootstream_suspend NULL
+#define stmp3xxx_dcp_bootstream_resume NULL
+#endif
+
+static struct platform_driver stmp3xxx_dcp_bootstream_driver = {
+ .probe = stmp3xxx_dcp_bootstream_probe,
+ .remove = stmp3xxx_dcp_bootstream_remove,
+ .suspend = stmp3xxx_dcp_bootstream_suspend,
+ .resume = stmp3xxx_dcp_bootstream_resume,
+ .driver = {
+ .name = "stmp3xxx-dcpboot",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmp3xxx_dcp_bootstream_init(void)
+{
+ return platform_driver_register(&stmp3xxx_dcp_bootstream_driver);
+}
+
+static void __exit stmp3xxx_dcp_bootstream_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_dcp_bootstream_driver);
+}
+
+MODULE_AUTHOR("Pantelis Antoniou <pantelis@embeddedalley.com>");
+MODULE_DESCRIPTION("DCP bootstream driver");
+MODULE_LICENSE("GPL");
+
+module_init(stmp3xxx_dcp_bootstream_init);
+module_exit(stmp3xxx_dcp_bootstream_exit);
diff --git a/arch/arm/mach-stmp3xxx/devices.c b/arch/arm/mach-stmp3xxx/devices.c
new file mode 100644
index 000000000000..a20dba31d352
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/devices.c
@@ -0,0 +1,592 @@
+/*
+ * Freescale STMP37XX/STMP378X platform devices
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/mtd/partitions.h>
+#include <linux/fsl_devices.h>
+#include <asm/setup.h>
+#include <asm/dma.h>
+
+#include <mach/gpio.h>
+#include <mach/regs-uartapp.h>
+#include <mach/regs-gpmi.h>
+#include <mach/regs-i2c.h>
+#include <mach/regs-ssp.h>
+#include <mach/regs-power.h>
+#include <mach/regs-pxp.h>
+#include <mach/regs-usbctrl.h>
+#include <mach/regs-digctl.h>
+#include <mach/regs-ocotp.h>
+#include <mach/ddi_bc.h>
+
+#include <mach/stmp3xxx.h>
+#include <mach/gpmi.h>
+#include <mach/lcdif.h>
+
+#include "common.h"
+
+static u64 common_dmamask = ~(u32)0;
+#define COMMON_COHERENT_DMAMASK (u32)0xffffffffU
+
+static struct resource appuart_resources[] = {
+ {
+ .start = IRQ_UARTAPP_INTERNAL,
+ .end = IRQ_UARTAPP_INTERNAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = IRQ_UARTAPP_RX_DMA,
+ .end = IRQ_UARTAPP_RX_DMA,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = IRQ_UARTAPP_TX_DMA,
+ .end = IRQ_UARTAPP_TX_DMA,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = REGS_UARTAPP_BASE,
+ .end = REGS_UARTAPP_BASE + 0xA0,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ /* Rx DMA channel */
+ .start = STMP3xxx_DMA(6, STMP3XXX_BUS_APBX),
+ .end = STMP3xxx_DMA(6, STMP3XXX_BUS_APBX),
+ .flags = IORESOURCE_DMA,
+ },
+ {
+ /* Tx DMA channel */
+ .start = STMP3xxx_DMA(7, STMP3XXX_BUS_APBX),
+ .end = STMP3xxx_DMA(7, STMP3XXX_BUS_APBX),
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static int appuart_pinmux(int req, int id)
+{
+ if (req)
+ return stmp3xxx_request_pin_group(&appuart_pins[id], "appuart");
+ else
+ stmp3xxx_release_pin_group(&appuart_pins[id], "appuart");
+ return 0;
+}
+
+struct platform_device stmp3xxx_appuart = {
+ .name = "stmp37xx-appuart",
+ .id = 0,
+ .resource = appuart_resources,
+ .num_resources = ARRAY_SIZE(appuart_resources),
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ .platform_data = appuart_pinmux,
+ },
+};
+
+static void dbguart_pinmux(int req, int id)
+{
+ if (req)
+ stmp3xxx_request_pin_group(&dbguart_pins[id], "dbguart");
+ else
+ stmp3xxx_release_pin_group(&dbguart_pins[id], "dbguart");
+}
+
+struct platform_device stmp3xxx_dbguart = {
+ .name = "stmp37xx-dbguart",
+ .dev = {
+ .platform_data = dbguart_pinmux,
+ },
+};
+
+struct platform_device stmp3xxx_watchdog = {
+ .name = "stmp3xxx_wdt",
+ .id = -1,
+};
+
+static struct resource ts_resource[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_TOUCH_DETECT,
+ .end = IRQ_TOUCH_DETECT,
+ },
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_LRADC_CH5,
+ .end = IRQ_LRADC_CH5,
+ },
+};
+
+struct platform_device stmp3xxx_touchscreen = {
+ .name = "stmp3xxx_ts",
+ .id = -1,
+ .resource = ts_resource,
+ .num_resources = ARRAY_SIZE(ts_resource),
+};
+
+/*
+ * Keypad device
+ */
+static struct resource keyboard_resource[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_LRADC_CH0,
+ .end = IRQ_LRADC_CH0,
+ },
+};
+
+struct platform_device stmp3xxx_keyboard = {
+ .name = "stmp3xxx-keyboard",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(keyboard_resource),
+ .resource = keyboard_resource,
+};
+
+static struct resource gpmi_resources[] = {
+ [0] = {
+ .flags = IORESOURCE_MEM,
+ .start = REGS_GPMI_BASE,
+ .end = REGS_GPMI_BASE + 0x100,
+ },
+ [1] = {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_GPMI_DMA,
+ .end = IRQ_GPMI_DMA,
+ },
+ [2] = {
+ .flags = IORESOURCE_DMA,
+ .start = STMP3xxx_DMA(4, STMP3XXX_BUS_APBH),
+ .end = STMP3xxx_DMA(8, STMP3XXX_BUS_APBH),
+ },
+};
+
+struct platform_device stmp3xxx_gpmi = {
+ .name = "gpmi",
+ .id = -1,
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ },
+ .resource = gpmi_resources,
+ .num_resources = ARRAY_SIZE(gpmi_resources),
+};
+
+static struct resource i2c_resources[] = {
+ [0] = {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_I2C_ERROR,
+ .end = IRQ_I2C_ERROR,
+ },
+ [1] = {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_I2C_DMA,
+ .end = IRQ_I2C_DMA,
+ },
+
+};
+
+struct platform_device stmp378x_i2c = {
+ .name = "i2c_stmp",
+ .id = 0,
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ },
+ .resource = i2c_resources,
+ .num_resources = ARRAY_SIZE(i2c_resources),
+};
+
+struct resource mmc1_resource[] = {
+ [0] = {
+ .flags = IORESOURCE_MEM,
+ .start = REGS_SSP_BASE,
+ .end = REGS_SSP_BASE + 0x4000,
+ },
+ [1] = {
+ .flags = IORESOURCE_DMA,
+ .start = STMP3xxx_DMA(1, STMP3XXX_BUS_APBH),
+ .end = STMP3xxx_DMA(1, STMP3XXX_BUS_APBH),
+ },
+ [2] = {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_SSP1_DMA,
+ .end = IRQ_SSP1_DMA,
+ },
+ [3] = {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_SSP_ERROR,
+ .end = IRQ_SSP_ERROR,
+ },
+};
+
+struct platform_device stmp3xxx_mmc = {
+ .name = "stmp3xxx-mmc",
+ .id = 1,
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ },
+ .resource = mmc1_resource,
+ .num_resources = ARRAY_SIZE(mmc1_resource),
+};
+
+struct platform_device stmp3xxx_rtc = {
+ .name = "stmp3xxx-rtc",
+ .id = -1,
+};
+
+static struct resource fb_resource[] = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_LCDIF_DMA,
+ .end = IRQ_LCDIF_DMA,
+ },
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_LCDIF_ERROR,
+ .end = IRQ_LCDIF_ERROR,
+ },
+};
+
+static struct stmp3xxx_platform_fb_data stmp3xxx_framebuffer_pdata = {
+ .list = LIST_HEAD_INIT(stmp3xxx_framebuffer_pdata.list),
+};
+
+struct platform_device stmp3xxx_framebuffer = {
+ .name = "stmp3xxx-fb",
+ .id = -1,
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ .platform_data = &stmp3xxx_framebuffer_pdata,
+ },
+ .num_resources = ARRAY_SIZE(fb_resource),
+ .resource = fb_resource,
+};
+
+/* PxP */
+static struct resource pxp_resource[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ .start = REGS_PXP_BASE,
+ .end = REGS_PXP_BASE + 0x400,
+ },
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_PXP,
+ .end = IRQ_PXP,
+ },
+};
+
+struct platform_device stmp3xxx_pxp = {
+ .name = "stmp3xxx-pxp",
+ .id = -1,
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ },
+ .num_resources = ARRAY_SIZE(pxp_resource),
+ .resource = pxp_resource,
+};
+
+/* SSP 1&2 */
+
+struct resource ssp1_resources[] = {
+ [0] = {
+ .start = REGS_SSP_BASE,
+ .end = REGS_SSP_BASE + 0x200,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_SSP1_DMA,
+ .end = IRQ_SSP1_DMA,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = STMP3xxx_DMA(1, STMP3XXX_BUS_APBH),
+ .end = STMP3xxx_DMA(1, STMP3XXX_BUS_APBH),
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+struct resource ssp2_resources[] = {
+ [0] = {
+ .start = REGS_SSP_BASE + 0x800000,
+ .end = REGS_SSP_BASE + 0x200 + 0x800000,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_SSP2_DMA,
+ .end = IRQ_SSP2_DMA,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = STMP3xxx_DMA(2, STMP3XXX_BUS_APBH),
+ .end = STMP3xxx_DMA(2, STMP3XXX_BUS_APBH),
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+struct platform_device stmp3xxx_spi1 = {
+ .name = "stmp37xx_ssp",
+ .id = 1,
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ },
+ .resource = ssp1_resources,
+ .num_resources = ARRAY_SIZE(ssp1_resources),
+};
+
+struct platform_device stmp3xxx_spi2 = {
+ .name = "stmp37xx_ssp",
+ .id = 2,
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ },
+ .resource = ssp2_resources,
+ .num_resources = ARRAY_SIZE(ssp2_resources),
+};
+
+#define CMDLINE_DEVICE_CHOOSE(name, dev1, dev2) \
+ static char *cmdline_device_##name; \
+ static int cmdline_device_##name##_setup(char *dev) \
+ { \
+ cmdline_device_##name = dev + 1; \
+ return 0; \
+ } \
+ __setup(#name, cmdline_device_##name##_setup); \
+ int stmp3xxx_##name##_device_register(void) \
+ { \
+ struct platform_device *d = NULL; \
+ if (!cmdline_device_##name || \
+ !strcmp(cmdline_device_##name, #dev1)) \
+ d = &stmp3xxx_##dev1; \
+ else if (!strcmp(cmdline_device_##name, #dev2)) \
+ d = &stmp3xxx_##dev2; \
+ else \
+ printk(KERN_ERR"Unknown %s assignment '%s'.\n", \
+ #name, cmdline_device_##name); \
+ return d ? platform_device_register(d) : -ENOENT; \
+ }
+
+CMDLINE_DEVICE_CHOOSE(ssp1, mmc, spi1)
+CMDLINE_DEVICE_CHOOSE(ssp2, gpmi, spi2)
+
+struct platform_device stmp3xxx_backlight = {
+ .name = "stmp3xxx-bl",
+ .id = -1,
+};
+
+static const struct stmp3xxx_persistent_bit_config
+stmp3xxx_persistent_bit_tab[] = {
+ { .reg = 0, .start = 0, .width = 1,
+ .name = "CLOCKSOURCE" },
+ { .reg = 0, .start = 1, .width = 1,
+ .name = "ALARM_WAKE_EN" },
+ { .reg = 0, .start = 2, .width = 1,
+ .name = "ALARM_EN" },
+ { .reg = 0, .start = 3, .width = 1,
+ .name = "CLK_SECS" },
+ { .reg = 0, .start = 4, .width = 1,
+ .name = "XTAL24MHZ_PWRUP" },
+ { .reg = 0, .start = 5, .width = 1,
+ .name = "XTAL32MHZ_PWRUP" },
+ { .reg = 0, .start = 6, .width = 1,
+ .name = "XTAL32_FREQ" },
+ { .reg = 0, .start = 7, .width = 1,
+ .name = "ALARM_WAKE" },
+ { .reg = 0, .start = 8, .width = 5,
+ .name = "MSEC_RES" },
+ { .reg = 0, .start = 13, .width = 1,
+ .name = "DISABLE_XTALOK" },
+ { .reg = 0, .start = 14, .width = 2,
+ .name = "LOWERBIAS" },
+ { .reg = 0, .start = 16, .width = 1,
+ .name = "DISABLE_PSWITCH" },
+ { .reg = 0, .start = 17, .width = 1,
+ .name = "AUTO_RESTART" },
+ { .reg = 0, .start = 18, .width = 14,
+ .name = "SPARE_ANALOG" },
+
+ { .reg = 1, .start = 0, .width = 1,
+ .name = "FORCE_RECOVERY" },
+ { .reg = 1, .start = 1, .width = 1,
+ .name = "NAND_SECONDARY_BOOT" },
+ { .reg = 1, .start = 2, .width = 1,
+ .name = "NAND_SDK_BLOCK_REWRITE" },
+ { .reg = 1, .start = 3, .width = 1,
+ .name = "SD_SPEED_ENABLE" },
+ { .reg = 1, .start = 4, .width = 1,
+ .name = "SD_INIT_SEQ_1_DISABLE" },
+ { .reg = 1, .start = 5, .width = 1,
+ .name = "SD_CMD0_DISABLE" },
+ { .reg = 1, .start = 6, .width = 1,
+ .name = "SD_INIT_SEQ_2_ENABLE" },
+ { .reg = 1, .start = 7, .width = 1,
+ .name = "OTG_ATL_ROLE_BIT" },
+ { .reg = 1, .start = 8, .width = 1,
+ .name = "OTG_HNP_BIT" },
+ { .reg = 1, .start = 9, .width = 1,
+ .name = "USB_LOW_POWER_MODE" },
+ { .reg = 1, .start = 10, .width = 1,
+ .name = "SKIP_CHECKDISK" },
+ { .reg = 1, .start = 11, .width = 1,
+ .name = "USB_BOOT_PLAYER_MODE" },
+ { .reg = 1, .start = 12, .width = 1,
+ .name = "ENUMERATE_500MA_TWICE" },
+ { .reg = 1, .start = 13, .width = 19,
+ .name = "SPARE_GENERAL" },
+
+ { .reg = 2, .start = 0, .width = 32,
+ .name = "SPARE_2" },
+ { .reg = 3, .start = 0, .width = 32,
+ .name = "SPARE_3" },
+ { .reg = 4, .start = 0, .width = 32,
+ .name = "SPARE_4" },
+ { .reg = 5, .start = 0, .width = 32,
+ .name = "SPARE_5" },
+};
+
+static struct stmp3xxx_platform_persistent_data stmp3xxx_persistent_data = {
+ .bit_config_tab = stmp3xxx_persistent_bit_tab,
+ .bit_config_cnt = ARRAY_SIZE(stmp3xxx_persistent_bit_tab),
+};
+
+struct platform_device stmp3xxx_persistent = {
+ .name = "stmp3xxx-persistent",
+ .id = -1,
+ .dev.platform_data = &stmp3xxx_persistent_data,
+};
+
+struct platform_device stmp3xxx_dcp_bootstream = {
+ .name = "stmp3xxx-dcpboot",
+ .id = -1,
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ },
+};
+
+static struct resource dcp_resources[] = {
+ {
+ .start = IRQ_DCP_VMI,
+ .end = IRQ_DCP_VMI,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = IRQ_DCP,
+ .end = IRQ_DCP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device stmp3xxx_dcp = {
+ .name = "stmp3xxx-dcp",
+ .id = -1,
+ .resource = dcp_resources,
+ .num_resources = ARRAY_SIZE(dcp_resources),
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ },
+};
+
+struct platform_device stmp3xxx_mtest = {
+ .name = "stmp3xxx-mtest",
+ .id = -1,
+ .dev = {
+ .dma_mask = &common_dmamask,
+ .coherent_dma_mask = COMMON_COHERENT_DMAMASK,
+ },
+};
+
+struct platform_device stmp3xxx_rotdec = {
+ .name = "stmp3xxx-rotdec",
+ .id = -1,
+};
+
+static struct resource battery_resource[] = {
+ [0] = {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_VDD5V,
+ .end = IRQ_VDD5V,
+ },
+};
+
+static ddi_bc_Cfg_t battery_data = {
+ .u32StateMachinePeriod = 100, /* ms */
+ .u16CurrentRampSlope = 75, /* mA/s */
+ .u16ConditioningThresholdVoltage = 2900, /* mV */
+ .u16ConditioningMaxVoltage = 3000, /* mV */
+ .u16ConditioningCurrent = 60, /* mA */
+ .u32ConditioningTimeout = 4*60*60*1000, /* ms (4 hours) */
+ .u16ChargingVoltage = 4200, /* mV */
+ /* FIXME: the current comparator could have h/w bugs in current
+ * detection through POWER_STS.CHRGSTS bit */
+ .u16ChargingCurrent = 600, /* mA 600 */
+ .u16ChargingThresholdCurrent = 60, /* mA 60 */
+ .u32ChargingTimeout = 4*60*60*1000,/* ms (4 hours) */
+ .u32TopOffPeriod = 30*60*1000, /* ms (30 minutes) */
+ .useInternalBias = 0, /* ext bias current */
+ .monitorDieTemp = 1, /* Monitor the die */
+ .u8DieTempHigh = 115, /* deg centigrade */
+ .u8DieTempLow = 96, /* deg centigrade */
+ .u16DieTempSafeCurrent = 400, /* mA */
+ .monitorBatteryTemp = 0, /* Monitor the battery*/
+ .u8BatteryTempChannel = 1, /* LRADC 1 */
+ .u16BatteryTempHigh = 642, /* Unknown units */
+ .u16BatteryTempLow = 497, /* Unknown units */
+ .u16BatteryTempSafeCurrent = 0, /* mA */
+};
+
+struct platform_device stmp3xxx_battery = {
+ .name = "stmp3xxx-battery",
+ .resource = battery_resource,
+ .num_resources = ARRAY_SIZE(battery_resource),
+ .dev = {
+ .platform_data = &battery_data,
+ }
+};
+
+struct resource viim_resources[] = {
+ [0] = {
+ .start = REGS_DIGCTL_BASE_PHYS,
+ .end = REGS_DIGCTL_BASE_PHYS + PAGE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = REGS_OCOTP_BASE_PHYS,
+ .end = REGS_OCOTP_BASE_PHYS + PAGE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device stmp3xxx_viim = {
+ .name = "mxs_viim",
+ .id = 0,
+ .resource = viim_resources,
+ .num_resources = ARRAY_SIZE(viim_resources),
+};
diff --git a/arch/arm/mach-stmp3xxx/dma.c b/arch/arm/mach-stmp3xxx/dma.c
new file mode 100644
index 000000000000..49ed790b92d3
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/dma.c
@@ -0,0 +1,509 @@
+/*
+ * DMA helper routines for Freescale STMP37XX/STMP378X
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/sysdev.h>
+#include <linux/cpufreq.h>
+#include <asm/page.h>
+#include <mach/dma.h>
+#include <mach/regs-apbx.h>
+#include <mach/regs-apbh.h>
+
+static const size_t pool_item_size = sizeof(struct stmp3xxx_dma_command);
+static const size_t pool_alignment = 8;
+static struct stmp3xxx_dma_user {
+ void *pool;
+ int inuse;
+ char *name;
+} channels[MAX_DMA_CHANNELS];
+
+static inline int dmach(int ch)
+{
+ return ch % 16;
+}
+
+static inline int dmabus(int ch)
+{
+ return ch / 16;
+}
+
+#define IS_VALID_CHANNEL(ch) ((ch) >= 0 && (ch) < MAX_DMA_CHANNELS)
+#define IS_USED(ch) (channels[ch].inuse)
+
+int stmp3xxx_dma_request(int ch, struct device *dev, char *name)
+{
+ struct stmp3xxx_dma_user *user;
+ int err = 0;
+
+ user = channels + ch;
+ if (!IS_VALID_CHANNEL(ch)) {
+ err = -ENODEV;
+ goto out;
+ }
+ if (IS_USED(ch)) {
+ err = -EBUSY;
+ goto out;
+ }
+ /* Create a pool to allocate dma commands from */
+ user->pool = dma_pool_create(name, dev, pool_item_size,
+ pool_alignment, PAGE_SIZE);
+ if (user->pool == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+ user->name = name;
+ user->inuse++;
+out:
+ return err;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_request);
+
+int stmp3xxx_dma_release(int ch)
+{
+ struct stmp3xxx_dma_user *user = channels + ch;
+ int err = 0;
+
+ if (!IS_VALID_CHANNEL(ch)) {
+ err = -ENODEV;
+ goto out;
+ }
+ if (!IS_USED(ch)) {
+ err = -EBUSY;
+ goto out;
+ }
+ BUG_ON(user->pool == NULL);
+ dma_pool_destroy(user->pool);
+ user->inuse--;
+out:
+ return err;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_release);
+
+int stmp3xxx_dma_read_semaphore(int channel)
+{
+ int sem = -1;
+
+ switch (dmabus(channel)) {
+ case STMP3XXX_BUS_APBH:
+ sem =
+ (HW_APBH_CHn_SEMA_RD(dmach(channel)) &
+ BM_APBH_CHn_SEMA_PHORE) >> BP_APBH_CHn_SEMA_PHORE;
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ sem =
+ (HW_APBX_CHn_SEMA_RD(dmach(channel)) &
+ BM_APBX_CHn_SEMA_PHORE) >> BP_APBX_CHn_SEMA_PHORE;
+ break;
+ default:
+ BUG();
+ }
+ return sem;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_read_semaphore);
+
+int stmp3xxx_dma_allocate_command(int channel,
+ struct stmp3xxx_dma_descriptor *descriptor)
+{
+ struct stmp3xxx_dma_user *user = channels + channel;
+ int err = 0;
+
+ if (!IS_VALID_CHANNEL(channel)) {
+ err = -ENODEV;
+ goto out;
+ }
+ if (!IS_USED(channel)) {
+ err = -EBUSY;
+ goto out;
+ }
+ if (descriptor == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Allocate memory for a command from the buffer */
+ descriptor->command =
+ dma_pool_alloc(user->pool, GFP_KERNEL, &descriptor->handle);
+
+ /* Check it worked */
+ if (!descriptor->command) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memset(descriptor->command, 0, pool_item_size);
+out:
+ WARN_ON(err);
+ return err;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_allocate_command);
+
+int stmp3xxx_dma_free_command(int channel,
+ struct stmp3xxx_dma_descriptor *descriptor)
+{
+ int err = 0;
+
+ if (!IS_VALID_CHANNEL(channel)) {
+ err = -ENODEV;
+ goto out;
+ }
+ if (!IS_USED(channel)) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ /* Return the command memory to the pool */
+ dma_pool_free(channels[channel].pool, descriptor->command,
+ descriptor->handle);
+
+ /* Initialise descriptor so we're not tempted to use it */
+ descriptor->command = 0;
+ descriptor->handle = 0;
+ descriptor->virtual_buf_ptr = 0;
+ descriptor->next_descr = 0;
+
+ WARN_ON(err);
+out:
+ return err;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_free_command);
+
+void stmp3xxx_dma_go(int channel,
+ struct stmp3xxx_dma_descriptor *head, u32 semaphore)
+{
+ int ch = dmach(channel);
+
+ switch (dmabus(channel)) {
+ case STMP3XXX_BUS_APBH:
+ /* Set next command */
+ HW_APBH_CHn_NXTCMDAR_WR(ch, head->handle);
+ /* Set counting semaphore (kicks off transfer). Assumes
+ peripheral has been set up correctly */
+ HW_APBH_CHn_SEMA_WR(ch, semaphore);
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ /* Set next command */
+ HW_APBX_CHn_NXTCMDAR_WR(ch, head->handle);
+ /* Set counting semaphore (kicks off transfer). Assumes
+ peripheral has been set up correctly */
+ HW_APBX_CHn_SEMA_WR(ch, semaphore);
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_dma_go);
+
+int stmp3xxx_dma_running(int channel)
+{
+ switch (dmabus(channel)) {
+ case STMP3XXX_BUS_APBH:
+ return HW_APBH_CHn_SEMA_RD(dmach(channel)) &
+ BM_APBH_CHn_SEMA_PHORE;
+
+ case STMP3XXX_BUS_APBX:
+ return HW_APBX_CHn_SEMA_RD(dmach(channel)) &
+ BM_APBX_CHn_SEMA_PHORE;
+
+ default:
+ BUG();
+ return 0;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_dma_running);
+
+/*
+ * Circular dma chain management
+ */
+void stmp37cc_dma_print_chain(struct stmp37xx_circ_dma_chain *chain)
+{
+ int i;
+ struct stmp3xxx_dma_descriptor *d;
+
+ for (i = 0; i < chain->total_count; i++) {
+ d = &chain->chain[i];
+ printk(KERN_DEBUG"Chain item %d (%p), command = %p(%x)\n"
+ "...next = %x"
+ "...cmd = %x"
+ "...buf_ptr/alt = %x\n",
+ i, d, d->command, d->handle,
+ d->command->next, d->command->cmd,
+ d->command->alternate);
+ }
+}
+
+void stmp3xxx_dma_free_chain(struct stmp37xx_circ_dma_chain *chain)
+{
+ int i;
+
+ for (i = 0; i < chain->total_count; i++)
+ stmp3xxx_dma_free_command(
+ STMP3xxx_DMA(chain->channel, chain->bus),
+ &chain->chain[i]);
+}
+EXPORT_SYMBOL(stmp3xxx_dma_free_chain);
+
+int stmp3xxx_dma_make_chain(int ch, struct stmp37xx_circ_dma_chain *chain,
+ struct stmp3xxx_dma_descriptor descriptors[],
+ unsigned items)
+{
+ int i;
+ int err = 0;
+
+ if (items == 0)
+ return err;
+
+ for (i = 0; i < items; i++) {
+ err = stmp3xxx_dma_allocate_command(ch, &descriptors[i]);
+ if (err) {
+ WARN_ON(err);
+ /*
+ * Couldn't allocate the whole chain.
+ * deallocate what has been allocated
+ */
+ if (i) {
+ do {
+ stmp3xxx_dma_free_command(ch,
+ &descriptors
+ [i]);
+ } while (i-- >= 0);
+ }
+ return err;
+ }
+
+ /* link them! */
+ if (i > 0) {
+ descriptors[i - 1].next_descr = &descriptors[i];
+ descriptors[i - 1].command->next =
+ descriptors[i].handle;
+ }
+ }
+
+ /* make list circular */
+ descriptors[items - 1].next_descr = &descriptors[0];
+ descriptors[items - 1].command->next = descriptors[0].handle;
+
+ chain->total_count = items;
+ chain->chain = descriptors;
+ chain->free_index = 0;
+ chain->active_index = 0;
+ chain->cooked_index = 0;
+ chain->free_count = items;
+ chain->active_count = 0;
+ chain->cooked_count = 0;
+ chain->bus = dmabus(ch);
+ chain->channel = dmach(ch);
+ return err;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_make_chain);
+
+void stmp37xx_circ_clear_chain(struct stmp37xx_circ_dma_chain *chain)
+{
+ BUG_ON(stmp3xxx_dma_running(STMP3xxx_DMA(chain->channel, chain->bus)) >
+ 0);
+ chain->free_index = 0;
+ chain->active_index = 0;
+ chain->cooked_index = 0;
+ chain->free_count = chain->total_count;
+ chain->active_count = 0;
+ chain->cooked_count = 0;
+}
+EXPORT_SYMBOL(stmp37xx_circ_clear_chain);
+
+void stmp37xx_circ_advance_free(struct stmp37xx_circ_dma_chain *chain,
+ unsigned count)
+{
+ BUG_ON(chain->cooked_count < count);
+
+ chain->cooked_count -= count;
+ chain->cooked_index += count;
+ chain->cooked_index %= chain->total_count;
+ chain->free_count += count;
+}
+EXPORT_SYMBOL(stmp37xx_circ_advance_free);
+
+void stmp37xx_circ_advance_active(struct stmp37xx_circ_dma_chain *chain,
+ unsigned count)
+{
+ BUG_ON(chain->free_count < count);
+
+ chain->free_count -= count;
+ chain->free_index += count;
+ chain->free_index %= chain->total_count;
+ chain->active_count += count;
+
+ switch (chain->bus) {
+ case STMP3XXX_BUS_APBH:
+ /* Set counting semaphore (kicks off transfer). Assumes
+ peripheral has been set up correctly */
+ HW_APBH_CHn_SEMA_CLR(chain->channel,
+ BM_APBH_CHn_SEMA_INCREMENT_SEMA);
+ HW_APBH_CHn_SEMA_SET(chain->channel,
+ BF_APBH_CHn_SEMA_INCREMENT_SEMA(count));
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ /* Set counting semaphore (kicks off transfer). Assumes
+ peripheral has been set up correctly */
+ HW_APBX_CHn_SEMA_CLR(chain->channel,
+ BM_APBX_CHn_SEMA_INCREMENT_SEMA);
+ HW_APBX_CHn_SEMA_SET(chain->channel,
+ BF_APBX_CHn_SEMA_INCREMENT_SEMA(count));
+ break;
+
+ default:
+ BUG();
+ }
+}
+EXPORT_SYMBOL(stmp37xx_circ_advance_active);
+
+unsigned stmp37xx_circ_advance_cooked(struct stmp37xx_circ_dma_chain *chain)
+{
+ unsigned cooked;
+
+ cooked = chain->active_count -
+ stmp3xxx_dma_read_semaphore(STMP3xxx_DMA(chain->channel, chain->bus));
+
+ chain->active_count -= cooked;
+ chain->active_index += cooked;
+ chain->active_index %= chain->total_count;
+
+ chain->cooked_count += cooked;
+
+ return cooked;
+}
+EXPORT_SYMBOL(stmp37xx_circ_advance_cooked);
+
+void stmp3xxx_dma_set_alt_target(int channel, int function)
+{
+#if defined(CONFIG_ARCH_STMP37XX)
+ unsigned bits = 4;
+#elif defined(CONFIG_ARCH_STMP378X)
+ unsigned bits = 2;
+#else
+#error wrong arch
+#endif
+ int shift = dmach(channel) * bits;
+ unsigned mask = (1<<bits) - 1;
+
+ BUG_ON(function < 0 || function >= (1<<bits));
+ pr_debug("%s: channel = %d, using mask %x, "
+ "shift = %d\n", __func__, channel, mask, shift);
+
+ switch (dmabus(channel)) {
+ case STMP3XXX_BUS_APBH:
+ HW_APBH_DEVSEL_CLR(mask<<shift);
+ HW_APBH_DEVSEL_SET(function<<shift);
+ break;
+ case STMP3XXX_BUS_APBX:
+ HW_APBX_DEVSEL_CLR(mask<<shift);
+ HW_APBX_DEVSEL_SET(function<<shift);
+ break;
+ default:
+ BUG();
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_dma_set_alt_target);
+
+void stmp3xxx_dma_suspend(void)
+{
+ HW_APBH_CTRL0_SET(BM_APBH_CTRL0_CLKGATE);
+ HW_APBX_CTRL0_SET(BM_APBX_CTRL0_CLKGATE);
+}
+
+void stmp3xxx_dma_resume(void)
+{
+ HW_APBH_CTRL0_CLR(BM_APBH_CTRL0_CLKGATE | BM_APBH_CTRL0_SFTRST);
+ HW_APBX_CTRL0_CLR(BM_APBX_CTRL0_CLKGATE | BM_APBX_CTRL0_SFTRST);
+}
+
+#ifdef CONFIG_CPU_FREQ
+
+struct dma_notifier_block {
+ struct notifier_block nb;
+ void *data;
+};
+
+static int dma_cpufreq_notifier(struct notifier_block *self,
+ unsigned long phase, void *p)
+{
+ switch (phase) {
+ case CPUFREQ_POSTCHANGE:
+ stmp3xxx_dma_resume();
+ break;
+
+ case CPUFREQ_PRECHANGE:
+ stmp3xxx_dma_suspend();
+ break;
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct dma_notifier_block dma_cpufreq_nb = {
+ .nb = {
+ .notifier_call = dma_cpufreq_notifier,
+ },
+};
+#endif /* CONFIG_CPU_FREQ */
+
+
+static struct sysdev_class stmp3xxx_dma_sysclass = {
+ .name = "stmp3xxx-dma",
+#ifdef CONFIG_PM
+ .suspend = NULL,
+ .resume = NULL,
+#endif
+};
+
+static struct sys_device stmp3xxx_dma_device = {
+ .id = -1,
+ .cls = &stmp3xxx_dma_sysclass,
+};
+
+static ssize_t stmp3xxx_dma_list(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ int i, n = 0;
+
+ for (i = 0; i < MAX_DMA_CHANNELS; i++)
+ if (IS_USED(i))
+ n += sprintf(buf + n, "%d @ %s\t%s\n",
+ dmach(i), dmabus(i) == 0 ? "APBH" : "APBX",
+ channels[i].name ? channels[i].name : "unknown");
+ return n;
+}
+SYSDEV_ATTR(channels, 0444, stmp3xxx_dma_list, NULL);
+
+void __init stmp3xxx_dma_init(void)
+{
+ HW_APBH_CTRL0_CLR(BM_APBH_CTRL0_CLKGATE | BM_APBH_CTRL0_SFTRST);
+ HW_APBX_CTRL0_CLR(BM_APBX_CTRL0_CLKGATE | BM_APBX_CTRL0_SFTRST);
+ sysdev_class_register(&stmp3xxx_dma_sysclass);
+ sysdev_register(&stmp3xxx_dma_device);
+ sysdev_create_file(&stmp3xxx_dma_device, &attr_channels);
+
+#ifdef CONFIG_CPU_FREQ
+ cpufreq_register_notifier(&dma_cpufreq_nb.nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+#endif /* CONFIG_CPU_FREQ */
+
+}
diff --git a/arch/arm/mach-stmp3xxx/emi.S b/arch/arm/mach-stmp3xxx/emi.S
new file mode 100644
index 000000000000..c134c3dfa159
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/emi.S
@@ -0,0 +1,150 @@
+/*
+ * Freescale STMP378X low level RAM frequency manipulation
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <mach/hardware.h>
+#include <asm/system.h>
+#include <asm/pgtable-hwdef.h>
+#include <mach/regs-power.h>
+#include <mach/regs-clkctrl.h>
+#include "clock.h"
+
+.global cpu_arm926_switch_mm
+
+.align 8
+ENTRY(stmp3xxx_ram_freq_scale)
+ stmfd sp!, {r1 - r9, lr}
+
+ ldr r5, [r0, #SCALING_DATA_NEW_FREQ_OFFSET]
+ ldr r6, [r0, #SCALING_DATA_CUR_FREQ_OFFSET]
+ ldr r7, [r0, #SCALING_DATA_EMI_DIV_OFFSET]
+ ldr r8, [r0, #SCALING_DATA_FRAC_DIV_OFFSET]
+
+ adr r9, __stmp_temp_stack
+
+ @ clean cache
+ ldr r1, __stmp_flush_cache_addr
+ mov lr, pc
+ mov pc, r1
+
+ @ put DRAM into self refresh
+ ldr r0, __stmp_dram_ctl00
+ ldr r1, [r0, #0x20]
+ orr r1, r1, #(1 << 8)
+ str r1, [r0, #0x20]
+ @ wait for it to actually happen
+ ldr r0, __stmp_dram_emi00
+1: ldr r1, [r0, #0x10]
+ tst r1, #(1 << 1)
+ beq 1b
+ nop
+
+ @ prepare for change
+ cmp r5, #24
+ bgt 2f
+ bl stmp3xxx_ram_24M_set_timings
+ b 100f
+2: cmp r5, #48
+ bgt 3f
+ bl stmp3xxx_ram_48M_set_timings
+ b 100f
+3: cmp r5, #60
+ bgt 4f
+ bl stmp3xxx_ram_60M_set_timings
+ b 100f
+4: cmp r5, #80
+ bgt 5f
+ bl stmp3xxx_ram_80M_set_timings
+ b 100f
+5: cmp r5, #96
+ bgt 6f
+ bl stmp3xxx_ram_96M_set_timings
+ b 100f
+6: cmp r5, #120
+ bgt 7f
+ bl stmp3xxx_ram_120M_set_timings
+ b 100f
+7: cmp r5, #133
+ bgt 8f
+ bl stmp3xxx_ram_133M_set_timings
+ b 100f
+8: bl stmp3xxx_ram_150M_set_timings
+
+100:
+ @ RAM to clk from xtal
+ mov r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0xFF000000)
+ mov r1, #(1<<6)
+ str r1, [r0, #4]
+ mov r0, #(HW_CLKCTRL_EMI_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0xFF000000)
+101: ldr r1, [r0]
+ tst r1, #BM_CLKCTRL_EMI_BUSY_REF_XTAL
+ bne 101b
+
+ bl __stmp_emi_set_values
+
+ @ EMI back to PLL
+ mov r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0xFF000000)
+ mov r1, #(1<<6)
+ str r1, [r0, #8]
+
+ mov r0, #(HW_CLKCTRL_EMI_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0xFF000000)
+1: ldr r1, [r0]
+ tst r1, #BM_CLKCTRL_EMI_BUSY_REF_EMI
+ bne 1b
+ bic r1, #BM_CLKCTRL_EMI_DCC_RESYNC_ENABLE
+ str r1, [r0]
+
+ @ restore normal DRAM mode
+ ldr r0, __stmp_dram_ctl00
+ ldr r1, [r0, #0x20]
+ bic r1, r1, #(1 << 8)
+ str r1, [r0, #0x20]
+ @ wait for it to actually happen
+ ldr r0, __stmp_dram_emi00
+102: ldr r1, [r0, #0x10]
+ tst r1, #(1 << 1)
+ bne 102b
+
+ @ restore regs and return
+ ldmfd sp!, {r1 - r9, lr}
+ mov pc, lr
+
+ .space 0x100
+__stmp_temp_stack:
+ .word 0
+
+#include "emi.inc"
+
+__stmp_flush_cache_addr:
+ .word arm926_flush_kern_cache_all
+
+ENTRY(stmp3xxx_ram_funcs_sz)
+ .word . - stmp3xxx_ram_freq_scale
+
diff --git a/arch/arm/mach-stmp3xxx/emi.inc b/arch/arm/mach-stmp3xxx/emi.inc
new file mode 100644
index 000000000000..3fc5f89543e9
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/emi.inc
@@ -0,0 +1,708 @@
+/*
+ * Freescale STMP378X low level RAM timings tables for Micron mDDR
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+__stmp_emi_set_values:
+ stmfd r9!, {r0 - r4, lr}
+ mov r1, #(HW_CLKCTRL_EMI_ADDR & 0x000000FF)
+ orr r1, r1, #(HW_CLKCTRL_EMI_ADDR & 0x0000FF00)
+ orr r1, r1, #(HW_CLKCTRL_EMI_ADDR & 0x00FF0000)
+ orr r1, r1, #(HW_CLKCTRL_EMI_ADDR & 0xFF000000)
+
+ mov r3, #BM_CLKCTRL_EMI_DCC_RESYNC_ENABLE
+
+ mov r0, #(HW_CLKCTRL_FRAC_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_FRAC_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_FRAC_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_FRAC_ADDR & 0xFF000000)
+ ldr r2, [r0]
+
+ and r4, r2, #BM_CLKCTRL_FRAC_EMIFRAC
+ lsr r4, r4, #8
+ /* new pll div > cur pll div? */
+ cmp r4, r8
+ bgt 1f
+ bic r4, r2, #BM_CLKCTRL_FRAC_EMIFRAC
+ orr r4, r4, r8, lsl #8
+ str r4, [r0]
+ nop
+ nop
+ nop
+
+1: ldr r4, [r1]
+ and r4, r4, #BM_CLKCTRL_EMI_DIV_EMI
+ /* new emi div > cur emi div? */
+ cmp r4, r7
+ bgt 2f
+ mov r4, r7
+ orr r4, r4, #0x100
+ orr r4, r4, r3
+ str r4, [r1]
+11: ldr r4, [r1]
+ tst r4, #BM_CLKCTRL_EMI_BUSY_REF_EMI
+ bne 11b
+ tst r4, #BM_CLKCTRL_EMI_BUSY_REF_XTAL
+ bne 11b
+ tst r4, #BM_CLKCTRL_EMI_BUSY_DCC_RESYNC
+ bne 11b
+
+2: ldr r2, [r0]
+
+ and r4, r2, #BM_CLKCTRL_FRAC_EMIFRAC
+ lsr r4, r4, #8
+ /* new pll div != cur pll div? */
+ cmp r4, r8
+ beq 3f
+ bic r4, r2, #BM_CLKCTRL_FRAC_EMIFRAC
+ orr r4, r4, r8, lsl #8
+ str r4, [r0]
+ nop
+ nop
+ nop
+
+3: ldr r4, [r1]
+ and r4, r4, #BM_CLKCTRL_EMI_DIV_EMI
+ /* new emi div != cur emi div? */
+ cmp r4, r7
+ beq 4f
+ mov r4, r7
+ orr r4, r4, #0x100
+ orr r4, r4, r3
+ str r4, [r1]
+31: ldr r4, [r1]
+ tst r4, #BM_CLKCTRL_EMI_BUSY_REF_EMI
+ bne 31b
+ tst r4, #BM_CLKCTRL_EMI_BUSY_REF_XTAL
+ bne 31b
+ tst r4, #BM_CLKCTRL_EMI_BUSY_DCC_RESYNC
+ bne 31b
+
+4: ldmfd r9!, {r0 - r4, lr}
+ mov pc, lr
+
+stmp3xxx_ram_24M_set_timings:
+ ldr r0, __stmp_dram_ctl00
+ adr r1, __stmp_dram_24M_values
+1: ldr r2, [r1]
+ ldr r3, [r1, #4]
+ mov r4, r2, lsl #2
+ str r3, [r0, r4]
+ add r1, r1, #8
+ cmp r2, #40
+ bne 1b
+ mov pc, lr
+
+stmp3xxx_ram_48M_set_timings:
+ ldr r0, __stmp_dram_ctl00
+ adr r1, __stmp_dram_48M_values
+1: ldr r2, [r1]
+ ldr r3, [r1, #4]
+ mov r4, r2, lsl #2
+ str r3, [r0, r4]
+ add r1, r1, #8
+ cmp r2, #40
+ bne 1b
+ mov pc, lr
+
+stmp3xxx_ram_60M_set_timings:
+ ldr r0, __stmp_dram_ctl00
+ adr r1, __stmp_dram_60M_values
+1: ldr r2, [r1]
+ ldr r3, [r1, #4]
+ mov r4, r2, lsl #2
+ str r3, [r0, r4]
+ add r1, r1, #8
+ cmp r2, #40
+ bne 1b
+ mov pc, lr
+
+stmp3xxx_ram_80M_set_timings:
+ ldr r0, __stmp_dram_ctl00
+ adr r1, __stmp_dram_80M_values
+1: ldr r2, [r1]
+ ldr r3, [r1, #4]
+ mov r4, r2, lsl #2
+ str r3, [r0, r4]
+ add r1, r1, #8
+ cmp r2, #40
+ bne 1b
+ mov pc, lr
+
+stmp3xxx_ram_96M_set_timings:
+ ldr r0, __stmp_dram_ctl00
+ adr r1, __stmp_dram_96M_values
+1: ldr r2, [r1]
+ ldr r3, [r1, #4]
+ mov r4, r2, lsl #2
+ str r3, [r0, r4]
+ add r1, r1, #8
+ cmp r2, #40
+ bne 1b
+ mov pc, lr
+
+stmp3xxx_ram_120M_set_timings:
+ ldr r0, __stmp_dram_ctl00
+ adr r1, __stmp_dram_120M_values
+1: ldr r2, [r1]
+ ldr r3, [r1, #4]
+ mov r4, r2, lsl #2
+ str r3, [r0, r4]
+ add r1, r1, #8
+ cmp r2, #40
+ bne 1b
+ mov pc, lr
+
+stmp3xxx_ram_133M_set_timings:
+ ldr r0, __stmp_dram_ctl00
+ adr r1, __stmp_dram_133M_values
+1: ldr r2, [r1]
+ ldr r3, [r1, #4]
+ str r3, [r0, r2, lsl #2]
+ add r1, r1, #8
+ cmp r2, #40
+ bne 1b
+ mov pc, lr
+
+stmp3xxx_ram_150M_set_timings:
+ ldr r0, __stmp_dram_ctl00
+ adr r1, __stmp_dram_150M_values
+1: ldr r2, [r1]
+ ldr r3, [r1, #4]
+ str r3, [r0, r2, lsl #2]
+ add r1, r1, #8
+ cmp r2, #40
+ bne 1b
+ mov pc, lr
+
+__stmp_dram_ctl00:
+ .word IO_ADDRESS(0x800E0000)
+__stmp_dram_emi00:
+ .word IO_ADDRESS(0x80020000)
+__stmp_power_vdddctrl:
+ .word IO_ADDRESS(0x80044040)
+
+stmp3xxx_ram_save_timings:
+ ldr r0, __stmp_dram_ctl00
+ adr r1, __stmp_dram_saved_values
+1: ldr r2, [r1]
+ mov r4, r2, lsl #2
+ ldr r3, [r0, r4]
+ str r3, [r1, #4]
+ add r1, r1, #8
+ cmp r2, #40
+ bne 1b
+ mov pc, lr
+
+#ifdef CONFIG_STMP378X_RAM_MDDR
+__stmp_dram_24M_values:
+ .word 4
+ .word 0x01000101
+ .word 7
+ .word 0x01000101
+ .word 12
+ .word 0x02010002
+ .word 13
+ .word 0x06060a02
+ .word 15
+ .word 0x01030000
+ .word 17
+ .word 0x2d000102
+ .word 18
+ .word 0x20200000
+ .word 19
+ .word 0x027f1414
+ .word 20
+ .word 0x01021608
+ .word 21
+ .word 0x00000002
+ .word 26
+ .word 0x000000b3
+ .word 32
+ .word 0x00030687
+ .word 33
+ .word 0x00000003
+ .word 34
+ .word 0x000012c1
+ .word 40
+ .word 0x00010000
+
+__stmp_dram_48M_values:
+ .word 4
+ .word 0x01000101
+ .word 7
+ .word 0x01000101
+ .word 13
+ .word 0x06060a02
+ .word 12
+ .word 0x02010002
+ .word 15
+ .word 0x02040000
+ .word 17
+ .word 0x2d000104
+ .word 18
+ .word 0x1f1f0000
+ .word 19
+ .word 0x027f0a0a
+ .word 20
+ .word 0x02030a10
+ .word 21
+ .word 0x00000004
+ .word 26
+ .word 0x0000016f
+ .word 32
+ .word 0x00060d17
+ .word 33
+ .word 0x00000006
+ .word 34
+ .word 0x00002582
+ .word 40
+ .word 0x00020000
+
+__stmp_dram_60M_values:
+__stmp_dram_80M_values:
+ .word 4
+ .word 0x01000101
+ .word 7
+ .word 0x01000101
+ .word 12
+ .word 0x02020002
+ .word 13
+ .word 0x06060a02
+ .word 15
+ .word 0x02040000
+ .word 17
+ .word 0x2d000005
+ .word 18
+ .word 0x1f1f0000
+ .word 19
+ .word 0x027f0a0a
+ .word 20
+ .word 0x02040a10
+ .word 21
+ .word 0x00000006
+ .word 26
+ .word 0x000001cc
+ .word 32
+ .word 0x00081060
+ .word 33
+ .word 0x00000008
+ .word 34
+ .word 0x00002ee5
+ .word 40
+ .word 0x00020000
+
+__stmp_dram_96M_values:
+ .word 4
+ .word 0x00000101
+ .word 7
+ .word 0x01000001
+ .word 12
+ .word 0x02020002
+ .word 13
+ .word 0x06070a02
+ .word 15
+ .word 0x03050000
+ .word 17
+ .word 0x2d000808
+ .word 18
+ .word 0x1f1f0000
+ .word 19
+ .word 0x020c1010
+ .word 20
+ .word 0x0305101c
+ .word 21
+ .word 0x00000007
+ .word 26
+ .word 0x000002e6
+ .word 32
+ .word 0x000c1a3b
+ .word 33
+ .word 0x0000000c
+ .word 34
+ .word 0x00004b0d
+ .word 40
+ .word 0x00030000
+
+__stmp_dram_120M_values:
+ .word 4
+ .word 0x00000101
+ .word 7
+ .word 0x01000001
+ .word 12
+ .word 0x02020002
+ .word 13
+ .word 0x06070a02
+ .word 15
+ .word 0x03050000
+ .word 17
+ .word 0x2300080a
+ .word 18
+ .word 0x1f1f0000
+ .word 19
+ .word 0x020c1010
+ .word 20
+ .word 0x0306101c
+ .word 21
+ .word 0x00000009
+ .word 26
+ .word 0x000003a1
+ .word 32
+ .word 0x000f20ca
+ .word 33
+ .word 0x0000000f
+ .word 34
+ .word 0x00005dca
+ .word 40
+ .word 0x00040000
+
+__stmp_dram_133M_values:
+__stmp_dram_150M_values:
+ .word 4
+ .word 0x00000101
+ .word 7
+ .word 0x01000001
+ .word 12
+ .word 0x02020002
+ .word 13
+ .word 0x06070a02
+ .word 15
+ .word 0x03050000
+ .word 17
+ .word 0x2000080a
+ .word 18
+ .word 0x1f1f0000
+ .word 19
+ .word 0x020c1010
+ .word 20
+ .word 0x0306101c
+ .word 21
+ .word 0x0000000a
+ .word 26
+ .word 0x00000408
+ .word 32
+ .word 0x0010245f
+ .word 33
+ .word 0x00000010
+ .word 34
+ .word 0x00006808
+ .word 40
+ .word 0x00040000
+
+#elif CONFIG_STMP378X_RAM_DDR
+/* XXX: not quite ready yet */
+__stmp_dram_24M_values:
+ .word 4
+ .word 0x01000101
+ .word 7
+ .word 0x01000101
+ .word 11
+ .word 0x00070206
+ .word 12
+ .word 0x01010000 @ t_wr 1, t_rrd 1, t_cke 0
+ .word 13
+ .word 0x04040a01 @ t_wtr 1
+ .word 15
+ .word 0x01020000 @ t_rp 1, t_dal 2
+ .word 17
+ .word 0x3d000302 @ t_rc 2
+ .word 20
+ .word 0x01020508
+ .word 21
+ .word 0x00000002 @ t_rfc 2
+ .word 26
+ .word 0x000000b3 /* 0xd20 */ @ t_ref
+ .word 32
+ .word 0x00020690 @ t_xsnr 2, t_rasmax 0x690
+ .word 33
+ .word 0x000000c8 @ t_xsr 0xc8
+ .word 34
+ .word 0x000012c1 @ t_init
+ .word 40
+ .word 0x00010000
+
+@ not yet
+__stmp_dram_48M_values:
+ .word 4
+ .word 0x01000101
+ .word 7
+ .word 0x01000101
+ .word 11
+ .word 0x00070206
+ .word 12
+ .word 0x01010000 @ t_wr 1, t_rrd 1, t_cke 0
+ .word 13
+ .word 0x04040a01 @ t_wtr 1
+ .word 15
+ .word 0x01020000 @ t_rp 1, t_dal 2
+ .word 17
+ .word 0x39000104 @ t_rc 4
+ .word 19
+ .word 0x027f1010
+ .word 20
+ .word 0x02030a10
+ .word 21
+ .word 0x00000004 @ t_rfc
+ .word 26
+ .word 0x00000173 /* 0x1a42 */ @ t_ref
+ .word 32
+ .word 0x00040d21 @ t_xsnr 4, t_rasmax 0xd21
+ .word 33
+ .word 0x000000c8 @ t_xsr 0xc8
+ .word 34
+ .word 0x00002586 @ t_init
+ .word 40
+ .word 0x00010000
+
+__stmp_dram_60M_values:
+ .word 4
+ .word 0x01000101
+ .word 7
+ .word 0x01000101
+ .word 11
+ .word 0x00070206
+ .word 12
+ .word 0x01010000 @ t_wr 1, t_rrd 1, t_cke 0
+ .word 13
+ .word 0x04040a01 @ t_wtr 1
+ .word 15
+ .word 0x01020000 @ t_rp 1, t_dal 2
+ .word 17
+ .word 0x3d000105 @ t_rc 5
+ .word 19
+ .word 0x027f1313
+ .word 20
+ .word 0x01031523 @ t_rcd 1, t_rasmin 3
+ .word 21
+ .word 0x00000005 @ t_rfc 5
+ .word 26
+ .word 0x000001cc /* 0x20cd */ @ t_ref
+ .word 32
+ .word 0x00051068 @ t_xsnr 5, t_rasmax 0x1068
+ .word 33
+ .word 0x000000c8 @ t_xsr 0xc8
+ .word 34
+ .word 0x00002ee5 @ t_init
+ .word 40
+ .word 0x00010000
+
+__stmp_dram_80M_values:
+ .word 4
+ .word 0x00000101
+ .word 7
+ .word 0x01000001
+ .word 11
+ .word 0x00070206
+ .word 12
+ .word 0x02010000 @ t_wr 2, t_rrd 1, t_cke 0
+ .word 13
+ .word 0x04040a01 @ t_wtr 1
+ .word 15
+ .word 0x02040000 @ t_rp 2, t_dal 4
+ .word 17
+ .word 0x20001c05 @ dll_start_point 0x20, dll_increment 0x1c, t_rc 5
+ .word 19
+ .word 0x027f1313
+ .word 20
+ .word 0x02041522 @ t_rcd 2, t_rasmin 4, wr_dqs_shift 0x22
+ .word 21
+ .word 0x00000006 @ t_rfc 6
+ .word 26
+ .word 0x00000269 @ t_ref
+ .word 32
+ .word 0x000615d6 @ t_xsnr 6, t_rasmax 0x15d6
+ .word 33
+ .word 0x000000c8 @ t_xsr 0xc8
+ .word 34
+ .word 0x00003e80 @ t_init
+ .word 40
+ .word 0x00010000
+
+__stmp_dram_96M_values:
+ .word 4
+ .word 0x00000101
+ .word 7
+ .word 0x01000001
+ .word 11
+ .word 0x00070206
+ .word 12
+ .word 0x02020000 @ t_wr 2, t_rrd 2, t_cke 0
+ .word 13
+ .word 0x04040a01 @ t_wtr 1
+ .word 15
+ .word 0x02040000 @ t_rp 2, t_dal 4
+ .word 17
+ .word 0x2f001706 @ dll_start_point 0x2f, dll_increment 0x17, t_rc 6
+ .word 19
+ .word 0x027f1a1a
+ .word 20
+ .word 0x02051c21 @ t_rcd 2, t_rasmin 5, wr_dqs_shift 0x22
+ .word 21
+ .word 0x00000007 @ t_rfc 7
+ .word 26
+ .word 0x000002e6 /* 0x347b */ @ t_ref
+ .word 32
+ .word 0x00081a3e @ t_xsnr 8, t_rasmax 0x1a3e
+ .word 33
+ .word 0x000000c8 @ t_xsr 0xc8
+ .word 34
+ .word 0x00004b0d @ t_init
+ .word 40
+ .word 0x00010000
+
+__stmp_dram_120M_values:
+ .word 4
+ .word 0x00000101
+ .word 7
+ .word 0x01000001
+ .word 11
+ .word 0x00070206
+ .word 12
+ .word 0x02020000 @ t_wr 2, t_rrd 2, t_cke 0
+ .word 13
+ .word 0x04040a01 @ t_wtr 1
+ .word 15
+ .word 0x02040000 @ t_rp 2, t_dal 4
+ .word 17
+ .word 0x26001308 @ dll_start_point 0x26, dll_increment 0x13, t_rc 8
+ .word 19
+ .word 0x027f1a1a
+ .word 20
+ .word 0x02061c23 @ t_rcd 2, t_rasmin 6
+ .word 21
+ .word 0x00000009 @ t_rfc 9
+ .word 26
+ .word 0x000003a1 /* 0x41a6 */ @ t_ref
+ .word 32
+ .word 0x000a20ca @ t_xsnr 9, t_rasmax 0x20ca
+ .word 33
+ .word 0x000000c8 @ t_xsr 0xc8
+ .word 34
+ .word 0x00005dca @ t_init
+ .word 40
+ .word 0x00010000
+
+__stmp_dram_133M_values:
+ .word 4
+ .word 0x00000101
+ .word 7
+ .word 0x01000001
+ .word 11
+ .word 0x00070204
+ .word 12
+ .word 0x02020000
+ .word 13
+ .word 0x04040a01
+ .word 15
+ .word 0x02040000
+ .word 17
+ .word 0x19000f08 @ t_rc 0xa
+ .word 19
+ .word 0x02021313
+ .word 20
+ .word 0x02061521
+ .word 21
+ .word 0x0000000a
+ .word 26
+ .word 0x000003f7 /* 0x48b9 */
+ .word 32
+ .word 0x000a23cd
+ .word 33
+ .word 0x000000c8 @ t_xsr 0xc8
+ .word 34
+ .word 0x00006665
+ .word 40
+ .word 0x00010000
+
+__stmp_dram_150M_values:
+ .word 4
+ .word 0x00000101
+ .word 7
+ .word 0x01000001
+ .word 11
+ .word 0x00070206
+ .word 12
+ .word 0x02020000 @ t_wr 2, t_rrd 2, t_cke 0
+ .word 13
+ .word 0x05050a02 @ t_wtr 2
+ .word 15
+ .word 0x03060000 @ t_rp 3, t_dal 6
+ .word 17
+ .word 0x18000d0c @ dll_start_point 0x18, dll_increment 0xd, t_rc 0xc
+ .word 19
+ .word 0x027f0f0f
+ .word 20
+ .word 0x03071121 @ t_rcd 3, t_rasmin 7
+ .word 21
+ .word 0x0000000c @ t_rfc 0xc
+ .word 26
+ .word 0x000001cc /* 0x20cd */ @ t_ref
+ .word 32
+ .word 0x000c2860 @ t_xsnr 0xc, t_rasmax 0x2860
+ .word 33
+ .word 0x000000c8 @ t_xsr 0xc8
+ .word 34
+ .word 0x00007554 @ t_init
+ .word 40
+ .word 0x00010000
+
+#else
+#error RAM chip not defined
+#endif
+
+stmp3xxx_ram_restore_timings:
+ ldr r0, __stmp_dram_ctl00
+ adr r1, __stmp_dram_saved_values
+1: ldr r2, [r1]
+ ldr r3, [r1, #4]
+ mov r4, r2, lsl #2
+ str r3, [r0, r4]
+ add r1, r1, #8
+ cmp r2, #40
+ bne 1b
+ mov pc, lr
+
+__stmp_dram_saved_values:
+ .word 4
+ .word 0
+ .word 7
+ .word 0
+ .word 12
+ .word 0
+ .word 13
+ .word 0
+ .word 15
+ .word 0
+ .word 17
+ .word 0
+ .word 18
+ .word 0
+ .word 19
+ .word 0
+ .word 20
+ .word 0
+ .word 21
+ .word 0
+ .word 26
+ .word 0
+ .word 32
+ .word 0
+ .word 33
+ .word 0
+ .word 34
+ .word 0
+ .word 40
+ .word 0
+
diff --git a/arch/arm/mach-stmp3xxx/gpio.c b/arch/arm/mach-stmp3xxx/gpio.c
new file mode 100644
index 000000000000..3f5fad781319
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/gpio.c
@@ -0,0 +1,110 @@
+/*
+ * Freescale STMP37XX/STMP378X GPIO driver
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sysdev.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <mach/hardware.h>
+#include <mach/stmp3xxx_regs.h>
+
+#include "common.h"
+
+#define STMP3xxx_GPIO_TOTAL (3*32) /* three banks by 32 pins each */
+
+int gpio_request(unsigned id, char *label)
+{
+ BUG_ON(id > STMP3xxx_GPIO_TOTAL);
+ return stmp3xxx_request_pin(id, PIN_GPIO, label);
+}
+EXPORT_SYMBOL(gpio_request);
+
+void gpio_free(unsigned id)
+{
+ BUG_ON(id > STMP3xxx_GPIO_TOTAL);
+ stmp3xxx_release_pin(id, NULL);
+}
+EXPORT_SYMBOL(gpio_free);
+
+void gpio_set_value(unsigned id, int value)
+{
+ struct stmp3xxx_pinmux_bank *b;
+ int num = STMP3XXX_PINID_TO_PINNUM(id);
+
+ BUG_ON(id > STMP3xxx_GPIO_TOTAL);
+ b = stmp_pinmux_banks(STMP3XXX_PINID_TO_BANK(id));
+ spin_lock(&b->lock);
+ __raw_writel(1 << num, value ? b->hw_gpio_set : b->hw_gpio_clr);
+ spin_unlock(&b->lock);
+}
+EXPORT_SYMBOL(gpio_set_value);
+
+int gpio_get_value(unsigned id)
+{
+ struct stmp3xxx_pinmux_bank *b;
+ int v;
+ int num = STMP3XXX_PINID_TO_PINNUM(id);
+
+ BUG_ON(id > STMP3xxx_GPIO_TOTAL);
+ b = stmp_pinmux_banks(STMP3XXX_PINID_TO_BANK(id));
+ spin_lock(&b->lock);
+ v = (__raw_readl(b->hw_gpio_read) & (1 << num)) >> num;
+ spin_unlock(&b->lock);
+ return v;
+}
+EXPORT_SYMBOL(gpio_get_value);
+
+int gpio_to_irq(unsigned id)
+{
+ struct stmp3xxx_pinmux_bank *b;
+ BUG_ON(id > STMP3xxx_GPIO_TOTAL);
+ b = stmp_pinmux_banks(STMP3XXX_PINID_TO_BANK(id));
+ return b->irq;
+}
+EXPORT_SYMBOL(gpio_to_irq);
+
+void gpio_direction_output(unsigned id, int value)
+{
+ struct stmp3xxx_pinmux_bank *b;
+ int num = STMP3XXX_PINID_TO_PINNUM(id);
+
+ BUG_ON(id > STMP3xxx_GPIO_TOTAL);
+ b = stmp_pinmux_banks(STMP3XXX_PINID_TO_BANK(id));
+ spin_lock(&b->lock);
+ __raw_writel(1 << num, b->hw_gpio_doe + HW_STMP3xxx_SET);
+ __raw_writel(1 << num, value ? b->hw_gpio_set : b->hw_gpio_clr);
+ spin_unlock(&b->lock);
+}
+EXPORT_SYMBOL(gpio_direction_output);
+
+void gpio_direction_input(unsigned id)
+{
+ struct stmp3xxx_pinmux_bank *b;
+ int num = STMP3XXX_PINID_TO_PINNUM(id);
+
+ BUG_ON(id > STMP3xxx_GPIO_TOTAL);
+ b = stmp_pinmux_banks(STMP3XXX_PINID_TO_BANK(id));
+ spin_lock(&b->lock);
+ __raw_writel(1 << num, b->hw_gpio_doe + HW_STMP3xxx_CLR);
+ spin_unlock(&b->lock);
+}
+EXPORT_SYMBOL(gpio_direction_input);
+
+MODULE_AUTHOR("dmitry pervushin");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-stmp3xxx/gpmi.c b/arch/arm/mach-stmp3xxx/gpmi.c
new file mode 100644
index 000000000000..b699f56095cd
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/gpmi.c
@@ -0,0 +1,40 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI module pin multiplexing
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include "common.h"
+
+int gpmi_pinmux_request(char *title)
+{
+ int err = 0;
+
+ err = stmp3xxx_request_pin_group(&gpmi_pins, title);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(gpmi_pinmux_request);
+
+void gpmi_pinmux_free(char *title)
+{
+ stmp3xxx_release_pin_group(&gpmi_pins, title);
+}
+EXPORT_SYMBOL_GPL(gpmi_pinmux_free);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
diff --git a/arch/arm/mach-stmp3xxx/include/mach/arc_otg.h b/arch/arm/mach-stmp3xxx/include/mach/arc_otg.h
new file mode 100644
index 000000000000..e0213b21f4ff
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/arc_otg.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_ARC_OTG_H__
+#define __ASM_ARCH_MXC_ARC_OTG_H__
+
+#include <mach/hardware.h>
+
+/*
+ * register bits
+ */
+
+/* x_PORTSCx */
+#define PORTSC_PTS_MASK (3 << 30)/* parallel xcvr select mask */
+#define PORTSC_PTS_UTMI (0 << 30)/* UTMI/UTMI+ */
+#define PORTSC_PTS_PHILIPS (1 << 30)/* Philips classic */
+#define PORTSC_PTS_ULPI (2 << 30)/* ULPI */
+#define PORTSC_PTS_SERIAL (3 << 30)/* serial */
+#define PORTSC_STS (1 << 29)/* serial xcvr select */
+#define PORTSC_PTW (1 << 28)/* UTMI width */
+#define PORTSC_PORT_POWER (1 << 12)/* port power */
+#define PORTSC_LS_MASK (3 << 10)/* Line State mask */
+#define PORTSC_LS_SE0 (0 << 10)/* SE0 */
+#define PORTSC_LS_K_STATE (1 << 10)/* K-state */
+#define PORTSC_LS_J_STATE (2 << 10)/* J-state */
+#define PORTSC_PORT_RESET (1 << 8)/* Port reset */
+#define PORTSC_PORT_SUSPEND (1 << 7)/* Suspend */
+#define PORTSC_PORT_FORCE_RESUME (1 << 6)/* Force port resume */
+#define PORTSC_OVER_CURRENT_CHG (1 << 5)/* over current change */
+#define PORTSC_OVER_CURRENT_ACT (1 << 4)/* over currrent active */
+#define PORTSC_PORT_EN_DIS_CHANGE (1 << 3)/* port {en,dis}able change */
+#define PORTSC_PORT_ENABLE (1 << 2)/* port enabled */
+#define PORTSC_CONNECT_STATUS_CHANGE (1 << 1)/* connect status change */
+#define PORTSC_CURRENT_CONNECT_STATUS (1 << 0)/* current connect status */
+
+#define PORTSC_W1C_BITS (PORTSC_CONNECT_STATUS_CHANGE | \
+ PORTSC_PORT_EN_DIS_CHANGE | \
+ PORTSC_OVER_CURRENT_CHG)
+
+/* UOG_OTGSC Register Bits */
+/* control bits: */
+#define OTGSC_CTRL_VBUS_DISCHARGE (1 << 0)
+#define OTGSC_CTRL_VBUS_CHARGE (1 << 1)
+#define OTGSC_CTRL_OTG_TERM (1 << 3)/* controls DM pulldown */
+#define OTGSC_CTRL_DATA_PULSING (1 << 4)
+#define OTGSC_CTRL_USB_ID_PU (1 << 5)/* enable ID pullup */
+/* current status: (R/O) */
+#define OTGSC_STS_USB_ID (1 << 8)/* 0=A-device 1=B-device */
+#define OTGSC_STS_A_VBUS_VALID (1 << 9)
+#define OTGSC_STS_A_SESSION_VALID (1 << 10)
+#define OTGSC_STS_B_SESSION_VALID (1 << 11)
+#define OTGSC_STS_B_SESSION_END (1 << 12)
+#define OTGSC_STS_1ms_TIMER (1 << 13)
+#define OTGSC_STS_DATA_PULSE (1 << 14)
+/* interrupt status: (write to clear) */
+#define OTGSC_IS_MASK (0x7f << 16)
+#define OTGSC_IS_USB_ID (1 << 16)
+#define OTGSC_IS_A_VBUS_VALID (1 << 17)
+#define OTGSC_IS_A_SESSION_VALID (1 << 18)
+#define OTGSC_IS_B_SESSION_VALID (1 << 19)
+#define OTGSC_IS_B_SESSION_END (1 << 20)
+#define OTGSC_IS_1ms_TIMER (1 << 21)
+#define OTGSC_IS_DATA_PULSE (1 << 22)
+/* interrupt enables: */
+#define OTGSC_IE_MASK (0x7f << 24)
+#define OTGSC_IE_USB_ID (1 << 24)
+#define OTGSC_IE_A_VBUS_VALID (1 << 25)
+#define OTGSC_IE_A_SESSION_VALID (1 << 26)
+#define OTGSC_IE_B_SESSION_VALID (1 << 27)
+#define OTGSC_IE_B_SESSION_END (1 << 28)
+#define OTGSC_IE_1ms_TIMER (1 << 29)
+#define OTGSC_IE_DATA_PULSE (1 << 30)
+
+/* x_USBMODE */
+#define USBMODE_SLOM (1 << 3) /* setup lockout mode */
+#define USBMODE_ES (1 << 2) /* (big) endian select */
+#define USBMODE_CM_MASK (3 << 0) /* controller mode mask */
+#define USBMODE_CM_HOST (3 << 0) /* host */
+#define USBMODE_CM_DEVICE (2 << 0) /* device */
+#define USBMODE_CM_reserved (1 << 0) /* reserved */
+
+/* USBCMD */
+#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */
+#define UCMD_RESET (1 << 1) /* controller reset */
+#define UCMD_ITC_NO_THRESHOLD (~(0xff << 16))/* Interrupt Threshold Control */
+
+#define HCSPARAMS_PPC (0x1<<4) /* Port Power Control */
+#endif
diff --git a/arch/arm/mach-stmp3xxx/include/mach/cpu.h b/arch/arm/mach-stmp3xxx/include/mach/cpu.h
new file mode 100644
index 000000000000..2d0267a1934b
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/cpu.h
@@ -0,0 +1,33 @@
+/*
+ * Freescale STMP37XX/STMP378X CPU type detection
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_PLAT_CPU_H
+#define __ASM_PLAT_CPU_H
+
+#ifdef CONFIG_ARCH_STMP37XX
+#define cpu_is_stmp37xx() (1)
+#else
+#define cpu_is_stmp37xx() (0)
+#endif
+
+#ifdef CONFIG_ARCH_STMP378X
+#define cpu_is_stmp378x() (1)
+#else
+#define cpu_is_stmp378x() (0)
+#endif
+
+#endif /* __ASM_PLAT_CPU_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/dcp_bootstream_ioctl.h b/arch/arm/mach-stmp3xxx/include/mach/dcp_bootstream_ioctl.h
new file mode 100644
index 000000000000..aec63a5bfef4
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/dcp_bootstream_ioctl.h
@@ -0,0 +1,32 @@
+/*
+ * Freescale STMP378X DCP driver for bootstream update. Only handles the OTP KEY
+ * case and can only encrypt/decrypt.
+ *
+ * Author: Pantelis Antoniou <pantelis@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 DCP_BOOTSTREAM_IOCTL_H
+#define DCP_BOOTSTREAM_IOCTL_H
+
+/* remember to have included the proper _IO definition
+ * file before hand.
+ * For user space it's <sys/ioctl.h>
+ */
+
+#define DBS_IOCTL_BASE 'd'
+
+#define DBS_ENC _IOW(DBS_IOCTL_BASE, 0x00, uint8_t[16])
+#define DBS_DEC _IOW(DBS_IOCTL_BASE, 0x01, uint8_t[16])
+
+#endif
diff --git a/arch/arm/mach-stmp3xxx/include/mach/ddi_bc.h b/arch/arm/mach-stmp3xxx/include/mach/ddi_bc.h
new file mode 100644
index 000000000000..6f59b98d2f86
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/ddi_bc.h
@@ -0,0 +1,736 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_bc
+//! @{
+//
+// Copyright (c) 2004-2005 SigmaTel, Inc.
+//
+//! \file ddi_bc.h
+//! \brief Header file for the Battery Charger device driver.
+//! \date 06/2005
+//!
+//! This file contains externally visible declarations for the Battery Charger
+//! device driver.
+//!
+//! \see ddi_bc.c and related files.
+//! \todo [PUBS] Add definitions for TBDs in this file.
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _DDI_BC_H
+#define _DDI_BC_H
+
+////////////////////////////////////////////////////////////////////////////////
+// Includes
+////////////////////////////////////////////////////////////////////////////////
+
+#include <mach/stmp3xxx_regs.h>
+
+////////////////////////////////////////////////////////////////////////////////
+// Definitions
+////////////////////////////////////////////////////////////////////////////////
+#define DDI_BC_MAX_RESTART_CYCLES 100
+
+#define DDI_BC_LIION_CHARGING_VOLTAGE 4200
+#define DDI_BC_ALKALINE_NIMH_CHARGING_VOLTAGE 1750
+
+//! \brief Defines battery charger states.
+typedef enum _ddi_bc_State {
+ //! \brief TBD
+ DDI_BC_STATE_UNINITIALIZED = 0,
+ //! \brief TBD
+ DDI_BC_STATE_BROKEN = 1,
+ //! \brief TBD
+ DDI_BC_STATE_DISABLED = 2,
+ //! \brief TBD
+ DDI_BC_STATE_WAITING_TO_CHARGE = 3,
+ //! \brief TBD
+ DDI_BC_STATE_CONDITIONING = 4,
+ //! \brief TBD
+ DDI_BC_STATE_CHARGING = 5,
+ //! \brief TBD
+ DDI_BC_STATE_TOPPING_OFF = 6,
+ //! \brief TBD
+ DDI_BC_STATE_DCDC_MODE_WAITING_TO_CHARGE = 7,
+
+} ddi_bc_State_t;
+
+typedef enum _ddi_bc_BrokenReason {
+ //! \brief TBD
+ DDI_BC_BROKEN_UNINITIALIZED = 0,
+ //! \brief TBD
+ DDI_BC_BROKEN_CHARGING_TIMEOUT = 1,
+ //! \brief TBD
+ DDI_BC_BROKEN_FORCED_BY_APPLICATION = 2,
+ //! \brief TBD
+ DDI_BC_BROKEN_EXTERNAL_BATTERY_VOLTAGE_DETECTED = 3,
+ //! \brief TBD
+ DDI_BC_BROKEN_NO_BATTERY_DETECTED = 4,
+
+} ddi_bc_BrokenReason_t;
+
+//! \brief Defines the battery charger configuration.
+typedef struct _ddi_bc_Cfg {
+ //! \brief Units in milliseconds.
+ //!
+ //! This field configures the expected period between calls to
+ //! ddi_bc_StateMachine. If die temperature monitoring is
+ //! enabled, then the data sheet recommends the period be around
+ //! 100ms or less.
+ //!
+ //! Note that this period defines the minimum time resolution of
+ //! the battery charger.
+
+ uint32_t u32StateMachinePeriod;
+
+ //! \brief Units in mA/s.
+ //!
+ //! This field configures the slope of the current ramp. Any
+ //! time the battery charger increases its current draw, it will
+ //! ramp up the current no faster than this rate.
+ //!
+ //! Note that the minimum time resolution of the battery charger
+ //! is the configured period between calls to advance the state
+ //! machine. Also, the hardware has a minimum current resolution
+ //! of 10mA. If the given ramp slope cannot be expressed
+ //! exactly, then the largest expressible smaller slope will be
+ //! the result. If the actual period between calls to
+ //! ddi_bc_StateMachine is irregular, the current may ramp faster
+ //! than indicated.
+
+ uint16_t u16CurrentRampSlope;
+
+ //! \brief Units in millivolts.
+ //!
+ //! This field configures the threshold conditioning voltage. If
+ //! the battery’s voltage is below this value, it will be
+ //! conditioned until its voltage rises above the maximum
+ //! conditioning voltage. After that, the battery will be
+ //! charged normally.
+ //!
+ //! Note that the hardware has a minimum resolution of 8mV. If
+ //! the given voltage cannot be expressed exactly, then the
+ //! smallest expressible larger value will be used.
+
+ uint16_t u16ConditioningThresholdVoltage;
+
+ //! \brief Units in millivolts.
+ //!
+ //! This field configures the maximum conditioning voltage. If
+ //! the battery charger is conditioning a battery, normal
+ //! charging begins when the voltage rises above this value.
+ //!
+ //! This value should be slightly higher than the threshold
+ //! conditioning voltage because it is measured while a
+ //! conditioning current is actually flowing to the battery.
+ //! With a conditioning current of 0.1C, reasonable values for
+ //! the threshold and maximum conditioning voltages are 2.9V
+ //! and 3.0V respectively.
+ //!
+ //! Note that the hardware has a minimum resolution of 8mV. If
+ //! the given voltage cannot be expressed exactly, then the
+ //! smallest expressible larger value will be used.
+
+ uint16_t u16ConditioningMaxVoltage;
+
+ //! \brief Units in milliamps.
+ //!
+ //! This field configures the maximum conditioning current.
+ //! This is the maximum current that will be offered to a
+ //! battery while it is being conditioned. A typical value is
+ //! 0.1C.
+ //!
+ //! Note that the hardware has a minimum resolution of 10mA
+ //! (see the data sheet for details). If the given current
+ //! cannot be expressed exactly, then the largest expressible
+ //! smaller value will be used.
+
+ uint16_t u16ConditioningCurrent;
+
+ //! \brief Units in milliseconds.
+ //!
+ //! This field configures the conditioning time-out. This is
+ //! the maximum amount of time that a battery will be
+ //! conditioned before the battery charger declares it to be
+ //! broken.
+ //!
+ //! Note that the minimum time resolution of the battery
+ //! charger is the configured period between calls to advance
+ //! the state machine. If the given time-out cannot be
+ //! expressed exactly, then the shortest expressible longer
+ //! value will be used.
+
+ uint32_t u32ConditioningTimeout;
+
+ //! \brief Units in millivolts.
+ //!
+ //! This field configures the final charging voltage. At this
+ //! writing, only two values are permitted: 4100 or 4200.
+
+ uint16_t u16ChargingVoltage;
+
+ //! \brief Units in milliamps.
+ //!
+ //! This field configures the maximum current offered to a
+ //! charging battery.
+ //!
+ //! Note that the hardware has a minimum resolution of 10mA
+ //! (see the data sheet for details). If the given current
+ //! cannot be expressed exactly, then the largest expressible
+ //! smaller value will be used.
+
+ uint16_t u16ChargingCurrent;
+
+ //! \brief Units in milliamps.
+ //!
+ //! This field configures the current flow below which a
+ //! charging battery is regarded as fully charged (typical
+ //! 0.1C). At this point, the battery will be topped off.
+ //!
+ //! Note that the hardware has a minimum resolution of 10mA
+ //! (see the data sheet for details). If the given current
+ //! cannot be expressed exactly, then the largest expressible
+ //! smaller value will be used.
+
+ uint16_t u16ChargingThresholdCurrent;
+
+ //! \brief Units in milliamps.
+ //!
+ //! When charging while the DCDC converter's are enabled, the charger
+ //! is suppling current to both the battery and the Vbat input of the
+ //! DCDC converter. Once the total battery charger current falls
+ //! below this level, the charger will then stop charging until the
+ //! the battery voltage reaches the BC_LOW_DCDCMODE_BATTERY_VOLTAGE
+ //! threshold or until the DCDCs are no longer enabled.
+ //!
+ //! Typically, this value should be left at 180 to avoid the risk
+ //! of topping off the battery too long in DCDC mode and avoid
+ //! exceeding the BC_CHARGING_TIMEOUT time which would put the charger
+ //! driver in the broken state and completely disable charging.
+ //!
+ //! Note that the hardware has a minimum resolution of 10mA
+ //! (see the data sheet for details). If the given current
+ //! cannot be expressed exactly, then the largest expressible
+ //! smaller value will be used.
+ uint16_t u16DdcdModeChargingThresholdCurrent;
+
+ //! \brief Units in milliseconds.
+ //!
+ //! This field configures the charging time-out. This is the
+ //! maximum amount of time that a battery will be charged
+ //! before the battery charger declares it to be broken.
+ //!
+ //! Note that the minimum time resolution of the battery
+ //! charger is the configured period between calls to advance
+ //! the state machine. If the given time-out cannot be
+ //! expressed exactly, then the shortest expressible longer
+ //! value will be used.
+
+ uint32_t u32ChargingTimeout;
+
+ //! \brief Units in milliseconds.
+ //!
+ //! This field configures the top-off period. This is the
+ //! amount of time a battery will be held in the Topping Off
+ //! state before it is declared fully charged.
+ //!
+ //! Note that the minimum time resolution of the battery
+ //! charger is the configured period between calls to advance
+ //! the state machine. If the given time-out cannot be
+ //! expressed exactly, then the shortest expressible longer
+ //! value will be used.
+
+ uint32_t u32TopOffPeriod;
+
+ //! \brief Units in milliseconds.
+ //!
+ //! This field configures the top-off period when the DCDC
+ //! converters are enabled. To avoid topping off the LiIon
+ //! battery too long and reducing it's long term capacity,
+ //! This time should be kept failry short.
+ //!
+ //! Note that the minimum time resolution of the battery
+ //! charger is the configured period between calls to advance
+ //! the state machine. If the given time-out cannot be
+ //! expressed exactly, then the shortest expressible longer
+ //! value will be used.
+ uint32_t u32DcdcModeTopOffPeriod;
+
+ //! \brief Causes the battery charger to use an externally generated bias current
+ //!
+ //! If cleared, this causes the battery charger to use an
+ //! externally generated bias current, which is expected to be
+ //! quite precise. Otherwise, the battery charger will
+ //! generate a lesser-quality bias current internally.
+
+ uint8_t useInternalBias:1;
+
+ //! \brief Indicates that the battery charger is to monitor the die temperature.
+ //!
+ //! If set, this field indicates that the battery charger is to
+ //! monitor the die temperature. See below for fields that
+ //! configure the details.
+
+ uint8_t monitorDieTemp:1;
+
+ //! \brief Indicates that the battery charger is to monitor the battery temperature.
+ //!
+ //! If set, this field indicates that the battery charger is to
+ //! monitor the battery temperature. See below for fields that
+ //! configure the details.
+
+ uint8_t monitorBatteryTemp:1;
+
+ //! \brief Units in degrees centigrade.
+ //!
+ //! Note that the hardware reports die temperature in ranges of
+ //! 10 degree resolution minimum (see the data sheet for
+ //! details). If the battery charger is monitoring the die
+ //! temperature, and it rises to a range that includes a
+ //! temperature greater than or equal to this value, the
+ //! charging current will be clamped to the safe current.
+
+ int8_t u8DieTempHigh;
+
+ //! \brief Units in degrees centigrade.
+ //!
+ //! Note that the hardware reports die temperature in ranges of
+ //! 10 degrees minimum (see the data sheet for details). If the
+ //! charging current is being clamped because of a high die
+ //! temperature, and it falls to a range that doesn’t include a
+ //! temperatures greater than or equal to this value, the
+ //! charging current clamp will be released.
+
+ int8_t u8DieTempLow;
+
+ //! \brief Units in milliamps.
+ //!
+ //! If the battery charger detects a high die temperature, it
+ //! will clamp the charging current at or below this value.
+
+ uint16_t u16DieTempSafeCurrent;
+
+ //! \brief If the battery charger is monitoring the battery
+ //! temperature, this field indicates the LRADC channel to
+ //! read.
+
+ uint8_t u8BatteryTempChannel;
+
+ //! \brief If the battery charger is monitoring the battery
+ //! temperature, and it rises to a measurement greater than or
+ //! equal to this value, the charging current will be clamped
+ //! to the corresponding safe current.
+
+ uint16_t u16BatteryTempHigh;
+
+ //! \brief If the charging current is being clamped because of a high
+ //! battery temperature, and it falls below this value, the
+ //! charging current clamp will be released.
+
+ uint16_t u16BatteryTempLow;
+
+ //! \brief Units in milliamps.
+ //!
+ //! If the battery charger detects a high battery temperature,
+ //! it will clamp the charging current at or below this value.
+
+ uint16_t u16BatteryTempSafeCurrent;
+
+ //! \brief Units in millivolts.
+ //!
+ //! In the WaitingToCharge state, if we are in DCDC
+ //! operating modes, if the battery voltage measurement
+ //! is below this value, we immediately proceed with charging.
+ //! the low criteria for this value is that it must be high
+ //! to not risk the battery voltage getting too low. The
+ //! upper criteria is that you do not want the IR voltage
+ //! drop under heavy loads to make you start charging too soon
+ //! because the goal in DCDC operating mode is to not be constantly
+ //! topping off the battery which can shorten its life
+
+ uint16_t u16LowDcdcBatteryVoltage_mv;
+
+ uint32_t u32StateMachineNonChargingPeriod;
+} ddi_bc_Cfg_t;
+
+//! Status returned by Battery Charger functions.
+
+typedef enum _ddi_bc_Status {
+ //! \brief TBD
+ DDI_BC_STATUS_SUCCESS = 0,
+ //! \brief TBD
+ DDI_BC_STATUS_HARDWARE_DISABLED,
+ //! \brief TBD
+ DDI_BC_STATUS_BAD_BATTERY_MODE,
+ //! \brief TBD
+ DDI_BC_STATUS_CLOCK_GATE_CLOSED,
+ //! \brief TBD
+ DDI_BC_STATUS_NOT_INITIALIZED,
+ //! \brief TBD
+ DDI_BC_STATUS_ALREADY_INITIALIZED,
+ //! \brief TBD
+ DDI_BC_STATUS_BROKEN,
+ //! \brief TBD
+ DDI_BC_STATUS_NOT_BROKEN,
+ //! \brief TBD
+ DDI_BC_STATUS_NOT_DISABLED,
+ //! \brief TBD
+ DDI_BC_STATUS_BAD_ARGUMENT,
+ //! \brief TBD
+ DDI_BC_STATUS_CFG_BAD_BATTERY_TEMP_CHANNEL,
+ //! \brief TBD
+ DDI_BC_STATUS_CFG_BAD_CHARGING_VOLTAGE,
+} ddi_bc_Status_t;
+
+/////////////////////////////////////////////////////////////////////////////////
+// BCM Event Codes
+//
+// These are the codes that might be published to PMI Subscribers.
+/////////////////////////////////////////////////////////////////////////////////
+
+#define DDI_BC_EVENT_GROUP (11<<10)
+
+//! \brief TBD
+//! \todo [PUBS] Add definition(s)...
+typedef enum {
+ // Use the error code group value to make events unique for the EOI
+ //! \brief TBD
+ ddi_bc_MinEventCode = DDI_BC_EVENT_GROUP,
+ //! \brief TBD
+ ddi_bc_WaitingToChargeCode,
+ //! \brief TBD
+ ddi_bc_State_ConditioningCode,
+ //! \brief TBD
+ ddi_bc_State_Topping_OffCode,
+ //! \brief TBD
+ ddi_bc_State_BrokenCode,
+ //! \brief TBD
+ ddi_bc_SettingChargeCode,
+ //! \brief TBD
+ ddi_bc_RaisingDieTempAlarmCode,
+ //! \brief TBD
+ ddi_bc_DroppingDieTempAlarmCode,
+
+ //! \brief TBD
+ ddi_bc_MaxEventCode,
+ //! \brief TBD
+ ddi_bc_DcdcModeWaitingToChargeCode
+} ddi_bc_Event_t;
+
+////////////////////////////////////////////////////////////////////////////////
+// Prototypes
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+//! \brief Initialize the Battery Charger.
+//!
+//! \fntype Function
+//!
+//! This function initializes the Battery Charger.
+//!
+//! \param[in] pCfg A pointer to the new configuration.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS
+//! If the operation succeeded.
+//! \retval DDI_BC_STATUS_ALREADY_INITIALIZED
+//! If the Battery Charger is already initialized.
+//! \retval DDI_BC_STATUS_HARDWARE_DISABLED
+//! If the Battery Charger hardware is disabled by a laser fuse.
+//! \retval DDI_BC_STATUS_BAD_BATTERY_MODE
+//! If the power supply is set up for a non-rechargeable battery.
+//! \retval DDI_BC_STATUS_CLOCK_GATE_CLOSED
+//! If the clock gate for the power supply registers is closed.
+//! \retval DDI_BC_STATUS_CFG_BAD_CHARGING_VOLTAGE
+//! If the charging voltage is not either 4100 or 4200.
+//! \retval DDI_BC_STATUS_CFG_BAD_BATTERY_TEMP_CHANNEL
+//! If the LRADC channel number for monitoring battery temperature
+//! is bad.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_init.c.
+////////////////////////////////////////////////////////////////////////////////
+extern ddi_bc_Status_t ddi_bc_Init(ddi_bc_Cfg_t * pCfg);
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the Battery Charger configuration.
+//!
+//! \fntype Function
+//!
+//! This function reports the Battery Charger configuration.
+//!
+//! Note that, if the Battery Charger has not yet been initialized, the data
+//! returned by this function is unknown.
+//!
+//! \param[in,out] pCfg A pointer to a structure that will receive the data.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_api.c.
+////////////////////////////////////////////////////////////////////////////////
+extern void ddi_bc_QueryCfg(ddi_bc_Cfg_t * pCfg);
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Shut down the Battery Charger.
+//!
+//! \fntype Function
+//!
+//! This function immediately shuts down the Battery Charger hardware and
+//! returns the state machine to the Uninitialized state. Use this function to
+//! safely “mummify” the battery charger before retiring it from memory.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_api.c.
+////////////////////////////////////////////////////////////////////////////////
+extern void ddi_bc_ShutDown(void);
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Advances the state machine.
+//!
+//! \fntype Function
+//!
+//! This function advances the state machine.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If all goes well
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//! \retval DDI_BC_STATUS_BROKEN If the battery violated a time-out
+//! and has been declared broken.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_api.c.
+////////////////////////////////////////////////////////////////////////////////
+extern ddi_bc_Status_t ddi_bc_StateMachine(void);
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Get the Battery Charger's current state.
+//!
+//! \fntype Function
+//!
+//! This function returns the current state.
+//!
+//! \retval The current state.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_api.c.
+////////////////////////////////////////////////////////////////////////////////
+extern ddi_bc_State_t ddi_bc_GetState(void);
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Disable the Battery Charger.
+//!
+//! \fntype Function
+//!
+//! This function forces the Battery Charger into the Disabled state.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If all goes well
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_api.c.
+////////////////////////////////////////////////////////////////////////////////
+extern ddi_bc_Status_t ddi_bc_SetDisable(void);
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Enable the Battery Charger.
+//!
+//! \fntype Function
+//!
+//! If the Battery Charger is in the Disabled state, this function moves it to
+//! the Waiting to Charge state.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If all goes well
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//! \retval DDI_BC_STATUS_NOT_DISABLED If the Battery Charger is not
+//! disabled.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_api.c.
+////////////////////////////////////////////////////////////////////////////////
+extern ddi_bc_Status_t ddi_bc_SetEnable(void);
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Declare the battery to be broken.
+//!
+//! \fntype Function
+//!
+//! This function forces the Battery Charger into the Broken state.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If all goes well
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_api.c.
+////////////////////////////////////////////////////////////////////////////////
+extern ddi_bc_Status_t ddi_bc_SetBroken(void);
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Declare the battery to be fixed.
+//!
+//! \fntype Function
+//!
+//! If the Battery Charger is in the Broken state, this function moves it to
+//! the Disabled state.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If all goes well
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//! \retval DDI_BC_STATUS_NOT_BROKEN If the Battery Charger is not broken.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_api.c.
+////////////////////////////////////////////////////////////////////////////////
+extern ddi_bc_Status_t ddi_bc_SetFixed(void);
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Set the current limit.
+//!
+//! \fntype Function
+//!
+//! This function applies a limit to the current that the Battery Charger can
+//! draw.
+//!
+//! \param[in] u16Limit The maximum current the Battery Charger can draw
+//! (in mA).
+//!
+//! \retval The expressible version of the limit.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_api.c.
+////////////////////////////////////////////////////////////////////////////////
+extern uint16_t ddi_bc_SetCurrentLimit(uint16_t u16Limit);
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the current limit.
+//!
+//! \fntype Function
+//!
+//! This function reports the limit to the current that the Battery Charger can
+//! draw.
+//!
+//! \retval The current limit.
+//!
+//! \internal
+//! \see To view the function definition, see ddi_bc_api.c.
+////////////////////////////////////////////////////////////////////////////////
+extern uint16_t ddi_bc_GetCurrentLimit(void);
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Set the current threshold.
+//!
+//! \fntype Function
+//!
+//!
+//! \param[in] u16Current Current threshold where charger deactivates (in mA).
+//!
+//!
+////////////////////////////////////////////////////////////////////////////////
+extern uint16_t ddi_bc_SetCurrentThreshold(uint16_t u16Current);
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Set the battery charger state machine period.
+//!
+//! \fntype Function
+//!
+//! This function sets a new state machine period. The Period and Slope should
+//! be coordinated to achieve the minimal ramp step current which will minimize
+//! transients on the system.
+//!
+//! \param[in] u32StateMachinePeriod (in milliseconds)
+//! \param[in] u16CurrentRampSlope (in mA/s)
+//!
+//! \retval SUCCESS If all goes well
+//! \retval ERROR_DDI_BCM_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//!
+////////////////////////////////////////////////////////////////////////////////
+extern ddi_bc_Status_t ddi_bc_SetNewPeriodAndSlope(uint32_t
+ u32StateMachinePeriod,
+ uint16_t
+ u16CurrentRampSlope);
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the state machine period.
+//!
+//! \fntype Function
+//!
+//! This function reports the battery charger period.
+//!
+//! \retval The battery charger period (in milliseconds).
+//!
+////////////////////////////////////////////////////////////////////////////////
+extern uint32_t ddi_bc_GetStateMachinePeriod(void);
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the current ramp slope.
+//!
+//! \fntype Function
+//!
+//! This function reports the current ramp slope.
+//!
+//! \retval The current ramp slope (in mA/s).
+//!
+////////////////////////////////////////////////////////////////////////////////
+extern uint32_t ddi_bc_GetCurrentRampSlope(void);
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the time spent in the present state (milliseconds)
+//!
+//! \fntype Function
+//!
+//! This function reports the time spent in the present charging state. Note that
+//! for the states that actually charge the battery, this time does not include the
+//! time spent under alarm conditions such as die termperature alarm or battery
+//! temperature alarm.
+//!
+//! \retval The time spent in the current state in milliseconds.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint32_t ddi_bc_GetStateTime(void);
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the reason for being in the broken state
+//!
+//! \fntype Function
+//!
+//!
+//! \retval ddi_bc_BrokenReason_t enumeration
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_BrokenReason_t ddi_bc_GetBrokenReason(void);
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Restart the charge cycle
+//!
+//! \fntype Function
+//!
+//! \retval SUCCESS
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_ForceChargingToStart(void);
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+#endif // _DDI_BC_H
+//! @}
diff --git a/arch/arm/mach-stmp3xxx/include/mach/debug-macro.S b/arch/arm/mach-stmp3xxx/include/mach/debug-macro.S
new file mode 100644
index 000000000000..c354e4e0cb72
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/debug-macro.S
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ .macro addruart,rx
+ mrc p15, 0, \rx, c1, c0
+ tst \rx, #1 @ MMU enabled?
+ moveq \rx, #0x80000000 @ physical base address
+ addeq \rx, \rx, #0x00070000
+ movne \rx, #0xf0000000 @ virtual base
+ addne \rx, \rx, #0x00070000
+ .endm
+
+ .macro senduart,rd,rx
+ strb \rd, [\rx, #0] @ data register at 0
+ .endm
+
+ .macro waituart,rd,rx
+1001: ldr \rd, [\rx, #0x18] @ UARTFLG
+ tst \rd, #1 << 5 @ UARTFLGUTXFF - 1 when full
+ bne 1001b
+ .endm
+
+ .macro busyuart,rd,rx
+1001: ldr \rd, [\rx, #0x18] @ UARTFLG
+ tst \rd, #1 << 3 @ UARTFLGUBUSY - 1 when busy
+ bne 1001b
+ .endm
diff --git a/arch/arm/mach-stmp3xxx/include/mach/dma.h b/arch/arm/mach-stmp3xxx/include/mach/dma.h
new file mode 100644
index 000000000000..cc500ea3f053
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/dma.h
@@ -0,0 +1,161 @@
+/*
+ * Freescale STMP37XX/STMP378X DMA helper interface
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_PLAT_STMP3XXX_DMA_H
+#define __ASM_PLAT_STMP3XXX_DMA_H
+
+#include <linux/platform_device.h>
+#include <linux/dmapool.h>
+
+#if !defined(MAX_PIO_WORDS)
+#define MAX_PIO_WORDS (15)
+#endif
+
+#define STMP3XXX_BUS_APBH 0
+#define STMP3XXX_BUS_APBX 1
+#define STMP3XXX_DMA_MAX_CHANNEL 16
+
+
+#define STMP3xxx_DMA(channel, bus) ((bus) * 16 + (channel))
+
+struct stmp3xxx_dma_command {
+ u32 next;
+ u32 cmd;
+ union {
+ u32 buf_ptr;
+ u32 alternate;
+ };
+ u32 pio_words[MAX_PIO_WORDS];
+};
+
+struct stmp3xxx_dma_descriptor {
+ struct stmp3xxx_dma_command *command;
+ dma_addr_t handle;
+
+ /* The virtual address of the buffer pointer */
+ void *virtual_buf_ptr;
+ /* The next descriptor in a the DMA chain (optional) */
+ struct stmp3xxx_dma_descriptor *next_descr;
+};
+
+struct stmp37xx_circ_dma_chain {
+ unsigned total_count;
+ struct stmp3xxx_dma_descriptor *chain;
+
+ unsigned free_index;
+ unsigned free_count;
+ unsigned active_index;
+ unsigned active_count;
+ unsigned cooked_index;
+ unsigned cooked_count;
+
+ int bus;
+ unsigned channel;
+};
+
+static inline struct stmp3xxx_dma_descriptor
+ *stmp3xxx_dma_circ_get_free_head(struct stmp37xx_circ_dma_chain *chain)
+{
+ return &(chain->chain[chain->free_index]);
+}
+
+static inline struct stmp3xxx_dma_descriptor
+ *stmp3xxx_dma_circ_get_cooked_head(struct stmp37xx_circ_dma_chain *chain)
+{
+ return &(chain->chain[chain->cooked_index]);
+}
+
+int stmp3xxx_dma_request(int ch, struct device *dev, char *name);
+int stmp3xxx_dma_release(int ch);
+int stmp3xxx_dma_allocate_command(int ch,
+ struct stmp3xxx_dma_descriptor *descriptor);
+int stmp3xxx_dma_free_command(int ch,
+ struct stmp3xxx_dma_descriptor *descriptor);
+void stmp3xxx_dma_continue(int channel, u32 semaphore);
+void stmp3xxx_dma_go(int ch, struct stmp3xxx_dma_descriptor *head,
+ u32 semaphore);
+int stmp3xxx_dma_running(int ch);
+int stmp3xxx_dma_make_chain(int ch, struct stmp37xx_circ_dma_chain *chain,
+ struct stmp3xxx_dma_descriptor descriptors[],
+ unsigned items);
+void stmp3xxx_dma_free_chain(struct stmp37xx_circ_dma_chain *chain);
+void stmp37xx_circ_clear_chain(struct stmp37xx_circ_dma_chain *chain);
+void stmp37xx_circ_advance_free(struct stmp37xx_circ_dma_chain *chain,
+ unsigned count);
+void stmp37xx_circ_advance_active(struct stmp37xx_circ_dma_chain *chain,
+ unsigned count);
+unsigned stmp37xx_circ_advance_cooked(struct stmp37xx_circ_dma_chain *chain);
+int stmp3xxx_dma_read_semaphore(int ch);
+void stmp3xxx_dma_init(void);
+void stmp3xxx_dma_set_alt_target(int ch, int target);
+void stmp3xxx_dma_suspend(void);
+void stmp3xxx_dma_resume(void);
+
+/*
+ * STMP37xx and STMP378x have different DMA control
+ * registers layout
+ */
+
+void stmp3xxx_arch_dma_freeze(int ch);
+void stmp3xxx_arch_dma_unfreeze(int ch);
+void stmp3xxx_arch_dma_reset_channel(int ch);
+void stmp3xxx_arch_dma_enable_interrupt(int ch);
+void stmp3xxx_arch_dma_clear_interrupt(int ch);
+int stmp3xxx_arch_dma_is_interrupt(int ch);
+
+static inline void stmp3xxx_dma_reset_channel(int ch)
+{
+ stmp3xxx_arch_dma_reset_channel(ch);
+}
+
+
+static inline void stmp3xxx_dma_freeze(int ch)
+{
+ stmp3xxx_arch_dma_freeze(ch);
+}
+
+static inline void stmp3xxx_dma_unfreeze(int ch)
+{
+ stmp3xxx_arch_dma_unfreeze(ch);
+}
+
+static inline void stmp3xxx_dma_enable_interrupt(int ch)
+{
+ stmp3xxx_arch_dma_enable_interrupt(ch);
+}
+
+static inline void stmp3xxx_dma_clear_interrupt(int ch)
+{
+ stmp3xxx_arch_dma_clear_interrupt(ch);
+}
+
+static inline int stmp3xxx_dma_is_interrupt(int ch)
+{
+ return stmp3xxx_arch_dma_is_interrupt(ch);
+}
+
+#define MAX_DMA_ADDRESS 0xffffffff
+
+#define MAX_DMA_CHANNELS 32
+
+/*
+ * Channel number on the APBX bus
+ */
+
+#define STMP378X_APBX_I2C 3
+
+#endif /* __ASM_PLAT_STMP3XXX_DMA_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/entry-macro.S b/arch/arm/mach-stmp3xxx/include/mach/entry-macro.S
new file mode 100644
index 000000000000..731a92286da2
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/entry-macro.S
@@ -0,0 +1,35 @@
+/*
+ * Low-level IRQ helper macros for Freescale STMP378X
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ .macro disable_fiq
+ .endm
+
+ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
+
+ mov \base, #0xf0000000 @ vm address of IRQ controller
+ ldr \irqnr, [\base, #0x70] @ HW_ICOLL_STAT
+ cmp \irqnr, #0x7f
+ moveqs \irqnr, #0 @ Zero flag set for no IRQ
+
+ .endm
+
+ .macro get_irqnr_preamble, base, tmp
+ .endm
+
+ .macro arch_ret_to_user, tmp1, tmp2
+ .endm
diff --git a/arch/arm/mach-stmp3xxx/include/mach/fsl_usb.h b/arch/arm/mach-stmp3xxx/include/mach/fsl_usb.h
new file mode 100644
index 000000000000..87cc7d8a7abf
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/fsl_usb.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * USB Host side, platform-specific functionality.
+ */
+
+#include <linux/usb/fsl_xcvr.h>
+#include <mach/arc_otg.h>
+
+/* ehci_arc_hc_driver.flags value */
+#define FSL_PLATFORM_HC_FLAGS (HCD_USB2 | HCD_MEMORY)
+
+static void fsl_setup_phy(struct ehci_hcd *ehci,
+ enum fsl_usb2_phy_modes phy_mode,
+ int port_offset);
+
+static inline void fsl_platform_usb_setup(struct ehci_hcd *ehci)
+{
+ struct fsl_usb2_platform_data *pdata;
+
+ pdata = ehci_to_hcd(ehci)->self.controller->platform_data;
+ fsl_setup_phy(ehci, pdata->phy_mode, 0);
+}
+
+static inline void fsl_platform_set_host_mode(struct usb_hcd *hcd)
+{
+ unsigned int temp;
+ struct fsl_usb2_platform_data *pdata;
+
+ pdata = hcd->self.controller->platform_data;
+
+ if (pdata->xcvr_ops && pdata->xcvr_ops->set_host)
+ pdata->xcvr_ops->set_host();
+
+ /* set host mode */
+ temp = readl(hcd->regs + 0x1a8);
+ writel(temp | USBMODE_CM_HOST, hcd->regs + 0x1a8);
+}
+
+/* Needed for i2c/serial transceivers */
+static inline void
+fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata, int on)
+{
+}
+
+/* Set USB AHB burst length for host */
+static inline void fsl_platform_set_ahb_burst(struct usb_hcd *hcd)
+{
+}
diff --git a/arch/arm/mach-stmp3xxx/include/mach/fsl_usb_gadget.h b/arch/arm/mach-stmp3xxx/include/mach/fsl_usb_gadget.h
new file mode 100644
index 000000000000..638bfa4be52b
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/fsl_usb_gadget.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * USB Gadget side, platform-specific functionality.
+ */
+
+#include <linux/usb/fsl_xcvr.h>
+
+/* Needed for i2c/serial transceivers */
+static inline void
+fsl_platform_set_device_mode(struct fsl_usb2_platform_data *pdata)
+{
+}
+
+static inline void
+fsl_platform_pullup_enable(struct fsl_usb2_platform_data *pdata)
+{
+}
+
+static inline void
+fsl_platform_pullup_disable(struct fsl_usb2_platform_data *pdata)
+{
+}
+
+static inline void
+fsl_platform_set_test_mode(struct fsl_usb2_platform_data *pdata,
+ enum usb_test_mode mode)
+{
+}
diff --git a/arch/arm/mach-stmp3xxx/include/mach/gpio.h b/arch/arm/mach-stmp3xxx/include/mach/gpio.h
new file mode 100644
index 000000000000..2dedc05cd7c0
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/gpio.h
@@ -0,0 +1,29 @@
+/*
+ * Freescale STMP37XX/STMP378X GPIO interface
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_GPIO_H
+#define __ASM_ARCH_GPIO_H
+
+extern int gpio_request(unsigned gpio, char *label);
+extern void gpio_free(unsigned gpio);
+extern void gpio_set_value(unsigned gpio, int value);
+extern int gpio_get_value(unsigned gpio);
+extern int gpio_to_irq(unsigned gpio);
+extern void gpio_direction_input(unsigned id);
+extern void gpio_direction_output(unsigned id, int value);
+
+#endif /* __ASM_ARCH_GPIO_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/gpmi.h b/arch/arm/mach-stmp3xxx/include/mach/gpmi.h
new file mode 100644
index 000000000000..e3c00f02cfe4
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/gpmi.h
@@ -0,0 +1,45 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * Arch-dependent structure and functions declarations
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_PLAT_GPMI_H
+#define __ASM_PLAT_GPMI_H
+
+#define GPMI_PART_CONCAT 0x8000 /* indicates that partitions
+ should be concatenated */
+extern int gpmi_pinmux_request(char *);
+extern void gpmi_pinmux_free(char *);
+
+struct gpmi_platform_data {
+
+ u_int32_t uid_offset;
+ u_int32_t uid_size;
+
+ int items;
+ int io_uA;
+ char *concat_name;
+ char **concat_parts;
+
+ struct {
+ const char **part_probe_types;
+ int nr_partitions;
+ struct mtd_partition *partitions;
+ } parts[];
+
+};
+#endif /* __ASM_PLAT_GPMI_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/hardware.h b/arch/arm/mach-stmp3xxx/include/mach/hardware.h
new file mode 100644
index 000000000000..095834f23d0a
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/hardware.h
@@ -0,0 +1,32 @@
+/*
+ * This file contains the hardware definitions of the Freescale STMP378X
+ *
+ * Copyright (C) 2005 Sigmatel Inc
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H
+
+/*
+ * Where in virtual memory the IO devices (timers, system controllers
+ * and so on)
+ */
+#define IO_BASE 0xF0000000 /* VA of IO */
+#define IO_SIZE 0x00100000 /* How much? */
+#define IO_START 0x80000000 /* PA of IO */
+
+/* macro to get at IO space when running virtually */
+#define IO_ADDRESS(x) (((x) & 0x000fffff) + IO_BASE)
+
+#endif
diff --git a/arch/arm/mach-stmp3xxx/include/mach/i2c.h b/arch/arm/mach-stmp3xxx/include/mach/i2c.h
new file mode 100644
index 000000000000..05a57f6351e3
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/i2c.h
@@ -0,0 +1,48 @@
+/*
+ * Freescale STMP378X I2C low-level/dma functions
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 _ARM_ARCH_I2C_H
+#define _ARM_ARCH_I2C_H
+
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include <linux/completion.h>
+#include <linux/i2c.h>
+
+#define I2C_READ 1
+#define I2C_WRITE 0
+
+void hw_i2c_clear_dma_interrupt(void);
+int hw_i2c_init(struct device *dev);
+void hw_i2c_stop(struct device *dev);
+void hw_i2c_setup_write(u8 addr, void *buff, int len, int flags);
+void hw_i2c_setup_read(u8 addr, void *buff, int len, int flags);
+void hw_i2c_run(int dir);
+void hw_i2c_reset_dma(void);
+void hw_i2c_finish_read(void *buff, int len);
+
+struct stmp378x_i2c_dev {
+ struct device *dev;
+ int irq_dma;
+ int irq_err;
+ struct completion cmd_complete;
+ u32 cmd_err;
+ struct i2c_adapter adapter;
+};
+
+#endif
diff --git a/arch/arm/mach-stmp3xxx/include/mach/io.h b/arch/arm/mach-stmp3xxx/include/mach/io.h
new file mode 100644
index 000000000000..4cd92cf9c4f3
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/io.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2005 Sigmatel Inc
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffffffff
+
+static inline void __iomem *__io(unsigned long addr)
+{
+ return (void __iomem *)addr;
+}
+#define __io(a) __io(a)
+#define __mem_pci(a) (a)
+#define __mem_isa(a) (a)
+
+#endif
diff --git a/arch/arm/mach-stmp3xxx/include/mach/irqs.h b/arch/arm/mach-stmp3xxx/include/mach/irqs.h
new file mode 100644
index 000000000000..27e63917f3f6
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/irqs.h
@@ -0,0 +1,94 @@
+/*
+ * Freescale STMP378X interrupts
+ *
+ * Copyright (C) 2005 Sigmatel Inc
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#define IRQ_DEBUG_UART 0
+#define IRQ_COMMS_RX 1
+#define IRQ_COMMS_TX 1
+#define IRQ_SSP2_ERROR 2
+#define IRQ_VDD5V 3
+#define IRQ_HEADPHONE_SHORT 4
+#define IRQ_DAC_DMA 5
+#define IRQ_DAC_ERROR 6
+#define IRQ_ADC_DMA 7
+#define IRQ_ADC_ERROR 8
+#define IRQ_SPDIF_DMA 9
+#define IRQ_SAIF2_DMA 9
+#define IRQ_SPDIF_ERROR 10
+#define IRQ_SAIF1_IRQ 10
+#define IRQ_SAIF2_IRQ 10
+#define IRQ_USB_CTRL 11
+#define IRQ_USB_WAKEUP 12
+#define IRQ_GPMI_DMA 13
+#define IRQ_SSP1_DMA 14
+#define IRQ_SSP_ERROR 15
+#define IRQ_GPIO0 16
+#define IRQ_GPIO1 17
+#define IRQ_GPIO2 18
+#define IRQ_SAIF1_DMA 19
+#define IRQ_SSP2_DMA 20
+#define IRQ_ECC8_IRQ 21
+#define IRQ_RTC_ALARM 22
+#define IRQ_UARTAPP_TX_DMA 23
+#define IRQ_UARTAPP_INTERNAL 24
+#define IRQ_UARTAPP_RX_DMA 25
+#define IRQ_I2C_DMA 26
+#define IRQ_I2C_ERROR 27
+#define IRQ_TIMER0 28
+#define IRQ_TIMER1 29
+#define IRQ_TIMER2 30
+#define IRQ_TIMER3 31
+#define IRQ_BATT_BRNOUT 32
+#define IRQ_VDDD_BRNOUT 33
+#define IRQ_VDDIO_BRNOUT 34
+#define IRQ_VDD18_BRNOUT 35
+#define IRQ_TOUCH_DETECT 36
+#define IRQ_LRADC_CH0 37
+#define IRQ_LRADC_CH1 38
+#define IRQ_LRADC_CH2 39
+#define IRQ_LRADC_CH3 40
+#define IRQ_LRADC_CH4 41
+#define IRQ_LRADC_CH5 42
+#define IRQ_LRADC_CH6 43
+#define IRQ_LRADC_CH7 44
+#define IRQ_LCDIF_DMA 45
+#define IRQ_LCDIF_ERROR 46
+#define IRQ_DIGCTL_DEBUG_TRAP 47
+#define IRQ_RTC_1MSEC 48
+#define IRQ_DRI_DMA 49
+#define IRQ_DRI_ATTENTION 50
+#define IRQ_GPMI_ATTENTION 51
+#define IRQ_IR 52
+#define IRQ_DCP_VMI 53
+#define IRQ_DCP 54
+#define IRQ_BCH 56
+#define IRQ_PXP 57
+#define IRQ_UARTAPP2_TX_DMA 58
+#define IRQ_UARTAPP2_INTERNAL 59
+#define IRQ_UARTAPP2_RX_DMA 60
+#define IRQ_VDAC_DETECT 61
+#define IRQ_VDD5V_DROOP 64
+#define IRQ_DCDC4P2_BO 65
+
+
+#define NR_IRQS 128
+
+/* All interrupts are FIQ capable */
+#define FIQ_START IRQ_DEBUG_UART
+
+/* Hard disk IRQ is a GPMI attention IRQ */
+#define IRQ_HARDDISK IRQ_GPMI_ATTENTION
diff --git a/arch/arm/mach-stmp3xxx/include/mach/lcdif.h b/arch/arm/mach-stmp3xxx/include/mach/lcdif.h
new file mode 100644
index 000000000000..1bdadff8713a
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/lcdif.h
@@ -0,0 +1,430 @@
+/*
+ * Freescale STMP378X LCDIF interfaces
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 _ARCH_ARM_LCDIF_H
+#define _ARCH_ARM_LCDIF_H
+
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/list.h>
+#include <linux/backlight.h>
+#include <linux/dma-mapping.h>
+#include <linux/regulator/consumer.h>
+#include "dma.h"
+
+#include "regs-lcdif.h"
+#include "regs-apbh.h"
+
+enum {
+ SPI_MOSI = 0,
+ SPI_SCLK,
+ SPI_CS,
+};
+
+struct stmp3xxx_lcd_dma_chain_info {
+ dma_addr_t *dma_addr_p;
+ unsigned offset;
+};
+
+
+enum {
+ STMP3XXX_LCD_PANEL_SYSTEM = 0,
+ STMP3XXX_LCD_PANEL_VSYNC,
+ STMP3XXX_LCD_PANEL_DOTCLK,
+ STMP3XXX_LCD_PANEL_DVI,
+};
+
+struct stmp3xxx_platform_bl_data;
+struct stmp3xxx_platform_fb_entry {
+ char name[16];
+ u16 x_res;
+ u16 y_res;
+ u16 bpp;
+ u32 cycle_time_ns;
+ int lcd_type;
+ int (*init_panel)(struct device *dev, dma_addr_t phys, int memsize,
+ struct stmp3xxx_platform_fb_entry *pentry);
+ void (*release_panel)(struct device *dev,
+ struct stmp3xxx_platform_fb_entry *pentry);
+ int (*blank_panel)(int blank);
+ void (*run_panel)(void);
+ void (*stop_panel)(void);
+ int (*pan_display)(dma_addr_t phys);
+ int (*update_panel)(void *p, struct stmp3xxx_platform_fb_entry *pentry);
+ struct list_head link;
+ struct stmp3xxx_platform_bl_data *bl_data;
+};
+
+struct stmp3xxx_platform_fb_data {
+ struct list_head list;
+ struct stmp3xxx_platform_fb_entry *cur;
+};
+
+#define STMP3XXX_LCDIF_PANEL_INIT 1
+#define STMP3XXX_LCDIF_PANEL_RELEASE 2
+
+struct stmp3xxx_platform_bl_data {
+ struct list_head list;
+ struct regulator *regulator;
+ int bl_gpio;
+ int bl_max_intensity;
+ int bl_cons_intensity;
+ int bl_default_intensity;
+ int (*init_bl)(struct stmp3xxx_platform_bl_data *data);
+ int (*set_bl_intensity)(struct stmp3xxx_platform_bl_data *data,
+ struct backlight_device *bd, int suspended);
+ void (*free_bl)(struct stmp3xxx_platform_bl_data *data);
+};
+
+static inline void stmp3xxx_lcd_register_entry(
+ struct stmp3xxx_platform_fb_entry *pentry,
+ struct stmp3xxx_platform_fb_data *pdata)
+{
+ list_add_tail(&pentry->link, &pdata->list);
+ if (!pdata->cur)
+ pdata->cur = pentry;
+}
+
+static inline void stmp3xxx_lcd_move_pentry_up(
+ struct stmp3xxx_platform_fb_entry *pentry,
+ struct stmp3xxx_platform_fb_data *pdata)
+{
+ list_del(&pentry->link);
+ list_add(&pentry->link, &pdata->list);
+}
+
+static inline int stmp3xxx_lcd_iterate_pdata(
+ struct stmp3xxx_platform_fb_data *pdata,
+ int (*func)(struct stmp3xxx_platform_fb_entry *pentry,
+ void *data,
+ int ret_prev),
+ void *data)
+{
+ struct stmp3xxx_platform_fb_entry *pentry;
+ int ret = 0;
+ list_for_each_entry(pentry, &pdata->list, link) {
+ ret = func(pentry, data, ret);
+ }
+ return ret;
+}
+
+static inline void stmp3xxx_lcd_set_bl_pdata(
+ struct stmp3xxx_platform_bl_data *pdata)
+{
+ extern struct platform_device stmp3xxx_backlight;
+ stmp3xxx_backlight.dev.platform_data = pdata;
+}
+
+void stmp3xxx_init_lcdif(void);
+int stmp3xxx_lcdif_dma_init(struct device *dev, dma_addr_t phys, int memsize,
+ int lcd_master);
+void stmp3xxx_lcdif_dma_release(void);
+void stmp3xxx_lcdif_run(void);
+void stmp3xxx_lcdif_stop(void);
+int stmp3xxx_lcdif_pan_display(dma_addr_t addr);
+
+
+int stmp3xxx_lcdif_register_client(struct notifier_block *nb);
+void stmp3xxx_lcdif_unregister_client(struct notifier_block *nb);
+void stmp3xxx_lcdif_notify_clients(unsigned long event,
+ struct stmp3xxx_platform_fb_entry *pentry);
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, u_int32_t)
+#endif
+
+#define LCD_DMA_CHANNEL 0
+
+static inline void setup_dotclk_panel(u16 v_pulse_width,
+ u16 v_period,
+ u16 v_wait_cnt,
+ u16 v_active,
+ u16 h_pulse_width,
+ u16 h_period,
+ u16 h_wait_cnt,
+ u16 h_active,
+ int enable_present)
+{
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_DATA_SHIFT_DIR);
+
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_SHIFT_NUM_BITS);
+
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_BYTE_PACKING_FORMAT);
+ HW_LCDIF_CTRL1_SET(BF_LCDIF_CTRL1_BYTE_PACKING_FORMAT(7) |
+ BM_LCDIF_CTRL1_RECOVER_ON_UNDERFLOW);
+
+ HW_LCDIF_TRANSFER_COUNT_CLR(BM_LCDIF_TRANSFER_COUNT_H_COUNT |
+ BM_LCDIF_TRANSFER_COUNT_V_COUNT);
+ HW_LCDIF_TRANSFER_COUNT_SET(BF_LCDIF_TRANSFER_COUNT_H_COUNT(h_active) |
+ BF_LCDIF_TRANSFER_COUNT_V_COUNT(v_active));
+
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_VSYNC_MODE);
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_WAIT_FOR_VSYNC_EDGE);
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_DVI_MODE);
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_DOTCLK_MODE);
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_BYPASS_COUNT);
+
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_WORD_LENGTH |
+ BM_LCDIF_CTRL_INPUT_DATA_SWIZZLE |
+ BM_LCDIF_CTRL_LCD_DATABUS_WIDTH);
+ HW_LCDIF_CTRL_SET(BF_LCDIF_CTRL_WORD_LENGTH(3) | /* 24 bit */
+ BM_LCDIF_CTRL_DATA_SELECT | /* data mode */
+ BF_LCDIF_CTRL_INPUT_DATA_SWIZZLE(0) | /* no swap */
+ BF_LCDIF_CTRL_LCD_DATABUS_WIDTH(3)); /* 24 bit */
+
+ HW_LCDIF_VDCTRL0_SET(BM_LCDIF_VDCTRL0_ENABLE_POL |
+ BM_LCDIF_VDCTRL0_DOTCLK_POL);
+ HW_LCDIF_VDCTRL0_CLR(BM_LCDIF_VDCTRL0_VSYNC_POL |
+ BM_LCDIF_VDCTRL0_HSYNC_POL);
+
+ HW_LCDIF_VDCTRL0_CLR(BM_LCDIF_VDCTRL0_VSYNC_OEB); /* vsync is output */
+
+ /*
+ * need enable sig for true RGB i/f. Or, if not true RGB, leave it
+ * zero.
+ */
+ HW_LCDIF_VDCTRL0_SET(BM_LCDIF_VDCTRL0_ENABLE_PRESENT);
+
+ /*
+ * For DOTCLK mode, count VSYNC_PERIOD in terms of complete hz lines
+ */
+ HW_LCDIF_VDCTRL0_SET(BM_LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT |
+ BM_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT);
+
+ HW_LCDIF_VDCTRL0_CLR(BM_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH);
+ HW_LCDIF_VDCTRL0_SET(v_pulse_width);
+
+ HW_LCDIF_VDCTRL1_CLR(BM_LCDIF_VDCTRL1_VSYNC_PERIOD);
+ HW_LCDIF_VDCTRL1_SET(v_period);
+
+ HW_LCDIF_VDCTRL2_CLR(BM_LCDIF_VDCTRL2_HSYNC_PERIOD |
+ BM_LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH);
+
+ HW_LCDIF_VDCTRL2_SET(BF_LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH(h_pulse_width) |
+ BF_LCDIF_VDCTRL2_HSYNC_PERIOD(h_period));
+
+ HW_LCDIF_VDCTRL4_CLR(BM_LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT);
+ HW_LCDIF_VDCTRL4_SET(
+ BF_LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT(h_active));
+
+ HW_LCDIF_VDCTRL3_CLR(BM_LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT |
+ BM_LCDIF_VDCTRL3_VERTICAL_WAIT_CNT);
+ HW_LCDIF_VDCTRL3_SET(BF_LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT(h_wait_cnt) |
+ BF_LCDIF_VDCTRL3_VERTICAL_WAIT_CNT(v_wait_cnt));
+
+ HW_LCDIF_VDCTRL4_SET(BM_LCDIF_VDCTRL4_SYNC_SIGNALS_ON);
+}
+
+static inline void release_dotclk_panel(void)
+{
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_DOTCLK_MODE);
+ HW_LCDIF_VDCTRL0_WR(0);
+ HW_LCDIF_VDCTRL1_WR(0);
+ HW_LCDIF_VDCTRL2_WR(0);
+ HW_LCDIF_VDCTRL3_WR(0);
+}
+
+static inline void dotclk_dma_chain_init(int memsize, dma_addr_t video_phys,
+ struct stmp3xxx_dma_descriptor *video_dma_descriptor,
+ struct stmp3xxx_lcd_dma_chain_info *dma_chain_info,
+ unsigned *dma_chain_info_pos)
+{
+ unsigned i, bytes_left;
+ dma_addr_t phys = video_phys;
+ bytes_left = memsize;
+
+ for (i = 0; bytes_left > 0; ++i) {
+ unsigned this_chain = bytes_left < 0xff00 ? bytes_left : 0xff00;
+ /* Count of 0 in the DMA word means 65536 */
+ unsigned xfer_count = this_chain & 65535;
+ stmp3xxx_dma_allocate_command(
+ STMP3xxx_DMA(LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH),
+ &video_dma_descriptor[i]);
+ if (i != 0) {
+ /* Chain previous command to this one */
+ video_dma_descriptor[i - 1].command->next =
+ video_dma_descriptor[i].handle;
+ /* Enable DMA chaining, disable IRQ and semaphore
+ * on previous command
+ */
+ video_dma_descriptor[i - 1].command->cmd &=
+ ~(BM_APBH_CHn_CMD_IRQONCMPLT |
+ BM_APBH_CHn_CMD_SEMAPHORE);
+ }
+ video_dma_descriptor[i].command->cmd =
+ BF_APBH_CHn_CMD_XFER_COUNT(xfer_count) |
+ BF_APBH_CHn_CMD_CMDWORDS(1) |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(2); /* DMA read */
+ video_dma_descriptor[i].command->pio_words[0] =
+ BM_LCDIF_CTRL_RUN |
+ BF_LCDIF_CTRL_INPUT_DATA_SWIZZLE(1) |
+ BM_LCDIF_CTRL_DATA_SHIFT_DIR |
+ BM_LCDIF_CTRL_DOTCLK_MODE |
+ BM_LCDIF_CTRL_BYPASS_COUNT |
+ BM_LCDIF_CTRL_DATA_SELECT;
+ video_dma_descriptor[i].command->buf_ptr = phys;
+ dma_chain_info[*dma_chain_info_pos].dma_addr_p =
+ &video_dma_descriptor[i].command->buf_ptr;
+ dma_chain_info[*dma_chain_info_pos].offset = phys - video_phys;
+ ++*dma_chain_info_pos;
+ phys += this_chain;
+ bytes_left -= this_chain;
+ }
+ video_dma_descriptor[i-1].command->next =
+ video_dma_descriptor[0].handle;
+ pr_debug("%s: Used %u DMA chains to cover %u bytes\n", __func__, i,
+ memsize);
+}
+
+static inline void setup_dvi_panel(u16 h_active, u16 v_active,
+ u16 h_blanking, u16 v_lines,
+ u16 v1_blank_start, u16 v1_blank_end,
+ u16 v2_blank_start, u16 v2_blank_end,
+ u16 f1_start, u16 f1_end,
+ u16 f2_start, u16 f2_end)
+{
+ /* 32bit packed format (RGB) */
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_BYTE_PACKING_FORMAT);
+ HW_LCDIF_CTRL1_SET(BF_LCDIF_CTRL1_BYTE_PACKING_FORMAT(0x7) |
+ BM_LCDIF_CTRL1_RECOVER_ON_UNDERFLOW);
+
+ HW_LCDIF_TRANSFER_COUNT_CLR(BM_LCDIF_TRANSFER_COUNT_H_COUNT |
+ BM_LCDIF_TRANSFER_COUNT_V_COUNT);
+ HW_LCDIF_TRANSFER_COUNT_SET(BF_LCDIF_TRANSFER_COUNT_H_COUNT(h_active) |
+ BF_LCDIF_TRANSFER_COUNT_V_COUNT(v_active));
+
+ /* set lcdif to DVI mode */
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_DVI_MODE);
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_VSYNC_MODE);
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_DOTCLK_MODE);
+
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_BYPASS_COUNT);
+ /* convert input RGB -> YCbCr */
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_RGB_TO_YCBCR422_CSC);
+ /* interlace odd and even fields */
+ HW_LCDIF_CTRL1_SET(BM_LCDIF_CTRL1_INTERLACE_FIELDS);
+
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_WORD_LENGTH |
+ BM_LCDIF_CTRL_INPUT_DATA_SWIZZLE |
+ BM_LCDIF_CTRL_LCD_DATABUS_WIDTH);
+ HW_LCDIF_CTRL_SET(BF_LCDIF_CTRL_WORD_LENGTH(3) | /* 24 bit */
+ BM_LCDIF_CTRL_DATA_SELECT | /* data mode */
+ BF_LCDIF_CTRL_INPUT_DATA_SWIZZLE(0) | /* no swap */
+ BF_LCDIF_CTRL_LCD_DATABUS_WIDTH(1)); /* 8 bit */
+
+ /* LCDIF_DVI */
+ /* set frame size */
+ HW_LCDIF_DVICTRL0_CLR(BM_LCDIF_DVICTRL0_H_ACTIVE_CNT |
+ BM_LCDIF_DVICTRL0_H_BLANKING_CNT |
+ BM_LCDIF_DVICTRL0_V_LINES_CNT);
+ HW_LCDIF_DVICTRL0_SET(BF_LCDIF_DVICTRL0_H_ACTIVE_CNT(1440) |
+ BF_LCDIF_DVICTRL0_H_BLANKING_CNT(h_blanking) |
+ BF_LCDIF_DVICTRL0_V_LINES_CNT(v_lines));
+
+ /* set start/end of field-1 and start of field-2 */
+ HW_LCDIF_DVICTRL1_CLR(BM_LCDIF_DVICTRL1_F1_START_LINE |
+ BM_LCDIF_DVICTRL1_F1_END_LINE |
+ BM_LCDIF_DVICTRL1_F2_START_LINE);
+ HW_LCDIF_DVICTRL1_SET(BF_LCDIF_DVICTRL1_F1_START_LINE(f1_start) |
+ BF_LCDIF_DVICTRL1_F1_END_LINE(f1_end) |
+ BF_LCDIF_DVICTRL1_F2_START_LINE(f2_start));
+
+ /* set first vertical blanking interval and end of filed-2 */
+ HW_LCDIF_DVICTRL2_CLR(BM_LCDIF_DVICTRL2_F2_END_LINE |
+ BM_LCDIF_DVICTRL2_V1_BLANK_START_LINE |
+ BM_LCDIF_DVICTRL2_V1_BLANK_END_LINE);
+ HW_LCDIF_DVICTRL2_SET(BF_LCDIF_DVICTRL2_F2_END_LINE(f2_end) |
+ BF_LCDIF_DVICTRL2_V1_BLANK_START_LINE(
+ v1_blank_start) |
+ BF_LCDIF_DVICTRL2_V1_BLANK_END_LINE(
+ v1_blank_end));
+
+ /* set second vertical blanking interval */
+ HW_LCDIF_DVICTRL3_CLR(BM_LCDIF_DVICTRL3_V2_BLANK_START_LINE |
+ BM_LCDIF_DVICTRL3_V2_BLANK_END_LINE);
+ HW_LCDIF_DVICTRL3_SET(BF_LCDIF_DVICTRL3_V2_BLANK_START_LINE(
+ v2_blank_start) |
+ BF_LCDIF_DVICTRL3_V2_BLANK_END_LINE(
+ v2_blank_end));
+
+ /* fill the rest area black color if the input frame
+ * is not 720 pixels/line
+ */
+ if (h_active != 720) {
+ /* the input frame can't be less then (720-256) pixels/line */
+ if (720 - h_active > 0xff)
+ h_active = 720 - 0xff;
+
+ HW_LCDIF_DVICTRL4_CLR(BM_LCDIF_DVICTRL4_H_FILL_CNT |
+ BM_LCDIF_DVICTRL4_Y_FILL_VALUE |
+ BM_LCDIF_DVICTRL4_CB_FILL_VALUE |
+ BM_LCDIF_DVICTRL4_CR_FILL_VALUE);
+ HW_LCDIF_DVICTRL4_SET(BF_LCDIF_DVICTRL4_H_FILL_CNT(
+ 720 - h_active) |
+ BF_LCDIF_DVICTRL4_Y_FILL_VALUE(16) |
+ BF_LCDIF_DVICTRL4_CB_FILL_VALUE(128) |
+ BF_LCDIF_DVICTRL4_CR_FILL_VALUE(128));
+ }
+
+ /* Color Space Conversion RGB->YCbCr */
+ HW_LCDIF_CSC_COEFF0_CLR(BM_LCDIF_CSC_COEFF0_C0 |
+ BM_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER);
+ HW_LCDIF_CSC_COEFF0_SET(BF_LCDIF_CSC_COEFF0_C0(0x41) |
+ BF_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER(3));
+
+ HW_LCDIF_CSC_COEFF1_CLR(BM_LCDIF_CSC_COEFF1_C1 |
+ BM_LCDIF_CSC_COEFF1_C2);
+ HW_LCDIF_CSC_COEFF1_SET(BF_LCDIF_CSC_COEFF1_C1(0x81) |
+ BF_LCDIF_CSC_COEFF1_C2(0x19));
+
+ HW_LCDIF_CSC_COEFF2_CLR(BM_LCDIF_CSC_COEFF2_C3 |
+ BM_LCDIF_CSC_COEFF2_C4);
+ HW_LCDIF_CSC_COEFF2_SET(BF_LCDIF_CSC_COEFF2_C3(0x3DB) |
+ BF_LCDIF_CSC_COEFF2_C4(0x3B6));
+
+ HW_LCDIF_CSC_COEFF3_CLR(BM_LCDIF_CSC_COEFF3_C5 |
+ BM_LCDIF_CSC_COEFF3_C6);
+ HW_LCDIF_CSC_COEFF3_SET(BF_LCDIF_CSC_COEFF3_C5(0x70) |
+ BF_LCDIF_CSC_COEFF3_C6(0x70));
+
+ HW_LCDIF_CSC_COEFF4_CLR(BM_LCDIF_CSC_COEFF4_C7 |
+ BM_LCDIF_CSC_COEFF4_C8);
+ HW_LCDIF_CSC_COEFF4_SET(BF_LCDIF_CSC_COEFF4_C7(0x3A2) |
+ BF_LCDIF_CSC_COEFF4_C8(0x3EE));
+
+ HW_LCDIF_CSC_OFFSET_CLR(BM_LCDIF_CSC_OFFSET_CBCR_OFFSET |
+ BM_LCDIF_CSC_OFFSET_Y_OFFSET);
+ HW_LCDIF_CSC_OFFSET_SET(BF_LCDIF_CSC_OFFSET_CBCR_OFFSET(0x80) |
+ BF_LCDIF_CSC_OFFSET_Y_OFFSET(0x10));
+
+ HW_LCDIF_CSC_LIMIT_CLR(BM_LCDIF_CSC_LIMIT_CBCR_MIN |
+ BM_LCDIF_CSC_LIMIT_CBCR_MAX |
+ BM_LCDIF_CSC_LIMIT_Y_MIN |
+ BM_LCDIF_CSC_LIMIT_Y_MAX);
+ HW_LCDIF_CSC_LIMIT_SET(BF_LCDIF_CSC_LIMIT_CBCR_MIN(16) |
+ BF_LCDIF_CSC_LIMIT_CBCR_MAX(240) |
+ BF_LCDIF_CSC_LIMIT_Y_MIN(16) |
+ BF_LCDIF_CSC_LIMIT_Y_MAX(235));
+}
+
+static inline void release_dvi_panel(void)
+{
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_DVI_MODE);
+}
+
+#endif /* _ARCH_ARM_LCDIF_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/lradc.h b/arch/arm/mach-stmp3xxx/include/mach/lradc.h
new file mode 100644
index 000000000000..56b686896f29
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/lradc.h
@@ -0,0 +1,60 @@
+/*
+ * Freescale STMP37XX/STMP378X LRADC helper interface
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_PLAT_LRADC_H
+#define __ASM_PLAT_LRADC_H
+
+int hw_lradc_use_channel(int);
+int hw_lradc_unuse_channel(int);
+extern u32 hw_lradc_vddio(void);
+void hw_lradc_set_delay_trigger_kick(int trigger, int value);
+void hw_lradc_configure_channel(int channel, int enable_div2,
+ int enable_acc, int samples);
+int hw_lradc_present(int channel);
+int hw_lradc_init_ladder(int channel, int trigger, unsigned sampling);
+int hw_lradc_stop_ladder(int channel, int trigger);
+void hw_lradc_set_delay_trigger(int trigger, u32 trigger_lradc,
+ u32 delay_triggers, u32 loops, u32 delays);
+void hw_lradc_clear_delay_trigger(int trigger, u32 trigger_lradc,
+ u32 delay_triggers);
+
+
+#define LRADC_CH0 0
+#define LRADC_CH1 1
+#define LRADC_CH2 2
+#define LRADC_CH3 3
+#define LRADC_CH4 4
+#define LRADC_CH5 5
+#define LRADC_CH6 6
+#define LRADC_CH7 7
+#define LRADC_TOUCH_X_PLUS LRADC_CH2
+#define LRADC_TOUCH_Y_PLUS LRADC_CH3
+#define LRADC_TOUCH_X_MINUS LRADC_CH4
+#define LRADC_TOUCH_Y_MINUS LRADC_CH5
+#define VDDIO_VOLTAGE_CH LRADC_CH6
+#define BATTERY_VOLTAGE_CH LRADC_CH7
+
+#define LRADC_CLOCK_6MHZ 0
+#define LRADC_CLOCK_4MHZ 1
+#define LRADC_CLOCK_3MHZ 2
+#define LRADC_CLOCK_2MHZ 3
+
+#define LRADC_DELAY_TRIGGER_BUTTON 0
+#define LRADC_DELAY_TRIGGER_BATTERY 1
+#define LRADC_DELAY_TRIGGER_TOUCHSCREEN 2
+
+#endif /* __ASM_PLAT_LRADC_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/memory.h b/arch/arm/mach-stmp3xxx/include/mach/memory.h
new file mode 100644
index 000000000000..591130440844
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/memory.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+#include <asm/page.h>
+#include <asm/sizes.h>
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET UL(0x40000000)
+#define BUS_OFFSET UL(0x40000000)
+
+#ifndef __ASSEMBLY__
+
+#ifdef CONFIG_DMA_ZONE_SIZE
+#define MXC_DMA_ZONE_SIZE ((CONFIG_DMA_ZONE_SIZE * SZ_1M) >> PAGE_SHIFT)
+#else
+#define MXC_DMA_ZONE_SIZE ((12 * SZ_1M) >> PAGE_SHIFT)
+#endif
+
+static inline void __arch_adjust_zones(int node, unsigned long *zone_size,
+ unsigned long *zhole_size)
+{
+ if (node != 0)
+ return;
+ /* Create separate zone to reserve memory for DMA */
+ zone_size[1] = zone_size[0] - MXC_DMA_ZONE_SIZE;
+ zone_size[0] = MXC_DMA_ZONE_SIZE;
+ zhole_size[1] = zhole_size[0];
+ zhole_size[0] = 0;
+}
+
+#define arch_adjust_zones(node, size, holes) \
+ __arch_adjust_zones(node, size, holes)
+
+#endif
+
+/*
+ * Virtual view <-> DMA view memory address translations
+ * virt_to_bus: Used to translate the virtual address to an
+ * address suitable to be passed to set_dma_addr
+ * bus_to_virt: Used to convert an address for DMA operations
+ * to an address that the kernel can use.
+ */
+#define __virt_to_bus(x) __virt_to_phys(x)
+#define __bus_to_virt(x) __phys_to_virt(x)
+
+#define ISA_DMA_THRESHOLD (0x0003ffffULL)
+
+#define CONSISTENT_DMA_SIZE SZ_32M
+
+#endif
diff --git a/arch/arm/mach-stmp3xxx/include/mach/ocram-malloc.h b/arch/arm/mach-stmp3xxx/include/mach/ocram-malloc.h
new file mode 100644
index 000000000000..c8bee5352972
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/ocram-malloc.h
@@ -0,0 +1,26 @@
+/*
+ * Freescale STMP37XX/STMP378X OCRAM allocator interface
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_PLAT_OCRAM_MALLOC_H
+#define __ASM_PLAT_OCRAM_MALLOC_H
+
+extern int ocram_malloc_init(void);
+
+extern void *ocram_malloc(size_t size, dma_addr_t *phys);
+extern void ocram_free(void *tofree);
+
+#endif /* __ASM_PLAT_OCRAM_MALLOC_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/pins.h b/arch/arm/mach-stmp3xxx/include/mach/pins.h
new file mode 100644
index 000000000000..e651a82f2cc1
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/pins.h
@@ -0,0 +1,177 @@
+/*
+ * Freescale STMP378X SoC pin multiplexing
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_PINS_H
+#define __ASM_ARCH_PINS_H
+
+/*
+ * The number of pin banks and pins per a bank on STMP378x
+ */
+#define STMP3XXX_PINMUX_NR_BANKS 4
+#define STMP3XXX_PINMUX_BANK_SIZE 32
+
+/*
+ * Macro to convert a pin bank/number pair to a raw pin number
+ * STMP3XXX_PINMUX_BANK_SIZE and STMP3XXX_PINMUX_NR_BANKS should be
+ * defined before including this header.
+ */
+#define STMP3XXX_PINID(bank, pin) (bank * STMP3XXX_PINMUX_BANK_SIZE + pin)
+#define STMP3XXX_PINID_TO_BANK(pinid) (pinid / STMP3XXX_PINMUX_BANK_SIZE)
+#define STMP3XXX_PINID_TO_PINNUM(pinid) (pinid % STMP3XXX_PINMUX_BANK_SIZE)
+
+/*
+ * Special invalid pin identificator to show a pin doesn't exist
+ */
+#define PINID_NO_PIN STMP3XXX_PINID(STMP3XXX_PINMUX_NR_BANKS, 0)
+
+static inline int stmp3xxx_valid_pin(unsigned pin)
+{
+ return STMP3XXX_PINID_TO_BANK(pin) < STMP3XXX_PINMUX_NR_BANKS &&
+ STMP3XXX_PINID_TO_PINNUM(pin) < STMP3XXX_PINMUX_BANK_SIZE;
+}
+
+/*
+ * Define all STMP378x pins, a pin name corresponds to a STMP378x hardware
+ * interface this pin belongs to.
+ */
+
+/* Bank 0 */
+#define PINID_GPMI_D00 STMP3XXX_PINID(0, 0)
+#define PINID_GPMI_D01 STMP3XXX_PINID(0, 1)
+#define PINID_GPMI_D02 STMP3XXX_PINID(0, 2)
+#define PINID_GPMI_D03 STMP3XXX_PINID(0, 3)
+#define PINID_GPMI_D04 STMP3XXX_PINID(0, 4)
+#define PINID_GPMI_D05 STMP3XXX_PINID(0, 5)
+#define PINID_GPMI_D06 STMP3XXX_PINID(0, 6)
+#define PINID_GPMI_D07 STMP3XXX_PINID(0, 7)
+#define PINID_GPMI_D08 STMP3XXX_PINID(0, 8)
+#define PINID_GPMI_D09 STMP3XXX_PINID(0, 9)
+#define PINID_GPMI_D10 STMP3XXX_PINID(0, 10)
+#define PINID_GPMI_D11 STMP3XXX_PINID(0, 11)
+#define PINID_GPMI_D12 STMP3XXX_PINID(0, 12)
+#define PINID_GPMI_D13 STMP3XXX_PINID(0, 13)
+#define PINID_GPMI_D14 STMP3XXX_PINID(0, 14)
+#define PINID_GPMI_D15 STMP3XXX_PINID(0, 15)
+#define PINID_GPMI_CLE STMP3XXX_PINID(0, 16)
+#define PINID_GPMI_ALE STMP3XXX_PINID(0, 17)
+#define PINID_GMPI_CE2N STMP3XXX_PINID(0, 18)
+#define PINID_GPMI_RDY0 STMP3XXX_PINID(0, 19)
+#define PINID_GPMI_RDY1 STMP3XXX_PINID(0, 20)
+#define PINID_GPMI_RDY2 STMP3XXX_PINID(0, 21)
+#define PINID_GPMI_RDY3 STMP3XXX_PINID(0, 22)
+#define PINID_GPMI_WPN STMP3XXX_PINID(0, 23)
+#define PINID_GPMI_WRN STMP3XXX_PINID(0, 24)
+#define PINID_GPMI_RDN STMP3XXX_PINID(0, 25)
+#define PINID_AUART1_CTS STMP3XXX_PINID(0, 26)
+#define PINID_AUART1_RTS STMP3XXX_PINID(0, 27)
+#define PINID_AUART1_RX STMP3XXX_PINID(0, 28)
+#define PINID_AUART1_TX STMP3XXX_PINID(0, 29)
+#define PINID_I2C_SCL STMP3XXX_PINID(0, 30)
+#define PINID_I2C_SDA STMP3XXX_PINID(0, 31)
+
+/* Bank 1 */
+#define PINID_LCD_D00 STMP3XXX_PINID(1, 0)
+#define PINID_LCD_D01 STMP3XXX_PINID(1, 1)
+#define PINID_LCD_D02 STMP3XXX_PINID(1, 2)
+#define PINID_LCD_D03 STMP3XXX_PINID(1, 3)
+#define PINID_LCD_D04 STMP3XXX_PINID(1, 4)
+#define PINID_LCD_D05 STMP3XXX_PINID(1, 5)
+#define PINID_LCD_D06 STMP3XXX_PINID(1, 6)
+#define PINID_LCD_D07 STMP3XXX_PINID(1, 7)
+#define PINID_LCD_D08 STMP3XXX_PINID(1, 8)
+#define PINID_LCD_D09 STMP3XXX_PINID(1, 9)
+#define PINID_LCD_D10 STMP3XXX_PINID(1, 10)
+#define PINID_LCD_D11 STMP3XXX_PINID(1, 11)
+#define PINID_LCD_D12 STMP3XXX_PINID(1, 12)
+#define PINID_LCD_D13 STMP3XXX_PINID(1, 13)
+#define PINID_LCD_D14 STMP3XXX_PINID(1, 14)
+#define PINID_LCD_D15 STMP3XXX_PINID(1, 15)
+#define PINID_LCD_D16 STMP3XXX_PINID(1, 16)
+#define PINID_LCD_D17 STMP3XXX_PINID(1, 17)
+#define PINID_LCD_RESET STMP3XXX_PINID(1, 18)
+#define PINID_LCD_RS STMP3XXX_PINID(1, 19)
+#define PINID_LCD_WR STMP3XXX_PINID(1, 20)
+#define PINID_LCD_CS STMP3XXX_PINID(1, 21)
+#define PINID_LCD_DOTCK STMP3XXX_PINID(1, 22)
+#define PINID_LCD_ENABLE STMP3XXX_PINID(1, 23)
+#define PINID_LCD_HSYNC STMP3XXX_PINID(1, 24)
+#define PINID_LCD_VSYNC STMP3XXX_PINID(1, 25)
+#define PINID_PWM0 STMP3XXX_PINID(1, 26)
+#define PINID_PWM1 STMP3XXX_PINID(1, 27)
+#define PINID_PWM2 STMP3XXX_PINID(1, 28)
+#define PINID_PWM3 STMP3XXX_PINID(1, 29)
+#define PINID_PWM4 STMP3XXX_PINID(1, 30)
+
+/* Bank 2 */
+#define PINID_SSP1_CMD STMP3XXX_PINID(2, 0)
+#define PINID_SSP1_DETECT STMP3XXX_PINID(2, 1)
+#define PINID_SSP1_DATA0 STMP3XXX_PINID(2, 2)
+#define PINID_SSP1_DATA1 STMP3XXX_PINID(2, 3)
+#define PINID_SSP1_DATA2 STMP3XXX_PINID(2, 4)
+#define PINID_SSP1_DATA3 STMP3XXX_PINID(2, 5)
+#define PINID_SSP1_SCK STMP3XXX_PINID(2, 6)
+#define PINID_ROTARYA STMP3XXX_PINID(2, 7)
+#define PINID_ROTARYB STMP3XXX_PINID(2, 8)
+#define PINID_EMI_A00 STMP3XXX_PINID(2, 9)
+#define PINID_EMI_A01 STMP3XXX_PINID(2, 10)
+#define PINID_EMI_A02 STMP3XXX_PINID(2, 11)
+#define PINID_EMI_A03 STMP3XXX_PINID(2, 12)
+#define PINID_EMI_A04 STMP3XXX_PINID(2, 13)
+#define PINID_EMI_A05 STMP3XXX_PINID(2, 14)
+#define PINID_EMI_A06 STMP3XXX_PINID(2, 15)
+#define PINID_EMI_A07 STMP3XXX_PINID(2, 16)
+#define PINID_EMI_A08 STMP3XXX_PINID(2, 17)
+#define PINID_EMI_A09 STMP3XXX_PINID(2, 18)
+#define PINID_EMI_A10 STMP3XXX_PINID(2, 19)
+#define PINID_EMI_A11 STMP3XXX_PINID(2, 20)
+#define PINID_EMI_A12 STMP3XXX_PINID(2, 21)
+#define PINID_EMI_BA0 STMP3XXX_PINID(2, 22)
+#define PINID_EMI_BA1 STMP3XXX_PINID(2, 23)
+#define PINID_EMI_CASN STMP3XXX_PINID(2, 24)
+#define PINID_EMI_CE0N STMP3XXX_PINID(2, 25)
+#define PINID_EMI_CE1N STMP3XXX_PINID(2, 26)
+#define PINID_GPMI_CE1N STMP3XXX_PINID(2, 27)
+#define PINID_GPMI_CE0N STMP3XXX_PINID(2, 28)
+#define PINID_EMI_CKE STMP3XXX_PINID(2, 29)
+#define PINID_EMI_RASN STMP3XXX_PINID(2, 30)
+#define PINID_EMI_WEN STMP3XXX_PINID(2, 31)
+
+/* Bank 3 */
+#define PINID_EMI_D00 STMP3XXX_PINID(3, 0)
+#define PINID_EMI_D01 STMP3XXX_PINID(3, 1)
+#define PINID_EMI_D02 STMP3XXX_PINID(3, 2)
+#define PINID_EMI_D03 STMP3XXX_PINID(3, 3)
+#define PINID_EMI_D04 STMP3XXX_PINID(3, 4)
+#define PINID_EMI_D05 STMP3XXX_PINID(3, 5)
+#define PINID_EMI_D06 STMP3XXX_PINID(3, 6)
+#define PINID_EMI_D07 STMP3XXX_PINID(3, 7)
+#define PINID_EMI_D08 STMP3XXX_PINID(3, 8)
+#define PINID_EMI_D09 STMP3XXX_PINID(3, 9)
+#define PINID_EMI_D10 STMP3XXX_PINID(3, 10)
+#define PINID_EMI_D11 STMP3XXX_PINID(3, 11)
+#define PINID_EMI_D12 STMP3XXX_PINID(3, 12)
+#define PINID_EMI_D13 STMP3XXX_PINID(3, 13)
+#define PINID_EMI_D14 STMP3XXX_PINID(3, 14)
+#define PINID_EMI_D15 STMP3XXX_PINID(3, 15)
+#define PINID_EMI_DQM0 STMP3XXX_PINID(3, 16)
+#define PINID_EMI_DQM1 STMP3XXX_PINID(3, 17)
+#define PINID_EMI_DQS0 STMP3XXX_PINID(3, 18)
+#define PINID_EMI_DQS1 STMP3XXX_PINID(3, 19)
+#define PINID_EMI_CLK STMP3XXX_PINID(3, 20)
+#define PINID_EMI_CLKN STMP3XXX_PINID(3, 21)
+
+#endif /* __ASM_ARCH_PINS_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/platform.h b/arch/arm/mach-stmp3xxx/include/mach/platform.h
new file mode 100644
index 000000000000..55ab0c323b94
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/platform.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_PLAT_PLATFORM_H
+#define __ASM_PLAT_PLATFORM_H
+
+#include <asm/sizes.h>
+
+#define STMP378X_OCRAM_BASE 0x00000000
+#define STMP378X_OCRAM_SIZE (32 * SZ_1K)
+
+#define STMP378X_REGS_BASE 0x80000000
+#define STMP378X_REGS_SIZE SZ_1M
+
+/* Virtual address where registers are mapped */
+#define STMP3XXX_REGS_VA_BASE 0xf0000000
+
+/* Virtual address where OCRAM is mapped */
+#define STMP3XXX_OCRAM_VA_BASE 0xf1000000
+
+#endif /* __ASM_ARCH_PLATFORM_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/power.h b/arch/arm/mach-stmp3xxx/include/mach/power.h
new file mode 100644
index 000000000000..ac90f8621099
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/power.h
@@ -0,0 +1,67 @@
+/*
+ * Freescale STMP37XX/STMP378X voltage regulator structure declarations
+ *
+ * Embedded Alley Solutions, Inc <sources@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __VOLTAGE_H
+#define __VOLTAGE_H
+#include <linux/completion.h>
+//#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+
+struct stmp3xxx_regulator {
+ struct regulator_desc regulator;
+ struct stmp3xxx_regulator *parent;
+ struct stmp3xxx_platform_regulator_data *rdata;
+ struct completion done;
+
+ spinlock_t lock;
+ wait_queue_head_t wait_q;
+ struct notifier_block nb;
+
+ int mode;
+ int cur_voltage;
+ int cur_current;
+ int next_current;
+};
+
+
+struct stmp3xxx_platform_regulator_data {
+ char name[80];
+ char *parent_name;
+ int (*reg_register)(struct stmp3xxx_regulator *sreg);
+ int (*set_voltage)(struct stmp3xxx_regulator *sreg, int uv);
+ int (*get_voltage)(struct stmp3xxx_regulator *sreg);
+ int (*set_current)(struct stmp3xxx_regulator *sreg, int uA);
+ int (*get_current)(struct stmp3xxx_regulator *sreg);
+ int (*enable)(struct stmp3xxx_regulator *sreg);
+ int (*disable)(struct stmp3xxx_regulator *sreg);
+ int (*is_enabled)(struct stmp3xxx_regulator *sreg);
+ int (*set_mode)(struct stmp3xxx_regulator *sreg, int mode);
+ int (*get_mode)(struct stmp3xxx_regulator *sreg);
+ int (*get_optimum_mode)(struct stmp3xxx_regulator *sreg,
+ int input_uV, int output_uV, int load_uA);
+ u32 control_reg;
+ int min_voltage;
+ int max_voltage;
+ int max_current;
+ struct regulation_constraints *constraints;
+};
+
+int stmp3xxx_register_regulator(
+ struct stmp3xxx_regulator *reg_data, int reg,
+ struct regulator_init_data *initdata);
+
+#endif /* __VOLTAGE_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/pwm-led.h b/arch/arm/mach-stmp3xxx/include/mach/pwm-led.h
new file mode 100644
index 000000000000..dd9ded8fd682
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/pwm-led.h
@@ -0,0 +1,26 @@
+/*
+ * Freescale STMP37XX/STMP378X PWM LED arch-dependent structure
+ * and functions declarations
+ *
+ * Author: Drew Benedetti <drewb@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_PLAT_PWM_LED_H
+#define __ASM_PLAT_PWM_LED_H
+
+extern int pwm_led_pinmux_request(int, char *);
+extern void pwm_led_pinmux_free(int, char *);
+
+#endif /* __ASM_PLAT_PWM_LED_H */
+
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-apbh.h b/arch/arm/mach-stmp3xxx/include/mach/regs-apbh.h
new file mode 100644
index 000000000000..ba2db1ddae09
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-apbh.h
@@ -0,0 +1,300 @@
+/*
+ * STMP APBH Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___APBH_H
+#define __ARCH_ARM___APBH_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_APBH_BASE (REGS_BASE + 0x4000)
+#define REGS_APBH_BASE_PHYS (0x80004000)
+#define REGS_APBH_SIZE 0x00002000
+HW_REGISTER(HW_APBH_CTRL0, REGS_APBH_BASE, 0x00000000)
+#define HW_APBH_CTRL0_ADDR (REGS_APBH_BASE + 0x00000000)
+#define BM_APBH_CTRL0_SFTRST 0x80000000
+#define BM_APBH_CTRL0_CLKGATE 0x40000000
+#define BM_APBH_CTRL0_AHB_BURST8_EN 0x20000000
+#define BM_APBH_CTRL0_APB_BURST4_EN 0x10000000
+#define BP_APBH_CTRL0_RESET_CHANNEL 16
+#define BM_APBH_CTRL0_RESET_CHANNEL 0x00FF0000
+#define BF_APBH_CTRL0_RESET_CHANNEL(v) \
+ (((v) << 16) & BM_APBH_CTRL0_RESET_CHANNEL)
+#define BV_APBH_CTRL0_RESET_CHANNEL__LCDIF 0x01
+#define BV_APBH_CTRL0_RESET_CHANNEL__SSP1 0x02
+#define BV_APBH_CTRL0_RESET_CHANNEL__SSP2 0x04
+#define BV_APBH_CTRL0_RESET_CHANNEL__ATA 0x10
+#define BV_APBH_CTRL0_RESET_CHANNEL__NAND0 0x10
+#define BV_APBH_CTRL0_RESET_CHANNEL__NAND1 0x20
+#define BV_APBH_CTRL0_RESET_CHANNEL__NAND2 0x40
+#define BV_APBH_CTRL0_RESET_CHANNEL__NAND3 0x80
+#define BP_APBH_CTRL0_CLKGATE_CHANNEL 8
+#define BM_APBH_CTRL0_CLKGATE_CHANNEL 0x0000FF00
+#define BF_APBH_CTRL0_CLKGATE_CHANNEL(v) \
+ (((v) << 8) & BM_APBH_CTRL0_CLKGATE_CHANNEL)
+#define BV_APBH_CTRL0_CLKGATE_CHANNEL__LCDIF 0x01
+#define BV_APBH_CTRL0_CLKGATE_CHANNEL__SSP1 0x02
+#define BV_APBH_CTRL0_CLKGATE_CHANNEL__SSP2 0x04
+#define BV_APBH_CTRL0_CLKGATE_CHANNEL__ATA 0x10
+#define BV_APBH_CTRL0_CLKGATE_CHANNEL__NAND0 0x10
+#define BV_APBH_CTRL0_CLKGATE_CHANNEL__NAND1 0x20
+#define BV_APBH_CTRL0_CLKGATE_CHANNEL__NAND2 0x40
+#define BV_APBH_CTRL0_CLKGATE_CHANNEL__NAND3 0x80
+#define BP_APBH_CTRL0_FREEZE_CHANNEL 0
+#define BM_APBH_CTRL0_FREEZE_CHANNEL 0x000000FF
+#define BF_APBH_CTRL0_FREEZE_CHANNEL(v) \
+ (((v) << 0) & BM_APBH_CTRL0_FREEZE_CHANNEL)
+#define BV_APBH_CTRL0_FREEZE_CHANNEL__LCDIF 0x01
+#define BV_APBH_CTRL0_FREEZE_CHANNEL__SSP1 0x02
+#define BV_APBH_CTRL0_FREEZE_CHANNEL__SSP2 0x04
+#define BV_APBH_CTRL0_FREEZE_CHANNEL__ATA 0x10
+#define BV_APBH_CTRL0_FREEZE_CHANNEL__NAND0 0x10
+#define BV_APBH_CTRL0_FREEZE_CHANNEL__NAND1 0x20
+#define BV_APBH_CTRL0_FREEZE_CHANNEL__NAND2 0x40
+#define BV_APBH_CTRL0_FREEZE_CHANNEL__NAND3 0x80
+HW_REGISTER(HW_APBH_CTRL1, REGS_APBH_BASE, 0x00000010)
+#define HW_APBH_CTRL1_ADDR (REGS_APBH_BASE + 0x00000010)
+#define BM_APBH_CTRL1_CH7_CMDCMPLT_IRQ_EN 0x00800000
+#define BM_APBH_CTRL1_CH6_CMDCMPLT_IRQ_EN 0x00400000
+#define BM_APBH_CTRL1_CH5_CMDCMPLT_IRQ_EN 0x00200000
+#define BM_APBH_CTRL1_CH4_CMDCMPLT_IRQ_EN 0x00100000
+#define BM_APBH_CTRL1_CH3_CMDCMPLT_IRQ_EN 0x00080000
+#define BM_APBH_CTRL1_CH2_CMDCMPLT_IRQ_EN 0x00040000
+#define BM_APBH_CTRL1_CH1_CMDCMPLT_IRQ_EN 0x00020000
+#define BM_APBH_CTRL1_CH0_CMDCMPLT_IRQ_EN 0x00010000
+#define BM_APBH_CTRL1_CH7_CMDCMPLT_IRQ 0x00000080
+#define BM_APBH_CTRL1_CH6_CMDCMPLT_IRQ 0x00000040
+#define BM_APBH_CTRL1_CH5_CMDCMPLT_IRQ 0x00000020
+#define BM_APBH_CTRL1_CH4_CMDCMPLT_IRQ 0x00000010
+#define BM_APBH_CTRL1_CH3_CMDCMPLT_IRQ 0x00000008
+#define BM_APBH_CTRL1_CH2_CMDCMPLT_IRQ 0x00000004
+#define BM_APBH_CTRL1_CH1_CMDCMPLT_IRQ 0x00000002
+#define BM_APBH_CTRL1_CH0_CMDCMPLT_IRQ 0x00000001
+HW_REGISTER(HW_APBH_CTRL2, REGS_APBH_BASE, 0x00000020)
+#define HW_APBH_CTRL2_ADDR (REGS_APBH_BASE + 0x00000020)
+#define BM_APBH_CTRL2_CH7_ERROR_STATUS 0x00800000
+#define BV_APBH_CTRL2_CH7_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBH_CTRL2_CH7_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBH_CTRL2_CH6_ERROR_STATUS 0x00400000
+#define BV_APBH_CTRL2_CH6_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBH_CTRL2_CH6_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBH_CTRL2_CH5_ERROR_STATUS 0x00200000
+#define BV_APBH_CTRL2_CH5_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBH_CTRL2_CH5_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBH_CTRL2_CH4_ERROR_STATUS 0x00100000
+#define BV_APBH_CTRL2_CH4_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBH_CTRL2_CH4_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBH_CTRL2_CH3_ERROR_STATUS 0x00080000
+#define BV_APBH_CTRL2_CH3_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBH_CTRL2_CH3_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBH_CTRL2_CH2_ERROR_STATUS 0x00040000
+#define BV_APBH_CTRL2_CH2_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBH_CTRL2_CH2_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBH_CTRL2_CH1_ERROR_STATUS 0x00020000
+#define BV_APBH_CTRL2_CH1_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBH_CTRL2_CH1_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBH_CTRL2_CH0_ERROR_STATUS 0x00010000
+#define BV_APBH_CTRL2_CH0_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBH_CTRL2_CH0_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBH_CTRL2_CH7_ERROR_IRQ 0x00000080
+#define BM_APBH_CTRL2_CH6_ERROR_IRQ 0x00000040
+#define BM_APBH_CTRL2_CH5_ERROR_IRQ 0x00000020
+#define BM_APBH_CTRL2_CH4_ERROR_IRQ 0x00000010
+#define BM_APBH_CTRL2_CH3_ERROR_IRQ 0x00000008
+#define BM_APBH_CTRL2_CH2_ERROR_IRQ 0x00000004
+#define BM_APBH_CTRL2_CH1_ERROR_IRQ 0x00000002
+#define BM_APBH_CTRL2_CH0_ERROR_IRQ 0x00000001
+HW_REGISTER_0(HW_APBH_DEVSEL, REGS_APBH_BASE, 0x00000030)
+#define HW_APBH_DEVSEL_ADDR (REGS_APBH_BASE + 0x00000030)
+#define BP_APBH_DEVSEL_CH7 28
+#define BM_APBH_DEVSEL_CH7 0xF0000000
+#define BF_APBH_DEVSEL_CH7(v) \
+ (((v) << 28) & BM_APBH_DEVSEL_CH7)
+#define BP_APBH_DEVSEL_CH6 24
+#define BM_APBH_DEVSEL_CH6 0x0F000000
+#define BF_APBH_DEVSEL_CH6(v) \
+ (((v) << 24) & BM_APBH_DEVSEL_CH6)
+#define BP_APBH_DEVSEL_CH5 20
+#define BM_APBH_DEVSEL_CH5 0x00F00000
+#define BF_APBH_DEVSEL_CH5(v) \
+ (((v) << 20) & BM_APBH_DEVSEL_CH5)
+#define BP_APBH_DEVSEL_CH4 16
+#define BM_APBH_DEVSEL_CH4 0x000F0000
+#define BF_APBH_DEVSEL_CH4(v) \
+ (((v) << 16) & BM_APBH_DEVSEL_CH4)
+#define BP_APBH_DEVSEL_CH3 12
+#define BM_APBH_DEVSEL_CH3 0x0000F000
+#define BF_APBH_DEVSEL_CH3(v) \
+ (((v) << 12) & BM_APBH_DEVSEL_CH3)
+#define BP_APBH_DEVSEL_CH2 8
+#define BM_APBH_DEVSEL_CH2 0x00000F00
+#define BF_APBH_DEVSEL_CH2(v) \
+ (((v) << 8) & BM_APBH_DEVSEL_CH2)
+#define BP_APBH_DEVSEL_CH1 4
+#define BM_APBH_DEVSEL_CH1 0x000000F0
+#define BF_APBH_DEVSEL_CH1(v) \
+ (((v) << 4) & BM_APBH_DEVSEL_CH1)
+#define BP_APBH_DEVSEL_CH0 0
+#define BM_APBH_DEVSEL_CH0 0x0000000F
+#define BF_APBH_DEVSEL_CH0(v) \
+ (((v) << 0) & BM_APBH_DEVSEL_CH0)
+/*
+ * multi-register-define name HW_APBH_CHn_CURCMDAR
+ * base 0x00000040
+ * count 8
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBH_CHn_CURCMDAR, REGS_APBH_BASE, 0x00000040, 0x70)
+#define BP_APBH_CHn_CURCMDAR_CMD_ADDR 0
+#define BM_APBH_CHn_CURCMDAR_CMD_ADDR 0xFFFFFFFF
+#define BF_APBH_CHn_CURCMDAR_CMD_ADDR(v) (v)
+/*
+ * multi-register-define name HW_APBH_CHn_NXTCMDAR
+ * base 0x00000050
+ * count 8
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBH_CHn_NXTCMDAR, REGS_APBH_BASE, 0x00000050, 0x70)
+#define BP_APBH_CHn_NXTCMDAR_CMD_ADDR 0
+#define BM_APBH_CHn_NXTCMDAR_CMD_ADDR 0xFFFFFFFF
+#define BF_APBH_CHn_NXTCMDAR_CMD_ADDR(v) (v)
+/*
+ * multi-register-define name HW_APBH_CHn_CMD
+ * base 0x00000060
+ * count 8
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBH_CHn_CMD, REGS_APBH_BASE, 0x00000060, 0x70)
+#define BP_APBH_CHn_CMD_XFER_COUNT 16
+#define BM_APBH_CHn_CMD_XFER_COUNT 0xFFFF0000
+#define BF_APBH_CHn_CMD_XFER_COUNT(v) \
+ (((v) << 16) & BM_APBH_CHn_CMD_XFER_COUNT)
+#define BP_APBH_CHn_CMD_CMDWORDS 12
+#define BM_APBH_CHn_CMD_CMDWORDS 0x0000F000
+#define BF_APBH_CHn_CMD_CMDWORDS(v) \
+ (((v) << 12) & BM_APBH_CHn_CMD_CMDWORDS)
+#define BM_APBH_CHn_CMD_HALTONTERMINATE 0x00000100
+#define BM_APBH_CHn_CMD_WAIT4ENDCMD 0x00000080
+#define BM_APBH_CHn_CMD_SEMAPHORE 0x00000040
+#define BM_APBH_CHn_CMD_NANDWAIT4READY 0x00000020
+#define BM_APBH_CHn_CMD_NANDLOCK 0x00000010
+#define BM_APBH_CHn_CMD_IRQONCMPLT 0x00000008
+#define BM_APBH_CHn_CMD_CHAIN 0x00000004
+#define BP_APBH_CHn_CMD_COMMAND 0
+#define BM_APBH_CHn_CMD_COMMAND 0x00000003
+#define BF_APBH_CHn_CMD_COMMAND(v) \
+ (((v) << 0) & BM_APBH_CHn_CMD_COMMAND)
+#define BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER 0x0
+#define BV_APBH_CHn_CMD_COMMAND__DMA_WRITE 0x1
+#define BV_APBH_CHn_CMD_COMMAND__DMA_READ 0x2
+#define BV_APBH_CHn_CMD_COMMAND__DMA_SENSE 0x3
+/*
+ * multi-register-define name HW_APBH_CHn_BAR
+ * base 0x00000070
+ * count 8
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBH_CHn_BAR, REGS_APBH_BASE, 0x00000070, 0x70)
+#define BP_APBH_CHn_BAR_ADDRESS 0
+#define BM_APBH_CHn_BAR_ADDRESS 0xFFFFFFFF
+#define BF_APBH_CHn_BAR_ADDRESS(v) (v)
+/*
+ * multi-register-define name HW_APBH_CHn_SEMA
+ * base 0x00000080
+ * count 8
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBH_CHn_SEMA, REGS_APBH_BASE, 0x00000080, 0x70)
+#define BP_APBH_CHn_SEMA_PHORE 16
+#define BM_APBH_CHn_SEMA_PHORE 0x00FF0000
+#define BF_APBH_CHn_SEMA_PHORE(v) \
+ (((v) << 16) & BM_APBH_CHn_SEMA_PHORE)
+#define BP_APBH_CHn_SEMA_INCREMENT_SEMA 0
+#define BM_APBH_CHn_SEMA_INCREMENT_SEMA 0x000000FF
+#define BF_APBH_CHn_SEMA_INCREMENT_SEMA(v) \
+ (((v) << 0) & BM_APBH_CHn_SEMA_INCREMENT_SEMA)
+/*
+ * multi-register-define name HW_APBH_CHn_DEBUG1
+ * base 0x00000090
+ * count 8
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBH_CHn_DEBUG1, REGS_APBH_BASE, 0x00000090, 0x70)
+#define BM_APBH_CHn_DEBUG1_REQ 0x80000000
+#define BM_APBH_CHn_DEBUG1_BURST 0x40000000
+#define BM_APBH_CHn_DEBUG1_KICK 0x20000000
+#define BM_APBH_CHn_DEBUG1_END 0x10000000
+#define BM_APBH_CHn_DEBUG1_NEXTCMDADDRVALID 0x01000000
+#define BM_APBH_CHn_DEBUG1_RD_FIFO_EMPTY 0x00800000
+#define BM_APBH_CHn_DEBUG1_RD_FIFO_FULL 0x00400000
+#define BM_APBH_CHn_DEBUG1_WR_FIFO_EMPTY 0x00200000
+#define BM_APBH_CHn_DEBUG1_WR_FIFO_FULL 0x00100000
+#define BP_APBH_CHn_DEBUG1_STATEMACHINE 0
+#define BM_APBH_CHn_DEBUG1_STATEMACHINE 0x0000001F
+#define BF_APBH_CHn_DEBUG1_STATEMACHINE(v) \
+ (((v) << 0) & BM_APBH_CHn_DEBUG1_STATEMACHINE)
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__IDLE 0x00
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__REQ_CMD1 0x01
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__REQ_CMD3 0x02
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__REQ_CMD2 0x03
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__XFER_DECODE 0x04
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__REQ_WAIT 0x05
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__REQ_CMD4 0x06
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__PIO_REQ 0x07
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__READ_FLUSH 0x08
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__READ_WAIT 0x09
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__WRITE 0x0C
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__READ_REQ 0x0D
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__CHECK_CHAIN 0x0E
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__XFER_COMPLETE 0x0F
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__TERMINATE 0x14
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__WAIT_END 0x15
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__WRITE_WAIT 0x1C
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__HALT_AFTER_TERM 0x1D
+#define BV_APBH_CHn_DEBUG1_STATEMACHINE__CHECK_WAIT 0x1E
+/*
+ * multi-register-define name HW_APBH_CHn_DEBUG2
+ * base 0x000000A0
+ * count 8
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBH_CHn_DEBUG2, REGS_APBH_BASE, 0x000000a0, 0x70)
+#define BP_APBH_CHn_DEBUG2_APB_BYTES 16
+#define BM_APBH_CHn_DEBUG2_APB_BYTES 0xFFFF0000
+#define BF_APBH_CHn_DEBUG2_APB_BYTES(v) \
+ (((v) << 16) & BM_APBH_CHn_DEBUG2_APB_BYTES)
+#define BP_APBH_CHn_DEBUG2_AHB_BYTES 0
+#define BM_APBH_CHn_DEBUG2_AHB_BYTES 0x0000FFFF
+#define BF_APBH_CHn_DEBUG2_AHB_BYTES(v) \
+ (((v) << 0) & BM_APBH_CHn_DEBUG2_AHB_BYTES)
+HW_REGISTER_0(HW_APBH_VERSION, REGS_APBH_BASE, 0x000003f0)
+#define HW_APBH_VERSION_ADDR (REGS_APBH_BASE + 0x000003f0)
+#define BP_APBH_VERSION_MAJOR 24
+#define BM_APBH_VERSION_MAJOR 0xFF000000
+#define BF_APBH_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_APBH_VERSION_MAJOR)
+#define BP_APBH_VERSION_MINOR 16
+#define BM_APBH_VERSION_MINOR 0x00FF0000
+#define BF_APBH_VERSION_MINOR(v) \
+ (((v) << 16) & BM_APBH_VERSION_MINOR)
+#define BP_APBH_VERSION_STEP 0
+#define BM_APBH_VERSION_STEP 0x0000FFFF
+#define BF_APBH_VERSION_STEP(v) \
+ (((v) << 0) & BM_APBH_VERSION_STEP)
+#endif /* __ARCH_ARM___APBH_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-apbx.h b/arch/arm/mach-stmp3xxx/include/mach/regs-apbx.h
new file mode 100644
index 000000000000..43f649e950ac
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-apbx.h
@@ -0,0 +1,377 @@
+/*
+ * STMP APBX Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___APBX_H
+#define __ARCH_ARM___APBX_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_APBX_BASE (REGS_BASE + 0x24000)
+#define REGS_APBX_BASE_PHYS (0x80024000)
+#define REGS_APBX_SIZE 0x00002000
+HW_REGISTER(HW_APBX_CTRL0, REGS_APBX_BASE, 0x00000000)
+#define HW_APBX_CTRL0_ADDR (REGS_APBX_BASE + 0x00000000)
+#define BM_APBX_CTRL0_SFTRST 0x80000000
+#define BM_APBX_CTRL0_CLKGATE 0x40000000
+HW_REGISTER(HW_APBX_CTRL1, REGS_APBX_BASE, 0x00000010)
+#define HW_APBX_CTRL1_ADDR (REGS_APBX_BASE + 0x00000010)
+#define BM_APBX_CTRL1_CH15_CMDCMPLT_IRQ_EN 0x80000000
+#define BM_APBX_CTRL1_CH14_CMDCMPLT_IRQ_EN 0x40000000
+#define BM_APBX_CTRL1_CH13_CMDCMPLT_IRQ_EN 0x20000000
+#define BM_APBX_CTRL1_CH12_CMDCMPLT_IRQ_EN 0x10000000
+#define BM_APBX_CTRL1_CH11_CMDCMPLT_IRQ_EN 0x08000000
+#define BM_APBX_CTRL1_CH10_CMDCMPLT_IRQ_EN 0x04000000
+#define BM_APBX_CTRL1_CH9_CMDCMPLT_IRQ_EN 0x02000000
+#define BM_APBX_CTRL1_CH8_CMDCMPLT_IRQ_EN 0x01000000
+#define BM_APBX_CTRL1_CH7_CMDCMPLT_IRQ_EN 0x00800000
+#define BM_APBX_CTRL1_CH6_CMDCMPLT_IRQ_EN 0x00400000
+#define BM_APBX_CTRL1_CH5_CMDCMPLT_IRQ_EN 0x00200000
+#define BM_APBX_CTRL1_CH4_CMDCMPLT_IRQ_EN 0x00100000
+#define BM_APBX_CTRL1_CH3_CMDCMPLT_IRQ_EN 0x00080000
+#define BM_APBX_CTRL1_CH2_CMDCMPLT_IRQ_EN 0x00040000
+#define BM_APBX_CTRL1_CH1_CMDCMPLT_IRQ_EN 0x00020000
+#define BM_APBX_CTRL1_CH0_CMDCMPLT_IRQ_EN 0x00010000
+#define BM_APBX_CTRL1_CH15_CMDCMPLT_IRQ 0x00008000
+#define BM_APBX_CTRL1_CH14_CMDCMPLT_IRQ 0x00004000
+#define BM_APBX_CTRL1_CH13_CMDCMPLT_IRQ 0x00002000
+#define BM_APBX_CTRL1_CH12_CMDCMPLT_IRQ 0x00001000
+#define BM_APBX_CTRL1_CH11_CMDCMPLT_IRQ 0x00000800
+#define BM_APBX_CTRL1_CH10_CMDCMPLT_IRQ 0x00000400
+#define BM_APBX_CTRL1_CH9_CMDCMPLT_IRQ 0x00000200
+#define BM_APBX_CTRL1_CH8_CMDCMPLT_IRQ 0x00000100
+#define BM_APBX_CTRL1_CH7_CMDCMPLT_IRQ 0x00000080
+#define BM_APBX_CTRL1_CH6_CMDCMPLT_IRQ 0x00000040
+#define BM_APBX_CTRL1_CH5_CMDCMPLT_IRQ 0x00000020
+#define BM_APBX_CTRL1_CH4_CMDCMPLT_IRQ 0x00000010
+#define BM_APBX_CTRL1_CH3_CMDCMPLT_IRQ 0x00000008
+#define BM_APBX_CTRL1_CH2_CMDCMPLT_IRQ 0x00000004
+#define BM_APBX_CTRL1_CH1_CMDCMPLT_IRQ 0x00000002
+#define BM_APBX_CTRL1_CH0_CMDCMPLT_IRQ 0x00000001
+HW_REGISTER(HW_APBX_CTRL2, REGS_APBX_BASE, 0x00000020)
+#define HW_APBX_CTRL2_ADDR (REGS_APBX_BASE + 0x00000020)
+#define BM_APBX_CTRL2_CH15_ERROR_STATUS 0x80000000
+#define BV_APBX_CTRL2_CH15_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH15_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH14_ERROR_STATUS 0x40000000
+#define BV_APBX_CTRL2_CH14_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH14_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH13_ERROR_STATUS 0x20000000
+#define BV_APBX_CTRL2_CH13_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH13_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH12_ERROR_STATUS 0x10000000
+#define BV_APBX_CTRL2_CH12_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH12_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH11_ERROR_STATUS 0x08000000
+#define BV_APBX_CTRL2_CH11_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH11_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH10_ERROR_STATUS 0x04000000
+#define BV_APBX_CTRL2_CH10_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH10_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH9_ERROR_STATUS 0x02000000
+#define BV_APBX_CTRL2_CH9_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH9_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH8_ERROR_STATUS 0x01000000
+#define BV_APBX_CTRL2_CH8_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH8_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH7_ERROR_STATUS 0x00800000
+#define BV_APBX_CTRL2_CH7_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH7_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH6_ERROR_STATUS 0x00400000
+#define BV_APBX_CTRL2_CH6_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH6_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH5_ERROR_STATUS 0x00200000
+#define BV_APBX_CTRL2_CH5_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH5_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH4_ERROR_STATUS 0x00100000
+#define BV_APBX_CTRL2_CH4_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH4_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH3_ERROR_STATUS 0x00080000
+#define BV_APBX_CTRL2_CH3_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH3_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH2_ERROR_STATUS 0x00040000
+#define BV_APBX_CTRL2_CH2_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH2_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH1_ERROR_STATUS 0x00020000
+#define BV_APBX_CTRL2_CH1_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH1_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH0_ERROR_STATUS 0x00010000
+#define BV_APBX_CTRL2_CH0_ERROR_STATUS__TERMINATION 0x0
+#define BV_APBX_CTRL2_CH0_ERROR_STATUS__BUS_ERROR 0x1
+#define BM_APBX_CTRL2_CH15_ERROR_IRQ 0x00008000
+#define BM_APBX_CTRL2_CH14_ERROR_IRQ 0x00004000
+#define BM_APBX_CTRL2_CH13_ERROR_IRQ 0x00002000
+#define BM_APBX_CTRL2_CH12_ERROR_IRQ 0x00001000
+#define BM_APBX_CTRL2_CH11_ERROR_IRQ 0x00000800
+#define BM_APBX_CTRL2_CH10_ERROR_IRQ 0x00000400
+#define BM_APBX_CTRL2_CH9_ERROR_IRQ 0x00000200
+#define BM_APBX_CTRL2_CH8_ERROR_IRQ 0x00000100
+#define BM_APBX_CTRL2_CH7_ERROR_IRQ 0x00000080
+#define BM_APBX_CTRL2_CH6_ERROR_IRQ 0x00000040
+#define BM_APBX_CTRL2_CH5_ERROR_IRQ 0x00000020
+#define BM_APBX_CTRL2_CH4_ERROR_IRQ 0x00000010
+#define BM_APBX_CTRL2_CH3_ERROR_IRQ 0x00000008
+#define BM_APBX_CTRL2_CH2_ERROR_IRQ 0x00000004
+#define BM_APBX_CTRL2_CH1_ERROR_IRQ 0x00000002
+#define BM_APBX_CTRL2_CH0_ERROR_IRQ 0x00000001
+HW_REGISTER(HW_APBX_CHANNEL_CTRL, REGS_APBX_BASE, 0x00000030)
+#define HW_APBX_CHANNEL_CTRL_ADDR (REGS_APBX_BASE + 0x00000030)
+#define BP_APBX_CHANNEL_CTRL_RESET_CHANNEL 16
+#define BM_APBX_CHANNEL_CTRL_RESET_CHANNEL 0xFFFF0000
+#define BF_APBX_CHANNEL_CTRL_RESET_CHANNEL(v) \
+ (((v) << 16) & BM_APBX_CHANNEL_CTRL_RESET_CHANNEL)
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__AUDIOIN 0x0001
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__AUDIOOUT 0x0002
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__SPDIF_TX 0x0004
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__I2C 0x0008
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__SAIF1 0x0010
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__DRI 0x0020
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__IRDA_RX 0x0040
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__UART0_RX 0x0040
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__IRDA_TX 0x0080
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__UART0_TX 0x0080
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__UART1_RX 0x0100
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__UART1_TX 0x0200
+#define BV_APBX_CHANNEL_CTRL_RESET_CHANNEL__SAIF2 0x0400
+#define BP_APBX_CHANNEL_CTRL_FREEZE_CHANNEL 0
+#define BM_APBX_CHANNEL_CTRL_FREEZE_CHANNEL 0x0000FFFF
+#define BF_APBX_CHANNEL_CTRL_FREEZE_CHANNEL(v) \
+ (((v) << 0) & BM_APBX_CHANNEL_CTRL_FREEZE_CHANNEL)
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__AUDIOIN 0x0001
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__AUDIOOUT 0x0002
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__SPDIF_TX 0x0004
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__I2C 0x0008
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__SAIF1 0x0010
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__DRI 0x0020
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__IRDA_RX 0x0040
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__UART0_RX 0x0040
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__IRDA_TX 0x0080
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__UART0_TX 0x0080
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__UART1_RX 0x0100
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__UART1_TX 0x0200
+#define BV_APBX_CHANNEL_CTRL_FREEZE_CHANNEL__SAIF2 0x0400
+HW_REGISTER_0(HW_APBX_DEVSEL, REGS_APBX_BASE, 0x00000040)
+#define HW_APBX_DEVSEL_ADDR (REGS_APBX_BASE + 0x00000040)
+#define BP_APBX_DEVSEL_CH15 30
+#define BM_APBX_DEVSEL_CH15 0xC0000000
+#define BF_APBX_DEVSEL_CH15(v) \
+ (((v) << 30) & BM_APBX_DEVSEL_CH15)
+#define BP_APBX_DEVSEL_CH14 28
+#define BM_APBX_DEVSEL_CH14 0x30000000
+#define BF_APBX_DEVSEL_CH14(v) \
+ (((v) << 28) & BM_APBX_DEVSEL_CH14)
+#define BP_APBX_DEVSEL_CH13 26
+#define BM_APBX_DEVSEL_CH13 0x0C000000
+#define BF_APBX_DEVSEL_CH13(v) \
+ (((v) << 26) & BM_APBX_DEVSEL_CH13)
+#define BP_APBX_DEVSEL_CH12 24
+#define BM_APBX_DEVSEL_CH12 0x03000000
+#define BF_APBX_DEVSEL_CH12(v) \
+ (((v) << 24) & BM_APBX_DEVSEL_CH12)
+#define BP_APBX_DEVSEL_CH11 22
+#define BM_APBX_DEVSEL_CH11 0x00C00000
+#define BF_APBX_DEVSEL_CH11(v) \
+ (((v) << 22) & BM_APBX_DEVSEL_CH11)
+#define BP_APBX_DEVSEL_CH10 20
+#define BM_APBX_DEVSEL_CH10 0x00300000
+#define BF_APBX_DEVSEL_CH10(v) \
+ (((v) << 20) & BM_APBX_DEVSEL_CH10)
+#define BP_APBX_DEVSEL_CH9 18
+#define BM_APBX_DEVSEL_CH9 0x000C0000
+#define BF_APBX_DEVSEL_CH9(v) \
+ (((v) << 18) & BM_APBX_DEVSEL_CH9)
+#define BP_APBX_DEVSEL_CH8 16
+#define BM_APBX_DEVSEL_CH8 0x00030000
+#define BF_APBX_DEVSEL_CH8(v) \
+ (((v) << 16) & BM_APBX_DEVSEL_CH8)
+#define BP_APBX_DEVSEL_CH7 14
+#define BM_APBX_DEVSEL_CH7 0x0000C000
+#define BF_APBX_DEVSEL_CH7(v) \
+ (((v) << 14) & BM_APBX_DEVSEL_CH7)
+#define BV_APBX_DEVSEL_CH7__USE_I2C1 0x0
+#define BV_APBX_DEVSEL_CH7__USE_IRDA 0x1
+#define BP_APBX_DEVSEL_CH6 12
+#define BM_APBX_DEVSEL_CH6 0x00003000
+#define BF_APBX_DEVSEL_CH6(v) \
+ (((v) << 12) & BM_APBX_DEVSEL_CH6)
+#define BV_APBX_DEVSEL_CH6__USE_SAIF1 0x0
+#define BV_APBX_DEVSEL_CH6__USE_IRDA 0x1
+#define BP_APBX_DEVSEL_CH5 10
+#define BM_APBX_DEVSEL_CH5 0x00000C00
+#define BF_APBX_DEVSEL_CH5(v) \
+ (((v) << 10) & BM_APBX_DEVSEL_CH5)
+#define BP_APBX_DEVSEL_CH4 8
+#define BM_APBX_DEVSEL_CH4 0x00000300
+#define BF_APBX_DEVSEL_CH4(v) \
+ (((v) << 8) & BM_APBX_DEVSEL_CH4)
+#define BP_APBX_DEVSEL_CH3 6
+#define BM_APBX_DEVSEL_CH3 0x000000C0
+#define BF_APBX_DEVSEL_CH3(v) \
+ (((v) << 6) & BM_APBX_DEVSEL_CH3)
+#define BP_APBX_DEVSEL_CH2 4
+#define BM_APBX_DEVSEL_CH2 0x00000030
+#define BF_APBX_DEVSEL_CH2(v) \
+ (((v) << 4) & BM_APBX_DEVSEL_CH2)
+#define BP_APBX_DEVSEL_CH1 2
+#define BM_APBX_DEVSEL_CH1 0x0000000C
+#define BF_APBX_DEVSEL_CH1(v) \
+ (((v) << 2) & BM_APBX_DEVSEL_CH1)
+#define BP_APBX_DEVSEL_CH0 0
+#define BM_APBX_DEVSEL_CH0 0x00000003
+#define BF_APBX_DEVSEL_CH0(v) \
+ (((v) << 0) & BM_APBX_DEVSEL_CH0)
+/*
+ * multi-register-define name HW_APBX_CHn_CURCMDAR
+ * base 0x00000100
+ * count 16
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBX_CHn_CURCMDAR, REGS_APBX_BASE, 0x00000100, 0x70)
+#define BP_APBX_CHn_CURCMDAR_CMD_ADDR 0
+#define BM_APBX_CHn_CURCMDAR_CMD_ADDR 0xFFFFFFFF
+#define BF_APBX_CHn_CURCMDAR_CMD_ADDR(v) (v)
+/*
+ * multi-register-define name HW_APBX_CHn_NXTCMDAR
+ * base 0x00000110
+ * count 16
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBX_CHn_NXTCMDAR, REGS_APBX_BASE, 0x00000110, 0x70)
+#define BP_APBX_CHn_NXTCMDAR_CMD_ADDR 0
+#define BM_APBX_CHn_NXTCMDAR_CMD_ADDR 0xFFFFFFFF
+#define BF_APBX_CHn_NXTCMDAR_CMD_ADDR(v) (v)
+/*
+ * multi-register-define name HW_APBX_CHn_CMD
+ * base 0x00000120
+ * count 16
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBX_CHn_CMD, REGS_APBX_BASE, 0x00000120, 0x70)
+#define BP_APBX_CHn_CMD_XFER_COUNT 16
+#define BM_APBX_CHn_CMD_XFER_COUNT 0xFFFF0000
+#define BF_APBX_CHn_CMD_XFER_COUNT(v) \
+ (((v) << 16) & BM_APBX_CHn_CMD_XFER_COUNT)
+#define BP_APBX_CHn_CMD_CMDWORDS 12
+#define BM_APBX_CHn_CMD_CMDWORDS 0x0000F000
+#define BF_APBX_CHn_CMD_CMDWORDS(v) \
+ (((v) << 12) & BM_APBX_CHn_CMD_CMDWORDS)
+#define BM_APBX_CHn_CMD_HALTONTERMINATE 0x00000100
+#define BM_APBX_CHn_CMD_WAIT4ENDCMD 0x00000080
+#define BM_APBX_CHn_CMD_SEMAPHORE 0x00000040
+#define BM_APBX_CHn_CMD_IRQONCMPLT 0x00000008
+#define BM_APBX_CHn_CMD_CHAIN 0x00000004
+#define BP_APBX_CHn_CMD_COMMAND 0
+#define BM_APBX_CHn_CMD_COMMAND 0x00000003
+#define BF_APBX_CHn_CMD_COMMAND(v) \
+ (((v) << 0) & BM_APBX_CHn_CMD_COMMAND)
+#define BV_APBX_CHn_CMD_COMMAND__NO_DMA_XFER 0x0
+#define BV_APBX_CHn_CMD_COMMAND__DMA_WRITE 0x1
+#define BV_APBX_CHn_CMD_COMMAND__DMA_READ 0x2
+/*
+ * multi-register-define name HW_APBX_CHn_BAR
+ * base 0x00000130
+ * count 16
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBX_CHn_BAR, REGS_APBX_BASE, 0x00000130, 0x70)
+#define BP_APBX_CHn_BAR_ADDRESS 0
+#define BM_APBX_CHn_BAR_ADDRESS 0xFFFFFFFF
+#define BF_APBX_CHn_BAR_ADDRESS(v) (v)
+/*
+ * multi-register-define name HW_APBX_CHn_SEMA
+ * base 0x00000140
+ * count 16
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBX_CHn_SEMA, REGS_APBX_BASE, 0x00000140, 0x70)
+#define BP_APBX_CHn_SEMA_PHORE 16
+#define BM_APBX_CHn_SEMA_PHORE 0x00FF0000
+#define BF_APBX_CHn_SEMA_PHORE(v) \
+ (((v) << 16) & BM_APBX_CHn_SEMA_PHORE)
+#define BP_APBX_CHn_SEMA_INCREMENT_SEMA 0
+#define BM_APBX_CHn_SEMA_INCREMENT_SEMA 0x000000FF
+#define BF_APBX_CHn_SEMA_INCREMENT_SEMA(v) \
+ (((v) << 0) & BM_APBX_CHn_SEMA_INCREMENT_SEMA)
+/*
+ * multi-register-define name HW_APBX_CHn_DEBUG1
+ * base 0x00000150
+ * count 16
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBX_CHn_DEBUG1, REGS_APBX_BASE, 0x00000150, 0x70)
+#define BM_APBX_CHn_DEBUG1_REQ 0x80000000
+#define BM_APBX_CHn_DEBUG1_BURST 0x40000000
+#define BM_APBX_CHn_DEBUG1_KICK 0x20000000
+#define BM_APBX_CHn_DEBUG1_END 0x10000000
+#define BM_APBX_CHn_DEBUG1_NEXTCMDADDRVALID 0x01000000
+#define BM_APBX_CHn_DEBUG1_RD_FIFO_EMPTY 0x00800000
+#define BM_APBX_CHn_DEBUG1_RD_FIFO_FULL 0x00400000
+#define BM_APBX_CHn_DEBUG1_WR_FIFO_EMPTY 0x00200000
+#define BM_APBX_CHn_DEBUG1_WR_FIFO_FULL 0x00100000
+#define BP_APBX_CHn_DEBUG1_STATEMACHINE 0
+#define BM_APBX_CHn_DEBUG1_STATEMACHINE 0x0000001F
+#define BF_APBX_CHn_DEBUG1_STATEMACHINE(v) \
+ (((v) << 0) & BM_APBX_CHn_DEBUG1_STATEMACHINE)
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__IDLE 0x00
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__REQ_CMD1 0x01
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__REQ_CMD3 0x02
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__REQ_CMD2 0x03
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__XFER_DECODE 0x04
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__REQ_WAIT 0x05
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__REQ_CMD4 0x06
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__PIO_REQ 0x07
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__READ_FLUSH 0x08
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__READ_WAIT 0x09
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__WRITE 0x0C
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__READ_REQ 0x0D
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__CHECK_CHAIN 0x0E
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__XFER_COMPLETE 0x0F
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__WAIT_END 0x15
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__WRITE_WAIT 0x1C
+#define BV_APBX_CHn_DEBUG1_STATEMACHINE__CHECK_WAIT 0x1E
+/*
+ * multi-register-define name HW_APBX_CHn_DEBUG2
+ * base 0x00000160
+ * count 16
+ * offset 0x70
+ */
+HW_REGISTER_0_INDEXED(HW_APBX_CHn_DEBUG2, REGS_APBX_BASE, 0x00000160, 0x70)
+#define BP_APBX_CHn_DEBUG2_APB_BYTES 16
+#define BM_APBX_CHn_DEBUG2_APB_BYTES 0xFFFF0000
+#define BF_APBX_CHn_DEBUG2_APB_BYTES(v) \
+ (((v) << 16) & BM_APBX_CHn_DEBUG2_APB_BYTES)
+#define BP_APBX_CHn_DEBUG2_AHB_BYTES 0
+#define BM_APBX_CHn_DEBUG2_AHB_BYTES 0x0000FFFF
+#define BF_APBX_CHn_DEBUG2_AHB_BYTES(v) \
+ (((v) << 0) & BM_APBX_CHn_DEBUG2_AHB_BYTES)
+HW_REGISTER_0(HW_APBX_VERSION, REGS_APBX_BASE, 0x00000800)
+#define HW_APBX_VERSION_ADDR (REGS_APBX_BASE + 0x00000800)
+#define BP_APBX_VERSION_MAJOR 24
+#define BM_APBX_VERSION_MAJOR 0xFF000000
+#define BF_APBX_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_APBX_VERSION_MAJOR)
+#define BP_APBX_VERSION_MINOR 16
+#define BM_APBX_VERSION_MINOR 0x00FF0000
+#define BF_APBX_VERSION_MINOR(v) \
+ (((v) << 16) & BM_APBX_VERSION_MINOR)
+#define BP_APBX_VERSION_STEP 0
+#define BM_APBX_VERSION_STEP 0x0000FFFF
+#define BF_APBX_VERSION_STEP(v) \
+ (((v) << 0) & BM_APBX_VERSION_STEP)
+#endif /* __ARCH_ARM___APBX_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-audioin.h b/arch/arm/mach-stmp3xxx/include/mach/regs-audioin.h
new file mode 100644
index 000000000000..ebcca024d3fb
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-audioin.h
@@ -0,0 +1,161 @@
+/*
+ * STMP AUDIOIN Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___AUDIOIN_H
+#define __ARCH_ARM___AUDIOIN_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_AUDIOIN_BASE (REGS_BASE + 0x4c000)
+#define REGS_AUDIOIN_BASE_PHYS (0x8004C000)
+#define REGS_AUDIOIN_SIZE 0x00002000
+HW_REGISTER(HW_AUDIOIN_CTRL, REGS_AUDIOIN_BASE, 0x00000000)
+#define HW_AUDIOIN_CTRL_ADDR (REGS_AUDIOIN_BASE + 0x00000000)
+#define BM_AUDIOIN_CTRL_SFTRST 0x80000000
+#define BM_AUDIOIN_CTRL_CLKGATE 0x40000000
+#define BP_AUDIOIN_CTRL_DMAWAIT_COUNT 16
+#define BM_AUDIOIN_CTRL_DMAWAIT_COUNT 0x001F0000
+#define BF_AUDIOIN_CTRL_DMAWAIT_COUNT(v) \
+ (((v) << 16) & BM_AUDIOIN_CTRL_DMAWAIT_COUNT)
+#define BM_AUDIOIN_CTRL_LR_SWAP 0x00000400
+#define BM_AUDIOIN_CTRL_EDGE_SYNC 0x00000200
+#define BM_AUDIOIN_CTRL_INVERT_1BIT 0x00000100
+#define BM_AUDIOIN_CTRL_OFFSET_ENABLE 0x00000080
+#define BM_AUDIOIN_CTRL_HPF_ENABLE 0x00000040
+#define BM_AUDIOIN_CTRL_WORD_LENGTH 0x00000020
+#define BM_AUDIOIN_CTRL_LOOPBACK 0x00000010
+#define BM_AUDIOIN_CTRL_FIFO_UNDERFLOW_IRQ 0x00000008
+#define BM_AUDIOIN_CTRL_FIFO_OVERFLOW_IRQ 0x00000004
+#define BM_AUDIOIN_CTRL_FIFO_ERROR_IRQ_EN 0x00000002
+#define BM_AUDIOIN_CTRL_RUN 0x00000001
+HW_REGISTER(HW_AUDIOIN_STAT, REGS_AUDIOIN_BASE, 0x00000010)
+#define HW_AUDIOIN_STAT_ADDR (REGS_AUDIOIN_BASE + 0x00000010)
+#define BM_AUDIOIN_STAT_ADC_PRESENT 0x80000000
+HW_REGISTER(HW_AUDIOIN_ADCSRR, REGS_AUDIOIN_BASE, 0x00000020)
+#define HW_AUDIOIN_ADCSRR_ADDR (REGS_AUDIOIN_BASE + 0x00000020)
+#define BM_AUDIOIN_ADCSRR_OSR 0x80000000
+#define BV_AUDIOIN_ADCSRR_OSR__OSR6 0x0
+#define BV_AUDIOIN_ADCSRR_OSR__OSR12 0x1
+#define BP_AUDIOIN_ADCSRR_BASEMULT 28
+#define BM_AUDIOIN_ADCSRR_BASEMULT 0x70000000
+#define BF_AUDIOIN_ADCSRR_BASEMULT(v) \
+ (((v) << 28) & BM_AUDIOIN_ADCSRR_BASEMULT)
+#define BV_AUDIOIN_ADCSRR_BASEMULT__SINGLE_RATE 0x1
+#define BV_AUDIOIN_ADCSRR_BASEMULT__DOUBLE_RATE 0x2
+#define BV_AUDIOIN_ADCSRR_BASEMULT__QUAD_RATE 0x4
+#define BP_AUDIOIN_ADCSRR_SRC_HOLD 24
+#define BM_AUDIOIN_ADCSRR_SRC_HOLD 0x07000000
+#define BF_AUDIOIN_ADCSRR_SRC_HOLD(v) \
+ (((v) << 24) & BM_AUDIOIN_ADCSRR_SRC_HOLD)
+#define BP_AUDIOIN_ADCSRR_SRC_INT 16
+#define BM_AUDIOIN_ADCSRR_SRC_INT 0x001F0000
+#define BF_AUDIOIN_ADCSRR_SRC_INT(v) \
+ (((v) << 16) & BM_AUDIOIN_ADCSRR_SRC_INT)
+#define BP_AUDIOIN_ADCSRR_SRC_FRAC 0
+#define BM_AUDIOIN_ADCSRR_SRC_FRAC 0x00001FFF
+#define BF_AUDIOIN_ADCSRR_SRC_FRAC(v) \
+ (((v) << 0) & BM_AUDIOIN_ADCSRR_SRC_FRAC)
+HW_REGISTER(HW_AUDIOIN_ADCVOLUME, REGS_AUDIOIN_BASE, 0x00000030)
+#define HW_AUDIOIN_ADCVOLUME_ADDR (REGS_AUDIOIN_BASE + 0x00000030)
+#define BM_AUDIOIN_ADCVOLUME_VOLUME_UPDATE_LEFT 0x10000000
+#define BM_AUDIOIN_ADCVOLUME_EN_ZCD 0x02000000
+#define BP_AUDIOIN_ADCVOLUME_VOLUME_LEFT 16
+#define BM_AUDIOIN_ADCVOLUME_VOLUME_LEFT 0x00FF0000
+#define BF_AUDIOIN_ADCVOLUME_VOLUME_LEFT(v) \
+ (((v) << 16) & BM_AUDIOIN_ADCVOLUME_VOLUME_LEFT)
+#define BM_AUDIOIN_ADCVOLUME_VOLUME_UPDATE_RIGHT 0x00001000
+#define BP_AUDIOIN_ADCVOLUME_VOLUME_RIGHT 0
+#define BM_AUDIOIN_ADCVOLUME_VOLUME_RIGHT 0x000000FF
+#define BF_AUDIOIN_ADCVOLUME_VOLUME_RIGHT(v) \
+ (((v) << 0) & BM_AUDIOIN_ADCVOLUME_VOLUME_RIGHT)
+HW_REGISTER(HW_AUDIOIN_ADCDEBUG, REGS_AUDIOIN_BASE, 0x00000040)
+#define HW_AUDIOIN_ADCDEBUG_ADDR (REGS_AUDIOIN_BASE + 0x00000040)
+#define BM_AUDIOIN_ADCDEBUG_ENABLE_ADCDMA 0x80000000
+#define BM_AUDIOIN_ADCDEBUG_ADC_DMA_REQ_HAND_SHAKE_CLK_CROSS 0x00000008
+#define BM_AUDIOIN_ADCDEBUG_SET_INTERRUPT3_HAND_SHAKE 0x00000004
+#define BM_AUDIOIN_ADCDEBUG_DMA_PREQ 0x00000002
+#define BM_AUDIOIN_ADCDEBUG_FIFO_STATUS 0x00000001
+HW_REGISTER(HW_AUDIOIN_ADCVOL, REGS_AUDIOIN_BASE, 0x00000050)
+#define HW_AUDIOIN_ADCVOL_ADDR (REGS_AUDIOIN_BASE + 0x00000050)
+#define BM_AUDIOIN_ADCVOL_VOLUME_UPDATE_PENDING 0x10000000
+#define BM_AUDIOIN_ADCVOL_EN_ADC_ZCD 0x02000000
+#define BM_AUDIOIN_ADCVOL_MUTE 0x01000000
+#define BP_AUDIOIN_ADCVOL_SELECT_LEFT 12
+#define BM_AUDIOIN_ADCVOL_SELECT_LEFT 0x00003000
+#define BF_AUDIOIN_ADCVOL_SELECT_LEFT(v) \
+ (((v) << 12) & BM_AUDIOIN_ADCVOL_SELECT_LEFT)
+#define BP_AUDIOIN_ADCVOL_GAIN_LEFT 8
+#define BM_AUDIOIN_ADCVOL_GAIN_LEFT 0x00000F00
+#define BF_AUDIOIN_ADCVOL_GAIN_LEFT(v) \
+ (((v) << 8) & BM_AUDIOIN_ADCVOL_GAIN_LEFT)
+#define BP_AUDIOIN_ADCVOL_SELECT_RIGHT 4
+#define BM_AUDIOIN_ADCVOL_SELECT_RIGHT 0x00000030
+#define BF_AUDIOIN_ADCVOL_SELECT_RIGHT(v) \
+ (((v) << 4) & BM_AUDIOIN_ADCVOL_SELECT_RIGHT)
+#define BP_AUDIOIN_ADCVOL_GAIN_RIGHT 0
+#define BM_AUDIOIN_ADCVOL_GAIN_RIGHT 0x0000000F
+#define BF_AUDIOIN_ADCVOL_GAIN_RIGHT(v) \
+ (((v) << 0) & BM_AUDIOIN_ADCVOL_GAIN_RIGHT)
+HW_REGISTER(HW_AUDIOIN_MICLINE, REGS_AUDIOIN_BASE, 0x00000060)
+#define HW_AUDIOIN_MICLINE_ADDR (REGS_AUDIOIN_BASE + 0x00000060)
+#define BM_AUDIOIN_MICLINE_DIVIDE_LINE1 0x20000000
+#define BM_AUDIOIN_MICLINE_DIVIDE_LINE2 0x10000000
+#define BM_AUDIOIN_MICLINE_MIC_SELECT 0x01000000
+#define BP_AUDIOIN_MICLINE_MIC_RESISTOR 20
+#define BM_AUDIOIN_MICLINE_MIC_RESISTOR 0x00300000
+#define BF_AUDIOIN_MICLINE_MIC_RESISTOR(v) \
+ (((v) << 20) & BM_AUDIOIN_MICLINE_MIC_RESISTOR)
+#define BP_AUDIOIN_MICLINE_MIC_BIAS 16
+#define BM_AUDIOIN_MICLINE_MIC_BIAS 0x00070000
+#define BF_AUDIOIN_MICLINE_MIC_BIAS(v) \
+ (((v) << 16) & BM_AUDIOIN_MICLINE_MIC_BIAS)
+#define BP_AUDIOIN_MICLINE_MIC_CHOPCLK 4
+#define BM_AUDIOIN_MICLINE_MIC_CHOPCLK 0x00000030
+#define BF_AUDIOIN_MICLINE_MIC_CHOPCLK(v) \
+ (((v) << 4) & BM_AUDIOIN_MICLINE_MIC_CHOPCLK)
+#define BP_AUDIOIN_MICLINE_MIC_GAIN 0
+#define BM_AUDIOIN_MICLINE_MIC_GAIN 0x00000003
+#define BF_AUDIOIN_MICLINE_MIC_GAIN(v) \
+ (((v) << 0) & BM_AUDIOIN_MICLINE_MIC_GAIN)
+HW_REGISTER(HW_AUDIOIN_ANACLKCTRL, REGS_AUDIOIN_BASE, 0x00000070)
+#define HW_AUDIOIN_ANACLKCTRL_ADDR (REGS_AUDIOIN_BASE + 0x00000070)
+#define BM_AUDIOIN_ANACLKCTRL_CLKGATE 0x80000000
+#define BM_AUDIOIN_ANACLKCTRL_DITHER_OFF 0x00000400
+#define BM_AUDIOIN_ANACLKCTRL_SLOW_DITHER 0x00000200
+#define BM_AUDIOIN_ANACLKCTRL_INVERT_ADCCLK 0x00000100
+#define BP_AUDIOIN_ANACLKCTRL_ADCCLK_SHIFT 4
+#define BM_AUDIOIN_ANACLKCTRL_ADCCLK_SHIFT 0x00000030
+#define BF_AUDIOIN_ANACLKCTRL_ADCCLK_SHIFT(v) \
+ (((v) << 4) & BM_AUDIOIN_ANACLKCTRL_ADCCLK_SHIFT)
+#define BP_AUDIOIN_ANACLKCTRL_ADCDIV 0
+#define BM_AUDIOIN_ANACLKCTRL_ADCDIV 0x00000007
+#define BF_AUDIOIN_ANACLKCTRL_ADCDIV(v) \
+ (((v) << 0) & BM_AUDIOIN_ANACLKCTRL_ADCDIV)
+HW_REGISTER(HW_AUDIOIN_DATA, REGS_AUDIOIN_BASE, 0x00000080)
+#define HW_AUDIOIN_DATA_ADDR (REGS_AUDIOIN_BASE + 0x00000080)
+#define BP_AUDIOIN_DATA_HIGH 16
+#define BM_AUDIOIN_DATA_HIGH 0xFFFF0000
+#define BF_AUDIOIN_DATA_HIGH(v) \
+ (((v) << 16) & BM_AUDIOIN_DATA_HIGH)
+#define BP_AUDIOIN_DATA_LOW 0
+#define BM_AUDIOIN_DATA_LOW 0x0000FFFF
+#define BF_AUDIOIN_DATA_LOW(v) \
+ (((v) << 0) & BM_AUDIOIN_DATA_LOW)
+#endif /* __ARCH_ARM___AUDIOIN_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-audioout.h b/arch/arm/mach-stmp3xxx/include/mach/regs-audioout.h
new file mode 100644
index 000000000000..ec74f5e6f94d
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-audioout.h
@@ -0,0 +1,279 @@
+/*
+ * STMP AUDIOOUT Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___AUDIOOUT_H
+#define __ARCH_ARM___AUDIOOUT_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_AUDIOOUT_BASE (REGS_BASE + 0x48000)
+#define REGS_AUDIOOUT_BASE_PHYS (0x80048000)
+#define REGS_AUDIOOUT_SIZE 0x00002000
+HW_REGISTER(HW_AUDIOOUT_CTRL, REGS_AUDIOOUT_BASE, 0x00000000)
+#define HW_AUDIOOUT_CTRL_ADDR (REGS_AUDIOOUT_BASE + 0x00000000)
+#define BM_AUDIOOUT_CTRL_SFTRST 0x80000000
+#define BM_AUDIOOUT_CTRL_CLKGATE 0x40000000
+#define BP_AUDIOOUT_CTRL_DMAWAIT_COUNT 16
+#define BM_AUDIOOUT_CTRL_DMAWAIT_COUNT 0x001F0000
+#define BF_AUDIOOUT_CTRL_DMAWAIT_COUNT(v) \
+ (((v) << 16) & BM_AUDIOOUT_CTRL_DMAWAIT_COUNT)
+#define BM_AUDIOOUT_CTRL_LR_SWAP 0x00004000
+#define BM_AUDIOOUT_CTRL_EDGE_SYNC 0x00002000
+#define BM_AUDIOOUT_CTRL_INVERT_1BIT 0x00001000
+#define BP_AUDIOOUT_CTRL_SS3D_EFFECT 8
+#define BM_AUDIOOUT_CTRL_SS3D_EFFECT 0x00000300
+#define BF_AUDIOOUT_CTRL_SS3D_EFFECT(v) \
+ (((v) << 8) & BM_AUDIOOUT_CTRL_SS3D_EFFECT)
+#define BM_AUDIOOUT_CTRL_WORD_LENGTH 0x00000040
+#define BM_AUDIOOUT_CTRL_DAC_ZERO_ENABLE 0x00000020
+#define BM_AUDIOOUT_CTRL_LOOPBACK 0x00000010
+#define BM_AUDIOOUT_CTRL_FIFO_UNDERFLOW_IRQ 0x00000008
+#define BM_AUDIOOUT_CTRL_FIFO_OVERFLOW_IRQ 0x00000004
+#define BM_AUDIOOUT_CTRL_FIFO_ERROR_IRQ_EN 0x00000002
+#define BM_AUDIOOUT_CTRL_RUN 0x00000001
+HW_REGISTER(HW_AUDIOOUT_STAT, REGS_AUDIOOUT_BASE, 0x00000010)
+#define HW_AUDIOOUT_STAT_ADDR (REGS_AUDIOOUT_BASE + 0x00000010)
+#define BM_AUDIOOUT_STAT_DAC_PRESENT 0x80000000
+HW_REGISTER(HW_AUDIOOUT_DACSRR, REGS_AUDIOOUT_BASE, 0x00000020)
+#define HW_AUDIOOUT_DACSRR_ADDR (REGS_AUDIOOUT_BASE + 0x00000020)
+#define BM_AUDIOOUT_DACSRR_OSR 0x80000000
+#define BV_AUDIOOUT_DACSRR_OSR__OSR6 0x0
+#define BV_AUDIOOUT_DACSRR_OSR__OSR12 0x1
+#define BP_AUDIOOUT_DACSRR_BASEMULT 28
+#define BM_AUDIOOUT_DACSRR_BASEMULT 0x70000000
+#define BF_AUDIOOUT_DACSRR_BASEMULT(v) \
+ (((v) << 28) & BM_AUDIOOUT_DACSRR_BASEMULT)
+#define BV_AUDIOOUT_DACSRR_BASEMULT__SINGLE_RATE 0x1
+#define BV_AUDIOOUT_DACSRR_BASEMULT__DOUBLE_RATE 0x2
+#define BV_AUDIOOUT_DACSRR_BASEMULT__QUAD_RATE 0x4
+#define BP_AUDIOOUT_DACSRR_SRC_HOLD 24
+#define BM_AUDIOOUT_DACSRR_SRC_HOLD 0x07000000
+#define BF_AUDIOOUT_DACSRR_SRC_HOLD(v) \
+ (((v) << 24) & BM_AUDIOOUT_DACSRR_SRC_HOLD)
+#define BP_AUDIOOUT_DACSRR_SRC_INT 16
+#define BM_AUDIOOUT_DACSRR_SRC_INT 0x001F0000
+#define BF_AUDIOOUT_DACSRR_SRC_INT(v) \
+ (((v) << 16) & BM_AUDIOOUT_DACSRR_SRC_INT)
+#define BP_AUDIOOUT_DACSRR_SRC_FRAC 0
+#define BM_AUDIOOUT_DACSRR_SRC_FRAC 0x00001FFF
+#define BF_AUDIOOUT_DACSRR_SRC_FRAC(v) \
+ (((v) << 0) & BM_AUDIOOUT_DACSRR_SRC_FRAC)
+HW_REGISTER(HW_AUDIOOUT_DACVOLUME, REGS_AUDIOOUT_BASE, 0x00000030)
+#define HW_AUDIOOUT_DACVOLUME_ADDR (REGS_AUDIOOUT_BASE + 0x00000030)
+#define BM_AUDIOOUT_DACVOLUME_VOLUME_UPDATE_LEFT 0x10000000
+#define BM_AUDIOOUT_DACVOLUME_EN_ZCD 0x02000000
+#define BM_AUDIOOUT_DACVOLUME_MUTE_LEFT 0x01000000
+#define BP_AUDIOOUT_DACVOLUME_VOLUME_LEFT 16
+#define BM_AUDIOOUT_DACVOLUME_VOLUME_LEFT 0x00FF0000
+#define BF_AUDIOOUT_DACVOLUME_VOLUME_LEFT(v) \
+ (((v) << 16) & BM_AUDIOOUT_DACVOLUME_VOLUME_LEFT)
+#define BM_AUDIOOUT_DACVOLUME_VOLUME_UPDATE_RIGHT 0x00001000
+#define BM_AUDIOOUT_DACVOLUME_MUTE_RIGHT 0x00000100
+#define BP_AUDIOOUT_DACVOLUME_VOLUME_RIGHT 0
+#define BM_AUDIOOUT_DACVOLUME_VOLUME_RIGHT 0x000000FF
+#define BF_AUDIOOUT_DACVOLUME_VOLUME_RIGHT(v) \
+ (((v) << 0) & BM_AUDIOOUT_DACVOLUME_VOLUME_RIGHT)
+HW_REGISTER(HW_AUDIOOUT_DACDEBUG, REGS_AUDIOOUT_BASE, 0x00000040)
+#define HW_AUDIOOUT_DACDEBUG_ADDR (REGS_AUDIOOUT_BASE + 0x00000040)
+#define BM_AUDIOOUT_DACDEBUG_ENABLE_DACDMA 0x80000000
+#define BP_AUDIOOUT_DACDEBUG_RAM_SS 8
+#define BM_AUDIOOUT_DACDEBUG_RAM_SS 0x00000F00
+#define BF_AUDIOOUT_DACDEBUG_RAM_SS(v) \
+ (((v) << 8) & BM_AUDIOOUT_DACDEBUG_RAM_SS)
+#define BM_AUDIOOUT_DACDEBUG_SET_INTERRUPT1_CLK_CROSS 0x00000020
+#define BM_AUDIOOUT_DACDEBUG_SET_INTERRUPT0_CLK_CROSS 0x00000010
+#define BM_AUDIOOUT_DACDEBUG_SET_INTERRUPT1_HAND_SHAKE 0x00000008
+#define BM_AUDIOOUT_DACDEBUG_SET_INTERRUPT0_HAND_SHAKE 0x00000004
+#define BM_AUDIOOUT_DACDEBUG_DMA_PREQ 0x00000002
+#define BM_AUDIOOUT_DACDEBUG_FIFO_STATUS 0x00000001
+HW_REGISTER(HW_AUDIOOUT_HPVOL, REGS_AUDIOOUT_BASE, 0x00000050)
+#define HW_AUDIOOUT_HPVOL_ADDR (REGS_AUDIOOUT_BASE + 0x00000050)
+#define BM_AUDIOOUT_HPVOL_VOLUME_UPDATE_PENDING 0x10000000
+#define BM_AUDIOOUT_HPVOL_EN_MSTR_ZCD 0x02000000
+#define BM_AUDIOOUT_HPVOL_MUTE 0x01000000
+#define BM_AUDIOOUT_HPVOL_SELECT 0x00010000
+#define BP_AUDIOOUT_HPVOL_VOL_LEFT 8
+#define BM_AUDIOOUT_HPVOL_VOL_LEFT 0x00007F00
+#define BF_AUDIOOUT_HPVOL_VOL_LEFT(v) \
+ (((v) << 8) & BM_AUDIOOUT_HPVOL_VOL_LEFT)
+#define BP_AUDIOOUT_HPVOL_VOL_RIGHT 0
+#define BM_AUDIOOUT_HPVOL_VOL_RIGHT 0x0000007F
+#define BF_AUDIOOUT_HPVOL_VOL_RIGHT(v) \
+ (((v) << 0) & BM_AUDIOOUT_HPVOL_VOL_RIGHT)
+HW_REGISTER(HW_AUDIOOUT_RESERVED, REGS_AUDIOOUT_BASE, 0x00000060)
+#define HW_AUDIOOUT_RESERVED_ADDR (REGS_AUDIOOUT_BASE + 0x00000060)
+HW_REGISTER(HW_AUDIOOUT_PWRDN, REGS_AUDIOOUT_BASE, 0x00000070)
+#define HW_AUDIOOUT_PWRDN_ADDR (REGS_AUDIOOUT_BASE + 0x00000070)
+#define BM_AUDIOOUT_PWRDN_SPEAKER 0x01000000
+#define BM_AUDIOOUT_PWRDN_SELFBIAS 0x00100000
+#define BM_AUDIOOUT_PWRDN_RIGHT_ADC 0x00010000
+#define BM_AUDIOOUT_PWRDN_DAC 0x00001000
+#define BM_AUDIOOUT_PWRDN_ADC 0x00000100
+#define BM_AUDIOOUT_PWRDN_CAPLESS 0x00000010
+#define BM_AUDIOOUT_PWRDN_HEADPHONE 0x00000001
+HW_REGISTER(HW_AUDIOOUT_REFCTRL, REGS_AUDIOOUT_BASE, 0x00000080)
+#define HW_AUDIOOUT_REFCTRL_ADDR (REGS_AUDIOOUT_BASE + 0x00000080)
+#define BM_AUDIOOUT_REFCTRL_FASTSETTLING 0x04000000
+#define BM_AUDIOOUT_REFCTRL_RAISE_REF 0x02000000
+#define BM_AUDIOOUT_REFCTRL_XTAL_BGR_BIAS 0x01000000
+#define BP_AUDIOOUT_REFCTRL_VBG_ADJ 20
+#define BM_AUDIOOUT_REFCTRL_VBG_ADJ 0x00700000
+#define BF_AUDIOOUT_REFCTRL_VBG_ADJ(v) \
+ (((v) << 20) & BM_AUDIOOUT_REFCTRL_VBG_ADJ)
+#define BM_AUDIOOUT_REFCTRL_LOW_PWR 0x00080000
+#define BM_AUDIOOUT_REFCTRL_LW_REF 0x00040000
+#define BP_AUDIOOUT_REFCTRL_BIAS_CTRL 16
+#define BM_AUDIOOUT_REFCTRL_BIAS_CTRL 0x00030000
+#define BF_AUDIOOUT_REFCTRL_BIAS_CTRL(v) \
+ (((v) << 16) & BM_AUDIOOUT_REFCTRL_BIAS_CTRL)
+#define BM_AUDIOOUT_REFCTRL_VDDXTAL_TO_VDDD 0x00004000
+#define BM_AUDIOOUT_REFCTRL_ADJ_ADC 0x00002000
+#define BM_AUDIOOUT_REFCTRL_ADJ_VAG 0x00001000
+#define BP_AUDIOOUT_REFCTRL_ADC_REFVAL 8
+#define BM_AUDIOOUT_REFCTRL_ADC_REFVAL 0x00000F00
+#define BF_AUDIOOUT_REFCTRL_ADC_REFVAL(v) \
+ (((v) << 8) & BM_AUDIOOUT_REFCTRL_ADC_REFVAL)
+#define BP_AUDIOOUT_REFCTRL_VAG_VAL 4
+#define BM_AUDIOOUT_REFCTRL_VAG_VAL 0x000000F0
+#define BF_AUDIOOUT_REFCTRL_VAG_VAL(v) \
+ (((v) << 4) & BM_AUDIOOUT_REFCTRL_VAG_VAL)
+#define BP_AUDIOOUT_REFCTRL_DAC_ADJ 0
+#define BM_AUDIOOUT_REFCTRL_DAC_ADJ 0x00000007
+#define BF_AUDIOOUT_REFCTRL_DAC_ADJ(v) \
+ (((v) << 0) & BM_AUDIOOUT_REFCTRL_DAC_ADJ)
+HW_REGISTER(HW_AUDIOOUT_ANACTRL, REGS_AUDIOOUT_BASE, 0x00000090)
+#define HW_AUDIOOUT_ANACTRL_ADDR (REGS_AUDIOOUT_BASE + 0x00000090)
+#define BM_AUDIOOUT_ANACTRL_SHORT_CM_STS 0x10000000
+#define BM_AUDIOOUT_ANACTRL_SHORT_LR_STS 0x01000000
+#define BP_AUDIOOUT_ANACTRL_SHORTMODE_CM 20
+#define BM_AUDIOOUT_ANACTRL_SHORTMODE_CM 0x00300000
+#define BF_AUDIOOUT_ANACTRL_SHORTMODE_CM(v) \
+ (((v) << 20) & BM_AUDIOOUT_ANACTRL_SHORTMODE_CM)
+#define BP_AUDIOOUT_ANACTRL_SHORTMODE_LR 17
+#define BM_AUDIOOUT_ANACTRL_SHORTMODE_LR 0x00060000
+#define BF_AUDIOOUT_ANACTRL_SHORTMODE_LR(v) \
+ (((v) << 17) & BM_AUDIOOUT_ANACTRL_SHORTMODE_LR)
+#define BP_AUDIOOUT_ANACTRL_SHORT_LVLADJL 12
+#define BM_AUDIOOUT_ANACTRL_SHORT_LVLADJL 0x00007000
+#define BF_AUDIOOUT_ANACTRL_SHORT_LVLADJL(v) \
+ (((v) << 12) & BM_AUDIOOUT_ANACTRL_SHORT_LVLADJL)
+#define BP_AUDIOOUT_ANACTRL_SHORT_LVLADJR 8
+#define BM_AUDIOOUT_ANACTRL_SHORT_LVLADJR 0x00000700
+#define BF_AUDIOOUT_ANACTRL_SHORT_LVLADJR(v) \
+ (((v) << 8) & BM_AUDIOOUT_ANACTRL_SHORT_LVLADJR)
+#define BM_AUDIOOUT_ANACTRL_HP_HOLD_GND 0x00000020
+#define BM_AUDIOOUT_ANACTRL_HP_CLASSAB 0x00000010
+HW_REGISTER(HW_AUDIOOUT_TEST, REGS_AUDIOOUT_BASE, 0x000000a0)
+#define HW_AUDIOOUT_TEST_ADDR (REGS_AUDIOOUT_BASE + 0x000000a0)
+#define BP_AUDIOOUT_TEST_HP_ANTIPOP 28
+#define BM_AUDIOOUT_TEST_HP_ANTIPOP 0x70000000
+#define BF_AUDIOOUT_TEST_HP_ANTIPOP(v) \
+ (((v) << 28) & BM_AUDIOOUT_TEST_HP_ANTIPOP)
+#define BM_AUDIOOUT_TEST_TM_ADCIN_TOHP 0x04000000
+#define BM_AUDIOOUT_TEST_TM_LOOP 0x02000000
+#define BM_AUDIOOUT_TEST_TM_HPCOMMON 0x01000000
+#define BP_AUDIOOUT_TEST_HP_I1_ADJ 22
+#define BM_AUDIOOUT_TEST_HP_I1_ADJ 0x00C00000
+#define BF_AUDIOOUT_TEST_HP_I1_ADJ(v) \
+ (((v) << 22) & BM_AUDIOOUT_TEST_HP_I1_ADJ)
+#define BP_AUDIOOUT_TEST_HP_IALL_ADJ 20
+#define BM_AUDIOOUT_TEST_HP_IALL_ADJ 0x00300000
+#define BF_AUDIOOUT_TEST_HP_IALL_ADJ(v) \
+ (((v) << 20) & BM_AUDIOOUT_TEST_HP_IALL_ADJ)
+#define BM_AUDIOOUT_TEST_VAG_CLASSA 0x00002000
+#define BM_AUDIOOUT_TEST_VAG_DOUBLE_I 0x00001000
+#define BM_AUDIOOUT_TEST_ADCTODAC_LOOP 0x00000008
+#define BM_AUDIOOUT_TEST_DAC_CLASSA 0x00000004
+#define BM_AUDIOOUT_TEST_DAC_DOUBLE_I 0x00000002
+#define BM_AUDIOOUT_TEST_DAC_DIS_RTZ 0x00000001
+HW_REGISTER(HW_AUDIOOUT_BISTCTRL, REGS_AUDIOOUT_BASE, 0x000000b0)
+#define HW_AUDIOOUT_BISTCTRL_ADDR (REGS_AUDIOOUT_BASE + 0x000000b0)
+#define BM_AUDIOOUT_BISTCTRL_FAIL 0x00000008
+#define BM_AUDIOOUT_BISTCTRL_PASS 0x00000004
+#define BM_AUDIOOUT_BISTCTRL_DONE 0x00000002
+#define BM_AUDIOOUT_BISTCTRL_START 0x00000001
+HW_REGISTER(HW_AUDIOOUT_BISTSTAT0, REGS_AUDIOOUT_BASE, 0x000000c0)
+#define HW_AUDIOOUT_BISTSTAT0_ADDR (REGS_AUDIOOUT_BASE + 0x000000c0)
+#define BP_AUDIOOUT_BISTSTAT0_DATA 0
+#define BM_AUDIOOUT_BISTSTAT0_DATA 0x00FFFFFF
+#define BF_AUDIOOUT_BISTSTAT0_DATA(v) \
+ (((v) << 0) & BM_AUDIOOUT_BISTSTAT0_DATA)
+HW_REGISTER(HW_AUDIOOUT_BISTSTAT1, REGS_AUDIOOUT_BASE, 0x000000d0)
+#define HW_AUDIOOUT_BISTSTAT1_ADDR (REGS_AUDIOOUT_BASE + 0x000000d0)
+#define BP_AUDIOOUT_BISTSTAT1_STATE 24
+#define BM_AUDIOOUT_BISTSTAT1_STATE 0x1F000000
+#define BF_AUDIOOUT_BISTSTAT1_STATE(v) \
+ (((v) << 24) & BM_AUDIOOUT_BISTSTAT1_STATE)
+#define BP_AUDIOOUT_BISTSTAT1_ADDR 0
+#define BM_AUDIOOUT_BISTSTAT1_ADDR 0x000000FF
+#define BF_AUDIOOUT_BISTSTAT1_ADDR(v) \
+ (((v) << 0) & BM_AUDIOOUT_BISTSTAT1_ADDR)
+HW_REGISTER(HW_AUDIOOUT_ANACLKCTRL, REGS_AUDIOOUT_BASE, 0x000000e0)
+#define HW_AUDIOOUT_ANACLKCTRL_ADDR (REGS_AUDIOOUT_BASE + 0x000000e0)
+#define BM_AUDIOOUT_ANACLKCTRL_CLKGATE 0x80000000
+#define BM_AUDIOOUT_ANACLKCTRL_INVERT_DACCLK 0x00000010
+#define BP_AUDIOOUT_ANACLKCTRL_DACDIV 0
+#define BM_AUDIOOUT_ANACLKCTRL_DACDIV 0x00000007
+#define BF_AUDIOOUT_ANACLKCTRL_DACDIV(v) \
+ (((v) << 0) & BM_AUDIOOUT_ANACLKCTRL_DACDIV)
+HW_REGISTER(HW_AUDIOOUT_DATA, REGS_AUDIOOUT_BASE, 0x000000f0)
+#define HW_AUDIOOUT_DATA_ADDR (REGS_AUDIOOUT_BASE + 0x000000f0)
+#define BP_AUDIOOUT_DATA_HIGH 16
+#define BM_AUDIOOUT_DATA_HIGH 0xFFFF0000
+#define BF_AUDIOOUT_DATA_HIGH(v) \
+ (((v) << 16) & BM_AUDIOOUT_DATA_HIGH)
+#define BP_AUDIOOUT_DATA_LOW 0
+#define BM_AUDIOOUT_DATA_LOW 0x0000FFFF
+#define BF_AUDIOOUT_DATA_LOW(v) \
+ (((v) << 0) & BM_AUDIOOUT_DATA_LOW)
+HW_REGISTER(HW_AUDIOOUT_SPEAKERCTRL, REGS_AUDIOOUT_BASE, 0x00000100)
+#define HW_AUDIOOUT_SPEAKERCTRL_ADDR (REGS_AUDIOOUT_BASE + 0x00000100)
+#define BM_AUDIOOUT_SPEAKERCTRL_MUTE 0x01000000
+#define BP_AUDIOOUT_SPEAKERCTRL_I1_ADJ 22
+#define BM_AUDIOOUT_SPEAKERCTRL_I1_ADJ 0x00C00000
+#define BF_AUDIOOUT_SPEAKERCTRL_I1_ADJ(v) \
+ (((v) << 22) & BM_AUDIOOUT_SPEAKERCTRL_I1_ADJ)
+#define BP_AUDIOOUT_SPEAKERCTRL_IALL_ADJ 20
+#define BM_AUDIOOUT_SPEAKERCTRL_IALL_ADJ 0x00300000
+#define BF_AUDIOOUT_SPEAKERCTRL_IALL_ADJ(v) \
+ (((v) << 20) & BM_AUDIOOUT_SPEAKERCTRL_IALL_ADJ)
+#define BP_AUDIOOUT_SPEAKERCTRL_POSDRIVER 14
+#define BM_AUDIOOUT_SPEAKERCTRL_POSDRIVER 0x0000C000
+#define BF_AUDIOOUT_SPEAKERCTRL_POSDRIVER(v) \
+ (((v) << 14) & BM_AUDIOOUT_SPEAKERCTRL_POSDRIVER)
+#define BP_AUDIOOUT_SPEAKERCTRL_NEGDRIVER 12
+#define BM_AUDIOOUT_SPEAKERCTRL_NEGDRIVER 0x00003000
+#define BF_AUDIOOUT_SPEAKERCTRL_NEGDRIVER(v) \
+ (((v) << 12) & BM_AUDIOOUT_SPEAKERCTRL_NEGDRIVER)
+HW_REGISTER_0(HW_AUDIOOUT_VERSION, REGS_AUDIOOUT_BASE, 0x00000200)
+#define HW_AUDIOOUT_VERSION_ADDR (REGS_AUDIOOUT_BASE + 0x00000200)
+#define BP_AUDIOOUT_VERSION_MAJOR 24
+#define BM_AUDIOOUT_VERSION_MAJOR 0xFF000000
+#define BF_AUDIOOUT_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_AUDIOOUT_VERSION_MAJOR)
+#define BP_AUDIOOUT_VERSION_MINOR 16
+#define BM_AUDIOOUT_VERSION_MINOR 0x00FF0000
+#define BF_AUDIOOUT_VERSION_MINOR(v) \
+ (((v) << 16) & BM_AUDIOOUT_VERSION_MINOR)
+#define BP_AUDIOOUT_VERSION_STEP 0
+#define BM_AUDIOOUT_VERSION_STEP 0x0000FFFF
+#define BF_AUDIOOUT_VERSION_STEP(v) \
+ (((v) << 0) & BM_AUDIOOUT_VERSION_STEP)
+#endif /* __ARCH_ARM___AUDIOOUT_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-bch.h b/arch/arm/mach-stmp3xxx/include/mach/regs-bch.h
new file mode 100644
index 000000000000..2e4257314878
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-bch.h
@@ -0,0 +1,447 @@
+/*
+ * STMP BCH Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___BCH_H
+#define __ARCH_ARM___BCH_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_BCH_BASE (REGS_BASE + 0xa000)
+#define REGS_BCH_BASE_PHYS (0x8000A000)
+#define REGS_BCH_SIZE 0x00002000
+HW_REGISTER(HW_BCH_CTRL, REGS_BCH_BASE, 0x00000000)
+#define HW_BCH_CTRL_ADDR (REGS_BCH_BASE + 0x00000000)
+#define BM_BCH_CTRL_SFTRST 0x80000000
+#define BV_BCH_CTRL_SFTRST__RUN 0x0
+#define BV_BCH_CTRL_SFTRST__RESET 0x1
+#define BM_BCH_CTRL_CLKGATE 0x40000000
+#define BV_BCH_CTRL_CLKGATE__RUN 0x0
+#define BV_BCH_CTRL_CLKGATE__NO_CLKS 0x1
+#define BM_BCH_CTRL_DEBUGSYNDROME 0x00400000
+#define BP_BCH_CTRL_M2M_LAYOUT 18
+#define BM_BCH_CTRL_M2M_LAYOUT 0x000C0000
+#define BF_BCH_CTRL_M2M_LAYOUT(v) \
+ (((v) << 18) & BM_BCH_CTRL_M2M_LAYOUT)
+#define BM_BCH_CTRL_M2M_ENCODE 0x00020000
+#define BM_BCH_CTRL_M2M_ENABLE 0x00010000
+#define BM_BCH_CTRL_DEBUG_STALL_IRQ_EN 0x00000400
+#define BM_BCH_CTRL_COMPLETE_IRQ_EN 0x00000100
+#define BM_BCH_CTRL_BM_ERROR_IRQ 0x00000008
+#define BM_BCH_CTRL_DEBUG_STALL_IRQ 0x00000004
+#define BM_BCH_CTRL_COMPLETE_IRQ 0x00000001
+HW_REGISTER_0(HW_BCH_STATUS0, REGS_BCH_BASE, 0x00000010)
+#define HW_BCH_STATUS0_ADDR (REGS_BCH_BASE + 0x00000010)
+#define BP_BCH_STATUS0_HANDLE 20
+#define BM_BCH_STATUS0_HANDLE 0xFFF00000
+#define BF_BCH_STATUS0_HANDLE(v) \
+ (((v) << 20) & BM_BCH_STATUS0_HANDLE)
+#define BP_BCH_STATUS0_COMPLETED_CE 16
+#define BM_BCH_STATUS0_COMPLETED_CE 0x000F0000
+#define BF_BCH_STATUS0_COMPLETED_CE(v) \
+ (((v) << 16) & BM_BCH_STATUS0_COMPLETED_CE)
+#define BP_BCH_STATUS0_STATUS_BLK0 8
+#define BM_BCH_STATUS0_STATUS_BLK0 0x0000FF00
+#define BF_BCH_STATUS0_STATUS_BLK0(v) \
+ (((v) << 8) & BM_BCH_STATUS0_STATUS_BLK0)
+#define BV_BCH_STATUS0_STATUS_BLK0__ZERO 0x00
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR1 0x01
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR2 0x02
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR3 0x03
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR4 0x04
+#define BV_BCH_STATUS0_STATUS_BLK0__UNCORRECTABLE 0xFE
+#define BV_BCH_STATUS0_STATUS_BLK0__ERASED 0xFF
+#define BM_BCH_STATUS0_ALLONES 0x00000010
+#define BM_BCH_STATUS0_CORRECTED 0x00000008
+#define BM_BCH_STATUS0_UNCORRECTABLE 0x00000004
+HW_REGISTER_0(HW_BCH_MODE, REGS_BCH_BASE, 0x00000020)
+#define HW_BCH_MODE_ADDR (REGS_BCH_BASE + 0x00000020)
+#define BP_BCH_MODE_ERASE_THRESHOLD 0
+#define BM_BCH_MODE_ERASE_THRESHOLD 0x000000FF
+#define BF_BCH_MODE_ERASE_THRESHOLD(v) \
+ (((v) << 0) & BM_BCH_MODE_ERASE_THRESHOLD)
+HW_REGISTER_0(HW_BCH_ENCODEPTR, REGS_BCH_BASE, 0x00000030)
+#define HW_BCH_ENCODEPTR_ADDR (REGS_BCH_BASE + 0x00000030)
+#define BP_BCH_ENCODEPTR_ADDR 0
+#define BM_BCH_ENCODEPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_ENCODEPTR_ADDR(v) (v)
+HW_REGISTER_0(HW_BCH_DATAPTR, REGS_BCH_BASE, 0x00000040)
+#define HW_BCH_DATAPTR_ADDR (REGS_BCH_BASE + 0x00000040)
+#define BP_BCH_DATAPTR_ADDR 0
+#define BM_BCH_DATAPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_DATAPTR_ADDR(v) (v)
+HW_REGISTER_0(HW_BCH_METAPTR, REGS_BCH_BASE, 0x00000050)
+#define HW_BCH_METAPTR_ADDR (REGS_BCH_BASE + 0x00000050)
+#define BP_BCH_METAPTR_ADDR 0
+#define BM_BCH_METAPTR_ADDR 0xFFFFFFFF
+#define BF_BCH_METAPTR_ADDR(v) (v)
+HW_REGISTER_0(HW_BCH_LAYOUTSELECT, REGS_BCH_BASE, 0x00000070)
+#define HW_BCH_LAYOUTSELECT_ADDR (REGS_BCH_BASE + 0x00000070)
+#define BP_BCH_LAYOUTSELECT_CS15_SELECT 30
+#define BM_BCH_LAYOUTSELECT_CS15_SELECT 0xC0000000
+#define BF_BCH_LAYOUTSELECT_CS15_SELECT(v) \
+ (((v) << 30) & BM_BCH_LAYOUTSELECT_CS15_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS14_SELECT 28
+#define BM_BCH_LAYOUTSELECT_CS14_SELECT 0x30000000
+#define BF_BCH_LAYOUTSELECT_CS14_SELECT(v) \
+ (((v) << 28) & BM_BCH_LAYOUTSELECT_CS14_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS13_SELECT 26
+#define BM_BCH_LAYOUTSELECT_CS13_SELECT 0x0C000000
+#define BF_BCH_LAYOUTSELECT_CS13_SELECT(v) \
+ (((v) << 26) & BM_BCH_LAYOUTSELECT_CS13_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS12_SELECT 24
+#define BM_BCH_LAYOUTSELECT_CS12_SELECT 0x03000000
+#define BF_BCH_LAYOUTSELECT_CS12_SELECT(v) \
+ (((v) << 24) & BM_BCH_LAYOUTSELECT_CS12_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS11_SELECT 22
+#define BM_BCH_LAYOUTSELECT_CS11_SELECT 0x00C00000
+#define BF_BCH_LAYOUTSELECT_CS11_SELECT(v) \
+ (((v) << 22) & BM_BCH_LAYOUTSELECT_CS11_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS10_SELECT 20
+#define BM_BCH_LAYOUTSELECT_CS10_SELECT 0x00300000
+#define BF_BCH_LAYOUTSELECT_CS10_SELECT(v) \
+ (((v) << 20) & BM_BCH_LAYOUTSELECT_CS10_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS9_SELECT 18
+#define BM_BCH_LAYOUTSELECT_CS9_SELECT 0x000C0000
+#define BF_BCH_LAYOUTSELECT_CS9_SELECT(v) \
+ (((v) << 18) & BM_BCH_LAYOUTSELECT_CS9_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS8_SELECT 16
+#define BM_BCH_LAYOUTSELECT_CS8_SELECT 0x00030000
+#define BF_BCH_LAYOUTSELECT_CS8_SELECT(v) \
+ (((v) << 16) & BM_BCH_LAYOUTSELECT_CS8_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS7_SELECT 14
+#define BM_BCH_LAYOUTSELECT_CS7_SELECT 0x0000C000
+#define BF_BCH_LAYOUTSELECT_CS7_SELECT(v) \
+ (((v) << 14) & BM_BCH_LAYOUTSELECT_CS7_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS6_SELECT 12
+#define BM_BCH_LAYOUTSELECT_CS6_SELECT 0x00003000
+#define BF_BCH_LAYOUTSELECT_CS6_SELECT(v) \
+ (((v) << 12) & BM_BCH_LAYOUTSELECT_CS6_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS5_SELECT 10
+#define BM_BCH_LAYOUTSELECT_CS5_SELECT 0x00000C00
+#define BF_BCH_LAYOUTSELECT_CS5_SELECT(v) \
+ (((v) << 10) & BM_BCH_LAYOUTSELECT_CS5_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS4_SELECT 8
+#define BM_BCH_LAYOUTSELECT_CS4_SELECT 0x00000300
+#define BF_BCH_LAYOUTSELECT_CS4_SELECT(v) \
+ (((v) << 8) & BM_BCH_LAYOUTSELECT_CS4_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS3_SELECT 6
+#define BM_BCH_LAYOUTSELECT_CS3_SELECT 0x000000C0
+#define BF_BCH_LAYOUTSELECT_CS3_SELECT(v) \
+ (((v) << 6) & BM_BCH_LAYOUTSELECT_CS3_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS2_SELECT 4
+#define BM_BCH_LAYOUTSELECT_CS2_SELECT 0x00000030
+#define BF_BCH_LAYOUTSELECT_CS2_SELECT(v) \
+ (((v) << 4) & BM_BCH_LAYOUTSELECT_CS2_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS1_SELECT 2
+#define BM_BCH_LAYOUTSELECT_CS1_SELECT 0x0000000C
+#define BF_BCH_LAYOUTSELECT_CS1_SELECT(v) \
+ (((v) << 2) & BM_BCH_LAYOUTSELECT_CS1_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS0_SELECT 0
+#define BM_BCH_LAYOUTSELECT_CS0_SELECT 0x00000003
+#define BF_BCH_LAYOUTSELECT_CS0_SELECT(v) \
+ (((v) << 0) & BM_BCH_LAYOUTSELECT_CS0_SELECT)
+HW_REGISTER_0(HW_BCH_FLASH0LAYOUT0, REGS_BCH_BASE, 0x00000080)
+#define HW_BCH_FLASH0LAYOUT0_ADDR (REGS_BCH_BASE + 0x00000080)
+#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH0LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH0LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH0LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH0LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH0LAYOUT0_ECC0 12
+#define BM_BCH_FLASH0LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH0LAYOUT0_ECC0)
+#define BV_BCH_FLASH0LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+HW_REGISTER_0(HW_BCH_FLASH0LAYOUT1, REGS_BCH_BASE, 0x00000090)
+#define HW_BCH_FLASH0LAYOUT1_ADDR (REGS_BCH_BASE + 0x00000090)
+#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH0LAYOUT1_ECCN 12
+#define BM_BCH_FLASH0LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH0LAYOUT1_ECCN)
+#define BV_BCH_FLASH0LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+HW_REGISTER_0(HW_BCH_FLASH1LAYOUT0, REGS_BCH_BASE, 0x000000a0)
+#define HW_BCH_FLASH1LAYOUT0_ADDR (REGS_BCH_BASE + 0x000000a0)
+#define BP_BCH_FLASH1LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH1LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH1LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH1LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH1LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH1LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH1LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH1LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH1LAYOUT0_ECC0 12
+#define BM_BCH_FLASH1LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH1LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH1LAYOUT0_ECC0)
+#define BV_BCH_FLASH1LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH1LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH1LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH1LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH1LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH1LAYOUT0_DATA0_SIZE)
+HW_REGISTER_0(HW_BCH_FLASH1LAYOUT1, REGS_BCH_BASE, 0x000000b0)
+#define HW_BCH_FLASH1LAYOUT1_ADDR (REGS_BCH_BASE + 0x000000b0)
+#define BP_BCH_FLASH1LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH1LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH1LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH1LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH1LAYOUT1_ECCN 12
+#define BM_BCH_FLASH1LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH1LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH1LAYOUT1_ECCN)
+#define BV_BCH_FLASH1LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH1LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH1LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH1LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH1LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH1LAYOUT1_DATAN_SIZE)
+HW_REGISTER_0(HW_BCH_FLASH2LAYOUT0, REGS_BCH_BASE, 0x000000c0)
+#define HW_BCH_FLASH2LAYOUT0_ADDR (REGS_BCH_BASE + 0x000000c0)
+#define BP_BCH_FLASH2LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH2LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH2LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH2LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH2LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH2LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH2LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH2LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH2LAYOUT0_ECC0 12
+#define BM_BCH_FLASH2LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH2LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH2LAYOUT0_ECC0)
+#define BV_BCH_FLASH2LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH2LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH2LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH2LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH2LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH2LAYOUT0_DATA0_SIZE)
+HW_REGISTER_0(HW_BCH_FLASH2LAYOUT1, REGS_BCH_BASE, 0x000000d0)
+#define HW_BCH_FLASH2LAYOUT1_ADDR (REGS_BCH_BASE + 0x000000d0)
+#define BP_BCH_FLASH2LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH2LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH2LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH2LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH2LAYOUT1_ECCN 12
+#define BM_BCH_FLASH2LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH2LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH2LAYOUT1_ECCN)
+#define BV_BCH_FLASH2LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH2LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH2LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH2LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH2LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH2LAYOUT1_DATAN_SIZE)
+HW_REGISTER_0(HW_BCH_FLASH3LAYOUT0, REGS_BCH_BASE, 0x000000e0)
+#define HW_BCH_FLASH3LAYOUT0_ADDR (REGS_BCH_BASE + 0x000000e0)
+#define BP_BCH_FLASH3LAYOUT0_NBLOCKS 24
+#define BM_BCH_FLASH3LAYOUT0_NBLOCKS 0xFF000000
+#define BF_BCH_FLASH3LAYOUT0_NBLOCKS(v) \
+ (((v) << 24) & BM_BCH_FLASH3LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH3LAYOUT0_META_SIZE 16
+#define BM_BCH_FLASH3LAYOUT0_META_SIZE 0x00FF0000
+#define BF_BCH_FLASH3LAYOUT0_META_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH3LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH3LAYOUT0_ECC0 12
+#define BM_BCH_FLASH3LAYOUT0_ECC0 0x0000F000
+#define BF_BCH_FLASH3LAYOUT0_ECC0(v) \
+ (((v) << 12) & BM_BCH_FLASH3LAYOUT0_ECC0)
+#define BV_BCH_FLASH3LAYOUT0_ECC0__NONE 0x0
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC2 0x1
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC4 0x2
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC6 0x3
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC8 0x4
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC10 0x5
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC12 0x6
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC14 0x7
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC16 0x8
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC18 0x9
+#define BV_BCH_FLASH3LAYOUT0_ECC0__ECC20 0xA
+#define BP_BCH_FLASH3LAYOUT0_DATA0_SIZE 0
+#define BM_BCH_FLASH3LAYOUT0_DATA0_SIZE 0x00000FFF
+#define BF_BCH_FLASH3LAYOUT0_DATA0_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH3LAYOUT0_DATA0_SIZE)
+HW_REGISTER_0(HW_BCH_FLASH3LAYOUT1, REGS_BCH_BASE, 0x000000f0)
+#define HW_BCH_FLASH3LAYOUT1_ADDR (REGS_BCH_BASE + 0x000000f0)
+#define BP_BCH_FLASH3LAYOUT1_PAGE_SIZE 16
+#define BM_BCH_FLASH3LAYOUT1_PAGE_SIZE 0xFFFF0000
+#define BF_BCH_FLASH3LAYOUT1_PAGE_SIZE(v) \
+ (((v) << 16) & BM_BCH_FLASH3LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH3LAYOUT1_ECCN 12
+#define BM_BCH_FLASH3LAYOUT1_ECCN 0x0000F000
+#define BF_BCH_FLASH3LAYOUT1_ECCN(v) \
+ (((v) << 12) & BM_BCH_FLASH3LAYOUT1_ECCN)
+#define BV_BCH_FLASH3LAYOUT1_ECCN__NONE 0x0
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC2 0x1
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC4 0x2
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC6 0x3
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC8 0x4
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC10 0x5
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC12 0x6
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC14 0x7
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC16 0x8
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC18 0x9
+#define BV_BCH_FLASH3LAYOUT1_ECCN__ECC20 0xA
+#define BP_BCH_FLASH3LAYOUT1_DATAN_SIZE 0
+#define BM_BCH_FLASH3LAYOUT1_DATAN_SIZE 0x00000FFF
+#define BF_BCH_FLASH3LAYOUT1_DATAN_SIZE(v) \
+ (((v) << 0) & BM_BCH_FLASH3LAYOUT1_DATAN_SIZE)
+HW_REGISTER(HW_BCH_DEBUG0, REGS_BCH_BASE, 0x00000100)
+#define HW_BCH_DEBUG0_ADDR (REGS_BCH_BASE + 0x00000100)
+#define BM_BCH_DEBUG0_ROM_BIST_ENABLE 0x04000000
+#define BM_BCH_DEBUG0_ROM_BIST_COMPLETE 0x02000000
+#define BP_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 16
+#define BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 0x01FF0000
+#define BF_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL(v) \
+ (((v) << 16) & BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL)
+#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__TEST_MODE 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_SHIFT_SYND 0x00008000
+#define BM_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG 0x00004000
+#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__DATA 0x1
+#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__AUX 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_MODE4K 0x00002000
+#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__4k 0x1
+#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__2k 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_KICK 0x00001000
+#define BM_BCH_DEBUG0_KES_STANDALONE 0x00000800
+#define BV_BCH_DEBUG0_KES_STANDALONE__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_STANDALONE__TEST_MODE 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_STEP 0x00000400
+#define BM_BCH_DEBUG0_KES_DEBUG_STALL 0x00000200
+#define BV_BCH_DEBUG0_KES_DEBUG_STALL__NORMAL 0x0
+#define BV_BCH_DEBUG0_KES_DEBUG_STALL__WAIT 0x1
+#define BM_BCH_DEBUG0_BM_KES_TEST_BYPASS 0x00000100
+#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__NORMAL 0x0
+#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__TEST_MODE 0x1
+#define BP_BCH_DEBUG0_DEBUG_REG_SELECT 0
+#define BM_BCH_DEBUG0_DEBUG_REG_SELECT 0x0000003F
+#define BF_BCH_DEBUG0_DEBUG_REG_SELECT(v) \
+ (((v) << 0) & BM_BCH_DEBUG0_DEBUG_REG_SELECT)
+HW_REGISTER_0(HW_BCH_DBGKESREAD, REGS_BCH_BASE, 0x00000110)
+#define HW_BCH_DBGKESREAD_ADDR (REGS_BCH_BASE + 0x00000110)
+#define BP_BCH_DBGKESREAD_VALUES 0
+#define BM_BCH_DBGKESREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGKESREAD_VALUES(v) (v)
+HW_REGISTER_0(HW_BCH_DBGCSFEREAD, REGS_BCH_BASE, 0x00000120)
+#define HW_BCH_DBGCSFEREAD_ADDR (REGS_BCH_BASE + 0x00000120)
+#define BP_BCH_DBGCSFEREAD_VALUES 0
+#define BM_BCH_DBGCSFEREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGCSFEREAD_VALUES(v) (v)
+HW_REGISTER_0(HW_BCH_DBGSYNDGENREAD, REGS_BCH_BASE, 0x00000130)
+#define HW_BCH_DBGSYNDGENREAD_ADDR (REGS_BCH_BASE + 0x00000130)
+#define BP_BCH_DBGSYNDGENREAD_VALUES 0
+#define BM_BCH_DBGSYNDGENREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGSYNDGENREAD_VALUES(v) (v)
+HW_REGISTER_0(HW_BCH_DBGAHBMREAD, REGS_BCH_BASE, 0x00000140)
+#define HW_BCH_DBGAHBMREAD_ADDR (REGS_BCH_BASE + 0x00000140)
+#define BP_BCH_DBGAHBMREAD_VALUES 0
+#define BM_BCH_DBGAHBMREAD_VALUES 0xFFFFFFFF
+#define BF_BCH_DBGAHBMREAD_VALUES(v) (v)
+HW_REGISTER_0(HW_BCH_BLOCKNAME, REGS_BCH_BASE, 0x00000150)
+#define HW_BCH_BLOCKNAME_ADDR (REGS_BCH_BASE + 0x00000150)
+#define BP_BCH_BLOCKNAME_NAME 0
+#define BM_BCH_BLOCKNAME_NAME 0xFFFFFFFF
+#define BF_BCH_BLOCKNAME_NAME(v) (v)
+HW_REGISTER_0(HW_BCH_VERSION, REGS_BCH_BASE, 0x00000160)
+#define HW_BCH_VERSION_ADDR (REGS_BCH_BASE + 0x00000160)
+#define BP_BCH_VERSION_MAJOR 24
+#define BM_BCH_VERSION_MAJOR 0xFF000000
+#define BF_BCH_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_BCH_VERSION_MAJOR)
+#define BP_BCH_VERSION_MINOR 16
+#define BM_BCH_VERSION_MINOR 0x00FF0000
+#define BF_BCH_VERSION_MINOR(v) \
+ (((v) << 16) & BM_BCH_VERSION_MINOR)
+#define BP_BCH_VERSION_STEP 0
+#define BM_BCH_VERSION_STEP 0x0000FFFF
+#define BF_BCH_VERSION_STEP(v) \
+ (((v) << 0) & BM_BCH_VERSION_STEP)
+#endif /* __ARCH_ARM___BCH_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-clkctrl.h b/arch/arm/mach-stmp3xxx/include/mach/regs-clkctrl.h
new file mode 100644
index 000000000000..49705907fa6e
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-clkctrl.h
@@ -0,0 +1,275 @@
+/*
+ * STMP CLKCTRL Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___CLKCTRL_H
+#define __ARCH_ARM___CLKCTRL_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_CLKCTRL_BASE (REGS_BASE + 0x40000)
+#define REGS_CLKCTRL_BASE_PHYS (0x80040000)
+#define REGS_CLKCTRL_SIZE 0x00002000
+HW_REGISTER(HW_CLKCTRL_PLLCTRL0, REGS_CLKCTRL_BASE, 0x00000000)
+#define HW_CLKCTRL_PLLCTRL0_ADDR (REGS_CLKCTRL_BASE + 0x00000000)
+#define BP_CLKCTRL_PLLCTRL0_LFR_SEL 28
+#define BM_CLKCTRL_PLLCTRL0_LFR_SEL 0x30000000
+#define BF_CLKCTRL_PLLCTRL0_LFR_SEL(v) \
+ (((v) << 28) & BM_CLKCTRL_PLLCTRL0_LFR_SEL)
+#define BV_CLKCTRL_PLLCTRL0_LFR_SEL__DEFAULT 0x0
+#define BV_CLKCTRL_PLLCTRL0_LFR_SEL__TIMES_2 0x1
+#define BV_CLKCTRL_PLLCTRL0_LFR_SEL__TIMES_05 0x2
+#define BV_CLKCTRL_PLLCTRL0_LFR_SEL__UNDEFINED 0x3
+#define BP_CLKCTRL_PLLCTRL0_CP_SEL 24
+#define BM_CLKCTRL_PLLCTRL0_CP_SEL 0x03000000
+#define BF_CLKCTRL_PLLCTRL0_CP_SEL(v) \
+ (((v) << 24) & BM_CLKCTRL_PLLCTRL0_CP_SEL)
+#define BV_CLKCTRL_PLLCTRL0_CP_SEL__DEFAULT 0x0
+#define BV_CLKCTRL_PLLCTRL0_CP_SEL__TIMES_2 0x1
+#define BV_CLKCTRL_PLLCTRL0_CP_SEL__TIMES_05 0x2
+#define BV_CLKCTRL_PLLCTRL0_CP_SEL__UNDEFINED 0x3
+#define BP_CLKCTRL_PLLCTRL0_DIV_SEL 20
+#define BM_CLKCTRL_PLLCTRL0_DIV_SEL 0x00300000
+#define BF_CLKCTRL_PLLCTRL0_DIV_SEL(v) \
+ (((v) << 20) & BM_CLKCTRL_PLLCTRL0_DIV_SEL)
+#define BV_CLKCTRL_PLLCTRL0_DIV_SEL__DEFAULT 0x0
+#define BV_CLKCTRL_PLLCTRL0_DIV_SEL__LOWER 0x1
+#define BV_CLKCTRL_PLLCTRL0_DIV_SEL__LOWEST 0x2
+#define BV_CLKCTRL_PLLCTRL0_DIV_SEL__UNDEFINED 0x3
+#define BM_CLKCTRL_PLLCTRL0_EN_USB_CLKS 0x00040000
+#define BM_CLKCTRL_PLLCTRL0_POWER 0x00010000
+HW_REGISTER_0(HW_CLKCTRL_PLLCTRL1, REGS_CLKCTRL_BASE, 0x00000010)
+#define HW_CLKCTRL_PLLCTRL1_ADDR (REGS_CLKCTRL_BASE + 0x00000010)
+#define BM_CLKCTRL_PLLCTRL1_LOCK 0x80000000
+#define BM_CLKCTRL_PLLCTRL1_FORCE_LOCK 0x40000000
+#define BP_CLKCTRL_PLLCTRL1_LOCK_COUNT 0
+#define BM_CLKCTRL_PLLCTRL1_LOCK_COUNT 0x0000FFFF
+#define BF_CLKCTRL_PLLCTRL1_LOCK_COUNT(v) \
+ (((v) << 0) & BM_CLKCTRL_PLLCTRL1_LOCK_COUNT)
+HW_REGISTER(HW_CLKCTRL_CPU, REGS_CLKCTRL_BASE, 0x00000020)
+#define HW_CLKCTRL_CPU_ADDR (REGS_CLKCTRL_BASE + 0x00000020)
+#define BM_CLKCTRL_CPU_BUSY_REF_XTAL 0x20000000
+#define BM_CLKCTRL_CPU_BUSY_REF_CPU 0x10000000
+#define BM_CLKCTRL_CPU_DIV_XTAL_FRAC_EN 0x04000000
+#define BP_CLKCTRL_CPU_DIV_XTAL 16
+#define BM_CLKCTRL_CPU_DIV_XTAL 0x03FF0000
+#define BF_CLKCTRL_CPU_DIV_XTAL(v) \
+ (((v) << 16) & BM_CLKCTRL_CPU_DIV_XTAL)
+#define BM_CLKCTRL_CPU_INTERRUPT_WAIT 0x00001000
+#define BM_CLKCTRL_CPU_DIV_CPU_FRAC_EN 0x00000400
+#define BP_CLKCTRL_CPU_DIV_CPU 0
+#define BM_CLKCTRL_CPU_DIV_CPU 0x0000003F
+#define BF_CLKCTRL_CPU_DIV_CPU(v) \
+ (((v) << 0) & BM_CLKCTRL_CPU_DIV_CPU)
+HW_REGISTER(HW_CLKCTRL_HBUS, REGS_CLKCTRL_BASE, 0x00000030)
+#define HW_CLKCTRL_HBUS_ADDR (REGS_CLKCTRL_BASE + 0x00000030)
+#define BM_CLKCTRL_HBUS_BUSY 0x20000000
+#define BM_CLKCTRL_HBUS_DCP_AS_ENABLE 0x10000000
+#define BM_CLKCTRL_HBUS_PXP_AS_ENABLE 0x08000000
+#define BM_CLKCTRL_HBUS_APBHDMA_AS_ENABLE 0x04000000
+#define BM_CLKCTRL_HBUS_APBXDMA_AS_ENABLE 0x02000000
+#define BM_CLKCTRL_HBUS_TRAFFIC_JAM_AS_ENABLE 0x01000000
+#define BM_CLKCTRL_HBUS_TRAFFIC_AS_ENABLE 0x00800000
+#define BM_CLKCTRL_HBUS_CPU_DATA_AS_ENABLE 0x00400000
+#define BM_CLKCTRL_HBUS_CPU_INSTR_AS_ENABLE 0x00200000
+#define BM_CLKCTRL_HBUS_AUTO_SLOW_MODE 0x00100000
+#define BP_CLKCTRL_HBUS_SLOW_DIV 16
+#define BM_CLKCTRL_HBUS_SLOW_DIV 0x00070000
+#define BF_CLKCTRL_HBUS_SLOW_DIV(v) \
+ (((v) << 16) & BM_CLKCTRL_HBUS_SLOW_DIV)
+#define BV_CLKCTRL_HBUS_SLOW_DIV__BY1 0x0
+#define BV_CLKCTRL_HBUS_SLOW_DIV__BY2 0x1
+#define BV_CLKCTRL_HBUS_SLOW_DIV__BY4 0x2
+#define BV_CLKCTRL_HBUS_SLOW_DIV__BY8 0x3
+#define BV_CLKCTRL_HBUS_SLOW_DIV__BY16 0x4
+#define BV_CLKCTRL_HBUS_SLOW_DIV__BY32 0x5
+#define BM_CLKCTRL_HBUS_DIV_FRAC_EN 0x00000020
+#define BP_CLKCTRL_HBUS_DIV 0
+#define BM_CLKCTRL_HBUS_DIV 0x0000001F
+#define BF_CLKCTRL_HBUS_DIV(v) \
+ (((v) << 0) & BM_CLKCTRL_HBUS_DIV)
+HW_REGISTER_0(HW_CLKCTRL_XBUS, REGS_CLKCTRL_BASE, 0x00000040)
+#define HW_CLKCTRL_XBUS_ADDR (REGS_CLKCTRL_BASE + 0x00000040)
+#define BM_CLKCTRL_XBUS_BUSY 0x80000000
+#define BM_CLKCTRL_XBUS_DIV_FRAC_EN 0x00000400
+#define BP_CLKCTRL_XBUS_DIV 0
+#define BM_CLKCTRL_XBUS_DIV 0x000003FF
+#define BF_CLKCTRL_XBUS_DIV(v) \
+ (((v) << 0) & BM_CLKCTRL_XBUS_DIV)
+HW_REGISTER(HW_CLKCTRL_XTAL, REGS_CLKCTRL_BASE, 0x00000050)
+#define HW_CLKCTRL_XTAL_ADDR (REGS_CLKCTRL_BASE + 0x00000050)
+#define BM_CLKCTRL_XTAL_UART_CLK_GATE 0x80000000
+#define BM_CLKCTRL_XTAL_FILT_CLK24M_GATE 0x40000000
+#define BM_CLKCTRL_XTAL_PWM_CLK24M_GATE 0x20000000
+#define BM_CLKCTRL_XTAL_DRI_CLK24M_GATE 0x10000000
+#define BM_CLKCTRL_XTAL_DIGCTRL_CLK1M_GATE 0x08000000
+#define BM_CLKCTRL_XTAL_TIMROT_CLK32K_GATE 0x04000000
+#define BP_CLKCTRL_XTAL_DIV_UART 0
+#define BM_CLKCTRL_XTAL_DIV_UART 0x00000003
+#define BF_CLKCTRL_XTAL_DIV_UART(v) \
+ (((v) << 0) & BM_CLKCTRL_XTAL_DIV_UART)
+HW_REGISTER_0(HW_CLKCTRL_PIX, REGS_CLKCTRL_BASE, 0x00000060)
+#define HW_CLKCTRL_PIX_ADDR (REGS_CLKCTRL_BASE + 0x00000060)
+#define BM_CLKCTRL_PIX_CLKGATE 0x80000000
+#define BM_CLKCTRL_PIX_BUSY 0x20000000
+#define BM_CLKCTRL_PIX_DIV_FRAC_EN 0x00001000
+#define BP_CLKCTRL_PIX_DIV 0
+#define BM_CLKCTRL_PIX_DIV 0x00000FFF
+#define BF_CLKCTRL_PIX_DIV(v) \
+ (((v) << 0) & BM_CLKCTRL_PIX_DIV)
+HW_REGISTER_0(HW_CLKCTRL_SSP, REGS_CLKCTRL_BASE, 0x00000070)
+#define HW_CLKCTRL_SSP_ADDR (REGS_CLKCTRL_BASE + 0x00000070)
+#define BM_CLKCTRL_SSP_CLKGATE 0x80000000
+#define BM_CLKCTRL_SSP_BUSY 0x20000000
+#define BM_CLKCTRL_SSP_DIV_FRAC_EN 0x00000200
+#define BP_CLKCTRL_SSP_DIV 0
+#define BM_CLKCTRL_SSP_DIV 0x000001FF
+#define BF_CLKCTRL_SSP_DIV(v) \
+ (((v) << 0) & BM_CLKCTRL_SSP_DIV)
+HW_REGISTER_0(HW_CLKCTRL_GPMI, REGS_CLKCTRL_BASE, 0x00000080)
+#define HW_CLKCTRL_GPMI_ADDR (REGS_CLKCTRL_BASE + 0x00000080)
+#define BM_CLKCTRL_GPMI_CLKGATE 0x80000000
+#define BM_CLKCTRL_GPMI_BUSY 0x20000000
+#define BM_CLKCTRL_GPMI_DIV_FRAC_EN 0x00000400
+#define BP_CLKCTRL_GPMI_DIV 0
+#define BM_CLKCTRL_GPMI_DIV 0x000003FF
+#define BF_CLKCTRL_GPMI_DIV(v) \
+ (((v) << 0) & BM_CLKCTRL_GPMI_DIV)
+HW_REGISTER_0(HW_CLKCTRL_SPDIF, REGS_CLKCTRL_BASE, 0x00000090)
+#define HW_CLKCTRL_SPDIF_ADDR (REGS_CLKCTRL_BASE + 0x00000090)
+#define BM_CLKCTRL_SPDIF_CLKGATE 0x80000000
+HW_REGISTER_0(HW_CLKCTRL_EMI, REGS_CLKCTRL_BASE, 0x000000a0)
+#define HW_CLKCTRL_EMI_ADDR (REGS_CLKCTRL_BASE + 0x000000a0)
+#define BM_CLKCTRL_EMI_CLKGATE 0x80000000
+#define BM_CLKCTRL_EMI_SYNC_MODE_EN 0x40000000
+#define BM_CLKCTRL_EMI_BUSY_REF_XTAL 0x20000000
+#define BM_CLKCTRL_EMI_BUSY_REF_EMI 0x10000000
+#define BM_CLKCTRL_EMI_BUSY_REF_CPU 0x08000000
+#define BM_CLKCTRL_EMI_BUSY_SYNC_MODE 0x04000000
+#define BM_CLKCTRL_EMI_BUSY_DCC_RESYNC 0x00020000
+#define BM_CLKCTRL_EMI_DCC_RESYNC_ENABLE 0x00010000
+#define BP_CLKCTRL_EMI_DIV_XTAL 8
+#define BM_CLKCTRL_EMI_DIV_XTAL 0x00000F00
+#define BF_CLKCTRL_EMI_DIV_XTAL(v) \
+ (((v) << 8) & BM_CLKCTRL_EMI_DIV_XTAL)
+#define BP_CLKCTRL_EMI_DIV_EMI 0
+#define BM_CLKCTRL_EMI_DIV_EMI 0x0000003F
+#define BF_CLKCTRL_EMI_DIV_EMI(v) \
+ (((v) << 0) & BM_CLKCTRL_EMI_DIV_EMI)
+HW_REGISTER_0(HW_CLKCTRL_IR, REGS_CLKCTRL_BASE, 0x000000b0)
+#define HW_CLKCTRL_IR_ADDR (REGS_CLKCTRL_BASE + 0x000000b0)
+#define BM_CLKCTRL_IR_CLKGATE 0x80000000
+#define BM_CLKCTRL_IR_AUTO_DIV 0x20000000
+#define BM_CLKCTRL_IR_IR_BUSY 0x10000000
+#define BM_CLKCTRL_IR_IROV_BUSY 0x08000000
+#define BP_CLKCTRL_IR_IROV_DIV 16
+#define BM_CLKCTRL_IR_IROV_DIV 0x01FF0000
+#define BF_CLKCTRL_IR_IROV_DIV(v) \
+ (((v) << 16) & BM_CLKCTRL_IR_IROV_DIV)
+#define BP_CLKCTRL_IR_IR_DIV 0
+#define BM_CLKCTRL_IR_IR_DIV 0x000003FF
+#define BF_CLKCTRL_IR_IR_DIV(v) \
+ (((v) << 0) & BM_CLKCTRL_IR_IR_DIV)
+HW_REGISTER_0(HW_CLKCTRL_SAIF, REGS_CLKCTRL_BASE, 0x000000c0)
+#define HW_CLKCTRL_SAIF_ADDR (REGS_CLKCTRL_BASE + 0x000000c0)
+#define BM_CLKCTRL_SAIF_CLKGATE 0x80000000
+#define BM_CLKCTRL_SAIF_BUSY 0x20000000
+#define BM_CLKCTRL_SAIF_DIV_FRAC_EN 0x00010000
+#define BP_CLKCTRL_SAIF_DIV 0
+#define BM_CLKCTRL_SAIF_DIV 0x0000FFFF
+#define BF_CLKCTRL_SAIF_DIV(v) \
+ (((v) << 0) & BM_CLKCTRL_SAIF_DIV)
+HW_REGISTER_0(HW_CLKCTRL_TV, REGS_CLKCTRL_BASE, 0x000000d0)
+#define HW_CLKCTRL_TV_ADDR (REGS_CLKCTRL_BASE + 0x000000d0)
+#define BM_CLKCTRL_TV_CLK_TV108M_GATE 0x80000000
+#define BM_CLKCTRL_TV_CLK_TV_GATE 0x40000000
+HW_REGISTER_0(HW_CLKCTRL_ETM, REGS_CLKCTRL_BASE, 0x000000e0)
+#define HW_CLKCTRL_ETM_ADDR (REGS_CLKCTRL_BASE + 0x000000e0)
+#define BM_CLKCTRL_ETM_CLKGATE 0x80000000
+#define BM_CLKCTRL_ETM_BUSY 0x20000000
+#define BM_CLKCTRL_ETM_DIV_FRAC_EN 0x00000040
+#define BP_CLKCTRL_ETM_DIV 0
+#define BM_CLKCTRL_ETM_DIV 0x0000003F
+#define BF_CLKCTRL_ETM_DIV(v) \
+ (((v) << 0) & BM_CLKCTRL_ETM_DIV)
+HW_REGISTER(HW_CLKCTRL_FRAC, REGS_CLKCTRL_BASE, 0x000000f0)
+#define HW_CLKCTRL_FRAC_ADDR (REGS_CLKCTRL_BASE + 0x000000f0)
+#define BM_CLKCTRL_FRAC_CLKGATEIO 0x80000000
+#define BM_CLKCTRL_FRAC_IO_STABLE 0x40000000
+#define BP_CLKCTRL_FRAC_IOFRAC 24
+#define BM_CLKCTRL_FRAC_IOFRAC 0x3F000000
+#define BF_CLKCTRL_FRAC_IOFRAC(v) \
+ (((v) << 24) & BM_CLKCTRL_FRAC_IOFRAC)
+#define BM_CLKCTRL_FRAC_CLKGATEPIX 0x00800000
+#define BM_CLKCTRL_FRAC_PIX_STABLE 0x00400000
+#define BP_CLKCTRL_FRAC_PIXFRAC 16
+#define BM_CLKCTRL_FRAC_PIXFRAC 0x003F0000
+#define BF_CLKCTRL_FRAC_PIXFRAC(v) \
+ (((v) << 16) & BM_CLKCTRL_FRAC_PIXFRAC)
+#define BM_CLKCTRL_FRAC_CLKGATEEMI 0x00008000
+#define BM_CLKCTRL_FRAC_EMI_STABLE 0x00004000
+#define BP_CLKCTRL_FRAC_EMIFRAC 8
+#define BM_CLKCTRL_FRAC_EMIFRAC 0x00003F00
+#define BF_CLKCTRL_FRAC_EMIFRAC(v) \
+ (((v) << 8) & BM_CLKCTRL_FRAC_EMIFRAC)
+#define BM_CLKCTRL_FRAC_CLKGATECPU 0x00000080
+#define BM_CLKCTRL_FRAC_CPU_STABLE 0x00000040
+#define BP_CLKCTRL_FRAC_CPUFRAC 0
+#define BM_CLKCTRL_FRAC_CPUFRAC 0x0000003F
+#define BF_CLKCTRL_FRAC_CPUFRAC(v) \
+ (((v) << 0) & BM_CLKCTRL_FRAC_CPUFRAC)
+HW_REGISTER(HW_CLKCTRL_FRAC1, REGS_CLKCTRL_BASE, 0x00000100)
+#define HW_CLKCTRL_FRAC1_ADDR (REGS_CLKCTRL_BASE + 0x00000100)
+#define BM_CLKCTRL_FRAC1_CLKGATEVID 0x80000000
+#define BM_CLKCTRL_FRAC1_VID_STABLE 0x40000000
+HW_REGISTER(HW_CLKCTRL_CLKSEQ, REGS_CLKCTRL_BASE, 0x00000110)
+#define HW_CLKCTRL_CLKSEQ_ADDR (REGS_CLKCTRL_BASE + 0x00000110)
+#define BM_CLKCTRL_CLKSEQ_BYPASS_ETM 0x00000100
+#define BM_CLKCTRL_CLKSEQ_BYPASS_CPU 0x00000080
+#define BM_CLKCTRL_CLKSEQ_BYPASS_EMI 0x00000040
+#define BM_CLKCTRL_CLKSEQ_BYPASS_SSP 0x00000020
+#define BM_CLKCTRL_CLKSEQ_BYPASS_GPMI 0x00000010
+#define BM_CLKCTRL_CLKSEQ_BYPASS_IR 0x00000008
+#define BM_CLKCTRL_CLKSEQ_BYPASS_PIX 0x00000002
+#define BM_CLKCTRL_CLKSEQ_BYPASS_SAIF 0x00000001
+HW_REGISTER_0(HW_CLKCTRL_RESET, REGS_CLKCTRL_BASE, 0x00000120)
+#define HW_CLKCTRL_RESET_ADDR (REGS_CLKCTRL_BASE + 0x00000120)
+#define BM_CLKCTRL_RESET_CHIP 0x00000002
+#define BM_CLKCTRL_RESET_DIG 0x00000001
+HW_REGISTER_0(HW_CLKCTRL_STATUS, REGS_CLKCTRL_BASE, 0x00000130)
+#define HW_CLKCTRL_STATUS_ADDR (REGS_CLKCTRL_BASE + 0x00000130)
+#define BP_CLKCTRL_STATUS_CPU_LIMIT 30
+#define BM_CLKCTRL_STATUS_CPU_LIMIT 0xC0000000
+#define BF_CLKCTRL_STATUS_CPU_LIMIT(v) \
+ (((v) << 30) & BM_CLKCTRL_STATUS_CPU_LIMIT)
+HW_REGISTER_0(HW_CLKCTRL_VERSION, REGS_CLKCTRL_BASE, 0x00000140)
+#define HW_CLKCTRL_VERSION_ADDR (REGS_CLKCTRL_BASE + 0x00000140)
+#define BP_CLKCTRL_VERSION_MAJOR 24
+#define BM_CLKCTRL_VERSION_MAJOR 0xFF000000
+#define BF_CLKCTRL_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_CLKCTRL_VERSION_MAJOR)
+#define BP_CLKCTRL_VERSION_MINOR 16
+#define BM_CLKCTRL_VERSION_MINOR 0x00FF0000
+#define BF_CLKCTRL_VERSION_MINOR(v) \
+ (((v) << 16) & BM_CLKCTRL_VERSION_MINOR)
+#define BP_CLKCTRL_VERSION_STEP 0
+#define BM_CLKCTRL_VERSION_STEP 0x0000FFFF
+#define BF_CLKCTRL_VERSION_STEP(v) \
+ (((v) << 0) & BM_CLKCTRL_VERSION_STEP)
+#endif /* __ARCH_ARM___CLKCTRL_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-dcp.h b/arch/arm/mach-stmp3xxx/include/mach/regs-dcp.h
new file mode 100644
index 000000000000..014fae206f74
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-dcp.h
@@ -0,0 +1,481 @@
+/*
+ * STMP DCP Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___DCP_H
+#define __ARCH_ARM___DCP_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_DCP_BASE (REGS_BASE + 0x28000)
+#define REGS_DCP_BASE_PHYS (0x80028000)
+#define REGS_DCP_SIZE 0x00002000
+HW_REGISTER(HW_DCP_CTRL, REGS_DCP_BASE, 0x00000000)
+#define HW_DCP_CTRL_ADDR (REGS_DCP_BASE + 0x00000000)
+#define BM_DCP_CTRL_SFTRST 0x80000000
+#define BM_DCP_CTRL_CLKGATE 0x40000000
+#define BM_DCP_CTRL_PRESENT_CRYPTO 0x20000000
+#define BV_DCP_CTRL_PRESENT_CRYPTO__Present 0x1
+#define BV_DCP_CTRL_PRESENT_CRYPTO__Absent 0x0
+#define BM_DCP_CTRL_PRESENT_CSC 0x10000000
+#define BV_DCP_CTRL_PRESENT_CSC__Present 0x1
+#define BV_DCP_CTRL_PRESENT_CSC__Absent 0x0
+#define BM_DCP_CTRL_GATHER_RESIDUAL_WRITES 0x00800000
+#define BM_DCP_CTRL_ENABLE_CONTEXT_CACHING 0x00400000
+#define BM_DCP_CTRL_ENABLE_CONTEXT_SWITCHING 0x00200000
+#define BM_DCP_CTRL_CSC_INTERRUPT_ENABLE 0x00000100
+#define BP_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE 0
+#define BM_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE 0x000000FF
+#define BF_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE(v) \
+ (((v) << 0) & BM_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE)
+#define BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH0 0x01
+#define BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH1 0x02
+#define BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH2 0x04
+#define BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH3 0x08
+HW_REGISTER(HW_DCP_STAT, REGS_DCP_BASE, 0x00000010)
+#define HW_DCP_STAT_ADDR (REGS_DCP_BASE + 0x00000010)
+#define BM_DCP_STAT_OTP_KEY_READY 0x10000000
+#define BP_DCP_STAT_CUR_CHANNEL 24
+#define BM_DCP_STAT_CUR_CHANNEL 0x0F000000
+#define BF_DCP_STAT_CUR_CHANNEL(v) \
+ (((v) << 24) & BM_DCP_STAT_CUR_CHANNEL)
+#define BV_DCP_STAT_CUR_CHANNEL__None 0x0
+#define BV_DCP_STAT_CUR_CHANNEL__CH0 0x1
+#define BV_DCP_STAT_CUR_CHANNEL__CH1 0x2
+#define BV_DCP_STAT_CUR_CHANNEL__CH2 0x3
+#define BV_DCP_STAT_CUR_CHANNEL__CH3 0x4
+#define BV_DCP_STAT_CUR_CHANNEL__CSC 0x8
+#define BP_DCP_STAT_READY_CHANNELS 16
+#define BM_DCP_STAT_READY_CHANNELS 0x00FF0000
+#define BF_DCP_STAT_READY_CHANNELS(v) \
+ (((v) << 16) & BM_DCP_STAT_READY_CHANNELS)
+#define BV_DCP_STAT_READY_CHANNELS__CH0 0x01
+#define BV_DCP_STAT_READY_CHANNELS__CH1 0x02
+#define BV_DCP_STAT_READY_CHANNELS__CH2 0x04
+#define BV_DCP_STAT_READY_CHANNELS__CH3 0x08
+#define BM_DCP_STAT_CSCIRQ 0x00000100
+#define BP_DCP_STAT_IRQ 0
+#define BM_DCP_STAT_IRQ 0x0000000F
+#define BF_DCP_STAT_IRQ(v) \
+ (((v) << 0) & BM_DCP_STAT_IRQ)
+HW_REGISTER(HW_DCP_CHANNELCTRL, REGS_DCP_BASE, 0x00000020)
+#define HW_DCP_CHANNELCTRL_ADDR (REGS_DCP_BASE + 0x00000020)
+#define BP_DCP_CHANNELCTRL_CSC_PRIORITY 17
+#define BM_DCP_CHANNELCTRL_CSC_PRIORITY 0x00060000
+#define BF_DCP_CHANNELCTRL_CSC_PRIORITY(v) \
+ (((v) << 17) & BM_DCP_CHANNELCTRL_CSC_PRIORITY)
+#define BV_DCP_CHANNELCTRL_CSC_PRIORITY__HIGH 0x3
+#define BV_DCP_CHANNELCTRL_CSC_PRIORITY__MED 0x2
+#define BV_DCP_CHANNELCTRL_CSC_PRIORITY__LOW 0x1
+#define BV_DCP_CHANNELCTRL_CSC_PRIORITY__BACKGROUND 0x0
+#define BM_DCP_CHANNELCTRL_CH0_IRQ_MERGED 0x00010000
+#define BP_DCP_CHANNELCTRL_HIGH_PRIORITY_CHANNEL 8
+#define BM_DCP_CHANNELCTRL_HIGH_PRIORITY_CHANNEL 0x0000FF00
+#define BF_DCP_CHANNELCTRL_HIGH_PRIORITY_CHANNEL(v) \
+ (((v) << 8) & BM_DCP_CHANNELCTRL_HIGH_PRIORITY_CHANNEL)
+#define BV_DCP_CHANNELCTRL_HIGH_PRIORITY_CHANNEL__CH0 0x01
+#define BV_DCP_CHANNELCTRL_HIGH_PRIORITY_CHANNEL__CH1 0x02
+#define BV_DCP_CHANNELCTRL_HIGH_PRIORITY_CHANNEL__CH2 0x04
+#define BV_DCP_CHANNELCTRL_HIGH_PRIORITY_CHANNEL__CH3 0x08
+#define BP_DCP_CHANNELCTRL_ENABLE_CHANNEL 0
+#define BM_DCP_CHANNELCTRL_ENABLE_CHANNEL 0x000000FF
+#define BF_DCP_CHANNELCTRL_ENABLE_CHANNEL(v) \
+ (((v) << 0) & BM_DCP_CHANNELCTRL_ENABLE_CHANNEL)
+#define BV_DCP_CHANNELCTRL_ENABLE_CHANNEL__CH0 0x01
+#define BV_DCP_CHANNELCTRL_ENABLE_CHANNEL__CH1 0x02
+#define BV_DCP_CHANNELCTRL_ENABLE_CHANNEL__CH2 0x04
+#define BV_DCP_CHANNELCTRL_ENABLE_CHANNEL__CH3 0x08
+HW_REGISTER_0(HW_DCP_CAPABILITY0, REGS_DCP_BASE, 0x00000030)
+#define HW_DCP_CAPABILITY0_ADDR (REGS_DCP_BASE + 0x00000030)
+#define BM_DCP_CAPABILITY0_DISABLE_DECRYPT 0x80000000
+#define BM_DCP_CAPABILITY0_ENABLE_TZONE 0x40000000
+#define BP_DCP_CAPABILITY0_NUM_CHANNELS 8
+#define BM_DCP_CAPABILITY0_NUM_CHANNELS 0x00000F00
+#define BF_DCP_CAPABILITY0_NUM_CHANNELS(v) \
+ (((v) << 8) & BM_DCP_CAPABILITY0_NUM_CHANNELS)
+#define BP_DCP_CAPABILITY0_NUM_KEYS 0
+#define BM_DCP_CAPABILITY0_NUM_KEYS 0x000000FF
+#define BF_DCP_CAPABILITY0_NUM_KEYS(v) \
+ (((v) << 0) & BM_DCP_CAPABILITY0_NUM_KEYS)
+HW_REGISTER_0(HW_DCP_CAPABILITY1, REGS_DCP_BASE, 0x00000040)
+#define HW_DCP_CAPABILITY1_ADDR (REGS_DCP_BASE + 0x00000040)
+#define BP_DCP_CAPABILITY1_HASH_ALGORITHMS 16
+#define BM_DCP_CAPABILITY1_HASH_ALGORITHMS 0xFFFF0000
+#define BF_DCP_CAPABILITY1_HASH_ALGORITHMS(v) \
+ (((v) << 16) & BM_DCP_CAPABILITY1_HASH_ALGORITHMS)
+#define BV_DCP_CAPABILITY1_HASH_ALGORITHMS__SHA1 0x0001
+#define BV_DCP_CAPABILITY1_HASH_ALGORITHMS__CRC32 0x0002
+#define BP_DCP_CAPABILITY1_CIPHER_ALGORITHMS 0
+#define BM_DCP_CAPABILITY1_CIPHER_ALGORITHMS 0x0000FFFF
+#define BF_DCP_CAPABILITY1_CIPHER_ALGORITHMS(v) \
+ (((v) << 0) & BM_DCP_CAPABILITY1_CIPHER_ALGORITHMS)
+#define BV_DCP_CAPABILITY1_CIPHER_ALGORITHMS__AES128 0x0001
+HW_REGISTER_0(HW_DCP_CONTEXT, REGS_DCP_BASE, 0x00000050)
+#define HW_DCP_CONTEXT_ADDR (REGS_DCP_BASE + 0x00000050)
+#define BP_DCP_CONTEXT_ADDR 0
+#define BM_DCP_CONTEXT_ADDR 0xFFFFFFFF
+#define BF_DCP_CONTEXT_ADDR(v) (v)
+HW_REGISTER_0(HW_DCP_KEY, REGS_DCP_BASE, 0x00000060)
+#define HW_DCP_KEY_ADDR (REGS_DCP_BASE + 0x00000060)
+#define BP_DCP_KEY_INDEX 4
+#define BM_DCP_KEY_INDEX 0x00000030
+#define BF_DCP_KEY_INDEX(v) \
+ (((v) << 4) & BM_DCP_KEY_INDEX)
+#define BP_DCP_KEY_SUBWORD 0
+#define BM_DCP_KEY_SUBWORD 0x00000003
+#define BF_DCP_KEY_SUBWORD(v) \
+ (((v) << 0) & BM_DCP_KEY_SUBWORD)
+HW_REGISTER_0(HW_DCP_KEYDATA, REGS_DCP_BASE, 0x00000070)
+#define HW_DCP_KEYDATA_ADDR (REGS_DCP_BASE + 0x00000070)
+#define BP_DCP_KEYDATA_DATA 0
+#define BM_DCP_KEYDATA_DATA 0xFFFFFFFF
+#define BF_DCP_KEYDATA_DATA(v) (v)
+HW_REGISTER_0(HW_DCP_PACKET0, REGS_DCP_BASE, 0x00000080)
+#define HW_DCP_PACKET0_ADDR (REGS_DCP_BASE + 0x00000080)
+#define BP_DCP_PACKET0_ADDR 0
+#define BM_DCP_PACKET0_ADDR 0xFFFFFFFF
+#define BF_DCP_PACKET0_ADDR(v) (v)
+HW_REGISTER_0(HW_DCP_PACKET1, REGS_DCP_BASE, 0x00000090)
+#define HW_DCP_PACKET1_ADDR (REGS_DCP_BASE + 0x00000090)
+#define BP_DCP_PACKET1_TAG 24
+#define BM_DCP_PACKET1_TAG 0xFF000000
+#define BF_DCP_PACKET1_TAG(v) \
+ (((v) << 24) & BM_DCP_PACKET1_TAG)
+#define BM_DCP_PACKET1_OUTPUT_WORDSWAP 0x00800000
+#define BM_DCP_PACKET1_OUTPUT_BYTESWAP 0x00400000
+#define BM_DCP_PACKET1_INPUT_WORDSWAP 0x00200000
+#define BM_DCP_PACKET1_INPUT_BYTESWAP 0x00100000
+#define BM_DCP_PACKET1_KEY_WORDSWAP 0x00080000
+#define BM_DCP_PACKET1_KEY_BYTESWAP 0x00040000
+#define BM_DCP_PACKET1_TEST_SEMA_IRQ 0x00020000
+#define BM_DCP_PACKET1_CONSTANT_FILL 0x00010000
+#define BM_DCP_PACKET1_HASH_OUTPUT 0x00008000
+#define BV_DCP_PACKET1_HASH_OUTPUT__INPUT 0x00
+#define BV_DCP_PACKET1_HASH_OUTPUT__OUTPUT 0x01
+#define BM_DCP_PACKET1_CHECK_HASH 0x00004000
+#define BM_DCP_PACKET1_HASH_TERM 0x00002000
+#define BM_DCP_PACKET1_HASH_INIT 0x00001000
+#define BM_DCP_PACKET1_PAYLOAD_KEY 0x00000800
+#define BM_DCP_PACKET1_OTP_KEY 0x00000400
+#define BM_DCP_PACKET1_CIPHER_INIT 0x00000200
+#define BM_DCP_PACKET1_CIPHER_ENCRYPT 0x00000100
+#define BV_DCP_PACKET1_CIPHER_ENCRYPT__ENCRYPT 0x01
+#define BV_DCP_PACKET1_CIPHER_ENCRYPT__DECRYPT 0x00
+#define BM_DCP_PACKET1_ENABLE_BLIT 0x00000080
+#define BM_DCP_PACKET1_ENABLE_HASH 0x00000040
+#define BM_DCP_PACKET1_ENABLE_CIPHER 0x00000020
+#define BM_DCP_PACKET1_ENABLE_MEMCOPY 0x00000010
+#define BM_DCP_PACKET1_CHAIN_CONTIGUOUS 0x00000008
+#define BM_DCP_PACKET1_CHAIN 0x00000004
+#define BM_DCP_PACKET1_DECR_SEMAPHORE 0x00000002
+#define BM_DCP_PACKET1_INTERRUPT 0x00000001
+HW_REGISTER_0(HW_DCP_PACKET2, REGS_DCP_BASE, 0x000000a0)
+#define HW_DCP_PACKET2_ADDR (REGS_DCP_BASE + 0x000000a0)
+#define BP_DCP_PACKET2_CIPHER_CFG 24
+#define BM_DCP_PACKET2_CIPHER_CFG 0xFF000000
+#define BF_DCP_PACKET2_CIPHER_CFG(v) \
+ (((v) << 24) & BM_DCP_PACKET2_CIPHER_CFG)
+#define BP_DCP_PACKET2_HASH_SELECT 16
+#define BM_DCP_PACKET2_HASH_SELECT 0x000F0000
+#define BF_DCP_PACKET2_HASH_SELECT(v) \
+ (((v) << 16) & BM_DCP_PACKET2_HASH_SELECT)
+#define BV_DCP_PACKET2_HASH_SELECT__SHA1 0x00
+#define BV_DCP_PACKET2_HASH_SELECT__CRC32 0x01
+#define BP_DCP_PACKET2_KEY_SELECT 8
+#define BM_DCP_PACKET2_KEY_SELECT 0x0000FF00
+#define BF_DCP_PACKET2_KEY_SELECT(v) \
+ (((v) << 8) & BM_DCP_PACKET2_KEY_SELECT)
+#define BP_DCP_PACKET2_CIPHER_MODE 4
+#define BM_DCP_PACKET2_CIPHER_MODE 0x000000F0
+#define BF_DCP_PACKET2_CIPHER_MODE(v) \
+ (((v) << 4) & BM_DCP_PACKET2_CIPHER_MODE)
+#define BV_DCP_PACKET2_CIPHER_MODE__ECB 0x00
+#define BV_DCP_PACKET2_CIPHER_MODE__CBC 0x01
+#define BP_DCP_PACKET2_CIPHER_SELECT 0
+#define BM_DCP_PACKET2_CIPHER_SELECT 0x0000000F
+#define BF_DCP_PACKET2_CIPHER_SELECT(v) \
+ (((v) << 0) & BM_DCP_PACKET2_CIPHER_SELECT)
+#define BV_DCP_PACKET2_CIPHER_SELECT__AES128 0x00
+HW_REGISTER_0(HW_DCP_PACKET3, REGS_DCP_BASE, 0x000000b0)
+#define HW_DCP_PACKET3_ADDR (REGS_DCP_BASE + 0x000000b0)
+#define BP_DCP_PACKET3_ADDR 0
+#define BM_DCP_PACKET3_ADDR 0xFFFFFFFF
+#define BF_DCP_PACKET3_ADDR(v) (v)
+HW_REGISTER_0(HW_DCP_PACKET4, REGS_DCP_BASE, 0x000000c0)
+#define HW_DCP_PACKET4_ADDR (REGS_DCP_BASE + 0x000000c0)
+#define BP_DCP_PACKET4_ADDR 0
+#define BM_DCP_PACKET4_ADDR 0xFFFFFFFF
+#define BF_DCP_PACKET4_ADDR(v) (v)
+HW_REGISTER_0(HW_DCP_PACKET5, REGS_DCP_BASE, 0x000000d0)
+#define HW_DCP_PACKET5_ADDR (REGS_DCP_BASE + 0x000000d0)
+#define BP_DCP_PACKET5_COUNT 0
+#define BM_DCP_PACKET5_COUNT 0xFFFFFFFF
+#define BF_DCP_PACKET5_COUNT(v) (v)
+HW_REGISTER_0(HW_DCP_PACKET6, REGS_DCP_BASE, 0x000000e0)
+#define HW_DCP_PACKET6_ADDR (REGS_DCP_BASE + 0x000000e0)
+#define BP_DCP_PACKET6_ADDR 0
+#define BM_DCP_PACKET6_ADDR 0xFFFFFFFF
+#define BF_DCP_PACKET6_ADDR(v) (v)
+/*
+ * multi-register-define name HW_DCP_CHnCMDPTR
+ * base 0x00000100
+ * count 4
+ * offset 0x40
+ */
+HW_REGISTER_0_INDEXED(HW_DCP_CHnCMDPTR, REGS_DCP_BASE, 0x00000100, 0x40)
+#define BP_DCP_CHnCMDPTR_ADDR 0
+#define BM_DCP_CHnCMDPTR_ADDR 0xFFFFFFFF
+#define BF_DCP_CHnCMDPTR_ADDR(v) (v)
+/*
+ * multi-register-define name HW_DCP_CHnSEMA
+ * base 0x00000110
+ * count 4
+ * offset 0x40
+ */
+HW_REGISTER_0_INDEXED(HW_DCP_CHnSEMA, REGS_DCP_BASE, 0x00000110, 0x40)
+#define BP_DCP_CHnSEMA_VALUE 16
+#define BM_DCP_CHnSEMA_VALUE 0x00FF0000
+#define BF_DCP_CHnSEMA_VALUE(v) \
+ (((v) << 16) & BM_DCP_CHnSEMA_VALUE)
+#define BP_DCP_CHnSEMA_INCREMENT 0
+#define BM_DCP_CHnSEMA_INCREMENT 0x000000FF
+#define BF_DCP_CHnSEMA_INCREMENT(v) \
+ (((v) << 0) & BM_DCP_CHnSEMA_INCREMENT)
+/*
+ * multi-register-define name HW_DCP_CHnSTAT
+ * base 0x00000120
+ * count 4
+ * offset 0x40
+ */
+HW_REGISTER_INDEXED(HW_DCP_CHnSTAT, REGS_DCP_BASE, 0x00000120, 0x40)
+#define BP_DCP_CHnSTAT_TAG 24
+#define BM_DCP_CHnSTAT_TAG 0xFF000000
+#define BF_DCP_CHnSTAT_TAG(v) \
+ (((v) << 24) & BM_DCP_CHnSTAT_TAG)
+#define BP_DCP_CHnSTAT_ERROR_CODE 16
+#define BM_DCP_CHnSTAT_ERROR_CODE 0x00FF0000
+#define BF_DCP_CHnSTAT_ERROR_CODE(v) \
+ (((v) << 16) & BM_DCP_CHnSTAT_ERROR_CODE)
+#define BV_DCP_CHnSTAT_ERROR_CODE__NEXT_CHAIN_IS_0 0x01
+#define BV_DCP_CHnSTAT_ERROR_CODE__NO_CHAIN 0x02
+#define BV_DCP_CHnSTAT_ERROR_CODE__CONTEXT_ERROR 0x03
+#define BV_DCP_CHnSTAT_ERROR_CODE__PAYLOAD_ERROR 0x04
+#define BV_DCP_CHnSTAT_ERROR_CODE__INVALID_MODE 0x05
+#define BM_DCP_CHnSTAT_ERROR_PAGEFAULT 0x00000040
+#define BM_DCP_CHnSTAT_ERROR_DST 0x00000020
+#define BM_DCP_CHnSTAT_ERROR_SRC 0x00000010
+#define BM_DCP_CHnSTAT_ERROR_PACKET 0x00000008
+#define BM_DCP_CHnSTAT_ERROR_SETUP 0x00000004
+#define BM_DCP_CHnSTAT_HASH_MISMATCH 0x00000002
+/*
+ * multi-register-define name HW_DCP_CHnOPTS
+ * base 0x00000130
+ * count 4
+ * offset 0x40
+ */
+HW_REGISTER_INDEXED(HW_DCP_CHnOPTS, REGS_DCP_BASE, 0x00000130, 0x40)
+#define BP_DCP_CHnOPTS_RECOVERY_TIMER 0
+#define BM_DCP_CHnOPTS_RECOVERY_TIMER 0x0000FFFF
+#define BF_DCP_CHnOPTS_RECOVERY_TIMER(v) \
+ (((v) << 0) & BM_DCP_CHnOPTS_RECOVERY_TIMER)
+HW_REGISTER(HW_DCP_CSCCTRL0, REGS_DCP_BASE, 0x00000300)
+#define HW_DCP_CSCCTRL0_ADDR (REGS_DCP_BASE + 0x00000300)
+#define BM_DCP_CSCCTRL0_CLIP 0x00008000
+#define BM_DCP_CSCCTRL0_UPSAMPLE 0x00004000
+#define BM_DCP_CSCCTRL0_SCALE 0x00002000
+#define BM_DCP_CSCCTRL0_ROTATE 0x00001000
+#define BM_DCP_CSCCTRL0_SUBSAMPLE 0x00000800
+#define BM_DCP_CSCCTRL0_DELTA 0x00000400
+#define BP_DCP_CSCCTRL0_RGB_FORMAT 8
+#define BM_DCP_CSCCTRL0_RGB_FORMAT 0x00000300
+#define BF_DCP_CSCCTRL0_RGB_FORMAT(v) \
+ (((v) << 8) & BM_DCP_CSCCTRL0_RGB_FORMAT)
+#define BV_DCP_CSCCTRL0_RGB_FORMAT__RGB16_565 0x0
+#define BV_DCP_CSCCTRL0_RGB_FORMAT__YCbCrI 0x1
+#define BV_DCP_CSCCTRL0_RGB_FORMAT__RGB24 0x2
+#define BV_DCP_CSCCTRL0_RGB_FORMAT__YUV422I 0x3
+#define BP_DCP_CSCCTRL0_YUV_FORMAT 4
+#define BM_DCP_CSCCTRL0_YUV_FORMAT 0x000000F0
+#define BF_DCP_CSCCTRL0_YUV_FORMAT(v) \
+ (((v) << 4) & BM_DCP_CSCCTRL0_YUV_FORMAT)
+#define BV_DCP_CSCCTRL0_YUV_FORMAT__YUV420 0x0
+#define BV_DCP_CSCCTRL0_YUV_FORMAT__YUV422 0x2
+#define BM_DCP_CSCCTRL0_ENABLE 0x00000001
+HW_REGISTER(HW_DCP_CSCSTAT, REGS_DCP_BASE, 0x00000310)
+#define HW_DCP_CSCSTAT_ADDR (REGS_DCP_BASE + 0x00000310)
+#define BP_DCP_CSCSTAT_ERROR_CODE 16
+#define BM_DCP_CSCSTAT_ERROR_CODE 0x00FF0000
+#define BF_DCP_CSCSTAT_ERROR_CODE(v) \
+ (((v) << 16) & BM_DCP_CSCSTAT_ERROR_CODE)
+#define BV_DCP_CSCSTAT_ERROR_CODE__LUMA0_FETCH_ERROR_Y0 0x01
+#define BV_DCP_CSCSTAT_ERROR_CODE__LUMA1_FETCH_ERROR_Y1 0x02
+#define BV_DCP_CSCSTAT_ERROR_CODE__CHROMA_FETCH_ERROR_U 0x03
+#define BV_DCP_CSCSTAT_ERROR_CODE__CHROMA_FETCH_ERROR_V 0x04
+#define BM_DCP_CSCSTAT_ERROR_PAGEFAULT 0x00000040
+#define BM_DCP_CSCSTAT_ERROR_DST 0x00000020
+#define BM_DCP_CSCSTAT_ERROR_SRC 0x00000010
+#define BM_DCP_CSCSTAT_ERROR_SETUP 0x00000004
+#define BM_DCP_CSCSTAT_COMPLETE 0x00000001
+HW_REGISTER_0(HW_DCP_CSCOUTBUFPARAM, REGS_DCP_BASE, 0x00000320)
+#define HW_DCP_CSCOUTBUFPARAM_ADDR (REGS_DCP_BASE + 0x00000320)
+#define BP_DCP_CSCOUTBUFPARAM_FIELD_SIZE 12
+#define BM_DCP_CSCOUTBUFPARAM_FIELD_SIZE 0x00FFF000
+#define BF_DCP_CSCOUTBUFPARAM_FIELD_SIZE(v) \
+ (((v) << 12) & BM_DCP_CSCOUTBUFPARAM_FIELD_SIZE)
+#define BP_DCP_CSCOUTBUFPARAM_LINE_SIZE 0
+#define BM_DCP_CSCOUTBUFPARAM_LINE_SIZE 0x00000FFF
+#define BF_DCP_CSCOUTBUFPARAM_LINE_SIZE(v) \
+ (((v) << 0) & BM_DCP_CSCOUTBUFPARAM_LINE_SIZE)
+HW_REGISTER_0(HW_DCP_CSCINBUFPARAM, REGS_DCP_BASE, 0x00000330)
+#define HW_DCP_CSCINBUFPARAM_ADDR (REGS_DCP_BASE + 0x00000330)
+#define BP_DCP_CSCINBUFPARAM_LINE_SIZE 0
+#define BM_DCP_CSCINBUFPARAM_LINE_SIZE 0x00000FFF
+#define BF_DCP_CSCINBUFPARAM_LINE_SIZE(v) \
+ (((v) << 0) & BM_DCP_CSCINBUFPARAM_LINE_SIZE)
+HW_REGISTER_0(HW_DCP_CSCRGB, REGS_DCP_BASE, 0x00000340)
+#define HW_DCP_CSCRGB_ADDR (REGS_DCP_BASE + 0x00000340)
+#define BP_DCP_CSCRGB_ADDR 0
+#define BM_DCP_CSCRGB_ADDR 0xFFFFFFFF
+#define BF_DCP_CSCRGB_ADDR(v) (v)
+HW_REGISTER_0(HW_DCP_CSCLUMA, REGS_DCP_BASE, 0x00000350)
+#define HW_DCP_CSCLUMA_ADDR (REGS_DCP_BASE + 0x00000350)
+#define BP_DCP_CSCLUMA_ADDR 0
+#define BM_DCP_CSCLUMA_ADDR 0xFFFFFFFF
+#define BF_DCP_CSCLUMA_ADDR(v) (v)
+HW_REGISTER_0(HW_DCP_CSCCHROMAU, REGS_DCP_BASE, 0x00000360)
+#define HW_DCP_CSCCHROMAU_ADDR (REGS_DCP_BASE + 0x00000360)
+#define BP_DCP_CSCCHROMAU_ADDR 0
+#define BM_DCP_CSCCHROMAU_ADDR 0xFFFFFFFF
+#define BF_DCP_CSCCHROMAU_ADDR(v) (v)
+HW_REGISTER_0(HW_DCP_CSCCHROMAV, REGS_DCP_BASE, 0x00000370)
+#define HW_DCP_CSCCHROMAV_ADDR (REGS_DCP_BASE + 0x00000370)
+#define BP_DCP_CSCCHROMAV_ADDR 0
+#define BM_DCP_CSCCHROMAV_ADDR 0xFFFFFFFF
+#define BF_DCP_CSCCHROMAV_ADDR(v) (v)
+HW_REGISTER_0(HW_DCP_CSCCOEFF0, REGS_DCP_BASE, 0x00000380)
+#define HW_DCP_CSCCOEFF0_ADDR (REGS_DCP_BASE + 0x00000380)
+#define BP_DCP_CSCCOEFF0_C0 16
+#define BM_DCP_CSCCOEFF0_C0 0x03FF0000
+#define BF_DCP_CSCCOEFF0_C0(v) \
+ (((v) << 16) & BM_DCP_CSCCOEFF0_C0)
+#define BP_DCP_CSCCOEFF0_UV_OFFSET 8
+#define BM_DCP_CSCCOEFF0_UV_OFFSET 0x0000FF00
+#define BF_DCP_CSCCOEFF0_UV_OFFSET(v) \
+ (((v) << 8) & BM_DCP_CSCCOEFF0_UV_OFFSET)
+#define BP_DCP_CSCCOEFF0_Y_OFFSET 0
+#define BM_DCP_CSCCOEFF0_Y_OFFSET 0x000000FF
+#define BF_DCP_CSCCOEFF0_Y_OFFSET(v) \
+ (((v) << 0) & BM_DCP_CSCCOEFF0_Y_OFFSET)
+HW_REGISTER_0(HW_DCP_CSCCOEFF1, REGS_DCP_BASE, 0x00000390)
+#define HW_DCP_CSCCOEFF1_ADDR (REGS_DCP_BASE + 0x00000390)
+#define BP_DCP_CSCCOEFF1_C1 16
+#define BM_DCP_CSCCOEFF1_C1 0x03FF0000
+#define BF_DCP_CSCCOEFF1_C1(v) \
+ (((v) << 16) & BM_DCP_CSCCOEFF1_C1)
+#define BP_DCP_CSCCOEFF1_C4 0
+#define BM_DCP_CSCCOEFF1_C4 0x000003FF
+#define BF_DCP_CSCCOEFF1_C4(v) \
+ (((v) << 0) & BM_DCP_CSCCOEFF1_C4)
+HW_REGISTER_0(HW_DCP_CSCCOEFF2, REGS_DCP_BASE, 0x000003a0)
+#define HW_DCP_CSCCOEFF2_ADDR (REGS_DCP_BASE + 0x000003a0)
+#define BP_DCP_CSCCOEFF2_C2 16
+#define BM_DCP_CSCCOEFF2_C2 0x03FF0000
+#define BF_DCP_CSCCOEFF2_C2(v) \
+ (((v) << 16) & BM_DCP_CSCCOEFF2_C2)
+#define BP_DCP_CSCCOEFF2_C3 0
+#define BM_DCP_CSCCOEFF2_C3 0x000003FF
+#define BF_DCP_CSCCOEFF2_C3(v) \
+ (((v) << 0) & BM_DCP_CSCCOEFF2_C3)
+HW_REGISTER_0(HW_DCP_CSCCLIP, REGS_DCP_BASE, 0x000003d0)
+#define HW_DCP_CSCCLIP_ADDR (REGS_DCP_BASE + 0x000003d0)
+#define BP_DCP_CSCCLIP_HEIGHT 12
+#define BM_DCP_CSCCLIP_HEIGHT 0x00FFF000
+#define BF_DCP_CSCCLIP_HEIGHT(v) \
+ (((v) << 12) & BM_DCP_CSCCLIP_HEIGHT)
+#define BP_DCP_CSCCLIP_WIDTH 0
+#define BM_DCP_CSCCLIP_WIDTH 0x00000FFF
+#define BF_DCP_CSCCLIP_WIDTH(v) \
+ (((v) << 0) & BM_DCP_CSCCLIP_WIDTH)
+HW_REGISTER_0(HW_DCP_CSCXSCALE, REGS_DCP_BASE, 0x000003e0)
+#define HW_DCP_CSCXSCALE_ADDR (REGS_DCP_BASE + 0x000003e0)
+#define BP_DCP_CSCXSCALE_INT 24
+#define BM_DCP_CSCXSCALE_INT 0x03000000
+#define BF_DCP_CSCXSCALE_INT(v) \
+ (((v) << 24) & BM_DCP_CSCXSCALE_INT)
+#define BP_DCP_CSCXSCALE_FRAC 12
+#define BM_DCP_CSCXSCALE_FRAC 0x00FFF000
+#define BF_DCP_CSCXSCALE_FRAC(v) \
+ (((v) << 12) & BM_DCP_CSCXSCALE_FRAC)
+#define BP_DCP_CSCXSCALE_WIDTH 0
+#define BM_DCP_CSCXSCALE_WIDTH 0x00000FFF
+#define BF_DCP_CSCXSCALE_WIDTH(v) \
+ (((v) << 0) & BM_DCP_CSCXSCALE_WIDTH)
+HW_REGISTER_0(HW_DCP_CSCYSCALE, REGS_DCP_BASE, 0x000003f0)
+#define HW_DCP_CSCYSCALE_ADDR (REGS_DCP_BASE + 0x000003f0)
+#define BP_DCP_CSCYSCALE_INT 24
+#define BM_DCP_CSCYSCALE_INT 0x03000000
+#define BF_DCP_CSCYSCALE_INT(v) \
+ (((v) << 24) & BM_DCP_CSCYSCALE_INT)
+#define BP_DCP_CSCYSCALE_FRAC 12
+#define BM_DCP_CSCYSCALE_FRAC 0x00FFF000
+#define BF_DCP_CSCYSCALE_FRAC(v) \
+ (((v) << 12) & BM_DCP_CSCYSCALE_FRAC)
+#define BP_DCP_CSCYSCALE_HEIGHT 0
+#define BM_DCP_CSCYSCALE_HEIGHT 0x00000FFF
+#define BF_DCP_CSCYSCALE_HEIGHT(v) \
+ (((v) << 0) & BM_DCP_CSCYSCALE_HEIGHT)
+HW_REGISTER_0(HW_DCP_DBGSELECT, REGS_DCP_BASE, 0x00000400)
+#define HW_DCP_DBGSELECT_ADDR (REGS_DCP_BASE + 0x00000400)
+#define BP_DCP_DBGSELECT_INDEX 0
+#define BM_DCP_DBGSELECT_INDEX 0x000000FF
+#define BF_DCP_DBGSELECT_INDEX(v) \
+ (((v) << 0) & BM_DCP_DBGSELECT_INDEX)
+#define BV_DCP_DBGSELECT_INDEX__CONTROL 0x01
+#define BV_DCP_DBGSELECT_INDEX__OTPKEY0 0x10
+#define BV_DCP_DBGSELECT_INDEX__OTPKEY1 0x11
+#define BV_DCP_DBGSELECT_INDEX__OTPKEY2 0x12
+#define BV_DCP_DBGSELECT_INDEX__OTPKEY3 0x13
+HW_REGISTER_0(HW_DCP_DBGDATA, REGS_DCP_BASE, 0x00000410)
+#define HW_DCP_DBGDATA_ADDR (REGS_DCP_BASE + 0x00000410)
+#define BP_DCP_DBGDATA_DATA 0
+#define BM_DCP_DBGDATA_DATA 0xFFFFFFFF
+#define BF_DCP_DBGDATA_DATA(v) (v)
+HW_REGISTER_0(HW_DCP_PAGETABLE, REGS_DCP_BASE, 0x00000420)
+#define HW_DCP_PAGETABLE_ADDR (REGS_DCP_BASE + 0x00000420)
+#define BP_DCP_PAGETABLE_BASE 2
+#define BM_DCP_PAGETABLE_BASE 0xFFFFFFFC
+#define BF_DCP_PAGETABLE_BASE(v) \
+ (((v) << 2) & BM_DCP_PAGETABLE_BASE)
+#define BM_DCP_PAGETABLE_FLUSH 0x00000002
+#define BM_DCP_PAGETABLE_ENABLE 0x00000001
+HW_REGISTER_0(HW_DCP_VERSION, REGS_DCP_BASE, 0x00000430)
+#define HW_DCP_VERSION_ADDR (REGS_DCP_BASE + 0x00000430)
+#define BP_DCP_VERSION_MAJOR 24
+#define BM_DCP_VERSION_MAJOR 0xFF000000
+#define BF_DCP_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_DCP_VERSION_MAJOR)
+#define BP_DCP_VERSION_MINOR 16
+#define BM_DCP_VERSION_MINOR 0x00FF0000
+#define BF_DCP_VERSION_MINOR(v) \
+ (((v) << 16) & BM_DCP_VERSION_MINOR)
+#define BP_DCP_VERSION_STEP 0
+#define BM_DCP_VERSION_STEP 0x0000FFFF
+#define BF_DCP_VERSION_STEP(v) \
+ (((v) << 0) & BM_DCP_VERSION_STEP)
+#endif /* __ARCH_ARM___DCP_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-digctl.h b/arch/arm/mach-stmp3xxx/include/mach/regs-digctl.h
new file mode 100644
index 000000000000..287eb949ff7f
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-digctl.h
@@ -0,0 +1,451 @@
+/*
+ * STMP DIGCTL Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___DIGCTL_H
+#define __ARCH_ARM___DIGCTL_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_DIGCTL_BASE (REGS_BASE + 0x1c000)
+#define REGS_DIGCTL_BASE_PHYS (0x8001C000)
+#define REGS_DIGCTL_SIZE 0x00002000
+HW_REGISTER(HW_DIGCTL_CTRL, REGS_DIGCTL_BASE, 0x00000000)
+#define HW_DIGCTL_CTRL_ADDR (REGS_DIGCTL_BASE + 0x00000000)
+#define BM_DIGCTL_CTRL_XTAL24M_GATE 0x40000000
+#define BM_DIGCTL_CTRL_TRAP_IRQ 0x20000000
+#define BM_DIGCTL_CTRL_CACHE_BIST_TMODE 0x04000000
+#define BM_DIGCTL_CTRL_LCD_BIST_CLKEN 0x02000000
+#define BM_DIGCTL_CTRL_LCD_BIST_START 0x01000000
+#define BM_DIGCTL_CTRL_DCP_BIST_CLKEN 0x00800000
+#define BM_DIGCTL_CTRL_DCP_BIST_START 0x00400000
+#define BM_DIGCTL_CTRL_ARM_BIST_CLKEN 0x00200000
+#define BM_DIGCTL_CTRL_USB_TESTMODE 0x00100000
+#define BM_DIGCTL_CTRL_ANALOG_TESTMODE 0x00080000
+#define BM_DIGCTL_CTRL_DIGITAL_TESTMODE 0x00040000
+#define BM_DIGCTL_CTRL_ARM_BIST_START 0x00020000
+#define BM_DIGCTL_CTRL_UART_LOOPBACK 0x00010000
+#define BV_DIGCTL_CTRL_UART_LOOPBACK__NORMAL 0x0
+#define BV_DIGCTL_CTRL_UART_LOOPBACK__LOOPIT 0x1
+#define BM_DIGCTL_CTRL_SAIF_LOOPBACK 0x00008000
+#define BV_DIGCTL_CTRL_SAIF_LOOPBACK__NORMAL 0x0
+#define BV_DIGCTL_CTRL_SAIF_LOOPBACK__LOOPIT 0x1
+#define BP_DIGCTL_CTRL_SAIF_CLKMUX_SEL 13
+#define BM_DIGCTL_CTRL_SAIF_CLKMUX_SEL 0x00006000
+#define BF_DIGCTL_CTRL_SAIF_CLKMUX_SEL(v) \
+ (((v) << 13) & BM_DIGCTL_CTRL_SAIF_CLKMUX_SEL)
+#define BV_DIGCTL_CTRL_SAIF_CLKMUX_SEL__MBL_CLK_OUT 0x0
+#define BV_DIGCTL_CTRL_SAIF_CLKMUX_SEL__BL_CLK_OUT 0x1
+#define BV_DIGCTL_CTRL_SAIF_CLKMUX_SEL__M_CLK_OUT_BL_CLK_IN 0x2
+#define BV_DIGCTL_CTRL_SAIF_CLKMUX_SEL__BL_CLK_IN 0x3
+#define BM_DIGCTL_CTRL_SAIF_CLKMST_SEL 0x00001000
+#define BV_DIGCTL_CTRL_SAIF_CLKMST_SEL__SAIF1_MST 0x0
+#define BV_DIGCTL_CTRL_SAIF_CLKMST_SEL__SAIF2_MST 0x1
+#define BM_DIGCTL_CTRL_SAIF_ALT_BITCLK_SEL 0x00000800
+#define BM_DIGCTL_CTRL_SY_ENDIAN 0x00000200
+#define BM_DIGCTL_CTRL_SY_SFTRST 0x00000100
+#define BM_DIGCTL_CTRL_SY_CLKGATE 0x00000080
+#define BM_DIGCTL_CTRL_USE_SERIAL_JTAG 0x00000040
+#define BV_DIGCTL_CTRL_USE_SERIAL_JTAG__OLD_JTAG 0x0
+#define BV_DIGCTL_CTRL_USE_SERIAL_JTAG__SERIAL_JTAG 0x1
+#define BM_DIGCTL_CTRL_TRAP_IN_RANGE 0x00000020
+#define BM_DIGCTL_CTRL_TRAP_ENABLE 0x00000010
+#define BM_DIGCTL_CTRL_DEBUG_DISABLE 0x00000008
+#define BM_DIGCTL_CTRL_USB_CLKGATE 0x00000004
+#define BV_DIGCTL_CTRL_USB_CLKGATE__RUN 0x0
+#define BV_DIGCTL_CTRL_USB_CLKGATE__NO_CLKS 0x1
+#define BM_DIGCTL_CTRL_JTAG_SHIELD 0x00000002
+#define BV_DIGCTL_CTRL_JTAG_SHIELD__NORMAL 0x0
+#define BV_DIGCTL_CTRL_JTAG_SHIELD__SHIELDS_UP 0x1
+#define BM_DIGCTL_CTRL_LATCH_ENTROPY 0x00000001
+HW_REGISTER(HW_DIGCTL_STATUS, REGS_DIGCTL_BASE, 0x00000010)
+#define HW_DIGCTL_STATUS_ADDR (REGS_DIGCTL_BASE + 0x00000010)
+#define BM_DIGCTL_STATUS_USB_HS_PRESENT 0x80000000
+#define BM_DIGCTL_STATUS_USB_OTG_PRESENT 0x40000000
+#define BM_DIGCTL_STATUS_USB_HOST_PRESENT 0x20000000
+#define BM_DIGCTL_STATUS_USB_DEVICE_PRESENT 0x10000000
+#define BM_DIGCTL_STATUS_DCP_BIST_FAIL 0x00000400
+#define BM_DIGCTL_STATUS_DCP_BIST_PASS 0x00000200
+#define BM_DIGCTL_STATUS_DCP_BIST_DONE 0x00000100
+#define BM_DIGCTL_STATUS_LCD_BIST_FAIL 0x00000080
+#define BM_DIGCTL_STATUS_LCD_BIST_PASS 0x00000040
+#define BM_DIGCTL_STATUS_LCD_BIST_DONE 0x00000020
+#define BM_DIGCTL_STATUS_JTAG_IN_USE 0x00000010
+#define BP_DIGCTL_STATUS_PACKAGE_TYPE 1
+#define BM_DIGCTL_STATUS_PACKAGE_TYPE 0x0000000E
+#define BF_DIGCTL_STATUS_PACKAGE_TYPE(v) \
+ (((v) << 1) & BM_DIGCTL_STATUS_PACKAGE_TYPE)
+#define BM_DIGCTL_STATUS_WRITTEN 0x00000001
+HW_REGISTER(HW_DIGCTL_HCLKCOUNT, REGS_DIGCTL_BASE, 0x00000020)
+#define HW_DIGCTL_HCLKCOUNT_ADDR (REGS_DIGCTL_BASE + 0x00000020)
+#define BP_DIGCTL_HCLKCOUNT_COUNT 0
+#define BM_DIGCTL_HCLKCOUNT_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_HCLKCOUNT_COUNT(v) (v)
+HW_REGISTER(HW_DIGCTL_RAMCTRL, REGS_DIGCTL_BASE, 0x00000030)
+#define HW_DIGCTL_RAMCTRL_ADDR (REGS_DIGCTL_BASE + 0x00000030)
+#define BP_DIGCTL_RAMCTRL_SPEED_SELECT 8
+#define BM_DIGCTL_RAMCTRL_SPEED_SELECT 0x00000F00
+#define BF_DIGCTL_RAMCTRL_SPEED_SELECT(v) \
+ (((v) << 8) & BM_DIGCTL_RAMCTRL_SPEED_SELECT)
+#define BM_DIGCTL_RAMCTRL_RAM_REPAIR_EN 0x00000001
+HW_REGISTER(HW_DIGCTL_RAMREPAIR, REGS_DIGCTL_BASE, 0x00000040)
+#define HW_DIGCTL_RAMREPAIR_ADDR (REGS_DIGCTL_BASE + 0x00000040)
+#define BP_DIGCTL_RAMREPAIR_ADDR 0
+#define BM_DIGCTL_RAMREPAIR_ADDR 0x0000FFFF
+#define BF_DIGCTL_RAMREPAIR_ADDR(v) \
+ (((v) << 0) & BM_DIGCTL_RAMREPAIR_ADDR)
+HW_REGISTER(HW_DIGCTL_ROMCTRL, REGS_DIGCTL_BASE, 0x00000050)
+#define HW_DIGCTL_ROMCTRL_ADDR (REGS_DIGCTL_BASE + 0x00000050)
+#define BP_DIGCTL_ROMCTRL_RD_MARGIN 0
+#define BM_DIGCTL_ROMCTRL_RD_MARGIN 0x0000000F
+#define BF_DIGCTL_ROMCTRL_RD_MARGIN(v) \
+ (((v) << 0) & BM_DIGCTL_ROMCTRL_RD_MARGIN)
+HW_REGISTER_0(HW_DIGCTL_WRITEONCE, REGS_DIGCTL_BASE, 0x00000060)
+#define HW_DIGCTL_WRITEONCE_ADDR (REGS_DIGCTL_BASE + 0x00000060)
+#define BP_DIGCTL_WRITEONCE_BITS 0
+#define BM_DIGCTL_WRITEONCE_BITS 0xFFFFFFFF
+#define BF_DIGCTL_WRITEONCE_BITS(v) (v)
+HW_REGISTER_0(HW_DIGCTL_ENTROPY, REGS_DIGCTL_BASE, 0x00000090)
+#define HW_DIGCTL_ENTROPY_ADDR (REGS_DIGCTL_BASE + 0x00000090)
+#define BP_DIGCTL_ENTROPY_VALUE 0
+#define BM_DIGCTL_ENTROPY_VALUE 0xFFFFFFFF
+#define BF_DIGCTL_ENTROPY_VALUE(v) (v)
+HW_REGISTER_0(HW_DIGCTL_ENTROPY_LATCHED, REGS_DIGCTL_BASE, 0x000000a0)
+#define HW_DIGCTL_ENTROPY_LATCHED_ADDR (REGS_DIGCTL_BASE + 0x000000a0)
+#define BP_DIGCTL_ENTROPY_LATCHED_VALUE 0
+#define BM_DIGCTL_ENTROPY_LATCHED_VALUE 0xFFFFFFFF
+#define BF_DIGCTL_ENTROPY_LATCHED_VALUE(v) (v)
+HW_REGISTER(HW_DIGCTL_SJTAGDBG, REGS_DIGCTL_BASE, 0x000000b0)
+#define HW_DIGCTL_SJTAGDBG_ADDR (REGS_DIGCTL_BASE + 0x000000b0)
+#define BP_DIGCTL_SJTAGDBG_SJTAG_STATE 16
+#define BM_DIGCTL_SJTAGDBG_SJTAG_STATE 0x07FF0000
+#define BF_DIGCTL_SJTAGDBG_SJTAG_STATE(v) \
+ (((v) << 16) & BM_DIGCTL_SJTAGDBG_SJTAG_STATE)
+#define BM_DIGCTL_SJTAGDBG_SJTAG_TDO 0x00000400
+#define BM_DIGCTL_SJTAGDBG_SJTAG_TDI 0x00000200
+#define BM_DIGCTL_SJTAGDBG_SJTAG_MODE 0x00000100
+#define BP_DIGCTL_SJTAGDBG_DELAYED_ACTIVE 4
+#define BM_DIGCTL_SJTAGDBG_DELAYED_ACTIVE 0x000000F0
+#define BF_DIGCTL_SJTAGDBG_DELAYED_ACTIVE(v) \
+ (((v) << 4) & BM_DIGCTL_SJTAGDBG_DELAYED_ACTIVE)
+#define BM_DIGCTL_SJTAGDBG_ACTIVE 0x00000008
+#define BM_DIGCTL_SJTAGDBG_SJTAG_PIN_STATE 0x00000004
+#define BM_DIGCTL_SJTAGDBG_SJTAG_DEBUG_DATA 0x00000002
+#define BM_DIGCTL_SJTAGDBG_SJTAG_DEBUG_OE 0x00000001
+HW_REGISTER(HW_DIGCTL_MICROSECONDS, REGS_DIGCTL_BASE, 0x000000c0)
+#define HW_DIGCTL_MICROSECONDS_ADDR (REGS_DIGCTL_BASE + 0x000000c0)
+#define BP_DIGCTL_MICROSECONDS_VALUE 0
+#define BM_DIGCTL_MICROSECONDS_VALUE 0xFFFFFFFF
+#define BF_DIGCTL_MICROSECONDS_VALUE(v) (v)
+HW_REGISTER_0(HW_DIGCTL_DBGRD, REGS_DIGCTL_BASE, 0x000000d0)
+#define HW_DIGCTL_DBGRD_ADDR (REGS_DIGCTL_BASE + 0x000000d0)
+#define BP_DIGCTL_DBGRD_COMPLEMENT 0
+#define BM_DIGCTL_DBGRD_COMPLEMENT 0xFFFFFFFF
+#define BF_DIGCTL_DBGRD_COMPLEMENT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_DBG, REGS_DIGCTL_BASE, 0x000000e0)
+#define HW_DIGCTL_DBG_ADDR (REGS_DIGCTL_BASE + 0x000000e0)
+#define BP_DIGCTL_DBG_VALUE 0
+#define BM_DIGCTL_DBG_VALUE 0xFFFFFFFF
+#define BF_DIGCTL_DBG_VALUE(v) (v)
+HW_REGISTER(HW_DIGCTL_OCRAM_BIST_CSR, REGS_DIGCTL_BASE, 0x000000f0)
+#define HW_DIGCTL_OCRAM_BIST_CSR_ADDR (REGS_DIGCTL_BASE + 0x000000f0)
+#define BM_DIGCTL_OCRAM_BIST_CSR_BIST_DEBUG_MODE 0x00000400
+#define BM_DIGCTL_OCRAM_BIST_CSR_BIST_DATA_CHANGE 0x00000200
+#define BM_DIGCTL_OCRAM_BIST_CSR_BIST_CLKEN 0x00000100
+#define BM_DIGCTL_OCRAM_BIST_CSR_FAIL 0x00000008
+#define BM_DIGCTL_OCRAM_BIST_CSR_PASS 0x00000004
+#define BM_DIGCTL_OCRAM_BIST_CSR_DONE 0x00000002
+#define BM_DIGCTL_OCRAM_BIST_CSR_START 0x00000001
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS0, REGS_DIGCTL_BASE, 0x00000110)
+#define HW_DIGCTL_OCRAM_STATUS0_ADDR (REGS_DIGCTL_BASE + 0x00000110)
+#define BP_DIGCTL_OCRAM_STATUS0_FAILDATA00 0
+#define BM_DIGCTL_OCRAM_STATUS0_FAILDATA00 0xFFFFFFFF
+#define BF_DIGCTL_OCRAM_STATUS0_FAILDATA00(v) (v)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS1, REGS_DIGCTL_BASE, 0x00000120)
+#define HW_DIGCTL_OCRAM_STATUS1_ADDR (REGS_DIGCTL_BASE + 0x00000120)
+#define BP_DIGCTL_OCRAM_STATUS1_FAILDATA01 0
+#define BM_DIGCTL_OCRAM_STATUS1_FAILDATA01 0xFFFFFFFF
+#define BF_DIGCTL_OCRAM_STATUS1_FAILDATA01(v) (v)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS2, REGS_DIGCTL_BASE, 0x00000130)
+#define HW_DIGCTL_OCRAM_STATUS2_ADDR (REGS_DIGCTL_BASE + 0x00000130)
+#define BP_DIGCTL_OCRAM_STATUS2_FAILDATA10 0
+#define BM_DIGCTL_OCRAM_STATUS2_FAILDATA10 0xFFFFFFFF
+#define BF_DIGCTL_OCRAM_STATUS2_FAILDATA10(v) (v)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS3, REGS_DIGCTL_BASE, 0x00000140)
+#define HW_DIGCTL_OCRAM_STATUS3_ADDR (REGS_DIGCTL_BASE + 0x00000140)
+#define BP_DIGCTL_OCRAM_STATUS3_FAILDATA11 0
+#define BM_DIGCTL_OCRAM_STATUS3_FAILDATA11 0xFFFFFFFF
+#define BF_DIGCTL_OCRAM_STATUS3_FAILDATA11(v) (v)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS4, REGS_DIGCTL_BASE, 0x00000150)
+#define HW_DIGCTL_OCRAM_STATUS4_ADDR (REGS_DIGCTL_BASE + 0x00000150)
+#define BP_DIGCTL_OCRAM_STATUS4_FAILDATA20 0
+#define BM_DIGCTL_OCRAM_STATUS4_FAILDATA20 0xFFFFFFFF
+#define BF_DIGCTL_OCRAM_STATUS4_FAILDATA20(v) (v)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS5, REGS_DIGCTL_BASE, 0x00000160)
+#define HW_DIGCTL_OCRAM_STATUS5_ADDR (REGS_DIGCTL_BASE + 0x00000160)
+#define BP_DIGCTL_OCRAM_STATUS5_FAILDATA21 0
+#define BM_DIGCTL_OCRAM_STATUS5_FAILDATA21 0xFFFFFFFF
+#define BF_DIGCTL_OCRAM_STATUS5_FAILDATA21(v) (v)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS6, REGS_DIGCTL_BASE, 0x00000170)
+#define HW_DIGCTL_OCRAM_STATUS6_ADDR (REGS_DIGCTL_BASE + 0x00000170)
+#define BP_DIGCTL_OCRAM_STATUS6_FAILDATA30 0
+#define BM_DIGCTL_OCRAM_STATUS6_FAILDATA30 0xFFFFFFFF
+#define BF_DIGCTL_OCRAM_STATUS6_FAILDATA30(v) (v)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS7, REGS_DIGCTL_BASE, 0x00000180)
+#define HW_DIGCTL_OCRAM_STATUS7_ADDR (REGS_DIGCTL_BASE + 0x00000180)
+#define BP_DIGCTL_OCRAM_STATUS7_FAILDATA31 0
+#define BM_DIGCTL_OCRAM_STATUS7_FAILDATA31 0xFFFFFFFF
+#define BF_DIGCTL_OCRAM_STATUS7_FAILDATA31(v) (v)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS8, REGS_DIGCTL_BASE, 0x00000190)
+#define HW_DIGCTL_OCRAM_STATUS8_ADDR (REGS_DIGCTL_BASE + 0x00000190)
+#define BP_DIGCTL_OCRAM_STATUS8_FAILADDR01 16
+#define BM_DIGCTL_OCRAM_STATUS8_FAILADDR01 0x1FFF0000
+#define BF_DIGCTL_OCRAM_STATUS8_FAILADDR01(v) \
+ (((v) << 16) & BM_DIGCTL_OCRAM_STATUS8_FAILADDR01)
+#define BP_DIGCTL_OCRAM_STATUS8_FAILADDR00 0
+#define BM_DIGCTL_OCRAM_STATUS8_FAILADDR00 0x00001FFF
+#define BF_DIGCTL_OCRAM_STATUS8_FAILADDR00(v) \
+ (((v) << 0) & BM_DIGCTL_OCRAM_STATUS8_FAILADDR00)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS9, REGS_DIGCTL_BASE, 0x000001a0)
+#define HW_DIGCTL_OCRAM_STATUS9_ADDR (REGS_DIGCTL_BASE + 0x000001a0)
+#define BP_DIGCTL_OCRAM_STATUS9_FAILADDR11 16
+#define BM_DIGCTL_OCRAM_STATUS9_FAILADDR11 0x1FFF0000
+#define BF_DIGCTL_OCRAM_STATUS9_FAILADDR11(v) \
+ (((v) << 16) & BM_DIGCTL_OCRAM_STATUS9_FAILADDR11)
+#define BP_DIGCTL_OCRAM_STATUS9_FAILADDR10 0
+#define BM_DIGCTL_OCRAM_STATUS9_FAILADDR10 0x00001FFF
+#define BF_DIGCTL_OCRAM_STATUS9_FAILADDR10(v) \
+ (((v) << 0) & BM_DIGCTL_OCRAM_STATUS9_FAILADDR10)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS10, REGS_DIGCTL_BASE, 0x000001b0)
+#define HW_DIGCTL_OCRAM_STATUS10_ADDR (REGS_DIGCTL_BASE + 0x000001b0)
+#define BP_DIGCTL_OCRAM_STATUS10_FAILADDR21 16
+#define BM_DIGCTL_OCRAM_STATUS10_FAILADDR21 0x1FFF0000
+#define BF_DIGCTL_OCRAM_STATUS10_FAILADDR21(v) \
+ (((v) << 16) & BM_DIGCTL_OCRAM_STATUS10_FAILADDR21)
+#define BP_DIGCTL_OCRAM_STATUS10_FAILADDR20 0
+#define BM_DIGCTL_OCRAM_STATUS10_FAILADDR20 0x00001FFF
+#define BF_DIGCTL_OCRAM_STATUS10_FAILADDR20(v) \
+ (((v) << 0) & BM_DIGCTL_OCRAM_STATUS10_FAILADDR20)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS11, REGS_DIGCTL_BASE, 0x000001c0)
+#define HW_DIGCTL_OCRAM_STATUS11_ADDR (REGS_DIGCTL_BASE + 0x000001c0)
+#define BP_DIGCTL_OCRAM_STATUS11_FAILADDR31 16
+#define BM_DIGCTL_OCRAM_STATUS11_FAILADDR31 0x1FFF0000
+#define BF_DIGCTL_OCRAM_STATUS11_FAILADDR31(v) \
+ (((v) << 16) & BM_DIGCTL_OCRAM_STATUS11_FAILADDR31)
+#define BP_DIGCTL_OCRAM_STATUS11_FAILADDR30 0
+#define BM_DIGCTL_OCRAM_STATUS11_FAILADDR30 0x00001FFF
+#define BF_DIGCTL_OCRAM_STATUS11_FAILADDR30(v) \
+ (((v) << 0) & BM_DIGCTL_OCRAM_STATUS11_FAILADDR30)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS12, REGS_DIGCTL_BASE, 0x000001d0)
+#define HW_DIGCTL_OCRAM_STATUS12_ADDR (REGS_DIGCTL_BASE + 0x000001d0)
+#define BP_DIGCTL_OCRAM_STATUS12_FAILSTATE11 24
+#define BM_DIGCTL_OCRAM_STATUS12_FAILSTATE11 0x0F000000
+#define BF_DIGCTL_OCRAM_STATUS12_FAILSTATE11(v) \
+ (((v) << 24) & BM_DIGCTL_OCRAM_STATUS12_FAILSTATE11)
+#define BP_DIGCTL_OCRAM_STATUS12_FAILSTATE10 16
+#define BM_DIGCTL_OCRAM_STATUS12_FAILSTATE10 0x000F0000
+#define BF_DIGCTL_OCRAM_STATUS12_FAILSTATE10(v) \
+ (((v) << 16) & BM_DIGCTL_OCRAM_STATUS12_FAILSTATE10)
+#define BP_DIGCTL_OCRAM_STATUS12_FAILSTATE01 8
+#define BM_DIGCTL_OCRAM_STATUS12_FAILSTATE01 0x00000F00
+#define BF_DIGCTL_OCRAM_STATUS12_FAILSTATE01(v) \
+ (((v) << 8) & BM_DIGCTL_OCRAM_STATUS12_FAILSTATE01)
+#define BP_DIGCTL_OCRAM_STATUS12_FAILSTATE00 0
+#define BM_DIGCTL_OCRAM_STATUS12_FAILSTATE00 0x0000000F
+#define BF_DIGCTL_OCRAM_STATUS12_FAILSTATE00(v) \
+ (((v) << 0) & BM_DIGCTL_OCRAM_STATUS12_FAILSTATE00)
+HW_REGISTER(HW_DIGCTL_OCRAM_STATUS13, REGS_DIGCTL_BASE, 0x000001e0)
+#define HW_DIGCTL_OCRAM_STATUS13_ADDR (REGS_DIGCTL_BASE + 0x000001e0)
+#define BP_DIGCTL_OCRAM_STATUS13_FAILSTATE31 24
+#define BM_DIGCTL_OCRAM_STATUS13_FAILSTATE31 0x0F000000
+#define BF_DIGCTL_OCRAM_STATUS13_FAILSTATE31(v) \
+ (((v) << 24) & BM_DIGCTL_OCRAM_STATUS13_FAILSTATE31)
+#define BP_DIGCTL_OCRAM_STATUS13_FAILSTATE30 16
+#define BM_DIGCTL_OCRAM_STATUS13_FAILSTATE30 0x000F0000
+#define BF_DIGCTL_OCRAM_STATUS13_FAILSTATE30(v) \
+ (((v) << 16) & BM_DIGCTL_OCRAM_STATUS13_FAILSTATE30)
+#define BP_DIGCTL_OCRAM_STATUS13_FAILSTATE21 8
+#define BM_DIGCTL_OCRAM_STATUS13_FAILSTATE21 0x00000F00
+#define BF_DIGCTL_OCRAM_STATUS13_FAILSTATE21(v) \
+ (((v) << 8) & BM_DIGCTL_OCRAM_STATUS13_FAILSTATE21)
+#define BP_DIGCTL_OCRAM_STATUS13_FAILSTATE20 0
+#define BM_DIGCTL_OCRAM_STATUS13_FAILSTATE20 0x0000000F
+#define BF_DIGCTL_OCRAM_STATUS13_FAILSTATE20(v) \
+ (((v) << 0) & BM_DIGCTL_OCRAM_STATUS13_FAILSTATE20)
+HW_REGISTER_0(HW_DIGCTL_SCRATCH0, REGS_DIGCTL_BASE, 0x00000290)
+#define HW_DIGCTL_SCRATCH0_ADDR (REGS_DIGCTL_BASE + 0x00000290)
+#define BP_DIGCTL_SCRATCH0_PTR 0
+#define BM_DIGCTL_SCRATCH0_PTR 0xFFFFFFFF
+#define BF_DIGCTL_SCRATCH0_PTR(v) (v)
+HW_REGISTER_0(HW_DIGCTL_SCRATCH1, REGS_DIGCTL_BASE, 0x000002a0)
+#define HW_DIGCTL_SCRATCH1_ADDR (REGS_DIGCTL_BASE + 0x000002a0)
+#define BP_DIGCTL_SCRATCH1_PTR 0
+#define BM_DIGCTL_SCRATCH1_PTR 0xFFFFFFFF
+#define BF_DIGCTL_SCRATCH1_PTR(v) (v)
+HW_REGISTER_0(HW_DIGCTL_ARMCACHE, REGS_DIGCTL_BASE, 0x000002b0)
+#define HW_DIGCTL_ARMCACHE_ADDR (REGS_DIGCTL_BASE + 0x000002b0)
+#define BP_DIGCTL_ARMCACHE_VALID_SS 16
+#define BM_DIGCTL_ARMCACHE_VALID_SS 0x00030000
+#define BF_DIGCTL_ARMCACHE_VALID_SS(v) \
+ (((v) << 16) & BM_DIGCTL_ARMCACHE_VALID_SS)
+#define BP_DIGCTL_ARMCACHE_DRTY_SS 12
+#define BM_DIGCTL_ARMCACHE_DRTY_SS 0x00003000
+#define BF_DIGCTL_ARMCACHE_DRTY_SS(v) \
+ (((v) << 12) & BM_DIGCTL_ARMCACHE_DRTY_SS)
+#define BP_DIGCTL_ARMCACHE_CACHE_SS 8
+#define BM_DIGCTL_ARMCACHE_CACHE_SS 0x00000300
+#define BF_DIGCTL_ARMCACHE_CACHE_SS(v) \
+ (((v) << 8) & BM_DIGCTL_ARMCACHE_CACHE_SS)
+#define BP_DIGCTL_ARMCACHE_DTAG_SS 4
+#define BM_DIGCTL_ARMCACHE_DTAG_SS 0x00000030
+#define BF_DIGCTL_ARMCACHE_DTAG_SS(v) \
+ (((v) << 4) & BM_DIGCTL_ARMCACHE_DTAG_SS)
+#define BP_DIGCTL_ARMCACHE_ITAG_SS 0
+#define BM_DIGCTL_ARMCACHE_ITAG_SS 0x00000003
+#define BF_DIGCTL_ARMCACHE_ITAG_SS(v) \
+ (((v) << 0) & BM_DIGCTL_ARMCACHE_ITAG_SS)
+HW_REGISTER_0(HW_DIGCTL_DEBUG_TRAP_ADDR_LOW, REGS_DIGCTL_BASE, 0x000002c0)
+#define HW_DIGCTL_DEBUG_TRAP_ADDR_LOW_ADDR (REGS_DIGCTL_BASE + 0x000002c0)
+#define BP_DIGCTL_DEBUG_TRAP_ADDR_LOW_ADDR 0
+#define BM_DIGCTL_DEBUG_TRAP_ADDR_LOW_ADDR 0xFFFFFFFF
+#define BF_DIGCTL_DEBUG_TRAP_ADDR_LOW_ADDR(v) (v)
+HW_REGISTER_0(HW_DIGCTL_DEBUG_TRAP_ADDR_HIGH, REGS_DIGCTL_BASE, 0x000002d0)
+#define HW_DIGCTL_DEBUG_TRAP_ADDR_HIGH_ADDR (REGS_DIGCTL_BASE + 0x000002d0)
+#define BP_DIGCTL_DEBUG_TRAP_ADDR_HIGH_ADDR 0
+#define BM_DIGCTL_DEBUG_TRAP_ADDR_HIGH_ADDR 0xFFFFFFFF
+#define BF_DIGCTL_DEBUG_TRAP_ADDR_HIGH_ADDR(v) (v)
+HW_REGISTER_0(HW_DIGCTL_SGTL, REGS_DIGCTL_BASE, 0x00000300)
+#define HW_DIGCTL_SGTL_ADDR (REGS_DIGCTL_BASE + 0x00000300)
+#define BP_DIGCTL_SGTL_COPYRIGHT 0
+#define BM_DIGCTL_SGTL_COPYRIGHT 0xFFFFFFFF
+#define BF_DIGCTL_SGTL_COPYRIGHT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_CHIPID, REGS_DIGCTL_BASE, 0x00000310)
+#define HW_DIGCTL_CHIPID_ADDR (REGS_DIGCTL_BASE + 0x00000310)
+#define BP_DIGCTL_CHIPID_PRODUCT_CODE 16
+#define BM_DIGCTL_CHIPID_PRODUCT_CODE 0xFFFF0000
+#define BF_DIGCTL_CHIPID_PRODUCT_CODE(v) \
+ (((v) << 16) & BM_DIGCTL_CHIPID_PRODUCT_CODE)
+#define BP_DIGCTL_CHIPID_REVISION 0
+#define BM_DIGCTL_CHIPID_REVISION 0x000000FF
+#define BF_DIGCTL_CHIPID_REVISION(v) \
+ (((v) << 0) & BM_DIGCTL_CHIPID_REVISION)
+HW_REGISTER_0(HW_DIGCTL_AHB_STATS_SELECT, REGS_DIGCTL_BASE, 0x00000330)
+#define HW_DIGCTL_AHB_STATS_SELECT_ADDR (REGS_DIGCTL_BASE + 0x00000330)
+#define BP_DIGCTL_AHB_STATS_SELECT_L3_MASTER_SELECT 24
+#define BM_DIGCTL_AHB_STATS_SELECT_L3_MASTER_SELECT 0x0F000000
+#define BF_DIGCTL_AHB_STATS_SELECT_L3_MASTER_SELECT(v) \
+ (((v) << 24) & BM_DIGCTL_AHB_STATS_SELECT_L3_MASTER_SELECT)
+#define BV_DIGCTL_AHB_STATS_SELECT_L3_MASTER_SELECT__APBH 0x1
+#define BV_DIGCTL_AHB_STATS_SELECT_L3_MASTER_SELECT__APBX 0x2
+#define BV_DIGCTL_AHB_STATS_SELECT_L3_MASTER_SELECT__USB 0x4
+#define BP_DIGCTL_AHB_STATS_SELECT_L2_MASTER_SELECT 16
+#define BM_DIGCTL_AHB_STATS_SELECT_L2_MASTER_SELECT 0x000F0000
+#define BF_DIGCTL_AHB_STATS_SELECT_L2_MASTER_SELECT(v) \
+ (((v) << 16) & BM_DIGCTL_AHB_STATS_SELECT_L2_MASTER_SELECT)
+#define BV_DIGCTL_AHB_STATS_SELECT_L2_MASTER_SELECT__ARM_D 0x1
+#define BP_DIGCTL_AHB_STATS_SELECT_L1_MASTER_SELECT 8
+#define BM_DIGCTL_AHB_STATS_SELECT_L1_MASTER_SELECT 0x00000F00
+#define BF_DIGCTL_AHB_STATS_SELECT_L1_MASTER_SELECT(v) \
+ (((v) << 8) & BM_DIGCTL_AHB_STATS_SELECT_L1_MASTER_SELECT)
+#define BV_DIGCTL_AHB_STATS_SELECT_L1_MASTER_SELECT__ARM_I 0x1
+#define BP_DIGCTL_AHB_STATS_SELECT_L0_MASTER_SELECT 0
+#define BM_DIGCTL_AHB_STATS_SELECT_L0_MASTER_SELECT 0x0000000F
+#define BF_DIGCTL_AHB_STATS_SELECT_L0_MASTER_SELECT(v) \
+ (((v) << 0) & BM_DIGCTL_AHB_STATS_SELECT_L0_MASTER_SELECT)
+#define BV_DIGCTL_AHB_STATS_SELECT_L0_MASTER_SELECT__ECC8 0x1
+#define BV_DIGCTL_AHB_STATS_SELECT_L0_MASTER_SELECT__CRYPTO 0x2
+HW_REGISTER_0(HW_DIGCTL_L0_AHB_ACTIVE_CYCLES, REGS_DIGCTL_BASE, 0x00000340)
+#define HW_DIGCTL_L0_AHB_ACTIVE_CYCLES_ADDR (REGS_DIGCTL_BASE + 0x00000340)
+#define BP_DIGCTL_L0_AHB_ACTIVE_CYCLES_COUNT 0
+#define BM_DIGCTL_L0_AHB_ACTIVE_CYCLES_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L0_AHB_ACTIVE_CYCLES_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L0_AHB_DATA_STALLED, REGS_DIGCTL_BASE, 0x00000350)
+#define HW_DIGCTL_L0_AHB_DATA_STALLED_ADDR (REGS_DIGCTL_BASE + 0x00000350)
+#define BP_DIGCTL_L0_AHB_DATA_STALLED_COUNT 0
+#define BM_DIGCTL_L0_AHB_DATA_STALLED_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L0_AHB_DATA_STALLED_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L0_AHB_DATA_CYCLES, REGS_DIGCTL_BASE, 0x00000360)
+#define HW_DIGCTL_L0_AHB_DATA_CYCLES_ADDR (REGS_DIGCTL_BASE + 0x00000360)
+#define BP_DIGCTL_L0_AHB_DATA_CYCLES_COUNT 0
+#define BM_DIGCTL_L0_AHB_DATA_CYCLES_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L0_AHB_DATA_CYCLES_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L1_AHB_ACTIVE_CYCLES, REGS_DIGCTL_BASE, 0x00000370)
+#define HW_DIGCTL_L1_AHB_ACTIVE_CYCLES_ADDR (REGS_DIGCTL_BASE + 0x00000370)
+#define BP_DIGCTL_L1_AHB_ACTIVE_CYCLES_COUNT 0
+#define BM_DIGCTL_L1_AHB_ACTIVE_CYCLES_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L1_AHB_ACTIVE_CYCLES_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L1_AHB_DATA_STALLED, REGS_DIGCTL_BASE, 0x00000380)
+#define HW_DIGCTL_L1_AHB_DATA_STALLED_ADDR (REGS_DIGCTL_BASE + 0x00000380)
+#define BP_DIGCTL_L1_AHB_DATA_STALLED_COUNT 0
+#define BM_DIGCTL_L1_AHB_DATA_STALLED_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L1_AHB_DATA_STALLED_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L1_AHB_DATA_CYCLES, REGS_DIGCTL_BASE, 0x00000390)
+#define HW_DIGCTL_L1_AHB_DATA_CYCLES_ADDR (REGS_DIGCTL_BASE + 0x00000390)
+#define BP_DIGCTL_L1_AHB_DATA_CYCLES_COUNT 0
+#define BM_DIGCTL_L1_AHB_DATA_CYCLES_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L1_AHB_DATA_CYCLES_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L2_AHB_ACTIVE_CYCLES, REGS_DIGCTL_BASE, 0x000003a0)
+#define HW_DIGCTL_L2_AHB_ACTIVE_CYCLES_ADDR (REGS_DIGCTL_BASE + 0x000003a0)
+#define BP_DIGCTL_L2_AHB_ACTIVE_CYCLES_COUNT 0
+#define BM_DIGCTL_L2_AHB_ACTIVE_CYCLES_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L2_AHB_ACTIVE_CYCLES_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L2_AHB_DATA_STALLED, REGS_DIGCTL_BASE, 0x000003b0)
+#define HW_DIGCTL_L2_AHB_DATA_STALLED_ADDR (REGS_DIGCTL_BASE + 0x000003b0)
+#define BP_DIGCTL_L2_AHB_DATA_STALLED_COUNT 0
+#define BM_DIGCTL_L2_AHB_DATA_STALLED_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L2_AHB_DATA_STALLED_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L2_AHB_DATA_CYCLES, REGS_DIGCTL_BASE, 0x000003c0)
+#define HW_DIGCTL_L2_AHB_DATA_CYCLES_ADDR (REGS_DIGCTL_BASE + 0x000003c0)
+#define BP_DIGCTL_L2_AHB_DATA_CYCLES_COUNT 0
+#define BM_DIGCTL_L2_AHB_DATA_CYCLES_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L2_AHB_DATA_CYCLES_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L3_AHB_ACTIVE_CYCLES, REGS_DIGCTL_BASE, 0x000003d0)
+#define HW_DIGCTL_L3_AHB_ACTIVE_CYCLES_ADDR (REGS_DIGCTL_BASE + 0x000003d0)
+#define BP_DIGCTL_L3_AHB_ACTIVE_CYCLES_COUNT 0
+#define BM_DIGCTL_L3_AHB_ACTIVE_CYCLES_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L3_AHB_ACTIVE_CYCLES_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L3_AHB_DATA_STALLED, REGS_DIGCTL_BASE, 0x000003e0)
+#define HW_DIGCTL_L3_AHB_DATA_STALLED_ADDR (REGS_DIGCTL_BASE + 0x000003e0)
+#define BP_DIGCTL_L3_AHB_DATA_STALLED_COUNT 0
+#define BM_DIGCTL_L3_AHB_DATA_STALLED_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L3_AHB_DATA_STALLED_COUNT(v) (v)
+HW_REGISTER_0(HW_DIGCTL_L3_AHB_DATA_CYCLES, REGS_DIGCTL_BASE, 0x000003f0)
+#define HW_DIGCTL_L3_AHB_DATA_CYCLES_ADDR (REGS_DIGCTL_BASE + 0x000003f0)
+#define BP_DIGCTL_L3_AHB_DATA_CYCLES_COUNT 0
+#define BM_DIGCTL_L3_AHB_DATA_CYCLES_COUNT 0xFFFFFFFF
+#define BF_DIGCTL_L3_AHB_DATA_CYCLES_COUNT(v) (v)
+/*
+ * multi-register-define name HW_DIGCTL_MPTEn_LOC
+ * base 0x00000400
+ * count 16
+ * offset 0x10
+ */
+HW_REGISTER_0_INDEXED(HW_DIGCTL_MPTEn_LOC, REGS_DIGCTL_BASE, 0x00000400,
+ 0x10)
+#define BP_DIGCTL_MPTEn_LOC_LOC 0
+#define BM_DIGCTL_MPTEn_LOC_LOC 0x00000FFF
+#define BF_DIGCTL_MPTEn_LOC_LOC(v) \
+ (((v) << 0) & BM_DIGCTL_MPTEn_LOC_LOC)
+HW_REGISTER_0(HW_DIGCTL_EMICLK_DELAY, REGS_DIGCTL_BASE, 0x00000500)
+#define HW_DIGCTL_EMICLK_DELAY_ADDR (REGS_DIGCTL_BASE + 0x00000500)
+#define BP_DIGCTL_EMICLK_DELAY_NUM_TAPS 0
+#define BM_DIGCTL_EMICLK_DELAY_NUM_TAPS 0x0000001F
+#define BF_DIGCTL_EMICLK_DELAY_NUM_TAPS(v) \
+ (((v) << 0) & BM_DIGCTL_EMICLK_DELAY_NUM_TAPS)
+#endif /* __ARCH_ARM___DIGCTL_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-dram.h b/arch/arm/mach-stmp3xxx/include/mach/regs-dram.h
new file mode 100644
index 000000000000..059b2edd0db0
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-dram.h
@@ -0,0 +1,454 @@
+/*
+ * STMP DRAM Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___DRAM_H
+#define __ARCH_ARM___DRAM_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_DRAM_BASE (REGS_BASE + 0xe0000)
+#define REGS_DRAM_BASE_PHYS (0x800E0000)
+#define REGS_DRAM_SIZE 0x00002000
+HW_REGISTER_0(HW_DRAM_CTL00, REGS_DRAM_BASE, 0x00000000)
+#define HW_DRAM_CTL00_ADDR (REGS_DRAM_BASE + 0x00000000)
+#define BM_DRAM_CTL00_AHB0_W_PRIORITY 0x01000000
+#define BM_DRAM_CTL00_AHB0_R_PRIORITY 0x00010000
+#define BM_DRAM_CTL00_AHB0_FIFO_TYPE_REG 0x00000100
+#define BM_DRAM_CTL00_ADDR_CMP_EN 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL01, REGS_DRAM_BASE, 0x00000004)
+#define HW_DRAM_CTL01_ADDR (REGS_DRAM_BASE + 0x00000004)
+#define BM_DRAM_CTL01_AHB2_FIFO_TYPE_REG 0x01000000
+#define BM_DRAM_CTL01_AHB1_W_PRIORITY 0x00010000
+#define BM_DRAM_CTL01_AHB1_R_PRIORITY 0x00000100
+#define BM_DRAM_CTL01_AHB1_FIFO_TYPE_REG 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL02, REGS_DRAM_BASE, 0x00000008)
+#define HW_DRAM_CTL02_ADDR (REGS_DRAM_BASE + 0x00000008)
+#define BM_DRAM_CTL02_AHB3_R_PRIORITY 0x01000000
+#define BM_DRAM_CTL02_AHB3_FIFO_TYPE_REG 0x00010000
+#define BM_DRAM_CTL02_AHB2_W_PRIORITY 0x00000100
+#define BM_DRAM_CTL02_AHB2_R_PRIORITY 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL03, REGS_DRAM_BASE, 0x0000000c)
+#define HW_DRAM_CTL03_ADDR (REGS_DRAM_BASE + 0x0000000c)
+#define BM_DRAM_CTL03_AUTO_REFRESH_MODE 0x01000000
+#define BM_DRAM_CTL03_AREFRESH 0x00010000
+#define BM_DRAM_CTL03_AP 0x00000100
+#define BM_DRAM_CTL03_AHB3_W_PRIORITY 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL04, REGS_DRAM_BASE, 0x00000010)
+#define HW_DRAM_CTL04_ADDR (REGS_DRAM_BASE + 0x00000010)
+#define BM_DRAM_CTL04_DLL_BYPASS_MODE 0x01000000
+#define BM_DRAM_CTL04_DLLLOCKREG 0x00010000
+#define BM_DRAM_CTL04_CONCURRENTAP 0x00000100
+#define BM_DRAM_CTL04_BANK_SPLIT_EN 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL05, REGS_DRAM_BASE, 0x00000014)
+#define HW_DRAM_CTL05_ADDR (REGS_DRAM_BASE + 0x00000014)
+#define BM_DRAM_CTL05_INTRPTREADA 0x01000000
+#define BM_DRAM_CTL05_INTRPTAPBURST 0x00010000
+#define BM_DRAM_CTL05_FAST_WRITE 0x00000100
+#define BM_DRAM_CTL05_EN_LOWPOWER_MODE 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL06, REGS_DRAM_BASE, 0x00000018)
+#define HW_DRAM_CTL06_ADDR (REGS_DRAM_BASE + 0x00000018)
+#define BM_DRAM_CTL06_POWER_DOWN 0x01000000
+#define BM_DRAM_CTL06_PLACEMENT_EN 0x00010000
+#define BM_DRAM_CTL06_NO_CMD_INIT 0x00000100
+#define BM_DRAM_CTL06_INTRPTWRITEA 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL07, REGS_DRAM_BASE, 0x0000001c)
+#define HW_DRAM_CTL07_ADDR (REGS_DRAM_BASE + 0x0000001c)
+#define BM_DRAM_CTL07_RW_SAME_EN 0x01000000
+#define BM_DRAM_CTL07_REG_DIMM_ENABLE 0x00010000
+#define BM_DRAM_CTL07_RD2RD_TURN 0x00000100
+#define BM_DRAM_CTL07_PRIORITY_EN 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL08, REGS_DRAM_BASE, 0x00000020)
+#define HW_DRAM_CTL08_ADDR (REGS_DRAM_BASE + 0x00000020)
+#define BM_DRAM_CTL08_TRAS_LOCKOUT 0x01000000
+#define BM_DRAM_CTL08_START 0x00010000
+#define BM_DRAM_CTL08_SREFRESH 0x00000100
+#define BM_DRAM_CTL08_SDR_MODE 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL09, REGS_DRAM_BASE, 0x00000024)
+#define HW_DRAM_CTL09_ADDR (REGS_DRAM_BASE + 0x00000024)
+#define BP_DRAM_CTL09_OUT_OF_RANGE_TYPE 24
+#define BM_DRAM_CTL09_OUT_OF_RANGE_TYPE 0x03000000
+#define BF_DRAM_CTL09_OUT_OF_RANGE_TYPE(v) \
+ (((v) << 24) & BM_DRAM_CTL09_OUT_OF_RANGE_TYPE)
+#define BP_DRAM_CTL09_OUT_OF_RANGE_SOURCE_ID 16
+#define BM_DRAM_CTL09_OUT_OF_RANGE_SOURCE_ID 0x00030000
+#define BF_DRAM_CTL09_OUT_OF_RANGE_SOURCE_ID(v) \
+ (((v) << 16) & BM_DRAM_CTL09_OUT_OF_RANGE_SOURCE_ID)
+#define BM_DRAM_CTL09_WRITE_MODEREG 0x00000100
+#define BM_DRAM_CTL09_WRITEINTERP 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL10, REGS_DRAM_BASE, 0x00000028)
+#define HW_DRAM_CTL10_ADDR (REGS_DRAM_BASE + 0x00000028)
+#define BP_DRAM_CTL10_AGE_COUNT 24
+#define BM_DRAM_CTL10_AGE_COUNT 0x07000000
+#define BF_DRAM_CTL10_AGE_COUNT(v) \
+ (((v) << 24) & BM_DRAM_CTL10_AGE_COUNT)
+#define BP_DRAM_CTL10_ADDR_PINS 16
+#define BM_DRAM_CTL10_ADDR_PINS 0x00070000
+#define BF_DRAM_CTL10_ADDR_PINS(v) \
+ (((v) << 16) & BM_DRAM_CTL10_ADDR_PINS)
+#define BP_DRAM_CTL10_TEMRS 8
+#define BM_DRAM_CTL10_TEMRS 0x00000300
+#define BF_DRAM_CTL10_TEMRS(v) \
+ (((v) << 8) & BM_DRAM_CTL10_TEMRS)
+#define BP_DRAM_CTL10_Q_FULLNESS 0
+#define BM_DRAM_CTL10_Q_FULLNESS 0x00000003
+#define BF_DRAM_CTL10_Q_FULLNESS(v) \
+ (((v) << 0) & BM_DRAM_CTL10_Q_FULLNESS)
+HW_REGISTER_0(HW_DRAM_CTL11, REGS_DRAM_BASE, 0x0000002c)
+#define HW_DRAM_CTL11_ADDR (REGS_DRAM_BASE + 0x0000002c)
+#define BP_DRAM_CTL11_MAX_CS_REG 24
+#define BM_DRAM_CTL11_MAX_CS_REG 0x07000000
+#define BF_DRAM_CTL11_MAX_CS_REG(v) \
+ (((v) << 24) & BM_DRAM_CTL11_MAX_CS_REG)
+#define BP_DRAM_CTL11_COMMAND_AGE_COUNT 16
+#define BM_DRAM_CTL11_COMMAND_AGE_COUNT 0x00070000
+#define BF_DRAM_CTL11_COMMAND_AGE_COUNT(v) \
+ (((v) << 16) & BM_DRAM_CTL11_COMMAND_AGE_COUNT)
+#define BP_DRAM_CTL11_COLUMN_SIZE 8
+#define BM_DRAM_CTL11_COLUMN_SIZE 0x00000700
+#define BF_DRAM_CTL11_COLUMN_SIZE(v) \
+ (((v) << 8) & BM_DRAM_CTL11_COLUMN_SIZE)
+#define BP_DRAM_CTL11_CASLAT 0
+#define BM_DRAM_CTL11_CASLAT 0x00000007
+#define BF_DRAM_CTL11_CASLAT(v) \
+ (((v) << 0) & BM_DRAM_CTL11_CASLAT)
+HW_REGISTER_0(HW_DRAM_CTL12, REGS_DRAM_BASE, 0x00000030)
+#define HW_DRAM_CTL12_ADDR (REGS_DRAM_BASE + 0x00000030)
+#define BP_DRAM_CTL12_TWR_INT 24
+#define BM_DRAM_CTL12_TWR_INT 0x07000000
+#define BF_DRAM_CTL12_TWR_INT(v) \
+ (((v) << 24) & BM_DRAM_CTL12_TWR_INT)
+#define BP_DRAM_CTL12_TRRD 16
+#define BM_DRAM_CTL12_TRRD 0x00070000
+#define BF_DRAM_CTL12_TRRD(v) \
+ (((v) << 16) & BM_DRAM_CTL12_TRRD)
+#define BP_DRAM_CTL12_TCKE 0
+#define BM_DRAM_CTL12_TCKE 0x00000007
+#define BF_DRAM_CTL12_TCKE(v) \
+ (((v) << 0) & BM_DRAM_CTL12_TCKE)
+HW_REGISTER_0(HW_DRAM_CTL13, REGS_DRAM_BASE, 0x00000034)
+#define HW_DRAM_CTL13_ADDR (REGS_DRAM_BASE + 0x00000034)
+#define BP_DRAM_CTL13_CASLAT_LIN_GATE 24
+#define BM_DRAM_CTL13_CASLAT_LIN_GATE 0x0F000000
+#define BF_DRAM_CTL13_CASLAT_LIN_GATE(v) \
+ (((v) << 24) & BM_DRAM_CTL13_CASLAT_LIN_GATE)
+#define BP_DRAM_CTL13_CASLAT_LIN 16
+#define BM_DRAM_CTL13_CASLAT_LIN 0x000F0000
+#define BF_DRAM_CTL13_CASLAT_LIN(v) \
+ (((v) << 16) & BM_DRAM_CTL13_CASLAT_LIN)
+#define BP_DRAM_CTL13_APREBIT 8
+#define BM_DRAM_CTL13_APREBIT 0x00000F00
+#define BF_DRAM_CTL13_APREBIT(v) \
+ (((v) << 8) & BM_DRAM_CTL13_APREBIT)
+#define BP_DRAM_CTL13_TWTR 0
+#define BM_DRAM_CTL13_TWTR 0x00000007
+#define BF_DRAM_CTL13_TWTR(v) \
+ (((v) << 0) & BM_DRAM_CTL13_TWTR)
+HW_REGISTER_0(HW_DRAM_CTL14, REGS_DRAM_BASE, 0x00000038)
+#define HW_DRAM_CTL14_ADDR (REGS_DRAM_BASE + 0x00000038)
+#define BP_DRAM_CTL14_MAX_COL_REG 24
+#define BM_DRAM_CTL14_MAX_COL_REG 0x0F000000
+#define BF_DRAM_CTL14_MAX_COL_REG(v) \
+ (((v) << 24) & BM_DRAM_CTL14_MAX_COL_REG)
+#define BP_DRAM_CTL14_LOWPOWER_REFRESH_ENABLE 16
+#define BM_DRAM_CTL14_LOWPOWER_REFRESH_ENABLE 0x000F0000
+#define BF_DRAM_CTL14_LOWPOWER_REFRESH_ENABLE(v) \
+ (((v) << 16) & BM_DRAM_CTL14_LOWPOWER_REFRESH_ENABLE)
+#define BP_DRAM_CTL14_INITAREF 8
+#define BM_DRAM_CTL14_INITAREF 0x00000F00
+#define BF_DRAM_CTL14_INITAREF(v) \
+ (((v) << 8) & BM_DRAM_CTL14_INITAREF)
+#define BP_DRAM_CTL14_CS_MAP 0
+#define BM_DRAM_CTL14_CS_MAP 0x0000000F
+#define BF_DRAM_CTL14_CS_MAP(v) \
+ (((v) << 0) & BM_DRAM_CTL14_CS_MAP)
+HW_REGISTER_0(HW_DRAM_CTL15, REGS_DRAM_BASE, 0x0000003c)
+#define HW_DRAM_CTL15_ADDR (REGS_DRAM_BASE + 0x0000003c)
+#define BP_DRAM_CTL15_TRP 24
+#define BM_DRAM_CTL15_TRP 0x0F000000
+#define BF_DRAM_CTL15_TRP(v) \
+ (((v) << 24) & BM_DRAM_CTL15_TRP)
+#define BP_DRAM_CTL15_TDAL 16
+#define BM_DRAM_CTL15_TDAL 0x000F0000
+#define BF_DRAM_CTL15_TDAL(v) \
+ (((v) << 16) & BM_DRAM_CTL15_TDAL)
+#define BP_DRAM_CTL15_PORT_BUSY 8
+#define BM_DRAM_CTL15_PORT_BUSY 0x00000F00
+#define BF_DRAM_CTL15_PORT_BUSY(v) \
+ (((v) << 8) & BM_DRAM_CTL15_PORT_BUSY)
+#define BP_DRAM_CTL15_MAX_ROW_REG 0
+#define BM_DRAM_CTL15_MAX_ROW_REG 0x0000000F
+#define BF_DRAM_CTL15_MAX_ROW_REG(v) \
+ (((v) << 0) & BM_DRAM_CTL15_MAX_ROW_REG)
+HW_REGISTER_0(HW_DRAM_CTL16, REGS_DRAM_BASE, 0x00000040)
+#define HW_DRAM_CTL16_ADDR (REGS_DRAM_BASE + 0x00000040)
+#define BP_DRAM_CTL16_TMRD 24
+#define BM_DRAM_CTL16_TMRD 0x1F000000
+#define BF_DRAM_CTL16_TMRD(v) \
+ (((v) << 24) & BM_DRAM_CTL16_TMRD)
+#define BP_DRAM_CTL16_LOWPOWER_CONTROL 16
+#define BM_DRAM_CTL16_LOWPOWER_CONTROL 0x001F0000
+#define BF_DRAM_CTL16_LOWPOWER_CONTROL(v) \
+ (((v) << 16) & BM_DRAM_CTL16_LOWPOWER_CONTROL)
+#define BP_DRAM_CTL16_LOWPOWER_AUTO_ENABLE 8
+#define BM_DRAM_CTL16_LOWPOWER_AUTO_ENABLE 0x00001F00
+#define BF_DRAM_CTL16_LOWPOWER_AUTO_ENABLE(v) \
+ (((v) << 8) & BM_DRAM_CTL16_LOWPOWER_AUTO_ENABLE)
+#define BP_DRAM_CTL16_INT_ACK 0
+#define BM_DRAM_CTL16_INT_ACK 0x0000000F
+#define BF_DRAM_CTL16_INT_ACK(v) \
+ (((v) << 0) & BM_DRAM_CTL16_INT_ACK)
+HW_REGISTER_0(HW_DRAM_CTL17, REGS_DRAM_BASE, 0x00000044)
+#define HW_DRAM_CTL17_ADDR (REGS_DRAM_BASE + 0x00000044)
+#define BP_DRAM_CTL17_DLL_START_POINT 24
+#define BM_DRAM_CTL17_DLL_START_POINT 0xFF000000
+#define BF_DRAM_CTL17_DLL_START_POINT(v) \
+ (((v) << 24) & BM_DRAM_CTL17_DLL_START_POINT)
+#define BP_DRAM_CTL17_DLL_LOCK 16
+#define BM_DRAM_CTL17_DLL_LOCK 0x00FF0000
+#define BF_DRAM_CTL17_DLL_LOCK(v) \
+ (((v) << 16) & BM_DRAM_CTL17_DLL_LOCK)
+#define BP_DRAM_CTL17_DLL_INCREMENT 8
+#define BM_DRAM_CTL17_DLL_INCREMENT 0x0000FF00
+#define BF_DRAM_CTL17_DLL_INCREMENT(v) \
+ (((v) << 8) & BM_DRAM_CTL17_DLL_INCREMENT)
+#define BP_DRAM_CTL17_TRC 0
+#define BM_DRAM_CTL17_TRC 0x0000001F
+#define BF_DRAM_CTL17_TRC(v) \
+ (((v) << 0) & BM_DRAM_CTL17_TRC)
+HW_REGISTER_0(HW_DRAM_CTL18, REGS_DRAM_BASE, 0x00000048)
+#define HW_DRAM_CTL18_ADDR (REGS_DRAM_BASE + 0x00000048)
+#define BP_DRAM_CTL18_DLL_DQS_DELAY_1 24
+#define BM_DRAM_CTL18_DLL_DQS_DELAY_1 0x7F000000
+#define BF_DRAM_CTL18_DLL_DQS_DELAY_1(v) \
+ (((v) << 24) & BM_DRAM_CTL18_DLL_DQS_DELAY_1)
+#define BP_DRAM_CTL18_DLL_DQS_DELAY_0 16
+#define BM_DRAM_CTL18_DLL_DQS_DELAY_0 0x007F0000
+#define BF_DRAM_CTL18_DLL_DQS_DELAY_0(v) \
+ (((v) << 16) & BM_DRAM_CTL18_DLL_DQS_DELAY_0)
+#define BP_DRAM_CTL18_INT_STATUS 8
+#define BM_DRAM_CTL18_INT_STATUS 0x00001F00
+#define BF_DRAM_CTL18_INT_STATUS(v) \
+ (((v) << 8) & BM_DRAM_CTL18_INT_STATUS)
+#define BP_DRAM_CTL18_INT_MASK 0
+#define BM_DRAM_CTL18_INT_MASK 0x0000001F
+#define BF_DRAM_CTL18_INT_MASK(v) \
+ (((v) << 0) & BM_DRAM_CTL18_INT_MASK)
+HW_REGISTER_0(HW_DRAM_CTL19, REGS_DRAM_BASE, 0x0000004c)
+#define HW_DRAM_CTL19_ADDR (REGS_DRAM_BASE + 0x0000004c)
+#define BP_DRAM_CTL19_DQS_OUT_SHIFT_BYPASS 24
+#define BM_DRAM_CTL19_DQS_OUT_SHIFT_BYPASS 0xFF000000
+#define BF_DRAM_CTL19_DQS_OUT_SHIFT_BYPASS(v) \
+ (((v) << 24) & BM_DRAM_CTL19_DQS_OUT_SHIFT_BYPASS)
+#define BP_DRAM_CTL19_DQS_OUT_SHIFT 16
+#define BM_DRAM_CTL19_DQS_OUT_SHIFT 0x007F0000
+#define BF_DRAM_CTL19_DQS_OUT_SHIFT(v) \
+ (((v) << 16) & BM_DRAM_CTL19_DQS_OUT_SHIFT)
+#define BP_DRAM_CTL19_DLL_DQS_DELAY_BYPASS_1 8
+#define BM_DRAM_CTL19_DLL_DQS_DELAY_BYPASS_1 0x0000FF00
+#define BF_DRAM_CTL19_DLL_DQS_DELAY_BYPASS_1(v) \
+ (((v) << 8) & BM_DRAM_CTL19_DLL_DQS_DELAY_BYPASS_1)
+#define BP_DRAM_CTL19_DLL_DQS_DELAY_BYPASS_0 0
+#define BM_DRAM_CTL19_DLL_DQS_DELAY_BYPASS_0 0x000000FF
+#define BF_DRAM_CTL19_DLL_DQS_DELAY_BYPASS_0(v) \
+ (((v) << 0) & BM_DRAM_CTL19_DLL_DQS_DELAY_BYPASS_0)
+HW_REGISTER_0(HW_DRAM_CTL20, REGS_DRAM_BASE, 0x00000050)
+#define HW_DRAM_CTL20_ADDR (REGS_DRAM_BASE + 0x00000050)
+#define BP_DRAM_CTL20_TRCD_INT 24
+#define BM_DRAM_CTL20_TRCD_INT 0xFF000000
+#define BF_DRAM_CTL20_TRCD_INT(v) \
+ (((v) << 24) & BM_DRAM_CTL20_TRCD_INT)
+#define BP_DRAM_CTL20_TRAS_MIN 16
+#define BM_DRAM_CTL20_TRAS_MIN 0x00FF0000
+#define BF_DRAM_CTL20_TRAS_MIN(v) \
+ (((v) << 16) & BM_DRAM_CTL20_TRAS_MIN)
+#define BP_DRAM_CTL20_WR_DQS_SHIFT_BYPASS 8
+#define BM_DRAM_CTL20_WR_DQS_SHIFT_BYPASS 0x0000FF00
+#define BF_DRAM_CTL20_WR_DQS_SHIFT_BYPASS(v) \
+ (((v) << 8) & BM_DRAM_CTL20_WR_DQS_SHIFT_BYPASS)
+#define BP_DRAM_CTL20_WR_DQS_SHIFT 0
+#define BM_DRAM_CTL20_WR_DQS_SHIFT 0x0000007F
+#define BF_DRAM_CTL20_WR_DQS_SHIFT(v) \
+ (((v) << 0) & BM_DRAM_CTL20_WR_DQS_SHIFT)
+HW_REGISTER_0(HW_DRAM_CTL21, REGS_DRAM_BASE, 0x00000054)
+#define HW_DRAM_CTL21_ADDR (REGS_DRAM_BASE + 0x00000054)
+#define BP_DRAM_CTL21_OUT_OF_RANGE_LENGTH 8
+#define BM_DRAM_CTL21_OUT_OF_RANGE_LENGTH 0x0003FF00
+#define BF_DRAM_CTL21_OUT_OF_RANGE_LENGTH(v) \
+ (((v) << 8) & BM_DRAM_CTL21_OUT_OF_RANGE_LENGTH)
+#define BP_DRAM_CTL21_TRFC 0
+#define BM_DRAM_CTL21_TRFC 0x000000FF
+#define BF_DRAM_CTL21_TRFC(v) \
+ (((v) << 0) & BM_DRAM_CTL21_TRFC)
+HW_REGISTER_0(HW_DRAM_CTL22, REGS_DRAM_BASE, 0x00000058)
+#define HW_DRAM_CTL22_ADDR (REGS_DRAM_BASE + 0x00000058)
+#define BP_DRAM_CTL22_AHB0_WRCNT 16
+#define BM_DRAM_CTL22_AHB0_WRCNT 0x07FF0000
+#define BF_DRAM_CTL22_AHB0_WRCNT(v) \
+ (((v) << 16) & BM_DRAM_CTL22_AHB0_WRCNT)
+#define BP_DRAM_CTL22_AHB0_RDCNT 0
+#define BM_DRAM_CTL22_AHB0_RDCNT 0x000007FF
+#define BF_DRAM_CTL22_AHB0_RDCNT(v) \
+ (((v) << 0) & BM_DRAM_CTL22_AHB0_RDCNT)
+HW_REGISTER_0(HW_DRAM_CTL23, REGS_DRAM_BASE, 0x0000005c)
+#define HW_DRAM_CTL23_ADDR (REGS_DRAM_BASE + 0x0000005c)
+#define BP_DRAM_CTL23_AHB1_WRCNT 16
+#define BM_DRAM_CTL23_AHB1_WRCNT 0x07FF0000
+#define BF_DRAM_CTL23_AHB1_WRCNT(v) \
+ (((v) << 16) & BM_DRAM_CTL23_AHB1_WRCNT)
+#define BP_DRAM_CTL23_AHB1_RDCNT 0
+#define BM_DRAM_CTL23_AHB1_RDCNT 0x000007FF
+#define BF_DRAM_CTL23_AHB1_RDCNT(v) \
+ (((v) << 0) & BM_DRAM_CTL23_AHB1_RDCNT)
+HW_REGISTER_0(HW_DRAM_CTL24, REGS_DRAM_BASE, 0x00000060)
+#define HW_DRAM_CTL24_ADDR (REGS_DRAM_BASE + 0x00000060)
+#define BP_DRAM_CTL24_AHB2_WRCNT 16
+#define BM_DRAM_CTL24_AHB2_WRCNT 0x07FF0000
+#define BF_DRAM_CTL24_AHB2_WRCNT(v) \
+ (((v) << 16) & BM_DRAM_CTL24_AHB2_WRCNT)
+#define BP_DRAM_CTL24_AHB2_RDCNT 0
+#define BM_DRAM_CTL24_AHB2_RDCNT 0x000007FF
+#define BF_DRAM_CTL24_AHB2_RDCNT(v) \
+ (((v) << 0) & BM_DRAM_CTL24_AHB2_RDCNT)
+HW_REGISTER_0(HW_DRAM_CTL25, REGS_DRAM_BASE, 0x00000064)
+#define HW_DRAM_CTL25_ADDR (REGS_DRAM_BASE + 0x00000064)
+#define BP_DRAM_CTL25_AHB3_WRCNT 16
+#define BM_DRAM_CTL25_AHB3_WRCNT 0x07FF0000
+#define BF_DRAM_CTL25_AHB3_WRCNT(v) \
+ (((v) << 16) & BM_DRAM_CTL25_AHB3_WRCNT)
+#define BP_DRAM_CTL25_AHB3_RDCNT 0
+#define BM_DRAM_CTL25_AHB3_RDCNT 0x000007FF
+#define BF_DRAM_CTL25_AHB3_RDCNT(v) \
+ (((v) << 0) & BM_DRAM_CTL25_AHB3_RDCNT)
+HW_REGISTER_0(HW_DRAM_CTL26, REGS_DRAM_BASE, 0x00000068)
+#define HW_DRAM_CTL26_ADDR (REGS_DRAM_BASE + 0x00000068)
+#define BP_DRAM_CTL26_TREF 0
+#define BM_DRAM_CTL26_TREF 0x00000FFF
+#define BF_DRAM_CTL26_TREF(v) \
+ (((v) << 0) & BM_DRAM_CTL26_TREF)
+HW_REGISTER_0(HW_DRAM_CTL27, REGS_DRAM_BASE, 0x0000006c)
+#define HW_DRAM_CTL27_ADDR (REGS_DRAM_BASE + 0x0000006c)
+HW_REGISTER_0(HW_DRAM_CTL28, REGS_DRAM_BASE, 0x00000070)
+#define HW_DRAM_CTL28_ADDR (REGS_DRAM_BASE + 0x00000070)
+HW_REGISTER_0(HW_DRAM_CTL29, REGS_DRAM_BASE, 0x00000074)
+#define HW_DRAM_CTL29_ADDR (REGS_DRAM_BASE + 0x00000074)
+#define BP_DRAM_CTL29_LOWPOWER_INTERNAL_CNT 16
+#define BM_DRAM_CTL29_LOWPOWER_INTERNAL_CNT 0xFFFF0000
+#define BF_DRAM_CTL29_LOWPOWER_INTERNAL_CNT(v) \
+ (((v) << 16) & BM_DRAM_CTL29_LOWPOWER_INTERNAL_CNT)
+#define BP_DRAM_CTL29_LOWPOWER_EXTERNAL_CNT 0
+#define BM_DRAM_CTL29_LOWPOWER_EXTERNAL_CNT 0x0000FFFF
+#define BF_DRAM_CTL29_LOWPOWER_EXTERNAL_CNT(v) \
+ (((v) << 0) & BM_DRAM_CTL29_LOWPOWER_EXTERNAL_CNT)
+HW_REGISTER_0(HW_DRAM_CTL30, REGS_DRAM_BASE, 0x00000078)
+#define HW_DRAM_CTL30_ADDR (REGS_DRAM_BASE + 0x00000078)
+#define BP_DRAM_CTL30_LOWPOWER_REFRESH_HOLD 16
+#define BM_DRAM_CTL30_LOWPOWER_REFRESH_HOLD 0xFFFF0000
+#define BF_DRAM_CTL30_LOWPOWER_REFRESH_HOLD(v) \
+ (((v) << 16) & BM_DRAM_CTL30_LOWPOWER_REFRESH_HOLD)
+#define BP_DRAM_CTL30_LOWPOWER_POWER_DOWN_CNT 0
+#define BM_DRAM_CTL30_LOWPOWER_POWER_DOWN_CNT 0x0000FFFF
+#define BF_DRAM_CTL30_LOWPOWER_POWER_DOWN_CNT(v) \
+ (((v) << 0) & BM_DRAM_CTL30_LOWPOWER_POWER_DOWN_CNT)
+HW_REGISTER_0(HW_DRAM_CTL31, REGS_DRAM_BASE, 0x0000007c)
+#define HW_DRAM_CTL31_ADDR (REGS_DRAM_BASE + 0x0000007c)
+#define BP_DRAM_CTL31_TDLL 16
+#define BM_DRAM_CTL31_TDLL 0xFFFF0000
+#define BF_DRAM_CTL31_TDLL(v) \
+ (((v) << 16) & BM_DRAM_CTL31_TDLL)
+#define BP_DRAM_CTL31_LOWPOWER_SELF_REFRESH_CNT 0
+#define BM_DRAM_CTL31_LOWPOWER_SELF_REFRESH_CNT 0x0000FFFF
+#define BF_DRAM_CTL31_LOWPOWER_SELF_REFRESH_CNT(v) \
+ (((v) << 0) & BM_DRAM_CTL31_LOWPOWER_SELF_REFRESH_CNT)
+HW_REGISTER_0(HW_DRAM_CTL32, REGS_DRAM_BASE, 0x00000080)
+#define HW_DRAM_CTL32_ADDR (REGS_DRAM_BASE + 0x00000080)
+#define BP_DRAM_CTL32_TXSNR 16
+#define BM_DRAM_CTL32_TXSNR 0xFFFF0000
+#define BF_DRAM_CTL32_TXSNR(v) \
+ (((v) << 16) & BM_DRAM_CTL32_TXSNR)
+#define BP_DRAM_CTL32_TRAS_MAX 0
+#define BM_DRAM_CTL32_TRAS_MAX 0x0000FFFF
+#define BF_DRAM_CTL32_TRAS_MAX(v) \
+ (((v) << 0) & BM_DRAM_CTL32_TRAS_MAX)
+HW_REGISTER_0(HW_DRAM_CTL33, REGS_DRAM_BASE, 0x00000084)
+#define HW_DRAM_CTL33_ADDR (REGS_DRAM_BASE + 0x00000084)
+#define BP_DRAM_CTL33_VERSION 16
+#define BM_DRAM_CTL33_VERSION 0xFFFF0000
+#define BF_DRAM_CTL33_VERSION(v) \
+ (((v) << 16) & BM_DRAM_CTL33_VERSION)
+#define BP_DRAM_CTL33_TXSR 0
+#define BM_DRAM_CTL33_TXSR 0x0000FFFF
+#define BF_DRAM_CTL33_TXSR(v) \
+ (((v) << 0) & BM_DRAM_CTL33_TXSR)
+HW_REGISTER_0(HW_DRAM_CTL34, REGS_DRAM_BASE, 0x00000088)
+#define HW_DRAM_CTL34_ADDR (REGS_DRAM_BASE + 0x00000088)
+#define BP_DRAM_CTL34_TINIT 0
+#define BM_DRAM_CTL34_TINIT 0x00FFFFFF
+#define BF_DRAM_CTL34_TINIT(v) \
+ (((v) << 0) & BM_DRAM_CTL34_TINIT)
+HW_REGISTER_0(HW_DRAM_CTL35, REGS_DRAM_BASE, 0x0000008c)
+#define HW_DRAM_CTL35_ADDR (REGS_DRAM_BASE + 0x0000008c)
+#define BP_DRAM_CTL35_OUT_OF_RANGE_ADDR 0
+#define BM_DRAM_CTL35_OUT_OF_RANGE_ADDR 0x7FFFFFFF
+#define BF_DRAM_CTL35_OUT_OF_RANGE_ADDR(v) \
+ (((v) << 0) & BM_DRAM_CTL35_OUT_OF_RANGE_ADDR)
+HW_REGISTER_0(HW_DRAM_CTL36, REGS_DRAM_BASE, 0x00000090)
+#define HW_DRAM_CTL36_ADDR (REGS_DRAM_BASE + 0x00000090)
+#define BM_DRAM_CTL36_PWRUP_SREFRESH_EXIT 0x01000000
+#define BM_DRAM_CTL36_ENABLE_QUICK_SREFRESH 0x00010000
+#define BM_DRAM_CTL36_BUS_SHARE_ENABLE 0x00000100
+#define BM_DRAM_CTL36_ACTIVE_AGING 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL37, REGS_DRAM_BASE, 0x00000094)
+#define HW_DRAM_CTL37_ADDR (REGS_DRAM_BASE + 0x00000094)
+#define BP_DRAM_CTL37_BUS_SHARE_TIMEOUT 8
+#define BM_DRAM_CTL37_BUS_SHARE_TIMEOUT 0x0003FF00
+#define BF_DRAM_CTL37_BUS_SHARE_TIMEOUT(v) \
+ (((v) << 8) & BM_DRAM_CTL37_BUS_SHARE_TIMEOUT)
+#define BM_DRAM_CTL37_TREF_ENABLE 0x00000001
+HW_REGISTER_0(HW_DRAM_CTL38, REGS_DRAM_BASE, 0x00000098)
+#define HW_DRAM_CTL38_ADDR (REGS_DRAM_BASE + 0x00000098)
+#define BP_DRAM_CTL38_EMRS2_DATA_0 16
+#define BM_DRAM_CTL38_EMRS2_DATA_0 0x1FFF0000
+#define BF_DRAM_CTL38_EMRS2_DATA_0(v) \
+ (((v) << 16) & BM_DRAM_CTL38_EMRS2_DATA_0)
+#define BP_DRAM_CTL38_EMRS1_DATA 0
+#define BM_DRAM_CTL38_EMRS1_DATA 0x00001FFF
+#define BF_DRAM_CTL38_EMRS1_DATA(v) \
+ (((v) << 0) & BM_DRAM_CTL38_EMRS1_DATA)
+HW_REGISTER_0(HW_DRAM_CTL39, REGS_DRAM_BASE, 0x0000009c)
+#define HW_DRAM_CTL39_ADDR (REGS_DRAM_BASE + 0x0000009c)
+#define BP_DRAM_CTL39_EMRS2_DATA_2 16
+#define BM_DRAM_CTL39_EMRS2_DATA_2 0x1FFF0000
+#define BF_DRAM_CTL39_EMRS2_DATA_2(v) \
+ (((v) << 16) & BM_DRAM_CTL39_EMRS2_DATA_2)
+#define BP_DRAM_CTL39_EMRS2_DATA_1 0
+#define BM_DRAM_CTL39_EMRS2_DATA_1 0x00001FFF
+#define BF_DRAM_CTL39_EMRS2_DATA_1(v) \
+ (((v) << 0) & BM_DRAM_CTL39_EMRS2_DATA_1)
+HW_REGISTER_0(HW_DRAM_CTL40, REGS_DRAM_BASE, 0x000000a0)
+#define HW_DRAM_CTL40_ADDR (REGS_DRAM_BASE + 0x000000a0)
+#define BP_DRAM_CTL40_TPDEX 16
+#define BM_DRAM_CTL40_TPDEX 0xFFFF0000
+#define BF_DRAM_CTL40_TPDEX(v) \
+ (((v) << 16) & BM_DRAM_CTL40_TPDEX)
+#define BP_DRAM_CTL40_EMRS2_DATA_3 0
+#define BM_DRAM_CTL40_EMRS2_DATA_3 0x00001FFF
+#define BF_DRAM_CTL40_EMRS2_DATA_3(v) \
+ (((v) << 0) & BM_DRAM_CTL40_EMRS2_DATA_3)
+#endif /* __ARCH_ARM___DRAM_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-dri.h b/arch/arm/mach-stmp3xxx/include/mach/regs-dri.h
new file mode 100644
index 000000000000..7dec7e09c871
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-dri.h
@@ -0,0 +1,160 @@
+/*
+ * STMP DRI Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___DRI_H
+#define __ARCH_ARM___DRI_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_DRI_BASE (REGS_BASE + 0x74000)
+#define REGS_DRI_BASE_PHYS (0x80074000)
+#define REGS_DRI_SIZE 0x00002000
+HW_REGISTER(HW_DRI_CTRL, REGS_DRI_BASE, 0x00000000)
+#define HW_DRI_CTRL_ADDR (REGS_DRI_BASE + 0x00000000)
+#define BM_DRI_CTRL_SFTRST 0x80000000
+#define BV_DRI_CTRL_SFTRST__RUN 0x0
+#define BV_DRI_CTRL_SFTRST__RESET 0x1
+#define BM_DRI_CTRL_CLKGATE 0x40000000
+#define BV_DRI_CTRL_CLKGATE__RUN 0x0
+#define BV_DRI_CTRL_CLKGATE__NO_CLKS 0x1
+#define BM_DRI_CTRL_ENABLE_INPUTS 0x20000000
+#define BV_DRI_CTRL_ENABLE_INPUTS__ANALOG_LINE_IN 0x0
+#define BV_DRI_CTRL_ENABLE_INPUTS__DRI_DIGITAL_IN 0x1
+#define BM_DRI_CTRL_STOP_ON_OFLOW_ERROR 0x04000000
+#define BV_DRI_CTRL_STOP_ON_OFLOW_ERROR__IGNORE 0x0
+#define BV_DRI_CTRL_STOP_ON_OFLOW_ERROR__STOP 0x1
+#define BM_DRI_CTRL_STOP_ON_PILOT_ERROR 0x02000000
+#define BV_DRI_CTRL_STOP_ON_PILOT_ERROR__IGNORE 0x0
+#define BV_DRI_CTRL_STOP_ON_PILOT_ERROR__STOP 0x1
+#define BP_DRI_CTRL_DMA_DELAY_COUNT 16
+#define BM_DRI_CTRL_DMA_DELAY_COUNT 0x001F0000
+#define BF_DRI_CTRL_DMA_DELAY_COUNT(v) \
+ (((v) << 16) & BM_DRI_CTRL_DMA_DELAY_COUNT)
+#define BM_DRI_CTRL_REACQUIRE_PHASE 0x00008000
+#define BV_DRI_CTRL_REACQUIRE_PHASE__NORMAL 0x0
+#define BV_DRI_CTRL_REACQUIRE_PHASE__NEW_PHASE 0x1
+#define BM_DRI_CTRL_OVERFLOW_IRQ_EN 0x00000800
+#define BV_DRI_CTRL_OVERFLOW_IRQ_EN__DISABLED 0x0
+#define BV_DRI_CTRL_OVERFLOW_IRQ_EN__ENABLED 0x1
+#define BM_DRI_CTRL_PILOT_SYNC_LOSS_IRQ_EN 0x00000400
+#define BV_DRI_CTRL_PILOT_SYNC_LOSS_IRQ_EN__DISABLED 0x0
+#define BV_DRI_CTRL_PILOT_SYNC_LOSS_IRQ_EN__ENABLED 0x1
+#define BM_DRI_CTRL_ATTENTION_IRQ_EN 0x00000200
+#define BV_DRI_CTRL_ATTENTION_IRQ_EN__DISABLED 0x0
+#define BV_DRI_CTRL_ATTENTION_IRQ_EN__ENABLED 0x1
+#define BM_DRI_CTRL_OVERFLOW_IRQ 0x00000008
+#define BV_DRI_CTRL_OVERFLOW_IRQ__NO_REQUEST 0x0
+#define BV_DRI_CTRL_OVERFLOW_IRQ__REQUEST 0x1
+#define BM_DRI_CTRL_PILOT_SYNC_LOSS_IRQ 0x00000004
+#define BV_DRI_CTRL_PILOT_SYNC_LOSS_IRQ__NO_REQUEST 0x0
+#define BV_DRI_CTRL_PILOT_SYNC_LOSS_IRQ__REQUEST 0x1
+#define BM_DRI_CTRL_ATTENTION_IRQ 0x00000002
+#define BV_DRI_CTRL_ATTENTION_IRQ__NO_REQUEST 0x0
+#define BV_DRI_CTRL_ATTENTION_IRQ__REQUEST 0x1
+#define BM_DRI_CTRL_RUN 0x00000001
+#define BV_DRI_CTRL_RUN__HALT 0x0
+#define BV_DRI_CTRL_RUN__RUN 0x1
+HW_REGISTER_0(HW_DRI_TIMING, REGS_DRI_BASE, 0x00000010)
+#define HW_DRI_TIMING_ADDR (REGS_DRI_BASE + 0x00000010)
+#define BP_DRI_TIMING_PILOT_REP_RATE 16
+#define BM_DRI_TIMING_PILOT_REP_RATE 0x000F0000
+#define BF_DRI_TIMING_PILOT_REP_RATE(v) \
+ (((v) << 16) & BM_DRI_TIMING_PILOT_REP_RATE)
+#define BP_DRI_TIMING_GAP_DETECTION_INTERVAL 0
+#define BM_DRI_TIMING_GAP_DETECTION_INTERVAL 0x000000FF
+#define BF_DRI_TIMING_GAP_DETECTION_INTERVAL(v) \
+ (((v) << 0) & BM_DRI_TIMING_GAP_DETECTION_INTERVAL)
+HW_REGISTER_0(HW_DRI_STAT, REGS_DRI_BASE, 0x00000020)
+#define HW_DRI_STAT_ADDR (REGS_DRI_BASE + 0x00000020)
+#define BM_DRI_STAT_DRI_PRESENT 0x80000000
+#define BV_DRI_STAT_DRI_PRESENT__UNAVAILABLE 0x0
+#define BV_DRI_STAT_DRI_PRESENT__AVAILABLE 0x1
+#define BP_DRI_STAT_PILOT_PHASE 16
+#define BM_DRI_STAT_PILOT_PHASE 0x000F0000
+#define BF_DRI_STAT_PILOT_PHASE(v) \
+ (((v) << 16) & BM_DRI_STAT_PILOT_PHASE)
+#define BM_DRI_STAT_OVERFLOW_IRQ_SUMMARY 0x00000008
+#define BV_DRI_STAT_OVERFLOW_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_DRI_STAT_OVERFLOW_IRQ_SUMMARY__REQUEST 0x1
+#define BM_DRI_STAT_PILOT_SYNC_LOSS_IRQ_SUMMARY 0x00000004
+#define BV_DRI_STAT_PILOT_SYNC_LOSS_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_DRI_STAT_PILOT_SYNC_LOSS_IRQ_SUMMARY__REQUEST 0x1
+#define BM_DRI_STAT_ATTENTION_IRQ_SUMMARY 0x00000002
+#define BV_DRI_STAT_ATTENTION_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_DRI_STAT_ATTENTION_IRQ_SUMMARY__REQUEST 0x1
+HW_REGISTER_0(HW_DRI_DATA, REGS_DRI_BASE, 0x00000030)
+#define HW_DRI_DATA_ADDR (REGS_DRI_BASE + 0x00000030)
+#define BP_DRI_DATA_DATA 0
+#define BM_DRI_DATA_DATA 0xFFFFFFFF
+#define BF_DRI_DATA_DATA(v) (v)
+HW_REGISTER(HW_DRI_DEBUG0, REGS_DRI_BASE, 0x00000040)
+#define HW_DRI_DEBUG0_ADDR (REGS_DRI_BASE + 0x00000040)
+#define BM_DRI_DEBUG0_DMAREQ 0x80000000
+#define BM_DRI_DEBUG0_DMACMDKICK 0x40000000
+#define BM_DRI_DEBUG0_DRI_CLK_INPUT 0x20000000
+#define BM_DRI_DEBUG0_DRI_DATA_INPUT 0x10000000
+#define BM_DRI_DEBUG0_TEST_MODE 0x08000000
+#define BM_DRI_DEBUG0_PILOT_REP_RATE 0x04000000
+#define BV_DRI_DEBUG0_PILOT_REP_RATE__8_AT_4MHZ 0x0
+#define BV_DRI_DEBUG0_PILOT_REP_RATE__12_AT_6MHZ 0x1
+#define BP_DRI_DEBUG0_SPARE 18
+#define BM_DRI_DEBUG0_SPARE 0x03FC0000
+#define BF_DRI_DEBUG0_SPARE(v) \
+ (((v) << 18) & BM_DRI_DEBUG0_SPARE)
+#define BP_DRI_DEBUG0_FRAME 0
+#define BM_DRI_DEBUG0_FRAME 0x0003FFFF
+#define BF_DRI_DEBUG0_FRAME(v) \
+ (((v) << 0) & BM_DRI_DEBUG0_FRAME)
+HW_REGISTER(HW_DRI_DEBUG1, REGS_DRI_BASE, 0x00000050)
+#define HW_DRI_DEBUG1_ADDR (REGS_DRI_BASE + 0x00000050)
+#define BM_DRI_DEBUG1_INVERT_PILOT 0x80000000
+#define BV_DRI_DEBUG1_INVERT_PILOT__NORMAL 0x0
+#define BV_DRI_DEBUG1_INVERT_PILOT__INVERTED 0x1
+#define BM_DRI_DEBUG1_INVERT_ATTENTION 0x40000000
+#define BV_DRI_DEBUG1_INVERT_ATTENTION__NORMAL 0x0
+#define BV_DRI_DEBUG1_INVERT_ATTENTION__INVERTED 0x1
+#define BM_DRI_DEBUG1_INVERT_DRI_DATA 0x20000000
+#define BV_DRI_DEBUG1_INVERT_DRI_DATA__NORMAL 0x0
+#define BV_DRI_DEBUG1_INVERT_DRI_DATA__INVERTED 0x1
+#define BM_DRI_DEBUG1_INVERT_DRI_CLOCK 0x10000000
+#define BV_DRI_DEBUG1_INVERT_DRI_CLOCK__NORMAL 0x0
+#define BV_DRI_DEBUG1_INVERT_DRI_CLOCK__INVERTED 0x1
+#define BM_DRI_DEBUG1_REVERSE_FRAME 0x08000000
+#define BV_DRI_DEBUG1_REVERSE_FRAME__NORMAL 0x0
+#define BV_DRI_DEBUG1_REVERSE_FRAME__REVERSED 0x1
+#define BP_DRI_DEBUG1_SWIZZLED_FRAME 0
+#define BM_DRI_DEBUG1_SWIZZLED_FRAME 0x0003FFFF
+#define BF_DRI_DEBUG1_SWIZZLED_FRAME(v) \
+ (((v) << 0) & BM_DRI_DEBUG1_SWIZZLED_FRAME)
+HW_REGISTER_0(HW_DRI_VERSION, REGS_DRI_BASE, 0x00000060)
+#define HW_DRI_VERSION_ADDR (REGS_DRI_BASE + 0x00000060)
+#define BP_DRI_VERSION_MAJOR 24
+#define BM_DRI_VERSION_MAJOR 0xFF000000
+#define BF_DRI_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_DRI_VERSION_MAJOR)
+#define BP_DRI_VERSION_MINOR 16
+#define BM_DRI_VERSION_MINOR 0x00FF0000
+#define BF_DRI_VERSION_MINOR(v) \
+ (((v) << 16) & BM_DRI_VERSION_MINOR)
+#define BP_DRI_VERSION_STEP 0
+#define BM_DRI_VERSION_STEP 0x0000FFFF
+#define BF_DRI_VERSION_STEP(v) \
+ (((v) << 0) & BM_DRI_VERSION_STEP)
+#endif /* __ARCH_ARM___DRI_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-ecc8.h b/arch/arm/mach-stmp3xxx/include/mach/regs-ecc8.h
new file mode 100644
index 000000000000..527bce602f12
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-ecc8.h
@@ -0,0 +1,279 @@
+/*
+ * STMP ECC8 Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___ECC8_H
+#define __ARCH_ARM___ECC8_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_ECC8_BASE (REGS_BASE + 0x8000)
+#define REGS_ECC8_BASE_PHYS (0x80008000)
+#define REGS_ECC8_SIZE 0x00002000
+HW_REGISTER(HW_ECC8_CTRL, REGS_ECC8_BASE, 0x00000000)
+#define HW_ECC8_CTRL_ADDR (REGS_ECC8_BASE + 0x00000000)
+#define BM_ECC8_CTRL_SFTRST 0x80000000
+#define BV_ECC8_CTRL_SFTRST__RUN 0x0
+#define BV_ECC8_CTRL_SFTRST__RESET 0x1
+#define BM_ECC8_CTRL_CLKGATE 0x40000000
+#define BV_ECC8_CTRL_CLKGATE__RUN 0x0
+#define BV_ECC8_CTRL_CLKGATE__NO_CLKS 0x1
+#define BM_ECC8_CTRL_AHBM_SFTRST 0x20000000
+#define BV_ECC8_CTRL_AHBM_SFTRST__RUN 0x0
+#define BV_ECC8_CTRL_AHBM_SFTRST__RESET 0x1
+#define BP_ECC8_CTRL_THROTTLE 24
+#define BM_ECC8_CTRL_THROTTLE 0x0F000000
+#define BF_ECC8_CTRL_THROTTLE(v) \
+ (((v) << 24) & BM_ECC8_CTRL_THROTTLE)
+#define BM_ECC8_CTRL_DEBUG_STALL_IRQ_EN 0x00000400
+#define BM_ECC8_CTRL_DEBUG_WRITE_IRQ_EN 0x00000200
+#define BM_ECC8_CTRL_COMPLETE_IRQ_EN 0x00000100
+#define BM_ECC8_CTRL_BM_ERROR_IRQ 0x00000008
+#define BM_ECC8_CTRL_DEBUG_STALL_IRQ 0x00000004
+#define BM_ECC8_CTRL_DEBUG_WRITE_IRQ 0x00000002
+#define BM_ECC8_CTRL_COMPLETE_IRQ 0x00000001
+HW_REGISTER_0(HW_ECC8_STATUS0, REGS_ECC8_BASE, 0x00000010)
+#define HW_ECC8_STATUS0_ADDR (REGS_ECC8_BASE + 0x00000010)
+#define BP_ECC8_STATUS0_HANDLE 20
+#define BM_ECC8_STATUS0_HANDLE 0xFFF00000
+#define BF_ECC8_STATUS0_HANDLE(v) \
+ (((v) << 20) & BM_ECC8_STATUS0_HANDLE)
+#define BP_ECC8_STATUS0_COMPLETED_CE 16
+#define BM_ECC8_STATUS0_COMPLETED_CE 0x000F0000
+#define BF_ECC8_STATUS0_COMPLETED_CE(v) \
+ (((v) << 16) & BM_ECC8_STATUS0_COMPLETED_CE)
+#define BM_ECC8_STATUS0_RS8ECC_ENC_PRESENT 0x00008000
+#define BM_ECC8_STATUS0_RS8ECC_DEC_PRESENT 0x00004000
+#define BM_ECC8_STATUS0_RS4ECC_ENC_PRESENT 0x00002000
+#define BM_ECC8_STATUS0_RS4ECC_DEC_PRESENT 0x00001000
+#define BP_ECC8_STATUS0_STATUS_AUX 8
+#define BM_ECC8_STATUS0_STATUS_AUX 0x00000F00
+#define BF_ECC8_STATUS0_STATUS_AUX(v) \
+ (((v) << 8) & BM_ECC8_STATUS0_STATUS_AUX)
+#define BV_ECC8_STATUS0_STATUS_AUX__NO_ERRORS 0x0
+#define BV_ECC8_STATUS0_STATUS_AUX__ONE_CORRECTABLE 0x1
+#define BV_ECC8_STATUS0_STATUS_AUX__TWO_CORRECTABLE 0x2
+#define BV_ECC8_STATUS0_STATUS_AUX__THREE_CORRECTABLE 0x3
+#define BV_ECC8_STATUS0_STATUS_AUX__FOUR_CORRECTABLE 0x4
+#define BV_ECC8_STATUS0_STATUS_AUX__NOT_CHECKED 0xC
+#define BV_ECC8_STATUS0_STATUS_AUX__UNCORRECTABLE 0xE
+#define BV_ECC8_STATUS0_STATUS_AUX__ALL_ONES 0xF
+#define BM_ECC8_STATUS0_ALLONES 0x00000010
+#define BM_ECC8_STATUS0_CORRECTED 0x00000008
+#define BM_ECC8_STATUS0_UNCORRECTABLE 0x00000004
+HW_REGISTER_0(HW_ECC8_STATUS1, REGS_ECC8_BASE, 0x00000020)
+#define HW_ECC8_STATUS1_ADDR (REGS_ECC8_BASE + 0x00000020)
+#define BP_ECC8_STATUS1_STATUS_PAYLOAD7 28
+#define BM_ECC8_STATUS1_STATUS_PAYLOAD7 0xF0000000
+#define BF_ECC8_STATUS1_STATUS_PAYLOAD7(v) \
+ (((v) << 28) & BM_ECC8_STATUS1_STATUS_PAYLOAD7)
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__NO_ERRORS 0x0
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__ONE_CORRECTABLE 0x1
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__TWO_CORRECTABLE 0x2
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__THREE_CORRECTABLE 0x3
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__FOUR_CORRECTABLE 0x4
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__FIVE_CORRECTABLE 0x5
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__SIX_CORRECTABLE 0x6
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__SEVEN_CORRECTABLE 0x7
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__EIGHT_CORRECTABLE 0x8
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__NOT_CHECKED 0xC
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__UNCORRECTABLE 0xE
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD7__ALL_ONES 0xF
+#define BP_ECC8_STATUS1_STATUS_PAYLOAD6 24
+#define BM_ECC8_STATUS1_STATUS_PAYLOAD6 0x0F000000
+#define BF_ECC8_STATUS1_STATUS_PAYLOAD6(v) \
+ (((v) << 24) & BM_ECC8_STATUS1_STATUS_PAYLOAD6)
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__NO_ERRORS 0x0
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__ONE_CORRECTABLE 0x1
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__TWO_CORRECTABLE 0x2
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__THREE_CORRECTABLE 0x3
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__FOUR_CORRECTABLE 0x4
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__FIVE_CORRECTABLE 0x5
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__SIX_CORRECTABLE 0x6
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__SEVEN_CORRECTABLE 0x7
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__EIGHT_CORRECTABLE 0x8
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__NOT_CHECKED 0xC
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__UNCORRECTABLE 0xE
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD6__ALL_ONES 0xF
+#define BP_ECC8_STATUS1_STATUS_PAYLOAD5 20
+#define BM_ECC8_STATUS1_STATUS_PAYLOAD5 0x00F00000
+#define BF_ECC8_STATUS1_STATUS_PAYLOAD5(v) \
+ (((v) << 20) & BM_ECC8_STATUS1_STATUS_PAYLOAD5)
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__NO_ERRORS 0x0
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__ONE_CORRECTABLE 0x1
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__TWO_CORRECTABLE 0x2
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__THREE_CORRECTABLE 0x3
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__FOUR_CORRECTABLE 0x4
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__FIVE_CORRECTABLE 0x5
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__SIX_CORRECTABLE 0x6
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__SEVEN_CORRECTABLE 0x7
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__EIGHT_CORRECTABLE 0x8
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__NOT_CHECKED 0xC
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__UNCORRECTABLE 0xE
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD5__ALL_ONES 0xF
+#define BP_ECC8_STATUS1_STATUS_PAYLOAD4 16
+#define BM_ECC8_STATUS1_STATUS_PAYLOAD4 0x000F0000
+#define BF_ECC8_STATUS1_STATUS_PAYLOAD4(v) \
+ (((v) << 16) & BM_ECC8_STATUS1_STATUS_PAYLOAD4)
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__NO_ERRORS 0x0
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__ONE_CORRECTABLE 0x1
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__TWO_CORRECTABLE 0x2
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__THREE_CORRECTABLE 0x3
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__FOUR_CORRECTABLE 0x4
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__FIVE_CORRECTABLE 0x5
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__SIX_CORRECTABLE 0x6
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__SEVEN_CORRECTABLE 0x7
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__EIGHT_CORRECTABLE 0x8
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__NOT_CHECKED 0xC
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__UNCORRECTABLE 0xE
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD4__ALL_ONES 0xF
+#define BP_ECC8_STATUS1_STATUS_PAYLOAD3 12
+#define BM_ECC8_STATUS1_STATUS_PAYLOAD3 0x0000F000
+#define BF_ECC8_STATUS1_STATUS_PAYLOAD3(v) \
+ (((v) << 12) & BM_ECC8_STATUS1_STATUS_PAYLOAD3)
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__NO_ERRORS 0x0
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__ONE_CORRECTABLE 0x1
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__TWO_CORRECTABLE 0x2
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__THREE_CORRECTABLE 0x3
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__FOUR_CORRECTABLE 0x4
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__FIVE_CORRECTABLE 0x5
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__SIX_CORRECTABLE 0x6
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__SEVEN_CORRECTABLE 0x7
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__EIGHT_CORRECTABLE 0x8
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__NOT_CHECKED 0xC
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__UNCORRECTABLE 0xE
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD3__ALL_ONES 0xF
+#define BP_ECC8_STATUS1_STATUS_PAYLOAD2 8
+#define BM_ECC8_STATUS1_STATUS_PAYLOAD2 0x00000F00
+#define BF_ECC8_STATUS1_STATUS_PAYLOAD2(v) \
+ (((v) << 8) & BM_ECC8_STATUS1_STATUS_PAYLOAD2)
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__NO_ERRORS 0x0
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__ONE_CORRECTABLE 0x1
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__TWO_CORRECTABLE 0x2
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__THREE_CORRECTABLE 0x3
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__FOUR_CORRECTABLE 0x4
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__FIVE_CORRECTABLE 0x5
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__SIX_CORRECTABLE 0x6
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__SEVEN_CORRECTABLE 0x7
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__EIGHT_CORRECTABLE 0x8
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__NOT_CHECKED 0xC
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__UNCORRECTABLE 0xE
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD2__ALL_ONES 0xF
+#define BP_ECC8_STATUS1_STATUS_PAYLOAD1 4
+#define BM_ECC8_STATUS1_STATUS_PAYLOAD1 0x000000F0
+#define BF_ECC8_STATUS1_STATUS_PAYLOAD1(v) \
+ (((v) << 4) & BM_ECC8_STATUS1_STATUS_PAYLOAD1)
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__NO_ERRORS 0x0
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__ONE_CORRECTABLE 0x1
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__TWO_CORRECTABLE 0x2
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__THREE_CORRECTABLE 0x3
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__FOUR_CORRECTABLE 0x4
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__FIVE_CORRECTABLE 0x5
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__SIX_CORRECTABLE 0x6
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__SEVEN_CORRECTABLE 0x7
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__EIGHT_CORRECTABLE 0x8
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__NOT_CHECKED 0xC
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__UNCORRECTABLE 0xE
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD1__ALL_ONES 0xF
+#define BP_ECC8_STATUS1_STATUS_PAYLOAD0 0
+#define BM_ECC8_STATUS1_STATUS_PAYLOAD0 0x0000000F
+#define BF_ECC8_STATUS1_STATUS_PAYLOAD0(v) \
+ (((v) << 0) & BM_ECC8_STATUS1_STATUS_PAYLOAD0)
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__NO_ERRORS 0x0
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__ONE_CORRECTABLE 0x1
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__TWO_CORRECTABLE 0x2
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__THREE_CORRECTABLE 0x3
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__FOUR_CORRECTABLE 0x4
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__FIVE_CORRECTABLE 0x5
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__SIX_CORRECTABLE 0x6
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__SEVEN_CORRECTABLE 0x7
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__EIGHT_CORRECTABLE 0x8
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__NOT_CHECKED 0xC
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__UNCORRECTABLE 0xE
+#define BV_ECC8_STATUS1_STATUS_PAYLOAD0__ALL_ONES 0xF
+HW_REGISTER(HW_ECC8_DEBUG0, REGS_ECC8_BASE, 0x00000030)
+#define HW_ECC8_DEBUG0_ADDR (REGS_ECC8_BASE + 0x00000030)
+#define BP_ECC8_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 16
+#define BM_ECC8_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL 0x01FF0000
+#define BF_ECC8_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL(v) \
+ (((v) << 16) & BM_ECC8_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL)
+#define BV_ECC8_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__NORMAL 0x0
+#define BV_ECC8_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__TEST_MODE 0x1
+#define BM_ECC8_DEBUG0_KES_DEBUG_SHIFT_SYND 0x00008000
+#define BM_ECC8_DEBUG0_KES_DEBUG_PAYLOAD_FLAG 0x00004000
+#define BV_ECC8_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__DATA 0x1
+#define BV_ECC8_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__AUX 0x1
+#define BM_ECC8_DEBUG0_KES_DEBUG_MODE4K 0x00002000
+#define BV_ECC8_DEBUG0_KES_DEBUG_MODE4K__4k 0x1
+#define BV_ECC8_DEBUG0_KES_DEBUG_MODE4K__2k 0x1
+#define BM_ECC8_DEBUG0_KES_DEBUG_KICK 0x00001000
+#define BM_ECC8_DEBUG0_KES_STANDALONE 0x00000800
+#define BV_ECC8_DEBUG0_KES_STANDALONE__NORMAL 0x0
+#define BV_ECC8_DEBUG0_KES_STANDALONE__TEST_MODE 0x1
+#define BM_ECC8_DEBUG0_KES_DEBUG_STEP 0x00000400
+#define BM_ECC8_DEBUG0_KES_DEBUG_STALL 0x00000200
+#define BV_ECC8_DEBUG0_KES_DEBUG_STALL__NORMAL 0x0
+#define BV_ECC8_DEBUG0_KES_DEBUG_STALL__WAIT 0x1
+#define BM_ECC8_DEBUG0_BM_KES_TEST_BYPASS 0x00000100
+#define BV_ECC8_DEBUG0_BM_KES_TEST_BYPASS__NORMAL 0x0
+#define BV_ECC8_DEBUG0_BM_KES_TEST_BYPASS__TEST_MODE 0x1
+#define BP_ECC8_DEBUG0_DEBUG_REG_SELECT 0
+#define BM_ECC8_DEBUG0_DEBUG_REG_SELECT 0x0000003F
+#define BF_ECC8_DEBUG0_DEBUG_REG_SELECT(v) \
+ (((v) << 0) & BM_ECC8_DEBUG0_DEBUG_REG_SELECT)
+HW_REGISTER_0(HW_ECC8_DBGKESREAD, REGS_ECC8_BASE, 0x00000040)
+#define HW_ECC8_DBGKESREAD_ADDR (REGS_ECC8_BASE + 0x00000040)
+#define BP_ECC8_DBGKESREAD_VALUES 0
+#define BM_ECC8_DBGKESREAD_VALUES 0xFFFFFFFF
+#define BF_ECC8_DBGKESREAD_VALUES(v) (v)
+HW_REGISTER_0(HW_ECC8_DBGCSFEREAD, REGS_ECC8_BASE, 0x00000050)
+#define HW_ECC8_DBGCSFEREAD_ADDR (REGS_ECC8_BASE + 0x00000050)
+#define BP_ECC8_DBGCSFEREAD_VALUES 0
+#define BM_ECC8_DBGCSFEREAD_VALUES 0xFFFFFFFF
+#define BF_ECC8_DBGCSFEREAD_VALUES(v) (v)
+HW_REGISTER_0(HW_ECC8_DBGSYNDGENREAD, REGS_ECC8_BASE, 0x00000060)
+#define HW_ECC8_DBGSYNDGENREAD_ADDR (REGS_ECC8_BASE + 0x00000060)
+#define BP_ECC8_DBGSYNDGENREAD_VALUES 0
+#define BM_ECC8_DBGSYNDGENREAD_VALUES 0xFFFFFFFF
+#define BF_ECC8_DBGSYNDGENREAD_VALUES(v) (v)
+HW_REGISTER_0(HW_ECC8_DBGAHBMREAD, REGS_ECC8_BASE, 0x00000070)
+#define HW_ECC8_DBGAHBMREAD_ADDR (REGS_ECC8_BASE + 0x00000070)
+#define BP_ECC8_DBGAHBMREAD_VALUES 0
+#define BM_ECC8_DBGAHBMREAD_VALUES 0xFFFFFFFF
+#define BF_ECC8_DBGAHBMREAD_VALUES(v) (v)
+HW_REGISTER_0(HW_ECC8_BLOCKNAME, REGS_ECC8_BASE, 0x00000080)
+#define HW_ECC8_BLOCKNAME_ADDR (REGS_ECC8_BASE + 0x00000080)
+#define BP_ECC8_BLOCKNAME_NAME 0
+#define BM_ECC8_BLOCKNAME_NAME 0xFFFFFFFF
+#define BF_ECC8_BLOCKNAME_NAME(v) (v)
+HW_REGISTER_0(HW_ECC8_VERSION, REGS_ECC8_BASE, 0x000000a0)
+#define HW_ECC8_VERSION_ADDR (REGS_ECC8_BASE + 0x000000a0)
+#define BP_ECC8_VERSION_MAJOR 24
+#define BM_ECC8_VERSION_MAJOR 0xFF000000
+#define BF_ECC8_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_ECC8_VERSION_MAJOR)
+#define BP_ECC8_VERSION_MINOR 16
+#define BM_ECC8_VERSION_MINOR 0x00FF0000
+#define BF_ECC8_VERSION_MINOR(v) \
+ (((v) << 16) & BM_ECC8_VERSION_MINOR)
+#define BP_ECC8_VERSION_STEP 0
+#define BM_ECC8_VERSION_STEP 0x0000FFFF
+#define BF_ECC8_VERSION_STEP(v) \
+ (((v) << 0) & BM_ECC8_VERSION_STEP)
+#endif /* __ARCH_ARM___ECC8_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-emi.h b/arch/arm/mach-stmp3xxx/include/mach/regs-emi.h
new file mode 100644
index 000000000000..79893ccc67b9
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-emi.h
@@ -0,0 +1,175 @@
+/*
+ * STMP EMI Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___EMI_H
+#define __ARCH_ARM___EMI_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_EMI_BASE (REGS_BASE + 0x20000)
+#define REGS_EMI_BASE_PHYS (0x80020000)
+#define REGS_EMI_SIZE 0x00002000
+HW_REGISTER(HW_EMI_CTRL, REGS_EMI_BASE, 0x00000000)
+#define HW_EMI_CTRL_ADDR (REGS_EMI_BASE + 0x00000000)
+#define BM_EMI_CTRL_SFTRST 0x80000000
+#define BM_EMI_CTRL_CLKGATE 0x40000000
+#define BM_EMI_CTRL_TRAP_SR 0x20000000
+#define BM_EMI_CTRL_TRAP_INIT 0x10000000
+#define BP_EMI_CTRL_AXI_DEPTH 26
+#define BM_EMI_CTRL_AXI_DEPTH 0x0C000000
+#define BF_EMI_CTRL_AXI_DEPTH(v) \
+ (((v) << 26) & BM_EMI_CTRL_AXI_DEPTH)
+#define BV_EMI_CTRL_AXI_DEPTH__ONE 0x0
+#define BV_EMI_CTRL_AXI_DEPTH__TWO 0x1
+#define BV_EMI_CTRL_AXI_DEPTH__THREE 0x2
+#define BV_EMI_CTRL_AXI_DEPTH__FOUR 0x3
+#define BM_EMI_CTRL_DLL_SHIFT_RESET 0x02000000
+#define BM_EMI_CTRL_DLL_RESET 0x01000000
+#define BP_EMI_CTRL_ARB_MODE 22
+#define BM_EMI_CTRL_ARB_MODE 0x00C00000
+#define BF_EMI_CTRL_ARB_MODE(v) \
+ (((v) << 22) & BM_EMI_CTRL_ARB_MODE)
+#define BV_EMI_CTRL_ARB_MODE__TIMESTAMP 0x0
+#define BV_EMI_CTRL_ARB_MODE__WRITE_HYBRID 0x1
+#define BV_EMI_CTRL_ARB_MODE__PORT_PRIORITY 0x2
+#define BP_EMI_CTRL_PORT_PRIORITY_ORDER 16
+#define BM_EMI_CTRL_PORT_PRIORITY_ORDER 0x001F0000
+#define BF_EMI_CTRL_PORT_PRIORITY_ORDER(v) \
+ (((v) << 16) & BM_EMI_CTRL_PORT_PRIORITY_ORDER)
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT0123 0x00
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT0312 0x01
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT0231 0x02
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT0321 0x03
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT0213 0x04
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT0132 0x05
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT1023 0x06
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT1302 0x07
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT1230 0x08
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT1320 0x09
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT1203 0x0A
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT1032 0x0B
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT2013 0x0C
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT2301 0x0D
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT2130 0x0E
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT2310 0x0F
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT2103 0x10
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT2031 0x11
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT3012 0x12
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT3201 0x13
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT3120 0x14
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT3210 0x15
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT3102 0x16
+#define BV_EMI_CTRL_PORT_PRIORITY_ORDER__PORT3021 0x17
+#define BP_EMI_CTRL_PRIORITY_WRITE_ITER 12
+#define BM_EMI_CTRL_PRIORITY_WRITE_ITER 0x00007000
+#define BF_EMI_CTRL_PRIORITY_WRITE_ITER(v) \
+ (((v) << 12) & BM_EMI_CTRL_PRIORITY_WRITE_ITER)
+#define BP_EMI_CTRL_HIGH_PRIORITY_WRITE 8
+#define BM_EMI_CTRL_HIGH_PRIORITY_WRITE 0x00000700
+#define BF_EMI_CTRL_HIGH_PRIORITY_WRITE(v) \
+ (((v) << 8) & BM_EMI_CTRL_HIGH_PRIORITY_WRITE)
+#define BM_EMI_CTRL_MEM_WIDTH 0x00000040
+#define BM_EMI_CTRL_WRITE_PROTECT 0x00000020
+#define BM_EMI_CTRL_RESET_OUT 0x00000010
+#define BP_EMI_CTRL_CE_SELECT 0
+#define BM_EMI_CTRL_CE_SELECT 0x0000000F
+#define BF_EMI_CTRL_CE_SELECT(v) \
+ (((v) << 0) & BM_EMI_CTRL_CE_SELECT)
+#define BV_EMI_CTRL_CE_SELECT__NONE 0x0
+#define BV_EMI_CTRL_CE_SELECT__CE0 0x1
+#define BV_EMI_CTRL_CE_SELECT__CE1 0x2
+#define BV_EMI_CTRL_CE_SELECT__CE2 0x4
+#define BV_EMI_CTRL_CE_SELECT__CE3 0x8
+HW_REGISTER_0(HW_EMI_STAT, REGS_EMI_BASE, 0x00000010)
+#define HW_EMI_STAT_ADDR (REGS_EMI_BASE + 0x00000010)
+#define BM_EMI_STAT_DRAM_PRESENT 0x80000000
+#define BM_EMI_STAT_NOR_PRESENT 0x40000000
+#define BM_EMI_STAT_LARGE_DRAM_ENABLED 0x20000000
+#define BM_EMI_STAT_DRAM_HALTED 0x00000002
+#define BV_EMI_STAT_DRAM_HALTED__NOT_HALTED 0x0
+#define BV_EMI_STAT_DRAM_HALTED__HALTED 0x1
+#define BM_EMI_STAT_NOR_BUSY 0x00000001
+#define BV_EMI_STAT_NOR_BUSY__NOT_BUSY 0x0
+#define BV_EMI_STAT_NOR_BUSY__BUSY 0x1
+HW_REGISTER(HW_EMI_TIME, REGS_EMI_BASE, 0x00000020)
+#define HW_EMI_TIME_ADDR (REGS_EMI_BASE + 0x00000020)
+#define BP_EMI_TIME_THZ 24
+#define BM_EMI_TIME_THZ 0x0F000000
+#define BF_EMI_TIME_THZ(v) \
+ (((v) << 24) & BM_EMI_TIME_THZ)
+#define BP_EMI_TIME_TDH 16
+#define BM_EMI_TIME_TDH 0x000F0000
+#define BF_EMI_TIME_TDH(v) \
+ (((v) << 16) & BM_EMI_TIME_TDH)
+#define BP_EMI_TIME_TDS 8
+#define BM_EMI_TIME_TDS 0x00001F00
+#define BF_EMI_TIME_TDS(v) \
+ (((v) << 8) & BM_EMI_TIME_TDS)
+#define BP_EMI_TIME_TAS 0
+#define BM_EMI_TIME_TAS 0x0000000F
+#define BF_EMI_TIME_TAS(v) \
+ (((v) << 0) & BM_EMI_TIME_TAS)
+HW_REGISTER(HW_EMI_DDR_TEST_MODE_CSR, REGS_EMI_BASE, 0x00000030)
+#define HW_EMI_DDR_TEST_MODE_CSR_ADDR (REGS_EMI_BASE + 0x00000030)
+#define BM_EMI_DDR_TEST_MODE_CSR_DONE 0x00000002
+#define BM_EMI_DDR_TEST_MODE_CSR_START 0x00000001
+HW_REGISTER_0(HW_EMI_DEBUG, REGS_EMI_BASE, 0x00000080)
+#define HW_EMI_DEBUG_ADDR (REGS_EMI_BASE + 0x00000080)
+#define BP_EMI_DEBUG_NOR_STATE 0
+#define BM_EMI_DEBUG_NOR_STATE 0x0000000F
+#define BF_EMI_DEBUG_NOR_STATE(v) \
+ (((v) << 0) & BM_EMI_DEBUG_NOR_STATE)
+HW_REGISTER_0(HW_EMI_DDR_TEST_MODE_STATUS0, REGS_EMI_BASE, 0x00000090)
+#define HW_EMI_DDR_TEST_MODE_STATUS0_ADDR (REGS_EMI_BASE + 0x00000090)
+#define BP_EMI_DDR_TEST_MODE_STATUS0_ADDR0 0
+#define BM_EMI_DDR_TEST_MODE_STATUS0_ADDR0 0x00001FFF
+#define BF_EMI_DDR_TEST_MODE_STATUS0_ADDR0(v) \
+ (((v) << 0) & BM_EMI_DDR_TEST_MODE_STATUS0_ADDR0)
+HW_REGISTER_0(HW_EMI_DDR_TEST_MODE_STATUS1, REGS_EMI_BASE, 0x000000a0)
+#define HW_EMI_DDR_TEST_MODE_STATUS1_ADDR (REGS_EMI_BASE + 0x000000a0)
+#define BP_EMI_DDR_TEST_MODE_STATUS1_ADDR1 0
+#define BM_EMI_DDR_TEST_MODE_STATUS1_ADDR1 0x00001FFF
+#define BF_EMI_DDR_TEST_MODE_STATUS1_ADDR1(v) \
+ (((v) << 0) & BM_EMI_DDR_TEST_MODE_STATUS1_ADDR1)
+HW_REGISTER_0(HW_EMI_DDR_TEST_MODE_STATUS2, REGS_EMI_BASE, 0x000000b0)
+#define HW_EMI_DDR_TEST_MODE_STATUS2_ADDR (REGS_EMI_BASE + 0x000000b0)
+#define BP_EMI_DDR_TEST_MODE_STATUS2_DATA0 0
+#define BM_EMI_DDR_TEST_MODE_STATUS2_DATA0 0xFFFFFFFF
+#define BF_EMI_DDR_TEST_MODE_STATUS2_DATA0(v) (v)
+HW_REGISTER_0(HW_EMI_DDR_TEST_MODE_STATUS3, REGS_EMI_BASE, 0x000000c0)
+#define HW_EMI_DDR_TEST_MODE_STATUS3_ADDR (REGS_EMI_BASE + 0x000000c0)
+#define BP_EMI_DDR_TEST_MODE_STATUS3_DATA1 0
+#define BM_EMI_DDR_TEST_MODE_STATUS3_DATA1 0xFFFFFFFF
+#define BF_EMI_DDR_TEST_MODE_STATUS3_DATA1(v) (v)
+HW_REGISTER_0(HW_EMI_VERSION, REGS_EMI_BASE, 0x000000f0)
+#define HW_EMI_VERSION_ADDR (REGS_EMI_BASE + 0x000000f0)
+#define BP_EMI_VERSION_MAJOR 24
+#define BM_EMI_VERSION_MAJOR 0xFF000000
+#define BF_EMI_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_EMI_VERSION_MAJOR)
+#define BP_EMI_VERSION_MINOR 16
+#define BM_EMI_VERSION_MINOR 0x00FF0000
+#define BF_EMI_VERSION_MINOR(v) \
+ (((v) << 16) & BM_EMI_VERSION_MINOR)
+#define BP_EMI_VERSION_STEP 0
+#define BM_EMI_VERSION_STEP 0x0000FFFF
+#define BF_EMI_VERSION_STEP(v) \
+ (((v) << 0) & BM_EMI_VERSION_STEP)
+#endif /* __ARCH_ARM___EMI_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-gpmi.h b/arch/arm/mach-stmp3xxx/include/mach/regs-gpmi.h
new file mode 100644
index 000000000000..ea4f8904a034
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-gpmi.h
@@ -0,0 +1,329 @@
+/*
+ * STMP GPMI Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___GPMI_H
+#define __ARCH_ARM___GPMI_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_GPMI_BASE (REGS_BASE + 0xc000)
+#define REGS_GPMI_BASE_PHYS (0x8000C000)
+#define REGS_GPMI_SIZE 0x00002000
+HW_REGISTER(HW_GPMI_CTRL0, REGS_GPMI_BASE, 0x00000000)
+#define HW_GPMI_CTRL0_ADDR (REGS_GPMI_BASE + 0x00000000)
+#define BM_GPMI_CTRL0_SFTRST 0x80000000
+#define BV_GPMI_CTRL0_SFTRST__RUN 0x0
+#define BV_GPMI_CTRL0_SFTRST__RESET 0x1
+#define BM_GPMI_CTRL0_CLKGATE 0x40000000
+#define BV_GPMI_CTRL0_CLKGATE__RUN 0x0
+#define BV_GPMI_CTRL0_CLKGATE__NO_CLKS 0x1
+#define BM_GPMI_CTRL0_RUN 0x20000000
+#define BV_GPMI_CTRL0_RUN__IDLE 0x0
+#define BV_GPMI_CTRL0_RUN__BUSY 0x1
+#define BM_GPMI_CTRL0_DEV_IRQ_EN 0x10000000
+#define BM_GPMI_CTRL0_TIMEOUT_IRQ_EN 0x08000000
+#define BM_GPMI_CTRL0_UDMA 0x04000000
+#define BV_GPMI_CTRL0_UDMA__DISABLED 0x0
+#define BV_GPMI_CTRL0_UDMA__ENABLED 0x1
+#define BP_GPMI_CTRL0_COMMAND_MODE 24
+#define BM_GPMI_CTRL0_COMMAND_MODE 0x03000000
+#define BF_GPMI_CTRL0_COMMAND_MODE(v) \
+ (((v) << 24) & BM_GPMI_CTRL0_COMMAND_MODE)
+#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2
+#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3
+#define BM_GPMI_CTRL0_WORD_LENGTH 0x00800000
+#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0
+#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1
+#define BM_GPMI_CTRL0_LOCK_CS 0x00400000
+#define BV_GPMI_CTRL0_LOCK_CS__DISABLED 0x0
+#define BV_GPMI_CTRL0_LOCK_CS__ENABLED 0x1
+#define BP_GPMI_CTRL0_CS 20
+#define BM_GPMI_CTRL0_CS 0x00300000
+#define BF_GPMI_CTRL0_CS(v) \
+ (((v) << 20) & BM_GPMI_CTRL0_CS)
+#define BP_GPMI_CTRL0_ADDRESS 17
+#define BM_GPMI_CTRL0_ADDRESS 0x000E0000
+#define BF_GPMI_CTRL0_ADDRESS(v) \
+ (((v) << 17) & BM_GPMI_CTRL0_ADDRESS)
+#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0
+#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1
+#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2
+#define BM_GPMI_CTRL0_ADDRESS_INCREMENT 0x00010000
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0
+#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1
+#define BP_GPMI_CTRL0_XFER_COUNT 0
+#define BM_GPMI_CTRL0_XFER_COUNT 0x0000FFFF
+#define BF_GPMI_CTRL0_XFER_COUNT(v) \
+ (((v) << 0) & BM_GPMI_CTRL0_XFER_COUNT)
+HW_REGISTER_0(HW_GPMI_COMPARE, REGS_GPMI_BASE, 0x00000010)
+#define HW_GPMI_COMPARE_ADDR (REGS_GPMI_BASE + 0x00000010)
+#define BP_GPMI_COMPARE_MASK 16
+#define BM_GPMI_COMPARE_MASK 0xFFFF0000
+#define BF_GPMI_COMPARE_MASK(v) \
+ (((v) << 16) & BM_GPMI_COMPARE_MASK)
+#define BP_GPMI_COMPARE_REFERENCE 0
+#define BM_GPMI_COMPARE_REFERENCE 0x0000FFFF
+#define BF_GPMI_COMPARE_REFERENCE(v) \
+ (((v) << 0) & BM_GPMI_COMPARE_REFERENCE)
+HW_REGISTER(HW_GPMI_ECCCTRL, REGS_GPMI_BASE, 0x00000020)
+#define HW_GPMI_ECCCTRL_ADDR (REGS_GPMI_BASE + 0x00000020)
+#define BP_GPMI_ECCCTRL_HANDLE 16
+#define BM_GPMI_ECCCTRL_HANDLE 0xFFFF0000
+#define BF_GPMI_ECCCTRL_HANDLE(v) \
+ (((v) << 16) & BM_GPMI_ECCCTRL_HANDLE)
+#define BP_GPMI_ECCCTRL_ECC_CMD 13
+#define BM_GPMI_ECCCTRL_ECC_CMD 0x00006000
+#define BF_GPMI_ECCCTRL_ECC_CMD(v) \
+ (((v) << 13) & BM_GPMI_ECCCTRL_ECC_CMD)
+#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE_4_BIT 0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_4_BIT 0x1
+#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE_8_BIT 0x2
+#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_8_BIT 0x3
+#define BM_GPMI_ECCCTRL_ENABLE_ECC 0x00001000
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1
+#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0
+#define BP_GPMI_ECCCTRL_BUFFER_MASK 0
+#define BM_GPMI_ECCCTRL_BUFFER_MASK 0x000001FF
+#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \
+ (((v) << 0) & BM_GPMI_ECCCTRL_BUFFER_MASK)
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__AUXILIARY 0x100
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER7 0x080
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER6 0x040
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER5 0x020
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER4 0x010
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER3 0x008
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER2 0x004
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER1 0x002
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER0 0x001
+HW_REGISTER_0(HW_GPMI_ECCCOUNT, REGS_GPMI_BASE, 0x00000030)
+#define HW_GPMI_ECCCOUNT_ADDR (REGS_GPMI_BASE + 0x00000030)
+#define BP_GPMI_ECCCOUNT_COUNT 0
+#define BM_GPMI_ECCCOUNT_COUNT 0x0000FFFF
+#define BF_GPMI_ECCCOUNT_COUNT(v) \
+ (((v) << 0) & BM_GPMI_ECCCOUNT_COUNT)
+HW_REGISTER_0(HW_GPMI_PAYLOAD, REGS_GPMI_BASE, 0x00000040)
+#define HW_GPMI_PAYLOAD_ADDR (REGS_GPMI_BASE + 0x00000040)
+#define BP_GPMI_PAYLOAD_ADDRESS 2
+#define BM_GPMI_PAYLOAD_ADDRESS 0xFFFFFFFC
+#define BF_GPMI_PAYLOAD_ADDRESS(v) \
+ (((v) << 2) & BM_GPMI_PAYLOAD_ADDRESS)
+HW_REGISTER_0(HW_GPMI_AUXILIARY, REGS_GPMI_BASE, 0x00000050)
+#define HW_GPMI_AUXILIARY_ADDR (REGS_GPMI_BASE + 0x00000050)
+#define BP_GPMI_AUXILIARY_ADDRESS 2
+#define BM_GPMI_AUXILIARY_ADDRESS 0xFFFFFFFC
+#define BF_GPMI_AUXILIARY_ADDRESS(v) \
+ (((v) << 2) & BM_GPMI_AUXILIARY_ADDRESS)
+HW_REGISTER(HW_GPMI_CTRL1, REGS_GPMI_BASE, 0x00000060)
+#define HW_GPMI_CTRL1_ADDR (REGS_GPMI_BASE + 0x00000060)
+#define BM_GPMI_CTRL1_CE3_SEL 0x00800000
+#define BM_GPMI_CTRL1_CE2_SEL 0x00400000
+#define BM_GPMI_CTRL1_CE1_SEL 0x00200000
+#define BM_GPMI_CTRL1_CE0_SEL 0x00100000
+#define BM_GPMI_CTRL1_GANGED_RDYBUSY 0x00080000
+#define BM_GPMI_CTRL1_BCH_MODE 0x00040000
+#define BM_GPMI_CTRL1_DLL_ENABLE 0x00020000
+#define BM_GPMI_CTRL1_HALF_PERIOD 0x00010000
+#define BP_GPMI_CTRL1_RDN_DELAY 12
+#define BM_GPMI_CTRL1_RDN_DELAY 0x0000F000
+#define BF_GPMI_CTRL1_RDN_DELAY(v) \
+ (((v) << 12) & BM_GPMI_CTRL1_RDN_DELAY)
+#define BM_GPMI_CTRL1_DMA2ECC_MODE 0x00000800
+#define BM_GPMI_CTRL1_DEV_IRQ 0x00000400
+#define BM_GPMI_CTRL1_TIMEOUT_IRQ 0x00000200
+#define BM_GPMI_CTRL1_BURST_EN 0x00000100
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY3 0x00000080
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY2 0x00000040
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY1 0x00000020
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY0 0x00000010
+#define BM_GPMI_CTRL1_DEV_RESET 0x00000008
+#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0
+#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1
+#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY 0x00000004
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1
+#define BM_GPMI_CTRL1_CAMERA_MODE 0x00000002
+#define BM_GPMI_CTRL1_GPMI_MODE 0x00000001
+#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0
+#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1
+HW_REGISTER_0(HW_GPMI_TIMING0, REGS_GPMI_BASE, 0x00000070)
+#define HW_GPMI_TIMING0_ADDR (REGS_GPMI_BASE + 0x00000070)
+#define BP_GPMI_TIMING0_ADDRESS_SETUP 16
+#define BM_GPMI_TIMING0_ADDRESS_SETUP 0x00FF0000
+#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \
+ (((v) << 16) & BM_GPMI_TIMING0_ADDRESS_SETUP)
+#define BP_GPMI_TIMING0_DATA_HOLD 8
+#define BM_GPMI_TIMING0_DATA_HOLD 0x0000FF00
+#define BF_GPMI_TIMING0_DATA_HOLD(v) \
+ (((v) << 8) & BM_GPMI_TIMING0_DATA_HOLD)
+#define BP_GPMI_TIMING0_DATA_SETUP 0
+#define BM_GPMI_TIMING0_DATA_SETUP 0x000000FF
+#define BF_GPMI_TIMING0_DATA_SETUP(v) \
+ (((v) << 0) & BM_GPMI_TIMING0_DATA_SETUP)
+HW_REGISTER_0(HW_GPMI_TIMING1, REGS_GPMI_BASE, 0x00000080)
+#define HW_GPMI_TIMING1_ADDR (REGS_GPMI_BASE + 0x00000080)
+#define BP_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 16
+#define BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT 0xFFFF0000
+#define BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(v) \
+ (((v) << 16) & BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT)
+HW_REGISTER_0(HW_GPMI_TIMING2, REGS_GPMI_BASE, 0x00000090)
+#define HW_GPMI_TIMING2_ADDR (REGS_GPMI_BASE + 0x00000090)
+#define BP_GPMI_TIMING2_UDMA_TRP 24
+#define BM_GPMI_TIMING2_UDMA_TRP 0xFF000000
+#define BF_GPMI_TIMING2_UDMA_TRP(v) \
+ (((v) << 24) & BM_GPMI_TIMING2_UDMA_TRP)
+#define BP_GPMI_TIMING2_UDMA_ENV 16
+#define BM_GPMI_TIMING2_UDMA_ENV 0x00FF0000
+#define BF_GPMI_TIMING2_UDMA_ENV(v) \
+ (((v) << 16) & BM_GPMI_TIMING2_UDMA_ENV)
+#define BP_GPMI_TIMING2_UDMA_HOLD 8
+#define BM_GPMI_TIMING2_UDMA_HOLD 0x0000FF00
+#define BF_GPMI_TIMING2_UDMA_HOLD(v) \
+ (((v) << 8) & BM_GPMI_TIMING2_UDMA_HOLD)
+#define BP_GPMI_TIMING2_UDMA_SETUP 0
+#define BM_GPMI_TIMING2_UDMA_SETUP 0x000000FF
+#define BF_GPMI_TIMING2_UDMA_SETUP(v) \
+ (((v) << 0) & BM_GPMI_TIMING2_UDMA_SETUP)
+HW_REGISTER_0(HW_GPMI_DATA, REGS_GPMI_BASE, 0x000000a0)
+#define HW_GPMI_DATA_ADDR (REGS_GPMI_BASE + 0x000000a0)
+#define BP_GPMI_DATA_DATA 0
+#define BM_GPMI_DATA_DATA 0xFFFFFFFF
+#define BF_GPMI_DATA_DATA(v) (v)
+HW_REGISTER_0(HW_GPMI_STAT, REGS_GPMI_BASE, 0x000000b0)
+#define HW_GPMI_STAT_ADDR (REGS_GPMI_BASE + 0x000000b0)
+#define BM_GPMI_STAT_PRESENT 0x80000000
+#define BV_GPMI_STAT_PRESENT__UNAVAILABLE 0x0
+#define BV_GPMI_STAT_PRESENT__AVAILABLE 0x1
+#define BP_GPMI_STAT_RDY_TIMEOUT 8
+#define BM_GPMI_STAT_RDY_TIMEOUT 0x00000F00
+#define BF_GPMI_STAT_RDY_TIMEOUT(v) \
+ (((v) << 8) & BM_GPMI_STAT_RDY_TIMEOUT)
+#define BM_GPMI_STAT_ATA_IRQ 0x00000080
+#define BM_GPMI_STAT_INVALID_BUFFER_MASK 0x00000040
+#define BM_GPMI_STAT_FIFO_EMPTY 0x00000020
+#define BV_GPMI_STAT_FIFO_EMPTY__NOT_EMPTY 0x0
+#define BV_GPMI_STAT_FIFO_EMPTY__EMPTY 0x1
+#define BM_GPMI_STAT_FIFO_FULL 0x00000010
+#define BV_GPMI_STAT_FIFO_FULL__NOT_FULL 0x0
+#define BV_GPMI_STAT_FIFO_FULL__FULL 0x1
+#define BM_GPMI_STAT_DEV3_ERROR 0x00000008
+#define BM_GPMI_STAT_DEV2_ERROR 0x00000004
+#define BM_GPMI_STAT_DEV1_ERROR 0x00000002
+#define BM_GPMI_STAT_DEV0_ERROR 0x00000001
+HW_REGISTER_0(HW_GPMI_DEBUG, REGS_GPMI_BASE, 0x000000c0)
+#define HW_GPMI_DEBUG_ADDR (REGS_GPMI_BASE + 0x000000c0)
+#define BM_GPMI_DEBUG_READY3 0x80000000
+#define BM_GPMI_DEBUG_READY2 0x40000000
+#define BM_GPMI_DEBUG_READY1 0x20000000
+#define BM_GPMI_DEBUG_READY0 0x10000000
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END3 0x08000000
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END2 0x04000000
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END1 0x02000000
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END0 0x01000000
+#define BM_GPMI_DEBUG_SENSE3 0x00800000
+#define BM_GPMI_DEBUG_SENSE2 0x00400000
+#define BM_GPMI_DEBUG_SENSE1 0x00200000
+#define BM_GPMI_DEBUG_SENSE0 0x00100000
+#define BM_GPMI_DEBUG_DMAREQ3 0x00080000
+#define BM_GPMI_DEBUG_DMAREQ2 0x00040000
+#define BM_GPMI_DEBUG_DMAREQ1 0x00020000
+#define BM_GPMI_DEBUG_DMAREQ0 0x00010000
+#define BP_GPMI_DEBUG_CMD_END 12
+#define BM_GPMI_DEBUG_CMD_END 0x0000F000
+#define BF_GPMI_DEBUG_CMD_END(v) \
+ (((v) << 12) & BM_GPMI_DEBUG_CMD_END)
+#define BP_GPMI_DEBUG_UDMA_STATE 8
+#define BM_GPMI_DEBUG_UDMA_STATE 0x00000F00
+#define BF_GPMI_DEBUG_UDMA_STATE(v) \
+ (((v) << 8) & BM_GPMI_DEBUG_UDMA_STATE)
+#define BM_GPMI_DEBUG_BUSY 0x00000080
+#define BV_GPMI_DEBUG_BUSY__DISABLED 0x0
+#define BV_GPMI_DEBUG_BUSY__ENABLED 0x1
+#define BP_GPMI_DEBUG_PIN_STATE 4
+#define BM_GPMI_DEBUG_PIN_STATE 0x00000070
+#define BF_GPMI_DEBUG_PIN_STATE(v) \
+ (((v) << 4) & BM_GPMI_DEBUG_PIN_STATE)
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_IDLE 0x0
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_BYTCNT 0x1
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_ADDR 0x2
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_STALL 0x3
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_STROBE 0x4
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_ATARDY 0x5
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_DHOLD 0x6
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_DONE 0x7
+#define BP_GPMI_DEBUG_MAIN_STATE 0
+#define BM_GPMI_DEBUG_MAIN_STATE 0x0000000F
+#define BF_GPMI_DEBUG_MAIN_STATE(v) \
+ (((v) << 0) & BM_GPMI_DEBUG_MAIN_STATE)
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_IDLE 0x0
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_BYTCNT 0x1
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFE 0x2
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFR 0x3
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DMAREQ 0x4
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DMAACK 0x5
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFF 0x6
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_LDFIFO 0x7
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_LDDMAR 0x8
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_RDCMP 0x9
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DONE 0xA
+HW_REGISTER_0(HW_GPMI_VERSION, REGS_GPMI_BASE, 0x000000d0)
+#define HW_GPMI_VERSION_ADDR (REGS_GPMI_BASE + 0x000000d0)
+#define BP_GPMI_VERSION_MAJOR 24
+#define BM_GPMI_VERSION_MAJOR 0xFF000000
+#define BF_GPMI_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_GPMI_VERSION_MAJOR)
+#define BP_GPMI_VERSION_MINOR 16
+#define BM_GPMI_VERSION_MINOR 0x00FF0000
+#define BF_GPMI_VERSION_MINOR(v) \
+ (((v) << 16) & BM_GPMI_VERSION_MINOR)
+#define BP_GPMI_VERSION_STEP 0
+#define BM_GPMI_VERSION_STEP 0x0000FFFF
+#define BF_GPMI_VERSION_STEP(v) \
+ (((v) << 0) & BM_GPMI_VERSION_STEP)
+HW_REGISTER_0(HW_GPMI_DEBUG2, REGS_GPMI_BASE, 0x000000e0)
+#define HW_GPMI_DEBUG2_ADDR (REGS_GPMI_BASE + 0x000000e0)
+#define BP_GPMI_DEBUG2_SYND2GPMI_BE 12
+#define BM_GPMI_DEBUG2_SYND2GPMI_BE 0x0000F000
+#define BF_GPMI_DEBUG2_SYND2GPMI_BE(v) \
+ (((v) << 12) & BM_GPMI_DEBUG2_SYND2GPMI_BE)
+#define BM_GPMI_DEBUG2_GPMI2SYND_VALID 0x00000800
+#define BM_GPMI_DEBUG2_GPMI2SYND_READY 0x00000400
+#define BM_GPMI_DEBUG2_SYND2GPMI_VALID 0x00000200
+#define BM_GPMI_DEBUG2_SYND2GPMI_READY 0x00000100
+#define BM_GPMI_DEBUG2_VIEW_DELAYED_RDN 0x00000080
+#define BM_GPMI_DEBUG2_UPDATE_WINDOW 0x00000040
+#define BP_GPMI_DEBUG2_RDN_TAP 0
+#define BM_GPMI_DEBUG2_RDN_TAP 0x0000003F
+#define BF_GPMI_DEBUG2_RDN_TAP(v) \
+ (((v) << 0) & BM_GPMI_DEBUG2_RDN_TAP)
+HW_REGISTER_0(HW_GPMI_DEBUG3, REGS_GPMI_BASE, 0x000000f0)
+#define HW_GPMI_DEBUG3_ADDR (REGS_GPMI_BASE + 0x000000f0)
+#define BP_GPMI_DEBUG3_APB_WORD_CNTR 16
+#define BM_GPMI_DEBUG3_APB_WORD_CNTR 0xFFFF0000
+#define BF_GPMI_DEBUG3_APB_WORD_CNTR(v) \
+ (((v) << 16) & BM_GPMI_DEBUG3_APB_WORD_CNTR)
+#define BP_GPMI_DEBUG3_DEV_WORD_CNTR 0
+#define BM_GPMI_DEBUG3_DEV_WORD_CNTR 0x0000FFFF
+#define BF_GPMI_DEBUG3_DEV_WORD_CNTR(v) \
+ (((v) << 0) & BM_GPMI_DEBUG3_DEV_WORD_CNTR)
+#endif /* __ARCH_ARM___GPMI_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-i2c.h b/arch/arm/mach-stmp3xxx/include/mach/regs-i2c.h
new file mode 100644
index 000000000000..262faa25d4ea
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-i2c.h
@@ -0,0 +1,306 @@
+/*
+ * STMP I2C Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___I2C_H
+#define __ARCH_ARM___I2C_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_I2C_BASE (REGS_BASE + 0x58000)
+#define REGS_I2C_BASE_PHYS (0x80058000)
+#define REGS_I2C_SIZE 0x00002000
+HW_REGISTER(HW_I2C_CTRL0, REGS_I2C_BASE, 0x00000000)
+#define HW_I2C_CTRL0_ADDR (REGS_I2C_BASE + 0x00000000)
+#define BM_I2C_CTRL0_SFTRST 0x80000000
+#define BV_I2C_CTRL0_SFTRST__RUN 0x0
+#define BV_I2C_CTRL0_SFTRST__RESET 0x1
+#define BM_I2C_CTRL0_CLKGATE 0x40000000
+#define BV_I2C_CTRL0_CLKGATE__RUN 0x0
+#define BV_I2C_CTRL0_CLKGATE__NO_CLKS 0x1
+#define BM_I2C_CTRL0_RUN 0x20000000
+#define BV_I2C_CTRL0_RUN__HALT 0x0
+#define BV_I2C_CTRL0_RUN__RUN 0x1
+#define BM_I2C_CTRL0_PRE_ACK 0x08000000
+#define BM_I2C_CTRL0_ACKNOWLEDGE 0x04000000
+#define BV_I2C_CTRL0_ACKNOWLEDGE__SNAK 0x0
+#define BV_I2C_CTRL0_ACKNOWLEDGE__ACK 0x1
+#define BM_I2C_CTRL0_SEND_NAK_ON_LAST 0x02000000
+#define BV_I2C_CTRL0_SEND_NAK_ON_LAST__ACK_IT 0x0
+#define BV_I2C_CTRL0_SEND_NAK_ON_LAST__NAK_IT 0x1
+#define BM_I2C_CTRL0_PIO_MODE 0x01000000
+#define BM_I2C_CTRL0_MULTI_MASTER 0x00800000
+#define BV_I2C_CTRL0_MULTI_MASTER__SINGLE 0x0
+#define BV_I2C_CTRL0_MULTI_MASTER__MULTIPLE 0x1
+#define BM_I2C_CTRL0_CLOCK_HELD 0x00400000
+#define BV_I2C_CTRL0_CLOCK_HELD__RELEASE 0x0
+#define BV_I2C_CTRL0_CLOCK_HELD__HELD_LOW 0x1
+#define BM_I2C_CTRL0_RETAIN_CLOCK 0x00200000
+#define BV_I2C_CTRL0_RETAIN_CLOCK__RELEASE 0x0
+#define BV_I2C_CTRL0_RETAIN_CLOCK__HOLD_LOW 0x1
+#define BM_I2C_CTRL0_POST_SEND_STOP 0x00100000
+#define BV_I2C_CTRL0_POST_SEND_STOP__NO_STOP 0x0
+#define BV_I2C_CTRL0_POST_SEND_STOP__SEND_STOP 0x1
+#define BM_I2C_CTRL0_PRE_SEND_START 0x00080000
+#define BV_I2C_CTRL0_PRE_SEND_START__NO_START 0x0
+#define BV_I2C_CTRL0_PRE_SEND_START__SEND_START 0x1
+#define BM_I2C_CTRL0_SLAVE_ADDRESS_ENABLE 0x00040000
+#define BV_I2C_CTRL0_SLAVE_ADDRESS_ENABLE__DISABLED 0x0
+#define BV_I2C_CTRL0_SLAVE_ADDRESS_ENABLE__ENABLED 0x1
+#define BM_I2C_CTRL0_MASTER_MODE 0x00020000
+#define BV_I2C_CTRL0_MASTER_MODE__SLAVE 0x0
+#define BV_I2C_CTRL0_MASTER_MODE__MASTER 0x1
+#define BM_I2C_CTRL0_DIRECTION 0x00010000
+#define BV_I2C_CTRL0_DIRECTION__RECEIVE 0x0
+#define BV_I2C_CTRL0_DIRECTION__TRANSMIT 0x1
+#define BP_I2C_CTRL0_XFER_COUNT 0
+#define BM_I2C_CTRL0_XFER_COUNT 0x0000FFFF
+#define BF_I2C_CTRL0_XFER_COUNT(v) \
+ (((v) << 0) & BM_I2C_CTRL0_XFER_COUNT)
+HW_REGISTER(HW_I2C_TIMING0, REGS_I2C_BASE, 0x00000010)
+#define HW_I2C_TIMING0_ADDR (REGS_I2C_BASE + 0x00000010)
+#define BP_I2C_TIMING0_HIGH_COUNT 16
+#define BM_I2C_TIMING0_HIGH_COUNT 0x03FF0000
+#define BF_I2C_TIMING0_HIGH_COUNT(v) \
+ (((v) << 16) & BM_I2C_TIMING0_HIGH_COUNT)
+#define BP_I2C_TIMING0_RCV_COUNT 0
+#define BM_I2C_TIMING0_RCV_COUNT 0x000003FF
+#define BF_I2C_TIMING0_RCV_COUNT(v) \
+ (((v) << 0) & BM_I2C_TIMING0_RCV_COUNT)
+HW_REGISTER(HW_I2C_TIMING1, REGS_I2C_BASE, 0x00000020)
+#define HW_I2C_TIMING1_ADDR (REGS_I2C_BASE + 0x00000020)
+#define BP_I2C_TIMING1_LOW_COUNT 16
+#define BM_I2C_TIMING1_LOW_COUNT 0x03FF0000
+#define BF_I2C_TIMING1_LOW_COUNT(v) \
+ (((v) << 16) & BM_I2C_TIMING1_LOW_COUNT)
+#define BP_I2C_TIMING1_XMIT_COUNT 0
+#define BM_I2C_TIMING1_XMIT_COUNT 0x000003FF
+#define BF_I2C_TIMING1_XMIT_COUNT(v) \
+ (((v) << 0) & BM_I2C_TIMING1_XMIT_COUNT)
+HW_REGISTER(HW_I2C_TIMING2, REGS_I2C_BASE, 0x00000030)
+#define HW_I2C_TIMING2_ADDR (REGS_I2C_BASE + 0x00000030)
+#define BP_I2C_TIMING2_BUS_FREE 16
+#define BM_I2C_TIMING2_BUS_FREE 0x03FF0000
+#define BF_I2C_TIMING2_BUS_FREE(v) \
+ (((v) << 16) & BM_I2C_TIMING2_BUS_FREE)
+#define BP_I2C_TIMING2_LEADIN_COUNT 0
+#define BM_I2C_TIMING2_LEADIN_COUNT 0x000003FF
+#define BF_I2C_TIMING2_LEADIN_COUNT(v) \
+ (((v) << 0) & BM_I2C_TIMING2_LEADIN_COUNT)
+HW_REGISTER(HW_I2C_CTRL1, REGS_I2C_BASE, 0x00000040)
+#define HW_I2C_CTRL1_ADDR (REGS_I2C_BASE + 0x00000040)
+#define BM_I2C_CTRL1_CLR_GOT_A_NAK 0x10000000
+#define BV_I2C_CTRL1_CLR_GOT_A_NAK__DO_NOTHING 0x0
+#define BV_I2C_CTRL1_CLR_GOT_A_NAK__CLEAR 0x1
+#define BM_I2C_CTRL1_ACK_MODE 0x08000000
+#define BV_I2C_CTRL1_ACK_MODE__ACK_AFTER_HOLD_LOW 0x0
+#define BV_I2C_CTRL1_ACK_MODE__ACK_BEFORE_HOLD_LOW 0x1
+#define BM_I2C_CTRL1_FORCE_DATA_IDLE 0x04000000
+#define BM_I2C_CTRL1_FORCE_CLK_IDLE 0x02000000
+#define BM_I2C_CTRL1_BCAST_SLAVE_EN 0x01000000
+#define BV_I2C_CTRL1_BCAST_SLAVE_EN__NO_BCAST 0x0
+#define BV_I2C_CTRL1_BCAST_SLAVE_EN__WATCH_BCAST 0x1
+#define BP_I2C_CTRL1_SLAVE_ADDRESS_BYTE 16
+#define BM_I2C_CTRL1_SLAVE_ADDRESS_BYTE 0x00FF0000
+#define BF_I2C_CTRL1_SLAVE_ADDRESS_BYTE(v) \
+ (((v) << 16) & BM_I2C_CTRL1_SLAVE_ADDRESS_BYTE)
+#define BM_I2C_CTRL1_BUS_FREE_IRQ_EN 0x00008000
+#define BV_I2C_CTRL1_BUS_FREE_IRQ_EN__DISABLED 0x0
+#define BV_I2C_CTRL1_BUS_FREE_IRQ_EN__ENABLED 0x1
+#define BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ_EN 0x00004000
+#define BV_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ_EN__DISABLED 0x0
+#define BV_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ_EN__ENABLED 0x1
+#define BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ_EN 0x00002000
+#define BV_I2C_CTRL1_NO_SLAVE_ACK_IRQ_EN__DISABLED 0x0
+#define BV_I2C_CTRL1_NO_SLAVE_ACK_IRQ_EN__ENABLED 0x1
+#define BM_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ_EN 0x00001000
+#define BV_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ_EN__DISABLED 0x0
+#define BV_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ_EN__ENABLED 0x1
+#define BM_I2C_CTRL1_EARLY_TERM_IRQ_EN 0x00000800
+#define BV_I2C_CTRL1_EARLY_TERM_IRQ_EN__DISABLED 0x0
+#define BV_I2C_CTRL1_EARLY_TERM_IRQ_EN__ENABLED 0x1
+#define BM_I2C_CTRL1_MASTER_LOSS_IRQ_EN 0x00000400
+#define BV_I2C_CTRL1_MASTER_LOSS_IRQ_EN__DISABLED 0x0
+#define BV_I2C_CTRL1_MASTER_LOSS_IRQ_EN__ENABLED 0x1
+#define BM_I2C_CTRL1_SLAVE_STOP_IRQ_EN 0x00000200
+#define BV_I2C_CTRL1_SLAVE_STOP_IRQ_EN__DISABLED 0x0
+#define BV_I2C_CTRL1_SLAVE_STOP_IRQ_EN__ENABLED 0x1
+#define BM_I2C_CTRL1_SLAVE_IRQ_EN 0x00000100
+#define BV_I2C_CTRL1_SLAVE_IRQ_EN__DISABLED 0x0
+#define BV_I2C_CTRL1_SLAVE_IRQ_EN__ENABLED 0x1
+#define BM_I2C_CTRL1_BUS_FREE_IRQ 0x00000080
+#define BV_I2C_CTRL1_BUS_FREE_IRQ__NO_REQUEST 0x0
+#define BV_I2C_CTRL1_BUS_FREE_IRQ__REQUEST 0x1
+#define BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ 0x00000040
+#define BV_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ__NO_REQUEST 0x0
+#define BV_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ__REQUEST 0x1
+#define BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ 0x00000020
+#define BV_I2C_CTRL1_NO_SLAVE_ACK_IRQ__NO_REQUEST 0x0
+#define BV_I2C_CTRL1_NO_SLAVE_ACK_IRQ__REQUEST 0x1
+#define BM_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ 0x00000010
+#define BV_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ__NO_REQUEST 0x0
+#define BV_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ__REQUEST 0x1
+#define BM_I2C_CTRL1_EARLY_TERM_IRQ 0x00000008
+#define BV_I2C_CTRL1_EARLY_TERM_IRQ__NO_REQUEST 0x0
+#define BV_I2C_CTRL1_EARLY_TERM_IRQ__REQUEST 0x1
+#define BM_I2C_CTRL1_MASTER_LOSS_IRQ 0x00000004
+#define BV_I2C_CTRL1_MASTER_LOSS_IRQ__NO_REQUEST 0x0
+#define BV_I2C_CTRL1_MASTER_LOSS_IRQ__REQUEST 0x1
+#define BM_I2C_CTRL1_SLAVE_STOP_IRQ 0x00000002
+#define BV_I2C_CTRL1_SLAVE_STOP_IRQ__NO_REQUEST 0x0
+#define BV_I2C_CTRL1_SLAVE_STOP_IRQ__REQUEST 0x1
+#define BM_I2C_CTRL1_SLAVE_IRQ 0x00000001
+#define BV_I2C_CTRL1_SLAVE_IRQ__NO_REQUEST 0x0
+#define BV_I2C_CTRL1_SLAVE_IRQ__REQUEST 0x1
+HW_REGISTER_0(HW_I2C_STAT, REGS_I2C_BASE, 0x00000050)
+#define HW_I2C_STAT_ADDR (REGS_I2C_BASE + 0x00000050)
+#define BM_I2C_STAT_MASTER_PRESENT 0x80000000
+#define BV_I2C_STAT_MASTER_PRESENT__UNAVAILABLE 0x0
+#define BV_I2C_STAT_MASTER_PRESENT__AVAILABLE 0x1
+#define BM_I2C_STAT_SLAVE_PRESENT 0x40000000
+#define BV_I2C_STAT_SLAVE_PRESENT__UNAVAILABLE 0x0
+#define BV_I2C_STAT_SLAVE_PRESENT__AVAILABLE 0x1
+#define BM_I2C_STAT_ANY_ENABLED_IRQ 0x20000000
+#define BV_I2C_STAT_ANY_ENABLED_IRQ__NO_REQUESTS 0x0
+#define BV_I2C_STAT_ANY_ENABLED_IRQ__AT_LEAST_ONE_REQUEST 0x1
+#define BM_I2C_STAT_GOT_A_NAK 0x10000000
+#define BV_I2C_STAT_GOT_A_NAK__NO_NAK 0x0
+#define BV_I2C_STAT_GOT_A_NAK__DETECTED_NAK 0x1
+#define BP_I2C_STAT_RCVD_SLAVE_ADDR 16
+#define BM_I2C_STAT_RCVD_SLAVE_ADDR 0x00FF0000
+#define BF_I2C_STAT_RCVD_SLAVE_ADDR(v) \
+ (((v) << 16) & BM_I2C_STAT_RCVD_SLAVE_ADDR)
+#define BM_I2C_STAT_SLAVE_ADDR_EQ_ZERO 0x00008000
+#define BV_I2C_STAT_SLAVE_ADDR_EQ_ZERO__ZERO_NOT_MATCHED 0x0
+#define BV_I2C_STAT_SLAVE_ADDR_EQ_ZERO__WAS_ZERO 0x1
+#define BM_I2C_STAT_SLAVE_FOUND 0x00004000
+#define BV_I2C_STAT_SLAVE_FOUND__IDLE 0x0
+#define BV_I2C_STAT_SLAVE_FOUND__WAITING 0x1
+#define BM_I2C_STAT_SLAVE_SEARCHING 0x00002000
+#define BV_I2C_STAT_SLAVE_SEARCHING__IDLE 0x0
+#define BV_I2C_STAT_SLAVE_SEARCHING__ACTIVE 0x1
+#define BM_I2C_STAT_DATA_ENGINE_DMA_WAIT 0x00001000
+#define BV_I2C_STAT_DATA_ENGINE_DMA_WAIT__CONTINUE 0x0
+#define BV_I2C_STAT_DATA_ENGINE_DMA_WAIT__WAITING 0x1
+#define BM_I2C_STAT_BUS_BUSY 0x00000800
+#define BV_I2C_STAT_BUS_BUSY__IDLE 0x0
+#define BV_I2C_STAT_BUS_BUSY__BUSY 0x1
+#define BM_I2C_STAT_CLK_GEN_BUSY 0x00000400
+#define BV_I2C_STAT_CLK_GEN_BUSY__IDLE 0x0
+#define BV_I2C_STAT_CLK_GEN_BUSY__BUSY 0x1
+#define BM_I2C_STAT_DATA_ENGINE_BUSY 0x00000200
+#define BV_I2C_STAT_DATA_ENGINE_BUSY__IDLE 0x0
+#define BV_I2C_STAT_DATA_ENGINE_BUSY__BUSY 0x1
+#define BM_I2C_STAT_SLAVE_BUSY 0x00000100
+#define BV_I2C_STAT_SLAVE_BUSY__IDLE 0x0
+#define BV_I2C_STAT_SLAVE_BUSY__BUSY 0x1
+#define BM_I2C_STAT_BUS_FREE_IRQ_SUMMARY 0x00000080
+#define BV_I2C_STAT_BUS_FREE_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_I2C_STAT_BUS_FREE_IRQ_SUMMARY__REQUEST 0x1
+#define BM_I2C_STAT_DATA_ENGINE_CMPLT_IRQ_SUMMARY 0x00000040
+#define BV_I2C_STAT_DATA_ENGINE_CMPLT_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_I2C_STAT_DATA_ENGINE_CMPLT_IRQ_SUMMARY__REQUEST 0x1
+#define BM_I2C_STAT_NO_SLAVE_ACK_IRQ_SUMMARY 0x00000020
+#define BV_I2C_STAT_NO_SLAVE_ACK_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_I2C_STAT_NO_SLAVE_ACK_IRQ_SUMMARY__REQUEST 0x1
+#define BM_I2C_STAT_OVERSIZE_XFER_TERM_IRQ_SUMMARY 0x00000010
+#define BV_I2C_STAT_OVERSIZE_XFER_TERM_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_I2C_STAT_OVERSIZE_XFER_TERM_IRQ_SUMMARY__REQUEST 0x1
+#define BM_I2C_STAT_EARLY_TERM_IRQ_SUMMARY 0x00000008
+#define BV_I2C_STAT_EARLY_TERM_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_I2C_STAT_EARLY_TERM_IRQ_SUMMARY__REQUEST 0x1
+#define BM_I2C_STAT_MASTER_LOSS_IRQ_SUMMARY 0x00000004
+#define BV_I2C_STAT_MASTER_LOSS_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_I2C_STAT_MASTER_LOSS_IRQ_SUMMARY__REQUEST 0x1
+#define BM_I2C_STAT_SLAVE_STOP_IRQ_SUMMARY 0x00000002
+#define BV_I2C_STAT_SLAVE_STOP_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_I2C_STAT_SLAVE_STOP_IRQ_SUMMARY__REQUEST 0x1
+#define BM_I2C_STAT_SLAVE_IRQ_SUMMARY 0x00000001
+#define BV_I2C_STAT_SLAVE_IRQ_SUMMARY__NO_REQUEST 0x0
+#define BV_I2C_STAT_SLAVE_IRQ_SUMMARY__REQUEST 0x1
+HW_REGISTER_0(HW_I2C_DATA, REGS_I2C_BASE, 0x00000060)
+#define HW_I2C_DATA_ADDR (REGS_I2C_BASE + 0x00000060)
+#define BP_I2C_DATA_DATA 0
+#define BM_I2C_DATA_DATA 0xFFFFFFFF
+#define BF_I2C_DATA_DATA(v) (v)
+HW_REGISTER(HW_I2C_DEBUG0, REGS_I2C_BASE, 0x00000070)
+#define HW_I2C_DEBUG0_ADDR (REGS_I2C_BASE + 0x00000070)
+#define BM_I2C_DEBUG0_DMAREQ 0x80000000
+#define BM_I2C_DEBUG0_DMAENDCMD 0x40000000
+#define BM_I2C_DEBUG0_DMAKICK 0x20000000
+#define BM_I2C_DEBUG0_DMATERMINATE 0x10000000
+#define BP_I2C_DEBUG0_TBD 26
+#define BM_I2C_DEBUG0_TBD 0x0C000000
+#define BF_I2C_DEBUG0_TBD(v) \
+ (((v) << 26) & BM_I2C_DEBUG0_TBD)
+#define BP_I2C_DEBUG0_DMA_STATE 16
+#define BM_I2C_DEBUG0_DMA_STATE 0x03FF0000
+#define BF_I2C_DEBUG0_DMA_STATE(v) \
+ (((v) << 16) & BM_I2C_DEBUG0_DMA_STATE)
+#define BM_I2C_DEBUG0_START_TOGGLE 0x00008000
+#define BM_I2C_DEBUG0_STOP_TOGGLE 0x00004000
+#define BM_I2C_DEBUG0_GRAB_TOGGLE 0x00002000
+#define BM_I2C_DEBUG0_CHANGE_TOGGLE 0x00001000
+#define BM_I2C_DEBUG0_TESTMODE 0x00000800
+#define BM_I2C_DEBUG0_SLAVE_HOLD_CLK 0x00000400
+#define BP_I2C_DEBUG0_SLAVE_STATE 0
+#define BM_I2C_DEBUG0_SLAVE_STATE 0x000003FF
+#define BF_I2C_DEBUG0_SLAVE_STATE(v) \
+ (((v) << 0) & BM_I2C_DEBUG0_SLAVE_STATE)
+HW_REGISTER(HW_I2C_DEBUG1, REGS_I2C_BASE, 0x00000080)
+#define HW_I2C_DEBUG1_ADDR (REGS_I2C_BASE + 0x00000080)
+#define BM_I2C_DEBUG1_I2C_CLK_IN 0x80000000
+#define BM_I2C_DEBUG1_I2C_DATA_IN 0x40000000
+#define BP_I2C_DEBUG1_DMA_BYTE_ENABLES 24
+#define BM_I2C_DEBUG1_DMA_BYTE_ENABLES 0x0F000000
+#define BF_I2C_DEBUG1_DMA_BYTE_ENABLES(v) \
+ (((v) << 24) & BM_I2C_DEBUG1_DMA_BYTE_ENABLES)
+#define BP_I2C_DEBUG1_CLK_GEN_STATE 16
+#define BM_I2C_DEBUG1_CLK_GEN_STATE 0x00FF0000
+#define BF_I2C_DEBUG1_CLK_GEN_STATE(v) \
+ (((v) << 16) & BM_I2C_DEBUG1_CLK_GEN_STATE)
+#define BP_I2C_DEBUG1_LST_MODE 9
+#define BM_I2C_DEBUG1_LST_MODE 0x00000600
+#define BF_I2C_DEBUG1_LST_MODE(v) \
+ (((v) << 9) & BM_I2C_DEBUG1_LST_MODE)
+#define BV_I2C_DEBUG1_LST_MODE__BCAST 0x0
+#define BV_I2C_DEBUG1_LST_MODE__MY_WRITE 0x1
+#define BV_I2C_DEBUG1_LST_MODE__MY_READ 0x2
+#define BV_I2C_DEBUG1_LST_MODE__NOT_ME 0x3
+#define BM_I2C_DEBUG1_LOCAL_SLAVE_TEST 0x00000100
+#define BM_I2C_DEBUG1_FORCE_CLK_ON 0x00000010
+#define BM_I2C_DEBUG1_FORCE_ARB_LOSS 0x00000008
+#define BM_I2C_DEBUG1_FORCE_RCV_ACK 0x00000004
+#define BM_I2C_DEBUG1_FORCE_I2C_DATA_OE 0x00000002
+#define BM_I2C_DEBUG1_FORCE_I2C_CLK_OE 0x00000001
+HW_REGISTER_0(HW_I2C_VERSION, REGS_I2C_BASE, 0x00000090)
+#define HW_I2C_VERSION_ADDR (REGS_I2C_BASE + 0x00000090)
+#define BP_I2C_VERSION_MAJOR 24
+#define BM_I2C_VERSION_MAJOR 0xFF000000
+#define BF_I2C_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_I2C_VERSION_MAJOR)
+#define BP_I2C_VERSION_MINOR 16
+#define BM_I2C_VERSION_MINOR 0x00FF0000
+#define BF_I2C_VERSION_MINOR(v) \
+ (((v) << 16) & BM_I2C_VERSION_MINOR)
+#define BP_I2C_VERSION_STEP 0
+#define BM_I2C_VERSION_STEP 0x0000FFFF
+#define BF_I2C_VERSION_STEP(v) \
+ (((v) << 0) & BM_I2C_VERSION_STEP)
+#endif /* __ARCH_ARM___I2C_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-icoll.h b/arch/arm/mach-stmp3xxx/include/mach/regs-icoll.h
new file mode 100644
index 000000000000..fda52a92841c
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-icoll.h
@@ -0,0 +1,212 @@
+/*
+ * STMP ICOLL Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___ICOLL_H
+#define __ARCH_ARM___ICOLL_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_ICOLL_BASE (REGS_BASE + 0x0)
+#define REGS_ICOLL_BASE_PHYS (0x80000000)
+#define REGS_ICOLL_SIZE 0x00002000
+HW_REGISTER(HW_ICOLL_VECTOR, REGS_ICOLL_BASE, 0x00000000)
+#define HW_ICOLL_VECTOR_ADDR (REGS_ICOLL_BASE + 0x00000000)
+#define BP_ICOLL_VECTOR_IRQVECTOR 2
+#define BM_ICOLL_VECTOR_IRQVECTOR 0xFFFFFFFC
+#define BF_ICOLL_VECTOR_IRQVECTOR(v) \
+ (((v) << 2) & BM_ICOLL_VECTOR_IRQVECTOR)
+HW_REGISTER_0(HW_ICOLL_LEVELACK, REGS_ICOLL_BASE, 0x00000010)
+#define HW_ICOLL_LEVELACK_ADDR (REGS_ICOLL_BASE + 0x00000010)
+#define BP_ICOLL_LEVELACK_IRQLEVELACK 0
+#define BM_ICOLL_LEVELACK_IRQLEVELACK 0x0000000F
+#define BF_ICOLL_LEVELACK_IRQLEVELACK(v) \
+ (((v) << 0) & BM_ICOLL_LEVELACK_IRQLEVELACK)
+#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 0x1
+#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL1 0x2
+#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL2 0x4
+#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL3 0x8
+HW_REGISTER(HW_ICOLL_CTRL, REGS_ICOLL_BASE, 0x00000020)
+#define HW_ICOLL_CTRL_ADDR (REGS_ICOLL_BASE + 0x00000020)
+#define BM_ICOLL_CTRL_SFTRST 0x80000000
+#define BV_ICOLL_CTRL_SFTRST__RUN 0x0
+#define BV_ICOLL_CTRL_SFTRST__IN_RESET 0x1
+#define BM_ICOLL_CTRL_CLKGATE 0x40000000
+#define BV_ICOLL_CTRL_CLKGATE__RUN 0x0
+#define BV_ICOLL_CTRL_CLKGATE__NO_CLOCKS 0x1
+#define BP_ICOLL_CTRL_VECTOR_PITCH 21
+#define BM_ICOLL_CTRL_VECTOR_PITCH 0x00E00000
+#define BF_ICOLL_CTRL_VECTOR_PITCH(v) \
+ (((v) << 21) & BM_ICOLL_CTRL_VECTOR_PITCH)
+#define BV_ICOLL_CTRL_VECTOR_PITCH__DEFAULT_BY4 0x0
+#define BV_ICOLL_CTRL_VECTOR_PITCH__BY4 0x1
+#define BV_ICOLL_CTRL_VECTOR_PITCH__BY8 0x2
+#define BV_ICOLL_CTRL_VECTOR_PITCH__BY12 0x3
+#define BV_ICOLL_CTRL_VECTOR_PITCH__BY16 0x4
+#define BV_ICOLL_CTRL_VECTOR_PITCH__BY20 0x5
+#define BV_ICOLL_CTRL_VECTOR_PITCH__BY24 0x6
+#define BV_ICOLL_CTRL_VECTOR_PITCH__BY28 0x7
+#define BM_ICOLL_CTRL_BYPASS_FSM 0x00100000
+#define BV_ICOLL_CTRL_BYPASS_FSM__NORMAL 0x0
+#define BV_ICOLL_CTRL_BYPASS_FSM__BYPASS 0x1
+#define BM_ICOLL_CTRL_NO_NESTING 0x00080000
+#define BV_ICOLL_CTRL_NO_NESTING__NORMAL 0x0
+#define BV_ICOLL_CTRL_NO_NESTING__NO_NEST 0x1
+#define BM_ICOLL_CTRL_ARM_RSE_MODE 0x00040000
+#define BM_ICOLL_CTRL_FIQ_FINAL_ENABLE 0x00020000
+#define BV_ICOLL_CTRL_FIQ_FINAL_ENABLE__DISABLE 0x0
+#define BV_ICOLL_CTRL_FIQ_FINAL_ENABLE__ENABLE 0x1
+#define BM_ICOLL_CTRL_IRQ_FINAL_ENABLE 0x00010000
+#define BV_ICOLL_CTRL_IRQ_FINAL_ENABLE__DISABLE 0x0
+#define BV_ICOLL_CTRL_IRQ_FINAL_ENABLE__ENABLE 0x1
+HW_REGISTER(HW_ICOLL_VBASE, REGS_ICOLL_BASE, 0x00000040)
+#define HW_ICOLL_VBASE_ADDR (REGS_ICOLL_BASE + 0x00000040)
+#define BP_ICOLL_VBASE_TABLE_ADDRESS 2
+#define BM_ICOLL_VBASE_TABLE_ADDRESS 0xFFFFFFFC
+#define BF_ICOLL_VBASE_TABLE_ADDRESS(v) \
+ (((v) << 2) & BM_ICOLL_VBASE_TABLE_ADDRESS)
+HW_REGISTER_0(HW_ICOLL_STAT, REGS_ICOLL_BASE, 0x00000070)
+#define HW_ICOLL_STAT_ADDR (REGS_ICOLL_BASE + 0x00000070)
+#define BP_ICOLL_STAT_VECTOR_NUMBER 0
+#define BM_ICOLL_STAT_VECTOR_NUMBER 0x0000007F
+#define BF_ICOLL_STAT_VECTOR_NUMBER(v) \
+ (((v) << 0) & BM_ICOLL_STAT_VECTOR_NUMBER)
+/*
+ * multi-register-define name HW_ICOLL_RAWn
+ * base 0x000000A0
+ * count 4
+ * offset 0x10
+ */
+HW_REGISTER_0_INDEXED(HW_ICOLL_RAWn, REGS_ICOLL_BASE, 0x000000a0, 0x10)
+#define BP_ICOLL_RAWn_RAW_IRQS 0
+#define BM_ICOLL_RAWn_RAW_IRQS 0xFFFFFFFF
+#define BF_ICOLL_RAWn_RAW_IRQS(v) (v)
+/*
+ * multi-register-define name HW_ICOLL_INTERRUPTn
+ * base 0x00000120
+ * count 128
+ * offset 0x10
+ */
+HW_REGISTER_INDEXED(HW_ICOLL_INTERRUPTn, REGS_ICOLL_BASE, 0x00000120, 0x10)
+#define BM_ICOLL_INTERRUPTn_ENFIQ 0x00000010
+#define BV_ICOLL_INTERRUPTn_ENFIQ__DISABLE 0x0
+#define BV_ICOLL_INTERRUPTn_ENFIQ__ENABLE 0x1
+#define BM_ICOLL_INTERRUPTn_SOFTIRQ 0x00000008
+#define BV_ICOLL_INTERRUPTn_SOFTIRQ__NO_INTERRUPT 0x0
+#define BV_ICOLL_INTERRUPTn_SOFTIRQ__FORCE_INTERRUPT 0x1
+#define BM_ICOLL_INTERRUPTn_ENABLE 0x00000004
+#define BV_ICOLL_INTERRUPTn_ENABLE__DISABLE 0x0
+#define BV_ICOLL_INTERRUPTn_ENABLE__ENABLE 0x1
+#define BP_ICOLL_INTERRUPTn_PRIORITY 0
+#define BM_ICOLL_INTERRUPTn_PRIORITY 0x00000003
+#define BF_ICOLL_INTERRUPTn_PRIORITY(v) \
+ (((v) << 0) & BM_ICOLL_INTERRUPTn_PRIORITY)
+#define BV_ICOLL_INTERRUPTn_PRIORITY__LEVEL0 0x0
+#define BV_ICOLL_INTERRUPTn_PRIORITY__LEVEL1 0x1
+#define BV_ICOLL_INTERRUPTn_PRIORITY__LEVEL2 0x2
+#define BV_ICOLL_INTERRUPTn_PRIORITY__LEVEL3 0x3
+HW_REGISTER(HW_ICOLL_DEBUG, REGS_ICOLL_BASE, 0x00001120)
+#define HW_ICOLL_DEBUG_ADDR (REGS_ICOLL_BASE + 0x00001120)
+#define BP_ICOLL_DEBUG_INSERVICE 28
+#define BM_ICOLL_DEBUG_INSERVICE 0xF0000000
+#define BF_ICOLL_DEBUG_INSERVICE(v) \
+ (((v) << 28) & BM_ICOLL_DEBUG_INSERVICE)
+#define BV_ICOLL_DEBUG_INSERVICE__LEVEL0 0x1
+#define BV_ICOLL_DEBUG_INSERVICE__LEVEL1 0x2
+#define BV_ICOLL_DEBUG_INSERVICE__LEVEL2 0x4
+#define BV_ICOLL_DEBUG_INSERVICE__LEVEL3 0x8
+#define BP_ICOLL_DEBUG_LEVEL_REQUESTS 24
+#define BM_ICOLL_DEBUG_LEVEL_REQUESTS 0x0F000000
+#define BF_ICOLL_DEBUG_LEVEL_REQUESTS(v) \
+ (((v) << 24) & BM_ICOLL_DEBUG_LEVEL_REQUESTS)
+#define BV_ICOLL_DEBUG_LEVEL_REQUESTS__LEVEL0 0x1
+#define BV_ICOLL_DEBUG_LEVEL_REQUESTS__LEVEL1 0x2
+#define BV_ICOLL_DEBUG_LEVEL_REQUESTS__LEVEL2 0x4
+#define BV_ICOLL_DEBUG_LEVEL_REQUESTS__LEVEL3 0x8
+#define BP_ICOLL_DEBUG_REQUESTS_BY_LEVEL 20
+#define BM_ICOLL_DEBUG_REQUESTS_BY_LEVEL 0x00F00000
+#define BF_ICOLL_DEBUG_REQUESTS_BY_LEVEL(v) \
+ (((v) << 20) & BM_ICOLL_DEBUG_REQUESTS_BY_LEVEL)
+#define BV_ICOLL_DEBUG_REQUESTS_BY_LEVEL__LEVEL0 0x1
+#define BV_ICOLL_DEBUG_REQUESTS_BY_LEVEL__LEVEL1 0x2
+#define BV_ICOLL_DEBUG_REQUESTS_BY_LEVEL__LEVEL2 0x4
+#define BV_ICOLL_DEBUG_REQUESTS_BY_LEVEL__LEVEL3 0x8
+#define BM_ICOLL_DEBUG_FIQ 0x00020000
+#define BV_ICOLL_DEBUG_FIQ__NO_FIQ_REQUESTED 0x0
+#define BV_ICOLL_DEBUG_FIQ__FIQ_REQUESTED 0x1
+#define BM_ICOLL_DEBUG_IRQ 0x00010000
+#define BV_ICOLL_DEBUG_IRQ__NO_IRQ_REQUESTED 0x0
+#define BV_ICOLL_DEBUG_IRQ__IRQ_REQUESTED 0x1
+#define BP_ICOLL_DEBUG_VECTOR_FSM 0
+#define BM_ICOLL_DEBUG_VECTOR_FSM 0x000003FF
+#define BF_ICOLL_DEBUG_VECTOR_FSM(v) \
+ (((v) << 0) & BM_ICOLL_DEBUG_VECTOR_FSM)
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_IDLE 0x000
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_MULTICYCLE1 0x001
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_MULTICYCLE2 0x002
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_PENDING 0x004
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_MULTICYCLE3 0x008
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_MULTICYCLE4 0x010
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_ISR_RUNNING1 0x020
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_ISR_RUNNING2 0x040
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_ISR_RUNNING3 0x080
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_MULTICYCLE5 0x100
+#define BV_ICOLL_DEBUG_VECTOR_FSM__FSM_MULTICYCLE6 0x200
+HW_REGISTER(HW_ICOLL_DBGREAD0, REGS_ICOLL_BASE, 0x00001130)
+#define HW_ICOLL_DBGREAD0_ADDR (REGS_ICOLL_BASE + 0x00001130)
+#define BP_ICOLL_DBGREAD0_VALUE 0
+#define BM_ICOLL_DBGREAD0_VALUE 0xFFFFFFFF
+#define BF_ICOLL_DBGREAD0_VALUE(v) (v)
+HW_REGISTER(HW_ICOLL_DBGREAD1, REGS_ICOLL_BASE, 0x00001140)
+#define HW_ICOLL_DBGREAD1_ADDR (REGS_ICOLL_BASE + 0x00001140)
+#define BP_ICOLL_DBGREAD1_VALUE 0
+#define BM_ICOLL_DBGREAD1_VALUE 0xFFFFFFFF
+#define BF_ICOLL_DBGREAD1_VALUE(v) (v)
+HW_REGISTER(HW_ICOLL_DBGFLAG, REGS_ICOLL_BASE, 0x00001150)
+#define HW_ICOLL_DBGFLAG_ADDR (REGS_ICOLL_BASE + 0x00001150)
+#define BP_ICOLL_DBGFLAG_FLAG 0
+#define BM_ICOLL_DBGFLAG_FLAG 0x0000FFFF
+#define BF_ICOLL_DBGFLAG_FLAG(v) \
+ (((v) << 0) & BM_ICOLL_DBGFLAG_FLAG)
+/*
+ * multi-register-define name HW_ICOLL_DBGREQUESTn
+ * base 0x00001160
+ * count 4
+ * offset 0x10
+ */
+HW_REGISTER_0_INDEXED(HW_ICOLL_DBGREQUESTn, REGS_ICOLL_BASE, 0x00001160,
+ 0x10)
+#define BP_ICOLL_DBGREQUESTn_BITS 0
+#define BM_ICOLL_DBGREQUESTn_BITS 0xFFFFFFFF
+#define BF_ICOLL_DBGREQUESTn_BITS(v) (v)
+HW_REGISTER_0(HW_ICOLL_VERSION, REGS_ICOLL_BASE, 0x000011e0)
+#define HW_ICOLL_VERSION_ADDR (REGS_ICOLL_BASE + 0x000011e0)
+#define BP_ICOLL_VERSION_MAJOR 24
+#define BM_ICOLL_VERSION_MAJOR 0xFF000000
+#define BF_ICOLL_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_ICOLL_VERSION_MAJOR)
+#define BP_ICOLL_VERSION_MINOR 16
+#define BM_ICOLL_VERSION_MINOR 0x00FF0000
+#define BF_ICOLL_VERSION_MINOR(v) \
+ (((v) << 16) & BM_ICOLL_VERSION_MINOR)
+#define BP_ICOLL_VERSION_STEP 0
+#define BM_ICOLL_VERSION_STEP 0x0000FFFF
+#define BF_ICOLL_VERSION_STEP(v) \
+ (((v) << 0) & BM_ICOLL_VERSION_STEP)
+#endif /* __ARCH_ARM___ICOLL_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-ir.h b/arch/arm/mach-stmp3xxx/include/mach/regs-ir.h
new file mode 100644
index 000000000000..4ea39bf721ac
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-ir.h
@@ -0,0 +1,280 @@
+/*
+ * STMP IR Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___IR_H
+#define __ARCH_ARM___IR_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_IR_BASE (REGS_BASE + 0x78000)
+#define REGS_IR_BASE_PHYS (0x80078000)
+#define REGS_IR_SIZE 0x00002000
+HW_REGISTER(HW_IR_CTRL, REGS_IR_BASE, 0x00000000)
+#define HW_IR_CTRL_ADDR (REGS_IR_BASE + 0x00000000)
+#define BM_IR_CTRL_SFTRST 0x80000000
+#define BV_IR_CTRL_SFTRST__RUN 0x0
+#define BV_IR_CTRL_SFTRST__RESET 0x1
+#define BM_IR_CTRL_CLKGATE 0x40000000
+#define BP_IR_CTRL_MTA 24
+#define BM_IR_CTRL_MTA 0x07000000
+#define BF_IR_CTRL_MTA(v) \
+ (((v) << 24) & BM_IR_CTRL_MTA)
+#define BV_IR_CTRL_MTA__MTA_10MS 0x0
+#define BV_IR_CTRL_MTA__MTA_5MS 0x1
+#define BV_IR_CTRL_MTA__MTA_1MS 0x2
+#define BV_IR_CTRL_MTA__MTA_500US 0x3
+#define BV_IR_CTRL_MTA__MTA_100US 0x4
+#define BV_IR_CTRL_MTA__MTA_50US 0x5
+#define BV_IR_CTRL_MTA__MTA_10US 0x6
+#define BV_IR_CTRL_MTA__MTA_0 0x7
+#define BP_IR_CTRL_MODE 22
+#define BM_IR_CTRL_MODE 0x00C00000
+#define BF_IR_CTRL_MODE(v) \
+ (((v) << 22) & BM_IR_CTRL_MODE)
+#define BV_IR_CTRL_MODE__SIR 0x0
+#define BV_IR_CTRL_MODE__MIR 0x1
+#define BV_IR_CTRL_MODE__FIR 0x2
+#define BV_IR_CTRL_MODE__VFIR 0x3
+#define BP_IR_CTRL_SPEED 19
+#define BM_IR_CTRL_SPEED 0x00380000
+#define BF_IR_CTRL_SPEED(v) \
+ (((v) << 19) & BM_IR_CTRL_SPEED)
+#define BV_IR_CTRL_SPEED__SPD000 0x0
+#define BV_IR_CTRL_SPEED__SPD001 0x1
+#define BV_IR_CTRL_SPEED__SPD010 0x2
+#define BV_IR_CTRL_SPEED__SPD011 0x3
+#define BV_IR_CTRL_SPEED__SPD100 0x4
+#define BV_IR_CTRL_SPEED__SPD101 0x5
+#define BP_IR_CTRL_TC_TIME_DIV 8
+#define BM_IR_CTRL_TC_TIME_DIV 0x00003F00
+#define BF_IR_CTRL_TC_TIME_DIV(v) \
+ (((v) << 8) & BM_IR_CTRL_TC_TIME_DIV)
+#define BM_IR_CTRL_TC_TYPE 0x00000080
+#define BP_IR_CTRL_SIR_GAP 4
+#define BM_IR_CTRL_SIR_GAP 0x00000070
+#define BF_IR_CTRL_SIR_GAP(v) \
+ (((v) << 4) & BM_IR_CTRL_SIR_GAP)
+#define BV_IR_CTRL_SIR_GAP__GAP_10K 0x0
+#define BV_IR_CTRL_SIR_GAP__GAP_5K 0x1
+#define BV_IR_CTRL_SIR_GAP__GAP_1K 0x2
+#define BV_IR_CTRL_SIR_GAP__GAP_500 0x3
+#define BV_IR_CTRL_SIR_GAP__GAP_100 0x4
+#define BV_IR_CTRL_SIR_GAP__GAP_50 0x5
+#define BV_IR_CTRL_SIR_GAP__GAP_10 0x6
+#define BV_IR_CTRL_SIR_GAP__GAP_0 0x7
+#define BM_IR_CTRL_SIPEN 0x00000008
+#define BM_IR_CTRL_TCEN 0x00000004
+#define BM_IR_CTRL_TXEN 0x00000002
+#define BM_IR_CTRL_RXEN 0x00000001
+HW_REGISTER(HW_IR_TXDMA, REGS_IR_BASE, 0x00000010)
+#define HW_IR_TXDMA_ADDR (REGS_IR_BASE + 0x00000010)
+#define BM_IR_TXDMA_RUN 0x80000000
+#define BM_IR_TXDMA_EMPTY 0x20000000
+#define BM_IR_TXDMA_INT 0x10000000
+#define BM_IR_TXDMA_CHANGE 0x08000000
+#define BP_IR_TXDMA_NEW_MTA 24
+#define BM_IR_TXDMA_NEW_MTA 0x07000000
+#define BF_IR_TXDMA_NEW_MTA(v) \
+ (((v) << 24) & BM_IR_TXDMA_NEW_MTA)
+#define BP_IR_TXDMA_NEW_MODE 22
+#define BM_IR_TXDMA_NEW_MODE 0x00C00000
+#define BF_IR_TXDMA_NEW_MODE(v) \
+ (((v) << 22) & BM_IR_TXDMA_NEW_MODE)
+#define BP_IR_TXDMA_NEW_SPEED 19
+#define BM_IR_TXDMA_NEW_SPEED 0x00380000
+#define BF_IR_TXDMA_NEW_SPEED(v) \
+ (((v) << 19) & BM_IR_TXDMA_NEW_SPEED)
+#define BM_IR_TXDMA_BOF_TYPE 0x00040000
+#define BP_IR_TXDMA_XBOFS 12
+#define BM_IR_TXDMA_XBOFS 0x0003F000
+#define BF_IR_TXDMA_XBOFS(v) \
+ (((v) << 12) & BM_IR_TXDMA_XBOFS)
+#define BP_IR_TXDMA_XFER_COUNT 0
+#define BM_IR_TXDMA_XFER_COUNT 0x00000FFF
+#define BF_IR_TXDMA_XFER_COUNT(v) \
+ (((v) << 0) & BM_IR_TXDMA_XFER_COUNT)
+HW_REGISTER(HW_IR_RXDMA, REGS_IR_BASE, 0x00000020)
+#define HW_IR_RXDMA_ADDR (REGS_IR_BASE + 0x00000020)
+#define BM_IR_RXDMA_RUN 0x80000000
+#define BP_IR_RXDMA_XFER_COUNT 0
+#define BM_IR_RXDMA_XFER_COUNT 0x000003FF
+#define BF_IR_RXDMA_XFER_COUNT(v) \
+ (((v) << 0) & BM_IR_RXDMA_XFER_COUNT)
+HW_REGISTER(HW_IR_DBGCTRL, REGS_IR_BASE, 0x00000030)
+#define HW_IR_DBGCTRL_ADDR (REGS_IR_BASE + 0x00000030)
+#define BM_IR_DBGCTRL_VFIRSWZ 0x00001000
+#define BV_IR_DBGCTRL_VFIRSWZ__NORMAL 0
+#define BV_IR_DBGCTRL_VFIRSWZ__SWAP 1
+#define BM_IR_DBGCTRL_RXFRMOFF 0x00000800
+#define BM_IR_DBGCTRL_RXCRCOFF 0x00000400
+#define BM_IR_DBGCTRL_RXINVERT 0x00000200
+#define BM_IR_DBGCTRL_TXFRMOFF 0x00000100
+#define BM_IR_DBGCTRL_TXCRCOFF 0x00000080
+#define BM_IR_DBGCTRL_TXINVERT 0x00000040
+#define BM_IR_DBGCTRL_INTLOOPBACK 0x00000020
+#define BM_IR_DBGCTRL_DUPLEX 0x00000010
+#define BM_IR_DBGCTRL_MIO_RX 0x00000008
+#define BM_IR_DBGCTRL_MIO_TX 0x00000004
+#define BM_IR_DBGCTRL_MIO_SCLK 0x00000002
+#define BM_IR_DBGCTRL_MIO_EN 0x00000001
+HW_REGISTER(HW_IR_INTR, REGS_IR_BASE, 0x00000040)
+#define HW_IR_INTR_ADDR (REGS_IR_BASE + 0x00000040)
+#define BM_IR_INTR_RXABORT_IRQ_EN 0x00400000
+#define BV_IR_INTR_RXABORT_IRQ_EN__DISABLED 0x0
+#define BV_IR_INTR_RXABORT_IRQ_EN__ENABLED 0x1
+#define BM_IR_INTR_SPEED_IRQ_EN 0x00200000
+#define BV_IR_INTR_SPEED_IRQ_EN__DISABLED 0x0
+#define BV_IR_INTR_SPEED_IRQ_EN__ENABLED 0x1
+#define BM_IR_INTR_RXOF_IRQ_EN 0x00100000
+#define BV_IR_INTR_RXOF_IRQ_EN__DISABLED 0x0
+#define BV_IR_INTR_RXOF_IRQ_EN__ENABLED 0x1
+#define BM_IR_INTR_TXUF_IRQ_EN 0x00080000
+#define BV_IR_INTR_TXUF_IRQ_EN__DISABLED 0x0
+#define BV_IR_INTR_TXUF_IRQ_EN__ENABLED 0x1
+#define BM_IR_INTR_TC_IRQ_EN 0x00040000
+#define BV_IR_INTR_TC_IRQ_EN__DISABLED 0x0
+#define BV_IR_INTR_TC_IRQ_EN__ENABLED 0x1
+#define BM_IR_INTR_RX_IRQ_EN 0x00020000
+#define BV_IR_INTR_RX_IRQ_EN__DISABLED 0x0
+#define BV_IR_INTR_RX_IRQ_EN__ENABLED 0x1
+#define BM_IR_INTR_TX_IRQ_EN 0x00010000
+#define BV_IR_INTR_TX_IRQ_EN__DISABLED 0x0
+#define BV_IR_INTR_TX_IRQ_EN__ENABLED 0x1
+#define BM_IR_INTR_RXABORT_IRQ 0x00000040
+#define BV_IR_INTR_RXABORT_IRQ__NO_REQUEST 0x0
+#define BV_IR_INTR_RXABORT_IRQ__REQUEST 0x1
+#define BM_IR_INTR_SPEED_IRQ 0x00000020
+#define BV_IR_INTR_SPEED_IRQ__NO_REQUEST 0x0
+#define BV_IR_INTR_SPEED_IRQ__REQUEST 0x1
+#define BM_IR_INTR_RXOF_IRQ 0x00000010
+#define BV_IR_INTR_RXOF_IRQ__NO_REQUEST 0x0
+#define BV_IR_INTR_RXOF_IRQ__REQUEST 0x1
+#define BM_IR_INTR_TXUF_IRQ 0x00000008
+#define BV_IR_INTR_TXUF_IRQ__NO_REQUEST 0x0
+#define BV_IR_INTR_TXUF_IRQ__REQUEST 0x1
+#define BM_IR_INTR_TC_IRQ 0x00000004
+#define BV_IR_INTR_TC_IRQ__NO_REQUEST 0x0
+#define BV_IR_INTR_TC_IRQ__REQUEST 0x1
+#define BM_IR_INTR_RX_IRQ 0x00000002
+#define BV_IR_INTR_RX_IRQ__NO_REQUEST 0x0
+#define BV_IR_INTR_RX_IRQ__REQUEST 0x1
+#define BM_IR_INTR_TX_IRQ 0x00000001
+#define BV_IR_INTR_TX_IRQ__NO_REQUEST 0x0
+#define BV_IR_INTR_TX_IRQ__REQUEST 0x1
+HW_REGISTER_0(HW_IR_DATA, REGS_IR_BASE, 0x00000050)
+#define HW_IR_DATA_ADDR (REGS_IR_BASE + 0x00000050)
+#define BP_IR_DATA_DATA 0
+#define BM_IR_DATA_DATA 0xFFFFFFFF
+#define BF_IR_DATA_DATA(v) (v)
+HW_REGISTER_0(HW_IR_STAT, REGS_IR_BASE, 0x00000060)
+#define HW_IR_STAT_ADDR (REGS_IR_BASE + 0x00000060)
+#define BM_IR_STAT_PRESENT 0x80000000
+#define BV_IR_STAT_PRESENT__UNAVAILABLE 0x0
+#define BV_IR_STAT_PRESENT__AVAILABLE 0x1
+#define BP_IR_STAT_MODE_ALLOWED 29
+#define BM_IR_STAT_MODE_ALLOWED 0x60000000
+#define BF_IR_STAT_MODE_ALLOWED(v) \
+ (((v) << 29) & BM_IR_STAT_MODE_ALLOWED)
+#define BV_IR_STAT_MODE_ALLOWED__VFIR 0x0
+#define BV_IR_STAT_MODE_ALLOWED__FIR 0x1
+#define BV_IR_STAT_MODE_ALLOWED__MIR 0x2
+#define BV_IR_STAT_MODE_ALLOWED__SIR 0x3
+#define BM_IR_STAT_ANY_IRQ 0x10000000
+#define BV_IR_STAT_ANY_IRQ__NO_REQUEST 0x0
+#define BV_IR_STAT_ANY_IRQ__REQUEST 0x1
+#define BM_IR_STAT_RXABORT_SUMMARY 0x00400000
+#define BV_IR_STAT_RXABORT_SUMMARY__NO_REQUEST 0x0
+#define BV_IR_STAT_RXABORT_SUMMARY__REQUEST 0x1
+#define BM_IR_STAT_SPEED_SUMMARY 0x00200000
+#define BV_IR_STAT_SPEED_SUMMARY__NO_REQUEST 0x0
+#define BV_IR_STAT_SPEED_SUMMARY__REQUEST 0x1
+#define BM_IR_STAT_RXOF_SUMMARY 0x00100000
+#define BV_IR_STAT_RXOF_SUMMARY__NO_REQUEST 0x0
+#define BV_IR_STAT_RXOF_SUMMARY__REQUEST 0x1
+#define BM_IR_STAT_TXUF_SUMMARY 0x00080000
+#define BV_IR_STAT_TXUF_SUMMARY__NO_REQUEST 0x0
+#define BV_IR_STAT_TXUF_SUMMARY__REQUEST 0x1
+#define BM_IR_STAT_TC_SUMMARY 0x00040000
+#define BV_IR_STAT_TC_SUMMARY__NO_REQUEST 0x0
+#define BV_IR_STAT_TC_SUMMARY__REQUEST 0x1
+#define BM_IR_STAT_RX_SUMMARY 0x00020000
+#define BV_IR_STAT_RX_SUMMARY__NO_REQUEST 0x0
+#define BV_IR_STAT_RX_SUMMARY__REQUEST 0x1
+#define BM_IR_STAT_TX_SUMMARY 0x00010000
+#define BV_IR_STAT_TX_SUMMARY__NO_REQUEST 0x0
+#define BV_IR_STAT_TX_SUMMARY__REQUEST 0x1
+#define BM_IR_STAT_MEDIA_BUSY 0x00000004
+#define BM_IR_STAT_RX_ACTIVE 0x00000002
+#define BM_IR_STAT_TX_ACTIVE 0x00000001
+HW_REGISTER(HW_IR_TCCTRL, REGS_IR_BASE, 0x00000070)
+#define HW_IR_TCCTRL_ADDR (REGS_IR_BASE + 0x00000070)
+#define BM_IR_TCCTRL_INIT 0x80000000
+#define BM_IR_TCCTRL_GO 0x40000000
+#define BM_IR_TCCTRL_BUSY 0x20000000
+#define BM_IR_TCCTRL_TEMIC 0x01000000
+#define BV_IR_TCCTRL_TEMIC__LOW 0x0
+#define BV_IR_TCCTRL_TEMIC__HIGH 0x1
+#define BP_IR_TCCTRL_EXT_DATA 16
+#define BM_IR_TCCTRL_EXT_DATA 0x00FF0000
+#define BF_IR_TCCTRL_EXT_DATA(v) \
+ (((v) << 16) & BM_IR_TCCTRL_EXT_DATA)
+#define BP_IR_TCCTRL_DATA 8
+#define BM_IR_TCCTRL_DATA 0x0000FF00
+#define BF_IR_TCCTRL_DATA(v) \
+ (((v) << 8) & BM_IR_TCCTRL_DATA)
+#define BP_IR_TCCTRL_ADDR 5
+#define BM_IR_TCCTRL_ADDR 0x000000E0
+#define BF_IR_TCCTRL_ADDR(v) \
+ (((v) << 5) & BM_IR_TCCTRL_ADDR)
+#define BP_IR_TCCTRL_INDX 1
+#define BM_IR_TCCTRL_INDX 0x0000001E
+#define BF_IR_TCCTRL_INDX(v) \
+ (((v) << 1) & BM_IR_TCCTRL_INDX)
+#define BM_IR_TCCTRL_C 0x00000001
+HW_REGISTER_0(HW_IR_SI_READ, REGS_IR_BASE, 0x00000080)
+#define HW_IR_SI_READ_ADDR (REGS_IR_BASE + 0x00000080)
+#define BM_IR_SI_READ_ABORT 0x00000100
+#define BP_IR_SI_READ_DATA 0
+#define BM_IR_SI_READ_DATA 0x000000FF
+#define BF_IR_SI_READ_DATA(v) \
+ (((v) << 0) & BM_IR_SI_READ_DATA)
+HW_REGISTER_0(HW_IR_DEBUG, REGS_IR_BASE, 0x00000090)
+#define HW_IR_DEBUG_ADDR (REGS_IR_BASE + 0x00000090)
+#define BM_IR_DEBUG_TXDMAKICK 0x00000020
+#define BM_IR_DEBUG_RXDMAKICK 0x00000010
+#define BM_IR_DEBUG_TXDMAEND 0x00000008
+#define BM_IR_DEBUG_RXDMAEND 0x00000004
+#define BM_IR_DEBUG_TXDMAREQ 0x00000002
+#define BM_IR_DEBUG_RXDMAREQ 0x00000001
+HW_REGISTER_0(HW_IR_VERSION, REGS_IR_BASE, 0x000000a0)
+#define HW_IR_VERSION_ADDR (REGS_IR_BASE + 0x000000a0)
+#define BP_IR_VERSION_MAJOR 24
+#define BM_IR_VERSION_MAJOR 0xFF000000
+#define BF_IR_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_IR_VERSION_MAJOR)
+#define BP_IR_VERSION_MINOR 16
+#define BM_IR_VERSION_MINOR 0x00FF0000
+#define BF_IR_VERSION_MINOR(v) \
+ (((v) << 16) & BM_IR_VERSION_MINOR)
+#define BP_IR_VERSION_STEP 0
+#define BM_IR_VERSION_STEP 0x0000FFFF
+#define BF_IR_VERSION_STEP(v) \
+ (((v) << 0) & BM_IR_VERSION_STEP)
+#endif /* __ARCH_ARM___IR_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-lcdif.h b/arch/arm/mach-stmp3xxx/include/mach/regs-lcdif.h
new file mode 100644
index 000000000000..df60f2ca576c
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-lcdif.h
@@ -0,0 +1,497 @@
+/*
+ * STMP LCDIF Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___LCDIF_H
+#define __ARCH_ARM___LCDIF_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_LCDIF_BASE (REGS_BASE + 0x30000)
+#define REGS_LCDIF_BASE_PHYS (0x80030000)
+#define REGS_LCDIF_SIZE 0x00002000
+HW_REGISTER(HW_LCDIF_CTRL, REGS_LCDIF_BASE, 0x00000000)
+#define HW_LCDIF_CTRL_ADDR (REGS_LCDIF_BASE + 0x00000000)
+#define BM_LCDIF_CTRL_SFTRST 0x80000000
+#define BM_LCDIF_CTRL_CLKGATE 0x40000000
+#define BM_LCDIF_CTRL_YCBCR422_INPUT 0x20000000
+#define BM_LCDIF_CTRL_WAIT_FOR_VSYNC_EDGE 0x08000000
+#define BM_LCDIF_CTRL_DATA_SHIFT_DIR 0x04000000
+#define BV_LCDIF_CTRL_DATA_SHIFT_DIR__TXDATA_SHIFT_LEFT 0x0
+#define BV_LCDIF_CTRL_DATA_SHIFT_DIR__TXDATA_SHIFT_RIGHT 0x1
+#define BP_LCDIF_CTRL_SHIFT_NUM_BITS 21
+#define BM_LCDIF_CTRL_SHIFT_NUM_BITS 0x03E00000
+#define BF_LCDIF_CTRL_SHIFT_NUM_BITS(v) \
+ (((v) << 21) & BM_LCDIF_CTRL_SHIFT_NUM_BITS)
+#define BM_LCDIF_CTRL_DVI_MODE 0x00100000
+#define BM_LCDIF_CTRL_BYPASS_COUNT 0x00080000
+#define BM_LCDIF_CTRL_VSYNC_MODE 0x00040000
+#define BM_LCDIF_CTRL_DOTCLK_MODE 0x00020000
+#define BM_LCDIF_CTRL_DATA_SELECT 0x00010000
+#define BV_LCDIF_CTRL_DATA_SELECT__CMD_MODE 0x0
+#define BV_LCDIF_CTRL_DATA_SELECT__DATA_MODE 0x1
+#define BP_LCDIF_CTRL_INPUT_DATA_SWIZZLE 14
+#define BM_LCDIF_CTRL_INPUT_DATA_SWIZZLE 0x0000C000
+#define BF_LCDIF_CTRL_INPUT_DATA_SWIZZLE(v) \
+ (((v) << 14) & BM_LCDIF_CTRL_INPUT_DATA_SWIZZLE)
+#define BV_LCDIF_CTRL_INPUT_DATA_SWIZZLE__NO_SWAP 0x0
+#define BV_LCDIF_CTRL_INPUT_DATA_SWIZZLE__LITTLE_ENDIAN 0x0
+#define BV_LCDIF_CTRL_INPUT_DATA_SWIZZLE__BIG_ENDIAN_SWAP 0x1
+#define BV_LCDIF_CTRL_INPUT_DATA_SWIZZLE__SWAP_ALL_BYTES 0x1
+#define BV_LCDIF_CTRL_INPUT_DATA_SWIZZLE__HWD_SWAP 0x2
+#define BV_LCDIF_CTRL_INPUT_DATA_SWIZZLE__HWD_BYTE_SWAP 0x3
+#define BP_LCDIF_CTRL_CSC_DATA_SWIZZLE 12
+#define BM_LCDIF_CTRL_CSC_DATA_SWIZZLE 0x00003000
+#define BF_LCDIF_CTRL_CSC_DATA_SWIZZLE(v) \
+ (((v) << 12) & BM_LCDIF_CTRL_CSC_DATA_SWIZZLE)
+#define BV_LCDIF_CTRL_CSC_DATA_SWIZZLE__NO_SWAP 0x0
+#define BV_LCDIF_CTRL_CSC_DATA_SWIZZLE__LITTLE_ENDIAN 0x0
+#define BV_LCDIF_CTRL_CSC_DATA_SWIZZLE__BIG_ENDIAN_SWAP 0x1
+#define BV_LCDIF_CTRL_CSC_DATA_SWIZZLE__SWAP_ALL_BYTES 0x1
+#define BV_LCDIF_CTRL_CSC_DATA_SWIZZLE__HWD_SWAP 0x2
+#define BV_LCDIF_CTRL_CSC_DATA_SWIZZLE__HWD_BYTE_SWAP 0x3
+#define BP_LCDIF_CTRL_LCD_DATABUS_WIDTH 10
+#define BM_LCDIF_CTRL_LCD_DATABUS_WIDTH 0x00000C00
+#define BF_LCDIF_CTRL_LCD_DATABUS_WIDTH(v) \
+ (((v) << 10) & BM_LCDIF_CTRL_LCD_DATABUS_WIDTH)
+#define BV_LCDIF_CTRL_LCD_DATABUS_WIDTH__16_BIT 0x0
+#define BV_LCDIF_CTRL_LCD_DATABUS_WIDTH__8_BIT 0x1
+#define BV_LCDIF_CTRL_LCD_DATABUS_WIDTH__18_BIT 0x2
+#define BV_LCDIF_CTRL_LCD_DATABUS_WIDTH__24_BIT 0x3
+#define BP_LCDIF_CTRL_WORD_LENGTH 8
+#define BM_LCDIF_CTRL_WORD_LENGTH 0x00000300
+#define BF_LCDIF_CTRL_WORD_LENGTH(v) \
+ (((v) << 8) & BM_LCDIF_CTRL_WORD_LENGTH)
+#define BV_LCDIF_CTRL_WORD_LENGTH__16_BIT 0x0
+#define BV_LCDIF_CTRL_WORD_LENGTH__8_BIT 0x1
+#define BV_LCDIF_CTRL_WORD_LENGTH__18_BIT 0x2
+#define BV_LCDIF_CTRL_WORD_LENGTH__24_BIT 0x3
+#define BM_LCDIF_CTRL_RGB_TO_YCBCR422_CSC 0x00000080
+#define BM_LCDIF_CTRL_ENABLE_PXP_HANDSHAKE 0x00000040
+#define BM_LCDIF_CTRL_LCDIF_MASTER 0x00000020
+#define BM_LCDIF_CTRL_DMA_BURST_LENGTH 0x00000010
+#define BM_LCDIF_CTRL_DATA_FORMAT_16_BIT 0x00000008
+#define BM_LCDIF_CTRL_DATA_FORMAT_18_BIT 0x00000004
+#define BV_LCDIF_CTRL_DATA_FORMAT_18_BIT__LOWER_18_BITS_VALID 0x0
+#define BV_LCDIF_CTRL_DATA_FORMAT_18_BIT__UPPER_18_BITS_VALID 0x1
+#define BM_LCDIF_CTRL_DATA_FORMAT_24_BIT 0x00000002
+#define BV_LCDIF_CTRL_DATA_FORMAT_24_BIT__ALL_24_BITS_VALID 0x0
+#define BV_LCDIF_CTRL_DATA_FORMAT_24_BIT__DROP_UPPER_2_BITS_PER_BYTE 0x1
+#define BM_LCDIF_CTRL_RUN 0x00000001
+HW_REGISTER(HW_LCDIF_CTRL1, REGS_LCDIF_BASE, 0x00000010)
+#define HW_LCDIF_CTRL1_ADDR (REGS_LCDIF_BASE + 0x00000010)
+#define BM_LCDIF_CTRL1_BM_ERROR_IRQ_EN 0x04000000
+#define BM_LCDIF_CTRL1_BM_ERROR_IRQ 0x02000000
+#define BV_LCDIF_CTRL1_BM_ERROR_IRQ__NO_REQUEST 0x0
+#define BV_LCDIF_CTRL1_BM_ERROR_IRQ__REQUEST 0x1
+#define BM_LCDIF_CTRL1_RECOVER_ON_UNDERFLOW 0x01000000
+#define BM_LCDIF_CTRL1_INTERLACE_FIELDS 0x00800000
+#define BM_LCDIF_CTRL1_START_INTERLACE_FROM_SECOND_FIELD 0x00400000
+#define BM_LCDIF_CTRL1_FIFO_CLEAR 0x00200000
+#define BM_LCDIF_CTRL1_IRQ_ON_ALTERNATE_FIELDS 0x00100000
+#define BP_LCDIF_CTRL1_BYTE_PACKING_FORMAT 16
+#define BM_LCDIF_CTRL1_BYTE_PACKING_FORMAT 0x000F0000
+#define BF_LCDIF_CTRL1_BYTE_PACKING_FORMAT(v) \
+ (((v) << 16) & BM_LCDIF_CTRL1_BYTE_PACKING_FORMAT)
+#define BM_LCDIF_CTRL1_OVERFLOW_IRQ_EN 0x00008000
+#define BM_LCDIF_CTRL1_UNDERFLOW_IRQ_EN 0x00004000
+#define BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ_EN 0x00002000
+#define BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ_EN 0x00001000
+#define BM_LCDIF_CTRL1_OVERFLOW_IRQ 0x00000800
+#define BV_LCDIF_CTRL1_OVERFLOW_IRQ__NO_REQUEST 0x0
+#define BV_LCDIF_CTRL1_OVERFLOW_IRQ__REQUEST 0x1
+#define BM_LCDIF_CTRL1_UNDERFLOW_IRQ 0x00000400
+#define BV_LCDIF_CTRL1_UNDERFLOW_IRQ__NO_REQUEST 0x0
+#define BV_LCDIF_CTRL1_UNDERFLOW_IRQ__REQUEST 0x1
+#define BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ 0x00000200
+#define BV_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ__NO_REQUEST 0x0
+#define BV_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ__REQUEST 0x1
+#define BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ 0x00000100
+#define BV_LCDIF_CTRL1_VSYNC_EDGE_IRQ__NO_REQUEST 0x0
+#define BV_LCDIF_CTRL1_VSYNC_EDGE_IRQ__REQUEST 0x1
+#define BM_LCDIF_CTRL1_PAUSE_TRANSFER 0x00000040
+#define BM_LCDIF_CTRL1_PAUSE_TRANSFER_IRQ_EN 0x00000020
+#define BM_LCDIF_CTRL1_PAUSE_TRANSFER_IRQ 0x00000010
+#define BV_LCDIF_CTRL1_PAUSE_TRANSFER_IRQ__NO_REQUEST 0x0
+#define BV_LCDIF_CTRL1_PAUSE_TRANSFER_IRQ__REQUEST 0x1
+#define BM_LCDIF_CTRL1_LCD_CS_CTRL 0x00000008
+#define BM_LCDIF_CTRL1_BUSY_ENABLE 0x00000004
+#define BV_LCDIF_CTRL1_BUSY_ENABLE__BUSY_DISABLED 0x0
+#define BV_LCDIF_CTRL1_BUSY_ENABLE__BUSY_ENABLED 0x1
+#define BM_LCDIF_CTRL1_MODE86 0x00000002
+#define BV_LCDIF_CTRL1_MODE86__8080_MODE 0x0
+#define BV_LCDIF_CTRL1_MODE86__6800_MODE 0x1
+#define BM_LCDIF_CTRL1_RESET 0x00000001
+#define BV_LCDIF_CTRL1_RESET__LCDRESET_LOW 0x0
+#define BV_LCDIF_CTRL1_RESET__LCDRESET_HIGH 0x1
+HW_REGISTER_0(HW_LCDIF_TRANSFER_COUNT, REGS_LCDIF_BASE, 0x00000020)
+#define HW_LCDIF_TRANSFER_COUNT_ADDR (REGS_LCDIF_BASE + 0x00000020)
+#define BP_LCDIF_TRANSFER_COUNT_V_COUNT 16
+#define BM_LCDIF_TRANSFER_COUNT_V_COUNT 0xFFFF0000
+#define BF_LCDIF_TRANSFER_COUNT_V_COUNT(v) \
+ (((v) << 16) & BM_LCDIF_TRANSFER_COUNT_V_COUNT)
+#define BP_LCDIF_TRANSFER_COUNT_H_COUNT 0
+#define BM_LCDIF_TRANSFER_COUNT_H_COUNT 0x0000FFFF
+#define BF_LCDIF_TRANSFER_COUNT_H_COUNT(v) \
+ (((v) << 0) & BM_LCDIF_TRANSFER_COUNT_H_COUNT)
+HW_REGISTER_0(HW_LCDIF_CUR_BUF, REGS_LCDIF_BASE, 0x00000030)
+#define HW_LCDIF_CUR_BUF_ADDR (REGS_LCDIF_BASE + 0x00000030)
+#define BP_LCDIF_CUR_BUF_ADDR 0
+#define BM_LCDIF_CUR_BUF_ADDR 0xFFFFFFFF
+#define BF_LCDIF_CUR_BUF_ADDR(v) (v)
+HW_REGISTER_0(HW_LCDIF_NEXT_BUF, REGS_LCDIF_BASE, 0x00000040)
+#define HW_LCDIF_NEXT_BUF_ADDR (REGS_LCDIF_BASE + 0x00000040)
+#define BP_LCDIF_NEXT_BUF_ADDR 0
+#define BM_LCDIF_NEXT_BUF_ADDR 0xFFFFFFFF
+#define BF_LCDIF_NEXT_BUF_ADDR(v) (v)
+HW_REGISTER_0(HW_LCDIF_PAGETABLE, REGS_LCDIF_BASE, 0x00000050)
+#define HW_LCDIF_PAGETABLE_ADDR (REGS_LCDIF_BASE + 0x00000050)
+#define BP_LCDIF_PAGETABLE_BASE 14
+#define BM_LCDIF_PAGETABLE_BASE 0xFFFFC000
+#define BF_LCDIF_PAGETABLE_BASE(v) \
+ (((v) << 14) & BM_LCDIF_PAGETABLE_BASE)
+#define BM_LCDIF_PAGETABLE_FLUSH 0x00000002
+#define BM_LCDIF_PAGETABLE_ENABLE 0x00000001
+HW_REGISTER_0(HW_LCDIF_TIMING, REGS_LCDIF_BASE, 0x00000060)
+#define HW_LCDIF_TIMING_ADDR (REGS_LCDIF_BASE + 0x00000060)
+#define BP_LCDIF_TIMING_CMD_HOLD 24
+#define BM_LCDIF_TIMING_CMD_HOLD 0xFF000000
+#define BF_LCDIF_TIMING_CMD_HOLD(v) \
+ (((v) << 24) & BM_LCDIF_TIMING_CMD_HOLD)
+#define BP_LCDIF_TIMING_CMD_SETUP 16
+#define BM_LCDIF_TIMING_CMD_SETUP 0x00FF0000
+#define BF_LCDIF_TIMING_CMD_SETUP(v) \
+ (((v) << 16) & BM_LCDIF_TIMING_CMD_SETUP)
+#define BP_LCDIF_TIMING_DATA_HOLD 8
+#define BM_LCDIF_TIMING_DATA_HOLD 0x0000FF00
+#define BF_LCDIF_TIMING_DATA_HOLD(v) \
+ (((v) << 8) & BM_LCDIF_TIMING_DATA_HOLD)
+#define BP_LCDIF_TIMING_DATA_SETUP 0
+#define BM_LCDIF_TIMING_DATA_SETUP 0x000000FF
+#define BF_LCDIF_TIMING_DATA_SETUP(v) \
+ (((v) << 0) & BM_LCDIF_TIMING_DATA_SETUP)
+HW_REGISTER(HW_LCDIF_VDCTRL0, REGS_LCDIF_BASE, 0x00000070)
+#define HW_LCDIF_VDCTRL0_ADDR (REGS_LCDIF_BASE + 0x00000070)
+#define BM_LCDIF_VDCTRL0_VSYNC_OEB 0x20000000
+#define BV_LCDIF_VDCTRL0_VSYNC_OEB__VSYNC_OUTPUT 0x0
+#define BV_LCDIF_VDCTRL0_VSYNC_OEB__VSYNC_INPUT 0x1
+#define BM_LCDIF_VDCTRL0_ENABLE_PRESENT 0x10000000
+#define BM_LCDIF_VDCTRL0_VSYNC_POL 0x08000000
+#define BM_LCDIF_VDCTRL0_HSYNC_POL 0x04000000
+#define BM_LCDIF_VDCTRL0_DOTCLK_POL 0x02000000
+#define BM_LCDIF_VDCTRL0_ENABLE_POL 0x01000000
+#define BM_LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT 0x00200000
+#define BM_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT 0x00100000
+#define BM_LCDIF_VDCTRL0_HALF_LINE 0x00080000
+#define BM_LCDIF_VDCTRL0_HALF_LINE_MODE 0x00040000
+#define BP_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH 0
+#define BM_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH 0x0003FFFF
+#define BF_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH(v) \
+ (((v) << 0) & BM_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH)
+HW_REGISTER_0(HW_LCDIF_VDCTRL1, REGS_LCDIF_BASE, 0x00000080)
+#define HW_LCDIF_VDCTRL1_ADDR (REGS_LCDIF_BASE + 0x00000080)
+#define BP_LCDIF_VDCTRL1_VSYNC_PERIOD 0
+#define BM_LCDIF_VDCTRL1_VSYNC_PERIOD 0xFFFFFFFF
+#define BF_LCDIF_VDCTRL1_VSYNC_PERIOD(v) (v)
+HW_REGISTER_0(HW_LCDIF_VDCTRL2, REGS_LCDIF_BASE, 0x00000090)
+#define HW_LCDIF_VDCTRL2_ADDR (REGS_LCDIF_BASE + 0x00000090)
+#define BP_LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH 24
+#define BM_LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH 0xFF000000
+#define BF_LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH(v) \
+ (((v) << 24) & BM_LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH)
+#define BP_LCDIF_VDCTRL2_HSYNC_PERIOD 0
+#define BM_LCDIF_VDCTRL2_HSYNC_PERIOD 0x0003FFFF
+#define BF_LCDIF_VDCTRL2_HSYNC_PERIOD(v) \
+ (((v) << 0) & BM_LCDIF_VDCTRL2_HSYNC_PERIOD)
+HW_REGISTER_0(HW_LCDIF_VDCTRL3, REGS_LCDIF_BASE, 0x000000a0)
+#define HW_LCDIF_VDCTRL3_ADDR (REGS_LCDIF_BASE + 0x000000a0)
+#define BM_LCDIF_VDCTRL3_MUX_SYNC_SIGNALS 0x20000000
+#define BM_LCDIF_VDCTRL3_VSYNC_ONLY 0x10000000
+#define BP_LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT 16
+#define BM_LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT 0x0FFF0000
+#define BF_LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT(v) \
+ (((v) << 16) & BM_LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT)
+#define BP_LCDIF_VDCTRL3_VERTICAL_WAIT_CNT 0
+#define BM_LCDIF_VDCTRL3_VERTICAL_WAIT_CNT 0x0000FFFF
+#define BF_LCDIF_VDCTRL3_VERTICAL_WAIT_CNT(v) \
+ (((v) << 0) & BM_LCDIF_VDCTRL3_VERTICAL_WAIT_CNT)
+HW_REGISTER_0(HW_LCDIF_VDCTRL4, REGS_LCDIF_BASE, 0x000000b0)
+#define HW_LCDIF_VDCTRL4_ADDR (REGS_LCDIF_BASE + 0x000000b0)
+#define BM_LCDIF_VDCTRL4_SYNC_SIGNALS_ON 0x00040000
+#define BP_LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT 0
+#define BM_LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT 0x0003FFFF
+#define BF_LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT(v) \
+ (((v) << 0) & BM_LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT)
+HW_REGISTER_0(HW_LCDIF_DVICTRL0, REGS_LCDIF_BASE, 0x000000c0)
+#define HW_LCDIF_DVICTRL0_ADDR (REGS_LCDIF_BASE + 0x000000c0)
+#define BM_LCDIF_DVICTRL0_START_TRS 0x80000000
+#define BP_LCDIF_DVICTRL0_H_ACTIVE_CNT 20
+#define BM_LCDIF_DVICTRL0_H_ACTIVE_CNT 0x7FF00000
+#define BF_LCDIF_DVICTRL0_H_ACTIVE_CNT(v) \
+ (((v) << 20) & BM_LCDIF_DVICTRL0_H_ACTIVE_CNT)
+#define BP_LCDIF_DVICTRL0_H_BLANKING_CNT 10
+#define BM_LCDIF_DVICTRL0_H_BLANKING_CNT 0x000FFC00
+#define BF_LCDIF_DVICTRL0_H_BLANKING_CNT(v) \
+ (((v) << 10) & BM_LCDIF_DVICTRL0_H_BLANKING_CNT)
+#define BP_LCDIF_DVICTRL0_V_LINES_CNT 0
+#define BM_LCDIF_DVICTRL0_V_LINES_CNT 0x000003FF
+#define BF_LCDIF_DVICTRL0_V_LINES_CNT(v) \
+ (((v) << 0) & BM_LCDIF_DVICTRL0_V_LINES_CNT)
+HW_REGISTER_0(HW_LCDIF_DVICTRL1, REGS_LCDIF_BASE, 0x000000d0)
+#define HW_LCDIF_DVICTRL1_ADDR (REGS_LCDIF_BASE + 0x000000d0)
+#define BP_LCDIF_DVICTRL1_F1_START_LINE 20
+#define BM_LCDIF_DVICTRL1_F1_START_LINE 0x3FF00000
+#define BF_LCDIF_DVICTRL1_F1_START_LINE(v) \
+ (((v) << 20) & BM_LCDIF_DVICTRL1_F1_START_LINE)
+#define BP_LCDIF_DVICTRL1_F1_END_LINE 10
+#define BM_LCDIF_DVICTRL1_F1_END_LINE 0x000FFC00
+#define BF_LCDIF_DVICTRL1_F1_END_LINE(v) \
+ (((v) << 10) & BM_LCDIF_DVICTRL1_F1_END_LINE)
+#define BP_LCDIF_DVICTRL1_F2_START_LINE 0
+#define BM_LCDIF_DVICTRL1_F2_START_LINE 0x000003FF
+#define BF_LCDIF_DVICTRL1_F2_START_LINE(v) \
+ (((v) << 0) & BM_LCDIF_DVICTRL1_F2_START_LINE)
+HW_REGISTER_0(HW_LCDIF_DVICTRL2, REGS_LCDIF_BASE, 0x000000e0)
+#define HW_LCDIF_DVICTRL2_ADDR (REGS_LCDIF_BASE + 0x000000e0)
+#define BP_LCDIF_DVICTRL2_F2_END_LINE 20
+#define BM_LCDIF_DVICTRL2_F2_END_LINE 0x3FF00000
+#define BF_LCDIF_DVICTRL2_F2_END_LINE(v) \
+ (((v) << 20) & BM_LCDIF_DVICTRL2_F2_END_LINE)
+#define BP_LCDIF_DVICTRL2_V1_BLANK_START_LINE 10
+#define BM_LCDIF_DVICTRL2_V1_BLANK_START_LINE 0x000FFC00
+#define BF_LCDIF_DVICTRL2_V1_BLANK_START_LINE(v) \
+ (((v) << 10) & BM_LCDIF_DVICTRL2_V1_BLANK_START_LINE)
+#define BP_LCDIF_DVICTRL2_V1_BLANK_END_LINE 0
+#define BM_LCDIF_DVICTRL2_V1_BLANK_END_LINE 0x000003FF
+#define BF_LCDIF_DVICTRL2_V1_BLANK_END_LINE(v) \
+ (((v) << 0) & BM_LCDIF_DVICTRL2_V1_BLANK_END_LINE)
+HW_REGISTER_0(HW_LCDIF_DVICTRL3, REGS_LCDIF_BASE, 0x000000f0)
+#define HW_LCDIF_DVICTRL3_ADDR (REGS_LCDIF_BASE + 0x000000f0)
+#define BP_LCDIF_DVICTRL3_V2_BLANK_START_LINE 16
+#define BM_LCDIF_DVICTRL3_V2_BLANK_START_LINE 0x03FF0000
+#define BF_LCDIF_DVICTRL3_V2_BLANK_START_LINE(v) \
+ (((v) << 16) & BM_LCDIF_DVICTRL3_V2_BLANK_START_LINE)
+#define BP_LCDIF_DVICTRL3_V2_BLANK_END_LINE 0
+#define BM_LCDIF_DVICTRL3_V2_BLANK_END_LINE 0x000003FF
+#define BF_LCDIF_DVICTRL3_V2_BLANK_END_LINE(v) \
+ (((v) << 0) & BM_LCDIF_DVICTRL3_V2_BLANK_END_LINE)
+HW_REGISTER_0(HW_LCDIF_DVICTRL4, REGS_LCDIF_BASE, 0x00000100)
+#define HW_LCDIF_DVICTRL4_ADDR (REGS_LCDIF_BASE + 0x00000100)
+#define BP_LCDIF_DVICTRL4_Y_FILL_VALUE 24
+#define BM_LCDIF_DVICTRL4_Y_FILL_VALUE 0xFF000000
+#define BF_LCDIF_DVICTRL4_Y_FILL_VALUE(v) \
+ (((v) << 24) & BM_LCDIF_DVICTRL4_Y_FILL_VALUE)
+#define BP_LCDIF_DVICTRL4_CB_FILL_VALUE 16
+#define BM_LCDIF_DVICTRL4_CB_FILL_VALUE 0x00FF0000
+#define BF_LCDIF_DVICTRL4_CB_FILL_VALUE(v) \
+ (((v) << 16) & BM_LCDIF_DVICTRL4_CB_FILL_VALUE)
+#define BP_LCDIF_DVICTRL4_CR_FILL_VALUE 8
+#define BM_LCDIF_DVICTRL4_CR_FILL_VALUE 0x0000FF00
+#define BF_LCDIF_DVICTRL4_CR_FILL_VALUE(v) \
+ (((v) << 8) & BM_LCDIF_DVICTRL4_CR_FILL_VALUE)
+#define BP_LCDIF_DVICTRL4_H_FILL_CNT 0
+#define BM_LCDIF_DVICTRL4_H_FILL_CNT 0x000000FF
+#define BF_LCDIF_DVICTRL4_H_FILL_CNT(v) \
+ (((v) << 0) & BM_LCDIF_DVICTRL4_H_FILL_CNT)
+HW_REGISTER_0(HW_LCDIF_CSC_COEFF0, REGS_LCDIF_BASE, 0x00000110)
+#define HW_LCDIF_CSC_COEFF0_ADDR (REGS_LCDIF_BASE + 0x00000110)
+#define BP_LCDIF_CSC_COEFF0_C0 16
+#define BM_LCDIF_CSC_COEFF0_C0 0x03FF0000
+#define BF_LCDIF_CSC_COEFF0_C0(v) \
+ (((v) << 16) & BM_LCDIF_CSC_COEFF0_C0)
+#define BP_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER 0
+#define BM_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER 0x00000003
+#define BF_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER(v) \
+ (((v) << 0) & BM_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER)
+#define BV_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER__SAMPLE_AND_HOLD 0x0
+#define BV_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER__RSRVD 0x1
+#define BV_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER__INTERSTITIAL 0x2
+#define BV_LCDIF_CSC_COEFF0_CSC_SUBSAMPLE_FILTER__COSITED 0x3
+HW_REGISTER_0(HW_LCDIF_CSC_COEFF1, REGS_LCDIF_BASE, 0x00000120)
+#define HW_LCDIF_CSC_COEFF1_ADDR (REGS_LCDIF_BASE + 0x00000120)
+#define BP_LCDIF_CSC_COEFF1_C2 16
+#define BM_LCDIF_CSC_COEFF1_C2 0x03FF0000
+#define BF_LCDIF_CSC_COEFF1_C2(v) \
+ (((v) << 16) & BM_LCDIF_CSC_COEFF1_C2)
+#define BP_LCDIF_CSC_COEFF1_C1 0
+#define BM_LCDIF_CSC_COEFF1_C1 0x000003FF
+#define BF_LCDIF_CSC_COEFF1_C1(v) \
+ (((v) << 0) & BM_LCDIF_CSC_COEFF1_C1)
+HW_REGISTER_0(HW_LCDIF_CSC_COEFF2, REGS_LCDIF_BASE, 0x00000130)
+#define HW_LCDIF_CSC_COEFF2_ADDR (REGS_LCDIF_BASE + 0x00000130)
+#define BP_LCDIF_CSC_COEFF2_C4 16
+#define BM_LCDIF_CSC_COEFF2_C4 0x03FF0000
+#define BF_LCDIF_CSC_COEFF2_C4(v) \
+ (((v) << 16) & BM_LCDIF_CSC_COEFF2_C4)
+#define BP_LCDIF_CSC_COEFF2_C3 0
+#define BM_LCDIF_CSC_COEFF2_C3 0x000003FF
+#define BF_LCDIF_CSC_COEFF2_C3(v) \
+ (((v) << 0) & BM_LCDIF_CSC_COEFF2_C3)
+HW_REGISTER_0(HW_LCDIF_CSC_COEFF3, REGS_LCDIF_BASE, 0x00000140)
+#define HW_LCDIF_CSC_COEFF3_ADDR (REGS_LCDIF_BASE + 0x00000140)
+#define BP_LCDIF_CSC_COEFF3_C6 16
+#define BM_LCDIF_CSC_COEFF3_C6 0x03FF0000
+#define BF_LCDIF_CSC_COEFF3_C6(v) \
+ (((v) << 16) & BM_LCDIF_CSC_COEFF3_C6)
+#define BP_LCDIF_CSC_COEFF3_C5 0
+#define BM_LCDIF_CSC_COEFF3_C5 0x000003FF
+#define BF_LCDIF_CSC_COEFF3_C5(v) \
+ (((v) << 0) & BM_LCDIF_CSC_COEFF3_C5)
+HW_REGISTER_0(HW_LCDIF_CSC_COEFF4, REGS_LCDIF_BASE, 0x00000150)
+#define HW_LCDIF_CSC_COEFF4_ADDR (REGS_LCDIF_BASE + 0x00000150)
+#define BP_LCDIF_CSC_COEFF4_C8 16
+#define BM_LCDIF_CSC_COEFF4_C8 0x03FF0000
+#define BF_LCDIF_CSC_COEFF4_C8(v) \
+ (((v) << 16) & BM_LCDIF_CSC_COEFF4_C8)
+#define BP_LCDIF_CSC_COEFF4_C7 0
+#define BM_LCDIF_CSC_COEFF4_C7 0x000003FF
+#define BF_LCDIF_CSC_COEFF4_C7(v) \
+ (((v) << 0) & BM_LCDIF_CSC_COEFF4_C7)
+HW_REGISTER_0(HW_LCDIF_CSC_OFFSET, REGS_LCDIF_BASE, 0x00000160)
+#define HW_LCDIF_CSC_OFFSET_ADDR (REGS_LCDIF_BASE + 0x00000160)
+#define BP_LCDIF_CSC_OFFSET_CBCR_OFFSET 16
+#define BM_LCDIF_CSC_OFFSET_CBCR_OFFSET 0x01FF0000
+#define BF_LCDIF_CSC_OFFSET_CBCR_OFFSET(v) \
+ (((v) << 16) & BM_LCDIF_CSC_OFFSET_CBCR_OFFSET)
+#define BP_LCDIF_CSC_OFFSET_Y_OFFSET 0
+#define BM_LCDIF_CSC_OFFSET_Y_OFFSET 0x000001FF
+#define BF_LCDIF_CSC_OFFSET_Y_OFFSET(v) \
+ (((v) << 0) & BM_LCDIF_CSC_OFFSET_Y_OFFSET)
+HW_REGISTER_0(HW_LCDIF_CSC_LIMIT, REGS_LCDIF_BASE, 0x00000170)
+#define HW_LCDIF_CSC_LIMIT_ADDR (REGS_LCDIF_BASE + 0x00000170)
+#define BP_LCDIF_CSC_LIMIT_CBCR_MIN 24
+#define BM_LCDIF_CSC_LIMIT_CBCR_MIN 0xFF000000
+#define BF_LCDIF_CSC_LIMIT_CBCR_MIN(v) \
+ (((v) << 24) & BM_LCDIF_CSC_LIMIT_CBCR_MIN)
+#define BP_LCDIF_CSC_LIMIT_CBCR_MAX 16
+#define BM_LCDIF_CSC_LIMIT_CBCR_MAX 0x00FF0000
+#define BF_LCDIF_CSC_LIMIT_CBCR_MAX(v) \
+ (((v) << 16) & BM_LCDIF_CSC_LIMIT_CBCR_MAX)
+#define BP_LCDIF_CSC_LIMIT_Y_MIN 8
+#define BM_LCDIF_CSC_LIMIT_Y_MIN 0x0000FF00
+#define BF_LCDIF_CSC_LIMIT_Y_MIN(v) \
+ (((v) << 8) & BM_LCDIF_CSC_LIMIT_Y_MIN)
+#define BP_LCDIF_CSC_LIMIT_Y_MAX 0
+#define BM_LCDIF_CSC_LIMIT_Y_MAX 0x000000FF
+#define BF_LCDIF_CSC_LIMIT_Y_MAX(v) \
+ (((v) << 0) & BM_LCDIF_CSC_LIMIT_Y_MAX)
+HW_REGISTER(HW_LCDIF_PIN_SHARING_CTRL0, REGS_LCDIF_BASE, 0x00000180)
+#define HW_LCDIF_PIN_SHARING_CTRL0_ADDR (REGS_LCDIF_BASE + 0x00000180)
+#define BP_LCDIF_PIN_SHARING_CTRL0_MUX_OVERRIDE 4
+#define BM_LCDIF_PIN_SHARING_CTRL0_MUX_OVERRIDE 0x00000030
+#define BF_LCDIF_PIN_SHARING_CTRL0_MUX_OVERRIDE(v) \
+ (((v) << 4) & BM_LCDIF_PIN_SHARING_CTRL0_MUX_OVERRIDE)
+#define BV_LCDIF_PIN_SHARING_CTRL0_MUX_OVERRIDE__NO_OVERRIDE 0x0
+#define BV_LCDIF_PIN_SHARING_CTRL0_MUX_OVERRIDE__RSRVD 0x1
+#define BV_LCDIF_PIN_SHARING_CTRL0_MUX_OVERRIDE__LCDIF_SEL 0x2
+#define BV_LCDIF_PIN_SHARING_CTRL0_MUX_OVERRIDE__GPMI_SEL 0x3
+#define BM_LCDIF_PIN_SHARING_CTRL0_PIN_SHARING_IRQ_EN 0x00000004
+#define BM_LCDIF_PIN_SHARING_CTRL0_PIN_SHARING_IRQ 0x00000002
+#define BV_LCDIF_PIN_SHARING_CTRL0_PIN_SHARING_IRQ__NO_REQUEST 0x0
+#define BV_LCDIF_PIN_SHARING_CTRL0_PIN_SHARING_IRQ__REQUEST 0x1
+#define BM_LCDIF_PIN_SHARING_CTRL0_PIN_SHARING_ENABLE 0x00000001
+HW_REGISTER_0(HW_LCDIF_PIN_SHARING_CTRL1, REGS_LCDIF_BASE, 0x00000190)
+#define HW_LCDIF_PIN_SHARING_CTRL1_ADDR (REGS_LCDIF_BASE + 0x00000190)
+#define BP_LCDIF_PIN_SHARING_CTRL1_THRESHOLD1 0
+#define BM_LCDIF_PIN_SHARING_CTRL1_THRESHOLD1 0xFFFFFFFF
+#define BF_LCDIF_PIN_SHARING_CTRL1_THRESHOLD1(v) (v)
+HW_REGISTER_0(HW_LCDIF_PIN_SHARING_CTRL2, REGS_LCDIF_BASE, 0x000001a0)
+#define HW_LCDIF_PIN_SHARING_CTRL2_ADDR (REGS_LCDIF_BASE + 0x000001a0)
+#define BP_LCDIF_PIN_SHARING_CTRL2_THRESHOLD2 0
+#define BM_LCDIF_PIN_SHARING_CTRL2_THRESHOLD2 0xFFFFFFFF
+#define BF_LCDIF_PIN_SHARING_CTRL2_THRESHOLD2(v) (v)
+HW_REGISTER_0(HW_LCDIF_DATA, REGS_LCDIF_BASE, 0x000001b0)
+#define HW_LCDIF_DATA_ADDR (REGS_LCDIF_BASE + 0x000001b0)
+#define BP_LCDIF_DATA_DATA_THREE 24
+#define BM_LCDIF_DATA_DATA_THREE 0xFF000000
+#define BF_LCDIF_DATA_DATA_THREE(v) \
+ (((v) << 24) & BM_LCDIF_DATA_DATA_THREE)
+#define BP_LCDIF_DATA_DATA_TWO 16
+#define BM_LCDIF_DATA_DATA_TWO 0x00FF0000
+#define BF_LCDIF_DATA_DATA_TWO(v) \
+ (((v) << 16) & BM_LCDIF_DATA_DATA_TWO)
+#define BP_LCDIF_DATA_DATA_ONE 8
+#define BM_LCDIF_DATA_DATA_ONE 0x0000FF00
+#define BF_LCDIF_DATA_DATA_ONE(v) \
+ (((v) << 8) & BM_LCDIF_DATA_DATA_ONE)
+#define BP_LCDIF_DATA_DATA_ZERO 0
+#define BM_LCDIF_DATA_DATA_ZERO 0x000000FF
+#define BF_LCDIF_DATA_DATA_ZERO(v) \
+ (((v) << 0) & BM_LCDIF_DATA_DATA_ZERO)
+HW_REGISTER_0(HW_LCDIF_BM_ERROR_STAT, REGS_LCDIF_BASE, 0x000001c0)
+#define HW_LCDIF_BM_ERROR_STAT_ADDR (REGS_LCDIF_BASE + 0x000001c0)
+#define BP_LCDIF_BM_ERROR_STAT_ADDR 0
+#define BM_LCDIF_BM_ERROR_STAT_ADDR 0xFFFFFFFF
+#define BF_LCDIF_BM_ERROR_STAT_ADDR(v) (v)
+HW_REGISTER_0(HW_LCDIF_STAT, REGS_LCDIF_BASE, 0x000001d0)
+#define HW_LCDIF_STAT_ADDR (REGS_LCDIF_BASE + 0x000001d0)
+#define BM_LCDIF_STAT_PRESENT 0x80000000
+#define BM_LCDIF_STAT_DMA_REQ 0x40000000
+#define BM_LCDIF_STAT_LFIFO_FULL 0x20000000
+#define BM_LCDIF_STAT_LFIFO_EMPTY 0x10000000
+#define BM_LCDIF_STAT_TXFIFO_FULL 0x08000000
+#define BM_LCDIF_STAT_TXFIFO_EMPTY 0x04000000
+#define BM_LCDIF_STAT_BUSY 0x02000000
+#define BM_LCDIF_STAT_DVI_CURRENT_FIELD 0x01000000
+HW_REGISTER_0(HW_LCDIF_VERSION, REGS_LCDIF_BASE, 0x000001e0)
+#define HW_LCDIF_VERSION_ADDR (REGS_LCDIF_BASE + 0x000001e0)
+#define BP_LCDIF_VERSION_MAJOR 24
+#define BM_LCDIF_VERSION_MAJOR 0xFF000000
+#define BF_LCDIF_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_LCDIF_VERSION_MAJOR)
+#define BP_LCDIF_VERSION_MINOR 16
+#define BM_LCDIF_VERSION_MINOR 0x00FF0000
+#define BF_LCDIF_VERSION_MINOR(v) \
+ (((v) << 16) & BM_LCDIF_VERSION_MINOR)
+#define BP_LCDIF_VERSION_STEP 0
+#define BM_LCDIF_VERSION_STEP 0x0000FFFF
+#define BF_LCDIF_VERSION_STEP(v) \
+ (((v) << 0) & BM_LCDIF_VERSION_STEP)
+HW_REGISTER_0(HW_LCDIF_DEBUG0, REGS_LCDIF_BASE, 0x000001f0)
+#define HW_LCDIF_DEBUG0_ADDR (REGS_LCDIF_BASE + 0x000001f0)
+#define BM_LCDIF_DEBUG0_STREAMING_END_DETECTED 0x80000000
+#define BM_LCDIF_DEBUG0_WAIT_FOR_VSYNC_EDGE_OUT 0x40000000
+#define BM_LCDIF_DEBUG0_SYNC_SIGNALS_ON_REG 0x20000000
+#define BM_LCDIF_DEBUG0_DMACMDKICK 0x10000000
+#define BM_LCDIF_DEBUG0_ENABLE 0x08000000
+#define BM_LCDIF_DEBUG0_HSYNC 0x04000000
+#define BM_LCDIF_DEBUG0_VSYNC 0x02000000
+#define BM_LCDIF_DEBUG0_CUR_FRAME_TX 0x01000000
+#define BM_LCDIF_DEBUG0_EMPTY_WORD 0x00800000
+#define BP_LCDIF_DEBUG0_CUR_STATE 16
+#define BM_LCDIF_DEBUG0_CUR_STATE 0x007F0000
+#define BF_LCDIF_DEBUG0_CUR_STATE(v) \
+ (((v) << 16) & BM_LCDIF_DEBUG0_CUR_STATE)
+#define BM_LCDIF_DEBUG0_PXP_LCDIF_B0_READY 0x00008000
+#define BM_LCDIF_DEBUG0_LCDIF_PXP_B0_DONE 0x00004000
+#define BM_LCDIF_DEBUG0_PXP_LCDIF_B1_READY 0x00002000
+#define BM_LCDIF_DEBUG0_LCDIF_PXP_B1_DONE 0x00001000
+#define BM_LCDIF_DEBUG0_GPMI_LCDIF_REQ 0x00000800
+#define BM_LCDIF_DEBUG0_LCDIF_GPMI_GRANT 0x00000400
+HW_REGISTER_0(HW_LCDIF_DEBUG1, REGS_LCDIF_BASE, 0x00000200)
+#define HW_LCDIF_DEBUG1_ADDR (REGS_LCDIF_BASE + 0x00000200)
+#define BP_LCDIF_DEBUG1_H_DATA_COUNT 16
+#define BM_LCDIF_DEBUG1_H_DATA_COUNT 0xFFFF0000
+#define BF_LCDIF_DEBUG1_H_DATA_COUNT(v) \
+ (((v) << 16) & BM_LCDIF_DEBUG1_H_DATA_COUNT)
+#define BP_LCDIF_DEBUG1_V_DATA_COUNT 0
+#define BM_LCDIF_DEBUG1_V_DATA_COUNT 0x0000FFFF
+#define BF_LCDIF_DEBUG1_V_DATA_COUNT(v) \
+ (((v) << 0) & BM_LCDIF_DEBUG1_V_DATA_COUNT)
+#endif /* __ARCH_ARM___LCDIF_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-lradc.h b/arch/arm/mach-stmp3xxx/include/mach/regs-lradc.h
new file mode 100644
index 000000000000..cf71a09b3ff9
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-lradc.h
@@ -0,0 +1,520 @@
+/*
+ * STMP LRADC Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___LRADC_H
+#define __ARCH_ARM___LRADC_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_LRADC_BASE (REGS_BASE + 0x50000)
+#define REGS_LRADC_BASE_PHYS (0x80050000)
+#define REGS_LRADC_SIZE 0x00002000
+HW_REGISTER(HW_LRADC_CTRL0, REGS_LRADC_BASE, 0x00000000)
+#define HW_LRADC_CTRL0_ADDR (REGS_LRADC_BASE + 0x00000000)
+#define BM_LRADC_CTRL0_SFTRST 0x80000000
+#define BM_LRADC_CTRL0_CLKGATE 0x40000000
+#define BM_LRADC_CTRL0_ONCHIP_GROUNDREF 0x00200000
+#define BV_LRADC_CTRL0_ONCHIP_GROUNDREF__OFF 0x0
+#define BV_LRADC_CTRL0_ONCHIP_GROUNDREF__ON 0x1
+#define BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE 0x00100000
+#define BV_LRADC_CTRL0_TOUCH_DETECT_ENABLE__OFF 0x0
+#define BV_LRADC_CTRL0_TOUCH_DETECT_ENABLE__ON 0x1
+#define BM_LRADC_CTRL0_YMINUS_ENABLE 0x00080000
+#define BV_LRADC_CTRL0_YMINUS_ENABLE__OFF 0x0
+#define BV_LRADC_CTRL0_YMINUS_ENABLE__ON 0x1
+#define BM_LRADC_CTRL0_XMINUS_ENABLE 0x00040000
+#define BV_LRADC_CTRL0_XMINUS_ENABLE__OFF 0x0
+#define BV_LRADC_CTRL0_XMINUS_ENABLE__ON 0x1
+#define BM_LRADC_CTRL0_YPLUS_ENABLE 0x00020000
+#define BV_LRADC_CTRL0_YPLUS_ENABLE__OFF 0x0
+#define BV_LRADC_CTRL0_YPLUS_ENABLE__ON 0x1
+#define BM_LRADC_CTRL0_XPLUS_ENABLE 0x00010000
+#define BV_LRADC_CTRL0_XPLUS_ENABLE__OFF 0x0
+#define BV_LRADC_CTRL0_XPLUS_ENABLE__ON 0x1
+#define BP_LRADC_CTRL0_SCHEDULE 0
+#define BM_LRADC_CTRL0_SCHEDULE 0x000000FF
+#define BF_LRADC_CTRL0_SCHEDULE(v) \
+ (((v) << 0) & BM_LRADC_CTRL0_SCHEDULE)
+HW_REGISTER(HW_LRADC_CTRL1, REGS_LRADC_BASE, 0x00000010)
+#define HW_LRADC_CTRL1_ADDR (REGS_LRADC_BASE + 0x00000010)
+#define BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN 0x01000000
+#define BV_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN__DISABLE 0x0
+#define BV_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN__ENABLE 0x1
+#define BM_LRADC_CTRL1_LRADC7_IRQ_EN 0x00800000
+#define BV_LRADC_CTRL1_LRADC7_IRQ_EN__DISABLE 0x0
+#define BV_LRADC_CTRL1_LRADC7_IRQ_EN__ENABLE 0x1
+#define BM_LRADC_CTRL1_LRADC6_IRQ_EN 0x00400000
+#define BV_LRADC_CTRL1_LRADC6_IRQ_EN__DISABLE 0x0
+#define BV_LRADC_CTRL1_LRADC6_IRQ_EN__ENABLE 0x1
+#define BM_LRADC_CTRL1_LRADC5_IRQ_EN 0x00200000
+#define BV_LRADC_CTRL1_LRADC5_IRQ_EN__DISABLE 0x0
+#define BV_LRADC_CTRL1_LRADC5_IRQ_EN__ENABLE 0x1
+#define BM_LRADC_CTRL1_LRADC4_IRQ_EN 0x00100000
+#define BV_LRADC_CTRL1_LRADC4_IRQ_EN__DISABLE 0x0
+#define BV_LRADC_CTRL1_LRADC4_IRQ_EN__ENABLE 0x1
+#define BM_LRADC_CTRL1_LRADC3_IRQ_EN 0x00080000
+#define BV_LRADC_CTRL1_LRADC3_IRQ_EN__DISABLE 0x0
+#define BV_LRADC_CTRL1_LRADC3_IRQ_EN__ENABLE 0x1
+#define BM_LRADC_CTRL1_LRADC2_IRQ_EN 0x00040000
+#define BV_LRADC_CTRL1_LRADC2_IRQ_EN__DISABLE 0x0
+#define BV_LRADC_CTRL1_LRADC2_IRQ_EN__ENABLE 0x1
+#define BM_LRADC_CTRL1_LRADC1_IRQ_EN 0x00020000
+#define BV_LRADC_CTRL1_LRADC1_IRQ_EN__DISABLE 0x0
+#define BV_LRADC_CTRL1_LRADC1_IRQ_EN__ENABLE 0x1
+#define BM_LRADC_CTRL1_LRADC0_IRQ_EN 0x00010000
+#define BV_LRADC_CTRL1_LRADC0_IRQ_EN__DISABLE 0x0
+#define BV_LRADC_CTRL1_LRADC0_IRQ_EN__ENABLE 0x1
+#define BM_LRADC_CTRL1_TOUCH_DETECT_IRQ 0x00000100
+#define BV_LRADC_CTRL1_TOUCH_DETECT_IRQ__CLEAR 0x0
+#define BV_LRADC_CTRL1_TOUCH_DETECT_IRQ__PENDING 0x1
+#define BM_LRADC_CTRL1_LRADC7_IRQ 0x00000080
+#define BV_LRADC_CTRL1_LRADC7_IRQ__CLEAR 0x0
+#define BV_LRADC_CTRL1_LRADC7_IRQ__PENDING 0x1
+#define BM_LRADC_CTRL1_LRADC6_IRQ 0x00000040
+#define BV_LRADC_CTRL1_LRADC6_IRQ__CLEAR 0x0
+#define BV_LRADC_CTRL1_LRADC6_IRQ__PENDING 0x1
+#define BM_LRADC_CTRL1_LRADC5_IRQ 0x00000020
+#define BV_LRADC_CTRL1_LRADC5_IRQ__CLEAR 0x0
+#define BV_LRADC_CTRL1_LRADC5_IRQ__PENDING 0x1
+#define BM_LRADC_CTRL1_LRADC4_IRQ 0x00000010
+#define BV_LRADC_CTRL1_LRADC4_IRQ__CLEAR 0x0
+#define BV_LRADC_CTRL1_LRADC4_IRQ__PENDING 0x1
+#define BM_LRADC_CTRL1_LRADC3_IRQ 0x00000008
+#define BV_LRADC_CTRL1_LRADC3_IRQ__CLEAR 0x0
+#define BV_LRADC_CTRL1_LRADC3_IRQ__PENDING 0x1
+#define BM_LRADC_CTRL1_LRADC2_IRQ 0x00000004
+#define BV_LRADC_CTRL1_LRADC2_IRQ__CLEAR 0x0
+#define BV_LRADC_CTRL1_LRADC2_IRQ__PENDING 0x1
+#define BM_LRADC_CTRL1_LRADC1_IRQ 0x00000002
+#define BV_LRADC_CTRL1_LRADC1_IRQ__CLEAR 0x0
+#define BV_LRADC_CTRL1_LRADC1_IRQ__PENDING 0x1
+#define BM_LRADC_CTRL1_LRADC0_IRQ 0x00000001
+#define BV_LRADC_CTRL1_LRADC0_IRQ__CLEAR 0x0
+#define BV_LRADC_CTRL1_LRADC0_IRQ__PENDING 0x1
+HW_REGISTER(HW_LRADC_CTRL2, REGS_LRADC_BASE, 0x00000020)
+#define HW_LRADC_CTRL2_ADDR (REGS_LRADC_BASE + 0x00000020)
+#define BP_LRADC_CTRL2_DIVIDE_BY_TWO 24
+#define BM_LRADC_CTRL2_DIVIDE_BY_TWO 0xFF000000
+#define BF_LRADC_CTRL2_DIVIDE_BY_TWO(v) \
+ (((v) << 24) & BM_LRADC_CTRL2_DIVIDE_BY_TWO)
+#define BM_LRADC_CTRL2_BL_AMP_BYPASS 0x00800000
+#define BV_LRADC_CTRL2_BL_AMP_BYPASS__DISABLE 0x0
+#define BV_LRADC_CTRL2_BL_AMP_BYPASS__ENABLE 0x1
+#define BM_LRADC_CTRL2_BL_ENABLE 0x00400000
+#define BM_LRADC_CTRL2_BL_MUX_SELECT 0x00200000
+#define BP_LRADC_CTRL2_BL_BRIGHTNESS 16
+#define BM_LRADC_CTRL2_BL_BRIGHTNESS 0x001F0000
+#define BF_LRADC_CTRL2_BL_BRIGHTNESS(v) \
+ (((v) << 16) & BM_LRADC_CTRL2_BL_BRIGHTNESS)
+#define BM_LRADC_CTRL2_TEMPSENSE_PWD 0x00008000
+#define BV_LRADC_CTRL2_TEMPSENSE_PWD__ENABLE 0x0
+#define BV_LRADC_CTRL2_TEMPSENSE_PWD__DISABLE 0x1
+#define BM_LRADC_CTRL2_EXT_EN1 0x00002000
+#define BV_LRADC_CTRL2_EXT_EN1__DISABLE 0x0
+#define BV_LRADC_CTRL2_EXT_EN1__ENABLE 0x1
+#define BM_LRADC_CTRL2_EXT_EN0 0x00001000
+#define BM_LRADC_CTRL2_TEMP_SENSOR_IENABLE1 0x00000200
+#define BV_LRADC_CTRL2_TEMP_SENSOR_IENABLE1__DISABLE 0x0
+#define BV_LRADC_CTRL2_TEMP_SENSOR_IENABLE1__ENABLE 0x1
+#define BM_LRADC_CTRL2_TEMP_SENSOR_IENABLE0 0x00000100
+#define BV_LRADC_CTRL2_TEMP_SENSOR_IENABLE0__DISABLE 0x0
+#define BV_LRADC_CTRL2_TEMP_SENSOR_IENABLE0__ENABLE 0x1
+#define BP_LRADC_CTRL2_TEMP_ISRC1 4
+#define BM_LRADC_CTRL2_TEMP_ISRC1 0x000000F0
+#define BF_LRADC_CTRL2_TEMP_ISRC1(v) \
+ (((v) << 4) & BM_LRADC_CTRL2_TEMP_ISRC1)
+#define BV_LRADC_CTRL2_TEMP_ISRC1__300 0xF
+#define BV_LRADC_CTRL2_TEMP_ISRC1__280 0xE
+#define BV_LRADC_CTRL2_TEMP_ISRC1__260 0xD
+#define BV_LRADC_CTRL2_TEMP_ISRC1__240 0xC
+#define BV_LRADC_CTRL2_TEMP_ISRC1__220 0xB
+#define BV_LRADC_CTRL2_TEMP_ISRC1__200 0xA
+#define BV_LRADC_CTRL2_TEMP_ISRC1__180 0x9
+#define BV_LRADC_CTRL2_TEMP_ISRC1__160 0x8
+#define BV_LRADC_CTRL2_TEMP_ISRC1__140 0x7
+#define BV_LRADC_CTRL2_TEMP_ISRC1__120 0x6
+#define BV_LRADC_CTRL2_TEMP_ISRC1__100 0x5
+#define BV_LRADC_CTRL2_TEMP_ISRC1__80 0x4
+#define BV_LRADC_CTRL2_TEMP_ISRC1__60 0x3
+#define BV_LRADC_CTRL2_TEMP_ISRC1__40 0x2
+#define BV_LRADC_CTRL2_TEMP_ISRC1__20 0x1
+#define BV_LRADC_CTRL2_TEMP_ISRC1__ZERO 0x0
+#define BP_LRADC_CTRL2_TEMP_ISRC0 0
+#define BM_LRADC_CTRL2_TEMP_ISRC0 0x0000000F
+#define BF_LRADC_CTRL2_TEMP_ISRC0(v) \
+ (((v) << 0) & BM_LRADC_CTRL2_TEMP_ISRC0)
+#define BV_LRADC_CTRL2_TEMP_ISRC0__300 0xF
+#define BV_LRADC_CTRL2_TEMP_ISRC0__280 0xE
+#define BV_LRADC_CTRL2_TEMP_ISRC0__260 0xD
+#define BV_LRADC_CTRL2_TEMP_ISRC0__240 0xC
+#define BV_LRADC_CTRL2_TEMP_ISRC0__220 0xB
+#define BV_LRADC_CTRL2_TEMP_ISRC0__200 0xA
+#define BV_LRADC_CTRL2_TEMP_ISRC0__180 0x9
+#define BV_LRADC_CTRL2_TEMP_ISRC0__160 0x8
+#define BV_LRADC_CTRL2_TEMP_ISRC0__140 0x7
+#define BV_LRADC_CTRL2_TEMP_ISRC0__120 0x6
+#define BV_LRADC_CTRL2_TEMP_ISRC0__100 0x5
+#define BV_LRADC_CTRL2_TEMP_ISRC0__80 0x4
+#define BV_LRADC_CTRL2_TEMP_ISRC0__60 0x3
+#define BV_LRADC_CTRL2_TEMP_ISRC0__40 0x2
+#define BV_LRADC_CTRL2_TEMP_ISRC0__20 0x1
+#define BV_LRADC_CTRL2_TEMP_ISRC0__ZERO 0x0
+HW_REGISTER(HW_LRADC_CTRL3, REGS_LRADC_BASE, 0x00000030)
+#define HW_LRADC_CTRL3_ADDR (REGS_LRADC_BASE + 0x00000030)
+#define BP_LRADC_CTRL3_DISCARD 24
+#define BM_LRADC_CTRL3_DISCARD 0x03000000
+#define BF_LRADC_CTRL3_DISCARD(v) \
+ (((v) << 24) & BM_LRADC_CTRL3_DISCARD)
+#define BV_LRADC_CTRL3_DISCARD__1_SAMPLE 0x1
+#define BV_LRADC_CTRL3_DISCARD__2_SAMPLES 0x2
+#define BV_LRADC_CTRL3_DISCARD__3_SAMPLES 0x3
+#define BM_LRADC_CTRL3_FORCE_ANALOG_PWUP 0x00800000
+#define BV_LRADC_CTRL3_FORCE_ANALOG_PWUP__OFF 0x0
+#define BV_LRADC_CTRL3_FORCE_ANALOG_PWUP__ON 0x1
+#define BM_LRADC_CTRL3_FORCE_ANALOG_PWDN 0x00400000
+#define BV_LRADC_CTRL3_FORCE_ANALOG_PWDN__ON 0x0
+#define BV_LRADC_CTRL3_FORCE_ANALOG_PWDN__OFF 0x1
+#define BP_LRADC_CTRL3_CYCLE_TIME 8
+#define BM_LRADC_CTRL3_CYCLE_TIME 0x00000300
+#define BF_LRADC_CTRL3_CYCLE_TIME(v) \
+ (((v) << 8) & BM_LRADC_CTRL3_CYCLE_TIME)
+#define BV_LRADC_CTRL3_CYCLE_TIME__6MHZ 0x0
+#define BV_LRADC_CTRL3_CYCLE_TIME__4MHZ 0x1
+#define BV_LRADC_CTRL3_CYCLE_TIME__3MHZ 0x2
+#define BV_LRADC_CTRL3_CYCLE_TIME__2MHZ 0x3
+#define BP_LRADC_CTRL3_HIGH_TIME 4
+#define BM_LRADC_CTRL3_HIGH_TIME 0x00000030
+#define BF_LRADC_CTRL3_HIGH_TIME(v) \
+ (((v) << 4) & BM_LRADC_CTRL3_HIGH_TIME)
+#define BV_LRADC_CTRL3_HIGH_TIME__42NS 0x0
+#define BV_LRADC_CTRL3_HIGH_TIME__83NS 0x1
+#define BV_LRADC_CTRL3_HIGH_TIME__125NS 0x2
+#define BV_LRADC_CTRL3_HIGH_TIME__250NS 0x3
+#define BM_LRADC_CTRL3_DELAY_CLOCK 0x00000002
+#define BV_LRADC_CTRL3_DELAY_CLOCK__NORMAL 0x0
+#define BV_LRADC_CTRL3_DELAY_CLOCK__DELAYED 0x1
+#define BM_LRADC_CTRL3_INVERT_CLOCK 0x00000001
+#define BV_LRADC_CTRL3_INVERT_CLOCK__NORMAL 0x0
+#define BV_LRADC_CTRL3_INVERT_CLOCK__INVERT 0x1
+HW_REGISTER(HW_LRADC_STATUS, REGS_LRADC_BASE, 0x00000040)
+#define HW_LRADC_STATUS_ADDR (REGS_LRADC_BASE + 0x00000040)
+#define BM_LRADC_STATUS_TEMP1_PRESENT 0x04000000
+#define BM_LRADC_STATUS_TEMP0_PRESENT 0x02000000
+#define BM_LRADC_STATUS_TOUCH_PANEL_PRESENT 0x01000000
+#define BM_LRADC_STATUS_CHANNEL7_PRESENT 0x00800000
+#define BM_LRADC_STATUS_CHANNEL6_PRESENT 0x00400000
+#define BM_LRADC_STATUS_CHANNEL5_PRESENT 0x00200000
+#define BM_LRADC_STATUS_CHANNEL4_PRESENT 0x00100000
+#define BM_LRADC_STATUS_CHANNEL3_PRESENT 0x00080000
+#define BM_LRADC_STATUS_CHANNEL2_PRESENT 0x00040000
+#define BM_LRADC_STATUS_CHANNEL1_PRESENT 0x00020000
+#define BM_LRADC_STATUS_CHANNEL0_PRESENT 0x00010000
+#define BM_LRADC_STATUS_TOUCH_DETECT_RAW 0x00000001
+#define BV_LRADC_STATUS_TOUCH_DETECT_RAW__OPEN 0x0
+#define BV_LRADC_STATUS_TOUCH_DETECT_RAW__HIT 0x1
+/*
+ * multi-register-define name HW_LRADC_CHn
+ * base 0x00000050
+ * count 6
+ * offset 0x10
+ */
+HW_REGISTER_INDEXED(HW_LRADC_CHn, REGS_LRADC_BASE, 0x00000050, 0x10)
+#define BM_LRADC_CHn_TOGGLE 0x80000000
+#define BM_LRADC_CHn_ACCUMULATE 0x20000000
+#define BP_LRADC_CHn_NUM_SAMPLES 24
+#define BM_LRADC_CHn_NUM_SAMPLES 0x1F000000
+#define BF_LRADC_CHn_NUM_SAMPLES(v) \
+ (((v) << 24) & BM_LRADC_CHn_NUM_SAMPLES)
+#define BP_LRADC_CHn_VALUE 0
+#define BM_LRADC_CHn_VALUE 0x0003FFFF
+#define BF_LRADC_CHn_VALUE(v) \
+ (((v) << 0) & BM_LRADC_CHn_VALUE)
+HW_REGISTER(HW_LRADC_CH6, REGS_LRADC_BASE, 0x000000b0)
+#define HW_LRADC_CH6_ADDR (REGS_LRADC_BASE + 0x000000b0)
+#define BM_LRADC_CH6_TOGGLE 0x80000000
+#define BM_LRADC_CH6_ACCUMULATE 0x20000000
+#define BP_LRADC_CH6_NUM_SAMPLES 24
+#define BM_LRADC_CH6_NUM_SAMPLES 0x1F000000
+#define BF_LRADC_CH6_NUM_SAMPLES(v) \
+ (((v) << 24) & BM_LRADC_CH6_NUM_SAMPLES)
+#define BP_LRADC_CH6_VALUE 0
+#define BM_LRADC_CH6_VALUE 0x0003FFFF
+#define BF_LRADC_CH6_VALUE(v) \
+ (((v) << 0) & BM_LRADC_CH6_VALUE)
+HW_REGISTER(HW_LRADC_CH7, REGS_LRADC_BASE, 0x000000c0)
+#define HW_LRADC_CH7_ADDR (REGS_LRADC_BASE + 0x000000c0)
+#define BM_LRADC_CH7_TOGGLE 0x80000000
+#define BM_LRADC_CH7_TESTMODE_TOGGLE 0x40000000
+#define BM_LRADC_CH7_ACCUMULATE 0x20000000
+#define BP_LRADC_CH7_NUM_SAMPLES 24
+#define BM_LRADC_CH7_NUM_SAMPLES 0x1F000000
+#define BF_LRADC_CH7_NUM_SAMPLES(v) \
+ (((v) << 24) & BM_LRADC_CH7_NUM_SAMPLES)
+#define BP_LRADC_CH7_VALUE 0
+#define BM_LRADC_CH7_VALUE 0x0003FFFF
+#define BF_LRADC_CH7_VALUE(v) \
+ (((v) << 0) & BM_LRADC_CH7_VALUE)
+/*
+ * multi-register-define name HW_LRADC_DELAYn
+ * base 0x000000D0
+ * count 4
+ * offset 0x10
+ */
+HW_REGISTER_INDEXED(HW_LRADC_DELAYn, REGS_LRADC_BASE, 0x000000d0, 0x10)
+#define BP_LRADC_DELAYn_TRIGGER_LRADCS 24
+#define BM_LRADC_DELAYn_TRIGGER_LRADCS 0xFF000000
+#define BF_LRADC_DELAYn_TRIGGER_LRADCS(v) \
+ (((v) << 24) & BM_LRADC_DELAYn_TRIGGER_LRADCS)
+#define BM_LRADC_DELAYn_KICK 0x00100000
+#define BP_LRADC_DELAYn_TRIGGER_DELAYS 16
+#define BM_LRADC_DELAYn_TRIGGER_DELAYS 0x000F0000
+#define BF_LRADC_DELAYn_TRIGGER_DELAYS(v) \
+ (((v) << 16) & BM_LRADC_DELAYn_TRIGGER_DELAYS)
+#define BP_LRADC_DELAYn_LOOP_COUNT 11
+#define BM_LRADC_DELAYn_LOOP_COUNT 0x0000F800
+#define BF_LRADC_DELAYn_LOOP_COUNT(v) \
+ (((v) << 11) & BM_LRADC_DELAYn_LOOP_COUNT)
+#define BP_LRADC_DELAYn_DELAY 0
+#define BM_LRADC_DELAYn_DELAY 0x000007FF
+#define BF_LRADC_DELAYn_DELAY(v) \
+ (((v) << 0) & BM_LRADC_DELAYn_DELAY)
+HW_REGISTER(HW_LRADC_DEBUG0, REGS_LRADC_BASE, 0x00000110)
+#define HW_LRADC_DEBUG0_ADDR (REGS_LRADC_BASE + 0x00000110)
+#define BP_LRADC_DEBUG0_READONLY 16
+#define BM_LRADC_DEBUG0_READONLY 0xFFFF0000
+#define BF_LRADC_DEBUG0_READONLY(v) \
+ (((v) << 16) & BM_LRADC_DEBUG0_READONLY)
+#define BP_LRADC_DEBUG0_STATE 0
+#define BM_LRADC_DEBUG0_STATE 0x00000FFF
+#define BF_LRADC_DEBUG0_STATE(v) \
+ (((v) << 0) & BM_LRADC_DEBUG0_STATE)
+HW_REGISTER(HW_LRADC_DEBUG1, REGS_LRADC_BASE, 0x00000120)
+#define HW_LRADC_DEBUG1_ADDR (REGS_LRADC_BASE + 0x00000120)
+#define BP_LRADC_DEBUG1_REQUEST 16
+#define BM_LRADC_DEBUG1_REQUEST 0x00FF0000
+#define BF_LRADC_DEBUG1_REQUEST(v) \
+ (((v) << 16) & BM_LRADC_DEBUG1_REQUEST)
+#define BP_LRADC_DEBUG1_TESTMODE_COUNT 8
+#define BM_LRADC_DEBUG1_TESTMODE_COUNT 0x00001F00
+#define BF_LRADC_DEBUG1_TESTMODE_COUNT(v) \
+ (((v) << 8) & BM_LRADC_DEBUG1_TESTMODE_COUNT)
+#define BM_LRADC_DEBUG1_TESTMODE6 0x00000004
+#define BV_LRADC_DEBUG1_TESTMODE6__NORMAL 0x0
+#define BV_LRADC_DEBUG1_TESTMODE6__TEST 0x1
+#define BM_LRADC_DEBUG1_TESTMODE5 0x00000002
+#define BV_LRADC_DEBUG1_TESTMODE5__NORMAL 0x0
+#define BV_LRADC_DEBUG1_TESTMODE5__TEST 0x1
+#define BM_LRADC_DEBUG1_TESTMODE 0x00000001
+#define BV_LRADC_DEBUG1_TESTMODE__NORMAL 0x0
+#define BV_LRADC_DEBUG1_TESTMODE__TEST 0x1
+HW_REGISTER(HW_LRADC_CONVERSION, REGS_LRADC_BASE, 0x00000130)
+#define HW_LRADC_CONVERSION_ADDR (REGS_LRADC_BASE + 0x00000130)
+#define BM_LRADC_CONVERSION_AUTOMATIC 0x00100000
+#define BV_LRADC_CONVERSION_AUTOMATIC__DISABLE 0x0
+#define BV_LRADC_CONVERSION_AUTOMATIC__ENABLE 0x1
+#define BP_LRADC_CONVERSION_SCALE_FACTOR 16
+#define BM_LRADC_CONVERSION_SCALE_FACTOR 0x00030000
+#define BF_LRADC_CONVERSION_SCALE_FACTOR(v) \
+ (((v) << 16) & BM_LRADC_CONVERSION_SCALE_FACTOR)
+#define BV_LRADC_CONVERSION_SCALE_FACTOR__NIMH 0x0
+#define BV_LRADC_CONVERSION_SCALE_FACTOR__DUAL_NIMH 0x1
+#define BV_LRADC_CONVERSION_SCALE_FACTOR__LI_ION 0x2
+#define BV_LRADC_CONVERSION_SCALE_FACTOR__ALT_LI_ION 0x3
+#define BP_LRADC_CONVERSION_SCALED_BATT_VOLTAGE 0
+#define BM_LRADC_CONVERSION_SCALED_BATT_VOLTAGE 0x000003FF
+#define BF_LRADC_CONVERSION_SCALED_BATT_VOLTAGE(v) \
+ (((v) << 0) & BM_LRADC_CONVERSION_SCALED_BATT_VOLTAGE)
+HW_REGISTER(HW_LRADC_CTRL4, REGS_LRADC_BASE, 0x00000140)
+#define HW_LRADC_CTRL4_ADDR (REGS_LRADC_BASE + 0x00000140)
+#define BP_LRADC_CTRL4_LRADC7SELECT 28
+#define BM_LRADC_CTRL4_LRADC7SELECT 0xF0000000
+#define BF_LRADC_CTRL4_LRADC7SELECT(v) \
+ (((v) << 28) & BM_LRADC_CTRL4_LRADC7SELECT)
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL0 0x0
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL1 0x1
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL2 0x2
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL3 0x3
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL4 0x4
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL5 0x5
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL6 0x6
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL7 0x7
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL8 0x8
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL9 0x9
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL10 0xA
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL11 0xB
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL12 0xC
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL13 0xD
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL14 0xE
+#define BV_LRADC_CTRL4_LRADC7SELECT__CHANNEL15 0xF
+#define BP_LRADC_CTRL4_LRADC6SELECT 24
+#define BM_LRADC_CTRL4_LRADC6SELECT 0x0F000000
+#define BF_LRADC_CTRL4_LRADC6SELECT(v) \
+ (((v) << 24) & BM_LRADC_CTRL4_LRADC6SELECT)
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL0 0x0
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL1 0x1
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL2 0x2
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL3 0x3
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL4 0x4
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL5 0x5
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL6 0x6
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL7 0x7
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL8 0x8
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL9 0x9
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL10 0xA
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL11 0xB
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL12 0xC
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL13 0xD
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL14 0xE
+#define BV_LRADC_CTRL4_LRADC6SELECT__CHANNEL15 0xF
+#define BP_LRADC_CTRL4_LRADC5SELECT 20
+#define BM_LRADC_CTRL4_LRADC5SELECT 0x00F00000
+#define BF_LRADC_CTRL4_LRADC5SELECT(v) \
+ (((v) << 20) & BM_LRADC_CTRL4_LRADC5SELECT)
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL0 0x0
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL1 0x1
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL2 0x2
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL3 0x3
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL4 0x4
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL5 0x5
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL6 0x6
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL7 0x7
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL8 0x8
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL9 0x9
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL10 0xA
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL11 0xB
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL12 0xC
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL13 0xD
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL14 0xE
+#define BV_LRADC_CTRL4_LRADC5SELECT__CHANNEL15 0xF
+#define BP_LRADC_CTRL4_LRADC4SELECT 16
+#define BM_LRADC_CTRL4_LRADC4SELECT 0x000F0000
+#define BF_LRADC_CTRL4_LRADC4SELECT(v) \
+ (((v) << 16) & BM_LRADC_CTRL4_LRADC4SELECT)
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL0 0x0
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL1 0x1
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL2 0x2
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL3 0x3
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL4 0x4
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL5 0x5
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL6 0x6
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL7 0x7
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL8 0x8
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL9 0x9
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL10 0xA
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL11 0xB
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL12 0xC
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL13 0xD
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL14 0xE
+#define BV_LRADC_CTRL4_LRADC4SELECT__CHANNEL15 0xF
+#define BP_LRADC_CTRL4_LRADC3SELECT 12
+#define BM_LRADC_CTRL4_LRADC3SELECT 0x0000F000
+#define BF_LRADC_CTRL4_LRADC3SELECT(v) \
+ (((v) << 12) & BM_LRADC_CTRL4_LRADC3SELECT)
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL0 0x0
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL1 0x1
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL2 0x2
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL3 0x3
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL4 0x4
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL5 0x5
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL6 0x6
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL7 0x7
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL8 0x8
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL9 0x9
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL10 0xA
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL11 0xB
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL12 0xC
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL13 0xD
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL14 0xE
+#define BV_LRADC_CTRL4_LRADC3SELECT__CHANNEL15 0xF
+#define BP_LRADC_CTRL4_LRADC2SELECT 8
+#define BM_LRADC_CTRL4_LRADC2SELECT 0x00000F00
+#define BF_LRADC_CTRL4_LRADC2SELECT(v) \
+ (((v) << 8) & BM_LRADC_CTRL4_LRADC2SELECT)
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL0 0x0
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL1 0x1
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL2 0x2
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL3 0x3
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL4 0x4
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL5 0x5
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL6 0x6
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL7 0x7
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL8 0x8
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL9 0x9
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL10 0xA
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL11 0xB
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL12 0xC
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL13 0xD
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL14 0xE
+#define BV_LRADC_CTRL4_LRADC2SELECT__CHANNEL15 0xF
+#define BP_LRADC_CTRL4_LRADC1SELECT 4
+#define BM_LRADC_CTRL4_LRADC1SELECT 0x000000F0
+#define BF_LRADC_CTRL4_LRADC1SELECT(v) \
+ (((v) << 4) & BM_LRADC_CTRL4_LRADC1SELECT)
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL0 0x0
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL1 0x1
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL2 0x2
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL3 0x3
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL4 0x4
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL5 0x5
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL6 0x6
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL7 0x7
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL8 0x8
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL9 0x9
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL10 0xA
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL11 0xB
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL12 0xC
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL13 0xD
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL14 0xE
+#define BV_LRADC_CTRL4_LRADC1SELECT__CHANNEL15 0xF
+#define BP_LRADC_CTRL4_LRADC0SELECT 0
+#define BM_LRADC_CTRL4_LRADC0SELECT 0x0000000F
+#define BF_LRADC_CTRL4_LRADC0SELECT(v) \
+ (((v) << 0) & BM_LRADC_CTRL4_LRADC0SELECT)
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL0 0x0
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL1 0x1
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL2 0x2
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL3 0x3
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL4 0x4
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL5 0x5
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL6 0x6
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL7 0x7
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL8 0x8
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL9 0x9
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL10 0xA
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL11 0xB
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL12 0xC
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL13 0xD
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL14 0xE
+#define BV_LRADC_CTRL4_LRADC0SELECT__CHANNEL15 0xF
+HW_REGISTER_0(HW_LRADC_VERSION, REGS_LRADC_BASE, 0x00000150)
+#define HW_LRADC_VERSION_ADDR (REGS_LRADC_BASE + 0x00000150)
+#define BP_LRADC_VERSION_MAJOR 24
+#define BM_LRADC_VERSION_MAJOR 0xFF000000
+#define BF_LRADC_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_LRADC_VERSION_MAJOR)
+#define BP_LRADC_VERSION_MINOR 16
+#define BM_LRADC_VERSION_MINOR 0x00FF0000
+#define BF_LRADC_VERSION_MINOR(v) \
+ (((v) << 16) & BM_LRADC_VERSION_MINOR)
+#define BP_LRADC_VERSION_STEP 0
+#define BM_LRADC_VERSION_STEP 0x0000FFFF
+#define BF_LRADC_VERSION_STEP(v) \
+ (((v) << 0) & BM_LRADC_VERSION_STEP)
+#endif /* __ARCH_ARM___LRADC_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-ocotp.h b/arch/arm/mach-stmp3xxx/include/mach/regs-ocotp.h
new file mode 100644
index 000000000000..97358459689c
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-ocotp.h
@@ -0,0 +1,166 @@
+/*
+ * STMP OCOTP Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___OCOTP_H
+#define __ARCH_ARM___OCOTP_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_OCOTP_BASE (REGS_BASE + 0x2c000)
+#define REGS_OCOTP_BASE_PHYS (0x8002C000)
+#define REGS_OCOTP_SIZE 0x00002000
+HW_REGISTER(HW_OCOTP_CTRL, REGS_OCOTP_BASE, 0x00000000)
+#define HW_OCOTP_CTRL_ADDR (REGS_OCOTP_BASE + 0x00000000)
+#define BP_OCOTP_CTRL_WR_UNLOCK 16
+#define BM_OCOTP_CTRL_WR_UNLOCK 0xFFFF0000
+#define BF_OCOTP_CTRL_WR_UNLOCK(v) \
+ (((v) << 16) & BM_OCOTP_CTRL_WR_UNLOCK)
+#define BV_OCOTP_CTRL_WR_UNLOCK__KEY 0x3E77
+#define BM_OCOTP_CTRL_RELOAD_SHADOWS 0x00002000
+#define BM_OCOTP_CTRL_RD_BANK_OPEN 0x00001000
+#define BM_OCOTP_CTRL_ERROR 0x00000200
+#define BM_OCOTP_CTRL_BUSY 0x00000100
+#define BP_OCOTP_CTRL_ADDR 0
+#define BM_OCOTP_CTRL_ADDR 0x0000001F
+#define BF_OCOTP_CTRL_ADDR(v) \
+ (((v) << 0) & BM_OCOTP_CTRL_ADDR)
+HW_REGISTER_0(HW_OCOTP_DATA, REGS_OCOTP_BASE, 0x00000010)
+#define HW_OCOTP_DATA_ADDR (REGS_OCOTP_BASE + 0x00000010)
+#define BP_OCOTP_DATA_DATA 0
+#define BM_OCOTP_DATA_DATA 0xFFFFFFFF
+#define BF_OCOTP_DATA_DATA(v) (v)
+/*
+ * multi-register-define name HW_OCOTP_CUSTn
+ * base 0x00000020
+ * count 4
+ * offset 0x10
+ */
+HW_REGISTER_0_INDEXED(HW_OCOTP_CUSTn, REGS_OCOTP_BASE, 0x00000020, 0x10)
+#define BP_OCOTP_CUSTn_BITS 0
+#define BM_OCOTP_CUSTn_BITS 0xFFFFFFFF
+#define BF_OCOTP_CUSTn_BITS(v) (v)
+/*
+ * multi-register-define name HW_OCOTP_CRYPTOn
+ * base 0x00000060
+ * count 4
+ * offset 0x10
+ */
+HW_REGISTER_0_INDEXED(HW_OCOTP_CRYPTOn, REGS_OCOTP_BASE, 0x00000060, 0x10)
+#define BP_OCOTP_CRYPTOn_BITS 0
+#define BM_OCOTP_CRYPTOn_BITS 0xFFFFFFFF
+#define BF_OCOTP_CRYPTOn_BITS(v) (v)
+/*
+ * multi-register-define name HW_OCOTP_HWCAPn
+ * base 0x000000A0
+ * count 6
+ * offset 0x10
+ */
+HW_REGISTER_0_INDEXED(HW_OCOTP_HWCAPn, REGS_OCOTP_BASE, 0x000000a0, 0x10)
+#define BP_OCOTP_HWCAPn_BITS 0
+#define BM_OCOTP_HWCAPn_BITS 0xFFFFFFFF
+#define BF_OCOTP_HWCAPn_BITS(v) (v)
+HW_REGISTER_0(HW_OCOTP_SWCAP, REGS_OCOTP_BASE, 0x00000100)
+#define HW_OCOTP_SWCAP_ADDR (REGS_OCOTP_BASE + 0x00000100)
+#define BP_OCOTP_SWCAP_BITS 0
+#define BM_OCOTP_SWCAP_BITS 0xFFFFFFFF
+#define BF_OCOTP_SWCAP_BITS(v) (v)
+HW_REGISTER_0(HW_OCOTP_CUSTCAP, REGS_OCOTP_BASE, 0x00000110)
+#define HW_OCOTP_CUSTCAP_ADDR (REGS_OCOTP_BASE + 0x00000110)
+#define BP_OCOTP_CUSTCAP_BITS 0
+#define BM_OCOTP_CUSTCAP_BITS 0xFFFFFFFF
+#define BF_OCOTP_CUSTCAP_BITS(v) (v)
+HW_REGISTER_0(HW_OCOTP_LOCK, REGS_OCOTP_BASE, 0x00000120)
+#define HW_OCOTP_LOCK_ADDR (REGS_OCOTP_BASE + 0x00000120)
+#define BM_OCOTP_LOCK_ROM7 0x80000000
+#define BM_OCOTP_LOCK_ROM6 0x40000000
+#define BM_OCOTP_LOCK_ROM5 0x20000000
+#define BM_OCOTP_LOCK_ROM4 0x10000000
+#define BM_OCOTP_LOCK_ROM3 0x08000000
+#define BM_OCOTP_LOCK_ROM2 0x04000000
+#define BM_OCOTP_LOCK_ROM1 0x02000000
+#define BM_OCOTP_LOCK_ROM0 0x01000000
+#define BM_OCOTP_LOCK_HWSW_SHADOW_ALT 0x00800000
+#define BM_OCOTP_LOCK_CRYPTODCP_ALT 0x00400000
+#define BM_OCOTP_LOCK_CRYPTOKEY_ALT 0x00200000
+#define BM_OCOTP_LOCK_PIN 0x00100000
+#define BM_OCOTP_LOCK_OPS 0x00080000
+#define BM_OCOTP_LOCK_UN2 0x00040000
+#define BM_OCOTP_LOCK_UN1 0x00020000
+#define BM_OCOTP_LOCK_UN0 0x00010000
+#define BP_OCOTP_LOCK_UNALLOCATED 11
+#define BM_OCOTP_LOCK_UNALLOCATED 0x0000F800
+#define BF_OCOTP_LOCK_UNALLOCATED(v) \
+ (((v) << 11) & BM_OCOTP_LOCK_UNALLOCATED)
+#define BM_OCOTP_LOCK_ROM_SHADOW 0x00000400
+#define BM_OCOTP_LOCK_CUSTCAP 0x00000200
+#define BM_OCOTP_LOCK_HWSW 0x00000100
+#define BM_OCOTP_LOCK_CUSTCAP_SHADOW 0x00000080
+#define BM_OCOTP_LOCK_HWSW_SHADOW 0x00000040
+#define BM_OCOTP_LOCK_CRYPTODCP 0x00000020
+#define BM_OCOTP_LOCK_CRYPTOKEY 0x00000010
+#define BM_OCOTP_LOCK_CUST3 0x00000008
+#define BM_OCOTP_LOCK_CUST2 0x00000004
+#define BM_OCOTP_LOCK_CUST1 0x00000002
+#define BM_OCOTP_LOCK_CUST0 0x00000001
+/*
+ * multi-register-define name HW_OCOTP_OPSn
+ * base 0x00000130
+ * count 4
+ * offset 0x10
+ */
+HW_REGISTER_0_INDEXED(HW_OCOTP_OPSn, REGS_OCOTP_BASE, 0x00000130, 0x10)
+#define BP_OCOTP_OPSn_BITS 0
+#define BM_OCOTP_OPSn_BITS 0xFFFFFFFF
+#define BF_OCOTP_OPSn_BITS(v) (v)
+/*
+ * multi-register-define name HW_OCOTP_UNn
+ * base 0x00000170
+ * count 3
+ * offset 0x10
+ */
+HW_REGISTER_0_INDEXED(HW_OCOTP_UNn, REGS_OCOTP_BASE, 0x00000170, 0x10)
+#define BP_OCOTP_UNn_BITS 0
+#define BM_OCOTP_UNn_BITS 0xFFFFFFFF
+#define BF_OCOTP_UNn_BITS(v) (v)
+/*
+ * multi-register-define name HW_OCOTP_ROMn
+ * base 0x000001A0
+ * count 8
+ * offset 0x10
+ */
+HW_REGISTER_0_INDEXED(HW_OCOTP_ROMn, REGS_OCOTP_BASE, 0x000001a0, 0x10)
+#define BP_OCOTP_ROMn_BITS 0
+#define BM_OCOTP_ROMn_BITS 0xFFFFFFFF
+#define BF_OCOTP_ROMn_BITS(v) (v)
+HW_REGISTER_0(HW_OCOTP_VERSION, REGS_OCOTP_BASE, 0x00000220)
+#define HW_OCOTP_VERSION_ADDR (REGS_OCOTP_BASE + 0x00000220)
+#define BP_OCOTP_VERSION_MAJOR 24
+#define BM_OCOTP_VERSION_MAJOR 0xFF000000
+#define BF_OCOTP_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_OCOTP_VERSION_MAJOR)
+#define BP_OCOTP_VERSION_MINOR 16
+#define BM_OCOTP_VERSION_MINOR 0x00FF0000
+#define BF_OCOTP_VERSION_MINOR(v) \
+ (((v) << 16) & BM_OCOTP_VERSION_MINOR)
+#define BP_OCOTP_VERSION_STEP 0
+#define BM_OCOTP_VERSION_STEP 0x0000FFFF
+#define BF_OCOTP_VERSION_STEP(v) \
+ (((v) << 0) & BM_OCOTP_VERSION_STEP)
+#endif /* __ARCH_ARM___OCOTP_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-pinctrl.h b/arch/arm/mach-stmp3xxx/include/mach/regs-pinctrl.h
new file mode 100644
index 000000000000..00ed07d45384
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-pinctrl.h
@@ -0,0 +1,1255 @@
+/*
+ * STMP PINCTRL Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___PINCTRL_H
+#define __ARCH_ARM___PINCTRL_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_PINCTRL_BASE (REGS_BASE + 0x18000)
+#define REGS_PINCTRL_BASE_PHYS (0x80018000)
+#define REGS_PINCTRL_SIZE 0x00002000
+HW_REGISTER(HW_PINCTRL_CTRL, REGS_PINCTRL_BASE, 0x00000000)
+#define HW_PINCTRL_CTRL_ADDR (REGS_PINCTRL_BASE + 0x00000000)
+#define BM_PINCTRL_CTRL_SFTRST 0x80000000
+#define BM_PINCTRL_CTRL_CLKGATE 0x40000000
+#define BM_PINCTRL_CTRL_PRESENT3 0x08000000
+#define BM_PINCTRL_CTRL_PRESENT2 0x04000000
+#define BM_PINCTRL_CTRL_PRESENT1 0x02000000
+#define BM_PINCTRL_CTRL_PRESENT0 0x01000000
+#define BM_PINCTRL_CTRL_IRQOUT2 0x00000004
+#define BM_PINCTRL_CTRL_IRQOUT1 0x00000002
+#define BM_PINCTRL_CTRL_IRQOUT0 0x00000001
+HW_REGISTER(HW_PINCTRL_MUXSEL0, REGS_PINCTRL_BASE, 0x00000100)
+#define HW_PINCTRL_MUXSEL0_ADDR (REGS_PINCTRL_BASE + 0x00000100)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN15 30
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN15 0xC0000000
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN15(v) \
+ (((v) << 30) & BM_PINCTRL_MUXSEL0_BANK0_PIN15)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN14 28
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN14 0x30000000
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN14(v) \
+ (((v) << 28) & BM_PINCTRL_MUXSEL0_BANK0_PIN14)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN13 26
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN13 0x0C000000
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN13(v) \
+ (((v) << 26) & BM_PINCTRL_MUXSEL0_BANK0_PIN13)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN12 24
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN12 0x03000000
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN12(v) \
+ (((v) << 24) & BM_PINCTRL_MUXSEL0_BANK0_PIN12)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN11 22
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN11 0x00C00000
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN11(v) \
+ (((v) << 22) & BM_PINCTRL_MUXSEL0_BANK0_PIN11)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN10 20
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN10 0x00300000
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN10(v) \
+ (((v) << 20) & BM_PINCTRL_MUXSEL0_BANK0_PIN10)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN09 18
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN09 0x000C0000
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN09(v) \
+ (((v) << 18) & BM_PINCTRL_MUXSEL0_BANK0_PIN09)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN08 16
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN08 0x00030000
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN08(v) \
+ (((v) << 16) & BM_PINCTRL_MUXSEL0_BANK0_PIN08)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN07 14
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN07 0x0000C000
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN07(v) \
+ (((v) << 14) & BM_PINCTRL_MUXSEL0_BANK0_PIN07)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN06 12
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN06 0x00003000
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN06(v) \
+ (((v) << 12) & BM_PINCTRL_MUXSEL0_BANK0_PIN06)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN05 10
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN05 0x00000C00
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN05(v) \
+ (((v) << 10) & BM_PINCTRL_MUXSEL0_BANK0_PIN05)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN04 8
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN04 0x00000300
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN04(v) \
+ (((v) << 8) & BM_PINCTRL_MUXSEL0_BANK0_PIN04)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN03 6
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN03 0x000000C0
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN03(v) \
+ (((v) << 6) & BM_PINCTRL_MUXSEL0_BANK0_PIN03)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN02 4
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN02 0x00000030
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN02(v) \
+ (((v) << 4) & BM_PINCTRL_MUXSEL0_BANK0_PIN02)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN01 2
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN01 0x0000000C
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN01(v) \
+ (((v) << 2) & BM_PINCTRL_MUXSEL0_BANK0_PIN01)
+#define BP_PINCTRL_MUXSEL0_BANK0_PIN00 0
+#define BM_PINCTRL_MUXSEL0_BANK0_PIN00 0x00000003
+#define BF_PINCTRL_MUXSEL0_BANK0_PIN00(v) \
+ (((v) << 0) & BM_PINCTRL_MUXSEL0_BANK0_PIN00)
+HW_REGISTER(HW_PINCTRL_MUXSEL1, REGS_PINCTRL_BASE, 0x00000110)
+#define HW_PINCTRL_MUXSEL1_ADDR (REGS_PINCTRL_BASE + 0x00000110)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN31 30
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN31 0xC0000000
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN31(v) \
+ (((v) << 30) & BM_PINCTRL_MUXSEL1_BANK0_PIN31)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN30 28
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN30 0x30000000
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN30(v) \
+ (((v) << 28) & BM_PINCTRL_MUXSEL1_BANK0_PIN30)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN29 26
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN29 0x0C000000
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN29(v) \
+ (((v) << 26) & BM_PINCTRL_MUXSEL1_BANK0_PIN29)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN28 24
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN28 0x03000000
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN28(v) \
+ (((v) << 24) & BM_PINCTRL_MUXSEL1_BANK0_PIN28)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN27 22
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN27 0x00C00000
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN27(v) \
+ (((v) << 22) & BM_PINCTRL_MUXSEL1_BANK0_PIN27)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN26 20
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN26 0x00300000
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN26(v) \
+ (((v) << 20) & BM_PINCTRL_MUXSEL1_BANK0_PIN26)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN25 18
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN25 0x000C0000
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN25(v) \
+ (((v) << 18) & BM_PINCTRL_MUXSEL1_BANK0_PIN25)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN24 16
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN24 0x00030000
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN24(v) \
+ (((v) << 16) & BM_PINCTRL_MUXSEL1_BANK0_PIN24)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN23 14
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN23 0x0000C000
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN23(v) \
+ (((v) << 14) & BM_PINCTRL_MUXSEL1_BANK0_PIN23)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN22 12
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN22 0x00003000
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN22(v) \
+ (((v) << 12) & BM_PINCTRL_MUXSEL1_BANK0_PIN22)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN21 10
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN21 0x00000C00
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN21(v) \
+ (((v) << 10) & BM_PINCTRL_MUXSEL1_BANK0_PIN21)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN20 8
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN20 0x00000300
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN20(v) \
+ (((v) << 8) & BM_PINCTRL_MUXSEL1_BANK0_PIN20)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN19 6
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN19 0x000000C0
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN19(v) \
+ (((v) << 6) & BM_PINCTRL_MUXSEL1_BANK0_PIN19)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN18 4
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN18 0x00000030
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN18(v) \
+ (((v) << 4) & BM_PINCTRL_MUXSEL1_BANK0_PIN18)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN17 2
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN17 0x0000000C
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN17(v) \
+ (((v) << 2) & BM_PINCTRL_MUXSEL1_BANK0_PIN17)
+#define BP_PINCTRL_MUXSEL1_BANK0_PIN16 0
+#define BM_PINCTRL_MUXSEL1_BANK0_PIN16 0x00000003
+#define BF_PINCTRL_MUXSEL1_BANK0_PIN16(v) \
+ (((v) << 0) & BM_PINCTRL_MUXSEL1_BANK0_PIN16)
+HW_REGISTER(HW_PINCTRL_MUXSEL2, REGS_PINCTRL_BASE, 0x00000120)
+#define HW_PINCTRL_MUXSEL2_ADDR (REGS_PINCTRL_BASE + 0x00000120)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN15 30
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN15 0xC0000000
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN15(v) \
+ (((v) << 30) & BM_PINCTRL_MUXSEL2_BANK1_PIN15)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN14 28
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN14 0x30000000
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN14(v) \
+ (((v) << 28) & BM_PINCTRL_MUXSEL2_BANK1_PIN14)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN13 26
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN13 0x0C000000
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN13(v) \
+ (((v) << 26) & BM_PINCTRL_MUXSEL2_BANK1_PIN13)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN12 24
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN12 0x03000000
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN12(v) \
+ (((v) << 24) & BM_PINCTRL_MUXSEL2_BANK1_PIN12)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN11 22
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN11 0x00C00000
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN11(v) \
+ (((v) << 22) & BM_PINCTRL_MUXSEL2_BANK1_PIN11)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN10 20
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN10 0x00300000
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN10(v) \
+ (((v) << 20) & BM_PINCTRL_MUXSEL2_BANK1_PIN10)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN09 18
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN09 0x000C0000
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN09(v) \
+ (((v) << 18) & BM_PINCTRL_MUXSEL2_BANK1_PIN09)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN08 16
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN08 0x00030000
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN08(v) \
+ (((v) << 16) & BM_PINCTRL_MUXSEL2_BANK1_PIN08)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN07 14
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN07 0x0000C000
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN07(v) \
+ (((v) << 14) & BM_PINCTRL_MUXSEL2_BANK1_PIN07)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN06 12
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN06 0x00003000
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN06(v) \
+ (((v) << 12) & BM_PINCTRL_MUXSEL2_BANK1_PIN06)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN05 10
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN05 0x00000C00
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN05(v) \
+ (((v) << 10) & BM_PINCTRL_MUXSEL2_BANK1_PIN05)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN04 8
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN04 0x00000300
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN04(v) \
+ (((v) << 8) & BM_PINCTRL_MUXSEL2_BANK1_PIN04)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN03 6
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN03 0x000000C0
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN03(v) \
+ (((v) << 6) & BM_PINCTRL_MUXSEL2_BANK1_PIN03)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN02 4
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN02 0x00000030
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN02(v) \
+ (((v) << 4) & BM_PINCTRL_MUXSEL2_BANK1_PIN02)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN01 2
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN01 0x0000000C
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN01(v) \
+ (((v) << 2) & BM_PINCTRL_MUXSEL2_BANK1_PIN01)
+#define BP_PINCTRL_MUXSEL2_BANK1_PIN00 0
+#define BM_PINCTRL_MUXSEL2_BANK1_PIN00 0x00000003
+#define BF_PINCTRL_MUXSEL2_BANK1_PIN00(v) \
+ (((v) << 0) & BM_PINCTRL_MUXSEL2_BANK1_PIN00)
+HW_REGISTER(HW_PINCTRL_MUXSEL3, REGS_PINCTRL_BASE, 0x00000130)
+#define HW_PINCTRL_MUXSEL3_ADDR (REGS_PINCTRL_BASE + 0x00000130)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN30 28
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN30 0x30000000
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN30(v) \
+ (((v) << 28) & BM_PINCTRL_MUXSEL3_BANK1_PIN30)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN29 26
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN29 0x0C000000
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN29(v) \
+ (((v) << 26) & BM_PINCTRL_MUXSEL3_BANK1_PIN29)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN28 24
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN28 0x03000000
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN28(v) \
+ (((v) << 24) & BM_PINCTRL_MUXSEL3_BANK1_PIN28)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN27 22
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN27 0x00C00000
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN27(v) \
+ (((v) << 22) & BM_PINCTRL_MUXSEL3_BANK1_PIN27)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN26 20
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN26 0x00300000
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN26(v) \
+ (((v) << 20) & BM_PINCTRL_MUXSEL3_BANK1_PIN26)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN25 18
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN25 0x000C0000
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN25(v) \
+ (((v) << 18) & BM_PINCTRL_MUXSEL3_BANK1_PIN25)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN24 16
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN24 0x00030000
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN24(v) \
+ (((v) << 16) & BM_PINCTRL_MUXSEL3_BANK1_PIN24)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN23 14
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN23 0x0000C000
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN23(v) \
+ (((v) << 14) & BM_PINCTRL_MUXSEL3_BANK1_PIN23)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN22 12
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN22 0x00003000
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN22(v) \
+ (((v) << 12) & BM_PINCTRL_MUXSEL3_BANK1_PIN22)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN21 10
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN21 0x00000C00
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN21(v) \
+ (((v) << 10) & BM_PINCTRL_MUXSEL3_BANK1_PIN21)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN20 8
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN20 0x00000300
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN20(v) \
+ (((v) << 8) & BM_PINCTRL_MUXSEL3_BANK1_PIN20)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN19 6
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN19 0x000000C0
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN19(v) \
+ (((v) << 6) & BM_PINCTRL_MUXSEL3_BANK1_PIN19)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN18 4
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN18 0x00000030
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN18(v) \
+ (((v) << 4) & BM_PINCTRL_MUXSEL3_BANK1_PIN18)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN17 2
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN17 0x0000000C
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN17(v) \
+ (((v) << 2) & BM_PINCTRL_MUXSEL3_BANK1_PIN17)
+#define BP_PINCTRL_MUXSEL3_BANK1_PIN16 0
+#define BM_PINCTRL_MUXSEL3_BANK1_PIN16 0x00000003
+#define BF_PINCTRL_MUXSEL3_BANK1_PIN16(v) \
+ (((v) << 0) & BM_PINCTRL_MUXSEL3_BANK1_PIN16)
+HW_REGISTER(HW_PINCTRL_MUXSEL4, REGS_PINCTRL_BASE, 0x00000140)
+#define HW_PINCTRL_MUXSEL4_ADDR (REGS_PINCTRL_BASE + 0x00000140)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN15 30
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN15 0xC0000000
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN15(v) \
+ (((v) << 30) & BM_PINCTRL_MUXSEL4_BANK2_PIN15)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN14 28
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN14 0x30000000
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN14(v) \
+ (((v) << 28) & BM_PINCTRL_MUXSEL4_BANK2_PIN14)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN13 26
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN13 0x0C000000
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN13(v) \
+ (((v) << 26) & BM_PINCTRL_MUXSEL4_BANK2_PIN13)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN12 24
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN12 0x03000000
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN12(v) \
+ (((v) << 24) & BM_PINCTRL_MUXSEL4_BANK2_PIN12)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN11 22
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN11 0x00C00000
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN11(v) \
+ (((v) << 22) & BM_PINCTRL_MUXSEL4_BANK2_PIN11)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN10 20
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN10 0x00300000
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN10(v) \
+ (((v) << 20) & BM_PINCTRL_MUXSEL4_BANK2_PIN10)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN09 18
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN09 0x000C0000
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN09(v) \
+ (((v) << 18) & BM_PINCTRL_MUXSEL4_BANK2_PIN09)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN08 16
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN08 0x00030000
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN08(v) \
+ (((v) << 16) & BM_PINCTRL_MUXSEL4_BANK2_PIN08)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN07 14
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN07 0x0000C000
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN07(v) \
+ (((v) << 14) & BM_PINCTRL_MUXSEL4_BANK2_PIN07)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN06 12
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN06 0x00003000
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN06(v) \
+ (((v) << 12) & BM_PINCTRL_MUXSEL4_BANK2_PIN06)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN05 10
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN05 0x00000C00
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN05(v) \
+ (((v) << 10) & BM_PINCTRL_MUXSEL4_BANK2_PIN05)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN04 8
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN04 0x00000300
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN04(v) \
+ (((v) << 8) & BM_PINCTRL_MUXSEL4_BANK2_PIN04)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN03 6
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN03 0x000000C0
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN03(v) \
+ (((v) << 6) & BM_PINCTRL_MUXSEL4_BANK2_PIN03)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN02 4
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN02 0x00000030
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN02(v) \
+ (((v) << 4) & BM_PINCTRL_MUXSEL4_BANK2_PIN02)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN01 2
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN01 0x0000000C
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN01(v) \
+ (((v) << 2) & BM_PINCTRL_MUXSEL4_BANK2_PIN01)
+#define BP_PINCTRL_MUXSEL4_BANK2_PIN00 0
+#define BM_PINCTRL_MUXSEL4_BANK2_PIN00 0x00000003
+#define BF_PINCTRL_MUXSEL4_BANK2_PIN00(v) \
+ (((v) << 0) & BM_PINCTRL_MUXSEL4_BANK2_PIN00)
+HW_REGISTER(HW_PINCTRL_MUXSEL5, REGS_PINCTRL_BASE, 0x00000150)
+#define HW_PINCTRL_MUXSEL5_ADDR (REGS_PINCTRL_BASE + 0x00000150)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN31 30
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN31 0xC0000000
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN31(v) \
+ (((v) << 30) & BM_PINCTRL_MUXSEL5_BANK2_PIN31)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN30 28
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN30 0x30000000
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN30(v) \
+ (((v) << 28) & BM_PINCTRL_MUXSEL5_BANK2_PIN30)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN29 26
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN29 0x0C000000
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN29(v) \
+ (((v) << 26) & BM_PINCTRL_MUXSEL5_BANK2_PIN29)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN28 24
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN28 0x03000000
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN28(v) \
+ (((v) << 24) & BM_PINCTRL_MUXSEL5_BANK2_PIN28)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN27 22
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN27 0x00C00000
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN27(v) \
+ (((v) << 22) & BM_PINCTRL_MUXSEL5_BANK2_PIN27)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN26 20
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN26 0x00300000
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN26(v) \
+ (((v) << 20) & BM_PINCTRL_MUXSEL5_BANK2_PIN26)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN25 18
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN25 0x000C0000
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN25(v) \
+ (((v) << 18) & BM_PINCTRL_MUXSEL5_BANK2_PIN25)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN24 16
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN24 0x00030000
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN24(v) \
+ (((v) << 16) & BM_PINCTRL_MUXSEL5_BANK2_PIN24)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN23 14
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN23 0x0000C000
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN23(v) \
+ (((v) << 14) & BM_PINCTRL_MUXSEL5_BANK2_PIN23)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN22 12
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN22 0x00003000
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN22(v) \
+ (((v) << 12) & BM_PINCTRL_MUXSEL5_BANK2_PIN22)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN21 10
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN21 0x00000C00
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN21(v) \
+ (((v) << 10) & BM_PINCTRL_MUXSEL5_BANK2_PIN21)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN20 8
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN20 0x00000300
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN20(v) \
+ (((v) << 8) & BM_PINCTRL_MUXSEL5_BANK2_PIN20)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN19 6
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN19 0x000000C0
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN19(v) \
+ (((v) << 6) & BM_PINCTRL_MUXSEL5_BANK2_PIN19)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN18 4
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN18 0x00000030
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN18(v) \
+ (((v) << 4) & BM_PINCTRL_MUXSEL5_BANK2_PIN18)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN17 2
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN17 0x0000000C
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN17(v) \
+ (((v) << 2) & BM_PINCTRL_MUXSEL5_BANK2_PIN17)
+#define BP_PINCTRL_MUXSEL5_BANK2_PIN16 0
+#define BM_PINCTRL_MUXSEL5_BANK2_PIN16 0x00000003
+#define BF_PINCTRL_MUXSEL5_BANK2_PIN16(v) \
+ (((v) << 0) & BM_PINCTRL_MUXSEL5_BANK2_PIN16)
+HW_REGISTER(HW_PINCTRL_MUXSEL6, REGS_PINCTRL_BASE, 0x00000160)
+#define HW_PINCTRL_MUXSEL6_ADDR (REGS_PINCTRL_BASE + 0x00000160)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN15 30
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN15 0xC0000000
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN15(v) \
+ (((v) << 30) & BM_PINCTRL_MUXSEL6_BANK3_PIN15)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN14 28
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN14 0x30000000
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN14(v) \
+ (((v) << 28) & BM_PINCTRL_MUXSEL6_BANK3_PIN14)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN13 26
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN13 0x0C000000
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN13(v) \
+ (((v) << 26) & BM_PINCTRL_MUXSEL6_BANK3_PIN13)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN12 24
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN12 0x03000000
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN12(v) \
+ (((v) << 24) & BM_PINCTRL_MUXSEL6_BANK3_PIN12)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN11 22
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN11 0x00C00000
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN11(v) \
+ (((v) << 22) & BM_PINCTRL_MUXSEL6_BANK3_PIN11)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN10 20
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN10 0x00300000
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN10(v) \
+ (((v) << 20) & BM_PINCTRL_MUXSEL6_BANK3_PIN10)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN09 18
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN09 0x000C0000
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN09(v) \
+ (((v) << 18) & BM_PINCTRL_MUXSEL6_BANK3_PIN09)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN08 16
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN08 0x00030000
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN08(v) \
+ (((v) << 16) & BM_PINCTRL_MUXSEL6_BANK3_PIN08)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN07 14
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN07 0x0000C000
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN07(v) \
+ (((v) << 14) & BM_PINCTRL_MUXSEL6_BANK3_PIN07)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN06 12
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN06 0x00003000
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN06(v) \
+ (((v) << 12) & BM_PINCTRL_MUXSEL6_BANK3_PIN06)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN05 10
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN05 0x00000C00
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN05(v) \
+ (((v) << 10) & BM_PINCTRL_MUXSEL6_BANK3_PIN05)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN04 8
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN04 0x00000300
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN04(v) \
+ (((v) << 8) & BM_PINCTRL_MUXSEL6_BANK3_PIN04)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN03 6
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN03 0x000000C0
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN03(v) \
+ (((v) << 6) & BM_PINCTRL_MUXSEL6_BANK3_PIN03)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN02 4
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN02 0x00000030
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN02(v) \
+ (((v) << 4) & BM_PINCTRL_MUXSEL6_BANK3_PIN02)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN01 2
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN01 0x0000000C
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN01(v) \
+ (((v) << 2) & BM_PINCTRL_MUXSEL6_BANK3_PIN01)
+#define BP_PINCTRL_MUXSEL6_BANK3_PIN00 0
+#define BM_PINCTRL_MUXSEL6_BANK3_PIN00 0x00000003
+#define BF_PINCTRL_MUXSEL6_BANK3_PIN00(v) \
+ (((v) << 0) & BM_PINCTRL_MUXSEL6_BANK3_PIN00)
+HW_REGISTER(HW_PINCTRL_MUXSEL7, REGS_PINCTRL_BASE, 0x00000170)
+#define HW_PINCTRL_MUXSEL7_ADDR (REGS_PINCTRL_BASE + 0x00000170)
+#define BP_PINCTRL_MUXSEL7_BANK3_PIN21 10
+#define BM_PINCTRL_MUXSEL7_BANK3_PIN21 0x00000C00
+#define BF_PINCTRL_MUXSEL7_BANK3_PIN21(v) \
+ (((v) << 10) & BM_PINCTRL_MUXSEL7_BANK3_PIN21)
+#define BP_PINCTRL_MUXSEL7_BANK3_PIN20 8
+#define BM_PINCTRL_MUXSEL7_BANK3_PIN20 0x00000300
+#define BF_PINCTRL_MUXSEL7_BANK3_PIN20(v) \
+ (((v) << 8) & BM_PINCTRL_MUXSEL7_BANK3_PIN20)
+#define BP_PINCTRL_MUXSEL7_BANK3_PIN19 6
+#define BM_PINCTRL_MUXSEL7_BANK3_PIN19 0x000000C0
+#define BF_PINCTRL_MUXSEL7_BANK3_PIN19(v) \
+ (((v) << 6) & BM_PINCTRL_MUXSEL7_BANK3_PIN19)
+#define BP_PINCTRL_MUXSEL7_BANK3_PIN18 4
+#define BM_PINCTRL_MUXSEL7_BANK3_PIN18 0x00000030
+#define BF_PINCTRL_MUXSEL7_BANK3_PIN18(v) \
+ (((v) << 4) & BM_PINCTRL_MUXSEL7_BANK3_PIN18)
+#define BP_PINCTRL_MUXSEL7_BANK3_PIN17 2
+#define BM_PINCTRL_MUXSEL7_BANK3_PIN17 0x0000000C
+#define BF_PINCTRL_MUXSEL7_BANK3_PIN17(v) \
+ (((v) << 2) & BM_PINCTRL_MUXSEL7_BANK3_PIN17)
+#define BP_PINCTRL_MUXSEL7_BANK3_PIN16 0
+#define BM_PINCTRL_MUXSEL7_BANK3_PIN16 0x00000003
+#define BF_PINCTRL_MUXSEL7_BANK3_PIN16(v) \
+ (((v) << 0) & BM_PINCTRL_MUXSEL7_BANK3_PIN16)
+HW_REGISTER(HW_PINCTRL_DRIVE0, REGS_PINCTRL_BASE, 0x00000200)
+#define HW_PINCTRL_DRIVE0_ADDR (REGS_PINCTRL_BASE + 0x00000200)
+#define BP_PINCTRL_DRIVE0_BANK0_PIN07_MA 28
+#define BM_PINCTRL_DRIVE0_BANK0_PIN07_MA 0x30000000
+#define BF_PINCTRL_DRIVE0_BANK0_PIN07_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE0_BANK0_PIN07_MA)
+#define BP_PINCTRL_DRIVE0_BANK0_PIN06_MA 24
+#define BM_PINCTRL_DRIVE0_BANK0_PIN06_MA 0x03000000
+#define BF_PINCTRL_DRIVE0_BANK0_PIN06_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE0_BANK0_PIN06_MA)
+#define BP_PINCTRL_DRIVE0_BANK0_PIN05_MA 20
+#define BM_PINCTRL_DRIVE0_BANK0_PIN05_MA 0x00300000
+#define BF_PINCTRL_DRIVE0_BANK0_PIN05_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE0_BANK0_PIN05_MA)
+#define BP_PINCTRL_DRIVE0_BANK0_PIN04_MA 16
+#define BM_PINCTRL_DRIVE0_BANK0_PIN04_MA 0x00030000
+#define BF_PINCTRL_DRIVE0_BANK0_PIN04_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE0_BANK0_PIN04_MA)
+#define BP_PINCTRL_DRIVE0_BANK0_PIN03_MA 12
+#define BM_PINCTRL_DRIVE0_BANK0_PIN03_MA 0x00003000
+#define BF_PINCTRL_DRIVE0_BANK0_PIN03_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE0_BANK0_PIN03_MA)
+#define BP_PINCTRL_DRIVE0_BANK0_PIN02_MA 8
+#define BM_PINCTRL_DRIVE0_BANK0_PIN02_MA 0x00000300
+#define BF_PINCTRL_DRIVE0_BANK0_PIN02_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE0_BANK0_PIN02_MA)
+#define BP_PINCTRL_DRIVE0_BANK0_PIN01_MA 4
+#define BM_PINCTRL_DRIVE0_BANK0_PIN01_MA 0x00000030
+#define BF_PINCTRL_DRIVE0_BANK0_PIN01_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE0_BANK0_PIN01_MA)
+#define BP_PINCTRL_DRIVE0_BANK0_PIN00_MA 0
+#define BM_PINCTRL_DRIVE0_BANK0_PIN00_MA 0x00000003
+#define BF_PINCTRL_DRIVE0_BANK0_PIN00_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE0_BANK0_PIN00_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE1, REGS_PINCTRL_BASE, 0x00000210)
+#define HW_PINCTRL_DRIVE1_ADDR (REGS_PINCTRL_BASE + 0x00000210)
+#define BP_PINCTRL_DRIVE1_BANK0_PIN15_MA 28
+#define BM_PINCTRL_DRIVE1_BANK0_PIN15_MA 0x30000000
+#define BF_PINCTRL_DRIVE1_BANK0_PIN15_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE1_BANK0_PIN15_MA)
+#define BP_PINCTRL_DRIVE1_BANK0_PIN14_MA 24
+#define BM_PINCTRL_DRIVE1_BANK0_PIN14_MA 0x03000000
+#define BF_PINCTRL_DRIVE1_BANK0_PIN14_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE1_BANK0_PIN14_MA)
+#define BP_PINCTRL_DRIVE1_BANK0_PIN13_MA 20
+#define BM_PINCTRL_DRIVE1_BANK0_PIN13_MA 0x00300000
+#define BF_PINCTRL_DRIVE1_BANK0_PIN13_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE1_BANK0_PIN13_MA)
+#define BP_PINCTRL_DRIVE1_BANK0_PIN12_MA 16
+#define BM_PINCTRL_DRIVE1_BANK0_PIN12_MA 0x00030000
+#define BF_PINCTRL_DRIVE1_BANK0_PIN12_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE1_BANK0_PIN12_MA)
+#define BP_PINCTRL_DRIVE1_BANK0_PIN11_MA 12
+#define BM_PINCTRL_DRIVE1_BANK0_PIN11_MA 0x00003000
+#define BF_PINCTRL_DRIVE1_BANK0_PIN11_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE1_BANK0_PIN11_MA)
+#define BP_PINCTRL_DRIVE1_BANK0_PIN10_MA 8
+#define BM_PINCTRL_DRIVE1_BANK0_PIN10_MA 0x00000300
+#define BF_PINCTRL_DRIVE1_BANK0_PIN10_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE1_BANK0_PIN10_MA)
+#define BP_PINCTRL_DRIVE1_BANK0_PIN09_MA 4
+#define BM_PINCTRL_DRIVE1_BANK0_PIN09_MA 0x00000030
+#define BF_PINCTRL_DRIVE1_BANK0_PIN09_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE1_BANK0_PIN09_MA)
+#define BP_PINCTRL_DRIVE1_BANK0_PIN08_MA 0
+#define BM_PINCTRL_DRIVE1_BANK0_PIN08_MA 0x00000003
+#define BF_PINCTRL_DRIVE1_BANK0_PIN08_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE1_BANK0_PIN08_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE2, REGS_PINCTRL_BASE, 0x00000220)
+#define HW_PINCTRL_DRIVE2_ADDR (REGS_PINCTRL_BASE + 0x00000220)
+#define BP_PINCTRL_DRIVE2_BANK0_PIN23_MA 28
+#define BM_PINCTRL_DRIVE2_BANK0_PIN23_MA 0x30000000
+#define BF_PINCTRL_DRIVE2_BANK0_PIN23_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE2_BANK0_PIN23_MA)
+#define BP_PINCTRL_DRIVE2_BANK0_PIN22_MA 24
+#define BM_PINCTRL_DRIVE2_BANK0_PIN22_MA 0x03000000
+#define BF_PINCTRL_DRIVE2_BANK0_PIN22_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE2_BANK0_PIN22_MA)
+#define BP_PINCTRL_DRIVE2_BANK0_PIN21_MA 20
+#define BM_PINCTRL_DRIVE2_BANK0_PIN21_MA 0x00300000
+#define BF_PINCTRL_DRIVE2_BANK0_PIN21_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE2_BANK0_PIN21_MA)
+#define BP_PINCTRL_DRIVE2_BANK0_PIN20_MA 16
+#define BM_PINCTRL_DRIVE2_BANK0_PIN20_MA 0x00030000
+#define BF_PINCTRL_DRIVE2_BANK0_PIN20_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE2_BANK0_PIN20_MA)
+#define BP_PINCTRL_DRIVE2_BANK0_PIN19_MA 12
+#define BM_PINCTRL_DRIVE2_BANK0_PIN19_MA 0x00003000
+#define BF_PINCTRL_DRIVE2_BANK0_PIN19_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE2_BANK0_PIN19_MA)
+#define BP_PINCTRL_DRIVE2_BANK0_PIN18_MA 8
+#define BM_PINCTRL_DRIVE2_BANK0_PIN18_MA 0x00000300
+#define BF_PINCTRL_DRIVE2_BANK0_PIN18_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE2_BANK0_PIN18_MA)
+#define BP_PINCTRL_DRIVE2_BANK0_PIN17_MA 4
+#define BM_PINCTRL_DRIVE2_BANK0_PIN17_MA 0x00000030
+#define BF_PINCTRL_DRIVE2_BANK0_PIN17_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE2_BANK0_PIN17_MA)
+#define BP_PINCTRL_DRIVE2_BANK0_PIN16_MA 0
+#define BM_PINCTRL_DRIVE2_BANK0_PIN16_MA 0x00000003
+#define BF_PINCTRL_DRIVE2_BANK0_PIN16_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE2_BANK0_PIN16_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE3, REGS_PINCTRL_BASE, 0x00000230)
+#define HW_PINCTRL_DRIVE3_ADDR (REGS_PINCTRL_BASE + 0x00000230)
+#define BP_PINCTRL_DRIVE3_BANK0_PIN31_MA 28
+#define BM_PINCTRL_DRIVE3_BANK0_PIN31_MA 0x30000000
+#define BF_PINCTRL_DRIVE3_BANK0_PIN31_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE3_BANK0_PIN31_MA)
+#define BP_PINCTRL_DRIVE3_BANK0_PIN30_MA 24
+#define BM_PINCTRL_DRIVE3_BANK0_PIN30_MA 0x03000000
+#define BF_PINCTRL_DRIVE3_BANK0_PIN30_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE3_BANK0_PIN30_MA)
+#define BP_PINCTRL_DRIVE3_BANK0_PIN29_MA 20
+#define BM_PINCTRL_DRIVE3_BANK0_PIN29_MA 0x00300000
+#define BF_PINCTRL_DRIVE3_BANK0_PIN29_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE3_BANK0_PIN29_MA)
+#define BP_PINCTRL_DRIVE3_BANK0_PIN28_MA 16
+#define BM_PINCTRL_DRIVE3_BANK0_PIN28_MA 0x00030000
+#define BF_PINCTRL_DRIVE3_BANK0_PIN28_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE3_BANK0_PIN28_MA)
+#define BP_PINCTRL_DRIVE3_BANK0_PIN27_MA 12
+#define BM_PINCTRL_DRIVE3_BANK0_PIN27_MA 0x00003000
+#define BF_PINCTRL_DRIVE3_BANK0_PIN27_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE3_BANK0_PIN27_MA)
+#define BP_PINCTRL_DRIVE3_BANK0_PIN26_MA 8
+#define BM_PINCTRL_DRIVE3_BANK0_PIN26_MA 0x00000300
+#define BF_PINCTRL_DRIVE3_BANK0_PIN26_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE3_BANK0_PIN26_MA)
+#define BP_PINCTRL_DRIVE3_BANK0_PIN25_MA 4
+#define BM_PINCTRL_DRIVE3_BANK0_PIN25_MA 0x00000030
+#define BF_PINCTRL_DRIVE3_BANK0_PIN25_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE3_BANK0_PIN25_MA)
+#define BP_PINCTRL_DRIVE3_BANK0_PIN24_MA 0
+#define BM_PINCTRL_DRIVE3_BANK0_PIN24_MA 0x00000003
+#define BF_PINCTRL_DRIVE3_BANK0_PIN24_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE3_BANK0_PIN24_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE4, REGS_PINCTRL_BASE, 0x00000240)
+#define HW_PINCTRL_DRIVE4_ADDR (REGS_PINCTRL_BASE + 0x00000240)
+#define BP_PINCTRL_DRIVE4_BANK1_PIN07_MA 28
+#define BM_PINCTRL_DRIVE4_BANK1_PIN07_MA 0x30000000
+#define BF_PINCTRL_DRIVE4_BANK1_PIN07_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE4_BANK1_PIN07_MA)
+#define BP_PINCTRL_DRIVE4_BANK1_PIN06_MA 24
+#define BM_PINCTRL_DRIVE4_BANK1_PIN06_MA 0x03000000
+#define BF_PINCTRL_DRIVE4_BANK1_PIN06_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE4_BANK1_PIN06_MA)
+#define BP_PINCTRL_DRIVE4_BANK1_PIN05_MA 20
+#define BM_PINCTRL_DRIVE4_BANK1_PIN05_MA 0x00300000
+#define BF_PINCTRL_DRIVE4_BANK1_PIN05_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE4_BANK1_PIN05_MA)
+#define BP_PINCTRL_DRIVE4_BANK1_PIN04_MA 16
+#define BM_PINCTRL_DRIVE4_BANK1_PIN04_MA 0x00030000
+#define BF_PINCTRL_DRIVE4_BANK1_PIN04_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE4_BANK1_PIN04_MA)
+#define BP_PINCTRL_DRIVE4_BANK1_PIN03_MA 12
+#define BM_PINCTRL_DRIVE4_BANK1_PIN03_MA 0x00003000
+#define BF_PINCTRL_DRIVE4_BANK1_PIN03_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE4_BANK1_PIN03_MA)
+#define BP_PINCTRL_DRIVE4_BANK1_PIN02_MA 8
+#define BM_PINCTRL_DRIVE4_BANK1_PIN02_MA 0x00000300
+#define BF_PINCTRL_DRIVE4_BANK1_PIN02_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE4_BANK1_PIN02_MA)
+#define BP_PINCTRL_DRIVE4_BANK1_PIN01_MA 4
+#define BM_PINCTRL_DRIVE4_BANK1_PIN01_MA 0x00000030
+#define BF_PINCTRL_DRIVE4_BANK1_PIN01_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE4_BANK1_PIN01_MA)
+#define BP_PINCTRL_DRIVE4_BANK1_PIN00_MA 0
+#define BM_PINCTRL_DRIVE4_BANK1_PIN00_MA 0x00000003
+#define BF_PINCTRL_DRIVE4_BANK1_PIN00_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE4_BANK1_PIN00_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE5, REGS_PINCTRL_BASE, 0x00000250)
+#define HW_PINCTRL_DRIVE5_ADDR (REGS_PINCTRL_BASE + 0x00000250)
+#define BP_PINCTRL_DRIVE5_BANK1_PIN15_MA 28
+#define BM_PINCTRL_DRIVE5_BANK1_PIN15_MA 0x30000000
+#define BF_PINCTRL_DRIVE5_BANK1_PIN15_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE5_BANK1_PIN15_MA)
+#define BP_PINCTRL_DRIVE5_BANK1_PIN14_MA 24
+#define BM_PINCTRL_DRIVE5_BANK1_PIN14_MA 0x03000000
+#define BF_PINCTRL_DRIVE5_BANK1_PIN14_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE5_BANK1_PIN14_MA)
+#define BP_PINCTRL_DRIVE5_BANK1_PIN13_MA 20
+#define BM_PINCTRL_DRIVE5_BANK1_PIN13_MA 0x00300000
+#define BF_PINCTRL_DRIVE5_BANK1_PIN13_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE5_BANK1_PIN13_MA)
+#define BP_PINCTRL_DRIVE5_BANK1_PIN12_MA 16
+#define BM_PINCTRL_DRIVE5_BANK1_PIN12_MA 0x00030000
+#define BF_PINCTRL_DRIVE5_BANK1_PIN12_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE5_BANK1_PIN12_MA)
+#define BP_PINCTRL_DRIVE5_BANK1_PIN11_MA 12
+#define BM_PINCTRL_DRIVE5_BANK1_PIN11_MA 0x00003000
+#define BF_PINCTRL_DRIVE5_BANK1_PIN11_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE5_BANK1_PIN11_MA)
+#define BP_PINCTRL_DRIVE5_BANK1_PIN10_MA 8
+#define BM_PINCTRL_DRIVE5_BANK1_PIN10_MA 0x00000300
+#define BF_PINCTRL_DRIVE5_BANK1_PIN10_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE5_BANK1_PIN10_MA)
+#define BP_PINCTRL_DRIVE5_BANK1_PIN09_MA 4
+#define BM_PINCTRL_DRIVE5_BANK1_PIN09_MA 0x00000030
+#define BF_PINCTRL_DRIVE5_BANK1_PIN09_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE5_BANK1_PIN09_MA)
+#define BP_PINCTRL_DRIVE5_BANK1_PIN08_MA 0
+#define BM_PINCTRL_DRIVE5_BANK1_PIN08_MA 0x00000003
+#define BF_PINCTRL_DRIVE5_BANK1_PIN08_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE5_BANK1_PIN08_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE6, REGS_PINCTRL_BASE, 0x00000260)
+#define HW_PINCTRL_DRIVE6_ADDR (REGS_PINCTRL_BASE + 0x00000260)
+#define BP_PINCTRL_DRIVE6_BANK1_PIN23_MA 28
+#define BM_PINCTRL_DRIVE6_BANK1_PIN23_MA 0x30000000
+#define BF_PINCTRL_DRIVE6_BANK1_PIN23_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE6_BANK1_PIN23_MA)
+#define BP_PINCTRL_DRIVE6_BANK1_PIN22_MA 24
+#define BM_PINCTRL_DRIVE6_BANK1_PIN22_MA 0x03000000
+#define BF_PINCTRL_DRIVE6_BANK1_PIN22_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE6_BANK1_PIN22_MA)
+#define BP_PINCTRL_DRIVE6_BANK1_PIN21_MA 20
+#define BM_PINCTRL_DRIVE6_BANK1_PIN21_MA 0x00300000
+#define BF_PINCTRL_DRIVE6_BANK1_PIN21_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE6_BANK1_PIN21_MA)
+#define BP_PINCTRL_DRIVE6_BANK1_PIN20_MA 16
+#define BM_PINCTRL_DRIVE6_BANK1_PIN20_MA 0x00030000
+#define BF_PINCTRL_DRIVE6_BANK1_PIN20_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE6_BANK1_PIN20_MA)
+#define BP_PINCTRL_DRIVE6_BANK1_PIN19_MA 12
+#define BM_PINCTRL_DRIVE6_BANK1_PIN19_MA 0x00003000
+#define BF_PINCTRL_DRIVE6_BANK1_PIN19_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE6_BANK1_PIN19_MA)
+#define BP_PINCTRL_DRIVE6_BANK1_PIN18_MA 8
+#define BM_PINCTRL_DRIVE6_BANK1_PIN18_MA 0x00000300
+#define BF_PINCTRL_DRIVE6_BANK1_PIN18_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE6_BANK1_PIN18_MA)
+#define BP_PINCTRL_DRIVE6_BANK1_PIN17_MA 4
+#define BM_PINCTRL_DRIVE6_BANK1_PIN17_MA 0x00000030
+#define BF_PINCTRL_DRIVE6_BANK1_PIN17_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE6_BANK1_PIN17_MA)
+#define BP_PINCTRL_DRIVE6_BANK1_PIN16_MA 0
+#define BM_PINCTRL_DRIVE6_BANK1_PIN16_MA 0x00000003
+#define BF_PINCTRL_DRIVE6_BANK1_PIN16_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE6_BANK1_PIN16_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE7, REGS_PINCTRL_BASE, 0x00000270)
+#define HW_PINCTRL_DRIVE7_ADDR (REGS_PINCTRL_BASE + 0x00000270)
+#define BP_PINCTRL_DRIVE7_BANK1_PIN30_MA 24
+#define BM_PINCTRL_DRIVE7_BANK1_PIN30_MA 0x03000000
+#define BF_PINCTRL_DRIVE7_BANK1_PIN30_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE7_BANK1_PIN30_MA)
+#define BP_PINCTRL_DRIVE7_BANK1_PIN29_MA 20
+#define BM_PINCTRL_DRIVE7_BANK1_PIN29_MA 0x00300000
+#define BF_PINCTRL_DRIVE7_BANK1_PIN29_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE7_BANK1_PIN29_MA)
+#define BP_PINCTRL_DRIVE7_BANK1_PIN28_MA 16
+#define BM_PINCTRL_DRIVE7_BANK1_PIN28_MA 0x00030000
+#define BF_PINCTRL_DRIVE7_BANK1_PIN28_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE7_BANK1_PIN28_MA)
+#define BP_PINCTRL_DRIVE7_BANK1_PIN27_MA 12
+#define BM_PINCTRL_DRIVE7_BANK1_PIN27_MA 0x00003000
+#define BF_PINCTRL_DRIVE7_BANK1_PIN27_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE7_BANK1_PIN27_MA)
+#define BP_PINCTRL_DRIVE7_BANK1_PIN26_MA 8
+#define BM_PINCTRL_DRIVE7_BANK1_PIN26_MA 0x00000300
+#define BF_PINCTRL_DRIVE7_BANK1_PIN26_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE7_BANK1_PIN26_MA)
+#define BP_PINCTRL_DRIVE7_BANK1_PIN25_MA 4
+#define BM_PINCTRL_DRIVE7_BANK1_PIN25_MA 0x00000030
+#define BF_PINCTRL_DRIVE7_BANK1_PIN25_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE7_BANK1_PIN25_MA)
+#define BP_PINCTRL_DRIVE7_BANK1_PIN24_MA 0
+#define BM_PINCTRL_DRIVE7_BANK1_PIN24_MA 0x00000003
+#define BF_PINCTRL_DRIVE7_BANK1_PIN24_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE7_BANK1_PIN24_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE8, REGS_PINCTRL_BASE, 0x00000280)
+#define HW_PINCTRL_DRIVE8_ADDR (REGS_PINCTRL_BASE + 0x00000280)
+#define BP_PINCTRL_DRIVE8_BANK2_PIN07_MA 28
+#define BM_PINCTRL_DRIVE8_BANK2_PIN07_MA 0x30000000
+#define BF_PINCTRL_DRIVE8_BANK2_PIN07_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE8_BANK2_PIN07_MA)
+#define BP_PINCTRL_DRIVE8_BANK2_PIN06_MA 24
+#define BM_PINCTRL_DRIVE8_BANK2_PIN06_MA 0x03000000
+#define BF_PINCTRL_DRIVE8_BANK2_PIN06_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE8_BANK2_PIN06_MA)
+#define BP_PINCTRL_DRIVE8_BANK2_PIN05_MA 20
+#define BM_PINCTRL_DRIVE8_BANK2_PIN05_MA 0x00300000
+#define BF_PINCTRL_DRIVE8_BANK2_PIN05_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE8_BANK2_PIN05_MA)
+#define BP_PINCTRL_DRIVE8_BANK2_PIN04_MA 16
+#define BM_PINCTRL_DRIVE8_BANK2_PIN04_MA 0x00030000
+#define BF_PINCTRL_DRIVE8_BANK2_PIN04_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE8_BANK2_PIN04_MA)
+#define BP_PINCTRL_DRIVE8_BANK2_PIN03_MA 12
+#define BM_PINCTRL_DRIVE8_BANK2_PIN03_MA 0x00003000
+#define BF_PINCTRL_DRIVE8_BANK2_PIN03_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE8_BANK2_PIN03_MA)
+#define BP_PINCTRL_DRIVE8_BANK2_PIN02_MA 8
+#define BM_PINCTRL_DRIVE8_BANK2_PIN02_MA 0x00000300
+#define BF_PINCTRL_DRIVE8_BANK2_PIN02_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE8_BANK2_PIN02_MA)
+#define BP_PINCTRL_DRIVE8_BANK2_PIN01_MA 4
+#define BM_PINCTRL_DRIVE8_BANK2_PIN01_MA 0x00000030
+#define BF_PINCTRL_DRIVE8_BANK2_PIN01_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE8_BANK2_PIN01_MA)
+#define BP_PINCTRL_DRIVE8_BANK2_PIN00_MA 0
+#define BM_PINCTRL_DRIVE8_BANK2_PIN00_MA 0x00000003
+#define BF_PINCTRL_DRIVE8_BANK2_PIN00_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE8_BANK2_PIN00_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE9, REGS_PINCTRL_BASE, 0x00000290)
+#define HW_PINCTRL_DRIVE9_ADDR (REGS_PINCTRL_BASE + 0x00000290)
+#define BM_PINCTRL_DRIVE9_BANK2_PIN15_V 0x40000000
+#define BP_PINCTRL_DRIVE9_BANK2_PIN15_MA 28
+#define BM_PINCTRL_DRIVE9_BANK2_PIN15_MA 0x30000000
+#define BF_PINCTRL_DRIVE9_BANK2_PIN15_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE9_BANK2_PIN15_MA)
+#define BM_PINCTRL_DRIVE9_BANK2_PIN14_V 0x04000000
+#define BP_PINCTRL_DRIVE9_BANK2_PIN14_MA 24
+#define BM_PINCTRL_DRIVE9_BANK2_PIN14_MA 0x03000000
+#define BF_PINCTRL_DRIVE9_BANK2_PIN14_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE9_BANK2_PIN14_MA)
+#define BM_PINCTRL_DRIVE9_BANK2_PIN13_V 0x00400000
+#define BP_PINCTRL_DRIVE9_BANK2_PIN13_MA 20
+#define BM_PINCTRL_DRIVE9_BANK2_PIN13_MA 0x00300000
+#define BF_PINCTRL_DRIVE9_BANK2_PIN13_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE9_BANK2_PIN13_MA)
+#define BM_PINCTRL_DRIVE9_BANK2_PIN12_V 0x00040000
+#define BP_PINCTRL_DRIVE9_BANK2_PIN12_MA 16
+#define BM_PINCTRL_DRIVE9_BANK2_PIN12_MA 0x00030000
+#define BF_PINCTRL_DRIVE9_BANK2_PIN12_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE9_BANK2_PIN12_MA)
+#define BM_PINCTRL_DRIVE9_BANK2_PIN11_V 0x00004000
+#define BP_PINCTRL_DRIVE9_BANK2_PIN11_MA 12
+#define BM_PINCTRL_DRIVE9_BANK2_PIN11_MA 0x00003000
+#define BF_PINCTRL_DRIVE9_BANK2_PIN11_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE9_BANK2_PIN11_MA)
+#define BM_PINCTRL_DRIVE9_BANK2_PIN10_V 0x00000400
+#define BP_PINCTRL_DRIVE9_BANK2_PIN10_MA 8
+#define BM_PINCTRL_DRIVE9_BANK2_PIN10_MA 0x00000300
+#define BF_PINCTRL_DRIVE9_BANK2_PIN10_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE9_BANK2_PIN10_MA)
+#define BM_PINCTRL_DRIVE9_BANK2_PIN09_V 0x00000040
+#define BP_PINCTRL_DRIVE9_BANK2_PIN09_MA 4
+#define BM_PINCTRL_DRIVE9_BANK2_PIN09_MA 0x00000030
+#define BF_PINCTRL_DRIVE9_BANK2_PIN09_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE9_BANK2_PIN09_MA)
+#define BP_PINCTRL_DRIVE9_BANK2_PIN08_MA 0
+#define BM_PINCTRL_DRIVE9_BANK2_PIN08_MA 0x00000003
+#define BF_PINCTRL_DRIVE9_BANK2_PIN08_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE9_BANK2_PIN08_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE10, REGS_PINCTRL_BASE, 0x000002a0)
+#define HW_PINCTRL_DRIVE10_ADDR (REGS_PINCTRL_BASE + 0x000002a0)
+#define BM_PINCTRL_DRIVE10_BANK2_PIN23_V 0x40000000
+#define BP_PINCTRL_DRIVE10_BANK2_PIN23_MA 28
+#define BM_PINCTRL_DRIVE10_BANK2_PIN23_MA 0x30000000
+#define BF_PINCTRL_DRIVE10_BANK2_PIN23_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE10_BANK2_PIN23_MA)
+#define BM_PINCTRL_DRIVE10_BANK2_PIN22_V 0x04000000
+#define BP_PINCTRL_DRIVE10_BANK2_PIN22_MA 24
+#define BM_PINCTRL_DRIVE10_BANK2_PIN22_MA 0x03000000
+#define BF_PINCTRL_DRIVE10_BANK2_PIN22_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE10_BANK2_PIN22_MA)
+#define BM_PINCTRL_DRIVE10_BANK2_PIN21_V 0x00400000
+#define BP_PINCTRL_DRIVE10_BANK2_PIN21_MA 20
+#define BM_PINCTRL_DRIVE10_BANK2_PIN21_MA 0x00300000
+#define BF_PINCTRL_DRIVE10_BANK2_PIN21_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE10_BANK2_PIN21_MA)
+#define BM_PINCTRL_DRIVE10_BANK2_PIN20_V 0x00040000
+#define BP_PINCTRL_DRIVE10_BANK2_PIN20_MA 16
+#define BM_PINCTRL_DRIVE10_BANK2_PIN20_MA 0x00030000
+#define BF_PINCTRL_DRIVE10_BANK2_PIN20_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE10_BANK2_PIN20_MA)
+#define BM_PINCTRL_DRIVE10_BANK2_PIN19_V 0x00004000
+#define BP_PINCTRL_DRIVE10_BANK2_PIN19_MA 12
+#define BM_PINCTRL_DRIVE10_BANK2_PIN19_MA 0x00003000
+#define BF_PINCTRL_DRIVE10_BANK2_PIN19_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE10_BANK2_PIN19_MA)
+#define BM_PINCTRL_DRIVE10_BANK2_PIN18_V 0x00000400
+#define BP_PINCTRL_DRIVE10_BANK2_PIN18_MA 8
+#define BM_PINCTRL_DRIVE10_BANK2_PIN18_MA 0x00000300
+#define BF_PINCTRL_DRIVE10_BANK2_PIN18_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE10_BANK2_PIN18_MA)
+#define BM_PINCTRL_DRIVE10_BANK2_PIN17_V 0x00000040
+#define BP_PINCTRL_DRIVE10_BANK2_PIN17_MA 4
+#define BM_PINCTRL_DRIVE10_BANK2_PIN17_MA 0x00000030
+#define BF_PINCTRL_DRIVE10_BANK2_PIN17_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE10_BANK2_PIN17_MA)
+#define BM_PINCTRL_DRIVE10_BANK2_PIN16_V 0x00000004
+#define BP_PINCTRL_DRIVE10_BANK2_PIN16_MA 0
+#define BM_PINCTRL_DRIVE10_BANK2_PIN16_MA 0x00000003
+#define BF_PINCTRL_DRIVE10_BANK2_PIN16_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE10_BANK2_PIN16_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE11, REGS_PINCTRL_BASE, 0x000002b0)
+#define HW_PINCTRL_DRIVE11_ADDR (REGS_PINCTRL_BASE + 0x000002b0)
+#define BM_PINCTRL_DRIVE11_BANK2_PIN31_V 0x40000000
+#define BP_PINCTRL_DRIVE11_BANK2_PIN31_MA 28
+#define BM_PINCTRL_DRIVE11_BANK2_PIN31_MA 0x30000000
+#define BF_PINCTRL_DRIVE11_BANK2_PIN31_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE11_BANK2_PIN31_MA)
+#define BM_PINCTRL_DRIVE11_BANK2_PIN30_V 0x04000000
+#define BP_PINCTRL_DRIVE11_BANK2_PIN30_MA 24
+#define BM_PINCTRL_DRIVE11_BANK2_PIN30_MA 0x03000000
+#define BF_PINCTRL_DRIVE11_BANK2_PIN30_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE11_BANK2_PIN30_MA)
+#define BM_PINCTRL_DRIVE11_BANK2_PIN29_V 0x00400000
+#define BP_PINCTRL_DRIVE11_BANK2_PIN29_MA 20
+#define BM_PINCTRL_DRIVE11_BANK2_PIN29_MA 0x00300000
+#define BF_PINCTRL_DRIVE11_BANK2_PIN29_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE11_BANK2_PIN29_MA)
+#define BP_PINCTRL_DRIVE11_BANK2_PIN28_MA 16
+#define BM_PINCTRL_DRIVE11_BANK2_PIN28_MA 0x00030000
+#define BF_PINCTRL_DRIVE11_BANK2_PIN28_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE11_BANK2_PIN28_MA)
+#define BP_PINCTRL_DRIVE11_BANK2_PIN27_MA 12
+#define BM_PINCTRL_DRIVE11_BANK2_PIN27_MA 0x00003000
+#define BF_PINCTRL_DRIVE11_BANK2_PIN27_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE11_BANK2_PIN27_MA)
+#define BM_PINCTRL_DRIVE11_BANK2_PIN26_V 0x00000400
+#define BP_PINCTRL_DRIVE11_BANK2_PIN26_MA 8
+#define BM_PINCTRL_DRIVE11_BANK2_PIN26_MA 0x00000300
+#define BF_PINCTRL_DRIVE11_BANK2_PIN26_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE11_BANK2_PIN26_MA)
+#define BM_PINCTRL_DRIVE11_BANK2_PIN25_V 0x00000040
+#define BP_PINCTRL_DRIVE11_BANK2_PIN25_MA 4
+#define BM_PINCTRL_DRIVE11_BANK2_PIN25_MA 0x00000030
+#define BF_PINCTRL_DRIVE11_BANK2_PIN25_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE11_BANK2_PIN25_MA)
+#define BM_PINCTRL_DRIVE11_BANK2_PIN24_V 0x00000004
+#define BP_PINCTRL_DRIVE11_BANK2_PIN24_MA 0
+#define BM_PINCTRL_DRIVE11_BANK2_PIN24_MA 0x00000003
+#define BF_PINCTRL_DRIVE11_BANK2_PIN24_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE11_BANK2_PIN24_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE12, REGS_PINCTRL_BASE, 0x000002c0)
+#define HW_PINCTRL_DRIVE12_ADDR (REGS_PINCTRL_BASE + 0x000002c0)
+#define BM_PINCTRL_DRIVE12_BANK3_PIN07_V 0x40000000
+#define BP_PINCTRL_DRIVE12_BANK3_PIN07_MA 28
+#define BM_PINCTRL_DRIVE12_BANK3_PIN07_MA 0x30000000
+#define BF_PINCTRL_DRIVE12_BANK3_PIN07_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE12_BANK3_PIN07_MA)
+#define BM_PINCTRL_DRIVE12_BANK3_PIN06_V 0x04000000
+#define BP_PINCTRL_DRIVE12_BANK3_PIN06_MA 24
+#define BM_PINCTRL_DRIVE12_BANK3_PIN06_MA 0x03000000
+#define BF_PINCTRL_DRIVE12_BANK3_PIN06_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE12_BANK3_PIN06_MA)
+#define BM_PINCTRL_DRIVE12_BANK3_PIN05_V 0x00400000
+#define BP_PINCTRL_DRIVE12_BANK3_PIN05_MA 20
+#define BM_PINCTRL_DRIVE12_BANK3_PIN05_MA 0x00300000
+#define BF_PINCTRL_DRIVE12_BANK3_PIN05_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE12_BANK3_PIN05_MA)
+#define BM_PINCTRL_DRIVE12_BANK3_PIN04_V 0x00040000
+#define BP_PINCTRL_DRIVE12_BANK3_PIN04_MA 16
+#define BM_PINCTRL_DRIVE12_BANK3_PIN04_MA 0x00030000
+#define BF_PINCTRL_DRIVE12_BANK3_PIN04_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE12_BANK3_PIN04_MA)
+#define BM_PINCTRL_DRIVE12_BANK3_PIN03_V 0x00004000
+#define BP_PINCTRL_DRIVE12_BANK3_PIN03_MA 12
+#define BM_PINCTRL_DRIVE12_BANK3_PIN03_MA 0x00003000
+#define BF_PINCTRL_DRIVE12_BANK3_PIN03_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE12_BANK3_PIN03_MA)
+#define BM_PINCTRL_DRIVE12_BANK3_PIN02_V 0x00000400
+#define BP_PINCTRL_DRIVE12_BANK3_PIN02_MA 8
+#define BM_PINCTRL_DRIVE12_BANK3_PIN02_MA 0x00000300
+#define BF_PINCTRL_DRIVE12_BANK3_PIN02_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE12_BANK3_PIN02_MA)
+#define BM_PINCTRL_DRIVE12_BANK3_PIN01_V 0x00000040
+#define BP_PINCTRL_DRIVE12_BANK3_PIN01_MA 4
+#define BM_PINCTRL_DRIVE12_BANK3_PIN01_MA 0x00000030
+#define BF_PINCTRL_DRIVE12_BANK3_PIN01_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE12_BANK3_PIN01_MA)
+#define BM_PINCTRL_DRIVE12_BANK3_PIN00_V 0x00000004
+#define BP_PINCTRL_DRIVE12_BANK3_PIN00_MA 0
+#define BM_PINCTRL_DRIVE12_BANK3_PIN00_MA 0x00000003
+#define BF_PINCTRL_DRIVE12_BANK3_PIN00_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE12_BANK3_PIN00_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE13, REGS_PINCTRL_BASE, 0x000002d0)
+#define HW_PINCTRL_DRIVE13_ADDR (REGS_PINCTRL_BASE + 0x000002d0)
+#define BM_PINCTRL_DRIVE13_BANK3_PIN15_V 0x40000000
+#define BP_PINCTRL_DRIVE13_BANK3_PIN15_MA 28
+#define BM_PINCTRL_DRIVE13_BANK3_PIN15_MA 0x30000000
+#define BF_PINCTRL_DRIVE13_BANK3_PIN15_MA(v) \
+ (((v) << 28) & BM_PINCTRL_DRIVE13_BANK3_PIN15_MA)
+#define BM_PINCTRL_DRIVE13_BANK3_PIN14_V 0x04000000
+#define BP_PINCTRL_DRIVE13_BANK3_PIN14_MA 24
+#define BM_PINCTRL_DRIVE13_BANK3_PIN14_MA 0x03000000
+#define BF_PINCTRL_DRIVE13_BANK3_PIN14_MA(v) \
+ (((v) << 24) & BM_PINCTRL_DRIVE13_BANK3_PIN14_MA)
+#define BM_PINCTRL_DRIVE13_BANK3_PIN13_V 0x00400000
+#define BP_PINCTRL_DRIVE13_BANK3_PIN13_MA 20
+#define BM_PINCTRL_DRIVE13_BANK3_PIN13_MA 0x00300000
+#define BF_PINCTRL_DRIVE13_BANK3_PIN13_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE13_BANK3_PIN13_MA)
+#define BM_PINCTRL_DRIVE13_BANK3_PIN12_V 0x00040000
+#define BP_PINCTRL_DRIVE13_BANK3_PIN12_MA 16
+#define BM_PINCTRL_DRIVE13_BANK3_PIN12_MA 0x00030000
+#define BF_PINCTRL_DRIVE13_BANK3_PIN12_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE13_BANK3_PIN12_MA)
+#define BM_PINCTRL_DRIVE13_BANK3_PIN11_V 0x00004000
+#define BP_PINCTRL_DRIVE13_BANK3_PIN11_MA 12
+#define BM_PINCTRL_DRIVE13_BANK3_PIN11_MA 0x00003000
+#define BF_PINCTRL_DRIVE13_BANK3_PIN11_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE13_BANK3_PIN11_MA)
+#define BM_PINCTRL_DRIVE13_BANK3_PIN10_V 0x00000400
+#define BP_PINCTRL_DRIVE13_BANK3_PIN10_MA 8
+#define BM_PINCTRL_DRIVE13_BANK3_PIN10_MA 0x00000300
+#define BF_PINCTRL_DRIVE13_BANK3_PIN10_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE13_BANK3_PIN10_MA)
+#define BM_PINCTRL_DRIVE13_BANK3_PIN09_V 0x00000040
+#define BP_PINCTRL_DRIVE13_BANK3_PIN09_MA 4
+#define BM_PINCTRL_DRIVE13_BANK3_PIN09_MA 0x00000030
+#define BF_PINCTRL_DRIVE13_BANK3_PIN09_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE13_BANK3_PIN09_MA)
+#define BM_PINCTRL_DRIVE13_BANK3_PIN08_V 0x00000004
+#define BP_PINCTRL_DRIVE13_BANK3_PIN08_MA 0
+#define BM_PINCTRL_DRIVE13_BANK3_PIN08_MA 0x00000003
+#define BF_PINCTRL_DRIVE13_BANK3_PIN08_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE13_BANK3_PIN08_MA)
+HW_REGISTER(HW_PINCTRL_DRIVE14, REGS_PINCTRL_BASE, 0x000002e0)
+#define HW_PINCTRL_DRIVE14_ADDR (REGS_PINCTRL_BASE + 0x000002e0)
+#define BM_PINCTRL_DRIVE14_BANK3_PIN21_V 0x00400000
+#define BP_PINCTRL_DRIVE14_BANK3_PIN21_MA 20
+#define BM_PINCTRL_DRIVE14_BANK3_PIN21_MA 0x00300000
+#define BF_PINCTRL_DRIVE14_BANK3_PIN21_MA(v) \
+ (((v) << 20) & BM_PINCTRL_DRIVE14_BANK3_PIN21_MA)
+#define BM_PINCTRL_DRIVE14_BANK3_PIN20_V 0x00040000
+#define BP_PINCTRL_DRIVE14_BANK3_PIN20_MA 16
+#define BM_PINCTRL_DRIVE14_BANK3_PIN20_MA 0x00030000
+#define BF_PINCTRL_DRIVE14_BANK3_PIN20_MA(v) \
+ (((v) << 16) & BM_PINCTRL_DRIVE14_BANK3_PIN20_MA)
+#define BM_PINCTRL_DRIVE14_BANK3_PIN19_V 0x00004000
+#define BP_PINCTRL_DRIVE14_BANK3_PIN19_MA 12
+#define BM_PINCTRL_DRIVE14_BANK3_PIN19_MA 0x00003000
+#define BF_PINCTRL_DRIVE14_BANK3_PIN19_MA(v) \
+ (((v) << 12) & BM_PINCTRL_DRIVE14_BANK3_PIN19_MA)
+#define BM_PINCTRL_DRIVE14_BANK3_PIN18_V 0x00000400
+#define BP_PINCTRL_DRIVE14_BANK3_PIN18_MA 8
+#define BM_PINCTRL_DRIVE14_BANK3_PIN18_MA 0x00000300
+#define BF_PINCTRL_DRIVE14_BANK3_PIN18_MA(v) \
+ (((v) << 8) & BM_PINCTRL_DRIVE14_BANK3_PIN18_MA)
+#define BM_PINCTRL_DRIVE14_BANK3_PIN17_V 0x00000040
+#define BP_PINCTRL_DRIVE14_BANK3_PIN17_MA 4
+#define BM_PINCTRL_DRIVE14_BANK3_PIN17_MA 0x00000030
+#define BF_PINCTRL_DRIVE14_BANK3_PIN17_MA(v) \
+ (((v) << 4) & BM_PINCTRL_DRIVE14_BANK3_PIN17_MA)
+#define BM_PINCTRL_DRIVE14_BANK3_PIN16_V 0x00000004
+#define BP_PINCTRL_DRIVE14_BANK3_PIN16_MA 0
+#define BM_PINCTRL_DRIVE14_BANK3_PIN16_MA 0x00000003
+#define BF_PINCTRL_DRIVE14_BANK3_PIN16_MA(v) \
+ (((v) << 0) & BM_PINCTRL_DRIVE14_BANK3_PIN16_MA)
+HW_REGISTER(HW_PINCTRL_PULL0, REGS_PINCTRL_BASE, 0x00000400)
+#define HW_PINCTRL_PULL0_ADDR (REGS_PINCTRL_BASE + 0x00000400)
+#define BM_PINCTRL_PULL0_BANK0_PIN31 0x80000000
+#define BM_PINCTRL_PULL0_BANK0_PIN30 0x40000000
+#define BM_PINCTRL_PULL0_BANK0_PIN29 0x20000000
+#define BM_PINCTRL_PULL0_BANK0_PIN28 0x10000000
+#define BM_PINCTRL_PULL0_BANK0_PIN27 0x08000000
+#define BM_PINCTRL_PULL0_BANK0_PIN26 0x04000000
+#define BM_PINCTRL_PULL0_BANK0_PIN22 0x00400000
+#define BM_PINCTRL_PULL0_BANK0_PIN21 0x00200000
+#define BM_PINCTRL_PULL0_BANK0_PIN20 0x00100000
+#define BM_PINCTRL_PULL0_BANK0_PIN19 0x00080000
+#define BM_PINCTRL_PULL0_BANK0_PIN18 0x00040000
+#define BM_PINCTRL_PULL0_BANK0_PIN15 0x00008000
+#define BM_PINCTRL_PULL0_BANK0_PIN11 0x00000800
+#define BM_PINCTRL_PULL0_BANK0_PIN10 0x00000400
+#define BM_PINCTRL_PULL0_BANK0_PIN09 0x00000200
+#define BM_PINCTRL_PULL0_BANK0_PIN08 0x00000100
+#define BM_PINCTRL_PULL0_BANK0_PIN07 0x00000080
+#define BM_PINCTRL_PULL0_BANK0_PIN06 0x00000040
+#define BM_PINCTRL_PULL0_BANK0_PIN05 0x00000020
+#define BM_PINCTRL_PULL0_BANK0_PIN04 0x00000010
+#define BM_PINCTRL_PULL0_BANK0_PIN03 0x00000008
+#define BM_PINCTRL_PULL0_BANK0_PIN02 0x00000004
+#define BM_PINCTRL_PULL0_BANK0_PIN01 0x00000002
+#define BM_PINCTRL_PULL0_BANK0_PIN00 0x00000001
+HW_REGISTER(HW_PINCTRL_PULL1, REGS_PINCTRL_BASE, 0x00000410)
+#define HW_PINCTRL_PULL1_ADDR (REGS_PINCTRL_BASE + 0x00000410)
+#define BM_PINCTRL_PULL1_BANK1_PIN28 0x10000000
+#define BM_PINCTRL_PULL1_BANK1_PIN22 0x00400000
+#define BM_PINCTRL_PULL1_BANK1_PIN18 0x00040000
+HW_REGISTER(HW_PINCTRL_PULL2, REGS_PINCTRL_BASE, 0x00000420)
+#define HW_PINCTRL_PULL2_ADDR (REGS_PINCTRL_BASE + 0x00000420)
+#define BM_PINCTRL_PULL2_BANK2_PIN28 0x10000000
+#define BM_PINCTRL_PULL2_BANK2_PIN27 0x08000000
+#define BM_PINCTRL_PULL2_BANK2_PIN08 0x00000100
+#define BM_PINCTRL_PULL2_BANK2_PIN05 0x00000020
+#define BM_PINCTRL_PULL2_BANK2_PIN04 0x00000010
+#define BM_PINCTRL_PULL2_BANK2_PIN03 0x00000008
+#define BM_PINCTRL_PULL2_BANK2_PIN02 0x00000004
+#define BM_PINCTRL_PULL2_BANK2_PIN01 0x00000002
+#define BM_PINCTRL_PULL2_BANK2_PIN00 0x00000001
+HW_REGISTER(HW_PINCTRL_PULL3, REGS_PINCTRL_BASE, 0x00000430)
+#define HW_PINCTRL_PULL3_ADDR (REGS_PINCTRL_BASE + 0x00000430)
+#define BM_PINCTRL_PULL3_BANK3_PIN17 0x00020000
+#define BM_PINCTRL_PULL3_BANK3_PIN16 0x00010000
+#define BM_PINCTRL_PULL3_BANK3_PIN15 0x00008000
+#define BM_PINCTRL_PULL3_BANK3_PIN14 0x00004000
+#define BM_PINCTRL_PULL3_BANK3_PIN13 0x00002000
+#define BM_PINCTRL_PULL3_BANK3_PIN12 0x00001000
+#define BM_PINCTRL_PULL3_BANK3_PIN11 0x00000800
+#define BM_PINCTRL_PULL3_BANK3_PIN10 0x00000400
+#define BM_PINCTRL_PULL3_BANK3_PIN09 0x00000200
+#define BM_PINCTRL_PULL3_BANK3_PIN08 0x00000100
+#define BM_PINCTRL_PULL3_BANK3_PIN07 0x00000080
+#define BM_PINCTRL_PULL3_BANK3_PIN06 0x00000040
+#define BM_PINCTRL_PULL3_BANK3_PIN05 0x00000020
+#define BM_PINCTRL_PULL3_BANK3_PIN04 0x00000010
+#define BM_PINCTRL_PULL3_BANK3_PIN03 0x00000008
+#define BM_PINCTRL_PULL3_BANK3_PIN02 0x00000004
+#define BM_PINCTRL_PULL3_BANK3_PIN01 0x00000002
+#define BM_PINCTRL_PULL3_BANK3_PIN00 0x00000001
+HW_REGISTER(HW_PINCTRL_DOUT0, REGS_PINCTRL_BASE, 0x00000500)
+#define HW_PINCTRL_DOUT0_ADDR (REGS_PINCTRL_BASE + 0x00000500)
+#define BP_PINCTRL_DOUT0_DOUT 0
+#define BM_PINCTRL_DOUT0_DOUT 0xFFFFFFFF
+#define BF_PINCTRL_DOUT0_DOUT(v) (v)
+HW_REGISTER(HW_PINCTRL_DOUT1, REGS_PINCTRL_BASE, 0x00000510)
+#define HW_PINCTRL_DOUT1_ADDR (REGS_PINCTRL_BASE + 0x00000510)
+#define BP_PINCTRL_DOUT1_DOUT 0
+#define BM_PINCTRL_DOUT1_DOUT 0x7FFFFFFF
+#define BF_PINCTRL_DOUT1_DOUT(v) \
+ (((v) << 0) & BM_PINCTRL_DOUT1_DOUT)
+HW_REGISTER(HW_PINCTRL_DOUT2, REGS_PINCTRL_BASE, 0x00000520)
+#define HW_PINCTRL_DOUT2_ADDR (REGS_PINCTRL_BASE + 0x00000520)
+#define BP_PINCTRL_DOUT2_DOUT 0
+#define BM_PINCTRL_DOUT2_DOUT 0xFFFFFFFF
+#define BF_PINCTRL_DOUT2_DOUT(v) (v)
+HW_REGISTER(HW_PINCTRL_DIN0, REGS_PINCTRL_BASE, 0x00000600)
+#define HW_PINCTRL_DIN0_ADDR (REGS_PINCTRL_BASE + 0x00000600)
+#define BP_PINCTRL_DIN0_DIN 0
+#define BM_PINCTRL_DIN0_DIN 0xFFFFFFFF
+#define BF_PINCTRL_DIN0_DIN(v) (v)
+HW_REGISTER(HW_PINCTRL_DIN1, REGS_PINCTRL_BASE, 0x00000610)
+#define HW_PINCTRL_DIN1_ADDR (REGS_PINCTRL_BASE + 0x00000610)
+#define BP_PINCTRL_DIN1_DIN 0
+#define BM_PINCTRL_DIN1_DIN 0x7FFFFFFF
+#define BF_PINCTRL_DIN1_DIN(v) \
+ (((v) << 0) & BM_PINCTRL_DIN1_DIN)
+HW_REGISTER(HW_PINCTRL_DIN2, REGS_PINCTRL_BASE, 0x00000620)
+#define HW_PINCTRL_DIN2_ADDR (REGS_PINCTRL_BASE + 0x00000620)
+#define BP_PINCTRL_DIN2_DIN 0
+#define BM_PINCTRL_DIN2_DIN 0xFFFFFFFF
+#define BF_PINCTRL_DIN2_DIN(v) (v)
+HW_REGISTER(HW_PINCTRL_DOE0, REGS_PINCTRL_BASE, 0x00000700)
+#define HW_PINCTRL_DOE0_ADDR (REGS_PINCTRL_BASE + 0x00000700)
+#define BP_PINCTRL_DOE0_DOE 0
+#define BM_PINCTRL_DOE0_DOE 0xFFFFFFFF
+#define BF_PINCTRL_DOE0_DOE(v) (v)
+HW_REGISTER(HW_PINCTRL_DOE1, REGS_PINCTRL_BASE, 0x00000710)
+#define HW_PINCTRL_DOE1_ADDR (REGS_PINCTRL_BASE + 0x00000710)
+#define BP_PINCTRL_DOE1_DOE 0
+#define BM_PINCTRL_DOE1_DOE 0x7FFFFFFF
+#define BF_PINCTRL_DOE1_DOE(v) \
+ (((v) << 0) & BM_PINCTRL_DOE1_DOE)
+HW_REGISTER(HW_PINCTRL_DOE2, REGS_PINCTRL_BASE, 0x00000720)
+#define HW_PINCTRL_DOE2_ADDR (REGS_PINCTRL_BASE + 0x00000720)
+#define BP_PINCTRL_DOE2_DOE 0
+#define BM_PINCTRL_DOE2_DOE 0xFFFFFFFF
+#define BF_PINCTRL_DOE2_DOE(v) (v)
+HW_REGISTER(HW_PINCTRL_PIN2IRQ0, REGS_PINCTRL_BASE, 0x00000800)
+#define HW_PINCTRL_PIN2IRQ0_ADDR (REGS_PINCTRL_BASE + 0x00000800)
+#define BP_PINCTRL_PIN2IRQ0_PIN2IRQ 0
+#define BM_PINCTRL_PIN2IRQ0_PIN2IRQ 0xFFFFFFFF
+#define BF_PINCTRL_PIN2IRQ0_PIN2IRQ(v) (v)
+HW_REGISTER(HW_PINCTRL_PIN2IRQ1, REGS_PINCTRL_BASE, 0x00000810)
+#define HW_PINCTRL_PIN2IRQ1_ADDR (REGS_PINCTRL_BASE + 0x00000810)
+#define BP_PINCTRL_PIN2IRQ1_PIN2IRQ 0
+#define BM_PINCTRL_PIN2IRQ1_PIN2IRQ 0x7FFFFFFF
+#define BF_PINCTRL_PIN2IRQ1_PIN2IRQ(v) \
+ (((v) << 0) & BM_PINCTRL_PIN2IRQ1_PIN2IRQ)
+HW_REGISTER(HW_PINCTRL_PIN2IRQ2, REGS_PINCTRL_BASE, 0x00000820)
+#define HW_PINCTRL_PIN2IRQ2_ADDR (REGS_PINCTRL_BASE + 0x00000820)
+#define BP_PINCTRL_PIN2IRQ2_PIN2IRQ 0
+#define BM_PINCTRL_PIN2IRQ2_PIN2IRQ 0xFFFFFFFF
+#define BF_PINCTRL_PIN2IRQ2_PIN2IRQ(v) (v)
+HW_REGISTER(HW_PINCTRL_IRQEN0, REGS_PINCTRL_BASE, 0x00000900)
+#define HW_PINCTRL_IRQEN0_ADDR (REGS_PINCTRL_BASE + 0x00000900)
+#define BP_PINCTRL_IRQEN0_IRQEN 0
+#define BM_PINCTRL_IRQEN0_IRQEN 0xFFFFFFFF
+#define BF_PINCTRL_IRQEN0_IRQEN(v) (v)
+HW_REGISTER(HW_PINCTRL_IRQEN1, REGS_PINCTRL_BASE, 0x00000910)
+#define HW_PINCTRL_IRQEN1_ADDR (REGS_PINCTRL_BASE + 0x00000910)
+#define BP_PINCTRL_IRQEN1_IRQEN 0
+#define BM_PINCTRL_IRQEN1_IRQEN 0x7FFFFFFF
+#define BF_PINCTRL_IRQEN1_IRQEN(v) \
+ (((v) << 0) & BM_PINCTRL_IRQEN1_IRQEN)
+HW_REGISTER(HW_PINCTRL_IRQEN2, REGS_PINCTRL_BASE, 0x00000920)
+#define HW_PINCTRL_IRQEN2_ADDR (REGS_PINCTRL_BASE + 0x00000920)
+#define BP_PINCTRL_IRQEN2_IRQEN 0
+#define BM_PINCTRL_IRQEN2_IRQEN 0xFFFFFFFF
+#define BF_PINCTRL_IRQEN2_IRQEN(v) (v)
+HW_REGISTER(HW_PINCTRL_IRQLEVEL0, REGS_PINCTRL_BASE, 0x00000a00)
+#define HW_PINCTRL_IRQLEVEL0_ADDR (REGS_PINCTRL_BASE + 0x00000a00)
+#define BP_PINCTRL_IRQLEVEL0_IRQLEVEL 0
+#define BM_PINCTRL_IRQLEVEL0_IRQLEVEL 0xFFFFFFFF
+#define BF_PINCTRL_IRQLEVEL0_IRQLEVEL(v) (v)
+HW_REGISTER(HW_PINCTRL_IRQLEVEL1, REGS_PINCTRL_BASE, 0x00000a10)
+#define HW_PINCTRL_IRQLEVEL1_ADDR (REGS_PINCTRL_BASE + 0x00000a10)
+#define BP_PINCTRL_IRQLEVEL1_IRQLEVEL 0
+#define BM_PINCTRL_IRQLEVEL1_IRQLEVEL 0x7FFFFFFF
+#define BF_PINCTRL_IRQLEVEL1_IRQLEVEL(v) \
+ (((v) << 0) & BM_PINCTRL_IRQLEVEL1_IRQLEVEL)
+HW_REGISTER(HW_PINCTRL_IRQLEVEL2, REGS_PINCTRL_BASE, 0x00000a20)
+#define HW_PINCTRL_IRQLEVEL2_ADDR (REGS_PINCTRL_BASE + 0x00000a20)
+#define BP_PINCTRL_IRQLEVEL2_IRQLEVEL 0
+#define BM_PINCTRL_IRQLEVEL2_IRQLEVEL 0xFFFFFFFF
+#define BF_PINCTRL_IRQLEVEL2_IRQLEVEL(v) (v)
+HW_REGISTER(HW_PINCTRL_IRQPOL0, REGS_PINCTRL_BASE, 0x00000b00)
+#define HW_PINCTRL_IRQPOL0_ADDR (REGS_PINCTRL_BASE + 0x00000b00)
+#define BP_PINCTRL_IRQPOL0_IRQPOL 0
+#define BM_PINCTRL_IRQPOL0_IRQPOL 0xFFFFFFFF
+#define BF_PINCTRL_IRQPOL0_IRQPOL(v) (v)
+HW_REGISTER(HW_PINCTRL_IRQPOL1, REGS_PINCTRL_BASE, 0x00000b10)
+#define HW_PINCTRL_IRQPOL1_ADDR (REGS_PINCTRL_BASE + 0x00000b10)
+#define BP_PINCTRL_IRQPOL1_IRQPOL 0
+#define BM_PINCTRL_IRQPOL1_IRQPOL 0x7FFFFFFF
+#define BF_PINCTRL_IRQPOL1_IRQPOL(v) \
+ (((v) << 0) & BM_PINCTRL_IRQPOL1_IRQPOL)
+HW_REGISTER(HW_PINCTRL_IRQPOL2, REGS_PINCTRL_BASE, 0x00000b20)
+#define HW_PINCTRL_IRQPOL2_ADDR (REGS_PINCTRL_BASE + 0x00000b20)
+#define BP_PINCTRL_IRQPOL2_IRQPOL 0
+#define BM_PINCTRL_IRQPOL2_IRQPOL 0xFFFFFFFF
+#define BF_PINCTRL_IRQPOL2_IRQPOL(v) (v)
+HW_REGISTER(HW_PINCTRL_IRQSTAT0, REGS_PINCTRL_BASE, 0x00000c00)
+#define HW_PINCTRL_IRQSTAT0_ADDR (REGS_PINCTRL_BASE + 0x00000c00)
+#define BP_PINCTRL_IRQSTAT0_IRQSTAT 0
+#define BM_PINCTRL_IRQSTAT0_IRQSTAT 0xFFFFFFFF
+#define BF_PINCTRL_IRQSTAT0_IRQSTAT(v) (v)
+HW_REGISTER(HW_PINCTRL_IRQSTAT1, REGS_PINCTRL_BASE, 0x00000c10)
+#define HW_PINCTRL_IRQSTAT1_ADDR (REGS_PINCTRL_BASE + 0x00000c10)
+#define BP_PINCTRL_IRQSTAT1_IRQSTAT 0
+#define BM_PINCTRL_IRQSTAT1_IRQSTAT 0x7FFFFFFF
+#define BF_PINCTRL_IRQSTAT1_IRQSTAT(v) \
+ (((v) << 0) & BM_PINCTRL_IRQSTAT1_IRQSTAT)
+HW_REGISTER(HW_PINCTRL_IRQSTAT2, REGS_PINCTRL_BASE, 0x00000c20)
+#define HW_PINCTRL_IRQSTAT2_ADDR (REGS_PINCTRL_BASE + 0x00000c20)
+#define BP_PINCTRL_IRQSTAT2_IRQSTAT 0
+#define BM_PINCTRL_IRQSTAT2_IRQSTAT 0xFFFFFFFF
+#define BF_PINCTRL_IRQSTAT2_IRQSTAT(v) (v)
+#endif /* __ARCH_ARM___PINCTRL_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-power.h b/arch/arm/mach-stmp3xxx/include/mach/regs-power.h
new file mode 100644
index 000000000000..90fb0ff3a19c
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-power.h
@@ -0,0 +1,349 @@
+/*
+ * STMP POWER Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___POWER_H
+#define __ARCH_ARM___POWER_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_POWER_BASE (REGS_BASE + 0x44000)
+#define REGS_POWER_BASE_PHYS (0x80044000)
+#define REGS_POWER_SIZE 0x00002000
+HW_REGISTER(HW_POWER_CTRL, REGS_POWER_BASE, 0x00000000)
+#define HW_POWER_CTRL_ADDR (REGS_POWER_BASE + 0x00000000)
+#define BM_POWER_CTRL_CLKGATE 0x40000000
+#define BM_POWER_CTRL_PSWITCH_MID_TRAN 0x08000000
+#define BM_POWER_CTRL_DCDC4P2_BO_IRQ 0x01000000
+#define BM_POWER_CTRL_ENIRQ_DCDC4P2_BO 0x00800000
+#define BM_POWER_CTRL_VDD5V_DROOP_IRQ 0x00400000
+#define BM_POWER_CTRL_ENIRQ_VDD5V_DROOP 0x00200000
+#define BM_POWER_CTRL_PSWITCH_IRQ 0x00100000
+#define BM_POWER_CTRL_PSWITCH_IRQ_SRC 0x00080000
+#define BM_POWER_CTRL_POLARITY_PSWITCH 0x00040000
+#define BM_POWER_CTRL_ENIRQ_PSWITCH 0x00020000
+#define BM_POWER_CTRL_POLARITY_DC_OK 0x00010000
+#define BM_POWER_CTRL_DC_OK_IRQ 0x00008000
+#define BM_POWER_CTRL_ENIRQ_DC_OK 0x00004000
+#define BM_POWER_CTRL_BATT_BO_IRQ 0x00002000
+#define BM_POWER_CTRL_ENIRQBATT_BO 0x00001000
+#define BM_POWER_CTRL_VDDIO_BO_IRQ 0x00000800
+#define BM_POWER_CTRL_ENIRQ_VDDIO_BO 0x00000400
+#define BM_POWER_CTRL_VDDA_BO_IRQ 0x00000200
+#define BM_POWER_CTRL_ENIRQ_VDDA_BO 0x00000100
+#define BM_POWER_CTRL_VDDD_BO_IRQ 0x00000080
+#define BM_POWER_CTRL_ENIRQ_VDDD_BO 0x00000040
+#define BM_POWER_CTRL_POLARITY_VBUSVALID 0x00000020
+#define BM_POWER_CTRL_VBUSVALID_IRQ 0x00000010
+#define BM_POWER_CTRL_ENIRQ_VBUS_VALID 0x00000008
+#define BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO 0x00000004
+#define BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ 0x00000002
+#define BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO 0x00000001
+HW_REGISTER(HW_POWER_5VCTRL, REGS_POWER_BASE, 0x00000010)
+#define HW_POWER_5VCTRL_ADDR (REGS_POWER_BASE + 0x00000010)
+#define BP_POWER_5VCTRL_VBUSDROOP_TRSH 28
+#define BM_POWER_5VCTRL_VBUSDROOP_TRSH 0x30000000
+#define BF_POWER_5VCTRL_VBUSDROOP_TRSH(v) \
+ (((v) << 28) & BM_POWER_5VCTRL_VBUSDROOP_TRSH)
+#define BP_POWER_5VCTRL_HEADROOM_ADJ 24
+#define BM_POWER_5VCTRL_HEADROOM_ADJ 0x07000000
+#define BF_POWER_5VCTRL_HEADROOM_ADJ(v) \
+ (((v) << 24) & BM_POWER_5VCTRL_HEADROOM_ADJ)
+#define BM_POWER_5VCTRL_PWD_CHARGE_4P2 0x00100000
+#define BP_POWER_5VCTRL_CHARGE_4P2_ILIMIT 12
+#define BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT 0x0003F000
+#define BF_POWER_5VCTRL_CHARGE_4P2_ILIMIT(v) \
+ (((v) << 12) & BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT)
+#define BP_POWER_5VCTRL_VBUSVALID_TRSH 8
+#define BM_POWER_5VCTRL_VBUSVALID_TRSH 0x00000700
+#define BF_POWER_5VCTRL_VBUSVALID_TRSH(v) \
+ (((v) << 8) & BM_POWER_5VCTRL_VBUSVALID_TRSH)
+#define BM_POWER_5VCTRL_PWDN_5VBRNOUT 0x00000080
+#define BM_POWER_5VCTRL_ENABLE_LINREG_ILIMIT 0x00000040
+#define BM_POWER_5VCTRL_DCDC_XFER 0x00000020
+#define BM_POWER_5VCTRL_VBUSVALID_5VDETECT 0x00000010
+#define BM_POWER_5VCTRL_VBUSVALID_TO_B 0x00000008
+#define BM_POWER_5VCTRL_ILIMIT_EQ_ZERO 0x00000004
+#define BM_POWER_5VCTRL_PWRUP_VBUS_CMPS 0x00000002
+#define BM_POWER_5VCTRL_ENABLE_DCDC 0x00000001
+HW_REGISTER(HW_POWER_MINPWR, REGS_POWER_BASE, 0x00000020)
+#define HW_POWER_MINPWR_ADDR (REGS_POWER_BASE + 0x00000020)
+#define BM_POWER_MINPWR_LOWPWR_4P2 0x00004000
+#define BM_POWER_MINPWR_VDAC_DUMP_CTRL 0x00002000
+#define BM_POWER_MINPWR_PWD_BO 0x00001000
+#define BM_POWER_MINPWR_USE_VDDXTAL_VBG 0x00000800
+#define BM_POWER_MINPWR_PWD_ANA_CMPS 0x00000400
+#define BM_POWER_MINPWR_ENABLE_OSC 0x00000200
+#define BM_POWER_MINPWR_SELECT_OSC 0x00000100
+#define BM_POWER_MINPWR_VBG_OFF 0x00000080
+#define BM_POWER_MINPWR_DOUBLE_FETS 0x00000040
+#define BM_POWER_MINPWR_HALF_FETS 0x00000020
+#define BM_POWER_MINPWR_LESSANA_I 0x00000010
+#define BM_POWER_MINPWR_PWD_XTAL24 0x00000008
+#define BM_POWER_MINPWR_DC_STOPCLK 0x00000004
+#define BM_POWER_MINPWR_EN_DC_PFM 0x00000002
+#define BM_POWER_MINPWR_DC_HALFCLK 0x00000001
+HW_REGISTER(HW_POWER_CHARGE, REGS_POWER_BASE, 0x00000030)
+#define HW_POWER_CHARGE_ADDR (REGS_POWER_BASE + 0x00000030)
+#define BP_POWER_CHARGE_ADJ_VOLT 24
+#define BM_POWER_CHARGE_ADJ_VOLT 0x07000000
+#define BF_POWER_CHARGE_ADJ_VOLT(v) \
+ (((v) << 24) & BM_POWER_CHARGE_ADJ_VOLT)
+#define BM_POWER_CHARGE_RSRVD3 0x00800000
+#define BM_POWER_CHARGE_ENABLE_LOAD 0x00400000
+#define BM_POWER_CHARGE_ENABLE_CHARGER_RESISTORS 0x00200000
+#define BM_POWER_CHARGE_ENABLE_FAULT_DETECT 0x00100000
+#define BM_POWER_CHARGE_CHRG_STS_OFF 0x00080000
+#define BM_POWER_CHARGE_USE_EXTERN_R 0x00020000
+#define BM_POWER_CHARGE_PWD_BATTCHRG 0x00010000
+#define BP_POWER_CHARGE_STOP_ILIMIT 8
+#define BM_POWER_CHARGE_STOP_ILIMIT 0x00000F00
+#define BF_POWER_CHARGE_STOP_ILIMIT(v) \
+ (((v) << 8) & BM_POWER_CHARGE_STOP_ILIMIT)
+#define BP_POWER_CHARGE_BATTCHRG_I 0
+#define BM_POWER_CHARGE_BATTCHRG_I 0x0000003F
+#define BF_POWER_CHARGE_BATTCHRG_I(v) \
+ (((v) << 0) & BM_POWER_CHARGE_BATTCHRG_I)
+HW_REGISTER_0(HW_POWER_VDDDCTRL, REGS_POWER_BASE, 0x00000040)
+#define HW_POWER_VDDDCTRL_ADDR (REGS_POWER_BASE + 0x00000040)
+#define BP_POWER_VDDDCTRL_ADJTN 28
+#define BM_POWER_VDDDCTRL_ADJTN 0xF0000000
+#define BF_POWER_VDDDCTRL_ADJTN(v) \
+ (((v) << 28) & BM_POWER_VDDDCTRL_ADJTN)
+#define BM_POWER_VDDDCTRL_PWDN_BRNOUT 0x00800000
+#define BM_POWER_VDDDCTRL_DISABLE_STEPPING 0x00400000
+#define BM_POWER_VDDDCTRL_ENABLE_LINREG 0x00200000
+#define BM_POWER_VDDDCTRL_DISABLE_FET 0x00100000
+#define BP_POWER_VDDDCTRL_LINREG_OFFSET 16
+#define BM_POWER_VDDDCTRL_LINREG_OFFSET 0x00030000
+#define BF_POWER_VDDDCTRL_LINREG_OFFSET(v) \
+ (((v) << 16) & BM_POWER_VDDDCTRL_LINREG_OFFSET)
+#define BP_POWER_VDDDCTRL_BO_OFFSET 8
+#define BM_POWER_VDDDCTRL_BO_OFFSET 0x00000700
+#define BF_POWER_VDDDCTRL_BO_OFFSET(v) \
+ (((v) << 8) & BM_POWER_VDDDCTRL_BO_OFFSET)
+#define BP_POWER_VDDDCTRL_TRG 0
+#define BM_POWER_VDDDCTRL_TRG 0x0000001F
+#define BF_POWER_VDDDCTRL_TRG(v) \
+ (((v) << 0) & BM_POWER_VDDDCTRL_TRG)
+HW_REGISTER_0(HW_POWER_VDDACTRL, REGS_POWER_BASE, 0x00000050)
+#define HW_POWER_VDDACTRL_ADDR (REGS_POWER_BASE + 0x00000050)
+#define BM_POWER_VDDACTRL_PWDN_BRNOUT 0x00080000
+#define BM_POWER_VDDACTRL_DISABLE_STEPPING 0x00040000
+#define BM_POWER_VDDACTRL_ENABLE_LINREG 0x00020000
+#define BM_POWER_VDDACTRL_DISABLE_FET 0x00010000
+#define BP_POWER_VDDACTRL_LINREG_OFFSET 12
+#define BM_POWER_VDDACTRL_LINREG_OFFSET 0x00003000
+#define BF_POWER_VDDACTRL_LINREG_OFFSET(v) \
+ (((v) << 12) & BM_POWER_VDDACTRL_LINREG_OFFSET)
+#define BP_POWER_VDDACTRL_BO_OFFSET 8
+#define BM_POWER_VDDACTRL_BO_OFFSET 0x00000700
+#define BF_POWER_VDDACTRL_BO_OFFSET(v) \
+ (((v) << 8) & BM_POWER_VDDACTRL_BO_OFFSET)
+#define BP_POWER_VDDACTRL_TRG 0
+#define BM_POWER_VDDACTRL_TRG 0x0000001F
+#define BF_POWER_VDDACTRL_TRG(v) \
+ (((v) << 0) & BM_POWER_VDDACTRL_TRG)
+HW_REGISTER_0(HW_POWER_VDDIOCTRL, REGS_POWER_BASE, 0x00000060)
+#define HW_POWER_VDDIOCTRL_ADDR (REGS_POWER_BASE + 0x00000060)
+#define BP_POWER_VDDIOCTRL_ADJTN 20
+#define BM_POWER_VDDIOCTRL_ADJTN 0x00F00000
+#define BF_POWER_VDDIOCTRL_ADJTN(v) \
+ (((v) << 20) & BM_POWER_VDDIOCTRL_ADJTN)
+#define BM_POWER_VDDIOCTRL_PWDN_BRNOUT 0x00040000
+#define BM_POWER_VDDIOCTRL_DISABLE_STEPPING 0x00020000
+#define BM_POWER_VDDIOCTRL_DISABLE_FET 0x00010000
+#define BP_POWER_VDDIOCTRL_LINREG_OFFSET 12
+#define BM_POWER_VDDIOCTRL_LINREG_OFFSET 0x00003000
+#define BF_POWER_VDDIOCTRL_LINREG_OFFSET(v) \
+ (((v) << 12) & BM_POWER_VDDIOCTRL_LINREG_OFFSET)
+#define BP_POWER_VDDIOCTRL_BO_OFFSET 8
+#define BM_POWER_VDDIOCTRL_BO_OFFSET 0x00000700
+#define BF_POWER_VDDIOCTRL_BO_OFFSET(v) \
+ (((v) << 8) & BM_POWER_VDDIOCTRL_BO_OFFSET)
+#define BP_POWER_VDDIOCTRL_TRG 0
+#define BM_POWER_VDDIOCTRL_TRG 0x0000001F
+#define BF_POWER_VDDIOCTRL_TRG(v) \
+ (((v) << 0) & BM_POWER_VDDIOCTRL_TRG)
+HW_REGISTER_0(HW_POWER_VDDMEMCTRL, REGS_POWER_BASE, 0x00000070)
+#define HW_POWER_VDDMEMCTRL_ADDR (REGS_POWER_BASE + 0x00000070)
+#define BM_POWER_VDDMEMCTRL_PULLDOWN_ACTIVE 0x00000400
+#define BM_POWER_VDDMEMCTRL_ENABLE_ILIMIT 0x00000200
+#define BM_POWER_VDDMEMCTRL_ENABLE_LINREG 0x00000100
+#define BP_POWER_VDDMEMCTRL_TRG 0
+#define BM_POWER_VDDMEMCTRL_TRG 0x0000001F
+#define BF_POWER_VDDMEMCTRL_TRG(v) \
+ (((v) << 0) & BM_POWER_VDDMEMCTRL_TRG)
+HW_REGISTER_0(HW_POWER_DCDC4P2, REGS_POWER_BASE, 0x00000080)
+#define HW_POWER_DCDC4P2_ADDR (REGS_POWER_BASE + 0x00000080)
+#define BP_POWER_DCDC4P2_DROPOUT_CTRL 28
+#define BM_POWER_DCDC4P2_DROPOUT_CTRL 0xF0000000
+#define BF_POWER_DCDC4P2_DROPOUT_CTRL(v) \
+ (((v) << 28) & BM_POWER_DCDC4P2_DROPOUT_CTRL)
+#define BP_POWER_DCDC4P2_ISTEAL_THRESH 24
+#define BM_POWER_DCDC4P2_ISTEAL_THRESH 0x03000000
+#define BF_POWER_DCDC4P2_ISTEAL_THRESH(v) \
+ (((v) << 24) & BM_POWER_DCDC4P2_ISTEAL_THRESH)
+#define BM_POWER_DCDC4P2_ENABLE_4P2 0x00800000
+#define BM_POWER_DCDC4P2_ENABLE_DCDC 0x00400000
+#define BM_POWER_DCDC4P2_HYST_DIR 0x00200000
+#define BM_POWER_DCDC4P2_HYST_THRESH 0x00100000
+#define BP_POWER_DCDC4P2_TRG 16
+#define BM_POWER_DCDC4P2_TRG 0x00070000
+#define BF_POWER_DCDC4P2_TRG(v) \
+ (((v) << 16) & BM_POWER_DCDC4P2_TRG)
+#define BP_POWER_DCDC4P2_BO 8
+#define BM_POWER_DCDC4P2_BO 0x00001F00
+#define BF_POWER_DCDC4P2_BO(v) \
+ (((v) << 8) & BM_POWER_DCDC4P2_BO)
+#define BP_POWER_DCDC4P2_CMPTRIP 0
+#define BM_POWER_DCDC4P2_CMPTRIP 0x0000001F
+#define BF_POWER_DCDC4P2_CMPTRIP(v) \
+ (((v) << 0) & BM_POWER_DCDC4P2_CMPTRIP)
+HW_REGISTER_0(HW_POWER_MISC, REGS_POWER_BASE, 0x00000090)
+#define HW_POWER_MISC_ADDR (REGS_POWER_BASE + 0x00000090)
+#define BP_POWER_MISC_FREQSEL 4
+#define BM_POWER_MISC_FREQSEL 0x00000070
+#define BF_POWER_MISC_FREQSEL(v) \
+ (((v) << 4) & BM_POWER_MISC_FREQSEL)
+#define BM_POWER_MISC_RSRVD1 0x00000008
+#define BM_POWER_MISC_DELAY_TIMING 0x00000004
+#define BM_POWER_MISC_TEST 0x00000002
+#define BM_POWER_MISC_SEL_PLLCLK 0x00000001
+HW_REGISTER_0(HW_POWER_DCLIMITS, REGS_POWER_BASE, 0x000000a0)
+#define HW_POWER_DCLIMITS_ADDR (REGS_POWER_BASE + 0x000000a0)
+#define BP_POWER_DCLIMITS_POSLIMIT_BUCK 8
+#define BM_POWER_DCLIMITS_POSLIMIT_BUCK 0x00007F00
+#define BF_POWER_DCLIMITS_POSLIMIT_BUCK(v) \
+ (((v) << 8) & BM_POWER_DCLIMITS_POSLIMIT_BUCK)
+#define BP_POWER_DCLIMITS_NEGLIMIT 0
+#define BM_POWER_DCLIMITS_NEGLIMIT 0x0000007F
+#define BF_POWER_DCLIMITS_NEGLIMIT(v) \
+ (((v) << 0) & BM_POWER_DCLIMITS_NEGLIMIT)
+HW_REGISTER(HW_POWER_LOOPCTRL, REGS_POWER_BASE, 0x000000b0)
+#define HW_POWER_LOOPCTRL_ADDR (REGS_POWER_BASE + 0x000000b0)
+#define BM_POWER_LOOPCTRL_TOGGLE_DIF 0x00100000
+#define BM_POWER_LOOPCTRL_HYST_SIGN 0x00080000
+#define BM_POWER_LOOPCTRL_EN_CM_HYST 0x00040000
+#define BM_POWER_LOOPCTRL_EN_DF_HYST 0x00020000
+#define BM_POWER_LOOPCTRL_CM_HYST_THRESH 0x00010000
+#define BM_POWER_LOOPCTRL_DF_HYST_THRESH 0x00008000
+#define BM_POWER_LOOPCTRL_RCSCALE_THRESH 0x00004000
+#define BP_POWER_LOOPCTRL_EN_RCSCALE 12
+#define BM_POWER_LOOPCTRL_EN_RCSCALE 0x00003000
+#define BF_POWER_LOOPCTRL_EN_RCSCALE(v) \
+ (((v) << 12) & BM_POWER_LOOPCTRL_EN_RCSCALE)
+#define BP_POWER_LOOPCTRL_DC_FF 8
+#define BM_POWER_LOOPCTRL_DC_FF 0x00000700
+#define BF_POWER_LOOPCTRL_DC_FF(v) \
+ (((v) << 8) & BM_POWER_LOOPCTRL_DC_FF)
+#define BP_POWER_LOOPCTRL_DC_R 4
+#define BM_POWER_LOOPCTRL_DC_R 0x000000F0
+#define BF_POWER_LOOPCTRL_DC_R(v) \
+ (((v) << 4) & BM_POWER_LOOPCTRL_DC_R)
+#define BP_POWER_LOOPCTRL_DC_C 0
+#define BM_POWER_LOOPCTRL_DC_C 0x00000003
+#define BF_POWER_LOOPCTRL_DC_C(v) \
+ (((v) << 0) & BM_POWER_LOOPCTRL_DC_C)
+HW_REGISTER_0(HW_POWER_STS, REGS_POWER_BASE, 0x000000c0)
+#define HW_POWER_STS_ADDR (REGS_POWER_BASE + 0x000000c0)
+#define BP_POWER_STS_PWRUP_SOURCE 24
+#define BM_POWER_STS_PWRUP_SOURCE 0x3F000000
+#define BF_POWER_STS_PWRUP_SOURCE(v) \
+ (((v) << 24) & BM_POWER_STS_PWRUP_SOURCE)
+#define BP_POWER_STS_PSWITCH 20
+#define BM_POWER_STS_PSWITCH 0x00300000
+#define BF_POWER_STS_PSWITCH(v) \
+ (((v) << 20) & BM_POWER_STS_PSWITCH)
+#define BM_POWER_STS_AVALID_STATUS 0x00020000
+#define BM_POWER_STS_BVALID_STATUS 0x00010000
+#define BM_POWER_STS_VBUSVALID_STATUS 0x00008000
+#define BM_POWER_STS_SESSEND_STATUS 0x00004000
+#define BM_POWER_STS_BATT_BO 0x00002000
+#define BM_POWER_STS_VDD5V_FAULT 0x00001000
+#define BM_POWER_STS_CHRGSTS 0x00000800
+#define BM_POWER_STS_DCDC_4P2_BO 0x00000400
+#define BM_POWER_STS_DC_OK 0x00000200
+#define BM_POWER_STS_VDDIO_BO 0x00000100
+#define BM_POWER_STS_VDDA_BO 0x00000080
+#define BM_POWER_STS_VDDD_BO 0x00000040
+#define BM_POWER_STS_VDD5V_GT_VDDIO 0x00000020
+#define BM_POWER_STS_VDD5V_DROOP 0x00000010
+#define BM_POWER_STS_AVALID 0x00000008
+#define BM_POWER_STS_BVALID 0x00000004
+#define BM_POWER_STS_VBUSVALID 0x00000002
+#define BM_POWER_STS_SESSEND 0x00000001
+HW_REGISTER(HW_POWER_SPEED, REGS_POWER_BASE, 0x000000d0)
+#define HW_POWER_SPEED_ADDR (REGS_POWER_BASE + 0x000000d0)
+#define BP_POWER_SPEED_STATUS 16
+#define BM_POWER_SPEED_STATUS 0x00FF0000
+#define BF_POWER_SPEED_STATUS(v) \
+ (((v) << 16) & BM_POWER_SPEED_STATUS)
+#define BP_POWER_SPEED_CTRL 0
+#define BM_POWER_SPEED_CTRL 0x00000003
+#define BF_POWER_SPEED_CTRL(v) \
+ (((v) << 0) & BM_POWER_SPEED_CTRL)
+HW_REGISTER_0(HW_POWER_BATTMONITOR, REGS_POWER_BASE, 0x000000e0)
+#define HW_POWER_BATTMONITOR_ADDR (REGS_POWER_BASE + 0x000000e0)
+#define BP_POWER_BATTMONITOR_BATT_VAL 16
+#define BM_POWER_BATTMONITOR_BATT_VAL 0x03FF0000
+#define BF_POWER_BATTMONITOR_BATT_VAL(v) \
+ (((v) << 16) & BM_POWER_BATTMONITOR_BATT_VAL)
+#define BM_POWER_BATTMONITOR_EN_BATADJ 0x00000400
+#define BM_POWER_BATTMONITOR_PWDN_BATTBRNOUT 0x00000200
+#define BM_POWER_BATTMONITOR_BRWNOUT_PWD 0x00000100
+#define BP_POWER_BATTMONITOR_BRWNOUT_LVL 0
+#define BM_POWER_BATTMONITOR_BRWNOUT_LVL 0x0000001F
+#define BF_POWER_BATTMONITOR_BRWNOUT_LVL(v) \
+ (((v) << 0) & BM_POWER_BATTMONITOR_BRWNOUT_LVL)
+HW_REGISTER(HW_POWER_RESET, REGS_POWER_BASE, 0x00000100)
+#define HW_POWER_RESET_ADDR (REGS_POWER_BASE + 0x00000100)
+#define BP_POWER_RESET_UNLOCK 16
+#define BM_POWER_RESET_UNLOCK 0xFFFF0000
+#define BF_POWER_RESET_UNLOCK(v) \
+ (((v) << 16) & BM_POWER_RESET_UNLOCK)
+#define BV_POWER_RESET_UNLOCK__KEY 0x3E77
+#define BM_POWER_RESET_PWD_OFF 0x00000002
+#define BM_POWER_RESET_PWD 0x00000001
+HW_REGISTER(HW_POWER_DEBUG, REGS_POWER_BASE, 0x00000110)
+#define HW_POWER_DEBUG_ADDR (REGS_POWER_BASE + 0x00000110)
+#define BM_POWER_DEBUG_VBUSVALIDPIOLOCK 0x00000008
+#define BM_POWER_DEBUG_AVALIDPIOLOCK 0x00000004
+#define BM_POWER_DEBUG_BVALIDPIOLOCK 0x00000002
+#define BM_POWER_DEBUG_SESSENDPIOLOCK 0x00000001
+HW_REGISTER(HW_POWER_SPECIAL, REGS_POWER_BASE, 0x00000120)
+#define HW_POWER_SPECIAL_ADDR (REGS_POWER_BASE + 0x00000120)
+#define BP_POWER_SPECIAL_TEST 0
+#define BM_POWER_SPECIAL_TEST 0xFFFFFFFF
+#define BF_POWER_SPECIAL_TEST(v) (v)
+HW_REGISTER_0(HW_POWER_VERSION, REGS_POWER_BASE, 0x00000130)
+#define HW_POWER_VERSION_ADDR (REGS_POWER_BASE + 0x00000130)
+#define BP_POWER_VERSION_MAJOR 24
+#define BM_POWER_VERSION_MAJOR 0xFF000000
+#define BF_POWER_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_POWER_VERSION_MAJOR)
+#define BP_POWER_VERSION_MINOR 16
+#define BM_POWER_VERSION_MINOR 0x00FF0000
+#define BF_POWER_VERSION_MINOR(v) \
+ (((v) << 16) & BM_POWER_VERSION_MINOR)
+#define BP_POWER_VERSION_STEP 0
+#define BM_POWER_VERSION_STEP 0x0000FFFF
+#define BF_POWER_VERSION_STEP(v) \
+ (((v) << 0) & BM_POWER_VERSION_STEP)
+#endif /* __ARCH_ARM___POWER_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-pwm.h b/arch/arm/mach-stmp3xxx/include/mach/regs-pwm.h
new file mode 100644
index 000000000000..a257ee685242
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-pwm.h
@@ -0,0 +1,113 @@
+/*
+ * STMP PWM Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___PWM_H
+#define __ARCH_ARM___PWM_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_PWM_BASE (REGS_BASE + 0x64000)
+#define REGS_PWM_BASE_PHYS (0x80064000)
+#define REGS_PWM_SIZE 0x00002000
+HW_REGISTER(HW_PWM_CTRL, REGS_PWM_BASE, 0x00000000)
+#define HW_PWM_CTRL_ADDR (REGS_PWM_BASE + 0x00000000)
+#define BM_PWM_CTRL_SFTRST 0x80000000
+#define BM_PWM_CTRL_CLKGATE 0x40000000
+#define BM_PWM_CTRL_PWM4_PRESENT 0x20000000
+#define BM_PWM_CTRL_PWM3_PRESENT 0x10000000
+#define BM_PWM_CTRL_PWM2_PRESENT 0x08000000
+#define BM_PWM_CTRL_PWM1_PRESENT 0x04000000
+#define BM_PWM_CTRL_PWM0_PRESENT 0x02000000
+#define BM_PWM_CTRL_OUTPUT_CUTOFF_EN 0x00000040
+#define BM_PWM_CTRL_PWM2_ANA_CTRL_ENABLE 0x00000020
+#define BM_PWM_CTRL_PWM4_ENABLE 0x00000010
+#define BM_PWM_CTRL_PWM3_ENABLE 0x00000008
+#define BM_PWM_CTRL_PWM2_ENABLE 0x00000004
+#define BM_PWM_CTRL_PWM1_ENABLE 0x00000002
+#define BM_PWM_CTRL_PWM0_ENABLE 0x00000001
+/*
+ * multi-register-define name HW_PWM_ACTIVEn
+ * base 0x00000010
+ * count 5
+ * offset 0x20
+ */
+HW_REGISTER_INDEXED(HW_PWM_ACTIVEn, REGS_PWM_BASE, 0x00000010, 0x20)
+#define BP_PWM_ACTIVEn_INACTIVE 16
+#define BM_PWM_ACTIVEn_INACTIVE 0xFFFF0000
+#define BF_PWM_ACTIVEn_INACTIVE(v) \
+ (((v) << 16) & BM_PWM_ACTIVEn_INACTIVE)
+#define BP_PWM_ACTIVEn_ACTIVE 0
+#define BM_PWM_ACTIVEn_ACTIVE 0x0000FFFF
+#define BF_PWM_ACTIVEn_ACTIVE(v) \
+ (((v) << 0) & BM_PWM_ACTIVEn_ACTIVE)
+/*
+ * multi-register-define name HW_PWM_PERIODn
+ * base 0x00000020
+ * count 5
+ * offset 0x20
+ */
+HW_REGISTER_INDEXED(HW_PWM_PERIODn, REGS_PWM_BASE, 0x00000020, 0x20)
+#define BM_PWM_PERIODn_MATT_SEL 0x01000000
+#define BM_PWM_PERIODn_MATT 0x00800000
+#define BP_PWM_PERIODn_CDIV 20
+#define BM_PWM_PERIODn_CDIV 0x00700000
+#define BF_PWM_PERIODn_CDIV(v) \
+ (((v) << 20) & BM_PWM_PERIODn_CDIV)
+#define BV_PWM_PERIODn_CDIV__DIV_1 0x0
+#define BV_PWM_PERIODn_CDIV__DIV_2 0x1
+#define BV_PWM_PERIODn_CDIV__DIV_4 0x2
+#define BV_PWM_PERIODn_CDIV__DIV_8 0x3
+#define BV_PWM_PERIODn_CDIV__DIV_16 0x4
+#define BV_PWM_PERIODn_CDIV__DIV_64 0x5
+#define BV_PWM_PERIODn_CDIV__DIV_256 0x6
+#define BV_PWM_PERIODn_CDIV__DIV_1024 0x7
+#define BP_PWM_PERIODn_INACTIVE_STATE 18
+#define BM_PWM_PERIODn_INACTIVE_STATE 0x000C0000
+#define BF_PWM_PERIODn_INACTIVE_STATE(v) \
+ (((v) << 18) & BM_PWM_PERIODn_INACTIVE_STATE)
+#define BV_PWM_PERIODn_INACTIVE_STATE__HI_Z 0x0
+#define BV_PWM_PERIODn_INACTIVE_STATE__0 0x2
+#define BV_PWM_PERIODn_INACTIVE_STATE__1 0x3
+#define BP_PWM_PERIODn_ACTIVE_STATE 16
+#define BM_PWM_PERIODn_ACTIVE_STATE 0x00030000
+#define BF_PWM_PERIODn_ACTIVE_STATE(v) \
+ (((v) << 16) & BM_PWM_PERIODn_ACTIVE_STATE)
+#define BV_PWM_PERIODn_ACTIVE_STATE__HI_Z 0x0
+#define BV_PWM_PERIODn_ACTIVE_STATE__0 0x2
+#define BV_PWM_PERIODn_ACTIVE_STATE__1 0x3
+#define BP_PWM_PERIODn_PERIOD 0
+#define BM_PWM_PERIODn_PERIOD 0x0000FFFF
+#define BF_PWM_PERIODn_PERIOD(v) \
+ (((v) << 0) & BM_PWM_PERIODn_PERIOD)
+HW_REGISTER_0(HW_PWM_VERSION, REGS_PWM_BASE, 0x000000b0)
+#define HW_PWM_VERSION_ADDR (REGS_PWM_BASE + 0x000000b0)
+#define BP_PWM_VERSION_MAJOR 24
+#define BM_PWM_VERSION_MAJOR 0xFF000000
+#define BF_PWM_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_PWM_VERSION_MAJOR)
+#define BP_PWM_VERSION_MINOR 16
+#define BM_PWM_VERSION_MINOR 0x00FF0000
+#define BF_PWM_VERSION_MINOR(v) \
+ (((v) << 16) & BM_PWM_VERSION_MINOR)
+#define BP_PWM_VERSION_STEP 0
+#define BM_PWM_VERSION_STEP 0x0000FFFF
+#define BF_PWM_VERSION_STEP(v) \
+ (((v) << 0) & BM_PWM_VERSION_STEP)
+#endif /* __ARCH_ARM___PWM_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-pxp.h b/arch/arm/mach-stmp3xxx/include/mach/regs-pxp.h
new file mode 100644
index 000000000000..12e0e5f86b4e
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-pxp.h
@@ -0,0 +1,406 @@
+/*
+ * STMP PXP Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___PXP_H
+#define __ARCH_ARM___PXP_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_PXP_BASE (REGS_BASE + 0x2a000)
+#define REGS_PXP_BASE_PHYS (0x8002A000)
+#define REGS_PXP_SIZE 0x00002000
+HW_REGISTER(HW_PXP_CTRL, REGS_PXP_BASE, 0x00000000)
+#define HW_PXP_CTRL_ADDR (REGS_PXP_BASE + 0x00000000)
+#define BM_PXP_CTRL_SFTRST 0x80000000
+#define BM_PXP_CTRL_CLKGATE 0x40000000
+#define BP_PXP_CTRL_INTERLACED_OUTPUT 26
+#define BM_PXP_CTRL_INTERLACED_OUTPUT 0x0C000000
+#define BF_PXP_CTRL_INTERLACED_OUTPUT(v) \
+ (((v) << 26) & BM_PXP_CTRL_INTERLACED_OUTPUT)
+#define BV_PXP_CTRL_INTERLACED_OUTPUT__PROGRESSIVE 0x0
+#define BV_PXP_CTRL_INTERLACED_OUTPUT__FIELD0 0x1
+#define BV_PXP_CTRL_INTERLACED_OUTPUT__FIELD1 0x2
+#define BV_PXP_CTRL_INTERLACED_OUTPUT__INTERLACED 0x3
+#define BP_PXP_CTRL_INTERLACED_INPUT 24
+#define BM_PXP_CTRL_INTERLACED_INPUT 0x03000000
+#define BF_PXP_CTRL_INTERLACED_INPUT(v) \
+ (((v) << 24) & BM_PXP_CTRL_INTERLACED_INPUT)
+#define BV_PXP_CTRL_INTERLACED_INPUT__PROGRESSIVE 0x0
+#define BV_PXP_CTRL_INTERLACED_INPUT__FIELD0 0x2
+#define BV_PXP_CTRL_INTERLACED_INPUT__FIELD1 0x3
+#define BM_PXP_CTRL_ALPHA_OUTPUT 0x00400000
+#define BM_PXP_CTRL_IN_PLACE 0x00200000
+#define BM_PXP_CTRL_DELTA 0x00100000
+#define BM_PXP_CTRL_CROP 0x00080000
+#define BM_PXP_CTRL_SCALE 0x00040000
+#define BM_PXP_CTRL_UPSAMPLE 0x00020000
+#define BM_PXP_CTRL_SUBSAMPLE 0x00010000
+#define BP_PXP_CTRL_S0_FORMAT 12
+#define BM_PXP_CTRL_S0_FORMAT 0x0000F000
+#define BF_PXP_CTRL_S0_FORMAT(v) \
+ (((v) << 12) & BM_PXP_CTRL_S0_FORMAT)
+#define BV_PXP_CTRL_S0_FORMAT__RGB888 0x1
+#define BV_PXP_CTRL_S0_FORMAT__RGB565 0x4
+#define BV_PXP_CTRL_S0_FORMAT__RGB555 0x5
+#define BV_PXP_CTRL_S0_FORMAT__YUV422 0x8
+#define BV_PXP_CTRL_S0_FORMAT__YUV420 0x9
+#define BM_PXP_CTRL_VFLIP 0x00000800
+#define BM_PXP_CTRL_HFLIP 0x00000400
+#define BP_PXP_CTRL_ROTATE 8
+#define BM_PXP_CTRL_ROTATE 0x00000300
+#define BF_PXP_CTRL_ROTATE(v) \
+ (((v) << 8) & BM_PXP_CTRL_ROTATE)
+#define BV_PXP_CTRL_ROTATE__ROT_0 0x0
+#define BV_PXP_CTRL_ROTATE__ROT_90 0x1
+#define BV_PXP_CTRL_ROTATE__ROT_180 0x2
+#define BV_PXP_CTRL_ROTATE__ROT_270 0x3
+#define BP_PXP_CTRL_OUTPUT_RGB_FORMAT 4
+#define BM_PXP_CTRL_OUTPUT_RGB_FORMAT 0x000000F0
+#define BF_PXP_CTRL_OUTPUT_RGB_FORMAT(v) \
+ (((v) << 4) & BM_PXP_CTRL_OUTPUT_RGB_FORMAT)
+#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__ARGB8888 0x0
+#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB888 0x1
+#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB888P 0x2
+#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__ARGB1555 0x3
+#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB565 0x4
+#define BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB555 0x5
+#define BM_PXP_CTRL_ENABLE_LCD_HANDSHAKE 0x00000004
+#define BM_PXP_CTRL_IRQ_ENABLE 0x00000002
+#define BM_PXP_CTRL_ENABLE 0x00000001
+HW_REGISTER(HW_PXP_STAT, REGS_PXP_BASE, 0x00000010)
+#define HW_PXP_STAT_ADDR (REGS_PXP_BASE + 0x00000010)
+#define BP_PXP_STAT_BLOCKX 24
+#define BM_PXP_STAT_BLOCKX 0xFF000000
+#define BF_PXP_STAT_BLOCKX(v) \
+ (((v) << 24) & BM_PXP_STAT_BLOCKX)
+#define BP_PXP_STAT_BLOCKY 16
+#define BM_PXP_STAT_BLOCKY 0x00FF0000
+#define BF_PXP_STAT_BLOCKY(v) \
+ (((v) << 16) & BM_PXP_STAT_BLOCKY)
+#define BP_PXP_STAT_AXI_ERROR_ID 4
+#define BM_PXP_STAT_AXI_ERROR_ID 0x000000F0
+#define BF_PXP_STAT_AXI_ERROR_ID(v) \
+ (((v) << 4) & BM_PXP_STAT_AXI_ERROR_ID)
+#define BM_PXP_STAT_AXI_READ_ERROR 0x00000004
+#define BM_PXP_STAT_AXI_WRITE_ERROR 0x00000002
+#define BM_PXP_STAT_IRQ 0x00000001
+HW_REGISTER_0(HW_PXP_RGBBUF, REGS_PXP_BASE, 0x00000020)
+#define HW_PXP_RGBBUF_ADDR (REGS_PXP_BASE + 0x00000020)
+#define BP_PXP_RGBBUF_ADDR 0
+#define BM_PXP_RGBBUF_ADDR 0xFFFFFFFF
+#define BF_PXP_RGBBUF_ADDR(v) (v)
+HW_REGISTER_0(HW_PXP_RGBBUF2, REGS_PXP_BASE, 0x00000030)
+#define HW_PXP_RGBBUF2_ADDR (REGS_PXP_BASE + 0x00000030)
+#define BP_PXP_RGBBUF2_ADDR 0
+#define BM_PXP_RGBBUF2_ADDR 0xFFFFFFFF
+#define BF_PXP_RGBBUF2_ADDR(v) (v)
+HW_REGISTER_0(HW_PXP_RGBSIZE, REGS_PXP_BASE, 0x00000040)
+#define HW_PXP_RGBSIZE_ADDR (REGS_PXP_BASE + 0x00000040)
+#define BP_PXP_RGBSIZE_ALPHA 24
+#define BM_PXP_RGBSIZE_ALPHA 0xFF000000
+#define BF_PXP_RGBSIZE_ALPHA(v) \
+ (((v) << 24) & BM_PXP_RGBSIZE_ALPHA)
+#define BP_PXP_RGBSIZE_WIDTH 12
+#define BM_PXP_RGBSIZE_WIDTH 0x00FFF000
+#define BF_PXP_RGBSIZE_WIDTH(v) \
+ (((v) << 12) & BM_PXP_RGBSIZE_WIDTH)
+#define BP_PXP_RGBSIZE_HEIGHT 0
+#define BM_PXP_RGBSIZE_HEIGHT 0x00000FFF
+#define BF_PXP_RGBSIZE_HEIGHT(v) \
+ (((v) << 0) & BM_PXP_RGBSIZE_HEIGHT)
+HW_REGISTER_0(HW_PXP_S0BUF, REGS_PXP_BASE, 0x00000050)
+#define HW_PXP_S0BUF_ADDR (REGS_PXP_BASE + 0x00000050)
+#define BP_PXP_S0BUF_ADDR 0
+#define BM_PXP_S0BUF_ADDR 0xFFFFFFFF
+#define BF_PXP_S0BUF_ADDR(v) (v)
+HW_REGISTER_0(HW_PXP_S0UBUF, REGS_PXP_BASE, 0x00000060)
+#define HW_PXP_S0UBUF_ADDR (REGS_PXP_BASE + 0x00000060)
+#define BP_PXP_S0UBUF_ADDR 0
+#define BM_PXP_S0UBUF_ADDR 0xFFFFFFFF
+#define BF_PXP_S0UBUF_ADDR(v) (v)
+HW_REGISTER_0(HW_PXP_S0VBUF, REGS_PXP_BASE, 0x00000070)
+#define HW_PXP_S0VBUF_ADDR (REGS_PXP_BASE + 0x00000070)
+#define BP_PXP_S0VBUF_ADDR 0
+#define BM_PXP_S0VBUF_ADDR 0xFFFFFFFF
+#define BF_PXP_S0VBUF_ADDR(v) (v)
+HW_REGISTER_0(HW_PXP_S0PARAM, REGS_PXP_BASE, 0x00000080)
+#define HW_PXP_S0PARAM_ADDR (REGS_PXP_BASE + 0x00000080)
+#define BP_PXP_S0PARAM_XBASE 24
+#define BM_PXP_S0PARAM_XBASE 0xFF000000
+#define BF_PXP_S0PARAM_XBASE(v) \
+ (((v) << 24) & BM_PXP_S0PARAM_XBASE)
+#define BP_PXP_S0PARAM_YBASE 16
+#define BM_PXP_S0PARAM_YBASE 0x00FF0000
+#define BF_PXP_S0PARAM_YBASE(v) \
+ (((v) << 16) & BM_PXP_S0PARAM_YBASE)
+#define BP_PXP_S0PARAM_WIDTH 8
+#define BM_PXP_S0PARAM_WIDTH 0x0000FF00
+#define BF_PXP_S0PARAM_WIDTH(v) \
+ (((v) << 8) & BM_PXP_S0PARAM_WIDTH)
+#define BP_PXP_S0PARAM_HEIGHT 0
+#define BM_PXP_S0PARAM_HEIGHT 0x000000FF
+#define BF_PXP_S0PARAM_HEIGHT(v) \
+ (((v) << 0) & BM_PXP_S0PARAM_HEIGHT)
+HW_REGISTER_0(HW_PXP_S0BACKGROUND, REGS_PXP_BASE, 0x00000090)
+#define HW_PXP_S0BACKGROUND_ADDR (REGS_PXP_BASE + 0x00000090)
+#define BP_PXP_S0BACKGROUND_COLOR 0
+#define BM_PXP_S0BACKGROUND_COLOR 0xFFFFFFFF
+#define BF_PXP_S0BACKGROUND_COLOR(v) (v)
+HW_REGISTER_0(HW_PXP_S0CROP, REGS_PXP_BASE, 0x000000a0)
+#define HW_PXP_S0CROP_ADDR (REGS_PXP_BASE + 0x000000a0)
+#define BP_PXP_S0CROP_XBASE 24
+#define BM_PXP_S0CROP_XBASE 0xFF000000
+#define BF_PXP_S0CROP_XBASE(v) \
+ (((v) << 24) & BM_PXP_S0CROP_XBASE)
+#define BP_PXP_S0CROP_YBASE 16
+#define BM_PXP_S0CROP_YBASE 0x00FF0000
+#define BF_PXP_S0CROP_YBASE(v) \
+ (((v) << 16) & BM_PXP_S0CROP_YBASE)
+#define BP_PXP_S0CROP_WIDTH 8
+#define BM_PXP_S0CROP_WIDTH 0x0000FF00
+#define BF_PXP_S0CROP_WIDTH(v) \
+ (((v) << 8) & BM_PXP_S0CROP_WIDTH)
+#define BP_PXP_S0CROP_HEIGHT 0
+#define BM_PXP_S0CROP_HEIGHT 0x000000FF
+#define BF_PXP_S0CROP_HEIGHT(v) \
+ (((v) << 0) & BM_PXP_S0CROP_HEIGHT)
+HW_REGISTER_0(HW_PXP_S0SCALE, REGS_PXP_BASE, 0x000000b0)
+#define HW_PXP_S0SCALE_ADDR (REGS_PXP_BASE + 0x000000b0)
+#define BP_PXP_S0SCALE_YSCALE 16
+#define BM_PXP_S0SCALE_YSCALE 0x3FFF0000
+#define BF_PXP_S0SCALE_YSCALE(v) \
+ (((v) << 16) & BM_PXP_S0SCALE_YSCALE)
+#define BP_PXP_S0SCALE_XSCALE 0
+#define BM_PXP_S0SCALE_XSCALE 0x00003FFF
+#define BF_PXP_S0SCALE_XSCALE(v) \
+ (((v) << 0) & BM_PXP_S0SCALE_XSCALE)
+HW_REGISTER_0(HW_PXP_S0OFFSET, REGS_PXP_BASE, 0x000000c0)
+#define HW_PXP_S0OFFSET_ADDR (REGS_PXP_BASE + 0x000000c0)
+#define BP_PXP_S0OFFSET_YOFFSET 16
+#define BM_PXP_S0OFFSET_YOFFSET 0x0FFF0000
+#define BF_PXP_S0OFFSET_YOFFSET(v) \
+ (((v) << 16) & BM_PXP_S0OFFSET_YOFFSET)
+#define BP_PXP_S0OFFSET_XOFFSET 0
+#define BM_PXP_S0OFFSET_XOFFSET 0x00000FFF
+#define BF_PXP_S0OFFSET_XOFFSET(v) \
+ (((v) << 0) & BM_PXP_S0OFFSET_XOFFSET)
+HW_REGISTER_0(HW_PXP_CSCCOEFF0, REGS_PXP_BASE, 0x000000d0)
+#define HW_PXP_CSCCOEFF0_ADDR (REGS_PXP_BASE + 0x000000d0)
+#define BM_PXP_CSCCOEFF0_YCBCR_MODE 0x80000000
+#define BP_PXP_CSCCOEFF0_C0 18
+#define BM_PXP_CSCCOEFF0_C0 0x1FFC0000
+#define BF_PXP_CSCCOEFF0_C0(v) \
+ (((v) << 18) & BM_PXP_CSCCOEFF0_C0)
+#define BP_PXP_CSCCOEFF0_UV_OFFSET 9
+#define BM_PXP_CSCCOEFF0_UV_OFFSET 0x0003FE00
+#define BF_PXP_CSCCOEFF0_UV_OFFSET(v) \
+ (((v) << 9) & BM_PXP_CSCCOEFF0_UV_OFFSET)
+#define BP_PXP_CSCCOEFF0_Y_OFFSET 0
+#define BM_PXP_CSCCOEFF0_Y_OFFSET 0x000001FF
+#define BF_PXP_CSCCOEFF0_Y_OFFSET(v) \
+ (((v) << 0) & BM_PXP_CSCCOEFF0_Y_OFFSET)
+HW_REGISTER_0(HW_PXP_CSCCOEFF1, REGS_PXP_BASE, 0x000000e0)
+#define HW_PXP_CSCCOEFF1_ADDR (REGS_PXP_BASE + 0x000000e0)
+#define BP_PXP_CSCCOEFF1_C1 16
+#define BM_PXP_CSCCOEFF1_C1 0x07FF0000
+#define BF_PXP_CSCCOEFF1_C1(v) \
+ (((v) << 16) & BM_PXP_CSCCOEFF1_C1)
+#define BP_PXP_CSCCOEFF1_C4 0
+#define BM_PXP_CSCCOEFF1_C4 0x000007FF
+#define BF_PXP_CSCCOEFF1_C4(v) \
+ (((v) << 0) & BM_PXP_CSCCOEFF1_C4)
+HW_REGISTER_0(HW_PXP_CSCCOEFF2, REGS_PXP_BASE, 0x000000f0)
+#define HW_PXP_CSCCOEFF2_ADDR (REGS_PXP_BASE + 0x000000f0)
+#define BP_PXP_CSCCOEFF2_C2 16
+#define BM_PXP_CSCCOEFF2_C2 0x07FF0000
+#define BF_PXP_CSCCOEFF2_C2(v) \
+ (((v) << 16) & BM_PXP_CSCCOEFF2_C2)
+#define BP_PXP_CSCCOEFF2_C3 0
+#define BM_PXP_CSCCOEFF2_C3 0x000007FF
+#define BF_PXP_CSCCOEFF2_C3(v) \
+ (((v) << 0) & BM_PXP_CSCCOEFF2_C3)
+HW_REGISTER(HW_PXP_NEXT, REGS_PXP_BASE, 0x00000100)
+#define HW_PXP_NEXT_ADDR (REGS_PXP_BASE + 0x00000100)
+#define BP_PXP_NEXT_POINTER 2
+#define BM_PXP_NEXT_POINTER 0xFFFFFFFC
+#define BF_PXP_NEXT_POINTER(v) \
+ (((v) << 2) & BM_PXP_NEXT_POINTER)
+#define BM_PXP_NEXT_ENABLED 0x00000001
+HW_REGISTER_0(HW_PXP_PAGETABLE, REGS_PXP_BASE, 0x00000170)
+#define HW_PXP_PAGETABLE_ADDR (REGS_PXP_BASE + 0x00000170)
+#define BP_PXP_PAGETABLE_BASE 14
+#define BM_PXP_PAGETABLE_BASE 0xFFFFC000
+#define BF_PXP_PAGETABLE_BASE(v) \
+ (((v) << 14) & BM_PXP_PAGETABLE_BASE)
+#define BM_PXP_PAGETABLE_FLUSH 0x00000002
+#define BM_PXP_PAGETABLE_ENABLE 0x00000001
+HW_REGISTER_0(HW_PXP_S0COLORKEYLOW, REGS_PXP_BASE, 0x00000180)
+#define HW_PXP_S0COLORKEYLOW_ADDR (REGS_PXP_BASE + 0x00000180)
+#define BP_PXP_S0COLORKEYLOW_PIXEL 0
+#define BM_PXP_S0COLORKEYLOW_PIXEL 0x00FFFFFF
+#define BF_PXP_S0COLORKEYLOW_PIXEL(v) \
+ (((v) << 0) & BM_PXP_S0COLORKEYLOW_PIXEL)
+HW_REGISTER_0(HW_PXP_S0COLORKEYHIGH, REGS_PXP_BASE, 0x00000190)
+#define HW_PXP_S0COLORKEYHIGH_ADDR (REGS_PXP_BASE + 0x00000190)
+#define BP_PXP_S0COLORKEYHIGH_PIXEL 0
+#define BM_PXP_S0COLORKEYHIGH_PIXEL 0x00FFFFFF
+#define BF_PXP_S0COLORKEYHIGH_PIXEL(v) \
+ (((v) << 0) & BM_PXP_S0COLORKEYHIGH_PIXEL)
+HW_REGISTER_0(HW_PXP_OLCOLORKEYLOW, REGS_PXP_BASE, 0x000001a0)
+#define HW_PXP_OLCOLORKEYLOW_ADDR (REGS_PXP_BASE + 0x000001a0)
+#define BP_PXP_OLCOLORKEYLOW_PIXEL 0
+#define BM_PXP_OLCOLORKEYLOW_PIXEL 0x00FFFFFF
+#define BF_PXP_OLCOLORKEYLOW_PIXEL(v) \
+ (((v) << 0) & BM_PXP_OLCOLORKEYLOW_PIXEL)
+HW_REGISTER_0(HW_PXP_OLCOLORKEYHIGH, REGS_PXP_BASE, 0x000001b0)
+#define HW_PXP_OLCOLORKEYHIGH_ADDR (REGS_PXP_BASE + 0x000001b0)
+#define BP_PXP_OLCOLORKEYHIGH_PIXEL 0
+#define BM_PXP_OLCOLORKEYHIGH_PIXEL 0x00FFFFFF
+#define BF_PXP_OLCOLORKEYHIGH_PIXEL(v) \
+ (((v) << 0) & BM_PXP_OLCOLORKEYHIGH_PIXEL)
+HW_REGISTER_0(HW_PXP_DEBUGCTRL, REGS_PXP_BASE, 0x000001d0)
+#define HW_PXP_DEBUGCTRL_ADDR (REGS_PXP_BASE + 0x000001d0)
+#define BM_PXP_DEBUGCTRL_RESET_TLB_STATS 0x00000100
+#define BP_PXP_DEBUGCTRL_SELECT 0
+#define BM_PXP_DEBUGCTRL_SELECT 0x000000FF
+#define BF_PXP_DEBUGCTRL_SELECT(v) \
+ (((v) << 0) & BM_PXP_DEBUGCTRL_SELECT)
+#define BV_PXP_DEBUGCTRL_SELECT__NONE 0x0
+#define BV_PXP_DEBUGCTRL_SELECT__CTRL 0x1
+#define BV_PXP_DEBUGCTRL_SELECT__S0REGS 0x2
+#define BV_PXP_DEBUGCTRL_SELECT__S0BAX 0x3
+#define BV_PXP_DEBUGCTRL_SELECT__S0BAY 0x4
+#define BV_PXP_DEBUGCTRL_SELECT__PXBUF 0x5
+#define BV_PXP_DEBUGCTRL_SELECT__ROTATION 0x6
+#define BV_PXP_DEBUGCTRL_SELECT__ROTBUF0 0x7
+#define BV_PXP_DEBUGCTRL_SELECT__ROTBUF1 0x8
+#define BV_PXP_DEBUGCTRL_SELECT__TLBCOUNT 0xF0
+#define BV_PXP_DEBUGCTRL_SELECT__TLBHIT 0xF1
+#define BV_PXP_DEBUGCTRL_SELECT__TLBMISS 0xF2
+#define BV_PXP_DEBUGCTRL_SELECT__TLBLAT 0xF3
+#define BV_PXP_DEBUGCTRL_SELECT__TLBSTATE 0xF8
+HW_REGISTER_0(HW_PXP_DEBUG, REGS_PXP_BASE, 0x000001e0)
+#define HW_PXP_DEBUG_ADDR (REGS_PXP_BASE + 0x000001e0)
+#define BP_PXP_DEBUG_DATA 0
+#define BM_PXP_DEBUG_DATA 0xFFFFFFFF
+#define BF_PXP_DEBUG_DATA(v) (v)
+HW_REGISTER_0(HW_PXP_VERSION, REGS_PXP_BASE, 0x000001f0)
+#define HW_PXP_VERSION_ADDR (REGS_PXP_BASE + 0x000001f0)
+#define BP_PXP_VERSION_MAJOR 24
+#define BM_PXP_VERSION_MAJOR 0xFF000000
+#define BF_PXP_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_PXP_VERSION_MAJOR)
+#define BP_PXP_VERSION_MINOR 16
+#define BM_PXP_VERSION_MINOR 0x00FF0000
+#define BF_PXP_VERSION_MINOR(v) \
+ (((v) << 16) & BM_PXP_VERSION_MINOR)
+#define BP_PXP_VERSION_STEP 0
+#define BM_PXP_VERSION_STEP 0x0000FFFF
+#define BF_PXP_VERSION_STEP(v) \
+ (((v) << 0) & BM_PXP_VERSION_STEP)
+
+#define HW_PXP_OL0_ADDR (REGS_PXP_BASE + 0x00000200)
+/*
+ * multi-register-define name HW_PXP_OLn
+ * base 0x00000200
+ * count 8
+ * offset 0x40
+ */
+HW_REGISTER_0_INDEXED(HW_PXP_OLn, REGS_PXP_BASE, 0x00000200, 0x40)
+#define BP_PXP_OLn_ADDR 0
+#define BM_PXP_OLn_ADDR 0xFFFFFFFF
+#define BF_PXP_OLn_ADDR(v) (v)
+/*
+ * multi-register-define name HW_PXP_OLnSIZE
+ * base 0x00000210
+ * count 8
+ * offset 0x40
+ */
+HW_REGISTER_0_INDEXED(HW_PXP_OLnSIZE, REGS_PXP_BASE, 0x00000210, 0x40)
+#define BP_PXP_OLnSIZE_XBASE 24
+#define BM_PXP_OLnSIZE_XBASE 0xFF000000
+#define BF_PXP_OLnSIZE_XBASE(v) \
+ (((v) << 24) & BM_PXP_OLnSIZE_XBASE)
+#define BP_PXP_OLnSIZE_YBASE 16
+#define BM_PXP_OLnSIZE_YBASE 0x00FF0000
+#define BF_PXP_OLnSIZE_YBASE(v) \
+ (((v) << 16) & BM_PXP_OLnSIZE_YBASE)
+#define BP_PXP_OLnSIZE_WIDTH 8
+#define BM_PXP_OLnSIZE_WIDTH 0x0000FF00
+#define BF_PXP_OLnSIZE_WIDTH(v) \
+ (((v) << 8) & BM_PXP_OLnSIZE_WIDTH)
+#define BP_PXP_OLnSIZE_HEIGHT 0
+#define BM_PXP_OLnSIZE_HEIGHT 0x000000FF
+#define BF_PXP_OLnSIZE_HEIGHT(v) \
+ (((v) << 0) & BM_PXP_OLnSIZE_HEIGHT)
+/*
+ * multi-register-define name HW_PXP_OLnPARAM
+ * base 0x00000220
+ * count 8
+ * offset 0x40
+ */
+HW_REGISTER_0_INDEXED(HW_PXP_OLnPARAM, REGS_PXP_BASE, 0x00000220, 0x40)
+#define BP_PXP_OLnPARAM_ROP 16
+#define BM_PXP_OLnPARAM_ROP 0x000F0000
+#define BF_PXP_OLnPARAM_ROP(v) \
+ (((v) << 16) & BM_PXP_OLnPARAM_ROP)
+#define BV_PXP_OLnPARAM_ROP__MASKOL 0x0
+#define BV_PXP_OLnPARAM_ROP__MASKNOTOL 0x1
+#define BV_PXP_OLnPARAM_ROP__MASKOLNOT 0x2
+#define BV_PXP_OLnPARAM_ROP__MERGEOL 0x3
+#define BV_PXP_OLnPARAM_ROP__MERGENOTOL 0x4
+#define BV_PXP_OLnPARAM_ROP__MERGEOLNOT 0x5
+#define BV_PXP_OLnPARAM_ROP__NOTCOPYOL 0x6
+#define BV_PXP_OLnPARAM_ROP__NOT 0x7
+#define BV_PXP_OLnPARAM_ROP__NOTMASKOL 0x8
+#define BV_PXP_OLnPARAM_ROP__NOTMERGEOL 0x9
+#define BV_PXP_OLnPARAM_ROP__XOROL 0xA
+#define BV_PXP_OLnPARAM_ROP__NOTXOROL 0xB
+#define BP_PXP_OLnPARAM_ALPHA 8
+#define BM_PXP_OLnPARAM_ALPHA 0x0000FF00
+#define BF_PXP_OLnPARAM_ALPHA(v) \
+ (((v) << 8) & BM_PXP_OLnPARAM_ALPHA)
+#define BP_PXP_OLnPARAM_FORMAT 4
+#define BM_PXP_OLnPARAM_FORMAT 0x000000F0
+#define BF_PXP_OLnPARAM_FORMAT(v) \
+ (((v) << 4) & BM_PXP_OLnPARAM_FORMAT)
+#define BV_PXP_OLnPARAM_FORMAT__ARGB8888 0x0
+#define BV_PXP_OLnPARAM_FORMAT__RGB888 0x1
+#define BV_PXP_OLnPARAM_FORMAT__ARGB1555 0x3
+#define BV_PXP_OLnPARAM_FORMAT__RGB565 0x4
+#define BV_PXP_OLnPARAM_FORMAT__RGB555 0x5
+#define BM_PXP_OLnPARAM_ENABLE_COLORKEY 0x00000008
+#define BP_PXP_OLnPARAM_ALPHA_CNTL 1
+#define BM_PXP_OLnPARAM_ALPHA_CNTL 0x00000006
+#define BF_PXP_OLnPARAM_ALPHA_CNTL(v) \
+ (((v) << 1) & BM_PXP_OLnPARAM_ALPHA_CNTL)
+#define BV_PXP_OLnPARAM_ALPHA_CNTL__Embedded 0x0
+#define BV_PXP_OLnPARAM_ALPHA_CNTL__Override 0x1
+#define BV_PXP_OLnPARAM_ALPHA_CNTL__Multiply 0x2
+#define BV_PXP_OLnPARAM_ALPHA_CNTL__ROPs 0x3
+#define BM_PXP_OLnPARAM_ENABLE 0x00000001
+/*
+ * multi-register-define name HW_PXP_OLnPARAM2
+ * base 0x00000230
+ * count 8
+ * offset 0x40
+ */
+HW_REGISTER_0_INDEXED(HW_PXP_OLnPARAM2, REGS_PXP_BASE, 0x00000230, 0x40)
+#endif /* __ARCH_ARM___PXP_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-rtc.h b/arch/arm/mach-stmp3xxx/include/mach/regs-rtc.h
new file mode 100644
index 000000000000..6ee096587538
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-rtc.h
@@ -0,0 +1,149 @@
+/*
+ * STMP RTC Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___RTC_H
+#define __ARCH_ARM___RTC_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_RTC_BASE (REGS_BASE + 0x5c000)
+#define REGS_RTC_BASE_PHYS (0x8005C000)
+#define REGS_RTC_SIZE 0x00002000
+HW_REGISTER(HW_RTC_CTRL, REGS_RTC_BASE, 0x00000000)
+#define HW_RTC_CTRL_ADDR (REGS_RTC_BASE + 0x00000000)
+#define BM_RTC_CTRL_SFTRST 0x80000000
+#define BM_RTC_CTRL_CLKGATE 0x40000000
+#define BM_RTC_CTRL_SUPPRESS_COPY2ANALOG 0x00000040
+#define BM_RTC_CTRL_FORCE_UPDATE 0x00000020
+#define BM_RTC_CTRL_WATCHDOGEN 0x00000010
+#define BM_RTC_CTRL_ONEMSEC_IRQ 0x00000008
+#define BM_RTC_CTRL_ALARM_IRQ 0x00000004
+#define BM_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002
+#define BM_RTC_CTRL_ALARM_IRQ_EN 0x00000001
+HW_REGISTER(HW_RTC_STAT, REGS_RTC_BASE, 0x00000010)
+#define HW_RTC_STAT_ADDR (REGS_RTC_BASE + 0x00000010)
+#define BM_RTC_STAT_RTC_PRESENT 0x80000000
+#define BM_RTC_STAT_ALARM_PRESENT 0x40000000
+#define BM_RTC_STAT_WATCHDOG_PRESENT 0x20000000
+#define BM_RTC_STAT_XTAL32000_PRESENT 0x10000000
+#define BM_RTC_STAT_XTAL32768_PRESENT 0x08000000
+#define BP_RTC_STAT_STALE_REGS 16
+#define BM_RTC_STAT_STALE_REGS 0x00FF0000
+#define BF_RTC_STAT_STALE_REGS(v) \
+ (((v) << 16) & BM_RTC_STAT_STALE_REGS)
+#define BP_RTC_STAT_NEW_REGS 8
+#define BM_RTC_STAT_NEW_REGS 0x0000FF00
+#define BF_RTC_STAT_NEW_REGS(v) \
+ (((v) << 8) & BM_RTC_STAT_NEW_REGS)
+HW_REGISTER(HW_RTC_MILLISECONDS, REGS_RTC_BASE, 0x00000020)
+#define HW_RTC_MILLISECONDS_ADDR (REGS_RTC_BASE + 0x00000020)
+#define BP_RTC_MILLISECONDS_COUNT 0
+#define BM_RTC_MILLISECONDS_COUNT 0xFFFFFFFF
+#define BF_RTC_MILLISECONDS_COUNT(v) (v)
+HW_REGISTER(HW_RTC_SECONDS, REGS_RTC_BASE, 0x00000030)
+#define HW_RTC_SECONDS_ADDR (REGS_RTC_BASE + 0x00000030)
+#define BP_RTC_SECONDS_COUNT 0
+#define BM_RTC_SECONDS_COUNT 0xFFFFFFFF
+#define BF_RTC_SECONDS_COUNT(v) (v)
+HW_REGISTER(HW_RTC_ALARM, REGS_RTC_BASE, 0x00000040)
+#define HW_RTC_ALARM_ADDR (REGS_RTC_BASE + 0x00000040)
+#define BP_RTC_ALARM_VALUE 0
+#define BM_RTC_ALARM_VALUE 0xFFFFFFFF
+#define BF_RTC_ALARM_VALUE(v) (v)
+HW_REGISTER(HW_RTC_WATCHDOG, REGS_RTC_BASE, 0x00000050)
+#define HW_RTC_WATCHDOG_ADDR (REGS_RTC_BASE + 0x00000050)
+#define BP_RTC_WATCHDOG_COUNT 0
+#define BM_RTC_WATCHDOG_COUNT 0xFFFFFFFF
+#define BF_RTC_WATCHDOG_COUNT(v) (v)
+HW_REGISTER(HW_RTC_PERSISTENT0, REGS_RTC_BASE, 0x00000060)
+#define HW_RTC_PERSISTENT0_ADDR (REGS_RTC_BASE + 0x00000060)
+#define BP_RTC_PERSISTENT0_SPARE_ANALOG 18
+#define BM_RTC_PERSISTENT0_SPARE_ANALOG 0xFFFC0000
+#define BF_RTC_PERSISTENT0_SPARE_ANALOG(v) \
+ (((v) << 18) & BM_RTC_PERSISTENT0_SPARE_ANALOG)
+#define BM_RTC_PERSISTENT0_AUTO_RESTART 0x00020000
+#define BM_RTC_PERSISTENT0_DISABLE_PSWITCH 0x00010000
+#define BP_RTC_PERSISTENT0_LOWERBIAS 14
+#define BM_RTC_PERSISTENT0_LOWERBIAS 0x0000C000
+#define BF_RTC_PERSISTENT0_LOWERBIAS(v) \
+ (((v) << 14) & BM_RTC_PERSISTENT0_LOWERBIAS)
+#define BM_RTC_PERSISTENT0_DISABLE_XTALOK 0x00002000
+#define BP_RTC_PERSISTENT0_MSEC_RES 8
+#define BM_RTC_PERSISTENT0_MSEC_RES 0x00001F00
+#define BF_RTC_PERSISTENT0_MSEC_RES(v) \
+ (((v) << 8) & BM_RTC_PERSISTENT0_MSEC_RES)
+#define BM_RTC_PERSISTENT0_ALARM_WAKE 0x00000080
+#define BM_RTC_PERSISTENT0_XTAL32_FREQ 0x00000040
+#define BM_RTC_PERSISTENT0_XTAL32KHZ_PWRUP 0x00000020
+#define BM_RTC_PERSISTENT0_XTAL24MHZ_PWRUP 0x00000010
+#define BM_RTC_PERSISTENT0_LCK_SECS 0x00000008
+#define BM_RTC_PERSISTENT0_ALARM_EN 0x00000004
+#define BM_RTC_PERSISTENT0_ALARM_WAKE_EN 0x00000002
+#define BM_RTC_PERSISTENT0_CLOCKSOURCE 0x00000001
+HW_REGISTER(HW_RTC_PERSISTENT1, REGS_RTC_BASE, 0x00000070)
+#define HW_RTC_PERSISTENT1_ADDR (REGS_RTC_BASE + 0x00000070)
+#define BP_RTC_PERSISTENT1_GENERAL 0
+#define BM_RTC_PERSISTENT1_GENERAL 0xFFFFFFFF
+#define BF_RTC_PERSISTENT1_GENERAL(v) (v)
+#define BV_RTC_PERSISTENT1_GENERAL__ENUMERATE_500MA_TWICE 0x1000
+#define BV_RTC_PERSISTENT1_GENERAL__USB_BOOT_PLAYER_MODE 0x0800
+#define BV_RTC_PERSISTENT1_GENERAL__SKIP_CHECKDISK 0x0400
+#define BV_RTC_PERSISTENT1_GENERAL__USB_LOW_POWER_MODE 0x0200
+#define BV_RTC_PERSISTENT1_GENERAL__OTG_HNP_BIT 0x0100
+#define BV_RTC_PERSISTENT1_GENERAL__OTG_ATL_ROLE_BIT 0x0080
+HW_REGISTER(HW_RTC_PERSISTENT2, REGS_RTC_BASE, 0x00000080)
+#define HW_RTC_PERSISTENT2_ADDR (REGS_RTC_BASE + 0x00000080)
+#define BP_RTC_PERSISTENT2_GENERAL 0
+#define BM_RTC_PERSISTENT2_GENERAL 0xFFFFFFFF
+#define BF_RTC_PERSISTENT2_GENERAL(v) (v)
+HW_REGISTER(HW_RTC_PERSISTENT3, REGS_RTC_BASE, 0x00000090)
+#define HW_RTC_PERSISTENT3_ADDR (REGS_RTC_BASE + 0x00000090)
+#define BP_RTC_PERSISTENT3_GENERAL 0
+#define BM_RTC_PERSISTENT3_GENERAL 0xFFFFFFFF
+#define BF_RTC_PERSISTENT3_GENERAL(v) (v)
+HW_REGISTER(HW_RTC_PERSISTENT4, REGS_RTC_BASE, 0x000000a0)
+#define HW_RTC_PERSISTENT4_ADDR (REGS_RTC_BASE + 0x000000a0)
+#define BP_RTC_PERSISTENT4_GENERAL 0
+#define BM_RTC_PERSISTENT4_GENERAL 0xFFFFFFFF
+#define BF_RTC_PERSISTENT4_GENERAL(v) (v)
+HW_REGISTER(HW_RTC_PERSISTENT5, REGS_RTC_BASE, 0x000000b0)
+#define HW_RTC_PERSISTENT5_ADDR (REGS_RTC_BASE + 0x000000b0)
+#define BP_RTC_PERSISTENT5_GENERAL 0
+#define BM_RTC_PERSISTENT5_GENERAL 0xFFFFFFFF
+#define BF_RTC_PERSISTENT5_GENERAL(v) (v)
+HW_REGISTER(HW_RTC_DEBUG, REGS_RTC_BASE, 0x000000c0)
+#define HW_RTC_DEBUG_ADDR (REGS_RTC_BASE + 0x000000c0)
+#define BM_RTC_DEBUG_WATCHDOG_RESET_MASK 0x00000002
+#define BM_RTC_DEBUG_WATCHDOG_RESET 0x00000001
+HW_REGISTER_0(HW_RTC_VERSION, REGS_RTC_BASE, 0x000000d0)
+#define HW_RTC_VERSION_ADDR (REGS_RTC_BASE + 0x000000d0)
+#define BP_RTC_VERSION_MAJOR 24
+#define BM_RTC_VERSION_MAJOR 0xFF000000
+#define BF_RTC_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_RTC_VERSION_MAJOR)
+#define BP_RTC_VERSION_MINOR 16
+#define BM_RTC_VERSION_MINOR 0x00FF0000
+#define BF_RTC_VERSION_MINOR(v) \
+ (((v) << 16) & BM_RTC_VERSION_MINOR)
+#define BP_RTC_VERSION_STEP 0
+#define BM_RTC_VERSION_STEP 0x0000FFFF
+#define BF_RTC_VERSION_STEP(v) \
+ (((v) << 0) & BM_RTC_VERSION_STEP)
+#endif /* __ARCH_ARM___RTC_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-saif.h b/arch/arm/mach-stmp3xxx/include/mach/regs-saif.h
new file mode 100644
index 000000000000..494ea274c0c1
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-saif.h
@@ -0,0 +1,94 @@
+/*
+ * STMP SAIF Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___SAIF_H
+#define __ARCH_ARM___SAIF_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_SAIF_BASE (REGS_BASE + 0x42000)
+#define REGS_SAIF1_BASE_PHYS (0x80042000)
+#define REGS_SAIF2_BASE_PHYS (0x80046000)
+#define REGS_SAIF_SIZE 0x00002000
+HW_REGISTER(HW_SAIF_CTRL, REGS_SAIF_BASE, 0x00000000)
+#define HW_SAIF_CTRL_ADDR (REGS_SAIF_BASE + 0x00000000)
+#define BM_SAIF_CTRL_SFTRST 0x80000000
+#define BM_SAIF_CTRL_CLKGATE 0x40000000
+#define BP_SAIF_CTRL_BITCLK_MULT_RATE 27
+#define BM_SAIF_CTRL_BITCLK_MULT_RATE 0x38000000
+#define BF_SAIF_CTRL_BITCLK_MULT_RATE(v) \
+ (((v) << 27) & BM_SAIF_CTRL_BITCLK_MULT_RATE)
+#define BM_SAIF_CTRL_BITCLK_BASE_RATE 0x04000000
+#define BM_SAIF_CTRL_FIFO_ERROR_IRQ_EN 0x02000000
+#define BM_SAIF_CTRL_FIFO_SERVICE_IRQ_EN 0x01000000
+#define BP_SAIF_CTRL_DMAWAIT_COUNT 16
+#define BM_SAIF_CTRL_DMAWAIT_COUNT 0x001F0000
+#define BF_SAIF_CTRL_DMAWAIT_COUNT(v) \
+ (((v) << 16) & BM_SAIF_CTRL_DMAWAIT_COUNT)
+#define BP_SAIF_CTRL_CHANNEL_NUM_SELECT 14
+#define BM_SAIF_CTRL_CHANNEL_NUM_SELECT 0x0000C000
+#define BF_SAIF_CTRL_CHANNEL_NUM_SELECT(v) \
+ (((v) << 14) & BM_SAIF_CTRL_CHANNEL_NUM_SELECT)
+#define BM_SAIF_CTRL_BIT_ORDER 0x00001000
+#define BM_SAIF_CTRL_DELAY 0x00000800
+#define BM_SAIF_CTRL_JUSTIFY 0x00000400
+#define BM_SAIF_CTRL_LRCLK_POLARITY 0x00000200
+#define BM_SAIF_CTRL_BITCLK_EDGE 0x00000100
+#define BP_SAIF_CTRL_WORD_LENGTH 4
+#define BM_SAIF_CTRL_WORD_LENGTH 0x000000F0
+#define BF_SAIF_CTRL_WORD_LENGTH(v) \
+ (((v) << 4) & BM_SAIF_CTRL_WORD_LENGTH)
+#define BM_SAIF_CTRL_BITCLK_48XFS_ENABLE 0x00000008
+#define BM_SAIF_CTRL_SLAVE_MODE 0x00000004
+#define BM_SAIF_CTRL_READ_MODE 0x00000002
+#define BM_SAIF_CTRL_RUN 0x00000001
+HW_REGISTER(HW_SAIF_STAT, REGS_SAIF_BASE, 0x00000010)
+#define HW_SAIF_STAT_ADDR (REGS_SAIF_BASE + 0x00000010)
+#define BM_SAIF_STAT_PRESENT 0x80000000
+#define BM_SAIF_STAT_DMA_PREQ 0x00010000
+#define BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ 0x00000040
+#define BM_SAIF_STAT_FIFO_OVERFLOW_IRQ 0x00000020
+#define BM_SAIF_STAT_FIFO_SERVICE_IRQ 0x00000010
+#define BM_SAIF_STAT_BUSY 0x00000001
+HW_REGISTER(HW_SAIF_DATA, REGS_SAIF_BASE, 0x00000020)
+#define HW_SAIF_DATA_ADDR (REGS_SAIF_BASE + 0x00000020)
+#define BP_SAIF_DATA_PCM_RIGHT 16
+#define BM_SAIF_DATA_PCM_RIGHT 0xFFFF0000
+#define BF_SAIF_DATA_PCM_RIGHT(v) \
+ (((v) << 16) & BM_SAIF_DATA_PCM_RIGHT)
+#define BP_SAIF_DATA_PCM_LEFT 0
+#define BM_SAIF_DATA_PCM_LEFT 0x0000FFFF
+#define BF_SAIF_DATA_PCM_LEFT(v) \
+ (((v) << 0) & BM_SAIF_DATA_PCM_LEFT)
+HW_REGISTER_0(HW_SAIF_VERSION, REGS_SAIF_BASE, 0x00000030)
+#define HW_SAIF_VERSION_ADDR (REGS_SAIF_BASE + 0x00000030)
+#define BP_SAIF_VERSION_MAJOR 24
+#define BM_SAIF_VERSION_MAJOR 0xFF000000
+#define BF_SAIF_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_SAIF_VERSION_MAJOR)
+#define BP_SAIF_VERSION_MINOR 16
+#define BM_SAIF_VERSION_MINOR 0x00FF0000
+#define BF_SAIF_VERSION_MINOR(v) \
+ (((v) << 16) & BM_SAIF_VERSION_MINOR)
+#define BP_SAIF_VERSION_STEP 0
+#define BM_SAIF_VERSION_STEP 0x0000FFFF
+#define BF_SAIF_VERSION_STEP(v) \
+ (((v) << 0) & BM_SAIF_VERSION_STEP)
+#endif /* __ARCH_ARM___SAIF_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-spdif.h b/arch/arm/mach-stmp3xxx/include/mach/regs-spdif.h
new file mode 100644
index 000000000000..da03b86a01f2
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-spdif.h
@@ -0,0 +1,100 @@
+/*
+ * STMP SPDIF Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___SPDIF_H
+#define __ARCH_ARM___SPDIF_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_SPDIF_BASE (REGS_BASE + 0x54000)
+#define REGS_SPDIF_BASE_PHYS (0x80054000)
+#define REGS_SPDIF_SIZE 0x00002000
+HW_REGISTER(HW_SPDIF_CTRL, REGS_SPDIF_BASE, 0x00000000)
+#define HW_SPDIF_CTRL_ADDR (REGS_SPDIF_BASE + 0x00000000)
+#define BM_SPDIF_CTRL_SFTRST 0x80000000
+#define BM_SPDIF_CTRL_CLKGATE 0x40000000
+#define BP_SPDIF_CTRL_DMAWAIT_COUNT 16
+#define BM_SPDIF_CTRL_DMAWAIT_COUNT 0x001F0000
+#define BF_SPDIF_CTRL_DMAWAIT_COUNT(v) \
+ (((v) << 16) & BM_SPDIF_CTRL_DMAWAIT_COUNT)
+#define BM_SPDIF_CTRL_WAIT_END_XFER 0x00000020
+#define BM_SPDIF_CTRL_WORD_LENGTH 0x00000010
+#define BM_SPDIF_CTRL_FIFO_UNDERFLOW_IRQ 0x00000008
+#define BM_SPDIF_CTRL_FIFO_OVERFLOW_IRQ 0x00000004
+#define BM_SPDIF_CTRL_FIFO_ERROR_IRQ_EN 0x00000002
+#define BM_SPDIF_CTRL_RUN 0x00000001
+HW_REGISTER(HW_SPDIF_STAT, REGS_SPDIF_BASE, 0x00000010)
+#define HW_SPDIF_STAT_ADDR (REGS_SPDIF_BASE + 0x00000010)
+#define BM_SPDIF_STAT_PRESENT 0x80000000
+#define BM_SPDIF_STAT_END_XFER 0x00000001
+HW_REGISTER(HW_SPDIF_FRAMECTRL, REGS_SPDIF_BASE, 0x00000020)
+#define HW_SPDIF_FRAMECTRL_ADDR (REGS_SPDIF_BASE + 0x00000020)
+#define BM_SPDIF_FRAMECTRL_V_CONFIG 0x00020000
+#define BM_SPDIF_FRAMECTRL_AUTO_MUTE 0x00010000
+#define BM_SPDIF_FRAMECTRL_USER_DATA 0x00004000
+#define BM_SPDIF_FRAMECTRL_V 0x00002000
+#define BM_SPDIF_FRAMECTRL_L 0x00001000
+#define BP_SPDIF_FRAMECTRL_CC 4
+#define BM_SPDIF_FRAMECTRL_CC 0x000007F0
+#define BF_SPDIF_FRAMECTRL_CC(v) \
+ (((v) << 4) & BM_SPDIF_FRAMECTRL_CC)
+#define BM_SPDIF_FRAMECTRL_PRE 0x00000008
+#define BM_SPDIF_FRAMECTRL_COPY 0x00000004
+#define BM_SPDIF_FRAMECTRL_AUDIO 0x00000002
+#define BM_SPDIF_FRAMECTRL_PRO 0x00000001
+HW_REGISTER(HW_SPDIF_SRR, REGS_SPDIF_BASE, 0x00000030)
+#define HW_SPDIF_SRR_ADDR (REGS_SPDIF_BASE + 0x00000030)
+#define BP_SPDIF_SRR_BASEMULT 28
+#define BM_SPDIF_SRR_BASEMULT 0x70000000
+#define BF_SPDIF_SRR_BASEMULT(v) \
+ (((v) << 28) & BM_SPDIF_SRR_BASEMULT)
+#define BP_SPDIF_SRR_RATE 0
+#define BM_SPDIF_SRR_RATE 0x000FFFFF
+#define BF_SPDIF_SRR_RATE(v) \
+ (((v) << 0) & BM_SPDIF_SRR_RATE)
+HW_REGISTER(HW_SPDIF_DEBUG, REGS_SPDIF_BASE, 0x00000040)
+#define HW_SPDIF_DEBUG_ADDR (REGS_SPDIF_BASE + 0x00000040)
+#define BM_SPDIF_DEBUG_DMA_PREQ 0x00000002
+#define BM_SPDIF_DEBUG_FIFO_STATUS 0x00000001
+HW_REGISTER(HW_SPDIF_DATA, REGS_SPDIF_BASE, 0x00000050)
+#define HW_SPDIF_DATA_ADDR (REGS_SPDIF_BASE + 0x00000050)
+#define BP_SPDIF_DATA_HIGH 16
+#define BM_SPDIF_DATA_HIGH 0xFFFF0000
+#define BF_SPDIF_DATA_HIGH(v) \
+ (((v) << 16) & BM_SPDIF_DATA_HIGH)
+#define BP_SPDIF_DATA_LOW 0
+#define BM_SPDIF_DATA_LOW 0x0000FFFF
+#define BF_SPDIF_DATA_LOW(v) \
+ (((v) << 0) & BM_SPDIF_DATA_LOW)
+HW_REGISTER_0(HW_SPDIF_VERSION, REGS_SPDIF_BASE, 0x00000060)
+#define HW_SPDIF_VERSION_ADDR (REGS_SPDIF_BASE + 0x00000060)
+#define BP_SPDIF_VERSION_MAJOR 24
+#define BM_SPDIF_VERSION_MAJOR 0xFF000000
+#define BF_SPDIF_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_SPDIF_VERSION_MAJOR)
+#define BP_SPDIF_VERSION_MINOR 16
+#define BM_SPDIF_VERSION_MINOR 0x00FF0000
+#define BF_SPDIF_VERSION_MINOR(v) \
+ (((v) << 16) & BM_SPDIF_VERSION_MINOR)
+#define BP_SPDIF_VERSION_STEP 0
+#define BM_SPDIF_VERSION_STEP 0x0000FFFF
+#define BF_SPDIF_VERSION_STEP(v) \
+ (((v) << 0) & BM_SPDIF_VERSION_STEP)
+#endif /* __ARCH_ARM___SPDIF_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-ssp.h b/arch/arm/mach-stmp3xxx/include/mach/regs-ssp.h
new file mode 100644
index 000000000000..54b9beaebd7c
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-ssp.h
@@ -0,0 +1,355 @@
+/*
+ * STMP SSP Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___SSP_H
+#define __ARCH_ARM___SSP_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_SSP_BASE (REGS_BASE + 0x10000)
+#define REGS_SSP1_BASE_PHYS (0x80010000)
+#define REGS_SSP2_BASE_PHYS (0x80034000)
+#define REGS_SSP_SIZE 0x00002000
+HW_REGISTER(HW_SSP_CTRL0, REGS_SSP_BASE, 0x00000000)
+#define HW_SSP_CTRL0_ADDR (REGS_SSP_BASE + 0x00000000)
+#define BM_SSP_CTRL0_SFTRST 0x80000000
+#define BM_SSP_CTRL0_CLKGATE 0x40000000
+#define BM_SSP_CTRL0_RUN 0x20000000
+#define BM_SSP_CTRL0_SDIO_IRQ_CHECK 0x10000000
+#define BM_SSP_CTRL0_LOCK_CS 0x08000000
+#define BM_SSP_CTRL0_IGNORE_CRC 0x04000000
+#define BM_SSP_CTRL0_READ 0x02000000
+#define BM_SSP_CTRL0_DATA_XFER 0x01000000
+#define BP_SSP_CTRL0_BUS_WIDTH 22
+#define BM_SSP_CTRL0_BUS_WIDTH 0x00C00000
+#define BF_SSP_CTRL0_BUS_WIDTH(v) \
+ (((v) << 22) & BM_SSP_CTRL0_BUS_WIDTH)
+#define BV_SSP_CTRL0_BUS_WIDTH__ONE_BIT 0x0
+#define BV_SSP_CTRL0_BUS_WIDTH__FOUR_BIT 0x1
+#define BV_SSP_CTRL0_BUS_WIDTH__EIGHT_BIT 0x2
+#define BM_SSP_CTRL0_WAIT_FOR_IRQ 0x00200000
+#define BM_SSP_CTRL0_WAIT_FOR_CMD 0x00100000
+#define BM_SSP_CTRL0_LONG_RESP 0x00080000
+#define BM_SSP_CTRL0_CHECK_RESP 0x00040000
+#define BM_SSP_CTRL0_GET_RESP 0x00020000
+#define BM_SSP_CTRL0_ENABLE 0x00010000
+#define BP_SSP_CTRL0_XFER_COUNT 0
+#define BM_SSP_CTRL0_XFER_COUNT 0x0000FFFF
+#define BF_SSP_CTRL0_XFER_COUNT(v) \
+ (((v) << 0) & BM_SSP_CTRL0_XFER_COUNT)
+HW_REGISTER(HW_SSP_CMD0, REGS_SSP_BASE, 0x00000010)
+#define HW_SSP_CMD0_ADDR (REGS_SSP_BASE + 0x00000010)
+#define BM_SSP_CMD0_SLOW_CLKING_EN 0x00400000
+#define BM_SSP_CMD0_CONT_CLKING_EN 0x00200000
+#define BM_SSP_CMD0_APPEND_8CYC 0x00100000
+#define BP_SSP_CMD0_BLOCK_SIZE 16
+#define BM_SSP_CMD0_BLOCK_SIZE 0x000F0000
+#define BF_SSP_CMD0_BLOCK_SIZE(v) \
+ (((v) << 16) & BM_SSP_CMD0_BLOCK_SIZE)
+#define BP_SSP_CMD0_BLOCK_COUNT 8
+#define BM_SSP_CMD0_BLOCK_COUNT 0x0000FF00
+#define BF_SSP_CMD0_BLOCK_COUNT(v) \
+ (((v) << 8) & BM_SSP_CMD0_BLOCK_COUNT)
+#define BP_SSP_CMD0_CMD 0
+#define BM_SSP_CMD0_CMD 0x000000FF
+#define BF_SSP_CMD0_CMD(v) \
+ (((v) << 0) & BM_SSP_CMD0_CMD)
+#define BV_SSP_CMD0_CMD__MMC_GO_IDLE_STATE 0x00
+#define BV_SSP_CMD0_CMD__MMC_SEND_OP_COND 0x01
+#define BV_SSP_CMD0_CMD__MMC_ALL_SEND_CID 0x02
+#define BV_SSP_CMD0_CMD__MMC_SET_RELATIVE_ADDR 0x03
+#define BV_SSP_CMD0_CMD__MMC_SET_DSR 0x04
+#define BV_SSP_CMD0_CMD__MMC_RESERVED_5 0x05
+#define BV_SSP_CMD0_CMD__MMC_SWITCH 0x06
+#define BV_SSP_CMD0_CMD__MMC_SELECT_DESELECT_CARD 0x07
+#define BV_SSP_CMD0_CMD__MMC_SEND_EXT_CSD 0x08
+#define BV_SSP_CMD0_CMD__MMC_SEND_CSD 0x09
+#define BV_SSP_CMD0_CMD__MMC_SEND_CID 0x0A
+#define BV_SSP_CMD0_CMD__MMC_READ_DAT_UNTIL_STOP 0x0B
+#define BV_SSP_CMD0_CMD__MMC_STOP_TRANSMISSION 0x0C
+#define BV_SSP_CMD0_CMD__MMC_SEND_STATUS 0x0D
+#define BV_SSP_CMD0_CMD__MMC_BUSTEST_R 0x0E
+#define BV_SSP_CMD0_CMD__MMC_GO_INACTIVE_STATE 0x0F
+#define BV_SSP_CMD0_CMD__MMC_SET_BLOCKLEN 0x10
+#define BV_SSP_CMD0_CMD__MMC_READ_SINGLE_BLOCK 0x11
+#define BV_SSP_CMD0_CMD__MMC_READ_MULTIPLE_BLOCK 0x12
+#define BV_SSP_CMD0_CMD__MMC_BUSTEST_W 0x13
+#define BV_SSP_CMD0_CMD__MMC_WRITE_DAT_UNTIL_STOP 0x14
+#define BV_SSP_CMD0_CMD__MMC_SET_BLOCK_COUNT 0x17
+#define BV_SSP_CMD0_CMD__MMC_WRITE_BLOCK 0x18
+#define BV_SSP_CMD0_CMD__MMC_WRITE_MULTIPLE_BLOCK 0x19
+#define BV_SSP_CMD0_CMD__MMC_PROGRAM_CID 0x1A
+#define BV_SSP_CMD0_CMD__MMC_PROGRAM_CSD 0x1B
+#define BV_SSP_CMD0_CMD__MMC_SET_WRITE_PROT 0x1C
+#define BV_SSP_CMD0_CMD__MMC_CLR_WRITE_PROT 0x1D
+#define BV_SSP_CMD0_CMD__MMC_SEND_WRITE_PROT 0x1E
+#define BV_SSP_CMD0_CMD__MMC_ERASE_GROUP_START 0x23
+#define BV_SSP_CMD0_CMD__MMC_ERASE_GROUP_END 0x24
+#define BV_SSP_CMD0_CMD__MMC_ERASE 0x26
+#define BV_SSP_CMD0_CMD__MMC_FAST_IO 0x27
+#define BV_SSP_CMD0_CMD__MMC_GO_IRQ_STATE 0x28
+#define BV_SSP_CMD0_CMD__MMC_LOCK_UNLOCK 0x2A
+#define BV_SSP_CMD0_CMD__MMC_APP_CMD 0x37
+#define BV_SSP_CMD0_CMD__MMC_GEN_CMD 0x38
+#define BV_SSP_CMD0_CMD__SD_GO_IDLE_STATE 0x00
+#define BV_SSP_CMD0_CMD__SD_ALL_SEND_CID 0x02
+#define BV_SSP_CMD0_CMD__SD_SEND_RELATIVE_ADDR 0x03
+#define BV_SSP_CMD0_CMD__SD_SET_DSR 0x04
+#define BV_SSP_CMD0_CMD__SD_IO_SEND_OP_COND 0x05
+#define BV_SSP_CMD0_CMD__SD_SELECT_DESELECT_CARD 0x07
+#define BV_SSP_CMD0_CMD__SD_SEND_CSD 0x09
+#define BV_SSP_CMD0_CMD__SD_SEND_CID 0x0A
+#define BV_SSP_CMD0_CMD__SD_STOP_TRANSMISSION 0x0C
+#define BV_SSP_CMD0_CMD__SD_SEND_STATUS 0x0D
+#define BV_SSP_CMD0_CMD__SD_GO_INACTIVE_STATE 0x0F
+#define BV_SSP_CMD0_CMD__SD_SET_BLOCKLEN 0x10
+#define BV_SSP_CMD0_CMD__SD_READ_SINGLE_BLOCK 0x11
+#define BV_SSP_CMD0_CMD__SD_READ_MULTIPLE_BLOCK 0x12
+#define BV_SSP_CMD0_CMD__SD_WRITE_BLOCK 0x18
+#define BV_SSP_CMD0_CMD__SD_WRITE_MULTIPLE_BLOCK 0x19
+#define BV_SSP_CMD0_CMD__SD_PROGRAM_CSD 0x1B
+#define BV_SSP_CMD0_CMD__SD_SET_WRITE_PROT 0x1C
+#define BV_SSP_CMD0_CMD__SD_CLR_WRITE_PROT 0x1D
+#define BV_SSP_CMD0_CMD__SD_SEND_WRITE_PROT 0x1E
+#define BV_SSP_CMD0_CMD__SD_ERASE_WR_BLK_START 0x20
+#define BV_SSP_CMD0_CMD__SD_ERASE_WR_BLK_END 0x21
+#define BV_SSP_CMD0_CMD__SD_ERASE_GROUP_START 0x23
+#define BV_SSP_CMD0_CMD__SD_ERASE_GROUP_END 0x24
+#define BV_SSP_CMD0_CMD__SD_ERASE 0x26
+#define BV_SSP_CMD0_CMD__SD_LOCK_UNLOCK 0x2A
+#define BV_SSP_CMD0_CMD__SD_IO_RW_DIRECT 0x34
+#define BV_SSP_CMD0_CMD__SD_IO_RW_EXTENDED 0x35
+#define BV_SSP_CMD0_CMD__SD_APP_CMD 0x37
+#define BV_SSP_CMD0_CMD__SD_GEN_CMD 0x38
+HW_REGISTER_0(HW_SSP_CMD1, REGS_SSP_BASE, 0x00000020)
+#define HW_SSP_CMD1_ADDR (REGS_SSP_BASE + 0x00000020)
+#define BP_SSP_CMD1_CMD_ARG 0
+#define BM_SSP_CMD1_CMD_ARG 0xFFFFFFFF
+#define BF_SSP_CMD1_CMD_ARG(v) (v)
+HW_REGISTER_0(HW_SSP_COMPREF, REGS_SSP_BASE, 0x00000030)
+#define HW_SSP_COMPREF_ADDR (REGS_SSP_BASE + 0x00000030)
+#define BP_SSP_COMPREF_REFERENCE 0
+#define BM_SSP_COMPREF_REFERENCE 0xFFFFFFFF
+#define BF_SSP_COMPREF_REFERENCE(v) (v)
+HW_REGISTER_0(HW_SSP_COMPMASK, REGS_SSP_BASE, 0x00000040)
+#define HW_SSP_COMPMASK_ADDR (REGS_SSP_BASE + 0x00000040)
+#define BP_SSP_COMPMASK_MASK 0
+#define BM_SSP_COMPMASK_MASK 0xFFFFFFFF
+#define BF_SSP_COMPMASK_MASK(v) (v)
+HW_REGISTER_0(HW_SSP_TIMING, REGS_SSP_BASE, 0x00000050)
+#define HW_SSP_TIMING_ADDR (REGS_SSP_BASE + 0x00000050)
+#define BP_SSP_TIMING_TIMEOUT 16
+#define BM_SSP_TIMING_TIMEOUT 0xFFFF0000
+#define BF_SSP_TIMING_TIMEOUT(v) \
+ (((v) << 16) & BM_SSP_TIMING_TIMEOUT)
+#define BP_SSP_TIMING_CLOCK_DIVIDE 8
+#define BM_SSP_TIMING_CLOCK_DIVIDE 0x0000FF00
+#define BF_SSP_TIMING_CLOCK_DIVIDE(v) \
+ (((v) << 8) & BM_SSP_TIMING_CLOCK_DIVIDE)
+#define BP_SSP_TIMING_CLOCK_RATE 0
+#define BM_SSP_TIMING_CLOCK_RATE 0x000000FF
+#define BF_SSP_TIMING_CLOCK_RATE(v) \
+ (((v) << 0) & BM_SSP_TIMING_CLOCK_RATE)
+HW_REGISTER(HW_SSP_CTRL1, REGS_SSP_BASE, 0x00000060)
+#define HW_SSP_CTRL1_ADDR (REGS_SSP_BASE + 0x00000060)
+#define BM_SSP_CTRL1_SDIO_IRQ 0x80000000
+#define BM_SSP_CTRL1_SDIO_IRQ_EN 0x40000000
+#define BM_SSP_CTRL1_RESP_ERR_IRQ 0x20000000
+#define BM_SSP_CTRL1_RESP_ERR_IRQ_EN 0x10000000
+#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ 0x08000000
+#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN 0x04000000
+#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ 0x02000000
+#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN 0x01000000
+#define BM_SSP_CTRL1_DATA_CRC_IRQ 0x00800000
+#define BM_SSP_CTRL1_DATA_CRC_IRQ_EN 0x00400000
+#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ 0x00200000
+#define BM_SSP_CTRL1_FIFO_UNDERRUN_EN 0x00100000
+#define BM_SSP_CTRL1_CEATA_CCS_ERR_IRQ 0x00080000
+#define BM_SSP_CTRL1_CEATA_CCS_ERR_IRQ_EN 0x00040000
+#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ 0x00020000
+#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN 0x00010000
+#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ 0x00008000
+#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN 0x00004000
+#define BM_SSP_CTRL1_DMA_ENABLE 0x00002000
+#define BM_SSP_CTRL1_CEATA_CCS_ERR_EN 0x00001000
+#define BM_SSP_CTRL1_SLAVE_OUT_DISABLE 0x00000800
+#define BM_SSP_CTRL1_PHASE 0x00000400
+#define BM_SSP_CTRL1_POLARITY 0x00000200
+#define BM_SSP_CTRL1_SLAVE_MODE 0x00000100
+#define BP_SSP_CTRL1_WORD_LENGTH 4
+#define BM_SSP_CTRL1_WORD_LENGTH 0x000000F0
+#define BF_SSP_CTRL1_WORD_LENGTH(v) \
+ (((v) << 4) & BM_SSP_CTRL1_WORD_LENGTH)
+#define BV_SSP_CTRL1_WORD_LENGTH__RESERVED0 0x0
+#define BV_SSP_CTRL1_WORD_LENGTH__RESERVED1 0x1
+#define BV_SSP_CTRL1_WORD_LENGTH__RESERVED2 0x2
+#define BV_SSP_CTRL1_WORD_LENGTH__FOUR_BITS 0x3
+#define BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS 0x7
+#define BV_SSP_CTRL1_WORD_LENGTH__SIXTEEN_BITS 0xF
+#define BP_SSP_CTRL1_SSP_MODE 0
+#define BM_SSP_CTRL1_SSP_MODE 0x0000000F
+#define BF_SSP_CTRL1_SSP_MODE(v) \
+ (((v) << 0) & BM_SSP_CTRL1_SSP_MODE)
+#define BV_SSP_CTRL1_SSP_MODE__SPI 0x0
+#define BV_SSP_CTRL1_SSP_MODE__SSI 0x1
+#define BV_SSP_CTRL1_SSP_MODE__SD_MMC 0x3
+#define BV_SSP_CTRL1_SSP_MODE__MS 0x4
+#define BV_SSP_CTRL1_SSP_MODE__CE_ATA 0x7
+HW_REGISTER_0(HW_SSP_DATA, REGS_SSP_BASE, 0x00000070)
+#define HW_SSP_DATA_ADDR (REGS_SSP_BASE + 0x00000070)
+#define BP_SSP_DATA_DATA 0
+#define BM_SSP_DATA_DATA 0xFFFFFFFF
+#define BF_SSP_DATA_DATA(v) (v)
+HW_REGISTER_0(HW_SSP_SDRESP0, REGS_SSP_BASE, 0x00000080)
+#define HW_SSP_SDRESP0_ADDR (REGS_SSP_BASE + 0x00000080)
+#define BP_SSP_SDRESP0_RESP0 0
+#define BM_SSP_SDRESP0_RESP0 0xFFFFFFFF
+#define BF_SSP_SDRESP0_RESP0(v) (v)
+HW_REGISTER_0(HW_SSP_SDRESP1, REGS_SSP_BASE, 0x00000090)
+#define HW_SSP_SDRESP1_ADDR (REGS_SSP_BASE + 0x00000090)
+#define BP_SSP_SDRESP1_RESP1 0
+#define BM_SSP_SDRESP1_RESP1 0xFFFFFFFF
+#define BF_SSP_SDRESP1_RESP1(v) (v)
+HW_REGISTER_0(HW_SSP_SDRESP2, REGS_SSP_BASE, 0x000000a0)
+#define HW_SSP_SDRESP2_ADDR (REGS_SSP_BASE + 0x000000a0)
+#define BP_SSP_SDRESP2_RESP2 0
+#define BM_SSP_SDRESP2_RESP2 0xFFFFFFFF
+#define BF_SSP_SDRESP2_RESP2(v) (v)
+HW_REGISTER_0(HW_SSP_SDRESP3, REGS_SSP_BASE, 0x000000b0)
+#define HW_SSP_SDRESP3_ADDR (REGS_SSP_BASE + 0x000000b0)
+#define BP_SSP_SDRESP3_RESP3 0
+#define BM_SSP_SDRESP3_RESP3 0xFFFFFFFF
+#define BF_SSP_SDRESP3_RESP3(v) (v)
+HW_REGISTER_0(HW_SSP_STATUS, REGS_SSP_BASE, 0x000000c0)
+#define HW_SSP_STATUS_ADDR (REGS_SSP_BASE + 0x000000c0)
+#define BM_SSP_STATUS_PRESENT 0x80000000
+#define BM_SSP_STATUS_MS_PRESENT 0x40000000
+#define BM_SSP_STATUS_SD_PRESENT 0x20000000
+#define BM_SSP_STATUS_CARD_DETECT 0x10000000
+#define BM_SSP_STATUS_DMASENSE 0x00200000
+#define BM_SSP_STATUS_DMATERM 0x00100000
+#define BM_SSP_STATUS_DMAREQ 0x00080000
+#define BM_SSP_STATUS_DMAEND 0x00040000
+#define BM_SSP_STATUS_SDIO_IRQ 0x00020000
+#define BM_SSP_STATUS_RESP_CRC_ERR 0x00010000
+#define BM_SSP_STATUS_RESP_ERR 0x00008000
+#define BM_SSP_STATUS_RESP_TIMEOUT 0x00004000
+#define BM_SSP_STATUS_DATA_CRC_ERR 0x00002000
+#define BM_SSP_STATUS_TIMEOUT 0x00001000
+#define BM_SSP_STATUS_RECV_TIMEOUT_STAT 0x00000800
+#define BM_SSP_STATUS_CEATA_CCS_ERR 0x00000400
+#define BM_SSP_STATUS_FIFO_OVRFLW 0x00000200
+#define BM_SSP_STATUS_FIFO_FULL 0x00000100
+#define BM_SSP_STATUS_FIFO_EMPTY 0x00000020
+#define BM_SSP_STATUS_FIFO_UNDRFLW 0x00000010
+#define BM_SSP_STATUS_CMD_BUSY 0x00000008
+#define BM_SSP_STATUS_DATA_BUSY 0x00000004
+#define BM_SSP_STATUS_BUSY 0x00000001
+HW_REGISTER_0(HW_SSP_DEBUG, REGS_SSP_BASE, 0x00000100)
+#define HW_SSP_DEBUG_ADDR (REGS_SSP_BASE + 0x00000100)
+#define BP_SSP_DEBUG_DATACRC_ERR 28
+#define BM_SSP_DEBUG_DATACRC_ERR 0xF0000000
+#define BF_SSP_DEBUG_DATACRC_ERR(v) \
+ (((v) << 28) & BM_SSP_DEBUG_DATACRC_ERR)
+#define BM_SSP_DEBUG_DATA_STALL 0x08000000
+#define BP_SSP_DEBUG_DAT_SM 24
+#define BM_SSP_DEBUG_DAT_SM 0x07000000
+#define BF_SSP_DEBUG_DAT_SM(v) \
+ (((v) << 24) & BM_SSP_DEBUG_DAT_SM)
+#define BV_SSP_DEBUG_DAT_SM__DSM_IDLE 0x0
+#define BV_SSP_DEBUG_DAT_SM__DSM_WORD 0x2
+#define BV_SSP_DEBUG_DAT_SM__DSM_CRC1 0x3
+#define BV_SSP_DEBUG_DAT_SM__DSM_CRC2 0x4
+#define BV_SSP_DEBUG_DAT_SM__DSM_END 0x5
+#define BP_SSP_DEBUG_MSTK_SM 20
+#define BM_SSP_DEBUG_MSTK_SM 0x00F00000
+#define BF_SSP_DEBUG_MSTK_SM(v) \
+ (((v) << 20) & BM_SSP_DEBUG_MSTK_SM)
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_IDLE 0x0
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_CKON 0x1
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_BS1 0x2
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_TPC 0x3
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_BS2 0x4
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_HDSHK 0x5
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_BS3 0x6
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_RW 0x7
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_CRC1 0x8
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_CRC2 0x9
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_BS0 0xA
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_END1 0xB
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_END2W 0xC
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_END2R 0xD
+#define BV_SSP_DEBUG_MSTK_SM__MSTK_DONE 0xE
+#define BM_SSP_DEBUG_CMD_OE 0x00080000
+#define BP_SSP_DEBUG_DMA_SM 16
+#define BM_SSP_DEBUG_DMA_SM 0x00070000
+#define BF_SSP_DEBUG_DMA_SM(v) \
+ (((v) << 16) & BM_SSP_DEBUG_DMA_SM)
+#define BV_SSP_DEBUG_DMA_SM__DMA_IDLE 0x0
+#define BV_SSP_DEBUG_DMA_SM__DMA_DMAREQ 0x1
+#define BV_SSP_DEBUG_DMA_SM__DMA_DMAACK 0x2
+#define BV_SSP_DEBUG_DMA_SM__DMA_STALL 0x3
+#define BV_SSP_DEBUG_DMA_SM__DMA_BUSY 0x4
+#define BV_SSP_DEBUG_DMA_SM__DMA_DONE 0x5
+#define BV_SSP_DEBUG_DMA_SM__DMA_COUNT 0x6
+#define BP_SSP_DEBUG_MMC_SM 12
+#define BM_SSP_DEBUG_MMC_SM 0x0000F000
+#define BF_SSP_DEBUG_MMC_SM(v) \
+ (((v) << 12) & BM_SSP_DEBUG_MMC_SM)
+#define BV_SSP_DEBUG_MMC_SM__MMC_IDLE 0x0
+#define BV_SSP_DEBUG_MMC_SM__MMC_CMD 0x1
+#define BV_SSP_DEBUG_MMC_SM__MMC_TRC 0x2
+#define BV_SSP_DEBUG_MMC_SM__MMC_RESP 0x3
+#define BV_SSP_DEBUG_MMC_SM__MMC_RPRX 0x4
+#define BV_SSP_DEBUG_MMC_SM__MMC_TX 0x5
+#define BV_SSP_DEBUG_MMC_SM__MMC_CTOK 0x6
+#define BV_SSP_DEBUG_MMC_SM__MMC_RX 0x7
+#define BV_SSP_DEBUG_MMC_SM__MMC_CCS 0x8
+#define BV_SSP_DEBUG_MMC_SM__MMC_PUP 0x9
+#define BV_SSP_DEBUG_MMC_SM__MMC_WAIT 0xA
+#define BP_SSP_DEBUG_CMD_SM 10
+#define BM_SSP_DEBUG_CMD_SM 0x00000C00
+#define BF_SSP_DEBUG_CMD_SM(v) \
+ (((v) << 10) & BM_SSP_DEBUG_CMD_SM)
+#define BV_SSP_DEBUG_CMD_SM__CSM_IDLE 0x0
+#define BV_SSP_DEBUG_CMD_SM__CSM_INDEX 0x1
+#define BV_SSP_DEBUG_CMD_SM__CSM_ARG 0x2
+#define BV_SSP_DEBUG_CMD_SM__CSM_CRC 0x3
+#define BM_SSP_DEBUG_SSP_CMD 0x00000200
+#define BM_SSP_DEBUG_SSP_RESP 0x00000100
+#define BP_SSP_DEBUG_SSP_RXD 0
+#define BM_SSP_DEBUG_SSP_RXD 0x000000FF
+#define BF_SSP_DEBUG_SSP_RXD(v) \
+ (((v) << 0) & BM_SSP_DEBUG_SSP_RXD)
+HW_REGISTER_0(HW_SSP_VERSION, REGS_SSP_BASE, 0x00000110)
+#define HW_SSP_VERSION_ADDR (REGS_SSP_BASE + 0x00000110)
+#define BP_SSP_VERSION_MAJOR 24
+#define BM_SSP_VERSION_MAJOR 0xFF000000
+#define BF_SSP_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_SSP_VERSION_MAJOR)
+#define BP_SSP_VERSION_MINOR 16
+#define BM_SSP_VERSION_MINOR 0x00FF0000
+#define BF_SSP_VERSION_MINOR(v) \
+ (((v) << 16) & BM_SSP_VERSION_MINOR)
+#define BP_SSP_VERSION_STEP 0
+#define BM_SSP_VERSION_STEP 0x0000FFFF
+#define BF_SSP_VERSION_STEP(v) \
+ (((v) << 0) & BM_SSP_VERSION_STEP)
+#endif /* __ARCH_ARM___SSP_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-sydma.h b/arch/arm/mach-stmp3xxx/include/mach/regs-sydma.h
new file mode 100644
index 000000000000..c08fbdf95605
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-sydma.h
@@ -0,0 +1,117 @@
+/*
+ * STMP SYDMA Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___SYDMA_H
+#define __ARCH_ARM___SYDMA_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_SYDMA_BASE (REGS_BASE + 0x26000)
+#define REGS_SYDMA_BASE_PHYS (0x80026000)
+#define REGS_SYDMA_SIZE 0x00002000
+HW_REGISTER(HW_SYDMA_CTRL, REGS_SYDMA_BASE, 0x00000000)
+#define HW_SYDMA_CTRL_ADDR (REGS_SYDMA_BASE + 0x00000000)
+#define BM_SYDMA_CTRL_SFTRST 0x80000000
+#define BV_SYDMA_CTRL_SFTRST__RUN 0x0
+#define BV_SYDMA_CTRL_SFTRST__RESET 0x1
+#define BM_SYDMA_CTRL_CLKGATE 0x40000000
+#define BV_SYDMA_CTRL_CLKGATE__RUN 0x0
+#define BV_SYDMA_CTRL_CLKGATE__NO_CLKS 0x1
+#define BM_SYDMA_CTRL_COMPLETE_IRQ_EN 0x00000200
+#define BV_SYDMA_CTRL_COMPLETE_IRQ_EN__DISABLED 0x0
+#define BV_SYDMA_CTRL_COMPLETE_IRQ_EN__ENABLED 0x1
+#define BM_SYDMA_CTRL_ERROR_IRQ 0x00000004
+#define BM_SYDMA_CTRL_COMPLETE_IRQ 0x00000002
+#define BM_SYDMA_CTRL_RUN 0x00000001
+#define BV_SYDMA_CTRL_RUN__HALT 0x0
+#define BV_SYDMA_CTRL_RUN__RUN 0x1
+HW_REGISTER_0(HW_SYDMA_RADDR, REGS_SYDMA_BASE, 0x00000010)
+#define HW_SYDMA_RADDR_ADDR (REGS_SYDMA_BASE + 0x00000010)
+#define BP_SYDMA_RADDR_RSRC_ADDR 0
+#define BM_SYDMA_RADDR_RSRC_ADDR 0xFFFFFFFF
+#define BF_SYDMA_RADDR_RSRC_ADDR(v) (v)
+HW_REGISTER_0(HW_SYDMA_WADDR, REGS_SYDMA_BASE, 0x00000020)
+#define HW_SYDMA_WADDR_ADDR (REGS_SYDMA_BASE + 0x00000020)
+#define BP_SYDMA_WADDR_WSRC_ADDR 0
+#define BM_SYDMA_WADDR_WSRC_ADDR 0xFFFFFFFF
+#define BF_SYDMA_WADDR_WSRC_ADDR(v) (v)
+HW_REGISTER_0(HW_SYDMA_XFER_COUNT, REGS_SYDMA_BASE, 0x00000030)
+#define HW_SYDMA_XFER_COUNT_ADDR (REGS_SYDMA_BASE + 0x00000030)
+#define BP_SYDMA_XFER_COUNT_SIZE 0
+#define BM_SYDMA_XFER_COUNT_SIZE 0xFFFFFFFF
+#define BF_SYDMA_XFER_COUNT_SIZE(v) (v)
+HW_REGISTER_0(HW_SYDMA_BURST, REGS_SYDMA_BASE, 0x00000040)
+#define HW_SYDMA_BURST_ADDR (REGS_SYDMA_BASE + 0x00000040)
+#define BP_SYDMA_BURST_WLEN 2
+#define BM_SYDMA_BURST_WLEN 0x0000000C
+#define BF_SYDMA_BURST_WLEN(v) \
+ (((v) << 2) & BM_SYDMA_BURST_WLEN)
+#define BV_SYDMA_BURST_WLEN__1 0x0
+#define BV_SYDMA_BURST_WLEN__2 0x1
+#define BV_SYDMA_BURST_WLEN__4 0x2
+#define BV_SYDMA_BURST_WLEN__8 0x3
+#define BP_SYDMA_BURST_RLEN 0
+#define BM_SYDMA_BURST_RLEN 0x00000003
+#define BF_SYDMA_BURST_RLEN(v) \
+ (((v) << 0) & BM_SYDMA_BURST_RLEN)
+#define BV_SYDMA_BURST_RLEN__1 0x0
+#define BV_SYDMA_BURST_RLEN__2 0x1
+#define BV_SYDMA_BURST_RLEN__4 0x2
+#define BV_SYDMA_BURST_RLEN__8 0x3
+HW_REGISTER_0(HW_SYDMA_DACK, REGS_SYDMA_BASE, 0x00000050)
+#define HW_SYDMA_DACK_ADDR (REGS_SYDMA_BASE + 0x00000050)
+#define BP_SYDMA_DACK_WDELAY 4
+#define BM_SYDMA_DACK_WDELAY 0x000000F0
+#define BF_SYDMA_DACK_WDELAY(v) \
+ (((v) << 4) & BM_SYDMA_DACK_WDELAY)
+#define BP_SYDMA_DACK_RDELAY 0
+#define BM_SYDMA_DACK_RDELAY 0x0000000F
+#define BF_SYDMA_DACK_RDELAY(v) \
+ (((v) << 0) & BM_SYDMA_DACK_RDELAY)
+HW_REGISTER_0(HW_SYDMA_DEBUG0, REGS_SYDMA_BASE, 0x00000100)
+#define HW_SYDMA_DEBUG0_ADDR (REGS_SYDMA_BASE + 0x00000100)
+#define BP_SYDMA_DEBUG0_DATA 0
+#define BM_SYDMA_DEBUG0_DATA 0xFFFFFFFF
+#define BF_SYDMA_DEBUG0_DATA(v) (v)
+HW_REGISTER_0(HW_SYDMA_DEBUG1, REGS_SYDMA_BASE, 0x00000110)
+#define HW_SYDMA_DEBUG1_ADDR (REGS_SYDMA_BASE + 0x00000110)
+#define BP_SYDMA_DEBUG1_DATA 0
+#define BM_SYDMA_DEBUG1_DATA 0xFFFFFFFF
+#define BF_SYDMA_DEBUG1_DATA(v) (v)
+HW_REGISTER_0(HW_SYDMA_DEBUG2, REGS_SYDMA_BASE, 0x00000120)
+#define HW_SYDMA_DEBUG2_ADDR (REGS_SYDMA_BASE + 0x00000120)
+#define BP_SYDMA_DEBUG2_DATA 0
+#define BM_SYDMA_DEBUG2_DATA 0xFFFFFFFF
+#define BF_SYDMA_DEBUG2_DATA(v) (v)
+HW_REGISTER_0(HW_SYDMA_VERSION, REGS_SYDMA_BASE, 0x00000130)
+#define HW_SYDMA_VERSION_ADDR (REGS_SYDMA_BASE + 0x00000130)
+#define BP_SYDMA_VERSION_MAJOR 24
+#define BM_SYDMA_VERSION_MAJOR 0xFF000000
+#define BF_SYDMA_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_SYDMA_VERSION_MAJOR)
+#define BP_SYDMA_VERSION_MINOR 16
+#define BM_SYDMA_VERSION_MINOR 0x00FF0000
+#define BF_SYDMA_VERSION_MINOR(v) \
+ (((v) << 16) & BM_SYDMA_VERSION_MINOR)
+#define BP_SYDMA_VERSION_STEP 0
+#define BM_SYDMA_VERSION_STEP 0x0000FFFF
+#define BF_SYDMA_VERSION_STEP(v) \
+ (((v) << 0) & BM_SYDMA_VERSION_STEP)
+#endif /* __ARCH_ARM___SYDMA_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-timrot.h b/arch/arm/mach-stmp3xxx/include/mach/regs-timrot.h
new file mode 100644
index 000000000000..75f4aa1c7558
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-timrot.h
@@ -0,0 +1,215 @@
+/*
+ * STMP TIMROT Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___TIMROT_H
+#define __ARCH_ARM___TIMROT_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_TIMROT_BASE (REGS_BASE + 0x68000)
+#define REGS_TIMROT_BASE_PHYS (0x80068000)
+#define REGS_TIMROT_SIZE 0x00002000
+HW_REGISTER(HW_TIMROT_ROTCTRL, REGS_TIMROT_BASE, 0x00000000)
+#define HW_TIMROT_ROTCTRL_ADDR (REGS_TIMROT_BASE + 0x00000000)
+#define BM_TIMROT_ROTCTRL_SFTRST 0x80000000
+#define BM_TIMROT_ROTCTRL_CLKGATE 0x40000000
+#define BM_TIMROT_ROTCTRL_ROTARY_PRESENT 0x20000000
+#define BM_TIMROT_ROTCTRL_TIM3_PRESENT 0x10000000
+#define BM_TIMROT_ROTCTRL_TIM2_PRESENT 0x08000000
+#define BM_TIMROT_ROTCTRL_TIM1_PRESENT 0x04000000
+#define BM_TIMROT_ROTCTRL_TIM0_PRESENT 0x02000000
+#define BP_TIMROT_ROTCTRL_STATE 22
+#define BM_TIMROT_ROTCTRL_STATE 0x01C00000
+#define BF_TIMROT_ROTCTRL_STATE(v) \
+ (((v) << 22) & BM_TIMROT_ROTCTRL_STATE)
+#define BP_TIMROT_ROTCTRL_DIVIDER 16
+#define BM_TIMROT_ROTCTRL_DIVIDER 0x003F0000
+#define BF_TIMROT_ROTCTRL_DIVIDER(v) \
+ (((v) << 16) & BM_TIMROT_ROTCTRL_DIVIDER)
+#define BM_TIMROT_ROTCTRL_RELATIVE 0x00001000
+#define BP_TIMROT_ROTCTRL_OVERSAMPLE 10
+#define BM_TIMROT_ROTCTRL_OVERSAMPLE 0x00000C00
+#define BF_TIMROT_ROTCTRL_OVERSAMPLE(v) \
+ (((v) << 10) & BM_TIMROT_ROTCTRL_OVERSAMPLE)
+#define BV_TIMROT_ROTCTRL_OVERSAMPLE__8X 0x0
+#define BV_TIMROT_ROTCTRL_OVERSAMPLE__4X 0x1
+#define BV_TIMROT_ROTCTRL_OVERSAMPLE__2X 0x2
+#define BV_TIMROT_ROTCTRL_OVERSAMPLE__1X 0x3
+#define BM_TIMROT_ROTCTRL_POLARITY_B 0x00000200
+#define BM_TIMROT_ROTCTRL_POLARITY_A 0x00000100
+#define BP_TIMROT_ROTCTRL_SELECT_B 4
+#define BM_TIMROT_ROTCTRL_SELECT_B 0x00000070
+#define BF_TIMROT_ROTCTRL_SELECT_B(v) \
+ (((v) << 4) & BM_TIMROT_ROTCTRL_SELECT_B)
+#define BV_TIMROT_ROTCTRL_SELECT_B__NEVER_TICK 0x0
+#define BV_TIMROT_ROTCTRL_SELECT_B__PWM0 0x1
+#define BV_TIMROT_ROTCTRL_SELECT_B__PWM1 0x2
+#define BV_TIMROT_ROTCTRL_SELECT_B__PWM2 0x3
+#define BV_TIMROT_ROTCTRL_SELECT_B__PWM3 0x4
+#define BV_TIMROT_ROTCTRL_SELECT_B__PWM4 0x5
+#define BV_TIMROT_ROTCTRL_SELECT_B__ROTARYA 0x6
+#define BV_TIMROT_ROTCTRL_SELECT_B__ROTARYB 0x7
+#define BP_TIMROT_ROTCTRL_SELECT_A 0
+#define BM_TIMROT_ROTCTRL_SELECT_A 0x00000007
+#define BF_TIMROT_ROTCTRL_SELECT_A(v) \
+ (((v) << 0) & BM_TIMROT_ROTCTRL_SELECT_A)
+#define BV_TIMROT_ROTCTRL_SELECT_A__NEVER_TICK 0x0
+#define BV_TIMROT_ROTCTRL_SELECT_A__PWM0 0x1
+#define BV_TIMROT_ROTCTRL_SELECT_A__PWM1 0x2
+#define BV_TIMROT_ROTCTRL_SELECT_A__PWM2 0x3
+#define BV_TIMROT_ROTCTRL_SELECT_A__PWM3 0x4
+#define BV_TIMROT_ROTCTRL_SELECT_A__PWM4 0x5
+#define BV_TIMROT_ROTCTRL_SELECT_A__ROTARYA 0x6
+#define BV_TIMROT_ROTCTRL_SELECT_A__ROTARYB 0x7
+HW_REGISTER_0(HW_TIMROT_ROTCOUNT, REGS_TIMROT_BASE, 0x00000010)
+#define HW_TIMROT_ROTCOUNT_ADDR (REGS_TIMROT_BASE + 0x00000010)
+#define BP_TIMROT_ROTCOUNT_UPDOWN 0
+#define BM_TIMROT_ROTCOUNT_UPDOWN 0x0000FFFF
+#define BF_TIMROT_ROTCOUNT_UPDOWN(v) \
+ (((v) << 0) & BM_TIMROT_ROTCOUNT_UPDOWN)
+/*
+ * multi-register-define name HW_TIMROT_TIMCTRLn
+ * base 0x00000020
+ * count 3
+ * offset 0x20
+ */
+HW_REGISTER_INDEXED(HW_TIMROT_TIMCTRLn, REGS_TIMROT_BASE, 0x00000020, 0x20)
+#define BM_TIMROT_TIMCTRLn_IRQ 0x00008000
+#define BM_TIMROT_TIMCTRLn_IRQ_EN 0x00004000
+#define BM_TIMROT_TIMCTRLn_POLARITY 0x00000100
+#define BM_TIMROT_TIMCTRLn_UPDATE 0x00000080
+#define BM_TIMROT_TIMCTRLn_RELOAD 0x00000040
+#define BP_TIMROT_TIMCTRLn_PRESCALE 4
+#define BM_TIMROT_TIMCTRLn_PRESCALE 0x00000030
+#define BF_TIMROT_TIMCTRLn_PRESCALE(v) \
+ (((v) << 4) & BM_TIMROT_TIMCTRLn_PRESCALE)
+#define BV_TIMROT_TIMCTRLn_PRESCALE__DIV_BY_1 0x0
+#define BV_TIMROT_TIMCTRLn_PRESCALE__DIV_BY_2 0x1
+#define BV_TIMROT_TIMCTRLn_PRESCALE__DIV_BY_4 0x2
+#define BV_TIMROT_TIMCTRLn_PRESCALE__DIV_BY_8 0x3
+#define BP_TIMROT_TIMCTRLn_SELECT 0
+#define BM_TIMROT_TIMCTRLn_SELECT 0x0000000F
+#define BF_TIMROT_TIMCTRLn_SELECT(v) \
+ (((v) << 0) & BM_TIMROT_TIMCTRLn_SELECT)
+#define BV_TIMROT_TIMCTRLn_SELECT__NEVER_TICK 0x0
+#define BV_TIMROT_TIMCTRLn_SELECT__PWM0 0x1
+#define BV_TIMROT_TIMCTRLn_SELECT__PWM1 0x2
+#define BV_TIMROT_TIMCTRLn_SELECT__PWM2 0x3
+#define BV_TIMROT_TIMCTRLn_SELECT__PWM3 0x4
+#define BV_TIMROT_TIMCTRLn_SELECT__PWM4 0x5
+#define BV_TIMROT_TIMCTRLn_SELECT__ROTARYA 0x6
+#define BV_TIMROT_TIMCTRLn_SELECT__ROTARYB 0x7
+#define BV_TIMROT_TIMCTRLn_SELECT__32KHZ_XTAL 0x8
+#define BV_TIMROT_TIMCTRLn_SELECT__8KHZ_XTAL 0x9
+#define BV_TIMROT_TIMCTRLn_SELECT__4KHZ_XTAL 0xA
+#define BV_TIMROT_TIMCTRLn_SELECT__1KHZ_XTAL 0xB
+#define BV_TIMROT_TIMCTRLn_SELECT__TICK_ALWAYS 0xC
+/*
+ * multi-register-define name HW_TIMROT_TIMCOUNTn
+ * base 0x00000030
+ * count 3
+ * offset 0x20
+ */
+HW_REGISTER_0_INDEXED(HW_TIMROT_TIMCOUNTn, REGS_TIMROT_BASE, 0x00000030,
+ 0x20)
+#define BP_TIMROT_TIMCOUNTn_RUNNING_COUNT 16
+#define BM_TIMROT_TIMCOUNTn_RUNNING_COUNT 0xFFFF0000
+#define BF_TIMROT_TIMCOUNTn_RUNNING_COUNT(v) \
+ (((v) << 16) & BM_TIMROT_TIMCOUNTn_RUNNING_COUNT)
+#define BP_TIMROT_TIMCOUNTn_FIXED_COUNT 0
+#define BM_TIMROT_TIMCOUNTn_FIXED_COUNT 0x0000FFFF
+#define BF_TIMROT_TIMCOUNTn_FIXED_COUNT(v) \
+ (((v) << 0) & BM_TIMROT_TIMCOUNTn_FIXED_COUNT)
+HW_REGISTER(HW_TIMROT_TIMCTRL3, REGS_TIMROT_BASE, 0x00000080)
+#define HW_TIMROT_TIMCTRL3_ADDR (REGS_TIMROT_BASE + 0x00000080)
+#define BP_TIMROT_TIMCTRL3_TEST_SIGNAL 16
+#define BM_TIMROT_TIMCTRL3_TEST_SIGNAL 0x000F0000
+#define BF_TIMROT_TIMCTRL3_TEST_SIGNAL(v) \
+ (((v) << 16) & BM_TIMROT_TIMCTRL3_TEST_SIGNAL)
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__NEVER_TICK 0x0
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__PWM0 0x1
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__PWM1 0x2
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__PWM2 0x3
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__PWM3 0x4
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__PWM4 0x5
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__ROTARYA 0x6
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__ROTARYB 0x7
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__32KHZ_XTAL 0x8
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__8KHZ_XTAL 0x9
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__4KHZ_XTAL 0xA
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__1KHZ_XTAL 0xB
+#define BV_TIMROT_TIMCTRL3_TEST_SIGNAL__TICK_ALWAYS 0xC
+#define BM_TIMROT_TIMCTRL3_IRQ 0x00008000
+#define BM_TIMROT_TIMCTRL3_IRQ_EN 0x00004000
+#define BM_TIMROT_TIMCTRL3_DUTY_VALID 0x00000400
+#define BM_TIMROT_TIMCTRL3_DUTY_CYCLE 0x00000200
+#define BM_TIMROT_TIMCTRL3_POLARITY 0x00000100
+#define BM_TIMROT_TIMCTRL3_UPDATE 0x00000080
+#define BM_TIMROT_TIMCTRL3_RELOAD 0x00000040
+#define BP_TIMROT_TIMCTRL3_PRESCALE 4
+#define BM_TIMROT_TIMCTRL3_PRESCALE 0x00000030
+#define BF_TIMROT_TIMCTRL3_PRESCALE(v) \
+ (((v) << 4) & BM_TIMROT_TIMCTRL3_PRESCALE)
+#define BV_TIMROT_TIMCTRL3_PRESCALE__DIV_BY_1 0x0
+#define BV_TIMROT_TIMCTRL3_PRESCALE__DIV_BY_2 0x1
+#define BV_TIMROT_TIMCTRL3_PRESCALE__DIV_BY_4 0x2
+#define BV_TIMROT_TIMCTRL3_PRESCALE__DIV_BY_8 0x3
+#define BP_TIMROT_TIMCTRL3_SELECT 0
+#define BM_TIMROT_TIMCTRL3_SELECT 0x0000000F
+#define BF_TIMROT_TIMCTRL3_SELECT(v) \
+ (((v) << 0) & BM_TIMROT_TIMCTRL3_SELECT)
+#define BV_TIMROT_TIMCTRL3_SELECT__NEVER_TICK 0x0
+#define BV_TIMROT_TIMCTRL3_SELECT__PWM0 0x1
+#define BV_TIMROT_TIMCTRL3_SELECT__PWM1 0x2
+#define BV_TIMROT_TIMCTRL3_SELECT__PWM2 0x3
+#define BV_TIMROT_TIMCTRL3_SELECT__PWM3 0x4
+#define BV_TIMROT_TIMCTRL3_SELECT__PWM4 0x5
+#define BV_TIMROT_TIMCTRL3_SELECT__ROTARYA 0x6
+#define BV_TIMROT_TIMCTRL3_SELECT__ROTARYB 0x7
+#define BV_TIMROT_TIMCTRL3_SELECT__32KHZ_XTAL 0x8
+#define BV_TIMROT_TIMCTRL3_SELECT__8KHZ_XTAL 0x9
+#define BV_TIMROT_TIMCTRL3_SELECT__4KHZ_XTAL 0xA
+#define BV_TIMROT_TIMCTRL3_SELECT__1KHZ_XTAL 0xB
+#define BV_TIMROT_TIMCTRL3_SELECT__TICK_ALWAYS 0xC
+HW_REGISTER_0(HW_TIMROT_TIMCOUNT3, REGS_TIMROT_BASE, 0x00000090)
+#define HW_TIMROT_TIMCOUNT3_ADDR (REGS_TIMROT_BASE + 0x00000090)
+#define BP_TIMROT_TIMCOUNT3_LOW_RUNNING_COUNT 16
+#define BM_TIMROT_TIMCOUNT3_LOW_RUNNING_COUNT 0xFFFF0000
+#define BF_TIMROT_TIMCOUNT3_LOW_RUNNING_COUNT(v) \
+ (((v) << 16) & BM_TIMROT_TIMCOUNT3_LOW_RUNNING_COUNT)
+#define BP_TIMROT_TIMCOUNT3_HIGH_FIXED_COUNT 0
+#define BM_TIMROT_TIMCOUNT3_HIGH_FIXED_COUNT 0x0000FFFF
+#define BF_TIMROT_TIMCOUNT3_HIGH_FIXED_COUNT(v) \
+ (((v) << 0) & BM_TIMROT_TIMCOUNT3_HIGH_FIXED_COUNT)
+HW_REGISTER_0(HW_TIMROT_VERSION, REGS_TIMROT_BASE, 0x000000a0)
+#define HW_TIMROT_VERSION_ADDR (REGS_TIMROT_BASE + 0x000000a0)
+#define BP_TIMROT_VERSION_MAJOR 24
+#define BM_TIMROT_VERSION_MAJOR 0xFF000000
+#define BF_TIMROT_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_TIMROT_VERSION_MAJOR)
+#define BP_TIMROT_VERSION_MINOR 16
+#define BM_TIMROT_VERSION_MINOR 0x00FF0000
+#define BF_TIMROT_VERSION_MINOR(v) \
+ (((v) << 16) & BM_TIMROT_VERSION_MINOR)
+#define BP_TIMROT_VERSION_STEP 0
+#define BM_TIMROT_VERSION_STEP 0x0000FFFF
+#define BF_TIMROT_VERSION_STEP(v) \
+ (((v) << 0) & BM_TIMROT_VERSION_STEP)
+#endif /* __ARCH_ARM___TIMROT_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-tvenc.h b/arch/arm/mach-stmp3xxx/include/mach/regs-tvenc.h
new file mode 100644
index 000000000000..788d0978cf6d
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-tvenc.h
@@ -0,0 +1,337 @@
+/*
+ * STMP TVENC Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___TVENC_H
+#define __ARCH_ARM___TVENC_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_TVENC_BASE (REGS_BASE + 0x38000)
+#define REGS_TVENC_BASE_PHYS (0x80038000)
+#define REGS_TVENC_SIZE 0x00002000
+HW_REGISTER(HW_TVENC_CTRL, REGS_TVENC_BASE, 0x00000000)
+#define HW_TVENC_CTRL_ADDR (REGS_TVENC_BASE + 0x00000000)
+#define BM_TVENC_CTRL_SFTRST 0x80000000
+#define BM_TVENC_CTRL_CLKGATE 0x40000000
+#define BM_TVENC_CTRL_TVENC_MACROVISION_PRESENT 0x20000000
+#define BM_TVENC_CTRL_TVENC_COMPOSITE_PRESENT 0x10000000
+#define BM_TVENC_CTRL_TVENC_SVIDEO_PRESENT 0x08000000
+#define BM_TVENC_CTRL_TVENC_COMPONENT_PRESENT 0x04000000
+#define BM_TVENC_CTRL_DAC_FIFO_NO_WRITE 0x00000020
+#define BM_TVENC_CTRL_DAC_FIFO_NO_READ 0x00000010
+#define BM_TVENC_CTRL_DAC_DATA_FIFO_RST 0x00000008
+#define BM_TVENC_CTRL_DAC_MUX_MODE 0x00000001
+HW_REGISTER(HW_TVENC_CONFIG, REGS_TVENC_BASE, 0x00000010)
+#define HW_TVENC_CONFIG_ADDR (REGS_TVENC_BASE + 0x00000010)
+#define BM_TVENC_CONFIG_DEFAULT_PICFORM 0x08000000
+#define BP_TVENC_CONFIG_YDEL_ADJ 24
+#define BM_TVENC_CONFIG_YDEL_ADJ 0x07000000
+#define BF_TVENC_CONFIG_YDEL_ADJ(v) \
+ (((v) << 24) & BM_TVENC_CONFIG_YDEL_ADJ)
+#define BM_TVENC_CONFIG_ADD_YPBPR_PED 0x00200000
+#define BM_TVENC_CONFIG_PAL_SHAPE 0x00100000
+#define BM_TVENC_CONFIG_NO_PED 0x00080000
+#define BM_TVENC_CONFIG_COLOR_BAR_EN 0x00040000
+#define BP_TVENC_CONFIG_YGAIN_SEL 16
+#define BM_TVENC_CONFIG_YGAIN_SEL 0x00030000
+#define BF_TVENC_CONFIG_YGAIN_SEL(v) \
+ (((v) << 16) & BM_TVENC_CONFIG_YGAIN_SEL)
+#define BP_TVENC_CONFIG_CGAIN 14
+#define BM_TVENC_CONFIG_CGAIN 0x0000C000
+#define BF_TVENC_CONFIG_CGAIN(v) \
+ (((v) << 14) & BM_TVENC_CONFIG_CGAIN)
+#define BP_TVENC_CONFIG_CLK_PHS 12
+#define BM_TVENC_CONFIG_CLK_PHS 0x00003000
+#define BF_TVENC_CONFIG_CLK_PHS(v) \
+ (((v) << 12) & BM_TVENC_CONFIG_CLK_PHS)
+#define BM_TVENC_CONFIG_FSYNC_ENBL 0x00000400
+#define BM_TVENC_CONFIG_FSYNC_PHS 0x00000200
+#define BM_TVENC_CONFIG_HSYNC_PHS 0x00000100
+#define BM_TVENC_CONFIG_VSYNC_PHS 0x00000080
+#define BP_TVENC_CONFIG_SYNC_MODE 4
+#define BM_TVENC_CONFIG_SYNC_MODE 0x00000070
+#define BF_TVENC_CONFIG_SYNC_MODE(v) \
+ (((v) << 4) & BM_TVENC_CONFIG_SYNC_MODE)
+#define BP_TVENC_CONFIG_ENCD_MODE 0
+#define BM_TVENC_CONFIG_ENCD_MODE 0x00000007
+#define BF_TVENC_CONFIG_ENCD_MODE(v) \
+ (((v) << 0) & BM_TVENC_CONFIG_ENCD_MODE)
+HW_REGISTER(HW_TVENC_FILTCTRL, REGS_TVENC_BASE, 0x00000020)
+#define HW_TVENC_FILTCTRL_ADDR (REGS_TVENC_BASE + 0x00000020)
+#define BM_TVENC_FILTCTRL_YSHARP_BW 0x00080000
+#define BM_TVENC_FILTCTRL_YD_OFFSETSEL 0x00040000
+#define BM_TVENC_FILTCTRL_SEL_YLPF 0x00020000
+#define BM_TVENC_FILTCTRL_SEL_CLPF 0x00010000
+#define BM_TVENC_FILTCTRL_SEL_YSHARP 0x00008000
+#define BM_TVENC_FILTCTRL_YLPF_COEFSEL 0x00004000
+#define BM_TVENC_FILTCTRL_COEFSEL_CLPF 0x00002000
+#define BM_TVENC_FILTCTRL_YS_GAINSGN 0x00001000
+#define BP_TVENC_FILTCTRL_YS_GAINSEL 10
+#define BM_TVENC_FILTCTRL_YS_GAINSEL 0x00000C00
+#define BF_TVENC_FILTCTRL_YS_GAINSEL(v) \
+ (((v) << 10) & BM_TVENC_FILTCTRL_YS_GAINSEL)
+HW_REGISTER(HW_TVENC_SYNCOFFSET, REGS_TVENC_BASE, 0x00000030)
+#define HW_TVENC_SYNCOFFSET_ADDR (REGS_TVENC_BASE + 0x00000030)
+#define BP_TVENC_SYNCOFFSET_HSO 20
+#define BM_TVENC_SYNCOFFSET_HSO 0x7FF00000
+#define BF_TVENC_SYNCOFFSET_HSO(v) \
+ (((v) << 20) & BM_TVENC_SYNCOFFSET_HSO)
+#define BP_TVENC_SYNCOFFSET_VSO 10
+#define BM_TVENC_SYNCOFFSET_VSO 0x000FFC00
+#define BF_TVENC_SYNCOFFSET_VSO(v) \
+ (((v) << 10) & BM_TVENC_SYNCOFFSET_VSO)
+#define BP_TVENC_SYNCOFFSET_HLC 0
+#define BM_TVENC_SYNCOFFSET_HLC 0x000003FF
+#define BF_TVENC_SYNCOFFSET_HLC(v) \
+ (((v) << 0) & BM_TVENC_SYNCOFFSET_HLC)
+HW_REGISTER(HW_TVENC_HTIMINGSYNC0, REGS_TVENC_BASE, 0x00000040)
+#define HW_TVENC_HTIMINGSYNC0_ADDR (REGS_TVENC_BASE + 0x00000040)
+#define BP_TVENC_HTIMINGSYNC0_SYNC_END 16
+#define BM_TVENC_HTIMINGSYNC0_SYNC_END 0x03FF0000
+#define BF_TVENC_HTIMINGSYNC0_SYNC_END(v) \
+ (((v) << 16) & BM_TVENC_HTIMINGSYNC0_SYNC_END)
+#define BP_TVENC_HTIMINGSYNC0_SYNC_STRT 0
+#define BM_TVENC_HTIMINGSYNC0_SYNC_STRT 0x000003FF
+#define BF_TVENC_HTIMINGSYNC0_SYNC_STRT(v) \
+ (((v) << 0) & BM_TVENC_HTIMINGSYNC0_SYNC_STRT)
+HW_REGISTER(HW_TVENC_HTIMINGSYNC1, REGS_TVENC_BASE, 0x00000050)
+#define HW_TVENC_HTIMINGSYNC1_ADDR (REGS_TVENC_BASE + 0x00000050)
+#define BP_TVENC_HTIMINGSYNC1_SYNC_EQEND 16
+#define BM_TVENC_HTIMINGSYNC1_SYNC_EQEND 0x03FF0000
+#define BF_TVENC_HTIMINGSYNC1_SYNC_EQEND(v) \
+ (((v) << 16) & BM_TVENC_HTIMINGSYNC1_SYNC_EQEND)
+#define BP_TVENC_HTIMINGSYNC1_SYNC_SREND 0
+#define BM_TVENC_HTIMINGSYNC1_SYNC_SREND 0x000003FF
+#define BF_TVENC_HTIMINGSYNC1_SYNC_SREND(v) \
+ (((v) << 0) & BM_TVENC_HTIMINGSYNC1_SYNC_SREND)
+HW_REGISTER(HW_TVENC_HTIMINGACTIVE, REGS_TVENC_BASE, 0x00000060)
+#define HW_TVENC_HTIMINGACTIVE_ADDR (REGS_TVENC_BASE + 0x00000060)
+#define BP_TVENC_HTIMINGACTIVE_ACTV_END 16
+#define BM_TVENC_HTIMINGACTIVE_ACTV_END 0x03FF0000
+#define BF_TVENC_HTIMINGACTIVE_ACTV_END(v) \
+ (((v) << 16) & BM_TVENC_HTIMINGACTIVE_ACTV_END)
+#define BP_TVENC_HTIMINGACTIVE_ACTV_STRT 0
+#define BM_TVENC_HTIMINGACTIVE_ACTV_STRT 0x000003FF
+#define BF_TVENC_HTIMINGACTIVE_ACTV_STRT(v) \
+ (((v) << 0) & BM_TVENC_HTIMINGACTIVE_ACTV_STRT)
+HW_REGISTER(HW_TVENC_HTIMINGBURST0, REGS_TVENC_BASE, 0x00000070)
+#define HW_TVENC_HTIMINGBURST0_ADDR (REGS_TVENC_BASE + 0x00000070)
+#define BP_TVENC_HTIMINGBURST0_WBRST_STRT 16
+#define BM_TVENC_HTIMINGBURST0_WBRST_STRT 0x03FF0000
+#define BF_TVENC_HTIMINGBURST0_WBRST_STRT(v) \
+ (((v) << 16) & BM_TVENC_HTIMINGBURST0_WBRST_STRT)
+#define BP_TVENC_HTIMINGBURST0_NBRST_STRT 0
+#define BM_TVENC_HTIMINGBURST0_NBRST_STRT 0x000003FF
+#define BF_TVENC_HTIMINGBURST0_NBRST_STRT(v) \
+ (((v) << 0) & BM_TVENC_HTIMINGBURST0_NBRST_STRT)
+HW_REGISTER(HW_TVENC_HTIMINGBURST1, REGS_TVENC_BASE, 0x00000080)
+#define HW_TVENC_HTIMINGBURST1_ADDR (REGS_TVENC_BASE + 0x00000080)
+#define BP_TVENC_HTIMINGBURST1_BRST_END 0
+#define BM_TVENC_HTIMINGBURST1_BRST_END 0x000003FF
+#define BF_TVENC_HTIMINGBURST1_BRST_END(v) \
+ (((v) << 0) & BM_TVENC_HTIMINGBURST1_BRST_END)
+HW_REGISTER(HW_TVENC_VTIMING0, REGS_TVENC_BASE, 0x00000090)
+#define HW_TVENC_VTIMING0_ADDR (REGS_TVENC_BASE + 0x00000090)
+#define BP_TVENC_VTIMING0_VSTRT_PREEQ 16
+#define BM_TVENC_VTIMING0_VSTRT_PREEQ 0x03FF0000
+#define BF_TVENC_VTIMING0_VSTRT_PREEQ(v) \
+ (((v) << 16) & BM_TVENC_VTIMING0_VSTRT_PREEQ)
+#define BP_TVENC_VTIMING0_VSTRT_ACTV 8
+#define BM_TVENC_VTIMING0_VSTRT_ACTV 0x00003F00
+#define BF_TVENC_VTIMING0_VSTRT_ACTV(v) \
+ (((v) << 8) & BM_TVENC_VTIMING0_VSTRT_ACTV)
+#define BP_TVENC_VTIMING0_VSTRT_SUBPH 0
+#define BM_TVENC_VTIMING0_VSTRT_SUBPH 0x0000003F
+#define BF_TVENC_VTIMING0_VSTRT_SUBPH(v) \
+ (((v) << 0) & BM_TVENC_VTIMING0_VSTRT_SUBPH)
+HW_REGISTER(HW_TVENC_VTIMING1, REGS_TVENC_BASE, 0x000000a0)
+#define HW_TVENC_VTIMING1_ADDR (REGS_TVENC_BASE + 0x000000a0)
+#define BP_TVENC_VTIMING1_VSTRT_POSTEQ 24
+#define BM_TVENC_VTIMING1_VSTRT_POSTEQ 0x3F000000
+#define BF_TVENC_VTIMING1_VSTRT_POSTEQ(v) \
+ (((v) << 24) & BM_TVENC_VTIMING1_VSTRT_POSTEQ)
+#define BP_TVENC_VTIMING1_VSTRT_SERRA 16
+#define BM_TVENC_VTIMING1_VSTRT_SERRA 0x003F0000
+#define BF_TVENC_VTIMING1_VSTRT_SERRA(v) \
+ (((v) << 16) & BM_TVENC_VTIMING1_VSTRT_SERRA)
+#define BP_TVENC_VTIMING1_LAST_FLD_LN 0
+#define BM_TVENC_VTIMING1_LAST_FLD_LN 0x000003FF
+#define BF_TVENC_VTIMING1_LAST_FLD_LN(v) \
+ (((v) << 0) & BM_TVENC_VTIMING1_LAST_FLD_LN)
+HW_REGISTER(HW_TVENC_MISC, REGS_TVENC_BASE, 0x000000b0)
+#define HW_TVENC_MISC_ADDR (REGS_TVENC_BASE + 0x000000b0)
+#define BP_TVENC_MISC_LPF_RST_OFF 16
+#define BM_TVENC_MISC_LPF_RST_OFF 0x01FF0000
+#define BF_TVENC_MISC_LPF_RST_OFF(v) \
+ (((v) << 16) & BM_TVENC_MISC_LPF_RST_OFF)
+#define BM_TVENC_MISC_NTSC_LN_CNT 0x00000800
+#define BM_TVENC_MISC_PAL_FSC_PHASE_ALT 0x00000400
+#define BP_TVENC_MISC_FSC_PHASE_RST 8
+#define BM_TVENC_MISC_FSC_PHASE_RST 0x00000300
+#define BF_TVENC_MISC_FSC_PHASE_RST(v) \
+ (((v) << 8) & BM_TVENC_MISC_FSC_PHASE_RST)
+#define BP_TVENC_MISC_BRUCHB 6
+#define BM_TVENC_MISC_BRUCHB 0x000000C0
+#define BF_TVENC_MISC_BRUCHB(v) \
+ (((v) << 6) & BM_TVENC_MISC_BRUCHB)
+#define BP_TVENC_MISC_AGC_LVL_CTRL 4
+#define BM_TVENC_MISC_AGC_LVL_CTRL 0x00000030
+#define BF_TVENC_MISC_AGC_LVL_CTRL(v) \
+ (((v) << 4) & BM_TVENC_MISC_AGC_LVL_CTRL)
+#define BM_TVENC_MISC_CS_INVERT_CTRL 0x00000004
+#define BP_TVENC_MISC_Y_BLANK_CTRL 0
+#define BM_TVENC_MISC_Y_BLANK_CTRL 0x00000003
+#define BF_TVENC_MISC_Y_BLANK_CTRL(v) \
+ (((v) << 0) & BM_TVENC_MISC_Y_BLANK_CTRL)
+HW_REGISTER(HW_TVENC_COLORSUB0, REGS_TVENC_BASE, 0x000000c0)
+#define HW_TVENC_COLORSUB0_ADDR (REGS_TVENC_BASE + 0x000000c0)
+#define BP_TVENC_COLORSUB0_PHASE_INC 0
+#define BM_TVENC_COLORSUB0_PHASE_INC 0xFFFFFFFF
+#define BF_TVENC_COLORSUB0_PHASE_INC(v) (v)
+HW_REGISTER(HW_TVENC_COLORSUB1, REGS_TVENC_BASE, 0x000000d0)
+#define HW_TVENC_COLORSUB1_ADDR (REGS_TVENC_BASE + 0x000000d0)
+#define BP_TVENC_COLORSUB1_PHASE_OFFSET 0
+#define BM_TVENC_COLORSUB1_PHASE_OFFSET 0xFFFFFFFF
+#define BF_TVENC_COLORSUB1_PHASE_OFFSET(v) (v)
+HW_REGISTER(HW_TVENC_COPYPROTECT, REGS_TVENC_BASE, 0x000000e0)
+#define HW_TVENC_COPYPROTECT_ADDR (REGS_TVENC_BASE + 0x000000e0)
+#define BM_TVENC_COPYPROTECT_WSS_ENBL 0x00008000
+#define BM_TVENC_COPYPROTECT_CGMS_ENBL 0x00004000
+#define BP_TVENC_COPYPROTECT_WSS_CGMS_DATA 0
+#define BM_TVENC_COPYPROTECT_WSS_CGMS_DATA 0x00003FFF
+#define BF_TVENC_COPYPROTECT_WSS_CGMS_DATA(v) \
+ (((v) << 0) & BM_TVENC_COPYPROTECT_WSS_CGMS_DATA)
+HW_REGISTER(HW_TVENC_CLOSEDCAPTION, REGS_TVENC_BASE, 0x000000f0)
+#define HW_TVENC_CLOSEDCAPTION_ADDR (REGS_TVENC_BASE + 0x000000f0)
+#define BP_TVENC_CLOSEDCAPTION_CC_ENBL 18
+#define BM_TVENC_CLOSEDCAPTION_CC_ENBL 0x000C0000
+#define BF_TVENC_CLOSEDCAPTION_CC_ENBL(v) \
+ (((v) << 18) & BM_TVENC_CLOSEDCAPTION_CC_ENBL)
+#define BP_TVENC_CLOSEDCAPTION_CC_FILL 16
+#define BM_TVENC_CLOSEDCAPTION_CC_FILL 0x00030000
+#define BF_TVENC_CLOSEDCAPTION_CC_FILL(v) \
+ (((v) << 16) & BM_TVENC_CLOSEDCAPTION_CC_FILL)
+#define BP_TVENC_CLOSEDCAPTION_CC_DATA 0
+#define BM_TVENC_CLOSEDCAPTION_CC_DATA 0x0000FFFF
+#define BF_TVENC_CLOSEDCAPTION_CC_DATA(v) \
+ (((v) << 0) & BM_TVENC_CLOSEDCAPTION_CC_DATA)
+HW_REGISTER(HW_TVENC_COLORBURST, REGS_TVENC_BASE, 0x00000140)
+#define HW_TVENC_COLORBURST_ADDR (REGS_TVENC_BASE + 0x00000140)
+#define BP_TVENC_COLORBURST_NBA 24
+#define BM_TVENC_COLORBURST_NBA 0xFF000000
+#define BF_TVENC_COLORBURST_NBA(v) \
+ (((v) << 24) & BM_TVENC_COLORBURST_NBA)
+#define BP_TVENC_COLORBURST_PBA 16
+#define BM_TVENC_COLORBURST_PBA 0x00FF0000
+#define BF_TVENC_COLORBURST_PBA(v) \
+ (((v) << 16) & BM_TVENC_COLORBURST_PBA)
+HW_REGISTER(HW_TVENC_MACROVISION0, REGS_TVENC_BASE, 0x00000150)
+#define HW_TVENC_MACROVISION0_ADDR (REGS_TVENC_BASE + 0x00000150)
+#define BP_TVENC_MACROVISION0_DATA 0
+#define BM_TVENC_MACROVISION0_DATA 0xFFFFFFFF
+#define BF_TVENC_MACROVISION0_DATA(v) (v)
+HW_REGISTER(HW_TVENC_MACROVISION1, REGS_TVENC_BASE, 0x00000160)
+#define HW_TVENC_MACROVISION1_ADDR (REGS_TVENC_BASE + 0x00000160)
+#define BP_TVENC_MACROVISION1_DATA 0
+#define BM_TVENC_MACROVISION1_DATA 0xFFFFFFFF
+#define BF_TVENC_MACROVISION1_DATA(v) (v)
+HW_REGISTER(HW_TVENC_MACROVISION2, REGS_TVENC_BASE, 0x00000170)
+#define HW_TVENC_MACROVISION2_ADDR (REGS_TVENC_BASE + 0x00000170)
+#define BP_TVENC_MACROVISION2_DATA 0
+#define BM_TVENC_MACROVISION2_DATA 0xFFFFFFFF
+#define BF_TVENC_MACROVISION2_DATA(v) (v)
+HW_REGISTER(HW_TVENC_MACROVISION3, REGS_TVENC_BASE, 0x00000180)
+#define HW_TVENC_MACROVISION3_ADDR (REGS_TVENC_BASE + 0x00000180)
+#define BP_TVENC_MACROVISION3_DATA 0
+#define BM_TVENC_MACROVISION3_DATA 0xFFFFFFFF
+#define BF_TVENC_MACROVISION3_DATA(v) (v)
+HW_REGISTER(HW_TVENC_MACROVISION4, REGS_TVENC_BASE, 0x00000190)
+#define HW_TVENC_MACROVISION4_ADDR (REGS_TVENC_BASE + 0x00000190)
+#define BP_TVENC_MACROVISION4_MACV_TST 16
+#define BM_TVENC_MACROVISION4_MACV_TST 0x00FF0000
+#define BF_TVENC_MACROVISION4_MACV_TST(v) \
+ (((v) << 16) & BM_TVENC_MACROVISION4_MACV_TST)
+#define BP_TVENC_MACROVISION4_DATA 0
+#define BM_TVENC_MACROVISION4_DATA 0x000007FF
+#define BF_TVENC_MACROVISION4_DATA(v) \
+ (((v) << 0) & BM_TVENC_MACROVISION4_DATA)
+HW_REGISTER(HW_TVENC_DACCTRL, REGS_TVENC_BASE, 0x000001a0)
+#define HW_TVENC_DACCTRL_ADDR (REGS_TVENC_BASE + 0x000001a0)
+#define BM_TVENC_DACCTRL_TEST3 0x80000000
+#define BM_TVENC_DACCTRL_JACK1_DIS_DET_EN 0x10000000
+#define BM_TVENC_DACCTRL_TEST2 0x08000000
+#define BM_TVENC_DACCTRL_JACK1_DET_EN 0x01000000
+#define BM_TVENC_DACCTRL_TEST1 0x00800000
+#define BM_TVENC_DACCTRL_DISABLE_GND_DETECT 0x00400000
+#define BP_TVENC_DACCTRL_JACK_DIS_ADJ 20
+#define BM_TVENC_DACCTRL_JACK_DIS_ADJ 0x00300000
+#define BF_TVENC_DACCTRL_JACK_DIS_ADJ(v) \
+ (((v) << 20) & BM_TVENC_DACCTRL_JACK_DIS_ADJ)
+#define BM_TVENC_DACCTRL_GAINDN 0x00080000
+#define BM_TVENC_DACCTRL_GAINUP 0x00040000
+#define BM_TVENC_DACCTRL_INVERT_CLK 0x00020000
+#define BM_TVENC_DACCTRL_SELECT_CLK 0x00010000
+#define BM_TVENC_DACCTRL_BYPASS_ACT_CASCODE 0x00008000
+#define BM_TVENC_DACCTRL_PWRUP1 0x00001000
+#define BM_TVENC_DACCTRL_WELL_TOVDD 0x00000800
+#define BM_TVENC_DACCTRL_DUMP_TOVDD1 0x00000100
+#define BM_TVENC_DACCTRL_LOWER_SIGNAL 0x00000080
+#define BP_TVENC_DACCTRL_RVAL 4
+#define BM_TVENC_DACCTRL_RVAL 0x00000070
+#define BF_TVENC_DACCTRL_RVAL(v) \
+ (((v) << 4) & BM_TVENC_DACCTRL_RVAL)
+#define BM_TVENC_DACCTRL_NO_INTERNAL_TERM 0x00000008
+#define BM_TVENC_DACCTRL_HALF_CURRENT 0x00000004
+#define BP_TVENC_DACCTRL_CASC_ADJ 0
+#define BM_TVENC_DACCTRL_CASC_ADJ 0x00000003
+#define BF_TVENC_DACCTRL_CASC_ADJ(v) \
+ (((v) << 0) & BM_TVENC_DACCTRL_CASC_ADJ)
+HW_REGISTER(HW_TVENC_DACSTATUS, REGS_TVENC_BASE, 0x000001b0)
+#define HW_TVENC_DACSTATUS_ADDR (REGS_TVENC_BASE + 0x000001b0)
+#define BM_TVENC_DACSTATUS_JACK1_DET_STATUS 0x00000400
+#define BM_TVENC_DACSTATUS_JACK1_GROUNDED 0x00000080
+#define BM_TVENC_DACSTATUS_JACK1_DIS_DET_IRQ 0x00000010
+#define BM_TVENC_DACSTATUS_JACK1_DET_IRQ 0x00000002
+#define BM_TVENC_DACSTATUS_ENIRQ_JACK 0x00000001
+HW_REGISTER(HW_TVENC_VDACTEST, REGS_TVENC_BASE, 0x000001c0)
+#define HW_TVENC_VDACTEST_ADDR (REGS_TVENC_BASE + 0x000001c0)
+#define BM_TVENC_VDACTEST_ENABLE_PIX_INT_GAIN 0x00002000
+#define BM_TVENC_VDACTEST_BYPASS_PIX_INT 0x00001000
+#define BM_TVENC_VDACTEST_BYPASS_PIX_INT_DROOP 0x00000800
+#define BM_TVENC_VDACTEST_TEST_FIFO_FULL 0x00000400
+#define BP_TVENC_VDACTEST_DATA 0
+#define BM_TVENC_VDACTEST_DATA 0x000003FF
+#define BF_TVENC_VDACTEST_DATA(v) \
+ (((v) << 0) & BM_TVENC_VDACTEST_DATA)
+HW_REGISTER_0(HW_TVENC_VERSION, REGS_TVENC_BASE, 0x000001d0)
+#define HW_TVENC_VERSION_ADDR (REGS_TVENC_BASE + 0x000001d0)
+#define BP_TVENC_VERSION_MAJOR 24
+#define BM_TVENC_VERSION_MAJOR 0xFF000000
+#define BF_TVENC_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_TVENC_VERSION_MAJOR)
+#define BP_TVENC_VERSION_MINOR 16
+#define BM_TVENC_VERSION_MINOR 0x00FF0000
+#define BF_TVENC_VERSION_MINOR(v) \
+ (((v) << 16) & BM_TVENC_VERSION_MINOR)
+#define BP_TVENC_VERSION_STEP 0
+#define BM_TVENC_VERSION_STEP 0x0000FFFF
+#define BF_TVENC_VERSION_STEP(v) \
+ (((v) << 0) & BM_TVENC_VERSION_STEP)
+#endif /* __ARCH_ARM___TVENC_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-uartapp.h b/arch/arm/mach-stmp3xxx/include/mach/regs-uartapp.h
new file mode 100644
index 000000000000..a4246dc4ff97
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-uartapp.h
@@ -0,0 +1,238 @@
+/*
+ * STMP UARTAPP Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___UARTAPP_H
+#define __ARCH_ARM___UARTAPP_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_UARTAPP_BASE (REGS_BASE + 0x6c000)
+#define REGS_UARTAPP1_BASE_PHYS (0x8006C000)
+#define REGS_UARTAPP2_BASE_PHYS (0x8006E000)
+#define REGS_UARTAPP_SIZE 0x00002000
+HW_REGISTER(HW_UARTAPP_CTRL0, REGS_UARTAPP_BASE, 0x00000000)
+#define HW_UARTAPP_CTRL0_ADDR (REGS_UARTAPP_BASE + 0x00000000)
+#define BM_UARTAPP_CTRL0_SFTRST 0x80000000
+#define BM_UARTAPP_CTRL0_CLKGATE 0x40000000
+#define BM_UARTAPP_CTRL0_RUN 0x20000000
+#define BM_UARTAPP_CTRL0_RX_SOURCE 0x10000000
+#define BM_UARTAPP_CTRL0_RXTO_ENABLE 0x08000000
+#define BP_UARTAPP_CTRL0_RXTIMEOUT 16
+#define BM_UARTAPP_CTRL0_RXTIMEOUT 0x07FF0000
+#define BF_UARTAPP_CTRL0_RXTIMEOUT(v) \
+ (((v) << 16) & BM_UARTAPP_CTRL0_RXTIMEOUT)
+#define BP_UARTAPP_CTRL0_XFER_COUNT 0
+#define BM_UARTAPP_CTRL0_XFER_COUNT 0x0000FFFF
+#define BF_UARTAPP_CTRL0_XFER_COUNT(v) \
+ (((v) << 0) & BM_UARTAPP_CTRL0_XFER_COUNT)
+HW_REGISTER(HW_UARTAPP_CTRL1, REGS_UARTAPP_BASE, 0x00000010)
+#define HW_UARTAPP_CTRL1_ADDR (REGS_UARTAPP_BASE + 0x00000010)
+#define BM_UARTAPP_CTRL1_RUN 0x10000000
+#define BP_UARTAPP_CTRL1_XFER_COUNT 0
+#define BM_UARTAPP_CTRL1_XFER_COUNT 0x0000FFFF
+#define BF_UARTAPP_CTRL1_XFER_COUNT(v) \
+ (((v) << 0) & BM_UARTAPP_CTRL1_XFER_COUNT)
+HW_REGISTER(HW_UARTAPP_CTRL2, REGS_UARTAPP_BASE, 0x00000020)
+#define HW_UARTAPP_CTRL2_ADDR (REGS_UARTAPP_BASE + 0x00000020)
+#define BM_UARTAPP_CTRL2_INVERT_RTS 0x80000000
+#define BM_UARTAPP_CTRL2_INVERT_CTS 0x40000000
+#define BM_UARTAPP_CTRL2_INVERT_TX 0x20000000
+#define BM_UARTAPP_CTRL2_INVERT_RX 0x10000000
+#define BM_UARTAPP_CTRL2_RTS_SEMAPHORE 0x08000000
+#define BM_UARTAPP_CTRL2_DMAONERR 0x04000000
+#define BM_UARTAPP_CTRL2_TXDMAE 0x02000000
+#define BM_UARTAPP_CTRL2_RXDMAE 0x01000000
+#define BP_UARTAPP_CTRL2_RXIFLSEL 20
+#define BM_UARTAPP_CTRL2_RXIFLSEL 0x00700000
+#define BF_UARTAPP_CTRL2_RXIFLSEL(v) \
+ (((v) << 20) & BM_UARTAPP_CTRL2_RXIFLSEL)
+#define BV_UARTAPP_CTRL2_RXIFLSEL__NOT_EMPTY 0x0
+#define BV_UARTAPP_CTRL2_RXIFLSEL__ONE_QUARTER 0x1
+#define BV_UARTAPP_CTRL2_RXIFLSEL__ONE_HALF 0x2
+#define BV_UARTAPP_CTRL2_RXIFLSEL__THREE_QUARTERS 0x3
+#define BV_UARTAPP_CTRL2_RXIFLSEL__SEVEN_EIGHTHS 0x4
+#define BV_UARTAPP_CTRL2_RXIFLSEL__INVALID5 0x5
+#define BV_UARTAPP_CTRL2_RXIFLSEL__INVALID6 0x6
+#define BV_UARTAPP_CTRL2_RXIFLSEL__INVALID7 0x7
+#define BP_UARTAPP_CTRL2_TXIFLSEL 16
+#define BM_UARTAPP_CTRL2_TXIFLSEL 0x00070000
+#define BF_UARTAPP_CTRL2_TXIFLSEL(v) \
+ (((v) << 16) & BM_UARTAPP_CTRL2_TXIFLSEL)
+#define BV_UARTAPP_CTRL2_TXIFLSEL__EMPTY 0x0
+#define BV_UARTAPP_CTRL2_TXIFLSEL__ONE_QUARTER 0x1
+#define BV_UARTAPP_CTRL2_TXIFLSEL__ONE_HALF 0x2
+#define BV_UARTAPP_CTRL2_TXIFLSEL__THREE_QUARTERS 0x3
+#define BV_UARTAPP_CTRL2_TXIFLSEL__SEVEN_EIGHTHS 0x4
+#define BV_UARTAPP_CTRL2_TXIFLSEL__INVALID5 0x5
+#define BV_UARTAPP_CTRL2_TXIFLSEL__INVALID6 0x6
+#define BV_UARTAPP_CTRL2_TXIFLSEL__INVALID7 0x7
+#define BM_UARTAPP_CTRL2_CTSEN 0x00008000
+#define BM_UARTAPP_CTRL2_RTSEN 0x00004000
+#define BM_UARTAPP_CTRL2_OUT2 0x00002000
+#define BM_UARTAPP_CTRL2_OUT1 0x00001000
+#define BM_UARTAPP_CTRL2_RTS 0x00000800
+#define BM_UARTAPP_CTRL2_DTR 0x00000400
+#define BM_UARTAPP_CTRL2_RXE 0x00000200
+#define BM_UARTAPP_CTRL2_TXE 0x00000100
+#define BM_UARTAPP_CTRL2_LBE 0x00000080
+#define BM_UARTAPP_CTRL2_USE_LCR2 0x00000040
+#define BM_UARTAPP_CTRL2_SIRLP 0x00000004
+#define BM_UARTAPP_CTRL2_SIREN 0x00000002
+#define BM_UARTAPP_CTRL2_UARTEN 0x00000001
+HW_REGISTER(HW_UARTAPP_LINECTRL, REGS_UARTAPP_BASE, 0x00000030)
+#define HW_UARTAPP_LINECTRL_ADDR (REGS_UARTAPP_BASE + 0x00000030)
+#define BP_UARTAPP_LINECTRL_BAUD_DIVINT 16
+#define BM_UARTAPP_LINECTRL_BAUD_DIVINT 0xFFFF0000
+#define BF_UARTAPP_LINECTRL_BAUD_DIVINT(v) \
+ (((v) << 16) & BM_UARTAPP_LINECTRL_BAUD_DIVINT)
+#define BP_UARTAPP_LINECTRL_BAUD_DIVFRAC 8
+#define BM_UARTAPP_LINECTRL_BAUD_DIVFRAC 0x00003F00
+#define BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(v) \
+ (((v) << 8) & BM_UARTAPP_LINECTRL_BAUD_DIVFRAC)
+#define BM_UARTAPP_LINECTRL_SPS 0x00000080
+#define BP_UARTAPP_LINECTRL_WLEN 5
+#define BM_UARTAPP_LINECTRL_WLEN 0x00000060
+#define BF_UARTAPP_LINECTRL_WLEN(v) \
+ (((v) << 5) & BM_UARTAPP_LINECTRL_WLEN)
+#define BM_UARTAPP_LINECTRL_FEN 0x00000010
+#define BM_UARTAPP_LINECTRL_STP2 0x00000008
+#define BM_UARTAPP_LINECTRL_EPS 0x00000004
+#define BM_UARTAPP_LINECTRL_PEN 0x00000002
+#define BM_UARTAPP_LINECTRL_BRK 0x00000001
+HW_REGISTER(HW_UARTAPP_LINECTRL2, REGS_UARTAPP_BASE, 0x00000040)
+#define HW_UARTAPP_LINECTRL2_ADDR (REGS_UARTAPP_BASE + 0x00000040)
+#define BP_UARTAPP_LINECTRL2_BAUD_DIVINT 16
+#define BM_UARTAPP_LINECTRL2_BAUD_DIVINT 0xFFFF0000
+#define BF_UARTAPP_LINECTRL2_BAUD_DIVINT(v) \
+ (((v) << 16) & BM_UARTAPP_LINECTRL2_BAUD_DIVINT)
+#define BP_UARTAPP_LINECTRL2_BAUD_DIVFRAC 8
+#define BM_UARTAPP_LINECTRL2_BAUD_DIVFRAC 0x00003F00
+#define BF_UARTAPP_LINECTRL2_BAUD_DIVFRAC(v) \
+ (((v) << 8) & BM_UARTAPP_LINECTRL2_BAUD_DIVFRAC)
+#define BM_UARTAPP_LINECTRL2_SPS 0x00000080
+#define BP_UARTAPP_LINECTRL2_WLEN 5
+#define BM_UARTAPP_LINECTRL2_WLEN 0x00000060
+#define BF_UARTAPP_LINECTRL2_WLEN(v) \
+ (((v) << 5) & BM_UARTAPP_LINECTRL2_WLEN)
+#define BM_UARTAPP_LINECTRL2_FEN 0x00000010
+#define BM_UARTAPP_LINECTRL2_STP2 0x00000008
+#define BM_UARTAPP_LINECTRL2_EPS 0x00000004
+#define BM_UARTAPP_LINECTRL2_PEN 0x00000002
+HW_REGISTER(HW_UARTAPP_INTR, REGS_UARTAPP_BASE, 0x00000050)
+#define HW_UARTAPP_INTR_ADDR (REGS_UARTAPP_BASE + 0x00000050)
+#define BM_UARTAPP_INTR_OEIEN 0x04000000
+#define BM_UARTAPP_INTR_BEIEN 0x02000000
+#define BM_UARTAPP_INTR_PEIEN 0x01000000
+#define BM_UARTAPP_INTR_FEIEN 0x00800000
+#define BM_UARTAPP_INTR_RTIEN 0x00400000
+#define BM_UARTAPP_INTR_TXIEN 0x00200000
+#define BM_UARTAPP_INTR_RXIEN 0x00100000
+#define BM_UARTAPP_INTR_DSRMIEN 0x00080000
+#define BM_UARTAPP_INTR_DCDMIEN 0x00040000
+#define BM_UARTAPP_INTR_CTSMIEN 0x00020000
+#define BM_UARTAPP_INTR_RIMIEN 0x00010000
+#define BM_UARTAPP_INTR_OEIS 0x00000400
+#define BM_UARTAPP_INTR_BEIS 0x00000200
+#define BM_UARTAPP_INTR_PEIS 0x00000100
+#define BM_UARTAPP_INTR_FEIS 0x00000080
+#define BM_UARTAPP_INTR_RTIS 0x00000040
+#define BM_UARTAPP_INTR_TXIS 0x00000020
+#define BM_UARTAPP_INTR_RXIS 0x00000010
+#define BM_UARTAPP_INTR_DSRMIS 0x00000008
+#define BM_UARTAPP_INTR_DCDMIS 0x00000004
+#define BM_UARTAPP_INTR_CTSMIS 0x00000002
+#define BM_UARTAPP_INTR_RIMIS 0x00000001
+HW_REGISTER_0(HW_UARTAPP_DATA, REGS_UARTAPP_BASE, 0x00000060)
+#define HW_UARTAPP_DATA_ADDR (REGS_UARTAPP_BASE + 0x00000060)
+#define BP_UARTAPP_DATA_DATA 0
+#define BM_UARTAPP_DATA_DATA 0xFFFFFFFF
+#define BF_UARTAPP_DATA_DATA(v) (v)
+HW_REGISTER_0(HW_UARTAPP_STAT, REGS_UARTAPP_BASE, 0x00000070)
+#define HW_UARTAPP_STAT_ADDR (REGS_UARTAPP_BASE + 0x00000070)
+#define BM_UARTAPP_STAT_PRESENT 0x80000000
+#define BV_UARTAPP_STAT_PRESENT__UNAVAILABLE 0x0
+#define BV_UARTAPP_STAT_PRESENT__AVAILABLE 0x1
+#define BM_UARTAPP_STAT_HISPEED 0x40000000
+#define BV_UARTAPP_STAT_HISPEED__UNAVAILABLE 0x0
+#define BV_UARTAPP_STAT_HISPEED__AVAILABLE 0x1
+#define BM_UARTAPP_STAT_BUSY 0x20000000
+#define BM_UARTAPP_STAT_CTS 0x10000000
+#define BM_UARTAPP_STAT_TXFE 0x08000000
+#define BM_UARTAPP_STAT_RXFF 0x04000000
+#define BM_UARTAPP_STAT_TXFF 0x02000000
+#define BM_UARTAPP_STAT_RXFE 0x01000000
+#define BP_UARTAPP_STAT_RXBYTE_INVALID 20
+#define BM_UARTAPP_STAT_RXBYTE_INVALID 0x00F00000
+#define BF_UARTAPP_STAT_RXBYTE_INVALID(v) \
+ (((v) << 20) & BM_UARTAPP_STAT_RXBYTE_INVALID)
+#define BM_UARTAPP_STAT_OERR 0x00080000
+#define BM_UARTAPP_STAT_BERR 0x00040000
+#define BM_UARTAPP_STAT_PERR 0x00020000
+#define BM_UARTAPP_STAT_FERR 0x00010000
+#define BP_UARTAPP_STAT_RXCOUNT 0
+#define BM_UARTAPP_STAT_RXCOUNT 0x0000FFFF
+#define BF_UARTAPP_STAT_RXCOUNT(v) \
+ (((v) << 0) & BM_UARTAPP_STAT_RXCOUNT)
+HW_REGISTER_0(HW_UARTAPP_DEBUG, REGS_UARTAPP_BASE, 0x00000080)
+#define HW_UARTAPP_DEBUG_ADDR (REGS_UARTAPP_BASE + 0x00000080)
+#define BP_UARTAPP_DEBUG_RXIBAUD_DIV 16
+#define BM_UARTAPP_DEBUG_RXIBAUD_DIV 0xFFFF0000
+#define BF_UARTAPP_DEBUG_RXIBAUD_DIV(v) \
+ (((v) << 16) & BM_UARTAPP_DEBUG_RXIBAUD_DIV)
+#define BP_UARTAPP_DEBUG_RXFBAUD_DIV 10
+#define BM_UARTAPP_DEBUG_RXFBAUD_DIV 0x0000FC00
+#define BF_UARTAPP_DEBUG_RXFBAUD_DIV(v) \
+ (((v) << 10) & BM_UARTAPP_DEBUG_RXFBAUD_DIV)
+#define BM_UARTAPP_DEBUG_TXDMARUN 0x00000020
+#define BM_UARTAPP_DEBUG_RXDMARUN 0x00000010
+#define BM_UARTAPP_DEBUG_TXCMDEND 0x00000008
+#define BM_UARTAPP_DEBUG_RXCMDEND 0x00000004
+#define BM_UARTAPP_DEBUG_TXDMARQ 0x00000002
+#define BM_UARTAPP_DEBUG_RXDMARQ 0x00000001
+HW_REGISTER_0(HW_UARTAPP_VERSION, REGS_UARTAPP_BASE, 0x00000090)
+#define HW_UARTAPP_VERSION_ADDR (REGS_UARTAPP_BASE + 0x00000090)
+#define BP_UARTAPP_VERSION_MAJOR 24
+#define BM_UARTAPP_VERSION_MAJOR 0xFF000000
+#define BF_UARTAPP_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_UARTAPP_VERSION_MAJOR)
+#define BP_UARTAPP_VERSION_MINOR 16
+#define BM_UARTAPP_VERSION_MINOR 0x00FF0000
+#define BF_UARTAPP_VERSION_MINOR(v) \
+ (((v) << 16) & BM_UARTAPP_VERSION_MINOR)
+#define BP_UARTAPP_VERSION_STEP 0
+#define BM_UARTAPP_VERSION_STEP 0x0000FFFF
+#define BF_UARTAPP_VERSION_STEP(v) \
+ (((v) << 0) & BM_UARTAPP_VERSION_STEP)
+HW_REGISTER_0(HW_UARTAPP_AUTOBAUD, REGS_UARTAPP_BASE, 0x000000a0)
+#define HW_UARTAPP_AUTOBAUD_ADDR (REGS_UARTAPP_BASE + 0x000000a0)
+#define BP_UARTAPP_AUTOBAUD_REFCHAR1 24
+#define BM_UARTAPP_AUTOBAUD_REFCHAR1 0xFF000000
+#define BF_UARTAPP_AUTOBAUD_REFCHAR1(v) \
+ (((v) << 24) & BM_UARTAPP_AUTOBAUD_REFCHAR1)
+#define BP_UARTAPP_AUTOBAUD_REFCHAR0 16
+#define BM_UARTAPP_AUTOBAUD_REFCHAR0 0x00FF0000
+#define BF_UARTAPP_AUTOBAUD_REFCHAR0(v) \
+ (((v) << 16) & BM_UARTAPP_AUTOBAUD_REFCHAR0)
+#define BM_UARTAPP_AUTOBAUD_UPDATE_TX 0x00000010
+#define BM_UARTAPP_AUTOBAUD_TWO_REF_CHARS 0x00000008
+#define BM_UARTAPP_AUTOBAUD_START_WITH_RUNBIT 0x00000004
+#define BM_UARTAPP_AUTOBAUD_START_BAUD_DETECT 0x00000002
+#define BM_UARTAPP_AUTOBAUD_BAUD_DETECT_ENABLE 0x00000001
+#endif /* __ARCH_ARM___UARTAPP_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-uartdbg.h b/arch/arm/mach-stmp3xxx/include/mach/regs-uartdbg.h
new file mode 100644
index 000000000000..172faef91577
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-uartdbg.h
@@ -0,0 +1,287 @@
+/*
+ * STMP UARTDBG Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___UARTDBG_H
+#define __ARCH_ARM___UARTDBG_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_UARTDBG_BASE (REGS_BASE + 0x70000)
+#define REGS_UARTDBG_BASE_PHYS (0x80070000)
+#define REGS_UARTDBG_SIZE 0x00002000
+HW_REGISTER_0(HW_UARTDBGDR, REGS_UARTDBG_BASE, 0x00000000)
+#define HW_UARTDBGDR_ADDR (REGS_UARTDBG_BASE + 0x00000000)
+#define BP_UARTDBGDR_UNAVAILABLE 16
+#define BM_UARTDBGDR_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGDR_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGDR_UNAVAILABLE)
+#define BP_UARTDBGDR_RESERVED 12
+#define BM_UARTDBGDR_RESERVED 0x0000F000
+#define BF_UARTDBGDR_RESERVED(v) \
+ (((v) << 12) & BM_UARTDBGDR_RESERVED)
+#define BM_UARTDBGDR_OE 0x00000800
+#define BM_UARTDBGDR_BE 0x00000400
+#define BM_UARTDBGDR_PE 0x00000200
+#define BM_UARTDBGDR_FE 0x00000100
+#define BP_UARTDBGDR_DATA 0
+#define BM_UARTDBGDR_DATA 0x000000FF
+#define BF_UARTDBGDR_DATA(v) \
+ (((v) << 0) & BM_UARTDBGDR_DATA)
+HW_REGISTER_0(HW_UARTDBGRSR_ECR, REGS_UARTDBG_BASE, 0x00000004)
+#define HW_UARTDBGRSR_ECR_ADDR (REGS_UARTDBG_BASE + 0x00000004)
+#define BP_UARTDBGRSR_ECR_UNAVAILABLE 8
+#define BM_UARTDBGRSR_ECR_UNAVAILABLE 0xFFFFFF00
+#define BF_UARTDBGRSR_ECR_UNAVAILABLE(v) \
+ (((v) << 8) & BM_UARTDBGRSR_ECR_UNAVAILABLE)
+#define BP_UARTDBGRSR_ECR_EC 4
+#define BM_UARTDBGRSR_ECR_EC 0x000000F0
+#define BF_UARTDBGRSR_ECR_EC(v) \
+ (((v) << 4) & BM_UARTDBGRSR_ECR_EC)
+#define BM_UARTDBGRSR_ECR_OE 0x00000008
+#define BM_UARTDBGRSR_ECR_BE 0x00000004
+#define BM_UARTDBGRSR_ECR_PE 0x00000002
+#define BM_UARTDBGRSR_ECR_FE 0x00000001
+HW_REGISTER_0(HW_UARTDBGFR, REGS_UARTDBG_BASE, 0x00000018)
+#define HW_UARTDBGFR_ADDR (REGS_UARTDBG_BASE + 0x00000018)
+#define BP_UARTDBGFR_UNAVAILABLE 16
+#define BM_UARTDBGFR_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGFR_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGFR_UNAVAILABLE)
+#define BP_UARTDBGFR_RESERVED 9
+#define BM_UARTDBGFR_RESERVED 0x0000FE00
+#define BF_UARTDBGFR_RESERVED(v) \
+ (((v) << 9) & BM_UARTDBGFR_RESERVED)
+#define BM_UARTDBGFR_RI 0x00000100
+#define BM_UARTDBGFR_TXFE 0x00000080
+#define BM_UARTDBGFR_RXFF 0x00000040
+#define BM_UARTDBGFR_TXFF 0x00000020
+#define BM_UARTDBGFR_RXFE 0x00000010
+#define BM_UARTDBGFR_BUSY 0x00000008
+#define BM_UARTDBGFR_DCD 0x00000004
+#define BM_UARTDBGFR_DSR 0x00000002
+#define BM_UARTDBGFR_CTS 0x00000001
+HW_REGISTER_0(HW_UARTDBGILPR, REGS_UARTDBG_BASE, 0x00000020)
+#define HW_UARTDBGILPR_ADDR (REGS_UARTDBG_BASE + 0x00000020)
+#define BP_UARTDBGILPR_UNAVAILABLE 8
+#define BM_UARTDBGILPR_UNAVAILABLE 0xFFFFFF00
+#define BF_UARTDBGILPR_UNAVAILABLE(v) \
+ (((v) << 8) & BM_UARTDBGILPR_UNAVAILABLE)
+#define BP_UARTDBGILPR_ILPDVSR 0
+#define BM_UARTDBGILPR_ILPDVSR 0x000000FF
+#define BF_UARTDBGILPR_ILPDVSR(v) \
+ (((v) << 0) & BM_UARTDBGILPR_ILPDVSR)
+HW_REGISTER_0(HW_UARTDBGIBRD, REGS_UARTDBG_BASE, 0x00000024)
+#define HW_UARTDBGIBRD_ADDR (REGS_UARTDBG_BASE + 0x00000024)
+#define BP_UARTDBGIBRD_UNAVAILABLE 16
+#define BM_UARTDBGIBRD_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGIBRD_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGIBRD_UNAVAILABLE)
+#define BP_UARTDBGIBRD_BAUD_DIVINT 0
+#define BM_UARTDBGIBRD_BAUD_DIVINT 0x0000FFFF
+#define BF_UARTDBGIBRD_BAUD_DIVINT(v) \
+ (((v) << 0) & BM_UARTDBGIBRD_BAUD_DIVINT)
+HW_REGISTER_0(HW_UARTDBGFBRD, REGS_UARTDBG_BASE, 0x00000028)
+#define HW_UARTDBGFBRD_ADDR (REGS_UARTDBG_BASE + 0x00000028)
+#define BP_UARTDBGFBRD_UNAVAILABLE 8
+#define BM_UARTDBGFBRD_UNAVAILABLE 0xFFFFFF00
+#define BF_UARTDBGFBRD_UNAVAILABLE(v) \
+ (((v) << 8) & BM_UARTDBGFBRD_UNAVAILABLE)
+#define BP_UARTDBGFBRD_RESERVED 6
+#define BM_UARTDBGFBRD_RESERVED 0x000000C0
+#define BF_UARTDBGFBRD_RESERVED(v) \
+ (((v) << 6) & BM_UARTDBGFBRD_RESERVED)
+#define BP_UARTDBGFBRD_BAUD_DIVFRAC 0
+#define BM_UARTDBGFBRD_BAUD_DIVFRAC 0x0000003F
+#define BF_UARTDBGFBRD_BAUD_DIVFRAC(v) \
+ (((v) << 0) & BM_UARTDBGFBRD_BAUD_DIVFRAC)
+HW_REGISTER_0(HW_UARTDBGLCR_H, REGS_UARTDBG_BASE, 0x0000002c)
+#define HW_UARTDBGLCR_H_ADDR (REGS_UARTDBG_BASE + 0x0000002c)
+#define BP_UARTDBGLCR_H_UNAVAILABLE 16
+#define BM_UARTDBGLCR_H_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGLCR_H_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGLCR_H_UNAVAILABLE)
+#define BP_UARTDBGLCR_H_RESERVED 8
+#define BM_UARTDBGLCR_H_RESERVED 0x0000FF00
+#define BF_UARTDBGLCR_H_RESERVED(v) \
+ (((v) << 8) & BM_UARTDBGLCR_H_RESERVED)
+#define BM_UARTDBGLCR_H_SPS 0x00000080
+#define BP_UARTDBGLCR_H_WLEN 5
+#define BM_UARTDBGLCR_H_WLEN 0x00000060
+#define BF_UARTDBGLCR_H_WLEN(v) \
+ (((v) << 5) & BM_UARTDBGLCR_H_WLEN)
+#define BM_UARTDBGLCR_H_FEN 0x00000010
+#define BM_UARTDBGLCR_H_STP2 0x00000008
+#define BM_UARTDBGLCR_H_EPS 0x00000004
+#define BM_UARTDBGLCR_H_PEN 0x00000002
+#define BM_UARTDBGLCR_H_BRK 0x00000001
+HW_REGISTER_0(HW_UARTDBGCR, REGS_UARTDBG_BASE, 0x00000030)
+#define HW_UARTDBGCR_ADDR (REGS_UARTDBG_BASE + 0x00000030)
+#define BP_UARTDBGCR_UNAVAILABLE 16
+#define BM_UARTDBGCR_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGCR_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGCR_UNAVAILABLE)
+#define BM_UARTDBGCR_CTSEN 0x00008000
+#define BM_UARTDBGCR_RTSEN 0x00004000
+#define BM_UARTDBGCR_OUT2 0x00002000
+#define BM_UARTDBGCR_OUT1 0x00001000
+#define BM_UARTDBGCR_RTS 0x00000800
+#define BM_UARTDBGCR_DTR 0x00000400
+#define BM_UARTDBGCR_RXE 0x00000200
+#define BM_UARTDBGCR_TXE 0x00000100
+#define BM_UARTDBGCR_LBE 0x00000080
+#define BP_UARTDBGCR_RESERVED 3
+#define BM_UARTDBGCR_RESERVED 0x00000078
+#define BF_UARTDBGCR_RESERVED(v) \
+ (((v) << 3) & BM_UARTDBGCR_RESERVED)
+#define BM_UARTDBGCR_SIRLP 0x00000004
+#define BM_UARTDBGCR_SIREN 0x00000002
+#define BM_UARTDBGCR_UARTEN 0x00000001
+HW_REGISTER_0(HW_UARTDBGIFLS, REGS_UARTDBG_BASE, 0x00000034)
+#define HW_UARTDBGIFLS_ADDR (REGS_UARTDBG_BASE + 0x00000034)
+#define BP_UARTDBGIFLS_UNAVAILABLE 16
+#define BM_UARTDBGIFLS_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGIFLS_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGIFLS_UNAVAILABLE)
+#define BP_UARTDBGIFLS_RESERVED 6
+#define BM_UARTDBGIFLS_RESERVED 0x0000FFC0
+#define BF_UARTDBGIFLS_RESERVED(v) \
+ (((v) << 6) & BM_UARTDBGIFLS_RESERVED)
+#define BP_UARTDBGIFLS_RXIFLSEL 3
+#define BM_UARTDBGIFLS_RXIFLSEL 0x00000038
+#define BF_UARTDBGIFLS_RXIFLSEL(v) \
+ (((v) << 3) & BM_UARTDBGIFLS_RXIFLSEL)
+#define BV_UARTDBGIFLS_RXIFLSEL__NOT_EMPTY 0x0
+#define BV_UARTDBGIFLS_RXIFLSEL__ONE_QUARTER 0x1
+#define BV_UARTDBGIFLS_RXIFLSEL__ONE_HALF 0x2
+#define BV_UARTDBGIFLS_RXIFLSEL__THREE_QUARTERS 0x3
+#define BV_UARTDBGIFLS_RXIFLSEL__SEVEN_EIGHTHS 0x4
+#define BV_UARTDBGIFLS_RXIFLSEL__INVALID5 0x5
+#define BV_UARTDBGIFLS_RXIFLSEL__INVALID6 0x6
+#define BV_UARTDBGIFLS_RXIFLSEL__INVALID7 0x7
+#define BP_UARTDBGIFLS_TXIFLSEL 0
+#define BM_UARTDBGIFLS_TXIFLSEL 0x00000007
+#define BF_UARTDBGIFLS_TXIFLSEL(v) \
+ (((v) << 0) & BM_UARTDBGIFLS_TXIFLSEL)
+#define BV_UARTDBGIFLS_TXIFLSEL__EMPTY 0x0
+#define BV_UARTDBGIFLS_TXIFLSEL__ONE_QUARTER 0x1
+#define BV_UARTDBGIFLS_TXIFLSEL__ONE_HALF 0x2
+#define BV_UARTDBGIFLS_TXIFLSEL__THREE_QUARTERS 0x3
+#define BV_UARTDBGIFLS_TXIFLSEL__SEVEN_EIGHTHS 0x4
+#define BV_UARTDBGIFLS_TXIFLSEL__INVALID5 0x5
+#define BV_UARTDBGIFLS_TXIFLSEL__INVALID6 0x6
+#define BV_UARTDBGIFLS_TXIFLSEL__INVALID7 0x7
+HW_REGISTER_0(HW_UARTDBGIMSC, REGS_UARTDBG_BASE, 0x00000038)
+#define HW_UARTDBGIMSC_ADDR (REGS_UARTDBG_BASE + 0x00000038)
+#define BP_UARTDBGIMSC_UNAVAILABLE 16
+#define BM_UARTDBGIMSC_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGIMSC_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGIMSC_UNAVAILABLE)
+#define BP_UARTDBGIMSC_RESERVED 11
+#define BM_UARTDBGIMSC_RESERVED 0x0000F800
+#define BF_UARTDBGIMSC_RESERVED(v) \
+ (((v) << 11) & BM_UARTDBGIMSC_RESERVED)
+#define BM_UARTDBGIMSC_OEIM 0x00000400
+#define BM_UARTDBGIMSC_BEIM 0x00000200
+#define BM_UARTDBGIMSC_PEIM 0x00000100
+#define BM_UARTDBGIMSC_FEIM 0x00000080
+#define BM_UARTDBGIMSC_RTIM 0x00000040
+#define BM_UARTDBGIMSC_TXIM 0x00000020
+#define BM_UARTDBGIMSC_RXIM 0x00000010
+#define BM_UARTDBGIMSC_DSRMIM 0x00000008
+#define BM_UARTDBGIMSC_DCDMIM 0x00000004
+#define BM_UARTDBGIMSC_CTSMIM 0x00000002
+#define BM_UARTDBGIMSC_RIMIM 0x00000001
+HW_REGISTER_0(HW_UARTDBGRIS, REGS_UARTDBG_BASE, 0x0000003c)
+#define HW_UARTDBGRIS_ADDR (REGS_UARTDBG_BASE + 0x0000003c)
+#define BP_UARTDBGRIS_UNAVAILABLE 16
+#define BM_UARTDBGRIS_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGRIS_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGRIS_UNAVAILABLE)
+#define BP_UARTDBGRIS_RESERVED 11
+#define BM_UARTDBGRIS_RESERVED 0x0000F800
+#define BF_UARTDBGRIS_RESERVED(v) \
+ (((v) << 11) & BM_UARTDBGRIS_RESERVED)
+#define BM_UARTDBGRIS_OERIS 0x00000400
+#define BM_UARTDBGRIS_BERIS 0x00000200
+#define BM_UARTDBGRIS_PERIS 0x00000100
+#define BM_UARTDBGRIS_FERIS 0x00000080
+#define BM_UARTDBGRIS_RTRIS 0x00000040
+#define BM_UARTDBGRIS_TXRIS 0x00000020
+#define BM_UARTDBGRIS_RXRIS 0x00000010
+#define BM_UARTDBGRIS_DSRRMIS 0x00000008
+#define BM_UARTDBGRIS_DCDRMIS 0x00000004
+#define BM_UARTDBGRIS_CTSRMIS 0x00000002
+#define BM_UARTDBGRIS_RIRMIS 0x00000001
+HW_REGISTER_0(HW_UARTDBGMIS, REGS_UARTDBG_BASE, 0x00000040)
+#define HW_UARTDBGMIS_ADDR (REGS_UARTDBG_BASE + 0x00000040)
+#define BP_UARTDBGMIS_UNAVAILABLE 16
+#define BM_UARTDBGMIS_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGMIS_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGMIS_UNAVAILABLE)
+#define BP_UARTDBGMIS_RESERVED 11
+#define BM_UARTDBGMIS_RESERVED 0x0000F800
+#define BF_UARTDBGMIS_RESERVED(v) \
+ (((v) << 11) & BM_UARTDBGMIS_RESERVED)
+#define BM_UARTDBGMIS_OEMIS 0x00000400
+#define BM_UARTDBGMIS_BEMIS 0x00000200
+#define BM_UARTDBGMIS_PEMIS 0x00000100
+#define BM_UARTDBGMIS_FEMIS 0x00000080
+#define BM_UARTDBGMIS_RTMIS 0x00000040
+#define BM_UARTDBGMIS_TXMIS 0x00000020
+#define BM_UARTDBGMIS_RXMIS 0x00000010
+#define BM_UARTDBGMIS_DSRMMIS 0x00000008
+#define BM_UARTDBGMIS_DCDMMIS 0x00000004
+#define BM_UARTDBGMIS_CTSMMIS 0x00000002
+#define BM_UARTDBGMIS_RIMMIS 0x00000001
+HW_REGISTER_0(HW_UARTDBGICR, REGS_UARTDBG_BASE, 0x00000044)
+#define HW_UARTDBGICR_ADDR (REGS_UARTDBG_BASE + 0x00000044)
+#define BP_UARTDBGICR_UNAVAILABLE 16
+#define BM_UARTDBGICR_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGICR_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGICR_UNAVAILABLE)
+#define BP_UARTDBGICR_RESERVED 11
+#define BM_UARTDBGICR_RESERVED 0x0000F800
+#define BF_UARTDBGICR_RESERVED(v) \
+ (((v) << 11) & BM_UARTDBGICR_RESERVED)
+#define BM_UARTDBGICR_OEIC 0x00000400
+#define BM_UARTDBGICR_BEIC 0x00000200
+#define BM_UARTDBGICR_PEIC 0x00000100
+#define BM_UARTDBGICR_FEIC 0x00000080
+#define BM_UARTDBGICR_RTIC 0x00000040
+#define BM_UARTDBGICR_TXIC 0x00000020
+#define BM_UARTDBGICR_RXIC 0x00000010
+#define BM_UARTDBGICR_DSRMIC 0x00000008
+#define BM_UARTDBGICR_DCDMIC 0x00000004
+#define BM_UARTDBGICR_CTSMIC 0x00000002
+#define BM_UARTDBGICR_RIMIC 0x00000001
+HW_REGISTER_0(HW_UARTDBGDMACR, REGS_UARTDBG_BASE, 0x00000048)
+#define HW_UARTDBGDMACR_ADDR (REGS_UARTDBG_BASE + 0x00000048)
+#define BP_UARTDBGDMACR_UNAVAILABLE 16
+#define BM_UARTDBGDMACR_UNAVAILABLE 0xFFFF0000
+#define BF_UARTDBGDMACR_UNAVAILABLE(v) \
+ (((v) << 16) & BM_UARTDBGDMACR_UNAVAILABLE)
+#define BP_UARTDBGDMACR_RESERVED 3
+#define BM_UARTDBGDMACR_RESERVED 0x0000FFF8
+#define BF_UARTDBGDMACR_RESERVED(v) \
+ (((v) << 3) & BM_UARTDBGDMACR_RESERVED)
+#define BM_UARTDBGDMACR_DMAONERR 0x00000004
+#define BM_UARTDBGDMACR_TXDMAE 0x00000002
+#define BM_UARTDBGDMACR_RXDMAE 0x00000001
+#endif /* __ARCH_ARM___UARTDBG_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-usbctrl.h b/arch/arm/mach-stmp3xxx/include/mach/regs-usbctrl.h
new file mode 100644
index 000000000000..daf0f0c94fb3
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-usbctrl.h
@@ -0,0 +1,642 @@
+/*
+ * STMP USBCTRL Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___USBCTRL_H
+#define __ARCH_ARM___USBCTRL_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_USBCTRL_BASE (REGS_BASE + 0x80000)
+#define REGS_USBCTRL_BASE_PHYS (0x80080000)
+#define REGS_USBCTRL_SIZE 0x00002000
+HW_REGISTER_0(HW_USBCTRL_ID, REGS_USBCTRL_BASE, 0x00000000)
+#define HW_USBCTRL_ID_ADDR (REGS_USBCTRL_BASE + 0x00000000)
+#define BP_USBCTRL_ID_CIVERSION 29
+#define BM_USBCTRL_ID_CIVERSION 0xE0000000
+#define BF_USBCTRL_ID_CIVERSION(v) \
+ (((v) << 29) & BM_USBCTRL_ID_CIVERSION)
+#define BP_USBCTRL_ID_VERSION 25
+#define BM_USBCTRL_ID_VERSION 0x1E000000
+#define BF_USBCTRL_ID_VERSION(v) \
+ (((v) << 25) & BM_USBCTRL_ID_VERSION)
+#define BP_USBCTRL_ID_REVISION 21
+#define BM_USBCTRL_ID_REVISION 0x01E00000
+#define BF_USBCTRL_ID_REVISION(v) \
+ (((v) << 21) & BM_USBCTRL_ID_REVISION)
+#define BP_USBCTRL_ID_TAG 16
+#define BM_USBCTRL_ID_TAG 0x001F0000
+#define BF_USBCTRL_ID_TAG(v) \
+ (((v) << 16) & BM_USBCTRL_ID_TAG)
+#define BP_USBCTRL_ID_NID 8
+#define BM_USBCTRL_ID_NID 0x00003F00
+#define BF_USBCTRL_ID_NID(v) \
+ (((v) << 8) & BM_USBCTRL_ID_NID)
+#define BP_USBCTRL_ID_ID 0
+#define BM_USBCTRL_ID_ID 0x0000003F
+#define BF_USBCTRL_ID_ID(v) \
+ (((v) << 0) & BM_USBCTRL_ID_ID)
+HW_REGISTER_0(HW_USBCTRL_HWGENERAL, REGS_USBCTRL_BASE, 0x00000004)
+#define HW_USBCTRL_HWGENERAL_ADDR (REGS_USBCTRL_BASE + 0x00000004)
+#define BP_USBCTRL_HWGENERAL_SM 9
+#define BM_USBCTRL_HWGENERAL_SM 0x00000600
+#define BF_USBCTRL_HWGENERAL_SM(v) \
+ (((v) << 9) & BM_USBCTRL_HWGENERAL_SM)
+#define BP_USBCTRL_HWGENERAL_PHYM 6
+#define BM_USBCTRL_HWGENERAL_PHYM 0x000001C0
+#define BF_USBCTRL_HWGENERAL_PHYM(v) \
+ (((v) << 6) & BM_USBCTRL_HWGENERAL_PHYM)
+#define BP_USBCTRL_HWGENERAL_PHYW 4
+#define BM_USBCTRL_HWGENERAL_PHYW 0x00000030
+#define BF_USBCTRL_HWGENERAL_PHYW(v) \
+ (((v) << 4) & BM_USBCTRL_HWGENERAL_PHYW)
+#define BM_USBCTRL_HWGENERAL_BWT 0x00000008
+#define BP_USBCTRL_HWGENERAL_CLKC 1
+#define BM_USBCTRL_HWGENERAL_CLKC 0x00000006
+#define BF_USBCTRL_HWGENERAL_CLKC(v) \
+ (((v) << 1) & BM_USBCTRL_HWGENERAL_CLKC)
+#define BM_USBCTRL_HWGENERAL_RT 0x00000001
+HW_REGISTER_0(HW_USBCTRL_HWHOST, REGS_USBCTRL_BASE, 0x00000008)
+#define HW_USBCTRL_HWHOST_ADDR (REGS_USBCTRL_BASE + 0x00000008)
+#define BP_USBCTRL_HWHOST_TTPER 24
+#define BM_USBCTRL_HWHOST_TTPER 0xFF000000
+#define BF_USBCTRL_HWHOST_TTPER(v) \
+ (((v) << 24) & BM_USBCTRL_HWHOST_TTPER)
+#define BP_USBCTRL_HWHOST_TTASY 16
+#define BM_USBCTRL_HWHOST_TTASY 0x00FF0000
+#define BF_USBCTRL_HWHOST_TTASY(v) \
+ (((v) << 16) & BM_USBCTRL_HWHOST_TTASY)
+#define BP_USBCTRL_HWHOST_NPORT 1
+#define BM_USBCTRL_HWHOST_NPORT 0x0000000E
+#define BF_USBCTRL_HWHOST_NPORT(v) \
+ (((v) << 1) & BM_USBCTRL_HWHOST_NPORT)
+#define BM_USBCTRL_HWHOST_HC 0x00000001
+HW_REGISTER_0(HW_USBCTRL_HWDEVICE, REGS_USBCTRL_BASE, 0x0000000c)
+#define HW_USBCTRL_HWDEVICE_ADDR (REGS_USBCTRL_BASE + 0x0000000c)
+#define BP_USBCTRL_HWDEVICE_DEVEP 1
+#define BM_USBCTRL_HWDEVICE_DEVEP 0x0000003E
+#define BF_USBCTRL_HWDEVICE_DEVEP(v) \
+ (((v) << 1) & BM_USBCTRL_HWDEVICE_DEVEP)
+#define BM_USBCTRL_HWDEVICE_DC 0x00000001
+HW_REGISTER_0(HW_USBCTRL_HWTXBUF, REGS_USBCTRL_BASE, 0x00000010)
+#define HW_USBCTRL_HWTXBUF_ADDR (REGS_USBCTRL_BASE + 0x00000010)
+#define BM_USBCTRL_HWTXBUF_TXLCR 0x80000000
+#define BP_USBCTRL_HWTXBUF_TXCHANADD 16
+#define BM_USBCTRL_HWTXBUF_TXCHANADD 0x00FF0000
+#define BF_USBCTRL_HWTXBUF_TXCHANADD(v) \
+ (((v) << 16) & BM_USBCTRL_HWTXBUF_TXCHANADD)
+#define BP_USBCTRL_HWTXBUF_TXADD 8
+#define BM_USBCTRL_HWTXBUF_TXADD 0x0000FF00
+#define BF_USBCTRL_HWTXBUF_TXADD(v) \
+ (((v) << 8) & BM_USBCTRL_HWTXBUF_TXADD)
+#define BP_USBCTRL_HWTXBUF_TXBURST 0
+#define BM_USBCTRL_HWTXBUF_TXBURST 0x000000FF
+#define BF_USBCTRL_HWTXBUF_TXBURST(v) \
+ (((v) << 0) & BM_USBCTRL_HWTXBUF_TXBURST)
+HW_REGISTER_0(HW_USBCTRL_HWRXBUF, REGS_USBCTRL_BASE, 0x00000014)
+#define HW_USBCTRL_HWRXBUF_ADDR (REGS_USBCTRL_BASE + 0x00000014)
+#define BP_USBCTRL_HWRXBUF_RXADD 8
+#define BM_USBCTRL_HWRXBUF_RXADD 0x0000FF00
+#define BF_USBCTRL_HWRXBUF_RXADD(v) \
+ (((v) << 8) & BM_USBCTRL_HWRXBUF_RXADD)
+#define BP_USBCTRL_HWRXBUF_RXBURST 0
+#define BM_USBCTRL_HWRXBUF_RXBURST 0x000000FF
+#define BF_USBCTRL_HWRXBUF_RXBURST(v) \
+ (((v) << 0) & BM_USBCTRL_HWRXBUF_RXBURST)
+HW_REGISTER_0(HW_USBCTRL_GPTIMER0LD, REGS_USBCTRL_BASE, 0x00000080)
+#define HW_USBCTRL_GPTIMER0LD_ADDR (REGS_USBCTRL_BASE + 0x00000080)
+#define BP_USBCTRL_GPTIMER0LD_GPTLD 0
+#define BM_USBCTRL_GPTIMER0LD_GPTLD 0x00FFFFFF
+#define BF_USBCTRL_GPTIMER0LD_GPTLD(v) \
+ (((v) << 0) & BM_USBCTRL_GPTIMER0LD_GPTLD)
+HW_REGISTER_0(HW_USBCTRL_GPTIMER0CTRL, REGS_USBCTRL_BASE, 0x00000084)
+#define HW_USBCTRL_GPTIMER0CTRL_ADDR (REGS_USBCTRL_BASE + 0x00000084)
+#define BM_USBCTRL_GPTIMER0CTRL_GPTRUN 0x80000000
+#define BV_USBCTRL_GPTIMER0CTRL_GPTRUN__STOP 0
+#define BV_USBCTRL_GPTIMER0CTRL_GPTRUN__RUN 1
+#define BM_USBCTRL_GPTIMER0CTRL_GPTRST 0x40000000
+#define BV_USBCTRL_GPTIMER0CTRL_GPTRST__NOACTION 0
+#define BV_USBCTRL_GPTIMER0CTRL_GPTRST__LOADCOUNTER 1
+#define BM_USBCTRL_GPTIMER0CTRL_GPTMODE 0x01000000
+#define BV_USBCTRL_GPTIMER0CTRL_GPTMODE__ONESHOT 0
+#define BV_USBCTRL_GPTIMER0CTRL_GPTMODE__REPEAT 1
+#define BP_USBCTRL_GPTIMER0CTRL_GPTCNT 0
+#define BM_USBCTRL_GPTIMER0CTRL_GPTCNT 0x00FFFFFF
+#define BF_USBCTRL_GPTIMER0CTRL_GPTCNT(v) \
+ (((v) << 0) & BM_USBCTRL_GPTIMER0CTRL_GPTCNT)
+HW_REGISTER_0(HW_USBCTRL_GPTIMER1LD, REGS_USBCTRL_BASE, 0x00000088)
+#define HW_USBCTRL_GPTIMER1LD_ADDR (REGS_USBCTRL_BASE + 0x00000088)
+#define BP_USBCTRL_GPTIMER1LD_GPTLD 0
+#define BM_USBCTRL_GPTIMER1LD_GPTLD 0x00FFFFFF
+#define BF_USBCTRL_GPTIMER1LD_GPTLD(v) \
+ (((v) << 0) & BM_USBCTRL_GPTIMER1LD_GPTLD)
+HW_REGISTER_0(HW_USBCTRL_GPTIMER1CTRL, REGS_USBCTRL_BASE, 0x0000008c)
+#define HW_USBCTRL_GPTIMER1CTRL_ADDR (REGS_USBCTRL_BASE + 0x0000008c)
+#define BM_USBCTRL_GPTIMER1CTRL_GPTRUN 0x80000000
+#define BV_USBCTRL_GPTIMER1CTRL_GPTRUN__STOP 0
+#define BV_USBCTRL_GPTIMER1CTRL_GPTRUN__RUN 1
+#define BM_USBCTRL_GPTIMER1CTRL_GPTRST 0x40000000
+#define BV_USBCTRL_GPTIMER1CTRL_GPTRST__NOACTION 0
+#define BV_USBCTRL_GPTIMER1CTRL_GPTRST__LOADCOUNTER 1
+#define BM_USBCTRL_GPTIMER1CTRL_GPTMODE 0x01000000
+#define BV_USBCTRL_GPTIMER1CTRL_GPTMODE__ONESHOT 0
+#define BV_USBCTRL_GPTIMER1CTRL_GPTMODE__REPEAT 1
+#define BP_USBCTRL_GPTIMER1CTRL_GPTCNT 0
+#define BM_USBCTRL_GPTIMER1CTRL_GPTCNT 0x00FFFFFF
+#define BF_USBCTRL_GPTIMER1CTRL_GPTCNT(v) \
+ (((v) << 0) & BM_USBCTRL_GPTIMER1CTRL_GPTCNT)
+HW_REGISTER_0(HW_USBCTRL_SBUSCFG, REGS_USBCTRL_BASE, 0x00000090)
+#define HW_USBCTRL_SBUSCFG_ADDR (REGS_USBCTRL_BASE + 0x00000090)
+#define BP_USBCTRL_SBUSCFG_AHBBRST 0
+#define BM_USBCTRL_SBUSCFG_AHBBRST 0x00000007
+#define BF_USBCTRL_SBUSCFG_AHBBRST(v) \
+ (((v) << 0) & BM_USBCTRL_SBUSCFG_AHBBRST)
+#define BV_USBCTRL_SBUSCFG_AHBBRST__U_INCR 0x0
+#define BV_USBCTRL_SBUSCFG_AHBBRST__S_INCR4 0x1
+#define BV_USBCTRL_SBUSCFG_AHBBRST__S_INCR8 0x2
+#define BV_USBCTRL_SBUSCFG_AHBBRST__S_INCR16 0x3
+#define BV_USBCTRL_SBUSCFG_AHBBRST__RESERVED 0x4
+#define BV_USBCTRL_SBUSCFG_AHBBRST__U_INCR4 0x5
+#define BV_USBCTRL_SBUSCFG_AHBBRST__U_INCR8 0x6
+#define BV_USBCTRL_SBUSCFG_AHBBRST__U_INCR16 0x7
+HW_REGISTER_0(HW_USBCTRL_CAPLENGTH, REGS_USBCTRL_BASE, 0x00000100)
+#define HW_USBCTRL_CAPLENGTH_ADDR (REGS_USBCTRL_BASE + 0x00000100)
+#define BP_USBCTRL_CAPLENGTH_HCIVERSION 16
+#define BM_USBCTRL_CAPLENGTH_HCIVERSION 0xFFFF0000
+#define BF_USBCTRL_CAPLENGTH_HCIVERSION(v) \
+ (((v) << 16) & BM_USBCTRL_CAPLENGTH_HCIVERSION)
+#define BP_USBCTRL_CAPLENGTH_CAPLENGTH 0
+#define BM_USBCTRL_CAPLENGTH_CAPLENGTH 0x000000FF
+#define BF_USBCTRL_CAPLENGTH_CAPLENGTH(v) \
+ (((v) << 0) & BM_USBCTRL_CAPLENGTH_CAPLENGTH)
+HW_REGISTER_0(HW_USBCTRL_HCSPARAMS, REGS_USBCTRL_BASE, 0x00000104)
+#define HW_USBCTRL_HCSPARAMS_ADDR (REGS_USBCTRL_BASE + 0x00000104)
+#define BP_USBCTRL_HCSPARAMS_N_TT 24
+#define BM_USBCTRL_HCSPARAMS_N_TT 0x0F000000
+#define BF_USBCTRL_HCSPARAMS_N_TT(v) \
+ (((v) << 24) & BM_USBCTRL_HCSPARAMS_N_TT)
+#define BP_USBCTRL_HCSPARAMS_N_PTT 20
+#define BM_USBCTRL_HCSPARAMS_N_PTT 0x00F00000
+#define BF_USBCTRL_HCSPARAMS_N_PTT(v) \
+ (((v) << 20) & BM_USBCTRL_HCSPARAMS_N_PTT)
+#define BM_USBCTRL_HCSPARAMS_PI 0x00010000
+#define BP_USBCTRL_HCSPARAMS_N_CC 12
+#define BM_USBCTRL_HCSPARAMS_N_CC 0x0000F000
+#define BF_USBCTRL_HCSPARAMS_N_CC(v) \
+ (((v) << 12) & BM_USBCTRL_HCSPARAMS_N_CC)
+#define BP_USBCTRL_HCSPARAMS_N_PCC 8
+#define BM_USBCTRL_HCSPARAMS_N_PCC 0x00000F00
+#define BF_USBCTRL_HCSPARAMS_N_PCC(v) \
+ (((v) << 8) & BM_USBCTRL_HCSPARAMS_N_PCC)
+#define BM_USBCTRL_HCSPARAMS_PPC 0x00000010
+#define BP_USBCTRL_HCSPARAMS_N_PORTS 0
+#define BM_USBCTRL_HCSPARAMS_N_PORTS 0x0000000F
+#define BF_USBCTRL_HCSPARAMS_N_PORTS(v) \
+ (((v) << 0) & BM_USBCTRL_HCSPARAMS_N_PORTS)
+HW_REGISTER_0(HW_USBCTRL_HCCPARAMS, REGS_USBCTRL_BASE, 0x00000108)
+#define HW_USBCTRL_HCCPARAMS_ADDR (REGS_USBCTRL_BASE + 0x00000108)
+#define BP_USBCTRL_HCCPARAMS_EECP 8
+#define BM_USBCTRL_HCCPARAMS_EECP 0x0000FF00
+#define BF_USBCTRL_HCCPARAMS_EECP(v) \
+ (((v) << 8) & BM_USBCTRL_HCCPARAMS_EECP)
+#define BP_USBCTRL_HCCPARAMS_IST 4
+#define BM_USBCTRL_HCCPARAMS_IST 0x000000F0
+#define BF_USBCTRL_HCCPARAMS_IST(v) \
+ (((v) << 4) & BM_USBCTRL_HCCPARAMS_IST)
+#define BM_USBCTRL_HCCPARAMS_ASP 0x00000004
+#define BM_USBCTRL_HCCPARAMS_PFL 0x00000002
+#define BM_USBCTRL_HCCPARAMS_ADC 0x00000001
+HW_REGISTER_0(HW_USBCTRL_DCIVERSION, REGS_USBCTRL_BASE, 0x00000120)
+#define HW_USBCTRL_DCIVERSION_ADDR (REGS_USBCTRL_BASE + 0x00000120)
+#define BP_USBCTRL_DCIVERSION_DCIVERSION 0
+#define BM_USBCTRL_DCIVERSION_DCIVERSION 0x0000FFFF
+#define BF_USBCTRL_DCIVERSION_DCIVERSION(v) \
+ (((v) << 0) & BM_USBCTRL_DCIVERSION_DCIVERSION)
+HW_REGISTER_0(HW_USBCTRL_DCCPARAMS, REGS_USBCTRL_BASE, 0x00000124)
+#define HW_USBCTRL_DCCPARAMS_ADDR (REGS_USBCTRL_BASE + 0x00000124)
+#define BM_USBCTRL_DCCPARAMS_HC 0x00000100
+#define BM_USBCTRL_DCCPARAMS_DC 0x00000080
+#define BP_USBCTRL_DCCPARAMS_DEN 0
+#define BM_USBCTRL_DCCPARAMS_DEN 0x0000001F
+#define BF_USBCTRL_DCCPARAMS_DEN(v) \
+ (((v) << 0) & BM_USBCTRL_DCCPARAMS_DEN)
+HW_REGISTER_0(HW_USBCTRL_USBCMD, REGS_USBCTRL_BASE, 0x00000140)
+#define HW_USBCTRL_USBCMD_ADDR (REGS_USBCTRL_BASE + 0x00000140)
+#define BP_USBCTRL_USBCMD_ITC 16
+#define BM_USBCTRL_USBCMD_ITC 0x00FF0000
+#define BF_USBCTRL_USBCMD_ITC(v) \
+ (((v) << 16) & BM_USBCTRL_USBCMD_ITC)
+#define BV_USBCTRL_USBCMD_ITC__IMM 0x0
+#define BV_USBCTRL_USBCMD_ITC__1_MICROFRAME 0x1
+#define BV_USBCTRL_USBCMD_ITC__2_MICROFRAME 0x2
+#define BV_USBCTRL_USBCMD_ITC__4_MICROFRAME 0x4
+#define BV_USBCTRL_USBCMD_ITC__8_MICROFRAME 0x8
+#define BV_USBCTRL_USBCMD_ITC__16_MICROFRAME 0x10
+#define BV_USBCTRL_USBCMD_ITC__32_MICROFRAME 0x20
+#define BV_USBCTRL_USBCMD_ITC__64_MICROFRAME 0x40
+#define BM_USBCTRL_USBCMD_FS2 0x00008000
+#define BM_USBCTRL_USBCMD_ATDTW 0x00004000
+#define BM_USBCTRL_USBCMD_SUTW 0x00002000
+#define BM_USBCTRL_USBCMD_ASPE 0x00000800
+#define BP_USBCTRL_USBCMD_ASP 8
+#define BM_USBCTRL_USBCMD_ASP 0x00000300
+#define BF_USBCTRL_USBCMD_ASP(v) \
+ (((v) << 8) & BM_USBCTRL_USBCMD_ASP)
+#define BM_USBCTRL_USBCMD_LR 0x00000080
+#define BM_USBCTRL_USBCMD_IAA 0x00000040
+#define BM_USBCTRL_USBCMD_ASE 0x00000020
+#define BM_USBCTRL_USBCMD_PSE 0x00000010
+#define BM_USBCTRL_USBCMD_FS1 0x00000008
+#define BM_USBCTRL_USBCMD_FS0 0x00000004
+#define BM_USBCTRL_USBCMD_RST 0x00000002
+#define BM_USBCTRL_USBCMD_RS 0x00000001
+HW_REGISTER_0(HW_USBCTRL_USBSTS, REGS_USBCTRL_BASE, 0x00000144)
+#define HW_USBCTRL_USBSTS_ADDR (REGS_USBCTRL_BASE + 0x00000144)
+#define BM_USBCTRL_USBSTS_TI1 0x02000000
+#define BM_USBCTRL_USBSTS_TI0 0x01000000
+#define BM_USBCTRL_USBSTS_UPI 0x00080000
+#define BM_USBCTRL_USBSTS_UAI 0x00040000
+#define BM_USBCTRL_USBSTS_NAKI 0x00010000
+#define BM_USBCTRL_USBSTS_AS 0x00008000
+#define BM_USBCTRL_USBSTS_PS 0x00004000
+#define BM_USBCTRL_USBSTS_RCL 0x00002000
+#define BM_USBCTRL_USBSTS_HCH 0x00001000
+#define BM_USBCTRL_USBSTS_ULPII 0x00000400
+#define BM_USBCTRL_USBSTS_SLI 0x00000100
+#define BM_USBCTRL_USBSTS_SRI 0x00000080
+#define BM_USBCTRL_USBSTS_URI 0x00000040
+#define BM_USBCTRL_USBSTS_AAI 0x00000020
+#define BM_USBCTRL_USBSTS_SEI 0x00000010
+#define BM_USBCTRL_USBSTS_FRI 0x00000008
+#define BM_USBCTRL_USBSTS_PCI 0x00000004
+#define BM_USBCTRL_USBSTS_UEI 0x00000002
+#define BM_USBCTRL_USBSTS_UI 0x00000001
+HW_REGISTER_0(HW_USBCTRL_USBINTR, REGS_USBCTRL_BASE, 0x00000148)
+#define HW_USBCTRL_USBINTR_ADDR (REGS_USBCTRL_BASE + 0x00000148)
+#define BM_USBCTRL_USBINTR_TIE1 0x02000000
+#define BM_USBCTRL_USBINTR_TIE0 0x01000000
+#define BM_USBCTRL_USBINTR_UPIE 0x00080000
+#define BM_USBCTRL_USBINTR_UAIE 0x00040000
+#define BM_USBCTRL_USBINTR_NAKE 0x00010000
+#define BM_USBCTRL_USBINTR_ULPIE 0x00000400
+#define BM_USBCTRL_USBINTR_SLE 0x00000100
+#define BM_USBCTRL_USBINTR_SRE 0x00000080
+#define BM_USBCTRL_USBINTR_URE 0x00000040
+#define BM_USBCTRL_USBINTR_AAE 0x00000020
+#define BM_USBCTRL_USBINTR_SEE 0x00000010
+#define BM_USBCTRL_USBINTR_FRE 0x00000008
+#define BM_USBCTRL_USBINTR_PCE 0x00000004
+#define BM_USBCTRL_USBINTR_UEE 0x00000002
+#define BM_USBCTRL_USBINTR_UE 0x00000001
+HW_REGISTER_0(HW_USBCTRL_FRINDEX, REGS_USBCTRL_BASE, 0x0000014c)
+#define HW_USBCTRL_FRINDEX_ADDR (REGS_USBCTRL_BASE + 0x0000014c)
+#define BP_USBCTRL_FRINDEX_FRINDEX 3
+#define BM_USBCTRL_FRINDEX_FRINDEX 0x00003FF8
+#define BF_USBCTRL_FRINDEX_FRINDEX(v) \
+ (((v) << 3) & BM_USBCTRL_FRINDEX_FRINDEX)
+#define BV_USBCTRL_FRINDEX_FRINDEX__N_12 12
+#define BV_USBCTRL_FRINDEX_FRINDEX__N_11 11
+#define BV_USBCTRL_FRINDEX_FRINDEX__N_10 10
+#define BV_USBCTRL_FRINDEX_FRINDEX__N_9 9
+#define BV_USBCTRL_FRINDEX_FRINDEX__N_8 8
+#define BV_USBCTRL_FRINDEX_FRINDEX__N_7 7
+#define BV_USBCTRL_FRINDEX_FRINDEX__N_6 6
+#define BV_USBCTRL_FRINDEX_FRINDEX__N_5 5
+#define BP_USBCTRL_FRINDEX_UINDEX 0
+#define BM_USBCTRL_FRINDEX_UINDEX 0x00000007
+#define BF_USBCTRL_FRINDEX_UINDEX(v) \
+ (((v) << 0) & BM_USBCTRL_FRINDEX_UINDEX)
+HW_REGISTER_0(HW_USBCTRL_PERIODICLISTBASE, REGS_USBCTRL_BASE, 0x00000154)
+#define HW_USBCTRL_PERIODICLISTBASE_ADDR (REGS_USBCTRL_BASE + 0x00000154)
+#define BP_USBCTRL_PERIODICLISTBASE_PERBASE 12
+#define BM_USBCTRL_PERIODICLISTBASE_PERBASE 0xFFFFF000
+#define BF_USBCTRL_PERIODICLISTBASE_PERBASE(v) \
+ (((v) << 12) & BM_USBCTRL_PERIODICLISTBASE_PERBASE)
+HW_REGISTER_0(HW_USBCTRL_DEVICEADDR, REGS_USBCTRL_BASE, 0x00000154)
+#define HW_USBCTRL_DEVICEADDR_ADDR (REGS_USBCTRL_BASE + 0x00000154)
+#define BP_USBCTRL_DEVICEADDR_USBADR 25
+#define BM_USBCTRL_DEVICEADDR_USBADR 0xFE000000
+#define BF_USBCTRL_DEVICEADDR_USBADR(v) \
+ (((v) << 25) & BM_USBCTRL_DEVICEADDR_USBADR)
+#define BM_USBCTRL_DEVICEADDR_USBADRA 0x01000000
+HW_REGISTER_0(HW_USBCTRL_ASYNCLISTADDR, REGS_USBCTRL_BASE, 0x00000158)
+#define HW_USBCTRL_ASYNCLISTADDR_ADDR (REGS_USBCTRL_BASE + 0x00000158)
+#define BP_USBCTRL_ASYNCLISTADDR_ASYBASE 5
+#define BM_USBCTRL_ASYNCLISTADDR_ASYBASE 0xFFFFFFE0
+#define BF_USBCTRL_ASYNCLISTADDR_ASYBASE(v) \
+ (((v) << 5) & BM_USBCTRL_ASYNCLISTADDR_ASYBASE)
+HW_REGISTER_0(HW_USBCTRL_ENDPOINTLISTADDR, REGS_USBCTRL_BASE, 0x00000158)
+#define HW_USBCTRL_ENDPOINTLISTADDR_ADDR (REGS_USBCTRL_BASE + 0x00000158)
+#define BP_USBCTRL_ENDPOINTLISTADDR_EPBASE 11
+#define BM_USBCTRL_ENDPOINTLISTADDR_EPBASE 0xFFFFF800
+#define BF_USBCTRL_ENDPOINTLISTADDR_EPBASE(v) \
+ (((v) << 11) & BM_USBCTRL_ENDPOINTLISTADDR_EPBASE)
+HW_REGISTER_0(HW_USBCTRL_TTCTRL, REGS_USBCTRL_BASE, 0x0000015c)
+#define HW_USBCTRL_TTCTRL_ADDR (REGS_USBCTRL_BASE + 0x0000015c)
+#define BP_USBCTRL_TTCTRL_TTHA 24
+#define BM_USBCTRL_TTCTRL_TTHA 0x7F000000
+#define BF_USBCTRL_TTCTRL_TTHA(v) \
+ (((v) << 24) & BM_USBCTRL_TTCTRL_TTHA)
+HW_REGISTER_0(HW_USBCTRL_BURSTSIZE, REGS_USBCTRL_BASE, 0x00000160)
+#define HW_USBCTRL_BURSTSIZE_ADDR (REGS_USBCTRL_BASE + 0x00000160)
+#define BP_USBCTRL_BURSTSIZE_TXPBURST 8
+#define BM_USBCTRL_BURSTSIZE_TXPBURST 0x0000FF00
+#define BF_USBCTRL_BURSTSIZE_TXPBURST(v) \
+ (((v) << 8) & BM_USBCTRL_BURSTSIZE_TXPBURST)
+#define BP_USBCTRL_BURSTSIZE_RXPBURST 0
+#define BM_USBCTRL_BURSTSIZE_RXPBURST 0x000000FF
+#define BF_USBCTRL_BURSTSIZE_RXPBURST(v) \
+ (((v) << 0) & BM_USBCTRL_BURSTSIZE_RXPBURST)
+HW_REGISTER_0(HW_USBCTRL_TXFILLTUNING, REGS_USBCTRL_BASE, 0x00000164)
+#define HW_USBCTRL_TXFILLTUNING_ADDR (REGS_USBCTRL_BASE + 0x00000164)
+#define BP_USBCTRL_TXFILLTUNING_TXFIFOTHRES 16
+#define BM_USBCTRL_TXFILLTUNING_TXFIFOTHRES 0x003F0000
+#define BF_USBCTRL_TXFILLTUNING_TXFIFOTHRES(v) \
+ (((v) << 16) & BM_USBCTRL_TXFILLTUNING_TXFIFOTHRES)
+#define BP_USBCTRL_TXFILLTUNING_TXSCHEALTH 8
+#define BM_USBCTRL_TXFILLTUNING_TXSCHEALTH 0x00001F00
+#define BF_USBCTRL_TXFILLTUNING_TXSCHEALTH(v) \
+ (((v) << 8) & BM_USBCTRL_TXFILLTUNING_TXSCHEALTH)
+#define BP_USBCTRL_TXFILLTUNING_TXSCHOH 0
+#define BM_USBCTRL_TXFILLTUNING_TXSCHOH 0x0000007F
+#define BF_USBCTRL_TXFILLTUNING_TXSCHOH(v) \
+ (((v) << 0) & BM_USBCTRL_TXFILLTUNING_TXSCHOH)
+HW_REGISTER_0(HW_USBCTRL_IC_USB, REGS_USBCTRL_BASE, 0x0000016c)
+#define HW_USBCTRL_IC_USB_ADDR (REGS_USBCTRL_BASE + 0x0000016c)
+#define BM_USBCTRL_IC_USB_IC_ENABLE 0x00000008
+#define BP_USBCTRL_IC_USB_IC_VDD 0
+#define BM_USBCTRL_IC_USB_IC_VDD 0x00000007
+#define BF_USBCTRL_IC_USB_IC_VDD(v) \
+ (((v) << 0) & BM_USBCTRL_IC_USB_IC_VDD)
+#define BV_USBCTRL_IC_USB_IC_VDD__VOLTAGE_NONE 0x0
+#define BV_USBCTRL_IC_USB_IC_VDD__VOLTAGE_1_0 0x1
+#define BV_USBCTRL_IC_USB_IC_VDD__VOLTAGE_1_2 0x2
+#define BV_USBCTRL_IC_USB_IC_VDD__VOLTAGE_1_5 0x3
+#define BV_USBCTRL_IC_USB_IC_VDD__VOLTAGE_1_8 0x4
+#define BV_USBCTRL_IC_USB_IC_VDD__VOLTAGE_3_0 0x5
+#define BV_USBCTRL_IC_USB_IC_VDD__RESERVED0 0x6
+#define BV_USBCTRL_IC_USB_IC_VDD__RESERVED1 0x7
+HW_REGISTER_0(HW_USBCTRL_ULPI, REGS_USBCTRL_BASE, 0x00000170)
+#define HW_USBCTRL_ULPI_ADDR (REGS_USBCTRL_BASE + 0x00000170)
+#define BM_USBCTRL_ULPI_ULPIWU 0x80000000
+#define BM_USBCTRL_ULPI_ULPIRUN 0x40000000
+#define BM_USBCTRL_ULPI_ULPIRW 0x20000000
+#define BM_USBCTRL_ULPI_ULPISS 0x08000000
+#define BP_USBCTRL_ULPI_ULPIPORT 24
+#define BM_USBCTRL_ULPI_ULPIPORT 0x07000000
+#define BF_USBCTRL_ULPI_ULPIPORT(v) \
+ (((v) << 24) & BM_USBCTRL_ULPI_ULPIPORT)
+#define BP_USBCTRL_ULPI_ULPIADDR 16
+#define BM_USBCTRL_ULPI_ULPIADDR 0x00FF0000
+#define BF_USBCTRL_ULPI_ULPIADDR(v) \
+ (((v) << 16) & BM_USBCTRL_ULPI_ULPIADDR)
+#define BP_USBCTRL_ULPI_ULPIDATRD 8
+#define BM_USBCTRL_ULPI_ULPIDATRD 0x0000FF00
+#define BF_USBCTRL_ULPI_ULPIDATRD(v) \
+ (((v) << 8) & BM_USBCTRL_ULPI_ULPIDATRD)
+#define BP_USBCTRL_ULPI_ULPIDATWR 0
+#define BM_USBCTRL_ULPI_ULPIDATWR 0x000000FF
+#define BF_USBCTRL_ULPI_ULPIDATWR(v) \
+ (((v) << 0) & BM_USBCTRL_ULPI_ULPIDATWR)
+HW_REGISTER_0(HW_USBCTRL_ENDPTNAK, REGS_USBCTRL_BASE, 0x00000178)
+#define HW_USBCTRL_ENDPTNAK_ADDR (REGS_USBCTRL_BASE + 0x00000178)
+#define BP_USBCTRL_ENDPTNAK_EPTN 16
+#define BM_USBCTRL_ENDPTNAK_EPTN 0x001F0000
+#define BF_USBCTRL_ENDPTNAK_EPTN(v) \
+ (((v) << 16) & BM_USBCTRL_ENDPTNAK_EPTN)
+#define BP_USBCTRL_ENDPTNAK_EPRN 0
+#define BM_USBCTRL_ENDPTNAK_EPRN 0x0000001F
+#define BF_USBCTRL_ENDPTNAK_EPRN(v) \
+ (((v) << 0) & BM_USBCTRL_ENDPTNAK_EPRN)
+HW_REGISTER_0(HW_USBCTRL_ENDPTNAKEN, REGS_USBCTRL_BASE, 0x0000017c)
+#define HW_USBCTRL_ENDPTNAKEN_ADDR (REGS_USBCTRL_BASE + 0x0000017c)
+#define BP_USBCTRL_ENDPTNAKEN_EPTNE 16
+#define BM_USBCTRL_ENDPTNAKEN_EPTNE 0x001F0000
+#define BF_USBCTRL_ENDPTNAKEN_EPTNE(v) \
+ (((v) << 16) & BM_USBCTRL_ENDPTNAKEN_EPTNE)
+#define BP_USBCTRL_ENDPTNAKEN_EPRNE 0
+#define BM_USBCTRL_ENDPTNAKEN_EPRNE 0x0000001F
+#define BF_USBCTRL_ENDPTNAKEN_EPRNE(v) \
+ (((v) << 0) & BM_USBCTRL_ENDPTNAKEN_EPRNE)
+HW_REGISTER_0(HW_USBCTRL_PORTSC1, REGS_USBCTRL_BASE, 0x00000184)
+#define HW_USBCTRL_PORTSC1_ADDR (REGS_USBCTRL_BASE + 0x00000184)
+#define BP_USBCTRL_PORTSC1_PTS 30
+#define BM_USBCTRL_PORTSC1_PTS 0xC0000000
+#define BF_USBCTRL_PORTSC1_PTS(v) \
+ (((v) << 30) & BM_USBCTRL_PORTSC1_PTS)
+#define BV_USBCTRL_PORTSC1_PTS__UTMI 0
+#define BV_USBCTRL_PORTSC1_PTS__PHIL 1
+#define BV_USBCTRL_PORTSC1_PTS__ULPI 2
+#define BV_USBCTRL_PORTSC1_PTS__SERIAL 3
+#define BM_USBCTRL_PORTSC1_STS 0x20000000
+#define BM_USBCTRL_PORTSC1_PTW 0x10000000
+#define BP_USBCTRL_PORTSC1_PSPD 26
+#define BM_USBCTRL_PORTSC1_PSPD 0x0C000000
+#define BF_USBCTRL_PORTSC1_PSPD(v) \
+ (((v) << 26) & BM_USBCTRL_PORTSC1_PSPD)
+#define BV_USBCTRL_PORTSC1_PSPD__FULL 0
+#define BV_USBCTRL_PORTSC1_PSPD__LOW 1
+#define BV_USBCTRL_PORTSC1_PSPD__HIGH 2
+#define BM_USBCTRL_PORTSC1_SRT 0x02000000
+#define BM_USBCTRL_PORTSC1_PFSC 0x01000000
+#define BM_USBCTRL_PORTSC1_PHCD 0x00800000
+#define BM_USBCTRL_PORTSC1_WKOC 0x00400000
+#define BM_USBCTRL_PORTSC1_WKDS 0x00200000
+#define BM_USBCTRL_PORTSC1_WKCN 0x00100000
+#define BP_USBCTRL_PORTSC1_PTC 16
+#define BM_USBCTRL_PORTSC1_PTC 0x000F0000
+#define BF_USBCTRL_PORTSC1_PTC(v) \
+ (((v) << 16) & BM_USBCTRL_PORTSC1_PTC)
+#define BV_USBCTRL_PORTSC1_PTC__TEST_DISABLE 0
+#define BV_USBCTRL_PORTSC1_PTC__TEST_J_STATE 1
+#define BV_USBCTRL_PORTSC1_PTC__TEST_K_STATE 2
+#define BV_USBCTRL_PORTSC1_PTC__TEST_J_SE0_NAK 3
+#define BV_USBCTRL_PORTSC1_PTC__TEST_PACKET 4
+#define BV_USBCTRL_PORTSC1_PTC__TEST_FORCE_ENABLE_HS 5
+#define BV_USBCTRL_PORTSC1_PTC__TEST_FORCE_ENABLE_FS 6
+#define BV_USBCTRL_PORTSC1_PTC__TEST_FORCE_ENABLE_LS 7
+#define BP_USBCTRL_PORTSC1_PIC 14
+#define BM_USBCTRL_PORTSC1_PIC 0x0000C000
+#define BF_USBCTRL_PORTSC1_PIC(v) \
+ (((v) << 14) & BM_USBCTRL_PORTSC1_PIC)
+#define BV_USBCTRL_PORTSC1_PIC__OFF 0
+#define BV_USBCTRL_PORTSC1_PIC__AMBER 1
+#define BV_USBCTRL_PORTSC1_PIC__GREEN 2
+#define BV_USBCTRL_PORTSC1_PIC__UNDEF 3
+#define BM_USBCTRL_PORTSC1_PO 0x00002000
+#define BM_USBCTRL_PORTSC1_PP 0x00001000
+#define BP_USBCTRL_PORTSC1_LS 10
+#define BM_USBCTRL_PORTSC1_LS 0x00000C00
+#define BF_USBCTRL_PORTSC1_LS(v) \
+ (((v) << 10) & BM_USBCTRL_PORTSC1_LS)
+#define BV_USBCTRL_PORTSC1_LS__SE0 0
+#define BV_USBCTRL_PORTSC1_LS__K_STATE 1
+#define BV_USBCTRL_PORTSC1_LS__J_STATE 2
+#define BV_USBCTRL_PORTSC1_LS__UNDEF 3
+#define BM_USBCTRL_PORTSC1_HSP 0x00000200
+#define BM_USBCTRL_PORTSC1_PR 0x00000100
+#define BM_USBCTRL_PORTSC1_SUSP 0x00000080
+#define BM_USBCTRL_PORTSC1_FPR 0x00000040
+#define BM_USBCTRL_PORTSC1_OCC 0x00000020
+#define BM_USBCTRL_PORTSC1_OCA 0x00000010
+#define BM_USBCTRL_PORTSC1_PEC 0x00000008
+#define BM_USBCTRL_PORTSC1_PE 0x00000004
+#define BM_USBCTRL_PORTSC1_CSC 0x00000002
+#define BM_USBCTRL_PORTSC1_CCS 0x00000001
+HW_REGISTER_0(HW_USBCTRL_OTGSC, REGS_USBCTRL_BASE, 0x000001a4)
+#define HW_USBCTRL_OTGSC_ADDR (REGS_USBCTRL_BASE + 0x000001a4)
+#define BM_USBCTRL_OTGSC_DPIE 0x40000000
+#define BM_USBCTRL_OTGSC_ONEMSE 0x20000000
+#define BM_USBCTRL_OTGSC_BSEIE 0x10000000
+#define BM_USBCTRL_OTGSC_BSVIE 0x08000000
+#define BM_USBCTRL_OTGSC_ASVIE 0x04000000
+#define BM_USBCTRL_OTGSC_AVVIE 0x02000000
+#define BM_USBCTRL_OTGSC_IDIE 0x01000000
+#define BM_USBCTRL_OTGSC_DPIS 0x00400000
+#define BM_USBCTRL_OTGSC_ONEMSS 0x00200000
+#define BM_USBCTRL_OTGSC_BSEIS 0x00100000
+#define BM_USBCTRL_OTGSC_BSVIS 0x00080000
+#define BM_USBCTRL_OTGSC_ASVIS 0x00040000
+#define BM_USBCTRL_OTGSC_AVVIS 0x00020000
+#define BM_USBCTRL_OTGSC_IDIS 0x00010000
+#define BM_USBCTRL_OTGSC_DPS 0x00004000
+#define BM_USBCTRL_OTGSC_ONEMST 0x00002000
+#define BM_USBCTRL_OTGSC_BSE 0x00001000
+#define BM_USBCTRL_OTGSC_BSV 0x00000800
+#define BM_USBCTRL_OTGSC_ASV 0x00000400
+#define BM_USBCTRL_OTGSC_AVV 0x00000200
+#define BM_USBCTRL_OTGSC_ID 0x00000100
+#define BM_USBCTRL_OTGSC_HABA 0x00000080
+#define BM_USBCTRL_OTGSC_HADP 0x00000040
+#define BM_USBCTRL_OTGSC_IDPU 0x00000020
+#define BM_USBCTRL_OTGSC_DP 0x00000010
+#define BM_USBCTRL_OTGSC_OT 0x00000008
+#define BM_USBCTRL_OTGSC_HAAR 0x00000004
+#define BM_USBCTRL_OTGSC_VC 0x00000002
+#define BM_USBCTRL_OTGSC_VD 0x00000001
+HW_REGISTER_0(HW_USBCTRL_USBMODE, REGS_USBCTRL_BASE, 0x000001a8)
+#define HW_USBCTRL_USBMODE_ADDR (REGS_USBCTRL_BASE + 0x000001a8)
+#define BM_USBCTRL_USBMODE_VBPS 0x00000020
+#define BM_USBCTRL_USBMODE_SDIS 0x00000010
+#define BM_USBCTRL_USBMODE_SLOM 0x00000008
+#define BM_USBCTRL_USBMODE_ES 0x00000004
+#define BP_USBCTRL_USBMODE_CM 0
+#define BM_USBCTRL_USBMODE_CM 0x00000003
+#define BF_USBCTRL_USBMODE_CM(v) \
+ (((v) << 0) & BM_USBCTRL_USBMODE_CM)
+#define BV_USBCTRL_USBMODE_CM__IDLE 0x0
+#define BV_USBCTRL_USBMODE_CM__DEVICE 0x2
+#define BV_USBCTRL_USBMODE_CM__HOST 0x3
+HW_REGISTER_0(HW_USBCTRL_ENDPTSETUPSTAT, REGS_USBCTRL_BASE, 0x000001ac)
+#define HW_USBCTRL_ENDPTSETUPSTAT_ADDR (REGS_USBCTRL_BASE + 0x000001ac)
+#define BP_USBCTRL_ENDPTSETUPSTAT_ENDPTSETUPSTAT 0
+#define BM_USBCTRL_ENDPTSETUPSTAT_ENDPTSETUPSTAT 0x0000001F
+#define BF_USBCTRL_ENDPTSETUPSTAT_ENDPTSETUPSTAT(v) \
+ (((v) << 0) & BM_USBCTRL_ENDPTSETUPSTAT_ENDPTSETUPSTAT)
+HW_REGISTER_0(HW_USBCTRL_ENDPTPRIME, REGS_USBCTRL_BASE, 0x000001b0)
+#define HW_USBCTRL_ENDPTPRIME_ADDR (REGS_USBCTRL_BASE + 0x000001b0)
+#define BP_USBCTRL_ENDPTPRIME_PETB 16
+#define BM_USBCTRL_ENDPTPRIME_PETB 0x001F0000
+#define BF_USBCTRL_ENDPTPRIME_PETB(v) \
+ (((v) << 16) & BM_USBCTRL_ENDPTPRIME_PETB)
+#define BP_USBCTRL_ENDPTPRIME_PERB 0
+#define BM_USBCTRL_ENDPTPRIME_PERB 0x0000001F
+#define BF_USBCTRL_ENDPTPRIME_PERB(v) \
+ (((v) << 0) & BM_USBCTRL_ENDPTPRIME_PERB)
+HW_REGISTER_0(HW_USBCTRL_ENDPTFLUSH, REGS_USBCTRL_BASE, 0x000001b4)
+#define HW_USBCTRL_ENDPTFLUSH_ADDR (REGS_USBCTRL_BASE + 0x000001b4)
+#define BP_USBCTRL_ENDPTFLUSH_FETB 16
+#define BM_USBCTRL_ENDPTFLUSH_FETB 0x001F0000
+#define BF_USBCTRL_ENDPTFLUSH_FETB(v) \
+ (((v) << 16) & BM_USBCTRL_ENDPTFLUSH_FETB)
+#define BP_USBCTRL_ENDPTFLUSH_FERB 0
+#define BM_USBCTRL_ENDPTFLUSH_FERB 0x0000001F
+#define BF_USBCTRL_ENDPTFLUSH_FERB(v) \
+ (((v) << 0) & BM_USBCTRL_ENDPTFLUSH_FERB)
+HW_REGISTER_0(HW_USBCTRL_ENDPTSTAT, REGS_USBCTRL_BASE, 0x000001b8)
+#define HW_USBCTRL_ENDPTSTAT_ADDR (REGS_USBCTRL_BASE + 0x000001b8)
+#define BP_USBCTRL_ENDPTSTAT_ETBR 16
+#define BM_USBCTRL_ENDPTSTAT_ETBR 0x001F0000
+#define BF_USBCTRL_ENDPTSTAT_ETBR(v) \
+ (((v) << 16) & BM_USBCTRL_ENDPTSTAT_ETBR)
+#define BP_USBCTRL_ENDPTSTAT_ERBR 0
+#define BM_USBCTRL_ENDPTSTAT_ERBR 0x0000001F
+#define BF_USBCTRL_ENDPTSTAT_ERBR(v) \
+ (((v) << 0) & BM_USBCTRL_ENDPTSTAT_ERBR)
+HW_REGISTER_0(HW_USBCTRL_ENDPTCOMPLETE, REGS_USBCTRL_BASE, 0x000001bc)
+#define HW_USBCTRL_ENDPTCOMPLETE_ADDR (REGS_USBCTRL_BASE + 0x000001bc)
+#define BP_USBCTRL_ENDPTCOMPLETE_ETCE 16
+#define BM_USBCTRL_ENDPTCOMPLETE_ETCE 0x001F0000
+#define BF_USBCTRL_ENDPTCOMPLETE_ETCE(v) \
+ (((v) << 16) & BM_USBCTRL_ENDPTCOMPLETE_ETCE)
+#define BP_USBCTRL_ENDPTCOMPLETE_ERCE 0
+#define BM_USBCTRL_ENDPTCOMPLETE_ERCE 0x0000001F
+#define BF_USBCTRL_ENDPTCOMPLETE_ERCE(v) \
+ (((v) << 0) & BM_USBCTRL_ENDPTCOMPLETE_ERCE)
+HW_REGISTER_0(HW_USBCTRL_ENDPTCTRL0, REGS_USBCTRL_BASE, 0x000001c0)
+#define HW_USBCTRL_ENDPTCTRL0_ADDR (REGS_USBCTRL_BASE + 0x000001c0)
+#define BM_USBCTRL_ENDPTCTRL0_TXE 0x00800000
+#define BP_USBCTRL_ENDPTCTRL0_TXT 18
+#define BM_USBCTRL_ENDPTCTRL0_TXT 0x000C0000
+#define BF_USBCTRL_ENDPTCTRL0_TXT(v) \
+ (((v) << 18) & BM_USBCTRL_ENDPTCTRL0_TXT)
+#define BV_USBCTRL_ENDPTCTRL0_TXT__CONTROL 0
+#define BM_USBCTRL_ENDPTCTRL0_TXS 0x00010000
+#define BM_USBCTRL_ENDPTCTRL0_RXE 0x00000080
+#define BP_USBCTRL_ENDPTCTRL0_RXT 2
+#define BM_USBCTRL_ENDPTCTRL0_RXT 0x0000000C
+#define BF_USBCTRL_ENDPTCTRL0_RXT(v) \
+ (((v) << 2) & BM_USBCTRL_ENDPTCTRL0_RXT)
+#define BV_USBCTRL_ENDPTCTRL0_RXT__CONTROL 0
+#define BM_USBCTRL_ENDPTCTRL0_RXS 0x00000001
+/*
+ * multi-register-define name HW_USBCTRL_ENDPTCTRLn
+ * base 0x000001C0
+ * count 5
+ * offset 0x4
+ */
+HW_REGISTER_0_INDEXED(HW_USBCTRL_ENDPTCTRLn, REGS_USBCTRL_BASE, 0x000001c4,
+ 0x4)
+#define BM_USBCTRL_ENDPTCTRLn_TXE 0x00800000
+#define BM_USBCTRL_ENDPTCTRLn_TXR 0x00400000
+#define BM_USBCTRL_ENDPTCTRLn_TXI 0x00200000
+#define BP_USBCTRL_ENDPTCTRLn_TXT 18
+#define BM_USBCTRL_ENDPTCTRLn_TXT 0x000C0000
+#define BF_USBCTRL_ENDPTCTRLn_TXT(v) \
+ (((v) << 18) & BM_USBCTRL_ENDPTCTRLn_TXT)
+#define BV_USBCTRL_ENDPTCTRLn_TXT__CONTROL 0
+#define BV_USBCTRL_ENDPTCTRLn_TXT__ISO 1
+#define BV_USBCTRL_ENDPTCTRLn_TXT__BULK 2
+#define BV_USBCTRL_ENDPTCTRLn_TXT__INT 3
+#define BM_USBCTRL_ENDPTCTRLn_TXD 0x00020000
+#define BM_USBCTRL_ENDPTCTRLn_TXS 0x00010000
+#define BM_USBCTRL_ENDPTCTRLn_RXE 0x00000080
+#define BM_USBCTRL_ENDPTCTRLn_RXR 0x00000040
+#define BM_USBCTRL_ENDPTCTRLn_RXI 0x00000020
+#define BP_USBCTRL_ENDPTCTRLn_RXT 2
+#define BM_USBCTRL_ENDPTCTRLn_RXT 0x0000000C
+#define BF_USBCTRL_ENDPTCTRLn_RXT(v) \
+ (((v) << 2) & BM_USBCTRL_ENDPTCTRLn_RXT)
+#define BV_USBCTRL_ENDPTCTRLn_RXT__CONTROL 0
+#define BV_USBCTRL_ENDPTCTRLn_RXT__ISO 1
+#define BV_USBCTRL_ENDPTCTRLn_RXT__BULK 2
+#define BV_USBCTRL_ENDPTCTRLn_RXT__INT 3
+#define BM_USBCTRL_ENDPTCTRLn_RXD 0x00000002
+#define BM_USBCTRL_ENDPTCTRLn_RXS 0x00000001
+#endif /* __ARCH_ARM___USBCTRL_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regs-usbphy.h b/arch/arm/mach-stmp3xxx/include/mach/regs-usbphy.h
new file mode 100644
index 000000000000..044cd95a631d
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regs-usbphy.h
@@ -0,0 +1,193 @@
+/*
+ * STMP USBPHY Register Definitions
+ *
+ * Copyright 2008-2009 Freescale Semiconductor
+ *
+ * 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___USBPHY_H
+#define __ARCH_ARM___USBPHY_H 1
+
+#include <mach/stmp3xxx_regs.h>
+
+#define REGS_USBPHY_BASE (REGS_BASE + 0x7c000)
+#define REGS_USBPHY_BASE_PHYS (0x8007C000)
+#define REGS_USBPHY_SIZE 0x00002000
+HW_REGISTER(HW_USBPHY_PWD, REGS_USBPHY_BASE, 0x00000000)
+#define HW_USBPHY_PWD_ADDR (REGS_USBPHY_BASE + 0x00000000)
+#define BM_USBPHY_PWD_RXPWDRX 0x00100000
+#define BM_USBPHY_PWD_RXPWDDIFF 0x00080000
+#define BM_USBPHY_PWD_RXPWD1PT1 0x00040000
+#define BM_USBPHY_PWD_RXPWDENV 0x00020000
+#define BM_USBPHY_PWD_TXPWDV2I 0x00001000
+#define BM_USBPHY_PWD_TXPWDIBIAS 0x00000800
+#define BM_USBPHY_PWD_TXPWDFS 0x00000400
+HW_REGISTER(HW_USBPHY_TX, REGS_USBPHY_BASE, 0x00000010)
+#define HW_USBPHY_TX_ADDR (REGS_USBPHY_BASE + 0x00000010)
+#define BP_USBPHY_TX_USBPHY_TX_EDGECTRL 26
+#define BM_USBPHY_TX_USBPHY_TX_EDGECTRL 0x1C000000
+#define BF_USBPHY_TX_USBPHY_TX_EDGECTRL(v) \
+ (((v) << 26) & BM_USBPHY_TX_USBPHY_TX_EDGECTRL)
+#define BM_USBPHY_TX_USBPHY_TX_SYNC_INVERT 0x02000000
+#define BM_USBPHY_TX_USBPHY_TX_SYNC_MUX 0x01000000
+#define BM_USBPHY_TX_TXENCAL45DP 0x00200000
+#define BP_USBPHY_TX_TXCAL45DP 16
+#define BM_USBPHY_TX_TXCAL45DP 0x000F0000
+#define BF_USBPHY_TX_TXCAL45DP(v) \
+ (((v) << 16) & BM_USBPHY_TX_TXCAL45DP)
+#define BM_USBPHY_TX_TXENCAL45DN 0x00002000
+#define BP_USBPHY_TX_TXCAL45DN 8
+#define BM_USBPHY_TX_TXCAL45DN 0x00000F00
+#define BF_USBPHY_TX_TXCAL45DN(v) \
+ (((v) << 8) & BM_USBPHY_TX_TXCAL45DN)
+#define BP_USBPHY_TX_D_CAL 0
+#define BM_USBPHY_TX_D_CAL 0x0000000F
+#define BF_USBPHY_TX_D_CAL(v) \
+ (((v) << 0) & BM_USBPHY_TX_D_CAL)
+HW_REGISTER(HW_USBPHY_RX, REGS_USBPHY_BASE, 0x00000020)
+#define HW_USBPHY_RX_ADDR (REGS_USBPHY_BASE + 0x00000020)
+#define BM_USBPHY_RX_RXDBYPASS 0x00400000
+#define BP_USBPHY_RX_DISCONADJ 4
+#define BM_USBPHY_RX_DISCONADJ 0x00000070
+#define BF_USBPHY_RX_DISCONADJ(v) \
+ (((v) << 4) & BM_USBPHY_RX_DISCONADJ)
+#define BP_USBPHY_RX_ENVADJ 0
+#define BM_USBPHY_RX_ENVADJ 0x00000007
+#define BF_USBPHY_RX_ENVADJ(v) \
+ (((v) << 0) & BM_USBPHY_RX_ENVADJ)
+HW_REGISTER(HW_USBPHY_CTRL, REGS_USBPHY_BASE, 0x00000030)
+#define HW_USBPHY_CTRL_ADDR (REGS_USBPHY_BASE + 0x00000030)
+#define BM_USBPHY_CTRL_SFTRST 0x80000000
+#define BM_USBPHY_CTRL_CLKGATE 0x40000000
+#define BM_USBPHY_CTRL_UTMI_SUSPENDM 0x20000000
+#define BM_USBPHY_CTRL_HOST_FORCE_LS_SE0 0x10000000
+#define BM_USBPHY_CTRL_DATA_ON_LRADC 0x00002000
+#define BM_USBPHY_CTRL_DEVPLUGIN_IRQ 0x00001000
+#define BM_USBPHY_CTRL_ENIRQDEVPLUGIN 0x00000800
+#define BM_USBPHY_CTRL_RESUME_IRQ 0x00000400
+#define BM_USBPHY_CTRL_ENIRQRESUMEDETECT 0x00000200
+#define BM_USBPHY_CTRL_ENOTGIDDETECT 0x00000080
+#define BM_USBPHY_CTRL_DEVPLUGIN_POLARITY 0x00000020
+#define BM_USBPHY_CTRL_ENDEVPLUGINDETECT 0x00000010
+#define BM_USBPHY_CTRL_HOSTDISCONDETECT_IRQ 0x00000008
+#define BM_USBPHY_CTRL_ENIRQHOSTDISCON 0x00000004
+#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT 0x00000002
+HW_REGISTER_0(HW_USBPHY_STATUS, REGS_USBPHY_BASE, 0x00000040)
+#define HW_USBPHY_STATUS_ADDR (REGS_USBPHY_BASE + 0x00000040)
+#define BM_USBPHY_STATUS_RESUME_STATUS 0x00000400
+#define BM_USBPHY_STATUS_OTGID_STATUS 0x00000100
+#define BM_USBPHY_STATUS_DEVPLUGIN_STATUS 0x00000040
+#define BM_USBPHY_STATUS_HOSTDISCONDETECT_STATUS 0x00000008
+HW_REGISTER(HW_USBPHY_DEBUG, REGS_USBPHY_BASE, 0x00000050)
+#define HW_USBPHY_DEBUG_ADDR (REGS_USBPHY_BASE + 0x00000050)
+#define BM_USBPHY_DEBUG_CLKGATE 0x40000000
+#define BM_USBPHY_DEBUG_HOST_RESUME_DEBUG 0x20000000
+#define BP_USBPHY_DEBUG_SQUELCHRESETLENGTH 25
+#define BM_USBPHY_DEBUG_SQUELCHRESETLENGTH 0x1E000000
+#define BF_USBPHY_DEBUG_SQUELCHRESETLENGTH(v) \
+ (((v) << 25) & BM_USBPHY_DEBUG_SQUELCHRESETLENGTH)
+#define BM_USBPHY_DEBUG_ENSQUELCHRESET 0x01000000
+#define BP_USBPHY_DEBUG_SQUELCHRESETCOUNT 16
+#define BM_USBPHY_DEBUG_SQUELCHRESETCOUNT 0x001F0000
+#define BF_USBPHY_DEBUG_SQUELCHRESETCOUNT(v) \
+ (((v) << 16) & BM_USBPHY_DEBUG_SQUELCHRESETCOUNT)
+#define BM_USBPHY_DEBUG_ENTX2RXCOUNT 0x00001000
+#define BP_USBPHY_DEBUG_TX2RXCOUNT 8
+#define BM_USBPHY_DEBUG_TX2RXCOUNT 0x00000F00
+#define BF_USBPHY_DEBUG_TX2RXCOUNT(v) \
+ (((v) << 8) & BM_USBPHY_DEBUG_TX2RXCOUNT)
+#define BP_USBPHY_DEBUG_ENHSTPULLDOWN 4
+#define BM_USBPHY_DEBUG_ENHSTPULLDOWN 0x00000030
+#define BF_USBPHY_DEBUG_ENHSTPULLDOWN(v) \
+ (((v) << 4) & BM_USBPHY_DEBUG_ENHSTPULLDOWN)
+#define BP_USBPHY_DEBUG_HSTPULLDOWN 2
+#define BM_USBPHY_DEBUG_HSTPULLDOWN 0x0000000C
+#define BF_USBPHY_DEBUG_HSTPULLDOWN(v) \
+ (((v) << 2) & BM_USBPHY_DEBUG_HSTPULLDOWN)
+#define BM_USBPHY_DEBUG_DEBUG_INTERFACE_HOLD 0x00000002
+#define BM_USBPHY_DEBUG_OTGIDPIOLOCK 0x00000001
+HW_REGISTER_0(HW_USBPHY_DEBUG0_STATUS, REGS_USBPHY_BASE, 0x00000060)
+#define HW_USBPHY_DEBUG0_STATUS_ADDR (REGS_USBPHY_BASE + 0x00000060)
+#define BP_USBPHY_DEBUG0_STATUS_SQUELCH_COUNT 26
+#define BM_USBPHY_DEBUG0_STATUS_SQUELCH_COUNT 0xFC000000
+#define BF_USBPHY_DEBUG0_STATUS_SQUELCH_COUNT(v) \
+ (((v) << 26) & BM_USBPHY_DEBUG0_STATUS_SQUELCH_COUNT)
+#define BP_USBPHY_DEBUG0_STATUS_UTMI_RXERROR_FAIL_COUNT 16
+#define BM_USBPHY_DEBUG0_STATUS_UTMI_RXERROR_FAIL_COUNT 0x03FF0000
+#define BF_USBPHY_DEBUG0_STATUS_UTMI_RXERROR_FAIL_COUNT(v) \
+ (((v) << 16) & BM_USBPHY_DEBUG0_STATUS_UTMI_RXERROR_FAIL_COUNT)
+#define BP_USBPHY_DEBUG0_STATUS_LOOP_BACK_FAIL_COUNT 0
+#define BM_USBPHY_DEBUG0_STATUS_LOOP_BACK_FAIL_COUNT 0x0000FFFF
+#define BF_USBPHY_DEBUG0_STATUS_LOOP_BACK_FAIL_COUNT(v) \
+ (((v) << 0) & BM_USBPHY_DEBUG0_STATUS_LOOP_BACK_FAIL_COUNT)
+HW_REGISTER(HW_USBPHY_DEBUG1, REGS_USBPHY_BASE, 0x00000070)
+#define HW_USBPHY_DEBUG1_ADDR (REGS_USBPHY_BASE + 0x00000070)
+#define BP_USBPHY_DEBUG1_ENTAILADJVD 13
+#define BM_USBPHY_DEBUG1_ENTAILADJVD 0x00006000
+#define BF_USBPHY_DEBUG1_ENTAILADJVD(v) \
+ (((v) << 13) & BM_USBPHY_DEBUG1_ENTAILADJVD)
+#define BM_USBPHY_DEBUG1_ENTX2TX 0x00001000
+#define BP_USBPHY_DEBUG1_DBG_ADDRESS 0
+#define BM_USBPHY_DEBUG1_DBG_ADDRESS 0x0000000F
+#define BF_USBPHY_DEBUG1_DBG_ADDRESS(v) \
+ (((v) << 0) & BM_USBPHY_DEBUG1_DBG_ADDRESS)
+HW_REGISTER_0(HW_USBPHY_VERSION, REGS_USBPHY_BASE, 0x00000080)
+#define HW_USBPHY_VERSION_ADDR (REGS_USBPHY_BASE + 0x00000080)
+#define BP_USBPHY_VERSION_MAJOR 24
+#define BM_USBPHY_VERSION_MAJOR 0xFF000000
+#define BF_USBPHY_VERSION_MAJOR(v) \
+ (((v) << 24) & BM_USBPHY_VERSION_MAJOR)
+#define BP_USBPHY_VERSION_MINOR 16
+#define BM_USBPHY_VERSION_MINOR 0x00FF0000
+#define BF_USBPHY_VERSION_MINOR(v) \
+ (((v) << 16) & BM_USBPHY_VERSION_MINOR)
+#define BP_USBPHY_VERSION_STEP 0
+#define BM_USBPHY_VERSION_STEP 0x0000FFFF
+#define BF_USBPHY_VERSION_STEP(v) \
+ (((v) << 0) & BM_USBPHY_VERSION_STEP)
+HW_REGISTER(HW_USBPHY_IP, REGS_USBPHY_BASE, 0x00000090)
+#define HW_USBPHY_IP_ADDR (REGS_USBPHY_BASE + 0x00000090)
+#define BP_USBPHY_IP_DIV_SEL 23
+#define BM_USBPHY_IP_DIV_SEL 0x01800000
+#define BF_USBPHY_IP_DIV_SEL(v) \
+ (((v) << 23) & BM_USBPHY_IP_DIV_SEL)
+#define BV_USBPHY_IP_DIV_SEL__DEFAULT 0x0
+#define BV_USBPHY_IP_DIV_SEL__LOWER 0x1
+#define BV_USBPHY_IP_DIV_SEL__LOWEST 0x2
+#define BV_USBPHY_IP_DIV_SEL__UNDEFINED 0x3
+#define BP_USBPHY_IP_LFR_SEL 21
+#define BM_USBPHY_IP_LFR_SEL 0x00600000
+#define BF_USBPHY_IP_LFR_SEL(v) \
+ (((v) << 21) & BM_USBPHY_IP_LFR_SEL)
+#define BV_USBPHY_IP_LFR_SEL__DEFAULT 0x0
+#define BV_USBPHY_IP_LFR_SEL__TIMES_2 0x1
+#define BV_USBPHY_IP_LFR_SEL__TIMES_05 0x2
+#define BV_USBPHY_IP_LFR_SEL__UNDEFINED 0x3
+#define BP_USBPHY_IP_CP_SEL 19
+#define BM_USBPHY_IP_CP_SEL 0x00180000
+#define BF_USBPHY_IP_CP_SEL(v) \
+ (((v) << 19) & BM_USBPHY_IP_CP_SEL)
+#define BV_USBPHY_IP_CP_SEL__DEFAULT 0x0
+#define BV_USBPHY_IP_CP_SEL__TIMES_2 0x1
+#define BV_USBPHY_IP_CP_SEL__TIMES_05 0x2
+#define BV_USBPHY_IP_CP_SEL__UNDEFINED 0x3
+#define BM_USBPHY_IP_TSTI_TX_DP 0x00040000
+#define BM_USBPHY_IP_TSTI_TX_DM 0x00020000
+#define BM_USBPHY_IP_ANALOG_TESTMODE 0x00010000
+#define BM_USBPHY_IP_EN_USB_CLKS 0x00000004
+#define BM_USBPHY_IP_PLL_LOCKED 0x00000002
+#define BM_USBPHY_IP_PLL_POWER 0x00000001
+#endif /* __ARCH_ARM___USBPHY_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/regulator.h b/arch/arm/mach-stmp3xxx/include/mach/regulator.h
new file mode 100644
index 000000000000..01880bd157a7
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/regulator.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __PLAT_REGULATOR_H_
+#define __PLAT_REGULATOR_H_
+#define STMP3XXX_REG5V_NOT_USB 0
+#define STMP3XXX_REG5V_IS_USB 1
+#define STMP3XXX_VDDD 0
+#define STMP3XXX_VDDA 1
+#define STMP3XXX_VDDIO 2
+#define STMP3XXX_VDDDBO 3
+#define STMP3XXX_OVERALL_CUR 4
+
+#endif
diff --git a/arch/arm/mach-stmp3xxx/include/mach/rotdec.h b/arch/arm/mach-stmp3xxx/include/mach/rotdec.h
new file mode 100644
index 000000000000..9a8d305de5d9
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/rotdec.h
@@ -0,0 +1,26 @@
+/*
+ * Freescale STMP37XX/STMP378X dev board rotary encoder arch-dependent
+ * structure and functions declarations
+ *
+ * Author: Drew Benedetti <drewb@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_PLAT_ROTDEC_H
+#define __ASM_PLAT_ROTDEC_H
+
+extern int rotdec_pinmux_request(void);
+extern void rotdec_pinmux_free(void);
+
+#endif /* __ASM_PLAT_ROTDEC_H */
+
diff --git a/arch/arm/mach-stmp3xxx/include/mach/smp.h b/arch/arm/mach-stmp3xxx/include/mach/smp.h
new file mode 100644
index 000000000000..4c1091e227c0
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/smp.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 ASMARM_ARCH_SMP_H
+#define ASMARM_ARCH_SMP_H
+
+#include <linux/io.h>
+#include <mach/hardware.h>
+
+#define hard_smp_processor_id() \
+ ({ \
+ unsigned int cpunum; \
+ __asm__("mrc p15, 0, %0, c0, c0, 5" \
+ : "=r" (cpunum)); \
+ cpunum &= 0x0F; \
+ })
+
+extern void secondary_scan_irqs(void);
+
+#endif
diff --git a/arch/arm/mach-stmp3xxx/include/mach/stmp3xxx.h b/arch/arm/mach-stmp3xxx/include/mach/stmp3xxx.h
new file mode 100644
index 000000000000..4a3e428b7a96
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/stmp3xxx.h
@@ -0,0 +1,94 @@
+/*
+ * Freescale STMP37XX/STMP378X core structure and function declarations
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_PLAT_STMP3XXX_H
+#define __ASM_PLAT_STMP3XXX_H
+
+#include <linux/suspend.h>
+#include <mach/regs-ocotp.h>
+
+struct stmpkbd_keypair {
+ int raw;
+ int kcode;
+};
+
+struct stmp3xxxmmc_platform_data {
+ int (*hw_init)(void);
+ void (*hw_release)(void);
+ void (*cmd_pullup)(int enable);
+ int (*get_wp)(void);
+ unsigned long (*setclock)(unsigned long hz);
+ int read_uA;
+ int write_uA;
+};
+
+struct stmp37xx_spi_platform_data {
+ unsigned irq_pin;
+
+ int (*hw_init)(void *spi);
+ int (*hw_release)(void *spi);
+};
+
+struct stmp3xxx_persistent_bit_config {
+ int reg;
+ int start;
+ int width;
+ const char *name;
+};
+
+struct stmp3xxx_platform_persistent_data {
+ const struct stmp3xxx_persistent_bit_config *bit_config_tab;
+ int bit_config_cnt;
+};
+
+#define STMP3XXX_USB_DONT_REMAP 0x00000001
+struct stmp3xxx_usb_platform_data {
+ unsigned flags;
+ int (*phy_enable)(struct platform_device *);
+ void (*hw_init)(void);
+ void (*hw_release)(void);
+};
+
+extern int stmp3xxx_reset_block(u32 hwreg, int just_enable);
+
+extern struct platform_device stmp3xxx_appuart, stmp3xxx_dbguart,
+ stmp3xxx_watchdog, stmp3xxx_gpmi,
+ stmp3xxx_mmc, stmp3xxx_mmc2,
+ stmp3xxx_touchscreen,
+ stmp3xxx_keyboard, stmp3xxx_rtc,
+ stmp3xxx_framebuffer,
+ stmp3xxx_backlight,
+ stmp3xxx_rotdec,
+ stmp3xxx_ssp1, stmp3xxx_ssp2, stmp378x_i2c,
+ stmp3xxx_battery,
+ stmp3xxx_persistent, stmp3xxx_dcp_bootstream,
+ stmp3xxx_dcp,
+ stmp3xxx_mtest, stmp3xxx_pxp, stmp3xxx_viim;
+#ifdef CONFIG_PM
+suspend_state_t stmp37xx_pm_get_target(void);
+int stmp37xx_pm_sleep_was_deep(void);
+#endif
+
+extern int stmp3xxx_ssp1_device_register(void);
+extern int stmp3xxx_ssp2_device_register(void);
+
+extern int spdif_pinmux_request(void);
+extern void spdif_pinmux_release(void);
+
+extern int get_evk_board_version();
+
+#endif /* __ASM_PLAT_STMP3XXX_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/stmp3xxx_regs.h b/arch/arm/mach-stmp3xxx/include/mach/stmp3xxx_regs.h
new file mode 100644
index 000000000000..1ba0d2a01121
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/stmp3xxx_regs.h
@@ -0,0 +1,201 @@
+/*
+ * Freescale STMP37XX/STMP378X SoC register access interfaces
+ *
+ * The SoC registers may be accessed via:
+ *
+ * - single 32 bit address, or
+ * - four 32 bit addresses - general purpose, set, clear and toggle bits
+ *
+ * Multiple IP blocks (e.g. SSP, UART) provide identical register sets per
+ * each module
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_PLAT_STMP3XXX_REGS_H
+#define __ASM_PLAT_STMP3XXX_REGS_H
+
+#ifndef __ASSEMBLER__
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/suspend.h>
+#endif
+
+#include "platform.h"
+
+#define REGS_BASE STMP3XXX_REGS_VA_BASE
+
+#define HW_STMP3xxx_SET 0x04
+#define HW_STMP3xxx_CLR 0x08
+#define HW_STMP3xxx_TOG 0x0c
+
+#ifndef __ASSEMBLER__
+#define HW_REGISTER_FUNCS(id, base, offset, regset, rd, wr) \
+ static const u32 id##_OFFSET = offset; \
+ static inline u32 id##_RD_NB(u32 regbase) { \
+ if (!rd) \
+ printk(KERN_ERR"%s: cannot READ at %x+%x\n", \
+ #id, regbase, offset); \
+ return __raw_readl(regbase + offset); \
+ } \
+ static inline void id##_WR_NB(u32 regbase, u32 v) { \
+ if (!wr) \
+ printk(KERN_ERR"%s: cannot WRITE at %x+%x\n", \
+ #id, regbase, offset); \
+ __raw_writel(v, regbase + offset); \
+ } \
+ static inline void id##_SET_NB(u32 regbase, u32 v) { \
+ if (!wr) \
+ printk(KERN_ERR"%s: cannot SET at %x+%x\n", \
+ #id, regbase, offset); \
+ if (regset) \
+ __raw_writel(v, regbase + \
+ offset + HW_STMP3xxx_SET); \
+ else \
+ __raw_writel(v | __raw_readl(regbase + offset), \
+ regbase + offset); \
+ } \
+ static inline void id##_CLR_NB(u32 regbase, u32 v) { \
+ if (!wr) \
+ printk(KERN_ERR"%s: cannot CLR at %x+%x\n", \
+ #id, regbase, offset); \
+ if (regset) \
+ __raw_writel(v, regbase + \
+ offset + HW_STMP3xxx_CLR); \
+ else \
+ __raw_writel( \
+ ~v & __raw_readl(regbase + offset), \
+ regbase + offset); \
+ } \
+ static inline void id##_TOG_NB(u32 regbase, u32 v) { \
+ if (!wr) \
+ printk(KERN_ERR"%s: cannot TOG at %x+%x\n", \
+ #id, regbase, offset); \
+ if (regset) \
+ __raw_writel(v, regbase + \
+ offset + HW_STMP3xxx_TOG); \
+ else \
+ __raw_writel(v ^ __raw_readl(regbase + offset), \
+ regbase + offset); \
+ } \
+ static inline u32 id##_RD(void) { return id##_RD_NB(base); } \
+ static inline void id##_WR(u32 v) { id##_WR_NB(base, v); } \
+ static inline void id##_SET(u32 v) { id##_SET_NB(base, v); } \
+ static inline void id##_CLR(u32 v) { id##_CLR_NB(base, v); } \
+ static inline void id##_TOG(u32 v) { id##_TOG_NB(base, v); }
+
+#define HW_REGISTER_FUNCS_INDEXED(id, base, offset, regset, rd, wr, step)\
+ static inline u32 id##_OFFSET(int i) { \
+ return offset + i * step; \
+ } \
+ static inline u32 id##_RD_NB(u32 regbase, int i) { \
+ if (!rd) \
+ printk(KERN_ERR"%s(%d): can't READ at %x+%x\n", \
+ #id, i, regbase, offset + i * step); \
+ return __raw_readl(regbase + offset + i * step); \
+ } \
+ static inline void id##_WR_NB(u32 regbase, int i, u32 v) \
+ { \
+ if (!wr) \
+ printk(KERN_ERR"%s(%d): can't WRITE at %x+%x\n",\
+ #id, i, regbase, offset + i * step); \
+ __raw_writel(v, regbase + offset + i * step); \
+ } \
+ static inline void id##_SET_NB(u32 regbase, int i, u32 v) \
+ { \
+ if (!wr) \
+ printk(KERN_ERR"%s(%d): can't SET at %x+%x\n", \
+ #id, i, regbase, offset + i * step); \
+ if (regset) \
+ __raw_writel(v, regbase + offset + \
+ i * step + HW_STMP3xxx_SET); \
+ else \
+ __raw_writel(v | __raw_readl(regbase + \
+ offset + i * step), \
+ regbase + offset + i * step); \
+ } \
+ static inline void id##_CLR_NB(u32 regbase, int i, u32 v) \
+ { \
+ if (!wr) \
+ printk(KERN_ERR"%s(%d): cannot CLR at %x+%x\n", \
+ #id, i, regbase, offset + i * step); \
+ if (regset) \
+ __raw_writel(v, regbase + offset + \
+ i * step + HW_STMP3xxx_CLR); \
+ else \
+ __raw_writel(~v & __raw_readl(regbase + \
+ offset + i * step), \
+ regbase + offset + i * step); \
+ } \
+ static inline void id##_TOG_NB(u32 regbase, int i, u32 v) \
+ { \
+ if (!wr) \
+ printk(KERN_ERR"%s(%d): cannot TOG at %x+%x\n", \
+ #id, i, regbase, offset + i * step); \
+ if (regset) \
+ __raw_writel(v, regbase + offset + \
+ i * step + HW_STMP3xxx_TOG); \
+ else \
+ __raw_writel(v ^ __raw_readl(regbase + offset \
+ + i * step), \
+ regbase + offset + i * step); \
+ } \
+ static inline u32 id##_RD(int i) \
+ { \
+ return id##_RD_NB(base, i); \
+ } \
+ static inline void id##_WR(int i, u32 v) \
+ { \
+ id##_WR_NB(base, i, v); \
+ } \
+ static inline void id##_SET(int i, u32 v) \
+ { \
+ id##_SET_NB(base, i, v); \
+ } \
+ static inline void id##_CLR(int i, u32 v) \
+ { \
+ id##_CLR_NB(base, i, v); \
+ } \
+ static inline void id##_TOG(int i, u32 v) \
+ { \
+ id##_TOG_NB(base, i, v); \
+ }
+
+#define HW_REGISTER_WO(id, base, offset)\
+ HW_REGISTER_FUNCS(id, base, offset, 1, 0, 1)
+#define HW_REGISTER_RO(id, base, offset)\
+ HW_REGISTER_FUNCS(id, base, offset, 1, 1, 0)
+#define HW_REGISTER(id, base, offset) \
+ HW_REGISTER_FUNCS(id, base, offset, 1, 1, 1)
+#define HW_REGISTER_0(id, base, offset) \
+ HW_REGISTER_FUNCS(id, base, offset, 0, 1, 1)
+#define HW_REGISTER_INDEXED(id, base, offset, step) \
+ HW_REGISTER_FUNCS_INDEXED(id, base, offset, 1, 1, 1, step)
+#define HW_REGISTER_RO_INDEXED(id, base, offset, step) \
+ HW_REGISTER_FUNCS_INDEXED(id, base, offset, 1, 1, 0, step)
+#define HW_REGISTER_0_INDEXED(id, base, offset, step) \
+ HW_REGISTER_FUNCS_INDEXED(id, base, offset, 0, 1, 1, step)
+#else /* __ASSEMBLER__ */
+#define HW_REGISTER_FUNCS(id, base, offset, regset, rd, wr)
+#define HW_REGISTER_FUNCS_INDEXED(id, base, offset, regset, rd, wr, step)
+#define HW_REGISTER_WO(id, base, offset)
+#define HW_REGISTER_RO(id, base, offset)
+#define HW_REGISTER(id, base, offset)
+#define HW_REGISTER_0(id, base, offset)
+#define HW_REGISTER_INDEXED(id, base, offset, step)
+#define HW_REGISTER_RO_INDEXED(id, base, offset, step)
+#define HW_REGISTER_0_INDEXED(id, base, offset, step)
+#endif /* __ASSEMBLER__ */
+
+#endif /* __ASM_PLAT_STMP3XXX_REGS_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/system.h b/arch/arm/mach-stmp3xxx/include/mach/system.h
new file mode 100644
index 000000000000..d0a1fdf87687
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/system.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005 Sigmatel Inc
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_SYSTEM_H
+#define __ASM_ARCH_SYSTEM_H
+
+#include <asm/proc-fns.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-power.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)
+{
+ /* Set BATTCHRG to default value */
+ HW_POWER_CHARGE_WR(0x00010000);
+
+ /* Set MINPWR to default value */
+ HW_POWER_MINPWR_WR(0);
+
+ /* Reset digital side of chip (but not power or RTC) */
+ HW_CLKCTRL_RESET_WR(BM_CLKCTRL_RESET_DIG);
+
+ /* Should not return */
+}
+
+#endif
diff --git a/arch/arm/mach-stmp3xxx/include/mach/timex.h b/arch/arm/mach-stmp3xxx/include/mach/timex.h
new file mode 100644
index 000000000000..5ba435b51df5
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/timex.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 1999 ARM Limited
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * System time clock is sourced from the 32k clock
+ */
+#define CLOCK_TICK_RATE (32000)
diff --git a/arch/arm/mach-stmp3xxx/include/mach/uncompress.h b/arch/arm/mach-stmp3xxx/include/mach/uncompress.h
new file mode 100644
index 000000000000..9f551ae0f39b
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/uncompress.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2005 Sigmatel Inc
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_UNCOMPRESS_H
+#define __ASM_ARCH_UNCOMPRESS_H
+
+/*
+ * Register includes are for when the MMU enabled; we need to define our
+ * own stuff here for pre-MMU use
+ */
+#define UARTDBG_BASE 0x80070000
+#define UART(c) (((volatile unsigned *)UARTDBG_BASE)[c])
+
+/*
+ * This does not append a newline
+ */
+static void putc(char c)
+{
+ /* Wait for TX fifo empty */
+ while ((UART(6) & (1<<7)) == 0)
+ continue;
+
+ /* Write byte */
+ UART(0) = c;
+
+ /* Wait for last bit to exit the UART */
+ while (UART(6) & (1<<3))
+ continue;
+}
+
+static void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+
+#define arch_decomp_wdog()
+
+#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-stmp3xxx/include/mach/unique-id.h b/arch/arm/mach-stmp3xxx/include/mach/unique-id.h
new file mode 100644
index 000000000000..ee22ec2502c5
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/unique-id.h
@@ -0,0 +1,30 @@
+/*
+ * Unique ID interface for ID storage providers
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __UNIQUE_ID_H
+#define __UNIQUE_ID_H
+
+struct uid_ops {
+ ssize_t (*id_show)(void *context, char *page, int ascii);
+ ssize_t (*id_store)(void *context, const char *page,
+ size_t count, int ascii);
+};
+
+struct kobject *uid_provider_init(const char *name,
+ struct uid_ops *ops, void *context);
+void uid_provider_remove(const char *name);
+#endif
diff --git a/arch/arm/mach-stmp3xxx/include/mach/vmalloc.h b/arch/arm/mach-stmp3xxx/include/mach/vmalloc.h
new file mode 100644
index 000000000000..f7228a84ea0c
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/include/mach/vmalloc.h
@@ -0,0 +1,17 @@
+/*
+ * Based on include/asm/arm/arch-integrator/vmalloc.h
+ * Copyright (C) 2000 Russell King.
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#define VMALLOC_END (PAGE_OFFSET + 0x10000000)
diff --git a/arch/arm/mach-stmp3xxx/irq.c b/arch/arm/mach-stmp3xxx/irq.c
new file mode 100644
index 000000000000..19bd285c8398
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/irq.c
@@ -0,0 +1,86 @@
+/*
+ * Freescale STMP37XX/STMP378X common interrupt handling code
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/sysdev.h>
+
+#include <mach/regs-icoll.h>
+
+void __init stmp3xxx_init_irq(struct irq_chip *chip1,
+ struct irq_chip *chip2, int (*is_chip_2)(int))
+{
+ unsigned int i;
+ struct irq_chip *chip;
+
+ /* Reset the interrupt controller */
+ HW_ICOLL_CTRL_CLR(BM_ICOLL_CTRL_CLKGATE);
+ udelay(10);
+ HW_ICOLL_CTRL_CLR(BM_ICOLL_CTRL_SFTRST);
+ udelay(10);
+ HW_ICOLL_CTRL_SET(BM_ICOLL_CTRL_SFTRST);
+ while (!(HW_ICOLL_CTRL_RD() & BM_ICOLL_CTRL_CLKGATE))
+ continue;
+ HW_ICOLL_CTRL_CLR(BM_ICOLL_CTRL_SFTRST | BM_ICOLL_CTRL_CLKGATE);
+
+ /* Disable all interrupts initially */
+ for (i = 0; i < NR_IRQS; i++) {
+ chip = chip1;
+ if (is_chip_2 && is_chip_2(i))
+ chip = chip2;
+ chip->mask(i);
+ set_irq_chip(i, chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+ }
+
+ /* Ensure vector is cleared */
+ HW_ICOLL_LEVELACK_WR(1);
+ HW_ICOLL_LEVELACK_WR(2);
+ HW_ICOLL_LEVELACK_WR(4);
+ HW_ICOLL_LEVELACK_WR(8);
+
+ HW_ICOLL_VECTOR_WR(0);
+ /* Barrier */
+ (void) HW_ICOLL_STAT_RD();
+}
+
+#define irq_suspend NULL
+#define irq_resume NULL
+
+static struct sysdev_class irq_class = {
+ .name = "irq",
+ .suspend = irq_suspend,
+ .resume = irq_resume,
+};
+
+static struct sys_device irq_device = {
+ .id = 0,
+ .cls = &irq_class,
+};
+
+static int __init irq_init_sysfs(void)
+{
+ int ret = sysdev_class_register(&irq_class);
+ if (ret == 0)
+ ret = sysdev_register(&irq_device);
+ return ret;
+}
+
+device_initcall(irq_init_sysfs);
diff --git a/arch/arm/mach-stmp3xxx/lcd_hx8238a.c b/arch/arm/mach-stmp3xxx/lcd_hx8238a.c
new file mode 100644
index 000000000000..faa2740e04a5
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/lcd_hx8238a.c
@@ -0,0 +1,367 @@
+/*
+ * Freescale STMP37XX/STMP378X dotclk panel initialization
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <mach/regs-lcdif.h>
+#include <mach/regs-lradc.h>
+#include <mach/regs-pinctrl.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-pwm.h>
+#include <mach/regs-apbh.h>
+#include <mach/gpio.h>
+#include <mach/pins.h>
+#include <mach/lcdif.h>
+#include <mach/cpu.h>
+#include <mach/stmp3xxx.h>
+
+#include "common.h"
+
+#define MAX_CHAIN_LEN 10
+
+#define DOTCLK_H_ACTIVE 960
+#define DOTCLK_H_PULSE_WIDTH 2
+#define DOTCLK_HF_PORCH 1
+#define DOTCLK_HB_PORCH 67
+#define DOTCLK_H_WAIT_CNT (DOTCLK_H_PULSE_WIDTH + (3 * DOTCLK_HB_PORCH))
+#define DOTCLK_H_PERIOD (DOTCLK_H_WAIT_CNT + DOTCLK_HF_PORCH + DOTCLK_H_ACTIVE)
+
+#define DOTCLK_V_PULSE_WIDTH 2
+#define DOTCLK_V_ACTIVE 240
+#define DOTCLK_VF_PORCH 1
+#define DOTCLK_VB_PORCH 16
+#define DOTCLK_V_WAIT_CNT (DOTCLK_V_PULSE_WIDTH + DOTCLK_VB_PORCH)
+#define DOTCLK_V_PERIOD (DOTCLK_VF_PORCH + DOTCLK_V_ACTIVE + DOTCLK_V_WAIT_CNT)
+
+static struct pin_group *lcd_pins;
+static unsigned *lcd_spi_pins;
+static struct stmp3xxx_platform_bl_data bl_data;
+
+static void spi_write(u32 val)
+{
+ u32 mask;
+
+ gpio_set_value(lcd_spi_pins[SPI_MOSI], 0);
+ gpio_set_value(lcd_spi_pins[SPI_SCLK], 0);
+ gpio_set_value(lcd_spi_pins[SPI_CS], 0);
+
+ for (mask = 0x00800000; mask != 0; mask >>= 1) {
+ gpio_set_value(lcd_spi_pins[SPI_SCLK], 0);
+ if (val & mask)
+ gpio_set_value(lcd_spi_pins[SPI_MOSI], 1);
+ else
+ gpio_set_value(lcd_spi_pins[SPI_MOSI], 0);
+
+ gpio_set_value(lcd_spi_pins[SPI_SCLK], 1);
+ }
+
+ udelay(10);
+ gpio_set_value(lcd_spi_pins[SPI_MOSI], 0);
+ gpio_set_value(lcd_spi_pins[SPI_SCLK], 0);
+ gpio_set_value(lcd_spi_pins[SPI_CS], 1);
+}
+
+static void write_reg(u16 reg, u16 val)
+{
+ pr_debug("%s: writing %x to %x\n", __func__, reg, val);
+ spi_write(0x00700000 | reg);
+ spi_write(0x00720000 | val);
+}
+
+static void init_panel_hw(void)
+{
+ int i;
+ const unsigned short seq[] = {
+ 0x02, 0x0200,
+ 0x03, 0x6164,
+ 0x0E, 0x3380,
+ 0x1E, 0x00D2,
+ 0x01, 0x733F,
+ 0x04, 0x0448,
+ 0x05, 0xBC54,
+ 0x0A, 0x4008,
+ 0x0B, 0xD400,
+ 0x0D, 0x3229,
+ 0x0F, 0x0000,
+ 0x30, 0x0000,
+ 0x31, 0x0407,
+ 0x32, 0x0202,
+ 0x33, 0x0000,
+ 0x34, 0x0505,
+ 0x35, 0x0003,
+ 0x36, 0x0707,
+ 0x37, 0x0000,
+ 0x3A, 0x0904,
+ 0x3B, 0x0904,
+ };
+
+ for (i = 0; i < sizeof(seq) / sizeof(seq[0]); i += 2)
+ write_reg(seq[i], seq[i + 1]);
+}
+
+static int init_pinmux(void)
+{
+ int ret = -EINVAL;
+
+ if (cpu_is_stmp37xx())
+ lcd_pins = &stmp37xx_lcd_pins;
+ else if (cpu_is_stmp378x())
+ lcd_pins = &stmp378x_lcd_pins;
+ else
+ goto out;
+ ret = stmp3xxx_request_pin_group(lcd_pins, "lcd_hx8238a");
+
+out:
+ return ret;
+}
+
+static int init_pinmux_spi(void)
+{
+ int ret = -EINVAL;
+
+ if (cpu_is_stmp37xx())
+ lcd_spi_pins = stmp37xx_lcd_spi_pins;
+ else if (cpu_is_stmp378x())
+ lcd_spi_pins = stmp378x_lcd_spi_pins;
+ else
+ goto out_1;
+
+ ret = gpio_request(lcd_spi_pins[SPI_MOSI], "lcd_hx8238a");
+ if (ret)
+ goto out_1;
+
+ ret = gpio_request(lcd_spi_pins[SPI_SCLK], "lcd_hx8238a");
+ if (ret)
+ goto out_2;
+ ret = gpio_request(lcd_spi_pins[SPI_CS], "lcd_hx8238a");
+ if (ret)
+ goto out_3;
+
+ /* Enable these pins as outputs */
+ gpio_direction_output(lcd_spi_pins[SPI_MOSI], 0);
+ gpio_direction_output(lcd_spi_pins[SPI_SCLK], 0);
+ gpio_direction_output(lcd_spi_pins[SPI_CS], 1);
+
+ return 0;
+
+out_3:
+ gpio_free(lcd_spi_pins[SPI_SCLK]);
+out_2:
+ gpio_free(lcd_spi_pins[SPI_MOSI]);
+out_1:
+ return ret;
+}
+
+static void uninit_pinmux(void)
+{
+ stmp3xxx_release_pin_group(lcd_pins, "lcd_hx8238a");
+}
+
+static void uninit_pinmux_spi(void)
+{
+ gpio_free(lcd_spi_pins[SPI_MOSI]);
+ gpio_free(lcd_spi_pins[SPI_SCLK]);
+ gpio_free(lcd_spi_pins[SPI_CS]);
+}
+
+static struct clk *lcd_clk;
+
+static int init_panel(struct device *dev, dma_addr_t phys, int memsize,
+ struct stmp3xxx_platform_fb_entry *pentry)
+{
+ int ret = 0;
+
+ lcd_clk = clk_get(dev, "lcdif");
+ if (IS_ERR(lcd_clk)) {
+ ret = PTR_ERR(lcd_clk);
+ goto out_1;
+ }
+ ret = clk_enable(lcd_clk);
+ if (ret) {
+ clk_put(lcd_clk);
+ goto out_1;
+ }
+ ret = clk_set_rate(lcd_clk,
+ 1000000/pentry->cycle_time_ns); /* kHz */
+ if (ret) {
+ clk_disable(lcd_clk);
+ clk_put(lcd_clk);
+ goto out_1;
+ }
+
+ ret = init_pinmux();
+ if (ret)
+ goto out_1;
+ ret = init_pinmux_spi();
+ if (ret)
+ goto out_2;
+ init_panel_hw();
+
+ ret = stmp3xxx_lcdif_dma_init(dev, phys, memsize, 0);
+ if (ret)
+ goto out_3;
+
+ setup_dotclk_panel(DOTCLK_V_PULSE_WIDTH, DOTCLK_V_PERIOD,
+ DOTCLK_V_WAIT_CNT, DOTCLK_V_ACTIVE,
+ DOTCLK_H_PULSE_WIDTH, DOTCLK_H_PERIOD,
+ DOTCLK_V_WAIT_CNT, DOTCLK_H_ACTIVE, 1);
+
+ stmp3xxx_lcd_set_bl_pdata(pentry->bl_data);
+ stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_INIT, pentry);
+ return 0;
+out_3:
+ uninit_pinmux_spi();
+out_2:
+ uninit_pinmux();
+out_1:
+ return ret;
+}
+
+static void release_panel(struct device *dev,
+ struct stmp3xxx_platform_fb_entry *pentry)
+{
+ stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_RELEASE, pentry);
+ uninit_pinmux_spi();
+ uninit_pinmux();
+ release_dotclk_panel();
+ stmp3xxx_lcdif_dma_release();
+ clk_disable(lcd_clk);
+ clk_put(lcd_clk);
+}
+
+static int blank_panel(int blank)
+{
+ int ret = 0;
+
+ switch (blank) {
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_POWERDOWN:
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_RUN);
+ break;
+
+ case FB_BLANK_UNBLANK:
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_RUN);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static struct stmp3xxx_platform_fb_entry fb_entry = {
+ .name = "hx8238a",
+ .x_res = 240,
+ .y_res = 320,
+ .bpp = 32,
+ .cycle_time_ns = 150,
+ .lcd_type = STMP3XXX_LCD_PANEL_DOTCLK,
+ .init_panel = init_panel,
+ .release_panel = release_panel,
+ .blank_panel = blank_panel,
+ .run_panel = stmp3xxx_lcdif_run,
+ .stop_panel = stmp3xxx_lcdif_stop,
+ .pan_display = stmp3xxx_lcdif_pan_display,
+ .bl_data = &bl_data,
+};
+
+static struct clk *pwm_clk;
+static int init_bl(struct stmp3xxx_platform_bl_data *data)
+{
+ int ret = 0;
+
+ pwm_clk = clk_get(NULL, "pwm");
+ if (IS_ERR(pwm_clk)) {
+ ret = PTR_ERR(pwm_clk);
+ goto out;
+ }
+ clk_enable(pwm_clk);
+ stmp3xxx_reset_block(REGS_PWM_BASE, 1);
+
+ ret = stmp3xxx_request_pin(PINID_PWM2, PIN_FUN1, "lcd_hx8238a");
+ if (ret)
+ goto out_mux;
+ stmp3xxx_pin_voltage(PINID_PWM2, PIN_12MA, "lcd_hx8238a");
+ stmp3xxx_pin_strength(PINID_PWM2, PIN_3_3V, "lcd_hx8238a");
+
+ HW_PWM_CTRL_CLR(BM_PWM_CTRL_PWM2_ENABLE);
+ HW_PWM_CTRL_SET(BM_PWM_CTRL_PWM2_ANA_CTRL_ENABLE);
+ HW_PWM_ACTIVEn_WR(2, BF_PWM_ACTIVEn_INACTIVE(10) |
+ BF_PWM_ACTIVEn_ACTIVE(5));
+ HW_PWM_PERIODn_WR(2,
+ BF_PWM_PERIODn_CDIV(1) | /* divide by 2 */
+ BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */
+ BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */
+ BF_PWM_PERIODn_PERIOD(14));
+ return 0;
+
+out_mux:
+ clk_put(pwm_clk);
+out:
+ return ret;
+}
+
+static void free_bl(struct stmp3xxx_platform_bl_data *data)
+{
+ HW_PWM_CTRL_CLR(BM_PWM_CTRL_PWM2_ENABLE);
+ stmp3xxx_release_pin(PINID_PWM2, "lcd_hx8238a");
+ clk_disable(pwm_clk);
+ clk_put(pwm_clk);
+}
+
+static void set_bl_intensity(struct stmp3xxx_platform_bl_data *data,
+ struct backlight_device *bd, int suspended)
+{
+ int intensity = bd->props.brightness;
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (suspended)
+ intensity = 0;
+
+ HW_PWM_CTRL_CLR(BM_PWM_CTRL_PWM2_ENABLE);
+ if (intensity) {
+ HW_LRADC_CTRL2_CLR(BM_LRADC_CTRL2_BL_BRIGHTNESS);
+ HW_LRADC_CTRL2_SET(BM_LRADC_CTRL2_BL_ENABLE |
+ BM_LRADC_CTRL2_BL_MUX_SELECT |
+ BF_LRADC_CTRL2_BL_BRIGHTNESS(intensity - 1));
+ HW_PWM_CTRL_SET(BM_PWM_CTRL_PWM2_ENABLE);
+ }
+}
+
+static struct stmp3xxx_platform_bl_data bl_data = {
+ .bl_max_intensity = (BM_LRADC_CTRL2_BL_BRIGHTNESS >>
+ BP_LRADC_CTRL2_BL_BRIGHTNESS) + 1,
+ .bl_default_intensity = 0x10,
+ .init_bl = init_bl,
+ .free_bl = free_bl,
+ .set_bl_intensity = set_bl_intensity,
+};
+
+static int __init register_devices(void)
+{
+ stmp3xxx_lcd_register_entry(&fb_entry,
+ stmp3xxx_framebuffer.dev.platform_data);
+ return 0;
+}
+subsys_initcall(register_devices);
diff --git a/arch/arm/mach-stmp3xxx/lcd_lms350.c b/arch/arm/mach-stmp3xxx/lcd_lms350.c
new file mode 100644
index 000000000000..ded3110a1da3
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/lcd_lms350.c
@@ -0,0 +1,537 @@
+/*
+ * Freescale STMP378X Samsung LMS350 LCD panel initialization
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/notifier.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/regs-lcdif.h>
+#include <mach/regs-lradc.h>
+#include <mach/regs-pinctrl.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-pwm.h>
+#include <mach/regs-apbh.h>
+#include <mach/gpio.h>
+#include <mach/pins.h>
+#include <mach/lcdif.h>
+#include <mach/cpu.h>
+#include <mach/stmp3xxx.h>
+
+#include "common.h"
+
+#define DOTCLK_H_ACTIVE 320
+#define DOTCLK_H_PULSE_WIDTH 3
+#define DOTCLK_HF_PORCH 5
+#define DOTCLK_HB_PORCH 4
+#define DOTCLK_H_WAIT_CNT (DOTCLK_H_PULSE_WIDTH + (3 * DOTCLK_HB_PORCH))
+#define DOTCLK_H_PERIOD (DOTCLK_H_WAIT_CNT + DOTCLK_HF_PORCH + DOTCLK_H_ACTIVE)
+
+#define DOTCLK_V_PULSE_WIDTH 2
+#define DOTCLK_V_ACTIVE 240
+#define DOTCLK_VF_PORCH 2
+#define DOTCLK_VB_PORCH 5
+#define DOTCLK_V_WAIT_CNT (DOTCLK_V_PULSE_WIDTH + DOTCLK_VB_PORCH)
+#define DOTCLK_V_PERIOD (DOTCLK_VF_PORCH + DOTCLK_V_ACTIVE + DOTCLK_V_WAIT_CNT)
+
+static struct stmp3xxx_platform_bl_data bl_data;
+static struct pin_group *lcd_pins;
+static unsigned *lcd_spi_pins;
+
+static void spi_write(u32 val)
+{
+ u32 mask;
+
+ gpio_set_value(lcd_spi_pins[SPI_MOSI], 0);
+ gpio_set_value(lcd_spi_pins[SPI_SCLK], 1);
+ gpio_set_value(lcd_spi_pins[SPI_CS], 0);
+
+ for (mask = 0x00800000; mask != 0; mask >>= 1) {
+ gpio_set_value(lcd_spi_pins[SPI_SCLK], 0);
+ if (val & mask)
+ gpio_set_value(lcd_spi_pins[SPI_MOSI], 1);
+ else
+ gpio_set_value(lcd_spi_pins[SPI_MOSI], 0);
+
+ gpio_set_value(lcd_spi_pins[SPI_SCLK], 1);
+ }
+
+ udelay(10);
+ gpio_set_value(lcd_spi_pins[SPI_MOSI], 1);
+ gpio_set_value(lcd_spi_pins[SPI_SCLK], 1);
+ gpio_set_value(lcd_spi_pins[SPI_CS], 1);
+}
+
+static void write_reg(u16 reg, u16 val)
+{
+ pr_debug("%s: writing %x to %x\n", __func__, reg, val);
+ spi_write(0x00740000 | reg);
+ spi_write(0x00760000 | val);
+}
+
+static const unsigned short pon_seq[] = {
+ /* power on */
+ 0x07, 0x0000, 20,
+ 0x12, 0x1618, 0,
+ 0x11, 0x222f, 0,
+ 0x13, 0x40ca, 0,
+ 0x10, 0x3108, 300,
+ 0x12, 0x1658, 250,
+ 0x01, 0x2b1d, 0,
+ 0x02, 0x0300, 0,
+ 0x03, 0xD040, 0,
+ 0x08, (DOTCLK_VB_PORCH + DOTCLK_V_PULSE_WIDTH) - 2, 0,
+ 0x09, ((DOTCLK_H_PULSE_WIDTH / 3) + DOTCLK_HB_PORCH) - 2, 0,
+ 0x76, 0x2213, 0,
+ 0x0b, 0x33e1, 0,
+ 0x0c, 0x0020, 0,
+ 0x76, 0x0000, 0,
+ 0x0d, 0x0000, 0,
+ 0x0e, 0x0000, 0,
+ 0x14, 0x0000, 0,
+ 0x15, 0x0803, 0,
+ 0x16, 0x0000, 0,
+ 0x30, 0x0209, 0,
+ 0x31, 0x0404, 0,
+ 0x32, 0x0e07, 0,
+ 0x33, 0x0602, 0,
+ 0x34, 0x0707, 0,
+ 0x35, 0x0707, 0,
+ 0x36, 0x0707, 0,
+ 0x37, 0x0206, 0,
+ 0x38, 0x0f06, 0,
+ 0x39, 0x0611, 20,
+};
+
+static const unsigned short don_seq[] = {
+ /* display on */
+ 0x07, 0x0001, 150,
+ 0x07, 0x0101, 150,
+ 0x76, 0x2213, 0,
+ 0x1c, 0x6650, 0,
+ 0x0b, 0x33e0, 0,
+ 0x76, 0x0000, 0,
+ 0x07, 0x0103, 0,
+};
+
+
+static const unsigned short doff_seq[] = {
+ /* display off */
+ 0x0b, 0x33e1, 0,
+ 0x07, 0x0102, 150,
+ 0x07, 0x0100, 150,
+ 0x12, 0x0000, 0,
+ 0x10, 0x0000, 0,
+};
+
+static const unsigned short poff_seq[] = {
+ /* power off */
+ /* called after display off */
+ 0x07, 0x0000, 0,
+ 0x10, 0x0000, 0,
+ 0x11, 0x0000, 0,
+};
+
+static const unsigned short sby_seq[] = {
+ /* standby */
+ /* called after display off */
+ 0x10, 0x0001, 0
+};
+
+static const unsigned short csby_seq[] = {
+ /* cancel standby */
+ /* called after display on */
+ 0x10, 0x0000, 0
+};
+
+static void display_off(void)
+{
+ int i;
+ const unsigned short *seq;
+
+ seq = doff_seq;
+ for (i = 0; i < ARRAY_SIZE(doff_seq); i += 3) {
+ write_reg(seq[i], seq[i + 1]);
+ if (seq[i + 2])
+ udelay(seq[i + 2]);
+ }
+}
+
+static void display_on(void)
+{
+ int i;
+ const unsigned short *seq;
+
+ seq = don_seq;
+ for (i = 0; i < ARRAY_SIZE(don_seq); i += 3) {
+ write_reg(seq[i], seq[i + 1]);
+ if (seq[i + 2])
+ udelay(seq[i + 2]);
+ }
+}
+
+static void init_panel_hw(void)
+{
+ int i;
+ const unsigned short *seq;
+
+ seq = pon_seq;
+ for (i = 0; i < ARRAY_SIZE(pon_seq); i += 3) {
+ write_reg(seq[i], seq[i + 1]);
+ if (seq[i + 2])
+ udelay(seq[i + 2]);
+ }
+ display_on();
+}
+
+static int init_pinmux(void)
+{
+ int ret = -EINVAL;
+
+ if (cpu_is_stmp37xx())
+ lcd_pins = &stmp37xx_lcd_pins;
+ else if (cpu_is_stmp378x())
+ lcd_pins = &stmp378x_lcd_pins;
+ else
+ goto out;
+ ret = stmp3xxx_request_pin_group(lcd_pins, "lcd_lms350");
+
+out:
+ return ret;
+}
+
+static int init_pinmux_spi(void)
+{
+ int ret = -EINVAL;
+
+ if (cpu_is_stmp37xx())
+ lcd_spi_pins = stmp37xx_lcd_spi_pins;
+ else if (cpu_is_stmp378x())
+ lcd_spi_pins = stmp378x_lcd_spi_pins;
+ else
+ goto out_1;
+
+ ret = gpio_request(lcd_spi_pins[SPI_MOSI], "lcd_lms350");
+ if (ret)
+ goto out_1;
+
+ ret = gpio_request(lcd_spi_pins[SPI_SCLK], "lcd_lms350");
+ if (ret)
+ goto out_2;
+ ret = gpio_request(lcd_spi_pins[SPI_CS], "lcd_lms350");
+ if (ret)
+ goto out_3;
+
+ /* Enable these pins as outputs */
+ gpio_direction_output(lcd_spi_pins[SPI_MOSI], 1);
+ gpio_direction_output(lcd_spi_pins[SPI_SCLK], 1);
+ gpio_direction_output(lcd_spi_pins[SPI_CS], 1);
+
+ return 0;
+out_3:
+ gpio_free(lcd_spi_pins[SPI_SCLK]);
+out_2:
+ gpio_free(lcd_spi_pins[SPI_MOSI]);
+out_1:
+ return ret;
+}
+
+static void uninit_pinmux(void)
+{
+ stmp3xxx_release_pin_group(lcd_pins, "lcd_lms350");
+}
+
+static void uninit_pinmux_spi(void)
+{
+ gpio_free(lcd_spi_pins[SPI_MOSI]);
+ gpio_free(lcd_spi_pins[SPI_SCLK]);
+ gpio_free(lcd_spi_pins[SPI_CS]);
+}
+
+static struct clk *lcd_clk;
+
+static int init_panel(struct device *dev, dma_addr_t phys, int memsize,
+ struct stmp3xxx_platform_fb_entry *pentry)
+{
+ int ret = 0;
+
+ lcd_clk = clk_get(dev, "lcdif");
+ if (IS_ERR(lcd_clk)) {
+ ret = PTR_ERR(lcd_clk);
+ goto out_1;
+ }
+ ret = clk_enable(lcd_clk);
+ if (ret) {
+ clk_put(lcd_clk);
+ goto out_1;
+ }
+ ret = clk_set_rate(lcd_clk,
+ 1000000/pentry->cycle_time_ns); /* kHz */
+ if (ret) {
+ clk_disable(lcd_clk);
+ clk_put(lcd_clk);
+ goto out_1;
+ }
+
+ /*
+ * Make sure we do a high-to-low transition to reset the panel.
+ * First make it low for 100 msec, hi for 10 msec, low for 10 msec,
+ * then hi.
+ */
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_RESET); /* low */
+ mdelay(100);
+ HW_LCDIF_CTRL1_SET(BM_LCDIF_CTRL1_RESET); /* high */
+ mdelay(10);
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_RESET); /* low */
+
+ /* For the Samsung, Reset must be held low at least 30 uSec
+ * Therefore, we'll hold it low for about 10 mSec just to be sure.
+ * Then we'll wait 1 mSec afterwards.
+ */
+ mdelay(10);
+ HW_LCDIF_CTRL1_SET(BM_LCDIF_CTRL1_RESET); /* high */
+ mdelay(1);
+
+ ret = init_pinmux();
+ if (ret)
+ goto out_1;
+ ret = init_pinmux_spi();
+ if (ret)
+ goto out_2;
+ init_panel_hw();
+
+ setup_dotclk_panel(DOTCLK_V_PULSE_WIDTH, DOTCLK_V_PERIOD,
+ DOTCLK_V_WAIT_CNT, DOTCLK_V_ACTIVE,
+ DOTCLK_H_PULSE_WIDTH, DOTCLK_H_PERIOD,
+ DOTCLK_H_WAIT_CNT, DOTCLK_H_ACTIVE, 0);
+
+ ret = stmp3xxx_lcdif_dma_init(dev, phys, memsize, 1);
+ if (ret)
+ goto out_3;
+
+ stmp3xxx_lcd_set_bl_pdata(pentry->bl_data);
+ stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_INIT, pentry);
+ return 0;
+out_3:
+ uninit_pinmux_spi();
+out_2:
+ uninit_pinmux();
+out_1:
+ return ret;
+}
+
+static void release_panel(struct device *dev,
+ struct stmp3xxx_platform_fb_entry *pentry)
+{
+ stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_RELEASE, pentry);
+ display_off();
+ uninit_pinmux_spi();
+ uninit_pinmux();
+ release_dotclk_panel();
+ stmp3xxx_lcdif_dma_release();
+ clk_disable(lcd_clk);
+ clk_put(lcd_clk);
+}
+
+static int blank_panel(int blank)
+{
+ int ret = 0, count;
+
+ switch (blank) {
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_POWERDOWN:
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_BYPASS_COUNT);
+ for (count = 10000; count; count--) {
+ if (HW_LCDIF_STAT_RD() & BM_LCDIF_STAT_TXFIFO_EMPTY)
+ break;
+ udelay(1);
+ }
+ break;
+
+ case FB_BLANK_UNBLANK:
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_BYPASS_COUNT);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static void stop_panel(void)
+{
+ stmp3xxx_lcdif_stop();
+ display_off();
+}
+
+static void run_panel(void)
+{
+ display_on();
+ stmp3xxx_lcdif_run();
+}
+
+static struct stmp3xxx_platform_fb_entry fb_entry = {
+ .name = "lms350",
+ .x_res = 240,
+ .y_res = 320,
+ .bpp = 32,
+ .cycle_time_ns = 200,
+ .lcd_type = STMP3XXX_LCD_PANEL_DOTCLK,
+ .init_panel = init_panel,
+ .release_panel = release_panel,
+ .blank_panel = blank_panel,
+ .run_panel = run_panel,
+ .stop_panel = stop_panel,
+ .pan_display = stmp3xxx_lcdif_pan_display,
+ .bl_data = &bl_data,
+};
+
+static struct clk *pwm_clk;
+
+static int init_bl(struct stmp3xxx_platform_bl_data *data)
+{
+ int ret = 0;
+
+ pwm_clk = clk_get(NULL, "pwm");
+ if (IS_ERR(pwm_clk)) {
+ ret = PTR_ERR(pwm_clk);
+ goto out;
+ }
+ clk_enable(pwm_clk);
+ stmp3xxx_reset_block(REGS_PWM_BASE, 1);
+
+ ret = stmp3xxx_request_pin(PINID_PWM2, PIN_FUN1, "lcd_lms350");
+ if (ret)
+ goto out_mux;
+
+ stmp3xxx_pin_voltage(PINID_PWM2, PIN_8MA, "lcd_lms350");
+ stmp3xxx_pin_strength(PINID_PWM2, PIN_3_3V, "lcd_lms350");
+
+ HW_PWM_ACTIVEn_WR(2, BF_PWM_ACTIVEn_INACTIVE(0) |
+ BF_PWM_ACTIVEn_ACTIVE(0));
+ HW_PWM_PERIODn_WR(2,
+ BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */
+ BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */
+ BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */
+ BF_PWM_PERIODn_PERIOD(599));
+ HW_PWM_CTRL_SET(BM_PWM_CTRL_PWM2_ENABLE);
+
+ return 0;
+
+out_mux:
+ clk_put(pwm_clk);
+out:
+ return ret;
+}
+
+static void free_bl(struct stmp3xxx_platform_bl_data *data)
+{
+ HW_PWM_ACTIVEn_WR(2, BF_PWM_ACTIVEn_INACTIVE(0) |
+ BF_PWM_ACTIVEn_ACTIVE(0));
+ HW_PWM_PERIODn_WR(2,
+ BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */
+ BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */
+ BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */
+ BF_PWM_PERIODn_PERIOD(599));
+ HW_PWM_CTRL_CLR(BM_PWM_CTRL_PWM2_ENABLE);
+ stmp3xxx_pin_voltage(PINID_PWM2, PIN_4MA, "lcd_lms350");
+ stmp3xxx_pin_strength(PINID_PWM2, PIN_1_8V, "lcd_lms350");
+
+ stmp3xxx_release_pin(PINID_PWM2, "lcd_lms350");
+ clk_disable(pwm_clk);
+ clk_put(pwm_clk);
+}
+
+static int values[] = { 6, 9, 12, 15, 19, 24, 30, 40, 55, 75, 100 };
+static int power[] = {
+ 0, 1500, 3600, 6100, 10300,
+ 15500, 74200, 114200, 155200,
+ 190100, 191000
+};
+
+static int bl_to_power(int br)
+{
+ int base;
+ int rem;
+
+ if (br > 100)
+ br = 100;
+ base = power[br/10];
+ rem = br % 10;
+ if (!rem)
+ return base;
+ else
+ return base + (rem * (power[br/10 + 1]) - base) / 10;
+}
+
+static int set_bl_intensity(struct stmp3xxx_platform_bl_data *data,
+ struct backlight_device *bd, int suspended)
+{
+ int intensity = bd->props.brightness;
+ int scaled_int;
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (suspended)
+ intensity = 0;
+
+ /*
+ * This is not too cool but what can we do?
+ * Luminance changes non-linearly...
+ */
+ if (regulator_set_current_limit(data->regulator, bl_to_power(intensity),
+ bl_to_power(intensity)))
+ return -EBUSY;
+
+ scaled_int = values[intensity/10];
+ if (scaled_int < 100) {
+ int rem = intensity - 10 * (intensity/10); /* r = i % 10;*/
+ scaled_int += rem*(values[intensity/10 + 1] -
+ values[intensity/10])/10;
+ }
+ HW_PWM_ACTIVEn_WR(2,
+ BF_PWM_ACTIVEn_INACTIVE(scaled_int) |
+ BF_PWM_ACTIVEn_ACTIVE(0));
+ HW_PWM_PERIODn_WR(2,
+ BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */
+ BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */
+ BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */
+ BF_PWM_PERIODn_PERIOD(599));
+ return 0;
+}
+
+static struct stmp3xxx_platform_bl_data bl_data = {
+ .bl_max_intensity = 100,
+ .bl_default_intensity = 50,
+ .bl_cons_intensity = 50,
+ .init_bl = init_bl,
+ .free_bl = free_bl,
+ .set_bl_intensity = set_bl_intensity,
+};
+
+static int __init register_devices(void)
+{
+ stmp3xxx_lcd_register_entry(&fb_entry,
+ stmp3xxx_framebuffer.dev.platform_data);
+ return 0;
+}
+subsys_initcall(register_devices);
diff --git a/arch/arm/mach-stmp3xxx/lcd_lms430.c b/arch/arm/mach-stmp3xxx/lcd_lms430.c
new file mode 100644
index 000000000000..3dc31dd641f4
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/lcd_lms430.c
@@ -0,0 +1,376 @@
+/*
+ * Freescale STMP378X Samsung LMS430 LCD panel initialization
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/notifier.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/regs-lcdif.h>
+#include <mach/regs-lradc.h>
+#include <mach/regs-pinctrl.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-pwm.h>
+#include <mach/regs-apbh.h>
+#include <mach/gpio.h>
+#include <mach/pins.h>
+#include <mach/lcdif.h>
+#include <mach/cpu.h>
+#include <mach/stmp3xxx.h>
+
+#include "common.h"
+
+#define DOTCLK_H_ACTIVE 480
+#define DOTCLK_H_PULSE_WIDTH 1
+#define DOTCLK_HF_PORCH 8
+#define DOTCLK_HB_PORCH 15
+#define DOTCLK_H_WAIT_CNT (DOTCLK_H_PULSE_WIDTH + (3 * DOTCLK_HB_PORCH))
+#define DOTCLK_H_PERIOD (DOTCLK_H_WAIT_CNT + DOTCLK_HF_PORCH + DOTCLK_H_ACTIVE)
+
+#define DOTCLK_V_PULSE_WIDTH 1
+#define DOTCLK_V_ACTIVE 272
+#define DOTCLK_VF_PORCH 4
+#define DOTCLK_VB_PORCH 12
+#define DOTCLK_V_WAIT_CNT (DOTCLK_V_PULSE_WIDTH + DOTCLK_VB_PORCH)
+#define DOTCLK_V_PERIOD (DOTCLK_VF_PORCH + DOTCLK_V_ACTIVE + DOTCLK_V_WAIT_CNT)
+
+static struct stmp3xxx_platform_bl_data bl_data;
+static struct pin_group *lcd_pins;
+static unsigned *lcd_spi_pins;
+
+static int init_pinmux(void)
+{
+ int ret = -EINVAL;
+
+ if (cpu_is_stmp37xx())
+ lcd_pins = &stmp37xx_lcd_pins;
+ else if (cpu_is_stmp378x())
+ lcd_pins = &stmp378x_lcd_pins;
+ else
+ goto out;
+ ret = stmp3xxx_request_pin_group(lcd_pins, "lcd_lms430");
+
+out:
+ return ret;
+}
+
+static int init_pinmux_spi(void)
+{
+ int ret = -EINVAL;
+
+ if (cpu_is_stmp37xx())
+ lcd_spi_pins = stmp37xx_lcd_spi_pins;
+ else if (cpu_is_stmp378x())
+ lcd_spi_pins = stmp378x_lcd_spi_pins;
+ else
+ goto out_1;
+
+ ret = gpio_request(lcd_spi_pins[SPI_MOSI], "lcd_lms430");
+ if (ret)
+ goto out_1;
+
+ ret = gpio_request(lcd_spi_pins[SPI_SCLK], "lcd_lms430");
+ if (ret)
+ goto out_2;
+ ret = gpio_request(lcd_spi_pins[SPI_CS], "lcd_lms430");
+ if (ret)
+ goto out_3;
+
+ /* Enable these pins as outputs */
+ gpio_direction_output(lcd_spi_pins[SPI_MOSI], 1);
+ gpio_direction_output(lcd_spi_pins[SPI_SCLK], 1);
+ gpio_direction_output(lcd_spi_pins[SPI_CS], 1);
+
+ return 0;
+out_3:
+ gpio_free(lcd_spi_pins[SPI_SCLK]);
+out_2:
+ gpio_free(lcd_spi_pins[SPI_MOSI]);
+out_1:
+ return ret;
+}
+
+static void uninit_pinmux(void)
+{
+ stmp3xxx_release_pin_group(lcd_pins, "lcd_lms430");
+}
+
+static void uninit_pinmux_spi(void)
+{
+ gpio_free(lcd_spi_pins[SPI_MOSI]);
+ gpio_free(lcd_spi_pins[SPI_SCLK]);
+ gpio_free(lcd_spi_pins[SPI_CS]);
+}
+
+static struct clk *lcd_clk;
+
+static int init_panel(struct device *dev, dma_addr_t phys, int memsize,
+ struct stmp3xxx_platform_fb_entry *pentry)
+{
+ int ret = 0;
+
+ lcd_clk = clk_get(dev, "lcdif");
+ if (IS_ERR(lcd_clk)) {
+ ret = PTR_ERR(lcd_clk);
+ goto out_1;
+ }
+ ret = clk_enable(lcd_clk);
+ if (ret) {
+ clk_put(lcd_clk);
+ goto out_1;
+ }
+ ret = clk_set_rate(lcd_clk,
+ 1000000/pentry->cycle_time_ns); /* kHz */
+ if (ret) {
+ clk_disable(lcd_clk);
+ clk_put(lcd_clk);
+ goto out_1;
+ }
+
+ /*
+ * Make sure we do a high-to-low transition to reset the panel.
+ * First make it low for 100 msec, hi for 10 msec, low for 10 msec,
+ * then hi.
+ */
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_RESET); /* low */
+ mdelay(100);
+ HW_LCDIF_CTRL1_SET(BM_LCDIF_CTRL1_RESET); /* high */
+ mdelay(10);
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_RESET); /* low */
+
+ /* For the Samsung, Reset must be held low at least 30 uSec
+ * Therefore, we'll hold it low for about 10 mSec just to be sure.
+ * Then we'll wait 1 mSec afterwards.
+ */
+ mdelay(10);
+ HW_LCDIF_CTRL1_SET(BM_LCDIF_CTRL1_RESET); /* high */
+ mdelay(1);
+
+ ret = init_pinmux();
+ if (ret)
+ goto out_1;
+ ret = init_pinmux_spi();
+ if (ret)
+ goto out_2;
+
+ setup_dotclk_panel(DOTCLK_V_PULSE_WIDTH, DOTCLK_V_PERIOD,
+ DOTCLK_V_WAIT_CNT, DOTCLK_V_ACTIVE,
+ DOTCLK_H_PULSE_WIDTH, DOTCLK_H_PERIOD,
+ DOTCLK_H_WAIT_CNT, DOTCLK_H_ACTIVE, 0);
+
+ ret = stmp3xxx_lcdif_dma_init(dev, phys, memsize, 1);
+ if (ret)
+ goto out_3;
+
+ stmp3xxx_lcd_set_bl_pdata(pentry->bl_data);
+ stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_INIT, pentry);
+ return 0;
+out_3:
+ uninit_pinmux_spi();
+out_2:
+ uninit_pinmux();
+out_1:
+ return ret;
+}
+
+static void release_panel(struct device *dev,
+ struct stmp3xxx_platform_fb_entry *pentry)
+{
+ stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_RELEASE, pentry);
+ uninit_pinmux_spi();
+ uninit_pinmux();
+ release_dotclk_panel();
+ stmp3xxx_lcdif_dma_release();
+ clk_disable(lcd_clk);
+ clk_put(lcd_clk);
+}
+
+static int blank_panel(int blank)
+{
+ int ret = 0, count;
+
+ switch (blank) {
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_POWERDOWN:
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_BYPASS_COUNT);
+ for (count = 10000; count; count--) {
+ if (HW_LCDIF_STAT_RD() & BM_LCDIF_STAT_TXFIFO_EMPTY)
+ break;
+ udelay(1);
+ }
+ break;
+
+ case FB_BLANK_UNBLANK:
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_BYPASS_COUNT);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static struct stmp3xxx_platform_fb_entry fb_entry = {
+ .name = "lms430",
+ .x_res = 272,
+ .y_res = 480,
+ .bpp = 32,
+ .cycle_time_ns = 150,
+ .lcd_type = STMP3XXX_LCD_PANEL_DOTCLK,
+ .init_panel = init_panel,
+ .release_panel = release_panel,
+ .blank_panel = blank_panel,
+ .run_panel = stmp3xxx_lcdif_run,
+ .stop_panel = stmp3xxx_lcdif_stop,
+ .pan_display = stmp3xxx_lcdif_pan_display,
+ .bl_data = &bl_data,
+};
+
+static struct clk *pwm_clk;
+
+static int init_bl(struct stmp3xxx_platform_bl_data *data)
+{
+ int ret = 0;
+
+ pwm_clk = clk_get(NULL, "pwm");
+ if (IS_ERR(pwm_clk)) {
+ ret = PTR_ERR(pwm_clk);
+ goto out;
+ }
+ clk_enable(pwm_clk);
+ stmp3xxx_reset_block(REGS_PWM_BASE, 1);
+
+ ret = stmp3xxx_request_pin(PINID_PWM2, PIN_FUN1, "lcd_lms430");
+ if (ret)
+ goto out_mux;
+
+ stmp3xxx_pin_voltage(PINID_PWM2, PIN_8MA, "lcd_lms430");
+ stmp3xxx_pin_strength(PINID_PWM2, PIN_3_3V, "lcd_lms430");
+
+ HW_PWM_ACTIVEn_WR(2, BF_PWM_ACTIVEn_INACTIVE(0) |
+ BF_PWM_ACTIVEn_ACTIVE(0));
+ HW_PWM_PERIODn_WR(2,
+ BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */
+ BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */
+ BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */
+ BF_PWM_PERIODn_PERIOD(599));
+ HW_PWM_CTRL_SET(BM_PWM_CTRL_PWM2_ENABLE);
+
+ return 0;
+
+out_mux:
+ clk_put(pwm_clk);
+out:
+ return ret;
+}
+
+static void free_bl(struct stmp3xxx_platform_bl_data *data)
+{
+ HW_PWM_ACTIVEn_WR(2, BF_PWM_ACTIVEn_INACTIVE(0) |
+ BF_PWM_ACTIVEn_ACTIVE(0));
+ HW_PWM_PERIODn_WR(2,
+ BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */
+ BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */
+ BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */
+ BF_PWM_PERIODn_PERIOD(599));
+ HW_PWM_CTRL_CLR(BM_PWM_CTRL_PWM2_ENABLE);
+ stmp3xxx_pin_voltage(PINID_PWM2, PIN_4MA, "lcd_lms430");
+ stmp3xxx_pin_strength(PINID_PWM2, PIN_1_8V, "lcd_lms430");
+
+ stmp3xxx_release_pin(PINID_PWM2, "lcd_lms430");
+ clk_disable(pwm_clk);
+ clk_put(pwm_clk);
+}
+
+static int values[] = { 0, 4, 9, 14, 20, 27, 35, 45, 57, 75, 100 };
+static int power[] = {
+ 0, 1500, 3600, 6100, 10300,
+ 15500, 74200, 114200, 155200,
+ 190100, 191000
+};
+
+static int bl_to_power(int br)
+{
+ int base;
+ int rem;
+
+ if (br > 100)
+ br = 100;
+ base = power[br/10];
+ rem = br % 10;
+ if (!rem)
+ return base;
+ else
+ return base + (rem * (power[br/10 + 1]) - base) / 10;
+}
+
+static int set_bl_intensity(struct stmp3xxx_platform_bl_data *data,
+ struct backlight_device *bd, int suspended)
+{
+ int intensity = bd->props.brightness;
+ int scaled_int;
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (suspended)
+ intensity = 0;
+
+ /*
+ * This is not too cool but what can we do?
+ * Luminance changes non-linearly...
+ */
+ if (regulator_set_current_limit(data->regulator, bl_to_power(intensity), bl_to_power(intensity)))
+ return -EBUSY;
+
+ scaled_int = values[intensity/10];
+ if (scaled_int < 100) {
+ int rem = intensity - 10 * (intensity/10); /* r = i % 10;*/
+ scaled_int += rem*(values[intensity/10 + 1] -
+ values[intensity/10])/10;
+ }
+ HW_PWM_ACTIVEn_WR(2,
+ BF_PWM_ACTIVEn_INACTIVE(scaled_int) |
+ BF_PWM_ACTIVEn_ACTIVE(0));
+ HW_PWM_PERIODn_WR(2,
+ BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */
+ BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */
+ BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */
+ BF_PWM_PERIODn_PERIOD(399));
+ return 0;
+}
+
+static struct stmp3xxx_platform_bl_data bl_data = {
+ .bl_max_intensity = 100,
+ .bl_default_intensity = 50,
+ .bl_cons_intensity = 50,
+ .init_bl = init_bl,
+ .free_bl = free_bl,
+ .set_bl_intensity = set_bl_intensity,
+};
+
+static int __init register_devices(void)
+{
+ stmp3xxx_lcd_register_entry(&fb_entry,
+ stmp3xxx_framebuffer.dev.platform_data);
+ return 0;
+}
+subsys_initcall(register_devices);
diff --git a/arch/arm/mach-stmp3xxx/lradc.c b/arch/arm/mach-stmp3xxx/lradc.c
new file mode 100644
index 000000000000..6f23022b41c6
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/lradc.c
@@ -0,0 +1,294 @@
+/*
+ * Freescale STMP37XX/STMP378X LRADC helper routines
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sysdev.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <mach/hardware.h>
+#include <linux/delay.h>
+#include <mach/regs-lradc.h>
+#include <mach/lradc.h>
+
+static int channels[8];
+
+int hw_lradc_use_channel(int channel)
+{
+ if (channel < 0 || channel > 7)
+ return -EINVAL;
+ channels[channel]++;
+ return 0;
+}
+EXPORT_SYMBOL(hw_lradc_use_channel);
+
+int hw_lradc_unuse_channel(int channel)
+{
+ if (channel < 0 || channel > 7)
+ return -EINVAL;
+ channels[channel]--;
+ return 0;
+}
+EXPORT_SYMBOL(hw_lradc_unuse_channel);
+
+void hw_lradc_reinit(int enable_ground_ref, unsigned freq)
+{
+ HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_SFTRST);
+ udelay(1);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_SFTRST);
+
+ /* Clear the Clock Gate for normal operation */
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_CLKGATE);
+
+ if (enable_ground_ref)
+ HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_ONCHIP_GROUNDREF);
+ else
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_ONCHIP_GROUNDREF);
+
+ HW_LRADC_CTRL3_CLR(BM_LRADC_CTRL3_CYCLE_TIME);
+ HW_LRADC_CTRL3_SET(BF_LRADC_CTRL3_CYCLE_TIME(freq));
+
+ HW_LRADC_CTRL4_CLR(BM_LRADC_CTRL4_LRADC6SELECT |
+ BM_LRADC_CTRL4_LRADC7SELECT);
+ HW_LRADC_CTRL4_SET(BF_LRADC_CTRL4_LRADC6SELECT(VDDIO_VOLTAGE_CH));
+ HW_LRADC_CTRL4_SET(BF_LRADC_CTRL4_LRADC7SELECT(BATTERY_VOLTAGE_CH));
+}
+
+int hw_lradc_init_ladder(int channel, int trigger, unsigned sampling)
+{
+ /*
+ * check if the lradc channel is present in this product
+ */
+ if (!hw_lradc_present(channel))
+ return -ENODEV;
+
+ hw_lradc_configure_channel(channel,
+ !0 /* div2 */,
+ 0 /* acc */,
+ 0 /* num_samples */);
+
+ /* Setup the trigger loop forever */
+ hw_lradc_set_delay_trigger(trigger, 1<<channel,
+ 1<<trigger, 0, sampling);
+
+ /* Clear the accumulator & NUM_SAMPLES */
+ HW_LRADC_CHn_CLR(channel, 0xFFFFFFFF);
+ return 0;
+}
+EXPORT_SYMBOL(hw_lradc_init_ladder);
+
+int hw_lradc_stop_ladder(int channel, int trigger)
+{
+ /*
+ * check if the lradc channel is present in this product
+ */
+ if (!hw_lradc_present(channel))
+ return -ENODEV;
+ hw_lradc_clear_delay_trigger(trigger, 1<<channel, 1<<trigger);
+ return 0;
+}
+EXPORT_SYMBOL(hw_lradc_stop_ladder);
+
+int hw_lradc_present(int channel)
+{
+ if (channel < 0 || channel > 7)
+ return 0;
+ return HW_LRADC_STATUS_RD() & (1<<(16+channel));
+}
+EXPORT_SYMBOL(hw_lradc_present);
+
+void hw_lradc_configure_channel(int channel, int enable_div2,
+ int enable_acc, int samples)
+{
+ if (enable_div2)
+ HW_LRADC_CTRL2_SET(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1<<channel));
+ else
+ HW_LRADC_CTRL2_CLR(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1<<channel));
+
+ /* Clear the accumulator & NUM_SAMPLES */
+ HW_LRADC_CHn_CLR(channel, 0xFFFFFFFF);
+
+ /* Sets NUM_SAMPLES bitfield of HW_LRADC_CHn register. */
+ HW_LRADC_CHn_CLR(channel, BM_LRADC_CHn_NUM_SAMPLES);
+ HW_LRADC_CHn_SET(channel, BF_LRADC_CHn_NUM_SAMPLES(samples));
+
+ if (enable_acc)
+ HW_LRADC_CHn_SET(channel, BM_LRADC_CHn_ACCUMULATE);
+ else
+ HW_LRADC_CHn_CLR(channel, BM_LRADC_CHn_ACCUMULATE);
+}
+EXPORT_SYMBOL(hw_lradc_configure_channel);
+
+void hw_lradc_set_delay_trigger(int trigger, u32 trigger_lradc,
+ u32 delay_triggers, u32 loops, u32 delays)
+{
+ /* set TRIGGER_LRADCS in HW_LRADC_DELAYn */
+ HW_LRADC_DELAYn_SET(trigger,
+ BF_LRADC_DELAYn_TRIGGER_LRADCS(trigger_lradc));
+ HW_LRADC_DELAYn_SET(trigger,
+ BF_LRADC_DELAYn_TRIGGER_DELAYS(delay_triggers));
+
+ HW_LRADC_DELAYn_CLR(trigger,
+ BM_LRADC_DELAYn_LOOP_COUNT | BM_LRADC_DELAYn_DELAY);
+ HW_LRADC_DELAYn_SET(trigger,
+ BF_LRADC_DELAYn_LOOP_COUNT(loops));
+ HW_LRADC_DELAYn_SET(trigger,
+ BF_LRADC_DELAYn_DELAY(delays));
+}
+EXPORT_SYMBOL(hw_lradc_set_delay_trigger);
+
+void hw_lradc_clear_delay_trigger(int trigger, u32 trigger_lradc,
+ u32 delay_triggers)
+{
+ HW_LRADC_DELAYn_CLR(trigger,
+ BF_LRADC_DELAYn_TRIGGER_LRADCS(trigger_lradc));
+ HW_LRADC_DELAYn_CLR(trigger,
+ BF_LRADC_DELAYn_TRIGGER_DELAYS(delay_triggers));
+}
+EXPORT_SYMBOL(hw_lradc_clear_delay_trigger);
+
+void hw_lradc_set_delay_trigger_kick(int trigger, int value)
+{
+ if (value)
+ HW_LRADC_DELAYn_SET(trigger, BM_LRADC_DELAYn_KICK);
+ else
+ HW_LRADC_DELAYn_CLR(trigger, BM_LRADC_DELAYn_KICK);
+}
+EXPORT_SYMBOL(hw_lradc_set_delay_trigger_kick);
+
+u32 hw_lradc_vddio(void)
+{
+ /* Clear the Soft Reset and Clock Gate for normal operation */
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_SFTRST | BM_LRADC_CTRL0_CLKGATE);
+
+ /*
+ * Clear the divide by two for channel 6 since
+ * it has a HW divide-by-two built in.
+ */
+ HW_LRADC_CTRL2_CLR(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1<<VDDIO_VOLTAGE_CH));
+
+ /* Clear the accumulator & NUM_SAMPLES */
+ HW_LRADC_CHn_CLR(VDDIO_VOLTAGE_CH, 0xFFFFFFFF);
+
+ /* Clear the interrupt flag */
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC6_IRQ);
+
+ /*
+ * Get VddIO; this is the max scale value for the button resistor
+ * ladder.
+ * schedule ch 6:
+ */
+ HW_LRADC_CTRL0_SET(BF_LRADC_CTRL0_SCHEDULE(1<<VDDIO_VOLTAGE_CH));
+
+ /* wait for completion */
+ while ((HW_LRADC_CTRL1_RD() & BM_LRADC_CTRL1_LRADC6_IRQ) !=
+ BM_LRADC_CTRL1_LRADC6_IRQ)
+ cpu_relax();
+
+ /* Clear the interrupt flag */
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC6_IRQ);
+
+ /* read ch 6 value. */
+ return HW_LRADC_CHn_RD(6) & BM_LRADC_CHn_VALUE;
+}
+EXPORT_SYMBOL(hw_lradc_vddio);
+
+static u32 lradc_registers[0x16];
+static int do_gate;
+
+static int hw_lradc_suspend(struct sys_device *dev, pm_message_t state)
+{
+ int i;
+
+ do_gate = 1;
+ for (i = 0; i < ARRAY_SIZE(channels); i++)
+ if (channels[i] > 0) {
+ do_gate = 0;
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lradc_registers); i++)
+ lradc_registers[i] = __raw_readl(REGS_LRADC_BASE + (i << 4));
+
+ if (do_gate)
+ HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_CLKGATE);
+ return 0;
+}
+
+static int hw_lradc_resume(struct sys_device *dev)
+{
+ int i;
+
+ if (do_gate) {
+ HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_SFTRST);
+ udelay(10);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_SFTRST |
+ BM_LRADC_CTRL0_CLKGATE);
+ }
+ for (i = 0; i < ARRAY_SIZE(lradc_registers); i++)
+ __raw_writel(lradc_registers[i], REGS_LRADC_BASE + (i << 4));
+ return 0;
+}
+
+static struct sysdev_class stmp3xxx_lradc_sysclass = {
+ .name = "stmp3xxx-lradc",
+#ifdef CONFIG_PM
+ .suspend = hw_lradc_suspend,
+ .resume = hw_lradc_resume,
+#endif
+};
+
+static struct sys_device stmp3xxx_lradc_device = {
+ .id = -1,
+ .cls = &stmp3xxx_lradc_sysclass,
+};
+
+static int __initdata lradc_freq = LRADC_CLOCK_6MHZ;
+
+static int __init lradc_freq_setup(char *str)
+{
+ long freq;
+
+ if (strict_strtol(str, 0, &freq) < 0)
+ return 0;
+
+ if (freq < 0)
+ return 0;
+ if (freq >= 6)
+ lradc_freq = LRADC_CLOCK_6MHZ;
+ else if (freq >= 4)
+ lradc_freq = LRADC_CLOCK_4MHZ;
+ else if (freq >= 3)
+ lradc_freq = LRADC_CLOCK_3MHZ;
+ else if (freq >= 2)
+ lradc_freq = LRADC_CLOCK_2MHZ;
+ else
+ return 0;
+ return 1;
+}
+
+__setup("lradc_freq=", lradc_freq_setup);
+
+static int __init hw_lradc_init(void)
+{
+ hw_lradc_reinit(0, lradc_freq);
+ sysdev_class_register(&stmp3xxx_lradc_sysclass);
+ sysdev_register(&stmp3xxx_lradc_device);
+ return 0;
+}
+subsys_initcall(hw_lradc_init);
diff --git a/arch/arm/mach-stmp3xxx/mmc.c b/arch/arm/mach-stmp3xxx/mmc.c
new file mode 100644
index 000000000000..d90d680908ce
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/mmc.c
@@ -0,0 +1,161 @@
+/*
+ * Freescale STMP37XX/STMP378X MMC pin multiplexing
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <mach/stmp3xxx.h>
+#include <mach/regs-ssp.h>
+#include "common.h"
+
+#if defined(CONFIG_MACH_STMP378X)
+#define MMC_POWER PINID_PWM3
+#define MMC_WP PINID_PWM4
+#elif defined(CONFIG_MACH_STMP37XX)
+#define MMC_POWER PINID_PWM3
+#define MMC_WP PINID_PWM4
+#else
+#define MMC_POWER PINID_NO_PIN
+#define MMC_WP PINID_NO_PIN
+#endif
+
+static int mmc_drive_power;
+static int mmc_wp_supported;
+
+static struct pin_desc mmc_pins_desc[] = {
+ { PINID_SSP1_DATA0, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 },
+ { PINID_SSP1_DATA1, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 },
+ { PINID_SSP1_DATA2, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 },
+ { PINID_SSP1_DATA3, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 },
+ { PINID_SSP1_CMD, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 },
+ { PINID_SSP1_SCK, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_SSP1_DETECT, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+};
+
+static struct pin_group mmc_pins = {
+ .pins = mmc_pins_desc,
+ .nr_pins = ARRAY_SIZE(mmc_pins_desc),
+};
+
+static int stmp3xxxmmc_get_wp(void)
+{
+ if (mmc_wp_supported)
+ return gpio_get_value(MMC_WP);
+
+ return 0;
+}
+
+static int stmp3xxxmmc_hw_init_ssp1(void)
+{
+ int ret;
+
+ mmc_drive_power = stmp3xxx_valid_pin(MMC_POWER);
+ mmc_wp_supported = stmp3xxx_valid_pin(MMC_WP);
+
+ ret = stmp3xxx_request_pin_group(&mmc_pins, "mmc");
+ if (ret)
+ goto out;
+
+ if (mmc_wp_supported) {
+ /* Configure write protect GPIO pin */
+ ret = gpio_request(MMC_WP, "mmc wp");
+ if (ret)
+ goto out_wp;
+
+ gpio_set_value(MMC_WP, 0);
+ gpio_direction_input(MMC_WP);
+ }
+
+ if (mmc_drive_power) {
+ /* Configure POWER pin as gpio to drive power to MMC slot */
+ ret = gpio_request(MMC_POWER, "mmc power");
+ if (ret)
+ goto out_power;
+
+ gpio_direction_output(MMC_POWER, 0);
+ mdelay(100);
+ }
+
+ return 0;
+
+out_power:
+ if (mmc_wp_supported)
+ gpio_free(MMC_WP);
+out_wp:
+ stmp3xxx_release_pin_group(&mmc_pins, "mmc");
+out:
+ return ret;
+}
+
+static void stmp3xxxmmc_hw_release_ssp1(void)
+{
+ if (mmc_drive_power)
+ gpio_free(MMC_POWER);
+
+ if (mmc_wp_supported)
+ gpio_free(MMC_WP);
+
+ stmp3xxx_release_pin_group(&mmc_pins, "mmc");
+}
+
+static void stmp3xxxmmc_cmd_pullup_ssp1(int enable)
+{
+ stmp3xxx_pin_pullup(PINID_SSP1_CMD, enable, "mmc");
+}
+
+static unsigned long stmp3xxxmmc_setclock_ssp1(unsigned long hz)
+{
+ struct clk *ssp = clk_get(NULL, "ssp"), *parent;
+ char *p;
+ long r;
+
+ /* using SSP1, no timeout, clock rate 1 */
+ HW_SSP_TIMING_WR(BF_SSP_TIMING_CLOCK_DIVIDE(2) |
+ BF_SSP_TIMING_TIMEOUT(0xFFFF));
+
+ if (hz > 1000000)
+ p = "io";
+ else
+ p = "osc_24M";
+
+ parent = clk_get(NULL, p);
+ clk_set_parent(ssp, parent);
+ r = clk_set_rate(ssp, 2 * hz / 1000);
+ clk_put(parent);
+ clk_put(ssp);
+
+ return hz;
+}
+
+static struct stmp3xxxmmc_platform_data mmc_data = {
+ .hw_init = stmp3xxxmmc_hw_init_ssp1,
+ .hw_release = stmp3xxxmmc_hw_release_ssp1,
+ .get_wp = stmp3xxxmmc_get_wp,
+ .cmd_pullup = stmp3xxxmmc_cmd_pullup_ssp1,
+ .setclock = stmp3xxxmmc_setclock_ssp1,
+ .read_uA = 50000,
+ .write_uA = 70000,
+};
+
+void stmp3xxx_set_mmc_data(struct device *dev)
+{
+ dev->platform_data = &mmc_data;
+}
+EXPORT_SYMBOL(stmp3xxx_set_mmc_data);
diff --git a/arch/arm/mach-stmp3xxx/otp.c b/arch/arm/mach-stmp3xxx/otp.c
new file mode 100644
index 000000000000..ac0de7038490
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/otp.c
@@ -0,0 +1,434 @@
+/*
+ * Unique ID manipulation: Freescale STMP378X OTP bits read/write procedures
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/fcntl.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <mach/unique-id.h>
+#include <mach/regs-ocotp.h>
+#include <mach/regs-power.h>
+
+static DEFINE_MUTEX(otp_mutex);
+static unsigned otp_mode;
+static unsigned long otp_hclk_saved;
+static u32 otp_voltage_saved;
+
+static int otp_full; /* = 0. By default, show/set only customer bits */
+#define OTP_USER_OFFSET 0
+#define OTP_USER_SIZE 4
+
+/**
+ * otp_wait_busy - wait for completion of operation
+ *
+ * @flags: flags that should be clear in addition to _BUSY and _ERROR
+ *
+ * Returns 0 on success or -ETIMEDOUT on error
+ **/
+static int otp_wait_busy(u32 flags)
+{
+ int count;
+ u32 c;
+
+ for (count = 10000; count >= 0; count--) {
+ c = HW_OCOTP_CTRL_RD();
+ if (!(c & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR | flags)))
+ break;
+ cpu_relax();
+ }
+ if (count < 0)
+ return -ETIMEDOUT;
+ return 0;
+}
+
+/**
+ * otp_open - open OTP bits for read or write access
+ *
+ * @mode: either O_RDONLY or O_WRONLY
+ *
+ * Returns 0 on success, error code otherwise
+ **/
+static int otp_open(int mode)
+{
+ int r;
+ struct clk *hclk;
+ int err;
+
+ if (!mutex_trylock(&otp_mutex)) {
+ printk(KERN_ERR"%s: already opened\n", __func__);
+ return -EAGAIN;
+ }
+
+ if (mode == O_RDONLY) {
+ pr_debug("%s: read-only mode\n", __func__);
+
+ r = otp_wait_busy(0);
+ if (r) {
+ err = -ETIMEDOUT;
+ goto out;
+ }
+
+ /* 2. Set RD_BANK_OPEN */
+ HW_OCOTP_CTRL_SET(BM_OCOTP_CTRL_RD_BANK_OPEN);
+ udelay(10);
+
+ otp_wait_busy(0);
+ }
+
+ else if (mode == O_WRONLY) {
+ pr_debug("%s: write-only mode\n", __func__);
+ hclk = clk_get(NULL, "hclk");
+ if (IS_ERR(hclk)) {
+ err = PTR_ERR(hclk);
+ goto out;
+ }
+
+ /*
+ WARNING ACHTUNG UWAGA
+
+ the code below changes HCLK clock rate to 24M. This is
+ required to write OTP bits (7.2.2 in STMP378x Target
+ Specification), and might affect LCD operation, for example.
+ Moreover, this hacky code changes VDDIO to 2.8V; and resto-
+ res it only on otp_close(). This may affect... anything.
+
+ You are warned now.
+ */
+ otp_hclk_saved = clk_get_rate(hclk);
+ clk_set_rate(hclk, 24000);
+ /* Set the voltage to 2.8V */
+ otp_voltage_saved = HW_POWER_VDDIOCTRL_RD();
+ HW_POWER_VDDIOCTRL_WR(
+ (otp_voltage_saved & ~BM_POWER_VDDIOCTRL_TRG) | 0x00);
+
+ r = otp_wait_busy(BM_OCOTP_CTRL_RD_BANK_OPEN);
+ if (r < 0) {
+ HW_POWER_VDDIOCTRL_WR(otp_voltage_saved);
+ clk_set_rate(hclk, otp_hclk_saved);
+ clk_put(hclk);
+ err = -ETIMEDOUT;
+ goto out;
+ }
+
+ clk_put(hclk);
+ }
+
+ else {
+ pr_debug("%s: unknown mode '%d'\n", __func__, mode);
+ err = -EINVAL;
+ goto out;
+ }
+
+ otp_mode = mode;
+ return 0;
+out:
+ mutex_unlock(&otp_mutex);
+ pr_debug("%s: status %d\n", __func__, err);
+ return err;
+}
+
+/**
+ * otp_close - close the OTP bits after opening by otp_open
+ **/
+static void otp_close(void)
+{
+ struct clk *hclk;
+
+ if (!mutex_is_locked(&otp_mutex)) {
+ printk(KERN_ERR"%s: wasn't opened\n", __func__);
+ return;
+ }
+
+ if (otp_mode == O_RDONLY) {
+ /* 5. clear RD_BANK_OPEN */
+ HW_OCOTP_CTRL_CLR(BM_OCOTP_CTRL_RD_BANK_OPEN);
+ }
+
+ else if (otp_mode == O_WRONLY) {
+ hclk = clk_get(NULL, "hclk");
+ clk_set_rate(hclk, otp_hclk_saved);
+ HW_POWER_VDDIOCTRL_WR(otp_voltage_saved);
+ otp_wait_busy(0);
+ HW_OCOTP_CTRL_SET(BM_OCOTP_CTRL_RELOAD_SHADOWS);
+ otp_wait_busy(BM_OCOTP_CTRL_RELOAD_SHADOWS);
+ }
+
+ else {
+ return; /* -EINVAL. Who does really check close? */
+ }
+
+ otp_mode = 0;
+ mutex_unlock(&otp_mutex);
+}
+
+/**
+ * otp_read_bits - read the content of OTP
+ *
+ * @start: offset from 0, in u32's
+ * @len: number of OTP u32's to read
+ * @bits: caller-allocated buffer to save bits
+ * @size: size of @bits
+ *
+ * Returns number of u32's saved to buffer
+ **/
+static size_t otp_read_bits(int start, int len, u32 *bits, size_t size)
+{
+ int ofs;
+
+ BUG_ON(!mutex_is_locked(&otp_mutex));
+
+ /* read all stuff that caller needs */
+ if (start + len > 4 * 8) /* 4 banks, 8 registers each */
+ len = 4 * 8 - start;
+
+ for (ofs = start; ofs < len; ofs++) {
+ if (size/sizeof(*bits) <= 0) /* we drained out the buffer */
+ break;
+ *bits = __raw_readl(REGS_OCOTP_BASE +
+ HW_OCOTP_CUSTn_OFFSET(0) + ofs * 0x10);
+ bits++;
+ size -= sizeof(*bits);
+ }
+
+ return ofs - start; /* number of u32's that we saved to buffer */
+}
+
+/**
+ * otp_write_bits - store OTP bits
+ *
+ * @offset: offset from 0, in u32's
+ * @data: the u32 to write
+ * @magic: the magic value to be stored in UNLOCK field
+ *
+ **/
+static int otp_write_bits(int offset, u32 data, u32 magic)
+{
+ u32 c;
+ int r;
+
+ BUG_ON(!mutex_is_locked(&otp_mutex));
+
+ if (offset < 0 || offset > 0x1F)
+ return -EINVAL;
+
+ c = HW_OCOTP_CTRL_RD();
+ c &= ~BM_OCOTP_CTRL_ADDR;
+ c |= BF_OCOTP_CTRL_ADDR(offset);
+ c |= BF_OCOTP_CTRL_WR_UNLOCK(magic);
+ HW_OCOTP_CTRL_WR(c);
+
+ HW_OCOTP_DATA_WR(data);
+
+ r = otp_wait_busy(0);
+ if (r < 0)
+ return r;
+
+ udelay(2);
+ return 0;
+}
+
+static ssize_t otp_id_show(void *context, char *page, int ascii)
+{
+ char s[60];
+ int ret;
+ int n, i, j, r;
+ u32 otp_bits[4 * 8];
+
+ r = otp_open(O_RDONLY);
+ if (r < 0)
+ return 0;
+ n = otp_read_bits(0, 4 * 8, otp_bits, sizeof(otp_bits));
+ otp_close();
+
+ ret = 0;
+
+
+ if (ascii) {
+
+ strcpy(page, "");
+ ret = 0;
+
+ if (otp_full) {
+ for (i = 0; i < 4; i++) {
+
+ ret += sprintf(s, "Bank %d: ", i);
+ strcat(page, s);
+
+ for (j = 0; j < 8; j++) {
+
+ if (i * 4 + j > n)
+ break;
+ ret += sprintf(s, "%08X ",
+ otp_bits[i * 4 + j]);
+ strcat(page, s);
+ }
+
+ strcat(page, "\n");
+ ret++;
+ }
+ } else {
+ for (i = 0; i < OTP_USER_SIZE; i++) {
+ ret += sprintf(s, "%08X ",
+ otp_bits[i + OTP_USER_OFFSET]);
+ strcat(page, s);
+ }
+ strcat(page, "\n");
+ ret++;
+ }
+ } else {
+
+ if (otp_full) {
+ memcpy(page, otp_bits, sizeof(otp_bits));
+ ret = sizeof(otp_bits);
+ } else {
+ memcpy(page, otp_bits + OTP_USER_OFFSET,
+ OTP_USER_SIZE * sizeof(u32));
+ ret = OTP_USER_SIZE * sizeof(u32);
+ }
+ }
+
+ return ret;
+}
+
+static int otp_check_dry_run(const char *page, size_t count)
+{
+ if (count >= 3 && memcmp(page, "+++", 3) == 0)
+ return 3;
+ return 0;
+}
+
+static ssize_t otp_id_store(void *context, const char *page,
+ size_t count, int ascii)
+{
+ int r = 0;
+ const char *p, *cp, *d;
+ unsigned long index, value;
+ char tmps[20]; /* subject of strtoul */
+ int dry_run;
+
+ r = otp_open(O_WRONLY);
+ if (r < 0) {
+ printk(KERN_ERR"Cannot open OTP in WRITE mode\n");
+ return r;
+ }
+
+ if (ascii) {
+
+ dry_run = otp_check_dry_run(page, count);
+ if (dry_run > 0)
+ page += dry_run;
+
+ index = 0;
+ cp = page;
+
+ memset(tmps, 0, sizeof(tmps));
+
+ for (index = 0, cp = page; cp != NULL; index++) {
+ p = strchr(cp, ',');
+
+ d = strchr(cp, ':');
+ if (d && (!p || d < p)) {
+ strncpy(tmps, cp,
+ min_t(int, d - cp, sizeof(tmps) - 1));
+ r = strict_strtoul(tmps, 0, &index);
+ if (r < 0) {
+ pr_debug("Cannot parse '%s'\n", tmps);
+ break;
+ }
+ cp = d + 1;
+ }
+
+ memset(tmps, 0, sizeof(tmps));
+
+ if (!p)
+ strncpy(tmps, cp, sizeof(tmps));
+ else
+ strncpy(tmps, cp,
+ min_t(int, p - cp, sizeof(tmps) - 1));
+ r = strict_strtoul(tmps, 0, &value);
+ if (r < 0) {
+ pr_debug("Cannot parse '%s'\n", tmps);
+ break;
+ }
+
+ memset(tmps, 0, sizeof(tmps));
+
+ cp = p ? ++p : NULL;
+
+ if (!otp_full) {
+ index += OTP_USER_OFFSET;
+ if (index > OTP_USER_SIZE) {
+ printk(KERN_ERR"Cannot write at "
+ "offset %ld\n", index);
+ continue;
+ }
+ }
+
+ r = 0;
+ if (!dry_run) {
+ pr_debug("Index %ld, value 0x%08lx\n",
+ index, value);
+ r = otp_write_bits(index, value, 0x3e77);
+ } else
+ printk(KERN_NOTICE
+ "Dry-run: writing 0x%08lX => [%ld]\n",
+ value, index);
+ if (r < 0)
+ break;
+ }
+ } else {
+ printk(KERN_ERR"Binary write is not supported\n");
+ r = -ENOSYS;
+ }
+ otp_close();
+ return (r >= 0) ? count : r;
+}
+
+static struct uid_ops otp_ops = {
+ .id_show = otp_id_show,
+ .id_store = otp_id_store,
+};
+
+static int __init_or_module otp_init(void)
+{
+ void *p;
+
+ mutex_init(&otp_mutex);
+ p = uid_provider_init("otp", &otp_ops, NULL);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+ return 0;
+}
+
+static void __exit otp_remove(void)
+{
+ uid_provider_remove("otp");
+}
+
+module_param(otp_full, int, 0600);
+module_init(otp_init);
+module_exit(otp_remove);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
+MODULE_DESCRIPTION("Unique ID: OTP");
diff --git a/arch/arm/mach-stmp3xxx/persistent.c b/arch/arm/mach-stmp3xxx/persistent.c
new file mode 100644
index 000000000000..ea4efb650f82
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/persistent.c
@@ -0,0 +1,257 @@
+/*
+ * Freescale STMP378X Persistent bits manipulation driver
+ *
+ * Author: Pantelis Antoniou <pantelis@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sysdev.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <mach/stmp3xxx_regs.h>
+#include <mach/stmp3xxx.h>
+
+#include <mach/regs-rtc.h>
+
+struct stmp3xxx_persistent_data {
+ struct device *dev;
+ struct stmp3xxx_platform_persistent_data *pdata;
+ int count;
+ struct attribute_group attr_group;
+ /* attribute ** follow */
+ /* device_attribute follow */
+};
+
+#define pd_attribute_ptr(x) \
+ ((struct attribute **)((x) + 1))
+#define pd_device_attribute_ptr(x) \
+ ((struct device_attribute *)(pd_attribute_ptr(x) + (x)->count + 1))
+
+static inline u32 persistent_reg_read(int reg)
+{
+ u32 msk;
+
+ /* wait for stable value */
+ msk = BF_RTC_STAT_STALE_REGS(0x01 << reg);
+ while (HW_RTC_STAT_RD() & msk)
+ cpu_relax();
+
+ return __raw_readl(REGS_RTC_BASE + 0x60 + (reg * 0x10));
+}
+
+static inline void persistent_reg_wait_settle(int reg)
+{
+ u32 msk;
+
+ /* wait until the change is propagated */
+ msk = BF_RTC_STAT_NEW_REGS(0x01 << reg);
+ while (HW_RTC_STAT_RD() & msk)
+ cpu_relax();
+}
+
+static inline void persistent_reg_write(u32 val, int reg)
+{
+ __raw_writel(val, REGS_RTC_BASE + 0x60 + (reg * 0x10));
+ persistent_reg_wait_settle(reg);
+}
+
+static inline void persistent_reg_set(u32 val, int reg)
+{
+ __raw_writel(val, REGS_RTC_BASE + 0x60 + (reg * 0x10) + 0x4);
+ persistent_reg_wait_settle(reg);
+}
+
+static inline void persistent_reg_clr(u32 val, int reg)
+{
+ __raw_writel(val, REGS_RTC_BASE + 0x60 + (reg * 0x10) + 0x8);
+ persistent_reg_wait_settle(reg);
+}
+
+static inline void persistent_reg_tog(u32 val, int reg)
+{
+ __raw_writel(val, REGS_RTC_BASE + 0x60 + (reg * 0x10) + 0xc);
+ persistent_reg_wait_settle(reg);
+}
+
+static ssize_t
+persistent_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stmp3xxx_persistent_data *pd = platform_get_drvdata(pdev);
+ struct device_attribute *devattr = pd_device_attribute_ptr(pd);
+ const struct stmp3xxx_persistent_bit_config *pb;
+ int idx;
+ u32 val;
+
+ idx = attr - devattr;
+ if ((unsigned int)idx >= pd->count)
+ return -EINVAL;
+
+ pb = &pd->pdata->bit_config_tab[idx];
+
+ /* read value and shift */
+ val = persistent_reg_read(pb->reg);
+ val >>= pb->start;
+ val &= (1 << pb->width) - 1;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t
+persistent_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stmp3xxx_persistent_data *pd = platform_get_drvdata(pdev);
+ struct device_attribute *devattr = pd_device_attribute_ptr(pd);
+ const struct stmp3xxx_persistent_bit_config *pb;
+ int idx, r;
+ unsigned long val, msk;
+
+ idx = attr - devattr;
+ if ((unsigned int)idx >= pd->count)
+ return -EINVAL;
+
+ pb = &pd->pdata->bit_config_tab[idx];
+
+ /* get value to write */
+ r = strict_strtoul(buf, 10, &val);
+ if (r != 0)
+ return r;
+
+ /* verify it fits */
+ if ((unsigned int)val > (1 << pb->width) - 1)
+ return -EINVAL;
+
+ /* lockless update, first clear the area */
+ msk = ((1 << pb->width) - 1) << pb->start;
+ persistent_reg_clr(msk, pb->reg);
+
+ /* shift into position */
+ val <<= pb->start;
+ persistent_reg_set(val, pb->reg);
+
+ return count;
+}
+
+
+static int __devinit stmp3xxx_persistent_probe(struct platform_device *pdev)
+{
+ struct stmp3xxx_persistent_data *pd;
+ struct stmp3xxx_platform_persistent_data *pdata;
+ const struct stmp3xxx_persistent_bit_config *pb;
+ struct attribute **attr;
+ struct device_attribute *devattr;
+ int i, cnt, size;
+ int err;
+
+ pdata = pdev->dev.platform_data;
+ if (pdata == NULL)
+ return -ENODEV;
+
+ cnt = pdata->bit_config_cnt;
+ size = sizeof(*pd) +
+ (cnt + 1) * sizeof(struct atrribute *) +
+ cnt * sizeof(struct device_attribute);
+ pd = kzalloc(size, GFP_KERNEL);
+ if (pd == NULL)
+ return -ENOMEM;
+ pd->dev = &pdev->dev;
+ pd->pdata = pdata;
+ platform_set_drvdata(pdev, pd);
+ pd->count = cnt;
+ attr = pd_attribute_ptr(pd);
+ devattr = pd_device_attribute_ptr(pd);
+
+ /* build the attributes structures */
+ pd->attr_group.attrs = attr;
+ pb = pdata->bit_config_tab;
+ for (i = 0; i < cnt; i++) {
+ devattr[i].attr.name = pb[i].name;
+ devattr[i].attr.mode = S_IWUSR | S_IRUGO;
+ devattr[i].show = persistent_show;
+ devattr[i].store = persistent_store;
+ attr[i] = &devattr[i].attr;
+ }
+
+ err = sysfs_create_group(&pdev->dev.kobj, &pd->attr_group);
+ if (err != 0) {
+ platform_set_drvdata(pdev, NULL);
+ kfree(pd);
+ return err;
+ }
+
+ return 0;
+}
+
+static int stmp3xxx_persistent_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_persistent_data *pd;
+
+ pd = platform_get_drvdata(pdev);
+ sysfs_remove_group(&pdev->dev.kobj, &pd->attr_group);
+ platform_set_drvdata(pdev, NULL);
+ kfree(pd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int
+stmp3xxx_persistent_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+static int stmp3xxx_persistent_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define stmp3xxx_persistent_suspend NULL
+#define stmp3xxx_persistent_resume NULL
+#endif
+
+static struct platform_driver stmp3xxx_persistent_driver = {
+ .probe = stmp3xxx_persistent_probe,
+ .remove = stmp3xxx_persistent_remove,
+ .suspend = stmp3xxx_persistent_suspend,
+ .resume = stmp3xxx_persistent_resume,
+ .driver = {
+ .name = "stmp3xxx-persistent",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmp3xxx_persistent_init(void)
+{
+ return platform_driver_register(&stmp3xxx_persistent_driver);
+}
+
+static void __exit stmp3xxx_persistent_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_persistent_driver);
+}
+
+MODULE_AUTHOR("Pantelis Antoniou <pantelis@embeddedalley.com>");
+MODULE_DESCRIPTION("Persistent bits user-access driver");
+MODULE_LICENSE("GPL");
+
+module_init(stmp3xxx_persistent_init);
+module_exit(stmp3xxx_persistent_exit);
diff --git a/arch/arm/mach-stmp3xxx/pinmux.c b/arch/arm/mach-stmp3xxx/pinmux.c
new file mode 100644
index 000000000000..263f58550d2a
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/pinmux.c
@@ -0,0 +1,614 @@
+/*
+ * Freescale STMP378X/STMP378X Pin Multiplexing
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sysdev.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/sysdev.h>
+#include <linux/irq.h>
+#include <mach/hardware.h>
+#include <mach/regs-pinctrl.h>
+#include "pinmux.h"
+
+
+static struct stmp3xxx_pinmux_bank pinmux_banks[STMP3XXX_PINMUX_NR_BANKS] = {
+ [0] = {
+ .hw_muxsel = {HW_PINCTRL_MUXSEL0_ADDR, HW_PINCTRL_MUXSEL1_ADDR},
+ .hw_drive = {HW_PINCTRL_DRIVE0_ADDR, HW_PINCTRL_DRIVE1_ADDR,
+ HW_PINCTRL_DRIVE2_ADDR, HW_PINCTRL_DRIVE3_ADDR},
+ .hw_pull = HW_PINCTRL_PULL0_ADDR,
+ .functions = {0x0, 0x1, 0x2, 0x3},
+ .strengths = {0x0, 0x1, 0x2, 0x3, 0xff},
+ .hw_gpio_read = HW_PINCTRL_DIN0_ADDR,
+ .hw_gpio_set = HW_PINCTRL_DOUT0_ADDR + HW_STMP3xxx_SET,
+ .hw_gpio_clr = HW_PINCTRL_DOUT0_ADDR + HW_STMP3xxx_CLR,
+ .hw_gpio_doe = HW_PINCTRL_DOE0_ADDR,
+ .irq = IRQ_GPIO0,
+
+ .pin2irq = HW_PINCTRL_PIN2IRQ0_ADDR,
+ .irqstat = HW_PINCTRL_IRQSTAT0_ADDR,
+ .irqlevel = HW_PINCTRL_IRQLEVEL0_ADDR,
+ .irqpolarity = HW_PINCTRL_IRQPOL0_ADDR,
+ .irqen = HW_PINCTRL_IRQEN0_ADDR,
+ },
+ [1] = {
+ .hw_muxsel = {HW_PINCTRL_MUXSEL2_ADDR, HW_PINCTRL_MUXSEL3_ADDR},
+ .hw_drive = {HW_PINCTRL_DRIVE4_ADDR, HW_PINCTRL_DRIVE5_ADDR,
+ HW_PINCTRL_DRIVE6_ADDR, HW_PINCTRL_DRIVE7_ADDR},
+ .hw_pull = HW_PINCTRL_PULL1_ADDR,
+ .functions = {0x0, 0x1, 0x2, 0x3},
+ .strengths = {0x0, 0x1, 0x2, 0x3, 0xff},
+ .hw_gpio_read = HW_PINCTRL_DIN1_ADDR,
+ .hw_gpio_set = HW_PINCTRL_DOUT1_ADDR + HW_STMP3xxx_SET,
+ .hw_gpio_clr = HW_PINCTRL_DOUT1_ADDR + HW_STMP3xxx_CLR,
+ .hw_gpio_doe = HW_PINCTRL_DOE1_ADDR,
+ .irq = IRQ_GPIO1,
+
+ .pin2irq = HW_PINCTRL_PIN2IRQ1_ADDR,
+ .irqstat = HW_PINCTRL_IRQSTAT1_ADDR,
+ .irqlevel = HW_PINCTRL_IRQLEVEL1_ADDR,
+ .irqpolarity = HW_PINCTRL_IRQPOL1_ADDR,
+ .irqen = HW_PINCTRL_IRQEN1_ADDR,
+
+ },
+ [2] = {
+ .hw_muxsel = {HW_PINCTRL_MUXSEL4_ADDR, HW_PINCTRL_MUXSEL5_ADDR},
+ .hw_drive = {HW_PINCTRL_DRIVE8_ADDR, HW_PINCTRL_DRIVE9_ADDR,
+ HW_PINCTRL_DRIVE10_ADDR, HW_PINCTRL_DRIVE11_ADDR},
+ .hw_pull = HW_PINCTRL_PULL2_ADDR,
+ .functions = {0x0, 0x1, 0x2, 0x3},
+ .strengths = {0x0, 0x1, 0x2, 0x1, 0x2},
+ .hw_gpio_read = HW_PINCTRL_DIN2_ADDR,
+ .hw_gpio_set = HW_PINCTRL_DOUT2_ADDR + HW_STMP3xxx_SET,
+ .hw_gpio_clr = HW_PINCTRL_DOUT2_ADDR + HW_STMP3xxx_CLR,
+ .hw_gpio_doe = HW_PINCTRL_DOE2_ADDR,
+ .irq = IRQ_GPIO2,
+
+ .pin2irq = HW_PINCTRL_PIN2IRQ2_ADDR,
+ .irqstat = HW_PINCTRL_IRQSTAT2_ADDR,
+ .irqlevel = HW_PINCTRL_IRQLEVEL2_ADDR,
+ .irqpolarity = HW_PINCTRL_IRQPOL2_ADDR,
+ .irqen = HW_PINCTRL_IRQEN2_ADDR,
+ },
+ [3] = {
+ .hw_muxsel = {HW_PINCTRL_MUXSEL6_ADDR, HW_PINCTRL_MUXSEL7_ADDR},
+ .hw_drive = {HW_PINCTRL_DRIVE12_ADDR, HW_PINCTRL_DRIVE13_ADDR,
+ HW_PINCTRL_DRIVE14_ADDR, 0},
+ .hw_pull = HW_PINCTRL_PULL3_ADDR,
+ .functions = {0x0, 0x1, 0x2, 0x3},
+ .strengths = {0x0, 0x1, 0x2, 0x3, 0xff},
+ },
+};
+
+struct stmp3xxx_pinmux_bank *stmp_pinmux_banks(int i)
+{
+ BUG_ON(i < 0 || i > sizeof(pinmux_banks)/sizeof(pinmux_banks[0]));
+ return &pinmux_banks[i];
+}
+/* Compares pin owner and caller labels */
+static inline int stmp3xxx_check_owner(u32 bank, u32 pin, char *label)
+{
+ return label && strcmp(label, pinmux_banks[bank].pin_labels[pin]);
+}
+
+/* Check if requested pin is owned by caller */
+static int stmp3xxx_check_pin(u32 bank, u32 pin, char *label)
+{
+ struct stmp3xxx_pinmux_bank *pbank = &pinmux_banks[bank];
+
+ if (!test_bit(pin, &pbank->pin_map)) {
+ printk(KERN_WARNING
+ "%s: Accessing free pin %d:%d, caller %s\n",
+ __func__, bank, pin, label);
+
+ return -EINVAL;
+ }
+
+ if (stmp3xxx_check_owner(bank, pin, label)) {
+ printk(KERN_WARNING
+ "%s: Wrong pin owner %d:%d, caller %s owner %s\n",
+ __func__, bank, pin, label, pbank->pin_labels[pin]);
+
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void stmp3xxx_pin_strength(unsigned id, enum pin_strength strength, char *label)
+{
+ struct stmp3xxx_pinmux_bank *pbank;
+ u32 hwdrive, shift, val;
+ u32 bank, pin;
+
+ bank = STMP3XXX_PINID_TO_BANK(id);
+ pin = STMP3XXX_PINID_TO_PINNUM(id);
+ BUG_ON(bank >= STMP3XXX_PINMUX_NR_BANKS);
+
+ pr_debug("%s: label %s bank %d pin %d strength %d\n", __func__, label,
+ bank, pin, strength);
+
+ pbank = &pinmux_banks[bank];
+
+ hwdrive = pbank->hw_drive[pin / HW_DRIVE_PIN_NUM];
+ shift = (pin % HW_DRIVE_PIN_NUM) * HW_DRIVE_PIN_LEN;
+ val = pbank->strengths[strength];
+ if (val == 0xff) {
+ printk(KERN_WARNING
+ "%s: strength is not supported for bank %d, caller %s",
+ __func__, bank, label);
+ return;
+ }
+
+ spin_lock(&pbank->lock);
+ if (stmp3xxx_check_pin(bank, pin, label)) {
+ printk(KERN_WARNING "%s: Pin %d:%d access failure\n",
+ __func__, bank, pin);
+ goto out;
+ }
+
+ pr_debug("%s: writing 0x%x to 0x%x register\n", __func__,
+ val << shift, hwdrive);
+ __raw_writel(HW_DRIVE_PINDRV_MASK << shift, hwdrive + HW_STMP3xxx_CLR);
+ __raw_writel(val << shift, hwdrive + HW_STMP3xxx_SET);
+
+out:
+ spin_unlock(&pbank->lock);
+}
+
+void stmp3xxx_pin_voltage(unsigned id, enum pin_voltage voltage, char *label)
+{
+ struct stmp3xxx_pinmux_bank *pbank;
+ u32 hwdrive, shift;
+ u32 bank, pin;
+
+ bank = STMP3XXX_PINID_TO_BANK(id);
+ pin = STMP3XXX_PINID_TO_PINNUM(id);
+ BUG_ON(bank >= STMP3XXX_PINMUX_NR_BANKS);
+
+ pr_debug("%s: label %s bank %d pin %d voltage %d\n", __func__, label,
+ bank, pin, voltage);
+
+ pbank = &pinmux_banks[bank];
+
+ hwdrive = pbank->hw_drive[pin / HW_DRIVE_PIN_NUM];
+ shift = (pin % HW_DRIVE_PIN_NUM) * HW_DRIVE_PIN_LEN;
+
+ spin_lock(&pbank->lock);
+ if (stmp3xxx_check_pin(bank, pin, label)) {
+ printk(KERN_WARNING "%s: Pin %d:%d access failure\n",
+ __func__, bank, pin);
+ goto out;
+ }
+
+ pr_debug("%s: changing 0x%x bit in 0x%x register\n",
+ __func__, HW_DRIVE_PINV_MASK << shift, hwdrive);
+ if (voltage == PIN_1_8V)
+ __raw_writel(HW_DRIVE_PINV_MASK << shift,
+ hwdrive + HW_STMP3xxx_CLR);
+ else
+ __raw_writel(HW_DRIVE_PINV_MASK << shift,
+ hwdrive + HW_STMP3xxx_SET);
+out:
+ spin_unlock(&pbank->lock);
+}
+
+void stmp3xxx_pin_pullup(unsigned id, int enable, char *label)
+{
+ struct stmp3xxx_pinmux_bank *pbank;
+ u32 hwpull;
+ u32 bank, pin;
+
+ bank = STMP3XXX_PINID_TO_BANK(id);
+ pin = STMP3XXX_PINID_TO_PINNUM(id);
+ BUG_ON(bank >= STMP3XXX_PINMUX_NR_BANKS);
+
+ pr_debug("%s: label %s bank %d pin %d enable %d\n", __func__, label,
+ bank, pin, enable);
+
+ pbank = &pinmux_banks[bank];
+
+ hwpull = pbank->hw_pull;
+
+ spin_lock(&pbank->lock);
+ if (stmp3xxx_check_pin(bank, pin, label)) {
+ printk(KERN_WARNING "%s: Pin %d:%d access failure\n",
+ __func__, bank, pin);
+ goto out;
+ }
+
+ pr_debug("%s: changing 0x%x bit in 0x%x register\n",
+ __func__, 1 << pin, hwpull);
+ __raw_writel(1 << pin,
+ hwpull + (enable ? HW_STMP3xxx_SET : HW_STMP3xxx_CLR));
+out:
+ spin_unlock(&pbank->lock);
+}
+
+int stmp3xxx_request_pin(unsigned id, enum pin_fun fun, char *label)
+{
+ struct stmp3xxx_pinmux_bank *pbank;
+ u32 bank, pin;
+ int ret = 0;
+
+ bank = STMP3XXX_PINID_TO_BANK(id);
+ pin = STMP3XXX_PINID_TO_PINNUM(id);
+ BUG_ON(bank >= STMP3XXX_PINMUX_NR_BANKS);
+
+ pr_debug("%s: label %s bank %d pin %d fun %d\n", __func__, label,
+ bank, pin, fun);
+
+ pbank = &pinmux_banks[bank];
+
+ spin_lock(&pbank->lock);
+ if (test_bit(pin, &pbank->pin_map)) {
+ printk(KERN_WARNING
+ "%s: CONFLICT DETECTED pin %d:%d caller %s owner %s\n",
+ __func__, bank, pin, label, pbank->pin_labels[pin]);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ set_bit(pin, &pbank->pin_map);
+ pbank->pin_labels[pin] = label;
+
+ stmp3xxx_set_pin_type_chklock(id, fun, 0);
+
+out:
+ spin_unlock(&pbank->lock);
+ return ret;
+}
+
+void stmp3xxx_set_pin_type_chklock(unsigned id, enum pin_fun fun, int lock)
+{
+ struct stmp3xxx_pinmux_bank *pbank;
+ u32 hwmux, shift, val;
+ u32 bank, pin;
+
+ bank = STMP3XXX_PINID_TO_BANK(id);
+ pin = STMP3XXX_PINID_TO_PINNUM(id);
+ BUG_ON(bank >= STMP3XXX_PINMUX_NR_BANKS);
+
+ pbank = &pinmux_banks[bank];
+ hwmux = pbank->hw_muxsel[pin / HW_MUXSEL_PIN_NUM];
+ shift = (pin % HW_MUXSEL_PIN_NUM) * HW_MUXSEL_PIN_LEN;
+
+ val = pbank->functions[fun];
+ if (lock)
+ spin_lock(&pbank->lock);
+ shift = (pin % HW_MUXSEL_PIN_NUM) * HW_MUXSEL_PIN_LEN;
+ pr_debug("%s: writing 0x%x to 0x%x register\n",
+ __func__, val << shift, hwmux);
+ __raw_writel(HW_MUXSEL_PINFUN_MASK << shift, hwmux + HW_STMP3xxx_CLR);
+ __raw_writel(val << shift, hwmux + HW_STMP3xxx_SET);
+ if (lock)
+ spin_unlock(&pbank->lock);
+}
+
+void stmp3xxx_release_pin(unsigned id, char *label)
+{
+ struct stmp3xxx_pinmux_bank *pbank;
+ u32 bank, pin;
+
+ bank = STMP3XXX_PINID_TO_BANK(id);
+ pin = STMP3XXX_PINID_TO_PINNUM(id);
+ BUG_ON(bank >= STMP3XXX_PINMUX_NR_BANKS);
+
+ pr_debug("%s: label %s bank %d pin %d\n", __func__, label, bank, pin);
+
+ pbank = &pinmux_banks[bank];
+
+ spin_lock(&pbank->lock);
+ if (stmp3xxx_check_pin(bank, pin, label)) {
+ printk(KERN_WARNING "%s: Pin %d:%d access failure\n",
+ __func__, bank, pin);
+ goto out;
+ }
+
+ clear_bit(pin, &pbank->pin_map);
+ pbank->pin_labels[pin] = NULL;
+out:
+ spin_unlock(&pbank->lock);
+}
+
+int stmp3xxx_request_pin_group(struct pin_group *pin_group, char *label)
+{
+
+ struct pin_desc *pin;
+ int p;
+ int err = 0;
+
+ /* Allocate and configure pins */
+ for (p = 0; p < pin_group->nr_pins; p++) {
+ pr_debug("%s: #%d\n", __func__, p);
+ pin = &pin_group->pins[p];
+
+ err = stmp3xxx_request_pin(pin->id, pin->fun, label);
+ if (err)
+ goto out_err;
+
+ stmp3xxx_pin_strength(pin->id, pin->strength, label);
+ stmp3xxx_pin_voltage(pin->id, pin->voltage, label);
+ stmp3xxx_pin_pullup(pin->id, pin->pullup, label);
+ }
+
+ return 0;
+
+out_err:
+ /* Release allocated pins in case of error */
+ while (--p >= 0) {
+ pr_debug("%s: releasing #%d\n", __func__, p);
+ stmp3xxx_release_pin(pin_group->pins[p].id, label);
+ }
+ return err;
+}
+
+void stmp3xxx_release_pin_group(struct pin_group *pin_group, char *label)
+{
+ struct pin_desc *pin;
+ int p;
+
+ for (p = 0; p < pin_group->nr_pins; p++) {
+ pin = &pin_group->pins[p];
+ stmp3xxx_release_pin(pin->id, label);
+ }
+}
+
+/**
+ * stmp3xxx_configure_irq
+ *
+ * Configure the pin as interrupt source
+ *
+ * id: pin id
+ * type: interrupt type, as in linux/irq.h; IRQ_TYPE_NONE disables irq
+ *
+ **/
+
+void stmp3xxx_configure_irq(unsigned id, unsigned type)
+{
+ struct stmp3xxx_pinmux_bank *pbank;
+ unsigned pin, bank;
+ unsigned m;
+ int l, p;
+
+ bank = STMP3XXX_PINID_TO_BANK(id);
+ pin = STMP3XXX_PINID_TO_PINNUM(id);
+ BUG_ON(bank >= STMP3XXX_PINMUX_NR_BANKS);
+ pbank = &pinmux_banks[bank];
+
+ spin_lock(&pbank->lock);
+ if (stmp3xxx_check_pin(bank, pin, NULL)) {
+ printk(KERN_WARNING "%s: Pin %d:%d access failure\n",
+ __func__, bank, pin);
+ goto out;
+ }
+
+ pr_debug("%s: caller configures %d(%d:%d) as IRQ type 0x%x\n",
+ __func__, id, bank, pin, type);
+ m = 1<<pin;
+
+ if (type == IRQ_TYPE_NONE) {
+ pr_debug("%s: cleared\n", __func__);
+ __raw_writel(m, pbank->irqen + HW_STMP3xxx_CLR);
+ __raw_writel(m, pbank->pin2irq + HW_STMP3xxx_CLR);
+ goto out;
+ }
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ l = 0; p = 1; break;
+ case IRQ_TYPE_EDGE_FALLING:
+ l = 0; p = 0; break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ l = 1; p = 1; break;
+ case IRQ_TYPE_LEVEL_LOW:
+ l = 1; p = 0; break;
+ default:
+ pr_debug("%s: Incorrect GPIO interrupt type 0x%x\n",
+ __func__, type);
+ goto out;
+ }
+
+ __raw_writel(m,
+ pbank->irqlevel + (l ? HW_STMP3xxx_SET : HW_STMP3xxx_CLR));
+ __raw_writel(m,
+ pbank->irqpolarity + (p ? HW_STMP3xxx_SET : HW_STMP3xxx_CLR));
+
+ __raw_writel(m, pbank->irqstat + HW_STMP3xxx_CLR);
+
+ __raw_writel(m, pbank->pin2irq + HW_STMP3xxx_SET);
+
+ __raw_writel(m, pbank->irqen + HW_STMP3xxx_SET);
+out:
+ spin_unlock(&pbank->lock);
+ return;
+}
+EXPORT_SYMBOL(stmp3xxx_configure_irq);
+
+void stmp3xxx_pin_ack_irq(int irq)
+{
+ int b;
+ u32 stat;
+
+ for (b = 0; b < STMP3XXX_PINMUX_NR_BANKS; b++) {
+ if (irq != pinmux_banks[b].irq)
+ continue;
+ stat = __raw_readl(pinmux_banks[b].irqstat);
+ if (stat)
+ __raw_writel(stat,
+ pinmux_banks[b].irqstat + HW_STMP3xxx_CLR);
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_pin_ack_irq);
+
+#ifdef CONFIG_PM
+static int pinmux_suspend(struct sys_device *dev, pm_message_t state)
+{
+ return 0;
+}
+
+static int pinmux_resume(struct sys_device *dev)
+{
+ return 0;
+}
+#endif
+
+static struct sysdev_class stmp3xxx_pinmux_sysclass = {
+ .name = "stmp3xxx-pinmux",
+#ifdef CONFIG_PM
+ .suspend = pinmux_suspend,
+ .resume = pinmux_resume,
+#endif
+};
+
+static struct sys_device stmp3xxx_pinmux_device = {
+ .id = -1,
+ .cls = &stmp3xxx_pinmux_sysclass,
+};
+
+static char *str_function(unsigned f)
+{
+ static char s[10];
+
+ if (f != 3)
+ sprintf(s, "FUNC_%d", f);
+ else
+ strcpy(s, "GPIO");
+ return s;
+}
+
+static char *str_strength(unsigned f)
+{
+ static char *s[] = {
+ "4mA", "8mA", "12mA", "16mA", "20mA"
+ };
+ return s[f];
+}
+
+static char *str_pullup(unsigned f)
+{
+ return f ? "Pullup" : "";
+}
+
+static char *str_voltage(unsigned f)
+{
+ return f ? "3.3V" : "1.8V";
+}
+
+static char *str_irqtype(int level, int pol)
+{
+ if (level)
+ return pol ? "high lvl" : "low lvl";
+ return pol ? "raising edge" : "falling edge";
+}
+
+static char *str_irq(int v_icfg, int v_ien, int v_ilevel,
+ int v_ipol, int v_istat)
+{
+ static char irqcfg[30];
+
+ if (v_icfg <= 0)
+ return "";
+ sprintf(irqcfg, "IRQ%c %s %s",
+ v_istat ? '!' : ' ',
+ str_irqtype(v_ilevel, v_ipol),
+ v_ien ? "Enabled" : "Disabled");
+ return irqcfg;
+}
+
+static ssize_t pinmux_all_list(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ int b, pin, n = 0;
+ struct stmp3xxx_pinmux_bank *p;
+ u32 hwpull, hwmux, hwdrive, hwdrive_v, pm;
+ int shift1, shift2;
+ unsigned v_strength, v_pull, v_fun, v_voltage;
+ int v_ien, v_icfg, v_ilevel, v_ipol, v_istat;
+
+ for (b = 0; b < STMP3XXX_PINMUX_NR_BANKS; b++) {
+
+ p = pinmux_banks + b;
+
+ for (pin = 0, pm = 1; pin < 32; pin++, pm <<= 1) {
+ if ((p->pin_map & pm) == 0)
+ continue;
+
+ hwmux = p->hw_muxsel[pin / HW_MUXSEL_PIN_NUM];
+ shift1 = (pin % HW_MUXSEL_PIN_NUM) * HW_MUXSEL_PIN_LEN;
+ hwpull = p->hw_pull;
+ hwdrive = p->hw_drive[pin / HW_DRIVE_PIN_NUM];
+ shift2 = (pin % HW_DRIVE_PIN_NUM) * HW_DRIVE_PIN_LEN;
+
+ hwdrive_v = __raw_readl(hwdrive) >> shift2;
+ v_strength = hwdrive_v & HW_DRIVE_PINDRV_MASK;
+ v_voltage = hwdrive_v & HW_DRIVE_PINV_MASK;
+ v_pull = __raw_readl(hwpull) & pm;
+ v_fun = (__raw_readl(hwmux) >> shift1) &
+ HW_MUXSEL_PINFUN_MASK;
+
+ if (p->irqen == 0) {
+ v_icfg = -1;
+ v_ien = -1;
+ v_ilevel = -1;
+ v_ipol = -1;
+ v_istat = -1;
+ } else {
+ v_icfg = (__raw_readl(p->pin2irq) & pm)
+ >> pin;
+ v_ien = (__raw_readl(p->irqen) & pm)
+ >> pin;
+ v_ilevel = (__raw_readl(p->irqlevel) & pm)
+ >> pin;
+ v_ipol = (__raw_readl(p->irqpolarity) & pm)
+ >> pin;
+ v_istat = (__raw_readl(p->irqstat) & pm)
+ >> pin;
+ }
+ n += sprintf(buf + n,
+ "%d:%d\t%-15.15s\t%s\t%s\t%s\t%s\t%s\n",
+ b, pin, p->pin_labels[pin],
+ str_function(v_fun),
+ str_strength(v_strength),
+ str_voltage(v_voltage),
+ str_pullup(v_pull),
+ str_irq(v_icfg, v_ien, v_ilevel, v_ipol, v_istat));
+ }
+ }
+ return n;
+}
+SYSDEV_ATTR(all, 0444, pinmux_all_list, NULL);
+
+static int __init stmp3xxx_pinmux_init(void)
+{
+ int b;
+
+ for (b = 0; b < STMP3XXX_PINMUX_NR_BANKS; b++)
+ spin_lock_init(&pinmux_banks[b].lock);
+ sysdev_class_register(&stmp3xxx_pinmux_sysclass);
+ sysdev_register(&stmp3xxx_pinmux_device);
+ sysdev_create_file(&stmp3xxx_pinmux_device, &attr_all);
+
+ return 0;
+}
+
+arch_initcall(stmp3xxx_pinmux_init);
+
+MODULE_AUTHOR("Vladislav Buzov");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-stmp3xxx/pinmux.h b/arch/arm/mach-stmp3xxx/pinmux.h
new file mode 100644
index 000000000000..60e2e14bcd84
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/pinmux.h
@@ -0,0 +1,161 @@
+/*
+ * Freescale STMP37XX/STMP378X Pin Multiplexing
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __PINMUX_H
+#define __PINMUX_H
+
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+/* Pin definitions */
+#include <mach/pins.h>
+
+/*
+ * Each pin may be routed up to four different HW interfaces
+ * including GPIO
+ */
+enum pin_fun {
+ PIN_FUN1 = 0,
+ PIN_FUN2,
+ PIN_FUN3,
+ PIN_GPIO,
+};
+
+/*
+ * Each pin may have different output drive strength in range from
+ * 4mA to 20mA. The most common case is 4, 8 and 12 mA strengths.
+ */
+enum pin_strength {
+ PIN_4MA = 0,
+ PIN_8MA,
+ PIN_12MA,
+ PIN_16MA,
+ PIN_20MA,
+};
+
+/*
+ * Each pin can be programmed for 1.8V or 3.3V
+ */
+enum pin_voltage {
+ PIN_1_8V = 0,
+ PIN_3_3V,
+};
+
+/*
+ * Structure to define a group of pins and their parameters
+ */
+struct pin_desc {
+ unsigned id;
+ enum pin_fun fun;
+ enum pin_strength strength;
+ enum pin_voltage voltage;
+ unsigned pullup:1;
+};
+
+struct pin_group {
+ struct pin_desc *pins;
+ int nr_pins;
+};
+
+/* Set pin drive strength */
+void stmp3xxx_pin_strength(unsigned id, enum pin_strength strength,
+ char *label);
+
+/* Set pin voltage */
+void stmp3xxx_pin_voltage(unsigned id, enum pin_voltage voltage, char *label);
+
+/* Enable pull-up resisitor for a pin */
+void stmp3xxx_pin_pullup(unsigned id, int enable, char *label);
+
+/*
+ * Request a pin ownership, only one module (identified by @label)
+ * may own a pin.
+ */
+int stmp3xxx_request_pin(unsigned id, enum pin_fun fun, char *label);
+
+/* Release pin */
+void stmp3xxx_release_pin(unsigned id, char *label);
+
+void stmp3xxx_set_pin_type_chklock(unsigned id, enum pin_fun fun, int lock);
+#define stmp3xxx_set_pin_type(id, fun) stmp37xx_set_pin_type_chklock(id, fun, 1)
+
+/* Request and configure a group of pins */
+int stmp3xxx_request_pin_group(struct pin_group *pin_group, char *label);
+
+/* Release a group of pin for device */
+void stmp3xxx_release_pin_group(struct pin_group *pin_group, char *label);
+
+/* Configure pin as interrupt source */
+void stmp3xxx_configure_irq(unsigned id, unsigned type);
+
+/* Acknowledge irq in pinctrl registers */
+void stmp3xxx_pin_ack_irq(int irq);
+
+/*
+ * Each bank is associated with a number of registers to control
+ * pin function, drive strength, voltage and pull-up reigster. The
+ * number of registers of a given type depends on the number of bits
+ * describin particular pin.
+ */
+#define HW_MUXSEL_NUM 2 /* registers per bank */
+#define HW_MUXSEL_PIN_LEN 2 /* bits per pin */
+#define HW_MUXSEL_PIN_NUM 16 /* pins per register */
+#define HW_MUXSEL_PINFUN_MASK 0x3 /* pin function mask */
+#define HW_MUXSEL_PINFUN_NUM 4 /* four options for a pin */
+
+#define HW_DRIVE_NUM 4 /* registers per bank */
+#define HW_DRIVE_PIN_LEN 4 /* bits per pin */
+#define HW_DRIVE_PIN_NUM 8 /* pins per register */
+#define HW_DRIVE_PINDRV_MASK 0x3 /* pin strength mask - 2 bits */
+#define HW_DRIVE_PINDRV_NUM 5 /* five possible strength values */
+#define HW_DRIVE_PINV_MASK 0x4 /* pin voltage mask - 1 bit */
+
+
+struct stmp3xxx_pinmux_bank {
+ /* Pins allocation map */
+ unsigned long pin_map;
+
+ /* Pin owner names */
+ char *pin_labels[STMP3XXX_PINMUX_BANK_SIZE];
+
+ /* spin lock to protect an access to pin map */
+ spinlock_t lock;
+
+ /* Bank registers */
+ u32 hw_muxsel[HW_MUXSEL_NUM];
+ u32 hw_drive[HW_DRIVE_NUM];
+ u32 hw_pull;
+
+ u32 pin2irq, irqlevel, irqpolarity, irqen, irqstat;
+
+ /* HW MUXSEL register function bit values */
+ u8 functions[HW_MUXSEL_PINFUN_NUM];
+
+ /*
+ * HW DRIVE register strength bit values:
+ * 0xff - requested strength is not supported for this bank
+ */
+ u8 strengths[HW_DRIVE_PINDRV_NUM];
+
+ /* GPIO things */
+ u32 hw_gpio_read, hw_gpio_set, hw_gpio_clr, hw_gpio_doe;
+ int irq;
+};
+
+struct stmp3xxx_pinmux_bank *stmp_pinmux_banks(int i);
+
+#endif /* __PINMUX_H */
diff --git a/arch/arm/mach-stmp3xxx/pm.c b/arch/arm/mach-stmp3xxx/pm.c
new file mode 100644
index 000000000000..804431a251ab
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/pm.c
@@ -0,0 +1,592 @@
+/*
+ * Static Power Management support for Freescale STMP37XX/STMP378X
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/suspend.h>
+#include <linux/rtc.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kthread.h>
+
+#include <asm/dma.h>
+#include <asm/cacheflush.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/time.h>
+
+#include <mach/platform.h>
+
+#include <mach/regs-icoll.h>
+#include <mach/regs-rtc.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-pinctrl.h>
+#include <mach/regs-power.h>
+#include <mach/regs-gpmi.h>
+#include <mach/regs-pwm.h>
+#include <mach/regs-usbctrl.h>
+#include <mach/regs-apbh.h>
+#include <mach/regs-apbx.h>
+#include <mach/regs-rtc.h>
+#include <mach/regs-dram.h>
+#include <mach/regs-emi.h>
+#include <mach/regs-digctl.h>
+
+#include "clock.h"
+#include "sleep.h"
+#include "common.h"
+#define PENDING_IRQ_RETRY 100
+static void *saved_sram;
+static int saved_sleep_state;
+
+#define WAIT_DC_OK_CYCLES 24000
+#define WAIT_CYCLE(n) for (i = 0; i < n; i++);
+#define LOWER_VDDIO 10
+#define LOWER_VDDA 9
+#define LOWER_VDDD 0xa
+#define MAX_POWEROFF_CODE_SIZE (6 * 1024)
+
+static void stmp378x_standby(void)
+{
+ int i;
+ u32 reg_vddd, reg_vdda, reg_vddio;
+
+ /* DDR EnterSelfrefreshMode */
+ HW_DRAM_CTL08_SET(BM_DRAM_CTL08_SREFRESH);
+
+ /* Gating EMI CLock */
+ HW_CLKCTRL_EMI_SET(BM_CLKCTRL_EMI_CLKGATE);
+
+ /* Disable PLL */
+ HW_CLKCTRL_PLLCTRL0_CLR(BM_CLKCTRL_PLLCTRL0_POWER);
+
+ /* Reduce the VDDIO (3.050 volt) */
+ reg_vddio = HW_POWER_VDDIOCTRL_RD();
+ HW_POWER_VDDIOCTRL_WR(HW_POWER_VDDIOCTRL_RD() |
+ BM_POWER_VDDIOCTRL_BO_OFFSET);
+ HW_POWER_VDDIOCTRL_WR(
+ (HW_POWER_VDDIOCTRL_RD() &
+ ~BM_POWER_VDDIOCTRL_TRG)|LOWER_VDDIO);
+ WAIT_CYCLE(WAIT_DC_OK_CYCLES)
+
+ while (!(HW_POWER_STS_RD() & BM_POWER_STS_DC_OK))
+ ;
+
+ /* Reduce VDDA 1.725volt */
+ reg_vdda = HW_POWER_VDDACTRL_RD();
+ HW_POWER_VDDACTRL_WR(HW_POWER_VDDACTRL_RD() |
+ BM_POWER_VDDACTRL_BO_OFFSET);
+ HW_POWER_VDDACTRL_WR((HW_POWER_VDDACTRL_RD() &
+ ~BM_POWER_VDDACTRL_TRG) | LOWER_VDDA);
+ WAIT_CYCLE(WAIT_DC_OK_CYCLES)
+
+ /* wait for DC_OK */
+ while (!(HW_POWER_STS_RD() & BM_POWER_STS_DC_OK))
+ ;
+
+ /* Reduce VDDD 1.000 volt */
+ reg_vddd = HW_POWER_VDDDCTRL_RD();
+ HW_POWER_VDDDCTRL_WR(HW_POWER_VDDDCTRL_RD() |
+ BM_POWER_VDDDCTRL_BO_OFFSET);
+ HW_POWER_VDDDCTRL_WR((HW_POWER_VDDDCTRL_RD() &
+ ~BM_POWER_VDDDCTRL_TRG) | LOWER_VDDD);
+
+ WAIT_CYCLE(WAIT_DC_OK_CYCLES)
+
+ while (!(HW_POWER_STS_RD() & BM_POWER_STS_DC_OK))
+ ;
+
+ /* optimize the DCDC loop gain */
+ HW_POWER_LOOPCTRL_WR((HW_POWER_LOOPCTRL_RD() &
+ ~BM_POWER_LOOPCTRL_EN_RCSCALE));
+ HW_POWER_LOOPCTRL_WR((HW_POWER_LOOPCTRL_RD() &
+ ~BM_POWER_LOOPCTRL_DC_R) |
+ (2<<BP_POWER_LOOPCTRL_DC_R));
+
+ /* half the fets */
+ HW_POWER_MINPWR_SET(BM_POWER_MINPWR_HALF_FETS);
+
+ HW_POWER_LOOPCTRL_CLR(BM_POWER_LOOPCTRL_CM_HYST_THRESH);
+ HW_POWER_LOOPCTRL_CLR(BM_POWER_LOOPCTRL_EN_CM_HYST);
+ HW_POWER_LOOPCTRL_CLR(BM_POWER_LOOPCTRL_EN_DF_HYST);
+ /* enable PFM */
+ HW_POWER_LOOPCTRL_SET(BM_POWER_LOOPCTRL_HYST_SIGN);
+ HW_POWER_MINPWR_SET(BM_POWER_MINPWR_EN_DC_PFM);
+
+ HW_CLKCTRL_CPU_SET(BM_CLKCTRL_CPU_INTERRUPT_WAIT);
+ /* Power off ... */
+ asm("mcr p15, 0, r2, c7, c0, 4");
+ HW_CLKCTRL_CPU_CLR(BM_CLKCTRL_CPU_INTERRUPT_WAIT);
+
+ /* restore the DCDC parameter */
+
+ HW_POWER_MINPWR_CLR(BM_POWER_MINPWR_EN_DC_PFM);
+ HW_POWER_LOOPCTRL_CLR(BM_POWER_LOOPCTRL_HYST_SIGN);
+ HW_POWER_LOOPCTRL_SET(BM_POWER_LOOPCTRL_EN_DF_HYST);
+ HW_POWER_LOOPCTRL_SET(BM_POWER_LOOPCTRL_EN_CM_HYST);
+ HW_POWER_LOOPCTRL_SET(BM_POWER_LOOPCTRL_CM_HYST_THRESH);
+
+ HW_POWER_LOOPCTRL_WR((HW_POWER_LOOPCTRL_RD() &
+ ~BM_POWER_LOOPCTRL_DC_R) |
+ (2<<BP_POWER_LOOPCTRL_DC_R));
+ HW_POWER_LOOPCTRL_WR((HW_POWER_LOOPCTRL_RD() &
+ ~BM_POWER_LOOPCTRL_EN_RCSCALE) |
+ (3 << BP_POWER_LOOPCTRL_EN_RCSCALE));
+
+
+ /* Restore VDDD */
+ HW_POWER_VDDDCTRL_WR(reg_vddd);
+
+ WAIT_CYCLE(WAIT_DC_OK_CYCLES)
+ while (!(HW_POWER_STS_RD() & BM_POWER_STS_DC_OK))
+ ;
+
+ HW_POWER_VDDACTRL_WR(reg_vdda);
+ WAIT_CYCLE(WAIT_DC_OK_CYCLES)
+ while (!(HW_POWER_STS_RD() & BM_POWER_STS_DC_OK))
+ ;
+
+ HW_POWER_VDDIOCTRL_WR(reg_vddio);
+ WAIT_CYCLE(WAIT_DC_OK_CYCLES)
+ while (!(HW_POWER_STS_RD() & BM_POWER_STS_DC_OK))
+ ;
+
+
+ /* Enable PLL */
+ HW_CLKCTRL_PLLCTRL0_SET(BM_CLKCTRL_PLLCTRL0_POWER);
+ /* Ungating EMI CLock */
+ HW_CLKCTRL_EMI_CLR(BM_CLKCTRL_EMI_CLKGATE);
+
+ /* LeaveSelfrefreshMode */
+ HW_DRAM_CTL08_CLR(BM_DRAM_CTL08_SREFRESH);
+ WAIT_CYCLE(WAIT_DC_OK_CYCLES)
+}
+
+static inline void do_standby(void)
+{
+ void (*stmp37xx_cpu_standby_ptr) (void);
+ struct clk *cpu_clk;
+ struct clk *osc_clk;
+ struct clk *pll_clk;
+ struct clk *hbus_clk;
+ struct clk *cpu_parent = NULL;
+ int cpu_rate = 0;
+ int hbus_rate = 0;
+ int i, pending_irq;
+ u32 reg_clkctrl_clkseq, reg_clkctrl_xtal;
+
+ /*
+ * 1) switch clock domains from PLL to 24MHz
+ * 2) lower voltage (TODO)
+ * 3) switch EMI to 24MHz and turn PLL off (done in sleep.S)
+ */
+
+ /* save portion of SRAM to be used by suspend function. */
+ memcpy(saved_sram, (void *)STMP3XXX_OCRAM_VA_BASE,
+ stmp_standby_alloc_sz);
+
+ /* make sure SRAM copy gets physically written into SDRAM.
+ * SDRAM will be placed into self-refresh during power down
+ */
+ flush_cache_all();
+
+ /* copy suspend function into SRAM */
+ memcpy((void *)STMP3XXX_OCRAM_VA_BASE, (void*)stmp378x_standby,
+ MAX_POWEROFF_CODE_SIZE);
+
+ /* now switch the CPU to ref_xtal */
+ cpu_clk = clk_get(NULL, "cpu");
+ osc_clk = clk_get(NULL, "osc_24M");
+ pll_clk = clk_get(NULL, "pll");
+ hbus_clk = clk_get(NULL, "hclk");
+
+ if (!IS_ERR(cpu_clk) && !IS_ERR(osc_clk)) {
+ cpu_rate = clk_get_rate(cpu_clk);
+ cpu_parent = cpu_clk->parent;
+ hbus_rate = clk_get_rate(hbus_clk);
+ clk_set_parent(cpu_clk, osc_clk);
+ }
+
+ local_irq_disable();
+ local_fiq_disable();
+
+ stmp3xxx_dma_suspend();
+ stmp3xxx_suspend_timer();
+
+ HW_POWER_CTRL_SET(BM_POWER_CTRL_ENIRQ_PSWITCH);
+ HW_ICOLL_INTERRUPTn_SET(IRQ_VDD5V, BM_ICOLL_INTERRUPTn_ENABLE);
+
+ /* clear pending interrupt, if any */
+ for (i = 0; i < PENDING_IRQ_RETRY; i++) {
+ pending_irq = HW_ICOLL_STAT_RD() & 0x7f;
+ if (pending_irq == 0x7f)
+ break;
+ pr_info("irqn = %u\n", pending_irq);
+ /* Tell ICOLL to release IRQ line */
+ HW_ICOLL_VECTOR_WR(0x0);
+ /* ACK current interrupt */
+ HW_ICOLL_LEVELACK_WR(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0);
+ /* Barrier */
+ (void) HW_ICOLL_STAT_RD();
+ }
+
+ reg_clkctrl_clkseq = HW_CLKCTRL_CLKSEQ_RD();
+
+ HW_CLKCTRL_CLKSEQ_SET(BM_CLKCTRL_CLKSEQ_BYPASS_ETM |
+ BM_CLKCTRL_CLKSEQ_BYPASS_SSP |
+ BM_CLKCTRL_CLKSEQ_BYPASS_GPMI |
+ BM_CLKCTRL_CLKSEQ_BYPASS_IR |
+ BM_CLKCTRL_CLKSEQ_BYPASS_PIX|
+ BM_CLKCTRL_CLKSEQ_BYPASS_SAIF);
+
+ reg_clkctrl_xtal = HW_CLKCTRL_XTAL_RD();
+
+ HW_CLKCTRL_XTAL_SET(BM_CLKCTRL_XTAL_FILT_CLK24M_GATE |
+ BM_CLKCTRL_XTAL_PWM_CLK24M_GATE |
+ BM_CLKCTRL_XTAL_DRI_CLK24M_GATE);
+
+ /* do suspend */
+ stmp37xx_cpu_standby_ptr = (void *)STMP3XXX_OCRAM_VA_BASE;
+
+ stmp37xx_cpu_standby_ptr();
+
+ HW_CLKCTRL_CLKSEQ_WR(reg_clkctrl_clkseq);
+ HW_CLKCTRL_XTAL_WR(reg_clkctrl_xtal);
+
+ pr_info("wakeup irq source = %d\n", HW_ICOLL_STAT_RD());
+ saved_sleep_state = 0; /* waking from standby */
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_PSWITCH_IRQ);
+ stmp3xxx_resume_timer();
+ stmp3xxx_dma_resume();
+
+ local_fiq_enable();
+ local_irq_enable();
+
+ if (cpu_parent) {
+ clk_set_parent(cpu_clk, cpu_parent);
+ clk_set_rate(cpu_clk, cpu_rate);
+ clk_set_rate(hbus_clk, hbus_rate);
+ }
+
+ clk_put(hbus_clk);
+ clk_put(pll_clk);
+ clk_put(osc_clk);
+ clk_put(cpu_clk);
+
+ /* restoring portion of SRAM that was used by suspend function */
+ memcpy((void *)STMP3XXX_OCRAM_VA_BASE, saved_sram,
+ stmp_standby_alloc_sz);
+}
+
+static u32 clk_regs[] = {
+ HW_CLKCTRL_PLLCTRL0_ADDR,
+ HW_CLKCTRL_XTAL_ADDR,
+ HW_CLKCTRL_PIX_ADDR,
+ HW_CLKCTRL_SSP_ADDR,
+ HW_CLKCTRL_GPMI_ADDR,
+ HW_CLKCTRL_FRAC_ADDR,
+ HW_CLKCTRL_CLKSEQ_ADDR,
+};
+
+static noinline void do_mem(void)
+{
+ void (*stmp37xx_cpu_suspend_ptr) (u32);
+ struct sleep_data saved_context;
+ int i;
+ struct clk *cpu_clk;
+ struct clk *osc_clk;
+ struct clk *pll_clk;
+ struct clk *hbus_clk;
+ int cpu_rate = 0;
+ int hbus_rate = 0;
+
+ saved_context.fingerprint = SLEEP_DATA_FINGERPRINT;
+
+ saved_context.old_c00 = __raw_readl(0xC0000000);
+ saved_context.old_c04 = __raw_readl(0xC0000004);
+ __raw_writel((u32)&saved_context, (void *)0xC0000000);
+
+ local_irq_disable();
+ local_fiq_disable();
+
+ stmp3xxx_dma_suspend();
+ stmp3xxx_suspend_timer();
+
+ /* clocks */
+ for (i = 0; i < ARRAY_SIZE(clk_regs); i++)
+ saved_context.clks[i] =
+ __raw_readl(clk_regs[i]);
+
+ /* interrupt collector */
+ saved_context.icoll_ctrl = HW_ICOLL_CTRL_RD();
+ if (machine_is_stmp37xx()) {
+#ifdef CONFIG_MACH_STMP37XX
+ for (i = 0; i < 16; i++)
+ saved_context.icoll.prio[i] = HW_ICOLL_PRIORITYn_RD(i);
+#endif
+ } else if (machine_is_stmp378x()) {
+#ifdef CONFIG_MACH_STMP378X
+ for (i = 0; i < 128; i++)
+ saved_context.icoll.intr[i] = HW_ICOLL_INTERRUPTn_RD(i);
+#endif
+ }
+
+ /* save pinmux state */
+ for (i = 0; i < 0x100; i++)
+ saved_context.pinmux[i] =
+ __raw_readl(REGS_PINCTRL_BASE + (i<<4));
+
+ cpu_clk = clk_get(NULL, "cpu");
+ osc_clk = clk_get(NULL, "osc_24M");
+ pll_clk = clk_get(NULL, "pll");
+ hbus_clk = clk_get(NULL, "hclk");
+
+ cpu_rate = clk_get_rate(cpu_clk);
+ hbus_rate = clk_get_rate(hbus_clk);
+
+ /* save portion of SRAM to be used by suspend function. */
+ memcpy(saved_sram, (void *)STMP3XXX_OCRAM_VA_BASE, stmp_s2ram_alloc_sz);
+
+ /* set the PERSISTENT_SLEEP_BIT for bootloader */
+ HW_RTC_PERSISTENT1_SET(1 << 10); /* XXX: temp */
+
+ /*
+ * make sure SRAM copy gets physically written into SDRAM.
+ * SDRAM will be placed into self-refresh during power down
+ */
+ flush_cache_all();
+
+ /*copy suspend function into SRAM */
+ memcpy((void *)STMP3XXX_OCRAM_VA_BASE, stmp37xx_cpu_suspend,
+ stmp_s2ram_alloc_sz);
+
+ /* do suspend */
+ stmp37xx_cpu_suspend_ptr = (void *)STMP3XXX_OCRAM_VA_BASE;
+ stmp37xx_cpu_suspend_ptr(0);
+
+ saved_sleep_state = 1; /* waking from non-standby state */
+
+ /* restoring portion of SRAM that was used by suspend function */
+ memcpy((void *)STMP3XXX_OCRAM_VA_BASE, saved_sram, stmp_s2ram_alloc_sz);
+
+ /* clocks */
+ for (i = 0; i < ARRAY_SIZE(clk_regs); i++)
+ __raw_writel(saved_context.clks[i],
+ clk_regs[i]);
+
+ /* interrupt collector */
+ HW_ICOLL_CTRL_WR(saved_context.icoll_ctrl);
+ if (machine_is_stmp37xx()) {
+#ifdef CONFIG_MACH_STMP37XX
+ for (i = 0; i < 16; i++)
+ HW_ICOLL_PRIORITYn_WR(i, saved_context.icoll.prio[i]);
+#endif
+ } else if (machine_is_stmp378x()) {
+#ifdef CONFIG_MACH_STMP378X
+ for (i = 0; i < 128; i++)
+ HW_ICOLL_INTERRUPTn_WR(i, saved_context.icoll.intr[i]);
+#endif
+ }
+
+ /* restore pinmux state */
+ for (i = 0; i < 0x100; i++)
+ __raw_writel(saved_context.pinmux[i],
+ REGS_PINCTRL_BASE + (i<<4));
+
+ clk_set_parent(cpu_clk, cpu_clk->parent);
+ clk_set_rate(cpu_clk, cpu_rate);
+ clk_set_rate(hbus_clk, hbus_rate);
+
+ __raw_writel(saved_context.old_c00, 0xC0000000);
+ __raw_writel(saved_context.old_c04, 0xC0000004);
+
+ clk_put(hbus_clk);
+ clk_put(pll_clk);
+ clk_put(osc_clk);
+ clk_put(cpu_clk);
+
+ stmp3xxx_resume_timer();
+ stmp3xxx_dma_resume();
+
+ local_fiq_enable();
+ local_irq_enable();
+}
+
+static int stmp37xx_pm_enter(suspend_state_t state)
+{
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ do_standby();
+ break;
+ case PM_SUSPEND_MEM:
+ do_mem();
+ break;
+ }
+ return 0;
+}
+
+static int stmp37xx_pm_valid(suspend_state_t state)
+{
+ return (state == PM_SUSPEND_STANDBY) ||
+ (state == PM_SUSPEND_MEM);
+}
+
+static suspend_state_t saved_state;
+
+static int stmp37xx_pm_begin(suspend_state_t state)
+{
+ saved_state = state;
+ return 0;
+}
+
+static void stmp37xx_pm_end(void)
+{
+ /*XXX: Nothing to do */
+}
+
+suspend_state_t stmp37xx_pm_get_target(void)
+{
+ return saved_state;
+}
+EXPORT_SYMBOL(stmp37xx_pm_get_target);
+
+/**
+ * stmp37xx_pm_get_sleep_state - get sleep state we waking from
+ *
+ * returns boolean: 0 if waking up from standby, 1 otherwise
+ */
+int stmp37xx_pm_sleep_was_deep(void)
+{
+ return saved_sleep_state;
+}
+EXPORT_SYMBOL(stmp37xx_pm_sleep_was_deep);
+
+static struct platform_suspend_ops stmp37xx_suspend_ops = {
+ .enter = stmp37xx_pm_enter,
+ .valid = stmp37xx_pm_valid,
+ .begin = stmp37xx_pm_begin,
+ .end = stmp37xx_pm_end,
+};
+
+void stmp37xx_pm_idle(void)
+{
+ local_irq_disable();
+ local_fiq_disable();
+ if (need_resched()) {
+ local_fiq_enable();
+ local_irq_enable();
+ return;
+ }
+
+ HW_CLKCTRL_CPU_SET(1<<12);
+ __asm__ __volatile__ ("mcr p15, 0, r0, c7, c0, 4");
+
+ local_fiq_enable();
+ local_irq_enable();
+}
+
+static void stmp37xx_pm_power_off(void)
+{
+ HW_POWER_RESET_WR((0x3e77 << 16) | 1);
+}
+
+struct stmp37xx_pswitch_state {
+ int dev_running;
+};
+
+static DECLARE_COMPLETION(suspend_request);
+
+static int suspend_thread_fn(void *data)
+{
+ while (1) {
+ wait_for_completion(&suspend_request);
+ pm_suspend(PM_SUSPEND_STANDBY);
+ }
+ return 0;
+}
+
+static struct stmp37xx_pswitch_state pswitch_state = {
+ .dev_running = 0,
+};
+
+static irqreturn_t pswitch_interrupt(int irq, void *dev)
+{
+ int pin_value, i;
+
+ /* check if irq by pswitch */
+ if (!(HW_POWER_CTRL_RD() & BM_POWER_CTRL_PSWITCH_IRQ))
+ return IRQ_HANDLED;
+ for (i = 0; i < 3000; i++) {
+ pin_value = HW_POWER_STS_RD() &
+ BF_POWER_STS_PSWITCH(0x1);
+ if (pin_value == 0)
+ break;
+ mdelay(1);
+ }
+ if (i < 3000) {
+ pr_info("pswitch goto suspend\n");
+ complete(&suspend_request);
+ } else {
+ pr_info("release pswitch to power down\n");
+ for (i = 0; i < 5000; i++) {
+ pin_value = HW_POWER_STS_RD() &
+ BF_POWER_STS_PSWITCH(0x1);
+ if (pin_value == 0)
+ break;
+ mdelay(1);
+ }
+ pr_info("pswitch power down\n");
+ stmp37xx_pm_power_off();
+ }
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_PSWITCH_IRQ);
+ return IRQ_HANDLED;
+}
+
+static struct irqaction pswitch_irq = {
+ .name = "pswitch",
+ .flags = IRQF_DISABLED | IRQF_SHARED,
+ .handler = pswitch_interrupt,
+ .dev_id = &pswitch_state,
+};
+
+static void init_pswitch(void)
+{
+ kthread_run(suspend_thread_fn, NULL, "pswitch");
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_PSWITCH_IRQ);
+ HW_POWER_CTRL_SET(BM_POWER_CTRL_POLARITY_PSWITCH |
+ BM_POWER_CTRL_ENIRQ_PSWITCH);
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_PSWITCH_IRQ);
+ setup_irq(IRQ_VDD5V, &pswitch_irq);
+}
+
+static int __init stmp37xx_pm_init(void)
+{
+ saved_sram = kmalloc(0x4000, GFP_ATOMIC);
+ if (!saved_sram) {
+ printk(KERN_ERR
+ "PM Suspend: can't allocate memory to save portion of SRAM\n");
+ return -ENOMEM;
+ }
+
+ pm_power_off = stmp37xx_pm_power_off;
+ pm_idle = stmp37xx_pm_idle;
+ suspend_set_ops(&stmp37xx_suspend_ops);
+ init_pswitch();
+ return 0;
+}
+
+late_initcall(stmp37xx_pm_init);
diff --git a/arch/arm/mach-stmp3xxx/power-test.c b/arch/arm/mach-stmp3xxx/power-test.c
new file mode 100644
index 000000000000..70e52fc92904
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/power-test.c
@@ -0,0 +1,212 @@
+/*
+ * Power consumption test module
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+static struct regulator *reg;
+static struct regulator *freg;
+
+static struct timer_list pt_timer;
+static int timer_delay = 5*60*1000; /* 5min */
+static DEFINE_MUTEX(run_mutex);
+
+
+#define REG_GET() do {\
+ if (!reg) {\
+ reg = regulator_get(NULL, "power-test-1");\
+ if (!reg || IS_ERR(reg)) {\
+ reg = NULL ; return -ENODEV;\
+ } \
+ } \
+} while (0);
+
+static void timer_func(unsigned long data)
+{
+ regulator_set_current_limit(reg, 0, 0);
+ mutex_unlock(&run_mutex);
+}
+
+static ssize_t pt_mode_set(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ REG_GET();
+ if (buf[0] == 'f')
+ regulator_set_mode(reg, REGULATOR_MODE_FAST);
+ else
+ regulator_set_mode(reg, REGULATOR_MODE_NORMAL);
+ return size;
+}
+
+static ssize_t pt_mode_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ REG_GET();
+ if (regulator_get_mode(reg) == REGULATOR_MODE_FAST)
+ return snprintf(buf, 5, "fast\n");
+ else
+ return snprintf(buf, 7, "normal\n");
+}
+
+static ssize_t pt_val_fset(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int i, ret;
+ ret = sscanf(buf, "%u", &i);
+ if (ret != 1)
+ return -EINVAL;
+
+ if (!freg) {
+ freg = regulator_get(NULL, "stmp3xxx-bl-1");
+ if (!freg || IS_ERR(freg)) {
+ freg = NULL ; return -ENODEV;
+ }
+ }
+ regulator_set_mode(freg, REGULATOR_MODE_NORMAL);
+
+ if (!regulator_set_current_limit(freg, i, i))
+ printk(KERN_ERR "got backlight reg\n");
+ else
+ printk(KERN_ERR "failed to get backlight reg");
+
+ return size;
+}
+
+
+static ssize_t pt_val_set(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int i, ret;
+ REG_GET();
+
+ ret = sscanf(buf, "%u", &i);
+ if (ret != 1)
+ return -EINVAL;
+
+ mutex_lock(&run_mutex);
+ if (!regulator_set_current_limit(reg, i, i)) {
+ mod_timer(&pt_timer,
+ jiffies + msecs_to_jiffies(timer_delay));
+ return size;
+ } else {
+ mutex_unlock(&run_mutex);
+ return -EPERM;
+ }
+
+}
+
+static ssize_t pt_val_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ REG_GET();
+ return sprintf(buf, "%d\n", regulator_get_current_limit(reg));
+}
+
+static ssize_t pt_timeout_set(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int i, ret;
+ REG_GET();
+
+ ret = sscanf(buf, "%u", &i);
+ if (ret != 1)
+ return -EINVAL;
+
+ mutex_lock(&run_mutex);
+ timer_delay = 1000*i ;
+ mutex_unlock(&run_mutex);
+
+ return size;
+}
+
+static ssize_t pt_timeout_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ REG_GET();
+ return sprintf(buf, "%d\n", timer_delay);
+}
+
+static DEVICE_ATTR(mode, 0644, pt_mode_show, pt_mode_set);
+static DEVICE_ATTR(val, 0644, pt_val_show, pt_val_set);
+static DEVICE_ATTR(fval, 0644, pt_val_show, pt_val_fset);
+static DEVICE_ATTR(timeout, 0644, pt_timeout_show, pt_timeout_set);
+
+static int stmp3xxx_power_test_remove(struct platform_device *pdev)
+{
+ if (reg)
+ regulator_put(reg);
+
+ device_remove_file(&pdev->dev, &dev_attr_mode);
+ device_remove_file(&pdev->dev, &dev_attr_val);
+ device_remove_file(&pdev->dev, &dev_attr_timeout);
+ device_remove_file(&pdev->dev, &dev_attr_fval);
+ return 0;
+}
+
+static int stmp3xxx_power_test_probe(struct platform_device *pdev)
+{
+ init_timer(&pt_timer);
+ pt_timer.data = 0;
+ pt_timer.function = timer_func;
+
+ device_create_file(&pdev->dev, &dev_attr_mode);
+ device_create_file(&pdev->dev, &dev_attr_val);
+ device_create_file(&pdev->dev, &dev_attr_fval);
+ device_create_file(&pdev->dev, &dev_attr_timeout);
+ return 0;
+}
+
+static struct platform_driver stmp3xxx_power_test_driver = {
+ .probe = stmp3xxx_power_test_probe,
+ .remove = stmp3xxx_power_test_remove,
+ .driver = {
+ .name = "stmp3xxx-power-test",
+ .owner = THIS_MODULE,
+ },
+};
+
+struct platform_device stmp3xxx_pt = {
+ .name = "stmp3xxx-power-test",
+ .id = -1,
+};
+
+static int __init stmp3xxx_power_test_init(void)
+{
+
+ platform_device_register(&stmp3xxx_pt);
+ return platform_driver_register(&stmp3xxx_power_test_driver);
+}
+
+static void __exit stmp3xxx_power_test_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_power_test_driver);
+ platform_device_unregister(&stmp3xxx_pt);
+}
+
+MODULE_AUTHOR("<sed@embeddedalley.com>");
+MODULE_DESCRIPTION("Power test driver");
+MODULE_LICENSE("GPL");
+
+module_init(stmp3xxx_power_test_init);
+module_exit(stmp3xxx_power_test_exit);
diff --git a/arch/arm/mach-stmp3xxx/power.c b/arch/arm/mach-stmp3xxx/power.c
new file mode 100644
index 000000000000..416e567781c3
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/power.c
@@ -0,0 +1,531 @@
+/*
+ * Freescale STMP378X voltage regulator low-level driver
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/* #define DEBUG */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <mach/power.h>
+#include <mach/regulator.h>
+#include <mach/regs-power.h>
+#include <mach/stmp3xxx.h>
+
+static int get_voltage(struct stmp3xxx_regulator *sreg)
+{
+ struct stmp3xxx_platform_regulator_data *rdata = sreg->rdata;
+ u32 val = __raw_readl(rdata->control_reg) & 0x1f;
+ int uv = rdata->min_voltage + val *
+ (rdata->max_voltage - rdata->min_voltage) / 0x1f;
+ return uv;
+}
+
+static int get_bo_voltage(struct stmp3xxx_regulator *sreg)
+{
+ int uv;
+ int offs;
+
+ if (!sreg->parent)
+ return -EINVAL;
+
+ uv = get_voltage(sreg->parent);
+ offs = (__raw_readl(sreg->parent->rdata->control_reg) & ~0x700) >> 8;
+ return uv - 25000*offs;
+}
+
+static int set_voltage(struct stmp3xxx_regulator *sreg, int uv)
+{
+ u32 val, reg, i;
+
+ pr_debug("%s: uv %d, min %d, max %d\n", __func__,
+ uv, sreg->rdata->min_voltage, sreg->rdata->max_voltage);
+
+ if (uv < sreg->rdata->min_voltage || uv > sreg->rdata->max_voltage)
+ return -EINVAL;
+
+ val = (uv - sreg->rdata->min_voltage) * 0x1f /
+ (sreg->rdata->max_voltage - sreg->rdata->min_voltage);
+ reg = (__raw_readl(sreg->rdata->control_reg) & ~0x1f);
+ pr_debug("%s: calculated val %d\n", __func__, val);
+ __raw_writel(val | reg, sreg->rdata->control_reg);
+ for (i = 20; i; i--) {
+ if (HW_POWER_STS_RD() & BM_POWER_STS_DC_OK)
+ break;
+ udelay(1);
+ }
+
+ if (i)
+ goto out;
+
+ __raw_writel(val | reg, sreg->rdata->control_reg);
+ for (i = 40000; i; i--) {
+ if (HW_POWER_STS_RD() & BM_POWER_STS_DC_OK)
+ break;
+ udelay(1);
+ }
+
+ if (i)
+ goto out;
+
+ for (i = 40000; i; i--) {
+ if (HW_POWER_STS_RD() & BM_POWER_STS_DC_OK)
+ break;
+ udelay(1);
+ }
+
+out:
+ return !i;
+}
+
+static int set_bo_voltage(struct stmp3xxx_regulator *sreg, int bo_uv)
+{
+ int uv;
+ int offs;
+ u32 reg;
+ int i;
+
+ if (!sreg->parent)
+ return -EINVAL;
+
+ uv = get_voltage(sreg->parent);
+ offs = (uv - bo_uv) / 25000;
+ if (offs < 0 || offs > 7)
+ return -EINVAL;
+
+ reg = (__raw_readl(sreg->parent->rdata->control_reg) & ~0x700);
+ pr_debug("%s: calculated offs %d\n", __func__, offs);
+ __raw_writel((offs << 8) | reg, sreg->parent->rdata->control_reg);
+
+ for (i = 10000; i; i--) {
+ if (HW_POWER_STS_RD() & BM_POWER_STS_DC_OK)
+ break;
+ udelay(1);
+ }
+
+ if (i)
+ goto out;
+
+ for (i = 10000; i; i--) {
+ if (HW_POWER_STS_RD() & BM_POWER_STS_DC_OK)
+ break;
+ udelay(1);
+ }
+
+out:
+ return !i;
+}
+
+static int enable(struct stmp3xxx_regulator *sreg)
+{
+ /* XXX: TODO */
+ return 0;
+}
+
+static int disable(struct stmp3xxx_regulator *sreg)
+{
+ /* XXX: TODO */
+ return 0;
+}
+
+static int is_enabled(struct stmp3xxx_regulator *sreg)
+{
+ /* XXX: TODO */
+ return 1;
+}
+
+static int set_mode(struct stmp3xxx_regulator *sreg, int mode)
+{
+ int ret = 0;
+ u32 val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = __raw_readl(sreg->rdata->control_reg);
+ __raw_writel(val | (1 << 17), sreg->rdata->control_reg);
+ break;
+
+ case REGULATOR_MODE_NORMAL:
+ val = __raw_readl(sreg->rdata->control_reg);
+ __raw_writel(val & ~(1<<17), sreg->rdata->control_reg);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int get_mode(struct stmp3xxx_regulator *sreg)
+{
+ u32 val = __raw_readl(sreg->rdata->control_reg) & (1 << 17);
+
+ return val ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
+}
+
+static struct stmp3xxx_platform_regulator_data vddd_data = {
+ .name = "vddd",
+ .set_voltage = set_voltage,
+ .get_voltage = get_voltage,
+ .enable = enable,
+ .disable = disable,
+ .is_enabled = is_enabled,
+ .set_mode = set_mode,
+ .get_mode = get_mode,
+ .control_reg = HW_POWER_VDDDCTRL_ADDR,
+ .min_voltage = 800000,
+ .max_voltage = 1575000,
+};
+
+static struct stmp3xxx_platform_regulator_data vdddbo_data = {
+ .name = "vddd_bo",
+ .parent_name = "vddd",
+ .set_voltage = set_bo_voltage,
+ .get_voltage = get_bo_voltage,
+ .enable = enable,
+ .disable = disable,
+ .is_enabled = is_enabled,
+ .set_mode = set_mode,
+ .get_mode = get_mode,
+ .min_voltage = 800000,
+ .max_voltage = 1575000,
+};
+
+static struct stmp3xxx_platform_regulator_data vdda_data = {
+ .name = "vdda",
+ .set_voltage = set_voltage,
+ .get_voltage = get_voltage,
+ .enable = enable,
+ .disable = disable,
+ .is_enabled = is_enabled,
+ .set_mode = set_mode,
+ .get_mode = get_mode,
+ .control_reg = HW_POWER_VDDACTRL_ADDR,
+ .min_voltage = 1500000,
+ .max_voltage = 2275000,
+};
+
+static struct stmp3xxx_platform_regulator_data vddio_data = {
+ .name = "vddio",
+ .set_voltage = set_voltage,
+ .get_voltage = get_voltage,
+ .enable = enable,
+ .disable = disable,
+ .is_enabled = is_enabled,
+ .set_mode = set_mode,
+ .get_mode = get_mode,
+ .control_reg = HW_POWER_VDDIOCTRL_ADDR,
+ .min_voltage = 2800000,
+ .max_voltage = 3575000,
+};
+
+static struct regulator_init_data vddd_init = {
+ .constraints = {
+ .name = "vddd",
+ .min_uV = 800000,
+ .max_uV = 1575000,
+ .valid_modes_mask = REGULATOR_MODE_FAST |
+ REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_MODE,
+ .input_uV = 5000000,
+ }
+};
+
+static struct regulator_init_data vdddbo_init = {
+ .constraints = {
+ .name = "vdddbo",
+ .min_uV = 800000,
+ .max_uV = 1575000,
+ .valid_modes_mask = REGULATOR_MODE_FAST |
+ REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_MODE,
+ .input_uV = 5000000,
+ }
+};
+
+
+static struct regulator_init_data vdda_init = {
+ .constraints = {
+ .name = "vdda",
+ .min_uV = 1500000,
+ .max_uV = 2275000,
+ .valid_modes_mask = REGULATOR_MODE_FAST |
+ REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_MODE,
+ .input_uV = 5000000,
+ }
+};
+
+
+static struct regulator_init_data vddio_init = {
+ .constraints = {
+ .name = "vddio",
+ .min_uV = 2800000,
+ .max_uV = 3575000,
+ .valid_modes_mask = REGULATOR_MODE_FAST |
+ REGULATOR_MODE_NORMAL,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_MODE,
+ .input_uV = 5000000,
+ }
+};
+
+/* now the current regulators */
+/* Restriction: .... no set_current call on root regulator */
+static int main_add_current(struct stmp3xxx_regulator *sreg,
+ int uA)
+{
+
+ pr_debug("%s: enter reg %s, uA=%d\n",
+ __func__, sreg->regulator.name, uA);
+ if (uA > 0 && (sreg->cur_current + uA > sreg->rdata->max_current))
+ return -EINVAL;
+ else
+ sreg->cur_current += uA;
+ return 0;
+}
+
+static int cur_reg_set_current(struct stmp3xxx_regulator *sreg, int uA)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ pr_debug("%s: enter reg %s, uA=%d\n",
+ __func__, sreg->regulator.name, uA);
+
+ if (sreg->parent) {
+ spin_lock_irqsave(&sreg->parent->lock, flags);
+ ret = main_add_current(sreg->parent, uA - sreg->cur_current);
+ spin_unlock_irqrestore(&sreg->parent->lock, flags);
+ }
+
+
+ if ((!ret) || (!sreg->parent))
+ goto out;
+
+ if (sreg->mode == REGULATOR_MODE_FAST)
+ return ret;
+
+ while (ret) {
+ wait_event(sreg->parent->wait_q ,
+ (uA - sreg->cur_current <
+ sreg->parent->rdata->max_current -
+ sreg->parent->cur_current));
+ spin_lock_irqsave(&sreg->parent->lock, flags);
+ ret = main_add_current(sreg->parent, uA - sreg->cur_current);
+ spin_unlock_irqrestore(&sreg->parent->lock, flags);
+ }
+out:
+ if (sreg->parent && (uA - sreg->cur_current < 0))
+ wake_up_all(&sreg->parent->wait_q);
+ sreg->cur_current = uA;
+ return 0;
+
+}
+
+static int cur_reg_get_current(struct stmp3xxx_regulator *sreg)
+{
+ return sreg->cur_current;
+}
+
+static int enable_cur_reg(struct stmp3xxx_regulator *sreg)
+{
+ /* XXX: TODO */
+ return 0;
+}
+
+static int disable_cur_reg(struct stmp3xxx_regulator *sreg)
+{
+ /* XXX: TODO */
+ return 0;
+}
+
+static int cur_reg_is_enabled(struct stmp3xxx_regulator *sreg)
+{
+ /* XXX: TODO */
+ return 1;
+}
+
+static int cur_reg_set_mode(struct stmp3xxx_regulator *sreg, int mode)
+{
+ int ret = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ case REGULATOR_MODE_FAST:
+ sreg->mode = mode;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cur_reg_get_mode(struct stmp3xxx_regulator *sreg)
+{
+ return sreg->mode;
+}
+
+static struct stmp3xxx_platform_regulator_data overall_cur_data = {
+ .name = "overall_current",
+ .set_current = cur_reg_set_current,
+ .get_current = cur_reg_get_current,
+ .enable = enable_cur_reg,
+ .disable = disable_cur_reg,
+ .is_enabled = cur_reg_is_enabled,
+ .set_mode = cur_reg_set_mode,
+ .get_mode = cur_reg_get_mode,
+ .max_current = 0x7fffffff,
+};
+
+static struct regulator_init_data overall_cur_init = {
+ .constraints = {
+ .name = "overall_current",
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_FAST,
+ .valid_ops_mask = REGULATOR_CHANGE_CURRENT |
+ REGULATOR_CHANGE_MODE,
+ .max_uA = 0x7fffffff,
+ .min_uA = 0x0,
+ }
+};
+
+static struct stmp3xxx_platform_regulator_data sibling_cur_data = {
+ .parent_name = "overall_current",
+ .set_current = cur_reg_set_current,
+ .get_current = cur_reg_get_current,
+ .enable = enable_cur_reg,
+ .disable = disable_cur_reg,
+ .is_enabled = cur_reg_is_enabled,
+ .set_mode = cur_reg_set_mode,
+ .get_mode = cur_reg_get_mode,
+};
+
+static struct platform_device *devices[] = {
+ &stmp3xxx_keyboard,
+ &stmp3xxx_touchscreen,
+ &stmp3xxx_appuart,
+ &stmp3xxx_dbguart,
+ &stmp3xxx_watchdog,
+ &stmp3xxx_rtc,
+ &stmp3xxx_framebuffer,
+ &stmp3xxx_backlight,
+ &stmp3xxx_rotdec,
+ &stmp378x_i2c,
+ &stmp3xxx_persistent,
+ &stmp3xxx_dcp_bootstream,
+ &stmp3xxx_dcp,
+ &stmp3xxx_mtest,
+ &stmp3xxx_battery,
+ &stmp3xxx_pxp,
+};
+
+static int sibling_current_devices_num;
+
+int stmp3xxx_platform_add_regulator(const char *name, int count)
+{
+ int i;
+ pr_debug("%s: name %s, count %d\n", __func__, name, count);
+ for (i = sibling_current_devices_num;
+ i < sibling_current_devices_num + count;
+ i++) {
+ struct regulator_init_data *sibling_init =
+ kzalloc(sizeof(struct regulator_init_data),
+ GFP_KERNEL);
+ struct stmp3xxx_regulator *curr_reg =
+ kzalloc(sizeof(struct stmp3xxx_regulator),
+ GFP_KERNEL);
+ struct stmp3xxx_platform_regulator_data *d =
+ kzalloc(sizeof(struct stmp3xxx_platform_regulator_data),
+ GFP_KERNEL);
+ if (!d || !curr_reg || !sibling_init)
+ return -ENOMEM;
+
+ sibling_init->constraints.valid_modes_mask =
+ REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+ sibling_init->constraints.valid_ops_mask =
+ REGULATOR_CHANGE_CURRENT | REGULATOR_CHANGE_MODE;
+ sibling_init->constraints.max_uA = 0x7fffffff;
+ sibling_init->constraints.min_uA = 0x0;
+
+ memcpy(d, &sibling_cur_data, sizeof(sibling_cur_data));
+ d->parent_name = kstrdup(sibling_cur_data.parent_name,
+ GFP_KERNEL);
+ snprintf(d->name, 80, "%s-%d",
+ name, i - sibling_current_devices_num + 1);
+ sibling_init->constraints.name = kstrdup(d->name, GFP_KERNEL);
+ curr_reg->rdata = d;
+ stmp3xxx_register_regulator(curr_reg, 101 + i, sibling_init);
+ }
+ sibling_current_devices_num += count;
+ return 0;
+}
+
+static struct stmp3xxx_regulator vddd_reg = {
+ .rdata = &vddd_data,
+};
+
+static struct stmp3xxx_regulator vdda_reg = {
+ .rdata = &vdda_data,
+};
+
+static struct stmp3xxx_regulator vddio_reg = {
+ .rdata = &vddio_data,
+};
+
+static struct stmp3xxx_regulator vdddbo_reg = {
+ .rdata = &vdddbo_data,
+};
+
+static struct stmp3xxx_regulator overall_cur_reg = {
+ .rdata = &overall_cur_data,
+};
+
+
+static int __init regulators_init(void)
+{
+ int i;
+ int retval = 0;
+ u32 vddio = HW_POWER_VDDIOCTRL_RD() & ~0x1f;
+ pr_debug("regulators_init \n");
+ HW_POWER_VDDIOCTRL_WR(vddio | 0x14);
+ vdddbo_reg.parent = &vddd_reg;
+ stmp3xxx_register_regulator(&vddd_reg, STMP3XXX_VDDD, &vddd_init);
+ stmp3xxx_register_regulator(&vdddbo_reg, STMP3XXX_VDDDBO, &vdddbo_init);
+ stmp3xxx_register_regulator(&vdda_reg, STMP3XXX_VDDA, &vdda_init);
+ stmp3xxx_register_regulator(&vddio_reg, STMP3XXX_VDDIO, &vddio_init);
+ stmp3xxx_register_regulator(&overall_cur_reg,
+ STMP3XXX_OVERALL_CUR, &overall_cur_init);
+
+ for (i = 0; i < ARRAY_SIZE(devices); i++) {
+ retval = stmp3xxx_platform_add_regulator(devices[i]->name, 1);
+ if (retval)
+ return retval;
+ }
+ stmp3xxx_platform_add_regulator("mmc_ssp", 2);
+ stmp3xxx_platform_add_regulator("charger", 1);
+ stmp3xxx_platform_add_regulator("power-test", 1);
+ stmp3xxx_platform_add_regulator("cpufreq", 1);
+ return 0;
+}
+postcore_initcall(regulators_init);
diff --git a/arch/arm/mach-stmp3xxx/rotdec.c b/arch/arm/mach-stmp3xxx/rotdec.c
new file mode 100644
index 000000000000..d390102c1559
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/rotdec.c
@@ -0,0 +1,39 @@
+/*
+ * Freescale STMP378X Rotary Encoder module pin multiplexing
+ *
+ * Author: Drew Benedetti <drewb@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/pins.h>
+#include "pinmux.h"
+
+#define TITLE "stmp3xxx-rotdec"
+
+int rotdec_pinmux_request(void)
+{
+ return stmp3xxx_request_pin_group(&rotdec_pins, TITLE);
+}
+EXPORT_SYMBOL_GPL(rotdec_pinmux_request);
+
+void rotdec_pinmux_free(void)
+{
+ stmp3xxx_release_pin_group(&spdif_pins, TITLE);
+}
+EXPORT_SYMBOL_GPL(rotdec_pinmux_free);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Drew Benedetti <drewb@embeddedalley.com>");
diff --git a/arch/arm/mach-stmp3xxx/sleep.S b/arch/arm/mach-stmp3xxx/sleep.S
new file mode 100644
index 000000000000..c4bb4a331465
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/sleep.S
@@ -0,0 +1,540 @@
+/*
+ * Freescale STMP37XX/STMP378X low level sleep states support
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <mach/hardware.h>
+#include <asm/pgtable-hwdef.h>
+#include <mach/regs-power.h>
+#include <mach/regs-clkctrl.h>
+#include "sleep.h"
+
+#define HW_DRAM_CTL06 IO_ADDRESS(0x800E0018)
+#define HW_DRAM_CTL08 IO_ADDRESS(0x800E0020)
+#define HW_EMI_STAT IO_ADDRESS(0x80020010)
+#define HW_RTC_PERSISTENT0 \
+ IO_ADDRESS(0x8005C060)
+
+#define PHYS_RAM_START 0x40000000
+
+.global cpu_arm926_switch_mm
+
+ .text
+
+.align 8
+ENTRY(stmp37xx_cpu_standby)
+ @ save registers on stack
+ stmfd sp!, {r0 - r9, lr}
+
+ adr r9, __stmp_temp_stack
+
+ @ clean cache
+ ldr r1, __stmp_flush_cache_addr
+ mov lr, pc
+ mov pc, r1
+
+ @ put DRAM into self refresh
+ mov r0, #(HW_DRAM_CTL08 & 0x000000FF)
+ orr r0, r0, #(HW_DRAM_CTL08 & 0x0000FF00)
+ orr r0, r0, #(HW_DRAM_CTL08 & 0x00FF0000)
+ orr r0, r0, #(HW_DRAM_CTL08 & 0xFF000000)
+ ldr r1, [r0]
+ orr r1, r1, #(1 << 8)
+ str r1, [r0]
+ @ wait for it to actually happen
+ mov r0, #(HW_EMI_STAT & 0x000000FF)
+ orr r0, r0, #(HW_EMI_STAT & 0x0000FF00)
+ orr r0, r0, #(HW_EMI_STAT & 0x00FF0000)
+ orr r0, r0, #(HW_EMI_STAT & 0xFF000000)
+1: ldr r1, [r0]
+ teq r1, #(1 << 1)
+ beq 1b
+ nop
+ nop
+ nop
+ nop
+ nop
+
+#ifdef CONFIG_STMP378X_RAM_FREQ_SCALING
+ @ RAM to clk from xtal
+ mov lr, pc
+ b stmp3xxx_ram_save_timings
+ mov lr, pc
+ b stmp3xxx_ram_24M_set_timings
+
+ mov r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0xFF000000)
+ ldr r4, [r0]
+ mov r1, #(1<<6)
+ str r1, [r0, #4]
+1: ldr r1, [r0]
+ tst r1, #BM_CLKCTRL_EMI_BUSY_REF_XTAL
+ bne 1b
+
+ @ save RAM divisors
+ mov r0, #(HW_CLKCTRL_FRAC_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_FRAC_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_FRAC_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_FRAC_ADDR & 0xFF000000)
+ ldr r8, [r0]
+ and r8, r8, #(0x3F << 8)
+ lsr r8, r8, #8
+ mov r0, #(HW_CLKCTRL_EMI_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0xFF000000)
+ ldr r7, [r0]
+ and r7, r7, #0x3F
+
+ @ shut the PLL down
+ mov r0, #(HW_CLKCTRL_PLLCTRL0_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_PLLCTRL0_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_PLLCTRL0_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_PLLCTRL0_ADDR & 0xFF000000)
+ mov r1, #(1<<16)
+ str r1, [r0, #0x08] @ clear
+
+ @ set vddd to minimum
+ mov r0, #(HW_POWER_VDDDCTRL_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_POWER_VDDDCTRL_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_POWER_VDDDCTRL_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_POWER_VDDDCTRL_ADDR & 0xFF000000)
+ ldr r6, [r0]
+ bic r1, r6, #0xFF
+ bic r1, r1, #0x30
+ orr r1, r1, #0xa
+ str r1, [r0]
+ /* now wait 1000 us = 24000 cycles */
+ mov r0, #24 << 10
+3: sub r0, r0, #1
+ cmp r0, #0
+ bne 3b
+ nop
+#endif
+
+ @ do enter standby
+ mov r0, #(HW_CLKCTRL_CPU_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_CPU_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_CPU_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_CPU_ADDR & 0xFF000000)
+ mov r1, #(1<<12)
+ str r1, [r0, #4]
+ mov r2, #0
+ mcr p15, 0, r2, c7, c0, 4
+ nop
+
+ @ sleeping now...
+
+ @ remove INTERRUPT_WAIT bit
+ str r1, [r0, #8]
+ nop
+ nop
+ nop
+
+#ifdef CONFIG_STMP378X_RAM_FREQ_SCALING
+ @ restore vddd
+ mov r0, #(HW_POWER_VDDDCTRL_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_POWER_VDDDCTRL_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_POWER_VDDDCTRL_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_POWER_VDDDCTRL_ADDR & 0xFF000000)
+ ldr r1, [r0]
+ str r6, [r0]
+ /* now wait 1000 us = 24000 cycles */
+ mov r0, #24 << 10
+12: sub r0, r0, #1
+ cmp r0, #0
+ bne 12b
+ nop
+
+ @ put the PLL back up
+ mov r0, #(HW_CLKCTRL_PLLCTRL0_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_PLLCTRL0_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_PLLCTRL0_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_PLLCTRL0_ADDR & 0xFF000000)
+ mov r1, #(1<<16)
+ str r1, [r0, #0x04] @ set
+ /* now wait 10 us = 240 cycles */
+ mov r0, #240
+11: sub r0, r0, #1
+ cmp r0, #0
+ bne 11b
+ nop
+
+ @ set divisors and switch EMI back to PLL
+ mov lr, pc
+ b stmp3xxx_ram_restore_timings
+ mov lr, pc
+ b __stmp_emi_set_values
+
+ mov r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_CLKSEQ_ADDR & 0xFF000000)
+ mov r1, #(1<<6)
+ str r1, [r0, #8]
+
+ mov r0, #(HW_CLKCTRL_EMI_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_CLKCTRL_EMI_ADDR & 0xFF000000)
+ ldr r1, [r0]
+ bic r1, #BM_CLKCTRL_EMI_DCC_RESYNC_ENABLE
+ str r1, [r0]
+#endif
+
+ @ restore normal DRAM mode
+ mov r0, #(HW_DRAM_CTL08 & 0x000000FF)
+ orr r0, r0, #(HW_DRAM_CTL08 & 0x0000FF00)
+ orr r0, r0, #(HW_DRAM_CTL08 & 0x00FF0000)
+ orr r0, r0, #(HW_DRAM_CTL08 & 0xFF000000)
+ ldr r1, [r0]
+ bic r1, r1, #(1 << 8)
+ str r1, [r0]
+ @ wait for it to actually happen
+ mov r0, #(HW_EMI_STAT & 0x000000FF)
+ orr r0, r0, #(HW_EMI_STAT & 0x0000FF00)
+ orr r0, r0, #(HW_EMI_STAT & 0x00FF0000)
+ orr r0, r0, #(HW_EMI_STAT & 0xFF000000)
+102: ldr r1, [r0]
+ tst r1, #(1 << 1)
+ bne 102b
+
+ nop
+ nop
+ nop
+
+ @ restore regs and return
+ ldmfd sp!, {r0 - r9, pc}
+
+ .space 0x100
+__stmp_temp_stack:
+ .word 0
+
+#ifdef CONFIG_STMP378X_RAM_FREQ_SCALING
+#include "emi.inc"
+#endif
+
+__stmp_flush_cache_addr:
+ .word arm926_flush_kern_cache_all
+
+ENTRY(stmp_standby_alloc_sz)
+ .word . - stmp37xx_cpu_standby
+
+ENTRY(stmp37xx_cpu_suspend)
+ @ save registers on stack
+ stmfd sp!, {r1 - r12, lr}
+
+ @ save context
+ mov r0, #0xd3 @ SVC, Interrupts disabled
+ msr cpsr, r0
+ mov r1, #0xC0000000
+ ldr r1, [r1]
+ mrc p15, 0, r0, c1, c0, 0
+ str r0, [r1, #MMUCTL_OFFS]
+ mrc p15, 0, r0, c15, c1, 0
+ str r0, [r1, #MMUCPACCESS_OFS]
+ mrc p15, 0, r0, c2, c0, 0
+ str r0, [r1, #MMUTTB_OFFS]
+ mrc p15, 0, r0, c3, c0, 0
+ str r0, [r1, #MMUDOMAIN_OFFS]
+ mrc p15, 0, r0, c13, c0, 0
+ str r0, [r1, #MMUPID_OFFS]
+
+ str sp, [r1, #SVC_SP_OFFS]
+ mrs r0, spsr
+ str r0, [r1, #SVC_SPSR_OFFS]
+
+ add r2, r1, #FIQ_SPSR_OFFS
+ mov r0, #0xd1 @ FIQ, Interrupts disabled
+ msr cpsr, r0
+ mrs r3, spsr
+ stmia r2!, {r3, r8-r12, sp, lr}
+
+ add r2, r1, #ABT_SPSR_OFFS
+ mov r0, #0xd7 @ ABT, Interrupts disabled
+ msr cpsr, r0
+ mrs r3, spsr
+ stmia r2!, {r3, sp, lr}
+
+ add r2, r1, #IRQ_SPSR_OFFS
+ mov r0, #0xd2 @ IRQ, Interrupts disabled
+ msr cpsr, r0
+ mrs r3, spsr
+ stmia r2!, {r3, sp, lr}
+
+ add r2, r1, #UND_SPSR_OFFS
+ mov r0, #0xdb @ UND, Interrupts disabled
+ msr cpsr, r0
+ mrs r3, spsr
+ stmia r2!, {r3, sp, lr}
+
+ add r2, r1, #SYS_SP_OFFS
+ mov r0, #0xdf @ SYS, Interrupts disabled
+ msr cpsr, r0
+ stmia r2!, {sp, lr}
+
+ add r2, r1, #SVC_R8_OFFS
+ mov r0, #0xd3 @ Back to SVC, Interrupts disabled
+ msr cpsr, r0
+
+ @ save entry point
+ sub r1, r1, #(0xC0000000 - PHYS_RAM_START)
+ mov r0, #0xC0000000
+ str r1, [r0]
+ ldr r1, __stmp_resume_point
+ sub r1, r1, #(0xC0000000 - PHYS_RAM_START)
+ str r1, [r0, #4]
+ mov r0, #0
+
+ @ clean cache
+ ldr r1, __stmp_flush_cache_addr2
+ mov lr, pc
+ mov pc, r1
+
+ @ enable internal xtal
+ mov r2, #(HW_POWER_MINPWR_ADDR & 0x000000FF)
+ orr r2, r2, #(HW_POWER_MINPWR_ADDR & 0x0000FF00)
+ orr r2, r2, #(HW_POWER_MINPWR_ADDR & 0x00FF0000)
+ orr r2, r2, #(HW_POWER_MINPWR_ADDR & 0xFF000000)
+ ldr r1, [r2]
+ orr r1, r1, #(1<<9)
+ str r1, [r2]
+ orr r1, r1, #(1<<8)
+ str r1, [r2]
+
+ @ enable RTC/RAM clocks
+ mov r0, #(HW_RTC_PERSISTENT0 & 0x000000FF)
+ orr r0, r0, #(HW_RTC_PERSISTENT0 & 0x0000FF00)
+ orr r0, r0, #(HW_RTC_PERSISTENT0 & 0x00FF0000)
+ orr r0, r0, #(HW_RTC_PERSISTENT0 & 0xFF000000)
+ mov r1, #((1<<4)|(1<<5)|1)
+ str r1, [r0, #4]
+
+ @ put DRAM into self refresh
+ mov r0, #(HW_DRAM_CTL08 & 0x000000FF)
+ orr r0, r0, #(HW_DRAM_CTL08 & 0x0000FF00)
+ orr r0, r0, #(HW_DRAM_CTL08 & 0x00FF0000)
+ orr r0, r0, #(HW_DRAM_CTL08 & 0xFF000000)
+ ldr r1, [r0]
+ orr r1, r1, #(1 << 8)
+ str r1, [r0]
+ @ wait for it to actually happen
+ mov r0, #(HW_EMI_STAT & 0x000000FF)
+ orr r0, r0, #(HW_EMI_STAT & 0x0000FF00)
+ orr r0, r0, #(HW_EMI_STAT & 0x00FF0000)
+ orr r0, r0, #(HW_EMI_STAT & 0xFF000000)
+1: ldr r1, [r0]
+ teq r1, #(1 << 1)
+ beq 1b
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ @ power off RAM
+ mov r0, #(HW_DRAM_CTL06 & 0x000000FF)
+ orr r0, r0, #(HW_DRAM_CTL06 & 0x0000FF00)
+ orr r0, r0, #(HW_DRAM_CTL06 & 0x00FF0000)
+ orr r0, r0, #(HW_DRAM_CTL06 & 0xFF000000)
+ ldr r1, [r0]
+ orr r1, r1, #(1<<24)
+ str r1, [r0]
+ nop
+ nop
+ nop
+ nop
+
+ @ do enter sleep
+ mov r0, #(HW_POWER_RESET_ADDR & 0x000000FF)
+ orr r0, r0, #(HW_POWER_RESET_ADDR & 0x0000FF00)
+ orr r0, r0, #(HW_POWER_RESET_ADDR & 0x00FF0000)
+ orr r0, r0, #(HW_POWER_RESET_ADDR & 0xFF000000)
+ mov r1, #0xFF000000
+ orr r1, r1, #0x00FF0000
+ str r1, [r0, #8]
+ mov r1, #0x3E000000
+ orr r1, r1, #0x00770000
+ str r1, [r0, #4]
+ mov r1, #2
+ str r1, [r0, #8]
+ mov r1, #1
+ str r1, [r0, #4]
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ @ sleeping now...
+
+__restore_context:
+ mov r0, #0
+ mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer
+ mcr p15, 0, r0, c8, c7, 0 @ Invalidate TLBs
+ mcr p15, 0, r0, c7, c7, 0 @ Invalidate I & D cache
+ nop
+ nop
+
+ mov r0, #0xd3
+ msr cpsr, r0
+
+ bl __create_temp_page_tables
+ mov r3, r4
+
+ mov r1, #PHYS_RAM_START
+ ldr r1, [r1]
+ ldr r2, [r1, #MMUDOMAIN_OFFS]
+ ldr r4, [r1, #MMUCPACCESS_OFS]
+ ldr r5, [r1, #MMUPID_OFFS]
+ ldr r6, =__resume_after_mmu
+ ldr r7, [r1, #MMUCTL_OFFS]
+ ldr r8, [r1, #MMUTTB_OFFS]
+ add r1, r1, #(0xC0000000 - PHYS_RAM_START)
+ mov r0, #0
+@ mcr p15, 0, r4, c15, c1, 0 @ cpaccess
+ mcr p15, 0, r5, c13, c0, 0 @ pid
+ mcr p15, 0, r2, c3, c0, 0 @ domain
+ mcr p15, 0, r3, c2, c0, 0 @ ttb
+ b 1f
+ .align 5
+1: mov r0, r0
+ mcr p15, 0, r7, c1, c0, 0 @ mmuctl
+ nop
+ mrc p15, 0, r0, c3, c0, 0 @ read id
+ mov r0, r0
+ mov r0, r0
+ sub pc, r6, r5, lsr #32
+ nop
+ nop
+ nop
+__resume_after_mmu:
+ mov r0, #0
+ mcr p15, 0, r0, c8, c7, 0 @ Invalidate TLBs
+ mcr p15, 0, r0, c7, c7, 0 @ Invalidate I & D cache
+
+ mov r0, r8
+ bl cpu_arm926_switch_mm
+
+ mov r0, #0xd1 @FIQ, Interrupts disabled
+ ldr r2, [r1, #FIQ_SPSR_OFFS]
+ add r3, r1, #FIQ_R8_OFFS
+ msr cpsr, r0
+ msr spsr, r2
+ ldmia r3!, {r8-r12, sp, lr}
+
+ mov r0, #0xd7 @ABT, Interrupts disabled
+ ldr r2, [r1, #ABT_SPSR_OFFS]
+ add r3, r1, #ABT_SP_OFFS
+ msr cpsr, r0
+ msr spsr, r2
+ ldmia r3!, {sp, lr}
+
+ mov r0, #0xd2 @IRQ, Interrupts disabled
+ ldr r2, [r1, #IRQ_SPSR_OFFS]
+ add r3, r1, #IRQ_SP_OFFS
+ msr cpsr, r0
+ msr spsr, r2
+ ldmia r3!, {sp, lr}
+
+ mov r0, #0xdb @UND, Interrupts disabled
+ ldr r2, [r1, #UND_SPSR_OFFS]
+ add r3, r1, #UND_SP_OFFS
+ msr cpsr, r0
+ msr spsr, r2
+ ldmia r3!, {sp, lr}
+
+ mov r0, #0xdf @SYS, Interrupts disabled
+ add r3, r1, #SYS_SP_OFFS
+ msr cpsr, r0
+ ldmia r3!, {sp, lr}
+
+ mov r0, #0xd3 @SVC, interrupts disabled
+ ldr r2, [r1, #SVC_SPSR_OFFS]
+ ldr r3, [r1, #SVC_SP_OFFS]
+ msr cpsr, r0
+ msr spsr, r2
+ mov sp, r3
+
+#if 0
+ @ select CPU bypass, will be cleared afterwards
+ ldr r0, =HW_CLKCTRL_CLKSEQ_ADDR
+ ldr r2, =HW_CLKCTRL_HBUS_ADDR
+ ldr r4, =HW_CLKCTRL_CPU_ADDR
+ mov r1, #(1<<7)
+ ldr r3, [r2]
+ bic r3, r3, #BM_CLKCTRL_HBUS_DIV
+ orr r3, r3, #1
+ ldr r5, [r4]
+ bic r5, r5, #BM_CLKCTRL_CPU_DIV_CPU
+ orr r5, r5, #1
+ str r1, [r0, #4]
+ str r3, [r2]
+ str r5, [r4]
+#endif
+ @ restore regs and return
+ ldmfd sp!, {r1 - r12, lr}
+ mov pc, lr
+
+__stmp_flush_cache_addr2:
+ .word arm926_flush_kern_cache_all
+__stmp_resume_point:
+ .word __restore_context
+ENTRY(stmp_s2ram_alloc_sz)
+ .word . - stmp37xx_cpu_suspend
+
+__create_temp_page_tables:
+ ldr r4, =(__temp_ttb - 0xC0000000 + PHYS_RAM_START)
+
+ /*
+ * Clear the 16K level 1 swapper page table
+ */
+ mov r0, r4
+ mov r3, #0
+ add r6, r0, #0x4000
+1: str r3, [r0], #4
+ str r3, [r0], #4
+ str r3, [r0], #4
+ str r3, [r0], #4
+ teq r0, r6
+ bne 1b
+
+ /*
+ * Create identity mapping for the area close to where we are to
+ * cater for the MMU enable.
+ */
+ mov r6, pc, lsr #20 @ kind of where we are
+ ldr r7, =(PMD_TYPE_SECT | PMD_SECT_BUFFERABLE | PMD_SECT_CACHEABLE | PMD_BIT4 | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ)
+
+ orr r3, r7, r6, lsl #20 @ flags + kernel base
+ str r3, [r4, r6, lsl #2] @ identity mapping
+
+ mov r6, r6, lsl #20
+ add r6, r6, #(0xC0000000-PHYS_RAM_START)
+ str r3, [r4, r6, lsr #18]
+
+ mov pc, lr
+ .ltorg
+
+ .section ".sdata", "a"
+ .align 14
+__temp_ttb:
+ .space 0x8000
diff --git a/arch/arm/mach-stmp3xxx/sleep.h b/arch/arm/mach-stmp3xxx/sleep.h
new file mode 100644
index 000000000000..2d7414382bf0
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/sleep.h
@@ -0,0 +1,113 @@
+/*
+ * Deep Sleep related defines
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __PM_H__
+#define __PM_H__
+
+#include <mach/regs-clkctrl.h>
+
+#define MMUTTB1_MASK 0x00003FE0
+#define MMUTTBC_MASK 0xFFFFFFFC
+
+#define LINK_OFFS 0x08
+#define MMUCTL_OFFS 0x0C
+#define MMUAUXCTL_OFFS 0x10
+#define MMUCPACCESS_OFS 0x14
+#define MMUTTB_OFFS 0x18
+#define MMUPID_OFFS 0x1C
+#define MMUDOMAIN_OFFS 0x20
+#define SVC_R8_OFFS 0x2C
+#define SVC_SP_OFFS 0x40
+#define SVC_SPSR_OFFS 0x44
+#define FIQ_SPSR_OFFS 0x48
+#define FIQ_R8_OFFS 0x4C
+#define FIQ_SP_OFFS 0x60
+#define ABT_R8_OFFS 0x68
+#define ABT_SPSR_OFFS 0x7C
+#define ABT_SP_OFFS 0x80
+#define IRQ_R8_OFFS 0x88
+#define IRQ_SPSR_OFFS 0x9C
+#define IRQ_SP_OFFS 0xA0
+#define UND_SPSR_OFFS 0xA8
+#define UND_SP_OFFS 0xAC
+#define SYS_SPSR_OFFS 0xB4
+#define SYS_SP_OFFS 0xB8
+
+#ifndef __ASSEMBLER__
+#define SLEEP_DATA_FINGERPRINT 0xdeadbeef
+struct sleep_data {
+ u32 fingerprint;
+ u32 wake_addr;
+ u32 link_addr;
+ u32 mmuctl;
+ u32 mmuauxctl;
+ u32 mmucpaccess;
+ u32 mmuttb;
+ u32 mmupid;
+ u32 mmudomain;
+ u32 svc_r6;
+ u32 svc_r7;
+ u32 svc_r8;
+ u32 svc_r9;
+ u32 svc_r10;
+ u32 svc_r11;
+ u32 svc_r12;
+ u32 svc_sp;
+ u32 svc_spsr;
+ u32 fiq_spsr;
+ u32 fiq_r8;
+ u32 fiq_r9;
+ u32 fiq_r10;
+ u32 fiq_r11;
+ u32 fiq_r12;
+ u32 fiq_sp;
+ u32 fiq_lr;
+ u32 abt_r8;
+ u32 abt_r9;
+ u32 abt_r10;
+ u32 abt_r11;
+ u32 abt_r12;
+ u32 abt_spsr;
+ u32 abt_sp;
+ u32 abt_lr;
+ u32 irq_r8;
+ u32 irq_r9;
+ u32 irq_r10;
+ u32 irq_r11;
+ u32 irq_r12;
+ u32 irq_spsr;
+ u32 irq_sp;
+ u32 irq_lr;
+ u32 und_spsr;
+ u32 und_sp;
+ u32 und_lr;
+ u32 sys_spsr;
+ u32 sys_sp;
+ u32 sys_lr;
+ u32 pinmux[0x100];
+ u32 icoll_ctrl;
+ union {
+ u32 prio[0x10];
+ u32 intr[0x80];
+ } icoll;
+ u32 clks[16];
+ u32 old_c00;
+ u32 old_c04;
+};
+
+#endif /* __ASSEMBLER__ */
+#endif /* __PM_H__ */
diff --git a/arch/arm/mach-stmp3xxx/spdif.c b/arch/arm/mach-stmp3xxx/spdif.c
new file mode 100644
index 000000000000..4fab7f90d7f4
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/spdif.c
@@ -0,0 +1,36 @@
+/*
+ * Pin multiplexing for SPDIF transmitter on STMP3780 dev. board
+ *
+ * Author: Vladimir Barinov <vbarinov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include "common.h"
+
+int spdif_pinmux_request(void)
+{
+ return stmp3xxx_request_pin_group(&spdif_pins, "spdif");
+}
+EXPORT_SYMBOL_GPL(spdif_pinmux_request);
+
+void spdif_pinmux_free(void)
+{
+ stmp3xxx_release_pin_group(&spdif_pins, "spdif");
+}
+EXPORT_SYMBOL_GPL(spdif_pinmux_free);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vladimir Barinov <vbarinov@embeddedalley.com>");
diff --git a/arch/arm/mach-stmp3xxx/spi.c b/arch/arm/mach-stmp3xxx/spi.c
new file mode 100644
index 000000000000..fb310a8b74bd
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/spi.c
@@ -0,0 +1,106 @@
+/*
+ * Freescale STMP37XX/STMP378X SPI module pin multiplexing
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <mach/stmp3xxx.h>
+#include "common.h"
+
+/*
+ These pins are:
+ SCK (SSPx_SCK)
+ MOSI (SSPx_CMD)
+ MISO (SSPx_DATA0)
+ SSn (SSPx_DATA3)
+ Please add new pins in the same order, thanks :)
+*/
+static struct pin_desc ssp_pins_desc[2][4] = {
+ [0] = {
+ { PINID_SSP1_SCK, PIN_FUN1, PIN_8MA, PIN_3_3V, 0, },
+ { PINID_SSP1_CMD, PIN_FUN1, PIN_4MA, PIN_3_3V, 0, },
+ { PINID_SSP1_DATA0, PIN_FUN1, PIN_4MA, PIN_3_3V, 0, },
+ { PINID_SSP1_DATA3, PIN_FUN1, PIN_4MA, PIN_3_3V, 0, },
+ },
+ [1] = {
+#if defined(CONFIG_ARCH_STMP37XX)
+ { PINID_GPMI_IRQ, PIN_FUN3, PIN_8MA, PIN_3_3V, 0, },
+ { PINID_GPMI_RDY2, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, },
+ { PINID_EMI_CE2N, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, },
+ { PINID_GPMI_D03, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, },
+#elif defined(CONFIG_ARCH_STMP378X)
+ { PINID_GPMI_WRN, PIN_FUN3, PIN_8MA, PIN_3_3V, 0, },
+ { PINID_GPMI_RDY1, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, },
+ { PINID_GPMI_D00, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, },
+ { PINID_GPMI_D03, PIN_FUN3, PIN_4MA, PIN_3_3V, 0, },
+#endif
+ },
+};
+
+static struct pin_group ssp_pins[2] = {
+ [0] = {
+ .pins = ssp_pins_desc[0],
+ .nr_pins = ARRAY_SIZE(ssp_pins_desc[0]),
+ },
+ [1] = {
+ .pins = ssp_pins_desc[1],
+ .nr_pins = ARRAY_SIZE(ssp_pins_desc[1]),
+ },
+};
+
+int stmp37xx_spi_pins_request(char *id, int ssp)
+{
+ return stmp3xxx_request_pin_group(&ssp_pins[ssp-1], id);
+}
+EXPORT_SYMBOL_GPL(stmp37xx_spi_pins_request);
+
+void stmp37xx_spi_pins_release(char *id, int ssp)
+{
+ stmp3xxx_release_pin_group(&ssp_pins[ssp-1], id);
+}
+EXPORT_SYMBOL_GPL(stmp37xx_spi_pins_release);
+
+int stmp37xx_spi_enc_init(void *spi_dev)
+{
+ struct spi_device *spi = spi_dev;
+ struct stmp37xx_spi_platform_data *data = spi->dev.platform_data;
+
+ gpio_request(data->irq_pin, spi->dev.bus_id);
+ gpio_direction_input(data->irq_pin);
+ stmp3xxx_configure_irq(data->irq_pin, IRQ_TYPE_EDGE_FALLING);
+ spi->irq = gpio_to_irq(data->irq_pin);
+ dev_dbg(&spi->dev, "Assigned IRQ %d(%s)\n", spi->irq, __func__);
+ return 0;
+}
+
+int stmp37xx_spi_enc_release(void *spi_dev)
+{
+ struct spi_device *spi = spi_dev;
+ struct stmp37xx_spi_platform_data *data = spi->dev.platform_data;
+
+ stmp3xxx_configure_irq(data->irq_pin, IRQ_TYPE_NONE);
+ gpio_free(data->irq_pin);
+ return 0;
+}
+
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-stmp3xxx/stmp378x.c b/arch/arm/mach-stmp3xxx/stmp378x.c
new file mode 100644
index 000000000000..a5d282c1519b
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/stmp378x.c
@@ -0,0 +1,419 @@
+/*
+ * Freescale STMP378X platform support
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/sysdev.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/input.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/dma.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/map.h>
+#include <asm/mach/time.h>
+
+#include <mach/ocram-malloc.h>
+
+#include <mach/lcdif.h>
+#include <mach/system.h>
+#include <mach/platform.h>
+#include <mach/regs-icoll.h>
+#include <mach/regs-apbh.h>
+#include <mach/regs-apbx.h>
+#include <mach/regs-ocotp.h>
+
+#include "common.h"
+
+/*
+ * IRQ handling
+ */
+static void stmp378x_ack_irq(unsigned int irq)
+{
+ /* Tell ICOLL to release IRQ line */
+ HW_ICOLL_VECTOR_WR(0x0);
+
+ /* ACK current interrupt */
+ HW_ICOLL_LEVELACK_WR(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0);
+
+ /* Barrier */
+ (void) HW_ICOLL_STAT_RD();
+}
+
+static void stmp378x_disable_irq(unsigned int irq)
+{
+ /* IRQ disable */
+ HW_ICOLL_INTERRUPTn_CLR(irq, BM_ICOLL_INTERRUPTn_ENABLE);
+}
+
+static void stmp378x_mask_irq(unsigned int irq)
+{
+ /* IRQ disable */
+ HW_ICOLL_INTERRUPTn_CLR(irq, BM_ICOLL_INTERRUPTn_ENABLE);
+}
+
+static void stmp378x_unmask_irq(unsigned int irq)
+{
+ /* IRQ enable */
+ HW_ICOLL_INTERRUPTn_SET(irq, BM_ICOLL_INTERRUPTn_ENABLE);
+}
+
+static struct irq_chip stmp378x_chip = {
+ .disable = stmp378x_disable_irq,
+ .ack = stmp378x_ack_irq,
+ .mask = stmp378x_mask_irq,
+ .unmask = stmp378x_unmask_irq,
+};
+
+static void stmp378x_gpio_ack_irq(unsigned int irq)
+{
+ stmp3xxx_pin_ack_irq(irq);
+ stmp378x_ack_irq(irq);
+}
+
+static struct irq_chip stmp378x_gpio_chip = {
+ .disable = stmp378x_disable_irq,
+ .ack = stmp378x_gpio_ack_irq,
+ .mask = stmp378x_mask_irq,
+ .unmask = stmp378x_unmask_irq,
+};
+
+static int stmp378x_irq_is_gpio(int irq)
+{
+ return irq == IRQ_GPIO0 || irq == IRQ_GPIO1 || irq == IRQ_GPIO2;
+}
+
+void __init stmp378x_init_irq(void)
+{
+ stmp3xxx_init_irq(&stmp378x_chip,
+ &stmp378x_gpio_chip,
+ stmp378x_irq_is_gpio);
+}
+
+/*
+ * DMA interrupt handling
+ */
+void stmp3xxx_arch_dma_enable_interrupt(int channel)
+{
+ int dmabus = channel / 16;
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ HW_APBH_CTRL1_SET(1 << (16 + (channel % 16)));
+ HW_APBH_CTRL2_SET(1 << (16 + (channel % 16)));
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ HW_APBX_CTRL1_SET(1 << (16 + (channel % 16)));
+ HW_APBX_CTRL2_SET(1 << (16 + (channel % 16)));
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_enable_interrupt);
+
+void stmp3xxx_arch_dma_clear_interrupt(int channel)
+{
+ int dmabus = channel / 16;
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ HW_APBH_CTRL1_CLR(1 << (channel % 16));
+ HW_APBH_CTRL2_CLR(1 << (channel % 16));
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ HW_APBX_CTRL1_CLR(1 << (channel % 16));
+ HW_APBX_CTRL2_CLR(1 << (channel % 16));
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_clear_interrupt);
+
+int stmp3xxx_arch_dma_is_interrupt(int channel)
+{
+ int dmabus = channel / 16;
+ int r = 0;
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ r = HW_APBH_CTRL1_RD() & (1 << (channel % 16));
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ r = HW_APBX_CTRL1_RD() & (1 << (channel % 16));
+ break;
+ }
+ return r;
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_is_interrupt);
+
+void stmp3xxx_arch_dma_reset_channel(int channel)
+{
+ int dmabus = channel / 16;
+ unsigned chbit = 1 << (channel % 16);
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ /* Reset channel and wait for it to complete */
+ HW_APBH_CTRL0_SET(chbit <<
+ BP_APBH_CTRL0_RESET_CHANNEL);
+ while (HW_APBH_CTRL0_RD() &
+ (chbit << BP_APBH_CTRL0_RESET_CHANNEL))
+ continue;
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ /* Reset channel and wait for it to complete */
+ HW_APBX_CHANNEL_CTRL_SET(
+ BF_APBX_CHANNEL_CTRL_RESET_CHANNEL(chbit));
+ while (HW_APBX_CHANNEL_CTRL_RD() &
+ BF_APBX_CHANNEL_CTRL_RESET_CHANNEL(chbit))
+ continue;
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_reset_channel);
+
+void stmp3xxx_arch_dma_freeze(int channel)
+{
+ int dmabus = channel / 16;
+ unsigned chbit = 1 << (channel % 16);
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ HW_APBH_CTRL0_SET(1<<chbit);
+ break;
+ case STMP3XXX_BUS_APBX:
+ HW_APBX_CHANNEL_CTRL_SET(1<<chbit);
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_freeze);
+
+void stmp3xxx_arch_dma_unfreeze(int channel)
+{
+ int dmabus = channel / 16;
+ unsigned chbit = 1 << (channel % 16);
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ HW_APBH_CTRL0_CLR(1<<chbit);
+ break;
+ case STMP3XXX_BUS_APBX:
+ HW_APBX_CHANNEL_CTRL_CLR(1<<chbit);
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_unfreeze);
+
+/*
+ * STMP378x dedicated pin groups
+ */
+static struct pin_desc i2c_pins_desc[] = {
+ { PINID_I2C_SCL, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_I2C_SDA, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+};
+
+struct pin_group i2c_pins = {
+ .pins = i2c_pins_desc,
+ .nr_pins = ARRAY_SIZE(i2c_pins_desc),
+};
+
+static struct pin_desc gpmi_pins_desc[] = {
+ { PINID_GPMI_CE0N, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_CE1N, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GMPI_CE2N, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_CLE, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_ALE, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_WPN, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_GPMI_RDY1, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D00, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D01, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D02, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D03, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D04, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D05, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D06, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D07, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_RDY0, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_RDY2, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_RDY3, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_WRN, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_GPMI_RDN, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+};
+
+struct pin_group gpmi_pins = {
+ .pins = gpmi_pins_desc,
+ .nr_pins = ARRAY_SIZE(gpmi_pins_desc),
+};
+
+static struct pin_desc lcd_hx8238a_desc[] = {
+ { PINID_LCD_D00, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D01, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D02, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D03, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D04, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D05, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D06, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D07, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D08, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D09, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D10, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D11, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D12, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D13, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D14, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D15, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D16, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_D17, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_RESET, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_VSYNC, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_HSYNC, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_ENABLE, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_LCD_DOTCK, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D13, PIN_FUN2, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D12, PIN_FUN2, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D11, PIN_FUN2, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D10, PIN_FUN2, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D09, PIN_FUN2, PIN_8MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D08, PIN_FUN2, PIN_8MA, PIN_3_3V, 0 },
+};
+
+struct pin_group stmp378x_lcd_pins = {
+ .pins = lcd_hx8238a_desc,
+ .nr_pins = ARRAY_SIZE(lcd_hx8238a_desc),
+};
+
+unsigned stmp378x_lcd_spi_pins[] = {
+ [SPI_MOSI] = PINID_LCD_WR,
+ [SPI_SCLK] = PINID_LCD_RS,
+ [SPI_CS] = PINID_LCD_CS,
+};
+
+struct pin_desc appuart_pins_0[] = {
+ { PINID_AUART1_CTS, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+ { PINID_AUART1_RTS, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+ { PINID_AUART1_RX, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+ { PINID_AUART1_TX, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+};
+
+#if 0 /* temporary ? */
+struct pin_desc appuart_pins_1[] = {
+ { PINID_AUART2_CTS, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+ { PINID_AUART2_RTS, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+ { PINID_AUART2_RX, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+ { PINID_AUART2_TX, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+};
+#endif
+
+struct pin_group appuart_pins[] = {
+ [0] = {
+ .pins = appuart_pins_0,
+ .nr_pins = ARRAY_SIZE(appuart_pins_0),
+ },
+ [1] = {
+#if 0 /* undefined yet. */
+ .pins = appuart_pins_1,
+ .nr_pins = ARRAY_SIZE(appuart_pins_1),
+#endif
+ },
+};
+
+struct pin_desc dbguart_pins_0[] = {
+ { PINID_PWM0, PIN_FUN3, },
+ { PINID_PWM1, PIN_FUN3, },
+};
+
+struct pin_group dbguart_pins[] = {
+ [0] = {
+ .pins = dbguart_pins_0,
+ .nr_pins = ARRAY_SIZE(dbguart_pins_0),
+ },
+};
+
+static struct pin_desc usb_mux_pins_desc[] = {
+ { PINID_SSP1_DETECT, PIN_FUN3, },
+};
+
+struct pin_group usb_mux_pins = {
+ .pins = usb_mux_pins_desc,
+ .nr_pins = ARRAY_SIZE(usb_mux_pins_desc),
+};
+
+static struct pin_desc spdif_pins_desc[] = {
+ { PINID_ROTARYA, PIN_FUN3, PIN_4MA, PIN_1_8V, 0, },
+};
+
+struct pin_group spdif_pins = {
+ .pins = spdif_pins_desc,
+ .nr_pins = ARRAY_SIZE(spdif_pins_desc),
+};
+
+/*
+ * The registers are all very closely mapped, so we might as well map them all
+ * with a single mapping
+ *
+ * Logical Physical
+ * f0000000 80000000 On-chip registers
+ * f1000000 00000000 256k on-chip SRAM
+ */
+
+static struct map_desc stmp378x_io_desc[] __initdata = {
+ {
+ .virtual = IO_ADDRESS(STMP378X_REGS_BASE),
+ .pfn = __phys_to_pfn(STMP378X_REGS_BASE),
+ .length = STMP378X_REGS_SIZE,
+ .type = MT_DEVICE,
+ },
+ {
+ .virtual = STMP3XXX_OCRAM_VA_BASE,
+ .pfn = __phys_to_pfn(STMP378X_OCRAM_BASE),
+ .length = STMP378X_OCRAM_SIZE,
+ .type = MT_DEVICE,
+ },
+};
+
+void __init stmp378x_map_io(void)
+{
+ iotable_init(stmp378x_io_desc, ARRAY_SIZE(stmp378x_io_desc));
+}
+
+int get_evk_board_version()
+{
+ int boardid;
+ boardid = HW_OCOTP_CUSTCAP_RD();
+ boardid &= 0x30000000;
+ boardid = boardid >> 28;
+
+ return boardid;
+}
+
+EXPORT_SYMBOL_GPL(get_evk_board_version);
+
diff --git a/arch/arm/mach-stmp3xxx/stmp378x_devb.c b/arch/arm/mach-stmp3xxx/stmp378x_devb.c
new file mode 100644
index 000000000000..7fef2f5962ba
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/stmp378x_devb.c
@@ -0,0 +1,359 @@
+/*
+ * Freescale STMP378X development board support
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/usb/otg.h>
+
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/stmp3xxx.h>
+#include <mach/gpmi.h>
+#include <mach/power.h>
+#include <mach/regs-power.h>
+#include <mach/regs-digctl.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-pinctrl.h>
+#include <mach/regs-pwm.h>
+
+#include "common.h"
+
+static struct resource stmp378xdb_led_resources[] = {
+ {
+ .name = "led0",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_DISABLED,
+ },
+ {
+ .name = "led1",
+ .start = 1,
+ .end = 1,
+ .flags = IORESOURCE_DISABLED,
+ },
+ {
+ .name = "led2",
+ .start = 2,
+ .end = 2,
+ .flags = IORESOURCE_DISABLED,
+ },
+ {
+ .name = "led3",
+ .start = 3,
+ .end = 3,
+ .flags = IORESOURCE_DISABLED,
+ },
+ {
+ .name = "led4",
+ .start = 4,
+ .end = 4,
+ .flags = IORESOURCE_DISABLED,
+ },
+};
+
+static struct platform_device stmp378x_leds = {
+ .name = "stmp378x-pwm-led",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(stmp378xdb_led_resources),
+ .resource = stmp378xdb_led_resources,
+};
+
+static struct platform_device *devices[] = {
+ &stmp3xxx_keyboard,
+ &stmp3xxx_touchscreen,
+ &stmp3xxx_appuart,
+ &stmp3xxx_dbguart,
+ &stmp3xxx_watchdog,
+ &stmp3xxx_rtc,
+ &stmp3xxx_framebuffer,
+ &stmp3xxx_backlight,
+ &stmp3xxx_rotdec,
+ &stmp378x_i2c,
+ &stmp3xxx_persistent,
+ &stmp3xxx_dcp_bootstream,
+ &stmp3xxx_dcp,
+ &stmp3xxx_mtest,
+ &stmp3xxx_battery,
+ &stmp3xxx_pxp,
+ &stmp3xxx_viim,
+};
+
+static struct stmpkbd_keypair keyboard_data[] = {
+ { 100, KEY_F4 },
+ { 306, KEY_F5 },
+ { 626, KEY_F6 },
+ { 932, KEY_F7 },
+ { 1260, KEY_F8 },
+ { 1584, KEY_F9 },
+ { 1907, KEY_F10 },
+ { 2207, KEY_F11 },
+ { 2525, KEY_F12 },
+ { 2831, KEY_F13},
+ { 3134, KEY_F14 },
+ { -1, 0 },
+};
+const char *gpmi_part_probes[] = { "cmdlinepart", NULL };
+
+#define UID_SIZE SZ_1M
+#define UID_OFFSET (20*SZ_1M)
+
+struct mtd_partition gpmi_partitions_chip0[] = {
+ [0] = {
+ .offset = 0,
+ .size = UID_OFFSET,
+ .name = "Boot#0",
+ .mask_flags = 0,
+ },
+ /* there a 1M UID partition here */
+/*
+ [1] = {
+ .offset = MTDPART_OFS_APPEND,
+ .size = 5 * SZ_1M,
+ .name = "id",
+ .mask_flags = 0,
+ },
+*/
+ /* This partition is managed by UBI */
+ [1] = {
+ .offset = UID_OFFSET + UID_SIZE,
+ .size = MTDPART_SIZ_FULL,
+ .name = "UBI#0",
+ .mask_flags = 0,
+ },
+};
+
+struct mtd_partition gpmi_partitions_chip1[] = {
+ [0] = {
+ .offset = 0,
+ .size = UID_OFFSET,
+ .name = "Boot#1",
+ .mask_flags = 0,
+ },
+ /* This partition is managed by UBI */
+ [1] = {
+ .offset = UID_OFFSET,
+ .size = MTDPART_SIZ_FULL,
+ .name = "UBI#1",
+ .mask_flags = 0,
+ },
+};
+
+static char *gpmi_concat_parts[] = {
+ [0] = "UBI#0",
+ [1] = "UBI#1",
+ [2] = NULL,
+};
+
+static struct gpmi_platform_data gpmi_partitions = {
+ .uid_offset = UID_OFFSET,
+ .uid_size = UID_SIZE,
+ .io_uA = 70000,
+ .items = 2,
+ .concat_name = "UBI",
+ .concat_parts = gpmi_concat_parts,
+ .parts = {
+ [0] = {
+ .part_probe_types = gpmi_part_probes,
+ .nr_partitions = ARRAY_SIZE(gpmi_partitions_chip0),
+ .partitions = gpmi_partitions_chip0,
+ },
+ [1] = {
+ .part_probe_types = gpmi_part_probes,
+ .nr_partitions = ARRAY_SIZE(gpmi_partitions_chip1),
+ .partitions = gpmi_partitions_chip1,
+ },
+ },
+};
+
+int usb_host_wakeup_irq(struct device *wkup_dev)
+{
+ return 0;
+}
+EXPORT_SYMBOL(usb_host_wakeup_irq);
+
+void usb_host_set_wakeup(struct device *wkup_dev, bool para)
+{
+}
+EXPORT_SYMBOL(usb_host_set_wakeup);
+
+static struct stmp37xx_spi_platform_data enc_data = {
+ .irq_pin = PINID_SSP1_DATA1,
+ .hw_init = stmp37xx_spi_enc_init,
+ .hw_release = stmp37xx_spi_enc_release,
+};
+
+static struct spi_board_info spi_board_info[] __initdata = {
+#if defined(CONFIG_ENC28J60) || defined(CONFIG_ENC28J60_MODULE)
+ {
+ .modalias = "enc28j60",
+ .max_speed_hz = 6 * 1000 * 1000,
+ .bus_num = 1,
+ .chip_select = 0,
+ .platform_data = &enc_data,
+ },
+#endif
+};
+
+/*
+ * There are 4 LEDs connected to PWM module available on the STMP378x
+ * Dev. board:
+ * PWM0 - shared with Debug UART
+ * PWM1 - shared with Debug UART
+ * PWM2 - shared with LCD panel (LMS350)
+ * PWM3 - shared with SD/MMC card slot
+ *
+ * pwm_leds= option allows to choose PWM outputs to be used to control LEDs
+ * Conflicting kernel modules should be disabled
+ */
+#define PWM_MAX 4
+static int pwm_leds_enable;
+
+int __init pwm_leds_setup(char *str)
+{
+ char tmp[10];
+ char *l, *s;
+ unsigned long pwmn;
+ int r;
+
+ s = str;
+
+ do {
+ memset(tmp, 0, sizeof(tmp));
+
+ l = strchr(s, ',');
+
+ if (l) {
+ strncpy(tmp, s, min_t(int, l - s, sizeof(tmp) - 1));
+ r = strict_strtoul(tmp, 0, &pwmn);
+ s = ++l;
+ } else {
+ r = strict_strtoul(s, 0, &pwmn);
+ }
+
+ if (r == 0 && pwmn < PWM_MAX) {
+ stmp378xdb_led_resources[pwmn].flags = 0;
+ pwm_leds_enable++;
+ }
+ } while (l);
+
+ return 0;
+}
+__setup("pwm_leds=", pwm_leds_setup);
+
+int __init enc28j60_setup(char *str)
+{
+ char *cur = str,
+ *item;
+ unsigned long bus, cs, irqbank, irqpin;
+ char tmps[20];
+ int r;
+
+ bus = cs = irqbank = irqpin = 0;
+ /*
+ we recognize strings like enc28j60=chipselect@bus,irqpin
+ where
+ chipselect is 0..4,
+ bus is 0/1,
+ irqpin is in format bank:pin
+ */
+ item = strchr(cur, '@');
+ if (item) {
+ memset(tmps, 0, sizeof(tmps));
+ strncpy(tmps, cur, min_t(int, item - cur, sizeof(tmps - 1)));
+ r = strict_strtoul(tmps, 0, &cs);
+ if (r < 0)
+ return r;
+ cur = item + 1;
+ }
+
+ item = strchr(cur, ',');
+ if (item) {
+ memset(tmps, 0, sizeof(tmps));
+ strncpy(tmps, cur, min_t(int, item - cur, sizeof(tmps - 1)));
+ r = strict_strtoul(tmps, 0, &bus);
+ if (r < 0)
+ return r;
+ cur = item + 1;
+ }
+
+ item = strchr(cur, ':');
+ if (item) {
+ memset(tmps, 0, sizeof(tmps));
+ strncpy(tmps, cur, min_t(int, item - cur, sizeof(tmps - 1)));
+ r = strict_strtoul(tmps, 0, &irqbank);
+ if (r < 0)
+ return r;
+ cur = item + 1;
+ }
+
+ r = strict_strtoul(cur, 0, &irqpin);
+ if (r < 0)
+ return r;
+
+ pr_info("%s: bus = %ld, cs = %ld, irqpin = %ld + %ld\n",
+ __func__, bus, cs, irqbank, irqpin);
+
+ spi_board_info[0].bus_num = bus;
+ spi_board_info[0].chip_select = cs;
+ enc_data.irq_pin = STMP3XXX_PINID(irqbank, irqpin);
+ return 0;
+}
+__setup("enc28j60=", enc28j60_setup);
+
+static struct i2c_board_info __initdata stmp3xxx_i2c_devices[] = {
+ { I2C_BOARD_INFO("stfm1000", 0xc0), .flags = I2C_M_TEN }
+};
+
+static void __init stmp378x_devb_init(void)
+{
+ struct fsl_usb2_platform_data *udata;
+ stmp3xxx_init();
+
+ i2c_register_board_info(0, stmp3xxx_i2c_devices, ARRAY_SIZE(stmp3xxx_i2c_devices));
+
+ stmp3xxx_set_mmc_data(&stmp3xxx_mmc.dev);
+ stmp3xxx_gpmi.dev.platform_data = &gpmi_partitions;
+ stmp3xxx_keyboard.dev.platform_data = &keyboard_data;
+
+ spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
+ stmp3xxx_ssp1_device_register(); /* MMC or SSP */
+ stmp3xxx_ssp2_device_register(); /* MMC or SSP */
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+ if (pwm_leds_enable)
+ platform_device_register(&stmp378x_leds);
+}
+
+MACHINE_START(STMP378X, "STMP378X")
+ .phys_io = 0x80000000,
+ .io_pg_offst = ((0xf0000000) >> 18) & 0xfffc,
+ .boot_params = 0x40000100,
+ .map_io = stmp378x_map_io,
+ .init_irq = stmp378x_init_irq,
+ .timer = &stmp3xxx_timer,
+ .init_machine = stmp378x_devb_init,
+MACHINE_END
diff --git a/arch/arm/mach-stmp3xxx/stmp378x_devb_rotdec.c b/arch/arm/mach-stmp3xxx/stmp378x_devb_rotdec.c
new file mode 100644
index 000000000000..0fefe2635501
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/stmp378x_devb_rotdec.c
@@ -0,0 +1,47 @@
+/*
+ * Freescale STMP378X Rotary Encoder module pin multiplexing
+ *
+ * Author: Drew Benedetti <drewb@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <mach/pins.h>
+#include "pinmux.h"
+
+#define ROTARY_FUN PIN_FUN1
+
+#define TITLE "stmp3xxx-rotdec"
+
+int rotdec_pinmux_request(void)
+{
+ int rc = 0;
+
+ rc |= stmp3xxx_request_pin(PINID_ROTARYA, ROTARY_FUN, TITLE);
+ rc |= stmp3xxx_request_pin(PINID_ROTARYB, ROTARY_FUN, TITLE);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rotdec_pinmux_request);
+
+void rotdec_pinmux_free(void)
+{
+ stmp3xxx_release_pin(PINID_ROTARYA, TITLE);
+ stmp3xxx_release_pin(PINID_ROTARYB, TITLE);
+}
+EXPORT_SYMBOL_GPL(rotdec_pinmux_free);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Drew Benedetti <drewb@embeddedalley.com>");
diff --git a/arch/arm/mach-stmp3xxx/stmp378x_i2c.c b/arch/arm/mach-stmp3xxx/stmp378x_i2c.c
new file mode 100644
index 000000000000..d8612055f226
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/stmp378x_i2c.c
@@ -0,0 +1,275 @@
+/*
+ * Freescale STMP378X I2C low-level/dma functions
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <linux/dma-mapping.h>
+#include <mach/hardware.h>
+#include <mach/regs-i2c.h>
+#include <mach/regs-apbx.h>
+#include <mach/dma.h>
+#include <mach/i2c.h>
+
+#include "common.h"
+
+static unsigned int dma_channel =
+ STMP3xxx_DMA(STMP378X_APBX_I2C, STMP3XXX_BUS_APBX);
+
+
+static struct stmp3xxx_dma_descriptor i2c_dma_read[2];
+static struct stmp3xxx_dma_descriptor i2c_dma_write;
+static dma_addr_t i2c_buf_phys;
+static u8 *i2c_buf_virt;
+
+
+/*
+ * Select device to read from
+ */
+
+u32 cmd_i2c_select[4] = {
+ 0, /* Chain to i2c_read */
+
+ (BF_APBX_CHn_CMD_XFER_COUNT(1) |
+ /* BM_APBX_CHn_CMD_SEMAPHORE | */
+ BF_APBX_CHn_CMD_CMDWORDS(1) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_CHAIN |
+ BM_APBX_CHn_CMD_IRQONCMPLT | /* For debug*/
+ BF_APBX_CHn_CMD_COMMAND(BV_APBX_CHn_CMD_COMMAND__DMA_READ)),
+
+ 0, /* dma handler */
+
+ BM_I2C_CTRL0_RETAIN_CLOCK |
+ BM_I2C_CTRL0_PRE_SEND_START |
+ BM_I2C_CTRL0_MASTER_MODE |
+ BM_I2C_CTRL0_DIRECTION |
+ BF_I2C_CTRL0_XFER_COUNT(1)
+
+};
+
+u32 cmd_i2c_write[4] = {
+ 0,
+
+ (BM_APBX_CHn_CMD_SEMAPHORE |
+ BF_APBX_CHn_CMD_CMDWORDS(1) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BF_APBX_CHn_CMD_COMMAND(BV_APBX_CHn_CMD_COMMAND__DMA_READ)),
+
+ 0, /* dma handler */
+
+ BM_I2C_CTRL0_PRE_SEND_START |
+ BM_I2C_CTRL0_MASTER_MODE |
+/* BM_I2C_CTRL0_POST_SEND_STOP | */
+ BM_I2C_CTRL0_DIRECTION
+
+};
+
+
+u32 cmd_i2c_read[4] = {
+ 0,
+
+ (BM_APBX_CHn_CMD_SEMAPHORE |
+ BF_APBX_CHn_CMD_CMDWORDS(1) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BF_APBX_CHn_CMD_COMMAND(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE)),
+
+ 0, /* dma handler */
+
+ BM_I2C_CTRL0_SEND_NAK_ON_LAST |
+/* BM_I2C_CTRL0_POST_SEND_STOP | */
+ BM_I2C_CTRL0_MASTER_MODE |
+ 0 /*BF_I2C_CTRL0_DIRECTION(BV_I2C_CTRL0_DIRECTION__RECEIVE)*/
+};
+
+
+int hw_i2c_init_dma(struct device *dev)
+{
+ int ret;
+
+ ret = stmp3xxx_dma_request(dma_channel, dev, "i2c");
+ if (ret) {
+ dev_err(dev, "stmp3xxx_dma_request failed: error %d\n", ret);
+ return ret;
+ }
+
+ i2c_buf_virt =
+ dma_alloc_coherent(
+ dev,
+ PAGE_SIZE,
+ &i2c_buf_phys,
+ GFP_KERNEL);
+
+ if (i2c_buf_virt == NULL)
+ return -ENOMEM;
+
+
+ stmp3xxx_dma_allocate_command(
+ dma_channel,
+ &i2c_dma_read[0]);
+
+ stmp3xxx_dma_allocate_command(
+ dma_channel,
+ &i2c_dma_read[1]);
+
+ stmp3xxx_dma_allocate_command(
+ dma_channel,
+ &i2c_dma_write);
+
+ stmp3xxx_dma_reset_channel(dma_channel);
+ stmp3xxx_dma_clear_interrupt(dma_channel);
+ stmp3xxx_dma_enable_interrupt(dma_channel);
+ return 0;
+};
+
+void hw_i2c_free_dma(struct device *dev)
+{
+ stmp3xxx_dma_free_command(
+ dma_channel,
+ &i2c_dma_write);
+
+ stmp3xxx_dma_free_command(
+ dma_channel,
+ &i2c_dma_read[1]);
+
+ stmp3xxx_dma_free_command(
+ dma_channel,
+ &i2c_dma_read[0]);
+
+ dma_free_coherent(
+ dev,
+ PAGE_SIZE,
+ i2c_buf_virt,
+ i2c_buf_phys);
+
+ stmp3xxx_dma_release(dma_channel);
+}
+
+void hw_i2c_clear_dma_interrupt(void)
+{
+ stmp3xxx_dma_clear_interrupt(dma_channel);
+}
+EXPORT_SYMBOL(hw_i2c_clear_dma_interrupt);
+
+void hw_i2c_setup_write(u8 addr, void *buff, int len, int flags)
+{
+
+ memcpy(i2c_dma_write.command, &cmd_i2c_write, sizeof(cmd_i2c_write));
+
+ i2c_dma_write.command->cmd |=
+ BF_APBX_CHn_CMD_XFER_COUNT(len+1);
+
+ i2c_dma_write.command->pio_words[0] |=
+ BF_I2C_CTRL0_XFER_COUNT(len+1) | flags;
+
+ i2c_dma_write.command->buf_ptr = i2c_buf_phys;
+ i2c_buf_virt[0] = addr | I2C_WRITE ;
+ memcpy(&i2c_buf_virt[1], buff, len);
+}
+EXPORT_SYMBOL(hw_i2c_setup_write);
+
+void hw_i2c_finish_read(void *buff, int len)
+{
+ memcpy(buff, &i2c_buf_virt[1], len);
+
+}
+EXPORT_SYMBOL(hw_i2c_finish_read);
+
+void hw_i2c_setup_read(u8 addr, void *buff, int len, int flags)
+{
+
+ if (len > (PAGE_SIZE - 4))
+ BUG();
+
+ memcpy(i2c_dma_read[0].command,
+ &cmd_i2c_select,
+ sizeof(cmd_i2c_select));
+
+ memcpy(i2c_dma_read[1].command,
+ &cmd_i2c_read,
+ sizeof(cmd_i2c_read));
+
+ i2c_dma_read[0].command->next = i2c_dma_read[1].handle;
+ i2c_dma_read[0].command->buf_ptr = i2c_buf_phys ;
+ i2c_buf_virt[0] = addr | I2C_READ ;
+
+ i2c_dma_read[1].command->cmd |= BF_APBX_CHn_CMD_XFER_COUNT(len);
+
+ i2c_dma_read[1].command->pio_words[0] |=
+ BF_I2C_CTRL0_XFER_COUNT(len) | flags;
+
+ i2c_dma_read[1].command->buf_ptr = (u32)i2c_buf_phys + 1 ;
+ memcpy(&i2c_buf_virt[1], buff, len);
+
+}
+EXPORT_SYMBOL(hw_i2c_setup_read);
+
+void hw_i2c_run(int dir)
+{
+ if (dir == I2C_WRITE)
+ stmp3xxx_dma_go(dma_channel, &i2c_dma_write, 1);
+ else
+ stmp3xxx_dma_go(dma_channel, &i2c_dma_read[0], 1);
+}
+EXPORT_SYMBOL(hw_i2c_run);
+
+void hw_i2c_reset_dma(void)
+{
+ stmp3xxx_dma_reset_channel(dma_channel);
+ stmp3xxx_dma_clear_interrupt(dma_channel);
+}
+EXPORT_SYMBOL(hw_i2c_reset_dma);
+
+
+int hw_i2c_init(struct device *dev)
+{
+ if (stmp3xxx_request_pin_group(&i2c_pins, "i2c"))
+ return -1;
+
+
+ /* Take controller out of reset */
+ HW_I2C_CTRL0_CLR(BM_I2C_CTRL0_SFTRST | BM_I2C_CTRL0_CLKGATE);
+ udelay(10);
+
+/* * Set timing
+ * High time = 120 clks; read bit at 48 for 95Khz/24mhz
+ * Low time = 128 clks; write bit at 48 for 95khz/24mhz
+*/
+
+/*
+ Don't set 400khz by default; stfm1000 needs 100khz at the start.
+ HW_I2C_TIMING0_WR(0x00780030);
+ HW_I2C_TIMING1_WR(0x001F000F);
+ HW_I2C_TIMING2_WR(0x0015000D);
+*/
+ dev_dbg(dev, "I2C module version %x\n ", HW_I2C_VERSION_RD());
+ hw_i2c_init_dma(dev);
+ return 0;
+}
+EXPORT_SYMBOL(hw_i2c_init);
+
+void hw_i2c_stop(struct device *dev)
+{
+ HW_I2C_CTRL0_SET(BM_I2C_CTRL0_SFTRST);
+ hw_i2c_reset_dma();
+ hw_i2c_free_dma(dev);
+ stmp3xxx_release_pin_group(&i2c_pins, "i2c");
+}
+EXPORT_SYMBOL(hw_i2c_stop);
diff --git a/arch/arm/mach-stmp3xxx/stmp378x_lcdif.c b/arch/arm/mach-stmp3xxx/stmp378x_lcdif.c
new file mode 100644
index 000000000000..54aeb455c8b4
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/stmp378x_lcdif.c
@@ -0,0 +1,188 @@
+/*
+ * Freescale STMP378X LCDIF low-level routines
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/* #define DEBUG */
+
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+
+#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <mach/dma.h>
+#include <mach/regs-lcdif.h>
+#include <mach/regs-pinctrl.h>
+#include <mach/lcdif.h>
+
+#define MAX_CHAIN_LEN 10
+
+static struct stmp3xxx_dma_descriptor video_dma_descriptor[MAX_CHAIN_LEN];
+static struct stmp3xxx_lcd_dma_chain_info dma_chain_info[MAX_CHAIN_LEN];
+static unsigned dma_chain_info_pos;
+
+void stmp3xxx_init_lcdif(void)
+{
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_CLKGATE);
+ /* Reset controller */
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_SFTRST);
+ udelay(10);
+
+ /* Take controller out of reset */
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_SFTRST | BM_LCDIF_CTRL_CLKGATE);
+
+ /* Setup the bus protocol */
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_MODE86);
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_BUSY_ENABLE);
+
+ /* Take display out of reset */
+ HW_LCDIF_CTRL1_SET(BM_LCDIF_CTRL1_RESET);
+
+ /* VSYNC is an input by default */
+ HW_LCDIF_VDCTRL0_SET(BM_LCDIF_VDCTRL0_VSYNC_OEB);
+
+ /* Reset display */
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_RESET);
+ udelay(10);
+ HW_LCDIF_CTRL1_SET(BM_LCDIF_CTRL1_RESET);
+ udelay(10);
+}
+EXPORT_SYMBOL(stmp3xxx_init_lcdif);
+
+static int stmp378x_lcd_master = 1;
+int stmp3xxx_lcdif_dma_init(struct device *dev, dma_addr_t phys, int memsize,
+ int lcd_master)
+{
+ int ret = 0;
+
+ stmp378x_lcd_master = lcd_master;
+ if (lcd_master) {
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_LCDIF_MASTER);
+
+ HW_LCDIF_CUR_BUF_WR(phys);
+ HW_LCDIF_NEXT_BUF_WR(phys);
+ } else {
+ ret = stmp3xxx_dma_request(
+ STMP3xxx_DMA(LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH),
+ dev,
+ "lcdif");
+ if (ret) {
+ dev_err(dev,
+ "stmp3xxx_dma_request failed: error %d\n", ret);
+ goto out;
+ }
+
+ stmp3xxx_dma_reset_channel(
+ STMP3xxx_DMA(LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH));
+
+ stmp3xxx_dma_clear_interrupt(
+ STMP3xxx_DMA(LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH));
+ stmp3xxx_dma_enable_interrupt(
+ STMP3xxx_DMA(LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH));
+
+ dotclk_dma_chain_init(memsize, phys, video_dma_descriptor,
+ dma_chain_info, &dma_chain_info_pos);
+ }
+out:
+ return ret;
+}
+EXPORT_SYMBOL(stmp3xxx_lcdif_dma_init);
+
+void stmp3xxx_lcdif_dma_release(void)
+{
+ int i;
+
+ if (stmp378x_lcd_master) {
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_LCDIF_MASTER);
+ return;
+ }
+
+ for (i = 0; i < dma_chain_info_pos; i++)
+ stmp3xxx_dma_free_command(
+ STMP3xxx_DMA(LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH),
+ &video_dma_descriptor[i]);
+ stmp3xxx_dma_release(STMP3xxx_DMA(LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH));
+
+ dma_chain_info_pos = 0;
+}
+EXPORT_SYMBOL(stmp3xxx_lcdif_dma_release);
+
+void stmp3xxx_lcdif_run(void)
+{
+ if (stmp378x_lcd_master) {
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_LCDIF_MASTER);
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_RUN);
+ } else {
+ video_dma_descriptor[dma_chain_info_pos - 1].command->cmd &=
+ ~BM_APBH_CHn_CMD_SEMAPHORE;
+ stmp3xxx_dma_go(
+ STMP3xxx_DMA(LCD_DMA_CHANNEL, STMP3XXX_BUS_APBH),
+ video_dma_descriptor, 1);
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_lcdif_run);
+
+void stmp3xxx_lcdif_stop(void)
+{
+ if (stmp378x_lcd_master) {
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_RUN);
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_LCDIF_MASTER);
+ udelay(100);
+ } else {
+ video_dma_descriptor[dma_chain_info_pos - 1].command->cmd |=
+ BM_APBH_CHn_CMD_SEMAPHORE;
+ udelay(100);
+ }
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_CLKGATE);
+}
+EXPORT_SYMBOL(stmp3xxx_lcdif_stop);
+
+int stmp3xxx_lcdif_pan_display(dma_addr_t addr)
+{
+ if (stmp378x_lcd_master)
+ HW_LCDIF_NEXT_BUF_WR(addr);
+ else {
+ int i;
+ /* Modify the chain addresses */
+ for (i = 0; i < dma_chain_info_pos; ++i) {
+ *dma_chain_info[i].dma_addr_p = addr +
+ dma_chain_info[i].offset;
+ barrier();
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(stmp3xxx_lcdif_pan_display);
+
+static BLOCKING_NOTIFIER_HEAD(lcdif_client_list);
+
+int stmp3xxx_lcdif_register_client(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&lcdif_client_list, nb);
+}
+EXPORT_SYMBOL(stmp3xxx_lcdif_register_client);
+
+void stmp3xxx_lcdif_unregister_client(struct notifier_block *nb)
+{
+ blocking_notifier_chain_unregister(&lcdif_client_list, nb);
+}
+EXPORT_SYMBOL(stmp3xxx_lcdif_unregister_client);
+
+void stmp3xxx_lcdif_notify_clients(unsigned long event,
+ struct stmp3xxx_platform_fb_entry *pentry)
+{
+ blocking_notifier_call_chain(&lcdif_client_list, event, pentry);
+}
+EXPORT_SYMBOL(stmp3xxx_lcdif_notify_clients);
diff --git a/arch/arm/mach-stmp3xxx/stmp378x_pwm_led.c b/arch/arm/mach-stmp3xxx/stmp378x_pwm_led.c
new file mode 100644
index 000000000000..dd8970153357
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/stmp378x_pwm_led.c
@@ -0,0 +1,54 @@
+/*
+ * Freescale STMP378X PWM LEDs pin multiplexing
+ *
+ * Author: Drew Bendetti <drewb@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include "pinmux.h"
+
+#define PWM_PINID(n) STMP3XXX_PINID(1, 26 + n)
+
+int pwm_led_pinmux_request(int pwmn, char *title)
+{
+ int rc = 0;
+
+ /* PIN_FUN1 is PWM for these pins */
+ rc = stmp3xxx_request_pin(PWM_PINID(pwmn), PIN_FUN1, title);
+ if (rc)
+ return rc;
+
+ stmp3xxx_pin_voltage(PWM_PINID(pwmn), PIN_3_3V, title);
+ /* pwm0-3 support 4,8,12mA; pwm4 supports 8,16,24mA
+ * I'm forcing 8 here since it's the only one in common
+ */
+ stmp3xxx_pin_strength(PWM_PINID(pwmn), PIN_8MA, title);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pwm_led_pinmux_request);
+
+void pwm_led_pinmux_free(int pwmn, char *title)
+{
+ stmp3xxx_pin_voltage(PWM_PINID(pwmn), PIN_4MA, title);
+ stmp3xxx_pin_strength(PWM_PINID(pwmn), PIN_1_8V, title);
+
+ stmp3xxx_release_pin(PWM_PINID(pwmn), title);
+}
+EXPORT_SYMBOL_GPL(pwm_led_pinmux_free);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Drew Benedetti <drewb@embeddedalley.com>");
diff --git a/arch/arm/mach-stmp3xxx/stmp37xx.c b/arch/arm/mach-stmp3xxx/stmp37xx.c
new file mode 100644
index 000000000000..e273283478be
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/stmp37xx.c
@@ -0,0 +1,368 @@
+/*
+ * Freescale STMP37XX platform support
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/sysdev.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/spi/spi.h>
+#include <linux/fsl_devices.h>
+
+#include <mach/hardware.h>
+#include <asm/dma.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/map.h>
+#include <asm/mach/time.h>
+
+#include <mach/stmp3xxx.h>
+#include <mach/gpmi.h>
+#include <mach/ocram-malloc.h>
+
+#include <mach/platform.h>
+#include <mach/system.h>
+#include <mach/lcdif.h>
+
+#include <mach/regs-icoll.h>
+#include <mach/regs-rtc.h>
+#include <mach/regs-apbh.h>
+#include <mach/regs-apbx.h>
+#include <mach/regs-power.h>
+#include <mach/regs-usbctrl.h>
+#include <mach/regs-usbphy.h>
+#include <mach/regs-digctl.h>
+#include <mach/regs-ssp.h>
+
+#include "common.h"
+
+/*
+ * IRQ handling
+ */
+static void stmp37xx_ack_irq(unsigned int irq)
+{
+ /* Disable IRQ */
+ HW_ICOLL_PRIORITYn_CLR(irq / 4, 0x04 << ((irq % 4) * 8));
+
+ /* ACK current interrupt */
+ HW_ICOLL_LEVELACK_WR(1);
+
+ /* Barrier */
+ (void) HW_ICOLL_STAT_RD();
+}
+
+static void stmp37xx_mask_irq(unsigned int irq)
+{
+ /* IRQ disable */
+ HW_ICOLL_PRIORITYn_CLR(irq / 4, 0x04 << ((irq % 4) * 8));
+}
+
+static void stmp37xx_unmask_irq(unsigned int irq)
+{
+ /* IRQ enable */
+ HW_ICOLL_PRIORITYn_SET(irq / 4, 0x04 << ((irq % 4) * 8));
+}
+
+static struct irq_chip stmp37xx_chip = {
+ .ack = stmp37xx_ack_irq,
+ .mask = stmp37xx_mask_irq,
+ .unmask = stmp37xx_unmask_irq,
+};
+
+void __init stmp37xx_init_irq(void)
+{
+ stmp3xxx_init_irq(&stmp37xx_chip, NULL, NULL);
+}
+
+/*
+ * DMA interrupt handling
+ */
+void stmp3xxx_arch_dma_enable_interrupt(int channel)
+{
+ int dmabus = channel / 16;
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ HW_APBH_CTRL1_SET(1 << (8 + (channel % 16)));
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ HW_APBX_CTRL1_SET(1 << (8 + (channel % 16)));
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_enable_interrupt);
+
+void stmp3xxx_arch_dma_clear_interrupt(int channel)
+{
+ int dmabus = channel / 16;
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ HW_APBH_CTRL1_CLR(1 << (channel % 16));
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ HW_APBX_CTRL1_CLR(1 << (channel % 16));
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_clear_interrupt);
+
+int stmp3xxx_arch_dma_is_interrupt(int channel)
+{
+ int r = 0;
+
+ int dmabus = channel / 16;
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ r = HW_APBH_CTRL1_RD() & (1 << (channel % 16));
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ r = HW_APBX_CTRL1_RD() & (1 << (channel % 16));
+ break;
+ }
+ return r;
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_is_interrupt);
+
+void stmp3xxx_arch_dma_reset_channel(int channel)
+{
+ int dmabus = channel / 16;
+ unsigned chbit = 1 << (channel % 16);
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ /* Reset channel and wait for it to complete */
+ HW_APBH_CTRL0_SET(chbit << BP_APBH_CTRL0_RESET_CHANNEL);
+ while (HW_APBH_CTRL0_RD() &
+ (chbit << BP_APBH_CTRL0_RESET_CHANNEL))
+ continue;
+ break;
+
+ case STMP3XXX_BUS_APBX:
+ /* Reset channel and wait for it to complete */
+ HW_APBX_CTRL0_SET(chbit << BP_APBX_CTRL0_RESET_CHANNEL);
+ while (HW_APBX_CTRL0_RD() &
+ (chbit << BP_APBX_CTRL0_RESET_CHANNEL))
+ continue;
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_reset_channel);
+
+void stmp3xxx_arch_dma_freeze(int channel)
+{
+ int dmabus = channel / 16;
+ unsigned chbit = 1 << (channel % 16);
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ HW_APBH_CTRL0_SET(1<<chbit);
+ break;
+ case STMP3XXX_BUS_APBX:
+ HW_APBX_CTRL0_SET(1<<chbit);
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_freeze);
+
+void stmp3xxx_arch_dma_unfreeze(int channel)
+{
+ int dmabus = channel / 16;
+ unsigned chbit = 1 << (channel % 16);
+
+ switch (dmabus) {
+ case STMP3XXX_BUS_APBH:
+ HW_APBH_CTRL0_CLR(1<<chbit);
+ break;
+ case STMP3XXX_BUS_APBX:
+ HW_APBX_CTRL0_CLR(1<<chbit);
+ break;
+ }
+}
+EXPORT_SYMBOL(stmp3xxx_arch_dma_unfreeze);
+
+/*
+ * STMP37xx dedicated pin groups
+ */
+static struct pin_desc gpmi_pins_desc[] = {
+ { PINID_GPMI_A0, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_A1, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_A2, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_RESETN, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_IRQ, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D00, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D01, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D02, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D03, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D04, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D05, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D06, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_D07, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_RDY0, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_RDY2, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_RDY3, PIN_FUN1, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_GPMI_WRN, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_GPMI_RDN, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_EMI_CE3N, PIN_FUN2, PIN_4MA, PIN_3_3V, 0 },
+ { PINID_EMI_CE2N, PIN_FUN2, PIN_4MA, PIN_3_3V, 0 },
+};
+
+struct pin_group gpmi_pins = {
+ .pins = gpmi_pins_desc,
+ .nr_pins = ARRAY_SIZE(gpmi_pins_desc),
+};
+
+static struct pin_desc lcd_hx8238a_desc[] = {
+ { PINID_LCD_D00, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D01, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D02, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D03, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D04, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D05, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D06, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D07, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D08, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D09, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D10, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D11, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D12, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D13, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D14, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_D15, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_RESET, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_RS, PIN_FUN1, PIN_12MA, PIN_3_3V, 0 },
+ { PINID_LCD_BUSY, PIN_FUN2, PIN_12MA, PIN_3_3V, 0 },
+};
+
+struct pin_group stmp37xx_lcd_pins = {
+ .pins = lcd_hx8238a_desc,
+ .nr_pins = ARRAY_SIZE(lcd_hx8238a_desc),
+};
+
+unsigned stmp37xx_lcd_spi_pins[] = {
+ [SPI_MOSI] = PINID_LCD_WR_RWN,
+ [SPI_SCLK] = PINID_LCD_RD_E,
+ [SPI_CS] = PINID_LCD_CS
+};
+
+/*
+ * The registers are all very closely mapped, so we might as well map them all
+ * with a single mapping
+ *
+ * Logical Physical
+ * f0000000 80000000 On-chip registers
+ * f1000000 00000000 256k on-chip SRAM
+ */
+
+static struct map_desc stmp37xx_io_desc[] __initdata = {
+ {
+ .virtual = IO_ADDRESS(STMP37XX_REGS_BASE),
+ .pfn = __phys_to_pfn(STMP37XX_REGS_BASE),
+ .length = SZ_1M,
+ .type = MT_DEVICE
+ },
+ {
+ .virtual = STMP3XXX_OCRAM_VA_BASE,
+ .pfn = __phys_to_pfn(STMP37XX_OCRAM_BASE),
+ .length = STMP37XX_OCRAM_SIZE,
+ .type = MT_DEVICE,
+ },
+};
+
+void __init stmp37xx_map_io(void)
+{
+ iotable_init(stmp37xx_io_desc, ARRAY_SIZE(stmp37xx_io_desc));
+}
+
+/*
+ * STMP37xx specific devices
+ */
+
+static u64 common_dmamask = (u32)0xffffffffU;
+#define COMMON_COHERENT_DMAMASK (u32)0xffffffffU
+
+struct pin_desc appuart_pins_0[] = {
+ { PINID_UART2_CTS, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+ { PINID_UART2_RTS, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+ { PINID_UART2_RX, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+ { PINID_UART2_TX, PIN_FUN1, PIN_4MA, PIN_1_8V, 0, },
+};
+
+struct pin_group appuart_pins[] = {
+ [0] = {
+ .pins = appuart_pins_0,
+ .nr_pins = ARRAY_SIZE(appuart_pins_0),
+ },
+};
+
+struct pin_desc dbguart_pins_0[] = {
+ { PINID_PWM0, PIN_FUN3, },
+ { PINID_PWM1, PIN_FUN3, },
+};
+
+struct pin_group dbguart_pins[] = {
+ [0] = {
+ .pins = dbguart_pins_0,
+ .nr_pins = ARRAY_SIZE(dbguart_pins_0),
+ },
+};
+
+/* SPI */
+static struct stmp37xx_spi_platform_data enc_data = {
+ .irq_pin = PINID_GPMI_D01,
+ .hw_init = stmp37xx_spi_enc_init,
+ .hw_release = stmp37xx_spi_enc_release,
+};
+
+static struct spi_board_info spi_board_info[] __initdata = {
+#ifdef CONFIG_ENC28J60
+{
+ .modalias = "enc28j60",
+ .irq = IRQ_RESERVED_59,
+ .max_speed_hz = 20 * 1000 * 1000,
+ .bus_num = 2,
+ .chip_select = 0,
+ .platform_data = &enc_data,
+ },
+#endif
+};
+
+int stmp37xx_add_devices(void)
+{
+ if (ARRAY_SIZE(spi_board_info))
+ spi_register_board_info(spi_board_info,
+ ARRAY_SIZE(spi_board_info));
+
+ return 0;
+}
+EXPORT_SYMBOL(stmp37xx_add_devices);
diff --git a/arch/arm/mach-stmp3xxx/timer.c b/arch/arm/mach-stmp3xxx/timer.c
new file mode 100644
index 000000000000..07c0ccaefbc6
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/timer.c
@@ -0,0 +1,188 @@
+/*
+ * System timer for Freescale STMP37XX/STMP378X
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+#include <linux/irq.h>
+
+#include <mach/hardware.h>
+#include <asm/system.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+
+#include <mach/regs-timrot.h>
+
+#include "common.h"
+
+static irqreturn_t
+stmp3xxx_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *c = dev_id;
+
+ if (HW_TIMROT_TIMCTRLn_RD(0) & (1<<15)) {
+ HW_TIMROT_TIMCTRLn_CLR(0, (1<<15));
+ c->event_handler(c);
+ } else if (HW_TIMROT_TIMCTRLn_RD(1) & (1<<15)) {
+ HW_TIMROT_TIMCTRLn_CLR(1, (1<<15));
+ HW_TIMROT_TIMCTRLn_CLR(1, BM_TIMROT_TIMCTRLn_IRQ_EN);
+ HW_TIMROT_TIMCOUNTn_WR(1, 0xFFFF);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static cycle_t stmp3xxx_clock_read(void)
+{
+ return ~((HW_TIMROT_TIMCOUNTn_RD(1) & 0xFFFF0000) >> 16);
+}
+
+static int
+stmp3xxx_timrot_set_next_event(unsigned long delta,
+ struct clock_event_device *dev)
+{
+ HW_TIMROT_TIMCOUNTn_WR(0, delta); /* reload */
+ return 0;
+}
+
+static void
+stmp3xxx_timrot_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *dev)
+{
+}
+
+static struct clock_event_device ckevt_timrot = {
+ .name = "timrot",
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .shift = 32,
+ .cpumask = CPU_MASK_CPU0,
+ .set_next_event = stmp3xxx_timrot_set_next_event,
+ .set_mode = stmp3xxx_timrot_set_mode,
+};
+
+static struct clocksource cksrc_stmp3xxx = {
+ .name = "cksrc_stmp3xxx",
+ .rating = 250,
+ .read = stmp3xxx_clock_read,
+ .mask = CLOCKSOURCE_MASK(16),
+ .shift = 10,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static struct irqaction stmp3xxx_timer_irq = {
+ .name = "stmp3xxx_timer",
+ .flags = IRQF_DISABLED | IRQF_TIMER,
+ .handler = stmp3xxx_timer_interrupt,
+ .dev_id = &ckevt_timrot,
+};
+
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ */
+static inline void __init stmp3xxx_time_init(unsigned long reload,
+ unsigned int ctrl)
+{
+ cksrc_stmp3xxx.mult = clocksource_hz2mult(CLOCK_TICK_RATE,
+ cksrc_stmp3xxx.shift);
+ ckevt_timrot.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC,
+ ckevt_timrot.shift);
+ ckevt_timrot.min_delta_ns = clockevent_delta2ns(2, &ckevt_timrot);
+ ckevt_timrot.max_delta_ns = clockevent_delta2ns(0xFFF, &ckevt_timrot);
+
+ HW_TIMROT_ROTCTRL_CLR(BM_TIMROT_ROTCTRL_SFTRST |
+ BM_TIMROT_ROTCTRL_CLKGATE);
+ HW_TIMROT_TIMCOUNTn_WR(0, 0);
+ HW_TIMROT_TIMCOUNTn_WR(1, 0);
+
+ HW_TIMROT_TIMCTRLn_WR(0,
+ (BF_TIMROT_TIMCTRLn_SELECT(8) | /* 32 kHz */
+ BF_TIMROT_TIMCTRLn_PRESCALE(0) |
+ BM_TIMROT_TIMCTRLn_RELOAD |
+ BM_TIMROT_TIMCTRLn_UPDATE |
+ BM_TIMROT_TIMCTRLn_IRQ_EN));
+ HW_TIMROT_TIMCTRLn_WR(1,
+ (BF_TIMROT_TIMCTRLn_SELECT(8) | /* 32 kHz */
+ BF_TIMROT_TIMCTRLn_PRESCALE(0) |
+ BM_TIMROT_TIMCTRLn_RELOAD |
+ BM_TIMROT_TIMCTRLn_UPDATE));
+
+ HW_TIMROT_TIMCOUNTn_WR(0, CLOCK_TICK_RATE / HZ - 1);
+ HW_TIMROT_TIMCOUNTn_WR(1, 0xFFFF); /* reload */
+
+ setup_irq(IRQ_TIMER0, &stmp3xxx_timer_irq);
+
+ clocksource_register(&cksrc_stmp3xxx);
+ clockevents_register_device(&ckevt_timrot);
+}
+
+static void __init stmp3xxx_init_timer(void)
+{
+ stmp3xxx_time_init(1000 / HZ - 1, 0);
+}
+
+#ifdef CONFIG_PM
+
+void stmp3xxx_suspend_timer(void)
+{
+ HW_TIMROT_TIMCTRLn_CLR(0, BM_TIMROT_TIMCTRLn_IRQ_EN);
+ HW_TIMROT_TIMCTRLn_CLR(0, (1<<15));
+ HW_TIMROT_ROTCTRL_SET(BM_TIMROT_ROTCTRL_CLKGATE);
+}
+
+void stmp3xxx_resume_timer(void)
+{
+ HW_TIMROT_ROTCTRL_CLR(BM_TIMROT_ROTCTRL_SFTRST |
+ BM_TIMROT_ROTCTRL_CLKGATE);
+
+
+ HW_TIMROT_TIMCTRLn_WR(0,
+ (BF_TIMROT_TIMCTRLn_SELECT(8) | /* 32 kHz */
+ BF_TIMROT_TIMCTRLn_PRESCALE(0) |
+ BM_TIMROT_TIMCTRLn_UPDATE |
+ BM_TIMROT_TIMCTRLn_IRQ_EN));
+ HW_TIMROT_TIMCTRLn_WR(1,
+ (BF_TIMROT_TIMCTRLn_SELECT(8) | /* 32 kHz */
+ BF_TIMROT_TIMCTRLn_PRESCALE(0) |
+ BM_TIMROT_TIMCTRLn_RELOAD |
+ BM_TIMROT_TIMCTRLn_UPDATE));
+
+ HW_TIMROT_TIMCOUNTn_WR(0, CLOCK_TICK_RATE / HZ - 1);
+ HW_TIMROT_TIMCOUNTn_WR(1, 0xFFFF); /* reload */
+}
+
+#else
+
+#define stmp3xxx_suspend_timer NULL
+#define stmp3xxx_resume_timer NULL
+
+#endif /* CONFIG_PM */
+
+struct sys_timer stmp3xxx_timer = {
+ .init = stmp3xxx_init_timer,
+ .suspend = stmp3xxx_suspend_timer,
+ .resume = stmp3xxx_resume_timer,
+};
diff --git a/arch/arm/mach-stmp3xxx/tvenc.c b/arch/arm/mach-stmp3xxx/tvenc.c
new file mode 100644
index 000000000000..eb35eb12169b
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/tvenc.c
@@ -0,0 +1,272 @@
+/*
+ * Freescale STMP378X dvi panel initialization
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/* #define DEBUG */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <mach/regs-lcdif.h>
+#include <mach/regs-lradc.h>
+#include <mach/regs-pinctrl.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-pwm.h>
+#include <mach/regs-apbh.h>
+#include <mach/gpio.h>
+#include <mach/pins.h>
+#include <mach/lcdif.h>
+#include <mach/cpu.h>
+#include <mach/stmp3xxx.h>
+
+#include "pinmux.h"
+
+#include <mach/regs-tvenc.h>
+
+enum {
+ TVENC_MODE_OFF = 0,
+ TVENC_MODE_NTSC,
+ TVENC_MODE_PAL,
+};
+
+/* NTSC 720x480 mode */
+#define NTSC_X_RES 720
+#define NTSC_Y_RES 480
+#define NTSC_H_BLANKING 262
+#define NTSC_V_LINES 525
+
+/* PAL 720x576 mode */
+#define PAL_X_RES 720
+#define PAL_Y_RES 576
+#define PAL_H_BLANKING 274
+#define PAL_V_LINES 625
+
+/* frame size */
+#define DVI_H_BLANKING(m) (m == TVENC_MODE_NTSC ? \
+ NTSC_H_BLANKING : PAL_H_BLANKING)
+#define DVI_V_LINES(m) (m == TVENC_MODE_NTSC ? \
+ NTSC_V_LINES : PAL_V_LINES)
+#define DVI_H_ACTIVE(m) (m == TVENC_MODE_NTSC ? NTSC_X_RES : PAL_X_RES)
+#define DVI_V_ACTIVE(m) (m == TVENC_MODE_NTSC ? NTSC_Y_RES : PAL_Y_RES)
+/* fileds range */
+#define DVI_F1_START(m) 1
+#define DVI_F1_END(m) (DVI_V_LINES(m) / 2)
+#define DVI_F2_START(m) (DVI_F1_END(m) + 1)
+#define DVI_F2_END(m) DVI_V_LINES(m)
+/* blanking range */
+#define DVI_V1_BLANK_START(m) DVI_F1_END(m)
+#define DVI_V1_BLANK_END(m) (DVI_V1_BLANK_START(m) + \
+ (DVI_V_LINES(m) - DVI_V_ACTIVE(m)) / 2)
+#define DVI_V2_BLANK_START(m) DVI_F2_END(m)
+#define DVI_V2_BLANK_END(m) ((DVI_V2_BLANK_START(m) + \
+ (DVI_V_LINES(m) - DVI_V_ACTIVE(m)) / 2 - 1) % \
+ DVI_V_LINES(m))
+
+static struct clk *lcd_clk;
+static struct clk *clk_ref_vid;
+static struct clk *clk_tv108M_ng;
+static struct clk *clk_tv27M;
+
+static int tvenc_mode;
+
+static void init_tvenc_hw(int mode)
+{
+ /* Reset module */
+ HW_TVENC_CTRL_SET(BM_TVENC_CTRL_SFTRST);
+ udelay(10);
+
+ /* Take module out of reset */
+ HW_TVENC_CTRL_CLR(BM_TVENC_CTRL_SFTRST | BM_TVENC_CTRL_CLKGATE);
+
+ if (mode == TVENC_MODE_NTSC) {
+ /* Config NTSC-M mode, 8-bit Y/C in, SYNC out */
+ HW_TVENC_CONFIG_CLR(BM_TVENC_CONFIG_SYNC_MODE |
+ BM_TVENC_CONFIG_PAL_SHAPE |
+ BM_TVENC_CONFIG_YGAIN_SEL |
+ BM_TVENC_CONFIG_CGAIN);
+ HW_TVENC_CONFIG_SET(BM_TVENC_CONFIG_FSYNC_PHS |
+ BF_TVENC_CONFIG_SYNC_MODE(0x4));
+
+ /* 859 pixels/line for NTSC */
+ HW_TVENC_SYNCOFFSET_WR(857);
+
+ HW_TVENC_COLORSUB0_WR(0x21F07C1F);
+ HW_TVENC_COLORBURST_CLR(BM_TVENC_COLORBURST_NBA |
+ BM_TVENC_COLORBURST_PBA);
+ HW_TVENC_COLORBURST_SET(BF_TVENC_COLORBURST_NBA(0xc8) |
+ BF_TVENC_COLORBURST_PBA(0));
+ } else if (mode == TVENC_MODE_PAL) {
+ /* Config PAL-B mode, 8-bit Y/C in, SYNC out */
+ HW_TVENC_CONFIG_CLR(BM_TVENC_CONFIG_SYNC_MODE |
+ BM_TVENC_CONFIG_ENCD_MODE |
+ BM_TVENC_CONFIG_YGAIN_SEL |
+ BM_TVENC_CONFIG_CGAIN |
+ BM_TVENC_CONFIG_FSYNC_PHS);
+ HW_TVENC_CONFIG_SET(BM_TVENC_CONFIG_PAL_SHAPE |
+ BF_TVENC_CONFIG_YGAIN_SEL(1) |
+ BF_TVENC_CONFIG_CGAIN(1) |
+ BF_TVENC_CONFIG_ENCD_MODE(0x1) |
+ BF_TVENC_CONFIG_SYNC_MODE(0x4));
+
+ /* 863 pixels/line for PAL */
+ HW_TVENC_SYNCOFFSET_WR(863);
+
+ HW_TVENC_COLORSUB0_WR(0x2A098ACB);
+ HW_TVENC_COLORBURST_CLR(BM_TVENC_COLORBURST_NBA |
+ BM_TVENC_COLORBURST_PBA);
+ HW_TVENC_COLORBURST_SET(BF_TVENC_COLORBURST_NBA(0xd6) |
+ BF_TVENC_COLORBURST_PBA(0x2a));
+ }
+
+ /* Power up DAC */
+ HW_TVENC_DACCTRL_WR(BM_TVENC_DACCTRL_GAINDN |
+ BM_TVENC_DACCTRL_GAINUP |
+ BM_TVENC_DACCTRL_PWRUP1 |
+ BM_TVENC_DACCTRL_DUMP_TOVDD1 |
+ BF_TVENC_DACCTRL_RVAL(3));
+
+ /* set all to zero is a requirement for NTSC */
+ HW_TVENC_MACROVISION0_WR(0);
+ HW_TVENC_MACROVISION1_WR(0);
+ HW_TVENC_MACROVISION2_WR(0);
+ HW_TVENC_MACROVISION3_WR(0);
+ HW_TVENC_MACROVISION4_WR(0);
+}
+
+static int init_panel(struct device *dev, dma_addr_t phys, int memsize,
+ struct stmp3xxx_platform_fb_entry *pentry)
+{
+ int ret = 0;
+
+ lcd_clk = clk_get(dev, "lcdif");
+ clk_enable(lcd_clk);
+ clk_set_rate(lcd_clk, 1000000/pentry->cycle_time_ns); /* kHz */
+
+ clk_ref_vid = clk_get(NULL, "ref_vid");
+ clk_tv108M_ng = clk_get(NULL, "tv108M_ng");
+ clk_tv27M = clk_get(NULL, "tv27M");
+ clk_enable(clk_ref_vid);
+ clk_enable(clk_tv108M_ng);
+ clk_enable(clk_tv27M);
+
+ tvenc_mode = pentry->x_res == NTSC_Y_RES ? TVENC_MODE_NTSC : \
+ TVENC_MODE_PAL;
+
+ init_tvenc_hw(tvenc_mode);
+
+ setup_dvi_panel(DVI_H_ACTIVE(tvenc_mode), DVI_V_ACTIVE(tvenc_mode),
+ DVI_H_BLANKING(tvenc_mode), DVI_V_LINES(tvenc_mode),
+ DVI_V1_BLANK_START(tvenc_mode),
+ DVI_V1_BLANK_END(tvenc_mode),
+ DVI_V2_BLANK_START(tvenc_mode),
+ DVI_V2_BLANK_END(tvenc_mode),
+ DVI_F1_START(tvenc_mode), DVI_F1_END(tvenc_mode),
+ DVI_F2_START(tvenc_mode), DVI_F2_END(tvenc_mode));
+
+ ret = stmp3xxx_lcdif_dma_init(dev, phys, memsize, 1);
+
+ return ret;
+}
+
+static void release_panel(struct device *dev,
+ struct stmp3xxx_platform_fb_entry *pentry)
+{
+ release_dvi_panel();
+
+ stmp3xxx_lcdif_dma_release();
+
+ clk_disable(clk_ref_vid);
+ clk_disable(clk_tv108M_ng);
+ clk_disable(clk_tv27M);
+ clk_disable(lcd_clk);
+ clk_put(clk_ref_vid);
+ clk_put(clk_tv108M_ng);
+ clk_put(clk_tv27M);
+ clk_put(lcd_clk);
+}
+
+static int blank_panel(int blank)
+{
+ int ret = 0, count;
+
+ switch (blank) {
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_POWERDOWN:
+ HW_LCDIF_CTRL_CLR(BM_LCDIF_CTRL_BYPASS_COUNT);
+
+ /* Wait until current transfer is complete, max 30ms */
+ for (count = 30000; count > 0; count--) {
+ if (HW_LCDIF_STAT_RD() & BM_LCDIF_STAT_TXFIFO_EMPTY)
+ break;
+ udelay(1);
+ }
+ break;
+
+ case FB_BLANK_UNBLANK:
+ HW_LCDIF_CTRL_SET(BM_LCDIF_CTRL_BYPASS_COUNT);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static struct stmp3xxx_platform_fb_entry ntsc_fb_entry = {
+ .name = "tvenc_ntsc",
+ /* x/y swapped */
+ .x_res = NTSC_Y_RES,
+ .y_res = NTSC_X_RES,
+ .bpp = 32,
+ /* the pix_clk should be near 27Mhz for proper syncronization */
+ .cycle_time_ns = 74,
+ .lcd_type = STMP3XXX_LCD_PANEL_DVI,
+ .init_panel = init_panel,
+ .release_panel = release_panel,
+ .blank_panel = blank_panel,
+ .run_panel = stmp3xxx_lcdif_run,
+ .pan_display = stmp3xxx_lcdif_pan_display,
+};
+
+static struct stmp3xxx_platform_fb_entry pal_fb_entry = {
+ .name = "tvenc_pal",
+ /* x/y swapped */
+ .x_res = PAL_Y_RES,
+ .y_res = PAL_X_RES,
+ .bpp = 32,
+ /* the pix_clk should be near 27Mhz for proper syncronization */
+ .cycle_time_ns = 74,
+ .lcd_type = STMP3XXX_LCD_PANEL_DVI,
+ .init_panel = init_panel,
+ .release_panel = release_panel,
+ .blank_panel = blank_panel,
+ .run_panel = stmp3xxx_lcdif_run,
+ .pan_display = stmp3xxx_lcdif_pan_display,
+};
+
+static int __init register_devices(void)
+{
+ stmp3xxx_lcd_register_entry(&ntsc_fb_entry,
+ stmp3xxx_framebuffer.dev.platform_data);
+ stmp3xxx_lcd_register_entry(&pal_fb_entry,
+ stmp3xxx_framebuffer.dev.platform_data);
+
+ return 0;
+}
+subsys_initcall(register_devices);
diff --git a/arch/arm/mach-stmp3xxx/unique-id.c b/arch/arm/mach-stmp3xxx/unique-id.c
new file mode 100644
index 000000000000..207b2287310f
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/unique-id.c
@@ -0,0 +1,199 @@
+/*
+ * Unique ID manipulation sysfs access generic functions
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+
+#include <mach/unique-id.h>
+
+static int unlock;
+static spinlock_t u_lock;
+static const unsigned long UID_AUTOLOCK_TIMEOUT = HZ * 60 * 3;
+static struct timer_list u_timer;
+
+static void uid_timer_autolock(unsigned long param)
+{
+ struct timer_list *tmr = (struct timer_list *)param;
+
+ if (spin_trylock(&u_lock)) {
+ if (unlock)
+ pr_debug("%s: locked down.\n", __func__);
+ unlock = 0;
+ spin_unlock(&u_lock);
+ }
+ mod_timer(tmr, jiffies + UID_AUTOLOCK_TIMEOUT);
+}
+
+static LIST_HEAD(uid_provider_list);
+
+struct uid_provider {
+ struct kobject *kobj;
+ struct list_head list;
+ struct uid_ops *ops;
+ void *context;
+};
+
+static struct uid_provider *uid_provider_find(const char *name);
+
+#define UID_FWD_SYSFS_FILE(var, file, param) \
+ static ssize_t var##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+ { \
+ struct uid_provider *p = \
+ uid_provider_find(kobject_name(kobj)); \
+ ssize_t r; \
+ BUG_ON(p == NULL); \
+ r = (p->ops && p->ops->file##_show) ? \
+ p->ops->file##_show(p->context, buf, param) : 0;\
+ return r; \
+ } \
+ \
+ static ssize_t var##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, const char *buf, \
+ size_t count) \
+ { \
+ struct uid_provider *p = \
+ uid_provider_find(kobject_name(kobj)); \
+ ssize_t r; \
+ int ul; \
+ BUG_ON(p == NULL); \
+ spin_lock(&u_lock); \
+ ul = unlock; \
+ spin_unlock(&u_lock); \
+ if (ul) \
+ r = (p->ops && p->ops->file##_store) ? \
+ p->ops->file##_store(p->context, buf, count, param) \
+ : count; \
+ else \
+ r = -EACCES; \
+ return r; \
+ }
+
+struct kobject *uid_kobj;
+
+#define UID_ATTR(_name, _varname) \
+ static struct kobj_attribute _varname##_attr = \
+ __ATTR(_name, 0644, _varname##_show, _varname##_store)
+
+UID_FWD_SYSFS_FILE(id, id, 1);
+UID_FWD_SYSFS_FILE(id_bin, id, 0);
+UID_ATTR(id, id);
+UID_ATTR(id.bin, id_bin);
+
+static struct attribute *uid_attrs[] = {
+ &id_attr.attr,
+ &id_bin_attr.attr,
+ NULL
+};
+
+static struct attribute_group uid_attr_group = {
+ .attrs = uid_attrs,
+};
+
+struct kobject *uid_provider_init(const char *name,
+ struct uid_ops *ops, void *context)
+{
+ struct uid_provider *new;
+ int err;
+
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!new) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ new->kobj = kobject_create_and_add(name, uid_kobj);
+ if (!new->kobj) {
+ err = -ENOMEM;
+ goto out;
+ }
+ new->ops = ops;
+ new->context = context;
+
+ err = sysfs_create_group(new->kobj, &uid_attr_group);
+ if (err)
+ goto out2;
+
+ list_add_tail(&new->list, &uid_provider_list);
+ return new->kobj;
+out2:
+ kobject_del(new->kobj);
+out:
+ kfree(new);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(uid_provider_init);
+
+static struct uid_provider *uid_provider_find(const char *name)
+{
+ struct uid_provider *p;
+
+ list_for_each_entry(p, &uid_provider_list, list) {
+ if (strcmp(kobject_name(p->kobj), name) == 0)
+ return p;
+ }
+ return NULL;
+}
+
+void uid_provider_remove(const char *name)
+{
+ struct uid_provider *p;
+
+ p = uid_provider_find(name);
+ if (!p)
+ return;
+ kobject_del(p->kobj);
+ list_del(&p->list);
+ kfree(p);
+}
+EXPORT_SYMBOL_GPL(uid_provider_remove);
+
+static int uid_sysfs_init(void)
+{
+ int error;
+
+ uid_kobj = kobject_create_and_add("uid", NULL);
+ if (!uid_kobj) {
+ error = -ENOMEM;
+ goto out1;
+ }
+
+ spin_lock_init(&u_lock);
+ setup_timer(&u_timer, uid_timer_autolock, (unsigned long)&u_timer);
+
+ /* try to lock each 3 minutes */
+ mod_timer(&u_timer, jiffies + UID_AUTOLOCK_TIMEOUT);
+ return 0;
+
+out1:
+ printk(KERN_ERR"%s failed, error %d.", __func__, error);
+ return error;
+}
+
+module_param(unlock, int, 0600)
+core_initcall(uid_sysfs_init);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
+MODULE_DESCRIPTION("Unique ID simple framework");
+
diff --git a/arch/arm/mach-stmp3xxx/usb.h b/arch/arm/mach-stmp3xxx/usb.h
new file mode 100644
index 000000000000..9b0e05b8ee98
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/usb.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+extern int usbotg_init(struct platform_device *pdev);
+extern void usbotg_uninit(struct fsl_usb2_platform_data *pdata);
+extern struct platform_device *host_pdev_register(struct resource *res,
+ int n_res, struct fsl_usb2_platform_data *config);
+
+static int usbotg_init_ext(struct platform_device *pdev);
+static void usbotg_uninit_ext(struct fsl_usb2_platform_data *pdata);
+static void usb_host_phy_resume(void);
+
+/*
+ * Determine which platform_data struct to use for the DR controller,
+ * based on which transceiver is configured.
+ * PDATA is a pointer to it.
+ */
+#if defined(CONFIG_UTMI_MXC)
+static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config;
+#define PDATA (&dr_utmi_config)
+#endif
+
+
+/*
+ * Used to set pdata->operating_mode before registering the platform_device.
+ * If OTG is configured, the controller operates in OTG mode,
+ * otherwise it's either host or device.
+ */
+#ifdef CONFIG_USB_OTG
+#define DR_UDC_MODE FSL_USB2_DR_OTG
+#define DR_HOST_MODE FSL_USB2_DR_OTG
+#else
+#define DR_UDC_MODE FSL_USB2_DR_DEVICE
+#define DR_HOST_MODE FSL_USB2_DR_HOST
+#endif
+
+
+#ifdef CONFIG_USB_EHCI_ARC_OTG
+static inline void dr_register_host(struct resource *r, int rs)
+{
+ PDATA->operating_mode = DR_HOST_MODE;
+ host_pdev_register(r, rs, PDATA);
+}
+#else
+static inline void dr_register_host(struct resource *r, int rs)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_GADGET_ARC
+static struct platform_device dr_udc_device;
+
+static inline void dr_register_udc(void)
+{
+ PDATA->operating_mode = DR_UDC_MODE;
+ dr_udc_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_udc_device))
+ printk(KERN_ERR "usb: can't register DR gadget\n");
+ else
+ printk(KERN_INFO "usb: DR gadget (%s) registered\n",
+ PDATA->transceiver);
+}
+#else
+static inline void dr_register_udc(void)
+{
+}
+#endif
+
+#ifdef CONFIG_USB_OTG
+static struct platform_device dr_otg_device;
+
+/*
+ * set the proper operating_mode and
+ * platform_data pointer, then register the
+ * device.
+ */
+static inline void dr_register_otg(void)
+{
+ PDATA->operating_mode = FSL_USB2_DR_OTG;
+ dr_otg_device.dev.platform_data = PDATA;
+
+ if (platform_device_register(&dr_otg_device))
+ printk(KERN_ERR "usb: can't register otg device\n");
+ else
+ printk(KERN_INFO "usb: DR OTG registered\n");
+}
+#else
+static inline void dr_register_otg(void)
+{
+}
+#endif
diff --git a/arch/arm/mach-stmp3xxx/usb_common.c b/arch/arm/mach-stmp3xxx/usb_common.c
new file mode 100644
index 000000000000..d571299a1dcc
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/usb_common.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * otg_{get,set}_transceiver() are from arm/plat-omap/usb.c.
+ * which is Copyright (C) 2004 Texas Instruments, Inc.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ *@defgroup USB ARC OTG USB Driver
+ */
+
+/*!
+ * @file usb_common.c
+ *
+ * @brief platform related part of usb driver.
+ * @ingroup USB
+ */
+
+/*!
+ *Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <linux/regulator/consumer.h>
+#include <mach/arc_otg.h>
+#include <asm/mach-types.h>
+#include <mach/regs-power.h>
+#include <mach/regs-digctl.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-usbctrl.h>
+#include <mach/regs-usbphy.h>
+#include "pinmux.h"
+#include "common.h"
+
+#define MXC_NUMBER_USB_TRANSCEIVER 6
+struct fsl_xcvr_ops *g_xc_ops[MXC_NUMBER_USB_TRANSCEIVER] = { NULL };
+
+void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops)
+{
+ int i;
+
+ pr_debug("%s\n", __func__);
+ for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) {
+ if (g_xc_ops[i] == NULL) {
+ g_xc_ops[i] = xcvr_ops;
+ return;
+ }
+ }
+
+ pr_debug("Failed %s\n", __func__);
+}
+EXPORT_SYMBOL(fsl_usb_xcvr_register);
+
+void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops)
+{
+ int i;
+
+ pr_debug("%s\n", __func__);
+ for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) {
+ if (g_xc_ops[i] == xcvr_ops) {
+ g_xc_ops[i] = NULL;
+ return;
+ }
+ }
+
+ pr_debug("Failed %s\n", __func__);
+}
+EXPORT_SYMBOL(fsl_usb_xcvr_unregister);
+
+#if defined(CONFIG_USB_OTG)
+static struct otg_transceiver *xceiv;
+
+/**
+ * otg_get_transceiver - find the (single) OTG transceiver driver
+ *
+ * Returns the transceiver driver, after getting a refcount to it; or
+ * null if there is no such transceiver. The caller is responsible for
+ * releasing that count.
+ */
+struct otg_transceiver *otg_get_transceiver(void)
+{
+ pr_debug("%s xceiv=0x%p\n", __func__, xceiv);
+ if (xceiv)
+ get_device(xceiv->dev);
+ return xceiv;
+}
+EXPORT_SYMBOL(otg_get_transceiver);
+
+int otg_set_transceiver(struct otg_transceiver *x)
+{
+ pr_debug("%s xceiv=0x%p x=0x%p\n", __func__, xceiv, x);
+ if (x == NULL)
+ stmp3xxx_release_pin_group(&usb_mux_pins, "usb");
+ if (xceiv && x)
+ return -EBUSY;
+ xceiv = x;
+ return 0;
+}
+EXPORT_SYMBOL(otg_set_transceiver);
+
+static struct resource *otg_resources;
+
+struct resource *otg_get_resources(void)
+{
+ pr_debug("otg_get_resources\n");
+ return otg_resources;
+}
+EXPORT_SYMBOL(otg_get_resources);
+
+int otg_set_resources(struct resource *resources)
+{
+ stmp3xxx_request_pin_group(&usb_mux_pins, "usb");
+ otg_resources = resources;
+ return 0;
+}
+EXPORT_SYMBOL(otg_set_resources);
+#endif
+
+static struct fsl_xcvr_ops *fsl_usb_get_xcvr(char *name)
+{
+ int i;
+
+ pr_debug("%s\n", __func__);
+ if (name == NULL) {
+ printk(KERN_ERR "get_xcvr(): No tranceiver name\n");
+ return NULL;
+ }
+
+ for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) {
+ if (strcmp(g_xc_ops[i]->name, name) == 0) {
+ return g_xc_ops[i];
+ }
+ }
+ pr_debug("Failed %s\n", __func__);
+ return NULL;
+}
+
+/* The dmamask must be set for EHCI to work */
+static u64 ehci_dmamask = ~(u32) 0;
+
+static int instance_id;
+struct platform_device *host_pdev_register(struct resource *res, int n_res,
+ struct fsl_usb2_platform_data *config)
+{
+ struct platform_device *pdev;
+ int rc;
+ instance_id = 0;
+
+ pr_debug("register host res=0x%p, size=%d\n", res, n_res);
+
+ pdev = platform_device_register_simple("fsl-ehci",
+ instance_id, res, n_res);
+ if (IS_ERR(pdev)) {
+ pr_debug("can't register %s Host, %ld\n",
+ config->name, PTR_ERR(pdev));
+ return NULL;
+ }
+
+ pdev->dev.coherent_dma_mask = 0xffffffff;
+ pdev->dev.dma_mask = &ehci_dmamask;
+
+ /*
+ * platform_device_add_data() makes a copy of
+ * the platform_data passed in. That makes it
+ * impossible to share the same config struct for
+ * all OTG devices (host,gadget,otg). So, just
+ * set the platorm_data pointer ourselves.
+ */
+ rc = platform_device_add_data(pdev, config,
+ sizeof(struct fsl_usb2_platform_data));
+ if (rc) {
+ platform_device_unregister(pdev);
+ return NULL;
+ }
+
+ pr_debug(KERN_INFO "usb: %s host (%s) registered\n", config->name,
+ config->transceiver);
+ pr_debug("pdev=0x%p dev=0x%p resources=0x%p pdata=0x%p\n",
+ pdev, &pdev->dev, pdev->resource, pdev->dev.platform_data);
+
+ instance_id++;
+
+ return pdev;
+}
+
+int usb_phy_enable()
+{
+ u32 tmp;
+ /*
+ * Set these bits so that we can force the OTG bits high
+ * so the ARC core operates properly
+ */
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_CLKGATE);
+ HW_POWER_DEBUG_SET(BM_POWER_DEBUG_VBUSVALIDPIOLOCK |
+ BM_POWER_DEBUG_AVALIDPIOLOCK |
+ BM_POWER_DEBUG_BVALIDPIOLOCK);
+ HW_POWER_STS_SET(BM_POWER_STS_BVALID | BM_POWER_STS_AVALID |
+ BM_POWER_STS_VBUSVALID);
+
+ /* Reset USBPHY module */
+ HW_USBPHY_CTRL_SET(BM_USBPHY_CTRL_SFTRST);
+ udelay(10);
+
+ /* Remove CLKGATE and SFTRST */
+ HW_USBPHY_CTRL_CLR(BM_USBPHY_CTRL_CLKGATE | BM_USBPHY_CTRL_SFTRST);
+
+ /* Turn on the USB clocks */
+ HW_CLKCTRL_PLLCTRL0_SET(BM_CLKCTRL_PLLCTRL0_EN_USB_CLKS);
+ HW_DIGCTL_CTRL_CLR(BM_DIGCTL_CTRL_USB_CLKGATE);
+
+ /* set UTMI xcvr */
+ /* Workaround an IC issue for ehci driver:
+ * when turn off root hub port power, EHCI set
+ * PORTSC reserved bits to be 0, but PTW with 0
+ * means 8 bits tranceiver width, here change
+ * it back to be 16 bits and do PHY diable and
+ * then enable.
+ */
+ tmp = HW_USBCTRL_PORTSC1_RD() & ~PORTSC_PTS_MASK;
+ tmp |= (PORTSC_PTS_UTMI | PORTSC_PTW);
+ HW_USBCTRL_PORTSC1_SET(tmp);
+
+ /* Power up the PHY */
+ HW_USBPHY_PWD_WR(0);
+
+ /*
+ * Set precharge bit to cure overshoot problems at the
+ * start of packets
+ */
+ HW_USBPHY_CTRL_SET(1 /* BM_USBPHY_CTRL_ENHSPRECHARGEXMIT */);
+
+#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE)
+ /* enable disconnect detector */
+ /* enable disconnect detector must be after entry high speed mode*/
+ /*HW_USBPHY_CTRL_SET(BM_USBPHY_CTRL_ENHOSTDISCONDETECT);
+ */
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(usb_phy_enable);
+
+static int otg_used;
+
+int usbotg_init(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ struct fsl_xcvr_ops *xops;
+
+ pr_debug("%s: pdev=0x%p pdata=0x%p\n", __func__, pdev, pdata);
+
+ xops = fsl_usb_get_xcvr(pdata->transceiver);
+ if (!xops) {
+ printk(KERN_ERR "DR transceiver ops missing\n");
+ return -EINVAL;
+ }
+ pdata->xcvr_ops = xops;
+ pdata->xcvr_type = xops->xcvr_type;
+ pdata->pdev = pdev;
+
+ otg_used = 0;
+ if (!otg_used) {
+ u32 tmp;
+ pr_debug("%s: grab pins\n", __func__);
+ if (xops->init)
+ xops->init(xops);
+ usb_phy_enable();
+ }
+
+ otg_used++;
+ pr_debug("%s: success\n", __func__);
+ return 0;
+}
+EXPORT_SYMBOL(usbotg_init);
+
+void usbotg_uninit(struct fsl_usb2_platform_data *pdata)
+{
+ u32 tmp;
+ pr_debug("%s\n", __func__);
+
+ if (pdata->xcvr_ops && pdata->xcvr_ops->uninit)
+ pdata->xcvr_ops->uninit(pdata->xcvr_ops);
+
+ pdata->regs = NULL;
+ otg_used--;
+}
+EXPORT_SYMBOL(usbotg_uninit);
diff --git a/arch/arm/mach-stmp3xxx/usb_dr.c b/arch/arm/mach-stmp3xxx/usb_dr.c
new file mode 100644
index 000000000000..b86095ea5a00
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/usb_dr.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include <mach/regs-usbctrl.h>
+#include <mach/regs-usbphy.h>
+#include "usb.h"
+
+/*
+ * platform data structs
+ * - Which one to use is determined by CONFIG options in usb.h
+ * - operating_mode plugged at run time
+ */
+static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config = {
+ .name = "DR",
+ .platform_init = usbotg_init,
+ .platform_uninit = usbotg_uninit,
+ .phy_mode = FSL_USB2_PHY_UTMI_WIDE,
+ .power_budget = 500, /* 500 mA max power */
+ .platform_resume = usb_host_phy_resume,
+ .transceiver = "utmi",
+};
+
+/*
+ * resources
+ */
+static struct resource otg_resources[] = {
+ [0] = {
+ .start = (u32)REGS_USBCTRL_BASE_PHYS,
+ .end = (u32)(REGS_USBCTRL_BASE_PHYS + SZ_4K),
+ .flags = IORESOURCE_MEM,
+ },
+
+ [1] = {
+ .start = IRQ_USB_CTRL,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 dr_udc_dmamask = ~(u32) 0;
+static void dr_udc_release(struct device *dev)
+{
+}
+
+/*
+ * platform device structs
+ * dev.platform_data field plugged at run time
+ */
+static struct platform_device dr_udc_device = {
+ .name = "fsl-usb2-udc",
+ .id = -1,
+ .dev = {
+ .release = dr_udc_release,
+ .dma_mask = &dr_udc_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static u64 dr_otg_dmamask = ~(u32) 0;
+static void dr_otg_release(struct device *dev)
+{}
+
+static struct platform_device __maybe_unused dr_otg_device = {
+ .name = "fsl-usb2-otg",
+ .id = -1,
+ .dev = {
+ .release = dr_otg_release,
+ .dma_mask = &dr_otg_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+static void usbotg_pm_clock(bool on)
+{
+}
+
+static void usb_host_phy_resume()
+{
+ HW_USBPHY_CTRL_CLR(BM_USBPHY_CTRL_ENHOSTDISCONDETECT);
+}
+
+static int __init usb_dr_init(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ dr_register_otg();
+ dr_register_host(otg_resources, ARRAY_SIZE(otg_resources));
+ dr_register_udc();
+
+ return 0;
+}
+
+module_init(usb_dr_init);
diff --git a/arch/arm/mach-stmp3xxx/utmixc.c b/arch/arm/mach-stmp3xxx/utmixc.c
new file mode 100644
index 000000000000..dcab25acf7fd
--- /dev/null
+++ b/arch/arm/mach-stmp3xxx/utmixc.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <linux/pmic_external.h>
+
+#include <mach/hardware.h>
+#include <mach/arc_otg.h>
+#include <asm/mach-types.h>
+
+static struct regulator *usbotg_regux;
+
+static void usb_utmi_init(struct fsl_xcvr_ops *this)
+{
+}
+
+static void usb_utmi_uninit(struct fsl_xcvr_ops *this)
+{
+}
+
+/*!
+ * set vbus power
+ *
+ * @param view viewport register
+ * @param on power on or off
+ */
+static void set_power(struct fsl_xcvr_ops *this,
+ struct fsl_usb2_platform_data *pdata, int on)
+{
+ struct device *dev = &pdata->pdev->dev;
+
+ pr_debug("real %s(on=%d) pdata=0x%p\n", __func__, on, pdata);
+}
+
+static struct fsl_xcvr_ops utmi_ops = {
+ .name = "utmi",
+ .xcvr_type = PORTSC_PTS_UTMI,
+ .init = usb_utmi_init,
+ .uninit = usb_utmi_uninit,
+ .set_vbus_power = set_power,
+};
+
+extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops);
+
+static int __init utmixc_init(void)
+{
+ fsl_usb_xcvr_register(&utmi_ops);
+ return 0;
+}
+
+extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops);
+
+static void __exit utmixc_exit(void)
+{
+ fsl_usb_xcvr_unregister(&utmi_ops);
+}
+
+module_init(utmixc_init);
+module_exit(utmixc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("utmi xcvr driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index ab5f7a21350b..d6deaf61325c 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -187,7 +187,8 @@ config CPU_ARM926T
ARCH_AT91SAM9260 || ARCH_AT91SAM9261 || \
ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || \
ARCH_AT91SAM9G20 || ARCH_AT91CAP9 || \
- ARCH_NS9XXX || ARCH_DAVINCI || ARCH_MX2
+ ARCH_NS9XXX || ARCH_DAVINCI || ARCH_MX2 || \
+ ARCH_MXC || ARCH_STMP3XXX
default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || \
ARCH_OMAP730 || ARCH_OMAP16XX || \
ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412 || \
@@ -400,7 +401,7 @@ config CPU_FEROCEON_OLD_ID
# ARMv6
config CPU_V6
bool "Support ARM V6 processor"
- depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP2 || ARCH_MX3 || ARCH_MSM || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176
+ depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP2 || ARCH_MXC || ARCH_MSM || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176
default y if ARCH_MX3
default y if ARCH_MSM
select CPU_32v6
@@ -428,7 +429,7 @@ config CPU_32v6K
# ARMv7
config CPU_V7
bool "Support ARM V7 processor"
- depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP3
+ depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP3 || ARCH_MXC
select CPU_32v6K
select CPU_32v7
select CPU_ABRT_EV7
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index b480f1d3591f..45563b8ecf1a 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -24,8 +24,16 @@
#include <asm/hardware/cache-l2x0.h>
#define CACHE_LINE_SIZE 32
+#ifdef CONFIG_OPROFILE_ARM11_EVTMON
+#include <linux/module.h>
+#define L2_ENABLE_BIT 0x1
+#define L2_EVTBUS_BIT 0x100000
+#define L2_CTL_REG (l2x0_base + L2X0_CTRL)
+#define L2_AUX_REG (l2x0_base + L2X0_AUX_CTRL)
+#endif
static void __iomem *l2x0_base;
+static unsigned long l2x0_aux;
static DEFINE_SPINLOCK(l2x0_lock);
static inline void sync_writel(unsigned long val, unsigned long reg,
@@ -53,6 +61,13 @@ static inline void l2x0_inv_all(void)
cache_sync();
}
+static void l2x0_flush_all(void)
+{
+ /* clean and invalidate all ways */
+ sync_writel(0xff, L2X0_CLEAN_INV_WAY, 0xff);
+ cache_sync();
+}
+
static void l2x0_inv_range(unsigned long start, unsigned long end)
{
unsigned long addr;
@@ -93,6 +108,49 @@ static void l2x0_flush_range(unsigned long start, unsigned long end)
cache_sync();
}
+#ifdef CONFIG_OPROFILE_ARM11_EVTMON
+/*!
+ * Enable the EVTBUS to monitor L2 cache events
+ */
+void l2x0_evtbus_enable(void)
+{
+ unsigned int flags;
+
+ local_irq_save(flags);
+ /* If L2 cache is enabled then disable L2 cache, enable L2 evtbus,
+ re-enable L2 cache */
+ if ((readl(L2_CTL_REG) & L2_ENABLE_BIT) != 0) {
+ writel(0, L2_CTL_REG);
+ writel((readl(L2_AUX_REG)| L2_EVTBUS_BIT), L2_AUX_REG);
+ writel(L2_ENABLE_BIT, L2_CTL_REG);
+ } else {
+ writel((readl(L2_AUX_REG)| L2_EVTBUS_BIT), L2_AUX_REG);
+ }
+ local_irq_restore(flags);
+}
+
+/*!
+ * Disable the EVTBUS
+ */
+void l2x0_evtbus_disable(void)
+{
+ unsigned int flags;
+
+ local_irq_save(flags);
+ /* If L2 cache is enabled then disable L2 cache, disable L2 evtbus,
+ re-enable L2 cache */
+ if ((readl(L2_CTL_REG) & L2_ENABLE_BIT) != 0) {
+ writel(0, L2_CTL_REG);
+ writel((readl(L2_AUX_REG)& ~L2_EVTBUS_BIT), L2_AUX_REG);
+ writel(L2_ENABLE_BIT, L2_CTL_REG);
+ } else {
+ writel((readl(L2_AUX_REG)& ~L2_EVTBUS_BIT), L2_AUX_REG);
+ }
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(l2x0_evtbus_enable);
+EXPORT_SYMBOL(l2x0_evtbus_disable);
+#endif
void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
{
__u32 aux;
@@ -105,6 +163,7 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
aux = readl(l2x0_base + L2X0_AUX_CTRL);
aux &= aux_mask;
aux |= aux_val;
+ l2x0_aux = aux;
writel(aux, l2x0_base + L2X0_AUX_CTRL);
l2x0_inv_all();
@@ -115,6 +174,29 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
outer_cache.inv_range = l2x0_inv_range;
outer_cache.clean_range = l2x0_clean_range;
outer_cache.flush_range = l2x0_flush_range;
+ outer_cache.flush_all = l2x0_flush_all;
printk(KERN_INFO "L2X0 cache controller enabled\n");
}
+EXPORT_SYMBOL(outer_cache);
+
+void l2x0_disable(void)
+{
+ if (readl(l2x0_base + L2X0_CTRL)
+ && !(readl(l2x0_base + L2X0_DEBUG_CTRL) & 0x2)) {
+ l2x0_flush_all();
+ writel(0, l2x0_base + L2X0_CTRL);
+ l2x0_flush_all();
+ }
+}
+
+void l2x0_enable(void)
+{
+ if (!readl(l2x0_base + L2X0_CTRL)) {
+ writel(l2x0_aux, l2x0_base + L2X0_AUX_CTRL);
+ l2x0_inv_all();
+ /* enable L2X0 */
+ writel(1, l2x0_base + L2X0_CTRL);
+ }
+}
+
diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S
index 2c6c2a7c05a0..8364f6c51a8d 100644
--- a/arch/arm/mm/cache-v6.S
+++ b/arch/arm/mm/cache-v6.S
@@ -20,6 +20,39 @@
#define D_CACHE_LINE_SIZE 32
#define BTB_FLUSH_SIZE 8
+#ifdef CONFIG_ARM_ERRATA_411920
+/*
+ * Invalidate the entire I cache (this code is a workaround for the ARM1136
+ * Errata 411920 - Invalidate Instruction Cache operation can fail. This
+ * Errata is present in 1136, 1156 and 1176. It does not affect the MPCore
+ *
+ * Registers:
+ * r0 - set to 0
+ * r1 - corrupted
+ */
+ENTRY(v6_icache_inval_all)
+ mov r0, #0
+ mrs r1, cpsr
+ cpsid ifa @ disable interrupts
+ mcr p15, 0, r0, c7, c5, 0 @ invalidate entire I-cache
+ mcr p15, 0, r0, c7, c5, 0 @ invalidate entire I-cache
+ mcr p15, 0, r0, c7, c5, 0 @ invalidate entire I-cache
+ mcr p15, 0, r0, c7, c5, 0 @ invalidate entire I-cache
+ msr cpsr_cx, r1 @ restore interrupts
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ mov pc, lr
+#endif
+
/*
* v6_flush_cache_all()
*
@@ -31,8 +64,12 @@ ENTRY(v6_flush_kern_cache_all)
mov r0, #0
#ifdef HARVARD_CACHE
mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate
+#ifndef CONFIG_ARM_ERRATA_411920
mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate
#else
+ b v6_icache_inval_all
+#endif
+#else
mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate
#endif
mov pc, lr
@@ -103,8 +140,12 @@ ENTRY(v6_coherent_user_range)
mov r0, #0
#ifdef HARVARD_CACHE
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
+#ifndef CONFIG_ARM_ERRATA_411920
mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate
#else
+ b v6_icache_inval_all
+#endif
+#else
mcr p15, 0, r0, c7, c5, 6 @ invalidate BTB
#endif
mov pc, lr
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index 0fa9bf388f0b..73b886e81e29 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -32,10 +32,14 @@ static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr)
asm( "mcrr p15, 0, %1, %0, c14\n"
" mcr p15, 0, %2, c7, c10, 4\n"
+#ifndef CONFIG_ARM_ERRATA_411920
" mcr p15, 0, %2, c7, c5, 0\n"
+#else
+ " bl v6_icache_inval_all\n"
+#endif
:
: "r" (to), "r" (to + PAGE_SIZE - L1_CACHE_BYTES), "r" (zero)
- : "cc");
+ : "r0", "r1", "lr");
}
void flush_cache_mm(struct mm_struct *mm)
@@ -48,11 +52,15 @@ void flush_cache_mm(struct mm_struct *mm)
if (cache_is_vipt_aliasing()) {
asm( "mcr p15, 0, %0, c7, c14, 0\n"
+ " mcr p15, 0, %0, c7, c10, 4\n"
+#ifndef CONFIG_ARM_ERRATA_411920
" mcr p15, 0, %0, c7, c5, 0\n"
- " mcr p15, 0, %0, c7, c10, 4"
+#else
+ " bl v6_icache_inval_all\n"
+#endif
:
: "r" (0)
- : "cc");
+ : "r0", "r1", "lr", "cc");
}
}
@@ -67,11 +75,15 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned
if (cache_is_vipt_aliasing()) {
asm( "mcr p15, 0, %0, c7, c14, 0\n"
+ " mcr p15, 0, %0, c7, c10, 4\n"
+#ifndef CONFIG_ARM_ERRATA_411920
" mcr p15, 0, %0, c7, c5, 0\n"
- " mcr p15, 0, %0, c7, c10, 4"
+#else
+ " bl v6_icache_inval_all\n"
+#endif
:
: "r" (0)
- : "cc");
+ : "r0", "r1", "lr", "cc");
}
}
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index 54b1f721dec8..2d9211930dc1 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S
@@ -119,8 +119,8 @@
.long 0x00 @ unused
.long PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_DEV_CACHED
.long PTE_EXT_TEX(2) @ L_PTE_MT_DEV_NONSHARED
- .long 0x00 @ unused
- .long 0x00 @ unused
+ .long PTE_EXT_TEX(4) | PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_OUTER_UNCACHED
+ .long PTE_EXT_TEX(6) | PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_OUTER_WRITETHRU
.long 0x00 @ unused
.endm
diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S
index 294943b85973..1c4488b73633 100644
--- a/arch/arm/mm/proc-v6.S
+++ b/arch/arm/mm/proc-v6.S
@@ -170,6 +170,22 @@ __v6_setup:
mrc p15, 0, r0, c1, c0, 0 @ read control register
bic r0, r0, r5 @ clear bits them
orr r0, r0, r6 @ set them
+#ifdef CONFIG_ARM_ERRATA_364296
+ /* Workaround for the 364296 ARM1136 r0pX errata (possible cache data
+ * corruption with hit-under-miss enabled). The conditional code below
+ * (setting the undocumented bit 31 in the auxiliary control register
+ * and the FI bit in the control register) disables hit-under-miss
+ * without putting the processor into full low interrupt latency mode.
+ */
+ ldr r6, =0x4107b360 @ id for ARM1136 r0pX
+ mrc p15, 0, r5, c0, c0, 0 @ get processor id
+ bic r5, r5, #0xf @ mask out part bits [3:0]
+ teq r5, r6 @ check for the faulty core
+ mrceq p15, 0, r5, c1, c0, 1 @ load aux control reg
+ orreq r5, r5, #(1 << 31) @ set the undocumented bit 31
+ mcreq p15, 0, r5, c1, c0, 1 @ write aux control reg
+ orreq r0, r0, #(1 << 21) @ low interrupt latency configuration
+#endif
mov pc, lr @ return to head.S:__ret
/*
diff --git a/arch/arm/oprofile/Makefile b/arch/arm/oprofile/Makefile
index 88e31f549f50..18c80fa3ad21 100644
--- a/arch/arm/oprofile/Makefile
+++ b/arch/arm/oprofile/Makefile
@@ -9,6 +9,7 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
oprofile-y := $(DRIVER_OBJS) common.o backtrace.o
oprofile-$(CONFIG_CPU_XSCALE) += op_model_xscale.o
oprofile-$(CONFIG_OPROFILE_ARM11_CORE) += op_model_arm11_core.o
+oprofile-$(CONFIG_OPROFILE_ARM11_EVTMON) += op_model_arm11_evtmon.o
oprofile-$(CONFIG_OPROFILE_ARMV6) += op_model_v6.o
oprofile-$(CONFIG_OPROFILE_MPCORE) += op_model_mpcore.o
oprofile-$(CONFIG_OPROFILE_ARMV7) += op_model_v7.o
diff --git a/arch/arm/oprofile/evtmon_regs.h b/arch/arm/oprofile/evtmon_regs.h
new file mode 100644
index 000000000000..def582bd17df
--- /dev/null
+++ b/arch/arm/oprofile/evtmon_regs.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * @file evtmon_regs.h
+ *
+ * @brief EVTMON Register definitions
+ *
+ * @ingroup MXC_Oprofile
+ */
+#ifndef _EVTMON_REGS_INCLUDED_
+#define _EVTMON_REGS_INCLUDED_
+
+#define MXC_CRM_AP_BASE (IO_ADDRESS(CRM_AP_BASE_ADDR))
+#define ECT_CTI_BASE (IO_ADDRESS(ECT_CTIO_BASE_ADDR))
+#define CLKCTR_BASE_ADDR (IO_ADDRESS(CLKCTL_BASE_ADDR))
+#define L2CC_BASE_ADDRESS (IO_ADDRESS(L2CC_BASE_ADDR))
+#define L2EM_BASE_ADDRESS (IO_ADDRESS(EVTMON_BASE_ADDR))
+
+/* L2 INT settings */
+#define L2EM_ENABLE_OVERFLOWINT 0x1
+#define L2EM_ENABLE_CNTINCRINT 0x3
+#define L2EM_ENABLE_MASK 0x1
+#define L2EM_INT_EDGE 0x2
+#define L2EM_INT_HIGH 0x4
+#define L2EM_INT_CLK_CYCLES (0x0 << 3)
+
+/* Reg definitions for EVTMON */
+#define L2EM_CTRL (L2EM_BASE_ADDRESS + 0x0)
+#define L2EM_STAT (L2EM_BASE_ADDRESS + 0x4)
+#define L2EM_CC(nr) (L2EM_BASE_ADDRESS + 0x8 +(4*nr))
+#define L2EM_CNT(nr) (L2EM_BASE_ADDRESS + 0x20 +(4*nr))
+
+/* Reg definitions for CLK_CTL */
+#define CLKCTL_SET_CTRL (CLKCTR_BASE_ADDR + 0x04)
+
+/* Reg definitions for ECT */
+#define ECT_CTI_CONTROL (ECT_CTI_BASE + 0x0)
+#define ECT_CTI_LOCK (ECT_CTI_BASE + 0x8)
+#define ECT_CTI_INEN(nr) (ECT_CTI_BASE + 0x20 + (4*nr))
+#define ECT_CTI_OUTEN(nr) (ECT_CTI_BASE + 0xA0 + (4*nr))
+#define ECT_CTI_INTACK (ECT_CTI_BASE + 0x10)
+#define ECT_CTI_TRIGOUTSTATUS (ECT_CTI_BASE + 0x134)
+
+#define ENABLE_L2CACHE 0x1
+#define EVTMON_ENABLE 0x001 /* Enable EVTMON */
+#define EVT_UNUSED 0x100
+#define MAX_PMUCOUNTERS 3
+
+#ifdef CONFIG_OPROFILE_ARM11_EVTMON
+#define ECT_WORKAROUND
+#endif
+
+#ifdef ECT_WORKAROUND
+#define ENABLE_CTI_CLOCK 0x00000020
+#define UNLOCK_ECT_CODE 0x0ACCE550
+#define ECT_CTI_CHAN_2 0x4
+#define ECT_CTI_CHAN_3 0x8
+#define ECT_CTI_TRIGIN_1 1
+#define ECT_CTI_TRIGIN_7 7
+#define ECT_CTI_TRIGOUT_2 2
+#define ECT_CTI_TRIGOUT_6 6
+#define ENABLE_ECT 0x1
+#define ACK_TRIG_OUT_2 0x4
+#define EM_SET_INT L2EM_ENABLE_CNTINCRINT
+#define EVENT_OVERFLOW_INT MXC_INT_ECT
+#else
+#define EVENT_OVERFLOW_INT ARM11_PMU_IRQ
+#define EM_SET_INT L2EM_ENABLE_OVERFLOWINT
+#endif
+
+int l2em_configure_counter(int nr, int type);
+void write_l2counter(int nr, u32 val);
+#endif
diff --git a/arch/arm/oprofile/op_model_arm11.c b/arch/arm/oprofile/op_model_arm11.c
new file mode 100644
index 000000000000..573269e36a48
--- /dev/null
+++ b/arch/arm/oprofile/op_model_arm11.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup MXC_Oprofile ARM11 Driver for Oprofile
+ */
+
+/*!
+ * @file op_model_arm11.c
+ *
+ *Based on the op_model_xscale.c driver by author Zwane Mwaikambo
+ *
+ * @ingroup MXC_Oprofile
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/oprofile.h>
+#include <linux/interrupt.h>
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "op_counter.h"
+#include "op_arm_model.h"
+#include "evtmon_regs.h"
+
+/*!
+ * defines used in ARM11 performance unit
+ */
+#define PMU_ENABLE 0x001 /* Enable counters */
+#define EVTMON_ENABLE 0x001 /* Enable EVTMON */
+#define PMN_RESET 0x002 /* Reset event counters */
+#define CCNT_RESET 0x004 /* Reset clock counter */
+#define PMU_RESET (CCNT_RESET | PMN_RESET)
+#define PMU_CNT64 0x008 /* Make CCNT count every 64th cycle */
+
+#define PMU_FLAG_CR0 0x080
+#define PMU_FLAG_CR1 0x100
+#define PMU_FLAG_CC 0x200
+
+/*!
+ * Different types of events that can be counted by the ARM11 PMU
+ * as used by Oprofile userspace.
+ */
+#define EVT_ICACHE_MISS 0x00
+#define EVT_STALL_INSTR 0x01
+#define EVT_DATA_STALL 0x02
+#define EVT_ITLB_MISS 0x03
+#define EVT_DTLB_MISS 0x04
+#define EVT_BRANCH 0x05
+#define EVT_BRANCH_MISS 0x06
+#define EVT_INSTRUCTION 0x07
+#define EVT_DCACHE_FULL_STALL_CONTIG 0x09
+#define EVT_DCACHE_ACCESS 0x0A
+#define EVT_DCACHE_MISS 0x0B
+#define EVT_DCACE_WRITE_BACK 0x0C
+#define EVT_PC_CHANGED 0x0D
+#define EVT_TLB_MISS 0x0F
+#define EVT_BCU_REQUEST 0x10
+#define EVT_BCU_FULL 0x11
+#define EVT_BCU_DRAIN 0x12
+#define EVT_ETMEXTOT0 0x20
+#define EVT_ETMEXTOT1 0x21
+/* EVT_CCNT is not hardware defined */
+#define EVT_CCNT 0xFE
+#define EVT_INCREMENT 0xFF
+#define EVT_UNUSED 0x100
+
+#define ECT_WORKAROUND
+
+#define COUNTER_MSB 0x80000000
+#define ENABLE_L2CACHE 0x1
+#define ENABLE_EVTBUS 0x100000
+#define PMU_OVERFLOWBIT_MASK 0x700
+#define VAR_NUM 0x0
+#define REV_NUM 0x2
+
+#ifdef ECT_WORKAROUND
+#define ENABLE_CTI_CLOCK 0x00000020
+#define UNLOCK_ECT_CODE 0x0ACCE550
+#define ECT_CTI_CHAN_2 0x4
+#define ECT_CTI_CHAN_3 0x8
+#define ECT_CTI_TRIGIN_1 1
+#define ECT_CTI_TRIGIN_7 7
+#define ECT_CTI_TRIGOUT_2 2
+#define ECT_CTI_TRIGOUT_6 6
+#define ENABLE_ECT 0x1
+#define ACK_TRIG_OUT_2 0x4
+#define EM_SET_INT L2EM_ENABLE_CNTINCRINT
+#define EVENT_OVERFLOW_INT INT_ECT
+#else
+#define EVENT_OVERFLOW_INT ARM11_PMU_IRQ
+#define EM_SET_INT L2EM_ENABLE_OVERFLOWINT
+#endif
+
+struct pmu_counter {
+ volatile unsigned long ovf;
+ unsigned long reset_counter;
+};
+
+static unsigned int r0p2_or_older_core;
+enum { CCNT, PMN0, PMN1, MAX_PMUCOUNTERS };
+enum { EMC0 = MAX_PMUCOUNTERS, EMC1, EMC2, EMC3, MAX_L2COUNTERS };
+
+static struct pmu_counter results[MAX_L2COUNTERS];
+
+struct pmu_type {
+ int id;
+ char *name;
+ int num_counters;
+ unsigned int int_enable;
+ unsigned int cnt_ovf[MAX_L2COUNTERS];
+ unsigned int int_mask[MAX_L2COUNTERS];
+};
+
+static struct pmu_type pmu_parms[] = {
+ {
+ .id = 0,
+ .name = "arm/arm11",
+ .num_counters = MAX_L2COUNTERS,
+ .int_mask = {[PMN0] = 0x10,[PMN1] = 0x20,
+ [CCNT] = 0x40,[EMC0] = 0x800,[EMC1] = 0x400,[EMC2] =
+ 0x200,[EMC3] = 0x100},
+ .cnt_ovf = {[CCNT] = 0x400,[PMN0] = 0x100,
+ [PMN1] = 0x200,[EMC0] = 0x1,[EMC1] = 0x2,[EMC2] =
+ 0x4,[EMC3] = 0x8},
+ },
+};
+
+static struct pmu_type *pmu;
+
+extern void l2_evtbus_enable(void);
+extern void l2_evtbus_disable(void);
+
+/*!
+ * function is used to write the EVTMON counter configuration register.
+ */
+static int l2em_configure_counter(int nr, int type)
+{
+ /* Configure the counter event source */
+ __raw_writel(((type << 2) & 0x7c), L2EM_CC(nr));
+ if (type)
+ __raw_writel((__raw_readl(L2EM_CC(nr)) | EM_SET_INT),
+ L2EM_CC(nr));
+
+ return 0;
+}
+
+/*!
+ * function is used to write the EVTMON counters
+ */
+static void write_l2counter(int nr, u32 val)
+{
+ __raw_writel(val, L2EM_CNT(nr));
+}
+
+/*!
+ * function is used to write the control register for the ARM11 performance counters
+ */
+static void write_pmnc(u32 val)
+{
+ pr_debug("PMC value written is %#08x\n", val);
+ __asm__ __volatile__("mcr p15, 0, %0, c15, c12, 0"::"r"(val));
+}
+
+/*!
+ * function is used to read the control register for the ARM11 performance counters
+ */
+static u32 read_pmnc(void)
+{
+ u32 val;
+ pr_debug("In function %s\n", __FUNCTION__);
+ __asm__ __volatile__("mrc p15, 0, %0, c15, c12, 0":"=r"(val));
+ pr_debug("PMC value read is %#08x\n", val);
+ return val;
+}
+
+/*!
+ * function is used to read the ARM11 performance counters
+ */
+static u32 read_counter(int counter)
+{
+ u32 val = 0;
+ pr_debug("In function %s\n", __FUNCTION__);
+
+ switch (counter) {
+ case CCNT:
+ __asm__ __volatile__("mrc p15, 0, %0, c15, c12, 1":"=r"(val));
+ break;
+ case PMN0:
+ __asm__ __volatile__("mrc p15, 0, %0, c15, c12, 2":"=r"(val));
+ break;
+ case PMN1:
+ __asm__ __volatile__("mrc p15, 0, %0, c15, c12, 3":"=r"(val));
+ break;
+ }
+
+ pr_debug("counter %d value read is %#08x\n", counter, val);
+ return val;
+}
+
+/*!
+ * function is used to write to the ARM11 performance counters
+ */
+static void write_counter(int counter, u32 val)
+{
+ pr_debug("counter %d value written is %#08x\n", counter, val);
+
+ switch (counter) {
+ case CCNT:
+ __asm__ __volatile__("mcr p15, 0, %0, c15, c12, 1": :"r"(val));
+ break;
+ case PMN0:
+ __asm__ __volatile__("mcr p15, 0, %0, c15, c12, 2": :"r"(val));
+ break;
+ case PMN1:
+ __asm__ __volatile__("mcr p15, 0, %0, c15, c12, 3": :"r"(val));
+ break;
+ }
+}
+
+/*!
+ * function is used to check the status of the ARM11 performance counters
+ */
+static int arm11_setup_ctrs(void)
+{
+ u32 pmnc = 0;
+ int i;
+
+ for (i = CCNT; i < MAX_L2COUNTERS; i++) {
+ if (counter_config[i].enabled)
+ continue;
+ counter_config[i].event = EVT_UNUSED;
+ }
+
+ if (counter_config[PMN0].enabled)
+ pmnc |= (counter_config[PMN0].event << 20);
+
+ if (counter_config[PMN1].enabled)
+ pmnc |= (counter_config[PMN1].event << 12);
+
+ pr_debug("arm11_setup_ctrs: pmnc: %#08x\n", pmnc);
+ write_pmnc(pmnc);
+
+ for (i = CCNT; i < MAX_L2COUNTERS; i++) {
+ if (counter_config[i].event == EVT_UNUSED) {
+ counter_config[i].event = 0;
+ pmu->int_enable &= ~pmu->int_mask[i];
+ pr_debug
+ ("arm11_setup_ctrs: The counter event is %d for counter%d\n",
+ counter_config[i].event, i);
+ continue;
+ }
+
+ results[i].reset_counter = counter_config[i].count;
+ if (i < MAX_PMUCOUNTERS)
+ write_counter(i, -(u32) counter_config[i].count);
+ else {
+ write_l2counter(i - EMC0,
+ -(u32) counter_config[i].count);
+ l2em_configure_counter(i - EMC0,
+ counter_config[i].event);
+ }
+ pmu->int_enable |= pmu->int_mask[i];
+ pr_debug
+ ("arm11_setup_ctrs: The values of int mask and enables are %x, %x\n",
+ pmu->int_mask[i], pmu->int_enable);
+
+ pr_debug("arm11_setup_ctrs: counter%d %#08x from %#08lx\n", i,
+ read_counter(i), counter_config[i].count);
+ }
+
+ return 0;
+}
+
+/*!
+ * function is the interrupt service handler for the ARM11 performance counters
+ */
+static irqreturn_t arm11_pmu_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+ int i;
+ u32 pmnc, emcs;
+
+ /* Disable L2_EVTMON */
+ emcs = __raw_readl(L2EM_STAT);
+ __raw_writel((__raw_readl(L2EM_CTRL) & ~EVTMON_ENABLE), L2EM_CTRL);
+
+ /* Disable ARM11 PMU while retaining interrupts and overflow bits */
+ pmnc = read_pmnc();
+ pmnc &= ~(PMU_ENABLE | PMU_OVERFLOWBIT_MASK);
+ write_pmnc(pmnc);
+
+ /* Read the overflow flag bits */
+ pmnc = read_pmnc();
+
+#ifdef ECT_WORKAROUND
+ for (i = CCNT; i < MAX_PMUCOUNTERS; i++) {
+#else
+ for (i = CCNT; i < MAX_L2COUNTERS; i++) {
+#endif
+ /* Process the counters only if respective overflow interrupt is enabled */
+ if (!(pmu->int_mask[i] & pmu->int_enable)) {
+ continue;
+ }
+
+ /* As per ARM11 errata ARM11 cores with revision less than or equal to R0P2
+ * have known bug i.e., missing overflow interrupt for two events(event
+ * no 0x7 and 0x22) due to double increament for cycle. In this case we will
+ * discard the sample as the pc value belongs to different interrupt.
+ */
+ if (r0p2_or_older_core && !(pmnc & pmu->cnt_ovf[i])
+ && !(read_counter(i) & COUNTER_MSB)) {
+ write_counter(i, -(u32) (results[i].reset_counter));
+ }
+
+ /* Check for the overflowed counter by checking set overflow flag bits */
+ if (!(pmnc & pmu->cnt_ovf[i]) && !(emcs & pmu->cnt_ovf[i])) {
+ continue;
+ }
+
+ /* Reload the overflowed counter with preset value and
+ * add the sample for respective event.
+ */
+ pr_debug("arm11_pmu_interrupt: writing to file\n");
+ if (i < MAX_PMUCOUNTERS)
+ write_counter(i, -(u32) results[i].reset_counter);
+ else
+ write_l2counter(i - EMC0,
+ -(u32) counter_config[i].count);
+
+ oprofile_add_sample(regs, i);
+ }
+
+ /* Clear overflow flags */
+ write_pmnc(pmnc);
+
+#ifdef ECT_WORKAROUND
+ /*
+ * If ECTTRIGOUT signal is interrupt it should be acknowledged
+ * until trigger is off.
+ */
+ while (__raw_readl(ECT_CTI_TRIGOUTSTATUS) & ECT_CTI_CHAN_2)
+ __raw_writel(ACK_TRIG_OUT_2, ECT_CTI_INTACK);
+#endif
+
+ /* Re-enable ARM11 PMU */
+ pmnc |= PMU_ENABLE;
+ write_pmnc(pmnc);
+
+ /* Re-enable L2_EVTMON */
+ __raw_writel((__raw_readl(L2EM_CTRL) | L2EM_ENABLE_MASK), L2EM_CTRL);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * function used to start the ARM11 performance counters
+ */
+static void arm11_pmu_stop(void)
+{
+ u32 pmnc = read_pmnc();
+
+ pmnc &= ~PMU_ENABLE;
+ write_pmnc(pmnc);
+ /* Disable the EVTMON */
+ __raw_writel((__raw_readl(L2EM_CTRL) & ~EVTMON_ENABLE), L2EM_CTRL);
+
+ /* Disable the EVTBUS */
+ l2_evtbus_disable();
+
+ free_irq(EVENT_OVERFLOW_INT, results);
+}
+
+/*!
+ * function used to start the ARM11 performance counters
+ */
+static int arm11_pmu_start(void)
+{
+ int ret;
+ u32 pmnc = read_pmnc();
+
+ ret = request_irq(EVENT_OVERFLOW_INT, arm11_pmu_interrupt, SA_INTERRUPT,
+ "ARM11 PMU", (void *)results);
+ pr_debug("requested IRQ\n");
+
+ if (ret < 0) {
+ printk(KERN_ERR
+ "oprofile: unable to request IRQ%d for ARM11 PMU\n",
+ ARM11_PMU_IRQ);
+ return ret;
+ }
+
+ /* Enable the EVTBUS */
+ l2_evtbus_enable();
+
+#ifdef ECT_WORKAROUND
+ __raw_writel(ENABLE_CTI_CLOCK, CLKCTL_SET_CTRL);
+ /* Unlock the AHB Interface */
+ __raw_writel(UNLOCK_ECT_CODE, ECT_CTI_LOCK);
+ /* Trigger to Channel Mapping */
+ __raw_writel(ECT_CTI_CHAN_2, ECT_CTI_INEN(ECT_CTI_TRIGIN_1));
+ /* Channel to triggers mapping */
+ __raw_writel(ECT_CTI_CHAN_2, ECT_CTI_OUTEN(ECT_CTI_TRIGOUT_2));
+ /* Trigger to Channel Mapping */
+ __raw_writel(ECT_CTI_CHAN_3, ECT_CTI_INEN(ECT_CTI_TRIGIN_7));
+ /* Channel to triggers mapping */
+ __raw_writel(ECT_CTI_CHAN_3, ECT_CTI_OUTEN(ECT_CTI_TRIGOUT_6));
+ /* Enable CTI Logic */
+ __raw_writel(ENABLE_ECT, ECT_CTI_CONTROL);
+#endif
+ pmnc |= pmu->int_enable;
+ pmnc |= PMU_ENABLE;
+
+ write_pmnc(pmnc);
+ pr_debug("arm11_pmu_start: pmnc: %#08x mask: %08x\n", pmnc,
+ pmu->int_enable);
+
+ /* Enable EVTMON with Edge triggered interrupt of one Clock Cycle */
+ __raw_writel((__raw_readl(L2EM_CTRL) |
+ (L2EM_INT_EDGE | L2EM_INT_CLK_CYCLES)), L2EM_CTRL);
+ __raw_writel((__raw_readl(L2EM_CTRL) | L2EM_ENABLE_MASK), L2EM_CTRL);
+
+ return 0;
+}
+
+/*!
+ * function detect the ARM11 performance counters
+ */
+static int arm11_detect_pmu(void)
+{
+ int ret = 0;
+ u32 id, rev;
+
+ id = (read_cpuid(CPUID_ID) >> 0x10) & 0xF;
+
+ switch (id) {
+ case 7:
+ pmu = &pmu_parms[0];
+ rev = read_cpuid(CPUID_ID);
+ /* Check if the ARM11 core is less than or equal to R0P2 */
+ if ((((rev >> 0x14) & 0xF) == VAR_NUM)
+ && (((rev & 0xF) <= REV_NUM))) {
+ r0p2_or_older_core = 1;
+ }
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+
+ if (!ret) {
+ op_arm_spec.name = pmu->name;
+ op_arm_spec.num_counters = pmu->num_counters;
+ pr_debug("arm11_detect_pmu: detected %s PMU\n", pmu->name);
+ }
+
+ return ret;
+}
+
+struct op_arm_model_spec op_arm_spec = {
+ .init = arm11_detect_pmu,
+ .setup_ctrs = arm11_setup_ctrs,
+ .start = arm11_pmu_start,
+ .stop = arm11_pmu_stop,
+};
diff --git a/arch/arm/oprofile/op_model_arm11_core.c b/arch/arm/oprofile/op_model_arm11_core.c
index ad80752cb9fb..86b7d86bb721 100644
--- a/arch/arm/oprofile/op_model_arm11_core.c
+++ b/arch/arm/oprofile/op_model_arm11_core.c
@@ -13,7 +13,12 @@
#include "op_counter.h"
#include "op_arm_model.h"
#include "op_model_arm11_core.h"
+#include "evtmon_regs.h"
+#define NEED_OPROFILE_CCNT_FIX
+#ifdef NEED_OPROFILE_CCNT_FIX /* Workaround to correctly map from user space counters to kernel space counters */
+struct op_counter_config tmp;
+#endif
/*
* ARM11 PMU support
*/
@@ -63,6 +68,12 @@ int arm11_setup_pmu(void)
arm11_write_pmnc(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT |
PMCR_C | PMCR_P);
+#ifdef NEED_OPROFILE_CCNT_FIX /* Workaround to correctly map from user space counters to kernel space counters */
+ tmp = counter_config[PMN0];
+ counter_config[PMN0] = counter_config[PMN1];
+ counter_config[PMN1] = counter_config[CCNT];
+ counter_config[CCNT] = tmp;
+#endif
for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) {
unsigned long event;
@@ -119,16 +130,49 @@ static irqreturn_t arm11_pmu_interrupt(int irq, void *arg)
unsigned int cnt;
u32 pmnc;
+#ifdef ECT_WORKAROUND
+ /* Disable L2_EVTMON */
+ __raw_writel((__raw_readl(L2EM_CTRL) & ~EVTMON_ENABLE), L2EM_CTRL);
+
+ /* Disable ARM11 PMU while retaining interrupts and overflow bits */
+ pmnc = arm11_read_pmnc();
+ pmnc &= ~(PMU_ENABLE | PMU_OVERFLOWBIT_MASK);
+ arm11_write_pmnc(pmnc);
+
+ while (__raw_readl(ECT_CTI_TRIGOUTSTATUS) & ECT_CTI_CHAN_2)
+ __raw_writel(ACK_TRIG_OUT_2, ECT_CTI_INTACK);
+
+ pmnc = arm11_read_pmnc();
+
+ /* Re-enable ARM11 PMU */
+ pmnc |= PMU_ENABLE;
+ arm11_write_pmnc(pmnc);
+
+ /* Re-enable L2_EVTMON */
+ __raw_writel((__raw_readl(L2EM_CTRL) | L2EM_ENABLE_MASK), L2EM_CTRL);
+#else
pmnc = arm11_read_pmnc();
+#endif
for (cnt = PMN0; cnt <= CCNT; cnt++) {
if ((pmnc & (PMCR_OFL_PMN0 << cnt)) && (pmnc & (PMCR_IEN_PMN0 << cnt))) {
arm11_reset_counter(cnt);
+#ifdef NEED_OPROFILE_CCNT_FIX /* Workaround to correctly map from user space counters to kernel space counters */
+ if (cnt == PMN0)
+ oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), PMN1));
+ else if (cnt == PMN1)
+ oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), CCNT));
+ else if (cnt == CCNT)
+ oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), PMN0));
+#else
oprofile_add_sample(regs, CPU_COUNTER(smp_processor_id(), cnt));
+#endif
}
}
+#ifndef ECT_WORKAROUND
/* Clear counter flag(s) */
arm11_write_pmnc(pmnc);
+#endif
return IRQ_HANDLED;
}
diff --git a/arch/arm/oprofile/op_model_arm11_core.h b/arch/arm/oprofile/op_model_arm11_core.h
index 6f8538e5a960..b59d15107d8d 100644
--- a/arch/arm/oprofile/op_model_arm11_core.h
+++ b/arch/arm/oprofile/op_model_arm11_core.h
@@ -35,6 +35,8 @@
#define CCNT 2
#define CPU_COUNTER(cpu, counter) ((cpu) * 3 + (counter))
+#define PMU_ENABLE 0x001 /* Enable counters */
+#define PMU_OVERFLOWBIT_MASK 0x700
int arm11_setup_pmu(void);
int arm11_start_pmu(void);
@@ -42,4 +44,15 @@ int arm11_stop_pmu(void);
int arm11_request_interrupts(int *, int);
void arm11_release_interrupts(int *, int);
+#ifdef CONFIG_OPROFILE_ARM11_EVTMON
+extern int arm11_evtmon_setup_ctrs(void);
+extern void arm11_evtmon_stop(void);
+extern int arm11_evtmon_start(void);
+extern int arm11_evtmon_detect(void);
+#else
+#define arm11_evtmon_setup_ctrs() do {} while(0)
+#define arm11_evtmon_stop() do {} while(0)
+#define arm11_evtmon_start() do {} while(0)
+#define arm11_evtmon_detect() do {} while(0)
+#endif
#endif
diff --git a/arch/arm/oprofile/op_model_arm11_evtmon.c b/arch/arm/oprofile/op_model_arm11_evtmon.c
new file mode 100644
index 000000000000..5ac3fa1f074a
--- /dev/null
+++ b/arch/arm/oprofile/op_model_arm11_evtmon.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup MXC_Oprofile ARM11 EVTMON Driver for Oprofile
+ */
+
+/*!
+ * @file op_model_arm11_evtmon.c
+ *
+ *Based on the op_model_xscale.c driver by author Zwane Mwaikambo
+ *
+ * @ingroup MXC_Oprofile
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/oprofile.h>
+#include <linux/interrupt.h>
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "op_counter.h"
+#include "op_arm_model.h"
+#include "evtmon_regs.h"
+
+struct pmu_counter {
+ volatile unsigned long ovf;
+ unsigned long reset_counter;
+};
+
+enum { EMC0 = MAX_PMUCOUNTERS, EMC1, EMC2, EMC3, MAX_L2COUNTERS };
+
+static struct pmu_counter results[MAX_L2COUNTERS];
+
+struct pmu_type {
+ int id;
+ char *name;
+ int num_counters;
+ unsigned int int_enable;
+ unsigned int cnt_ovf[MAX_L2COUNTERS];
+ unsigned int int_mask[MAX_L2COUNTERS];
+};
+
+static struct pmu_type pmu_parms[] = {
+ {
+ .id = 0,
+ .int_mask = {[EMC0] = 0x800,[EMC1] = 0x400,[EMC2] =
+ 0x200,[EMC3] = 0x100},
+ .cnt_ovf = {[EMC0] = 0x1,[EMC1] = 0x2,[EMC2] = 0x4,[EMC3] = 0x8},
+ },
+};
+
+static struct pmu_type *pmu;
+
+extern void l2x0_evtbus_enable(void);
+extern void l2x0_evtbus_disable(void);
+
+/*!
+ * function is used to write the EVTMON counter configuration register.
+ */
+int l2em_configure_counter(int nr, int type)
+{
+ /* Configure the counter event source */
+ __raw_writel(((type << 2) & 0x7c), L2EM_CC(nr));
+ if (type)
+ __raw_writel((__raw_readl(L2EM_CC(nr)) | EM_SET_INT),
+ L2EM_CC(nr));
+
+ return 0;
+}
+
+/*!
+ * function is used to write the EVTMON counters
+ */
+void write_l2counter(int nr, u32 val)
+{
+ __raw_writel(val, L2EM_CNT(nr));
+}
+
+/*!
+ * function is used to check the status of the ARM11 evtmon counters
+ */
+int arm11_evtmon_setup_ctrs(void)
+{
+ int i;
+
+ for (i = EMC0; i < MAX_L2COUNTERS; i++) {
+ if (counter_config[i].enabled)
+ continue;
+ counter_config[i].event = EVT_UNUSED;
+ }
+
+ for (i = EMC0; i < MAX_L2COUNTERS; i++) {
+ if (counter_config[i].event == EVT_UNUSED) {
+ counter_config[i].event = 0;
+ pmu->int_enable &= ~pmu->int_mask[i];
+ pr_debug
+ ("arm11_setup_ctrs: The counter event is %lu for counter%d\n",
+ counter_config[i].event, i);
+ continue;
+ }
+
+ results[i].reset_counter = counter_config[i].count;
+ write_l2counter(i - EMC0, -(u32) counter_config[i].count);
+ l2em_configure_counter(i - EMC0, counter_config[i].event);
+
+ pmu->int_enable |= pmu->int_mask[i];
+ pr_debug
+ ("arm11_setup_ctrs: The values of int mask and enables are %x, %x\n",
+ pmu->int_mask[i], pmu->int_enable);
+
+ }
+
+ return 0;
+}
+
+/*!
+ * function used to start the ARM11 evtmon counters
+ */
+void arm11_evtmon_stop(void)
+{
+
+ /* Disable the EVTMON */
+ __raw_writel((__raw_readl(L2EM_CTRL) & ~EVTMON_ENABLE), L2EM_CTRL);
+
+ /* Disable the EVTBUS */
+ l2x0_evtbus_disable();
+
+}
+
+/*!
+ * function used to start the ARM11 evtmon counters
+ */
+int arm11_evtmon_start(void)
+{
+
+ /* Enable the EVTBUS */
+ l2x0_evtbus_enable();
+
+#ifdef ECT_WORKAROUND
+ __raw_writel(ENABLE_CTI_CLOCK, CLKCTL_SET_CTRL);
+ /* Unlock the AHB Interface */
+ __raw_writel(UNLOCK_ECT_CODE, ECT_CTI_LOCK);
+ /* Trigger to Channel Mapping */
+ __raw_writel(ECT_CTI_CHAN_2, ECT_CTI_INEN(ECT_CTI_TRIGIN_1));
+ /* Channel to triggers mapping */
+ __raw_writel(ECT_CTI_CHAN_2, ECT_CTI_OUTEN(ECT_CTI_TRIGOUT_2));
+ /* Trigger to Channel Mapping */
+ __raw_writel(ECT_CTI_CHAN_3, ECT_CTI_INEN(ECT_CTI_TRIGIN_7));
+ /* Channel to triggers mapping */
+ __raw_writel(ECT_CTI_CHAN_3, ECT_CTI_OUTEN(ECT_CTI_TRIGOUT_6));
+ /* Enable CTI Logic */
+ __raw_writel(ENABLE_ECT, ECT_CTI_CONTROL);
+#endif
+
+ /* Enable EVTMON with Edge triggered interrupt of one Clock Cycle */
+ __raw_writel((__raw_readl(L2EM_CTRL) |
+ (L2EM_INT_EDGE | L2EM_INT_CLK_CYCLES)), L2EM_CTRL);
+ __raw_writel((__raw_readl(L2EM_CTRL) | L2EM_ENABLE_MASK), L2EM_CTRL);
+
+ return 0;
+}
+
+/*!
+ * function detect the ARM11 evtmon counters
+ */
+int arm11_evtmon_detect(void)
+{
+ int ret = 0;
+
+ pmu = &pmu_parms[0];
+ op_armv6_spec.num_counters = MAX_L2COUNTERS;
+
+ return ret;
+}
diff --git a/arch/arm/oprofile/op_model_v6.c b/arch/arm/oprofile/op_model_v6.c
index fe581383d3e2..a8bf2ab6cd0d 100644
--- a/arch/arm/oprofile/op_model_v6.c
+++ b/arch/arm/oprofile/op_model_v6.c
@@ -28,16 +28,21 @@
#include "op_counter.h"
#include "op_arm_model.h"
#include "op_model_arm11_core.h"
+#include "evtmon_regs.h"
static int irqs[] = {
#ifdef CONFIG_ARCH_OMAP2
3,
#endif
+#ifdef CONFIG_ARCH_MXC
+ EVENT_OVERFLOW_INT,
+#endif
};
static void armv6_pmu_stop(void)
{
arm11_stop_pmu();
+ arm11_evtmon_stop();
arm11_release_interrupts(irqs, ARRAY_SIZE(irqs));
}
@@ -46,21 +51,31 @@ static int armv6_pmu_start(void)
int ret;
ret = arm11_request_interrupts(irqs, ARRAY_SIZE(irqs));
- if (ret >= 0)
+ if (ret >= 0){
ret = arm11_start_pmu();
+ arm11_evtmon_start();
+ }
return ret;
}
+static int armv6_setup_pmu(void)
+{
+ arm11_setup_pmu();
+ arm11_evtmon_setup_ctrs();
+ return 0;
+}
+
static int armv6_detect_pmu(void)
{
+ arm11_evtmon_detect();
return 0;
}
struct op_arm_model_spec op_armv6_spec = {
.init = armv6_detect_pmu,
.num_counters = 3,
- .setup_ctrs = arm11_setup_pmu,
+ .setup_ctrs = armv6_setup_pmu,
.start = armv6_pmu_start,
.stop = armv6_pmu_stop,
.name = "arm/armv6",
diff --git a/arch/arm/oprofile/op_model_v7.c b/arch/arm/oprofile/op_model_v7.c
index f20295f14adb..e3bf4fa07e10 100644
--- a/arch/arm/oprofile/op_model_v7.c
+++ b/arch/arm/oprofile/op_model_v7.c
@@ -371,6 +371,9 @@ static int irqs[] = {
#ifdef CONFIG_ARCH_OMAP3
INT_34XX_BENCH_MPU_EMUL,
#endif
+#ifdef CONFIG_ARCH_MXC
+ MXC_INT_PMU,
+#endif
};
static void armv7_pmnc_stop(void)
diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig
index b2a7e3fad117..47b986ab3b18 100644
--- a/arch/arm/plat-mxc/Kconfig
+++ b/arch/arm/plat-mxc/Kconfig
@@ -6,32 +6,159 @@ choice
prompt "MXC/iMX Base Type"
default ARCH_MX3
-config ARCH_MX2
- bool "MX2-based"
- help
- This enables support for systems based on the Freescale i.MX2 family
+config ARCH_MX37
+ bool "MX37-based"
+ help
+ This enables support for systems based on Freescale i.MX37
+
+config ARCH_MX35
+ bool "MX35-based"
+ help
+ This enables support for systems based on Freescale i.MX35
+
+config ARCH_MX51
+ bool "MX51-based"
+ help
+ This enables support for systems based on Freescale i.MX51
config ARCH_MX3
bool "MX3-based"
help
- This enables support for systems based on the Freescale i.MX3 family
+ This enables support for systems based on the Freescale i.MX31 and i.MX32
+
+config ARCH_MX27
+ bool "MX27-based"
+ help
+ This enables support for systems based on the Freescale i.MX27
+
+config ARCH_MX25
+ bool "MX25-based"
+ select MX25_OPTIONSS
+ help
+ This enables support for systems based on the Freescale i.MX25
endchoice
-source "arch/arm/mach-mx2/Kconfig"
+source "arch/arm/mach-mx27/Kconfig"
+
+source "arch/arm/mach-mx25/Kconfig"
+
source "arch/arm/mach-mx3/Kconfig"
+source "arch/arm/mach-mx35/Kconfig"
+
+source "arch/arm/mach-mx37/Kconfig"
+
+source "arch/arm/mach-mx51/Kconfig"
+
endmenu
-config MXC_IRQ_PRIOR
- bool "Use IRQ priority"
+config MXC_TZIC
+ bool
+ depends on ARCH_MXC
+
+config ARCH_HAS_RNGA
+ bool
+ depends on ARCH_MXC
+
+config ARCH_HAS_RNGC
+ bool
+ depends on ARCH_MXC
+
+config MXC_DSP_BRINGUP
+ bool
+ depends on ARCH_MXC
+
+config ARCH_HAS_EVTMON
+ bool
+ depends on ARCH_MXC
+
+config MXC_EMMA
+ bool
+ depends on ARCH_MXC
+
+config MXC_FB_IRAM
+ bool
+ depends on ARCH_MXC
+
+config DMA_ZONE_SIZE
+ int "DMA memory zone size"
+ range 0 64
+ default 24
+ help
+ This is the size in MB for the DMA zone. The DMA zone is used for
+ dedicated memory for large contiguous video buffers
+
+# set iff we need the 1504 transceiver code
+config ISP1504_MXC
+ bool
+ select ISP1504_MXC_OTG if USB_GADGET && USB_EHCI_HCD && USB_OTG
+ default y if USB_EHCI_FSL_1504 || USB_GADGET_FSL_1504
+
+config ISP1504_MXC_OTG
+ tristate
+ help
+ Support for USB OTG pin detect using the ISP1504 transceiver on MXC platforms.
+
+# set iff we need the UTMI transceiver code
+config UTMI_MXC
+ bool
+ select UTMI_MXC_OTG if ARCH_MX25 && USB_GADGET && USB_EHCI_HCD && USB_OTG
+ default y if USB_EHCI_FSL_UTMI || USB_GADGET_FSL_UTMI
+ depends on ARCH_MX25 || ARCH_MX35 || ARCH_MX37 || ARCH_MX51
+
+config UTMI_MXC_OTG
+ tristate
+ help
+ Support for USB OTG pin detect using the UTMI transceiver on MXC platforms.
+
+# set iff we need the 1301 transceiver code
+config ISP1301_MXC
+ bool
+ default y if USB_EHCI_FSL_1301 || USB_GADGET_FSL_1301
+ select I2C_MXC
+
+# set iff we need the mx13783 transceiver code
+config MC13783_MXC
+ bool
+ default y if USB_EHCI_FSL_MC13783 || USB_GADGET_FSL_MC13783
+ select SPI_MXC
+
+choice
+ prompt "Select serial USB transceiver mode"
+ depends on ISP1301_MXC || MC13783_MXC
+ default MXC_USB_SU6
+
+config MXC_USB_SU6
+ bool "Single Ended Unidirectional Mode"
+ help
+ If you say yes to this option, the serial tranceiver operates in SU6 mode.
+ This option will work for either the Freescale MC13783 or Philips ISP1301
+ transceiver.
+
+config MXC_USB_SB3
+ bool "Single Ended Bidirectional Mode"
+ help
+ If you say yes to this option, the serial tranceiver operates in SB3 mode.
+ Not recommended for the Freescale MC13783.
+
+config MXC_USB_DU6
+ bool "Differential Unidirectional Mode"
+ help
+ If you say yes to this option, the serial tranceiver operates in DU6 mode.
+
+config MXC_USB_DB4
+ bool "Differential Bidirectional Mode"
+ help
+ If you say yes to this option, the serial tranceiver operates in DB4 mode.
+
+endchoice
+
+config MXC_PWM
+ tristate "Enable PWM driver"
depends on ARCH_MXC
+ select HAVE_PWM
help
- Select this if you want to use prioritized IRQ handling.
- This feature prevents higher priority ISR to be interrupted
- by lower priority IRQ even IRQF_DISABLED flag is not set.
- This may be useful in embedded applications, where are strong
- requirements for timing.
- Say N here, unless you have a specialized requirement.
+ Enable support for the i.MX PWM controller(s).
endif
diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
index 067556f7c91f..25c4f086654c 100644
--- a/arch/arm/plat-mxc/Makefile
+++ b/arch/arm/plat-mxc/Makefile
@@ -3,6 +3,59 @@
#
# Common support
-obj-y := irq.o clock.o gpio.o time.o devices.o
+obj-y := cpu_common.o gpio.o clock.o wdog.o snoop.o io.o time.o
obj-$(CONFIG_ARCH_MX2) += iomux-mx1-mx2.o dma-mx1-mx2.o
+
+ifneq ($(CONFIG_ARCH_MX27),y)
+obj-y += spba.o sdma/
+endif
+
+ifeq ($(CONFIG_MXC_TZIC),y)
+obj-y += tzic.o
+else
+obj-y += irq.o
+endif
+
+obj-$(CONFIG_ARCH_MX27) += dma_mx2.o usb_common.o
+obj-$(CONFIG_ARCH_MX3) += dptc.o usb_common.o entry-pm.o
+obj-$(CONFIG_ARCH_MX35) += usb_common.o serialxc.o
+obj-$(CONFIG_ARCH_MX37) += usb_common.o utmixc.o dptc.o dvfs_core.o
+obj-$(CONFIG_ARCH_MX51) += usb_common.o utmixc.o dvfs_core.o
+
+# LEDs support
+obj-$(CONFIG_LEDS) += leds.o
+
+# CPU FREQ support
+obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o
+
+# USB support
+obj-$(CONFIG_ISP1504_MXC) += isp1504xc.o
+obj-$(CONFIG_ISP1301_MXC) += isp1301xc.o
+obj-$(CONFIG_MC13783_MXC) += mc13783_xc.o
+
+# obj-$(CONFIG_USB_EHCI_FSL_UTMI) += utmixc.o
+ifneq ($(strip $(CONFIG_USB_EHCI_FSL_UTMI) $(CONFIG_USB_GADGET_FSL_UTMI)),)
+obj-y += utmixc.o
+endif
+
+ifneq ($(CONFIG_USB_EHCI_ARC_H1)$(CONFIG_USB_EHCI_ARC_H2),)
+ifneq ($(CONFIG_ARCH_MX51),y)
+obj-y += serialxc.o
+else
+obj-y += isp1504xc.o
+endif
+endif
+
+ifneq ($(CONFIG_ARCH_MX25)$(CONFIG_USB),)
+obj-y += usb_common.o
+endif
+
+ifeq ($(CONFIG_ARCH_MX25),y)
+ifneq ($(CONFIG_USB_EHCI_ARC_H2),)
+obj-y += serialxc.o
+endif
+endif
+
+obj-$(CONFIG_MXC_PWM) += pwm.o
+
diff --git a/arch/arm/plat-mxc/clock.c b/arch/arm/plat-mxc/clock.c
index 0a38f0b396eb..e9014d0900dd 100644
--- a/arch/arm/plat-mxc/clock.c
+++ b/arch/arm/plat-mxc/clock.c
@@ -4,7 +4,7 @@
* Copyright (C) 2004 - 2005 Nokia corporation
* Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
* Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com>
- * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or
@@ -25,6 +25,7 @@
/* #define DEBUG */
#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/init.h>
@@ -35,13 +36,28 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/semaphore.h>
#include <linux/string.h>
#include <mach/clock.h>
+#if (defined(CONFIG_ARCH_MX51) || defined(CONFIG_ARCH_MX37))
+extern int dvfs_core_is_active;
+extern int lp_high_freq;
+extern int lp_med_freq;
+extern int low_bus_freq_mode;
+extern int high_bus_freq_mode;
+extern int set_high_bus_freq(int high_freq);
+extern int set_low_bus_freq(void);
+extern int low_freq_bus_used(void);
+#else
+int dvfs_core_is_active;
+#endif
+
static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
+static DEFINE_SPINLOCK(clockfw_lock);
/*-------------------------------------------------------------------------
* Standard clock functions defined in include/linux/clk.h
@@ -113,14 +129,16 @@ EXPORT_SYMBOL(clk_get);
static void __clk_disable(struct clk *clk)
{
- if (clk == NULL || IS_ERR(clk))
+ if (clk == NULL || IS_ERR(clk) || !clk->usecount)
return;
- __clk_disable(clk->parent);
- __clk_disable(clk->secondary);
+ if (!(--clk->usecount)) {
+ if (clk->disable)
+ clk->disable(clk);
- if (!(--clk->usecount) && clk->disable)
- clk->disable(clk);
+ __clk_disable(clk->parent);
+ __clk_disable(clk->secondary);
+ }
}
static int __clk_enable(struct clk *clk)
@@ -128,12 +146,13 @@ static int __clk_enable(struct clk *clk)
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
- __clk_enable(clk->parent);
- __clk_enable(clk->secondary);
-
- if (clk->usecount++ == 0 && clk->enable)
- clk->enable(clk);
+ if (clk->usecount++ == 0) {
+ __clk_enable(clk->parent);
+ __clk_enable(clk->secondary);
+ if (clk->enable)
+ clk->enable(clk);
+ }
return 0;
}
@@ -142,14 +161,38 @@ static int __clk_enable(struct clk *clk)
*/
int clk_enable(struct clk *clk)
{
+ unsigned long flags;
int ret = 0;
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
- mutex_lock(&clocks_mutex);
+ spin_lock_irqsave(&clockfw_lock, flags);
+
ret = __clk_enable(clk);
- mutex_unlock(&clocks_mutex);
+
+ spin_unlock_irqrestore(&clockfw_lock, flags);
+
+ if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
+ && (clk_get_usecount(clk) == 1)) {
+#if (defined(CONFIG_ARCH_MX51) || defined(CONFIG_ARCH_MX37))
+ if (low_freq_bus_used() && !low_bus_freq_mode)
+ set_low_bus_freq();
+ else {
+ if (!high_bus_freq_mode) {
+ /* Currently at ow or medium set point,
+ * need to set to high setpoint
+ */
+ set_high_bus_freq(0);
+ } else if (high_bus_freq_mode || low_bus_freq_mode) {
+ /* Currently at ow or high set point,
+ * need to set to medium setpoint
+ */
+ set_high_bus_freq(0);
+ }
+ }
+#endif
+ }
return ret;
}
@@ -161,15 +204,60 @@ EXPORT_SYMBOL(clk_enable);
*/
void clk_disable(struct clk *clk)
{
+ unsigned long flags;
+
if (clk == NULL || IS_ERR(clk))
return;
- mutex_lock(&clocks_mutex);
+ spin_lock_irqsave(&clockfw_lock, flags);
+
__clk_disable(clk);
- mutex_unlock(&clocks_mutex);
+
+ spin_unlock_irqrestore(&clockfw_lock, flags);
+
+ if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
+ && (clk_get_usecount(clk) == 0)) {
+#if (defined(CONFIG_ARCH_MX51) || defined(CONFIG_ARCH_MX37))
+ if (low_freq_bus_used() && !low_bus_freq_mode)
+ set_low_bus_freq();
+ else {
+ if (!high_bus_freq_mode) {
+ /* Currently at ow or medium set point,
+ * need to set to high setpoint
+ */
+ set_high_bus_freq(0);
+ } else if (high_bus_freq_mode || low_bus_freq_mode) {
+ /* Currently at ow or high set point,
+ * need to set to medium setpoint
+ */
+ set_high_bus_freq(0);
+ }
+ }
+#endif
+ }
}
+
EXPORT_SYMBOL(clk_disable);
+/*!
+ * @brief Function to get the usage count for the requested clock.
+ *
+ * This function returns the reference count for the clock.
+ *
+ * @param clk Handle to clock to disable.
+ *
+ * @return Returns the usage count for the requested clock.
+ */
+int clk_get_usecount(struct clk *clk)
+{
+ if (clk == NULL || IS_ERR(clk))
+ return 0;
+
+ return clk->usecount;
+}
+
+EXPORT_SYMBOL(clk_get_usecount);
+
/* Retrieve the *current* clock rate. If the clock itself
* does not provide a special calculation routine, ask
* its parent and so on, until one is able to return
@@ -180,10 +268,7 @@ unsigned long clk_get_rate(struct clk *clk)
if (clk == NULL || IS_ERR(clk))
return 0UL;
- if (clk->get_rate)
- return clk->get_rate(clk);
-
- return clk_get_rate(clk->parent);
+ return clk->rate;
}
EXPORT_SYMBOL(clk_get_rate);
@@ -208,19 +293,48 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
}
EXPORT_SYMBOL(clk_round_rate);
+/* Propagate rate to children */
+void propagate_rate(struct clk *tclk)
+{
+ struct clk *clkp;
+
+ if (tclk == NULL || IS_ERR(tclk))
+ return;
+
+ pr_debug("mxc clock: finding children of %s-%d\n", tclk->name,
+ tclk->id);
+ list_for_each_entry(clkp, &clocks, node) {
+ if (likely(clkp->parent != tclk))
+ continue;
+ pr_debug("mxc clock: %s-%d: recalculating rate: old = %lu, ",
+ clkp->name, clkp->id, clkp->rate);
+ if (likely((u32) clkp->recalc))
+ clkp->recalc(clkp);
+ else
+ clkp->rate = tclk->rate;
+ pr_debug("new = %lu\n", clkp->rate);
+ propagate_rate(clkp);
+ }
+}
+
/* Set the clock to the requested clock rate. The rate must
* match a supported rate exactly based on what clk_round_rate returns
*/
int clk_set_rate(struct clk *clk, unsigned long rate)
{
+ unsigned long flags;
int ret = -EINVAL;
if (clk == NULL || IS_ERR(clk) || clk->set_rate == NULL || rate == 0)
return ret;
- mutex_lock(&clocks_mutex);
+ spin_lock_irqsave(&clockfw_lock, flags);
+
ret = clk->set_rate(clk, rate);
- mutex_unlock(&clocks_mutex);
+ if (unlikely((ret == 0) && (clk->flags & RATE_PROPAGATES)))
+ propagate_rate(clk);
+
+ spin_unlock_irqrestore(&clockfw_lock, flags);
return ret;
}
@@ -229,17 +343,35 @@ EXPORT_SYMBOL(clk_set_rate);
/* Set the clock's parent to another clock source */
int clk_set_parent(struct clk *clk, struct clk *parent)
{
+ unsigned long flags;
int ret = -EINVAL;
+ struct clk *prev_parent = clk->parent;
if (clk == NULL || IS_ERR(clk) || parent == NULL ||
IS_ERR(parent) || clk->set_parent == NULL)
return ret;
- mutex_lock(&clocks_mutex);
+ if (clk->usecount != 0) {
+ clk_enable(parent);
+ }
+
+ spin_lock_irqsave(&clockfw_lock, flags);
ret = clk->set_parent(clk, parent);
- if (ret == 0)
+ if (ret == 0) {
clk->parent = parent;
- mutex_unlock(&clocks_mutex);
+ if (clk->recalc) {
+ clk->recalc(clk);
+ } else {
+ clk->rate = parent->rate;
+ }
+ if (unlikely(clk->flags & RATE_PROPAGATES))
+ propagate_rate(clk);
+ }
+ spin_unlock_irqrestore(&clockfw_lock, flags);
+
+ if (clk->usecount != 0) {
+ clk_disable(prev_parent);
+ }
return ret;
}
@@ -286,43 +418,164 @@ void clk_unregister(struct clk *clk)
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)
+
+static void *mxc_proc_clocks_seq_start(struct seq_file *file, loff_t *index)
{
- struct clk *clkp;
- char *p = page;
- int len;
+ unsigned int i;
+ unsigned int name_length;
+ unsigned int longest_length = 0;
+ struct clk *current_clock = 0;
+ struct clk *clock;
+
+ /* Examine the clock list. */
+
+ i = 0;
+
+ list_for_each_entry(clock, &clocks, node) {
+ if (i++ == *index)
+ current_clock = clock;
+ name_length = strlen(clock->name);
+ if (name_length > longest_length)
+ longest_length = name_length;
+ }
- 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");
+ /* Check if we found the indicated clock. */
+
+ if (!current_clock)
+ return NULL;
+
+ /* Stash the length of the longest clock name for later use. */
+
+ file->private = (void *) longest_length;
+
+ /* Return success. */
+
+ return current_clock;
+}
+
+static void *mxc_proc_clocks_seq_next(struct seq_file *file, void *data,
+ loff_t *index)
+{
+ struct clk *current_clock = (struct clk *) data;
+
+ /* Check for nonsense. */
+
+ if (!current_clock)
+ return NULL;
+
+ /* Check if the current clock is the last. */
+
+ if (list_is_last(&current_clock->node, &clocks))
+ return NULL;
+
+ /* Move to the next clock structure. */
+
+ current_clock = list_entry(current_clock->node.next,
+ typeof(*current_clock), node);
+
+ (*index)++;
+
+ /* Return the new current clock. */
+
+ return current_clock;
+
+}
+
+static void mxc_proc_clocks_seq_stop(struct seq_file *file, void *data)
+{
+}
+
+static int mxc_proc_clocks_seq_show(struct seq_file *file, void *data)
+{
+ int result;
+ struct clk *clock = (struct clk *) data;
+ struct clk *parent = clock->parent;
+ unsigned int longest_length = (unsigned int) file->private;
+ unsigned long range_divisor;
+ const char *range_units;
+
+ if (clock->rate >= 1000000) {
+ range_divisor = 1000000;
+ range_units = "MHz";
+ } else if (clock->rate >= 1000) {
+ range_divisor = 1000;
+ range_units = "KHz";
+ } else {
+ range_divisor = 1;
+ range_units = "Hz";
}
- len = (p - page) - off;
- if (len < 0)
- len = 0;
+ if (parent)
+ result = seq_printf(file,
+ "%s-%-d%*s %s-%-d%*s %c%c%c%c%c%c %3d",
+ clock->name,
+ clock->id,
+ longest_length - strlen(clock->name), "",
+ parent->name,
+ parent->id,
+ longest_length - strlen(parent->name), "",
+ (clock->flags & RATE_PROPAGATES) ? 'P' : '_',
+ (clock->flags & ALWAYS_ENABLED) ? 'A' : '_',
+ (clock->flags & RATE_FIXED) ? 'F' : '_',
+ (clock->flags & CPU_FREQ_TRIG_UPDATE) ? 'T' : '_',
+ (clock->flags & AHB_HIGH_SET_POINT) ? 'H' : '_',
+ (clock->flags & AHB_MED_SET_POINT) ? 'M' : '_',
+ clock->usecount);
+ else
+ result = seq_printf(file,
+ "%s-%-d%*s %*s %c%c%c%c%c%c %3d",
+ clock->name,
+ clock->id,
+ longest_length - strlen(clock->name), "",
+ longest_length + 2, "",
+ (clock->flags & RATE_PROPAGATES) ? 'P' : '_',
+ (clock->flags & ALWAYS_ENABLED) ? 'A' : '_',
+ (clock->flags & RATE_FIXED) ? 'F' : '_',
+ (clock->flags & CPU_FREQ_TRIG_UPDATE) ? 'T' : '_',
+ (clock->flags & AHB_HIGH_SET_POINT) ? 'H' : '_',
+ (clock->flags & AHB_MED_SET_POINT) ? 'M' : '_',
+ clock->usecount);
+
+ if (result)
+ return result;
+
+ result = seq_printf(file, " %10lu (%lu%s)\n",
+ clock->rate,
+ clock->rate / range_divisor, range_units);
+
+ return result;
+
+}
- *eof = (len <= count) ? 1 : 0;
- *start = page + off;
+static const struct seq_operations mxc_proc_clocks_seq_ops = {
+ .start = mxc_proc_clocks_seq_start,
+ .next = mxc_proc_clocks_seq_next,
+ .stop = mxc_proc_clocks_seq_stop,
+ .show = mxc_proc_clocks_seq_show
+};
- return len;
+static int mxc_proc_clocks_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &mxc_proc_clocks_seq_ops);
}
+static const struct file_operations mxc_proc_clocks_ops = {
+ .open = mxc_proc_clocks_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
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);
+ res = create_proc_entry("cpu/clocks", 0, NULL);
if (!res) {
printk(KERN_ERR "Failed to create proc/cpu/clocks\n");
return -ENOMEM;
}
+ res->proc_fops = &mxc_proc_clocks_ops;
return 0;
}
diff --git a/arch/arm/plat-mxc/cpu_common.c b/arch/arm/plat-mxc/cpu_common.c
new file mode 100644
index 000000000000..f9edd6b04404
--- /dev/null
+++ b/arch/arm/plat-mxc/cpu_common.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/setup.h>
+extern int mxc_early_serial_console_init(char *);
+
+/*!
+ * @file plat-mxc/cpu_common.c
+ *
+ * @brief This file contains the common CPU initialization code.
+ *
+ * @ingroup MSL_MX31 MSL_MXC91321
+ */
+
+static void __init system_rev_setup(char **p)
+{
+ system_rev = simple_strtoul(*p, NULL, 16);
+}
+
+__early_param("system_rev=", system_rev_setup);
+
+int mxc_jtag_enabled; /* OFF: 0 (default), ON: 1 */
+
+/*
+ * Here are the JTAG options from the command line. By default JTAG
+ * is OFF which means JTAG is not connected and WFI is enabled
+ *
+ * "on" -- JTAG is connected, so WFI is disabled
+ * "off" -- JTAG is disconnected, so WFI is enabled
+ */
+
+static void __init jtag_wfi_setup(char **p)
+{
+ if (memcmp(*p, "on", 2) == 0) {
+ mxc_jtag_enabled = 1;
+ *p += 2;
+ } else if (memcmp(*p, "off", 3) == 0) {
+ mxc_jtag_enabled = 0;
+ *p += 3;
+ }
+}
+
+__early_param("jtag=", jtag_wfi_setup);
+
+void __init mxc_cpu_common_init(void)
+{
+ pr_info("CPU is %s%x Revision %u.%u\n",
+ (mxc_cpu() < 0x100) ? "i.MX" : "MXC",
+ mxc_cpu(), mxc_cpu_rev_major(), mxc_cpu_rev_minor());
+}
+
+/**
+ * early_console_setup - setup debugging console
+ *
+ * Consoles started here require little enough setup that we can start using
+ * them very early in the boot process, either right after the machine
+ * vector initialization, or even before if the drivers can detect their hw.
+ *
+ * Returns non-zero if a console couldn't be setup.
+ * This function is developed based on
+ * early_console_setup function as defined in arch/ia64/kernel/setup.c
+ */
+int __init early_console_setup(char *cmdline)
+{
+ int earlycons = 0;
+
+#ifdef CONFIG_SERIAL_MXC_CONSOLE
+ if (!mxc_early_serial_console_init(cmdline))
+ earlycons++;
+#endif
+
+ return (earlycons) ? 0 : -1;
+}
diff --git a/arch/arm/plat-mxc/cpufreq.c b/arch/arm/plat-mxc/cpufreq.c
new file mode 100644
index 000000000000..312cb6574676
--- /dev/null
+++ b/arch/arm/plat-mxc/cpufreq.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file cpufreq.c
+ *
+ * @brief A driver for the Freescale Semiconductor i.MXC CPUfreq module.
+ *
+ * The CPUFREQ driver is for controling CPU frequency. It allows you to change
+ * the CPU clock speed on the fly.
+ *
+ * @ingroup PM
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <asm/setup.h>
+#include <mach/clock.h>
+#include <asm/cacheflush.h>
+#include <linux/hrtimer.h>
+
+int cpu_freq_khz_min;
+int cpu_freq_khz_max;
+int arm_lpm_clk;
+int arm_normal_clk;
+int cpufreq_suspended;
+int cpufreq_trig_needed;
+
+static struct clk *cpu_clk;
+static struct regulator *gp_regulator;
+static struct cpu_wp *cpu_wp_tbl;
+static struct cpufreq_frequency_table imx_freq_table[4];
+extern int low_bus_freq_mode;
+extern int high_bus_freq_mode;
+extern int dvfs_core_is_active;
+extern int cpu_wp_nr;
+extern char *gp_reg_id;
+
+extern int set_low_bus_freq(void);
+extern int set_high_bus_freq(int high_bus_speed);
+extern int low_freq_bus_used(void);
+
+#ifdef CONFIG_ARCH_MX51
+extern struct cpu_wp *(*get_cpu_wp)(int *wp);
+#endif
+
+int set_cpu_freq(int freq)
+{
+ int ret = 0;
+ int org_cpu_rate;
+ int gp_volt = 0;
+ int i;
+
+ org_cpu_rate = clk_get_rate(cpu_clk);
+ if (org_cpu_rate == freq)
+ return ret;
+
+ for (i = 0; i < cpu_wp_nr; i++) {
+ if (freq == cpu_wp_tbl[i].cpu_rate)
+ gp_volt = cpu_wp_tbl[i].cpu_voltage;
+ }
+
+ if (gp_volt == 0)
+ return ret;
+
+ /*Set the voltage for the GP domain. */
+ if (freq > org_cpu_rate) {
+ ret = regulator_set_voltage(gp_regulator, gp_volt, gp_volt);
+ if (ret < 0) {
+ printk(KERN_DEBUG "COULD NOT SET GP VOLTAGE!!!!\n");
+ return ret;
+ }
+ }
+
+ ret = clk_set_rate(cpu_clk, freq);
+ if (ret != 0) {
+ printk(KERN_DEBUG "cannot set CPU clock rate\n");
+ return ret;
+ }
+
+ if (freq < org_cpu_rate) {
+ ret = regulator_set_voltage(gp_regulator, gp_volt, gp_volt);
+ if (ret < 0) {
+ printk(KERN_DEBUG "COULD NOT SET GP VOLTAGE!!!!\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int mxc_verify_speed(struct cpufreq_policy *policy)
+{
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ return cpufreq_frequency_table_verify(policy, imx_freq_table);
+}
+
+static unsigned int mxc_get_speed(unsigned int cpu)
+{
+ if (cpu)
+ return 0;
+
+ return clk_get_rate(cpu_clk) / 1000;
+}
+
+static int calc_frequency_khz(int target, unsigned int relation)
+{
+ int i;
+
+ if ((target * 1000) == clk_get_rate(cpu_clk))
+ return target;
+
+ if (relation == CPUFREQ_RELATION_H) {
+ for (i = cpu_wp_nr - 1; i >= 0; i--) {
+ if (imx_freq_table[i].frequency <= target)
+ return imx_freq_table[i].frequency;
+ }
+ } else if (relation == CPUFREQ_RELATION_L) {
+ for (i = 0; i < cpu_wp_nr; i++) {
+ if (imx_freq_table[i].frequency >= target)
+ return imx_freq_table[i].frequency;
+ }
+ }
+ printk(KERN_ERR "Error: No valid cpufreq relation\n");
+ return cpu_freq_khz_max;
+}
+
+static int mxc_set_target(struct cpufreq_policy *policy,
+ unsigned int target_freq, unsigned int relation)
+{
+ struct cpufreq_freqs freqs;
+ int freq_Hz;
+ int low_freq_bus_ready = 0;
+ int ret = 0;
+
+ if (dvfs_core_is_active || cpufreq_suspended) {
+ target_freq = clk_get_rate(cpu_clk) / 1000;
+ freq_Hz = calc_frequency_khz(target_freq, relation) * 1000;
+ if (freq_Hz == arm_lpm_clk)
+ freqs.old = cpu_wp_tbl[cpu_wp_nr - 2].cpu_rate / 1000;
+ else
+ freqs.old = arm_lpm_clk / 1000;
+
+ freqs.new = freq_Hz / 1000;
+ freqs.cpu = 0;
+ freqs.flags = 0;
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ return ret;
+ }
+ /*
+ * Some governors do not respects CPU and policy lower limits
+ * which leads to bad things (division by zero etc), ensure
+ * that such things do not happen.
+ */
+ if (target_freq < policy->cpuinfo.min_freq)
+ target_freq = policy->cpuinfo.min_freq;
+
+ if (target_freq < policy->min)
+ target_freq = policy->min;
+
+ freq_Hz = calc_frequency_khz(target_freq, relation) * 1000;
+
+ freqs.old = clk_get_rate(cpu_clk) / 1000;
+ freqs.new = freq_Hz / 1000;
+ freqs.cpu = 0;
+ freqs.flags = 0;
+ low_freq_bus_ready = low_freq_bus_used();
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ if (!dvfs_core_is_active) {
+ if ((freq_Hz == arm_lpm_clk) && (!low_bus_freq_mode)
+ && (low_freq_bus_ready)) {
+ if (freqs.old != freqs.new)
+ ret = set_cpu_freq(freq_Hz);
+ set_low_bus_freq();
+
+ } else {
+ set_high_bus_freq(0);
+ ret = set_cpu_freq(freq_Hz);
+ }
+ }
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return ret;
+}
+
+static int __init mxc_cpufreq_driver_init(struct cpufreq_policy *policy)
+{
+ int ret;
+ int i;
+
+ printk(KERN_INFO "i.MXC CPU frequency driver\n");
+
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ cpu_clk = clk_get(NULL, "cpu_clk");
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_ERR "%s: failed to get cpu clock\n", __func__);
+ return PTR_ERR(cpu_clk);
+ }
+
+ gp_regulator = regulator_get(NULL, gp_reg_id);
+ if (IS_ERR(gp_regulator)) {
+ clk_put(cpu_clk);
+ printk(KERN_ERR "%s: failed to get gp regulator\n", __func__);
+ return PTR_ERR(gp_regulator);
+ }
+
+ /* Set the current working point. */
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+
+ cpu_freq_khz_min = cpu_wp_tbl[0].cpu_rate / 1000;
+ cpu_freq_khz_max = cpu_wp_tbl[0].cpu_rate / 1000;
+
+ for (i = 0; i < cpu_wp_nr; i++) {
+ imx_freq_table[cpu_wp_nr - 1 - i].index = cpu_wp_nr - i;
+ imx_freq_table[cpu_wp_nr - 1 - i].frequency =
+ cpu_wp_tbl[i].cpu_rate / 1000;
+
+ if ((cpu_wp_tbl[i].cpu_rate / 1000) < cpu_freq_khz_min)
+ cpu_freq_khz_min = cpu_wp_tbl[i].cpu_rate / 1000;
+
+ if ((cpu_wp_tbl[i].cpu_rate / 1000) > cpu_freq_khz_max)
+ cpu_freq_khz_max = cpu_wp_tbl[i].cpu_rate / 1000;
+ }
+
+ imx_freq_table[i].index = 0;
+ imx_freq_table[i].frequency = CPUFREQ_TABLE_END;
+
+ policy->cur = clk_get_rate(cpu_clk) / 1000;
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+ policy->min = policy->cpuinfo.min_freq = cpu_freq_khz_min;
+ policy->max = policy->cpuinfo.max_freq = cpu_freq_khz_max;
+
+ arm_lpm_clk = cpu_freq_khz_min * 1000;
+ arm_normal_clk = cpu_freq_khz_max * 1000;
+
+ /* Manual states, that PLL stabilizes in two CLK32 periods */
+ policy->cpuinfo.transition_latency = 10;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, imx_freq_table);
+
+ if (ret < 0) {
+ clk_put(cpu_clk);
+ regulator_put(gp_regulator);
+ printk(KERN_ERR "%s: failed to register i.MXC CPUfreq\n",
+ __func__);
+ return ret;
+ }
+
+ cpufreq_frequency_table_get_attr(imx_freq_table, policy->cpu);
+ return 0;
+}
+
+static int mxc_cpufreq_suspend(struct cpufreq_policy *policy,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int mxc_cpufreq_resume(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+
+static int mxc_cpufreq_driver_exit(struct cpufreq_policy *policy)
+{
+ cpufreq_frequency_table_put_attr(policy->cpu);
+
+ /* Reset CPU to 665MHz */
+ if (!dvfs_core_is_active)
+ set_cpu_freq(arm_normal_clk);
+ if (!high_bus_freq_mode)
+ set_high_bus_freq(1);
+
+ clk_put(cpu_clk);
+ regulator_put(gp_regulator);
+ return 0;
+}
+
+static struct cpufreq_driver mxc_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = mxc_verify_speed,
+ .target = mxc_set_target,
+ .get = mxc_get_speed,
+ .init = mxc_cpufreq_driver_init,
+ .exit = mxc_cpufreq_driver_exit,
+ .suspend = mxc_cpufreq_suspend,
+ .resume = mxc_cpufreq_resume,
+ .name = "imx",
+};
+
+static int __devinit mxc_cpufreq_init(void)
+{
+ return cpufreq_register_driver(&mxc_driver);
+}
+
+static void mxc_cpufreq_exit(void)
+{
+ cpufreq_unregister_driver(&mxc_driver);
+}
+
+module_init(mxc_cpufreq_init);
+module_exit(mxc_cpufreq_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("CPUfreq driver for i.MX");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/dma_mx2.c b/arch/arm/plat-mxc/dma_mx2.c
new file mode 100644
index 000000000000..5a7e93b31d57
--- /dev/null
+++ b/arch/arm/plat-mxc/dma_mx2.c
@@ -0,0 +1,1316 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/* Front-end to the DMA handling. This handles the allocation/freeing
+ * of DMA channels, and provides a unified interface to the machines
+ * DMA facilities.
+ */
+
+/*!
+ * @file plat-mxc/dma_mx2.c
+ * @brief This file contains functions for DMA API
+ *
+ * @ingroup DMA_MX27
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/mman.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+
+#include <linux/proc_fs.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <asm/delay.h>
+
+#include <asm/atomic.h>
+
+/* commented temperily for mx27 compilation
+#define DMA_PM
+*/
+#ifdef DMA_PM
+#include <linux/pm.h>
+#include <mach/apmc.h>
+struct apmc_user *dma_apmc_user;
+struct pm_dev *dma_pm;
+#define DMA_PMST_RESUME 0
+#define DMA_PMST_STANDBY 1
+#define DMA_PMST_SUSPEND 2
+static unsigned int dma_pm_status = DMA_PMST_RESUME;
+#endif
+
+/*!
+ * This variable is used to controll the clock of DMA.
+ * It counts the number of actived channels
+ */
+static atomic_t g_dma_actived = ATOMIC_INIT(0);
+
+/*!
+ * This variable point a proc file which contains the information
+ * of DMA channels
+ */
+static struct proc_dir_entry *g_proc_dir;
+
+/*!
+ * The dma channels
+ */
+static mxc_dma_channel_t g_dma_channels[MAX_DMA_CHANNELS];
+static mx2_dma_priv_t g_dma_privates[MXC_DMA_CHANNELS];
+static mx2_dma_bd_t g_dma_bd_table[MXC_DMA_CHANNELS][MAX_BD_SIZE];
+
+static DEFINE_SPINLOCK(dma_list_lock);
+
+static struct clk *dma_clk;
+
+/*!@brief flush buffer descriptor ring*/
+#define flush_dma_bd(private) \
+ { \
+ atomic_set(&(private->bd_used), 0); \
+ private->bd_rd = private->bd_wr;\
+ }
+
+/*!@brief get next buffer discriptor */
+#define next_dma_bd(private) \
+ ({ \
+ int bd_next = (private->bd_rd+1)%MAX_BD_SIZE; \
+ (bd_next == private->bd_wr) ? NULL: private->bd_ring+bd_next;\
+ })
+
+static inline int consume_dma_bd(mxc_dma_channel_t * dma, int error);
+/*!
+ *@brief allocate a dma channel.
+ *
+ *@param idx Requested channel NO.
+ * @li MXC_INVLAID_CHANNEL System allocates a free channel which is not statically allocated.
+ * @li Others User requests a specific channel
+ *@return @li MXC_INVLAID_CHANNEL Failure
+ * @li Others Success
+ */
+static inline int get_dma_channel(int idx)
+{
+ int i;
+ mxc_dma_channel_t *p;
+
+ if ((idx >= MAX_DMA_CHANNELS) && (idx != MXC_DMA_DYNAMIC_CHANNEL)) {
+ return -1;
+ }
+ if (idx != MXC_DMA_DYNAMIC_CHANNEL) {
+ p = g_dma_channels + idx;
+ BUG_ON(p->dynamic != 0);
+ if (xchg(&p->lock, 1) != 0) {
+ return -1;
+ }
+ return idx;
+ }
+
+ p = g_dma_channels;
+ for (i = 0; (i < MAX_DMA_CHANNELS); i++, p++) {
+ if (p->dynamic && (xchg(&p->lock, 1) == 0)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*!
+ *@brief release a dma channel.
+ *
+ *@param idx channel number
+ *@return none;
+ */
+static inline void put_dma_channel(int idx)
+{
+ mxc_dma_channel_t *p;
+
+ if ((idx < MAX_DMA_CHANNELS) && (idx >= 0)) {
+ p = g_dma_channels + idx;
+ (void)xchg(&p->lock, 0);
+ }
+}
+
+/*!
+ *@brief Get dma list for /proc/dma
+ */
+static int mxc_get_dma_list(char *buf)
+{
+ mxc_dma_channel_t *dma;
+ char *p = buf;
+ int i;
+
+ for (i = 0, dma = g_dma_channels; i < MAX_DMA_CHANNELS; i++, dma++) {
+ if (dma->lock) {
+ p += sprintf(p, "dma channel %2d: %s\n", i,
+ dma->dev_name ? dma->dev_name : "unknown");
+ } else {
+ p += sprintf(p, "dma channel %2d: unused\n", i);
+ }
+ }
+
+ return p - buf;
+}
+
+/*!@brief save the mask of dma interrupts*/
+#define save_dma_interrupt(flags) \
+ flags = __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DIMR)
+
+/*!@brief restore the mask of dma interrupts*/
+#define restore_dma_interrupt(flags) \
+ __raw_writel(flags, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DIMR)
+
+/*!@brief disable interrupt of dma channel*/
+static inline void mask_dma_interrupt(int channel)
+{
+ unsigned long reg;
+ save_dma_interrupt(reg);
+ reg |= 1 << channel; /*mask interrupt; */
+ restore_dma_interrupt(reg);
+}
+
+/*!@brief enable interrupt of dma channel */
+static inline void unmask_dma_interrupt(int channel)
+{
+ unsigned long reg;
+ save_dma_interrupt(reg);
+ reg &= ~(1 << channel); /*unmask interrupt; */
+ restore_dma_interrupt(reg);
+}
+
+/*!@brief get interrupt event of dma channel */
+static unsigned long inline __get_dma_interrupt(int channel)
+{
+ unsigned long mode;
+ mode = 0;
+ if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DISR) & (1 << channel))
+ mode |= DMA_DONE;
+
+ if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOSR) &
+ (1 << channel))
+ mode |= DMA_BURST_TIMEOUT;
+ if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DSESR) & (1 << channel))
+ mode |= DMA_TRANSFER_ERROR;
+
+ if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBOSR) & (1 << channel))
+ mode |= DMA_BUFFER_OVERFLOW;
+ if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DRTOSR) &
+ (1 << channel))
+ mode |= DMA_REQUEST_TIMEOUT;
+ return mode;
+}
+
+/*!
+ *@brief clean all event of dma interrupt and return the valid event.
+ */
+static unsigned long inline __clear_dma_interrupt(int channel)
+{
+ unsigned long mode;
+ mode = __get_dma_interrupt(channel);
+ __raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DISR);
+ __raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOSR);
+ __raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DRTOSR);
+ __raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DSESR);
+ __raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBOSR);
+
+ return mode;
+}
+
+/*!@brief This function enables dma clocks without lock */
+static void inline __enable_dma_clk(void)
+{
+ unsigned long reg;
+ clk_enable(dma_clk);
+ reg = __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR);
+ reg |= 0x1;
+ __raw_writel(reg, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR);
+}
+
+/*!@brief This function disables dma clocks without lock */
+static void inline __disable_dma_clk(void)
+{
+ unsigned long reg;
+ reg = __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR);
+ reg &= ~0x1;
+ __raw_writel(reg, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR);
+ clk_disable(dma_clk);
+}
+
+/*!@brief This function enables dma clocks with lock */
+static void inline enable_dma_clk(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dma_list_lock, flags);
+ if (atomic_read(&g_dma_actived) == 0) {
+ __enable_dma_clk();
+ }
+ spin_unlock_irqrestore(&dma_list_lock, flags);
+ return;
+}
+
+/*!@brief This function disables dma clocks without locked */
+static void inline disable_dma_clk(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dma_list_lock, flags);
+ if (atomic_read(&g_dma_actived) == 0) {
+ __disable_dma_clk();
+ }
+ spin_unlock_irqrestore(&dma_list_lock, flags);
+ return;
+}
+
+/*!@brief select a buffer to transfer and
+ * setup dma channel for current transfer
+ */
+static void setup_dmac(mxc_dma_channel_t * dma)
+{
+ mx2_dma_priv_t *priv = (mx2_dma_priv_t *) dma->private;
+ dma_regs_t *dma_base = (dma_regs_t *) (priv->dma_base);
+ mx2_dma_bd_t *p, *q;
+ unsigned long ctrl_val;
+
+ if (dma->active == 0) {
+ printk(KERN_ERR
+ "dma channel %d is not enabled, when receiving this channel 's interrupt\n",
+ dma->channel);
+ return;
+ }
+ if (atomic_read(&(priv->bd_used)) <= 0) {
+ printk(KERN_ERR "dma channel %d is empty\n", dma->channel);
+ dma->active = 0;
+ atomic_dec(&g_dma_actived);
+ return;
+ }
+ /* BUSY: transfering
+ * PEND: Wait for set to DMAC.
+ * s1: no transfering:
+ * set first(one BUSY). if there are more than one tranfer. set second &repeat is enabled(two BUSY).
+ *
+ * s2: transfering & just on transfer
+ * one BUSY. set the tranesfer and set repeat bit(two BUSY)
+ * s3: transfering & repeat has set
+ * has two BUSY.
+ */
+ p = priv->bd_ring + priv->bd_rd;
+ q = next_dma_bd(priv);
+ if (!(p->state & DMA_BD_ST_BUSY)) {
+ /*NOTICE:: This is first buffer or dma chain does not support chain-buffer. So CEN must clear & set again */
+ ctrl_val =
+ __raw_readl(&(dma_base->Ctl)) &
+ (~(DMA_CTL_ACRPT | DMA_CTL_RPT | DMA_CTL_CEN));
+ __raw_writel(ctrl_val, &(dma_base->Ctl));
+ if (p->mode != dma->mode) {
+ dma->mode = p->mode; /* bi-dir channel do mode change */
+ if (dma->mode == MXC_DMA_MODE_READ) {
+ DMA_CTL_SET_SMOD(ctrl_val,
+ priv->dma_info->sourceType);
+ DMA_CTL_SET_SSIZ(ctrl_val,
+ priv->dma_info->sourcePort);
+ DMA_CTL_SET_DMOD(ctrl_val,
+ priv->dma_info->destType);
+ DMA_CTL_SET_DSIZ(ctrl_val,
+ priv->dma_info->destPort);
+ } else {
+ DMA_CTL_SET_SMOD(ctrl_val,
+ priv->dma_info->destType);
+ DMA_CTL_SET_SSIZ(ctrl_val,
+ priv->dma_info->destPort);
+ DMA_CTL_SET_DMOD(ctrl_val,
+ priv->dma_info->sourceType);
+ DMA_CTL_SET_DSIZ(ctrl_val,
+ priv->dma_info->sourcePort);
+ }
+ }
+ __raw_writel(p->src_addr, &(dma_base->SourceAddr));
+ __raw_writel(p->dst_addr, &(dma_base->DestAddr));
+ __raw_writel(p->count, &(dma_base->Count));
+ p->state |= DMA_BD_ST_BUSY;
+ p->state &= ~(DMA_BD_ST_PEND);
+ ctrl_val |= DMA_CTL_CEN;
+ __raw_writel(ctrl_val, &(dma_base->Ctl));
+ if (q && priv->dma_chaining) { /*DO chain-buffer */
+ __raw_writel(q->src_addr, &(dma_base->SourceAddr));
+ __raw_writel(q->dst_addr, &(dma_base->DestAddr));
+ __raw_writel(q->count, &(dma_base->Count));
+ q->state |= DMA_BD_ST_BUSY;
+ q->state &= ~(DMA_BD_ST_PEND);
+ ctrl_val |= DMA_CTL_ACRPT | DMA_CTL_RPT | DMA_CTL_CEN;
+ __raw_writel(ctrl_val, &(dma_base->Ctl));
+ }
+ } else { /* Just dma channel which supports dma buffer can run to there */
+ BUG_ON(!priv->dma_chaining);
+ if (q) { /* p is tranfering, then q must be set into dma controller */
+ /*WARNING:: [1] dangerous area begin.
+ * If the p is completed during MCU run in this erea, the dma channel is crashed.
+ */
+ __raw_writel(q->src_addr, &(dma_base->SourceAddr));
+ __raw_writel(q->dst_addr, &(dma_base->DestAddr));
+ __raw_writel(q->count, &(dma_base->Count));
+ /*WARNING:: [2] dangerous area end */
+ ctrl_val =
+ __raw_readl(&(dma_base->Ctl)) | (DMA_CTL_ACRPT |
+ DMA_CTL_RPT |
+ DMA_CTL_CEN);
+ __raw_writel(ctrl_val, &(dma_base->Ctl));
+
+ /* WARNING:: This is workaround and it is dangerous:
+ * the judgement is not safety.
+ */
+ if (!__get_dma_interrupt(dma->channel)) {
+ q->state |= DMA_BD_ST_BUSY;
+ q->state &= ~(DMA_BD_ST_PEND);
+ } else {
+ /*Waiting re-enable is in ISR */
+ printk(KERN_ERR
+ "Warning:: The privous transfer is completed. Maybe the chain buffer is stopped.");
+ }
+ } else { /* Last buffer is transfering: just clear RPT bit */
+ ctrl_val =
+ __raw_readl(&(dma_base->Ctl)) &
+ (~(DMA_CTL_ACRPT | DMA_CTL_RPT));
+ __raw_writel(ctrl_val, &(dma_base->Ctl));
+ }
+ }
+}
+
+/*!
+ * @brief interrupt handler of dma channel
+ */
+static irqreturn_t dma_irq_handler(int irq, void *dev_id)
+{
+ mxc_dma_channel_t *dma = (mxc_dma_channel_t *) dev_id;
+ mx2_dma_priv_t *priv = (mx2_dma_priv_t *) (dma ? dma->private : NULL);
+ dma_regs_t *dma_base;
+ int state, error = MXC_DMA_DONE;
+
+ BUG_ON(priv == NULL);
+
+ dma_base = (dma_regs_t *) priv->dma_base;
+
+ state = __clear_dma_interrupt(dma->channel);
+
+ priv->trans_bytes += dma_base->transferd;
+ if (state != DMA_DONE) {
+ if (state & DMA_REQUEST_TIMEOUT) {
+ error = MXC_DMA_REQUEST_TIMEOUT;
+ } else {
+ error = MXC_DMA_TRANSFER_ERROR;
+ }
+ }
+ if (consume_dma_bd(dma, error)) {
+ disable_dma_clk();
+ if (dma->cb_fn) {
+ dma->cb_fn(dma->cb_args, error, priv->trans_bytes);
+ }
+ priv->trans_bytes = 0;
+ } else {
+ disable_dma_clk();
+ }
+ return IRQ_HANDLED;
+}
+
+/*!
+ *@brief Set DMA channel parameters
+ *
+ *@param dma Requested channel NO.
+ *@param dma_info Channel configuration
+ *@return @li 0 Success
+ * @li others Failure
+ */
+static int setup_dma_channel(mxc_dma_channel_t * dma, mx2_dma_info_t * dma_info)
+{
+ mx2_dma_priv_t *priv = (mx2_dma_priv_t *) (dma ? dma->private : NULL);
+ dma_regs_t *dma_base;
+ unsigned long reg;
+
+ if (!dma_info || !priv) {
+ return -1;
+ }
+
+ if (dma_info->sourceType > 3) {
+ return -1;
+ }
+ if (dma_info->destType > 3) {
+ return -1;
+ }
+ if (dma_info->destPort > 3) {
+ return -1;
+ }
+ if (dma_info->sourcePort > 3) {
+ return -1;
+ }
+ if (dma_info->M2D_Valid) {
+ /*add for second dma */
+ if (dma_info->W < dma_info->X) {
+ return -1;
+ }
+ }
+
+ priv->dma_chaining = dma_info->dma_chaining;
+ priv->ren = dma_info->ren;
+
+ if (dma_info->sourceType != DMA_TYPE_FIFO
+ && dma_info->destType != DMA_TYPE_FIFO) {
+ if (dma_info->ren) {
+ printk(KERN_INFO
+ "Warning:request enable just affect source or destination port is FIFO !\n");
+ priv->ren = 0;
+ }
+ }
+
+ if (dma_info->M2D_Valid) {
+ if (dma_info->msel) {
+ __raw_writel(dma_info->W,
+ IO_ADDRESS(DMA_BASE_ADDR) + DMA_WSRB);
+ __raw_writel(dma_info->X,
+ IO_ADDRESS(DMA_BASE_ADDR) + DMA_XSRB);
+ __raw_writel(dma_info->Y,
+ IO_ADDRESS(DMA_BASE_ADDR) + DMA_YSRB);
+
+ } else {
+ __raw_writel(dma_info->W,
+ IO_ADDRESS(DMA_BASE_ADDR) + DMA_WSRA);
+ __raw_writel(dma_info->X,
+ IO_ADDRESS(DMA_BASE_ADDR) + DMA_XSRA);
+ __raw_writel(dma_info->Y,
+ IO_ADDRESS(DMA_BASE_ADDR) + DMA_YSRA);
+ }
+ }
+
+ dma_base = (dma_regs_t *) (priv->dma_base);
+
+ __raw_writel(dma_info->burstLength, &(dma_base->BurstLength));
+ __raw_writel(dma_info->request, &(dma_base->RequestSource));
+
+ if (dma_info->ren) {
+ reg = dma_info->busuntils & 0x1FFFF;
+ if (dma_info->rto_en) {
+ reg |= 0xE000;
+ }
+ __raw_writel(reg, &(dma_base->BusUtilt));
+ } else {
+ __raw_writel(dma_info->busuntils, &(dma_base->BusUtilt));
+ }
+
+ reg = __raw_readl(&(dma_base->Ctl)) & (~(DMA_CTL_ACRPT | DMA_CTL_RPT));
+
+ if (dma_info->dir) {
+ reg |= DMA_CTL_MDIR;
+ } else {
+ reg &= ~DMA_CTL_MDIR;
+ }
+
+ if (priv->ren) {
+ reg |= DMA_CTL_REN;
+ } else {
+ reg &= ~DMA_CTL_REN;
+ }
+
+ if ((dma_info->M2D_Valid) && (dma_info->msel)) {
+ reg |= DMA_CTL_MSEL;
+ } else {
+ reg &= ~DMA_CTL_MSEL;
+ }
+
+ if (dma_info->mode) {
+ DMA_CTL_SET_SMOD(reg, dma_info->destType);
+ DMA_CTL_SET_SSIZ(reg, dma_info->destPort);
+ DMA_CTL_SET_DMOD(reg, dma_info->sourceType);
+ DMA_CTL_SET_DSIZ(reg, dma_info->sourcePort);
+ } else {
+ DMA_CTL_SET_SMOD(reg, dma_info->sourceType);
+ DMA_CTL_SET_SSIZ(reg, dma_info->sourcePort);
+ DMA_CTL_SET_DMOD(reg, dma_info->destType);
+ DMA_CTL_SET_DSIZ(reg, dma_info->destPort);
+ }
+
+ __raw_writel(reg, &(dma_base->Ctl));
+
+ __clear_dma_interrupt(dma->channel);
+ unmask_dma_interrupt(dma->channel);
+
+ disable_dma_clk();
+ return 0;
+}
+
+/*!@brief setup interrupt and setup dma channel by dma parameter */
+static inline int __init_dma_channel(mxc_dma_channel_t * chan,
+ mx2_dma_info_t * dma_info)
+{
+ mx2_dma_priv_t *dma_private = (mx2_dma_priv_t *) chan->private;
+ dma_regs_t *dma_base;
+ int ret;
+
+ mask_dma_interrupt(chan->channel);
+ ret =
+ request_irq(dma_private->dma_irq, dma_irq_handler,
+ IRQF_DISABLED | IRQF_SHARED, chan->dev_name,
+ (void *)chan);
+ if (ret) {
+ printk(KERN_ERR
+ "%s: unable to request IRQ %d for DMA channel\n",
+ chan->dev_name, dma_private->dma_irq);
+ return ret;
+ }
+
+ enable_dma_clk();
+
+ dma_base = (dma_regs_t *) (dma_private->dma_base);
+ __raw_writel(0, &(dma_base->Ctl));
+
+ ret = 0;
+ if ((ret = setup_dma_channel(chan, dma_info))) {
+ free_irq(dma_private->dma_irq, (void *)chan);
+ }
+ disable_dma_clk();
+ return 0;
+}
+
+/*!@brief initialize buffer descriptor ring.*/
+static inline void init_dma_bd(mx2_dma_priv_t * private)
+{
+ int i;
+ mx2_dma_bd_t *pbd;
+ private->bd_rd = private->bd_wr = 0;
+ atomic_set(&(private->bd_used), 0);
+ for (i = 0, pbd = private->bd_ring; i < MAX_BD_SIZE; i++, pbd++) {
+ pbd->state = 0;
+ }
+}
+
+/*!@brief add dma buffer into buffer descriptor ring */
+static inline int fill_dma_bd(mxc_dma_channel_t * dma,
+ mxc_dma_requestbuf_t * buf, int num,
+ mxc_dma_mode_t mode)
+{
+ int i, wr;
+ unsigned long flags, mask;
+ mx2_dma_priv_t *priv = dma->private;
+ mx2_dma_bd_t *p, *q;
+
+ if ((atomic_read(&(priv->bd_used)) + num) > MAX_BD_SIZE) {
+ return -EBUSY;
+ }
+
+ for (i = 0; i < num; i++) {
+ wr = priv->bd_wr;
+ p = priv->bd_ring + wr;
+ p->mode = mode;
+ p->count = buf[i].num_of_bytes;
+ p->src_addr = buf[i].src_addr;
+ p->dst_addr = buf[i].dst_addr;
+ if (i == num - 1) {
+ p->state = DMA_BD_ST_LAST | DMA_BD_ST_PEND;
+ } else {
+ p->state = DMA_BD_ST_PEND;
+ }
+ priv->bd_wr = (wr + 1) % MAX_BD_SIZE;
+ atomic_inc(&(priv->bd_used));
+
+ if (atomic_read(&(priv->bd_used)) != 2)
+ continue;
+ /* Disable interrupt of this channel */
+ local_irq_save(flags);
+ local_irq_disable();
+ save_dma_interrupt(mask);
+ mask_dma_interrupt(dma->channel);
+ local_irq_restore(flags);
+ /*TODO ::
+ * If channel is transfering and supports chain_buffer,
+ * when the new buffer is 2st buffer , repeat must be enabled
+ */
+ if (priv->dma_chaining && dma->active) {
+ q = priv->bd_ring + priv->bd_rd;
+ if (q && (q->state & DMA_BD_ST_BUSY)) {
+ if (atomic_read(&(priv->bd_used)) == 2) {
+ setup_dmac(dma);
+ }
+ }
+ }
+ restore_dma_interrupt(mask);
+ }
+ return 0;
+}
+
+/*!@brief add sg-list into buffer descriptor ring */
+static inline int fill_dma_bd_by_sg(mxc_dma_channel_t * dma,
+ struct scatterlist *sg, int num,
+ int real_bytes, mxc_dma_mode_t mode)
+{
+ int i, wr, total_bytes = real_bytes;
+ unsigned long flags, mask;
+ mx2_dma_priv_t *priv = dma->private;
+ mx2_dma_bd_t *p, *q;
+ if ((atomic_read(&(priv->bd_used)) + num) > MAX_BD_SIZE) {
+ return -EBUSY;
+ }
+
+ for (i = 0; i < num && ((real_bytes <= 0) || (total_bytes > 0)); i++) {
+ wr = priv->bd_wr;
+ p = priv->bd_ring + wr;
+ p->mode = mode;
+ if (real_bytes > 0) {
+ if (sg[i].length >= total_bytes) {
+ p->count = total_bytes;
+ } else {
+ p->count = sg[i].length;
+ }
+ total_bytes -= p->count;
+ } else {
+ p->count = sg[i].length;
+ }
+ if (mode == MXC_DMA_MODE_READ) {
+ p->src_addr = priv->dma_info->per_address;
+ p->dst_addr = sg[i].dma_address;
+ } else {
+ p->dst_addr = priv->dma_info->per_address;
+ p->src_addr = sg[i].dma_address;
+ }
+ if ((i == num - 1) || ((real_bytes > 0) && (total_bytes == 0))) {
+ p->state = DMA_BD_ST_LAST | DMA_BD_ST_PEND;
+ } else {
+ p->state = DMA_BD_ST_PEND;
+ }
+ priv->bd_wr = (wr + 1) % MAX_BD_SIZE;
+ atomic_inc(&(priv->bd_used));
+
+ if (atomic_read(&(priv->bd_used)) != 2)
+ continue;
+ /* Disable interrupt of this channel */
+ local_irq_save(flags);
+ local_irq_disable();
+ save_dma_interrupt(mask);
+ mask_dma_interrupt(dma->channel);
+ local_irq_restore(flags);
+ /*TODO ::
+ * If channel is transfering and supports chain_buffer,
+ * when the new buffer is 2st buffer , repeat must be enabled
+ */
+ if (priv->dma_chaining && dma->active) {
+ q = next_dma_bd(priv);
+ if (q && (q->state & DMA_BD_ST_BUSY)) {
+ if ((atomic_read(&(priv->bd_used))) == 2) {
+ setup_dmac(dma);
+ }
+ }
+ }
+ restore_dma_interrupt(mask);
+ }
+ return 0;
+}
+
+/*!@brief select next buffer descripter to transfer.
+ * return 1: need call call-back function. 0: Not need call call-back.
+ * it just is called in ISR
+ */
+static inline int consume_dma_bd(mxc_dma_channel_t * dma, int error)
+{
+ mx2_dma_priv_t *priv = dma->private;
+ mx2_dma_bd_t *p;
+ int notify = 0;
+ if (priv == NULL) {
+ printk(KERN_ERR
+ "request dma channel %d which is not initialize completed.!\n",
+ dma->channel);
+ return 1;
+ }
+ if (error != MXC_DMA_DONE) {
+ for (p = priv->bd_ring + priv->bd_rd;
+ atomic_read(&(priv->bd_used)) > 0;) {
+ priv->bd_rd = (priv->bd_rd + 1) % MAX_BD_SIZE;
+ atomic_dec(&(priv->bd_used));
+ if (p->state & DMA_BD_ST_LAST) {
+ p->state = 0;
+ break;
+ }
+ p->state = 0;
+ }
+ notify = 1;
+ } else {
+ p = priv->bd_ring + priv->bd_rd;
+ priv->bd_rd = (priv->bd_rd + 1) % MAX_BD_SIZE;
+ atomic_dec(&(priv->bd_used));
+ notify = (p->state & DMA_BD_ST_LAST) == DMA_BD_ST_LAST;
+ }
+ if (atomic_read(&(priv->bd_used)) <= 0) {
+ dma->active = 0;
+ atomic_dec(&g_dma_actived);
+ } else {
+ setup_dmac(dma);
+ }
+ return notify;
+}
+
+/*!
+ * This function is generally called by the driver at open time.
+ * The DMA driver would do any initialization steps that is required
+ * to get the channel ready for data transfer.
+ *
+ * @param channel_id a pre-defined id. The peripheral driver would specify
+ * the id associated with its peripheral. This would be
+ * used by the DMA driver to identify the peripheral
+ * requesting DMA and do the necessary setup on the
+ * channel associated with the particular peripheral.
+ * The DMA driver could use static or dynamic DMA channel
+ * allocation.
+ * @param dev_name module name or device name
+ * @return returns a negative number on error if request for a DMA channel did not
+ * succeed, returns the channel number to be used on success.
+ */
+int mxc_dma_request_ext(mxc_dma_device_t channel_id, char *dev_name,
+ struct dma_channel_info *info)
+{
+ mxc_dma_channel_t *dma;
+ mx2_dma_priv_t *dma_private = NULL;
+ mx2_dma_info_t *dma_info = mxc_dma_get_info(channel_id);
+ int index;
+ int ret;
+
+ if (dma_info == NULL) {
+ return -EINVAL;
+ }
+
+ if ((index = get_dma_channel(dma_info->dma_chan)) < 0) {
+ return -ENODEV;
+ }
+
+ dma = g_dma_channels + index;
+ dma_private = (mx2_dma_priv_t *) dma->private;
+ if (dma_private == NULL) {
+ printk(KERN_ERR
+ "request dma channel %d which is not initialize completed.!\n",
+ index);
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ dma->active = 0;
+ dma_private->dma_info = NULL;
+ dma->cb_fn = NULL;
+ dma->cb_args = NULL;
+ dma->dev_name = dev_name;
+ dma->mode = dma_info->mode ? MXC_DMA_MODE_WRITE : MXC_DMA_MODE_READ;
+ init_dma_bd(dma_private);
+
+ if (!(ret = __init_dma_channel(dma, dma_info))) {
+ dma_private->dma_info = dma_info;
+ return index;
+ }
+ exit:
+ put_dma_channel(index);
+ return ret;
+}
+
+/*!
+ * This function is generally called by the driver at close time. The DMA
+ * driver would do any cleanup associated with this channel.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @return returns a negative number on error or 0 on success
+ */
+int mxc_dma_free(int channel_num)
+{
+ mxc_dma_channel_t *dma;
+ mx2_dma_priv_t *dma_private;
+
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ dma = g_dma_channels + channel_num;
+ dma_private = (mx2_dma_priv_t *) dma->private;
+ if (dma_private == NULL) {
+ printk(KERN_ERR
+ "Free dma %d which is not completed initialization \n",
+ channel_num);
+ return -EFAULT;
+ }
+ if (dma->lock) {
+ if (dma->active) { /*Channel is busy */
+ mxc_dma_disable(channel_num);
+ }
+
+ dma_private = (mx2_dma_priv_t *) dma->private;
+
+ enable_dma_clk();
+ mask_dma_interrupt(channel_num);
+ disable_dma_clk();
+
+ free_irq(dma_private->dma_irq, (void *)dma);
+ put_dma_channel(channel_num);
+ }
+ return 0;
+}
+
+/*!
+ * This function would just configure the buffers specified by the user into
+ * dma channel. The caller must call mxc_dma_enable to start this transfer.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @param dma_buf an array of physical addresses to the user defined
+ * buffers. The caller must guarantee the dma_buf is
+ * available until the transfer is completed.
+ * @param num_buf number of buffers in the array
+ * @param mode specifies whether this is READ or WRITE operation
+ * @return This function returns a negative number on error if buffer could not be
+ * added with DMA for transfer. On Success, it returns 0
+ */
+int mxc_dma_config(int channel_num, mxc_dma_requestbuf_t * dma_buf, int num_buf,
+ mxc_dma_mode_t mode)
+{
+ mxc_dma_channel_t *dma;
+ mx2_dma_priv_t *dma_private;
+
+ if ((dma_buf == NULL) || (num_buf < 1)) {
+ return -EINVAL;
+ }
+
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ dma = g_dma_channels + channel_num;
+ dma_private = (mx2_dma_priv_t *) dma->private;
+ if (dma_private == NULL) {
+ printk(KERN_ERR
+ "config dma %d which is not completed initialization \n",
+ channel_num);
+ return -EFAULT;
+ }
+
+ if (dma->lock == 0) {
+ return -ENODEV;
+ }
+
+ /*TODO: dma chainning can not support on bi-dir channel */
+ if (dma_private->dma_chaining && (dma->mode != mode)) {
+ return -EINVAL;
+ }
+
+ /*TODO: fill dma buffer into driver .
+ * If driver is no enought buffer to save them , it will return -EBUSY
+ */
+ if (fill_dma_bd(dma, dma_buf, num_buf, mode)) {
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function would just configure the scatterlist specified by the
+ * user into dma channel. This is a slight variation of mxc_dma_config(),
+ * it is provided for the convenience of drivers that have a scatterlist
+ * passed into them. It is the calling driver's responsibility to have the
+ * correct physical address filled in the "dma_address" field of the
+ * scatterlist.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @param sg a scatterlist of buffers. The caller must guarantee
+ * the dma_buf is available until the transfer is
+ * completed.
+ * @param num_buf number of buffers in the array
+ * @param num_of_bytes total number of bytes to transfer. If set to 0, this
+ * would imply to use the length field of the scatterlist
+ * for each DMA transfer. Else it would calculate the size
+ * for each DMA transfer.
+ * @param mode specifies whether this is READ or WRITE operation
+ * @return This function returns a negative number on error if buffer could not
+ * be added with DMA for transfer. On Success, it returns 0
+ */
+int mxc_dma_sg_config(int channel_num, struct scatterlist *sg,
+ int num_buf, int num_of_bytes, mxc_dma_mode_t mode)
+{
+ mxc_dma_channel_t *dma;
+ mx2_dma_priv_t *dma_private;
+
+ if ((sg == NULL) || (num_buf < 1) || (num_of_bytes < 0)) {
+ return -EINVAL;
+ }
+
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ dma = g_dma_channels + channel_num;
+ dma_private = (mx2_dma_priv_t *) dma->private;
+ if (dma_private == NULL) {
+ printk(KERN_ERR
+ "config_sg dma %d which is not completed initialization \n",
+ channel_num);
+ return -EFAULT;
+ }
+
+ if (dma->lock == 0) {
+ return -ENODEV;
+ }
+
+ /*TODO: dma chainning can not support on bi-dir channel */
+ if (dma_private->dma_chaining && (dma->mode != mode)) {
+ return -EINVAL;
+ }
+
+ /*TODO: fill dma buffer into driver .
+ * If driver is no enought buffer to save them , it will return -EBUSY
+ */
+ if (fill_dma_bd_by_sg(dma, sg, num_buf, num_of_bytes, mode)) {
+ return -EBUSY;
+ }
+ return 0;
+}
+
+/*!
+ * This function is provided if the driver would like to set/change its
+ * callback function.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @param callback a callback function to provide notification on transfer
+ * completion, user could specify NULL if he does not wish
+ * to be notified
+ * @param arg an argument that gets passed in to the callback
+ * function, used by the user to do any driver specific
+ * operations.
+ * @return this function returns an error if the callback could not be set
+ * for the channel
+ */
+int mxc_dma_callback_set(int channel_num, mxc_dma_callback_t callback,
+ void *arg)
+{
+ mxc_dma_channel_t *dma;
+
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+ dma = g_dma_channels + channel_num;
+
+ if (!dma->lock) {
+ return -ENODEV;
+ }
+
+ if (dma->active) {
+ return -EBUSY;
+ }
+ dma->cb_fn = callback;
+ dma->cb_args = arg;
+ return 0;
+
+}
+
+/*!
+ * This stops the DMA channel and any ongoing transfers. Subsequent use of
+ * mxc_dma_enable() will restart the channel and restart the transfer.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @return returns a negative number on error or 0 on success
+ */
+int mxc_dma_disable(int channel_num)
+{
+ mxc_dma_channel_t *dma;
+ mx2_dma_priv_t *priv;
+ unsigned long ctrl_val;
+
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ dma = g_dma_channels + channel_num;
+
+ if (dma->lock == 0) {
+ return -EINVAL;
+ }
+
+ if (!dma->active) {
+ return -EINVAL;
+ }
+
+ priv = (mx2_dma_priv_t *) dma->private;
+ if (priv == NULL) {
+ printk(KERN_ERR "disable a uncompleted dma channel %d\n",
+ channel_num);
+ return -EFAULT;
+ }
+
+ dma->active = 0;
+ enable_dma_clk();
+
+ __clear_dma_interrupt(channel_num);
+ ctrl_val =
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_CCR(channel_num));
+ ctrl_val &= ~DMA_CTL_CEN; /* clear CEN bit */
+ __raw_writel(ctrl_val,
+ IO_ADDRESS(DMA_BASE_ADDR) + DMA_CCR(channel_num));
+ disable_dma_clk();
+ atomic_dec(&g_dma_actived);
+
+ /*TODO: Clear all request buffers */
+ flush_dma_bd(priv);
+ return 0;
+}
+
+/*!
+ * This starts DMA transfer. Or it restarts DMA on a stopped channel
+ * previously stopped with mxc_dma_disable().
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @return returns a negative number on error or 0 on success
+ */
+int mxc_dma_enable(int channel_num)
+{
+ mxc_dma_channel_t *dma;
+ mx2_dma_priv_t *priv;
+
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ dma = g_dma_channels + channel_num;
+
+ if (dma->lock == 0) {
+ return -EINVAL;
+ }
+
+ priv = (mx2_dma_priv_t *) dma->private;
+ if (priv == NULL) {
+ printk(KERN_ERR "enable a uncompleted dma channel %d\n",
+ channel_num);
+ return -EFAULT;
+ }
+
+ if (dma->active) {
+ return 0;
+ }
+ dma->active = 1;
+ priv->trans_bytes = 0;
+
+ enable_dma_clk();
+
+ atomic_inc(&g_dma_actived);
+ __clear_dma_interrupt(channel_num);
+
+ setup_dmac(dma);
+ disable_dma_clk();
+ return 0;
+}
+
+/*!
+*@brief Dump DMA registers
+*
+*@param channel Requested channel NO.
+*@return none
+*/
+
+void mxc_dump_dma_register(int channel)
+{
+ mxc_dma_channel_t *dma = &g_dma_channels[channel];
+ mx2_dma_priv_t *priv = (mx2_dma_priv_t *) dma->private;
+ dma_regs_t *dma_base;
+
+ printk(KERN_INFO "======== Dump dma channel %d \n", channel);
+ if ((unsigned)channel >= MXC_DMA_CHANNELS) {
+ printk(KERN_INFO "Channel number is invalid \n");
+ return;
+ }
+ if (!dma->lock) {
+ printk(KERN_INFO "Channel is not allocated \n");
+ return;
+ }
+
+ printk(KERN_INFO "g_dma_actived = %d\n", atomic_read(&g_dma_actived));
+
+ enable_dma_clk();
+ dma_base = (dma_regs_t *) (priv->dma_base);
+ printk(KERN_INFO "DMA COMMON REGISTER\n");
+ printk(KERN_INFO "DMA CONTROL DMA_DCR: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR));
+ printk(KERN_INFO "DMA Interrupt status DMA_DISR: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DISR));
+ printk(KERN_INFO "DMA Interrupt Mask DMA_DIMR: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DIMR));
+ printk(KERN_INFO "DMA Burst Time Out DMA_DBTOSR: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOSR));
+ printk(KERN_INFO "DMA request Time Out DMA_DRTOSR: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DRTOSR));
+ printk(KERN_INFO "DMA Transfer Error DMA_DSESR: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DSESR));
+ printk(KERN_INFO "DMA DMA_Overflow DMA_DBOSR: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBOSR));
+ printk(KERN_INFO "DMA Burst Time OutCtl DMA_BurstTOCtl: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOCR));
+
+ printk(KERN_INFO "DMA 2D X size: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_XSRA));
+ printk(KERN_INFO "DMA 2D Y size: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_YSRA));
+ printk(KERN_INFO "DMA 2D Z size: %08x\n",
+ __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_WSRA));
+
+ printk(KERN_INFO "DMA Chan %2d Sourc SourceAddr: %08x\n", channel,
+ __raw_readl(&(dma_base->SourceAddr)));
+ printk(KERN_INFO "DMA Chan %2d dest DestAddr: %08x\n", channel,
+ __raw_readl(&(dma_base->DestAddr)));
+ printk(KERN_INFO "DMA Chan %2d count Count: %08x\n", channel,
+ __raw_readl(&(dma_base->Count)));
+ printk(KERN_INFO "DMA Chan %2d Ctl Ctl: %08x\n", channel,
+ __raw_readl(&(dma_base->Ctl)));
+ printk(KERN_INFO "DMA Chan %2d request RequestSource: %08x\n",
+ channel, __raw_readl(&(dma_base->RequestSource)));
+ printk(KERN_INFO "DMA Chan %2d burstL BurstLength: %08x\n", channel,
+ __raw_readl(&(dma_base->BurstLength)));
+ printk(KERN_INFO "DMA Chan %2d requestTO ReqTimeout: %08x\n", channel,
+ __raw_readl(&(dma_base->ReqTimeout)));
+ printk(KERN_INFO "DMA Chan %2d BusUtilt BusUtilt: %08x\n", channel,
+ __raw_readl(&(dma_base->BusUtilt)));
+
+ disable_dma_clk();
+}
+
+#ifdef DMA_PM
+
+static int channel_in_use(void)
+{
+ int i;
+ for (i = 0; i < MXC_DMA_CHANNELS; i++) {
+ if (dma_chan[i].lock)
+ return 1;
+ }
+ return 0;
+}
+
+int mxc_dma_pm_standby(void)
+{
+ unsigned long reg;
+ if (dma_pm_status == DMA_PMST_STANDBY)
+ return 0;
+
+ if (!channel_in_use()) {
+ /*Disable DMA */
+ __disable_dma_clk();
+ dma_pm_status = DMA_PMST_STANDBY;
+ return 0;
+ }
+ return -1;
+}
+
+int mxc_dma_pm_resume(void)
+{
+ unsigned long reg;
+ if (dma_pm_status == DMA_PMST_RESUME)
+ return 0;
+
+ /*Enable HCLK_DMA and DMA(ipg clock) */
+ dma_pm_status = DMA_PMST_RESUME;
+ return 0;
+}
+
+int mxc_dma_pm_suspend(void)
+{
+ unsigned long reg;
+ if (dma_pm_status == DMA_PMST_SUSPEND)
+ return 0;
+
+ if (!channel_in_use()) {
+ /*Disable DMA */
+ __disable_dma_clk();
+ dma_pm_status = DMA_PMST_SUSPEND;
+ return 0;
+ }
+ return -1;
+}
+
+int mxc_dma_pm_handler(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+ int ret = 0;
+ switch (rqst) {
+ /*APM doesn't send PM_STANDBY and PM_STANDBY_RESUME request now. */
+ case PM_SUSPEND:
+ ret = dma_pm_suspend();
+ break;
+ case PM_RESUME:
+ ret = dma_pm_resume();
+ break;
+ }
+ return ret;
+}
+
+#endif /*DMA_PM */
+
+int __init mxc_dma_init(void)
+{
+ int i;
+ mxc_dma_channel_t *dma = g_dma_channels;
+ mx2_dma_priv_t *private = g_dma_privates;
+
+ memset(dma, 0, sizeof(mxc_dma_channel_t) * MXC_DMA_CHANNELS);
+ for (i = 0; i < MXC_DMA_CHANNELS; i++, dma++, private++) {
+ dma->channel = i;
+ dma->private = private;
+ private->dma_base =
+ (unsigned int)(IO_ADDRESS(DMA_BASE_ADDR + DMA_CH_BASE(i)));
+ private->dma_irq = i + MXC_DMA_INTR_0; /*Dma channel interrupt number */
+ private->bd_ring = &g_dma_bd_table[i][0];
+ }
+
+ mxc_dma_load_info(g_dma_channels);
+
+ dma_clk = clk_get(NULL, "dma_clk");
+ clk_enable(dma_clk);
+
+ __raw_writel(0x2, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR); /*reset DMA; */
+
+ disable_dma_clk();
+
+ /*use module init because create_proc after init_dma */
+ g_proc_dir = create_proc_entry("dma", 0, NULL);
+ g_proc_dir->read_proc = (read_proc_t *) mxc_get_dma_list;
+ g_proc_dir->data = NULL;
+
+#ifdef DMA_PM
+ /* Register the device with power management. */
+ dma_pm = pm_register(PM_DMA_DEV, PM_SYS_UNKNOWN, dma_pm_handler);
+#endif
+
+ return 0;
+}
+
+arch_initcall(mxc_dma_init);
+
+EXPORT_SYMBOL(mxc_dma_request_ext);
+EXPORT_SYMBOL(mxc_dma_free);
+EXPORT_SYMBOL(mxc_dma_callback_set);
+EXPORT_SYMBOL(mxc_dma_enable);
+EXPORT_SYMBOL(mxc_dma_disable);
+EXPORT_SYMBOL(mxc_dma_config);
+EXPORT_SYMBOL(mxc_dma_sg_config);
+EXPORT_SYMBOL(mxc_dump_dma_register);
diff --git a/arch/arm/plat-mxc/dptc.c b/arch/arm/plat-mxc/dptc.c
new file mode 100644
index 000000000000..6b7f5599909e
--- /dev/null
+++ b/arch/arm/plat-mxc/dptc.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file dptc.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC DPTC module.
+ *
+ * The DPTC driver is designed to control the MXC DPTC hardware.
+ * hardware. Upon initialization, the DPTC driver initializes the DPTC hardware
+ * sets up driver nodes attaches to the DPTC interrupt and initializes internal
+ * data structures. When the DPTC interrupt occurs the driver checks the cause
+ * of the interrupt (lower frequency, increase frequency or emergency) and changes
+ * the CPU voltage according to translation table that is loaded into the driver.
+ * The driver read method is used to read the log buffer.
+ * Driver ioctls are used to change driver parameters and enable/disable the
+ * DVFS operation.
+ *
+ * @ingroup PM
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/clock.h>
+#include <mach/gpio.h>
+#include <mach/hardware.h>
+#include <mach/mxc_dptc.h>
+
+/*
+ * Convenience conversion.
+ * Here atm, maybe there is somewhere better for this.
+ */
+#define mV_to_uV(mV) (mV * 1000)
+#define uV_to_mV(uV) (uV / 1000)
+#define V_to_uV(V) (mV_to_uV(V * 1000))
+#define uV_to_V(uV) (uV_to_mV(uV) / 1000)
+
+enum {
+ DPTC_PTVAI_NOCHANGE = 0x0,
+ DPTC_PTVAI_DECREASE,
+ DPTC_PTVAI_INCREASE,
+ DPTC_PTVAI_EMERG,
+};
+
+struct device *dev_data0;
+struct device *dev_data1;
+
+/*!
+ * In case the MXC device has multiple DPTC modules, this structure is used to
+ * store information specific to each DPTC module.
+ */
+struct dptc_device {
+ /* DPTC delayed work */
+ struct delayed_work dptc_work;
+ /* DPTC spinlock */
+ spinlock_t lock;
+ /* DPTC regulator */
+ struct regulator *dptc_reg;
+ /* DPTC clock */
+ struct clk *dptc_clk;
+ /* DPTC is active flag */
+ int dptc_is_active;
+ /* turbo mode active flag */
+ int turbo_mode_active;
+ /* DPTC current working point */
+ int curr_wp;
+ /* DPTC vai bits */
+ u32 ptvai;
+ /* The interrupt number used by the DPTC device */
+ int irq;
+ /* DPTC platform data pointer */
+ struct mxc_dptc_data *dptc_platform_data;
+};
+
+static void update_dptc_wp(struct dptc_device *drv_data, u32 wp)
+{
+ struct mxc_dptc_data *dptc_data = drv_data->dptc_platform_data;
+ int voltage_uV;
+ int ret = 0;
+
+ voltage_uV = dptc_data->dptc_wp_allfreq[wp].voltage * 1000;
+
+ __raw_writel(dptc_data->dptc_wp_allfreq[wp].dcvr0,
+ dptc_data->dcvr0_reg_addr);
+ __raw_writel(dptc_data->dptc_wp_allfreq[wp].dcvr1,
+ dptc_data->dcvr0_reg_addr + 0x4);
+ __raw_writel(dptc_data->dptc_wp_allfreq[wp].dcvr2,
+ dptc_data->dcvr0_reg_addr + 0x8);
+ __raw_writel(dptc_data->dptc_wp_allfreq[wp].dcvr3,
+ dptc_data->dcvr0_reg_addr + 0xC);
+
+ /* Set the voltage */
+ ret = regulator_set_voltage(drv_data->dptc_reg, voltage_uV, voltage_uV);
+ if (ret < 0)
+ printk(KERN_DEBUG "COULD NOT SET VOLTAGE!!!!!\n");
+
+ pr_debug("dcvr0-3: 0x%x, 0x%x, 0x%x, 0x%x; vol: %d\n",
+ dptc_data->dptc_wp_allfreq[wp].dcvr0,
+ dptc_data->dptc_wp_allfreq[wp].dcvr1,
+ dptc_data->dptc_wp_allfreq[wp].dcvr2,
+ dptc_data->dptc_wp_allfreq[wp].dcvr3,
+ dptc_data->dptc_wp_allfreq[wp].voltage);
+}
+
+static irqreturn_t dptc_irq(int irq, void *dev_id)
+{
+ struct device *dev = dev_id;
+ struct dptc_device *drv_data = dev->driver_data;
+ struct mxc_dptc_data *dptc_data = dev->platform_data;
+ u32 dptccr = __raw_readl(dptc_data->dptccr_reg_addr);
+ u32 gpc_cntr = __raw_readl(dptc_data->gpc_cntr_reg_addr);
+
+ gpc_cntr = (gpc_cntr & dptc_data->dptccr);
+
+ if (gpc_cntr) {
+ drv_data->ptvai =
+ (dptccr & dptc_data->vai_mask) >> dptc_data->vai_offset;
+ pr_debug("dptc_irq: vai = 0x%x (0x%x)!!!!!!!\n",
+ drv_data->ptvai, dptccr);
+
+ /* disable DPTC and mask its interrupt */
+ dptccr = (dptccr & ~(dptc_data->dptc_enable_bit)) |
+ (dptc_data->irq_mask);
+ dptccr = (dptccr & ~(dptc_data->dptc_nvcr_bit));
+ __raw_writel(dptccr, dptc_data->dptccr_reg_addr);
+
+ if (drv_data->turbo_mode_active == 1)
+ schedule_delayed_work(&drv_data->dptc_work, 0);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void dptc_workqueue_handler(struct work_struct *work1)
+{
+ struct delayed_work *dptc_work_tmp =
+ container_of(work1, struct delayed_work, work);
+ struct dptc_device *drv_data =
+ container_of(dptc_work_tmp, struct dptc_device, dptc_work);
+ struct mxc_dptc_data *dptc_data = drv_data->dptc_platform_data;
+ u32 dptccr = __raw_readl(dptc_data->dptccr_reg_addr);
+
+ switch (drv_data->ptvai) {
+ case DPTC_PTVAI_DECREASE:
+ drv_data->curr_wp++;
+ break;
+ case DPTC_PTVAI_INCREASE:
+ case DPTC_PTVAI_EMERG:
+ drv_data->curr_wp--;
+ if (drv_data->curr_wp < 0) {
+ /* already max voltage */
+ drv_data->curr_wp = 0;
+ printk(KERN_WARNING "dptc: already maximum voltage\n");
+ }
+ break;
+
+ /* Unknown interrupt cause */
+ default:
+ BUG();
+ }
+
+ if (drv_data->curr_wp > dptc_data->dptc_wp_supported
+ || drv_data->curr_wp < 0) {
+ panic("Can't support this working point: %d\n",
+ drv_data->curr_wp);
+ }
+ update_dptc_wp(drv_data, drv_data->curr_wp);
+
+ /* Enable DPTC and unmask its interrupt */
+ dptccr = (dptccr & ~(dptc_data->irq_mask)) |
+ dptc_data->dptc_nvcr_bit | dptc_data->dptc_enable_bit;
+ __raw_writel(dptccr, dptc_data->dptccr_reg_addr);
+}
+
+/* Start DPTC unconditionally */
+static int start_dptc(struct device *dev)
+{
+ struct mxc_dptc_data *dptc_data = dev->platform_data;
+ struct dptc_device *drv_data = dev->driver_data;
+ u32 dptccr;
+ unsigned long flags;
+ unsigned long clk_rate;
+ int voltage_uV;
+
+ /* Get the voltage */
+ voltage_uV = regulator_get_voltage(drv_data->dptc_reg);
+ drv_data->curr_wp =
+ (dptc_data->dptc_wp_allfreq[0].voltage - (voltage_uV / 1000)) / 25;
+
+ update_dptc_wp(drv_data, drv_data->curr_wp);
+
+ /* Set the voltage */
+ spin_lock_irqsave(&drv_data->lock, flags);
+
+ clk_rate = clk_get_rate(drv_data->dptc_clk);
+
+ if (clk_rate < dptc_data->clk_max_val)
+ goto err;
+
+ if (dptc_data->gpc_irq_bit != 0x0) {
+ /* Enable ARM domain frequency and/or voltage update needed
+ and enable ARM IRQ */
+ __raw_writel(dptc_data->gpc_irq_bit | dptc_data->gpc_adu,
+ dptc_data->gpc_cntr_reg_addr);
+ }
+
+ dptccr = __raw_readl(dptc_data->dptccr_reg_addr);
+
+ /* Enable DPTC and unmask its interrupt */
+ dptccr = ((dptccr & ~(dptc_data->irq_mask)) | dptc_data->enable_config);
+
+ __raw_writel(dptccr, dptc_data->dptccr_reg_addr);
+
+ spin_unlock_irqrestore(&drv_data->lock, flags);
+
+ drv_data->dptc_is_active = 1;
+ drv_data->turbo_mode_active = 1;
+
+ pr_info("DPTC has been started \n");
+
+ return 0;
+
+err:
+ spin_unlock_irqrestore(&drv_data->lock, flags);
+ pr_info("DPTC is not enabled\n");
+ return -1;
+}
+
+/* Stop DPTC unconditionally */
+static void stop_dptc(struct device *dev)
+{
+ struct mxc_dptc_data *dptc_data = dev->platform_data;
+ struct dptc_device *drv_data = dev->driver_data;
+ u32 dptccr;
+
+ dptccr = __raw_readl(dptc_data->dptccr_reg_addr);
+
+ /* disable DPTC and mask its interrupt */
+ dptccr = ((dptccr & ~(dptc_data->dptc_enable_bit)) |
+ dptc_data->irq_mask) & (~dptc_data->dptc_nvcr_bit);
+
+ __raw_writel(dptccr, dptc_data->dptccr_reg_addr);
+
+ /* Restore Turbo Mode voltage to highest wp */
+ update_dptc_wp(drv_data, 0);
+ drv_data->curr_wp = 0;
+
+ regulator_put(drv_data->dptc_reg);
+
+ pr_info("DPTC has been stopped\n");
+}
+
+/*
+ This function does not change the working point. It can be
+ called from an interrupt context.
+*/
+void dptc_suspend(int id)
+{
+ struct mxc_dptc_data *dptc_data;
+ struct dptc_device *drv_data;
+ u32 dptccr;
+
+ switch (id) {
+ case DPTC_GP_ID:
+ dptc_data = dev_data0->platform_data;
+ drv_data = dev_data0->driver_data;
+ break;
+ case DPTC_LP_ID:
+ if (dev_data1 == NULL)
+ return;
+
+ dptc_data = dev_data1->platform_data;
+ drv_data = dev_data1->driver_data;
+ break;
+ /* Unknown DPTC ID */
+ default:
+ return;
+ }
+
+ if (!drv_data->dptc_is_active)
+ return;
+
+ dptccr = __raw_readl(dptc_data->dptccr_reg_addr);
+
+ /* Disable DPTC and mask its interrupt */
+ dptccr = (dptccr & ~(dptc_data->dptc_enable_bit)) | dptc_data->irq_mask;
+
+ __raw_writel(dptccr, dptc_data->dptccr_reg_addr);
+}
+EXPORT_SYMBOL(dptc_suspend);
+
+/*
+ This function does not change the working point. It can be
+ called from an interrupt context.
+*/
+void dptc_resume(int id)
+{
+ struct mxc_dptc_data *dptc_data;
+ struct dptc_device *drv_data;
+ u32 dptccr;
+
+ switch (id) {
+ case DPTC_GP_ID:
+ dptc_data = dev_data0->platform_data;
+ drv_data = dev_data0->driver_data;
+ break;
+ case DPTC_LP_ID:
+ if (dev_data1 == NULL)
+ return;
+
+ dptc_data = dev_data1->platform_data;
+ drv_data = dev_data1->driver_data;
+ break;
+ /* Unknown DPTC ID */
+ default:
+ return;
+ }
+
+ if (!drv_data->dptc_is_active)
+ return;
+
+ __raw_writel(dptc_data->dptc_wp_allfreq[0].dcvr0,
+ dptc_data->dcvr0_reg_addr);
+ __raw_writel(dptc_data->dptc_wp_allfreq[0].dcvr1,
+ dptc_data->dcvr0_reg_addr + 0x4);
+ __raw_writel(dptc_data->dptc_wp_allfreq[0].dcvr2,
+ dptc_data->dcvr0_reg_addr + 0x8);
+ __raw_writel(dptc_data->dptc_wp_allfreq[0].dcvr3,
+ dptc_data->dcvr0_reg_addr + 0xC);
+
+ dptccr = __raw_readl(dptc_data->dptccr_reg_addr);
+
+ /* Enable DPTC and unmask its interrupt */
+ dptccr = (dptccr & ~(dptc_data->irq_mask)) | dptc_data->dptc_enable_bit;
+
+ __raw_writel(dptccr, dptc_data->dptccr_reg_addr);
+}
+EXPORT_SYMBOL(dptc_resume);
+
+/*!
+ * This function is called to put the DPTC in a low power state.
+ *
+ */
+void dptc_disable(struct device *dev)
+{
+ struct dptc_device *drv_data = dev->driver_data;
+
+ if (!(drv_data->dptc_is_active))
+ return;
+
+ stop_dptc(dev);
+ drv_data->dptc_is_active = 0;
+ drv_data->turbo_mode_active = 0;
+}
+
+/*!
+ * This function is called to resume the DPTC from a low power state.
+ *
+ */
+int dptc_enable(struct device *dev)
+{
+ struct dptc_device *drv_data = dev->driver_data;
+
+ if (drv_data->dptc_is_active)
+ return 0;
+
+ return start_dptc(dev);
+}
+
+static ssize_t dptc_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dptc_device *drv_data = dev->driver_data;
+
+ if (drv_data->dptc_is_active)
+ return sprintf(buf, "DPTC is enabled\n");
+ else
+ return sprintf(buf, "DPTC is disabled\n");
+}
+
+static ssize_t dptc_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (strstr(buf, "0") != NULL) {
+ dptc_disable(dev);
+ } else if (strstr(buf, "1") != NULL) {
+ dptc_enable(dev);
+ }
+
+ return size;
+}
+
+static DEVICE_ATTR(enable, 0644, dptc_show, dptc_store);
+
+/*!
+ * This is the probe routine for the DPTC driver.
+ *
+ * @param pdev The platform device structure
+ *
+ * @return The function returns 0 on success
+ *
+ */
+static int __devinit mxc_dptc_probe(struct platform_device *pdev)
+{
+ struct dptc_device *dptc_device_data;
+ int ret = 0;
+ struct resource *res;
+ u32 dptccr = 0;
+ struct clk *ckih_clk;
+ struct mxc_dptc_data *dptc_data = pdev->dev.platform_data;
+
+ if (dptc_data == NULL) {
+ printk(KERN_ERR "DPTC: Pointer to DPTC data is NULL\
+ not started\n");
+ return -1;
+ }
+
+ dptc_device_data = kzalloc(sizeof(struct dptc_device), GFP_KERNEL);
+ if (!dptc_device_data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ /*
+ * Request the DPTC interrupt
+ */
+ dptc_device_data->irq = platform_get_irq(pdev, 0);
+ if (dptc_device_data->irq < 0) {
+ ret = dptc_device_data->irq;
+ goto err1;
+ }
+
+ ret =
+ request_irq(dptc_device_data->irq, dptc_irq, IRQF_SHARED,
+ pdev->name, &pdev->dev);
+ if (ret) {
+ printk(KERN_ERR "DPTC: Unable to attach to DPTC interrupt\n");
+ goto err1;
+ }
+
+ dptc_device_data->curr_wp = 0;
+ dptc_device_data->dptc_is_active = 0;
+ dptc_device_data->turbo_mode_active = 0;
+ dptc_device_data->ptvai = 0;
+
+ dptccr = __raw_readl(dptc_data->dptccr_reg_addr);
+
+ printk(KERN_INFO "DPTC mxc_dptc_probe()\n");
+
+ spin_lock_init(&dptc_device_data->lock);
+
+ if (dptc_data->dptc_wp_allfreq == NULL) {
+ ckih_clk = clk_get(NULL, "ckih");
+ if (cpu_is_mx31() &
+ (mxc_cpu_is_rev(CHIP_REV_2_0) < 0) &
+ (clk_get_rate(ckih_clk) == 27000000))
+ printk(KERN_ERR "DPTC: DPTC not supported on TO1.x \
+ & ckih = 27M\n");
+ else
+ printk(KERN_ERR "DPTC: Pointer to DPTC table is NULL\
+ not started\n");
+ goto err1;
+ }
+
+ dptc_device_data->dptc_reg = regulator_get(NULL, dptc_data->reg_id);
+ if (IS_ERR(dptc_device_data->dptc_reg)) {
+ clk_put(dptc_device_data->dptc_clk);
+ printk(KERN_ERR "%s: failed to get regulator\n", __func__);
+ goto err1;
+ }
+
+ INIT_DELAYED_WORK(&dptc_device_data->dptc_work, dptc_workqueue_handler);
+
+ /* Enable Reference Circuits */
+ dptccr = (dptccr & ~(dptc_data->dcr_mask)) | dptc_data->init_config;
+ __raw_writel(dptccr, dptc_data->dptccr_reg_addr);
+
+ ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_enable.attr);
+ if (ret) {
+ printk(KERN_ERR
+ "DPTC: Unable to register sysdev entry for dptc");
+ goto err1;
+ }
+
+ if (ret != 0) {
+ printk(KERN_ERR "DPTC: Unable to start");
+ goto err1;
+ }
+
+ dptc_device_data->dptc_clk = clk_get(NULL, dptc_data->clk_id);
+
+ if (pdev->id == 0)
+ dev_data0 = &pdev->dev;
+ else
+ dev_data1 = &pdev->dev;
+
+ dptc_device_data->dptc_platform_data = pdev->dev.platform_data;
+
+ /* Set driver data */
+ platform_set_drvdata(pdev, dptc_device_data);
+
+ return 0;
+
+err1:
+ dev_err(&pdev->dev, "Failed to probe DPTC\n");
+ kfree(dptc_device_data);
+ return ret;
+}
+
+/*!
+ * This function is called to put DPTC in a low power state.
+ *
+ * @param pdev the device structure
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_dptc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct dptc_device *drv_data = pdev->dev.driver_data;
+
+ if (drv_data->dptc_is_active)
+ stop_dptc(&pdev->dev);
+
+ return 0;
+}
+
+/*!
+ * This function is called to resume the MU from a low power state.
+ *
+ * @param dev the device structure
+ * @param level the stage in device suspension process that we want the
+ * device to be put in
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_dptc_resume(struct platform_device *pdev)
+{
+ struct dptc_device *drv_data = pdev->dev.driver_data;
+
+ if (drv_data->dptc_is_active)
+ return start_dptc(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver mxc_dptc_driver = {
+ .driver = {
+ .name = "mxc_dptc",
+ .owner = THIS_MODULE,
+ },
+ .probe = mxc_dptc_probe,
+ .suspend = mxc_dptc_suspend,
+ .resume = mxc_dptc_resume,
+};
+
+/*!
+ * This function is called to resume the MU from a low power state.
+ *
+ * @param dev the device structure used to give information on which MU
+ * device (0 through 3 channels) to suspend
+ * @param level the stage in device suspension process that we want the
+ * device to be put in
+ *
+ * @return The function always returns 0.
+ */
+
+static int __init dptc_init(void)
+{
+ if (platform_driver_register(&mxc_dptc_driver) != 0) {
+ printk(KERN_ERR "mxc_dptc_driver register failed\n");
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "DPTC driver module loaded\n");
+
+ return 0;
+}
+
+static void __exit dptc_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&mxc_dptc_driver);
+
+ printk("DPTC driver module unloaded\n");
+}
+
+module_init(dptc_init);
+module_exit(dptc_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("DPTC driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/dvfs_core.c b/arch/arm/plat-mxc/dvfs_core.c
new file mode 100644
index 000000000000..169c9feab697
--- /dev/null
+++ b/arch/arm/plat-mxc/dvfs_core.c
@@ -0,0 +1,925 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file dvfs_core.c
+ *
+ * @brief A simplied driver for the Freescale Semiconductor MXC DVFS module.
+ *
+ * Upon initialization, the DVFS driver initializes the DVFS hardware
+ * sets up driver nodes attaches to the DVFS interrupt and initializes internal
+ * data structures. When the DVFS interrupt occurs the driver checks the cause
+ * of the interrupt (lower frequency, increase frequency or emergency) and
+ * changes the CPU voltage according to translation table that is loaded into
+ * the driver.
+ *
+ * @ingroup PM
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <mach/mxc_dvfs.h>
+
+#define MXC_DVFSTHRS_UPTHR_MASK 0x0FC00000
+#define MXC_DVFSTHRS_UPTHR_OFFSET 22
+#define MXC_DVFSTHRS_DNTHR_MASK 0x003F0000
+#define MXC_DVFSTHRS_DNTHR_OFFSET 16
+#define MXC_DVFSTHRS_PNCTHR_MASK 0x0000003F
+#define MXC_DVFSTHRS_PNCTHR_OFFSET 0
+
+#define MXC_DVFSCOUN_DNCNT_MASK 0x00FF0000
+#define MXC_DVFSCOUN_DNCNT_OFFSET 16
+#define MXC_DVFSCOUN_UPCNT_MASK 0x000000FF
+#define MXC_DVFSCOUN_UPCNT_OFFSET 0
+
+#define MXC_DVFSEMAC_EMAC_MASK 0x000001FF
+#define MXC_DVFSEMAC_EMAC_OFFSET 0
+
+#define MXC_DVFSCNTR_DVFEV 0x10000000
+#define MXC_DVFSCNTR_LBMI 0x08000000
+#define MXC_DVFSCNTR_LBFL 0x06000000
+#define MXC_DVFSCNTR_DVFIS 0x01000000
+#define MXC_DVFSCNTR_FSVAIM 0x00400000
+#define MXC_DVFSCNTR_FSVAI_MASK 0x00300000
+#define MXC_DVFSCNTR_FSVAI_OFFSET 20
+#define MXC_DVFSCNTR_WFIM 0x00080000
+#define MXC_DVFSCNTR_WFIM_OFFSET 19
+#define MXC_DVFSCNTR_MAXF_MASK 0x00040000
+#define MXC_DVFSCNTR_MAXF_OFFSET 18
+#define MXC_DVFSCNTR_MINF_MASK 0x00020000
+#define MXC_DVFSCNTR_MINF_OFFSET 17
+#define MXC_DVFSCNTR_LTBRSR_MASK 0x00000018
+#define MXC_DVFSCNTR_LTBRSR_OFFSET 3
+#define MXC_DVFSCNTR_DVFEN 0x00000001
+
+extern int dvfs_core_is_active;
+extern void setup_pll(void);
+static struct mxc_dvfs_platform_data *dvfs_data;
+static struct device *dvfs_dev;
+static struct cpu_wp *cpu_wp_tbl;
+int dvfs_core_resume;
+int curr_wp;
+int old_wp;
+
+extern int cpufreq_trig_needed;
+struct timeval core_prev_intr;
+
+void dump_dvfs_core_regs(void);
+static struct delayed_work dvfs_core_handler;
+
+/*
+ * Clock structures
+ */
+static struct clk *pll1_sw_clk;
+static struct clk *cpu_clk;
+static struct clk *dvfs_clk;
+static struct regulator *core_regulator;
+
+extern int cpu_wp_nr;
+#ifdef CONFIG_ARCH_MX51
+extern struct cpu_wp *(*get_cpu_wp)(int *wp);
+#endif
+
+enum {
+ FSVAI_FREQ_NOCHANGE = 0x0,
+ FSVAI_FREQ_INCREASE,
+ FSVAI_FREQ_DECREASE,
+ FSVAI_FREQ_EMERG,
+};
+
+/*
+ * Load tracking buffer source: 1 for ld_add; 0 for pre_ld_add; 2 for after EMA
+ */
+#define DVFS_LTBRSR (2 << MXC_DVFSCNTR_LTBRSR_OFFSET)
+
+extern struct dvfs_wp dvfs_core_setpoint[2];
+extern int low_bus_freq_mode;
+extern int high_bus_freq_mode;
+extern int set_low_bus_freq(void);
+extern int set_high_bus_freq(int high_bus_speed);
+extern int low_freq_bus_used(void);
+extern void propagate_rate(struct clk *tclk);
+
+DEFINE_SPINLOCK(mxc_dvfs_core_lock);
+
+static void dvfs_load_config(int set_point)
+{
+ u32 reg;
+ reg = 0;
+
+ reg |= dvfs_core_setpoint[set_point].upthr << MXC_DVFSTHRS_UPTHR_OFFSET;
+ reg |= dvfs_core_setpoint[set_point].downthr <<
+ MXC_DVFSTHRS_DNTHR_OFFSET;
+ reg |= dvfs_core_setpoint[set_point].panicthr;
+ __raw_writel(reg, dvfs_data->dvfs_thrs_reg_addr);
+
+ reg = 0;
+ reg |= dvfs_core_setpoint[set_point].downcnt <<
+ MXC_DVFSCOUN_DNCNT_OFFSET;
+ reg |= dvfs_core_setpoint[set_point].upcnt << MXC_DVFSCOUN_UPCNT_OFFSET;
+ __raw_writel(reg, dvfs_data->dvfs_coun_reg_addr);
+
+ /* Set EMAC value */
+ __raw_writel((dvfs_core_setpoint[set_point].emac <<
+ MXC_DVFSEMAC_EMAC_OFFSET),
+ dvfs_data->dvfs_emac_reg_addr);
+
+
+}
+
+static int set_cpu_freq(int wp)
+{
+ int arm_podf;
+ int podf;
+ int vinc = 0;
+ int ret = 0;
+ int org_cpu_rate;
+ unsigned long rate = 0;
+ int gp_volt = 0;
+ u32 reg;
+ u32 reg1;
+ if (cpu_wp_tbl[wp].pll_rate != cpu_wp_tbl[old_wp].pll_rate) {
+ /* PLL_RELOCK, set ARM_FREQ_SHIFT_DIVIDER */
+ reg = __raw_readl(dvfs_data->ccm_cdcr_reg_addr);
+ reg &= 0xFFFFFFFB;
+ __raw_writel(reg, dvfs_data->ccm_cdcr_reg_addr);
+ org_cpu_rate = clk_get_rate(cpu_clk);
+ rate = cpu_wp_tbl[wp].cpu_rate;
+
+ if (org_cpu_rate == rate)
+ return ret;
+
+ gp_volt = cpu_wp_tbl[wp].cpu_voltage;
+
+ if (gp_volt == 0)
+ return ret;
+ /*Set the voltage for the GP domain. */
+ if (rate > org_cpu_rate) {
+ ret = regulator_set_voltage(core_regulator, gp_volt,
+ gp_volt);
+ if (ret < 0) {
+ printk(KERN_DEBUG "COULD NOT SET GP VOLTAGE\n");
+ return ret;
+ }
+ udelay(dvfs_data->delay_time);
+ }
+ setup_pll();
+ /* START the GPC main control FSM */
+ /* set VINC */
+ reg = __raw_readl(dvfs_data->gpc_vcr_reg_addr);
+ reg &= ~(MXC_GPCVCR_VINC_MASK | MXC_GPCVCR_VCNTU_MASK |
+ MXC_GPCVCR_VCNT_MASK);
+
+ if (rate > org_cpu_rate)
+ reg |= 1 << MXC_GPCVCR_VINC_OFFSET;
+
+ reg |= (1 << MXC_GPCVCR_VCNTU_OFFSET) |
+ (100 << MXC_GPCVCR_VCNT_OFFSET);
+ __raw_writel(reg, dvfs_data->gpc_vcr_reg_addr);
+
+ reg = __raw_readl(dvfs_data->gpc_cntr_reg_addr);
+ reg |= MXC_GPCCNTR_FUPD;
+ reg |= MXC_GPCCNTR_ADU;
+ __raw_writel(reg, dvfs_data->gpc_cntr_reg_addr);
+
+ reg |= MXC_GPCCNTR_STRT;
+ __raw_writel(reg, dvfs_data->gpc_cntr_reg_addr);
+ while (__raw_readl(dvfs_data->gpc_cntr_reg_addr) & 0x4000)
+ udelay(50);
+
+ if (rate < org_cpu_rate) {
+ ret = regulator_set_voltage(core_regulator,
+ gp_volt, gp_volt);
+ if (ret < 0) {
+ printk(KERN_DEBUG
+ "COULD NOT SET GP VOLTAGE!!!!\n");
+ return ret;
+ }
+ udelay(dvfs_data->delay_time);
+ }
+
+ clk_set_rate(cpu_clk, rate);
+ } else {
+ podf = cpu_wp_tbl[wp].cpu_podf;
+ gp_volt = cpu_wp_tbl[wp].cpu_voltage;
+
+ /* Change arm_podf only */
+ /* set ARM_FREQ_SHIFT_DIVIDER */
+ reg = __raw_readl(dvfs_data->ccm_cdcr_reg_addr);
+ reg &= 0xFFFFFFFB;
+ reg |= 1 << 2;
+ __raw_writel(reg, dvfs_data->ccm_cdcr_reg_addr);
+
+ /* Get ARM_PODF */
+ reg = __raw_readl(dvfs_data->ccm_cacrr_reg_addr);
+ arm_podf = reg & 0x07;
+ if (podf == arm_podf) {
+ printk(KERN_DEBUG
+ "No need to change freq and voltage!!!!\n");
+ return 0;
+ }
+
+ /* Check if FSVAI indicate freq up */
+ if (podf < arm_podf) {
+ ret = regulator_set_voltage(core_regulator,
+ gp_volt, gp_volt);
+ if (ret < 0) {
+ printk(KERN_DEBUG
+ "COULD NOT SET GP VOLTAGE!!!!\n");
+ return 0;
+ }
+ udelay(dvfs_data->delay_time);
+ vinc = 1;
+ } else {
+ vinc = 0;
+ }
+
+ arm_podf = podf;
+ /* Set ARM_PODF */
+ reg &= 0xFFFFFFF8;
+ reg |= arm_podf;
+
+ reg1 = __raw_readl(dvfs_data->ccm_cdhipr_reg_addr);
+ if ((reg1 & 0x00010000) == 0)
+ __raw_writel(reg, dvfs_data->ccm_cacrr_reg_addr);
+ else {
+ printk(KERN_DEBUG "ARM_PODF still in busy!!!!\n");
+ return 0;
+ }
+
+ /* START the GPC main control FSM */
+ reg = __raw_readl(dvfs_data->gpc_cntr_reg_addr);
+ reg |= MXC_GPCCNTR_FUPD;
+ /* ADU=1, select ARM domain */
+ reg |= MXC_GPCCNTR_ADU;
+ __raw_writel(reg, dvfs_data->gpc_cntr_reg_addr);
+ /* set VINC */
+ reg = __raw_readl(dvfs_data->gpc_vcr_reg_addr);
+ reg &=
+ ~(MXC_GPCVCR_VINC_MASK | MXC_GPCVCR_VCNTU_MASK |
+ MXC_GPCVCR_VCNT_MASK);
+ reg |= (1 << MXC_GPCVCR_VCNTU_OFFSET) |
+ (100 << MXC_GPCVCR_VCNT_OFFSET) |
+ (vinc << MXC_GPCVCR_VINC_OFFSET);
+ __raw_writel(reg, dvfs_data->gpc_vcr_reg_addr);
+
+ reg = __raw_readl(dvfs_data->gpc_cntr_reg_addr);
+ reg &= (~(MXC_GPCCNTR_ADU | MXC_GPCCNTR_FUPD));
+ reg |= MXC_GPCCNTR_ADU | MXC_GPCCNTR_FUPD | MXC_GPCCNTR_STRT;
+ __raw_writel(reg, dvfs_data->gpc_cntr_reg_addr);
+
+ /* Wait for arm podf Enable */
+ while ((__raw_readl(dvfs_data->gpc_cntr_reg_addr) &
+ MXC_GPCCNTR_STRT) == MXC_GPCCNTR_STRT) {
+ printk(KERN_DEBUG "Waiting arm_podf enabled!\n");
+ udelay(10);
+ }
+
+ if (vinc == 0) {
+ ret = regulator_set_voltage(core_regulator,
+ gp_volt, gp_volt);
+ if (ret < 0) {
+ printk(KERN_DEBUG
+ "COULD NOT SET GP VOLTAGE!!!!\n");
+ return ret;
+ }
+ udelay(dvfs_data->delay_time);
+ }
+
+ propagate_rate(pll1_sw_clk);
+ }
+#if defined(CONFIG_CPU_FREQ)
+ cpufreq_trig_needed = 1;
+#endif
+ old_wp = wp;
+
+ return ret;
+}
+
+static int start_dvfs(void)
+{
+ u32 reg;
+ unsigned long flags;
+
+ if (dvfs_core_is_active)
+ return 0;
+
+ spin_lock_irqsave(&mxc_dvfs_core_lock, flags);
+
+ clk_enable(dvfs_clk);
+
+ dvfs_load_config(0);
+
+ /* config reg GPC_CNTR */
+ reg = __raw_readl(dvfs_data->gpc_cntr_reg_addr);
+
+ reg &= ~MXC_GPCCNTR_GPCIRQM;
+ /* GPCIRQ=1, select ARM IRQ */
+ reg |= MXC_GPCCNTR_GPCIRQ_ARM;
+ /* ADU=1, select ARM domain */
+ reg |= MXC_GPCCNTR_ADU;
+ __raw_writel(reg, dvfs_data->gpc_cntr_reg_addr);
+
+ /* Set PREDIV bits */
+ reg = __raw_readl(dvfs_data->dvfs_cntr_reg_addr);
+ reg = (reg & ~(dvfs_data->prediv_mask));
+ reg |= (dvfs_data->prediv_val) << (dvfs_data->prediv_offset);
+ __raw_writel(reg, dvfs_data->dvfs_cntr_reg_addr);
+
+ /* Enable DVFS interrupt */
+ reg = __raw_readl(dvfs_data->dvfs_cntr_reg_addr);
+ /* FSVAIM=0 */
+ reg = (reg & ~MXC_DVFSCNTR_FSVAIM);
+ /* Set MAXF, MINF */
+ reg = (reg & ~(MXC_DVFSCNTR_MAXF_MASK | MXC_DVFSCNTR_MINF_MASK));
+ reg |= 1 << MXC_DVFSCNTR_MAXF_OFFSET;
+ /* Select ARM domain */
+ reg |= MXC_DVFSCNTR_DVFIS;
+ /* Enable DVFS frequency adjustment interrupt */
+ reg = (reg & ~MXC_DVFSCNTR_FSVAIM);
+ /* Set load tracking buffer register source */
+ reg = (reg & ~MXC_DVFSCNTR_LTBRSR_MASK);
+ reg |= DVFS_LTBRSR;
+ /* Set DIV3CK */
+ reg = (reg & ~(dvfs_data->div3ck_mask));
+ reg |= (dvfs_data->div3ck_val) << (dvfs_data->div3ck_offset);
+ __raw_writel(reg, dvfs_data->dvfs_cntr_reg_addr);
+
+ /* Enable DVFS */
+ reg = __raw_readl(dvfs_data->dvfs_cntr_reg_addr);
+ reg |= MXC_DVFSCNTR_DVFEN;
+ __raw_writel(reg, dvfs_data->dvfs_cntr_reg_addr);
+
+ dvfs_core_is_active = 1;
+
+ spin_unlock_irqrestore(&mxc_dvfs_core_lock, flags);
+
+ printk(KERN_DEBUG "DVFS is started\n");
+
+ return 0;
+}
+
+/*!
+ * This function is called for module initialization.
+ * It sets up the DVFS hardware.
+ * It sets default values for DVFS thresholds and counters. The default
+ * values was chosen from a set of different reasonable values. They was tested
+ * and the default values in the driver gave the best results.
+ * More work should be done to find optimal values.
+ *
+ * @return 0 if successful; non-zero otherwise.
+ *
+ */
+static int init_dvfs_controller(void)
+{
+ /* DVFS loading config */
+ dvfs_load_config(0);
+
+ return 0;
+}
+
+static irqreturn_t dvfs_irq(int irq, void *dev_id)
+{
+ u32 reg;
+
+ /* Check if DVFS0 (ARM) id requesting for freqency/voltage update */
+ if ((__raw_readl(dvfs_data->gpc_cntr_reg_addr) & MXC_GPCCNTR_DVFS0CR) ==
+ 0)
+ return IRQ_NONE;
+
+ /* Mask DVFS irq */
+ reg = __raw_readl(dvfs_data->dvfs_cntr_reg_addr);
+ /* FSVAIM=1 */
+ reg |= MXC_DVFSCNTR_FSVAIM;
+ __raw_writel(reg, dvfs_data->dvfs_cntr_reg_addr);
+
+ /* Mask GPC1 irq */
+ reg = __raw_readl(dvfs_data->gpc_cntr_reg_addr);
+ reg |= MXC_GPCCNTR_GPCIRQM | 0x1000000;
+ __raw_writel(reg, dvfs_data->gpc_cntr_reg_addr);
+
+ schedule_delayed_work(&dvfs_core_handler, 0);
+ return IRQ_HANDLED;
+}
+
+static void dvfs_core_work_handler(struct work_struct *work)
+{
+ u32 fsvai;
+ u32 reg;
+ u32 curr_cpu;
+ int ret = 0;
+ int maxf = 0, minf = 0;
+ int low_freq_bus_ready = 0;
+ int bus_incr = 0, cpu_dcr = 0;
+
+ low_freq_bus_ready = low_freq_bus_used();
+
+
+ /* Check DVFS frequency adjustment interrupt status */
+ reg = __raw_readl(dvfs_data->dvfs_cntr_reg_addr);
+ fsvai = (reg & MXC_DVFSCNTR_FSVAI_MASK) >> MXC_DVFSCNTR_FSVAI_OFFSET;
+ /* Check FSVAI, FSVAI=0 is error */
+ if (fsvai == FSVAI_FREQ_NOCHANGE) {
+ /* Do nothing. Freq change is not required */
+ goto END;
+ }
+
+ curr_cpu = clk_get_rate(cpu_clk);
+
+ /* If FSVAI indicate freq down,
+ check arm-clk is not in lowest frequency 200 MHz */
+ if (fsvai == FSVAI_FREQ_DECREASE) {
+ if (curr_cpu == cpu_wp_tbl[cpu_wp_nr - 1].cpu_rate) {
+ minf = 1;
+ if (low_bus_freq_mode)
+ goto END;
+ } else {
+ /* freq down */
+ curr_wp++;
+ if (curr_wp >= cpu_wp_nr) {
+ curr_wp = cpu_wp_nr - 1;
+ goto END;
+ }
+
+ if (curr_wp == cpu_wp_nr - 1 && !low_freq_bus_ready) {
+ minf = 1;
+ dvfs_load_config(1);
+ } else {
+ cpu_dcr = 1;
+ }
+ }
+ } else {
+ if (curr_cpu == cpu_wp_tbl[0].cpu_rate) {
+ maxf = 1;
+ goto END;
+ } else {
+ if (low_bus_freq_mode) {
+ /* bump up LP freq first. */
+ bus_incr = 1;
+ dvfs_load_config(2);
+ } else {
+ /* freq up */
+ curr_wp = 0;
+ maxf = 1;
+ dvfs_load_config(0);
+ }
+ }
+ }
+
+ low_freq_bus_ready = low_freq_bus_used();
+ if ((curr_wp == cpu_wp_nr - 1) && (!low_bus_freq_mode)
+ && (low_freq_bus_ready) && !bus_incr) {
+ if (cpu_dcr)
+ ret = set_cpu_freq(curr_wp);
+ if (!cpu_dcr) {
+ set_low_bus_freq();
+ dvfs_load_config(3);
+ } else {
+ dvfs_load_config(2);
+ cpu_dcr = 0;
+ }
+ } else {
+ if (low_bus_freq_mode)
+ set_high_bus_freq(0);
+
+ if (!bus_incr)
+ ret = set_cpu_freq(curr_wp);
+ bus_incr = 0;
+ }
+
+#if defined(CONFIG_CPU_FREQ)
+ if (cpufreq_trig_needed == 1) {
+ cpufreq_trig_needed = 0;
+ cpufreq_update_policy(0);
+ }
+#endif
+
+END: /* Set MAXF, MINF */
+ reg = __raw_readl(dvfs_data->dvfs_cntr_reg_addr);
+ reg = (reg & ~(MXC_DVFSCNTR_MAXF_MASK | MXC_DVFSCNTR_MINF_MASK));
+ reg |= maxf << MXC_DVFSCNTR_MAXF_OFFSET;
+ reg |= minf << MXC_DVFSCNTR_MINF_OFFSET;
+
+ /* Enable DVFS interrupt */
+ /* FSVAIM=0 */
+ reg = (reg & ~MXC_DVFSCNTR_FSVAIM);
+ reg |= FSVAI_FREQ_NOCHANGE;
+ /* LBFL=1 */
+ reg = (reg & ~MXC_DVFSCNTR_LBFL);
+ reg |= MXC_DVFSCNTR_LBFL;
+ __raw_writel(reg, dvfs_data->dvfs_cntr_reg_addr);
+ /*Unmask GPC1 IRQ */
+ reg = __raw_readl(dvfs_data->gpc_cntr_reg_addr);
+ reg &= ~MXC_GPCCNTR_GPCIRQM;
+ __raw_writel(reg, dvfs_data->gpc_cntr_reg_addr);
+}
+
+
+/*!
+ * This function disables the DVFS module.
+ */
+static void stop_dvfs(void)
+{
+ u32 reg = 0;
+ unsigned long flags;
+ u32 curr_cpu;
+
+ if (dvfs_core_is_active) {
+
+ /* Mask dvfs irq, disable DVFS */
+ reg = __raw_readl(dvfs_data->dvfs_cntr_reg_addr);
+ /* FSVAIM=1 */
+ reg |= MXC_DVFSCNTR_FSVAIM;
+ __raw_writel(reg, dvfs_data->dvfs_cntr_reg_addr);
+
+ curr_wp = 0;
+ if (!high_bus_freq_mode)
+ set_high_bus_freq(1);
+
+ curr_cpu = clk_get_rate(cpu_clk);
+ if (curr_cpu != cpu_wp_tbl[curr_wp].cpu_rate) {
+ set_cpu_freq(curr_wp);
+#if defined(CONFIG_CPU_FREQ)
+ if (cpufreq_trig_needed == 1) {
+ cpufreq_trig_needed = 0;
+ cpufreq_update_policy(0);
+ }
+#endif
+ }
+ spin_lock_irqsave(&mxc_dvfs_core_lock, flags);
+
+ reg = __raw_readl(dvfs_data->dvfs_cntr_reg_addr);
+ reg = (reg & ~MXC_DVFSCNTR_DVFEN);
+ __raw_writel(reg, dvfs_data->dvfs_cntr_reg_addr);
+
+ spin_unlock_irqrestore(&mxc_dvfs_core_lock, flags);
+
+ dvfs_core_is_active = 0;
+
+ clk_disable(dvfs_clk);
+ }
+
+ printk(KERN_DEBUG "DVFS is stopped\n");
+}
+void dump_dvfs_core_regs()
+{
+ struct timeval cur;
+ u32 diff = 0;
+ if (core_prev_intr.tv_sec == 0)
+ do_gettimeofday(&core_prev_intr);
+ else {
+ do_gettimeofday(&cur);
+ diff = (cur.tv_sec - core_prev_intr.tv_sec)*1000000
+ + (cur.tv_usec - core_prev_intr.tv_usec);
+ core_prev_intr = cur;
+ }
+ if (diff < 90000)
+ printk(KERN_DEBUG "diff = %d\n", diff);
+
+ printk(KERN_INFO "THRS = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr));
+ printk(KERN_INFO "COUNT = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x04));
+ printk(KERN_INFO "SIG1 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x08));
+ printk(KERN_INFO "SIG0 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x0c));
+ printk(KERN_INFO "GPC0 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x10));
+ printk(KERN_INFO "GPC1 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x14));
+ printk(KERN_INFO "GPBT = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x18));
+ printk(KERN_INFO "EMAC = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x1c));
+ printk(KERN_INFO "CNTR = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x20));
+ printk(KERN_INFO "LTR0_0 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x24));
+ printk(KERN_INFO "LTR0_1 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x28));
+ printk(KERN_INFO "LTR1_0 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x2c));
+ printk(KERN_DEBUG "LTR1_1 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x30));
+ printk(KERN_INFO "PT0 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x34));
+ printk(KERN_INFO "PT1 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x38));
+ printk(KERN_INFO "PT2 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x3c));
+ printk(KERN_INFO "PT3 = 0x%08x\n",
+ __raw_readl(dvfs_data->dvfs_thrs_reg_addr+0x40));
+}
+
+static ssize_t downthreshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", dvfs_core_setpoint[0].downthr);
+}
+
+static ssize_t downthreshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int ret = 0;
+ int val;
+ ret = sscanf(buf, "%u", &val);
+ dvfs_core_setpoint[0].downthr = val;
+
+ return size;
+}
+
+static ssize_t downcount_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", dvfs_core_setpoint[0].downcnt);
+}
+
+static ssize_t downcount_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int ret = 0;
+ int val;
+ ret = sscanf(buf, "%u", &val);
+ dvfs_core_setpoint[0].downcnt = val;
+
+ return size;
+}
+
+
+static ssize_t dvfs_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (dvfs_core_is_active)
+ return sprintf(buf, "DVFS is enabled\n");
+ else
+ return sprintf(buf, "DVFS is disabled\n");
+}
+
+static ssize_t dvfs_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (strstr(buf, "1") != NULL) {
+ if (start_dvfs() != 0)
+ printk(KERN_ERR "Failed to start DVFS\n");
+ } else if (strstr(buf, "0") != NULL)
+ stop_dvfs();
+
+ return size;
+}
+
+static ssize_t dvfs_regs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (dvfs_core_is_active)
+ dump_dvfs_core_regs();
+ return 0;
+}
+
+static ssize_t dvfs_regs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ if (dvfs_core_is_active)
+ dump_dvfs_core_regs();
+ return 0;
+
+ return size;
+}
+
+static DEVICE_ATTR(enable, 0644, dvfs_enable_show, dvfs_enable_store);
+static DEVICE_ATTR(show_regs, 0644, dvfs_regs_show, dvfs_regs_store);
+
+static DEVICE_ATTR(down_threshold, 0644, downthreshold_show,
+ downthreshold_store);
+static DEVICE_ATTR(down_count, 0644, downcount_show, downcount_store);
+
+/*!
+ * This is the probe routine for the DVFS driver.
+ *
+ * @param pdev The platform device structure
+ *
+ * @return The function returns 0 on success
+ */
+static int __devinit mxc_dvfs_core_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct resource *res;
+ int irq;
+
+ printk(KERN_INFO "mxc_dvfs_core_probe\n");
+ dvfs_dev = &pdev->dev;
+ dvfs_data = pdev->dev.platform_data;
+
+ INIT_DELAYED_WORK(&dvfs_core_handler, dvfs_core_work_handler);
+
+ pll1_sw_clk = clk_get(NULL, "pll1_sw_clk");
+ if (IS_ERR(pll1_sw_clk)) {
+ printk(KERN_INFO "%s: failed to get pll1_sw_clk\n", __func__);
+ return PTR_ERR(pll1_sw_clk);
+ }
+
+ cpu_clk = clk_get(NULL, dvfs_data->clk1_id);
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_ERR "%s: failed to get cpu clock\n", __func__);
+ return PTR_ERR(cpu_clk);
+ }
+
+ dvfs_clk = clk_get(NULL, dvfs_data->clk2_id);
+ if (IS_ERR(dvfs_clk)) {
+ printk(KERN_ERR "%s: failed to get dvfs clock\n", __func__);
+ return PTR_ERR(dvfs_clk);
+ }
+
+ core_regulator = regulator_get(NULL, dvfs_data->reg_id);
+ if (IS_ERR(core_regulator)) {
+ clk_put(cpu_clk);
+ clk_put(dvfs_clk);
+ printk(KERN_ERR "%s: failed to get gp regulator\n", __func__);
+ return PTR_ERR(core_regulator);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ err = -ENODEV;
+ goto err1;
+ }
+
+ /*
+ * Request the DVFS interrupt
+ */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ err = irq;
+ goto err1;
+ }
+
+ /* request the DVFS interrupt */
+ err = request_irq(irq, dvfs_irq, IRQF_SHARED, "dvfs", dvfs_dev);
+ if (err)
+ printk(KERN_ERR
+ "DVFS: Unable to attach to DVFS interrupt,err = %d",
+ err);
+
+ clk_enable(dvfs_clk);
+ err = init_dvfs_controller();
+ if (err) {
+ printk(KERN_ERR "DVFS: Unable to initialize DVFS");
+ return err;
+ }
+ clk_disable(dvfs_clk);
+
+ err = sysfs_create_file(&dvfs_dev->kobj, &dev_attr_enable.attr);
+ if (err) {
+ printk(KERN_ERR
+ "DVFS: Unable to register sysdev entry for DVFS");
+ return err;
+ }
+
+ err = sysfs_create_file(&dvfs_dev->kobj, &dev_attr_show_regs.attr);
+ if (err) {
+ printk(KERN_ERR
+ "DVFS: Unable to register sysdev entry for DVFS");
+ return err;
+ }
+
+
+ err = sysfs_create_file(&dvfs_dev->kobj, &dev_attr_down_threshold.attr);
+ if (err) {
+ printk(KERN_ERR
+ "DVFS: Unable to register sysdev entry for DVFS");
+ return err;
+ }
+
+ err = sysfs_create_file(&dvfs_dev->kobj, &dev_attr_down_count.attr);
+ if (err) {
+ printk(KERN_ERR
+ "DVFS: Unable to register sysdev entry for DVFS");
+ return err;
+ }
+
+ /* Set the current working point. */
+ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
+ old_wp = 0;
+ curr_wp = 0;
+ dvfs_core_resume = 0;
+ cpufreq_trig_needed = 0;
+
+ return err;
+
+err1:
+ dev_err(&pdev->dev, "Failed to probe DVFS CORE\n");
+ return err;
+}
+
+/*!
+ * This function is called to put DVFS in a low power state.
+ *
+ * @param pdev the device structure
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_dvfs_core_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ if (dvfs_core_is_active) {
+ dvfs_core_resume = 1;
+ stop_dvfs();
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is called to resume the MU from a low power state.
+ *
+ * @param dev the device structure
+ * @param level the stage in device suspension process that we want the
+ * device to be put in
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_dvfs_core_resume(struct platform_device *pdev)
+{
+ if (dvfs_core_resume) {
+ dvfs_core_resume = 0;
+ start_dvfs();
+ }
+
+ return 0;
+}
+
+static struct platform_driver mxc_dvfs_core_driver = {
+ .driver = {
+ .name = "mxc_dvfs_core",
+ },
+ .probe = mxc_dvfs_core_probe,
+ .suspend = mxc_dvfs_core_suspend,
+ .resume = mxc_dvfs_core_resume,
+};
+
+static int __init dvfs_init(void)
+{
+ if (platform_driver_register(&mxc_dvfs_core_driver) != 0) {
+ printk(KERN_ERR "mxc_dvfs_core_driver register failed\n");
+ return -ENODEV;
+ }
+
+ dvfs_core_is_active = 0;
+ printk(KERN_INFO "DVFS driver module loaded\n");
+ return 0;
+}
+
+static void __exit dvfs_cleanup(void)
+{
+ stop_dvfs();
+
+ /* release the DVFS interrupt */
+ free_irq(MXC_INT_GPC1, NULL);
+
+ sysfs_remove_file(&dvfs_dev->kobj, &dev_attr_enable.attr);
+
+ /* Unregister the device structure */
+ platform_driver_unregister(&mxc_dvfs_core_driver);
+
+ clk_put(cpu_clk);
+ clk_put(dvfs_clk);
+
+ dvfs_core_is_active = 0;
+ printk(KERN_INFO "DVFS driver module unloaded\n");
+
+}
+
+module_init(dvfs_init);
+module_exit(dvfs_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("DVFS driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/entry-pm.S b/arch/arm/plat-mxc/entry-pm.S
new file mode 100644
index 000000000000..4a3af0e16191
--- /dev/null
+++ b/arch/arm/plat-mxc/entry-pm.S
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file plat-mxc/entry-pm.S
+ *
+ * @brief This file contains common pm entry .
+ *
+ * @ingroup MXC_PM
+ */
+
+#include <asm/assembler.h>
+#include <asm/ptrace.h>
+#include <asm/memory.h>
+#include <asm/system.h>
+#include <mach/hardware.h>
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
+#include <asm/proc-fns.h>
+#include <asm/vfpmacros.h>
+
+#define WAIT_MODE 111
+#define DOZE_MODE 112
+#define STOP_MODE 113
+#define DSM_MODE 114
+
+#define PM_XLOAD_SIZE 0x04
+#define PM_XLOAD_ENTRY 0x08
+#define PM_XLOAD_SUSPEND_MODE 0x0C
+#define PM_XLOAD_CORE_SP 0x10
+
+#define PROCINFO_PROC_FNS 36
+#define PROC_FIN_FN 12
+#define PROC_IDLE_FN 20
+
+#ifdef CONFIG_FIQ
+#define ARM_CONTEXT_SIZE 12
+#else
+#define ARM_CONTEXT_SIZE 8
+#endif
+
+#ifdef CONFIG_PM_VERBOSE
+resume_str:
+ .string "Resume from DSM..."
+ .size resume_str, . - resume_str
+
+.macro show_resume_str
+ ldr r0, =resume_str
+ bl printk
+.endm
+
+#else
+.macro show_resume_str
+.endm
+#endif
+
+ .data
+ .align 3
+arm_core_context:
+ .rept ARM_CONTEXT_SIZE
+ .long 0
+ .endr
+
+#ifdef CONFIG_VFP
+ .text
+ .align 5
+arm_vfp_save:
+ mov ip, sp
+ stmdb sp!, {r0-r8, fp, ip, lr, pc}
+ sub fp, ip, #4
+ mov r1, #THREAD_SIZE
+ sub r1, r1, #1
+ bic r0, sp, r1
+ ldr r8, [r0, #TI_CPU]
+ add r4, r0, #TI_VFPSTATE
+
+ ldr r3, =last_VFP_context
+ VFPFMRX r2, FPEXC
+ tst r2, #FPEXC_EN
+ bne 1f
+
+ ldr r4, [r3, r8, lsl #2]
+ cmp r4, #0
+ beq dead_vfp
+1:
+ bic r1, r2, #FPEXC_EN
+ VFPFMXR FPEXC, r1
+ /*TODO: SMP */
+ VFPFSTMIA r4, r1
+ VFPFMRX r5, FPSCR
+ tst r2, #FPEXC_EX
+ VFPFMRX r6, FPINST, NE
+ tstne r2, #FPEXC_FP2V
+ VFPFMRX r7, FPINST2, NE
+ stmia r4, {r2, r5, r6, r7}
+
+ mov r1, #0
+ str r1, [r3, r8, lsl #2]
+dead_vfp:
+ ldmia sp, {r0-r8, fp, sp, pc}
+#endif
+/*
+ * The function just be called in this file
+ * Current r0 ~r4 are not saved.
+ * Otherwise, the working registers should be saved
+ */
+ .text
+ .align 5
+arm_core_save:
+ mov ip, sp
+ stmdb sp!, {r8, r9, sl, fp, ip, lr, pc}
+ sub fp, ip, #4
+ ldr r0, =arm_core_context
+ mov r3, r0
+ /* SVC mode */
+ mrs r1, spsr @Save spsr
+ mrs r2, cpsr @Save cpsr
+ stmia r0!, {r1, r2}
+ /* Abort mode */
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | ABT_MODE
+ stmia r0!, {sp} @Save stack pointer for abort mode
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | UND_MODE
+ stmia r0!, {sp} @Save stack pointer for undefine mode
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | IRQ_MODE
+ stmia r0!, {sp} @Save stack pointer for irq mode
+#ifdef CONFIG_FIQ
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | FIQ_MODE
+ /*Save general register and sp for fiq mode*/
+ stmia r0!, {r8-r9, sl, fp, ip, sp}
+#endif
+ ldr r0, [r3, #4]
+ msr cpsr_c, r0
+ ldmia sp, {r8-r9, sl, fp, sp, pc}
+
+/*
+ * The function just be called in this file
+ * Current r0 ~r4 are not saved.
+ * Otherwise, the working registers should be saved
+ */
+arm_core_restore:
+ mov ip, sp
+ stmdb sp!, {fp, ip, lr, pc}
+ sub fp, ip, #4
+ ldr r0, =arm_core_context
+ mov r3, r0
+ /* SVC mode */
+ add r0, r0, #8 @skip svc mode
+ /* Abort mode */
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | ABT_MODE
+ ldmia r0!, {sp} @restore stack pointer for abort mode
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | UND_MODE
+ ldmia r0!, {sp} @restore stack pointer for undefine mode
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | IRQ_MODE
+ ldmia r0!, {sp} @restore stack pointer for irq mode
+#ifdef CONFIG_FIQ
+ msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | FIQ_MODE
+ /*Save general register and sp for fiq mode*/
+ ldmia r0!, {r8-r9, sl, fp, ip, sp}
+#endif
+ ldmia r3!, {r1, r2}
+ msr cpsr, r2 @restore cpsr
+ msr spsr, r1 @restore spsr
+ ldmia sp, {fp, sp, pc}
+
+mxc_cp15_context:
+ .rept 16
+ .long 0
+ .endr
+
+ .align 5
+mxc_cp15_restore:
+ /* Physical address */
+ adr r0, mxc_cp15_context
+ ldmia r0, {r1-r9}
+#ifndef CONFIG_PM_DEBUG
+ @Add dynamic check to skip this block when debug
+ sub lr, lr, #PHYS_OFFSET
+ add lr, lr, #PAGE_OFFSET
+#endif
+ mcr p15, 0, r3, c1, c0, 2 @CP Access Register
+ mcr p15, 0, r2, c1, c0, 1 @Aux Control register
+
+#ifndef CONFIG_PM_DEBUG
+ mcr p15, 0, r0, c7, c5, 6 @flush BTAC/BTB
+ mcr p15, 0, r0, c7, c7, 0 @invalidate both caches
+ mcr p15, 0, r0, c8, c7, 0 @Inval TLBs
+#endif
+
+ mcr p15, 0, r4, c13, c0, 0 @PID
+ mcr p15, 0, r5, c13, c0, 1 @Context ID
+
+ mcr p15, 0, r6, c3, c0, 0 @Domain Access Register
+ mcr p15, 0, r7, c2, c0, 0 @TTB0
+ mcr p15, 0, r8, c2, c0, 1 @TTB1
+ mcr p15, 0, r9, c2, c0, 2 @TTBC
+
+ mcr p15, 0, r1, c1, c0, 0 @Control Register
+ /* mcu enabled */
+ mrc p15, 0, r0, c2, c0, 0
+
+ mov pc, lr
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+mxc_cp15_save:
+ mov ip, sp
+ stmdb sp!, {r8-r9, fp, ip, lr, pc}
+ sub fp, ip, #4
+ ldr r0, =mxc_cp15_context
+/* System Control Registers */
+ mrc p15, 0, r1, c1, c0, 0 @Control Register
+ mrc p15, 0, r2, c1, c0, 1 @Aux Control Register
+ mrc p15, 0, r3, c1, c0, 2 @CP access Register
+
+/* Memory management Registers */
+ mrc p15, 0, r4, c13, c0, 0 @PID
+ mrc p15, 0, r5, c13, c0, 1 @Context ID
+
+ mrc p15, 0, r6, c3, c0, 0 @Domain Access Register
+
+ mrc p15, 0, r7, c2, c0, 0 @TTB0
+ mrc p15, 0, r8, c2, c0, 1 @TTB1
+ mrc p15, 0, r9, c2, c0, 2 @TTBC
+ stmia r0, {r1-r9}
+ ldmia sp, {r8, r9, fp, sp, pc}
+
+/*
+ * int __mxc_pm_arch_entry(u32 entry, u32 size)
+ */
+ .align 5
+ .globl mxc_pm_arch_entry
+mxc_pm_arch_entry:
+ mov ip, sp
+ stmdb sp!, {r4-r9, sl, fp, ip, lr, pc}
+ sub fp, ip, #4
+ sub sp, sp, #4
+ mov r8, r0 @save entry
+ mov r9, r1 @save entry size
+#ifdef CONFIG_VFP
+ bl arm_vfp_save
+#endif
+ /* r0 ~r3, ip is dirty*/
+ bl arm_core_save @save arm context
+ bl mxc_cp15_save
+ mov r0, sp
+ mov r1, r8 @restore entry
+ mov r2, r9 @restore entry size
+ bl __mxc_pm_xload_setup
+1: bl cpu_v6_proc_fin
+ bl cpu_v6_do_idle
+ nop
+ nop
+ nop
+ nop
+__mxc_pm_arch_leave:
+ adr r0, __mxc_pm_xload_info
+ ldr sp, [r0, #PM_XLOAD_CORE_SP]
+
+#ifndef CONFIG_PM_DEBUG
+ sub sp, sp, #PAGE_OFFSET
+ add sp, sp, #PHYS_OFFSET
+#endif
+ bl mxc_cp15_restore
+#ifndef CONFIG_PM_DEBUG
+ sub sp, sp, #PHYS_OFFSET
+ add sp, sp, #PAGE_OFFSET
+#endif
+ show_resume_str
+ bl arm_core_restore
+ ldmib sp, {r4-r9, sl, fp, sp, pc}
+
+__mxc_pm_xload_info:
+ adr pc, __mxc_pm_xload_entry @Jump instruction
+ .long __mxc_pm_xload_end - __mxc_pm_xload_info @loader size
+ .long (__mxc_pm_arch_leave - PAGE_OFFSET + PHYS_OFFSET) @resume entry
+ .long 0 @suspend state
+ .long 0 @Core Stack pointer
+__mxc_pm_xload_entry:
+ adr r0, __mxc_pm_xload_info
+ ldr pc, [r0, #PM_XLOAD_ENTRY]
+__mxc_pm_xload_end:
+
+/*
+ * __mxc_pm_xload_setup(u32 sp, u32 entry, u32 size)
+ * r0~r6 is dirty
+ */
+__mxc_pm_xload_setup:
+ ldr r3, =__mxc_pm_xload_info
+ str r0, [r3, #PM_XLOAD_CORE_SP]
+ ldr r4, [r3, #PM_XLOAD_SIZE]
+ cmp r2, r4
+ blo 2f
+1: ldr r5, [r3], #4
+ str r5, [r1], #4
+ subs r4, r4, #4
+ bhi 1b
+ b 3f
+2: str r3, [r1]
+3: mov pc, lr
diff --git a/arch/arm/plat-mxc/gpio.c b/arch/arm/plat-mxc/gpio.c
index de5c4747453f..e42f52896ee6 100644
--- a/arch/arm/plat-mxc/gpio.c
+++ b/arch/arm/plat-mxc/gpio.c
@@ -3,7 +3,7 @@
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*
* Based on code from Freescale,
- * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -20,9 +20,12 @@
*/
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/gpio.h>
+#include <linux/sysdev.h>
+#include <mach/gpio.h>
#include <mach/hardware.h>
#include <asm-generic/bug.h>
@@ -64,6 +67,8 @@ static void gpio_unmask_irq(u32 irq)
_set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 1);
}
+static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset);
+
static int gpio_set_irq_type(u32 irq, u32 type)
{
u32 gpio = irq_to_gpio(irq);
@@ -72,20 +77,37 @@ static int gpio_set_irq_type(u32 irq, u32 type)
int edge;
void __iomem *reg = port->base;
+ port->both_edges &= ~(1 << (gpio & 31));
switch (type) {
case IRQ_TYPE_EDGE_RISING:
edge = GPIO_INT_RISE_EDGE;
+ set_irq_handler(irq, handle_edge_irq);
break;
case IRQ_TYPE_EDGE_FALLING:
edge = GPIO_INT_FALL_EDGE;
+ set_irq_handler(irq, handle_edge_irq);
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ val = mxc_gpio_get(&port->chip, gpio & 31);
+ if (val) {
+ edge = GPIO_INT_LOW_LEV;
+ pr_debug("mxc: set GPIO %d to low trigger\n", gpio);
+ } else {
+ edge = GPIO_INT_HIGH_LEV;
+ pr_debug("mxc: set GPIO %d to high trigger\n", gpio);
+ }
+ port->both_edges |= 1 << (gpio & 31);
+ set_irq_handler(irq, handle_edge_irq);
break;
case IRQ_TYPE_LEVEL_LOW:
edge = GPIO_INT_LOW_LEV;
+ set_irq_handler(irq, handle_level_irq);
break;
case IRQ_TYPE_LEVEL_HIGH:
edge = GPIO_INT_HIGH_LEV;
+ set_irq_handler(irq, handle_level_irq);
break;
- default: /* this includes IRQ_TYPE_EDGE_BOTH */
+ default:
return -EINVAL;
}
@@ -98,6 +120,34 @@ static int gpio_set_irq_type(u32 irq, u32 type)
return 0;
}
+static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
+{
+ void __iomem *reg = port->base;
+ u32 bit, val;
+ int edge;
+
+ reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
+ bit = gpio & 0xf;
+ val = __raw_readl(reg);
+ edge = (val >> (bit << 1)) & 3;
+ val &= ~(0x3 << (bit << 1));
+ switch (edge) {
+ case GPIO_INT_HIGH_LEV:
+ edge = GPIO_INT_LOW_LEV;
+ pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
+ break;
+ case GPIO_INT_LOW_LEV:
+ edge = GPIO_INT_HIGH_LEV;
+ pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
+ break;
+ default:
+ pr_err("mxc: invalid configuration for GPIO %d: %x\n",
+ gpio, edge);
+ return;
+ }
+ __raw_writel(val | (edge << (bit << 1)), reg);
+}
+
/* handle n interrupts in one status register */
static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
{
@@ -105,26 +155,38 @@ static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
gpio_irq_no = port->virtual_irq_start;
for (; irq_stat != 0; irq_stat >>= 1, gpio_irq_no++) {
+ u32 gpio = irq_to_gpio(gpio_irq_no);
if ((irq_stat & 1) == 0)
continue;
BUG_ON(!(irq_desc[gpio_irq_no].handle_irq));
+
+ if (port->both_edges & (1 << (gpio & 31)))
+ mxc_flip_edge(port, gpio);
+
irq_desc[gpio_irq_no].handle_irq(gpio_irq_no,
&irq_desc[gpio_irq_no]);
}
}
-#ifdef CONFIG_ARCH_MX3
-/* MX3 has one interrupt *per* gpio port */
-static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
+#ifndef CONFIG_ARCH_MX2
+/* one interrupt *per* gpio port */
+static void gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
u32 irq_stat;
+ u32 mask = 0xFFFFFFFF;
struct mxc_gpio_port *port = (struct mxc_gpio_port *)get_irq_data(irq);
+#ifdef MXC_GPIO_SPLIT_IRQ_2
+ if (irq == port->irq)
+ mask = 0x0000FFFF;
+ else
+ mask = 0xFFFF0000;
+#endif
+
irq_stat = __raw_readl(port->base + GPIO_ISR) &
- __raw_readl(port->base + GPIO_IMR);
- BUG_ON(!irq_stat);
+ (__raw_readl(port->base + GPIO_IMR) & mask);
mxc_gpio_irq_handler(port, irq_stat);
}
#endif
@@ -150,11 +212,44 @@ static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
}
#endif
+/*
+ * Set interrupt number "irq" in the GPIO as a wake-up source.
+ * While system is running all registered GPIO interrupts need to have
+ * wake-up enabled. When system is suspended, only selected GPIO interrupts
+ * need to have wake-up enabled.
+ * @param irq interrupt source number
+ * @param enable enable as wake-up if equal to non-zero
+ * @return This function returns 0 on success.
+ */
+static int gpio_set_wake_irq(u32 irq, u32 enable)
+{
+ u32 gpio = irq_to_gpio(irq);
+ u32 gpio_idx = gpio & 0x1F;
+ struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32];
+
+ if (enable) {
+ port->suspend_wakeup |= (1 << gpio_idx);
+ if (port->irq_high && (gpio_idx >= 16))
+ enable_irq_wake(port->irq_high);
+ else
+ enable_irq_wake(port->irq);
+ } else {
+ port->suspend_wakeup &= ~(1 << gpio_idx);
+ if (port->irq_high && (gpio_idx >= 16))
+ disable_irq_wake(port->irq_high);
+ else
+ disable_irq_wake(port->irq);
+ }
+
+ return 0;
+}
+
static struct irq_chip gpio_irq_chip = {
.ack = gpio_ack_irq,
.mask = gpio_mask_irq,
.unmask = gpio_unmask_irq,
.set_type = gpio_set_irq_type,
+ .set_wake = gpio_set_wake_irq,
};
static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset,
@@ -200,14 +295,103 @@ static int mxc_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
static int mxc_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
- _set_gpio_direction(chip, offset, 1);
mxc_gpio_set(chip, offset, value);
+ _set_gpio_direction(chip, offset, 1);
return 0;
}
+#ifdef CONFIG_PM
+/*!
+ * This function puts the GPIO in low-power mode/state.
+ * All the interrupts that are enabled are first saved.
+ * Only those interrupts which registers as a wake source by calling
+ * enable_irq_wake are enabled. All other interrupts are disabled.
+ *
+ * @param dev the system device structure used to give information
+ * on GPIO to suspend
+ * @param mesg the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_gpio_suspend(struct sys_device *dev, pm_message_t mesg)
+{
+ int i;
+ struct mxc_gpio_port *port = mxc_gpio_ports;
+
+ for (i = 0; i < GPIO_PORT_NUM; i++) {
+ void __iomem *isr_reg;
+ void __iomem *imr_reg;
+
+ isr_reg = port[i].base + GPIO_ISR;
+ imr_reg = port[i].base + GPIO_IMR;
+
+ if (__raw_readl(isr_reg) & port[i].suspend_wakeup)
+ return -EPERM;
+
+ port[i].saved_wakeup = __raw_readl(imr_reg);
+ __raw_writel(port[i].suspend_wakeup, imr_reg);
+ }
+
+ return 0;
+}
+
+/*!
+ * This function brings the GPIO back from low-power state.
+ * All the interrupts enabled before suspension are re-enabled from
+ * the saved information.
+ *
+ * @param dev the system device structure used to give information
+ * on GPIO to resume
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_gpio_resume(struct sys_device *dev)
+{
+ int i;
+ struct mxc_gpio_port *port = mxc_gpio_ports;
+
+ for (i = 0; i < GPIO_PORT_NUM; i++) {
+ void __iomem *isr_reg;
+ void __iomem *imr_reg;
+
+ isr_reg = port[i].base + GPIO_ISR;
+ imr_reg = port[i].base + GPIO_IMR;
+
+ __raw_writel(port[i].saved_wakeup, imr_reg);
+ }
+
+ return 0;
+}
+#else
+#define mxc_gpio_suspend NULL
+#define mxc_gpio_resume NULL
+#endif /* CONFIG_PM */
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct sysdev_class mxc_gpio_sysclass = {
+ .name = "mxc_gpio",
+ .suspend = mxc_gpio_suspend,
+ .resume = mxc_gpio_resume,
+};
+
+/*!
+ * This structure represents GPIO as a system device.
+ * System devices follow a slightly different driver model.
+ * They don't need to do dynammic driver binding, can't be probed,
+ * and don't reside on any type of peripheral bus.
+ * So, it is represented and treated a little differently.
+ */
+static struct sys_device mxc_gpio_device = {
+ .id = 0,
+ .cls = &mxc_gpio_sysclass,
+};
+
int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
{
int i, j;
+ int ret = 0;
/* save for local usage */
mxc_gpio_ports = port;
@@ -235,12 +419,15 @@ int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
port[i].chip.ngpio = 32;
/* its a serious configuration bug when it fails */
- BUG_ON( gpiochip_add(&port[i].chip) < 0 );
+ BUG_ON(gpiochip_add(&port[i].chip) < 0);
-#ifdef CONFIG_ARCH_MX3
- /* setup one handler for each entry */
- set_irq_chained_handler(port[i].irq, mx3_gpio_irq_handler);
+#ifndef CONFIG_ARCH_MX2
+ set_irq_chained_handler(port[i].irq, gpio_irq_handler);
set_irq_data(port[i].irq, &port[i]);
+ if (port[i].irq_high) {
+ set_irq_chained_handler(port[i].irq_high, gpio_irq_handler);
+ set_irq_data(port[i].irq_high, &port[i]);
+ }
#endif
}
@@ -249,5 +436,10 @@ int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
set_irq_chained_handler(port[0].irq, mx2_gpio_irq_handler);
set_irq_data(port[0].irq, port);
#endif
- return 0;
+
+ ret = sysdev_class_register(&mxc_gpio_sysclass);
+ if (ret == 0)
+ ret = sysdev_register(&mxc_gpio_device);
+
+ return ret;
}
diff --git a/arch/arm/plat-mxc/include/mach/arc_otg.h b/arch/arm/plat-mxc/include/mach/arc_otg.h
new file mode 100644
index 000000000000..467ab1379e62
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/arc_otg.h
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_ARC_OTG_H__
+#define __ASM_ARCH_MXC_ARC_OTG_H__
+
+#define USB_OTGREGS_BASE (OTG_BASE_ADDR + 0x000)
+#define USB_H1REGS_BASE (OTG_BASE_ADDR + 0x200)
+#define USB_H2REGS_BASE (OTG_BASE_ADDR + 0x400)
+#ifdef CONFIG_ARCH_MX51
+#define USB_H3REGS_BASE (OTG_BASE_ADDR + 0x600)
+#define USB_OTHERREGS_BASE (OTG_BASE_ADDR + 0x800)
+#else
+#define USB_OTHERREGS_BASE (OTG_BASE_ADDR + 0x600)
+#endif
+
+
+#define USBOTG_REG32(offset) (*((volatile u32 *)(IO_ADDRESS(USB_OTGREGS_BASE + (offset)))))
+#define USBOTG_REG16(offset) (*((volatile u16 *)(IO_ADDRESS(USB_OTGREGS_BASE + (offset)))))
+
+#define USBH1_REG32(offset) (*((volatile u32 *)(IO_ADDRESS(USB_H1REGS_BASE + (offset)))))
+#define USBH1_REG16(offset) (*((volatile u16 *)(IO_ADDRESS(USB_H1REGS_BASE + (offset)))))
+
+#define USBH2_REG32(offset) (*((volatile u32 *)(IO_ADDRESS(USB_H2REGS_BASE + (offset)))))
+#define USBH2_REG16(offset) (*((volatile u16 *)(IO_ADDRESS(USB_H2REGS_BASE + (offset)))))
+
+#define USBOTHER_REG(offset) (*((volatile u32 *)(IO_ADDRESS(USB_OTHERREGS_BASE + (offset)))))
+
+/*
+ * OTG registers
+ */
+#define UOG_ID USBOTG_REG32(0x00) /* Host ID */
+#define UOG_HWGENERAL USBOTG_REG32(0x04) /* Host General */
+#define UOG_HWHOST USBOTG_REG32(0x08) /* Host h/w params */
+#define UOG_HWTXBUF USBOTG_REG32(0x10) /* TX buffer h/w params */
+#define UOG_HWRXBUF USBOTG_REG32(0x14) /* RX buffer h/w params */
+#define UOG_CAPLENGTH USBOTG_REG16(0x100) /* Capability register length */
+#define UOG_HCIVERSION USBOTG_REG16(0x102) /* Host Interface version */
+#define UOG_HCSPARAMS USBOTG_REG32(0x104) /* Host control structural params */
+#define UOG_HCCPARAMS USBOTG_REG32(0x108) /* control capability params */
+#define UOG_DCIVERSION USBOTG_REG32(0x120) /* device interface version */
+/* start EHCI registers: */
+#define UOG_USBCMD USBOTG_REG32(0x140) /* USB command register */
+#define UOG_USBSTS USBOTG_REG32(0x144) /* USB status register */
+#define UOG_USBINTR USBOTG_REG32(0x148) /* interrupt enable register */
+#define UOG_FRINDEX USBOTG_REG32(0x14c) /* USB frame index */
+/* segment (0x150) addr bits 63:32 if needed */
+#define UOG_PERIODICLISTBASE USBOTG_REG32(0x154) /* host crtlr frame list base addr */
+#define UOG_DEVICEADDR USBOTG_REG32(0x154) /* device crtlr device address */
+#define UOG_ASYNCLISTADDR USBOTG_REG32(0x158) /* host ctrlr next async addr */
+#define UOG_EPLISTADDR USBOTG_REG32(0x158) /* device ctrlr endpoint list addr */
+#define UOG_BURSTSIZE USBOTG_REG32(0x160) /* host ctrlr embedded TT async buf status */
+#define UOG_TXFILLTUNING USBOTG_REG32(0x164) /* TX FIFO fill tuning */
+#define UOG_ULPIVIEW USBOTG_REG32(0x170) /* ULPI viewport */
+#define UOG_CFGFLAG USBOTG_REG32(0x180) /* configflag (supports HS) */
+#define UOG_PORTSC1 USBOTG_REG32(0x184) /* port status and control */
+/* end EHCI registers: */
+#define UOG_OTGSC USBOTG_REG32(0x1a4) /* OTG status and control */
+#define UOG_USBMODE USBOTG_REG32(0x1a8) /* USB device mode */
+#define UOG_ENDPTSETUPSTAT USBOTG_REG32(0x1ac) /* endpoint setup status */
+#define UOG_ENDPTPRIME USBOTG_REG32(0x1b0) /* endpoint initialization */
+#define UOG_ENDPTFLUSH USBOTG_REG32(0x1b4) /* endpoint de-initialize */
+#define UOG_ENDPTSTAT USBOTG_REG32(0x1b8) /* endpoint status */
+#define UOG_ENDPTCOMPLETE USBOTG_REG32(0x1bc) /* endpoint complete */
+#define UOG_EPCTRL0 USBOTG_REG32(0x1c0) /* endpoint control0 */
+#define UOG_EPCTRL1 USBOTG_REG32(0x1c4) /* endpoint control1 */
+#define UOG_EPCTRL2 USBOTG_REG32(0x1c8) /* endpoint control2 */
+#define UOG_EPCTRL3 USBOTG_REG32(0x1cc) /* endpoint control3 */
+#define UOG_EPCTRL4 USBOTG_REG32(0x1d0) /* endpoint control4 */
+#define UOG_EPCTRL5 USBOTG_REG32(0x1d4) /* endpoint control5 */
+#define UOG_EPCTRL6 USBOTG_REG32(0x1d8) /* endpoint control6 */
+#define UOG_EPCTRL7 USBOTG_REG32(0x1dc) /* endpoint control7 */
+
+/*
+ * Host 1 registers
+ */
+#define UH1_ID USBH1_REG32(0x00) /* Host ID */
+#define UH1_HWGENERAL USBH1_REG32(0x04) /* Host General */
+#define UH1_HWHOST USBH1_REG32(0x08) /* Host h/w params */
+#define UH1_HWTXBUF USBH1_REG32(0x10) /* TX buffer h/w params */
+#define UH1_HWRXBUF USBH1_REG32(0x14) /* RX buffer h/w params */
+#define UH1_CAPLENGTH USBH1_REG16(0x100) /* Capability register length */
+#define UH1_HCIVERSION USBH1_REG16(0x102) /* Host Interface version */
+#define UH1_HCSPARAMS USBH1_REG32(0x104) /* Host control structural params */
+#define UH1_HCCPARAMS USBH1_REG32(0x108) /* control capability params */
+/* start EHCI registers: */
+#define UH1_USBCMD USBH1_REG32(0x140) /* USB command register */
+#define UH1_USBSTS USBH1_REG32(0x144) /* USB status register */
+#define UH1_USBINTR USBH1_REG32(0x148) /* interrupt enable register */
+#define UH1_FRINDEX USBH1_REG32(0x14c) /* USB frame index */
+/* segment (0x150) addr bits 63:32 if needed */
+#define UH1_PERIODICLISTBASE USBH1_REG32(0x154) /* host crtlr frame list base addr */
+#define UH1_ASYNCLISTADDR USBH1_REG32(0x158) /* host ctrlr nest async addr */
+#define UH1_BURSTSIZE USBH1_REG32(0x160) /* host ctrlr embedded TT async buf status */
+#define UH1_TXFILLTUNING USBH1_REG32(0x164) /* TX FIFO fill tuning */
+/* configured_flag (0x180) configflag (supports HS) */
+#define UH1_PORTSC1 USBH1_REG32(0x184) /* port status and control */
+/* end EHCI registers: */
+#define UH1_USBMODE USBH1_REG32(0x1a8) /* USB device mode */
+
+/*
+ * Host 2 registers
+ */
+#define UH2_ID USBH2_REG32(0x00) /* Host ID */
+#define UH2_HWGENERAL USBH2_REG32(0x04) /* Host General */
+#define UH2_HWHOST USBH2_REG32(0x08) /* Host h/w params */
+#define UH2_HWTXBUF USBH2_REG32(0x10) /* TX buffer h/w params */
+#define UH2_HWRXBUF USBH2_REG32(0x14) /* RX buffer h/w params */
+#define UH2_CAPLENGTH USBH2_REG16(0x100) /* Capability register length */
+#define UH2_HCIVERSION USBH2_REG16(0x102) /* Host Interface version */
+#define UH2_HCSPARAMS USBH2_REG32(0x104) /* Host control structural params */
+#define UH2_HCCPARAMS USBH2_REG32(0x108) /* control capability params */
+/* start EHCI registers: */
+#define UH2_USBCMD USBH2_REG32(0x140) /* USB command register */
+#define UH2_USBSTS USBH2_REG32(0x144) /* USB status register */
+#define UH2_USBINTR USBH2_REG32(0x148) /* interrupt enable register */
+#define UH2_FRINDEX USBH2_REG32(0x14c) /* USB frame index */
+/* segment (0x150) addr bits 63:32 if needed */
+#define UH2_PERIODICLISTBASE USBH2_REG32(0x154) /* host crtlr frame list base addr */
+#define UH2_ASYNCLISTADDR USBH2_REG32(0x158) /* host ctrlr nest async addr */
+#define UH2_BURSTSIZE USBH2_REG32(0x160) /* host ctrlr embedded TT async buf status */
+#define UH2_TXFILLTUNING USBH2_REG32(0x164) /* TX FIFO fill tuning */
+#define UH2_ULPIVIEW USBH2_REG32(0x170) /* ULPI viewport */
+/* configured_flag (0x180) configflag (supports HS) */
+#define UH2_PORTSC1 USBH2_REG32(0x184) /* port status and control */
+/* end EHCI registers */
+#define UH2_USBMODE USBH2_REG32(0x1a8) /* USB device mode */
+
+/*
+ * other regs (not part of ARC core)
+ */
+#define USBCTRL USBOTHER_REG(0x00) /* USB Control register */
+#define USB_OTG_MIRROR USBOTHER_REG(0x04) /* USB OTG mirror register */
+#define USB_PHY_CTR_FUNC USBOTHER_REG(0x08) /* OTG UTMI PHY Function Control register */
+#define USB_PHY_CTR_FUNC2 USBOTHER_REG(0x0c) /* OTG UTMI PHY Function Control register */
+#define USB_CTRL_1 USBOTHER_REG(0x10) /* USB Cotrol Register 1*/
+#define USBCTRL_HOST2 USBOTHER_REG(0x14) /* USB Cotrol Register 1*/
+#define USBCTRL_HOST3 USBOTHER_REG(0x18) /* USB Cotrol Register 1*/
+
+/*
+ * register bits
+ */
+
+/* x_PORTSCx */
+#define PORTSC_PTS_MASK (3 << 30) /* parallel xcvr select mask */
+#define PORTSC_PTS_UTMI (0 << 30) /* UTMI/UTMI+ */
+#define PORTSC_PTS_PHILIPS (1 << 30) /* Philips classic */
+#define PORTSC_PTS_ULPI (2 << 30) /* ULPI */
+#define PORTSC_PTS_SERIAL (3 << 30) /* serial */
+#define PORTSC_STS (1 << 29) /* serial xcvr select */
+#define PORTSC_PTW (1 << 28) /* UTMI width */
+#define PORTSC_PHCD (1 << 23) /* Low Power Suspend */
+#define PORTSC_PORT_POWER (1 << 12) /* port power */
+#define PORTSC_LS_MASK (3 << 10) /* Line State mask */
+#define PORTSC_LS_SE0 (0 << 10) /* SE0 */
+#define PORTSC_LS_K_STATE (1 << 10) /* K-state */
+#define PORTSC_LS_J_STATE (2 << 10) /* J-state */
+#define PORTSC_PORT_RESET (1 << 8) /* Port reset */
+#define PORTSC_PORT_SUSPEND (1 << 7) /* Suspend */
+#define PORTSC_PORT_FORCE_RESUME (1 << 6) /* Force port resume */
+#define PORTSC_OVER_CURRENT_CHG (1 << 5) /* over current change */
+#define PORTSC_OVER_CURRENT_ACT (1 << 4) /* over currrent active */
+#define PORTSC_PORT_EN_DIS_CHANGE (1 << 3) /* port {en,dis}able change */
+#define PORTSC_PORT_ENABLE (1 << 2) /* port enabled */
+#define PORTSC_CONNECT_STATUS_CHANGE (1 << 1) /* connect status change */
+#define PORTSC_CURRENT_CONNECT_STATUS (1 << 0) /* current connect status */
+
+#define PORTSC_W1C_BITS \
+ ( PORTSC_CONNECT_STATUS_CHANGE | \
+ PORTSC_PORT_EN_DIS_CHANGE | \
+ PORTSC_OVER_CURRENT_CHG )
+
+/* UOG_OTGSC Register Bits */
+/* control bits: */
+#define OTGSC_CTRL_VBUS_DISCHARGE (1 << 0)
+#define OTGSC_CTRL_VBUS_CHARGE (1 << 1)
+#define OTGSC_CTRL_OTG_TERM (1 << 3) /* controls DM pulldown */
+#define OTGSC_CTRL_DATA_PULSING (1 << 4)
+#define OTGSC_CTRL_USB_ID_PU (1 << 5) /* enable ID pullup */
+/* current status: (R/O) */
+#define OTGSC_STS_USB_ID (1 << 8) /* 0=A-device 1=B-device */
+#define OTGSC_STS_A_VBUS_VALID (1 << 9)
+#define OTGSC_STS_A_SESSION_VALID (1 << 10)
+#define OTGSC_STS_B_SESSION_VALID (1 << 11)
+#define OTGSC_STS_B_SESSION_END (1 << 12)
+#define OTGSC_STS_1ms_TIMER (1 << 13)
+#define OTGSC_STS_DATA_PULSE (1 << 14)
+/* interrupt status: (write to clear) */
+#define OTGSC_IS_MASK (0x7f << 16)
+#define OTGSC_IS_USB_ID (1 << 16)
+#define OTGSC_IS_A_VBUS_VALID (1 << 17)
+#define OTGSC_IS_A_SESSION_VALID (1 << 18)
+#define OTGSC_IS_B_SESSION_VALID (1 << 19)
+#define OTGSC_IS_B_SESSION_END (1 << 20)
+#define OTGSC_IS_1ms_TIMER (1 << 21)
+#define OTGSC_IS_DATA_PULSE (1 << 22)
+/* interrupt enables: */
+#define OTGSC_IE_MASK (0x7f << 24)
+#define OTGSC_IE_USB_ID (1 << 24)
+#define OTGSC_IE_A_VBUS_VALID (1 << 25)
+#define OTGSC_IE_A_SESSION_VALID (1 << 26)
+#define OTGSC_IE_B_SESSION_VALID (1 << 27)
+#define OTGSC_IE_B_SESSION_END (1 << 28)
+#define OTGSC_IE_1ms_TIMER (1 << 29)
+#define OTGSC_IE_DATA_PULSE (1 << 30)
+
+#if 1 /* FIXME these here for compatibility between my names and Leo's */
+/* OTG interrupt enable bit masks */
+#define OTGSC_INTERRUPT_ENABLE_BITS_MASK OTGSC_IE_MASK
+#define OTGSC_INTSTS_MASK OTGSC_IS_MASK
+
+/* OTG interrupt status bit masks */
+#define OTGSC_INTERRUPT_STATUS_BITS_MASK OTGSC_IS_MASK
+#endif
+
+/* x_USBMODE */
+#define USBMODE_SLOM (1 << 3) /* setup lockout mode */
+#define USBMODE_ES (1 << 2) /* (big) endian select */
+#define USBMODE_CM_MASK (3 << 0) /* controller mode mask */
+#define USBMODE_CM_HOST (3 << 0) /* host */
+#define USBMODE_CM_DEVICE (2 << 0) /* device */
+#define USBMODE_CM_reserved (1 << 0) /* reserved */
+
+/* USBCTRL */
+#define UCTRL_OWIR (1 << 31) /* OTG wakeup intr request received */
+#define UCTRL_OSIC_MASK (3 << 29) /* OTG Serial Interface Config: */
+#define UCTRL_OSIC_DU6 (0 << 29) /* Differential/unidirectional 6 wire */
+#define UCTRL_OSIC_DB4 (1 << 29) /* Differential/bidirectional 4 wire */
+#define UCTRL_OSIC_SU6 (2 << 29) /* single-ended/unidirectional 6 wire */
+#define UCTRL_OSIC_SB3 (3 << 29) /* single-ended/bidirectional 3 wire */
+
+#define UCTRL_OUIE (1 << 28) /* OTG ULPI intr enable */
+#define UCTRL_OWIE (1 << 27) /* OTG wakeup intr enable */
+#define UCTRL_OBPVAL_RXDP (1 << 26) /* OTG RxDp status in bypass mode */
+#define UCTRL_OBPVAL_RXDM (1 << 25) /* OTG RxDm status in bypass mode */
+#define UCTRL_OPM (1 << 24) /* OTG power mask */
+#define UCTRL_H2WIR (1 << 23) /* HOST2 wakeup intr request received */
+#define UCTRL_H2SIC_MASK (3 << 21) /* HOST2 Serial Interface Config: */
+#define UCTRL_H2SIC_DU6 (0 << 21) /* Differential/unidirectional 6 wire */
+#define UCTRL_H2SIC_DB4 (1 << 21) /* Differential/bidirectional 4 wire */
+#define UCTRL_H2SIC_SU6 (2 << 21) /* single-ended/unidirectional 6 wire */
+#define UCTRL_H2SIC_SB3 (3 << 21) /* single-ended/bidirectional 3 wire */
+
+#ifdef CONFIG_ARCH_MX51
+#define UCTRL_H2UIE (1 << 8) /* HOST2 ULPI intr enable */
+#define UCTRL_H2WIE (1 << 7) /* HOST2 wakeup intr enable */
+#define UCTRL_H2PP 0 /* Power Polarity for uh2 */
+#define UCTRL_H2PM (1 << 4) /* HOST2 power mask */
+#else
+#define UCTRL_H2UIE (1 << 20) /* HOST2 ULPI intr enable */
+#define UCTRL_H2WIE (1 << 19) /* HOST2 wakeup intr enable */
+#define UCTRL_H2PP (1 << 18) /* Power Polarity for uh2 */
+#define UCTRL_H2PM (1 << 16) /* HOST2 power mask */
+#endif
+#define UCTRL_H2OVBWK_EN (1 << 6) /* OTG VBUS Wakeup Enable */
+#define UCTRL_H2OIDWK_EN (1 << 5) /* OTG ID Wakeup Enable */
+
+#define UCTRL_H1WIR (1 << 15) /* HOST1 wakeup intr request received */
+#define UCTRL_H1SIC_MASK (3 << 13) /* HOST1 Serial Interface Config: */
+#define UCTRL_H1SIC_DU6 (0 << 13) /* Differential/unidirectional 6 wire */
+#define UCTRL_H1SIC_DB4 (1 << 13) /* Differential/bidirectional 4 wire */
+#define UCTRL_H1SIC_SU6 (2 << 13) /* single-ended/unidirectional 6 wire */
+#define UCTRL_H1SIC_SB3 (3 << 13) /* single-ended/bidirectional 3 wire */
+#define UCTRL_OLOCKD (1 << 13) /* otg lock disable */
+#define UCTRL_H2LOCKD (1 << 12) /* HOST2 lock disable */
+#define UCTRL_H1UIE (1 << 12) /* Host1 ULPI interrupt enable */
+
+#if defined(CONFIG_ARCH_MX37)
+/* VBUS wakeup enable, UTMI only */
+#define UCTRL_VBUS_WKUP_EN (1 << 12)
+#elif defined(CONFIG_ARCH_MX25) || defined(CONFIG_ARCH_MX35)
+#define UCTRL_VBUS_WKUP_EN (1 << 15)
+#endif
+
+#define UCTRL_PP (1 << 11) /* power polarity bit */
+#define UCTRL_H1WIE (1 << 11) /* HOST1 wakeup intr enable */
+#define UCTRL_H1BPVAL_RXDP (1 << 10) /* HOST1 RxDp status in bypass mode */
+#define UCTRL_XCSO (1 << 10) /* Xcvr Clock Select for OTG port */
+#define UCTRL_H1BPVAL_RXDM (1 << 9) /* HOST1 RxDm status in bypass mode */
+#define UCTRL_XCSH2 (1 << 9) /* Xcvr Clock Select for Host port */
+#define UCTRL_H1PM (1 << 8) /* HOST1 power mask */
+#define UCTRL_IP_PULIDP (1 << 8) /* Ipp_Puimpel_Pullup_Dp */
+
+#define UCTRL_IP_PUE_UP (1 << 7) /* ipp_pue_pullup_dp */
+#define UCTRL_IP_PUE_DOWN (1 << 6) /* ipp_pue_pulldwn_dpdm */
+#define UCTRL_H2DT (1 << 5) /* HOST2 TLL disabled */
+#define UCTRL_H1DT (1 << 4) /* HOST1 TLL disabled */
+#define UCTRL_USBTE (1 << 4) /* USBT Transceiver enable */
+#define UCTRL_OCPOL (1 << 3) /* OverCurrent Polarity */
+#define UCTRL_OCE (1 << 2) /* OverCurrent Enable */
+#define UCTRL_H2OCPOL (1 << 2) /* OverCurrent Polarity of Host2 */
+#define UCTRL_H2OCS (1 << 1) /* Host OverCurrent State */
+#define UCTRL_BPE (1 << 0) /* bypass mode enable */
+#define UCTRL_OTD (1 << 0) /* OTG TLL Disable */
+#define UCTRL_OOCS (1 << 0) /* OTG OverCurrent State */
+
+/* USBCMD */
+#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */
+#define UCMD_RESET (1 << 1) /* controller reset */
+#define UCMD_ITC_NO_THRESHOLD ~(0xff << 16) /* Interrupt Threshold Control */
+
+/* OTG_MIRROR */
+#define OTGM_SESEND (1 << 4) /* B device session end */
+#define OTGM_VBUSVAL (1 << 3) /* Vbus valid */
+#define OTGM_BSESVLD (1 << 2) /* B session Valid */
+#define OTGM_ASESVLD (1 << 1) /* A session Valid */
+#define OTGM_IDIDG (1 << 0) /* OTG ID pin status */
+ /* 1=high: Operate as B-device */
+ /* 0=low : Operate as A-device */
+
+/* USB_PHY_CTRL_FUNC */
+/* PHY control0 Register Bit Masks */
+#define USB_UTMI_PHYCTRL_CONF2 (1 << 26)
+
+#define USB_UTMI_PHYCTRL_UTMI_ENABLE (1 << 24)
+#define USB_UTMI_PHYCTRL_CHGRDETEN (1 << 24) /* Enable Charger Detector */
+#define USB_UTMI_PHYCTRL_CHGRDETON (1 << 23) /* Charger Detector Power On Control */
+#define USB_UTMI_PHYCTRL_OC_POL (1 << 9) /* OTG Polarity of Overcurrent */
+#define USB_UTMI_PHYCTRL_OC_DIS (1 << 8) /* OTG Disable Overcurrent Event */
+/* USB_PHY_CTRL_FUNC2*/
+#define USB_UTMI_PHYCTRL2_PLLDIV_MASK 0x3
+#define USB_UTMI_PHYCTRL2_PLLDIV_SHIFT 0
+#define USB_UTMI_PHYCTRL2_HSDEVSEL_MASK 0x3
+#define USB_UTMI_PHYCTRL2_HSDEVSEL_SHIFT 19
+
+/* USB_CTRL_1 */
+#define USB_CTRL_UH1_EXT_CLK_EN (1 << 25)
+#define USB_CTRL_UH2_EXT_CLK_EN (1 << 26)
+
+/* ULPIVIEW register bits */
+#define ULPIVW_OFF (0x170)
+#define ULPIVW_WU (1 << 31) /* Wakeup */
+#define ULPIVW_RUN (1 << 30) /* read/write run */
+#define ULPIVW_WRITE (1 << 29) /* 0=read 1=write */
+#define ULPIVW_SS (1 << 27) /* SyncState */
+#define ULPIVW_PORT_MASK 0x07 /* Port field */
+#define ULPIVW_PORT_SHIFT 24
+#define ULPIVW_ADDR_MASK 0xFF /* data address field */
+#define ULPIVW_ADDR_SHIFT 16
+#define ULPIVW_RDATA_MASK 0xFF /* read data field */
+#define ULPIVW_RDATA_SHIFT 8
+#define ULPIVW_WDATA_MASK 0xFF /* write data field */
+#define ULPIVW_WDATA_SHIFT 0
+
+#define HCSPARAMS_PPC (0x1<<4) /* Port Power Control */
+
+extern enum fsl_usb2_modes get_usb_mode(struct fsl_usb2_platform_data *pdata);
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/audio_controls.h b/arch/arm/plat-mxc/include/mach/audio_controls.h
new file mode 100644
index 000000000000..810bf50b16a8
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/audio_controls.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @file include/asm-arm/arch-mxc/audio_controls.h
+ * @brief this file implements the mxc sound driver interface to OSS framework
+ * @ingroup SOUND_DRV
+ */
+#ifndef __ASM_ARCH_MXC_AUDIO_CONTROLS_H__
+#define __ASM_ARCH_MXC_AUDIO_CONTROLS_H__
+
+/*!
+ * This ioctl can be used to get the adder configuration, use the audio control
+ * SNDCTL_MC13783_READ_OUT_MIXER.\n
+ * Possible returned values are :
+ * @see MC13783_AUDIO_ADDER_STEREO
+ * @see MC13783_AUDIO_ADDER_STEREO_OPPOSITE
+ * @see MC13783_AUDIO_ADDER_MONO
+ * @see MC13783_AUDIO_ADDER_MONO_OPPOSITE
+ *
+ */
+#define SNDCTL_MC13783_READ_OUT_ADDER _SIOR('Z', 6, int)
+
+/*!
+ * To set the adder configuration, use the audio control
+ * SNDCTL_MC13783_WRITE_OUT_MIXER. Possible arguments are : \n
+ * @see MC13783_AUDIO_ADDER_STEREO
+ * @see MC13783_AUDIO_ADDER_STEREO_OPPOSITE
+ * @see MC13783_AUDIO_ADDER_MONO
+ * @see MC13783_AUDIO_ADDER_MONO_OPPOSITE
+ *
+ */
+#define SNDCTL_MC13783_WRITE_OUT_ADDER _SIOWR('Z', 7, int)
+
+/*!
+ * To get the codec balance configuration, use the audio control
+ * SNDCTL_MC13783_READ_OUT_BALANCE.\n
+ * Range is 0 (-21 dB left) to 100 (-21 dB right), linear, 3dB step ;
+ * 50 is no balance.
+ * \n Examples:
+ * \n 0 : -21dB left 50 : balance deactivated 100 : -21 dB right
+ *
+ */
+#define SNDCTL_MC13783_READ_OUT_BALANCE _SIOR('Z', 8, int)
+
+/*!
+ * To set the codec balance configuration, use the audio control
+ * SNDCTL_MC13783_WRITE_OUT_BALANCE.\n
+ * Range is 0 (-21 dB left) to 100 (-21 dB right), linear, 3dB step ;
+ * 50 is no balance.
+ * \n Examples:
+ * \n 0 : -21dB left 50 : balance deactivated 100 : -21 dB right
+ *
+ */
+#define SNDCTL_MC13783_WRITE_OUT_BALANCE _SIOWR('Z', 9, int)
+
+/*!
+ * To set the codec filter configuration, use the audio control
+ * SNDCTL_MC13783_WRITE_CODEC_FILTER.
+ * The new configuration replaces the old one.\n
+ * Possible arguments are :
+ * @see MC13783_CODEC_FILTER_DISABLE
+ * @see MC13783_CODEC_FILTER_HIGH_PASS_IN
+ * @see MC13783_CODEC_FILTER_HIGH_PASS_OUT
+ * @see MC13783_CODEC_FILTER_DITHERING \n
+ *
+ */
+#define SNDCTL_MC13783_WRITE_CODEC_FILTER _SIOWR('Z', 20, int)
+
+/*!
+ * To get the codec filter configuration, use the audio control :
+ * SNDCTL_MC13783_READ_CODEC_FILTER.
+ * The new configuration replaces the old one.\n
+ * Possible returned values are :
+ * @see MC13783_CODEC_FILTER_DISABLE
+ * @see MC13783_CODEC_FILTER_HIGH_PASS_IN
+ * @see MC13783_CODEC_FILTER_HIGH_PASS_OUT
+ * @see MC13783_CODEC_FILTER_DITHERING \n
+ *
+ */
+#define SNDCTL_MC13783_READ_CODEC_FILTER _SIOR('Z', 21, int)
+
+/*
+ * To set the clock configuration, use the audio control
+ * SNDCTL_MC13783_WRITE_MASTER_CLOCK. \n
+ * Possible arguments are : \n
+ * 1 : to MCU master \n
+ * 2 : to MC13783 master
+ */
+#define SNDCTL_MC13783_WRITE_MASTER_CLOCK _SIOR('Z', 30, int)
+
+/*!
+ * To set the output port, use the audio control
+ * SNDCTL_MC13783_WRITE_PORT.\n
+ * Possible returned values are :
+ * \n 1 : to port 4
+ * \n 2 : to port 5
+ * Possible returned values are :
+ * \n 1 : port 4
+ * \n 2 : port 5
+ */
+#define SNDCTL_MC13783_WRITE_PORT _SIOR('Z', 31, int)
+
+/*!
+ * Argument for the mc13783 adder configuration
+ * @see SNDCTL_MC13783_WRITE_OUT_ADDER
+ * @see SNDCTL_MC13783_READ_OUT_ADDER
+ */
+#define MC13783_AUDIO_ADDER_STEREO 0x1
+/*!
+ * Argument for the mc13783 adder configuration
+ * @see SNDCTL_MC13783_WRITE_OUT_ADDER
+ * @see SNDCTL_MC13783_READ_OUT_ADDER
+ */
+#define MC13783_AUDIO_ADDER_STEREO_OPPOSITE 0x2
+/*!
+ * Argument for the mc13783 adder configuration
+ * @see SNDCTL_MC13783_WRITE_OUT_ADDER
+ * @see SNDCTL_MC13783_READ_OUT_ADDER
+ */
+#define MC13783_AUDIO_ADDER_MONO 0x4
+/*!
+ * Argument for the mc13783 adder configuration
+ * @see SNDCTL_MC13783_WRITE_OUT_ADDER
+ * @see SNDCTL_MC13783_READ_OUT_ADDER
+ */
+#define MC13783_AUDIO_ADDER_MONO_OPPOSITE 0x8
+
+/*!
+ * Argument for the mc13783 codec filter configuration
+ * @see SNDCTL_MC13783_WRITE_CODEC_FILTER
+ * @see SNDCTL_MC13783_READ_CODEC_FILTER
+ */
+#define MC13783_CODEC_FILTER_DISABLE 0x0
+/*!
+ * Argument for the mc13783 codec filter configuration
+ * @see SNDCTL_MC13783_WRITE_CODEC_FILTER
+ * @see SNDCTL_MC13783_READ_CODEC_FILTER
+ */
+#define MC13783_CODEC_FILTER_HIGH_PASS_IN 0x1
+/*!
+ * Argument for the mc13783 codec filter configuration
+ * @see SNDCTL_MC13783_WRITE_CODEC_FILTER
+ * @see SNDCTL_MC13783_READ_CODEC_FILTER
+ */
+#define MC13783_CODEC_FILTER_HIGH_PASS_OUT 0x2
+/*!
+ * Argument for the mc13783 codec filter configuration
+ * @see SNDCTL_MC13783_WRITE_CODEC_FILTER
+ * @see SNDCTL_MC13783_READ_CODEC_FILTER
+ */
+#define MC13783_CODEC_FILTER_DITHERING 0x4
+
+/*!
+ * Argument for the system audio clocking selection
+ * @see MXC_AUDIO_CLOCKING_MCU_MASTER
+ * @see SNDCTL_CLK_SET_MASTER
+ */
+#define MXC_AUDIO_CLOCKING_MC13783_MASTER 0x0
+
+/*!
+ * Argument for the system audio clocking selection
+ * @see MXC_AUDIO_CLOCKING_MC13783_MASTER
+ * @see SNDCTL_CLK_SET_MASTER
+ */
+#define MXC_AUDIO_CLOCKING_MCU_MASTER 0x1
+
+/*!
+ * Argument for the DAM output port selection
+ * @see SNDCTL_DAM_SET_OUT_PORT
+ * @see MXC_DAM_OUT_PORT_AD2
+ */
+#define MXC_DAM_OUT_PORT_AD1 0x0
+
+/*!
+ * Argument for the DAM output port selection
+ * @see SNDCTL_DAM_SET_OUT_PORT
+ * @see MXC_DAM_OUT_PORT_AD1
+ */
+#define MXC_DAM_OUT_PORT_AD2 0x1
+
+/*!
+ * Argument for the mc13783 codec filter configuration
+ * @see SNDCTL_MC13783_WRITE_CODEC_FILTER
+ * @see SNDCTL_MC13783_READ_CODEC_FILTER
+ */
+#define MC13783_CODEC_FILTER_DISABLE 0x0
+
+/*!
+ * Argument for the mc13783 codec filter configuration
+ * @see SNDCTL_MC13783_WRITE_CODEC_FILTER
+ * @see SNDCTL_MC13783_READ_CODEC_FILTER
+ */
+#define MC13783_CODEC_FILTER_HIGH_PASS_IN 0x1
+
+/*!
+ * Argument for the mc13783 codec filter configuration
+ * @see SNDCTL_MC13783_WRITE_CODEC_FILTER
+ * @see SNDCTL_MC13783_READ_CODEC_FILTER
+ */
+#define MC13783_CODEC_FILTER_HIGH_PASS_OUT 0x2
+
+/*!
+ * Argument for the mc13783 codec filter configuration
+ * @see SNDCTL_MC13783_WRITE_CODEC_FILTER
+ * @see SNDCTL_MC13783_READ_CODEC_FILTER
+ */
+#define MC13783_CODEC_FILTER_DITHERING 0x4
+
+#endif /* __ASM_ARCH_MXC_AUDIO_CONTROLS_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/clock.h b/arch/arm/plat-mxc/include/mach/clock.h
index d21f78e78819..2086701ccfd6 100644
--- a/arch/arm/plat-mxc/include/mach/clock.h
+++ b/arch/arm/plat-mxc/include/mach/clock.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or
@@ -34,6 +34,8 @@ struct clk {
struct clk *parent;
/* Secondary clock to enable/disable with this clock */
struct clk *secondary;
+ /* Current clock rate */
+ unsigned long rate;
/* Reference count of clock enable/disable */
__s8 usecount;
/* Register bit position for clock's enable/disable control. */
@@ -41,8 +43,9 @@ struct clk {
/* Register address for clock's enable/disable control. */
void __iomem *enable_reg;
u32 flags;
- /* get the current clock rate (always a fresh value) */
- unsigned long (*get_rate) (struct clk *);
+ /* Function ptr to recalculate the clock's rate based on parent
+ clock's rate */
+ void (*recalc) (struct clk *);
/* Function ptr to set the clock to a new rate. The rate must match a
supported rate returned from round_rate. Leave blank if clock is not
programmable */
@@ -62,6 +65,16 @@ struct clk {
int clk_register(struct clk *clk);
void clk_unregister(struct clk *clk);
+int clk_get_usecount(struct clk *clk);
+int clk_set_pll_dither(struct clk *clk, unsigned int pll_ppm);
+
+/* Clock flags */
+#define RATE_PROPAGATES (1 << 0) /* Program children too */
+#define ALWAYS_ENABLED (1 << 1) /* Clock cannot be disabled */
+#define RATE_FIXED (1 << 2) /* Fixed clock rate */
+#define CPU_FREQ_TRIG_UPDATE (1 << 3) /* CPUFREQ trig update */
+#define AHB_HIGH_SET_POINT (1 << 4) /* Requires max AHB clock */
+#define AHB_MED_SET_POINT (1 << 5) /* Requires med AHB clock */
#endif /* __ASSEMBLY__ */
#endif /* __ASM_ARCH_MXC_CLOCK_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/common.h b/arch/arm/plat-mxc/include/mach/common.h
index 6350287a59b9..01a770c794e7 100644
--- a/arch/arm/plat-mxc/include/mach/common.h
+++ b/arch/arm/plat-mxc/include/mach/common.h
@@ -16,7 +16,11 @@ struct platform_device;
extern void mxc_map_io(void);
extern void mxc_init_irq(void);
extern void mxc_timer_init(const char *clk_timer);
-extern int mxc_clocks_init(unsigned long fref);
+extern int __init mxc_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2);
+extern int mxc_init_devices(void);
+extern void mxc_cpu_init(void) __init;
+extern void mxc_cpu_common_init(void);
+extern void __init early_console_setup(char *);
extern int mxc_register_gpios(void);
extern int mxc_register_device(struct platform_device *pdev, void *data);
diff --git a/arch/arm/plat-mxc/include/mach/dma.h b/arch/arm/plat-mxc/include/mach/dma.h
index c822d569a05e..d6ba0e8f4333 100644
--- a/arch/arm/plat-mxc/include/mach/dma.h
+++ b/arch/arm/plat-mxc/include/mach/dma.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -11,4 +11,281 @@
#ifndef __ASM_ARCH_MXC_DMA_H__
#define __ASM_ARCH_MXC_DMA_H__
+#define MXC_DMA_DYNAMIC_CHANNEL 255
+
+#define MXC_DMA_DONE 0x0
+#define MXC_DMA_REQUEST_TIMEOUT 0x1
+#define MXC_DMA_TRANSFER_ERROR 0x2
+
+/*! This defines the list of device ID's for DMA */
+typedef enum mxc_dma_device {
+ MXC_DMA_UART1_RX,
+ MXC_DMA_UART1_TX,
+ MXC_DMA_UART2_RX,
+ MXC_DMA_UART2_TX,
+ MXC_DMA_UART3_RX,
+ MXC_DMA_UART3_TX,
+ MXC_DMA_UART4_RX,
+ MXC_DMA_UART4_TX,
+ MXC_DMA_UART5_RX,
+ MXC_DMA_UART5_TX,
+ MXC_DMA_UART6_RX,
+ MXC_DMA_UART6_TX,
+ MXC_DMA_MMC1_WIDTH_1,
+ MXC_DMA_MMC1_WIDTH_4,
+ MXC_DMA_MMC2_WIDTH_1,
+ MXC_DMA_MMC2_WIDTH_4,
+ MXC_DMA_SSI1_8BIT_RX0,
+ MXC_DMA_SSI1_8BIT_TX0,
+ MXC_DMA_SSI1_16BIT_RX0,
+ MXC_DMA_SSI1_16BIT_TX0,
+ MXC_DMA_SSI1_24BIT_RX0,
+ MXC_DMA_SSI1_24BIT_TX0,
+ MXC_DMA_SSI1_8BIT_RX1,
+ MXC_DMA_SSI1_8BIT_TX1,
+ MXC_DMA_SSI1_16BIT_RX1,
+ MXC_DMA_SSI1_16BIT_TX1,
+ MXC_DMA_SSI1_24BIT_RX1,
+ MXC_DMA_SSI1_24BIT_TX1,
+ MXC_DMA_SSI2_8BIT_RX0,
+ MXC_DMA_SSI2_8BIT_TX0,
+ MXC_DMA_SSI2_16BIT_RX0,
+ MXC_DMA_SSI2_16BIT_TX0,
+ MXC_DMA_SSI2_24BIT_RX0,
+ MXC_DMA_SSI2_24BIT_TX0,
+ MXC_DMA_SSI2_8BIT_RX1,
+ MXC_DMA_SSI2_8BIT_TX1,
+ MXC_DMA_SSI2_16BIT_RX1,
+ MXC_DMA_SSI2_16BIT_TX1,
+ MXC_DMA_SSI2_24BIT_RX1,
+ MXC_DMA_SSI2_24BIT_TX1,
+ MXC_DMA_FIR_RX,
+ MXC_DMA_FIR_TX,
+ MXC_DMA_CSPI1_RX,
+ MXC_DMA_CSPI1_TX,
+ MXC_DMA_CSPI2_RX,
+ MXC_DMA_CSPI2_TX,
+ MXC_DMA_CSPI3_RX,
+ MXC_DMA_CSPI3_TX,
+ MXC_DMA_ATA_RX,
+ MXC_DMA_ATA_TX,
+ MXC_DMA_MEMORY,
+ MXC_DMA_FIFO_MEMORY,
+ MXC_DMA_DSP_PACKET_DATA0_RD,
+ MXC_DMA_DSP_PACKET_DATA0_WR,
+ MXC_DMA_DSP_PACKET_DATA1_RD,
+ MXC_DMA_DSP_PACKET_DATA1_WR,
+ MXC_DMA_DSP_LOG0_CHNL,
+ MXC_DMA_DSP_LOG1_CHNL,
+ MXC_DMA_DSP_LOG2_CHNL,
+ MXC_DMA_DSP_LOG3_CHNL,
+ MXC_DMA_CSI_RX,
+ MXC_DMA_SPDIF_16BIT_TX,
+ MXC_DMA_SPDIF_16BIT_RX,
+ MXC_DMA_SPDIF_32BIT_TX,
+ MXC_DMA_SPDIF_32BIT_RX,
+ MXC_DMA_ASRC_A_RX,
+ MXC_DMA_ASRC_A_TX,
+ MXC_DMA_ASRC_B_RX,
+ MXC_DMA_ASRC_B_TX,
+ MXC_DMA_ASRC_C_RX,
+ MXC_DMA_ASRC_C_TX,
+ MXC_DMA_ASRCA_ESAI,
+ MXC_DMA_ASRCB_ESAI,
+ MXC_DMA_ASRCC_ESAI,
+ MXC_DMA_ASRCA_SSI1_TX0,
+ MXC_DMA_ASRCA_SSI1_TX1,
+ MXC_DMA_ASRCA_SSI2_TX0,
+ MXC_DMA_ASRCA_SSI2_TX1,
+ MXC_DMA_ASRCB_SSI1_TX0,
+ MXC_DMA_ASRCB_SSI1_TX1,
+ MXC_DMA_ASRCB_SSI2_TX0,
+ MXC_DMA_ASRCB_SSI2_TX1,
+ MXC_DMA_ESAI_16BIT_RX,
+ MXC_DMA_ESAI_16BIT_TX,
+ MXC_DMA_ESAI_24BIT_RX,
+ MXC_DMA_ESAI_24BIT_TX,
+ MXC_DMA_TEST_RAM2D2RAM,
+ MXC_DMA_TEST_RAM2RAM2D,
+ MXC_DMA_TEST_RAM2D2RAM2D,
+ MXC_DMA_TEST_RAM2RAM,
+ MXC_DMA_TEST_HW_CHAINING,
+ MXC_DMA_TEST_SW_CHAINING
+} mxc_dma_device_t;
+
+/*! This defines the prototype of callback funtion registered by the drivers */
+typedef void (*mxc_dma_callback_t) (void *arg, int error_status,
+ unsigned int count);
+
+/*! This defines the type of DMA transfer requested */
+typedef enum mxc_dma_mode {
+ MXC_DMA_MODE_READ,
+ MXC_DMA_MODE_WRITE,
+} mxc_dma_mode_t;
+
+/*! This defines the DMA channel parameters */
+typedef struct mxc_dma_channel {
+ unsigned int active:1; /*!< When there has a active tranfer, it is set to 1 */
+ unsigned int lock; /*!< Defines the channel is allocated or not */
+ int curr_buf; /*!< Current buffer */
+ mxc_dma_mode_t mode; /*!< Read or Write */
+ unsigned int channel; /*!< Channel info */
+ unsigned int dynamic:1; /*!< Channel not statically allocated when 1 */
+ char *dev_name; /*!< Device name */
+ void *private; /*!< Private structure for platform */
+ mxc_dma_callback_t cb_fn; /*!< The callback function */
+ void *cb_args; /*!< The argument of callback function */
+} mxc_dma_channel_t;
+
+/*! This structure contains the information about a dma transfer */
+typedef struct mxc_dma_requestbuf {
+ dma_addr_t src_addr; /*!< source address */
+ dma_addr_t dst_addr; /*!< destination address */
+ int num_of_bytes; /*!< the length of this transfer : bytes */
+} mxc_dma_requestbuf_t;
+
+/*! This struct contains the information for asrc special*/
+struct dma_channel_asrc_info {
+ u32 channs; /*!< data channels in asrc */
+};
+
+/*! This struct contains the information for device special*/
+struct dma_channel_info {
+ struct dma_channel_asrc_info asrc; /*!< asrc special information */
+};
+
+#if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX21)
+#include <mach/mx2_dma.h>
+#else
+#include <mach/sdma.h>
+#endif
+
+/*!
+ * This function is generally called by the driver at open time.
+ * The DMA driver would do any initialization steps that is required
+ * to get the channel ready for data transfer.
+ *
+ * @param channel_id a pre-defined id. The peripheral driver would specify
+ * the id associated with its peripheral. This would be
+ * used by the DMA driver to identify the peripheral
+ * requesting DMA and do the necessary setup on the
+ * channel associated with the particular peripheral.
+ * The DMA driver could use static or dynamic DMA channel
+ * allocation.
+ * @param dev_name module name or device name
+ * @param data the customized parameter for special channel.
+ * @return returns a negative number on error if request for a DMA channel did not
+ * succeed, returns the channel number to be used on success.
+ */
+extern int mxc_dma_request_ext(mxc_dma_device_t channel_id, char *dev_name,
+ struct dma_channel_info *info);
+
+static inline int mxc_dma_request(mxc_dma_device_t channel_id, char *dev_name)
+{
+ return mxc_dma_request_ext(channel_id, dev_name, NULL);
+}
+
+/*!
+ * This function is generally called by the driver at close time. The DMA
+ * driver would do any cleanup associated with this channel.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @return returns a negative number on error or 0 on success
+ */
+extern int mxc_dma_free(int channel_num);
+
+/*!
+ * This function would just configure the buffers specified by the user into
+ * dma channel. The caller must call mxc_dma_enable to start this transfer.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @param dma_buf an array of physical addresses to the user defined
+ * buffers. The caller must guarantee the dma_buf is
+ * available until the transfer is completed.
+ * @param num_buf number of buffers in the array
+ * @param mode specifies whether this is READ or WRITE operation
+ * @return This function returns a negative number on error if buffer could not be
+ * added with DMA for transfer. On Success, it returns 0
+ */
+extern int mxc_dma_config(int channel_num, mxc_dma_requestbuf_t *dma_buf,
+ int num_buf, mxc_dma_mode_t mode);
+
+/*!
+ * This function would just configure the scatterlist specified by the
+ * user into dma channel. This is a slight variation of mxc_dma_config(),
+ * it is provided for the convenience of drivers that have a scatterlist
+ * passed into them. It is the calling driver's responsibility to have the
+ * correct physical address filled in the "dma_address" field of the
+ * scatterlist.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @param sg a scatterlist of buffers. The caller must guarantee
+ * the dma_buf is available until the transfer is
+ * completed.
+ * @param num_buf number of buffers in the array
+ * @param num_of_bytes total number of bytes to transfer. If set to 0, this
+ * would imply to use the length field of the scatterlist
+ * for each DMA transfer. Else it would calculate the size
+ * for each DMA transfer.
+ * @param mode specifies whether this is READ or WRITE operation
+ * @return This function returns a negative number on error if buffer could not
+ * be added with DMA for transfer. On Success, it returns 0
+ */
+extern int mxc_dma_sg_config(int channel_num, struct scatterlist *sg,
+ int num_buf, int num_of_bytes,
+ mxc_dma_mode_t mode);
+
+/*!
+ * This function is provided if the driver would like to set/change its
+ * callback function.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @param callback a callback function to provide notification on transfer
+ * completion, user could specify NULL if he does not wish
+ * to be notified
+ * @param arg an argument that gets passed in to the callback
+ * function, used by the user to do any driver specific
+ * operations.
+ * @return this function returns a negative number on error if the callback
+ * could not be set for the channel or 0 on success
+ */
+extern int mxc_dma_callback_set(int channel_num, mxc_dma_callback_t callback,
+ void *arg);
+
+/*!
+ * This stops the DMA channel and any ongoing transfers. Subsequent use of
+ * mxc_dma_enable() will restart the channel and restart the transfer.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @return returns a negative number on error or 0 on success
+ */
+extern int mxc_dma_disable(int channel_num);
+
+/*!
+ * This starts DMA transfer. Or it restarts DMA on a stopped channel
+ * previously stopped with mxc_dma_disable().
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @return returns a negative number on error or 0 on success
+ */
+extern int mxc_dma_enable(int channel_num);
+
#endif
diff --git a/arch/arm/plat-mxc/include/mach/dptc.h b/arch/arm/plat-mxc/include/mach/dptc.h
new file mode 100644
index 000000000000..ac897bbea2a7
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/dptc.h
@@ -0,0 +1,186 @@
+
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_DPTC_H__
+#define __ASM_ARCH_MXC_DPTC_H__
+
+#include <mach/dvfs_dptc_struct.h>
+
+/*!
+ * DPTC proc file system entry name
+ */
+#define PROC_NODE_NAME "dptc"
+
+int __init init_dptc_controller(dvfs_dptc_params_s * params);
+
+/*!
+ * This function enables the DPTC module. this function updates the DPTC
+ * thresholds, updates the PMIC, unmasks the DPTC interrupt and enables
+ * the DPTC module
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ *
+ * @return 0 if DPTC module was enabled else returns -EINVAL.
+ */
+int start_dptc(dvfs_dptc_params_s * params);
+/*!
+ * This function disables the DPTC module.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ *
+ * @return 0 if DPTC module was disabled else returns -EINVAL.
+ */
+int stop_dptc(dvfs_dptc_params_s * params);
+/*!
+ * This function updates the drivers current working point index. This index is
+ * used for access the current DTPC table entry and it corresponds to the
+ * current CPU working point measured by the DPTC hardware.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param new_wp New working point index value to be set.
+ *
+ */
+void set_dptc_wp(dvfs_dptc_params_s * params, int new_wp);
+/*!
+ * This function updates the DPTC threshold registers.
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ * @param freq_index translation table index of the current CPU
+ * frequency.
+ *
+ */
+void update_dptc_thresholds(dvfs_dptc_tables_s * dptc_tables_ptr,
+ int wp, int freq_index);
+/*!
+ * This function adds a new entry to the DPTC log buffer.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ * @param wp value of the working point index written
+ * to the log buffer.
+ * @param freq_index value of the frequency index written to
+ * the log buffer.
+ *
+ * @return number of log buffer entries.
+ *
+ */
+
+void add_dptc_log_entry(dvfs_dptc_params_s * params,
+ dptc_log_s * dptc_log, int wp, int freq_index);
+
+/*!
+ * This function updates the CPU voltage, produced by PMIC, by calling PMIC
+ * driver functions.
+ *
+ * @param dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ */
+void set_pmic_voltage(dvfs_dptc_tables_s * dptc_tables_ptr, int wp);
+
+/*!
+ * This function enables the DPTC reference circuits.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param rc_state each high bit specifies which
+ * reference circuite to enable.
+ * @return 0 on success, error code on failure
+ */
+int enable_ref_circuits(dvfs_dptc_params_s * params, unsigned char rc_state);
+
+/*!
+ * This function disables the DPTC reference circuits.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param rc_state each high bit specifies which
+ * reference circuite to disable
+ * @return 0 on success, error code on failure
+ */
+int disable_ref_circuits(dvfs_dptc_params_s * params, unsigned char rc_state);
+
+/*!
+ * This function is the DPTC Interrupt handler.
+ * This function wakes-up the dptc_workqueue_handler function that handles the
+ * DPTC interrupt.
+ */
+void dptc_irq(void);
+
+/*!
+ * This function updates the drivers current frequency index.This index is
+ * used for access the current DTPC table entry and it corresponds to the
+ * current CPU frequency (each CPU frequency has a separate index number
+ * according to the loaded DPTC table).
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ * @param freq_index New frequency index value to be set.
+ *
+ * @return 0 if the frequency index was updated (the new index is a
+ * valid index and the DPTC module isn't active) else returns
+ * -EINVAL.
+ *
+ */
+int set_dptc_curr_freq(dvfs_dptc_params_s * params, unsigned int freq_index);
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+/*
+ * DPTC SDMA callback.
+ * Updates the PMIC voltage
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ */
+void dptc_sdma_callback(dvfs_dptc_params_s * params);
+#endif
+
+/*!
+ * This function is called to put the DPTC in a low power state.
+ *
+ * @param pdev the device structure used to give information on which
+ * device to suspend (not relevant for DPTC)
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+int mxc_dptc_suspend(struct platform_device *pdev, pm_message_t state);
+
+/*!
+ * This function is called to put the DPTC in a low power state.
+ *
+ */
+void dptc_suspend(void);
+
+/*!
+ * This function is called to resume the DPTC from a low power state.
+ *
+ * @param pdev the device structure used to give information on which
+ * device to suspend (not relevant for DPTC)
+ *
+ * @return The function always returns 0.
+ */
+int mxc_dptc_resume(struct platform_device *dev);
+
+/*!
+ * This function is called to resume the DPTC from a low power state.
+ *
+ */
+void dptc_resume(void);
+
+/*!
+ * This function initializes DPTC according to turbo mode status
+ *
+ * @param status Turbo mode disable, 1 - turbo mode enabled
+ *
+ */
+void dptc_set_turbo_mode(unsigned int status);
+
+#endif /* __ASM_ARCH_MXC_DPTC_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/dvfs_dptc_struct.h b/arch/arm/plat-mxc/include/mach/dvfs_dptc_struct.h
new file mode 100644
index 000000000000..006bcf293d63
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/dvfs_dptc_struct.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file arch-mxc/dvfs_dptc_struct.h
+ *
+ * @brief MXC dvfs & dptc structure definitions file.
+ *
+ * @ingroup PM_MX27 PM_MX31 PM_MXC91321 PM_MXC91311
+ */
+#ifndef __ASM_ARCH_MXC_DVFS_DPTC_STRUCT_H__
+#define __ASM_ARCH_MXC_DVFS_DPTC_STRUCT_H__
+
+#include <linux/semaphore.h>
+#include <mach/pm_api.h>
+
+/*!
+ * Number of entries in the DPTC log buffer
+ */
+#define LOG_ENTRIES 1024
+
+/*!
+ * Log buffer Structure.\n
+ * This structure records the DPTC changes. \n
+ * This structure can be read by the user using the proc file system DPTC read entry.
+ */
+typedef struct {
+ /*!
+ * Index to the head of the log buffer
+ */
+ int head;
+
+ /*!
+ * Index to the tail of the log buffer
+ */
+ int tail;
+
+ /*!
+ * Mutex to allow access to the log buffer
+ */
+ struct semaphore mutex;
+
+ /*!
+ * Array of log buffer entries
+ */
+ dptc_log_entry_s entries[LOG_ENTRIES];
+} dptc_log_s;
+
+/*!
+ * DPTC driver data structure.\n
+ * Holds all driver parameters and data structures.
+ */
+typedef struct {
+ /*!
+ * This variable holds the current frequency index
+ */
+ int current_freq_index;
+
+ /*!
+ * Boolean variable, if TRUE the DPTC module is enabled
+ * if FALSE the DPTC module is disabled
+ */
+ int dptc_is_active;
+
+ /*!
+ * Boolean variable, if TRUE turbo mode enable
+ * if FALSE turbo mode disabled
+ */
+ int turbo_mode_active;
+
+ /*!
+ * Boolean variable, if TRUE the DVFS module is enabled
+ * if FALSE the DPTC module is disabled
+ */
+ int dvfs_is_active;
+
+ /*!
+ * Boolean variable, if TRUE the DPTC module is suspended
+ */
+ int suspended;
+
+ unsigned char rc_state;
+
+ /*!
+ * Pointer to the DVFS & DPTC translation table
+ */
+ dvfs_dptc_tables_s *dvfs_dptc_tables_ptr;
+
+ /*!
+ * The DPTC log buffer
+ */
+ dptc_log_s dptc_log_buffer;
+
+ /*!
+ * The DVFS log buffer
+ */
+ unsigned char *dvfs_log_buffer;
+
+ /*!
+ * The DVFS log buffer physical address (for SDMA)
+ */
+ dma_addr_t dvfs_log_buffer_phys;
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+ /*!
+ * SDMA channel number
+ */
+ int sdma_channel;
+
+ /*!
+ * This holds the previous working point
+ */
+ int prev_wp;
+
+ /*!
+ * Wait entry for predictive DVFS
+ */
+ wait_queue_head_t dvfs_pred_wait;
+#endif
+
+ /*!
+ * This holds the current DVFS mode
+ */
+ unsigned int dvfs_mode;
+
+ /*!
+ * Log buffer read pointer
+ */
+ unsigned char *read_ptr;
+
+ /*
+ * Number of characters in log buffer
+ */
+ int chars_in_buffer;
+} dvfs_dptc_params_s;
+
+/*!
+ * This struct contains the array with values of supported frequencies in Hz
+ */
+typedef struct {
+ /*
+ * Number of supported states
+ */
+ unsigned int num_of_states;
+ /*!
+ * Array of frequencies
+ */
+ unsigned int *freqs;
+} dvfs_states_table;
+
+/*
+ * if not defined define TREU and FALSE values.
+ */
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif /* TRUE */
+
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/entry-macro.S b/arch/arm/plat-mxc/include/mach/entry-macro.S
index 11632028f7d1..92f553baf447 100644
--- a/arch/arm/plat-mxc/include/mach/entry-macro.S
+++ b/arch/arm/plat-mxc/include/mach/entry-macro.S
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2007 Lennert Buytenhek <buytenh@wantstofly.org>
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -9,6 +9,8 @@
* published by the Free Software Foundation.
*/
+#include <mach/hardware.h>
+
#define AVIC_NIMASK 0x04
@ this macro disables fast irq (not implemented)
@@ -16,10 +18,14 @@
.endm
.macro get_irqnr_preamble, base, tmp
+#ifdef CONFIG_MXC_TZIC
+ ldr \base, =TZIC_IO_ADDRESS(TZIC_BASE_ADDR)
+#else
ldr \base, =AVIC_IO_ADDRESS(AVIC_BASE_ADDR)
#ifdef CONFIG_MXC_IRQ_PRIOR
ldr r4, [\base, #AVIC_NIMASK]
#endif
+#endif
.endm
.macro arch_ret_to_user, tmp1, tmp2
@@ -29,6 +35,32 @@
@ and returns its number in irqnr
@ and returns if an interrupt occured in irqstat
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
+#ifdef CONFIG_MXC_TZIC
+ @ Load offset & priority of the highest priority
+ @ interrupt pending.
+ @ 0xD80 is HIPND0 register
+ ldr \irqnr, =0
+ ldr \irqstat, =0x0D80
+1000:
+ ldr \tmp, [\irqstat, \base]
+ cmp \tmp, #0
+ bne 1001f
+ addeq \irqnr, \irqnr, #32
+ addeq \irqstat, \irqstat, #4
+ cmp \irqnr, #128
+ blo 1000b
+ b 2001f
+1001: ldr \irqstat, =1
+1002: tst \tmp, \irqstat
+ bne 2002f
+ movs \tmp, \tmp, lsr #1
+ addne \irqnr, \irqnr, #1
+ bne 1002b
+2001:
+ ldr \irqnr, =0
+2002:
+ movs \irqnr, \irqnr
+#else
@ Load offset & priority of the highest priority
@ interrupt pending from AVIC_NIVECSR
ldr \irqstat, [\base, #0x40]
@@ -42,6 +74,7 @@
strne \tmp, [\base, #AVIC_NIMASK]
streq r4, [\base, #AVIC_NIMASK]
#endif
+#endif
.endm
@ irq priority table (not used)
diff --git a/arch/arm/plat-mxc/include/mach/fsl_usb.h b/arch/arm/plat-mxc/include/mach/fsl_usb.h
new file mode 100644
index 000000000000..f1650602f996
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/fsl_usb.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * USB Host side, platform-specific functionality.
+ */
+
+#include <linux/usb/fsl_xcvr.h>
+#include <mach/arc_otg.h>
+
+/* ehci_arc_hc_driver.flags value */
+#define FSL_PLATFORM_HC_FLAGS (HCD_USB2 | HCD_MEMORY)
+
+static void fsl_setup_phy(struct ehci_hcd *ehci,
+ enum fsl_usb2_phy_modes phy_mode,
+ int port_offset);
+
+static inline void fsl_platform_usb_setup(struct ehci_hcd *ehci)
+{
+ struct fsl_usb2_platform_data *pdata;
+
+ pdata = ehci_to_hcd(ehci)->self.controller->platform_data;
+ fsl_setup_phy(ehci, pdata->phy_mode, 0);
+}
+
+static inline void fsl_platform_set_host_mode(struct usb_hcd *hcd)
+{
+ unsigned int temp;
+ struct fsl_usb2_platform_data *pdata;
+
+ pdata = hcd->self.controller->platform_data;
+
+ if (pdata->xcvr_ops && pdata->xcvr_ops->set_host)
+ pdata->xcvr_ops->set_host();
+
+ /* set host mode */
+ temp = readl(hcd->regs + 0x1a8);
+ writel(temp | USBMODE_CM_HOST, hcd->regs + 0x1a8);
+}
+
+/* Needed for enable PP and i2c/serial transceivers */
+static inline void
+fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata, int on)
+{
+ u32 temp;
+
+ /* HCSPARAMS */
+ temp = readl(pdata->regs + 0x104);
+ /* Port Power Control */
+ if (temp & HCSPARAMS_PPC) {
+ temp = readl(pdata->regs + FSL_SOC_USB_PORTSC1);
+ writel(temp | PORT_POWER, pdata->regs + FSL_SOC_USB_PORTSC1);
+ }
+
+ if (pdata->xcvr_ops && pdata->xcvr_ops->set_vbus_power)
+ pdata->xcvr_ops->set_vbus_power(pdata->xcvr_ops, pdata, on);
+}
+
+/* Set USB AHB burst length for host */
+static inline void fsl_platform_set_ahb_burst(struct usb_hcd *hcd)
+{
+ struct fsl_usb2_platform_data *pdata;
+ unsigned int temp;
+
+ pdata = hcd->self.controller->platform_data;
+ if (pdata->change_ahb_burst) {
+ temp = readl(hcd->regs + FSL_SOC_USB_SBUSCFG);
+ writel((temp & (~(0x7))) | pdata->ahb_burst_mode,
+ hcd->regs + FSL_SOC_USB_SBUSCFG);
+ }
+
+ /* Increase TX fifo threshold for USB+ATA for i.mx35 2.0 */
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1) {
+ temp = readl(hcd->regs + FSL_SOC_USB_TXFILLTUNING);
+ /* Change TX FIFO threshold to be 0x20 */
+ writel((temp & (~(0x3f << 16))) | (0x20 << 16),
+ hcd->regs + FSL_SOC_USB_TXFILLTUNING);
+ }
+}
diff --git a/arch/arm/plat-mxc/include/mach/fsl_usb_gadget.h b/arch/arm/plat-mxc/include/mach/fsl_usb_gadget.h
new file mode 100644
index 000000000000..d3c581e70765
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/fsl_usb_gadget.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * USB Gadget side, platform-specific functionality.
+ */
+
+#include <linux/usb/fsl_xcvr.h>
+
+/* Needed for i2c/serial transceivers */
+static inline void
+fsl_platform_set_device_mode(struct fsl_usb2_platform_data *pdata)
+{
+ if (pdata->xcvr_ops && pdata->xcvr_ops->set_device)
+ pdata->xcvr_ops->set_device();
+}
+
+static inline void
+fsl_platform_pullup_enable(struct fsl_usb2_platform_data *pdata)
+{
+ if (pdata->xcvr_ops && pdata->xcvr_ops->pullup)
+ pdata->xcvr_ops->pullup(1);
+}
+
+static inline void
+fsl_platform_pullup_disable(struct fsl_usb2_platform_data *pdata)
+{
+ if (pdata->xcvr_ops && pdata->xcvr_ops->pullup)
+ pdata->xcvr_ops->pullup(0);
+}
diff --git a/arch/arm/plat-mxc/include/mach/gpio.h b/arch/arm/plat-mxc/include/mach/gpio.h
index 65eedc0d196f..a2d90c5177ab 100644
--- a/arch/arm/plat-mxc/include/mach/gpio.h
+++ b/arch/arm/plat-mxc/include/mach/gpio.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or
@@ -27,14 +27,18 @@
#define gpio_set_value __gpio_set_value
#define gpio_cansleep __gpio_cansleep
-#define gpio_to_irq(gpio) (MXC_MAX_INT_LINES + (gpio))
-#define irq_to_gpio(irq) ((irq) - MXC_MAX_INT_LINES)
+#define gpio_to_irq(gpio) (MXC_GPIO_IRQ_START + (gpio))
+#define irq_to_gpio(irq) ((irq) - MXC_GPIO_IRQ_START)
struct mxc_gpio_port {
void __iomem *base;
int irq;
+ int irq_high;
int virtual_irq_start;
struct gpio_chip chip;
+ u32 both_edges;
+ u32 suspend_wakeup;
+ u32 saved_wakeup;
};
int mxc_gpio_init(struct mxc_gpio_port*, int);
diff --git a/arch/arm/plat-mxc/include/mach/hardware.h b/arch/arm/plat-mxc/include/mach/hardware.h
index 3caadeeda701..57a7a468c8f8 100644
--- a/arch/arm/plat-mxc/include/mach/hardware.h
+++ b/arch/arm/plat-mxc/include/mach/hardware.h
@@ -1,20 +1,11 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
- * Copyright 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.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
*/
#ifndef __ASM_ARCH_MXC_HARDWARE_H__
@@ -22,16 +13,150 @@
#include <asm/sizes.h>
+/*
+ * ---------------------------------------------------------------------------
+ * Processor specific defines
+ * ---------------------------------------------------------------------------
+ */
+#define CHIP_REV_1_0 0x10
+#define CHIP_REV_1_1 0x11
+#define CHIP_REV_1_2 0x12
+#define CHIP_REV_1_3 0x13
+#define CHIP_REV_2_0 0x20
+#define CHIP_REV_2_1 0x21
+#define CHIP_REV_2_2 0x22
+#define CHIP_REV_2_3 0x23
+#define CHIP_REV_3_0 0x30
+#define CHIP_REV_3_1 0x31
+#define CHIP_REV_3_2 0x32
+
+#define BOARD_REV_2 0x100
+
+#ifndef __ASSEMBLY__
+extern unsigned int system_rev;
+#endif
+#define mxc_set_system_rev(part, rev) { \
+ system_rev = (part << 12) | rev; \
+}
+
+#define mxc_cpu() (system_rev >> 12)
+#define mxc_is_cpu(part) ((mxc_cpu() == part) ? 1 : 0)
+#define mxc_cpu_rev() (system_rev & 0xFF)
+#define mxc_cpu_rev_major() ((system_rev >> 4) & 0xF)
+#define mxc_cpu_rev_minor() (system_rev & 0xF)
+#define mxc_cpu_is_rev(rev) \
+ ((mxc_cpu_rev() == rev) ? 1 : ((mxc_cpu_rev() < rev) ? -1 : 2))
+#define MXC_REV(type) \
+static inline int type## _rev (int rev) \
+{ \
+ return (type() ? mxc_cpu_is_rev(rev) : 0); \
+}
+
#ifdef CONFIG_ARCH_MX3
# include <mach/mx31.h>
+#define cpu_is_mx31() (mxc_is_cpu(0x31)) /*system_rev got from Redboot */
+#define cpu_is_mx32() (mxc_is_cpu(0x32)) /*system_rev got from Redboot */
+#else
+#define cpu_is_mx31() (0)
+#define cpu_is_mx32() (0)
+#endif
+
+#ifdef CONFIG_ARCH_MX35
+#include <mach/mx35.h>
+#define cpu_is_mx35() (1)
+#define board_is_mx35(rev) ((system_rev & rev) ? 1 : 0)
+#else
+#define cpu_is_mx35() (0)
+#define board_is_mx35(rev) (0)
+#endif
+
+#ifdef CONFIG_ARCH_MX37
+#include <mach/mx37.h>
+#define cpu_is_mx37() (1)
+#define board_is_mx37(rev) ((system_rev & rev) ? 1 : 0)
+#else
+#define cpu_is_mx37() (0)
+#define board_is_mx37(rev) (0)
#endif
-#ifdef CONFIG_ARCH_MX2
-# ifdef CONFIG_MACH_MX27
-# include <mach/mx27.h>
-# endif
+#ifdef CONFIG_ARCH_MX51
+#include <mach/mx51.h>
+#define cpu_is_mx51() (1)
+#define board_is_mx51(rev) ((system_rev & rev) ? 1 : 0)
+/* BB25:Bit8 is set to 1, BB20: Bit8 is set to 0 */
+#define board_is_babbage_2_5() ((system_rev & 0x1FF) >> 8)
+#else
+#define cpu_is_mx51() (0)
+#define board_is_mx51(rev) (0)
+#define board_is_babbage_2_5() (0)
#endif
+#ifdef CONFIG_ARCH_MX21
+#include <mach/mx21.h>
+#define cpu_is_mx21() (1)
+#else
+#define cpu_is_mx21() (0)
+#endif
+
+#ifdef CONFIG_ARCH_MX25
+#include <mach/mx25.h>
+#define cpu_is_mx25() (1)
+#else
+#define cpu_is_mx25() (0)
+#endif
+
+#ifdef CONFIG_ARCH_MX27
+#include <mach/mx27.h>
+#define cpu_is_mx27() (1)
+#else
+#define cpu_is_mx27() (0)
+#endif
+
+#define cpu_is_mx2() \
+ (cpu_is_mx21() || cpu_is_mx25() || cpu_is_mx27())
+#define cpu_is_mx3() \
+ (cpu_is_mx31() || cpu_is_mx35() || cpu_is_mx37())
+
+
+#ifndef __ASSEMBLY__
+/*
+ * Create inline functions to test for cpu revision
+ * Function name is cpu_is_<cpu name>_rev(rev)
+ *
+ * Returns:
+ * 0 - not the cpu queried
+ * 1 - cpu and revision match
+ * 2 - cpu matches, but cpu revision is greater than queried rev
+ * -1 - cpu matches, but cpu revision is less than queried rev
+ */
+MXC_REV(cpu_is_mx21);
+MXC_REV(cpu_is_mx25);
+MXC_REV(cpu_is_mx27);
+MXC_REV(cpu_is_mx31);
+MXC_REV(cpu_is_mx32);
+MXC_REV(cpu_is_mx35);
+MXC_REV(cpu_is_mx37);
+MXC_REV(cpu_is_mx51);
+#endif
#include <mach/mxc.h>
+#define MXC_MAX_GPIO_LINES (GPIO_NUM_PIN * GPIO_PORT_NUM)
+
+#define MXC_EXP_IO_BASE (MXC_MAX_INT_LINES + MXC_MAX_GPIO_LINES)
+#define MXC_MAX_EXP_IO_LINES 16
+
+#ifdef CONFIG_MXC_PSEUDO_IRQS
+#define MXC_PSEUDO_IO_BASE (MXC_EXP_IO_BASE + MXC_MAX_EXP_IO_LINES)
+#define MXC_MAX_PSEUDO_IO_LINES 16
+#else
+#define MXC_MAX_PSEUDO_IO_LINES 0
+#endif
+
+#ifndef MXC_INT_FORCE
+#define MXC_INT_FORCE -1
+#endif
+#define MXC_MAX_INTS (MXC_MAX_INT_LINES + \
+ MXC_MAX_GPIO_LINES + \
+ MXC_MAX_EXP_IO_LINES + \
+ MXC_MAX_PSEUDO_IO_LINES)
#endif /* __ASM_ARCH_MXC_HARDWARE_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/hw_events.h b/arch/arm/plat-mxc/include/mach/hw_events.h
new file mode 100644
index 000000000000..f0aa3ad7a266
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/hw_events.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * hw_events.h
+ * include the headset/cvbs interrupt detect
+ */
+
+#ifndef HW_EVENT_H
+#define HW_EVENT_H
+
+#define HW_EVENT_GROUP 2
+#define HWE_DEF_PRIORITY 1
+#define HWE_HIGH_PRIORITY 0
+
+typedef enum {
+
+ HWE_PHONEJACK_PLUG = 0,
+ HWE_BAT_CHARGER_PLUG,
+ HWE_BAT_CHARGER_OVERVOLTAGE,
+ HWE_BAT_BATTERY_LOW,
+ HWE_BAT_POWER_FAILED,
+ HWE_BAT_CHARGER_FULL,
+ HWE_POWER_KEY,
+} HW_EVENT_T;
+
+typedef enum {
+
+ PJT_NONE = 0,
+ PJT_CVBS,
+ PJT_HEADSET,
+} PHONEJACK_TYPE;
+
+typedef enum {
+
+ PWRK_UNPRESS = 0,
+ PWRK_PRESS,
+} POWERKEY_TYPE;
+
+typedef enum {
+
+ UNPLUG = 0,
+ PLUGGED,
+} PLUG_TYPE;
+
+struct mxc_hw_event {
+ unsigned int event;
+ int args;
+};
+
+#ifdef __KERNEL__
+extern int hw_event_send(int priority, struct mxc_hw_event *new_event);
+#endif
+
+#endif /* HW_EVENT_H */
diff --git a/arch/arm/plat-mxc/include/mach/io.h b/arch/arm/plat-mxc/include/mach/io.h
index 5d4cb1196441..d1df347b1497 100644
--- a/arch/arm/plat-mxc/include/mach/io.h
+++ b/arch/arm/plat-mxc/include/mach/io.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -14,29 +14,34 @@
/* Allow IO space to be anywhere in the memory */
#define IO_SPACE_LIMIT 0xffffffff
-#ifdef CONFIG_ARCH_MX3
-#define __arch_ioremap __mx3_ioremap
-#define __arch_iounmap __iounmap
-
-static inline void __iomem *
-__mx3_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
-{
- if (mtype == MT_DEVICE) {
- /* Access all peripherals below 0x80000000 as nonshared device
- * but leave l2cc alone.
- */
- if ((phys_addr < 0x80000000) && ((phys_addr < L2CC_BASE_ADDR) ||
- (phys_addr >= L2CC_BASE_ADDR + L2CC_SIZE)))
- mtype = MT_DEVICE_NONSHARED;
- }
-
- return __arm_ioremap(phys_addr, size, mtype);
-}
-#endif
+extern void __iomem *__mxc_ioremap(unsigned long cookie, size_t size,
+ unsigned int mtype);
+extern void __mxc_iounmap(void __iomem *addr);
+
+#define __arch_ioremap(a, s, f) __mxc_ioremap(a, s, f)
+#define __arch_iounmap(a) __mxc_iounmap(a)
/* io address mapping macro */
#define __io(a) ((void __iomem *)(a))
#define __mem_pci(a) (a)
+/*!
+ * This function is called to read a CPLD register over CSPI.
+ *
+ * @param offset number of the cpld register to be read
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+unsigned int spi_cpld_read(unsigned int offset);
+
+/*!
+ * This function is called to write to a CPLD register over CSPI.
+ *
+ * @param offset number of the cpld register to be written
+ * @param reg_val value to be written
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+unsigned int spi_cpld_write(unsigned int offset, unsigned int reg_val);
#endif
diff --git a/arch/arm/plat-mxc/include/mach/irqs.h b/arch/arm/plat-mxc/include/mach/irqs.h
index b55bba35e18a..680a242b2d38 100644
--- a/arch/arm/plat-mxc/include/mach/irqs.h
+++ b/arch/arm/plat-mxc/include/mach/irqs.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -14,4 +14,24 @@
#include <mach/hardware.h>
extern void imx_irq_set_priority(unsigned char irq, unsigned char prio);
+#define MXC_IRQ_TO_EXPIO(irq) ((irq) - MXC_EXP_IO_BASE)
+
+#define MXC_IRQ_TO_GPIO(irq) ((irq) - MXC_GPIO_INT_BASE)
+#define MXC_GPIO_TO_IRQ(x) (MXC_GPIO_INT_BASE + (x))
+
+/* Number of normal interrupts */
+#define NR_IRQS MXC_MAX_INTS
+
+/* Number of fast interrupts */
+#define NR_FIQS MXC_MAX_INTS
+
+/* Number of interrupts */
+#define MXC_GPIO_IRQ_START MXC_MAX_INT_LINES
+
+/*
+ * This function is used to get the AVIC Lo and Hi interrupts
+ * that are enabled as wake up sources to wake up the core from suspend
+ */
+void mxc_get_wake_irq(u32 * wake_src[]);
+
#endif /* __ASM_ARCH_MXC_IRQS_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/memory.h b/arch/arm/plat-mxc/include/mach/memory.h
index d7a8d3ebed57..e101be98acf4 100644
--- a/arch/arm/plat-mxc/include/mach/memory.h
+++ b/arch/arm/plat-mxc/include/mach/memory.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -11,7 +11,57 @@
#ifndef __ASM_ARCH_MXC_MEMORY_H__
#define __ASM_ARCH_MXC_MEMORY_H__
-#include <mach/hardware.h>
+#include <asm/page.h>
+#include <asm/sizes.h>
+
+/* Start of physical RAM */
+#if defined(CONFIG_MACH_MX35EVB) || defined(CONFIG_ARCH_MX51)
+#define PHYS_OFFSET UL(0x90000000)
+#endif
+
+#ifdef CONFIG_MACH_MX27ADS
+#define PHYS_OFFSET UL(0xA0000000)
+#endif
+
+#ifdef CONFIG_MACH_MX37_3DS
+#define PHYS_OFFSET UL(0x40000000)
+#endif
+
+#ifndef PHYS_OFFSET
+#define PHYS_OFFSET UL(0x80000000)
+#endif
+
+/* Size of contiguous memory for DMA and other h/w blocks */
+#ifdef CONFIG_ARCH_MX51
+#define CONSISTENT_DMA_SIZE (64 * SZ_1M)
+#else
+#define CONSISTENT_DMA_SIZE (32 * SZ_1M)
+#endif
+
+#ifndef __ASSEMBLY__
+
+#ifdef CONFIG_DMA_ZONE_SIZE
+#define MXC_DMA_ZONE_SIZE ((CONFIG_DMA_ZONE_SIZE * SZ_1M) >> PAGE_SHIFT)
+#else
+#define MXC_DMA_ZONE_SIZE ((12 * SZ_1M) >> PAGE_SHIFT)
+#endif
+
+static inline void __arch_adjust_zones(int node, unsigned long *zone_size,
+ unsigned long *zhole_size)
+{
+ if (node != 0)
+ return;
+ /* Create separate zone to reserve memory for DMA */
+ zone_size[1] = zone_size[0] - MXC_DMA_ZONE_SIZE;
+ zone_size[0] = MXC_DMA_ZONE_SIZE;
+ zhole_size[1] = zhole_size[0];
+ zhole_size[0] = 0;
+}
+
+#define arch_adjust_zones(node, size, holes) \
+ __arch_adjust_zones(node, size, holes)
+
+#endif
/*
* Virtual view <-> DMA view memory address translations
diff --git a/arch/arm/plat-mxc/include/mach/mmc.h b/arch/arm/plat-mxc/include/mach/mmc.h
new file mode 100644
index 000000000000..cf76cceacb3b
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mmc.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MMC_H__
+#define __ASM_ARCH_MXC_MMC_H__
+
+#include <linux/mmc/host.h>
+
+struct mxc_mmc_platform_data {
+ unsigned int ocr_mask; /* available voltages */
+ unsigned int vendor_ver;
+ unsigned int caps;
+ unsigned int min_clk;
+ unsigned int max_clk;
+ unsigned int clk_flg; /* 1 clock enable, 0 not */
+ unsigned int reserved:16;
+ unsigned int card_fixed:1;
+ unsigned int card_inserted_state:1;
+// u32 (*translate_vdd)(struct device *, unsigned int);
+ unsigned int (*status) (struct device *);
+ int (*wp_status) (struct device *);
+ char *power_mmc;
+ char *clock_mmc;
+};
+
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/mtd-xip.h b/arch/arm/plat-mxc/include/mach/mtd-xip.h
new file mode 100644
index 000000000000..340bb9c1562c
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mtd-xip.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * MTD primitives for XIP support. Architecture specific functions
+ *
+ * Do not include this file directly. It's included from linux/mtd/xip.h
+ */
+
+/*
+ * 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 __ARCH_MXC_MTD_XIP_H__
+#define __ARCH_MXC_MTD_XIP_H__
+
+#include <linux/clocksource.h>
+#include <mach/hardware.h>
+#include <mach/system.h>
+
+#define xip_irqpending() \
+ ((__raw_readl(AVIC_NIVECSR) & __raw_readl(AVIC_FIVECSR)) != 0xFFFFFFFF)
+
+extern struct clocksource *mtd_xip_clksrc;
+
+#define xip_currtime() (unsigned long)clocksource_read(mtd_xip_clksrc)
+
+#if CLOCK_TICK_RATE > 1000000
+#define NUMERATOR 1
+#define DENOMINATOR (CLOCK_TICK_RATE/1000000 + 1)
+#else
+#define NUMERATOR (1000000/CLOCK_TICK_RATE)
+#define DENOMINATOR 1
+#endif
+
+static inline unsigned long xip_elapsed_since(unsigned long x)
+{
+ return (((xip_currtime() - x) * NUMERATOR) / DENOMINATOR);
+}
+
+/*
+ * Wait For Interrupt command for XIP kernel to put CPU in Idle mode
+ */
+#define xip_cpu_idle() arch_idle()
+
+#endif /* __ARCH_MXC_MTD_XIP_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx21.h b/arch/arm/plat-mxc/include/mach/mx21.h
new file mode 100644
index 000000000000..89a988079a82
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mx21.h
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup MSL Machine Specific Layer (MSL)
+ */
+
+/*!
+ * @defgroup System System-wide Misc Files for MSL
+ * @ingroup MSL
+ */
+
+/*!
+ * @file arch-mxc/mx21.h
+ * @brief This file contains register definitions.
+ *
+ * @ingroup System
+ */
+
+#ifndef __ASM_ARCH_MXC_MX21_H__
+#define __ASM_ARCH_MXC_MX21_H__
+
+#ifndef __ASM_ARCH_MXC_HARDWARE_H__
+#error "Do not include directly."
+#endif
+
+/*!
+ * defines the OS clock tick rate
+ */
+#define CLOCK_TICK_RATE 13300000
+
+/*!
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+#define MXC_UART_NR 4
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive Irda data.
+ */
+#define MXC_UART_IR_RXDMUX 0x0004
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive UART data.
+ */
+#define MXC_UART_RXDMUX 0x0004
+
+/*
+ * IRAM
+ */
+#define IRAM_BASE_ADDR 0xFFFFD800 /* internal ram */
+#define IRAM_BASE_ADDR_VIRT 0xD0000000
+#define IRAM_SIZE (SZ_4K + SZ_1K*2)
+
+/*
+ * Register offests.
+ */
+#define AIPI_BASE_ADDR 0x10000000
+#define AIPI_BASE_ADDR_VIRT 0xD4000000
+#define AIPI_SIZE SZ_1M
+
+#define DMA_BASE_ADDR (AIPI_BASE_ADDR + 0x01000)
+#define WDOG_BASE_ADDR (AIPI_BASE_ADDR + 0x02000)
+#define GPT1_BASE_ADDR (AIPI_BASE_ADDR + 0x03000)
+#define GPT2_BASE_ADDR (AIPI_BASE_ADDR + 0x04000)
+#define GPT3_BASE_ADDR (AIPI_BASE_ADDR + 0x05000)
+#define PWM_BASE_ADDR (AIPI_BASE_ADDR + 0x06000)
+#define RTC_BASE_ADDR (AIPI_BASE_ADDR + 0x07000)
+#define KPP_BASE_ADDR (AIPI_BASE_ADDR + 0x08000)
+#define OWIRE_BASE_ADDR (AIPI_BASE_ADDR + 0x09000)
+#define UART1_BASE_ADDR (AIPI_BASE_ADDR + 0x0A000)
+#define UART2_BASE_ADDR (AIPI_BASE_ADDR + 0x0B000)
+#define UART3_BASE_ADDR (AIPI_BASE_ADDR + 0x0C000)
+#define UART4_BASE_ADDR (AIPI_BASE_ADDR + 0x0D000)
+#define CSPI1_BASE_ADDR (AIPI_BASE_ADDR + 0x0E000)
+#define CSPI2_BASE_ADDR (AIPI_BASE_ADDR + 0x0F000)
+#define SSI1_BASE_ADDR (AIPI_BASE_ADDR + 0x10000)
+#define SSI2_BASE_ADDR (AIPI_BASE_ADDR + 0x11000)
+#define I2C_BASE_ADDR (AIPI_BASE_ADDR + 0x12000)
+#define SDHC1_BASE_ADDR (AIPI_BASE_ADDR + 0x13000)
+#define SDHC2_BASE_ADDR (AIPI_BASE_ADDR + 0x14000)
+#define GPIO_BASE_ADDR (AIPI_BASE_ADDR + 0x15000)
+#define AUDMUX_BASE_ADDR (AIPI_BASE_ADDR + 0x16000)
+#define CSPI3_BASE_ADDR (AIPI_BASE_ADDR + 0x17000)
+#define LCDC_BASE_ADDR (AIPI_BASE_ADDR + 0x21000)
+#define SLCDC_BASE_ADDR (AIPI_BASE_ADDR + 0x22000)
+#define USBOTG_BASE_ADDR (AIPI_BASE_ADDR + 0x24000)
+#define EMMA_BASE_ADDR (AIPI_BASE_ADDR + 0x26000)
+#define CCM_BASE_ADDR (AIPI_BASE_ADDR + 0x27000)
+#define FIRI_BASE_ADDR (AIPI_BASE_ADDR + 0x28000)
+#define JAM_BASE_ADDR (AIPI_BASE_ADDR + 0x3E000)
+#define MAX_BASE_ADDR (AIPI_BASE_ADDR + 0x3F000)
+
+/*
+ * ROMP and AVIC
+ */
+#define ROMP_BASE_ADDR 0x10041000
+
+#define AVIC_BASE_ADDR 0x10040000
+
+#define SAHB1_BASE_ADDR 0x80000000
+#define SAHB1_BASE_ADDR_VIRT 0xD4100000
+#define SAHB1_SIZE SZ_1M
+
+#define CSI_BASE_ADDR (SAHB1_BASE_ADDR + 0x0000)
+
+/*
+ * NAND, SDRAM, WEIM, M3IF, EMI controllers
+ */
+#define X_MEMC_BASE_ADDR 0xDF000000
+#define X_MEMC_BASE_ADDR_VIRT 0xD4320000
+#define X_MEMC_SIZE SZ_64K
+
+#define SDRAMC_BASE_ADDR (X_MEMC_BASE_ADDR)
+#define WEIM_BASE_ADDR (X_MEMC_BASE_ADDR + 0x1000)
+#define PCMCIA_CTL_BASE_ADDR (X_MEMC_BASE_ADDR + 0x2000)
+#define NFC_BASE_ADDR (X_MEMC_BASE_ADDR + 0x3000)
+
+/*
+ * Memory regions and CS
+ */
+#define SDRAM_BASE_ADDR 0xC0000000
+#define CSD1_BASE_ADDR 0xC4000000
+
+#define CS0_BASE_ADDR 0xC8000000
+
+#define CS1_BASE_ADDR 0xCC000000
+#define CS1_BASE_ADDR_VIRT 0xEB000000
+#define CS1_SIZE SZ_16M
+
+#define CS2_BASE_ADDR 0xD0000000
+#define CS3_BASE_ADDR 0xD1000000
+
+#define CS4_BASE_ADDR 0xD2000000
+
+#define CS5_BASE_ADDR 0xD2000000
+#define PCMCIA_MEM_BASE_ADDR 0xD4000000
+
+/*!
+ * 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 IO_ADDRESS(x) \
+ (((x >= AIPI_BASE_ADDR) && (x < (AIPI_BASE_ADDR + AIPI_SIZE))) ? AIPI_IO_ADDRESS(x):\
+ ((x >= SAHB1_BASE_ADDR) && (x < (SAHB1_BASE_ADDR + SAHB1_SIZE))) ? SAHB1_IO_ADDRESS(x):\
+ ((x >= CS1_BASE_ADDR) && (x < (CS1_BASE_ADDR + CS1_SIZE))) ? CS1_IO_ADDRESS(x):\
+ ((x >= X_MEMC_BASE_ADDR) && (x < (X_MEMC_BASE_ADDR + X_MEMC_SIZE))) ? X_MEMC_IO_ADDRESS(x):\
+ 0xDEADBEEF)
+
+/*
+ * define the address mapping macros: in physical address order
+ */
+
+#define AIPI_IO_ADDRESS(x) \
+ (((x) - AIPI_BASE_ADDR) + AIPI_BASE_ADDR_VIRT)
+
+#define AVIC_IO_ADDRESS(x) AIPI_IO_ADDRESS(x)
+
+#define SAHB1_IO_ADDRESS(x) \
+ (((x) - SAHB1_BASE_ADDR) + SAHB1_BASE_ADDR_VIRT)
+
+#define CS1_IO_ADDRESS(x) \
+ (((x) - CS1_BASE_ADDR) + CS1_BASE_ADDR_VIRT)
+
+#define X_MEMC_IO_ADDRESS(x) \
+ (((x) - X_MEMC_BASE_ADDR) + X_MEMC_BASE_ADDR_VIRT)
+
+#define PCMCIA_IO_ADDRESS(x) \
+ (((x) - X_MEMC_BASE_ADDR) + X_MEMC_BASE_ADDR_VIRT)
+
+#define IS_MEM_DEVICE_NONSHARED(x) 0
+
+/*
+ * MX21 ADS Interrupt numbers
+ */
+#define MXC_INT_CSPI3 6
+#define MXC_INT_GPIO 8
+#define MXC_INT_FIRI 9
+#define MXC_INT_SDHC2 10
+#define MXC_INT_SDHC1 11
+#define MXC_INT_I2C 12
+#define MXC_INT_SSI2 13
+#define MXC_INT_SSI1 14
+#define MXC_INT_CSPI2 15
+#define MXC_INT_CSPI1 16
+#define MXC_INT_UART4 17
+#define MXC_INT_UART3 18
+#define MXC_INT_UART2 19
+#define MXC_INT_UART1 20
+#define MXC_INT_KPP 21
+#define MXC_INT_RTC 22
+#define MXC_INT_PWM 23
+#define MXC_INT_GPT3 24
+#define MXC_INT_GPT2 25
+#define MXC_INT_GPT1 26
+#define MXC_INT_GPT INT_GPT1
+#define MXC_INT_WDOG 27
+#define MXC_INT_PCMCIA 28
+#define MXC_INT_NANDFC 29
+#define MXC_INT_BMI 30
+#define MXC_INT_CSI 31
+#define MXC_INT_DMACH0 32
+#define MXC_INT_DMACH1 33
+#define MXC_INT_DMACH2 34
+#define MXC_INT_DMACH3 35
+#define MXC_INT_DMACH4 36
+#define MXC_INT_DMACH5 37
+#define MXC_INT_DMACH6 38
+#define MXC_INT_DMACH7 39
+#define MXC_INT_DMACH8 40
+#define MXC_INT_DMACH9 41
+#define MXC_INT_DMACH10 42
+#define MXC_INT_DMACH11 43
+#define MXC_INT_DMACH12 44
+#define MXC_INT_DMACH13 45
+#define MXC_INT_DMACH14 46
+#define MXC_INT_DMACH15 47
+#define MXC_INT_EMMAENC 49
+#define MXC_INT_EMMADEC 50
+#define MXC_INT_EMMAPRP 51
+#define MXC_INT_EMMAPP 52
+#define MXC_INT_USBWKUP 53
+#define MXC_INT_USBDMA 54
+#define MXC_INT_USBHOST 55
+#define MXC_INT_USBFUNC 56
+#define MXC_INT_USBHNP 57
+#define MXC_INT_USBCTRL 58
+#define MXC_INT_SAHARA 59
+#define MXC_INT_SLCDC 60
+#define MXC_INT_LCDC 61
+
+#define MXC_MAX_INT_LINES 64
+#define MXC_MAX_EXT_LINES 0
+
+#define MXC_MUX_GPIO_INTERRUPTS 1
+#define MXC_GPIO_BASE (MXC_MAX_INT_LINES)
+
+/* 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
+
+/*!
+ * Number of GPIO port as defined in the IC Spec
+ */
+#define GPIO_PORT_NUM 6
+/*!
+ * Number of GPIO pins per port
+ */
+#define GPIO_NUM_PIN 32
+
+#define DMA_REQ_CSI_RX 31
+#define DMA_REQ_CSI_STAT 30
+#define DMA_REQ_BMI_RX 29
+#define DMA_REQ_BMI_TX 28
+#define DMA_REQ_UART1_TX 27
+#define DMA_REQ_UART1_RX 26
+#define DMA_REQ_UART2_TX 25
+#define DMA_REQ_UART2_RX 24
+#define DMA_REQ_UART3_TX 23
+#define DMA_REQ_UART3_RX 22
+#define DMA_REQ_UART4_TX 21
+#define DMA_REQ_UART4_RX 20
+#define DMA_REQ_CSPI1_TX 19
+#define DMA_REQ_CSPI1_RX 18
+#define DMA_REQ_CSPI2_TX 17
+#define DMA_REQ_CSPI2_RX 16
+#define DMA_REQ_SSI1_TX1 15
+#define DMA_REQ_SSI1_RX1 14
+#define DMA_REQ_SSI1_TX0 13
+#define DMA_REQ_SSI1_RX0 12
+#define DMA_REQ_SSI2_TX1 11
+#define DMA_REQ_SSI2_RX1 10
+#define DMA_REQ_SSI2_TX0 9
+#define DMA_REQ_SSI2_RX0 8
+#define DMA_REQ_SDHC1 7
+#define DMA_REQ_SDHC2 6
+#define DMA_FIRI_TX 5
+#define DMA_FIRI_RX 4
+#define DMA_EX 3
+#define DMA_REQ_CSPI3_TX 2
+#define DMA_REQ_CSPI3_RX 1
+
+#define MXC_TIMER_GPT1 1
+#define MXC_TIMER_GPT2 2
+#define MXC_TIMER_GPT3 3
+
+/*!
+ * NFMS bit in FMCR register for pagesize of nandflash
+ */
+#define NFMS (*((volatile u32 *)IO_ADDRESS(CCM_BASE_ADDR+0x814)))
+
+#define NFMS_BIT 5
+
+#endif /* __ASM_ARCH_MXC_MX21_H__ */
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 000000000000..ed06ae12d58c
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mx25.h
@@ -0,0 +1,510 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file arch-mxc/mx25.h
+ * @brief This file contains register definitions.
+ *
+ * @ingroup MSL_MX25
+ */
+
+#ifndef __ASM_ARCH_MXC_MX25_H__
+#define __ASM_ARCH_MXC_MX25_H__
+
+#ifndef __ASM_ARCH_MXC_HARDWARE_H__
+#error "Do not include directly."
+#endif
+
+/*!
+ * defines the OS clock tick rate
+ */
+#define CLOCK_TICK_RATE 13300000
+
+/*!
+ * Register an interrupt handler for the SMN as well as the SCC. In some
+ * implementations, the SMN is not connected at all, and in others, it is
+ * on the same interrupt line as the SCM. Comment this line out accordingly
+ */
+#define USE_SMN_INTERRUPT
+
+/*!
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+#define MXC_UART_NR 5
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive Irda data.
+ */
+#define MXC_UART_IR_RXDMUX 0x0004
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive UART data.
+ */
+#define MXC_UART_RXDMUX 0x0004
+
+/*!
+ * This option is used to set or clear the dspdma bit in the SDMA config
+ * register.
+ */
+#define MXC_SDMA_DSPDMA 0
+
+/*!
+ * Define this option to specify we are using the newer SDMA module.
+ */
+#define MXC_SDMA_V2
+
+/*
+ * MX25 memory map:
+ *
+ * Virt Phys Size What
+ * ---------------------------------------------------------------------------
+ * FC000000 43F00000 1M AIPS 1
+ * FC100000 50000000 1M SPBA
+ * FC200000 53F00000 1M AIPS 2
+ * FC300000 60000000 1M ROMPATCH (128M)
+ * FC400000 68000000 1M ASIC (128M)
+ * FC500000 78000000 128K FBC RAM (IRAM)
+ * 80000000 256M SDRAM0
+ * 90000000 256M SDRAM1
+ * A0000000 128M CS0 Flash
+ * A8000000 128M CS1 Flash
+ * B0000000 32M CS2 SRAM
+ * B2000000 32M CS3
+ * B4000000 32M CS4
+ * B6000000 32M CS5
+ * FC520000 B8000000 64K SDRAM, WEIM, M3IF, EMI controllers
+ * FC530000 BB000000 8K NFC
+ */
+
+/*
+ * IRAM
+ */
+#define IRAM_BASE_ADDR 0x78000000 /* internal ram */
+#define IRAM_BASE_ADDR_VIRT 0xFC500000
+#define IRAM_SIZE SZ_128K
+
+#ifndef CONFIG_SDMA_IRAM
+#define CONFIG_SDMA_IRAM_SIZE 0
+#endif
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+#define SND_RAM_SIZE 0x10000
+#else
+#define SND_RAM_SIZE 0
+#endif
+
+#define SND_RAM_BASE_ADDR (IRAM_BASE_ADDR + CONFIG_SDMA_IRAM_SIZE)
+
+#define USB_IRAM_BASE_ADDR (SND_RAM_BASE_ADDR + SND_RAM_SIZE)
+#ifdef CONFIG_USB_STATIC_IRAM_PPH
+#define USB_IRAM_SIZE (2*SZ_8K)
+#else
+#define USB_IRAM_SIZE 0
+#endif
+
+/*
+ * AIPS 1
+ */
+#define AIPS1_BASE_ADDR 0x43F00000
+#define AIPS1_BASE_ADDR_VIRT 0xFC000000
+#define AIPS1_SIZE SZ_1M
+
+#define MAX_BASE_ADDR (AIPS1_BASE_ADDR + 0x00004000)
+#define CLKCTL_BASE_ADDR (AIPS1_BASE_ADDR + 0x00008000)
+#define ETB_SLOT4_BASE_ADDR (AIPS1_BASE_ADDR + 0x0000C000)
+#define ETB_SLOT5_BASE_ADDR (AIPS1_BASE_ADDR + 0x00010000)
+#define AAPE_BASE_ADDR (AIPS1_BASE_ADDR + 0x00014000)
+#define I2C_BASE_ADDR (AIPS1_BASE_ADDR + 0x00080000)
+#define I2C3_BASE_ADDR (AIPS1_BASE_ADDR + 0x00084000)
+#define CAN1_BASE_ADDR (AIPS1_BASE_ADDR + 0x00088000)
+#define CAN3_BASE_ADDR (AIPS1_BASE_ADDR + 0x0008C000)
+#define UART1_BASE_ADDR (AIPS1_BASE_ADDR + 0x00090000)
+#define UART2_BASE_ADDR (AIPS1_BASE_ADDR + 0x00094000)
+#define I2C2_BASE_ADDR (AIPS1_BASE_ADDR + 0x00098000)
+#define OWIRE_BASE_ADDR (AIPS1_BASE_ADDR + 0x0009C000)
+#define ATA_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A0000)
+#define CSPI1_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A4000)
+#define KPP_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A8000)
+#define IOMUXC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000AC000)
+#define AUDMUX_BASE_ADDR (AIPS1_BASE_ADDR + 0x000B0000)
+#define ECT_A_BASE_ADDR (AIPS1_BASE_ADDR + 0x000B8000)
+#define ECT_B_BASE_ADDR (AIPS1_BASE_ADDR + 0x000BC000)
+
+/*
+ * SPBA global module enabled #0
+ */
+#define SPBA0_BASE_ADDR 0x50000000
+#define SPBA0_BASE_ADDR_VIRT 0xFC100000
+#define SPBA0_SIZE SZ_1M
+
+#define CSPI3_BASE_ADDR (SPBA0_BASE_ADDR + 0x00004000)
+#define UART4_BASE_ADDR (SPBA0_BASE_ADDR + 0x00008000)
+#define UART3_BASE_ADDR (SPBA0_BASE_ADDR + 0x0000C000)
+#define CSPI2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00010000)
+#define SSI2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00014000)
+#define ESAI_BASE_ADDR (SPBA0_BASE_ADDR + 0x00018000)
+#define ATA_DMA_BASE_ADDR (SPBA0_BASE_ADDR + 0x00020000)
+#define SIM1_BASE_ADDR (SPBA0_BASE_ADDR + 0x00024000)
+#define SIM2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00028000)
+#define UART5_BASE_ADDR (SPBA0_BASE_ADDR + 0x0002C000)
+#define TSC_BASE_ADDR (SPBA0_BASE_ADDR + 0x00030000)
+#define SSI1_BASE_ADDR (SPBA0_BASE_ADDR + 0x00034000)
+#define FEC_BASE_ADDR (SPBA0_BASE_ADDR + 0x00038000)
+#define SPBA_CTRL_BASE_ADDR (SPBA0_BASE_ADDR + 0x0003C000)
+
+/*!
+ * defines for SPBA modules
+ */
+#define SPBA_CSPI3 (0x1 << 2)
+#define SPBA_UART4 (0x2 << 2)
+#define SPBA_UART3 (0x3 << 2)
+#define SPBA_CSPI2 (0x4 << 2)
+#define SPBA_SSI2 (0x5 << 2)
+#define SPBA_ESAI (0x6 << 2)
+#define SPBA_ATA (0x8 << 2)
+#define SPBA_SIM1 (0x9 << 2)
+#define SPBA_SIM2 (0xA << 2)
+#define SPBA_UART5 (0xB << 2)
+#define SPBA_ANALOG (0xC << 2)
+#define SPBA_SSI1 (0xD << 2)
+#define SPBA_FEC (0xE << 2)
+
+/*!
+ * Defines for modules using static and dynamic DMA channels
+ */
+#define MXC_DMA_CHANNEL_IRAM 30
+#define MXC_DMA_CHANNEL_UART1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART3_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART3_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART4_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART4_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART5_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART5_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MMC1 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI1_RX MXC_DMA_DYNAMIC_CHANNEL
+#ifdef CONFIG_SDMA_IRAM
+#define MXC_DMA_CHANNEL_SSI1_TX (MXC_DMA_CHANNEL_IRAM + 1)
+#else
+#define MXC_DMA_CHANNEL_SSI1_TX MXC_DMA_DYNAMIC_CHANNEL
+#endif
+#define MXC_DMA_CHANNEL_SSI2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI3_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI3_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ATA_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ATA_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MEMORY MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ESAI_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ESAI_TX MXC_DMA_DYNAMIC_CHANNEL
+
+/*
+ * AIPS 2
+ */
+#define AIPS2_BASE_ADDR 0x53F00000
+#define AIPS2_BASE_ADDR_VIRT 0xFC200000
+#define AIPS2_SIZE SZ_1M
+
+#define CCM_BASE_ADDR (AIPS2_BASE_ADDR + 0x00080000)
+#define GPT4_BASE_ADDR (AIPS2_BASE_ADDR + 0x00084000)
+#define GPT3_BASE_ADDR (AIPS2_BASE_ADDR + 0x00088000)
+#define GPT2_BASE_ADDR (AIPS2_BASE_ADDR + 0x0008C000)
+#define GPT1_BASE_ADDR (AIPS2_BASE_ADDR + 0x00090000)
+#define EPIT1_BASE_ADDR (AIPS2_BASE_ADDR + 0x00094000)
+#define EPIT2_BASE_ADDR (AIPS2_BASE_ADDR + 0x00098000)
+#define GPIO4_BASE_ADDR (AIPS2_BASE_ADDR + 0x0009C000)
+#define PWM2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000A0000)
+#define GPIO3_BASE_ADDR (AIPS2_BASE_ADDR + 0x000AD000)
+#define PWM3_BASE_ADDR (AIPS2_BASE_ADDR + 0x000A8000)
+#define SCC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000AC000)
+#define RNGB_BASE_ADDR (AIPS2_BASE_ADDR + 0x000B0000)
+#define MMC_SDHC1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000B4000)
+#define MMC_SDHC2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000B8000)
+#define LCDC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000BC000)
+#define SLCDC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C0000)
+#define PWM4_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C8000)
+#define GPIO1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000CC000)
+#define GPIO2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D0000)
+#define SDMA_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D4000)
+#define WDOG1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DC000)
+#define PWM1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000E0000)
+#define RTIC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000EC000)
+#define IIM_BASE_ADDR (AIPS2_BASE_ADDR + 0x000F0000)
+#define USBOTG_BASE_ADDR (AIPS2_BASE_ADDR + 0x000F4000)
+#define OTG_BASE_ADDR USBOTG_BASE_ADDR
+#define CSI_BASE_ADDR (AIPS2_BASE_ADDR + 0x000F8000)
+#define DRYICE_BASE_ADDR (AIPS2_BASE_ADDR + 0x000FC000)
+#define SRTC_BASE_ADDR (DRYICE_BASE_ADDR)
+
+/*
+ * ROMP and ASIC
+ */
+#define ROMP_BASE_ADDR 0x60000000
+#define ROMP_BASE_ADDR_VIRT 0xFC300000
+#define ROMP_SIZE SZ_1M
+
+#define ASIC_BASE_ADDR 0x68000000
+#define ASIC_BASE_ADDR_VIRT 0xFC400000
+#define ASIC_SIZE SZ_1M
+#define AVIC_BASE_ADDR ASIC_BASE_ADDR
+#define AVIC_BASE_ADDR_VIRT ASIC_BASE_ADDR_VIRT
+#define AVIC_SIZE ASIC_SIZE
+
+/*
+ * SDRAM, WEIM, M3IF, EMI controllers
+ */
+#define X_MEMC_BASE_ADDR 0xB8000000
+#define X_MEMC_BASE_ADDR_VIRT 0xFC520000
+#define X_MEMC_SIZE SZ_64K
+
+#define SDRAMC_BASE_ADDR (X_MEMC_BASE_ADDR + 0x1000)
+#define WEIM_BASE_ADDR (X_MEMC_BASE_ADDR + 0x2000)
+#define M3IF_BASE_ADDR (X_MEMC_BASE_ADDR + 0x3000)
+#define EMI_CTL_BASE_ADDR (X_MEMC_BASE_ADDR + 0x4000)
+
+/*
+ * NFC controller
+ */
+#define NFC_BASE_ADDR 0xBB000000
+#define NFC_BASE_ADDR_VIRT 0xFC530000
+#define NFC_SIZE SZ_8K
+
+/*
+ * Memory regions and CS
+ */
+#define CSD0_BASE_ADDR 0x80000000
+#define CSD1_BASE_ADDR 0x90000000
+
+#define SDRAM_BASE_ADDR CSD0_BASE_ADDR
+
+#define CS0_BASE_ADDR 0xA0000000
+#define CS1_BASE_ADDR 0xA8000000
+#define CS2_BASE_ADDR 0xB0000000
+#define CS3_BASE_ADDR 0xB2000000
+#define CS4_BASE_ADDR 0xB4000000
+#define CS4_SIZE SZ_32M
+#define CS5_BASE_ADDR 0xB6000000
+#define CS5_SIZE SZ_32M
+
+/*!
+ * 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 IO_ADDRESS(x) \
+ (((x >= AIPS1_BASE_ADDR) && (x < (AIPS1_BASE_ADDR + AIPS1_SIZE))) ? AIPS1_IO_ADDRESS(x):\
+ ((x >= SPBA0_BASE_ADDR) && (x < (SPBA0_BASE_ADDR + SPBA0_SIZE))) ? SPBA0_IO_ADDRESS(x):\
+ ((x >= AIPS2_BASE_ADDR) && (x < (AIPS2_BASE_ADDR + AIPS2_SIZE))) ? AIPS2_IO_ADDRESS(x):\
+ ((x >= ROMP_BASE_ADDR) && (x < (ROMP_BASE_ADDR + ROMP_SIZE))) ? ROMP_IO_ADDRESS(x):\
+ ((x >= ASIC_BASE_ADDR) && (x < (ASIC_BASE_ADDR + AVIC_SIZE))) ? ASIC_IO_ADDRESS(x):\
+ ((x >= IRAM_BASE_ADDR) && (x < (IRAM_BASE_ADDR + IRAM_SIZE))) ? IRAM_IO_ADDRESS(x):\
+ ((x >= X_MEMC_BASE_ADDR) && (x < (X_MEMC_BASE_ADDR + X_MEMC_SIZE))) ? X_MEMC_IO_ADDRESS(x):\
+ ((x >= NFC_BASE_ADDR) && (x < (NFC_BASE_ADDR + NFC_SIZE))) ? NFC_IO_ADDRESS(x):\
+ 0xDEADBEEF)
+
+/*
+ * define the address mapping macros: in physical address order
+ */
+
+#define AIPS1_IO_ADDRESS(x) \
+ (((x) - AIPS1_BASE_ADDR) + AIPS1_BASE_ADDR_VIRT)
+
+#define SPBA0_IO_ADDRESS(x) \
+ (((x) - SPBA0_BASE_ADDR) + SPBA0_BASE_ADDR_VIRT)
+
+#define AIPS2_IO_ADDRESS(x) \
+ (((x) - AIPS2_BASE_ADDR) + AIPS2_BASE_ADDR_VIRT)
+
+#define ROMP_IO_ADDRESS(x) \
+ (((x) - ROMP_BASE_ADDR) + ROMP_BASE_ADDR_VIRT)
+
+#define ASIC_IO_ADDRESS(x) \
+ (((x) - ASIC_BASE_ADDR) + ASIC_BASE_ADDR_VIRT)
+
+/* for entry-macro.S */
+#define AVIC_IO_ADDRESS(x) ASIC_IO_ADDRESS(x)
+
+#define IRAM_IO_ADDRESS(x) \
+ (((x) - IRAM_BASE_ADDR) + IRAM_BASE_ADDR_VIRT)
+
+#define X_MEMC_IO_ADDRESS(x) \
+ (((x) - X_MEMC_BASE_ADDR) + X_MEMC_BASE_ADDR_VIRT)
+
+#define NFC_IO_ADDRESS(x) \
+ (((x) - NFC_BASE_ADDR) + NFC_BASE_ADDR_VIRT)
+
+#define IS_MEM_DEVICE_NONSHARED(x) 0
+
+/*
+ * DMA request assignments
+ */
+#define DMA_REQ_EXTREQ0 0
+#define DMA_REQ_CCM 1
+#define DMA_REQ_ATA_TX_END 2
+#define DMA_REQ_ATA_TX 3
+#define DMA_REQ_ATA_RX 4
+#define DMA_REQ_CSPI2_RX 6
+#define DMA_REQ_CSPI2_TX 7
+#define DMA_REQ_CSPI1_RX 8
+#define DMA_REQ_CSPI1_TX 9
+#define DMA_REQ_UART3_RX 10
+#define DMA_REQ_UART3_TX 11
+#define DMA_REQ_UART4_RX 12
+#define DMA_REQ_UART4_TX 13
+#define DMA_REQ_EXTREQ1 14
+#define DMA_REQ_EXTREQ2 15
+#define DMA_REQ_UART2_RX 16
+#define DMA_REQ_UART2_TX 17
+#define DMA_REQ_UART1_RX 18
+#define DMA_REQ_UART1_TX 19
+#define DMA_REQ_SSI2_RX1 22
+#define DMA_REQ_SSI2_TX1 23
+#define DMA_REQ_SSI2_RX0 24
+#define DMA_REQ_SSI2_TX0 25
+#define DMA_REQ_SSI1_RX1 26
+#define DMA_REQ_SSI1_TX1 27
+#define DMA_REQ_SSI1_RX0 28
+#define DMA_REQ_SSI1_TX0 29
+#define DMA_REQ_NFC 30
+#define DMA_REQ_ECT 31
+#define DMA_REQ_ESAI_RX 32
+#define DMA_REQ_ESAI_TX 33
+#define DMA_REQ_CSPI3_RX 34
+#define DMA_REQ_CSPI3_TX 35
+#define DMA_REQ_SIM2_RX 36
+#define DMA_REQ_SIM2_TX 37
+#define DMA_REQ_SIM1_RX 38
+#define DMA_REQ_SIM1_TX 39
+#define DMA_REQ_TSC_GCQ 44
+#define DMA_REQ_TSC_TCQ 45
+#define DMA_REQ_UART5_RX 46
+#define DMA_REQ_UART5_TX 47
+
+/*
+ * Interrupt numbers
+ */
+#define MXC_INT_CSPI3 0
+#define MXC_INT_GPT4 1
+#define MXC_INT_OWIRE 2
+#define MXC_INT_I2C 3
+#define MXC_INT_I2C2 4
+#define MXC_INT_UART4 5
+#define MXC_INT_RTIC 6
+#define MXC_INT_ESAI 7
+#define MXC_INT_SDHC2 8
+#define MXC_INT_SDHC1 9
+#define MXC_INT_I2C3 10
+#define MXC_INT_SSI2 11
+#define MXC_INT_SSI1 12
+#define MXC_INT_CSPI2 13
+#define MXC_INT_CSPI1 14
+#define MXC_INT_ATA 15
+#define MXC_INT_GPIO3 16
+#define MXC_INT_CSI 17
+#define MXC_INT_UART3 18
+#define MXC_INT_IIM 19
+#define MXC_INT_SIM1 20
+#define MXC_INT_SIM2 21
+#define MXC_INT_RNG 22
+#define MXC_INT_GPIO4 23
+#define MXC_INT_KPP 24
+#define MXC_INT_DRYICE_NORM 25
+#define MXC_INT_PWM 26
+#define MXC_INT_EPIT2 27
+#define MXC_INT_EPIT1 28
+#define MXC_INT_GPT3 29
+#define MXC_INT_POWER_FAIL 30
+#define MXC_INT_CRM 31
+#define MXC_INT_UART2 32
+#define MXC_INT_NANDFC 33
+#define MXC_INT_SDMA 34
+#define MXC_INT_USB_HTG 35
+#define MXC_INT_PWM2 36
+#define MXC_INT_USB_OTG 37
+#define MXC_INT_SLCDC 38
+#define MXC_INT_LCDC 39
+#define MXC_INT_UART5 40
+#define MXC_INT_PWM3 41
+#define MXC_INT_PWM4 42
+#define MXC_INT_CAN1 43
+#define MXC_INT_CAN2 44
+#define MXC_INT_UART1 45
+#define MXC_INT_TSC 46
+#define MXC_INT_ECT 48
+#define MXC_INT_SCC_SCM 49
+#define MXC_INT_SCC_SMN 50
+#define MXC_INT_GPIO2 51
+#define MXC_INT_GPIO1 52
+#define MXC_INT_GPT2 53
+#define MXC_INT_GPT1 54
+#define MXC_INT_WDOG 55
+#define MXC_INT_DRYICE_SEC 56
+#define MXC_INT_FEC 57
+#define MXC_INT_EXT_INT5 58
+#define MXC_INT_EXT_INT4 59
+#define MXC_INT_EXT_INT3 60
+#define MXC_INT_EXT_INT2 61
+#define MXC_INT_EXT_INT1 62
+#define MXC_INT_EXT_INT0 63
+
+#define MXC_INT_GPT MXC_INT_GPT1
+
+#define MXC_MAX_INT_LINES 64
+#define MXC_MAX_EXT_LINES 6
+
+#define MXC_GPIO_INT_BASE (MXC_MAX_INT_LINES)
+
+/* 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
+
+/*!
+ * Number of GPIO port as defined in the IC Spec
+ */
+#define GPIO_PORT_NUM 4
+/*!
+ * Number of GPIO pins per port
+ */
+#define GPIO_NUM_PIN 32
+
+#define MXC_TIMER_GPT1 1
+#define MXC_TIMER_GPT2 2
+#define MXC_TIMER_GPT3 3
+#define MXC_TIMER_GPT4 4
+
+/*!
+ * NFMS bit in RCSR register for pagesize of nandflash
+ */
+#define NFMS (*((volatile u32 *)IO_ADDRESS(CCM_BASE_ADDR + 0x28)))
+#define NFMS_NF_DWIDTH 14
+#define NFMS_NF_PG_SZ 8
+
+#endif /* __ASM_ARCH_MXC_MX25_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx27.h b/arch/arm/plat-mxc/include/mach/mx27.h
index a86db64744a1..8c439eb7e7ee 100644
--- a/arch/arm/plat-mxc/include/mach/mx27.h
+++ b/arch/arm/plat-mxc/include/mach/mx27.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or
@@ -24,6 +24,35 @@
#error "Do not include directly."
#endif
+/*!
+ * defines the OS clock tick rate
+ */
+#define CLOCK_TICK_RATE 13300000
+
+/*!
+ * Register an interrupt handler for the SMN as well as the SCC. In some
+ * implementations, the SMN is not connected at all, and in others, it is
+ * on the same interrupt line as the SCM. Comment this line out accordingly
+ */
+#define USE_SMN_INTERRUPT
+
+/*!
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+#define MXC_UART_NR 6
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive Irda data.
+ */
+#define MXC_UART_IR_RXDMUX 0x0004
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive UART data.
+ */
+#define MXC_UART_RXDMUX 0x0004
+
/* IRAM */
#define IRAM_BASE_ADDR 0xFFFF4C00 /* internal ram */
@@ -33,7 +62,7 @@
#define AIPI_SIZE SZ_1M
#define DMA_BASE_ADDR (AIPI_BASE_ADDR + 0x01000)
-#define WDOG_BASE_ADDR (AIPI_BASE_ADDR + 0x02000)
+#define WDOG1_BASE_ADDR (AIPI_BASE_ADDR + 0x02000)
#define GPT1_BASE_ADDR (AIPI_BASE_ADDR + 0x03000)
#define GPT2_BASE_ADDR (AIPI_BASE_ADDR + 0x04000)
#define GPT3_BASE_ADDR (AIPI_BASE_ADDR + 0x05000)
@@ -118,6 +147,8 @@
#define CS2_BASE_ADDR 0xD0000000
#define CS3_BASE_ADDR 0xD2000000
#define CS4_BASE_ADDR 0xD4000000
+#define CS4_BASE_ADDR_VIRT 0xF4300000
+#define CS4_SIZE SZ_1M
#define CS5_BASE_ADDR 0xD6000000
#define PCMCIA_MEM_BASE_ADDR 0xDC000000
@@ -154,6 +185,11 @@
#define PCMCIA_IO_ADDRESS(x) \
(((x) - X_MEMC_BASE_ADDR) + X_MEMC_BASE_ADDR_VIRT)
+#define IS_MEM_DEVICE_NONSHARED(x) 0
+
+/*
+ * MX27 ADS Interrupt numbers
+ */
/* fixed interrput numbers */
#define MXC_INT_CCM 63
#define MXC_INT_IIM 62
@@ -195,7 +231,7 @@
#define MXC_INT_GPT1 26
#define MXC_INT_GPT2 25
#define MXC_INT_GPT3 24
-#define MXC_INT_GPT INT_GPT1
+#define MXC_INT_GPT MXC_INT_GPT1
#define MXC_INT_PWM 23
#define MXC_INT_RTC 22
#define MXC_INT_KPP 21
@@ -220,6 +256,35 @@
#define MXC_INT_GPT6 2
#define MXC_INT_I2C2 1
+#define MXC_MAX_INT_LINES 64
+#define MXC_MAX_EXT_LINES 0
+
+#define MXC_MUX_GPIO_INTERRUPTS 1
+#define MXC_GPIO_INT_BASE (MXC_MAX_INT_LINES)
+
+/* 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
+
+/*!
+ * Number of GPIO port as defined in the IC Spec
+ */
+#define GPIO_PORT_NUM 6
+/*!
+ * Number of GPIO pins per port
+ */
+#define GPIO_NUM_PIN 32
+
/* fixed DMA request numbers */
#define DMA_REQ_NFC 37
#define DMA_REQ_SDHC3 36
@@ -258,44 +323,19 @@
#define DMA_REQ_CSPI3_TX 2
#define DMA_REQ_CSPI3_RX 1
-/* silicon revisions specific to i.MX27 */
-#define CHIP_REV_1_0 0x00
-#define CHIP_REV_2_0 0x01
+#define MXC_TIMER_GPT1 1
+#define MXC_TIMER_GPT2 2
+#define MXC_TIMER_GPT3 3
+#define MXC_TIMER_GPT4 4
+#define MXC_TIMER_GPT5 5
+#define MXC_TIMER_GPT6 6
-#ifndef __ASSEMBLY__
-extern int mx27_revision(void);
-#endif
-
-/* 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
-
-/* Mandatory defines used globally */
-
-/* this is an i.MX27 CPU */
-#define cpu_is_mx27() (1)
-
-/* this CPU supports up to 192 GPIOs (don't forget the baseboard!) */
-#define ARCH_NR_GPIOS (192 + 16)
-
-/* OS clock tick rate */
-#define CLOCK_TICK_RATE 13300000
-
-/* Start of RAM */
-#define PHYS_OFFSET SDRAM_BASE_ADDR
+/*!
+ * NFMS bit in FMCR register for pagesize of nandflash
+ */
+#define NFMS (*((volatile u32 *)IO_ADDRESS(SYSCTRL_BASE_ADDR+0x14)))
-/* max interrupt lines count */
-#define NR_IRQS 256
+#define NFMS_BIT 5
/* count of internal interrupt sources */
#define MXC_MAX_INT_LINES 64
diff --git a/arch/arm/plat-mxc/include/mach/mx2_dma.h b/arch/arm/plat-mxc/include/mach/mx2_dma.h
new file mode 100644
index 000000000000..8f5ae658f96b
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mx2_dma.h
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MX2_H__
+#define __ASM_ARCH_MXC_MX2_H__
+
+/*!
+ * @defgroup DMA_MX27 DMA driver for i.MX27
+ */
+
+/*!
+ *@file arch-mxc/mx2_dma.h
+ *@brief DMA driver header file
+ *
+ * @ingroup DMA_MX27
+ *
+ */
+
+#include <mach/hardware.h>
+#include <asm/dma.h>
+#include <stdarg.h>
+
+#define MXC_DMA_INTR_0 32
+
+#define DMA_DCR 0x000 /* 32bit dma control reg */
+#define DMA_DISR 0x004 /* 32bit dma interrupt status reg */
+#define DMA_DIMR 0x008 /* 32bit dma interrupt mask reg */
+#define DMA_DBTOSR 0x00c /* 32bit dma burst timeout stat reg */
+#define DMA_DRTOSR 0x010 /* 32bit dma req timeout status reg */
+#define DMA_DSESR 0x014 /* 32bit dma transfer err status reg */
+#define DMA_DBOSR 0x018 /* 32bit dma buffer overflow stat reg */
+#define DMA_DBTOCR 0x01c /* 32bit dma burst timeout ctrl reg */
+
+#define DMA_WSRA 0x040 /* 32bit dma W-size A reg */
+#define DMA_XSRA 0x044 /* 32bit dma X-size A reg */
+#define DMA_YSRA 0x048 /* 32bit dma Y-size A reg */
+#define DMA_WSRB 0x04C /* 32bit dma W-size B reg */
+#define DMA_XSRB 0x050 /* 32bit dma X-size B reg */
+#define DMA_YSRB 0x054 /* 32bit dma Y-size B reg */
+
+#define DMA_CH_BASE(x) (0x080+0x040*(x))
+
+#define DMA_SAR(x) (DMA_CH_BASE(x)+0x000)
+#define DMA_DAR(x) (DMA_CH_BASE(x)+0x004)
+#define DMA_CNTR(x) (DMA_CH_BASE(x)+0x008)
+#define DMA_CCR(x) (DMA_CH_BASE(x)+0x00C) /* 32bit dma ch0 control reg */
+#define DMA_RSSR(x) (DMA_CH_BASE(x)+0x010) /* 32bit dma ch0 req source sel reg */
+#define DMA_BLR(x) (DMA_CH_BASE(x)+0x014) /* 32bit dma ch0 burst lenght reg */
+#define DMA_RTOR(x) (DMA_CH_BASE(x)+0x018) /* 32bit dma ch0 req time out reg */
+#define DMA_BUCR(x) (DMA_CH_BASE(x)+0x018) /* 32bit dma ch0 bus utilization reg */
+#define DMA_CCNR(x) (DMA_CH_BASE(x)+0x01C) /* 32bit dma ch0 */
+
+#define DMA_TCR 0x480 /*32bit dma test control reg */
+#define DMA_TFIFOA 0x484 /* 32bit dma test fifo A reg */
+#define DMA_TDRR 0x488 /* 32bit dma test request reg */
+#define DMA_TDIPR 0x48c /* 32bit dma test in progress reg */
+#define DMA_TFIFOB 0x490 /* 32bit dma test fifo B reg */
+
+/*!
+ * This defines maximum DMA address
+ */
+#define MAX_DMA_ADDRESS 0xffffffff
+
+#define MXC_DMA_CHANNELS 16
+#define MAX_DMA_CHANNELS MXC_DMA_CHANNELS
+
+#define MX_DMA_CHANNELS MXC_DMA_CHANNELS
+
+/*!@def DMA_MEM_SIZE_8 DMA access port size, 8 bit*/
+/*!@def DMA_MEM_SIZE_16 DMA access port size, 16 bit*/
+/*!@def DMA_MEM_SIZE_32 DMA access port size, 32 bit*/
+#define DMA_MEM_SIZE_8 0x1
+#define DMA_MEM_SIZE_16 0x2
+#define DMA_MEM_SIZE_32 0x0
+
+/*!@def DMA_TYPE_LINEAR DMA transfer type, linear*/
+/*!@def DMA_TYPE_2D DMA transfer type, 2D*/
+/*!@def DMA_TYPE_FIFO DMA transfer type, FIFO*/
+/*!@def DMA_TYPE_EBE DMA transfer type, end-of-burst enable FIFO*/
+#define DMA_TYPE_LINEAR 0x0
+#define DMA_TYPE_2D 0x01
+#define DMA_TYPE_FIFO 0x2
+#define DMA_TYPE_EBE 0x3
+
+/*!@def DMA_DONE DMA transfer done*/
+/*!@def DMA_BURST_TIMEOUT DMA transfer timeout error*/
+/*!@def DMA_REQUEST_TIMEOUT DMA transfer request timeout error*/
+/*!@def DMA_TRANSFER_ERROR DMA transfer error*/
+/*!@def DMA_BUFFER_OVERFLOW DMA transfer buffer overflow error*/
+#define DMA_DONE 0x1000
+#define DMA_BURST_TIMEOUT 0x1
+#define DMA_REQUEST_TIMEOUT 0x2
+#define DMA_TRANSFER_ERROR 0x4
+#define DMA_BUFFER_OVERFLOW 0x8
+
+/*!@brief DMA control register*/
+typedef struct {
+ volatile u32 CEN:1; /*!< Dma channel enable */
+ volatile u32 FRC:1; /*!< Force a dma cycle bit */
+ volatile u32 RPT:1; /*!< Repeat bit */
+ volatile u32 REN:1; /*!< Request enable bit */
+ volatile u32 SSIZ:2; /*!< Source port size, 2 bit in length */
+ volatile u32 DSIZ:2; /*!< Dest port size, 2 bit in length */
+ volatile u32 MSEL:1; /*!< 2D memory register set bit */
+ volatile u32 MDIR:1; /*!< Transfer direction, inversed or normal */
+ volatile u32 SMOD:2; /*!< Source mode, 2 bit in length */
+ volatile u32 DMOD:2; /*!< Dest mode, 2 bit in length */
+ volatile u32 ACRPT:1; /*!< Auto clear repeat bit */
+ volatile u32 Reserver:17; /*!< Reserved bits */
+
+} dma_regs_control;
+
+#define DMA_CTL_CEN 0x1
+#define DMA_CTL_FRC 0x2
+#define DMA_CTL_RPT 0x4
+#define DMA_CTL_REN 0x8
+
+#define DMA_CTL_MSEL 0x100
+#define DMA_CTL_MDIR 0x200
+#define DMA_CTL_ACRPT 0x4000
+
+#define DMA_CTL_GET_SSIZ(x) (((x)>>4)&0x3)
+#define DMA_CTL_GET_DSIZ(x) (((x)>>6)&0x3)
+#define DMA_CTL_GET_SMOD(x) (((x)>>10)&0x3)
+#define DMA_CTL_GET_DMOD(x) (((x)>>12)&0x3)
+
+#define DMA_CTL_SET_SSIZ(x,value) do{ \
+ (x)&=~(0x3<<4); \
+ (x)|=(value)<<4; \
+ }while(0)
+
+#define DMA_CTL_SET_DSIZ(x,value) do{ \
+ (x)&=~(0x3<<6); \
+ (x)|=(value)<<6; \
+ }while(0)
+
+#define DMA_CTL_SET_SMOD(x,value) do{ \
+ (x)&=~(0x3<<10); \
+ (x)|=(value)<<10; \
+ }while(0)
+
+#define DMA_CTL_SET_DMOD(x,value) do{ \
+ (x)&=~(0x3<<12); \
+ (x)|=(value)<<12; \
+ }while(0)
+
+typedef struct {
+ volatile u32 SourceAddr;
+ volatile u32 DestAddr;
+ volatile u32 Count;
+ volatile u32 Ctl;
+ volatile u32 RequestSource;
+ volatile u32 BurstLength;
+ union {
+ volatile u32 ReqTimeout;
+ volatile u32 BusUtilt;
+ };
+ volatile u32 transferd;
+} dma_regs_t;
+
+#ifndef TRANSFER_32BIT
+/*!
+ * This defines DMA access data size
+ */
+
+#define TRANSFER_8BIT DMA_MEM_SIZE_8
+#define TRANSFER_16BIT DMA_MEM_SIZE_16
+#define TRANSFER_32BIT DMA_MEM_SIZE_32
+
+#endif
+
+/*!
+ * This defines maximum device name length passed during mxc_request_dma().
+ */
+#define MAX_DEVNAME_LENGTH 32
+#define MAX_BD_SIZE 32
+
+/*!
+ * Structure containing dma channel parameters.
+ */
+typedef struct {
+ unsigned long dma_chan; /*!< the dma channel information: dynamic or channel number */
+ u32 mode:1; /*!< the initialized dma mode, 0 for dma read, 1 for dma write */
+ u32 rto_en:1; /*!< enable request-timeout. It is valid when REN=1 */
+ u32 dir:1; /*!< Transfer direction, 0 for increment, 1 for decrement */
+ u32 dma_chaining:1; /*!< Autoclear bit for chainbuffer */
+ u32 ren:1; /*!< enable transfer based request signal */
+ u32 M2D_Valid:1; /*!< enable 2D address module. 0 for disable it. 1 for enabled it */
+ u32 msel:1; /*!<2D memory selection, 0 for set A, 1 for set B */
+ u32 burstLength; /*!< Channel burst length */
+ u32 request; /*!< Request source. */
+ u32 busuntils; /*!< when REN=0, Bus utilization, otherwise it it request timeout */
+ u32 sourceType; /*!< Source type, see DMA_TYPE_* */
+ u32 sourcePort; /*!< Source port size, see DMA_MEM_SIZE_* */
+ u32 destType; /*!< Destination type, see DMA_TYPE_* */
+ u32 destPort; /*!< Destination port size, see DMA_MEM_SIZE_* */
+ __u32 per_address; /*< Peripheral source/destination
+ * physical address
+ */
+ u32 W; /*!< 2D Wide-size */
+ u32 X; /*!< 2D X-size */
+ u32 Y; /*!< 2D Y-size */
+} mx2_dma_info_t;
+
+/*!
+ * Structure of dma buffer descriptor
+ */
+typedef struct {
+ unsigned long state; /*!< dma bd state */
+ int mode; /*!< the dma mode of this bd */
+ unsigned long count; /*!< the length of the dma transfer */
+ unsigned long src_addr; /*!< the source address of the dma transfer */
+ unsigned long dst_addr; /*!< the destination address of the dma transfer */
+} mx2_dma_bd_t;
+
+/*!
+ * the states of dma buffer descriptor
+ */
+#define DMA_BD_ST_BUSY 0x20000000 /*!< dma bd is transfering or has be configured into controller */
+#define DMA_BD_ST_PEND 0x10000000 /*!< dma bd is waiting to be configured into controller */
+#define DMA_BD_ST_LAST 0x08000000 /*!< dma bd is the last dma bd which is built in one dma transfer request
+ * When completed this bd, the callback function must be called.
+ */
+
+/*!
+ * This structure containing the private information for MX2
+ */
+typedef struct mx2_dma_priv_s {
+ unsigned int dma_chaining:1; /* 1: using headware dma chaining feature */
+ unsigned int ren:1; /* 1: dma start besed on request signal */
+ unsigned long trans_bytes; /* To store the transfered data bytes in this transfer */
+ mx2_dma_info_t *dma_info; /* To store the pointer for dma parameter for reading and wirting */
+ int bd_rd; /* the read index of bd ring */
+ int bd_wr; /* the write index of bd ring */
+ atomic_t bd_used; /* the valid bd number in bd ring */
+ mx2_dma_bd_t *bd_ring; /* the pointer of bd ring */
+ unsigned long dma_base; /* register base address of this channel */
+ int dma_irq; /* irq number of this channel */
+} mx2_dma_priv_t;
+
+/*!
+ * @brief get the dma info by channel_id
+ */
+extern mx2_dma_info_t *mxc_dma_get_info(mxc_dma_device_t channel_id);
+
+/*!
+ * @brief: scan dma parameter list . And collect information about which channels are dynamic .
+ */
+extern void mxc_dma_load_info(mxc_dma_channel_t * dma);
+
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/mx31.h b/arch/arm/plat-mxc/include/mach/mx31.h
index 0536f8917bc0..9de43ceed9c4 100644
--- a/arch/arm/plat-mxc/include/mach/mx31.h
+++ b/arch/arm/plat-mxc/include/mach/mx31.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -20,6 +20,36 @@
*/
#define CLOCK_TICK_RATE 16625000
+/*!
+ * Register an interrupt handler for the SMN as well as the SCC. In some
+ * implementations, the SMN is not connected at all, and in others, it is
+ * on the same interrupt line as the SCM. Comment this line out accordingly
+ */
+#define USE_SMN_INTERRUPT
+
+/*
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+#define MXC_UART_NR 5
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive Irda data.
+ */
+#define MXC_UART_IR_RXDMUX 0x0004
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive UART data.
+ */
+#define MXC_UART_RXDMUX 0x0004
+
+/*!
+ * This option is used to set or clear the dspdma bit in the SDMA config
+ * register.
+ */
+#define MXC_SDMA_DSPDMA 0
+
/*
* MX31 memory map:
*
@@ -44,32 +74,24 @@
* FC320000 B8000000 64K NAND, SDRAM, WEIM, M3IF, EMI controllers
* C0000000 64M PCMCIA/CF
*/
-
-#define CS0_BASE_ADDR 0xA0000000
-#define CS1_BASE_ADDR 0xA8000000
-#define CS2_BASE_ADDR 0xB0000000
-#define CS3_BASE_ADDR 0xB2000000
-
-#define CS4_BASE_ADDR 0xB4000000
-#define CS4_BASE_ADDR_VIRT 0xF4000000
-#define CS4_SIZE SZ_32M
-
-#define CS5_BASE_ADDR 0xB6000000
-#define PCMCIA_MEM_BASE_ADDR 0xBC000000
-
/*
* IRAM
*/
#define IRAM_BASE_ADDR 0x1FFC0000 /* internal ram */
-#define IRAM_BASE_ADDR_VIRT 0xF8000000
+#define IRAM_BASE_ADDR_VIRT 0xF80C0000
#define IRAM_SIZE SZ_16K
+#define USB_IRAM_BASE_ADDR (IRAM_BASE_ADDR)
+#ifdef CONFIG_USB_STATIC_IRAM
+#define USB_IRAM_SIZE (2*SZ_8K)
+#else
+#define USB_IRAM_SIZE 0
+#endif
+
/*
* L2CC
*/
#define L2CC_BASE_ADDR 0x30000000
-#define L2CC_BASE_ADDR_VIRT 0xF9000000
-#define L2CC_SIZE SZ_1M
/*
* AIPS 1
@@ -120,6 +142,59 @@
#define MSHC2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00024000)
#define SPBA_CTRL_BASE_ADDR (SPBA0_BASE_ADDR + 0x0003C000)
+/*!
+ * defines for SPBA modules
+ */
+#define SPBA_SDHC1 0x04
+#define SPBA_SDHC2 0x08
+#define SPBA_UART3 0x0C
+#define SPBA_CSPI2 0x10
+#define SPBA_SSI2 0x14
+#define SPBA_SIM 0x18
+#define SPBA_IIM 0x1C
+#define SPBA_ATA 0x20
+
+/*!
+ * Defines for modules using static and dynamic DMA channels
+ */
+
+#ifdef CONFIG_SDMA_IRAM
+#define MXC_DMA_CHANNEL_IRAM 30
+#endif /*CONFIG_SDMA_IRAM */
+
+#define MXC_DMA_CHANNEL_UART1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART3_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART3_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART4_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART4_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART5_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART5_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MMC1 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MMC2 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI2_RX MXC_DMA_DYNAMIC_CHANNEL
+
+#ifdef CONFIG_SDMA_IRAM
+#define MXC_DMA_CHANNEL_SSI2_TX (MXC_DMA_CHANNEL_IRAM + 1)
+#else /*CONFIG_SDMA_IRAM */
+#define MXC_DMA_CHANNEL_SSI2_TX MXC_DMA_DYNAMIC_CHANNEL
+#endif /*CONFIG_SDMA_IRAM */
+
+#define MXC_DMA_CHANNEL_FIR_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_FIR_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ATA_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ATA_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MEMORY MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_FIFO_MEMORY MXC_DMA_DYNAMIC_CHANNEL
+
/*
* AIPS 2
*/
@@ -140,11 +215,12 @@
#define IPU_CTRL_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C0000)
#define AUDMUX_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C4000)
#define MPEG4_ENC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C8000)
+#define VPU_BASE_ADDR MPEG4_ENC_BASE_ADDR
#define GPIO1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000CC000)
#define GPIO2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D0000)
#define SDMA_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D4000)
#define RTC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D8000)
-#define WDOG_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DC000)
+#define WDOG1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DC000)
#define PWM_BASE_ADDR (AIPS2_BASE_ADDR + 0x000E0000)
#define RTIC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000EC000)
@@ -191,6 +267,11 @@
#define CS5_BASE_ADDR 0xB6000000
#define PCMCIA_MEM_BASE_ADDR 0xBC000000
+/*
+ * VL2CC for i.MX32
+ */
+#define VL2CC_BASE_ADDR 0xE0000000
+
/*!
* 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
@@ -198,9 +279,7 @@
* it returns 0xDEADBEEF
*/
#define IO_ADDRESS(x) \
- (void __iomem *) \
(((x >= IRAM_BASE_ADDR) && (x < (IRAM_BASE_ADDR + IRAM_SIZE))) ? IRAM_IO_ADDRESS(x):\
- ((x >= L2CC_BASE_ADDR) && (x < (L2CC_BASE_ADDR + L2CC_SIZE))) ? L2CC_IO_ADDRESS(x):\
((x >= AIPS1_BASE_ADDR) && (x < (AIPS1_BASE_ADDR + AIPS1_SIZE))) ? AIPS1_IO_ADDRESS(x):\
((x >= SPBA0_BASE_ADDR) && (x < (SPBA0_BASE_ADDR + SPBA0_SIZE))) ? SPBA0_IO_ADDRESS(x):\
((x >= AIPS2_BASE_ADDR) && (x < (AIPS2_BASE_ADDR + AIPS2_SIZE))) ? AIPS2_IO_ADDRESS(x):\
@@ -217,9 +296,6 @@
#define IRAM_IO_ADDRESS(x) \
(((x) - IRAM_BASE_ADDR) + IRAM_BASE_ADDR_VIRT)
-#define L2CC_IO_ADDRESS(x) \
- (((x) - L2CC_BASE_ADDR) + L2CC_BASE_ADDR_VIRT)
-
#define AIPS1_IO_ADDRESS(x) \
(((x) - AIPS1_BASE_ADDR) + AIPS1_BASE_ADDR_VIRT)
@@ -244,8 +320,47 @@
#define PCMCIA_IO_ADDRESS(x) \
(((x) - X_MEMC_BASE_ADDR) + X_MEMC_BASE_ADDR_VIRT)
-/* Start of physical RAM - On many MX31 platforms, this is the first SDRAM bank (CSD0) */
-#define PHYS_OFFSET CSD0_BASE_ADDR
+/*
+ * DMA request assignments
+ */
+#define DMA_REQ_ECT 31
+#define DMA_REQ_NFC 30
+#define DMA_REQ_SSI1_TX1 29
+#define DMA_REQ_SSI1_RX1 28
+#define DMA_REQ_SSI1_TX2 27
+#define DMA_REQ_SSI1_RX2 26
+#define DMA_REQ_SSI2_TX1 25
+#define DMA_REQ_SSI2_RX1 24
+#define DMA_REQ_SSI2_TX2 23
+#define DMA_REQ_SSI2_RX2 22
+#define DMA_REQ_SDHC2 21
+#define DMA_REQ_SDHC1 20
+#define DMA_REQ_UART1_TX 19
+#define DMA_REQ_UART1_RX 18
+#define DMA_REQ_FIRI_TX 17
+#define DMA_REQ_FIRI_RX 16
+#define DMA_REQ_UART2_TX 17
+#define DMA_REQ_UART2_RX 16
+#define DMA_REQ_EXTREQ1 15
+#define DMA_REQ_EXTREQ2 14
+#define DMA_REQ_UART4_TX 13
+#define DMA_REQ_UART4_RX 12
+#define DMA_REQ_CSPI3_TX 11
+#define DMA_REQ_CSPI3_RX 10
+#define DMA_REQ_UART5_TX 11
+#define DMA_REQ_UART5_RX 10
+#define DMA_REQ_CSPI1_TX 9
+#define DMA_REQ_CSPI1_RX 8
+#define DMA_REQ_UART3_TX 9
+#define DMA_REQ_UART3_RX 8
+#define DMA_REQ_CSPI2_TX 7
+#define DMA_REQ_CSPI2_RX 6
+#define DMA_REQ_SIM 5
+#define DMA_REQ_ATA_RX 4
+#define DMA_REQ_ATA_TX 3
+#define DMA_REQ_ATA_TX_END 2
+#define DMA_REQ_CCM 1
+#define DMA_REQ_EXTREQ0 0
/*
* Interrupt numbers
@@ -255,7 +370,7 @@
#define MXC_INT_CS8900A 2
#define MXC_INT_I2C3 3
#define MXC_INT_I2C2 4
-#define MXC_INT_MPEG4_ENCODER 5
+#define MXC_INT_MPEG4_ENC 5
#define MXC_INT_RTIC 6
#define MXC_INT_FIRI 7
#define MXC_INT_MMC_SDHC2 8
@@ -267,12 +382,13 @@
#define MXC_INT_CSPI1 14
#define MXC_INT_ATA 15
#define MXC_INT_MBX 16
+#define MXC_INT_VPU MXC_INT_MBX
#define MXC_INT_CSPI3 17
#define MXC_INT_UART3 18
#define MXC_INT_IIM 19
#define MXC_INT_SIM2 20
#define MXC_INT_SIM1 21
-#define MXC_INT_RNGA 22
+#define MXC_INT_RNG 22
#define MXC_INT_EVTMON 23
#define MXC_INT_KPP 24
#define MXC_INT_RTC 25
@@ -281,7 +397,7 @@
#define MXC_INT_EPIT1 28
#define MXC_INT_GPT 29
#define MXC_INT_RESV30 30
-#define MXC_INT_RESV31 31
+#define MXC_INT_DVFS 31
#define MXC_INT_UART2 32
#define MXC_INT_NANDFC 33
#define MXC_INT_SDMA 34
@@ -318,10 +434,28 @@
#define MXC_MAX_INT_LINES 64
#define MXC_GPIO_INT_BASE MXC_MAX_INT_LINES
-#define MXC_MAX_GPIO_LINES (GPIO_NUM_PIN * GPIO_PORT_NUM)
-#define MXC_MAX_VIRTUAL_INTS 16
-#define NR_IRQS (MXC_MAX_INT_LINES + MXC_MAX_GPIO_LINES + MXC_MAX_VIRTUAL_INTS)
+/*!
+ * Interrupt Number for ARM11 PMU
+ */
+#define ARM11_PMU_IRQ MXC_INT_EVTMON
+
+/* 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
+
+/* this CPU supports up to 96 GPIOs */
+#define ARCH_NR_GPIOS 96
/*!
* Number of GPIO port as defined in the IC Spec
@@ -332,54 +466,12 @@
*/
#define GPIO_NUM_PIN 32
-#define PROD_SIGNATURE 0x1 /* For MX31 */
-
-/* silicon revisions specific to i.MX31 */
-#define CHIP_REV_1_0 0x10
-#define CHIP_REV_1_1 0x11
-#define CHIP_REV_1_2 0x12
-#define CHIP_REV_1_3 0x13
-#define CHIP_REV_2_0 0x20
-#define CHIP_REV_2_1 0x21
-#define CHIP_REV_2_2 0x22
-#define CHIP_REV_2_3 0x23
-#define CHIP_REV_3_0 0x30
-#define CHIP_REV_3_1 0x31
-#define CHIP_REV_3_2 0x32
-
-#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)
-
-/* this is a i.MX31 CPU */
-#define cpu_is_mx31() (1)
-
-extern unsigned int system_rev;
-
-static inline int mx31_revision(void)
-{
- return system_rev;
-}
-#endif
+/*!
+ * NFMS bit in RCSR register for pagesize of nandflash
+ */
+#define NFMS (*((volatile u32 *)IO_ADDRESS(CCM_BASE_ADDR+0xc)))
+#define NFMS_BIT 30
+#define NFMS_NF_DWIDTH 31
+#define NFMS_NF_PG_SZ 30
#endif /* __ASM_ARCH_MXC_MX31_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx35.h b/arch/arm/plat-mxc/include/mach/mx35.h
new file mode 100644
index 000000000000..b3c5ed07aefa
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mx35.h
@@ -0,0 +1,480 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MX35_H__
+#define __ASM_ARCH_MXC_MX35_H__
+
+#ifndef __ASM_ARCH_MXC_HARDWARE_H__
+#error "Do not include directly."
+#endif
+
+/*!
+ * @file arch-mxc/mx35.h
+ * @brief This file contains register definitions.
+ *
+ * @ingroup MSL_MX35
+ */
+/*!
+ * defines the OS clock tick rate
+ */
+#define CLOCK_TICK_RATE 16625000
+
+/*!
+ * Register an interrupt handler for the SMN as well as the SCC. In some
+ * implementations, the SMN is not connected at all, and in others, it is
+ * on the same interrupt line as the SCM. Comment this line out accordingly
+ */
+#define USE_SMN_INTERRUPT
+
+/*
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+#define MXC_UART_NR 3
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive Irda data.
+ */
+#define MXC_UART_IR_RXDMUX 0x0004
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive UART data.
+ */
+#define MXC_UART_RXDMUX 0x0004
+
+/*!
+ * This option is used to set or clear the dspdma bit in the SDMA config
+ * register.
+ */
+#define MXC_SDMA_DSPDMA 0
+
+/*!
+ * Define this option to specify we are using the newer SDMA module.
+ */
+#define MXC_SDMA_V2
+
+/*
+ * IRAM
+ */
+#define IRAM_BASE_ADDR 0x10000000 /* internal ram */
+#define IRAM_BASE_ADDR_VIRT 0xF8400000
+#define IRAM_SIZE SZ_128K
+
+#ifndef CONFIG_SDMA_IRAM
+#define CONFIG_SDMA_IRAM_SIZE 0
+#endif
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+#define SND_RAM_SIZE 0x10000
+#else
+#define SND_RAM_SIZE 0
+#endif
+
+#define SND_RAM_BASE_ADDR (IRAM_BASE_ADDR + CONFIG_SDMA_IRAM_SIZE)
+#define MLB_IRAM_ADDR_OFFSET CONFIG_SDMA_IRAM_SIZE + SND_RAM_SIZE
+
+/*
+ * L2CC
+ */
+#define L2CC_BASE_ADDR 0x30000000
+
+/*
+ * AIPS 1
+ */
+#define AIPS1_BASE_ADDR 0x43F00000
+#define AIPS1_BASE_ADDR_VIRT 0xF8500000
+#define AIPS1_SIZE SZ_1M
+
+#define MAX_BASE_ADDR (AIPS1_BASE_ADDR + 0x00004000)
+#define EVTMON_BASE_ADDR (AIPS1_BASE_ADDR + 0x00008000)
+#define CLKCTL_BASE_ADDR (AIPS1_BASE_ADDR + 0x0000C000)
+#define ETB_SLOT4_BASE_ADDR (AIPS1_BASE_ADDR + 0x00010000)
+#define ETB_SLOT5_BASE_ADDR (AIPS1_BASE_ADDR + 0x00014000)
+#define ECT_CTIO_BASE_ADDR (AIPS1_BASE_ADDR + 0x00018000)
+#define I2C_BASE_ADDR (AIPS1_BASE_ADDR + 0x00080000)
+#define I2C3_BASE_ADDR (AIPS1_BASE_ADDR + 0x00084000)
+#define UART1_BASE_ADDR (AIPS1_BASE_ADDR + 0x00090000)
+#define UART2_BASE_ADDR (AIPS1_BASE_ADDR + 0x00094000)
+#define I2C2_BASE_ADDR (AIPS1_BASE_ADDR + 0x00098000)
+#define OWIRE_BASE_ADDR (AIPS1_BASE_ADDR + 0x0009C000)
+#define SSI1_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A0000)
+#define CSPI1_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A4000)
+#define KPP_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A8000)
+#define IOMUXC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000AC000)
+#define ECT_IP1_BASE_ADDR (AIPS1_BASE_ADDR + 0x000B8000)
+#define ECT_IP2_BASE_ADDR (AIPS1_BASE_ADDR + 0x000BC000)
+
+/*
+ * SPBA global module enabled #0
+ */
+#define SPBA0_BASE_ADDR 0x50000000
+#define SPBA0_BASE_ADDR_VIRT 0xF8600000
+#define SPBA0_SIZE SZ_1M
+
+#define UART3_BASE_ADDR (SPBA0_BASE_ADDR + 0x0000C000)
+#define CSPI2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00010000)
+#define SSI2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00014000)
+#define ATA_BASE_ADDR (SPBA0_BASE_ADDR + 0x00020000)
+#define ATA_DMA_BASE_ADDR (SPBA0_BASE_ADDR + 0x00020000)
+#define MSHC1_BASE_ADDR (SPBA0_BASE_ADDR + 0x00024000)
+#define SPDIF_BASE_ADDR (SPBA0_BASE_ADDR + 0x00028000)
+#define ASRC_BASE_ADDR (SPBA0_BASE_ADDR + 0x0002C000)
+#define ESAI_BASE_ADDR (SPBA0_BASE_ADDR + 0x00034000)
+#define FEC_BASE_ADDR (SPBA0_BASE_ADDR + 0x00038000)
+#define SPBA_CTRL_BASE_ADDR (SPBA0_BASE_ADDR + 0x0003C000)
+
+/*!
+ * defines for SPBA modules
+ */
+#define SPBA_UART3 0x0C
+#define SPBA_CSPI2 0x10
+#define SPBA_SSI2 0x14
+#define SPBA_ATA 0x20
+#define SPBA_MSHC 0x24
+#define SPBA_SPDIR 0x28
+#define SPBA_ASRC 0x2C
+#define SPBA_ESAI 0x34
+#define SPBA_FEC 0x38
+
+/*!
+ * Defines for modules using static and dynamic DMA channels
+ */
+#define MXC_DMA_CHANNEL_IRAM 30
+#define MXC_DMA_CHANNEL_UART1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART3_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART3_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MMC1 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MMC2 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MMC3 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI1_RX MXC_DMA_DYNAMIC_CHANNEL
+#ifdef CONFIG_SDMA_IRAM
+#define MXC_DMA_CHANNEL_SSI1_TX (MXC_DMA_CHANNEL_IRAM + 1)
+#else
+#define MXC_DMA_CHANNEL_SSI1_TX MXC_DMA_DYNAMIC_CHANNEL
+#endif
+#define MXC_DMA_CHANNEL_SSI2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ATA_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ATA_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MEMORY MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SPDIF_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SPDIF_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCA_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCA_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCB_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCB_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCC_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCC_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ESAI_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ESAI_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCA_ESAI MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCB_ESAI MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCC_ESAI MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCA_SSI1_TX0 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCA_SSI1_TX1 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCA_SSI2_TX0 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCA_SSI2_TX1 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCB_SSI1_TX0 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCB_SSI1_TX1 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCB_SSI2_TX0 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ASRCB_SSI2_TX1 MXC_DMA_DYNAMIC_CHANNEL
+
+/*
+ * AIPS 2
+ */
+#define AIPS2_BASE_ADDR 0x53F00000
+#define AIPS2_BASE_ADDR_VIRT 0xF8700000
+#define AIPS2_SIZE SZ_1M
+#define CCM_BASE_ADDR (AIPS2_BASE_ADDR + 0x00080000)
+#define GPT1_BASE_ADDR (AIPS2_BASE_ADDR + 0x00090000)
+#define EPIT1_BASE_ADDR (AIPS2_BASE_ADDR + 0x00094000)
+#define EPIT2_BASE_ADDR (AIPS2_BASE_ADDR + 0x00098000)
+#define GPIO3_BASE_ADDR (AIPS2_BASE_ADDR + 0x000A4000)
+#define SCC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000AC000)
+#define RNGC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000B0000)
+#define MMC_SDHC1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000B4000)
+#define MMC_SDHC2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000B8000)
+#define MMC_SDHC3_BASE_ADDR (AIPS2_BASE_ADDR + 0x000BC000)
+#define IPU_CTRL_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C0000)
+#define AUDMUX_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C4000)
+#define GPIO1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000CC000)
+#define GPIO2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D0000)
+#define SDMA_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D4000)
+#define RTC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D8000)
+#define WDOG1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DC000)
+#define PWM_BASE_ADDR (AIPS2_BASE_ADDR + 0x000E0000)
+#define CAN1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000E4000)
+#define CAN2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000E8000)
+#define RTIC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000EC000)
+#define IIM_BASE_ADDR (AIPS2_BASE_ADDR + 0x000F0000)
+#define OTG_BASE_ADDR (AIPS2_BASE_ADDR + 0x000F4000)
+#define MLB_BASE_ADDR (AIPS2_BASE_ADDR + 0x000F8000)
+
+/*
+ * ROMP and AVIC
+ */
+#define ROMP_BASE_ADDR 0x60000000
+#define ROMP_BASE_ADDR_VIRT 0xF8800000
+#define ROMP_SIZE SZ_1M
+
+#define AVIC_BASE_ADDR 0x68000000
+#define AVIC_BASE_ADDR_VIRT 0xF8900000
+#define AVIC_SIZE SZ_1M
+
+/*
+ * SDRAM, WEIM, M3IF, EMI controllers
+ */
+#define X_MEMC_BASE_ADDR 0xB8000000
+#define X_MEMC_BASE_ADDR_VIRT 0xF8A00000
+#define X_MEMC_SIZE SZ_1M
+
+#define ESDCTL_BASE_ADDR (X_MEMC_BASE_ADDR + 0x1000)
+#define WEIM_BASE_ADDR (X_MEMC_BASE_ADDR + 0x2000)
+#define M3IF_BASE_ADDR (X_MEMC_BASE_ADDR + 0x3000)
+#define EMI_CTL_BASE_ADDR (X_MEMC_BASE_ADDR + 0x4000)
+
+/*
+ * NFC controller
+ */
+#define NFC_BASE_ADDR 0xBB000000
+#define NFC_BASE_ADDR_VIRT 0xF8B00000
+#define NFC_SIZE SZ_1M
+
+/*
+ * Memory regions and CS
+ */
+/* MX35 ADS SDRAM is from 0x80000000, 64M */
+#define IPU_MEM_BASE_ADDR 0x70000000
+#define CSD0_BASE_ADDR 0x80000000
+#define CSD1_BASE_ADDR 0x90000000
+
+#define SDRAM_BASE_ADDR CSD0_BASE_ADDR
+
+#define CS0_BASE_ADDR 0xA0000000
+#define CS1_BASE_ADDR 0xA8000000
+#define CS2_BASE_ADDR 0xB0000000
+#define CS3_BASE_ADDR 0xB2000000
+#define CS4_BASE_ADDR 0xB4000000
+#define CS5_BASE_ADDR 0xB6000000
+
+/*!
+ * 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 IO_ADDRESS(x) \
+ (((x >= IRAM_BASE_ADDR) && (x < (IRAM_BASE_ADDR + IRAM_SIZE))) ? IRAM_IO_ADDRESS(x):\
+ ((x >= AIPS1_BASE_ADDR) && (x < (AIPS1_BASE_ADDR + AIPS1_SIZE))) ? AIPS1_IO_ADDRESS(x):\
+ ((x >= SPBA0_BASE_ADDR) && (x < (SPBA0_BASE_ADDR + SPBA0_SIZE))) ? SPBA0_IO_ADDRESS(x):\
+ ((x >= AIPS2_BASE_ADDR) && (x < (AIPS2_BASE_ADDR + AIPS2_SIZE))) ? AIPS2_IO_ADDRESS(x):\
+ ((x >= ROMP_BASE_ADDR) && (x < (ROMP_BASE_ADDR + ROMP_SIZE))) ? ROMP_IO_ADDRESS(x):\
+ ((x >= AVIC_BASE_ADDR) && (x < (AVIC_BASE_ADDR + AVIC_SIZE))) ? AVIC_IO_ADDRESS(x):\
+ ((x >= NFC_BASE_ADDR) && (x < (NFC_BASE_ADDR + NFC_SIZE))) ? NFC_IO_ADDRESS(x):\
+ ((x >= X_MEMC_BASE_ADDR) && (x < (X_MEMC_BASE_ADDR + X_MEMC_SIZE))) ? X_MEMC_IO_ADDRESS(x):\
+ 0xDEADBEEF)
+
+/*
+ * define the address mapping macros: in physical address order
+ */
+
+#define IRAM_IO_ADDRESS(x) \
+ (((x) - IRAM_BASE_ADDR) + IRAM_BASE_ADDR_VIRT)
+
+#define AIPS1_IO_ADDRESS(x) \
+ (((x) - AIPS1_BASE_ADDR) + AIPS1_BASE_ADDR_VIRT)
+
+#define SPBA0_IO_ADDRESS(x) \
+ (((x) - SPBA0_BASE_ADDR) + SPBA0_BASE_ADDR_VIRT)
+
+#define AIPS2_IO_ADDRESS(x) \
+ (((x) - AIPS2_BASE_ADDR) + AIPS2_BASE_ADDR_VIRT)
+
+#define ROMP_IO_ADDRESS(x) \
+ (((x) - ROMP_BASE_ADDR) + ROMP_BASE_ADDR_VIRT)
+
+#define AVIC_IO_ADDRESS(x) \
+ (((x) - AVIC_BASE_ADDR) + AVIC_BASE_ADDR_VIRT)
+
+#define X_MEMC_IO_ADDRESS(x) \
+ (((x) - X_MEMC_BASE_ADDR) + X_MEMC_BASE_ADDR_VIRT)
+
+#define NFC_IO_ADDRESS(x) \
+ (((x) - NFC_BASE_ADDR) + NFC_BASE_ADDR_VIRT)
+
+/*
+ * DMA request assignments
+ */
+#define DMA_REQ_ASRC_DMA6 41
+#define DMA_REQ_ASRC_DMA5 40
+#define DMA_REQ_ASRC_DMA4 39
+#define DMA_REQ_ASRC_DMA3 38
+#define DMA_REQ_ASRC_DMA2 37
+#define DMA_REQ_ASRC_DMA1 36
+#define DMA_REQ_RSVD3 35
+#define DMA_REQ_RSVD2 34
+#define DMA_REQ_ESAI_TX 33
+#define DMA_REQ_ESAI_RX 32
+#define DMA_REQ_ECT 31
+#define DMA_REQ_NFC 30
+#define DMA_REQ_SSI1_TX1 29
+#define DMA_REQ_SSI1_RX1 28
+#define DMA_REQ_SSI1_TX2 27
+#define DMA_REQ_SSI1_RX2 26
+#define DMA_REQ_SSI2_TX1 25
+#define DMA_REQ_SSI2_RX1 24
+#define DMA_REQ_SSI2_TX2 23
+#define DMA_REQ_SSI2_RX2 22
+#define DMA_REQ_IPU 21
+#define DMA_REQ_RSVD1 20
+#define DMA_REQ_UART1_TX 19
+#define DMA_REQ_UART1_RX 18
+#define DMA_REQ_UART2_TX 17
+#define DMA_REQ_UART2_RX 16
+#define DMA_REQ_EXTREQ1 15
+#define DMA_REQ_EXTREQ2 14
+#define DMA_REQ_SPDIF_TX 13
+#define DMA_REQ_SPDIF_RX 12
+#define DMA_REQ_UART3_TX 11
+#define DMA_REQ_UART3_RX 10
+#define DMA_REQ_CSPI1_TX 9
+#define DMA_REQ_CSPI1_RX 8
+#define DMA_REQ_CSPI2_TX 7
+#define DMA_REQ_CSPI2_RX 6
+#define DMA_REQ_MSHC 5
+#define DMA_REQ_ATA_RX 4
+#define DMA_REQ_ATA_TX 3
+#define DMA_REQ_ATA_TX_END 2
+#define DMA_REQ_DPTC 1
+#define DMA_REQ_DVFS 1
+#define DMA_REQ_EXTREQ0 0
+
+/*
+ * Interrupt numbers
+ */
+#define MXC_INT_BASE 0
+#define MXC_INT_RESV0 0
+#define MXC_INT_RESV1 1
+#define MXC_INT_OWIRE 2
+#define MXC_INT_I2C3 3
+#define MXC_INT_I2C2 4
+#define MXC_INT_RESV2 5
+#define MXC_INT_RTIC 6
+#define MXC_INT_MMC_SDHC1 7
+#define MXC_INT_MMC_SDHC2 8
+#define MXC_INT_MMC_SDHC3 9
+#define MXC_INT_I2C 10
+#define MXC_INT_SSI1 11
+#define MXC_INT_SSI2 12
+#define MXC_INT_CSPI2 13
+#define MXC_INT_CSPI1 14
+#define MXC_INT_ATA 15
+#define MXC_INT_GPU2D 16
+#define MXC_INT_ASRC 17
+#define MXC_INT_UART3 18
+#define MXC_INT_IIM 19
+#define MXC_INT_RESV20 20
+#define MXC_INT_RESV21 21
+#define MXC_INT_RNG 22
+#define MXC_INT_EVTMON 23
+#define MXC_INT_KPP 24
+#define MXC_INT_RTC 25
+#define MXC_INT_PWM 26
+#define MXC_INT_EPIT2 27
+#define MXC_INT_EPIT1 28
+#define MXC_INT_GPT 29
+#define MXC_INT_POWERFAIL 30
+#define MXC_INT_CCM 31
+#define MXC_INT_UART2 32
+#define MXC_INT_NANDFC 33
+#define MXC_INT_SDMA 34
+#define MXC_INT_USB_HS 35
+#define MXC_INT_RESV36 36
+#define MXC_INT_USB_OTG 37
+#define MXC_INT_RESV38 38
+#define MXC_INT_MSHC1 39
+#define MXC_INT_ESAI 40
+#define MXC_INT_IPU_ERR 41
+#define MXC_INT_IPU_SYN 42
+#define MXC_INT_CAN1 43
+#define MXC_INT_CAN2 44
+#define MXC_INT_UART1 45
+#define MXC_INT_MLB 46
+#define MXC_INT_SPDIF 47
+#define MXC_INT_ECT 48
+#define MXC_INT_SCC_SCM 49
+#define MXC_INT_SCC_SMN 50
+#define MXC_INT_GPIO2 51
+#define MXC_INT_GPIO1 52
+#define MXC_INT_RESV53 53
+#define MXC_INT_RESV54 54
+#define MXC_INT_WDOG 55
+#define MXC_INT_GPIO3 56
+#define MXC_INT_FEC 57
+#define MXC_INT_EXT_POWER 58
+#define MXC_INT_EXT_TEMPER 59
+#define MXC_INT_EXT_SENSOR60 60
+#define MXC_INT_EXT_SENSOR61 61
+#define MXC_INT_EXT_WDOG 62
+#define MXC_INT_EXT_TV 63
+
+#define MXC_MAX_INT_LINES 64
+
+#define MXC_INT_FORCE MXC_INT_RESV0
+/*!
+ * Interrupt Number for ARM11 PMU
+ */
+#define ARM11_PMU_IRQ MXC_INT_EVTMON
+/* DVFS interrupt*/
+#define MXC_INT_DVFS MXC_INT_CCM
+
+#define MXC_GPIO_INT_BASE (MXC_MAX_INT_LINES)
+
+/* 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
+
+/*!
+ * Number of GPIO port as defined in the IC Spec
+ */
+#define GPIO_PORT_NUM 3
+/*!
+ * Number of GPIO pins per port
+ */
+#define GPIO_NUM_PIN 32
+
+/*!
+ * NFMS bit in RCSR register for pagesize of nandflash
+ */
+#define NFMS (*((volatile u32 *)IO_ADDRESS(CCM_BASE_ADDR+0x18)))
+#define NFMS_BIT 8
+#define NFMS_NF_DWIDTH 14
+#define NFMS_NF_PG_SZ 8
+
+#endif /* __ASM_ARCH_MXC_MX35_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx37.h b/arch/arm/plat-mxc/include/mach/mx37.h
new file mode 100644
index 000000000000..e72d605f3feb
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mx37.h
@@ -0,0 +1,525 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MX37_H__
+#define __ASM_ARCH_MXC_MX37_H__
+
+#ifndef __ASM_ARCH_MXC_HARDWARE_H__
+#error "Do not include directly."
+#endif
+
+/*!
+ * @file arch-mxc/mx37.h
+ * @brief This file contains register definitions.
+ *
+ * @ingroup MSL_MX37
+ */
+/*!
+ * defines the hardware clock tick rate
+ */
+
+#define CLOCK_TICK_RATE 8000000
+
+/*!
+ * Register an interrupt handler for the SMN as well as the SCC. In some
+ * implementations, the SMN is not connected at all, and in others, it is
+ * on the same interrupt line as the SCM. Comment this line out accordingly
+ */
+#define USE_SMN_INTERRUPT
+
+/*
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+#define MXC_UART_NR 3
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive Irda data.
+ */
+#define MXC_UART_IR_RXDMUX 0x0004
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive UART data.
+ */
+#define MXC_UART_RXDMUX 0x0004
+
+/*!
+ * This option is used to set or clear the dspdma bit in the SDMA config
+ * register.
+ */
+#define MXC_SDMA_DSPDMA 0
+
+/*!
+ * Define this option to specify we are using the newer SDMA module.
+ */
+#define MXC_SDMA_V2
+
+/*
+ * IRAM
+ */
+#define IRAM_BASE_ADDR 0x10000000 /* internal ram */
+#define IRAM_BASE_ADDR_VIRT 0xF8000000
+#define IRAM_SIZE (9*SZ_8K) /* 72KB */
+
+#if defined(CONFIG_MXC_SECURITY_SCC2) \
+ || defined(CONFIG_MXC_SECURITY_SCC2_MODULE)
+#define SCC_IRAM_SIZE SZ_16K
+#else
+#define SCC_IRAM_SIZE 0
+#endif
+
+/*#ifndef CONFIG_SDMA_IRAM
+#define CONFIG_SDMA_IRAM_SIZE 0
+#endif*/
+#ifdef CONFIG_SDMA_IRAM
+#define SDMA_IRAM_SIZE CONFIG_SDMA_IRAM_SIZE
+#else
+#define SDMA_IRAM_SIZE 0
+#endif
+
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+#define SND_RAM_SIZE 0x6000
+#else
+#define SND_RAM_SIZE 0
+#endif
+
+#ifdef CONFIG_USB_STATIC_IRAM
+#define USB_IRAM_SIZE SZ_8K
+#else
+#define USB_IRAM_SIZE 0
+#endif
+
+#if (IRAM_SIZE < (SCC_IRAM_SIZE + SDMA_IRAM_SIZE + SND_RAM_SIZE + \
+ USB_IRAM_SIZE))
+#error "IRAM size exceeded"
+#endif
+
+#ifdef CONFIG_MXC_VPU_IRAM
+#define VPU_IRAM_SIZE (IRAM_BASE_ADDR + IRAM_SIZE - VPU_IRAM_BASE_ADDR)
+#else
+#define VPU_IRAM_SIZE 0
+#endif
+
+#define SCC_IRAM_BASE_ADDR (IRAM_BASE_ADDR + IRAM_SIZE - SCC_IRAM_SIZE)
+#define SDMA_RAM_BASE_ADDR (IRAM_BASE_ADDR)
+#define SND_RAM_BASE_ADDR (IRAM_BASE_ADDR + SDMA_IRAM_SIZE)
+#define USB_IRAM_BASE_ADDR (SND_RAM_BASE_ADDR + SND_RAM_SIZE)
+#define VPU_IRAM_BASE_ADDR (USB_IRAM_BASE_ADDR + USB_IRAM_SIZE)
+
+/*
+ * NFC
+ */
+#define NFC_BASE_ADDR_AXI 0x7FFF0000 /* NAND flash AXI */
+#define NFC_BASE_ADDR_AXI_VIRT 0xF9000000
+#define NFC_AXI_SIZE SZ_64K
+
+/*
+ * L2CC
+ */
+#define L2CC_BASE_ADDR 0xB0000000
+
+#define PLATFORM_BASE_ADDR 0xB0400000
+#define PLATFORM_BASE_ADDR_VIRT 0xFA000000
+#define PLATFORM_SIZE SZ_1M
+#define EVTMON_BASE_ADDR (PLATFORM_BASE_ADDR + 0x00000000)
+#define ARM1176_BASE_ADDR (PLATFORM_BASE_ADDR + 0x00004000)
+
+#define TZIC_BASE_ADDR 0xB0800000
+#define TZIC_BASE_ADDR_VIRT 0xFA100000
+#define TZIC_SIZE SZ_1M
+
+#define DEBUG_BASE_ADDR 0xB0C00000
+#define DEBUG_BASE_ADDR_VIRT 0xFA200000
+#define DEBUG_SIZE SZ_1M
+#define ETB_BASE_ADDR (DEBUG_BASE_ADDR + 0x00001000)
+#define ETM_BASE_ADDR (DEBUG_BASE_ADDR + 0x00002000)
+#define TPIU_BASE_ADDR (DEBUG_BASE_ADDR + 0x00003000)
+#define CTI0_BASE_ADDR (DEBUG_BASE_ADDR + 0x00004000)
+#define CTI1_BASE_ADDR (DEBUG_BASE_ADDR + 0x00005000)
+#define CTI2_BASE_ADDR (DEBUG_BASE_ADDR + 0x00006000)
+
+/*
+ * AIPS 1
+ */
+#define AIPS1_BASE_ADDR 0xC3F00000
+#define AIPS1_BASE_ADDR_VIRT 0xFC000000
+#define AIPS1_SIZE SZ_1M
+
+#define MAX_BASE_ADDR (AIPS1_BASE_ADDR + 0x00080000)
+#define GPIO1_BASE_ADDR (AIPS1_BASE_ADDR + 0x00084000)
+#define GPIO2_BASE_ADDR (AIPS1_BASE_ADDR + 0x00088000)
+#define GPIO3_BASE_ADDR (AIPS1_BASE_ADDR + 0x0008C000)
+#define KPP_BASE_ADDR (AIPS1_BASE_ADDR + 0x00094000)
+#define WDOG1_BASE_ADDR (AIPS1_BASE_ADDR + 0x00098000)
+#define WDOG2_BASE_ADDR (AIPS1_BASE_ADDR + 0x0009C000)
+#define GPT1_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A0000)
+#define SRTC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A4000)
+#define IOMUXC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A8000)
+#define IIM_BASE_ADDR (AIPS1_BASE_ADDR + 0x000AC000)
+#define CSU_BASE_ADDR (AIPS1_BASE_ADDR + 0x000B0000)
+#define SDMA_BASE_ADDR (AIPS1_BASE_ADDR + 0x000B4000)
+#define SCC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000BC000)
+#define ROMCP_BASE_ADDR (AIPS1_BASE_ADDR + 0x000C0000)
+#define RTIC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000C4000)
+#define VPU_BASE_ADDR (AIPS1_BASE_ADDR + 0x000D0000)
+#define OTG_BASE_ADDR (AIPS1_BASE_ADDR + 0x000D4000)
+#define ATA_BASE_ADDR (AIPS1_BASE_ADDR + 0x000D8000)
+#define MSHC1_BASE_ADDR (AIPS1_BASE_ADDR + 0x000E0000)
+#define FEC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000E8000)
+#define RNGC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000EC000)
+#define TVE_BASE_ADDR (AIPS1_BASE_ADDR + 0x000F0000)
+#define ECT_CTIO_BASE_ADDR (AIPS1_BASE_ADDR + 0x00018000)
+#define CLKCTL_BASE_ADDR (AIPS1_BASE_ADDR + 0x0000C000)
+
+/*
+ * SPBA global module enabled #0
+ */
+#define SPBA0_BASE_ADDR 0xC0000000
+#define SPBA0_BASE_ADDR_VIRT 0xFC100000
+#define SPBA0_SIZE SZ_1M
+
+#define MMC_SDHC1_BASE_ADDR (SPBA0_BASE_ADDR + 0x00004000)
+#define MMC_SDHC2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00008000)
+#define UART3_BASE_ADDR (SPBA0_BASE_ADDR + 0x0000C000)
+#define CSPI2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00010000)
+#define SSI2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00014000)
+#define MMC_SDHC3_BASE_ADDR (SPBA0_BASE_ADDR + 0x00020000)
+#define SPDIF_BASE_ADDR (SPBA0_BASE_ADDR + 0x00028000)
+#define ATA_DMA_BASE_ADDR (SPBA0_BASE_ADDR + 0x00034000)
+#define SPBA_CTRL_BASE_ADDR (SPBA0_BASE_ADDR + 0x0003C000)
+
+/*!
+ * defines for SPBA modules
+ */
+#define SPBA_SDHC1 0x04
+#define SPBA_SDHC2 0x08
+#define SPBA_UART3 0x0C
+#define SPBA_CSPI2 0x10
+#define SPBA_SSI2 0x14
+#define SPBA_SDHC3 0x20
+#define SPBA_SPDIF 0x28
+#define SPBA_ATA 0x34
+
+/*!
+ * Defines for modules using static and dynamic DMA channels
+ */
+#define MXC_DMA_CHANNEL_IRAM 30
+#define MXC_DMA_CHANNEL_SPDIF_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART3_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART3_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MMC1 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MMC2 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI2_RX MXC_DMA_DYNAMIC_CHANNEL
+#ifdef CONFIG_SDMA_IRAM
+#define MXC_DMA_CHANNEL_SSI2_TX (MXC_DMA_CHANNEL_IRAM + 1)
+#else /*CONFIG_SDMA_IRAM */
+#define MXC_DMA_CHANNEL_SSI2_TX MXC_DMA_DYNAMIC_CHANNEL
+#endif /*CONFIG_SDMA_IRAM */
+#define MXC_DMA_CHANNEL_CSPI1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI3_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI3_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ATA_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ATA_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MEMORY MXC_DMA_DYNAMIC_CHANNEL
+
+/*
+ * AIPS 2
+ */
+#define AIPS2_BASE_ADDR 0xE3F00000
+#define AIPS2_BASE_ADDR_VIRT 0xFC200000
+#define AIPS2_SIZE SZ_1M
+
+#define PLL0_BASE_ADDR (AIPS2_BASE_ADDR + 0x00080000)
+#define PLL1_BASE_ADDR (AIPS2_BASE_ADDR + 0x00084000)
+#define PLL2_BASE_ADDR (AIPS2_BASE_ADDR + 0x00088000)
+#define CCM_BASE_ADDR (AIPS2_BASE_ADDR + 0x0008C000)
+#define GPC_BASE_ADDR (AIPS2_BASE_ADDR + 0x00090000)
+#define SRC_BASE_ADDR (AIPS2_BASE_ADDR + 0x00094000)
+#define EPIT1_BASE_ADDR (AIPS2_BASE_ADDR + 0x00098000)
+#define EPIT2_BASE_ADDR (AIPS2_BASE_ADDR + 0x0009C000)
+#define PWM_BASE_ADDR (AIPS2_BASE_ADDR + 0x000A0000)
+#define OWIRE_BASE_ADDR (AIPS2_BASE_ADDR + 0x000A4000)
+#define CSPI3_BASE_ADDR (AIPS2_BASE_ADDR + 0x000A8000)
+#define CSPI1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000AC000)
+#define UART1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000B0000)
+#define UART2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000BC000)
+#define I2C3_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C0000)
+#define I2C2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C4000)
+#define I2C_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C8000)
+#define SSI1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000CC000)
+#define AUDMUX_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D0000)
+#define EMI_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DBF00)
+
+#define M4IF_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D8000)
+#define ESDCTL_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D9000)
+#define WEIM_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DA000)
+#define NFC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DB000)
+
+/*
+ * Memory regions and CS
+ */
+#define IPU_CTRL_BASE_ADDR 0x80000000
+#define CSD0_BASE_ADDR 0x40000000
+#define CSD1_BASE_ADDR 0x50000000
+
+#define CS0_BASE_ADDR 0x60000000
+#define CS1_BASE_ADDR 0x68000000
+#define CS2_BASE_ADDR 0x70000000
+
+/*!
+ * 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 IO_ADDRESS(x) \
+ (((x >= (unsigned long)IRAM_BASE_ADDR) && (x < (unsigned long)IRAM_BASE_ADDR + IRAM_SIZE)) ? IRAM_IO_ADDRESS(x):\
+ ((x >= (unsigned long)PLATFORM_BASE_ADDR) && (x < (unsigned long)PLATFORM_BASE_ADDR + PLATFORM_SIZE)) ? PLATFORM_IO_ADDRESS(x):\
+ ((x >= (unsigned long)TZIC_BASE_ADDR) && (x < (unsigned long)TZIC_BASE_ADDR + TZIC_SIZE)) ? TZIC_IO_ADDRESS(x):\
+ ((x >= (unsigned long)DEBUG_BASE_ADDR) && (x < (unsigned long)DEBUG_BASE_ADDR + DEBUG_SIZE)) ? DEBUG_IO_ADDRESS(x):\
+ ((x >= (unsigned long)SPBA0_BASE_ADDR) && (x < (unsigned long)SPBA0_BASE_ADDR + SPBA0_SIZE)) ? SPBA0_IO_ADDRESS(x):\
+ ((x >= (unsigned long)AIPS1_BASE_ADDR) && (x < (unsigned long)AIPS1_BASE_ADDR + AIPS1_SIZE)) ? AIPS1_IO_ADDRESS(x):\
+ ((x >= (unsigned long)AIPS2_BASE_ADDR) && (x < (unsigned long)AIPS2_BASE_ADDR + AIPS2_SIZE)) ? AIPS2_IO_ADDRESS(x):\
+ ((x >= (unsigned long)NFC_BASE_ADDR_AXI) && (x < (unsigned long)NFC_BASE_ADDR_AXI + NFC_AXI_SIZE)) ? NFC_BASE_ADDR_AXI_IO_ADDRESS(x):\
+ 0xDEADBEEF)
+
+/*
+ * define the address mapping macros: in physical address order
+ */
+
+#define IRAM_IO_ADDRESS(x) \
+ (((x) - IRAM_BASE_ADDR) + IRAM_BASE_ADDR_VIRT)
+
+#define PLATFORM_IO_ADDRESS(x) \
+ (((x) - PLATFORM_BASE_ADDR) + PLATFORM_BASE_ADDR_VIRT)
+
+#define TZIC_IO_ADDRESS(x) \
+ (((x) - TZIC_BASE_ADDR) + TZIC_BASE_ADDR_VIRT)
+
+#define DEBUG_IO_ADDRESS(x) \
+ (((x) - DEBUG_BASE_ADDR) + DEBUG_BASE_ADDR_VIRT)
+
+#define SPBA0_IO_ADDRESS(x) \
+ (((x) - SPBA0_BASE_ADDR) + SPBA0_BASE_ADDR_VIRT)
+
+#define AIPS1_IO_ADDRESS(x) \
+ (((x) - AIPS1_BASE_ADDR) + AIPS1_BASE_ADDR_VIRT)
+
+#define AIPS2_IO_ADDRESS(x) \
+ (((x) - AIPS2_BASE_ADDR) + AIPS2_BASE_ADDR_VIRT)
+
+#define NFC_BASE_ADDR_AXI_IO_ADDRESS(x) \
+ (((x) - NFC_BASE_ADDR_AXI) + NFC_BASE_ADDR_AXI_VIRT)
+
+#define IS_MEM_DEVICE_NONSHARED(x) ((x) >= 0x80000000)
+
+/*
+ * DMA request assignments
+ */
+#define DMA_REQ_RESV47 47
+#define DMA_REQ_VPU 46
+#define DMA_REQ_SPDIF_TX 45
+#define DMA_REQ_UART3_TX 44
+#define DMA_REQ_UART3_RX 43
+#define DMA_REQ_I2C2 42
+#define DMA_REQ_I2C1 41
+#define DMA_REQ_SDHC3 40
+#define DMA_REQ_CSPI3_TX 39
+#define DMA_REQ_CSPI3_RX 38
+#define DMA_REQ_RESV37 37
+#define DMA_REQ_IPU 36
+#define DMA_REQ_RESV35 35
+#define DMA_REQ_EPIT2 34
+#define DMA_REQ_RESV33 33
+#define DMA_REQ_RESV32 32
+#define DMA_REQ_ECT 31
+#define DMA_REQ_NFC 30
+#define DMA_REQ_SSI1_TX1 29
+#define DMA_REQ_SSI1_RX1 28
+#define DMA_REQ_SSI1_TX2 27
+#define DMA_REQ_SSI1_RX2 26
+#define DMA_REQ_SSI2_TX1 25
+#define DMA_REQ_SSI2_RX1 24
+#define DMA_REQ_SSI2_TX2 23
+#define DMA_REQ_SSI2_RX2 22
+#define DMA_REQ_SDHC2 21
+#define DMA_REQ_SDHC1 20
+#define DMA_REQ_UART1_TX 19
+#define DMA_REQ_UART1_RX 18
+#define DMA_REQ_UART2_TX 17
+#define DMA_REQ_UART2_RX 16
+#define DMA_REQ_GPIO1_0 15
+#define DMA_REQ_GPIO1_1 14
+#define DMA_REQ_RESV13 13
+#define DMA_REQ_RESV12 12
+#define DMA_REQ_RESV11 11
+#define DMA_REQ_RESV10 10
+#define DMA_REQ_CSPI1_TX 9
+#define DMA_REQ_CSPI1_RX 8
+#define DMA_REQ_CSPI2_TX 7
+#define DMA_REQ_CSPI2_RX 6
+#define DMA_REQ_RESV5 5
+#define DMA_REQ_ATA_TX_END 4
+#define DMA_REQ_ATA_TX 3
+#define DMA_REQ_ATA_RX 2
+#define DMA_REQ_GPC 1
+#define DMA_REQ_RESV0 0
+
+/*
+ * Interrupt numbers
+ */
+#define MXC_INT_BASE 0
+#define MXC_INT_RESV0 0
+#define MXC_INT_MMC_SDHC1 1
+#define MXC_INT_MMC_SDHC2 2
+#define MXC_INT_MMC_SDHC3 3
+#define MXC_INT_RESV4 4
+#define MXC_INT_RESV5 5
+#define MXC_INT_SDMA 6
+#define MXC_INT_IOMUX 7
+#define MXC_INT_RESV8 8
+#define MXC_INT_VPU 9
+#define MXC_INT_IPU_ERR 10
+#define MXC_INT_IPU_SYN 11
+#define MXC_INT_RESV12 12
+#define MXC_INT_RESV13 13
+#define MXC_INT_RNG 14
+#define MXC_INT_EMI 15
+#define MXC_INT_RESV16 16
+#define MXC_INT_RESV17 17
+#define MXC_INT_USB_OTG 18
+#define MXC_INT_RESV19 19
+#define MXC_INT_RESV20 20
+#define MXC_INT_SCC_SMN 21
+#define MXC_INT_SCC_STZ 22
+#define MXC_INT_SCC_SCM 23
+#define MXC_INT_SRTC_NTZ 24
+#define MXC_INT_SRTC_TZ 25
+#define MXC_INT_RTIC 26
+#define MXC_INT_CSU 27
+#define MXC_INT_RESV28 28
+#define MXC_INT_SSI1 29
+#define MXC_INT_SSI2 30
+#define MXC_INT_UART1 31
+#define MXC_INT_UART2 32
+#define MXC_INT_UART3 33
+#define MXC_INT_RESV34 34
+#define MXC_INT_RESV35 35
+#define MXC_INT_CSPI1 36
+#define MXC_INT_CSPI2 37
+#define MXC_INT_CSPI3 38
+#define MXC_INT_GPT 39
+#define MXC_INT_EPIT1 40
+#define MXC_INT_EPIT2 41
+#define MXC_INT_GPIO1_INT7 42
+#define MXC_INT_GPIO1_INT6 43
+#define MXC_INT_GPIO1_INT5 44
+#define MXC_INT_GPIO1_INT4 45
+#define MXC_INT_GPIO1_INT3 46
+#define MXC_INT_GPIO1_INT2 47
+#define MXC_INT_GPIO1_INT1 48
+#define MXC_INT_GPIO1_INT0 49
+#define MXC_INT_GPIO1_LOW 50
+#define MXC_INT_GPIO1_HIGH 51
+#define MXC_INT_GPIO2_LOW 52
+#define MXC_INT_GPIO2_HIGH 53
+#define MXC_INT_GPIO3_LOW 54
+#define MXC_INT_GPIO3_HIGH 55
+#define MXC_INT_RESV56 56
+#define MXC_INT_RESV57 57
+#define MXC_INT_WDOG1 58
+#define MXC_INT_WDOG2 59
+#define MXC_INT_KPP 60
+#define MXC_INT_PWM 61
+#define MXC_INT_I2C 62
+#define MXC_INT_I2C2 63
+#define MXC_INT_I2C3 64
+#define MXC_INT_MSHC1 65
+#define MXC_INT_RESV66 66
+#define MXC_INT_RESV67 67
+#define MXC_INT_RESV68 68
+#define MXC_INT_IIM 69
+#define MXC_INT_ATA 70
+#define MXC_INT_CCM1 71
+#define MXC_INT_CCM2 72
+#define MXC_INT_GPC1 73
+#define MXC_INT_GPC2 74
+#define MXC_INT_SRC 75
+#define MXC_INT_EVTMON 76
+#define MXC_INT_PER_MEASURE 77
+#define MXC_INT_DECODE_ERR 78
+#define MXC_INT_EVT_COUNT 79
+#define MXC_INT_SLAVE_ERR 80
+#define MXC_INT_RESV81 81
+#define MXC_INT_RESV82 82
+#define MXC_INT_RESV83 83
+#define MXC_INT_RESV84 84
+#define MXC_INT_RESV85 85
+#define MXC_INT_RESV86 86
+#define MXC_INT_FEC 87
+#define MXC_INT_OWIRE 88
+#define MXC_INT_CTI0 89
+#define MXC_INT_CTM0 90
+#define MXC_INT_SPDIF 91
+#define MXC_INT_TVOUT 92
+
+#define MXC_MAX_INT_LINES 128
+
+/*!
+ * Interrupt Number for ARM11 PMU
+ */
+#define ARM11_PMU_IRQ MXC_INT_PER_MEASURE
+
+#define MXC_GPIO_INT_BASE (MXC_MAX_INT_LINES)
+
+/* 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
+
+/*!
+ * Number of GPIO port as defined in the IC Spec
+ */
+#define GPIO_PORT_NUM 3
+/*!
+ * Number of GPIO pins per port
+ */
+#define GPIO_NUM_PIN 32
+
+#define MXC_GPIO_SPLIT_IRQ_2
+
+/*!
+ * Macro to convert elv, llv, ulv to a data which is used to set DCVR0, DCVR1,
+ * DCVR2, or DCVR3.
+ */
+#define DCVR(elv, llv, ulv) ((elv << 0) | (llv << 10) | (ulv << 21))
+
+#endif /* __ASM_ARCH_MXC_MX37_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx51.h b/arch/arm/plat-mxc/include/mach/mx51.h
new file mode 100644
index 000000000000..4d0aaa8bb2c6
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mx51.h
@@ -0,0 +1,535 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_MX51_H__
+#define __ASM_ARCH_MXC_MX51_H__
+
+#ifndef __ASM_ARCH_MXC_HARDWARE_H__
+#error "Do not include directly."
+#endif
+
+/*!
+ * @file arch-mxc/mx51.h
+ * @brief This file contains register definitions.
+ *
+ * @ingroup MSL_MX51
+ */
+/*!
+ * defines the hardware clock tick rate
+ */
+#define CLOCK_TICK_RATE 8000000
+
+/*!
+ * Register an interrupt handler for the SMN as well as the SCC. In some
+ * implementations, the SMN is not connected at all, and in others, it is
+ * on the same interrupt line as the SCM. Comment this line out accordingly
+ */
+#define USE_SMN_INTERRUPT
+
+/*
+ * UART Chip level Configuration that a user may not have to edit. These
+ * configuration vary depending on how the UART module is integrated with
+ * the ARM core
+ */
+#define MXC_UART_NR 3
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive Irda data.
+ */
+#define MXC_UART_IR_RXDMUX 0x0004
+/*!
+ * This option is used to set or clear the RXDMUXSEL bit in control reg 3.
+ * Certain platforms need this bit to be set in order to receive UART data.
+ */
+#define MXC_UART_RXDMUX 0x0004
+
+/*!
+ * This option is used to set or clear the dspdma bit in the SDMA config
+ * register.
+ */
+#define MXC_SDMA_DSPDMA 0
+
+/*!
+ * Define this option to specify we are using the newer SDMA module.
+ */
+#define MXC_SDMA_V2
+
+ /*
+ * IRAM
+ */
+#define IRAM_BASE_ADDR 0x1FFE0000 /* internal ram */
+#define IRAM_BASE_ADDR_VIRT 0xFA3E0000
+#define IRAM_PARTITIONS 16
+#define IRAM_PARTITIONS_TO1 12
+#define IRAM_SIZE (IRAM_PARTITIONS*SZ_8K) /* 128KB */
+
+#if defined(CONFIG_MXC_SECURITY_SCC2) \
+ || defined(CONFIG_MXC_SECURITY_SCC2_MODULE)
+#define SCC_IRAM_SIZE SZ_16K
+#else
+#define SCC_IRAM_SIZE 0
+#endif
+
+#ifdef CONFIG_SDMA_IRAM
+#define SDMA_IRAM_SIZE CONFIG_SDMA_IRAM_SIZE
+#else
+#define SDMA_IRAM_SIZE 0
+#endif
+
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+#define SND_RAM_SIZE 0x6000
+#else
+#define SND_RAM_SIZE 0
+#endif
+
+#ifdef CONFIG_MXC_VPU_IRAM
+#define VPU_IRAM_SIZE 0x7000
+#else
+#define VPU_IRAM_SIZE 0
+#endif
+
+#if (IRAM_SIZE < (SDMA_IRAM_SIZE + SND_RAM_SIZE + VPU_IRAM_SIZE + \
+ SCC_IRAM_SIZE))
+#error "IRAM size exceeded"
+#endif
+
+#define SCC_IRAM_BASE_ADDR (IRAM_BASE_ADDR + IRAM_SIZE - SCC_IRAM_SIZE)
+#define VPU_IRAM_BASE_ADDR (SCC_IRAM_BASE_ADDR - VPU_IRAM_SIZE)
+#define SND_RAM_BASE_ADDR (VPU_IRAM_BASE_ADDR - SND_RAM_SIZE)
+#define SDMA_IRAM_BASE_ADDR (SND_RAM_BASE_ADDR - SDMA_IRAM_SIZE)
+#define IDLE_IRAM_BASE_ADDR (SDMA_IRAM_BASE_ADDR - SZ_4K)
+#define SUSPEND_IRAM_BASE_ADDR (IDLE_IRAM_BASE_ADDR - SZ_4K)
+/*
+ * NFC
+ */
+#define NFC_BASE_ADDR_AXI 0xCFFF0000 /* NAND flash AXI */
+#define NFC_BASE_ADDR_AXI_VIRT 0xF9000000
+#define NFC_AXI_SIZE SZ_64K
+
+/*
+ * Graphics Memory of GPU
+ */
+#define GPU_BASE_ADDR 0x20000000
+#define GPU2D_BASE_ADDR 0xD0000000
+
+#define TZIC_BASE_ADDR 0x8FFFC000
+#define TZIC_BASE_ADDR_VIRT 0xFA100000
+#define TZIC_SIZE SZ_16K
+
+#define DEBUG_BASE_ADDR 0x60000000
+#define DEBUG_BASE_ADDR_VIRT 0xFA200000
+#define DEBUG_SIZE SZ_1M
+#define ETB_BASE_ADDR (DEBUG_BASE_ADDR + 0x00001000)
+#define ETM_BASE_ADDR (DEBUG_BASE_ADDR + 0x00002000)
+#define TPIU_BASE_ADDR (DEBUG_BASE_ADDR + 0x00003000)
+#define CTI0_BASE_ADDR (DEBUG_BASE_ADDR + 0x00004000)
+#define CTI1_BASE_ADDR (DEBUG_BASE_ADDR + 0x00005000)
+#define CTI2_BASE_ADDR (DEBUG_BASE_ADDR + 0x00006000)
+#define CTI3_BASE_ADDR (DEBUG_BASE_ADDR + 0x00007000)
+#define CORTEX_DBG_BASE_ADDR (DEBUG_BASE_ADDR + 0x00008000)
+
+/*
+ * SPBA global module enabled #0
+ */
+#define SPBA0_BASE_ADDR 0x70000000
+#define SPBA0_BASE_ADDR_VIRT 0xFB100000
+#define SPBA0_SIZE SZ_1M
+
+#define MMC_SDHC1_BASE_ADDR (SPBA0_BASE_ADDR + 0x00004000)
+#define MMC_SDHC2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00008000)
+#define UART3_BASE_ADDR (SPBA0_BASE_ADDR + 0x0000C000)
+#define CSPI1_BASE_ADDR (SPBA0_BASE_ADDR + 0x00010000)
+#define SSI2_BASE_ADDR (SPBA0_BASE_ADDR + 0x00014000)
+#define MMC_SDHC3_BASE_ADDR (SPBA0_BASE_ADDR + 0x00020000)
+#define MMC_SDHC4_BASE_ADDR (SPBA0_BASE_ADDR + 0x00024000)
+#define SPDIF_BASE_ADDR (SPBA0_BASE_ADDR + 0x00028000)
+#define ATA_DMA_BASE_ADDR (SPBA0_BASE_ADDR + 0x00030000)
+#define SLIM_DMA_BASE_ADDR (SPBA0_BASE_ADDR + 0x00034000)
+#define HSI2C_DMA_BASE_ADDR (SPBA0_BASE_ADDR + 0x00038000)
+#define SPBA_CTRL_BASE_ADDR (SPBA0_BASE_ADDR + 0x0003C000)
+
+/*!
+ * defines for SPBA modules
+ */
+#define SPBA_SDHC1 0x04
+#define SPBA_SDHC2 0x08
+#define SPBA_UART3 0x0C
+#define SPBA_CSPI1 0x10
+#define SPBA_SSI2 0x14
+#define SPBA_SDHC3 0x20
+#define SPBA_SDHC4 0x24
+#define SPBA_SPDIF 0x28
+#define SPBA_ATA 0x30
+#define SPBA_SLIM 0x34
+#define SPBA_HSI2C 0x38
+#define SPBA_CTRL 0x3C
+
+/*
+ * AIPS 1
+ */
+#define AIPS1_BASE_ADDR 0x73F00000
+#define AIPS1_BASE_ADDR_VIRT 0xFB000000
+#define AIPS1_SIZE SZ_1M
+
+#define OTG_BASE_ADDR (AIPS1_BASE_ADDR + 0x00080000)
+#define GPIO1_BASE_ADDR (AIPS1_BASE_ADDR + 0x00084000)
+#define GPIO2_BASE_ADDR (AIPS1_BASE_ADDR + 0x00088000)
+#define GPIO3_BASE_ADDR (AIPS1_BASE_ADDR + 0x0008C000)
+#define GPIO4_BASE_ADDR (AIPS1_BASE_ADDR + 0x00090000)
+#define KPP_BASE_ADDR (AIPS1_BASE_ADDR + 0x00094000)
+#define WDOG1_BASE_ADDR (AIPS1_BASE_ADDR + 0x00098000)
+#define WDOG2_BASE_ADDR (AIPS1_BASE_ADDR + 0x0009C000)
+#define GPT1_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A0000)
+#define SRTC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A4000)
+#define IOMUXC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000A8000)
+#define EPIT1_BASE_ADDR (AIPS1_BASE_ADDR + 0x000AC000)
+#define EPIT2_BASE_ADDR (AIPS1_BASE_ADDR + 0x000B0000)
+#define PWM1_BASE_ADDR (AIPS1_BASE_ADDR + 0x000B4000)
+#define PWM2_BASE_ADDR (AIPS1_BASE_ADDR + 0x000B8000)
+#define UART1_BASE_ADDR (AIPS1_BASE_ADDR + 0x000BC000)
+#define UART2_BASE_ADDR (AIPS1_BASE_ADDR + 0x000C0000)
+#define SRC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000D0000)
+#define CCM_BASE_ADDR (AIPS1_BASE_ADDR + 0x000D4000)
+#define GPC_BASE_ADDR (AIPS1_BASE_ADDR + 0x000D8000)
+
+/*!
+ * Defines for modules using static and dynamic DMA channels
+ */
+#define MXC_DMA_CHANNEL_IRAM 30
+#define MXC_DMA_CHANNEL_SPDIF_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART3_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_UART3_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MMC1 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MMC2 MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_SSI2_RX MXC_DMA_DYNAMIC_CHANNEL
+#ifdef CONFIG_SDMA_IRAM
+#define MXC_DMA_CHANNEL_SSI2_TX (MXC_DMA_CHANNEL_IRAM + 1)
+#else /*CONFIG_SDMA_IRAM */
+#define MXC_DMA_CHANNEL_SSI2_TX MXC_DMA_DYNAMIC_CHANNEL
+#endif /*CONFIG_SDMA_IRAM */
+#define MXC_DMA_CHANNEL_CSPI1_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI1_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI2_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI2_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI3_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_CSPI3_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ATA_RX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_ATA_TX MXC_DMA_DYNAMIC_CHANNEL
+#define MXC_DMA_CHANNEL_MEMORY MXC_DMA_DYNAMIC_CHANNEL
+
+/*
+ * AIPS 2
+ */
+#define AIPS2_BASE_ADDR 0x83F00000
+#define AIPS2_BASE_ADDR_VIRT 0xFB200000
+#define AIPS2_SIZE SZ_1M
+
+#define PLL1_BASE_ADDR (AIPS2_BASE_ADDR + 0x00080000)
+#define PLL2_BASE_ADDR (AIPS2_BASE_ADDR + 0x00084000)
+#define PLL3_BASE_ADDR (AIPS2_BASE_ADDR + 0x00088000)
+#define AHBMAX_BASE_ADDR (AIPS2_BASE_ADDR + 0x00094000)
+#define IIM_BASE_ADDR (AIPS2_BASE_ADDR + 0x00098000)
+#define CSU_BASE_ADDR (AIPS2_BASE_ADDR + 0x0009C000)
+#define ARM_BASE_ADDR (AIPS2_BASE_ADDR + 0x000A0000)
+#define OWIRE_BASE_ADDR (AIPS2_BASE_ADDR + 0x000A4000)
+#define FIRI_BASE_ADDR (AIPS2_BASE_ADDR + 0x000A8000)
+#define CSPI2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000AC000)
+#define SDMA_BASE_ADDR (AIPS2_BASE_ADDR + 0x000B0000)
+#define SCC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000B4000)
+#define ROMCP_BASE_ADDR (AIPS2_BASE_ADDR + 0x000B8000)
+#define RTIC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000BC000)
+#define CSPI3_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C0000)
+#define I2C2_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C4000)
+#define I2C1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000C8000)
+#define SSI1_BASE_ADDR (AIPS2_BASE_ADDR + 0x000CC000)
+#define AUDMUX_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D0000)
+#define M4IF_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D8000)
+#define ESDCTL_BASE_ADDR (AIPS2_BASE_ADDR + 0x000D9000)
+#define WEIM_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DA000)
+#define NFC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DB000)
+#define EMI_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DBF00)
+#define MIPI_HSC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000DC000)
+#define ATA_BASE_ADDR (AIPS2_BASE_ADDR + 0x000E0000)
+#define SIM_BASE_ADDR (AIPS2_BASE_ADDR + 0x000E4000)
+#define SSI3BASE_ADDR (AIPS2_BASE_ADDR + 0x000E8000)
+#define FEC_BASE_ADDR (AIPS2_BASE_ADDR + 0x000EC000)
+#define TVE_BASE_ADDR (AIPS2_BASE_ADDR + 0x000F0000)
+#define VPU_BASE_ADDR (AIPS2_BASE_ADDR + 0x000F4000)
+#define SAHARA_BASE_ADDR (AIPS2_BASE_ADDR + 0x000F8000)
+
+/*
+ * Memory regions and CS
+ */
+#define GPU_CTRL_BASE_ADDR 0x30000000
+#define IPU_CTRL_BASE_ADDR 0x40000000
+#define CSD0_BASE_ADDR 0x90000000
+#define CSD1_BASE_ADDR 0xA0000000
+#define CS0_BASE_ADDR 0xB0000000
+#define CS1_BASE_ADDR 0xB8000000
+#define CS2_BASE_ADDR 0xC0000000
+#define CS3_BASE_ADDR 0xC8000000
+#define CS4_BASE_ADDR 0xCC000000
+#define CS5_BASE_ADDR 0xCE000000
+
+/*!
+ * 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 IO_ADDRESS(x) \
+ ((((x) >= (unsigned long)IRAM_BASE_ADDR) && \
+ ((x) < (unsigned long)IRAM_BASE_ADDR + IRAM_SIZE)) ? \
+ IRAM_IO_ADDRESS(x):\
+ (((x) >= (unsigned long)TZIC_BASE_ADDR) && \
+ ((x) < (unsigned long)TZIC_BASE_ADDR + TZIC_SIZE)) ? \
+ TZIC_IO_ADDRESS(x):\
+ (((x) >= (unsigned long)DEBUG_BASE_ADDR) && \
+ ((x) < (unsigned long)DEBUG_BASE_ADDR + DEBUG_SIZE)) ? \
+ DEBUG_IO_ADDRESS(x):\
+ (((x) >= (unsigned long)SPBA0_BASE_ADDR) && \
+ ((x) < (unsigned long)SPBA0_BASE_ADDR + SPBA0_SIZE)) ? \
+ SPBA0_IO_ADDRESS(x):\
+ (((x) >= (unsigned long)AIPS1_BASE_ADDR) && \
+ ((x) < (unsigned long)AIPS1_BASE_ADDR + AIPS1_SIZE)) ? \
+ AIPS1_IO_ADDRESS(x):\
+ (((x) >= (unsigned long)AIPS2_BASE_ADDR) && \
+ ((x) < (unsigned long)AIPS2_BASE_ADDR + AIPS2_SIZE)) ? \
+ AIPS2_IO_ADDRESS(x):\
+ (((x) >= (unsigned long)NFC_BASE_ADDR_AXI) && \
+ ((x) < (unsigned long)NFC_BASE_ADDR_AXI + NFC_AXI_SIZE)) ? \
+ NFC_BASE_ADDR_AXI_IO_ADDRESS(x):\
+ 0xDEADBEEF)
+
+/*
+ * define the address mapping macros: in physical address order
+ */
+#define IRAM_IO_ADDRESS(x) \
+ (((x) - IRAM_BASE_ADDR) + IRAM_BASE_ADDR_VIRT)
+
+#define TZIC_IO_ADDRESS(x) \
+ (((x) - TZIC_BASE_ADDR) + TZIC_BASE_ADDR_VIRT)
+
+#define DEBUG_IO_ADDRESS(x) \
+ (((x) - DEBUG_BASE_ADDR) + DEBUG_BASE_ADDR_VIRT)
+
+#define SPBA0_IO_ADDRESS(x) \
+ (((x) - SPBA0_BASE_ADDR) + SPBA0_BASE_ADDR_VIRT)
+
+#define AIPS1_IO_ADDRESS(x) \
+ (((x) - AIPS1_BASE_ADDR) + AIPS1_BASE_ADDR_VIRT)
+
+#define AIPS2_IO_ADDRESS(x) \
+ (((x) - AIPS2_BASE_ADDR) + AIPS2_BASE_ADDR_VIRT)
+
+#define NFC_BASE_ADDR_AXI_IO_ADDRESS(x) \
+ (((x) - NFC_BASE_ADDR_AXI) + NFC_BASE_ADDR_AXI_VIRT)
+
+#define IS_MEM_DEVICE_NONSHARED(x) 0
+
+/*
+ * DMA request assignments
+ */
+#define DMA_REQ_SSI3_TX1 47
+#define DMA_REQ_SSI3_RX1 46
+#define DMA_REQ_SPDIF 45
+#define DMA_REQ_UART3_TX 44
+#define DMA_REQ_UART3_RX 43
+#define DMA_REQ_SLIM_B_TX 42
+#define DMA_REQ_SDHC4 41
+#define DMA_REQ_SDHC3 40
+#define DMA_REQ_CSPI_TX 39
+#define DMA_REQ_CSPI_RX 38
+#define DMA_REQ_SSI3_TX2 37
+#define DMA_REQ_IPU 36
+#define DMA_REQ_SSI3_RX2 35
+#define DMA_REQ_EPIT2 34
+#define DMA_REQ_CTI2_1 33
+#define DMA_REQ_EMI_WR 32
+#define DMA_REQ_CTI2_0 31
+#define DMA_REQ_EMI_RD 30
+#define DMA_REQ_SSI1_TX1 29
+#define DMA_REQ_SSI1_RX1 28
+#define DMA_REQ_SSI1_TX2 27
+#define DMA_REQ_SSI1_RX2 26
+#define DMA_REQ_SSI2_TX1 25
+#define DMA_REQ_SSI2_RX1 24
+#define DMA_REQ_SSI2_TX2 23
+#define DMA_REQ_SSI2_RX2 22
+#define DMA_REQ_SDHC2 21
+#define DMA_REQ_SDHC1 20
+#define DMA_REQ_UART1_TX 19
+#define DMA_REQ_UART1_RX 18
+#define DMA_REQ_UART2_TX 17
+#define DMA_REQ_UART2_RX 16
+#define DMA_REQ_GPU 15
+#define DMA_REQ_EXTREQ1 14
+#define DMA_REQ_FIRI_TX 13
+#define DMA_REQ_FIRI_RX 12
+#define DMA_REQ_HS_I2C_RX 11
+#define DMA_REQ_HS_I2C_TX 10
+#define DMA_REQ_CSPI2_TX 9
+#define DMA_REQ_CSPI2_RX 8
+#define DMA_REQ_CSPI1_TX 7
+#define DMA_REQ_CSPI1_RX 6
+#define DMA_REQ_SLIM_B 5
+#define DMA_REQ_ATA_TX_END 4
+#define DMA_REQ_ATA_TX 3
+#define DMA_REQ_ATA_RX 2
+#define DMA_REQ_GPC 1
+#define DMA_REQ_VPU 0
+
+/*
+ * Interrupt numbers
+ */
+#define MXC_INT_BASE 0
+#define MXC_INT_RESV0 0
+#define MXC_INT_MMC_SDHC1 1
+#define MXC_INT_MMC_SDHC2 2
+#define MXC_INT_MMC_SDHC3 3
+#define MXC_INT_MMC_SDHC4 4
+#define MXC_INT_RESV5 5
+#define MXC_INT_SDMA 6
+#define MXC_INT_IOMUX 7
+#define MXC_INT_NFC 8
+#define MXC_INT_VPU 9
+#define MXC_INT_IPU_ERR 10
+#define MXC_INT_IPU_SYN 11
+#define MXC_INT_GPU 12
+#define MXC_INT_RESV13 13
+#define MXC_INT_USB_H1 14
+#define MXC_INT_EMI 15
+#define MXC_INT_USB_H2 16
+#define MXC_INT_USB_H3 17
+#define MXC_INT_USB_OTG 18
+#define MXC_INT_SAHARA_H0 19
+#define MXC_INT_SAHARA_H1 20
+#define MXC_INT_SCC_SMN 21
+#define MXC_INT_SCC_STZ 22
+#define MXC_INT_SCC_SCM 23
+#define MXC_INT_SRTC_NTZ 24
+#define MXC_INT_SRTC_TZ 25
+#define MXC_INT_RTIC 26
+#define MXC_INT_CSU 27
+#define MXC_INT_SLIM_B 28
+#define MXC_INT_SSI1 29
+#define MXC_INT_SSI2 30
+#define MXC_INT_UART1 31
+#define MXC_INT_UART2 32
+#define MXC_INT_UART3 33
+#define MXC_INT_RESV34 34
+#define MXC_INT_RESV35 35
+#define MXC_INT_CSPI1 36
+#define MXC_INT_CSPI2 37
+#define MXC_INT_CSPI 38
+#define MXC_INT_GPT 39
+#define MXC_INT_EPIT1 40
+#define MXC_INT_EPIT2 41
+#define MXC_INT_GPIO1_INT7 42
+#define MXC_INT_GPIO1_INT6 43
+#define MXC_INT_GPIO1_INT5 44
+#define MXC_INT_GPIO1_INT4 45
+#define MXC_INT_GPIO1_INT3 46
+#define MXC_INT_GPIO1_INT2 47
+#define MXC_INT_GPIO1_INT1 48
+#define MXC_INT_GPIO1_INT0 49
+#define MXC_INT_GPIO1_LOW 50
+#define MXC_INT_GPIO1_HIGH 51
+#define MXC_INT_GPIO2_LOW 52
+#define MXC_INT_GPIO2_HIGH 53
+#define MXC_INT_GPIO3_LOW 54
+#define MXC_INT_GPIO3_HIGH 55
+#define MXC_INT_GPIO4_LOW 56
+#define MXC_INT_GPIO4_HIGH 57
+#define MXC_INT_WDOG1 58
+#define MXC_INT_WDOG2 59
+#define MXC_INT_KPP 60
+#define MXC_INT_PWM1 61
+#define MXC_INT_I2C1 62
+#define MXC_INT_I2C2 63
+#define MXC_INT_HS_I2C 64
+#define MXC_INT_RESV65 65
+#define MXC_INT_RESV66 66
+#define MXC_INT_SIM_IPB 67
+#define MXC_INT_SIM_DAT 68
+#define MXC_INT_IIM 69
+#define MXC_INT_ATA 70
+#define MXC_INT_CCM1 71
+#define MXC_INT_CCM2 72
+#define MXC_INT_GPC1 73
+#define MXC_INT_GPC2 74
+#define MXC_INT_SRC 75
+#define MXC_INT_NM 76
+#define MXC_INT_PMU 77
+#define MXC_INT_CTI_IRQ 78
+#define MXC_INT_CTI1_TG0 79
+#define MXC_INT_CTI1_TG1 80
+#define MXC_INT_MCG_ERR 81
+#define MXC_INT_MCG_TMR 82
+#define MXC_INT_MCG_FUNC 83
+#define MXC_INT_GPU2_IRQ 84
+#define MXC_INT_GPU2_BUSY 85
+#define MXC_INT_RESV86 86
+#define MXC_INT_FEC 87
+#define MXC_INT_OWIRE 88
+#define MXC_INT_CTI1_TG2 89
+#define MXC_INT_SJC 90
+#define MXC_INT_SPDIF 91
+#define MXC_INT_TVE 92
+#define MXC_INT_FIRI 93
+#define MXC_INT_PWM2 94
+#define MXC_INT_SLIM_EXP 95
+#define MXC_INT_SSI3 96
+#define MXC_INT_EMI_BOOT 97
+#define MXC_INT_CTI1_TG3 98
+#define MXC_INT_SMC_RX 99
+#define MXC_INT_VPU_IDLE 100
+#define MXC_INT_EMI_NFC 101
+#define MXC_INT_GPU_IDLE 102
+
+#define MXC_MAX_INT_LINES 128
+
+#define MXC_GPIO_INT_BASE (MXC_MAX_INT_LINES)
+
+/* 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
+
+/*!
+ * Number of GPIO port as defined in the IC Spec
+ */
+#define GPIO_PORT_NUM 4
+/*!
+ * Number of GPIO pins per port
+ */
+#define GPIO_NUM_PIN 32
+
+#define MXC_GPIO_SPLIT_IRQ_2
+
+#endif /* __ASM_ARCH_MXC_MX51_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h
index f6caab062131..e3eb1871ad44 100644
--- a/arch/arm/plat-mxc/include/mach/mxc.h
+++ b/arch/arm/plat-mxc/include/mach/mxc.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de)
*
* This program is free software; you can redistribute it and/or
@@ -24,19 +24,500 @@
#error "Do not include directly."
#endif
-/* clean up all things that are not used */
-#ifndef CONFIG_ARCH_MX3
-# define cpu_is_mx31() (0)
+#ifndef __ASSEMBLY__
+#include <linux/types.h>
+
+/*!
+ * This structure is used to define the One wire platform data.
+ * It includes search rom accelerator.
+ */
+
+struct mxc_w1_config {
+ int search_rom_accelerator;
+};
+/*!
+ * This structure is used to define the SPI master controller's platform
+ * data. It includes the SPI bus number and the maximum number of
+ * slaves/chips it supports.
+ */
+struct mxc_spi_master {
+ /*!
+ * SPI Master's bus number.
+ */
+ unsigned int bus_num;
+ /*!
+ * SPI Master's maximum number of chip selects.
+ */
+ unsigned int maxchipselect;
+ /*!
+ * CSPI Hardware Version.
+ */
+ unsigned int spi_version;
+ /*!
+ * CSPI chipselect pin table.
+ * Workaround for ecspi chipselect pin may not keep correct level when
+ * idle.
+ */
+ void (*chipselect_active) (int cspi_mode, int status, int chipselect);
+ void (*chipselect_inactive) (int cspi_mode, int status, int chipselect);
+};
+
+struct mxc_ipu_config {
+ int rev;
+ struct clk *di_clk[2];
+};
+
+struct mxc_ir_platform_data {
+ int uart_ir_mux;
+ int ir_rx_invert;
+ int ir_tx_invert;
+ struct clk *uart_clk;
+};
+
+struct mxc_i2c_platform_data {
+ u32 i2c_clk;
+};
+
+/*
+ * This struct is to define the number of SSIs on a platform,
+ * DAM source port config, DAM external port config,
+ * regulator names, and other stuff audio needs.
+ */
+struct mxc_audio_platform_data {
+ int ssi_num;
+ int src_port;
+ int ext_port;
+
+ int intr_id_hp;
+ int ext_ram;
+ struct clk *ssi_clk[2];
+ char *regulator1;
+ char *regulator2;
+
+ int hp_irq;
+ int (*hp_status) (void);
+
+ char *vddio_reg;
+ char *vdda_reg;
+ char *vddd_reg;
+ int vddio; /* voltage of VDDIO (uv) */
+ int vdda; /* voltage of vdda (uv) */
+ int vddd; /* voltage of vddd (uv), 0 if not connected */
+ int sysclk;
+
+ int (*init) (void); /* board specific init */
+ int (*amp_enable) (int enable);
+ int (*finit) (void); /* board specific finit */
+ void *priv; /* used by board specific functions */
+};
+
+struct mxc_spdif_platform_data {
+ int spdif_tx;
+ int spdif_rx;
+ int spdif_clk_44100;
+ int spdif_clk_48000;
+ int spdif_clkid;
+ struct clk *spdif_clk;
+ struct clk *spdif_core_clk;
+ struct clk *spdif_audio_clk;
+};
+
+struct mxc_asrc_platform_data {
+ struct clk *asrc_core_clk;
+ struct clk *asrc_audio_clk;
+ unsigned int channel_bits;
+};
+
+struct mxc_bt_platform_data {
+ char *bt_vdd;
+ char *bt_vdd_parent;
+ char *bt_vusb;
+ char *bt_vusb_parent;
+ void (*bt_reset) (void);
+};
+
+struct mxc_lightsensor_platform_data {
+ char *vdd_reg;
+ int rext;
+};
+
+struct mxc_fb_platform_data {
+ struct fb_videomode *mode;
+ char *mode_str;
+ u32 interface_pix_fmt;
+};
+
+struct mxc_lcd_platform_data {
+ char *io_reg;
+ char *core_reg;
+ char *analog_reg;
+ void (*reset) (void);
+};
+
+struct mxc_dvfs_platform_data {
+ /** Supply voltage regulator name string */
+ char *reg_id;
+ /* CPU clock name string */
+ char *clk1_id;
+ /* DVFS clock name string */
+ char *clk2_id;
+ /* GPC control reg address */
+ unsigned int gpc_cntr_reg_addr;
+ /* GPC voltage counter reg address */
+ unsigned int gpc_vcr_reg_addr;
+ /* CCM DVFS control reg address */
+ unsigned int ccm_cdcr_reg_addr;
+ /* CCM ARM clock root reg address */
+ unsigned int ccm_cacrr_reg_addr;
+ /* CCM divider handshake in-progree reg address */
+ unsigned int ccm_cdhipr_reg_addr;
+ /* DVFS threshold reg address */
+ unsigned int dvfs_thrs_reg_addr;
+ /* DVFS counters reg address */
+ unsigned int dvfs_coun_reg_addr;
+ /* DVFS EMAC reg address */
+ unsigned int dvfs_emac_reg_addr;
+ /* DVFS control reg address */
+ unsigned int dvfs_cntr_reg_addr;
+ /* PREDIV mask */
+ u32 prediv_mask;
+ /* PREDIV offset */
+ int prediv_offset;
+ /* PREDIV value */
+ int prediv_val;
+ /* DIV3CK mask */
+ u32 div3ck_mask;
+ /* DIV3CK offset */
+ int div3ck_offset;
+ /* DIV3CK value */
+ int div3ck_val;
+ /* EMAC value */
+ int emac_val;
+ /* Frequency increase threshold. Increase frequency change request
+ will be sent if DVFS counter value will be more than this value */
+ int upthr_val;
+ /* Frequency decrease threshold. Decrease frequency change request
+ will be sent if DVFS counter value will be less than this value */
+ int dnthr_val;
+ /* Panic threshold. Panic frequency change request
+ will be sent if DVFS counter value will be more than this value */
+ int pncthr_val;
+ /* The amount of times the up threshold should be exceeded
+ before DVFS will trigger frequency increase request */
+ int upcnt_val;
+ /* The amount of times the down threshold should be exceeded
+ before DVFS will trigger frequency decrease request */
+ int dncnt_val;
+ /* Delay time in us */
+ int delay_time;
+ /* Number of woking points supported */
+ int num_wp;
+};
+
+struct mxc_tsc_platform_data {
+ char *vdd_reg;
+ int penup_threshold;
+ void (*active) (void);
+ void (*inactive) (void);
+};
+
+struct mxc_tvout_platform_data {
+ char *io_reg;
+ char *core_reg;
+ char *analog_reg;
+ u32 detect_line;
+};
+
+struct mxc_tvin_platform_data {
+ char *dvddio_reg;
+ char *dvdd_reg;
+ char *avdd_reg;
+ char *pvdd_reg;
+ void (*pwdn) (int pwdn);
+ void (*reset) (void);
+};
+
+/*! Platform data for the IDE drive structure. */
+struct mxc_ide_platform_data {
+ char *power_drive; /*!< The power pointer */
+ char *power_io; /*!< The power pointer */
+};
+
+struct mxc_camera_platform_data {
+ char *core_regulator;
+ char *io_regulator;
+ char *analog_regulator;
+ char *gpo_regulator;
+ u32 mclk;
+ u32 csi;
+};
+
+/*gpo1-3 is in fixed state by hardware design,
+ * only deal with reset pin and clock_enable pin
+ * only poll mode can be used to control the chip,
+ * interrupt mode is not supported by 3ds*/
+struct mxc_fm_platform_data {
+ char *reg_vio;
+ char *reg_vdd;
+ void (*gpio_get) (void);
+ void (*gpio_put) (void);
+ void (*reset) (void);
+ void (*clock_ctl) (int flag);
+ u8 sksnr; /*0,disable;1,most stop;0xf,fewest stop*/
+ u8 skcnt; /*0,disable;1,most stop;0xf,fewest stop*/
+ /*
+ 00 = 87.5-108 MHz (USA,Europe) (Default).
+ 01 = 76-108 MHz (Japan wide band).
+ 10 = 76-90 MHz (Japan).
+ 11 = Reserved.
+ */
+ u8 band;
+ /*
+ 00 = 200 kHz (USA, Australia) (default).
+ 01 = 100 kHz (Europe, Japan).
+ 10 = 50 kHz.
+ */
+ u8 space;
+ u8 seekth;
+};
+
+struct mxc_mma7450_platform_data {
+ char *reg_dvdd_io;
+ char *reg_avdd;
+ void (*gpio_pin_get) (void);
+ void (*gpio_pin_put) (void);
+ int int1;
+ int int2;
+};
+
+struct mxc_keyp_platform_data {
+ u16 *matrix;
+ void (*active) (void);
+ void (*inactive) (void);
+ char *vdd_reg;
+};
+
+struct mxc_unifi_platform_data {
+ void (*hardreset) (int pin_level);
+ void (*enable) (int en);
+ /* power parameters */
+ char *reg_gpo1;
+ char *reg_gpo2;
+ char *reg_1v5_ana_bb;
+ char *reg_vdd_vpa;
+ char *reg_1v5_dd;
+
+ int host_id;
+
+ void *priv;
+};
+
+struct mxc_gps_platform_data {
+ char *core_reg;
+ char *analog_reg;
+ struct regulator *gps_regu_core;
+ struct regulator *gps_regu_analog;
+};
+
+struct mxc_mlb_platform_data {
+ u32 buf_address;
+ u32 phy_address;
+ char *reg_nvcc;
+ char *mlb_clk;
+};
+
+struct flexcan_platform_data {
+ char *core_reg;
+ char *io_reg;
+ void (*xcvr_enable) (int id, int en);
+ void (*active) (int id);
+ void (*inactive) (int id);
+};
+
+struct mxc_srtc_platform_data {
+ u32 srtc_sec_mode_addr;
+};
+
+struct tve_platform_data {
+ char *dac_reg;
+ char *dig_reg;
+};
+
+/* The name that links the i.MX NAND Flash Controller driver to its devices. */
+
+#define IMX_NFC_DRIVER_NAME ("imx_nfc")
+
+/* Resource names for the i.MX NAND Flash Controller driver. */
+
+#define IMX_NFC_BUFFERS_ADDR_RES_NAME \
+ ("i.MX NAND Flash Controller Buffer")
+#define IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME \
+ ("i.MX NAND Flash Controller Primary Registers")
+#define IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME \
+ ("i.MX NAND Flash Controller Secondary Registers")
+#define IMX_NFC_INTERRUPT_RES_NAME \
+ ("i.MX NAND Flash Controller Interrupt")
+
+/**
+ * struct imx_nfc_platform_data - i.MX NFC driver platform data.
+ *
+ * This structure communicates information to the i.MX NFC driver that can't be
+ * expressed as resources.
+ *
+ * @nfc_major_version: The "major version" of the NFC hardware.
+ * @nfc_minor_version: The "minor version" of the NFC hardware.
+ * @force_ce: If true, this flag causes the driver to assert the
+ * hardware chip enable signal for the currently selected
+ * chip as long as the MTD NAND Flash HAL has the chip
+ * selected (not just when an I/O transaction is in
+ * progress).
+ * @target_cycle_in_ns: The target read and write cycle period, in nanoseconds.
+ * NAND Flash part data sheets give minimum times for read
+ * and write cycles in nanoseconds (usually tRC and tWC,
+ * respectively). Set this value to the maximum of these
+ * two parameters. The driver will set the NFC clock as
+ * close as possible without violating this value.
+ * @clock_name: The name of the clock used by the NAND Flash controller.
+ * @init: A pointer to a function the driver must call so the
+ * platform can prepare for this device to operate. This
+ * pointer may be NULL.
+ * @exit: A pointer to a function the driver must call so the
+ * platform clean up after this device stops operating.
+ * This pointer may be NULL.
+ * @set_page_size: A pointer to a function the driver can call to set the
+ * page size. This pointer may be NULL.
+ *
+ * For some i.MX SoC's, the NFC gets information about the
+ * page size from signals driven by a system register
+ * outside the NFC. The address and format of this external
+ * register varies across SoC's. In other SoC's, the NFC
+ * still receives this signal, but it is overridden by a
+ * page size register in the NFC itself.
+ *
+ * For SoC's where the page size *must* be set in an
+ * external register, the driver must rely on a platform-
+ * specific function, and this member must point to it.
+ *
+ * For SoC's where the NFC has its own page size register,
+ * the driver will set that register itself and ignore the
+ * external signals. In this case, there's no need for the
+ * platform-specific function and this member must be NULL.
+ *
+ * This function accepts the page size in bytes (MTD calls
+ * this the "writesize") discovered by the NAND Flash MTD
+ * base driver (e.g., 512, 2048, 4096). This size refers
+ * specifically to the the data bytes in the page, *not*
+ * including out-of-band bytes. The return value is zero if
+ * the operation succeeded. The driver does *not* view a
+ * non-zero value as an error code - only an indication of
+ * failure. The driver will decide for itself what error
+ * code to return to its caller.
+ * @interleave: Indicates that the driver should "interleave" the NAND
+ * Flash chips it finds. If true, the driver will aggregate
+ * the chips "horizontally" such that MTD will see a single
+ * chip with a potentially very large page size. This can
+ * improve write performance for some applications.
+ * @partitions: An optional pointer to an array of partitions. If this
+ * is NULL, the driver will create a single MTD that
+ * represents the entire medium.
+ * @partition_count: The number of elements in the partition array.
+ */
+
+struct imx_nfc_platform_data {
+ unsigned int nfc_major_version;
+ unsigned int nfc_minor_version;
+ int force_ce;
+ unsigned int target_cycle_in_ns;
+ char *clock_name;
+ int (*init)(void);
+ void (*exit)(void);
+ int (*set_page_size)(unsigned int data_size_in_bytes);
+ int interleave;
+ struct mtd_partition *partitions;
+ unsigned int partition_count;
+};
+
+extern void mxc_wd_reset(void);
+unsigned long board_get_ckih_rate(void);
+
+int mxc_snoop_set_config(u32 num, unsigned long base, int size);
+int mxc_snoop_get_status(u32 num, u32 * statl, u32 * stath);
+
+struct platform_device;
+void mxc_pg_enable(struct platform_device *pdev);
+void mxc_pg_disable(struct platform_device *pdev);
+
+struct mxc_unifi_platform_data *get_unifi_plat_data(void);
+
+struct mxc_sim_platform_data {
+ unsigned int clk_rate;
+ char *clock_sim;
+ char *power_sim;
+ int (*init)(struct platform_device *pdev);
+ void (*exit)(void);
+ unsigned int detect; /* 1 have detect pin, 0 not */
+};
+
+#endif /* __ASSEMBLY__ */
+
+#define MUX_IO_P 29
+#define MUX_IO_I 24
+#define IOMUX_TO_GPIO(pin) ((((unsigned int)pin >> MUX_IO_P) * GPIO_NUM_PIN) + ((pin >> MUX_IO_I) & ((1 << (MUX_IO_P - MUX_IO_I)) -1)))
+#define IOMUX_TO_IRQ(pin) (MXC_GPIO_INT_BASE + IOMUX_TO_GPIO(pin))
+#define GPIO_TO_PORT(n) (n / GPIO_NUM_PIN)
+#define GPIO_TO_INDEX(n) (n % GPIO_NUM_PIN)
+
+/* DMA driver defines */
+#define MXC_IDE_DMA_WATERMARK 32 /* DMA watermark level in bytes */
+#define MXC_IDE_DMA_BD_NR (512/3/4) /* Number of BDs per channel */
+
+#ifndef IS_MEM_DEVICE_NONSHARED
+/* all peripherals on MXC so far are below 0x80000000 but leave L2CC alone */
+#define IS_MEM_DEVICE_NONSHARED(x) ((x) < 0x80000000 && (x) != L2CC_BASE_ADDR)
#endif
+/*!
+ * DPTC GP and LP ID
+ */
+#define DPTC_GP_ID 0
+#define DPTC_LP_ID 1
-#ifndef CONFIG_MACH_MX27
-# define cpu_is_mx27() (0)
+#ifndef __ASSEMBLY__
+#include <linux/types.h>
+
+struct cpu_wp {
+ u32 pll_reg;
+ u32 pll_rate;
+ u32 cpu_rate;
+ u32 pdr0_reg;
+ u32 pdf;
+ u32 mfi;
+ u32 mfd;
+ u32 mfn;
+ u32 cpu_voltage;
+ u32 cpu_podf;
+};
+
+#ifndef CONFIG_ARCH_MX51
+struct cpu_wp *get_cpu_wp(int *wp);
#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)
+enum mxc_cpu_pwr_mode {
+ WAIT_CLOCKED, /* wfi only */
+ WAIT_UNCLOCKED, /* WAIT */
+ WAIT_UNCLOCKED_POWER_OFF, /* WAIT + SRPG */
+ STOP_POWER_ON, /* just STOP */
+ STOP_POWER_OFF, /* STOP + SRPG */
+};
+
+void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode);
+int tzic_enable_wake(int is_idle);
+void gpio_activate_audio_ports(void);
+void gpio_inactivate_audio_ports(void);
+void gpio_activate_bt_audio_port(void);
+void gpio_inactivate_bt_audio_port(void);
+void gpio_activate_esai_ports(void);
+void gpio_deactivate_esai_ports(void);
+
#endif
-#endif /* __ASM_ARCH_MXC_H__ */
+#endif /* __ASM_ARCH_MXC_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mxc_dptc.h b/arch/arm/plat-mxc/include/mach/mxc_dptc.h
new file mode 100644
index 000000000000..b42fc36af3e1
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_dptc.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup DPTC Dynamic Process and Temperatur Compensation (DPTC) Driver
+ */
+
+/*!
+ * @file arch-mxc/mxc_dptc.h
+ *
+ * @brief This file contains the DPTC configuration structure definition.
+ *
+ *
+ * @ingroup DPTC
+ */
+
+#ifndef __ASM_ARCH_MXC_DPTC_H__
+#define __ASM_ARCH_MXC_DPTC_H__
+
+#ifdef __KERNEL__
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+
+#define DPTC_WP_SUPPORTED 17
+#define DPTC_GP_WP_SUPPORTED 7
+#define DPTC_LP_WP_SUPPORTED 9
+
+struct dptc_wp {
+ u32 dcvr0;
+ u32 dcvr1;
+ u32 dcvr2;
+ u32 dcvr3;
+ u32 voltage;
+};
+
+/*!
+ * This structure is used to define the dptc controller's platform
+ * data. It includes the regulator name string and DPTC clock name string.
+ */
+struct mxc_dptc_data {
+ /** Regulator name string */
+ char *reg_id;
+ /* DPTC clock name string */
+ char *clk_id;
+ /* Control reg address */
+ unsigned int dptccr_reg_addr;
+ /* Comparator value reg 0 address */
+ unsigned int dcvr0_reg_addr;
+ /* GPC control reg address */
+ unsigned int gpc_cntr_reg_addr;
+ /* DPTC interrupt status bit */
+ unsigned int dptccr;
+ /* The number of DPTC working points */
+ unsigned int dptc_wp_supported;
+ /* Maximum value of DPTC clock rate */
+ unsigned long clk_max_val;
+ /* DPTC working points */
+ struct dptc_wp *dptc_wp_allfreq;
+ /* DPTC enable bit */
+ u32 dptc_enable_bit;
+ /* DPTC ADU bit */
+ int gpc_adu;
+ /* VAI mask */
+ u32 vai_mask;
+ /* VAI offset */
+ int vai_offset;
+ /* Mask DPTC interrupt */
+ u32 irq_mask;
+ /* DPTC no voltage change request bit */
+ u32 dptc_nvcr_bit;
+ /* ARM interrrupt bit */
+ u32 gpc_irq_bit;
+ /* dptc init config */
+ u32 init_config;
+ /* dptc enable config */
+ u32 enable_config;
+ /* dptc counting range mask */
+ u32 dcr_mask;
+};
+
+/*!
+ * This function is called to put the DPTC in a low power state.
+ *
+ * @param id The DPTC device id. DPTC_GP_ID is for DPTC GP;
+ * DPTC_LP_ID is for DPTC LP
+ */
+void dptc_suspend(int id);
+/*!
+ * This function is called to resume the DPTC from a low power state.
+ *
+ * @param id The DPTC device id. DPTC_GP_ID is for DPTC GP;
+ * DPTC_LP_ID is for DPTC LP
+ */
+void dptc_resume(int id);
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_ARCH_MXC_DPTC_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mxc_dvfs.h b/arch/arm/plat-mxc/include/mach/mxc_dvfs.h
new file mode 100644
index 000000000000..3a5fd7f3efa2
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_dvfs.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup DVFS Dynamic Voltage and Frequency Scaling (DVFS) Driver
+ */
+
+/*!
+ * @file arch-mxc/mxc_dvfs.h
+ *
+ * @brief This file contains the DVFS configuration structure definition.
+ *
+ *
+ * @ingroup DVFS
+ */
+
+#ifndef __ASM_ARCH_MXC_DVFS_H__
+#define __ASM_ARCH_MXC_DVFS_H__
+
+#ifdef __KERNEL__
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+
+#define MXC_GPCCNTR_GPCIRQ2M (1 << 25)
+#define MXC_GPCCNTR_GPCIRQ2 (1 << 24)
+#define MXC_GPCCNTR_GPCIRQM (1 << 21)
+#define MXC_GPCCNTR_GPCIRQ_ARM (1 << 20)
+#define MXC_GPCCNTR_GPCIRQ_SDMA (0 << 20)
+#define MXC_GPCCNTR_DVFS0CR (1 << 16)
+#define MXC_GPCCNTR_ADU_MASK 0x8000
+#define MXC_GPCCNTR_ADU (1 << 15)
+#define MXC_GPCCNTR_STRT (1 << 14)
+#define MXC_GPCCNTR_FUPD_MASK 0x2000
+#define MXC_GPCCNTR_FUPD (1 << 13)
+#define MXC_GPCCNTR_HTRI_MASK 0x0000000F
+#define MXC_GPCCNTR_HTRI_OFFSET 0
+
+#define MXC_GPCVCR_VINC_MASK 0x00020000
+#define MXC_GPCVCR_VINC_OFFSET 17
+#define MXC_GPCVCR_VCNTU_MASK 0x00010000
+#define MXC_GPCVCR_VCNTU_OFFSET 16
+#define MXC_GPCVCR_VCNT_MASK 0x00007FFF
+#define MXC_GPCVCR_VCNT_OFFSET 0
+
+/* DVFS-PER */
+#define MXC_DVFSPER_PMCR0_UDCS (1 << 27)
+#define MXC_DVFSPER_PMCR0_UDCS_MASK 0x8000000
+#define MXC_DVFSPER_PMCR0_ENABLE_MASK 0x10
+#define MXC_DVFSPER_PMCR0_ENABLE (1 << 4)
+
+/*
+ * DVFS structure
+ */
+struct dvfs_wp {
+ int upthr;
+ int downthr;
+ int panicthr;
+ int upcnt;
+ int downcnt;
+ int emac;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_ARCH_MXC_DVFS_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mxc_edid.h b/arch/arm/plat-mxc/include/mach/mxc_edid.h
new file mode 100644
index 000000000000..aac49ad93ec5
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_edid.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxc_edid.h
+ *
+ * @brief MXC EDID tools
+ *
+ * @ingroup Framebuffer
+ */
+
+#ifndef MXC_EDID_H
+#define MXC_EDID_H
+
+int read_edid(struct i2c_adapter *adp,
+ struct fb_var_screeninfo *einfo,
+ int *dvi);
+
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/mxc_gpc.h b/arch/arm/plat-mxc/include/mach/mxc_gpc.h
new file mode 100644
index 000000000000..d3b3ae2d1b13
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_gpc.h
@@ -0,0 +1,74 @@
+
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup LPMD Low-Level Power Management Driver
+ */
+
+/*!
+ * @file arch-mxc/mxc_gpc.h
+ *
+ * @brief This file contains the chip level configuration details and
+ * public API declarations for GPC module
+ *
+ * @ingroup LPMD
+ */
+
+#ifndef __ASM_ARCH_MXC_GPC_H__
+#define __ASM_ARCH_MXC_GPC_H__
+
+/* AP Power Gating modules */
+typedef enum {
+ POWER_GATING_MODULE_AP_EMBEDDED_MEM_DEEPSLEEP,
+ POWER_GATING_MODULE_DISPLAY_BUFFER,
+ POWER_GATING_MODULE_EMI_DEEPSLEEP,
+ POWER_GATING_MODULE_IPU_STOP,
+ POWER_GATING_MODULE_L2_MEM_STOP,
+ POWER_GATING_MODULE_ARM_PLATFORM_STOP,
+} mxc_pm_ap_power_gating_modules_t;
+
+/* AP Power Gating pull-down config of modules */
+typedef enum {
+ POWER_GATING_PULL_DOWN_DISPLAY_BUFFER,
+ POWER_GATING_PULL_DOWN_EMI,
+ POWER_GATING_PULL_DOWN_IPU,
+ POWER_GATING_PULL_DOWN_L2_MEM,
+ POWER_GATING_PULL_DOWN_ARMPLATFORM,
+} mxc_pm_ap_power_gating_pulldown_t;
+
+/*!
+ * This function enables/disables the AP power gating by writing the APPCR
+ * register of the GPC module.
+ *
+ * @param enable Enable/Disable module power down
+ * 0 - disable; 1 - enable
+ * @param modules The desired module to be power gated
+ *
+ */
+void mxc_gpc_powergate_module(int enable,
+ mxc_pm_ap_power_gating_modules_t module);
+
+/*!
+ * This function enables/disables the AP power gating pull down selection of a
+ * module by writing the APPCR register of the GPC module.
+ *
+ * @param enable Enable/Disable module pull down
+ * 0 - disable; 1 - enable
+ * @param modules The desired module to be pulled down
+ *
+ */
+void mxc_gpc_powergate_pulldown(int enable,
+ mxc_pm_ap_power_gating_pulldown_t pulldown);
+
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/mxc_pm.h b/arch/arm/plat-mxc/include/mach/mxc_pm.h
new file mode 100644
index 000000000000..0cd865f2777c
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_pm.h
@@ -0,0 +1,252 @@
+
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup LPMD Low-Level Power Management Driver
+ */
+
+/*!
+ * @file arch-mxc/mxc_pm.h
+ *
+ * @brief This file contains the chip level configuration details and
+ * public API declarations for CRM_AP module
+ *
+ * @ingroup LPMD
+ */
+
+#ifndef __ASM_ARCH_MXC_PM_H__
+#define __ASM_ARCH_MXC_PM_H__
+
+#define WAIT_MODE 111
+#define DOZE_MODE 112
+#define STOP_MODE 113
+#define DSM_MODE 114
+/*
+ * MXC91231 Break-Point Frequency below which is low frequency and
+ * above which is high frequency
+ */
+#define BREAKPT_FREQ ((long)(400000000))
+
+#define GATE_STOP_WAIT 9
+#define GATE_STOP 10
+
+/*
+ * Used for MHz conversion
+ */
+#define MEGA_HERTZ 1000000
+
+/*
+ * If invalid frequency value other than the following
+ * CORE_133 - ARM desired to run @133MHz, LoV (1.2V)
+ * CORE_266 - ARM desired to run @266MHz, LoV (1.2V)
+ * CORE_399 - ARM desired to run @399MHz, LoV (1.2V)
+ * CORE_532 - ARM desired to run @133MHz, HiV (1.6V)
+ * are passed then this error is returned,
+ */
+#define ERR_FREQ_INVALID 1
+
+/*
+ * For MXC91231 Pass1, Integer DVFS greater than 133MHz is not allowed
+ * due to the hardware issue
+ */
+#define INTEGER_DVFS_NOT_ALLOW 1
+
+/*
+ * If PLL freq is less than desired ARM frequency during Integer
+ * DVFS, then return this error
+ */
+#define PLL_LESS_ARM_ERR 2
+
+/*
+ * Frequency change within the same-lo voltage is not approved.
+ * Inorder to do Integer DFS, move to the high voltage range and
+ * then set LFDF and move to the low voltage range
+ */
+#define INT_DFS_LOW_NOT_ALLOW 3
+
+/*
+ * If the desired AHB or IPG exceeds 133MHz or 66.5MHz respectively,
+ * then return this error
+ */
+#define AHB_IPG_EXCEED_LIMIT 4
+
+/*
+ * If the desired ARM frequency is too low to get by PLL scaling
+ * and the mxc_pm_pllscale API is called, return this error:
+ */
+#define PLL_DVFS_FREQ_TOO_LOW 5
+
+/*
+ * Invalid frequencies requested
+ */
+#define MXC_PM_INVALID_PARAM 6
+
+/*
+ * If AHB and/or IPG frequencies are greater than maximum allowed
+ */
+#define FREQ_OUT_OF_RANGE 2
+
+/*
+ * If AHB and/or IPG frequencies are other than 100 or 50Mhz
+ */
+#define BUS_FREQ_INVALID 2
+
+/*
+ * If MAX_PDF is greater than max value (8) then return this error
+ */
+#define AHB_MAX_DIV_ERR 3
+
+/*
+ * If IPG_PDF is greater than max value (2) then return this error
+ */
+#define IPG_MAX_DIV_ERR 4
+
+/*
+ * If ARM freq is out of range i.e., less than 133 or greater than
+ * 399 then return this error
+ */
+#define INVALID_ARM_FREQ 5
+
+/*
+ * This file includes all platform APIs. Some of the APIs are not
+ * appicable to some platforms. So, this error is used to indicate
+ * that a particular API is not available
+ */
+#define MXC_PM_API_NOT_SUPPORTED 6
+
+/*
+ * Error when frequency scaling is attempted while switch between MPLL and
+ * TPLL is in progress on MXC91321
+ */
+#define ERR_DFSP_SWITCH 2
+
+/*!
+ * Additional define for stop mode
+ */
+#define PM_SUSPEND_STOP ((__force suspend_state_t) 2)
+
+/*!
+ * CKOH pins configuration
+ */
+#define CKOH_AP_SEL 1
+#define CKOH_AHB_SEL 2
+#define CKOH_IP_SEL 3
+
+/*!
+ * Defines for Stop and DSM mode acknowledgements
+ */
+#define MXC_PM_LOWPWR_ACK_SDMA 0x01
+#define MXC_PM_LOWPWR_ACK_IPU 0x02
+#define MXC_PM_LOWPWR_ACK_MAX 0x04
+#define MXC_PM_LOWPWR_ACK_MQSPI 0x08
+#define MXC_PM_LOWPWR_ACK_USB 0x10
+#define MXC_PM_LOWPWR_ACK_RTIC 0x20
+
+/*
+ * PMIC configuration
+ */
+#define MXC_PMIC_1_2_VOLT 0xC
+#define MXC_PMIC_1_6_VOLT 0x1C
+#define MXC_PMIC_1_0_VOLT 0x4
+#if defined(CONFIG_ARCH_MXC91321) || defined(CONFIG_ARCH_MXC91231)
+#define MXC_PMIC_DVS_SPEED 0x1
+#else
+#define MXC_PMIC_DVS_SPEED 0x3
+#endif
+
+/*!
+ * Implementing Level 1 CRM Gate Control. Level 2 gate control
+ * is provided at module level using LPMD registers
+ *
+ * @param group The desired clock gate control register bits.
+ * Possible values are 0 through 6
+ * @param opt The desired option requesting clock to run during stop
+ * and wait modes or just during the stop mode. Possible
+ * values are GATE_STOP_WAIT and GATE_STOP.
+ *
+ */
+void mxc_pm_clockgate(int group, int opt);
+
+/*!
+ * Implementing steps required to transition to low-power modes
+ *
+ * @param mode The desired low-power mode. Possible values are,
+ * WAIT_MODE, STOP_MODE or DSM_MODE
+ *
+ */
+void mxc_pm_lowpower(int mode);
+
+/*!
+ * Enables acknowledgement from module when entering stop or DSM mode.
+ *
+ * @param ack The desired module acknowledgement to enable.
+ *
+ */
+void mxc_pm_lp_ack_enable(int ack);
+
+/*!
+ * Disables acknowledgement from module when entering stop or DSM mode.
+ *
+ * @param ack The desired module acknowledgement to disable.
+ *
+ */
+void mxc_pm_lp_ack_disable(int ack);
+
+/*!
+ * Implementing steps required to set Integer Scaling
+ *
+ * @param armfreq The desired ARM frequency. AHB and IP
+ * frequency are changed depending on ARM
+ * frequency and the divider values.
+ * @param ahbfreq The desired AHB frequency
+ * @param ipfreq The desired IP frequency
+ *
+ * @return Returns 0 on success or
+ * Returns -PLL_LESS_ARM_ERR if pllfreq is less than
+ * desired core freq
+ */
+int mxc_pm_intscale(long armfreq, long ahbfreq, long ipfreq);
+
+/*!
+ * To calculate MFI, MFN, MFD values. Using this the output frequency
+ * whose value is calculated using,
+ * 2 * REF_FREQ * (MF / PDF), where
+ * REF_FREQ is 26 Mhz
+ * MF = MFI + (MFN + MFD)
+ * PDF is assumed to be 1
+ *
+ * @param armfreq The desired ARM frequency
+ * @param ahbfreq The desired AHB frequency
+ * @param ipfreq The desired IP frequency
+ *
+ * @return Returns 0 on success or
+ * Returns -1 on error
+ */
+int mxc_pm_pllscale(long armfreq, long ahbfreq, long ipfreq);
+
+/*!
+ * To change AP core frequency and/or voltage suitably
+ *
+ * @param armfreq The desired ARM frequency
+ * @param ahbfreq The desired AHB frequency
+ * @param ipfreq The desired IP frequency
+ *
+ * @return Returns -ERR_FREQ_INVALID on failure
+ * Returns 0 on success
+ */
+int mxc_pm_dvfs(unsigned long armfreq, long ahbfreq, long ipfreq);
+
+extern void mxc_pm_arch_entry(u32 entry, u32 size);
+
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/mxc_scc.h b/arch/arm/plat-mxc/include/mach/mxc_scc.h
new file mode 100644
index 000000000000..d23f6c3e9913
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_scc.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file arch-mxc/mxc_scc.h
+ *
+ * @brief This is intended to be the file which contains all of code or changes
+ * needed to port the driver.
+ *
+ * @ingroup MXCSCC
+ */
+
+#ifndef __ASM_ARCH_MXC_SCC_H__
+#define __ASM_ARCH_MXC_SCC_H__
+
+#include <mach/hardware.h>
+
+/*!
+ * Expected to come from platform header files.
+ * This symbol must be the address of the SCC
+ */
+#define SCC_BASE SCC_BASE_ADDR
+
+/*!
+ * This must be the interrupt line number of the SCM interrupt.
+ */
+#define INT_SCC_SCM MXC_INT_SCC_SCM
+
+/*!
+ * if #USE_SMN_INTERRUPT is defined, this must be the interrupt line number of
+ * the SMN interrupt.
+ */
+#define INT_SCC_SMN MXC_INT_SCC_SMN
+
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/mxc_timer.h b/arch/arm/plat-mxc/include/mach/mxc_timer.h
index 130aebfbe168..59fe2dceeb07 100644
--- a/arch/arm/plat-mxc/include/mach/mxc_timer.h
+++ b/arch/arm/plat-mxc/include/mach/mxc_timer.h
@@ -65,9 +65,8 @@ static void gpt_irq_acknowledge(void)
{
__raw_writel(0, TIMER_BASE + MXC_TSTAT);
}
-#endif /* CONFIG_ARCH_IMX */
-#ifdef CONFIG_ARCH_MX2
+#elif defined(CONFIG_ARCH_MX2)
#define TIMER_BASE IO_ADDRESS(GPT1_BASE_ADDR)
#define TIMER_INTERRUPT MXC_INT_GPT1
@@ -103,15 +102,15 @@ static void gpt_irq_acknowledge(void)
{
__raw_writel(TSTAT_CAPT | TSTAT_COMP, TIMER_BASE + MXC_TSTAT);
}
-#endif /* CONFIG_ARCH_MX2 */
-#ifdef CONFIG_ARCH_MX3
+#else
#define TIMER_BASE IO_ADDRESS(GPT1_BASE_ADDR)
#define TIMER_INTERRUPT MXC_INT_GPT
#define MXC_TCTL 0x00
-#define TCTL_VAL (TCTL_CLK_IPG | TCTL_WAITEN)
+#define TCTL_VAL (TCTL_CLK_HIGH_FREQ | TCTL_WAITEN)
#define TCTL_CLK_IPG (1<<6)
+#define TCTL_CLK_HIGH_FREQ (2<<6)
#define TCTL_FRR (1<<9)
#define TCTL_WAITEN (1<<3)
diff --git a/arch/arm/plat-mxc/include/mach/mxc_uart.h b/arch/arm/plat-mxc/include/mach/mxc_uart.h
new file mode 100644
index 000000000000..d1db8023d724
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_uart.h
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup UART Universal Asynchronous Receiver Transmitter (UART) Driver
+ */
+
+/*!
+ * @file arch-mxc/mxc_uart.h
+ *
+ * @brief This file contains the UART configuration structure definition.
+ *
+ *
+ * @ingroup UART
+ */
+
+#ifndef __ASM_ARCH_MXC_UART_H__
+#define __ASM_ARCH_MXC_UART_H__
+
+#ifdef __KERNEL__
+
+#include <linux/serial_core.h>
+#include <mach/dma.h>
+
+/*
+ * The modes of the UART ports
+ */
+#define MODE_DTE 0
+#define MODE_DCE 1
+/*
+ * Is the UART configured to be a IR port
+ */
+#define IRDA 0
+#define NO_IRDA 1
+
+/*!
+ * This structure is used to store the the physical and virtual
+ * addresses of the UART DMA receive buffer.
+ */
+typedef struct {
+ /*!
+ * DMA Receive buffer virtual address
+ */
+ char *rx_buf;
+ /*!
+ * DMA Receive buffer physical address
+ */
+ dma_addr_t rx_handle;
+} mxc_uart_rxdmamap;
+
+/*!
+ * This structure is a way for the low level driver to define their own
+ * \b uart_port structure. This structure includes the core \b uart_port
+ * structure that is provided by Linux as an element and has other
+ * elements that are specifically required by this low-level driver.
+ */
+typedef struct {
+ /*!
+ * The port structure holds all the information about the UART
+ * port like base address, and so on.
+ */
+ struct uart_port port;
+ /*!
+ * Flag to determine if the interrupts are muxed.
+ */
+ int ints_muxed;
+ /*!
+ * Array that holds the receive and master interrupt numbers
+ * when the interrupts are not muxed.
+ */
+ int irqs[2];
+ /*!
+ * Flag to determine the DTE/DCE mode.
+ */
+ int mode;
+ /*!
+ * Flag to hold the IR mode of the port.
+ */
+ int ir_mode;
+ /*!
+ * Flag to enable/disable the UART port.
+ */
+ int enabled;
+ /*!
+ * Flag to indicate if we wish to use hardware-driven hardware
+ * flow control.
+ */
+ int hardware_flow;
+ /*!
+ * Holds the threshold value at which the CTS line is deasserted in
+ * case we use hardware-driven hardware flow control.
+ */
+ unsigned int cts_threshold;
+ /*!
+ * Flag to enable/disable DMA data transfer.
+ */
+ int dma_enabled;
+ /*!
+ * Holds the DMA receive buffer size.
+ */
+ int dma_rxbuf_size;
+ /*!
+ * DMA Receive buffers information
+ */
+ mxc_uart_rxdmamap *rx_dmamap;
+ /*!
+ * DMA RX buffer id
+ */
+ int dma_rxbuf_id;
+ /*!
+ * DMA Transmit buffer virtual address
+ */
+ char *tx_buf;
+ /*!
+ * DMA Transmit buffer physical address
+ */
+ dma_addr_t tx_handle;
+ /*!
+ * Holds the RxFIFO threshold value.
+ */
+ unsigned int rx_threshold;
+ /*!
+ * Holds the TxFIFO threshold value.
+ */
+ unsigned int tx_threshold;
+ /*!
+ * Information whether this is a shared UART
+ */
+ unsigned int shared;
+ /*!
+ * Clock id for UART clock
+ */
+ struct clk *clk;
+ /*!
+ * Information whether RXDMUXSEL must be set or not for IR port
+ */
+ int rxd_mux;
+ int ir_tx_inv;
+ int ir_rx_inv;
+ /*!
+ * DMA ID for transmit
+ */
+ mxc_dma_device_t dma_tx_id;
+ /*!
+ * DMA ID for receive
+ */
+ mxc_dma_device_t dma_rx_id;
+} uart_mxc_port;
+
+/* Address offsets of the UART registers */
+#define MXC_UARTURXD 0x000 /* Receive reg */
+#define MXC_UARTUTXD 0x040 /* Transmitter reg */
+#define MXC_UARTUCR1 0x080 /* Control reg 1 */
+#define MXC_UARTUCR2 0x084 /* Control reg 2 */
+#define MXC_UARTUCR3 0x088 /* Control reg 3 */
+#define MXC_UARTUCR4 0x08C /* Control reg 4 */
+#define MXC_UARTUFCR 0x090 /* FIFO control reg */
+#define MXC_UARTUSR1 0x094 /* Status reg 1 */
+#define MXC_UARTUSR2 0x098 /* Status reg 2 */
+#define MXC_UARTUESC 0x09C /* Escape character reg */
+#define MXC_UARTUTIM 0x0A0 /* Escape timer reg */
+#define MXC_UARTUBIR 0x0A4 /* BRM incremental reg */
+#define MXC_UARTUBMR 0x0A8 /* BRM modulator reg */
+#define MXC_UARTUBRC 0x0AC /* Baud rate count reg */
+#define MXC_UARTONEMS 0x0B0 /* One millisecond reg */
+#define MXC_UARTUTS 0x0B4 /* Test reg */
+
+/* Bit definations of UCR1 */
+#define MXC_UARTUCR1_ADEN 0x8000
+#define MXC_UARTUCR1_ADBR 0x4000
+#define MXC_UARTUCR1_TRDYEN 0x2000
+#define MXC_UARTUCR1_IDEN 0x1000
+#define MXC_UARTUCR1_RRDYEN 0x0200
+#define MXC_UARTUCR1_RXDMAEN 0x0100
+#define MXC_UARTUCR1_IREN 0x0080
+#define MXC_UARTUCR1_TXMPTYEN 0x0040
+#define MXC_UARTUCR1_RTSDEN 0x0020
+#define MXC_UARTUCR1_SNDBRK 0x0010
+#define MXC_UARTUCR1_TXDMAEN 0x0008
+#define MXC_UARTUCR1_ATDMAEN 0x0004
+#define MXC_UARTUCR1_DOZE 0x0002
+#define MXC_UARTUCR1_UARTEN 0x0001
+
+/* Bit definations of UCR2 */
+#define MXC_UARTUCR2_ESCI 0x8000
+#define MXC_UARTUCR2_IRTS 0x4000
+#define MXC_UARTUCR2_CTSC 0x2000
+#define MXC_UARTUCR2_CTS 0x1000
+#define MXC_UARTUCR2_PREN 0x0100
+#define MXC_UARTUCR2_PROE 0x0080
+#define MXC_UARTUCR2_STPB 0x0040
+#define MXC_UARTUCR2_WS 0x0020
+#define MXC_UARTUCR2_RTSEN 0x0010
+#define MXC_UARTUCR2_ATEN 0x0008
+#define MXC_UARTUCR2_TXEN 0x0004
+#define MXC_UARTUCR2_RXEN 0x0002
+#define MXC_UARTUCR2_SRST 0x0001
+
+/* Bit definations of UCR3 */
+#define MXC_UARTUCR3_DTREN 0x2000
+#define MXC_UARTUCR3_PARERREN 0x1000
+#define MXC_UARTUCR3_FRAERREN 0x0800
+#define MXC_UARTUCR3_DSR 0x0400
+#define MXC_UARTUCR3_DCD 0x0200
+#define MXC_UARTUCR3_RI 0x0100
+#define MXC_UARTUCR3_RXDSEN 0x0040
+#define MXC_UARTUCR3_AWAKEN 0x0010
+#define MXC_UARTUCR3_DTRDEN 0x0008
+#define MXC_UARTUCR3_RXDMUXSEL 0x0004
+#define MXC_UARTUCR3_INVT 0x0002
+
+/* Bit definations of UCR4 */
+#define MXC_UARTUCR4_CTSTL_OFFSET 10
+#define MXC_UARTUCR4_CTSTL_MASK (0x3F << 10)
+#define MXC_UARTUCR4_INVR 0x0200
+#define MXC_UARTUCR4_ENIRI 0x0100
+#define MXC_UARTUCR4_REF16 0x0040
+#define MXC_UARTUCR4_IRSC 0x0020
+#define MXC_UARTUCR4_TCEN 0x0008
+#define MXC_UARTUCR4_OREN 0x0002
+#define MXC_UARTUCR4_DREN 0x0001
+
+/* Bit definations of UFCR */
+#define MXC_UARTUFCR_RFDIV 0x0200 /* Ref freq div is set to 2 */
+#define MXC_UARTUFCR_RFDIV_OFFSET 7
+#define MXC_UARTUFCR_RFDIV_MASK (0x7 << 7)
+#define MXC_UARTUFCR_TXTL_OFFSET 10
+#define MXC_UARTUFCR_DCEDTE 0x0040
+
+/* Bit definations of URXD */
+#define MXC_UARTURXD_ERR 0x4000
+#define MXC_UARTURXD_OVRRUN 0x2000
+#define MXC_UARTURXD_FRMERR 0x1000
+#define MXC_UARTURXD_BRK 0x0800
+#define MXC_UARTURXD_PRERR 0x0400
+
+/* Bit definations of USR1 */
+#define MXC_UARTUSR1_PARITYERR 0x8000
+#define MXC_UARTUSR1_RTSS 0x4000
+#define MXC_UARTUSR1_TRDY 0x2000
+#define MXC_UARTUSR1_RTSD 0x1000
+#define MXC_UARTUSR1_FRAMERR 0x0400
+#define MXC_UARTUSR1_RRDY 0x0200
+#define MXC_UARTUSR1_AGTIM 0x0100
+#define MXC_UARTUSR1_DTRD 0x0080
+#define MXC_UARTUSR1_AWAKE 0x0010
+
+/* Bit definations of USR2 */
+#define MXC_UARTUSR2_TXFE 0x4000
+#define MXC_UARTUSR2_IDLE 0x1000
+#define MXC_UARTUSR2_RIDELT 0x0400
+#define MXC_UARTUSR2_RIIN 0x0200
+#define MXC_UARTUSR2_DCDDELT 0x0040
+#define MXC_UARTUSR2_DCDIN 0x0020
+#define MXC_UARTUSR2_TXDC 0x0008
+#define MXC_UARTUSR2_ORE 0x0002
+#define MXC_UARTUSR2_RDR 0x0001
+#define MXC_UARTUSR2_BRCD 0x0004
+
+/* Bit definations of UTS */
+#define MXC_UARTUTS_LOOP 0x1000
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_ARCH_MXC_UART_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mxc_vpu.h b/arch/arm/plat-mxc/include/mach/mxc_vpu.h
new file mode 100644
index 000000000000..8a42c7de5634
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_vpu.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+/*!
+ * @defgroup VPU Video Processor Unit Driver
+ */
+
+/*!
+ * @file arch-mxc/mxc_vpu.h
+ *
+ * @brief VPU system initialization and file operation definition
+ *
+ * @ingroup VPU
+ */
+
+#ifndef __ASM_ARCH_MXC_VPU_H__
+#define __ASM_ARCH_MXC_VPU_H__
+
+#include <linux/fs.h>
+
+struct vpu_mem_desc {
+ u32 size;
+ dma_addr_t phy_addr;
+ u32 cpu_addr; /* cpu address to free the dma mem */
+ u32 virt_uaddr; /* virtual user space address */
+};
+
+#define VPU_IOC_MAGIC 'V'
+
+#define VPU_IOC_PHYMEM_ALLOC _IO(VPU_IOC_MAGIC, 0)
+#define VPU_IOC_PHYMEM_FREE _IO(VPU_IOC_MAGIC, 1)
+#define VPU_IOC_WAIT4INT _IO(VPU_IOC_MAGIC, 2)
+#define VPU_IOC_PHYMEM_DUMP _IO(VPU_IOC_MAGIC, 3)
+#define VPU_IOC_REG_DUMP _IO(VPU_IOC_MAGIC, 4)
+#define VPU_IOC_VL2CC_FLUSH _IO(VPU_IOC_MAGIC, 5)
+#define VPU_IOC_IRAM_SETTING _IO(VPU_IOC_MAGIC, 6)
+#define VPU_IOC_CLKGATE_SETTING _IO(VPU_IOC_MAGIC, 7)
+#define VPU_IOC_GET_WORK_ADDR _IO(VPU_IOC_MAGIC, 8)
+#define VPU_IOC_GET_PIC_PARA_ADDR _IO(VPU_IOC_MAGIC, 9)
+#define VPU_IOC_GET_USER_DATA_ADDR _IO(VPU_IOC_MAGIC, 10)
+#define VPU_IOC_SYS_SW_RESET _IO(VPU_IOC_MAGIC, 11)
+#define VPU_IOC_GET_SHARE_MEM _IO(VPU_IOC_MAGIC, 12)
+
+#define BIT_CODE_RUN 0x000
+#define BIT_CODE_DOWN 0x004
+#define BIT_INT_CLEAR 0x00C
+#define BIT_INT_STATUS 0x010
+
+#define BIT_WORK_CTRL_BUF_BASE 0x100
+#define BIT_WORK_CTRL_BUF_REG(i) (BIT_WORK_CTRL_BUF_BASE + i * 4)
+#define BIT_CODE_BUF_ADDR BIT_WORK_CTRL_BUF_REG(0)
+#define BIT_WORK_BUF_ADDR BIT_WORK_CTRL_BUF_REG(1)
+#define BIT_PARA_BUF_ADDR BIT_WORK_CTRL_BUF_REG(2)
+#define BIT_BIT_STREAM_CTRL BIT_WORK_CTRL_BUF_REG(3)
+#define BIT_FRAME_MEM_CTRL BIT_WORK_CTRL_BUF_REG(4)
+#define BIT_BIT_STREAM_PARAM BIT_WORK_CTRL_BUF_REG(5)
+
+#define BIT_RESET_CTRL 0x11C
+
+/* i could be 0, 1, 2, 3 */
+#define BIT_RD_PTR_BASE 0x120
+#define BIT_RD_PTR_REG(i) (BIT_RD_PTR_BASE + i * 8)
+#define BIT_WR_PTR_REG(i) (BIT_RD_PTR_BASE + i * 8 + 4)
+
+/* i could be 0, 1, 2, 3 */
+#define BIT_FRM_DIS_FLG_BASE (cpu_is_mx51() ? 0x150 : 0x140)
+#define BIT_FRM_DIS_FLG_REG(i) (BIT_FRM_DIS_FLG_BASE + i * 4)
+
+#define BIT_BUSY_FLAG 0x160
+#define BIT_RUN_COMMAND 0x164
+#define BIT_INT_ENABLE 0x170
+
+#define BITVAL_PIC_RUN 8
+
+#define VPU_SLEEP_REG_VALUE 10
+#define VPU_WAKE_REG_VALUE 11
+
+int vl2cc_init(u32 vl2cc_hw_base);
+void vl2cc_enable(void);
+void vl2cc_flush(void);
+void vl2cc_disable(void);
+void vl2cc_cleanup(void);
+
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/pcmcia.h b/arch/arm/plat-mxc/include/mach/pcmcia.h
new file mode 100644
index 000000000000..c50302bbbac5
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/pcmcia.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_PCMCIA_H__
+#define __ASM_ARCH_MXC_PCMCIA_H__
+
+#include <mach/hardware.h>
+
+#define WINDOW_SIZE 0x1000000 /* The size of a window: 16M */
+#define PCMCIA_WINDOWS 5 /* How many windows / socket */
+#define SOCKET_NO 1 /* How many sockets */
+
+#define ATTRIBUTE_MEMORY_WINDOW 0
+#define IO_WINDOW 1
+#define COMMON_MEMORY_WINDOW 2
+
+/*
+ * PCMCIA socket
+ */
+#define PCMCIAPrtSp WINDOW_SIZE /* PCMCIA window size */
+#define PCMCIASp (4*PCMCIAPrtSp) /* PCMCIA Space [byte] */
+#define PCMCIAIOSp PCMCIAPrtSp /* PCMCIA I/O Space [byte] */
+#define PCMCIAAttrSp PCMCIAPrtSp /* PCMCIA Attribute Space [byte] */
+#define PCMCIAMemSp PCMCIAPrtSp /* PCMCIA Memory Space [byte] */
+
+#define PCMCIA0Sp PCMCIASp /* PCMCIA 0 Space [byte] */
+#define PCMCIA0IOSp PCMCIAIOSp /* PCMCIA 0 I/O Space [byte] */
+#define PCMCIA0AttrSp PCMCIAAttrSp /* PCMCIA 0 Attribute Space [byte] */
+#define PCMCIA0MemSp PCMCIAMemSp /* PCMCIA 0 Memory Space [byte] */
+
+#define _PCMCIA(Nb) /* PCMCIA [0..1] */ \
+ (PCMCIA_MEM_BASE_ADDR + (Nb) * PCMCIASp)
+
+#define _PCMCIAAttr(Nb) _PCMCIA (Nb) /* PCMCIA I/O [0..1] */
+
+#define _PCMCIAIO(Nb) /* PCMCIA Attribute [0..1] */ \
+ (_PCMCIA (Nb) + (IO_WINDOW) * PCMCIAPrtSp)
+#define _PCMCIAMem(Nb) /* PCMCIA Memory [0..1] */ \
+ (_PCMCIA (Nb) + (COMMON_MEMORY_WINDOW) * PCMCIAPrtSp)
+
+#define _PCMCIA0 _PCMCIA (0) /* PCMCIA 0 */
+#define _PCMCIA0IO _PCMCIAIO (0) /* PCMCIA 0 I/O */
+#define _PCMCIA0Attr _PCMCIAAttr (0) /* PCMCIA 0 Attribute */
+#define _PCMCIA0Mem _PCMCIAMem (0) /* PCMCIA 0 Memory */
+
+/*
+ * Module: PCMCIA, Addr Range: 0xB8004000 - 0xB8004FFF, Size: 4 Kbyte
+ */
+#define PCMCIA_BASE_ADDR (PCMCIA_CTL_BASE_ADDR) /* PCMCIA Base Address */
+#define PCMCIA_IO_ADDR(x) (* (volatile u32 *)PCMCIA_IO_ADDRESS(x))
+
+#define _reg_PCMCIA_PIPR PCMCIA_IO_ADDR(PCMCIA_BASE_ADDR + 0x00) /* PCMCIA input pins register */
+#define _reg_PCMCIA_PSCR PCMCIA_IO_ADDR(PCMCIA_BASE_ADDR + 0x04) /* PCMCIA Status Changed Register */
+#define _reg_PCMCIA_PER PCMCIA_IO_ADDR(PCMCIA_BASE_ADDR + 0x08) /* PCMCIA Enable Register */
+
+/* win: 0-4 */
+#define _reg_PCMCIA_PBR(win) PCMCIA_IO_ADDR(PCMCIA_BASE_ADDR + 0x0C + 4 * (win)) /* PCMCIA Base Register x */
+#define _reg_PCMCIA_POR(win) PCMCIA_IO_ADDR(PCMCIA_BASE_ADDR + 0x28 + 4 * (win)) /* PCMCIA Option Register x */
+#define _reg_PCMCIA_POFR(win) PCMCIA_IO_ADDR(PCMCIA_BASE_ADDR + 0x44 + 4 * (win)) /* PCMCIA Offset Register x */
+
+#define _reg_PCMCIA_PGCR PCMCIA_IO_ADDR(PCMCIA_BASE_ADDR + 0x60) /* PCMCIA General Control Register */
+#define _reg_PCMCIA_PGSR PCMCIA_IO_ADDR(PCMCIA_BASE_ADDR + 0x64) /* PCMCIA General Status Register */
+
+/* PCMCIA_PIPR - PCMCIA Input Pins Register - fields */
+#define PCMCIA_PIPR_POWERON (1 << 8) /* card indicates "power on" */
+#define PCMCIA_PIPR_RDY (1 << 7) /* card is ready */
+#define PCMCIA_PIPR_BVD2 (1 << 6) /* battery voltage 2/SPKR in */
+#define PCMCIA_PIPR_BVD1 (1 << 5) /* battery voltage 1/STSCHG */
+#define PCMCIA_PIPR_CD (3 << 3) /* card detect 1 and 2 */
+#define PCMCIA_PIPR_WP (1 << 2) /* write protect switch enabled */
+#define PCMCIA_PIPR_VS (3 << 0) /* voltage sense bits */
+#define PCMCIA_PIPR_VS_5V (1 << 0) /* 5v */
+
+/* PCMCIA_PSCR - PCMCIA Status Change Register - fields */
+#define PCMCIA_PSCR_POWC (1 << 11) /* */
+#define PCMCIA_PSCR_RDYR (1 << 10) /* */
+#define PCMCIA_PSCR_RDYF (1 << 9) /* */
+#define PCMCIA_PSCR_RDYH (1 << 8) /* */
+#define PCMCIA_PSCR_RDYL (1 << 7) /* */
+#define PCMCIA_PSCR_BVDC2 (1 << 6) /* */
+#define PCMCIA_PSCR_BVDC1 (1 << 5) /* */
+#define PCMCIA_PSCR_CDC2 (1 << 4) /* */
+#define PCMCIA_PSCR_CDC1 (1 << 3) /* */
+#define PCMCIA_PSCR_WPC (1 << 2) /* */
+#define PCMCIA_PSCR_VSC2 (1 << 1) /* */
+#define PCMCIA_PSCR_VSC1 (1 << 0) /* */
+
+/* PCMCIA_PER - PCMCIA Enable Register - fields */
+#define PCMCIA_PER_ERRINTEN (1 << 12) /* error interrupt enable */
+#define PCMCIA_PER_POWERONEN (1 << 11) /* power on interrupt enable */
+#define PCMCIA_PER_RDYRE (1 << 10) /* RDY/nIREQ pin rising edge */
+#define PCMCIA_PER_RDYFE (1 << 9) /* RDY/nIREQ pin falling edge */
+#define PCMCIA_PER_RDYHE (1 << 8) /* RDY/nIREQ pin high */
+#define PCMCIA_PER_RDYLE (1 << 7) /* RDY/nIREQ pin low */
+#define PCMCIA_PER_BVDE2 (1 << 6) /* battery voltage 2/SPKR in */
+#define PCMCIA_PER_BVDE1 (1 << 5) /* battery voltage 1/STSCHG */
+#define PCMCIA_PER_CDE2 (1 << 4) /* card detect 2 */
+#define PCMCIA_PER_CDE1 (1 << 3) /* card detect 1 */
+#define PCMCIA_PER_WPE (1 << 2) /* write protect */
+#define PCMCIA_PER_VSE2 (1 << 1) /* voltage sense 2 */
+#define PCMCIA_PER_VSE1 (1 << 0) /* voltage sense 1 */
+
+/* PCMCIA_POR[0-4] - PCMCIA Option Registers 0-4 - fields */
+#define PCMCIA_POR_PV (1 << 29) /* set iff bank is valid */
+#define PCMCIA_POR_WPEN (1 << 28) /* write protect (WP) input signal is enabled */
+#define PCMCIA_POR_WP (1 << 27) /* write protected */
+
+#define PCMCIA_POR_PRS_SHIFT (25)
+#define PCMCIA_POR_PRS(x) (((x) & 0x3) << PCMCIA_POR_PRS_SHIFT )
+#define PCMCIA_POR_PRS_MASK PCMCIA_POR_PRS(3) /* PCMCIA region select */
+#define PCMCIA_POR_PRS_COMMON (0) /* values of POR_PRS field */
+#define PCMCIA_POR_PRS_TRUE_IDE (1)
+#define PCMCIA_POR_PRS_ATTRIBUTE (2)
+#define PCMCIA_POR_PRS_IO (3)
+
+#define PCMCIA_POR_PPS_8 (1 << 24) /* PCMCIA Port size = 8bits */
+#define PCMCIA_POR_PPS_16 (0 << 24) /* PCMCIA Port size = 16bits */
+
+#define PCMCIA_POR_PSL_SHIFT (17) /* strobe length */
+#define PCMCIA_POR_PSL(x) (((x) & 0x7F) << PCMCIA_POR_PSL_SHIFT)
+#define PCMCIA_POR_PSL_MASK PCMCIA_POR_PSL(0x7f)
+
+#define PCMCIA_POR_PSST_SHIFT (11) /* strobe setup time */
+#define PCMCIA_POR_PSST(x) (((x) & 0x3F) << PCMCIA_POR_PSST_SHIFT)
+#define PCMCIA_POR_PSST_MASK PCMCIA_POR_PSST(0x3f)
+
+#define PCMCIA_POR_PSHT_SHIFT (5) /* strobe hold time */
+#define PCMCIA_POR_PSHT(x) (((x) & 0x3F) << PCMCIA_POR_PSHT_SHIFT)
+#define PCMCIA_POR_PSHT_MASK PCMCIA_POR_PSHT(0x3f)
+
+#define PCMCIA_POR_BSIZE_SHIFT (0) /* bank size */
+#define PCMCIA_POR_BSIZE(x) (((x) & 0x1F) << PCMCIA_POR_BSIZE_SHIFT)
+#define PCMCIA_POR_BSIZE_MASK PCMCIA_POR_BSIZE(0x1F)
+
+/* some handy BSIZE values */
+#define POR_BSIZE_1 PCMCIA_POR_BSIZE(0x00)
+#define POR_BSIZE_2 PCMCIA_POR_BSIZE(0x01)
+#define POR_BSIZE_4 PCMCIA_POR_BSIZE(0x03)
+#define POR_BSIZE_8 PCMCIA_POR_BSIZE(0x02)
+#define POR_BSIZE_16 PCMCIA_POR_BSIZE(0x06)
+#define POR_BSIZE_32 PCMCIA_POR_BSIZE(0x07)
+#define POR_BSIZE_64 PCMCIA_POR_BSIZE(0x05)
+#define POR_BSIZE_128 PCMCIA_POR_BSIZE(0x04)
+#define POR_BSIZE_256 PCMCIA_POR_BSIZE(0x0C)
+#define POR_BSIZE_512 PCMCIA_POR_BSIZE(0x0D)
+#define POR_BSIZE_1K PCMCIA_POR_BSIZE(0x0F)
+#define POR_BSIZE_2K PCMCIA_POR_BSIZE(0x0E)
+
+#define POR_BSIZE_4K PCMCIA_POR_BSIZE(0x0A)
+#define POR_BSIZE_8K PCMCIA_POR_BSIZE(0x0B)
+#define POR_BSIZE_16K PCMCIA_POR_BSIZE(0x09)
+#define POR_BSIZE_32K PCMCIA_POR_BSIZE(0x08)
+#define POR_BSIZE_64K PCMCIA_POR_BSIZE(0x18)
+#define POR_BSIZE_128K PCMCIA_POR_BSIZE(0x19)
+#define POR_BSIZE_256K PCMCIA_POR_BSIZE(0x1B)
+#define POR_BSIZE_512K PCMCIA_POR_BSIZE(0x1A)
+#define POR_BSIZE_1M PCMCIA_POR_BSIZE(0x1E)
+#define POR_BSIZE_2M PCMCIA_POR_BSIZE(0x1F)
+#define POR_BSIZE_4M PCMCIA_POR_BSIZE(0x1D)
+#define POR_BSIZE_8M PCMCIA_POR_BSIZE(0x1C)
+#define POR_BSIZE_16M PCMCIA_POR_BSIZE(0x14)
+#define POR_BSIZE_32M PCMCIA_POR_BSIZE(0x15)
+#define POR_BSIZE_64M PCMCIA_POR_BSIZE(0x17)
+
+/* Window size */
+#define POR_1 0x1
+#define POR_2 0x2
+#define POR_4 0x4
+#define POR_8 0x8
+#define POR_16 0x10
+#define POR_32 0x20
+#define POR_64 0x40
+#define POR_128 0x80
+#define POR_256 0x100
+#define POR_512 0x200
+
+#define POR_1K 0x400
+#define POR_2K 0x800
+#define POR_4K 0x1000
+#define POR_8K 0x2000
+#define POR_16K 0x4000
+#define POR_32K 0x8000
+#define POR_64K 0x10000
+#define POR_128K 0x20000
+#define POR_256K 0x40000
+#define POR_512K 0x80000
+
+#define POR_1M 0x100000
+#define POR_2M 0x200000
+#define POR_4M 0x400000
+#define POR_8M 0x800000
+#define POR_16M 0x1000000
+#define POR_32M 0x2000000
+#define POR_64M 0x4000000
+
+/* PCMCIA_PGCR - PCMCIA General Control Register - fields */
+#define PCMCIA_PGCR_LPMEN (1 << 3) /* Low power Mode Enable */
+#define PCMCIA_PGCR_SPKREN (1 << 2) /* SPKROUT routing enable */
+#define PCMCIA_PGCR_POE (1 << 1) /* Controller out enable */
+#define PCMCIA_PGCR_RESET (1 << 0) /* Card reset */
+
+/* PCMCIA_PGSR - PCMCIA General Status Register - fields */
+#define PCMCIA_PGSR_NWINE (1 << 4) /* No Window error */
+#define PCMCIA_PGSR_LPE (1 << 3) /* Low Power error */
+#define PCMCIA_PGSR_SE (1 << 2) /* Size error */
+#define PCMCIA_PGSR_CDE (1 << 1) /* Card Detect error */
+#define PCMCIA_PGSR_WPE (1 << 0) /* Write Protect error */
+
+#endif /* __ASM_ARCH_MXC_PCMCIA_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/pmic_audio.h b/arch/arm/plat-mxc/include/mach/pmic_audio.h
new file mode 100644
index 000000000000..8ec3d2c642e4
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/pmic_audio.h
@@ -0,0 +1,2315 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_PMIC_AUDIO_H__
+#define __ASM_ARCH_MXC_PMIC_AUDIO_H__
+
+/*!
+ * @defgroup PMIC_AUDIO PMIC Audio Driver
+ * @ingroup PMIC_DRVRS
+ */
+
+/*!
+ * @file arch-mxc/pmic_audio.h
+ * @brief External definitions for the PMIC Audio Client driver.
+ *
+ * The PMIC Audio driver and this API were developed to support the
+ * audio playback, recording, and mixing capabilities of the power
+ * management ICs that are available from Freescale Semiconductor, Inc.
+ *
+ * The following table shows which audio-related capabilities are supported
+ * by each power management IC:
+ *
+ * @ingroup PMIC_AUDIO
+ */
+
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+/***************************************************************************
+ * TYPEDEFS AND ENUMERATIONS *
+ ***************************************************************************/
+
+/*!
+ * @name General Setup and Audio Device Access Typedefs and Enumerations
+ * Typedefs and enumerations that are used for initial access to the
+ * PMIC Audio hardware.
+ */
+/*@{*/
+
+/*!
+ * @typedef PMIC_AUDIO_HANDLE
+ * @brief Define typedef for a handle to the PMIC Audio hardware.
+ *
+ * Define a "handle" that is returned when the PMIC Audio hardware
+ * is opened. This handle grants exclusive access to the PMIC Audio
+ * hardware and must be used in all subsequent function calls. When access
+ * to the PMIC Audio hardware is no longer required, then a close
+ * operation must be done with this handle. The handle is no longer valid
+ * if the close operation was successful.
+ */
+typedef long *PMIC_AUDIO_HANDLE;
+
+/*!
+ * @enum PMIC_AUDIO_EVENTS
+ * @brief Identify the audio events that have been detected and should be
+ * handled.
+ *
+ * This enumeration defines all of the possible PMIC Audio events. Multiple
+ * events may be selected when defining a mask and multiple events may be
+ * signalled together.
+ *
+ * Note that the MICROPHONE_DETECT and MICROPHONE_REMOVED events may also be
+ * used to signal the operation of a serial or parallel microphone switch
+ * when used with a combined headset+microphone device. In that case the
+ * HEADSET_DETECT state must also be checked to determine if it's only the
+ * microphone switch being operated or whether the microphone has truly been
+ * inserted/removed (along with the headset).
+ */
+typedef enum {
+ HEADSET_DETECTED = 1, /*!< Detected headset insertion. */
+ HEADSET_STEREO = 2, /*!< Detected stereo headset device. */
+ HEADSET_MONO = 4, /*!< Detected mono headset device. */
+ HEADSET_THERMAL_SHUTDOWN = 8, /*!< Detected output amplifier
+ shutdown due to thermal
+ limits . */
+ HEADSET_SHORT_CIRCUIT = 16, /*!< Detected output amplifier
+ short circuit condition
+ . */
+ HEADSET_REMOVED = 32, /*!< Detected headset removal. */
+ MICROPHONE_DETECTED = 64, /*!< Detected microphone insertion. */
+ MICROPHONE_REMOVED = 128, /*!< Detected microphone removal. */
+ PTT_BUTTON_PRESS = 256, /*!< Detected PTT button down
+ . */
+ PTT_BUTTON_RANGE = 512, /*!< Detected PTT button within
+ voltage range
+ . */
+ PTT_SHORT_OR_INVALID = 1024 /*!< Detected PTT button outside
+ of voltage range or invalid
+ device . */
+} PMIC_AUDIO_EVENTS;
+
+/*!
+ * @typedef PMIC_AUDIO_CALLBACK
+ * @brief Typedef for PMIC Audio event notification callback function.
+ *
+ * Define a typedef for the PMIC Audio event notification callback
+ * function. The signalled events are passed to the function as the first
+ * argument. The callback function should then process whatever events it
+ * can and then return the set of unhandled events (if any).
+ */
+typedef PMIC_AUDIO_EVENTS(*PMIC_AUDIO_CALLBACK) (const PMIC_AUDIO_EVENTS event);
+
+typedef struct {
+ int hs_state;
+ int event_type;
+} PMIC_HS_STATE;
+
+/*!
+ * @enum PMIC_AUDIO_SOURCE
+ * @brief Select an audio signal processing component.
+ *
+ * This enumeration defines all of the possible PMIC audio signal handling
+ * components which can be acquired by calling pmic_audio_open().
+ *
+ * Note that the EXTERNAL_STEREO_IN selection is used to simply gain access
+ * to the stereo input pins. The stereo input signal can then be routed
+ * directly to the output amplifiers. In this case, no signal processing is
+ * done by either the Voice CODEC or the Stereo DAC.
+ */
+typedef enum {
+ STEREO_DAC, /*!< Open connection to Stereo DAC. */
+ VOICE_CODEC, /*!< Open connection to Voice CODEC. */
+ EXTERNAL_STEREO_IN /*!< Open connection to external stereo inputs. */
+} PMIC_AUDIO_SOURCE;
+
+/*@}*/
+
+/*!
+ * @name Data Bus Setup and Configuration Typedefs and Enumerations
+ * Typedefs and enumerations that are used to define and configure
+ * the data bus protocol in order to communicate with the Stereo DAC
+ * or the Voice CODEC.
+ */
+/*@{*/
+
+/*!
+ * @enum PMIC_AUDIO_DATA_BUS
+ * @brief Select the data bus used to transfer data between the host and
+ * Voice CODEC and/or the Stereo DAC.
+ *
+ * This enumeration defines all of the possible PMIC audio data buses that
+ * can be used to transfer data between the host and the Voice CODEC and/or
+ * the Stereo DAC on the PMIC.
+ *
+ * Note that the same data bus may be used to transfer audio data to/from
+ * the Voice CODEC and the Stereo DAC. However, in this case, the data bus
+ * must be configured for network mode with different timeslots assigned to
+ * the Voice CODEC and the Stereo DAC. Also, the sampling rates must be
+ * identical for both the Voice CODEC and the Stereo DAC in order to avoid
+ * a data bus timing conflict and audio signal distortion.
+ */
+typedef enum {
+ AUDIO_DATA_BUS_1, /*!< Use data bus 1 for audio data. */
+ AUDIO_DATA_BUS_2 /*!< Use data bus 2 for audio data. */
+} PMIC_AUDIO_DATA_BUS;
+
+/*!
+ * @enum PMIC_AUDIO_BUS_PROTOCOL
+ * @brief Select the data bus protocol to be used.
+ *
+ * This enumeration defines all of the possible PMIC audio data bus protocols
+ * that may be selected.
+ */
+typedef enum {
+ NORMAL_MSB_JUSTIFIED_MODE, /*!< Transmit and receive audio data
+ in normal MSB-justified mode. */
+ NETWORK_MODE, /*!< Transmit and receive audio data
+ in network mode. */
+ I2S_MODE, /*!< Transmit and receive audio data
+ in I2S mode. */
+ SPD_IF_MODE /*!< Transmit and receive audio data
+ in SPD/IF mode . */
+} PMIC_AUDIO_BUS_PROTOCOL;
+
+/*!
+ * @enum PMIC_AUDIO_BUS_MODE
+ * @brief Select the data bus mode to be used.
+ *
+ * This enumeration defines all of the possible PMIC audio data bus modes
+ * that may be selected. When configured in BUS_MASTER_MODE, the PMIC is
+ * responsible for supplying the data bus clock signals. Alternatively,
+ * when configured in BUS_SLAVE_MODE, the PMIC will use the data bus clock
+ * signals that are supplied by the bus master.
+ */
+typedef enum {
+ BUS_MASTER_MODE = 0, /*!< Operate as bus master. */
+ BUS_SLAVE_MODE = 1 /*!< Operate as bus slave. */
+} PMIC_AUDIO_BUS_MODE;
+
+/*!
+ * @enum PMIC_AUDIO_CLOCK_IN_SOURCE
+ * @brief Select the clock signal source when in bus master mode.
+ *
+ * This enumeration defines all of the possible PMIC audio clock signal
+ * sources that may be selected. One of these clock signal sources must
+ * be selected in order to use either the Voice CODEC or the Stereo DAC.
+ *
+ * When configured in BUS_MASTER_MODE, the PMIC's onboard PLL circuits
+ * will also be driven by the selected clock input signal.
+ */
+typedef enum {
+ CLOCK_IN_DEFAULT, /*!< Just use default (power-up) clock input. */
+ CLOCK_IN_CLIA, /*!< Use the CLIA clock source (Stereo DAC
+ default) . */
+ CLOCK_IN_CLIB, /*!< Use the CLIB clock source (Voice CODEC
+ default) . */
+ CLOCK_IN_CLKIN, /*!< Use the CLKIN clock source
+ . */
+ CLOCK_IN_MCLK, /*!< Disable the internal PLL and use the MCLK
+ clock source (Stereo DAC only)
+ . */
+ CLOCK_IN_FSYNC, /*!< Internal PLL input from external framesync
+ (Stereo DAC only) . */
+ CLOCK_IN_BITCLK /*!< Internal PLL input from external bitclock
+ (Stereo DAC only) */
+} PMIC_AUDIO_CLOCK_IN_SOURCE;
+
+/*!
+ * @enum PMIC_AUDIO_CLOCK_INVERT
+ * @brief Select whether to invert the frame sync or bit clock signals.
+ *
+ * This enumeration enables or disables the inversion of the incoming
+ * frame sync or bit clock signals.
+ */
+typedef enum {
+ NO_INVERT = 0, /*!< Do not invert the clock signals. */
+ INVERT_BITCLOCK = 1, /*!< Invert the BCLK input signal. */
+ INVERT_FRAMESYNC = 2 /*!< Invert the FSYNC input signal. */
+} PMIC_AUDIO_CLOCK_INVERT;
+
+/*!
+ * @enum PMIC_AUDIO_NUMSLOTS
+ * @brief Select whether to invert the frame sync or bit clock signals.
+ *
+ * This enumeration defines all of the possible number of timeslots that may
+ * be selected when the PMIC is configured as the data bus master. One of these
+ * options must be selected if the Stereo DAC is to provide the data bus
+ * clock signals.
+ *
+ * Note that the Voice CODEC currently only allows USE_4_TIMESLOTS when
+ * operating in data bus master mode.
+ */
+typedef enum {
+ USE_2_TIMESLOTS, /*!< Configure for 2 timeslots. */
+ USE_4_TIMESLOTS, /*!< Configure for 4 timeslots. */
+ USE_8_STAR_TIMESLOTS, /*!< Configure for 8 (Left, Right, 6 other) timeslots. */
+ USE_8_TIMESLOTS /*!< Configure for 8 timeslots. */
+} PMIC_AUDIO_NUMSLOTS;
+
+/*!
+ * @enum PMIC_AUDIO_STDAC_SAMPLING_RATE
+ * @brief Select the audio data sampling rate for the Stereo DAC.
+ *
+ * This enumeration defines all of the possible sampling rates currently
+ * supported by the Stereo DAC. One of these sampling rates must be selected
+ * and it must match that of the audio stream or else signal distortion will
+ * occur.
+ */
+typedef enum {
+ STDAC_RATE_8_KHZ, /*!< Use 8 kHz sampling rate. */
+ STDAC_RATE_11_025_KHZ, /*!< Use 11.025 kHz sampling rate. */
+ STDAC_RATE_12_KHZ, /*!< Use 12 kHz sampling rate. */
+ STDAC_RATE_16_KHZ, /*!< Use 16 kHz sampling rate. */
+ STDAC_RATE_22_050_KHZ, /*!< Use 22.050 kHz sampling rate. */
+ STDAC_RATE_24_KHZ, /*!< Use 24 kHz sampling rate. */
+ STDAC_RATE_32_KHZ, /*!< Use 32 kHz sampling rate. */
+ STDAC_RATE_44_1_KHZ, /*!< Use 44.1 kHz sampling rate. */
+ STDAC_RATE_48_KHZ, /*!< Use 48 kHz sampling rate. */
+ STDAC_RATE_64_KHZ, /*!< Use 64 kHz sampling rate
+ . */
+ STDAC_RATE_96_KHZ /*!< Use 96 kHz sampling rate.
+ . */
+} PMIC_AUDIO_STDAC_SAMPLING_RATE;
+
+/*!
+ * @enum PMIC_AUDIO_VCODEC_SAMPLING_RATE
+ * @brief Select the audio data sampling rate for the Voice CODEC.
+ *
+ * This enumeration defines all of the possible sampling rates currently
+ * supported by the Voice CODEC. One of these sampling rates must be selected
+ * and it must match that of the audio stream or else signal distortion will
+ * occur.
+ */
+typedef enum {
+ VCODEC_RATE_8_KHZ, /*!< Use 8 kHz sampling rate. */
+ VCODEC_RATE_16_KHZ, /*!< Use 16 kHz sampling rate. */
+} PMIC_AUDIO_VCODEC_SAMPLING_RATE;
+
+/*!
+ * @enum PMIC_AUDIO_ANTI_POP_RAMP_SPEED
+ * @brief Select the anti-pop circuitry's ramp up speed.
+ *
+ * This enumeration defines all of the possible ramp up speeds for the
+ * anti-pop circuitry. A slow ramp up speed may be required in order to
+ * avoid the popping noise that is typically generated during the insertion
+ * or removal of a headset or microphone.
+ */
+typedef enum {
+ ANTI_POP_RAMP_FAST, /*!< Select fast ramp up. */
+ ANTI_POP_RAMP_SLOW /*!< Select slow ramp up. */
+} PMIC_AUDIO_ANTI_POP_RAMP_SPEED;
+
+/*@}*/
+
+/*!
+ * @name General Voice CODEC Configuration Typedefs and Enumerations
+ * Typedefs and enumerations that are used to define and configure
+ * the basic operating options for the Voice CODEC.
+ */
+/*@{*/
+
+/*!
+ * @enum PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ
+ * @brief Select the Voice CODEC input clock frequency.
+ *
+ * This enumeration defines all of the supported Voice CODEC input clock
+ * frequencies. One of these frequencies must be selected in order to
+ * properly configure the Voice CODEC to operate at the required sampling
+ * rate.
+ */
+typedef enum {
+ VCODEC_CLI_13MHZ, /*!< Clock frequency is 13MHz. */
+ VCODEC_CLI_15_36MHZ, /*!< Clock frequency is 15.36MHz. */
+ VCODEC_CLI_16_8MHZ, /*!< Clock frequency is 16.8MHz
+ . */
+ VCODEC_CLI_26MHZ, /*!< Clock frequency is 26MHz. */
+ VCODEC_CLI_33_6MHZ, /*!< Clock frequency is 33.6MHz. */
+} PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ;
+
+/*!
+ * @enum PMIC_AUDIO_VCODEC_CONFIG
+ * @brief Select the Voice CODEC configuration options.
+ *
+ * This enumeration is used to enable/disable each of the Voice CODEC options.
+ * This includes the use of highpass digital filters and audio signal
+ * loopback modes.
+ *
+ * Note that resetting the digital filters is now handled by the
+ * pmic_audio_digital_filter_reset() API.
+ */
+typedef enum {
+ DITHERING = 1, /*!< Enable/disable dithering. */
+ INPUT_HIGHPASS_FILTER = 2, /*!< Enable/disable the input high
+ pass digital filter. */
+ OUTPUT_HIGHPASS_FILTER = 4, /*!< Enable/disable the output high
+ pass digital filter. */
+ ANALOG_LOOPBACK = 8, /*!< Enable/disable the analog
+ loopback path
+ . */
+ DIGITAL_LOOPBACK = 16, /*!< Enable/disable the digital
+ loopback path. */
+ VCODEC_MASTER_CLOCK_OUTPUTS = 32, /*!< Enable/disable the bus master
+ clock outputs. */
+ TRISTATE_TS = 64 /*!< Enable/disable FSYNC, BITCLK,
+ and TX tristate. */
+} PMIC_AUDIO_VCODEC_CONFIG;
+
+/*@}*/
+
+/*!
+ * @name General Stereo DAC Configuration Typedefs and Enumerations
+ * Typedefs and enumerations that are used to define and configure
+ * the basic operating options for the Stereo DAC.
+ */
+/*@{*/
+
+/*!
+ * @enum PMIC_AUDIO_STDAC_CLOCK_IN_FREQ
+ * @brief Select the Stereo DAC input clock frequency.
+ *
+ * This enumeration defines all of the supported Stereo DAC input clock
+ * frequencies. One of these frequencies must be selected in order to
+ * properly configure the Stereo DAC to operate at the required sampling
+ * rate.
+ */
+typedef enum {
+ STDAC_CLI_3_36864MHZ, /*!< Clock frequency is 3.36864MHz
+ . */
+ STDAC_CLI_12MHZ, /*!< Clock frequency is 12MHz.
+ . */
+ STDAC_CLI_13MHZ, /*!< Clock frequency is 13MHz. */
+ STDAC_CLI_15_36MHZ, /*!< Clock frequency is 15.36MHz. */
+ STDAC_CLI_16_8MHZ, /*!< Clock frequency is 16.8MHz
+ . */
+ STDAC_CLI_26MHZ, /*!< Clock frequency is 26MHz. */
+ STDAC_CLI_33_6MHZ, /*!< Clock frequency is 33.6MHz. */
+ STDAC_MCLK_PLL_DISABLED, /*!< Use MCLK and disable internal PLL. */
+ STDAC_FSYNC_IN_PLL, /*!< Use FSYNC as internal PLL input. */
+ STDAC_BCLK_IN_PLL /*!< Use BCLK as internal PLL input. */
+} PMIC_AUDIO_STDAC_CLOCK_IN_FREQ;
+
+/*!
+ * @enum PMIC_AUDIO_STDAC_CONFIG
+ * @brief Select the Stereo DAC configuration options.
+ *
+ * This enumeration is used to enable/disable each of the Stereo DAC options.
+ */
+typedef enum {
+ STDAC_MASTER_CLOCK_OUTPUTS = 1 /*!< Enable/disable the bus master clock
+ outputs. */
+} PMIC_AUDIO_STDAC_CONFIG;
+
+/*@}*/
+
+/*!
+ * @name Voice CODEC Audio Port Mixing Typedefs and Enumerations
+ * Typedefs and enumerations that are used for setting up the audio mixer
+ * within the Voice CODEC.
+ */
+/*@{*/
+
+/*!
+ * @enum PMIC_AUDIO_VCODEC_TIMESLOT
+ * @brief Select the Stereo DAC configuration options.
+ *
+ * This enumeration is used to select the timeslot for both the primary and
+ * secondary (for mc13783-only) audio channels to the Voice CODEC.
+ */
+typedef enum {
+ USE_TS0, /*!< Use timeslot 0 for audio signal source
+ . */
+ USE_TS1, /*!< Use timeslot 1 for audio signal source
+ . */
+ USE_TS2, /*!< Use timeslot 2 for audio signal source
+ . */
+ USE_TS3 /*!< Use timeslot 3 for audio signal source
+ . */
+} PMIC_AUDIO_VCODEC_TIMESLOT;
+
+/*!
+ * @enum PMIC_AUDIO_VCODEC_MIX_IN_GAIN
+ * @brief Select the secondary channel input gain for the Voice CODEC mixer.
+ *
+ * This enumeration selects the secondary channel input gain for the Voice
+ * CODEC mixer.
+ */
+typedef enum {
+ VCODEC_NO_MIX, /*!< No audio mixing . */
+ VCODEC_MIX_IN_0DB, /*!< Mix with 0dB secondary channel gain
+ . */
+ VCODEC_MIX_IN_MINUS_6DB, /*!< Mix with -6dB secondary channel gain
+ . */
+ VCODEC_MIX_IN_MINUS_12DB, /*!< Mix with -12dB secondary channel gain
+ . */
+} PMIC_AUDIO_VCODEC_MIX_IN_GAIN;
+
+/*!
+ * @enum PMIC_AUDIO_VCODEC_MIX_OUT_GAIN
+ * @brief Select the output gain for the Voice CODEC mixer.
+ *
+ * This enumeration selects the output gain for the Voice CODEC mixer.
+ */
+typedef enum {
+ VCODEC_MIX_OUT_0DB, /*!< Select 0dB mixer output gain
+ . */
+ VCODEC_MIX_OUT_MINUS_6DB, /*!< Select -6dB mixer output gain
+ . */
+} PMIC_AUDIO_VCODEC_MIX_OUT_GAIN;
+
+/*@}*/
+
+/*!
+ * @name Stereo DAC Audio Port Mixing Typedefs and Enumerations
+ * Typedefs and enumerations that are used for setting up the audio mixer
+ * within the Stereo DAC.
+ */
+/*@{*/
+
+/*!
+ * @enum PMIC_AUDIO_STDAC_TIMESLOTS
+ * @brief Select the timeslots used to transmit the left and right audio
+ * channels to the Stereo DAC.
+ *
+ * This enumeration is used to select the timeslots used to transmit the
+ * data corresponding to the left and right audio channels to the Stereo
+ * DAC.
+ */
+typedef enum {
+ USE_TS0_TS1, /*!< Use timeslots 0 and 1 for left and
+ right channels, respectively. */
+ USE_TS2_TS3, /*!< Use timeslots 2 and 3 for left and
+ right channels, respectively
+ . */
+ USE_TS4_TS5, /*!< Use timeslots 4 and 5 for left and
+ right channels, respectively
+ . */
+ USE_TS6_TS7 /*!< Use timeslots 6 and 7 for left and
+ right channels, respectively
+ . */
+} PMIC_AUDIO_STDAC_TIMESLOTS;
+
+/*!
+ * @enum PMIC_AUDIO_STDAC_MIX_IN_GAIN
+ * @brief Select the secondary channel input gain for the Stereo DAC mixer.
+ *
+ * This enumeration is used to select the secondary channel input gain for
+ * the Stereo DAC mixer.
+ */
+typedef enum {
+ STDAC_NO_MIX, /*!< No mixing, keep separate left
+ and right audio channels. */
+ STDAC_MIX_IN_0DB, /*!< Mix left and right audio channels
+ together with 0dB secondary
+ channel gain. */
+ STDAC_MIX_IN_MINUS_6DB, /*!< Mix left and right audio channels
+ together with -6dB secondary
+ channel gain. */
+ STDAC_MIX_IN_MINUS_12DB /*!< Mix left and right audio channels
+ together with -12dB secondary
+ channel gain . */
+} PMIC_AUDIO_STDAC_MIX_IN_GAIN;
+
+/*!
+ * @enum PMIC_AUDIO_STDAC_MIX_OUT_GAIN
+ * @brief Select the output gain for the Stereo DAC mixer.
+ *
+ * This enumeration is used to select the output gain for the Stereo DAC
+ * mixer.
+ */
+typedef enum {
+ STDAC_MIX_OUT_0DB, /*!< Select 0dB mixer output gain. */
+ STDAC_MIX_OUT_MINUS_6DB, /*!< Select -6dB mixer output gain
+ . */
+} PMIC_AUDIO_STDAC_MIX_OUT_GAIN;
+
+/*@}*/
+
+/*!
+ * @name Microphone Input Typedefs and Enumerations
+ * Typedefs and enumerations that are used for selecting and setting up
+ * one or more or microphone inputs for recording.
+ */
+/*@{*/
+
+/*!
+ * @enum PMIC_AUDIO_MIC_BIAS
+ * @brief Select the microphone bias circuit to be enabled/disabled.
+ *
+ * This enumeration lists all of the available microphone bias circuits that
+ * may be enabled or disabled.
+ */
+typedef enum {
+ NO_BIAS = 0, /*!< No microphone bias circuit selected. */
+ MIC_BIAS1 = 1, /*!< Enable/disable microphone bias 1 circuit. */
+ MIC_BIAS2 = 2, /*!< Enable/disable microphone bias 2 circuit. */
+} PMIC_AUDIO_MIC_BIAS;
+
+/*!
+ * @enum PMIC_AUDIO_INPUT_PORT
+ * @brief Select an audio input port for recording.
+ *
+ * This enumeration lists all of the available audio input ports that may
+ * be selected for a recording operation.
+ */
+typedef enum {
+ NO_MIC, /*!< No microphone input selected. */
+ MIC1_LEFT, /*!< Enable left/mono channel microphone input
+ . */
+ MIC1_RIGHT_MIC_MONO, /*!< Enable right channel microphone input. */
+ MIC2_AUX, /*!< Enable auxiliary microphone input. */
+ TXIN_EXT /*!< Enable external mono input. */
+} PMIC_AUDIO_INPUT_PORT;
+
+/*!
+ * @enum PMIC_AUDIO_INPUT_MIC_STATE
+ * @brief Control whether the input microphone is on/off.
+ *
+ * This enumeration allows the currently selected input microphone amplifier
+ * to be turned on/off.
+ */
+typedef enum {
+ MICROPHONE_ON, /*!< Turn microphone input on for recording. */
+ MICROPHONE_OFF /*!< Turn microphone input off (mute). */
+} PMIC_AUDIO_INPUT_MIC_STATE;
+
+/*!
+ * @enum PMIC_AUDIO_INPUT_CONFIG
+ * @brief Enable/disable the audio input options.
+ *
+ * This enumeration allows for enabling/disabling any of the audio input
+ * section options.
+ */
+typedef enum {
+ MIC_AMP_AUTO_DISABLE = 1 /*!< Enable/disable automatic disabling of
+ microphone input amplifiers following
+ headset insertion/removal */
+} PMIC_AUDIO_INPUT_CONFIG;
+
+/*!
+ * @enum PMIC_AUDIO_MIC_AMP_MODE
+ * @brief Select the operating mode for the microphone amplifiers.
+ *
+ * This enumeration is used to select the operating mode for the microphone
+ * amplifier.
+ */
+typedef enum {
+ AMP_OFF, /*!< Disable input amplifier. */
+ VOLTAGE_TO_VOLTAGE, /*!< Operate input amplifier in
+ voltage-to-voltage mode
+ . */
+ CURRENT_TO_VOLTAGE /*!< Operate input amplifier in
+ current-to-voltage mode */
+} PMIC_AUDIO_MIC_AMP_MODE;
+
+/*!
+ * @enum PMIC_AUDIO_MIC_GAIN
+ * @brief Select the microphone amplifier gain level.
+ *
+ * This enumeration lists all of the available microphone amplifier gain
+ * levels.
+ */
+typedef enum {
+ MIC_GAIN_MINUS_8DB, /*!< Select -8dB microphone amplifier gain
+ . */
+ MIC_GAIN_MINUS_7DB, /*!< Select -7dB microphone amplifier gain
+ . */
+ MIC_GAIN_MINUS_6DB, /*!< Select -6dB microphone amplifier gain
+ . */
+ MIC_GAIN_MINUS_5DB, /*!< Select -5dB microphone amplifier gain
+ . */
+ MIC_GAIN_MINUS_4DB, /*!< Select -4dB microphone amplifier gain
+ . */
+ MIC_GAIN_MINUS_3DB, /*!< Select -3dB microphone amplifier gain
+ . */
+ MIC_GAIN_MINUS_2DB, /*!< Select -2dB microphone amplifier gain
+ . */
+ MIC_GAIN_MINUS_1DB, /*!< Select -1dB microphone amplifier gain
+ . */
+ MIC_GAIN_0DB, /*!< Select 0dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_1DB, /*!< Select 1dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_2DB, /*!< Select 2dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_3DB, /*!< Select 3dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_4DB, /*!< Select 4dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_5DB, /*!< Select 5dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_6DB, /*!< Select 6dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_7DB, /*!< Select 7dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_8DB, /*!< Select 8dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_9DB, /*!< Select 9dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_10DB, /*!< Select 10dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_11DB, /*!< Select 11dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_12DB, /*!< Select 12dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_13DB, /*!< Select 13dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_14DB, /*!< Select 14dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_15DB, /*!< Select 15dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_16DB, /*!< Select 16dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_17DB, /*!< Select 17dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_18DB, /*!< Select 18dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_19DB, /*!< Select 19dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_20DB, /*!< Select 20dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_21DB, /*!< Select 21dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_22DB, /*!< Select 22dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_23DB, /*!< Select 23dB microphone amplifier gain. */
+ MIC_GAIN_PLUS_24DB, /*!< Select 24dB microphone amplifier gain
+ . */
+ MIC_GAIN_PLUS_25DB, /*!< Select 25dB microphone amplifier gain
+ . */
+ MIC_GAIN_PLUS_26DB, /*!< Select 26dB microphone amplifier gain
+ . */
+ MIC_GAIN_PLUS_27DB, /*!< Select 27dB microphone amplifier gain
+ . */
+ MIC_GAIN_PLUS_28DB, /*!< Select 28dB microphone amplifier gain
+ . */
+ MIC_GAIN_PLUS_29DB, /*!< Select 29dB microphone amplifier gain
+ . */
+ MIC_GAIN_PLUS_30DB, /*!< Select 30dB microphone amplifier gain
+ . */
+ MIC_GAIN_PLUS_31DB /*!< Select 31dB microphone amplifier gain
+ . */
+} PMIC_AUDIO_MIC_GAIN;
+
+/*@}*/
+
+/*!
+ * @name Audio Output Section Typedefs and Enumerations
+ * Typedefs and enumerations that are used for selecting and setting up
+ * one or more or audio output ports for playback.
+ */
+/*@{*/
+
+/*!
+ * @enum PMIC_AUDIO_OUTPUT_PORT
+ * @brief Select the audio output port.
+ *
+ * This enumeration lists all of the available audio output ports. One or
+ * more may be selected as desired to handle the output audio stream from
+ * either the Voice CODEC or the Stereo DAC.
+ */
+typedef enum {
+ MONO_SPEAKER = 1, /*!< Select mono output speaker. */
+ MONO_LOUDSPEAKER = 2, /*!< Select mono loudspeaker
+ . */
+ MONO_ALERT = 4, /*!< Select mono alert output */
+ MONO_EXTOUT = 8, /*!< Select mono external output */
+ MONO_CDCOUT = 16, /*!< Select dedicated Voice CODEC output
+ . */
+ STEREO_LEFT_LOW_POWER = 32, /*!< Select stereo left channel low power
+ output . */
+ STEREO_HEADSET_LEFT = 64, /*!< Select stereo headset left channel. */
+ STEREO_HEADSET_RIGHT = 128, /*!< Select stereo headset right channel. */
+ STEREO_OUT_LEFT = 256, /*!< Select stereo external left channel
+ output . */
+ STEREO_OUT_RIGHT = 512 /*!< Select stereo external right channel
+ output . */
+} PMIC_AUDIO_OUTPUT_PORT;
+
+/*!
+ * @enum PMIC_AUDIO_OUTPUT_CONFIG
+ * @brief Enable/disable the audio output section options.
+ *
+ * This enumeration is used to enable/disable any of the audio output section
+ * options.
+ */
+typedef enum {
+ MONO_SPEAKER_INVERT_OUT_ONLY = 1, /*!< Enable/disable the non-inverted
+ mono speaker output */
+ MONO_LOUDSPEAKER_COMMON_BIAS = 2, /*!< Enable/disable the loudspeaker
+ output amplifier common bias
+ . */
+ HEADSET_DETECT_ENABLE = 4, /*!< Enable/disable headset
+ insertion/removal detection
+ . */
+ STEREO_HEADSET_AMP_AUTO_DISABLE = 8 /*!< Enable/disable automatic
+ disabling of the stereo headset
+ output amplifiers following
+ headset insertion/removal. */
+} PMIC_AUDIO_OUTPUT_CONFIG;
+
+/*!
+ * @enum PMIC_AUDIO_STEREO_IN_GAIN
+ * @brief Select the amplifier gain for the external stereo inputs.
+ *
+ * This enumeration is used to select the amplifier gain level to be used for
+ * the external stereo inputs.
+ */
+typedef enum {
+ STEREO_IN_GAIN_0DB, /*!< Select 0dB external stereo signal
+ input gain. */
+ STEREO_IN_GAIN_PLUS_18DB /*!< Select 18dB external stereo signal
+ input gain . */
+} PMIC_AUDIO_STEREO_IN_GAIN;
+
+/*!
+ * @enum PMIC_AUDIO_OUTPUT_PGA_GAIN
+ * @brief Select the output PGA amplifier gain level.
+ *
+ * This enumeration is used to select the output PGA amplifier gain level.
+ */
+typedef enum {
+ OUTPGA_GAIN_MINUS_33DB, /*!< Select -33dB output PGA gain
+ . */
+ OUTPGA_GAIN_MINUS_30DB, /*!< Select -30dB output PGA gain
+ . */
+ OUTPGA_GAIN_MINUS_27DB, /*!< Select -27dB output PGA gain
+ . */
+ OUTPGA_GAIN_MINUS_24DB, /*!< Select -24dB output PGA gain. */
+ OUTPGA_GAIN_MINUS_21DB, /*!< Select -21dB output PGA gain. */
+ OUTPGA_GAIN_MINUS_18DB, /*!< Select -18dB output PGA gain. */
+ OUTPGA_GAIN_MINUS_15DB, /*!< Select -15dB output PGA gain. */
+ OUTPGA_GAIN_MINUS_12DB, /*!< Select -12dB output PGA gain. */
+ OUTPGA_GAIN_MINUS_9DB, /*!< Select -9dB output PGA gain. */
+ OUTPGA_GAIN_MINUS_6DB, /*!< Select -6dB output PGA gain. */
+ OUTPGA_GAIN_MINUS_3DB, /*!< Select -3dB output PGA gain. */
+ OUTPGA_GAIN_0DB, /*!< Select 0dB output PGA gain. */
+ OUTPGA_GAIN_PLUS_3DB, /*!< Select 3dB output PGA gain. */
+ OUTPGA_GAIN_PLUS_6DB, /*!< Select 6dB output PGA gain. */
+ OUTPGA_GAIN_PLUS_9DB, /*!< Select 9dB output PGA gain.
+ . */
+ OUTPGA_GAIN_PLUS_12DB, /*!< Select 12dB output PGA gain
+ . */
+ OUTPGA_GAIN_PLUS_15DB, /*!< Select 15dB output PGA gain
+ . */
+ OUTPGA_GAIN_PLUS_18DB, /*!< Select 18dB output PGA gain
+ . */
+ OUTPGA_GAIN_PLUS_21DB /*!< Select 21dB output PGA gain
+ . */
+} PMIC_AUDIO_OUTPUT_PGA_GAIN;
+
+/*!
+ * @enum PMIC_AUDIO_OUTPUT_BALANCE_GAIN
+ * @brief Select the left/right channel balance gain level.
+ *
+ * This enumeration is used to select the balance gain level that is to be
+ * separately applied to the left and right audio channels.
+ */
+typedef enum {
+ BAL_GAIN_MINUS_21DB, /*!< Select -21dB channel balance
+ gain . */
+ BAL_GAIN_MINUS_18DB, /*!< Select -18dB channel balance
+ gain . */
+ BAL_GAIN_MINUS_15DB, /*!< Select -15dB channel balance
+ gain . */
+ BAL_GAIN_MINUS_12DB, /*!< Select -12dB channel balance
+ gain . */
+ BAL_GAIN_MINUS_9DB, /*!< Select -9dB channel balance
+ gain . */
+ BAL_GAIN_MINUS_6DB, /*!< Select -6dB channel balance
+ gain . */
+ BAL_GAIN_MINUS_3DB, /*!< Select -3dB channel balance
+ gain . */
+ BAL_GAIN_0DB /*!< Select 0dB channel balance gain. */
+} PMIC_AUDIO_OUTPUT_BALANCE_GAIN;
+
+/*!
+ * @enum PMIC_AUDIO_MONO_ADDER_MODE
+ * @brief Select the output mono adder operating mode.
+ *
+ * This enumeration is used to select the operating mode for the mono adder
+ * in the audio output section.
+ */
+typedef enum {
+ MONO_ADDER_OFF, /*!< Disable mono adder (keep separate
+ left and right channels). */
+ MONO_ADD_LEFT_RIGHT, /*!< Add left and right channels. */
+ MONO_ADD_OPPOSITE_PHASE, /*!< Add left and right channels but
+ with outputs in opposite phase
+ . */
+ STEREO_OPPOSITE_PHASE /*!< Keep separate left and right
+ channels but invert phase of
+ left channel . */
+} PMIC_AUDIO_MONO_ADDER_MODE;
+
+/*!
+ * @enum PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN
+ * @brief Select the mono adder output amplifier gain level.
+ *
+ * This enumeration is used to select the output amplifier gain level for
+ * the mono adder.
+ */
+typedef enum {
+ MONOADD_GAIN_MINUS_6DB, /*!< Select -6dB mono adder output gain
+ . */
+ MONOADD_GAIN_MINUS_3DB, /*!< Select -3dB mono adder output gain
+ . */
+ MONOADD_GAIN_0DB /*!< Select 0dB mono adder output gain. */
+} PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN;
+
+/*@}*/
+
+/***************************************************************************
+ * PMIC-SPECIFIC DEFINITIONS *
+ ***************************************************************************/
+
+/*!
+ * @name Definition of PMIC-specific Capabilities
+ * Constants that are used to define PMIC-specific capabilities.
+ */
+/*@{*/
+
+/*!
+ * Define the minimum Stereo DAC sampling rate (Hz).
+ */
+extern const unsigned MIN_STDAC_SAMPLING_RATE_HZ;
+/*!
+ * Define the maximum Stereo DAC sampling rate (Hz).
+ */
+extern const unsigned MAX_STDAC_SAMPLING_RATE_HZ;
+
+/*@}*/
+
+#define DEBUG_AUDIO
+
+#ifdef __KERNEL__
+
+/***************************************************************************
+ * PMIC API DEFINITIONS *
+ ***************************************************************************/
+
+/*!
+ * @name General Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Audio
+ * hardware.
+ */
+/*@{*/
+
+/*!
+ * This function enables the Headset detection mechanism in hardware
+ */
+PMIC_STATUS pmic_audio_set_autodetect(int val);
+
+/*!
+ * @brief Request exclusive access to the PMIC Audio hardware.
+ *
+ * Attempt to open and gain exclusive access to a key PMIC audio hardware
+ * component (e.g., the Stereo DAC or the Voice CODEC). Depending upon the
+ * type of audio operation that is desired and the nature of the audio data
+ * stream, the Stereo DAC and/or the Voice CODEC will be a required hardware
+ * component and needs to be acquired by calling this function.
+ *
+ * If the open request is successful, then a numeric handle is returned
+ * and this handle must be used in all subsequent function calls to complete
+ * the configuration of either the Stereo DAC or the Voice CODEC and along
+ * with any other associated audio hardware components that will be needed.
+ *
+ * The same handle must also be used in the close call when use of the PMIC
+ * audio hardware is no longer required.
+ *
+ * The open request will fail if the requested audio hardware component has
+ * already been acquired by a previous open call but not yet closed.
+ *
+ * @param[out] handle Device handle to be used for subsequent PMIC
+ * Connectivity API calls.
+ * @param[in] device The required PMIC audio hardware component.
+ *
+ * @retval PMIC_SUCCESS If the open request was successful
+ * @retval PMIC_PARAMETER_ERROR If the handle argument is NULL.
+ * @retval PMIC_ERROR If the audio hardware component is
+ * unavailable.
+ */
+PMIC_STATUS pmic_audio_open(PMIC_AUDIO_HANDLE * const handle,
+ const PMIC_AUDIO_SOURCE device);
+
+/*!
+ * @brief Terminate further access to the PMIC audio hardware.
+ *
+ * Terminate further access to the PMIC audio hardware that was previously
+ * acquired by calling pmic_audio_open(). This now allows another thread to
+ * successfully call pmic_audio_open() to gain access.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the close request was successful.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_close(const PMIC_AUDIO_HANDLE handle);
+
+/*!
+ * @brief Configure the data bus protocol to be used.
+ *
+ * Provide the parameters needed to properly configure the audio data bus
+ * protocol so that data can be read/written to either the Stereo DAC or
+ * the Voice CODEC.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] busID Select data bus to be used.
+ * @param[in] protocol Select the data bus protocol.
+ * @param[in] masterSlave Select the data bus timing mode.
+ * @param[in] numSlots Define the number of timeslots (only if in
+ * master mode).
+ *
+ * @retval PMIC_SUCCESS If the protocol was successful configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the protocol parameters
+ * are invalid.
+ */
+PMIC_STATUS pmic_audio_set_protocol(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_DATA_BUS busID,
+ const PMIC_AUDIO_BUS_PROTOCOL protocol,
+ const PMIC_AUDIO_BUS_MODE masterSlave,
+ const PMIC_AUDIO_NUMSLOTS numSlots);
+
+/*!
+ * @brief Retrieve the current data bus protocol configuration.
+ *
+ * Retrieve the parameters that define the current audio data bus protocol.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] busID The data bus being used.
+ * @param[out] protocol The data bus protocol being used.
+ * @param[out] masterSlave The data bus timing mode being used.
+ * @param[out] numSlots The number of timeslots being used (if in
+ * master mode).
+ *
+ * @retval PMIC_SUCCESS If the protocol was successful retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_get_protocol(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_DATA_BUS * const busID,
+ PMIC_AUDIO_BUS_PROTOCOL * const protocol,
+ PMIC_AUDIO_BUS_MODE * const masterSlave,
+ PMIC_AUDIO_NUMSLOTS * const numSlots);
+
+/*!
+ * @brief Enable the Stereo DAC or the Voice CODEC.
+ *
+ * Explicitly enable the Stereo DAC or the Voice CODEC to begin audio
+ * playback or recording as required. This should only be done after
+ * successfully configuring all of the associated audio components (e.g.,
+ * microphones, amplifiers, etc.).
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the device was successful enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_ERROR If the device could not be enabled.
+ */
+PMIC_STATUS pmic_audio_enable(const PMIC_AUDIO_HANDLE handle);
+
+/*!
+ * @brief Disable the Stereo DAC or the Voice CODEC.
+ *
+ * Explicitly disable the Stereo DAC or the Voice CODEC to end audio
+ * playback or recording as required.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the device was successful disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_ERROR If the device could not be disabled.
+ */
+PMIC_STATUS pmic_audio_disable(const PMIC_AUDIO_HANDLE handle);
+
+/*!
+ * @brief Reset the selected audio hardware control registers to their
+ * power on state.
+ *
+ * This resets all of the audio hardware control registers currently
+ * associated with the device handle back to their power on states. For
+ * example, if the handle is associated with the Stereo DAC and a
+ * specific output port and output amplifiers, then this function will
+ * reset all of those components to their power on state.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the reset operation was successful.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_ERROR If the reset was unsuccessful.
+ */
+PMIC_STATUS pmic_audio_reset(const PMIC_AUDIO_HANDLE handle);
+
+/*!
+ * @brief Reset all audio hardware control registers to their power on state.
+ *
+ * This resets all of the audio hardware control registers back to their
+ * power on states. Use this function with care since it also invalidates
+ * (i.e., automatically closes) all currently opened device handles.
+ *
+ * @retval PMIC_SUCCESS If the reset operation was successful.
+ * @retval PMIC_ERROR If the reset was unsuccessful.
+ */
+PMIC_STATUS pmic_audio_reset_all(void);
+
+/*!
+ * @brief Set the Audio callback function.
+ *
+ * Register a callback function that will be used to signal PMIC audio
+ * events. For example, the OSS audio driver should register a callback
+ * function in order to be notified of headset connect/disconnect events.
+ *
+ * @param[in] func A pointer to the callback function.
+ * @param[in] eventMask A mask selecting events to be notified.
+ * @param[in] hs_state To know the headset state.
+ *
+ * @retval PMIC_SUCCESS If the callback was successfully
+ * registered.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the eventMask is invalid.
+ */
+PMIC_STATUS pmic_audio_set_callback(void *func,
+ const PMIC_AUDIO_EVENTS eventMask,
+ PMIC_HS_STATE * hs_state);
+
+/*!
+ * @brief Deregisters the existing audio callback function.
+ *
+ * Deregister the callback function that was previously registered by calling
+ * pmic_audio_set_callback().
+ *
+ *
+ * @retval PMIC_SUCCESS If the callback was successfully
+ * deregistered.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_clear_callback(void);
+
+/*!
+ * @brief Get the current audio callback function settings.
+ *
+ * Get the current callback function and event mask.
+ *
+ * @param[out] func The current callback function.
+ * @param[out] eventMask The current event selection mask.
+ *
+ * @retval PMIC_SUCCESS If the callback information was
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_get_callback(PMIC_AUDIO_CALLBACK * const func,
+ PMIC_AUDIO_EVENTS * const eventMask);
+
+/*!
+ * @brief Enable the anti-pop circuitry to avoid extra noise when inserting
+ * or removing a external device (e.g., a headset).
+ *
+ * Enable the use of the built-in anti-pop circuitry to prevent noise from
+ * being generated when an external audio device is inserted or removed
+ * from an audio plug. A slow ramp speed may be needed to avoid extra noise.
+ *
+ * @param[in] rampSpeed The desired anti-pop circuitry ramp speed.
+ *
+ * @retval PMIC_SUCCESS If the anti-pop circuitry was successfully
+ * enabled.
+ * @retval PMIC_ERROR If the anti-pop circuitry could not be
+ * enabled.
+ */
+PMIC_STATUS pmic_audio_antipop_enable(const PMIC_AUDIO_ANTI_POP_RAMP_SPEED
+ rampSpeed);
+
+/*!
+ * @brief Disable the anti-pop circuitry.
+ *
+ * Disable the use of the built-in anti-pop circuitry to prevent noise from
+ * being generated when an external audio device is inserted or removed
+ * from an audio plug.
+ *
+ * @retval PMIC_SUCCESS If the anti-pop circuitry was successfully
+ * disabled.
+ * @retval PMIC_ERROR If the anti-pop circuitry could not be
+ * disabled.
+ */
+PMIC_STATUS pmic_audio_antipop_disable(void);
+
+/*!
+ * @brief Performs a reset of the Voice CODEC/Stereo DAC digital filter.
+ *
+ * This function performs a reset of the digital filter using the back-to-back
+ * SPI write procedure.
+ *
+ * @retval PMIC_SUCCESS If the digital filter was successfully
+ * reset.
+ * @retval PMIC_ERROR If the digital filter could not be reset.
+ */
+PMIC_STATUS pmic_audio_digital_filter_reset(const PMIC_AUDIO_HANDLE handle);
+
+/*!
+ * @brief Get the most recent PTT button voltage reading.
+ *
+ * This function returns the most recent reading for the PTT button voltage.
+ * The value may be used during the processing of the PTT_BUTTON_RANGE event
+ * as part of the headset ID detection process.
+ *
+ * @retval PMIC_SUCCESS If the most recent PTT button voltage was
+ * returned.
+ * @retval PMIC_PARAMETER_ERROR If a NULL pointer argument was given.
+ */
+PMIC_STATUS pmic_audio_get_ptt_button_level(unsigned int *const level);
+
+#ifdef DEBUG_AUDIO
+
+/*!
+ * @brief Provide a hexadecimal dump of all PMIC audio registers (DEBUG only).
+ *
+ * This function is intended strictly for debugging purposes only (i.e.,
+ * the DEBUG macro must be defined) and will print the current values of the
+ * following PMIC registers:
+ *
+ * - AUD_CODEC (Voice CODEC state)
+ * - ST_DAC (Stereo DAC state)
+ * - RX_AUD_AMPS (audio input section state)
+ * - TX_AUD_AMPS (audio output section state)
+ *
+ * The register fields will also be decoded.
+ */
+void pmic_audio_dump_registers(void);
+
+#endif /* DEBUG */
+
+/*@}*/
+
+/*!
+ * @name General Voice CODEC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Voice
+ * CODEC hardware.
+ */
+/*@{*/
+
+/*!
+ * @brief Set the Voice CODEC clock source and operating characteristics.
+ *
+ * Define the Voice CODEC clock source and operating characteristics. This
+ * must be done before the Voice CODEC is enabled.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] clockIn Select the clock signal source.
+ * @param[in] clockFreq Select the clock signal frequency.
+ * @param[in] samplingRate Select the audio data sampling rate.
+ * @param[in] invert Enable inversion of the frame sync and/or
+ * bit clock inputs.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC clock settings were
+ * successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or clock configuration was
+ * invalid.
+ * @retval PMIC_ERROR If the Voice CODEC clock configuration
+ * could not be set.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_clock(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_CLOCK_IN_SOURCE
+ clockIn,
+ const PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ
+ clockFreq,
+ const PMIC_AUDIO_VCODEC_SAMPLING_RATE
+ samplingRate,
+ const PMIC_AUDIO_CLOCK_INVERT invert);
+
+/*!
+ * @brief Get the Voice CODEC clock source and operating characteristics.
+ *
+ * Get the current Voice CODEC clock source and operating characteristics.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] clockIn The clock signal source.
+ * @param[out] clockFreq The clock signal frequency.
+ * @param[out] samplingRate The audio data sampling rate.
+ * @param[out] invert Inversion of the frame sync and/or
+ * bit clock inputs is enabled/disabled.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC clock settings were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle invalid.
+ * @retval PMIC_ERROR If the Voice CODEC clock configuration
+ * could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_clock(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_CLOCK_IN_SOURCE *
+ const clockIn,
+ PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ *
+ const clockFreq,
+ PMIC_AUDIO_VCODEC_SAMPLING_RATE *
+ const samplingRate,
+ PMIC_AUDIO_CLOCK_INVERT * const invert);
+
+/*!
+ * @brief Set the Voice CODEC primary audio channel timeslot.
+ *
+ * Set the Voice CODEC primary audio channel timeslot. This function must be
+ * used if the default timeslot for the primary audio channel is to be changed.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] timeslot Select the primary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC primary audio channel
+ * timeslot was successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
+ * was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC primary audio channel
+ * timeslot could not be set.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_VCODEC_TIMESLOT
+ timeslot);
+
+/*!
+ * @brief Get the current Voice CODEC primary audio channel timeslot.
+ *
+ * Get the current Voice CODEC primary audio channel timeslot.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] timeslot The primary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC primary audio channel
+ * timeslot was successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC primary audio channel
+ * timeslot could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_VCODEC_TIMESLOT *
+ const timeslot);
+
+/*!
+ * @brief Set the Voice CODEC secondary recording audio channel timeslot.
+ *
+ * Set the Voice CODEC secondary audio channel timeslot. This function must be
+ * used if the default timeslot for the secondary audio channel is to be
+ * changed. The secondary audio channel timeslot is used to transmit the audio
+ * data that was recorded by the Voice CODEC from the secondary audio input
+ * channel.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] timeslot Select the secondary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC secondary audio channel
+ * timeslot was successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
+ * was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC secondary audio channel
+ * timeslot could not be set.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_secondary_txslot(const PMIC_AUDIO_HANDLE
+ handle,
+ const
+ PMIC_AUDIO_VCODEC_TIMESLOT
+ timeslot);
+
+/*!
+ * @brief Get the Voice CODEC secondary recording audio channel timeslot.
+ *
+ * Get the Voice CODEC secondary audio channel timeslot.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] timeslot The secondary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC secondary audio channel
+ * timeslot was successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC secondary audio channel
+ * timeslot could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_secondary_txslot(const PMIC_AUDIO_HANDLE
+ handle,
+ PMIC_AUDIO_VCODEC_TIMESLOT *
+ const timeslot);
+
+/*!
+ * @brief Set/Enable the Voice CODEC options.
+ *
+ * Set or enable various Voice CODEC options. The available options include
+ * the use of dithering, highpass digital filters, and loopback modes.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] config The Voice CODEC options to enable.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC options were
+ * successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or Voice CODEC options
+ * were invalid.
+ * @retval PMIC_ERROR If the Voice CODEC options could not be
+ * successfully set/enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_VCODEC_CONFIG config);
+
+/*!
+ * @brief Clear/Disable the Voice CODEC options.
+ *
+ * Clear or disable various Voice CODEC options.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] config The Voice CODEC options to be cleared/disabled.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC options were
+ * successfully cleared/disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the Voice CODEC options
+ * were invalid.
+ * @retval PMIC_ERROR If the Voice CODEC options could not be
+ * cleared/disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_clear_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_VCODEC_CONFIG
+ config);
+
+/*!
+ * @brief Get the current Voice CODEC options.
+ *
+ * Get the current Voice CODEC options.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] config The current set of Voice CODEC options.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC options were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC options could not be
+ * retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_config(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_VCODEC_CONFIG *
+ const config);
+
+/*!
+ * @brief Enable the Voice CODEC bypass audio pathway.
+ *
+ * Enables the Voice CODEC bypass pathway for audio data. This allows direct
+ * output of the voltages on the TX data bus line to the output amplifiers
+ * (bypassing the digital-to-analog converters within the Voice CODEC).
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC bypass was successfully
+ * enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC bypass could not be
+ * enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_enable_bypass(const PMIC_AUDIO_HANDLE handle);
+
+/*!
+ * @brief Disable the Voice CODEC bypass audio pathway.
+ *
+ * Disables the Voice CODEC bypass pathway for audio data. This means that
+ * the TX data bus line will deliver digital data to the digital-to-analog
+ * converters within the Voice CODEC.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC bypass was successfully
+ * disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC bypass could not be
+ * disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_disable_bypass(const PMIC_AUDIO_HANDLE handle);
+
+/*@}*/
+
+/*!
+ * @name General Stereo DAC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Stereo
+ * DAC hardware.
+ */
+/*@{*/
+
+/*!
+ * @brief Set the Stereo DAC clock source and operating characteristics.
+ *
+ * Define the Stereo DAC clock source and operating characteristics. This
+ * must be done before the Stereo DAC is enabled.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] clockIn Select the clock signal source.
+ * @param[in] clockFreq Select the clock signal frequency.
+ * @param[in] samplingRate Select the audio data sampling rate.
+ * @param[in] invert Enable inversion of the frame sync and/or
+ * bit clock inputs.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC clock settings were
+ * successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or clock configuration was
+ * invalid.
+ * @retval PMIC_ERROR If the Stereo DAC clock configuration
+ * could not be set.
+ */
+PMIC_STATUS pmic_audio_stdac_set_clock(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_CLOCK_IN_SOURCE clockIn,
+ const PMIC_AUDIO_STDAC_CLOCK_IN_FREQ
+ clockFreq,
+ const PMIC_AUDIO_STDAC_SAMPLING_RATE
+ samplingRate,
+ const PMIC_AUDIO_CLOCK_INVERT invert);
+
+/*!
+ * @brief Get the Stereo DAC clock source and operating characteristics.
+ *
+ * Get the current Stereo DAC clock source and operating characteristics.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] clockIn The clock signal source.
+ * @param[out] clockFreq The clock signal frequency.
+ * @param[out] samplingRate The audio data sampling rate.
+ * @param[out] invert Inversion of the frame sync and/or
+ * bit clock inputs is enabled/disabled.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC clock settings were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle invalid.
+ * @retval PMIC_ERROR If the Stereo DAC clock configuration
+ * could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_stdac_get_clock(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_CLOCK_IN_SOURCE *
+ const clockIn,
+ PMIC_AUDIO_STDAC_SAMPLING_RATE *
+ const samplingRate,
+ PMIC_AUDIO_STDAC_CLOCK_IN_FREQ *
+ const clockFreq,
+ PMIC_AUDIO_CLOCK_INVERT * const invert);
+
+/*!
+ * @brief Set the Stereo DAC primary audio channel timeslot.
+ *
+ * Set the Stereo DAC primary audio channel timeslot. This function must be
+ * used if the default timeslot for the primary audio channel is to be changed.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] timeslot Select the primary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC primary audio channel
+ * timeslot was successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
+ * was invalid.
+ * @retval PMIC_ERROR If the Stereo DAC primary audio channel
+ * timeslot could not be set.
+ */
+PMIC_STATUS pmic_audio_stdac_set_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_STDAC_TIMESLOTS
+ timeslot);
+
+/*!
+ * @brief Get the current Stereo DAC primary audio channel timeslot.
+ *
+ * Get the current Stereo DAC primary audio channel timeslot.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] timeslot The primary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC primary audio channel
+ * timeslot was successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Stereo DAC primary audio channel
+ * timeslot could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_stdac_get_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_STDAC_TIMESLOTS *
+ const timeslot);
+
+/*!
+ * @brief Set/Enable the Stereo DAC options.
+ *
+ * Set or enable various Stereo DAC options. The available options include
+ * enabling/disabling the bus master clock outputs.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] config The Stereo DAC options to enable.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC options were
+ * successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or Stereo DAC options
+ * were invalid.
+ * @retval PMIC_ERROR If the Stereo DAC options could not be
+ * successfully set/enabled.
+ */
+PMIC_STATUS pmic_audio_stdac_set_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_STDAC_CONFIG config);
+
+/*!
+ * @brief Clear/Disable the Stereo DAC options.
+ *
+ * Clear or disable various Stereo DAC options.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] config The Stereo DAC options to be cleared/disabled.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC options were
+ * successfully cleared/disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the Stereo DAC options
+ * were invalid.
+ * @retval PMIC_ERROR If the Stereo DAC options could not be
+ * cleared/disabled.
+ */
+PMIC_STATUS pmic_audio_stdac_clear_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_STDAC_CONFIG config);
+
+/*!
+ * @brief Get the current Stereo DAC options.
+ *
+ * Get the current Stereo DAC options.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] config The current set of Stereo DAC options.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC options were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Stereo DAC options could not be
+ * retrieved.
+ */
+PMIC_STATUS pmic_audio_stdac_get_config(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_STDAC_CONFIG * const config);
+
+/*@}*/
+
+/*!
+ * @name Audio Input Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC audio
+ * input hardware.
+ */
+/*@{*/
+
+/*!
+ * @brief Set/Enable the audio input section options.
+ *
+ * Set or enable various audio input section options. The only available
+ * option right now is to enable the automatic disabling of the microphone
+ * input amplifiers when a microphone/headset is inserted or removed.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] config The audio input section options to enable.
+ *
+ * @retval PMIC_SUCCESS If the audio input section options were
+ * successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or audio input section
+ * options were invalid.
+ * @retval PMIC_ERROR If the audio input section options could
+ * not be successfully set/enabled.
+ */
+PMIC_STATUS pmic_audio_input_set_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_INPUT_CONFIG config);
+
+/*!
+ * @brief Clear/Disable the audio input section options.
+ *
+ * Clear or disable various audio input section options.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] config The audio input section options to be
+ * cleared/disabled.
+ *
+ * @retval PMIC_SUCCESS If the audio input section options were
+ * successfully cleared/disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the audio input section
+ * options were invalid.
+ * @retval PMIC_ERROR If the audio input section options could
+ * not be cleared/disabled.
+ */
+PMIC_STATUS pmic_audio_input_clear_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_INPUT_CONFIG config);
+
+/*!
+ * @brief Get the current audio input section options.
+ *
+ * Get the current audio input section options.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] config The current set of audio input section options.
+ *
+ * @retval PMIC_SUCCESS If the audio input section options were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the audio input section options could
+ * not be retrieved.
+ */
+PMIC_STATUS pmic_audio_input_get_config(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_INPUT_CONFIG * const config);
+
+/*@}*/
+
+/*!
+ * @name Audio Recording Using the Voice CODEC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Voice CODEC
+ * to perform audio recording.
+ */
+/*@{*/
+
+/*!
+ * @brief Select the microphone inputs to be used for Voice CODEC recording.
+ *
+ * Select left and right microphone inputs for Voice CODEC
+ * recording. It is possible to disable or not use a particular microphone
+ * input channel by specifying NO_MIC as a parameter.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] leftChannel Select the left microphone input channel.
+ * @param[in] rightChannel Select the right microphone input channel.
+ *
+ * @retval PMIC_SUCCESS If the microphone input channels were
+ * successfully enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or microphone input ports
+ * were invalid.
+ * @retval PMIC_ERROR If the microphone input channels could
+ * not be successfully enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_mic(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_INPUT_PORT leftChannel,
+ const PMIC_AUDIO_INPUT_PORT rightChannel);
+
+/*!
+ * @brief Get the current microphone inputs being used for Voice CODEC
+ * recording.
+ *
+ * Get the left and right microphone inputs currently being
+ * used for Voice CODEC recording.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] leftChannel The left microphone input channel.
+ * @param[out] rightChannel The right microphone input channel.
+ *
+ * @retval PMIC_SUCCESS If the microphone input channels were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the microphone input channels could
+ * not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_mic(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_INPUT_PORT * const leftChannel,
+ PMIC_AUDIO_INPUT_PORT *
+ const rightChannel);
+
+/*!
+ * @brief Enable/disable the microphone input.
+ *
+ * This function enables/disables the current microphone input channel. The
+ * input amplifier is automatically turned off when the microphone input is
+ * disabled.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] leftChannel The left microphone input channel state.
+ * @param[in] rightChannel the right microphone input channel state.
+ *
+ * @retval PMIC_SUCCESS If the microphone input channels were
+ * successfully reconfigured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or microphone input states
+ * were invalid.
+ * @retval PMIC_ERROR If the microphone input channels could
+ * not be reconfigured.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_mic_on_off(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_INPUT_MIC_STATE
+ leftChannel,
+ const PMIC_AUDIO_INPUT_MIC_STATE
+ rightChannel);
+
+/*!
+ * @brief Return the current state of the microphone inputs.
+ *
+ * This function returns the current state (on/off) of the microphone
+ * input channels.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] leftChannel The current left microphone input channel
+ * state.
+ * @param[out] rightChannel the current right microphone input channel
+ * state.
+ *
+ * @retval PMIC_SUCCESS If the microphone input channel states
+ * were successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the microphone input channel states
+ * could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_mic_on_off(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_INPUT_MIC_STATE *
+ const leftChannel,
+ PMIC_AUDIO_INPUT_MIC_STATE *
+ const rightChannel);
+
+/*!
+ * @brief Set the microphone input amplifier mode and gain level.
+ *
+ * This function sets the current microphone input amplifier operating mode
+ * and gain level.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] leftChannelMode The left microphone input amplifier mode.
+ * @param[in] leftChannelGain The left microphone input amplifier gain level.
+ * @param[in] rightChannelMode The right microphone input amplifier mode.
+ * @param[in] rightChannelGain The right microphone input amplifier gain
+ * level.
+ *
+ * @retval PMIC_SUCCESS If the microphone input amplifiers were
+ * successfully reconfigured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or microphone input amplifier
+ * modes or gain levels were invalid.
+ * @retval PMIC_ERROR If the microphone input amplifiers could
+ * not be reconfigured.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_record_gain(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_MIC_AMP_MODE
+ leftChannelMode,
+ const PMIC_AUDIO_MIC_GAIN
+ leftChannelGain,
+ const PMIC_AUDIO_MIC_AMP_MODE
+ rightChannelMode,
+ const PMIC_AUDIO_MIC_GAIN
+ rightChannelGain);
+
+/*!
+ * @brief Get the current microphone input amplifier mode and gain level.
+ *
+ * This function gets the current microphone input amplifier operating mode
+ * and gain level.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] leftChannelMode The left microphone input amplifier mode.
+ * @param[out] leftChannelGain The left microphone input amplifier gain level.
+ * @param[out] rightChannelMode The right microphone input amplifier mode.
+ * @param[out] rightChannelGain The right microphone input amplifier gain
+ * level.
+ *
+ * @retval PMIC_SUCCESS If the microphone input amplifier modes
+ * and gain levels were successfully
+ * retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the microphone input amplifier modes
+ * and gain levels could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_record_gain(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_MIC_AMP_MODE *
+ const leftChannelMode,
+ PMIC_AUDIO_MIC_GAIN *
+ const leftChannelGain,
+ PMIC_AUDIO_MIC_AMP_MODE *
+ const rightChannelMode,
+ PMIC_AUDIO_MIC_GAIN *
+ const rightChannelGain);
+
+/*!
+ * @brief Enable a microphone bias circuit.
+ *
+ * This function enables one of the available microphone bias circuits.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] biasCircuit The microphone bias circuit to be enabled.
+ *
+ * @retval PMIC_SUCCESS If the microphone bias circuit was
+ * successfully enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or selected microphone bias
+ * circuit was invalid.
+ * @retval PMIC_ERROR If the microphone bias circuit could not
+ * be enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_enable_micbias(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_MIC_BIAS
+ biasCircuit);
+
+/*!
+ * @brief Disable a microphone bias circuit.
+ *
+ * This function disables one of the available microphone bias circuits.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] biasCircuit The microphone bias circuit to be disabled.
+ *
+ * @retval PMIC_SUCCESS If the microphone bias circuit was
+ * successfully disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or selected microphone bias
+ * circuit was invalid.
+ * @retval PMIC_ERROR If the microphone bias circuit could not
+ * be disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_disable_micbias(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_MIC_BIAS
+ biasCircuit);
+
+/*@}*/
+
+/*!
+ * @name Audio Playback Using the Voice CODEC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Voice CODEC
+ * to perform audio playback.
+ */
+/*@{*/
+
+/*!
+ * @brief Configure and enable the Voice CODEC mixer.
+ *
+ * This function configures and enables the Voice CODEC mixer.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] rxSecondaryTimeslot The timeslot used for the secondary audio
+ * channel.
+ * @param[in] gainIn The secondary audio channel gain level.
+ * @param[in] gainOut The mixer output gain level.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC mixer was successfully
+ * configured and enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or mixer configuration
+ * was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC mixer could not be
+ * reconfigured or enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_enable_mixer(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_VCODEC_TIMESLOT
+ rxSecondaryTimeslot,
+ const PMIC_AUDIO_VCODEC_MIX_IN_GAIN
+ gainIn,
+ const PMIC_AUDIO_VCODEC_MIX_OUT_GAIN
+ gainOut);
+
+/*!
+ * @brief Disable the Voice CODEC mixer.
+ *
+ * This function disables the Voice CODEC mixer.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC mixer was successfully
+ * disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC mixer could not be
+ * disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_disable_mixer(const PMIC_AUDIO_HANDLE handle);
+
+/*@}*/
+
+/*!
+ * @name Audio Playback Using the Stereo DAC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Stereo DAC
+ * to perform audio playback.
+ */
+/*@{*/
+
+/*!
+ * @brief Configure and enable the Stereo DAC mixer.
+ *
+ * This function configures and enables the Stereo DAC mixer.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] rxSecondaryTimeslot The timeslot used for the secondary audio
+ * channel.
+ * @param[in] gainIn The secondary audio channel gain level.
+ * @param[in] gainOut The mixer output gain level.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC mixer was successfully
+ * configured and enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or mixer configuration
+ * was invalid.
+ * @retval PMIC_ERROR If the Stereo DAC mixer could not be
+ * reconfigured or enabled.
+ */
+PMIC_STATUS pmic_audio_stdac_enable_mixer(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_STDAC_TIMESLOTS
+ rxSecondaryTimeslot,
+ const PMIC_AUDIO_STDAC_MIX_IN_GAIN
+ gainIn,
+ const PMIC_AUDIO_STDAC_MIX_OUT_GAIN
+ gainOut);
+
+/*!
+ * @brief Disable the Stereo DAC mixer.
+ *
+ * This function disables the Stereo DAC mixer.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC mixer was successfully
+ * disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Stereo DAC mixer could not be
+ * disabled.
+ */
+PMIC_STATUS pmic_audio_stdac_disable_mixer(const PMIC_AUDIO_HANDLE handle);
+
+/*@}*/
+
+/*!
+ * @name Audio Output Section Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC audio output
+ * section to support playback.
+ */
+/*@{*/
+
+/*!
+ * @brief Select the audio output ports.
+ *
+ * This function selects the audio output ports to be used. This also enables
+ * the appropriate output amplifiers.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] port The audio output ports to be used.
+ *
+ * @retval PMIC_SUCCESS If the audio output ports were successfully
+ * acquired.
+ * @retval PMIC_PARAMETER_ERROR If the handle or output ports were
+ * invalid.
+ * @retval PMIC_ERROR If the audio output ports could not be
+ * acquired.
+ */
+PMIC_STATUS pmic_audio_output_set_port(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_PORT port);
+
+/*!
+ * @brief Deselect/disable the audio output ports.
+ *
+ * This function disables the audio output ports that were previously enabled
+ * by calling pmic_audio_output_set_port().
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] port The audio output ports to be disabled.
+ *
+ * @retval PMIC_SUCCESS If the audio output ports were successfully
+ * disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or output ports were
+ * invalid.
+ * @retval PMIC_ERROR If the audio output ports could not be
+ * disabled.
+ */
+PMIC_STATUS pmic_audio_output_clear_port(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_PORT port);
+
+/*!
+ * @brief Get the current audio output ports.
+ *
+ * This function retrieves the audio output ports that are currently being
+ * used.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] port The audio output ports currently being used.
+ *
+ * @retval PMIC_SUCCESS If the audio output ports were successfully
+ * retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the audio output ports could not be
+ * retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_port(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_OUTPUT_PORT * const port);
+
+/*!
+ * @brief Set the gain level for the external stereo inputs.
+ *
+ * This function sets the gain levels for the external stereo inputs.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] gain The external stereo input gain level.
+ *
+ * @retval PMIC_SUCCESS If the gain level was successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
+ * @retval PMIC_ERROR If the gain level could not be set.
+ */
+PMIC_STATUS pmic_audio_output_set_stereo_in_gain(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_STEREO_IN_GAIN
+ gain);
+
+/*!
+ * @brief Get the current gain level for the external stereo inputs.
+ *
+ * This function retrieves the current gain levels for the external stereo
+ * inputs.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] gain The current external stereo input gain
+ * level.
+ *
+ * @retval PMIC_SUCCESS If the gain level was successfully
+ * retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the gain level could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_stereo_in_gain(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_STEREO_IN_GAIN *
+ const gain);
+
+/*!
+ * @brief Set the output PGA gain level.
+ *
+ * This function sets the audio output PGA gain level.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] gain The output PGA gain level.
+ *
+ * @retval PMIC_SUCCESS If the gain level was successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
+ * @retval PMIC_ERROR If the gain level could not be set.
+ */
+PMIC_STATUS pmic_audio_output_set_pgaGain(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_PGA_GAIN
+ gain);
+
+/*!
+ * @brief Get the output PGA gain level.
+ *
+ * This function retrieves the current audio output PGA gain level.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] gain The current output PGA gain level.
+ *
+ * @retval PMIC_SUCCESS If the gain level was successfully
+ * retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the gain level could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_pgaGain(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_OUTPUT_PGA_GAIN *
+ const gain);
+
+/*!
+ * @brief Enable the output mixer.
+ *
+ * This function enables the output mixer for the audio stream that
+ * corresponds to the current handle (i.e., the Voice CODEC, Stereo DAC, or
+ * the external stereo inputs).
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the mixer was successfully enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the mixer could not be enabled.
+ */
+PMIC_STATUS pmic_audio_output_enable_mixer(const PMIC_AUDIO_HANDLE handle);
+
+/*!
+ * @brief Disable the output mixer.
+ *
+ * This function disables the output mixer for the audio stream that
+ * corresponds to the current handle (i.e., the Voice CODEC, Stereo DAC, or
+ * the external stereo inputs).
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the mixer was successfully disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the mixer could not be disabled.
+ */
+PMIC_STATUS pmic_audio_output_disable_mixer(const PMIC_AUDIO_HANDLE handle);
+
+/*!
+ * @brief Configure and enable the output balance amplifiers.
+ *
+ * This function configures and enables the output balance amplifiers.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] leftGain The desired left channel gain level.
+ * @param[in] rightGain The desired right channel gain level.
+ *
+ * @retval PMIC_SUCCESS If the output balance amplifiers were
+ * successfully configured and enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or gain levels were invalid.
+ * @retval PMIC_ERROR If the output balance amplifiers could not
+ * be reconfigured or enabled.
+ */
+PMIC_STATUS pmic_audio_output_set_balance(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_BALANCE_GAIN
+ leftGain,
+ const PMIC_AUDIO_OUTPUT_BALANCE_GAIN
+ rightGain);
+
+/*!
+ * @brief Get the current output balance amplifier gain levels.
+ *
+ * This function retrieves the current output balance amplifier gain levels.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] leftGain The current left channel gain level.
+ * @param[out] rightGain The current right channel gain level.
+ *
+ * @retval PMIC_SUCCESS If the output balance amplifier gain levels
+ * were successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the output balance amplifier gain levels
+ * could be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_balance(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_OUTPUT_BALANCE_GAIN *
+ const leftGain,
+ PMIC_AUDIO_OUTPUT_BALANCE_GAIN *
+ const rightGain);
+
+/*!
+ * @brief Configure and enable the output mono adder.
+ *
+ * This function configures and enables the output mono adder.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] mode The desired mono adder operating mode.
+ *
+ * @retval PMIC_SUCCESS If the mono adder was successfully
+ * configured and enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or mono adder mode was
+ * invalid.
+ * @retval PMIC_ERROR If the mono adder could not be reconfigured
+ * or enabled.
+ */
+PMIC_STATUS pmic_audio_output_enable_mono_adder(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_MONO_ADDER_MODE
+ mode);
+
+/*!
+ * @brief Disable the output mono adder.
+ *
+ * This function disables the output mono adder.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the mono adder was successfully
+ * disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the mono adder could not be disabled.
+ */
+PMIC_STATUS pmic_audio_output_disable_mono_adder(const PMIC_AUDIO_HANDLE
+ handle);
+
+/*!
+ * @brief Configure the mono adder output gain level.
+ *
+ * This function configures the mono adder output amplifier gain level.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] gain The desired output gain level.
+ *
+ * @retval PMIC_SUCCESS If the mono adder output amplifier gain
+ * level was successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
+ * @retval PMIC_ERROR If the mono adder output amplifier gain
+ * level could not be reconfigured.
+ */
+PMIC_STATUS pmic_audio_output_set_mono_adder_gain(const PMIC_AUDIO_HANDLE
+ handle,
+ const
+ PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN
+ gain);
+
+/*!
+ * @brief Get the current mono adder output gain level.
+ *
+ * This function retrieves the current mono adder output amplifier gain level.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] gain The current output gain level.
+ *
+ * @retval PMIC_SUCCESS If the mono adder output amplifier gain
+ * level was successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the mono adder output amplifier gain
+ * level could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_mono_adder_gain(const PMIC_AUDIO_HANDLE
+ handle,
+ PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN
+ * const gain);
+
+/*!
+ * @brief Set various audio output section options.
+ *
+ * This function sets one or more audio output section configuration
+ * options. The currently supported options include whether to disable
+ * the non-inverting mono speaker output, enabling the loudspeaker common
+ * bias circuit, enabling detection of headset insertion/removal, and
+ * whether to automatically disable the headset amplifiers when a headset
+ * insertion/removal has been detected.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] config The desired audio output section
+ * configuration options to be set.
+ *
+ * @retval PMIC_SUCCESS If the desired configuration options were
+ * all successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle or configuration options
+ * were invalid.
+ * @retval PMIC_ERROR If the desired configuration options
+ * could not be set.
+ */
+PMIC_STATUS pmic_audio_output_set_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_CONFIG config);
+
+/*!
+ * @brief Clear various audio output section options.
+ *
+ * This function clears one or more audio output section configuration
+ * options.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[in] config The desired audio output section
+ * configuration options to be cleared.
+ *
+ * @retval PMIC_SUCCESS If the desired configuration options were
+ * all successfully cleared.
+ * @retval PMIC_PARAMETER_ERROR If the handle or configuration options
+ * were invalid.
+ * @retval PMIC_ERROR If the desired configuration options
+ * could not be cleared.
+ */
+PMIC_STATUS pmic_audio_output_clear_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_CONFIG
+ config);
+
+/*!
+ * @brief Get the current audio output section options.
+ *
+ * This function retrieves the current audio output section configuration
+ * option settings.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] config The current audio output section
+ * configuration option settings.
+ *
+ * @retval PMIC_SUCCESS If the current configuration options were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the current configuration options
+ * could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_config(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_OUTPUT_CONFIG *
+ const config);
+
+/*!
+ * @brief Enable the phantom ground circuit that is used to help identify
+ * the type of headset that has been inserted.
+ *
+ * This function enables the phantom ground circuit that is used to help
+ * identify the type of headset (e.g., stereo or mono) that has been inserted.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the phantom ground circuit was
+ * successfully enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the phantom ground circuit could not
+ * be enabled.
+ */
+PMIC_STATUS pmic_audio_output_enable_phantom_ground(void);
+
+/*!
+ * @brief Disable the phantom ground circuit that is used to help identify
+ * the type of headset that has been inserted.
+ *
+ * This function disables the phantom ground circuit that is used to help
+ * identify the type of headset (e.g., stereo or mono) that has been inserted.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the phantom ground circuit was
+ * successfully disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the phantom ground circuit could not
+ * be disabled.
+ */
+PMIC_STATUS pmic_audio_output_disable_phantom_ground(void);
+
+/*!
+ * @brief Enable/Disable fm output
+ *
+ * This function enables/disables the fm output.
+ *
+ * @param[in] enable True to enable and false to disable
+ *
+ * @retval PMIC_SUCCESS If the fm output was
+ * successfully enable or disabled
+ * @retval PMIC_ERROR If the operation fails
+ */
+PMIC_STATUS pmic_audio_fm_output_enable(bool enable);
+
+/*@}*/
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_ARCH_MXC_PMIC_AUDIO_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/pmic_convity.h b/arch/arm/plat-mxc/include/mach/pmic_convity.h
new file mode 100644
index 000000000000..b88bb54bad26
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/pmic_convity.h
@@ -0,0 +1,873 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_PMIC_CONVITY_H__
+#define __ASM_ARCH_MXC_PMIC_CONVITY_H__
+
+/*!
+ * @defgroup PMIC_CONNECTIVITY PMIC Connectivity Driver
+ * @ingroup PMIC_DRVRS
+ */
+
+/*!
+ * @file arch-mxc/pmic_convity.h
+ * @brief External definitions for the PMIC Connectivity Client driver.
+ *
+ * The PMIC Connectivity driver and this API were developed to support the
+ * external connectivity capabilities of several power management ICs that
+ * are available from Freescale Semiconductor, Inc.
+ *
+ * The following operating modes, in terms of external connectivity, are
+ * supported:
+ *
+ *
+ *
+ * @ingroup PMIC_CONNECTIVITY
+ */
+
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+/***************************************************************************
+ * TYPEDEFS AND ENUMERATIONS *
+ ***************************************************************************/
+
+/*!
+ * @name General Setup and Configuration Typedefs and Enumerations
+ * Typedefs and enumerations that are used for initial access to and
+ * configuration of the PMIC Connectivity hardware.
+ */
+/*@{*/
+
+#define DEBUG_CONVITY
+
+/*!
+ * @typedef PMIC_CONVITY_HANDLE
+ * @brief Define typedef for a handle to the PMIC Connectivity hardware.
+ *
+ * Define a "handle" that is returned when the PMIC Connectivity hardware
+ * is opened. This handle grants exclusive access to the PMIC Connectivity
+ * hardware and must be used in all subsequent function calls. When access
+ * to the PMIC Connectivity hardware is no longer required, then a close
+ * operation must be done with this handle. The handle is no longer valid
+ * if the close operation was successful.
+ */
+typedef long PMIC_CONVITY_HANDLE;
+
+/*!
+ * @enum PMIC_CONVITY_MODE
+ * @brief Select the main Connectivity operating mode.
+ *
+ * Defines all possible PMIC Connectivity main operating modes. Only one of
+ * these modes can be active at a time.
+ */
+typedef enum {
+ USB, /*!< Select USB mode (this is also the Reset/Default
+ mode). */
+ RS232, /*!< Select RS-232 mode. for SC55112 */
+ RS232_1, /*!< Select RS-232_1 mode. */
+ RS232_2, /*!< Select RS-232_2 mode. */
+ CEA936_MONO, /*!< Select CE-936 Mono mode . */
+ CEA936_STEREO, /*!< Select CE-936 Stereo mode . */
+ CEA936_TEST_RIGHT, /*!< Select CE-936 Right Channel Test mode
+ . */
+ CEA936_TEST_LEFT /*!< Select CE-936 Left Channel Test mode
+ . */
+} PMIC_CONVITY_MODE;
+
+/*!
+ * @enum PMIC_CONVITY_EVENTS
+ * @brief Identify the connectivity events that have been detected and should
+ * be handled.
+ *
+ * Defines all possible PMIC Connectivity events. Multiple events may be
+ * selected when defining a mask.
+ */
+typedef enum {
+ USB_DETECT_4V4_RISE = 1, /*!< Detected 4.4V rising edge. */
+ USB_DETECT_4V4_FALL = 2, /*!< Detected 4.4V falling edge. */
+ USB_DETECT_2V0_RISE = 4, /*!< Detected 2.0V rising edge. */
+ USB_DETECT_2V0_FALL = 8, /*!< Detected 2.0V falling edge. */
+ USB_DETECT_0V8_RISE = 16, /*!< Detected 0.8V rising edge. */
+ USB_DETECT_0V8_FALL = 32, /*!< Detected 0.8V falling edge. */
+ USB_DETECT_MINI_A = 64, /*!< Detected USB mini A plug. */
+ USB_DETECT_MINI_B = 128, /*!< Detected USB mini B plug. */
+ USB_DETECT_NON_USB_ACCESSORY = 256, /*!< Detected a non-USB connection
+ . */
+ USB_DETECT_FACTORY_MODE = 512, /*!< Detected a factory-mode
+ connection . */
+ USB_DP_HI = 1024,
+
+ USB_DM_HI = 2048
+} PMIC_CONVITY_EVENTS;
+
+/*!
+ * @typedef PMIC_CONVITY_CALLBACK
+ * @brief Typedef for PMIC Connectivity event notification callback function.
+ *
+ * Define a typedef for the PMIC Connectivity event notification callback
+ * function. The signalled events are passed to the function as the first
+ * argument. The callback function should then process whatever events it
+ * can and then return the set of unhandled events (if any).
+ */
+typedef void (*PMIC_CONVITY_CALLBACK) (const PMIC_CONVITY_EVENTS event);
+
+/*@}*/
+
+/*!
+ * @name USB and USB On-The-Go Mode-specific Typedefs and Enumerations
+ * Typedefs and enumerations that are used only for setting up and controlling
+ * the USB and USB On-The-Go modes of operation.
+ */
+/*@{*/
+
+/*!
+ * @enum PMIC_CONVITY_USB_DEVICE_TYPE
+ * @brief Select the USB device type (either A or B).
+ *
+ * Defines all possible USB device types. This must match the physical
+ * connector being used.
+ */
+typedef enum {
+ USB_A_DEVICE,
+ USB_B_DEVICE
+} PMIC_CONVITY_USB_DEVICE_TYPE;
+
+/*!
+ * @enum PMIC_CONVITY_USB_SPEED
+ * @brief Select the USB transceiver operating speed.
+ *
+ * Defines all possible USB transceiver operating speeds. Only one
+ * speed setting may be used at a time.
+ */
+typedef enum {
+ USB_LOW_SPEED, /*!< Select 1.5 Mbps. */
+ USB_FULL_SPEED, /*!< Select 12 Mbps. */
+ USB_HIGH_SPEED /*!< Select 480 Mbps <b>(currently
+ not supported)</b>. */
+} PMIC_CONVITY_USB_SPEED;
+
+/*!
+ * @enum PMIC_CONVITY_USB_MODE
+ * @brief Select the USB transceiver operating mode.
+ *
+ * Defines all possible USB transceiver operating modes. Only one
+ * mode may be used at a time. The selected mode, in combination with
+ * the USB bus speed, determines the selection of pull-up and pull-down
+ * resistors.
+ */
+typedef enum {
+ USB_HOST,
+ USB_PERIPHERAL
+} PMIC_CONVITY_USB_MODE;
+
+/*!
+ * @enum PMIC_CONVITY_USB_POWER_IN
+ * @brief Select the USB transceiver's power regulator input source.
+ *
+ * Defines all possible input power sources for the USB transceiver power
+ * regulator. Only one power supply source may be selected at a time.
+ */
+typedef enum {
+
+ USB_POWER_INTERNAL_BOOST, /*!< Select internal power source
+ with boost. */
+
+ USB_POWER_VBUS, /*!< Select VBUS power source. */
+
+ USB_POWER_INTERNAL /*!< Select internal power source
+ . */
+} PMIC_CONVITY_USB_POWER_IN;
+
+/*!
+ * @enum PMIC_CONVITY_USB_POWER_OUT
+ * @brief Select the USB transceiver power regulator output voltage.
+ *
+ * Defines all possible output voltages for the USB transceiver power
+ * regulator. Only one power output voltage level may be selected at
+ * a time.
+ */
+typedef enum {
+ USB_POWER_2V775, /*!< Select 2.775V output voltage
+ . */
+ USB_POWER_3V3 /*!< Select 3.3V output voltage. */
+} PMIC_CONVITY_USB_POWER_OUT;
+
+/*!
+ * @enum PMIC_CONVITY_USB_TRANSCEIVER_MODE
+ * @brief Select the USB transceiver operating mode.
+ *
+ * Defines all valid USB transceiver operating modes. Only one of the
+ * following USB transceiver modes may be selected at a time.
+ */
+typedef enum {
+ USB_TRANSCEIVER_OFF, /*!< USB transceiver currently off
+ . */
+ USB_SINGLE_ENDED_UNIDIR, /*!< Select Single-ended
+ unidirectional transmit mode. */
+ USB_SINGLE_ENDED_UNIDIR_TX, /*!< Select Single-ended
+ unidirectional transmit mode. */
+ USB_SINGLE_ENDED_UNIDIR_RX, /*!< Select Single-ended
+ unidirectional receive mode. */
+ USB_SINGLE_ENDED_BIDIR, /*!< Select Single-ended
+ bidirectional transmit mode. */
+ USB_SINGLE_ENDED_LOW, /*!< Select USB SE0 mode. */
+ USB_DIFFERENTIAL_UNIDIR_TX, /*!< Select Differential
+ unidirectional transmit mode
+ . */
+ USB_DIFFERENTIAL_UNIDIR, /*!< Select Differential
+ unidirectional transmit mode
+ . */
+
+ USB_DIFFERENTIAL_UNIDIR_RX, /*!< Select Differential
+ unidirectional receive mode. */
+ USB_DIFFERENTIAL_BIDIR, /*!< Select Differential
+ bidirectional transmit mode
+ */
+ USB_SUSPEND_ON, /*!< Select Suspend mode. */
+ USB_SUSPEND_OFF, /*!< Terminate Suspend mode. */
+ USB_OTG_SRP_DLP_START, /*!< Start USB On-The-Go Session
+ Request Protocol using Data
+ Line Pulsing. */
+ USB_OTG_SRP_DLP_STOP /*!< Terminate USB On-The-Go Session
+ Request Protocol using Data
+ Line Pulsing. */
+} PMIC_CONVITY_USB_TRANSCEIVER_MODE;
+
+/*!
+ * @enum PMIC_CONVITY_USB_OTG_CONFIG
+ * @brief Select the USB On-The-Go configuration options.
+ *
+ * Defines all possible USB On-The-Go configuration options. Multiple
+ * configuration options may be selected at the same time. However, only one
+ * VBUS current limit may be selected at a time. Selecting more than one
+ * VBUS current limit will result in undefined and implementation-dependent
+ * behavior.
+ */
+typedef enum {
+ USB_OTG_SE0CONN = 0x00001, /*!< Enable automatic
+ connection of a pull-up
+ resistor to VUSB when the
+ SE0 condition is detected. */
+ USB_OTG_DLP_SRP = 0x00002, /*!< Enable use of the hardware
+ timer to control the
+ duration of the data line
+ pulse during the session
+ request protocol. */
+ USB_PULL_OVERRIDE = 0x00004, /*!< Enable automatic disconnect
+ of pull-up and pull-down
+ resistors when transmitter
+ is enabled. */
+
+ USB_DP150K_PU = 0x00008,
+
+ USB_VBUS_CURRENT_LIMIT_HIGH = 0x00010, /*!< Select current limit to 200mA
+ for VBUS regulator. */
+ USB_VBUS_CURRENT_LIMIT_LOW = 0x00020, /*!< Select low current limit
+ for VBUS regulator. */
+ USB_VBUS_CURRENT_LIMIT_LOW_10MS = 0x00040, /*!< Select low current limit
+ for VBUS regulator for
+ 10 ms . */
+ USB_VBUS_CURRENT_LIMIT_LOW_20MS = 0x00080, /*!< Select low current limit
+ for VBUS regulator for
+ 20 ms . */
+ USB_VBUS_CURRENT_LIMIT_LOW_30MS = 0x00100, /*!< Select low current limit
+ for VBUS regulator for
+ 30 ms . */
+ USB_VBUS_CURRENT_LIMIT_LOW_40MS = 0x00200, /*!< Select low current limit
+ for VBUS regulator for
+ 40 ms . */
+ USB_VBUS_CURRENT_LIMIT_LOW_50MS = 0x00400, /*!< Select low current limit
+ for VBUS regulator for
+ 50 ms . */
+ USB_VBUS_CURRENT_LIMIT_LOW_60MS = 0x00800, /*!< Select low current limit
+ for VBUS regulator for
+ 60 ms . */
+
+ USB_VBUS_PULLDOWN = 0x01000, /*!< Enable VBUS pull-down. */
+
+ USB_USBCNTRL = 0x02000,
+
+ USB_UDP_PD = 0x04000,
+
+ USB_UDM_PD = 0x08000,
+
+ USB_PU = 0x10000,
+
+ USBXCVREN = 0x20000
+} PMIC_CONVITY_USB_OTG_CONFIG;
+/*@}*/
+
+/*!
+ * @name RS-232 Mode-specific Typedefs and Enumerations
+ * Typedefs and enumerations that are used only for setting up and controlling
+ * the RS-232 mode of operation.
+ */
+/*@{*/
+
+/*!
+ * @enum PMIC_CONVITY_RS232_EXTERNAL
+ * @brief Select the RS-232 transceiver external connections.
+ *
+ * Defines all valid RS-232 transceiver external RX/TX connection options.
+ * Only one connection mode may be selected at a time.
+ */
+typedef enum {
+ RS232_TX_UDM_RX_UDP, /*!< Select RS-232 TX on UDM */
+ RS232_TX_UDP_RX_UDM, /*!< Select RS-232 TX on UDP
+ . */
+ RS232_TX_RX_EXTERNAL_DEFAULT /*!< Use power on default. */
+} PMIC_CONVITY_RS232_EXTERNAL;
+
+/*!
+ * @enum PMIC_CONVITY_RS232_INTERNAL
+ * @brief Select the RS-232 transceiver internal connections.
+ *
+ * Defines all valid RS-232 transceiver internal RX/TX connection options.
+ * Only one connection mode can be selected at a time.
+ */
+typedef enum {
+ RS232_TX_USE0VM_RX_UDATVP, /*!< Select RS-232 TX from USE0VM
+ . */
+ RS232_TX_UDATVP_RX_URXVM, /*!< Select RS-232 TX from UDATVP
+ . */
+ RS232_TX_UTXDI_RX_URXDO, /*!< Select RS-232 TX from UTXDI
+ . */
+ RS232_TX_RX_INTERNAL_DEFAULT /*!< Use power on default. */
+} PMIC_CONVITY_RS232_INTERNAL;
+
+/*@}*/
+
+/*!
+ * @name CEA-936 Mode-specific Typedefs and Enumerations
+ * Typedefs and enumerations that are used only for setting up and controlling
+ * the CEA-936 mode of operation.
+ */
+/*@{*/
+
+/*!
+ * @enum PMIC_CONVITY_CEA936_EXIT_SIGNAL
+ * @brief Select the CEA-936 mode exit signal.
+ *
+ * Defines all valid CEA-936 connection termination signals. Only one
+ * termination signal can be selected at a time.
+ */
+typedef enum {
+ CEA936_UID_NO_PULLDOWN, /*!< No UID pull-down . */
+ CEA936_UID_PULLDOWN_6MS, /*!< UID pull-down for 6 ms (+/-2 ms)
+ . */
+ CEA936_UID_PULLDOWN, /*!< UID pulled down . */
+ CEA936_UDMPULSE /*!< UDM pulsed . */
+} PMIC_CONVITY_CEA936_EXIT_SIGNAL;
+
+/*@}*/
+
+/***************************************************************************
+ * PMIC API DEFINITIONS *
+ ***************************************************************************/
+
+/*!
+ * @name General Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Connectivity
+ * hardware.
+ */
+/*@{*/
+
+/*!
+ * @brief Request exclusive access to the PMIC Connectivity hardware.
+ *
+ * Attempt to open and gain exclusive access to the PMIC Connectivity
+ * hardware. An initial operating mode (e.g., USB or RS-232) must also
+ * be specified.
+ *
+ * If the open request is successful, then a numeric handle is returned
+ * and this handle must be used in all subsequent function calls. The
+ * same handle must also be used in the close call when use of the PMIC
+ * connectivity hardware is no longer required.
+ *
+ * The open request will fail if another thread has already obtained the
+ * device handle and has not yet called pmic_convity_close() with it.
+ *
+ * @param handle Device handle to be used for subsequent PMIC
+ * Connectivity API calls.
+ * @param mode Initial connectivity operating mode.
+ *
+ * @retval PMIC_SUCCESS If the open request was successful
+ * @retval PMIC_ERROR If the connectivity hardware cannot be opened.
+ */
+PMIC_STATUS pmic_convity_open(PMIC_CONVITY_HANDLE * const handle,
+ const PMIC_CONVITY_MODE mode);
+
+/*!
+ * @brief Terminate further access to the PMIC Connectivity hardware.
+ *
+ * Terminate further access to the PMIC Connectivity hardware. This also
+ * allows another thread to successfully call pmic_convity_open() to gain
+ * access.
+ *
+ * @param handle Device handle from open() call.
+ *
+ * @retval PMIC_SUCCESS If the close request was successful.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_convity_close(const PMIC_CONVITY_HANDLE handle);
+
+/*!
+ * @brief Set the PMIC Connectivity main operating mode.
+ *
+ * Change the current operating mode of the PMIC Connectivity hardware.
+ * The available connectivity operating modes are hardware-dependent and
+ * consists of one or more of the following: USB (including USB On-the-Go),
+ * RS-232, and CEA-936. Requesting an operating mode that is not supported
+ * by the PMIC hardware will return PMIC_NOT_SUPPORTED.
+ *
+ * @param handle Device handle from open() call.
+ * @param mode Desired operating mode.
+ *
+ * @retval PMIC_SUCCESS If the requested mode was successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_NOT_SUPPORTED If the PMIC hardware does not support
+ * the desired operating mode.
+ */
+PMIC_STATUS pmic_convity_set_mode(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_MODE mode);
+
+/*!
+ * @brief Get the current PMIC Connectivity main operating mode.
+ *
+ * Get the current operating mode for the PMIC Connectivity hardware.
+ *
+ * @param handle Device handle from open() call.
+ * @param mode The current PMIC Connectivity operating mode.
+ *
+ * @retval PMIC_SUCCESS If the requested mode was successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_convity_get_mode(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_MODE * const mode);
+
+/*!
+ * @brief Reset the Connectivity hardware to it's power on state.
+ *
+ * Restore all registers to the initial power-on/reset state.
+ *
+ * @param handle Device handle from open() call.
+ *
+ * @retval PMIC_SUCCESS If the reset was successful.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_convity_reset(const PMIC_CONVITY_HANDLE handle);
+
+/*!
+ * @brief Set the Connectivity callback function.
+ *
+ * Register a callback function that will be used to signal PMIC Connectivity
+ * events. For example, the USB subsystem should register a callback function
+ * in order to be notified of device connect/disconnect events. Note, however,
+ * that non-USB events may also be signalled depending upon the PMIC hardware
+ * capabilities. Therefore, the callback function must be able to properly
+ * handle all of the possible events if support for non-USB peripherals is
+ * also to be included.
+ *
+ * @param handle Device handle from open() call.
+ * @param func A pointer to the callback function.
+ * @param eventMask A mask selecting events to be notified.
+ *
+ * @retval PMIC_SUCCESS If the callback was successfully registered.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the eventMask is invalid.
+ */
+PMIC_STATUS pmic_convity_set_callback(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_CALLBACK func,
+ const PMIC_CONVITY_EVENTS eventMask);
+
+/*!
+ * @brief Deregisters the existing Connectivity callback function.
+ *
+ * Deregister the callback function that was previously registered by calling
+ * pmic_convity_set_callback().
+ *
+ * @param handle Device handle from open() call.
+ *
+ * @retval PMIC_SUCCESS If the callback was successfully deregistered.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_convity_clear_callback(const PMIC_CONVITY_HANDLE handle);
+
+/*!
+ * @brief Get the current Connectivity callback function settings.
+ *
+ * Get the current callback function and event mask.
+ *
+ * @param handle Device handle from open() call.
+ * @param func The current callback function.
+ * @param eventMask The current event selection mask.
+ *
+ * @retval PMIC_SUCCESS If the callback information was successfully
+ * retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_convity_get_callback(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_CALLBACK * const func,
+ PMIC_CONVITY_EVENTS * const eventMask);
+
+/*@}*/
+
+/***************************************************************************/
+
+/*!
+ * @name USB and USB On-The-Go APIs
+ * USB Connectivity mode-specific configuration and setup functions.
+ */
+/*@{*/
+
+/*!
+ * @brief Set the USB transceiver's operating speed.
+ *
+ * Set the USB transceiver speed.
+ *
+ * @param handle Device handle from open() call.
+ * @param speed The desired USB transceiver speed.
+ *
+ * @retval PMIC_SUCCESS If the transceiver speed was successfully
+ * set.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_NOT_SUPPORTED If the high speed (480 Mbps) mode is
+ * requested.
+ */
+PMIC_STATUS pmic_convity_usb_set_speed(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_SPEED speed);
+
+/*!
+ * This function enables/disables VUSB and VBUS output.
+ * This API configures the VUSBEN and VBUSEN bits of USB register
+ *
+ * @param handle Device handle from open() call.
+ * @param out_type true, for VUSB
+ * false, for VBUS
+ * @param out if true, output is enabled
+ * if false, output is disabled
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+
+PMIC_STATUS pmic_convity_set_output(const PMIC_CONVITY_HANDLE handle,
+ bool out_type, bool out);
+
+/*!
+ * @brief Get the USB transceiver's operating speed.
+ *
+ * Get the USB transceiver speed.
+ *
+ * @param handle Device handle from open() call.
+ * @param speed The current USB transceiver speed.
+ * @param mode The current USB transceiver mode.
+ *
+ * @retval PMIC_SUCCESS If the transceiver speed was successfully
+ * set.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * obtained
+ */
+PMIC_STATUS pmic_convity_usb_get_speed(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_USB_SPEED * const speed,
+ PMIC_CONVITY_USB_MODE * const mode);
+
+/*!
+ * @brief Set the USB transceiver's power supply configuration.
+ *
+ * Set the USB transceiver's power supply configuration.
+ *
+ * @param handle Device handle from open() call.
+ * @param pwrin USB transceiver regulator input power source.
+ * @param pwrout USB transceiver regulator output power level.
+ *
+ * @retval PMIC_SUCCESS If the USB transceiver's power supply
+ * configuration was successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_NOT_SUPPORTED If the PMIC hardware does not support
+ * the desired configuration.
+ */
+PMIC_STATUS pmic_convity_usb_set_power_source(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_POWER_IN
+ pwrin,
+ const PMIC_CONVITY_USB_POWER_OUT
+ pwrout);
+
+/*!
+ * @brief Get the USB transceiver's power supply configuration.
+ *
+ * Get the USB transceiver's current power supply configuration.
+ *
+ * @param handle Device handle from open() call.
+ * @param pwrin USB transceiver regulator input power source
+ * @param pwrout USB transceiver regulator output power level
+ *
+ * @retval PMIC_SUCCESS If the USB transceiver's power supply
+ * configuration was successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_convity_usb_get_power_source(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_USB_POWER_IN *
+ const pwrin,
+ PMIC_CONVITY_USB_POWER_OUT *
+ const pwrout);
+
+/*!
+ * @brief Set the current USB transceiver operating mode.
+ *
+ * Set the USB transceiver's operating mode.
+ *
+ * @param handle Device handle from open() call.
+ * @param mode Desired operating mode.
+ *
+ * @retval PMIC_SUCCESS If the USB transceiver's operating mode
+ * was successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_NOT_SUPPORTED If the desired USB transceiver mode is
+ * not supported by the PMIC hardware.
+ */
+PMIC_STATUS pmic_convity_usb_set_xcvr(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_TRANSCEIVER_MODE
+ mode);
+
+/*!
+ * @brief Get the current USB transceiver operating mode.
+ *
+ * Get the USB transceiver's current operating mode.
+ *
+ * @param handle Device handle from open() call.
+ * @param mode Current operating mode.
+ *
+ * @retval PMIC_SUCCESS If the USB transceiver's operating mode
+ * was successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_convity_usb_get_xcvr(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_USB_TRANSCEIVER_MODE *
+ const mode);
+
+/*!
+ * @brief Set the current USB On-The-Go data line pulse duration (ms).
+ *
+ * Set the Data Line Pulse duration (in milliseconds) for the USB OTG
+ * Session Request Protocol.
+ *
+ * Note that for mc13783 the duration is fixed at 7.5 ms and calling this
+ * function will simply return PMIC_NOT_SUPPORTED.
+ *
+ * @param handle Device handle from open() call.
+ * @param duration The data line pulse duration (ms).
+ *
+ * @retval PMIC_SUCCESS If the pulse duration was successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the data line pulse
+ * duration is invalid.
+ * @retval PMIC_NOT_SUPPORTED If the desired data line pulse duration
+ * is not supported by the PMIC hardware.
+ */
+PMIC_STATUS pmic_convity_usb_otg_set_dlp_duration(const PMIC_CONVITY_HANDLE
+ handle,
+ const unsigned int duration);
+
+/*!
+ * @brief Get the current USB On-The-Go data line pulse duration (ms).
+ *
+ * Get the current Data Line Pulse duration (in milliseconds) for the USB
+ * OTG Session Request Protocol.
+ *
+ * Note that the Data Line Pulse duration is fixed at 7.5 ms for the mc13783
+ * PMIC. Therefore, calling this function while using the mc13783 PMIC will
+ * simply return PMIC_NOT_SUPPORTED.
+ *
+ * @param handle Device handle from open() call.
+ * @param duration The data line pulse duration (ms).
+ *
+ * @retval PMIC_SUCCESS If the pulse duration was successfully
+ * obtained.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_NOT_SUPPORTED If called using the mc13783 PMIC.
+ */
+PMIC_STATUS pmic_convity_usb_otg_get_dlp_duration(const PMIC_CONVITY_HANDLE
+ handle,
+ unsigned int *const duration);
+
+/*!
+ * @brief Start the USB OTG Host Negotiation Protocol (HNP) process.
+ *
+ * This function must be called during the start of the HNP process to
+ * properly reconfigure the pull-up resistor on the D+ line for both
+ * the USB A and B devices.
+ *
+ * @param handle device handle from open() call
+ * @param deviceType the USB device type (either A or B)
+ *
+ * @return PMIC_SUCCESS if the HNP was successfully started
+ */
+PMIC_STATUS pmic_convity_usb_otg_begin_hnp(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_DEVICE_TYPE
+ deviceType);
+
+/*!
+ * @brief Complete the USB OTG Host Negotiation Protocol (HNP) process.
+ *
+ * This function must be called during the end of the HNP process to
+ * properly reconfigure the pull-up resistor on the D+ line for both
+ * the USB A and B devices.
+ *
+ * @param handle device handle from open() call
+ * @param deviceType the USB device type (either A or B)
+ *
+ * @return PMIC_SUCCESS if the HNP was successfully ended
+ */
+PMIC_STATUS pmic_convity_usb_otg_end_hnp(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_DEVICE_TYPE
+ deviceType);
+
+/*!
+ * @brief Set the current USB On-The-Go configuration.
+ *
+ * Set the USB On-The-Go (OTG) configuration. Multiple configuration settings
+ * may be OR'd together in a single call. However, selecting conflicting
+ * settings (e.g., multiple VBUS current limits) will result in undefined
+ * behavior.
+ *
+ * @param handle Device handle from open() call.
+ * @param cfg Desired USB OTG configuration.
+ *
+ * @retval PMIC_SUCCESS If the OTG configuration was successfully
+ * set.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_NOT_SUPPORTED If the desired USB OTG configuration is
+ * not supported by the PMIC hardware.
+ */
+PMIC_STATUS pmic_convity_usb_otg_set_config(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_OTG_CONFIG
+ cfg);
+
+/*!
+ * @brief Clear the current USB On-The-Go configuration.
+ *
+ * Clears the USB On-The-Go (OTG) configuration. Multiple configuration settings
+ * may be OR'd together in a single call. However, selecting conflicting
+ * settings (e.g., multiple VBUS current limits) will result in undefined
+ * behavior.
+ *
+ * @param handle Device handle from open() call.
+ * @param cfg USB OTG configuration settings to be cleared.
+ *
+ * @retval PMIC_SUCCESS If the OTG configuration was successfully
+ * cleared.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_NOT_SUPPORTED If the desired USB OTG configuration is
+ * not supported by the PMIC hardware.
+ */
+PMIC_STATUS pmic_convity_usb_otg_clear_config(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_OTG_CONFIG
+ cfg);
+
+/*!
+ * @brief Get the current USB On-The-Go configuration.
+ *
+ * Get the current USB On-The-Go (OTG) configuration.
+ *
+ * @param handle Device handle from open() call.
+ * @param cfg The current USB OTG configuration.
+ *
+ * @retval PMIC_SUCCESS If the OTG configuration was successfully
+ * retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_convity_usb_otg_get_config(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_USB_OTG_CONFIG *
+ const cfg);
+
+/*@}*/
+
+/***************************************************************************/
+
+/*!
+ * @name RS-232 APIs
+ * RS-232 Connectivity mode-specific configuration and setup functions.
+ */
+/*@{*/
+
+/*!
+ * @brief Set the current RS-232 operating configuration.
+ *
+ * Set the connectivity interface to the selected RS-232 operating mode.
+ * Note that the RS-232 operating mode will be automatically overridden
+ * if the USB_EN is asserted at any time (e.g., when a USB device is
+ * attached).
+ *
+ * @param handle Device handle from open() call.
+ * @param cfgInternal RS-232 transceiver internal connections.
+ * @param cfgExternal RS-232 transceiver external connections.
+ *
+ * @retval PMIC_SUCCESS If the requested RS-232 mode was set.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_NOT_SUPPORTED If the desired RS-232 configuration is
+ * not supported by the PMIC hardware.
+ */
+PMIC_STATUS pmic_convity_rs232_set_config(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_RS232_INTERNAL
+ cfgInternal,
+ const PMIC_CONVITY_RS232_EXTERNAL
+ cfgExternal);
+
+/*!
+ * @brief Get the current RS-232 operating configuration.
+ *
+ * Get the connectivity interface's current RS-232 operating mode.
+ *
+ * @param handle Device handle from open() call.
+ * @param cfgInternal RS-232 transceiver internal connections.
+ * @param cfgExternal RS-232 transceiver external connections.
+ *
+ * @retval PMIC_SUCCESS If the requested RS-232 mode was retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_convity_rs232_get_config(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_RS232_INTERNAL *
+ const cfgInternal,
+ PMIC_CONVITY_RS232_EXTERNAL *
+ const cfgExternal);
+
+/***************************************************************************/
+
+/*@}*/
+
+/*!
+ * @name CE-936 APIs
+ * CE-936 Connectivity mode-specific configuration and setup functions.
+ */
+/*@{*/
+
+/*!
+ * @brief Send a signal to exit CEA-936 mode.
+ *
+ * Signal the attached device to exit the current CEA-936 operating mode.
+ * Returns an error if the current operating mode is not CEA-936.
+ *
+ * @param handle Device handle from open() call.
+ * @param signal Type of exit signal to be sent.
+ *
+ * @retval PMIC_SUCCESS If the CEA-936 exit mode signal was sent.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_NOT_SUPPORTED If the desired CEA-936 exit mode signal
+ * is not supported by the PMIC hardware.
+ */
+PMIC_STATUS pmic_convity_cea936_exit_signal(const PMIC_CONVITY_HANDLE handle,
+ const
+ PMIC_CONVITY_CEA936_EXIT_SIGNAL
+ signal);
+
+/*@}*/
+
+#endif /* __ASM_ARCH_MXC_PMIC_CONVITY_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/pmic_power.h b/arch/arm/plat-mxc/include/mach/pmic_power.h
new file mode 100644
index 000000000000..60f23ab9c888
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/pmic_power.h
@@ -0,0 +1,1358 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+#ifndef __ASM_ARCH_MXC_PMIC_POWER_H__
+#define __ASM_ARCH_MXC_PMIC_POWER_H__
+
+/*!
+ * @defgroup PMIC_POWER PMIC Power Driver
+ * @ingroup PMIC_DRVRS
+ */
+
+/*!
+ * @file arch-mxc/pmic_power.h
+ * @brief This is the header of PMIC power driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+#include <linux/ioctl.h>
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+/*!
+ * @name IOCTL user space interface
+ */
+/*! @{ */
+
+/*!
+ * Turn on a regulator.
+ */
+#define PMIC_REGULATOR_ON _IOWR('p', 0xf0, int)
+
+/*!
+ * Turn off a regulator.
+ */
+#define PMIC_REGULATOR_OFF _IOWR('p', 0xf1, int)
+
+/*!
+ * Set regulator configuration.
+ */
+#define PMIC_REGULATOR_SET_CONFIG _IOWR('p', 0xf2, int)
+
+/*!
+ * Get regulator configuration.
+ */
+#define PMIC_REGULATOR_GET_CONFIG _IOWR('p', 0xf3, int)
+
+/*!
+ * Miscellaneous Power Test.
+ */
+#define PMIC_POWER_CHECK_MISC _IOWR('p', 0xf4, int)
+
+/*! @} */
+
+/*!
+ * This enumeration define all power interrupts
+ */
+typedef enum {
+ /*!
+ * BP turn on threshold detection
+ */
+ PWR_IT_BPONI = 0,
+ /*!
+ * End of life / low battery detect
+ */
+ PWR_IT_LOBATLI,
+ /*!
+ * Low battery warning
+ */
+ PWR_IT_LOBATHI,
+ /*!
+ * ON1B event
+ */
+ PWR_IT_ONOFD1I,
+ /*!
+ * ON2B event
+ */
+ PWR_IT_ONOFD2I,
+ /*!
+ * ON3B event
+ */
+ PWR_IT_ONOFD3I,
+ /*!
+ * System reset
+ */
+ PWR_IT_SYSRSTI,
+ /*!
+ * Power ready
+ */
+ PWR_IT_PWRRDYI,
+ /*!
+ * Power cut event
+ */
+ PWR_IT_PCI,
+ /*!
+ * Warm start event
+ */
+ PWR_IT_WARMI,
+ /*!
+ * Memory hold event
+ */
+} t_pwr_int;
+
+/*!
+ * VHOLD regulator output voltage setting.
+ */
+typedef enum {
+ VH_1_875V, /*!< 1.875V */
+ VH_2_5V, /*!< 2.5V */
+ VH_1_55V, /*!< 1.55V */
+ VH_PASSTHROUGH, /*!< Pass-through mode */
+} t_vhold_voltage;
+
+/*!
+ * PMIC power control configuration.
+ */
+
+typedef struct {
+ bool pc_enable; /*!< Power cut enable */
+ unsigned char pc_timer; /*!< Power cut timer value */
+ bool pc_count_enable; /*!< Power cut counter enable,
+ If TURE, Power cuts are disabled
+ when pc_count > pc_max_count;
+ If FALSE, Power cuts are not
+ disabled when
+ pc_count > pc_max_count */
+ unsigned char pc_count; /*!< Power cut count */
+ unsigned char pc_max_count; /*!< Power cut maximum count */
+ bool warm_enable; /*!< User Off state enable */
+ bool user_off_pc; /*!< Automatic transition to user off
+ during power cut */
+ bool clk_32k_enable; /*!< 32 kHz output buffer enable
+ during memory hold */
+ bool clk_32k_user_off; /*!< Keeps the CLK32KMCU active during
+ user off power cut modes */
+ bool en_vbkup1; /*!< enable VBKUP1 regulator */
+ bool auto_en_vbkup1; /*!< automatically enable VBKUP1
+ regulator in the memory hold
+ and user of modes */
+ t_vhold_voltage vhold_voltage; /*!< output voltage for VBKUP1 */
+ bool en_vbkup2; /*!< enable VBKUP2 regulator */
+ bool auto_en_vbkup2; /*!< automatically enable VBKUP2
+ regulator in the memory hold
+ and user of modes */
+ t_vhold_voltage vhold_voltage2; /*!< output voltage for VBKUP2 */
+ unsigned char mem_timer; /*!< duration of the memory hold
+ timer */
+ bool mem_allon; /*!< memory hold timer infinity mode,
+ If TRUE, the memory hold timer
+ will be set to infinity and
+ the mem_timer filed will be
+ ignored */
+} t_pc_config;
+
+/*!
+ * brief PMIC regulators.
+ */
+
+typedef enum {
+ SW_SW1A = 0, /*!< SW1A or SW1 */
+ SW_SW1B, /*!< SW1B */
+ SW_SW2A, /*!< SW2A or SW2 */
+ SW_SW2B, /*!< SW2B */
+ SW_SW3, /*!< SW3 */
+ SW_PLL, /*!< PLL */
+ REGU_VAUDIO, /*!< VAUDIO */
+ REGU_VIOHI, /*!< VIOHI */
+ REGU_VIOLO, /*!< VIOLO */
+ REGU_VDIG, /*!< VDIG */
+ REGU_VGEN, /*!< VGEN */
+ REGU_VRFDIG, /*!< VRFDIG */
+ REGU_VRFREF, /*!< VRFREF */
+ REGU_VRFCP, /*!< VRFCP */
+ REGU_VSIM, /*!< VSIM */
+ REGU_VESIM, /*!< VESIM */
+ REGU_VCAM, /*!< VCAM */
+ REGU_VRFBG, /*!< VRFBG */
+ REGU_VVIB, /*!< VVIB */
+ REGU_VRF1, /*!< VRF1 */
+ REGU_VRF2, /*!< VRF2 */
+ REGU_VMMC1, /*!< VMMC1 or VMMC */
+ REGU_VMMC2, /*!< VMMC2 */
+ REGU_GPO1, /*!< GPIO1 */
+ REGU_GPO2, /*!< GPO2 */
+ REGU_GPO3, /*!< GPO3 */
+ REGU_GPO4, /*!< GPO4 */
+ REGU_V1, /*!< V1 */
+ REGU_V2, /*!< V2 */
+ REGU_V3, /*!< V3 */
+ REGU_V4, /*!< V4 */
+} t_pmic_regulator;
+
+/*!
+ * @enum t_pmic_regulator_voltage_sw1
+ * @brief PMIC Switch mode regulator SW1 output voltages.
+ */
+
+typedef enum {
+ SW1_1V = 0, /*!< 1.0 V */
+ SW1_1_1V, /*!< 1.1 V */
+ SW1_1_2V, /*!< 1.2 V */
+ SW1_1_3V, /*!< 1.3 V */
+ SW1_1_4V, /*!< 1.4 V */
+ SW1_1_55V, /*!< 1.55 V */
+ SW1_1_625V, /*!< 1.625 V */
+ SW1_1_875V, /*!< 1.875 V */
+} t_pmic_regulator_voltage_sw1;
+
+/*!
+ * @enum t_pmic_regulator_voltage_sw1a
+ * @brief PMIC regulator SW1A output voltage.
+ */
+typedef enum {
+ SW1A_0_9V = 0, /*!< 0.900 V */
+ SW1A_0_925V, /*!< 0.925 V */
+ SW1A_0_95V, /*!< 0.950 V */
+ SW1A_0_975V, /*!< 0.975 V */
+ SW1A_1V, /*!< 1.000 V */
+ SW1A_1_025V, /*!< 1.025 V */
+ SW1A_1_05V, /*!< 1.050 V */
+ SW1A_1_075V, /*!< 1.075 V */
+ SW1A_1_1V, /*!< 1.100 V */
+ SW1A_1_125V, /*!< 1.125 V */
+ SW1A_1_15V, /*!< 1.150 V */
+ SW1A_1_175V, /*!< 1.175 V */
+ SW1A_1_2V, /*!< 1.200 V */
+ SW1A_1_225V, /*!< 1.225 V */
+ SW1A_1_25V, /*!< 1.250 V */
+ SW1A_1_275V, /*!< 1.275 V */
+ SW1A_1_3V, /*!< 1.300 V */
+ SW1A_1_325V, /*!< 1.325 V */
+ SW1A_1_35V, /*!< 1.350 V */
+ SW1A_1_375V, /*!< 1.375 V */
+ SW1A_1_4V, /*!< 1.400 V */
+ SW1A_1_425V, /*!< 1.425 V */
+ SW1A_1_45V, /*!< 1.450 V */
+ SW1A_1_475V, /*!< 1.475 V */
+ SW1A_1_5V, /*!< 1.500 V */
+ SW1A_1_525V, /*!< 1.525 V */
+ SW1A_1_55V, /*!< 1.550 V */
+ SW1A_1_575V, /*!< 1.575 V */
+ SW1A_1_6V, /*!< 1.600 V */
+ SW1A_1_625V, /*!< 1.625 V */
+ SW1A_1_65V, /*!< 1.650 V */
+ SW1A_1_675V, /*!< 1.675 V */
+ SW1A_1_7V, /*!< 1.700 V */
+ SW1A_1_8V = 36, /*!< 1.800 V */
+ SW1A_1_85V = 40, /*!< 1.850 V */
+ SW1A_2V = 44, /*!< 2_000 V */
+ SW1A_2_1V = 48, /*!< 2_100 V */
+ SW1A_2_2V = 52, /*!< 2_200 V */
+} t_pmic_regulator_voltage_sw1a;
+
+/*!
+ * @enum t_pmic_regulator_voltage_sw1b
+ * @brief PMIC regulator SW1B output voltage.
+ */
+typedef enum {
+ SW1B_0_9V = 0, /*!< 0.900 V */
+ SW1B_0_925V, /*!< 0.925 V */
+ SW1B_0_95V, /*!< 0.950 V */
+ SW1B_0_975V, /*!< 0.975 V */
+ SW1B_1V, /*!< 1.000 V */
+ SW1B_1_025V, /*!< 1.025 V */
+ SW1B_1_05V, /*!< 1.050 V */
+ SW1B_1_075V, /*!< 1.075 V */
+ SW1B_1_1V, /*!< 1.100 V */
+ SW1B_1_125V, /*!< 1.125 V */
+ SW1B_1_15V, /*!< 1.150 V */
+ SW1B_1_175V, /*!< 1.175 V */
+ SW1B_1_2V, /*!< 1.200 V */
+ SW1B_1_225V, /*!< 1.225 V */
+ SW1B_1_25V, /*!< 1.250 V */
+ SW1B_1_275V, /*!< 1.275 V */
+ SW1B_1_3V, /*!< 1.300 V */
+ SW1B_1_325V, /*!< 1.325 V */
+ SW1B_1_35V, /*!< 1.350 V */
+ SW1B_1_375V, /*!< 1.375 V */
+ SW1B_1_4V, /*!< 1.400 V */
+ SW1B_1_425V, /*!< 1.425 V */
+ SW1B_1_45V, /*!< 1.450 V */
+ SW1B_1_475V, /*!< 1.475 V */
+ SW1B_1_5V, /*!< 1.500 V */
+ SW1B_1_525V, /*!< 1.525 V */
+ SW1B_1_55V, /*!< 1.550 V */
+ SW1B_1_575V, /*!< 1.575 V */
+ SW1B_1_6V, /*!< 1.600 V */
+ SW1B_1_625V, /*!< 1.625 V */
+ SW1B_1_65V, /*!< 1.650 V */
+ SW1B_1_675V, /*!< 1.675 V */
+ SW1B_1_7V, /*!< 1.700 V */
+ SW1B_1_8V = 36, /*!< 1.800 V */
+ SW1B_1_85V = 40, /*!< 1.850 V */
+ SW1B_2V = 44, /*!< 2_000 V */
+ SW1B_2_1V = 48, /*!< 2_100 V */
+ SW1B_2_2V = 52, /*!< 2_200 V */
+} t_pmic_regulator_voltage_sw1b;
+
+/*!
+ * @enum t_pmic_regulator_voltage_sw2
+ * @brief PMIC Switch mode regulator SW2 output voltages.
+ */
+typedef enum {
+ SW2_1V = 0, /*!< 1.0 V */
+ SW2_1_1V, /*!< 1.1 V */
+ SW2_1_2V, /*!< 1.2 V */
+ SW2_1_3V, /*!< 1.3 V */
+ SW2_1_4V, /*!< 1.4 V */
+ SW2_1_55V, /*!< 1.55 V */
+ SW2_1_625V, /*!< 1.625 V */
+ SW2_1_875V, /*!< 1.875 V */
+} t_pmic_regulator_voltage_sw2;
+
+/*!
+ * @enum t_pmic_regulator_voltage_sw2a
+ * @brief PMIC regulator SW2A output voltage.
+ */
+typedef enum {
+ SW2A_0_9V = 0, /*!< 0.900 V */
+ SW2A_0_925V, /*!< 0.925 V */
+ SW2A_0_95V, /*!< 0.950 V */
+ SW2A_0_975V, /*!< 0.975 V */
+ SW2A_1V, /*!< 1.000 V */
+ SW2A_1_025V, /*!< 1.025 V */
+ SW2A_1_05V, /*!< 1.050 V */
+ SW2A_1_075V, /*!< 1.075 V */
+ SW2A_1_1V, /*!< 1.100 V */
+ SW2A_1_125V, /*!< 1.125 V */
+ SW2A_1_15V, /*!< 1.150 V */
+ SW2A_1_175V, /*!< 1.175 V */
+ SW2A_1_2V, /*!< 1.200 V */
+ SW2A_1_225V, /*!< 1.225 V */
+ SW2A_1_25V, /*!< 1.250 V */
+ SW2A_1_275V, /*!< 1.275 V */
+ SW2A_1_3V, /*!< 1.300 V */
+ SW2A_1_325V, /*!< 1.325 V */
+ SW2A_1_35V, /*!< 1.350 V */
+ SW2A_1_375V, /*!< 1.375 V */
+ SW2A_1_4V, /*!< 1.400 V */
+ SW2A_1_425V, /*!< 1.425 V */
+ SW2A_1_45V, /*!< 1.450 V */
+ SW2A_1_475V, /*!< 1.475 V */
+ SW2A_1_5V, /*!< 1.500 V */
+ SW2A_1_525V, /*!< 1.525 V */
+ SW2A_1_55V, /*!< 1.550 V */
+ SW2A_1_575V, /*!< 1.575 V */
+ SW2A_1_6V, /*!< 1.600 V */
+ SW2A_1_625V, /*!< 1.625 V */
+ SW2A_1_65V, /*!< 1.650 V */
+ SW2A_1_675V, /*!< 1.675 V */
+ SW2A_1_7V, /*!< 1.700 V */
+ SW2A_1_8V = 36, /*!< 1.800 V */
+ SW2A_1_9V = 40, /*!< 1.900 V */
+ SW2A_2V = 44, /*!< 2_000 V */
+ SW2A_2_1V = 48, /*!< 2_100 V */
+ SW2A_2_2V = 52, /*!< 2_200 V */
+} t_pmic_regulator_voltage_sw2a;
+
+/*!
+ * @enum t_pmic_regulator_voltage_sw2b
+ * @brief PMIC regulator SW2B output voltage.
+ */
+typedef enum {
+ SW2B_0_9V = 0, /*!< 0.900 V */
+ SW2B_0_925V, /*!< 0.925 V */
+ SW2B_0_95V, /*!< 0.950 V */
+ SW2B_0_975V, /*!< 0.975 V */
+ SW2B_1V, /*!< 1.000 V */
+ SW2B_1_025V, /*!< 1.025 V */
+ SW2B_1_05V, /*!< 1.050 V */
+ SW2B_1_075V, /*!< 1.075 V */
+ SW2B_1_1V, /*!< 1.100 V */
+ SW2B_1_125V, /*!< 1.125 V */
+ SW2B_1_15V, /*!< 1.150 V */
+ SW2B_1_175V, /*!< 1.175 V */
+ SW2B_1_2V, /*!< 1.200 V */
+ SW2B_1_225V, /*!< 1.225 V */
+ SW2B_1_25V, /*!< 1.250 V */
+ SW2B_1_275V, /*!< 1.275 V */
+ SW2B_1_3V, /*!< 1.300 V */
+ SW2B_1_325V, /*!< 1.325 V */
+ SW2B_1_35V, /*!< 1.350 V */
+ SW2B_1_375V, /*!< 1.375 V */
+ SW2B_1_4V, /*!< 1.400 V */
+ SW2B_1_425V, /*!< 1.425 V */
+ SW2B_1_45V, /*!< 1.450 V */
+ SW2B_1_475V, /*!< 1.475 V */
+ SW2B_1_5V, /*!< 1.500 V */
+ SW2B_1_525V, /*!< 1.525 V */
+ SW2B_1_55V, /*!< 1.550 V */
+ SW2B_1_575V, /*!< 1.575 V */
+ SW2B_1_6V, /*!< 1.600 V */
+ SW2B_1_625V, /*!< 1.625 V */
+ SW2B_1_65V, /*!< 1.650 V */
+ SW2B_1_675V, /*!< 1.675 V */
+ SW2B_1_7V, /*!< 1.700 V */
+ SW2B_1_8V = 36, /*!< 1.800 V */
+ SW2B_1_9V = 40, /*!< 1.900 V */
+ SW2B_2V = 44, /*!< 2_000 V */
+ SW2B_2_1V = 48, /*!< 2_100 V */
+ SW2B_2_2V = 52, /*!< 2_200 V */
+} t_pmic_regulator_voltage_sw2b;
+
+/*!
+ * @enum t_pmic_regulator_voltage_sw3
+ * @brief PMIC Switch mode regulator SW3 output voltages.
+ */
+typedef enum {
+ SW3_5V = 0, /*!< 5.0 V */
+ SW3_5_1V = 0, /*!< 5.1 V */
+ SW3_5_6V, /*!< 5.6 V */
+} t_pmic_regulator_voltage_sw3;
+
+/*!
+ * @enum t_switcher_factor
+ * @brief PLL multiplication factor
+ */
+typedef enum {
+ FACTOR_28 = 0, /*!< 917 504 kHz */
+ FACTOR_29, /*!< 950 272 kHz */
+ FACTOR_30, /*!< 983 040 kHz */
+ FACTOR_31, /*!< 1 015 808 kHz */
+ FACTOR_32, /*!< 1 048 576 kHz */
+ FACTOR_33, /*!< 1 081 344 kHz */
+ FACTOR_34, /*!< 1 114 112 kHz */
+ FACTOR_35, /*!< 1 146 880 kHz */
+} t_switcher_factor;
+
+/*!
+ * @enum t_pmic_regulator_voltage_violo
+ * @brief PMIC regulator VIOLO output voltage.
+ */
+typedef enum {
+ VIOLO_1_2V = 0, /*!< 1.2 V */
+ VIOLO_1_3V, /*!< 1.3 V */
+ VIOLO_1_5V, /*!< 1.5 V */
+ VIOLO_1_8V, /*!< 1.8 V */
+} t_pmic_regulator_voltage_violo;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vdig
+ * @brief PMIC regulator VDIG output voltage.
+ */
+typedef enum {
+ VDIG_1_2V = 0, /*!< 1.2 V */
+ VDIG_1_3V, /*!< 1.3 V */
+ VDIG_1_5V, /*!< 1.5 V */
+ VDIG_1_8V, /*!< 1.8 V */
+} t_pmic_regulator_voltage_vdig;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vgen
+ * @brief PMIC regulator VGEN output voltage.
+ */
+typedef enum {
+ VGEN_1_2V = 0, /*!< 1.2 V */
+ VENG_1_3V, /*!< 1.3 V */
+ VGEN_1_5V, /*!< 1.5 V */
+ VGEN_1_8V, /*!< 1.8 V */
+ VGEN_1_1V, /*!< 1.1 V */
+ VGEN_2V, /*!< 2 V */
+ VGEN_2_775V, /*!< 2.775 V */
+ VGEN_2_4V, /*!< 2.4 V */
+} t_pmic_regulator_voltage_vgen;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vrfdig
+ * @brief PMIC regulator VRFDIG output voltage.
+ */
+typedef enum {
+ VRFDIG_1_2V = 0, /*!< 1.2 V */
+ VRFDIG_1_5V, /*!< 1.5 V */
+ VRFDIG_1_8V, /*!< 1.8 V */
+ VRFDIG_1_875V, /*!< 1.875 V */
+} t_pmic_regulator_voltage_vrfdig;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vrfref
+ * @brief PMIC regulator VRFREF output voltage.
+ */
+typedef enum {
+ VRFREF_2_475V = 0, /*!< 2.475 V */
+ VRFREF_2_6V, /*!< 2.600 V */
+ VRFREF_2_7V, /*!< 2.700 V */
+ VRFREF_2_775V, /*!< 2.775 V */
+} t_pmic_regulator_voltage_vrfref;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vrfcp
+ * @brief PMIC regulator VRFCP output voltage.
+ */
+typedef enum {
+ VRFCP_2_7V = 0, /*!< 2.700 V */
+ VRFCP_2_775V, /*!< 2.775 V */
+} t_pmic_regulator_voltage_vrfcp;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vsim
+ * @brief PMIC linear regulator VSIM output voltage.
+ */
+typedef enum {
+ VSIM_1_8V = 0, /*!< 1.8 V */
+ VSIM_2_9V, /*!< 2.90 V */
+ VSIM_3V = 1, /*!< 3 V */
+} t_pmic_regulator_voltage_vsim;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vesim
+ * @brief PMIC regulator VESIM output voltage.
+ */
+typedef enum {
+ VESIM_1_8V = 0, /*!< 1.80 V */
+ VESIM_2_9V, /*!< 2.90 V */
+} t_pmic_regulator_voltage_vesim;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vcam
+ * @brief PMIC regulator VCAM output voltage.
+ */
+typedef enum {
+ VCAM_1_5V = 0, /*!< 1.50 V */
+ VCAM_1_8V, /*!< 1.80 V */
+ VCAM_2_5V, /*!< 2.50 V */
+ VCAM_2_55V, /*!< 2.55 V */
+ VCAM_2_6V, /*!< 2.60 V */
+ VCAM_2_75V, /*!< 2.75 V */
+ VCAM_2_8V, /*!< 2.80 V */
+ VCAM_3V, /*!< 3.00 V */
+} t_pmic_regulator_voltage_vcam;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vvib
+ * @brief PMIC linear regulator V_VIB output voltage.
+ */
+typedef enum {
+ VVIB_1_3V = 0, /*!< 1.30 V */
+ VVIB_1_8V, /*!< 1.80 V */
+ VVIB_2V, /*!< 2 V */
+ VVIB_3V, /*!< 3 V */
+} t_pmic_regulator_voltage_vvib;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vrf1
+ * @brief PMIC regulator VRF1 output voltage.
+ */
+typedef enum {
+ VRF1_1_5V = 0, /*!< 1.500 V */
+ VRF1_1_875V, /*!< 1.875 V */
+ VRF1_2_7V, /*!< 2.700 V */
+ VRF1_2_775V, /*!< 2.775 V */
+} t_pmic_regulator_voltage_vrf1;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vrf2
+ * @brief PMIC regulator VRF2 output voltage.
+ */
+typedef enum {
+ VRF2_1_5V = 0, /*!< 1.500 V */
+ VRF2_1_875V, /*!< 1.875 V */
+ VRF2_2_7V, /*!< 2.700 V */
+ VRF2_2_775V, /*!< 2.775 V */
+} t_pmic_regulator_voltage_vrf2;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vmmc
+ * @brief PMIC linear regulator VMMC output voltage.
+ */
+typedef enum {
+ VMMC_OFF = 0, /*!< Output off */
+ VMMC_1_6V, /*!< 1.6 V */
+ VMMC_1_8V, /*!< 1.8 V */
+ VMMC_2V, /*!< 2 V */
+ VMMC_2_2V, /*!< 2.2 V */
+ VMMC_2_4V, /*!< 2.4 V */
+ VMMC_2_6V, /*!< 2.6 V */
+ VMMC_2_8V, /*!< 2.8 V */
+ VMMC_3V, /*!< 3 V */
+ VMMC_3_2V, /*!< 3.2 V */
+ VMMC_3_3V, /*!< 3.3 V */
+ VMMC_3_4V, /*!< 3.4 V */
+} t_pmic_regulator_voltage_vmmc;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vmmc1
+ * @brief PMIC regulator VMMC1 output voltage.
+ */
+typedef enum {
+ VMMC1_1_6V = 0, /*!< 1.60 V */
+ VMMC1_1_8V, /*!< 1.80 V */
+ VMMC1_2V, /*!< 2.00 V */
+ VMMC1_2_6V, /*!< 2.60 V */
+ VMMC1_2_7V, /*!< 2.70 V */
+ VMMC1_2_8V, /*!< 2.80 V */
+ VMMC1_2_9V, /*!< 2.90 V */
+ VMMC1_3V, /*!< 3.00 V */
+} t_pmic_regulator_voltage_vmmc1;
+
+/*!
+ * @enum t_pmic_regulator_voltage_vmmc2
+ * @brief PMIC regulator VMMC2 output voltage.
+ */
+typedef enum {
+ VMMC2_1_6V = 0, /*!< 1.60 V */
+ VMMC2_1_8V, /*!< 1.80 V */
+ VMMC2_2V, /*!< 2.00 V */
+ VMMC2_2_6V, /*!< 2.60 V */
+ VMMC2_2_7V, /*!< 2.70 V */
+ VMMC2_2_8V, /*!< 2.80 V */
+ VMMC2_2_9V, /*!< 2.90 V */
+ VMMC2_3V, /*!< 3.00 V */
+} t_pmic_regulator_voltage_vmmc2;
+
+/*!
+ * @enum t_pmic_regulator_voltage_v1
+ * @brief PMIC linear regulator V1 output voltages.
+ */
+typedef enum {
+ V1_2_775V = 0, /*!< 2.775 V */
+ V1_1_2V, /*!< 1.2 V */
+ V1_1_3V, /*!< 1.3 V */
+ V1_1_4V, /*!< 1.4 V */
+ V1_1_55V, /*!< 1.55 V */
+ V1_1_75V, /*!< 1.75 V */
+ V1_1_875V, /*!< 1.875 V */
+ V1_2_475V, /*!< 2.475 V */
+} t_pmic_regulator_voltage_v1;
+
+/*!
+ * @enum t_pmic_regulator_voltage_v2
+ * @brief PMIC linear regulator V2 output voltage, V2 has fixed
+ * output voltage 2.775 volts.
+ */
+typedef enum {
+ V2_2_775V = 0, /*!< 2.775 V */
+} t_pmic_regulator_voltage_v2;
+
+/*!
+ * @enum t_pmic_regulator_voltage_v3
+ * @brief PMIC linear regulator V3 output voltage.
+ */
+typedef enum {
+ V3_1_875V = 0, /*!< 1.875 V */
+ V3_2_775V, /*!< 2.775 V */
+} t_pmic_regulator_voltage_v3;
+
+/*!
+ * @enum t_pmic_regulator_voltage_v4
+ * @brief PMIC linear regulator V4 output voltage, V4 has fixed
+ * output voltage 2.775 volts.
+ */
+typedef enum {
+ V4_2_775V = 0, /*!< 2.775 V */
+} t_pmic_regulator_voltage_v4;
+
+/*!
+ * @union t_regulator_voltage
+ * @brief PMIC regulator output voltages.
+ */
+typedef union {
+ t_pmic_regulator_voltage_sw1 sw1; /*!< SW1 voltage */
+ t_pmic_regulator_voltage_sw1a sw1a; /*!< SW1A voltage */
+ t_pmic_regulator_voltage_sw1b sw1b; /*!< SW1B voltage */
+ t_pmic_regulator_voltage_sw2 sw2; /*!< SW2 voltage */
+ t_pmic_regulator_voltage_sw2a sw2a; /*!< SW2A voltage */
+ t_pmic_regulator_voltage_sw2b sw2b; /*!< SW2B voltage */
+ t_pmic_regulator_voltage_sw3 sw3; /*!< SW3 voltage */
+ t_pmic_regulator_voltage_violo violo; /*!< VIOLO voltage */
+ t_pmic_regulator_voltage_vdig vdig; /*!< VDIG voltage */
+ t_pmic_regulator_voltage_vgen vgen; /*!< VGEN voltage */
+ t_pmic_regulator_voltage_vrfdig vrfdig; /*!< VRFDIG voltage */
+ t_pmic_regulator_voltage_vrfref vrfref; /*!< VRFREF voltage */
+ t_pmic_regulator_voltage_vrfcp vrfcp; /*!< VRFCP voltage */
+ t_pmic_regulator_voltage_vsim vsim; /*!< VSIM voltage */
+ t_pmic_regulator_voltage_vesim vesim; /*!< VESIM voltage */
+ t_pmic_regulator_voltage_vcam vcam; /*!< VCAM voltage */
+ t_pmic_regulator_voltage_vvib vvib; /*!< VVIB voltage */
+ t_pmic_regulator_voltage_vrf1 vrf1; /*!< VRF1 voltage */
+ t_pmic_regulator_voltage_vrf2 vrf2; /*!< VRF2 voltage */
+ t_pmic_regulator_voltage_vmmc vmmc; /*!< VMMC voltage */
+ t_pmic_regulator_voltage_vmmc1 vmmc1; /*!< VMMC1 voltage */
+ t_pmic_regulator_voltage_vmmc2 vmmc2; /*!< VMMC2 voltage */
+ t_pmic_regulator_voltage_v1 v1; /*!< V1 voltage */
+ t_pmic_regulator_voltage_v2 v2; /*!< V2 voltage */
+ t_pmic_regulator_voltage_v3 v3; /*!< V3 voltage */
+ t_pmic_regulator_voltage_v4 v4; /*!< V4 voltage */
+} t_regulator_voltage;
+
+/*!
+ * @enum t_pmic_regulator_sw_mode
+ * @brief define switch mode regulator mode.
+ *
+ * The synchronous rectifier can be disabled (and pulse-skipping enabled)
+ * to improve low current efficiency. Software should disable synchronous
+ * rectifier / enable the pulse skipping for average loads less than
+ * approximately 30 mA, depending on the quiescent current penalty due to
+ * synchronous mode.
+ */
+typedef enum {
+ SYNC_RECT = 0,
+ NO_PULSE_SKIP,
+ PULSE_SKIP,
+ LOW_POWER,
+} t_pmic_regulator_sw_mode;
+
+/*!
+ * Generic PMIC switch mode regulator mode.
+ */
+typedef t_pmic_regulator_sw_mode t_regulator_sw_mode;
+typedef t_pmic_regulator_sw_mode t_regulator_stby_mode;
+
+/*!
+ * @enum t_regulator_lp_mode
+ * @brief Low power mode control modes.
+ */
+
+typedef enum {
+ /*!
+ * Low Power Mode is disabled
+ */
+ LOW_POWER_DISABLED = 0,
+ /*!
+ * Low Power Mode is controlled by STANDBY pin and/or LVS pin
+ */
+ LOW_POWER_CTRL_BY_PIN,
+ /*!
+ * Set Low Power mode no matter of hardware pins
+ */
+ LOW_POWER_EN,
+ /*!
+ * Set Low Power mode and control by STANDBY
+ */
+ LOW_POWER_AND_LOW_POWER_CTRL_BY_PIN,
+} t_regulator_lp_mode;
+
+/*!
+ * @enum t_switcher_dvs_speed
+ * @brief DVS speed setting
+ */
+typedef enum {
+ /*!
+ * Transition speed is dictated by the current
+ * limit and input -output conditions
+ */
+ DICTATED = 0,
+ /*!
+ * 25mV step each 4us
+ */
+ DVS_4US,
+ /*!
+ * 25mV step each 8us
+ */
+ DVS_8US,
+ /*!
+ * 25mV step each 16us
+ */
+ DVS_16US,
+} t_switcher_dvs_speed;
+
+/*!
+ * @struct t_regulator_config
+ * @brief regulator configuration.
+ *
+ */
+
+typedef struct {
+ /*!
+ * Switch mode regulator operation mode. This field only applies to
+ * switch mode regulators.
+ */
+ t_regulator_sw_mode mode;
+ /*!
+ * Switch mode stby regulator operation mode. This field only applies
+ * to switch mode regulators.
+ */
+ t_regulator_stby_mode stby_mode;
+ /*!
+ * Regulator output voltage.
+ */
+ t_regulator_voltage voltage;
+ /*!
+ * Regulator output voltage in LVS mode.
+ */
+ t_regulator_voltage voltage_lvs;
+ /*!
+ * Regulator output voltage in standby mode.
+ */
+ t_regulator_voltage voltage_stby;
+ /*!
+ * Regulator low power mode.
+ */
+ t_regulator_lp_mode lp_mode;
+ /*!
+ * Switcher dvs speed
+ */
+ t_switcher_dvs_speed dvs_speed;
+ /*!
+ * Switcher panic mode
+ */
+ bool panic_mode;
+ /*!
+ * Switcher softstart
+ */
+ bool softstart;
+ /*!
+ * PLL Multiplication factor
+ */
+ t_switcher_factor factor;
+} t_regulator_config;
+
+/*!
+ * @struct t_regulator_cfg_param
+ * @brief regulator configuration structure for IOCTL.
+ *
+ */
+typedef struct {
+ /*!
+ * Regulator.
+ */
+ t_pmic_regulator regulator;
+ /*!
+ * Regulator configuration.
+ */
+ t_regulator_config cfg;
+} t_regulator_cfg_param;
+
+/*!
+ * This struct list all state reads in Power Up Sense
+ */
+struct t_p_up_sense {
+ /*!
+ * power up sense ictest
+ */
+ bool state_ictest;
+ /*!
+ * power up sense clksel
+ */
+ bool state_clksel;
+ /*!
+ * power up mode supply 1
+ */
+ bool state_pums1;
+ /*!
+ * power up mode supply 2
+ */
+ bool state_pums2;
+ /*!
+ * power up mode supply 3
+ */
+ bool state_pums3;
+ /*!
+ * power up sense charge mode 0
+ */
+ bool state_chrgmode0;
+ /*!
+ * power up sense charge mode 1
+ */
+ bool state_chrgmode1;
+ /*!
+ * power up sense USB mode
+ */
+ bool state_umod;
+ /*!
+ * power up sense boot mode enable for USB/RS232
+ */
+ bool state_usben;
+ /*!
+ * power up sense switcher 1a1b joined
+ */
+ bool state_sw_1a1b_joined;
+ /*!
+ * power up sense switcher 1a1b joined
+ */
+ bool state_sw_2a2b_joined;
+};
+
+/*!
+ * This enumeration define all On_OFF button
+ */
+typedef enum {
+ /*!
+ * ON1B
+ */
+ BT_ON1B = 0,
+ /*!
+ * ON2B
+ */
+ BT_ON2B,
+ /*!
+ * ON3B
+ */
+ BT_ON3B,
+} t_button;
+
+#ifdef __KERNEL__
+/* EXPORTED FUNCTIONS */
+
+/*!
+ * This function sets user power off in power control register and thus powers
+ * off the phone.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+void pmic_power_off(void);
+
+/*!
+ * This function sets the power control configuration.
+ *
+ * @param pc_config power control configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_set_pc_config(t_pc_config * pc_config);
+
+/*!
+ * This function retrives the power control configuration.
+ *
+ * @param pc_config pointer to power control configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_pc_config(t_pc_config * pc_config);
+
+/*!
+ * This function turns on a regulator.
+ *
+ * @param regulator The regulator to be turned on.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_on(t_pmic_regulator regulator);
+
+/*!
+ * This function turns off a regulator.
+ *
+ * @param regulator The regulator to be turned off.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_off(t_pmic_regulator regulator);
+
+/*!
+ * This function sets the regulator output voltage.
+ *
+ * @param regulator The regulator to be turned off.
+ * @param voltage The regulator output voltage.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_set_voltage(t_pmic_regulator regulator,
+ t_regulator_voltage voltage);
+
+/*!
+ * This function retrieves the regulator output voltage.
+ *
+ * @param regulator The regulator to be turned off.
+ * @param voltage Pointer to regulator output voltage.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_get_voltage(t_pmic_regulator regulator,
+ t_regulator_voltage * voltage);
+
+/*!
+ * This function sets the DVS voltage
+ *
+ * @param regulator The regulator to be configured.
+ * @param dvs The switch Dynamic Voltage Scaling
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_dvs(t_pmic_regulator regulator,
+ t_regulator_voltage dvs);
+
+/*!
+ * This function gets the DVS voltage
+ *
+ * @param regulator The regulator to be handled.
+ * @param dvs The switch Dynamic Voltage Scaling
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_dvs(t_pmic_regulator regulator,
+ t_regulator_voltage * dvs);
+
+/*!
+ * This function sets the standby voltage
+ *
+ * @param regulator The regulator to be configured.
+ * @param stby The switch standby voltage
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_stby(t_pmic_regulator regulator,
+ t_regulator_voltage stby);
+
+/*!
+ * This function gets the standby voltage
+ *
+ * @param regulator The regulator to be handled.
+ * @param stby The switch standby voltage
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_stby(t_pmic_regulator regulator,
+ t_regulator_voltage * stby);
+
+/*!
+ * This function sets the switchers mode.
+ *
+ * @param regulator The regulator to be configured.
+ * @param mode The switcher mode
+ * @param stby Switch between main and standby.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_mode(t_pmic_regulator regulator,
+ t_regulator_sw_mode mode, bool stby);
+
+/*!
+ * This function gets the switchers mode.
+ *
+ * @param regulator The regulator to be handled.
+ * @param mode The switcher mode.
+ * @param stby Switch between main and standby.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_mode(t_pmic_regulator regulator,
+ t_regulator_sw_mode * mode, bool stby);
+
+/*!
+ * This function sets the switch dvs speed
+ *
+ * @param regulator The regulator to be configured.
+ * @param speed The dvs speed.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_dvs_speed(t_pmic_regulator regulator,
+ t_switcher_dvs_speed speed);
+
+/*!
+ * This function gets the switch dvs speed
+ *
+ * @param regulator The regulator to be handled.
+ * @param speed The dvs speed.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_dvs_speed(t_pmic_regulator regulator,
+ t_switcher_dvs_speed * speed);
+
+/*!
+ * This function sets the switch panic mode
+ *
+ * @param regulator The regulator to be configured.
+ * @param panic_mode Enable or disable panic mode
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_panic_mode(t_pmic_regulator regulator,
+ bool panic_mode);
+
+/*!
+ * This function gets the switch panic mode
+ *
+ * @param regulator The regulator to be handled
+ * @param panic_mode Enable or disable panic mode
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_panic_mode(t_pmic_regulator regulator,
+ bool * panic_mode);
+
+/*!
+ * This function sets the switch softstart mode
+ *
+ * @param regulator The regulator to be configured.
+ * @param softstart Enable or disable softstart.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_softstart(t_pmic_regulator regulator,
+ bool softstart);
+
+/*!
+ * This function gets the switch softstart mode
+ *
+ * @param regulator The regulator to be handled
+ * @param softstart Enable or disable softstart.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_softstart(t_pmic_regulator regulator,
+ bool * softstart);
+
+/*!
+ * This function sets the PLL multiplication factor
+ *
+ * @param regulator The regulator to be configured.
+ * @param factor The multiplication factor.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_factor(t_pmic_regulator regulator,
+ t_switcher_factor factor);
+
+/*!
+ * This function gets the PLL multiplication factor
+ *
+ * @param regulator The regulator to be handled
+ * @param factor The multiplication factor.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_factor(t_pmic_regulator regulator,
+ t_switcher_factor * factor);
+
+/*!
+ * This function enables or disables low power mode.
+ *
+ * @param regulator The regulator to be configured.
+ * @param mode Select nominal or low power mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_set_lp_mode(t_pmic_regulator regulator,
+ t_regulator_lp_mode lp_mode);
+
+/*!
+ * This function gets low power mode.
+ *
+ * @param regulator The regulator to be handled
+ * @param mode Select nominal or low power mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_get_lp_mode(t_pmic_regulator regulator,
+ t_regulator_lp_mode * lp_mode);
+
+/*!
+ * This function sets the regulator configuration.
+ *
+ * @param regulator The regulator to be turned off.
+ * @param config The regulator output configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_set_config(t_pmic_regulator regulator,
+ t_regulator_config * config);
+
+/*!
+ * This function retrieves the regulator output configuration.
+ *
+ * @param regulator The regulator to be turned off.
+ * @param config Pointer to regulator configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_get_config(t_pmic_regulator regulator,
+ t_regulator_config * config);
+
+/*!
+ * This function enables automatically VBKUP2 in the memory hold modes.
+ *
+ * @param en if true, enable VBKUP2AUTOMH
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_vbkup2_auto_en(bool en);
+
+/*!
+ * This function gets state of automatically VBKUP2.
+ *
+ * @param en if true, VBKUP2AUTOMH is enabled
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_vbkup2_auto_state(bool * en);
+
+/*!
+ * This function enables battery detect function.
+ *
+ * @param en if true, enable BATTDETEN
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_bat_det_en(bool en);
+
+/*!
+ * This function gets state of battery detect function.
+ *
+ * @param en if true, BATTDETEN is enabled
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_bat_det_state(bool * en);
+
+/*!
+ * This function enables control of VVIB by VIBEN pin.
+ *
+ * @param en if true, enable VIBPINCTRL
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_vib_pin_en(bool en);
+
+/*!
+ * This function gets state of control of VVIB by VIBEN pin.
+ * @param en if true, VIBPINCTRL is enabled
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_gets_vib_pin_state(bool * en);
+
+/*!
+ * This function returns power up sense value
+ *
+ * @param p_up_sense value of power up sense
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_power_mode_sense(struct t_p_up_sense *p_up_sense);
+
+/*!
+ * This function configures the Regen assignment for all regulator
+ *
+ * @param regulator type of regulator
+ * @param en_dis if true, the regulator is enabled by regen.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_regen_assig(t_pmic_regulator regulator, bool en_dis);
+
+/*!
+ * This function gets the Regen assignment for all regulator
+ *
+ * @param regulator type of regulator
+ * @param en_dis return value, if true :
+ * the regulator is enabled by regen.
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_regen_assig(t_pmic_regulator regu, bool * en_dis);
+
+/*!
+ * This function sets the Regen polarity.
+ *
+ * @param en_dis If true regen is inverted.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_regen_inv(bool en_dis);
+
+/*!
+ * This function gets the Regen polarity.
+ *
+ * @param en_dis If true regen is inverted.
+ *
+ * @return This function returns 0 if successful.
+ */
+
+PMIC_STATUS pmic_power_get_regen_inv(bool * en_dis);
+
+/*!
+ * This function enables esim control voltage.
+ *
+ * @param vesim if true, enable VESIMESIMEN
+ * @param vmmc1 if true, enable VMMC1ESIMEN
+ * @param vmmc2 if true, enable VMMC2ESIMEN
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_esim_v_en(bool vesim, bool vmmc1, bool vmmc2);
+
+/*!
+ * This function gets esim control voltage values.
+ *
+ * @param vesim if true, enable VESIMESIMEN
+ * @param vmmc1 if true, enable VMMC1ESIMEN
+ * @param vmmc2 if true, enable VMMC2ESIMEN
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_gets_esim_v_state(bool * vesim,
+ bool * vmmc1, bool * vmmc2);
+
+/*!
+ * This function enables auto reset after a system reset.
+ *
+ * @param en if true, the auto reset is enabled
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_auto_reset_en(bool en);
+
+/*!
+ * This function gets auto reset configuration.
+ *
+ * @param en if true, the auto reset is enabled
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_auto_reset_en(bool * en);
+
+/*!
+ * This function configures a system reset on a button.
+ *
+ * @param bt type of button.
+ * @param sys_rst if true, enable the system reset on this button
+ * @param deb_time sets the debounce time on this button pin
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_conf_button(t_button bt, bool sys_rst, int deb_time);
+
+/*!
+ * This function gets configuration of a button.
+ *
+ * @param bt type of button.
+ * @param sys_rst if true, the system reset is enabled on this button
+ * @param deb_time gets the debounce time on this button pin
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_conf_button(t_button bt,
+ bool * sys_rst, int *deb_time);
+
+/*!
+ * This function is used to subscribe on power event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_event_sub(t_pwr_int event, void *callback);
+
+/*!
+ * This function is used to un subscribe on power event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_event_unsub(t_pwr_int event, void *callback);
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_ARCH_MXC_PMIC_POWER_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/sdma.h b/arch/arm/plat-mxc/include/mach/sdma.h
new file mode 100644
index 000000000000..1b145e8fc8d8
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/sdma.h
@@ -0,0 +1,560 @@
+
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_SDMA_H__
+#define __ASM_ARCH_MXC_SDMA_H__
+
+/*!
+ * @defgroup SDMA Smart Direct Memory Access (SDMA) Driver
+ */
+
+/*!
+ * @file arch-mxc/sdma.h
+ *
+ * @brief This file contains the SDMA API declarations.
+ *
+ * SDMA is responsible on moving data between peripherals and memories (MCU, EMI and DSP).
+ *
+ * @ingroup SDMA
+ */
+
+#include <linux/interrupt.h>
+#include <asm/dma.h>
+#include <stdarg.h>
+
+#include <mach/hardware.h>
+
+/*!
+ * This defines maximum DMA address
+ */
+#define MAX_DMA_ADDRESS 0xffffffff
+
+/*!
+ * This defines maximum number of DMA channels
+ */
+#ifdef CONFIG_MXC_SDMA_API
+#define MAX_DMA_CHANNELS 32
+#define MAX_BD_NUMBER 16
+#define MXC_SDMA_DEFAULT_PRIORITY 1
+#define MXC_SDMA_MIN_PRIORITY 1
+#define MXC_SDMA_MAX_PRIORITY 7
+#else
+#define MAX_DMA_CHANNELS 0
+#endif
+
+#define MXC_FIFO_MEM_DEST_FIXED 0x1
+#define MXC_FIFO_MEM_SRC_FIXED 0x2
+
+#define SDMA_ASRC_INFO_WML_OFF 0
+#define SDMA_ASRC_INFO_WML_MASK ((1 << 10) - 1)
+#define SDMA_ASRC_INFO_PS (1 << 10)
+#define SDMA_ASRC_INFO_PA (1 << 11)
+#define SDMA_ASRC_INFO_TXFR_DIR (1 << 14)
+#define SDMA_ASRC_INFO_N_OFF (24)
+#define SDMA_ASRC_INFO_N_MASK ((1 << 4) - 1)
+
+#define SDMA_ASRC_P2P_INFO_LWML_OFF 0
+#define SDMA_ASRC_P2P_INFO_LWML_MASK ((1 << 8) - 1)
+#define SDMA_ASRC_P2P_INFO_PS (1 << 8)
+#define SDMA_ASRC_P2P_INFO_PA (1 << 9)
+#define SDMA_ASRC_P2P_INFO_SPDIF (1 << 10)
+#define SDMA_ASRC_P2P_INFO_SP (1 << 11)
+#define SDMA_ASRC_P2P_INFO_DP (1 << 12)
+#define SDMA_ASRC_P2P_INFO_HWML_OFF 14
+#define SDMA_ASRC_P2P_INFO_HWML_MASK ((1 << 10) - 1)
+#define SDMA_ASRC_P2P_INFO_LWE (1 << 28)
+#define SDMA_ASRC_P2P_INFO_HWE (1 << 29)
+#define SDMA_ASRC_P2P_INFO_CONT (1 << 31)
+
+/*!
+ * This enumerates transfer types
+ */
+typedef enum {
+ emi_2_per = 0, /*!< EMI memory to peripheral */
+ emi_2_int, /*!< EMI memory to internal RAM */
+ emi_2_emi, /*!< EMI memory to EMI memory */
+ emi_2_dsp, /*!< EMI memory to DSP memory */
+ per_2_int, /*!< Peripheral to internal RAM */
+ per_2_emi, /*!< Peripheral to internal EMI memory */
+ per_2_dsp, /*!< Peripheral to DSP memory */
+ per_2_per, /*!< Peripheral to Peripheral */
+ int_2_per, /*!< Internal RAM to peripheral */
+ int_2_int, /*!< Internal RAM to Internal RAM */
+ int_2_emi, /*!< Internal RAM to EMI memory */
+ int_2_dsp, /*!< Internal RAM to DSP memory */
+ dsp_2_per, /*!< DSP memory to peripheral */
+ dsp_2_int, /*!< DSP memory to internal RAM */
+ dsp_2_emi, /*!< DSP memory to EMI memory */
+ dsp_2_dsp, /*!< DSP memory to DSP memory */
+ emi_2_dsp_loop, /*!< EMI memory to DSP memory loopback */
+ dsp_2_emi_loop, /*!< DSP memory to EMI memory loopback */
+ dvfs_pll, /*!< DVFS script with PLL change */
+ dvfs_pdr /*!< DVFS script without PLL change */
+} sdma_transferT;
+
+/*!
+ * This enumerates peripheral types
+ */
+typedef enum {
+ SSI, /*!< MCU domain SSI */
+ SSI_SP, /*!< Shared SSI */
+ MMC, /*!< MMC */
+ SDHC, /*!< SDHC */
+ UART, /*!< MCU domain UART */
+ UART_SP, /*!< Shared UART */
+ FIRI, /*!< FIRI */
+ CSPI, /*!< MCU domain CSPI */
+ CSPI_SP, /*!< Shared CSPI */
+ SIM, /*!< SIM */
+ ATA, /*!< ATA */
+ CCM, /*!< CCM */
+ EXT, /*!< External peripheral */
+ MSHC, /*!< Memory Stick Host Controller */
+ MSHC_SP, /*!< Shared Memory Stick Host Controller */
+ DSP, /*!< DSP */
+ MEMORY, /*!< Memory */
+ FIFO_MEMORY, /*!< FIFO type Memory */
+ SPDIF, /*!< SPDIF */
+ IPU_MEMORY, /*!< IPU Memory */
+ ASRC, /*!< ASRC */
+ ESAI, /*!< ESAI */
+} sdma_periphT;
+
+#ifndef TRANSFER_32BIT
+/*!
+ * This defines SDMA access data size
+ */
+#define TRANSFER_32BIT 0x00
+#define TRANSFER_8BIT 0x01
+#define TRANSFER_16BIT 0x02
+#define TRANSFER_24BIT 0x03
+
+#endif
+
+/*!
+ * This defines maximum device name length passed during mxc_request_dma().
+ */
+#define MAX_DEVNAME_LENGTH 32
+
+/*!
+ * This defines SDMA interrupt callback function prototype.
+ */
+typedef void (*dma_callback_t) (void *arg);
+
+/*!
+ * Structure containing sdma channel parameters.
+ */
+typedef struct {
+ __u32 watermark_level; /*!< Lower/upper threshold that
+ * triggers SDMA event
+ * for p2p, this is event1 watermark level
+ */
+ __u32 per_address; /*!< Peripheral source/destination
+ * physical address
+ * for p2p, this is destination address
+ */
+ sdma_periphT peripheral_type; /*!< Peripheral type */
+ sdma_transferT transfer_type; /*!< Transfer type */
+ int event_id; /*!< Event number,
+ * needed by all channels
+ * that started by peripherals dma
+ * request (per_2_*,*_2_per)
+ * Not used for memory and DSP
+ * transfers.
+ */
+ int event_id2; /*!< Second event number,
+ * used in ATA scripts only.
+ */
+ int bd_number; /*!< Buffer descriptors number.
+ * If not set, single buffer
+ * descriptor will be used.
+ */
+ dma_callback_t callback; /*! callback function */
+ void *arg; /*! callback argument */
+ unsigned long word_size:8; /*!< SDMA data access word size */
+ unsigned long ext:1; /*!< 1: extend parameter structure */
+} dma_channel_params;
+
+typedef struct {
+ dma_channel_params common;
+ unsigned long p2p_dir:1; /*!< 0: per2 to per.
+ * the device of peripheral_type is per.
+ * 1: per to per2
+ * the device of peripheral_type is per2
+ */
+ unsigned long info_bits; /*!< info field in context */
+ unsigned long info_mask; /*!< info field mask in context */
+ __u32 watermark_level2; /*!< event2 threshold that
+ * triggers SDMA event
+ * just valid for p2p.
+ */
+ __u32 per_address2; /*!< Peripheral source
+ * physical address.
+ * just valid for p2p.
+ */
+ struct dma_channel_info info; /*!< the channel special parameter */
+} dma_channel_ext_params;
+
+/*!
+ * Structure containing sdma request parameters.
+ */
+typedef struct {
+ /*! physical source memory address */
+ __u8 *sourceAddr;
+ /*! physical destination memory address */
+ __u8 *destAddr;
+ /*! amount of data to transfer,
+ * updated during mxc_dma_get_config
+ */
+ __u16 count;
+ /*!< DONE bit of the buffer descriptor,
+ * updated during mxc_dma_get_config
+ * 0 - means the BD is done and closed by SDMA
+ * 1 - means the BD is still being processed by SDMA
+ */
+ int bd_done;
+ /*!< CONT bit of the buffer descriptor,
+ * set it if full multi-buffer descriptor mechanism
+ * required.
+ */
+ int bd_cont;
+ /*!< ERROR bit of the buffer descriptor,
+ * updated during mxc_dma_get_config.
+ * If it is set - there was an error during BD processing.
+ */
+ int bd_error;
+} dma_request_t;
+
+/*!
+ * Structure containing sdma request parameters.
+ */
+typedef struct {
+ /*! address of ap_2_ap script */
+ int mxc_sdma_ap_2_ap_addr;
+ /*! address of ap_2_bp script */
+ int mxc_sdma_ap_2_bp_addr;
+ /*! address of ap_2_ap_fixed script */
+ int mxc_sdma_ap_2_ap_fixed_addr;
+ /*! address of bp_2_ap script */
+ int mxc_sdma_bp_2_ap_addr;
+ /*! address of loopback_on_dsp_side script */
+ int mxc_sdma_loopback_on_dsp_side_addr;
+ /*! address of mcu_interrupt_only script */
+ int mxc_sdma_mcu_interrupt_only_addr;
+
+ /*! address of firi_2_per script */
+ int mxc_sdma_firi_2_per_addr;
+ /*! address of firi_2_mcu script */
+ int mxc_sdma_firi_2_mcu_addr;
+ /*! address of per_2_firi script */
+ int mxc_sdma_per_2_firi_addr;
+ /*! address of mcu_2_firi script */
+ int mxc_sdma_mcu_2_firi_addr;
+
+ /*! address of uart_2_per script */
+ int mxc_sdma_uart_2_per_addr;
+ /*! address of uart_2_mcu script */
+ int mxc_sdma_uart_2_mcu_addr;
+ /*! address of per_2_app script */
+ int mxc_sdma_per_2_app_addr;
+ /*! address of mcu_2_app script */
+ int mxc_sdma_mcu_2_app_addr;
+ /*! address of per_2_per script */
+ int mxc_sdma_per_2_per_addr;
+
+ /*! address of uartsh_2_per script */
+ int mxc_sdma_uartsh_2_per_addr;
+ /*! address of uartsh_2_mcu script */
+ int mxc_sdma_uartsh_2_mcu_addr;
+ /*! address of per_2_shp script */
+ int mxc_sdma_per_2_shp_addr;
+ /*! address of mcu_2_shp script */
+ int mxc_sdma_mcu_2_shp_addr;
+
+ /*! address of ata_2_mcu script */
+ int mxc_sdma_ata_2_mcu_addr;
+ /*! address of mcu_2_ata script */
+ int mxc_sdma_mcu_2_ata_addr;
+
+ /*! address of app_2_per script */
+ int mxc_sdma_app_2_per_addr;
+ /*! address of app_2_mcu script */
+ int mxc_sdma_app_2_mcu_addr;
+ /*! address of shp_2_per script */
+ int mxc_sdma_shp_2_per_addr;
+ /*! address of shp_2_mcu script */
+ int mxc_sdma_shp_2_mcu_addr;
+
+ /*! address of mshc_2_mcu script */
+ int mxc_sdma_mshc_2_mcu_addr;
+ /*! address of mcu_2_mshc script */
+ int mxc_sdma_mcu_2_mshc_addr;
+
+ /*! address of spdif_2_mcu script */
+ int mxc_sdma_spdif_2_mcu_addr;
+ /*! address of mcu_2_spdif script */
+ int mxc_sdma_mcu_2_spdif_addr;
+
+ /*! address of asrc_2_mcu script */
+ int mxc_sdma_asrc_2_mcu_addr;
+
+ /*! address of ext_mem_2_ipu script */
+ int mxc_sdma_ext_mem_2_ipu_addr;
+
+ /*! address of descrambler script */
+ int mxc_sdma_descrambler_addr;
+
+ /*! address of dptc_dvfs script */
+ int mxc_sdma_dptc_dvfs_addr;
+
+ int mxc_sdma_utra_addr;
+
+ /*! address where ram code starts */
+ int mxc_sdma_ram_code_start_addr;
+ /*! size of the ram code */
+ int mxc_sdma_ram_code_size;
+ /*! RAM image address */
+ unsigned short *mxc_sdma_start_addr;
+} sdma_script_start_addrs;
+
+/*! Structure to store the initialized dma_channel parameters */
+typedef struct mxc_sdma_channel_params {
+ /*! Channel type (static channel number or dynamic channel) */
+ unsigned int channel_num;
+ /*! Channel priority [0x1(lowest) - 0x7(highest)] */
+ unsigned int chnl_priority;
+ /*! Channel params */
+ dma_channel_params chnl_params;
+} mxc_sdma_channel_params_t;
+
+/*! Structure to store the initialized dma_channel extend parameters */
+typedef struct mxc_sdma_channel_ext_params {
+ /*! Channel type (static channel number or dynamic channel) */
+ unsigned int channel_num;
+ /*! Channel priority [0x1(lowest) - 0x7(highest)] */
+ unsigned int chnl_priority;
+ /*! Channel extend params */
+ dma_channel_ext_params chnl_ext_params;
+} mxc_sdma_channel_ext_params_t;
+
+/*! Private SDMA data structure */
+typedef struct mxc_dma_channel_private {
+ /*! ID of the buffer that was processed */
+ unsigned int buf_tail;
+ /*! Tasklet for the channel */
+ struct tasklet_struct chnl_tasklet;
+ /*! Flag indicates if interrupt is required after every BD transfer */
+ int intr_after_every_bd;
+} mxc_dma_channel_private_t;
+
+/*!
+ * Setup channel according to parameters.
+ * Must be called once after mxc_request_dma()
+ *
+ * @param channel channel number
+ * @param p channel parameters pointer
+ * @return 0 on success, error code on fail
+ */
+int mxc_dma_setup_channel(int channel, dma_channel_params * p);
+
+/*!
+ * Setup the channel priority. This can be used to change the default priority
+ * for the channel.
+ *
+ * @param channel channel number
+ * @param priority priority to be set for the channel
+ *
+ * @return 0 on success, error code on failure
+ */
+int mxc_dma_set_channel_priority(unsigned int channel, unsigned int priority);
+
+/*!
+ * Allocates dma channel.
+ * If channel's value is 0, then the function allocates a free channel
+ * dynamically and sets its value to channel.
+ * Else allocates requested channel if it is free.
+ * If the channel is busy or no free channels (in dynamic allocation) -EBUSY returned.
+ *
+ * @param channel pointer to channel number
+ * @param devicename device name
+ * @return 0 on success, error code on fail
+ */
+int mxc_request_dma(int *channel, const char *devicename);
+
+/*!
+ * Configures request parameters. Can be called multiple times after
+ * mxc_request_dma() and mxc_dma_setup_channel().
+ *
+ *
+ * @param channel channel number
+ * @param p request parameters pointer
+ * @param bd_index index of buffer descriptor to set
+ * @return 0 on success, error code on fail
+ */
+/* int mxc_dma_set_config(int channel, dma_request_t *p, int bd_index); */
+int mxc_dma_set_config(int channel, dma_request_t * p, int bd_index);
+
+/*!
+ * Returns request parameters.
+ *
+ * @param channel channel number
+ * @param p request parameters pointer
+ * @param bd_index index of buffer descriptor to get
+ * @return 0 on success, error code on fail
+ */
+/* int mxc_dma_get_config(int channel, dma_request_t *p, int bd_index); */
+int mxc_dma_get_config(int channel, dma_request_t * p, int bd_index);
+
+/*!
+ * This function is used by MXC IPC's write_ex2. It passes the a pointer to the
+ * data control structure to iapi_write_ipcv2()
+ *
+ * @param channel SDMA channel number
+ * @param ctrl_ptr Data Control structure pointer
+ */
+int mxc_sdma_write_ipcv2(int channel, void *ctrl_ptr);
+
+/*!
+ * This function is used by MXC IPC's read_ex2. It passes the a pointer to the
+ * data control structure to iapi_read_ipcv2()
+ *
+ * @param channel SDMA channel number
+ * @param ctrl_ptr Data Control structure pointer
+ */
+int mxc_sdma_read_ipcv2(int channel, void *ctrl_ptr);
+
+/*!
+ * Starts dma channel.
+ *
+ * @param channel channel number
+ */
+int mxc_dma_start(int channel);
+
+/*!
+ * Stops dma channel.
+ *
+ * @param channel channel number
+ */
+int mxc_dma_stop(int channel);
+
+/*!
+ * Frees dma channel.
+ *
+ * @param channel channel number
+ */
+void mxc_free_dma(int channel);
+
+/*!
+ * Sets callback function. Used with standard dma api
+ * for supporting interrupts
+ *
+ * @param channel channel number
+ * @param callback callback function pointer
+ * @param arg argument for callback function
+ */
+void mxc_dma_set_callback(int channel, dma_callback_t callback, void *arg);
+
+/*!
+ * Allocates uncachable buffer. Uses hash table.
+ *
+ * @param size size of allocated buffer
+ * @return pointer to buffer
+ */
+void *sdma_malloc(size_t size);
+
+#ifdef CONFIG_SDMA_IRAM
+/*!
+ * Allocates uncachable buffer from IRAM..
+ *
+ * @param size size of allocated buffer
+ * @return pointer to buffer
+ */
+void *sdma_iram_malloc(size_t size);
+#endif /*CONFIG_SDMA_IRAM */
+
+/*!
+ * Frees uncachable buffer. Uses hash table.
+ */
+void sdma_free(void *buf);
+
+/*!
+ * Converts virtual to physical address. Uses hash table.
+ *
+ * @param buf virtual address pointer
+ * @return physical address value
+ */
+unsigned long sdma_virt_to_phys(void *buf);
+
+/*!
+ * Converts physical to virtual address. Uses hash table.
+ *
+ * @param buf physical address value
+ * @return virtual address pointer
+ */
+void *sdma_phys_to_virt(unsigned long buf);
+
+/*!
+ * Configures the BD_INTR bit on a buffer descriptor parameters.
+ *
+ *
+ * @param channel channel number
+ * @param bd_index index of buffer descriptor to set
+ * @param bd_intr flag to set or clear the BD_INTR bit
+ */
+void mxc_dma_set_bd_intr(int channel, int bd_index, int bd_intr);
+
+/*!
+ * Gets the BD_INTR bit on a buffer descriptor.
+ *
+ *
+ * @param channel channel number
+ * @param bd_index index of buffer descriptor to set
+ *
+ * @return returns the BD_INTR bit status
+ */
+int mxc_dma_get_bd_intr(int channel, int bd_index);
+
+/*!
+ * Stop the current transfer
+ *
+ * @param channel channel number
+ * @param buffer_number number of buffers (beginning with 0),
+ * whose done bits should be reset to 0
+ */
+int mxc_dma_reset(int channel, int buffer_number);
+
+/*!
+ * This functions Returns the SDMA paramaters associated for a module
+ *
+ * @param channel_id the ID of the module requesting DMA
+ * @return returns the sdma parameters structure for the device
+ */
+mxc_sdma_channel_params_t *mxc_sdma_get_channel_params(mxc_dma_device_t
+ channel_id);
+
+/*!
+ * This functions marks the SDMA channels that are statically allocated
+ *
+ * @param chnl the channel array used to store channel information
+ */
+void mxc_get_static_channels(mxc_dma_channel_t * chnl);
+
+/*!
+ * Initializes SDMA driver
+ */
+int __init sdma_init(void);
+
+#define DEFAULT_ERR 1
+
+#endif
diff --git a/arch/arm/plat-mxc/include/mach/spba.h b/arch/arm/plat-mxc/include/mach/spba.h
new file mode 100644
index 000000000000..8d018be8a0b7
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/spba.h
@@ -0,0 +1,66 @@
+
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup SPBA Shared Peripheral Bus Arbiter (SPBA)
+ * @ingroup MSL_MX31 MSL_MX35 MSL_MX37 MSL_MX51 MSL_MXC91321
+ */
+
+/*!
+ * @file arch-mxc/spba.h
+ * @brief This file contains the Shared Peripheral Bus Arbiter (spba) API.
+ *
+ * @ingroup SPBA
+ */
+
+#ifndef __ASM_ARCH_MXC_SPBA_H__
+#define __ASM_ARCH_MXC_SPBA_H__
+
+#ifdef __KERNEL__
+
+#define MXC_SPBA_RAR_MASK 0x7
+
+/*!
+ * Defines three SPBA masters: A - ARM, C - SDMA (no master B for MX31)
+ */
+enum spba_masters {
+ SPBA_MASTER_A = 1,
+ SPBA_MASTER_B = 2,
+ SPBA_MASTER_C = 4,
+};
+
+/*!
+ * This function allows the three masters (A, B, C) to take ownership of a
+ * shared peripheral.
+ *
+ * @param mod specified module as defined in \b enum \b #spba_module
+ * @param master one of more (or-ed together) masters as defined in \b enum \b #spba_masters
+ *
+ * @return 0 if successful; -1 otherwise.
+ */
+int spba_take_ownership(int mod, int master);
+
+/*!
+ * This function releases the ownership for a shared peripheral.
+ *
+ * @param mod specified module as defined in \b enum \b #spba_module
+ * @param master one of more (or-ed together) masters as defined in \b enum \b #spba_masters
+ *
+ * @return 0 if successful; -1 otherwise.
+ */
+int spba_rel_ownership(int mod, int master);
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_ARCH_MXC_SPBA_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/system.h b/arch/arm/plat-mxc/include/mach/system.h
index bbfc37465fc5..c1edb8f2420f 100644
--- a/arch/arm/plat-mxc/include/mach/system.h
+++ b/arch/arm/plat-mxc/include/mach/system.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1999 ARM Limited
* Copyright (C) 2000 Deep Blue Solutions Ltd
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,14 +21,17 @@
#ifndef __ASM_ARCH_MXC_SYSTEM_H__
#define __ASM_ARCH_MXC_SYSTEM_H__
-static inline void arch_idle(void)
-{
- cpu_do_idle();
-}
+/*!
+ * This function puts the CPU into idle mode. It is called by default_idle()
+ * in process.c file.
+ */
+extern void arch_idle(void);
-static inline void arch_reset(char mode)
-{
- cpu_reset(0);
-}
+/*
+ * This function resets the system. It is called by machine_restart().
+ *
+ * @param mode indicates different kinds of resets
+ */
+extern void arch_reset(char mode);
-#endif /* __ASM_ARCH_MXC_SYSTEM_H__ */
+#endif /* __ASM_ARCH_MXC_SYSTEM_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/uncompress.h b/arch/arm/plat-mxc/include/mach/uncompress.h
index de6fe0365982..8314fafacfd5 100644
--- a/arch/arm/plat-mxc/include/mach/uncompress.h
+++ b/arch/arm/plat-mxc/include/mach/uncompress.h
@@ -27,6 +27,8 @@
#include <mach/hardware.h>
+unsigned int system_rev;
+
#define UART(x) (*(volatile unsigned long *)(serial_port + (x)))
#define USR2 0x98
diff --git a/arch/arm/plat-mxc/io.c b/arch/arm/plat-mxc/io.c
new file mode 100644
index 000000000000..7681ecf8dfb6
--- /dev/null
+++ b/arch/arm/plat-mxc/io.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * mxc custom ioremap implementation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+void *__iomem __mxc_ioremap(unsigned long cookie, size_t size,
+ unsigned int mtype)
+{
+ if (mtype == MT_DEVICE && IS_MEM_DEVICE_NONSHARED(cookie)) {
+ mtype = MT_DEVICE_NONSHARED;
+ }
+ return __arm_ioremap(cookie, size, mtype);
+}
+
+EXPORT_SYMBOL(__mxc_ioremap);
+
+void __mxc_iounmap(void __iomem * addr)
+{
+ extern void __iounmap(volatile void __iomem * addr);
+
+ __iounmap(addr);
+}
+
+EXPORT_SYMBOL(__mxc_iounmap);
diff --git a/arch/arm/plat-mxc/irq.c b/arch/arm/plat-mxc/irq.c
index d862c9e5f8db..70d9ce81b9d6 100644
--- a/arch/arm/plat-mxc/irq.c
+++ b/arch/arm/plat-mxc/irq.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or
@@ -19,6 +19,7 @@
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/sysdev.h>
#include <mach/common.h>
#define AVIC_BASE IO_ADDRESS(AVIC_BASE_ADDR)
@@ -32,8 +33,6 @@
#define AVIC_INTTYPEL (AVIC_BASE + 0x1C) /* int type reg low */
#define AVIC_NIPRIORITY(x) (AVIC_BASE + (0x20 + 4 * (7 - (x)))) /* int priority */
#define AVIC_NIVECSR (AVIC_BASE + 0x40) /* norm int vector/status */
-#define AVIC_FIVECSR (AVIC_BASE + 0x44) /* fast int vector/status */
-#define AVIC_INTSRCH (AVIC_BASE + 0x48) /* int source reg high */
#define AVIC_INTSRCL (AVIC_BASE + 0x4C) /* int source reg low */
#define AVIC_INTFRCH (AVIC_BASE + 0x50) /* int force reg high */
#define AVIC_INTFRCL (AVIC_BASE + 0x54) /* int force reg low */
@@ -42,10 +41,10 @@
#define AVIC_FIPNDH (AVIC_BASE + 0x60) /* fast int pending high */
#define AVIC_FIPNDL (AVIC_BASE + 0x64) /* fast int pending low */
-#define SYSTEM_PREV_REG IO_ADDRESS(IIM_BASE_ADDR + 0x20)
-#define SYSTEM_SREV_REG IO_ADDRESS(IIM_BASE_ADDR + 0x24)
-#define IIM_PROD_REV_SH 3
-#define IIM_PROD_REV_LEN 5
+#define IRQ_BIT(irq) (1 << (irq))
+
+static uint32_t saved_wakeup_low, saved_wakeup_high;
+static uint32_t suspend_wakeup_low, suspend_wakeup_high;
#ifdef CONFIG_MXC_IRQ_PRIOR
void imx_irq_set_priority(unsigned char irq, unsigned char prio)
@@ -77,12 +76,121 @@ static void mxc_unmask_irq(unsigned int irq)
__raw_writel(irq, AVIC_INTENNUM);
}
+/*!
+ * Set interrupt number "irq" in the AVIC as a wake-up source.
+ *
+ * @param irq interrupt source number
+ * @param enable enable as wake-up if equal to non-zero
+ * disble as wake-up if equal to zero
+ *
+ * @return This function returns 0 on success.
+ */
+static int mxc_set_wake_irq(unsigned int irq, unsigned int enable)
+{
+ uint32_t *wakeup_intr;
+ uint32_t irq_bit;
+
+ if (irq < 32) {
+ wakeup_intr = &suspend_wakeup_low;
+ irq_bit = IRQ_BIT(irq);
+ } else {
+ wakeup_intr = &suspend_wakeup_high;
+ irq_bit = IRQ_BIT(irq - 32);
+ }
+
+ if (enable) {
+ *wakeup_intr |= irq_bit;
+ } else {
+ *wakeup_intr &= ~irq_bit;
+ }
+
+ return 0;
+}
+
static struct irq_chip mxc_avic_chip = {
.ack = mxc_mask_irq,
.mask = mxc_mask_irq,
.unmask = mxc_unmask_irq,
+ .set_wake = mxc_set_wake_irq,
+};
+
+#ifdef CONFIG_PM
+/*!
+ * This function puts the AVIC in low-power mode/state.
+ * All the interrupts that are enabled are first saved.
+ * Only those interrupts which registers as a wake source by calling
+ * enable_irq_wake are enabled. All other interrupts are disabled.
+ *
+ * @param dev the system device structure used to give information
+ * on AVIC to suspend
+ * @param mesg the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_avic_suspend(struct sys_device *dev, pm_message_t mesg)
+{
+ saved_wakeup_high = __raw_readl(AVIC_INTENABLEH);
+ saved_wakeup_low = __raw_readl(AVIC_INTENABLEL);
+
+ __raw_writel(suspend_wakeup_high, AVIC_INTENABLEH);
+ __raw_writel(suspend_wakeup_low, AVIC_INTENABLEL);
+
+ return 0;
+}
+
+/*!
+ * This function brings the AVIC back from low-power state.
+ * All the interrupts enabled before suspension are re-enabled from
+ * the saved information.
+ *
+ * @param dev the system device structure used to give information
+ * on AVIC to resume
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_avic_resume(struct sys_device *dev)
+{
+ __raw_writel(saved_wakeup_high, AVIC_INTENABLEH);
+ __raw_writel(saved_wakeup_low, AVIC_INTENABLEL);
+
+ return 0;
+}
+
+#else
+#define mxc_avic_suspend NULL
+#define mxc_avic_resume NULL
+#endif /* CONFIG_PM */
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct sysdev_class mxc_avic_sysclass = {
+ .name = "mxc_irq",
+ .suspend = mxc_avic_suspend,
+ .resume = mxc_avic_resume,
};
+/*!
+ * This structure represents AVIC as a system device.
+ * System devices follow a slightly different driver model.
+ * They don't need to do dynammic driver binding, can't be probed,
+ * and don't reside on any type of peripheral bus.
+ * So, it is represented and treated a little differently.
+ */
+static struct sys_device mxc_avic_device = {
+ .id = 0,
+ .cls = &mxc_avic_sysclass,
+};
+
+/*
+ * This function is used to get the AVIC Lo and Hi interrupts
+ * that are enabled as wake up sources to wake up the core from suspend
+ */
+void mxc_get_wake_irq(u32 * wake_src[])
+{
+ *wake_src[0] = __raw_readl(AVIC_INTENABLEL);
+ *wake_src[1] = __raw_readl(AVIC_INTENABLEH);
+}
+
/*
* This function initializes the AVIC hardware and disables all the
* interrupts. It registers the interrupt enable and disable functions
@@ -91,7 +199,7 @@ static struct irq_chip mxc_avic_chip = {
void __init mxc_init_irq(void)
{
int i;
- u32 reg;
+ static int initialized;
/* put the AVIC into the reset value with
* all interrupts disabled
@@ -106,18 +214,46 @@ void __init mxc_init_irq(void)
/* all IRQ no FIQ */
__raw_writel(0, AVIC_INTTYPEH);
__raw_writel(0, AVIC_INTTYPEL);
- for (i = 0; i < MXC_MAX_INT_LINES; i++) {
- set_irq_chip(i, &mxc_avic_chip);
- set_irq_handler(i, handle_level_irq);
- set_irq_flags(i, IRQF_VALID);
+ if (!initialized) {
+ for (i = 0; i < MXC_MAX_INT_LINES; i++) {
+ set_irq_chip(i, &mxc_avic_chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
}
/* Set default priority value (0) for all IRQ's */
for (i = 0; i < 8; i++)
__raw_writel(0, AVIC_NIPRIORITY(i));
- /* init architectures chained interrupt handler */
- mxc_register_gpios();
+ if (MXC_INT_FORCE >= 32)
+ __raw_writel(1 << (MXC_INT_FORCE & 31), AVIC_INTFRCH);
+ else if (MXC_INT_FORCE >= 0)
+ __raw_writel(1 << MXC_INT_FORCE, AVIC_INTFRCL);
+
+ initialized = 1;
printk(KERN_INFO "MXC IRQ initialized\n");
}
+
+/*!
+ * This function registers AVIC hardware as a system device.
+ * System devices will only be suspended with interrupts disabled, and
+ * after all other devices have been suspended. On resume, they will be
+ * resumed before any other devices, and also with interrupts disabled.
+ *
+ * @return This function returns 0 on success.
+ */
+static int __init mxc_avic_sysinit(void)
+{
+ int ret = 0;
+
+ ret = sysdev_class_register(&mxc_avic_sysclass);
+ if (ret == 0) {
+ ret = sysdev_register(&mxc_avic_device);
+ }
+
+ return ret;
+}
+
+arch_initcall(mxc_avic_sysinit);
diff --git a/arch/arm/plat-mxc/isp1301xc.c b/arch/arm/plat-mxc/isp1301xc.c
new file mode 100644
index 000000000000..dceea44fd4da
--- /dev/null
+++ b/arch/arm/plat-mxc/isp1301xc.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <linux/i2c.h>
+
+#include <mach/arc_otg.h>
+
+/*
+ * ISP1301 register addresses,all register of ISP1301
+ * is one-byte length register
+ */
+
+/* ISP1301: I2C device address */
+#define ISP1301_DEV_ADDR 0x2D
+
+/* ISP 1301 register set*/
+#define ISP1301_MODE_REG1_SET 0x04
+#define ISP1301_MODE_REG1_CLR 0x05
+
+#define ISP1301_CTRL_REG1_SET 0x06
+#define ISP1301_CTRL_REG1_CLR 0x07
+
+#define ISP1301_INT_SRC_REG 0x08
+#define ISP1301_INT_LAT_REG_SET 0x0a
+#define ISP1301_INT_LAT_REG_CLR 0x0b
+#define ISP1301_INT_FALSE_REG_SET 0x0c
+#define ISP1301_INT_FALSE_REG_CLR 0x0d
+#define ISP1301_INT_TRUE_REG_SET 0x0e
+#define ISP1301_INT_TRUE_REG_CLR 0x0f
+
+#define ISP1301_CTRL_REG2_SET 0x10
+#define ISP1301_CTRL_REG2_CLR 0x11
+
+#define ISP1301_MODE_REG2_SET 0x12
+#define ISP1301_MODE_REG2_CLR 0x13
+
+#define ISP1301_BCD_DEV_REG0 0x14
+#define ISP1301_BCD_DEV_REG1 0x15
+
+/* OTG Control register bit description */
+#define DP_PULLUP 0x01
+#define DM_PULLUP 0x02
+#define DP_PULLDOWN 0x04
+#define DM_PULLDOWN 0x08
+#define ID_PULLDOWN 0x10
+#define VBUS_DRV 0x20
+#define VBUS_DISCHRG 0x40
+#define VBUS_CHRG 0x80
+
+/* Mode Control 1 register bit description */
+#define SPEED_REG 0x01
+#define SUSPEND_REG 0x02
+#define DAT_SE0 0x04
+#define TRANSP_EN 0x08
+#define BDIS_ACON_EN 0x10
+#define OE_INT_EN 0x20
+#define UART_EN 0x40
+
+/* Mode Control 2 register bit description */
+#define SPD_SUSP_CTRL 0x02
+#define BI_DI 0x04
+
+static int isp1301_attach(struct i2c_adapter *adapter);
+static int isp1301_detach(struct i2c_client *client);
+
+static struct i2c_driver isp1301_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "isp1301 Client",
+ },
+ .attach_adapter = isp1301_attach,
+ .detach_client = isp1301_detach,
+};
+
+static struct i2c_client isp1301_i2c_client = {
+ .name = "isp1301 I2C dev",
+ .addr = ISP1301_DEV_ADDR,
+ .driver = &isp1301_i2c_driver,
+};
+
+static unsigned short normal_i2c[] = { ISP1301_DEV_ADDR, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static int isp1301_detect_client(struct i2c_adapter *adapter, int address,
+ int kind)
+{
+ isp1301_i2c_client.adapter = adapter;
+ if (i2c_attach_client(&isp1301_i2c_client)) {
+ isp1301_i2c_client.adapter = NULL;
+ printk(KERN_ERR "isp1301_attach: i2c_attach_client failed\n");
+ return -1;
+ }
+
+ printk(KERN_INFO "isp1301 Detected\n");
+ return 0;
+}
+
+/*!
+ * isp1301 I2C attach function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int isp1301_attach(struct i2c_adapter *adapter)
+{
+ return i2c_probe(adapter, &addr_data, &isp1301_detect_client);
+}
+
+/*!
+ * isp1301 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int isp1301_detach(struct i2c_client *client)
+{
+ int err;
+
+ if (!isp1301_i2c_client.adapter)
+ return -1;
+
+ err = i2c_detach_client(&isp1301_i2c_client);
+ isp1301_i2c_client.adapter = NULL;
+
+ return err;
+}
+
+static void isp1301_init(struct fsl_xcvr_ops *this)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ i2c_add_driver(&isp1301_i2c_driver);
+}
+
+static void isp1301_uninit(struct fsl_xcvr_ops *this)
+{
+ // DDD do this for host only:
+ /* disable OTG VBUS */
+ i2c_del_driver(&isp1301_i2c_driver);
+}
+
+/* Write ISP1301 register*/
+static inline void isp1301_write_reg(char reg, char data)
+{
+ i2c_smbus_write_byte_data(&isp1301_i2c_client, reg, data);
+}
+
+/* read ISP1301 register*/
+static inline char isp1301_read_reg(char reg)
+{
+ return i2c_smbus_read_byte_data(&isp1301_i2c_client, reg);
+}
+
+/* set ISP1301 as USB host*/
+static inline void isp1301_set_serial_host(void)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ isp1301_write_reg(ISP1301_MODE_REG2_CLR, 0xFF);
+#if defined(CONFIG_MXC_USB_SB3) || defined(CONFIG_MXC_USB_DB4)
+ isp1301_write_reg(ISP1301_MODE_REG2_SET, SPD_SUSP_CTRL | BI_DI);
+#else
+ isp1301_write_reg(ISP1301_MODE_REG2_SET, SPD_SUSP_CTRL);
+#endif
+
+ isp1301_write_reg(ISP1301_MODE_REG1_CLR, 0xFF);
+#if defined(CONFIG_MXC_USB_SB3) || defined(CONFIG_MXC_USB_SU6)
+ isp1301_write_reg(ISP1301_MODE_REG1_SET, DAT_SE0 | SPEED_REG);
+#else
+ isp1301_write_reg(ISP1301_MODE_REG1_SET, SPEED_REG);
+#endif
+
+ /* configure transceiver for host mode */
+ isp1301_write_reg(ISP1301_CTRL_REG1_SET,
+ (VBUS_DRV | DP_PULLDOWN | DM_PULLDOWN));
+}
+
+/* set ISP1301 as USB device */
+static inline void isp1301_set_serial_dev(void)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ isp1301_write_reg(ISP1301_MODE_REG2_CLR, 0xFF);
+#if defined(CONFIG_MXC_USB_SB3) || defined(CONFIG_MXC_USB_DB4)
+ isp1301_write_reg(ISP1301_MODE_REG2_SET, SPD_SUSP_CTRL | BI_DI);
+#else
+ isp1301_write_reg(ISP1301_MODE_REG2_SET, SPD_SUSP_CTRL);
+#endif
+
+ isp1301_write_reg(ISP1301_MODE_REG1_CLR, 0xFF);
+#if defined(CONFIG_MXC_USB_SB3) || defined(CONFIG_MXC_USB_SU6)
+ isp1301_write_reg(ISP1301_MODE_REG1_SET, DAT_SE0 | SPEED_REG);
+#else
+ isp1301_write_reg(ISP1301_MODE_REG1_SET, SPEED_REG);
+#endif
+
+ /* FS mode, DP pull down, DM pull down */
+ isp1301_write_reg(ISP1301_CTRL_REG1_SET,
+ (DP_PULLDOWN | DM_PULLDOWN | DP_PULLUP));
+}
+
+static void isp1301_set_vbus_power(struct fsl_xcvr_ops *this,
+ struct fsl_usb2_platform_data *pdata, int on)
+{
+ pr_debug("%s(on=%d)\n", __FUNCTION__, on);
+ if (on) {
+ /* disable D+ pull-up */
+ isp1301_write_reg(ISP1301_CTRL_REG1_CLR, DP_PULLUP);
+ /* enable D+ pull-down */
+ isp1301_write_reg(ISP1301_CTRL_REG1_SET, DP_PULLDOWN);
+ /* turn on Vbus */
+ isp1301_write_reg(ISP1301_CTRL_REG1_SET, VBUS_DRV);
+ } else {
+ /* D+ pull up, D- pull down */
+ isp1301_write_reg(ISP1301_CTRL_REG1_SET,
+ (DP_PULLUP | DM_PULLDOWN));
+ /* disable D- pull up, disable D+ pull down */
+ isp1301_write_reg(ISP1301_CTRL_REG1_CLR,
+ (DM_PULLUP | DP_PULLDOWN));
+ }
+}
+
+/*
+ * Enable or disable the D+ pullup.
+ */
+static void isp1301_pullup(int on)
+{
+ pr_debug("%s(%d)\n", __func__, on);
+
+ if (on)
+ isp1301_write_reg(ISP1301_CTRL_REG1_SET, DP_PULLUP);
+ else
+ isp1301_write_reg(ISP1301_CTRL_REG1_CLR, DP_PULLUP);
+}
+
+static struct fsl_xcvr_ops isp1301_ops_otg = {
+ .name = "isp1301",
+ .xcvr_type = PORTSC_PTS_SERIAL,
+ .init = isp1301_init,
+ .uninit = isp1301_uninit,
+ .set_host = isp1301_set_serial_host,
+ .set_device = isp1301_set_serial_dev,
+ .set_vbus_power = isp1301_set_vbus_power,
+ .pullup = isp1301_pullup,
+};
+
+extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops);
+
+static int __init isp1301xc_init(void)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ fsl_usb_xcvr_register(&isp1301_ops_otg);
+
+ return 0;
+}
+
+extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops);
+
+static void __exit isp1301xc_exit(void)
+{
+ fsl_usb_xcvr_unregister(&isp1301_ops_otg);
+}
+
+module_init(isp1301xc_init);
+module_exit(isp1301xc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("isp1301");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/isp1504xc.c b/arch/arm/plat-mxc/isp1504xc.c
new file mode 100644
index 000000000000..7ef89db09364
--- /dev/null
+++ b/arch/arm/plat-mxc/isp1504xc.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/delay.h>
+#include <linux/usb/fsl_xcvr.h>
+
+#include <mach/hardware.h>
+#include <mach/arc_otg.h>
+#include <asm/mach-types.h>
+
+/* ISP 1504 register addresses */
+#define ISP1504_VID_LOW 0x00 /* Vendor ID low */
+#define ISP1504_VID_HIGH 0x01 /* Vendor ID high */
+#define ISP1504_PID_LOW 0x02 /* Product ID low */
+#define ISP1504_PID_HIGH 0x03 /* Product ID high */
+#define ISP1504_FUNC 0x04 /* Function Control */
+#define ISP1504_ITFCTL 0x07 /* Interface Control */
+#define ISP1504_OTGCTL 0x0A /* OTG Control */
+
+/* add to above register address to access Set/Clear functions */
+#define ISP1504_REG_SET 0x01
+#define ISP1504_REG_CLEAR 0x02
+
+/* 1504 OTG Control Register bits */
+#define USE_EXT_VBUS_IND (1 << 7) /* Use ext. Vbus indicator */
+#define DRV_VBUS_EXT (1 << 6) /* Drive Vbus external */
+#define DRV_VBUS (1 << 5) /* Drive Vbus */
+#define CHRG_VBUS (1 << 4) /* Charge Vbus */
+#define DISCHRG_VBUS (1 << 3) /* Discharge Vbus */
+#define DM_PULL_DOWN (1 << 2) /* enable DM Pull Down */
+#define DP_PULL_DOWN (1 << 1) /* enable DP Pull Down */
+#define ID_PULL_UP (1 << 0) /* enable ID Pull Up */
+
+/* 1504 OTG Function Control Register bits */
+#define SUSPENDM (1 << 6) /* places the PHY into
+ low-power mode */
+#define DRV_RESET (1 << 5) /* Active HIGH transceiver
+ reset */
+
+/*!
+ * read ULPI register 'reg' thru VIEWPORT register 'view'
+ *
+ * @param reg register to read
+ * @param view the ULPI VIEWPORT register address
+ * @return return isp1504 register value
+ */
+static u8 isp1504_read(int reg, volatile u32 *view)
+{
+ u32 data;
+
+ /* make sure interface is running */
+ if (!(__raw_readl(view) && ULPIVW_SS)) {
+ __raw_writel(ULPIVW_WU, view);
+ do { /* wait for wakeup */
+ data = __raw_readl(view);
+ } while (data & ULPIVW_WU);
+ }
+
+ /* read the register */
+ __raw_writel((ULPIVW_RUN | (reg << ULPIVW_ADDR_SHIFT)), view);
+
+ do { /* wait for completion */
+ data = __raw_readl(view);
+ } while (data & ULPIVW_RUN);
+
+ return (u8) (data >> ULPIVW_RDATA_SHIFT) & ULPIVW_RDATA_MASK;
+}
+
+/*!
+ * set bits into OTG ISP1504 register 'reg' thru VIEWPORT register 'view'
+ *
+ * @param bits set value
+ * @param reg which register
+ * @param view the ULPI VIEWPORT register address
+ */
+static void isp1504_set(u8 bits, int reg, volatile u32 *view)
+{
+ u32 data;
+
+ /* make sure interface is running */
+ if (!(__raw_readl(view) && ULPIVW_SS)) {
+ __raw_writel(ULPIVW_WU, view);
+ do { /* wait for wakeup */
+ data = __raw_readl(view);
+ } while (data & ULPIVW_WU);
+ }
+
+ __raw_writel((ULPIVW_RUN | ULPIVW_WRITE |
+ ((reg + ISP1504_REG_SET) << ULPIVW_ADDR_SHIFT) |
+ ((bits & ULPIVW_WDATA_MASK) << ULPIVW_WDATA_SHIFT)),
+ view);
+
+ while (__raw_readl(view) & ULPIVW_RUN) /* wait for completion */
+ continue;
+}
+
+/*!
+ * clear bits in OTG ISP1504 register 'reg' thru VIEWPORT register 'view'
+ *
+ * @param bits bits to clear
+ * @param reg in this register
+ * @param view the ULPI VIEWPORT register address
+ */
+static void isp1504_clear(u8 bits, int reg, volatile u32 *view)
+{
+ __raw_writel((ULPIVW_RUN | ULPIVW_WRITE |
+ ((reg + ISP1504_REG_CLEAR) << ULPIVW_ADDR_SHIFT) |
+ ((bits & ULPIVW_WDATA_MASK) << ULPIVW_WDATA_SHIFT)),
+ view);
+
+ while (__raw_readl(view) & ULPIVW_RUN) /* wait for completion */
+ continue;
+}
+
+extern int gpio_usbotg_hs_active(void);
+
+static void isp1508_fix(u32 *view)
+{
+ if (!machine_is_mx31_3ds())
+ gpio_usbotg_hs_active();
+
+ /* Set bits IND_PASS_THRU and IND_COMPL */
+ isp1504_set(0x60, ISP1504_ITFCTL, view);
+
+ /* Set bit USE_EXT_VBUS_IND */
+ isp1504_set(USE_EXT_VBUS_IND, ISP1504_OTGCTL, view);
+}
+
+/*!
+ * set vbus power
+ *
+ * @param view viewport register
+ * @param on power on or off
+ */
+static void isp1504_set_vbus_power(struct fsl_xcvr_ops *this,
+ struct fsl_usb2_platform_data *pdata, int on)
+{
+ u32 *view = pdata->regs + ULPIVW_OFF;
+
+ pr_debug("real %s(on=%d) view=0x%p\n", __FUNCTION__, on, view);
+
+ pr_debug("ULPI Vendor ID 0x%x Product ID 0x%x\n",
+ (isp1504_read(ISP1504_VID_HIGH, view) << 8) |
+ isp1504_read(ISP1504_VID_LOW, view),
+ (isp1504_read(ISP1504_PID_HIGH, view) << 8) |
+ isp1504_read(ISP1504_PID_LOW, view));
+
+ pr_debug("OTG Control before=0x%x\n",
+ isp1504_read(ISP1504_OTGCTL, view));
+
+ if (on) {
+ isp1504_set(DRV_VBUS_EXT | /* enable external Vbus */
+ DRV_VBUS | /* enable internal Vbus */
+ USE_EXT_VBUS_IND | /* use external indicator */
+ CHRG_VBUS, /* charge Vbus */
+ ISP1504_OTGCTL, view);
+
+ } else {
+ isp1508_fix(view);
+
+ isp1504_clear(DRV_VBUS_EXT | /* disable external Vbus */
+ DRV_VBUS, /* disable internal Vbus */
+ ISP1504_OTGCTL, view);
+
+ isp1504_set(USE_EXT_VBUS_IND | /* use external indicator */
+ DISCHRG_VBUS, /* discharge Vbus */
+ ISP1504_OTGCTL, view);
+ }
+
+ pr_debug("OTG Control after = 0x%x\n",
+ isp1504_read(ISP1504_OTGCTL, view));
+}
+
+/*!
+ * set remote wakeup
+ *
+ * @param view viewport register
+ */
+static void isp1504_set_remote_wakeup(u32 * view)
+{
+ __raw_writel(~ULPIVW_WRITE & __raw_readl(view), view);
+ __raw_writel((1 << ULPIVW_PORT_SHIFT) | __raw_readl(view), view);
+ __raw_writel(ULPIVW_RUN | __raw_readl(view), view);
+
+ while (__raw_readl(view) & ULPIVW_RUN) /* wait for completion */
+ continue;
+}
+
+static void isp1504_init(struct fsl_xcvr_ops *this)
+{
+ pr_debug("%s:\n", __FUNCTION__);
+}
+
+static void isp1504_uninit(struct fsl_xcvr_ops *this)
+{
+ pr_debug("%s:\n", __FUNCTION__);
+}
+
+static void isp1504_suspend(struct fsl_xcvr_ops *this)
+{
+ pr_debug("%s\n", __func__);
+
+ /* send suspend command */
+ isp1504_clear(SUSPENDM, ISP1504_FUNC, &UOG_ULPIVIEW);
+ pr_debug("%s.\n", __func__);
+}
+
+/*!
+ * Set the 1504 transceiver to the proper mode for testing purposes.
+ *
+ * @param view the ULPI VIEWPORT register address
+ * @param test_mode Set the 1504 transceiver to disable bit stuffing and NRZI
+ */
+ static void isp1504_set_test_mode(volatile u32 *view, enum usb_test_mode test_mode)
+{
+ if (test_mode == USB_TEST_J || test_mode == USB_TEST_K) {
+ printk(KERN_INFO "udc: disable bit stuffing and NRZI\n");
+ /* Disable bit-stuffing and NRZI encoding. */
+ isp1504_set(0x10, 0x04, view);
+ }
+}
+
+static struct fsl_xcvr_ops isp1504_ops = {
+ .name = "isp1504",
+ .xcvr_type = PORTSC_PTS_ULPI,
+ .init = isp1504_init,
+ .uninit = isp1504_uninit,
+ .suspend = isp1504_suspend,
+ .set_vbus_power = isp1504_set_vbus_power,
+ .set_remote_wakeup = isp1504_set_remote_wakeup,
+ .set_test_mode = isp1504_set_test_mode,
+};
+
+extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops);
+extern int fsl_usb_xcvr_suspend(struct fsl_xcvr_ops *xcvr_ops);
+
+static int __init isp1504xc_init(void)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ fsl_usb_xcvr_register(&isp1504_ops);
+
+ /* suspend isp1504 */
+ if (fsl_usb_xcvr_suspend(&isp1504_ops))
+ pr_debug("%s: failed to suspend isp1504\n", __func__);
+
+ return 0;
+}
+
+extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops);
+
+static void __exit isp1504xc_exit(void)
+{
+ fsl_usb_xcvr_unregister(&isp1504_ops);
+}
+
+module_init(isp1504xc_init);
+module_exit(isp1504xc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("isp1504 xcvr driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/leds.c b/arch/arm/plat-mxc/leds.c
new file mode 100644
index 000000000000..e059d238f82b
--- /dev/null
+++ b/arch/arm/plat-mxc/leds.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * The LED can be used for debugging purpose. To enalbe the LEDs, in the
+ * config file, select:
+ * CONFIG_LEDS
+ * CONFIG_LEDS_TIMER --- enable the OS tick LED once every 50 ticks (.5sec)
+ * CONFIG_LEDS_CPU --- enable the cpu idle in/out LED (blink fast)
+ *
+ * The two LEDs can be disabled through user space by issuing:
+ * echo "claim" > /sys/devices/system/leds/leds0/event
+ * To release the LEDs back to the normal operation, do:
+ * echo "release" > /sys/devices/system/leds/leds0/event
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <asm/leds.h>
+
+#define LED_STATE_ENABLED (1 << 0)
+#define LED_STATE_CLAIMED (1 << 1)
+
+static unsigned int led_state;
+static unsigned int hw_led_state;
+
+static void mxc_leds_event(led_event_t evt)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ switch (evt) {
+ case led_start:
+ hw_led_state = MXC_BD_LED1 | MXC_BD_LED2;
+ led_state = LED_STATE_ENABLED;
+ break;
+
+ case led_stop:
+ case led_halted:
+ hw_led_state = 0;
+ led_state &= ~LED_STATE_ENABLED;
+ MXC_BD_LED_OFF(MXC_BD_LED1 | MXC_BD_LED2);
+ break;
+
+ case led_claim:
+ led_state |= LED_STATE_CLAIMED;
+ hw_led_state = 0;
+ break;
+
+ case led_release:
+ led_state &= ~LED_STATE_CLAIMED;
+ hw_led_state = 0;
+ break;
+
+#ifdef CONFIG_LEDS_TIMER
+ case led_timer:
+ if (!(led_state & LED_STATE_CLAIMED))
+ hw_led_state ^= MXC_BD_LED1;
+ break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+ case led_idle_start:
+ if (!(led_state & LED_STATE_CLAIMED))
+ hw_led_state &= ~MXC_BD_LED2;
+ break;
+
+ case led_idle_end:
+ if (!(led_state & LED_STATE_CLAIMED))
+ hw_led_state |= MXC_BD_LED2;
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ if (led_state & LED_STATE_ENABLED) {
+ MXC_BD_LED_OFF(~hw_led_state);
+ MXC_BD_LED_ON(hw_led_state);
+ }
+
+ local_irq_restore(flags);
+}
+
+static int __init mxc_leds_init(void)
+{
+ led_state = LED_STATE_ENABLED;
+ leds_event = mxc_leds_event;
+ return 0;
+}
+
+core_initcall(mxc_leds_init);
diff --git a/arch/arm/plat-mxc/mc13783_xc.c b/arch/arm/plat-mxc/mc13783_xc.c
new file mode 100644
index 000000000000..3fad32f3b230
--- /dev/null
+++ b/arch/arm/plat-mxc/mc13783_xc.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <mach/pmic_external.h>
+#include <mach/pmic_convity.h>
+#include <mach/arc_otg.h>
+
+/* Events to be passed to the thread */
+#define MC13783_USB_VBUS_ON 0x0001
+#define MC13783_USB_VBUS_OFF 0x0002
+#define MC13783_USB_DETECT_MINI_A 0x0004
+#define MC13783_USB_DETECT_MINI_B 0x0008
+
+extern void otg_set_serial_peripheral(void);
+extern void otg_set_serial_host(void);
+
+static unsigned int p_event;
+static PMIC_CONVITY_EVENTS g_event;
+static PMIC_CONVITY_HANDLE pmic_handle = (PMIC_CONVITY_HANDLE) NULL;
+
+static void xc_workqueue_handler(struct work_struct *work);
+
+DECLARE_WORK(xc_work, xc_workqueue_handler);
+
+DECLARE_MUTEX(pmic_mx);
+
+static void pmic_event_handler(const PMIC_CONVITY_EVENTS event)
+{
+ if (event & USB_DETECT_4V4_RISE)
+ pr_debug("%s: USB_DETECT_4V4_RISE\n", __func__);
+
+ if (event & USB_DETECT_4V4_FALL)
+ pr_debug("%s: USB_DETECT_4V4_FALL\n", __func__);
+
+ if (event & USB_DETECT_2V0_RISE)
+ pr_debug("%s: USB_DETECT_2V0_RISE\n", __func__);
+
+ if (event & USB_DETECT_2V0_FALL)
+ pr_debug("%s: USB_DETECT_2V0_FALL\n", __func__);
+
+ if (event & USB_DETECT_0V8_RISE)
+ pr_debug("%s: USB_DETECT_0V8_RISE\n", __func__);
+
+ if (event & USB_DETECT_0V8_FALL)
+ pr_debug("%s: USB_DETECT_0V8_FALL\n", __func__);
+
+ if (event & USB_DETECT_MINI_B) {
+ pr_debug("%s: USB_DETECT_MINI_B\n", __func__);
+ otg_set_serial_peripheral();
+ g_event = USB_DETECT_MINI_B;
+ p_event = MC13783_USB_DETECT_MINI_B;
+ schedule_work(&xc_work);
+ }
+ if (event & USB_DETECT_MINI_A) {
+ pr_debug("%s: USB_DETECT_MINI_A\n", __func__);
+ otg_set_serial_host();
+ g_event = USB_DETECT_MINI_A;
+ p_event = MC13783_USB_DETECT_MINI_A;
+ schedule_work(&xc_work);
+ }
+
+ /*
+ * Mini-B cable insertion/removal does not generate cable-detect
+ * event, so we rely on the VBUS changes to identify a mini-b cable
+ * connect. This logic is only used if mini-b is the first cable that
+ * is connected after bootup. At all other times, removal of mini-a
+ * cable is used to initialize peripheral.
+ */
+ if (g_event != USB_DETECT_MINI_A && g_event != USB_DETECT_MINI_B) {
+ if ((event & USB_DETECT_0V8_RISE) &&
+ (event & USB_DETECT_2V0_RISE) &&
+ (event & USB_DETECT_4V4_RISE)) {
+ otg_set_serial_peripheral();
+ g_event = USB_DETECT_MINI_B;
+ p_event = MC13783_USB_DETECT_MINI_B;
+ schedule_work(&xc_work);
+ }
+ }
+}
+
+static int usb_pmic_mod_init(void)
+{
+ PMIC_STATUS rs = PMIC_ERROR;
+
+ init_MUTEX_LOCKED(&pmic_mx);
+
+ rs = pmic_convity_open(&pmic_handle, USB);
+ if (rs != PMIC_SUCCESS) {
+ printk(KERN_ERR "pmic_convity_open returned error %d\n", rs);
+ return rs;
+ }
+
+ rs = pmic_convity_set_callback(pmic_handle, pmic_event_handler,
+ USB_DETECT_4V4_RISE | USB_DETECT_4V4_FALL
+ | USB_DETECT_2V0_RISE |
+ USB_DETECT_2V0_FALL | USB_DETECT_0V8_RISE
+ | USB_DETECT_0V8_FALL | USB_DETECT_MINI_A
+ | USB_DETECT_MINI_B);
+
+ if (rs != PMIC_SUCCESS) {
+ printk(KERN_ERR
+ "pmic_convity_set_callback returned error %d\n", rs);
+ return rs;
+ }
+
+ return rs;
+}
+
+static void usb_pmic_mod_exit(void)
+{
+ PMIC_STATUS rs;
+
+ pmic_convity_set_mode(pmic_handle, RS232_1);
+ pmic_convity_clear_callback(pmic_handle);
+
+ if (pmic_handle != (PMIC_CONVITY_HANDLE) NULL) {
+ rs = pmic_convity_close(pmic_handle);
+ if (rs != PMIC_SUCCESS) {
+ printk(KERN_ERR
+ "pmic_convity_close() returned error %d", rs);
+ } else {
+ pmic_handle = (PMIC_CONVITY_HANDLE) NULL;
+ }
+ }
+}
+
+static inline void mc13783_set_host(void)
+{
+ PMIC_STATUS rs = PMIC_ERROR;
+
+ rs = pmic_convity_usb_otg_clear_config(pmic_handle, USB_OTG_SE0CONN);
+ rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_PU);
+
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_UDM_PD);
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_UDP_PD);
+
+ if (rs != PMIC_SUCCESS)
+ printk(KERN_ERR "mc13783_set_host failed\n");
+
+}
+
+static inline void mc13783_set_peripheral(void)
+{
+ PMIC_STATUS rs = PMIC_ERROR;
+
+ rs = pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDM_PD);
+ rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDP_PD);
+
+ rs |= pmic_convity_usb_set_speed(pmic_handle, USB_FULL_SPEED);
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_OTG_SE0CONN);
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_PU);
+
+ if (rs != PMIC_SUCCESS)
+ printk(KERN_ERR "mc13783_set_peripheral failed\n");
+}
+
+void mc13783_set_vbus_power(struct fsl_xcvr_ops *this,
+ struct fsl_usb2_platform_data *pdata, int on)
+{
+ if (on) {
+ p_event = MC13783_USB_VBUS_ON;
+ schedule_work(&xc_work);
+ }
+}
+
+static struct fsl_xcvr_ops mc13783_ops_otg = {
+ .name = "mc13783",
+ .xcvr_type = PORTSC_PTS_SERIAL,
+ .set_host = mc13783_set_host,
+ .set_device = mc13783_set_peripheral,
+ .set_vbus_power = mc13783_set_vbus_power,
+};
+
+extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops);
+
+static void xc_workqueue_handler(struct work_struct *work)
+{
+ PMIC_STATUS rs = PMIC_ERROR;
+
+ down(&pmic_mx);
+
+ switch (p_event) {
+ case MC13783_USB_VBUS_OFF:
+ mc13783_set_peripheral();
+ break;
+ case MC13783_USB_VBUS_ON:
+ mc13783_set_host();
+ break;
+ case MC13783_USB_DETECT_MINI_B:
+ rs = pmic_convity_set_output(pmic_handle, true, false);
+ rs |=
+ pmic_convity_usb_otg_clear_config(pmic_handle,
+ USB_VBUS_CURRENT_LIMIT_LOW_30MS);
+
+ if (rs != PMIC_SUCCESS)
+ printk(KERN_ERR "MC13783_USB_VBUS_OFF failed\n");
+ break;
+ case MC13783_USB_DETECT_MINI_A:
+ rs = pmic_convity_set_output(pmic_handle, true, true);
+ rs |=
+ pmic_convity_usb_otg_set_config(pmic_handle,
+ USB_VBUS_CURRENT_LIMIT_LOW_30MS);
+
+ if (rs != PMIC_SUCCESS)
+ printk(KERN_ERR "MC13783_USB_VBUS_ON failed\n");
+ break;
+ default:
+ break;
+ }
+ up(&pmic_mx);
+}
+
+int mc13783xc_init(void)
+{
+ PMIC_STATUS rs = PMIC_ERROR;
+
+#if defined(CONFIG_MXC_USB_SB3)
+ int xc_mode = USB_SINGLE_ENDED_BIDIR;
+#elif defined(CONFIG_MXC_USB_SU6)
+ int xc_mode = USB_SINGLE_ENDED_UNIDIR;
+#elif defined(CONFIG_MXC_USB_DB4)
+ int xc_mode = USB_DIFFERENTIAL_BIDIR;
+#else
+ int xc_mode = USB_DIFFERENTIAL_UNIDIR;
+#endif
+
+ rs = usb_pmic_mod_init();
+ if (rs != PMIC_SUCCESS) {
+ usb_pmic_mod_exit();
+ printk(KERN_ERR "usb_pmic_mod_init failed\n");
+ return rs;
+ }
+
+ rs = pmic_convity_usb_set_xcvr(pmic_handle, xc_mode);
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_OTG_SE0CONN);
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USBXCVREN);
+ rs |= pmic_convity_set_output(pmic_handle, false, true);
+
+ rs |= pmic_convity_usb_otg_set_config(pmic_handle, USB_PULL_OVERRIDE);
+ rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_USBCNTRL);
+ rs |= pmic_convity_usb_otg_clear_config(pmic_handle, USB_DP150K_PU);
+
+ if (rs != PMIC_SUCCESS)
+ printk(KERN_ERR "pmic configuration failed\n");
+
+ fsl_usb_xcvr_register(&mc13783_ops_otg);
+
+ mc13783_set_peripheral();
+
+ return rs;
+}
+
+extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops);
+
+void mc13783xc_uninit(void)
+{
+ /* Clear stuff from init */
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_OTG_SE0CONN);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USBXCVREN);
+ pmic_convity_set_output(pmic_handle, false, false);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_PULL_OVERRIDE);
+
+ /* Clear host mode */
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDP_PD);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDM_PD);
+
+ /* Clear peripheral mode */
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_PU);
+
+ /* Vbus off */
+ pmic_convity_set_output(pmic_handle, true, false);
+ pmic_convity_usb_otg_clear_config(pmic_handle,
+ USB_VBUS_CURRENT_LIMIT_LOW_30MS);
+
+ usb_pmic_mod_exit();
+
+ fsl_usb_xcvr_unregister(&mc13783_ops_otg);
+}
+
+module_init(mc13783xc_init);
+module_exit(mc13783xc_uninit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("mc13783xc");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c
new file mode 100644
index 000000000000..ee43e6a6710e
--- /dev/null
+++ b/arch/arm/plat-mxc/pwm.c
@@ -0,0 +1,300 @@
+/*
+ * simple driver for PWM (Pulse Width Modulator) controller
+ *
+ * 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.
+ *
+ * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <mach/hardware.h>
+
+
+/* i.MX1 and i.MX21 share the same PWM function block: */
+
+#define MX1_PWMC 0x00 /* PWM Control Register */
+#define MX1_PWMS 0x04 /* PWM Sample Register */
+#define MX1_PWMP 0x08 /* PWM Period Register */
+
+
+/* i.MX27, i.MX31, i.MX35, i.MX51 share the same PWM function block: */
+
+#define MX3_PWMCR 0x00 /* PWM Control Register */
+#define MX3_PWMSAR 0x0C /* PWM Sample Register */
+#define MX3_PWMPR 0x10 /* PWM Period Register */
+#define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4)
+
+#define MX3_PWMCR_STOPEN (1 << 25)
+#define MX3_PWMCR_DOZEEN (1 << 24)
+#define MX3_PWMCR_WAITEN (1 << 23)
+#define MX3_PWMCR_DBGEN (1 << 22)
+#define MX3_PWMCR_CLKSRC_IPG (1 << 16)
+#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
+#define MX3_PWMCR_CLKSRC_IPG_32k (3 << 16)
+#define MX3_PWMCR_EN (1 << 0)
+
+struct pwm_device {
+ struct list_head node;
+ struct platform_device *pdev;
+
+ const char *label;
+ struct clk *clk;
+
+ int clk_enabled;
+ void __iomem *mmio_base;
+
+ unsigned int use_count;
+ unsigned int pwm_id;
+};
+
+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() || cpu_is_mx51()) {
+ unsigned long long c;
+ unsigned long period_cycles, duty_cycles, prescale;
+ c = clk_get_rate(pwm->clk);
+ c = c * period_ns;
+ do_div(c, 1000000000);
+ period_cycles = c;
+
+ prescale = period_cycles / 0x10000 + 1;
+
+ period_cycles /= prescale;
+ c = (unsigned long long)period_cycles * duty_ns;
+ do_div(c, period_ns);
+ duty_cycles = c;
+
+ writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR);
+ writel(period_cycles, pwm->mmio_base + MX3_PWMPR);
+ writel(MX3_PWMCR_PRESCALER(prescale) |
+ MX3_PWMCR_CLKSRC_IPG_HIGH |
+ MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEEN |
+ MX3_PWMCR_WAITEN | MX3_PWMCR_DBGEN,
+ pwm->mmio_base + MX3_PWMCR);
+ } else if (cpu_is_mx21()) {
+ /* The PWM subsystem allows for exact frequencies. However,
+ * I cannot connect a scope on my device to the PWM line and
+ * thus cannot provide the program the PWM controller
+ * exactly. Instead, I'm relying on the fact that the
+ * Bootloader (u-boot or WinCE+haret) has programmed the PWM
+ * function group already. So I'll just modify the PWM sample
+ * register to follow the ratio of duty_ns vs. period_ns
+ * accordingly.
+ *
+ * This is good enought for programming the brightness of
+ * the LCD backlight.
+ *
+ * The real implementation would divide PERCLK[0] first by
+ * both the prescaler (/1 .. /128) and then by CLKSEL
+ * (/2 .. /16).
+ */
+ u32 max = readl(pwm->mmio_base + MX1_PWMP);
+ u32 p = max * duty_ns / period_ns;
+ writel(max - p, pwm->mmio_base + MX1_PWMS);
+ } else {
+ BUG();
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_enable(struct pwm_device *pwm)
+{
+ unsigned long reg;
+ int rc = 0;
+
+ if (!pwm->clk_enabled) {
+ rc = clk_enable(pwm->clk);
+ if (!rc)
+ pwm->clk_enabled = 1;
+ }
+
+ reg = readl(pwm->mmio_base + MX3_PWMCR);
+ reg |= MX3_PWMCR_EN;
+ writel(reg, pwm->mmio_base + MX3_PWMCR);
+ return rc;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+ unsigned long reg;
+
+ if (pwm->clk_enabled) {
+ clk_disable(pwm->clk);
+ pwm->clk_enabled = 0;
+ }
+
+ reg = readl(pwm->mmio_base + MX3_PWMCR);
+ reg &= ~MX3_PWMCR_EN;
+ writel(reg, pwm->mmio_base + MX3_PWMCR);
+
+}
+EXPORT_SYMBOL(pwm_disable);
+
+static DEFINE_MUTEX(pwm_lock);
+static LIST_HEAD(pwm_list);
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+ struct pwm_device *pwm;
+ int found = 0;
+
+ mutex_lock(&pwm_lock);
+
+ list_for_each_entry(pwm, &pwm_list, node) {
+ if (pwm->pwm_id == pwm_id) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ if (pwm->use_count == 0) {
+ pwm->use_count++;
+ pwm->label = label;
+ } else
+ pwm = ERR_PTR(-EBUSY);
+ } else
+ pwm = ERR_PTR(-ENOENT);
+
+ mutex_unlock(&pwm_lock);
+ return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_device *pwm)
+{
+ mutex_lock(&pwm_lock);
+
+ if (pwm->use_count) {
+ pwm->use_count--;
+ pwm->label = NULL;
+ } else
+ pr_warning("PWM device already freed\n");
+
+ mutex_unlock(&pwm_lock);
+}
+EXPORT_SYMBOL(pwm_free);
+
+static int __devinit mxc_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_device *pwm;
+ struct resource *r;
+ int ret = 0;
+
+ pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+ if (pwm == NULL) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ pwm->clk = clk_get(&pdev->dev, "pwm");
+
+ if (IS_ERR(pwm->clk)) {
+ ret = PTR_ERR(pwm->clk);
+ goto err_free;
+ }
+
+ pwm->clk_enabled = 0;
+
+ pwm->use_count = 0;
+ pwm->pwm_id = pdev->id;
+ pwm->pdev = pdev;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no memory resource defined\n");
+ ret = -ENODEV;
+ goto err_free_clk;
+ }
+
+ r = request_mem_region(r->start, r->end - r->start + 1, pdev->name);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "failed to request memory resource\n");
+ ret = -EBUSY;
+ goto err_free_clk;
+ }
+
+ pwm->mmio_base = ioremap(r->start, r->end - r->start + 1);
+ if (pwm->mmio_base == NULL) {
+ dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ ret = -ENODEV;
+ goto err_free_mem;
+ }
+
+ mutex_lock(&pwm_lock);
+ list_add_tail(&pwm->node, &pwm_list);
+ mutex_unlock(&pwm_lock);
+
+ platform_set_drvdata(pdev, pwm);
+ return 0;
+
+err_free_mem:
+ release_mem_region(r->start, r->end - r->start + 1);
+err_free_clk:
+ clk_put(pwm->clk);
+err_free:
+ kfree(pwm);
+ return ret;
+}
+
+static int __devexit mxc_pwm_remove(struct platform_device *pdev)
+{
+ struct pwm_device *pwm;
+ struct resource *r;
+
+ pwm = platform_get_drvdata(pdev);
+ if (pwm == NULL)
+ return -ENODEV;
+
+ mutex_lock(&pwm_lock);
+ list_del(&pwm->node);
+ mutex_unlock(&pwm_lock);
+
+ iounmap(pwm->mmio_base);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(r->start, r->end - r->start + 1);
+
+ clk_put(pwm->clk);
+
+ kfree(pwm);
+ return 0;
+}
+
+static struct platform_driver mxc_pwm_driver = {
+ .driver = {
+ .name = "mxc_pwm",
+ },
+ .probe = mxc_pwm_probe,
+ .remove = __devexit_p(mxc_pwm_remove),
+};
+
+static int __init mxc_pwm_init(void)
+{
+ return platform_driver_register(&mxc_pwm_driver);
+}
+arch_initcall(mxc_pwm_init);
+
+static void __exit mxc_pwm_exit(void)
+{
+ platform_driver_unregister(&mxc_pwm_driver);
+}
+module_exit(mxc_pwm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/arch/arm/plat-mxc/sdma/Makefile b/arch/arm/plat-mxc/sdma/Makefile
new file mode 100644
index 000000000000..59f94b2da7d0
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/Makefile
@@ -0,0 +1,18 @@
+ifneq ($(KBUILD_SRC),)
+ccflags-y += -I$(KBUILD_SRC)/arch/arm/plat-mxc/sdma/iapi/include \
+ -I$(KBUILD_SRC)/include/linux \
+ -DMCU -DOS=LINUX \
+ -DL_I_T_T_L_E_ENDIAN=0 -DB_I_G_ENDIAN=1 \
+ -DENDIANNESS=L_I_T_T_L_E_ENDIAN
+else
+ccflags-y += -Iarch/arm/plat-mxc/sdma/iapi/include \
+ -Iinclude/linux \
+ -DMCU -DOS=LINUX \
+ -DL_I_T_T_L_E_ENDIAN=0 -DB_I_G_ENDIAN=1 \
+ -DENDIANNESS=L_I_T_T_L_E_ENDIAN
+endif
+
+obj-y += dma_sdma.o
+obj-$(CONFIG_MXC_SDMA_API) += sdma.o
+obj-$(CONFIG_MXC_SDMA_API) += iapi/
+obj-$(CONFIG_MXC_SDMA_API) += sdma_malloc.o
diff --git a/arch/arm/plat-mxc/sdma/dma_sdma.c b/arch/arm/plat-mxc/sdma/dma_sdma.c
new file mode 100644
index 000000000000..5ca75d08bd08
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/dma_sdma.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file plat-mxc/sdma/dma_sdma.c
+ * @brief Front-end to the DMA handling. This handles the allocation/freeing
+ * of DMA channels, and provides a unified interface to the machines
+ * DMA facilities. This file contains functions for Smart DMA.
+ *
+ * @ingroup SDMA
+ */
+
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <mach/dma.h>
+#include <mach/hardware.h>
+
+#ifdef CONFIG_MXC_SDMA_API
+
+static mxc_dma_channel_t mxc_sdma_channels[MAX_DMA_CHANNELS];
+static mxc_dma_channel_private_t mxc_sdma_private[MAX_DMA_CHANNELS];
+
+extern struct clk *mxc_sdma_ahb_clk, *mxc_sdma_ipg_clk;
+
+/*!
+ * Tasket to handle processing the channel buffers
+ *
+ * @param arg channel id
+ */
+static void mxc_sdma_channeltasklet(unsigned long arg)
+{
+ dma_request_t request_t;
+ dma_channel_params chnl_param;
+ mxc_dma_channel_t *chnl_info;
+ mxc_dma_channel_private_t *data_priv;
+ int bd_intr = 0, error = MXC_DMA_DONE;
+
+ chnl_info = &mxc_sdma_channels[arg];
+ data_priv = chnl_info->private;
+ chnl_param =
+ mxc_sdma_get_channel_params(chnl_info->channel)->chnl_params;
+
+ mxc_dma_get_config(arg, &request_t, data_priv->buf_tail);
+
+ while (request_t.bd_done == 0) {
+ bd_intr = mxc_dma_get_bd_intr(arg, data_priv->buf_tail);
+ if ((data_priv->buf_tail += 1) >= chnl_param.bd_number) {
+ data_priv->buf_tail = 0;
+ }
+ chnl_info->active = 0;
+ if (request_t.bd_error) {
+ error = MXC_DMA_TRANSFER_ERROR;
+ }
+
+ if (bd_intr != 0) {
+ chnl_info->cb_fn(chnl_info->cb_args, error,
+ request_t.count);
+ error = MXC_DMA_DONE;
+ }
+
+ if (data_priv->buf_tail == chnl_info->curr_buf) {
+ break;
+ }
+ memset(&request_t, 0, sizeof(dma_request_t));
+ mxc_dma_get_config(arg, &request_t, data_priv->buf_tail);
+ }
+}
+
+/*!
+ * This function is generally called by the driver at open time.
+ * The DMA driver would do any initialization steps that is required
+ * to get the channel ready for data transfer.
+ *
+ * @param channel_id a pre-defined id. The peripheral driver would specify
+ * the id associated with its peripheral. This would be
+ * used by the DMA driver to identify the peripheral
+ * requesting DMA and do the necessary setup on the
+ * channel associated with the particular peripheral.
+ * The DMA driver could use static or dynamic DMA channel
+ * allocation.
+ * @param dev_name module name or device name
+ * @return returns a negative number on error if request for a DMA channel did not
+ * succeed, returns the channel number to be used on success.
+ */
+int mxc_dma_request_ext(mxc_dma_device_t channel_id, char *dev_name,
+ struct dma_channel_info *info)
+{
+ mxc_sdma_channel_params_t *chnl;
+ mxc_dma_channel_private_t *data_priv;
+ int ret = 0, i = 0, channel_num = 0;
+ mxc_sdma_channel_ext_params_t *p;
+
+ chnl = mxc_sdma_get_channel_params(channel_id);
+ if (chnl == NULL) {
+ return -EINVAL;
+ }
+
+ if (info) {
+ if (!chnl->chnl_params.ext)
+ return -EINVAL;
+ p = (mxc_sdma_channel_ext_params_t *)chnl;
+ memcpy(&p->chnl_ext_params.info, info, sizeof(info));
+ }
+
+
+ /* Enable the SDMA clock */
+ clk_enable(mxc_sdma_ahb_clk);
+ clk_enable(mxc_sdma_ipg_clk);
+
+ channel_num = chnl->channel_num;
+ if (chnl->channel_num == MXC_DMA_DYNAMIC_CHANNEL) {
+ /* Get the first free channel */
+ for (i = (MAX_DMA_CHANNELS - 1); i > 0; i--) {
+ /* See if channel is available */
+ if ((mxc_sdma_channels[i].dynamic != 1)
+ || (mxc_sdma_channels[i].lock != 0)) {
+ continue;
+ }
+ channel_num = i;
+ /* Check to see if we can get this channel */
+ ret = mxc_request_dma(&channel_num, dev_name);
+ if (ret == 0) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ if (ret != 0) {
+ /* No free channel */
+ goto err_ret;
+ }
+ } else {
+ if (mxc_sdma_channels[chnl->channel_num].lock == 1) {
+ ret = -ENODEV;
+ goto err_ret;
+ }
+ ret = mxc_request_dma(&channel_num, dev_name);
+ if (ret != 0) {
+ goto err_ret;
+ }
+ }
+
+ ret = mxc_dma_setup_channel(channel_num, &chnl->chnl_params);
+
+ if (ret == 0) {
+ if (chnl->chnl_priority != MXC_SDMA_DEFAULT_PRIORITY) {
+ ret =
+ mxc_dma_set_channel_priority(channel_num,
+ chnl->chnl_priority);
+ if (ret != 0) {
+ pr_info("Failed to set channel prority,\
+ continue with the existing \
+ priority\n");
+ goto err_ret;
+ }
+ }
+ mxc_sdma_channels[channel_num].lock = 1;
+ if ((chnl->chnl_params.transfer_type == per_2_emi)
+ || (chnl->chnl_params.transfer_type == dsp_2_emi)) {
+ mxc_sdma_channels[channel_num].mode = MXC_DMA_MODE_READ;
+ } else {
+ mxc_sdma_channels[channel_num].mode =
+ MXC_DMA_MODE_WRITE;
+ }
+ mxc_sdma_channels[channel_num].channel = channel_id;
+ data_priv = mxc_sdma_channels[channel_num].private;
+ tasklet_init(&data_priv->chnl_tasklet,
+ mxc_sdma_channeltasklet, channel_num);
+ if ((channel_id == MXC_DMA_ATA_RX)
+ || (channel_id == MXC_DMA_ATA_TX)) {
+ data_priv->intr_after_every_bd = 0;
+ } else {
+ data_priv->intr_after_every_bd = 1;
+ }
+ }
+ err_ret:
+ if (ret != 0) {
+ clk_disable(mxc_sdma_ahb_clk);
+ clk_disable(mxc_sdma_ipg_clk);
+ channel_num = -ENODEV;
+ }
+
+ return channel_num;
+}
+
+/*!
+ * This function is generally called by the driver at close time. The DMA
+ * driver would do any cleanup associated with this channel.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @return returns a negative number on error or 0 on success
+ */
+int mxc_dma_free(int channel_num)
+{
+ mxc_dma_channel_private_t *data_priv;
+
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ if (mxc_sdma_channels[channel_num].lock != 1) {
+ return -ENODEV;
+ }
+
+ mxc_free_dma(channel_num);
+
+ /* Disable the SDMA clock */
+ clk_disable(mxc_sdma_ahb_clk);
+ clk_disable(mxc_sdma_ipg_clk);
+
+ mxc_sdma_channels[channel_num].lock = 0;
+ mxc_sdma_channels[channel_num].active = 0;
+ mxc_sdma_channels[channel_num].curr_buf = 0;
+ data_priv = mxc_sdma_channels[channel_num].private;
+ data_priv->buf_tail = 0;
+ tasklet_kill(&data_priv->chnl_tasklet);
+
+ return 0;
+}
+
+/*!
+ * Callback function called from the SDMA Interrupt routine
+ *
+ * @param arg driver specific argument that was registered
+ */
+static void mxc_dma_chnl_callback(void *arg)
+{
+ int priv;
+ mxc_dma_channel_private_t *data_priv;
+
+ priv = (int)arg;
+ data_priv = mxc_sdma_channels[priv].private;
+ /* Process the buffers in a tasklet */
+ tasklet_schedule(&data_priv->chnl_tasklet);
+}
+
+/*!
+ * This function would just configure the buffers specified by the user into
+ * dma channel. The caller must call mxc_dma_enable to start this transfer.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @param dma_buf an array of physical addresses to the user defined
+ * buffers. The caller must guarantee the dma_buf is
+ * available until the transfer is completed.
+ * @param num_buf number of buffers in the array
+ * @param mode specifies whether this is READ or WRITE operation
+ * @return This function returns a negative number on error if buffer could not be
+ * added with DMA for transfer. On Success, it returns 0
+ */
+int mxc_dma_config(int channel_num, mxc_dma_requestbuf_t * dma_buf,
+ int num_buf, mxc_dma_mode_t mode)
+{
+ int ret = 0, i = 0, prev_buf;
+ mxc_dma_channel_t *chnl_info;
+ mxc_dma_channel_private_t *data_priv;
+ mxc_sdma_channel_params_t *chnl;
+ dma_channel_params chnl_param;
+ dma_request_t request_t;
+
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ if (num_buf <= 0) {
+ return -EINVAL;
+ }
+
+ chnl_info = &mxc_sdma_channels[channel_num];
+ data_priv = chnl_info->private;
+ if (chnl_info->lock != 1) {
+ return -ENODEV;
+ }
+
+ /* Check to see if all buffers are taken */
+ if (chnl_info->active == 1) {
+ return -EBUSY;
+ }
+
+ chnl = mxc_sdma_get_channel_params(chnl_info->channel);
+ chnl_param = chnl->chnl_params;
+
+ /* Re-setup the SDMA channel if the transfer direction is changed */
+ if ((chnl_param.peripheral_type != MEMORY) && (mode != chnl_info->mode)) {
+ if (chnl_param.peripheral_type == DSP) {
+ if (mode == MXC_DMA_MODE_READ) {
+ chnl_param.transfer_type = dsp_2_emi;
+ } else {
+ chnl_param.transfer_type = emi_2_dsp;
+ }
+ } else if (chnl_param.peripheral_type == FIFO_MEMORY) {
+ if (mode == MXC_DMA_MODE_READ)
+ chnl_param.per_address = MXC_FIFO_MEM_SRC_FIXED;
+ else
+ chnl_param.per_address =
+ MXC_FIFO_MEM_DEST_FIXED;
+ } else {
+ if (mode == MXC_DMA_MODE_READ) {
+ chnl_param.transfer_type = per_2_emi;
+ } else {
+ chnl_param.transfer_type = emi_2_per;
+ }
+ }
+ chnl_param.callback = mxc_dma_chnl_callback;
+ chnl_param.arg = (void *)channel_num;
+ ret = mxc_dma_setup_channel(channel_num, &chnl_param);
+ if (ret != 0) {
+ return ret;
+ }
+ if (chnl->chnl_priority != MXC_SDMA_DEFAULT_PRIORITY) {
+ ret =
+ mxc_dma_set_channel_priority(channel_num,
+ chnl->chnl_priority);
+ if (ret != 0) {
+ pr_info("Failed to set channel prority,\
+ continue with the existing \
+ priority\n");
+ }
+ }
+ chnl_info->mode = mode;
+ }
+
+ for (i = 0; i < num_buf; i++, dma_buf++) {
+ /* Check to see if all buffers are taken */
+ if (chnl_info->active == 1) {
+ break;
+ }
+ request_t.destAddr = (__u8 *) dma_buf->dst_addr;
+ request_t.sourceAddr = (__u8 *) dma_buf->src_addr;
+ if (chnl_param.peripheral_type == ASRC)
+ request_t.count = dma_buf->num_of_bytes / 4;
+ else
+ request_t.count = dma_buf->num_of_bytes;
+ request_t.bd_cont = 1;
+ ret = mxc_dma_set_config(channel_num, &request_t,
+ chnl_info->curr_buf);
+ if (ret != 0) {
+ break;
+ }
+ if (data_priv->intr_after_every_bd == 0) {
+ if (i == num_buf - 1) {
+ mxc_dma_set_bd_intr(channel_num,
+ chnl_info->curr_buf, 1);
+ } else {
+ mxc_dma_set_bd_intr(channel_num,
+ chnl_info->curr_buf, 0);
+ }
+ }
+
+ prev_buf = chnl_info->curr_buf;
+ if ((chnl_info->curr_buf += 1) >= chnl_param.bd_number) {
+ chnl_info->curr_buf = 0;
+ }
+ if (chnl_info->curr_buf == data_priv->buf_tail) {
+ if ((data_priv->intr_after_every_bd == 0)
+ && (i != num_buf - 1)) {
+ /*
+ * Set the BD_INTR flag on the last BD that
+ * was queued
+ */
+ mxc_dma_set_bd_intr(channel_num, prev_buf, 1);
+ }
+ chnl_info->active = 1;
+ }
+ }
+
+ if (i == 0) {
+ return -EBUSY;
+ }
+ return 0;
+}
+
+/*!
+ * This function would just configure the scatterlist specified by the
+ * user into dma channel. This is a slight variation of mxc_dma_config(),
+ * it is provided for the convenience of drivers that have a scatterlist
+ * passed into them. It is the calling driver's responsibility to have the
+ * correct physical address filled in the "dma_address" field of the
+ * scatterlist.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @param sg a scatterlist of buffers. The caller must guarantee
+ * the dma_buf is available until the transfer is
+ * completed.
+ * @param num_buf number of buffers in the array
+ * @param num_of_bytes total number of bytes to transfer. If set to 0, this
+ * would imply to use the length field of the scatterlist
+ * for each DMA transfer. Else it would calculate the size
+ * for each DMA transfer.
+ * @param mode specifies whether this is READ or WRITE operation
+ * @return This function returns a negative number on error if buffer could not
+ * be added with DMA for transfer. On Success, it returns 0
+ */
+int mxc_dma_sg_config(int channel_num, struct scatterlist *sg,
+ int num_buf, int num_of_bytes, mxc_dma_mode_t mode)
+{
+ int ret = 0, i = 0;
+ mxc_dma_requestbuf_t *dma_buf;
+
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ if (mxc_sdma_channels[channel_num].lock != 1) {
+ return -ENODEV;
+ }
+
+ dma_buf =
+ (mxc_dma_requestbuf_t *) kmalloc(num_buf *
+ sizeof(mxc_dma_requestbuf_t),
+ GFP_KERNEL);
+
+ if (dma_buf == NULL) {
+ return -EFAULT;
+ }
+
+ for (i = 0; i < num_buf; i++) {
+ if (mode == MXC_DMA_MODE_READ) {
+ (dma_buf + i)->dst_addr = sg->dma_address;
+ } else {
+ (dma_buf + i)->src_addr = sg->dma_address;
+ }
+
+ if ((num_of_bytes > sg->length) || (num_of_bytes == 0)) {
+ (dma_buf + i)->num_of_bytes = sg->length;
+ } else {
+ (dma_buf + i)->num_of_bytes = num_of_bytes;
+ }
+ sg++;
+ num_of_bytes -= (dma_buf + i)->num_of_bytes;
+ }
+
+ ret = mxc_dma_config(channel_num, dma_buf, num_buf, mode);
+ kfree(dma_buf);
+ return ret;
+}
+
+/*!
+ * This function is provided if the driver would like to set/change its
+ * callback function.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @param callback a callback function to provide notification on transfer
+ * completion, user could specify NULL if he does not wish
+ * to be notified
+ * @param arg an argument that gets passed in to the callback
+ * function, used by the user to do any driver specific
+ * operations.
+ * @return this function returns a negative number on error if the callback
+ * could not be set for the channel or 0 on success
+ */
+int mxc_dma_callback_set(int channel_num,
+ mxc_dma_callback_t callback, void *arg)
+{
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ if (mxc_sdma_channels[channel_num].lock != 1) {
+ return -ENODEV;
+ }
+
+ mxc_sdma_channels[channel_num].cb_fn = callback;
+ mxc_sdma_channels[channel_num].cb_args = arg;
+
+ mxc_dma_set_callback(channel_num, mxc_dma_chnl_callback,
+ (void *)channel_num);
+
+ return 0;
+}
+
+/*!
+ * This stops the DMA channel and any ongoing transfers. Subsequent use of
+ * mxc_dma_enable() will restart the channel and restart the transfer.
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @return returns a negative number on error or 0 on success
+ */
+int mxc_dma_disable(int channel_num)
+{
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ if (mxc_sdma_channels[channel_num].lock != 1) {
+ return -ENODEV;
+ }
+
+ mxc_dma_stop(channel_num);
+ return 0;
+}
+
+/*!
+ * This starts DMA transfer. Or it restarts DMA on a stopped channel
+ * previously stopped with mxc_dma_disable().
+ *
+ * @param channel_num the channel number returned at request time. This
+ * would be used by the DMA driver to identify the calling
+ * driver and do the necessary cleanup on the channel
+ * associated with the particular peripheral
+ * @return returns a negative number on error or 0 on success
+ */
+int mxc_dma_enable(int channel_num)
+{
+ if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
+ return -EINVAL;
+ }
+
+ if (mxc_sdma_channels[channel_num].lock != 1) {
+ return -ENODEV;
+ }
+
+ mxc_dma_start(channel_num);
+ return 0;
+}
+
+/*!
+ * Initializes dma structure with dma_operations
+ *
+ * @param dma dma structure
+ * @return returns 0 on success
+ */
+static int __init mxc_dma_init(void)
+{
+ int i;
+ for (i = 0; i < MAX_DMA_CHANNELS; i++) {
+ mxc_sdma_channels[i].active = 0;
+ mxc_sdma_channels[i].lock = 0;
+ mxc_sdma_channels[i].curr_buf = 0;
+ mxc_sdma_channels[i].dynamic = 1;
+ mxc_sdma_private[i].buf_tail = 0;
+ mxc_sdma_channels[i].private = &mxc_sdma_private[i];
+ }
+ /*
+ * Make statically allocated channels unavailable for dynamic channel
+ * requests
+ */
+ mxc_get_static_channels(mxc_sdma_channels);
+
+ return 0;
+}
+
+arch_initcall(mxc_dma_init);
+
+#else
+int mxc_request_dma(int *channel, const char *devicename)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_setup_channel(int channel, dma_channel_params * p)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_set_channel_priority(unsigned int channel, unsigned int priority)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_set_config(int channel, dma_request_t * p, int bd_index)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_get_config(int channel, dma_request_t * p, int bd_index)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_start(int channel)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_stop(int channel)
+{
+ return -ENODEV;
+}
+
+void mxc_free_dma(int channel)
+{
+}
+
+void mxc_dma_set_callback(int channel, dma_callback_t callback, void *arg)
+{
+}
+
+void *sdma_malloc(size_t size)
+{
+ return 0;
+}
+
+void sdma_free(void *buf)
+{
+}
+
+void *sdma_phys_to_virt(unsigned long buf)
+{
+ return 0;
+}
+
+unsigned long sdma_virt_to_phys(void *buf)
+{
+ return 0;
+}
+
+int mxc_dma_request(mxc_dma_device_t channel_id, char *dev_name)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_free(int channel_num)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_config(int channel_num, mxc_dma_requestbuf_t * dma_buf,
+ int num_buf, mxc_dma_mode_t mode)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_sg_config(int channel_num, struct scatterlist *sg,
+ int num_buf, int num_of_bytes, mxc_dma_mode_t mode)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_callback_set(int channel_num, mxc_dma_callback_t callback,
+ void *arg)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_disable(int channel_num)
+{
+ return -ENODEV;
+}
+
+int mxc_dma_enable(int channel_num)
+{
+ return -ENODEV;
+}
+
+EXPORT_SYMBOL(mxc_request_dma);
+EXPORT_SYMBOL(mxc_dma_setup_channel);
+EXPORT_SYMBOL(mxc_dma_set_channel_priority);
+EXPORT_SYMBOL(mxc_dma_set_config);
+EXPORT_SYMBOL(mxc_dma_get_config);
+EXPORT_SYMBOL(mxc_dma_start);
+EXPORT_SYMBOL(mxc_dma_stop);
+EXPORT_SYMBOL(mxc_free_dma);
+EXPORT_SYMBOL(mxc_dma_set_callback);
+EXPORT_SYMBOL(sdma_malloc);
+EXPORT_SYMBOL(sdma_free);
+EXPORT_SYMBOL(sdma_phys_to_virt);
+EXPORT_SYMBOL(sdma_virt_to_phys);
+
+#endif
+
+EXPORT_SYMBOL(mxc_dma_request_ext);
+EXPORT_SYMBOL(mxc_dma_free);
+EXPORT_SYMBOL(mxc_dma_config);
+EXPORT_SYMBOL(mxc_dma_sg_config);
+EXPORT_SYMBOL(mxc_dma_callback_set);
+EXPORT_SYMBOL(mxc_dma_disable);
+EXPORT_SYMBOL(mxc_dma_enable);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Linux SDMA API");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/sdma/iapi/Makefile b/arch/arm/plat-mxc/sdma/iapi/Makefile
new file mode 100644
index 000000000000..b6a5d6aebda0
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for I.API sources.
+#
+
+obj-y := src/ \ No newline at end of file
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/epm.h b/arch/arm/plat-mxc/sdma/iapi/include/epm.h
new file mode 100644
index 000000000000..a12fff923a15
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/epm.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_SDMA_REGS_H__
+#define __ASM_ARCH_MXC_SDMA_REGS_H__
+
+#include <mach/hardware.h>
+
+/* SDMA Reg definition */
+#define SDMA_BASE_IO_ADDR IO_ADDRESS(SDMA_BASE_ADDR)
+
+#define SDMA_H_C0PTR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x000))
+#define SDMA_H_INTR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x004))
+#define SDMA_H_STATSTOP *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x008))
+#define SDMA_H_START *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x00C))
+#define SDMA_H_EVTOVR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x010))
+#define SDMA_H_DSPOVR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x014))
+#define SDMA_H_HOSTOVR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x018))
+#define SDMA_H_EVTPEND *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x01C))
+#define SDMA_H_DSPENBL *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x020))
+#define SDMA_H_RESET *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x024))
+#define SDMA_H_EVTERR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x028))
+#define SDMA_H_INTRMSK *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x02C))
+#define SDMA_H_PSW *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x030))
+#define SDMA_H_EVTERRDBG *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x034))
+#define SDMA_H_CONFIG *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x038))
+#define SDMA_ONCE_ENB *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x040))
+#define SDMA_ONCE_DATA *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x044))
+#define SDMA_ONCE_INSTR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x048))
+#define SDMA_ONCE_STAT *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x04C))
+#define SDMA_ONCE_CMD *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x050))
+#define SDMA_EVT_MIRROR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x054))
+#define SDMA_ILLINSTADDR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x058))
+#define SDMA_CHN0ADDR *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x05C))
+#define SDMA_ONCE_RTB *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x060))
+#define SDMA_XTRIG_CONF1 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x070))
+#define SDMA_XTRIG_CONF2 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x074))
+
+#ifdef MXC_SDMA_V2
+#define SDMA_CHNENBL_0 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x200))
+#define SDMA_CHNENBL_1 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x204))
+#define SDMA_CHNENBL_2 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x208))
+#define SDMA_CHNENBL_3 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x20C))
+#define SDMA_CHNENBL_4 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x210))
+#define SDMA_CHNENBL_5 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x214))
+#define SDMA_CHNENBL_6 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x218))
+#define SDMA_CHNENBL_7 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x21C))
+#define SDMA_CHNENBL_8 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x220))
+#define SDMA_CHNENBL_9 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x224))
+#define SDMA_CHNENBL_10 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x228))
+#define SDMA_CHNENBL_11 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x22C))
+#define SDMA_CHNENBL_12 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x230))
+#define SDMA_CHNENBL_13 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x234))
+#define SDMA_CHNENBL_14 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x238))
+#define SDMA_CHNENBL_15 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x23C))
+#define SDMA_CHNENBL_16 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x240))
+#define SDMA_CHNENBL_17 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x244))
+#define SDMA_CHNENBL_18 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x248))
+#define SDMA_CHNENBL_19 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x24C))
+#define SDMA_CHNENBL_20 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x250))
+#define SDMA_CHNENBL_21 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x254))
+#define SDMA_CHNENBL_22 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x258))
+#define SDMA_CHNENBL_23 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x25C))
+#define SDMA_CHNENBL_24 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x260))
+#define SDMA_CHNENBL_25 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x264))
+#define SDMA_CHNENBL_26 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x268))
+#define SDMA_CHNENBL_27 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x26C))
+#define SDMA_CHNENBL_28 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x270))
+#define SDMA_CHNENBL_29 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x274))
+#define SDMA_CHNENBL_30 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x278))
+#define SDMA_CHNENBL_31 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x27C))
+#define SDMA_CHNENBL_32 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x280))
+#define SDMA_CHNENBL_33 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x284))
+#define SDMA_CHNENBL_34 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x288))
+#define SDMA_CHNENBL_35 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x28C))
+#define SDMA_CHNENBL_36 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x290))
+#define SDMA_CHNENBL_37 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x294))
+#define SDMA_CHNENBL_38 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x298))
+#define SDMA_CHNENBL_39 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x29C))
+#define SDMA_CHNENBL_40 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2A0))
+#define SDMA_CHNENBL_41 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2A4))
+#define SDMA_CHNENBL_42 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2A8))
+#define SDMA_CHNENBL_43 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2AC))
+#define SDMA_CHNENBL_44 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2B0))
+#define SDMA_CHNENBL_45 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2B4))
+#define SDMA_CHNENBL_46 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2B8))
+#define SDMA_CHNENBL_47 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x2BC))
+
+#define SDMA_ONCE_COUNT *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x300))
+#define SDMA_ONCE_ECTL *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x304))
+#define SDMA_ONCE_EAA *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x308))
+#define SDMA_ONCE_EAB *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x30C))
+#define SDMA_ONCE_EAM *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x310))
+#define SDMA_ONCE_ED *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x314))
+#define SDMA_ONCE_EDM *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x318))
+#define SDMA_ONCE_PCMATCH *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x31C))
+
+#else
+
+#define SDMA_CHNENBL_0 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x080))
+#define SDMA_CHNENBL_1 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x084))
+#define SDMA_CHNENBL_2 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x088))
+#define SDMA_CHNENBL_3 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x08C))
+#define SDMA_CHNENBL_4 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x090))
+#define SDMA_CHNENBL_5 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x094))
+#define SDMA_CHNENBL_6 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x098))
+#define SDMA_CHNENBL_7 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x09C))
+#define SDMA_CHNENBL_8 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0A0))
+#define SDMA_CHNENBL_9 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0A4))
+#define SDMA_CHNENBL_10 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0A8))
+#define SDMA_CHNENBL_11 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0AC))
+#define SDMA_CHNENBL_12 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0B0))
+#define SDMA_CHNENBL_13 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0B4))
+#define SDMA_CHNENBL_14 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0B8))
+#define SDMA_CHNENBL_15 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0BC))
+#define SDMA_CHNENBL_16 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0C0))
+#define SDMA_CHNENBL_17 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0C4))
+#define SDMA_CHNENBL_18 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0C8))
+#define SDMA_CHNENBL_19 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0CC))
+#define SDMA_CHNENBL_20 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0D0))
+#define SDMA_CHNENBL_21 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0D4))
+#define SDMA_CHNENBL_22 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0D8))
+#define SDMA_CHNENBL_23 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0DC))
+#define SDMA_CHNENBL_24 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0E0))
+#define SDMA_CHNENBL_25 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0E4))
+#define SDMA_CHNENBL_26 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0E8))
+#define SDMA_CHNENBL_27 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0EC))
+#define SDMA_CHNENBL_28 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0F0))
+#define SDMA_CHNENBL_29 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0F4))
+#define SDMA_CHNENBL_30 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0F8))
+#define SDMA_CHNENBL_31 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x0FC))
+
+#define SDMA_ONCE_COUNT *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x200))
+#define SDMA_ONCE_ECTL *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x204))
+#define SDMA_ONCE_EAA *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x208))
+#define SDMA_ONCE_EAB *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x20C))
+#define SDMA_ONCE_EAM *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x210))
+#define SDMA_ONCE_ED *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x214))
+#define SDMA_ONCE_EDM *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x218))
+#define SDMA_ONCE_PCMATCH *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x21C))
+
+#endif /* MXC_SDMA_V2 */
+
+#define SDMA_CHNPRI_0 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x100))
+#define SDMA_CHNPRI_1 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x104))
+#define SDMA_CHNPRI_2 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x108))
+#define SDMA_CHNPRI_3 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x10C))
+#define SDMA_CHNPRI_4 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x110))
+#define SDMA_CHNPRI_5 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x114))
+#define SDMA_CHNPRI_6 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x118))
+#define SDMA_CHNPRI_7 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x11C))
+#define SDMA_CHNPRI_8 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x120))
+#define SDMA_CHNPRI_9 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x124))
+#define SDMA_CHNPRI_10 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x128))
+#define SDMA_CHNPRI_11 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x12C))
+#define SDMA_CHNPRI_12 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x130))
+#define SDMA_CHNPRI_13 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x134))
+#define SDMA_CHNPRI_14 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x138))
+#define SDMA_CHNPRI_15 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x13C))
+#define SDMA_CHNPRI_16 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x140))
+#define SDMA_CHNPRI_17 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x144))
+#define SDMA_CHNPRI_18 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x148))
+#define SDMA_CHNPRI_19 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x14C))
+#define SDMA_CHNPRI_20 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x150))
+#define SDMA_CHNPRI_21 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x154))
+#define SDMA_CHNPRI_22 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x158))
+#define SDMA_CHNPRI_23 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x15C))
+#define SDMA_CHNPRI_24 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x160))
+#define SDMA_CHNPRI_25 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x164))
+#define SDMA_CHNPRI_26 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x168))
+#define SDMA_CHNPRI_27 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x16C))
+#define SDMA_CHNPRI_28 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x170))
+#define SDMA_CHNPRI_29 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x174))
+#define SDMA_CHNPRI_30 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x178))
+#define SDMA_CHNPRI_31 *((volatile unsigned long *)(SDMA_BASE_IO_ADDR + 0x17C))
+
+#endif /* _mcuEpm_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapi.h b/arch/arm/plat-mxc/sdma/iapi/include/iapi.h
new file mode 100644
index 000000000000..d7300218057b
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/iapi.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapi.h
+ *
+ * $Id iapi.h $
+ *
+ * Description:
+ * Unique include for the whole IAPI library.
+ *
+ *
+ * http//compass.mot.com/go/115342679
+ *
+ * $Log iapi.h $
+ *
+ * ***************************************************************************/
+
+#ifndef _iapi_h
+#define _iapi_h
+
+/* ****************************************************************************
+ * Include File Section
+ * ***************************************************************************/
+
+#include "sdmaStruct.h"
+#include "iapiDefaults.h"
+#include "iapiLow.h"
+#include "iapiMiddle.h"
+#include "iapiHigh.h"
+
+#ifdef MCU
+#include "iapiLowMcu.h"
+#include "iapiMiddleMcu.h"
+#endif /* MCU */
+
+
+
+#endif /* _iapi_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiDefaults.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiDefaults.h
new file mode 100644
index 000000000000..b03a53ae1893
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiDefaults.h
@@ -0,0 +1,128 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiDefaults.h
+ *
+ * $Id iapiDefaults.h $
+ *
+ * Description:
+ * This library is written in C to guarantee functionality and integrity in
+ * the usage of SDMA virtual DMA channels. This API (Application Programming
+ * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE
+ * fashion.
+ *
+ *
+ *
+ *
+ * $Log iapiDefaults.h $
+ *
+ *****************************************************************************/
+
+
+#ifndef _iapi_defaults_h
+#define _iapi_defaults_h
+
+/******************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include "epm.h"
+#include "sdmaStruct.h"
+
+/* ****************************************************************************
+ * Macro-command Section
+ * ***************************************************************************/
+
+/**
+ * Error codes
+ * lower 5 bits free to include channel number when available
+ * and bit number 6 must be set when channel number is available
+ *
+ * Note :
+ * 1) Abbreviations / naming convention :
+ * - BD : Buffer Descriptor
+ * - CC : Channel Context
+ * - CCB : Channel Control Block
+ * - CD : Channel Descriptor
+ * - B : Buffer
+ * - CH : Channel
+ *
+ */
+#define IAPI_SUCCESS 0
+#define IAPI_FAILURE -1
+#define IAPI_ERR_CH_AVAILABLE 0x00020
+#define IAPI_ERR_NO_ERROR 0x00000
+#define IAPI_ERR_NO_CCB_DEFINED 0x01000
+#define IAPI_ERR_BD_UNINITIALIZED 0x02000
+#define IAPI_ERR_BD_ALLOCATED 0x03000
+#define IAPI_ERR_BD_ALLOCATION 0x04000
+#define IAPI_ERR_CCB_ALLOC_FAILED 0x05000
+#define IAPI_ERR_CCB_UNINITIALIZED 0x06000
+#define IAPI_ERR_CC_ALREADY_DEFINED 0x07000
+#define IAPI_ERR_CC_ALLOC_FAILED 0x08000
+#define IAPI_ERR_CD_ALREADY_DEFINED 0x09000
+#define IAPI_ERR_CD_ALLOC_FAILED 0x0A000
+#define IAPI_ERR_CD_CHANGE_CH_NUMBER 0x0B000
+#define IAPI_ERR_CD_CHANGE_CCB_PTR 0x0C000
+#define IAPI_ERR_CD_CHANGE_UNKNOWN 0x0D000
+#define IAPI_ERR_CD_CHANGE 0x0E000
+#define IAPI_ERR_CD_UNINITIALIZED 0x0F000
+#define IAPI_ERR_CLOSE 0x10000
+#define IAPI_ERR_B_ALLOC_FAILED 0x11000
+#define IAPI_ERR_CONFIG_OVERRIDE 0x12000
+#define IAPI_ERR_CH_IN_USE 0x13000
+#define IAPI_ERR_CALLBACKSYNCH_UNKNOWN 0x14000
+#define IAPI_ERR_INVALID_PARAMETER 0x15000
+#define IAPI_ERR_TRUST 0x16000
+#define IAPI_ERR_CHANNEL_UNINITIALIZED 0x17000
+#define IAPI_ERR_RROR_BIT_READ 0x18000
+#define IAPI_ERR_RROR_BIT_WRITE 0x19000
+#define IAPI_ERR_NOT_ALLOWED 0x1A000
+#define IAPI_ERR_NO_OS_FN 0x1B000
+
+
+/*
+ * Global Variable Section
+ */
+
+/*
+ * Table to hold pointers to the callback functions registered by the users of
+ *I.API
+ */
+extern void (* callbackIsrTable[CH_NUM])(channelDescriptor * cd_p, void * arg);
+
+/*
+ * Table to hold user registered data pointers, to be privided in the callback
+ *function
+ */
+extern void * userArgTable[CH_NUM];
+
+/* channelDescriptor data structure filled with default data*/
+extern channelDescriptor iapi_ChannelDefaults;
+
+/* Global variable to hold the last error encountered in I.API operations*/
+extern unsigned int iapi_errno;
+
+/* Used in synchronization, to mark started channels*/
+extern volatile unsigned long iapi_SDMAIntr;
+
+/* Hold a pointer to the start of the CCB array, to be used in the IRQ routine
+ *to find the channel descriptor for the channed sending the interrupt to the
+ *core.
+ */
+extern channelControlBlock * iapi_CCBHead;
+
+/* configs_data structure filled with default data*/
+extern configs_data iapi_ConfigDefaults;
+
+#endif /* iapiDefaults_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiHigh.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiHigh.h
new file mode 100644
index 000000000000..14cfae539eb8
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiHigh.h
@@ -0,0 +1,136 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiHigh.h
+ *
+ * $Id iapiHigh.h $
+ *
+ * Description:
+ * prototypes for high level function of I.API
+ *
+ *
+ * http://venerque.sps.mot.com/pjt/sfs/www/iapi/softsim_api.pdf
+ *
+ * $Log iapiHigh.h
+ *
+ * ***************************************************************************/
+
+#ifndef _iapiHigh_h
+#define _iapiHigh_h
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include "sdmaStruct.h"
+
+/* ****************************************************************************
+ * Macro-command Section
+ *****************************************************************************/
+enum {
+ IAPI_CHANGE_CHANDESC,
+ IAPI_CHANGE_BDNUM,
+ IAPI_CHANGE_BUFFSIZE,
+ IAPI_CHANGE_CHANBLOCK,
+ IAPI_CHANGE_INSTANCE,
+ IAPI_CHANGE_OWNERSHIP,
+ IAPI_CHANGE_SYNCH,
+ IAPI_CHANGE_TRUST,
+ IAPI_CHANGE_CALLBACKFUNC,
+ IAPI_CHANGE_CHANCCB,
+ IAPI_CHANGE_PRIORITY,
+ IAPI_CHANGE_BDWRAP,
+ IAPI_CHANGE_WATERMARK,
+ IAPI_CHANGE_SET_BDCONT,
+ IAPI_CHANGE_UNSET_BDCONT,
+ IAPI_CHANGE_SET_BDEXTD,
+ IAPI_CHANGE_UNSET_BDEXTD,
+ IAPI_CHANGE_EVTMASK1,
+ IAPI_CHANGE_EVTMASK2,
+ IAPI_CHANGE_PERIPHADDR,
+ IAPI_CHANGE_SET_BDINTR,
+ IAPI_CHANGE_UNSET_BDINTR,
+ IAPI_CHANGE_SET_TRANSFER_CD,
+ IAPI_CHANGE_FORCE_CLOSE,
+ IAPI_CHANGE_SET_TRANSFER,
+ IAPI_CHANGE_USER_ARG,
+ IAPI_CHANGE_SET_BUFFERADDR,
+ IAPI_CHANGE_SET_EXTDBUFFERADDR,
+ IAPI_CHANGE_SET_COMMAND,
+ IAPI_CHANGE_SET_COUNT,
+ IAPI_CHANGE_SET_STATUS,
+ IAPI_CHANGE_GET_BUFFERADDR,
+ IAPI_CHANGE_GET_EXTDBUFFERADDR,
+ IAPI_CHANGE_GET_COMMAND,
+ IAPI_CHANGE_GET_COUNT,
+ IAPI_CHANGE_GET_STATUS,
+ IAPI_CHANGE_SET_ENDIANNESS
+};
+
+
+/*
+ * Public Function Prototype Section
+ */
+int iapi_Open ( channelDescriptor * cd_p, unsigned char channelNumber );
+int iapi_Close( channelDescriptor * cd_p );
+int iapi_Read ( channelDescriptor * cd_p, void * buf, unsigned short nbyte );
+int iapi_Write( channelDescriptor * cd_p, void * buf, unsigned short nbyte );
+int iapi_MemCopy(channelDescriptor * cd_p, void* dest, void* src,
+ unsigned long size);
+int iapi_IoCtl( channelDescriptor * cd_p, unsigned long ctlRequest,
+ unsigned long param);
+
+
+int iapi_Read_ipcv2( channelDescriptor * cd_p, void * data_control_struct_ipcv2);
+
+int iapi_Write_ipcv2( channelDescriptor * cd_p, void * data_control_struct_ipcv2);
+
+#ifdef MCU
+int iapi_Init(channelDescriptor * cd_p, configs_data * config_p,
+ unsigned short* ram_image, unsigned short code_size,
+ unsigned long start_addr);
+#endif /* MCU */
+#ifdef DSP
+int iapi_Init(channelDescriptor * cd_p);
+#endif /* DSP */
+
+int iapi_StartChannel(unsigned char channel);
+int iapi_StopChannel(unsigned char channel);
+int iapi_SynchChannel(unsigned char channel);
+
+int iapi_GetChannelNumber(channelDescriptor * cd_p);
+unsigned long iapi_GetError(channelDescriptor * cd_p);
+int iapi_GetCount(channelDescriptor * cd_p);
+int iapi_GetCountAll(channelDescriptor * cd_p);
+
+#ifndef IRQ_KEYWORD
+#define IRQ_KEYWORD
+#endif /* IRQ_KEYWORD */
+
+IRQ_KEYWORD void IRQ_Handler(void);
+
+#ifdef MCU
+int iapi_GetScript(channelDescriptor * cd_p, void * buf, unsigned short size,
+ unsigned long address);
+int iapi_GetContext(channelDescriptor * cd_p, void * buf,
+ unsigned char channel);
+int iapi_SetScript(channelDescriptor * cd_p, void * buf, unsigned short nbyte,
+ unsigned long destAddr);
+int iapi_SetContext(channelDescriptor * cd_p, void * buf,
+ unsigned char channel);
+int iapi_AssignScript(channelDescriptor * cd_p, script_data * data_p);
+
+int iapi_SetChannelEventMapping(unsigned char event, unsigned long channel_map);
+#endif /* MCU */
+
+#endif /* _iapiHigh_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiLow.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiLow.h
new file mode 100644
index 000000000000..43aff7a4e903
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiLow.h
@@ -0,0 +1,78 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiLow.h
+ *
+ * $Id iapiLow.h $
+ *
+ * Description:
+ * prototypes for low level function of I.API
+ *
+ *
+ *
+ *
+ * $Log iapiLow.h
+ *
+ * ***************************************************************************/
+
+#ifndef _iapiLow_h
+#define _iapiLow_h
+
+/* ****************************************************************************
+ * Boolean identifiers
+ *****************************************************************************/
+#define NO_OS 0
+#define LINUX 1
+#define SYMBIAN 2
+#define WINCE 3
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include "sdmaStruct.h"
+#include "iapiDefaults.h"
+#include "iapiOS.h"
+#ifdef MCU
+#include "iapiLowMcu.h"
+#endif /*MCU*/
+#if OS == NO_OS
+#include <stdlib.h>
+#elif OS == LINUX
+#include <linux/types.h>
+#endif
+
+
+/* ****************************************************************************
+ * Macro-command Section
+ *****************************************************************************/
+
+#define GOTO_SLEEP(x) (iapi_GotoSleep)(x)
+#define INIT_SLEEP(x) (iapi_InitSleep)(x)
+
+/* ****************************************************************************
+ * Public Function Prototype Section
+ *****************************************************************************/
+void iapi_lowStartChannel ( unsigned char channel );
+void iapi_lowStopChannel ( unsigned char channel );
+void iapi_AttachCallbackISR (channelDescriptor * cd_p,
+ void (* func_p)(channelDescriptor * cd_p, void * arg));
+void iapi_DetachCallbackISR (channelDescriptor * cd_p);
+void iapi_ChangeCallbackISR (channelDescriptor * cd_p,
+ void (* func_p)(channelDescriptor * cd_p, void * arg));
+void iapi_lowSynchChannel ( unsigned char channel );
+void iapi_SetBufferDescriptor(bufferDescriptor *bd_p, unsigned char command,
+ unsigned char status, unsigned short count,
+ void * buffAddr, void * extBufferAddr);
+
+#endif /* _iapiLow_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiLowDsp.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiLowDsp.h
new file mode 100644
index 000000000000..0adaa2548b9e
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiLowDsp.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/* ****************************************************************************
+ *
+ * File: iapiLowDsp.h
+ *
+ * $Id iapiLowDsp.h $
+ *
+ * Description:
+ * prototypes for low level function of I.API for DSP side only
+ *
+ *
+ *
+ *
+ * $Log iapiLowDsp.h
+ *
+ * ***************************************************************************/
+
+#ifndef _iapiLowDsp_h
+#define _iapiLowDsp_h
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+
+
+/* ****************************************************************************
+ * Public Function Prototype Section
+ *****************************************************************************/
+/* WARNING !!!!!
+ * This file is empty and it is normal, because there is no low level functions
+ * dedicated to the DSP but the file (iapi_LowDsp.h) must still exist because
+ * some project directly links the file. Previously, there were function
+ * iapi_EnableInterrupts,iapi_DisableInterrupts,iapi_WaitCore,iapi_StartChannel
+ * iapi_StopChannel but they are common to both MCU and DSP, so they have been
+ * moved to iapi_Low.h file.
+ */
+
+#endif /* _iapiLowDsp_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiLowMcu.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiLowMcu.h
new file mode 100644
index 000000000000..12bea564c116
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiLowMcu.h
@@ -0,0 +1,60 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiLowMcu.h
+ *
+ * $Id iapiLowMcu.h $
+ *
+ * Description:
+ * prototypes for low level function of I.API of MCU side only
+ *
+ *
+ *
+ *
+ * $Log iapiLowMcu.h $
+ *
+ * ***************************************************************************/
+
+#ifndef _iapiLowMcu_h
+#define _iapiLowMcu_h
+
+/******************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include "sdmaStruct.h"
+#include "iapiDefaults.h"
+
+/* ****************************************************************************
+ * Public Function Prototype Section
+ * ***************************************************************************/
+
+
+void iapi_InitChannelTables ( void );
+int iapi_ChannelConfig(unsigned char channel, unsigned eventOverride,
+ unsigned mcuOverride, unsigned dspOverride);
+int iapi_Channel0Command(channelDescriptor * cd_p, void * buf,
+ unsigned short nbyte, unsigned char command);
+void iapi_lowGetScript(channelDescriptor * cd_p, void * buf, unsigned short size,
+ unsigned long address);
+void iapi_lowGetContext(channelDescriptor * cd_p, void * buf,
+ unsigned char channel);
+void iapi_lowSetScript(channelDescriptor * cd_p, void * buf, unsigned short nbyte,
+ unsigned long destAddr);
+void iapi_lowSetContext(channelDescriptor * cd_p, void * buf,
+ unsigned char channel);
+int iapi_lowAssignScript(channelDescriptor * cd_p, script_data * data_p);
+
+int iapi_lowSetChannelEventMapping(unsigned char event, unsigned long channel_map);
+
+#endif /* _iapiLowMcu_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddle.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddle.h
new file mode 100644
index 000000000000..2470ffcaabf1
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddle.h
@@ -0,0 +1,52 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiMiddle.h
+ *
+ * $Id iapiMiddle.h $
+ *
+ * Description:
+ * prototypes for middle level function of I.API
+ *
+ *
+ *
+ *
+ * $Log iapiMiddle.h
+ *
+ * ***************************************************************************/
+
+#ifndef _iapiMiddle_h
+#define _iapiMiddle_h
+
+/* ****************************************************************************
+ * Include File Section
+ ******************************************************************************/
+#include "sdmaStruct.h"
+#ifdef MCU
+#include "iapiMiddleMcu.h"
+#endif /* MCU */
+
+/* ****************************************************************************
+ * Public Function Prototype Section
+ ******************************************************************************/
+bufferDescriptor * iapi_AllocBD (channelControlBlock * ccb_p);
+int iapi_AllocContext(contextData ** ctxd_p, unsigned char channel);
+int iapi_AllocChannelDesc(channelDescriptor ** cd_p, unsigned char channel);
+int iapi_ChangeChannelDesc (channelDescriptor * cd_p,
+ unsigned char whatToChange, unsigned long newval);
+void iapi_InitializeCallbackISR (void (* func_p)(channelDescriptor * cd_p,
+ void * arg));
+int iapi_InitializeMemory (channelControlBlock * ccb_p);
+
+#endif /* iapiMiddle_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddleMcu.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddleMcu.h
new file mode 100644
index 000000000000..a47c02d4440b
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiMiddleMcu.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiMiddleMcu.h
+ *
+ * $Id iapiMiddleMcu.h $
+ *
+ * Description:
+ * prototypes for middle level function of I.API
+ *
+ *
+ *
+ *
+ * $Log iapiMiddleMcu.h
+ *
+ * ***************************************************************************/
+
+#ifndef _iapiMiddleMcu_h
+#define _iapiMiddleMcu_h
+
+/* ****************************************************************************
+ * Include File Section
+ ******************************************************************************/
+#include "sdmaStruct.h"
+
+/* ****************************************************************************
+ * Public Function Prototype Section
+ ******************************************************************************/
+
+#endif /* iapiMiddleMcu_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/iapiOS.h b/arch/arm/plat-mxc/sdma/iapi/include/iapiOS.h
new file mode 100644
index 000000000000..17186dae0fd5
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/iapiOS.h
@@ -0,0 +1,96 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiOS.h
+ *
+ * $Id iapiOS.h $
+ *
+ * Description:
+ * prototypes for OS level function of I.API
+ *
+ *
+ *
+ *
+ * $Log iapiOS.h
+ *
+ * ***************************************************************************/
+
+#ifndef _iapiOS_h
+#define _iapiOS_h
+
+/* ****************************************************************************
+ * Boolean identifiers
+ *****************************************************************************/
+#define NO_OS 0
+#define LINUX 1
+#define SYMBIAN 2
+#define WINCE 3
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include "sdmaStruct.h"
+#include "iapiDefaults.h"
+#ifdef MCU
+#include "iapiLowMcu.h"
+#endif /*MCU*/
+#if OS == NO_OS
+#include <stdlib.h>
+#elif OS == LINUX
+#include <linux/types.h>
+#endif
+
+/* ****************************************************************************
+ * Macro-command Section
+ *****************************************************************************/
+#define SDMA_ERAM 0
+#define SDMA_IRAM 1
+#ifdef CONFIG_SDMA_IRAM
+#define MALLOC(x, s) (s == SDMA_ERAM)? (* iapi_Malloc)(x):(* iapi_iram_Malloc)(x)
+#else /*CONFIG_SDMA_IRAM */
+#define MALLOC(x, s) (* iapi_Malloc)(x)
+#endif /*CONFIG_SDMA_IRAM */
+#define FREE(x) if (x!=NULL) (* iapi_Free)(x)
+
+#define GOTO_SLEEP(x) (iapi_GotoSleep)(x)
+#define INIT_SLEEP(x) (iapi_InitSleep)(x)
+
+/* ****************************************************************************
+ * Public Function Prototype Section
+ *****************************************************************************/
+
+#ifdef CONFIG_SDMA_IRAM
+extern void*(* iapi_iram_Malloc) (size_t size);
+#endif /*CONFIG_SDMA_IRAM*/
+
+extern void*(* iapi_Malloc) (size_t size);
+extern void (* iapi_Free) (void * ptr);
+
+extern void*(* iapi_Virt2Phys) (void * ptr);
+extern void*(* iapi_Phys2Virt) (void * ptr);
+
+extern void (* iapi_WakeUp)(int);
+extern void (* iapi_GotoSleep)(int);
+extern void (* iapi_InitSleep)(int);
+
+extern void*(* iapi_memcpy)(void *dest, const void *src, size_t count);
+extern void*(* iapi_memset)(void *dest, int c, size_t count);
+
+extern void (* iapi_EnableInterrupts)(void);
+extern void (* iapi_DisableInterrupts)(void);
+
+extern int (* iapi_GetChannel)(int);
+extern int (* iapi_ReleaseChannel)(int);
+
+#endif /* _iapiOS_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/include/sdmaStruct.h b/arch/arm/plat-mxc/sdma/iapi/include/sdmaStruct.h
new file mode 100644
index 000000000000..bc42dabba554
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/include/sdmaStruct.h
@@ -0,0 +1,426 @@
+/******************************************************************************
+ *
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: sdmaStruct.h
+ *
+ * $Id sdmaStruct.h $
+ *
+ * Description: provides necessary definitions and inclusion for ipcmStruct.c
+ *
+ * $Log $
+ *
+ *****************************************************************************/
+#ifndef _sdmaStruct_h
+#define _sdmaStruct_h
+
+/* ****************************************************************************
+ * Include File Section
+ ******************************************************************************/
+
+/* ****************************************************************************
+ * Macro-command Section
+ ******************************************************************************/
+
+/**
+ * Identifier NULL
+ */
+#ifndef NULL
+#define NULL 0
+#endif
+
+/**
+ * Boolean identifiers
+ */
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+/**
+ * Number of channels
+ */
+#define CH_NUM 32
+/**
+ * Number of events
+ */
+#ifdef MXC_SDMA_V2
+#define EVENTS_NUM 48
+#else
+#define EVENTS_NUM 32
+#endif
+/**
+ * Channel configuration
+ */
+#define DONT_OWN_CHANNEL 0
+#define OWN_CHANNEL 1
+
+/**
+ * Ownership (value defined to computed decimal value)
+ */
+#define CH_OWNSHP_OFFSET_EVT 0
+#define CH_OWNSHP_OFFSET_MCU 1
+#define CH_OWNSHP_OFFSET_DSP 2
+/**
+ * Indexof the greg which holds address to start a script from when channel
+ * becomes current.
+ */
+#define SDMA_NUMBER_GREGS 8
+
+/**
+ * Channel contexts management
+ */
+
+#define CHANNEL_CONTEXT_BASE_ADDRESS 0x800
+/**
+ * Buffer descriptor status values.
+ */
+#define BD_DONE 0x01
+#define BD_WRAP 0x02
+#define BD_CONT 0x04
+#define BD_INTR 0x08
+#define BD_RROR 0x10
+#define BD_LAST 0x20
+#define BD_EXTD 0x80
+
+
+/**
+ * Data Node descriptor status values.
+ */
+#define DND_END_OF_FRAME 0x80
+#define DND_END_OF_XFER 0x40
+#define DND_DONE 0x20
+#define DND_UNUSED 0x01
+
+/**
+ * IPCV2 descriptor status values.
+ */
+#define BD_IPCV2_END_OF_FRAME 0x40
+
+
+#define IPCV2_MAX_NODES 50
+/**
+ * Error bit set in the CCB status field by the SDMA,
+ * in setbd routine, in case of a transfer error
+ */
+#define DATA_ERROR 0x10000000
+
+/**
+ * Buffer descriptor commands.
+ */
+#define C0_ADDR 0x01
+#define C0_LOAD 0x02
+#define C0_DUMP 0x03
+#define C0_SETCTX 0x07
+#define C0_GETCTX 0x03
+#define C0_SETDM 0x01
+#define C0_SETPM 0x04
+#define C0_GETDM 0x02
+#define C0_GETPM 0x08
+/**
+ * Transfer types, encoded in the BD command field
+ */
+#define TRANSFER_32BIT 0x00
+#define TRANSFER_8BIT 0x01
+#define TRANSFER_16BIT 0x02
+#define TRANSFER_24BIT 0x03
+/**
+ * Change endianness indicator in the BD command field
+ */
+#define CHANGE_ENDIANNESS 0x80
+/**
+ * Size in bytes
+ */
+#define SDMA_BD_SIZE 8
+#define SDMA_EXTENDED_BD_SIZE 12
+#define BD_NUMBER 4
+/**
+ * Channel interrupt policy
+ */
+#define DEFAULT_POLL 0
+#define CALLBACK_ISR 1
+/**
+ * Channel status
+ */
+#define UNINITIALIZED 0
+#define INITIALIZED 1
+
+/**
+ * IoCtl particular values
+ */
+#define SET_BIT_ALL 0xFFFFFFFF
+#define BD_NUM_OFFSET 16
+#define BD_NUM_MASK 0xFFFF0000
+
+/**
+ * Maximum values for IoCtl calls, used in high or middle level calls
+ */
+#define MAX_BD_NUM 256
+#define MAX_BD_SIZE 65536
+#define MAX_BLOCKING 2
+#define MAX_SYNCH 2
+#define MAX_OWNERSHIP 8
+#define MAX_CH_PRIORITY 8
+#define MAX_TRUST 2
+#define MAX_WML 256
+
+
+/**
+ * Access to channelDescriptor fields
+ */
+enum {
+ IAPI_CHANNELNUMBER,
+ IAPI_BUFFERDESCNUMBER,
+ IAPI_BUFFERSIZE,
+ IAPI_BLOCKING,
+ IAPI_CALLBACKSYNCH,
+ IAPI_OWNERSHIP,
+ IAPI_PRIORITY,
+ IAPI_TRUST,
+ IAPI_UNUSED,
+ IAPI_CALLBACKISR_PTR,
+ IAPI_CCB_PTR,
+ IAPI_BDWRAP,
+ IAPI_WML
+};
+
+/**
+ * Default values for channel descriptor - nobody ownes the channel
+ */
+#define CD_DEFAULT_OWNERSHIP 7
+
+
+/**
+ * User Type Section
+ */
+
+/**
+ * Command/Mode/Count of buffer descriptors
+ */
+
+#if (ENDIANNESS==B_I_G_ENDIAN)
+typedef struct iapi_modeCount_ipcv2 {
+ unsigned long status : 8; /**< L, E , D bits stored here */
+ unsigned long reserved : 8;
+ unsigned long count : 16; /**< <size of the buffer pointed by this BD */
+} modeCount_ipcv2;
+#else
+typedef struct iapi_modeCount_ipcv2 {
+ unsigned long count : 16; /**<size of the buffer pointed by this BD */
+ unsigned long reserved : 8; /**Reserved*/
+ unsigned long status : 8; /**< L, E , D bits stored here */
+} modeCount_ipcv2;
+#endif
+/**
+ * Data Node descriptor - IPCv2
+ * (differentiated between evolutions of SDMA)
+ */
+typedef struct iapi_dataNodeDescriptor {
+ modeCount_ipcv2 mode; /**<command, status and count */
+ void * bufferAddr; /**<address of the buffer described */
+} dataNodeDescriptor;
+
+#if (ENDIANNESS==B_I_G_ENDIAN)
+typedef struct iapi_modeCount_ipcv1_v2 {
+ unsigned long endianness: 1;
+ unsigned long reserved: 7;
+ unsigned long status : 8; /**< E,R,I,C,W,D status bits stored here */
+ unsigned long count : 16; /**< size of the buffer pointed by this BD */
+} modeCount_ipcv1_v2;
+#else
+typedef struct iapi_modeCount_ipcv1_v2 {
+ unsigned long count : 16; /**<size of the buffer pointed by this BD */
+ unsigned long status : 8; /**< E,R,I,C,W,D status bits stored here */
+ unsigned long reserved: 7;
+ unsigned long endianness: 1;
+} modeCount_ipcv1_v2;
+#endif
+/**
+ * Buffer descriptor
+ * (differentiated between evolutions of SDMA)
+ */
+typedef struct iapi_bufferDescriptor_ipcv1_v2 {
+ modeCount_ipcv1_v2 mode; /**<command, status and count */
+ void * bufferAddr; /**<address of the buffer described */
+ void * extBufferAddr; /**<extended buffer address */
+} bufferDescriptor_ipcv1_v2;
+
+
+/**
+ * Mode/Count of data node descriptors - IPCv2
+ */
+
+#if (ENDIANNESS==B_I_G_ENDIAN)
+typedef struct iapi_modeCount {
+ unsigned long command : 8; /**< command mostlky used for channel 0 */
+ unsigned long status : 8; /**< E,R,I,C,W,D status bits stored here */
+ unsigned long count : 16; /**< size of the buffer pointed by this BD */
+} modeCount;
+#else
+typedef struct iapi_modeCount {
+ unsigned long count : 16; /**<size of the buffer pointed by this BD */
+ unsigned long status : 8; /**< E,R,I,C,W,D status bits stored here */
+ unsigned long command : 8; /**< command mostlky used for channel 0 */
+} modeCount;
+#endif
+
+
+/**
+ * Buffer descriptor
+ * (differentiated between evolutions of SDMA)
+ */
+typedef struct iapi_bufferDescriptor {
+ modeCount mode; /**<command, status and count */
+ void * bufferAddr; /**<address of the buffer described */
+ void * extBufferAddr; /**<extended buffer address */
+} bufferDescriptor;
+
+
+
+struct iapi_channelControlBlock;
+struct iapi_channelDescriptor;
+/**
+ * Channel Descriptor
+ */
+typedef struct iapi_channelDescriptor {
+ unsigned char channelNumber ;/**<stores the channel number */
+ unsigned char bufferDescNumber;/**<number of BD's automatically allocated for this channel */
+ unsigned short bufferSize ;/**<size (in bytes) of buffer descriptors */
+ unsigned long blocking :3;/**<blocking / non blocking feature selection */
+ unsigned long callbackSynch :1;/**<polling/ callback method selection */
+ unsigned long ownership :3;/**<ownership of the channel (host+dedicated+event)*/
+ unsigned long priority :3;/**<reflects the SDMA channel priority register */
+ unsigned long trust :1;/**<trusted buffers or kernel allocated */
+ unsigned long useDataSize :1;/**<indicates if the dataSize field is meaningfull */
+ unsigned long dataSize :2;/**<data transfer size - 8,16,24 or 32 bits*/
+ unsigned long forceClose :1;/**<if TRUE, close channel even with BD owned by SDMA*/
+ unsigned long scriptId :7;/**<number of the script */
+ unsigned long watermarkLevel:10;/**<Watermark level for the peripheral access*/
+ unsigned long eventMask1; /**<First Event mask */
+ unsigned long eventMask2; /**<Second Event mask */
+ unsigned long peripheralAddr; /**<Address of the peripheral or its fifo when needed */
+ void (* callbackISR_ptr)(struct iapi_channelDescriptor*, void*); /**<pointer to the callback function (or NULL) */
+ struct iapi_channelControlBlock * ccb_ptr; /**<pointer to the channel control block associated to this channel */
+} channelDescriptor;
+
+/**
+ * Channel Status
+ */
+typedef struct iapi_channelStatus {
+ unsigned long unused :29;/**<*/
+ unsigned long openedInit : 1;/**<channel is initialized or not */
+ unsigned long stateDirection: 1;/**<sdma is reading/writing (as seen from channel owner core) */
+ unsigned long execute : 1;/**<channel is being processed (started) or not */
+} channelStatus;
+
+/**
+ * Channel control Block
+ */
+typedef struct iapi_channelControlBlock {
+ bufferDescriptor * currentBDptr; /**<current buffer descriptor processed */
+ bufferDescriptor * baseBDptr; /**<first element of buffer descriptor array */
+ channelDescriptor * channelDescriptor; /**<pointer to the channel descriptor */
+ channelStatus status; /**<open/close ; started/stopped ; read/write */
+} channelControlBlock;
+
+/**
+ * Context structure.
+ */
+#if (ENDIANNESS==B_I_G_ENDIAN)
+typedef struct iapi_stateRegisters {
+ unsigned long sf : 1;/**<source falut while loading data */
+ unsigned long unused0: 1;/**<*/
+ unsigned long rpc :14;/**<return program counter */
+ unsigned long t : 1;/**<test bit:status of arithmetic & test instruction*/
+ unsigned long unused1: 1;/**<*/
+ unsigned long pc :14;/**<program counter */
+ unsigned long lm : 2;/**<loop mode */
+ unsigned long epc :14;/**<loop end program counter */
+ unsigned long df : 1;/**<destiantion falut while storing data */
+ unsigned long unused2: 1;/**<*/
+ unsigned long spc :14;/**<loop start program counter */
+} stateRegiters;
+#else
+typedef struct iapi_stateRegisters {
+ unsigned long pc :14;/**<program counter */
+ unsigned long unused1: 1;/**<*/
+ unsigned long t : 1;/**<test bit: status of arithmetic & test instruction*/
+ unsigned long rpc :14;/**<return program counter */
+ unsigned long unused0: 1;/**<*/
+ unsigned long sf : 1;/**<source falut while loading data */
+ unsigned long spc :14;/**<loop start program counter */
+ unsigned long unused2: 1;/**<*/
+ unsigned long df : 1;/**<destiantion falut while storing data */
+ unsigned long epc :14;/**<loop end program counter */
+ unsigned long lm : 2;/**<loop mode */
+} stateRegiters;
+#endif
+
+/**
+ * This is SDMA version of SDMA
+ */
+typedef struct iapi_contextData {
+ stateRegiters channelState; /**<channel state bits */
+ unsigned long gReg[ SDMA_NUMBER_GREGS ]; /**<general registers */
+ unsigned long mda; /**<burst dma destination address register */
+ unsigned long msa; /**<burst dma source address register */
+ unsigned long ms; /**<burst dma status register */
+ unsigned long md; /**<burst dma data register */
+ unsigned long pda; /**<peripheral dma destination address register */
+ unsigned long psa; /**<peripheral dma source address register */
+ unsigned long ps; /**<peripheral dma status register */
+ unsigned long pd; /**<peripheral dma data register */
+ unsigned long ca; /**<CRC polynomial register */
+ unsigned long cs; /**<CRC accumulator register */
+ unsigned long dda; /**<dedicated core destination address register */
+ unsigned long dsa; /**<dedicated core source address register */
+ unsigned long ds; /**<dedicated core status register */
+ unsigned long dd; /**<dedicated core data register */
+ unsigned long scratch0; /**<scratch */
+ unsigned long scratch1; /**<scratch */
+ unsigned long scratch2; /**<scratch */
+ unsigned long scratch3; /**<scratch */
+ unsigned long scratch4; /**<scratch */
+ unsigned long scratch5; /**<scratch */
+ unsigned long scratch6; /**<scratch */
+ unsigned long scratch7; /**<scratch */
+
+} contextData;
+
+/**
+ *This structure holds the necessary data for the assignment in the
+ * dynamic channel-script association
+ */
+typedef struct iapi_script_data {
+ unsigned short load_address;/**<start address of the script*/
+ unsigned long wml; /**<parameters for the channel descriptor*/
+ unsigned long shp_addr; /**<shared peripheral base address*/
+ unsigned long per_addr; /**<peripheral base address for p2p source*/
+ unsigned long event_mask1; /**<First Event mask */
+ unsigned long event_mask2; /**<Second Event mask */
+} script_data;
+
+/**
+ *This structure holds the the useful bits of the CONFIG register
+ */
+typedef struct iapi_configs_data {
+ unsigned long dspdma :1; /*indicates if the DSPDMA is used */
+ unsigned long rtdobs :1; /*indicates if Real-Time Debug pins are enabled */
+ unsigned long acr :1; /**indicates if AHB freq /core freq = 2 or 1 */
+ unsigned long csm :2; /**indicates which context switch mode is selected*/
+} configs_data;
+
+#endif /* _sdmaStruct_h */
diff --git a/arch/arm/plat-mxc/sdma/iapi/src/Makefile b/arch/arm/plat-mxc/sdma/iapi/src/Makefile
new file mode 100644
index 000000000000..4c112bf22384
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/src/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for I.API sources.
+#
+ifneq ($(KBUILD_SRC),)
+ccflags-y += -I$(KBUILD_SRC)/arch/arm/plat-mxc/sdma/iapi/include \
+ -I$(KBUILD_SRC)/include/linux \
+ -DMCU -DOS=LINUX \
+ -DL_I_T_T_L_E_ENDIAN=0 -DB_I_G_ENDIAN=1 \
+ -DENDIANNESS=L_I_T_T_L_E_ENDIAN
+else
+ccflags-y += -Iarch/arm/plat-mxc/sdma/iapi/include \
+ -Iinclude/linux \
+ -DMCU -DOS=LINUX \
+ -DL_I_T_T_L_E_ENDIAN=0 -DB_I_G_ENDIAN=1 \
+ -DENDIANNESS=L_I_T_T_L_E_ENDIAN
+endif
+
+obj-y += iapiLow.o iapiLowMcu.o iapiMiddle.o iapiMiddleMcu.o iapiHigh.o iapiDefaults.o iapiOS.o
diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiDefaults.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiDefaults.c
new file mode 100644
index 000000000000..345c240cb15c
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiDefaults.c
@@ -0,0 +1,110 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiDefaults.c
+ *
+ * $Id iapiDefaults.c $
+ *
+ * Description:
+ * This library is written in C to guarantee functionality and integrity in
+ * the usage of SDMA virtual DMA channels. This API (Application Programming
+ * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE
+ * fashion.
+ *
+ * Usage:
+ *
+ * Files:
+ *
+ *
+* /
+ *
+ * $Log iapiDefaults.c $
+ *
+ *****************************************************************************/
+
+/* ****************************************************************************
+ * Include File Section
+ ******************************************************************************/
+#include "iapiDefaults.h"
+
+/* ****************************************************************************
+ * Global Variable Section
+ ******************************************************************************/
+
+/**
+ * @brief System Call-back ISRs Table
+ */
+void (* callbackIsrTable[CH_NUM])(channelDescriptor* cd_p, void* arg);
+
+/**
+ * @brief User registered pointers table
+ */
+void * userArgTable[CH_NUM];
+
+/**
+ * @brief Pointer to the first CCB in the CCB array
+ */
+channelControlBlock * iapi_CCBHead = NULL;
+
+
+/**Default channel description.
+ *
+ * Initialization values are:\n
+ * - channelNumber = 0
+ * - bufferDescNumber = 1
+ * - bufferSize = 8
+ * - blocking = 0
+ * - callbackSynch = DEFAULT_POLL
+ * - ownership = CD_DEFAULT_OWNERSHIP
+ * - priority = 1
+ * - trust = TRUE
+ * - useDataSize = 0
+ * - dataSize = 0
+ * - forceClose = 0
+ * - scriptId = 0
+ * - watermarkLevel = 0
+ * - eventMask1 = 0
+ * - eventMask2 = 0
+ * - peripheralAddr = NULL
+ * - callbackISR_ptr = NULL
+ * - iapi_channelControlBlock = NULL
+ */
+channelDescriptor iapi_ChannelDefaults = {0, 1, 8, 0, DEFAULT_POLL,
+ CD_DEFAULT_OWNERSHIP, 1, TRUE, 0, 0, 0, 0,
+ 0, 0x00, 0x00, 0x00, NULL, NULL};
+
+/**
+ * Integrated error management
+ */
+unsigned int iapi_errno = 0;
+volatile unsigned long iapi_SDMAIntr = 0;
+
+/* Default config register.
+ * Initialization values are:
+ * dspdma used
+ * Real-Time Debug pins disabled
+ * AHB freq / core freq = 2
+ * dynamic context switch
+*/
+configs_data iapi_ConfigDefaults = {1, 0, 0, 3};
+
+#ifdef SDMA_SKYE
+/* Default sdma State : UNDEF
+ *possible value are UNDEF, OPEN, LOCK, CLOSED, CLOSE_LOCK
+ */
+
+sdmaState iapi_SdmaState= UNDEF;
+#endif
+
+/* ***************************************************************************/
diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiHigh.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiHigh.c
new file mode 100644
index 000000000000..abdd2d89ab67
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiHigh.c
@@ -0,0 +1,2798 @@
+/******************************************************************************
+ *
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiHigh.c
+ *
+ * $Id iapiHigh.c $
+ *
+ * Description:
+ * This library is written in C to guarantee functionality and integrity in
+ * the usage of SDMA virtual DMA channels. This API (Application Programming
+ * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE
+ * fashion.
+ * These are the HIGH level functions of the I.API.
+ *
+ *
+ * /
+ *
+ * $Log iapiHigh.c $
+ *
+ *****************************************************************************/
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include <stdarg.h>
+#include <string.h>
+
+#include "epm.h"
+#include "iapi.h"
+
+/* ****************************************************************************
+ * External Reference Section (for compatibility with already developed code)
+ *****************************************************************************/
+static void iapi_read_ipcv2_callback(struct iapi_channelDescriptor* cd_p, void* data);
+
+/* ****************************************************************************
+ * Global Variable Section
+ *****************************************************************************/
+#define MAX_CHANNEL 32
+
+static dataNodeDescriptor* dnd_read_control_struct[MAX_CHANNEL];
+
+/* MASK to get Nullify all the bits of Status in Data Node descriptor apart from L, E and D*/
+
+#define GET_LED_MASK 0xE0
+
+/*Table defines mapping of Data Node Descriptor to Buffer Descriptor status*/
+
+static unsigned char dnd_2_bd_status[]=
+{
+0x85, /*00 L = 0, E = 0, D = 0*/
+0x00, /*01*/
+0x00, /*02*/
+0x00, /*03*/
+0x00, /*04*/
+0x00, /*05*/
+0x00, /*06*/
+0x00, /*07*/
+0x00, /*08*/
+0x00, /*09*/
+0x00, /*0A*/
+0x00, /*0B*/
+0x00, /*0C*/
+0x00, /*0D*/
+0x00, /*0E*/
+0x00, /*0F*/
+0x00, /*10*/
+0x00,/*11*/
+0x00,/*12*/
+0x00,/*13*/
+0x00,/*14*/
+0x00,/*15*/
+0x00,/*16*/
+0x00,/*17*/
+0x00,/*18*/
+0x00,/*19*/
+0x00,/*1A*/
+0x00,/*1B*/
+0x00,/*1C*/
+0x00,/*1D*/
+0x00,/*1E*/
+0x00,/*1F*/
+0x84,/*20 L = 0, E = 0, D = 1 */
+0x00,/*21*/
+0x00,/*22*/
+0x00,/*23*/
+0x00,/*24*/
+0x00,/*25*/
+0x00,/*26*/
+0x00,/*27*/
+0x00,/*28*/
+0x00,/*29*/
+0x00,/*2A*/
+0x00,/*2B*/
+0x00,/*2C*/
+0x00,/*2D*/
+0x00,/*2E*/
+0x00,/*2F*/
+0x00,/*30*/
+0x00,/*31*/
+0x00,/*32*/
+0x00,/*33*/
+0x00,/*34*/
+0x00,/*35*/
+0x00,/*36*/
+0x00,/*37*/
+0x00,/*38*/
+0x00,/*39*/
+0x00,/*3A*/
+0x00,/*3B*/
+0x00,/*3C*/
+0x00,/*3D*/
+0x00,/*3E*/
+0x00,/*3F*/
+0xAB,/*40 L = 0, E = 1, D = 0*/
+0x00,/*41*/
+0x00,/*42*/
+0x00,/*43*/
+0x00,/*44*/
+0x00,/*45*/
+0x00,/*46*/
+0x00,/*47*/
+0x00,/*48*/
+0x00,/*49*/
+0x00,/*4A*/
+0x00,/*4B*/
+0x00,/*4C*/
+0x00,/*4D*/
+0x00,/*4E*/
+0x00,/*4F*/
+0x00,/*50*/
+0x00,/*51*/
+0x00,/*52*/
+0x00,/*53*/
+0x00,/*54*/
+0x00,/*55*/
+0x00,/*56*/
+0x00,/*57*/
+0x00,/*58*/
+0x00,/*59*/
+0x00,/*5A*/
+0x00,/*5B*/
+0x00,/*5C*/
+0x00,/*5D*/
+0x00,/*5E*/
+0x00,/*5F*/
+0xAA,/*60 L = 0, E = 1, D = 1*/
+0x00,/*61*/
+0x00,/*62*/
+0x00,/*63*/
+0x00,/*64*/
+0x00,/*65*/
+0x00,/*66*/
+0x00,/*67*/
+0x00,/*68*/
+0x00,/*69*/
+0x00,/*6A*/
+0x00,/*6B*/
+0x00,/*6C*/
+0x00,/*6D*/
+0x00,/*6E*/
+0x00,/*6F*/
+0x00,/*70*/
+0x00,/*71*/
+0x00,/*72*/
+0x00,/*73*/
+0x00,/*74*/
+0x00,/*75*/
+0x00,/*76*/
+0x00,/*77*/
+0x00,/*78*/
+0x00,/*79*/
+0x00,/*7A*/
+0x00,/*7B*/
+0x00,/*7C*/
+0x00,/*7D*/
+0x00,/*7E*/
+0x00,/*7F*/
+0xC5,/*80 L = 1, E = 0, D = 0*/
+0x00,/*81*/
+0x00,/*82*/
+0x00,/*83*/
+0x00,/*84*/
+0x00,/*85*/
+0x00,/*86*/
+0x00,/*87*/
+0x00,/*88*/
+0x00,/*89*/
+0x00,/*8A*/
+0x00,/*8B*/
+0x00,/*8C*/
+0x00,/*8D*/
+0x00,/*8E*/
+0x00,/*8F*/
+0x00,/*90*/
+0x00,/*91*/
+0x00,/*92*/
+0x00,/*93*/
+0x00,/*94*/
+0x00,/*95*/
+0x00,/*96*/
+0x00,/*97*/
+0x00,/*98*/
+0x00,/*99*/
+0x00,/*9A*/
+0x00,/*9B*/
+0x00,/*9C*/
+0x00,/*9D*/
+0x00,/*9E*/
+0x00,/*9F*/
+0xC4,/*A0 L = 1, E = 0, D = 1*/
+0x00,/*A1*/
+0x00,/*A2*/
+0x00,/*A3*/
+0x00,/*A4*/
+0x00,/*A5*/
+0x00,/*A6*/
+0x00,/*A7*/
+0x00,/*A8*/
+0x00,/*A9*/
+0x00,/*AA*/
+0x00,/*AB*/
+0x00,/*AC*/
+0x00,/*AD*/
+0x00,/*AE*/
+0x00,/*AF*/
+0x00,/*B0*/
+0x00,/*B1*/
+0x00,/*B2*/
+0x00,/*B3*/
+0x00,/*B4*/
+0x00,/*B5*/
+0x00,/*B6*/
+0x00,/*B7*/
+0x00,/*B8*/
+0x00,/*B9*/
+0x00,/*BA*/
+0x00,/*BB*/
+0x00,/*BC*/
+0x00,/*BD*/
+0x00,/*BE*/
+0x00,/*BF*/
+0xEB,/*C0 L = 1, E = 1, D = 0*/
+0x00,/*C1*/
+0x00,/*C2*/
+0x00,/*C3*/
+0x00,/*C4*/
+0x00,/*C5*/
+0x00,/*C6*/
+0x00,/*C7*/
+0x00,/*C8*/
+0x00,/*C9*/
+0x00,/*CA*/
+0x00,/*CB*/
+0x00,/*CC*/
+0x00,/*CD*/
+0x00,/*CE*/
+0x00,/*CF*/
+0x00,/*D0*/
+0x00,/*D1*/
+0x00,/*D2*/
+0x00,/*D3*/
+0x00,/*D4*/
+0x00,/*D5*/
+0x00,/*D6*/
+0x00,/*D7*/
+0x00,/*D8*/
+0x00,/*D9*/
+0x00,/*DA*/
+0x00,/*DB*/
+0x00,/*DC*/
+0x00,/*DD*/
+0x00,/*DE*/
+0x00,/*DF*/
+0xEA,/*E0 L = 1, E = 1, D = 1*/
+0x00,/*E1*/
+0x00,/*E2*/
+0x00,/*E3*/
+0x00,/*E4*/
+0x00,/*E5*/
+0x00,/*E6*/
+0x00,/*E7*/
+0x00,/*E8*/
+0x00,/*E9*/
+0x00,/*EA*/
+0x00,/*EB*/
+0x00,/*EC*/
+0x00,/*ED*/
+0x00,/*EE*/
+0x00,/*EF*/
+0x00,/*F0*/
+0x00,/*F1*/
+0x00,/*F2*/
+0x00,/*F3*/
+0x00,/*F4*/
+0x00,/*F5*/
+0x00,/*F6*/
+0x00,/*F7*/
+0x00,/*F8*/
+0x00,/*F9*/
+0x00,/*FA*/
+0x00,/*FB*/
+0x00,/*FC*/
+0x00,/*FD*/
+0x00,/*FE*/
+0x00/*FF*/
+};
+/* ****************************************************************************
+ * Function Section
+ *****************************************************************************/
+
+/* ***************************************************************************/
+/**Opens an SDMA channel to be used by the library.
+ *
+ * <b>Algorithm:</b>\n
+ *
+ * - Check if initialization is necessary.
+ * - Check that user initialized OS dependant functions.
+ * - Test validity of input parameters
+ * - Check whole channel control block data structure
+ * - Finish initializations (tables with default values)
+ * - Initialize channel 0 is dedicated to communications with SDMA
+ * - Check channel control block definition
+ * - if the channel descriptor is not initialized, initialize it with
+ * the default value
+ * - If buffer descriptor already allocated, exit with iapi_errno filled
+ * complete the lowest bits with the number of 'D' bits set
+ * - Buffer Descriptors allocation
+ * - Channel's configuration properties (mcu side only)
+ * - read/write direction => enable/disable channel setting
+ *
+ * @param *cd_p -If channelNumber is 0, it is pointer to channel descriptor for the channnel 0 to be opened and
+ has default values.
+ * For other channels,this function should be called after channel 0 has been opened, and it is channel descriptor for
+ channel 0.Must be allocated.
+ * @param channelNumber channel to be opened
+ *
+ * @return
+ * - IAPI_SUCCESS : OK
+ * - -iapi_errno : close failed, return negated value of iapi_errno
+ */
+int
+iapi_Open (channelDescriptor * cd_p, unsigned char channelNumber)
+{
+ channelControlBlock * ccb_p;
+ channelControlBlock * local_ccb_p;
+ channelDescriptor * local_cd_p;
+ bufferDescriptor * bd_p;
+ unsigned char index = 0;
+ int result = IAPI_SUCCESS;
+#ifdef MCU
+ volatile unsigned long * channelPriorityMatx;
+#endif /* MCU */
+
+
+ /*
+ * 1. Check if initialization is necessary
+ */
+ if (cd_p == NULL){
+ result = IAPI_ERR_CD_UNINITIALIZED |
+ IAPI_ERR_CH_AVAILABLE | channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Verify these functions every time*/
+ if((iapi_GetChannel == NULL)||(iapi_ReleaseChannel == NULL))
+ {
+ result = IAPI_ERR_NO_OS_FN | channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Try to aquire channel*/
+ if(iapi_GetChannel(channelNumber) != 0)
+ {
+ result = IAPI_ERR_CH_IN_USE | channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+ if (channelNumber == 0 && cd_p->ccb_ptr == NULL){
+ /* Verify that the user initialized all OS dependant functions required
+ * by the library.
+ */
+ if((iapi_Malloc == NULL)||(iapi_Free == NULL)||(iapi_Virt2Phys == NULL)||
+ (iapi_Phys2Virt == NULL)||(iapi_GotoSleep == NULL)||
+ (iapi_WakeUp == NULL)||(iapi_InitSleep == NULL)||(iapi_memset == NULL)||
+ (iapi_memcpy == NULL))
+ {
+ result = IAPI_ERR_NO_OS_FN | channelNumber;
+ iapi_errno = result;
+ iapi_ReleaseChannel(channelNumber);
+ return -result;
+ }
+ /* Whole channel control block data structure */
+ ccb_p = (channelControlBlock *)
+ MALLOC(CH_NUM*sizeof(channelControlBlock), SDMA_IRAM);
+ if (ccb_p == NULL){
+ result = IAPI_ERR_CCB_ALLOC_FAILED |
+ IAPI_ERR_CH_AVAILABLE | channelNumber;
+ iapi_errno = result;
+ iapi_ReleaseChannel(channelNumber);
+ return -result;
+ }
+ /* Zero-out the CCB structures array just allocated*/
+ iapi_memset(ccb_p, 0x00, CH_NUM*sizeof(channelControlBlock));
+ /* Save the address of the CCB structures array*/
+ iapi_CCBHead = ccb_p;
+
+ cd_p->ccb_ptr = (struct iapi_channelControlBlock *)ccb_p;
+ ccb_p->channelDescriptor = cd_p;
+#ifdef MCU
+ /* finish initializations */
+ iapi_InitChannelTables();
+#endif /* MCU */
+ /* Channel 0 is dedicated to communications with SDMA */
+ cd_p->ownership = ((DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT ) |
+ ( OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU ) |
+ (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP ));
+ cd_p->bufferDescNumber = 1;
+ }
+
+ /*
+ * 2. Check channel control block
+ */
+ ccb_p = cd_p->ccb_ptr;
+ if (ccb_p == NULL){
+ result = IAPI_ERR_NO_CCB_DEFINED | IAPI_ERR_CH_AVAILABLE|channelNumber;
+ iapi_errno = result;
+ iapi_ReleaseChannel(channelNumber);
+ return -result;
+ }
+
+ /* Control block & Descriptor associated with the channel being worked on */
+ local_ccb_p = &ccb_p[channelNumber];
+ local_cd_p = ccb_p[channelNumber].channelDescriptor;
+
+ /* If the channel is not initialized, initialize it with the default value */
+ if (local_cd_p == NULL){
+ result = iapi_AllocChannelDesc (&local_cd_p, channelNumber);
+ if ( result!= IAPI_SUCCESS)
+ {
+ iapi_ReleaseChannel(channelNumber);
+ return result; //is allready negated from iapi_AllocChannelDesc
+ }
+
+ local_cd_p->ccb_ptr = (struct iapi_channelControlBlock *)local_ccb_p;
+ local_ccb_p->channelDescriptor = local_cd_p;
+ }
+
+ /*
+ * 3. If buffer descriptor already allocated, exit with iapi_errno filled
+ */
+ if ( local_ccb_p->baseBDptr != NULL ){
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(local_ccb_p->baseBDptr);
+ result = IAPI_ERR_BD_ALLOCATED;
+ for (index=1 ; index< local_cd_p->bufferDescNumber ; index++){
+ if ((bd_p->mode.status & BD_DONE) == BD_DONE){
+ /* complete the lowest bits with the number of 'D' bits set */
+ result++;
+ }
+ bd_p++;
+ }
+ iapi_errno = result;
+ iapi_ReleaseChannel(channelNumber);
+ return -result;
+ }
+
+ /*
+ * 4. Buffer Descriptors allocation
+ */
+ iapi_InitializeMemory(local_ccb_p);
+
+#ifdef MCU
+ /*
+ * 5. Channel's configuration properties (mcu side only)
+ */
+ iapi_ChannelConfig( channelNumber,
+ ( local_cd_p->ownership >> CH_OWNSHP_OFFSET_EVT ) & 1UL,
+ ( local_cd_p->ownership >> CH_OWNSHP_OFFSET_MCU ) & 1UL,
+ ( local_cd_p->ownership >> CH_OWNSHP_OFFSET_DSP ) & 1UL);
+#endif /* MCU */
+
+ /* Setting interrupt handling */
+ iapi_ChangeCallbackISR(local_cd_p, local_cd_p->callbackISR_ptr);
+
+ /* Call initialization fn for polling synch on this channel*/
+ INIT_SLEEP(channelNumber);
+
+ /* No user arg pointer yet*/
+ userArgTable[cd_p->channelNumber]= NULL;
+
+ /*
+ * 6. read/write direction => enable/disable channel
+ */
+#ifdef MCU
+ channelPriorityMatx = &SDMA_CHNPRI_0;
+ channelPriorityMatx[channelNumber] = 1;
+#endif /* MCU */
+
+ local_ccb_p->status.openedInit = TRUE;
+ iapi_ReleaseChannel(channelNumber);
+ return IAPI_SUCCESS;
+}
+
+/* ***************************************************************************/
+/** Attempts to read nbyte from the data buffer descriptor associated with the
+ * channel channelNumber, into the user's data buffer pointed to by buf.
+ *
+ * <b>Algorithm:</b>\n
+ * - Check data structures are properly initialized:
+ * - Channel descriptor validity
+ * - Control block & Descriptor associated with the channel being worked on
+ * - Check initialization has been done for trusted channels
+ * - If transfer data size is used, check validity of combination transfer
+ * size/requested bytes
+ * - Set the 'D' done bits on all buffer descriptors
+ * - Starting of the channel
+ * - Synchronization mechanism handling:
+ * - for callback: just exit function
+ * - for polling: call the synchronization function then read data from
+ * buffer until either nbyte parameter is reached or all buffer descriptors
+ * have been processed.
+ *
+ * <b>Notes:</b>\n
+ * 1) Virtual DMA SDMA channels are unidirectional, an iapi_Read authorized
+ * on a channel means that we are expecting to receive from the SDMA. The
+ * meaning of an interrupt received from the SDMA is therefore that the
+ * data has been copied from the SDMA to the host's data buffers and is
+ * already passed on upper layers of the application.\n
+ *
+ * @param *cd_p chanenl descriptor for the channel to read from
+ * @param *buf buffer to receive the data
+ * @param nbyte number of bytes to read from channel
+ *
+ * @return
+ * - number of bytes read
+ * - -iapi_errno : in case of failure return negated value of iapi_errno
+ */
+int
+iapi_Read (channelDescriptor * cd_p, void * buf, unsigned short nbyte)
+{
+ int index = 0;
+ int readBytes;
+ int toRead;
+ int result = IAPI_SUCCESS;
+ unsigned int copyFinished;
+ int bufsize;
+ bufferDescriptor * bd_p;
+ channelControlBlock * ccb_p;
+ unsigned char * local_buf;
+ unsigned char chNum;
+ unsigned char div;
+
+ iapi_errno = IAPI_ERR_NO_ERROR;
+
+ /*
+ * 1. Check data structures are properly initialized
+ */
+ /* Channel descriptor validity */
+ if (cd_p == NULL){
+ result = IAPI_ERR_CD_UNINITIALIZED;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Channel control block validity */
+ if (cd_p->ccb_ptr == NULL){
+ result = IAPI_ERR_CCB_UNINITIALIZED;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Control block & Descriptor associated with the channel being worked on */
+ chNum = cd_p->channelNumber;
+ ccb_p = cd_p->ccb_ptr;
+
+ /* Try to aquire channel*/
+ if(iapi_GetChannel(chNum) != 0)
+ {
+ result = IAPI_ERR_CH_IN_USE | chNum;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Check if channel is already opened/initialized */
+ if (ccb_p->status.openedInit == FALSE) {
+ result = IAPI_ERR_CHANNEL_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+
+ /* Buffer descriptor validity */
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ if (bd_p == NULL ){
+ result = IAPI_ERR_BD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+
+
+ /* Check initialization has been done for trusted channels */
+ if (cd_p->trust == TRUE) {
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ for(index=0 ; index < cd_p->bufferDescNumber ; index++){
+ if ((bd_p->bufferAddr == NULL) || (bd_p->mode.count == 0)){
+ result = IAPI_ERR_BD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ bd_p++;
+ }
+ }
+
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ /*If transfer data size is used, check that the required read length is
+ * divisible by transfer data size expressed in bytes
+ */
+ if(cd_p->useDataSize)
+ {
+ /*Check for divisibility only if data size different then 8bit*/
+ if(cd_p->dataSize != TRANSFER_8BIT)
+ {
+ switch(cd_p->dataSize)
+ {
+ case TRANSFER_32BIT:
+ div = 4;
+ break;
+ case TRANSFER_16BIT:
+ div = 2;
+ break;
+ case TRANSFER_24BIT:
+ div = 3;
+ break;
+ /*we should not get to default*/
+ default:
+ result = IAPI_ERR_INVALID_PARAMETER | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ /*check the total number of bytes requested*/
+ if((nbyte % div) != 0)
+ {
+ result = IAPI_ERR_INVALID_PARAMETER | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ /*now check the length of every BD*/
+ for(index=0 ; index < cd_p->bufferDescNumber ; index++)
+ {
+ if((bd_p->mode.count % div) != 0)
+ {
+ result = IAPI_ERR_INVALID_PARAMETER | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ bd_p++;
+ }
+ }
+ }
+
+ /*
+ * 2. Set the 'D' done bits on all buffer descriptors
+ */
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ for (index=0 ; index < cd_p->bufferDescNumber ; index++) {
+ bd_p->mode.status |= BD_DONE;
+ bd_p++;
+ }
+
+ /*
+ * 3. Starting of the channel
+ */
+ iapi_lowStartChannel (chNum);
+ ccb_p->status.execute = TRUE;
+ readBytes = 0;
+
+ /*
+ * 4. Synchronization mechanism handling
+ */
+ if( cd_p->callbackSynch == DEFAULT_POLL){
+ iapi_SynchChannel(chNum);
+
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ toRead = nbyte;
+ copyFinished = FALSE;
+ local_buf = (unsigned char *)buf;
+
+ /*
+ * Check the 'RROR' bit on all buffer descriptors, set error number
+ * and return IAPI_FAILURE if set.
+ */
+ for (index=0 ; index < cd_p->bufferDescNumber ; index++)
+ {
+ if(bd_p->mode.status & BD_RROR)
+ {
+ result = IAPI_ERR_RROR_BIT_READ | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ bd_p++;
+ }
+
+
+ /*
+ * 5. Read loop
+ */
+
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ while (!copyFinished)
+ {
+ if (!(bd_p->mode.status & BD_DONE))
+ {
+ if (cd_p->trust == FALSE) {
+ bufsize = cd_p->bufferSize;
+ } else {
+ bufsize = bd_p->mode.count;
+ }
+ /*if L bit is set, read only "count" bytes and exit the loop*/
+ if(bd_p->mode.status & BD_LAST)
+ {
+ bufsize = bd_p->mode.count;
+ copyFinished = TRUE;
+ }
+ if (toRead > bufsize)
+ {
+ if (cd_p->trust == FALSE)
+ {
+ iapi_memcpy(local_buf, iapi_Phys2Virt(bd_p->bufferAddr), bufsize);
+ local_buf += bufsize;
+ }
+ readBytes += bufsize;
+ toRead -= bufsize;
+ /*advance bd_p only if bit L is not set. The loop will exit anyway.*/
+ if(!(bd_p->mode.status & BD_LAST))
+ {
+ if (bd_p->mode.status & BD_WRAP)
+ {
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ }
+ else if(((bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr) +
+ (cd_p->bufferDescNumber - 1)*sizeof(bufferDescriptor)) != bd_p)
+ {
+ bd_p++;
+ }
+ else
+ {
+ /* finished here : end of buffer descriptors */
+ copyFinished = TRUE;
+ }
+ }
+ }
+ else
+ {
+ if (cd_p->trust == FALSE)
+ {
+ iapi_memcpy(local_buf, iapi_Phys2Virt(bd_p->bufferAddr), toRead);
+ local_buf += toRead;
+ }
+ readBytes += toRead;
+ toRead = 0;
+ /* finished successfully : readBytes = nbytes */
+ copyFinished = TRUE;
+ }
+ }
+ else
+ {
+ /* finished here : buffer not already done*/
+ copyFinished = TRUE;
+ }
+ }
+ iapi_ReleaseChannel(chNum);
+ }
+
+ /*
+ *If synchronization type is callback, the user of I.API must
+ *release the channel
+ */
+ return readBytes;
+}
+
+/* ***************************************************************************/
+/*Attempts to write nbyte from the buffer pointed to by buf to the channel
+ * data buffers associated with the opened channel number channelNumber
+ *
+ * <b>Algorithm:</b>\n
+ *
+ * - Check data structures are properly initialized:
+ * - Channel descriptor validity
+ * - Channel control block validity
+ * - Buffer descriptor validity
+ * - If transfer data size is used, check validity of combination transfer
+ * size/requested bytes
+ * - Write loop\n
+ * Write occurs in the buffer acceded form buffer descriptor and continues
+ * to the "next" buffer which can be:\n
+ * -# the last BD of the ring so re-start from beginning\n
+ * -# the last BD of the BD array but no ring so finish\n
+ * -# (general case) the next BD in the BD array\n
+ * And copy continues until data fit in the current buffer or the nbyte
+ * parameter is reached.
+ * - Starting of the channel
+ *
+ * <b>Notes:</b>\n
+ * 1) Virtual DMA SDMA channels are unidirectionnal, an iapi_Write authorized
+ * on a channel means that we are expecting to send to the SDMA. The
+ * meaning of an interrupt received from the SDMA is therfore that the
+ * data has been delivered to the SDMA.
+ *
+ * @param *cd_p chanenl descriptor for the channel to write to
+ * @param *buf buffer with data to be written
+ * @param nbyte number of bytes to write to channel
+ *
+ * @return
+ * - number of bytes written
+ * - -iapi_errno if failure
+ */
+int
+iapi_Write (channelDescriptor * cd_p, void * buf, unsigned short nbyte)
+{
+ unsigned int writtenBytes = 0;
+ unsigned int toWrite;
+ int result = IAPI_SUCCESS;
+ unsigned int copyFinished;
+ unsigned int buffsize;
+ unsigned int index = 0;
+ bufferDescriptor * bd_p;
+ channelControlBlock * ccb_p;
+ unsigned char * local_buf;
+ unsigned char chNum;
+ unsigned char div;
+
+ iapi_errno = IAPI_ERR_NO_ERROR;
+
+ /*
+ * 1. Check data structures are properly initialized
+ */
+ /* Channel descriptor validity */
+ if (cd_p == NULL){
+ result = IAPI_ERR_CD_UNINITIALIZED;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Channel control block validity */
+ if (cd_p->ccb_ptr == NULL){
+ result = IAPI_ERR_CCB_UNINITIALIZED;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Control block & Descriptpor associated with the channel being worked on */
+ chNum = cd_p->channelNumber;
+ ccb_p = cd_p->ccb_ptr;
+
+ /* Try to aquire channel*/
+ if(iapi_GetChannel(chNum) != 0)
+ {
+ result = IAPI_ERR_CH_IN_USE | chNum;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Buffer descriptor validity */
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ if (bd_p == NULL ){
+ result = IAPI_ERR_BD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+
+ /* Check initialization has been done for trusted channels */
+ if (cd_p->trust == TRUE) {
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ for(index=0 ; index < cd_p->bufferDescNumber ; index++){
+ if ((bd_p->bufferAddr == NULL) || (bd_p->mode.count == 0)){
+ result = IAPI_ERR_BD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ bd_p++;
+ }
+ }
+
+
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ /*If transfer data size is used, check that the required write length is
+ * divisible by transfer data size expressed in bytes
+ */
+ if(cd_p->useDataSize)
+ {
+ /*Check for divisibility only if data size different then 8bit*/
+ if(cd_p->dataSize != TRANSFER_8BIT)
+ {
+ switch(cd_p->dataSize)
+ {
+ case TRANSFER_32BIT:
+ div = 4;
+ break;
+ case TRANSFER_16BIT:
+ div = 2;
+ break;
+ case TRANSFER_24BIT:
+ div = 3;
+ break;
+ /*we should not get to default*/
+ default:
+ result = IAPI_ERR_INVALID_PARAMETER | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ /*check the total number of bytes requested*/
+ if((nbyte % div) != 0)
+ {
+ result = IAPI_ERR_INVALID_PARAMETER | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ /*now check the length of every BD*/
+ for(index=0 ; index < cd_p->bufferDescNumber ; index++)
+ {
+ if((bd_p->mode.count % div) != 0)
+ {
+ result = IAPI_ERR_INVALID_PARAMETER | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ bd_p++;
+ }
+ }
+ }
+
+ /*
+ * 2. Write loop
+ */
+
+ local_buf = (unsigned char *)buf;
+ toWrite = nbyte;
+ copyFinished = FALSE;
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+
+ while (!copyFinished){
+
+ /* variable buffsize contains the nb of bytes that the SDMA will transfer at each pass of the while loop*/
+
+ /* in NON trusted mode, buffsize is copied from Channel descriptor bufferSize (same size for all transfers) */
+ if (cd_p->trust == FALSE) {
+ buffsize = cd_p->bufferSize;
+ }
+ /* in TRUSTED mode, it's up to the user to specify the size of each buffer thru an IoCtl call */
+ /* This IoCtl has directly modified the bd_p->mode.count */
+ /* therefore, buffersize is copied from the bd_p->mode.count */
+ else {
+ buffsize = bd_p->mode.count;
+ }
+
+ /* in any mode (trusted or non trusted), the transfer size must be overridden by */
+ /* "toWrite" when there is less remaining bytes to transfer than the current buffer size */
+ if (toWrite < buffsize) {
+ buffsize = toWrite;
+ }
+
+
+ if (!(bd_p->mode.status & BD_DONE)){
+ /* More data to write than a single buffer can contain */
+ if (cd_p->trust == FALSE ){
+ iapi_memcpy(iapi_Phys2Virt(bd_p->bufferAddr), local_buf, buffsize);
+ local_buf += buffsize;
+ }
+
+ /* update the BD count that will be used by the SDMA to transfer the proper nb of bytes */
+ bd_p->mode.count = buffsize;
+
+ bd_p->mode.status |= BD_DONE;
+ writtenBytes += buffsize;
+ toWrite -= buffsize;
+ /* Prepares access to the "next" buffer */
+ /* - case 1 - finished successfully : writtenBytes = nbytes */
+ if (toWrite == 0) {
+ copyFinished = TRUE;
+ }
+ /* - case 2 - Last BD and WRAP bit set so re-start from beginning */
+ /*else if ((bd_p->mode.status & BD_WRAP)){
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ }*/
+ /* - case 3 - Last BD of the BD but nor ring*/
+ else if (((bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr) +
+ (cd_p->bufferDescNumber - 1) * sizeof(bufferDescriptor)) == bd_p){
+ copyFinished = TRUE;
+ }
+ /* - case 4 - general : next BD in the BD array */
+ else {
+ bd_p++;
+ }
+
+ } else {
+ /* finished here : buffer not already done */
+ copyFinished = TRUE;
+ }
+ }
+
+ ccb_p->currentBDptr = ccb_p->baseBDptr;
+
+ /*
+ * 3. Starting of the channel
+ */
+ iapi_lowStartChannel(chNum);
+ ccb_p->status.execute = TRUE;
+
+ if( cd_p->callbackSynch == DEFAULT_POLL)
+ {
+ iapi_SynchChannel(chNum);
+ /*
+ * Check the 'RROR' bit on all buffer descriptors, set error number
+ * and return IAPI_FAILURE if set.
+ */
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ for (index=0 ; index < cd_p->bufferDescNumber ; index++)
+ {
+ if(bd_p->mode.status & BD_RROR)
+ {
+ result = IAPI_ERR_RROR_BIT_WRITE | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ bd_p++;
+ }
+ iapi_ReleaseChannel(chNum);
+ }
+
+ /*
+ *If synchronization type is callback, the user of I.API must
+ *release the channel
+ */
+ return writtenBytes;
+}
+
+
+
+
+/* ***************************************************************************/
+/* This function is used to receive data from the SDMA.
+ *
+ * <b>Algorithm:</b>\n
+ *
+ * The data control structure would be copied to IPCv1 complied Buffer
+ * Descriptor Array. This array shall be allocated from non cacheable memory.
+ * It would then provide this buffer descriptor array as an input to SDMA using
+ * channel control block and then configure the Host Enable (HE) or
+ * DSP enable (DE) bit of SDMA for the channel used for this transfer depending
+ * on the source.
+ *
+ * <b>Notes:</b>\n
+ * Virtual DMA channels are unidirectional, an iapi_Write_ipcv2 authorized
+ * on a channel means that source processor is expecting to send to the destination
+ * processor. The meaning of an interrupt received from the SDMA notifies that the
+ * data has been delivered to the destination processor.
+ *
+ * @param *cd_p chanenl descriptor for the channel to receive from
+ * @param *data_control_struct_ipcv2
+
+ * Data Control structure:
+ * -------------------------
+ * | Data Node Descriptor 1|
+ * -------------------------
+ * | Data Node Descriptor 2|
+ * -------------------------
+ * | : |
+ * | : |
+ * -------------------------
+ * |Data Node Descriptor n |
+ * -------------------------
+ *
+ * Data Node Descriptor (Buffer Descriptor):
+ *------------------------------------------------------------------------------
+ *| 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 … 0|
+ *------------------------------------------------------------------------------
+ *| L E D R R R R R |<---- Reserved ----> |<- Length-> |
+ *------------------------------------------------------------------------------
+ *| <---------------------------- Data Ptr ----------------------------------->|
+ *------------------------------------------------------------------------------
+ *
+ * L bit (LAST): If set, means that this buffer of data is the last buffer of the frame
+ * E bit (END): If set, we reached the end of the buffers passed to the function
+ * D bit (DONE): Only valid on the read callback. When set, means that the buffer has been
+ * filled by the SDMA.
+ * Length: Length of data pointed by this node in bytes
+ * Data Ptr: Pointer to the data pointed to by this node.
+ * The Function Shall not be called for the same channel unless the Read callback has been
+ * received for channel for which it has been called already.
+ *
+ * @return
+ * - IAPI_SUCCESS on success, IAPI_ERROR otherwise
+ *
+ *- -iapi_errno if failure
+ */
+
+int iapi_Read_ipcv2( channelDescriptor * cd_p, void * data_control_struct_ipcv2)
+{
+ channelControlBlock * ccb_p;
+
+
+/* The Parameters passed are considered to be validated by the upper layers*/
+
+ bufferDescriptor_ipcv1_v2 *bd_ipcv2_p;
+ dataNodeDescriptor *dnd_p = (dataNodeDescriptor*)data_control_struct_ipcv2;
+
+ ccb_p = cd_p->ccb_ptr;
+ iapi_errno = IAPI_ERR_NO_ERROR;
+
+ if(ccb_p->baseBDptr == NULL)
+{
+ iapi_errno = IAPI_ERR_BD_UNINITIALIZED;
+ return -(IAPI_ERR_BD_UNINITIALIZED);
+}
+
+ ccb_p->currentBDptr = ccb_p->baseBDptr;
+
+ /* Copy the data Node descriptor information to new BDs */
+ bd_ipcv2_p = (bufferDescriptor_ipcv1_v2*)iapi_Phys2Virt(ccb_p->baseBDptr);
+
+ while(1)
+ {
+ bd_ipcv2_p->bufferAddr = dnd_p->bufferAddr;
+ bd_ipcv2_p->mode.count = dnd_p->mode.count;
+#ifdef MCU
+ bd_ipcv2_p->mode.endianness = 1;
+#endif
+#ifdef DSP
+ bd_ipcv2_p->mode.endianness = 0;
+#endif
+
+ bd_ipcv2_p->mode.status = dnd_2_bd_status[dnd_p->mode.status & GET_LED_MASK];
+
+ if((dnd_p->mode.status & DND_END_OF_XFER) != 0)
+ {
+ /* Break the loop at End of Transfer */
+ break;
+
+ }
+ bd_ipcv2_p++;
+ dnd_p++;
+
+ }
+ /*
+ * Store the buffer address
+ */
+ dnd_read_control_struct[cd_p->channelNumber] = (dataNodeDescriptor*)data_control_struct_ipcv2;
+ /*
+ * Register the Call Back
+ */
+
+ iapi_AttachCallbackISR(cd_p, iapi_read_ipcv2_callback);
+
+ /*
+ * Starting of the channel
+ */
+ iapi_lowStartChannel(cd_p->channelNumber);
+ ccb_p->status.execute = TRUE;
+
+ return IAPI_SUCCESS;
+
+}
+
+
+/* ***************************************************************************/
+/*
+ * The function is used send a group of buffers to SDMA.
+ * <b>Algorithm:</b>\n
+ *
+ * The data control structure would be copied to IPCv1 complied Buffer
+ * Descriptor Array. This array shall be allocated from non cacheable memory.
+ * It would then provide this buffer descriptor array as an input to SDMA using
+ * channel control block and then configure the Host Enable (HE) or
+ * DSP enable (DE) bit of SDMA for the channel used for this transfer depending
+ * on the source.
+ * The Function Shall not be called for the same channel unless the Read callback has been
+ * received for channel for which it has been called already.
+ *
+ * <b>Notes:</b>\n
+ * Virtual DMA channels are unidirectional, an iapi_Write_ipcv2 authorized
+ * on a channel means that source processor is expecting to send to the destination
+ * processor. The meaning of an interrupt received from the SDMA notifies that the
+ * data has been delivered to the destination processor.
+ *
+ * @param *cd_p chanenl descriptor for the channel to write to
+ * @param *data_control_struct_ipcv2
+
+ * Data Control structure:
+ * -------------------------
+ * | Data Node Descriptor 1|
+ * -------------------------
+ * | Data Node Descriptor 2|
+ * -------------------------
+ * | : |
+ * | : |
+ * -------------------------
+ * |Data Node Descriptor n |
+ * -------------------------
+ *
+ * Data Node Descriptor (Buffer Descriptor):
+ *------------------------------------------------------------------------------
+ *| 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 … 0|
+ *------------------------------------------------------------------------------
+ *| L E D R R R R R |<---- Reserved ----> |<- Length-> |
+ *------------------------------------------------------------------------------
+ *| <---------------------------- Data Ptr ----------------------------------->|
+ *------------------------------------------------------------------------------
+ *
+ * L bit (LAST): If set, means that this buffer of data is the last buffer of the frame
+ * E bit (END): If set, we reached the end of the buffers passed to the function
+ * D bit (DONE): Only valid on the read callback. When set, means that the buffer has been
+ * filled by the SDMA.
+ * Length: Length of data pointed by this node in bytes
+ * Data Ptr: Pointer to the data pointed to by this node.
+ *
+ *
+ * @return
+ * - iapi sucess on success.
+ * - -iapi_errno if failure
+ */
+
+int iapi_Write_ipcv2( channelDescriptor * cd_p, void * data_control_struct_ipcv2)
+{
+
+ channelControlBlock * ccb_p;
+
+/* The Parameters passed are considered to be validated by the upper layers*/
+
+ bufferDescriptor_ipcv1_v2 *bd_ipcv2_p;
+ dataNodeDescriptor *dnd_p = (dataNodeDescriptor*)data_control_struct_ipcv2;
+ ccb_p = cd_p->ccb_ptr;
+ iapi_errno = IAPI_ERR_NO_ERROR;
+
+ if(ccb_p->baseBDptr == NULL)
+{
+ iapi_errno = IAPI_ERR_BD_UNINITIALIZED;
+ return -(IAPI_ERR_BD_UNINITIALIZED);
+}
+
+
+ ccb_p->currentBDptr = ccb_p->baseBDptr;
+
+ bd_ipcv2_p = (bufferDescriptor_ipcv1_v2*)iapi_Phys2Virt(ccb_p->currentBDptr);
+ /* Copy the data Node descriptor information to new BDs */
+ while(1)
+ {
+ bd_ipcv2_p->bufferAddr = dnd_p->bufferAddr;
+ bd_ipcv2_p->mode.count = dnd_p->mode.count;
+
+#ifdef MCU
+ bd_ipcv2_p->mode.endianness = 1;
+#endif
+#ifdef DSP
+ bd_ipcv2_p->mode.endianness = 0;
+#endif
+
+ bd_ipcv2_p->mode.status = dnd_2_bd_status[dnd_p->mode.status & GET_LED_MASK];
+
+ if((dnd_p->mode.status & DND_END_OF_XFER) != 0)
+ {
+ /* Break the loop at End of Transfer */
+ break;
+ }
+ bd_ipcv2_p++;
+ dnd_p++;
+
+ }
+
+ /*
+ * Starting of the channel
+ */
+ iapi_lowStartChannel(cd_p->channelNumber);
+ ccb_p->status.execute = TRUE;
+
+ return IAPI_SUCCESS;
+
+}
+
+/* ***************************************************************************/
+/** Call back ISR for the IPCv2 Receive.
+ *
+ * <b>Algorithm:</b>\n
+ * - This would copy back the informationfrom IPCv1 BD to IPCv2 BD on
+ * the receiving processor
+ *
+ * @return
+ * - void
+ */
+
+void iapi_read_ipcv2_callback(struct iapi_channelDescriptor* cd_p, void* data)
+{
+ dataNodeDescriptor *dnd_p = dnd_read_control_struct[cd_p->channelNumber];//cd_p->ccb_ptr->channelDNDBuffer;
+ bufferDescriptor_ipcv1_v2 *bd_ipcv2_p = (bufferDescriptor_ipcv1_v2*)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ int index = MAX_BD_NUM - 1;
+
+
+ do
+ {
+ dnd_p->mode.status = 0;
+ dnd_p->mode.count = bd_ipcv2_p->mode.count;
+
+ dnd_p->mode.status |= bd_ipcv2_p->mode.status & BD_DONE ? 0x00 : DND_DONE ;
+ dnd_p->mode.status |= bd_ipcv2_p->mode.status & BD_IPCV2_END_OF_FRAME ? DND_END_OF_FRAME : 0x00;
+ dnd_p->mode.status |= bd_ipcv2_p->mode.status & BD_LAST ? DND_END_OF_XFER : 0x00;
+ cd_p->ccb_ptr->currentBDptr = (bufferDescriptor*)iapi_Virt2Phys(bd_ipcv2_p);
+
+ if((bd_ipcv2_p->mode.status & BD_LAST) != 0 ||
+ (bd_ipcv2_p->mode.status & BD_CONT) == 0
+ )
+ break;
+ dnd_p++;
+ bd_ipcv2_p++;
+
+ }while(index--);
+
+ /*Call back the Original ISR */
+ cd_p->callbackISR_ptr(cd_p, data);
+}
+
+/* ***************************************************************************/
+/**Terminates a channel.
+ *
+ * <b>Algorithm:</b>\n
+ * - Check input parameters ans data structures
+ * - Check that all buffes have been processed (test all 'D' bits)
+ * - Stop the channel execution
+ * - Free alocated memory structures
+ * - Re-instantiate default interrupt handling
+ *
+ * @param *cd_p chanenl descriptor for the channel to close
+ *
+ * @return
+ * - IAPI_SUCCESS : OK
+ * - -iapi_errno : close failed
+ */
+int
+iapi_Close (channelDescriptor * cd_p)
+{
+ int index = 0;
+ int result = IAPI_SUCCESS;
+ unsigned char chNum;
+ bufferDescriptor * bd_p;
+ channelControlBlock * ccb_p;
+
+ /*
+ * 1. Check input parameters ans data structures
+ */
+ if (cd_p != NULL){
+ if (cd_p->ccb_ptr != NULL) {
+ chNum = cd_p->channelNumber;
+ ccb_p = cd_p->ccb_ptr;
+ } else {
+ result = IAPI_ERR_NO_CCB_DEFINED | IAPI_ERR_CH_AVAILABLE;
+ iapi_errno = result;
+ return -result;
+ }
+ } else {
+ result = IAPI_ERR_CD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE;
+ iapi_errno = result;
+ return -result;
+ }
+ /* Try to aquire channel*/
+ if(iapi_GetChannel(chNum) != 0)
+ {
+ result = IAPI_ERR_CH_IN_USE | chNum;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /*
+ * 2. Check that all buffes have been processed (test all 'D' bits),
+ * only if the forceClose bit in channel descriptor is set to FALSE
+ */
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ if (bd_p == NULL){
+ result = IAPI_ERR_BD_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE | chNum;
+ iapi_errno = result;
+ return -result;
+ }
+ if(cd_p->forceClose == FALSE)
+ {
+ for (index=cd_p->bufferDescNumber ; index>0 ; index--){
+ if (bd_p->mode.status & BD_DONE){
+ result = IAPI_ERR_CLOSE | IAPI_ERR_CH_AVAILABLE | chNum;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ }
+ bd_p++;
+ }
+ }
+ /*if the closing is forced,mark channel unused,set BD ownership to processor*/
+ else
+ {
+ ccb_p->status.execute = FALSE;
+ for (index=cd_p->bufferDescNumber ; index>0 ; index--)
+ {
+ bd_p->mode.status &= ~BD_DONE;
+ bd_p++;
+ }
+ }
+
+ /*
+ * 3. Stop the channel execution
+ */
+ iapi_lowStopChannel(chNum);
+
+ /*
+ * 4. Free alocated memory structures
+ */
+ if (cd_p->trust == FALSE ){
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr);
+ for (index=cd_p->bufferDescNumber ; index>0 ; index--){
+ FREE (iapi_Phys2Virt(bd_p->bufferAddr));
+ bd_p++;
+ }
+ }
+
+ /*
+ * 5. Re-instantiate default interrupt handling
+ */
+ iapi_DetachCallbackISR (cd_p);
+ FREE ((bufferDescriptor *)iapi_Phys2Virt(ccb_p->baseBDptr));
+ FREE (cd_p);
+ ccb_p->baseBDptr = NULL;
+ ccb_p->currentBDptr = NULL;
+ ccb_p->channelDescriptor = NULL;
+ ccb_p->status.openedInit = FALSE;
+
+ iapi_ReleaseChannel(chNum);
+
+ return IAPI_SUCCESS;
+}
+
+/* ***************************************************************************/
+/**The request argument selects the control function to be performed.
+ *
+ * <b>Algorithm:</b>\n
+ *
+ * - Check data structures are properly initialized:
+ * - Channel descriptor validity
+ * - Channel control block validity
+ * - The ctlRequest parameter contains in the lower 16 bits the control code of
+ * the change to be performed, and in the upper 16 bits, the BD to be
+ * modified if the change affects a BD od the channel.
+ * - Selection of the parameter to change and appropriate sanity checks:
+ * - Channel Descriptor: changes the pointer to the channel descriptor
+ * structure, the pointer to the new channel descriptor is given in the third
+ * argument call
+ * - Buffer Descriptor Number: changes the number of buffer descriptor for the
+ * channel
+ * - Buffer size: changes the size of the data buffers pointed to by the
+ * buffer descriptor; note that all buffer descriptors are assumed to have the
+ * same size for a given buffer descripotr chain
+ * - Blocking policy: changes the blocking policy for the read and write calls
+ * - Ownership: changes direction: turnaround
+ * - Synchronization method: changes the callback type, default or user. The*
+ * callback function table is set accordingly
+ * - Trust property: trust can only be changed through ChangeChannelDesc first
+ * request, this guarantees the close/open sequence for the channel
+ * - Callback Interrupt service routine pointer: changes the callback function
+ * pointer, when this method is used, to replace it with a new one
+ * - Channel control block pointer: not available
+ * - Priority: changes the channel priority directly in SDMA register
+ * - Watermark level: changes the value of the peripheral watermark level that
+ * passed to the script. The new value is passed in the third parameter call.
+ * - Wrap bit: changes to set to 1 the Wrap bit of the last buffer descriptor
+ *
+ * @param *cd_p channel descriptor for the channel to modify
+ * @param ctlRequest request control code and, if tha case, number of BD to be
+ * changed
+ * @param param parameter for the modification
+ *
+ * @return
+ * - IAPI_SUCCESS : OK
+ * - -iapi_errno : operation failed
+ */
+int
+iapi_IoCtl (channelDescriptor * cd_p, unsigned long ctlRequest,
+ unsigned long param)
+{
+ int retvalue;
+ int result = IAPI_SUCCESS;
+ unsigned char chNum;
+ unsigned long clean_ctlRequest; /* lower 16 bits of the ctlRequest*/
+ unsigned long bd_num; /* upper 16 bits of the ctlRequest*/
+
+ /*
+ * 1. Check data structures are properly initialized
+ */
+ /* Channel descriptor validity */
+ if (cd_p == NULL){
+ result = IAPI_ERR_CD_UNINITIALIZED;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Channel control block validity */
+ if (cd_p->ccb_ptr == NULL){
+ result = IAPI_ERR_CCB_UNINITIALIZED;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Control block & Descriptor associated with the channel being worked on */
+ chNum = cd_p->channelNumber;
+
+ /* Remove, if exists, BD number specified in upper bits of ctlRequest*/
+ clean_ctlRequest = ctlRequest & (~BD_NUM_MASK);
+
+ /* Extract, if exists, BD number specified in upper bits of ctlRequest*/
+ bd_num = (ctlRequest & BD_NUM_MASK) >> BD_NUM_OFFSET;
+
+ /* Check that the bd_num is valid*/
+ if(bd_num > cd_p->bufferDescNumber)
+ {
+ result = IAPI_ERR_INVALID_PARAMETER | chNum;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /*All checks OK, try to aquire channel*/
+ if(iapi_GetChannel(chNum) != 0)
+ {
+ result = IAPI_ERR_CH_IN_USE | chNum;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /*
+ * 2. Selection of the parameter to change and appropriate sanity checks
+ */
+ switch (clean_ctlRequest){
+
+ /*
+ * Channel Descriptor
+ * --- Changes the pointer to the channel descriptor structure: the pointer
+ * to the new channel descriptor is given in the third argument call.
+ */
+ case IAPI_CHANGE_CHANDESC:
+ if ((void *) param == NULL) {
+ result = IAPI_ERR_INVALID_PARAMETER;
+ iapi_errno = result;
+ iapi_ReleaseChannel(chNum);
+ return -result;
+ } else {
+ channelDescriptor * chParam = (channelDescriptor *)param;
+ if (chParam->channelNumber != chNum){
+ /* Release ch so it can be aquired by the Close fn*/
+ iapi_ReleaseChannel(chNum);
+ result = iapi_Close(cd_p);
+ if (result == IAPI_SUCCESS){
+ FREE((void*)cd_p);
+ iapi_AllocChannelDesc (&cd_p, chParam->channelNumber);
+ iapi_memcpy((void*)cd_p, (void*)chParam, sizeof (channelDescriptor));
+ /* Channel is released allready, so Open can get the channel*/
+ result = iapi_Open(cd_p, chParam->channelNumber);
+ if(result != IAPI_SUCCESS)
+ {
+ return result; /* error code already set in iapi_Open*/
+ }
+ } else {
+ return result; /* error code already set in iapi_Close*/
+ }
+ } else {
+ result = IAPI_ERR_CD_CHANGE | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_ReleaseChannel(chNum);
+ iapi_errno = result;
+ return -result;
+ }
+ return IAPI_SUCCESS;
+ }
+
+ /*
+ * Buffer Descriptor Number
+ * --- Changes the number of buffer descriptor for the channel.
+ */
+ case IAPI_CHANGE_BDNUM:
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_BUFFERDESCNUMBER, param);
+ iapi_ReleaseChannel(chNum);
+ return result;
+
+ /*
+ * Buffer size
+ * --- Changes the size of the data buffers pointed to by the buffer
+ * descriptor; note that all buffer descriptors are assumed to have the
+ * same size for a given buffer descripotr chain.
+ */
+ case IAPI_CHANGE_BUFFSIZE:
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_BUFFERSIZE, param);
+ iapi_ReleaseChannel(chNum);
+ return result;
+
+ /*
+ * Blocking policy
+ * --- Changes the blocking policy for the read and write calls.
+ */
+ case IAPI_CHANGE_CHANBLOCK:
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_BLOCKING, param);
+ iapi_ReleaseChannel(chNum);
+ return result;
+
+ /*
+ * Ownership
+ * --- Changes direction: turnaround
+ */
+ case IAPI_CHANGE_OWNERSHIP:
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_OWNERSHIP, param);
+ iapi_ReleaseChannel(chNum);
+ return result;
+
+ /*
+ * Synchronization method
+ * --- Changes the callback type, default or user. The callback function
+ * table is set accordingly.
+ */
+ case IAPI_CHANGE_SYNCH:
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_CALLBACKSYNCH, param);
+ iapi_ReleaseChannel(chNum);
+ return result;
+
+ /*
+ * Trust property
+ * --- trust can only be changed through ChangeChannelDesc first request,
+ * this guarantees the close/open sequence for the channel.
+ */
+ case IAPI_CHANGE_TRUST:
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_TRUST, param);
+ iapi_ReleaseChannel(chNum);
+ return result;
+
+ /*
+ * Callback Interrupt service routine pointer
+ * --- Cahnges the callback function pointer, when this method is used, to
+ * replace it with a new one.
+ */
+ case IAPI_CHANGE_CALLBACKFUNC:
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_CALLBACKISR_PTR, param);
+ iapi_ReleaseChannel(chNum);
+ return result;
+
+ /*
+ * Channel control block pointer
+ * --- NA
+ */
+ case IAPI_CHANGE_CHANCCB:
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_CCB_PTR, param);
+ iapi_ReleaseChannel(chNum);
+ return result;
+
+#ifdef MCU
+ /*
+ * Priority
+ * --- Changes the channel priority directly in SDMA register
+ */
+ case IAPI_CHANGE_PRIORITY:
+ {
+ volatile unsigned long * ChannelPriorities = &SDMA_CHNPRI_0;
+ if(param < MAX_CH_PRIORITY)
+ {
+ ChannelPriorities[ cd_p->channelNumber ] = param;
+ }
+ else
+ {
+ iapi_ReleaseChannel(chNum);
+ return IAPI_FAILURE;
+ }
+ }
+ break;
+#endif /* MCU */
+
+ /*
+ * Wrap
+ * --- Set to 1 the wrap bit of the last buffer descriptor of the array.
+ * it provides the possibility to have a circular buffer structure.
+ */
+ case IAPI_CHANGE_BDWRAP:
+ {
+ result = iapi_ChangeChannelDesc(cd_p,IAPI_BDWRAP , param);
+ iapi_ReleaseChannel(chNum);
+ return result;
+ }
+
+ /*
+ * Watermark
+ * --- Changes the value of the peripheral watermark level that triggers
+ * a DMA request. It impacts context of the channel, therefore channel 0
+ * must be started to update the context with this new value.
+ */
+ case IAPI_CHANGE_WATERMARK:
+ {
+ result = iapi_ChangeChannelDesc(cd_p,IAPI_WML , param);
+ iapi_ReleaseChannel(chNum);
+ return result;
+ }
+ /*
+ * INTR
+ * --- Set the INTR bit on specified BD or on all BD's if SET_BIT_ALL
+ * is passed as parameter.
+ */
+ case IAPI_CHANGE_SET_BDINTR:
+ {
+ bufferDescriptor * bde_p;
+ int j = 0;
+
+ retvalue = IAPI_SUCCESS;
+ if(param == SET_BIT_ALL)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for(j = 0; j < cd_p->bufferDescNumber; j++)
+ {
+ bde_p->mode.status |= BD_INTR;
+ bde_p++;
+ }
+ }
+ else if(param < cd_p->bufferDescNumber)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) +
+ param;
+ bde_p->mode.status |= BD_INTR;
+ }
+ else
+ {
+ retvalue = IAPI_FAILURE;
+ }
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * INTR
+ * --- Unset the INTR bit on specified BD or on all BD's if SET_BIT_ALL
+ * is passed as parameter.
+ */
+ case IAPI_CHANGE_UNSET_BDINTR:
+ {
+ bufferDescriptor * bde_p;
+
+ int j = 0;
+
+ retvalue = IAPI_SUCCESS;
+ if(param == SET_BIT_ALL)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for(j = 0; j < cd_p->bufferDescNumber; j++)
+ {
+ bde_p->mode.status &= ~BD_INTR;
+ bde_p++;
+ }
+ }
+ else if(param < cd_p->bufferDescNumber)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) +
+ param;
+ bde_p->mode.status &= ~BD_INTR;
+ }
+ else
+ {
+ retvalue = IAPI_FAILURE;
+ }
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+/*
+ * EventMask1
+ * --- Changes the value of the eventMask1
+ */
+ case IAPI_CHANGE_EVTMASK1:
+ {
+ cd_p->eventMask1 = param;
+ }
+ break;
+ /*
+ * EventMask2
+ * --- Changes the value of the eventMask2
+ */
+ case IAPI_CHANGE_EVTMASK2:
+ {
+ cd_p->eventMask2 = param;
+ }
+ break;
+ /*
+ * Peripheral Address
+ * --- Changes the value of the peripheralAddr
+ */
+ case IAPI_CHANGE_PERIPHADDR:
+ {
+ cd_p->peripheralAddr = param;
+ }
+ break;
+ /*
+ * Cont
+ * --- Set the CONT bit on specified BD on all BD's if SET_BIT_ALL
+ * is passed as parameter.
+ */
+ case IAPI_CHANGE_SET_BDCONT:
+ {
+ bufferDescriptor * bde_p;
+ int j = 0;
+
+ retvalue = IAPI_SUCCESS;
+ if(param == SET_BIT_ALL)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for(j = 0; j < cd_p->bufferDescNumber; j++)
+ {
+ bde_p->mode.status |= BD_CONT;
+ bde_p++;
+ }
+ }
+ else if(param < cd_p->bufferDescNumber)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) +
+ param;
+ bde_p->mode.status |= BD_CONT;
+ }
+ else
+ {
+ retvalue = IAPI_FAILURE;
+ }
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * Cont
+ * --- Unset the CONT bit on specified BD or on all BD's if SET_BIT_ALL
+ * is passed as parameter.
+ */
+ case IAPI_CHANGE_UNSET_BDCONT:
+ {
+ bufferDescriptor * bde_p;
+
+ int j = 0;
+
+ retvalue = IAPI_SUCCESS;
+ if(param == SET_BIT_ALL)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for(j = 0; j < cd_p->bufferDescNumber; j++)
+ {
+ bde_p->mode.status &= ~BD_CONT;
+ bde_p++;
+ }
+ }
+ else if(param < cd_p->bufferDescNumber)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) +
+ param;
+ bde_p->mode.status &= ~BD_CONT;
+ }
+ else
+ {
+ retvalue = IAPI_FAILURE;
+ }
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+
+ /*
+ * EXTD
+ * --- Set the EXTD bit on specified BD or on all BD's if SET_BIT_ALL
+ * is passed as parameter.
+ */
+ case IAPI_CHANGE_SET_BDEXTD:
+ {
+ bufferDescriptor * bde_p;
+ int j = 0;
+
+ retvalue = IAPI_SUCCESS;
+ if(param == SET_BIT_ALL)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for(j = 0; j < cd_p->bufferDescNumber; j++)
+ {
+ bde_p->mode.status |= BD_EXTD;
+ bde_p++;
+ }
+ }
+ else if(param < cd_p->bufferDescNumber)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) +
+ param;
+ bde_p->mode.status |= BD_EXTD;
+ }
+ else
+ {
+ retvalue = IAPI_FAILURE;
+ }
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * EXTD
+ * --- Unset the EXTD bit on specified BD or on all BD's if SET_BIT_ALL
+ * is passed as parameter.
+ */
+ case IAPI_CHANGE_UNSET_BDEXTD:
+ {
+ bufferDescriptor * bde_p;
+
+ int j = 0;
+
+ retvalue = IAPI_SUCCESS;
+ if(param == SET_BIT_ALL)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for(j = 0; j < cd_p->bufferDescNumber; j++)
+ {
+ bde_p->mode.status &= ~BD_EXTD;
+ bde_p++;
+ }
+ }
+ else if(param < cd_p->bufferDescNumber)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) +
+ param;
+ bde_p->mode.status &= ~BD_EXTD;
+ }
+ else
+ {
+ retvalue = IAPI_FAILURE;
+ }
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+
+ /*
+ * TRANSFER SIZE to be used for this channel
+ * --- Set the transfer size used indicator and code for transfer size in
+ * the CD
+ */
+case IAPI_CHANGE_SET_TRANSFER_CD:
+ {
+ bufferDescriptor * bde_p;
+ int j = 0;
+ retvalue = IAPI_SUCCESS;
+ if((param == TRANSFER_8BIT) || (param == TRANSFER_16BIT) ||
+ (param == TRANSFER_24BIT) || (param == TRANSFER_32BIT))
+ {
+ cd_p->useDataSize = TRUE;
+ cd_p->dataSize = param;
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for(j = 0; j < cd_p->bufferDescNumber; j++)
+ {
+ bde_p->mode.command = param;
+ bde_p++;
+ }
+ }
+ else
+ {
+ retvalue = IAPI_FAILURE;
+ }
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+
+
+ /*
+ * USER_ARG
+ * --- Set the user selectable pointer to be received by the callback
+ * function, if IRQ synch is used
+ */
+ case IAPI_CHANGE_USER_ARG:
+ {
+ userArgTable[cd_p->channelNumber]= (void*)param;
+ iapi_ReleaseChannel(chNum);
+ return IAPI_SUCCESS;
+ }
+ /*
+ * FORCE_CLOSE
+ * --- Set the forceClose bit in channelDescriptor to value passed in param.
+ * If this bit is TRUE, the channel in closed even if some BD are still
+ * owned by the SDMA.
+ */
+ case IAPI_CHANGE_FORCE_CLOSE:
+ {
+ retvalue = IAPI_SUCCESS;
+ if((param == TRUE) || (param == FALSE))
+ {
+ cd_p->forceClose = param;
+ }
+ else
+ {
+ iapi_errno = IAPI_ERR_INVALID_PARAMETER | cd_p->channelNumber;
+ retvalue = -iapi_errno;
+ }
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * TRANSFER type
+ * --- Set the last 2 bits in the command field of the BD to specify the
+ * transfer type 8, 16, 24, or 32 bits on all BD's, allready set in the CD
+ */
+ case IAPI_CHANGE_SET_TRANSFER:
+ {
+ bufferDescriptor * bde_p;
+ int j = 0;
+
+ retvalue = IAPI_SUCCESS;
+ if((param == TRANSFER_8BIT)||(param == TRANSFER_16BIT)||
+ (param == TRANSFER_24BIT)||(param == TRANSFER_32BIT))
+ {
+ bde_p = cd_p->ccb_ptr->baseBDptr;
+ for(j = 0; j < cd_p->bufferDescNumber; j++)
+ {
+ bde_p->mode.command = param;
+ bde_p++;
+ }
+ }
+ else
+ {
+ retvalue = IAPI_FAILURE;
+ }
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * BUFFER address
+ * --- Change buffer address in BD specified in the upper 16 bits of the
+ * ctlRequest.
+ */
+ case IAPI_CHANGE_SET_BUFFERADDR:
+ {
+ bufferDescriptor * bde_p;
+ retvalue = IAPI_SUCCESS;
+
+ /* Get pointer to the BD structure to change*/
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bde_p+= bd_num;
+
+ /* DO NOT translate address to physical */
+ bde_p->bufferAddr = (void*)param;
+
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * BUFFER address
+ * --- Get the buffer address from the BD specified in the upper 16 bits of the
+ * ctlRequest.
+ */
+ case IAPI_CHANGE_GET_BUFFERADDR:
+ {
+ bufferDescriptor * bde_p;
+ retvalue = IAPI_SUCCESS;
+
+ /* Get pointer to the BD structure to change*/
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bde_p+= bd_num;
+ /* Translate to virtual*/
+ *((unsigned long*)param) = (unsigned long)bde_p->bufferAddr;
+
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * EXTENDED BUFFER address
+ * --- Change extended buffer address in BD specified in the upper 16 bits
+ * of the ctlRequest.
+ */
+ case IAPI_CHANGE_SET_EXTDBUFFERADDR:
+ {
+ bufferDescriptor * bde_p;
+ retvalue = IAPI_SUCCESS;
+
+ /* Get pointer to the BD structure to change*/
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bde_p+= bd_num;
+
+ /* DO NOT translate address to physical. The user might want something else
+ *here
+ */
+ bde_p->extBufferAddr = (void*)param;
+
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * EXTENDED BUFFER address
+ * --- Get extended buffer address from the BD specified in the upper 16 bits
+ * of the ctlRequest.
+ */
+ case IAPI_CHANGE_GET_EXTDBUFFERADDR:
+ {
+ bufferDescriptor * bde_p;
+ retvalue = IAPI_SUCCESS;
+
+ /* Get pointer to the BD structure to change*/
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bde_p+= bd_num;
+
+ /* DO NOT translate address to vitual - user knows what is here.
+ */
+ *((unsigned long*)param) = (unsigned long)bde_p->extBufferAddr;
+
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * COMMAND field
+ * --- Change command field in BD specified in the upper 16 bits of the
+ * ctlRequest.
+ */
+ case IAPI_CHANGE_SET_COMMAND:
+ {
+ bufferDescriptor * bde_p;
+ retvalue = IAPI_SUCCESS;
+
+ /* Get pointer to the BD structure to change*/
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bde_p+= bd_num;
+ /* Update command field*/
+ bde_p->mode.command = param;
+
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * COMMAND field
+ * --- Get the command field from the BD specified in the upper 16 bits
+ * of the ctlRequest.
+ */
+ case IAPI_CHANGE_GET_COMMAND:
+ {
+ bufferDescriptor * bde_p;
+ retvalue = IAPI_SUCCESS;
+
+ /* Get pointer to the BD structure to change*/
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bde_p+= bd_num;
+ /* Get the command field*/
+ *((unsigned long*)param) = bde_p->mode.command;
+
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * COUNT field
+ * --- Change count field in BD specified in the upper 16 bits of the
+ * ctlRequest.
+ */
+ case IAPI_CHANGE_SET_COUNT:
+ {
+ bufferDescriptor * bde_p;
+ retvalue = IAPI_SUCCESS;
+
+ /* Get pointer to the BD structure to change*/
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bde_p+= bd_num;
+ /* Update count field*/
+ bde_p->mode.count = param;
+
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * COUNT field
+ * --- Get the count field of the BD specified in the upper 16 bits of the
+ * ctlRequest.
+ */
+ case IAPI_CHANGE_GET_COUNT:
+ {
+ bufferDescriptor * bde_p;
+ retvalue = IAPI_SUCCESS;
+
+ /* Get pointer to the BD structure to change*/
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bde_p+= bd_num;
+ /* Update count field*/
+ *((unsigned long*)param) = bde_p->mode.count;
+
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * STATUS field
+ * --- Change status field in BD specified in the upper 16 bits of the
+ * ctlRequest.
+ */
+ case IAPI_CHANGE_SET_STATUS:
+ {
+ bufferDescriptor * bde_p;
+ retvalue = IAPI_SUCCESS;
+
+ /* Get pointer to the BD structure to change*/
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bde_p+= bd_num;
+ /* Update status field*/
+ bde_p->mode.status = param;
+
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+ /*
+ * STATUS field
+ * --- Get the status field of the BD specified in the upper 16 bits
+ * of the ctlRequest.
+ */
+ case IAPI_CHANGE_GET_STATUS:
+ {
+ bufferDescriptor * bde_p;
+ retvalue = IAPI_SUCCESS;
+
+ /* Get pointer to the BD structure to change*/
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bde_p+= bd_num;
+ /* Update status field*/
+ *((unsigned long*)param) = bde_p->mode.status;
+
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+
+#ifdef MCU
+ /*
+ * Endianness
+ * --- Set the ENDIANNESS indicator in the command filed of the specified BD
+ * or on all BD's if SET_BIT_ALL is passed as parameter.
+ */
+ case IAPI_CHANGE_SET_ENDIANNESS:
+ {
+ bufferDescriptor * bde_p;
+ int j = 0;
+
+ retvalue = IAPI_SUCCESS;
+ if(param == SET_BIT_ALL)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for(j = 0; j < cd_p->bufferDescNumber; j++)
+ {
+ bde_p->mode.command = CHANGE_ENDIANNESS;
+ bde_p++;
+ }
+ }
+ else if(param < cd_p->bufferDescNumber)
+ {
+ bde_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr) +
+ param;
+ bde_p->mode.command = CHANGE_ENDIANNESS;
+ }
+ else
+ {
+ retvalue = IAPI_FAILURE;
+ }
+ iapi_ReleaseChannel(chNum);
+ return retvalue;
+ }
+#endif
+
+#ifdef SDMA_SKYE
+#ifdef MCU
+
+ /*
+ * SDMA State
+ * --- Enter the SDMA into LOCK Mode. No RAM updation allowed except same Context
+ * update with same PC Value.
+ */
+ case IAPI_ENTER_LOCK_MODE:
+ {
+ if(param == RESET_CLEAR_LOCK)
+ {
+ SDMA_SDMA_LOCK = (1 << RESET_CLR_BIT_OFFSET);
+ SDMA_SDMA_LOCK = (1 << LOCK_BIT_OFFSET);
+ iapi_SdmaState = LOCK;
+ }
+ else if(param == RESET_NOCLEAR_LOCK)
+ {
+ SDMA_SDMA_LOCK = (1 << LOCK_BIT_OFFSET);
+ iapi_SdmaState = LOCK;
+ }
+
+ }
+ break;
+
+#endif
+#endif
+ default:
+ retvalue = IAPI_ERR_CD_CHANGE_UNKNOWN | IAPI_ERR_CH_AVAILABLE | chNum;
+ iapi_errno = retvalue;
+ iapi_ReleaseChannel(chNum);
+ return -retvalue;
+ }
+
+
+ iapi_ReleaseChannel(chNum);
+ return IAPI_SUCCESS;
+}
+/* ***************************************************************************/
+/**Initialization of the SDMA - opening of channel 0, download RAM image.
+ *
+ * <b>Algorithm:</b>\n
+ * - open channel 0
+ * - if ram_image pointer passed is not NULL, download RAM image to SDMA
+ *
+ * @param
+ * - cd_p channel descriptor pointer for channel 0
+ * - ram_image pointer to RAM image to download, or NULL if this operation
+ * is not required
+ * - code_size size of the RAM image, in bytes
+ * - start_addr start address for the RAM image
+ *
+ * @return
+ * - IAPI_SUCCESS if all operations were successful
+ * - negated I.API error code if any operation failed
+ */
+#ifdef MCU
+int
+iapi_Init(channelDescriptor * cd_p, configs_data * config_p, unsigned short* ram_image,
+ unsigned short code_size, unsigned long start_addr)
+{
+#endif
+#ifdef DSP
+int
+iapi_Init(channelDescriptor * cd_p)
+{
+#endif
+
+int retvalue = IAPI_SUCCESS; /* Variable to store the results from I.API calls */
+
+ /* Check initialization not allredy done*/
+ if(iapi_CCBHead != NULL)
+ {
+ retvalue = IAPI_ERR_NOT_ALLOWED;
+ iapi_errno = retvalue;
+ return -retvalue;
+ }
+ /* Be sure SDMA has not started yet */
+#ifdef MCU
+ SDMA_H_C0PTR = 0x0;
+#endif
+#ifdef DSP
+ SDMA_D_C0PTR = 0x0;
+#endif
+
+ /*Try to open channel 0*/
+ retvalue = iapi_Open(cd_p, 0);
+ if(retvalue != IAPI_SUCCESS)
+ {
+ return retvalue;
+ }
+
+#ifdef MCU
+ /* Set Command Channel (Channel Zero) */
+ SDMA_CHN0ADDR = 0x4050;
+
+ /* Set bits of CONFIG register but with static context switching */
+ SDMA_H_CONFIG = (config_p->dspdma << 12) | (config_p->rtdobs << 11) |
+ (config_p->acr << 4) | (0);
+
+ /* Send the address for the host channel table to the SDMA*/
+ SDMA_H_C0PTR = (unsigned long)iapi_Virt2Phys(iapi_CCBHead);
+ /* If required, download the RAM image for SDMA*/
+ if(ram_image != NULL)
+ {
+ retvalue = iapi_SetScript(cd_p, (void*)ram_image, code_size,
+ start_addr);
+ }
+
+ /* Set bits of CONFIG register with given context switching mode */
+ SDMA_H_CONFIG = (config_p->dspdma << 12) | (config_p->rtdobs << 11) |
+ (config_p->acr << 4) | (config_p->csm);
+
+#endif
+#ifdef DSP
+ /* Send the address for the host channel table to the SDMA*/
+ SDMA_D_C0PTR = (unsigned long)iapi_Virt2Phys(iapi_CCBHead);
+#endif
+
+#ifdef SDMA_SKYE
+ iapi_SdmaState = OPEN;
+#endif
+
+ return retvalue;
+}
+
+
+/* ***************************************************************************/
+/**High layer interface for starting a channel
+ *
+ * <b>Algorithm:</b>\n
+ * - call low layer function for starting a channel
+ *
+ * @return
+ * - IAPI_SUCCESS
+ */
+int
+iapi_StartChannel(unsigned char channel)
+{
+ iapi_lowStartChannel(channel);
+ return IAPI_SUCCESS;
+}
+/* ***************************************************************************/
+/**High layer interface for stopping a channel
+ *
+ * <b>Algorithm:</b>\n
+ * - call low layer function for stopping a channel
+ *
+ * @return
+ * - IAPI_SUCCESS
+ */
+int
+iapi_StopChannel(unsigned char channel)
+{
+ iapi_lowStopChannel(channel);
+ return IAPI_SUCCESS;
+}
+
+/* ***************************************************************************/
+/**High layer interface for synchronising a channel
+ *
+ * <b>Algorithm:</b>\n
+ * - call low layer function for stopping a channel
+ *
+ * @return
+ * - IAPI_SUCCESS
+ */
+int iapi_SynchChannel(unsigned char channel)
+{
+ iapi_lowSynchChannel(channel);
+ return IAPI_SUCCESS;
+}
+
+#ifdef MCU
+/* ***************************************************************************/
+/**High layer interface for getting program memory data from SDMA
+ *
+ * <b>Algorithm:</b>\n
+ * - call coresponding low layer function
+ *
+ * @return
+ * - IAPI_SUCCESS
+ */
+int
+iapi_GetScript(channelDescriptor * cd_p, void * buf, unsigned short size,
+ unsigned long address)
+{
+ iapi_lowGetScript(cd_p, buf, size, address);
+ return IAPI_SUCCESS;
+}
+
+/* ***************************************************************************/
+/**High layer interface for getting data memory from SDMA
+ *
+ * <b>Algorithm:</b>\n
+ * - call coresponding low layer function
+ *
+ * @return
+ * - IAPI_SUCCESS
+ */
+int
+iapi_GetContext(channelDescriptor * cd_p, void * buf,
+ unsigned char channel)
+{
+ iapi_lowGetContext(cd_p, buf, channel);
+ return IAPI_SUCCESS;
+}
+
+/* ***************************************************************************/
+/**High layer interface for set program memory data to SDMA - e.g. scripts
+ *
+ * <b>Algorithm:</b>\n
+ * - call coresponding low layer function
+ *
+ * @return
+ * - IAPI_SUCCESS
+ */
+int
+iapi_SetScript(channelDescriptor * cd_p, void * buf, unsigned short nbyte,
+ unsigned long destAddr)
+{
+ iapi_lowSetScript(cd_p, buf, nbyte, destAddr);
+ return IAPI_SUCCESS;
+}
+
+/* ***************************************************************************/
+/**High layer interface for set data memory to SDMA - e.g. contexts.
+ *
+ * <b>Algorithm:</b>\n
+ * - call coresponding low layer function
+ *
+ * @return
+ * - IAPI_SUCCESS
+ */
+int
+iapi_SetContext(channelDescriptor * cd_p, void * buf,
+ unsigned char channel)
+{
+ iapi_lowSetContext(cd_p, buf, channel);
+ return IAPI_SUCCESS;
+}
+
+/* ***************************************************************************/
+/**High layer interface used to associate specified channel with a script.
+ *
+ * <b>Algorithm:</b>\n
+ * - call coresponding low layer function
+ *
+ * @return
+ * - IAPI_SUCCESS
+ */
+int
+iapi_AssignScript(channelDescriptor * cd_p, script_data * data_p)
+{
+ /* VERIFY THAT THE CHANNEL IT IS OPENED !!!!*/
+ return iapi_lowAssignScript(cd_p, data_p);
+}
+
+/* ***************************************************************************/
+/**High layer interface used to associate specified channel with a script.
+ *
+ * <b>Algorithm:</b>\n
+ * - call coresponding low layer function
+ *
+ * @return
+ * - IAPI_SUCCESS
+ */
+int
+iapi_SetChannelEventMapping(unsigned char event, unsigned long channel_map)
+{
+ return iapi_lowSetChannelEventMapping(event, channel_map);
+}
+#endif
+
+
+
+#ifdef DSP
+#define SDMA_DI SDMA_D_INTR
+void IRQ_Handler();
+#pragma interrupt IRQ_Handler
+#endif
+
+#ifdef MCU
+#define SDMA_DI SDMA_H_INTR
+#endif
+
+#ifndef IRQ_KEYWORD
+#define IRQ_KEYWORD
+#endif /* IRQ_KEYWORD */
+
+/* ***************************************************************************/
+/**
+ *@brief Find the first set bit in data parameter.
+ *
+ * Find the first set bit in unsigned integer parameter data. Data is scanned
+ * from MSB to LSB, searching for the set bit. The value returned is the
+ * offset from the most significant bit of data. If bit 31 is set, the value
+ * returned is zero. If no bits are set, a value of 32 is returned. This is compliant
+ * with the MCore FF1 instruction.
+ *
+ *
+ *
+ * @param
+ * - data: variable to check
+ *
+ * @return
+ * - the offset of the most significant bit set from the MSB
+ */
+unsigned int
+quartz_FF1( unsigned int data )
+{
+ register unsigned int result = 0;
+ while ( (result <= 31 ) && !( data & 0x80000000U) )
+ {
+ data <<= 1U;
+ result++;
+ }
+
+ return result;
+}
+
+IRQ_KEYWORD
+void
+IRQ_Handler(void)
+{
+ unsigned int intrReg;/* interrupt register mask for clearing the interrupt bit */
+ unsigned char chNum; /* SDMA channel number generating the a IRQ*/
+
+ /* Disable interrupts */
+ iapi_DisableInterrupts();
+ /*
+ * Clear interrupt in SDMA DI register => ACK to the SDMA the IT request.
+ * Get each interrupt number, clear them one after the other.
+ */
+ if(SDMA_DI != 0)
+ {
+ chNum = (unsigned char)(CH_NUM - 1 - quartz_FF1(SDMA_DI));
+ intrReg = (unsigned int)(1 << chNum);
+ }
+ else
+ {
+ chNum = 32;
+ intrReg = 0;
+ }
+
+ while (intrReg != 0)
+ {
+ SDMA_DI &= intrReg;
+ iapi_SDMAIntr |= intrReg;
+ iapi_WakeUp(chNum);
+ if (callbackIsrTable[chNum] != NULL)
+ {
+ /* release channel before callback, so IoCtl's are available*/
+ iapi_ReleaseChannel(chNum);
+ callbackIsrTable[chNum](iapi_CCBHead[chNum].channelDescriptor,
+ userArgTable[chNum]);
+ }
+
+ chNum = (unsigned char)(CH_NUM - 1 - quartz_FF1(SDMA_DI));
+ intrReg = (unsigned int)(1 << chNum);
+ }
+
+ /* Enable interrupts */
+ iapi_EnableInterrupts();
+}
+
+/* ***************************************************************************/
+/**
+ *@brief Perform a memory copy operation, in the memory of the same processor
+ *
+ * Size bytes are copied from the src address to dest address. It is used
+ * the channel pointed by cd_p, which must be configured prior to this call:
+ * opened, associated with the script to perform the operation - DSP_2_DSP,
+ * or MCU_2_MCU - and have the synchronization option set.
+ *
+ *
+ *
+ * @param
+ * - cd_p: channel configured to perform DSP_2_DSP or MCU_2_MCU transfers
+ * - dest: destination memory address
+ * - src : source memory address
+ * - size: number of bytes to copy from src to dest
+ *
+ * @return
+ * - the offset of the most significant bit set from the MSB
+ */
+
+int iapi_MemCopy(channelDescriptor * cd_p, void* dest, void* src, unsigned long size)
+{
+ int result = IAPI_SUCCESS;
+ bufferDescriptor * bd_p;
+
+ /* Channel descriptor validity */
+ if (cd_p == NULL)
+ {
+ result = IAPI_ERR_CD_UNINITIALIZED;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Check and set correct parameter */
+ if(cd_p->trust != TRUE)
+ {
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_TRUST, TRUE);
+ }
+
+ if(cd_p->bufferDescNumber != 1)
+ {
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_BUFFERDESCNUMBER, 1);
+ if(result != IAPI_SUCCESS)
+ {
+ return result;
+ }
+ }
+
+ if(cd_p->bufferSize != size)
+ {
+ result = iapi_ChangeChannelDesc(cd_p, IAPI_BUFFERSIZE, size);
+ if(result != IAPI_SUCCESS)
+ {
+ return result;
+ }
+ }
+ /* Set addresses*/
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ bd_p->bufferAddr = iapi_Virt2Phys(src);
+ bd_p->extBufferAddr = iapi_Virt2Phys(dest);
+
+ /* Set mode*/
+ bd_p->mode.count = size;
+ bd_p->mode.command = 0x00;
+ bd_p->mode.status = BD_INTR|BD_EXTD|BD_DONE|BD_WRAP;
+
+ /*Decide if we sleep or not*/
+ if(cd_p->callbackSynch == DEFAULT_POLL)
+ {
+ iapi_StartChannel(cd_p->channelNumber);
+ /* Call synchronization routine*/
+ iapi_SynchChannel(cd_p->channelNumber);
+ }
+ else
+ {
+ /* Just start the channel*/
+ iapi_StartChannel(cd_p->channelNumber);
+ }
+
+ return result;
+}
+
+/* ***************************************************************************/
+/**Return the channel number from the channel descriptor
+ *
+ * @param cd_p pointer to channel descriptor to obtain the channel number
+ *
+ * @return
+ * - the channel number
+ *
+ */
+int iapi_GetChannelNumber(channelDescriptor * cd_p)
+{
+ return cd_p->channelNumber;
+}
+
+/* ***************************************************************************/
+/**Return the error bit from the current BD of the channel
+ *
+ *
+ * @param cd_p pointer to channel descriptor
+ *
+ * @return
+ * - 0 if no error detected
+ * - BD_RROR | DATA_ERROR if error detected
+ *
+ */
+unsigned long iapi_GetError(channelDescriptor * cd_p)
+{
+ return ((cd_p->ccb_ptr->currentBDptr->mode.status & BD_RROR) |
+ (*(unsigned long*)&cd_p->ccb_ptr->status & DATA_ERROR));
+}
+
+/* ***************************************************************************/
+/**Return the count from the current BD of the channel
+ *
+ *
+ * @param cd_p pointer to channel descriptor
+ *
+ * @return
+ * - count field of the current BD for the channel
+ *
+ */
+int iapi_GetCount(channelDescriptor * cd_p)
+{
+ return (int)(cd_p->ccb_ptr->currentBDptr->mode.count);
+}
+
+/* ***************************************************************************/
+/**Return the sum of counts for all the BD's owned by the processor for
+ * the channel specified by the received parameter.
+ *
+ *
+ * @param cd_p pointer to channel descriptor
+ *
+ * @return
+ * - sum of count fields
+ *
+ */
+int iapi_GetCountAll(channelDescriptor * cd_p)
+{
+ int retval = 0;
+ int i = 0;
+ bufferDescriptor* bd_p;
+
+ bd_p = cd_p->ccb_ptr->baseBDptr;
+
+ while((i < cd_p->bufferDescNumber) && ((bd_p->mode.status & BD_DONE) == 0))
+ {
+ retval += bd_p->mode.count;
+ i++;
+ bd_p++;
+ }
+ return retval;
+}
diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiLow.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiLow.c
new file mode 100644
index 000000000000..1470f2f366fd
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiLow.c
@@ -0,0 +1,150 @@
+/******************************************************************************
+ *
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiLow.c
+ *
+ * $Id iapiLow.c $
+ *
+ * Description:
+ * This library is written in C to guarantee functionality and integrity in
+ * the usage of SDMA virtual DMA channels. This API (Application Programming
+ * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE
+ * fashion.
+ * These are the LOW level functions of the I.API.
+ *
+ *
+ * /
+ *
+ * $Log iapiLow.c $
+ *
+ *****************************************************************************/
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include "epm.h"
+#include "iapiLow.h"
+
+/**
+ * Function Section
+ */
+
+
+/* ***************************************************************************/
+/**Records an ISR callback function pointer into the ISR callback
+ * function table
+ *
+ * @param cd_p channel descriptor to attach callback to
+ * @param func_p pointer to the callback function to be registered
+ *
+ * @return none
+ */
+void
+iapi_AttachCallbackISR (channelDescriptor * cd_p,
+ void (* func_p)(channelDescriptor * cd_p, void * arg))
+
+{
+ if (cd_p->callbackSynch == CALLBACK_ISR) {
+ iapi_DisableInterrupts();
+ callbackIsrTable[cd_p->channelNumber] = func_p;
+ iapi_EnableInterrupts();
+ } else if (cd_p->callbackSynch == DEFAULT_POLL) {
+ callbackIsrTable[cd_p->channelNumber] = NULL;
+ } else {
+ iapi_errno = IAPI_ERR_CALLBACKSYNCH_UNKNOWN | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ }
+}
+
+
+/* ***************************************************************************/
+/**Detaches (removes) an ISR callback function pointer from the ISR callback
+ * function table
+ *
+ * <b>Algorithm:</b>\n
+ * - Attach a null function to replace the original one.
+ *
+ * @param cd_p channel descriptor to detach callback from
+ *
+ * @return none
+ */
+void
+iapi_DetachCallbackISR (channelDescriptor * cd_p)
+
+{
+ iapi_AttachCallbackISR (cd_p, NULL);
+}
+
+/* ***************************************************************************/
+/**Updates an ISR callback function pointer into the ISR callback function
+ * table
+ *
+ * <b>Algorithm:</b>\n
+ * - Detach the old function pointer (if any) and attach the new one
+ *
+ * @param cd_p channel descriptor to attach callback to
+ * @param func_p pointer to the callback function to be registered
+ *
+ * @return none
+ */
+void
+iapi_ChangeCallbackISR (channelDescriptor * cd_p,
+ void (* func_p)(channelDescriptor * cd_p, void * arg))
+{
+ iapi_DetachCallbackISR(cd_p);
+ iapi_AttachCallbackISR(cd_p, func_p);
+}
+
+/* ***************************************************************************/
+/**Loop while the channel is not done on the SDMA
+ *
+ * <b>Algorithm:</b>\n
+ * - Loop doing nothing but checking the I.API global variable to indicate
+ * that the channel has been completed (interrupt from SDMA)
+ *
+ * <b>Notes:</b>\n
+ * - The ISR must update the I.API global variable iapi_SDMAIntr.
+ *
+ * @param channel channel number to poll on
+ *
+ * @return none
+ */
+void
+iapi_lowSynchChannel (unsigned char channel)
+{
+ while (!((1UL << channel) & iapi_SDMAIntr)) ;
+ iapi_SDMAIntr &= ~(1UL << channel);
+}
+
+/* ***************************************************************************/
+/**Fill the buffer descriptor with the values given in parameter.
+ *
+ * @return none
+ */
+void
+iapi_SetBufferDescriptor( bufferDescriptor * bd_p, unsigned char command,
+ unsigned char status, unsigned short count,
+ void * buffAddr, void * extBufferAddr)
+{
+ bd_p->mode.command = command;
+ bd_p->mode.status = status;
+ bd_p->mode.count = count;
+ if (buffAddr != NULL) {
+ bd_p->bufferAddr = iapi_Virt2Phys(buffAddr);
+ } else {
+ bd_p->bufferAddr = buffAddr;
+ }
+ bd_p->extBufferAddr = extBufferAddr;
+}
+
diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiLowDsp.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiLowDsp.c
new file mode 100644
index 000000000000..df7d44a9bd46
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiLowDsp.c
@@ -0,0 +1,79 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiLowDsp.c
+ *
+ * $Id iapiLowDsp.c $
+ *
+ * Description:
+ * This library is written in C to guarantee functionality and integrity in
+ * the usage of SDMA virtual DMA channels. This API (Application Programming
+ * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE
+ * fashion.
+ * These are the LOW level functions of the I.API specific to MCU.
+ *
+ *
+ *
+ *
+ * $Log iapiLowDsp.c $
+ *
+ *****************************************************************************/
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include "epm.h"
+#include "iapiLow.h"
+
+/* ****************************************************************************
+ * Function Section
+ *****************************************************************************/
+#ifdef DSP
+
+/* ***************************************************************************/
+/**Starts the channel (core specific register)
+ *
+ * <b>Algorithm:</b>\n
+ * - Bit numbered "channel" of DspEnStartReg register is set
+ *
+ * @param channel channel to start
+ *
+ * @return none
+ */
+void
+iapi_lowStartChannel (unsigned char channel)
+{
+ SDMA_D_START |= (1 << channel);
+}
+
+/* ***************************************************************************/
+/**Stops the channel (core specific register)
+ *
+ * <b>Algorithm:</b>
+ * - Bit numbered "channel" of DspEnStopReg register is cleared
+ *
+ * <b>Notes:</b>\n
+ * - This is a write one to clear register
+ *
+ * @param channel channel to stop
+ *
+ * @return none
+ */
+void
+iapi_lowStopChannel (unsigned char channel)
+{
+ SDMA_D_STATSTOP &= (1 << channel);
+}
+
+#endif /* DSP */
diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiLowMcu.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiLowMcu.c
new file mode 100644
index 000000000000..886c70c4c3d3
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiLowMcu.c
@@ -0,0 +1,518 @@
+/******************************************************************************
+ *
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiLowMcu.c
+ *
+ * $Id iapiLowMcu.c $
+ *
+ * Description:
+ * This library is written in C to guarantee functionality and integrity in
+ * the usage of SDMA virtual DMA channels. This API (Application Programming
+ * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE
+ * fashion.
+ * These are the LOW level functions of the I.API specific to MCU.
+ *
+ *
+ * http://compass/mot.com/go/115342679
+ *
+ * $Log iapiLowMcu.c $
+ *
+ *****************************************************************************/
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include <string.h>
+
+#include "epm.h"
+#include "iapiLow.h"
+
+/* ****************************************************************************
+ * Function Section
+ *****************************************************************************/
+#ifdef MCU
+
+/* ***************************************************************************/
+/**Send a command on SDMA's channel zero.
+ * Check if buffer descriptor is already used by the sdma, if yes return
+ * an error as c0BDNum is wrong.
+ *
+ * <b>Notes</b>\n
+ * There is an upgrade in the script on the Context load command and
+ * the fact that the context structure has a fixed length of 20 or 24
+ * depending on SDMA versions.
+ *
+ * @return
+ * - IAPI_SUCCESS
+ * - -iapi_errno if failure
+ */
+int
+iapi_Channel0Command( channelDescriptor * cd_p, void * buf,
+ unsigned short nbyte, unsigned char command)
+{
+ channelControlBlock * ccb_p;
+ bufferDescriptor * bd_p;
+ int result = IAPI_SUCCESS;
+ unsigned char chNum;
+
+
+ /*
+ * Check data structures are properly initialized
+ */
+ /* Channel descriptor validity */
+ if (cd_p == NULL){
+ result = IAPI_ERR_CD_UNINITIALIZED;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Channel control block validity */
+ if (cd_p->ccb_ptr == NULL){
+ result = IAPI_ERR_CCB_UNINITIALIZED;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Control block & Descriptpor associated with the channel being worked on */
+ chNum = cd_p->channelNumber;
+ ccb_p = cd_p->ccb_ptr;
+
+ /* Is channel already in use ? */
+ if (ccb_p->baseBDptr != NULL ) {
+ result = IAPI_ERR_BD_ALLOCATED | IAPI_ERR_CH_AVAILABLE | chNum;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Allocation of buffer descriptors */
+ bd_p = (bufferDescriptor *)MALLOC(sizeof( bufferDescriptor ), SDMA_ERAM);
+ if (bd_p != NULL) {
+ ccb_p->baseBDptr = (bufferDescriptor *)iapi_Virt2Phys(bd_p);
+ } else {
+ result = IAPI_ERR_BD_ALLOCATION | IAPI_ERR_CH_AVAILABLE | chNum;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Buffer descriptor setting */
+ iapi_SetBufferDescriptor( bd_p, command , BD_WRAP|BD_DONE|BD_INTR , nbyte,
+ buf,NULL);
+
+ /* Actually the transfer */
+ iapi_lowStartChannel( cd_p->channelNumber );
+ iapi_lowSynchChannel( cd_p->channelNumber );
+
+ /* Cleaning of allocation */
+ FREE( bd_p );
+ ccb_p->baseBDptr = NULL;
+
+ return IAPI_SUCCESS;
+
+}
+
+/* ***************************************************************************/
+/**Starts the channel (core specific register)
+ *
+ * <b>Algorithm:</b>\n
+ * - Bit numbered "channel" of HostEnStartReg register is set
+ *
+ * @param channel channel to start
+ *
+ * @return none
+ */
+void
+iapi_lowStartChannel (unsigned char channel)
+{
+ SDMA_H_START |= 1 << channel;
+}
+
+/* ***************************************************************************/
+/**Stops the channel (core specific register)
+ *
+ * <b>Algorithm:</b>
+ * - Bit numbered "channel" of HostEnStopReg register is cleared
+ *
+ * <b>Notes:</b>\n
+ * - This is a write one to clear register
+ *
+ * @param channel channel to stop
+ *
+ * @return none
+ */
+void
+iapi_lowStopChannel (unsigned char channel)
+{
+ SDMA_H_STATSTOP &= 1 << channel;
+}
+
+/* ***************************************************************************/
+/**Initialize the initial priority of registers and channel enable
+ * RAM from the MCU side. No channels are enabled, all priorities are set to 0.
+ *
+ * @return none
+ */
+void
+iapi_InitChannelTables(void)
+{
+
+ /* No channel is enabled*/
+ iapi_memset((void *)&SDMA_CHNENBL_0, 0x00, sizeof(unsigned long)*EVENTS_NUM);
+ /* All channels have priority 0*/
+ iapi_memset((void *)&SDMA_CHNPRI_0, 0x00, sizeof(unsigned long)*CH_NUM);
+}
+
+/* ***************************************************************************/
+/** The host enable (HE), hosts override (HO), dsp enable (DE), dsp override
+ * (DO) registers are involved here.
+ * Host and Dsp enable registers are here to signify that the MCU or DSP side
+ * have prepared the appropriate buffers and are now ready. If the channel is
+ * owned by the MCU the override bit for that channel needs to be cleared :
+ * the host allows the channel to be used.\n
+ *
+ * Then the override bits can define (mcuOverride dspOverride):\n
+ * - 0 0 channel is public: transfer to/from MCU to DSP
+ * - 0 1 channel if owned by DSP
+ * - 1 0 channel if owned by MCU
+ * - 1 1 channel zero config
+ *
+ * See also :\n
+ * IAPI Table 1.1 "Channel configuration properties"
+ *
+ * @param channel channel to configure
+ * @param eventOverride event ownership
+ * @param mcuOverride ARM ownership
+ * @param dspOverride DSP ownership
+ *
+ * @return
+ * - -iapi_errno if the 3 override parameters are all set
+ * - IAPI_SUCCESS in other cases (valid cases)
+ */
+int
+iapi_ChannelConfig (unsigned char channel, unsigned eventOverride,
+ unsigned mcuOverride, unsigned dspOverride)
+{
+ int result = IAPI_SUCCESS;
+
+ if ( ( eventOverride == 1 ) &&
+ ( mcuOverride == 1 ) &&
+ ( dspOverride == 1 ) ){
+ result = IAPI_ERR_CONFIG_OVERRIDE ;
+ iapi_errno = result;
+ return -result;
+ } else {
+ /*
+ * DSP side
+ */
+ if ( dspOverride ){
+ SDMA_H_DSPOVR &= ~( 1 << channel );
+ } else {
+ SDMA_H_DSPOVR |= ( 1 << channel );
+ }
+ /*
+ * Event
+ */
+ if ( eventOverride ){
+ SDMA_H_EVTOVR &= ~( 1 << channel );
+ } else {
+ SDMA_H_EVTOVR |= ( 1 << channel );
+ }
+ /*
+ * MCU side
+ */
+ if ( mcuOverride ) {
+ SDMA_H_HOSTOVR &= ~( 1 << channel );
+ } else {
+ SDMA_H_HOSTOVR |= ( 1 << channel );
+ }
+ }
+ return IAPI_SUCCESS;
+}
+/* ***************************************************************************/
+/**Load the context data of a channel from SDMA
+ *
+ * <b>Algorithm:</b>\n
+ * - Setup BD with appropiate parameters
+ * - Start channel
+ * - Poll for answer
+ *
+ * @param *cd_p channel descriptor for channel 0
+ * @param *buf pointer to receive context data
+ * @param channel channel for which the context data is requested
+ *
+ * @return none
+ */
+void
+iapi_lowGetContext(channelDescriptor * cd_p, void * buf, unsigned char channel)
+{
+ bufferDescriptor * bd_p;
+
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+
+ /*Setup buffer descriptor with channel 0 command*/
+ iapi_SetBufferDescriptor(&bd_p[0],
+ C0_GETDM,
+ (unsigned char)(BD_DONE | BD_INTR | BD_WRAP | BD_EXTD),
+ (unsigned short)sizeof(contextData)/4,
+ buf,
+ (void *)(CHANNEL_CONTEXT_BASE_ADDRESS + (sizeof(contextData)*channel/4)));
+ /* Receive, polling method*/
+ iapi_lowStartChannel(cd_p->channelNumber);
+ iapi_lowSynchChannel(cd_p->channelNumber);
+}
+/* ***************************************************************************/
+/**Read "size" byte /2 at SDMA address (address) and write them in buf
+ *
+ * <b>Algorithm:</b>\n
+ * - Setup BD with appropiate parameters (C0_GETPM)
+ * - Start channel
+ * - Poll for answer
+ *
+ * <b>Notes</b>\n
+ * - Parameter "size" is in bytes, it represents the size of "buf", e.g.
+ * the size in bytes of the script to be loaded.
+ * - Parameter "address" denotes the RAM address for the script in SDMA
+ *
+ * @param *cd_p channel descriptor for channel 0
+ * @param *buf pointer to receive the data
+ * @param size number of bytes to read
+ * @param address address in SDMA RAM to start reading from
+ *
+ * @return none
+ */
+void
+iapi_lowGetScript(channelDescriptor * cd_p, void * buf, unsigned short size,
+ unsigned long address)
+{
+ bufferDescriptor * bd_p;
+
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+
+ /*Setup buffer descriptor with channel 0 command*/
+ iapi_SetBufferDescriptor(&bd_p[0],
+ C0_GETPM,
+ (unsigned char)(BD_DONE | BD_INTR | BD_WRAP | BD_EXTD),
+ (unsigned short)size/2,/*count in shorts*/
+ buf,
+ (void *)address);
+ /* Receive, polling method*/
+ iapi_lowStartChannel(cd_p->channelNumber);
+ iapi_lowSynchChannel(cd_p->channelNumber);
+}
+
+/* ***************************************************************************/
+/**Load a SDMA script to SDMA
+ *
+ * <b>Algorithm:</b>\n
+ * - Setup BD with appropiate parameters (C0_SETPM)
+ * - Start channel
+ * - Poll for answer
+ *
+ * <b>Notes</b>\b
+ * - Parameter "size" is in bytes, it represents the size of "buf", e.g.
+ * the size in bytes of the script to be uploaded.
+ * - Parameter "address" denotes the RAM address for the script in SDMA
+ *
+ * @param *cd_p channel descriptor for channel 0
+ * @param *buf pointer to the script
+ * @param size size of the script, in bytes
+ * @param address address in SDMA RAM to place the script
+ *
+ * @return none
+ */
+void
+iapi_lowSetScript(channelDescriptor * cd_p, void * buf, unsigned short size,
+ unsigned long address)
+{
+ bufferDescriptor * bd_p;
+
+ bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+
+ /*Setup buffer descriptor with channel 0 command*/
+ iapi_SetBufferDescriptor(&bd_p[0],
+ C0_SETPM,
+ (unsigned char)(BD_DONE | BD_INTR | BD_WRAP | BD_EXTD),
+ (unsigned short)size/2,/*count in shorts*/
+ buf,
+ (void *)(address));
+ /* Receive, polling method*/
+ iapi_lowStartChannel(cd_p->channelNumber);
+ iapi_lowSynchChannel(cd_p->channelNumber);
+}
+
+
+/* ***************************************************************************/
+/**Load the context for a channel to SDMA
+ *
+ * <b>Algorithm:</b>\n
+ * - Send context and poll for answer.
+ *
+ * @param *cd_p channel descriptor for channel 0
+ * @param *buf pointer to context data
+ * @param channel channel to place the context for
+ *
+ * @return none
+ */
+void
+iapi_lowSetContext(channelDescriptor * cd_p, void * buf, unsigned char channel)
+{
+
+ bufferDescriptor * local_bd_p;
+#ifdef SDMA_SKYE
+
+ unsigned char command =0;
+
+ local_bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+
+
+ command = channel <<3 ;
+ command = command | C0_SETCTX;
+ iapi_SetBufferDescriptor( &local_bd_p[0],
+ command,
+ (unsigned char)(BD_DONE | BD_INTR | BD_WRAP),
+ (unsigned short)(sizeof(contextData)/4),
+ buf,
+ NULL);
+#else
+
+ local_bd_p = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+
+
+
+
+ iapi_SetBufferDescriptor( &local_bd_p[0],
+ C0_SETDM,
+ (unsigned char)(BD_DONE | BD_INTR | BD_WRAP|BD_EXTD),
+ (unsigned short)(sizeof(contextData)/4),
+ buf,
+ (void *)(2048+(sizeof(contextData)/4)*channel));
+#endif
+ /* Send */
+ iapi_lowStartChannel( cd_p->channelNumber );
+ iapi_lowSynchChannel( cd_p->channelNumber );
+
+}
+
+/* ***************************************************************************/
+/**Associate specified channel with the script starting at the
+ * specified address. Channel 0 command is used to load the set-up context
+ * for the channel. The address used must be generated by the GUI tool
+ * used to create RAM images for SDMA.
+ *
+ * <b>Algorithm:</b>\n
+ * - Set-up and load the context.
+ *
+ * @param *cd_p pointer to the channel descriptor of the channel
+ * @param *data_p: pointer to the data identifying the script to be associated
+ * with the channel
+ *
+ * @return
+ * - IAPI_SUCCESS : OK
+ * - -iapi_errno : operation failed, return negated value of iapi_errno
+ */
+
+int
+iapi_lowAssignScript(channelDescriptor * cd_p, script_data * data_p)
+{
+ contextData * chContext; /* context to be loaded for the channel */
+ channelDescriptor * cd0_p; /* pointer to channel descriptor of channel 0*/
+ int result = IAPI_SUCCESS;
+
+ /*Verify passed data*/
+ if(cd_p == NULL || data_p == NULL)
+ {
+ result = IAPI_ERR_INVALID_PARAMETER;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* Allocate context and initialize PC to required script start adress*/
+ chContext = (contextData *) MALLOC(sizeof(contextData), SDMA_ERAM);
+ if (chContext == NULL)
+ {
+ result = IAPI_ERR_B_ALLOC_FAILED | cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+ iapi_memset(chContext, 0x00, sizeof(contextData));
+ chContext->channelState.pc = data_p->load_address;
+
+ /* Send by context the event mask,base address for peripheral
+ * and watermark level
+ */
+ chContext->gReg[0] = data_p->event_mask2;
+ chContext->gReg[1] = data_p->event_mask1;
+ chContext->gReg[6] = data_p->shp_addr;
+ chContext->gReg[7] = data_p->wml;
+ if (data_p->per_addr)
+ chContext->gReg[2] = data_p->per_addr;
+
+ /* Set transmited data to the CD*/
+ cd_p->watermarkLevel = data_p->wml;
+ cd_p->eventMask1 = data_p->event_mask1;
+ cd_p->eventMask2 = data_p->event_mask2;
+
+ /* Get the cd0_p*/
+ cd0_p = (cd_p->ccb_ptr - cd_p->channelNumber)->channelDescriptor;
+
+ /*load the context*/
+ iapi_lowSetContext(cd0_p, chContext, cd_p->channelNumber);
+
+ /* release allocated memory*/
+ FREE(chContext);
+
+ return IAPI_SUCCESS;
+}
+
+/* ***************************************************************************/
+/** Set the channels to be triggered by an event. The for every channel that
+ *must be triggered by the event, the corresponding bit from channel_map
+ *parameter must be set to 1. (e.g. for the event to trigger channels 31 and
+ *0 one must pass 0x80000001)
+ *
+ *
+ * <b>Algorithm:</b>\n
+ * - Update the register from Channel Enable RAM with the channel_map
+ *
+ * @param event event for which to set the channel association
+ * @param channel_map channels to be triggered by event. Put the corresponding
+ * bit from this 32-bit value to 1 for every channel that should be
+ * triggered by the event.
+ *
+ * @return
+ * - IAPI_SUCCESS : OK
+ * - -iapi_errno : operation failed, return negated value of iapi_errno
+ */
+int
+iapi_lowSetChannelEventMapping(unsigned char event, unsigned long channel_map)
+{
+ volatile unsigned long * channelEnableMatx;
+ int result = IAPI_SUCCESS;
+
+ /* Check validity of event*/
+ if (event < EVENTS_NUM)
+ {
+ channelEnableMatx = &SDMA_CHNENBL_0;
+ channelEnableMatx[event] |= channel_map;
+ return result;
+ }
+ else
+ {
+ result = IAPI_ERR_INVALID_PARAMETER | event;
+ iapi_errno = result;
+ return -result;
+ }
+}
+
+#endif /* MCU */
diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddle.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddle.c
new file mode 100644
index 000000000000..3b8d7a7c4f2e
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddle.c
@@ -0,0 +1,623 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiMiddle.c
+ *
+ * $Id iapiMiddle.c $
+ *
+ * Description:
+ * This library is written in C to guarantee functionality and integrity in
+ * the usage of SDMA virtual DMA channels. This API (Application Programming
+ * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE
+ * fashion.
+ * These are the MIDDLE level functions of the I.API.
+ *
+ *
+ *
+ *
+ * $Log iapiMiddle.c $
+ *
+ *****************************************************************************/
+
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include "epm.h"
+#include <string.h>
+
+#include "iapiLow.h"
+#include "iapiMiddle.h"
+
+/* ****************************************************************************
+ * Global Variable Section
+ *****************************************************************************/
+
+
+/* ****************************************************************************
+ * Function Section
+ *****************************************************************************/
+
+/* ***************************************************************************/
+/**Allocates one Buffer Descriptor structure using information present in the
+ * channel descriptor.
+ *
+ * @param *ccb_p channel control block used to get the channel descriptor
+ *
+ * @return
+ * - pointer on the new Buffer Descriptor
+ * - NULL if allocation failed
+ *
+ */
+bufferDescriptor *
+iapi_AllocBD (channelControlBlock * ccb_p)
+
+{
+ bufferDescriptor * ptrBD = NULL;
+
+ if (ccb_p->channelDescriptor->bufferDescNumber != 0){
+#ifdef CONFIG_SDMA_IRAM
+ channelDescriptor * cd_p = ccb_p->channelDescriptor;
+ if(cd_p->channelNumber >= MXC_DMA_CHANNEL_IRAM) {
+ ptrBD = (bufferDescriptor *)
+ MALLOC( ccb_p->channelDescriptor->bufferDescNumber *
+ sizeof(bufferDescriptor), SDMA_IRAM);
+ } else
+#endif /*CONFIG_SDMA_IRAM*/
+ {
+ ptrBD = (bufferDescriptor *)
+ MALLOC( ccb_p->channelDescriptor->bufferDescNumber *
+ sizeof(bufferDescriptor), SDMA_ERAM);
+ }
+ }
+ if (ptrBD != NULL) {
+ ptrBD->mode.command = 0;
+ ptrBD->mode.status = 0;
+ ptrBD->mode.count = 0;
+ ptrBD->bufferAddr = NULL;
+ }
+
+ return ptrBD;
+}
+
+/* ***************************************************************************/
+/**Allocate one channel context data structure.
+ *
+ * @param **ctxd_p pointer to context data to be allocated
+ * @param channel channel number of context data structure
+ *
+ * @return
+ * - IAPI_SUCCESS
+ * - -iapi_errno if allocation failed
+ */
+int
+iapi_AllocContext(contextData ** ctxd_p, unsigned char channel)
+{
+ contextData * ctxData;
+ int result = IAPI_SUCCESS;
+
+ if (*ctxd_p != NULL){
+ result = IAPI_ERR_CC_ALREADY_DEFINED | IAPI_ERR_CH_AVAILABLE | channel;
+ iapi_errno = result;
+ return -result;
+ }
+
+ ctxData = (contextData *)MALLOC(sizeof(contextData), SDMA_ERAM);
+
+ if (ctxData !=NULL) {
+ *ctxd_p = ctxData;
+ return IAPI_SUCCESS;
+
+ } else {
+ *ctxd_p = NULL;
+ result = IAPI_ERR_CC_ALLOC_FAILED | IAPI_ERR_CH_AVAILABLE | channel;
+ iapi_errno = result;
+ return -result;
+ }
+}
+
+/* ***************************************************************************/
+/**Allocates channel description and fill in with default values.
+ *
+ * <b>Algorithm:</b>\n
+ * - Check channel properties.
+ * - Then modifies the properties of the channel description with default
+ *
+ * @param **cd_p pointer to channel descriptor to be allocated
+ * @param channel channel number of channel descriptor
+ *
+ * @return
+ * - IAPI_SUCCESS
+ * - -iapi_errno if allocation failed
+ *
+ */
+int
+iapi_AllocChannelDesc (channelDescriptor ** cd_p, unsigned char channel)
+{
+#ifdef MCU
+ volatile unsigned long * chPriorities = &SDMA_CHNPRI_0;
+#endif /* MCU */
+ channelDescriptor * tmpCDptr;
+ int result = IAPI_SUCCESS;
+
+
+ if (*cd_p != NULL){
+ result = IAPI_ERR_CD_ALREADY_DEFINED | IAPI_ERR_CH_AVAILABLE | channel;
+ iapi_errno = result;
+ return -result;
+ }
+
+ tmpCDptr = (channelDescriptor *)MALLOC(sizeof(channelDescriptor), SDMA_ERAM);
+
+ if (tmpCDptr != NULL){
+ iapi_memcpy(tmpCDptr, &iapi_ChannelDefaults, sizeof (channelDescriptor));
+ tmpCDptr->channelNumber = channel;
+#ifdef MCU
+ if (chPriorities[channel] != 0) {
+ tmpCDptr->priority = chPriorities[channel];
+ } else {
+ chPriorities[channel] = tmpCDptr->priority;
+ }
+#endif
+ * cd_p = tmpCDptr ;
+ return IAPI_SUCCESS;
+ } else {
+ * cd_p = NULL;
+ result = IAPI_ERR_CD_ALLOC_FAILED | IAPI_ERR_CH_AVAILABLE | channel;
+ iapi_errno = result;
+ return -result;
+ }
+}
+
+/* ***************************************************************************/
+/**Changes channel description information after performing sanity checks.
+ *
+ * <b>Algorithm:</b>\n
+ * - Check channel properties.
+ * - Then modifies the properties of the channel description.
+ *
+ * @param *cd_p channel descriptor of the channel to change
+ * @param whatToChange control code indicating the desired change
+ * @param newval new value
+ *
+ * @return
+ * - IAPI_SUCCESS
+ * - IAPI_FAILURE if change failed
+ *
+ */
+int
+iapi_ChangeChannelDesc (channelDescriptor * cd_p, unsigned char whatToChange,
+ unsigned long newval)
+{
+ bufferDescriptor * tmpBDptr;
+ unsigned char index = 0;
+ int result = IAPI_SUCCESS;
+
+ /* verify parameter validity */
+ if (cd_p == NULL) {
+ result = IAPI_ERR_CD_UNINITIALIZED;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* verify channel descriptor initialization */
+ if (cd_p->ccb_ptr == NULL){
+ result = IAPI_ERR_CCB_UNINITIALIZED | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /* verify channel is not in use */
+ tmpBDptr = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for (index=cd_p->bufferDescNumber ; index>0 ; index--){
+ if (tmpBDptr->mode.status & BD_DONE){
+ result = IAPI_ERR_CH_IN_USE | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+ tmpBDptr++;
+ }
+
+ /* Select the change accorded to the selector given in parameter */
+ switch (whatToChange){
+
+ /*
+ * Channel Number
+ */
+ case IAPI_CHANNELNUMBER:
+ /* Channel number can not be changed (description remains attached) */
+ result = IAPI_ERR_CD_CHANGE_CH_NUMBER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+
+ /*
+ * Buffer Descriptor Number
+ */
+ case IAPI_BUFFERDESCNUMBER:
+ if (newval < MAX_BD_NUM){
+ if (newval != cd_p->bufferDescNumber){
+ /* Free memory used for previous old data */
+ if (cd_p->ccb_ptr->baseBDptr != NULL){
+ tmpBDptr = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for (index=0 ; index < cd_p->bufferDescNumber ; index++){
+ if (tmpBDptr->bufferAddr != NULL){
+ if (cd_p->trust == FALSE) {
+ FREE(iapi_Phys2Virt(tmpBDptr->bufferAddr));
+ }
+ }
+ tmpBDptr ++ ;
+ }
+ FREE((bufferDescriptor *)iapi_Phys2Virt((cd_p->ccb_ptr)->baseBDptr));
+ }
+ (cd_p->ccb_ptr)->baseBDptr = NULL;
+ (cd_p->ccb_ptr)->currentBDptr = NULL;
+ /* Allocate and initialize structures */
+ cd_p->bufferDescNumber = (unsigned char)newval;
+ cd_p->ccb_ptr->status.openedInit = FALSE;
+ if (IAPI_SUCCESS !=iapi_InitializeMemory (cd_p->ccb_ptr))
+ {
+ result = IAPI_ERR_BD_ALLOCATION | cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+ cd_p->ccb_ptr->status.openedInit = TRUE;
+ }
+ break;
+ } else {
+ result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /*
+ * Buffer size
+ */
+ case IAPI_BUFFERSIZE:
+ if (newval < MAX_BD_SIZE)
+ {
+ if (newval != cd_p->bufferSize)
+ {
+ /* Free memory used for previous old data */
+ if (cd_p->ccb_ptr->baseBDptr != NULL)
+ {
+ tmpBDptr = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ for (index=0 ; index < cd_p->bufferDescNumber ; index++)
+ {
+ if (cd_p->trust == FALSE)
+ {
+ FREE(iapi_Phys2Virt(tmpBDptr->bufferAddr));
+ }
+ tmpBDptr ++ ;
+ }
+ FREE((bufferDescriptor *)iapi_Phys2Virt((cd_p->ccb_ptr)->baseBDptr));
+ }
+ (cd_p->ccb_ptr)->baseBDptr = NULL;
+ (cd_p->ccb_ptr)->currentBDptr = NULL;
+ /* Allocate and initialize structures */
+ cd_p->bufferSize = (unsigned short)newval;
+ cd_p->ccb_ptr->status.openedInit = FALSE;
+ if (IAPI_SUCCESS !=iapi_InitializeMemory (cd_p->ccb_ptr))
+ {
+ result = IAPI_ERR_BD_ALLOCATION | cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+ cd_p->ccb_ptr->status.openedInit = TRUE;
+ }
+ break;
+ }
+ else
+ {
+ result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+
+ /*
+ * Blocking / non blocking feature
+ */
+ case IAPI_BLOCKING:
+ if (newval < MAX_BLOCKING){
+ cd_p->blocking = newval;
+ break;
+ } else {
+ result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /*
+ * Synchronization method
+ */
+ case IAPI_CALLBACKSYNCH:
+ if (newval < MAX_SYNCH){
+ cd_p->callbackSynch = newval;
+ iapi_ChangeCallbackISR( cd_p, cd_p->callbackISR_ptr);
+ break;
+ } else {
+ result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+
+ /*
+ * Ownership of the channel
+ */
+ case IAPI_OWNERSHIP:
+#ifdef DSP
+ result = IAPI_ERR_NOT_ALLOWED | cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+#endif /* DSP */
+#ifdef MCU
+ if (newval < MAX_OWNERSHIP){
+ cd_p->ownership = newval;
+ iapi_ChannelConfig( cd_p->channelNumber,
+ ( newval >> CH_OWNSHP_OFFSET_EVT ) & 1,
+ ( newval >> CH_OWNSHP_OFFSET_MCU ) & 1,
+ ( newval >> CH_OWNSHP_OFFSET_DSP ) & 1);
+ break;
+ } else {
+ result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+#endif /* MCU */
+
+ /*
+ * Priority
+ */
+ case IAPI_PRIORITY:
+#ifdef DSP
+ result = IAPI_ERR_NOT_ALLOWED | cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+#endif /* DSP */
+
+#ifdef MCU
+ if (newval < MAX_CH_PRIORITY){
+ volatile unsigned long * ChannelPriorities = &SDMA_CHNPRI_0;
+ ChannelPriorities[ cd_p->channelNumber ] = newval;
+ break;
+ } else {
+ result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+#endif /* MCU */
+
+
+ /*
+ * "Trust" property
+ */
+ case IAPI_TRUST:
+ if (newval < MAX_TRUST){
+ if (cd_p->trust != newval){
+ cd_p->trust = newval;
+ if (newval == FALSE) {
+ if (IAPI_SUCCESS !=iapi_InitializeMemory (cd_p->ccb_ptr))
+ {
+ result = IAPI_ERR_BD_ALLOCATION | cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+ }
+ }
+ break;
+ } else {
+ result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+ /*
+ * Callback function pointer
+ */
+ case IAPI_CALLBACKISR_PTR:
+ if ( (void *)newval != NULL){
+ {
+ union {
+ void * voidstar;
+ void (* funcptr)(channelDescriptor * cd_p, void * arg);
+ } value;
+ value.voidstar = (void*) newval;
+ cd_p->callbackISR_ptr = value.funcptr;
+ }
+ iapi_ChangeCallbackISR( cd_p, cd_p->callbackISR_ptr);
+ break;
+ } else {
+ result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+
+ /*
+ * Channel Control Block pointer
+ */
+ case IAPI_CCB_PTR:
+ cd_p->ccb_ptr = (channelControlBlock *)newval;
+ cd_p->ccb_ptr->channelDescriptor = cd_p;
+ break;
+
+ /*
+ * WRAP/UNWRAP
+ */
+ case IAPI_BDWRAP:
+ /* point to first BD */
+ tmpBDptr = (bufferDescriptor *)iapi_Phys2Virt(cd_p->ccb_ptr->baseBDptr);
+ /* to point to last BD */
+ tmpBDptr += cd_p->bufferDescNumber - 1;
+ if (newval == TRUE){
+ /* wrap last BD */
+ tmpBDptr->mode.status |= BD_WRAP;
+ break;
+ }
+ else if (newval == FALSE){
+ /* unwrap last BD */
+ tmpBDptr->mode.status &= ~BD_WRAP;
+ break;
+ }
+ else {
+ result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+
+ /*
+ * Watermark level
+ */
+ case IAPI_WML:
+#ifdef DSP
+ result = IAPI_ERR_NOT_ALLOWED | cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+#endif /* DSP */
+#ifdef MCU
+ if (newval < MAX_WML){
+ if (cd_p->watermarkLevel != newval){
+ cd_p->watermarkLevel = newval;
+ }
+ break;
+ }
+ else {
+ result = IAPI_ERR_INVALID_PARAMETER | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+#endif /* MCU */
+
+ /*
+ * Detect errors
+ */
+ default:
+ result = IAPI_ERR_CD_CHANGE_UNKNOWN | IAPI_ERR_CH_AVAILABLE |
+ cd_p->channelNumber;
+ iapi_errno = result;
+ return -result;
+ }
+
+ return IAPI_SUCCESS;
+}
+
+
+/* ***************************************************************************/
+/**Initialize a table of function pointers that contain the interrupt Service
+ * Routine callback pointers for the SDMA channels with a default value
+ *
+ * <b>Algorithm:</b>\n
+ * - Loop on each element of the global IAPI variable callbackIsrTable
+ *
+ * @param *func_p default callback functon for all SDMA channels
+ *
+ * @return none
+ */
+void
+iapi_InitializeCallbackISR(void(* func_p)(channelDescriptor * cd_p, void * arg))
+{
+ unsigned long chCnt;
+
+ for (chCnt = 0 ; chCnt < CH_NUM ; chCnt++){
+ callbackIsrTable[chCnt] = func_p;
+ }
+}
+
+/* ***************************************************************************/
+/**For the specified channel control block, attach the array of buffer
+ * descriptors, the channel description structure and initialize channel's
+ * status using information in the channel descriptor.
+ *
+ * @param *ccb_p pointer to channel control block
+ *
+ * @return none
+ *
+ */
+int
+iapi_InitializeMemory (channelControlBlock * ccb_p)
+{
+ bufferDescriptor * bd_p;
+ unsigned char index;
+ int result = IAPI_SUCCESS;
+
+ /* Attach the array of Buffer descriptors */
+ bd_p = iapi_AllocBD( ccb_p );
+ if (bd_p != NULL)
+ {
+ ccb_p->baseBDptr = (bufferDescriptor *)iapi_Virt2Phys(bd_p);
+ ccb_p->currentBDptr = ccb_p->baseBDptr;
+ for(index=0 ;index < ccb_p->channelDescriptor->bufferDescNumber-1 ; index++)
+ {
+ if (ccb_p->channelDescriptor->trust == TRUE)
+ {
+ iapi_SetBufferDescriptor(bd_p,
+ (unsigned char)ccb_p->channelDescriptor->dataSize,
+ BD_CONT|BD_EXTD, ccb_p->channelDescriptor->bufferSize,
+ NULL, NULL);
+ }
+ else
+ {
+ if (ccb_p->channelDescriptor->bufferSize != 0)
+ {
+ iapi_SetBufferDescriptor(bd_p,
+ (unsigned char)ccb_p->channelDescriptor->dataSize,
+ BD_CONT|BD_EXTD, ccb_p->channelDescriptor->bufferSize,
+ MALLOC(ccb_p->channelDescriptor->bufferSize, SDMA_ERAM), NULL);
+ }
+ }
+ bd_p++;
+ }
+
+ if (ccb_p->channelDescriptor->trust == TRUE)
+ {
+ iapi_SetBufferDescriptor(bd_p,
+ (unsigned char)ccb_p->channelDescriptor->dataSize,
+ BD_EXTD|BD_WRAP|BD_INTR, ccb_p->channelDescriptor->bufferSize,
+ NULL, NULL);
+ }
+ else
+ {
+ if (ccb_p->channelDescriptor->bufferSize != 0)
+ {
+ iapi_SetBufferDescriptor(bd_p,
+ (unsigned char)ccb_p->channelDescriptor->dataSize,
+ BD_EXTD|BD_WRAP|BD_INTR,
+ ccb_p->channelDescriptor->bufferSize,
+ MALLOC(ccb_p->channelDescriptor->bufferSize, SDMA_ERAM), NULL);
+ }
+ }
+ }
+ else
+ {
+ result = IAPI_ERR_BD_ALLOCATION;
+ return -result;
+ }
+ return result;
+}
diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddleMcu.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddleMcu.c
new file mode 100644
index 000000000000..8dc814418e4c
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiMiddleMcu.c
@@ -0,0 +1,52 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiMiddleMcu.c
+ *
+ * $Id iapiMiddleMcu.c $
+ *
+ * Description:
+ * This library is written in C to guarantee functionality and integrity in
+ * the usage of SDMA virtual DMA channels. This API (Application Programming
+ * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE
+ * fashion.
+ * These are the MIDDLE level functions of the I.API specific to MCU.
+ *
+ *
+ *
+ *
+ * $Log iapiMiddleMcu.c $
+ *
+ *****************************************************************************/
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include "epm.h"
+#include <string.h>
+
+#include "iapiLow.h"
+#include "iapiMiddle.h"
+
+/* ****************************************************************************
+ * Global Variable Section
+ *****************************************************************************/
+
+/*extern void * __HEAP_START;
+extern void * __HEAP_END;
+*/
+
+/* ****************************************************************************
+ * Function Section
+ *****************************************************************************/
diff --git a/arch/arm/plat-mxc/sdma/iapi/src/iapiOS.c b/arch/arm/plat-mxc/sdma/iapi/src/iapiOS.c
new file mode 100644
index 000000000000..643cab549742
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/iapi/src/iapiOS.c
@@ -0,0 +1,64 @@
+/******************************************************************************
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * 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
+ *
+ ******************************************************************************
+ *
+ * File: iapiOS.c
+ *
+ * $Id iapiOS.c $
+ *
+ * Description:
+ * This library is written in C to guarantee functionality and integrity in
+ * the usage of SDMA virtual DMA channels. This API (Application Programming
+ * Interface) allow SDMA channels' access in an OPEN, READ, WRITE, CLOSE
+ * fashion.
+ * These are the OS level functions of the I.API - are OS dependant and must
+ * be provided by the user of I.API.
+ *
+ *
+ * /
+ *
+ * $Log iapiOS.c $
+ *
+ *****************************************************************************/
+
+/* ****************************************************************************
+ * Include File Section
+ *****************************************************************************/
+#include "epm.h"
+#include "iapiLow.h"
+
+/**
+ * Function Section
+ */
+#ifdef CONFIG_SDMA_IRAM
+void*(* iapi_iram_Malloc) (size_t size);
+#endif /*CONFIG_SDMA_IRAM*/
+
+void*(* iapi_Malloc) (size_t size);
+void (* iapi_Free) (void * ptr);
+
+void*(* iapi_Virt2Phys) (void * ptr);
+void*(* iapi_Phys2Virt) (void * ptr);
+
+void (* iapi_WakeUp)(int);
+void (* iapi_GotoSleep)(int);
+void (* iapi_InitSleep)(int);
+
+void*(* iapi_memcpy)(void *dest, const void *src, size_t count);
+void*(* iapi_memset)(void *dest, int c, size_t count);
+
+void (* iapi_EnableInterrupts)(void);
+void (* iapi_DisableInterrupts)(void);
+
+int (* iapi_GetChannel)(int);
+int (* iapi_ReleaseChannel)(int);
diff --git a/arch/arm/plat-mxc/sdma/sdma.c b/arch/arm/plat-mxc/sdma/sdma.c
new file mode 100644
index 000000000000..542860532356
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/sdma.c
@@ -0,0 +1,1481 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file plat-mxc/sdma/sdma.c
+ * @brief This file contains functions for Smart DMA API
+ *
+ * SDMA (Smart DMA) is used for transferring data between MCU and peripherals
+ *
+ * @ingroup SDMA
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/proc_fs.h>
+
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+#include <mach/dma.h>
+#include <mach/hardware.h>
+
+#include "iapi.h"
+
+#define M3_BASE_ADDRESS CSD0_BASE_ADDR
+#define CHAD(ch) sdma_data[0].cd->ccb_ptr[ch].channelDescriptor
+
+/*!
+ * SDMA status mutex
+ */
+static struct semaphore sdma_status_mutex;
+
+/*!
+ * SDMA channel sleep queues
+ */
+//static struct semaphore sdma_sleep_mutex[MAX_DMA_CHANNELS];
+static wait_queue_head_t sdma_sleep_queue[MAX_DMA_CHANNELS];
+
+/*!
+ * SDMA channel synchronization
+ */
+static struct semaphore sdma_synch_mutex[MAX_DMA_CHANNELS];
+
+/*!
+ * SDMA buffers pool initialization function
+ */
+extern void init_sdma_pool(void);
+
+/*!
+ * Flags are save and restored during interrupt handler
+ */
+unsigned long flags;
+
+struct clk *mxc_sdma_ahb_clk, *mxc_sdma_ipg_clk;
+
+/*!
+ * Structure containing sdma channels information.
+ */
+typedef struct {
+ /*! Channel number */
+ int channel;
+ /*! Channel usage name */
+ int in_use;
+ /*! Name of device using the channel */
+ char devicename[MAX_DEVNAME_LENGTH];
+ /*! Transfer type. Needed for setting SDMA script */
+ sdma_transferT transfer_type;
+ /*! Peripheral type. Needed for setting SDMA script */
+ sdma_periphT peripheral_type;
+ /*! Watermark level of device's fifo */
+ __u32 watermark_level;
+ /*! Peripheral event id */
+ int event_id;
+ /*! Peripheral event id2 (for channels that use 2 events) */
+ int event_id2;
+ /*! Running status (boolean) */
+ int running;
+ /*! buffer descriptors number */
+ int bd_number;
+ /*! callback function */
+ dma_callback_t callback;
+ /*! callback argument */
+ void *arg;
+ /*! SDMA data access word size */
+ unsigned long word_size:8;
+ /*! channel descriptor pointer */
+ channelDescriptor *cd;
+} sdma_struct;
+
+/*!
+ * Used to save the status of channels.
+ */
+static sdma_struct sdma_data[MAX_DMA_CHANNELS];
+
+/*!
+ * Stores the start address of the SDMA scripts
+ */
+static sdma_script_start_addrs sdma_script_addrs;
+
+extern void mxc_sdma_get_script_info(sdma_script_start_addrs * sdma_script_add);
+
+/*!
+ * Init sleep mutex of the channel
+ *
+ * @param channel channel number
+ */
+static void sdma_init_sleep(int channel)
+{
+ init_waitqueue_head(&sdma_sleep_queue[channel]);
+}
+
+/*!
+ * Puts channel to sleep
+ *
+ * @param channel channel number
+ */
+static void sdma_sleep_channel(int channel)
+{
+ while ((iapi_SDMAIntr & (1 << channel)) == 0) {
+ wait_event_interruptible(sdma_sleep_queue[channel],
+ ((iapi_SDMAIntr & (1 << channel)) !=
+ 0));
+ }
+}
+
+/*!
+ * Wake up channel from sleep
+ *
+ * @param channel channel number
+ */
+static void sdma_wakeup_channel(int channel)
+{
+ wake_up_interruptible(&sdma_sleep_queue[channel]);
+}
+
+/*!
+ * Sdma interrupt handler routine.
+ * Calls channels callback function
+ *
+ * @param irq the interrupt number
+ * @param dev_id driver private data
+ * @return the function returns \b IRQ_RETVAL(1) - interrupt was handled
+ */
+static irqreturn_t sdma_int_handler(int irq, void *dev_id)
+{
+ IRQ_Handler();
+ return IRQ_RETVAL(1);
+}
+
+/*!
+ * I.API channel callback function
+ *
+ * @param cd channel descriptor structure
+ * @param channel_data SDMA struct of the current channel
+ */
+static void iapi_interrupt_callback(channelDescriptor * cd,
+ sdma_struct * channel_data)
+{
+ int channel;
+ dma_callback_t callback;
+ void *arg;
+
+ channel = channel_data->channel;
+
+ channel_data->running = 0;
+
+ arg = channel_data->arg;
+
+ if (arg == 0) {
+ arg = (void *)&channel;
+ }
+
+ callback = channel_data->callback;
+
+ if (callback != 0) {
+ callback(arg);
+ }
+}
+
+/*!
+ * Returns pc of SDMA script according to peripheral and transfer type
+ *
+ * @param peripheral_type peripheral type
+ * @param transfer_type transfer type
+ *
+ * @return PC of SDMA script
+*/
+static unsigned short sdma_get_pc(sdma_periphT peripheral_type,
+ sdma_transferT transfer_type)
+{
+ int res = 0;
+
+ if (peripheral_type == MEMORY) {
+ switch (transfer_type) {
+ case emi_2_int:
+ res = sdma_script_addrs.mxc_sdma_ap_2_ap_addr;
+ break;
+ case emi_2_emi:
+ res = sdma_script_addrs.mxc_sdma_ap_2_ap_addr;
+ break;
+ case int_2_emi:
+ res = sdma_script_addrs.mxc_sdma_ap_2_ap_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == DSP) {
+ switch (transfer_type) {
+ case emi_2_dsp:
+ res = sdma_script_addrs.mxc_sdma_ap_2_bp_addr;
+ break;
+ case dsp_2_emi:
+ res = sdma_script_addrs.mxc_sdma_bp_2_ap_addr;
+ break;
+ case dsp_2_emi_loop:
+ res =
+ sdma_script_addrs.
+ mxc_sdma_loopback_on_dsp_side_addr;
+ break;
+ case emi_2_dsp_loop:
+ res =
+ sdma_script_addrs.mxc_sdma_mcu_interrupt_only_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == FIRI) {
+ switch (transfer_type) {
+ case per_2_int:
+ res = sdma_script_addrs.mxc_sdma_firi_2_per_addr;
+ break;
+ case per_2_emi:
+ res = sdma_script_addrs.mxc_sdma_firi_2_mcu_addr;
+ break;
+ case int_2_per:
+ res = sdma_script_addrs.mxc_sdma_per_2_firi_addr;
+ break;
+ case emi_2_per:
+ res = sdma_script_addrs.mxc_sdma_mcu_2_firi_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == UART) {
+ switch (transfer_type) {
+ case per_2_int:
+ res = sdma_script_addrs.mxc_sdma_uart_2_per_addr;
+ break;
+ case per_2_emi:
+ res = sdma_script_addrs.mxc_sdma_uart_2_mcu_addr;
+ break;
+ case int_2_per:
+ res = sdma_script_addrs.mxc_sdma_per_2_app_addr;
+ break;
+ case emi_2_per:
+ res = sdma_script_addrs.mxc_sdma_mcu_2_app_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == UART_SP) {
+ switch (transfer_type) {
+ case per_2_int:
+ res = sdma_script_addrs.mxc_sdma_uartsh_2_per_addr;
+ break;
+ case per_2_emi:
+ res = sdma_script_addrs.mxc_sdma_uartsh_2_mcu_addr;
+ break;
+ case int_2_per:
+ res = sdma_script_addrs.mxc_sdma_per_2_shp_addr;
+ break;
+ case emi_2_per:
+ res = sdma_script_addrs.mxc_sdma_mcu_2_shp_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == ATA) {
+ switch (transfer_type) {
+ case per_2_emi:
+ res = sdma_script_addrs.mxc_sdma_ata_2_mcu_addr;
+ break;
+ case emi_2_per:
+ res = sdma_script_addrs.mxc_sdma_mcu_2_ata_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == CSPI || peripheral_type == EXT ||
+ peripheral_type == SSI) {
+ switch (transfer_type) {
+ case per_2_int:
+ res = sdma_script_addrs.mxc_sdma_app_2_per_addr;
+ break;
+ case per_2_emi:
+ res = sdma_script_addrs.mxc_sdma_app_2_mcu_addr;
+ break;
+ case int_2_per:
+ res = sdma_script_addrs.mxc_sdma_per_2_app_addr;
+ break;
+ case emi_2_per:
+ res = sdma_script_addrs.mxc_sdma_mcu_2_app_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == SSI_SP || peripheral_type == MMC ||
+ peripheral_type == SDHC || peripheral_type == CSPI_SP ||
+ peripheral_type == ESAI || peripheral_type == MSHC_SP) {
+ switch (transfer_type) {
+ case per_2_int:
+ res = sdma_script_addrs.mxc_sdma_shp_2_per_addr;
+ break;
+ case per_2_emi:
+ res = sdma_script_addrs.mxc_sdma_shp_2_mcu_addr;
+ break;
+ case int_2_per:
+ res = sdma_script_addrs.mxc_sdma_per_2_shp_addr;
+ break;
+ case emi_2_per:
+ res = sdma_script_addrs.mxc_sdma_mcu_2_shp_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == ASRC) {
+ switch (transfer_type) {
+ case per_2_emi:
+ res = sdma_script_addrs.mxc_sdma_asrc_2_mcu_addr;
+ break;
+ case emi_2_per:
+ res = sdma_script_addrs.mxc_sdma_asrc_2_mcu_addr;
+ break;
+ case per_2_per:
+ res = sdma_script_addrs.mxc_sdma_per_2_per_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == MSHC) {
+ switch (transfer_type) {
+ case per_2_emi:
+ res = sdma_script_addrs.mxc_sdma_mshc_2_mcu_addr;
+ break;
+ case emi_2_per:
+ res = sdma_script_addrs.mxc_sdma_mcu_2_mshc_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == CCM) {
+ switch (transfer_type) {
+ case per_2_emi:
+ res = sdma_script_addrs.mxc_sdma_dptc_dvfs_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == FIFO_MEMORY) {
+ res = sdma_script_addrs.mxc_sdma_ap_2_ap_fixed_addr;
+ } else if (peripheral_type == SPDIF) {
+ switch (transfer_type) {
+ case per_2_emi:
+ res = sdma_script_addrs.mxc_sdma_spdif_2_mcu_addr;
+ break;
+ case emi_2_per:
+ res = sdma_script_addrs.mxc_sdma_mcu_2_spdif_addr;
+ break;
+ default:
+ res = -EINVAL;
+ }
+ } else if (peripheral_type == IPU_MEMORY) {
+ if (transfer_type == emi_2_per) {
+ res = sdma_script_addrs.mxc_sdma_ext_mem_2_ipu_addr;
+ } else {
+ res = -EINVAL;
+ }
+ }
+
+ if (res < 0) {
+ printk(KERN_ERR "SDMA script not found\n");
+ }
+
+ return res;
+
+}
+
+static inline int sdma_asrc_set_info(dma_channel_params *p,
+ script_data *pcontext, int eflags)
+{
+ dma_channel_ext_params *ep = (dma_channel_ext_params *) p;
+ unsigned int wml, tmp, wml1, wml2;
+ struct dma_channel_asrc_info *info = &(ep->info.asrc);
+ wml = 0;
+ if (p->transfer_type == per_2_per) {
+ if (!p->ext)
+ return wml;
+ wml1 = p->watermark_level;
+ wml2 = ep->watermark_level2;
+ if (info->channs) {
+ wml |= (info->channs & SDMA_ASRC_INFO_N_MASK) <<
+ SDMA_ASRC_INFO_N_OFF;
+ if (ep->p2p_dir)
+ wml2 *= info->channs & SDMA_ASRC_INFO_N_MASK;
+ else
+ wml1 *= info->channs & SDMA_ASRC_INFO_N_MASK;
+ }
+ if (info->channs & 1) {
+ if (ep->p2p_dir)
+ wml |= SDMA_ASRC_P2P_INFO_PS;
+ else
+ wml |= SDMA_ASRC_P2P_INFO_PA;
+ }
+ if (wml1 > wml2) {
+ tmp = wml2 & SDMA_ASRC_P2P_INFO_LWML_MASK;
+ wml |= tmp << SDMA_ASRC_P2P_INFO_LWML_OFF;
+ tmp = wml1 & SDMA_ASRC_P2P_INFO_HWML_MASK;
+ wml |= tmp << SDMA_ASRC_P2P_INFO_HWML_OFF;
+ if (eflags & (1 << 31))
+ wml |= SDMA_ASRC_P2P_INFO_LWE;
+ if (eflags & (1 << 30))
+ wml |= SDMA_ASRC_P2P_INFO_HWE;
+ } else {
+ tmp = wml1 & SDMA_ASRC_P2P_INFO_LWML_MASK;
+ wml |= tmp << SDMA_ASRC_P2P_INFO_LWML_OFF;
+ tmp = wml2 & SDMA_ASRC_P2P_INFO_HWML_MASK;
+ wml |= tmp << SDMA_ASRC_P2P_INFO_HWML_OFF;
+ wml |= eflags >> 2;
+ tmp = pcontext->event_mask2;
+ pcontext->event_mask2 = pcontext->event_mask1;
+ pcontext->event_mask1 = tmp;
+ }
+ } else {
+ if (p->ext && info->channs) {
+ wml |= (info->channs & SDMA_ASRC_INFO_N_MASK) <<
+ SDMA_ASRC_INFO_N_OFF;
+ tmp = (info->channs * p->watermark_level) &
+ SDMA_ASRC_INFO_WML_MASK;
+ wml |= tmp << SDMA_ASRC_INFO_WML_OFF;
+ } else {
+ tmp = (p->watermark_level & SDMA_ASRC_INFO_WML_MASK);
+ wml |= tmp << SDMA_ASRC_INFO_WML_OFF;
+ }
+
+ if (p->transfer_type == per_2_emi)
+ wml |= SDMA_ASRC_INFO_TXFR_DIR;
+
+ if (p->ext && (info->channs & 1)) {
+ if (p->transfer_type == per_2_emi)
+ wml |= SDMA_ASRC_INFO_PS;
+ else
+ wml |= SDMA_ASRC_INFO_PA;
+ }
+ wml |= eflags;
+ }
+ return wml;
+}
+
+/*!
+ * Downloads channel context according to channel parameters
+ *
+ * @param channel channel number
+ * @param p channel parameters
+ */
+static int sdma_load_context(int channel, dma_channel_params * p)
+{
+ script_data context;
+ int res;
+ int event1_greater_than_32;
+ int event2_greater_than_32;
+ dma_channel_ext_params *ep = (dma_channel_ext_params *) p;
+
+ res = 0;
+
+ memset(&context, 0, sizeof(script_data));
+ context.load_address = sdma_get_pc(p->peripheral_type,
+ p->transfer_type);
+
+ if (context.load_address > 0) {
+ if ((p->peripheral_type != MEMORY)
+ && (p->peripheral_type != DSP)) {
+ /* Handle multiple event channels differently */
+ if (p->event_id2) {
+ if (p->event_id2 < 32) {
+ context.event_mask2 =
+ 0x1 << p->event_id2;
+ event2_greater_than_32 = 0;
+ } else {
+ context.event_mask2 =
+ 0x1 << (p->event_id2 - 32);
+ event2_greater_than_32 = 1 << 31;
+ }
+ if (p->event_id < 32) {
+ context.event_mask1 =
+ 0x1 << p->event_id;
+ event1_greater_than_32 = 0;
+ } else {
+ context.event_mask1 =
+ 0x1 << (p->event_id - 32);
+ event1_greater_than_32 = 1 << 30;
+ }
+ } else {
+ event1_greater_than_32 = 0;
+ event2_greater_than_32 = 0;
+ if (p->event_id < 32) {
+ context.event_mask1 =
+ 0x1 << p->event_id;
+ context.event_mask2 = 0;
+ } else {
+ context.event_mask1 = 0;
+ context.event_mask2 =
+ 0x1 << (p->event_id - 32);
+ }
+ }
+
+ if (p->ext)
+ context.wml = ep->info_bits;
+ /* Watermark Level */
+ if (p->peripheral_type == ASRC) {
+ context.wml |= sdma_asrc_set_info(p,
+ &context,
+ event2_greater_than_32
+ |
+ event1_greater_than_32);
+ } else
+ context.wml |= event2_greater_than_32 |
+ event1_greater_than_32 | p->watermark_level;
+
+ /* Address */
+ context.shp_addr = (unsigned long)(p->per_address);
+ if (p->ext)
+ context.per_addr = ep->per_address2;
+ iapi_IoCtl(sdma_data[channel].cd,
+ IAPI_CHANGE_PERIPHADDR, p->per_address);
+ } else {
+ context.wml = M3_BASE_ADDRESS;
+ }
+
+ sdma_data[channel].transfer_type = p->transfer_type;
+ sdma_data[channel].peripheral_type = p->peripheral_type;
+ sdma_data[channel].watermark_level = p->watermark_level;
+ iapi_AssignScript(sdma_data[channel].cd, &context);
+ } else {
+ res = context.load_address;
+ }
+
+ return res;
+}
+
+/*!
+ * Setup channel according to parameters. Must be called once after mxc_request_dma()
+ *
+ * @param channel channel number
+ * @param p channel parameters pointer
+ * @return 0 on success, error code on fail
+ */
+int mxc_dma_setup_channel(int channel, dma_channel_params * p)
+{
+ int err = 0;
+ int i;
+
+ mxc_dma_stop(channel);
+
+ for (i = 0; i < sdma_data[channel].bd_number; i++) {
+ iapi_IoCtl(sdma_data[channel].cd,
+ (i << BD_NUM_OFFSET) |
+ IAPI_CHANGE_SET_STATUS, (unsigned long)0);
+ }
+
+ sdma_data[channel].bd_number = (p->bd_number <= 0) ? 1 : p->bd_number;
+
+ sdma_data[channel].word_size = p->word_size;
+
+ sdma_data[channel].event_id = p->event_id;
+ sdma_data[channel].event_id2 = p->event_id2;
+
+ sdma_data[channel].callback = p->callback;
+
+ sdma_data[channel].arg = p->arg;
+
+ err = iapi_IoCtl(sdma_data[channel].cd,
+ IAPI_CHANGE_BDNUM, sdma_data[channel].bd_number);
+
+ if (err < 0) {
+ printk(KERN_ERR "Failed allocating buffer \
+descriptors (0x%x)\n", err);
+ err = -ENOMEM;
+ goto setup_channel_fail;
+ }
+
+ if (channel != 0) {
+ switch (p->transfer_type) {
+ case dsp_2_per:
+ break;
+ case emi_2_per:
+ case int_2_per:
+ case per_2_int:
+ case per_2_emi:
+ case per_2_per:
+ /*
+ * Peripheral <------> Memory
+ * evtOvr = 0 dspOvr = 1
+ */
+ iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_OWNERSHIP,
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) |
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) |
+ (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP));
+ if (p->event_id) {
+ err = iapi_SetChannelEventMapping(p->event_id,
+ 0x1 <<
+ channel);
+ }
+ if (!err && p->event_id2) {
+ err = iapi_SetChannelEventMapping(p->event_id2,
+ 0x1 <<
+ channel);
+ }
+ break;
+ case emi_2_dsp:
+ case int_2_dsp:
+ case dsp_2_int:
+ case dsp_2_emi:
+ case dsp_2_dsp:
+ /*
+ * DSP <-----------> Memory
+ * evtOvr = 1 dspOvr = 0
+ */
+ iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_OWNERSHIP,
+ (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) |
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) |
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP));
+ break;
+ case emi_2_int:
+ case emi_2_emi:
+ case int_2_int:
+ case int_2_emi:
+ case emi_2_dsp_loop:
+ case dsp_2_emi_loop:
+ /* evtOvr = 1 dspOvr = 1 */
+ iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_OWNERSHIP,
+ (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) |
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) |
+ (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP));
+ break;
+ case per_2_dsp:
+ /* evtOvr = 0 dspOvr = 0 */
+ iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_OWNERSHIP,
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) |
+ (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) |
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP));
+ err = iapi_SetChannelEventMapping(p->event_id,
+ 0x1 << channel);
+ break;
+ default:
+ break;
+ printk(KERN_ERR "Wrong SDMA transfer type\n");
+ err = -EINVAL;
+ }
+ if (err == 0) {
+ err = sdma_load_context(channel, p);
+ iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_PRIORITY,
+ MXC_SDMA_DEFAULT_PRIORITY);
+ }
+ }
+ setup_channel_fail:
+ return err;
+}
+
+/*!
+ * Setup the channel priority. This can be used to change the default priority
+ * for the channel.
+ *
+ * @param channel channel number
+ * @param priority priority to be set for the channel
+ *
+ * @return 0 on success, error code on failure
+ */
+int mxc_dma_set_channel_priority(unsigned int channel, unsigned int priority)
+{
+ if (priority < MXC_SDMA_MIN_PRIORITY
+ || priority > MXC_SDMA_MAX_PRIORITY) {
+ return -EINVAL;
+ }
+ return iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_PRIORITY,
+ priority);
+}
+
+/*!
+ * Allocates dma channel.
+ * If channel's value is 0, then the function allocates a free channel
+ * dynamically and sets its value to channel.
+ * Else allocates requested channel if it is free.
+ * If the channel is busy or no free channels (in dynamic allocation) -EBUSY returned.
+ *
+ * @param channel pointer to channel number
+ * @param devicename device name
+ * @return 0 on success, error code on fail
+ */
+int mxc_request_dma(int *channel, const char *devicename)
+{
+ int i, res;
+
+ res = 0;
+
+ down(&sdma_status_mutex);
+
+ /* Dynamic allocation */
+ if (*channel == 0) {
+ for (i = MAX_DMA_CHANNELS - 1; i > 0; i--) {
+#ifdef CONFIG_SDMA_IRAM
+ /*TODO:It will be removed after DPTC used UDMA interface */
+ if (i >= MXC_DMA_CHANNEL_IRAM)
+ continue;
+#endif /*CONFIG_SDMA_IRAM */
+ if (!sdma_data[i].in_use) {
+ *channel = i;
+ break;
+ }
+ }
+ }
+
+ if (*channel > 0 && *channel < MAX_DMA_CHANNELS &&
+ sdma_data[*channel].in_use == 0) {
+ res = iapi_Open(sdma_data[0].cd, *channel);
+
+ if (res < 0) {
+ printk(KERN_ERR "Failed iapi_Open channel %d, 0x%x\n",
+ *channel, res);
+ } else {
+ sdma_data[*channel].in_use = 1;
+ strcpy(sdma_data[*channel].devicename, devicename);
+ sdma_data[*channel].cd = CHAD(*channel);
+
+ iapi_IoCtl(sdma_data[*channel].cd, IAPI_CHANGE_SYNCH,
+ CALLBACK_ISR);
+ iapi_IoCtl(sdma_data[*channel].cd,
+ IAPI_CHANGE_CALLBACKFUNC,
+ (unsigned long)iapi_interrupt_callback);
+ iapi_IoCtl(sdma_data[*channel].cd,
+ IAPI_CHANGE_USER_ARG,
+ (unsigned long)&(sdma_data[*channel]));
+ }
+ } else {
+ res = -EBUSY;
+ }
+
+ up(&sdma_status_mutex);
+
+ return res;
+}
+
+/*!
+ * Configures request parameters. Can be called multiple times after
+ * mxc_request_dma() and mxc_dma_setup_channel().
+ *
+ *
+ * @param channel channel number
+ * @param p request parameters pointer
+ * @param bd_index index of buffer descriptor to set
+ * @return 0 on success, error code on fail
+ */
+int mxc_dma_set_config(int channel, dma_request_t * p, int bd_index)
+{
+ unsigned char param;
+
+ if (!sdma_data[channel].in_use) {
+ return -EINVAL;
+ }
+
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_SET_TRANSFER_CD, sdma_data[channel].word_size);
+
+ param = BD_DONE | BD_INTR | BD_EXTD;
+
+ if (sdma_data[channel].bd_number > 1 && p->bd_cont == 1) {
+ param |= BD_CONT;
+ }
+
+ if (bd_index == sdma_data[channel].bd_number - 1) {
+ param |= BD_WRAP;
+ }
+
+ switch (sdma_data[channel].transfer_type) {
+ case emi_2_per:
+ case dsp_2_per:
+ case int_2_per:
+ case emi_2_dsp:
+ case int_2_dsp:
+ case emi_2_dsp_loop:
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_SET_BUFFERADDR,
+ (unsigned long)p->sourceAddr);
+ break;
+ case per_2_int:
+ case per_2_emi:
+ case per_2_dsp:
+ case dsp_2_int:
+ case dsp_2_emi:
+ case dsp_2_dsp:
+ case dsp_2_emi_loop:
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_SET_BUFFERADDR,
+ (unsigned long)p->destAddr);
+ break;
+ case emi_2_int:
+ case emi_2_emi:
+ case int_2_int:
+ case int_2_emi:
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_SET_BUFFERADDR,
+ (unsigned long)p->sourceAddr);
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_SET_EXTDBUFFERADDR,
+ (unsigned long)p->destAddr);
+ break;
+ default:
+ break;
+ }
+
+ /* Change the endianness for DSP to MCU Data transfers */
+ if (sdma_data[channel].transfer_type == dsp_2_emi ||
+ sdma_data[channel].transfer_type == emi_2_dsp) {
+ iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_SET_ENDIANNESS,
+ SET_BIT_ALL);
+ }
+
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_SET_COUNT, p->count);
+
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) | IAPI_CHANGE_SET_STATUS, param);
+
+ return 0;
+}
+
+/*!
+ * Configures the BD_INTR bit on a buffer descriptor parameters.
+ *
+ *
+ * @param channel channel number
+ * @param bd_index index of buffer descriptor to set
+ * @param bd_intr flag to set or clear the BD_INTR bit
+ * @return 0 on success, error code on fail
+ */
+void mxc_dma_set_bd_intr(int channel, int bd_index, int bd_intr)
+{
+ unsigned long param;
+
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_GET_STATUS, (unsigned long)&param);
+
+ if (bd_intr) {
+ param |= BD_INTR;
+ } else {
+ param &= ~BD_INTR;
+ }
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) | IAPI_CHANGE_SET_STATUS, param);
+
+}
+
+/*!
+ * Gets the BD_INTR bit on a buffer descriptor.
+ *
+ *
+ * @param channel channel number
+ * @param bd_index index of buffer descriptor to set
+ *
+ * @return returns the BD_INTR bit status
+ */
+int mxc_dma_get_bd_intr(int channel, int bd_index)
+{
+ unsigned long bd_status = 0;
+
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_GET_STATUS, (unsigned long)&bd_status);
+
+ return (bd_status & BD_INTR);
+}
+
+/*!
+ * Stop the current transfer
+ *
+ * @param channel channel number
+ * @param buffer_number number of buffers (beginning with 0),
+ * whose done bits should be reset to 0
+ */
+int mxc_dma_reset(int channel, int buffer_number)
+{
+ unsigned char param = 0;
+ int i = 0;
+
+ if (!sdma_data[channel].in_use) {
+ return -EINVAL;
+ }
+
+ /* clear the BD_DONE bits for all the necessary buffers */
+ for (i = 0; i < buffer_number; i++) {
+
+ iapi_IoCtl(sdma_data[channel].cd, (i << BD_NUM_OFFSET) |
+ IAPI_CHANGE_GET_STATUS, (unsigned long)&param);
+
+ /* clear the BD_DONE bit of the buffer */
+ param = param & (~BD_DONE);
+
+ iapi_IoCtl(sdma_data[channel].cd, (i << BD_NUM_OFFSET) |
+ IAPI_CHANGE_SET_STATUS, param);
+ }
+
+ return 0;
+}
+
+/*!
+ * Returns request parameters.
+ *
+ * @param channel channel number
+ * @param p request parameters pointer
+ * @param bd_index index of buffer descriptor to get
+ * @return 0 on success, error code on fail
+ */
+int mxc_dma_get_config(int channel, dma_request_t * p, int bd_index)
+{
+ int err = 0;
+ unsigned long bd_status;
+ unsigned long bd_count;
+ __u8 *sourceAddr;
+ __u8 *destAddr;
+
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_GET_STATUS, (unsigned long)&bd_status);
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_GET_COUNT, (unsigned long)&bd_count);
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_GET_BUFFERADDR, (unsigned long)&sourceAddr);
+
+ switch (sdma_data[channel].transfer_type) {
+ case emi_2_per:
+ case dsp_2_per:
+ case int_2_per:
+ case emi_2_dsp:
+ case int_2_dsp:
+ case emi_2_dsp_loop:
+ p->sourceAddr = sourceAddr;
+ break;
+ case per_2_int:
+ case per_2_emi:
+ case per_2_dsp:
+ case dsp_2_int:
+ case dsp_2_emi:
+ case dsp_2_dsp:
+ case dsp_2_emi_loop:
+ p->destAddr = sourceAddr;
+ break;
+ case emi_2_int:
+ case emi_2_emi:
+ case int_2_int:
+ case int_2_emi:
+ p->sourceAddr = sourceAddr;
+ iapi_IoCtl(sdma_data[channel].cd,
+ (bd_index << BD_NUM_OFFSET) |
+ IAPI_CHANGE_GET_EXTDBUFFERADDR,
+ (unsigned long)&destAddr);
+ p->destAddr = destAddr;
+ break;
+ default:
+ break;
+ }
+
+ p->count = bd_count;
+ p->bd_done = bd_status & BD_DONE;
+ p->bd_cont = bd_status & BD_CONT;
+ p->bd_error = bd_status & BD_RROR;
+
+ return err;
+}
+
+/*!
+ * This function is used by MXC IPC's write_ex2. It passes the pointer to the
+ * data control structure to iapi_write_ipcv2()
+ *
+ * @param channel SDMA channel number
+ * @param ctrl_ptr Data Control structure pointer
+ */
+int mxc_sdma_write_ipcv2(int channel, void *ctrl_ptr)
+{
+ return iapi_Write_ipcv2(sdma_data[channel].cd, ctrl_ptr);
+}
+
+/*!
+ * This function is used by MXC IPC's read_ex2. It passes the pointer to the
+ * data control structure to iapi_read_ipcv2()
+ *
+ * @param channel SDMA channel number
+ * @param ctrl_ptr Data Control structure pointer
+ */
+int mxc_sdma_read_ipcv2(int channel, void *ctrl_ptr)
+{
+ return iapi_Read_ipcv2(sdma_data[channel].cd, ctrl_ptr);
+}
+
+/*!
+ * Starts dma channel.
+ *
+ * @param channel channel number
+ */
+int mxc_dma_start(int channel)
+{
+ if (sdma_data[channel].running == 0) {
+ sdma_data[channel].running = 1;
+ iapi_StartChannel(channel);
+ }
+
+ return 0;
+}
+
+/*!
+ * Stops dma channel.
+ *
+ * @param channel channel number
+ */
+int mxc_dma_stop(int channel)
+{
+ iapi_StopChannel(channel);
+ sdma_data[channel].running = 0;
+
+ return 0;
+}
+
+/*!
+ * Frees dma channel.
+ *
+ * @param channel channel number
+ */
+void mxc_free_dma(int channel)
+{
+ int i;
+
+ mxc_dma_stop(channel);
+
+ if (sdma_data[channel].event_id != 0) {
+ iapi_SetChannelEventMapping(sdma_data[channel].event_id, 0x0);
+ }
+ if (sdma_data[channel].event_id2 != 0) {
+ iapi_SetChannelEventMapping(sdma_data[channel].event_id2, 0x0);
+ }
+
+ sdma_data[channel].event_id = 0;
+
+ iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_PRIORITY, 0x0);
+ iapi_IoCtl(sdma_data[channel].cd, IAPI_CHANGE_OWNERSHIP,
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) |
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) |
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP));
+
+ for (i = 0; i < sdma_data[channel].bd_number; i++) {
+ iapi_IoCtl(sdma_data[channel].cd,
+ (i << BD_NUM_OFFSET) |
+ IAPI_CHANGE_SET_STATUS, (unsigned long)0);
+ }
+
+ iapi_Close(sdma_data[channel].cd);
+
+ strcpy(sdma_data[channel].devicename, "not used");
+
+ sdma_data[channel].in_use = 0;
+}
+
+/*!
+ * Initializes channel's priorities
+ *
+ */
+static void __init init_priorities(void)
+{
+ iapi_IoCtl(sdma_data[0].cd, IAPI_CHANGE_PRIORITY, 0x7);
+}
+
+/*!
+ * Initializes events table
+ */
+static void __init init_event_table(void)
+{
+ int channel;
+
+ for (channel = 0; channel < MAX_DMA_CHANNELS; channel++) {
+ iapi_SetChannelEventMapping(channel, 0);
+ }
+}
+
+/*!
+ * Sets callback function. Used with standard dma api
+ * for supporting interrupts
+ *
+ * @param channel channel number
+ * @param callback callback function pointer
+ * @param arg argument for callback function
+ */
+void mxc_dma_set_callback(int channel, dma_callback_t callback, void *arg)
+{
+ sdma_data[channel].callback = callback;
+ sdma_data[channel].arg = arg;
+}
+
+/*!
+ * Synchronization function used by I.API
+ *
+ * @param channel channel number
+ */
+static int getChannel(int channel)
+{
+ if (irqs_disabled() || in_atomic()) {
+ if (down_trylock(&sdma_synch_mutex[channel])) {
+ return -EBUSY;
+ }
+ } else {
+ if (down_interruptible(&sdma_synch_mutex[channel])) {
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * Synchronization function used by I.API
+ *
+ * @param channel channel number
+ */
+static int releaseChannel(int channel)
+{
+ up(&sdma_synch_mutex[channel]);
+ return 0;
+}
+
+/*!
+ * Unmask interrupt function. Used by I.API
+ *
+ */
+static void unmask_sdma_interrupt(void)
+{
+ /* Commented out tp take care of the PREEMPT_RT option
+ * local_irq_restore(flags);
+ */
+}
+
+/*!
+ * Mask interrupt function. Used by I.API
+ *
+ */
+static void mask_sdma_interrupt(void)
+{
+ /* Commented to take of the PREEMPT_RT option
+ * local_irq_save(flags);
+ */
+}
+
+/*!
+ * Initializes I.API
+ */
+static void __init init_iapi_struct(void)
+{
+ channelDescriptor *cd;
+
+ printk(KERN_INFO "Using SDMA I.API\n");
+
+ iapi_Malloc = &sdma_malloc;
+#ifdef CONFIG_SDMA_IRAM
+ iapi_iram_Malloc = &sdma_iram_malloc;
+#endif /*CONFIG_SDMA_IRAM */
+
+ iapi_Free = &sdma_free;
+ iapi_Virt2Phys = (void *(*)(void *))&sdma_virt_to_phys;
+ iapi_Phys2Virt = (void *(*)(void *))&sdma_phys_to_virt;
+ iapi_memset = &memset;
+ iapi_memcpy = &memcpy;
+
+ iapi_GotoSleep = &sdma_sleep_channel;
+ iapi_WakeUp = &sdma_wakeup_channel;
+ iapi_InitSleep = &sdma_init_sleep;
+ iapi_ReleaseChannel = &releaseChannel;
+ iapi_GetChannel = &getChannel;
+
+ iapi_EnableInterrupts = &unmask_sdma_interrupt;
+ iapi_DisableInterrupts = &mask_sdma_interrupt;
+
+ cd = kmalloc(sizeof(channelDescriptor), GFP_KERNEL);
+
+ memset(cd, 0, sizeof(channelDescriptor));
+
+ sdma_data[0].cd = cd;
+}
+
+/*!
+ * Initializes channel synchronization mutexes
+ */
+static void __init init_mutexes(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_DMA_CHANNELS; i++) {
+ init_MUTEX(&sdma_synch_mutex[i]);
+ }
+
+ init_MUTEX(&sdma_status_mutex);
+}
+
+/*!
+ * Channels status read proc file system function
+ *
+ * @param buf pointer to the buffer the data shuld be written to.
+ * @param start pointer to the pointer where the new data is
+ * written to.
+ * procedure should update the start pointer to point to
+ * where in the buffer the data was written.
+ * @param offset offset from start of the file
+ * @param count number of bytes to read.
+ * @param eof pointer to eof flag. sould be set to 1 when
+ * reaching eof.
+ * @param data driver specific data pointer.
+ *
+ * @return number byte read from the log buffer.
+ */
+static int proc_read_channels(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ char *log;
+ char *log_ptr;
+ char tmp[48];
+ int i;
+
+ log = kmalloc(4096, GFP_KERNEL);
+ memset(log, 0, 4096);
+ log_ptr = log;
+
+ for (i = 0; i < MAX_DMA_CHANNELS; i++) {
+ if (sdma_data[i].in_use == 0) {
+ continue;
+ }
+
+ memset(tmp, 0, 48);
+ sprintf(tmp, "Channel %d: %s\n", i, sdma_data[i].devicename);
+
+ strcpy(log_ptr, tmp);
+ log_ptr += strlen(tmp);
+ }
+
+ if (offset > strlen(log)) {
+ *eof = 1;
+ count = 0;
+ } else {
+ if (offset + count > strlen(log)) {
+ count = strlen(log) - offset;
+ *eof = 1;
+ } else {
+ *eof = 0;
+ }
+
+ memcpy(buf, log, count);
+ *start = buf;
+ kfree(log);
+ }
+
+ return count;
+}
+
+/*!
+ * SDMA proc file system read function
+ */
+static int __init init_proc_fs(void)
+{
+ struct proc_dir_entry *sdma_proc_dir;
+ int res;
+
+ res = 0;
+
+ sdma_proc_dir = proc_mkdir("sdma", NULL);
+ create_proc_read_entry("channels", 0, sdma_proc_dir,
+ proc_read_channels, NULL);
+
+ if (res < 0) {
+ printk(KERN_WARNING "Failed create SDMA proc entry\n");
+ }
+
+ return res;
+}
+
+/*!
+ * Initializes SDMA private data
+ */
+static void __init init_sdma_data(void)
+{
+ int i;
+
+ memset(sdma_data, 0, sizeof(sdma_struct) * MAX_DMA_CHANNELS);
+ sdma_data[0].in_use = 1;
+ strcpy(sdma_data[0].devicename, "MCU");
+
+ for (i = 0; i < MAX_DMA_CHANNELS; i++) {
+ sdma_data[i].channel = i;
+ }
+}
+
+#if defined(CONFIG_MXC_SUPER_GEM)
+/*!
+ * Initialize the Super GEM SDMA channel
+ *
+ * @return returns -1 on error, 0 on success.
+ */
+static int __init init_super_gem(void)
+{
+ channelDescriptor *cd;
+ script_data context;
+ int res = 0;
+
+ res = iapi_Open(sdma_data[0].cd, MXC_DMA_CHANNEL_GEM);
+ if (res < 0) {
+ return -1;
+ }
+ sdma_data[MXC_DMA_CHANNEL_GEM].in_use = 1;
+ cd = CHAD(MXC_DMA_CHANNEL_GEM);
+ memset(&context, 0, sizeof(script_data));
+ context.load_address = sdma_script_addrs.mxc_sdma_utra_addr;
+ context.wml = M3_BASE_ADDRESS;
+ res = iapi_AssignScript(cd, &context);
+ if (res < 0) {
+ iapi_Close(cd);
+ sdma_data[MXC_DMA_CHANNEL_GEM].in_use = 0;
+ return -1;
+ }
+ res =
+ iapi_IoCtl(cd, IAPI_CHANGE_OWNERSHIP,
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_EVT) |
+ (DONT_OWN_CHANNEL << CH_OWNSHP_OFFSET_MCU) |
+ (OWN_CHANNEL << CH_OWNSHP_OFFSET_DSP));
+ if (res < 0) {
+ iapi_Close(cd);
+ sdma_data[MXC_DMA_CHANNEL_GEM].in_use = 0;
+ return -1;
+ }
+ /* Set EP=1, which is required to start SuperGem script the first time */
+ /* This can be done only on the AP side */
+ SDMA_H_EVTPEND |= 1 << MXC_DMA_CHANNEL_GEM;
+
+ res =
+ iapi_SetChannelEventMapping(DMA_REQ_GEM, 1 << MXC_DMA_CHANNEL_GEM);
+ if (res < 0) {
+ iapi_Close(cd);
+ sdma_data[MXC_DMA_CHANNEL_GEM].in_use = 0;
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+/*!
+ * Initializes dma
+ */
+int __init sdma_init(void)
+{
+ int res = 0;
+ configs_data confreg_data;
+
+ /* Initialize to the default values */
+ confreg_data = iapi_ConfigDefaults;
+
+ confreg_data.dspdma = MXC_SDMA_DSPDMA;
+ /* Set ACR bit */
+ mxc_sdma_ahb_clk = clk_get(NULL, "sdma_ahb_clk");
+ mxc_sdma_ipg_clk = clk_get(NULL, "sdma_ipg_clk");
+ clk_enable(mxc_sdma_ahb_clk);
+ clk_enable(mxc_sdma_ipg_clk);
+ if (clk_get_rate(mxc_sdma_ahb_clk) / clk_get_rate(mxc_sdma_ipg_clk) < 2) {
+ printk(KERN_INFO "Setting SDMA ACR\n");
+ confreg_data.acr = 1;
+ }
+
+ init_sdma_data();
+
+ init_sdma_pool();
+
+ res = request_irq(MXC_INT_SDMA, sdma_int_handler, 0, "mxcsdma", 0);
+
+ if (res < 0) {
+ goto sdma_init_fail;
+ }
+
+ init_mutexes();
+
+ init_iapi_struct();
+
+ mxc_sdma_get_script_info(&sdma_script_addrs);
+
+ res = iapi_Init(sdma_data[0].cd, &confreg_data,
+ sdma_script_addrs.mxc_sdma_start_addr,
+ sdma_script_addrs.mxc_sdma_ram_code_size * 2,
+ sdma_script_addrs.mxc_sdma_ram_code_start_addr);
+
+ if (res < 0) {
+ free_irq(MXC_INT_SDMA, 0);
+ goto sdma_init_fail;
+ }
+
+ init_priorities();
+
+ init_event_table();
+
+#if defined(CONFIG_MXC_SUPER_GEM)
+ res = init_super_gem();
+ if (res < 0) {
+ free_irq(MXC_INT_SDMA, 0);
+ goto sdma_init_fail;
+ }
+#endif
+
+ init_proc_fs();
+
+ printk(KERN_INFO "MXC DMA API initialized\n");
+
+ clk_disable(mxc_sdma_ahb_clk);
+ clk_disable(mxc_sdma_ipg_clk);
+ return res;
+
+ sdma_init_fail:
+ printk(KERN_ERR "Error 0x%x in sdma_init\n", res);
+ clk_disable(mxc_sdma_ahb_clk);
+ clk_disable(mxc_sdma_ipg_clk);
+ return res;
+
+}
+
+arch_initcall(sdma_init);
+
+EXPORT_SYMBOL(mxc_request_dma);
+EXPORT_SYMBOL(mxc_free_dma);
+EXPORT_SYMBOL(mxc_dma_setup_channel);
+EXPORT_SYMBOL(mxc_dma_set_channel_priority);
+EXPORT_SYMBOL(mxc_dma_set_config);
+EXPORT_SYMBOL(mxc_dma_get_config);
+EXPORT_SYMBOL(mxc_dma_set_bd_intr);
+EXPORT_SYMBOL(mxc_dma_get_bd_intr);
+EXPORT_SYMBOL(mxc_dma_reset);
+EXPORT_SYMBOL(mxc_sdma_write_ipcv2);
+EXPORT_SYMBOL(mxc_sdma_read_ipcv2);
+EXPORT_SYMBOL(mxc_dma_start);
+EXPORT_SYMBOL(mxc_dma_stop);
+EXPORT_SYMBOL(sdma_malloc);
+EXPORT_SYMBOL(sdma_free);
+EXPORT_SYMBOL(mxc_dma_set_callback);
+EXPORT_SYMBOL(sdma_virt_to_phys);
+EXPORT_SYMBOL(sdma_phys_to_virt);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Linux SDMA API");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/sdma/sdma_malloc.c b/arch/arm/plat-mxc/sdma/sdma_malloc.c
new file mode 100644
index 000000000000..2b04ef6c3c08
--- /dev/null
+++ b/arch/arm/plat-mxc/sdma/sdma_malloc.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file plat-mxc/sdma/sdma_malloc.c
+ * @brief This file contains functions for SDMA non-cacheable buffers allocation
+ *
+ * SDMA (Smart DMA) is used for transferring data between MCU and peripherals
+ *
+ * @ingroup SDMA
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <asm/dma.h>
+#include <asm/mach/dma.h>
+#include <mach/hardware.h>
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+
+#define DEBUG 0
+
+#if DEBUG
+#define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#ifdef CONFIG_SDMA_IRAM
+#define IRAM_VIRT_BASE IRAM_BASE_ADDR_VIRT
+#define IRAM_PHYS_BASE IRAM_BASE_ADDR
+#if (CONFIG_SDMA_IRAM_SIZE&0x3FF)
+#error "IRAM size of SDMA should be multiple of 1Kbytes"
+#else
+#define IRAM_SDMA_SIZE CONFIG_SDMA_IRAM_SIZE /* 4K */
+#endif
+#define IRAM_UNIT_SIZE 512
+#define IRAM_POOL_SIZE (IRAM_SDMA_SIZE/IRAM_UNIT_SIZE)
+
+#define IS_IRAM_VIRT(x) (((x)<IRAM_VIRT_BASE)?0:\
+ (((x) - IRAM_VIRT_BASE)>IRAM_SDMA_SIZE)?0:1)
+
+#define IS_IRAM_PHYS(x) (((x)<IRAM_PHYS_BASE)?0:\
+ (((x) - IRAM_PHYS_BASE)>IRAM_SDMA_SIZE)?0:1)
+#endif /*CONFIG_SDMA_IRAM */
+
+/*!
+ * Defines SDMA non-cacheable buffers pool
+ */
+static struct dma_pool *pool;
+
+#ifdef CONFIG_SDMA_IRAM
+typedef struct iram_head_s {
+ struct list_head list;
+} iram_head_t;
+
+static spinlock_t iram_pool_lock = SPIN_LOCK_UNLOCKED;
+static struct list_head iram_free_list;
+static unsigned char iram_pool_flag[IRAM_POOL_SIZE];
+
+static void sdma_iram_free(void *buf);
+#endif /*CONFIG_SDMA_IRAM */
+
+/*!
+ * SDMA memory conversion hashing structure
+ */
+typedef struct {
+ struct list_head node;
+ int use_count;
+ /*! Virtual address */
+ void *virt;
+ /*! Physical address */
+ unsigned long phys;
+} virt_phys_struct;
+
+static struct list_head buf_map;
+
+/*!
+ * Defines the size of each buffer in SDMA pool.
+ * The size must be at least 512 bytes, because
+ * sdma channel control blocks array size is 512 bytes
+ */
+#define SDMA_POOL_SIZE 1024
+
+/*!
+ * Adds new buffer structure into conversion hash tables
+ *
+ * @param vf SDMA memory conversion hashing structure
+ *
+ * @return 1 on success, 0 on fail
+ */
+static int add_entry(virt_phys_struct * vf)
+{
+ virt_phys_struct *p;
+
+ vf->phys &= PAGE_MASK;
+ vf->virt = (void *)((u32) vf->virt & PAGE_MASK);
+
+ list_for_each_entry(p, &buf_map, node) {
+ if (p->virt == vf->virt) {
+ p->use_count++;
+ return 0;
+ }
+ }
+
+ p = kmalloc(sizeof(virt_phys_struct), GFP_KERNEL);
+ if (p == 0) {
+ return -ENOMEM;
+ }
+
+ *p = *vf;
+ p->use_count = 1;
+ list_add_tail(&p->node, &buf_map);
+
+ DPRINTK("added vaddr 0x%p, paddr 0x%08X to list\n", p->virt, p->phys);
+
+ return 0;
+}
+
+/*!
+ * Deletes buffer stracture from conversion hash tables
+ *
+ * @param buf SDMA memory buffer virtual addr
+ *
+ * @return 0 on success, -1 on fail
+ */
+static int delete_entry(void *buf)
+{
+ virt_phys_struct *p;
+
+ buf = (void *)((u32) buf & PAGE_MASK);
+
+ list_for_each_entry(p, &buf_map, node) {
+ if (p->virt == buf) {
+ p->use_count--;
+ break;
+ }
+ }
+
+ if (p->use_count == 0) {
+ list_del(&p->node);
+ kfree(p);
+ }
+
+ return 0;
+}
+
+/*!
+ * Virtual to physical address conversion functio
+ *
+ * @param buf pointer to virtual address
+ *
+ * @return physical address
+ */
+unsigned long sdma_virt_to_phys(void *buf)
+{
+ u32 offset = (u32) buf & (~PAGE_MASK);
+ virt_phys_struct *p;
+
+ DPRINTK("searching for vaddr 0x%p\n", buf);
+
+#ifdef CONFIG_SDMA_IRAM
+ if (IS_IRAM_VIRT((unsigned long)buf)) {
+ if ((unsigned long)buf & (IRAM_UNIT_SIZE - 1)) {
+ printk(KERN_WARNING "%s buffer offset = %ld\n",
+ __FUNCTION__, (unsigned long)buf);
+ }
+ return (unsigned long)buf + IRAM_PHYS_BASE - IRAM_VIRT_BASE;
+ }
+#endif /*CONFIG_SDMA_IRAM */
+
+ list_for_each_entry(p, &buf_map, node) {
+ if ((u32) p->virt == ((u32) buf & PAGE_MASK)) {
+ return p->phys | offset;
+ }
+ }
+
+ if (virt_addr_valid(buf)) {
+ return virt_to_phys(buf);
+ }
+
+ printk(KERN_WARNING
+ "SDMA malloc: could not translate virt address 0x%p\n", buf);
+ return 0;
+}
+
+/*!
+ * Physical to virtual address conversion functio
+ *
+ * @param buf pointer to physical address
+ *
+ * @return virtual address
+ */
+void *sdma_phys_to_virt(unsigned long buf)
+{
+ u32 offset = buf & (~PAGE_MASK);
+ virt_phys_struct *p;
+
+#ifdef CONFIG_SDMA_IRAM
+ if (IS_IRAM_PHYS((unsigned long)buf)) {
+ if (buf & (IRAM_UNIT_SIZE - 1)) {
+ printk(KERN_WARNING "%s buffer offset = %ld\n",
+ __FUNCTION__, (unsigned long)buf);
+ }
+ return (void *)buf + IRAM_VIRT_BASE - IRAM_PHYS_BASE;
+ }
+#endif /*CONFIG_SDMA_IRAM */
+
+ list_for_each_entry(p, &buf_map, node) {
+ if (p->phys == (buf & PAGE_MASK)) {
+ return (void *)((u32) p->virt | offset);
+ }
+ }
+
+ printk(KERN_WARNING
+ "SDMA malloc: could not translate phys address 0x%lx\n", buf);
+ return 0;
+}
+
+/*!
+ * Allocates uncacheable buffer
+ *
+ * @param size size of allocated buffer
+ * @return pointer to buffer
+ */
+void *sdma_malloc(size_t size)
+{
+ void *buf;
+ dma_addr_t dma_addr;
+ virt_phys_struct vf;
+
+ if (size > SDMA_POOL_SIZE) {
+ printk(KERN_WARNING
+ "size in sdma_malloc is more than %d bytes\n",
+ SDMA_POOL_SIZE);
+ buf = 0;
+ } else {
+ buf = dma_pool_alloc(pool, GFP_KERNEL, &dma_addr);
+ if (buf > 0) {
+ vf.virt = buf;
+ vf.phys = dma_addr;
+
+ if (add_entry(&vf) < 0) {
+ dma_pool_free(pool, buf, dma_addr);
+ buf = 0;
+ }
+ }
+ }
+
+ DPRINTK("allocated vaddr 0x%p\n", buf);
+ return buf;
+}
+
+/*!
+ * Frees uncacheable buffer
+ *
+ * @param buf buffer pointer for deletion
+ */
+void sdma_free(void *buf)
+{
+#ifdef CONFIG_SDMA_IRAM
+ if (IS_IRAM_VIRT((unsigned long)buf)) {
+ sdma_iram_free(buf);
+ return;
+ }
+#endif /*CONFIG_SDMA_IRAM */
+
+ dma_pool_free(pool, buf, sdma_virt_to_phys(buf));
+ delete_entry(buf);
+}
+
+#ifdef CONFIG_SDMA_IRAM
+/*!
+ * Allocates uncacheable buffer from IRAM
+ */
+void *sdma_iram_malloc(size_t size)
+{
+ void *buf = NULL;
+ int index = -1;
+ unsigned long flags;
+ if (size > IRAM_UNIT_SIZE) {
+ printk(KERN_WARNING
+ "size in sdma_iram_malloc is more than %d bytes\n",
+ IRAM_UNIT_SIZE);
+ } else {
+ spin_lock_irqsave(&iram_pool_lock, flags);
+ if (!list_empty(&iram_free_list)) {
+ buf =
+ list_entry(iram_free_list.next, iram_head_t, list);
+ list_del(iram_free_list.next);
+ index =
+ ((unsigned long)buf -
+ IRAM_VIRT_BASE) / IRAM_UNIT_SIZE;
+ if (index < 0 || index >= IRAM_POOL_SIZE) {
+ spin_unlock_irqrestore(&iram_pool_lock, flags);
+ printk(KERN_ERR "The iram pool has crashed\n");
+ return NULL;
+ }
+ if (iram_pool_flag[index]) {
+ spin_unlock_irqrestore(&iram_pool_lock, flags);
+ printk(KERN_WARNING
+ "iram block %d already has been allocated \n",
+ index);
+ }
+ iram_pool_flag[index] = 1;
+ }
+ spin_unlock_irqrestore(&iram_pool_lock, flags);
+ if ((unsigned long)buf & (IRAM_UNIT_SIZE - 1)) {
+ printk(KERN_WARNING
+ "the start address is not align of %d, buffer offset %ld\n",
+ IRAM_UNIT_SIZE, (unsigned long)buf);
+
+ buf = PTR_ALIGN(buf, IRAM_UNIT_SIZE);
+ }
+ }
+ return buf;
+}
+
+/*!
+ * Free uncacheable buffer into IRAM.
+ */
+static void sdma_iram_free(void *buf)
+{
+ iram_head_t *p;
+ int index;
+ unsigned long flags;
+ /* The check of parameter will be done in sdma_free */
+ index = ((unsigned long)buf - IRAM_VIRT_BASE) / IRAM_UNIT_SIZE;
+ spin_lock_irqsave(&iram_pool_lock, flags);
+ p = (iram_head_t *) ((unsigned long)buf & (~(IRAM_UNIT_SIZE - 1)));
+ list_add_tail(&(p->list), &iram_free_list);
+ if (iram_pool_flag[index]) {
+ iram_pool_flag[index] = 0;
+ spin_unlock_irqrestore(&iram_pool_lock, flags);
+ } else {
+ printk(KERN_WARNING
+ "Free %p which IRAM block %d is already freed\n", buf,
+ index);
+ spin_unlock_irqrestore(&iram_pool_lock, flags);
+ }
+}
+
+/*!
+ * Initialized the free list of IRAM.
+ */
+static void iram_pool_init(void)
+{
+ int i;
+ iram_head_t *p;
+ memset(iram_pool_flag, 0, IRAM_POOL_SIZE);
+ INIT_LIST_HEAD(&iram_free_list);
+ for (i = 0; i < IRAM_POOL_SIZE; i++) {
+ p = (iram_head_t *) (IRAM_VIRT_BASE + i * IRAM_UNIT_SIZE);
+ list_add_tail(&(p->list), &iram_free_list);
+ }
+}
+#endif /*CONFIG_SDMA_IRAM */
+
+/*!
+ * SDMA buffers pool initialization function
+ */
+void __init init_sdma_pool(void)
+{
+#ifdef CONFIG_SDMA_IRAM
+ iram_pool_init();
+#endif /*CONFIG_SDMA_IRAM */
+
+ pool = dma_pool_create("SDMA", NULL, SDMA_POOL_SIZE, 0, 0);
+
+ INIT_LIST_HEAD(&buf_map);
+}
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Linux SDMA API");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/serialxc.c b/arch/arm/plat-mxc/serialxc.c
new file mode 100644
index 000000000000..cf18bfdc8a76
--- /dev/null
+++ b/arch/arm/plat-mxc/serialxc.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/usb/fsl_xcvr.h>
+
+#include <mach/hardware.h>
+#include <mach/arc_otg.h>
+
+static void usb_serial_init(struct fsl_xcvr_ops *this)
+{
+}
+
+static void usb_serial_uninit(struct fsl_xcvr_ops *this)
+{
+}
+
+static struct fsl_xcvr_ops serial_ops = {
+ .name = "serial",
+ .xcvr_type = PORTSC_PTS_SERIAL,
+ .init = usb_serial_init,
+ .uninit = usb_serial_uninit,
+};
+
+extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops);
+
+static int __init serialxc_init(void)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ fsl_usb_xcvr_register(&serial_ops);
+
+ return 0;
+}
+
+extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops);
+
+static void __exit serialxc_exit(void)
+{
+ fsl_usb_xcvr_unregister(&serial_ops);
+}
+
+module_init(serialxc_init);
+module_exit(serialxc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("serial xcvr driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/snoop.c b/arch/arm/plat-mxc/snoop.c
new file mode 100644
index 000000000000..b6e48c7e59d0
--- /dev/null
+++ b/arch/arm/plat-mxc/snoop.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#ifdef M4IF_BASE_ADDR
+#define SNOOP_V2
+#define MAX_SNOOP 2
+#define g_snoop_base (IO_ADDRESS(M4IF_BASE_ADDR) + 0x4C)
+#elif defined(M3IF_BASE_ADDR)
+#define MAX_SNOOP 1
+#define g_snoop_base (IO_ADDRESS(M3IF_BASE_ADDR) + 0x28)
+#else
+#define MAX_SNOOP 0
+#define g_snoop_base 0
+#endif
+
+/* M3IF Snooping Configuration Register 0 (M3IFSCFG0) READ/WRITE*/
+#define SBAR(x) (x * 0x14)
+/* M3IF Snooping Configuration Register 1 (M3IFSCFG1) READ/WRITE*/
+#define SERL(x) ((x * 0x14) + 0x4)
+/* M3IF Snooping Configuration Register 2 (M3IFSCFG2) READ/WRITE*/
+#define SERH(x) ((x * 0x14) + 0x8)
+/* M3IF Snooping Status Register 0 (M3IFSSR0) READ/WRITE */
+#define SSRL(x) ((x * 0x14) + 0xC)
+/* M3IF Snooping Status Register 1 (M3IFSSR1) */
+#define SSRH(x) ((x * 0x14) + 0x10)
+
+#if MAX_SNOOP
+
+int mxc_snoop_set_config(u32 num, unsigned long base, int size)
+{
+ u32 reg;
+ uint32_t msb;
+ uint32_t seg_size;
+ uint32_t window_size = 0;
+ int i;
+
+ if (num >= MAX_SNOOP) {
+ return -EINVAL;
+ }
+
+ /* Setup M3IF for snooping */
+ if (size) {
+
+ if (base == 0) {
+ return -EINVAL;
+ }
+
+ msb = fls(size);
+ if (!(size & ((1UL << msb) - 1)))
+ msb--; /* Already aligned to power 2 */
+ if (msb < 11)
+ msb = 11;
+
+ window_size = (1UL << msb);
+ seg_size = window_size / 64;
+
+ msb -= 11;
+
+ reg = base & ~((1UL << msb) - 1);
+ reg |= msb << 1;
+ reg |= 1; /* enable snooping */
+ reg |= 0x80; /* Set pulse width to default (M4IF only) */
+ __raw_writel(reg, g_snoop_base + SBAR(num));
+
+ reg = 0;
+ for (i = 0; i < 32; i++) {
+ if (i * seg_size >= size)
+ break;
+ reg |= 1UL << i;
+ }
+ __raw_writel(reg, g_snoop_base + SERL(num));
+
+ reg = 0;
+ for (i = 32; i < 64; i++) {
+ if (i * seg_size >= size)
+ break;
+ reg |= 1UL << (i - 32);
+ }
+ __raw_writel(reg, g_snoop_base + SERH(num));
+
+ pr_debug
+ ("Snooping unit # %d enabled: window size = 0x%X, M3IFSCFG0=0x%08X, M3IFSCFG1=0x%08X, M3IFSCFG2=0x%08X\n",
+ num, window_size, __raw_readl(g_snoop_base + SBAR(num)),
+ __raw_readl(g_snoop_base + SERL(num)),
+ __raw_readl(g_snoop_base + SERH(num)));
+ } else {
+ __raw_writel(0, g_snoop_base + SBAR(num));
+ }
+
+ return window_size;
+}
+
+EXPORT_SYMBOL(mxc_snoop_set_config);
+
+int mxc_snoop_get_status(u32 num, u32 * statl, u32 * stath)
+{
+ if (num >= MAX_SNOOP) {
+ return -EINVAL;
+ }
+
+ *statl = __raw_readl(g_snoop_base + SSRL(num));
+ *stath = __raw_readl(g_snoop_base + SSRH(num));
+ /* DPRINTK("status = 0x%08X%08X\n", stat[1], stat[0]); */
+
+#ifdef SNOOP_V2
+ __raw_writel(*statl, g_snoop_base + SSRL(num));
+ __raw_writel(*stath, g_snoop_base + SSRH(num));
+#else
+ __raw_writel(0x0, g_snoop_base + SSRL(num));
+ __raw_writel(0x0, g_snoop_base + SSRH(num));
+#endif
+ return 0;
+}
+
+EXPORT_SYMBOL(mxc_snoop_get_status);
+
+#endif /* MAX_SNOOP */
diff --git a/arch/arm/plat-mxc/spba.c b/arch/arm/plat-mxc/spba.c
new file mode 100644
index 000000000000..a7f21a9e36df
--- /dev/null
+++ b/arch/arm/plat-mxc/spba.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <mach/spba.h>
+
+/*!
+ * @file plat-mxc/spba.c
+ *
+ * @brief This file contains the SPBA API implementation details.
+ *
+ * @ingroup SPBA
+ */
+
+static DEFINE_SPINLOCK(spba_lock);
+
+#define SPBA_MASTER_MIN 1
+#define SPBA_MASTER_MAX 7
+
+/*!
+ * the base addresses for the SPBA modules
+ */
+static unsigned long spba_base = (unsigned long)IO_ADDRESS(SPBA_CTRL_BASE_ADDR);
+
+/*!
+ * SPBA clock
+ */
+static struct clk *spba_clk;
+/*!
+ * This function allows the three masters (A, B, C) to take ownership of a
+ * shared peripheral.
+ *
+ * @param mod specified module as defined in \b enum \b #spba_module
+ * @param master one of more (or-ed together) masters as defined in \b enum \b #spba_masters
+ *
+ * @return 0 if successful; -1 otherwise.
+ */
+int spba_take_ownership(int mod, int master)
+{
+ unsigned long spba_flags;
+ __u32 rtn_val = -1;
+
+ if (master < SPBA_MASTER_MIN || master > SPBA_MASTER_MAX) {
+ printk("spba_take_ownership() invalide master= %d\n", master);
+ BUG(); /* oops */
+ }
+
+ if (spba_clk == NULL)
+ spba_clk = clk_get(NULL, "spba_clk");
+
+ clk_enable(spba_clk);
+
+ spin_lock_irqsave(&spba_lock, spba_flags);
+ __raw_writel(master, spba_base + mod);
+
+ if ((__raw_readl(spba_base + mod) & MXC_SPBA_RAR_MASK) == master) {
+ rtn_val = 0;
+ }
+
+ spin_unlock_irqrestore(&spba_lock, spba_flags);
+
+ clk_disable(spba_clk);
+ return rtn_val;
+}
+
+/*!
+ * This function releases the ownership for a shared peripheral.
+ *
+ * @param mod specified module as defined in \b enum \b #spba_module
+ * @param master one of more (or-ed together) masters as defined in \b enum \b #spba_masters
+ *
+ * @return 0 if successful; -1 otherwise.
+ */
+int spba_rel_ownership(int mod, int master)
+{
+ unsigned long spba_flags;
+ volatile unsigned long rar;
+
+ if (master < SPBA_MASTER_MIN || master > SPBA_MASTER_MAX) {
+ printk("spba_take_ownership() invalide master= %d\n", master);
+ BUG(); /* oops */
+ }
+
+ if (spba_clk == NULL)
+ spba_clk = clk_get(NULL, "spba_clk");
+
+ clk_enable(spba_clk);
+
+ if ((__raw_readl(spba_base + mod) & master) == 0) {
+ clk_disable(spba_clk);
+ return 0; /* does not own it */
+ }
+
+ spin_lock_irqsave(&spba_lock, spba_flags);
+
+ /* Since only the last 3 bits are writeable, doesn't need to mask off
+ bits 31-3 */
+ rar = __raw_readl(spba_base + mod) & (~master);
+ __raw_writel(rar, spba_base + mod);
+
+ if ((__raw_readl(spba_base + mod) & master) != 0) {
+ spin_unlock_irqrestore(&spba_lock, spba_flags);
+ clk_disable(spba_clk);
+ return -1;
+ }
+
+ spin_unlock_irqrestore(&spba_lock, spba_flags);
+
+ clk_disable(spba_clk);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(spba_take_ownership);
+EXPORT_SYMBOL(spba_rel_ownership);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SPBA");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/tzic.c b/arch/arm/plat-mxc/tzic.c
new file mode 100644
index 000000000000..5cfe328fc865
--- /dev/null
+++ b/arch/arm/plat-mxc/tzic.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+
+/*
+ *****************************************
+ * TZIC Registers *
+ *****************************************
+ */
+#define TZIC_BASE IO_ADDRESS(TZIC_BASE_ADDR)
+#define TZIC_INTCNTL (TZIC_BASE + 0x0000) /* control register */
+#define TZIC_INTTYPE (TZIC_BASE + 0x0004) /* Controller type register */
+#define TZIC_IMPID (TZIC_BASE + 0x0008) /* Distributor Implementer Identification Register */
+#define TZIC_PRIOMASK (TZIC_BASE + 0x000C) /* Priority Mask Reg */
+#define TZIC_SYNCCTRL (TZIC_BASE + 0x0010) /* Synchronizer Control register */
+#define TZIC_DSMINT (TZIC_BASE + 0x0014) /* DSM interrupt Holdoffregister */
+#define TZIC_INTSEC0 (TZIC_BASE + 0x0080) /* interrupt security register 0 */
+#define TZIC_ENSET0 (TZIC_BASE + 0x0100) /* Enable Set Register 0 */
+#define TZIC_ENCLEAR0 (TZIC_BASE + 0x0180) /* Enable Clear Register 0 */
+#define TZIC_SRCSET0 (TZIC_BASE + 0x0200) /* Source Set Register 0 */
+#define TZIC_SRCCLAR0 (TZIC_BASE + 0x0280) /* Source Clear Register 0 */
+#define TZIC_PRIORITY0 (TZIC_BASE + 0x0400) /* Priority Register 0 */
+#define TZIC_PND0 (TZIC_BASE + 0x0D00) /* Pending Register 0 */
+#define TZIC_HIPND0 (TZIC_BASE + 0x0D80) /* High Priority Pending Register */
+#define TZIC_WAKEUP0 (TZIC_BASE + 0x0E00) /* Wakeup Config Register */
+#define TZIC_SWINT (TZIC_BASE + 0x0F00) /* Software Interrupt Rigger Register */
+#define TZIC_ID0 (TZIC_BASE + 0x0FD0) /* Indentification Register 0 */
+
+/*!
+ * Disable interrupt number "irq" in the TZIC
+ *
+ * @param irq interrupt source number
+ */
+static void mxc_mask_irq(unsigned int irq)
+{
+ int index, off;
+
+ index = irq >> 5;
+ off = irq & 0x1F;
+ __raw_writel(1 << off, TZIC_ENCLEAR0 + (index << 2));
+}
+
+/*!
+ * Enable interrupt number "irq" in the TZIC
+ *
+ * @param irq interrupt source number
+ */
+static void mxc_unmask_irq(unsigned int irq)
+{
+ int index, off;
+
+ index = irq >> 5;
+ off = irq & 0x1F;
+ __raw_writel(1 << off, TZIC_ENSET0 + (index << 2));
+}
+
+static unsigned int wakeup_intr[4];
+
+/*!
+ * Set interrupt number "irq" in the TZIC as a wake-up source.
+ *
+ * @param irq interrupt source number
+ * @param enable enable as wake-up if equal to non-zero
+ * disble as wake-up if equal to zero
+ *
+ * @return This function returns 0 on success.
+ */
+static int mxc_set_wake_irq(unsigned int irq, unsigned int enable)
+{
+ unsigned int index, off;
+
+ index = irq >> 5;
+ off = irq & 0x1F;
+
+ if (index > 3)
+ return -1;
+
+ if (enable)
+ wakeup_intr[index] |= (1 << off);
+ else
+ wakeup_intr[index] &= ~(1 << off);
+
+ return 0;
+}
+
+static struct irq_chip mxc_tzic_chip = {
+ .name = "MXC_TZIC",
+ .ack = mxc_mask_irq,
+ .mask = mxc_mask_irq,
+ .unmask = mxc_unmask_irq,
+ .set_wake = mxc_set_wake_irq,
+};
+
+/*!
+ * This function initializes the TZIC hardware and disables all the
+ * interrupts. It registers the interrupt enable and disable functions
+ * to the kernel for each interrupt source.
+ */
+void __init mxc_init_irq(void)
+{
+ int i;
+
+ /* put the TZIC into the reset value with
+ * all interrupts disabled
+ */
+ i = __raw_readl(TZIC_INTCNTL);
+
+ __raw_writel(0x80010001, TZIC_INTCNTL);
+ i = __raw_readl(TZIC_INTCNTL);
+ __raw_writel(0x1f, TZIC_PRIOMASK);
+ i = __raw_readl(TZIC_PRIOMASK);
+ __raw_writel(0x02, TZIC_SYNCCTRL);
+ i = __raw_readl(TZIC_SYNCCTRL);
+ for (i = 0; i < 4; i++) {
+ __raw_writel(0xFFFFFFFF, TZIC_INTSEC0 + i * 4);
+ }
+ /* disable all interrupts */
+ for (i = 0; i < 4; i++) {
+ __raw_writel(0xFFFFFFFF, TZIC_ENCLEAR0 + i * 4);
+ }
+
+ /* all IRQ no FIQ Warning :: No selection */
+
+ for (i = 0; i < MXC_MAX_INT_LINES; i++) {
+ set_irq_chip(i, &mxc_tzic_chip);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ }
+
+ printk(KERN_INFO "MXC IRQ initialized\n");
+}
+
+/*!
+ * enable wakeup interrupt
+ *
+ * @param is_idle 1 if called in idle loop (enset registers);
+ * 0 to be used when called from low power entry
+ * @return 0 if successful; non-zero otherwise
+ */
+int tzic_enable_wake(int is_idle)
+{
+ unsigned int i, v;
+
+ __raw_writel(1, TZIC_DSMINT);
+ if (unlikely(__raw_readl(TZIC_DSMINT) == 0))
+ return -EAGAIN;
+
+ if (likely(is_idle)) {
+ for (i = 0; i < 4; i++) {
+ v = __raw_readl(TZIC_ENSET0 + i * 4);
+ __raw_writel(v, TZIC_WAKEUP0 + i * 4);
+ }
+ } else {
+ for (i = 0; i < 4; i++) {
+ v = wakeup_intr[i];
+ __raw_writel(v, TZIC_WAKEUP0 + i * 4);
+ }
+ }
+ return 0;
+}
diff --git a/arch/arm/plat-mxc/usb_common.c b/arch/arm/plat-mxc/usb_common.c
new file mode 100644
index 000000000000..389efb7e128d
--- /dev/null
+++ b/arch/arm/plat-mxc/usb_common.c
@@ -0,0 +1,898 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * otg_{get,set}_transceiver() are from arm/plat-omap/usb.c.
+ * which is Copyright (C) 2004 Texas Instruments, Inc.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ *@defgroup USB ARC OTG USB Driver
+ */
+
+/*!
+ * @file usb_common.c
+ *
+ * @brief platform related part of usb driver.
+ * @ingroup USB
+ */
+
+/*!
+ *Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <linux/regulator/consumer.h>
+#include <mach/arc_otg.h>
+#include <asm/mach-types.h>
+
+#define MXC_NUMBER_USB_TRANSCEIVER 6
+struct fsl_xcvr_ops *g_xc_ops[MXC_NUMBER_USB_TRANSCEIVER] = { NULL };
+
+enum fsl_usb2_modes get_usb_mode(struct fsl_usb2_platform_data *pdata)
+{
+ enum fsl_usb2_modes mode;
+ mode = FSL_USB_UNKNOWN;
+
+ if (!strcmp("DR", pdata->name)) {
+ if ((UOG_USBMODE & 0x3) == 0x2)
+ mode = FSL_USB_DR_DEVICE;
+ else if ((UOG_USBMODE & 0x3) == 0x3)
+ mode = FSL_USB_DR_HOST;
+ } else if (!strcmp("Host 1", pdata->name))
+ mode = FSL_USB_MPH_HOST1;
+ else if (!strcmp("Host 2", pdata->name))
+ mode = FSL_USB_MPH_HOST2;
+
+ if (mode == FSL_USB_UNKNOWN)
+ printk(KERN_ERR "unknow usb mode,name is %s\n", pdata->name);
+ return mode;
+}
+
+static struct clk *usb_clk;
+static struct clk *usb_ahb_clk;
+
+extern int gpio_usbotg_hs_active(void);
+extern int gpio_usbotg_hs_inactive(void);
+
+/*
+ * make sure USB_CLK is running at 60 MHz +/- 1000 Hz
+ */
+static int fsl_check_usbclk(void)
+{
+ unsigned long freq;
+
+ usb_ahb_clk = clk_get(NULL, "usb_ahb_clk");
+ if (clk_enable(usb_ahb_clk)) {
+ printk(KERN_ERR "clk_enable(usb_ahb_clk) failed\n");
+ return -EINVAL;
+ }
+ clk_put(usb_ahb_clk);
+
+ usb_clk = clk_get(NULL, "usb_clk");
+ freq = clk_get_rate(usb_clk);
+ clk_put(usb_clk);
+ if ((freq < 59999000) || (freq > 60001000)) {
+ printk(KERN_ERR "USB_CLK=%lu, should be 60MHz\n", freq);
+ return -1;
+ }
+
+ return 0;
+}
+
+void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops)
+{
+ int i;
+
+ pr_debug("%s\n", __func__);
+ for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) {
+ if (g_xc_ops[i] == NULL) {
+ g_xc_ops[i] = xcvr_ops;
+ return;
+ }
+ }
+
+ pr_debug("Failed %s\n", __func__);
+}
+EXPORT_SYMBOL(fsl_usb_xcvr_register);
+
+void fsl_platform_set_test_mode (struct fsl_usb2_platform_data *pdata, enum usb_test_mode mode)
+{
+ if (pdata->xcvr_ops && pdata->xcvr_ops->set_test_mode)
+ pdata->xcvr_ops->set_test_mode((u32 *)(pdata->regs + ULPIVW_OFF), mode);
+}
+EXPORT_SYMBOL(fsl_platform_set_test_mode);
+
+void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops)
+{
+ int i;
+
+ pr_debug("%s\n", __func__);
+ for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) {
+ if (g_xc_ops[i] == xcvr_ops) {
+ g_xc_ops[i] = NULL;
+ return;
+ }
+ }
+
+ pr_debug("Failed %s\n", __func__);
+}
+EXPORT_SYMBOL(fsl_usb_xcvr_unregister);
+
+static struct fsl_xcvr_ops *fsl_usb_get_xcvr(char *name)
+{
+ int i;
+
+ pr_debug("%s\n", __func__);
+ if (name == NULL) {
+ printk(KERN_ERR "get_xcvr(): No tranceiver name\n");
+ return NULL;
+ }
+
+ for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) {
+ if (strcmp(g_xc_ops[i]->name, name) == 0) {
+ return g_xc_ops[i];
+ }
+ }
+ pr_debug("Failed %s\n", __func__);
+ return NULL;
+}
+
+/* The dmamask must be set for EHCI to work */
+static u64 ehci_dmamask = ~(u32) 0;
+
+/*!
+ * Register an instance of a USB host platform device.
+ *
+ * @param res: resource pointer
+ * @param n_res: number of resources
+ * @param config: config pointer
+ *
+ * @return newly-registered platform_device
+ *
+ * The USB controller supports 3 host interfaces, and the
+ * kernel can be configured to support some number of them.
+ * Each supported host interface is registered as an instance
+ * of the "fsl-ehci" device. Call this function multiple times
+ * to register each host interface.
+ */
+static int instance_id = 0;
+struct platform_device *host_pdev_register(struct resource *res, int n_res,
+ struct fsl_usb2_platform_data *config)
+{
+ struct platform_device *pdev;
+ int rc;
+
+ pr_debug("register host res=0x%p, size=%d\n", res, n_res);
+
+ pdev = platform_device_register_simple("fsl-ehci",
+ instance_id, res, n_res);
+ if (IS_ERR(pdev)) {
+ pr_debug("can't register %s Host, %ld\n",
+ config->name, PTR_ERR(pdev));
+ return NULL;
+ }
+
+ pdev->dev.coherent_dma_mask = 0xffffffff;
+ pdev->dev.dma_mask = &ehci_dmamask;
+
+ /*
+ * platform_device_add_data() makes a copy of
+ * the platform_data passed in. That makes it
+ * impossible to share the same config struct for
+ * all OTG devices (host,gadget,otg). So, just
+ * set the platorm_data pointer ourselves.
+ */
+ rc = platform_device_add_data(pdev, config,
+ sizeof(struct fsl_usb2_platform_data));
+ if (rc) {
+ platform_device_unregister(pdev);
+ return NULL;
+ }
+
+ printk(KERN_INFO "usb: %s host (%s) registered\n", config->name,
+ config->transceiver);
+ pr_debug("pdev=0x%p dev=0x%p resources=0x%p pdata=0x%p\n",
+ pdev, &pdev->dev, pdev->resource, pdev->dev.platform_data);
+
+ instance_id++;
+
+ return pdev;
+}
+
+/* DDD looks like this is needed by Belcarra code */
+void fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata, int on)
+{
+ if (pdata->xcvr_ops && pdata->xcvr_ops->set_vbus_power)
+ pdata->xcvr_ops->set_vbus_power(pdata->xcvr_ops, pdata, on);
+}
+EXPORT_SYMBOL(fsl_platform_set_vbus_power);
+
+/* DDD looks like this is needed by Belcarra code */
+void fsl_platform_perform_remote_wakeup(struct fsl_usb2_platform_data *pdata)
+{
+ if (pdata->xcvr_ops && pdata->xcvr_ops->set_remote_wakeup)
+ pdata->xcvr_ops->set_remote_wakeup(
+ (u32 *)(pdata->regs + ULPIVW_OFF));
+}
+EXPORT_SYMBOL(fsl_platform_perform_remote_wakeup);
+
+#if defined(CONFIG_USB_OTG)
+static struct otg_transceiver *xceiv;
+
+/**
+ * otg_get_transceiver - find the (single) OTG transceiver driver
+ *
+ * Returns the transceiver driver, after getting a refcount to it; or
+ * null if there is no such transceiver. The caller is responsible for
+ * releasing that count.
+ */
+struct otg_transceiver *otg_get_transceiver(void)
+{
+ pr_debug("%s xceiv=0x%p\n", __func__, xceiv);
+ if (xceiv)
+ get_device(xceiv->dev);
+ return xceiv;
+}
+EXPORT_SYMBOL(otg_get_transceiver);
+
+int otg_set_transceiver(struct otg_transceiver *x)
+{
+ pr_debug("%s xceiv=0x%p x=0x%p\n", __func__, xceiv, x);
+ if (xceiv && x)
+ return -EBUSY;
+ xceiv = x;
+ return 0;
+}
+EXPORT_SYMBOL(otg_set_transceiver);
+
+static struct resource *otg_resources;
+
+struct resource *otg_get_resources(void)
+{
+ return otg_resources;
+}
+EXPORT_SYMBOL(otg_get_resources);
+
+int otg_set_resources(struct resource *resources)
+{
+ otg_resources = resources;
+ return 0;
+}
+EXPORT_SYMBOL(otg_set_resources);
+#endif
+
+static void usbh1_set_serial_xcvr(void)
+{
+ pr_debug("%s: \n", __func__);
+ USBCTRL &= ~(UCTRL_H1SIC_MASK | UCTRL_BPE); /* disable bypass mode */
+ USBCTRL |= UCTRL_H1SIC_SU6 | /* single-ended / unidir. */
+ UCTRL_H1WIE | UCTRL_H1DT | /* disable H1 TLL */
+ UCTRL_H1PM; /* power mask */
+}
+
+static void usbh1_set_ulpi_xcvr(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ /* Stop then Reset */
+ UH1_USBCMD &= ~UCMD_RUN_STOP;
+ while (UH1_USBCMD & UCMD_RUN_STOP) ;
+
+ UH1_USBCMD |= UCMD_RESET;
+ while (UH1_USBCMD & UCMD_RESET) ;
+
+ /* Select the clock from external PHY */
+ USB_CTRL_1 |= USB_CTRL_UH1_EXT_CLK_EN;
+
+ /* select ULPI PHY PTS=2 */
+ UH1_PORTSC1 = (UH1_PORTSC1 & ~PORTSC_PTS_MASK) | PORTSC_PTS_ULPI;
+
+ USBCTRL |= UCTRL_H1WIE; /* HOST1 wakeup intr enable */
+ USBCTRL |= UCTRL_H1UIE; /* Host1 ULPI interrupt enable */
+ USBCTRL &= ~UCTRL_H1PM; /* HOST1 power mask */
+
+ /* Interrupt Threshold Control:Immediate (no threshold) */
+ UH1_USBCMD &= UCMD_ITC_NO_THRESHOLD;
+
+ UH1_USBCMD |= UCMD_RESET; /* reset the controller */
+
+ /* allow controller to reset, and leave time for
+ * the ULPI transceiver to reset too.
+ */
+ msleep(100);
+
+ /* Turn off the usbpll for ulpi tranceivers */
+ clk_disable(usb_clk);
+}
+static void usbh2_set_ulpi_xcvr(void)
+{
+ u32 tmp;
+
+ pr_debug("%s\n", __func__);
+ USBCTRL &= ~(UCTRL_H2SIC_MASK | UCTRL_BPE);
+ USBCTRL |= UCTRL_H2WIE | /* wakeup intr enable */
+ UCTRL_H2UIE | /* ULPI intr enable */
+ UCTRL_H2DT | /* disable H2 TLL */
+ UCTRL_H2PM; /* power mask */
+
+ /* must set ULPI phy before turning off clock */
+ tmp = UH2_PORTSC1 & ~PORTSC_PTS_MASK;
+ tmp |= PORTSC_PTS_ULPI;
+ UH2_PORTSC1 = tmp;
+
+ UH2_USBCMD |= UCMD_RESET; /* reset the controller */
+
+ /* allow controller to reset, and leave time for
+ * the ULPI transceiver to reset too.
+ */
+ msleep(100);
+
+ /* Turn off the usbpll for ulpi tranceivers */
+ clk_disable(usb_clk);
+}
+
+static void usbh2_set_serial_xcvr(void)
+{
+ pr_debug("%s: \n", __func__);
+
+ /* Stop then Reset */
+ UH2_USBCMD &= ~UCMD_RUN_STOP;
+ while (UH2_USBCMD & UCMD_RUN_STOP) ;
+
+ UH2_USBCMD |= UCMD_RESET;
+ while (UH2_USBCMD & UCMD_RESET) ;
+
+ USBCTRL &= ~(UCTRL_H2SIC_MASK); /* Disable bypass mode */
+ USBCTRL &= ~(UCTRL_H2PM); /* Power Mask */
+ USBCTRL &= ~UCTRL_H2OCPOL; /* OverCurrent Polarity is Low Active */
+ USBCTRL &= ~UCTRL_H2WIE; /* Wakeup intr disable */
+ USBCTRL |= UCTRL_IP_PUE_DOWN | /* ipp_pue_pulldwn_dpdm */
+ UCTRL_USBTE | /* USBT is enabled */
+ UCTRL_H2DT; /* Disable H2 TLL */
+
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 0) {
+ /* Disable Host2 bus Lock for i.MX35 1.0 */
+ USBCTRL |= UCTRL_H2LOCKD;
+ /* USBOTG_PWR low active */
+ USBCTRL &= ~UCTRL_PP;
+ /* OverCurrent Polarity is Low Active */
+ USBCTRL &= ~UCTRL_OCPOL;
+ } else if (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1) {
+ /* i.MX35 2.0 OTG and Host2 have seperate OC/PWR polarity */
+ USBCTRL &= ~UCTRL_H2PP;
+ USBCTRL &= ~UCTRL_H2OCPOL;
+ } else if (cpu_is_mx25()) {
+ /*
+ * USBH2_PWR and USBH2_OC are active high.
+ * Must force xcvr clock to "internal" so that
+ * we can write to PTS field after it's been
+ * cleared by ehci_turn_off_all_ports().
+ */
+ USBCTRL |= UCTRL_H2PP | UCTRL_H2OCPOL | UCTRL_XCSH2;
+ /* Disable Host2 bus Lock */
+ USBCTRL |= UCTRL_H2LOCKD;
+ }
+
+ USBCTRL &= ~(UCTRL_PP);
+ UH2_PORTSC1 = (UH2_PORTSC1 & (~PORTSC_PTS_MASK)) | PORTSC_PTS_SERIAL;
+
+ if (UH2_HCSPARAMS & HCSPARAMS_PPC)
+ UH2_PORTSC1 |= PORTSC_PORT_POWER;
+
+ /* Reset controller before set host mode */
+ UH2_USBCMD |= UCMD_RESET;
+ while (UH2_USBCMD & UCMD_RESET) ;
+
+ msleep(100);
+}
+
+/*!
+ * Register remote wakeup by this usb controller
+ *
+ * @param pdev: platform_device for this usb controller
+ *
+ * @return 0 or negative error code in case not supportted.
+ */
+static int usb_register_remote_wakeup(struct platform_device *pdev)
+{
+ struct resource *res;
+ int irq;
+
+ pr_debug("%s: pdev=0x%p \n", __func__, pdev);
+ if (!cpu_is_mx51())
+ return -ECANCELED;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ pdev->dev.bus_id);
+ return -ENODEV;
+ }
+ irq = res->start;
+ pdev->dev.power.can_wakeup = 1;
+ enable_irq_wake(irq);
+
+ return 0;
+}
+
+extern void gpio_usbh1_setback_stp(void);
+extern void gpio_usbh2_setback_stp(void);
+
+int fsl_usb_host_init(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ struct fsl_xcvr_ops *xops;
+
+ pr_debug("%s: pdev=0x%p pdata=0x%p\n", __func__, pdev, pdata);
+
+ xops = fsl_usb_get_xcvr(pdata->transceiver);
+ if (!xops) {
+ printk(KERN_ERR "%s transceiver ops missing\n", pdata->name);
+ return -EINVAL;
+ }
+ pdata->xcvr_ops = xops;
+ pdata->xcvr_type = xops->xcvr_type;
+ pdata->pdev = pdev;
+
+ if (fsl_check_usbclk() != 0)
+ return -EINVAL;
+
+ pr_debug("%s: grab pins\n", __func__);
+ if (pdata->gpio_usb_active())
+ return -EINVAL;
+
+ if (clk_enable(usb_clk)) {
+ printk(KERN_ERR "clk_enable(usb_clk) failed\n");
+ return -EINVAL;
+ }
+
+ if (cpu_is_mx51()) {
+ usb_clk = clk_get(NULL, "usboh3_clk");
+ clk_enable(usb_clk);
+ clk_put(usb_clk);
+ }
+
+ /* enable board power supply for xcvr */
+ if (pdata->xcvr_pwr) {
+ if (pdata->xcvr_pwr->regu1)
+ regulator_enable(pdata->xcvr_pwr->regu1);
+ if (pdata->xcvr_pwr->regu2)
+ regulator_enable(pdata->xcvr_pwr->regu2);
+ }
+
+ if (xops->init)
+ xops->init(xops);
+
+ if (usb_register_remote_wakeup(pdev))
+ pr_debug("%s port is not a wakeup source.\n", pdata->name);
+
+ if (xops->xcvr_type == PORTSC_PTS_SERIAL) {
+ if (cpu_is_mx35()) {
+ usbh2_set_serial_xcvr();
+ /* Close the internal 60Mhz */
+ USBCTRL &= ~UCTRL_XCSH2;
+ } else if (cpu_is_mx25())
+ usbh2_set_serial_xcvr();
+ else
+ usbh1_set_serial_xcvr();
+ } else if (xops->xcvr_type == PORTSC_PTS_ULPI) {
+ if (cpu_is_mx51()) {
+#ifdef CONFIG_USB_EHCI_ARC_H1
+ if (!strcmp("Host 1", pdata->name)) {
+ usbh1_set_ulpi_xcvr();
+ if (cpu_is_mx51()) {
+ gpio_usbh1_setback_stp();
+ /* disable remote wakeup irq */
+ USBCTRL &= ~UCTRL_H1WIE;
+ }
+ }
+#endif
+#ifdef CONFIG_USB_EHCI_ARC_H2
+ if (!strcmp("Host 2", pdata->name)) {
+ usbh2_set_ulpi_xcvr();
+ if (cpu_is_mx51())
+ gpio_usbh2_setback_stp();
+ }
+#endif
+ } else
+ usbh2_set_ulpi_xcvr();
+ }
+
+ pr_debug("%s: %s success\n", __func__, pdata->name);
+ return 0;
+}
+EXPORT_SYMBOL(fsl_usb_host_init);
+
+void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata)
+{
+ pr_debug("%s\n", __func__);
+
+ if (pdata->xcvr_ops && pdata->xcvr_ops->uninit)
+ pdata->xcvr_ops->uninit(pdata->xcvr_ops);
+
+ pdata->regs = NULL;
+
+ pdata->gpio_usb_inactive();
+ if (pdata->xcvr_type == PORTSC_PTS_SERIAL) {
+ /* Workaround an IC issue for ehci driver.
+ * when turn off root hub port power, EHCI set
+ * PORTSC reserved bits to be 0, but PTS with 0
+ * means UTMI interface, so here force the Host2
+ * port use the internal 60Mhz.
+ */
+ if (cpu_is_mx35())
+ USBCTRL |= UCTRL_XCSH2;
+ clk_disable(usb_clk);
+ }
+
+ /* disable board power supply for xcvr */
+ if (pdata->xcvr_pwr) {
+ if (pdata->xcvr_pwr->regu1)
+ regulator_disable(pdata->xcvr_pwr->regu1);
+ if (pdata->xcvr_pwr->regu2)
+ regulator_disable(pdata->xcvr_pwr->regu2);
+ }
+
+ if (cpu_is_mx51()) {
+ usb_clk = clk_get(NULL, "usboh3_clk");
+ clk_disable(usb_clk);
+ clk_put(usb_clk);
+ }
+}
+EXPORT_SYMBOL(fsl_usb_host_uninit);
+
+static void otg_set_serial_xcvr(void)
+{
+ pr_debug("%s\n", __func__);
+}
+
+void otg_set_serial_host(void)
+{
+ pr_debug("%s\n", __func__);
+ /* set USBCTRL for host operation
+ * disable: bypass mode,
+ * set: single-ended/unidir/6 wire, OTG wakeup intr enable,
+ * power mask
+ */
+ USBCTRL &= ~UCTRL_OSIC_MASK;
+#if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX3)
+ USBCTRL &= ~UCTRL_BPE;
+#endif
+
+#if defined(CONFIG_MXC_USB_SB3)
+ USBCTRL |= UCTRL_OSIC_SB3 | UCTRL_OWIE | UCTRL_OPM;
+#elif defined(CONFIG_MXC_USB_SU6)
+ USBCTRL |= UCTRL_OSIC_SU6 | UCTRL_OWIE | UCTRL_OPM;
+#elif defined(CONFIG_MXC_USB_DB4)
+ USBCTRL |= UCTRL_OSIC_DB4 | UCTRL_OWIE | UCTRL_OPM;
+#else
+ USBCTRL |= UCTRL_OSIC_DU6 | UCTRL_OWIE | UCTRL_OPM;
+#endif
+
+ USB_OTG_MIRROR = OTGM_VBUSVAL | OTGM_ASESVLD; /* 0xa */
+}
+EXPORT_SYMBOL(otg_set_serial_host);
+
+void otg_set_serial_peripheral(void)
+{
+ /* set USBCTRL for device operation
+ * disable: bypass mode
+ * set: differential/unidir/6 wire, OTG wakeup intr enable,
+ * power mask
+ */
+ USBCTRL &= ~UCTRL_OSIC_MASK;
+#if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX3)
+ USBCTRL &= ~UCTRL_BPE;
+#endif
+
+#if defined(CONFIG_MXC_USB_SB3)
+ USBCTRL |= UCTRL_OSIC_SB3 | UCTRL_OWIE | UCTRL_OPM;
+#elif defined(CONFIG_MXC_USB_SU6)
+ USBCTRL |= UCTRL_OSIC_SU6 | UCTRL_OWIE | UCTRL_OPM;
+#elif defined(CONFIG_MXC_USB_DB4)
+ USBCTRL |= UCTRL_OSIC_DB4 | UCTRL_OWIE | UCTRL_OPM;
+#else
+ USBCTRL |= UCTRL_OSIC_DU6 | UCTRL_OWIE | UCTRL_OPM;
+#endif
+
+ USB_OTG_MIRROR = OTGM_VBUSVAL | OTGM_BSESVLD | OTGM_IDIDG; /* oxd */
+}
+EXPORT_SYMBOL(otg_set_serial_peripheral);
+
+static void otg_set_ulpi_xcvr(void)
+{
+ u32 tmp;
+
+ pr_debug("%s\n", __func__);
+ USBCTRL &= ~UCTRL_OSIC_MASK;
+#if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX3)
+ USBCTRL &= ~UCTRL_BPE;
+#endif
+ USBCTRL |= UCTRL_OUIE | /* ULPI intr enable */
+ UCTRL_OWIE | /* OTG wakeup intr enable */
+ UCTRL_OPM; /* power mask */
+
+ /* must set ULPI phy before turning off clock */
+ tmp = UOG_PORTSC1 & ~PORTSC_PTS_MASK;
+ tmp |= PORTSC_PTS_ULPI;
+ UOG_PORTSC1 = tmp;
+
+ /* need to reset the controller here so that the ID pin
+ * is correctly detected.
+ */
+ UOG_USBCMD |= UCMD_RESET;
+
+ /* allow controller to reset, and leave time for
+ * the ULPI transceiver to reset too.
+ */
+ msleep(100);
+
+ /* Turn off the usbpll for ulpi tranceivers */
+ clk_disable(usb_clk);
+}
+
+int fsl_usb_xcvr_suspend(struct fsl_xcvr_ops *xcvr_ops)
+{
+ if (!machine_is_mx31_3ds())
+ return -ECANCELED;
+
+ if (xcvr_ops->xcvr_type == PORTSC_PTS_ULPI) {
+ if (fsl_check_usbclk() != 0)
+ return -EINVAL;
+ if (gpio_usbotg_hs_active())
+ return -EINVAL;
+ clk_enable(usb_clk);
+
+ otg_set_ulpi_xcvr();
+
+ if (xcvr_ops->suspend)
+ /* suspend transceiver */
+ xcvr_ops->suspend(xcvr_ops);
+
+ gpio_usbotg_hs_inactive();
+ clk_disable(usb_clk);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(fsl_usb_xcvr_suspend);
+
+static void otg_set_utmi_xcvr(void)
+{
+ u32 tmp;
+
+ /* Stop then Reset */
+ UOG_USBCMD &= ~UCMD_RUN_STOP;
+ while (UOG_USBCMD & UCMD_RUN_STOP) ;
+
+ UOG_USBCMD |= UCMD_RESET;
+ while ((UOG_USBCMD) & (UCMD_RESET)) ;
+
+ if (cpu_is_mx51()) {
+ /* OTG Polarity of Overcurrent is Low active */
+ USB_PHY_CTR_FUNC |= USB_UTMI_PHYCTRL_OC_POL;
+ /* Enable OTG Overcurrent Event */
+ USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_OC_DIS;
+ } else if (cpu_is_mx25()) {
+ USBCTRL |= UCTRL_OCPOL;
+ USBCTRL &= ~UCTRL_PP;
+ } else {
+ /* USBOTG_PWR low active */
+ USBCTRL &= ~UCTRL_PP;
+ /* OverCurrent Polarity is Low Active */
+ USBCTRL &= ~UCTRL_OCPOL;
+
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 0)
+ /* OTG Lock Disable */
+ USBCTRL |= UCTRL_OLOCKD;
+ }
+
+ USBCTRL &= ~UCTRL_OPM; /* OTG Power Mask */
+ USBCTRL &= ~UCTRL_OWIE; /* OTG Wakeup Intr Disable */
+
+ /* set UTMI xcvr */
+ tmp = UOG_PORTSC1 & ~PORTSC_PTS_MASK;
+ tmp |= PORTSC_PTS_UTMI;
+ UOG_PORTSC1 = tmp;
+
+ if (cpu_is_mx51()) {
+ /* Set the PHY clock to 19.2MHz */
+ USB_PHY_CTR_FUNC2 &= ~USB_UTMI_PHYCTRL2_PLLDIV_MASK;
+ USB_PHY_CTR_FUNC2 |= 0x01;
+ } else if (machine_is_mx37_3ds()) {
+ /* Reference voltage for HS disconnect envelope detector */
+ /* adjust the Squelch level */
+ USB_PHY_CTR_FUNC2 &= ~(USB_UTMI_PHYCTRL2_HSDEVSEL_MASK <<
+ USB_UTMI_PHYCTRL2_HSDEVSEL_SHIFT);
+ }
+
+ /* Workaround an IC issue for ehci driver:
+ * when turn off root hub port power, EHCI set
+ * PORTSC reserved bits to be 0, but PTW with 0
+ * means 8 bits tranceiver width, here change
+ * it back to be 16 bits and do PHY diable and
+ * then enable.
+ */
+ UOG_PORTSC1 |= PORTSC_PTW;
+
+ if (cpu_is_mx35() || cpu_is_mx25()) {
+ /* Enable UTMI interface in PHY control Reg */
+ USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_UTMI_ENABLE;
+ USB_PHY_CTR_FUNC |= USB_UTMI_PHYCTRL_UTMI_ENABLE;
+ }
+
+ /* need to reset the controller here so that the ID pin
+ * is correctly detected.
+ */
+ /* Stop then Reset */
+ UOG_USBCMD &= ~UCMD_RUN_STOP;
+ while (UOG_USBCMD & UCMD_RUN_STOP) ;
+
+ UOG_USBCMD |= UCMD_RESET;
+ while ((UOG_USBCMD) & (UCMD_RESET)) ;
+
+ /* allow controller to reset, and leave time for
+ * the ULPI transceiver to reset too.
+ */
+ msleep(100);
+
+ /* Turn off the usbpll for mx25 UTMI tranceivers */
+ /* DDD: can we do this UTMI xcvrs on all boards? */
+ if (cpu_is_mx25()) {
+ clk_disable(usb_clk);
+ } else if (cpu_is_mx37()) {
+ /* fix USB PHY Power Gating leakage issue for i.MX37 */
+ USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_CHGRDETON;
+ USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_CHGRDETEN;
+ }
+}
+
+static int otg_used = 0;
+
+int usbotg_init(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ struct fsl_xcvr_ops *xops;
+
+ pr_debug("%s: pdev=0x%p pdata=0x%p\n", __func__, pdev, pdata);
+
+ xops = fsl_usb_get_xcvr(pdata->transceiver);
+ if (!xops) {
+ printk(KERN_ERR "DR transceiver ops missing\n");
+ return -EINVAL;
+ }
+ pdata->xcvr_ops = xops;
+ pdata->xcvr_type = xops->xcvr_type;
+ pdata->pdev = pdev;
+
+ if (!otg_used) {
+ if (fsl_check_usbclk() != 0)
+ return -EINVAL;
+
+ pr_debug("%s: grab pins\n", __func__);
+ if (pdata->gpio_usb_active())
+ return -EINVAL;
+
+ if (clk_enable(usb_clk)) {
+ printk(KERN_ERR "clk_enable(usb_clk) failed\n");
+ return -EINVAL;
+ }
+
+ if (xops->init)
+ xops->init(xops);
+
+ UOG_PORTSC1 = UOG_PORTSC1 & ~PORTSC_PHCD;
+ if (xops->xcvr_type == PORTSC_PTS_SERIAL) {
+ if (pdata->operating_mode == FSL_USB2_DR_HOST) {
+ otg_set_serial_host();
+ /* need reset */
+ UOG_USBCMD |= UCMD_RESET;
+ msleep(100);
+ } else if (pdata->operating_mode == FSL_USB2_DR_DEVICE)
+ otg_set_serial_peripheral();
+ otg_set_serial_xcvr();
+ } else if (xops->xcvr_type == PORTSC_PTS_ULPI) {
+ otg_set_ulpi_xcvr();
+ } else if (xops->xcvr_type == PORTSC_PTS_UTMI) {
+ otg_set_utmi_xcvr();
+ }
+ }
+
+ if (usb_register_remote_wakeup(pdev))
+ pr_debug("DR is not a wakeup source.\n");
+
+ otg_used++;
+ pr_debug("%s: success\n", __func__);
+ return 0;
+}
+EXPORT_SYMBOL(usbotg_init);
+
+void usbotg_uninit(struct fsl_usb2_platform_data *pdata)
+{
+ pr_debug("%s\n", __func__);
+
+ otg_used--;
+ if (!otg_used) {
+ if (pdata->xcvr_ops && pdata->xcvr_ops->uninit)
+ pdata->xcvr_ops->uninit(pdata->xcvr_ops);
+
+ pdata->regs = NULL;
+
+ if (machine_is_mx31_3ds()) {
+ if (pdata->xcvr_ops && pdata->xcvr_ops->suspend)
+ pdata->xcvr_ops->suspend(pdata->xcvr_ops);
+ clk_disable(usb_clk);
+ }
+ msleep(1);
+ UOG_PORTSC1 = UOG_PORTSC1 | PORTSC_PHCD;
+ pdata->gpio_usb_inactive();
+ if (pdata->xcvr_type == PORTSC_PTS_SERIAL)
+ clk_disable(usb_clk);
+ clk_disable(usb_ahb_clk);
+ }
+}
+EXPORT_SYMBOL(usbotg_uninit);
+
+int usb_host_wakeup_irq(struct device *wkup_dev)
+{
+ int wakeup_req = 0;
+ struct fsl_usb2_platform_data *pdata = wkup_dev->platform_data;
+
+ if (!strcmp("Host 1", pdata->name)) {
+ wakeup_req = USBCTRL & UCTRL_H1WIR;
+ } else if (!strcmp("DR", pdata->name)) {
+ wakeup_req = USBCTRL & UCTRL_OWIR;
+ /* If DR is in device mode, let udc handle it */
+ if (wakeup_req && ((UOG_USBMODE & 0x3) == 0x2))
+ wakeup_req = 0;
+ }
+
+ return wakeup_req;
+}
+EXPORT_SYMBOL(usb_host_wakeup_irq);
+
+void usb_host_set_wakeup(struct device *wkup_dev, bool para)
+{
+ struct fsl_usb2_platform_data *pdata = wkup_dev->platform_data;
+
+ /* If this device may wakeup */
+ if (device_may_wakeup(wkup_dev) && para) {
+ if (!strcmp("Host 1", pdata->name)) {
+ USBCTRL |= UCTRL_H1WIE;
+ } else if (!strcmp("DR", pdata->name)) {
+ USBCTRL |= UCTRL_OWIE;
+ /* Enable OTG ID Wakeup */
+ USBCTRL_HOST2 |= (1 << 5);
+ }
+ }
+
+ if (!para) {
+ if (!strcmp("Host 1", pdata->name))
+ USBCTRL &= ~UCTRL_H1WIE;
+ else if (!strcmp("DR", pdata->name)) {
+ USBCTRL &= ~UCTRL_OWIE;
+ USBCTRL_HOST2 &= ~(1 << 5);
+ }
+ }
+}
+EXPORT_SYMBOL(usb_host_set_wakeup);
diff --git a/arch/arm/plat-mxc/utmixc.c b/arch/arm/plat-mxc/utmixc.c
new file mode 100644
index 000000000000..308d79344a01
--- /dev/null
+++ b/arch/arm/plat-mxc/utmixc.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <linux/pmic_external.h>
+
+#include <mach/hardware.h>
+#include <mach/arc_otg.h>
+#include <asm/mach-types.h>
+
+static struct regulator *usbotg_regux;
+
+static void usb_utmi_init(struct fsl_xcvr_ops *this)
+{
+#if defined(CONFIG_MXC_PMIC_MC13892_MODULE) || defined(CONFIG_MXC_PMIC_MC13892)
+ if (machine_is_mx51_3ds()) {
+ unsigned int value;
+
+ /* VUSBIN */
+ pmic_read_reg(REG_USB1, &value, 0xffffff);
+ value |= 0x1;
+ value |= (0x1 << 3);
+ pmic_write_reg(REG_USB1, value, 0xffffff);
+ }
+#endif
+}
+
+static void usb_utmi_uninit(struct fsl_xcvr_ops *this)
+{
+}
+
+/*!
+ * set vbus power
+ *
+ * @param view viewport register
+ * @param on power on or off
+ */
+static void set_power(struct fsl_xcvr_ops *this,
+ struct fsl_usb2_platform_data *pdata, int on)
+{
+ struct device *dev = &pdata->pdev->dev;
+
+ pr_debug("real %s(on=%d) pdata=0x%p\n", __func__, on, pdata);
+ if (machine_is_mx37_3ds()) {
+ if (on) {
+ if (!board_is_mx37(BOARD_REV_2))
+ usbotg_regux = regulator_get(dev, "DCDC2");
+ else
+ usbotg_regux = regulator_get(dev, "SWBST");
+
+ regulator_enable(usbotg_regux);
+ } else {
+ regulator_disable(usbotg_regux);
+ regulator_put(usbotg_regux);
+ }
+ }
+}
+
+static struct fsl_xcvr_ops utmi_ops = {
+ .name = "utmi",
+ .xcvr_type = PORTSC_PTS_UTMI,
+ .init = usb_utmi_init,
+ .uninit = usb_utmi_uninit,
+ .set_vbus_power = set_power,
+};
+
+extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops);
+
+static int __init utmixc_init(void)
+{
+ fsl_usb_xcvr_register(&utmi_ops);
+ return 0;
+}
+
+extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops);
+
+static void __exit utmixc_exit(void)
+{
+ fsl_usb_xcvr_unregister(&utmi_ops);
+}
+
+module_init(utmixc_init);
+module_exit(utmixc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("utmi xcvr driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-mxc/wdog.c b/arch/arm/plat-mxc/wdog.c
new file mode 100644
index 000000000000..80dd1bd31be9
--- /dev/null
+++ b/arch/arm/plat-mxc/wdog.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file plat-mxc/wdog.c
+ * @brief This file contains watchdog timer implementations.
+ *
+ * This file contains watchdog timer implementations for timer tick.
+ *
+ * @ingroup WDOG
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#define WDOG_WT 0x8 /* WDOG WT starting bit inside WCR */
+#define WCR_WOE_BIT (1 << 6)
+#define WCR_WDA_BIT (1 << 5)
+#define WCR_SRS_BIT (1 << 4)
+#define WCR_WRE_BIT (1 << 3)
+#define WCR_WDE_BIT (1 << 2)
+#define WCR_WDBG_BIT (1 << 1)
+#define WCR_WDZST_BIT (1 << 0)
+
+/*
+ * WatchDog
+ */
+#define WDOG_WCR 0 /* 16bit watchdog control reg */
+#define WDOG_WSR 2 /* 16bit watchdog service reg */
+#define WDOG_WRSR 4 /* 16bit watchdog reset status reg */
+
+/*!
+ * The base addresses for the WDOG modules
+ */
+static unsigned long wdog_base[2] = {
+ IO_ADDRESS(WDOG1_BASE_ADDR),
+#ifdef WDOG2_BASE_ADDR
+ IO_ADDRESS(WDOG2_BASE_ADDR),
+#endif
+};
+
+void mxc_wd_reset(void)
+{
+ u16 reg;
+ struct clk *clk;
+
+ clk = clk_get(NULL, "wdog_clk");
+ clk_enable(clk);
+
+ reg = __raw_readw(wdog_base[0] + WDOG_WCR) & ~WCR_SRS_BIT;
+ reg |= WCR_WDE_BIT;
+ __raw_writew(reg, wdog_base[0] + WDOG_WCR);
+}
diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig
index 31656c33e05e..0aa9fe1362b9 100644
--- a/arch/arm/plat-s3c/Kconfig
+++ b/arch/arm/plat-s3c/Kconfig
@@ -8,7 +8,6 @@ config PLAT_S3C
bool
depends on ARCH_S3C2410
default y if ARCH_S3C2410
- select NO_IOPORT
help
Base platform code for any Samsung S3C device
diff --git a/arch/arm/plat-s3c/include/plat/regs-adc.h b/arch/arm/plat-s3c/include/plat/regs-adc.h
index 4323cccc86cd..f69027842b89 100644
--- a/arch/arm/plat-s3c/include/plat/regs-adc.h
+++ b/arch/arm/plat-s3c/include/plat/regs-adc.h
@@ -19,6 +19,7 @@
#define S3C2410_ADCDLY S3C2410_ADCREG(0x08)
#define S3C2410_ADCDAT0 S3C2410_ADCREG(0x0C)
#define S3C2410_ADCDAT1 S3C2410_ADCREG(0x10)
+#define S3C2410_ADCMUX S3C2410_ADCREG(0x18)
/* ADCCON Register Bits */
diff --git a/arch/arm/plat-s3c/include/plat/regs-serial.h b/arch/arm/plat-s3c/include/plat/regs-serial.h
index a0daa647b92c..c031332a9ba8 100644
--- a/arch/arm/plat-s3c/include/plat/regs-serial.h
+++ b/arch/arm/plat-s3c/include/plat/regs-serial.h
@@ -100,6 +100,8 @@
#define S3C2443_UCON_RXERR_IRQEN (1<<6)
#define S3C2443_UCON_LOOPBACK (1<<5)
+#define S3C2443_UCON_TXMODEMASK (3<<2)
+
#define S3C2410_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
S3C2410_UCON_RXILEVEL | \
S3C2410_UCON_TXIRQMODE | \
@@ -216,6 +218,19 @@ struct s3c2410_uartcfg {
struct s3c24xx_uart_clksrc *clocks;
unsigned int clocks_size;
+
+ /*
+ * Values for configuring the GPIOs of the different ports
+ * Luis Galdos
+ */
+ unsigned int rx_gpio;
+ unsigned int rx_cfg;
+ unsigned int tx_gpio;
+ unsigned int tx_cfg;
+ unsigned int cts_gpio;
+ unsigned int cts_cfg;
+ unsigned int rts_gpio;
+ unsigned int rts_cfg;
};
/* s3c24xx_uart_devs
@@ -224,7 +239,7 @@ struct s3c2410_uartcfg {
* or platform_add_device() before the console_initcall()
*/
-extern struct platform_device *s3c24xx_uart_devs[3];
+extern struct platform_device *s3c24xx_uart_devs[];
#endif /* __ASSEMBLY__ */
diff --git a/arch/arm/plat-s3c/include/plat/uncompress.h b/arch/arm/plat-s3c/include/plat/uncompress.h
index 4df006b9cc10..05c0dbd6baea 100644
--- a/arch/arm/plat-s3c/include/plat/uncompress.h
+++ b/arch/arm/plat-s3c/include/plat/uncompress.h
@@ -64,26 +64,28 @@ uart_rd(unsigned int reg)
static void putc(int ch)
{
- if (uart_rd(S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE) {
- int level;
+ if (uart_rd(S3C2410_UCON) & S3C2443_UCON_TXMODEMASK) {
+ if (uart_rd(S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE) {
+ int level;
- while (1) {
- level = uart_rd(S3C2410_UFSTAT);
- level &= fifo_mask;
+ while (1) {
+ level = uart_rd(S3C2410_UFSTAT);
+ level &= fifo_mask;
- if (level < fifo_max)
- break;
- }
+ if (level < fifo_max)
+ break;
+ }
- } else {
- /* not using fifos */
+ } else {
+ /* not using fifos */
- while ((uart_rd(S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE) != S3C2410_UTRSTAT_TXE)
- barrier();
- }
+ while ((uart_rd(S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE) != S3C2410_UTRSTAT_TXE)
+ barrier();
+ }
- /* write byte to transmission register */
- uart_wr(S3C2410_UTXH, ch);
+ /* write byte to transmission register */
+ uart_wr(S3C2410_UTXH, ch);
+ }
}
static inline void flush(void)
diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig
index 0af3872fb763..0f9fd357045c 100644
--- a/arch/arm/plat-s3c24xx/Kconfig
+++ b/arch/arm/plat-s3c24xx/Kconfig
@@ -8,7 +8,6 @@ config PLAT_S3C24XX
bool
depends on ARCH_S3C2410
default y if ARCH_S3C2410
- select NO_IOPORT
select ARCH_REQUIRE_GPIOLIB
help
Base platform code for any Samsung S3C24XX device
@@ -49,6 +48,13 @@ config S3C2410_DMA_DEBUG
Enable debugging output for the DMA code. This option sends info
to the kernel log, at priority KERN_DEBUG.
+config DEFAULT_GPIOLIB
+ bool "Default GPIOLIB support"
+ depends on ARCH_S3C2410
+ default n
+ help
+ Provides the default GPIOLIB support instead of a machine specific one.
+
config MACH_SMDK
bool
help
diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile
index d82767b2b833..573ad6894de7 100644
--- a/arch/arm/plat-s3c24xx/Makefile
+++ b/arch/arm/plat-s3c24xx/Makefile
@@ -16,7 +16,7 @@ obj-y += cpu.o
obj-y += irq.o
obj-y += devs.o
obj-y += gpio.o
-obj-y += gpiolib.o
+obj-$(CONFIG_DEFAULT_GPIOLIB) += gpiolib.o
obj-y += time.o
obj-y += clock.o
obj-y += pwm-clock.o
diff --git a/arch/arm/plat-s3c24xx/cpu.c b/arch/arm/plat-s3c24xx/cpu.c
index 22a329513c0f..23299da6418e 100644
--- a/arch/arm/plat-s3c24xx/cpu.c
+++ b/arch/arm/plat-s3c24xx/cpu.c
@@ -285,7 +285,7 @@ void __init s3c24xx_init_clocks(int xtal)
static int nr_uarts __initdata = 0;
-static struct s3c2410_uartcfg uart_cfgs[3];
+static struct s3c2410_uartcfg uart_cfgs[4];
/* s3c24xx_init_uartdevs
*
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index e93f8bf6d338..3b5ae72c9faa 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -76,6 +76,19 @@ static struct resource s3c2410_uart2_resource[] = {
}
};
+static struct resource s3c2410_uart3_resource[] = {
+ [0] = {
+ .start = S3C2443_PA_UART3,
+ .end = S3C2443_PA_UART3 + 0x3fff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3C2443_RX3,
+ .end = IRQ_S3C2443_ERR3,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = {
[0] = {
.resources = s3c2410_uart0_resource,
@@ -89,6 +102,11 @@ struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = {
.resources = s3c2410_uart2_resource,
.nr_resources = ARRAY_SIZE(s3c2410_uart2_resource),
},
+ [3] = {
+ .resources = s3c2410_uart3_resource,
+ .nr_resources = ARRAY_SIZE(s3c2410_uart3_resource),
+ }
+
};
/* yart devices */
@@ -105,13 +123,18 @@ static struct platform_device s3c24xx_uart_device2 = {
.id = 2,
};
-struct platform_device *s3c24xx_uart_src[3] = {
+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[3] = {
+struct platform_device *s3c24xx_uart_devs[4] = {
};
/* USB Host Controller */
diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c
index 1baf941d1930..cabb6a3e8217 100644
--- a/arch/arm/plat-s3c24xx/dma.c
+++ b/arch/arm/plat-s3c24xx/dma.c
@@ -26,15 +26,15 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/delay.h>
-#include <linux/io.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <mach/hardware.h>
+#include <asm/io.h>
#include <asm/dma.h>
#include <asm/mach/dma.h>
-#include <mach/map.h>
+#include <asm/mach/map.h>
#include <plat/dma.h>
@@ -75,6 +75,8 @@ dma_wrreg(struct s3c2410_dma_chan *chan, int reg, unsigned long val)
struct s3c2410_dma_regstate {
unsigned long dcsrc;
unsigned long disrc;
+ unsigned long dcdst;
+ unsigned long didst;
unsigned long dstat;
unsigned long dcon;
unsigned long dmsktrig;
@@ -92,6 +94,8 @@ dmadbg_capture(struct s3c2410_dma_chan *chan, struct s3c2410_dma_regstate *regs)
{
regs->dcsrc = dma_rdreg(chan, S3C2410_DMA_DCSRC);
regs->disrc = dma_rdreg(chan, S3C2410_DMA_DISRC);
+ regs->dcdst = dma_rdreg(chan, S3C2410_DMA_DCDST);
+ regs->didst = dma_rdreg(chan, S3C2410_DMA_DIDST);
regs->dstat = dma_rdreg(chan, S3C2410_DMA_DSTAT);
regs->dcon = dma_rdreg(chan, S3C2410_DMA_DCON);
regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
@@ -101,10 +105,11 @@ static void
dmadbg_dumpregs(const char *fname, int line, struct s3c2410_dma_chan *chan,
struct s3c2410_dma_regstate *regs)
{
- printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n",
- chan->number, fname, line,
- regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig,
- regs->dcon);
+
+ printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DCDST=%08lx, DIDST=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n",
+ chan->number, fname, line,
+ regs->dcsrc, regs->disrc, regs->dcdst, regs->didst, regs->dstat, regs->dmsktrig,
+ regs->dcon);
}
static void
@@ -266,6 +271,15 @@ s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan,
writel(buf->data, chan->addr_reg);
+
+ /*
+ * DONT use the reload feature cause when we have more than one buffer in the
+ * queue, we can't update the initial data pointer for the next transfer. This
+ * causes that the DMA-controller writes two times into the same DMA-buffer
+ * and we get corrupted data
+ * (Luis Galdos)
+ */
+ reload = S3C2410_DCON_NORELOAD;
dma_wrreg(chan, S3C2410_DMA_DCON,
chan->dcon | reload | (buf->size/chan->xfer_unit));
@@ -398,6 +412,7 @@ static int s3c2410_dma_start(struct s3c2410_dma_chan *chan)
* the first buffer is finished, the new one will be loaded onto
* the channel */
+#if 0
if (chan->next != NULL) {
if (chan->load_state == S3C2410_DMALOAD_1LOADED) {
@@ -413,7 +428,7 @@ static int s3c2410_dma_start(struct s3c2410_dma_chan *chan)
s3c2410_dma_loadbuffer(chan, chan->next);
}
}
-
+#endif
local_irq_restore(flags);
@@ -424,7 +439,12 @@ static int s3c2410_dma_start(struct s3c2410_dma_chan *chan)
*
* work out if we can queue another buffer into the DMA engine
*/
-
+/*
+ * The auto reload feature was giving some problems when sending different
+ * DMA-buffers.
+ * (Luis Galdos)
+ */
+#if 0
static int
s3c2410_dma_canload(struct s3c2410_dma_chan *chan)
{
@@ -434,6 +454,7 @@ s3c2410_dma_canload(struct s3c2410_dma_chan *chan)
return 0;
}
+#endif
/* s3c2410_dma_enqueue
*
@@ -508,27 +529,27 @@ int s3c2410_dma_enqueue(unsigned int channel, void *id,
chan->next = buf;
/* check to see if we can load a buffer */
- if (chan->state == S3C2410_DMA_RUNNING) {
- if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) {
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
- printk(KERN_ERR "dma%d: loadbuffer:"
- "timeout loading buffer\n",
- chan->number);
- dbg_showchan(chan);
- local_irq_restore(flags);
- return -EINVAL;
- }
- }
-
- while (s3c2410_dma_canload(chan) && chan->next != NULL) {
- s3c2410_dma_loadbuffer(chan, chan->next);
- }
- } else if (chan->state == S3C2410_DMA_IDLE) {
+/* if (chan->state == S3C2410_DMA_RUNNING) { */
+/* if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { */
+/* if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { */
+/* printk(KERN_ERR "dma%d: loadbuffer:" */
+/* "timeout loading buffer\n", */
+/* chan->number); */
+/* dbg_showchan(chan); */
+/* local_irq_restore(flags); */
+/* return -EINVAL; */
+/* } */
+/* } */
+
+/* while (s3c2410_dma_canload(chan) && chan->next != NULL) { */
+/* s3c2410_dma_loadbuffer(chan, chan->next); */
+/* } */
+/* } else if (chan->state == S3C2410_DMA_IDLE) { */
if (chan->flags & S3C2410_DMAF_AUTOSTART) {
s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,
S3C2410_DMAOP_START);
}
- }
+/* } */
local_irq_restore(flags);
return 0;
@@ -569,12 +590,12 @@ s3c2410_dma_lastxfer(struct s3c2410_dma_chan *chan)
break;
case S3C2410_DMALOAD_1LOADED:
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
- /* flag error? */
- printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n",
- chan->number, __func__);
- return;
- }
+ //if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
+ // /* flag error? */
+ // printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n",
+ // chan->number, __func__);
+ // return;
+ //}
break;
case S3C2410_DMALOAD_1LOADED_1RUNNING:
@@ -678,6 +699,7 @@ s3c2410_dma_irq(int irq, void *devpw)
if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) {
unsigned long flags;
+ unsigned long tmp;
switch (chan->load_state) {
case S3C2410_DMALOAD_1RUNNING:
@@ -689,12 +711,13 @@ s3c2410_dma_irq(int irq, void *devpw)
break;
case S3C2410_DMALOAD_1LOADED:
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
- /* flag error? */
- printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n",
- chan->number, __func__);
- return IRQ_HANDLED;
- }
+ /* printk(KERN_INFO "DMA: 1 LOADED\n"); */
+ //if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
+ // /* flag error? */
+ // printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n",
+ // chan->number, __func__);
+ // return IRQ_HANDLED;
+ //}
break;
@@ -707,9 +730,21 @@ s3c2410_dma_irq(int irq, void *devpw)
return IRQ_HANDLED;
}
+ /*
+ * Since we are not using the auto-reload feature, we must restart
+ * the DMA-channel at this point
+ * (Luis Galdos)
+ */
local_irq_save(flags);
+ chan->load_state = S3C2410_DMALOAD_NONE;
s3c2410_dma_loadbuffer(chan, chan->next);
+ tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
+ tmp &= ~S3C2410_DMASKTRIG_STOP;
+ tmp |= S3C2410_DMASKTRIG_ON;
+ dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp);
+ s3c2410_dma_call_op(chan, S3C2410_DMAOP_START);
local_irq_restore(flags);
+
} else {
s3c2410_dma_lastxfer(chan);
@@ -1237,7 +1272,7 @@ int s3c2410_dma_getposition(dmach_t channel, dma_addr_t *src, dma_addr_t *dst)
EXPORT_SYMBOL(s3c2410_dma_getposition);
-static struct s3c2410_dma_chan *to_dma_chan(struct sys_device *dev)
+static inline struct s3c2410_dma_chan *to_dma_chan(struct sys_device *dev)
{
return container_of(dev, struct s3c2410_dma_chan, dev);
}
diff --git a/arch/arm/plat-s3c24xx/include/plat/hsmmc.h b/arch/arm/plat-s3c24xx/include/plat/hsmmc.h
new file mode 100644
index 000000000000..4fe4d20a3ff9
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/plat/hsmmc.h
@@ -0,0 +1,30 @@
+#ifndef __ASM_S3C_HSMMC_H
+#define __ASM_S3C_HSMMC_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/mmc/host.h>
+
+struct s3c_hsmmc_cfg {
+ u32 hwport; /* hardware port number */
+ u32 host_caps; /* host capabilities */
+ u32 bus_width; /* host capabilities */
+
+ void *base; /* base address of host */
+ ulong ctrl3[2]; /* preset value of control3 reg */
+ char *clk_name[3]; /* HS-MMC has 3 clock source */
+ void (*set_gpio)(void);
+
+
+ /* GPIOs for additional features */
+ unsigned int wprotect_invert : 1;
+ unsigned int detect_invert : 1; /* set => detect active high. */
+ unsigned int gpio_detect;
+ unsigned int gpio_wprotect;
+ unsigned int gpio_led;
+};
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_S3C_HSMMC_H */
+
diff --git a/arch/arm/plat-s3c24xx/include/plat/pcmcia.h b/arch/arm/plat-s3c24xx/include/plat/pcmcia.h
new file mode 100644
index 000000000000..c3ec7ee7c0e4
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/plat/pcmcia.h
@@ -0,0 +1,25 @@
+/* linux/include/asm-arm/arch-s3c2410/udc.h
+ *
+ * Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Changelog:
+ * 14-Mar-2005 RTP Created file
+ * 02-Aug-2005 RTP File rename
+ * 07-Sep-2005 BJD Minor cleanups, changed cmd to enum
+ * 18-Jan-2007 HMW Add per-platform vbus_draw function
+*/
+
+#ifndef __ASM_ARM_ARCH_PCMCIA_H
+#define __ASM_ARM_ARCH_PCMCIA_H
+
+struct s3c2443_pcmcia_pdata {
+ unsigned int gpio_detect;
+};
+
+#endif /* __ASM_ARM_ARCH_PCMCIA_H */
diff --git a/arch/arm/plat-s3c24xx/include/plat/ts.h b/arch/arm/plat-s3c24xx/include/plat/ts.h
new file mode 100644
index 000000000000..5563cb488d3d
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/plat/ts.h
@@ -0,0 +1,33 @@
+/* drivers/input/touchsrceen/elfin_ts.h
+ *
+ * Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Changelog:
+ * 24-Mar-2005 RTP Created file
+ */
+
+#ifndef __ASM_ARM_ELFIN_TS_H
+#define __ASM_ARM_FLFIN_TS_H
+
+struct s3c_ts_mach_info {
+ int delay;
+ int presc;
+ int oversampling_shift;
+ int probes;
+ int trigger_ms;
+ unsigned long xmin;
+ unsigned long xmax;
+ unsigned long ymin;
+ unsigned long ymax;
+};
+
+void __init set_s3c_ts_info(struct s3c_ts_mach_info *hard_s3c_ts_info);
+
+#endif /* __ASM_ARM_ELFIN_TS_H */
+
diff --git a/arch/arm/plat-s3c24xx/include/plat/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h
new file mode 100644
index 000000000000..b8aa6cb69b58
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/plat/udc.h
@@ -0,0 +1,36 @@
+/* linux/include/asm-arm/arch-s3c2410/udc.h
+ *
+ * Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Changelog:
+ * 14-Mar-2005 RTP Created file
+ * 02-Aug-2005 RTP File rename
+ * 07-Sep-2005 BJD Minor cleanups, changed cmd to enum
+ * 18-Jan-2007 HMW Add per-platform vbus_draw function
+*/
+
+#ifndef __ASM_ARM_ARCH_UDC_H
+#define __ASM_ARM_ARCH_UDC_H
+
+enum s3c2410_udc_cmd_e {
+ S3C2410_UDC_P_ENABLE = 1, /* Pull-up enable */
+ S3C2410_UDC_P_DISABLE = 2, /* Pull-up disable */
+ S3C2410_UDC_P_RESET = 3, /* UDC reset, in case of */
+};
+
+struct s3c2410_udc_mach_info {
+ void (*udc_command)(enum s3c2410_udc_cmd_e);
+ void (*vbus_draw)(unsigned int ma);
+ unsigned int vbus_pin;
+ unsigned char vbus_pin_inverted;
+};
+
+extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);
+
+#endif /* __ASM_ARM_ARCH_UDC_H */
diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c
index 963f7a4f26f2..a5040b163c18 100644
--- a/arch/arm/plat-s3c24xx/irq.c
+++ b/arch/arm/plat-s3c24xx/irq.c
@@ -253,6 +253,9 @@ s3c_irqext_type(unsigned int irq, unsigned int type)
void __iomem *gpcon_reg;
unsigned long gpcon_offset, extint_offset;
unsigned long newvalue = 0, value;
+#if defined(CONFIG_CPU_S3C2443)
+ unsigned int t;
+#endif
if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
{
@@ -322,6 +325,49 @@ s3c_irqext_type(unsigned int irq, unsigned int type)
}
value = __raw_readl(extint_reg);
+
+ /* According to Samsung's document 'S3C2443 guide to extra gpio'
+ * several registers (including EXTINT0-2) must use a precompiled
+ * function for read access. We don't have the original code but
+ * we have seen the following behavior in U-Boot:
+ * EXTINT0:
+ * 1. Read values are given in big endian (at nibble level):
+ * a read value of 0x00001234 is really 0x43210000.
+ * 2. The read value does not return the LSB:
+ * after writing 0xffffffff value read is 0x7fffffff.
+ * EXTINT1/2:
+ * 1. Read values are given in big endian (at nibble level)
+ * and bits at nibble level suffer a 1 bit rotation to the left:
+ * after writing 0x12488822 value read is 0x44111842
+ */
+#if defined(CONFIG_CPU_S3C2443)
+ t = (value & 0x0000000f) << 28;
+ t |= (value & 0x000000f0) << 20;
+ t |= (value & 0x00000f00) << 12;
+ t |= (value & 0x0000f000) << 4;
+ t |= (value & 0x000f0000) >> 4;
+ t |= (value & 0x00f00000) >> 12;
+ t |= (value & 0x0f000000) >> 20;
+ t |= (value & 0xf0000000) >> 28;
+ value = t;
+
+ if (S3C24XX_EXTINT1 == extint_reg ||
+ S3C24XX_EXTINT2 == extint_reg) {
+ /* Swap bits in nibbles */
+ unsigned char old_n, new_n;
+ int i;
+
+ t = 0;
+ for (i=0; i < 8; i++) {
+ old_n = (value & (0xf << (4*i))) >> (4*i);
+ new_n = ((old_n & 0xe) >> 1);
+ if (old_n & 0x1)
+ new_n |= 0x8;
+ t |= new_n << (4*i);
+ }
+ value = t;
+ }
+#endif /* CONFIG_CPU_S3C2443 */
value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
__raw_writel(value, extint_reg);
@@ -729,7 +775,6 @@ void __init s3c24xx_init_irq(void)
}
/* setup the cascade irq handlers */
-
set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
@@ -786,5 +831,10 @@ void __init s3c24xx_init_irq(void)
set_irq_flags(irqno, IRQF_VALID);
}
+ /* Register CF/ATA interrupt */
+ set_irq_chip(IRQ_S3C2443_CFCON, &s3c_irq_level_chip);
+ set_irq_handler(IRQ_S3C2443_CFCON, handle_level_irq);
+ set_irq_flags(IRQ_S3C2443_CFCON, IRQF_VALID);
+
irqdbf("s3c2410: registered interrupt handlers\n");
}
diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c
index 8efb57ad5019..6462ef6a7a9c 100644
--- a/arch/arm/plat-s3c24xx/pm.c
+++ b/arch/arm/plat-s3c24xx/pm.c
@@ -43,13 +43,19 @@
#include <plat/regs-serial.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
+#include <mach/regs-gpioj.h>
#include <mach/regs-mem.h>
#include <mach/regs-irq.h>
-
+#include <mach/gpio.h>
#include <asm/mach/time.h>
+#include <asm/mach/irq.h>
#include <plat/pm.h>
+/* For the S3C2443 (Luis Galdos) */
+#include <mach/regs-s3c2443-clock.h>
+#include <mach/regs-s3c2443-mem.h>
+
/* for external use */
unsigned long s3c_pm_flags;
@@ -119,6 +125,55 @@ static struct sleep_save misc_save[] = {
SAVE_ITEM(S3C2410_DCLKCON),
};
+/* Registers for the S3C2443 machines */
+static struct sleep_save s3c2443_core_regs[] = {
+ SAVE_ITEM(S3C2443_LOCKCON0),
+ SAVE_ITEM(S3C2443_LOCKCON1),
+ SAVE_ITEM(S3C2443_OSCSET),
+ SAVE_ITEM(S3C2443_MPLLCON),
+ SAVE_ITEM(S3C2443_EPLLCON),
+ SAVE_ITEM(S3C2443_CLKSRC),
+ SAVE_ITEM(S3C2443_CLKDIV0),
+ SAVE_ITEM(S3C2443_CLKDIV1),
+ SAVE_ITEM(S3C2443_HCLKCON),
+ SAVE_ITEM(S3C2443_PCLKCON),
+ SAVE_ITEM(S3C2443_SCLKCON),
+ SAVE_ITEM(S3C2443_BANKCFG),
+ SAVE_ITEM(S3C2443_BANKCON1),
+ SAVE_ITEM(S3C2443_BANKCON2),
+ SAVE_ITEM(S3C2443_BANKCON3),
+ SAVE_ITEM(S3C2443_REFRESH),
+ SAVE_ITEM(S3C2443_TIMEOUT),
+};
+
+static struct gpio_sleep s3c2443_main_gpios[] = {
+ {
+ .base = S3C2443_GPACDL,
+ }, {
+ .base = S3C2443_GPACDH,
+ }, {
+ .base = S3C2410_GPBCON,
+ }, {
+ .base = S3C2410_GPCCON,
+ }, {
+ .base = S3C2410_GPDCON,
+ }, {
+ .base = S3C2410_GPECON,
+ }, {
+ .base = S3C2410_GPFCON,
+ }, {
+ .base = S3C2410_GPGCON,
+ }, {
+ .base = S3C2410_GPHCON,
+ }, {
+ .base = S3C2440_GPJCON,
+ }, {
+ .base = S3C2443_GPLCON,
+ }, {
+ .base = S3C2443_GPMCON,
+ }
+};
+
#ifdef CONFIG_S3C2410_PM_DEBUG
#define SAVE_UART(va) \
@@ -493,25 +548,39 @@ static void s3c2410_pm_configure_extint(void)
#define OFFS_DAT (S3C2410_GPADAT - S3C2410_GPACON)
#define OFFS_UP (S3C2410_GPBUP - S3C2410_GPBCON)
-/* s3c2410_pm_save_gpios()
+/* s3c24xx_pm_save_gpios()
*
* Save the state of the GPIOs
*/
-
-static void s3c2410_pm_save_gpios(void)
+static void s3c24xx_pm_save_gpios(struct gpio_sleep *gps, int count)
{
- struct gpio_sleep *gps = gpio_save;
- unsigned int gpio;
+ int gpio;
- for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) {
+ for (gpio = 0; gpio < count; gpio++, gps++) {
void __iomem *base = gps->base;
+ /*
+ * By the S3C2443 the GPIOs of the port A only have a configuration
+ * register and a special read function
+ */
+ if (base == S3C2443_GPACDL || base == S3C2443_GPACDH) {
+ unsigned int pin;
+
+ if (base == S3C2443_GPACDL)
+ pin = S3C2410_GPA1;
+ else
+ pin = S3C2410_GPA8;
+
+ gps->gpcon = s3c2443_gpio_read_porta(pin);
+ continue;
+ }
+
gps->gpcon = __raw_readl(base + OFFS_CON);
gps->gpdat = __raw_readl(base + OFFS_DAT);
- if (gpio > 0)
+ /* The port GPACON doesn't have a up register */
+ if (gps->base != S3C2410_GPACON)
gps->gpup = __raw_readl(base + OFFS_UP);
-
}
}
@@ -563,7 +632,7 @@ static inline int is_out(unsigned long con)
* state for when it is next output.
*/
-static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps)
+static void s3c24xx_pm_restore_gpio(int index, struct gpio_sleep *gps)
{
void __iomem *base = gps->base;
unsigned long gps_gpcon = gps->gpcon;
@@ -577,6 +646,17 @@ static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps)
old_gpcon = __raw_readl(base + OFFS_CON);
old_gpdat = __raw_readl(base + OFFS_DAT);
+ /* The GPIOs of the port A need a different handling */
+ if (base == S3C2443_GPACDL || base == S3C2443_GPACDH) {
+ gpcon = gps->gpcon;
+ __raw_writel(gpcon, base + OFFS_CON);
+
+ DBG("GPACD%c CON %08lx => %08lx\n",
+ (base == S3C2443_GPACDH) ? 'H' : 'L',
+ old_gpcon, gpcon);
+ return;
+ }
+
if (base == S3C2410_GPACON) {
/* GPACON only has one bit per control / data and no PULLUPs.
* GPACON[x] = 0 => Output, 1 => SFN */
@@ -645,23 +725,21 @@ static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps)
__raw_writel(gps->gpup, base + OFFS_UP);
}
- DBG("GPIO[%d] CON %08lx => %08lx, DAT %08lx => %08lx\n",
+ DBG("GPIO[%02d] CON %08lx => %08lx, DAT %08lx => %08lx\n",
index, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
}
-/** s3c2410_pm_restore_gpios()
+/** s3c24xx_pm_restore_gpios()
*
* Restore the state of the GPIOs
*/
-
-static void s3c2410_pm_restore_gpios(void)
+static void s3c24xx_pm_restore_gpios(struct gpio_sleep *gps, int count)
{
- struct gpio_sleep *gps = gpio_save;
int gpio;
- for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) {
- s3c2410_pm_restore_gpio(gpio, gps);
+ for (gpio = 0; gpio < count; gpio++, gps++) {
+ s3c24xx_pm_restore_gpio(gpio, gps);
}
}
@@ -714,7 +792,7 @@ static int s3c2410_pm_enter(suspend_state_t state)
/* save all necessary core registers not covered by the drivers */
- s3c2410_pm_save_gpios();
+ s3c24xx_pm_save_gpios(gpio_save, ARRAY_SIZE(gpio_save));
s3c2410_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save));
s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save));
@@ -767,7 +845,7 @@ static int s3c2410_pm_enter(suspend_state_t state)
s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
s3c2410_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));
s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save));
- s3c2410_pm_restore_gpios();
+ s3c24xx_pm_restore_gpios(gpio_save, ARRAY_SIZE(gpio_save));
s3c2410_pm_debug_init();
@@ -793,8 +871,220 @@ static int s3c2410_pm_enter(suspend_state_t state)
return 0;
}
-static struct platform_suspend_ops s3c2410_pm_ops = {
- .enter = s3c2410_pm_enter,
+
+/* Set all the GPIOS as INPUT, but not the configured as wakeup */
+static void s3c2443_pm_input_gpios(struct gpio_sleep *gps, int count)
+{
+ int gpio;
+
+ for (gpio = 0; gpio < count; gpio++, gps++) {
+ void __iomem *base = gps->base;
+ ulong new_con;
+
+ /* The IOs of the port A are only outputs */
+ if (base == S3C2443_GPACDL || base == S3C2443_GPACDH)
+ continue;
+
+ /* Let the IRQ as they as */
+ if (base == S3C2410_GPFCON || base == S3C2410_GPGCON)
+ continue;
+
+ /* @FIXME: This is really ugly. Need to have a list with the IOs */
+ new_con = 0x00;
+ __raw_writel(new_con, OFFS_CON + base);
+ }
+}
+
+/*
+ * PM enter function for the S3C2443
+ * (Luis Galdos)
+ */
+static int s3c2443_pm_enter(suspend_state_t state)
+{
+ unsigned long regs_save[16];
+ unsigned long intmsk, tomask, eintmsk;
+ unsigned long regval;
+
+ /* ensure the debug is initialised (if enabled) */
+
+ s3c2410_pm_debug_init();
+
+ DBG("s3c2410_pm_enter(%d)\n", state);
+
+ if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {
+ printk(KERN_ERR PFX "error: no cpu sleep functions set\n");
+ return -EINVAL;
+ }
+
+ /* check if we have anything to wake-up with... bad things seem
+ * to happen if you suspend with no wakeup (system will often
+ * require a full power-cycle)
+ */
+ if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
+ !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
+ printk(KERN_ERR PFX "No sources enabled for wake-up! Sleep abort.\n");
+ return -EINVAL;
+ }
+
+ /* prepare check area if configured */
+
+ s3c2410_pm_check_prepare();
+
+ /* store the physical address of the register recovery block */
+
+ s3c2410_sleep_save_phys = virt_to_phys(regs_save);
+
+ DBG("s3c2410_sleep_save_phys=0x%08lx\n", s3c2410_sleep_save_phys);
+
+ /* Save all necessary core registers not covered by the drivers */
+ s3c24xx_pm_save_gpios(s3c2443_main_gpios,
+ ARRAY_SIZE(s3c2443_main_gpios));
+
+ s3c2410_pm_do_save(s3c2443_core_regs, ARRAY_SIZE(s3c2443_core_regs));
+ s3c2410_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
+ s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save));
+
+ /* set the irq configuration for wake */
+ s3c2410_pm_configure_extint();
+ /* Set the IO as input */
+ s3c2443_pm_input_gpios(s3c2443_main_gpios,
+ ARRAY_SIZE(s3c2443_main_gpios));
+
+
+ /* Save the configuration of the interrupt mask */
+ intmsk = __raw_readl(S3C2410_INTMSK);
+ tomask = s3c_irqwake_intmask;
+ DBG("INTMSK : 0x%08lx -> 0x%08lx\n", intmsk, tomask);
+ __raw_writel(tomask, S3C2410_INTMSK);
+
+ eintmsk = __raw_readl(S3C24XX_EINTMASK);
+ tomask = s3c_irqwake_eintmask;
+ DBG("EINTMSK : 0x%08lx -> 0x%08lx\n", eintmsk, tomask);
+ __raw_writel(tomask, S3C24XX_EINTMASK);
+
+ regval = __raw_readl(S3C2410_SRCPND);
+ DBG("SRCPND : 0x%08lx\n", regval);
+ __raw_writel(regval, S3C2410_SRCPND);
+
+ regval = __raw_readl(S3C2410_INTPND);
+ DBG("INTPND : 0x%08lx\n", regval);
+ __raw_writel(regval, S3C2410_INTPND);
+
+ regval = __raw_readl(S3C24XX_EINTPEND);
+ DBG("EINTPDN : 0x%08lx\n", regval);
+ regval = __raw_writel(regval, S3C24XX_EINTPEND);
+
+
+ /* Call the CPU dependent prepare function */
+ pm_cpu_prep();
+
+ /* flush cache back to ram */
+ flush_cache_all();
+
+ /* s3c2410_cpu_save will also act as our return point from when
+ * we resume as it saves its own register state, so use the return
+ * code to differentiate return from save and return from sleep */
+
+ if (s3c2410_cpu_save(regs_save) == 0) {
+
+ flush_cache_all();
+
+#if 0
+/*
+ * When enabled skip the entering of the suspend mode and wait by using
+ * the function mdelay().
+ */
+#define S3C2443_PM_SKIP_SLEEP
+#define S3C2443_PM_SKIP_SLEEP_SECS (10)
+#endif
+
+ /* This is the suspend function of the core */
+#if !defined(S3C2443_PM_SKIP_SLEEP)
+ pm_cpu_sleep();
+#else
+ {
+ int cnt;
+ int secs = S3C2443_PM_SKIP_SLEEP_SECS;
+
+ printk(KERN_INFO "[ SUSPEND ] Skipping the sleep mode\n");
+
+ for (cnt = 0; cnt < secs * 10; cnt++)
+ mdelay(100);
+ }
+#endif
+ } else
+ printk(KERN_ERR "[ ERROR ] Save of CPU registers failed\n");
+
+
+ /* Reinit the CPU */
+ cpu_init();
+
+#ifdef CONFIG_S3C2443_PCMCIA
+ /*
+ * When PCMCIA is enabled, there is a problem when wakeing up from
+ * sleep. this is becasue the register EXTINT(0,1,2) should be saved
+ * before going to sleep and restored after the wake up interrupt.
+ * On the S3C2443 there is a problem to read that register being
+ * necessary to use a special access sequence.
+ * That sequence is unknown at the moment and, for that reason, we
+ * just reconfigure here the card detect gpio line.
+ * This hack should be removed in the near future and some additional
+ * code should be added here to save/restore some of the external
+ * interrupt registers.
+ */
+ set_irq_type(62, IRQ_TYPE_EDGE_BOTH);
+#endif
+
+ /* These functions are normally called below */
+ s3c2410_pm_do_restore_core(s3c2443_core_regs,
+ ARRAY_SIZE(s3c2443_core_regs));
+ s3c2410_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));
+ s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save));
+
+ /* Restore the configuration of the GPIOs */
+ s3c24xx_pm_restore_gpios(s3c2443_main_gpios,
+ ARRAY_SIZE(s3c2443_main_gpios));
+
+ s3c2410_pm_debug_init();
+
+ /* Inform which source generates the interrupt */
+ printk("[ WAKEUP ] INTPND : 0x%08x | EINTPND 0x%08x\n",
+ __raw_readl(S3C2410_SRCPND),
+ __raw_readl(S3C24XX_EINTPEND));
+
+ /* Restore the interrupts mask register */
+ __raw_writel(intmsk, S3C2410_INTMSK);
+ __raw_writel(eintmsk, S3C24XX_EINTMASK);
+
+ return 0;
+}
+
+/*
+ * This function will call the corresponding PM enter-function by checking the
+ * processor type
+ * (Luis Galdos)
+ */
+static int s3c24xx_pm_enter(suspend_state_t state)
+{
+ int retval, is2443;
+
+ /* @FIXME: This is really ugly (Luis Galdos) */
+#if defined(CONFIG_CPU_S3C2443)
+ is2443 = 1;
+#else
+ is2443 = 0;
+#endif
+
+ if (is2443)
+ retval = s3c2443_pm_enter(state);
+ else
+ retval = s3c2410_pm_enter(state);
+
+ return retval;
+}
+
+static struct platform_suspend_ops s3c24xx_pm_ops = {
+ .enter = s3c24xx_pm_enter,
.valid = suspend_valid_only_mem,
};
@@ -807,8 +1097,8 @@ static struct platform_suspend_ops s3c2410_pm_ops = {
int __init s3c2410_pm_init(void)
{
- printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n");
+ printk("S3C24XX Power Management, (c) 2004 Simtec Electronics\n");
- suspend_set_ops(&s3c2410_pm_ops);
+ suspend_set_ops(&s3c24xx_pm_ops);
return 0;
}
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 43aa2020f85c..85b4c98a2a5f 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: Thu Sep 25 10:10:50 2008
+# Last update: Tue Dec 29 20:50:49 2009
#
# machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number
#
@@ -916,7 +916,7 @@ nxdb500 MACH_NXDB500 NXDB500 905
apf9328 MACH_APF9328 APF9328 906
omap_wipoq MACH_OMAP_WIPOQ OMAP_WIPOQ 907
omap_twip MACH_OMAP_TWIP OMAP_TWIP 908
-palmt650 MACH_PALMT650 PALMT650 909
+treo650 MACH_TREO650 TREO650 909
acumen MACH_ACUMEN ACUMEN 910
xp100 MACH_XP100 XP100 911
fs2410 MACH_FS2410 FS2410 912
@@ -928,7 +928,7 @@ palmt5 MACH_PALMT5 PALMT5 917
palmtc MACH_PALMTC PALMTC 918
omap_apollon MACH_OMAP_APOLLON OMAP_APOLLON 919
mxc30030evb MACH_MXC30030EVB MXC30030EVB 920
-rea_2d MACH_REA_2D REA_2D 921
+rea_cpu2 MACH_REA_2D REA_2D 921
eti3e524 MACH_TI3E524 TI3E524 922
ateb9200 MACH_ATEB9200 ATEB9200 923
auckland MACH_AUCKLAND AUCKLAND 924
@@ -1232,7 +1232,7 @@ ql202b MACH_QL202B QL202B 1226
vpac270 MACH_VPAC270 VPAC270 1227
rd129 MACH_RD129 RD129 1228
htcwizard MACH_HTCWIZARD HTCWIZARD 1229
-xscale_treo680 MACH_XSCALE_TREO680 XSCALE_TREO680 1230
+treo680 MACH_TREO680 TREO680 1230
tecon_tmezon MACH_TECON_TMEZON TECON_TMEZON 1231
zylonite MACH_ZYLONITE ZYLONITE 1233
gene1270 MACH_GENE1270 GENE1270 1234
@@ -1380,7 +1380,7 @@ holon MACH_HOLON HOLON 1377
olip8 MACH_OLIP8 OLIP8 1378
ghi270hg MACH_GHI270HG GHI270HG 1379
davinci_dm6467_evm MACH_DAVINCI_DM6467_EVM DAVINCI_DM6467_EVM 1380
-davinci_dm355_evm MACH_DAVINCI_DM350_EVM DAVINCI_DM350_EVM 1381
+davinci_dm355_evm MACH_DAVINCI_DM355_EVM DAVINCI_DM355_EVM 1381
blackriver MACH_BLACKRIVER BLACKRIVER 1383
sandgate_wp MACH_SANDGATEWP SANDGATEWP 1384
cdotbwsg MACH_CDOTBWSG CDOTBWSG 1385
@@ -1418,10 +1418,10 @@ looxc550 MACH_LOOXC550 LOOXC550 1417
cnty_titan MACH_CNTY_TITAN CNTY_TITAN 1418
app3xx MACH_APP3XX APP3XX 1419
sideoatsgrama MACH_SIDEOATSGRAMA SIDEOATSGRAMA 1420
-palmtreo700p MACH_PALMTREO700P PALMTREO700P 1421
-palmtreo700w MACH_PALMTREO700W PALMTREO700W 1422
-palmtreo750 MACH_PALMTREO750 PALMTREO750 1423
-palmtreo755p MACH_PALMTREO755P PALMTREO755P 1424
+treo700p MACH_TREO700P TREO700P 1421
+treo700w MACH_TREO700W TREO700W 1422
+treo750 MACH_TREO750 TREO750 1423
+treo755p MACH_TREO755P TREO755P 1424
ezreganut9200 MACH_EZREGANUT9200 EZREGANUT9200 1425
sarge MACH_SARGE SARGE 1426
a696 MACH_A696 A696 1427
@@ -1455,7 +1455,7 @@ gba MACH_GBA GBA 1457
h6044 MACH_H6044 H6044 1458
app MACH_APP APP 1459
tct_hammer MACH_TCT_HAMMER TCT_HAMMER 1460
-herald MACH_HERMES HERMES 1461
+herald MACH_HERALD HERALD 1461
artemis MACH_ARTEMIS ARTEMIS 1462
htctitan MACH_HTCTITAN HTCTITAN 1463
qranium MACH_QRANIUM QRANIUM 1464
@@ -1638,7 +1638,7 @@ mx35evb MACH_MX35EVB MX35EVB 1643
aml_m8050 MACH_AML_M8050 AML_M8050 1644
mx35_3ds MACH_MX35_3DS MX35_3DS 1645
mars MACH_MARS MARS 1646
-ntosd_644xa MACH_NTOSD_644XA NTOSD_644XA 1647
+neuros_osd2 MACH_NEUROS_OSD2 NEUROS_OSD2 1647
badger MACH_BADGER BADGER 1648
trizeps4wl MACH_TRIZEPS4WL TRIZEPS4WL 1649
trizeps5 MACH_TRIZEPS5 TRIZEPS5 1650
@@ -1654,7 +1654,7 @@ vf10xx MACH_VF10XX VF10XX 1659
zoran43xx MACH_ZORAN43XX ZORAN43XX 1660
sonix926 MACH_SONIX926 SONIX926 1661
celestialsemi MACH_CELESTIALSEMI CELESTIALSEMI 1662
-cc9m2443 MACH_CC9M2443 CC9M2443 1663
+cc9m2443js MACH_CC9M2443JS CC9M2443JS 1663
tw5334 MACH_TW5334 TW5334 1664
omap_htcartemis MACH_HTCARTEMIS HTCARTEMIS 1665
nal_hlite MACH_NAL_HLITE NAL_HLITE 1666
@@ -1721,7 +1721,7 @@ sapphire MACH_SAPPHIRE SAPPHIRE 1729
csb637xo MACH_CSB637XO CSB637XO 1730
evisiong MACH_EVISIONG EVISIONG 1731
stmp37xx MACH_STMP37XX STMP37XX 1732
-stmp378x MACH_STMP38XX STMP38XX 1733
+stmp378x MACH_STMP378X STMP378X 1733
tnt MACH_TNT TNT 1734
tbxt MACH_TBXT TBXT 1735
playmate MACH_PLAYMATE PLAYMATE 1736
@@ -1769,13 +1769,14 @@ 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
-at572d940hfeb MACH_AT572D940HFEB AT572D940HFEB 1783
+at572d940hfek MACH_AT572D940HFEB AT572D940HFEB 1783
cybook3 MACH_CYBOOK3 CYBOOK3 1784
wdg002 MACH_WDG002 WDG002 1785
sg560adsl MACH_SG560ADSL SG560ADSL 1786
nextio_n2800_ica MACH_NEXTIO_N2800_ICA NEXTIO_N2800_ICA 1787
+dove_db MACH_DOVE_DB DOVE_DB 1788
marvell_newdb MACH_MARVELL_NEWDB MARVELL_NEWDB 1789
vandihud MACH_VANDIHUD VANDIHUD 1790
magx_e8 MACH_MAGX_E8 MAGX_E8 1791
@@ -1802,7 +1803,7 @@ ccw9p9215js MACH_CCW9P9215JS CCW9P9215JS 1811
rd88f5181l_ge MACH_RD88F5181L_GE RD88F5181L_GE 1812
sifmain MACH_SIFMAIN SIFMAIN 1813
sam9_l9261 MACH_SAM9_L9261 SAM9_L9261 1814
-cc9m2443js MACH_CC9M2443JS CC9M2443JS 1815
+cc9m2443 MACH_CC9M2443 CC9M2443 1815
xaria300 MACH_XARIA300 XARIA300 1816
it9200 MACH_IT9200 IT9200 1817
rd88f5181l_fxo MACH_RD88F5181L_FXO RD88F5181L_FXO 1818
@@ -1811,13 +1812,13 @@ pilz_pmi5 MACH_PILZ_PMI5 PILZ_PMI5 1820
jade MACH_JADE JADE 1821
ks8695_softplc MACH_KS8695_SOFTPLC KS8695_SOFTPLC 1822
gprisc3 MACH_GPRISC3 GPRISC3 1823
-stamp9260 MACH_STAMP9260 STAMP9260 1824
+stamp9g20 MACH_STAMP9G20 STAMP9G20 1824
smdk6430 MACH_SMDK6430 SMDK6430 1825
smdkc100 MACH_SMDKC100 SMDKC100 1826
tavorevb MACH_TAVOREVB TAVOREVB 1827
saar MACH_SAAR SAAR 1828
deister_eyecam MACH_DEISTER_EYECAM DEISTER_EYECAM 1829
-at91sam9m10ek MACH_AT91SAM9M10EK AT91SAM9M10EK 1830
+at91sam9m10g45ek MACH_AT91SAM9M10G45EK AT91SAM9M10G45EK 1830
linkstation_produo MACH_LINKSTATION_PRODUO LINKSTATION_PRODUO 1831
hit_b0 MACH_HIT_B0 HIT_B0 1832
adx_rmu MACH_ADX_RMU ADX_RMU 1833
@@ -1899,3 +1900,695 @@ rut100 MACH_RUT100 RUT100 1908
asusp535 MACH_ASUSP535 ASUSP535 1909
htcraphael MACH_HTCRAPHAEL HTCRAPHAEL 1910
sygdg1 MACH_SYGDG1 SYGDG1 1911
+sygdg2 MACH_SYGDG2 SYGDG2 1912
+seoul MACH_SEOUL SEOUL 1913
+salerno MACH_SALERNO SALERNO 1914
+ucn_s3c64xx MACH_UCN_S3C64XX UCN_S3C64XX 1915
+msm7201a MACH_MSM7201A MSM7201A 1916
+lpr1 MACH_LPR1 LPR1 1917
+armadillo500fx MACH_ARMADILLO500FX ARMADILLO500FX 1918
+g3evm MACH_G3EVM G3EVM 1919
+z3_dm355 MACH_Z3_DM355 Z3_DM355 1920
+w90p910evb MACH_W90P910EVB W90P910EVB 1921
+w90p920evb MACH_W90P920EVB W90P920EVB 1922
+w90p950evb MACH_W90P950EVB W90P950EVB 1923
+w90n960evb MACH_W90N960EVB W90N960EVB 1924
+camhd MACH_CAMHD CAMHD 1925
+mvc100 MACH_MVC100 MVC100 1926
+electrum_200 MACH_ELECTRUM_200 ELECTRUM_200 1927
+htcjade MACH_HTCJADE HTCJADE 1928
+memphis MACH_MEMPHIS MEMPHIS 1929
+imx27sbc MACH_IMX27SBC IMX27SBC 1930
+lextar MACH_LEXTAR LEXTAR 1931
+mv88f6281gtw_ge MACH_MV88F6281GTW_GE MV88F6281GTW_GE 1932
+ncp MACH_NCP NCP 1933
+z32an_series MACH_Z32AN Z32AN 1934
+tmq_capd MACH_TMQ_CAPD TMQ_CAPD 1935
+omap3_wl MACH_OMAP3_WL OMAP3_WL 1936
+chumby MACH_CHUMBY CHUMBY 1937
+atsarm9 MACH_ATSARM9 ATSARM9 1938
+davinci_dm365_evm MACH_DAVINCI_DM365_EVM DAVINCI_DM365_EVM 1939
+bahamas MACH_BAHAMAS BAHAMAS 1940
+das MACH_DAS DAS 1941
+minidas MACH_MINIDAS MINIDAS 1942
+vk1000 MACH_VK1000 VK1000 1943
+centro MACH_CENTRO CENTRO 1944
+ctera_2bay MACH_CTERA_2BAY CTERA_2BAY 1945
+edgeconnect MACH_EDGECONNECT EDGECONNECT 1946
+nd27000 MACH_ND27000 ND27000 1947
+cobra MACH_GEMALTO_COBRA GEMALTO_COBRA 1948
+ingelabs_comet MACH_INGELABS_COMET INGELABS_COMET 1949
+pollux_wiz MACH_POLLUX_WIZ POLLUX_WIZ 1950
+blackstone MACH_BLACKSTONE BLACKSTONE 1951
+topaz MACH_TOPAZ TOPAZ 1952
+aixle MACH_AIXLE AIXLE 1953
+mw998 MACH_MW998 MW998 1954
+nokia_rx51 MACH_NOKIA_RX51 NOKIA_RX51 1955
+vsc5605ev MACH_VSC5605EV VSC5605EV 1956
+nt98700dk MACH_NT98700DK NT98700DK 1957
+icontact MACH_ICONTACT ICONTACT 1958
+swarco_frcpu MACH_SWARCO_FRCPU SWARCO_FRCPU 1959
+swarco_scpu MACH_SWARCO_SCPU SWARCO_SCPU 1960
+bbox_p16 MACH_BBOX_P16 BBOX_P16 1961
+bstd MACH_BSTD BSTD 1962
+sbc2440ii MACH_SBC2440II SBC2440II 1963
+pcm034 MACH_PCM034 PCM034 1964
+neso MACH_NESO NESO 1965
+wlnx_9g20 MACH_WLNX_9G20 WLNX_9G20 1966
+omap_zoom2 MACH_OMAP_ZOOM2 OMAP_ZOOM2 1967
+totemnova MACH_TOTEMNOVA TOTEMNOVA 1968
+c5000 MACH_C5000 C5000 1969
+unipo_at91sam9263 MACH_UNIPO_AT91SAM9263 UNIPO_AT91SAM9263 1970
+ethernut5 MACH_ETHERNUT5 ETHERNUT5 1971
+arm11 MACH_ARM11 ARM11 1972
+cpuat9260 MACH_CPUAT9260 CPUAT9260 1973
+cpupxa255 MACH_CPUPXA255 CPUPXA255 1974
+eukrea_cpuimx27 MACH_CPUIMX27 CPUIMX27 1975
+cheflux MACH_CHEFLUX CHEFLUX 1976
+eb_cpux9k2 MACH_EB_CPUX9K2 EB_CPUX9K2 1977
+opcotec MACH_OPCOTEC OPCOTEC 1978
+yt MACH_YT YT 1979
+motoq MACH_MOTOQ MOTOQ 1980
+bsb1 MACH_BSB1 BSB1 1981
+acs5k MACH_ACS5K ACS5K 1982
+milan MACH_MILAN MILAN 1983
+quartzv2 MACH_QUARTZV2 QUARTZV2 1984
+rsvp MACH_RSVP RSVP 1985
+rmp200 MACH_RMP200 RMP200 1986
+snapper_9260 MACH_SNAPPER_9260 SNAPPER_9260 1987
+dsm320 MACH_DSM320 DSM320 1988
+adsgcm MACH_ADSGCM ADSGCM 1989
+ase2_400 MACH_ASE2_400 ASE2_400 1990
+pizza MACH_PIZZA PIZZA 1991
+spot_ngpl MACH_SPOT_NGPL SPOT_NGPL 1992
+armata MACH_ARMATA ARMATA 1993
+exeda MACH_EXEDA EXEDA 1994
+mx31sf005 MACH_MX31SF005 MX31SF005 1995
+f5d8231_4_v2 MACH_F5D8231_4_V2 F5D8231_4_V2 1996
+q2440 MACH_Q2440 Q2440 1997
+qq2440 MACH_QQ2440 QQ2440 1998
+mini2440 MACH_MINI2440 MINI2440 1999
+colibri300 MACH_COLIBRI300 COLIBRI300 2000
+jades MACH_JADES JADES 2001
+spark MACH_SPARK SPARK 2002
+benzina MACH_BENZINA BENZINA 2003
+blaze MACH_BLAZE BLAZE 2004
+linkstation_ls_hgl MACH_LINKSTATION_LS_HGL LINKSTATION_LS_HGL 2005
+htckovsky MACH_HTCVENUS HTCVENUS 2006
+sony_prs505 MACH_SONY_PRS505 SONY_PRS505 2007
+hanlin_v3 MACH_HANLIN_V3 HANLIN_V3 2008
+sapphira MACH_SAPPHIRA SAPPHIRA 2009
+dack_sda_01 MACH_DACK_SDA_01 DACK_SDA_01 2010
+armbox MACH_ARMBOX ARMBOX 2011
+harris_rvp MACH_HARRIS_RVP HARRIS_RVP 2012
+ribaldo MACH_RIBALDO RIBALDO 2013
+agora MACH_AGORA AGORA 2014
+omap3_mini MACH_OMAP3_MINI OMAP3_MINI 2015
+a9sam6432_b MACH_A9SAM6432_B A9SAM6432_B 2016
+usg2410 MACH_USG2410 USG2410 2017
+pc72052_i10_revb MACH_PC72052_I10_REVB PC72052_I10_REVB 2018
+mx35_exm32 MACH_MX35_EXM32 MX35_EXM32 2019
+topas910 MACH_TOPAS910 TOPAS910 2020
+hyena MACH_HYENA HYENA 2021
+pospax MACH_POSPAX POSPAX 2022
+hdl_gx MACH_HDL_GX HDL_GX 2023
+ctera_4bay MACH_CTERA_4BAY CTERA_4BAY 2024
+ctera_plug_c MACH_CTERA_PLUG_C CTERA_PLUG_C 2025
+crwea_plug_i MACH_CRWEA_PLUG_I CRWEA_PLUG_I 2026
+egauge2 MACH_EGAUGE2 EGAUGE2 2027
+didj MACH_DIDJ DIDJ 2028
+m_s3c2443 MACH_MEISTER MEISTER 2029
+htcblackstone MACH_HTCBLACKSTONE HTCBLACKSTONE 2030
+cpuat9g20 MACH_CPUAT9G20 CPUAT9G20 2031
+smdk6440 MACH_SMDK6440 SMDK6440 2032
+omap_35xx_mvp MACH_OMAP_35XX_MVP OMAP_35XX_MVP 2033
+ctera_plug_i MACH_CTERA_PLUG_I CTERA_PLUG_I 2034
+pvg610_100 MACH_PVG610 PVG610 2035
+hprw6815 MACH_HPRW6815 HPRW6815 2036
+omap3_oswald MACH_OMAP3_OSWALD OMAP3_OSWALD 2037
+nas4220b MACH_NAS4220B NAS4220B 2038
+htcraphael_cdma MACH_HTCRAPHAEL_CDMA HTCRAPHAEL_CDMA 2039
+htcdiamond_cdma MACH_HTCDIAMOND_CDMA HTCDIAMOND_CDMA 2040
+scaler MACH_SCALER SCALER 2041
+zylonite2 MACH_ZYLONITE2 ZYLONITE2 2042
+aspenite MACH_ASPENITE ASPENITE 2043
+teton MACH_TETON TETON 2044
+ttc_dkb MACH_TTC_DKB TTC_DKB 2045
+bishop2 MACH_BISHOP2 BISHOP2 2046
+ippv5 MACH_IPPV5 IPPV5 2047
+farm926 MACH_FARM926 FARM926 2048
+mmccpu MACH_MMCCPU MMCCPU 2049
+sgmsfl MACH_SGMSFL SGMSFL 2050
+tt8000 MACH_TT8000 TT8000 2051
+zrn4300lp MACH_ZRN4300LP ZRN4300LP 2052
+mptc MACH_MPTC MPTC 2053
+h6051 MACH_H6051 H6051 2054
+pvg610_101 MACH_PVG610_101 PVG610_101 2055
+stamp9261_pc_evb MACH_STAMP9261_PC_EVB STAMP9261_PC_EVB 2056
+pelco_odysseus MACH_PELCO_ODYSSEUS PELCO_ODYSSEUS 2057
+tny_a9260 MACH_TNY_A9260 TNY_A9260 2058
+tny_a9g20 MACH_TNY_A9G20 TNY_A9G20 2059
+aesop_mp2530f MACH_AESOP_MP2530F AESOP_MP2530F 2060
+dx900 MACH_DX900 DX900 2061
+cpodc2 MACH_CPODC2 CPODC2 2062
+tilt_8925 MACH_TILT_8925 TILT_8925 2063
+davinci_dm357_evm MACH_DAVINCI_DM357_EVM DAVINCI_DM357_EVM 2064
+swordfish MACH_SWORDFISH SWORDFISH 2065
+corvus MACH_CORVUS CORVUS 2066
+taurus MACH_TAURUS TAURUS 2067
+axm MACH_AXM AXM 2068
+axc MACH_AXC AXC 2069
+baby MACH_BABY BABY 2070
+mp200 MACH_MP200 MP200 2071
+pcm043 MACH_PCM043 PCM043 2072
+hanlin_v3c MACH_HANLIN_V3C HANLIN_V3C 2073
+kbk9g20 MACH_KBK9G20 KBK9G20 2074
+adsturbog5 MACH_ADSTURBOG5 ADSTURBOG5 2075
+avenger_lite1 MACH_AVENGER_LITE1 AVENGER_LITE1 2076
+suc82x MACH_SUC SUC 2077
+at91sam7s256 MACH_AT91SAM7S256 AT91SAM7S256 2078
+mendoza MACH_MENDOZA MENDOZA 2079
+kira MACH_KIRA KIRA 2080
+mx1hbm MACH_MX1HBM MX1HBM 2081
+quatro43xx MACH_QUATRO43XX QUATRO43XX 2082
+quatro4230 MACH_QUATRO4230 QUATRO4230 2083
+nsb400 MACH_NSB400 NSB400 2084
+drp255 MACH_DRP255 DRP255 2085
+thoth MACH_THOTH THOTH 2086
+firestone MACH_FIRESTONE FIRESTONE 2087
+asusp750 MACH_ASUSP750 ASUSP750 2088
+ctera_dl MACH_CTERA_DL CTERA_DL 2089
+socr MACH_SOCR SOCR 2090
+htcoxygen MACH_HTCOXYGEN HTCOXYGEN 2091
+heroc MACH_HEROC HEROC 2092
+zeno6800 MACH_ZENO6800 ZENO6800 2093
+sc2mcs MACH_SC2MCS SC2MCS 2094
+gene100 MACH_GENE100 GENE100 2095
+as353x MACH_AS353X AS353X 2096
+sheevaplug MACH_SHEEVAPLUG SHEEVAPLUG 2097
+at91sam9g20 MACH_AT91SAM9G20 AT91SAM9G20 2098
+mv88f6192gtw_fe MACH_MV88F6192GTW_FE MV88F6192GTW_FE 2099
+cc9200 MACH_CC9200 CC9200 2100
+sm9200 MACH_SM9200 SM9200 2101
+tp9200 MACH_TP9200 TP9200 2102
+snapperdv MACH_SNAPPERDV SNAPPERDV 2103
+avengers_lite MACH_AVENGERS_LITE AVENGERS_LITE 2104
+avengers_lite1 MACH_AVENGERS_LITE1 AVENGERS_LITE1 2105
+omap3axon MACH_OMAP3AXON OMAP3AXON 2106
+ma8xx MACH_MA8XX MA8XX 2107
+mp201ek MACH_MP201EK MP201EK 2108
+davinci_tux MACH_DAVINCI_TUX DAVINCI_TUX 2109
+mpa1600 MACH_MPA1600 MPA1600 2110
+pelco_troy MACH_PELCO_TROY PELCO_TROY 2111
+nsb667 MACH_NSB667 NSB667 2112
+rovers5_4mpix MACH_ROVERS5_4MPIX ROVERS5_4MPIX 2113
+twocom MACH_TWOCOM TWOCOM 2114
+ubisys_p9_rcu3r2 MACH_UBISYS_P9_RCU3R2 UBISYS_P9_RCU3R2 2115
+hero_espresso MACH_HERO_ESPRESSO HERO_ESPRESSO 2116
+afeusb MACH_AFEUSB AFEUSB 2117
+t830 MACH_T830 T830 2118
+spd8020_cc MACH_SPD8020_CC SPD8020_CC 2119
+om_3d7k MACH_OM_3D7K OM_3D7K 2120
+picocom2 MACH_PICOCOM2 PICOCOM2 2121
+uwg4mx27 MACH_UWG4MX27 UWG4MX27 2122
+uwg4mx31 MACH_UWG4MX31 UWG4MX31 2123
+cherry MACH_CHERRY CHERRY 2124
+mx51_babbage MACH_MX51_BABBAGE MX51_BABBAGE 2125
+s3c2440turkiye MACH_S3C2440TURKIYE S3C2440TURKIYE 2126
+tx37 MACH_TX37 TX37 2127
+sbc2800_9g20 MACH_SBC2800_9G20 SBC2800_9G20 2128
+benzglb MACH_BENZGLB BENZGLB 2129
+benztd MACH_BENZTD BENZTD 2130
+cartesio_plus MACH_CARTESIO_PLUS CARTESIO_PLUS 2131
+solrad_g20 MACH_SOLRAD_G20 SOLRAD_G20 2132
+mx27wallace MACH_MX27WALLACE MX27WALLACE 2133
+fmzwebmodul MACH_FMZWEBMODUL FMZWEBMODUL 2134
+rd78x00_masa MACH_RD78X00_MASA RD78X00_MASA 2135
+smallogger MACH_SMALLOGGER SMALLOGGER 2136
+ccw9p9215 MACH_CCW9P9215 CCW9P9215 2137
+dm355_leopard MACH_DM355_LEOPARD DM355_LEOPARD 2138
+ts219 MACH_TS219 TS219 2139
+tny_a9263 MACH_TNY_A9263 TNY_A9263 2140
+apollo MACH_APOLLO APOLLO 2141
+at91cap9stk MACH_AT91CAP9STK AT91CAP9STK 2142
+spc300 MACH_SPC300 SPC300 2143
+eko MACH_EKO EKO 2144
+ccw9m2443 MACH_CCW9M2443 CCW9M2443 2145
+ccw9m2443js MACH_CCW9M2443JS CCW9M2443JS 2146
+m2m_router_device MACH_M2M_ROUTER_DEVICE M2M_ROUTER_DEVICE 2147
+str9104nas MACH_STAR9104NAS STAR9104NAS 2148
+pca100 MACH_PCA100 PCA100 2149
+z3_dm365_mod_01 MACH_Z3_DM365_MOD_01 Z3_DM365_MOD_01 2150
+hipox MACH_HIPOX HIPOX 2151
+omap3_piteds MACH_OMAP3_PITEDS OMAP3_PITEDS 2152
+bm150r MACH_BM150R BM150R 2153
+tbone MACH_TBONE TBONE 2154
+merlin MACH_MERLIN MERLIN 2155
+falcon MACH_FALCON FALCON 2156
+davinci_da850_evm MACH_DAVINCI_DA850_EVM DAVINCI_DA850_EVM 2157
+s5p6440 MACH_S5P6440 S5P6440 2158
+at91sam9g10ek MACH_AT91SAM9G10EK AT91SAM9G10EK 2159
+omap_4430sdp MACH_OMAP_4430SDP OMAP_4430SDP 2160
+lpc313x MACH_LPC313X LPC313X 2161
+magx_zn5 MACH_MAGX_ZN5 MAGX_ZN5 2162
+magx_em30 MACH_MAGX_EM30 MAGX_EM30 2163
+magx_ve66 MACH_MAGX_VE66 MAGX_VE66 2164
+meesc MACH_MEESC MEESC 2165
+otc570 MACH_OTC570 OTC570 2166
+bcu2412 MACH_BCU2412 BCU2412 2167
+beacon MACH_BEACON BEACON 2168
+actia_tgw MACH_ACTIA_TGW ACTIA_TGW 2169
+e4430 MACH_E4430 E4430 2170
+ql300 MACH_QL300 QL300 2171
+btmavb101 MACH_BTMAVB101 BTMAVB101 2172
+btmawb101 MACH_BTMAWB101 BTMAWB101 2173
+sq201 MACH_SQ201 SQ201 2174
+quatro45xx MACH_QUATRO45XX QUATRO45XX 2175
+openpad MACH_OPENPAD OPENPAD 2176
+tx25 MACH_TX25 TX25 2177
+omap3_torpedo MACH_OMAP3_TORPEDO OMAP3_TORPEDO 2178
+htcraphael_k MACH_HTCRAPHAEL_K HTCRAPHAEL_K 2179
+lal43 MACH_LAL43 LAL43 2181
+htcraphael_cdma500 MACH_HTCRAPHAEL_CDMA500 HTCRAPHAEL_CDMA500 2182
+anw6410 MACH_ANW6410 ANW6410 2183
+htcprophet MACH_HTCPROPHET HTCPROPHET 2185
+cfa_10022 MACH_CFA_10022 CFA_10022 2186
+imx27_visstrim_m10 MACH_IMX27_VISSTRIM_M10 IMX27_VISSTRIM_M10 2187
+px2imx27 MACH_PX2IMX27 PX2IMX27 2188
+stm3210e_eval MACH_STM3210E_EVAL STM3210E_EVAL 2189
+dvs10 MACH_DVS10 DVS10 2190
+portuxg20 MACH_PORTUXG20 PORTUXG20 2191
+arm_spv MACH_ARM_SPV ARM_SPV 2192
+smdkc110 MACH_SMDKC110 SMDKC110 2193
+cabespresso MACH_CABESPRESSO CABESPRESSO 2194
+hmc800 MACH_HMC800 HMC800 2195
+sholes MACH_SHOLES SHOLES 2196
+btmxc31 MACH_BTMXC31 BTMXC31 2197
+dt501 MACH_DT501 DT501 2198
+ktx MACH_KTX KTX 2199
+omap3517evm MACH_OMAP3517EVM OMAP3517EVM 2200
+netspace_v2 MACH_NETSPACE_V2 NETSPACE_V2 2201
+netspace_max_v2 MACH_NETSPACE_MAX_V2 NETSPACE_MAX_V2 2202
+d2net_v2 MACH_D2NET_V2 D2NET_V2 2203
+net2big_v2 MACH_NET2BIG_V2 NET2BIG_V2 2204
+net4big_v2 MACH_NET4BIG_V2 NET4BIG_V2 2205
+net5big_v2 MACH_NET5BIG_V2 NET5BIG_V2 2206
+endb2443 MACH_ENDB2443 ENDB2443 2207
+inetspace_v2 MACH_INETSPACE_V2 INETSPACE_V2 2208
+tros MACH_TROS TROS 2209
+pelco_homer MACH_PELCO_HOMER PELCO_HOMER 2210
+ofsp8 MACH_OFSP8 OFSP8 2211
+at91sam9g45ekes MACH_AT91SAM9G45EKES AT91SAM9G45EKES 2212
+guf_cupid MACH_GUF_CUPID GUF_CUPID 2213
+eab1r MACH_EAB1R EAB1R 2214
+desirec MACH_DESIREC DESIREC 2215
+cordoba MACH_CORDOBA CORDOBA 2216
+irvine MACH_IRVINE IRVINE 2217
+sff772 MACH_SFF772 SFF772 2218
+pelco_milano MACH_PELCO_MILANO PELCO_MILANO 2219
+pc7302 MACH_PC7302 PC7302 2220
+bip6000 MACH_BIP6000 BIP6000 2221
+silvermoon MACH_SILVERMOON SILVERMOON 2222
+vc0830 MACH_VC0830 VC0830 2223
+dt430 MACH_DT430 DT430 2224
+ji42pf MACH_JI42PF JI42PF 2225
+gnet_ksm MACH_GNET_KSM GNET_KSM 2226
+gnet_sgm MACH_GNET_SGM GNET_SGM 2227
+gnet_sgr MACH_GNET_SGR GNET_SGR 2228
+omap3_icetekevm MACH_OMAP3_ICETEKEVM OMAP3_ICETEKEVM 2229
+pnp MACH_PNP PNP 2230
+ctera_2bay_k MACH_CTERA_2BAY_K CTERA_2BAY_K 2231
+ctera_2bay_u MACH_CTERA_2BAY_U CTERA_2BAY_U 2232
+sas_c MACH_SAS_C SAS_C 2233
+vma2315 MACH_VMA2315 VMA2315 2234
+vcs MACH_VCS VCS 2235
+spear600 MACH_SPEAR600 SPEAR600 2236
+spear300 MACH_SPEAR300 SPEAR300 2237
+spear1300 MACH_SPEAR1300 SPEAR1300 2238
+lilly1131 MACH_LILLY1131 LILLY1131 2239
+arvoo_ax301 MACH_ARVOO_AX301 ARVOO_AX301 2240
+mapphone MACH_MAPPHONE MAPPHONE 2241
+legend MACH_LEGEND LEGEND 2242
+salsa MACH_SALSA SALSA 2243
+lounge MACH_LOUNGE LOUNGE 2244
+vision MACH_VISION VISION 2245
+vmb20 MACH_VMB20 VMB20 2246
+hy2410 MACH_HY2410 HY2410 2247
+hy9315 MACH_HY9315 HY9315 2248
+bullwinkle MACH_BULLWINKLE BULLWINKLE 2249
+arm_ultimator2 MACH_ARM_ULTIMATOR2 ARM_ULTIMATOR2 2250
+vs_v210 MACH_VS_V210 VS_V210 2252
+vs_v212 MACH_VS_V212 VS_V212 2253
+hmt MACH_HMT HMT 2254
+suen3 MACH_SUEN3 SUEN3 2255
+vesper MACH_VESPER VESPER 2256
+str9 MACH_STR9 STR9 2257
+omap3_wl_ff MACH_OMAP3_WL_FF OMAP3_WL_FF 2258
+simcom MACH_SIMCOM SIMCOM 2259
+mcwebio MACH_MCWEBIO MCWEBIO 2260
+omap3_phrazer MACH_OMAP3_PHRAZER OMAP3_PHRAZER 2261
+darwin MACH_DARWIN DARWIN 2262
+oratiscomu MACH_ORATISCOMU ORATISCOMU 2263
+rtsbc20 MACH_RTSBC20 RTSBC20 2264
+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
+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
+sgh_i900 MACH_SGH_I900 SGH_I900 2276
+bst100 MACH_BST100 BST100 2277
+passion MACH_PASSION PASSION 2278
+indesign_at91sam MACH_INDESIGN_AT91SAM INDESIGN_AT91SAM 2279
+c4_badger MACH_C4_BADGER C4_BADGER 2280
+c4_viper MACH_C4_VIPER C4_VIPER 2281
+d2net MACH_D2NET D2NET 2282
+bigdisk MACH_BIGDISK BIGDISK 2283
+notalvision MACH_NOTALVISION NOTALVISION 2284
+omap3_kboc MACH_OMAP3_KBOC OMAP3_KBOC 2285
+cyclone MACH_CYCLONE CYCLONE 2286
+ninja MACH_NINJA NINJA 2287
+at91sam9g20ek_2mmc MACH_AT91SAM9G20EK_2MMC AT91SAM9G20EK_2MMC 2288
+bcmring MACH_BCMRING BCMRING 2289
+resol_dl2 MACH_RESOL_DL2 RESOL_DL2 2290
+ifosw MACH_IFOSW IFOSW 2291
+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
+siena 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
+net5big MACH_NET5BIG NET5BIG 2426
+seabed9263 MACH_SEABED9263 SEABED9263 2427
+mx51_m2id MACH_MX51_M2ID MX51_M2ID 2428
+octvocplus_eb MACH_OCTVOCPLUS_EB OCTVOCPLUS_EB 2429
+klk_firefox MACH_KLK_FIREFOX KLK_FIREFOX 2430
+klk_wirma_module MACH_KLK_WIRMA_MODULE KLK_WIRMA_MODULE 2431
+klk_wirma_mmi MACH_KLK_WIRMA_MMI KLK_WIRMA_MMI 2432
+supersonic MACH_SUPERSONIC SUPERSONIC 2433
+liberty MACH_LIBERTY LIBERTY 2434
+mh355 MACH_MH355 MH355 2435
+pc7802 MACH_PC7802 PC7802 2436
+gnet_sgc MACH_GNET_SGC GNET_SGC 2437
+einstein15 MACH_EINSTEIN15 EINSTEIN15 2438
+cmpd MACH_CMPD CMPD 2439
+davinci_hase1 MACH_DAVINCI_HASE1 DAVINCI_HASE1 2440
+lgeincitephone MACH_LGEINCITEPHONE LGEINCITEPHONE 2441
+ea313x MACH_EA313X EA313X 2442
+fwbd_39064 MACH_FWBD_39064 FWBD_39064 2443
+fwbd_390128 MACH_FWBD_390128 FWBD_390128 2444
+pelco_moe MACH_PELCO_MOE PELCO_MOE 2445
+minimix27 MACH_MINIMIX27 MINIMIX27 2446
+omap3_thunder MACH_OMAP3_THUNDER OMAP3_THUNDER 2447
+passionc MACH_PASSIONC PASSIONC 2448
+mx27amata MACH_MX27AMATA MX27AMATA 2449
+bgat1 MACH_BGAT1 BGAT1 2450
+buzz MACH_BUZZ BUZZ 2451
+mb9g20 MACH_MB9G20 MB9G20 2452
+yushan MACH_YUSHAN YUSHAN 2453
+lizard MACH_LIZARD LIZARD 2454
+omap3polycom MACH_OMAP3POLYCOM OMAP3POLYCOM 2455
+smdkv210 MACH_SMDKV210 SMDKV210 2456
+bravo MACH_BRAVO BRAVO 2457
+siogentoo1 MACH_SIOGENTOO1 SIOGENTOO1 2458
+siogentoo2 MACH_SIOGENTOO2 SIOGENTOO2 2459
+sm3k MACH_SM3K SM3K 2460
+acer_tempo_f900 MACH_ACER_TEMPO_F900 ACER_TEMPO_F900 2461
+sst61vc010_dev MACH_SST61VC010_DEV SST61VC010_DEV 2462
+glittertind MACH_GLITTERTIND GLITTERTIND 2463
+omap_zoom3 MACH_OMAP_ZOOM3 OMAP_ZOOM3 2464
+omap_3630sdp MACH_OMAP_3630SDP OMAP_3630SDP 2465
+cybook2440 MACH_CYBOOK2440 CYBOOK2440 2466
+torino_s MACH_TORINO_S TORINO_S 2467
+havana MACH_HAVANA HAVANA 2468
+beaumont_11 MACH_BEAUMONT_11 BEAUMONT_11 2469
+vanguard MACH_VANGUARD VANGUARD 2470
+s5pc110_draco MACH_S5PC110_DRACO S5PC110_DRACO 2471
+cartesio_two MACH_CARTESIO_TWO CARTESIO_TWO 2472
+aster MACH_ASTER ASTER 2473
+voguesv210 MACH_VOGUESV210 VOGUESV210 2474
+acm500x MACH_ACM500X ACM500X 2475
+km9260 MACH_KM9260 KM9260 2476
+nideflexg1 MACH_NIDEFLEXG1 NIDEFLEXG1 2477
+ctera_plug_io MACH_CTERA_PLUG_IO CTERA_PLUG_IO 2478
+smartq7 MACH_SMARTQ7 SMARTQ7 2479
+at91sam9g10ek2 MACH_AT91SAM9G10EK2 AT91SAM9G10EK2 2480
+asusp527 MACH_ASUSP527 ASUSP527 2481
+at91sam9g20mpm2 MACH_AT91SAM9G20MPM2 AT91SAM9G20MPM2 2482
+topasa900 MACH_TOPASA900 TOPASA900 2483
+electrum_100 MACH_ELECTRUM_100 ELECTRUM_100 2484
+mx51grb MACH_MX51GRB MX51GRB 2485
+xea300 MACH_XEA300 XEA300 2486
+htcstartrek MACH_HTCSTARTREK HTCSTARTREK 2487
+lima MACH_LIMA LIMA 2488
+csb740 MACH_CSB740 CSB740 2489
+usb_s8815 MACH_USB_S8815 USB_S8815 2490
+watson_efm_plugin MACH_WATSON_EFM_PLUGIN WATSON_EFM_PLUGIN 2491
+milkyway MACH_MILKYWAY MILKYWAY 2492
+g4evm MACH_G4EVM G4EVM 2493
+picomod6 MACH_PICOMOD6 PICOMOD6 2494
+omapl138_hawkboard MACH_OMAPL138_HAWKBOARD OMAPL138_HAWKBOARD 2495
+ip6000 MACH_IP6000 IP6000 2496
+ip6010 MACH_IP6010 IP6010 2497
+utm400 MACH_UTM400 UTM400 2498
+omap3_zybex MACH_OMAP3_ZYBEX OMAP3_ZYBEX 2499
+wireless_space MACH_WIRELESS_SPACE WIRELESS_SPACE 2500
+sx560 MACH_SX560 SX560 2501
+ts41x MACH_TS41X TS41X 2502
+elphel10373 MACH_ELPHEL10373 ELPHEL10373 2503
+rhobot MACH_RHOBOT RHOBOT 2504
+mx51_refresh MACH_MX51_REFRESH MX51_REFRESH 2505
+ls9260 MACH_LS9260 LS9260 2506
+shank MACH_SHANK SHANK 2507
+qsd8x50_st1 MACH_QSD8X50_ST1 QSD8X50_ST1 2508
+at91sam9m10ekes MACH_AT91SAM9M10EKES AT91SAM9M10EKES 2509
+hiram MACH_HIRAM HIRAM 2510
+phy3250 MACH_PHY3250 PHY3250 2511
+ea3250 MACH_EA3250 EA3250 2512
+fdi3250 MACH_FDI3250 FDI3250 2513
+whitestone MACH_WHITESTONE WHITESTONE 2514
+at91sam9263nit MACH_AT91SAM9263NIT AT91SAM9263NIT 2515
+ccmx51 MACH_CCMX51 CCMX51 2516
+ccmx51js MACH_CCMX51JS CCMX51JS 2517
+ccwmx51 MACH_CCWMX51 CCWMX51 2518
+ccwmx51js MACH_CCWMX51JS CCWMX51JS 2519
+mini6410 MACH_MINI6410 MINI6410 2520
+tiny6410 MACH_TINY6410 TINY6410 2521
+nano6410 MACH_NANO6410 NANO6410 2522
+at572d940hfnldb MACH_AT572D940HFNLDB AT572D940HFNLDB 2523
+htcleo MACH_HTCLEO HTCLEO 2524
+avp13 MACH_AVP13 AVP13 2525
+xxsvideod MACH_XXSVIDEOD XXSVIDEOD 2526
+vpnext MACH_VPNEXT VPNEXT 2527
+swarco_itc3 MACH_SWARCO_ITC3 SWARCO_ITC3 2528
+tx51 MACH_TX51 TX51 2529
+dolby_cat1021 MACH_DOLBY_CAT1021 DOLBY_CAT1021 2530
+mx28evk MACH_MX28EVK MX28EVK 2531
+phoenix260 MACH_PHOENIX260 PHOENIX260 2532
+uvaca_stork MACH_UVACA_STORK UVACA_STORK 2533
+smartq5 MACH_SMARTQ5 SMARTQ5 2534
+all3078 MACH_ALL3078 ALL3078 2535
+ctera_2bay_ds MACH_CTERA_2BAY_DS CTERA_2BAY_DS 2536
+siogentoo3 MACH_SIOGENTOO3 SIOGENTOO3 2537
+epb5000 MACH_EPB5000 EPB5000 2538
+hy9263 MACH_HY9263 HY9263 2539
+acer_tempo_m900 MACH_ACER_TEMPO_M900 ACER_TEMPO_M900 2540
+acer_tempo_dx650 MACH_ACER_TEMPO_DX900 ACER_TEMPO_DX900 2541
+acer_tempo_x960 MACH_ACER_TEMPO_X960 ACER_TEMPO_X960 2542
+acer_eten_v900 MACH_ACER_ETEN_V900 ACER_ETEN_V900 2543
+acer_eten_x900 MACH_ACER_ETEN_X900 ACER_ETEN_X900 2544
+bonnell MACH_BONNELL BONNELL 2545
+oht_mx27 MACH_OHT_MX27 OHT_MX27 2546
+htcquartz MACH_HTCQUARTZ HTCQUARTZ 2547
+davinci_dm6467tevm MACH_DAVINCI_DM6467TEVM DAVINCI_DM6467TEVM 2548
+c3ax03 MACH_C3AX03 C3AX03 2549
+mxt_td60 MACH_MXT_TD60 MXT_TD60 2550
+esyx MACH_ESYX ESYX 2551
+dove_db2 MACH_DOVE_DB2 DOVE_DB2 2552
+bulldog MACH_BULLDOG BULLDOG 2553
+derell_me2000 MACH_DERELL_ME2000 DERELL_ME2000 2554
+bcmring_base MACH_BCMRING_BASE BCMRING_BASE 2555
+bcmring_evm MACH_BCMRING_EVM BCMRING_EVM 2556
+bcmring_evm_jazz MACH_BCMRING_EVM_JAZZ BCMRING_EVM_JAZZ 2557
+bcmring_sp MACH_BCMRING_SP BCMRING_SP 2558
+bcmring_sv MACH_BCMRING_SV BCMRING_SV 2559
+bcmring_sv_jazz MACH_BCMRING_SV_JAZZ BCMRING_SV_JAZZ 2560
+bcmring_tablet MACH_BCMRING_TABLET BCMRING_TABLET 2561
+bcmring_vp MACH_BCMRING_VP BCMRING_VP 2562
+bcmring_evm_seikor MACH_BCMRING_EVM_SEIKOR BCMRING_EVM_SEIKOR 2563
+bcmring_sp_wqvga MACH_BCMRING_SP_WQVGA BCMRING_SP_WQVGA 2564
+bcmring_custom MACH_BCMRING_CUSTOM BCMRING_CUSTOM 2565
+acer_s200 MACH_ACER_S200 ACER_S200 2566
+bt270 MACH_BT270 BT270 2567
+iseo MACH_ISEO ISEO 2568
+cezanne MACH_CEZANNE CEZANNE 2569
+lucca MACH_LUCCA LUCCA 2570
+supersmart MACH_SUPERSMART SUPERSMART 2571
+arm11_board MACH_CS_MISANO CS_MISANO 2572
+magnolia2 MACH_MAGNOLIA2 MAGNOLIA2 2573
+emxx MACH_EMXX EMXX 2574
+outlaw MACH_OUTLAW OUTLAW 2575
+riot_bei2 MACH_RIOT_BEI2 RIOT_BEI2 2576
+riot_vox MACH_RIOT_VOX RIOT_VOX 2577
+riot_x37 MACH_RIOT_X37 RIOT_X37 2578
+mega25mx MACH_MEGA25MX MEGA25MX 2579
+benzina2 MACH_BENZINA2 BENZINA2 2580
+ignite MACH_IGNITE IGNITE 2581
+foggia MACH_FOGGIA FOGGIA 2582
+arezzo MACH_AREZZO AREZZO 2583
+leica_skywalker MACH_LEICA_SKYWALKER LEICA_SKYWALKER 2584
+jacinto2_jamr MACH_JACINTO2_JAMR JACINTO2_JAMR 2585
+gts_nova MACH_GTS_NOVA GTS_NOVA 2586
+p3600 MACH_P3600 P3600 2587
+dlt2 MACH_DLT2 DLT2 2588
+df3120 MACH_DF3120 DF3120 2589
+ecucore_9g20 MACH_ECUCORE_9G20 ECUCORE_9G20 2590
+nautel_lpc3240 MACH_NAUTEL_LPC3240 NAUTEL_LPC3240 2591
+glacier MACH_GLACIER GLACIER 2592
+phrazer_bulldog MACH_PHRAZER_BULLDOG PHRAZER_BULLDOG 2593
+omap3_bulldog MACH_OMAP3_BULLDOG OMAP3_BULLDOG 2594
+pca101 MACH_PCA101 PCA101 2595
+buzzc MACH_BUZZC BUZZC 2596
+sasie2 MACH_SASIE2 SASIE2 2597
+davinci_cio MACH_DAVINCI_CIO DAVINCI_CIO 2598
+smartmeter_dl MACH_SMARTMETER_DL SMARTMETER_DL 2599
+wzl6410 MACH_WZL6410 WZL6410 2600
+wzl6410m MACH_WZL6410M WZL6410M 2601
+wzl6410f MACH_WZL6410F WZL6410F 2602
+wzl6410i MACH_WZL6410I WZL6410I 2603
+spacecom1 MACH_SPACECOM1 SPACECOM1 2604
+pingu920 MACH_PINGU920 PINGU920 2605
+bravoc MACH_BRAVOC BRAVOC 2606
+cybo2440 MACH_CYBO2440 CYBO2440 2607
diff --git a/crypto/Kconfig b/crypto/Kconfig
index dc20a34ba5ef..69ba63c2fb1d 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -19,6 +19,8 @@ menuconfig CRYPTO
if CRYPTO
+source "crypto/ocf/Kconfig"
+
comment "Crypto core or helper"
config CRYPTO_FIPS
@@ -132,6 +134,14 @@ config CRYPTO_TEST
help
Quick & dirty crypto test module.
+config CRYPTO_CRYPTODEV
+ tristate "Cryptodev (/dev/crypto) interface"
+ depends on CRYPTO
+ select CRYPTO_ALGAPI
+ help
+ Device /dev/crypto gives userspace programs access to
+ kernel crypto algorithms.
+
comment "Authenticated Encryption with Associated Data"
config CRYPTO_CCM
diff --git a/crypto/Makefile b/crypto/Makefile
index cd4a4ed078ff..d9200dc4a973 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -78,8 +78,15 @@ obj-$(CONFIG_CRYPTO_RNG2) += krng.o
obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
+obj-$(CONFIG_CRYPTO_CRYPTODEV) += cryptodev.o
+
#
# generic algorithms and the async_tx api
#
obj-$(CONFIG_XOR_BLOCKS) += xor.o
obj-$(CONFIG_ASYNC_CORE) += async_tx/
+
+#
+# OCF layer
+#
+obj-$(CONFIG_OCF_OCF) += ocf/
diff --git a/crypto/cryptodev.c b/crypto/cryptodev.c
new file mode 100644
index 000000000000..5b8d00b02f28
--- /dev/null
+++ b/crypto/cryptodev.c
@@ -0,0 +1,558 @@
+/*
+ * Driver for /dev/crypto device (aka CryptoDev)
+ *
+ * Copyright (c) 2004 Michal Ludvig <mludvig@suse.cz>, SuSE Labs
+ *
+ * Device /dev/crypto provides an interface for
+ * accessing kernel CryptoAPI algorithms (ciphers,
+ * hashes) from userspace programs.
+ *
+ * /dev/crypto interface was originally introduced in
+ * OpenBSD and this module attempts to keep the API,
+ * although a bit extended.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/miscdevice.h>
+#include <linux/crypto.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/random.h>
+#include <linux/cryptodev.h>
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <linux/scatterlist.h>
+
+MODULE_AUTHOR("Michal Ludvig <mludvig@suse.cz>");
+MODULE_DESCRIPTION("CryptoDev driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/* ====== Compile-time config ====== */
+
+#define CRYPTODEV_STATS
+
+/* ====== Module parameters ====== */
+
+static int verbosity = 0;
+module_param(verbosity, int, 0644);
+MODULE_PARM_DESC(verbosity, "0: normal, 1: verbose, 2: debug");
+
+#ifdef CRYPTODEV_STATS
+static int enable_stats = 0;
+module_param(enable_stats, int, 0644);
+MODULE_PARM_DESC(enable_stats, "collect statictics about cryptodev usage");
+#endif
+
+/* ====== Debug helpers ====== */
+
+#define PFX "cryptodev: "
+#define dprintk(level,severity,format,a...) \
+ do { \
+ if (level <= verbosity) \
+ printk(severity PFX "%s[%u]: " format, \
+ current->comm, current->pid, \
+ ##a); \
+ } while (0)
+
+/* ====== CryptoAPI ====== */
+
+struct csession {
+ struct list_head entry;
+ struct semaphore sem;
+ struct crypto_blkcipher *tfm;
+ uint32_t sid;
+#ifdef CRYPTODEV_STATS
+#if ! ((COP_ENCRYPT < 2) && (COP_DECRYPT < 2))
+#error Struct csession.stat uses COP_{ENCRYPT,DECRYPT} as indices. Do something!
+#endif
+ unsigned long long stat[2];
+ size_t stat_max_size, stat_count;
+#endif
+};
+
+struct fcrypt {
+ struct list_head list;
+ struct semaphore sem;
+};
+
+/* Prepare session for future use. */
+static int
+crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
+{
+ struct csession *ses_new, *ses_ptr;
+ struct crypto_blkcipher *tfm;
+ int ret = 0;
+ char alg_name[MAX_ALG_NAME_LEN+1];
+ char alg_full_name[MAX_ALG_NAME_LEN+1];
+ const char *mode;
+ u8 *keyp;
+
+ /* Does the request make sense? */
+ if (!sop->cipher == !sop->mac) {
+ dprintk(1, KERN_DEBUG, "Both 'cipher' and 'mac' set or unset.\n");
+ return -EINVAL;
+ }
+
+ /* Copy-in the algorithm name if necessary. */
+ if (!sop->alg_namelen) {
+ /* Hmm, compatibility with OpenBSD CRYPTO_* constants...
+ Should we support it? */
+ dprintk(2, KERN_DEBUG, "OpenBSD constants are not (yet?) supported.\n");
+ return -EINVAL;
+ }
+
+ if(sop->alg_namelen > MAX_ALG_NAME_LEN) {
+ dprintk(1, KERN_DEBUG, "Algorithm name too long (%zu > %u)\n",
+ sop->alg_namelen, MAX_ALG_NAME_LEN);
+ return -EINVAL;
+ }
+
+ copy_from_user(alg_name, sop->alg_name, sop->alg_namelen);
+ alg_name[sop->alg_namelen] = '\0';
+
+ if(!sop->cipher) {
+ dprintk(2, KERN_DEBUG, "Hashes are not yet supported.\n");
+ return -EINVAL;
+ }
+
+ switch (sop->cipher & CRYPTO_FLAG_MASK) {
+ case CRYPTO_FLAG_ECB: mode = "ebc"; break;
+ case CRYPTO_FLAG_CBC: mode = "cbc"; break;
+ case CRYPTO_FLAG_CFB: mode = "cfb"; break;
+ case CRYPTO_FLAG_CTR: mode = "ctr"; break;
+#if 0
+ /* These modes are not yet supported. */
+ case CRYPTO_FLAG_OFB: mode = "ofb"; break;
+#endif
+ default: return -EINVAL;
+ }
+ snprintf(alg_full_name, sizeof(alg_full_name) - 1, "%s(%s)", mode, alg_name);
+
+ /* Set-up crypto transform. */
+ tfm = crypto_alloc_blkcipher(alg_full_name, 0, 0);
+ if (!tfm) {
+ dprintk(1, KERN_DEBUG, "Failed to load transform for %s %s\n",
+ alg_name, mode);
+ return -EINVAL;
+ }
+
+#if 0
+ /* Was correct key length supplied? */
+ if ((sop->keylen < crypto_tfm_alg_min_keysize(tfm)) ||
+ (sop->keylen > crypto_tfm_alg_max_keysize(tfm))) {
+ dprintk(1, KERN_DEBUG,
+ "Wrong keylen '%zu' for algorithm '%s'. Use %u to %u.\n",
+ sop->keylen, alg_name, crypto_tfm_alg_min_keysize(tfm),
+ crypto_tfm_alg_max_keysize(tfm));
+ crypto_free_blkcipher(tfm);
+ return -EINVAL;
+ }
+#endif
+
+ /* Copy the key from user and set to TFM. */
+ keyp = kmalloc(sop->keylen, GFP_KERNEL);
+ if (keyp == NULL) {
+ dprintk(1, KERN_ERR,
+ "Unable to allocate key buffer.\n");
+ crypto_free_blkcipher(tfm);
+ return -ENOMEM;
+ }
+ copy_from_user(keyp, sop->key, sop->keylen);
+ ret = crypto_blkcipher_setkey(tfm, keyp, sop->keylen);
+ kfree(keyp);
+ if (ret) {
+ dprintk(2, KERN_DEBUG,
+ "Setting key failed for %s-%zu-%s: flags=0x%X\n",
+ alg_name, sop->keylen*8, mode, crypto_blkcipher_tfm(tfm)->crt_flags);
+ dprintk(2, KERN_DEBUG,
+ "(see CRYPTO_TFM_RES_* in <linux/crypto.h> for details)\n");
+ crypto_free_blkcipher(tfm);
+ return -EINVAL;
+ }
+
+ /* Create a session and put it to the list. */
+ ses_new = kmalloc(sizeof(*ses_new), GFP_KERNEL);
+ if(!ses_new)
+ return -ENOMEM;
+
+ memset(ses_new, 0, sizeof(*ses_new));
+ get_random_bytes(&ses_new->sid, sizeof(ses_new->sid));
+ ses_new->tfm = tfm;
+ init_MUTEX(&ses_new->sem);
+
+ down(&fcr->sem);
+restart:
+ list_for_each_entry(ses_ptr, &fcr->list, entry) {
+ /* Check for duplicate SID */
+ if (unlikely(ses_new->sid == ses_ptr->sid)) {
+ get_random_bytes(&ses_new->sid, sizeof(ses_new->sid));
+ /* Unless we have a broken RNG this
+ shouldn't loop forever... ;-) */
+ goto restart;
+ }
+ }
+
+ list_add(&ses_new->entry, &fcr->list);
+ up(&fcr->sem);
+
+ dprintk(2, KERN_DEBUG, "Added session 0x%08X (%s-%zu-%s)\n",
+ ses_new->sid, alg_name, sop->keylen*8, mode);
+
+ /* Fill in some values for the user. */
+ sop->ses = ses_new->sid;
+ sop->blocksize = crypto_blkcipher_blocksize(tfm);
+
+ return 0;
+}
+
+/* Everything that needs to be done when remowing a session. */
+static inline void
+crypto_destroy_session(struct csession *ses_ptr)
+{
+ if(down_trylock(&ses_ptr->sem)) {
+ dprintk(2, KERN_DEBUG, "Waiting for semaphore of sid=0x%08X\n",
+ ses_ptr->sid);
+ down(&ses_ptr->sem);
+ }
+ dprintk(2, KERN_DEBUG, "Removed session 0x%08X\n", ses_ptr->sid);
+#if defined(CRYPTODEV_STATS)
+ if(enable_stats)
+ dprintk(2, KERN_DEBUG,
+ "Usage in Bytes: enc=%llu, dec=%llu, max=%zu, avg=%lu, cnt=%zu\n",
+ ses_ptr->stat[COP_ENCRYPT], ses_ptr->stat[COP_DECRYPT],
+ ses_ptr->stat_max_size, ses_ptr->stat_count > 0
+ ? ((unsigned long)(ses_ptr->stat[COP_ENCRYPT]+
+ ses_ptr->stat[COP_DECRYPT]) /
+ ses_ptr->stat_count) : 0,
+ ses_ptr->stat_count);
+#endif
+ crypto_free_blkcipher(ses_ptr->tfm);
+ ses_ptr->tfm = NULL;
+ up(&ses_ptr->sem);
+ kfree(ses_ptr);
+}
+
+/* Look up a session by ID and remove. */
+static int
+crypto_finish_session(struct fcrypt *fcr, uint32_t sid)
+{
+ struct csession *tmp, *ses_ptr;
+ struct list_head *head;
+ int ret = 0;
+
+ down(&fcr->sem);
+ head = &fcr->list;
+ list_for_each_entry_safe(ses_ptr, tmp, head, entry) {
+ if(ses_ptr->sid == sid) {
+ list_del(&ses_ptr->entry);
+ crypto_destroy_session(ses_ptr);
+ break;
+ }
+ }
+
+ if (!ses_ptr) {
+ dprintk(1, KERN_ERR, "Session with sid=0x%08X not found!\n", sid);
+ ret = -ENOENT;
+ }
+ up(&fcr->sem);
+
+ return ret;
+}
+
+/* Remove all sessions when closing the file */
+static int
+crypto_finish_all_sessions(struct fcrypt *fcr)
+{
+ struct csession *tmp, *ses_ptr;
+
+ down(&fcr->sem);
+ list_for_each_entry_safe(ses_ptr, tmp, &fcr->list, entry) {
+ list_del(&ses_ptr->entry);
+ crypto_destroy_session(ses_ptr);
+ }
+ up(&fcr->sem);
+
+ return 0;
+}
+
+/* Look up session by session ID. The returned session is locked. */
+static struct csession *
+crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid)
+{
+ struct csession *ses_ptr;
+
+ down(&fcr->sem);
+ list_for_each_entry(ses_ptr, &fcr->list, entry) {
+ if(ses_ptr->sid == sid) {
+ down(&ses_ptr->sem);
+ break;
+ }
+ }
+ up(&fcr->sem);
+
+ return ses_ptr;
+}
+
+/* This is the main crypto function - feed it with plaintext
+ and get a ciphertext (or vice versa :-) */
+static int
+crypto_run(struct fcrypt *fcr, struct crypt_op *cop)
+{
+ char *data, *ivp;
+ char __user *src, __user *dst;
+ struct scatterlist sg;
+ struct csession *ses_ptr;
+ unsigned int ivsize;
+ size_t nbytes, bufsize;
+ int ret = 0;
+ struct blkcipher_desc desc;
+
+ nbytes = cop->len;
+
+ if (cop->op != COP_ENCRYPT && cop->op != COP_DECRYPT) {
+ dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", cop->op);
+ return -EINVAL;
+ }
+
+ ses_ptr = crypto_get_session_by_sid(fcr, cop->ses);
+ if (!ses_ptr) {
+ dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", cop->ses);
+ return -EINVAL;
+ }
+
+ if (nbytes % crypto_blkcipher_blocksize(ses_ptr->tfm)) {
+ dprintk(1, KERN_ERR,
+ "data size (%zu) isn't a multiple of block size (%u)\n",
+ nbytes, crypto_blkcipher_blocksize(ses_ptr->tfm));
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ bufsize = PAGE_SIZE < nbytes ? PAGE_SIZE : nbytes;
+ data = (char*)__get_free_page(GFP_KERNEL);
+
+ if (unlikely(!data)) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ ivsize = crypto_blkcipher_ivsize(ses_ptr->tfm);
+
+ ivp = kmalloc(ivsize, GFP_KERNEL);
+ if (unlikely(!ivp)) {
+ free_page((unsigned long)data);
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ if (cop->iv) {
+ copy_from_user(ivp, cop->iv, ivsize);
+ crypto_blkcipher_set_iv(ses_ptr->tfm, ivp, ivsize);
+ }
+
+ src = cop->src;
+ dst = cop->dst;
+
+ desc.tfm = ses_ptr->tfm;
+ desc.info = NULL;
+ desc.flags = 0;
+
+ while (nbytes > 0) {
+ size_t current_len = nbytes > bufsize ? bufsize : nbytes;
+
+ copy_from_user(data, src, current_len);
+
+ sg_set_buf(&sg, data, current_len);
+
+ if (cop->op == COP_DECRYPT)
+ ret = crypto_blkcipher_decrypt(&desc, &sg, &sg, current_len);
+ else
+ ret = crypto_blkcipher_encrypt(&desc, &sg, &sg, current_len);
+
+ if (unlikely(ret)) {
+ dprintk(0, KERN_ERR, "CryptoAPI failure: flags=0x%x\n",
+ crypto_blkcipher_tfm(ses_ptr->tfm)->crt_flags);
+ goto out;
+ }
+
+ copy_to_user(dst, data, current_len);
+
+ nbytes -= current_len;
+ src += current_len;
+ dst += current_len;
+ }
+
+#if defined(CRYPTODEV_STATS)
+ if (enable_stats) {
+ /* this is safe - we check cop->op at the function entry */
+ ses_ptr->stat[cop->op] += cop->len;
+ if (ses_ptr->stat_max_size < cop->len)
+ ses_ptr->stat_max_size = cop->len;
+ ses_ptr->stat_count++;
+ }
+#endif
+
+out:
+ free_page((unsigned long)data);
+
+ kfree(ivp);
+
+out_unlock:
+ up(&ses_ptr->sem);
+
+ return ret;
+}
+
+/* ====== /dev/crypto ====== */
+
+static int
+cryptodev_open(struct inode *inode, struct file *filp)
+{
+ struct fcrypt *fcr;
+
+ fcr = kmalloc(sizeof(*fcr), GFP_KERNEL);
+ if(!fcr)
+ return -ENOMEM;
+
+ memset(fcr, 0, sizeof(*fcr));
+ init_MUTEX(&fcr->sem);
+ INIT_LIST_HEAD(&fcr->list);
+ filp->private_data = fcr;
+
+ return 0;
+}
+
+static int
+cryptodev_release(struct inode *inode, struct file *filp)
+{
+ struct fcrypt *fcr = filp->private_data;
+
+ if(fcr) {
+ crypto_finish_all_sessions(fcr);
+ kfree(fcr);
+ filp->private_data = NULL;
+ }
+ return 0;
+}
+
+static int
+clonefd(struct file *filp)
+{
+ int fd;
+
+ fd = get_unused_fd();
+ if (fd >= 0) {
+ get_file(filp);
+ fd_install(fd, filp);
+ }
+
+ return fd;
+}
+
+static int
+cryptodev_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct session_op sop;
+ struct crypt_op cop;
+ struct fcrypt *fcr = filp->private_data;
+ uint32_t ses;
+ int ret, fd;
+
+ if (!fcr)
+ BUG();
+
+ switch (cmd) {
+ case CRIOGET:
+ fd = clonefd(filp);
+ put_user(fd, (int*)arg);
+ return 0;
+
+ case CIOCGSESSION:
+ copy_from_user(&sop, (void*)arg, sizeof(sop));
+ ret = crypto_create_session(fcr, &sop);
+ if (ret)
+ return ret;
+ copy_to_user((void*)arg, &sop, sizeof(sop));
+ return 0;
+
+ case CIOCFSESSION:
+ get_user(ses, (uint32_t*)arg);
+ ret = crypto_finish_session(fcr, ses);
+ return ret;
+
+ case CIOCCRYPT:
+ copy_from_user(&cop, (void*)arg, sizeof(cop));
+ ret = crypto_run(fcr, &cop);
+ copy_to_user((void*)arg, &cop, sizeof(cop));
+ return ret;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+struct file_operations cryptodev_fops = {
+ .owner = THIS_MODULE,
+ .open = cryptodev_open,
+ .release = cryptodev_release,
+ .ioctl = cryptodev_ioctl,
+};
+
+struct miscdevice cryptodev = {
+ .minor = CRYPTODEV_MINOR,
+ .name = "crypto",
+ .fops = &cryptodev_fops,
+};
+
+static int
+cryptodev_register(void)
+{
+ int rc;
+
+ rc = misc_register (&cryptodev);
+ if (rc) {
+ printk(KERN_ERR PFX "registeration of /dev/crypto failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static void
+cryptodev_deregister(void)
+{
+ misc_deregister(&cryptodev);
+}
+
+/* ====== Module init/exit ====== */
+
+int __init
+init_cryptodev(void)
+{
+ int rc;
+
+ rc = cryptodev_register();
+ if (rc)
+ return rc;
+
+ printk(KERN_INFO PFX "driver loaded.\n");
+
+ return 0;
+}
+
+void __exit
+exit_cryptodev(void)
+{
+ cryptodev_deregister();
+ printk(KERN_INFO PFX "driver unloaded.\n");
+}
+
+module_init(init_cryptodev);
+module_exit(exit_cryptodev);
diff --git a/crypto/ocf/ChangeLog b/crypto/ocf/ChangeLog
new file mode 100644
index 000000000000..e913ef2890b5
--- /dev/null
+++ b/crypto/ocf/ChangeLog
@@ -0,0 +1,1960 @@
+2008-09-18 01:27 davidm
+
+ * Makefile, README, README.sglinux,
+ patches/linux-2.6.26-natt.patch, patches/linux-2.6.26-ocf.patch,
+ patches/openssl-0.9.8g.patch, patches/openssl-0.9.8i.patch:
+
+ Updates for a new OCF release with openssl-0.9.8i and linux-2.6.26
+ support.
+
+2008-09-18 00:19 davidm
+
+ * Config.in, Kconfig, Makefile, ep80579/Makefile,
+ ep80579/environment.mk, ep80579/icp_asym.c, ep80579/icp_common.c,
+ ep80579/icp_ocf.h, ep80579/icp_sym.c,
+ ep80579/linux_2.6_kernel_space.mk:
+
+ A new driver from Intel for their Intel QuickAssist enabled EP80579
+ Integrated Processor Product Line.
+
+ Adrian Hoban Brad Vrabete
+
+
+2008-07-25 01:01 gerg
+
+ * ocf-compat.h:
+
+ From linux-2.6.26 onwards there is now a linux/fdtable.h include
+ that contains the file_fdtable() definition.
+
+2008-07-05 01:20 davidm
+
+ * Makefile, patches/linux-2.6.25-natt.patch,
+ patches/linux-2.6.25-ocf.patch:
+
+ A new ocf-linux release and some patches to send to the OS guys.
+
+2008-07-03 21:21 davidm
+
+ * crypto.c:
+
+ Clean up a some possible deadlock/busy wait issues with
+ locking/sleeping. This has greatly improved openswan 2.6.14
+ reliability ;-)
+
+ Make more of our state available in /sys for debugging.
+
+2008-06-23 20:38 davidm
+
+ * Config.in, Kconfig:
+
+ Some settings should be bool's John Gumb
+
+2008-05-28 03:43 philipc
+
+ * ixp4xx/ixp4xx.c: Revert previous checkin since the
+ callbacks are meant to complete the crypto request. Before
+ reverting this, CONFIG_DEBUG_SLAB gives: slab error in
+ verify_redzone_free(): cache `ixp4xx_q': double free detected when
+ trying to ssh to the device.
+
+2008-04-27 19:31 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ We were not completing a crypto request under some error
+ conditions.
+
+2008-04-02 01:51 davidm
+
+ * talitos/talitos.c:
+
+ This patch is for crypto/ocf/talitos for use on linux 2.6.23. It
+ is applied to the ocf-linux-20071215 release.
+
+ Signed-off-by: Lee Nipper
+
+2008-02-29 00:43 davidm
+
+ * crypto.c, ocf-compat.h, ixp4xx/ixp4xx.c:
+
+ freshen up the 2.4 support, some recent OCF changes and openswan
+ changes are not that old-os friendly.
+
+ Force OCF to select HW/SW, otherwise it may get stuck on the first
+ device. This change means we will favour HW over SW, but we will
+ use both as required. Passing in a crid of 0 to crypto_newsession
+ effectively meant we were stuck on the first device registered with
+ OCF, not good. This only applied to ipsec, cryptodev already did
+ the right thing.
+
+2008-01-31 07:37 gerg
+
+ * hifn/hifn7751.c:
+
+ The linux-2.6.24 modules build fails if the pci ID table doesn't
+ have a NULL entry at the end. So add one.
+
+2008-01-29 09:16 gerg
+
+ * cryptosoft.c, ocf-compat.h:
+
+ Added some compatability macros for scatterlist changes from 2.6.24
+ onwards.
+
+2007-12-16 07:31 davidm
+
+ * Makefile:
+
+ missed an openssl patch name change
+
+2007-12-16 07:27 davidm
+
+ * Makefile, README, README.sglinux, patches/linux-2.6.23-ocf.patch,
+ patches/openssl-0.9.8e.patch, patches/openssl-0.9.8g.patch,
+ patches/ssl.patch:
+
+ updates for a new ocf release and associated bits
+
+2007-12-16 06:36 davidm
+
+ * crypto.c:
+
+ Be very careful what you do while potentially in an driver unload
+ state or we will call through NULL pointers.
+
+ Reported by Nawang Chhetan .
+
+2007-12-14 22:32 davidm
+
+ * cryptodev.c:
+
+ Add in an unlock_ioctl when available to help SMP systems a lot.
+ Otherwise all ioctls get a BKL :-(
+
+ Problem found by Egor N. Martovetsky
+
+2007-12-14 18:29 davidm
+
+ * cryptosoft.c:
+
+ reformat the alg table to make it easier to read.
+
+2007-12-14 18:29 davidm
+
+ * crypto.c:
+
+ Fix more driver locking/sleeping bugs report by Nawang Chhetan
+
+
+2007-12-12 21:36 davidm
+
+ * Config.in, Kconfig, Makefile, pasemi/Makefile, pasemi/pasemi.c,
+ pasemi/pasemi_fnu.h:
+
+ Here I'm including my PA Semi driver patch to OCF. Please consider
+ it for inclusion into next OCF release.
+
+ Egor N. Martovetsky
+
+2007-12-05 00:37 davidm
+
+ * patches/: linux-2.4.35-ocf.patch, linux-2.6.22-ocf.patch:
+
+ More correct count setting if we get a signal
+
+ Adrian Hoban
+
+2007-12-05 00:02 davidm
+
+ * random.c:
+
+ OCF has a static array for holding random data. The random number
+ generator I have can write directly into physically contiguous
+ memory. Static memory comes from the heap and isn't physically
+ contiguous. I could use kmalloc'd memory and then copy into the OCF
+ static buf but I'd like to avoid a memory copy. The following patch
+ (Physically_Contig_Rand.patch) allows me to avoid a memory copy and
+ should not impact the other OCF drivers:
+
+ Adrian Hoban
+
+2007-12-05 00:01 davidm
+
+ * Kconfig:
+
+ Fix a typo in the Kconfig
+
+2007-11-23 19:15 davidm
+
+ * talitos/talitos_dev.h:
+
+ fix the DPRINTF macro so that it actually compiles.
+
+2007-11-22 19:41 davidm
+
+ * ocf-compat.h, talitos/talitos.c, talitos/talitos_dev.h:
+
+ Various updates to get talitos compiling and work on real-world (ie
+ ubuntu) kernels.
+
+2007-11-08 02:21 davidm
+
+ * crypto.c:
+
+ keep and eye on us being completely blocked. If we have Q's to
+ process, but all the requests are blocked, sleep. We do not want
+ to busy loop until a driver unblocks as it uses valuable CPU
+ resources that could be doing something much more important ;-)
+
+2007-11-07 19:04 davidm
+
+ * hifn/hifn7751.c:
+
+ hifn driver was failing to unblock itself under some "out of
+ resources" conditions. It would return ERESTART to signal it was
+ full but never call crypto_unblock to start things moving again.
+
+2007-11-06 02:09 davidm
+
+ * hifn/hifn7751.c:
+
+ Remove some bogus trace left in the driver for the overflow (too
+ busy) case.
+
+2007-10-12 21:10 gerg
+
+ * crypto.c, ixp4xx/ixp4xx.c:
+
+ Fix up use of kmem_cache_create() - it takes one less argument in
+ 2.6.23 onwards.
+
+2007-10-03 02:41 gerg
+
+ * ixp4xx/Makefile:
+
+ The directory locations for includes in CSR-2.4 is different. Need
+ to modify the CFLAGS accordingly if using CSR-2.4.
+
+2007-09-22 00:39 philipc
+
+ * ixp4xx/Makefile: linux 2.4 make dep was failing. This is
+ a quick fix to get it building, need to double check this.
+
+2007-09-19 00:13 mmccreat
+
+ * Config.in: Add config option CONFIG_OCF_IXP4XX_SHA1_MD5,
+ that enables SHA1 and MD5 hashing to be done by the IXP4xx crypto
+ accelerator (although it is much slower than using cryptosoft).
+
+2007-09-18 21:45 mmccreat
+
+ * Makefile, random.c: - Force the inclusion of autoconf.h,
+ which contains #defines for CONFIG_xxx options for OCF. -
+ Removing additional -D option, now that we are including the
+ CONFIG_xxx #defines.
+
+2007-09-18 21:44 mmccreat
+
+ * Kconfig: Add config option CONFIG_OCF_IXP4XX_SHA1_MD5,
+ that enables SHA1 and MD5 hashing to be done by the IXP4xx crypto
+ accelerator (although it is much slower than using cryptosoft).
+
+2007-09-18 21:37 mmccreat
+
+ * cryptodev.h: The CRYPTO_MAX_DATA_LEN limit should be
+ 0xFFFF ie 64K - 1.
+
+2007-09-18 21:19 mmccreat
+
+ * ixp4xx/ixp4xx.c: - Rework the code so that the correct IXP
+ function, ixCryptoAccHashPerform(), is used to calculate SHA1 and
+ MD5 hashes. NB: The performance of using the IXP4xx hardware is
+ really, really poor compared to using cryptosoft (and the kernel
+ crypto). - Only support SHA1 and MD5 hashing if the
+ CONFIG_OCF_IXP4XX_SHA1_MD5 is enabled.
+
+2007-08-30 21:42 davidm
+
+ * Makefile:
+
+ do not archive build files in the crypto-tools archive
+
+2007-08-22 19:19 mmccreat
+
+ * cryptodev.c: Fix up the checking for key lengths, when the
+ key can be of unlimited size.
+
+2007-08-16 01:50 davidm
+
+ * Makefile:
+
+ Better 2.4 compat for "make dep" now working with fastdep.
+
+2007-07-28 08:25 davidm
+
+ * Makefile, README, README.sglinux, patches/crypto-tools.patch,
+ patches/linux-2.4.29-ocf.patch, patches/linux-2.4.35-ocf.patch,
+ patches/linux-2.6.11-ocf.patch, patches/linux-2.6.22-ocf.patch,
+ patches/ssl.patch:
+
+ Update all the patches and put the patch making target back into
+ the Makefile.
+
+2007-07-28 08:25 davidm
+
+ * hifn/hifn7751.c:
+
+ fix an unused variable warning when HARVESTING is disabled
+
+2007-07-27 21:33 davidm
+
+ * hifn/hifn7751.c, ixp4xx/ixp4xx.c, safe/safe.c, talitos/talitos.c:
+
+
+ Remove all the random code if OCF does not have radom harvesting
+ enabled.
+
+2007-07-26 00:36 davidm
+
+ * Kconfig, hifn/hifnHIPP.c, hifn/hifnHIPPvar.h:
+
+ Changes to get the hifn HIPP stub driver to build.
+
+2007-07-25 21:25 davidm
+
+ * Makefile, hifn/Makefile, hifn/hifnHIPP.c, hifn/hifnHIPPreg.h,
+ hifn/hifnHIPPvar.h, ixp4xx/Makefile, ocfnull/Makefile,
+ safe/Makefile, talitos/Makefile:
+
+ Bring in the hifnHIPP driver written by Xelerance. This is the
+ super hifn chip with full protocol offload.
+
+ Switch to much more traditional Makefile/subdir building. The
+ Makefiles are nicer now, but still not beautiful, 2.6 and 2.4
+ capable builds result in a certain amount of uglyiness.
+
+2007-07-24 21:46 davidm
+
+ * cryptodev.c:
+
+ Clean up all the driver id checking and session management so
+ adding/removing drivers all continues to run cleanly.
+
+2007-07-24 20:14 davidm
+
+ * talitos/talitos.c:
+
+ From: Ahsan Kabir
+
+ less than .1% packet corruption was detected using the talitos
+ driver. It turns out we don't need the cipher iv out len/ptr field
+ to do ESP IPsec. Therefore we set the len field as 0, which tells
+ the SEC not to do anything with this len/ptr field.
+
+ Signed-off-by: Ahsan Kabir
+ Signed-off-by: Kim Phillips
+
+2007-07-24 08:25 davidm
+
+ * cryptosoft.c, ocf-bench.c, hifn/hifn7751.c, ixp4xx/ixp4xx.c,
+ safe/safe.c, talitos/talitos.c:
+
+ Switch the remaining GFP_ATOMIC to the newer and not deprecated
+ SLAB_ATOMIC
+
+2007-07-23 22:16 mmccreat
+
+ * ixp4xx/ixp4xx.c: Add missing ";" from end of dprintk()
+ call!
+
+2007-07-21 01:16 davidm
+
+ * ocf-compat.h, rndtest.c, hifn/hifn7751.c, safe/safe.c:
+
+ pci_register_driver is nothing like it is depending on the kernel,
+ so we need a compat function to fix it up for all kernels before
+ 2.6.10, and in different ways for older and not so older versions.
+
+2007-07-20 21:54 davidm
+
+ * safe/safe.c:
+
+ Make the debug macro ';' safe so you do not get compiler warnings
+
+2007-07-20 21:53 davidm
+
+ * talitos/: talitos.c, talitos_dev.h, talitos_soft.h:
+
+ update to the latest FreeBSD driver structure and fix up the code
+ as required.
+
+2007-07-20 03:07 davidm
+
+ * rndtest.c:
+
+ more headers needed to compile on 2.4
+
+2007-07-20 03:00 davidm
+
+ * cryptosoft.c:
+
+ Put in the 2.4 stubs to support compression
+
+2007-07-20 02:53 davidm
+
+ * crypto.c, ocf-compat.h:
+
+ move some more compat stuff into the compat header.
+
+2007-07-20 02:47 davidm
+
+ * talitos/talitos.c:
+
+ support of_platform_driver for newer, ARCH=powerpc based kernels.
+ Signed-off-by: Kim Phillips
+
+2007-07-20 02:46 davidm
+
+ * talitos/talitos.c:
+
+ From: Ahsan Kabir
+
+ When Talitos completes job both the channel and execution unit are
+ capable of generating interrupts. Talitos used to take two
+ interrupts per request - one for channel completion and the other
+ for execution unit completion. This patch ensures that Talitos
+ takes interrupt only for channel completion. Execution unit will
+ generate interrupt only when there is error and the error
+ interrupts for execution units are not masked.
+
+ Signed-off-by: Ahsan Kabir
+ Signed-off-by: Kim Phillips
+
+2007-07-20 02:37 davidm
+
+ * cryptodev.c:
+
+ Unless the user specifies, select from both HW and SW.
+
+ Clean up some debug to report the actual ioctl name.
+
+ Compiler warning on newer compilers.
+
+2007-07-20 02:35 davidm
+
+ * cryptodev.h:
+
+ moved dprintk to the compat code, seemed nicer in there.
+
+2007-07-20 02:35 davidm
+
+ * rndtest.c:
+
+ Need to clean up some warnings etc, more includes
+
+2007-07-20 02:34 davidm
+
+ * ocf-compat.h, hifn/hifn7751.c, safe/safe.c:
+
+ new shared IRQ flags for 2.6.22 and a safer version of the debug
+ macro
+
+2007-07-20 00:52 davidm
+
+ * cryptosoft.c:
+
+ Implement compression based on the code from the openswan guys.
+
+2007-07-20 00:52 davidm
+
+ * criov.c:
+
+ Fix compiler warning on non-value returning void func.
+
+2007-07-18 22:55 davidm
+
+ * hifn/hifn7751.c, safe/safe.c:
+
+ Use pci_register_driver rather than pci_module_init.
+ pci_module_init has been dropped in 2.6.22 yet pci_register_driver
+ has always existed and used to do some crazy hotplug junk.
+
+2007-07-18 21:55 gerg
+
+ * ixp4xx/ixp4xx.c:
+
+ Added a missing ";" at the end of the ixp_kproces() prototype. It
+ is IXP465 specific, only showed up when generateing for SG720.
+
+2007-07-17 00:37 davidm
+
+ * Makefile, cryptodev.c, random.c, rndtest.c, hifn/hifn7751.c,
+ ocfnull/ocfnull.c, safe/safe.c:
+
+ Fixup all the debug support for 2.4 kernels, clean up the entropy
+ harvester to be far more robust.
+
+2007-07-14 02:19 davidm
+
+ * talitos/talitos.c:
+
+ Old patch that had not been applied Kim Phillips
+
+
+2007-07-14 01:12 davidm
+
+ * Config.in, Kconfig, Makefile, cryptodev.c, cryptodev.h, random.c,
+ syscall.h:
+
+ Finally ditched all the syscall stuff. You can now enable/disable
+ the random harvestor. Pulled in most of random.c from openswan
+ project and fixed some obvious bugs (that were always there).
+
+2007-07-13 21:59 davidm
+
+ * ocf-compat.h:
+
+ Better error printing and checking for drivers
+
+2007-07-13 21:56 davidm
+
+ * cryptosoft.c:
+
+ Fix some incorrect debug (reporting wrong error type)
+
+2007-07-13 21:55 davidm
+
+ * hifn/hifn7751.c, safe/safe.c:
+
+ Make the code more similar to Free-BSD by reverting to the same
+ debug macros
+
+2007-07-13 21:53 davidm
+
+ * ocfnull/ocfnull.c:
+
+ Fix up the null driver to work again in the new framework.
+
+2007-07-06 23:54 mmccreat
+
+ * cryptodev.c, cryptodev.h, cryptosoft.c: - Update OCF to
+ work with new Crypto API introduced in 2.6.19 kerneli, and add
+ macros so it work with older kernels. - Add support for SHA256,
+ SHA384 and SHA512 HASH and HMAC algorithms. - Cryptosoft: Only
+ register algorithms that the kernel has implementations for.
+
+2007-07-03 19:52 davidm
+
+ * Kconfig, README, README.sglinux, criov.c, crypto.c, cryptodev.c,
+ cryptodev.h, cryptosoft.c, ocf-bench.c, ocf-compat.h, random.c,
+ rndtest.c, uio.h, hifn/hifn7751.c, hifn/hifn7751reg.h,
+ hifn/hifn7751var.h, ixp4xx/ixp4xx.c, ocfnull/ocfnull.c,
+ safe/safe.c, safe/safevar.h, talitos/talitos.c:
+
+ Updated OCF to the lastest FreeBSD version.
+
+ There was a lot of change in here, some of which will help FIP's,
+ some which won't.
+
+ Did lots of cleaning and diff reduction against the freebsd code.
+ Still more cleaning to do.
+
+2007-06-01 21:58 gerg
+
+ * Config.in:
+
+ Put the regular old Config.in back, needed for puclic releases.
+
+2007-06-01 21:58 gerg
+
+ * Kconfig:
+
+ Change the CONFIG_OCF_IXP400 dependencies to be the same as they
+ where in the Config.in file.
+
+2007-05-28 21:40 gerg
+
+ * Config.in, Kconfig:
+
+ Switch all module configuration over to new style Kconfigs.
+
+2007-05-24 18:49 davidm
+
+ * cryptodev.c, random.c:
+
+ Work around some problems on redhat systems with errno redefinition
+
+2007-04-30 21:09 gerg
+
+ * cryptosoft.c:
+
+ The CRYPTO_TFM_MODE_ family of defines no longer exists from 2.6.21
+ onwards. As far as I can tell you don't need to pass it to the
+ crypto_alloc_tfm() function anymore.
+
+ So define it to be 0 if it doesn't exist.
+
+2007-04-03 02:13 gerg
+
+ * syscall.h:
+
+ Added syscall macros for SH architecture. Just temporary, 'till
+ Dave fixes the OCF code to not use syscalls from the modules :-)
+
+2007-02-16 23:10 davidm
+
+ * syscall.h:
+
+ ensure the temprary 2.6 fix doesn't break 2.4
+
+2007-02-07 22:23 gerg
+
+ * cryptodev.c, random.c, syscall.h:
+
+ Temporary fix for new 2.6 kernels no longer defining in-kernel
+ system call functions. Define them locally for now until we fix
+ properly.
+
+2007-02-07 03:10 gerg
+
+ * ixp4xx/ixp4xx.c:
+
+ Changes to support the different INIT_WORK() mechanism from kernels
+ 2.6.20 onwards.
+
+2007-02-06 02:38 gerg
+
+ * crypto.c:
+
+ Cleaned up use of kmem_cache_t and use of SLAB_ATOMIC.
+
+2006-12-05 20:50 cpascoe
+
+ * hifn/hifn7751.c, safe/safe.c, talitos/talitos.c:
+
+ Remove pt_regs from OCF interrupt handlers for 2.6.19+
+
+2006-12-02 03:36 gerg
+
+ * criov.c, crypto.c, cryptodev.c, cryptosoft.c, ocf-bench.c,
+ random.c, rndtest.c, hifn/hifn7751.c, ixp4xx/ixp4xx.c,
+ ocfnull/ocfnull.c, safe/safe.c, talitos/talitos.c:
+
+ Can no longer include linux/config.h as of 2.6.19 kernels. Need to
+ conditionally include it based on AUTOCONF_INCLUDED not being
+ defined.
+
+2006-10-13 21:52 cpascoe
+
+ * random.c:
+
+ Remove another race condition that may result in us running more
+ than one random thread if modules are reloaded during heavy system
+ load.
+
+2006-10-13 21:18 cpascoe
+
+ * random.c:
+
+ - Permit delivery of SIGKILL to the random thread. - Do not exit
+ prematurely if poll() is interrupted. - Improve exit conditions so
+ that we quit immediately, rather than loop infinitely, if the
+ last RNG source is removed while we are trying to fill our
+ buffer.
+
+2006-10-03 20:28 cpascoe
+
+ * crypto.c, crypto.c: AutoMerged
+ >
+ > Zero the empty half of "new driver" buffer, and not past the end
+ of the old
+ > buffer that we are about to free.
+
+2006-10-03 20:28 cpascoe
+
+ * crypto.c:
+
+ Zero the empty half of "new driver" buffer, and not past the end of
+ the old buffer that we are about to free.
+
+2006-08-25 23:57 davidm
+
+ * cryptosoft.c:
+
+ Do not print errors for failed ALG requests unless debugging
+
+2006-07-14 21:44 davidm
+
+ * cryptodev.h:
+
+ 2.6.11 and earlier did not have the files_fdtable macro.
+
+2006-06-21 21:26 gerg
+
+ * cryptodev.h, hifn/hifn7751.c, ocfnull/ocfnull.c, safe/safe.c,
+ talitos/talitos.c:
+
+ Fixed up more occurrances of MODULE_PARM() needing to be converted
+ to module_param() (as of 2.6.17 and onwards).
+
+2006-06-21 00:28 gerg
+
+ * cryptosoft.c:
+
+ Change use of MODULE_PARM to module_param() for 2.6.17+ kernels.
+
+2006-06-20 22:13 gerg
+
+ * crypto.c, cryptodev.c, cryptodev.h, ocf-bench.c, ixp4xx/ixp4xx.c:
+
+
+ As of 2.6.17 and onwards module_param is used in place of
+ MODULE_PARM.
+
+2006-06-06 00:31 gerg
+
+ * Makefile:
+
+ Fix Makefile to find includes is using CSR-2.1.
+
+2006-05-31 01:44 gerg
+
+ * cryptodev.h:
+
+ The vars "crypto_usercrypto", "crypto_userasymcrypto" are declared
+ as extern in the header file, but static in the c file. I guessed
+ that they should probably be truely static, so removed the extern
+ declarations from the header.
+
+2006-05-25 21:06 davidm
+
+ * talitos/talitos.c:
+
+ This fixes a situation that I never provably experienced, where a
+ descriptor in memory may be reserved within the proper lock, and
+ freed immediately after, only for a few cycles, right outside the
+ lock. Kim Phillips
+
+2006-05-15 19:49 davidm
+
+ * criov.c, crypto.c, cryptodev.c, cryptodev.h, cryptosoft.c,
+ ocf-bench.c, random.c, rndtest.c, uio.h, ixp4xx/ixp4xx.c:
+
+ Remove the "all rights reserved" from the Intel copyrights.
+
+2006-05-12 21:19 davidm
+
+ * Config.in, Kconfig, Makefile, cryptodev.c, ocf-bench.c,
+ ocfnull/ocfnull.c:
+
+ Add in a null OCF driver that does nothing at all, useful for
+ measuring the cost of various parts of the ipsec stack.
+
+2006-05-12 21:17 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ make sure we do not overwrite a correctly set error type.
+
+2006-05-12 06:52 davidm
+
+ * crypto.c:
+
+ Fix a problem where a driver would return ERESTART (full) but then
+ unblock itself before the upper layer had marked it as blocked.
+ This caused the code to get stuck in crypto_proc and process no
+ more requests.
+
+2006-05-12 06:47 davidm
+
+ * cryptosoft.c:
+
+ Implement CRD_F_KEY_EXPLICIT for cryptosoft so keys can be changed
+ on an active session.
+
+2006-05-10 20:09 davidm
+
+ * README, criov.c, crypto.c, cryptodev.h, cryptosoft.c,
+ ocf-bench.c, random.c, rndtest.c, uio.h, hifn/hifn7751.c,
+ ixp4xx/ixp4xx.c, safe/safe.c, safe/safevar.h:
+
+ update email addresses and other house cleaning
+
+2006-05-10 20:08 davidm
+
+ * cryptodev.c:
+
+ pull in better error checking from openswan modifications
+
+2006-05-10 19:11 davidm
+
+ * cryptosoft.c:
+
+ Fix an unused variable warning when various options are disabled.
+
+2006-05-10 19:10 davidm
+
+ * cryptodev.h:
+
+ Add support for 2.4 kernels for the new FD cloning operation
+
+2006-05-09 19:48 davidm
+
+ * hifn/hifn7751.c:
+
+ remove the hifn 7855 support, this driver will never work with
+ that chip.
+
+2006-05-08 23:34 davidm
+
+ * hifn/hifn7751var.h:
+
+ Contiguous buffer support so that ocf-bench can run properly.
+
+2006-05-05 23:21 davidm
+
+ * hifn/hifn7751.c:
+
+ Add in contiguous buffer support so that ocf-bench and run on the
+ driver.
+
+2006-05-05 23:14 davidm
+
+ * ocf-bench.c:
+
+ Our requests were out of order, need to do crypto then auth on
+ encrypt requests. Some drivers enforce this.
+
+2006-05-04 23:21 davidm
+
+ * crypto.c:
+
+ Do not run "newsession" with lock, since newsession may sleep on
+ some targets. Handle the accounting so that things are not pulled
+ from underneath us.
+
+2006-05-04 23:20 davidm
+
+ * cryptodev.c:
+
+ Switch to a less optimal (marginally) solution for creating a new
+ fd that appears to work in far more versions of the kernel
+ including 64bit versions.
+
+2006-05-04 18:54 davidm
+
+ * ocf-bench.c:
+
+ Turn off the IXP access lib benchmarking by default as most people
+ don't have it.
+
+ Paul Wouters
+
+2006-04-01 08:23 davidm
+
+ * Makefile:
+
+ Remove more temp files when cleaning
+
+2006-04-01 08:12 davidm
+
+ * hifn/hifn7751reg.h:
+
+ 7855 PCI id's as yet untested
+
+2006-04-01 08:08 davidm
+
+ * hifn/hifn7751.c:
+
+ add PCI id's for the 7855 and AES support, card is untested still
+ as it requires 128MB of PCI memory !
+
+2006-03-31 08:38 davidm
+
+ * README.sglinux:
+
+ small update to instructions with corrected patch name
+
+2006-03-31 00:23 davidm
+
+ * Config.in:
+
+ Add the Talitos driver to the 2.4 config, even though it probably
+ won't compile.
+
+2006-03-30 07:48 davidm
+
+ * Kconfig, Makefile, talitos/talitos.c, talitos/talitos_dev.h,
+ talitos/talitos_soft.h:
+
+ Please find attached the freescale SEC driver for OCF. It's been
+ (most recently) tested on an SEC2.0 based MPC8541E
+ (cryptographically identical to the MPC8555E) under 2.6.15.2, with
+ openssl-0.9.8a and openswan-2.4.3 (2.4.5rc5 won't keep the security
+ association up for me for some reason).
+
+ Please feel free to add it to your next release of OCF-Linux :-)
+
+ Kim Phillips
+
+2006-03-20 19:34 davidm
+
+ * safe/: safe.c, safevar.h:
+
+ Safenet 1141 v1.0 chips have a DMA lockup error if you access the
+ chip while DMA'ing. As a work around you need to limit your DMA to
+ 256 byte chunks using scatter/gather descriptors. All the SG/SME
+ products have v1.0 chips and would lockup with more than two
+ outstanding packets at one time.
+
+ Fix the KASSERT macro
+
+ Add some more exhaustive initialisation.
+
+2006-03-15 21:58 davidm
+
+ * cryptodev.h, random.c:
+
+ Switch random support to "ints" since that is what the kernel uses
+ and not using the same thing is 64bit wise a bad idea.
+
+ Fix FIP's code to ensure correct amount of data is passed in.
+
+ Add work around for broken 64bit OS RNG support (disable it)
+
+ General code cleanups.
+
+2006-03-15 21:55 davidm
+
+ * hifn/hifn7751.c:
+
+ Fixes for 64bit OS's, make sure PCI address are within bus space,
+ make sure we order writes to the bus so that chip functions
+ correctly. Some small cleanups.
+
+2006-03-15 21:48 davidm
+
+ * hifn/hifn7751var.h:
+
+ Remove unused field from structure
+
+2006-03-15 21:47 davidm
+
+ * safe/safe.c:
+
+ Make the code more 64bit OS compatible, force PCI address space
+ and so on.
+
+2006-03-09 20:42 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ If we call ixpCryptoAccInit() and it fails, just assume that it
+ has already been called. This allows our "rc" scripts to be
+ openswan and freeswan compatible ore easily.
+
+2006-02-28 23:21 davidm
+
+ * README:
+
+ generalise it a bit so rel-dates don't get in the way
+
+2006-02-28 01:52 davidm
+
+ * README, patches/ssh.patch, patches/ssl.patch:
+
+ Updated the README and patches for a release
+
+2006-02-25 09:21 davidm
+
+ * README, README.sglinux:
+
+ updates for a new release of OCF, ssl patches and so on.
+
+2006-02-25 08:44 davidm
+
+ * crypto.c:
+
+ We were calling the "process"routines with interrupts disabled. I
+ can see no good reason for this and it provokes badness warnings in
+ 2.6 under some conditions.
+
+ I am going to run with the Q's unlocked for processing, and
+ hopefully it will allow the system to be more responsive. It
+ hasn't affected ipsec throughput in any way. Userland throughput
+ (multi threaded) may have improved significantly though, but it
+ needs more testing.
+
+2006-02-24 23:32 davidm
+
+ * cryptodev.c:
+
+ Whoa, set the segments with uninitted values can't be good. Clean
+ out the rest of the old code that was accidently left in.
+
+2006-02-22 01:02 davidm
+
+ * cryptodev.c:
+
+ Still not sure about this one, but this is working for all the
+ cases I can see so far. If it gets weird, I am going to switch to
+ a simple clone and chain the sessions rather than a new fcr per fd.
+
+2006-02-20 22:12 davidm
+
+ * cryptodev.c:
+
+ Error handling case could free data that was not allocated
+
+ Ronen Shitrit Marvell Semiconductor Israel
+ Ltd
+
+2006-02-20 21:57 davidm
+
+ * cryptosoft.c:
+
+ Proper SHA/MD5 (non hmac) implementation, remove some retrictions
+ for hashes (which only applied to crypto) and fix over zealous
+ error checking.
+
+ Ronen Shitrit Marvell Semiconductor Israel
+ Ltd
+
+2006-02-09 21:15 davidm
+
+ * cryptodev.c:
+
+ The code wasn't quite right and needed some fixing for proper file
+ accounting.
+
+2006-01-24 20:08 davidm
+
+ * crypto.c, cryptodev.c, hifn/hifn7751.c:
+
+ We implement our own open in the CRIOGET ioctl now which does
+ pretty much the same thing as the BSD version, rather than use an
+ open system call which gets caught out by chroot.
+
+2006-01-06 00:42 gerg
+
+ * cryptosoft.c:
+
+ Moved "{" block marker inside the set of
+ "defined(CONFIG_CRYPTO_HMAC) || defined(CONFIG_CRYPTO_HMAC_MODULE)"
+ code (in function swcr_process) so that it compiled if these where
+ not defined.
+
+2005-11-11 01:44 davidm
+
+ * Makefile, README:
+
+ cleanups for a general release of OCF
+
+2005-11-11 01:18 davidm
+
+ * patches/ssl.patch:
+
+ Updated the ssl patch to the latest and greatest changed we have
+ made.
+
+2005-11-10 21:41 davidm
+
+ * Makefile, ixp4xx/ixp4xx.c:
+
+ Better debug for bad input.
+
+ Have make clean do more cleaning and less talking.
+
+2005-11-03 20:53 davidm
+
+ * cryptosoft.c:
+
+ clean up some compilation errors with various options on/off
+
+2005-10-25 00:25 davidm
+
+ * Makefile:
+
+ Add a patch target that generates full kernel patches to add OCF
+ into either a 2.4 or 2.6 kernel as a single patch.
+
+2005-10-25 00:24 davidm
+
+ * Kconfig:
+
+ Make sure all OCF settings depend on OCF_OCF
+
+2005-09-23 02:45 davidm
+
+ * README, README.sglinux:
+
+ new crypto-tools archive to keep the tools up to date
+
+2005-09-23 02:08 davidm
+
+ * Makefile, README, README.sglinux:
+
+ updates for doing OCF releases
+
+2005-09-23 01:59 davidm
+
+ * patches/: ssh.patch, ssl.patch:
+
+ updated the patches for the latest in fixes etc to ssh/ssl for a
+ new OCF release before the openswan 2.4.1 merge.
+
+2005-09-21 00:57 davidm
+
+ * Makefile, cryptosoft.c, hifn/hifn7751.c, ixp4xx/ixp4xx.c:
+
+ Fixes for building cleanly under 2.6
+
+2005-09-13 23:11 davidm
+
+ * ocf-bench.c:
+
+ Add an exit function for cleaner 2.6 support. Patch from Ronen
+ Shitrit
+
+2005-07-30 00:23 davidm
+
+ * cryptosoft.c:
+
+ Add proper hooks for MD5/SHA and their HMAC countrparts processing
+ from cryptodev.
+
+2005-07-29 01:50 davidm
+
+ * cryptodev.c:
+
+ cryptodev did not support MD5 and SHA1, only the HMAC versions
+
+2005-07-29 01:05 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ register for MD5, return 16 bytes for MD5 and 12 for MD5_HMAC,
+ likewise for SHA1 (only 20 is not HMAC).
+
+2005-07-28 21:52 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ First pass fix of userland MD5 processing. We now do as well as
+ the safenet does, I think.
+
+2005-07-27 06:23 davidm
+
+ * cryptodev.c:
+
+ udelay doesn't give up the current thread, thus the kernel will get
+ locked if a process is killed but the hardware never completes the
+ crypto request.
+
+2005-07-22 02:07 davidm
+
+ * crypto.c, cryptodev.h:
+
+ Implement queuing limits for input/output and OCF requests.
+
+ Implement 2.6 style work queues instead of the 2.4 task_queues.
+
+2005-07-21 20:42 davidm
+
+ * cryptodev.h, ocf-bench.c, ixp4xx/ixp4xx.c:
+
+ Fix OCF to use work queues, add 2.4 version of work queues to
+ cryptodev.h for use in ipsec and elsewhere.
+
+ Problem and initial patch provided by David Vrabel
+ . Cleaned up and 2.4 support added by me.
+
+2005-07-21 19:08 davidm
+
+ * random.c:
+
+ Fix spinlock initialisation, problem reported by Andy @
+ windsorcarclub.co.uk.
+
+2005-07-20 20:24 davidm
+
+ * cryptodev.c:
+
+ fix a silly spelling mistake
+
+2005-07-08 00:56 gerg
+
+ * Makefile:
+
+ Only build ocf-bench when CONFIG_OCF_BENCH is acrually enabled.
+
+2005-06-27 20:29 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ Ok, we need a simple implentation here or we go too slow for UDP
+ tests. For now, if the Q is full just ditch the packet, someone
+ has to do it.
+
+2005-06-25 01:13 davidm
+
+ * safe/safe.c:
+
+ bytes swapping etc all seems wrong for safenet on BE, these fixes
+ see both MD5 and SHA1 working with OpenSwan.
+
+2005-06-22 23:10 davidm
+
+ * random.c:
+
+ clean up some ifdef code a little
+
+2005-06-22 21:28 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ Make sure we do not call blocking functions from ISR callable
+ routines. In this case we were calling ixCryptoAccCtxUnregister.
+
+ Run all the random numbers through SHA1 process to ensure more
+ uniform distribution of bits (NOTE: it is not more random in any
+ way)
+
+2005-06-21 00:11 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ do not process requests from the register callback, gets the AES
+ code all messed up.
+
+ Align caches on HW cache boundaries ot improve speed.
+
+ More tracking or potential errors.
+
+2005-06-15 01:55 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ technically ixCryptoAccCtxRegister cannot be called from IRQ
+ context, so run it from the immediate BH.
+
+2005-06-14 23:13 davidm
+
+ * ocf-bench.c:
+
+ Fix some compile warnings/errors
+
+2005-06-14 20:52 davidm
+
+ * Config.in, Kconfig, Makefile, ocf-bench.c:
+
+ Add in kernel benchmark driver
+
+ Support for building under CSR 1.4 and 2.0 is now complete
+
+2005-06-14 20:51 davidm
+
+ * crypto.c:
+
+ Do not use immediate processing for SW drivers otherwise we hold a
+ lock for too long. Instead force BATCH processing.
+
+ Problem found by David Vrabel
+
+2005-06-14 20:46 davidm
+
+ * cryptodev.c:
+
+ Fix up AES minimum key size
+
+ Make some more variables static
+
+2005-06-14 20:36 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ Use kernel caches as they are more efficient and faster to obtain.
+
+ Fix some spelling
+
+ Tune PKE to only use the space required. Turn off go fast options
+ to reduce any speed-related cracking.
+
+ Only zero data that needs to be zeroed (save some cycles)
+
+2005-06-02 21:42 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ Added optimisations ideas from Intel the improve the PKE
+ performance for 512 and 1024 bits operations.
+
+2005-06-01 02:13 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ Looks like I found the ixp bug. Using OSAL buffer routines on
+ non-OSAL buffers is a very very bad thing to do. Must double check
+ all the API's I am using (ie., PKE) just to be sure.
+
+2005-05-31 21:38 davidm
+
+ * Config.in, Kconfig:
+
+ Updated/Added the menu wrapper for the config options
+
+2005-05-31 21:18 gerg
+
+ * Config.in:
+
+ Reworked the config.in so that each sub-module has its own
+ Config.in. That way it is easier to make release trees with some
+ modules left in.
+
+2005-05-30 19:46 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ Not all the Pke code was appropriately ifdef'd
+
+2005-05-28 01:49 davidm
+
+ * cryptosoft.c:
+
+ We were not injecting data in the right places nor calling the
+ cipher code the best was under all situations. We are now good
+ enought to do ESP/AH processing with 3DES and SHA1-HMAC.
+
+2005-05-28 01:42 davidm
+
+ * hifn/hifn7751.c:
+
+ Non atomic kmallocs at IRQ time are bad and cause lockups
+
+2005-05-21 08:31 davidm
+
+ * README, README.sglinux:
+
+ Some small updates to email and patches that no longer exist
+
+2005-05-21 08:25 davidm
+
+ * cryptosoft.c:
+
+ better error message on failure, a lot of the error check we do
+ sems to break apps like cryptotest, not sure who is right yet.
+
+2005-05-21 00:55 davidm
+
+ * criov.c, crypto.c, cryptodev.c, cryptodev.h, cryptosoft.c,
+ random.c, rndtest.c, uio.h, hifn/hifn7751.c, ixp4xx/ixp4xx.c,
+ safe/safe.c, safe/safevar.h:
+
+ Convert to CyberGuard email addresses for OCF files.
+
+2005-05-21 00:28 davidm
+
+ * crypto.c, cryptodev.c, cryptosoft.c, hifn/hifn7751.c,
+ ixp4xx/ixp4xx.c, safe/safe.c:
+
+ final round of naming changes, hifn driver also confirmed working
+ in SG710
+
+2005-05-20 23:50 davidm
+
+ * cryptosoft.c, random.c, uio.h, ixp4xx/ixp4xx.c:
+
+ more copyright/author updates etc for Intel
+
+2005-05-20 23:48 davidm
+
+ * criov.c, crypto.c, cryptodev.c, cryptodev.h, cryptosoft.c,
+ random.c, rndtest.c, uio.h, ixp4xx/ixp4xx.c, safe/safe.c,
+ safe/safevar.h:
+
+ updated copyrights to reflect Intels interest/investment in the
+ project
+
+2005-05-20 20:31 davidm
+
+ * hifn/: hifn7751.c, hifn7751var.h:
+
+ Most of a working SKB implementation now
+
+2005-05-20 20:30 davidm
+
+ * safe/: md5.c, md5.h, safe.c, sha1.c, sha1.h:
+
+ Full AH/ESP processing now working, just added BSD sha/md5
+ routines as needed for now.
+
+2005-05-20 20:30 davidm
+
+ * random.c:
+
+ use the new BSD rndtest code rather than the old GPL fips code
+
+2005-05-20 20:28 davidm
+
+ * Makefile, fips.c, fips.h, rndtest.c, rndtest.h:
+
+ Get rid of the GPL version of the fips test and use a BSD licensed
+ version so no one can get upset and cry derivative :-)
+
+2005-05-20 08:19 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ High throughput improvements. Can now handle multiple simultaneous
+ requests.
+
+2005-05-20 00:55 davidm
+
+ * ixp4xx/ixp4xx.c:
+
+ works with openswan/OCF for ipsec receive.
+
+ fixes ESP/AUTH processing (esp. HMAC)
+
+ This driver needs a rework for kernel IPSEC as it's current packet
+ at a time processing is not up to the task.
+
+2005-05-20 00:53 davidm
+
+ * cryptosoft.c:
+
+ working ESP/AUTH code now impleneted. Can do RX processing for
+ openswan KLIPS.
+
+ Fixes numerous problems in skb processing
+
+ Fixes broken HMAC code and IV processing
+
+2005-05-14 01:44 davidm
+
+ * cryptodev.c, ixp4xx/ixp4xx.c:
+
+ Cleanup IXP key processing to guarantee only a single outstanding
+ request rather than relying on the intel driver to get it right.
+ Stops us losing requests.
+
+ Tighten up the cryptodev response to "no answer" so that we don't
+ take all the CPU. Only happens if there is a driver bug.
+
+2005-05-14 00:07 davidm
+
+ * Config.in, Kconfig, Makefile, crypto.c, fips.c, fips.h, random.c,
+ ixp4xx/ixp4xx.c, tools/bench-ocf:
+
+ PKE and RNG support running on the ixp driver, added PKE bench
+ marking to script. Still some multi-thread problems in the PKE
+ code.
+
+ Added FIP RNG checking option to config and code.
+
+2005-05-10 19:18 davidm
+
+ * Makefile, ixp4xx/ixp4xx.c:
+
+ Get the OCF stuff building for the 465 and CSR-2.0
+
+2005-04-27 19:18 davidm
+
+ * cryptodev.h, random.c, hifn/hifn7751.c, safe/safe.c:
+ cleanup the random number interface some more
+
+2005-04-27 00:57 davidm
+
+ * cryptodev.h, cryptosoft.c, random.c, hifn/hifn7751.c,
+ ixp4xx/ixp4xx.c, patches/linux-2.4.29-add_true_randomness.patch,
+ patches/linux-2.6.11-add_true_randomness.patch, safe/safe.c,
+ safe/safevar.h:
+
+ Switch to a more "user" like random number handling. Drivers no
+ longer poll for RNG data, we pull it as needed to fill
+ /dev/random's entropy.
+
+ Implement sk_buff handling within the OCF framework.
+
+ fixup IV handling in cryptosoft.
+
+2005-04-27 00:41 davidm
+
+ * crypto.c:
+
+ Fix a race condition with the starting of kernel threads. The
+ threads were running before the pid assignment in the parent.
+
+2005-03-24 23:57 davidm
+
+ * Makefile:
+ include crypto-tools.patch in the release file
+
+
+
+2005-03-24 20:14 davidm
+
+ * safe/safe.c:
+ Fixup compile time warnings due to some left over BSDisms
+
+
+2005-03-24 00:53 davidm
+
+ * Makefile, README, README.sglinux, patches/linux-2.4.29-ocf.patch,
+ patches/linux-2.6.11-ocf.patch, patches/ocf-linux-2.4.29.patch,
+ patches/ocf-linux-2.6.11.patch:
+ added cleaner patch names and a tarball target to aid releases
+
+
+2005-03-24 00:28 davidm
+
+ * patches/crypto-tools.patch,
+ patches/linux-2.4.29-add_true_randomness.patch,
+ patches/linux-2.6.11-add_true_randomness.patch,
+ patches/ocf-linux-2.4.29.patch, patches/ocf-linux-2.6.11.patch,
+ patches/ssh.patch, patches/ssl.patch, tools/bench-ocf:
+ move all the release file patches into CVS for simplicity
+
+
+2005-03-23 20:37 davidm
+
+ * safe/safe.c:
+ remove excessive debug from RNG routines so that you can turn on
+ debug and live through it
+
+
+
+2005-03-23 02:23 davidm
+
+ * safe/safe.c:
+ fix memory corruption for mod_exp and the safenet, we were copying
+ back more than the space available.
+
+
+
+2005-03-22 21:45 davidm
+
+ * crypto.c, cryptodev.c, cryptodev.h, safe/safe.c:
+ fixup a major sync issues with key processing (callback called
+ before sleeping). Improve its performance while we are there with
+ a CBIMM (callback immediate) option.
+
+
+
+2005-03-19 00:33 davidm
+
+ * random.c:
+ A new randomness function for both 2.4 and 2.6 that replaces out
+ previous old solution for the hifn driver with more generic code
+ that works on both kernels.
+
+ add_true_randomness(u_int32_t *buf, int nwords);
+
+
+
+2005-03-18 21:01 davidm
+
+ * Makefile, cryptodev.h, random.c, hifn/hifn7751.c, safe/safe.c:
+ RNG support in both the safenet and the hifn plus the required
+ kernel support.
+
+ "hd /dev/random" runs much much faster now :-)
+
+
+
+2005-03-17 23:29 toby
+
+ * cryptodev.c: Make sure the CIOCASYMFEAT ioctl on
+ /dev/crypto copies out the capable features.
+
+2005-03-17 01:19 davidm
+
+ * safe/: safe.c, safereg.h, safevar.h:
+ hardware PK acceleration on the safenet (CRK_MOD_EXP only)
+
+
+2005-03-16 04:28 davidm
+
+ * criov.c, crypto.c, cryptodev.c, safe/safe.c:
+ fixup the FBSD id stuff to compile :-)
+
+2005-03-16 04:02 davidm
+
+ * README, README.sglinux, TODO:
+ Updated with versions, removed tabs, new kernel versions, web
+ site etc
+
+2005-03-16 03:45 davidm
+
+ * criov.c, crypto.c, cryptodev.c, cryptodev.h:
+ more license formatting and version id's to help keep in touch with
+ FreeBSD
+
+2005-03-16 03:16 davidm
+
+ * safe/safe.c:
+ up to date with FreeBSD versioning, no changes to merge
+
+2005-03-16 03:15 davidm
+
+ * safe/safe.c:
+ include FreeBSD version info so I can track changes better
+
+
+2005-03-16 03:11 davidm
+
+ * hifn/: hifn7751.c, hifn7751reg.h, hifn7751var.h:
+ incorporate latest updates from FreeBSD:
+
+ | Update support for 795x parts: | o rework pll setup code to
+ follow h/w specification | o add hint.hifn.X.pllconfig to specify
+ reference clock setup | requirements; default is pci66 which
+ means the clock is | derived from the PCI bus clock and the card
+ resides in a | 66MHz slot | | Tested on 7955 and 7956 cards;
+ support for 7954 cards not enabled | since we have no cards to test
+ against.
+
+
+
+2005-03-16 02:50 davidm
+
+ * Config.in, Kconfig, README, crypto.c, cryptodev.c, cryptodev.h,
+ hifn/hifn7751.c, hifn/hifn7751var.h, safe/safe.c, safe/safevar.h:
+ cleanups to build and run on 2.6.11 and 2.4.29 for a public release
+
+ included configs locally to reduce patch to kernel and required
+ user effort.
+
+ pci_dma_sync_single fix from Michele Baldessari
+ , with modifications by me to work on all
+ kernels.
+
+
+
+2005-03-11 23:30 davidm
+
+ * Makefile:
+ fix up the compiling again, I had outstanding commits for this one
+ :-)
+
+2005-03-11 21:56 danield
+
+ * Makefile: Get ocf modules building again.
+
+2004-12-25 07:12 davidm
+
+ * TODO:
+ safenet is all good now AKAIK
+
+2004-12-25 07:02 davidm
+
+ * Makefile, crypto.c, cryptodev.c, cryptosoft.c, uio.h:
+ 2.6 port of the OCF framework, tested on Xscale and compiled for
+ x86
+
+
+
+2004-12-10 02:41 davidm
+
+ * hifn/hifn7751.c:
+ Fix compilation as l_flags was not defined for LOCK in pci_remove
+
+
+2004-12-06 19:15 davidm
+
+ * hifn/hifn7751.c, safe/safe.c:
+ * Put locking into hifn_remove to ensure interrupts are not running
+ while we remove the driver
+
+ Use del_timer_sync (need to ensure timer is not running on
+ another CPU
+ * when we delete it).
+
+ Improvements suggested by Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+
+
+2004-12-02 09:16 davidm
+
+ * README, README.sglinux:
+ * Fix up all tabs to be spaces * explain how to update the ocf
+ support in the patch to the current version.
+
+
+
+2004-12-02 09:11 davidm
+
+ * README:
+ * fix some spelling/grammar * add more info on the ssl.patch file
+ and what it contains
+
+2004-12-02 09:08 davidm
+
+ * README.sglinux:
+ give some more instructions on the shar archive and extracting the
+ release.
+
+2004-12-02 09:03 davidm
+
+ * README.sglinux:
+ how to include crypto-tools into 3.1.6
+
+
+
+2004-12-02 08:48 davidm
+
+ * README.sglinux:
+ updated for a new crypto patch for openssl-0.9.7e
+
+2004-12-02 08:04 davidm
+
+ * README, README.sglinux:
+ Clean up the README's to use the new SG Linux alpha and a specific
+ 2.4.28 patch.
+
+
+
+2004-12-01 23:11 davidm
+
+ * Makefile, criov.c, crypto.c, cryptodev.c, cryptosoft.c,
+ hifn/Makefile, hifn/hifn7751.c, ixp4xx/Makefile, ixp4xx/ixp4xx.c,
+ safe/Makefile, safe/safe.c:
+ Re-worked the Makefiles so that including the code into a standard
+ non-SG kernel is really easy.
+
+ Fixed a non-initialised bug that was introduce into cryptosoft.c
+ with the additional error checking.
+
+
+
+2004-12-01 01:54 davidm
+
+ * TODO:
+ updates based on things that have been fixed.
+
+2004-12-01 01:53 davidm
+
+ * safe/: safe.c, safevar.h:
+ Cleanup some old hacks based on the much cleaner port of the hifn
+ driver.
+
+2004-12-01 01:53 davidm
+
+ * cryptosoft.c:
+ fix some crashes due to bad buffer sizes etc.
+
+2004-11-27 09:16 davidm
+
+ * crypto.c, cryptodev.c, ixp4xx/ixp4xx.c:
+ Fixed some nasty problems that were stopping reliable behaviour:
+
+ 1) we were not initialising some of our lists/wait queues, which
+ meant they appears to have things in them when in fact they did
+ not. This actually didn't seems to cause any problems but is
+ extremely bogus.
+
+ 2) While a process was waiting for the crypto engine, if
+ itreceived a signal we would lose sync with the engine and end
+ up allowing out-of-band actions that were invalid (ie.,
+ unregistering a context that was still active in a crypto
+ operation).
+
+ Of source the CryptACC should probably deal with this as well
+ ;-)
+
+
+
+2004-11-26 01:33 davidm
+
+ * TODO:
+ We should also hook in the random number generators to linux
+
+
+2004-11-26 01:33 davidm
+
+ * hifn/hifn7751.c:
+ Changed all the accesses to DMS descriptors to not set the valid
+ bit until after everything else was set. This got the driver
+ running smoothly, along with a fixup to the pci_map_uio which
+ wasn't settings lengths correctly.
+
+
+
+2004-11-25 21:15 davidm
+
+ * ixp4xx/ixp4xx.c:
+ document why using a new context for each packet is actually a
+ better idea than allocating one (actually two) per session.
+
+
+
+2004-11-25 08:48 davidm
+
+ * README, README.sglinux:
+ updates from email with Intel to fix a few little things
+
+
+2004-11-25 00:02 davidm
+
+ * README, README.sglinux, safe/safe.c:
+ Some cleanups of doc and so on for Intel/General use
+
+2004-11-23 07:58 davidm
+
+ * TODO:
+ safenet is working on big endian machines now
+
+
+
+2004-11-23 07:56 davidm
+
+ * ixp4xx/: Makefile, ixp4xx.c:
+ Reference the Intel library, cleanup the IV sizes and turn on AES.
+ Fix Makefile for new config options.
+
+
+
+2004-11-23 07:52 davidm
+
+ * Makefile:
+ Complete the changes of ixp to ixp4xx (IXP4xx) as appropriate
+
+
+2004-11-23 00:33 davidm
+
+ * Makefile, README, ixp4xx/Makefile:
+ Changes all references to ixp to be ixp4xx at Intels request
+
+
+2004-11-20 01:07 davidm
+
+ * safe/: safe.c, safereg.h:
+ fully working safenet on BE machines
+
+2004-11-19 01:03 davidm
+
+ * Makefile, hifn/Makefile, hifn/hifn7751.c, hifn/hifn7751reg.h,
+ hifn/hifn7751var.h:
+ hifn driver code complete and compiling, needs a test now ;-)
+
+
+2004-11-18 21:45 davidm
+
+ * hifn/: Makefile, hifn7751.c, hifn7751var.h:
+ Makefile for building the hifn driver
+
+2004-11-18 21:44 davidm
+
+ * hifn/: hifn7751.c, hifn7751reg.h, hifn7751var.h:
+ Checkin the orginal freebsd source for the hifn driver as a
+ reference in the future.
+
+
+
+2004-11-18 10:05 davidm
+
+ * Makefile, README, TODO, criov.c, crypto.c, cryptodev.c,
+ cryptodev.h, cryptosoft.c, uio.h, ixp4xx/Makefile, ixp4xx/ixp4xx.c,
+ safe/Makefile, safe/safe.c, safe/safevar.h:
+ Clean up license and copyright info to be more acceptable (and
+ present) Clean up debug and trace Fixup memory freeing etc on
+ safenet Fix compiler warnings (some were bugs)
+
+
+
+2004-11-17 02:23 davidm
+
+ * safe/safe.c:
+ working on Xscale (big endian) now but packet data is getting
+ stuffed up due to endian problems (at least now we are talking to
+ the chip correctly for BE). Good enough to test packet throughput,
+ no good for testing scp.
+
+
+
+2004-11-17 02:21 davidm
+
+ * criov.c:
+ Make sure public symbols are exported by including the correct
+ header files
+
+2004-11-17 02:15 davidm
+
+ * crypto.c:
+ even better cleanup of kernel threads on exit
+
+2004-11-17 02:15 davidm
+
+ * cryptosoft.c:
+ return some trace to debug so it doesn't appear
+
+2004-11-17 02:14 davidm
+
+ * Makefile:
+ Make sure all drivers get built, not just IXP
+
+2004-11-16 21:31 davidm
+
+ * crypto.c:
+ Fix problem with reboots and driver not unloading cleanly, we were
+ not handling signals correctly in the kernel threads,
+
+2004-11-10 10:46 davidm
+
+ * ixp4xx/ixp4xx.c:
+ fix serious context leak, itturns out the context is still
+ considered busy while it is calling the perform callback, so we
+ cleanup on closing the session and on allocating the next context
+ to work around this.
+
+
+
+2004-11-10 05:26 davidm
+
+ * crypto.c, cryptodev.c, ixp4xx/ixp4xx.c, safe/safe.c:
+ cleaned out some debug, found MAX tunnels bug, traced it various
+ other cleanups.
+
+
+
+2004-11-10 04:02 davidm
+
+ * cryptodev.c, cryptosoft.c, ixp4xx/ixp4xx.c, safe/Makefile,
+ safe/safe.c:
+ Fix up kmalloc usage to always zero resulting buffer everywhere
+ (stops crashes in ixp)
+
+ Add some function debug to ixp so you can see it working
+
+ Fix safe driver to build and install in a real tree.
+
+
+
+2004-11-10 02:27 davidm
+
+ * Makefile, criov.c, crypto.c, cryptosoft.c, ixp4xx/Makefile,
+ ixp4xx/ixp4xx.c:
+ Compiling OCF modules for the IXP crypto, needs testing now
+
+
+2004-11-09 19:16 davidm
+
+ * criov.c, crypto.c, cryptodev.c, cryptodev.h, cryptosoft.c,
+ cryptosoft.h, uio.h, safe/safe.c, safe/safevar.h:
+ The linux port of OCF with working safenet and software modules.
+ Still some bugs with mutiple crypto threads using the safenet
+ driver.
+
+
+
+2004-11-09 18:49 davidm
+
+ * Makefile, criov.c, crypto.c, cryptodev.c, cryptodev.h,
+ cryptosoft.c, cryptosoft.h, uio.h, safe/Makefile, safe/safe.c,
+ safe/safereg.h, safe/safevar.h:
+ Check in the orginal free-bsd sources for the OCF support. This
+ allows us to diff against it later to see if we botched anything
+ major league.
+
+
+
diff --git a/crypto/ocf/Config.in b/crypto/ocf/Config.in
new file mode 100644
index 000000000000..cb01ea9f7112
--- /dev/null
+++ b/crypto/ocf/Config.in
@@ -0,0 +1,34 @@
+#############################################################################
+
+mainmenu_option next_comment
+comment 'OCF Configuration'
+tristate 'OCF (Open Cryptograhic Framework)' CONFIG_OCF_OCF
+dep_mbool ' enable fips RNG checks (fips check on RNG data before use)' \
+ CONFIG_OCF_FIPS $CONFIG_OCF_OCF
+dep_mbool ' enable harvesting entropy for /dev/random' \
+ CONFIG_OCF_RANDOMHARVEST $CONFIG_OCF_OCF
+dep_tristate ' cryptodev (user space support)' \
+ CONFIG_OCF_CRYPTODEV $CONFIG_OCF_OCF
+dep_tristate ' cryptosoft (software crypto engine)' \
+ CONFIG_OCF_CRYPTOSOFT $CONFIG_OCF_OCF
+dep_tristate ' safenet (HW crypto engine)' \
+ CONFIG_OCF_SAFE $CONFIG_OCF_OCF
+dep_tristate ' IXP4xx (HW crypto engine)' \
+ CONFIG_OCF_IXP4XX $CONFIG_OCF_OCF
+dep_mbool ' Enable IXP4xx HW to perform SHA1 and MD5 hashing (very slow)' \
+ CONFIG_OCF_IXP4XX_SHA1_MD5 $CONFIG_OCF_IXP4XX
+dep_tristate ' hifn (HW crypto engine)' \
+ CONFIG_OCF_HIFN $CONFIG_OCF_OCF
+dep_tristate ' talitos (HW crypto engine)' \
+ CONFIG_OCF_TALITOS $CONFIG_OCF_OCF
+dep_tristate ' pasemi (HW crypto engine)' \
+ CONFIG_OCF_PASEMI $CONFIG_OCF_OCF
+dep_tristate ' ep80579 (HW crypto engine)' \
+ CONFIG_OCF_EP80579 $CONFIG_OCF_OCF
+dep_tristate ' ocfnull (does no crypto)' \
+ CONFIG_OCF_OCFNULL $CONFIG_OCF_OCF
+dep_tristate ' ocf-bench (HW crypto in-kernel benchmark)' \
+ CONFIG_OCF_BENCH $CONFIG_OCF_OCF
+endmenu
+
+#############################################################################
diff --git a/crypto/ocf/Kconfig b/crypto/ocf/Kconfig
new file mode 100644
index 000000000000..74b98b290190
--- /dev/null
+++ b/crypto/ocf/Kconfig
@@ -0,0 +1,101 @@
+menu "OCF Configuration"
+
+config OCF_OCF
+ tristate "OCF (Open Cryptograhic Framework)"
+ help
+ A linux port of the OpenBSD/FreeBSD crypto framework.
+
+config OCF_RANDOMHARVEST
+ bool "crypto random --- harvest entropy for /dev/random"
+ depends on OCF_OCF
+ help
+ Includes code to harvest random numbers from devices that support it.
+
+config OCF_FIPS
+ bool "enable fips RNG checks"
+ depends on OCF_OCF && OCF_RANDOMHARVEST
+ help
+ Run all RNG provided data through a fips check before
+ adding it /dev/random's entropy pool.
+
+config OCF_CRYPTODEV
+ tristate "cryptodev (user space support)"
+ depends on OCF_OCF
+ help
+ The user space API to access crypto hardware.
+
+config OCF_CRYPTOSOFT
+ tristate "cryptosoft (software crypto engine)"
+ depends on OCF_OCF
+ help
+ A software driver for the OCF framework that uses
+ the kernel CryptoAPI.
+
+config OCF_SAFE
+ tristate "safenet (HW crypto engine)"
+ depends on OCF_OCF
+ help
+ A driver for a number of the safenet Excel crypto accelerators.
+ Currently tested and working on the 1141 and 1741.
+
+config OCF_IXP4XX
+ tristate "IXP4xx (HW crypto engine)"
+ depends on OCF_OCF
+ help
+ XScale IXP4xx crypto accelerator driver. Requires the
+ Intel Access library.
+
+config OCF_IXP4XX_SHA1_MD5
+ bool "IXP4xx SHA1 and MD5 Hashing"
+ depends on OCF_IXP4XX
+ help
+ Allows the IXP4xx crypto accelerator to perform SHA1 and MD5 hashing.
+ Note: this is MUCH slower than using cryptosoft (software crypto engine).
+
+config OCF_HIFN
+ tristate "hifn (HW crypto engine)"
+ depends on OCF_OCF
+ help
+ OCF driver for various HIFN based crypto accelerators.
+ (7951, 7955, 7956, 7751, 7811)
+
+config OCF_HIFNHIPP
+ tristate "Hifn HIPP (HW packet crypto engine)"
+ depends on OCF_OCF
+ help
+ OCF driver for various HIFN (HIPP) based crypto accelerators
+ (7855)
+
+config OCF_TALITOS
+ tristate "talitos (HW crypto engine)"
+ depends on OCF_OCF
+ help
+ OCF driver for Freescale's security engine (SEC/talitos).
+
+config OCF_PASEMI
+ tristate "pasemi (HW crypto engine)"
+ depends on OCF_OCF && PPC_PASEMI
+ help
+ OCF driver for the PA Semi PWRficient DMA Engine
+
+config OCF_EP80579
+ tristate "ep80579 (HW crypto engine)"
+ depends on OCF_OCF
+ help
+ OCF driver for the Intel EP80579 Integrated Processor Product Line.
+
+config OCF_OCFNULL
+ tristate "ocfnull (fake crypto engine)"
+ depends on OCF_OCF
+ help
+ OCF driver for measuring ipsec overheads (does no crypto)
+
+config OCF_BENCH
+ tristate "ocf-bench (HW crypto in-kernel benchmark)"
+ depends on OCF_OCF
+ help
+ A very simple encryption test for the in-kernel interface
+ of OCF. Also includes code to benchmark the IXP Access library
+ for comparison.
+
+endmenu
diff --git a/crypto/ocf/Makefile b/crypto/ocf/Makefile
new file mode 100644
index 000000000000..c44f74f12205
--- /dev/null
+++ b/crypto/ocf/Makefile
@@ -0,0 +1,121 @@
+# for SGlinux builds
+-include $(ROOTDIR)/modules/.config
+
+OCF_OBJS = crypto.o criov.o
+
+ifdef CONFIG_OCF_RANDOMHARVEST
+ OCF_OBJS += random.o
+endif
+
+ifdef CONFIG_OCF_FIPS
+ OCF_OBJS += rndtest.o
+endif
+
+# Add in autoconf.h to get #defines for CONFIG_xxx
+AUTOCONF_H=$(ROOTDIR)/modules/autoconf.h
+ifeq ($(AUTOCONF_H), $(wildcard $(AUTOCONF_H)))
+ EXTRA_CFLAGS += -include $(AUTOCONF_H)
+ export EXTRA_CFLAGS
+endif
+
+ifndef obj
+ obj ?= .
+ _obj = subdir
+ mod-subdirs := safe hifn ixp4xx talitos ocfnull
+ export-objs += crypto.o criov.o random.o
+ list-multi += ocf.o
+ _slash :=
+else
+ _obj = obj
+ _slash := /
+endif
+
+EXTRA_CFLAGS += -I$(obj)/.
+
+obj-$(CONFIG_OCF_OCF) += ocf.o
+obj-$(CONFIG_OCF_CRYPTODEV) += cryptodev.o
+obj-$(CONFIG_OCF_CRYPTOSOFT) += cryptosoft.o
+obj-$(CONFIG_OCF_BENCH) += ocf-bench.o
+
+$(_obj)-$(CONFIG_OCF_SAFE) += safe$(_slash)
+$(_obj)-$(CONFIG_OCF_HIFN) += hifn$(_slash)
+$(_obj)-$(CONFIG_OCF_IXP4XX) += ixp4xx$(_slash)
+$(_obj)-$(CONFIG_OCF_TALITOS) += talitos$(_slash)
+$(_obj)-$(CONFIG_OCF_PASEMI) += pasemi$(_slash)
+# $(_obj)-$(CONFIG_OCF_EP80579) += ep80579$(_slash)
+$(_obj)-$(CONFIG_OCF_OCFNULL) += ocfnull$(_slash)
+
+ocf-objs := $(OCF_OBJS)
+
+$(list-multi) dummy1: $(ocf-objs)
+ $(LD) -r -o $@ $(ocf-objs)
+
+.PHONY:
+clean:
+ rm -f *.o *.ko .*.o.flags .*.ko.cmd .*.o.cmd .*.mod.o.cmd *.mod.c
+ rm -f */*.o */*.ko */.*.o.cmd */.*.ko.cmd */.*.mod.o.cmd */*.mod.c */.*.o.flags
+
+ifdef TOPDIR
+-include $(TOPDIR)/Rules.make
+endif
+
+#
+# release gen targets
+#
+
+.PHONY: patch
+patch:
+ REL=`date +%Y%m%d`; \
+ patch=ocf-linux-$$REL.patch; \
+ patch24=ocf-linux-24-$$REL.patch; \
+ patch26=ocf-linux-26-$$REL.patch; \
+ ( \
+ find . -name Makefile; \
+ find . -name Config.in; \
+ find . -name Kconfig; \
+ find . -name README; \
+ find . -name '*.[ch]' | grep -v '.mod.c'; \
+ ) | while read t; do \
+ diff -Nau /dev/null $$t | sed 's?^+++ \./?+++ linux/crypto/ocf/?'; \
+ done > $$patch; \
+ cat patches/linux-2.4.35-ocf.patch $$patch > $$patch24; \
+ cat patches/linux-2.6.26-ocf.patch $$patch > $$patch26
+
+.PHONY: tarball
+tarball:
+ REL=`date +%Y%m%d`; RELDIR=/tmp/ocf-linux-$$REL; \
+ CURDIR=`pwd`; \
+ rm -rf /tmp/ocf-linux-$$REL*; \
+ mkdir -p $$RELDIR/tools; \
+ cp README* $$RELDIR; \
+ cp patches/openss*.patch $$RELDIR; \
+ cp patches/crypto-tools.patch $$RELDIR; \
+ cp tools/[!C]* $$RELDIR/tools; \
+ cd ..; \
+ tar cvf $$RELDIR/ocf-linux.tar \
+ --exclude=CVS \
+ --exclude=.* \
+ --exclude=*.o \
+ --exclude=*.ko \
+ --exclude=*.mod.* \
+ --exclude=README* \
+ --exclude=ocf-*.patch \
+ --exclude=ocf/patches/openss*.patch \
+ --exclude=ocf/patches/crypto-tools.patch \
+ --exclude=ocf/tools \
+ ocf; \
+ gzip -9 $$RELDIR/ocf-linux.tar; \
+ cd /tmp; \
+ tar cvf ocf-linux-$$REL.tar ocf-linux-$$REL; \
+ gzip -9 ocf-linux-$$REL.tar; \
+ cd $$CURDIR/../../user; \
+ rm -rf /tmp/crypto-tools-$$REL*; \
+ tar cvf /tmp/crypto-tools-$$REL.tar \
+ --exclude=CVS \
+ --exclude=.* \
+ --exclude=*.o \
+ --exclude=cryptotest \
+ --exclude=cryptokeytest \
+ crypto-tools; \
+ gzip -9 /tmp/crypto-tools-$$REL.tar
+
diff --git a/crypto/ocf/README b/crypto/ocf/README
new file mode 100644
index 000000000000..f004fd46f70b
--- /dev/null
+++ b/crypto/ocf/README
@@ -0,0 +1,167 @@
+README - ocf-linux-20071215
+---------------------------
+
+This README provides instructions for getting ocf-linux compiled and
+operating in a generic linux environment. For other information you
+might like to visit the home page for this project:
+
+ http://ocf-linux.sourceforge.net/
+
+Adding OCF to linux
+-------------------
+
+ Not much in this file for now, just some notes. I usually build
+ the ocf support as modules but it can be built into the kernel as
+ well. To use it:
+
+ * mknod /dev/crypto c 10 70
+
+ * to add OCF to your kernel source, you have two options. Apply
+ the kernel specific patch:
+
+ cd linux-2.4*; gunzip < ocf-linux-24-XXXXXXXX.patch.gz | patch -p1
+ cd linux-2.6*; gunzip < ocf-linux-26-XXXXXXXX.patch.gz | patch -p1
+
+ if you do one of the above, then you can proceed to the next step,
+ or you can do the above process by hand with using the patches against
+ linux-2.4.35 and 2.6.23 to include the ocf code under crypto/ocf.
+ Here's how to add it:
+
+ for 2.4.35 (and later)
+
+ cd linux-2.4.35/crypto
+ tar xvzf ocf-linux.tar.gz
+ cd ..
+ patch -p1 < crypto/ocf/patches/linux-2.4.35-ocf.patch
+
+ for 2.6.23 (and later), find the kernel patch specific (or nearest)
+ to your kernel versions and then:
+
+ cd linux-2.6.NN/crypto
+ tar xvzf ocf-linux.tar.gz
+ cd ..
+ patch -p1 < crypto/ocf/patches/linux-2.6.NN-ocf.patch
+
+ It should be easy to take this patch and apply it to other more
+ recent versions of the kernels. The same patches should also work
+ relatively easily on kernels as old as 2.6.11 and 2.4.18.
+
+ * under 2.4 if you are on a non-x86 platform, you may need to:
+
+ cp linux-2.X.x/include/asm-i386/kmap_types.h linux-2.X.x/include/asm-YYY
+
+ so that you can build the kernel crypto support needed for the cryptosoft
+ driver.
+
+ * For simplicity you should enable all the crypto support in your kernel
+ except for the test driver. Likewise for the OCF options. Do not
+ enable OCF crypto drivers for HW that you do not have (for example
+ ixp4xx will not compile on non-Xscale systems).
+
+ * make sure that cryptodev.h (from ocf-linux.tar.gz) is installed as
+ crypto/cryptodev.h in an include directory that is used for building
+ applications for your platform. For example on a host system that
+ might be:
+
+ /usr/include/crypto/cryptodev.h
+
+ * patch your openssl-0.9.8i code with the openssl-0.9.8i.patch.
+ (NOTE: there is no longer a need to patch ssh). The patch is against:
+ openssl-0_9_8e
+
+ If you need a patch for an older version of openssl, you should look
+ to older OCF releases. This patch is unlikely to work on older
+ openssl versions.
+
+ openssl-0.9.8i.patch
+ - enables --with-cryptodev for non BSD systems
+ - adds -cpu option to openssl speed for calculating CPU load
+ under linux
+ - fixes null pointer in openssl speed multi thread output.
+ - fixes test keys to work with linux crypto's more stringent
+ key checking.
+ - adds MD5/SHA acceleration (Ronen Shitrit), only enabled
+ with the --with-cryptodev-digests option
+ - fixes bug in engine code caching.
+
+ * build crypto-tools-XXXXXXXX.tar.gz if you want to try some of the BSD
+ tools for testing OCF (ie., cryptotest).
+
+How to load the OCF drivers
+---------------------------
+
+ First insert the base modules:
+
+ insmod ocf
+ insmod cryptodev
+
+ You can then install the software OCF driver with:
+
+ insmod cryptosoft
+
+ and one or more of the OCF HW drivers with:
+
+ insmod safe
+ insmod hifn7751
+ insmod ixp4xx
+ ...
+
+ all the drivers take a debug option to enable verbose debug so that
+ you can see what is going on. For debug you load them as:
+
+ insmod ocf crypto_debug=1
+ insmod cryptodev cryptodev_debug=1
+ insmod cryptosoft swcr_debug=1
+
+ You may load more than one OCF crypto driver but then there is no guarantee
+ as to which will be used.
+
+ You can also enable debug at run time on 2.6 systems with the following:
+
+ echo 1 > /sys/module/ocf/parameters/crypto_debug
+ echo 1 > /sys/module/cryptodev/parameters/cryptodev_debug
+ echo 1 > /sys/module/cryptosoft/parameters/swcr_debug
+ echo 1 > /sys/module/hifn7751/parameters/hifn_debug
+ echo 1 > /sys/module/safe/parameters/safe_debug
+ echo 1 > /sys/module/ixp4xx/parameters/ixp_debug
+ ...
+
+Testing the OCF support
+-----------------------
+
+ run "cryptotest", it should do a short test for a couple of
+ des packets. If it does everything is working.
+
+ If this works, then ssh will use the driver when invoked as:
+
+ ssh -c 3des username@host
+
+ to see for sure that it is operating, enable debug as defined above.
+
+ To get a better idea of performance run:
+
+ cryptotest 100 4096
+
+ There are more options to cryptotest, see the help.
+
+ It is also possible to use openssl to test the speed of the crypto
+ drivers.
+
+ openssl speed -evp des -engine cryptodev -elapsed
+ openssl speed -evp des3 -engine cryptodev -elapsed
+ openssl speed -evp aes128 -engine cryptodev -elapsed
+
+ and multiple threads (10) with:
+
+ openssl speed -evp des -engine cryptodev -elapsed -multi 10
+ openssl speed -evp des3 -engine cryptodev -elapsed -multi 10
+ openssl speed -evp aes128 -engine cryptodev -elapsed -multi 10
+
+ for public key testing you can try:
+
+ cryptokeytest
+ openssl speed -engine cryptodev rsa -elapsed
+ openssl speed -engine cryptodev dsa -elapsed
+
+David McCullough
+david_mccullough@securecomputing.com
diff --git a/crypto/ocf/criov.c b/crypto/ocf/criov.c
new file mode 100644
index 000000000000..3bf7c32ff2d1
--- /dev/null
+++ b/crypto/ocf/criov.c
@@ -0,0 +1,215 @@
+/* $OpenBSD: criov.c,v 1.9 2002/01/29 15:48:29 jason Exp $ */
+
+/*
+ * Linux port done by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2006-2007 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * Copyright (c) 1999 Theo de Raadt
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+__FBSDID("$FreeBSD: src/sys/opencrypto/criov.c,v 1.5 2006/06/04 22:15:13 pjd Exp $");
+ */
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/uio.h>
+#include <linux/skbuff.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+
+#include <uio.h>
+#include <cryptodev.h>
+
+/*
+ * This macro is only for avoiding code duplication, as we need to skip
+ * given number of bytes in the same way in three functions below.
+ */
+#define CUIO_SKIP() do { \
+ KASSERT(off >= 0, ("%s: off %d < 0", __func__, off)); \
+ KASSERT(len >= 0, ("%s: len %d < 0", __func__, len)); \
+ while (off > 0) { \
+ KASSERT(iol >= 0, ("%s: empty in skip", __func__)); \
+ if (off < iov->iov_len) \
+ break; \
+ off -= iov->iov_len; \
+ iol--; \
+ iov++; \
+ } \
+} while (0)
+
+void
+cuio_copydata(struct uio* uio, int off, int len, caddr_t cp)
+{
+ struct iovec *iov = uio->uio_iov;
+ int iol = uio->uio_iovcnt;
+ unsigned count;
+
+ CUIO_SKIP();
+ while (len > 0) {
+ KASSERT(iol >= 0, ("%s: empty", __func__));
+ count = min((int)(iov->iov_len - off), len);
+ memcpy(cp, ((caddr_t)iov->iov_base) + off, count);
+ len -= count;
+ cp += count;
+ off = 0;
+ iol--;
+ iov++;
+ }
+}
+
+void
+cuio_copyback(struct uio* uio, int off, int len, caddr_t cp)
+{
+ struct iovec *iov = uio->uio_iov;
+ int iol = uio->uio_iovcnt;
+ unsigned count;
+
+ CUIO_SKIP();
+ while (len > 0) {
+ KASSERT(iol >= 0, ("%s: empty", __func__));
+ count = min((int)(iov->iov_len - off), len);
+ memcpy(((caddr_t)iov->iov_base) + off, cp, count);
+ len -= count;
+ cp += count;
+ off = 0;
+ iol--;
+ iov++;
+ }
+}
+
+/*
+ * Return a pointer to iov/offset of location in iovec list.
+ */
+struct iovec *
+cuio_getptr(struct uio *uio, int loc, int *off)
+{
+ struct iovec *iov = uio->uio_iov;
+ int iol = uio->uio_iovcnt;
+
+ while (loc >= 0) {
+ /* Normal end of search */
+ if (loc < iov->iov_len) {
+ *off = loc;
+ return (iov);
+ }
+
+ loc -= iov->iov_len;
+ if (iol == 0) {
+ if (loc == 0) {
+ /* Point at the end of valid data */
+ *off = iov->iov_len;
+ return (iov);
+ } else
+ return (NULL);
+ } else {
+ iov++, iol--;
+ }
+ }
+
+ return (NULL);
+}
+
+EXPORT_SYMBOL(cuio_copyback);
+EXPORT_SYMBOL(cuio_copydata);
+EXPORT_SYMBOL(cuio_getptr);
+
+
+static void
+skb_copy_bits_back(struct sk_buff *skb, int offset, caddr_t cp, int len)
+{
+ int i;
+ if (offset < skb_headlen(skb)) {
+ memcpy(skb->data + offset, cp, min_t(int, skb_headlen(skb), len));
+ len -= skb_headlen(skb);
+ cp += skb_headlen(skb);
+ }
+ offset -= skb_headlen(skb);
+ for (i = 0; len > 0 && i < skb_shinfo(skb)->nr_frags; i++) {
+ if (offset < skb_shinfo(skb)->frags[i].size) {
+ memcpy(page_address(skb_shinfo(skb)->frags[i].page) +
+ skb_shinfo(skb)->frags[i].page_offset,
+ cp, min_t(int, skb_shinfo(skb)->frags[i].size, len));
+ len -= skb_shinfo(skb)->frags[i].size;
+ cp += skb_shinfo(skb)->frags[i].size;
+ }
+ offset -= skb_shinfo(skb)->frags[i].size;
+ }
+}
+
+void
+crypto_copyback(int flags, caddr_t buf, int off, int size, caddr_t in)
+{
+
+ if ((flags & CRYPTO_F_SKBUF) != 0)
+ skb_copy_bits_back((struct sk_buff *)buf, off, in, size);
+ else if ((flags & CRYPTO_F_IOV) != 0)
+ cuio_copyback((struct uio *)buf, off, size, in);
+ else
+ bcopy(in, buf + off, size);
+}
+
+void
+crypto_copydata(int flags, caddr_t buf, int off, int size, caddr_t out)
+{
+
+ if ((flags & CRYPTO_F_SKBUF) != 0)
+ skb_copy_bits((struct sk_buff *)buf, off, out, size);
+ else if ((flags & CRYPTO_F_IOV) != 0)
+ cuio_copydata((struct uio *)buf, off, size, out);
+ else
+ bcopy(buf + off, out, size);
+}
+
+int
+crypto_apply(int flags, caddr_t buf, int off, int len,
+ int (*f)(void *, void *, u_int), void *arg)
+{
+#if 0
+ int error;
+
+ if ((flags & CRYPTO_F_SKBUF) != 0)
+ error = XXXXXX((struct mbuf *)buf, off, len, f, arg);
+ else if ((flags & CRYPTO_F_IOV) != 0)
+ error = cuio_apply((struct uio *)buf, off, len, f, arg);
+ else
+ error = (*f)(arg, buf + off, len);
+ return (error);
+#else
+ KASSERT(0, ("crypto_apply not implemented!\n"));
+#endif
+ return 0;
+}
+
+EXPORT_SYMBOL(crypto_copyback);
+EXPORT_SYMBOL(crypto_copydata);
+EXPORT_SYMBOL(crypto_apply);
+
diff --git a/crypto/ocf/crypto.c b/crypto/ocf/crypto.c
new file mode 100644
index 000000000000..cffa7ed812fc
--- /dev/null
+++ b/crypto/ocf/crypto.c
@@ -0,0 +1,1748 @@
+/*-
+ * Linux port done by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2006-2007 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * Copyright (c) 2002-2006 Sam Leffler. All rights reserved.
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if 0
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/opencrypto/crypto.c,v 1.27 2007/03/21 03:42:51 sam Exp $");
+#endif
+
+/*
+ * Cryptographic Subsystem.
+ *
+ * This code is derived from the Openbsd Cryptographic Framework (OCF)
+ * that has the copyright shown below. Very little of the original
+ * code remains.
+ */
+/*-
+ * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
+ *
+ * This code was written by Angelos D. Keromytis in Athens, Greece, in
+ * February 2000. Network Security Technologies Inc. (NSTI) kindly
+ * supported the development of this code.
+ *
+ * Copyright (c) 2000, 2001 Angelos D. Keromytis
+ *
+ * Permission to use, copy, and modify this software with or without fee
+ * is hereby granted, provided that this entire notice is included in
+ * all source code copies of any software which is or includes a copy or
+ * modification of this software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
+ * PURPOSE.
+ *
+__FBSDID("$FreeBSD: src/sys/opencrypto/crypto.c,v 1.16 2005/01/07 02:29:16 imp Exp $");
+ */
+
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/version.h>
+#include <linux/kthread.h>
+#include <cryptodev.h>
+
+/*
+ * keep track of whether or not we have been initialised, a big
+ * issue if we are linked into the kernel and a driver gets started before
+ * us
+ */
+static int crypto_initted = 0;
+
+/*
+ * Crypto drivers register themselves by allocating a slot in the
+ * crypto_drivers table with crypto_get_driverid() and then registering
+ * each algorithm they support with crypto_register() and crypto_kregister().
+ */
+
+/*
+ * lock on driver table
+ * we track its state as spin_is_locked does not do anything on non-SMP boxes
+ */
+static spinlock_t crypto_drivers_lock;
+static int crypto_drivers_locked; /* for non-SMP boxes */
+
+#define CRYPTO_DRIVER_LOCK() \
+ ({ \
+ spin_lock_irqsave(&crypto_drivers_lock, d_flags); \
+ crypto_drivers_locked = 1; \
+ dprintk("%s,%d: DRIVER_LOCK()\n", __FILE__, __LINE__); \
+ })
+#define CRYPTO_DRIVER_UNLOCK() \
+ ({ \
+ dprintk("%s,%d: DRIVER_UNLOCK()\n", __FILE__, __LINE__); \
+ crypto_drivers_locked = 0; \
+ spin_unlock_irqrestore(&crypto_drivers_lock, d_flags); \
+ })
+#define CRYPTO_DRIVER_ASSERT() \
+ ({ \
+ if (!crypto_drivers_locked) { \
+ dprintk("%s,%d: DRIVER_ASSERT!\n", __FILE__, __LINE__); \
+ } \
+ })
+
+/*
+ * Crypto device/driver capabilities structure.
+ *
+ * Synchronization:
+ * (d) - protected by CRYPTO_DRIVER_LOCK()
+ * (q) - protected by CRYPTO_Q_LOCK()
+ * Not tagged fields are read-only.
+ */
+struct cryptocap {
+ device_t cc_dev; /* (d) device/driver */
+ u_int32_t cc_sessions; /* (d) # of sessions */
+ u_int32_t cc_koperations; /* (d) # os asym operations */
+ /*
+ * Largest possible operator length (in bits) for each type of
+ * encryption algorithm. XXX not used
+ */
+ u_int16_t cc_max_op_len[CRYPTO_ALGORITHM_MAX + 1];
+ u_int8_t cc_alg[CRYPTO_ALGORITHM_MAX + 1];
+ u_int8_t cc_kalg[CRK_ALGORITHM_MAX + 1];
+
+ int cc_flags; /* (d) flags */
+#define CRYPTOCAP_F_CLEANUP 0x80000000 /* needs resource cleanup */
+ int cc_qblocked; /* (q) symmetric q blocked */
+ int cc_kqblocked; /* (q) asymmetric q blocked */
+};
+static struct cryptocap *crypto_drivers = NULL;
+static int crypto_drivers_num = 0;
+
+/*
+ * There are two queues for crypto requests; one for symmetric (e.g.
+ * cipher) operations and one for asymmetric (e.g. MOD)operations.
+ * A single mutex is used to lock access to both queues. We could
+ * have one per-queue but having one simplifies handling of block/unblock
+ * operations.
+ */
+static int crp_sleep = 0;
+static LIST_HEAD(crp_q); /* request queues */
+static LIST_HEAD(crp_kq);
+
+static spinlock_t crypto_q_lock;
+
+int crypto_all_qblocked = 0; /* protect with Q_LOCK */
+module_param(crypto_all_qblocked, int, 0444);
+MODULE_PARM_DESC(crypto_all_qblocked, "Are all crypto queues blocked");
+
+int crypto_all_kqblocked = 0; /* protect with Q_LOCK */
+module_param(crypto_all_kqblocked, int, 0444);
+MODULE_PARM_DESC(crypto_all_kqblocked, "Are all asym crypto queues blocked");
+
+#define CRYPTO_Q_LOCK() \
+ ({ \
+ spin_lock_irqsave(&crypto_q_lock, q_flags); \
+ dprintk("%s,%d: Q_LOCK()\n", __FILE__, __LINE__); \
+ })
+#define CRYPTO_Q_UNLOCK() \
+ ({ \
+ dprintk("%s,%d: Q_UNLOCK()\n", __FILE__, __LINE__); \
+ spin_unlock_irqrestore(&crypto_q_lock, q_flags); \
+ })
+
+/*
+ * There are two queues for processing completed crypto requests; one
+ * for the symmetric and one for the asymmetric ops. We only need one
+ * but have two to avoid type futzing (cryptop vs. cryptkop). A single
+ * mutex is used to lock access to both queues. Note that this lock
+ * must be separate from the lock on request queues to insure driver
+ * callbacks don't generate lock order reversals.
+ */
+static LIST_HEAD(crp_ret_q); /* callback queues */
+static LIST_HEAD(crp_ret_kq);
+
+static spinlock_t crypto_ret_q_lock;
+#define CRYPTO_RETQ_LOCK() \
+ ({ \
+ spin_lock_irqsave(&crypto_ret_q_lock, r_flags); \
+ dprintk("%s,%d: RETQ_LOCK\n", __FILE__, __LINE__); \
+ })
+#define CRYPTO_RETQ_UNLOCK() \
+ ({ \
+ dprintk("%s,%d: RETQ_UNLOCK\n", __FILE__, __LINE__); \
+ spin_unlock_irqrestore(&crypto_ret_q_lock, r_flags); \
+ })
+#define CRYPTO_RETQ_EMPTY() (list_empty(&crp_ret_q) && list_empty(&crp_ret_kq))
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static kmem_cache_t *cryptop_zone;
+static kmem_cache_t *cryptodesc_zone;
+#else
+static struct kmem_cache *cryptop_zone;
+static struct kmem_cache *cryptodesc_zone;
+#endif
+
+#define debug crypto_debug
+int crypto_debug = 0;
+module_param(crypto_debug, int, 0644);
+MODULE_PARM_DESC(crypto_debug, "Enable debug");
+EXPORT_SYMBOL(crypto_debug);
+
+/*
+ * Maximum number of outstanding crypto requests before we start
+ * failing requests. We need this to prevent DOS when too many
+ * requests are arriving for us to keep up. Otherwise we will
+ * run the system out of memory. Since crypto is slow, we are
+ * usually the bottleneck that needs to say, enough is enough.
+ *
+ * We cannot print errors when this condition occurs, we are already too
+ * slow, printing anything will just kill us
+ */
+
+static int crypto_q_cnt = 0;
+module_param(crypto_q_cnt, int, 0444);
+MODULE_PARM_DESC(crypto_q_cnt,
+ "Current number of outstanding crypto requests");
+
+static int crypto_q_max = 1000;
+module_param(crypto_q_max, int, 0644);
+MODULE_PARM_DESC(crypto_q_max,
+ "Maximum number of outstanding crypto requests");
+
+#define bootverbose crypto_verbose
+static int crypto_verbose = 0;
+module_param(crypto_verbose, int, 0644);
+MODULE_PARM_DESC(crypto_verbose,
+ "Enable verbose crypto startup");
+
+int crypto_usercrypto = 1; /* userland may do crypto reqs */
+module_param(crypto_usercrypto, int, 0644);
+MODULE_PARM_DESC(crypto_usercrypto,
+ "Enable/disable user-mode access to crypto support");
+
+int crypto_userasymcrypto = 1; /* userland may do asym crypto reqs */
+module_param(crypto_userasymcrypto, int, 0644);
+MODULE_PARM_DESC(crypto_userasymcrypto,
+ "Enable/disable user-mode access to asymmetric crypto support");
+
+int crypto_devallowsoft = 0; /* only use hardware crypto */
+module_param(crypto_devallowsoft, int, 0644);
+MODULE_PARM_DESC(crypto_devallowsoft,
+ "Enable/disable use of software crypto support");
+
+static struct task_struct *cryptothread = NULL;
+static struct completion cryptoproc_exited;
+static DECLARE_WAIT_QUEUE_HEAD(cryptoproc_wait);
+static struct task_struct *cryptoretthread = NULL;
+static struct completion cryptoretproc_exited;
+static DECLARE_WAIT_QUEUE_HEAD(cryptoretproc_wait);
+
+static int crypto_proc(void *arg);
+static int crypto_ret_proc(void *arg);
+static int crypto_invoke(struct cryptocap *cap, struct cryptop *crp, int hint);
+static int crypto_kinvoke(struct cryptkop *krp, int flags);
+static void crypto_exit(void);
+static int crypto_init(void);
+
+static struct cryptostats cryptostats;
+
+static struct cryptocap *
+crypto_checkdriver(u_int32_t hid)
+{
+ if (crypto_drivers == NULL)
+ return NULL;
+ return (hid >= crypto_drivers_num ? NULL : &crypto_drivers[hid]);
+}
+
+/*
+ * Compare a driver's list of supported algorithms against another
+ * list; return non-zero if all algorithms are supported.
+ */
+static int
+driver_suitable(const struct cryptocap *cap, const struct cryptoini *cri)
+{
+ const struct cryptoini *cr;
+
+ /* See if all the algorithms are supported. */
+ for (cr = cri; cr; cr = cr->cri_next)
+ if (cap->cc_alg[cr->cri_alg] == 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * Select a driver for a new session that supports the specified
+ * algorithms and, optionally, is constrained according to the flags.
+ * The algorithm we use here is pretty stupid; just use the
+ * first driver that supports all the algorithms we need. If there
+ * are multiple drivers we choose the driver with the fewest active
+ * sessions. We prefer hardware-backed drivers to software ones.
+ *
+ * XXX We need more smarts here (in real life too, but that's
+ * XXX another story altogether).
+ */
+static struct cryptocap *
+crypto_select_driver(const struct cryptoini *cri, int flags)
+{
+ struct cryptocap *cap, *best;
+ int match, hid;
+
+ CRYPTO_DRIVER_ASSERT();
+
+ /*
+ * Look first for hardware crypto devices if permitted.
+ */
+ if (flags & CRYPTOCAP_F_HARDWARE)
+ match = CRYPTOCAP_F_HARDWARE;
+ else
+ match = CRYPTOCAP_F_SOFTWARE;
+ best = NULL;
+again:
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
+ cap = &crypto_drivers[hid];
+ /*
+ * If it's not initialized, is in the process of
+ * going away, or is not appropriate (hardware
+ * or software based on match), then skip.
+ */
+ if (cap->cc_dev == NULL ||
+ (cap->cc_flags & CRYPTOCAP_F_CLEANUP) ||
+ (cap->cc_flags & match) == 0)
+ continue;
+
+ /* verify all the algorithms are supported. */
+ if (driver_suitable(cap, cri)) {
+ if (best == NULL ||
+ cap->cc_sessions < best->cc_sessions)
+ best = cap;
+ }
+ }
+ if (best != NULL)
+ return best;
+ if (match == CRYPTOCAP_F_HARDWARE && (flags & CRYPTOCAP_F_SOFTWARE)) {
+ /* sort of an Algol 68-style for loop */
+ match = CRYPTOCAP_F_SOFTWARE;
+ goto again;
+ }
+ return best;
+}
+
+/*
+ * Create a new session. The crid argument specifies a crypto
+ * driver to use or constraints on a driver to select (hardware
+ * only, software only, either). Whatever driver is selected
+ * must be capable of the requested crypto algorithms.
+ */
+int
+crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int crid)
+{
+ struct cryptocap *cap;
+ u_int32_t hid, lid;
+ int err;
+ unsigned long d_flags;
+
+ CRYPTO_DRIVER_LOCK();
+ if ((crid & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+ /*
+ * Use specified driver; verify it is capable.
+ */
+ cap = crypto_checkdriver(crid);
+ if (cap != NULL && !driver_suitable(cap, cri))
+ cap = NULL;
+ } else {
+ /*
+ * No requested driver; select based on crid flags.
+ */
+ cap = crypto_select_driver(cri, crid);
+ /*
+ * if NULL then can't do everything in one session.
+ * XXX Fix this. We need to inject a "virtual" session
+ * XXX layer right about here.
+ */
+ }
+ if (cap != NULL) {
+ /* Call the driver initialization routine. */
+ hid = cap - crypto_drivers;
+ lid = hid; /* Pass the driver ID. */
+ cap->cc_sessions++;
+ CRYPTO_DRIVER_UNLOCK();
+ err = CRYPTODEV_NEWSESSION(cap->cc_dev, &lid, cri);
+ CRYPTO_DRIVER_LOCK();
+ if (err == 0) {
+ (*sid) = (cap->cc_flags & 0xff000000)
+ | (hid & 0x00ffffff);
+ (*sid) <<= 32;
+ (*sid) |= (lid & 0xffffffff);
+ } else
+ cap->cc_sessions--;
+ } else
+ err = EINVAL;
+ CRYPTO_DRIVER_UNLOCK();
+ return err;
+}
+
+static void
+crypto_remove(struct cryptocap *cap)
+{
+ CRYPTO_DRIVER_ASSERT();
+ if (cap->cc_sessions == 0 && cap->cc_koperations == 0)
+ bzero(cap, sizeof(*cap));
+}
+
+/*
+ * Delete an existing session (or a reserved session on an unregistered
+ * driver).
+ */
+int
+crypto_freesession(u_int64_t sid)
+{
+ struct cryptocap *cap;
+ u_int32_t hid;
+ int err = 0;
+ unsigned long d_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+ CRYPTO_DRIVER_LOCK();
+
+ if (crypto_drivers == NULL) {
+ err = EINVAL;
+ goto done;
+ }
+
+ /* Determine two IDs. */
+ hid = CRYPTO_SESID2HID(sid);
+
+ if (hid >= crypto_drivers_num) {
+ dprintk("%s - INVALID DRIVER NUM %d\n", __FUNCTION__, hid);
+ err = ENOENT;
+ goto done;
+ }
+ cap = &crypto_drivers[hid];
+
+ if (cap->cc_dev) {
+ CRYPTO_DRIVER_UNLOCK();
+ /* Call the driver cleanup routine, if available, unlocked. */
+ err = CRYPTODEV_FREESESSION(cap->cc_dev, sid);
+ CRYPTO_DRIVER_LOCK();
+ }
+
+ if (cap->cc_sessions)
+ cap->cc_sessions--;
+
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP)
+ crypto_remove(cap);
+
+done:
+ CRYPTO_DRIVER_UNLOCK();
+ return err;
+}
+
+/*
+ * Return an unused driver id. Used by drivers prior to registering
+ * support for the algorithms they handle.
+ */
+int32_t
+crypto_get_driverid(device_t dev, int flags)
+{
+ struct cryptocap *newdrv;
+ int i;
+ unsigned long d_flags;
+
+ if ((flags & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+ printf("%s: no flags specified when registering driver\n",
+ device_get_nameunit(dev));
+ return -1;
+ }
+
+ CRYPTO_DRIVER_LOCK();
+
+ for (i = 0; i < crypto_drivers_num; i++) {
+ if (crypto_drivers[i].cc_dev == NULL &&
+ (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) == 0) {
+ break;
+ }
+ }
+
+ /* Out of entries, allocate some more. */
+ if (i == crypto_drivers_num) {
+ /* Be careful about wrap-around. */
+ if (2 * crypto_drivers_num <= crypto_drivers_num) {
+ CRYPTO_DRIVER_UNLOCK();
+ printk("crypto: driver count wraparound!\n");
+ return -1;
+ }
+
+ newdrv = kmalloc(2 * crypto_drivers_num * sizeof(struct cryptocap),
+ GFP_KERNEL);
+ if (newdrv == NULL) {
+ CRYPTO_DRIVER_UNLOCK();
+ printk("crypto: no space to expand driver table!\n");
+ return -1;
+ }
+
+ memcpy(newdrv, crypto_drivers,
+ crypto_drivers_num * sizeof(struct cryptocap));
+ memset(&newdrv[crypto_drivers_num], 0,
+ crypto_drivers_num * sizeof(struct cryptocap));
+
+ crypto_drivers_num *= 2;
+
+ kfree(crypto_drivers);
+ crypto_drivers = newdrv;
+ }
+
+ /* NB: state is zero'd on free */
+ crypto_drivers[i].cc_sessions = 1; /* Mark */
+ crypto_drivers[i].cc_dev = dev;
+ crypto_drivers[i].cc_flags = flags;
+ if (bootverbose)
+ printf("crypto: assign %s driver id %u, flags %u\n",
+ device_get_nameunit(dev), i, flags);
+
+ CRYPTO_DRIVER_UNLOCK();
+
+ return i;
+}
+
+/*
+ * Lookup a driver by name. We match against the full device
+ * name and unit, and against just the name. The latter gives
+ * us a simple widlcarding by device name. On success return the
+ * driver/hardware identifier; otherwise return -1.
+ */
+int
+crypto_find_driver(const char *match)
+{
+ int i, len = strlen(match);
+ unsigned long d_flags;
+
+ CRYPTO_DRIVER_LOCK();
+ for (i = 0; i < crypto_drivers_num; i++) {
+ device_t dev = crypto_drivers[i].cc_dev;
+ if (dev == NULL ||
+ (crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP))
+ continue;
+ if (strncmp(match, device_get_nameunit(dev), len) == 0 ||
+ strncmp(match, device_get_name(dev), len) == 0)
+ break;
+ }
+ CRYPTO_DRIVER_UNLOCK();
+ return i < crypto_drivers_num ? i : -1;
+}
+
+/*
+ * Return the device_t for the specified driver or NULL
+ * if the driver identifier is invalid.
+ */
+device_t
+crypto_find_device_byhid(int hid)
+{
+ struct cryptocap *cap = crypto_checkdriver(hid);
+ return cap != NULL ? cap->cc_dev : NULL;
+}
+
+/*
+ * Return the device/driver capabilities.
+ */
+int
+crypto_getcaps(int hid)
+{
+ struct cryptocap *cap = crypto_checkdriver(hid);
+ return cap != NULL ? cap->cc_flags : 0;
+}
+
+/*
+ * Register support for a key-related algorithm. This routine
+ * is called once for each algorithm supported a driver.
+ */
+int
+crypto_kregister(u_int32_t driverid, int kalg, u_int32_t flags)
+{
+ struct cryptocap *cap;
+ int err;
+ unsigned long d_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+ CRYPTO_DRIVER_LOCK();
+
+ cap = crypto_checkdriver(driverid);
+ if (cap != NULL &&
+ (CRK_ALGORITM_MIN <= kalg && kalg <= CRK_ALGORITHM_MAX)) {
+ /*
+ * XXX Do some performance testing to determine placing.
+ * XXX We probably need an auxiliary data structure that
+ * XXX describes relative performances.
+ */
+
+ cap->cc_kalg[kalg] = flags | CRYPTO_ALG_FLAG_SUPPORTED;
+ if (bootverbose)
+ printf("crypto: %s registers key alg %u flags %u\n"
+ , device_get_nameunit(cap->cc_dev)
+ , kalg
+ , flags
+ );
+ err = 0;
+ } else
+ err = EINVAL;
+
+ CRYPTO_DRIVER_UNLOCK();
+ return err;
+}
+
+/*
+ * Register support for a non-key-related algorithm. This routine
+ * is called once for each such algorithm supported by a driver.
+ */
+int
+crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
+ u_int32_t flags)
+{
+ struct cryptocap *cap;
+ int err;
+ unsigned long d_flags;
+
+ dprintk("%s(id=0x%x, alg=%d, maxoplen=%d, flags=0x%x)\n", __FUNCTION__,
+ driverid, alg, maxoplen, flags);
+
+ CRYPTO_DRIVER_LOCK();
+
+ cap = crypto_checkdriver(driverid);
+ /* NB: algorithms are in the range [1..max] */
+ if (cap != NULL &&
+ (CRYPTO_ALGORITHM_MIN <= alg && alg <= CRYPTO_ALGORITHM_MAX)) {
+ /*
+ * XXX Do some performance testing to determine placing.
+ * XXX We probably need an auxiliary data structure that
+ * XXX describes relative performances.
+ */
+
+ cap->cc_alg[alg] = flags | CRYPTO_ALG_FLAG_SUPPORTED;
+ cap->cc_max_op_len[alg] = maxoplen;
+ if (bootverbose)
+ printf("crypto: %s registers alg %u flags %u maxoplen %u\n"
+ , device_get_nameunit(cap->cc_dev)
+ , alg
+ , flags
+ , maxoplen
+ );
+ cap->cc_sessions = 0; /* Unmark */
+ err = 0;
+ } else
+ err = EINVAL;
+
+ CRYPTO_DRIVER_UNLOCK();
+ return err;
+}
+
+static void
+driver_finis(struct cryptocap *cap)
+{
+ u_int32_t ses, kops;
+
+ CRYPTO_DRIVER_ASSERT();
+
+ ses = cap->cc_sessions;
+ kops = cap->cc_koperations;
+ bzero(cap, sizeof(*cap));
+ if (ses != 0 || kops != 0) {
+ /*
+ * If there are pending sessions,
+ * just mark as invalid.
+ */
+ cap->cc_flags |= CRYPTOCAP_F_CLEANUP;
+ cap->cc_sessions = ses;
+ cap->cc_koperations = kops;
+ }
+}
+
+/*
+ * Unregister a crypto driver. If there are pending sessions using it,
+ * leave enough information around so that subsequent calls using those
+ * sessions will correctly detect the driver has been unregistered and
+ * reroute requests.
+ */
+int
+crypto_unregister(u_int32_t driverid, int alg)
+{
+ struct cryptocap *cap;
+ int i, err;
+ unsigned long d_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+ CRYPTO_DRIVER_LOCK();
+
+ cap = crypto_checkdriver(driverid);
+ if (cap != NULL &&
+ (CRYPTO_ALGORITHM_MIN <= alg && alg <= CRYPTO_ALGORITHM_MAX) &&
+ cap->cc_alg[alg] != 0) {
+ cap->cc_alg[alg] = 0;
+ cap->cc_max_op_len[alg] = 0;
+
+ /* Was this the last algorithm ? */
+ for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
+ if (cap->cc_alg[i] != 0)
+ break;
+
+ if (i == CRYPTO_ALGORITHM_MAX + 1)
+ driver_finis(cap);
+ err = 0;
+ } else
+ err = EINVAL;
+ CRYPTO_DRIVER_UNLOCK();
+ return err;
+}
+
+/*
+ * Unregister all algorithms associated with a crypto driver.
+ * If there are pending sessions using it, leave enough information
+ * around so that subsequent calls using those sessions will
+ * correctly detect the driver has been unregistered and reroute
+ * requests.
+ */
+int
+crypto_unregister_all(u_int32_t driverid)
+{
+ struct cryptocap *cap;
+ int err;
+ unsigned long d_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+ CRYPTO_DRIVER_LOCK();
+ cap = crypto_checkdriver(driverid);
+ if (cap != NULL) {
+ driver_finis(cap);
+ err = 0;
+ } else
+ err = EINVAL;
+ CRYPTO_DRIVER_UNLOCK();
+
+ return err;
+}
+
+/*
+ * Clear blockage on a driver. The what parameter indicates whether
+ * the driver is now ready for cryptop's and/or cryptokop's.
+ */
+int
+crypto_unblock(u_int32_t driverid, int what)
+{
+ struct cryptocap *cap;
+ int err;
+ unsigned long q_flags;
+
+ CRYPTO_Q_LOCK();
+ cap = crypto_checkdriver(driverid);
+ if (cap != NULL) {
+ if (what & CRYPTO_SYMQ) {
+ cap->cc_qblocked = 0;
+ crypto_all_qblocked = 0;
+ }
+ if (what & CRYPTO_ASYMQ) {
+ cap->cc_kqblocked = 0;
+ crypto_all_kqblocked = 0;
+ }
+ if (crp_sleep)
+ wake_up_interruptible(&cryptoproc_wait);
+ err = 0;
+ } else
+ err = EINVAL;
+ CRYPTO_Q_UNLOCK(); //DAVIDM should this be a driver lock
+
+ return err;
+}
+
+/*
+ * Add a crypto request to a queue, to be processed by the kernel thread.
+ */
+int
+crypto_dispatch(struct cryptop *crp)
+{
+ struct cryptocap *cap;
+ int result = -1;
+ unsigned long q_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+
+ cryptostats.cs_ops++;
+
+ CRYPTO_Q_LOCK();
+ if (crypto_q_cnt >= crypto_q_max) {
+ CRYPTO_Q_UNLOCK();
+ cryptostats.cs_drops++;
+ return ENOMEM;
+ }
+ crypto_q_cnt++;
+
+ /*
+ * Caller marked the request to be processed immediately; dispatch
+ * it directly to the driver unless the driver is currently blocked.
+ */
+ if ((crp->crp_flags & CRYPTO_F_BATCH) == 0) {
+ int hid = CRYPTO_SESID2HID(crp->crp_sid);
+ cap = crypto_checkdriver(hid);
+ /* Driver cannot disappear when there is an active session. */
+ KASSERT(cap != NULL, ("%s: Driver disappeared.", __func__));
+ if (!cap->cc_qblocked) {
+ crypto_all_qblocked = 0;
+ crypto_drivers[hid].cc_qblocked = 1;
+ CRYPTO_Q_UNLOCK();
+ result = crypto_invoke(cap, crp, 0);
+ CRYPTO_Q_LOCK();
+ if (result != ERESTART)
+ crypto_drivers[hid].cc_qblocked = 0;
+ }
+ }
+ if (result == ERESTART) {
+ /*
+ * The driver ran out of resources, mark the
+ * driver ``blocked'' for cryptop's and put
+ * the request back in the queue. It would
+ * best to put the request back where we got
+ * it but that's hard so for now we put it
+ * at the front. This should be ok; putting
+ * it at the end does not work.
+ */
+ list_add(&crp->crp_next, &crp_q);
+ cryptostats.cs_blocks++;
+ } else if (result == -1) {
+ TAILQ_INSERT_TAIL(&crp_q, crp, crp_next);
+ }
+ if (crp_sleep)
+ wake_up_interruptible(&cryptoproc_wait);
+ CRYPTO_Q_UNLOCK();
+ return 0;
+}
+
+/*
+ * Add an asymetric crypto request to a queue,
+ * to be processed by the kernel thread.
+ */
+int
+crypto_kdispatch(struct cryptkop *krp)
+{
+ int error;
+ unsigned long q_flags;
+
+ cryptostats.cs_kops++;
+
+ error = crypto_kinvoke(krp, krp->krp_crid);
+ if (error == ERESTART) {
+ CRYPTO_Q_LOCK();
+ TAILQ_INSERT_TAIL(&crp_kq, krp, krp_next);
+ if (crp_sleep)
+ wake_up_interruptible(&cryptoproc_wait);
+ CRYPTO_Q_UNLOCK();
+ error = 0;
+ }
+ return error;
+}
+
+/*
+ * Verify a driver is suitable for the specified operation.
+ */
+static __inline int
+kdriver_suitable(const struct cryptocap *cap, const struct cryptkop *krp)
+{
+ return (cap->cc_kalg[krp->krp_op] & CRYPTO_ALG_FLAG_SUPPORTED) != 0;
+}
+
+/*
+ * Select a driver for an asym operation. The driver must
+ * support the necessary algorithm. The caller can constrain
+ * which device is selected with the flags parameter. The
+ * algorithm we use here is pretty stupid; just use the first
+ * driver that supports the algorithms we need. If there are
+ * multiple suitable drivers we choose the driver with the
+ * fewest active operations. We prefer hardware-backed
+ * drivers to software ones when either may be used.
+ */
+static struct cryptocap *
+crypto_select_kdriver(const struct cryptkop *krp, int flags)
+{
+ struct cryptocap *cap, *best, *blocked;
+ int match, hid;
+
+ CRYPTO_DRIVER_ASSERT();
+
+ /*
+ * Look first for hardware crypto devices if permitted.
+ */
+ if (flags & CRYPTOCAP_F_HARDWARE)
+ match = CRYPTOCAP_F_HARDWARE;
+ else
+ match = CRYPTOCAP_F_SOFTWARE;
+ best = NULL;
+ blocked = NULL;
+again:
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
+ cap = &crypto_drivers[hid];
+ /*
+ * If it's not initialized, is in the process of
+ * going away, or is not appropriate (hardware
+ * or software based on match), then skip.
+ */
+ if (cap->cc_dev == NULL ||
+ (cap->cc_flags & CRYPTOCAP_F_CLEANUP) ||
+ (cap->cc_flags & match) == 0)
+ continue;
+
+ /* verify all the algorithms are supported. */
+ if (kdriver_suitable(cap, krp)) {
+ if (best == NULL ||
+ cap->cc_koperations < best->cc_koperations)
+ best = cap;
+ }
+ }
+ if (best != NULL)
+ return best;
+ if (match == CRYPTOCAP_F_HARDWARE && (flags & CRYPTOCAP_F_SOFTWARE)) {
+ /* sort of an Algol 68-style for loop */
+ match = CRYPTOCAP_F_SOFTWARE;
+ goto again;
+ }
+ return best;
+}
+
+/*
+ * Dispatch an assymetric crypto request.
+ */
+static int
+crypto_kinvoke(struct cryptkop *krp, int crid)
+{
+ struct cryptocap *cap = NULL;
+ int error;
+ unsigned long d_flags;
+
+ KASSERT(krp != NULL, ("%s: krp == NULL", __func__));
+ KASSERT(krp->krp_callback != NULL,
+ ("%s: krp->crp_callback == NULL", __func__));
+
+ CRYPTO_DRIVER_LOCK();
+ if ((crid & (CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE)) == 0) {
+ cap = crypto_checkdriver(crid);
+ if (cap != NULL) {
+ /*
+ * Driver present, it must support the necessary
+ * algorithm and, if s/w drivers are excluded,
+ * it must be registered as hardware-backed.
+ */
+ if (!kdriver_suitable(cap, krp) ||
+ (!crypto_devallowsoft &&
+ (cap->cc_flags & CRYPTOCAP_F_HARDWARE) == 0))
+ cap = NULL;
+ }
+ } else {
+ /*
+ * No requested driver; select based on crid flags.
+ */
+ if (!crypto_devallowsoft) /* NB: disallow s/w drivers */
+ crid &= ~CRYPTOCAP_F_SOFTWARE;
+ cap = crypto_select_kdriver(krp, crid);
+ }
+ if (cap != NULL && !cap->cc_kqblocked) {
+ krp->krp_hid = cap - crypto_drivers;
+ cap->cc_koperations++;
+ CRYPTO_DRIVER_UNLOCK();
+ error = CRYPTODEV_KPROCESS(cap->cc_dev, krp, 0);
+ CRYPTO_DRIVER_LOCK();
+ if (error == ERESTART) {
+ cap->cc_koperations--;
+ CRYPTO_DRIVER_UNLOCK();
+ return (error);
+ }
+ /* return the actual device used */
+ krp->krp_crid = krp->krp_hid;
+ } else {
+ /*
+ * NB: cap is !NULL if device is blocked; in
+ * that case return ERESTART so the operation
+ * is resubmitted if possible.
+ */
+ error = (cap == NULL) ? ENODEV : ERESTART;
+ }
+ CRYPTO_DRIVER_UNLOCK();
+
+ if (error) {
+ krp->krp_status = error;
+ crypto_kdone(krp);
+ }
+ return 0;
+}
+
+
+/*
+ * Dispatch a crypto request to the appropriate crypto devices.
+ */
+static int
+crypto_invoke(struct cryptocap *cap, struct cryptop *crp, int hint)
+{
+ KASSERT(crp != NULL, ("%s: crp == NULL", __func__));
+ KASSERT(crp->crp_callback != NULL,
+ ("%s: crp->crp_callback == NULL", __func__));
+ KASSERT(crp->crp_desc != NULL, ("%s: crp->crp_desc == NULL", __func__));
+
+ dprintk("%s()\n", __FUNCTION__);
+
+#ifdef CRYPTO_TIMING
+ if (crypto_timing)
+ crypto_tstat(&cryptostats.cs_invoke, &crp->crp_tstamp);
+#endif
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP) {
+ struct cryptodesc *crd;
+ u_int64_t nid;
+
+ /*
+ * Driver has unregistered; migrate the session and return
+ * an error to the caller so they'll resubmit the op.
+ *
+ * XXX: What if there are more already queued requests for this
+ * session?
+ */
+ crypto_freesession(crp->crp_sid);
+
+ for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
+ crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
+
+ /* XXX propagate flags from initial session? */
+ if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI),
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE) == 0)
+ crp->crp_sid = nid;
+
+ crp->crp_etype = EAGAIN;
+ crypto_done(crp);
+ return 0;
+ } else {
+ /*
+ * Invoke the driver to process the request.
+ */
+ return CRYPTODEV_PROCESS(cap->cc_dev, crp, hint);
+ }
+}
+
+/*
+ * Release a set of crypto descriptors.
+ */
+void
+crypto_freereq(struct cryptop *crp)
+{
+ struct cryptodesc *crd;
+
+ if (crp == NULL)
+ return;
+
+#ifdef DIAGNOSTIC
+ {
+ struct cryptop *crp2;
+ unsigned long q_flags;
+
+ CRYPTO_Q_LOCK();
+ TAILQ_FOREACH(crp2, &crp_q, crp_next) {
+ KASSERT(crp2 != crp,
+ ("Freeing cryptop from the crypto queue (%p).",
+ crp));
+ }
+ CRYPTO_Q_UNLOCK();
+ CRYPTO_RETQ_LOCK();
+ TAILQ_FOREACH(crp2, &crp_ret_q, crp_next) {
+ KASSERT(crp2 != crp,
+ ("Freeing cryptop from the return queue (%p).",
+ crp));
+ }
+ CRYPTO_RETQ_UNLOCK();
+ }
+#endif
+
+ while ((crd = crp->crp_desc) != NULL) {
+ crp->crp_desc = crd->crd_next;
+ kmem_cache_free(cryptodesc_zone, crd);
+ }
+ kmem_cache_free(cryptop_zone, crp);
+}
+
+/*
+ * Acquire a set of crypto descriptors.
+ */
+struct cryptop *
+crypto_getreq(int num)
+{
+ struct cryptodesc *crd;
+ struct cryptop *crp;
+
+ crp = kmem_cache_alloc(cryptop_zone, SLAB_ATOMIC);
+ if (crp != NULL) {
+ memset(crp, 0, sizeof(*crp));
+ INIT_LIST_HEAD(&crp->crp_next);
+ init_waitqueue_head(&crp->crp_waitq);
+ while (num--) {
+ crd = kmem_cache_alloc(cryptodesc_zone, SLAB_ATOMIC);
+ if (crd == NULL) {
+ crypto_freereq(crp);
+ return NULL;
+ }
+ memset(crd, 0, sizeof(*crd));
+ crd->crd_next = crp->crp_desc;
+ crp->crp_desc = crd;
+ }
+ }
+ return crp;
+}
+
+/*
+ * Invoke the callback on behalf of the driver.
+ */
+void
+crypto_done(struct cryptop *crp)
+{
+ unsigned long q_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if ((crp->crp_flags & CRYPTO_F_DONE) == 0) {
+ crp->crp_flags |= CRYPTO_F_DONE;
+ CRYPTO_Q_LOCK();
+ crypto_q_cnt--;
+ CRYPTO_Q_UNLOCK();
+ } else
+ printk("crypto: crypto_done op already done, flags 0x%x",
+ crp->crp_flags);
+ if (crp->crp_etype != 0)
+ cryptostats.cs_errs++;
+ /*
+ * CBIMM means unconditionally do the callback immediately;
+ * CBIFSYNC means do the callback immediately only if the
+ * operation was done synchronously. Both are used to avoid
+ * doing extraneous context switches; the latter is mostly
+ * used with the software crypto driver.
+ */
+ if ((crp->crp_flags & CRYPTO_F_CBIMM) ||
+ ((crp->crp_flags & CRYPTO_F_CBIFSYNC) &&
+ (CRYPTO_SESID2CAPS(crp->crp_sid) & CRYPTOCAP_F_SYNC))) {
+ /*
+ * Do the callback directly. This is ok when the
+ * callback routine does very little (e.g. the
+ * /dev/crypto callback method just does a wakeup).
+ */
+ crp->crp_callback(crp);
+ } else {
+ unsigned long r_flags;
+ /*
+ * Normal case; queue the callback for the thread.
+ */
+ CRYPTO_RETQ_LOCK();
+ if (CRYPTO_RETQ_EMPTY())
+ wake_up_interruptible(&cryptoretproc_wait);/* shared wait channel */
+ TAILQ_INSERT_TAIL(&crp_ret_q, crp, crp_next);
+ CRYPTO_RETQ_UNLOCK();
+ }
+}
+
+/*
+ * Invoke the callback on behalf of the driver.
+ */
+void
+crypto_kdone(struct cryptkop *krp)
+{
+ struct cryptocap *cap;
+ unsigned long d_flags;
+
+ if ((krp->krp_flags & CRYPTO_KF_DONE) != 0)
+ printk("crypto: crypto_kdone op already done, flags 0x%x",
+ krp->krp_flags);
+ krp->krp_flags |= CRYPTO_KF_DONE;
+ if (krp->krp_status != 0)
+ cryptostats.cs_kerrs++;
+
+ CRYPTO_DRIVER_LOCK();
+ /* XXX: What if driver is loaded in the meantime? */
+ if (krp->krp_hid < crypto_drivers_num) {
+ cap = &crypto_drivers[krp->krp_hid];
+ cap->cc_koperations--;
+ KASSERT(cap->cc_koperations >= 0, ("cc_koperations < 0"));
+ if (cap->cc_flags & CRYPTOCAP_F_CLEANUP)
+ crypto_remove(cap);
+ }
+ CRYPTO_DRIVER_UNLOCK();
+
+ /*
+ * CBIMM means unconditionally do the callback immediately;
+ * This is used to avoid doing extraneous context switches
+ */
+ if ((krp->krp_flags & CRYPTO_KF_CBIMM)) {
+ /*
+ * Do the callback directly. This is ok when the
+ * callback routine does very little (e.g. the
+ * /dev/crypto callback method just does a wakeup).
+ */
+ krp->krp_callback(krp);
+ } else {
+ unsigned long r_flags;
+ /*
+ * Normal case; queue the callback for the thread.
+ */
+ CRYPTO_RETQ_LOCK();
+ if (CRYPTO_RETQ_EMPTY())
+ wake_up_interruptible(&cryptoretproc_wait);/* shared wait channel */
+ TAILQ_INSERT_TAIL(&crp_ret_kq, krp, krp_next);
+ CRYPTO_RETQ_UNLOCK();
+ }
+}
+
+int
+crypto_getfeat(int *featp)
+{
+ int hid, kalg, feat = 0;
+ unsigned long d_flags;
+
+ CRYPTO_DRIVER_LOCK();
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
+ const struct cryptocap *cap = &crypto_drivers[hid];
+
+ if ((cap->cc_flags & CRYPTOCAP_F_SOFTWARE) &&
+ !crypto_devallowsoft) {
+ continue;
+ }
+ for (kalg = 0; kalg < CRK_ALGORITHM_MAX; kalg++)
+ if (cap->cc_kalg[kalg] & CRYPTO_ALG_FLAG_SUPPORTED)
+ feat |= 1 << kalg;
+ }
+ CRYPTO_DRIVER_UNLOCK();
+ *featp = feat;
+ return (0);
+}
+
+/*
+ * Crypto thread, dispatches crypto requests.
+ */
+static int
+crypto_proc(void *arg)
+{
+ struct cryptop *crp, *submit;
+ struct cryptkop *krp, *krpp;
+ struct cryptocap *cap;
+ u_int32_t hid;
+ int result, hint;
+ unsigned long q_flags;
+
+ ocf_daemonize("crypto");
+
+ CRYPTO_Q_LOCK();
+ for (;;) {
+ /*
+ * we need to make sure we don't get into a busy loop with nothing
+ * to do, the two crypto_all_*blocked vars help us find out when
+ * we are all full and can do nothing on any driver or Q. If so we
+ * wait for an unblock.
+ */
+ crypto_all_qblocked = !list_empty(&crp_q);
+
+ /*
+ * Find the first element in the queue that can be
+ * processed and look-ahead to see if multiple ops
+ * are ready for the same driver.
+ */
+ submit = NULL;
+ hint = 0;
+ list_for_each_entry(crp, &crp_q, crp_next) {
+ hid = CRYPTO_SESID2HID(crp->crp_sid);
+ cap = crypto_checkdriver(hid);
+ /*
+ * Driver cannot disappear when there is an active
+ * session.
+ */
+ KASSERT(cap != NULL, ("%s:%u Driver disappeared.",
+ __func__, __LINE__));
+ if (cap == NULL || cap->cc_dev == NULL) {
+ /* Op needs to be migrated, process it. */
+ if (submit == NULL)
+ submit = crp;
+ break;
+ }
+ if (!cap->cc_qblocked) {
+ if (submit != NULL) {
+ /*
+ * We stop on finding another op,
+ * regardless whether its for the same
+ * driver or not. We could keep
+ * searching the queue but it might be
+ * better to just use a per-driver
+ * queue instead.
+ */
+ if (CRYPTO_SESID2HID(submit->crp_sid) == hid)
+ hint = CRYPTO_HINT_MORE;
+ break;
+ } else {
+ submit = crp;
+ if ((submit->crp_flags & CRYPTO_F_BATCH) == 0)
+ break;
+ /* keep scanning for more are q'd */
+ }
+ }
+ }
+ if (submit != NULL) {
+ hid = CRYPTO_SESID2HID(submit->crp_sid);
+ crypto_all_qblocked = 0;
+ list_del(&submit->crp_next);
+ crypto_drivers[hid].cc_qblocked = 1;
+ cap = crypto_checkdriver(hid);
+ CRYPTO_Q_UNLOCK();
+ KASSERT(cap != NULL, ("%s:%u Driver disappeared.",
+ __func__, __LINE__));
+ result = crypto_invoke(cap, submit, hint);
+ CRYPTO_Q_LOCK();
+ if (result == ERESTART) {
+ /*
+ * The driver ran out of resources, mark the
+ * driver ``blocked'' for cryptop's and put
+ * the request back in the queue. It would
+ * best to put the request back where we got
+ * it but that's hard so for now we put it
+ * at the front. This should be ok; putting
+ * it at the end does not work.
+ */
+ /* XXX validate sid again? */
+ list_add(&submit->crp_next, &crp_q);
+ cryptostats.cs_blocks++;
+ } else
+ crypto_drivers[hid].cc_qblocked=0;
+ }
+
+ crypto_all_kqblocked = !list_empty(&crp_kq);
+
+ /* As above, but for key ops */
+ krp = NULL;
+ list_for_each_entry(krpp, &crp_kq, krp_next) {
+ cap = crypto_checkdriver(krpp->krp_hid);
+ if (cap == NULL || cap->cc_dev == NULL) {
+ /*
+ * Operation needs to be migrated, invalidate
+ * the assigned device so it will reselect a
+ * new one below. Propagate the original
+ * crid selection flags if supplied.
+ */
+ krp->krp_hid = krp->krp_crid &
+ (CRYPTOCAP_F_SOFTWARE|CRYPTOCAP_F_HARDWARE);
+ if (krp->krp_hid == 0)
+ krp->krp_hid =
+ CRYPTOCAP_F_SOFTWARE|CRYPTOCAP_F_HARDWARE;
+ break;
+ }
+ if (!cap->cc_kqblocked) {
+ krp = krpp;
+ break;
+ }
+ }
+ if (krp != NULL) {
+ crypto_all_kqblocked = 0;
+ list_del(&krp->krp_next);
+ crypto_drivers[krp->krp_hid].cc_kqblocked = 1;
+ CRYPTO_Q_UNLOCK();
+ result = crypto_kinvoke(krp, krp->krp_hid);
+ CRYPTO_Q_LOCK();
+ if (result == ERESTART) {
+ /*
+ * The driver ran out of resources, mark the
+ * driver ``blocked'' for cryptkop's and put
+ * the request back in the queue. It would
+ * best to put the request back where we got
+ * it but that's hard so for now we put it
+ * at the front. This should be ok; putting
+ * it at the end does not work.
+ */
+ /* XXX validate sid again? */
+ list_add(&krp->krp_next, &crp_kq);
+ cryptostats.cs_kblocks++;
+ } else
+ crypto_drivers[krp->krp_hid].cc_kqblocked = 0;
+ }
+
+ if (submit == NULL && krp == NULL) {
+ /*
+ * Nothing more to be processed. Sleep until we're
+ * woken because there are more ops to process.
+ * This happens either by submission or by a driver
+ * becoming unblocked and notifying us through
+ * crypto_unblock. Note that when we wakeup we
+ * start processing each queue again from the
+ * front. It's not clear that it's important to
+ * preserve this ordering since ops may finish
+ * out of order if dispatched to different devices
+ * and some become blocked while others do not.
+ */
+ dprintk("%s - sleeping (qe=%d qb=%d kqe=%d kqb=%d)\n",
+ __FUNCTION__,
+ list_empty(&crp_q), crypto_all_qblocked,
+ list_empty(&crp_kq), crypto_all_kqblocked);
+ CRYPTO_Q_UNLOCK();
+ crp_sleep = 1;
+ wait_event_interruptible(cryptoproc_wait,
+ !(list_empty(&crp_q) || crypto_all_qblocked) ||
+ !(list_empty(&crp_kq) || crypto_all_kqblocked) ||
+ cryptothread == NULL);
+ crp_sleep = 0;
+ if (signal_pending (current)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_lock_irq(&current->sigmask_lock);
+#endif
+ flush_signals(current);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_unlock_irq(&current->sigmask_lock);
+#endif
+ }
+ CRYPTO_Q_LOCK();
+ dprintk("%s - awake\n", __FUNCTION__);
+ if (cryptothread == NULL)
+ break;
+ cryptostats.cs_intrs++;
+ }
+ }
+ CRYPTO_Q_UNLOCK();
+ complete_and_exit(&cryptoproc_exited, 0);
+}
+
+/*
+ * Crypto returns thread, does callbacks for processed crypto requests.
+ * Callbacks are done here, rather than in the crypto drivers, because
+ * callbacks typically are expensive and would slow interrupt handling.
+ */
+static int
+crypto_ret_proc(void *arg)
+{
+ struct cryptop *crpt;
+ struct cryptkop *krpt;
+ unsigned long r_flags;
+
+ ocf_daemonize("crypto_ret");
+
+ CRYPTO_RETQ_LOCK();
+ for (;;) {
+ /* Harvest return q's for completed ops */
+ crpt = NULL;
+ if (!list_empty(&crp_ret_q))
+ crpt = list_entry(crp_ret_q.next, typeof(*crpt), crp_next);
+ if (crpt != NULL)
+ list_del(&crpt->crp_next);
+
+ krpt = NULL;
+ if (!list_empty(&crp_ret_kq))
+ krpt = list_entry(crp_ret_kq.next, typeof(*krpt), krp_next);
+ if (krpt != NULL)
+ list_del(&krpt->krp_next);
+
+ if (crpt != NULL || krpt != NULL) {
+ CRYPTO_RETQ_UNLOCK();
+ /*
+ * Run callbacks unlocked.
+ */
+ if (crpt != NULL)
+ crpt->crp_callback(crpt);
+ if (krpt != NULL)
+ krpt->krp_callback(krpt);
+ CRYPTO_RETQ_LOCK();
+ } else {
+ /*
+ * Nothing more to be processed. Sleep until we're
+ * woken because there are more returns to process.
+ */
+ dprintk("%s - sleeping\n", __FUNCTION__);
+ CRYPTO_RETQ_UNLOCK();
+ wait_event_interruptible(cryptoretproc_wait,
+ cryptoretthread == NULL ||
+ !list_empty(&crp_ret_q) ||
+ !list_empty(&crp_ret_kq));
+ if (signal_pending (current)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_lock_irq(&current->sigmask_lock);
+#endif
+ flush_signals(current);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_unlock_irq(&current->sigmask_lock);
+#endif
+ }
+ CRYPTO_RETQ_LOCK();
+ dprintk("%s - awake\n", __FUNCTION__);
+ if (cryptoretthread == NULL) {
+ dprintk("%s - EXITING!\n", __FUNCTION__);
+ break;
+ }
+ cryptostats.cs_rets++;
+ }
+ }
+ CRYPTO_RETQ_UNLOCK();
+ complete_and_exit(&cryptoretproc_exited, 0);
+}
+
+
+#if 0 /* should put this into /proc or something */
+static void
+db_show_drivers(void)
+{
+ int hid;
+
+ db_printf("%12s %4s %4s %8s %2s %2s\n"
+ , "Device"
+ , "Ses"
+ , "Kops"
+ , "Flags"
+ , "QB"
+ , "KB"
+ );
+ for (hid = 0; hid < crypto_drivers_num; hid++) {
+ const struct cryptocap *cap = &crypto_drivers[hid];
+ if (cap->cc_dev == NULL)
+ continue;
+ db_printf("%-12s %4u %4u %08x %2u %2u\n"
+ , device_get_nameunit(cap->cc_dev)
+ , cap->cc_sessions
+ , cap->cc_koperations
+ , cap->cc_flags
+ , cap->cc_qblocked
+ , cap->cc_kqblocked
+ );
+ }
+}
+
+DB_SHOW_COMMAND(crypto, db_show_crypto)
+{
+ struct cryptop *crp;
+
+ db_show_drivers();
+ db_printf("\n");
+
+ db_printf("%4s %8s %4s %4s %4s %4s %8s %8s\n",
+ "HID", "Caps", "Ilen", "Olen", "Etype", "Flags",
+ "Desc", "Callback");
+ TAILQ_FOREACH(crp, &crp_q, crp_next) {
+ db_printf("%4u %08x %4u %4u %4u %04x %8p %8p\n"
+ , (int) CRYPTO_SESID2HID(crp->crp_sid)
+ , (int) CRYPTO_SESID2CAPS(crp->crp_sid)
+ , crp->crp_ilen, crp->crp_olen
+ , crp->crp_etype
+ , crp->crp_flags
+ , crp->crp_desc
+ , crp->crp_callback
+ );
+ }
+ if (!TAILQ_EMPTY(&crp_ret_q)) {
+ db_printf("\n%4s %4s %4s %8s\n",
+ "HID", "Etype", "Flags", "Callback");
+ TAILQ_FOREACH(crp, &crp_ret_q, crp_next) {
+ db_printf("%4u %4u %04x %8p\n"
+ , (int) CRYPTO_SESID2HID(crp->crp_sid)
+ , crp->crp_etype
+ , crp->crp_flags
+ , crp->crp_callback
+ );
+ }
+ }
+}
+
+DB_SHOW_COMMAND(kcrypto, db_show_kcrypto)
+{
+ struct cryptkop *krp;
+
+ db_show_drivers();
+ db_printf("\n");
+
+ db_printf("%4s %5s %4s %4s %8s %4s %8s\n",
+ "Op", "Status", "#IP", "#OP", "CRID", "HID", "Callback");
+ TAILQ_FOREACH(krp, &crp_kq, krp_next) {
+ db_printf("%4u %5u %4u %4u %08x %4u %8p\n"
+ , krp->krp_op
+ , krp->krp_status
+ , krp->krp_iparams, krp->krp_oparams
+ , krp->krp_crid, krp->krp_hid
+ , krp->krp_callback
+ );
+ }
+ if (!TAILQ_EMPTY(&crp_ret_q)) {
+ db_printf("%4s %5s %8s %4s %8s\n",
+ "Op", "Status", "CRID", "HID", "Callback");
+ TAILQ_FOREACH(krp, &crp_ret_kq, krp_next) {
+ db_printf("%4u %5u %08x %4u %8p\n"
+ , krp->krp_op
+ , krp->krp_status
+ , krp->krp_crid, krp->krp_hid
+ , krp->krp_callback
+ );
+ }
+ }
+}
+#endif
+
+
+static int
+crypto_init(void)
+{
+ int error;
+ struct task_struct *t;
+
+ dprintk("%s(0x%x)\n", __FUNCTION__, (int) crypto_init);
+
+ if (crypto_initted)
+ return 0;
+ crypto_initted = 1;
+
+ spin_lock_init(&crypto_drivers_lock);
+ spin_lock_init(&crypto_q_lock);
+ spin_lock_init(&crypto_ret_q_lock);
+
+ cryptop_zone = kmem_cache_create("cryptop", sizeof(struct cryptop),
+ 0, SLAB_HWCACHE_ALIGN, NULL
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ , NULL
+#endif
+ );
+
+ cryptodesc_zone = kmem_cache_create("cryptodesc", sizeof(struct cryptodesc),
+ 0, SLAB_HWCACHE_ALIGN, NULL
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ , NULL
+#endif
+ );
+
+ if (cryptodesc_zone == NULL || cryptop_zone == NULL) {
+ printk("crypto: crypto_init cannot setup crypto zones\n");
+ error = ENOMEM;
+ goto bad;
+ }
+
+ crypto_drivers_num = CRYPTO_DRIVERS_INITIAL;
+ crypto_drivers = kmalloc(crypto_drivers_num * sizeof(struct cryptocap),
+ GFP_KERNEL);
+ if (crypto_drivers == NULL) {
+ printk("crypto: crypto_init cannot setup crypto drivers\n");
+ error = ENOMEM;
+ goto bad;
+ }
+
+ memset(crypto_drivers, 0, crypto_drivers_num * sizeof(struct cryptocap));
+
+ init_completion(&cryptoproc_exited);
+ init_completion(&cryptoretproc_exited);
+
+// cryptothread = -1; /* to avoid race condition where proc runs first */
+ t = kthread_create(crypto_proc, NULL,
+ "cryptothread");
+ if (IS_ERR(t)) {
+ error = PTR_ERR(t);
+ printk("crypto: crypto_init cannot start crypto thread; error %d",
+ error);
+ goto bad;
+ }
+ cryptothread = t;
+ wake_up_process(t);
+
+// cryptoretthread = -1; /* to avoid race condition where proc runs first */
+ t = kthread_create(crypto_ret_proc, NULL,
+ "cryptoretthread");
+ if (IS_ERR(t)) {
+ error = PTR_ERR(t);
+ printk("crypto: crypto_init cannot start cryptoret thread; error %d",
+ error);
+ goto bad;
+ }
+ cryptoretthread = t;
+ wake_up_process(t);
+
+ return 0;
+bad:
+ crypto_exit();
+ return error;
+}
+
+
+static void
+crypto_exit(void)
+{
+ unsigned long d_flags;
+
+ dprintk("%s()\n", __FUNCTION__);
+
+ /*
+ * Terminate any crypto threads.
+ */
+
+ CRYPTO_DRIVER_LOCK();
+ if (NULL != cryptothread)
+ send_sig(SIGTERM, cryptothread, 1);
+ cryptothread = NULL;
+ wake_up_interruptible(&cryptoproc_wait);
+ CRYPTO_DRIVER_UNLOCK();
+
+ wait_for_completion(&cryptoproc_exited);
+
+ CRYPTO_DRIVER_LOCK();
+ if (NULL != cryptoretthread)
+ send_sig(SIGTERM, cryptoretthread, 1);
+ cryptoretthread = NULL;
+ wake_up_interruptible(&cryptoretproc_wait);
+ CRYPTO_DRIVER_UNLOCK();
+
+ wait_for_completion(&cryptoretproc_exited);
+
+ /* XXX flush queues??? */
+
+ /*
+ * Reclaim dynamically allocated resources.
+ */
+ if (crypto_drivers != NULL)
+ kfree(crypto_drivers);
+
+ if (cryptodesc_zone != NULL)
+ kmem_cache_destroy(cryptodesc_zone);
+ if (cryptop_zone != NULL)
+ kmem_cache_destroy(cryptop_zone);
+}
+
+
+EXPORT_SYMBOL(crypto_newsession);
+EXPORT_SYMBOL(crypto_freesession);
+EXPORT_SYMBOL(crypto_get_driverid);
+EXPORT_SYMBOL(crypto_kregister);
+EXPORT_SYMBOL(crypto_register);
+EXPORT_SYMBOL(crypto_unregister);
+EXPORT_SYMBOL(crypto_unregister_all);
+EXPORT_SYMBOL(crypto_unblock);
+EXPORT_SYMBOL(crypto_dispatch);
+EXPORT_SYMBOL(crypto_kdispatch);
+EXPORT_SYMBOL(crypto_freereq);
+EXPORT_SYMBOL(crypto_getreq);
+EXPORT_SYMBOL(crypto_done);
+EXPORT_SYMBOL(crypto_kdone);
+EXPORT_SYMBOL(crypto_getfeat);
+EXPORT_SYMBOL(crypto_userasymcrypto);
+EXPORT_SYMBOL(crypto_getcaps);
+EXPORT_SYMBOL(crypto_find_driver);
+EXPORT_SYMBOL(crypto_find_device_byhid);
+
+module_init(crypto_init);
+module_exit(crypto_exit);
+
+MODULE_LICENSE("BSD");
+MODULE_AUTHOR("David McCullough <david_mccullough@securecomputing.com>");
+MODULE_DESCRIPTION("OCF (OpenBSD Cryptographic Framework)");
diff --git a/crypto/ocf/cryptodev.c b/crypto/ocf/cryptodev.c
new file mode 100644
index 000000000000..13d57a11fd7e
--- /dev/null
+++ b/crypto/ocf/cryptodev.c
@@ -0,0 +1,1048 @@
+/* $OpenBSD: cryptodev.c,v 1.52 2002/06/19 07:22:46 deraadt Exp $ */
+
+/*-
+ * Linux port done by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2006-2007 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * Copyright (c) 2001 Theo de Raadt
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ *
+__FBSDID("$FreeBSD: src/sys/opencrypto/cryptodev.c,v 1.34 2007/05/09 19:37:02 gnn Exp $");
+ */
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include <linux/file.h>
+#include <linux/mount.h>
+#include <linux/miscdevice.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+
+#include <cryptodev.h>
+#include <uio.h>
+
+extern asmlinkage long sys_dup(unsigned int fildes);
+
+#define debug cryptodev_debug
+int cryptodev_debug = 0;
+module_param(cryptodev_debug, int, 0644);
+MODULE_PARM_DESC(cryptodev_debug, "Enable cryptodev debug");
+
+struct csession_info {
+ u_int16_t blocksize;
+ u_int16_t minkey, maxkey;
+
+ u_int16_t keysize;
+ /* u_int16_t hashsize; */
+ u_int16_t authsize;
+ /* u_int16_t ctxsize; */
+};
+
+struct csession {
+ struct list_head list;
+ u_int64_t sid;
+ u_int32_t ses;
+
+ wait_queue_head_t waitq;
+
+ u_int32_t cipher;
+
+ u_int32_t mac;
+
+ caddr_t key;
+ int keylen;
+ u_char tmp_iv[EALG_MAX_BLOCK_LEN];
+
+ caddr_t mackey;
+ int mackeylen;
+
+ struct csession_info info;
+
+ struct iovec iovec;
+ struct uio uio;
+ int error;
+};
+
+struct fcrypt {
+ struct list_head csessions;
+ int sesn;
+};
+
+static struct csession *csefind(struct fcrypt *, u_int);
+static int csedelete(struct fcrypt *, struct csession *);
+static struct csession *cseadd(struct fcrypt *, struct csession *);
+static struct csession *csecreate(struct fcrypt *, u_int64_t,
+ struct cryptoini *crie, struct cryptoini *cria, struct csession_info *);
+static int csefree(struct csession *);
+
+static int cryptodev_op(struct csession *, struct crypt_op *);
+static int cryptodev_key(struct crypt_kop *);
+static int cryptodev_find(struct crypt_find_op *);
+
+static int cryptodev_cb(void *);
+static int cryptodev_open(struct inode *inode, struct file *filp);
+
+/*
+ * Check a crypto identifier to see if it requested
+ * a valid crid and it's capabilities match.
+ */
+static int
+checkcrid(int crid)
+{
+ int hid = crid & ~(CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_HARDWARE);
+ int typ = crid & (CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_HARDWARE);
+ int caps = 0;
+
+ /* if the user hasn't selected a driver, then just call newsession */
+ if (hid == 0 && typ != 0)
+ return 0;
+
+ caps = crypto_getcaps(hid);
+
+ /* didn't find anything with capabilities */
+ if (caps == 0) {
+ dprintk("%s: hid=%x typ=%x not matched\n", __FUNCTION__, hid, typ);
+ return EINVAL;
+ }
+
+ /* the user didn't specify SW or HW, so the driver is ok */
+ if (typ == 0)
+ return 0;
+
+ /* if the type specified didn't match */
+ if (typ != (caps & (CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_HARDWARE))) {
+ dprintk("%s: hid=%x typ=%x caps=%x not matched\n", __FUNCTION__,
+ hid, typ, caps);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+cryptodev_op(struct csession *cse, struct crypt_op *cop)
+{
+ struct cryptop *crp = NULL;
+ struct cryptodesc *crde = NULL, *crda = NULL;
+ int error = 0;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (cop->len > CRYPTO_MAX_DATA_LEN) {
+ dprintk("%s: %d > %d\n", __FUNCTION__, cop->len, CRYPTO_MAX_DATA_LEN);
+ return (E2BIG);
+ }
+
+ if (cse->info.blocksize && (cop->len % cse->info.blocksize) != 0) {
+ dprintk("%s: blocksize=%d len=%d\n", __FUNCTION__, cse->info.blocksize,
+ cop->len);
+ return (EINVAL);
+ }
+
+ cse->uio.uio_iov = &cse->iovec;
+ cse->uio.uio_iovcnt = 1;
+ cse->uio.uio_offset = 0;
+#if 0
+ cse->uio.uio_resid = cop->len;
+ cse->uio.uio_segflg = UIO_SYSSPACE;
+ cse->uio.uio_rw = UIO_WRITE;
+ cse->uio.uio_td = td;
+#endif
+ cse->uio.uio_iov[0].iov_len = cop->len;
+ if (cse->info.authsize)
+ cse->uio.uio_iov[0].iov_len += cse->info.authsize;
+ cse->uio.uio_iov[0].iov_base = kmalloc(cse->uio.uio_iov[0].iov_len,
+ GFP_KERNEL);
+
+ if (cse->uio.uio_iov[0].iov_base == NULL) {
+ dprintk("%s: iov_base kmalloc(%d) failed\n", __FUNCTION__,
+ cse->uio.uio_iov[0].iov_len);
+ return (ENOMEM);
+ }
+
+ crp = crypto_getreq((cse->info.blocksize != 0) + (cse->info.authsize != 0));
+ if (crp == NULL) {
+ dprintk("%s: ENOMEM\n", __FUNCTION__);
+ error = ENOMEM;
+ goto bail;
+ }
+
+ if (cse->info.authsize) {
+ crda = crp->crp_desc;
+ if (cse->info.blocksize)
+ crde = crda->crd_next;
+ } else {
+ if (cse->info.blocksize)
+ crde = crp->crp_desc;
+ else {
+ dprintk("%s: bad request\n", __FUNCTION__);
+ error = EINVAL;
+ goto bail;
+ }
+ }
+
+ if ((error = copy_from_user(cse->uio.uio_iov[0].iov_base, cop->src,
+ cop->len))) {
+ dprintk("%s: bad copy\n", __FUNCTION__);
+ goto bail;
+ }
+
+ if (crda) {
+ crda->crd_skip = 0;
+ crda->crd_len = cop->len;
+ crda->crd_inject = cop->len;
+
+ crda->crd_alg = cse->mac;
+ crda->crd_key = cse->mackey;
+ crda->crd_klen = cse->mackeylen * 8;
+ }
+
+ if (crde) {
+ if (cop->op == COP_ENCRYPT)
+ crde->crd_flags |= CRD_F_ENCRYPT;
+ else
+ crde->crd_flags &= ~CRD_F_ENCRYPT;
+ crde->crd_len = cop->len;
+ crde->crd_inject = 0;
+
+ crde->crd_alg = cse->cipher;
+ crde->crd_key = cse->key;
+ crde->crd_klen = cse->keylen * 8;
+ }
+
+ crp->crp_ilen = cse->uio.uio_iov[0].iov_len;
+ crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIMM
+ | (cop->flags & COP_F_BATCH);
+ crp->crp_buf = (caddr_t)&cse->uio;
+ crp->crp_callback = (int (*) (struct cryptop *)) cryptodev_cb;
+ crp->crp_sid = cse->sid;
+ crp->crp_opaque = (void *)cse;
+
+ if (cop->iv) {
+ if (crde == NULL) {
+ error = EINVAL;
+ dprintk("%s no crde\n", __FUNCTION__);
+ goto bail;
+ }
+ if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */
+ error = EINVAL;
+ dprintk("%s arc4 with IV\n", __FUNCTION__);
+ goto bail;
+ }
+ if ((error = copy_from_user(cse->tmp_iv, cop->iv,
+ cse->info.blocksize))) {
+ dprintk("%s bad iv copy\n", __FUNCTION__);
+ goto bail;
+ }
+ memcpy(crde->crd_iv, cse->tmp_iv, cse->info.blocksize);
+ crde->crd_flags |= CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT;
+ crde->crd_skip = 0;
+ } else if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */
+ crde->crd_skip = 0;
+ } else if (crde) {
+ crde->crd_flags |= CRD_F_IV_PRESENT;
+ crde->crd_skip = cse->info.blocksize;
+ crde->crd_len -= cse->info.blocksize;
+ }
+
+ if (cop->mac && crda == NULL) {
+ error = EINVAL;
+ dprintk("%s no crda\n", __FUNCTION__);
+ goto bail;
+ }
+
+ /*
+ * Let the dispatch run unlocked, then, interlock against the
+ * callback before checking if the operation completed and going
+ * to sleep. This insures drivers don't inherit our lock which
+ * results in a lock order reversal between crypto_dispatch forced
+ * entry and the crypto_done callback into us.
+ */
+ error = crypto_dispatch(crp);
+ if (error == 0) {
+ dprintk("%s about to WAIT\n", __FUNCTION__);
+ /*
+ * we really need to wait for driver to complete to maintain
+ * state, luckily interrupts will be remembered
+ */
+ do {
+ error = wait_event_interruptible(crp->crp_waitq,
+ ((crp->crp_flags & CRYPTO_F_DONE) != 0));
+ /*
+ * we can't break out of this loop or we will leave behind
+ * a huge mess, however, staying here means if your driver
+ * is broken user applications can hang and not be killed.
+ * The solution, fix your driver :-)
+ */
+ if (error) {
+ schedule();
+ error = 0;
+ }
+ } while ((crp->crp_flags & CRYPTO_F_DONE) == 0);
+ dprintk("%s finished WAITING error=%d\n", __FUNCTION__, error);
+ }
+
+ if (crp->crp_etype != 0) {
+ error = crp->crp_etype;
+ dprintk("%s error in crp processing\n", __FUNCTION__);
+ goto bail;
+ }
+
+ if (cse->error) {
+ error = cse->error;
+ dprintk("%s error in cse processing\n", __FUNCTION__);
+ goto bail;
+ }
+
+ if (cop->dst && (error = copy_to_user(cop->dst,
+ cse->uio.uio_iov[0].iov_base, cop->len))) {
+ dprintk("%s bad dst copy\n", __FUNCTION__);
+ goto bail;
+ }
+
+ if (cop->mac &&
+ (error=copy_to_user(cop->mac,
+ (caddr_t)cse->uio.uio_iov[0].iov_base + cop->len,
+ cse->info.authsize))) {
+ dprintk("%s bad mac copy\n", __FUNCTION__);
+ goto bail;
+ }
+
+bail:
+ if (crp)
+ crypto_freereq(crp);
+ if (cse->uio.uio_iov[0].iov_base)
+ kfree(cse->uio.uio_iov[0].iov_base);
+
+ return (error);
+}
+
+static int
+cryptodev_cb(void *op)
+{
+ struct cryptop *crp = (struct cryptop *) op;
+ struct csession *cse = (struct csession *)crp->crp_opaque;
+ int error;
+
+ dprintk("%s()\n", __FUNCTION__);
+ error = crp->crp_etype;
+ if (error == EAGAIN) {
+ crp->crp_flags &= ~CRYPTO_F_DONE;
+#ifdef NOTYET
+ /*
+ * DAVIDM I am fairly sure that we should turn this into a batch
+ * request to stop bad karma/lockup, revisit
+ */
+ crp->crp_flags |= CRYPTO_F_BATCH;
+#endif
+ return crypto_dispatch(crp);
+ }
+ if (error != 0 || (crp->crp_flags & CRYPTO_F_DONE)) {
+ cse->error = error;
+ wake_up_interruptible(&crp->crp_waitq);
+ }
+ return (0);
+}
+
+static int
+cryptodevkey_cb(void *op)
+{
+ struct cryptkop *krp = (struct cryptkop *) op;
+ dprintk("%s()\n", __FUNCTION__);
+ wake_up_interruptible(&krp->krp_waitq);
+ return (0);
+}
+
+static int
+cryptodev_key(struct crypt_kop *kop)
+{
+ struct cryptkop *krp = NULL;
+ int error = EINVAL;
+ int in, out, size, i;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (kop->crk_iparams + kop->crk_oparams > CRK_MAXPARAM) {
+ dprintk("%s params too big\n", __FUNCTION__);
+ return (EFBIG);
+ }
+
+ in = kop->crk_iparams;
+ out = kop->crk_oparams;
+ switch (kop->crk_op) {
+ case CRK_MOD_EXP:
+ if (in == 3 && out == 1)
+ break;
+ return (EINVAL);
+ case CRK_MOD_EXP_CRT:
+ if (in == 6 && out == 1)
+ break;
+ return (EINVAL);
+ case CRK_DSA_SIGN:
+ if (in == 5 && out == 2)
+ break;
+ return (EINVAL);
+ case CRK_DSA_VERIFY:
+ if (in == 7 && out == 0)
+ break;
+ return (EINVAL);
+ case CRK_DH_COMPUTE_KEY:
+ if (in == 3 && out == 1)
+ break;
+ return (EINVAL);
+ default:
+ return (EINVAL);
+ }
+
+ krp = (struct cryptkop *)kmalloc(sizeof *krp, GFP_KERNEL);
+ if (!krp)
+ return (ENOMEM);
+ bzero(krp, sizeof *krp);
+ krp->krp_op = kop->crk_op;
+ krp->krp_status = kop->crk_status;
+ krp->krp_iparams = kop->crk_iparams;
+ krp->krp_oparams = kop->crk_oparams;
+ krp->krp_crid = kop->crk_crid;
+ krp->krp_status = 0;
+ krp->krp_flags = CRYPTO_KF_CBIMM;
+ krp->krp_callback = (int (*) (struct cryptkop *)) cryptodevkey_cb;
+ init_waitqueue_head(&krp->krp_waitq);
+
+ for (i = 0; i < CRK_MAXPARAM; i++)
+ krp->krp_param[i].crp_nbits = kop->crk_param[i].crp_nbits;
+ for (i = 0; i < krp->krp_iparams + krp->krp_oparams; i++) {
+ size = (krp->krp_param[i].crp_nbits + 7) / 8;
+ if (size == 0)
+ continue;
+ krp->krp_param[i].crp_p = (caddr_t) kmalloc(size, GFP_KERNEL);
+ if (i >= krp->krp_iparams)
+ continue;
+ error = copy_from_user(krp->krp_param[i].crp_p,
+ kop->crk_param[i].crp_p, size);
+ if (error)
+ goto fail;
+ }
+
+ error = crypto_kdispatch(krp);
+ if (error)
+ goto fail;
+
+ do {
+ error = wait_event_interruptible(krp->krp_waitq,
+ ((krp->krp_flags & CRYPTO_KF_DONE) != 0));
+ /*
+ * we can't break out of this loop or we will leave behind
+ * a huge mess, however, staying here means if your driver
+ * is broken user applications can hang and not be killed.
+ * The solution, fix your driver :-)
+ */
+ if (error) {
+ schedule();
+ error = 0;
+ }
+ } while ((krp->krp_flags & CRYPTO_KF_DONE) == 0);
+
+ dprintk("%s finished WAITING error=%d\n", __FUNCTION__, error);
+
+ kop->crk_crid = krp->krp_crid; /* device that did the work */
+ if (krp->krp_status != 0) {
+ error = krp->krp_status;
+ goto fail;
+ }
+
+ for (i = krp->krp_iparams; i < krp->krp_iparams + krp->krp_oparams; i++) {
+ size = (krp->krp_param[i].crp_nbits + 7) / 8;
+ if (size == 0)
+ continue;
+ error = copy_to_user(kop->crk_param[i].crp_p, krp->krp_param[i].crp_p,
+ size);
+ if (error)
+ goto fail;
+ }
+
+fail:
+ if (krp) {
+ kop->crk_status = krp->krp_status;
+ for (i = 0; i < CRK_MAXPARAM; i++) {
+ if (krp->krp_param[i].crp_p)
+ kfree(krp->krp_param[i].crp_p);
+ }
+ kfree(krp);
+ }
+ return (error);
+}
+
+static int
+cryptodev_find(struct crypt_find_op *find)
+{
+ device_t dev;
+
+ if (find->crid != -1) {
+ dev = crypto_find_device_byhid(find->crid);
+ if (dev == NULL)
+ return (ENOENT);
+ strlcpy(find->name, device_get_nameunit(dev),
+ sizeof(find->name));
+ } else {
+ find->crid = crypto_find_driver(find->name);
+ if (find->crid == -1)
+ return (ENOENT);
+ }
+ return (0);
+}
+
+static struct csession *
+csefind(struct fcrypt *fcr, u_int ses)
+{
+ struct csession *cse;
+
+ dprintk("%s()\n", __FUNCTION__);
+ list_for_each_entry(cse, &fcr->csessions, list)
+ if (cse->ses == ses)
+ return (cse);
+ return (NULL);
+}
+
+static int
+csedelete(struct fcrypt *fcr, struct csession *cse_del)
+{
+ struct csession *cse;
+
+ dprintk("%s()\n", __FUNCTION__);
+ list_for_each_entry(cse, &fcr->csessions, list) {
+ if (cse == cse_del) {
+ list_del(&cse->list);
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static struct csession *
+cseadd(struct fcrypt *fcr, struct csession *cse)
+{
+ dprintk("%s()\n", __FUNCTION__);
+ list_add_tail(&cse->list, &fcr->csessions);
+ cse->ses = fcr->sesn++;
+ return (cse);
+}
+
+static struct csession *
+csecreate(struct fcrypt *fcr, u_int64_t sid, struct cryptoini *crie,
+ struct cryptoini *cria, struct csession_info *info)
+{
+ struct csession *cse;
+
+ dprintk("%s()\n", __FUNCTION__);
+ cse = (struct csession *) kmalloc(sizeof(struct csession), GFP_KERNEL);
+ if (cse == NULL)
+ return NULL;
+ memset(cse, 0, sizeof(struct csession));
+
+ INIT_LIST_HEAD(&cse->list);
+ init_waitqueue_head(&cse->waitq);
+
+ cse->key = crie->cri_key;
+ cse->keylen = crie->cri_klen/8;
+ cse->mackey = cria->cri_key;
+ cse->mackeylen = cria->cri_klen/8;
+ cse->sid = sid;
+ cse->cipher = crie->cri_alg;
+ cse->mac = cria->cri_alg;
+ cse->info = *info;
+ cseadd(fcr, cse);
+ return (cse);
+}
+
+static int
+csefree(struct csession *cse)
+{
+ int error;
+
+ dprintk("%s()\n", __FUNCTION__);
+ error = crypto_freesession(cse->sid);
+ if (cse->key)
+ kfree(cse->key);
+ if (cse->mackey)
+ kfree(cse->mackey);
+ kfree(cse);
+ return(error);
+}
+
+static int
+cryptodev_ioctl(
+ struct inode *inode,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct cryptoini cria, crie;
+ struct fcrypt *fcr = filp->private_data;
+ struct csession *cse;
+ struct csession_info info;
+ struct session2_op sop;
+ struct crypt_op cop;
+ struct crypt_kop kop;
+ struct crypt_find_op fop;
+ u_int64_t sid;
+ u_int32_t ses;
+ int feat, fd, error = 0, crid;
+ mm_segment_t fs;
+
+ dprintk("%s(cmd=%x arg=%lx)\n", __FUNCTION__, cmd, arg);
+
+ switch (cmd) {
+
+ case CRIOGET: {
+ dprintk("%s(CRIOGET)\n", __FUNCTION__);
+ fs = get_fs();
+ set_fs(get_ds());
+ for (fd = 0; fd < files_fdtable(current->files)->max_fds; fd++)
+ if (files_fdtable(current->files)->fd[fd] == filp)
+ break;
+ fd = sys_dup(fd);
+ set_fs(fs);
+ put_user(fd, (int *) arg);
+ return IS_ERR_VALUE(fd) ? fd : 0;
+ }
+
+#define CIOCGSESSSTR (cmd == CIOCGSESSION ? "CIOCGSESSION" : "CIOCGSESSION2")
+ case CIOCGSESSION:
+ case CIOCGSESSION2:
+ dprintk("%s(%s)\n", __FUNCTION__, CIOCGSESSSTR);
+ memset(&crie, 0, sizeof(crie));
+ memset(&cria, 0, sizeof(cria));
+ memset(&info, 0, sizeof(info));
+ memset(&sop, 0, sizeof(sop));
+
+ if (copy_from_user(&sop, (void*)arg, (cmd == CIOCGSESSION) ?
+ sizeof(struct session_op) : sizeof(sop))) {
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EFAULT;
+ goto bail;
+ }
+
+ switch (sop.cipher) {
+ case 0:
+ dprintk("%s(%s) - no cipher\n", __FUNCTION__, CIOCGSESSSTR);
+ break;
+ case CRYPTO_NULL_CBC:
+ info.blocksize = NULL_BLOCK_LEN;
+ info.minkey = NULL_MIN_KEY_LEN;
+ info.maxkey = NULL_MAX_KEY_LEN;
+ break;
+ case CRYPTO_DES_CBC:
+ info.blocksize = DES_BLOCK_LEN;
+ info.minkey = DES_MIN_KEY_LEN;
+ info.maxkey = DES_MAX_KEY_LEN;
+ break;
+ case CRYPTO_3DES_CBC:
+ info.blocksize = DES3_BLOCK_LEN;
+ info.minkey = DES3_MIN_KEY_LEN;
+ info.maxkey = DES3_MAX_KEY_LEN;
+ break;
+ case CRYPTO_BLF_CBC:
+ info.blocksize = BLOWFISH_BLOCK_LEN;
+ info.minkey = BLOWFISH_MIN_KEY_LEN;
+ info.maxkey = BLOWFISH_MAX_KEY_LEN;
+ break;
+ case CRYPTO_CAST_CBC:
+ info.blocksize = CAST128_BLOCK_LEN;
+ info.minkey = CAST128_MIN_KEY_LEN;
+ info.maxkey = CAST128_MAX_KEY_LEN;
+ break;
+ case CRYPTO_SKIPJACK_CBC:
+ info.blocksize = SKIPJACK_BLOCK_LEN;
+ info.minkey = SKIPJACK_MIN_KEY_LEN;
+ info.maxkey = SKIPJACK_MAX_KEY_LEN;
+ break;
+ case CRYPTO_AES_CBC:
+ info.blocksize = AES_BLOCK_LEN;
+ info.minkey = AES_MIN_KEY_LEN;
+ info.maxkey = AES_MAX_KEY_LEN;
+ break;
+ case CRYPTO_ARC4:
+ info.blocksize = ARC4_BLOCK_LEN;
+ info.minkey = ARC4_MIN_KEY_LEN;
+ info.maxkey = ARC4_MAX_KEY_LEN;
+ break;
+ case CRYPTO_CAMELLIA_CBC:
+ info.blocksize = CAMELLIA_BLOCK_LEN;
+ info.minkey = CAMELLIA_MIN_KEY_LEN;
+ info.maxkey = CAMELLIA_MAX_KEY_LEN;
+ break;
+ default:
+ dprintk("%s(%s) - bad cipher\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EINVAL;
+ goto bail;
+ }
+
+ switch (sop.mac) {
+ case 0:
+ dprintk("%s(%s) - no mac\n", __FUNCTION__, CIOCGSESSSTR);
+ break;
+ case CRYPTO_NULL_HMAC:
+ info.authsize = NULL_HASH_LEN;
+ break;
+ case CRYPTO_MD5:
+ info.authsize = MD5_HASH_LEN;
+ break;
+ case CRYPTO_SHA1:
+ info.authsize = SHA1_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_256:
+ info.authsize = SHA2_256_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_384:
+ info.authsize = SHA2_384_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_512:
+ info.authsize = SHA2_512_HASH_LEN;
+ break;
+ case CRYPTO_RIPEMD160:
+ info.authsize = RIPEMD160_HASH_LEN;
+ break;
+ case CRYPTO_MD5_HMAC:
+ info.authsize = MD5_HASH_LEN;
+ break;
+ case CRYPTO_SHA1_HMAC:
+ info.authsize = SHA1_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_256_HMAC:
+ info.authsize = SHA2_256_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_384_HMAC:
+ info.authsize = SHA2_384_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_512_HMAC:
+ info.authsize = SHA2_512_HASH_LEN;
+ break;
+ case CRYPTO_RIPEMD160_HMAC:
+ info.authsize = RIPEMD160_HASH_LEN;
+ break;
+ default:
+ dprintk("%s(%s) - bad mac\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EINVAL;
+ goto bail;
+ }
+
+ if (info.blocksize) {
+ crie.cri_alg = sop.cipher;
+ crie.cri_klen = sop.keylen * 8;
+ if ((info.maxkey && sop.keylen > info.maxkey) ||
+ sop.keylen < info.minkey) {
+ dprintk("%s(%s) - bad key\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EINVAL;
+ goto bail;
+ }
+
+ crie.cri_key = (u_int8_t *) kmalloc(crie.cri_klen/8+1, GFP_KERNEL);
+ if (copy_from_user(crie.cri_key, sop.key,
+ crie.cri_klen/8)) {
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EFAULT;
+ goto bail;
+ }
+ if (info.authsize)
+ crie.cri_next = &cria;
+ }
+
+ if (info.authsize) {
+ cria.cri_alg = sop.mac;
+ cria.cri_klen = sop.mackeylen * 8;
+ if ((info.maxkey && sop.mackeylen > info.maxkey) ||
+ sop.keylen < info.minkey) {
+ dprintk("%s(%s) - mackeylen %d\n", __FUNCTION__, CIOCGSESSSTR,
+ sop.mackeylen);
+ error = EINVAL;
+ goto bail;
+ }
+
+ if (cria.cri_klen) {
+ cria.cri_key = (u_int8_t *) kmalloc(cria.cri_klen/8,GFP_KERNEL);
+ if (copy_from_user(cria.cri_key, sop.mackey,
+ cria.cri_klen / 8)) {
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EFAULT;
+ goto bail;
+ }
+ }
+ }
+
+ /* NB: CIOGSESSION2 has the crid */
+ if (cmd == CIOCGSESSION2) {
+ crid = sop.crid;
+ error = checkcrid(crid);
+ if (error) {
+ dprintk("%s(%s) - checkcrid %x\n", __FUNCTION__,
+ CIOCGSESSSTR, error);
+ goto bail;
+ }
+ } else {
+ /* allow either HW or SW to be used */
+ crid = CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE;
+ }
+ error = crypto_newsession(&sid, (info.blocksize ? &crie : &cria), crid);
+ if (error) {
+ dprintk("%s(%s) - newsession %d\n",__FUNCTION__,CIOCGSESSSTR,error);
+ goto bail;
+ }
+
+ cse = csecreate(fcr, sid, &crie, &cria, &info);
+ if (cse == NULL) {
+ crypto_freesession(sid);
+ error = EINVAL;
+ dprintk("%s(%s) - csecreate failed\n", __FUNCTION__, CIOCGSESSSTR);
+ goto bail;
+ }
+ sop.ses = cse->ses;
+
+ if (cmd == CIOCGSESSION2) {
+ /* return hardware/driver id */
+ sop.crid = CRYPTO_SESID2HID(cse->sid);
+ }
+
+ if (copy_to_user((void*)arg, &sop, (cmd == CIOCGSESSION) ?
+ sizeof(struct session_op) : sizeof(sop))) {
+ dprintk("%s(%s) - bad copy\n", __FUNCTION__, CIOCGSESSSTR);
+ error = EFAULT;
+ }
+bail:
+ if (error) {
+ dprintk("%s(%s) - bail %d\n", __FUNCTION__, CIOCGSESSSTR, error);
+ if (crie.cri_key)
+ kfree(crie.cri_key);
+ if (cria.cri_key)
+ kfree(cria.cri_key);
+ }
+ break;
+ case CIOCFSESSION:
+ dprintk("%s(CIOCFSESSION)\n", __FUNCTION__);
+ get_user(ses, (uint32_t*)arg);
+ cse = csefind(fcr, ses);
+ if (cse == NULL) {
+ error = EINVAL;
+ dprintk("%s(CIOCFSESSION) - Fail %d\n", __FUNCTION__, error);
+ break;
+ }
+ csedelete(fcr, cse);
+ error = csefree(cse);
+ break;
+ case CIOCCRYPT:
+ dprintk("%s(CIOCCRYPT)\n", __FUNCTION__);
+ if(copy_from_user(&cop, (void*)arg, sizeof(cop))) {
+ dprintk("%s(CIOCCRYPT) - bad copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ cse = csefind(fcr, cop.ses);
+ if (cse == NULL) {
+ error = EINVAL;
+ dprintk("%s(CIOCCRYPT) - Fail %d\n", __FUNCTION__, error);
+ break;
+ }
+ error = cryptodev_op(cse, &cop);
+ if(copy_to_user((void*)arg, &cop, sizeof(cop))) {
+ dprintk("%s(CIOCCRYPT) - bad return copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ break;
+ case CIOCKEY:
+ case CIOCKEY2:
+ dprintk("%s(CIOCKEY)\n", __FUNCTION__);
+ if (!crypto_userasymcrypto)
+ return (EPERM); /* XXX compat? */
+ if(copy_from_user(&kop, (void*)arg, sizeof(kop))) {
+ dprintk("%s(CIOCKEY) - bad copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ if (cmd == CIOCKEY) {
+ /* NB: crypto core enforces s/w driver use */
+ kop.crk_crid =
+ CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE;
+ }
+ error = cryptodev_key(&kop);
+ if(copy_to_user((void*)arg, &kop, sizeof(kop))) {
+ dprintk("%s(CIOCGKEY) - bad return copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ break;
+ case CIOCASYMFEAT:
+ dprintk("%s(CIOCASYMFEAT)\n", __FUNCTION__);
+ if (!crypto_userasymcrypto) {
+ /*
+ * NB: if user asym crypto operations are
+ * not permitted return "no algorithms"
+ * so well-behaved applications will just
+ * fallback to doing them in software.
+ */
+ feat = 0;
+ } else
+ error = crypto_getfeat(&feat);
+ if (!error) {
+ error = copy_to_user((void*)arg, &feat, sizeof(feat));
+ }
+ break;
+ case CIOCFINDDEV:
+ if (copy_from_user(&fop, (void*)arg, sizeof(fop))) {
+ dprintk("%s(CIOCFINDDEV) - bad copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ error = cryptodev_find(&fop);
+ if (copy_to_user((void*)arg, &fop, sizeof(fop))) {
+ dprintk("%s(CIOCFINDDEV) - bad return copy\n", __FUNCTION__);
+ error = EFAULT;
+ goto bail;
+ }
+ break;
+ default:
+ dprintk("%s(unknown ioctl 0x%x)\n", __FUNCTION__, cmd);
+ error = EINVAL;
+ break;
+ }
+ return(-error);
+}
+
+#ifdef HAVE_UNLOCKED_IOCTL
+static long
+cryptodev_unlocked_ioctl(
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ return cryptodev_ioctl(NULL, filp, cmd, arg);
+}
+#endif
+
+static int
+cryptodev_open(struct inode *inode, struct file *filp)
+{
+ struct fcrypt *fcr;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (filp->private_data) {
+ printk("cryptodev: Private data already exists !\n");
+ return(0);
+ }
+
+ fcr = kmalloc(sizeof(*fcr), GFP_KERNEL);
+ if (!fcr) {
+ dprintk("%s() - malloc failed\n", __FUNCTION__);
+ return(-ENOMEM);
+ }
+ memset(fcr, 0, sizeof(*fcr));
+
+ INIT_LIST_HEAD(&fcr->csessions);
+ filp->private_data = fcr;
+ return(0);
+}
+
+static int
+cryptodev_release(struct inode *inode, struct file *filp)
+{
+ struct fcrypt *fcr = filp->private_data;
+ struct csession *cse, *tmp;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (!filp) {
+ printk("cryptodev: No private data on release\n");
+ return(0);
+ }
+
+ list_for_each_entry_safe(cse, tmp, &fcr->csessions, list) {
+ list_del(&cse->list);
+ (void)csefree(cse);
+ }
+ filp->private_data = NULL;
+ kfree(fcr);
+ return(0);
+}
+
+static struct file_operations cryptodev_fops = {
+ .owner = THIS_MODULE,
+ .open = cryptodev_open,
+ .release = cryptodev_release,
+ .ioctl = cryptodev_ioctl,
+#ifdef HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = cryptodev_unlocked_ioctl,
+#endif
+};
+
+static struct miscdevice cryptodev = {
+ .minor = CRYPTODEV_MINOR,
+ .name = "crypto",
+ .fops = &cryptodev_fops,
+};
+
+static int __init
+cryptodev_init(void)
+{
+ int rc;
+
+ dprintk("%s(%p)\n", __FUNCTION__, cryptodev_init);
+ rc = misc_register(&cryptodev);
+ if (rc) {
+ printk(KERN_ERR "cryptodev: registration of /dev/crypto failed\n");
+ return(rc);
+ }
+
+ return(0);
+}
+
+static void __exit
+cryptodev_exit(void)
+{
+ dprintk("%s()\n", __FUNCTION__);
+ misc_deregister(&cryptodev);
+}
+
+module_init(cryptodev_init);
+module_exit(cryptodev_exit);
+
+MODULE_LICENSE("BSD");
+MODULE_AUTHOR("David McCullough <david_mccullough@securecomputing.com>");
+MODULE_DESCRIPTION("Cryptodev (user interface to OCF)");
diff --git a/crypto/ocf/cryptodev.h b/crypto/ocf/cryptodev.h
new file mode 100644
index 000000000000..fa1a57b4f856
--- /dev/null
+++ b/crypto/ocf/cryptodev.h
@@ -0,0 +1,478 @@
+/* $FreeBSD: src/sys/opencrypto/cryptodev.h,v 1.25 2007/05/09 19:37:02 gnn Exp $ */
+/* $OpenBSD: cryptodev.h,v 1.31 2002/06/11 11:14:29 beck Exp $ */
+
+/*-
+ * Linux port done by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2006-2007 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
+ * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting
+ *
+ * This code was written by Angelos D. Keromytis in Athens, Greece, in
+ * February 2000. Network Security Technologies Inc. (NSTI) kindly
+ * supported the development of this code.
+ *
+ * Copyright (c) 2000 Angelos D. Keromytis
+ *
+ * Permission to use, copy, and modify this software with or without fee
+ * is hereby granted, provided that this entire notice is included in
+ * all source code copies of any software which is or includes a copy or
+ * modification of this software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
+ * PURPOSE.
+ *
+ * Copyright (c) 2001 Theo de Raadt
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ *
+ */
+
+#ifndef _CRYPTO_CRYPTO_H_
+#define _CRYPTO_CRYPTO_H_
+
+/* Some initial values */
+#define CRYPTO_DRIVERS_INITIAL 4
+#define CRYPTO_SW_SESSIONS 32
+
+/* Hash values */
+#define NULL_HASH_LEN 0
+#define MD5_HASH_LEN 16
+#define SHA1_HASH_LEN 20
+#define RIPEMD160_HASH_LEN 20
+#define SHA2_256_HASH_LEN 32
+#define SHA2_384_HASH_LEN 48
+#define SHA2_512_HASH_LEN 64
+#define MD5_KPDK_HASH_LEN 16
+#define SHA1_KPDK_HASH_LEN 20
+/* Maximum hash algorithm result length */
+#define HASH_MAX_LEN SHA2_512_HASH_LEN /* Keep this updated */
+
+/* HMAC values */
+#define NULL_HMAC_BLOCK_LEN 1
+#define MD5_HMAC_BLOCK_LEN 64
+#define SHA1_HMAC_BLOCK_LEN 64
+#define RIPEMD160_HMAC_BLOCK_LEN 64
+#define SHA2_256_HMAC_BLOCK_LEN 64
+#define SHA2_384_HMAC_BLOCK_LEN 128
+#define SHA2_512_HMAC_BLOCK_LEN 128
+/* Maximum HMAC block length */
+#define HMAC_MAX_BLOCK_LEN SHA2_512_HMAC_BLOCK_LEN /* Keep this updated */
+#define HMAC_IPAD_VAL 0x36
+#define HMAC_OPAD_VAL 0x5C
+
+/* Encryption algorithm block sizes */
+#define NULL_BLOCK_LEN 1
+#define DES_BLOCK_LEN 8
+#define DES3_BLOCK_LEN 8
+#define BLOWFISH_BLOCK_LEN 8
+#define SKIPJACK_BLOCK_LEN 8
+#define CAST128_BLOCK_LEN 8
+#define RIJNDAEL128_BLOCK_LEN 16
+#define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN
+#define CAMELLIA_BLOCK_LEN 16
+#define ARC4_BLOCK_LEN 1
+#define EALG_MAX_BLOCK_LEN AES_BLOCK_LEN /* Keep this updated */
+
+/* Encryption algorithm min and max key sizes */
+#define NULL_MIN_KEY_LEN 0
+#define NULL_MAX_KEY_LEN 0
+#define DES_MIN_KEY_LEN 8
+#define DES_MAX_KEY_LEN 8
+#define DES3_MIN_KEY_LEN 24
+#define DES3_MAX_KEY_LEN 24
+#define BLOWFISH_MIN_KEY_LEN 4
+#define BLOWFISH_MAX_KEY_LEN 56
+#define SKIPJACK_MIN_KEY_LEN 10
+#define SKIPJACK_MAX_KEY_LEN 10
+#define CAST128_MIN_KEY_LEN 5
+#define CAST128_MAX_KEY_LEN 16
+#define RIJNDAEL128_MIN_KEY_LEN 16
+#define RIJNDAEL128_MAX_KEY_LEN 32
+#define AES_MIN_KEY_LEN RIJNDAEL128_MIN_KEY_LEN
+#define AES_MAX_KEY_LEN RIJNDAEL128_MAX_KEY_LEN
+#define CAMELLIA_MIN_KEY_LEN 16
+#define CAMELLIA_MAX_KEY_LEN 32
+#define ARC4_MIN_KEY_LEN 1
+#define ARC4_MAX_KEY_LEN 256
+
+/* Max size of data that can be processed */
+#define CRYPTO_MAX_DATA_LEN 64*1024 - 1
+
+#define CRYPTO_ALGORITHM_MIN 1
+#define CRYPTO_DES_CBC 1
+#define CRYPTO_3DES_CBC 2
+#define CRYPTO_BLF_CBC 3
+#define CRYPTO_CAST_CBC 4
+#define CRYPTO_SKIPJACK_CBC 5
+#define CRYPTO_MD5_HMAC 6
+#define CRYPTO_SHA1_HMAC 7
+#define CRYPTO_RIPEMD160_HMAC 8
+#define CRYPTO_MD5_KPDK 9
+#define CRYPTO_SHA1_KPDK 10
+#define CRYPTO_RIJNDAEL128_CBC 11 /* 128 bit blocksize */
+#define CRYPTO_AES_CBC 11 /* 128 bit blocksize -- the same as above */
+#define CRYPTO_ARC4 12
+#define CRYPTO_MD5 13
+#define CRYPTO_SHA1 14
+#define CRYPTO_NULL_HMAC 15
+#define CRYPTO_NULL_CBC 16
+#define CRYPTO_DEFLATE_COMP 17 /* Deflate compression algorithm */
+#define CRYPTO_SHA2_256_HMAC 18
+#define CRYPTO_SHA2_384_HMAC 19
+#define CRYPTO_SHA2_512_HMAC 20
+#define CRYPTO_CAMELLIA_CBC 21
+#define CRYPTO_SHA2_256 22
+#define CRYPTO_SHA2_384 23
+#define CRYPTO_SHA2_512 24
+#define CRYPTO_RIPEMD160 25
+#define CRYPTO_ALGORITHM_MAX 25 /* Keep updated - see below */
+
+/* Algorithm flags */
+#define CRYPTO_ALG_FLAG_SUPPORTED 0x01 /* Algorithm is supported */
+#define CRYPTO_ALG_FLAG_RNG_ENABLE 0x02 /* Has HW RNG for DH/DSA */
+#define CRYPTO_ALG_FLAG_DSA_SHA 0x04 /* Can do SHA on msg */
+
+/*
+ * Crypto driver/device flags. They can set in the crid
+ * parameter when creating a session or submitting a key
+ * op to affect the device/driver assigned. If neither
+ * of these are specified then the crid is assumed to hold
+ * the driver id of an existing (and suitable) device that
+ * must be used to satisfy the request.
+ */
+#define CRYPTO_FLAG_HARDWARE 0x01000000 /* hardware accelerated */
+#define CRYPTO_FLAG_SOFTWARE 0x02000000 /* software implementation */
+
+/* NB: deprecated */
+struct session_op {
+ u_int32_t cipher; /* ie. CRYPTO_DES_CBC */
+ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */
+
+ u_int32_t keylen; /* cipher key */
+ caddr_t key;
+ int mackeylen; /* mac key */
+ caddr_t mackey;
+
+ u_int32_t ses; /* returns: session # */
+};
+
+struct session2_op {
+ u_int32_t cipher; /* ie. CRYPTO_DES_CBC */
+ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */
+
+ u_int32_t keylen; /* cipher key */
+ caddr_t key;
+ int mackeylen; /* mac key */
+ caddr_t mackey;
+
+ u_int32_t ses; /* returns: session # */
+ int crid; /* driver id + flags (rw) */
+ int pad[4]; /* for future expansion */
+};
+
+struct crypt_op {
+ u_int32_t ses;
+ u_int16_t op; /* i.e. COP_ENCRYPT */
+#define COP_NONE 0
+#define COP_ENCRYPT 1
+#define COP_DECRYPT 2
+ u_int16_t flags;
+#define COP_F_BATCH 0x0008 /* Batch op if possible */
+ u_int len;
+ caddr_t src, dst; /* become iov[] inside kernel */
+ caddr_t mac; /* must be big enough for chosen MAC */
+ caddr_t iv;
+};
+
+/*
+ * Parameters for looking up a crypto driver/device by
+ * device name or by id. The latter are returned for
+ * created sessions (crid) and completed key operations.
+ */
+struct crypt_find_op {
+ int crid; /* driver id + flags */
+ char name[32]; /* device/driver name */
+};
+
+/* bignum parameter, in packed bytes, ... */
+struct crparam {
+ caddr_t crp_p;
+ u_int crp_nbits;
+};
+
+#define CRK_MAXPARAM 8
+
+struct crypt_kop {
+ u_int crk_op; /* ie. CRK_MOD_EXP or other */
+ u_int crk_status; /* return status */
+ u_short crk_iparams; /* # of input parameters */
+ u_short crk_oparams; /* # of output parameters */
+ u_int crk_crid; /* NB: only used by CIOCKEY2 (rw) */
+ struct crparam crk_param[CRK_MAXPARAM];
+};
+#define CRK_ALGORITM_MIN 0
+#define CRK_MOD_EXP 0
+#define CRK_MOD_EXP_CRT 1
+#define CRK_DSA_SIGN 2
+#define CRK_DSA_VERIFY 3
+#define CRK_DH_COMPUTE_KEY 4
+#define CRK_ALGORITHM_MAX 4 /* Keep updated - see below */
+
+#define CRF_MOD_EXP (1 << CRK_MOD_EXP)
+#define CRF_MOD_EXP_CRT (1 << CRK_MOD_EXP_CRT)
+#define CRF_DSA_SIGN (1 << CRK_DSA_SIGN)
+#define CRF_DSA_VERIFY (1 << CRK_DSA_VERIFY)
+#define CRF_DH_COMPUTE_KEY (1 << CRK_DH_COMPUTE_KEY)
+
+/*
+ * done against open of /dev/crypto, to get a cloned descriptor.
+ * Please use F_SETFD against the cloned descriptor.
+ */
+#define CRIOGET _IOWR('c', 100, u_int32_t)
+#define CRIOASYMFEAT CIOCASYMFEAT
+#define CRIOFINDDEV CIOCFINDDEV
+
+/* the following are done against the cloned descriptor */
+#define CIOCGSESSION _IOWR('c', 101, struct session_op)
+#define CIOCFSESSION _IOW('c', 102, u_int32_t)
+#define CIOCCRYPT _IOWR('c', 103, struct crypt_op)
+#define CIOCKEY _IOWR('c', 104, struct crypt_kop)
+#define CIOCASYMFEAT _IOR('c', 105, u_int32_t)
+#define CIOCGSESSION2 _IOWR('c', 106, struct session2_op)
+#define CIOCKEY2 _IOWR('c', 107, struct crypt_kop)
+#define CIOCFINDDEV _IOWR('c', 108, struct crypt_find_op)
+
+struct cryptotstat {
+ struct timespec acc; /* total accumulated time */
+ struct timespec min; /* min time */
+ struct timespec max; /* max time */
+ u_int32_t count; /* number of observations */
+};
+
+struct cryptostats {
+ u_int32_t cs_ops; /* symmetric crypto ops submitted */
+ u_int32_t cs_errs; /* symmetric crypto ops that failed */
+ u_int32_t cs_kops; /* asymetric/key ops submitted */
+ u_int32_t cs_kerrs; /* asymetric/key ops that failed */
+ u_int32_t cs_intrs; /* crypto swi thread activations */
+ u_int32_t cs_rets; /* crypto return thread activations */
+ u_int32_t cs_blocks; /* symmetric op driver block */
+ u_int32_t cs_kblocks; /* symmetric op driver block */
+ /*
+ * When CRYPTO_TIMING is defined at compile time and the
+ * sysctl debug.crypto is set to 1, the crypto system will
+ * accumulate statistics about how long it takes to process
+ * crypto requests at various points during processing.
+ */
+ struct cryptotstat cs_invoke; /* crypto_dipsatch -> crypto_invoke */
+ struct cryptotstat cs_done; /* crypto_invoke -> crypto_done */
+ struct cryptotstat cs_cb; /* crypto_done -> callback */
+ struct cryptotstat cs_finis; /* callback -> callback return */
+
+ u_int32_t cs_drops; /* crypto ops dropped due to congestion */
+};
+
+#ifdef __KERNEL__
+
+/* Standard initialization structure beginning */
+struct cryptoini {
+ int cri_alg; /* Algorithm to use */
+ int cri_klen; /* Key length, in bits */
+ int cri_mlen; /* Number of bytes we want from the
+ entire hash. 0 means all. */
+ caddr_t cri_key; /* key to use */
+ u_int8_t cri_iv[EALG_MAX_BLOCK_LEN]; /* IV to use */
+ struct cryptoini *cri_next;
+};
+
+/* Describe boundaries of a single crypto operation */
+struct cryptodesc {
+ int crd_skip; /* How many bytes to ignore from start */
+ int crd_len; /* How many bytes to process */
+ int crd_inject; /* Where to inject results, if applicable */
+ int crd_flags;
+
+#define CRD_F_ENCRYPT 0x01 /* Set when doing encryption */
+#define CRD_F_IV_PRESENT 0x02 /* When encrypting, IV is already in
+ place, so don't copy. */
+#define CRD_F_IV_EXPLICIT 0x04 /* IV explicitly provided */
+#define CRD_F_DSA_SHA_NEEDED 0x08 /* Compute SHA-1 of buffer for DSA */
+#define CRD_F_KEY_EXPLICIT 0x10 /* Key explicitly provided */
+#define CRD_F_COMP 0x0f /* Set when doing compression */
+
+ struct cryptoini CRD_INI; /* Initialization/context data */
+#define crd_iv CRD_INI.cri_iv
+#define crd_key CRD_INI.cri_key
+#define crd_alg CRD_INI.cri_alg
+#define crd_klen CRD_INI.cri_klen
+
+ struct cryptodesc *crd_next;
+};
+
+/* Structure describing complete operation */
+struct cryptop {
+ struct list_head crp_next;
+ wait_queue_head_t crp_waitq;
+
+ u_int64_t crp_sid; /* Session ID */
+ int crp_ilen; /* Input data total length */
+ int crp_olen; /* Result total length */
+
+ int crp_etype; /*
+ * Error type (zero means no error).
+ * All error codes except EAGAIN
+ * indicate possible data corruption (as in,
+ * the data have been touched). On all
+ * errors, the crp_sid may have changed
+ * (reset to a new one), so the caller
+ * should always check and use the new
+ * value on future requests.
+ */
+ int crp_flags;
+
+#define CRYPTO_F_SKBUF 0x0001 /* Input/output are skbuf chains */
+#define CRYPTO_F_IOV 0x0002 /* Input/output are uio */
+#define CRYPTO_F_REL 0x0004 /* Must return data in same place */
+#define CRYPTO_F_BATCH 0x0008 /* Batch op if possible */
+#define CRYPTO_F_CBIMM 0x0010 /* Do callback immediately */
+#define CRYPTO_F_DONE 0x0020 /* Operation completed */
+#define CRYPTO_F_CBIFSYNC 0x0040 /* Do CBIMM if op is synchronous */
+
+ caddr_t crp_buf; /* Data to be processed */
+ caddr_t crp_opaque; /* Opaque pointer, passed along */
+ struct cryptodesc *crp_desc; /* Linked list of processing descriptors */
+
+ int (*crp_callback)(struct cryptop *); /* Callback function */
+};
+
+#define CRYPTO_BUF_CONTIG 0x0
+#define CRYPTO_BUF_IOV 0x1
+#define CRYPTO_BUF_SKBUF 0x2
+
+#define CRYPTO_OP_DECRYPT 0x0
+#define CRYPTO_OP_ENCRYPT 0x1
+
+/*
+ * Hints passed to process methods.
+ */
+#define CRYPTO_HINT_MORE 0x1 /* more ops coming shortly */
+
+struct cryptkop {
+ struct list_head krp_next;
+ wait_queue_head_t krp_waitq;
+
+ int krp_flags;
+#define CRYPTO_KF_DONE 0x0001 /* Operation completed */
+#define CRYPTO_KF_CBIMM 0x0002 /* Do callback immediately */
+
+ u_int krp_op; /* ie. CRK_MOD_EXP or other */
+ u_int krp_status; /* return status */
+ u_short krp_iparams; /* # of input parameters */
+ u_short krp_oparams; /* # of output parameters */
+ u_int krp_crid; /* desired device, etc. */
+ u_int32_t krp_hid;
+ struct crparam krp_param[CRK_MAXPARAM]; /* kvm */
+ int (*krp_callback)(struct cryptkop *);
+};
+
+#include <ocf-compat.h>
+
+/*
+ * Session ids are 64 bits. The lower 32 bits contain a "local id" which
+ * is a driver-private session identifier. The upper 32 bits contain a
+ * "hardware id" used by the core crypto code to identify the driver and
+ * a copy of the driver's capabilities that can be used by client code to
+ * optimize operation.
+ */
+#define CRYPTO_SESID2HID(_sid) (((_sid) >> 32) & 0x00ffffff)
+#define CRYPTO_SESID2CAPS(_sid) (((_sid) >> 32) & 0xff000000)
+#define CRYPTO_SESID2LID(_sid) (((u_int32_t) (_sid)) & 0xffffffff)
+
+extern int crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard);
+extern int crypto_freesession(u_int64_t sid);
+#define CRYPTOCAP_F_HARDWARE CRYPTO_FLAG_HARDWARE
+#define CRYPTOCAP_F_SOFTWARE CRYPTO_FLAG_SOFTWARE
+#define CRYPTOCAP_F_SYNC 0x04000000 /* operates synchronously */
+extern int32_t crypto_get_driverid(device_t dev, int flags);
+extern int crypto_find_driver(const char *);
+extern device_t crypto_find_device_byhid(int hid);
+extern int crypto_getcaps(int hid);
+extern int crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen,
+ u_int32_t flags);
+extern int crypto_kregister(u_int32_t, int, u_int32_t);
+extern int crypto_unregister(u_int32_t driverid, int alg);
+extern int crypto_unregister_all(u_int32_t driverid);
+extern int crypto_dispatch(struct cryptop *crp);
+extern int crypto_kdispatch(struct cryptkop *);
+#define CRYPTO_SYMQ 0x1
+#define CRYPTO_ASYMQ 0x2
+extern int crypto_unblock(u_int32_t, int);
+extern void crypto_done(struct cryptop *crp);
+extern void crypto_kdone(struct cryptkop *);
+extern int crypto_getfeat(int *);
+
+extern void crypto_freereq(struct cryptop *crp);
+extern struct cryptop *crypto_getreq(int num);
+
+extern int crypto_usercrypto; /* userland may do crypto requests */
+extern int crypto_userasymcrypto; /* userland may do asym crypto reqs */
+extern int crypto_devallowsoft; /* only use hardware crypto */
+
+/*
+ * random number support, crypto_unregister_all will unregister
+ */
+extern int crypto_rregister(u_int32_t driverid,
+ int (*read_random)(void *arg, u_int32_t *buf, int len), void *arg);
+extern int crypto_runregister_all(u_int32_t driverid);
+
+/*
+ * Crypto-related utility routines used mainly by drivers.
+ *
+ * XXX these don't really belong here; but for now they're
+ * kept apart from the rest of the system.
+ */
+struct uio;
+extern void cuio_copydata(struct uio* uio, int off, int len, caddr_t cp);
+extern void cuio_copyback(struct uio* uio, int off, int len, caddr_t cp);
+extern struct iovec *cuio_getptr(struct uio *uio, int loc, int *off);
+
+extern void crypto_copyback(int flags, caddr_t buf, int off, int size,
+ caddr_t in);
+extern void crypto_copydata(int flags, caddr_t buf, int off, int size,
+ caddr_t out);
+extern int crypto_apply(int flags, caddr_t buf, int off, int len,
+ int (*f)(void *, void *, u_int), void *arg);
+
+#endif /* __KERNEL__ */
+#endif /* _CRYPTO_CRYPTO_H_ */
diff --git a/crypto/ocf/cryptosoft.c b/crypto/ocf/cryptosoft.c
new file mode 100644
index 000000000000..4ac89cac5b5d
--- /dev/null
+++ b/crypto/ocf/cryptosoft.c
@@ -0,0 +1,1075 @@
+/*
+ * An OCF module that uses the linux kernel cryptoapi, based on the
+ * original cryptosoft for BSD by Angelos D. Keromytis (angelos@cis.upenn.edu)
+ * but is mostly unrecognisable,
+ *
+ * Written by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2004-2007 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ * ---------------------------------------------------------------------------
+ */
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/crypto.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+#include <linux/random.h>
+#include <linux/completion.h>
+#include <asm/scatterlist.h>
+
+#include <cryptodev.h>
+#include <uio.h>
+
+struct {
+ softc_device_decl sc_dev;
+} swcr_softc;
+
+#define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK)
+
+/* Software session entry */
+
+#define SW_TYPE_CIPHER 0
+#define SW_TYPE_HMAC 1
+#define SW_TYPE_AUTH2 2
+#define SW_TYPE_HASH 3
+#define SW_TYPE_COMP 4
+#define SW_TYPE_BLKCIPHER 5
+#define SW_TYPE_ABLKCIPHER 6
+
+struct swcr_data {
+ int sw_type;
+ int sw_alg;
+ struct crypto_tfm *sw_tfm;
+ union {
+ struct {
+ char *sw_key;
+ int sw_klen;
+ int sw_mlen;
+ } hmac;
+ void *sw_comp_buf;
+ } u;
+ struct swcr_data *sw_next;
+};
+
+#ifndef CRYPTO_TFM_MODE_CBC
+/*
+ * As of linux-2.6.21 this is no longer defined, and presumably no longer
+ * needed to be passed into the crypto core code.
+ */
+#define CRYPTO_TFM_MODE_CBC 0
+#define CRYPTO_TFM_MODE_ECB 0
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ /*
+ * Linux 2.6.19 introduced a new Crypto API, setup macro's to convert new
+ * API into old API.
+ */
+
+ /* Symmetric/Block Cipher */
+ struct blkcipher_desc
+ {
+ struct crypto_tfm *tfm;
+ void *info;
+ };
+ #define ecb(X) #X
+ #define cbc(X) #X
+ #define crypto_has_blkcipher(X, Y, Z) crypto_alg_available(X, 0)
+ #define crypto_blkcipher_cast(X) X
+ #define crypto_blkcipher_tfm(X) X
+ #define crypto_alloc_blkcipher(X, Y, Z) crypto_alloc_tfm(X, mode)
+ #define crypto_blkcipher_ivsize(X) crypto_tfm_alg_ivsize(X)
+ #define crypto_blkcipher_blocksize(X) crypto_tfm_alg_blocksize(X)
+ #define crypto_blkcipher_setkey(X, Y, Z) crypto_cipher_setkey(X, Y, Z)
+ #define crypto_blkcipher_encrypt_iv(W, X, Y, Z) \
+ crypto_cipher_encrypt_iv((W)->tfm, X, Y, Z, (u8 *)((W)->info))
+ #define crypto_blkcipher_decrypt_iv(W, X, Y, Z) \
+ crypto_cipher_decrypt_iv((W)->tfm, X, Y, Z, (u8 *)((W)->info))
+
+ /* Hash/HMAC/Digest */
+ struct hash_desc
+ {
+ struct crypto_tfm *tfm;
+ };
+ #define hmac(X) #X
+ #define crypto_has_hash(X, Y, Z) crypto_alg_available(X, 0)
+ #define crypto_hash_cast(X) X
+ #define crypto_hash_tfm(X) X
+ #define crypto_alloc_hash(X, Y, Z) crypto_alloc_tfm(X, mode)
+ #define crypto_hash_digestsize(X) crypto_tfm_alg_digestsize(X)
+ #define crypto_hash_digest(W, X, Y, Z) \
+ crypto_digest_digest((W)->tfm, X, sg_num, Z)
+
+ /* Asymmetric Cipher */
+ #define crypto_has_cipher(X, Y, Z) crypto_alg_available(X, 0)
+
+ /* Compression */
+ #define crypto_has_comp(X, Y, Z) crypto_alg_available(X, 0)
+ #define crypto_comp_tfm(X) X
+ #define crypto_comp_cast(X) X
+ #define crypto_alloc_comp(X, Y, Z) crypto_alloc_tfm(X, mode)
+#else
+ #define ecb(X) "ecb(" #X ")"
+ #define cbc(X) "cbc(" #X ")"
+ #define hmac(X) "hmac(" #X ")"
+#endif /* if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+
+struct crypto_details
+{
+ char *alg_name;
+ int mode;
+ int sw_type;
+};
+
+struct ablkcipher_result {
+ struct completion completion;
+ int err;
+};
+
+/*
+ * This needs to be kept updated with CRYPTO_xxx list (cryptodev.h).
+ * If the Algorithm is not supported, then insert a {NULL, 0, 0} entry.
+ *
+ * IMPORTANT: The index to the array IS CRYPTO_xxx.
+ */
+static struct crypto_details crypto_details[CRYPTO_ALGORITHM_MAX + 1] = {
+ { NULL, 0, 0 },
+ /* CRYPTO_xxx index starts at 1 */
+ { cbc(des), CRYPTO_TFM_MODE_CBC, SW_TYPE_BLKCIPHER },
+ { cbc(des3_ede), CRYPTO_TFM_MODE_CBC, SW_TYPE_BLKCIPHER },
+ { cbc(blowfish), CRYPTO_TFM_MODE_CBC, SW_TYPE_BLKCIPHER },
+ { cbc(cast5), CRYPTO_TFM_MODE_CBC, SW_TYPE_BLKCIPHER },
+ { cbc(skipjack), CRYPTO_TFM_MODE_CBC, SW_TYPE_BLKCIPHER },
+ { hmac(md5), 0, SW_TYPE_HMAC },
+ { hmac(sha1), 0, SW_TYPE_HMAC },
+ { hmac(ripemd160), 0, SW_TYPE_HMAC },
+ { "md5-kpdk??", 0, SW_TYPE_HASH },
+ { "sha1-kpdk??", 0, SW_TYPE_HASH },
+// { cbc(aes), CRYPTO_TFM_MODE_CBC, SW_TYPE_BLKCIPHER },
+ { cbc(aes), CRYPTO_TFM_MODE_CBC, SW_TYPE_ABLKCIPHER },
+ { ecb(arc4), CRYPTO_TFM_MODE_ECB, SW_TYPE_BLKCIPHER },
+ { "md5", 0, SW_TYPE_HASH },
+ { "sha1", 0, SW_TYPE_HASH },
+ { hmac(digest_null), 0, SW_TYPE_HMAC },
+ { cbc(cipher_null), CRYPTO_TFM_MODE_CBC, SW_TYPE_BLKCIPHER },
+ { "deflate", 0, SW_TYPE_COMP },
+ { hmac(sha256), 0, SW_TYPE_HMAC },
+ { hmac(sha384), 0, SW_TYPE_HMAC },
+ { hmac(sha512), 0, SW_TYPE_HMAC },
+ { cbc(camellia), CRYPTO_TFM_MODE_CBC, SW_TYPE_BLKCIPHER },
+ { "sha256", 0, SW_TYPE_HASH },
+ { "sha384", 0, SW_TYPE_HASH },
+ { "sha512", 0, SW_TYPE_HASH },
+ { "ripemd160", 0, SW_TYPE_HASH },
+};
+
+int32_t swcr_id = -1;
+module_param(swcr_id, int, 0444);
+MODULE_PARM_DESC(swcr_id, "Read-Only OCF ID for cryptosoft driver");
+
+int swcr_fail_if_compression_grows = 1;
+module_param(swcr_fail_if_compression_grows, int, 0644);
+MODULE_PARM_DESC(swcr_fail_if_compression_grows,
+ "Treat compression that results in more data as a failure");
+
+static struct swcr_data **swcr_sessions = NULL;
+static u_int32_t swcr_sesnum = 0;
+
+static int swcr_process(device_t, struct cryptop *, int);
+static int swcr_newsession(device_t, u_int32_t *, struct cryptoini *);
+static int swcr_freesession(device_t, u_int64_t);
+static void ablkcipher_complete(struct crypto_async_request *req, int err);
+
+static device_method_t swcr_methods = {
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, swcr_newsession),
+ DEVMETHOD(cryptodev_freesession,swcr_freesession),
+ DEVMETHOD(cryptodev_process, swcr_process),
+};
+
+#define debug swcr_debug
+int swcr_debug = 0;
+module_param(swcr_debug, int, 0644);
+MODULE_PARM_DESC(swcr_debug, "Enable debug");
+
+/*
+ * Completion function for ablkciphers
+ */
+static void ablkcipher_complete(struct crypto_async_request *req, int err)
+{
+ struct ablkcipher_result *res = req->data;
+
+ if (err == -EINPROGRESS)
+ return;
+
+ printk("%s(): Calling req->completion\n", __FUNCTION__);
+ res->err = err;
+ complete(&res->completion);
+}
+
+/*
+ * Generate a new software session.
+ */
+static int
+swcr_newsession(device_t dev, u_int32_t *sid, struct cryptoini *cri)
+{
+ struct swcr_data **swd;
+ u_int32_t i;
+ int error;
+ char *algo;
+ int mode, sw_type;
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (sid == NULL || cri == NULL) {
+ dprintk("%s,%d - EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ if (swcr_sessions) {
+ for (i = 1; i < swcr_sesnum; i++)
+ if (swcr_sessions[i] == NULL)
+ break;
+ } else
+ i = 1; /* NB: to silence compiler warning */
+
+ if (swcr_sessions == NULL || i == swcr_sesnum) {
+ if (swcr_sessions == NULL) {
+ i = 1; /* We leave swcr_sessions[0] empty */
+ swcr_sesnum = CRYPTO_SW_SESSIONS;
+ } else
+ swcr_sesnum *= 2;
+
+ swd = kmalloc(swcr_sesnum * sizeof(struct swcr_data *), SLAB_ATOMIC);
+ if (swd == NULL) {
+ /* Reset session number */
+ if (swcr_sesnum == CRYPTO_SW_SESSIONS)
+ swcr_sesnum = 0;
+ else
+ swcr_sesnum /= 2;
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+ memset(swd, 0, swcr_sesnum * sizeof(struct swcr_data *));
+
+ /* Copy existing sessions */
+ if (swcr_sessions) {
+ memcpy(swd, swcr_sessions,
+ (swcr_sesnum / 2) * sizeof(struct swcr_data *));
+ kfree(swcr_sessions);
+ }
+
+ swcr_sessions = swd;
+ }
+
+ swd = &swcr_sessions[i];
+ *sid = i;
+
+ while (cri) {
+ *swd = (struct swcr_data *) kmalloc(sizeof(struct swcr_data),
+ SLAB_ATOMIC);
+ if (*swd == NULL) {
+ swcr_freesession(NULL, i);
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+ memset(*swd, 0, sizeof(struct swcr_data));
+
+ if (cri->cri_alg > CRYPTO_ALGORITHM_MAX) {
+ printk("cryptosoft: Unknown algorithm 0x%x\n", cri->cri_alg);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+
+ algo = crypto_details[cri->cri_alg].alg_name;
+ if (!algo || !*algo) {
+ printk("cryptosoft: Unsupported algorithm 0x%x\n", cri->cri_alg);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+
+ mode = crypto_details[cri->cri_alg].mode;
+ sw_type = crypto_details[cri->cri_alg].sw_type;
+
+ /* Algorithm specific configuration */
+ switch (cri->cri_alg) {
+ case CRYPTO_NULL_CBC:
+ cri->cri_klen = 0; /* make it work with crypto API */
+ break;
+ default:
+ break;
+ }
+
+ if (sw_type == SW_TYPE_BLKCIPHER) {
+ dprintk("%s crypto_alloc_blkcipher(%s, 0x%x)\n", __FUNCTION__,
+ algo, mode);
+
+ (*swd)->sw_tfm = crypto_blkcipher_tfm(
+ crypto_alloc_blkcipher(algo, 0,
+ CRYPTO_ALG_ASYNC));
+ if (!(*swd)->sw_tfm) {
+ dprintk("cryptosoft: crypto_alloc_blkcipher failed(%s,0x%x)\n",
+ algo,mode);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+
+ if (debug) {
+ dprintk("%s key:cri->cri_klen=%d,(cri->cri_klen + 7)/8=%d",
+ __FUNCTION__,cri->cri_klen,(cri->cri_klen + 7)/8);
+ for (i = 0; i < (cri->cri_klen + 7) / 8; i++)
+ {
+ dprintk("%s0x%x", (i % 8) ? " " : "\n ",cri->cri_key[i]);
+ }
+ dprintk("\n");
+ }
+ error = crypto_blkcipher_setkey(
+ crypto_blkcipher_cast((*swd)->sw_tfm), cri->cri_key,
+ (cri->cri_klen + 7) / 8);
+ if (error) {
+ printk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n", error,
+ (*swd)->sw_tfm->crt_flags);
+ swcr_freesession(NULL, i);
+ return error;
+ }
+ } else if (sw_type == SW_TYPE_ABLKCIPHER) {
+ dprintk("%s crypto_alloc_ablkcipher(%s, 0x%x)\n", __FUNCTION__,
+ algo, mode);
+
+ (*swd)->sw_tfm = crypto_ablkcipher_tfm(
+ crypto_alloc_ablkcipher(algo, 0,
+ 0));
+ if (!(*swd)->sw_tfm) {
+ dprintk("cryptosoft: crypto_alloc_ablkcipher failed(%s,0x%x)\n",
+ algo,mode);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+
+ if (debug) {
+ dprintk("%s key:cri->cri_klen=%d,(cri->cri_klen + 7)/8=%d",
+ __FUNCTION__,cri->cri_klen,(cri->cri_klen + 7)/8);
+ for (i = 0; i < (cri->cri_klen + 7) / 8; i++)
+ {
+ dprintk("%s0x%x", (i % 8) ? " " : "\n ",cri->cri_key[i]);
+ }
+ dprintk("\n");
+ }
+ error = crypto_ablkcipher_setkey(
+ __crypto_ablkcipher_cast((*swd)->sw_tfm), cri->cri_key,
+ (cri->cri_klen + 7) / 8);
+ if (error) {
+ printk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n", error,
+ (*swd)->sw_tfm->crt_flags);
+ swcr_freesession(NULL, i);
+ return error;
+ }
+ } else if (sw_type == SW_TYPE_HMAC || sw_type == SW_TYPE_HASH) {
+ dprintk("%s crypto_alloc_hash(%s, 0x%x)\n", __FUNCTION__,
+ algo, mode);
+
+ (*swd)->sw_tfm = crypto_hash_tfm(
+ crypto_alloc_hash(algo, 0, CRYPTO_ALG_ASYNC));
+
+ if (!(*swd)->sw_tfm) {
+ dprintk("cryptosoft: crypto_alloc_hash failed(%s,0x%x)\n",
+ algo, mode);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+
+ (*swd)->u.hmac.sw_klen = (cri->cri_klen + 7) / 8;
+ (*swd)->u.hmac.sw_key = (char *)kmalloc((*swd)->u.hmac.sw_klen,
+ SLAB_ATOMIC);
+ if ((*swd)->u.hmac.sw_key == NULL) {
+ swcr_freesession(NULL, i);
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+ memcpy((*swd)->u.hmac.sw_key, cri->cri_key, (*swd)->u.hmac.sw_klen);
+ if (cri->cri_mlen) {
+ (*swd)->u.hmac.sw_mlen = cri->cri_mlen;
+ } else {
+ (*swd)->u.hmac.sw_mlen =
+ crypto_hash_digestsize(
+ crypto_hash_cast((*swd)->sw_tfm));
+ }
+ } else if (sw_type == SW_TYPE_COMP) {
+ (*swd)->sw_tfm = crypto_comp_tfm(
+ crypto_alloc_comp(algo, 0, CRYPTO_ALG_ASYNC));
+ if (!(*swd)->sw_tfm) {
+ dprintk("cryptosoft: crypto_alloc_comp failed(%s,0x%x)\n",
+ algo, mode);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+ (*swd)->u.sw_comp_buf = kmalloc(CRYPTO_MAX_DATA_LEN, SLAB_ATOMIC);
+ if ((*swd)->u.sw_comp_buf == NULL) {
+ swcr_freesession(NULL, i);
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+ } else {
+ printk("cryptosoft: Unhandled sw_type %d\n", sw_type);
+ swcr_freesession(NULL, i);
+ return EINVAL;
+ }
+
+ (*swd)->sw_alg = cri->cri_alg;
+ (*swd)->sw_type = sw_type;
+
+ cri = cri->cri_next;
+ swd = &((*swd)->sw_next);
+ }
+ return 0;
+}
+
+/*
+ * Free a session.
+ */
+static int
+swcr_freesession(device_t dev, u_int64_t tid)
+{
+ struct swcr_data *swd;
+ u_int32_t sid = CRYPTO_SESID2LID(tid);
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (sid > swcr_sesnum || swcr_sessions == NULL ||
+ swcr_sessions[sid] == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ return(EINVAL);
+ }
+
+ /* Silently accept and return */
+ if (sid == 0)
+ return(0);
+
+ while ((swd = swcr_sessions[sid]) != NULL) {
+ swcr_sessions[sid] = swd->sw_next;
+ if (swd->sw_tfm)
+ crypto_free_tfm(swd->sw_tfm);
+ if (swd->sw_type == SW_TYPE_COMP) {
+ if (swd->u.sw_comp_buf)
+ kfree(swd->u.sw_comp_buf);
+ } else {
+ if (swd->u.hmac.sw_key)
+ kfree(swd->u.hmac.sw_key);
+ }
+ kfree(swd);
+ }
+ return 0;
+}
+
+/*
+ * Process a software request.
+ */
+static int
+swcr_process(device_t dev, struct cryptop *crp, int hint)
+{
+ struct cryptodesc *crd;
+ struct swcr_data *sw;
+ u_int32_t lid;
+#define SCATTERLIST_MAX 16
+ struct scatterlist sg[SCATTERLIST_MAX];
+ int sg_num, sg_len, skip;
+ struct sk_buff *skb = NULL;
+ struct uio *uiop = NULL;
+
+ dprintk("%s()\n", __FUNCTION__);
+ /* Sanity check */
+ if (crp == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ crp->crp_etype = 0;
+
+ if (crp->crp_desc == NULL || crp->crp_buf == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ crp->crp_etype = EINVAL;
+ goto done;
+ }
+
+ lid = crp->crp_sid & 0xffffffff;
+ if (lid >= swcr_sesnum || lid == 0 || swcr_sessions == NULL ||
+ swcr_sessions[lid] == NULL) {
+ crp->crp_etype = ENOENT;
+ dprintk("%s,%d: ENOENT\n", __FILE__, __LINE__);
+ goto done;
+ }
+
+ /*
+ * do some error checking outside of the loop for SKB and IOV processing
+ * this leaves us with valid skb or uiop pointers for later
+ */
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ skb = (struct sk_buff *) crp->crp_buf;
+ if (skb_shinfo(skb)->nr_frags >= SCATTERLIST_MAX) {
+ printk("%s,%d: %d nr_frags > SCATTERLIST_MAX", __FILE__, __LINE__,
+ skb_shinfo(skb)->nr_frags);
+ goto done;
+ }
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ uiop = (struct uio *) crp->crp_buf;
+ if (uiop->uio_iovcnt > SCATTERLIST_MAX) {
+ printk("%s,%d: %d uio_iovcnt > SCATTERLIST_MAX", __FILE__, __LINE__,
+ uiop->uio_iovcnt);
+ goto done;
+ }
+ }
+
+ /* Go through crypto descriptors, processing as we go */
+ for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
+ /*
+ * Find the crypto context.
+ *
+ * XXX Note that the logic here prevents us from having
+ * XXX the same algorithm multiple times in a session
+ * XXX (or rather, we can but it won't give us the right
+ * XXX results). To do that, we'd need some way of differentiating
+ * XXX between the various instances of an algorithm (so we can
+ * XXX locate the correct crypto context).
+ */
+ for (sw = swcr_sessions[lid]; sw && sw->sw_alg != crd->crd_alg;
+ sw = sw->sw_next)
+ ;
+
+ /* No such context ? */
+ if (sw == NULL) {
+ crp->crp_etype = EINVAL;
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ goto done;
+ }
+
+ skip = crd->crd_skip;
+
+ /*
+ * setup the SG list skip from the start of the buffer
+ */
+ memset(sg, 0, sizeof(sg));
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ int i, len;
+
+ sg_num = 0;
+ sg_len = 0;
+
+ if (skip < skb_headlen(skb)) {
+ len = skb_headlen(skb) - skip;
+ if (len + sg_len > crd->crd_len)
+ len = crd->crd_len - sg_len;
+ sg_set_page(&sg[sg_num],
+ virt_to_page(skb->data + skip), len,
+ offset_in_page(skb->data + skip));
+ sg_len += len;
+ sg_num++;
+ skip = 0;
+ } else
+ skip -= skb_headlen(skb);
+
+ for (i = 0; sg_len < crd->crd_len &&
+ i < skb_shinfo(skb)->nr_frags &&
+ sg_num < SCATTERLIST_MAX; i++) {
+ if (skip < skb_shinfo(skb)->frags[i].size) {
+ len = skb_shinfo(skb)->frags[i].size - skip;
+ if (len + sg_len > crd->crd_len)
+ len = crd->crd_len - sg_len;
+ sg_set_page(&sg[sg_num],
+ skb_shinfo(skb)->frags[i].page,
+ len,
+ skb_shinfo(skb)->frags[i].page_offset + skip);
+ sg_len += len;
+ sg_num++;
+ skip = 0;
+ } else
+ skip -= skb_shinfo(skb)->frags[i].size;
+ }
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ int len;
+
+ sg_len = 0;
+ for (sg_num = 0; sg_len <= crd->crd_len &&
+ sg_num < uiop->uio_iovcnt &&
+ sg_num < SCATTERLIST_MAX; sg_num++) {
+ if (skip <= uiop->uio_iov[sg_num].iov_len) {
+ len = uiop->uio_iov[sg_num].iov_len - skip;
+ if (len + sg_len > crd->crd_len)
+ len = crd->crd_len - sg_len;
+ sg_set_page(&sg[sg_num],
+ virt_to_page(uiop->uio_iov[sg_num].iov_base+skip),
+ len,
+ offset_in_page(uiop->uio_iov[sg_num].iov_base+skip));
+ sg_len += len;
+ skip = 0;
+ } else
+ skip -= uiop->uio_iov[sg_num].iov_len;
+ }
+ } else {
+ sg_len = (crp->crp_ilen - skip);
+ if (sg_len > crd->crd_len)
+ sg_len = crd->crd_len;
+ sg_set_page(&sg[0], virt_to_page(crp->crp_buf + skip),
+ sg_len, offset_in_page(crp->crp_buf + skip));
+ sg_num = 1;
+ }
+
+
+ switch (sw->sw_type) {
+ case SW_TYPE_BLKCIPHER: {
+ unsigned char iv[EALG_MAX_BLOCK_LEN];
+ unsigned char *ivp = iv;
+ int ivsize =
+ crypto_blkcipher_ivsize(crypto_blkcipher_cast(sw->sw_tfm));
+ struct blkcipher_desc desc;
+
+ if (sg_len < crypto_blkcipher_blocksize(
+ crypto_blkcipher_cast(sw->sw_tfm))) {
+ crp->crp_etype = EINVAL;
+ dprintk("%s,%d: EINVAL len %d < %d\n", __FILE__, __LINE__,
+ sg_len, crypto_blkcipher_blocksize(
+ crypto_blkcipher_cast(sw->sw_tfm)));
+ goto done;
+ }
+
+ if (ivsize > sizeof(iv)) {
+ crp->crp_etype = EINVAL;
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ goto done;
+ }
+
+ if (crd->crd_flags & CRD_F_KEY_EXPLICIT) {
+ int i, error;
+
+ if (debug) {
+ dprintk("%s key:", __FUNCTION__);
+ for (i = 0; i < (crd->crd_klen + 7) / 8; i++)
+ dprintk("%s0x%x", (i % 8) ? " " : "\n ",
+ crd->crd_key[i]);
+ dprintk("\n");
+ }
+ error = crypto_blkcipher_setkey(
+ crypto_blkcipher_cast(sw->sw_tfm), crd->crd_key,
+ (crd->crd_klen + 7) / 8);
+ if (error) {
+ dprintk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n",
+ error, sw->sw_tfm->crt_flags);
+ crp->crp_etype = -error;
+ }
+ }
+
+ memset(&desc, 0, sizeof(desc));
+ desc.tfm = crypto_blkcipher_cast(sw->sw_tfm);
+
+ if (crd->crd_flags & CRD_F_ENCRYPT) { /* encrypt */
+
+ if (crd->crd_flags & CRD_F_IV_EXPLICIT) {
+ ivp = crd->crd_iv;
+ } else {
+ get_random_bytes(ivp, ivsize);
+ }
+ /*
+ * do we have to copy the IV back to the buffer ?
+ */
+ if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) {
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, ivsize, (caddr_t)ivp);
+ }
+ desc.info = ivp;
+ crypto_blkcipher_encrypt_iv(&desc, sg, sg, sg_len);
+
+ } else { /*decrypt */
+
+ if (crd->crd_flags & CRD_F_IV_EXPLICIT) {
+ ivp = crd->crd_iv;
+ } else {
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, ivsize, (caddr_t)ivp);
+ }
+ desc.info = ivp;
+ crypto_blkcipher_decrypt_iv(&desc, sg, sg, sg_len);
+ }
+ } break;
+ case SW_TYPE_ABLKCIPHER: {
+ unsigned char iv[EALG_MAX_BLOCK_LEN];
+ unsigned char *ivp = iv;
+ int ivsize =
+ crypto_ablkcipher_ivsize(__crypto_ablkcipher_cast(sw->sw_tfm));
+ struct ablkcipher_request *req;
+ struct ablkcipher_result result;
+ unsigned int ret;
+
+ init_completion(&result.completion);
+ req = ablkcipher_request_alloc( __crypto_ablkcipher_cast(sw->sw_tfm), GFP_KERNEL);
+ if (!req) {
+ dprintk("failed to allocate request\n");
+ crp->crp_etype = EFAULT;
+ goto done;
+ }
+
+ if (sg_len < crypto_ablkcipher_blocksize(
+ __crypto_ablkcipher_cast(sw->sw_tfm))) {
+ crp->crp_etype = EINVAL;
+ dprintk("%s,%d: EINVAL len %d < %d\n", __FILE__, __LINE__,
+ sg_len, crypto_ablkcipher_blocksize(
+ __crypto_ablkcipher_cast(sw->sw_tfm)));
+ goto done;
+ }
+
+ if (ivsize > sizeof(iv)) {
+ crp->crp_etype = EINVAL;
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ goto done;
+ }
+
+ if (1) { /* Flag not implemented for cryptosoft */
+ /*if (crd->crd_flags & CRD_F_KEY_EXPLICIT) {*/
+ int i, error;
+
+ if (debug) {
+ dprintk("%s key:", __FUNCTION__);
+ for (i = 0; i < (crd->crd_klen + 7) / 8; i++)
+ dprintk("%s0x%x", (i % 8) ? " " : "\n ",
+ crd->crd_key[i]);
+ dprintk("\n");
+ }
+ error = crypto_ablkcipher_setkey(
+ __crypto_ablkcipher_cast(sw->sw_tfm), crd->crd_key,
+ (crd->crd_klen + 7) / 8);
+ if (error) {
+ dprintk("cryptosoft: setkey failed %d (crt_flags=0x%x)\n",
+ error, sw->sw_tfm->crt_flags);
+ crp->crp_etype = -error;
+ }
+ }
+
+ /* Fill in ablkcipher_request struct */
+ ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ ablkcipher_complete, &result);
+
+
+ if (crd->crd_flags & CRD_F_ENCRYPT) { /* encrypt */
+
+ if (1) /* Flag not implemented for cryptosoft */
+// if (crd->crd_flags & CRD_F_IV_EXPLICIT) {
+ ivp = crd->crd_iv;
+ else
+ get_random_bytes(ivp, ivsize);
+
+// /*
+// * do we have to copy the IV back to the buffer ?
+// */
+// if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) {
+// crypto_copyback(crp->crp_flags, crp->crp_buf,
+// crd->crd_inject, ivsize, (caddr_t)ivp);
+// }
+ /* Fill in ablkcipher_request struct */
+ ablkcipher_request_set_crypt(req, sg, sg, sg_len, ivp);
+ ret = crypto_ablkcipher_encrypt(req);
+
+ } else { /*decrypt */
+ if (1) /* Flag not implemented for cryptosoft */
+// if (crd->crd_flags & CRD_F_IV_EXPLICIT) {
+ ivp = crd->crd_iv;
+ else
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, ivsize, (caddr_t)ivp);
+ /* Fill in ablkcipher_request struct */
+ ablkcipher_request_set_crypt(req, sg, sg, sg_len, ivp);
+ ret = crypto_ablkcipher_decrypt(req);
+ }
+
+ /* wait here for completion? */
+
+ switch (ret) {
+ case 0:
+ break;
+ case -EINPROGRESS:
+ case -EBUSY:
+ ret = wait_for_completion_interruptible(
+ &result.completion);
+ if (!ret && !((ret = result.err))) {
+ INIT_COMPLETION(result.completion);
+ break;
+ }
+ /* fall through */
+ default:
+ printk("process failed err=%d\n", -ret);
+ ablkcipher_request_free(req);
+ goto done;
+ }
+ ablkcipher_request_free(req);
+ } break;
+ case SW_TYPE_HMAC:
+ case SW_TYPE_HASH:
+ {
+ char result[HASH_MAX_LEN];
+ struct hash_desc desc;
+
+ /* check we have room for the result */
+ if (crp->crp_ilen - crd->crd_inject < sw->u.hmac.sw_mlen) {
+ dprintk(
+ "cryptosoft: EINVAL crp_ilen=%d, len=%d, inject=%d digestsize=%d\n",
+ crp->crp_ilen, crd->crd_skip + sg_len, crd->crd_inject,
+ sw->u.hmac.sw_mlen);
+ crp->crp_etype = EINVAL;
+ goto done;
+ }
+
+ memset(&desc, 0, sizeof(desc));
+ desc.tfm = crypto_hash_cast(sw->sw_tfm);
+
+ memset(result, 0, sizeof(result));
+
+ if (sw->sw_type == SW_TYPE_HMAC) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ crypto_hmac(sw->sw_tfm, sw->u.hmac.sw_key, &sw->u.hmac.sw_klen,
+ sg, sg_num, result);
+#else
+ crypto_hash_setkey(desc.tfm, sw->u.hmac.sw_key,
+ sw->u.hmac.sw_klen);
+ crypto_hash_digest(&desc, sg, sg_len, result);
+#endif /* #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+
+ } else { /* SW_TYPE_HASH */
+ crypto_hash_digest(&desc, sg, sg_len, result);
+ }
+
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, sw->u.hmac.sw_mlen, result);
+ }
+ break;
+
+ case SW_TYPE_COMP: {
+ void *ibuf = NULL;
+ void *obuf = sw->u.sw_comp_buf;
+ int ilen = sg_len, olen = CRYPTO_MAX_DATA_LEN;
+ int ret = 0;
+
+ /*
+ * we need to use an additional copy if there is more than one
+ * input chunk since the kernel comp routines do not handle
+ * SG yet. Otherwise we just use the input buffer as is.
+ * Rather than allocate another buffer we just split the tmp
+ * buffer we already have.
+ * Perhaps we should just use zlib directly ?
+ */
+ if (sg_num > 1) {
+ int blk;
+
+ ibuf = obuf;
+ for (blk = 0; blk < sg_num; blk++) {
+ memcpy(obuf, sg_virt(&sg[blk]),
+ sg[blk].length);
+ obuf += sg[blk].length;
+ }
+ olen -= sg_len;
+ } else
+ ibuf = sg_virt(&sg[0]);
+
+ if (crd->crd_flags & CRD_F_ENCRYPT) { /* compress */
+ ret = crypto_comp_compress(crypto_comp_cast(sw->sw_tfm),
+ ibuf, ilen, obuf, &olen);
+ if (!ret && olen > crd->crd_len) {
+ dprintk("cryptosoft: ERANGE compress %d into %d\n",
+ crd->crd_len, olen);
+ if (swcr_fail_if_compression_grows)
+ ret = ERANGE;
+ }
+ } else { /* decompress */
+ ret = crypto_comp_decompress(crypto_comp_cast(sw->sw_tfm),
+ ibuf, ilen, obuf, &olen);
+ if (!ret && (olen + crd->crd_inject) > crp->crp_olen) {
+ dprintk("cryptosoft: ETOOSMALL decompress %d into %d, "
+ "space for %d,at offset %d\n",
+ crd->crd_len, olen, crp->crp_olen, crd->crd_inject);
+ ret = ETOOSMALL;
+ }
+ }
+ if (ret)
+ dprintk("%s,%d: ret = %d\n", __FILE__, __LINE__, ret);
+
+ /*
+ * on success copy result back,
+ * linux crpyto API returns -errno, we need to fix that
+ */
+ crp->crp_etype = ret < 0 ? -ret : ret;
+ if (ret == 0) {
+ /* copy back the result and return it's size */
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, olen, obuf);
+ crp->crp_olen = olen;
+ }
+
+
+ } break;
+
+ default:
+ /* Unknown/unsupported algorithm */
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ crp->crp_etype = EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ crypto_done(crp);
+ return 0;
+}
+
+static int
+cryptosoft_init(void)
+{
+ int i, sw_type, mode;
+ char *algo;
+
+ dprintk("%s(%p)\n", __FUNCTION__, cryptosoft_init);
+
+ softc_device_init(&swcr_softc, "cryptosoft", 0, swcr_methods);
+
+ swcr_id = crypto_get_driverid(softc_get_device(&swcr_softc),
+ CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC);
+ if (swcr_id < 0) {
+ printk("Software crypto device cannot initialize!");
+ return -ENODEV;
+ }
+
+#define REGISTER(alg) \
+ crypto_register(swcr_id, alg, 0,0);
+
+ for (i = CRYPTO_ALGORITHM_MIN; i <= CRYPTO_ALGORITHM_MAX; ++i)
+ {
+
+ algo = crypto_details[i].alg_name;
+ if (!algo || !*algo)
+ {
+ dprintk("%s:Algorithm %d not supported\n", __FUNCTION__, i);
+ continue;
+ }
+
+ mode = crypto_details[i].mode;
+ sw_type = crypto_details[i].sw_type;
+
+ switch (sw_type)
+ {
+ case SW_TYPE_CIPHER:
+ if (crypto_has_cipher(algo, 0, CRYPTO_ALG_ASYNC))
+ {
+ REGISTER(i);
+ }
+ else
+ {
+ dprintk("%s:CIPHER algorithm %d:'%s' not supported\n",
+ __FUNCTION__, i, algo);
+ }
+ break;
+ case SW_TYPE_HMAC:
+ if (crypto_has_hash(algo, 0, CRYPTO_ALG_ASYNC))
+ {
+ REGISTER(i);
+ }
+ else
+ {
+ dprintk("%s:HMAC algorithm %d:'%s' not supported\n",
+ __FUNCTION__, i, algo);
+ }
+ break;
+ case SW_TYPE_HASH:
+ if (crypto_has_hash(algo, 0, CRYPTO_ALG_ASYNC))
+ {
+ REGISTER(i);
+ }
+ else
+ {
+ dprintk("%s:HASH algorithm %d:'%s' not supported\n",
+ __FUNCTION__, i, algo);
+ }
+ break;
+ case SW_TYPE_COMP:
+ if (crypto_has_comp(algo, 0, CRYPTO_ALG_ASYNC))
+ {
+ REGISTER(i);
+ }
+ else
+ {
+ dprintk("%s:COMP algorithm %d:'%s' not supported\n",
+ __FUNCTION__, i, algo);
+ }
+ break;
+ case SW_TYPE_BLKCIPHER:
+ if (crypto_has_blkcipher(algo, 0, CRYPTO_ALG_ASYNC))
+ {
+ REGISTER(i);
+ }
+ else
+ {
+ dprintk("%s:BLKCIPHER algorithm %d:'%s' not supported\n",
+ __FUNCTION__, i, algo);
+ }
+ break;
+ case SW_TYPE_ABLKCIPHER:
+ if (crypto_has_ablkcipher(algo, 0, 0))
+ {
+ REGISTER(i);
+ }
+ else
+ {
+ dprintk("%s:ABLKCIPHER algorithm %d:'%s' not supported\n",
+ __FUNCTION__, i, algo);
+ }
+ break;
+ default:
+ dprintk(
+ "%s:Algorithm Type %d not supported (algorithm %d:'%s')\n",
+ __FUNCTION__, sw_type, i, algo);
+ break;
+ }
+ }
+
+ return(0);
+}
+
+static void
+cryptosoft_exit(void)
+{
+ dprintk("%s()\n", __FUNCTION__);
+ crypto_unregister_all(swcr_id);
+ swcr_id = -1;
+}
+
+late_initcall(cryptosoft_init);
+module_exit(cryptosoft_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("David McCullough <david_mccullough@securecomputing.com>");
+MODULE_DESCRIPTION("Cryptosoft (OCF module for kernel crypto)");
diff --git a/crypto/ocf/ep80579/Makefile b/crypto/ocf/ep80579/Makefile
new file mode 100644
index 000000000000..19ff6eb0545a
--- /dev/null
+++ b/crypto/ocf/ep80579/Makefile
@@ -0,0 +1,107 @@
+#########################################################################
+#
+# Targets supported
+# all - builds everything and installs
+# install - identical to all
+# depend - build dependencies
+# clean - clears derived objects except the .depend files
+# distclean- clears all derived objects and the .depend file
+#
+# @par
+# This file is provided under a dual BSD/GPLv2 license. When using or
+# redistributing this file, you may do so under either license.
+#
+# GPL LICENSE SUMMARY
+#
+# Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+#
+# 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, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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.
+# The full GNU General Public License is included in this distribution
+# in the file called LICENSE.GPL.
+#
+# Contact Information:
+# Intel Corporation
+#
+# BSD LICENSE
+#
+# Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+# OWNER 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 OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#
+# version: Security.L.1.0.130
+############################################################################
+
+
+####################Common variables and definitions########################
+
+# Ensure The ENV_DIR environmental var is defined.
+ifndef ICP_ENV_DIR
+$(error ICP_ENV_DIR is undefined. Please set the path to your environment makefile \
+ "-> setenv ICP_ENV_DIR <path>")
+endif
+
+#Add your project environment Makefile
+include $(ICP_ENV_DIR)/environment.mk
+
+#include the makefile with all the default and common Make variable definitions
+include $(ICP_BUILDSYSTEM_PATH)/build_files/common.mk
+
+#Add the name for the executable, Library or Module output definitions
+OUTPUT_NAME= icp_ocf
+
+# List of Source Files to be compiled
+SOURCES= icp_common.c icp_sym.c icp_asym.c
+
+#common includes between all supported OSes
+INCLUDES= -I $(ICP_API_DIR) -I$(ICP_LAC_API) \
+-I$(ICP_OCF_SRC_DIR)
+
+# The location of the os level makefile needs to be changed.
+include $(ICP_ENV_DIR)/$(ICP_OS)_$(ICP_OS_LEVEL).mk
+
+# On the line directly below list the outputs you wish to build for,
+# e.g "lib_static lib_shared exe module" as show below
+install: module
+
+###################Include rules makefiles########################
+include $(ICP_BUILDSYSTEM_PATH)/build_files/rules.mk
+###################End of Rules inclusion#########################
+
+
diff --git a/crypto/ocf/ep80579/environment.mk b/crypto/ocf/ep80579/environment.mk
new file mode 100644
index 000000000000..a674b4589fcd
--- /dev/null
+++ b/crypto/ocf/ep80579/environment.mk
@@ -0,0 +1,75 @@
+ ###########################################################################
+ #
+# This file is provided under a dual BSD/GPLv2 license. When using or
+# redistributing this file, you may do so under either license.
+#
+# GPL LICENSE SUMMARY
+#
+# Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+#
+# 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, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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.
+# The full GNU General Public License is included in this distribution
+# in the file called LICENSE.GPL.
+#
+# Contact Information:
+# Intel Corporation
+#
+# BSD LICENSE
+#
+# Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+# OWNER 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 OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#
+# version: Security.L.1.0.130
+ #
+ ###########################################################################
+
+
+ICP_LAC_API=$(ICP_ROOT)/Acceleration/include/lac
+ICP_BTR_API=$(ICP_ROOT)/Acceleration/include/btr
+ICP_API_DIR=$(ICP_ROOT)/Acceleration/include
+ICP_OCF_SHIM_DIR?=$(KERNEL_SOURCE_ROOT)/crypto/ocf/
+
+ICP_OS_LEVEL?=kernel_space
+
+ICP_OS?=linux_2.6
+
+ICP_CORE?=ia
+
diff --git a/crypto/ocf/ep80579/icp_asym.c b/crypto/ocf/ep80579/icp_asym.c
new file mode 100644
index 000000000000..1a9bd28bf4d1
--- /dev/null
+++ b/crypto/ocf/ep80579/icp_asym.c
@@ -0,0 +1,1375 @@
+/***************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+ *
+ * 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, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR 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.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Corporation
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * version: Security.L.1.0.130
+ *
+ ***************************************************************************/
+
+#include "icp_ocf.h"
+
+/*The following define values (containing the word 'INDEX') are used to find
+the index of each input buffer of the crypto_kop struct (see OCF cryptodev.h).
+These values were found through analysis of the OCF OpenSSL patch. If the
+calling program uses different input buffer positions, these defines will have
+to be changed.*/
+
+/*DIFFIE HELLMAN buffer index values*/
+#define ICP_DH_KRP_PARAM_PRIME_INDEX (0)
+#define ICP_DH_KRP_PARAM_BASE_INDEX (1)
+#define ICP_DH_KRP_PARAM_PRIVATE_VALUE_INDEX (2)
+#define ICP_DH_KRP_PARAM_RESULT_INDEX (3)
+
+/*MOD EXP buffer index values*/
+#define ICP_MOD_EXP_KRP_PARAM_BASE_INDEX (0)
+#define ICP_MOD_EXP_KRP_PARAM_EXPONENT_INDEX (1)
+#define ICP_MOD_EXP_KRP_PARAM_MODULUS_INDEX (2)
+#define ICP_MOD_EXP_KRP_PARAM_RESULT_INDEX (3)
+
+#define SINGLE_BYTE_VALUE (4)
+
+/*MOD EXP CRT buffer index values*/
+#define ICP_MOD_EXP_CRT_KRP_PARAM_PRIME_P_INDEX (0)
+#define ICP_MOD_EXP_CRT_KRP_PARAM_PRIME_Q_INDEX (1)
+#define ICP_MOD_EXP_CRT_KRP_PARAM_I_INDEX (2)
+#define ICP_MOD_EXP_CRT_KRP_PARAM_EXPONENT_DP_INDEX (3)
+#define ICP_MOD_EXP_CRT_KRP_PARAM_EXPONENT_DQ_INDEX (4)
+#define ICP_MOD_EXP_CRT_KRP_PARAM_COEFF_QINV_INDEX (5)
+#define ICP_MOD_EXP_CRT_KRP_PARAM_RESULT_INDEX (6)
+
+/*DSA sign buffer index values*/
+#define ICP_DSA_SIGN_KRP_PARAM_DGST_INDEX (0)
+#define ICP_DSA_SIGN_KRP_PARAM_PRIME_P_INDEX (1)
+#define ICP_DSA_SIGN_KRP_PARAM_PRIME_Q_INDEX (2)
+#define ICP_DSA_SIGN_KRP_PARAM_G_INDEX (3)
+#define ICP_DSA_SIGN_KRP_PARAM_X_INDEX (4)
+#define ICP_DSA_SIGN_KRP_PARAM_R_RESULT_INDEX (5)
+#define ICP_DSA_SIGN_KRP_PARAM_S_RESULT_INDEX (6)
+
+/*DSA verify buffer index values*/
+#define ICP_DSA_VERIFY_KRP_PARAM_DGST_INDEX (0)
+#define ICP_DSA_VERIFY_KRP_PARAM_PRIME_P_INDEX (1)
+#define ICP_DSA_VERIFY_KRP_PARAM_PRIME_Q_INDEX (2)
+#define ICP_DSA_VERIFY_KRP_PARAM_G_INDEX (3)
+#define ICP_DSA_VERIFY_KRP_PARAM_PUBKEY_INDEX (4)
+#define ICP_DSA_VERIFY_KRP_PARAM_SIG_R_INDEX (5)
+#define ICP_DSA_VERIFY_KRP_PARAM_SIG_S_INDEX (6)
+
+/*DSA sign prime Q vs random number K size check values*/
+#define DONT_RUN_LESS_THAN_CHECK (0)
+#define FAIL_A_IS_GREATER_THAN_B (1)
+#define FAIL_A_IS_EQUAL_TO_B (1)
+#define SUCCESS_A_IS_LESS_THAN_B (0)
+#define DSA_SIGN_RAND_GEN_VAL_CHECK_MAX_ITERATIONS (500)
+
+/* We need to set a cryptokp success value just in case it is set or allocated
+ and not set to zero outside of this module */
+#define CRYPTO_OP_SUCCESS (0)
+
+static int icp_ocfDrvDHComputeKey(struct cryptkop *krp);
+
+static int icp_ocfDrvModExp(struct cryptkop *krp);
+
+static int icp_ocfDrvModExpCRT(struct cryptkop *krp);
+
+static int
+icp_ocfDrvCheckALessThanB(CpaFlatBuffer * pK, CpaFlatBuffer * pQ, int *doCheck);
+
+static int icp_ocfDrvDsaSign(struct cryptkop *krp);
+
+static int icp_ocfDrvDsaVerify(struct cryptkop *krp);
+
+static void
+icp_ocfDrvDhP1CallBack(void *callbackTag,
+ CpaStatus status,
+ void *pOpData, CpaFlatBuffer * pLocalOctetStringPV);
+
+static void
+icp_ocfDrvModExpCallBack(void *callbackTag,
+ CpaStatus status,
+ void *pOpData, CpaFlatBuffer * pResult);
+
+static void
+icp_ocfDrvModExpCRTCallBack(void *callbackTag,
+ CpaStatus status,
+ void *pOpData, CpaFlatBuffer * pOutputData);
+
+static void
+icp_ocfDrvDsaVerifyCallBack(void *callbackTag,
+ CpaStatus status,
+ void *pOpData, CpaBoolean verifyStatus);
+
+static void
+icp_ocfDrvDsaRSSignCallBack(void *callbackTag,
+ CpaStatus status,
+ void *pOpData,
+ CpaBoolean protocolStatus,
+ CpaFlatBuffer * pR, CpaFlatBuffer * pS);
+
+/* Name : icp_ocfDrvPkeProcess
+ *
+ * Description : This function will choose which PKE process to follow
+ * based on the input arguments
+ */
+int icp_ocfDrvPkeProcess(device_t dev, struct cryptkop *krp, int hint)
+{
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+
+ if (NULL == krp) {
+ DPRINTK("%s(): Invalid input parameters, cryptkop = %p\n",
+ __FUNCTION__, krp);
+ return EINVAL;
+ }
+
+ if (CPA_TRUE == atomic_read(&icp_ocfDrvIsExiting)) {
+ krp->krp_status = ECANCELED;
+ return ECANCELED;
+ }
+
+ switch (krp->krp_op) {
+ case CRK_DH_COMPUTE_KEY:
+ DPRINTK("%s() doing DH_COMPUTE_KEY\n", __FUNCTION__);
+ lacStatus = icp_ocfDrvDHComputeKey(krp);
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): icp_ocfDrvDHComputeKey failed "
+ "(%d).\n", __FUNCTION__, lacStatus);
+ krp->krp_status = ECANCELED;
+ return ECANCELED;
+ }
+
+ break;
+
+ case CRK_MOD_EXP:
+ DPRINTK("%s() doing MOD_EXP \n", __FUNCTION__);
+ lacStatus = icp_ocfDrvModExp(krp);
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): icp_ocfDrvModExp failed (%d).\n",
+ __FUNCTION__, lacStatus);
+ krp->krp_status = ECANCELED;
+ return ECANCELED;
+ }
+
+ break;
+
+ case CRK_MOD_EXP_CRT:
+ DPRINTK("%s() doing MOD_EXP_CRT \n", __FUNCTION__);
+ lacStatus = icp_ocfDrvModExpCRT(krp);
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): icp_ocfDrvModExpCRT "
+ "failed (%d).\n", __FUNCTION__, lacStatus);
+ krp->krp_status = ECANCELED;
+ return ECANCELED;
+ }
+
+ break;
+
+ case CRK_DSA_SIGN:
+ DPRINTK("%s() doing DSA_SIGN \n", __FUNCTION__);
+ lacStatus = icp_ocfDrvDsaSign(krp);
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): icp_ocfDrvDsaSign "
+ "failed (%d).\n", __FUNCTION__, lacStatus);
+ krp->krp_status = ECANCELED;
+ return ECANCELED;
+ }
+
+ break;
+
+ case CRK_DSA_VERIFY:
+ DPRINTK("%s() doing DSA_VERIFY \n", __FUNCTION__);
+ lacStatus = icp_ocfDrvDsaVerify(krp);
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): icp_ocfDrvDsaVerify "
+ "failed (%d).\n", __FUNCTION__, lacStatus);
+ krp->krp_status = ECANCELED;
+ return ECANCELED;
+ }
+
+ break;
+
+ default:
+ EPRINTK("%s(): Asymettric function not "
+ "supported (%d).\n", __FUNCTION__, krp->krp_op);
+ krp->krp_status = EOPNOTSUPP;
+ return EOPNOTSUPP;
+ }
+
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+}
+
+/* Name : icp_ocfDrvSwapBytes
+ *
+ * Description : This function is used to swap the byte order of a buffer.
+ * It has been seen that in general we are passed little endian byte order
+ * buffers, but LAC only accepts big endian byte order buffers.
+ */
+static void inline
+icp_ocfDrvSwapBytes(u_int8_t * num, u_int32_t buff_len_bytes)
+{
+
+ int i;
+ u_int8_t *end_ptr;
+ u_int8_t hold_val;
+
+ end_ptr = num + (buff_len_bytes - 1);
+ buff_len_bytes = buff_len_bytes >> 1;
+ for (i = 0; i < buff_len_bytes; i++) {
+ hold_val = *num;
+ *num = *end_ptr;
+ num++;
+ *end_ptr = hold_val;
+ end_ptr--;
+ }
+}
+
+/* Name : icp_ocfDrvDHComputeKey
+ *
+ * Description : This function will map Diffie Hellman calls from OCF
+ * to the LAC API. OCF uses this function for Diffie Hellman Phase1 and
+ * Phase2. LAC has a separate Diffie Hellman Phase2 call, however both phases
+ * break down to a modular exponentiation.
+ */
+static int icp_ocfDrvDHComputeKey(struct cryptkop *krp)
+{
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+ void *callbackTag = NULL;
+ CpaCyDhPhase1KeyGenOpData *pPhase1OpData = NULL;
+ CpaFlatBuffer *pLocalOctetStringPV = NULL;
+ uint32_t dh_prime_len_bytes = 0, dh_prime_len_bits = 0;
+
+ /* Input checks - check prime is a multiple of 8 bits to allow for
+ allocation later */
+ dh_prime_len_bits =
+ (krp->krp_param[ICP_DH_KRP_PARAM_PRIME_INDEX].crp_nbits);
+
+ /* LAC can reject prime lengths based on prime key sizes, we just
+ need to make sure we can allocate space for the base and
+ exponent buffers correctly */
+ if ((dh_prime_len_bits % NUM_BITS_IN_BYTE) != 0) {
+ APRINTK("%s(): Warning Prime number buffer size is not a "
+ "multiple of 8 bits\n", __FUNCTION__);
+ }
+
+ /* Result storage space should be the same size as the prime as this
+ value can take up the same amount of storage space */
+ if (dh_prime_len_bits !=
+ krp->krp_param[ICP_DH_KRP_PARAM_RESULT_INDEX].crp_nbits) {
+ DPRINTK("%s(): Return Buffer must be the same size "
+ "as the Prime buffer\n", __FUNCTION__);
+ krp->krp_status = EINVAL;
+ return EINVAL;
+ }
+ /* Switch to size in bytes */
+ BITS_TO_BYTES(dh_prime_len_bytes, dh_prime_len_bits);
+
+ callbackTag = krp;
+
+ pPhase1OpData = kmem_cache_zalloc(drvDH_zone, GFP_KERNEL);
+ if (NULL == pPhase1OpData) {
+ APRINTK("%s():Failed to get memory for key gen data\n",
+ __FUNCTION__);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ pLocalOctetStringPV = kmem_cache_zalloc(drvFlatBuffer_zone, GFP_KERNEL);
+ if (NULL == pLocalOctetStringPV) {
+ APRINTK("%s():Failed to get memory for pLocalOctetStringPV\n",
+ __FUNCTION__);
+ kmem_cache_free(drvDH_zone, pPhase1OpData);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ /* Link parameters */
+ pPhase1OpData->primeP.pData =
+ krp->krp_param[ICP_DH_KRP_PARAM_PRIME_INDEX].crp_p;
+
+ pPhase1OpData->primeP.dataLenInBytes = dh_prime_len_bytes;
+
+ icp_ocfDrvSwapBytes(pPhase1OpData->primeP.pData, dh_prime_len_bytes);
+
+ pPhase1OpData->baseG.pData =
+ krp->krp_param[ICP_DH_KRP_PARAM_BASE_INDEX].crp_p;
+
+ BITS_TO_BYTES(pPhase1OpData->baseG.dataLenInBytes,
+ krp->krp_param[ICP_DH_KRP_PARAM_BASE_INDEX].crp_nbits);
+
+ icp_ocfDrvSwapBytes(pPhase1OpData->baseG.pData,
+ pPhase1OpData->baseG.dataLenInBytes);
+
+ pPhase1OpData->privateValueX.pData =
+ krp->krp_param[ICP_DH_KRP_PARAM_PRIVATE_VALUE_INDEX].crp_p;
+
+ BITS_TO_BYTES(pPhase1OpData->privateValueX.dataLenInBytes,
+ krp->krp_param[ICP_DH_KRP_PARAM_PRIVATE_VALUE_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(pPhase1OpData->privateValueX.pData,
+ pPhase1OpData->privateValueX.dataLenInBytes);
+
+ /* Output parameters */
+ pLocalOctetStringPV->pData =
+ krp->krp_param[ICP_DH_KRP_PARAM_RESULT_INDEX].crp_p;
+
+ BITS_TO_BYTES(pLocalOctetStringPV->dataLenInBytes,
+ krp->krp_param[ICP_DH_KRP_PARAM_RESULT_INDEX].crp_nbits);
+
+ lacStatus = cpaCyDhKeyGenPhase1(CPA_INSTANCE_HANDLE_SINGLE,
+ icp_ocfDrvDhP1CallBack,
+ callbackTag, pPhase1OpData,
+ pLocalOctetStringPV);
+
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): DH Phase 1 Key Gen failed (%d).\n",
+ __FUNCTION__, lacStatus);
+ icp_ocfDrvFreeFlatBuffer(pLocalOctetStringPV);
+ kmem_cache_free(drvDH_zone, pPhase1OpData);
+ }
+
+ return lacStatus;
+}
+
+/* Name : icp_ocfDrvModExp
+ *
+ * Description : This function will map ordinary Modular Exponentiation calls
+ * from OCF to the LAC API.
+ *
+ */
+static int icp_ocfDrvModExp(struct cryptkop *krp)
+{
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+ void *callbackTag = NULL;
+ CpaCyLnModExpOpData *pModExpOpData = NULL;
+ CpaFlatBuffer *pResult = NULL;
+
+ if ((krp->krp_param[ICP_MOD_EXP_KRP_PARAM_MODULUS_INDEX].crp_nbits %
+ NUM_BITS_IN_BYTE) != 0) {
+ DPRINTK("%s(): Warning - modulus buffer size (%d) is not a "
+ "multiple of 8 bits\n", __FUNCTION__,
+ krp->krp_param[ICP_MOD_EXP_KRP_PARAM_MODULUS_INDEX].
+ crp_nbits);
+ }
+
+ /* Result storage space should be the same size as the prime as this
+ value can take up the same amount of storage space */
+ if (krp->krp_param[ICP_MOD_EXP_KRP_PARAM_MODULUS_INDEX].crp_nbits >
+ krp->krp_param[ICP_MOD_EXP_KRP_PARAM_RESULT_INDEX].crp_nbits) {
+ APRINTK("%s(): Return Buffer size must be the same or"
+ " greater than the Modulus buffer\n", __FUNCTION__);
+ krp->krp_status = EINVAL;
+ return EINVAL;
+ }
+
+ callbackTag = krp;
+
+ pModExpOpData = kmem_cache_zalloc(drvLnModExp_zone, GFP_KERNEL);
+ if (NULL == pModExpOpData) {
+ APRINTK("%s():Failed to get memory for key gen data\n",
+ __FUNCTION__);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ pResult = kmem_cache_zalloc(drvFlatBuffer_zone, GFP_KERNEL);
+ if (NULL == pResult) {
+ APRINTK("%s():Failed to get memory for ModExp result\n",
+ __FUNCTION__);
+ kmem_cache_free(drvLnModExp_zone, pModExpOpData);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ /* Link parameters */
+ pModExpOpData->modulus.pData =
+ krp->krp_param[ICP_MOD_EXP_KRP_PARAM_MODULUS_INDEX].crp_p;
+ BITS_TO_BYTES(pModExpOpData->modulus.dataLenInBytes,
+ krp->krp_param[ICP_MOD_EXP_KRP_PARAM_MODULUS_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(pModExpOpData->modulus.pData,
+ pModExpOpData->modulus.dataLenInBytes);
+
+ /*OCF patch to Openswan Pluto regularly sends the base value as 2
+ bits in size. In this case, it has been found it is better to
+ use the base size memory space as the input buffer (if the number
+ is in bits is less than a byte, the number of bits is the input
+ value) */
+ if (krp->krp_param[ICP_MOD_EXP_KRP_PARAM_BASE_INDEX].crp_nbits <
+ NUM_BITS_IN_BYTE) {
+ DPRINTK("%s : base is small (%d)\n", __FUNCTION__, krp->
+ krp_param[ICP_MOD_EXP_KRP_PARAM_BASE_INDEX].crp_nbits);
+ pModExpOpData->base.dataLenInBytes = SINGLE_BYTE_VALUE;
+ pModExpOpData->base.pData =
+ (uint8_t *) & (krp->
+ krp_param[ICP_MOD_EXP_KRP_PARAM_BASE_INDEX].
+ crp_nbits);
+ *((uint32_t *) pModExpOpData->base.pData) =
+ htonl(*((uint32_t *) pModExpOpData->base.pData));
+
+ } else {
+
+ DPRINTK("%s : base is big (%d)\n", __FUNCTION__, krp->
+ krp_param[ICP_MOD_EXP_KRP_PARAM_BASE_INDEX].crp_nbits);
+ pModExpOpData->base.pData =
+ krp->krp_param[ICP_MOD_EXP_KRP_PARAM_BASE_INDEX].crp_p;
+ BITS_TO_BYTES(pModExpOpData->base.dataLenInBytes,
+ krp->krp_param[ICP_MOD_EXP_KRP_PARAM_BASE_INDEX].
+ crp_nbits);
+ icp_ocfDrvSwapBytes(pModExpOpData->base.pData,
+ pModExpOpData->base.dataLenInBytes);
+ }
+
+ pModExpOpData->exponent.pData =
+ krp->krp_param[ICP_MOD_EXP_KRP_PARAM_EXPONENT_INDEX].crp_p;
+ BITS_TO_BYTES(pModExpOpData->exponent.dataLenInBytes,
+ krp->krp_param[ICP_MOD_EXP_KRP_PARAM_EXPONENT_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(pModExpOpData->exponent.pData,
+ pModExpOpData->exponent.dataLenInBytes);
+ /* Output parameters */
+ pResult->pData =
+ krp->krp_param[ICP_MOD_EXP_KRP_PARAM_RESULT_INDEX].crp_p,
+ BITS_TO_BYTES(pResult->dataLenInBytes,
+ krp->krp_param[ICP_MOD_EXP_KRP_PARAM_RESULT_INDEX].
+ crp_nbits);
+
+ lacStatus = cpaCyLnModExp(CPA_INSTANCE_HANDLE_SINGLE,
+ icp_ocfDrvModExpCallBack,
+ callbackTag, pModExpOpData, pResult);
+
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): Mod Exp Operation failed (%d).\n",
+ __FUNCTION__, lacStatus);
+ krp->krp_status = ECANCELED;
+ icp_ocfDrvFreeFlatBuffer(pResult);
+ kmem_cache_free(drvLnModExp_zone, pModExpOpData);
+ }
+
+ return lacStatus;
+}
+
+/* Name : icp_ocfDrvModExpCRT
+ *
+ * Description : This function will map ordinary Modular Exponentiation Chinese
+ * Remainder Theorem implementaion calls from OCF to the LAC API.
+ *
+ * Note : Mod Exp CRT for this driver is accelerated through LAC RSA type 2
+ * decrypt operation. Therefore P and Q input values must always be prime
+ * numbers. Although basic primality checks are done in LAC, it is up to the
+ * user to do any correct prime number checking before passing the inputs.
+ */
+
+static int icp_ocfDrvModExpCRT(struct cryptkop *krp)
+{
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+ CpaCyRsaDecryptOpData *rsaDecryptOpData = NULL;
+ void *callbackTag = NULL;
+ CpaFlatBuffer *pOutputData = NULL;
+
+ /*Parameter input checks are all done by LAC, no need to repeat
+ them here. */
+ callbackTag = krp;
+
+ rsaDecryptOpData = kmem_cache_zalloc(drvRSADecrypt_zone, GFP_KERNEL);
+ if (NULL == rsaDecryptOpData) {
+ APRINTK("%s():Failed to get memory"
+ " for MOD EXP CRT Op data struct\n", __FUNCTION__);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ rsaDecryptOpData->pRecipientPrivateKey
+ = kmem_cache_zalloc(drvRSAPrivateKey_zone, GFP_KERNEL);
+ if (NULL == rsaDecryptOpData->pRecipientPrivateKey) {
+ APRINTK("%s():Failed to get memory for MOD EXP CRT"
+ " private key values struct\n", __FUNCTION__);
+ kmem_cache_free(drvRSADecrypt_zone, rsaDecryptOpData);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ rsaDecryptOpData->pRecipientPrivateKey->
+ version = CPA_CY_RSA_VERSION_TWO_PRIME;
+ rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRepType = CPA_CY_RSA_PRIVATE_KEY_REP_TYPE_2;
+
+ pOutputData = kmem_cache_zalloc(drvFlatBuffer_zone, GFP_KERNEL);
+ if (NULL == pOutputData) {
+ APRINTK("%s():Failed to get memory"
+ " for MOD EXP CRT output data\n", __FUNCTION__);
+ kmem_cache_free(drvRSAPrivateKey_zone,
+ rsaDecryptOpData->pRecipientPrivateKey);
+ kmem_cache_free(drvRSADecrypt_zone, rsaDecryptOpData);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ rsaDecryptOpData->pRecipientPrivateKey->
+ version = CPA_CY_RSA_VERSION_TWO_PRIME;
+ rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRepType = CPA_CY_RSA_PRIVATE_KEY_REP_TYPE_2;
+
+ /* Link parameters */
+ rsaDecryptOpData->inputData.pData =
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_I_INDEX].crp_p;
+ BITS_TO_BYTES(rsaDecryptOpData->inputData.dataLenInBytes,
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_I_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(rsaDecryptOpData->inputData.pData,
+ rsaDecryptOpData->inputData.dataLenInBytes);
+
+ rsaDecryptOpData->pRecipientPrivateKey->privateKeyRep2.prime1P.pData =
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_PRIME_P_INDEX].crp_p;
+ BITS_TO_BYTES(rsaDecryptOpData->pRecipientPrivateKey->privateKeyRep2.
+ prime1P.dataLenInBytes,
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_PRIME_P_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.prime1P.pData,
+ rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.prime1P.dataLenInBytes);
+
+ rsaDecryptOpData->pRecipientPrivateKey->privateKeyRep2.prime2Q.pData =
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_PRIME_Q_INDEX].crp_p;
+ BITS_TO_BYTES(rsaDecryptOpData->pRecipientPrivateKey->privateKeyRep2.
+ prime2Q.dataLenInBytes,
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_PRIME_Q_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.prime2Q.pData,
+ rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.prime2Q.dataLenInBytes);
+
+ rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.exponent1Dp.pData =
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_EXPONENT_DP_INDEX].crp_p;
+ BITS_TO_BYTES(rsaDecryptOpData->pRecipientPrivateKey->privateKeyRep2.
+ exponent1Dp.dataLenInBytes,
+ krp->
+ krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_EXPONENT_DP_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.exponent1Dp.pData,
+ rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.exponent1Dp.dataLenInBytes);
+
+ rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.exponent2Dq.pData =
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_EXPONENT_DQ_INDEX].crp_p;
+ BITS_TO_BYTES(rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.exponent2Dq.dataLenInBytes,
+ krp->
+ krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_EXPONENT_DQ_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.exponent2Dq.pData,
+ rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.exponent2Dq.dataLenInBytes);
+
+ rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.coefficientQInv.pData =
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_COEFF_QINV_INDEX].crp_p;
+ BITS_TO_BYTES(rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.coefficientQInv.dataLenInBytes,
+ krp->
+ krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_COEFF_QINV_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.coefficientQInv.pData,
+ rsaDecryptOpData->pRecipientPrivateKey->
+ privateKeyRep2.coefficientQInv.dataLenInBytes);
+
+ /* Output Parameter */
+ pOutputData->pData =
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_RESULT_INDEX].crp_p;
+ BITS_TO_BYTES(pOutputData->dataLenInBytes,
+ krp->krp_param[ICP_MOD_EXP_CRT_KRP_PARAM_RESULT_INDEX].
+ crp_nbits);
+
+ lacStatus = cpaCyRsaDecrypt(CPA_INSTANCE_HANDLE_SINGLE,
+ icp_ocfDrvModExpCRTCallBack,
+ callbackTag, rsaDecryptOpData, pOutputData);
+
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): Mod Exp CRT Operation failed (%d).\n",
+ __FUNCTION__, lacStatus);
+ krp->krp_status = ECANCELED;
+ icp_ocfDrvFreeFlatBuffer(pOutputData);
+ kmem_cache_free(drvRSAPrivateKey_zone,
+ rsaDecryptOpData->pRecipientPrivateKey);
+ kmem_cache_free(drvRSADecrypt_zone, rsaDecryptOpData);
+ }
+
+ return lacStatus;
+}
+
+/* Name : icp_ocfDrvCheckALessThanB
+ *
+ * Description : This function will check whether the first argument is less
+ * than the second. It is used to check whether the DSA RS sign Random K
+ * value is less than the Prime Q value (as defined in the specification)
+ *
+ */
+static int
+icp_ocfDrvCheckALessThanB(CpaFlatBuffer * pK, CpaFlatBuffer * pQ, int *doCheck)
+{
+
+ uint8_t *MSB_K = pK->pData;
+ uint8_t *MSB_Q = pQ->pData;
+ uint32_t buffer_lengths_in_bytes = pQ->dataLenInBytes;
+
+ if (DONT_RUN_LESS_THAN_CHECK == *doCheck) {
+ return FAIL_A_IS_GREATER_THAN_B;
+ }
+
+/*Check MSBs
+if A == B, check next MSB
+if A > B, return A_IS_GREATER_THAN_B
+if A < B, return A_IS_LESS_THAN_B (success)
+*/
+ while (*MSB_K == *MSB_Q) {
+ MSB_K++;
+ MSB_Q++;
+
+ buffer_lengths_in_bytes--;
+ if (0 == buffer_lengths_in_bytes) {
+ DPRINTK("%s() Buffers have equal value!!\n",
+ __FUNCTION__);
+ return FAIL_A_IS_EQUAL_TO_B;
+ }
+
+ }
+
+ if (*MSB_K < *MSB_Q) {
+ return SUCCESS_A_IS_LESS_THAN_B;
+ } else {
+ return FAIL_A_IS_GREATER_THAN_B;
+ }
+
+}
+
+/* Name : icp_ocfDrvDsaSign
+ *
+ * Description : This function will map DSA RS Sign from OCF to the LAC API.
+ *
+ * NOTE: From looking at OCF patch to OpenSSL and even the number of input
+ * parameters, OCF expects us to generate the random seed value. This value
+ * is generated and passed to LAC, however the number is discared in the
+ * callback and not returned to the user.
+ */
+static int icp_ocfDrvDsaSign(struct cryptkop *krp)
+{
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+ CpaCyDsaRSSignOpData *dsaRsSignOpData = NULL;
+ void *callbackTag = NULL;
+ CpaCyRandGenOpData randGenOpData;
+ int primeQSizeInBytes = 0;
+ int doCheck = 0;
+ CpaFlatBuffer randData;
+ CpaBoolean protocolStatus = CPA_FALSE;
+ CpaFlatBuffer *pR = NULL;
+ CpaFlatBuffer *pS = NULL;
+
+ callbackTag = krp;
+
+ BITS_TO_BYTES(primeQSizeInBytes,
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_PRIME_Q_INDEX].
+ crp_nbits);
+
+ if (DSA_RS_SIGN_PRIMEQ_SIZE_IN_BYTES != primeQSizeInBytes) {
+ APRINTK("%s(): DSA PRIME Q size not equal to the "
+ "FIPS defined 20bytes, = %d\n",
+ __FUNCTION__, primeQSizeInBytes);
+ krp->krp_status = EDOM;
+ return EDOM;
+ }
+
+ dsaRsSignOpData = kmem_cache_zalloc(drvDSARSSign_zone, GFP_KERNEL);
+ if (NULL == dsaRsSignOpData) {
+ APRINTK("%s():Failed to get memory"
+ " for DSA RS Sign Op data struct\n", __FUNCTION__);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ dsaRsSignOpData->K.pData =
+ kmem_cache_alloc(drvDSARSSignKValue_zone, GFP_ATOMIC);
+
+ if (NULL == dsaRsSignOpData->K.pData) {
+ APRINTK("%s():Failed to get memory"
+ " for DSA RS Sign Op Random value\n", __FUNCTION__);
+ kmem_cache_free(drvDSARSSign_zone, dsaRsSignOpData);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ pR = kmem_cache_zalloc(drvFlatBuffer_zone, GFP_KERNEL);
+ if (NULL == pR) {
+ APRINTK("%s():Failed to get memory"
+ " for DSA signature R\n", __FUNCTION__);
+ kmem_cache_free(drvDSARSSignKValue_zone,
+ dsaRsSignOpData->K.pData);
+ kmem_cache_free(drvDSARSSign_zone, dsaRsSignOpData);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ pS = kmem_cache_zalloc(drvFlatBuffer_zone, GFP_KERNEL);
+ if (NULL == pS) {
+ APRINTK("%s():Failed to get memory"
+ " for DSA signature S\n", __FUNCTION__);
+ icp_ocfDrvFreeFlatBuffer(pR);
+ kmem_cache_free(drvDSARSSignKValue_zone,
+ dsaRsSignOpData->K.pData);
+ kmem_cache_free(drvDSARSSign_zone, dsaRsSignOpData);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ /*link prime number parameter for ease of processing */
+ dsaRsSignOpData->P.pData =
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_PRIME_P_INDEX].crp_p;
+ BITS_TO_BYTES(dsaRsSignOpData->P.dataLenInBytes,
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_PRIME_P_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(dsaRsSignOpData->P.pData,
+ dsaRsSignOpData->P.dataLenInBytes);
+
+ dsaRsSignOpData->Q.pData =
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_PRIME_Q_INDEX].crp_p;
+ BITS_TO_BYTES(dsaRsSignOpData->Q.dataLenInBytes,
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_PRIME_Q_INDEX].
+ crp_nbits);
+
+ icp_ocfDrvSwapBytes(dsaRsSignOpData->Q.pData,
+ dsaRsSignOpData->Q.dataLenInBytes);
+
+ /*generate random number with equal buffer size to Prime value Q,
+ but value less than Q */
+ dsaRsSignOpData->K.dataLenInBytes = dsaRsSignOpData->Q.dataLenInBytes;
+
+ randGenOpData.generateBits = CPA_TRUE;
+ randGenOpData.lenInBytes = dsaRsSignOpData->K.dataLenInBytes;
+
+ icp_ocfDrvPtrAndLenToFlatBuffer(dsaRsSignOpData->K.pData,
+ dsaRsSignOpData->K.dataLenInBytes,
+ &randData);
+
+ doCheck = 0;
+ while (icp_ocfDrvCheckALessThanB(&(dsaRsSignOpData->K),
+ &(dsaRsSignOpData->Q), &doCheck)) {
+
+ if (CPA_STATUS_SUCCESS
+ != cpaCyRandGen(CPA_INSTANCE_HANDLE_SINGLE,
+ NULL, NULL, &randGenOpData, &randData)) {
+ APRINTK("%s(): ERROR - Failed to generate DSA RS Sign K"
+ "value\n", __FUNCTION__);
+ icp_ocfDrvFreeFlatBuffer(pS);
+ icp_ocfDrvFreeFlatBuffer(pR);
+ kmem_cache_free(drvDSARSSignKValue_zone,
+ dsaRsSignOpData->K.pData);
+ kmem_cache_free(drvDSARSSign_zone, dsaRsSignOpData);
+ krp->krp_status = EAGAIN;
+ return EAGAIN;
+ }
+
+ doCheck++;
+ if (DSA_SIGN_RAND_GEN_VAL_CHECK_MAX_ITERATIONS == doCheck) {
+ APRINTK("%s(): ERROR - Failed to find DSA RS Sign K "
+ "value less than Q value\n", __FUNCTION__);
+ icp_ocfDrvFreeFlatBuffer(pS);
+ icp_ocfDrvFreeFlatBuffer(pR);
+ kmem_cache_free(drvDSARSSignKValue_zone,
+ dsaRsSignOpData->K.pData);
+ kmem_cache_free(drvDSARSSign_zone, dsaRsSignOpData);
+ krp->krp_status = EAGAIN;
+ return EAGAIN;
+ }
+
+ }
+ /*Rand Data - no need to swap bytes for pK */
+
+ /* Link parameters */
+ dsaRsSignOpData->G.pData =
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_G_INDEX].crp_p;
+ BITS_TO_BYTES(dsaRsSignOpData->G.dataLenInBytes,
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_G_INDEX].crp_nbits);
+
+ icp_ocfDrvSwapBytes(dsaRsSignOpData->G.pData,
+ dsaRsSignOpData->G.dataLenInBytes);
+
+ dsaRsSignOpData->X.pData =
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_X_INDEX].crp_p;
+ BITS_TO_BYTES(dsaRsSignOpData->X.dataLenInBytes,
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_X_INDEX].crp_nbits);
+ icp_ocfDrvSwapBytes(dsaRsSignOpData->X.pData,
+ dsaRsSignOpData->X.dataLenInBytes);
+
+ dsaRsSignOpData->M.pData =
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_DGST_INDEX].crp_p;
+ BITS_TO_BYTES(dsaRsSignOpData->M.dataLenInBytes,
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_DGST_INDEX].
+ crp_nbits);
+ icp_ocfDrvSwapBytes(dsaRsSignOpData->M.pData,
+ dsaRsSignOpData->M.dataLenInBytes);
+
+ /* Output Parameters */
+ pS->pData = krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_S_RESULT_INDEX].crp_p;
+ BITS_TO_BYTES(pS->dataLenInBytes,
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_S_RESULT_INDEX].
+ crp_nbits);
+
+ pR->pData = krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_R_RESULT_INDEX].crp_p;
+ BITS_TO_BYTES(pR->dataLenInBytes,
+ krp->krp_param[ICP_DSA_SIGN_KRP_PARAM_R_RESULT_INDEX].
+ crp_nbits);
+
+ lacStatus = cpaCyDsaSignRS(CPA_INSTANCE_HANDLE_SINGLE,
+ icp_ocfDrvDsaRSSignCallBack,
+ callbackTag, dsaRsSignOpData,
+ &protocolStatus, pR, pS);
+
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): DSA RS Sign Operation failed (%d).\n",
+ __FUNCTION__, lacStatus);
+ krp->krp_status = ECANCELED;
+ icp_ocfDrvFreeFlatBuffer(pS);
+ icp_ocfDrvFreeFlatBuffer(pR);
+ kmem_cache_free(drvDSARSSignKValue_zone,
+ dsaRsSignOpData->K.pData);
+ kmem_cache_free(drvDSARSSign_zone, dsaRsSignOpData);
+ }
+
+ return lacStatus;
+}
+
+/* Name : icp_ocfDrvDsaVerify
+ *
+ * Description : This function will map DSA RS Verify from OCF to the LAC API.
+ *
+ */
+static int icp_ocfDrvDsaVerify(struct cryptkop *krp)
+{
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+ CpaCyDsaVerifyOpData *dsaVerifyOpData = NULL;
+ void *callbackTag = NULL;
+ CpaBoolean verifyStatus = CPA_FALSE;
+
+ callbackTag = krp;
+
+ dsaVerifyOpData = kmem_cache_zalloc(drvDSAVerify_zone, GFP_KERNEL);
+ if (NULL == dsaVerifyOpData) {
+ APRINTK("%s():Failed to get memory"
+ " for DSA Verify Op data struct\n", __FUNCTION__);
+ krp->krp_status = ENOMEM;
+ return ENOMEM;
+ }
+
+ /* Link parameters */
+ dsaVerifyOpData->P.pData =
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_PRIME_P_INDEX].crp_p;
+ BITS_TO_BYTES(dsaVerifyOpData->P.dataLenInBytes,
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_PRIME_P_INDEX].
+ crp_nbits);
+ icp_ocfDrvSwapBytes(dsaVerifyOpData->P.pData,
+ dsaVerifyOpData->P.dataLenInBytes);
+
+ dsaVerifyOpData->Q.pData =
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_PRIME_Q_INDEX].crp_p;
+ BITS_TO_BYTES(dsaVerifyOpData->Q.dataLenInBytes,
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_PRIME_Q_INDEX].
+ crp_nbits);
+ icp_ocfDrvSwapBytes(dsaVerifyOpData->Q.pData,
+ dsaVerifyOpData->Q.dataLenInBytes);
+
+ dsaVerifyOpData->G.pData =
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_G_INDEX].crp_p;
+ BITS_TO_BYTES(dsaVerifyOpData->G.dataLenInBytes,
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_G_INDEX].
+ crp_nbits);
+ icp_ocfDrvSwapBytes(dsaVerifyOpData->G.pData,
+ dsaVerifyOpData->G.dataLenInBytes);
+
+ dsaVerifyOpData->Y.pData =
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_PUBKEY_INDEX].crp_p;
+ BITS_TO_BYTES(dsaVerifyOpData->Y.dataLenInBytes,
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_PUBKEY_INDEX].
+ crp_nbits);
+ icp_ocfDrvSwapBytes(dsaVerifyOpData->Y.pData,
+ dsaVerifyOpData->Y.dataLenInBytes);
+
+ dsaVerifyOpData->M.pData =
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_DGST_INDEX].crp_p;
+ BITS_TO_BYTES(dsaVerifyOpData->M.dataLenInBytes,
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_DGST_INDEX].
+ crp_nbits);
+ icp_ocfDrvSwapBytes(dsaVerifyOpData->M.pData,
+ dsaVerifyOpData->M.dataLenInBytes);
+
+ dsaVerifyOpData->R.pData =
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_SIG_R_INDEX].crp_p;
+ BITS_TO_BYTES(dsaVerifyOpData->R.dataLenInBytes,
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_SIG_R_INDEX].
+ crp_nbits);
+ icp_ocfDrvSwapBytes(dsaVerifyOpData->R.pData,
+ dsaVerifyOpData->R.dataLenInBytes);
+
+ dsaVerifyOpData->S.pData =
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_SIG_S_INDEX].crp_p;
+ BITS_TO_BYTES(dsaVerifyOpData->S.dataLenInBytes,
+ krp->krp_param[ICP_DSA_VERIFY_KRP_PARAM_SIG_S_INDEX].
+ crp_nbits);
+ icp_ocfDrvSwapBytes(dsaVerifyOpData->S.pData,
+ dsaVerifyOpData->S.dataLenInBytes);
+
+ lacStatus = cpaCyDsaVerify(CPA_INSTANCE_HANDLE_SINGLE,
+ icp_ocfDrvDsaVerifyCallBack,
+ callbackTag, dsaVerifyOpData, &verifyStatus);
+
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): DSA Verify Operation failed (%d).\n",
+ __FUNCTION__, lacStatus);
+ kmem_cache_free(drvDSAVerify_zone, dsaVerifyOpData);
+ krp->krp_status = ECANCELED;
+ }
+
+ return lacStatus;
+}
+
+/* Name : icp_ocfDrvReadRandom
+ *
+ * Description : This function will map RNG functionality calls from OCF
+ * to the LAC API.
+ */
+int icp_ocfDrvReadRandom(void *arg, uint32_t * buf, int maxwords)
+{
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+ CpaCyRandGenOpData randGenOpData;
+ CpaFlatBuffer randData;
+
+ if (NULL == buf) {
+ APRINTK("%s(): Invalid input parameters\n", __FUNCTION__);
+ return EINVAL;
+ }
+
+ /* maxwords here is number of integers to generate data for */
+ randGenOpData.generateBits = CPA_TRUE;
+
+ randGenOpData.lenInBytes = maxwords * sizeof(uint32_t);
+
+ icp_ocfDrvPtrAndLenToFlatBuffer((Cpa8U *) buf,
+ randGenOpData.lenInBytes, &randData);
+
+ lacStatus = cpaCyRandGen(CPA_INSTANCE_HANDLE_SINGLE,
+ NULL, NULL, &randGenOpData, &randData);
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): icp_LacSymRandGen failed (%d). \n",
+ __FUNCTION__, lacStatus);
+ return RETURN_RAND_NUM_GEN_FAILED;
+ }
+
+ return randGenOpData.lenInBytes / sizeof(uint32_t);
+}
+
+/* Name : icp_ocfDrvDhP1Callback
+ *
+ * Description : When this function returns it signifies that the LAC
+ * component has completed the DH operation.
+ */
+static void
+icp_ocfDrvDhP1CallBack(void *callbackTag,
+ CpaStatus status,
+ void *pOpData, CpaFlatBuffer * pLocalOctetStringPV)
+{
+ struct cryptkop *krp = NULL;
+ CpaCyDhPhase1KeyGenOpData *pPhase1OpData = NULL;
+
+ if (NULL == callbackTag) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "callbackTag data is NULL\n", __FUNCTION__);
+ return;
+ }
+ krp = (struct cryptkop *)callbackTag;
+
+ if (NULL == pOpData) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "Operation Data is NULL\n", __FUNCTION__);
+ krp->krp_status = ECANCELED;
+ crypto_kdone(krp);
+ return;
+ }
+ pPhase1OpData = (CpaCyDhPhase1KeyGenOpData *) pOpData;
+
+ if (NULL == pLocalOctetStringPV) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "pLocalOctetStringPV Data is NULL\n", __FUNCTION__);
+ memset(pPhase1OpData, 0, sizeof(CpaCyDhPhase1KeyGenOpData));
+ kmem_cache_free(drvDH_zone, pPhase1OpData);
+ krp->krp_status = ECANCELED;
+ crypto_kdone(krp);
+ return;
+ }
+
+ if (CPA_STATUS_SUCCESS == status) {
+ krp->krp_status = CRYPTO_OP_SUCCESS;
+ } else {
+ APRINTK("%s(): Diffie Hellman Phase1 Key Gen failed - "
+ "Operation Status = %d\n", __FUNCTION__, status);
+ krp->krp_status = ECANCELED;
+ }
+
+ icp_ocfDrvSwapBytes(pLocalOctetStringPV->pData,
+ pLocalOctetStringPV->dataLenInBytes);
+
+ icp_ocfDrvFreeFlatBuffer(pLocalOctetStringPV);
+ memset(pPhase1OpData, 0, sizeof(CpaCyDhPhase1KeyGenOpData));
+ kmem_cache_free(drvDH_zone, pPhase1OpData);
+
+ crypto_kdone(krp);
+
+ return;
+}
+
+/* Name : icp_ocfDrvModExpCallBack
+ *
+ * Description : When this function returns it signifies that the LAC
+ * component has completed the Mod Exp operation.
+ */
+static void
+icp_ocfDrvModExpCallBack(void *callbackTag,
+ CpaStatus status,
+ void *pOpdata, CpaFlatBuffer * pResult)
+{
+ struct cryptkop *krp = NULL;
+ CpaCyLnModExpOpData *pLnModExpOpData = NULL;
+
+ if (NULL == callbackTag) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "callbackTag data is NULL\n", __FUNCTION__);
+ return;
+ }
+ krp = (struct cryptkop *)callbackTag;
+
+ if (NULL == pOpdata) {
+ DPRINTK("%s(): Invalid Mod Exp input parameters - "
+ "Operation Data is NULL\n", __FUNCTION__);
+ krp->krp_status = ECANCELED;
+ crypto_kdone(krp);
+ return;
+ }
+ pLnModExpOpData = (CpaCyLnModExpOpData *) pOpdata;
+
+ if (NULL == pResult) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "pResult data is NULL\n", __FUNCTION__);
+ krp->krp_status = ECANCELED;
+ memset(pLnModExpOpData, 0, sizeof(CpaCyLnModExpOpData));
+ kmem_cache_free(drvLnModExp_zone, pLnModExpOpData);
+ crypto_kdone(krp);
+ return;
+ }
+
+ if (CPA_STATUS_SUCCESS == status) {
+ krp->krp_status = CRYPTO_OP_SUCCESS;
+ } else {
+ APRINTK("%s(): LAC Mod Exp Operation failed - "
+ "Operation Status = %d\n", __FUNCTION__, status);
+ krp->krp_status = ECANCELED;
+ }
+
+ icp_ocfDrvSwapBytes(pResult->pData, pResult->dataLenInBytes);
+
+ /*switch base size value back to original */
+ if (pLnModExpOpData->base.pData ==
+ (uint8_t *) & (krp->
+ krp_param[ICP_MOD_EXP_KRP_PARAM_BASE_INDEX].
+ crp_nbits)) {
+ *((uint32_t *) pLnModExpOpData->base.pData) =
+ ntohl(*((uint32_t *) pLnModExpOpData->base.pData));
+ }
+ icp_ocfDrvFreeFlatBuffer(pResult);
+ memset(pLnModExpOpData, 0, sizeof(CpaCyLnModExpOpData));
+ kmem_cache_free(drvLnModExp_zone, pLnModExpOpData);
+
+ crypto_kdone(krp);
+
+ return;
+
+}
+
+/* Name : icp_ocfDrvModExpCRTCallBack
+ *
+ * Description : When this function returns it signifies that the LAC
+ * component has completed the Mod Exp CRT operation.
+ */
+static void
+icp_ocfDrvModExpCRTCallBack(void *callbackTag,
+ CpaStatus status,
+ void *pOpData, CpaFlatBuffer * pOutputData)
+{
+ struct cryptkop *krp = NULL;
+ CpaCyRsaDecryptOpData *pDecryptData = NULL;
+
+ if (NULL == callbackTag) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "callbackTag data is NULL\n", __FUNCTION__);
+ return;
+ }
+
+ krp = (struct cryptkop *)callbackTag;
+
+ if (NULL == pOpData) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "Operation Data is NULL\n", __FUNCTION__);
+ krp->krp_status = ECANCELED;
+ crypto_kdone(krp);
+ return;
+ }
+ pDecryptData = (CpaCyRsaDecryptOpData *) pOpData;
+
+ if (NULL == pOutputData) {
+ DPRINTK("%s(): Invalid input parameter - "
+ "pOutputData is NULL\n", __FUNCTION__);
+ memset(pDecryptData->pRecipientPrivateKey, 0,
+ sizeof(CpaCyRsaPrivateKey));
+ kmem_cache_free(drvRSAPrivateKey_zone,
+ pDecryptData->pRecipientPrivateKey);
+ memset(pDecryptData, 0, sizeof(CpaCyRsaDecryptOpData));
+ kmem_cache_free(drvRSADecrypt_zone, pDecryptData);
+ krp->krp_status = ECANCELED;
+ crypto_kdone(krp);
+ return;
+ }
+
+ if (CPA_STATUS_SUCCESS == status) {
+ krp->krp_status = CRYPTO_OP_SUCCESS;
+ } else {
+ APRINTK("%s(): LAC Mod Exp CRT operation failed - "
+ "Operation Status = %d\n", __FUNCTION__, status);
+ krp->krp_status = ECANCELED;
+ }
+
+ icp_ocfDrvSwapBytes(pOutputData->pData, pOutputData->dataLenInBytes);
+
+ icp_ocfDrvFreeFlatBuffer(pOutputData);
+ memset(pDecryptData->pRecipientPrivateKey, 0,
+ sizeof(CpaCyRsaPrivateKey));
+ kmem_cache_free(drvRSAPrivateKey_zone,
+ pDecryptData->pRecipientPrivateKey);
+ memset(pDecryptData, 0, sizeof(CpaCyRsaDecryptOpData));
+ kmem_cache_free(drvRSADecrypt_zone, pDecryptData);
+
+ crypto_kdone(krp);
+
+ return;
+}
+
+/* Name : icp_ocfDrvDsaRSSignCallBack
+ *
+ * Description : When this function returns it signifies that the LAC
+ * component has completed the DSA RS sign operation.
+ */
+static void
+icp_ocfDrvDsaRSSignCallBack(void *callbackTag,
+ CpaStatus status,
+ void *pOpData,
+ CpaBoolean protocolStatus,
+ CpaFlatBuffer * pR, CpaFlatBuffer * pS)
+{
+ struct cryptkop *krp = NULL;
+ CpaCyDsaRSSignOpData *pSignData = NULL;
+
+ if (NULL == callbackTag) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "callbackTag data is NULL\n", __FUNCTION__);
+ return;
+ }
+
+ krp = (struct cryptkop *)callbackTag;
+
+ if (NULL == pOpData) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "Operation Data is NULL\n", __FUNCTION__);
+ krp->krp_status = ECANCELED;
+ crypto_kdone(krp);
+ return;
+ }
+ pSignData = (CpaCyDsaRSSignOpData *) pOpData;
+
+ if (NULL == pR) {
+ DPRINTK("%s(): Invalid input parameter - "
+ "pR sign is NULL\n", __FUNCTION__);
+ icp_ocfDrvFreeFlatBuffer(pS);
+ kmem_cache_free(drvDSARSSign_zone, pSignData);
+ krp->krp_status = ECANCELED;
+ crypto_kdone(krp);
+ return;
+ }
+
+ if (NULL == pS) {
+ DPRINTK("%s(): Invalid input parameter - "
+ "pS sign is NULL\n", __FUNCTION__);
+ icp_ocfDrvFreeFlatBuffer(pR);
+ kmem_cache_free(drvDSARSSign_zone, pSignData);
+ krp->krp_status = ECANCELED;
+ crypto_kdone(krp);
+ return;
+ }
+
+ if (CPA_STATUS_SUCCESS != status) {
+ APRINTK("%s(): LAC DSA RS Sign operation failed - "
+ "Operation Status = %d\n", __FUNCTION__, status);
+ krp->krp_status = ECANCELED;
+ } else {
+ krp->krp_status = CRYPTO_OP_SUCCESS;
+
+ if (CPA_TRUE != protocolStatus) {
+ DPRINTK("%s(): LAC DSA RS Sign operation failed due "
+ "to protocol error\n", __FUNCTION__);
+ krp->krp_status = EIO;
+ }
+ }
+
+ /* Swap bytes only when the callback status is successful and
+ protocolStatus is set to true */
+ if (CPA_STATUS_SUCCESS == status && CPA_TRUE == protocolStatus) {
+ icp_ocfDrvSwapBytes(pR->pData, pR->dataLenInBytes);
+ icp_ocfDrvSwapBytes(pS->pData, pS->dataLenInBytes);
+ }
+
+ icp_ocfDrvFreeFlatBuffer(pR);
+ icp_ocfDrvFreeFlatBuffer(pS);
+ memset(pSignData->K.pData, 0, pSignData->K.dataLenInBytes);
+ kmem_cache_free(drvDSARSSignKValue_zone, pSignData->K.pData);
+ memset(pSignData, 0, sizeof(CpaCyDsaRSSignOpData));
+ kmem_cache_free(drvDSARSSign_zone, pSignData);
+ crypto_kdone(krp);
+
+ return;
+}
+
+/* Name : icp_ocfDrvDsaVerifyCallback
+ *
+ * Description : When this function returns it signifies that the LAC
+ * component has completed the DSA Verify operation.
+ */
+static void
+icp_ocfDrvDsaVerifyCallBack(void *callbackTag,
+ CpaStatus status,
+ void *pOpData, CpaBoolean verifyStatus)
+{
+
+ struct cryptkop *krp = NULL;
+ CpaCyDsaVerifyOpData *pVerData = NULL;
+
+ if (NULL == callbackTag) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "callbackTag data is NULL\n", __FUNCTION__);
+ return;
+ }
+
+ krp = (struct cryptkop *)callbackTag;
+
+ if (NULL == pOpData) {
+ DPRINTK("%s(): Invalid input parameters - "
+ "Operation Data is NULL\n", __FUNCTION__);
+ krp->krp_status = ECANCELED;
+ crypto_kdone(krp);
+ return;
+ }
+ pVerData = (CpaCyDsaVerifyOpData *) pOpData;
+
+ if (CPA_STATUS_SUCCESS != status) {
+ APRINTK("%s(): LAC DSA Verify operation failed - "
+ "Operation Status = %d\n", __FUNCTION__, status);
+ krp->krp_status = ECANCELED;
+ } else {
+ krp->krp_status = CRYPTO_OP_SUCCESS;
+
+ if (CPA_TRUE != verifyStatus) {
+ DPRINTK("%s(): DSA signature invalid\n", __FUNCTION__);
+ krp->krp_status = EIO;
+ }
+ }
+
+ /* Swap bytes only when the callback status is successful and
+ verifyStatus is set to true */
+ /*Just swapping back the key values for now. Possibly all
+ swapped buffers need to be reverted */
+ if (CPA_STATUS_SUCCESS == status && CPA_TRUE == verifyStatus) {
+ icp_ocfDrvSwapBytes(pVerData->R.pData,
+ pVerData->R.dataLenInBytes);
+ icp_ocfDrvSwapBytes(pVerData->S.pData,
+ pVerData->S.dataLenInBytes);
+ }
+
+ memset(pVerData, 0, sizeof(CpaCyDsaVerifyOpData));
+ kmem_cache_free(drvDSAVerify_zone, pVerData);
+ crypto_kdone(krp);
+
+ return;
+}
diff --git a/crypto/ocf/ep80579/icp_common.c b/crypto/ocf/ep80579/icp_common.c
new file mode 100644
index 000000000000..461370c2c7c8
--- /dev/null
+++ b/crypto/ocf/ep80579/icp_common.c
@@ -0,0 +1,891 @@
+/***************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+ *
+ * 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, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR 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.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Corporation
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * version: Security.L.1.0.130
+ *
+ ***************************************************************************/
+
+/*
+ * An OCF module that uses Intel® QuickAssist Integrated Accelerator to do the
+ * crypto.
+ *
+ * This driver requires the ICP Access Library that is available from Intel in
+ * order to operate.
+ */
+
+#include "icp_ocf.h"
+
+#define ICP_OCF_COMP_NAME "ICP_OCF"
+#define ICP_OCF_VER_MAIN (2)
+#define ICP_OCF_VER_MJR (0)
+#define ICP_OCF_VER_MNR (0)
+
+#define MAX_DEREG_RETRIES (100)
+#define DEFAULT_DEREG_RETRIES (10)
+#define DEFAULT_DEREG_DELAY_IN_JIFFIES (10)
+
+/* This defines the maximum number of sessions possible between OCF
+ and the OCF Tolapai Driver. If set to zero, there is no limit. */
+#define DEFAULT_OCF_TO_DRV_MAX_SESSION_COUNT (0)
+#define NUM_SUPPORTED_CAPABILITIES (21)
+
+/*Slabs zones*/
+struct kmem_cache *drvSessionData_zone = NULL;
+struct kmem_cache *drvOpData_zone = NULL;
+struct kmem_cache *drvDH_zone = NULL;
+struct kmem_cache *drvLnModExp_zone = NULL;
+struct kmem_cache *drvRSADecrypt_zone = NULL;
+struct kmem_cache *drvRSAPrivateKey_zone = NULL;
+struct kmem_cache *drvDSARSSign_zone = NULL;
+struct kmem_cache *drvDSARSSignKValue_zone = NULL;
+struct kmem_cache *drvDSAVerify_zone = NULL;
+
+/*Slab zones for flatbuffers and bufferlist*/
+struct kmem_cache *drvFlatBuffer_zone = NULL;
+
+static int icp_ocfDrvInit(void);
+static void icp_ocfDrvExit(void);
+static void icp_ocfDrvFreeCaches(void);
+static void icp_ocfDrvDeferedFreeLacSessionProcess(void *arg);
+
+int32_t icp_ocfDrvDriverId = INVALID_DRIVER_ID;
+
+/* Module parameter - gives the number of times LAC deregistration shall be
+ re-tried */
+int num_dereg_retries = DEFAULT_DEREG_RETRIES;
+
+/* Module parameter - gives the delay time in jiffies before a LAC session
+ shall be attempted to be deregistered again */
+int dereg_retry_delay_in_jiffies = DEFAULT_DEREG_DELAY_IN_JIFFIES;
+
+/* Module parameter - gives the maximum number of sessions possible between
+ OCF and the OCF Tolapai Driver. If set to zero, there is no limit.*/
+int max_sessions = DEFAULT_OCF_TO_DRV_MAX_SESSION_COUNT;
+
+/* This is set when the module is removed from the system, no further
+ processing can take place if this is set */
+atomic_t icp_ocfDrvIsExiting = ATOMIC_INIT(0);
+
+/* This is used to show how many lac sessions were not deregistered*/
+atomic_t lac_session_failed_dereg_count = ATOMIC_INIT(0);
+
+/* This is used to track the number of registered sessions between OCF and
+ * and the OCF Tolapai driver, when max_session is set to value other than
+ * zero. This ensures that the max_session set for the OCF and the driver
+ * is equal to the LAC registered sessions */
+atomic_t num_ocf_to_drv_registered_sessions = ATOMIC_INIT(0);
+
+/* Head of linked list used to store session data */
+struct list_head icp_ocfDrvGlobalSymListHead;
+struct list_head icp_ocfDrvGlobalSymListHead_FreeMemList;
+
+spinlock_t icp_ocfDrvSymSessInfoListSpinlock = SPIN_LOCK_UNLOCKED;
+rwlock_t icp_kmem_cache_destroy_alloc_lock = RW_LOCK_UNLOCKED;
+
+struct workqueue_struct *icp_ocfDrvFreeLacSessionWorkQ;
+
+struct icp_drvBuffListInfo defBuffListInfo;
+
+static struct {
+ softc_device_decl sc_dev;
+} icpDev;
+
+static device_method_t icp_methods = {
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, icp_ocfDrvNewSession),
+ DEVMETHOD(cryptodev_freesession, icp_ocfDrvFreeLACSession),
+ DEVMETHOD(cryptodev_process, icp_ocfDrvSymProcess),
+ DEVMETHOD(cryptodev_kprocess, icp_ocfDrvPkeProcess),
+};
+
+module_param(num_dereg_retries, int, S_IRUGO);
+module_param(dereg_retry_delay_in_jiffies, int, S_IRUGO);
+module_param(max_sessions, int, S_IRUGO);
+
+MODULE_PARM_DESC(num_dereg_retries,
+ "Number of times to retry LAC Sym Session Deregistration. "
+ "Default 10, Max 100");
+MODULE_PARM_DESC(dereg_retry_delay_in_jiffies, "Delay in jiffies "
+ "(added to a schedule() function call) before a LAC Sym "
+ "Session Dereg is retried. Default 10");
+MODULE_PARM_DESC(max_sessions, "This sets the maximum number of sessions "
+ "between OCF and this driver. If this value is set to zero, "
+ "max session count checking is disabled. Default is zero(0)");
+
+/* Name : icp_ocfDrvInit
+ *
+ * Description : This function will register all the symmetric and asymmetric
+ * functionality that will be accelerated by the hardware. It will also
+ * get a unique driver ID from the OCF and initialise all slab caches
+ */
+static int __init icp_ocfDrvInit(void)
+{
+ int ocfStatus = 0;
+
+ IPRINTK("=== %s ver %d.%d.%d ===\n", ICP_OCF_COMP_NAME,
+ ICP_OCF_VER_MAIN, ICP_OCF_VER_MJR, ICP_OCF_VER_MNR);
+
+ if (MAX_DEREG_RETRIES < num_dereg_retries) {
+ EPRINTK("Session deregistration retry count set to greater "
+ "than %d", MAX_DEREG_RETRIES);
+ return -1;
+ }
+
+ /* Initialize and Start the Cryptographic component */
+ if (CPA_STATUS_SUCCESS !=
+ cpaCyStartInstance(CPA_INSTANCE_HANDLE_SINGLE)) {
+ EPRINTK("Failed to initialize and start the instance "
+ "of the Cryptographic component.\n");
+ return -1;
+ }
+
+ /* Set the default size of BufferList to allocate */
+ memset(&defBuffListInfo, 0, sizeof(struct icp_drvBuffListInfo));
+ if (ICP_OCF_DRV_STATUS_SUCCESS !=
+ icp_ocfDrvBufferListMemInfo(ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS,
+ &defBuffListInfo)) {
+ EPRINTK("Failed to get bufferlist memory info.\n");
+ return -1;
+ }
+
+ /*Register OCF Tolapai Driver with OCF */
+ memset(&icpDev, 0, sizeof(icpDev));
+ softc_device_init(&icpDev, "icp", 0, icp_methods);
+
+ icp_ocfDrvDriverId = crypto_get_driverid(softc_get_device(&icpDev),
+ CRYPTOCAP_F_HARDWARE);
+
+ if (icp_ocfDrvDriverId < 0) {
+ EPRINTK("%s : ICP driver failed to register with OCF!\n",
+ __FUNCTION__);
+ return -ENODEV;
+ }
+
+ /*Create all the slab caches used by the OCF Tolapai Driver */
+ drvSessionData_zone =
+ ICP_CACHE_CREATE("ICP Session Data", struct icp_drvSessionData);
+ ICP_CACHE_NULL_CHECK(drvSessionData_zone);
+
+ /*
+ * Allocation of the OpData includes the allocation space for meta data.
+ * The memory after the opData structure is reserved for this meta data.
+ */
+ drvOpData_zone =
+ kmem_cache_create("ICP Op Data", sizeof(struct icp_drvOpData) +
+ defBuffListInfo.metaSize ,0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+
+
+ ICP_CACHE_NULL_CHECK(drvOpData_zone);
+
+ drvDH_zone = ICP_CACHE_CREATE("ICP DH data", CpaCyDhPhase1KeyGenOpData);
+ ICP_CACHE_NULL_CHECK(drvDH_zone);
+
+ drvLnModExp_zone =
+ ICP_CACHE_CREATE("ICP ModExp data", CpaCyLnModExpOpData);
+ ICP_CACHE_NULL_CHECK(drvLnModExp_zone);
+
+ drvRSADecrypt_zone =
+ ICP_CACHE_CREATE("ICP RSA decrypt data", CpaCyRsaDecryptOpData);
+ ICP_CACHE_NULL_CHECK(drvRSADecrypt_zone);
+
+ drvRSAPrivateKey_zone =
+ ICP_CACHE_CREATE("ICP RSA private key data", CpaCyRsaPrivateKey);
+ ICP_CACHE_NULL_CHECK(drvRSAPrivateKey_zone);
+
+ drvDSARSSign_zone =
+ ICP_CACHE_CREATE("ICP DSA Sign", CpaCyDsaRSSignOpData);
+ ICP_CACHE_NULL_CHECK(drvDSARSSign_zone);
+
+ /*too awkward to use a macro here */
+ drvDSARSSignKValue_zone =
+ kmem_cache_create("ICP DSA Sign Rand Val",
+ DSA_RS_SIGN_PRIMEQ_SIZE_IN_BYTES, 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ ICP_CACHE_NULL_CHECK(drvDSARSSignKValue_zone);
+
+ drvDSAVerify_zone =
+ ICP_CACHE_CREATE("ICP DSA Verify", CpaCyDsaVerifyOpData);
+ ICP_CACHE_NULL_CHECK(drvDSAVerify_zone);
+
+ drvFlatBuffer_zone =
+ ICP_CACHE_CREATE("ICP Flat Buffers", CpaFlatBuffer);
+ ICP_CACHE_NULL_CHECK(drvFlatBuffer_zone);
+
+ /* Register the ICP symmetric crypto support. */
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_NULL_CBC);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_DES_CBC);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_3DES_CBC);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_AES_CBC);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_ARC4);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_MD5);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_MD5_HMAC);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA1);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA1_HMAC);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_256);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_256_HMAC);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_384);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_384_HMAC);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_512);
+ ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(CRYPTO_SHA2_512_HMAC);
+
+ /* Register the ICP asymmetric algorithm support */
+ ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(CRK_DH_COMPUTE_KEY);
+ ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(CRK_MOD_EXP);
+ ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(CRK_MOD_EXP_CRT);
+ ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(CRK_DSA_SIGN);
+ ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(CRK_DSA_VERIFY);
+
+ /* Register the ICP random number generator support */
+ if (OCF_REGISTRATION_STATUS_SUCCESS ==
+ crypto_rregister(icp_ocfDrvDriverId, icp_ocfDrvReadRandom, NULL)) {
+ ocfStatus++;
+ }
+
+ if (OCF_ZERO_FUNCTIONALITY_REGISTERED == ocfStatus) {
+ DPRINTK("%s: Failed to register any device capabilities\n",
+ __FUNCTION__);
+ icp_ocfDrvFreeCaches();
+ icp_ocfDrvDriverId = INVALID_DRIVER_ID;
+ return -ECANCELED;
+ }
+
+ DPRINTK("%s: Registered %d of %d device capabilities\n",
+ __FUNCTION__, ocfStatus, NUM_SUPPORTED_CAPABILITIES);
+
+/*Session data linked list used during module exit*/
+ INIT_LIST_HEAD(&icp_ocfDrvGlobalSymListHead);
+ INIT_LIST_HEAD(&icp_ocfDrvGlobalSymListHead_FreeMemList);
+
+ icp_ocfDrvFreeLacSessionWorkQ =
+ create_singlethread_workqueue("ocfLacDeregWorkQueue");
+
+ return 0;
+}
+
+/* Name : icp_ocfDrvExit
+ *
+ * Description : This function will deregister all the symmetric sessions
+ * registered with the LAC component. It will also deregister all symmetric
+ * and asymmetric functionality that can be accelerated by the hardware via OCF
+ * and random number generation if it is enabled.
+ */
+static void icp_ocfDrvExit(void)
+{
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+ struct icp_drvSessionData *sessionData = NULL;
+ struct icp_drvSessionData *tempSessionData = NULL;
+ int i, remaining_delay_time_in_jiffies = 0;
+ /* There is a possibility of a process or new session command being */
+ /* sent before this variable is incremented. The aim of this variable */
+ /* is to stop a loop of calls creating a deadlock situation which */
+ /* would prevent the driver from exiting. */
+
+ atomic_inc(&icp_ocfDrvIsExiting);
+
+ /*Existing sessions will be routed to another driver after these calls */
+ crypto_unregister_all(icp_ocfDrvDriverId);
+ crypto_runregister_all(icp_ocfDrvDriverId);
+
+ /*If any sessions are waiting to be deregistered, do that. This also
+ flushes the work queue */
+ destroy_workqueue(icp_ocfDrvFreeLacSessionWorkQ);
+
+ /*ENTER CRITICAL SECTION */
+ spin_lock_bh(&icp_ocfDrvSymSessInfoListSpinlock);
+ list_for_each_entry_safe(tempSessionData, sessionData,
+ &icp_ocfDrvGlobalSymListHead, listNode) {
+ for (i = 0; i < num_dereg_retries; i++) {
+ /*No harm if bad input - LAC will handle error cases */
+ if (ICP_SESSION_RUNNING == tempSessionData->inUse) {
+ lacStatus =
+ cpaCySymRemoveSession
+ (CPA_INSTANCE_HANDLE_SINGLE,
+ tempSessionData->sessHandle);
+ if (CPA_STATUS_SUCCESS == lacStatus) {
+ /* Succesfully deregistered */
+ break;
+ } else if (CPA_STATUS_RETRY != lacStatus) {
+ atomic_inc
+ (&lac_session_failed_dereg_count);
+ break;
+ }
+
+ /*schedule_timout returns the time left for completion if
+ * this task is set to TASK_INTERRUPTIBLE */
+ remaining_delay_time_in_jiffies =
+ dereg_retry_delay_in_jiffies;
+ while (0 > remaining_delay_time_in_jiffies) {
+ remaining_delay_time_in_jiffies =
+ schedule_timeout
+ (remaining_delay_time_in_jiffies);
+ }
+
+ DPRINTK
+ ("%s(): Retry %d to deregistrate the session\n",
+ __FUNCTION__, i);
+ }
+ }
+
+ /*remove from current list */
+ list_del(&(tempSessionData->listNode));
+ /*add to free mem linked list */
+ list_add(&(tempSessionData->listNode),
+ &icp_ocfDrvGlobalSymListHead_FreeMemList);
+
+ }
+
+ /*EXIT CRITICAL SECTION */
+ spin_unlock_bh(&icp_ocfDrvSymSessInfoListSpinlock);
+
+ /*set back to initial values */
+ sessionData = NULL;
+ /*still have a reference in our list! */
+ tempSessionData = NULL;
+ /*free memory */
+ list_for_each_entry_safe(tempSessionData, sessionData,
+ &icp_ocfDrvGlobalSymListHead_FreeMemList,
+ listNode) {
+
+ list_del(&(tempSessionData->listNode));
+ /* Free allocated CpaCySymSessionCtx */
+ if (NULL != tempSessionData->sessHandle) {
+ kfree(tempSessionData->sessHandle);
+ }
+ memset(tempSessionData, 0, sizeof(struct icp_drvSessionData));
+ kmem_cache_free(drvSessionData_zone, tempSessionData);
+ }
+
+ if (0 != atomic_read(&lac_session_failed_dereg_count)) {
+ DPRINTK("%s(): %d LAC sessions were not deregistered "
+ "correctly. This is not a clean exit! \n",
+ __FUNCTION__,
+ atomic_read(&lac_session_failed_dereg_count));
+ }
+
+ icp_ocfDrvFreeCaches();
+ icp_ocfDrvDriverId = INVALID_DRIVER_ID;
+
+ /* Shutdown the Cryptographic component */
+ lacStatus = cpaCyStopInstance(CPA_INSTANCE_HANDLE_SINGLE);
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ DPRINTK("%s(): Failed to stop instance of the "
+ "Cryptographic component.(status == %d)\n",
+ __FUNCTION__, lacStatus);
+ }
+
+}
+
+/* Name : icp_ocfDrvFreeCaches
+ *
+ * Description : This function deregisters all slab caches
+ */
+static void icp_ocfDrvFreeCaches(void)
+{
+ if (atomic_read(&icp_ocfDrvIsExiting) != CPA_TRUE) {
+ atomic_set(&icp_ocfDrvIsExiting, 1);
+ }
+
+ /*Sym Zones */
+ ICP_CACHE_DESTROY(drvSessionData_zone);
+ ICP_CACHE_DESTROY(drvOpData_zone);
+
+ /*Asym zones */
+ ICP_CACHE_DESTROY(drvDH_zone);
+ ICP_CACHE_DESTROY(drvLnModExp_zone);
+ ICP_CACHE_DESTROY(drvRSADecrypt_zone);
+ ICP_CACHE_DESTROY(drvRSAPrivateKey_zone);
+ ICP_CACHE_DESTROY(drvDSARSSignKValue_zone);
+ ICP_CACHE_DESTROY(drvDSARSSign_zone);
+ ICP_CACHE_DESTROY(drvDSAVerify_zone);
+
+ /*FlatBuffer and BufferList Zones */
+ ICP_CACHE_DESTROY(drvFlatBuffer_zone);
+
+}
+
+/* Name : icp_ocfDrvDeregRetry
+ *
+ * Description : This function will try to farm the session deregistration
+ * off to a work queue. If it fails, nothing more can be done and it
+ * returns an error
+ */
+
+int icp_ocfDrvDeregRetry(CpaCySymSessionCtx sessionToDeregister)
+{
+ struct icp_ocfDrvFreeLacSession *workstore = NULL;
+
+ DPRINTK("%s(): Retry - Deregistering session (%p)\n",
+ __FUNCTION__, sessionToDeregister);
+
+ /*make sure the session is not available to be allocated during this
+ process */
+ atomic_inc(&lac_session_failed_dereg_count);
+
+ /*Farm off to work queue */
+ workstore =
+ kmalloc(sizeof(struct icp_ocfDrvFreeLacSession), GFP_ATOMIC);
+ if (NULL == workstore) {
+ DPRINTK("%s(): unable to free session - no memory available "
+ "for work queue\n", __FUNCTION__);
+ return ENOMEM;
+ }
+
+ workstore->sessionToDeregister = sessionToDeregister;
+
+ INIT_WORK(&(workstore->work), icp_ocfDrvDeferedFreeLacSessionProcess,
+ workstore);
+ queue_work(icp_ocfDrvFreeLacSessionWorkQ, &(workstore->work));
+
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+
+}
+
+/* Name : icp_ocfDrvDeferedFreeLacSessionProcess
+ *
+ * Description : This function will retry (module input parameter)
+ * 'num_dereg_retries' times to deregister any symmetric session that recieves a
+ * CPA_STATUS_RETRY message from the LAC component. This function is run in
+ * Thread context because it is called from a worker thread
+ */
+static void icp_ocfDrvDeferedFreeLacSessionProcess(void *arg)
+{
+ struct icp_ocfDrvFreeLacSession *workstore = NULL;
+ CpaCySymSessionCtx sessionToDeregister = NULL;
+ int i = 0;
+ int remaining_delay_time_in_jiffies = 0;
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+
+ workstore = (struct icp_ocfDrvFreeLacSession *)arg;
+ if (NULL == workstore) {
+ DPRINTK("%s() function called with null parameter \n",
+ __FUNCTION__);
+ return;
+ }
+
+ sessionToDeregister = workstore->sessionToDeregister;
+ kfree(workstore);
+
+ /*if exiting, give deregistration one more blast only */
+ if (atomic_read(&icp_ocfDrvIsExiting) == CPA_TRUE) {
+ lacStatus = cpaCySymRemoveSession(CPA_INSTANCE_HANDLE_SINGLE,
+ sessionToDeregister);
+
+ if (lacStatus != CPA_STATUS_SUCCESS) {
+ DPRINTK("%s() Failed to Dereg LAC session %p "
+ "during module exit\n", __FUNCTION__,
+ sessionToDeregister);
+ return;
+ }
+
+ atomic_dec(&lac_session_failed_dereg_count);
+ return;
+ }
+
+ for (i = 0; i <= num_dereg_retries; i++) {
+ lacStatus = cpaCySymRemoveSession(CPA_INSTANCE_HANDLE_SINGLE,
+ sessionToDeregister);
+
+ if (lacStatus == CPA_STATUS_SUCCESS) {
+ atomic_dec(&lac_session_failed_dereg_count);
+ return;
+ }
+ if (lacStatus != CPA_STATUS_RETRY) {
+ DPRINTK("%s() Failed to deregister session - lacStatus "
+ " = %d", __FUNCTION__, lacStatus);
+ break;
+ }
+
+ /*schedule_timout returns the time left for completion if this
+ task is set to TASK_INTERRUPTIBLE */
+ remaining_delay_time_in_jiffies = dereg_retry_delay_in_jiffies;
+ while (0 > remaining_delay_time_in_jiffies) {
+ remaining_delay_time_in_jiffies =
+ schedule_timeout(remaining_delay_time_in_jiffies);
+ }
+
+ }
+
+ DPRINTK("%s(): Unable to deregister session\n", __FUNCTION__);
+ DPRINTK("%s(): Number of unavailable LAC sessions = %d\n", __FUNCTION__,
+ atomic_read(&lac_session_failed_dereg_count));
+}
+
+/* Name : icp_ocfDrvPtrAndLenToFlatBuffer
+ *
+ * Description : This function converts a "pointer and length" buffer
+ * structure to Fredericksburg Flat Buffer (CpaFlatBuffer) format.
+ *
+ * This function assumes that the data passed in are valid.
+ */
+inline void
+icp_ocfDrvPtrAndLenToFlatBuffer(void *pData, uint32_t len,
+ CpaFlatBuffer * pFlatBuffer)
+{
+ pFlatBuffer->pData = pData;
+ pFlatBuffer->dataLenInBytes = len;
+}
+
+/* Name : icp_ocfDrvSingleSkBuffToFlatBuffer
+ *
+ * Description : This function converts a single socket buffer (sk_buff)
+ * structure to a Fredericksburg Flat Buffer (CpaFlatBuffer) format.
+ *
+ * This function assumes that the data passed in are valid.
+ */
+static inline void
+icp_ocfDrvSingleSkBuffToFlatBuffer(struct sk_buff *pSkb,
+ CpaFlatBuffer * pFlatBuffer)
+{
+ pFlatBuffer->pData = pSkb->data;
+ pFlatBuffer->dataLenInBytes = skb_headlen(pSkb);
+}
+
+/* Name : icp_ocfDrvSkBuffToBufferList
+ *
+ * Description : This function converts a socket buffer (sk_buff) structure to
+ * Fredericksburg Scatter/Gather (CpaBufferList) buffer format.
+ *
+ * This function assumes that the bufferlist has been allocated with the correct
+ * number of buffer arrays.
+ *
+ */
+inline int
+icp_ocfDrvSkBuffToBufferList(struct sk_buff *pSkb, CpaBufferList * bufferList)
+{
+ CpaFlatBuffer *curFlatBuffer = NULL;
+ char *skbuffPageAddr = NULL;
+ struct sk_buff *pCurFrag = NULL;
+ struct skb_shared_info *pShInfo = NULL;
+ uint32_t page_offset = 0, i = 0;
+
+ DPRINTK("%s(): Entry Point\n", __FUNCTION__);
+
+ /*
+ * In all cases, the first skb needs to be translated to FlatBuffer.
+ * Perform a buffer translation for the first skbuff
+ */
+ curFlatBuffer = bufferList->pBuffers;
+ icp_ocfDrvSingleSkBuffToFlatBuffer(pSkb, curFlatBuffer);
+
+ /* Set the userData to point to the original sk_buff */
+ bufferList->pUserData = (void *)pSkb;
+
+ /* We now know we'll have at least one element in the SGL */
+ bufferList->numBuffers = 1;
+
+ if (0 == skb_is_nonlinear(pSkb)) {
+ /* Is a linear buffer - therefore it's a single skbuff */
+ DPRINTK("%s(): Exit Point\n", __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+ }
+
+ curFlatBuffer++;
+ pShInfo = skb_shinfo(pSkb);
+ if (pShInfo->frag_list != NULL && pShInfo->nr_frags != 0) {
+ EPRINTK("%s():"
+ "Translation for a combination of frag_list "
+ "and frags[] array not supported!\n", __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_FAIL;
+ } else if (pShInfo->frag_list != NULL) {
+ /*
+ * Non linear skbuff supported through frag_list
+ * Perform translation for each fragment (sk_buff)
+ * in the frag_list of the first sk_buff.
+ */
+ for (pCurFrag = pShInfo->frag_list;
+ pCurFrag != NULL; pCurFrag = pCurFrag->next) {
+ icp_ocfDrvSingleSkBuffToFlatBuffer(pCurFrag,
+ curFlatBuffer);
+ curFlatBuffer++;
+ bufferList->numBuffers++;
+ }
+ } else if (pShInfo->nr_frags != 0) {
+ /*
+ * Perform translation for each fragment in frags array
+ * and add to the BufferList
+ */
+ for (i = 0; i < pShInfo->nr_frags; i++) {
+ /* Get the page address and offset of this frag */
+ skbuffPageAddr = (char *)pShInfo->frags[i].page;
+ page_offset = pShInfo->frags[i].page_offset;
+
+ /* Convert a pointer and length to a flat buffer */
+ icp_ocfDrvPtrAndLenToFlatBuffer(skbuffPageAddr +
+ page_offset,
+ pShInfo->frags[i].size,
+ curFlatBuffer);
+ curFlatBuffer++;
+ bufferList->numBuffers++;
+ }
+ } else {
+ EPRINTK("%s():" "Could not recognize skbuff fragments!\n",
+ __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_FAIL;
+ }
+
+ DPRINTK("%s(): Exit Point\n", __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+}
+
+/* Name : icp_ocfDrvBufferListToSkBuff
+ *
+ * Description : This function converts a Fredericksburg Scatter/Gather
+ * (CpaBufferList) buffer format to socket buffer structure.
+ */
+inline int
+icp_ocfDrvBufferListToSkBuff(CpaBufferList * bufferList, struct sk_buff **skb)
+{
+ DPRINTK("%s(): Entry Point\n", __FUNCTION__);
+
+ /* Retrieve the orignal skbuff */
+ *skb = (struct sk_buff *)bufferList->pUserData;
+ if (NULL == *skb) {
+ EPRINTK("%s():"
+ "Error on converting from a BufferList. "
+ "The BufferList does not contain an sk_buff.\n",
+ __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_FAIL;
+ }
+ DPRINTK("%s(): Exit Point\n", __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+}
+
+/* Name : icp_ocfDrvPtrAndLenToBufferList
+ *
+ * Description : This function converts a "pointer and length" buffer
+ * structure to Fredericksburg Scatter/Gather Buffer (CpaBufferList) format.
+ *
+ * This function assumes that the data passed in are valid.
+ */
+inline void
+icp_ocfDrvPtrAndLenToBufferList(void *pDataIn, uint32_t length,
+ CpaBufferList * pBufferList)
+{
+ pBufferList->numBuffers = 1;
+ pBufferList->pBuffers->pData = pDataIn;
+ pBufferList->pBuffers->dataLenInBytes = length;
+}
+
+/* Name : icp_ocfDrvBufferListToPtrAndLen
+ *
+ * Description : This function converts Fredericksburg Scatter/Gather Buffer
+ * (CpaBufferList) format to a "pointer and length" buffer structure.
+ *
+ * This function assumes that the data passed in are valid.
+ */
+inline void
+icp_ocfDrvBufferListToPtrAndLen(CpaBufferList * pBufferList,
+ void **ppDataOut, uint32_t * pLength)
+{
+ *ppDataOut = pBufferList->pBuffers->pData;
+ *pLength = pBufferList->pBuffers->dataLenInBytes;
+}
+
+/* Name : icp_ocfDrvBufferListMemInfo
+ *
+ * Description : This function will set the number of flat buffers in
+ * bufferlist, the size of memory to allocate for the pPrivateMetaData
+ * member of the CpaBufferList.
+ */
+int
+icp_ocfDrvBufferListMemInfo(uint16_t numBuffers,
+ struct icp_drvBuffListInfo *buffListInfo)
+{
+ buffListInfo->numBuffers = numBuffers;
+
+ if (CPA_STATUS_SUCCESS !=
+ cpaCyBufferListGetMetaSize(CPA_INSTANCE_HANDLE_SINGLE,
+ buffListInfo->numBuffers,
+ &(buffListInfo->metaSize))) {
+ EPRINTK("%s() Failed to get buffer list meta size.\n",
+ __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_FAIL;
+ }
+
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+}
+
+/* Name : icp_ocfDrvGetSkBuffFrags
+ *
+ * Description : This function will determine the number of
+ * fragments in a socket buffer(sk_buff).
+ */
+inline uint16_t icp_ocfDrvGetSkBuffFrags(struct sk_buff * pSkb)
+{
+ uint16_t numFrags = 0;
+ struct sk_buff *pCurFrag = NULL;
+ struct skb_shared_info *pShInfo = NULL;
+
+ if (NULL == pSkb)
+ return 0;
+
+ numFrags = 1;
+ if (0 == skb_is_nonlinear(pSkb)) {
+ /* Linear buffer - it's a single skbuff */
+ return numFrags;
+ }
+
+ pShInfo = skb_shinfo(pSkb);
+ if (NULL != pShInfo->frag_list && 0 != pShInfo->nr_frags) {
+ EPRINTK("%s(): Combination of frag_list "
+ "and frags[] array not supported!\n", __FUNCTION__);
+ return 0;
+ } else if (0 != pShInfo->nr_frags) {
+ numFrags += pShInfo->nr_frags;
+ return numFrags;
+ } else if (NULL != pShInfo->frag_list) {
+ for (pCurFrag = pShInfo->frag_list;
+ pCurFrag != NULL; pCurFrag = pCurFrag->next) {
+ numFrags++;
+ }
+ return numFrags;
+ } else {
+ return 0;
+ }
+}
+
+/* Name : icp_ocfDrvFreeFlatBuffer
+ *
+ * Description : This function will deallocate flat buffer.
+ */
+inline void icp_ocfDrvFreeFlatBuffer(CpaFlatBuffer * pFlatBuffer)
+{
+ if (pFlatBuffer != NULL) {
+ memset(pFlatBuffer, 0, sizeof(CpaFlatBuffer));
+ kmem_cache_free(drvFlatBuffer_zone, pFlatBuffer);
+ }
+}
+
+/* Name : icp_ocfDrvAllocMetaData
+ *
+ * Description : This function will allocate memory for the
+ * pPrivateMetaData member of CpaBufferList.
+ */
+inline int
+icp_ocfDrvAllocMetaData(CpaBufferList * pBufferList,
+ const struct icp_drvOpData *pOpData)
+{
+ Cpa32U metaSize = 0;
+
+ if (pBufferList->numBuffers <= ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS){
+ void *pOpDataStartAddr = (void *)pOpData;
+
+ if (0 == defBuffListInfo.metaSize) {
+ pBufferList->pPrivateMetaData = NULL;
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+ }
+ /*
+ * The meta data allocation has been included as part of the
+ * op data. It has been pre-allocated in memory just after the
+ * icp_drvOpData structure.
+ */
+ pBufferList->pPrivateMetaData = pOpDataStartAddr +
+ sizeof(struct icp_drvOpData);
+ } else {
+ if (CPA_STATUS_SUCCESS !=
+ cpaCyBufferListGetMetaSize(CPA_INSTANCE_HANDLE_SINGLE,
+ pBufferList->numBuffers,
+ &metaSize)) {
+ EPRINTK("%s() Failed to get buffer list meta size.\n",
+ __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_FAIL;
+ }
+
+ if (0 == metaSize) {
+ pBufferList->pPrivateMetaData = NULL;
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+ }
+
+ pBufferList->pPrivateMetaData = kmalloc(metaSize, GFP_ATOMIC);
+ }
+ if (NULL == pBufferList->pPrivateMetaData) {
+ EPRINTK("%s() Failed to allocate pPrivateMetaData.\n",
+ __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_FAIL;
+ }
+
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+}
+
+/* Name : icp_ocfDrvFreeMetaData
+ *
+ * Description : This function will deallocate pPrivateMetaData memory.
+ */
+inline void icp_ocfDrvFreeMetaData(CpaBufferList * pBufferList)
+{
+ if (NULL == pBufferList->pPrivateMetaData) {
+ return;
+ }
+
+ /*
+ * Only free the meta data if the BufferList has more than
+ * ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS number of buffers.
+ * Otherwise, the meta data shall be freed when the icp_drvOpData is
+ * freed.
+ */
+ if (ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS < pBufferList->numBuffers){
+ kfree(pBufferList->pPrivateMetaData);
+ }
+}
+
+module_init(icp_ocfDrvInit);
+module_exit(icp_ocfDrvExit);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Intel");
+MODULE_DESCRIPTION("OCF Driver for Intel Quick Assist crypto acceleration");
diff --git a/crypto/ocf/ep80579/icp_ocf.h b/crypto/ocf/ep80579/icp_ocf.h
new file mode 100644
index 000000000000..dff0a8b7d461
--- /dev/null
+++ b/crypto/ocf/ep80579/icp_ocf.h
@@ -0,0 +1,363 @@
+/***************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+ *
+ * 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, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR 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.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Corporation
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * version: Security.L.1.0.130
+ *
+ ***************************************************************************/
+
+/*
+ * OCF drv driver header file for the Intel ICP processor.
+ */
+
+#ifndef ICP_OCF_H
+#define ICP_OCF_H
+
+#include <linux/crypto.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+
+#include "cryptodev.h"
+#include "uio.h"
+
+#include "cpa.h"
+#include "cpa_cy_im.h"
+#include "cpa_cy_sym.h"
+#include "cpa_cy_rand.h"
+#include "cpa_cy_dh.h"
+#include "cpa_cy_rsa.h"
+#include "cpa_cy_ln.h"
+#include "cpa_cy_common.h"
+#include "cpa_cy_dsa.h"
+
+#define NUM_BITS_IN_BYTE (8)
+#define NUM_BITS_IN_BYTE_MINUS_ONE (NUM_BITS_IN_BYTE -1)
+#define INVALID_DRIVER_ID (-1)
+#define RETURN_RAND_NUM_GEN_FAILED (-1)
+
+/*This is define means only one operation can be chained to another
+(resulting in one chain of two operations)*/
+#define MAX_NUM_OF_CHAINED_OPS (1)
+/*This is the max block cipher initialisation vector*/
+#define MAX_IV_LEN_IN_BYTES (20)
+/*This is used to check whether the OCF to this driver session limit has
+ been disabled*/
+#define NO_OCF_TO_DRV_MAX_SESSIONS (0)
+
+/*OCF values mapped here*/
+#define ICP_SHA1_DIGEST_SIZE_IN_BYTES (SHA1_HASH_LEN)
+#define ICP_SHA256_DIGEST_SIZE_IN_BYTES (SHA2_256_HASH_LEN)
+#define ICP_SHA384_DIGEST_SIZE_IN_BYTES (SHA2_384_HASH_LEN)
+#define ICP_SHA512_DIGEST_SIZE_IN_BYTES (SHA2_512_HASH_LEN)
+#define ICP_MD5_DIGEST_SIZE_IN_BYTES (MD5_HASH_LEN)
+#define ARC4_COUNTER_LEN (ARC4_BLOCK_LEN)
+
+#define OCF_REGISTRATION_STATUS_SUCCESS (0)
+#define OCF_ZERO_FUNCTIONALITY_REGISTERED (0)
+#define ICP_OCF_DRV_NO_CRYPTO_PROCESS_ERROR (0)
+#define ICP_OCF_DRV_STATUS_SUCCESS (0)
+#define ICP_OCF_DRV_STATUS_FAIL (1)
+
+/*Turn on/off debug options*/
+#define ICP_OCF_PRINT_DEBUG_MESSAGES (0)
+#define ICP_OCF_PRINT_KERN_ALERT (1)
+#define ICP_OCF_PRINT_KERN_ERRS (1)
+
+/*DSA Prime Q size in bytes (as defined in the standard) */
+#define DSA_RS_SIGN_PRIMEQ_SIZE_IN_BYTES (20)
+
+/*MACRO DEFINITIONS*/
+
+#define BITS_TO_BYTES(bytes, bits) \
+ bytes = (bits + NUM_BITS_IN_BYTE_MINUS_ONE) / NUM_BITS_IN_BYTE
+
+#define ICP_CACHE_CREATE(cache_ID, cache_name) \
+ kmem_cache_create(cache_ID, sizeof(cache_name),0, \
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+
+#define ICP_CACHE_NULL_CHECK(slab_zone) \
+{ \
+ if(NULL == slab_zone){ \
+ icp_ocfDrvFreeCaches(); \
+ EPRINTK("%s() line %d: Not enough memory!\n", \
+ __FUNCTION__, __LINE__); \
+ return ENOMEM; \
+ } \
+}
+
+#define ICP_CACHE_DESTROY(slab_zone) \
+{ \
+ if(NULL != slab_zone){ \
+ kmem_cache_destroy(slab_zone); \
+ slab_zone = NULL; \
+ } \
+}
+
+#define ICP_REGISTER_SYM_FUNCTIONALITY_WITH_OCF(alg) \
+{ \
+ if(OCF_REGISTRATION_STATUS_SUCCESS == \
+ crypto_register(icp_ocfDrvDriverId, \
+ alg, \
+ 0, \
+ 0)) { \
+ ocfStatus++; \
+ } \
+}
+
+#define ICP_REGISTER_ASYM_FUNCTIONALITY_WITH_OCF(alg) \
+{ \
+ if(OCF_REGISTRATION_STATUS_SUCCESS == \
+ crypto_kregister(icp_ocfDrvDriverId, \
+ alg, \
+ 0)){ \
+ ocfStatus++; \
+ } \
+}
+
+#if ICP_OCF_PRINT_DEBUG_MESSAGES == 1
+#define DPRINTK(args...) \
+{ \
+ printk(args); \
+}
+
+#else //ICP_OCF_PRINT_DEBUG_MESSAGES == 1
+
+#define DPRINTK(args...)
+
+#endif //ICP_OCF_PRINT_DEBUG_MESSAGES == 1
+
+#if ICP_OCF_PRINT_KERN_ALERT == 1
+#define APRINTK(args...) \
+{ \
+ printk(KERN_ALERT args); \
+}
+
+#else //ICP_OCF_PRINT_KERN_ALERT == 1
+
+#define APRINTK(args...)
+
+#endif //ICP_OCF_PRINT_KERN_ALERT == 1
+
+#if ICP_OCF_PRINT_KERN_ERRS == 1
+#define EPRINTK(args...) \
+{ \
+ printk(KERN_ERR args); \
+}
+
+#else //ICP_OCF_PRINT_KERN_ERRS == 1
+
+#define EPRINTK(args...)
+
+#endif //ICP_OCF_PRINT_KERN_ERRS == 1
+
+#define IPRINTK(args...) \
+{ \
+ printk(KERN_INFO args); \
+}
+
+/*END OF MACRO DEFINITIONS*/
+
+typedef enum {
+ ICP_OCF_DRV_ALG_CIPHER = 0,
+ ICP_OCF_DRV_ALG_HASH
+} icp_ocf_drv_alg_type_t;
+
+/* These are all defined in icp_common.c */
+extern atomic_t lac_session_failed_dereg_count;
+extern atomic_t icp_ocfDrvIsExiting;
+extern atomic_t num_ocf_to_drv_registered_sessions;
+
+/*These are use inputs used in icp_sym.c and icp_common.c
+ They are instantiated in icp_common.c*/
+extern int max_sessions;
+
+extern int32_t icp_ocfDrvDriverId;
+extern struct list_head icp_ocfDrvGlobalSymListHead;
+extern struct list_head icp_ocfDrvGlobalSymListHead_FreeMemList;
+extern struct workqueue_struct *icp_ocfDrvFreeLacSessionWorkQ;
+extern spinlock_t icp_ocfDrvSymSessInfoListSpinlock;
+extern rwlock_t icp_kmem_cache_destroy_alloc_lock;
+
+/*Slab zones for symettric functionality, instantiated in icp_common.c*/
+extern struct kmem_cache *drvSessionData_zone;
+extern struct kmem_cache *drvOpData_zone;
+
+/*Slabs zones for asymettric functionality, instantiated in icp_common.c*/
+extern struct kmem_cache *drvDH_zone;
+extern struct kmem_cache *drvLnModExp_zone;
+extern struct kmem_cache *drvRSADecrypt_zone;
+extern struct kmem_cache *drvRSAPrivateKey_zone;
+extern struct kmem_cache *drvDSARSSign_zone;
+extern struct kmem_cache *drvDSARSSignKValue_zone;
+extern struct kmem_cache *drvDSAVerify_zone;
+
+/*Slab zones for flatbuffers and bufferlist*/
+extern struct kmem_cache *drvFlatBuffer_zone;
+
+#define ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS (16)
+
+struct icp_drvBuffListInfo {
+ Cpa16U numBuffers;
+ Cpa32U metaSize;
+ Cpa32U metaOffset;
+ Cpa32U buffListSize;
+};
+extern struct icp_drvBuffListInfo defBuffListInfo;
+
+/*
+* This struct is used to keep a reference to the relevant node in the list
+* of sessionData structs, to the buffer type required by OCF and to the OCF
+* provided crp struct that needs to be returned. All this info is needed in
+* the callback function.
+*
+* IV can sometimes be stored in non-contiguous memory (e.g. skbuff
+* linked/frag list, therefore a contiguous memory space for the IV data must be
+* created and passed to LAC
+*
+*/
+struct icp_drvOpData {
+ CpaCySymOpData lacOpData;
+ uint32_t digestSizeInBytes;
+ struct cryptop *crp;
+ uint8_t bufferType;
+ uint8_t ivData[MAX_IV_LEN_IN_BYTES];
+ uint16_t numBufferListArray;
+ CpaBufferList srcBuffer;
+ CpaFlatBuffer bufferListArray[ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS];
+ CpaBoolean verifyResult;
+};
+/*Values used to derisk chances of performs being called against
+deregistered sessions (for which the slab page has been reclaimed)
+This is not a fix - since page frames are reclaimed from a slab, one cannot
+rely on that memory not being re-used by another app.*/
+typedef enum {
+ ICP_SESSION_INITIALISED = 0x5C5C5C,
+ ICP_SESSION_RUNNING = 0x005C00,
+ ICP_SESSION_DEREGISTERED = 0xC5C5C5
+} usage_derisk;
+
+/*
+This is the OCF<->OCF_DRV session object:
+
+1.The first member is a listNode. These session objects are added to a linked
+ list in order to make it easier to remove them all at session exit time.
+2.The second member is used to give the session object state and derisk the
+ possibility of OCF batch calls executing against a deregistered session (as
+ described above).
+3.The third member is a LAC<->OCF_DRV session handle (initialised with the first
+ perform request for that session).
+4.The fourth is the LAC session context. All the parameters for this structure
+ are only known when the first perform request for this session occurs. That is
+ why the OCF Tolapai Driver only registers a new LAC session at perform time
+*/
+struct icp_drvSessionData {
+ struct list_head listNode;
+ usage_derisk inUse;
+ CpaCySymSessionCtx sessHandle;
+ CpaCySymSessionSetupData lacSessCtx;
+};
+
+/* This struct is required for deferred session
+ deregistration as a work queue function can
+ only have one argument*/
+struct icp_ocfDrvFreeLacSession {
+ CpaCySymSessionCtx sessionToDeregister;
+ struct work_struct work;
+};
+
+int icp_ocfDrvNewSession(device_t dev, uint32_t * sild, struct cryptoini *cri);
+
+int icp_ocfDrvFreeLACSession(device_t dev, uint64_t sid);
+
+int icp_ocfDrvSymProcess(device_t dev, struct cryptop *crp, int hint);
+
+int icp_ocfDrvPkeProcess(device_t dev, struct cryptkop *krp, int hint);
+
+int icp_ocfDrvReadRandom(void *arg, uint32_t * buf, int maxwords);
+
+int icp_ocfDrvDeregRetry(CpaCySymSessionCtx sessionToDeregister);
+
+int icp_ocfDrvSkBuffToBufferList(struct sk_buff *skb,
+ CpaBufferList * bufferList);
+
+int icp_ocfDrvBufferListToSkBuff(CpaBufferList * bufferList,
+ struct sk_buff **skb);
+
+void icp_ocfDrvPtrAndLenToFlatBuffer(void *pData, uint32_t len,
+ CpaFlatBuffer * pFlatBuffer);
+
+void icp_ocfDrvPtrAndLenToBufferList(void *pDataIn, uint32_t length,
+ CpaBufferList * pBufferList);
+
+void icp_ocfDrvBufferListToPtrAndLen(CpaBufferList * pBufferList,
+ void **ppDataOut, uint32_t * pLength);
+
+int icp_ocfDrvBufferListMemInfo(uint16_t numBuffers,
+ struct icp_drvBuffListInfo *buffListInfo);
+
+uint16_t icp_ocfDrvGetSkBuffFrags(struct sk_buff *pSkb);
+
+void icp_ocfDrvFreeFlatBuffer(CpaFlatBuffer * pFlatBuffer);
+
+int icp_ocfDrvAllocMetaData(CpaBufferList * pBufferList,
+ const struct icp_drvOpData *pOpData);
+
+void icp_ocfDrvFreeMetaData(CpaBufferList * pBufferList);
+
+#endif
+/* ICP_OCF_H */
diff --git a/crypto/ocf/ep80579/icp_sym.c b/crypto/ocf/ep80579/icp_sym.c
new file mode 100644
index 000000000000..d632970165b5
--- /dev/null
+++ b/crypto/ocf/ep80579/icp_sym.c
@@ -0,0 +1,1382 @@
+/***************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+ *
+ * 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, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR 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.
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Corporation
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * version: Security.L.1.0.130
+ *
+ ***************************************************************************/
+/*
+ * An OCF module that uses the API for Intel® QuickAssist Technology to do the
+ * cryptography.
+ *
+ * This driver requires the ICP Access Library that is available from Intel in
+ * order to operate.
+ */
+
+#include "icp_ocf.h"
+
+/*This is the call back function for all symmetric cryptographic processes.
+ Its main functionality is to free driver crypto operation structure and to
+ call back to OCF*/
+static void
+icp_ocfDrvSymCallBack(void *callbackTag,
+ CpaStatus status,
+ const CpaCySymOp operationType,
+ void *pOpData,
+ CpaBufferList * pDstBuffer, CpaBoolean verifyResult);
+
+/*This function is used to extract crypto processing information from the OCF
+ inputs, so as that it may be passed onto LAC*/
+static int
+icp_ocfDrvProcessDataSetup(struct icp_drvOpData *drvOpData,
+ struct cryptodesc *crp_desc);
+
+/*This function checks whether the crp_desc argument pertains to a digest or a
+ cipher operation*/
+static int icp_ocfDrvAlgCheck(struct cryptodesc *crp_desc);
+
+/*This function copies all the passed in session context information and stores
+ it in a LAC context structure*/
+static int
+icp_ocfDrvAlgorithmSetup(struct cryptoini *cri,
+ CpaCySymSessionSetupData * lacSessCtx);
+
+/*This top level function is used to find a pointer to where a digest is
+ stored/needs to be inserted. */
+static uint8_t *icp_ocfDrvDigestPointerFind(struct icp_drvOpData *drvOpData,
+ struct cryptodesc *crp_desc);
+
+/*This function is called when a digest pointer has to be found within a
+ SKBUFF.*/
+static inline uint8_t *icp_ocfDrvSkbuffDigestPointerFind(struct icp_drvOpData
+ *drvOpData,
+ int offsetInBytes,
+ uint32_t
+ digestSizeInBytes);
+
+/*The following two functions are called if the SKBUFF digest pointer is not
+ positioned in the linear portion of the buffer (i.e. it is in a linked SKBUFF
+ or page fragment).*/
+/*This function takes care of the page fragment case.*/
+static inline uint8_t *icp_ocfDrvDigestSkbNRFragsCheck(struct sk_buff *skb,
+ struct skb_shared_info
+ *skb_shared,
+ int offsetInBytes,
+ uint32_t
+ digestSizeInBytes);
+
+/*This function takes care of the linked list case.*/
+static inline uint8_t *icp_ocfDrvDigestSkbFragListCheck(struct sk_buff *skb,
+ struct skb_shared_info
+ *skb_shared,
+ int offsetInBytes,
+ uint32_t
+ digestSizeInBytes);
+
+/*This function is used to free an OCF->OCF_DRV session object*/
+static void icp_ocfDrvFreeOCFSession(struct icp_drvSessionData *sessionData);
+
+/*max IOV buffs supported in a UIO structure*/
+#define NUM_IOV_SUPPORTED (1)
+
+/* Name : icp_ocfDrvSymCallBack
+ *
+ * Description : When this function returns it signifies that the LAC
+ * component has completed the relevant symmetric operation.
+ *
+ * Notes : The callbackTag is a pointer to an icp_drvOpData. This memory
+ * object was passed to LAC for the cryptographic processing and contains all
+ * the relevant information for cleaning up buffer handles etc. so that the
+ * OCF Tolapai Driver portion of this crypto operation can be fully completed.
+ */
+static void
+icp_ocfDrvSymCallBack(void *callbackTag,
+ CpaStatus status,
+ const CpaCySymOp operationType,
+ void *pOpData,
+ CpaBufferList * pDstBuffer, CpaBoolean verifyResult)
+{
+ struct cryptop *crp = NULL;
+ struct icp_drvOpData *temp_drvOpData =
+ (struct icp_drvOpData *)callbackTag;
+ uint64_t *tempBasePtr = NULL;
+ uint32_t tempLen = 0;
+
+ if (NULL == temp_drvOpData) {
+ DPRINTK("%s(): The callback from the LAC component"
+ " has failed due to Null userOpaque data"
+ "(status == %d).\n", __FUNCTION__, status);
+ DPRINTK("%s(): Unable to call OCF back! \n", __FUNCTION__);
+ return;
+ }
+
+ crp = temp_drvOpData->crp;
+ crp->crp_etype = ICP_OCF_DRV_NO_CRYPTO_PROCESS_ERROR;
+
+ if (NULL == pOpData) {
+ DPRINTK("%s(): The callback from the LAC component"
+ " has failed due to Null Symmetric Op data"
+ "(status == %d).\n", __FUNCTION__, status);
+ crp->crp_etype = ECANCELED;
+ crypto_done(crp);
+ return;
+ }
+
+ if (NULL == pDstBuffer) {
+ DPRINTK("%s(): The callback from the LAC component"
+ " has failed due to Null Dst Bufferlist data"
+ "(status == %d).\n", __FUNCTION__, status);
+ crp->crp_etype = ECANCELED;
+ crypto_done(crp);
+ return;
+ }
+
+ if (CPA_STATUS_SUCCESS == status) {
+
+ if (temp_drvOpData->bufferType == CRYPTO_F_SKBUF) {
+ if (ICP_OCF_DRV_STATUS_SUCCESS !=
+ icp_ocfDrvBufferListToSkBuff(pDstBuffer,
+ (struct sk_buff **)
+ &(crp->crp_buf))) {
+ EPRINTK("%s(): BufferList to SkBuff "
+ "conversion error.\n", __FUNCTION__);
+ crp->crp_etype = EPERM;
+ }
+ } else {
+ icp_ocfDrvBufferListToPtrAndLen(pDstBuffer,
+ (void **)&tempBasePtr,
+ &tempLen);
+ crp->crp_olen = (int)tempLen;
+ }
+
+ } else {
+ DPRINTK("%s(): The callback from the LAC component has failed"
+ "(status == %d).\n", __FUNCTION__, status);
+
+ crp->crp_etype = ECANCELED;
+ }
+
+ if (temp_drvOpData->numBufferListArray >
+ ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS) {
+ kfree(pDstBuffer->pBuffers);
+ }
+ icp_ocfDrvFreeMetaData(pDstBuffer);
+ kmem_cache_free(drvOpData_zone, temp_drvOpData);
+
+ /* Invoke the OCF callback function */
+ crypto_done(crp);
+
+ return;
+}
+
+/* Name : icp_ocfDrvNewSession
+ *
+ * Description : This function will create a new Driver<->OCF session
+ *
+ * Notes : LAC session registration happens during the first perform call.
+ * That is the first time we know all information about a given session.
+ */
+int icp_ocfDrvNewSession(device_t dev, uint32_t * sid, struct cryptoini *cri)
+{
+ struct icp_drvSessionData *sessionData = NULL;
+ uint32_t delete_session = 0;
+
+ /* The SID passed in should be our driver ID. We can return the */
+ /* local ID (LID) which is a unique identifier which we can use */
+ /* to differentiate between the encrypt/decrypt LAC session handles */
+ if (NULL == sid) {
+ EPRINTK("%s(): Invalid input parameters - NULL sid.\n",
+ __FUNCTION__);
+ return EINVAL;
+ }
+
+ if (NULL == cri) {
+ EPRINTK("%s(): Invalid input parameters - NULL cryptoini.\n",
+ __FUNCTION__);
+ return EINVAL;
+ }
+
+ if (icp_ocfDrvDriverId != *sid) {
+ EPRINTK("%s(): Invalid input parameters - bad driver ID\n",
+ __FUNCTION__);
+ EPRINTK("\t sid = 0x08%p \n \t cri = 0x08%p \n", sid, cri);
+ return EINVAL;
+ }
+
+ sessionData = kmem_cache_zalloc(drvSessionData_zone, GFP_ATOMIC);
+ if (NULL == sessionData) {
+ DPRINTK("%s():No memory for Session Data\n", __FUNCTION__);
+ return ENOMEM;
+ }
+
+ /*ENTER CRITICAL SECTION */
+ spin_lock_bh(&icp_ocfDrvSymSessInfoListSpinlock);
+ /*put this check in the spinlock so no new sessions can be added to the
+ linked list when we are exiting */
+ if (CPA_TRUE == atomic_read(&icp_ocfDrvIsExiting)) {
+ delete_session++;
+
+ } else if (NO_OCF_TO_DRV_MAX_SESSIONS != max_sessions) {
+ if (atomic_read(&num_ocf_to_drv_registered_sessions) >=
+ (max_sessions -
+ atomic_read(&lac_session_failed_dereg_count))) {
+ delete_session++;
+ } else {
+ atomic_inc(&num_ocf_to_drv_registered_sessions);
+ /* Add to session data linked list */
+ list_add(&(sessionData->listNode),
+ &icp_ocfDrvGlobalSymListHead);
+ }
+
+ } else if (NO_OCF_TO_DRV_MAX_SESSIONS == max_sessions) {
+ list_add(&(sessionData->listNode),
+ &icp_ocfDrvGlobalSymListHead);
+ }
+
+ sessionData->inUse = ICP_SESSION_INITIALISED;
+
+ /*EXIT CRITICAL SECTION */
+ spin_unlock_bh(&icp_ocfDrvSymSessInfoListSpinlock);
+
+ if (delete_session) {
+ DPRINTK("%s():No Session handles available\n", __FUNCTION__);
+ kmem_cache_free(drvSessionData_zone, sessionData);
+ return EPERM;
+ }
+
+ if (ICP_OCF_DRV_STATUS_SUCCESS !=
+ icp_ocfDrvAlgorithmSetup(cri, &(sessionData->lacSessCtx))) {
+ DPRINTK("%s():algorithm not supported\n", __FUNCTION__);
+ icp_ocfDrvFreeOCFSession(sessionData);
+ return EINVAL;
+ }
+
+ if (cri->cri_next) {
+ if (cri->cri_next->cri_next != NULL) {
+ DPRINTK("%s():only two chained algorithms supported\n",
+ __FUNCTION__);
+ icp_ocfDrvFreeOCFSession(sessionData);
+ return EPERM;
+ }
+
+ if (ICP_OCF_DRV_STATUS_SUCCESS !=
+ icp_ocfDrvAlgorithmSetup(cri->cri_next,
+ &(sessionData->lacSessCtx))) {
+ DPRINTK("%s():second algorithm not supported\n",
+ __FUNCTION__);
+ icp_ocfDrvFreeOCFSession(sessionData);
+ return EINVAL;
+ }
+
+ sessionData->lacSessCtx.symOperation =
+ CPA_CY_SYM_OP_ALGORITHM_CHAINING;
+ }
+
+ *sid = (uint32_t) sessionData;
+
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+}
+
+/* Name : icp_ocfDrvAlgorithmSetup
+ *
+ * Description : This function builds the session context data from the
+ * information supplied through OCF. Algorithm chain order and whether the
+ * session is Encrypt/Decrypt can only be found out at perform time however, so
+ * the session is registered with LAC at that time.
+ */
+static int
+icp_ocfDrvAlgorithmSetup(struct cryptoini *cri,
+ CpaCySymSessionSetupData * lacSessCtx)
+{
+
+ lacSessCtx->sessionPriority = CPA_CY_PRIORITY_NORMAL;
+
+ switch (cri->cri_alg) {
+
+ case CRYPTO_NULL_CBC:
+ DPRINTK("%s(): NULL CBC\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_CIPHER;
+ lacSessCtx->cipherSetupData.cipherAlgorithm =
+ CPA_CY_SYM_CIPHER_NULL;
+ lacSessCtx->cipherSetupData.cipherKeyLenInBytes =
+ cri->cri_klen / NUM_BITS_IN_BYTE;
+ lacSessCtx->cipherSetupData.pCipherKey = cri->cri_key;
+ break;
+
+ case CRYPTO_DES_CBC:
+ DPRINTK("%s(): DES CBC\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_CIPHER;
+ lacSessCtx->cipherSetupData.cipherAlgorithm =
+ CPA_CY_SYM_CIPHER_DES_CBC;
+ lacSessCtx->cipherSetupData.cipherKeyLenInBytes =
+ cri->cri_klen / NUM_BITS_IN_BYTE;
+ lacSessCtx->cipherSetupData.pCipherKey = cri->cri_key;
+ break;
+
+ case CRYPTO_3DES_CBC:
+ DPRINTK("%s(): 3DES CBC\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_CIPHER;
+ lacSessCtx->cipherSetupData.cipherAlgorithm =
+ CPA_CY_SYM_CIPHER_3DES_CBC;
+ lacSessCtx->cipherSetupData.cipherKeyLenInBytes =
+ cri->cri_klen / NUM_BITS_IN_BYTE;
+ lacSessCtx->cipherSetupData.pCipherKey = cri->cri_key;
+ break;
+
+ case CRYPTO_AES_CBC:
+ DPRINTK("%s(): AES CBC\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_CIPHER;
+ lacSessCtx->cipherSetupData.cipherAlgorithm =
+ CPA_CY_SYM_CIPHER_AES_CBC;
+ lacSessCtx->cipherSetupData.cipherKeyLenInBytes =
+ cri->cri_klen / NUM_BITS_IN_BYTE;
+ lacSessCtx->cipherSetupData.pCipherKey = cri->cri_key;
+ break;
+
+ case CRYPTO_ARC4:
+ DPRINTK("%s(): ARC4\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_CIPHER;
+ lacSessCtx->cipherSetupData.cipherAlgorithm =
+ CPA_CY_SYM_CIPHER_ARC4;
+ lacSessCtx->cipherSetupData.cipherKeyLenInBytes =
+ cri->cri_klen / NUM_BITS_IN_BYTE;
+ lacSessCtx->cipherSetupData.pCipherKey = cri->cri_key;
+ break;
+
+ case CRYPTO_SHA1:
+ DPRINTK("%s(): SHA1\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_HASH;
+ lacSessCtx->hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA1;
+ lacSessCtx->hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_PLAIN;
+ lacSessCtx->hashSetupData.digestResultLenInBytes =
+ (cri->cri_mlen ?
+ cri->cri_mlen : ICP_SHA1_DIGEST_SIZE_IN_BYTES);
+
+ break;
+
+ case CRYPTO_SHA1_HMAC:
+ DPRINTK("%s(): SHA1_HMAC\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_HASH;
+ lacSessCtx->hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_SHA1;
+ lacSessCtx->hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
+ lacSessCtx->hashSetupData.digestResultLenInBytes =
+ (cri->cri_mlen ?
+ cri->cri_mlen : ICP_SHA1_DIGEST_SIZE_IN_BYTES);
+ lacSessCtx->hashSetupData.authModeSetupData.authKey =
+ cri->cri_key;
+ lacSessCtx->hashSetupData.authModeSetupData.authKeyLenInBytes =
+ cri->cri_klen / NUM_BITS_IN_BYTE;
+ lacSessCtx->hashSetupData.authModeSetupData.aadLenInBytes = 0;
+
+ break;
+
+ case CRYPTO_SHA2_256:
+ DPRINTK("%s(): SHA256\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_HASH;
+ lacSessCtx->hashSetupData.hashAlgorithm =
+ CPA_CY_SYM_HASH_SHA256;
+ lacSessCtx->hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_PLAIN;
+ lacSessCtx->hashSetupData.digestResultLenInBytes =
+ (cri->cri_mlen ?
+ cri->cri_mlen : ICP_SHA256_DIGEST_SIZE_IN_BYTES);
+
+ break;
+
+ case CRYPTO_SHA2_256_HMAC:
+ DPRINTK("%s(): SHA256_HMAC\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_HASH;
+ lacSessCtx->hashSetupData.hashAlgorithm =
+ CPA_CY_SYM_HASH_SHA256;
+ lacSessCtx->hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
+ lacSessCtx->hashSetupData.digestResultLenInBytes =
+ (cri->cri_mlen ?
+ cri->cri_mlen : ICP_SHA256_DIGEST_SIZE_IN_BYTES);
+ lacSessCtx->hashSetupData.authModeSetupData.authKey =
+ cri->cri_key;
+ lacSessCtx->hashSetupData.authModeSetupData.authKeyLenInBytes =
+ cri->cri_klen / NUM_BITS_IN_BYTE;
+ lacSessCtx->hashSetupData.authModeSetupData.aadLenInBytes = 0;
+
+ break;
+
+ case CRYPTO_SHA2_384:
+ DPRINTK("%s(): SHA384\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_HASH;
+ lacSessCtx->hashSetupData.hashAlgorithm =
+ CPA_CY_SYM_HASH_SHA384;
+ lacSessCtx->hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_PLAIN;
+ lacSessCtx->hashSetupData.digestResultLenInBytes =
+ (cri->cri_mlen ?
+ cri->cri_mlen : ICP_SHA384_DIGEST_SIZE_IN_BYTES);
+
+ break;
+
+ case CRYPTO_SHA2_384_HMAC:
+ DPRINTK("%s(): SHA384_HMAC\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_HASH;
+ lacSessCtx->hashSetupData.hashAlgorithm =
+ CPA_CY_SYM_HASH_SHA384;
+ lacSessCtx->hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
+ lacSessCtx->hashSetupData.digestResultLenInBytes =
+ (cri->cri_mlen ?
+ cri->cri_mlen : ICP_SHA384_DIGEST_SIZE_IN_BYTES);
+ lacSessCtx->hashSetupData.authModeSetupData.authKey =
+ cri->cri_key;
+ lacSessCtx->hashSetupData.authModeSetupData.authKeyLenInBytes =
+ cri->cri_klen / NUM_BITS_IN_BYTE;
+ lacSessCtx->hashSetupData.authModeSetupData.aadLenInBytes = 0;
+
+ break;
+
+ case CRYPTO_SHA2_512:
+ DPRINTK("%s(): SHA512\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_HASH;
+ lacSessCtx->hashSetupData.hashAlgorithm =
+ CPA_CY_SYM_HASH_SHA512;
+ lacSessCtx->hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_PLAIN;
+ lacSessCtx->hashSetupData.digestResultLenInBytes =
+ (cri->cri_mlen ?
+ cri->cri_mlen : ICP_SHA512_DIGEST_SIZE_IN_BYTES);
+
+ break;
+
+ case CRYPTO_SHA2_512_HMAC:
+ DPRINTK("%s(): SHA512_HMAC\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_HASH;
+ lacSessCtx->hashSetupData.hashAlgorithm =
+ CPA_CY_SYM_HASH_SHA512;
+ lacSessCtx->hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
+ lacSessCtx->hashSetupData.digestResultLenInBytes =
+ (cri->cri_mlen ?
+ cri->cri_mlen : ICP_SHA512_DIGEST_SIZE_IN_BYTES);
+ lacSessCtx->hashSetupData.authModeSetupData.authKey =
+ cri->cri_key;
+ lacSessCtx->hashSetupData.authModeSetupData.authKeyLenInBytes =
+ cri->cri_klen / NUM_BITS_IN_BYTE;
+ lacSessCtx->hashSetupData.authModeSetupData.aadLenInBytes = 0;
+
+ break;
+
+ case CRYPTO_MD5:
+ DPRINTK("%s(): MD5\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_HASH;
+ lacSessCtx->hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_MD5;
+ lacSessCtx->hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_PLAIN;
+ lacSessCtx->hashSetupData.digestResultLenInBytes =
+ (cri->cri_mlen ?
+ cri->cri_mlen : ICP_MD5_DIGEST_SIZE_IN_BYTES);
+
+ break;
+
+ case CRYPTO_MD5_HMAC:
+ DPRINTK("%s(): MD5_HMAC\n", __FUNCTION__);
+ lacSessCtx->symOperation = CPA_CY_SYM_OP_HASH;
+ lacSessCtx->hashSetupData.hashAlgorithm = CPA_CY_SYM_HASH_MD5;
+ lacSessCtx->hashSetupData.hashMode = CPA_CY_SYM_HASH_MODE_AUTH;
+ lacSessCtx->hashSetupData.digestResultLenInBytes =
+ (cri->cri_mlen ?
+ cri->cri_mlen : ICP_MD5_DIGEST_SIZE_IN_BYTES);
+ lacSessCtx->hashSetupData.authModeSetupData.authKey =
+ cri->cri_key;
+ lacSessCtx->hashSetupData.authModeSetupData.authKeyLenInBytes =
+ cri->cri_klen / NUM_BITS_IN_BYTE;
+ lacSessCtx->hashSetupData.authModeSetupData.aadLenInBytes = 0;
+
+ break;
+
+ default:
+ DPRINTK("%s(): ALG Setup FAIL\n", __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_FAIL;
+ }
+
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+}
+
+/* Name : icp_ocfDrvFreeOCFSession
+ *
+ * Description : This function deletes all existing Session data representing
+ * the Cryptographic session established between OCF and this driver. This
+ * also includes freeing the memory allocated for the session context. The
+ * session object is also removed from the session linked list.
+ */
+static void icp_ocfDrvFreeOCFSession(struct icp_drvSessionData *sessionData)
+{
+
+ sessionData->inUse = ICP_SESSION_DEREGISTERED;
+
+ /*ENTER CRITICAL SECTION */
+ spin_lock_bh(&icp_ocfDrvSymSessInfoListSpinlock);
+
+ if (CPA_TRUE == atomic_read(&icp_ocfDrvIsExiting)) {
+ /*If the Driver is exiting, allow that process to
+ handle any deletions */
+ /*EXIT CRITICAL SECTION */
+ spin_unlock_bh(&icp_ocfDrvSymSessInfoListSpinlock);
+ return;
+ }
+
+ atomic_dec(&num_ocf_to_drv_registered_sessions);
+
+ list_del(&(sessionData->listNode));
+
+ /*EXIT CRITICAL SECTION */
+ spin_unlock_bh(&icp_ocfDrvSymSessInfoListSpinlock);
+
+ if (NULL != sessionData->sessHandle) {
+ kfree(sessionData->sessHandle);
+ }
+ kmem_cache_free(drvSessionData_zone, sessionData);
+}
+
+/* Name : icp_ocfDrvFreeLACSession
+ *
+ * Description : This attempts to deregister a LAC session. If it fails, the
+ * deregistation retry function is called.
+ */
+int icp_ocfDrvFreeLACSession(device_t dev, uint64_t sid)
+{
+ CpaCySymSessionCtx sessionToDeregister = NULL;
+ struct icp_drvSessionData *sessionData = NULL;
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+ int retval = 0;
+
+ sessionData = (struct icp_drvSessionData *)CRYPTO_SESID2LID(sid);
+ if (NULL == sessionData) {
+ EPRINTK("%s(): OCF Free session called with Null Session ID.\n",
+ __FUNCTION__);
+ return EINVAL;
+ }
+
+ sessionToDeregister = sessionData->sessHandle;
+
+ if (ICP_SESSION_INITIALISED == sessionData->inUse) {
+ DPRINTK("%s() Session not registered with LAC\n", __FUNCTION__);
+ } else if (NULL == sessionData->sessHandle) {
+ EPRINTK
+ ("%s(): OCF Free session called with Null Session Handle.\n",
+ __FUNCTION__);
+ return EINVAL;
+ } else {
+ lacStatus = cpaCySymRemoveSession(CPA_INSTANCE_HANDLE_SINGLE,
+ sessionToDeregister);
+ if (CPA_STATUS_RETRY == lacStatus) {
+ if (ICP_OCF_DRV_STATUS_SUCCESS !=
+ icp_ocfDrvDeregRetry(&sessionToDeregister)) {
+ /* the retry function increments the
+ dereg failed count */
+ DPRINTK("%s(): LAC failed to deregister the "
+ "session. (localSessionId= %p)\n",
+ __FUNCTION__, sessionToDeregister);
+ retval = EPERM;
+ }
+
+ } else if (CPA_STATUS_SUCCESS != lacStatus) {
+ DPRINTK("%s(): LAC failed to deregister the session. "
+ "localSessionId= %p, lacStatus = %d\n",
+ __FUNCTION__, sessionToDeregister, lacStatus);
+ atomic_inc(&lac_session_failed_dereg_count);
+ retval = EPERM;
+ }
+ }
+
+ icp_ocfDrvFreeOCFSession(sessionData);
+ return retval;
+
+}
+
+/* Name : icp_ocfDrvAlgCheck
+ *
+ * Description : This function checks whether the cryptodesc argument pertains
+ * to a sym or hash function
+ */
+static int icp_ocfDrvAlgCheck(struct cryptodesc *crp_desc)
+{
+
+ if (crp_desc->crd_alg == CRYPTO_3DES_CBC ||
+ crp_desc->crd_alg == CRYPTO_AES_CBC ||
+ crp_desc->crd_alg == CRYPTO_DES_CBC ||
+ crp_desc->crd_alg == CRYPTO_NULL_CBC ||
+ crp_desc->crd_alg == CRYPTO_ARC4) {
+ return ICP_OCF_DRV_ALG_CIPHER;
+ }
+
+ return ICP_OCF_DRV_ALG_HASH;
+}
+
+/* Name : icp_ocfDrvSymProcess
+ *
+ * Description : This function will map symmetric functionality calls from OCF
+ * to the LAC API. It will also allocate memory to store the session context.
+ *
+ * Notes: If it is the first perform call for a given session, then a LAC
+ * session is registered. After the session is registered, no checks as
+ * to whether session paramaters have changed (e.g. alg chain order) are
+ * done.
+ */
+int icp_ocfDrvSymProcess(device_t dev, struct cryptop *crp, int hint)
+{
+ struct icp_drvSessionData *sessionData = NULL;
+ struct icp_drvOpData *drvOpData = NULL;
+ CpaStatus lacStatus = CPA_STATUS_SUCCESS;
+ Cpa32U sessionCtxSizeInBytes = 0;
+ uint16_t numBufferListArray = 0;
+
+ if (NULL == crp) {
+ DPRINTK("%s(): Invalid input parameters, cryptop is NULL\n",
+ __FUNCTION__);
+ return EINVAL;
+ }
+
+ if (NULL == crp->crp_desc) {
+ DPRINTK("%s(): Invalid input parameters, no crp_desc attached "
+ "to crp\n", __FUNCTION__);
+ crp->crp_etype = EINVAL;
+ return EINVAL;
+ }
+
+ if (NULL == crp->crp_buf) {
+ DPRINTK("%s(): Invalid input parameters, no buffer attached "
+ "to crp\n", __FUNCTION__);
+ crp->crp_etype = EINVAL;
+ return EINVAL;
+ }
+
+ if (CPA_TRUE == atomic_read(&icp_ocfDrvIsExiting)) {
+ crp->crp_etype = EFAULT;
+ return EFAULT;
+ }
+
+ sessionData = (struct icp_drvSessionData *)
+ (CRYPTO_SESID2LID(crp->crp_sid));
+ if (NULL == sessionData) {
+ DPRINTK("%s(): Invalid input parameters, Null Session ID \n",
+ __FUNCTION__);
+ crp->crp_etype = EINVAL;
+ return EINVAL;
+ }
+
+/*If we get a request against a deregisted session, cancel operation*/
+ if (ICP_SESSION_DEREGISTERED == sessionData->inUse) {
+ DPRINTK("%s(): Session ID %d was deregistered \n",
+ __FUNCTION__, (int)(CRYPTO_SESID2LID(crp->crp_sid)));
+ crp->crp_etype = EFAULT;
+ return EFAULT;
+ }
+
+/*If none of the session states are set, then the session structure was either
+ not initialised properly or we are reading from a freed memory area (possible
+ due to OCF batch mode not removing queued requests against deregistered
+ sessions*/
+ if (ICP_SESSION_INITIALISED != sessionData->inUse &&
+ ICP_SESSION_RUNNING != sessionData->inUse) {
+ DPRINTK("%s(): Session - ID %d - not properly initialised or "
+ "memory freed back to the kernel \n",
+ __FUNCTION__, (int)(CRYPTO_SESID2LID(crp->crp_sid)));
+ crp->crp_etype = EINVAL;
+ return EINVAL;
+ }
+
+ /*For the below checks, remember error checking is already done in LAC.
+ We're not validating inputs subsequent to registration */
+ if (sessionData->inUse == ICP_SESSION_INITIALISED) {
+ DPRINTK("%s(): Initialising session\n", __FUNCTION__);
+
+ if (NULL != crp->crp_desc->crd_next) {
+ if (ICP_OCF_DRV_ALG_CIPHER ==
+ icp_ocfDrvAlgCheck(crp->crp_desc)) {
+
+ sessionData->lacSessCtx.algChainOrder =
+ CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;
+
+ if (crp->crp_desc->crd_flags & CRD_F_ENCRYPT) {
+ sessionData->lacSessCtx.cipherSetupData.
+ cipherDirection =
+ CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;
+ } else {
+ sessionData->lacSessCtx.cipherSetupData.
+ cipherDirection =
+ CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT;
+ }
+ } else {
+ sessionData->lacSessCtx.algChainOrder =
+ CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;
+
+ if (crp->crp_desc->crd_next->crd_flags &
+ CRD_F_ENCRYPT) {
+ sessionData->lacSessCtx.cipherSetupData.
+ cipherDirection =
+ CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;
+ } else {
+ sessionData->lacSessCtx.cipherSetupData.
+ cipherDirection =
+ CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT;
+ }
+
+ }
+
+ } else if (ICP_OCF_DRV_ALG_CIPHER ==
+ icp_ocfDrvAlgCheck(crp->crp_desc)) {
+ if (crp->crp_desc->crd_flags & CRD_F_ENCRYPT) {
+ sessionData->lacSessCtx.cipherSetupData.
+ cipherDirection =
+ CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT;
+ } else {
+ sessionData->lacSessCtx.cipherSetupData.
+ cipherDirection =
+ CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT;
+ }
+
+ }
+
+ /*No action required for standalone Auth here */
+
+ /* Allocate memory for SymSessionCtx before the Session Registration */
+ lacStatus =
+ cpaCySymSessionCtxGetSize(CPA_INSTANCE_HANDLE_SINGLE,
+ &(sessionData->lacSessCtx),
+ &sessionCtxSizeInBytes);
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): cpaCySymSessionCtxGetSize failed - %d\n",
+ __FUNCTION__, lacStatus);
+ return EINVAL;
+ }
+ sessionData->sessHandle =
+ kmalloc(sessionCtxSizeInBytes, GFP_ATOMIC);
+ if (NULL == sessionData->sessHandle) {
+ EPRINTK
+ ("%s(): Failed to get memory for SymSessionCtx\n",
+ __FUNCTION__);
+ return ENOMEM;
+ }
+
+ lacStatus = cpaCySymInitSession(CPA_INSTANCE_HANDLE_SINGLE,
+ icp_ocfDrvSymCallBack,
+ &(sessionData->lacSessCtx),
+ sessionData->sessHandle);
+
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): cpaCySymInitSession failed -%d \n",
+ __FUNCTION__, lacStatus);
+ return EFAULT;
+ }
+
+ sessionData->inUse = ICP_SESSION_RUNNING;
+ }
+
+ drvOpData = kmem_cache_zalloc(drvOpData_zone, GFP_ATOMIC);
+ if (NULL == drvOpData) {
+ EPRINTK("%s():Failed to get memory for drvOpData\n",
+ __FUNCTION__);
+ crp->crp_etype = ENOMEM;
+ return ENOMEM;
+ }
+
+ drvOpData->lacOpData.pSessionCtx = sessionData->sessHandle;
+ drvOpData->digestSizeInBytes = sessionData->lacSessCtx.hashSetupData.
+ digestResultLenInBytes;
+ drvOpData->crp = crp;
+
+ /* Set the default buffer list array memory allocation */
+ drvOpData->srcBuffer.pBuffers = drvOpData->bufferListArray;
+ drvOpData->numBufferListArray = ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS;
+
+ /*
+ * Allocate buffer list array memory allocation if the
+ * data fragment is more than the default allocation
+ */
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ numBufferListArray = icp_ocfDrvGetSkBuffFrags((struct sk_buff *)
+ crp->crp_buf);
+ if (ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS < numBufferListArray) {
+ DPRINTK("%s() numBufferListArray more than default\n",
+ __FUNCTION__);
+ drvOpData->srcBuffer.pBuffers = NULL;
+ drvOpData->srcBuffer.pBuffers =
+ kmalloc(numBufferListArray *
+ sizeof(CpaFlatBuffer), GFP_ATOMIC);
+ if (NULL == drvOpData->srcBuffer.pBuffers) {
+ EPRINTK("%s() Failed to get memory for "
+ "pBuffers\n", __FUNCTION__);
+ kmem_cache_free(drvOpData_zone, drvOpData);
+ crp->crp_etype = ENOMEM;
+ return ENOMEM;
+ }
+ drvOpData->numBufferListArray = numBufferListArray;
+ }
+ }
+
+ /*
+ * Check the type of buffer structure we got and convert it into
+ * CpaBufferList format.
+ */
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ if (ICP_OCF_DRV_STATUS_SUCCESS !=
+ icp_ocfDrvSkBuffToBufferList((struct sk_buff *)crp->crp_buf,
+ &(drvOpData->srcBuffer))) {
+ EPRINTK("%s():Failed to translate from SK_BUF "
+ "to bufferlist\n", __FUNCTION__);
+ crp->crp_etype = EINVAL;
+ goto err;
+ }
+
+ drvOpData->bufferType = CRYPTO_F_SKBUF;
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ /* OCF only supports IOV of one entry. */
+ if (NUM_IOV_SUPPORTED ==
+ ((struct uio *)(crp->crp_buf))->uio_iovcnt) {
+
+ icp_ocfDrvPtrAndLenToBufferList(((struct uio *)(crp->
+ crp_buf))->
+ uio_iov[0].iov_base,
+ ((struct uio *)(crp->
+ crp_buf))->
+ uio_iov[0].iov_len,
+ &(drvOpData->
+ srcBuffer));
+
+ drvOpData->bufferType = CRYPTO_F_IOV;
+
+ } else {
+ DPRINTK("%s():Unable to handle IOVs with lengths of "
+ "greater than one!\n", __FUNCTION__);
+ crp->crp_etype = EINVAL;
+ goto err;
+ }
+
+ } else {
+ icp_ocfDrvPtrAndLenToBufferList(crp->crp_buf,
+ crp->crp_ilen,
+ &(drvOpData->srcBuffer));
+
+ drvOpData->bufferType = CRYPTO_BUF_CONTIG;
+ }
+
+ if (ICP_OCF_DRV_STATUS_SUCCESS !=
+ icp_ocfDrvProcessDataSetup(drvOpData, drvOpData->crp->crp_desc)) {
+ crp->crp_etype = EINVAL;
+ goto err;
+ }
+
+ if (drvOpData->crp->crp_desc->crd_next != NULL) {
+ if (icp_ocfDrvProcessDataSetup(drvOpData, drvOpData->crp->
+ crp_desc->crd_next)) {
+ crp->crp_etype = EINVAL;
+ goto err;
+ }
+
+ }
+
+ /* Allocate srcBuffer's private meta data */
+ if (ICP_OCF_DRV_STATUS_SUCCESS !=
+ icp_ocfDrvAllocMetaData(&(drvOpData->srcBuffer), drvOpData)) {
+ EPRINTK("%s() icp_ocfDrvAllocMetaData failed\n", __FUNCTION__);
+ memset(&(drvOpData->lacOpData), 0, sizeof(CpaCySymOpData));
+ crp->crp_etype = EINVAL;
+ goto err;
+ }
+
+ /* Perform "in-place" crypto operation */
+ lacStatus = cpaCySymPerformOp(CPA_INSTANCE_HANDLE_SINGLE,
+ (void *)drvOpData,
+ &(drvOpData->lacOpData),
+ &(drvOpData->srcBuffer),
+ &(drvOpData->srcBuffer),
+ &(drvOpData->verifyResult));
+ if (CPA_STATUS_RETRY == lacStatus) {
+ DPRINTK("%s(): cpaCySymPerformOp retry, lacStatus = %d\n",
+ __FUNCTION__, lacStatus);
+ memset(&(drvOpData->lacOpData), 0, sizeof(CpaCySymOpData));
+ crp->crp_etype = EINVAL;
+ goto err;
+ }
+ if (CPA_STATUS_SUCCESS != lacStatus) {
+ EPRINTK("%s(): cpaCySymPerformOp failed, lacStatus = %d\n",
+ __FUNCTION__, lacStatus);
+ memset(&(drvOpData->lacOpData), 0, sizeof(CpaCySymOpData));
+ crp->crp_etype = EINVAL;
+ goto err;
+ }
+
+ return 0; //OCF success status value
+
+ err:
+ if (drvOpData->numBufferListArray > ICP_OCF_DRV_DEFAULT_BUFFLIST_ARRAYS) {
+ kfree(drvOpData->srcBuffer.pBuffers);
+ }
+ icp_ocfDrvFreeMetaData(&(drvOpData->srcBuffer));
+ kmem_cache_free(drvOpData_zone, drvOpData);
+
+ return crp->crp_etype;
+}
+
+/* Name : icp_ocfDrvProcessDataSetup
+ *
+ * Description : This function will setup all the cryptographic operation data
+ * that is required by LAC to execute the operation.
+ */
+static int icp_ocfDrvProcessDataSetup(struct icp_drvOpData *drvOpData,
+ struct cryptodesc *crp_desc)
+{
+ CpaCyRandGenOpData randGenOpData;
+ CpaFlatBuffer randData;
+
+ drvOpData->lacOpData.packetType = CPA_CY_SYM_PACKET_TYPE_FULL;
+
+ /* Convert from the cryptop to the ICP LAC crypto parameters */
+ switch (crp_desc->crd_alg) {
+ case CRYPTO_NULL_CBC:
+ drvOpData->lacOpData.
+ cryptoStartSrcOffsetInBytes = crp_desc->crd_skip;
+ drvOpData->lacOpData.
+ messageLenToCipherInBytes = crp_desc->crd_len;
+ drvOpData->verifyResult = CPA_FALSE;
+ drvOpData->lacOpData.ivLenInBytes = NULL_BLOCK_LEN;
+ break;
+ case CRYPTO_DES_CBC:
+ drvOpData->lacOpData.
+ cryptoStartSrcOffsetInBytes = crp_desc->crd_skip;
+ drvOpData->lacOpData.
+ messageLenToCipherInBytes = crp_desc->crd_len;
+ drvOpData->verifyResult = CPA_FALSE;
+ drvOpData->lacOpData.ivLenInBytes = DES_BLOCK_LEN;
+ break;
+ case CRYPTO_3DES_CBC:
+ drvOpData->lacOpData.
+ cryptoStartSrcOffsetInBytes = crp_desc->crd_skip;
+ drvOpData->lacOpData.
+ messageLenToCipherInBytes = crp_desc->crd_len;
+ drvOpData->verifyResult = CPA_FALSE;
+ drvOpData->lacOpData.ivLenInBytes = DES3_BLOCK_LEN;
+ break;
+ case CRYPTO_ARC4:
+ drvOpData->lacOpData.
+ cryptoStartSrcOffsetInBytes = crp_desc->crd_skip;
+ drvOpData->lacOpData.
+ messageLenToCipherInBytes = crp_desc->crd_len;
+ drvOpData->verifyResult = CPA_FALSE;
+ drvOpData->lacOpData.ivLenInBytes = ARC4_COUNTER_LEN;
+ break;
+ case CRYPTO_AES_CBC:
+ drvOpData->lacOpData.
+ cryptoStartSrcOffsetInBytes = crp_desc->crd_skip;
+ drvOpData->lacOpData.
+ messageLenToCipherInBytes = crp_desc->crd_len;
+ drvOpData->verifyResult = CPA_FALSE;
+ drvOpData->lacOpData.ivLenInBytes = RIJNDAEL128_BLOCK_LEN;
+ break;
+ case CRYPTO_SHA1:
+ case CRYPTO_SHA1_HMAC:
+ case CRYPTO_SHA2_256:
+ case CRYPTO_SHA2_256_HMAC:
+ case CRYPTO_SHA2_384:
+ case CRYPTO_SHA2_384_HMAC:
+ case CRYPTO_SHA2_512:
+ case CRYPTO_SHA2_512_HMAC:
+ case CRYPTO_MD5:
+ case CRYPTO_MD5_HMAC:
+ drvOpData->lacOpData.
+ hashStartSrcOffsetInBytes = crp_desc->crd_skip;
+ drvOpData->lacOpData.
+ messageLenToHashInBytes = crp_desc->crd_len;
+ drvOpData->lacOpData.
+ pDigestResult =
+ icp_ocfDrvDigestPointerFind(drvOpData, crp_desc);
+
+ if (NULL == drvOpData->lacOpData.pDigestResult) {
+ DPRINTK("%s(): ERROR - could not calculate "
+ "Digest Result memory address\n", __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_FAIL;
+ }
+
+ drvOpData->lacOpData.digestVerify = CPA_FALSE;
+ break;
+ default:
+ DPRINTK("%s(): Crypto process error - algorithm not "
+ "found \n", __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_FAIL;
+ }
+
+ /* Figure out what the IV is supposed to be */
+ if ((crp_desc->crd_alg == CRYPTO_DES_CBC) ||
+ (crp_desc->crd_alg == CRYPTO_3DES_CBC) ||
+ (crp_desc->crd_alg == CRYPTO_AES_CBC)) {
+ /*ARC4 doesn't use an IV */
+ if (crp_desc->crd_flags & CRD_F_IV_EXPLICIT) {
+ /* Explicit IV provided to OCF */
+ drvOpData->lacOpData.pIv = crp_desc->crd_iv;
+ } else {
+ /* IV is not explicitly provided to OCF */
+
+ /* Point the LAC OP Data IV pointer to our allocated
+ storage location for this session. */
+ drvOpData->lacOpData.pIv = drvOpData->ivData;
+
+ if ((crp_desc->crd_flags & CRD_F_ENCRYPT) &&
+ ((crp_desc->crd_flags & CRD_F_IV_PRESENT) == 0)) {
+
+ /* Encrypting - need to create IV */
+ randGenOpData.generateBits = CPA_TRUE;
+ randGenOpData.lenInBytes = MAX_IV_LEN_IN_BYTES;
+
+ icp_ocfDrvPtrAndLenToFlatBuffer((Cpa8U *)
+ drvOpData->
+ ivData,
+ MAX_IV_LEN_IN_BYTES,
+ &randData);
+
+ if (CPA_STATUS_SUCCESS !=
+ cpaCyRandGen(CPA_INSTANCE_HANDLE_SINGLE,
+ NULL, NULL,
+ &randGenOpData, &randData)) {
+ DPRINTK("%s(): ERROR - Failed to"
+ " generate"
+ " Initialisation Vector\n",
+ __FUNCTION__);
+ return ICP_OCF_DRV_STATUS_FAIL;
+ }
+
+ crypto_copyback(drvOpData->crp->
+ crp_flags,
+ drvOpData->crp->crp_buf,
+ crp_desc->crd_inject,
+ drvOpData->lacOpData.
+ ivLenInBytes,
+ (caddr_t) (drvOpData->lacOpData.
+ pIv));
+ } else {
+ /* Reading IV from buffer */
+ crypto_copydata(drvOpData->crp->
+ crp_flags,
+ drvOpData->crp->crp_buf,
+ crp_desc->crd_inject,
+ drvOpData->lacOpData.
+ ivLenInBytes,
+ (caddr_t) (drvOpData->lacOpData.
+ pIv));
+ }
+
+ }
+
+ }
+
+ return ICP_OCF_DRV_STATUS_SUCCESS;
+}
+
+/* Name : icp_ocfDrvDigestPointerFind
+ *
+ * Description : This function is used to find the memory address of where the
+ * digest information shall be stored in. Input buffer types are an skbuff, iov
+ * or flat buffer. The address is found using the buffer data start address and
+ * an offset.
+ *
+ * Note: In the case of a linux skbuff, the digest address may exist within
+ * a memory space linked to from the start buffer. These linked memory spaces
+ * must be traversed by the data length offset in order to find the digest start
+ * address. Whether there is enough space for the digest must also be checked.
+ */
+
+static uint8_t *icp_ocfDrvDigestPointerFind(struct icp_drvOpData *drvOpData,
+ struct cryptodesc *crp_desc)
+{
+
+ int offsetInBytes = crp_desc->crd_inject;
+ uint32_t digestSizeInBytes = drvOpData->digestSizeInBytes;
+ uint8_t *flat_buffer_base = NULL;
+ int flat_buffer_length = 0;
+ struct sk_buff *skb;
+
+ if (drvOpData->crp->crp_flags & CRYPTO_F_SKBUF) {
+ /*check if enough overall space to store hash */
+ skb = (struct sk_buff *)(drvOpData->crp->crp_buf);
+
+ if (skb->len < (offsetInBytes + digestSizeInBytes)) {
+ DPRINTK("%s() Not enough space for Digest"
+ " payload after the offset (%d), "
+ "digest size (%d) \n", __FUNCTION__,
+ offsetInBytes, digestSizeInBytes);
+ return NULL;
+ }
+
+ return icp_ocfDrvSkbuffDigestPointerFind(drvOpData,
+ offsetInBytes,
+ digestSizeInBytes);
+
+ } else {
+ /* IOV or flat buffer */
+ if (drvOpData->crp->crp_flags & CRYPTO_F_IOV) {
+ /*single IOV check has already been done */
+ flat_buffer_base = ((struct uio *)
+ (drvOpData->crp->crp_buf))->
+ uio_iov[0].iov_base;
+ flat_buffer_length = ((struct uio *)
+ (drvOpData->crp->crp_buf))->
+ uio_iov[0].iov_len;
+ } else {
+ flat_buffer_base = (uint8_t *) drvOpData->crp->crp_buf;
+ flat_buffer_length = drvOpData->crp->crp_ilen;
+ }
+
+ if (flat_buffer_length < (offsetInBytes + digestSizeInBytes)) {
+ DPRINTK("%s() Not enough space for Digest "
+ "(IOV/Flat Buffer) \n", __FUNCTION__);
+ return NULL;
+ } else {
+ return (uint8_t *) (flat_buffer_base + offsetInBytes);
+ }
+ }
+ DPRINTK("%s() Should not reach this point\n", __FUNCTION__);
+ return NULL;
+}
+
+/* Name : icp_ocfDrvSkbuffDigestPointerFind
+ *
+ * Description : This function is used by icp_ocfDrvDigestPointerFind to process
+ * the non-linear portion of the skbuff if the fragmentation type is a linked
+ * list (frag_list is not NULL in the skb_shared_info structure)
+ */
+static inline uint8_t *icp_ocfDrvSkbuffDigestPointerFind(struct icp_drvOpData
+ *drvOpData,
+ int offsetInBytes,
+ uint32_t
+ digestSizeInBytes)
+{
+
+ struct sk_buff *skb = NULL;
+ struct skb_shared_info *skb_shared = NULL;
+
+ uint32_t skbuffisnonlinear = 0;
+
+ uint32_t skbheadlen = 0;
+
+ skb = (struct sk_buff *)(drvOpData->crp->crp_buf);
+ skbuffisnonlinear = skb_is_nonlinear(skb);
+
+ skbheadlen = skb_headlen(skb);
+
+ /*Linear skb checks */
+ if (skbheadlen > offsetInBytes) {
+
+ if (skbheadlen >= (offsetInBytes + digestSizeInBytes)) {
+ return (uint8_t *) (skb->data + offsetInBytes);
+ } else {
+ DPRINTK("%s() Auth payload stretches "
+ "accross contiguous memory\n", __FUNCTION__);
+ return NULL;
+ }
+ } else {
+ if (skbuffisnonlinear) {
+ offsetInBytes -= skbheadlen;
+ } else {
+ DPRINTK("%s() Offset outside of buffer boundaries\n",
+ __FUNCTION__);
+ return NULL;
+ }
+ }
+
+ /*Non Linear checks */
+ skb_shared = (struct skb_shared_info *)(skb->end);
+ if (unlikely(NULL == skb_shared)) {
+ DPRINTK("%s() skbuff shared info stucture is NULL! \n",
+ __FUNCTION__);
+ return NULL;
+ } else if ((0 != skb_shared->nr_frags) &&
+ (skb_shared->frag_list != NULL)) {
+ DPRINTK("%s() skbuff nr_frags AND "
+ "frag_list not supported \n", __FUNCTION__);
+ return NULL;
+ }
+
+ /*TCP segmentation more likely than IP fragmentation */
+ if (likely(0 != skb_shared->nr_frags)) {
+ return icp_ocfDrvDigestSkbNRFragsCheck(skb, skb_shared,
+ offsetInBytes,
+ digestSizeInBytes);
+ } else if (skb_shared->frag_list != NULL) {
+ return icp_ocfDrvDigestSkbFragListCheck(skb, skb_shared,
+ offsetInBytes,
+ digestSizeInBytes);
+ } else {
+ DPRINTK("%s() skbuff is non-linear but does not show any "
+ "linked data\n", __FUNCTION__);
+ return NULL;
+ }
+
+}
+
+/* Name : icp_ocfDrvDigestSkbNRFragsCheck
+ *
+ * Description : This function is used by icp_ocfDrvSkbuffDigestPointerFind to
+ * process the non-linear portion of the skbuff, if the fragmentation type is
+ * page fragments
+ */
+static inline uint8_t *icp_ocfDrvDigestSkbNRFragsCheck(struct sk_buff *skb,
+ struct skb_shared_info
+ *skb_shared,
+ int offsetInBytes,
+ uint32_t
+ digestSizeInBytes)
+{
+ int i = 0;
+ /*nr_frags starts from 1 */
+ if (MAX_SKB_FRAGS < skb_shared->nr_frags) {
+ DPRINTK("%s error processing skbuff "
+ "page frame -- MAX FRAGS exceeded \n", __FUNCTION__);
+ return NULL;
+ }
+
+ for (i = 0; i < skb_shared->nr_frags; i++) {
+
+ if (offsetInBytes >= skb_shared->frags[i].size) {
+ /*offset still greater than data position */
+ offsetInBytes -= skb_shared->frags[i].size;
+ } else {
+ /* found the page containing start of hash */
+
+ if (NULL == skb_shared->frags[i].page) {
+ DPRINTK("%s() Linked page is NULL!\n",
+ __FUNCTION__);
+ return NULL;
+ }
+
+ if (offsetInBytes + digestSizeInBytes >
+ skb_shared->frags[i].size) {
+ DPRINTK("%s() Auth payload stretches accross "
+ "contiguous memory\n", __FUNCTION__);
+ return NULL;
+ } else {
+ return (uint8_t *) (skb_shared->frags[i].page +
+ skb_shared->frags[i].
+ page_offset +
+ offsetInBytes);
+ }
+ }
+ /*only possible if internal page sizes are set wrong */
+ if (offsetInBytes < 0) {
+ DPRINTK("%s error processing skbuff page frame "
+ "-- offset calculation \n", __FUNCTION__);
+ return NULL;
+ }
+ }
+ /*only possible if internal page sizes are set wrong */
+ DPRINTK("%s error processing skbuff page frame "
+ "-- ran out of page fragments, remaining offset = %d \n",
+ __FUNCTION__, offsetInBytes);
+ return NULL;
+
+}
+
+/* Name : icp_ocfDrvDigestSkbFragListCheck
+ *
+ * Description : This function is used by icp_ocfDrvSkbuffDigestPointerFind to
+ * process the non-linear portion of the skbuff, if the fragmentation type is
+ * a linked list
+ *
+ */
+static inline uint8_t *icp_ocfDrvDigestSkbFragListCheck(struct sk_buff *skb,
+ struct skb_shared_info
+ *skb_shared,
+ int offsetInBytes,
+ uint32_t
+ digestSizeInBytes)
+{
+
+ struct sk_buff *skb_list = skb_shared->frag_list;
+ /*check added for readability */
+ if (NULL == skb_list) {
+ DPRINTK("%s error processing skbuff "
+ "-- no more list! \n", __FUNCTION__);
+ return NULL;
+ }
+
+ for (; skb_list; skb_list = skb_list->next) {
+ if (NULL == skb_list) {
+ DPRINTK("%s error processing skbuff "
+ "-- no more list! \n", __FUNCTION__);
+ return NULL;
+ }
+
+ if (offsetInBytes >= skb_list->len) {
+ offsetInBytes -= skb_list->len;
+
+ } else {
+ if (offsetInBytes + digestSizeInBytes > skb_list->len) {
+ DPRINTK("%s() Auth payload stretches accross "
+ "contiguous memory\n", __FUNCTION__);
+ return NULL;
+ } else {
+ return (uint8_t *)
+ (skb_list->data + offsetInBytes);
+ }
+
+ }
+
+ /*This check is only needed if internal skb_list length values
+ are set wrong. */
+ if (0 > offsetInBytes) {
+ DPRINTK("%s() error processing skbuff object -- offset "
+ "calculation \n", __FUNCTION__);
+ return NULL;
+ }
+
+ }
+
+ /*catch all for unusual for-loop exit.
+ This code should never be reached */
+ DPRINTK("%s() Catch-All hit! Process error.\n", __FUNCTION__);
+ return NULL;
+}
diff --git a/crypto/ocf/ep80579/linux_2.6_kernel_space.mk b/crypto/ocf/ep80579/linux_2.6_kernel_space.mk
new file mode 100644
index 000000000000..96afa9a45379
--- /dev/null
+++ b/crypto/ocf/ep80579/linux_2.6_kernel_space.mk
@@ -0,0 +1,69 @@
+###################
+# @par
+# This file is provided under a dual BSD/GPLv2 license. When using or
+# redistributing this file, you may do so under either license.
+#
+# GPL LICENSE SUMMARY
+#
+# Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+#
+# 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, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR 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.
+# The full GNU General Public License is included in this distribution
+# in the file called LICENSE.GPL.
+#
+# Contact Information:
+# Intel Corporation
+#
+# BSD LICENSE
+#
+# Copyright(c) 2007,2008 Intel Corporation. All rights reserved.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Intel Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+# OWNER 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 OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#
+# version: Security.L.1.0.130
+###################
+
+#specific include directories in kernel space
+INCLUDES+=#e.g. -I$(OSAL_DIR)/include \
+
+#Extra Flags Specific in kernel space e.g. include path or debug flags etc. e.g to add an include path EXTRA_CFLAGS += -I$(src)/../include
+EXTRA_CFLAGS += $(INCLUDES) -O2 -Wall
+EXTRA_LDFLAGS +=-whole-archive
+
diff --git a/crypto/ocf/hifn/Makefile b/crypto/ocf/hifn/Makefile
new file mode 100644
index 000000000000..163fed0543d1
--- /dev/null
+++ b/crypto/ocf/hifn/Makefile
@@ -0,0 +1,13 @@
+# for SGlinux builds
+-include $(ROOTDIR)/modules/.config
+
+obj-$(CONFIG_OCF_HIFN) += hifn7751.o
+obj-$(CONFIG_OCF_HIFNHIPP) += hifnHIPP.o
+
+obj ?= .
+EXTRA_CFLAGS += -I$(obj)/.. -I$(obj)/
+
+ifdef TOPDIR
+-include $(TOPDIR)/Rules.make
+endif
+
diff --git a/crypto/ocf/hifn/hifn7751.c b/crypto/ocf/hifn/hifn7751.c
new file mode 100644
index 000000000000..6459f3582ee8
--- /dev/null
+++ b/crypto/ocf/hifn/hifn7751.c
@@ -0,0 +1,2970 @@
+/* $OpenBSD: hifn7751.c,v 1.120 2002/05/17 00:33:34 deraadt Exp $ */
+
+/*-
+ * Invertex AEON / Hifn 7751 driver
+ * Copyright (c) 1999 Invertex Inc. All rights reserved.
+ * Copyright (c) 1999 Theo de Raadt
+ * Copyright (c) 2000-2001 Network Security Technologies, Inc.
+ * http://www.netsec.net
+ * Copyright (c) 2003 Hifn Inc.
+ *
+ * This driver is based on a previous driver by Invertex, for which they
+ * requested: Please send any comments, feedback, bug-fixes, or feature
+ * requests to software@invertex.com.
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ *
+ *
+__FBSDID("$FreeBSD: src/sys/dev/hifn/hifn7751.c,v 1.40 2007/03/21 03:42:49 sam Exp $");
+ */
+
+/*
+ * Driver for various Hifn encryption processors.
+ */
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+
+#include <cryptodev.h>
+#include <uio.h>
+#include <hifn/hifn7751reg.h>
+#include <hifn/hifn7751var.h>
+
+#if 1
+#define DPRINTF(a...) if (hifn_debug) { \
+ printk("%s: ", sc ? \
+ device_get_nameunit(sc->sc_dev) : "hifn"); \
+ printk(a); \
+ } else
+#else
+#define DPRINTF(a...)
+#endif
+
+static inline int
+pci_get_revid(struct pci_dev *dev)
+{
+ u8 rid = 0;
+ pci_read_config_byte(dev, PCI_REVISION_ID, &rid);
+ return rid;
+}
+
+static struct hifn_stats hifnstats;
+
+#define debug hifn_debug
+int hifn_debug = 0;
+module_param(hifn_debug, int, 0644);
+MODULE_PARM_DESC(hifn_debug, "Enable debug");
+
+int hifn_maxbatch = 1;
+module_param(hifn_maxbatch, int, 0644);
+MODULE_PARM_DESC(hifn_maxbatch, "max ops to batch w/o interrupt");
+
+#ifdef MODULE_PARM
+char *hifn_pllconfig = NULL;
+MODULE_PARM(hifn_pllconfig, "s");
+#else
+char hifn_pllconfig[32]; /* This setting is RO after loading */
+module_param_string(hifn_pllconfig, hifn_pllconfig, 32, 0444);
+#endif
+MODULE_PARM_DESC(hifn_pllconfig, "PLL config, ie., pci66, ext33, ...");
+
+#ifdef HIFN_VULCANDEV
+#include <sys/conf.h>
+#include <sys/uio.h>
+
+static struct cdevsw vulcanpk_cdevsw; /* forward declaration */
+#endif
+
+/*
+ * Prototypes and count for the pci_device structure
+ */
+static int hifn_probe(struct pci_dev *dev, const struct pci_device_id *ent);
+static void hifn_remove(struct pci_dev *dev);
+
+static int hifn_newsession(device_t, u_int32_t *, struct cryptoini *);
+static int hifn_freesession(device_t, u_int64_t);
+static int hifn_process(device_t, struct cryptop *, int);
+
+static device_method_t hifn_methods = {
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, hifn_newsession),
+ DEVMETHOD(cryptodev_freesession,hifn_freesession),
+ DEVMETHOD(cryptodev_process, hifn_process),
+};
+
+static void hifn_reset_board(struct hifn_softc *, int);
+static void hifn_reset_puc(struct hifn_softc *);
+static void hifn_puc_wait(struct hifn_softc *);
+static int hifn_enable_crypto(struct hifn_softc *);
+static void hifn_set_retry(struct hifn_softc *sc);
+static void hifn_init_dma(struct hifn_softc *);
+static void hifn_init_pci_registers(struct hifn_softc *);
+static int hifn_sramsize(struct hifn_softc *);
+static int hifn_dramsize(struct hifn_softc *);
+static int hifn_ramtype(struct hifn_softc *);
+static void hifn_sessions(struct hifn_softc *);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+static irqreturn_t hifn_intr(int irq, void *arg);
+#else
+static irqreturn_t hifn_intr(int irq, void *arg, struct pt_regs *regs);
+#endif
+static u_int hifn_write_command(struct hifn_command *, u_int8_t *);
+static u_int32_t hifn_next_signature(u_int32_t a, u_int cnt);
+static void hifn_callback(struct hifn_softc *, struct hifn_command *, u_int8_t *);
+static int hifn_crypto(struct hifn_softc *, struct hifn_command *, struct cryptop *, int);
+static int hifn_readramaddr(struct hifn_softc *, int, u_int8_t *);
+static int hifn_writeramaddr(struct hifn_softc *, int, u_int8_t *);
+static int hifn_dmamap_load_src(struct hifn_softc *, struct hifn_command *);
+static int hifn_dmamap_load_dst(struct hifn_softc *, struct hifn_command *);
+static int hifn_init_pubrng(struct hifn_softc *);
+static void hifn_tick(unsigned long arg);
+static void hifn_abort(struct hifn_softc *);
+static void hifn_alloc_slot(struct hifn_softc *, int *, int *, int *, int *);
+
+static void hifn_write_reg_0(struct hifn_softc *, bus_size_t, u_int32_t);
+static void hifn_write_reg_1(struct hifn_softc *, bus_size_t, u_int32_t);
+
+#ifdef CONFIG_OCF_RANDOMHARVEST
+static int hifn_read_random(void *arg, u_int32_t *buf, int len);
+#endif
+
+#define HIFN_MAX_CHIPS 8
+static struct hifn_softc *hifn_chip_idx[HIFN_MAX_CHIPS];
+
+static __inline u_int32_t
+READ_REG_0(struct hifn_softc *sc, bus_size_t reg)
+{
+ u_int32_t v = readl(sc->sc_bar0 + reg);
+ sc->sc_bar0_lastreg = (bus_size_t) -1;
+ return (v);
+}
+#define WRITE_REG_0(sc, reg, val) hifn_write_reg_0(sc, reg, val)
+
+static __inline u_int32_t
+READ_REG_1(struct hifn_softc *sc, bus_size_t reg)
+{
+ u_int32_t v = readl(sc->sc_bar1 + reg);
+ sc->sc_bar1_lastreg = (bus_size_t) -1;
+ return (v);
+}
+#define WRITE_REG_1(sc, reg, val) hifn_write_reg_1(sc, reg, val)
+
+/*
+ * map in a given buffer (great on some arches :-)
+ */
+
+static int
+pci_map_uio(struct hifn_softc *sc, struct hifn_operand *buf, struct uio *uio)
+{
+ struct iovec *iov = uio->uio_iov;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ buf->mapsize = 0;
+ for (buf->nsegs = 0; buf->nsegs < uio->uio_iovcnt; ) {
+ buf->segs[buf->nsegs].ds_addr = pci_map_single(sc->sc_pcidev,
+ iov->iov_base, iov->iov_len,
+ PCI_DMA_BIDIRECTIONAL);
+ buf->segs[buf->nsegs].ds_len = iov->iov_len;
+ buf->mapsize += iov->iov_len;
+ iov++;
+ buf->nsegs++;
+ }
+ /* identify this buffer by the first segment */
+ buf->map = (void *) buf->segs[0].ds_addr;
+ return(0);
+}
+
+/*
+ * map in a given sk_buff
+ */
+
+static int
+pci_map_skb(struct hifn_softc *sc,struct hifn_operand *buf,struct sk_buff *skb)
+{
+ int i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ buf->mapsize = 0;
+
+ buf->segs[0].ds_addr = pci_map_single(sc->sc_pcidev,
+ skb->data, skb_headlen(skb), PCI_DMA_BIDIRECTIONAL);
+ buf->segs[0].ds_len = skb_headlen(skb);
+ buf->mapsize += buf->segs[0].ds_len;
+
+ buf->nsegs = 1;
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; ) {
+ buf->segs[buf->nsegs].ds_len = skb_shinfo(skb)->frags[i].size;
+ buf->segs[buf->nsegs].ds_addr = pci_map_single(sc->sc_pcidev,
+ page_address(skb_shinfo(skb)->frags[i].page) +
+ skb_shinfo(skb)->frags[i].page_offset,
+ buf->segs[buf->nsegs].ds_len, PCI_DMA_BIDIRECTIONAL);
+ buf->mapsize += buf->segs[buf->nsegs].ds_len;
+ buf->nsegs++;
+ }
+
+ /* identify this buffer by the first segment */
+ buf->map = (void *) buf->segs[0].ds_addr;
+ return(0);
+}
+
+/*
+ * map in a given contiguous buffer
+ */
+
+static int
+pci_map_buf(struct hifn_softc *sc,struct hifn_operand *buf, void *b, int len)
+{
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ buf->mapsize = 0;
+ buf->segs[0].ds_addr = pci_map_single(sc->sc_pcidev,
+ b, len, PCI_DMA_BIDIRECTIONAL);
+ buf->segs[0].ds_len = len;
+ buf->mapsize += buf->segs[0].ds_len;
+ buf->nsegs = 1;
+
+ /* identify this buffer by the first segment */
+ buf->map = (void *) buf->segs[0].ds_addr;
+ return(0);
+}
+
+#if 0 /* not needed at this time */
+static void
+pci_sync_iov(struct hifn_softc *sc, struct hifn_operand *buf)
+{
+ int i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+ for (i = 0; i < buf->nsegs; i++)
+ pci_dma_sync_single_for_cpu(sc->sc_pcidev, buf->segs[i].ds_addr,
+ buf->segs[i].ds_len, PCI_DMA_BIDIRECTIONAL);
+}
+#endif
+
+static void
+pci_unmap_buf(struct hifn_softc *sc, struct hifn_operand *buf)
+{
+ int i;
+ DPRINTF("%s()\n", __FUNCTION__);
+ for (i = 0; i < buf->nsegs; i++) {
+ pci_unmap_single(sc->sc_pcidev, buf->segs[i].ds_addr,
+ buf->segs[i].ds_len, PCI_DMA_BIDIRECTIONAL);
+ buf->segs[i].ds_addr = 0;
+ buf->segs[i].ds_len = 0;
+ }
+ buf->nsegs = 0;
+ buf->mapsize = 0;
+ buf->map = 0;
+}
+
+static const char*
+hifn_partname(struct hifn_softc *sc)
+{
+ /* XXX sprintf numbers when not decoded */
+ switch (pci_get_vendor(sc->sc_pcidev)) {
+ case PCI_VENDOR_HIFN:
+ switch (pci_get_device(sc->sc_pcidev)) {
+ case PCI_PRODUCT_HIFN_6500: return "Hifn 6500";
+ case PCI_PRODUCT_HIFN_7751: return "Hifn 7751";
+ case PCI_PRODUCT_HIFN_7811: return "Hifn 7811";
+ case PCI_PRODUCT_HIFN_7951: return "Hifn 7951";
+ case PCI_PRODUCT_HIFN_7955: return "Hifn 7955";
+ case PCI_PRODUCT_HIFN_7956: return "Hifn 7956";
+ }
+ return "Hifn unknown-part";
+ case PCI_VENDOR_INVERTEX:
+ switch (pci_get_device(sc->sc_pcidev)) {
+ case PCI_PRODUCT_INVERTEX_AEON: return "Invertex AEON";
+ }
+ return "Invertex unknown-part";
+ case PCI_VENDOR_NETSEC:
+ switch (pci_get_device(sc->sc_pcidev)) {
+ case PCI_PRODUCT_NETSEC_7751: return "NetSec 7751";
+ }
+ return "NetSec unknown-part";
+ }
+ return "Unknown-vendor unknown-part";
+}
+
+static u_int
+checkmaxmin(struct pci_dev *dev, const char *what, u_int v, u_int min, u_int max)
+{
+ struct hifn_softc *sc = pci_get_drvdata(dev);
+ if (v > max) {
+ device_printf(sc->sc_dev, "Warning, %s %u out of range, "
+ "using max %u\n", what, v, max);
+ v = max;
+ } else if (v < min) {
+ device_printf(sc->sc_dev, "Warning, %s %u out of range, "
+ "using min %u\n", what, v, min);
+ v = min;
+ }
+ return v;
+}
+
+/*
+ * Select PLL configuration for 795x parts. This is complicated in
+ * that we cannot determine the optimal parameters without user input.
+ * The reference clock is derived from an external clock through a
+ * multiplier. The external clock is either the host bus (i.e. PCI)
+ * or an external clock generator. When using the PCI bus we assume
+ * the clock is either 33 or 66 MHz; for an external source we cannot
+ * tell the speed.
+ *
+ * PLL configuration is done with a string: "pci" for PCI bus, or "ext"
+ * for an external source, followed by the frequency. We calculate
+ * the appropriate multiplier and PLL register contents accordingly.
+ * When no configuration is given we default to "pci66" since that
+ * always will allow the card to work. If a card is using the PCI
+ * bus clock and in a 33MHz slot then it will be operating at half
+ * speed until the correct information is provided.
+ *
+ * We use a default setting of "ext66" because according to Mike Ham
+ * of HiFn, almost every board in existence has an external crystal
+ * populated at 66Mhz. Using PCI can be a problem on modern motherboards,
+ * because PCI33 can have clocks from 0 to 33Mhz, and some have
+ * non-PCI-compliant spread-spectrum clocks, which can confuse the pll.
+ */
+static void
+hifn_getpllconfig(struct pci_dev *dev, u_int *pll)
+{
+ const char *pllspec = hifn_pllconfig;
+ u_int freq, mul, fl, fh;
+ u_int32_t pllconfig;
+ char *nxt;
+
+ if (pllspec == NULL)
+ pllspec = "ext66";
+ fl = 33, fh = 66;
+ pllconfig = 0;
+ if (strncmp(pllspec, "ext", 3) == 0) {
+ pllspec += 3;
+ pllconfig |= HIFN_PLL_REF_SEL;
+ switch (pci_get_device(dev)) {
+ case PCI_PRODUCT_HIFN_7955:
+ case PCI_PRODUCT_HIFN_7956:
+ fl = 20, fh = 100;
+ break;
+#ifdef notyet
+ case PCI_PRODUCT_HIFN_7954:
+ fl = 20, fh = 66;
+ break;
+#endif
+ }
+ } else if (strncmp(pllspec, "pci", 3) == 0)
+ pllspec += 3;
+ freq = strtoul(pllspec, &nxt, 10);
+ if (nxt == pllspec)
+ freq = 66;
+ else
+ freq = checkmaxmin(dev, "frequency", freq, fl, fh);
+ /*
+ * Calculate multiplier. We target a Fck of 266 MHz,
+ * allowing only even values, possibly rounded down.
+ * Multipliers > 8 must set the charge pump current.
+ */
+ mul = checkmaxmin(dev, "PLL divisor", (266 / freq) &~ 1, 2, 12);
+ pllconfig |= (mul / 2 - 1) << HIFN_PLL_ND_SHIFT;
+ if (mul > 8)
+ pllconfig |= HIFN_PLL_IS;
+ *pll = pllconfig;
+}
+
+/*
+ * Attach an interface that successfully probed.
+ */
+static int
+hifn_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+ struct hifn_softc *sc = NULL;
+ char rbase;
+ u_int16_t ena, rev;
+ int rseg, rc;
+ unsigned long mem_start, mem_len;
+ static int num_chips = 0;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ if (pci_enable_device(dev) < 0)
+ return(-ENODEV);
+
+ if (pci_set_mwi(dev))
+ return(-ENODEV);
+
+ if (!dev->irq) {
+ printk("hifn: found device with no IRQ assigned. check BIOS settings!");
+ pci_disable_device(dev);
+ return(-ENODEV);
+ }
+
+ sc = (struct hifn_softc *) kmalloc(sizeof(*sc), GFP_KERNEL);
+ if (!sc)
+ return(-ENOMEM);
+ memset(sc, 0, sizeof(*sc));
+
+ softc_device_init(sc, "hifn", num_chips, hifn_methods);
+
+ sc->sc_pcidev = dev;
+ sc->sc_irq = -1;
+ sc->sc_cid = -1;
+ sc->sc_num = num_chips++;
+ if (sc->sc_num < HIFN_MAX_CHIPS)
+ hifn_chip_idx[sc->sc_num] = sc;
+
+ pci_set_drvdata(sc->sc_pcidev, sc);
+
+ spin_lock_init(&sc->sc_mtx);
+
+ /* XXX handle power management */
+
+ /*
+ * The 7951 and 795x have a random number generator and
+ * public key support; note this.
+ */
+ if (pci_get_vendor(dev) == PCI_VENDOR_HIFN &&
+ (pci_get_device(dev) == PCI_PRODUCT_HIFN_7951 ||
+ pci_get_device(dev) == PCI_PRODUCT_HIFN_7955 ||
+ pci_get_device(dev) == PCI_PRODUCT_HIFN_7956))
+ sc->sc_flags = HIFN_HAS_RNG | HIFN_HAS_PUBLIC;
+ /*
+ * The 7811 has a random number generator and
+ * we also note it's identity 'cuz of some quirks.
+ */
+ if (pci_get_vendor(dev) == PCI_VENDOR_HIFN &&
+ pci_get_device(dev) == PCI_PRODUCT_HIFN_7811)
+ sc->sc_flags |= HIFN_IS_7811 | HIFN_HAS_RNG;
+
+ /*
+ * The 795x parts support AES.
+ */
+ if (pci_get_vendor(dev) == PCI_VENDOR_HIFN &&
+ (pci_get_device(dev) == PCI_PRODUCT_HIFN_7955 ||
+ pci_get_device(dev) == PCI_PRODUCT_HIFN_7956)) {
+ sc->sc_flags |= HIFN_IS_7956 | HIFN_HAS_AES;
+ /*
+ * Select PLL configuration. This depends on the
+ * bus and board design and must be manually configured
+ * if the default setting is unacceptable.
+ */
+ hifn_getpllconfig(dev, &sc->sc_pllconfig);
+ }
+
+ /*
+ * Setup PCI resources. Note that we record the bus
+ * tag and handle for each register mapping, this is
+ * used by the READ_REG_0, WRITE_REG_0, READ_REG_1,
+ * and WRITE_REG_1 macros throughout the driver.
+ */
+ mem_start = pci_resource_start(sc->sc_pcidev, 0);
+ mem_len = pci_resource_len(sc->sc_pcidev, 0);
+ sc->sc_bar0 = (ocf_iomem_t) ioremap(mem_start, mem_len);
+ if (!sc->sc_bar0) {
+ device_printf(sc->sc_dev, "cannot map bar%d register space\n", 0);
+ goto fail;
+ }
+ sc->sc_bar0_lastreg = (bus_size_t) -1;
+
+ mem_start = pci_resource_start(sc->sc_pcidev, 1);
+ mem_len = pci_resource_len(sc->sc_pcidev, 1);
+ sc->sc_bar1 = (ocf_iomem_t) ioremap(mem_start, mem_len);
+ if (!sc->sc_bar1) {
+ device_printf(sc->sc_dev, "cannot map bar%d register space\n", 1);
+ goto fail;
+ }
+ sc->sc_bar1_lastreg = (bus_size_t) -1;
+
+ /* fix up the bus size */
+ if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) {
+ device_printf(sc->sc_dev, "No usable DMA configuration, aborting.\n");
+ goto fail;
+ }
+ if (pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK)) {
+ device_printf(sc->sc_dev,
+ "No usable consistent DMA configuration, aborting.\n");
+ goto fail;
+ }
+
+ hifn_set_retry(sc);
+
+ /*
+ * Setup the area where the Hifn DMA's descriptors
+ * and associated data structures.
+ */
+ sc->sc_dma = (struct hifn_dma *) pci_alloc_consistent(dev,
+ sizeof(*sc->sc_dma),
+ &sc->sc_dma_physaddr);
+ if (!sc->sc_dma) {
+ device_printf(sc->sc_dev, "cannot alloc sc_dma\n");
+ goto fail;
+ }
+ bzero(sc->sc_dma, sizeof(*sc->sc_dma));
+
+ /*
+ * Reset the board and do the ``secret handshake''
+ * to enable the crypto support. Then complete the
+ * initialization procedure by setting up the interrupt
+ * and hooking in to the system crypto support so we'll
+ * get used for system services like the crypto device,
+ * IPsec, RNG device, etc.
+ */
+ hifn_reset_board(sc, 0);
+
+ if (hifn_enable_crypto(sc) != 0) {
+ device_printf(sc->sc_dev, "crypto enabling failed\n");
+ goto fail;
+ }
+ hifn_reset_puc(sc);
+
+ hifn_init_dma(sc);
+ hifn_init_pci_registers(sc);
+
+ pci_set_master(sc->sc_pcidev);
+
+ /* XXX can't dynamically determine ram type for 795x; force dram */
+ if (sc->sc_flags & HIFN_IS_7956)
+ sc->sc_drammodel = 1;
+ else if (hifn_ramtype(sc))
+ goto fail;
+
+ if (sc->sc_drammodel == 0)
+ hifn_sramsize(sc);
+ else
+ hifn_dramsize(sc);
+
+ /*
+ * Workaround for NetSec 7751 rev A: half ram size because two
+ * of the address lines were left floating
+ */
+ if (pci_get_vendor(dev) == PCI_VENDOR_NETSEC &&
+ pci_get_device(dev) == PCI_PRODUCT_NETSEC_7751 &&
+ pci_get_revid(dev) == 0x61) /*XXX???*/
+ sc->sc_ramsize >>= 1;
+
+ /*
+ * Arrange the interrupt line.
+ */
+ rc = request_irq(dev->irq, hifn_intr, IRQF_SHARED, "hifn", sc);
+ if (rc) {
+ device_printf(sc->sc_dev, "could not map interrupt: %d\n", rc);
+ goto fail;
+ }
+ sc->sc_irq = dev->irq;
+
+ hifn_sessions(sc);
+
+ /*
+ * NB: Keep only the low 16 bits; this masks the chip id
+ * from the 7951.
+ */
+ rev = READ_REG_1(sc, HIFN_1_REVID) & 0xffff;
+
+ rseg = sc->sc_ramsize / 1024;
+ rbase = 'K';
+ if (sc->sc_ramsize >= (1024 * 1024)) {
+ rbase = 'M';
+ rseg /= 1024;
+ }
+ device_printf(sc->sc_dev, "%s, rev %u, %d%cB %cram",
+ hifn_partname(sc), rev,
+ rseg, rbase, sc->sc_drammodel ? 'd' : 's');
+ if (sc->sc_flags & HIFN_IS_7956)
+ printf(", pll=0x%x<%s clk, %ux mult>",
+ sc->sc_pllconfig,
+ sc->sc_pllconfig & HIFN_PLL_REF_SEL ? "ext" : "pci",
+ 2 + 2*((sc->sc_pllconfig & HIFN_PLL_ND) >> 11));
+ printf("\n");
+
+ sc->sc_cid = crypto_get_driverid(softc_get_device(sc),CRYPTOCAP_F_HARDWARE);
+ if (sc->sc_cid < 0) {
+ device_printf(sc->sc_dev, "could not get crypto driver id\n");
+ goto fail;
+ }
+
+ WRITE_REG_0(sc, HIFN_0_PUCNFG,
+ READ_REG_0(sc, HIFN_0_PUCNFG) | HIFN_PUCNFG_CHIPID);
+ ena = READ_REG_0(sc, HIFN_0_PUSTAT) & HIFN_PUSTAT_CHIPENA;
+
+ switch (ena) {
+ case HIFN_PUSTAT_ENA_2:
+ crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_ARC4, 0, 0);
+ if (sc->sc_flags & HIFN_HAS_AES)
+ crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0);
+ /*FALLTHROUGH*/
+ case HIFN_PUSTAT_ENA_1:
+ crypto_register(sc->sc_cid, CRYPTO_MD5, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_SHA1, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_DES_CBC, 0, 0);
+ break;
+ }
+
+ if (sc->sc_flags & (HIFN_HAS_PUBLIC | HIFN_HAS_RNG))
+ hifn_init_pubrng(sc);
+
+ init_timer(&sc->sc_tickto);
+ sc->sc_tickto.function = hifn_tick;
+ sc->sc_tickto.data = (unsigned long) sc->sc_num;
+ mod_timer(&sc->sc_tickto, jiffies + HZ);
+
+ return (0);
+
+fail:
+ if (sc->sc_cid >= 0)
+ crypto_unregister_all(sc->sc_cid);
+ if (sc->sc_irq != -1)
+ free_irq(sc->sc_irq, sc);
+ if (sc->sc_dma) {
+ /* Turn off DMA polling */
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
+
+ pci_free_consistent(sc->sc_pcidev,
+ sizeof(*sc->sc_dma),
+ sc->sc_dma, sc->sc_dma_physaddr);
+ }
+ kfree(sc);
+ return (-ENXIO);
+}
+
+/*
+ * Detach an interface that successfully probed.
+ */
+static void
+hifn_remove(struct pci_dev *dev)
+{
+ struct hifn_softc *sc = pci_get_drvdata(dev);
+ unsigned long l_flags;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ KASSERT(sc != NULL, ("hifn_detach: null software carrier!"));
+
+ /* disable interrupts */
+ HIFN_LOCK(sc);
+ WRITE_REG_1(sc, HIFN_1_DMA_IER, 0);
+ HIFN_UNLOCK(sc);
+
+ /*XXX other resources */
+ del_timer_sync(&sc->sc_tickto);
+
+ /* Turn off DMA polling */
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
+
+ crypto_unregister_all(sc->sc_cid);
+
+ free_irq(sc->sc_irq, sc);
+
+ pci_free_consistent(sc->sc_pcidev, sizeof(*sc->sc_dma),
+ sc->sc_dma, sc->sc_dma_physaddr);
+}
+
+
+static int
+hifn_init_pubrng(struct hifn_softc *sc)
+{
+ int i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ if ((sc->sc_flags & HIFN_IS_7811) == 0) {
+ /* Reset 7951 public key/rng engine */
+ WRITE_REG_1(sc, HIFN_1_PUB_RESET,
+ READ_REG_1(sc, HIFN_1_PUB_RESET) | HIFN_PUBRST_RESET);
+
+ for (i = 0; i < 100; i++) {
+ DELAY(1000);
+ if ((READ_REG_1(sc, HIFN_1_PUB_RESET) &
+ HIFN_PUBRST_RESET) == 0)
+ break;
+ }
+
+ if (i == 100) {
+ device_printf(sc->sc_dev, "public key init failed\n");
+ return (1);
+ }
+ }
+
+ /* Enable the rng, if available */
+#ifdef CONFIG_OCF_RANDOMHARVEST
+ if (sc->sc_flags & HIFN_HAS_RNG) {
+ if (sc->sc_flags & HIFN_IS_7811) {
+ u_int32_t r;
+ r = READ_REG_1(sc, HIFN_1_7811_RNGENA);
+ if (r & HIFN_7811_RNGENA_ENA) {
+ r &= ~HIFN_7811_RNGENA_ENA;
+ WRITE_REG_1(sc, HIFN_1_7811_RNGENA, r);
+ }
+ WRITE_REG_1(sc, HIFN_1_7811_RNGCFG,
+ HIFN_7811_RNGCFG_DEFL);
+ r |= HIFN_7811_RNGENA_ENA;
+ WRITE_REG_1(sc, HIFN_1_7811_RNGENA, r);
+ } else
+ WRITE_REG_1(sc, HIFN_1_RNG_CONFIG,
+ READ_REG_1(sc, HIFN_1_RNG_CONFIG) |
+ HIFN_RNGCFG_ENA);
+
+ sc->sc_rngfirst = 1;
+ crypto_rregister(sc->sc_cid, hifn_read_random, sc);
+ }
+#endif
+
+ /* Enable public key engine, if available */
+ if (sc->sc_flags & HIFN_HAS_PUBLIC) {
+ WRITE_REG_1(sc, HIFN_1_PUB_IEN, HIFN_PUBIEN_DONE);
+ sc->sc_dmaier |= HIFN_DMAIER_PUBDONE;
+ WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
+#ifdef HIFN_VULCANDEV
+ sc->sc_pkdev = make_dev(&vulcanpk_cdevsw, 0,
+ UID_ROOT, GID_WHEEL, 0666,
+ "vulcanpk");
+ sc->sc_pkdev->si_drv1 = sc;
+#endif
+ }
+
+ return (0);
+}
+
+#ifdef CONFIG_OCF_RANDOMHARVEST
+static int
+hifn_read_random(void *arg, u_int32_t *buf, int len)
+{
+ struct hifn_softc *sc = (struct hifn_softc *) arg;
+ u_int32_t sts;
+ int i, rc = 0;
+
+ if (len <= 0)
+ return rc;
+
+ if (sc->sc_flags & HIFN_IS_7811) {
+ /* ONLY VALID ON 7811!!!! */
+ for (i = 0; i < 5; i++) {
+ sts = READ_REG_1(sc, HIFN_1_7811_RNGSTS);
+ if (sts & HIFN_7811_RNGSTS_UFL) {
+ device_printf(sc->sc_dev,
+ "RNG underflow: disabling\n");
+ /* DAVIDM perhaps return -1 */
+ break;
+ }
+ if ((sts & HIFN_7811_RNGSTS_RDY) == 0)
+ break;
+
+ /*
+ * There are at least two words in the RNG FIFO
+ * at this point.
+ */
+ if (rc < len)
+ buf[rc++] = READ_REG_1(sc, HIFN_1_7811_RNGDAT);
+ if (rc < len)
+ buf[rc++] = READ_REG_1(sc, HIFN_1_7811_RNGDAT);
+ }
+ } else
+ buf[rc++] = READ_REG_1(sc, HIFN_1_RNG_DATA);
+
+ /* NB: discard first data read */
+ if (sc->sc_rngfirst) {
+ sc->sc_rngfirst = 0;
+ rc = 0;
+ }
+
+ return(rc);
+}
+#endif /* CONFIG_OCF_RANDOMHARVEST */
+
+static void
+hifn_puc_wait(struct hifn_softc *sc)
+{
+ int i;
+ int reg = HIFN_0_PUCTRL;
+
+ if (sc->sc_flags & HIFN_IS_7956) {
+ reg = HIFN_0_PUCTRL2;
+ }
+
+ for (i = 5000; i > 0; i--) {
+ DELAY(1);
+ if (!(READ_REG_0(sc, reg) & HIFN_PUCTRL_RESET))
+ break;
+ }
+ if (!i)
+ device_printf(sc->sc_dev, "proc unit did not reset(0x%x)\n",
+ READ_REG_0(sc, HIFN_0_PUCTRL));
+}
+
+/*
+ * Reset the processing unit.
+ */
+static void
+hifn_reset_puc(struct hifn_softc *sc)
+{
+ /* Reset processing unit */
+ int reg = HIFN_0_PUCTRL;
+
+ if (sc->sc_flags & HIFN_IS_7956) {
+ reg = HIFN_0_PUCTRL2;
+ }
+ WRITE_REG_0(sc, reg, HIFN_PUCTRL_DMAENA);
+
+ hifn_puc_wait(sc);
+}
+
+/*
+ * Set the Retry and TRDY registers; note that we set them to
+ * zero because the 7811 locks up when forced to retry (section
+ * 3.6 of "Specification Update SU-0014-04". Not clear if we
+ * should do this for all Hifn parts, but it doesn't seem to hurt.
+ */
+static void
+hifn_set_retry(struct hifn_softc *sc)
+{
+ DPRINTF("%s()\n", __FUNCTION__);
+ /* NB: RETRY only responds to 8-bit reads/writes */
+ pci_write_config_byte(sc->sc_pcidev, HIFN_RETRY_TIMEOUT, 0);
+ pci_write_config_dword(sc->sc_pcidev, HIFN_TRDY_TIMEOUT, 0);
+}
+
+/*
+ * Resets the board. Values in the regesters are left as is
+ * from the reset (i.e. initial values are assigned elsewhere).
+ */
+static void
+hifn_reset_board(struct hifn_softc *sc, int full)
+{
+ u_int32_t reg;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+ /*
+ * Set polling in the DMA configuration register to zero. 0x7 avoids
+ * resetting the board and zeros out the other fields.
+ */
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
+
+ /*
+ * Now that polling has been disabled, we have to wait 1 ms
+ * before resetting the board.
+ */
+ DELAY(1000);
+
+ /* Reset the DMA unit */
+ if (full) {
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MODE);
+ DELAY(1000);
+ } else {
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG,
+ HIFN_DMACNFG_MODE | HIFN_DMACNFG_MSTRESET);
+ hifn_reset_puc(sc);
+ }
+
+ KASSERT(sc->sc_dma != NULL, ("hifn_reset_board: null DMA tag!"));
+ bzero(sc->sc_dma, sizeof(*sc->sc_dma));
+
+ /* Bring dma unit out of reset */
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
+
+ hifn_puc_wait(sc);
+ hifn_set_retry(sc);
+
+ if (sc->sc_flags & HIFN_IS_7811) {
+ for (reg = 0; reg < 1000; reg++) {
+ if (READ_REG_1(sc, HIFN_1_7811_MIPSRST) &
+ HIFN_MIPSRST_CRAMINIT)
+ break;
+ DELAY(1000);
+ }
+ if (reg == 1000)
+ device_printf(sc->sc_dev, ": cram init timeout\n");
+ } else {
+ /* set up DMA configuration register #2 */
+ /* turn off all PK and BAR0 swaps */
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG2,
+ (3 << HIFN_DMACNFG2_INIT_WRITE_BURST_SHIFT)|
+ (3 << HIFN_DMACNFG2_INIT_READ_BURST_SHIFT)|
+ (2 << HIFN_DMACNFG2_TGT_WRITE_BURST_SHIFT)|
+ (2 << HIFN_DMACNFG2_TGT_READ_BURST_SHIFT));
+ }
+}
+
+static u_int32_t
+hifn_next_signature(u_int32_t a, u_int cnt)
+{
+ int i;
+ u_int32_t v;
+
+ for (i = 0; i < cnt; i++) {
+
+ /* get the parity */
+ v = a & 0x80080125;
+ v ^= v >> 16;
+ v ^= v >> 8;
+ v ^= v >> 4;
+ v ^= v >> 2;
+ v ^= v >> 1;
+
+ a = (v & 1) ^ (a << 1);
+ }
+
+ return a;
+}
+
+
+/*
+ * Checks to see if crypto is already enabled. If crypto isn't enable,
+ * "hifn_enable_crypto" is called to enable it. The check is important,
+ * as enabling crypto twice will lock the board.
+ */
+static int
+hifn_enable_crypto(struct hifn_softc *sc)
+{
+ u_int32_t dmacfg, ramcfg, encl, addr, i;
+ char offtbl[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 };
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ ramcfg = READ_REG_0(sc, HIFN_0_PUCNFG);
+ dmacfg = READ_REG_1(sc, HIFN_1_DMA_CNFG);
+
+ /*
+ * The RAM config register's encrypt level bit needs to be set before
+ * every read performed on the encryption level register.
+ */
+ WRITE_REG_0(sc, HIFN_0_PUCNFG, ramcfg | HIFN_PUCNFG_CHIPID);
+
+ encl = READ_REG_0(sc, HIFN_0_PUSTAT) & HIFN_PUSTAT_CHIPENA;
+
+ /*
+ * Make sure we don't re-unlock. Two unlocks kills chip until the
+ * next reboot.
+ */
+ if (encl == HIFN_PUSTAT_ENA_1 || encl == HIFN_PUSTAT_ENA_2) {
+#ifdef HIFN_DEBUG
+ if (hifn_debug)
+ device_printf(sc->sc_dev,
+ "Strong crypto already enabled!\n");
+#endif
+ goto report;
+ }
+
+ if (encl != 0 && encl != HIFN_PUSTAT_ENA_0) {
+#ifdef HIFN_DEBUG
+ if (hifn_debug)
+ device_printf(sc->sc_dev,
+ "Unknown encryption level 0x%x\n", encl);
+#endif
+ return 1;
+ }
+
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_UNLOCK |
+ HIFN_DMACNFG_MSTRESET | HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
+ DELAY(1000);
+ addr = READ_REG_1(sc, HIFN_UNLOCK_SECRET1);
+ DELAY(1000);
+ WRITE_REG_1(sc, HIFN_UNLOCK_SECRET2, 0);
+ DELAY(1000);
+
+ for (i = 0; i <= 12; i++) {
+ addr = hifn_next_signature(addr, offtbl[i] + 0x101);
+ WRITE_REG_1(sc, HIFN_UNLOCK_SECRET2, addr);
+
+ DELAY(1000);
+ }
+
+ WRITE_REG_0(sc, HIFN_0_PUCNFG, ramcfg | HIFN_PUCNFG_CHIPID);
+ encl = READ_REG_0(sc, HIFN_0_PUSTAT) & HIFN_PUSTAT_CHIPENA;
+
+#ifdef HIFN_DEBUG
+ if (hifn_debug) {
+ if (encl != HIFN_PUSTAT_ENA_1 && encl != HIFN_PUSTAT_ENA_2)
+ device_printf(sc->sc_dev, "Engine is permanently "
+ "locked until next system reset!\n");
+ else
+ device_printf(sc->sc_dev, "Engine enabled "
+ "successfully!\n");
+ }
+#endif
+
+report:
+ WRITE_REG_0(sc, HIFN_0_PUCNFG, ramcfg);
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, dmacfg);
+
+ switch (encl) {
+ case HIFN_PUSTAT_ENA_1:
+ case HIFN_PUSTAT_ENA_2:
+ break;
+ case HIFN_PUSTAT_ENA_0:
+ default:
+ device_printf(sc->sc_dev, "disabled\n");
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Give initial values to the registers listed in the "Register Space"
+ * section of the HIFN Software Development reference manual.
+ */
+static void
+hifn_init_pci_registers(struct hifn_softc *sc)
+{
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ /* write fixed values needed by the Initialization registers */
+ WRITE_REG_0(sc, HIFN_0_PUCTRL, HIFN_PUCTRL_DMAENA);
+ WRITE_REG_0(sc, HIFN_0_FIFOCNFG, HIFN_FIFOCNFG_THRESHOLD);
+ WRITE_REG_0(sc, HIFN_0_PUIER, HIFN_PUIER_DSTOVER);
+
+ /* write all 4 ring address registers */
+ WRITE_REG_1(sc, HIFN_1_DMA_CRAR, sc->sc_dma_physaddr +
+ offsetof(struct hifn_dma, cmdr[0]));
+ WRITE_REG_1(sc, HIFN_1_DMA_SRAR, sc->sc_dma_physaddr +
+ offsetof(struct hifn_dma, srcr[0]));
+ WRITE_REG_1(sc, HIFN_1_DMA_DRAR, sc->sc_dma_physaddr +
+ offsetof(struct hifn_dma, dstr[0]));
+ WRITE_REG_1(sc, HIFN_1_DMA_RRAR, sc->sc_dma_physaddr +
+ offsetof(struct hifn_dma, resr[0]));
+
+ DELAY(2000);
+
+ /* write status register */
+ WRITE_REG_1(sc, HIFN_1_DMA_CSR,
+ HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS |
+ HIFN_DMACSR_S_CTRL_DIS | HIFN_DMACSR_C_CTRL_DIS |
+ HIFN_DMACSR_D_ABORT | HIFN_DMACSR_D_DONE | HIFN_DMACSR_D_LAST |
+ HIFN_DMACSR_D_WAIT | HIFN_DMACSR_D_OVER |
+ HIFN_DMACSR_R_ABORT | HIFN_DMACSR_R_DONE | HIFN_DMACSR_R_LAST |
+ HIFN_DMACSR_R_WAIT | HIFN_DMACSR_R_OVER |
+ HIFN_DMACSR_S_ABORT | HIFN_DMACSR_S_DONE | HIFN_DMACSR_S_LAST |
+ HIFN_DMACSR_S_WAIT |
+ HIFN_DMACSR_C_ABORT | HIFN_DMACSR_C_DONE | HIFN_DMACSR_C_LAST |
+ HIFN_DMACSR_C_WAIT |
+ HIFN_DMACSR_ENGINE |
+ ((sc->sc_flags & HIFN_HAS_PUBLIC) ?
+ HIFN_DMACSR_PUBDONE : 0) |
+ ((sc->sc_flags & HIFN_IS_7811) ?
+ HIFN_DMACSR_ILLW | HIFN_DMACSR_ILLR : 0));
+
+ sc->sc_d_busy = sc->sc_r_busy = sc->sc_s_busy = sc->sc_c_busy = 0;
+ sc->sc_dmaier |= HIFN_DMAIER_R_DONE | HIFN_DMAIER_C_ABORT |
+ HIFN_DMAIER_D_OVER | HIFN_DMAIER_R_OVER |
+ HIFN_DMAIER_S_ABORT | HIFN_DMAIER_D_ABORT | HIFN_DMAIER_R_ABORT |
+ ((sc->sc_flags & HIFN_IS_7811) ?
+ HIFN_DMAIER_ILLW | HIFN_DMAIER_ILLR : 0);
+ sc->sc_dmaier &= ~HIFN_DMAIER_C_WAIT;
+ WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
+
+
+ if (sc->sc_flags & HIFN_IS_7956) {
+ u_int32_t pll;
+
+ WRITE_REG_0(sc, HIFN_0_PUCNFG, HIFN_PUCNFG_COMPSING |
+ HIFN_PUCNFG_TCALLPHASES |
+ HIFN_PUCNFG_TCDRVTOTEM | HIFN_PUCNFG_BUS32);
+
+ /* turn off the clocks and insure bypass is set */
+ pll = READ_REG_1(sc, HIFN_1_PLL);
+ pll = (pll &~ (HIFN_PLL_PK_CLK_SEL | HIFN_PLL_PE_CLK_SEL))
+ | HIFN_PLL_BP | HIFN_PLL_MBSET;
+ WRITE_REG_1(sc, HIFN_1_PLL, pll);
+ DELAY(10*1000); /* 10ms */
+
+ /* change configuration */
+ pll = (pll &~ HIFN_PLL_CONFIG) | sc->sc_pllconfig;
+ WRITE_REG_1(sc, HIFN_1_PLL, pll);
+ DELAY(10*1000); /* 10ms */
+
+ /* disable bypass */
+ pll &= ~HIFN_PLL_BP;
+ WRITE_REG_1(sc, HIFN_1_PLL, pll);
+ /* enable clocks with new configuration */
+ pll |= HIFN_PLL_PK_CLK_SEL | HIFN_PLL_PE_CLK_SEL;
+ WRITE_REG_1(sc, HIFN_1_PLL, pll);
+ } else {
+ WRITE_REG_0(sc, HIFN_0_PUCNFG, HIFN_PUCNFG_COMPSING |
+ HIFN_PUCNFG_DRFR_128 | HIFN_PUCNFG_TCALLPHASES |
+ HIFN_PUCNFG_TCDRVTOTEM | HIFN_PUCNFG_BUS32 |
+ (sc->sc_drammodel ? HIFN_PUCNFG_DRAM : HIFN_PUCNFG_SRAM));
+ }
+
+ WRITE_REG_0(sc, HIFN_0_PUISR, HIFN_PUISR_DSTOVER);
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE | HIFN_DMACNFG_LAST |
+ ((HIFN_POLL_FREQUENCY << 16 ) & HIFN_DMACNFG_POLLFREQ) |
+ ((HIFN_POLL_SCALAR << 8) & HIFN_DMACNFG_POLLINVAL));
+}
+
+/*
+ * The maximum number of sessions supported by the card
+ * is dependent on the amount of context ram, which
+ * encryption algorithms are enabled, and how compression
+ * is configured. This should be configured before this
+ * routine is called.
+ */
+static void
+hifn_sessions(struct hifn_softc *sc)
+{
+ u_int32_t pucnfg;
+ int ctxsize;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ pucnfg = READ_REG_0(sc, HIFN_0_PUCNFG);
+
+ if (pucnfg & HIFN_PUCNFG_COMPSING) {
+ if (pucnfg & HIFN_PUCNFG_ENCCNFG)
+ ctxsize = 128;
+ else
+ ctxsize = 512;
+ /*
+ * 7955/7956 has internal context memory of 32K
+ */
+ if (sc->sc_flags & HIFN_IS_7956)
+ sc->sc_maxses = 32768 / ctxsize;
+ else
+ sc->sc_maxses = 1 +
+ ((sc->sc_ramsize - 32768) / ctxsize);
+ } else
+ sc->sc_maxses = sc->sc_ramsize / 16384;
+
+ if (sc->sc_maxses > 2048)
+ sc->sc_maxses = 2048;
+}
+
+/*
+ * Determine ram type (sram or dram). Board should be just out of a reset
+ * state when this is called.
+ */
+static int
+hifn_ramtype(struct hifn_softc *sc)
+{
+ u_int8_t data[8], dataexpect[8];
+ int i;
+
+ for (i = 0; i < sizeof(data); i++)
+ data[i] = dataexpect[i] = 0x55;
+ if (hifn_writeramaddr(sc, 0, data))
+ return (-1);
+ if (hifn_readramaddr(sc, 0, data))
+ return (-1);
+ if (bcmp(data, dataexpect, sizeof(data)) != 0) {
+ sc->sc_drammodel = 1;
+ return (0);
+ }
+
+ for (i = 0; i < sizeof(data); i++)
+ data[i] = dataexpect[i] = 0xaa;
+ if (hifn_writeramaddr(sc, 0, data))
+ return (-1);
+ if (hifn_readramaddr(sc, 0, data))
+ return (-1);
+ if (bcmp(data, dataexpect, sizeof(data)) != 0) {
+ sc->sc_drammodel = 1;
+ return (0);
+ }
+
+ return (0);
+}
+
+#define HIFN_SRAM_MAX (32 << 20)
+#define HIFN_SRAM_STEP_SIZE 16384
+#define HIFN_SRAM_GRANULARITY (HIFN_SRAM_MAX / HIFN_SRAM_STEP_SIZE)
+
+static int
+hifn_sramsize(struct hifn_softc *sc)
+{
+ u_int32_t a;
+ u_int8_t data[8];
+ u_int8_t dataexpect[sizeof(data)];
+ int32_t i;
+
+ for (i = 0; i < sizeof(data); i++)
+ data[i] = dataexpect[i] = i ^ 0x5a;
+
+ for (i = HIFN_SRAM_GRANULARITY - 1; i >= 0; i--) {
+ a = i * HIFN_SRAM_STEP_SIZE;
+ bcopy(&i, data, sizeof(i));
+ hifn_writeramaddr(sc, a, data);
+ }
+
+ for (i = 0; i < HIFN_SRAM_GRANULARITY; i++) {
+ a = i * HIFN_SRAM_STEP_SIZE;
+ bcopy(&i, dataexpect, sizeof(i));
+ if (hifn_readramaddr(sc, a, data) < 0)
+ return (0);
+ if (bcmp(data, dataexpect, sizeof(data)) != 0)
+ return (0);
+ sc->sc_ramsize = a + HIFN_SRAM_STEP_SIZE;
+ }
+
+ return (0);
+}
+
+/*
+ * XXX For dram boards, one should really try all of the
+ * HIFN_PUCNFG_DSZ_*'s. This just assumes that PUCNFG
+ * is already set up correctly.
+ */
+static int
+hifn_dramsize(struct hifn_softc *sc)
+{
+ u_int32_t cnfg;
+
+ if (sc->sc_flags & HIFN_IS_7956) {
+ /*
+ * 7955/7956 have a fixed internal ram of only 32K.
+ */
+ sc->sc_ramsize = 32768;
+ } else {
+ cnfg = READ_REG_0(sc, HIFN_0_PUCNFG) &
+ HIFN_PUCNFG_DRAMMASK;
+ sc->sc_ramsize = 1 << ((cnfg >> 13) + 18);
+ }
+ return (0);
+}
+
+static void
+hifn_alloc_slot(struct hifn_softc *sc, int *cmdp, int *srcp, int *dstp, int *resp)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ if (dma->cmdi == HIFN_D_CMD_RSIZE) {
+ dma->cmdi = 0;
+ dma->cmdr[HIFN_D_CMD_RSIZE].l = htole32(HIFN_D_JUMP|HIFN_D_MASKDONEIRQ);
+ wmb();
+ dma->cmdr[HIFN_D_CMD_RSIZE].l |= htole32(HIFN_D_VALID);
+ HIFN_CMDR_SYNC(sc, HIFN_D_CMD_RSIZE,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ }
+ *cmdp = dma->cmdi++;
+ dma->cmdk = dma->cmdi;
+
+ if (dma->srci == HIFN_D_SRC_RSIZE) {
+ dma->srci = 0;
+ dma->srcr[HIFN_D_SRC_RSIZE].l = htole32(HIFN_D_JUMP|HIFN_D_MASKDONEIRQ);
+ wmb();
+ dma->srcr[HIFN_D_SRC_RSIZE].l |= htole32(HIFN_D_VALID);
+ HIFN_SRCR_SYNC(sc, HIFN_D_SRC_RSIZE,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ }
+ *srcp = dma->srci++;
+ dma->srck = dma->srci;
+
+ if (dma->dsti == HIFN_D_DST_RSIZE) {
+ dma->dsti = 0;
+ dma->dstr[HIFN_D_DST_RSIZE].l = htole32(HIFN_D_JUMP|HIFN_D_MASKDONEIRQ);
+ wmb();
+ dma->dstr[HIFN_D_DST_RSIZE].l |= htole32(HIFN_D_VALID);
+ HIFN_DSTR_SYNC(sc, HIFN_D_DST_RSIZE,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ }
+ *dstp = dma->dsti++;
+ dma->dstk = dma->dsti;
+
+ if (dma->resi == HIFN_D_RES_RSIZE) {
+ dma->resi = 0;
+ dma->resr[HIFN_D_RES_RSIZE].l = htole32(HIFN_D_JUMP|HIFN_D_MASKDONEIRQ);
+ wmb();
+ dma->resr[HIFN_D_RES_RSIZE].l |= htole32(HIFN_D_VALID);
+ HIFN_RESR_SYNC(sc, HIFN_D_RES_RSIZE,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ }
+ *resp = dma->resi++;
+ dma->resk = dma->resi;
+}
+
+static int
+hifn_writeramaddr(struct hifn_softc *sc, int addr, u_int8_t *data)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+ hifn_base_command_t wc;
+ const u_int32_t masks = HIFN_D_VALID | HIFN_D_LAST | HIFN_D_MASKDONEIRQ;
+ int r, cmdi, resi, srci, dsti;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ wc.masks = htole16(3 << 13);
+ wc.session_num = htole16(addr >> 14);
+ wc.total_source_count = htole16(8);
+ wc.total_dest_count = htole16(addr & 0x3fff);
+
+ hifn_alloc_slot(sc, &cmdi, &srci, &dsti, &resi);
+
+ WRITE_REG_1(sc, HIFN_1_DMA_CSR,
+ HIFN_DMACSR_C_CTRL_ENA | HIFN_DMACSR_S_CTRL_ENA |
+ HIFN_DMACSR_D_CTRL_ENA | HIFN_DMACSR_R_CTRL_ENA);
+
+ /* build write command */
+ bzero(dma->command_bufs[cmdi], HIFN_MAX_COMMAND);
+ *(hifn_base_command_t *)dma->command_bufs[cmdi] = wc;
+ bcopy(data, &dma->test_src, sizeof(dma->test_src));
+
+ dma->srcr[srci].p = htole32(sc->sc_dma_physaddr
+ + offsetof(struct hifn_dma, test_src));
+ dma->dstr[dsti].p = htole32(sc->sc_dma_physaddr
+ + offsetof(struct hifn_dma, test_dst));
+
+ dma->cmdr[cmdi].l = htole32(16 | masks);
+ dma->srcr[srci].l = htole32(8 | masks);
+ dma->dstr[dsti].l = htole32(4 | masks);
+ dma->resr[resi].l = htole32(4 | masks);
+
+ for (r = 10000; r >= 0; r--) {
+ DELAY(10);
+ if ((dma->resr[resi].l & htole32(HIFN_D_VALID)) == 0)
+ break;
+ }
+ if (r == 0) {
+ device_printf(sc->sc_dev, "writeramaddr -- "
+ "result[%d](addr %d) still valid\n", resi, addr);
+ r = -1;
+ return (-1);
+ } else
+ r = 0;
+
+ WRITE_REG_1(sc, HIFN_1_DMA_CSR,
+ HIFN_DMACSR_C_CTRL_DIS | HIFN_DMACSR_S_CTRL_DIS |
+ HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS);
+
+ return (r);
+}
+
+static int
+hifn_readramaddr(struct hifn_softc *sc, int addr, u_int8_t *data)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+ hifn_base_command_t rc;
+ const u_int32_t masks = HIFN_D_VALID | HIFN_D_LAST | HIFN_D_MASKDONEIRQ;
+ int r, cmdi, srci, dsti, resi;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ rc.masks = htole16(2 << 13);
+ rc.session_num = htole16(addr >> 14);
+ rc.total_source_count = htole16(addr & 0x3fff);
+ rc.total_dest_count = htole16(8);
+
+ hifn_alloc_slot(sc, &cmdi, &srci, &dsti, &resi);
+
+ WRITE_REG_1(sc, HIFN_1_DMA_CSR,
+ HIFN_DMACSR_C_CTRL_ENA | HIFN_DMACSR_S_CTRL_ENA |
+ HIFN_DMACSR_D_CTRL_ENA | HIFN_DMACSR_R_CTRL_ENA);
+
+ bzero(dma->command_bufs[cmdi], HIFN_MAX_COMMAND);
+ *(hifn_base_command_t *)dma->command_bufs[cmdi] = rc;
+
+ dma->srcr[srci].p = htole32(sc->sc_dma_physaddr +
+ offsetof(struct hifn_dma, test_src));
+ dma->test_src = 0;
+ dma->dstr[dsti].p = htole32(sc->sc_dma_physaddr +
+ offsetof(struct hifn_dma, test_dst));
+ dma->test_dst = 0;
+ dma->cmdr[cmdi].l = htole32(8 | masks);
+ dma->srcr[srci].l = htole32(8 | masks);
+ dma->dstr[dsti].l = htole32(8 | masks);
+ dma->resr[resi].l = htole32(HIFN_MAX_RESULT | masks);
+
+ for (r = 10000; r >= 0; r--) {
+ DELAY(10);
+ if ((dma->resr[resi].l & htole32(HIFN_D_VALID)) == 0)
+ break;
+ }
+ if (r == 0) {
+ device_printf(sc->sc_dev, "readramaddr -- "
+ "result[%d](addr %d) still valid\n", resi, addr);
+ r = -1;
+ } else {
+ r = 0;
+ bcopy(&dma->test_dst, data, sizeof(dma->test_dst));
+ }
+
+ WRITE_REG_1(sc, HIFN_1_DMA_CSR,
+ HIFN_DMACSR_C_CTRL_DIS | HIFN_DMACSR_S_CTRL_DIS |
+ HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS);
+
+ return (r);
+}
+
+/*
+ * Initialize the descriptor rings.
+ */
+static void
+hifn_init_dma(struct hifn_softc *sc)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+ int i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ hifn_set_retry(sc);
+
+ /* initialize static pointer values */
+ for (i = 0; i < HIFN_D_CMD_RSIZE; i++)
+ dma->cmdr[i].p = htole32(sc->sc_dma_physaddr +
+ offsetof(struct hifn_dma, command_bufs[i][0]));
+ for (i = 0; i < HIFN_D_RES_RSIZE; i++)
+ dma->resr[i].p = htole32(sc->sc_dma_physaddr +
+ offsetof(struct hifn_dma, result_bufs[i][0]));
+
+ dma->cmdr[HIFN_D_CMD_RSIZE].p =
+ htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, cmdr[0]));
+ dma->srcr[HIFN_D_SRC_RSIZE].p =
+ htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, srcr[0]));
+ dma->dstr[HIFN_D_DST_RSIZE].p =
+ htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, dstr[0]));
+ dma->resr[HIFN_D_RES_RSIZE].p =
+ htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, resr[0]));
+
+ dma->cmdu = dma->srcu = dma->dstu = dma->resu = 0;
+ dma->cmdi = dma->srci = dma->dsti = dma->resi = 0;
+ dma->cmdk = dma->srck = dma->dstk = dma->resk = 0;
+}
+
+/*
+ * Writes out the raw command buffer space. Returns the
+ * command buffer size.
+ */
+static u_int
+hifn_write_command(struct hifn_command *cmd, u_int8_t *buf)
+{
+ struct hifn_softc *sc = NULL;
+ u_int8_t *buf_pos;
+ hifn_base_command_t *base_cmd;
+ hifn_mac_command_t *mac_cmd;
+ hifn_crypt_command_t *cry_cmd;
+ int using_mac, using_crypt, len, ivlen;
+ u_int32_t dlen, slen;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ buf_pos = buf;
+ using_mac = cmd->base_masks & HIFN_BASE_CMD_MAC;
+ using_crypt = cmd->base_masks & HIFN_BASE_CMD_CRYPT;
+
+ base_cmd = (hifn_base_command_t *)buf_pos;
+ base_cmd->masks = htole16(cmd->base_masks);
+ slen = cmd->src_mapsize;
+ if (cmd->sloplen)
+ dlen = cmd->dst_mapsize - cmd->sloplen + sizeof(u_int32_t);
+ else
+ dlen = cmd->dst_mapsize;
+ base_cmd->total_source_count = htole16(slen & HIFN_BASE_CMD_LENMASK_LO);
+ base_cmd->total_dest_count = htole16(dlen & HIFN_BASE_CMD_LENMASK_LO);
+ dlen >>= 16;
+ slen >>= 16;
+ base_cmd->session_num = htole16(
+ ((slen << HIFN_BASE_CMD_SRCLEN_S) & HIFN_BASE_CMD_SRCLEN_M) |
+ ((dlen << HIFN_BASE_CMD_DSTLEN_S) & HIFN_BASE_CMD_DSTLEN_M));
+ buf_pos += sizeof(hifn_base_command_t);
+
+ if (using_mac) {
+ mac_cmd = (hifn_mac_command_t *)buf_pos;
+ dlen = cmd->maccrd->crd_len;
+ mac_cmd->source_count = htole16(dlen & 0xffff);
+ dlen >>= 16;
+ mac_cmd->masks = htole16(cmd->mac_masks |
+ ((dlen << HIFN_MAC_CMD_SRCLEN_S) & HIFN_MAC_CMD_SRCLEN_M));
+ mac_cmd->header_skip = htole16(cmd->maccrd->crd_skip);
+ mac_cmd->reserved = 0;
+ buf_pos += sizeof(hifn_mac_command_t);
+ }
+
+ if (using_crypt) {
+ cry_cmd = (hifn_crypt_command_t *)buf_pos;
+ dlen = cmd->enccrd->crd_len;
+ cry_cmd->source_count = htole16(dlen & 0xffff);
+ dlen >>= 16;
+ cry_cmd->masks = htole16(cmd->cry_masks |
+ ((dlen << HIFN_CRYPT_CMD_SRCLEN_S) & HIFN_CRYPT_CMD_SRCLEN_M));
+ cry_cmd->header_skip = htole16(cmd->enccrd->crd_skip);
+ cry_cmd->reserved = 0;
+ buf_pos += sizeof(hifn_crypt_command_t);
+ }
+
+ if (using_mac && cmd->mac_masks & HIFN_MAC_CMD_NEW_KEY) {
+ bcopy(cmd->mac, buf_pos, HIFN_MAC_KEY_LENGTH);
+ buf_pos += HIFN_MAC_KEY_LENGTH;
+ }
+
+ if (using_crypt && cmd->cry_masks & HIFN_CRYPT_CMD_NEW_KEY) {
+ switch (cmd->cry_masks & HIFN_CRYPT_CMD_ALG_MASK) {
+ case HIFN_CRYPT_CMD_ALG_3DES:
+ bcopy(cmd->ck, buf_pos, HIFN_3DES_KEY_LENGTH);
+ buf_pos += HIFN_3DES_KEY_LENGTH;
+ break;
+ case HIFN_CRYPT_CMD_ALG_DES:
+ bcopy(cmd->ck, buf_pos, HIFN_DES_KEY_LENGTH);
+ buf_pos += HIFN_DES_KEY_LENGTH;
+ break;
+ case HIFN_CRYPT_CMD_ALG_RC4:
+ len = 256;
+ do {
+ int clen;
+
+ clen = MIN(cmd->cklen, len);
+ bcopy(cmd->ck, buf_pos, clen);
+ len -= clen;
+ buf_pos += clen;
+ } while (len > 0);
+ bzero(buf_pos, 4);
+ buf_pos += 4;
+ break;
+ case HIFN_CRYPT_CMD_ALG_AES:
+ /*
+ * AES keys are variable 128, 192 and
+ * 256 bits (16, 24 and 32 bytes).
+ */
+ bcopy(cmd->ck, buf_pos, cmd->cklen);
+ buf_pos += cmd->cklen;
+ break;
+ }
+ }
+
+ if (using_crypt && cmd->cry_masks & HIFN_CRYPT_CMD_NEW_IV) {
+ switch (cmd->cry_masks & HIFN_CRYPT_CMD_ALG_MASK) {
+ case HIFN_CRYPT_CMD_ALG_AES:
+ ivlen = HIFN_AES_IV_LENGTH;
+ break;
+ default:
+ ivlen = HIFN_IV_LENGTH;
+ break;
+ }
+ bcopy(cmd->iv, buf_pos, ivlen);
+ buf_pos += ivlen;
+ }
+
+ if ((cmd->base_masks & (HIFN_BASE_CMD_MAC|HIFN_BASE_CMD_CRYPT)) == 0) {
+ bzero(buf_pos, 8);
+ buf_pos += 8;
+ }
+
+ return (buf_pos - buf);
+}
+
+static int
+hifn_dmamap_aligned(struct hifn_operand *op)
+{
+ struct hifn_softc *sc = NULL;
+ int i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ for (i = 0; i < op->nsegs; i++) {
+ if (op->segs[i].ds_addr & 3)
+ return (0);
+ if ((i != (op->nsegs - 1)) && (op->segs[i].ds_len & 3))
+ return (0);
+ }
+ return (1);
+}
+
+static __inline int
+hifn_dmamap_dstwrap(struct hifn_softc *sc, int idx)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+
+ if (++idx == HIFN_D_DST_RSIZE) {
+ dma->dstr[idx].l = htole32(HIFN_D_VALID | HIFN_D_JUMP |
+ HIFN_D_MASKDONEIRQ);
+ HIFN_DSTR_SYNC(sc, idx,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ idx = 0;
+ }
+ return (idx);
+}
+
+static int
+hifn_dmamap_load_dst(struct hifn_softc *sc, struct hifn_command *cmd)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+ struct hifn_operand *dst = &cmd->dst;
+ u_int32_t p, l;
+ int idx, used = 0, i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ idx = dma->dsti;
+ for (i = 0; i < dst->nsegs - 1; i++) {
+ dma->dstr[idx].p = htole32(dst->segs[i].ds_addr);
+ dma->dstr[idx].l = htole32(HIFN_D_MASKDONEIRQ | dst->segs[i].ds_len);
+ wmb();
+ dma->dstr[idx].l |= htole32(HIFN_D_VALID);
+ HIFN_DSTR_SYNC(sc, idx,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ used++;
+
+ idx = hifn_dmamap_dstwrap(sc, idx);
+ }
+
+ if (cmd->sloplen == 0) {
+ p = dst->segs[i].ds_addr;
+ l = HIFN_D_MASKDONEIRQ | HIFN_D_LAST |
+ dst->segs[i].ds_len;
+ } else {
+ p = sc->sc_dma_physaddr +
+ offsetof(struct hifn_dma, slop[cmd->slopidx]);
+ l = HIFN_D_MASKDONEIRQ | HIFN_D_LAST |
+ sizeof(u_int32_t);
+
+ if ((dst->segs[i].ds_len - cmd->sloplen) != 0) {
+ dma->dstr[idx].p = htole32(dst->segs[i].ds_addr);
+ dma->dstr[idx].l = htole32(HIFN_D_MASKDONEIRQ |
+ (dst->segs[i].ds_len - cmd->sloplen));
+ wmb();
+ dma->dstr[idx].l |= htole32(HIFN_D_VALID);
+ HIFN_DSTR_SYNC(sc, idx,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ used++;
+
+ idx = hifn_dmamap_dstwrap(sc, idx);
+ }
+ }
+ dma->dstr[idx].p = htole32(p);
+ dma->dstr[idx].l = htole32(l);
+ wmb();
+ dma->dstr[idx].l |= htole32(HIFN_D_VALID);
+ HIFN_DSTR_SYNC(sc, idx, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ used++;
+
+ idx = hifn_dmamap_dstwrap(sc, idx);
+
+ dma->dsti = idx;
+ dma->dstu += used;
+ return (idx);
+}
+
+static __inline int
+hifn_dmamap_srcwrap(struct hifn_softc *sc, int idx)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+
+ if (++idx == HIFN_D_SRC_RSIZE) {
+ dma->srcr[idx].l = htole32(HIFN_D_VALID |
+ HIFN_D_JUMP | HIFN_D_MASKDONEIRQ);
+ HIFN_SRCR_SYNC(sc, HIFN_D_SRC_RSIZE,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ idx = 0;
+ }
+ return (idx);
+}
+
+static int
+hifn_dmamap_load_src(struct hifn_softc *sc, struct hifn_command *cmd)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+ struct hifn_operand *src = &cmd->src;
+ int idx, i;
+ u_int32_t last = 0;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ idx = dma->srci;
+ for (i = 0; i < src->nsegs; i++) {
+ if (i == src->nsegs - 1)
+ last = HIFN_D_LAST;
+
+ dma->srcr[idx].p = htole32(src->segs[i].ds_addr);
+ dma->srcr[idx].l = htole32(src->segs[i].ds_len |
+ HIFN_D_MASKDONEIRQ | last);
+ wmb();
+ dma->srcr[idx].l |= htole32(HIFN_D_VALID);
+ HIFN_SRCR_SYNC(sc, idx,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ idx = hifn_dmamap_srcwrap(sc, idx);
+ }
+ dma->srci = idx;
+ dma->srcu += src->nsegs;
+ return (idx);
+}
+
+
+static int
+hifn_crypto(
+ struct hifn_softc *sc,
+ struct hifn_command *cmd,
+ struct cryptop *crp,
+ int hint)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+ u_int32_t cmdlen, csr;
+ int cmdi, resi, err = 0;
+ unsigned long l_flags;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ /*
+ * need 1 cmd, and 1 res
+ *
+ * NB: check this first since it's easy.
+ */
+ HIFN_LOCK(sc);
+ if ((dma->cmdu + 1) > HIFN_D_CMD_RSIZE ||
+ (dma->resu + 1) > HIFN_D_RES_RSIZE) {
+#ifdef HIFN_DEBUG
+ if (hifn_debug) {
+ device_printf(sc->sc_dev,
+ "cmd/result exhaustion, cmdu %u resu %u\n",
+ dma->cmdu, dma->resu);
+ }
+#endif
+ hifnstats.hst_nomem_cr++;
+ sc->sc_needwakeup |= CRYPTO_SYMQ;
+ HIFN_UNLOCK(sc);
+ return (ERESTART);
+ }
+
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ if (pci_map_skb(sc, &cmd->src, cmd->src_skb)) {
+ hifnstats.hst_nomem_load++;
+ err = ENOMEM;
+ goto err_srcmap1;
+ }
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ if (pci_map_uio(sc, &cmd->src, cmd->src_io)) {
+ hifnstats.hst_nomem_load++;
+ err = ENOMEM;
+ goto err_srcmap1;
+ }
+ } else {
+ if (pci_map_buf(sc, &cmd->src, cmd->src_buf, crp->crp_ilen)) {
+ hifnstats.hst_nomem_load++;
+ err = ENOMEM;
+ goto err_srcmap1;
+ }
+ }
+
+ if (hifn_dmamap_aligned(&cmd->src)) {
+ cmd->sloplen = cmd->src_mapsize & 3;
+ cmd->dst = cmd->src;
+ } else {
+ if (crp->crp_flags & CRYPTO_F_IOV) {
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ err = EINVAL;
+ goto err_srcmap;
+ } else if (crp->crp_flags & CRYPTO_F_SKBUF) {
+#ifdef NOTYET
+ int totlen, len;
+ struct mbuf *m, *m0, *mlast;
+
+ KASSERT(cmd->dst_m == cmd->src_m,
+ ("hifn_crypto: dst_m initialized improperly"));
+ hifnstats.hst_unaligned++;
+ /*
+ * Source is not aligned on a longword boundary.
+ * Copy the data to insure alignment. If we fail
+ * to allocate mbufs or clusters while doing this
+ * we return ERESTART so the operation is requeued
+ * at the crypto later, but only if there are
+ * ops already posted to the hardware; otherwise we
+ * have no guarantee that we'll be re-entered.
+ */
+ totlen = cmd->src_mapsize;
+ if (cmd->src_m->m_flags & M_PKTHDR) {
+ len = MHLEN;
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 && !m_dup_pkthdr(m0, cmd->src_m, M_DONTWAIT)) {
+ m_free(m0);
+ m0 = NULL;
+ }
+ } else {
+ len = MLEN;
+ MGET(m0, M_DONTWAIT, MT_DATA);
+ }
+ if (m0 == NULL) {
+ hifnstats.hst_nomem_mbuf++;
+ err = dma->cmdu ? ERESTART : ENOMEM;
+ goto err_srcmap;
+ }
+ if (totlen >= MINCLSIZE) {
+ MCLGET(m0, M_DONTWAIT);
+ if ((m0->m_flags & M_EXT) == 0) {
+ hifnstats.hst_nomem_mcl++;
+ err = dma->cmdu ? ERESTART : ENOMEM;
+ m_freem(m0);
+ goto err_srcmap;
+ }
+ len = MCLBYTES;
+ }
+ totlen -= len;
+ m0->m_pkthdr.len = m0->m_len = len;
+ mlast = m0;
+
+ while (totlen > 0) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ hifnstats.hst_nomem_mbuf++;
+ err = dma->cmdu ? ERESTART : ENOMEM;
+ m_freem(m0);
+ goto err_srcmap;
+ }
+ len = MLEN;
+ if (totlen >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ hifnstats.hst_nomem_mcl++;
+ err = dma->cmdu ? ERESTART : ENOMEM;
+ mlast->m_next = m;
+ m_freem(m0);
+ goto err_srcmap;
+ }
+ len = MCLBYTES;
+ }
+
+ m->m_len = len;
+ m0->m_pkthdr.len += len;
+ totlen -= len;
+
+ mlast->m_next = m;
+ mlast = m;
+ }
+ cmd->dst_m = m0;
+#else
+ device_printf(sc->sc_dev,
+ "%s,%d: CRYPTO_F_SKBUF unaligned not implemented\n",
+ __FILE__, __LINE__);
+ err = EINVAL;
+ goto err_srcmap;
+#endif
+ } else {
+ device_printf(sc->sc_dev,
+ "%s,%d: unaligned contig buffers not implemented\n",
+ __FILE__, __LINE__);
+ err = EINVAL;
+ goto err_srcmap;
+ }
+ }
+
+ if (cmd->dst_map == NULL) {
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ if (pci_map_skb(sc, &cmd->dst, cmd->dst_skb)) {
+ hifnstats.hst_nomem_map++;
+ err = ENOMEM;
+ goto err_dstmap1;
+ }
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ if (pci_map_uio(sc, &cmd->dst, cmd->dst_io)) {
+ hifnstats.hst_nomem_load++;
+ err = ENOMEM;
+ goto err_dstmap1;
+ }
+ } else {
+ if (pci_map_buf(sc, &cmd->dst, cmd->dst_buf, crp->crp_ilen)) {
+ hifnstats.hst_nomem_load++;
+ err = ENOMEM;
+ goto err_dstmap1;
+ }
+ }
+ }
+
+#ifdef HIFN_DEBUG
+ if (hifn_debug) {
+ device_printf(sc->sc_dev,
+ "Entering cmd: stat %8x ien %8x u %d/%d/%d/%d n %d/%d\n",
+ READ_REG_1(sc, HIFN_1_DMA_CSR),
+ READ_REG_1(sc, HIFN_1_DMA_IER),
+ dma->cmdu, dma->srcu, dma->dstu, dma->resu,
+ cmd->src_nsegs, cmd->dst_nsegs);
+ }
+#endif
+
+#if 0
+ if (cmd->src_map == cmd->dst_map) {
+ bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
+ BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
+ } else {
+ bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
+ BUS_DMASYNC_PREWRITE);
+ bus_dmamap_sync(sc->sc_dmat, cmd->dst_map,
+ BUS_DMASYNC_PREREAD);
+ }
+#endif
+
+ /*
+ * need N src, and N dst
+ */
+ if ((dma->srcu + cmd->src_nsegs) > HIFN_D_SRC_RSIZE ||
+ (dma->dstu + cmd->dst_nsegs + 1) > HIFN_D_DST_RSIZE) {
+#ifdef HIFN_DEBUG
+ if (hifn_debug) {
+ device_printf(sc->sc_dev,
+ "src/dst exhaustion, srcu %u+%u dstu %u+%u\n",
+ dma->srcu, cmd->src_nsegs,
+ dma->dstu, cmd->dst_nsegs);
+ }
+#endif
+ hifnstats.hst_nomem_sd++;
+ err = ERESTART;
+ goto err_dstmap;
+ }
+
+ if (dma->cmdi == HIFN_D_CMD_RSIZE) {
+ dma->cmdi = 0;
+ dma->cmdr[HIFN_D_CMD_RSIZE].l = htole32(HIFN_D_JUMP|HIFN_D_MASKDONEIRQ);
+ wmb();
+ dma->cmdr[HIFN_D_CMD_RSIZE].l |= htole32(HIFN_D_VALID);
+ HIFN_CMDR_SYNC(sc, HIFN_D_CMD_RSIZE,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ }
+ cmdi = dma->cmdi++;
+ cmdlen = hifn_write_command(cmd, dma->command_bufs[cmdi]);
+ HIFN_CMD_SYNC(sc, cmdi, BUS_DMASYNC_PREWRITE);
+
+ /* .p for command/result already set */
+ dma->cmdr[cmdi].l = htole32(cmdlen | HIFN_D_LAST |
+ HIFN_D_MASKDONEIRQ);
+ wmb();
+ dma->cmdr[cmdi].l |= htole32(HIFN_D_VALID);
+ HIFN_CMDR_SYNC(sc, cmdi,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ dma->cmdu++;
+
+ /*
+ * We don't worry about missing an interrupt (which a "command wait"
+ * interrupt salvages us from), unless there is more than one command
+ * in the queue.
+ */
+ if (dma->cmdu > 1) {
+ sc->sc_dmaier |= HIFN_DMAIER_C_WAIT;
+ WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
+ }
+
+ hifnstats.hst_ipackets++;
+ hifnstats.hst_ibytes += cmd->src_mapsize;
+
+ hifn_dmamap_load_src(sc, cmd);
+
+ /*
+ * Unlike other descriptors, we don't mask done interrupt from
+ * result descriptor.
+ */
+#ifdef HIFN_DEBUG
+ if (hifn_debug)
+ device_printf(sc->sc_dev, "load res\n");
+#endif
+ if (dma->resi == HIFN_D_RES_RSIZE) {
+ dma->resi = 0;
+ dma->resr[HIFN_D_RES_RSIZE].l = htole32(HIFN_D_JUMP|HIFN_D_MASKDONEIRQ);
+ wmb();
+ dma->resr[HIFN_D_RES_RSIZE].l |= htole32(HIFN_D_VALID);
+ HIFN_RESR_SYNC(sc, HIFN_D_RES_RSIZE,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ }
+ resi = dma->resi++;
+ KASSERT(dma->hifn_commands[resi] == NULL,
+ ("hifn_crypto: command slot %u busy", resi));
+ dma->hifn_commands[resi] = cmd;
+ HIFN_RES_SYNC(sc, resi, BUS_DMASYNC_PREREAD);
+ if ((hint & CRYPTO_HINT_MORE) && sc->sc_curbatch < hifn_maxbatch) {
+ dma->resr[resi].l = htole32(HIFN_MAX_RESULT |
+ HIFN_D_LAST | HIFN_D_MASKDONEIRQ);
+ wmb();
+ dma->resr[resi].l |= htole32(HIFN_D_VALID);
+ sc->sc_curbatch++;
+ if (sc->sc_curbatch > hifnstats.hst_maxbatch)
+ hifnstats.hst_maxbatch = sc->sc_curbatch;
+ hifnstats.hst_totbatch++;
+ } else {
+ dma->resr[resi].l = htole32(HIFN_MAX_RESULT | HIFN_D_LAST);
+ wmb();
+ dma->resr[resi].l |= htole32(HIFN_D_VALID);
+ sc->sc_curbatch = 0;
+ }
+ HIFN_RESR_SYNC(sc, resi,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ dma->resu++;
+
+ if (cmd->sloplen)
+ cmd->slopidx = resi;
+
+ hifn_dmamap_load_dst(sc, cmd);
+
+ csr = 0;
+ if (sc->sc_c_busy == 0) {
+ csr |= HIFN_DMACSR_C_CTRL_ENA;
+ sc->sc_c_busy = 1;
+ }
+ if (sc->sc_s_busy == 0) {
+ csr |= HIFN_DMACSR_S_CTRL_ENA;
+ sc->sc_s_busy = 1;
+ }
+ if (sc->sc_r_busy == 0) {
+ csr |= HIFN_DMACSR_R_CTRL_ENA;
+ sc->sc_r_busy = 1;
+ }
+ if (sc->sc_d_busy == 0) {
+ csr |= HIFN_DMACSR_D_CTRL_ENA;
+ sc->sc_d_busy = 1;
+ }
+ if (csr)
+ WRITE_REG_1(sc, HIFN_1_DMA_CSR, csr);
+
+#ifdef HIFN_DEBUG
+ if (hifn_debug) {
+ device_printf(sc->sc_dev, "command: stat %8x ier %8x\n",
+ READ_REG_1(sc, HIFN_1_DMA_CSR),
+ READ_REG_1(sc, HIFN_1_DMA_IER));
+ }
+#endif
+
+ sc->sc_active = 5;
+ HIFN_UNLOCK(sc);
+ KASSERT(err == 0, ("hifn_crypto: success with error %u", err));
+ return (err); /* success */
+
+err_dstmap:
+ if (cmd->src_map != cmd->dst_map)
+ pci_unmap_buf(sc, &cmd->dst);
+err_dstmap1:
+err_srcmap:
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ if (cmd->src_skb != cmd->dst_skb)
+#ifdef NOTYET
+ m_freem(cmd->dst_m);
+#else
+ device_printf(sc->sc_dev,
+ "%s,%d: CRYPTO_F_SKBUF src != dst not implemented\n",
+ __FILE__, __LINE__);
+#endif
+ }
+ pci_unmap_buf(sc, &cmd->src);
+err_srcmap1:
+ HIFN_UNLOCK(sc);
+ return (err);
+}
+
+static void
+hifn_tick(unsigned long arg)
+{
+ struct hifn_softc *sc;
+ unsigned long l_flags;
+
+ if (arg >= HIFN_MAX_CHIPS)
+ return;
+ sc = hifn_chip_idx[arg];
+ if (!sc)
+ return;
+
+ HIFN_LOCK(sc);
+ if (sc->sc_active == 0) {
+ struct hifn_dma *dma = sc->sc_dma;
+ u_int32_t r = 0;
+
+ if (dma->cmdu == 0 && sc->sc_c_busy) {
+ sc->sc_c_busy = 0;
+ r |= HIFN_DMACSR_C_CTRL_DIS;
+ }
+ if (dma->srcu == 0 && sc->sc_s_busy) {
+ sc->sc_s_busy = 0;
+ r |= HIFN_DMACSR_S_CTRL_DIS;
+ }
+ if (dma->dstu == 0 && sc->sc_d_busy) {
+ sc->sc_d_busy = 0;
+ r |= HIFN_DMACSR_D_CTRL_DIS;
+ }
+ if (dma->resu == 0 && sc->sc_r_busy) {
+ sc->sc_r_busy = 0;
+ r |= HIFN_DMACSR_R_CTRL_DIS;
+ }
+ if (r)
+ WRITE_REG_1(sc, HIFN_1_DMA_CSR, r);
+ } else
+ sc->sc_active--;
+ HIFN_UNLOCK(sc);
+ mod_timer(&sc->sc_tickto, jiffies + HZ);
+}
+
+static irqreturn_t
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+hifn_intr(int irq, void *arg)
+#else
+hifn_intr(int irq, void *arg, struct pt_regs *regs)
+#endif
+{
+ struct hifn_softc *sc = arg;
+ struct hifn_dma *dma;
+ u_int32_t dmacsr, restart;
+ int i, u;
+ unsigned long l_flags;
+
+ dmacsr = READ_REG_1(sc, HIFN_1_DMA_CSR);
+
+ /* Nothing in the DMA unit interrupted */
+ if ((dmacsr & sc->sc_dmaier) == 0)
+ return IRQ_NONE;
+
+ HIFN_LOCK(sc);
+
+ dma = sc->sc_dma;
+
+#ifdef HIFN_DEBUG
+ if (hifn_debug) {
+ device_printf(sc->sc_dev,
+ "irq: stat %08x ien %08x damier %08x i %d/%d/%d/%d k %d/%d/%d/%d u %d/%d/%d/%d\n",
+ dmacsr, READ_REG_1(sc, HIFN_1_DMA_IER), sc->sc_dmaier,
+ dma->cmdi, dma->srci, dma->dsti, dma->resi,
+ dma->cmdk, dma->srck, dma->dstk, dma->resk,
+ dma->cmdu, dma->srcu, dma->dstu, dma->resu);
+ }
+#endif
+
+ WRITE_REG_1(sc, HIFN_1_DMA_CSR, dmacsr & sc->sc_dmaier);
+
+ if ((sc->sc_flags & HIFN_HAS_PUBLIC) &&
+ (dmacsr & HIFN_DMACSR_PUBDONE))
+ WRITE_REG_1(sc, HIFN_1_PUB_STATUS,
+ READ_REG_1(sc, HIFN_1_PUB_STATUS) | HIFN_PUBSTS_DONE);
+
+ restart = dmacsr & (HIFN_DMACSR_D_OVER | HIFN_DMACSR_R_OVER);
+ if (restart)
+ device_printf(sc->sc_dev, "overrun %x\n", dmacsr);
+
+ if (sc->sc_flags & HIFN_IS_7811) {
+ if (dmacsr & HIFN_DMACSR_ILLR)
+ device_printf(sc->sc_dev, "illegal read\n");
+ if (dmacsr & HIFN_DMACSR_ILLW)
+ device_printf(sc->sc_dev, "illegal write\n");
+ }
+
+ restart = dmacsr & (HIFN_DMACSR_C_ABORT | HIFN_DMACSR_S_ABORT |
+ HIFN_DMACSR_D_ABORT | HIFN_DMACSR_R_ABORT);
+ if (restart) {
+ device_printf(sc->sc_dev, "abort, resetting.\n");
+ hifnstats.hst_abort++;
+ hifn_abort(sc);
+ HIFN_UNLOCK(sc);
+ return IRQ_HANDLED;
+ }
+
+ if ((dmacsr & HIFN_DMACSR_C_WAIT) && (dma->cmdu == 0)) {
+ /*
+ * If no slots to process and we receive a "waiting on
+ * command" interrupt, we disable the "waiting on command"
+ * (by clearing it).
+ */
+ sc->sc_dmaier &= ~HIFN_DMAIER_C_WAIT;
+ WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
+ }
+
+ /* clear the rings */
+ i = dma->resk; u = dma->resu;
+ while (u != 0) {
+ HIFN_RESR_SYNC(sc, i,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ if (dma->resr[i].l & htole32(HIFN_D_VALID)) {
+ HIFN_RESR_SYNC(sc, i,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ break;
+ }
+
+ if (i != HIFN_D_RES_RSIZE) {
+ struct hifn_command *cmd;
+ u_int8_t *macbuf = NULL;
+
+ HIFN_RES_SYNC(sc, i, BUS_DMASYNC_POSTREAD);
+ cmd = dma->hifn_commands[i];
+ KASSERT(cmd != NULL,
+ ("hifn_intr: null command slot %u", i));
+ dma->hifn_commands[i] = NULL;
+
+ if (cmd->base_masks & HIFN_BASE_CMD_MAC) {
+ macbuf = dma->result_bufs[i];
+ macbuf += 12;
+ }
+
+ hifn_callback(sc, cmd, macbuf);
+ hifnstats.hst_opackets++;
+ u--;
+ }
+
+ if (++i == (HIFN_D_RES_RSIZE + 1))
+ i = 0;
+ }
+ dma->resk = i; dma->resu = u;
+
+ i = dma->srck; u = dma->srcu;
+ while (u != 0) {
+ if (i == HIFN_D_SRC_RSIZE)
+ i = 0;
+ HIFN_SRCR_SYNC(sc, i,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ if (dma->srcr[i].l & htole32(HIFN_D_VALID)) {
+ HIFN_SRCR_SYNC(sc, i,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ break;
+ }
+ i++, u--;
+ }
+ dma->srck = i; dma->srcu = u;
+
+ i = dma->cmdk; u = dma->cmdu;
+ while (u != 0) {
+ HIFN_CMDR_SYNC(sc, i,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ if (dma->cmdr[i].l & htole32(HIFN_D_VALID)) {
+ HIFN_CMDR_SYNC(sc, i,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ break;
+ }
+ if (i != HIFN_D_CMD_RSIZE) {
+ u--;
+ HIFN_CMD_SYNC(sc, i, BUS_DMASYNC_POSTWRITE);
+ }
+ if (++i == (HIFN_D_CMD_RSIZE + 1))
+ i = 0;
+ }
+ dma->cmdk = i; dma->cmdu = u;
+
+ HIFN_UNLOCK(sc);
+
+ if (sc->sc_needwakeup) { /* XXX check high watermark */
+ int wakeup = sc->sc_needwakeup & (CRYPTO_SYMQ|CRYPTO_ASYMQ);
+#ifdef HIFN_DEBUG
+ if (hifn_debug)
+ device_printf(sc->sc_dev,
+ "wakeup crypto (%x) u %d/%d/%d/%d\n",
+ sc->sc_needwakeup,
+ dma->cmdu, dma->srcu, dma->dstu, dma->resu);
+#endif
+ sc->sc_needwakeup &= ~wakeup;
+ crypto_unblock(sc->sc_cid, wakeup);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Allocate a new 'session' and return an encoded session id. 'sidp'
+ * contains our registration id, and should contain an encoded session
+ * id on successful allocation.
+ */
+static int
+hifn_newsession(device_t dev, u_int32_t *sidp, struct cryptoini *cri)
+{
+ struct hifn_softc *sc = device_get_softc(dev);
+ struct cryptoini *c;
+ int mac = 0, cry = 0, sesn;
+ struct hifn_session *ses = NULL;
+ unsigned long l_flags;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ KASSERT(sc != NULL, ("hifn_newsession: null softc"));
+ if (sidp == NULL || cri == NULL || sc == NULL) {
+ DPRINTF("%s,%d: %s - EINVAL\n", __FILE__, __LINE__, __FUNCTION__);
+ return (EINVAL);
+ }
+
+ HIFN_LOCK(sc);
+ if (sc->sc_sessions == NULL) {
+ ses = sc->sc_sessions = (struct hifn_session *)kmalloc(sizeof(*ses),
+ SLAB_ATOMIC);
+ if (ses == NULL) {
+ HIFN_UNLOCK(sc);
+ return (ENOMEM);
+ }
+ sesn = 0;
+ sc->sc_nsessions = 1;
+ } else {
+ for (sesn = 0; sesn < sc->sc_nsessions; sesn++) {
+ if (!sc->sc_sessions[sesn].hs_used) {
+ ses = &sc->sc_sessions[sesn];
+ break;
+ }
+ }
+
+ if (ses == NULL) {
+ sesn = sc->sc_nsessions;
+ ses = (struct hifn_session *)kmalloc((sesn + 1) * sizeof(*ses),
+ SLAB_ATOMIC);
+ if (ses == NULL) {
+ HIFN_UNLOCK(sc);
+ return (ENOMEM);
+ }
+ bcopy(sc->sc_sessions, ses, sesn * sizeof(*ses));
+ bzero(sc->sc_sessions, sesn * sizeof(*ses));
+ kfree(sc->sc_sessions);
+ sc->sc_sessions = ses;
+ ses = &sc->sc_sessions[sesn];
+ sc->sc_nsessions++;
+ }
+ }
+ HIFN_UNLOCK(sc);
+
+ bzero(ses, sizeof(*ses));
+ ses->hs_used = 1;
+
+ for (c = cri; c != NULL; c = c->cri_next) {
+ switch (c->cri_alg) {
+ case CRYPTO_MD5:
+ case CRYPTO_SHA1:
+ case CRYPTO_MD5_HMAC:
+ case CRYPTO_SHA1_HMAC:
+ if (mac) {
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ return (EINVAL);
+ }
+ mac = 1;
+ ses->hs_mlen = c->cri_mlen;
+ if (ses->hs_mlen == 0) {
+ switch (c->cri_alg) {
+ case CRYPTO_MD5:
+ case CRYPTO_MD5_HMAC:
+ ses->hs_mlen = 16;
+ break;
+ case CRYPTO_SHA1:
+ case CRYPTO_SHA1_HMAC:
+ ses->hs_mlen = 20;
+ break;
+ }
+ }
+ break;
+ case CRYPTO_DES_CBC:
+ case CRYPTO_3DES_CBC:
+ case CRYPTO_AES_CBC:
+ /* XXX this may read fewer, does it matter? */
+ read_random(ses->hs_iv,
+ c->cri_alg == CRYPTO_AES_CBC ?
+ HIFN_AES_IV_LENGTH : HIFN_IV_LENGTH);
+ /*FALLTHROUGH*/
+ case CRYPTO_ARC4:
+ if (cry) {
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ return (EINVAL);
+ }
+ cry = 1;
+ break;
+ default:
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ return (EINVAL);
+ }
+ }
+ if (mac == 0 && cry == 0) {
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ return (EINVAL);
+ }
+
+ *sidp = HIFN_SID(device_get_unit(sc->sc_dev), sesn);
+
+ return (0);
+}
+
+/*
+ * Deallocate a session.
+ * XXX this routine should run a zero'd mac/encrypt key into context ram.
+ * XXX to blow away any keys already stored there.
+ */
+static int
+hifn_freesession(device_t dev, u_int64_t tid)
+{
+ struct hifn_softc *sc = device_get_softc(dev);
+ int session, error;
+ u_int32_t sid = CRYPTO_SESID2LID(tid);
+ unsigned long l_flags;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ KASSERT(sc != NULL, ("hifn_freesession: null softc"));
+ if (sc == NULL) {
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ return (EINVAL);
+ }
+
+ HIFN_LOCK(sc);
+ session = HIFN_SESSION(sid);
+ if (session < sc->sc_nsessions) {
+ bzero(&sc->sc_sessions[session], sizeof(struct hifn_session));
+ error = 0;
+ } else {
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ error = EINVAL;
+ }
+ HIFN_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+hifn_process(device_t dev, struct cryptop *crp, int hint)
+{
+ struct hifn_softc *sc = device_get_softc(dev);
+ struct hifn_command *cmd = NULL;
+ int session, err, ivlen;
+ struct cryptodesc *crd1, *crd2, *maccrd, *enccrd;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ if (crp == NULL || crp->crp_callback == NULL) {
+ hifnstats.hst_invalid++;
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ return (EINVAL);
+ }
+ session = HIFN_SESSION(crp->crp_sid);
+
+ if (sc == NULL || session >= sc->sc_nsessions) {
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ err = EINVAL;
+ goto errout;
+ }
+
+ cmd = kmalloc(sizeof(struct hifn_command), SLAB_ATOMIC);
+ if (cmd == NULL) {
+ hifnstats.hst_nomem++;
+ err = ENOMEM;
+ goto errout;
+ }
+ memset(cmd, 0, sizeof(*cmd));
+
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ cmd->src_skb = (struct sk_buff *)crp->crp_buf;
+ cmd->dst_skb = (struct sk_buff *)crp->crp_buf;
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ cmd->src_io = (struct uio *)crp->crp_buf;
+ cmd->dst_io = (struct uio *)crp->crp_buf;
+ } else {
+ cmd->src_buf = crp->crp_buf;
+ cmd->dst_buf = crp->crp_buf;
+ }
+
+ crd1 = crp->crp_desc;
+ if (crd1 == NULL) {
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ err = EINVAL;
+ goto errout;
+ }
+ crd2 = crd1->crd_next;
+
+ if (crd2 == NULL) {
+ if (crd1->crd_alg == CRYPTO_MD5_HMAC ||
+ crd1->crd_alg == CRYPTO_SHA1_HMAC ||
+ crd1->crd_alg == CRYPTO_SHA1 ||
+ crd1->crd_alg == CRYPTO_MD5) {
+ maccrd = crd1;
+ enccrd = NULL;
+ } else if (crd1->crd_alg == CRYPTO_DES_CBC ||
+ crd1->crd_alg == CRYPTO_3DES_CBC ||
+ crd1->crd_alg == CRYPTO_AES_CBC ||
+ crd1->crd_alg == CRYPTO_ARC4) {
+ if ((crd1->crd_flags & CRD_F_ENCRYPT) == 0)
+ cmd->base_masks |= HIFN_BASE_CMD_DECODE;
+ maccrd = NULL;
+ enccrd = crd1;
+ } else {
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ err = EINVAL;
+ goto errout;
+ }
+ } else {
+ if ((crd1->crd_alg == CRYPTO_MD5_HMAC ||
+ crd1->crd_alg == CRYPTO_SHA1_HMAC ||
+ crd1->crd_alg == CRYPTO_MD5 ||
+ crd1->crd_alg == CRYPTO_SHA1) &&
+ (crd2->crd_alg == CRYPTO_DES_CBC ||
+ crd2->crd_alg == CRYPTO_3DES_CBC ||
+ crd2->crd_alg == CRYPTO_AES_CBC ||
+ crd2->crd_alg == CRYPTO_ARC4) &&
+ ((crd2->crd_flags & CRD_F_ENCRYPT) == 0)) {
+ cmd->base_masks = HIFN_BASE_CMD_DECODE;
+ maccrd = crd1;
+ enccrd = crd2;
+ } else if ((crd1->crd_alg == CRYPTO_DES_CBC ||
+ crd1->crd_alg == CRYPTO_ARC4 ||
+ crd1->crd_alg == CRYPTO_3DES_CBC ||
+ crd1->crd_alg == CRYPTO_AES_CBC) &&
+ (crd2->crd_alg == CRYPTO_MD5_HMAC ||
+ crd2->crd_alg == CRYPTO_SHA1_HMAC ||
+ crd2->crd_alg == CRYPTO_MD5 ||
+ crd2->crd_alg == CRYPTO_SHA1) &&
+ (crd1->crd_flags & CRD_F_ENCRYPT)) {
+ enccrd = crd1;
+ maccrd = crd2;
+ } else {
+ /*
+ * We cannot order the 7751 as requested
+ */
+ DPRINTF("%s,%d: %s %d,%d,%d - EINVAL\n",__FILE__,__LINE__,__FUNCTION__, crd1->crd_alg, crd2->crd_alg, crd1->crd_flags & CRD_F_ENCRYPT);
+ err = EINVAL;
+ goto errout;
+ }
+ }
+
+ if (enccrd) {
+ cmd->enccrd = enccrd;
+ cmd->base_masks |= HIFN_BASE_CMD_CRYPT;
+ switch (enccrd->crd_alg) {
+ case CRYPTO_ARC4:
+ cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_RC4;
+ break;
+ case CRYPTO_DES_CBC:
+ cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_DES |
+ HIFN_CRYPT_CMD_MODE_CBC |
+ HIFN_CRYPT_CMD_NEW_IV;
+ break;
+ case CRYPTO_3DES_CBC:
+ cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_3DES |
+ HIFN_CRYPT_CMD_MODE_CBC |
+ HIFN_CRYPT_CMD_NEW_IV;
+ break;
+ case CRYPTO_AES_CBC:
+ cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_AES |
+ HIFN_CRYPT_CMD_MODE_CBC |
+ HIFN_CRYPT_CMD_NEW_IV;
+ break;
+ default:
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ err = EINVAL;
+ goto errout;
+ }
+ if (enccrd->crd_alg != CRYPTO_ARC4) {
+ ivlen = ((enccrd->crd_alg == CRYPTO_AES_CBC) ?
+ HIFN_AES_IV_LENGTH : HIFN_IV_LENGTH);
+ if (enccrd->crd_flags & CRD_F_ENCRYPT) {
+ if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
+ bcopy(enccrd->crd_iv, cmd->iv, ivlen);
+ else
+ bcopy(sc->sc_sessions[session].hs_iv,
+ cmd->iv, ivlen);
+
+ if ((enccrd->crd_flags & CRD_F_IV_PRESENT)
+ == 0) {
+ crypto_copyback(crp->crp_flags,
+ crp->crp_buf, enccrd->crd_inject,
+ ivlen, cmd->iv);
+ }
+ } else {
+ if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
+ bcopy(enccrd->crd_iv, cmd->iv, ivlen);
+ else {
+ crypto_copydata(crp->crp_flags,
+ crp->crp_buf, enccrd->crd_inject,
+ ivlen, cmd->iv);
+ }
+ }
+ }
+
+ if (enccrd->crd_flags & CRD_F_KEY_EXPLICIT)
+ cmd->cry_masks |= HIFN_CRYPT_CMD_NEW_KEY;
+ cmd->ck = enccrd->crd_key;
+ cmd->cklen = enccrd->crd_klen >> 3;
+ cmd->cry_masks |= HIFN_CRYPT_CMD_NEW_KEY;
+
+ /*
+ * Need to specify the size for the AES key in the masks.
+ */
+ if ((cmd->cry_masks & HIFN_CRYPT_CMD_ALG_MASK) ==
+ HIFN_CRYPT_CMD_ALG_AES) {
+ switch (cmd->cklen) {
+ case 16:
+ cmd->cry_masks |= HIFN_CRYPT_CMD_KSZ_128;
+ break;
+ case 24:
+ cmd->cry_masks |= HIFN_CRYPT_CMD_KSZ_192;
+ break;
+ case 32:
+ cmd->cry_masks |= HIFN_CRYPT_CMD_KSZ_256;
+ break;
+ default:
+ DPRINTF("%s,%d: %s - EINVAL\n",__FILE__,__LINE__,__FUNCTION__);
+ err = EINVAL;
+ goto errout;
+ }
+ }
+ }
+
+ if (maccrd) {
+ cmd->maccrd = maccrd;
+ cmd->base_masks |= HIFN_BASE_CMD_MAC;
+
+ switch (maccrd->crd_alg) {
+ case CRYPTO_MD5:
+ cmd->mac_masks |= HIFN_MAC_CMD_ALG_MD5 |
+ HIFN_MAC_CMD_RESULT | HIFN_MAC_CMD_MODE_HASH |
+ HIFN_MAC_CMD_POS_IPSEC;
+ break;
+ case CRYPTO_MD5_HMAC:
+ cmd->mac_masks |= HIFN_MAC_CMD_ALG_MD5 |
+ HIFN_MAC_CMD_RESULT | HIFN_MAC_CMD_MODE_HMAC |
+ HIFN_MAC_CMD_POS_IPSEC | HIFN_MAC_CMD_TRUNC;
+ break;
+ case CRYPTO_SHA1:
+ cmd->mac_masks |= HIFN_MAC_CMD_ALG_SHA1 |
+ HIFN_MAC_CMD_RESULT | HIFN_MAC_CMD_MODE_HASH |
+ HIFN_MAC_CMD_POS_IPSEC;
+ break;
+ case CRYPTO_SHA1_HMAC:
+ cmd->mac_masks |= HIFN_MAC_CMD_ALG_SHA1 |
+ HIFN_MAC_CMD_RESULT | HIFN_MAC_CMD_MODE_HMAC |
+ HIFN_MAC_CMD_POS_IPSEC | HIFN_MAC_CMD_TRUNC;
+ break;
+ }
+
+ if (maccrd->crd_alg == CRYPTO_SHA1_HMAC ||
+ maccrd->crd_alg == CRYPTO_MD5_HMAC) {
+ cmd->mac_masks |= HIFN_MAC_CMD_NEW_KEY;
+ bcopy(maccrd->crd_key, cmd->mac, maccrd->crd_klen >> 3);
+ bzero(cmd->mac + (maccrd->crd_klen >> 3),
+ HIFN_MAC_KEY_LENGTH - (maccrd->crd_klen >> 3));
+ }
+ }
+
+ cmd->crp = crp;
+ cmd->session_num = session;
+ cmd->softc = sc;
+
+ err = hifn_crypto(sc, cmd, crp, hint);
+ if (!err) {
+ return 0;
+ } else if (err == ERESTART) {
+ /*
+ * There weren't enough resources to dispatch the request
+ * to the part. Notify the caller so they'll requeue this
+ * request and resubmit it again soon.
+ */
+#ifdef HIFN_DEBUG
+ if (hifn_debug)
+ device_printf(sc->sc_dev, "requeue request\n");
+#endif
+ kfree(cmd);
+ sc->sc_needwakeup |= CRYPTO_SYMQ;
+ return (err);
+ }
+
+errout:
+ if (cmd != NULL)
+ kfree(cmd);
+ if (err == EINVAL)
+ hifnstats.hst_invalid++;
+ else
+ hifnstats.hst_nomem++;
+ crp->crp_etype = err;
+ crypto_done(crp);
+ return (err);
+}
+
+static void
+hifn_abort(struct hifn_softc *sc)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+ struct hifn_command *cmd;
+ struct cryptop *crp;
+ int i, u;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ i = dma->resk; u = dma->resu;
+ while (u != 0) {
+ cmd = dma->hifn_commands[i];
+ KASSERT(cmd != NULL, ("hifn_abort: null command slot %u", i));
+ dma->hifn_commands[i] = NULL;
+ crp = cmd->crp;
+
+ if ((dma->resr[i].l & htole32(HIFN_D_VALID)) == 0) {
+ /* Salvage what we can. */
+ u_int8_t *macbuf;
+
+ if (cmd->base_masks & HIFN_BASE_CMD_MAC) {
+ macbuf = dma->result_bufs[i];
+ macbuf += 12;
+ } else
+ macbuf = NULL;
+ hifnstats.hst_opackets++;
+ hifn_callback(sc, cmd, macbuf);
+ } else {
+#if 0
+ if (cmd->src_map == cmd->dst_map) {
+ bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
+ BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+ } else {
+ bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_sync(sc->sc_dmat, cmd->dst_map,
+ BUS_DMASYNC_POSTREAD);
+ }
+#endif
+
+ if (cmd->src_skb != cmd->dst_skb) {
+#ifdef NOTYET
+ m_freem(cmd->src_m);
+ crp->crp_buf = (caddr_t)cmd->dst_m;
+#else
+ device_printf(sc->sc_dev,
+ "%s,%d: CRYPTO_F_SKBUF src != dst not implemented\n",
+ __FILE__, __LINE__);
+#endif
+ }
+
+ /* non-shared buffers cannot be restarted */
+ if (cmd->src_map != cmd->dst_map) {
+ /*
+ * XXX should be EAGAIN, delayed until
+ * after the reset.
+ */
+ crp->crp_etype = ENOMEM;
+ pci_unmap_buf(sc, &cmd->dst);
+ } else
+ crp->crp_etype = ENOMEM;
+
+ pci_unmap_buf(sc, &cmd->src);
+
+ kfree(cmd);
+ if (crp->crp_etype != EAGAIN)
+ crypto_done(crp);
+ }
+
+ if (++i == HIFN_D_RES_RSIZE)
+ i = 0;
+ u--;
+ }
+ dma->resk = i; dma->resu = u;
+
+ hifn_reset_board(sc, 1);
+ hifn_init_dma(sc);
+ hifn_init_pci_registers(sc);
+}
+
+static void
+hifn_callback(struct hifn_softc *sc, struct hifn_command *cmd, u_int8_t *macbuf)
+{
+ struct hifn_dma *dma = sc->sc_dma;
+ struct cryptop *crp = cmd->crp;
+ struct cryptodesc *crd;
+ int i, u, ivlen;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+#if 0
+ if (cmd->src_map == cmd->dst_map) {
+ bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ } else {
+ bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_sync(sc->sc_dmat, cmd->dst_map,
+ BUS_DMASYNC_POSTREAD);
+ }
+#endif
+
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ if (cmd->src_skb != cmd->dst_skb) {
+#ifdef NOTYET
+ crp->crp_buf = (caddr_t)cmd->dst_m;
+ totlen = cmd->src_mapsize;
+ for (m = cmd->dst_m; m != NULL; m = m->m_next) {
+ if (totlen < m->m_len) {
+ m->m_len = totlen;
+ totlen = 0;
+ } else
+ totlen -= m->m_len;
+ }
+ cmd->dst_m->m_pkthdr.len = cmd->src_m->m_pkthdr.len;
+ m_freem(cmd->src_m);
+#else
+ device_printf(sc->sc_dev,
+ "%s,%d: CRYPTO_F_SKBUF src != dst not implemented\n",
+ __FILE__, __LINE__);
+#endif
+ }
+ }
+
+ if (cmd->sloplen != 0) {
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ cmd->src_mapsize - cmd->sloplen, cmd->sloplen,
+ (caddr_t)&dma->slop[cmd->slopidx]);
+ }
+
+ i = dma->dstk; u = dma->dstu;
+ while (u != 0) {
+ if (i == HIFN_D_DST_RSIZE)
+ i = 0;
+#if 0
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+#endif
+ if (dma->dstr[i].l & htole32(HIFN_D_VALID)) {
+#if 0
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+#endif
+ break;
+ }
+ i++, u--;
+ }
+ dma->dstk = i; dma->dstu = u;
+
+ hifnstats.hst_obytes += cmd->dst_mapsize;
+
+ if ((cmd->base_masks & (HIFN_BASE_CMD_CRYPT | HIFN_BASE_CMD_DECODE)) ==
+ HIFN_BASE_CMD_CRYPT) {
+ for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
+ if (crd->crd_alg != CRYPTO_DES_CBC &&
+ crd->crd_alg != CRYPTO_3DES_CBC &&
+ crd->crd_alg != CRYPTO_AES_CBC)
+ continue;
+ ivlen = ((crd->crd_alg == CRYPTO_AES_CBC) ?
+ HIFN_AES_IV_LENGTH : HIFN_IV_LENGTH);
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ crd->crd_skip + crd->crd_len - ivlen, ivlen,
+ cmd->softc->sc_sessions[cmd->session_num].hs_iv);
+ break;
+ }
+ }
+
+ if (macbuf != NULL) {
+ for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
+ int len;
+
+ if (crd->crd_alg != CRYPTO_MD5 &&
+ crd->crd_alg != CRYPTO_SHA1 &&
+ crd->crd_alg != CRYPTO_MD5_HMAC &&
+ crd->crd_alg != CRYPTO_SHA1_HMAC) {
+ continue;
+ }
+ len = cmd->softc->sc_sessions[cmd->session_num].hs_mlen;
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject, len, macbuf);
+ break;
+ }
+ }
+
+ if (cmd->src_map != cmd->dst_map)
+ pci_unmap_buf(sc, &cmd->dst);
+ pci_unmap_buf(sc, &cmd->src);
+ kfree(cmd);
+ crypto_done(crp);
+}
+
+/*
+ * 7811 PB3 rev/2 parts lock-up on burst writes to Group 0
+ * and Group 1 registers; avoid conditions that could create
+ * burst writes by doing a read in between the writes.
+ *
+ * NB: The read we interpose is always to the same register;
+ * we do this because reading from an arbitrary (e.g. last)
+ * register may not always work.
+ */
+static void
+hifn_write_reg_0(struct hifn_softc *sc, bus_size_t reg, u_int32_t val)
+{
+ if (sc->sc_flags & HIFN_IS_7811) {
+ if (sc->sc_bar0_lastreg == reg - 4)
+ readl(sc->sc_bar0 + HIFN_0_PUCNFG);
+ sc->sc_bar0_lastreg = reg;
+ }
+ writel(val, sc->sc_bar0 + reg);
+}
+
+static void
+hifn_write_reg_1(struct hifn_softc *sc, bus_size_t reg, u_int32_t val)
+{
+ if (sc->sc_flags & HIFN_IS_7811) {
+ if (sc->sc_bar1_lastreg == reg - 4)
+ readl(sc->sc_bar1 + HIFN_1_REVID);
+ sc->sc_bar1_lastreg = reg;
+ }
+ writel(val, sc->sc_bar1 + reg);
+}
+
+
+static struct pci_device_id hifn_pci_tbl[] = {
+ { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7951,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7955,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7956,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ { PCI_VENDOR_NETSEC, PCI_PRODUCT_NETSEC_7751,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ { PCI_VENDOR_INVERTEX, PCI_PRODUCT_INVERTEX_AEON,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7811,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ /*
+ * Other vendors share this PCI ID as well, such as
+ * http://www.powercrypt.com, and obviously they also
+ * use the same key.
+ */
+ { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7751,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ { 0, 0, 0, 0, 0, 0, }
+};
+MODULE_DEVICE_TABLE(pci, hifn_pci_tbl);
+
+static struct pci_driver hifn_driver = {
+ .name = "hifn",
+ .id_table = hifn_pci_tbl,
+ .probe = hifn_probe,
+ .remove = hifn_remove,
+ /* add PM stuff here one day */
+};
+
+static int __init hifn_init (void)
+{
+ struct hifn_softc *sc = NULL;
+ int rc;
+
+ DPRINTF("%s(%p)\n", __FUNCTION__, hifn_init);
+
+ rc = pci_register_driver(&hifn_driver);
+ pci_register_driver_compat(&hifn_driver, rc);
+
+ return rc;
+}
+
+static void __exit hifn_exit (void)
+{
+ pci_unregister_driver(&hifn_driver);
+}
+
+module_init(hifn_init);
+module_exit(hifn_exit);
+
+MODULE_LICENSE("BSD");
+MODULE_AUTHOR("David McCullough <david_mccullough@securecomputing.com>");
+MODULE_DESCRIPTION("OCF driver for hifn PCI crypto devices");
diff --git a/crypto/ocf/hifn/hifn7751reg.h b/crypto/ocf/hifn/hifn7751reg.h
new file mode 100644
index 000000000000..ccf54f9c4e24
--- /dev/null
+++ b/crypto/ocf/hifn/hifn7751reg.h
@@ -0,0 +1,540 @@
+/* $FreeBSD: src/sys/dev/hifn/hifn7751reg.h,v 1.7 2007/03/21 03:42:49 sam Exp $ */
+/* $OpenBSD: hifn7751reg.h,v 1.35 2002/04/08 17:49:42 jason Exp $ */
+
+/*-
+ * Invertex AEON / Hifn 7751 driver
+ * Copyright (c) 1999 Invertex Inc. All rights reserved.
+ * Copyright (c) 1999 Theo de Raadt
+ * Copyright (c) 2000-2001 Network Security Technologies, Inc.
+ * http://www.netsec.net
+ *
+ * Please send any comments, feedback, bug-fixes, or feature requests to
+ * software@invertex.com.
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ *
+ */
+#ifndef __HIFN_H__
+#define __HIFN_H__
+
+/*
+ * Some PCI configuration space offset defines. The names were made
+ * identical to the names used by the Linux kernel.
+ */
+#define HIFN_BAR0 PCIR_BAR(0) /* PUC register map */
+#define HIFN_BAR1 PCIR_BAR(1) /* DMA register map */
+#define HIFN_TRDY_TIMEOUT 0x40
+#define HIFN_RETRY_TIMEOUT 0x41
+
+/*
+ * PCI vendor and device identifiers
+ * (the names are preserved from their OpenBSD source).
+ */
+#define PCI_VENDOR_HIFN 0x13a3 /* Hifn */
+#define PCI_PRODUCT_HIFN_7751 0x0005 /* 7751 */
+#define PCI_PRODUCT_HIFN_6500 0x0006 /* 6500 */
+#define PCI_PRODUCT_HIFN_7811 0x0007 /* 7811 */
+#define PCI_PRODUCT_HIFN_7855 0x001f /* 7855 */
+#define PCI_PRODUCT_HIFN_7951 0x0012 /* 7951 */
+#define PCI_PRODUCT_HIFN_7955 0x0020 /* 7954/7955 */
+#define PCI_PRODUCT_HIFN_7956 0x001d /* 7956 */
+
+#define PCI_VENDOR_INVERTEX 0x14e1 /* Invertex */
+#define PCI_PRODUCT_INVERTEX_AEON 0x0005 /* AEON */
+
+#define PCI_VENDOR_NETSEC 0x1660 /* NetSec */
+#define PCI_PRODUCT_NETSEC_7751 0x7751 /* 7751 */
+
+/*
+ * The values below should multiple of 4 -- and be large enough to handle
+ * any command the driver implements.
+ *
+ * MAX_COMMAND = base command + mac command + encrypt command +
+ * mac-key + rc4-key
+ * MAX_RESULT = base result + mac result + mac + encrypt result
+ *
+ *
+ */
+#define HIFN_MAX_COMMAND (8 + 8 + 8 + 64 + 260)
+#define HIFN_MAX_RESULT (8 + 4 + 20 + 4)
+
+/*
+ * hifn_desc_t
+ *
+ * Holds an individual descriptor for any of the rings.
+ */
+typedef struct hifn_desc {
+ volatile u_int32_t l; /* length and status bits */
+ volatile u_int32_t p;
+} hifn_desc_t;
+
+/*
+ * Masks for the "length" field of struct hifn_desc.
+ */
+#define HIFN_D_LENGTH 0x0000ffff /* length bit mask */
+#define HIFN_D_MASKDONEIRQ 0x02000000 /* mask the done interrupt */
+#define HIFN_D_DESTOVER 0x04000000 /* destination overflow */
+#define HIFN_D_OVER 0x08000000 /* overflow */
+#define HIFN_D_LAST 0x20000000 /* last descriptor in chain */
+#define HIFN_D_JUMP 0x40000000 /* jump descriptor */
+#define HIFN_D_VALID 0x80000000 /* valid bit */
+
+
+/*
+ * Processing Unit Registers (offset from BASEREG0)
+ */
+#define HIFN_0_PUDATA 0x00 /* Processing Unit Data */
+#define HIFN_0_PUCTRL 0x04 /* Processing Unit Control */
+#define HIFN_0_PUISR 0x08 /* Processing Unit Interrupt Status */
+#define HIFN_0_PUCNFG 0x0c /* Processing Unit Configuration */
+#define HIFN_0_PUIER 0x10 /* Processing Unit Interrupt Enable */
+#define HIFN_0_PUSTAT 0x14 /* Processing Unit Status/Chip ID */
+#define HIFN_0_FIFOSTAT 0x18 /* FIFO Status */
+#define HIFN_0_FIFOCNFG 0x1c /* FIFO Configuration */
+#define HIFN_0_PUCTRL2 0x28 /* Processing Unit Control (2nd map) */
+#define HIFN_0_MUTE1 0x80
+#define HIFN_0_MUTE2 0x90
+#define HIFN_0_SPACESIZE 0x100 /* Register space size */
+
+/* Processing Unit Control Register (HIFN_0_PUCTRL) */
+#define HIFN_PUCTRL_CLRSRCFIFO 0x0010 /* clear source fifo */
+#define HIFN_PUCTRL_STOP 0x0008 /* stop pu */
+#define HIFN_PUCTRL_LOCKRAM 0x0004 /* lock ram */
+#define HIFN_PUCTRL_DMAENA 0x0002 /* enable dma */
+#define HIFN_PUCTRL_RESET 0x0001 /* Reset processing unit */
+
+/* Processing Unit Interrupt Status Register (HIFN_0_PUISR) */
+#define HIFN_PUISR_CMDINVAL 0x8000 /* Invalid command interrupt */
+#define HIFN_PUISR_DATAERR 0x4000 /* Data error interrupt */
+#define HIFN_PUISR_SRCFIFO 0x2000 /* Source FIFO ready interrupt */
+#define HIFN_PUISR_DSTFIFO 0x1000 /* Destination FIFO ready interrupt */
+#define HIFN_PUISR_DSTOVER 0x0200 /* Destination overrun interrupt */
+#define HIFN_PUISR_SRCCMD 0x0080 /* Source command interrupt */
+#define HIFN_PUISR_SRCCTX 0x0040 /* Source context interrupt */
+#define HIFN_PUISR_SRCDATA 0x0020 /* Source data interrupt */
+#define HIFN_PUISR_DSTDATA 0x0010 /* Destination data interrupt */
+#define HIFN_PUISR_DSTRESULT 0x0004 /* Destination result interrupt */
+
+/* Processing Unit Configuration Register (HIFN_0_PUCNFG) */
+#define HIFN_PUCNFG_DRAMMASK 0xe000 /* DRAM size mask */
+#define HIFN_PUCNFG_DSZ_256K 0x0000 /* 256k dram */
+#define HIFN_PUCNFG_DSZ_512K 0x2000 /* 512k dram */
+#define HIFN_PUCNFG_DSZ_1M 0x4000 /* 1m dram */
+#define HIFN_PUCNFG_DSZ_2M 0x6000 /* 2m dram */
+#define HIFN_PUCNFG_DSZ_4M 0x8000 /* 4m dram */
+#define HIFN_PUCNFG_DSZ_8M 0xa000 /* 8m dram */
+#define HIFN_PUNCFG_DSZ_16M 0xc000 /* 16m dram */
+#define HIFN_PUCNFG_DSZ_32M 0xe000 /* 32m dram */
+#define HIFN_PUCNFG_DRAMREFRESH 0x1800 /* DRAM refresh rate mask */
+#define HIFN_PUCNFG_DRFR_512 0x0000 /* 512 divisor of ECLK */
+#define HIFN_PUCNFG_DRFR_256 0x0800 /* 256 divisor of ECLK */
+#define HIFN_PUCNFG_DRFR_128 0x1000 /* 128 divisor of ECLK */
+#define HIFN_PUCNFG_TCALLPHASES 0x0200 /* your guess is as good as mine... */
+#define HIFN_PUCNFG_TCDRVTOTEM 0x0100 /* your guess is as good as mine... */
+#define HIFN_PUCNFG_BIGENDIAN 0x0080 /* DMA big endian mode */
+#define HIFN_PUCNFG_BUS32 0x0040 /* Bus width 32bits */
+#define HIFN_PUCNFG_BUS16 0x0000 /* Bus width 16 bits */
+#define HIFN_PUCNFG_CHIPID 0x0020 /* Allow chipid from PUSTAT */
+#define HIFN_PUCNFG_DRAM 0x0010 /* Context RAM is DRAM */
+#define HIFN_PUCNFG_SRAM 0x0000 /* Context RAM is SRAM */
+#define HIFN_PUCNFG_COMPSING 0x0004 /* Enable single compression context */
+#define HIFN_PUCNFG_ENCCNFG 0x0002 /* Encryption configuration */
+
+/* Processing Unit Interrupt Enable Register (HIFN_0_PUIER) */
+#define HIFN_PUIER_CMDINVAL 0x8000 /* Invalid command interrupt */
+#define HIFN_PUIER_DATAERR 0x4000 /* Data error interrupt */
+#define HIFN_PUIER_SRCFIFO 0x2000 /* Source FIFO ready interrupt */
+#define HIFN_PUIER_DSTFIFO 0x1000 /* Destination FIFO ready interrupt */
+#define HIFN_PUIER_DSTOVER 0x0200 /* Destination overrun interrupt */
+#define HIFN_PUIER_SRCCMD 0x0080 /* Source command interrupt */
+#define HIFN_PUIER_SRCCTX 0x0040 /* Source context interrupt */
+#define HIFN_PUIER_SRCDATA 0x0020 /* Source data interrupt */
+#define HIFN_PUIER_DSTDATA 0x0010 /* Destination data interrupt */
+#define HIFN_PUIER_DSTRESULT 0x0004 /* Destination result interrupt */
+
+/* Processing Unit Status Register/Chip ID (HIFN_0_PUSTAT) */
+#define HIFN_PUSTAT_CMDINVAL 0x8000 /* Invalid command interrupt */
+#define HIFN_PUSTAT_DATAERR 0x4000 /* Data error interrupt */
+#define HIFN_PUSTAT_SRCFIFO 0x2000 /* Source FIFO ready interrupt */
+#define HIFN_PUSTAT_DSTFIFO 0x1000 /* Destination FIFO ready interrupt */
+#define HIFN_PUSTAT_DSTOVER 0x0200 /* Destination overrun interrupt */
+#define HIFN_PUSTAT_SRCCMD 0x0080 /* Source command interrupt */
+#define HIFN_PUSTAT_SRCCTX 0x0040 /* Source context interrupt */
+#define HIFN_PUSTAT_SRCDATA 0x0020 /* Source data interrupt */
+#define HIFN_PUSTAT_DSTDATA 0x0010 /* Destination data interrupt */
+#define HIFN_PUSTAT_DSTRESULT 0x0004 /* Destination result interrupt */
+#define HIFN_PUSTAT_CHIPREV 0x00ff /* Chip revision mask */
+#define HIFN_PUSTAT_CHIPENA 0xff00 /* Chip enabled mask */
+#define HIFN_PUSTAT_ENA_2 0x1100 /* Level 2 enabled */
+#define HIFN_PUSTAT_ENA_1 0x1000 /* Level 1 enabled */
+#define HIFN_PUSTAT_ENA_0 0x3000 /* Level 0 enabled */
+#define HIFN_PUSTAT_REV_2 0x0020 /* 7751 PT6/2 */
+#define HIFN_PUSTAT_REV_3 0x0030 /* 7751 PT6/3 */
+
+/* FIFO Status Register (HIFN_0_FIFOSTAT) */
+#define HIFN_FIFOSTAT_SRC 0x7f00 /* Source FIFO available */
+#define HIFN_FIFOSTAT_DST 0x007f /* Destination FIFO available */
+
+/* FIFO Configuration Register (HIFN_0_FIFOCNFG) */
+#define HIFN_FIFOCNFG_THRESHOLD 0x0400 /* must be written as this value */
+
+/*
+ * DMA Interface Registers (offset from BASEREG1)
+ */
+#define HIFN_1_DMA_CRAR 0x0c /* DMA Command Ring Address */
+#define HIFN_1_DMA_SRAR 0x1c /* DMA Source Ring Address */
+#define HIFN_1_DMA_RRAR 0x2c /* DMA Result Ring Address */
+#define HIFN_1_DMA_DRAR 0x3c /* DMA Destination Ring Address */
+#define HIFN_1_DMA_CSR 0x40 /* DMA Status and Control */
+#define HIFN_1_DMA_IER 0x44 /* DMA Interrupt Enable */
+#define HIFN_1_DMA_CNFG 0x48 /* DMA Configuration */
+#define HIFN_1_PLL 0x4c /* 7955/7956: PLL config */
+#define HIFN_1_7811_RNGENA 0x60 /* 7811: rng enable */
+#define HIFN_1_7811_RNGCFG 0x64 /* 7811: rng config */
+#define HIFN_1_7811_RNGDAT 0x68 /* 7811: rng data */
+#define HIFN_1_7811_RNGSTS 0x6c /* 7811: rng status */
+#define HIFN_1_DMA_CNFG2 0x6c /* 7955/7956: dma config #2 */
+#define HIFN_1_7811_MIPSRST 0x94 /* 7811: MIPS reset */
+#define HIFN_1_REVID 0x98 /* Revision ID */
+
+#define HIFN_1_PUB_RESET 0x204 /* Public/RNG Reset */
+#define HIFN_1_PUB_BASE 0x300 /* Public Base Address */
+#define HIFN_1_PUB_OPLEN 0x304 /* 7951-compat Public Operand Length */
+#define HIFN_1_PUB_OP 0x308 /* 7951-compat Public Operand */
+#define HIFN_1_PUB_STATUS 0x30c /* 7951-compat Public Status */
+#define HIFN_1_PUB_IEN 0x310 /* Public Interrupt enable */
+#define HIFN_1_RNG_CONFIG 0x314 /* RNG config */
+#define HIFN_1_RNG_DATA 0x318 /* RNG data */
+#define HIFN_1_PUB_MODE 0x320 /* PK mode */
+#define HIFN_1_PUB_FIFO_OPLEN 0x380 /* first element of oplen fifo */
+#define HIFN_1_PUB_FIFO_OP 0x384 /* first element of op fifo */
+#define HIFN_1_PUB_MEM 0x400 /* start of Public key memory */
+#define HIFN_1_PUB_MEMEND 0xbff /* end of Public key memory */
+
+/* DMA Status and Control Register (HIFN_1_DMA_CSR) */
+#define HIFN_DMACSR_D_CTRLMASK 0xc0000000 /* Destinition Ring Control */
+#define HIFN_DMACSR_D_CTRL_NOP 0x00000000 /* Dest. Control: no-op */
+#define HIFN_DMACSR_D_CTRL_DIS 0x40000000 /* Dest. Control: disable */
+#define HIFN_DMACSR_D_CTRL_ENA 0x80000000 /* Dest. Control: enable */
+#define HIFN_DMACSR_D_ABORT 0x20000000 /* Destinition Ring PCIAbort */
+#define HIFN_DMACSR_D_DONE 0x10000000 /* Destinition Ring Done */
+#define HIFN_DMACSR_D_LAST 0x08000000 /* Destinition Ring Last */
+#define HIFN_DMACSR_D_WAIT 0x04000000 /* Destinition Ring Waiting */
+#define HIFN_DMACSR_D_OVER 0x02000000 /* Destinition Ring Overflow */
+#define HIFN_DMACSR_R_CTRL 0x00c00000 /* Result Ring Control */
+#define HIFN_DMACSR_R_CTRL_NOP 0x00000000 /* Result Control: no-op */
+#define HIFN_DMACSR_R_CTRL_DIS 0x00400000 /* Result Control: disable */
+#define HIFN_DMACSR_R_CTRL_ENA 0x00800000 /* Result Control: enable */
+#define HIFN_DMACSR_R_ABORT 0x00200000 /* Result Ring PCI Abort */
+#define HIFN_DMACSR_R_DONE 0x00100000 /* Result Ring Done */
+#define HIFN_DMACSR_R_LAST 0x00080000 /* Result Ring Last */
+#define HIFN_DMACSR_R_WAIT 0x00040000 /* Result Ring Waiting */
+#define HIFN_DMACSR_R_OVER 0x00020000 /* Result Ring Overflow */
+#define HIFN_DMACSR_S_CTRL 0x0000c000 /* Source Ring Control */
+#define HIFN_DMACSR_S_CTRL_NOP 0x00000000 /* Source Control: no-op */
+#define HIFN_DMACSR_S_CTRL_DIS 0x00004000 /* Source Control: disable */
+#define HIFN_DMACSR_S_CTRL_ENA 0x00008000 /* Source Control: enable */
+#define HIFN_DMACSR_S_ABORT 0x00002000 /* Source Ring PCI Abort */
+#define HIFN_DMACSR_S_DONE 0x00001000 /* Source Ring Done */
+#define HIFN_DMACSR_S_LAST 0x00000800 /* Source Ring Last */
+#define HIFN_DMACSR_S_WAIT 0x00000400 /* Source Ring Waiting */
+#define HIFN_DMACSR_ILLW 0x00000200 /* Illegal write (7811 only) */
+#define HIFN_DMACSR_ILLR 0x00000100 /* Illegal read (7811 only) */
+#define HIFN_DMACSR_C_CTRL 0x000000c0 /* Command Ring Control */
+#define HIFN_DMACSR_C_CTRL_NOP 0x00000000 /* Command Control: no-op */
+#define HIFN_DMACSR_C_CTRL_DIS 0x00000040 /* Command Control: disable */
+#define HIFN_DMACSR_C_CTRL_ENA 0x00000080 /* Command Control: enable */
+#define HIFN_DMACSR_C_ABORT 0x00000020 /* Command Ring PCI Abort */
+#define HIFN_DMACSR_C_DONE 0x00000010 /* Command Ring Done */
+#define HIFN_DMACSR_C_LAST 0x00000008 /* Command Ring Last */
+#define HIFN_DMACSR_C_WAIT 0x00000004 /* Command Ring Waiting */
+#define HIFN_DMACSR_PUBDONE 0x00000002 /* Public op done (7951 only) */
+#define HIFN_DMACSR_ENGINE 0x00000001 /* Command Ring Engine IRQ */
+
+/* DMA Interrupt Enable Register (HIFN_1_DMA_IER) */
+#define HIFN_DMAIER_D_ABORT 0x20000000 /* Destination Ring PCIAbort */
+#define HIFN_DMAIER_D_DONE 0x10000000 /* Destination Ring Done */
+#define HIFN_DMAIER_D_LAST 0x08000000 /* Destination Ring Last */
+#define HIFN_DMAIER_D_WAIT 0x04000000 /* Destination Ring Waiting */
+#define HIFN_DMAIER_D_OVER 0x02000000 /* Destination Ring Overflow */
+#define HIFN_DMAIER_R_ABORT 0x00200000 /* Result Ring PCI Abort */
+#define HIFN_DMAIER_R_DONE 0x00100000 /* Result Ring Done */
+#define HIFN_DMAIER_R_LAST 0x00080000 /* Result Ring Last */
+#define HIFN_DMAIER_R_WAIT 0x00040000 /* Result Ring Waiting */
+#define HIFN_DMAIER_R_OVER 0x00020000 /* Result Ring Overflow */
+#define HIFN_DMAIER_S_ABORT 0x00002000 /* Source Ring PCI Abort */
+#define HIFN_DMAIER_S_DONE 0x00001000 /* Source Ring Done */
+#define HIFN_DMAIER_S_LAST 0x00000800 /* Source Ring Last */
+#define HIFN_DMAIER_S_WAIT 0x00000400 /* Source Ring Waiting */
+#define HIFN_DMAIER_ILLW 0x00000200 /* Illegal write (7811 only) */
+#define HIFN_DMAIER_ILLR 0x00000100 /* Illegal read (7811 only) */
+#define HIFN_DMAIER_C_ABORT 0x00000020 /* Command Ring PCI Abort */
+#define HIFN_DMAIER_C_DONE 0x00000010 /* Command Ring Done */
+#define HIFN_DMAIER_C_LAST 0x00000008 /* Command Ring Last */
+#define HIFN_DMAIER_C_WAIT 0x00000004 /* Command Ring Waiting */
+#define HIFN_DMAIER_PUBDONE 0x00000002 /* public op done (7951 only) */
+#define HIFN_DMAIER_ENGINE 0x00000001 /* Engine IRQ */
+
+/* DMA Configuration Register (HIFN_1_DMA_CNFG) */
+#define HIFN_DMACNFG_BIGENDIAN 0x10000000 /* big endian mode */
+#define HIFN_DMACNFG_POLLFREQ 0x00ff0000 /* Poll frequency mask */
+#define HIFN_DMACNFG_UNLOCK 0x00000800
+#define HIFN_DMACNFG_POLLINVAL 0x00000700 /* Invalid Poll Scalar */
+#define HIFN_DMACNFG_LAST 0x00000010 /* Host control LAST bit */
+#define HIFN_DMACNFG_MODE 0x00000004 /* DMA mode */
+#define HIFN_DMACNFG_DMARESET 0x00000002 /* DMA Reset # */
+#define HIFN_DMACNFG_MSTRESET 0x00000001 /* Master Reset # */
+
+/* DMA Configuration Register (HIFN_1_DMA_CNFG2) */
+#define HIFN_DMACNFG2_PKSWAP32 (1 << 19) /* swap the OPLEN/OP reg */
+#define HIFN_DMACNFG2_PKSWAP8 (1 << 18) /* swap the bits of OPLEN/OP */
+#define HIFN_DMACNFG2_BAR0_SWAP32 (1<<17) /* swap the bytes of BAR0 */
+#define HIFN_DMACNFG2_BAR1_SWAP8 (1<<16) /* swap the bits of BAR0 */
+#define HIFN_DMACNFG2_INIT_WRITE_BURST_SHIFT 12
+#define HIFN_DMACNFG2_INIT_READ_BURST_SHIFT 8
+#define HIFN_DMACNFG2_TGT_WRITE_BURST_SHIFT 4
+#define HIFN_DMACNFG2_TGT_READ_BURST_SHIFT 0
+
+/* 7811 RNG Enable Register (HIFN_1_7811_RNGENA) */
+#define HIFN_7811_RNGENA_ENA 0x00000001 /* enable RNG */
+
+/* 7811 RNG Config Register (HIFN_1_7811_RNGCFG) */
+#define HIFN_7811_RNGCFG_PRE1 0x00000f00 /* first prescalar */
+#define HIFN_7811_RNGCFG_OPRE 0x00000080 /* output prescalar */
+#define HIFN_7811_RNGCFG_DEFL 0x00000f80 /* 2 words/ 1/100 sec */
+
+/* 7811 RNG Status Register (HIFN_1_7811_RNGSTS) */
+#define HIFN_7811_RNGSTS_RDY 0x00004000 /* two numbers in FIFO */
+#define HIFN_7811_RNGSTS_UFL 0x00001000 /* rng underflow */
+
+/* 7811 MIPS Reset Register (HIFN_1_7811_MIPSRST) */
+#define HIFN_MIPSRST_BAR2SIZE 0xffff0000 /* sdram size */
+#define HIFN_MIPSRST_GPRAMINIT 0x00008000 /* gpram can be accessed */
+#define HIFN_MIPSRST_CRAMINIT 0x00004000 /* ctxram can be accessed */
+#define HIFN_MIPSRST_LED2 0x00000400 /* external LED2 */
+#define HIFN_MIPSRST_LED1 0x00000200 /* external LED1 */
+#define HIFN_MIPSRST_LED0 0x00000100 /* external LED0 */
+#define HIFN_MIPSRST_MIPSDIS 0x00000004 /* disable MIPS */
+#define HIFN_MIPSRST_MIPSRST 0x00000002 /* warm reset MIPS */
+#define HIFN_MIPSRST_MIPSCOLD 0x00000001 /* cold reset MIPS */
+
+/* Public key reset register (HIFN_1_PUB_RESET) */
+#define HIFN_PUBRST_RESET 0x00000001 /* reset public/rng unit */
+
+/* Public operation register (HIFN_1_PUB_OP) */
+#define HIFN_PUBOP_AOFFSET 0x0000003e /* A offset */
+#define HIFN_PUBOP_BOFFSET 0x00000fc0 /* B offset */
+#define HIFN_PUBOP_MOFFSET 0x0003f000 /* M offset */
+#define HIFN_PUBOP_OP_MASK 0x003c0000 /* Opcode: */
+#define HIFN_PUBOP_OP_NOP 0x00000000 /* NOP */
+#define HIFN_PUBOP_OP_ADD 0x00040000 /* ADD */
+#define HIFN_PUBOP_OP_ADDC 0x00080000 /* ADD w/carry */
+#define HIFN_PUBOP_OP_SUB 0x000c0000 /* SUB */
+#define HIFN_PUBOP_OP_SUBC 0x00100000 /* SUB w/carry */
+#define HIFN_PUBOP_OP_MODADD 0x00140000 /* Modular ADD */
+#define HIFN_PUBOP_OP_MODSUB 0x00180000 /* Modular SUB */
+#define HIFN_PUBOP_OP_INCA 0x001c0000 /* INC A */
+#define HIFN_PUBOP_OP_DECA 0x00200000 /* DEC A */
+#define HIFN_PUBOP_OP_MULT 0x00240000 /* MULT */
+#define HIFN_PUBOP_OP_MODMULT 0x00280000 /* Modular MULT */
+#define HIFN_PUBOP_OP_MODRED 0x002c0000 /* Modular Red */
+#define HIFN_PUBOP_OP_MODEXP 0x00300000 /* Modular Exp */
+
+/* Public operand length register (HIFN_1_PUB_OPLEN) */
+#define HIFN_PUBOPLEN_MODLEN 0x0000007f
+#define HIFN_PUBOPLEN_EXPLEN 0x0003ff80
+#define HIFN_PUBOPLEN_REDLEN 0x003c0000
+
+/* Public status register (HIFN_1_PUB_STATUS) */
+#define HIFN_PUBSTS_DONE 0x00000001 /* operation done */
+#define HIFN_PUBSTS_CARRY 0x00000002 /* carry */
+#define HIFN_PUBSTS_FIFO_EMPTY 0x00000100 /* fifo empty */
+#define HIFN_PUBSTS_FIFO_FULL 0x00000200 /* fifo full */
+#define HIFN_PUBSTS_FIFO_OVFL 0x00000400 /* fifo overflow */
+#define HIFN_PUBSTS_FIFO_WRITE 0x000f0000 /* fifo write */
+#define HIFN_PUBSTS_FIFO_READ 0x0f000000 /* fifo read */
+
+/* Public interrupt enable register (HIFN_1_PUB_IEN) */
+#define HIFN_PUBIEN_DONE 0x00000001 /* operation done interrupt */
+
+/* Random number generator config register (HIFN_1_RNG_CONFIG) */
+#define HIFN_RNGCFG_ENA 0x00000001 /* enable rng */
+
+/*
+ * Register offsets in register set 1
+ */
+
+#define HIFN_UNLOCK_SECRET1 0xf4
+#define HIFN_UNLOCK_SECRET2 0xfc
+
+/*
+ * PLL config register
+ *
+ * This register is present only on 7954/7955/7956 parts. It must be
+ * programmed according to the bus interface method used by the h/w.
+ * Note that the parts require a stable clock. Since the PCI clock
+ * may vary the reference clock must usually be used. To avoid
+ * overclocking the core logic, setup must be done carefully, refer
+ * to the driver for details. The exact multiplier required varies
+ * by part and system configuration; refer to the Hifn documentation.
+ */
+#define HIFN_PLL_REF_SEL 0x00000001 /* REF/HBI clk selection */
+#define HIFN_PLL_BP 0x00000002 /* bypass (used during setup) */
+/* bit 2 reserved */
+#define HIFN_PLL_PK_CLK_SEL 0x00000008 /* public key clk select */
+#define HIFN_PLL_PE_CLK_SEL 0x00000010 /* packet engine clk select */
+/* bits 5-9 reserved */
+#define HIFN_PLL_MBSET 0x00000400 /* must be set to 1 */
+#define HIFN_PLL_ND 0x00003800 /* Fpll_ref multiplier select */
+#define HIFN_PLL_ND_SHIFT 11
+#define HIFN_PLL_ND_2 0x00000000 /* 2x */
+#define HIFN_PLL_ND_4 0x00000800 /* 4x */
+#define HIFN_PLL_ND_6 0x00001000 /* 6x */
+#define HIFN_PLL_ND_8 0x00001800 /* 8x */
+#define HIFN_PLL_ND_10 0x00002000 /* 10x */
+#define HIFN_PLL_ND_12 0x00002800 /* 12x */
+/* bits 14-15 reserved */
+#define HIFN_PLL_IS 0x00010000 /* charge pump current select */
+/* bits 17-31 reserved */
+
+/*
+ * Board configuration specifies only these bits.
+ */
+#define HIFN_PLL_CONFIG (HIFN_PLL_IS|HIFN_PLL_ND|HIFN_PLL_REF_SEL)
+
+/*
+ * Public Key Engine Mode Register
+ */
+#define HIFN_PKMODE_HOSTINVERT (1 << 0) /* HOST INVERT */
+#define HIFN_PKMODE_ENHANCED (1 << 1) /* Enable enhanced mode */
+
+
+/*********************************************************************
+ * Structs for board commands
+ *
+ *********************************************************************/
+
+/*
+ * Structure to help build up the command data structure.
+ */
+typedef struct hifn_base_command {
+ volatile u_int16_t masks;
+ volatile u_int16_t session_num;
+ volatile u_int16_t total_source_count;
+ volatile u_int16_t total_dest_count;
+} hifn_base_command_t;
+
+#define HIFN_BASE_CMD_MAC 0x0400
+#define HIFN_BASE_CMD_CRYPT 0x0800
+#define HIFN_BASE_CMD_DECODE 0x2000
+#define HIFN_BASE_CMD_SRCLEN_M 0xc000
+#define HIFN_BASE_CMD_SRCLEN_S 14
+#define HIFN_BASE_CMD_DSTLEN_M 0x3000
+#define HIFN_BASE_CMD_DSTLEN_S 12
+#define HIFN_BASE_CMD_LENMASK_HI 0x30000
+#define HIFN_BASE_CMD_LENMASK_LO 0x0ffff
+
+/*
+ * Structure to help build up the command data structure.
+ */
+typedef struct hifn_crypt_command {
+ volatile u_int16_t masks;
+ volatile u_int16_t header_skip;
+ volatile u_int16_t source_count;
+ volatile u_int16_t reserved;
+} hifn_crypt_command_t;
+
+#define HIFN_CRYPT_CMD_ALG_MASK 0x0003 /* algorithm: */
+#define HIFN_CRYPT_CMD_ALG_DES 0x0000 /* DES */
+#define HIFN_CRYPT_CMD_ALG_3DES 0x0001 /* 3DES */
+#define HIFN_CRYPT_CMD_ALG_RC4 0x0002 /* RC4 */
+#define HIFN_CRYPT_CMD_ALG_AES 0x0003 /* AES */
+#define HIFN_CRYPT_CMD_MODE_MASK 0x0018 /* Encrypt mode: */
+#define HIFN_CRYPT_CMD_MODE_ECB 0x0000 /* ECB */
+#define HIFN_CRYPT_CMD_MODE_CBC 0x0008 /* CBC */
+#define HIFN_CRYPT_CMD_MODE_CFB 0x0010 /* CFB */
+#define HIFN_CRYPT_CMD_MODE_OFB 0x0018 /* OFB */
+#define HIFN_CRYPT_CMD_CLR_CTX 0x0040 /* clear context */
+#define HIFN_CRYPT_CMD_NEW_KEY 0x0800 /* expect new key */
+#define HIFN_CRYPT_CMD_NEW_IV 0x1000 /* expect new iv */
+
+#define HIFN_CRYPT_CMD_SRCLEN_M 0xc000
+#define HIFN_CRYPT_CMD_SRCLEN_S 14
+
+#define HIFN_CRYPT_CMD_KSZ_MASK 0x0600 /* AES key size: */
+#define HIFN_CRYPT_CMD_KSZ_128 0x0000 /* 128 bit */
+#define HIFN_CRYPT_CMD_KSZ_192 0x0200 /* 192 bit */
+#define HIFN_CRYPT_CMD_KSZ_256 0x0400 /* 256 bit */
+
+/*
+ * Structure to help build up the command data structure.
+ */
+typedef struct hifn_mac_command {
+ volatile u_int16_t masks;
+ volatile u_int16_t header_skip;
+ volatile u_int16_t source_count;
+ volatile u_int16_t reserved;
+} hifn_mac_command_t;
+
+#define HIFN_MAC_CMD_ALG_MASK 0x0001
+#define HIFN_MAC_CMD_ALG_SHA1 0x0000
+#define HIFN_MAC_CMD_ALG_MD5 0x0001
+#define HIFN_MAC_CMD_MODE_MASK 0x000c
+#define HIFN_MAC_CMD_MODE_HMAC 0x0000
+#define HIFN_MAC_CMD_MODE_SSL_MAC 0x0004
+#define HIFN_MAC_CMD_MODE_HASH 0x0008
+#define HIFN_MAC_CMD_MODE_FULL 0x0004
+#define HIFN_MAC_CMD_TRUNC 0x0010
+#define HIFN_MAC_CMD_RESULT 0x0020
+#define HIFN_MAC_CMD_APPEND 0x0040
+#define HIFN_MAC_CMD_SRCLEN_M 0xc000
+#define HIFN_MAC_CMD_SRCLEN_S 14
+
+/*
+ * MAC POS IPsec initiates authentication after encryption on encodes
+ * and before decryption on decodes.
+ */
+#define HIFN_MAC_CMD_POS_IPSEC 0x0200
+#define HIFN_MAC_CMD_NEW_KEY 0x0800
+
+/*
+ * The poll frequency and poll scalar defines are unshifted values used
+ * to set fields in the DMA Configuration Register.
+ */
+#ifndef HIFN_POLL_FREQUENCY
+#define HIFN_POLL_FREQUENCY 0x1
+#endif
+
+#ifndef HIFN_POLL_SCALAR
+#define HIFN_POLL_SCALAR 0x0
+#endif
+
+#define HIFN_MAX_SEGLEN 0xffff /* maximum dma segment len */
+#define HIFN_MAX_DMALEN 0x3ffff /* maximum dma length */
+#endif /* __HIFN_H__ */
diff --git a/crypto/ocf/hifn/hifn7751var.h b/crypto/ocf/hifn/hifn7751var.h
new file mode 100644
index 000000000000..9a5f2319dbc3
--- /dev/null
+++ b/crypto/ocf/hifn/hifn7751var.h
@@ -0,0 +1,369 @@
+/* $FreeBSD: src/sys/dev/hifn/hifn7751var.h,v 1.9 2007/03/21 03:42:49 sam Exp $ */
+/* $OpenBSD: hifn7751var.h,v 1.42 2002/04/08 17:49:42 jason Exp $ */
+
+/*-
+ * Invertex AEON / Hifn 7751 driver
+ * Copyright (c) 1999 Invertex Inc. All rights reserved.
+ * Copyright (c) 1999 Theo de Raadt
+ * Copyright (c) 2000-2001 Network Security Technologies, Inc.
+ * http://www.netsec.net
+ *
+ * Please send any comments, feedback, bug-fixes, or feature requests to
+ * software@invertex.com.
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F30602-01-2-0537.
+ *
+ */
+
+#ifndef __HIFN7751VAR_H__
+#define __HIFN7751VAR_H__
+
+#ifdef __KERNEL__
+
+/*
+ * Some configurable values for the driver. By default command+result
+ * descriptor rings are the same size. The src+dst descriptor rings
+ * are sized at 3.5x the number of potential commands. Slower parts
+ * (e.g. 7951) tend to run out of src descriptors; faster parts (7811)
+ * src+cmd/result descriptors. It's not clear that increasing the size
+ * of the descriptor rings helps performance significantly as other
+ * factors tend to come into play (e.g. copying misaligned packets).
+ */
+#define HIFN_D_CMD_RSIZE 24 /* command descriptors */
+#define HIFN_D_SRC_RSIZE ((HIFN_D_CMD_RSIZE * 7) / 2) /* source descriptors */
+#define HIFN_D_RES_RSIZE HIFN_D_CMD_RSIZE /* result descriptors */
+#define HIFN_D_DST_RSIZE HIFN_D_SRC_RSIZE /* destination descriptors */
+
+/*
+ * Length values for cryptography
+ */
+#define HIFN_DES_KEY_LENGTH 8
+#define HIFN_3DES_KEY_LENGTH 24
+#define HIFN_MAX_CRYPT_KEY_LENGTH HIFN_3DES_KEY_LENGTH
+#define HIFN_IV_LENGTH 8
+#define HIFN_AES_IV_LENGTH 16
+#define HIFN_MAX_IV_LENGTH HIFN_AES_IV_LENGTH
+
+/*
+ * Length values for authentication
+ */
+#define HIFN_MAC_KEY_LENGTH 64
+#define HIFN_MD5_LENGTH 16
+#define HIFN_SHA1_LENGTH 20
+#define HIFN_MAC_TRUNC_LENGTH 12
+
+#define MAX_SCATTER 64
+
+/*
+ * Data structure to hold all 4 rings and any other ring related data.
+ */
+struct hifn_dma {
+ /*
+ * Descriptor rings. We add +1 to the size to accomidate the
+ * jump descriptor.
+ */
+ struct hifn_desc cmdr[HIFN_D_CMD_RSIZE+1];
+ struct hifn_desc srcr[HIFN_D_SRC_RSIZE+1];
+ struct hifn_desc dstr[HIFN_D_DST_RSIZE+1];
+ struct hifn_desc resr[HIFN_D_RES_RSIZE+1];
+
+ struct hifn_command *hifn_commands[HIFN_D_RES_RSIZE];
+
+ u_char command_bufs[HIFN_D_CMD_RSIZE][HIFN_MAX_COMMAND];
+ u_char result_bufs[HIFN_D_CMD_RSIZE][HIFN_MAX_RESULT];
+ u_int32_t slop[HIFN_D_CMD_RSIZE];
+
+ u_int64_t test_src, test_dst;
+
+ /*
+ * Our current positions for insertion and removal from the desriptor
+ * rings.
+ */
+ int cmdi, srci, dsti, resi;
+ volatile int cmdu, srcu, dstu, resu;
+ int cmdk, srck, dstk, resk;
+};
+
+struct hifn_session {
+ int hs_used;
+ int hs_mlen;
+ u_int8_t hs_iv[HIFN_MAX_IV_LENGTH];
+};
+
+#define HIFN_RING_SYNC(sc, r, i, f) \
+ /* DAVIDM bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_dmamap, (f)) */
+
+#define HIFN_CMDR_SYNC(sc, i, f) HIFN_RING_SYNC((sc), cmdr, (i), (f))
+#define HIFN_RESR_SYNC(sc, i, f) HIFN_RING_SYNC((sc), resr, (i), (f))
+#define HIFN_SRCR_SYNC(sc, i, f) HIFN_RING_SYNC((sc), srcr, (i), (f))
+#define HIFN_DSTR_SYNC(sc, i, f) HIFN_RING_SYNC((sc), dstr, (i), (f))
+
+#define HIFN_CMD_SYNC(sc, i, f) \
+ /* DAVIDM bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_dmamap, (f)) */
+
+#define HIFN_RES_SYNC(sc, i, f) \
+ /* DAVIDM bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_dmamap, (f)) */
+
+typedef int bus_size_t;
+
+/*
+ * Holds data specific to a single HIFN board.
+ */
+struct hifn_softc {
+ softc_device_decl sc_dev;
+
+ struct pci_dev *sc_pcidev; /* PCI device pointer */
+ spinlock_t sc_mtx; /* per-instance lock */
+
+ int sc_num; /* for multiple devs */
+
+ ocf_iomem_t sc_bar0;
+ bus_size_t sc_bar0_lastreg;/* bar0 last reg written */
+ ocf_iomem_t sc_bar1;
+ bus_size_t sc_bar1_lastreg;/* bar1 last reg written */
+
+ int sc_irq;
+
+ u_int32_t sc_dmaier;
+ u_int32_t sc_drammodel; /* 1=dram, 0=sram */
+ u_int32_t sc_pllconfig; /* 7954/7955/7956 PLL config */
+
+ struct hifn_dma *sc_dma;
+ dma_addr_t sc_dma_physaddr;/* physical address of sc_dma */
+
+ int sc_dmansegs;
+ int32_t sc_cid;
+ int sc_maxses;
+ int sc_nsessions;
+ struct hifn_session *sc_sessions;
+ int sc_ramsize;
+ int sc_flags;
+#define HIFN_HAS_RNG 0x1 /* includes random number generator */
+#define HIFN_HAS_PUBLIC 0x2 /* includes public key support */
+#define HIFN_HAS_AES 0x4 /* includes AES support */
+#define HIFN_IS_7811 0x8 /* Hifn 7811 part */
+#define HIFN_IS_7956 0x10 /* Hifn 7956/7955 don't have SDRAM */
+
+ struct timer_list sc_tickto; /* for managing DMA */
+
+ int sc_rngfirst;
+ int sc_rnghz; /* RNG polling frequency */
+
+ int sc_c_busy; /* command ring busy */
+ int sc_s_busy; /* source data ring busy */
+ int sc_d_busy; /* destination data ring busy */
+ int sc_r_busy; /* result ring busy */
+ int sc_active; /* for initial countdown */
+ int sc_needwakeup; /* ops q'd wating on resources */
+ int sc_curbatch; /* # ops submitted w/o int */
+ int sc_suspended;
+#ifdef HIFN_VULCANDEV
+ struct cdev *sc_pkdev;
+#endif
+};
+
+#define HIFN_LOCK(_sc) spin_lock_irqsave(&(_sc)->sc_mtx, l_flags)
+#define HIFN_UNLOCK(_sc) spin_unlock_irqrestore(&(_sc)->sc_mtx, l_flags)
+
+/*
+ * hifn_command_t
+ *
+ * This is the control structure used to pass commands to hifn_encrypt().
+ *
+ * flags
+ * -----
+ * Flags is the bitwise "or" values for command configuration. A single
+ * encrypt direction needs to be set:
+ *
+ * HIFN_ENCODE or HIFN_DECODE
+ *
+ * To use cryptography, a single crypto algorithm must be included:
+ *
+ * HIFN_CRYPT_3DES or HIFN_CRYPT_DES
+ *
+ * To use authentication is used, a single MAC algorithm must be included:
+ *
+ * HIFN_MAC_MD5 or HIFN_MAC_SHA1
+ *
+ * By default MD5 uses a 16 byte hash and SHA-1 uses a 20 byte hash.
+ * If the value below is set, hash values are truncated or assumed
+ * truncated to 12 bytes:
+ *
+ * HIFN_MAC_TRUNC
+ *
+ * Keys for encryption and authentication can be sent as part of a command,
+ * or the last key value used with a particular session can be retrieved
+ * and used again if either of these flags are not specified.
+ *
+ * HIFN_CRYPT_NEW_KEY, HIFN_MAC_NEW_KEY
+ *
+ * session_num
+ * -----------
+ * A number between 0 and 2048 (for DRAM models) or a number between
+ * 0 and 768 (for SRAM models). Those who don't want to use session
+ * numbers should leave value at zero and send a new crypt key and/or
+ * new MAC key on every command. If you use session numbers and
+ * don't send a key with a command, the last key sent for that same
+ * session number will be used.
+ *
+ * Warning: Using session numbers and multiboard at the same time
+ * is currently broken.
+ *
+ * mbuf
+ * ----
+ * Either fill in the mbuf pointer and npa=0 or
+ * fill packp[] and packl[] and set npa to > 0
+ *
+ * mac_header_skip
+ * ---------------
+ * The number of bytes of the source_buf that are skipped over before
+ * authentication begins. This must be a number between 0 and 2^16-1
+ * and can be used by IPsec implementers to skip over IP headers.
+ * *** Value ignored if authentication not used ***
+ *
+ * crypt_header_skip
+ * -----------------
+ * The number of bytes of the source_buf that are skipped over before
+ * the cryptographic operation begins. This must be a number between 0
+ * and 2^16-1. For IPsec, this number will always be 8 bytes larger
+ * than the auth_header_skip (to skip over the ESP header).
+ * *** Value ignored if cryptography not used ***
+ *
+ */
+struct hifn_operand {
+ union {
+ struct sk_buff *skb;
+ struct uio *io;
+ unsigned char *buf;
+ } u;
+ void *map;
+ bus_size_t mapsize;
+ int nsegs;
+ struct {
+ dma_addr_t ds_addr;
+ int ds_len;
+ } segs[MAX_SCATTER];
+};
+
+struct hifn_command {
+ u_int16_t session_num;
+ u_int16_t base_masks, cry_masks, mac_masks;
+ u_int8_t iv[HIFN_MAX_IV_LENGTH], *ck, mac[HIFN_MAC_KEY_LENGTH];
+ int cklen;
+ int sloplen, slopidx;
+
+ struct hifn_operand src;
+ struct hifn_operand dst;
+
+ struct hifn_softc *softc;
+ struct cryptop *crp;
+ struct cryptodesc *enccrd, *maccrd;
+};
+
+#define src_skb src.u.skb
+#define src_io src.u.io
+#define src_map src.map
+#define src_mapsize src.mapsize
+#define src_segs src.segs
+#define src_nsegs src.nsegs
+#define src_buf src.u.buf
+
+#define dst_skb dst.u.skb
+#define dst_io dst.u.io
+#define dst_map dst.map
+#define dst_mapsize dst.mapsize
+#define dst_segs dst.segs
+#define dst_nsegs dst.nsegs
+#define dst_buf dst.u.buf
+
+/*
+ * Return values for hifn_crypto()
+ */
+#define HIFN_CRYPTO_SUCCESS 0
+#define HIFN_CRYPTO_BAD_INPUT (-1)
+#define HIFN_CRYPTO_RINGS_FULL (-2)
+
+/**************************************************************************
+ *
+ * Function: hifn_crypto
+ *
+ * Purpose: Called by external drivers to begin an encryption on the
+ * HIFN board.
+ *
+ * Blocking/Non-blocking Issues
+ * ============================
+ * The driver cannot block in hifn_crypto (no calls to tsleep) currently.
+ * hifn_crypto() returns HIFN_CRYPTO_RINGS_FULL if there is not enough
+ * room in any of the rings for the request to proceed.
+ *
+ * Return Values
+ * =============
+ * 0 for success, negative values on error
+ *
+ * Defines for negative error codes are:
+ *
+ * HIFN_CRYPTO_BAD_INPUT : The passed in command had invalid settings.
+ * HIFN_CRYPTO_RINGS_FULL : All DMA rings were full and non-blocking
+ * behaviour was requested.
+ *
+ *************************************************************************/
+
+/*
+ * Convert back and forth from 'sid' to 'card' and 'session'
+ */
+#define HIFN_CARD(sid) (((sid) & 0xf0000000) >> 28)
+#define HIFN_SESSION(sid) ((sid) & 0x000007ff)
+#define HIFN_SID(crd,ses) (((crd) << 28) | ((ses) & 0x7ff))
+
+#endif /* _KERNEL */
+
+struct hifn_stats {
+ u_int64_t hst_ibytes;
+ u_int64_t hst_obytes;
+ u_int32_t hst_ipackets;
+ u_int32_t hst_opackets;
+ u_int32_t hst_invalid;
+ u_int32_t hst_nomem; /* malloc or one of hst_nomem_* */
+ u_int32_t hst_abort;
+ u_int32_t hst_noirq; /* IRQ for no reason */
+ u_int32_t hst_totbatch; /* ops submitted w/o interrupt */
+ u_int32_t hst_maxbatch; /* max ops submitted together */
+ u_int32_t hst_unaligned; /* unaligned src caused copy */
+ /*
+ * The following divides hst_nomem into more specific buckets.
+ */
+ u_int32_t hst_nomem_map; /* bus_dmamap_create failed */
+ u_int32_t hst_nomem_load; /* bus_dmamap_load_* failed */
+ u_int32_t hst_nomem_mbuf; /* MGET* failed */
+ u_int32_t hst_nomem_mcl; /* MCLGET* failed */
+ u_int32_t hst_nomem_cr; /* out of command/result descriptor */
+ u_int32_t hst_nomem_sd; /* out of src/dst descriptors */
+};
+
+#endif /* __HIFN7751VAR_H__ */
diff --git a/crypto/ocf/hifn/hifnHIPP.c b/crypto/ocf/hifn/hifnHIPP.c
new file mode 100644
index 000000000000..45ad7bacfa11
--- /dev/null
+++ b/crypto/ocf/hifn/hifnHIPP.c
@@ -0,0 +1,420 @@
+/*-
+ * Driver for Hifn HIPP-I/II chipset
+ * Copyright (c) 2006 Michael Richardson <mcr@xelerance.com>
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored by Hifn Inc.
+ *
+ */
+
+/*
+ * Driver for various Hifn encryption processors.
+ */
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <linux/uio.h>
+#include <linux/sysfs.h>
+#include <linux/miscdevice.h>
+#include <asm/io.h>
+
+#include <cryptodev.h>
+
+#include "hifnHIPPreg.h"
+#include "hifnHIPPvar.h"
+
+#if 1
+#define DPRINTF(a...) if (hipp_debug) { \
+ printk("%s: ", sc ? \
+ device_get_nameunit(sc->sc_dev) : "hifn"); \
+ printk(a); \
+ } else
+#else
+#define DPRINTF(a...)
+#endif
+
+typedef int bus_size_t;
+
+static inline int
+pci_get_revid(struct pci_dev *dev)
+{
+ u8 rid = 0;
+ pci_read_config_byte(dev, PCI_REVISION_ID, &rid);
+ return rid;
+}
+
+#define debug hipp_debug
+int hipp_debug = 0;
+module_param(hipp_debug, int, 0644);
+MODULE_PARM_DESC(hipp_debug, "Enable debug");
+
+int hipp_maxbatch = 1;
+module_param(hipp_maxbatch, int, 0644);
+MODULE_PARM_DESC(hipp_maxbatch, "max ops to batch w/o interrupt");
+
+static int hipp_probe(struct pci_dev *dev, const struct pci_device_id *ent);
+static void hipp_remove(struct pci_dev *dev);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+static irqreturn_t hipp_intr(int irq, void *arg);
+#else
+static irqreturn_t hipp_intr(int irq, void *arg, struct pt_regs *regs);
+#endif
+
+static int hipp_num_chips = 0;
+static struct hipp_softc *hipp_chip_idx[HIPP_MAX_CHIPS];
+
+static int hipp_newsession(device_t, u_int32_t *, struct cryptoini *);
+static int hipp_freesession(device_t, u_int64_t);
+static int hipp_process(device_t, struct cryptop *, int);
+
+static device_method_t hipp_methods = {
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, hipp_newsession),
+ DEVMETHOD(cryptodev_freesession,hipp_freesession),
+ DEVMETHOD(cryptodev_process, hipp_process),
+};
+
+static __inline u_int32_t
+READ_REG(struct hipp_softc *sc, unsigned int barno, bus_size_t reg)
+{
+ u_int32_t v = readl(sc->sc_bar[barno] + reg);
+ //sc->sc_bar0_lastreg = (bus_size_t) -1;
+ return (v);
+}
+static __inline void
+WRITE_REG(struct hipp_softc *sc, unsigned int barno, bus_size_t reg, u_int32_t val)
+{
+ writel(val, sc->sc_bar[barno] + reg);
+}
+
+#define READ_REG_0(sc, reg) READ_REG(sc, 0, reg)
+#define WRITE_REG_0(sc, reg, val) WRITE_REG(sc,0, reg, val)
+#define READ_REG_1(sc, reg) READ_REG(sc, 1, reg)
+#define WRITE_REG_1(sc, reg, val) WRITE_REG(sc,1, reg, val)
+
+static int
+hipp_newsession(device_t dev, u_int32_t *sidp, struct cryptoini *cri)
+{
+ return EINVAL;
+}
+
+static int
+hipp_freesession(device_t dev, u_int64_t tid)
+{
+ return EINVAL;
+}
+
+static int
+hipp_process(device_t dev, struct cryptop *crp, int hint)
+{
+ return EINVAL;
+}
+
+static const char*
+hipp_partname(struct hipp_softc *sc, char buf[128], size_t blen)
+{
+ char *n = NULL;
+
+ switch (pci_get_vendor(sc->sc_pcidev)) {
+ case PCI_VENDOR_HIFN:
+ switch (pci_get_device(sc->sc_pcidev)) {
+ case PCI_PRODUCT_HIFN_7855: n = "Hifn 7855";
+ case PCI_PRODUCT_HIFN_8155: n = "Hifn 8155";
+ case PCI_PRODUCT_HIFN_6500: n = "Hifn 6500";
+ }
+ }
+
+ if(n==NULL) {
+ snprintf(buf, blen, "VID=%02x,PID=%02x",
+ pci_get_vendor(sc->sc_pcidev),
+ pci_get_device(sc->sc_pcidev));
+ } else {
+ buf[0]='\0';
+ strncat(buf, n, blen);
+ }
+ return buf;
+}
+
+struct hipp_fs_entry {
+ struct attribute attr;
+ /* other stuff */
+};
+
+
+static ssize_t
+cryptoid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hipp_softc *sc;
+
+ sc = pci_get_drvdata(to_pci_dev (dev));
+ return sprintf (buf, "%d\n", sc->sc_cid);
+}
+
+struct device_attribute hipp_dev_cryptoid = __ATTR_RO(cryptoid);
+
+/*
+ * Attach an interface that successfully probed.
+ */
+static int
+hipp_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+ struct hipp_softc *sc = NULL;
+ int i;
+ //char rbase;
+ //u_int16_t ena;
+ int rev;
+ //int rseg;
+ int rc;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ if (pci_enable_device(dev) < 0)
+ return(-ENODEV);
+
+ if (pci_set_mwi(dev))
+ return(-ENODEV);
+
+ if (!dev->irq) {
+ printk("hifn: found device with no IRQ assigned. check BIOS settings!");
+ pci_disable_device(dev);
+ return(-ENODEV);
+ }
+
+ sc = (struct hipp_softc *) kmalloc(sizeof(*sc), GFP_KERNEL);
+ if (!sc)
+ return(-ENOMEM);
+ memset(sc, 0, sizeof(*sc));
+
+ softc_device_init(sc, "hifn-hipp", hipp_num_chips, hipp_methods);
+
+ sc->sc_pcidev = dev;
+ sc->sc_irq = -1;
+ sc->sc_cid = -1;
+ sc->sc_num = hipp_num_chips++;
+
+ if (sc->sc_num < HIPP_MAX_CHIPS)
+ hipp_chip_idx[sc->sc_num] = sc;
+
+ pci_set_drvdata(sc->sc_pcidev, sc);
+
+ spin_lock_init(&sc->sc_mtx);
+
+ /*
+ * Setup PCI resources.
+ * The READ_REG_0, WRITE_REG_0, READ_REG_1,
+ * and WRITE_REG_1 macros throughout the driver are used
+ * to permit better debugging.
+ */
+ for(i=0; i<4; i++) {
+ unsigned long mem_start, mem_len;
+ mem_start = pci_resource_start(sc->sc_pcidev, i);
+ mem_len = pci_resource_len(sc->sc_pcidev, i);
+ sc->sc_barphy[i] = (caddr_t)mem_start;
+ sc->sc_bar[i] = (ocf_iomem_t) ioremap(mem_start, mem_len);
+ if (!sc->sc_bar[i]) {
+ device_printf(sc->sc_dev, "cannot map bar%d register space\n", i);
+ goto fail;
+ }
+ }
+
+ //hipp_reset_board(sc, 0);
+ pci_set_master(sc->sc_pcidev);
+
+ /*
+ * Arrange the interrupt line.
+ */
+ rc = request_irq(dev->irq, hipp_intr, IRQF_SHARED, "hifn", sc);
+ if (rc) {
+ device_printf(sc->sc_dev, "could not map interrupt: %d\n", rc);
+ goto fail;
+ }
+ sc->sc_irq = dev->irq;
+
+ rev = READ_REG_1(sc, HIPP_1_REVID) & 0xffff;
+
+ {
+ char b[32];
+ device_printf(sc->sc_dev, "%s, rev %u",
+ hipp_partname(sc, b, sizeof(b)), rev);
+ }
+
+#if 0
+ if (sc->sc_flags & HIFN_IS_7956)
+ printf(", pll=0x%x<%s clk, %ux mult>",
+ sc->sc_pllconfig,
+ sc->sc_pllconfig & HIFN_PLL_REF_SEL ? "ext" : "pci",
+ 2 + 2*((sc->sc_pllconfig & HIFN_PLL_ND) >> 11));
+#endif
+ printf("\n");
+
+ sc->sc_cid = crypto_get_driverid(softc_get_device(sc),CRYPTOCAP_F_HARDWARE);
+ if (sc->sc_cid < 0) {
+ device_printf(sc->sc_dev, "could not get crypto driver id\n");
+ goto fail;
+ }
+
+#if 0 /* cannot work with a non-GPL module */
+ /* make a sysfs entry to let the world know what entry we got */
+ sysfs_create_file(&sc->sc_pcidev->dev.kobj, &hipp_dev_cryptoid.attr);
+#endif
+
+#if 0
+ init_timer(&sc->sc_tickto);
+ sc->sc_tickto.function = hifn_tick;
+ sc->sc_tickto.data = (unsigned long) sc->sc_num;
+ mod_timer(&sc->sc_tickto, jiffies + HZ);
+#endif
+
+#if 0 /* no code here yet ?? */
+ crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0);
+#endif
+
+ return (0);
+
+fail:
+ if (sc->sc_cid >= 0)
+ crypto_unregister_all(sc->sc_cid);
+ if (sc->sc_irq != -1)
+ free_irq(sc->sc_irq, sc);
+
+#if 0
+ if (sc->sc_dma) {
+ /* Turn off DMA polling */
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
+
+ pci_free_consistent(sc->sc_pcidev,
+ sizeof(*sc->sc_dma),
+ sc->sc_dma, sc->sc_dma_physaddr);
+ }
+#endif
+ kfree(sc);
+ return (-ENXIO);
+}
+
+/*
+ * Detach an interface that successfully probed.
+ */
+static void
+hipp_remove(struct pci_dev *dev)
+{
+ struct hipp_softc *sc = pci_get_drvdata(dev);
+ unsigned long l_flags;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ /* disable interrupts */
+ HIPP_LOCK(sc);
+
+#if 0
+ WRITE_REG_1(sc, HIFN_1_DMA_IER, 0);
+ HIFN_UNLOCK(sc);
+
+ /*XXX other resources */
+ del_timer_sync(&sc->sc_tickto);
+
+ /* Turn off DMA polling */
+ WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
+ HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
+#endif
+
+ crypto_unregister_all(sc->sc_cid);
+
+ free_irq(sc->sc_irq, sc);
+
+#if 0
+ pci_free_consistent(sc->sc_pcidev, sizeof(*sc->sc_dma),
+ sc->sc_dma, sc->sc_dma_physaddr);
+#endif
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+static irqreturn_t hipp_intr(int irq, void *arg)
+#else
+static irqreturn_t hipp_intr(int irq, void *arg, struct pt_regs *regs)
+#endif
+{
+ struct hipp_softc *sc = arg;
+
+ sc = sc; /* shut up compiler */
+
+ return IRQ_HANDLED;
+}
+
+static struct pci_device_id hipp_pci_tbl[] = {
+ { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_7855,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ { PCI_VENDOR_HIFN, PCI_PRODUCT_HIFN_8155,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+};
+MODULE_DEVICE_TABLE(pci, hipp_pci_tbl);
+
+static struct pci_driver hipp_driver = {
+ .name = "hipp",
+ .id_table = hipp_pci_tbl,
+ .probe = hipp_probe,
+ .remove = hipp_remove,
+ /* add PM stuff here one day */
+};
+
+static int __init hipp_init (void)
+{
+ struct hipp_softc *sc = NULL;
+ int rc;
+
+ DPRINTF("%s(%p)\n", __FUNCTION__, hipp_init);
+
+ rc = pci_register_driver(&hipp_driver);
+ pci_register_driver_compat(&hipp_driver, rc);
+
+ return rc;
+}
+
+static void __exit hipp_exit (void)
+{
+ pci_unregister_driver(&hipp_driver);
+}
+
+module_init(hipp_init);
+module_exit(hipp_exit);
+
+MODULE_LICENSE("BSD");
+MODULE_AUTHOR("Michael Richardson <mcr@xelerance.com>");
+MODULE_DESCRIPTION("OCF driver for hifn HIPP-I/II PCI crypto devices");
diff --git a/crypto/ocf/hifn/hifnHIPPreg.h b/crypto/ocf/hifn/hifnHIPPreg.h
new file mode 100644
index 000000000000..8c0e72038f70
--- /dev/null
+++ b/crypto/ocf/hifn/hifnHIPPreg.h
@@ -0,0 +1,46 @@
+/*-
+ * Hifn HIPP-I/HIPP-II (7855/8155) driver.
+ * Copyright (c) 2006 Michael Richardson <mcr@xelerance.com>
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored by Hifn inc.
+ *
+ */
+
+#ifndef __HIFNHIPP_H__
+#define __HIFNHIPP_H__
+
+/*
+ * PCI vendor and device identifiers
+ */
+#define PCI_VENDOR_HIFN 0x13a3 /* Hifn */
+#define PCI_PRODUCT_HIFN_6500 0x0006 /* 6500 */
+#define PCI_PRODUCT_HIFN_7855 0x001f /* 7855 */
+#define PCI_PRODUCT_HIFN_8155 0x999 /* XXX 8155 */
+
+#define HIPP_1_REVID 0x01 /* BOGUS */
+
+#endif /* __HIPP_H__ */
diff --git a/crypto/ocf/hifn/hifnHIPPvar.h b/crypto/ocf/hifn/hifnHIPPvar.h
new file mode 100644
index 000000000000..61d292fb5e42
--- /dev/null
+++ b/crypto/ocf/hifn/hifnHIPPvar.h
@@ -0,0 +1,93 @@
+/*
+ * Hifn HIPP-I/HIPP-II (7855/8155) driver.
+ * Copyright (c) 2006 Michael Richardson <mcr@xelerance.com> *
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Effort sponsored by Hifn inc.
+ *
+ */
+
+#ifndef __HIFNHIPPVAR_H__
+#define __HIFNHIPPVAR_H__
+
+#define HIPP_MAX_CHIPS 8
+
+/*
+ * Holds data specific to a single Hifn HIPP-I board.
+ */
+struct hipp_softc {
+ softc_device_decl sc_dev;
+
+ struct pci_dev *sc_pcidev; /* device backpointer */
+ ocf_iomem_t sc_bar[5];
+ caddr_t sc_barphy[5]; /* physical address */
+ int sc_num; /* for multiple devs */
+ spinlock_t sc_mtx; /* per-instance lock */
+ int32_t sc_cid;
+ int sc_irq;
+
+#if 0
+
+ u_int32_t sc_dmaier;
+ u_int32_t sc_drammodel; /* 1=dram, 0=sram */
+ u_int32_t sc_pllconfig; /* 7954/7955/7956 PLL config */
+
+ struct hifn_dma *sc_dma;
+ dma_addr_t sc_dma_physaddr;/* physical address of sc_dma */
+
+ int sc_dmansegs;
+ int sc_maxses;
+ int sc_nsessions;
+ struct hifn_session *sc_sessions;
+ int sc_ramsize;
+ int sc_flags;
+#define HIFN_HAS_RNG 0x1 /* includes random number generator */
+#define HIFN_HAS_PUBLIC 0x2 /* includes public key support */
+#define HIFN_HAS_AES 0x4 /* includes AES support */
+#define HIFN_IS_7811 0x8 /* Hifn 7811 part */
+#define HIFN_IS_7956 0x10 /* Hifn 7956/7955 don't have SDRAM */
+
+ struct timer_list sc_tickto; /* for managing DMA */
+
+ int sc_rngfirst;
+ int sc_rnghz; /* RNG polling frequency */
+
+ int sc_c_busy; /* command ring busy */
+ int sc_s_busy; /* source data ring busy */
+ int sc_d_busy; /* destination data ring busy */
+ int sc_r_busy; /* result ring busy */
+ int sc_active; /* for initial countdown */
+ int sc_needwakeup; /* ops q'd wating on resources */
+ int sc_curbatch; /* # ops submitted w/o int */
+ int sc_suspended;
+ struct miscdevice sc_miscdev;
+#endif
+};
+
+#define HIPP_LOCK(_sc) spin_lock_irqsave(&(_sc)->sc_mtx, l_flags)
+#define HIPP_UNLOCK(_sc) spin_unlock_irqrestore(&(_sc)->sc_mtx, l_flags)
+
+#endif /* __HIFNHIPPVAR_H__ */
diff --git a/crypto/ocf/ixp4xx/Makefile b/crypto/ocf/ixp4xx/Makefile
new file mode 100644
index 000000000000..d94a3b79fa5c
--- /dev/null
+++ b/crypto/ocf/ixp4xx/Makefile
@@ -0,0 +1,104 @@
+# for SGlinux builds
+-include $(ROOTDIR)/modules/.config
+
+#
+# You will need to point this at your Intel ixp425 includes, this portion
+# of the Makefile only really works under SGLinux with the appropriate libs
+# installed. They can be downloaded from http://www.snapgear.org/
+#
+ifeq ($(CONFIG_CPU_IXP46X),y)
+IXPLATFORM = ixp46X
+else
+ifeq ($(CONFIG_CPU_IXP43X),y)
+IXPLATFORM = ixp43X
+else
+IXPLATFORM = ixp42X
+endif
+endif
+
+ifdef CONFIG_IXP400_LIB_2_4
+IX_XSCALE_SW = $(ROOTDIR)/modules/ixp425/ixp400-2.4/ixp400_xscale_sw
+OSAL_DIR = $(ROOTDIR)/modules/ixp425/ixp400-2.4/ixp_osal
+endif
+ifdef CONFIG_IXP400_LIB_2_1
+IX_XSCALE_SW = $(ROOTDIR)/modules/ixp425/ixp400-2.1/ixp400_xscale_sw
+OSAL_DIR = $(ROOTDIR)/modules/ixp425/ixp400-2.1/ixp_osal
+endif
+ifdef CONFIG_IXP400_LIB_2_0
+IX_XSCALE_SW = $(ROOTDIR)/modules/ixp425/ixp400-2.0/ixp400_xscale_sw
+OSAL_DIR = $(ROOTDIR)/modules/ixp425/ixp400-2.0/ixp_osal
+endif
+ifdef IX_XSCALE_SW
+ifdef CONFIG_IXP400_LIB_2_4
+IXP_CFLAGS = \
+ -I$(ROOTDIR)/. \
+ -I$(IX_XSCALE_SW)/src/include \
+ -I$(OSAL_DIR)/common/include/ \
+ -I$(OSAL_DIR)/common/include/modules/ \
+ -I$(OSAL_DIR)/common/include/modules/ddk/ \
+ -I$(OSAL_DIR)/common/include/modules/bufferMgt/ \
+ -I$(OSAL_DIR)/common/include/modules/ioMem/ \
+ -I$(OSAL_DIR)/common/os/linux/include/ \
+ -I$(OSAL_DIR)/common/os/linux/include/core/ \
+ -I$(OSAL_DIR)/common/os/linux/include/modules/ \
+ -I$(OSAL_DIR)/common/os/linux/include/modules/ddk/ \
+ -I$(OSAL_DIR)/common/os/linux/include/modules/bufferMgt/ \
+ -I$(OSAL_DIR)/common/os/linux/include/modules/ioMem/ \
+ -I$(OSAL_DIR)/platforms/$(IXPLATFORM)/include/ \
+ -I$(OSAL_DIR)/platforms/$(IXPLATFORM)/os/linux/include/ \
+ -DENABLE_IOMEM -DENABLE_BUFFERMGT -DENABLE_DDK \
+ -DUSE_IXP4XX_CRYPTO
+else
+IXP_CFLAGS = \
+ -I$(ROOTDIR)/. \
+ -I$(IX_XSCALE_SW)/src/include \
+ -I$(OSAL_DIR)/ \
+ -I$(OSAL_DIR)/os/linux/include/ \
+ -I$(OSAL_DIR)/os/linux/include/modules/ \
+ -I$(OSAL_DIR)/os/linux/include/modules/ioMem/ \
+ -I$(OSAL_DIR)/os/linux/include/modules/bufferMgt/ \
+ -I$(OSAL_DIR)/os/linux/include/core/ \
+ -I$(OSAL_DIR)/os/linux/include/platforms/ \
+ -I$(OSAL_DIR)/os/linux/include/platforms/ixp400/ \
+ -I$(OSAL_DIR)/os/linux/include/platforms/ixp400/ixp425 \
+ -I$(OSAL_DIR)/os/linux/include/platforms/ixp400/ixp465 \
+ -I$(OSAL_DIR)/os/linux/include/core/ \
+ -I$(OSAL_DIR)/include/ \
+ -I$(OSAL_DIR)/include/modules/ \
+ -I$(OSAL_DIR)/include/modules/bufferMgt/ \
+ -I$(OSAL_DIR)/include/modules/ioMem/ \
+ -I$(OSAL_DIR)/include/platforms/ \
+ -I$(OSAL_DIR)/include/platforms/ixp400/ \
+ -DUSE_IXP4XX_CRYPTO
+endif
+endif
+ifdef CONFIG_IXP400_LIB_1_4
+IXP_CFLAGS = \
+ -I$(ROOTDIR)/. \
+ -I$(ROOTDIR)/modules/ixp425/ixp400-1.4/ixp400_xscale_sw/src/include \
+ -I$(ROOTDIR)/modules/ixp425/ixp400-1.4/ixp400_xscale_sw/src/linux \
+ -DUSE_IXP4XX_CRYPTO
+endif
+ifndef IXPDIR
+IXPDIR = ixp-version-is-not-supported
+endif
+
+ifeq ($(CONFIG_CPU_IXP46X),y)
+IXP_CFLAGS += -D__ixp46X
+else
+ifeq ($(CONFIG_CPU_IXP43X),y)
+IXP_CFLAGS += -D__ixp43X
+else
+IXP_CFLAGS += -D__ixp42X
+endif
+endif
+
+obj-$(CONFIG_OCF_IXP4XX) += ixp4xx.o
+
+obj ?= .
+EXTRA_CFLAGS += $(IXP_CFLAGS) -I$(obj)/.. -I$(obj)/.
+
+ifdef TOPDIR
+-include $(TOPDIR)/Rules.make
+endif
+
diff --git a/crypto/ocf/ixp4xx/ixp4xx.c b/crypto/ocf/ixp4xx/ixp4xx.c
new file mode 100644
index 000000000000..fba31b000bdc
--- /dev/null
+++ b/crypto/ocf/ixp4xx/ixp4xx.c
@@ -0,0 +1,1328 @@
+/*
+ * An OCF module that uses Intels IXP CryptACC API to do the crypto.
+ * This driver requires the IXP400 Access Library that is available
+ * from Intel in order to operate (or compile).
+ *
+ * Written by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2006-2007 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ */
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/crypto.h>
+#include <linux/interrupt.h>
+#include <asm/scatterlist.h>
+
+#include <IxTypes.h>
+#include <IxOsBuffMgt.h>
+#include <IxNpeDl.h>
+#include <IxCryptoAcc.h>
+#include <IxQMgr.h>
+#include <IxOsServices.h>
+#include <IxOsCacheMMU.h>
+
+#include <cryptodev.h>
+#include <uio.h>
+
+#ifndef IX_MBUF_PRIV
+#define IX_MBUF_PRIV(x) ((x)->priv)
+#endif
+
+struct ixp_data;
+
+struct ixp_q {
+ struct list_head ixp_q_list;
+ struct ixp_data *ixp_q_data;
+ struct cryptop *ixp_q_crp;
+ struct cryptodesc *ixp_q_ccrd;
+ struct cryptodesc *ixp_q_acrd;
+ IX_MBUF ixp_q_mbuf;
+ UINT8 *ixp_hash_dest; /* Location for hash in client buffer */
+ UINT8 *ixp_hash_src; /* Location of hash in internal buffer */
+ unsigned char ixp_q_iv_data[IX_CRYPTO_ACC_MAX_CIPHER_IV_LENGTH];
+ unsigned char *ixp_q_iv;
+};
+
+struct ixp_data {
+ int ixp_registered; /* is the context registered */
+ int ixp_crd_flags; /* detect direction changes */
+
+ int ixp_cipher_alg;
+ int ixp_auth_alg;
+
+ UINT32 ixp_ctx_id;
+ UINT32 ixp_hash_key_id; /* used when hashing */
+ IxCryptoAccCtx ixp_ctx;
+ IX_MBUF ixp_pri_mbuf;
+ IX_MBUF ixp_sec_mbuf;
+
+ struct work_struct ixp_pending_work;
+ struct work_struct ixp_registration_work;
+ struct list_head ixp_q; /* unprocessed requests */
+};
+
+#ifdef __ixp46X
+
+#define MAX_IOP_SIZE 64 /* words */
+#define MAX_OOP_SIZE 128
+
+#define MAX_PARAMS 3
+
+struct ixp_pkq {
+ struct list_head pkq_list;
+ struct cryptkop *pkq_krp;
+
+ IxCryptoAccPkeEauInOperands pkq_op;
+ IxCryptoAccPkeEauOpResult pkq_result;
+
+ UINT32 pkq_ibuf0[MAX_IOP_SIZE];
+ UINT32 pkq_ibuf1[MAX_IOP_SIZE];
+ UINT32 pkq_ibuf2[MAX_IOP_SIZE];
+ UINT32 pkq_obuf[MAX_OOP_SIZE];
+};
+
+static LIST_HEAD(ixp_pkq); /* current PK wait list */
+static struct ixp_pkq *ixp_pk_cur;
+static spinlock_t ixp_pkq_lock;
+
+#endif /* __ixp46X */
+
+static int ixp_blocked = 0;
+
+static int32_t ixp_id = -1;
+static struct ixp_data **ixp_sessions = NULL;
+static u_int32_t ixp_sesnum = 0;
+
+static int ixp_process(device_t, struct cryptop *, int);
+static int ixp_newsession(device_t, u_int32_t *, struct cryptoini *);
+static int ixp_freesession(device_t, u_int64_t);
+#ifdef __ixp46X
+static int ixp_kprocess(device_t, struct cryptkop *krp, int hint);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static kmem_cache_t *qcache;
+#else
+static struct kmem_cache *qcache;
+#endif
+
+#define debug ixp_debug
+static int ixp_debug = 0;
+module_param(ixp_debug, int, 0644);
+MODULE_PARM_DESC(ixp_debug, "Enable debug");
+
+static int ixp_init_crypto = 1;
+module_param(ixp_init_crypto, int, 0444); /* RO after load/boot */
+MODULE_PARM_DESC(ixp_init_crypto, "Call ixCryptoAccInit (default is 1)");
+
+static void ixp_process_pending(void *arg);
+static void ixp_registration(void *arg);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void ixp_process_pending_wq(struct work_struct *work);
+static void ixp_registration_wq(struct work_struct *work);
+#endif
+
+/*
+ * dummy device structure
+ */
+
+static struct {
+ softc_device_decl sc_dev;
+} ixpdev;
+
+static device_method_t ixp_methods = {
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, ixp_newsession),
+ DEVMETHOD(cryptodev_freesession,ixp_freesession),
+ DEVMETHOD(cryptodev_process, ixp_process),
+#ifdef __ixp46X
+ DEVMETHOD(cryptodev_kprocess, ixp_kprocess),
+#endif
+};
+
+/*
+ * Generate a new software session.
+ */
+static int
+ixp_newsession(device_t dev, u_int32_t *sid, struct cryptoini *cri)
+{
+ struct ixp_data *ixp;
+ u_int32_t i;
+#define AUTH_LEN(cri, def) \
+ (cri->cri_mlen ? cri->cri_mlen : (def))
+
+ dprintk("%s():alg %d\n", __FUNCTION__,cri->cri_alg);
+ if (sid == NULL || cri == NULL) {
+ dprintk("%s,%d - EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ if (ixp_sessions) {
+ for (i = 1; i < ixp_sesnum; i++)
+ if (ixp_sessions[i] == NULL)
+ break;
+ } else
+ i = 1; /* NB: to silence compiler warning */
+
+ if (ixp_sessions == NULL || i == ixp_sesnum) {
+ struct ixp_data **ixpd;
+
+ if (ixp_sessions == NULL) {
+ i = 1; /* We leave ixp_sessions[0] empty */
+ ixp_sesnum = CRYPTO_SW_SESSIONS;
+ } else
+ ixp_sesnum *= 2;
+
+ ixpd = kmalloc(ixp_sesnum * sizeof(struct ixp_data *), SLAB_ATOMIC);
+ if (ixpd == NULL) {
+ /* Reset session number */
+ if (ixp_sesnum == CRYPTO_SW_SESSIONS)
+ ixp_sesnum = 0;
+ else
+ ixp_sesnum /= 2;
+ dprintk("%s,%d: ENOBUFS\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+ memset(ixpd, 0, ixp_sesnum * sizeof(struct ixp_data *));
+
+ /* Copy existing sessions */
+ if (ixp_sessions) {
+ memcpy(ixpd, ixp_sessions,
+ (ixp_sesnum / 2) * sizeof(struct ixp_data *));
+ kfree(ixp_sessions);
+ }
+
+ ixp_sessions = ixpd;
+ }
+
+ ixp_sessions[i] = (struct ixp_data *) kmalloc(sizeof(struct ixp_data),
+ SLAB_ATOMIC);
+ if (ixp_sessions[i] == NULL) {
+ ixp_freesession(NULL, i);
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ return ENOBUFS;
+ }
+
+ *sid = i;
+
+ ixp = ixp_sessions[i];
+ memset(ixp, 0, sizeof(*ixp));
+
+ ixp->ixp_cipher_alg = -1;
+ ixp->ixp_auth_alg = -1;
+ ixp->ixp_ctx_id = -1;
+ INIT_LIST_HEAD(&ixp->ixp_q);
+
+ ixp->ixp_ctx.useDifferentSrcAndDestMbufs = 0;
+
+ while (cri) {
+ switch (cri->cri_alg) {
+ case CRYPTO_DES_CBC:
+ ixp->ixp_cipher_alg = cri->cri_alg;
+ ixp->ixp_ctx.cipherCtx.cipherAlgo = IX_CRYPTO_ACC_CIPHER_DES;
+ ixp->ixp_ctx.cipherCtx.cipherMode = IX_CRYPTO_ACC_MODE_CBC;
+ ixp->ixp_ctx.cipherCtx.cipherKeyLen = (cri->cri_klen + 7) / 8;
+ ixp->ixp_ctx.cipherCtx.cipherBlockLen = IX_CRYPTO_ACC_DES_BLOCK_64;
+ ixp->ixp_ctx.cipherCtx.cipherInitialVectorLen =
+ IX_CRYPTO_ACC_DES_IV_64;
+ memcpy(ixp->ixp_ctx.cipherCtx.key.cipherKey,
+ cri->cri_key, (cri->cri_klen + 7) / 8);
+ break;
+
+ case CRYPTO_3DES_CBC:
+ ixp->ixp_cipher_alg = cri->cri_alg;
+ ixp->ixp_ctx.cipherCtx.cipherAlgo = IX_CRYPTO_ACC_CIPHER_3DES;
+ ixp->ixp_ctx.cipherCtx.cipherMode = IX_CRYPTO_ACC_MODE_CBC;
+ ixp->ixp_ctx.cipherCtx.cipherKeyLen = (cri->cri_klen + 7) / 8;
+ ixp->ixp_ctx.cipherCtx.cipherBlockLen = IX_CRYPTO_ACC_DES_BLOCK_64;
+ ixp->ixp_ctx.cipherCtx.cipherInitialVectorLen =
+ IX_CRYPTO_ACC_DES_IV_64;
+ memcpy(ixp->ixp_ctx.cipherCtx.key.cipherKey,
+ cri->cri_key, (cri->cri_klen + 7) / 8);
+ break;
+
+ case CRYPTO_RIJNDAEL128_CBC:
+ ixp->ixp_cipher_alg = cri->cri_alg;
+ ixp->ixp_ctx.cipherCtx.cipherAlgo = IX_CRYPTO_ACC_CIPHER_AES;
+ ixp->ixp_ctx.cipherCtx.cipherMode = IX_CRYPTO_ACC_MODE_CBC;
+ ixp->ixp_ctx.cipherCtx.cipherKeyLen = (cri->cri_klen + 7) / 8;
+ ixp->ixp_ctx.cipherCtx.cipherBlockLen = 16;
+ ixp->ixp_ctx.cipherCtx.cipherInitialVectorLen = 16;
+ memcpy(ixp->ixp_ctx.cipherCtx.key.cipherKey,
+ cri->cri_key, (cri->cri_klen + 7) / 8);
+ break;
+
+ case CRYPTO_MD5:
+ case CRYPTO_MD5_HMAC:
+ ixp->ixp_auth_alg = cri->cri_alg;
+ ixp->ixp_ctx.authCtx.authAlgo = IX_CRYPTO_ACC_AUTH_MD5;
+ ixp->ixp_ctx.authCtx.authDigestLen = AUTH_LEN(cri, MD5_HASH_LEN);
+ ixp->ixp_ctx.authCtx.aadLen = 0;
+ /* Only MD5_HMAC needs a key */
+ if (cri->cri_alg == CRYPTO_MD5_HMAC) {
+ ixp->ixp_ctx.authCtx.authKeyLen = (cri->cri_klen + 7) / 8;
+ if (ixp->ixp_ctx.authCtx.authKeyLen >
+ sizeof(ixp->ixp_ctx.authCtx.key.authKey)) {
+ printk(
+ "ixp4xx: Invalid key length for MD5_HMAC - %d bits\n",
+ cri->cri_klen);
+ ixp_freesession(NULL, i);
+ return EINVAL;
+ }
+ memcpy(ixp->ixp_ctx.authCtx.key.authKey,
+ cri->cri_key, (cri->cri_klen + 7) / 8);
+ }
+ break;
+
+ case CRYPTO_SHA1:
+ case CRYPTO_SHA1_HMAC:
+ ixp->ixp_auth_alg = cri->cri_alg;
+ ixp->ixp_ctx.authCtx.authAlgo = IX_CRYPTO_ACC_AUTH_SHA1;
+ ixp->ixp_ctx.authCtx.authDigestLen = AUTH_LEN(cri, SHA1_HASH_LEN);
+ ixp->ixp_ctx.authCtx.aadLen = 0;
+ /* Only SHA1_HMAC needs a key */
+ if (cri->cri_alg == CRYPTO_SHA1_HMAC) {
+ ixp->ixp_ctx.authCtx.authKeyLen = (cri->cri_klen + 7) / 8;
+ if (ixp->ixp_ctx.authCtx.authKeyLen >
+ sizeof(ixp->ixp_ctx.authCtx.key.authKey)) {
+ printk(
+ "ixp4xx: Invalid key length for SHA1_HMAC - %d bits\n",
+ cri->cri_klen);
+ ixp_freesession(NULL, i);
+ return EINVAL;
+ }
+ memcpy(ixp->ixp_ctx.authCtx.key.authKey,
+ cri->cri_key, (cri->cri_klen + 7) / 8);
+ }
+ break;
+
+ default:
+ printk("ixp: unknown algo 0x%x\n", cri->cri_alg);
+ ixp_freesession(NULL, i);
+ return EINVAL;
+ }
+ cri = cri->cri_next;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ INIT_WORK(&ixp->ixp_pending_work, ixp_process_pending_wq);
+ INIT_WORK(&ixp->ixp_registration_work, ixp_registration_wq);
+#else
+ INIT_WORK(&ixp->ixp_pending_work, ixp_process_pending, ixp);
+ INIT_WORK(&ixp->ixp_registration_work, ixp_registration, ixp);
+#endif
+
+ return 0;
+}
+
+
+/*
+ * Free a session.
+ */
+static int
+ixp_freesession(device_t dev, u_int64_t tid)
+{
+ u_int32_t sid = CRYPTO_SESID2LID(tid);
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (sid > ixp_sesnum || ixp_sessions == NULL ||
+ ixp_sessions[sid] == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ /* Silently accept and return */
+ if (sid == 0)
+ return 0;
+
+ if (ixp_sessions[sid]) {
+ if (ixp_sessions[sid]->ixp_ctx_id != -1) {
+ ixCryptoAccCtxUnregister(ixp_sessions[sid]->ixp_ctx_id);
+ ixp_sessions[sid]->ixp_ctx_id = -1;
+ }
+
+ flush_scheduled_work();
+
+ kfree(ixp_sessions[sid]);
+ }
+ ixp_sessions[sid] = NULL;
+ if (ixp_blocked) {
+ ixp_blocked = 0;
+ crypto_unblock(ixp_id, CRYPTO_SYMQ);
+ }
+ return 0;
+}
+
+
+/*
+ * callback for when hash processing is complete
+ */
+
+static void
+ixp_hash_perform_cb(
+ UINT32 hash_key_id,
+ IX_MBUF *bufp,
+ IxCryptoAccStatus status)
+{
+ struct ixp_q *q;
+
+ dprintk("%s(%u, %p, 0x%x)\n", __FUNCTION__, hash_key_id, bufp, status);
+
+ if (bufp == NULL) {
+ printk("ixp: NULL buf in %s\n", __FUNCTION__);
+ return;
+ }
+
+ q = IX_MBUF_PRIV(bufp);
+ if (q == NULL) {
+ printk("ixp: NULL priv in %s\n", __FUNCTION__);
+ return;
+ }
+
+ if (status == IX_CRYPTO_ACC_STATUS_SUCCESS) {
+ /* On success, need to copy hash back into original client buffer */
+ memcpy(q->ixp_hash_dest, q->ixp_hash_src,
+ (q->ixp_q_data->ixp_auth_alg == CRYPTO_SHA1) ?
+ SHA1_HASH_LEN : MD5_HASH_LEN);
+ }
+ else {
+ printk("ixp: hash perform failed status=%d\n", status);
+ q->ixp_q_crp->crp_etype = EINVAL;
+ }
+
+ /* Free internal buffer used for hashing */
+ kfree(IX_MBUF_MDATA(&q->ixp_q_mbuf));
+
+ crypto_done(q->ixp_q_crp);
+ kmem_cache_free(qcache, q);
+}
+
+/*
+ * setup a request and perform it
+ */
+static void
+ixp_q_process(struct ixp_q *q)
+{
+ IxCryptoAccStatus status;
+ struct ixp_data *ixp = q->ixp_q_data;
+ int auth_off = 0;
+ int auth_len = 0;
+ int crypt_off = 0;
+ int crypt_len = 0;
+ int icv_off = 0;
+ char *crypt_func;
+
+ dprintk("%s(%p)\n", __FUNCTION__, q);
+
+ if (q->ixp_q_ccrd) {
+ if (q->ixp_q_ccrd->crd_flags & CRD_F_IV_EXPLICIT) {
+ q->ixp_q_iv = q->ixp_q_ccrd->crd_iv;
+ } else {
+ q->ixp_q_iv = q->ixp_q_iv_data;
+ crypto_copydata(q->ixp_q_crp->crp_flags, q->ixp_q_crp->crp_buf,
+ q->ixp_q_ccrd->crd_inject,
+ ixp->ixp_ctx.cipherCtx.cipherInitialVectorLen,
+ (caddr_t) q->ixp_q_iv);
+ }
+
+ if (q->ixp_q_acrd) {
+ auth_off = q->ixp_q_acrd->crd_skip;
+ auth_len = q->ixp_q_acrd->crd_len;
+ icv_off = q->ixp_q_acrd->crd_inject;
+ }
+
+ crypt_off = q->ixp_q_ccrd->crd_skip;
+ crypt_len = q->ixp_q_ccrd->crd_len;
+ } else { /* if (q->ixp_q_acrd) */
+ auth_off = q->ixp_q_acrd->crd_skip;
+ auth_len = q->ixp_q_acrd->crd_len;
+ icv_off = q->ixp_q_acrd->crd_inject;
+ }
+
+ if (q->ixp_q_crp->crp_flags & CRYPTO_F_SKBUF) {
+ struct sk_buff *skb = (struct sk_buff *) q->ixp_q_crp->crp_buf;
+ if (skb_shinfo(skb)->nr_frags) {
+ /*
+ * DAVIDM fix this limitation one day by using
+ * a buffer pool and chaining, it is not currently
+ * needed for current user/kernel space acceleration
+ */
+ printk("ixp: Cannot handle fragmented skb's yet !\n");
+ q->ixp_q_crp->crp_etype = ENOENT;
+ goto done;
+ }
+ IX_MBUF_MLEN(&q->ixp_q_mbuf) =
+ IX_MBUF_PKT_LEN(&q->ixp_q_mbuf) = skb->len;
+ IX_MBUF_MDATA(&q->ixp_q_mbuf) = skb->data;
+ } else if (q->ixp_q_crp->crp_flags & CRYPTO_F_IOV) {
+ struct uio *uiop = (struct uio *) q->ixp_q_crp->crp_buf;
+ if (uiop->uio_iovcnt != 1) {
+ /*
+ * DAVIDM fix this limitation one day by using
+ * a buffer pool and chaining, it is not currently
+ * needed for current user/kernel space acceleration
+ */
+ printk("ixp: Cannot handle more than 1 iovec yet !\n");
+ q->ixp_q_crp->crp_etype = ENOENT;
+ goto done;
+ }
+ IX_MBUF_MLEN(&q->ixp_q_mbuf) =
+ IX_MBUF_PKT_LEN(&q->ixp_q_mbuf) = uiop->uio_iov[0].iov_len;
+ IX_MBUF_MDATA(&q->ixp_q_mbuf) = uiop->uio_iov[0].iov_base;
+ } else /* contig buffer */ {
+ IX_MBUF_MLEN(&q->ixp_q_mbuf) =
+ IX_MBUF_PKT_LEN(&q->ixp_q_mbuf) = q->ixp_q_crp->crp_ilen;
+ IX_MBUF_MDATA(&q->ixp_q_mbuf) = q->ixp_q_crp->crp_buf;
+ }
+
+ IX_MBUF_PRIV(&q->ixp_q_mbuf) = q;
+
+ if (ixp->ixp_auth_alg == CRYPTO_SHA1 || ixp->ixp_auth_alg == CRYPTO_MD5) {
+ /*
+ * For SHA1 and MD5 hash, need to create an internal buffer that is big
+ * enough to hold the original data + the appropriate padding for the
+ * hash algorithm.
+ */
+ UINT8 *tbuf = NULL;
+
+ IX_MBUF_MLEN(&q->ixp_q_mbuf) = IX_MBUF_PKT_LEN(&q->ixp_q_mbuf) =
+ ((IX_MBUF_MLEN(&q->ixp_q_mbuf) * 8) + 72 + 511) / 8;
+ tbuf = kmalloc(IX_MBUF_MLEN(&q->ixp_q_mbuf), SLAB_ATOMIC);
+
+ if (IX_MBUF_MDATA(&q->ixp_q_mbuf) == NULL) {
+ printk("ixp: kmalloc(%u, SLAB_ATOMIC) failed\n",
+ IX_MBUF_MLEN(&q->ixp_q_mbuf));
+ q->ixp_q_crp->crp_etype = ENOMEM;
+ goto done;
+ }
+ memcpy(tbuf, &(IX_MBUF_MDATA(&q->ixp_q_mbuf))[auth_off], auth_len);
+
+ /* Set location in client buffer to copy hash into */
+ q->ixp_hash_dest =
+ &(IX_MBUF_MDATA(&q->ixp_q_mbuf))[auth_off + auth_len];
+
+ IX_MBUF_MDATA(&q->ixp_q_mbuf) = tbuf;
+
+ /* Set location in internal buffer for where hash starts */
+ q->ixp_hash_src = &(IX_MBUF_MDATA(&q->ixp_q_mbuf))[auth_len];
+
+ crypt_func = "ixCryptoAccHashPerform";
+ status = ixCryptoAccHashPerform(ixp->ixp_ctx.authCtx.authAlgo,
+ &q->ixp_q_mbuf, ixp_hash_perform_cb, 0, auth_len, auth_len,
+ &ixp->ixp_hash_key_id);
+ }
+ else {
+ crypt_func = "ixCryptoAccAuthCryptPerform";
+ status = ixCryptoAccAuthCryptPerform(ixp->ixp_ctx_id, &q->ixp_q_mbuf,
+ NULL, auth_off, auth_len, crypt_off, crypt_len, icv_off,
+ q->ixp_q_iv);
+ }
+
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS == status)
+ return;
+
+ if (IX_CRYPTO_ACC_STATUS_QUEUE_FULL == status) {
+ q->ixp_q_crp->crp_etype = ENOMEM;
+ goto done;
+ }
+
+ printk("ixp: %s failed %u\n", crypt_func, status);
+ q->ixp_q_crp->crp_etype = EINVAL;
+
+done:
+ crypto_done(q->ixp_q_crp);
+ kmem_cache_free(qcache, q);
+}
+
+
+/*
+ * because we cannot process the Q from the Register callback
+ * we do it here on a task Q.
+ */
+
+static void
+ixp_process_pending(void *arg)
+{
+ struct ixp_data *ixp = arg;
+ struct ixp_q *q = NULL;
+
+ dprintk("%s(%p)\n", __FUNCTION__, arg);
+
+ if (!ixp)
+ return;
+
+ while (!list_empty(&ixp->ixp_q)) {
+ q = list_entry(ixp->ixp_q.next, struct ixp_q, ixp_q_list);
+ list_del(&q->ixp_q_list);
+ ixp_q_process(q);
+ }
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void
+ixp_process_pending_wq(struct work_struct *work)
+{
+ struct ixp_data *ixp = container_of(work, struct ixp_data,
+ ixp_pending_work);
+ ixp_process_pending(ixp);
+}
+#endif
+
+/*
+ * callback for when context registration is complete
+ */
+
+static void
+ixp_register_cb(UINT32 ctx_id, IX_MBUF *bufp, IxCryptoAccStatus status)
+{
+ int i;
+ struct ixp_data *ixp;
+ struct ixp_q *q;
+
+ dprintk("%s(%d, %p, %d)\n", __FUNCTION__, ctx_id, bufp, status);
+
+ /*
+ * free any buffer passed in to this routine
+ */
+ if (bufp) {
+ IX_MBUF_MLEN(bufp) = IX_MBUF_PKT_LEN(bufp) = 0;
+ kfree(IX_MBUF_MDATA(bufp));
+ IX_MBUF_MDATA(bufp) = NULL;
+ }
+
+ for (i = 0; i < ixp_sesnum; i++) {
+ ixp = ixp_sessions[i];
+ if (ixp && ixp->ixp_ctx_id == ctx_id)
+ break;
+ }
+ if (i >= ixp_sesnum) {
+ printk("ixp: invalid context id %d\n", ctx_id);
+ return;
+ }
+
+ if (IX_CRYPTO_ACC_STATUS_WAIT == status) {
+ /* this is normal to free the first of two buffers */
+ dprintk("ixp: register not finished yet.\n");
+ return;
+ }
+
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS != status) {
+ printk("ixp: register failed 0x%x\n", status);
+ while (!list_empty(&ixp->ixp_q)) {
+ q = list_entry(ixp->ixp_q.next, struct ixp_q, ixp_q_list);
+ list_del(&q->ixp_q_list);
+ q->ixp_q_crp->crp_etype = EINVAL;
+ crypto_done(q->ixp_q_crp);
+ kmem_cache_free(qcache, q);
+ }
+ return;
+ }
+
+ /*
+ * we are now registered, we cannot start processing the Q here
+ * or we get strange errors with AES (DES/3DES seem to be ok).
+ */
+ ixp->ixp_registered = 1;
+ schedule_work(&ixp->ixp_pending_work);
+}
+
+
+/*
+ * callback for when data processing is complete
+ */
+
+static void
+ixp_perform_cb(
+ UINT32 ctx_id,
+ IX_MBUF *sbufp,
+ IX_MBUF *dbufp,
+ IxCryptoAccStatus status)
+{
+ struct ixp_q *q;
+
+ dprintk("%s(%d, %p, %p, 0x%x)\n", __FUNCTION__, ctx_id, sbufp,
+ dbufp, status);
+
+ if (sbufp == NULL) {
+ printk("ixp: NULL sbuf in ixp_perform_cb\n");
+ return;
+ }
+
+ q = IX_MBUF_PRIV(sbufp);
+ if (q == NULL) {
+ printk("ixp: NULL priv in ixp_perform_cb\n");
+ return;
+ }
+
+ if (status != IX_CRYPTO_ACC_STATUS_SUCCESS) {
+ printk("ixp: perform failed status=%d\n", status);
+ q->ixp_q_crp->crp_etype = EINVAL;
+ }
+
+ crypto_done(q->ixp_q_crp);
+ kmem_cache_free(qcache, q);
+}
+
+
+/*
+ * registration is not callable at IRQ time, so we defer
+ * to a task queue, this routines completes the registration for us
+ * when the task queue runs
+ *
+ * Unfortunately this means we cannot tell OCF that the driver is blocked,
+ * we do that on the next request.
+ */
+
+static void
+ixp_registration(void *arg)
+{
+ struct ixp_data *ixp = arg;
+ struct ixp_q *q = NULL;
+ IX_MBUF *pri = NULL, *sec = NULL;
+ int status = IX_CRYPTO_ACC_STATUS_SUCCESS;
+
+ if (!ixp) {
+ printk("ixp: ixp_registration with no arg\n");
+ return;
+ }
+
+ if (ixp->ixp_ctx_id != -1) {
+ ixCryptoAccCtxUnregister(ixp->ixp_ctx_id);
+ ixp->ixp_ctx_id = -1;
+ }
+
+ if (list_empty(&ixp->ixp_q)) {
+ printk("ixp: ixp_registration with no Q\n");
+ return;
+ }
+
+ /*
+ * setup the primary and secondary buffers
+ */
+ q = list_entry(ixp->ixp_q.next, struct ixp_q, ixp_q_list);
+ if (q->ixp_q_acrd) {
+ pri = &ixp->ixp_pri_mbuf;
+ sec = &ixp->ixp_sec_mbuf;
+ IX_MBUF_MLEN(pri) = IX_MBUF_PKT_LEN(pri) = 128;
+ IX_MBUF_MDATA(pri) = (unsigned char *) kmalloc(128, SLAB_ATOMIC);
+ IX_MBUF_MLEN(sec) = IX_MBUF_PKT_LEN(sec) = 128;
+ IX_MBUF_MDATA(sec) = (unsigned char *) kmalloc(128, SLAB_ATOMIC);
+ }
+
+ /* Only need to register if a crypt op or HMAC op */
+ if (!(ixp->ixp_auth_alg == CRYPTO_SHA1 ||
+ ixp->ixp_auth_alg == CRYPTO_MD5)) {
+ status = ixCryptoAccCtxRegister(
+ &ixp->ixp_ctx,
+ pri, sec,
+ ixp_register_cb,
+ ixp_perform_cb,
+ &ixp->ixp_ctx_id);
+ }
+ else {
+ /* Otherwise we start processing pending q */
+ schedule_work(&ixp->ixp_pending_work);
+ }
+
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS == status)
+ return;
+
+ if (IX_CRYPTO_ACC_STATUS_EXCEED_MAX_TUNNELS == status) {
+ printk("ixp: ixCryptoAccCtxRegister failed (out of tunnels)\n");
+ ixp_blocked = 1;
+ /* perhaps we should return EGAIN on queued ops ? */
+ return;
+ }
+
+ printk("ixp: ixCryptoAccCtxRegister failed %d\n", status);
+ ixp->ixp_ctx_id = -1;
+
+ /*
+ * everything waiting is toasted
+ */
+ while (!list_empty(&ixp->ixp_q)) {
+ q = list_entry(ixp->ixp_q.next, struct ixp_q, ixp_q_list);
+ list_del(&q->ixp_q_list);
+ q->ixp_q_crp->crp_etype = ENOENT;
+ crypto_done(q->ixp_q_crp);
+ kmem_cache_free(qcache, q);
+ }
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void
+ixp_registration_wq(struct work_struct *work)
+{
+ struct ixp_data *ixp = container_of(work, struct ixp_data,
+ ixp_registration_work);
+ ixp_registration(ixp);
+}
+#endif
+
+/*
+ * Process a request.
+ */
+static int
+ixp_process(device_t dev, struct cryptop *crp, int hint)
+{
+ struct ixp_data *ixp;
+ unsigned int lid;
+ struct ixp_q *q = NULL;
+ int status;
+
+ dprintk("%s()\n", __FUNCTION__);
+
+ /* Sanity check */
+ if (crp == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ crp->crp_etype = 0;
+
+ if (ixp_blocked)
+ return ERESTART;
+
+ if (crp->crp_desc == NULL || crp->crp_buf == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ crp->crp_etype = EINVAL;
+ goto done;
+ }
+
+ /*
+ * find the session we are using
+ */
+
+ lid = crp->crp_sid & 0xffffffff;
+ if (lid >= ixp_sesnum || lid == 0 || ixp_sessions == NULL ||
+ ixp_sessions[lid] == NULL) {
+ crp->crp_etype = ENOENT;
+ dprintk("%s,%d: ENOENT\n", __FILE__, __LINE__);
+ goto done;
+ }
+ ixp = ixp_sessions[lid];
+
+ /*
+ * setup a new request ready for queuing
+ */
+ q = kmem_cache_alloc(qcache, SLAB_ATOMIC);
+ if (q == NULL) {
+ dprintk("%s,%d: ENOMEM\n", __FILE__, __LINE__);
+ crp->crp_etype = ENOMEM;
+ goto done;
+ }
+ /*
+ * save some cycles by only zeroing the important bits
+ */
+ memset(&q->ixp_q_mbuf, 0, sizeof(q->ixp_q_mbuf));
+ q->ixp_q_ccrd = NULL;
+ q->ixp_q_acrd = NULL;
+ q->ixp_q_crp = crp;
+ q->ixp_q_data = ixp;
+
+ /*
+ * point the cipher and auth descriptors appropriately
+ * check that we have something to do
+ */
+ if (crp->crp_desc->crd_alg == ixp->ixp_cipher_alg)
+ q->ixp_q_ccrd = crp->crp_desc;
+ else if (crp->crp_desc->crd_alg == ixp->ixp_auth_alg)
+ q->ixp_q_acrd = crp->crp_desc;
+ else {
+ crp->crp_etype = ENOENT;
+ dprintk("%s,%d: bad desc match: ENOENT\n", __FILE__, __LINE__);
+ goto done;
+ }
+ if (crp->crp_desc->crd_next) {
+ if (crp->crp_desc->crd_next->crd_alg == ixp->ixp_cipher_alg)
+ q->ixp_q_ccrd = crp->crp_desc->crd_next;
+ else if (crp->crp_desc->crd_next->crd_alg == ixp->ixp_auth_alg)
+ q->ixp_q_acrd = crp->crp_desc->crd_next;
+ else {
+ crp->crp_etype = ENOENT;
+ dprintk("%s,%d: bad desc match: ENOENT\n", __FILE__, __LINE__);
+ goto done;
+ }
+ }
+
+ /*
+ * If there is a direction change for this context then we mark it as
+ * unregistered and re-register is for the new direction. This is not
+ * a very expensive operation and currently only tends to happen when
+ * user-space application are doing benchmarks
+ *
+ * DM - we should be checking for pending requests before unregistering.
+ */
+ if (q->ixp_q_ccrd && ixp->ixp_registered &&
+ ixp->ixp_crd_flags != (q->ixp_q_ccrd->crd_flags & CRD_F_ENCRYPT)) {
+ dprintk("%s - detected direction change on session\n", __FUNCTION__);
+ ixp->ixp_registered = 0;
+ }
+
+ /*
+ * if we are registered, call straight into the perform code
+ */
+ if (ixp->ixp_registered) {
+ ixp_q_process(q);
+ return 0;
+ }
+
+ /*
+ * the only part of the context not set in newsession is the direction
+ * dependent parts
+ */
+ if (q->ixp_q_ccrd) {
+ ixp->ixp_crd_flags = (q->ixp_q_ccrd->crd_flags & CRD_F_ENCRYPT);
+ if (q->ixp_q_ccrd->crd_flags & CRD_F_ENCRYPT) {
+ ixp->ixp_ctx.operation = q->ixp_q_acrd ?
+ IX_CRYPTO_ACC_OP_ENCRYPT_AUTH : IX_CRYPTO_ACC_OP_ENCRYPT;
+ } else {
+ ixp->ixp_ctx.operation = q->ixp_q_acrd ?
+ IX_CRYPTO_ACC_OP_AUTH_DECRYPT : IX_CRYPTO_ACC_OP_DECRYPT;
+ }
+ } else {
+ /* q->ixp_q_acrd must be set if we are here */
+ ixp->ixp_ctx.operation = IX_CRYPTO_ACC_OP_AUTH_CALC;
+ }
+
+ status = list_empty(&ixp->ixp_q);
+ list_add_tail(&q->ixp_q_list, &ixp->ixp_q);
+ if (status)
+ schedule_work(&ixp->ixp_registration_work);
+ return 0;
+
+done:
+ if (q)
+ kmem_cache_free(qcache, q);
+ crypto_done(crp);
+ return 0;
+}
+
+
+#ifdef __ixp46X
+/*
+ * key processing support for the ixp465
+ */
+
+
+/*
+ * copy a BN (LE) into a buffer (BE) an fill out the op appropriately
+ * assume zeroed and only copy bits that are significant
+ */
+
+static int
+ixp_copy_ibuf(struct crparam *p, IxCryptoAccPkeEauOperand *op, UINT32 *buf)
+{
+ unsigned char *src = (unsigned char *) p->crp_p;
+ unsigned char *dst;
+ int len, bits = p->crp_nbits;
+
+ dprintk("%s()\n", __FUNCTION__);
+
+ if (bits > MAX_IOP_SIZE * sizeof(UINT32) * 8) {
+ dprintk("%s - ibuf too big (%d > %d)\n", __FUNCTION__,
+ bits, MAX_IOP_SIZE * sizeof(UINT32) * 8);
+ return -1;
+ }
+
+ len = (bits + 31) / 32; /* the number UINT32's needed */
+
+ dst = (unsigned char *) &buf[len];
+ dst--;
+
+ while (bits > 0) {
+ *dst-- = *src++;
+ bits -= 8;
+ }
+
+#if 0 /* no need to zero remaining bits as it is done during request alloc */
+ while (dst > (unsigned char *) buf)
+ *dst-- = '\0';
+#endif
+
+ op->pData = buf;
+ op->dataLen = len;
+ return 0;
+}
+
+/*
+ * copy out the result, be as forgiving as we can about small output buffers
+ */
+
+static int
+ixp_copy_obuf(struct crparam *p, IxCryptoAccPkeEauOpResult *op, UINT32 *buf)
+{
+ unsigned char *dst = (unsigned char *) p->crp_p;
+ unsigned char *src = (unsigned char *) buf;
+ int len, z, bits = p->crp_nbits;
+
+ dprintk("%s()\n", __FUNCTION__);
+
+ len = op->dataLen * sizeof(UINT32);
+
+ /* skip leading zeroes to be small buffer friendly */
+ z = 0;
+ while (z < len && src[z] == '\0')
+ z++;
+
+ src += len;
+ src--;
+ len -= z;
+
+ while (len > 0 && bits > 0) {
+ *dst++ = *src--;
+ len--;
+ bits -= 8;
+ }
+
+ while (bits > 0) {
+ *dst++ = '\0';
+ bits -= 8;
+ }
+
+ if (len > 0) {
+ dprintk("%s - obuf is %d (z=%d, ob=%d) bytes too small\n",
+ __FUNCTION__, len, z, p->crp_nbits / 8);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * the parameter offsets for exp_mod
+ */
+
+#define IXP_PARAM_BASE 0
+#define IXP_PARAM_EXP 1
+#define IXP_PARAM_MOD 2
+#define IXP_PARAM_RES 3
+
+/*
+ * key processing complete callback, is also used to start processing
+ * by passing a NULL for pResult
+ */
+
+static void
+ixp_kperform_cb(
+ IxCryptoAccPkeEauOperation operation,
+ IxCryptoAccPkeEauOpResult *pResult,
+ BOOL carryOrBorrow,
+ IxCryptoAccStatus status)
+{
+ struct ixp_pkq *q, *tmp;
+ unsigned long flags;
+
+ dprintk("%s(0x%x, %p, %d, 0x%x)\n", __FUNCTION__, operation, pResult,
+ carryOrBorrow, status);
+
+ /* handle a completed request */
+ if (pResult) {
+ if (ixp_pk_cur && &ixp_pk_cur->pkq_result == pResult) {
+ q = ixp_pk_cur;
+ if (status != IX_CRYPTO_ACC_STATUS_SUCCESS) {
+ dprintk("%s() - op failed 0x%x\n", __FUNCTION__, status);
+ q->pkq_krp->krp_status = ERANGE; /* could do better */
+ } else {
+ /* copy out the result */
+ if (ixp_copy_obuf(&q->pkq_krp->krp_param[IXP_PARAM_RES],
+ &q->pkq_result, q->pkq_obuf))
+ q->pkq_krp->krp_status = ERANGE;
+ }
+ crypto_kdone(q->pkq_krp);
+ kfree(q);
+ ixp_pk_cur = NULL;
+ } else
+ printk("%s - callback with invalid result pointer\n", __FUNCTION__);
+ }
+
+ spin_lock_irqsave(&ixp_pkq_lock, flags);
+ if (ixp_pk_cur || list_empty(&ixp_pkq)) {
+ spin_unlock_irqrestore(&ixp_pkq_lock, flags);
+ return;
+ }
+
+ list_for_each_entry_safe(q, tmp, &ixp_pkq, pkq_list) {
+
+ list_del(&q->pkq_list);
+ ixp_pk_cur = q;
+
+ spin_unlock_irqrestore(&ixp_pkq_lock, flags);
+
+ status = ixCryptoAccPkeEauPerform(
+ IX_CRYPTO_ACC_OP_EAU_MOD_EXP,
+ &q->pkq_op,
+ ixp_kperform_cb,
+ &q->pkq_result);
+
+ if (status == IX_CRYPTO_ACC_STATUS_SUCCESS) {
+ dprintk("%s() - ixCryptoAccPkeEauPerform SUCCESS\n", __FUNCTION__);
+ return; /* callback will return here for callback */
+ } else if (status == IX_CRYPTO_ACC_STATUS_RETRY) {
+ printk("%s() - ixCryptoAccPkeEauPerform RETRY\n", __FUNCTION__);
+ } else {
+ printk("%s() - ixCryptoAccPkeEauPerform failed %d\n",
+ __FUNCTION__, status);
+ }
+ q->pkq_krp->krp_status = ERANGE; /* could do better */
+ crypto_kdone(q->pkq_krp);
+ kfree(q);
+ spin_lock_irqsave(&ixp_pkq_lock, flags);
+ }
+ spin_unlock_irqrestore(&ixp_pkq_lock, flags);
+}
+
+
+static int
+ixp_kprocess(device_t dev, struct cryptkop *krp, int hint)
+{
+ struct ixp_pkq *q;
+ int rc = 0;
+ unsigned long flags;
+
+ dprintk("%s l1=%d l2=%d l3=%d l4=%d\n", __FUNCTION__,
+ krp->krp_param[IXP_PARAM_BASE].crp_nbits,
+ krp->krp_param[IXP_PARAM_EXP].crp_nbits,
+ krp->krp_param[IXP_PARAM_MOD].crp_nbits,
+ krp->krp_param[IXP_PARAM_RES].crp_nbits);
+
+
+ if (krp->krp_op != CRK_MOD_EXP) {
+ krp->krp_status = EOPNOTSUPP;
+ goto err;
+ }
+
+ q = (struct ixp_pkq *) kmalloc(sizeof(*q), GFP_KERNEL);
+ if (q == NULL) {
+ krp->krp_status = ENOMEM;
+ goto err;
+ }
+
+ /*
+ * The PKE engine does not appear to zero the output buffer
+ * appropriately, so we need to do it all here.
+ */
+ memset(q, 0, sizeof(*q));
+
+ q->pkq_krp = krp;
+ INIT_LIST_HEAD(&q->pkq_list);
+
+ if (ixp_copy_ibuf(&krp->krp_param[IXP_PARAM_BASE], &q->pkq_op.modExpOpr.M,
+ q->pkq_ibuf0))
+ rc = 1;
+ if (!rc && ixp_copy_ibuf(&krp->krp_param[IXP_PARAM_EXP],
+ &q->pkq_op.modExpOpr.e, q->pkq_ibuf1))
+ rc = 2;
+ if (!rc && ixp_copy_ibuf(&krp->krp_param[IXP_PARAM_MOD],
+ &q->pkq_op.modExpOpr.N, q->pkq_ibuf2))
+ rc = 3;
+
+ if (rc) {
+ kfree(q);
+ krp->krp_status = ERANGE;
+ goto err;
+ }
+
+ q->pkq_result.pData = q->pkq_obuf;
+ q->pkq_result.dataLen =
+ (krp->krp_param[IXP_PARAM_RES].crp_nbits + 31) / 32;
+
+ spin_lock_irqsave(&ixp_pkq_lock, flags);
+ list_add_tail(&q->pkq_list, &ixp_pkq);
+ spin_unlock_irqrestore(&ixp_pkq_lock, flags);
+
+ if (!ixp_pk_cur)
+ ixp_kperform_cb(0, NULL, 0, 0);
+ return (0);
+
+err:
+ crypto_kdone(krp);
+ return (0);
+}
+
+
+
+#ifdef CONFIG_OCF_RANDOMHARVEST
+/*
+ * We run the random number generator output through SHA so that it
+ * is FIPS compliant.
+ */
+
+static volatile int sha_done = 0;
+static unsigned char sha_digest[20];
+
+static void
+ixp_hash_cb(UINT8 *digest, IxCryptoAccStatus status)
+{
+ dprintk("%s(%p, %d)\n", __FUNCTION__, digest, status);
+ if (sha_digest != digest)
+ printk("digest error\n");
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS == status)
+ sha_done = 1;
+ else
+ sha_done = -status;
+}
+
+static int
+ixp_read_random(void *arg, u_int32_t *buf, int maxwords)
+{
+ IxCryptoAccStatus status;
+ int i, n, rc;
+
+ dprintk("%s(%p, %d)\n", __FUNCTION__, buf, maxwords);
+ memset(buf, 0, maxwords * sizeof(*buf));
+ status = ixCryptoAccPkePseudoRandomNumberGet(maxwords, buf);
+ if (status != IX_CRYPTO_ACC_STATUS_SUCCESS) {
+ dprintk("%s: ixCryptoAccPkePseudoRandomNumberGet failed %d\n",
+ __FUNCTION__, status);
+ return 0;
+ }
+
+ /*
+ * run the random data through SHA to make it look more random
+ */
+
+ n = sizeof(sha_digest); /* process digest bytes at a time */
+
+ rc = 0;
+ for (i = 0; i < maxwords; i += n / sizeof(*buf)) {
+ if ((maxwords - i) * sizeof(*buf) < n)
+ n = (maxwords - i) * sizeof(*buf);
+ sha_done = 0;
+ status = ixCryptoAccPkeHashPerform(IX_CRYPTO_ACC_AUTH_SHA1,
+ (UINT8 *) &buf[i], n, ixp_hash_cb, sha_digest);
+ if (status != IX_CRYPTO_ACC_STATUS_SUCCESS) {
+ dprintk("ixCryptoAccPkeHashPerform failed %d\n", status);
+ return -EIO;
+ }
+ while (!sha_done)
+ schedule();
+ if (sha_done < 0) {
+ dprintk("ixCryptoAccPkeHashPerform failed CB %d\n", -sha_done);
+ return 0;
+ }
+ memcpy(&buf[i], sha_digest, n);
+ rc += n / sizeof(*buf);;
+ }
+
+ return rc;
+}
+#endif /* CONFIG_OCF_RANDOMHARVEST */
+
+#endif /* __ixp46X */
+
+
+
+/*
+ * our driver startup and shutdown routines
+ */
+
+static int
+ixp_init(void)
+{
+ dprintk("%s(%p)\n", __FUNCTION__, ixp_init);
+
+ if (ixp_init_crypto && ixCryptoAccInit() != IX_CRYPTO_ACC_STATUS_SUCCESS)
+ printk("ixCryptoAccInit failed, assuming already initialised!\n");
+
+ qcache = kmem_cache_create("ixp4xx_q", sizeof(struct ixp_q), 0,
+ SLAB_HWCACHE_ALIGN, NULL
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ , NULL
+#endif
+ );
+ if (!qcache) {
+ printk("failed to create Qcache\n");
+ return -ENOENT;
+ }
+
+ memset(&ixpdev, 0, sizeof(ixpdev));
+ softc_device_init(&ixpdev, "ixp4xx", 0, ixp_methods);
+
+ ixp_id = crypto_get_driverid(softc_get_device(&ixpdev),
+ CRYPTOCAP_F_HARDWARE);
+ if (ixp_id < 0)
+ panic("IXP/OCF crypto device cannot initialize!");
+
+#define REGISTER(alg) \
+ crypto_register(ixp_id,alg,0,0)
+
+ REGISTER(CRYPTO_DES_CBC);
+ REGISTER(CRYPTO_3DES_CBC);
+ REGISTER(CRYPTO_RIJNDAEL128_CBC);
+#ifdef CONFIG_OCF_IXP4XX_SHA1_MD5
+ REGISTER(CRYPTO_MD5);
+ REGISTER(CRYPTO_SHA1);
+#endif
+ REGISTER(CRYPTO_MD5_HMAC);
+ REGISTER(CRYPTO_SHA1_HMAC);
+#undef REGISTER
+
+#ifdef __ixp46X
+ spin_lock_init(&ixp_pkq_lock);
+ /*
+ * we do not enable the go fast options here as they can potentially
+ * allow timing based attacks
+ *
+ * http://www.openssl.org/news/secadv_20030219.txt
+ */
+ ixCryptoAccPkeEauExpConfig(0, 0);
+ crypto_kregister(ixp_id, CRK_MOD_EXP, 0);
+#ifdef CONFIG_OCF_RANDOMHARVEST
+ crypto_rregister(ixp_id, ixp_read_random, NULL);
+#endif
+#endif
+
+ return 0;
+}
+
+static void
+ixp_exit(void)
+{
+ dprintk("%s()\n", __FUNCTION__);
+ crypto_unregister_all(ixp_id);
+ ixp_id = -1;
+ kmem_cache_destroy(qcache);
+ qcache = NULL;
+}
+
+module_init(ixp_init);
+module_exit(ixp_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("David McCullough <dmccullough@cyberguard.com>");
+MODULE_DESCRIPTION("ixp (OCF module for IXP4xx crypto)");
diff --git a/crypto/ocf/ocf-bench.c b/crypto/ocf/ocf-bench.c
new file mode 100644
index 000000000000..5eef6b102eeb
--- /dev/null
+++ b/crypto/ocf/ocf-bench.c
@@ -0,0 +1,436 @@
+/*
+ * A loadable module that benchmarks the OCF crypto speed from kernel space.
+ *
+ * Copyright (C) 2004-2007 David McCullough <david_mccullough@securecomputing.com>
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ */
+
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <cryptodev.h>
+
+#ifdef I_HAVE_AN_XSCALE_WITH_INTEL_SDK
+#define BENCH_IXP_ACCESS_LIB 1
+#endif
+#ifdef BENCH_IXP_ACCESS_LIB
+#include <IxTypes.h>
+#include <IxOsBuffMgt.h>
+#include <IxNpeDl.h>
+#include <IxCryptoAcc.h>
+#include <IxQMgr.h>
+#include <IxOsServices.h>
+#include <IxOsCacheMMU.h>
+#endif
+
+/*
+ * support for access lib version 1.4
+ */
+#ifndef IX_MBUF_PRIV
+#define IX_MBUF_PRIV(x) ((x)->priv)
+#endif
+
+/*
+ * the number of simultaneously active requests
+ */
+static int request_q_len = 20;
+module_param(request_q_len, int, 0);
+MODULE_PARM_DESC(request_q_len, "Number of outstanding requests");
+/*
+ * how many requests we want to have processed
+ */
+static int request_num = 1024;
+module_param(request_num, int, 0);
+MODULE_PARM_DESC(request_num, "run for at least this many requests");
+/*
+ * the size of each request
+ */
+static int request_size = 1500;
+module_param(request_size, int, 0);
+MODULE_PARM_DESC(request_size, "size of each request");
+
+/*
+ * a structure for each request
+ */
+typedef struct {
+ struct work_struct work;
+#ifdef BENCH_IXP_ACCESS_LIB
+ IX_MBUF mbuf;
+#endif
+ unsigned char *buffer;
+} request_t;
+
+static request_t *requests;
+
+static int outstanding;
+static int total;
+
+/*************************************************************************/
+/*
+ * OCF benchmark routines
+ */
+
+static uint64_t ocf_cryptoid;
+static int ocf_init(void);
+static int ocf_cb(struct cryptop *crp);
+static void ocf_request(void *arg);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void ocf_request_wq(struct work_struct *work);
+#endif
+
+static int
+ocf_init(void)
+{
+ int error;
+ struct cryptoini crie, cria;
+ struct cryptodesc crda, crde;
+
+ memset(&crie, 0, sizeof(crie));
+ memset(&cria, 0, sizeof(cria));
+ memset(&crde, 0, sizeof(crde));
+ memset(&crda, 0, sizeof(crda));
+
+ cria.cri_alg = CRYPTO_SHA1_HMAC;
+ cria.cri_klen = 20 * 8;
+ cria.cri_key = "0123456789abcdefghij";
+
+ crie.cri_alg = CRYPTO_3DES_CBC;
+ crie.cri_klen = 24 * 8;
+ crie.cri_key = "0123456789abcdefghijklmn";
+
+ crie.cri_next = &cria;
+
+ error = crypto_newsession(&ocf_cryptoid, &crie, 0);
+ if (error) {
+ printk("crypto_newsession failed %d\n", error);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+ocf_cb(struct cryptop *crp)
+{
+ request_t *r = (request_t *) crp->crp_opaque;
+
+ if (crp->crp_etype)
+ printk("Error in OCF processing: %d\n", crp->crp_etype);
+ total++;
+ crypto_freereq(crp);
+ crp = NULL;
+
+ if (total > request_num) {
+ outstanding--;
+ return 0;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ INIT_WORK(&r->work, ocf_request_wq);
+#else
+ INIT_WORK(&r->work, ocf_request, r);
+#endif
+ schedule_work(&r->work);
+ return 0;
+}
+
+
+static void
+ocf_request(void *arg)
+{
+ request_t *r = arg;
+ struct cryptop *crp = crypto_getreq(2);
+ struct cryptodesc *crde, *crda;
+
+ if (!crp) {
+ outstanding--;
+ return;
+ }
+
+ crde = crp->crp_desc;
+ crda = crde->crd_next;
+
+ crda->crd_skip = 0;
+ crda->crd_flags = 0;
+ crda->crd_len = request_size;
+ crda->crd_inject = request_size;
+ crda->crd_alg = CRYPTO_SHA1_HMAC;
+ crda->crd_key = "0123456789abcdefghij";
+ crda->crd_klen = 20 * 8;
+
+ crde->crd_skip = 0;
+ crde->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_ENCRYPT;
+ crde->crd_len = request_size;
+ crde->crd_inject = request_size;
+ crde->crd_alg = CRYPTO_3DES_CBC;
+ crde->crd_key = "0123456789abcdefghijklmn";
+ crde->crd_klen = 24 * 8;
+
+ crp->crp_ilen = request_size + 64;
+ crp->crp_flags = CRYPTO_F_CBIMM;
+ crp->crp_buf = (caddr_t) r->buffer;
+ crp->crp_callback = ocf_cb;
+ crp->crp_sid = ocf_cryptoid;
+ crp->crp_opaque = (caddr_t) r;
+ crypto_dispatch(crp);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void
+ocf_request_wq(struct work_struct *work)
+{
+ request_t *r = container_of(work, request_t, work);
+ ocf_request(r);
+}
+#endif
+
+/*************************************************************************/
+#ifdef BENCH_IXP_ACCESS_LIB
+/*************************************************************************/
+/*
+ * CryptoAcc benchmark routines
+ */
+
+static IxCryptoAccCtx ixp_ctx;
+static UINT32 ixp_ctx_id;
+static IX_MBUF ixp_pri;
+static IX_MBUF ixp_sec;
+static int ixp_registered = 0;
+
+static void ixp_register_cb(UINT32 ctx_id, IX_MBUF *bufp,
+ IxCryptoAccStatus status);
+static void ixp_perform_cb(UINT32 ctx_id, IX_MBUF *sbufp, IX_MBUF *dbufp,
+ IxCryptoAccStatus status);
+static void ixp_request(void *arg);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void ixp_request_wq(struct work_struct *work);
+#endif
+
+static int
+ixp_init(void)
+{
+ IxCryptoAccStatus status;
+
+ ixp_ctx.cipherCtx.cipherAlgo = IX_CRYPTO_ACC_CIPHER_3DES;
+ ixp_ctx.cipherCtx.cipherMode = IX_CRYPTO_ACC_MODE_CBC;
+ ixp_ctx.cipherCtx.cipherKeyLen = 24;
+ ixp_ctx.cipherCtx.cipherBlockLen = IX_CRYPTO_ACC_DES_BLOCK_64;
+ ixp_ctx.cipherCtx.cipherInitialVectorLen = IX_CRYPTO_ACC_DES_IV_64;
+ memcpy(ixp_ctx.cipherCtx.key.cipherKey, "0123456789abcdefghijklmn", 24);
+
+ ixp_ctx.authCtx.authAlgo = IX_CRYPTO_ACC_AUTH_SHA1;
+ ixp_ctx.authCtx.authDigestLen = 12;
+ ixp_ctx.authCtx.aadLen = 0;
+ ixp_ctx.authCtx.authKeyLen = 20;
+ memcpy(ixp_ctx.authCtx.key.authKey, "0123456789abcdefghij", 20);
+
+ ixp_ctx.useDifferentSrcAndDestMbufs = 0;
+ ixp_ctx.operation = IX_CRYPTO_ACC_OP_ENCRYPT_AUTH ;
+
+ IX_MBUF_MLEN(&ixp_pri) = IX_MBUF_PKT_LEN(&ixp_pri) = 128;
+ IX_MBUF_MDATA(&ixp_pri) = (unsigned char *) kmalloc(128, SLAB_ATOMIC);
+ IX_MBUF_MLEN(&ixp_sec) = IX_MBUF_PKT_LEN(&ixp_sec) = 128;
+ IX_MBUF_MDATA(&ixp_sec) = (unsigned char *) kmalloc(128, SLAB_ATOMIC);
+
+ status = ixCryptoAccCtxRegister(&ixp_ctx, &ixp_pri, &ixp_sec,
+ ixp_register_cb, ixp_perform_cb, &ixp_ctx_id);
+
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS == status) {
+ while (!ixp_registered)
+ schedule();
+ return ixp_registered < 0 ? -1 : 0;
+ }
+
+ printk("ixp: ixCryptoAccCtxRegister failed %d\n", status);
+ return -1;
+}
+
+static void
+ixp_register_cb(UINT32 ctx_id, IX_MBUF *bufp, IxCryptoAccStatus status)
+{
+ if (bufp) {
+ IX_MBUF_MLEN(bufp) = IX_MBUF_PKT_LEN(bufp) = 0;
+ kfree(IX_MBUF_MDATA(bufp));
+ IX_MBUF_MDATA(bufp) = NULL;
+ }
+
+ if (IX_CRYPTO_ACC_STATUS_WAIT == status)
+ return;
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS == status)
+ ixp_registered = 1;
+ else
+ ixp_registered = -1;
+}
+
+static void
+ixp_perform_cb(
+ UINT32 ctx_id,
+ IX_MBUF *sbufp,
+ IX_MBUF *dbufp,
+ IxCryptoAccStatus status)
+{
+ request_t *r = NULL;
+
+ total++;
+ if (total > request_num) {
+ outstanding--;
+ return;
+ }
+
+ if (!sbufp || !(r = IX_MBUF_PRIV(sbufp))) {
+ printk("crappo %p %p\n", sbufp, r);
+ outstanding--;
+ return;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ INIT_WORK(&r->work, ixp_request_wq);
+#else
+ INIT_WORK(&r->work, ixp_request, r);
+#endif
+ schedule_work(&r->work);
+}
+
+static void
+ixp_request(void *arg)
+{
+ request_t *r = arg;
+ IxCryptoAccStatus status;
+
+ memset(&r->mbuf, 0, sizeof(r->mbuf));
+ IX_MBUF_MLEN(&r->mbuf) = IX_MBUF_PKT_LEN(&r->mbuf) = request_size + 64;
+ IX_MBUF_MDATA(&r->mbuf) = r->buffer;
+ IX_MBUF_PRIV(&r->mbuf) = r;
+ status = ixCryptoAccAuthCryptPerform(ixp_ctx_id, &r->mbuf, NULL,
+ 0, request_size, 0, request_size, request_size, r->buffer);
+ if (IX_CRYPTO_ACC_STATUS_SUCCESS != status) {
+ printk("status1 = %d\n", status);
+ outstanding--;
+ return;
+ }
+ return;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+static void
+ixp_request_wq(struct work_struct *work)
+{
+ request_t *r = container_of(work, request_t, work);
+ ixp_request(r);
+}
+#endif
+
+/*************************************************************************/
+#endif /* BENCH_IXP_ACCESS_LIB */
+/*************************************************************************/
+
+int
+ocfbench_init(void)
+{
+ int i, jstart, jstop;
+
+ printk("Crypto Speed tests\n");
+
+ requests = kmalloc(sizeof(request_t) * request_q_len, GFP_KERNEL);
+ if (!requests) {
+ printk("malloc failed\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < request_q_len; i++) {
+ /* +64 for return data */
+ requests[i].buffer = kmalloc(request_size + 128, GFP_DMA);
+ if (!requests[i].buffer) {
+ printk("malloc failed\n");
+ return -EINVAL;
+ }
+ memset(requests[i].buffer, '0' + i, request_size + 128);
+ }
+
+ /*
+ * OCF benchmark
+ */
+ printk("OCF: testing ...\n");
+ ocf_init();
+ total = outstanding = 0;
+ jstart = jiffies;
+ for (i = 0; i < request_q_len; i++) {
+ outstanding++;
+ ocf_request(&requests[i]);
+ }
+ while (outstanding > 0)
+ schedule();
+ jstop = jiffies;
+
+ printk("OCF: %d requests of %d bytes in %d jiffies\n", total, request_size,
+ jstop - jstart);
+
+#ifdef BENCH_IXP_ACCESS_LIB
+ /*
+ * IXP benchmark
+ */
+ printk("IXP: testing ...\n");
+ ixp_init();
+ total = outstanding = 0;
+ jstart = jiffies;
+ for (i = 0; i < request_q_len; i++) {
+ outstanding++;
+ ixp_request(&requests[i]);
+ }
+ while (outstanding > 0)
+ schedule();
+ jstop = jiffies;
+
+ printk("IXP: %d requests of %d bytes in %d jiffies\n", total, request_size,
+ jstop - jstart);
+#endif /* BENCH_IXP_ACCESS_LIB */
+
+ for (i = 0; i < request_q_len; i++)
+ kfree(requests[i].buffer);
+ kfree(requests);
+ return -EINVAL; /* always fail to load so it can be re-run quickly ;-) */
+}
+
+static void __exit ocfbench_exit(void)
+{
+}
+
+module_init(ocfbench_init);
+module_exit(ocfbench_exit);
+
+MODULE_LICENSE("BSD");
+MODULE_AUTHOR("David McCullough <david_mccullough@securecomputing.com>");
+MODULE_DESCRIPTION("Benchmark various in-kernel crypto speeds");
diff --git a/crypto/ocf/ocf-compat.h b/crypto/ocf/ocf-compat.h
new file mode 100644
index 000000000000..fb50b41e9f8f
--- /dev/null
+++ b/crypto/ocf/ocf-compat.h
@@ -0,0 +1,270 @@
+#ifndef _BSD_COMPAT_H_
+#define _BSD_COMPAT_H_ 1
+/****************************************************************************/
+/*
+ * Provide compat routines for older linux kernels and BSD kernels
+ *
+ * Written by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2007 David McCullough <david_mccullough@securecomputing.com>
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this file
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ */
+/****************************************************************************/
+#ifdef __KERNEL__
+/*
+ * fake some BSD driver interface stuff specifically for OCF use
+ */
+
+typedef struct ocf_device *device_t;
+
+typedef struct {
+ int (*cryptodev_newsession)(device_t dev, u_int32_t *sidp, struct cryptoini *cri);
+ int (*cryptodev_freesession)(device_t dev, u_int64_t tid);
+ int (*cryptodev_process)(device_t dev, struct cryptop *crp, int hint);
+ int (*cryptodev_kprocess)(device_t dev, struct cryptkop *krp, int hint);
+} device_method_t;
+#define DEVMETHOD(id, func) id: func
+
+struct ocf_device {
+ char name[32]; /* the driver name */
+ char nameunit[32]; /* the driver name + HW instance */
+ int unit;
+ device_method_t methods;
+ void *softc;
+};
+
+#define CRYPTODEV_NEWSESSION(dev, sid, cri) \
+ ((*(dev)->methods.cryptodev_newsession)(dev,sid,cri))
+#define CRYPTODEV_FREESESSION(dev, sid) \
+ ((*(dev)->methods.cryptodev_freesession)(dev, sid))
+#define CRYPTODEV_PROCESS(dev, crp, hint) \
+ ((*(dev)->methods.cryptodev_process)(dev, crp, hint))
+#define CRYPTODEV_KPROCESS(dev, krp, hint) \
+ ((*(dev)->methods.cryptodev_kprocess)(dev, krp, hint))
+
+#define device_get_name(dev) ((dev)->name)
+#define device_get_nameunit(dev) ((dev)->nameunit)
+#define device_get_unit(dev) ((dev)->unit)
+#define device_get_softc(dev) ((dev)->softc)
+
+#define softc_device_decl \
+ struct ocf_device _device; \
+ device_t
+
+#define softc_device_init(_sc, _name, _unit, _methods) \
+ if (1) {\
+ strncpy((_sc)->_device.name, _name, sizeof((_sc)->_device.name) - 1); \
+ snprintf((_sc)->_device.nameunit, sizeof((_sc)->_device.name), "%s%d", _name, _unit); \
+ (_sc)->_device.unit = _unit; \
+ (_sc)->_device.methods = _methods; \
+ (_sc)->_device.softc = (void *) _sc; \
+ *(device_t *)((softc_get_device(_sc))+1) = &(_sc)->_device; \
+ } else
+
+#define softc_get_device(_sc) (&(_sc)->_device)
+
+/*
+ * iomem support for 2.4 and 2.6 kernels
+ */
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define ocf_iomem_t unsigned long
+
+/*
+ * implement simple workqueue like support for older kernels
+ */
+
+#include <linux/tqueue.h>
+
+#define work_struct tq_struct
+
+#define INIT_WORK(wp, fp, ap) \
+ do { \
+ (wp)->sync = 0; \
+ (wp)->routine = (fp); \
+ (wp)->data = (ap); \
+ } while (0)
+
+#define schedule_work(wp) \
+ do { \
+ queue_task((wp), &tq_immediate); \
+ mark_bh(IMMEDIATE_BH); \
+ } while (0)
+
+#define flush_scheduled_work() run_task_queue(&tq_immediate)
+
+#else
+#define ocf_iomem_t void __iomem *
+
+#include <linux/workqueue.h>
+
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+#include <linux/fdtable.h>
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+#define files_fdtable(files) (files)
+#endif
+
+#ifdef MODULE_PARM
+#undef module_param /* just in case */
+#define module_param(a,b,c) MODULE_PARM(a,"i")
+#endif
+
+#define bzero(s,l) memset(s,0,l)
+#define bcopy(s,d,l) memcpy(d,s,l)
+#define bcmp(x, y, l) memcmp(x,y,l)
+
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
+#define device_printf(dev, a...) ({ \
+ printk("%s: ", device_get_nameunit(dev)); printk(a); \
+ })
+
+#undef printf
+#define printf(fmt...) printk(fmt)
+
+#define KASSERT(c,p) if (!(c)) { printk p ; } else
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define ocf_daemonize(str) \
+ daemonize(); \
+ spin_lock_irq(&current->sigmask_lock); \
+ sigemptyset(&current->blocked); \
+ recalc_sigpending(current); \
+ spin_unlock_irq(&current->sigmask_lock); \
+ sprintf(current->comm, str);
+#else
+#define ocf_daemonize(str) daemonize(str);
+#endif
+
+#define TAILQ_INSERT_TAIL(q,d,m) list_add_tail(&(d)->m, (q))
+#define TAILQ_EMPTY(q) list_empty(q)
+#define TAILQ_FOREACH(v, q, m) list_for_each_entry(v, q, m)
+
+#define read_random(p,l) get_random_bytes(p,l)
+
+#define DELAY(x) ((x) > 2000 ? mdelay((x)/1000) : udelay(x))
+#define strtoul simple_strtoul
+
+#define pci_get_vendor(dev) ((dev)->vendor)
+#define pci_get_device(dev) ((dev)->device)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#define pci_set_consistent_dma_mask(dev, mask) (0)
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
+#define pci_dma_sync_single_for_cpu pci_dma_sync_single
+#endif
+
+#ifndef DMA_32BIT_MASK
+#define DMA_32BIT_MASK 0x00000000ffffffffULL
+#endif
+
+#define htole32(x) cpu_to_le32(x)
+#define htobe32(x) cpu_to_be32(x)
+#define htole16(x) cpu_to_le16(x)
+#define htobe16(x) cpu_to_be16(x)
+
+/* older kernels don't have these */
+
+#ifndef IRQ_NONE
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define irqreturn_t void
+#endif
+#ifndef IRQF_SHARED
+#define IRQF_SHARED SA_SHIRQ
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+# define strlcpy(dest,src,len) \
+ ({strncpy(dest,src,(len)-1); ((char *)dest)[(len)-1] = '\0'; })
+#endif
+
+#ifndef MAX_ERRNO
+#define MAX_ERRNO 4095
+#endif
+#ifndef IS_ERR_VALUE
+#define IS_ERR_VALUE(x) ((unsigned long)(x) >= (unsigned long)-MAX_ERRNO)
+#endif
+
+/*
+ * common debug for all
+ */
+#if 1
+#define dprintk(a...) do { if (debug) printk(a); } while(0)
+#else
+#define dprintk(a...)
+#endif
+
+#ifndef SLAB_ATOMIC
+/* Changed in 2.6.20, must use GFP_ATOMIC now */
+#define SLAB_ATOMIC GFP_ATOMIC
+#endif
+
+/*
+ * need some additional support for older kernels */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,2)
+#define pci_register_driver_compat(driver, rc) \
+ do { \
+ if ((rc) > 0) { \
+ (rc) = 0; \
+ } else if (rc == 0) { \
+ (rc) = -ENODEV; \
+ } else { \
+ pci_unregister_driver(driver); \
+ } \
+ } while (0)
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
+#define pci_register_driver_compat(driver,rc) ((rc) = (rc) < 0 ? (rc) : 0)
+#else
+#define pci_register_driver_compat(driver,rc)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+
+#include <asm/scatterlist.h>
+
+static inline void sg_set_page(struct scatterlist *sg, struct page *page,
+ unsigned int len, unsigned int offset)
+{
+ sg->page = page;
+ sg->offset = offset;
+ sg->length = len;
+}
+
+static inline void *sg_virt(struct scatterlist *sg)
+{
+ return page_address(sg->page) + sg->offset;
+}
+
+#endif
+
+#endif /* __KERNEL__ */
+
+/****************************************************************************/
+#endif /* _BSD_COMPAT_H_ */
diff --git a/crypto/ocf/ocfnull/Makefile b/crypto/ocf/ocfnull/Makefile
new file mode 100644
index 000000000000..044bcacb7bce
--- /dev/null
+++ b/crypto/ocf/ocfnull/Makefile
@@ -0,0 +1,12 @@
+# for SGlinux builds
+-include $(ROOTDIR)/modules/.config
+
+obj-$(CONFIG_OCF_OCFNULL) += ocfnull.o
+
+obj ?= .
+EXTRA_CFLAGS += -I$(obj)/..
+
+ifdef TOPDIR
+-include $(TOPDIR)/Rules.make
+endif
+
diff --git a/crypto/ocf/ocfnull/ocfnull.c b/crypto/ocf/ocfnull/ocfnull.c
new file mode 100644
index 000000000000..b4ad84c044f5
--- /dev/null
+++ b/crypto/ocf/ocfnull/ocfnull.c
@@ -0,0 +1,203 @@
+/*
+ * An OCF module for determining the cost of crypto versus the cost of
+ * IPSec processing outside of OCF. This modules gives us the effect of
+ * zero cost encryption, of course you will need to run it at both ends
+ * since it does no crypto at all.
+ *
+ * Written by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2006-2007 David McCullough
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ */
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/crypto.h>
+#include <linux/interrupt.h>
+
+#include <cryptodev.h>
+#include <uio.h>
+
+static int32_t null_id = -1;
+static u_int32_t null_sesnum = 0;
+
+static int null_process(device_t, struct cryptop *, int);
+static int null_newsession(device_t, u_int32_t *, struct cryptoini *);
+static int null_freesession(device_t, u_int64_t);
+
+#define debug ocfnull_debug
+int ocfnull_debug = 0;
+module_param(ocfnull_debug, int, 0644);
+MODULE_PARM_DESC(ocfnull_debug, "Enable debug");
+
+/*
+ * dummy device structure
+ */
+
+static struct {
+ softc_device_decl sc_dev;
+} nulldev;
+
+static device_method_t null_methods = {
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, null_newsession),
+ DEVMETHOD(cryptodev_freesession,null_freesession),
+ DEVMETHOD(cryptodev_process, null_process),
+};
+
+/*
+ * Generate a new software session.
+ */
+static int
+null_newsession(device_t arg, u_int32_t *sid, struct cryptoini *cri)
+{
+ dprintk("%s()\n", __FUNCTION__);
+ if (sid == NULL || cri == NULL) {
+ dprintk("%s,%d - EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ if (null_sesnum == 0)
+ null_sesnum++;
+ *sid = null_sesnum++;
+ return 0;
+}
+
+
+/*
+ * Free a session.
+ */
+static int
+null_freesession(device_t arg, u_int64_t tid)
+{
+ u_int32_t sid = CRYPTO_SESID2LID(tid);
+
+ dprintk("%s()\n", __FUNCTION__);
+ if (sid > null_sesnum) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ /* Silently accept and return */
+ if (sid == 0)
+ return 0;
+ return 0;
+}
+
+
+/*
+ * Process a request.
+ */
+static int
+null_process(device_t arg, struct cryptop *crp, int hint)
+{
+ unsigned int lid;
+
+ dprintk("%s()\n", __FUNCTION__);
+
+ /* Sanity check */
+ if (crp == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+
+ crp->crp_etype = 0;
+
+ if (crp->crp_desc == NULL || crp->crp_buf == NULL) {
+ dprintk("%s,%d: EINVAL\n", __FILE__, __LINE__);
+ crp->crp_etype = EINVAL;
+ goto done;
+ }
+
+ /*
+ * find the session we are using
+ */
+
+ lid = crp->crp_sid & 0xffffffff;
+ if (lid >= null_sesnum || lid == 0) {
+ crp->crp_etype = ENOENT;
+ dprintk("%s,%d: ENOENT\n", __FILE__, __LINE__);
+ goto done;
+ }
+
+done:
+ crypto_done(crp);
+ return 0;
+}
+
+
+/*
+ * our driver startup and shutdown routines
+ */
+
+static int
+null_init(void)
+{
+ dprintk("%s(%p)\n", __FUNCTION__, null_init);
+
+ memset(&nulldev, 0, sizeof(nulldev));
+ softc_device_init(&nulldev, "ocfnull", 0, null_methods);
+
+ null_id = crypto_get_driverid(softc_get_device(&nulldev),
+ CRYPTOCAP_F_HARDWARE);
+ if (null_id < 0)
+ panic("ocfnull: crypto device cannot initialize!");
+
+#define REGISTER(alg) \
+ crypto_register(null_id,alg,0,0)
+ REGISTER(CRYPTO_DES_CBC);
+ REGISTER(CRYPTO_3DES_CBC);
+ REGISTER(CRYPTO_RIJNDAEL128_CBC);
+ REGISTER(CRYPTO_MD5);
+ REGISTER(CRYPTO_SHA1);
+ REGISTER(CRYPTO_MD5_HMAC);
+ REGISTER(CRYPTO_SHA1_HMAC);
+#undef REGISTER
+
+ return 0;
+}
+
+static void
+null_exit(void)
+{
+ dprintk("%s()\n", __FUNCTION__);
+ crypto_unregister_all(null_id);
+ null_id = -1;
+}
+
+module_init(null_init);
+module_exit(null_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("David McCullough <david_mccullough@securecomputing.com>");
+MODULE_DESCRIPTION("ocfnull - claims a lot but does nothing");
diff --git a/crypto/ocf/pasemi/Makefile b/crypto/ocf/pasemi/Makefile
new file mode 100644
index 000000000000..b0a3980f219e
--- /dev/null
+++ b/crypto/ocf/pasemi/Makefile
@@ -0,0 +1,12 @@
+# for SGlinux builds
+-include $(ROOTDIR)/modules/.config
+
+obj-$(CONFIG_OCF_PASEMI) += pasemi.o
+
+obj ?= .
+EXTRA_CFLAGS += -I$(obj)/.. -I$(obj)/
+
+ifdef TOPDIR
+-include $(TOPDIR)/Rules.make
+endif
+
diff --git a/crypto/ocf/pasemi/pasemi.c b/crypto/ocf/pasemi/pasemi.c
new file mode 100644
index 000000000000..c3bb9313d33b
--- /dev/null
+++ b/crypto/ocf/pasemi/pasemi.c
@@ -0,0 +1,1009 @@
+/*
+ * Copyright (C) 2007 PA Semi, Inc
+ *
+ * Driver for the PA Semi PWRficient DMA Crypto Engine
+ *
+ * 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 AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <asm/scatterlist.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <cryptodev.h>
+#include <uio.h>
+#include "pasemi_fnu.h"
+
+#define DRV_NAME "pasemi"
+
+#define TIMER_INTERVAL 1000
+
+static void __devexit pasemi_dma_remove(struct pci_dev *pdev);
+static struct pasdma_status volatile * dma_status;
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable debug");
+
+static void pasemi_desc_start(struct pasemi_desc *desc, u64 hdr)
+{
+ desc->postop = 0;
+ desc->quad[0] = hdr;
+ desc->quad_cnt = 1;
+ desc->size = 1;
+}
+
+static void pasemi_desc_build(struct pasemi_desc *desc, u64 val)
+{
+ desc->quad[desc->quad_cnt++] = val;
+ desc->size = (desc->quad_cnt + 1) / 2;
+}
+
+static void pasemi_desc_hdr(struct pasemi_desc *desc, u64 hdr)
+{
+ desc->quad[0] |= hdr;
+}
+
+static int pasemi_desc_size(struct pasemi_desc *desc)
+{
+ return desc->size;
+}
+
+static void pasemi_ring_add_desc(
+ struct pasemi_fnu_txring *ring,
+ struct pasemi_desc *desc,
+ struct cryptop *crp) {
+ int i;
+ int ring_index = 2 * (ring->next_to_fill & (TX_RING_SIZE-1));
+
+ TX_DESC_INFO(ring, ring->next_to_fill).desc_size = desc->size;
+ TX_DESC_INFO(ring, ring->next_to_fill).desc_postop = desc->postop;
+ TX_DESC_INFO(ring, ring->next_to_fill).cf_crp = crp;
+
+ for (i = 0; i < desc->quad_cnt; i += 2) {
+ ring_index = 2 * (ring->next_to_fill & (TX_RING_SIZE-1));
+ ring->desc[ring_index] = desc->quad[i];
+ ring->desc[ring_index + 1] = desc->quad[i + 1];
+ ring->next_to_fill++;
+ }
+
+ if (desc->quad_cnt & 1)
+ ring->desc[ring_index + 1] = 0;
+}
+
+static void pasemi_ring_incr(struct pasemi_softc *sc, int chan_index, int incr)
+{
+ out_le32(sc->dma_regs + PAS_DMA_TXCHAN_INCR(sc->base_chan + chan_index),
+ incr);
+}
+
+/*
+ * Generate a new software session.
+ */
+static int
+pasemi_newsession(device_t dev, u_int32_t *sidp, struct cryptoini *cri)
+{
+ struct cryptoini *c, *encini = NULL, *macini = NULL;
+ struct pasemi_softc *sc = device_get_softc(dev);
+ struct pasemi_session *ses = NULL, **sespp;
+ int sesn, blksz = 0;
+ u64 ccmd = 0;
+ unsigned long flags;
+ struct pasemi_desc init_desc;
+ struct pasemi_fnu_txring *txring;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+ if (sidp == NULL || cri == NULL || sc == NULL) {
+ DPRINTF("%s,%d - EINVAL\n", __FILE__, __LINE__);
+ return -EINVAL;
+ }
+ for (c = cri; c != NULL; c = c->cri_next) {
+ if (ALG_IS_SIG(c->cri_alg)) {
+ if (macini)
+ return -EINVAL;
+ macini = c;
+ } else if (ALG_IS_CIPHER(c->cri_alg)) {
+ if (encini)
+ return -EINVAL;
+ encini = c;
+ } else {
+ DPRINTF("UNKNOWN c->cri_alg %d\n", c->cri_alg);
+ return -EINVAL;
+ }
+ }
+ if (encini == NULL && macini == NULL)
+ return -EINVAL;
+ if (encini) {
+ /* validate key length */
+ switch (encini->cri_alg) {
+ case CRYPTO_DES_CBC:
+ if (encini->cri_klen != 64)
+ return -EINVAL;
+ ccmd = DMA_CALGO_DES;
+ break;
+ case CRYPTO_3DES_CBC:
+ if (encini->cri_klen != 192)
+ return -EINVAL;
+ ccmd = DMA_CALGO_3DES;
+ break;
+ case CRYPTO_AES_CBC:
+ if (encini->cri_klen != 128 &&
+ encini->cri_klen != 192 &&
+ encini->cri_klen != 256)
+ return -EINVAL;
+ ccmd = DMA_CALGO_AES;
+ break;
+ case CRYPTO_ARC4:
+ if (encini->cri_klen != 128)
+ return -EINVAL;
+ ccmd = DMA_CALGO_ARC;
+ break;
+ default:
+ DPRINTF("UNKNOWN encini->cri_alg %d\n",
+ encini->cri_alg);
+ return -EINVAL;
+ }
+ }
+
+ if (macini) {
+ switch (macini->cri_alg) {
+ case CRYPTO_MD5:
+ case CRYPTO_MD5_HMAC:
+ blksz = 16;
+ break;
+ case CRYPTO_SHA1:
+ case CRYPTO_SHA1_HMAC:
+ blksz = 20;
+ break;
+ default:
+ DPRINTF("UNKNOWN macini->cri_alg %d\n",
+ macini->cri_alg);
+ return -EINVAL;
+ }
+ if (((macini->cri_klen + 7) / 8) > blksz) {
+ DPRINTF("key length %d bigger than blksize %d not supported\n",
+ ((macini->cri_klen + 7) / 8), blksz);
+ return -EINVAL;
+ }
+ }
+
+ for (sesn = 0; sesn < sc->sc_nsessions; sesn++) {
+ if (sc->sc_sessions[sesn] == NULL) {
+ sc->sc_sessions[sesn] = (struct pasemi_session *)
+ kzalloc(sizeof(struct pasemi_session), GFP_ATOMIC);
+ ses = sc->sc_sessions[sesn];
+ break;
+ } else if (sc->sc_sessions[sesn]->used == 0) {
+ ses = sc->sc_sessions[sesn];
+ break;
+ }
+ }
+
+ if (ses == NULL) {
+ sespp = (struct pasemi_session **)
+ kzalloc(sc->sc_nsessions * 2 *
+ sizeof(struct pasemi_session *), GFP_ATOMIC);
+ if (sespp == NULL)
+ return -ENOMEM;
+ memcpy(sespp, sc->sc_sessions,
+ sc->sc_nsessions * sizeof(struct pasemi_session *));
+ kfree(sc->sc_sessions);
+ sc->sc_sessions = sespp;
+ sesn = sc->sc_nsessions;
+ ses = sc->sc_sessions[sesn] = (struct pasemi_session *)
+ kzalloc(sizeof(struct pasemi_session), GFP_ATOMIC);
+ if (ses == NULL)
+ return -ENOMEM;
+ sc->sc_nsessions *= 2;
+ }
+
+ ses->used = 1;
+
+ ses->dma_addr = pci_map_single(sc->dma_pdev, (void *) ses->civ,
+ sizeof(struct pasemi_session), DMA_TO_DEVICE);
+
+ /* enter the channel scheduler */
+ spin_lock_irqsave(&sc->sc_chnlock, flags);
+
+ /* ARC4 has to be processed by the even channel */
+ if (encini && (encini->cri_alg == CRYPTO_ARC4))
+ ses->chan = sc->sc_lastchn & ~1;
+ else
+ ses->chan = sc->sc_lastchn;
+ sc->sc_lastchn = (sc->sc_lastchn + 1) % sc->sc_num_channels;
+
+ spin_unlock_irqrestore(&sc->sc_chnlock, flags);
+
+ txring = &sc->tx[ses->chan];
+
+ if (encini) {
+ ses->ccmd = ccmd;
+
+ /* get an IV */
+ /* XXX may read fewer than requested */
+ get_random_bytes(ses->civ, sizeof(ses->civ));
+
+ ses->keysz = (encini->cri_klen - 63) / 64;
+ memcpy(ses->key, encini->cri_key, (ses->keysz + 1) * 8);
+
+ pasemi_desc_start(&init_desc,
+ XCT_CTRL_HDR(ses->chan, (encini && macini) ? 0x68 : 0x40, DMA_FN_CIV0));
+ pasemi_desc_build(&init_desc,
+ XCT_FUN_SRC_PTR((encini && macini) ? 0x68 : 0x40, ses->dma_addr));
+ }
+ if (macini) {
+ if (macini->cri_alg == CRYPTO_MD5_HMAC ||
+ macini->cri_alg == CRYPTO_SHA1_HMAC)
+ memcpy(ses->hkey, macini->cri_key, blksz);
+ else {
+ /* Load initialization constants(RFC 1321, 3174) */
+ ses->hiv[0] = 0x67452301efcdab89ULL;
+ ses->hiv[1] = 0x98badcfe10325476ULL;
+ ses->hiv[2] = 0xc3d2e1f000000000ULL;
+ }
+ ses->hseq = 0ULL;
+ }
+
+ spin_lock_irqsave(&txring->fill_lock, flags);
+
+ if (((txring->next_to_fill + pasemi_desc_size(&init_desc)) -
+ txring->next_to_clean) > TX_RING_SIZE) {
+ spin_unlock_irqrestore(&txring->fill_lock, flags);
+ return ERESTART;
+ }
+
+ if (encini) {
+ pasemi_ring_add_desc(txring, &init_desc, NULL);
+ pasemi_ring_incr(sc, ses->chan,
+ pasemi_desc_size(&init_desc));
+ }
+
+ txring->sesn = sesn;
+ spin_unlock_irqrestore(&txring->fill_lock, flags);
+
+ *sidp = PASEMI_SID(sesn);
+ return 0;
+}
+
+/*
+ * Deallocate a session.
+ */
+static int
+pasemi_freesession(device_t dev, u_int64_t tid)
+{
+ struct pasemi_softc *sc = device_get_softc(dev);
+ int session;
+ u_int32_t sid = ((u_int32_t) tid) & 0xffffffff;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ if (sc == NULL)
+ return -EINVAL;
+ session = PASEMI_SESSION(sid);
+ if (session >= sc->sc_nsessions || !sc->sc_sessions[session])
+ return -EINVAL;
+
+ pci_unmap_single(sc->dma_pdev,
+ sc->sc_sessions[session]->dma_addr,
+ sizeof(struct pasemi_session), DMA_TO_DEVICE);
+ memset(sc->sc_sessions[session], 0,
+ sizeof(struct pasemi_session));
+
+ return 0;
+}
+
+static int
+pasemi_process(device_t dev, struct cryptop *crp, int hint)
+{
+
+ int err = 0, ivsize, srclen = 0, reinit = 0, reinit_size = 0, chsel;
+ struct pasemi_softc *sc = device_get_softc(dev);
+ struct cryptodesc *crd1, *crd2, *maccrd, *enccrd;
+ caddr_t ivp;
+ struct pasemi_desc init_desc, work_desc;
+ struct pasemi_session *ses;
+ struct sk_buff *skb;
+ struct uio *uiop;
+ unsigned long flags;
+ struct pasemi_fnu_txring *txring;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ if (crp == NULL || crp->crp_callback == NULL || sc == NULL)
+ return -EINVAL;
+
+ crp->crp_etype = 0;
+ if (PASEMI_SESSION(crp->crp_sid) >= sc->sc_nsessions)
+ return -EINVAL;
+
+ ses = sc->sc_sessions[PASEMI_SESSION(crp->crp_sid)];
+
+ crd1 = crp->crp_desc;
+ if (crd1 == NULL) {
+ err = -EINVAL;
+ goto errout;
+ }
+ crd2 = crd1->crd_next;
+
+ if (ALG_IS_SIG(crd1->crd_alg)) {
+ maccrd = crd1;
+ if (crd2 == NULL)
+ enccrd = NULL;
+ else if (ALG_IS_CIPHER(crd2->crd_alg) &&
+ (crd2->crd_flags & CRD_F_ENCRYPT) == 0)
+ enccrd = crd2;
+ else
+ goto erralg;
+ } else if (ALG_IS_CIPHER(crd1->crd_alg)) {
+ enccrd = crd1;
+ if (crd2 == NULL)
+ maccrd = NULL;
+ else if (ALG_IS_SIG(crd2->crd_alg) &&
+ (crd1->crd_flags & CRD_F_ENCRYPT))
+ maccrd = crd2;
+ else
+ goto erralg;
+ } else
+ goto erralg;
+
+ chsel = ses->chan;
+
+ txring = &sc->tx[chsel];
+
+ if (enccrd && !maccrd) {
+ if (enccrd->crd_alg == CRYPTO_ARC4)
+ reinit = 1;
+ reinit_size = 0x40;
+ srclen = crp->crp_ilen;
+
+ pasemi_desc_start(&work_desc, XCT_FUN_O | XCT_FUN_I
+ | XCT_FUN_FUN(chsel));
+ if (enccrd->crd_flags & CRD_F_ENCRYPT)
+ pasemi_desc_hdr(&work_desc, XCT_FUN_CRM_ENC);
+ else
+ pasemi_desc_hdr(&work_desc, XCT_FUN_CRM_DEC);
+ } else if (enccrd && maccrd) {
+ if (enccrd->crd_alg == CRYPTO_ARC4)
+ reinit = 1;
+ reinit_size = 0x68;
+
+ if (enccrd->crd_flags & CRD_F_ENCRYPT) {
+ /* Encrypt -> Authenticate */
+ pasemi_desc_start(&work_desc, XCT_FUN_O | XCT_FUN_I | XCT_FUN_CRM_ENC_SIG
+ | XCT_FUN_A | XCT_FUN_FUN(chsel));
+ srclen = maccrd->crd_skip + maccrd->crd_len;
+ } else {
+ /* Authenticate -> Decrypt */
+ pasemi_desc_start(&work_desc, XCT_FUN_O | XCT_FUN_I | XCT_FUN_CRM_SIG_DEC
+ | XCT_FUN_24BRES | XCT_FUN_FUN(chsel));
+ pasemi_desc_build(&work_desc, 0);
+ pasemi_desc_build(&work_desc, 0);
+ pasemi_desc_build(&work_desc, 0);
+ work_desc.postop = PASEMI_CHECK_SIG;
+ srclen = crp->crp_ilen;
+ }
+
+ pasemi_desc_hdr(&work_desc, XCT_FUN_SHL(maccrd->crd_skip / 4));
+ pasemi_desc_hdr(&work_desc, XCT_FUN_CHL(enccrd->crd_skip - maccrd->crd_skip));
+ } else if (!enccrd && maccrd) {
+ srclen = maccrd->crd_len;
+
+ pasemi_desc_start(&init_desc,
+ XCT_CTRL_HDR(chsel, 0x58, DMA_FN_HKEY0));
+ pasemi_desc_build(&init_desc,
+ XCT_FUN_SRC_PTR(0x58, ((struct pasemi_session *)ses->dma_addr)->hkey));
+
+ pasemi_desc_start(&work_desc, XCT_FUN_O | XCT_FUN_I | XCT_FUN_CRM_SIG
+ | XCT_FUN_A | XCT_FUN_FUN(chsel));
+ }
+
+ if (enccrd) {
+ switch (enccrd->crd_alg) {
+ case CRYPTO_3DES_CBC:
+ pasemi_desc_hdr(&work_desc, XCT_FUN_ALG_3DES |
+ XCT_FUN_BCM_CBC);
+ ivsize = sizeof(u64);
+ break;
+ case CRYPTO_DES_CBC:
+ pasemi_desc_hdr(&work_desc, XCT_FUN_ALG_DES |
+ XCT_FUN_BCM_CBC);
+ ivsize = sizeof(u64);
+ break;
+ case CRYPTO_AES_CBC:
+ pasemi_desc_hdr(&work_desc, XCT_FUN_ALG_AES |
+ XCT_FUN_BCM_CBC);
+ ivsize = 2 * sizeof(u64);
+ break;
+ case CRYPTO_ARC4:
+ pasemi_desc_hdr(&work_desc, XCT_FUN_ALG_ARC);
+ ivsize = 0;
+ break;
+ default:
+ printk(DRV_NAME ": unimplemented enccrd->crd_alg %d\n",
+ enccrd->crd_alg);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ ivp = (ivsize == sizeof(u64)) ? (caddr_t) &ses->civ[1] : (caddr_t) &ses->civ[0];
+ if (enccrd->crd_flags & CRD_F_ENCRYPT) {
+ if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
+ memcpy(ivp, enccrd->crd_iv, ivsize);
+ /* If IV is not present in the buffer already, it has to be copied there */
+ if ((enccrd->crd_flags & CRD_F_IV_PRESENT) == 0)
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ enccrd->crd_inject, ivsize, ivp);
+ } else {
+ if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
+ /* IV is provided expicitly in descriptor */
+ memcpy(ivp, enccrd->crd_iv, ivsize);
+ else
+ /* IV is provided in the packet */
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ enccrd->crd_inject, ivsize,
+ ivp);
+ }
+ }
+
+ if (maccrd) {
+ switch (maccrd->crd_alg) {
+ case CRYPTO_MD5:
+ pasemi_desc_hdr(&work_desc, XCT_FUN_SIG_MD5 |
+ XCT_FUN_HSZ((crp->crp_ilen - maccrd->crd_inject) / 4));
+ break;
+ case CRYPTO_SHA1:
+ pasemi_desc_hdr(&work_desc, XCT_FUN_SIG_SHA1 |
+ XCT_FUN_HSZ((crp->crp_ilen - maccrd->crd_inject) / 4));
+ break;
+ case CRYPTO_MD5_HMAC:
+ pasemi_desc_hdr(&work_desc, XCT_FUN_SIG_HMAC_MD5 |
+ XCT_FUN_HSZ((crp->crp_ilen - maccrd->crd_inject) / 4));
+ break;
+ case CRYPTO_SHA1_HMAC:
+ pasemi_desc_hdr(&work_desc, XCT_FUN_SIG_HMAC_SHA1 |
+ XCT_FUN_HSZ((crp->crp_ilen - maccrd->crd_inject) / 4));
+ break;
+ default:
+ printk(DRV_NAME ": unimplemented maccrd->crd_alg %d\n",
+ maccrd->crd_alg);
+ err = -EINVAL;
+ goto errout;
+ }
+ }
+
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ /* using SKB buffers */
+ skb = (struct sk_buff *)crp->crp_buf;
+ if (skb_shinfo(skb)->nr_frags) {
+ printk(DRV_NAME ": skb frags unimplemented\n");
+ err = -EINVAL;
+ goto errout;
+ }
+ pasemi_desc_build(
+ &work_desc,
+ XCT_FUN_DST_PTR(skb->len, pci_map_single(
+ sc->dma_pdev, skb->data,
+ skb->len, DMA_TO_DEVICE)));
+ pasemi_desc_build(
+ &work_desc,
+ XCT_FUN_SRC_PTR(
+ srclen, pci_map_single(
+ sc->dma_pdev, skb->data,
+ srclen, DMA_TO_DEVICE)));
+ pasemi_desc_hdr(&work_desc, XCT_FUN_LLEN(srclen));
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ /* using IOV buffers */
+ uiop = (struct uio *)crp->crp_buf;
+ if (uiop->uio_iovcnt > 1) {
+ printk(DRV_NAME ": iov frags unimplemented\n");
+ err = -EINVAL;
+ goto errout;
+ }
+
+ /* crp_olen is never set; always use crp_ilen */
+ pasemi_desc_build(
+ &work_desc,
+ XCT_FUN_DST_PTR(crp->crp_ilen, pci_map_single(
+ sc->dma_pdev,
+ uiop->uio_iov->iov_base,
+ crp->crp_ilen, DMA_TO_DEVICE)));
+ pasemi_desc_hdr(&work_desc, XCT_FUN_LLEN(srclen));
+
+ pasemi_desc_build(
+ &work_desc,
+ XCT_FUN_SRC_PTR(srclen, pci_map_single(
+ sc->dma_pdev,
+ uiop->uio_iov->iov_base,
+ srclen, DMA_TO_DEVICE)));
+ } else {
+ /* using contig buffers */
+ pasemi_desc_build(
+ &work_desc,
+ XCT_FUN_DST_PTR(crp->crp_ilen, pci_map_single(
+ sc->dma_pdev,
+ crp->crp_buf,
+ crp->crp_ilen, DMA_TO_DEVICE)));
+ pasemi_desc_build(
+ &work_desc,
+ XCT_FUN_SRC_PTR(srclen, pci_map_single(
+ sc->dma_pdev,
+ crp->crp_buf, srclen,
+ DMA_TO_DEVICE)));
+ pasemi_desc_hdr(&work_desc, XCT_FUN_LLEN(srclen));
+ }
+
+ spin_lock_irqsave(&txring->fill_lock, flags);
+
+ if (txring->sesn != PASEMI_SESSION(crp->crp_sid)) {
+ txring->sesn = PASEMI_SESSION(crp->crp_sid);
+ reinit = 1;
+ }
+
+ if (enccrd) {
+ pasemi_desc_start(&init_desc,
+ XCT_CTRL_HDR(chsel, reinit ? reinit_size : 0x10, DMA_FN_CIV0));
+ pasemi_desc_build(&init_desc,
+ XCT_FUN_SRC_PTR(reinit ? reinit_size : 0x10, ses->dma_addr));
+ }
+
+ if (((txring->next_to_fill + pasemi_desc_size(&init_desc) +
+ pasemi_desc_size(&work_desc)) -
+ txring->next_to_clean) > TX_RING_SIZE) {
+ spin_unlock_irqrestore(&txring->fill_lock, flags);
+ err = ERESTART;
+ goto errout;
+ }
+
+ pasemi_ring_add_desc(txring, &init_desc, NULL);
+ pasemi_ring_add_desc(txring, &work_desc, crp);
+
+ pasemi_ring_incr(sc, chsel,
+ pasemi_desc_size(&init_desc) +
+ pasemi_desc_size(&work_desc));
+
+ spin_unlock_irqrestore(&txring->fill_lock, flags);
+
+ mod_timer(&txring->crypto_timer, jiffies + TIMER_INTERVAL);
+
+ return 0;
+
+erralg:
+ printk(DRV_NAME ": unsupported algorithm or algorithm order alg1 %d alg2 %d\n",
+ crd1->crd_alg, crd2->crd_alg);
+ err = -EINVAL;
+
+errout:
+ if (err != ERESTART) {
+ crp->crp_etype = err;
+ crypto_done(crp);
+ }
+ return err;
+}
+
+static int pasemi_clean_tx(struct pasemi_softc *sc, int chan)
+{
+ int i, j, ring_idx;
+ struct pasemi_fnu_txring *ring = &sc->tx[chan];
+ u16 delta_cnt;
+ int flags, loops = 10;
+ int desc_size;
+ struct cryptop *crp;
+
+ spin_lock_irqsave(&ring->clean_lock, flags);
+
+ while ((delta_cnt = (dma_status->tx_sta[sc->base_chan + chan]
+ & PAS_STATUS_PCNT_M) - ring->total_pktcnt)
+ && loops--) {
+
+ for (i = 0; i < delta_cnt; i++) {
+ desc_size = TX_DESC_INFO(ring, ring->next_to_clean).desc_size;
+ crp = TX_DESC_INFO(ring, ring->next_to_clean).cf_crp;
+ if (crp) {
+ ring_idx = 2 * (ring->next_to_clean & (TX_RING_SIZE-1));
+ if (TX_DESC_INFO(ring, ring->next_to_clean).desc_postop & PASEMI_CHECK_SIG) {
+ /* Need to make sure signature matched,
+ * if not - return error */
+ if (!(ring->desc[ring_idx + 1] & (1ULL << 63)))
+ crp->crp_etype = -EINVAL;
+ }
+ crypto_done(TX_DESC_INFO(ring,
+ ring->next_to_clean).cf_crp);
+ TX_DESC_INFO(ring, ring->next_to_clean).cf_crp = NULL;
+ pci_unmap_single(
+ sc->dma_pdev,
+ XCT_PTR_ADDR_LEN(ring->desc[ring_idx + 1]),
+ PCI_DMA_TODEVICE);
+
+ ring->desc[ring_idx] = ring->desc[ring_idx + 1] = 0;
+
+ ring->next_to_clean++;
+ for (j = 1; j < desc_size; j++) {
+ ring_idx = 2 *
+ (ring->next_to_clean &
+ (TX_RING_SIZE-1));
+ pci_unmap_single(
+ sc->dma_pdev,
+ XCT_PTR_ADDR_LEN(ring->desc[ring_idx]),
+ PCI_DMA_TODEVICE);
+ if (ring->desc[ring_idx + 1])
+ pci_unmap_single(
+ sc->dma_pdev,
+ XCT_PTR_ADDR_LEN(
+ ring->desc[
+ ring_idx + 1]),
+ PCI_DMA_TODEVICE);
+ ring->desc[ring_idx] =
+ ring->desc[ring_idx + 1] = 0;
+ ring->next_to_clean++;
+ }
+ } else {
+ for (j = 0; j < desc_size; j++) {
+ ring_idx = 2 * (ring->next_to_clean & (TX_RING_SIZE-1));
+ ring->desc[ring_idx] =
+ ring->desc[ring_idx + 1] = 0;
+ ring->next_to_clean++;
+ }
+ }
+ }
+
+ ring->total_pktcnt += delta_cnt;
+ }
+ spin_unlock_irqrestore(&ring->clean_lock, flags);
+
+ return 0;
+}
+
+static void sweepup_tx(struct pasemi_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sc->sc_num_channels; i++)
+ pasemi_clean_tx(sc, i);
+}
+
+static irqreturn_t pasemi_intr(int irq, void *arg, struct pt_regs *regs)
+{
+ struct pasemi_softc *sc = arg;
+ unsigned int reg;
+ int chan = irq - sc->base_irq;
+ int chan_index = sc->base_chan + chan;
+ u64 stat = dma_status->tx_sta[chan_index];
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ if (!(stat & PAS_STATUS_CAUSE_M))
+ return IRQ_NONE;
+
+ pasemi_clean_tx(sc, chan);
+
+ stat = dma_status->tx_sta[chan_index];
+
+ reg = PAS_IOB_DMA_TXCH_RESET_PINTC |
+ PAS_IOB_DMA_TXCH_RESET_PCNT(sc->tx[chan].total_pktcnt);
+
+ if (stat & PAS_STATUS_SOFT)
+ reg |= PAS_IOB_DMA_RXCH_RESET_SINTC;
+
+ out_le32(sc->iob_regs + PAS_IOB_DMA_TXCH_RESET(chan_index), reg);
+
+
+ return IRQ_HANDLED;
+}
+
+static int pasemi_dma_setup_tx_resources(struct pasemi_softc *sc, int chan)
+{
+ u32 val;
+ int chan_index = chan + sc->base_chan;
+ int ret;
+ struct pasemi_fnu_txring *ring;
+
+ ring = &sc->tx[chan];
+
+ spin_lock_init(&ring->fill_lock);
+ spin_lock_init(&ring->clean_lock);
+
+ ring->desc_info = kzalloc(sizeof(struct pasemi_desc_info) *
+ TX_RING_SIZE, GFP_KERNEL);
+ if (!ring->desc_info)
+ return -ENOMEM;
+
+ /* Allocate descriptors */
+ ring->desc = dma_alloc_coherent(&sc->dma_pdev->dev,
+ TX_RING_SIZE *
+ 2 * sizeof(u64),
+ &ring->dma, GFP_KERNEL);
+ if (!ring->desc)
+ return -ENOMEM;
+
+ memset((void *) ring->desc, 0, TX_RING_SIZE * 2 * sizeof(u64));
+
+ out_le32(sc->iob_regs + PAS_IOB_DMA_TXCH_RESET(chan_index), 0x30);
+
+ ring->total_pktcnt = 0;
+
+ out_le32(sc->dma_regs + PAS_DMA_TXCHAN_BASEL(chan_index),
+ PAS_DMA_TXCHAN_BASEL_BRBL(ring->dma));
+
+ val = PAS_DMA_TXCHAN_BASEU_BRBH(ring->dma >> 32);
+ val |= PAS_DMA_TXCHAN_BASEU_SIZ(TX_RING_SIZE >> 2);
+
+ out_le32(sc->dma_regs + PAS_DMA_TXCHAN_BASEU(chan_index), val);
+
+ out_le32(sc->dma_regs + PAS_DMA_TXCHAN_CFG(chan_index),
+ PAS_DMA_TXCHAN_CFG_TY_FUNC |
+ PAS_DMA_TXCHAN_CFG_TATTR(chan) |
+ PAS_DMA_TXCHAN_CFG_WT(2));
+
+ /* enable tx channel */
+ out_le32(sc->dma_regs +
+ PAS_DMA_TXCHAN_TCMDSTA(chan_index),
+ PAS_DMA_TXCHAN_TCMDSTA_EN);
+
+ out_le32(sc->iob_regs + PAS_IOB_DMA_TXCH_CFG(chan_index),
+ PAS_IOB_DMA_TXCH_CFG_CNTTH(1000));
+
+ ring->next_to_fill = 0;
+ ring->next_to_clean = 0;
+
+ snprintf(ring->irq_name, sizeof(ring->irq_name),
+ "%s%d", "crypto", chan);
+
+ ring->irq = irq_create_mapping(NULL, sc->base_irq + chan);
+ ret = request_irq(ring->irq, (irq_handler_t)
+ pasemi_intr, IRQF_DISABLED, ring->irq_name, sc);
+ if (ret) {
+ printk(KERN_ERR DRV_NAME ": failed to hook irq %d ret %d\n",
+ ring->irq, ret);
+ ring->irq = -1;
+ return ret;
+ }
+
+ setup_timer(&ring->crypto_timer, (void *) sweepup_tx, (unsigned long) sc);
+
+ return 0;
+}
+
+static device_method_t pasemi_methods = {
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, pasemi_newsession),
+ DEVMETHOD(cryptodev_freesession, pasemi_freesession),
+ DEVMETHOD(cryptodev_process, pasemi_process),
+};
+
+/* Set up the crypto device structure, private data,
+ * and anything else we need before we start */
+
+static int __devinit
+pasemi_dma_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct pasemi_softc *sc;
+ int ret, i;
+
+ DPRINTF(KERN_ERR "%s()\n", __FUNCTION__);
+
+ sc = kzalloc(sizeof(*sc), GFP_KERNEL);
+ if (!sc)
+ return -ENOMEM;
+
+ softc_device_init(sc, DRV_NAME, 1, pasemi_methods);
+
+ pci_set_drvdata(pdev, sc);
+
+ spin_lock_init(&sc->sc_chnlock);
+
+ sc->sc_sessions = (struct pasemi_session **)
+ kzalloc(PASEMI_INITIAL_SESSIONS *
+ sizeof(struct pasemi_session *), GFP_ATOMIC);
+ if (sc->sc_sessions == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ sc->sc_nsessions = PASEMI_INITIAL_SESSIONS;
+ sc->sc_lastchn = 0;
+ sc->base_irq = pdev->irq + 6;
+ sc->base_chan = 6;
+ sc->sc_cid = -1;
+ sc->dma_pdev = pdev;
+
+ sc->iob_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa001, NULL);
+ if (!sc->iob_pdev) {
+ dev_err(&pdev->dev, "Can't find I/O Bridge\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* This is hardcoded and ugly, but we have some firmware versions
+ * who don't provide the register space in the device tree. Luckily
+ * they are at well-known locations so we can just do the math here.
+ */
+ sc->dma_regs =
+ ioremap(0xe0000000 + (sc->dma_pdev->devfn << 12), 0x2000);
+ sc->iob_regs =
+ ioremap(0xe0000000 + (sc->iob_pdev->devfn << 12), 0x2000);
+ if (!sc->dma_regs || !sc->iob_regs) {
+ dev_err(&pdev->dev, "Can't map registers\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ dma_status = __ioremap(0xfd800000, 0x1000, 0);
+ if (!dma_status) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "Can't map dmastatus space\n");
+ goto out;
+ }
+
+ sc->tx = (struct pasemi_fnu_txring *)
+ kzalloc(sizeof(struct pasemi_fnu_txring)
+ * 8, GFP_KERNEL);
+ if (!sc->tx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Initialize the h/w */
+ out_le32(sc->dma_regs + PAS_DMA_COM_CFG,
+ (in_le32(sc->dma_regs + PAS_DMA_COM_CFG) |
+ PAS_DMA_COM_CFG_FWF));
+ out_le32(sc->dma_regs + PAS_DMA_COM_TXCMD, PAS_DMA_COM_TXCMD_EN);
+
+ for (i = 0; i < PASEMI_FNU_CHANNELS; i++) {
+ sc->sc_num_channels++;
+ ret = pasemi_dma_setup_tx_resources(sc, i);
+ if (ret)
+ goto out;
+ }
+
+ sc->sc_cid = crypto_get_driverid(softc_get_device(sc),
+ CRYPTOCAP_F_HARDWARE);
+ if (sc->sc_cid < 0) {
+ printk(KERN_ERR DRV_NAME ": could not get crypto driver id\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ /* register algorithms with the framework */
+ printk(DRV_NAME ":");
+
+ crypto_register(sc->sc_cid, CRYPTO_DES_CBC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_ARC4, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_SHA1, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_MD5, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0);
+
+ return 0;
+
+out:
+ pasemi_dma_remove(pdev);
+ return ret;
+}
+
+#define MAX_RETRIES 5000
+
+static void pasemi_free_tx_resources(struct pasemi_softc *sc, int chan)
+{
+ struct pasemi_fnu_txring *ring = &sc->tx[chan];
+ int chan_index = chan + sc->base_chan;
+ int retries;
+ u32 stat;
+
+ /* Stop the channel */
+ out_le32(sc->dma_regs +
+ PAS_DMA_TXCHAN_TCMDSTA(chan_index),
+ PAS_DMA_TXCHAN_TCMDSTA_ST);
+
+ for (retries = 0; retries < MAX_RETRIES; retries++) {
+ stat = in_le32(sc->dma_regs +
+ PAS_DMA_TXCHAN_TCMDSTA(chan_index));
+ if (!(stat & PAS_DMA_TXCHAN_TCMDSTA_ACT))
+ break;
+ cond_resched();
+ }
+
+ if (stat & PAS_DMA_TXCHAN_TCMDSTA_ACT)
+ dev_err(&sc->dma_pdev->dev, "Failed to stop tx channel %d\n",
+ chan_index);
+
+ /* Disable the channel */
+ out_le32(sc->dma_regs +
+ PAS_DMA_TXCHAN_TCMDSTA(chan_index),
+ 0);
+
+ if (ring->desc_info)
+ kfree((void *) ring->desc_info);
+ if (ring->desc)
+ dma_free_coherent(&sc->dma_pdev->dev,
+ TX_RING_SIZE *
+ 2 * sizeof(u64),
+ (void *) ring->desc, ring->dma);
+ if (ring->irq != -1)
+ free_irq(ring->irq, sc);
+
+ del_timer(&ring->crypto_timer);
+}
+
+static void __devexit pasemi_dma_remove(struct pci_dev *pdev)
+{
+ struct pasemi_softc *sc = pci_get_drvdata(pdev);
+ int i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ if (sc->sc_cid >= 0) {
+ crypto_unregister_all(sc->sc_cid);
+ }
+
+ if (sc->tx) {
+ for (i = 0; i < sc->sc_num_channels; i++)
+ pasemi_free_tx_resources(sc, i);
+
+ kfree(sc->tx);
+ }
+ if (sc->sc_sessions) {
+ for (i = 0; i < sc->sc_nsessions; i++)
+ kfree(sc->sc_sessions[i]);
+ kfree(sc->sc_sessions);
+ }
+ if (sc->iob_pdev)
+ pci_dev_put(sc->iob_pdev);
+ if (sc->dma_regs)
+ iounmap(sc->dma_regs);
+ if (sc->iob_regs)
+ iounmap(sc->iob_regs);
+ kfree(sc);
+}
+
+static struct pci_device_id pasemi_dma_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_PASEMI, 0xa007) },
+};
+
+MODULE_DEVICE_TABLE(pci, pasemi_dma_pci_tbl);
+
+static struct pci_driver pasemi_dma_driver = {
+ .name = "pasemi_dma",
+ .id_table = pasemi_dma_pci_tbl,
+ .probe = pasemi_dma_probe,
+ .remove = __devexit_p(pasemi_dma_remove),
+};
+
+static void __exit pasemi_dma_cleanup_module(void)
+{
+ pci_unregister_driver(&pasemi_dma_driver);
+ __iounmap(dma_status);
+ dma_status = NULL;
+}
+
+int pasemi_dma_init_module(void)
+{
+ return pci_register_driver(&pasemi_dma_driver);
+}
+
+module_init(pasemi_dma_init_module);
+module_exit(pasemi_dma_cleanup_module);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Egor Martovetsky egor@pasemi.com");
+MODULE_DESCRIPTION("OCF driver for PA Semi PWRficient DMA Crypto Engine");
diff --git a/crypto/ocf/pasemi/pasemi_fnu.h b/crypto/ocf/pasemi/pasemi_fnu.h
new file mode 100644
index 000000000000..1a0dcc8bbd37
--- /dev/null
+++ b/crypto/ocf/pasemi/pasemi_fnu.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2007 PA Semi, Inc
+ *
+ * Driver for the PA Semi PWRficient DMA Crypto Engine, soft state and
+ * hardware register layouts.
+ *
+ * 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 PASEMI_FNU_H
+#define PASEMI_FNU_H
+
+#include <linux/spinlock.h>
+
+#define PASEMI_SESSION(sid) ((sid) & 0xffffffff)
+#define PASEMI_SID(sesn) ((sesn) & 0xffffffff)
+#define DPRINTF(a...) if (debug) { printk(DRV_NAME ": " a); }
+
+/* Must be a power of two */
+#define RX_RING_SIZE 512
+#define TX_RING_SIZE 512
+#define TX_DESC(ring, num) ((ring)->desc[2 * (num & (TX_RING_SIZE-1))])
+#define TX_DESC_INFO(ring, num) ((ring)->desc_info[(num) & (TX_RING_SIZE-1)])
+#define MAX_DESC_SIZE 8
+#define PASEMI_INITIAL_SESSIONS 10
+#define PASEMI_FNU_CHANNELS 8
+
+/* DMA descriptor */
+struct pasemi_desc {
+ u64 quad[2*MAX_DESC_SIZE];
+ int quad_cnt;
+ int size;
+ int postop;
+};
+
+/*
+ * Holds per descriptor data
+ */
+struct pasemi_desc_info {
+ int desc_size;
+ int desc_postop;
+#define PASEMI_CHECK_SIG 0x1
+
+ struct cryptop *cf_crp;
+};
+
+/*
+ * Holds per channel data
+ */
+struct pasemi_fnu_txring {
+ volatile u64 *desc;
+ volatile struct
+ pasemi_desc_info *desc_info;
+ dma_addr_t dma;
+ struct timer_list crypto_timer;
+ spinlock_t fill_lock;
+ spinlock_t clean_lock;
+ unsigned int next_to_fill;
+ unsigned int next_to_clean;
+ u16 total_pktcnt;
+ int irq;
+ int sesn;
+ char irq_name[10];
+};
+
+/*
+ * Holds data specific to a single pasemi device.
+ */
+struct pasemi_softc {
+ softc_device_decl sc_cdev;
+ struct pci_dev *dma_pdev; /* device backpointer */
+ struct pci_dev *iob_pdev; /* device backpointer */
+ void __iomem *dma_regs;
+ void __iomem *iob_regs;
+ int base_irq;
+ int base_chan;
+ int32_t sc_cid; /* crypto tag */
+ int sc_nsessions;
+ struct pasemi_session **sc_sessions;
+ int sc_num_channels;/* number of crypto channels */
+
+ /* pointer to the array of txring datastructures, one txring per channel */
+ struct pasemi_fnu_txring *tx;
+
+ /*
+ * mutual exclusion for the channel scheduler
+ */
+ spinlock_t sc_chnlock;
+ /* last channel used, for now use round-robin to allocate channels */
+ int sc_lastchn;
+};
+
+struct pasemi_session {
+ u64 civ[2];
+ u64 keysz;
+ u64 key[4];
+ u64 ccmd;
+ u64 hkey[4];
+ u64 hseq;
+ u64 giv[2];
+ u64 hiv[4];
+
+ int used;
+ dma_addr_t dma_addr;
+ int chan;
+};
+
+/* status register layout in IOB region, at 0xfd800000 */
+struct pasdma_status {
+ u64 rx_sta[64];
+ u64 tx_sta[20];
+};
+
+#define ALG_IS_CIPHER(alg) ((alg == CRYPTO_DES_CBC) || \
+ (alg == CRYPTO_3DES_CBC) || \
+ (alg == CRYPTO_AES_CBC) || \
+ (alg == CRYPTO_ARC4) || \
+ (alg == CRYPTO_NULL_CBC))
+
+#define ALG_IS_SIG(alg) ((alg == CRYPTO_MD5) || \
+ (alg == CRYPTO_MD5_HMAC) || \
+ (alg == CRYPTO_SHA1) || \
+ (alg == CRYPTO_SHA1_HMAC) || \
+ (alg == CRYPTO_NULL_HMAC))
+
+enum {
+ PAS_DMA_COM_TXCMD = 0x100, /* Transmit Command Register */
+ PAS_DMA_COM_TXSTA = 0x104, /* Transmit Status Register */
+ PAS_DMA_COM_RXCMD = 0x108, /* Receive Command Register */
+ PAS_DMA_COM_RXSTA = 0x10c, /* Receive Status Register */
+ PAS_DMA_COM_CFG = 0x114, /* DMA Configuration Register */
+};
+
+/* All these registers live in the PCI configuration space for the DMA PCI
+ * device. Use the normal PCI config access functions for them.
+ */
+
+#define PAS_DMA_COM_CFG_FWF 0x18000000
+
+#define PAS_DMA_COM_TXCMD_EN 0x00000001 /* enable */
+#define PAS_DMA_COM_TXSTA_ACT 0x00000001 /* active */
+#define PAS_DMA_COM_RXCMD_EN 0x00000001 /* enable */
+#define PAS_DMA_COM_RXSTA_ACT 0x00000001 /* active */
+
+#define _PAS_DMA_TXCHAN_STRIDE 0x20 /* Size per channel */
+#define _PAS_DMA_TXCHAN_TCMDSTA 0x300 /* Command / Status */
+#define _PAS_DMA_TXCHAN_CFG 0x304 /* Configuration */
+#define _PAS_DMA_TXCHAN_DSCRBU 0x308 /* Descriptor BU Allocation */
+#define _PAS_DMA_TXCHAN_INCR 0x310 /* Descriptor increment */
+#define _PAS_DMA_TXCHAN_CNT 0x314 /* Descriptor count/offset */
+#define _PAS_DMA_TXCHAN_BASEL 0x318 /* Descriptor ring base (low) */
+#define _PAS_DMA_TXCHAN_BASEU 0x31c /* (high) */
+#define PAS_DMA_TXCHAN_TCMDSTA(c) (0x300+(c)*_PAS_DMA_TXCHAN_STRIDE)
+#define PAS_DMA_TXCHAN_TCMDSTA_EN 0x00000001 /* Enabled */
+#define PAS_DMA_TXCHAN_TCMDSTA_ST 0x00000002 /* Stop interface */
+#define PAS_DMA_TXCHAN_TCMDSTA_ACT 0x00010000 /* Active */
+#define PAS_DMA_TXCHAN_CFG(c) (0x304+(c)*_PAS_DMA_TXCHAN_STRIDE)
+#define PAS_DMA_TXCHAN_CFG_TY_FUNC 0x00000002 /* Type = interface */
+#define PAS_DMA_TXCHAN_CFG_TY_IFACE 0x00000000 /* Type = interface */
+#define PAS_DMA_TXCHAN_CFG_TATTR_M 0x0000003c
+#define PAS_DMA_TXCHAN_CFG_TATTR_S 2
+#define PAS_DMA_TXCHAN_CFG_TATTR(x) (((x) << PAS_DMA_TXCHAN_CFG_TATTR_S) & \
+ PAS_DMA_TXCHAN_CFG_TATTR_M)
+#define PAS_DMA_TXCHAN_CFG_WT_M 0x000001c0
+#define PAS_DMA_TXCHAN_CFG_WT_S 6
+#define PAS_DMA_TXCHAN_CFG_WT(x) (((x) << PAS_DMA_TXCHAN_CFG_WT_S) & \
+ PAS_DMA_TXCHAN_CFG_WT_M)
+#define PAS_DMA_TXCHAN_CFG_LPSQ_FAST 0x00000400
+#define PAS_DMA_TXCHAN_CFG_LPDQ_FAST 0x00000800
+#define PAS_DMA_TXCHAN_CFG_CF 0x00001000 /* Clean first line */
+#define PAS_DMA_TXCHAN_CFG_CL 0x00002000 /* Clean last line */
+#define PAS_DMA_TXCHAN_CFG_UP 0x00004000 /* update tx descr when sent */
+#define PAS_DMA_TXCHAN_INCR(c) (0x310+(c)*_PAS_DMA_TXCHAN_STRIDE)
+#define PAS_DMA_TXCHAN_BASEL(c) (0x318+(c)*_PAS_DMA_TXCHAN_STRIDE)
+#define PAS_DMA_TXCHAN_BASEL_BRBL_M 0xffffffc0
+#define PAS_DMA_TXCHAN_BASEL_BRBL_S 0
+#define PAS_DMA_TXCHAN_BASEL_BRBL(x) (((x) << PAS_DMA_TXCHAN_BASEL_BRBL_S) & \
+ PAS_DMA_TXCHAN_BASEL_BRBL_M)
+#define PAS_DMA_TXCHAN_BASEU(c) (0x31c+(c)*_PAS_DMA_TXCHAN_STRIDE)
+#define PAS_DMA_TXCHAN_BASEU_BRBH_M 0x00000fff
+#define PAS_DMA_TXCHAN_BASEU_BRBH_S 0
+#define PAS_DMA_TXCHAN_BASEU_BRBH(x) (((x) << PAS_DMA_TXCHAN_BASEU_BRBH_S) & \
+ PAS_DMA_TXCHAN_BASEU_BRBH_M)
+/* # of cache lines worth of buffer ring */
+#define PAS_DMA_TXCHAN_BASEU_SIZ_M 0x3fff0000
+#define PAS_DMA_TXCHAN_BASEU_SIZ_S 16 /* 0 = 16K */
+#define PAS_DMA_TXCHAN_BASEU_SIZ(x) (((x) << PAS_DMA_TXCHAN_BASEU_SIZ_S) & \
+ PAS_DMA_TXCHAN_BASEU_SIZ_M)
+
+#define PAS_STATUS_PCNT_M 0x000000000000ffffull
+#define PAS_STATUS_PCNT_S 0
+#define PAS_STATUS_DCNT_M 0x00000000ffff0000ull
+#define PAS_STATUS_DCNT_S 16
+#define PAS_STATUS_BPCNT_M 0x0000ffff00000000ull
+#define PAS_STATUS_BPCNT_S 32
+#define PAS_STATUS_CAUSE_M 0xf000000000000000ull
+#define PAS_STATUS_TIMER 0x1000000000000000ull
+#define PAS_STATUS_ERROR 0x2000000000000000ull
+#define PAS_STATUS_SOFT 0x4000000000000000ull
+#define PAS_STATUS_INT 0x8000000000000000ull
+
+#define PAS_IOB_DMA_RXCH_CFG(i) (0x1100 + (i)*4)
+#define PAS_IOB_DMA_RXCH_CFG_CNTTH_M 0x00000fff
+#define PAS_IOB_DMA_RXCH_CFG_CNTTH_S 0
+#define PAS_IOB_DMA_RXCH_CFG_CNTTH(x) (((x) << PAS_IOB_DMA_RXCH_CFG_CNTTH_S) & \
+ PAS_IOB_DMA_RXCH_CFG_CNTTH_M)
+#define PAS_IOB_DMA_TXCH_CFG(i) (0x1200 + (i)*4)
+#define PAS_IOB_DMA_TXCH_CFG_CNTTH_M 0x00000fff
+#define PAS_IOB_DMA_TXCH_CFG_CNTTH_S 0
+#define PAS_IOB_DMA_TXCH_CFG_CNTTH(x) (((x) << PAS_IOB_DMA_TXCH_CFG_CNTTH_S) & \
+ PAS_IOB_DMA_TXCH_CFG_CNTTH_M)
+#define PAS_IOB_DMA_RXCH_STAT(i) (0x1300 + (i)*4)
+#define PAS_IOB_DMA_RXCH_STAT_INTGEN 0x00001000
+#define PAS_IOB_DMA_RXCH_STAT_CNTDEL_M 0x00000fff
+#define PAS_IOB_DMA_RXCH_STAT_CNTDEL_S 0
+#define PAS_IOB_DMA_RXCH_STAT_CNTDEL(x) (((x) << PAS_IOB_DMA_RXCH_STAT_CNTDEL_S) &\
+ PAS_IOB_DMA_RXCH_STAT_CNTDEL_M)
+#define PAS_IOB_DMA_TXCH_STAT(i) (0x1400 + (i)*4)
+#define PAS_IOB_DMA_TXCH_STAT_INTGEN 0x00001000
+#define PAS_IOB_DMA_TXCH_STAT_CNTDEL_M 0x00000fff
+#define PAS_IOB_DMA_TXCH_STAT_CNTDEL_S 0
+#define PAS_IOB_DMA_TXCH_STAT_CNTDEL(x) (((x) << PAS_IOB_DMA_TXCH_STAT_CNTDEL_S) &\
+ PAS_IOB_DMA_TXCH_STAT_CNTDEL_M)
+#define PAS_IOB_DMA_RXCH_RESET(i) (0x1500 + (i)*4)
+#define PAS_IOB_DMA_RXCH_RESET_PCNT_M 0xffff0000
+#define PAS_IOB_DMA_RXCH_RESET_PCNT_S 16
+#define PAS_IOB_DMA_RXCH_RESET_PCNT(x) (((x) << PAS_IOB_DMA_RXCH_RESET_PCNT_S) & \
+ PAS_IOB_DMA_RXCH_RESET_PCNT_M)
+#define PAS_IOB_DMA_RXCH_RESET_PCNTRST 0x00000020
+#define PAS_IOB_DMA_RXCH_RESET_DCNTRST 0x00000010
+#define PAS_IOB_DMA_RXCH_RESET_TINTC 0x00000008
+#define PAS_IOB_DMA_RXCH_RESET_DINTC 0x00000004
+#define PAS_IOB_DMA_RXCH_RESET_SINTC 0x00000002
+#define PAS_IOB_DMA_RXCH_RESET_PINTC 0x00000001
+#define PAS_IOB_DMA_TXCH_RESET(i) (0x1600 + (i)*4)
+#define PAS_IOB_DMA_TXCH_RESET_PCNT_M 0xffff0000
+#define PAS_IOB_DMA_TXCH_RESET_PCNT_S 16
+#define PAS_IOB_DMA_TXCH_RESET_PCNT(x) (((x) << PAS_IOB_DMA_TXCH_RESET_PCNT_S) & \
+ PAS_IOB_DMA_TXCH_RESET_PCNT_M)
+#define PAS_IOB_DMA_TXCH_RESET_PCNTRST 0x00000020
+#define PAS_IOB_DMA_TXCH_RESET_DCNTRST 0x00000010
+#define PAS_IOB_DMA_TXCH_RESET_TINTC 0x00000008
+#define PAS_IOB_DMA_TXCH_RESET_DINTC 0x00000004
+#define PAS_IOB_DMA_TXCH_RESET_SINTC 0x00000002
+#define PAS_IOB_DMA_TXCH_RESET_PINTC 0x00000001
+
+#define PAS_IOB_DMA_COM_TIMEOUTCFG 0x1700
+#define PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT_M 0x00ffffff
+#define PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT_S 0
+#define PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT(x) (((x) << PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT_S) & \
+ PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT_M)
+
+/* Transmit descriptor fields */
+#define XCT_MACTX_T 0x8000000000000000ull
+#define XCT_MACTX_ST 0x4000000000000000ull
+#define XCT_MACTX_NORES 0x0000000000000000ull
+#define XCT_MACTX_8BRES 0x1000000000000000ull
+#define XCT_MACTX_24BRES 0x2000000000000000ull
+#define XCT_MACTX_40BRES 0x3000000000000000ull
+#define XCT_MACTX_I 0x0800000000000000ull
+#define XCT_MACTX_O 0x0400000000000000ull
+#define XCT_MACTX_E 0x0200000000000000ull
+#define XCT_MACTX_VLAN_M 0x0180000000000000ull
+#define XCT_MACTX_VLAN_NOP 0x0000000000000000ull
+#define XCT_MACTX_VLAN_REMOVE 0x0080000000000000ull
+#define XCT_MACTX_VLAN_INSERT 0x0100000000000000ull
+#define XCT_MACTX_VLAN_REPLACE 0x0180000000000000ull
+#define XCT_MACTX_CRC_M 0x0060000000000000ull
+#define XCT_MACTX_CRC_NOP 0x0000000000000000ull
+#define XCT_MACTX_CRC_INSERT 0x0020000000000000ull
+#define XCT_MACTX_CRC_PAD 0x0040000000000000ull
+#define XCT_MACTX_CRC_REPLACE 0x0060000000000000ull
+#define XCT_MACTX_SS 0x0010000000000000ull
+#define XCT_MACTX_LLEN_M 0x00007fff00000000ull
+#define XCT_MACTX_LLEN_S 32ull
+#define XCT_MACTX_LLEN(x) ((((long)(x)) << XCT_MACTX_LLEN_S) & \
+ XCT_MACTX_LLEN_M)
+#define XCT_MACTX_IPH_M 0x00000000f8000000ull
+#define XCT_MACTX_IPH_S 27ull
+#define XCT_MACTX_IPH(x) ((((long)(x)) << XCT_MACTX_IPH_S) & \
+ XCT_MACTX_IPH_M)
+#define XCT_MACTX_IPO_M 0x0000000007c00000ull
+#define XCT_MACTX_IPO_S 22ull
+#define XCT_MACTX_IPO(x) ((((long)(x)) << XCT_MACTX_IPO_S) & \
+ XCT_MACTX_IPO_M)
+#define XCT_MACTX_CSUM_M 0x0000000000000060ull
+#define XCT_MACTX_CSUM_NOP 0x0000000000000000ull
+#define XCT_MACTX_CSUM_TCP 0x0000000000000040ull
+#define XCT_MACTX_CSUM_UDP 0x0000000000000060ull
+#define XCT_MACTX_V6 0x0000000000000010ull
+#define XCT_MACTX_C 0x0000000000000004ull
+#define XCT_MACTX_AL2 0x0000000000000002ull
+
+#define XCT_PTR_T 0x8000000000000000ull
+#define XCT_PTR_LEN_M 0x7ffff00000000000ull
+#define XCT_PTR_LEN_S 44
+#define XCT_PTR_LEN(x) ((((long)(x)) << XCT_PTR_LEN_S) & \
+ XCT_PTR_LEN_M)
+#define XCT_PTR_ADDR_M 0x00000fffffffffffull
+#define XCT_PTR_ADDR_S 0
+#define XCT_PTR_ADDR(x) ((((long)(x)) << XCT_PTR_ADDR_S) & \
+ XCT_PTR_ADDR_M)
+
+/* Function descriptor fields */
+#define XCT_FUN_T 0x8000000000000000ull
+#define XCT_FUN_ST 0x4000000000000000ull
+#define XCT_FUN_NORES 0x0000000000000000ull
+#define XCT_FUN_8BRES 0x1000000000000000ull
+#define XCT_FUN_24BRES 0x2000000000000000ull
+#define XCT_FUN_40BRES 0x3000000000000000ull
+#define XCT_FUN_I 0x0800000000000000ull
+#define XCT_FUN_O 0x0400000000000000ull
+#define XCT_FUN_E 0x0200000000000000ull
+#define XCT_FUN_FUN_S 54
+#define XCT_FUN_FUN_M 0x01c0000000000000ull
+#define XCT_FUN_FUN(num) ((((long)(num)) << XCT_FUN_FUN_S) & \
+ XCT_FUN_FUN_M)
+#define XCT_FUN_CRM_NOP 0x0000000000000000ull
+#define XCT_FUN_CRM_SIG 0x0008000000000000ull
+#define XCT_FUN_CRM_ENC 0x0010000000000000ull
+#define XCT_FUN_CRM_DEC 0x0018000000000000ull
+#define XCT_FUN_CRM_SIG_ENC 0x0020000000000000ull
+#define XCT_FUN_CRM_ENC_SIG 0x0028000000000000ull
+#define XCT_FUN_CRM_SIG_DEC 0x0030000000000000ull
+#define XCT_FUN_CRM_DEC_SIG 0x0038000000000000ull
+#define XCT_FUN_LLEN_M 0x0007ffff00000000ull
+#define XCT_FUN_LLEN_S 32ULL
+#define XCT_FUN_LLEN(x) ((((long)(x)) << XCT_FUN_LLEN_S) & \
+ XCT_FUN_LLEN_M)
+#define XCT_FUN_SHL_M 0x00000000f8000000ull
+#define XCT_FUN_SHL_S 27ull
+#define XCT_FUN_SHL(x) ((((long)(x)) << XCT_FUN_SHL_S) & \
+ XCT_FUN_SHL_M)
+#define XCT_FUN_CHL_M 0x0000000007c00000ull
+#define XCT_FUN_CHL_S 22ull
+#define XCT_FUN_CHL(x) ((((long)(x)) << XCT_FUN_CHL_S) & \
+ XCT_FUN_CHL_M)
+#define XCT_FUN_HSZ_M 0x00000000003c0000ull
+#define XCT_FUN_HSZ_S 18ull
+#define XCT_FUN_HSZ(x) ((((long)(x)) << XCT_FUN_HSZ_S) & \
+ XCT_FUN_HSZ_M)
+#define XCT_FUN_ALG_DES 0x0000000000000000ull
+#define XCT_FUN_ALG_3DES 0x0000000000008000ull
+#define XCT_FUN_ALG_AES 0x0000000000010000ull
+#define XCT_FUN_ALG_ARC 0x0000000000018000ull
+#define XCT_FUN_ALG_KASUMI 0x0000000000020000ull
+#define XCT_FUN_BCM_ECB 0x0000000000000000ull
+#define XCT_FUN_BCM_CBC 0x0000000000001000ull
+#define XCT_FUN_BCM_CFB 0x0000000000002000ull
+#define XCT_FUN_BCM_OFB 0x0000000000003000ull
+#define XCT_FUN_BCM_CNT 0x0000000000003800ull
+#define XCT_FUN_BCM_KAS_F8 0x0000000000002800ull
+#define XCT_FUN_BCM_KAS_F9 0x0000000000001800ull
+#define XCT_FUN_BCP_NO_PAD 0x0000000000000000ull
+#define XCT_FUN_BCP_ZRO 0x0000000000000200ull
+#define XCT_FUN_BCP_PL 0x0000000000000400ull
+#define XCT_FUN_BCP_INCR 0x0000000000000600ull
+#define XCT_FUN_SIG_MD5 (0ull << 4)
+#define XCT_FUN_SIG_SHA1 (2ull << 4)
+#define XCT_FUN_SIG_HMAC_MD5 (8ull << 4)
+#define XCT_FUN_SIG_HMAC_SHA1 (10ull << 4)
+#define XCT_FUN_A 0x0000000000000008ull
+#define XCT_FUN_C 0x0000000000000004ull
+#define XCT_FUN_AL2 0x0000000000000002ull
+#define XCT_FUN_SE 0x0000000000000001ull
+
+#define XCT_FUN_SRC_PTR(len, addr) (XCT_PTR_LEN(len) | XCT_PTR_ADDR(addr))
+#define XCT_FUN_DST_PTR(len, addr) (XCT_FUN_SRC_PTR(len, addr) | \
+ 0x8000000000000000ull)
+
+#define XCT_CTRL_HDR_FUN_NUM_M 0x01c0000000000000ull
+#define XCT_CTRL_HDR_FUN_NUM_S 54
+#define XCT_CTRL_HDR_LEN_M 0x0007ffff00000000ull
+#define XCT_CTRL_HDR_LEN_S 32
+#define XCT_CTRL_HDR_REG_M 0x00000000000000ffull
+#define XCT_CTRL_HDR_REG_S 0
+
+#define XCT_CTRL_HDR(funcN,len,reg) (0x9400000000000000ull | \
+ ((((long)(funcN)) << XCT_CTRL_HDR_FUN_NUM_S) \
+ & XCT_CTRL_HDR_FUN_NUM_M) | \
+ ((((long)(len)) << \
+ XCT_CTRL_HDR_LEN_S) & XCT_CTRL_HDR_LEN_M) | \
+ ((((long)(reg)) << \
+ XCT_CTRL_HDR_REG_S) & XCT_CTRL_HDR_REG_M))
+
+/* Function config command options */
+#define DMA_CALGO_DES 0x00
+#define DMA_CALGO_3DES 0x01
+#define DMA_CALGO_AES 0x02
+#define DMA_CALGO_ARC 0x03
+
+#define DMA_FN_CIV0 0x02
+#define DMA_FN_CIV1 0x03
+#define DMA_FN_HKEY0 0x0a
+
+#define XCT_PTR_ADDR_LEN(ptr) ((ptr) & XCT_PTR_ADDR_M), \
+ (((ptr) & XCT_PTR_LEN_M) >> XCT_PTR_LEN_S)
+
+#endif /* PASEMI_FNU_H */
diff --git a/crypto/ocf/random.c b/crypto/ocf/random.c
new file mode 100644
index 000000000000..94f30416f6fa
--- /dev/null
+++ b/crypto/ocf/random.c
@@ -0,0 +1,317 @@
+/*
+ * A system independant way of adding entropy to the kernels pool
+ * this way the drivers can focus on the real work and we can take
+ * care of pushing it to the appropriate place in the kernel.
+ *
+ * This should be fast and callable from timers/interrupts
+ *
+ * Written by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2006-2007 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ */
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/poll.h>
+#include <linux/random.h>
+#include <cryptodev.h>
+
+#ifdef CONFIG_OCF_FIPS
+#include "rndtest.h"
+#endif
+
+#ifndef HAS_RANDOM_INPUT_WAIT
+#error "Please do not enable OCF_RANDOMHARVEST unless you have applied patches"
+#endif
+
+/*
+ * a hack to access the debug levels from the crypto driver
+ */
+extern int crypto_debug;
+#define debug crypto_debug
+
+/*
+ * a list of all registered random providers
+ */
+static LIST_HEAD(random_ops);
+static int started = 0;
+static int initted = 0;
+
+struct random_op {
+ struct list_head random_list;
+ u_int32_t driverid;
+ int (*read_random)(void *arg, u_int32_t *buf, int len);
+ void *arg;
+};
+
+static int random_proc(void *arg);
+
+static pid_t randomproc = (pid_t) -1;
+static spinlock_t random_lock;
+
+/*
+ * just init the spin locks
+ */
+static int
+crypto_random_init(void)
+{
+ spin_lock_init(&random_lock);
+ initted = 1;
+ return(0);
+}
+
+/*
+ * Add the given random reader to our list (if not present)
+ * and start the thread (if not already started)
+ *
+ * we have to assume that driver id is ok for now
+ */
+int
+crypto_rregister(
+ u_int32_t driverid,
+ int (*read_random)(void *arg, u_int32_t *buf, int len),
+ void *arg)
+{
+ unsigned long flags;
+ int ret = 0;
+ struct random_op *rops, *tmp;
+
+ dprintk("%s,%d: %s(0x%x, %p, %p)\n", __FILE__, __LINE__,
+ __FUNCTION__, driverid, read_random, arg);
+
+ if (!initted)
+ crypto_random_init();
+
+#if 0
+ struct cryptocap *cap;
+
+ cap = crypto_checkdriver(driverid);
+ if (!cap)
+ return EINVAL;
+#endif
+
+ list_for_each_entry_safe(rops, tmp, &random_ops, random_list) {
+ if (rops->driverid == driverid && rops->read_random == read_random)
+ return EEXIST;
+ }
+
+ rops = (struct random_op *) kmalloc(sizeof(*rops), GFP_KERNEL);
+ if (!rops)
+ return ENOMEM;
+
+ rops->driverid = driverid;
+ rops->read_random = read_random;
+ rops->arg = arg;
+
+ spin_lock_irqsave(&random_lock, flags);
+ list_add_tail(&rops->random_list, &random_ops);
+ if (!started) {
+ randomproc = kernel_thread(random_proc, NULL, CLONE_FS|CLONE_FILES);
+ if (randomproc < 0) {
+ ret = randomproc;
+ printk("crypto: crypto_rregister cannot start random thread; "
+ "error %d", ret);
+ } else
+ started = 1;
+ }
+ spin_unlock_irqrestore(&random_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(crypto_rregister);
+
+int
+crypto_runregister_all(u_int32_t driverid)
+{
+ struct random_op *rops, *tmp;
+ unsigned long flags;
+
+ dprintk("%s,%d: %s(0x%x)\n", __FILE__, __LINE__, __FUNCTION__, driverid);
+
+ list_for_each_entry_safe(rops, tmp, &random_ops, random_list) {
+ if (rops->driverid == driverid) {
+ list_del(&rops->random_list);
+ kfree(rops);
+ }
+ }
+
+ spin_lock_irqsave(&random_lock, flags);
+ if (list_empty(&random_ops) && started)
+ kill_proc(randomproc, SIGKILL, 1);
+ spin_unlock_irqrestore(&random_lock, flags);
+ return(0);
+}
+EXPORT_SYMBOL(crypto_runregister_all);
+
+/*
+ * while we can add entropy to random.c continue to read random data from
+ * the drivers and push it to random.
+ */
+static int
+random_proc(void *arg)
+{
+ int n;
+ int wantcnt;
+ int bufcnt = 0;
+ int retval = 0;
+ int *buf = NULL;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ daemonize();
+ spin_lock_irq(&current->sigmask_lock);
+ sigemptyset(&current->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+ sprintf(current->comm, "ocf-random");
+#else
+ daemonize("ocf-random");
+ allow_signal(SIGKILL);
+#endif
+
+ (void) get_fs();
+ set_fs(get_ds());
+
+#ifdef CONFIG_OCF_FIPS
+#define NUM_INT (RNDTEST_NBYTES/sizeof(int))
+#else
+#define NUM_INT 32
+#endif
+
+ /*
+ * some devices can transferr their RNG data direct into memory,
+ * so make sure it is device friendly
+ */
+ buf = kmalloc(NUM_INT * sizeof(int), GFP_DMA);
+ if (NULL == buf) {
+ printk("crypto: RNG could not allocate memory\n");
+ retval = -ENOMEM;
+ goto bad_alloc;
+ }
+
+ wantcnt = NUM_INT; /* start by adding some entropy */
+
+ /*
+ * its possible due to errors or driver removal that we no longer
+ * have anything to do, if so exit or we will consume all the CPU
+ * doing nothing
+ */
+ while (!list_empty(&random_ops)) {
+ struct random_op *rops, *tmp;
+
+#ifdef CONFIG_OCF_FIPS
+ if (wantcnt)
+ wantcnt = NUM_INT; /* FIPs mode can do 20000 bits or none */
+#endif
+
+ /* see if we can get enough entropy to make the world
+ * a better place.
+ */
+ while (bufcnt < wantcnt && bufcnt < NUM_INT) {
+ list_for_each_entry_safe(rops, tmp, &random_ops, random_list) {
+
+ n = (*rops->read_random)(rops->arg, &buf[bufcnt],
+ NUM_INT - bufcnt);
+
+ /* on failure remove the random number generator */
+ if (n == -1) {
+ list_del(&rops->random_list);
+ printk("crypto: RNG (driverid=0x%x) failed, disabling\n",
+ rops->driverid);
+ kfree(rops);
+ } else if (n > 0)
+ bufcnt += n;
+ }
+ /* give up CPU for a bit, just in case as this is a loop */
+ schedule();
+ }
+
+
+#ifdef CONFIG_OCF_FIPS
+ if (bufcnt > 0 && rndtest_buf((unsigned char *) &buf[0])) {
+ dprintk("crypto: buffer had fips errors, discarding\n");
+ bufcnt = 0;
+ }
+#endif
+
+ /*
+ * if we have a certified buffer, we can send some data
+ * to /dev/random and move along
+ */
+ if (bufcnt > 0) {
+ /* add what we have */
+ random_input_words(buf, bufcnt, bufcnt*sizeof(int)*8);
+ bufcnt = 0;
+ }
+
+ /* give up CPU for a bit so we don't hog while filling */
+ schedule();
+
+ /* wait for needing more */
+ wantcnt = random_input_wait();
+
+ if (wantcnt <= 0)
+ wantcnt = 0; /* try to get some info again */
+ else
+ /* round up to one word or we can loop forever */
+ wantcnt = (wantcnt + (sizeof(int)*8)) / (sizeof(int)*8);
+ if (wantcnt > NUM_INT) {
+ wantcnt = NUM_INT;
+ }
+
+ if (signal_pending(current)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_lock_irq(&current->sigmask_lock);
+#endif
+ flush_signals(current);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ spin_unlock_irq(&current->sigmask_lock);
+#endif
+ }
+ }
+
+ kfree(buf);
+
+bad_alloc:
+ spin_lock_irq(&random_lock);
+ randomproc = (pid_t) -1;
+ started = 0;
+ spin_unlock_irq(&random_lock);
+
+ return retval;
+}
+
diff --git a/crypto/ocf/rndtest.c b/crypto/ocf/rndtest.c
new file mode 100644
index 000000000000..34b68d8198d1
--- /dev/null
+++ b/crypto/ocf/rndtest.c
@@ -0,0 +1,300 @@
+/* $OpenBSD$ */
+
+/*
+ * OCF/Linux port done by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2006-2007 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ * The license and original author are listed below.
+ *
+ * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jason L. Wright
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <cryptodev.h>
+#include "rndtest.h"
+
+static struct rndtest_stats rndstats;
+
+static void rndtest_test(struct rndtest_state *);
+
+/* The tests themselves */
+static int rndtest_monobit(struct rndtest_state *);
+static int rndtest_runs(struct rndtest_state *);
+static int rndtest_longruns(struct rndtest_state *);
+static int rndtest_chi_4(struct rndtest_state *);
+
+static int rndtest_runs_check(struct rndtest_state *, int, int *);
+static void rndtest_runs_record(struct rndtest_state *, int, int *);
+
+static const struct rndtest_testfunc {
+ int (*test)(struct rndtest_state *);
+} rndtest_funcs[] = {
+ { rndtest_monobit },
+ { rndtest_runs },
+ { rndtest_chi_4 },
+ { rndtest_longruns },
+};
+
+#define RNDTEST_NTESTS (sizeof(rndtest_funcs)/sizeof(rndtest_funcs[0]))
+
+static void
+rndtest_test(struct rndtest_state *rsp)
+{
+ int i, rv = 0;
+
+ rndstats.rst_tests++;
+ for (i = 0; i < RNDTEST_NTESTS; i++)
+ rv |= (*rndtest_funcs[i].test)(rsp);
+ rsp->rs_discard = (rv != 0);
+}
+
+
+extern int crypto_debug;
+#define rndtest_verbose 2
+#define rndtest_report(rsp, failure, fmt, a...) \
+ { if (failure || crypto_debug) { printk("rng_test: " fmt "\n", a); } else; }
+
+#define RNDTEST_MONOBIT_MINONES 9725
+#define RNDTEST_MONOBIT_MAXONES 10275
+
+static int
+rndtest_monobit(struct rndtest_state *rsp)
+{
+ int i, ones = 0, j;
+ u_int8_t r;
+
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
+ r = rsp->rs_buf[i];
+ for (j = 0; j < 8; j++, r <<= 1)
+ if (r & 0x80)
+ ones++;
+ }
+ if (ones > RNDTEST_MONOBIT_MINONES &&
+ ones < RNDTEST_MONOBIT_MAXONES) {
+ if (rndtest_verbose > 1)
+ rndtest_report(rsp, 0, "monobit pass (%d < %d < %d)",
+ RNDTEST_MONOBIT_MINONES, ones,
+ RNDTEST_MONOBIT_MAXONES);
+ return (0);
+ } else {
+ if (rndtest_verbose)
+ rndtest_report(rsp, 1,
+ "monobit failed (%d ones)", ones);
+ rndstats.rst_monobit++;
+ return (-1);
+ }
+}
+
+#define RNDTEST_RUNS_NINTERVAL 6
+
+static const struct rndtest_runs_tabs {
+ u_int16_t min, max;
+} rndtest_runs_tab[] = {
+ { 2343, 2657 },
+ { 1135, 1365 },
+ { 542, 708 },
+ { 251, 373 },
+ { 111, 201 },
+ { 111, 201 },
+};
+
+static int
+rndtest_runs(struct rndtest_state *rsp)
+{
+ int i, j, ones, zeros, rv = 0;
+ int onei[RNDTEST_RUNS_NINTERVAL], zeroi[RNDTEST_RUNS_NINTERVAL];
+ u_int8_t c;
+
+ bzero(onei, sizeof(onei));
+ bzero(zeroi, sizeof(zeroi));
+ ones = zeros = 0;
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
+ c = rsp->rs_buf[i];
+ for (j = 0; j < 8; j++, c <<= 1) {
+ if (c & 0x80) {
+ ones++;
+ rndtest_runs_record(rsp, zeros, zeroi);
+ zeros = 0;
+ } else {
+ zeros++;
+ rndtest_runs_record(rsp, ones, onei);
+ ones = 0;
+ }
+ }
+ }
+ rndtest_runs_record(rsp, ones, onei);
+ rndtest_runs_record(rsp, zeros, zeroi);
+
+ rv |= rndtest_runs_check(rsp, 0, zeroi);
+ rv |= rndtest_runs_check(rsp, 1, onei);
+
+ if (rv)
+ rndstats.rst_runs++;
+
+ return (rv);
+}
+
+static void
+rndtest_runs_record(struct rndtest_state *rsp, int len, int *intrv)
+{
+ if (len == 0)
+ return;
+ if (len > RNDTEST_RUNS_NINTERVAL)
+ len = RNDTEST_RUNS_NINTERVAL;
+ len -= 1;
+ intrv[len]++;
+}
+
+static int
+rndtest_runs_check(struct rndtest_state *rsp, int val, int *src)
+{
+ int i, rv = 0;
+
+ for (i = 0; i < RNDTEST_RUNS_NINTERVAL; i++) {
+ if (src[i] < rndtest_runs_tab[i].min ||
+ src[i] > rndtest_runs_tab[i].max) {
+ rndtest_report(rsp, 1,
+ "%s interval %d failed (%d, %d-%d)",
+ val ? "ones" : "zeros",
+ i + 1, src[i], rndtest_runs_tab[i].min,
+ rndtest_runs_tab[i].max);
+ rv = -1;
+ } else {
+ rndtest_report(rsp, 0,
+ "runs pass %s interval %d (%d < %d < %d)",
+ val ? "ones" : "zeros",
+ i + 1, rndtest_runs_tab[i].min, src[i],
+ rndtest_runs_tab[i].max);
+ }
+ }
+ return (rv);
+}
+
+static int
+rndtest_longruns(struct rndtest_state *rsp)
+{
+ int i, j, ones = 0, zeros = 0, maxones = 0, maxzeros = 0;
+ u_int8_t c;
+
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
+ c = rsp->rs_buf[i];
+ for (j = 0; j < 8; j++, c <<= 1) {
+ if (c & 0x80) {
+ zeros = 0;
+ ones++;
+ if (ones > maxones)
+ maxones = ones;
+ } else {
+ ones = 0;
+ zeros++;
+ if (zeros > maxzeros)
+ maxzeros = zeros;
+ }
+ }
+ }
+
+ if (maxones < 26 && maxzeros < 26) {
+ rndtest_report(rsp, 0, "longruns pass (%d ones, %d zeros)",
+ maxones, maxzeros);
+ return (0);
+ } else {
+ rndtest_report(rsp, 1, "longruns fail (%d ones, %d zeros)",
+ maxones, maxzeros);
+ rndstats.rst_longruns++;
+ return (-1);
+ }
+}
+
+/*
+ * chi^2 test over 4 bits: (this is called the poker test in FIPS 140-2,
+ * but it is really the chi^2 test over 4 bits (the poker test as described
+ * by Knuth vol 2 is something different, and I take him as authoritative
+ * on nomenclature over NIST).
+ */
+#define RNDTEST_CHI4_K 16
+#define RNDTEST_CHI4_K_MASK (RNDTEST_CHI4_K - 1)
+
+/*
+ * The unnormalized values are used so that we don't have to worry about
+ * fractional precision. The "real" value is found by:
+ * (V - 1562500) * (16 / 5000) = Vn (where V is the unnormalized value)
+ */
+#define RNDTEST_CHI4_VMIN 1563181 /* 2.1792 */
+#define RNDTEST_CHI4_VMAX 1576929 /* 46.1728 */
+
+static int
+rndtest_chi_4(struct rndtest_state *rsp)
+{
+ unsigned int freq[RNDTEST_CHI4_K], i, sum;
+
+ for (i = 0; i < RNDTEST_CHI4_K; i++)
+ freq[i] = 0;
+
+ /* Get number of occurances of each 4 bit pattern */
+ for (i = 0; i < RNDTEST_NBYTES; i++) {
+ freq[(rsp->rs_buf[i] >> 4) & RNDTEST_CHI4_K_MASK]++;
+ freq[(rsp->rs_buf[i] >> 0) & RNDTEST_CHI4_K_MASK]++;
+ }
+
+ for (i = 0, sum = 0; i < RNDTEST_CHI4_K; i++)
+ sum += freq[i] * freq[i];
+
+ if (sum >= 1563181 && sum <= 1576929) {
+ rndtest_report(rsp, 0, "chi^2(4): pass (sum %u)", sum);
+ return (0);
+ } else {
+ rndtest_report(rsp, 1, "chi^2(4): failed (sum %u)", sum);
+ rndstats.rst_chi++;
+ return (-1);
+ }
+}
+
+int
+rndtest_buf(unsigned char *buf)
+{
+ struct rndtest_state rsp;
+
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.rs_buf = buf;
+ rndtest_test(&rsp);
+ return(rsp.rs_discard);
+}
+
diff --git a/crypto/ocf/rndtest.h b/crypto/ocf/rndtest.h
new file mode 100644
index 000000000000..e9d8ec8d3949
--- /dev/null
+++ b/crypto/ocf/rndtest.h
@@ -0,0 +1,54 @@
+/* $FreeBSD: src/sys/dev/rndtest/rndtest.h,v 1.1 2003/03/11 22:54:44 sam Exp $ */
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jason L. Wright
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/* Some of the tests depend on these values */
+#define RNDTEST_NBYTES 2500
+#define RNDTEST_NBITS (8 * RNDTEST_NBYTES)
+
+struct rndtest_state {
+ int rs_discard; /* discard/accept random data */
+ u_int8_t *rs_buf;
+};
+
+struct rndtest_stats {
+ u_int32_t rst_discard; /* number of bytes discarded */
+ u_int32_t rst_tests; /* number of test runs */
+ u_int32_t rst_monobit; /* monobit test failures */
+ u_int32_t rst_runs; /* 0/1 runs failures */
+ u_int32_t rst_longruns; /* longruns failures */
+ u_int32_t rst_chi; /* chi^2 failures */
+};
+
+extern int rndtest_buf(unsigned char *buf);
diff --git a/crypto/ocf/safe/Makefile b/crypto/ocf/safe/Makefile
new file mode 100644
index 000000000000..9a36b081e5a2
--- /dev/null
+++ b/crypto/ocf/safe/Makefile
@@ -0,0 +1,12 @@
+# for SGlinux builds
+-include $(ROOTDIR)/modules/.config
+
+obj-$(CONFIG_OCF_SAFE) += safe.o
+
+obj ?= .
+EXTRA_CFLAGS += -I$(obj)/.. -I$(obj)/
+
+ifdef TOPDIR
+-include $(TOPDIR)/Rules.make
+endif
+
diff --git a/crypto/ocf/safe/md5.c b/crypto/ocf/safe/md5.c
new file mode 100644
index 000000000000..077c42e7876b
--- /dev/null
+++ b/crypto/ocf/safe/md5.c
@@ -0,0 +1,308 @@
+/* $KAME: md5.c,v 1.5 2000/11/08 06:13:08 itojun Exp $ */
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/crypto/md5.c,v 1.9 2004/01/27 19:49:19 des Exp $");
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/time.h>
+#include <sys/systm.h>
+#include <crypto/md5.h>
+#endif
+
+#define SHIFT(X, s) (((X) << (s)) | ((X) >> (32 - (s))))
+
+#define F(X, Y, Z) (((X) & (Y)) | ((~X) & (Z)))
+#define G(X, Y, Z) (((X) & (Z)) | ((Y) & (~Z)))
+#define H(X, Y, Z) ((X) ^ (Y) ^ (Z))
+#define I(X, Y, Z) ((Y) ^ ((X) | (~Z)))
+
+#define ROUND1(a, b, c, d, k, s, i) { \
+ (a) = (a) + F((b), (c), (d)) + X[(k)] + T[(i)]; \
+ (a) = SHIFT((a), (s)); \
+ (a) = (b) + (a); \
+}
+
+#define ROUND2(a, b, c, d, k, s, i) { \
+ (a) = (a) + G((b), (c), (d)) + X[(k)] + T[(i)]; \
+ (a) = SHIFT((a), (s)); \
+ (a) = (b) + (a); \
+}
+
+#define ROUND3(a, b, c, d, k, s, i) { \
+ (a) = (a) + H((b), (c), (d)) + X[(k)] + T[(i)]; \
+ (a) = SHIFT((a), (s)); \
+ (a) = (b) + (a); \
+}
+
+#define ROUND4(a, b, c, d, k, s, i) { \
+ (a) = (a) + I((b), (c), (d)) + X[(k)] + T[(i)]; \
+ (a) = SHIFT((a), (s)); \
+ (a) = (b) + (a); \
+}
+
+#define Sa 7
+#define Sb 12
+#define Sc 17
+#define Sd 22
+
+#define Se 5
+#define Sf 9
+#define Sg 14
+#define Sh 20
+
+#define Si 4
+#define Sj 11
+#define Sk 16
+#define Sl 23
+
+#define Sm 6
+#define Sn 10
+#define So 15
+#define Sp 21
+
+#define MD5_A0 0x67452301
+#define MD5_B0 0xefcdab89
+#define MD5_C0 0x98badcfe
+#define MD5_D0 0x10325476
+
+/* Integer part of 4294967296 times abs(sin(i)), where i is in radians. */
+static const u_int32_t T[65] = {
+ 0,
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+ 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
+};
+
+static const u_int8_t md5_paddat[MD5_BUFLEN] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static void md5_calc(u_int8_t *, md5_ctxt *);
+
+void md5_init(ctxt)
+ md5_ctxt *ctxt;
+{
+ ctxt->md5_n = 0;
+ ctxt->md5_i = 0;
+ ctxt->md5_sta = MD5_A0;
+ ctxt->md5_stb = MD5_B0;
+ ctxt->md5_stc = MD5_C0;
+ ctxt->md5_std = MD5_D0;
+ bzero(ctxt->md5_buf, sizeof(ctxt->md5_buf));
+}
+
+void md5_loop(ctxt, input, len)
+ md5_ctxt *ctxt;
+ u_int8_t *input;
+ u_int len; /* number of bytes */
+{
+ u_int gap, i;
+
+ ctxt->md5_n += len * 8; /* byte to bit */
+ gap = MD5_BUFLEN - ctxt->md5_i;
+
+ if (len >= gap) {
+ bcopy((void *)input, (void *)(ctxt->md5_buf + ctxt->md5_i),
+ gap);
+ md5_calc(ctxt->md5_buf, ctxt);
+
+ for (i = gap; i + MD5_BUFLEN <= len; i += MD5_BUFLEN) {
+ md5_calc((u_int8_t *)(input + i), ctxt);
+ }
+
+ ctxt->md5_i = len - i;
+ bcopy((void *)(input + i), (void *)ctxt->md5_buf, ctxt->md5_i);
+ } else {
+ bcopy((void *)input, (void *)(ctxt->md5_buf + ctxt->md5_i),
+ len);
+ ctxt->md5_i += len;
+ }
+}
+
+void md5_pad(ctxt)
+ md5_ctxt *ctxt;
+{
+ u_int gap;
+
+ /* Don't count up padding. Keep md5_n. */
+ gap = MD5_BUFLEN - ctxt->md5_i;
+ if (gap > 8) {
+ bcopy(md5_paddat,
+ (void *)(ctxt->md5_buf + ctxt->md5_i),
+ gap - sizeof(ctxt->md5_n));
+ } else {
+ /* including gap == 8 */
+ bcopy(md5_paddat, (void *)(ctxt->md5_buf + ctxt->md5_i),
+ gap);
+ md5_calc(ctxt->md5_buf, ctxt);
+ bcopy((md5_paddat + gap),
+ (void *)ctxt->md5_buf,
+ MD5_BUFLEN - sizeof(ctxt->md5_n));
+ }
+
+ /* 8 byte word */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ bcopy(&ctxt->md5_n8[0], &ctxt->md5_buf[56], 8);
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ ctxt->md5_buf[56] = ctxt->md5_n8[7];
+ ctxt->md5_buf[57] = ctxt->md5_n8[6];
+ ctxt->md5_buf[58] = ctxt->md5_n8[5];
+ ctxt->md5_buf[59] = ctxt->md5_n8[4];
+ ctxt->md5_buf[60] = ctxt->md5_n8[3];
+ ctxt->md5_buf[61] = ctxt->md5_n8[2];
+ ctxt->md5_buf[62] = ctxt->md5_n8[1];
+ ctxt->md5_buf[63] = ctxt->md5_n8[0];
+#endif
+
+ md5_calc(ctxt->md5_buf, ctxt);
+}
+
+void md5_result(digest, ctxt)
+ u_int8_t *digest;
+ md5_ctxt *ctxt;
+{
+ /* 4 byte words */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ bcopy(&ctxt->md5_st8[0], digest, 16);
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ digest[ 0] = ctxt->md5_st8[ 3]; digest[ 1] = ctxt->md5_st8[ 2];
+ digest[ 2] = ctxt->md5_st8[ 1]; digest[ 3] = ctxt->md5_st8[ 0];
+ digest[ 4] = ctxt->md5_st8[ 7]; digest[ 5] = ctxt->md5_st8[ 6];
+ digest[ 6] = ctxt->md5_st8[ 5]; digest[ 7] = ctxt->md5_st8[ 4];
+ digest[ 8] = ctxt->md5_st8[11]; digest[ 9] = ctxt->md5_st8[10];
+ digest[10] = ctxt->md5_st8[ 9]; digest[11] = ctxt->md5_st8[ 8];
+ digest[12] = ctxt->md5_st8[15]; digest[13] = ctxt->md5_st8[14];
+ digest[14] = ctxt->md5_st8[13]; digest[15] = ctxt->md5_st8[12];
+#endif
+}
+
+static void md5_calc(b64, ctxt)
+ u_int8_t *b64;
+ md5_ctxt *ctxt;
+{
+ u_int32_t A = ctxt->md5_sta;
+ u_int32_t B = ctxt->md5_stb;
+ u_int32_t C = ctxt->md5_stc;
+ u_int32_t D = ctxt->md5_std;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u_int32_t *X = (u_int32_t *)b64;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ /* 4 byte words */
+ /* what a brute force but fast! */
+ u_int32_t X[16];
+ u_int8_t *y = (u_int8_t *)X;
+ y[ 0] = b64[ 3]; y[ 1] = b64[ 2]; y[ 2] = b64[ 1]; y[ 3] = b64[ 0];
+ y[ 4] = b64[ 7]; y[ 5] = b64[ 6]; y[ 6] = b64[ 5]; y[ 7] = b64[ 4];
+ y[ 8] = b64[11]; y[ 9] = b64[10]; y[10] = b64[ 9]; y[11] = b64[ 8];
+ y[12] = b64[15]; y[13] = b64[14]; y[14] = b64[13]; y[15] = b64[12];
+ y[16] = b64[19]; y[17] = b64[18]; y[18] = b64[17]; y[19] = b64[16];
+ y[20] = b64[23]; y[21] = b64[22]; y[22] = b64[21]; y[23] = b64[20];
+ y[24] = b64[27]; y[25] = b64[26]; y[26] = b64[25]; y[27] = b64[24];
+ y[28] = b64[31]; y[29] = b64[30]; y[30] = b64[29]; y[31] = b64[28];
+ y[32] = b64[35]; y[33] = b64[34]; y[34] = b64[33]; y[35] = b64[32];
+ y[36] = b64[39]; y[37] = b64[38]; y[38] = b64[37]; y[39] = b64[36];
+ y[40] = b64[43]; y[41] = b64[42]; y[42] = b64[41]; y[43] = b64[40];
+ y[44] = b64[47]; y[45] = b64[46]; y[46] = b64[45]; y[47] = b64[44];
+ y[48] = b64[51]; y[49] = b64[50]; y[50] = b64[49]; y[51] = b64[48];
+ y[52] = b64[55]; y[53] = b64[54]; y[54] = b64[53]; y[55] = b64[52];
+ y[56] = b64[59]; y[57] = b64[58]; y[58] = b64[57]; y[59] = b64[56];
+ y[60] = b64[63]; y[61] = b64[62]; y[62] = b64[61]; y[63] = b64[60];
+#endif
+
+ ROUND1(A, B, C, D, 0, Sa, 1); ROUND1(D, A, B, C, 1, Sb, 2);
+ ROUND1(C, D, A, B, 2, Sc, 3); ROUND1(B, C, D, A, 3, Sd, 4);
+ ROUND1(A, B, C, D, 4, Sa, 5); ROUND1(D, A, B, C, 5, Sb, 6);
+ ROUND1(C, D, A, B, 6, Sc, 7); ROUND1(B, C, D, A, 7, Sd, 8);
+ ROUND1(A, B, C, D, 8, Sa, 9); ROUND1(D, A, B, C, 9, Sb, 10);
+ ROUND1(C, D, A, B, 10, Sc, 11); ROUND1(B, C, D, A, 11, Sd, 12);
+ ROUND1(A, B, C, D, 12, Sa, 13); ROUND1(D, A, B, C, 13, Sb, 14);
+ ROUND1(C, D, A, B, 14, Sc, 15); ROUND1(B, C, D, A, 15, Sd, 16);
+
+ ROUND2(A, B, C, D, 1, Se, 17); ROUND2(D, A, B, C, 6, Sf, 18);
+ ROUND2(C, D, A, B, 11, Sg, 19); ROUND2(B, C, D, A, 0, Sh, 20);
+ ROUND2(A, B, C, D, 5, Se, 21); ROUND2(D, A, B, C, 10, Sf, 22);
+ ROUND2(C, D, A, B, 15, Sg, 23); ROUND2(B, C, D, A, 4, Sh, 24);
+ ROUND2(A, B, C, D, 9, Se, 25); ROUND2(D, A, B, C, 14, Sf, 26);
+ ROUND2(C, D, A, B, 3, Sg, 27); ROUND2(B, C, D, A, 8, Sh, 28);
+ ROUND2(A, B, C, D, 13, Se, 29); ROUND2(D, A, B, C, 2, Sf, 30);
+ ROUND2(C, D, A, B, 7, Sg, 31); ROUND2(B, C, D, A, 12, Sh, 32);
+
+ ROUND3(A, B, C, D, 5, Si, 33); ROUND3(D, A, B, C, 8, Sj, 34);
+ ROUND3(C, D, A, B, 11, Sk, 35); ROUND3(B, C, D, A, 14, Sl, 36);
+ ROUND3(A, B, C, D, 1, Si, 37); ROUND3(D, A, B, C, 4, Sj, 38);
+ ROUND3(C, D, A, B, 7, Sk, 39); ROUND3(B, C, D, A, 10, Sl, 40);
+ ROUND3(A, B, C, D, 13, Si, 41); ROUND3(D, A, B, C, 0, Sj, 42);
+ ROUND3(C, D, A, B, 3, Sk, 43); ROUND3(B, C, D, A, 6, Sl, 44);
+ ROUND3(A, B, C, D, 9, Si, 45); ROUND3(D, A, B, C, 12, Sj, 46);
+ ROUND3(C, D, A, B, 15, Sk, 47); ROUND3(B, C, D, A, 2, Sl, 48);
+
+ ROUND4(A, B, C, D, 0, Sm, 49); ROUND4(D, A, B, C, 7, Sn, 50);
+ ROUND4(C, D, A, B, 14, So, 51); ROUND4(B, C, D, A, 5, Sp, 52);
+ ROUND4(A, B, C, D, 12, Sm, 53); ROUND4(D, A, B, C, 3, Sn, 54);
+ ROUND4(C, D, A, B, 10, So, 55); ROUND4(B, C, D, A, 1, Sp, 56);
+ ROUND4(A, B, C, D, 8, Sm, 57); ROUND4(D, A, B, C, 15, Sn, 58);
+ ROUND4(C, D, A, B, 6, So, 59); ROUND4(B, C, D, A, 13, Sp, 60);
+ ROUND4(A, B, C, D, 4, Sm, 61); ROUND4(D, A, B, C, 11, Sn, 62);
+ ROUND4(C, D, A, B, 2, So, 63); ROUND4(B, C, D, A, 9, Sp, 64);
+
+ ctxt->md5_sta += A;
+ ctxt->md5_stb += B;
+ ctxt->md5_stc += C;
+ ctxt->md5_std += D;
+}
diff --git a/crypto/ocf/safe/md5.h b/crypto/ocf/safe/md5.h
new file mode 100644
index 000000000000..690f5bfc11f3
--- /dev/null
+++ b/crypto/ocf/safe/md5.h
@@ -0,0 +1,76 @@
+/* $FreeBSD: src/sys/crypto/md5.h,v 1.4 2002/03/20 05:13:50 alfred Exp $ */
+/* $KAME: md5.h,v 1.4 2000/03/27 04:36:22 sumikawa Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NETINET6_MD5_H_
+#define _NETINET6_MD5_H_
+
+#define MD5_BUFLEN 64
+
+typedef struct {
+ union {
+ u_int32_t md5_state32[4];
+ u_int8_t md5_state8[16];
+ } md5_st;
+
+#define md5_sta md5_st.md5_state32[0]
+#define md5_stb md5_st.md5_state32[1]
+#define md5_stc md5_st.md5_state32[2]
+#define md5_std md5_st.md5_state32[3]
+#define md5_st8 md5_st.md5_state8
+
+ union {
+ u_int64_t md5_count64;
+ u_int8_t md5_count8[8];
+ } md5_count;
+#define md5_n md5_count.md5_count64
+#define md5_n8 md5_count.md5_count8
+
+ u_int md5_i;
+ u_int8_t md5_buf[MD5_BUFLEN];
+} md5_ctxt;
+
+extern void md5_init(md5_ctxt *);
+extern void md5_loop(md5_ctxt *, u_int8_t *, u_int);
+extern void md5_pad(md5_ctxt *);
+extern void md5_result(u_int8_t *, md5_ctxt *);
+
+/* compatibility */
+#define MD5_CTX md5_ctxt
+#define MD5Init(x) md5_init((x))
+#define MD5Update(x, y, z) md5_loop((x), (y), (z))
+#define MD5Final(x, y) \
+do { \
+ md5_pad((y)); \
+ md5_result((x), (y)); \
+} while (0)
+
+#endif /* ! _NETINET6_MD5_H_*/
diff --git a/crypto/ocf/safe/safe.c b/crypto/ocf/safe/safe.c
new file mode 100644
index 000000000000..a77e3a2370f3
--- /dev/null
+++ b/crypto/ocf/safe/safe.c
@@ -0,0 +1,2288 @@
+/*-
+ * Linux port done by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2004-2007 David McCullough
+ * The license and original author are listed below.
+ *
+ * Copyright (c) 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003 Global Technology Associates, Inc.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+__FBSDID("$FreeBSD: src/sys/dev/safe/safe.c,v 1.18 2007/03/21 03:42:50 sam Exp $");
+ */
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+
+/*
+ * SafeNet SafeXcel-1141 hardware crypto accelerator
+ */
+
+#include <cryptodev.h>
+#include <uio.h>
+#include <safe/safereg.h>
+#include <safe/safevar.h>
+
+#if 1
+#define DPRINTF(a) do { \
+ if (debug) { \
+ printk("%s: ", sc ? \
+ device_get_nameunit(sc->sc_dev) : "safe"); \
+ printk a; \
+ } \
+ } while (0)
+#else
+#define DPRINTF(a)
+#endif
+
+/*
+ * until we find a cleaner way, include the BSD md5/sha1 code
+ * here
+ */
+#define HMAC_HACK 1
+#ifdef HMAC_HACK
+#define LITTLE_ENDIAN 1234
+#define BIG_ENDIAN 4321
+#ifdef __LITTLE_ENDIAN
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#ifdef __BIG_ENDIAN
+#define BYTE_ORDER BIG_ENDIAN
+#endif
+#include <safe/md5.h>
+#include <safe/md5.c>
+#include <safe/sha1.h>
+#include <safe/sha1.c>
+
+u_int8_t hmac_ipad_buffer[64] = {
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36
+};
+
+u_int8_t hmac_opad_buffer[64] = {
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
+ 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C
+};
+#endif /* HMAC_HACK */
+
+/* add proc entry for this */
+struct safe_stats safestats;
+
+#define debug safe_debug
+int safe_debug = 0;
+module_param(safe_debug, int, 0644);
+MODULE_PARM_DESC(safe_debug, "Enable debug");
+
+static void safe_callback(struct safe_softc *, struct safe_ringentry *);
+static void safe_feed(struct safe_softc *, struct safe_ringentry *);
+#if defined(CONFIG_OCF_RANDOMHARVEST) && !defined(SAFE_NO_RNG)
+static void safe_rng_init(struct safe_softc *);
+int safe_rngbufsize = 8; /* 32 bytes each read */
+module_param(safe_rngbufsize, int, 0644);
+MODULE_PARM_DESC(safe_rngbufsize, "RNG polling buffer size (32-bit words)");
+int safe_rngmaxalarm = 8; /* max alarms before reset */
+module_param(safe_rngmaxalarm, int, 0644);
+MODULE_PARM_DESC(safe_rngmaxalarm, "RNG max alarms before reset");
+#endif /* SAFE_NO_RNG */
+
+static void safe_totalreset(struct safe_softc *sc);
+static int safe_dmamap_aligned(struct safe_softc *sc, const struct safe_operand *op);
+static int safe_dmamap_uniform(struct safe_softc *sc, const struct safe_operand *op);
+static int safe_free_entry(struct safe_softc *sc, struct safe_ringentry *re);
+static int safe_kprocess(device_t dev, struct cryptkop *krp, int hint);
+static int safe_kstart(struct safe_softc *sc);
+static int safe_ksigbits(struct safe_softc *sc, struct crparam *cr);
+static void safe_kfeed(struct safe_softc *sc);
+static void safe_kpoll(unsigned long arg);
+static void safe_kload_reg(struct safe_softc *sc, u_int32_t off,
+ u_int32_t len, struct crparam *n);
+
+static int safe_newsession(device_t, u_int32_t *, struct cryptoini *);
+static int safe_freesession(device_t, u_int64_t);
+static int safe_process(device_t, struct cryptop *, int);
+
+static device_method_t safe_methods = {
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, safe_newsession),
+ DEVMETHOD(cryptodev_freesession,safe_freesession),
+ DEVMETHOD(cryptodev_process, safe_process),
+ DEVMETHOD(cryptodev_kprocess, safe_kprocess),
+};
+
+#define READ_REG(sc,r) readl((sc)->sc_base_addr + (r))
+#define WRITE_REG(sc,r,val) writel((val), (sc)->sc_base_addr + (r))
+
+#define SAFE_MAX_CHIPS 8
+static struct safe_softc *safe_chip_idx[SAFE_MAX_CHIPS];
+
+/*
+ * split our buffers up into safe DMAable byte fragments to avoid lockup
+ * bug in 1141 HW on rev 1.0.
+ */
+
+static int
+pci_map_linear(
+ struct safe_softc *sc,
+ struct safe_operand *buf,
+ void *addr,
+ int len)
+{
+ dma_addr_t tmp;
+ int chunk, tlen = len;
+
+ tmp = pci_map_single(sc->sc_pcidev, addr, len, PCI_DMA_BIDIRECTIONAL);
+
+ buf->mapsize += len;
+ while (len > 0) {
+ chunk = (len > sc->sc_max_dsize) ? sc->sc_max_dsize : len;
+ buf->segs[buf->nsegs].ds_addr = tmp;
+ buf->segs[buf->nsegs].ds_len = chunk;
+ buf->segs[buf->nsegs].ds_tlen = tlen;
+ buf->nsegs++;
+ tmp += chunk;
+ len -= chunk;
+ tlen = 0;
+ }
+ return 0;
+}
+
+/*
+ * map in a given uio buffer (great on some arches :-)
+ */
+
+static int
+pci_map_uio(struct safe_softc *sc, struct safe_operand *buf, struct uio *uio)
+{
+ struct iovec *iov = uio->uio_iov;
+ int n;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ buf->mapsize = 0;
+ buf->nsegs = 0;
+
+ for (n = 0; n < uio->uio_iovcnt; n++) {
+ pci_map_linear(sc, buf, iov->iov_base, iov->iov_len);
+ iov++;
+ }
+
+ /* identify this buffer by the first segment */
+ buf->map = (void *) buf->segs[0].ds_addr;
+ return(0);
+}
+
+/*
+ * map in a given sk_buff
+ */
+
+static int
+pci_map_skb(struct safe_softc *sc,struct safe_operand *buf,struct sk_buff *skb)
+{
+ int i;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ buf->mapsize = 0;
+ buf->nsegs = 0;
+
+ pci_map_linear(sc, buf, skb->data, skb_headlen(skb));
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ pci_map_linear(sc, buf,
+ page_address(skb_shinfo(skb)->frags[i].page) +
+ skb_shinfo(skb)->frags[i].page_offset,
+ skb_shinfo(skb)->frags[i].size);
+ }
+
+ /* identify this buffer by the first segment */
+ buf->map = (void *) buf->segs[0].ds_addr;
+ return(0);
+}
+
+
+#if 0 /* not needed at this time */
+static void
+pci_sync_operand(struct safe_softc *sc, struct safe_operand *buf)
+{
+ int i;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+ for (i = 0; i < buf->nsegs; i++)
+ pci_dma_sync_single_for_cpu(sc->sc_pcidev, buf->segs[i].ds_addr,
+ buf->segs[i].ds_len, PCI_DMA_BIDIRECTIONAL);
+}
+#endif
+
+static void
+pci_unmap_operand(struct safe_softc *sc, struct safe_operand *buf)
+{
+ int i;
+ DPRINTF(("%s()\n", __FUNCTION__));
+ for (i = 0; i < buf->nsegs; i++) {
+ if (buf->segs[i].ds_tlen) {
+ DPRINTF(("%s - unmap %d 0x%x %d\n", __FUNCTION__, i, buf->segs[i].ds_addr, buf->segs[i].ds_tlen));
+ pci_unmap_single(sc->sc_pcidev, buf->segs[i].ds_addr,
+ buf->segs[i].ds_tlen, PCI_DMA_BIDIRECTIONAL);
+ DPRINTF(("%s - unmap %d 0x%x %d done\n", __FUNCTION__, i, buf->segs[i].ds_addr, buf->segs[i].ds_tlen));
+ }
+ buf->segs[i].ds_addr = 0;
+ buf->segs[i].ds_len = 0;
+ buf->segs[i].ds_tlen = 0;
+ }
+ buf->nsegs = 0;
+ buf->mapsize = 0;
+ buf->map = 0;
+}
+
+
+/*
+ * SafeXcel Interrupt routine
+ */
+static irqreturn_t
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+safe_intr(int irq, void *arg)
+#else
+safe_intr(int irq, void *arg, struct pt_regs *regs)
+#endif
+{
+ struct safe_softc *sc = arg;
+ int stat;
+ unsigned long flags;
+
+ stat = READ_REG(sc, SAFE_HM_STAT);
+
+ DPRINTF(("%s(stat=0x%x)\n", __FUNCTION__, stat));
+
+ if (stat == 0) /* shared irq, not for us */
+ return IRQ_NONE;
+
+ WRITE_REG(sc, SAFE_HI_CLR, stat); /* IACK */
+
+ if ((stat & SAFE_INT_PE_DDONE)) {
+ /*
+ * Descriptor(s) done; scan the ring and
+ * process completed operations.
+ */
+ spin_lock_irqsave(&sc->sc_ringmtx, flags);
+ while (sc->sc_back != sc->sc_front) {
+ struct safe_ringentry *re = sc->sc_back;
+
+#ifdef SAFE_DEBUG
+ if (debug) {
+ safe_dump_ringstate(sc, __func__);
+ safe_dump_request(sc, __func__, re);
+ }
+#endif
+ /*
+ * safe_process marks ring entries that were allocated
+ * but not used with a csr of zero. This insures the
+ * ring front pointer never needs to be set backwards
+ * in the event that an entry is allocated but not used
+ * because of a setup error.
+ */
+ DPRINTF(("%s re->re_desc.d_csr=0x%x\n", __FUNCTION__, re->re_desc.d_csr));
+ if (re->re_desc.d_csr != 0) {
+ if (!SAFE_PE_CSR_IS_DONE(re->re_desc.d_csr)) {
+ DPRINTF(("%s !CSR_IS_DONE\n", __FUNCTION__));
+ break;
+ }
+ if (!SAFE_PE_LEN_IS_DONE(re->re_desc.d_len)) {
+ DPRINTF(("%s !LEN_IS_DONE\n", __FUNCTION__));
+ break;
+ }
+ sc->sc_nqchip--;
+ safe_callback(sc, re);
+ }
+ if (++(sc->sc_back) == sc->sc_ringtop)
+ sc->sc_back = sc->sc_ring;
+ }
+ spin_unlock_irqrestore(&sc->sc_ringmtx, flags);
+ }
+
+ /*
+ * Check to see if we got any DMA Error
+ */
+ if (stat & SAFE_INT_PE_ERROR) {
+ printk("%s: dmaerr dmastat %08x\n", device_get_nameunit(sc->sc_dev),
+ (int)READ_REG(sc, SAFE_PE_DMASTAT));
+ safestats.st_dmaerr++;
+ safe_totalreset(sc);
+#if 0
+ safe_feed(sc);
+#endif
+ }
+
+ if (sc->sc_needwakeup) { /* XXX check high watermark */
+ int wakeup = sc->sc_needwakeup & (CRYPTO_SYMQ|CRYPTO_ASYMQ);
+ DPRINTF(("%s: wakeup crypto %x\n", __func__,
+ sc->sc_needwakeup));
+ sc->sc_needwakeup &= ~wakeup;
+ crypto_unblock(sc->sc_cid, wakeup);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * safe_feed() - post a request to chip
+ */
+static void
+safe_feed(struct safe_softc *sc, struct safe_ringentry *re)
+{
+ DPRINTF(("%s()\n", __FUNCTION__));
+#ifdef SAFE_DEBUG
+ if (debug) {
+ safe_dump_ringstate(sc, __func__);
+ safe_dump_request(sc, __func__, re);
+ }
+#endif
+ sc->sc_nqchip++;
+ if (sc->sc_nqchip > safestats.st_maxqchip)
+ safestats.st_maxqchip = sc->sc_nqchip;
+ /* poke h/w to check descriptor ring, any value can be written */
+ WRITE_REG(sc, SAFE_HI_RD_DESCR, 0);
+}
+
+#define N(a) (sizeof(a) / sizeof (a[0]))
+static void
+safe_setup_enckey(struct safe_session *ses, caddr_t key)
+{
+ int i;
+
+ bcopy(key, ses->ses_key, ses->ses_klen / 8);
+
+ /* PE is little-endian, insure proper byte order */
+ for (i = 0; i < N(ses->ses_key); i++)
+ ses->ses_key[i] = htole32(ses->ses_key[i]);
+}
+
+static void
+safe_setup_mackey(struct safe_session *ses, int algo, caddr_t key, int klen)
+{
+#ifdef HMAC_HACK
+ MD5_CTX md5ctx;
+ SHA1_CTX sha1ctx;
+ int i;
+
+
+ for (i = 0; i < klen; i++)
+ key[i] ^= HMAC_IPAD_VAL;
+
+ if (algo == CRYPTO_MD5_HMAC) {
+ MD5Init(&md5ctx);
+ MD5Update(&md5ctx, key, klen);
+ MD5Update(&md5ctx, hmac_ipad_buffer, MD5_HMAC_BLOCK_LEN - klen);
+ bcopy(md5ctx.md5_st8, ses->ses_hminner, sizeof(md5ctx.md5_st8));
+ } else {
+ SHA1Init(&sha1ctx);
+ SHA1Update(&sha1ctx, key, klen);
+ SHA1Update(&sha1ctx, hmac_ipad_buffer,
+ SHA1_HMAC_BLOCK_LEN - klen);
+ bcopy(sha1ctx.h.b32, ses->ses_hminner, sizeof(sha1ctx.h.b32));
+ }
+
+ for (i = 0; i < klen; i++)
+ key[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);
+
+ if (algo == CRYPTO_MD5_HMAC) {
+ MD5Init(&md5ctx);
+ MD5Update(&md5ctx, key, klen);
+ MD5Update(&md5ctx, hmac_opad_buffer, MD5_HMAC_BLOCK_LEN - klen);
+ bcopy(md5ctx.md5_st8, ses->ses_hmouter, sizeof(md5ctx.md5_st8));
+ } else {
+ SHA1Init(&sha1ctx);
+ SHA1Update(&sha1ctx, key, klen);
+ SHA1Update(&sha1ctx, hmac_opad_buffer,
+ SHA1_HMAC_BLOCK_LEN - klen);
+ bcopy(sha1ctx.h.b32, ses->ses_hmouter, sizeof(sha1ctx.h.b32));
+ }
+
+ for (i = 0; i < klen; i++)
+ key[i] ^= HMAC_OPAD_VAL;
+
+#if 0
+ /*
+ * this code prevents SHA working on a BE host,
+ * so it is obviously wrong. I think the byte
+ * swap setup we do with the chip fixes this for us
+ */
+
+ /* PE is little-endian, insure proper byte order */
+ for (i = 0; i < N(ses->ses_hminner); i++) {
+ ses->ses_hminner[i] = htole32(ses->ses_hminner[i]);
+ ses->ses_hmouter[i] = htole32(ses->ses_hmouter[i]);
+ }
+#endif
+#else /* HMAC_HACK */
+ printk("safe: md5/sha not implemented\n");
+#endif /* HMAC_HACK */
+}
+#undef N
+
+/*
+ * Allocate a new 'session' and return an encoded session id. 'sidp'
+ * contains our registration id, and should contain an encoded session
+ * id on successful allocation.
+ */
+static int
+safe_newsession(device_t dev, u_int32_t *sidp, struct cryptoini *cri)
+{
+ struct safe_softc *sc = device_get_softc(dev);
+ struct cryptoini *c, *encini = NULL, *macini = NULL;
+ struct safe_session *ses = NULL;
+ int sesn;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ if (sidp == NULL || cri == NULL || sc == NULL)
+ return (EINVAL);
+
+ for (c = cri; c != NULL; c = c->cri_next) {
+ if (c->cri_alg == CRYPTO_MD5_HMAC ||
+ c->cri_alg == CRYPTO_SHA1_HMAC ||
+ c->cri_alg == CRYPTO_NULL_HMAC) {
+ if (macini)
+ return (EINVAL);
+ macini = c;
+ } else if (c->cri_alg == CRYPTO_DES_CBC ||
+ c->cri_alg == CRYPTO_3DES_CBC ||
+ c->cri_alg == CRYPTO_AES_CBC ||
+ c->cri_alg == CRYPTO_NULL_CBC) {
+ if (encini)
+ return (EINVAL);
+ encini = c;
+ } else
+ return (EINVAL);
+ }
+ if (encini == NULL && macini == NULL)
+ return (EINVAL);
+ if (encini) { /* validate key length */
+ switch (encini->cri_alg) {
+ case CRYPTO_DES_CBC:
+ if (encini->cri_klen != 64)
+ return (EINVAL);
+ break;
+ case CRYPTO_3DES_CBC:
+ if (encini->cri_klen != 192)
+ return (EINVAL);
+ break;
+ case CRYPTO_AES_CBC:
+ if (encini->cri_klen != 128 &&
+ encini->cri_klen != 192 &&
+ encini->cri_klen != 256)
+ return (EINVAL);
+ break;
+ }
+ }
+
+ if (sc->sc_sessions == NULL) {
+ ses = sc->sc_sessions = (struct safe_session *)
+ kmalloc(sizeof(struct safe_session), SLAB_ATOMIC);
+ if (ses == NULL)
+ return (ENOMEM);
+ memset(ses, 0, sizeof(struct safe_session));
+ sesn = 0;
+ sc->sc_nsessions = 1;
+ } else {
+ for (sesn = 0; sesn < sc->sc_nsessions; sesn++) {
+ if (sc->sc_sessions[sesn].ses_used == 0) {
+ ses = &sc->sc_sessions[sesn];
+ break;
+ }
+ }
+
+ if (ses == NULL) {
+ sesn = sc->sc_nsessions;
+ ses = (struct safe_session *)
+ kmalloc((sesn + 1) * sizeof(struct safe_session), SLAB_ATOMIC);
+ if (ses == NULL)
+ return (ENOMEM);
+ memset(ses, 0, (sesn + 1) * sizeof(struct safe_session));
+ bcopy(sc->sc_sessions, ses, sesn *
+ sizeof(struct safe_session));
+ bzero(sc->sc_sessions, sesn *
+ sizeof(struct safe_session));
+ kfree(sc->sc_sessions);
+ sc->sc_sessions = ses;
+ ses = &sc->sc_sessions[sesn];
+ sc->sc_nsessions++;
+ }
+ }
+
+ bzero(ses, sizeof(struct safe_session));
+ ses->ses_used = 1;
+
+ if (encini) {
+ /* get an IV */
+ /* XXX may read fewer than requested */
+ read_random(ses->ses_iv, sizeof(ses->ses_iv));
+
+ ses->ses_klen = encini->cri_klen;
+ if (encini->cri_key != NULL)
+ safe_setup_enckey(ses, encini->cri_key);
+ }
+
+ if (macini) {
+ ses->ses_mlen = macini->cri_mlen;
+ if (ses->ses_mlen == 0) {
+ if (macini->cri_alg == CRYPTO_MD5_HMAC)
+ ses->ses_mlen = MD5_HASH_LEN;
+ else
+ ses->ses_mlen = SHA1_HASH_LEN;
+ }
+
+ if (macini->cri_key != NULL) {
+ safe_setup_mackey(ses, macini->cri_alg, macini->cri_key,
+ macini->cri_klen / 8);
+ }
+ }
+
+ *sidp = SAFE_SID(device_get_unit(sc->sc_dev), sesn);
+ return (0);
+}
+
+/*
+ * Deallocate a session.
+ */
+static int
+safe_freesession(device_t dev, u_int64_t tid)
+{
+ struct safe_softc *sc = device_get_softc(dev);
+ int session, ret;
+ u_int32_t sid = ((u_int32_t) tid) & 0xffffffff;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ if (sc == NULL)
+ return (EINVAL);
+
+ session = SAFE_SESSION(sid);
+ if (session < sc->sc_nsessions) {
+ bzero(&sc->sc_sessions[session], sizeof(sc->sc_sessions[session]));
+ ret = 0;
+ } else
+ ret = EINVAL;
+ return (ret);
+}
+
+
+static int
+safe_process(device_t dev, struct cryptop *crp, int hint)
+{
+ struct safe_softc *sc = device_get_softc(dev);
+ int err = 0, i, nicealign, uniform;
+ struct cryptodesc *crd1, *crd2, *maccrd, *enccrd;
+ int bypass, oplen, ivsize;
+ caddr_t iv;
+ int16_t coffset;
+ struct safe_session *ses;
+ struct safe_ringentry *re;
+ struct safe_sarec *sa;
+ struct safe_pdesc *pd;
+ u_int32_t cmd0, cmd1, staterec;
+ unsigned long flags;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ if (crp == NULL || crp->crp_callback == NULL || sc == NULL) {
+ safestats.st_invalid++;
+ return (EINVAL);
+ }
+ if (SAFE_SESSION(crp->crp_sid) >= sc->sc_nsessions) {
+ safestats.st_badsession++;
+ return (EINVAL);
+ }
+
+ spin_lock_irqsave(&sc->sc_ringmtx, flags);
+ if (sc->sc_front == sc->sc_back && sc->sc_nqchip != 0) {
+ safestats.st_ringfull++;
+ sc->sc_needwakeup |= CRYPTO_SYMQ;
+ spin_unlock_irqrestore(&sc->sc_ringmtx, flags);
+ return (ERESTART);
+ }
+ re = sc->sc_front;
+
+ staterec = re->re_sa.sa_staterec; /* save */
+ /* NB: zero everything but the PE descriptor */
+ bzero(&re->re_sa, sizeof(struct safe_ringentry) - sizeof(re->re_desc));
+ re->re_sa.sa_staterec = staterec; /* restore */
+
+ re->re_crp = crp;
+ re->re_sesn = SAFE_SESSION(crp->crp_sid);
+
+ re->re_src.nsegs = 0;
+ re->re_dst.nsegs = 0;
+
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ re->re_src_skb = (struct sk_buff *)crp->crp_buf;
+ re->re_dst_skb = (struct sk_buff *)crp->crp_buf;
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ re->re_src_io = (struct uio *)crp->crp_buf;
+ re->re_dst_io = (struct uio *)crp->crp_buf;
+ } else {
+ safestats.st_badflags++;
+ err = EINVAL;
+ goto errout; /* XXX we don't handle contiguous blocks! */
+ }
+
+ sa = &re->re_sa;
+ ses = &sc->sc_sessions[re->re_sesn];
+
+ crd1 = crp->crp_desc;
+ if (crd1 == NULL) {
+ safestats.st_nodesc++;
+ err = EINVAL;
+ goto errout;
+ }
+ crd2 = crd1->crd_next;
+
+ cmd0 = SAFE_SA_CMD0_BASIC; /* basic group operation */
+ cmd1 = 0;
+ if (crd2 == NULL) {
+ if (crd1->crd_alg == CRYPTO_MD5_HMAC ||
+ crd1->crd_alg == CRYPTO_SHA1_HMAC ||
+ crd1->crd_alg == CRYPTO_NULL_HMAC) {
+ maccrd = crd1;
+ enccrd = NULL;
+ cmd0 |= SAFE_SA_CMD0_OP_HASH;
+ } else if (crd1->crd_alg == CRYPTO_DES_CBC ||
+ crd1->crd_alg == CRYPTO_3DES_CBC ||
+ crd1->crd_alg == CRYPTO_AES_CBC ||
+ crd1->crd_alg == CRYPTO_NULL_CBC) {
+ maccrd = NULL;
+ enccrd = crd1;
+ cmd0 |= SAFE_SA_CMD0_OP_CRYPT;
+ } else {
+ safestats.st_badalg++;
+ err = EINVAL;
+ goto errout;
+ }
+ } else {
+ if ((crd1->crd_alg == CRYPTO_MD5_HMAC ||
+ crd1->crd_alg == CRYPTO_SHA1_HMAC ||
+ crd1->crd_alg == CRYPTO_NULL_HMAC) &&
+ (crd2->crd_alg == CRYPTO_DES_CBC ||
+ crd2->crd_alg == CRYPTO_3DES_CBC ||
+ crd2->crd_alg == CRYPTO_AES_CBC ||
+ crd2->crd_alg == CRYPTO_NULL_CBC) &&
+ ((crd2->crd_flags & CRD_F_ENCRYPT) == 0)) {
+ maccrd = crd1;
+ enccrd = crd2;
+ } else if ((crd1->crd_alg == CRYPTO_DES_CBC ||
+ crd1->crd_alg == CRYPTO_3DES_CBC ||
+ crd1->crd_alg == CRYPTO_AES_CBC ||
+ crd1->crd_alg == CRYPTO_NULL_CBC) &&
+ (crd2->crd_alg == CRYPTO_MD5_HMAC ||
+ crd2->crd_alg == CRYPTO_SHA1_HMAC ||
+ crd2->crd_alg == CRYPTO_NULL_HMAC) &&
+ (crd1->crd_flags & CRD_F_ENCRYPT)) {
+ enccrd = crd1;
+ maccrd = crd2;
+ } else {
+ safestats.st_badalg++;
+ err = EINVAL;
+ goto errout;
+ }
+ cmd0 |= SAFE_SA_CMD0_OP_BOTH;
+ }
+
+ if (enccrd) {
+ if (enccrd->crd_flags & CRD_F_KEY_EXPLICIT)
+ safe_setup_enckey(ses, enccrd->crd_key);
+
+ if (enccrd->crd_alg == CRYPTO_DES_CBC) {
+ cmd0 |= SAFE_SA_CMD0_DES;
+ cmd1 |= SAFE_SA_CMD1_CBC;
+ ivsize = 2*sizeof(u_int32_t);
+ } else if (enccrd->crd_alg == CRYPTO_3DES_CBC) {
+ cmd0 |= SAFE_SA_CMD0_3DES;
+ cmd1 |= SAFE_SA_CMD1_CBC;
+ ivsize = 2*sizeof(u_int32_t);
+ } else if (enccrd->crd_alg == CRYPTO_AES_CBC) {
+ cmd0 |= SAFE_SA_CMD0_AES;
+ cmd1 |= SAFE_SA_CMD1_CBC;
+ if (ses->ses_klen == 128)
+ cmd1 |= SAFE_SA_CMD1_AES128;
+ else if (ses->ses_klen == 192)
+ cmd1 |= SAFE_SA_CMD1_AES192;
+ else
+ cmd1 |= SAFE_SA_CMD1_AES256;
+ ivsize = 4*sizeof(u_int32_t);
+ } else {
+ cmd0 |= SAFE_SA_CMD0_CRYPT_NULL;
+ ivsize = 0;
+ }
+
+ /*
+ * Setup encrypt/decrypt state. When using basic ops
+ * we can't use an inline IV because hash/crypt offset
+ * must be from the end of the IV to the start of the
+ * crypt data and this leaves out the preceding header
+ * from the hash calculation. Instead we place the IV
+ * in the state record and set the hash/crypt offset to
+ * copy both the header+IV.
+ */
+ if (enccrd->crd_flags & CRD_F_ENCRYPT) {
+ cmd0 |= SAFE_SA_CMD0_OUTBOUND;
+
+ if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
+ iv = enccrd->crd_iv;
+ else
+ iv = (caddr_t) ses->ses_iv;
+ if ((enccrd->crd_flags & CRD_F_IV_PRESENT) == 0) {
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ enccrd->crd_inject, ivsize, iv);
+ }
+ bcopy(iv, re->re_sastate.sa_saved_iv, ivsize);
+ /* make iv LE */
+ for (i = 0; i < ivsize/sizeof(re->re_sastate.sa_saved_iv[0]); i++)
+ re->re_sastate.sa_saved_iv[i] =
+ cpu_to_le32(re->re_sastate.sa_saved_iv[i]);
+ cmd0 |= SAFE_SA_CMD0_IVLD_STATE | SAFE_SA_CMD0_SAVEIV;
+ re->re_flags |= SAFE_QFLAGS_COPYOUTIV;
+ } else {
+ cmd0 |= SAFE_SA_CMD0_INBOUND;
+
+ if (enccrd->crd_flags & CRD_F_IV_EXPLICIT) {
+ bcopy(enccrd->crd_iv,
+ re->re_sastate.sa_saved_iv, ivsize);
+ } else {
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ enccrd->crd_inject, ivsize,
+ (caddr_t)re->re_sastate.sa_saved_iv);
+ }
+ /* make iv LE */
+ for (i = 0; i < ivsize/sizeof(re->re_sastate.sa_saved_iv[0]); i++)
+ re->re_sastate.sa_saved_iv[i] =
+ cpu_to_le32(re->re_sastate.sa_saved_iv[i]);
+ cmd0 |= SAFE_SA_CMD0_IVLD_STATE;
+ }
+ /*
+ * For basic encryption use the zero pad algorithm.
+ * This pads results to an 8-byte boundary and
+ * suppresses padding verification for inbound (i.e.
+ * decrypt) operations.
+ *
+ * NB: Not sure if the 8-byte pad boundary is a problem.
+ */
+ cmd0 |= SAFE_SA_CMD0_PAD_ZERO;
+
+ /* XXX assert key bufs have the same size */
+ bcopy(ses->ses_key, sa->sa_key, sizeof(sa->sa_key));
+ }
+
+ if (maccrd) {
+ if (maccrd->crd_flags & CRD_F_KEY_EXPLICIT) {
+ safe_setup_mackey(ses, maccrd->crd_alg,
+ maccrd->crd_key, maccrd->crd_klen / 8);
+ }
+
+ if (maccrd->crd_alg == CRYPTO_MD5_HMAC) {
+ cmd0 |= SAFE_SA_CMD0_MD5;
+ cmd1 |= SAFE_SA_CMD1_HMAC; /* NB: enable HMAC */
+ } else if (maccrd->crd_alg == CRYPTO_SHA1_HMAC) {
+ cmd0 |= SAFE_SA_CMD0_SHA1;
+ cmd1 |= SAFE_SA_CMD1_HMAC; /* NB: enable HMAC */
+ } else {
+ cmd0 |= SAFE_SA_CMD0_HASH_NULL;
+ }
+ /*
+ * Digest data is loaded from the SA and the hash
+ * result is saved to the state block where we
+ * retrieve it for return to the caller.
+ */
+ /* XXX assert digest bufs have the same size */
+ bcopy(ses->ses_hminner, sa->sa_indigest,
+ sizeof(sa->sa_indigest));
+ bcopy(ses->ses_hmouter, sa->sa_outdigest,
+ sizeof(sa->sa_outdigest));
+
+ cmd0 |= SAFE_SA_CMD0_HSLD_SA | SAFE_SA_CMD0_SAVEHASH;
+ re->re_flags |= SAFE_QFLAGS_COPYOUTICV;
+ }
+
+ if (enccrd && maccrd) {
+ /*
+ * The offset from hash data to the start of
+ * crypt data is the difference in the skips.
+ */
+ bypass = maccrd->crd_skip;
+ coffset = enccrd->crd_skip - maccrd->crd_skip;
+ if (coffset < 0) {
+ DPRINTF(("%s: hash does not precede crypt; "
+ "mac skip %u enc skip %u\n",
+ __func__, maccrd->crd_skip, enccrd->crd_skip));
+ safestats.st_skipmismatch++;
+ err = EINVAL;
+ goto errout;
+ }
+ oplen = enccrd->crd_skip + enccrd->crd_len;
+ if (maccrd->crd_skip + maccrd->crd_len != oplen) {
+ DPRINTF(("%s: hash amount %u != crypt amount %u\n",
+ __func__, maccrd->crd_skip + maccrd->crd_len,
+ oplen));
+ safestats.st_lenmismatch++;
+ err = EINVAL;
+ goto errout;
+ }
+#ifdef SAFE_DEBUG
+ if (debug) {
+ printf("mac: skip %d, len %d, inject %d\n",
+ maccrd->crd_skip, maccrd->crd_len,
+ maccrd->crd_inject);
+ printf("enc: skip %d, len %d, inject %d\n",
+ enccrd->crd_skip, enccrd->crd_len,
+ enccrd->crd_inject);
+ printf("bypass %d coffset %d oplen %d\n",
+ bypass, coffset, oplen);
+ }
+#endif
+ if (coffset & 3) { /* offset must be 32-bit aligned */
+ DPRINTF(("%s: coffset %u misaligned\n",
+ __func__, coffset));
+ safestats.st_coffmisaligned++;
+ err = EINVAL;
+ goto errout;
+ }
+ coffset >>= 2;
+ if (coffset > 255) { /* offset must be <256 dwords */
+ DPRINTF(("%s: coffset %u too big\n",
+ __func__, coffset));
+ safestats.st_cofftoobig++;
+ err = EINVAL;
+ goto errout;
+ }
+ /*
+ * Tell the hardware to copy the header to the output.
+ * The header is defined as the data from the end of
+ * the bypass to the start of data to be encrypted.
+ * Typically this is the inline IV. Note that you need
+ * to do this even if src+dst are the same; it appears
+ * that w/o this bit the crypted data is written
+ * immediately after the bypass data.
+ */
+ cmd1 |= SAFE_SA_CMD1_HDRCOPY;
+ /*
+ * Disable IP header mutable bit handling. This is
+ * needed to get correct HMAC calculations.
+ */
+ cmd1 |= SAFE_SA_CMD1_MUTABLE;
+ } else {
+ if (enccrd) {
+ bypass = enccrd->crd_skip;
+ oplen = bypass + enccrd->crd_len;
+ } else {
+ bypass = maccrd->crd_skip;
+ oplen = bypass + maccrd->crd_len;
+ }
+ coffset = 0;
+ }
+ /* XXX verify multiple of 4 when using s/g */
+ if (bypass > 96) { /* bypass offset must be <= 96 bytes */
+ DPRINTF(("%s: bypass %u too big\n", __func__, bypass));
+ safestats.st_bypasstoobig++;
+ err = EINVAL;
+ goto errout;
+ }
+
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ if (pci_map_skb(sc, &re->re_src, re->re_src_skb)) {
+ safestats.st_noload++;
+ err = ENOMEM;
+ goto errout;
+ }
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ if (pci_map_uio(sc, &re->re_src, re->re_src_io)) {
+ safestats.st_noload++;
+ err = ENOMEM;
+ goto errout;
+ }
+ }
+ nicealign = safe_dmamap_aligned(sc, &re->re_src);
+ uniform = safe_dmamap_uniform(sc, &re->re_src);
+
+ DPRINTF(("src nicealign %u uniform %u nsegs %u\n",
+ nicealign, uniform, re->re_src.nsegs));
+ if (re->re_src.nsegs > 1) {
+ re->re_desc.d_src = sc->sc_spalloc.dma_paddr +
+ ((caddr_t) sc->sc_spfree - (caddr_t) sc->sc_spring);
+ for (i = 0; i < re->re_src_nsegs; i++) {
+ /* NB: no need to check if there's space */
+ pd = sc->sc_spfree;
+ if (++(sc->sc_spfree) == sc->sc_springtop)
+ sc->sc_spfree = sc->sc_spring;
+
+ KASSERT((pd->pd_flags&3) == 0 ||
+ (pd->pd_flags&3) == SAFE_PD_DONE,
+ ("bogus source particle descriptor; flags %x",
+ pd->pd_flags));
+ pd->pd_addr = re->re_src_segs[i].ds_addr;
+ pd->pd_size = re->re_src_segs[i].ds_len;
+ pd->pd_flags = SAFE_PD_READY;
+ }
+ cmd0 |= SAFE_SA_CMD0_IGATHER;
+ } else {
+ /*
+ * No need for gather, reference the operand directly.
+ */
+ re->re_desc.d_src = re->re_src_segs[0].ds_addr;
+ }
+
+ if (enccrd == NULL && maccrd != NULL) {
+ /*
+ * Hash op; no destination needed.
+ */
+ } else {
+ if (crp->crp_flags & (CRYPTO_F_IOV|CRYPTO_F_SKBUF)) {
+ if (!nicealign) {
+ safestats.st_iovmisaligned++;
+ err = EINVAL;
+ goto errout;
+ }
+ if (uniform != 1) {
+ device_printf(sc->sc_dev, "!uniform source\n");
+ if (!uniform) {
+ /*
+ * There's no way to handle the DMA
+ * requirements with this uio. We
+ * could create a separate DMA area for
+ * the result and then copy it back,
+ * but for now we just bail and return
+ * an error. Note that uio requests
+ * > SAFE_MAX_DSIZE are handled because
+ * the DMA map and segment list for the
+ * destination wil result in a
+ * destination particle list that does
+ * the necessary scatter DMA.
+ */
+ safestats.st_iovnotuniform++;
+ err = EINVAL;
+ goto errout;
+ }
+ } else
+ re->re_dst = re->re_src;
+ } else {
+ safestats.st_badflags++;
+ err = EINVAL;
+ goto errout;
+ }
+
+ if (re->re_dst.nsegs > 1) {
+ re->re_desc.d_dst = sc->sc_dpalloc.dma_paddr +
+ ((caddr_t) sc->sc_dpfree - (caddr_t) sc->sc_dpring);
+ for (i = 0; i < re->re_dst_nsegs; i++) {
+ pd = sc->sc_dpfree;
+ KASSERT((pd->pd_flags&3) == 0 ||
+ (pd->pd_flags&3) == SAFE_PD_DONE,
+ ("bogus dest particle descriptor; flags %x",
+ pd->pd_flags));
+ if (++(sc->sc_dpfree) == sc->sc_dpringtop)
+ sc->sc_dpfree = sc->sc_dpring;
+ pd->pd_addr = re->re_dst_segs[i].ds_addr;
+ pd->pd_flags = SAFE_PD_READY;
+ }
+ cmd0 |= SAFE_SA_CMD0_OSCATTER;
+ } else {
+ /*
+ * No need for scatter, reference the operand directly.
+ */
+ re->re_desc.d_dst = re->re_dst_segs[0].ds_addr;
+ }
+ }
+
+ /*
+ * All done with setup; fillin the SA command words
+ * and the packet engine descriptor. The operation
+ * is now ready for submission to the hardware.
+ */
+ sa->sa_cmd0 = cmd0 | SAFE_SA_CMD0_IPCI | SAFE_SA_CMD0_OPCI;
+ sa->sa_cmd1 = cmd1
+ | (coffset << SAFE_SA_CMD1_OFFSET_S)
+ | SAFE_SA_CMD1_SAREV1 /* Rev 1 SA data structure */
+ | SAFE_SA_CMD1_SRPCI
+ ;
+ /*
+ * NB: the order of writes is important here. In case the
+ * chip is scanning the ring because of an outstanding request
+ * it might nab this one too. In that case we need to make
+ * sure the setup is complete before we write the length
+ * field of the descriptor as it signals the descriptor is
+ * ready for processing.
+ */
+ re->re_desc.d_csr = SAFE_PE_CSR_READY | SAFE_PE_CSR_SAPCI;
+ if (maccrd)
+ re->re_desc.d_csr |= SAFE_PE_CSR_LOADSA | SAFE_PE_CSR_HASHFINAL;
+ wmb();
+ re->re_desc.d_len = oplen
+ | SAFE_PE_LEN_READY
+ | (bypass << SAFE_PE_LEN_BYPASS_S)
+ ;
+
+ safestats.st_ipackets++;
+ safestats.st_ibytes += oplen;
+
+ if (++(sc->sc_front) == sc->sc_ringtop)
+ sc->sc_front = sc->sc_ring;
+
+ /* XXX honor batching */
+ safe_feed(sc, re);
+ spin_unlock_irqrestore(&sc->sc_ringmtx, flags);
+ return (0);
+
+errout:
+ if (re->re_src.map != re->re_dst.map)
+ pci_unmap_operand(sc, &re->re_dst);
+ if (re->re_src.map)
+ pci_unmap_operand(sc, &re->re_src);
+ spin_unlock_irqrestore(&sc->sc_ringmtx, flags);
+ if (err != ERESTART) {
+ crp->crp_etype = err;
+ crypto_done(crp);
+ } else {
+ sc->sc_needwakeup |= CRYPTO_SYMQ;
+ }
+ return (err);
+}
+
+static void
+safe_callback(struct safe_softc *sc, struct safe_ringentry *re)
+{
+ struct cryptop *crp = (struct cryptop *)re->re_crp;
+ struct cryptodesc *crd;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ safestats.st_opackets++;
+ safestats.st_obytes += re->re_dst.mapsize;
+
+ if (re->re_desc.d_csr & SAFE_PE_CSR_STATUS) {
+ device_printf(sc->sc_dev, "csr 0x%x cmd0 0x%x cmd1 0x%x\n",
+ re->re_desc.d_csr,
+ re->re_sa.sa_cmd0, re->re_sa.sa_cmd1);
+ safestats.st_peoperr++;
+ crp->crp_etype = EIO; /* something more meaningful? */
+ }
+
+ if (re->re_dst.map != NULL && re->re_dst.map != re->re_src.map)
+ pci_unmap_operand(sc, &re->re_dst);
+ pci_unmap_operand(sc, &re->re_src);
+
+ /*
+ * If result was written to a differet mbuf chain, swap
+ * it in as the return value and reclaim the original.
+ */
+ if ((crp->crp_flags & CRYPTO_F_SKBUF) && re->re_src_skb != re->re_dst_skb) {
+ device_printf(sc->sc_dev, "no CRYPTO_F_SKBUF swapping support\n");
+ /* kfree_skb(skb) */
+ /* crp->crp_buf = (caddr_t)re->re_dst_skb */
+ return;
+ }
+
+ if (re->re_flags & SAFE_QFLAGS_COPYOUTIV) {
+ /* copy out IV for future use */
+ for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
+ int i;
+ int ivsize;
+
+ if (crd->crd_alg == CRYPTO_DES_CBC ||
+ crd->crd_alg == CRYPTO_3DES_CBC) {
+ ivsize = 2*sizeof(u_int32_t);
+ } else if (crd->crd_alg == CRYPTO_AES_CBC) {
+ ivsize = 4*sizeof(u_int32_t);
+ } else
+ continue;
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ crd->crd_skip + crd->crd_len - ivsize, ivsize,
+ (caddr_t)sc->sc_sessions[re->re_sesn].ses_iv);
+ for (i = 0;
+ i < ivsize/sizeof(sc->sc_sessions[re->re_sesn].ses_iv[0]);
+ i++)
+ sc->sc_sessions[re->re_sesn].ses_iv[i] =
+ cpu_to_le32(sc->sc_sessions[re->re_sesn].ses_iv[i]);
+ break;
+ }
+ }
+
+ if (re->re_flags & SAFE_QFLAGS_COPYOUTICV) {
+ /* copy out ICV result */
+ for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
+ if (!(crd->crd_alg == CRYPTO_MD5_HMAC ||
+ crd->crd_alg == CRYPTO_SHA1_HMAC ||
+ crd->crd_alg == CRYPTO_NULL_HMAC))
+ continue;
+ if (crd->crd_alg == CRYPTO_SHA1_HMAC) {
+ /*
+ * SHA-1 ICV's are byte-swapped; fix 'em up
+ * before copy them to their destination.
+ */
+ re->re_sastate.sa_saved_indigest[0] =
+ cpu_to_be32(re->re_sastate.sa_saved_indigest[0]);
+ re->re_sastate.sa_saved_indigest[1] =
+ cpu_to_be32(re->re_sastate.sa_saved_indigest[1]);
+ re->re_sastate.sa_saved_indigest[2] =
+ cpu_to_be32(re->re_sastate.sa_saved_indigest[2]);
+ } else {
+ re->re_sastate.sa_saved_indigest[0] =
+ cpu_to_le32(re->re_sastate.sa_saved_indigest[0]);
+ re->re_sastate.sa_saved_indigest[1] =
+ cpu_to_le32(re->re_sastate.sa_saved_indigest[1]);
+ re->re_sastate.sa_saved_indigest[2] =
+ cpu_to_le32(re->re_sastate.sa_saved_indigest[2]);
+ }
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ crd->crd_inject,
+ sc->sc_sessions[re->re_sesn].ses_mlen,
+ (caddr_t)re->re_sastate.sa_saved_indigest);
+ break;
+ }
+ }
+ crypto_done(crp);
+}
+
+
+#if defined(CONFIG_OCF_RANDOMHARVEST) && !defined(SAFE_NO_RNG)
+#define SAFE_RNG_MAXWAIT 1000
+
+static void
+safe_rng_init(struct safe_softc *sc)
+{
+ u_int32_t w, v;
+ int i;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ WRITE_REG(sc, SAFE_RNG_CTRL, 0);
+ /* use default value according to the manual */
+ WRITE_REG(sc, SAFE_RNG_CNFG, 0x834); /* magic from SafeNet */
+ WRITE_REG(sc, SAFE_RNG_ALM_CNT, 0);
+
+ /*
+ * There is a bug in rev 1.0 of the 1140 that when the RNG
+ * is brought out of reset the ready status flag does not
+ * work until the RNG has finished its internal initialization.
+ *
+ * So in order to determine the device is through its
+ * initialization we must read the data register, using the
+ * status reg in the read in case it is initialized. Then read
+ * the data register until it changes from the first read.
+ * Once it changes read the data register until it changes
+ * again. At this time the RNG is considered initialized.
+ * This could take between 750ms - 1000ms in time.
+ */
+ i = 0;
+ w = READ_REG(sc, SAFE_RNG_OUT);
+ do {
+ v = READ_REG(sc, SAFE_RNG_OUT);
+ if (v != w) {
+ w = v;
+ break;
+ }
+ DELAY(10);
+ } while (++i < SAFE_RNG_MAXWAIT);
+
+ /* Wait Until data changes again */
+ i = 0;
+ do {
+ v = READ_REG(sc, SAFE_RNG_OUT);
+ if (v != w)
+ break;
+ DELAY(10);
+ } while (++i < SAFE_RNG_MAXWAIT);
+}
+
+static __inline void
+safe_rng_disable_short_cycle(struct safe_softc *sc)
+{
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ WRITE_REG(sc, SAFE_RNG_CTRL,
+ READ_REG(sc, SAFE_RNG_CTRL) &~ SAFE_RNG_CTRL_SHORTEN);
+}
+
+static __inline void
+safe_rng_enable_short_cycle(struct safe_softc *sc)
+{
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ WRITE_REG(sc, SAFE_RNG_CTRL,
+ READ_REG(sc, SAFE_RNG_CTRL) | SAFE_RNG_CTRL_SHORTEN);
+}
+
+static __inline u_int32_t
+safe_rng_read(struct safe_softc *sc)
+{
+ int i;
+
+ i = 0;
+ while (READ_REG(sc, SAFE_RNG_STAT) != 0 && ++i < SAFE_RNG_MAXWAIT)
+ ;
+ return READ_REG(sc, SAFE_RNG_OUT);
+}
+
+static int
+safe_read_random(void *arg, u_int32_t *buf, int maxwords)
+{
+ struct safe_softc *sc = (struct safe_softc *) arg;
+ int i, rc;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ safestats.st_rng++;
+ /*
+ * Fetch the next block of data.
+ */
+ if (maxwords > safe_rngbufsize)
+ maxwords = safe_rngbufsize;
+ if (maxwords > SAFE_RNG_MAXBUFSIZ)
+ maxwords = SAFE_RNG_MAXBUFSIZ;
+retry:
+ /* read as much as we can */
+ for (rc = 0; rc < maxwords; rc++) {
+ if (READ_REG(sc, SAFE_RNG_STAT) != 0)
+ break;
+ buf[rc] = READ_REG(sc, SAFE_RNG_OUT);
+ }
+ if (rc == 0)
+ return 0;
+ /*
+ * Check the comparator alarm count and reset the h/w if
+ * it exceeds our threshold. This guards against the
+ * hardware oscillators resonating with external signals.
+ */
+ if (READ_REG(sc, SAFE_RNG_ALM_CNT) > safe_rngmaxalarm) {
+ u_int32_t freq_inc, w;
+
+ DPRINTF(("%s: alarm count %u exceeds threshold %u\n", __func__,
+ (unsigned)READ_REG(sc, SAFE_RNG_ALM_CNT), safe_rngmaxalarm));
+ safestats.st_rngalarm++;
+ safe_rng_enable_short_cycle(sc);
+ freq_inc = 18;
+ for (i = 0; i < 64; i++) {
+ w = READ_REG(sc, SAFE_RNG_CNFG);
+ freq_inc = ((w + freq_inc) & 0x3fL);
+ w = ((w & ~0x3fL) | freq_inc);
+ WRITE_REG(sc, SAFE_RNG_CNFG, w);
+
+ WRITE_REG(sc, SAFE_RNG_ALM_CNT, 0);
+
+ (void) safe_rng_read(sc);
+ DELAY(25);
+
+ if (READ_REG(sc, SAFE_RNG_ALM_CNT) == 0) {
+ safe_rng_disable_short_cycle(sc);
+ goto retry;
+ }
+ freq_inc = 1;
+ }
+ safe_rng_disable_short_cycle(sc);
+ } else
+ WRITE_REG(sc, SAFE_RNG_ALM_CNT, 0);
+
+ return(rc);
+}
+#endif /* defined(CONFIG_OCF_RANDOMHARVEST) && !defined(SAFE_NO_RNG) */
+
+
+/*
+ * Resets the board. Values in the regesters are left as is
+ * from the reset (i.e. initial values are assigned elsewhere).
+ */
+static void
+safe_reset_board(struct safe_softc *sc)
+{
+ u_int32_t v;
+ /*
+ * Reset the device. The manual says no delay
+ * is needed between marking and clearing reset.
+ */
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ v = READ_REG(sc, SAFE_PE_DMACFG) &~
+ (SAFE_PE_DMACFG_PERESET | SAFE_PE_DMACFG_PDRRESET |
+ SAFE_PE_DMACFG_SGRESET);
+ WRITE_REG(sc, SAFE_PE_DMACFG, v
+ | SAFE_PE_DMACFG_PERESET
+ | SAFE_PE_DMACFG_PDRRESET
+ | SAFE_PE_DMACFG_SGRESET);
+ WRITE_REG(sc, SAFE_PE_DMACFG, v);
+}
+
+/*
+ * Initialize registers we need to touch only once.
+ */
+static void
+safe_init_board(struct safe_softc *sc)
+{
+ u_int32_t v, dwords;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ v = READ_REG(sc, SAFE_PE_DMACFG);
+ v &=~ ( SAFE_PE_DMACFG_PEMODE
+ | SAFE_PE_DMACFG_FSENA /* failsafe enable */
+ | SAFE_PE_DMACFG_GPRPCI /* gather ring on PCI */
+ | SAFE_PE_DMACFG_SPRPCI /* scatter ring on PCI */
+ | SAFE_PE_DMACFG_ESDESC /* endian-swap descriptors */
+ | SAFE_PE_DMACFG_ESPDESC /* endian-swap part. desc's */
+ | SAFE_PE_DMACFG_ESSA /* endian-swap SA's */
+ | SAFE_PE_DMACFG_ESPACKET /* swap the packet data */
+ );
+ v |= SAFE_PE_DMACFG_FSENA /* failsafe enable */
+ | SAFE_PE_DMACFG_GPRPCI /* gather ring on PCI */
+ | SAFE_PE_DMACFG_SPRPCI /* scatter ring on PCI */
+ | SAFE_PE_DMACFG_ESDESC /* endian-swap descriptors */
+ | SAFE_PE_DMACFG_ESPDESC /* endian-swap part. desc's */
+ | SAFE_PE_DMACFG_ESSA /* endian-swap SA's */
+#if 0
+ | SAFE_PE_DMACFG_ESPACKET /* swap the packet data */
+#endif
+ ;
+ WRITE_REG(sc, SAFE_PE_DMACFG, v);
+
+#ifdef __BIG_ENDIAN
+ /* tell the safenet that we are 4321 and not 1234 */
+ WRITE_REG(sc, SAFE_ENDIAN, 0xe4e41b1b);
+#endif
+
+ if (sc->sc_chiprev == SAFE_REV(1,0)) {
+ /*
+ * Avoid large PCI DMA transfers. Rev 1.0 has a bug where
+ * "target mode transfers" done while the chip is DMA'ing
+ * >1020 bytes cause the hardware to lockup. To avoid this
+ * we reduce the max PCI transfer size and use small source
+ * particle descriptors (<= 256 bytes).
+ */
+ WRITE_REG(sc, SAFE_DMA_CFG, 256);
+ device_printf(sc->sc_dev,
+ "Reduce max DMA size to %u words for rev %u.%u WAR\n",
+ (unsigned) ((READ_REG(sc, SAFE_DMA_CFG)>>2) & 0xff),
+ (unsigned) SAFE_REV_MAJ(sc->sc_chiprev),
+ (unsigned) SAFE_REV_MIN(sc->sc_chiprev));
+ sc->sc_max_dsize = 256;
+ } else {
+ sc->sc_max_dsize = SAFE_MAX_DSIZE;
+ }
+
+ /* NB: operands+results are overlaid */
+ WRITE_REG(sc, SAFE_PE_PDRBASE, sc->sc_ringalloc.dma_paddr);
+ WRITE_REG(sc, SAFE_PE_RDRBASE, sc->sc_ringalloc.dma_paddr);
+ /*
+ * Configure ring entry size and number of items in the ring.
+ */
+ KASSERT((sizeof(struct safe_ringentry) % sizeof(u_int32_t)) == 0,
+ ("PE ring entry not 32-bit aligned!"));
+ dwords = sizeof(struct safe_ringentry) / sizeof(u_int32_t);
+ WRITE_REG(sc, SAFE_PE_RINGCFG,
+ (dwords << SAFE_PE_RINGCFG_OFFSET_S) | SAFE_MAX_NQUEUE);
+ WRITE_REG(sc, SAFE_PE_RINGPOLL, 0); /* disable polling */
+
+ WRITE_REG(sc, SAFE_PE_GRNGBASE, sc->sc_spalloc.dma_paddr);
+ WRITE_REG(sc, SAFE_PE_SRNGBASE, sc->sc_dpalloc.dma_paddr);
+ WRITE_REG(sc, SAFE_PE_PARTSIZE,
+ (SAFE_TOTAL_DPART<<16) | SAFE_TOTAL_SPART);
+ /*
+ * NB: destination particles are fixed size. We use
+ * an mbuf cluster and require all results go to
+ * clusters or smaller.
+ */
+ WRITE_REG(sc, SAFE_PE_PARTCFG, sc->sc_max_dsize);
+
+ /* it's now safe to enable PE mode, do it */
+ WRITE_REG(sc, SAFE_PE_DMACFG, v | SAFE_PE_DMACFG_PEMODE);
+
+ /*
+ * Configure hardware to use level-triggered interrupts and
+ * to interrupt after each descriptor is processed.
+ */
+ WRITE_REG(sc, SAFE_HI_CFG, SAFE_HI_CFG_LEVEL);
+ WRITE_REG(sc, SAFE_HI_CLR, 0xffffffff);
+ WRITE_REG(sc, SAFE_HI_DESC_CNT, 1);
+ WRITE_REG(sc, SAFE_HI_MASK, SAFE_INT_PE_DDONE | SAFE_INT_PE_ERROR);
+}
+
+
+/*
+ * Clean up after a chip crash.
+ * It is assumed that the caller in splimp()
+ */
+static void
+safe_cleanchip(struct safe_softc *sc)
+{
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ if (sc->sc_nqchip != 0) {
+ struct safe_ringentry *re = sc->sc_back;
+
+ while (re != sc->sc_front) {
+ if (re->re_desc.d_csr != 0)
+ safe_free_entry(sc, re);
+ if (++re == sc->sc_ringtop)
+ re = sc->sc_ring;
+ }
+ sc->sc_back = re;
+ sc->sc_nqchip = 0;
+ }
+}
+
+/*
+ * free a safe_q
+ * It is assumed that the caller is within splimp().
+ */
+static int
+safe_free_entry(struct safe_softc *sc, struct safe_ringentry *re)
+{
+ struct cryptop *crp;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ /*
+ * Free header MCR
+ */
+ if ((re->re_dst_skb != NULL) && (re->re_src_skb != re->re_dst_skb))
+#ifdef NOTYET
+ m_freem(re->re_dst_m);
+#else
+ printk("%s,%d: SKB not supported\n", __FILE__, __LINE__);
+#endif
+
+ crp = (struct cryptop *)re->re_crp;
+
+ re->re_desc.d_csr = 0;
+
+ crp->crp_etype = EFAULT;
+ crypto_done(crp);
+ return(0);
+}
+
+/*
+ * Routine to reset the chip and clean up.
+ * It is assumed that the caller is in splimp()
+ */
+static void
+safe_totalreset(struct safe_softc *sc)
+{
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ safe_reset_board(sc);
+ safe_init_board(sc);
+ safe_cleanchip(sc);
+}
+
+/*
+ * Is the operand suitable aligned for direct DMA. Each
+ * segment must be aligned on a 32-bit boundary and all
+ * but the last segment must be a multiple of 4 bytes.
+ */
+static int
+safe_dmamap_aligned(struct safe_softc *sc, const struct safe_operand *op)
+{
+ int i;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ for (i = 0; i < op->nsegs; i++) {
+ if (op->segs[i].ds_addr & 3)
+ return (0);
+ if (i != (op->nsegs - 1) && (op->segs[i].ds_len & 3))
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Is the operand suitable for direct DMA as the destination
+ * of an operation. The hardware requires that each ``particle''
+ * but the last in an operation result have the same size. We
+ * fix that size at SAFE_MAX_DSIZE bytes. This routine returns
+ * 0 if some segment is not a multiple of of this size, 1 if all
+ * segments are exactly this size, or 2 if segments are at worst
+ * a multple of this size.
+ */
+static int
+safe_dmamap_uniform(struct safe_softc *sc, const struct safe_operand *op)
+{
+ int result = 1;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ if (op->nsegs > 0) {
+ int i;
+
+ for (i = 0; i < op->nsegs-1; i++) {
+ if (op->segs[i].ds_len % sc->sc_max_dsize)
+ return (0);
+ if (op->segs[i].ds_len != sc->sc_max_dsize)
+ result = 2;
+ }
+ }
+ return (result);
+}
+
+static int
+safe_kprocess(device_t dev, struct cryptkop *krp, int hint)
+{
+ struct safe_softc *sc = device_get_softc(dev);
+ struct safe_pkq *q;
+ unsigned long flags;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ if (sc == NULL) {
+ krp->krp_status = EINVAL;
+ goto err;
+ }
+
+ if (krp->krp_op != CRK_MOD_EXP) {
+ krp->krp_status = EOPNOTSUPP;
+ goto err;
+ }
+
+ q = (struct safe_pkq *) kmalloc(sizeof(*q), GFP_KERNEL);
+ if (q == NULL) {
+ krp->krp_status = ENOMEM;
+ goto err;
+ }
+ memset(q, 0, sizeof(*q));
+ q->pkq_krp = krp;
+ INIT_LIST_HEAD(&q->pkq_list);
+
+ spin_lock_irqsave(&sc->sc_pkmtx, flags);
+ list_add_tail(&q->pkq_list, &sc->sc_pkq);
+ safe_kfeed(sc);
+ spin_unlock_irqrestore(&sc->sc_pkmtx, flags);
+ return (0);
+
+err:
+ crypto_kdone(krp);
+ return (0);
+}
+
+#define SAFE_CRK_PARAM_BASE 0
+#define SAFE_CRK_PARAM_EXP 1
+#define SAFE_CRK_PARAM_MOD 2
+
+static int
+safe_kstart(struct safe_softc *sc)
+{
+ struct cryptkop *krp = sc->sc_pkq_cur->pkq_krp;
+ int exp_bits, mod_bits, base_bits;
+ u_int32_t op, a_off, b_off, c_off, d_off;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ if (krp->krp_iparams < 3 || krp->krp_oparams != 1) {
+ krp->krp_status = EINVAL;
+ return (1);
+ }
+
+ base_bits = safe_ksigbits(sc, &krp->krp_param[SAFE_CRK_PARAM_BASE]);
+ if (base_bits > 2048)
+ goto too_big;
+ if (base_bits <= 0) /* 5. base not zero */
+ goto too_small;
+
+ exp_bits = safe_ksigbits(sc, &krp->krp_param[SAFE_CRK_PARAM_EXP]);
+ if (exp_bits > 2048)
+ goto too_big;
+ if (exp_bits <= 0) /* 1. exponent word length > 0 */
+ goto too_small; /* 4. exponent not zero */
+
+ mod_bits = safe_ksigbits(sc, &krp->krp_param[SAFE_CRK_PARAM_MOD]);
+ if (mod_bits > 2048)
+ goto too_big;
+ if (mod_bits <= 32) /* 2. modulus word length > 1 */
+ goto too_small; /* 8. MSW of modulus != zero */
+ if (mod_bits < exp_bits) /* 3 modulus len >= exponent len */
+ goto too_small;
+ if ((krp->krp_param[SAFE_CRK_PARAM_MOD].crp_p[0] & 1) == 0)
+ goto bad_domain; /* 6. modulus is odd */
+ if (mod_bits > krp->krp_param[krp->krp_iparams].crp_nbits)
+ goto too_small; /* make sure result will fit */
+
+ /* 7. modulus > base */
+ if (mod_bits < base_bits)
+ goto too_small;
+ if (mod_bits == base_bits) {
+ u_int8_t *basep, *modp;
+ int i;
+
+ basep = krp->krp_param[SAFE_CRK_PARAM_BASE].crp_p +
+ ((base_bits + 7) / 8) - 1;
+ modp = krp->krp_param[SAFE_CRK_PARAM_MOD].crp_p +
+ ((mod_bits + 7) / 8) - 1;
+
+ for (i = 0; i < (mod_bits + 7) / 8; i++, basep--, modp--) {
+ if (*modp < *basep)
+ goto too_small;
+ if (*modp > *basep)
+ break;
+ }
+ }
+
+ /* And on the 9th step, he rested. */
+
+ WRITE_REG(sc, SAFE_PK_A_LEN, (exp_bits + 31) / 32);
+ WRITE_REG(sc, SAFE_PK_B_LEN, (mod_bits + 31) / 32);
+ if (mod_bits > 1024) {
+ op = SAFE_PK_FUNC_EXP4;
+ a_off = 0x000;
+ b_off = 0x100;
+ c_off = 0x200;
+ d_off = 0x300;
+ } else {
+ op = SAFE_PK_FUNC_EXP16;
+ a_off = 0x000;
+ b_off = 0x080;
+ c_off = 0x100;
+ d_off = 0x180;
+ }
+ sc->sc_pk_reslen = b_off - a_off;
+ sc->sc_pk_resoff = d_off;
+
+ /* A is exponent, B is modulus, C is base, D is result */
+ safe_kload_reg(sc, a_off, b_off - a_off,
+ &krp->krp_param[SAFE_CRK_PARAM_EXP]);
+ WRITE_REG(sc, SAFE_PK_A_ADDR, a_off >> 2);
+ safe_kload_reg(sc, b_off, b_off - a_off,
+ &krp->krp_param[SAFE_CRK_PARAM_MOD]);
+ WRITE_REG(sc, SAFE_PK_B_ADDR, b_off >> 2);
+ safe_kload_reg(sc, c_off, b_off - a_off,
+ &krp->krp_param[SAFE_CRK_PARAM_BASE]);
+ WRITE_REG(sc, SAFE_PK_C_ADDR, c_off >> 2);
+ WRITE_REG(sc, SAFE_PK_D_ADDR, d_off >> 2);
+
+ WRITE_REG(sc, SAFE_PK_FUNC, op | SAFE_PK_FUNC_RUN);
+
+ return (0);
+
+too_big:
+ krp->krp_status = E2BIG;
+ return (1);
+too_small:
+ krp->krp_status = ERANGE;
+ return (1);
+bad_domain:
+ krp->krp_status = EDOM;
+ return (1);
+}
+
+static int
+safe_ksigbits(struct safe_softc *sc, struct crparam *cr)
+{
+ u_int plen = (cr->crp_nbits + 7) / 8;
+ int i, sig = plen * 8;
+ u_int8_t c, *p = cr->crp_p;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ for (i = plen - 1; i >= 0; i--) {
+ c = p[i];
+ if (c != 0) {
+ while ((c & 0x80) == 0) {
+ sig--;
+ c <<= 1;
+ }
+ break;
+ }
+ sig -= 8;
+ }
+ return (sig);
+}
+
+static void
+safe_kfeed(struct safe_softc *sc)
+{
+ struct safe_pkq *q, *tmp;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ if (list_empty(&sc->sc_pkq) && sc->sc_pkq_cur == NULL)
+ return;
+ if (sc->sc_pkq_cur != NULL)
+ return;
+ list_for_each_entry_safe(q, tmp, &sc->sc_pkq, pkq_list) {
+ sc->sc_pkq_cur = q;
+ list_del(&q->pkq_list);
+ if (safe_kstart(sc) != 0) {
+ crypto_kdone(q->pkq_krp);
+ kfree(q);
+ sc->sc_pkq_cur = NULL;
+ } else {
+ /* op started, start polling */
+ mod_timer(&sc->sc_pkto, jiffies + 1);
+ break;
+ }
+ }
+}
+
+static void
+safe_kpoll(unsigned long arg)
+{
+ struct safe_softc *sc = NULL;
+ struct safe_pkq *q;
+ struct crparam *res;
+ int i;
+ u_int32_t buf[64];
+ unsigned long flags;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ if (arg >= SAFE_MAX_CHIPS)
+ return;
+ sc = safe_chip_idx[arg];
+ if (!sc) {
+ DPRINTF(("%s() - bad callback\n", __FUNCTION__));
+ return;
+ }
+
+ spin_lock_irqsave(&sc->sc_pkmtx, flags);
+ if (sc->sc_pkq_cur == NULL)
+ goto out;
+ if (READ_REG(sc, SAFE_PK_FUNC) & SAFE_PK_FUNC_RUN) {
+ /* still running, check back later */
+ mod_timer(&sc->sc_pkto, jiffies + 1);
+ goto out;
+ }
+
+ q = sc->sc_pkq_cur;
+ res = &q->pkq_krp->krp_param[q->pkq_krp->krp_iparams];
+ bzero(buf, sizeof(buf));
+ bzero(res->crp_p, (res->crp_nbits + 7) / 8);
+ for (i = 0; i < sc->sc_pk_reslen >> 2; i++)
+ buf[i] = le32_to_cpu(READ_REG(sc, SAFE_PK_RAM_START +
+ sc->sc_pk_resoff + (i << 2)));
+ bcopy(buf, res->crp_p, (res->crp_nbits + 7) / 8);
+ /*
+ * reduce the bits that need copying if possible
+ */
+ res->crp_nbits = min(res->crp_nbits,sc->sc_pk_reslen * 8);
+ res->crp_nbits = safe_ksigbits(sc, res);
+
+ for (i = SAFE_PK_RAM_START; i < SAFE_PK_RAM_END; i += 4)
+ WRITE_REG(sc, i, 0);
+
+ crypto_kdone(q->pkq_krp);
+ kfree(q);
+ sc->sc_pkq_cur = NULL;
+
+ safe_kfeed(sc);
+out:
+ spin_unlock_irqrestore(&sc->sc_pkmtx, flags);
+}
+
+static void
+safe_kload_reg(struct safe_softc *sc, u_int32_t off, u_int32_t len,
+ struct crparam *n)
+{
+ u_int32_t buf[64], i;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ bzero(buf, sizeof(buf));
+ bcopy(n->crp_p, buf, (n->crp_nbits + 7) / 8);
+
+ for (i = 0; i < len >> 2; i++)
+ WRITE_REG(sc, SAFE_PK_RAM_START + off + (i << 2),
+ cpu_to_le32(buf[i]));
+}
+
+#ifdef SAFE_DEBUG
+static void
+safe_dump_dmastatus(struct safe_softc *sc, const char *tag)
+{
+ printf("%s: ENDIAN 0x%x SRC 0x%x DST 0x%x STAT 0x%x\n"
+ , tag
+ , READ_REG(sc, SAFE_DMA_ENDIAN)
+ , READ_REG(sc, SAFE_DMA_SRCADDR)
+ , READ_REG(sc, SAFE_DMA_DSTADDR)
+ , READ_REG(sc, SAFE_DMA_STAT)
+ );
+}
+
+static void
+safe_dump_intrstate(struct safe_softc *sc, const char *tag)
+{
+ printf("%s: HI_CFG 0x%x HI_MASK 0x%x HI_DESC_CNT 0x%x HU_STAT 0x%x HM_STAT 0x%x\n"
+ , tag
+ , READ_REG(sc, SAFE_HI_CFG)
+ , READ_REG(sc, SAFE_HI_MASK)
+ , READ_REG(sc, SAFE_HI_DESC_CNT)
+ , READ_REG(sc, SAFE_HU_STAT)
+ , READ_REG(sc, SAFE_HM_STAT)
+ );
+}
+
+static void
+safe_dump_ringstate(struct safe_softc *sc, const char *tag)
+{
+ u_int32_t estat = READ_REG(sc, SAFE_PE_ERNGSTAT);
+
+ /* NB: assume caller has lock on ring */
+ printf("%s: ERNGSTAT %x (next %u) back %lu front %lu\n",
+ tag,
+ estat, (estat >> SAFE_PE_ERNGSTAT_NEXT_S),
+ (unsigned long)(sc->sc_back - sc->sc_ring),
+ (unsigned long)(sc->sc_front - sc->sc_ring));
+}
+
+static void
+safe_dump_request(struct safe_softc *sc, const char* tag, struct safe_ringentry *re)
+{
+ int ix, nsegs;
+
+ ix = re - sc->sc_ring;
+ printf("%s: %p (%u): csr %x src %x dst %x sa %x len %x\n"
+ , tag
+ , re, ix
+ , re->re_desc.d_csr
+ , re->re_desc.d_src
+ , re->re_desc.d_dst
+ , re->re_desc.d_sa
+ , re->re_desc.d_len
+ );
+ if (re->re_src.nsegs > 1) {
+ ix = (re->re_desc.d_src - sc->sc_spalloc.dma_paddr) /
+ sizeof(struct safe_pdesc);
+ for (nsegs = re->re_src.nsegs; nsegs; nsegs--) {
+ printf(" spd[%u] %p: %p size %u flags %x"
+ , ix, &sc->sc_spring[ix]
+ , (caddr_t)(uintptr_t) sc->sc_spring[ix].pd_addr
+ , sc->sc_spring[ix].pd_size
+ , sc->sc_spring[ix].pd_flags
+ );
+ if (sc->sc_spring[ix].pd_size == 0)
+ printf(" (zero!)");
+ printf("\n");
+ if (++ix == SAFE_TOTAL_SPART)
+ ix = 0;
+ }
+ }
+ if (re->re_dst.nsegs > 1) {
+ ix = (re->re_desc.d_dst - sc->sc_dpalloc.dma_paddr) /
+ sizeof(struct safe_pdesc);
+ for (nsegs = re->re_dst.nsegs; nsegs; nsegs--) {
+ printf(" dpd[%u] %p: %p flags %x\n"
+ , ix, &sc->sc_dpring[ix]
+ , (caddr_t)(uintptr_t) sc->sc_dpring[ix].pd_addr
+ , sc->sc_dpring[ix].pd_flags
+ );
+ if (++ix == SAFE_TOTAL_DPART)
+ ix = 0;
+ }
+ }
+ printf("sa: cmd0 %08x cmd1 %08x staterec %x\n",
+ re->re_sa.sa_cmd0, re->re_sa.sa_cmd1, re->re_sa.sa_staterec);
+ printf("sa: key %x %x %x %x %x %x %x %x\n"
+ , re->re_sa.sa_key[0]
+ , re->re_sa.sa_key[1]
+ , re->re_sa.sa_key[2]
+ , re->re_sa.sa_key[3]
+ , re->re_sa.sa_key[4]
+ , re->re_sa.sa_key[5]
+ , re->re_sa.sa_key[6]
+ , re->re_sa.sa_key[7]
+ );
+ printf("sa: indigest %x %x %x %x %x\n"
+ , re->re_sa.sa_indigest[0]
+ , re->re_sa.sa_indigest[1]
+ , re->re_sa.sa_indigest[2]
+ , re->re_sa.sa_indigest[3]
+ , re->re_sa.sa_indigest[4]
+ );
+ printf("sa: outdigest %x %x %x %x %x\n"
+ , re->re_sa.sa_outdigest[0]
+ , re->re_sa.sa_outdigest[1]
+ , re->re_sa.sa_outdigest[2]
+ , re->re_sa.sa_outdigest[3]
+ , re->re_sa.sa_outdigest[4]
+ );
+ printf("sr: iv %x %x %x %x\n"
+ , re->re_sastate.sa_saved_iv[0]
+ , re->re_sastate.sa_saved_iv[1]
+ , re->re_sastate.sa_saved_iv[2]
+ , re->re_sastate.sa_saved_iv[3]
+ );
+ printf("sr: hashbc %u indigest %x %x %x %x %x\n"
+ , re->re_sastate.sa_saved_hashbc
+ , re->re_sastate.sa_saved_indigest[0]
+ , re->re_sastate.sa_saved_indigest[1]
+ , re->re_sastate.sa_saved_indigest[2]
+ , re->re_sastate.sa_saved_indigest[3]
+ , re->re_sastate.sa_saved_indigest[4]
+ );
+}
+
+static void
+safe_dump_ring(struct safe_softc *sc, const char *tag)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sc->sc_ringmtx, flags);
+ printf("\nSafeNet Ring State:\n");
+ safe_dump_intrstate(sc, tag);
+ safe_dump_dmastatus(sc, tag);
+ safe_dump_ringstate(sc, tag);
+ if (sc->sc_nqchip) {
+ struct safe_ringentry *re = sc->sc_back;
+ do {
+ safe_dump_request(sc, tag, re);
+ if (++re == sc->sc_ringtop)
+ re = sc->sc_ring;
+ } while (re != sc->sc_front);
+ }
+ spin_unlock_irqrestore(&sc->sc_ringmtx, flags);
+}
+#endif /* SAFE_DEBUG */
+
+
+static int safe_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+ struct safe_softc *sc = NULL;
+ u32 mem_start, mem_len, cmd;
+ int i, rc, devinfo;
+ dma_addr_t raddr;
+ static int num_chips = 0;
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ if (pci_enable_device(dev) < 0)
+ return(-ENODEV);
+
+ if (!dev->irq) {
+ printk("safe: found device with no IRQ assigned. check BIOS settings!");
+ pci_disable_device(dev);
+ return(-ENODEV);
+ }
+
+ if (pci_set_mwi(dev)) {
+ printk("safe: pci_set_mwi failed!");
+ return(-ENODEV);
+ }
+
+ sc = (struct safe_softc *) kmalloc(sizeof(*sc), GFP_KERNEL);
+ if (!sc)
+ return(-ENOMEM);
+ memset(sc, 0, sizeof(*sc));
+
+ softc_device_init(sc, "safe", num_chips, safe_methods);
+
+ sc->sc_irq = -1;
+ sc->sc_cid = -1;
+ sc->sc_pcidev = dev;
+ if (num_chips < SAFE_MAX_CHIPS) {
+ safe_chip_idx[device_get_unit(sc->sc_dev)] = sc;
+ num_chips++;
+ }
+
+ INIT_LIST_HEAD(&sc->sc_pkq);
+ spin_lock_init(&sc->sc_pkmtx);
+
+ pci_set_drvdata(sc->sc_pcidev, sc);
+
+ /* we read its hardware registers as memory */
+ mem_start = pci_resource_start(sc->sc_pcidev, 0);
+ mem_len = pci_resource_len(sc->sc_pcidev, 0);
+
+ sc->sc_base_addr = (ocf_iomem_t) ioremap(mem_start, mem_len);
+ if (!sc->sc_base_addr) {
+ device_printf(sc->sc_dev, "failed to ioremap 0x%x-0x%x\n",
+ mem_start, mem_start + mem_len - 1);
+ goto out;
+ }
+
+ /* fix up the bus size */
+ if (pci_set_dma_mask(sc->sc_pcidev, DMA_32BIT_MASK)) {
+ device_printf(sc->sc_dev, "No usable DMA configuration, aborting.\n");
+ goto out;
+ }
+ if (pci_set_consistent_dma_mask(sc->sc_pcidev, DMA_32BIT_MASK)) {
+ device_printf(sc->sc_dev, "No usable consistent DMA configuration, aborting.\n");
+ goto out;
+ }
+
+ pci_set_master(sc->sc_pcidev);
+
+ pci_read_config_dword(sc->sc_pcidev, PCI_COMMAND, &cmd);
+
+ if (!(cmd & PCI_COMMAND_MEMORY)) {
+ device_printf(sc->sc_dev, "failed to enable memory mapping\n");
+ goto out;
+ }
+
+ if (!(cmd & PCI_COMMAND_MASTER)) {
+ device_printf(sc->sc_dev, "failed to enable bus mastering\n");
+ goto out;
+ }
+
+ rc = request_irq(dev->irq, safe_intr, IRQF_SHARED, "safe", sc);
+ if (rc) {
+ device_printf(sc->sc_dev, "failed to hook irq %d\n", sc->sc_irq);
+ goto out;
+ }
+ sc->sc_irq = dev->irq;
+
+ sc->sc_chiprev = READ_REG(sc, SAFE_DEVINFO) &
+ (SAFE_DEVINFO_REV_MAJ | SAFE_DEVINFO_REV_MIN);
+
+ /*
+ * Allocate packet engine descriptors.
+ */
+ sc->sc_ringalloc.dma_vaddr = pci_alloc_consistent(sc->sc_pcidev,
+ SAFE_MAX_NQUEUE * sizeof (struct safe_ringentry),
+ &sc->sc_ringalloc.dma_paddr);
+ if (!sc->sc_ringalloc.dma_vaddr) {
+ device_printf(sc->sc_dev, "cannot allocate PE descriptor ring\n");
+ goto out;
+ }
+
+ /*
+ * Hookup the static portion of all our data structures.
+ */
+ sc->sc_ring = (struct safe_ringentry *) sc->sc_ringalloc.dma_vaddr;
+ sc->sc_ringtop = sc->sc_ring + SAFE_MAX_NQUEUE;
+ sc->sc_front = sc->sc_ring;
+ sc->sc_back = sc->sc_ring;
+ raddr = sc->sc_ringalloc.dma_paddr;
+ bzero(sc->sc_ring, SAFE_MAX_NQUEUE * sizeof(struct safe_ringentry));
+ for (i = 0; i < SAFE_MAX_NQUEUE; i++) {
+ struct safe_ringentry *re = &sc->sc_ring[i];
+
+ re->re_desc.d_sa = raddr +
+ offsetof(struct safe_ringentry, re_sa);
+ re->re_sa.sa_staterec = raddr +
+ offsetof(struct safe_ringentry, re_sastate);
+
+ raddr += sizeof (struct safe_ringentry);
+ }
+ spin_lock_init(&sc->sc_ringmtx);
+
+ /*
+ * Allocate scatter and gather particle descriptors.
+ */
+ sc->sc_spalloc.dma_vaddr = pci_alloc_consistent(sc->sc_pcidev,
+ SAFE_TOTAL_SPART * sizeof (struct safe_pdesc),
+ &sc->sc_spalloc.dma_paddr);
+ if (!sc->sc_spalloc.dma_vaddr) {
+ device_printf(sc->sc_dev, "cannot allocate source particle descriptor ring\n");
+ goto out;
+ }
+ sc->sc_spring = (struct safe_pdesc *) sc->sc_spalloc.dma_vaddr;
+ sc->sc_springtop = sc->sc_spring + SAFE_TOTAL_SPART;
+ sc->sc_spfree = sc->sc_spring;
+ bzero(sc->sc_spring, SAFE_TOTAL_SPART * sizeof(struct safe_pdesc));
+
+ sc->sc_dpalloc.dma_vaddr = pci_alloc_consistent(sc->sc_pcidev,
+ SAFE_TOTAL_DPART * sizeof (struct safe_pdesc),
+ &sc->sc_dpalloc.dma_paddr);
+ if (!sc->sc_dpalloc.dma_vaddr) {
+ device_printf(sc->sc_dev, "cannot allocate destination particle descriptor ring\n");
+ goto out;
+ }
+ sc->sc_dpring = (struct safe_pdesc *) sc->sc_dpalloc.dma_vaddr;
+ sc->sc_dpringtop = sc->sc_dpring + SAFE_TOTAL_DPART;
+ sc->sc_dpfree = sc->sc_dpring;
+ bzero(sc->sc_dpring, SAFE_TOTAL_DPART * sizeof(struct safe_pdesc));
+
+ sc->sc_cid = crypto_get_driverid(softc_get_device(sc), CRYPTOCAP_F_HARDWARE);
+ if (sc->sc_cid < 0) {
+ device_printf(sc->sc_dev, "could not get crypto driver id\n");
+ goto out;
+ }
+
+ printf("%s:", device_get_nameunit(sc->sc_dev));
+
+ devinfo = READ_REG(sc, SAFE_DEVINFO);
+ if (devinfo & SAFE_DEVINFO_RNG) {
+ sc->sc_flags |= SAFE_FLAGS_RNG;
+ printf(" rng");
+ }
+ if (devinfo & SAFE_DEVINFO_PKEY) {
+ printf(" key");
+ sc->sc_flags |= SAFE_FLAGS_KEY;
+ crypto_kregister(sc->sc_cid, CRK_MOD_EXP, 0);
+#if 0
+ crypto_kregister(sc->sc_cid, CRK_MOD_EXP_CRT, 0);
+#endif
+ init_timer(&sc->sc_pkto);
+ sc->sc_pkto.function = safe_kpoll;
+ sc->sc_pkto.data = (unsigned long) device_get_unit(sc->sc_dev);
+ }
+ if (devinfo & SAFE_DEVINFO_DES) {
+ printf(" des/3des");
+ crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_DES_CBC, 0, 0);
+ }
+ if (devinfo & SAFE_DEVINFO_AES) {
+ printf(" aes");
+ crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0);
+ }
+ if (devinfo & SAFE_DEVINFO_MD5) {
+ printf(" md5");
+ crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0);
+ }
+ if (devinfo & SAFE_DEVINFO_SHA1) {
+ printf(" sha1");
+ crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0);
+ }
+ printf(" null");
+ crypto_register(sc->sc_cid, CRYPTO_NULL_CBC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_NULL_HMAC, 0, 0);
+ /* XXX other supported algorithms */
+ printf("\n");
+
+ safe_reset_board(sc); /* reset h/w */
+ safe_init_board(sc); /* init h/w */
+
+#if defined(CONFIG_OCF_RANDOMHARVEST) && !defined(SAFE_NO_RNG)
+ if (sc->sc_flags & SAFE_FLAGS_RNG) {
+ safe_rng_init(sc);
+ crypto_rregister(sc->sc_cid, safe_read_random, sc);
+ }
+#endif /* SAFE_NO_RNG */
+
+ return (0);
+
+out:
+ if (sc->sc_cid >= 0)
+ crypto_unregister_all(sc->sc_cid);
+ if (sc->sc_irq != -1)
+ free_irq(sc->sc_irq, sc);
+ if (sc->sc_ringalloc.dma_vaddr)
+ pci_free_consistent(sc->sc_pcidev,
+ SAFE_MAX_NQUEUE * sizeof (struct safe_ringentry),
+ sc->sc_ringalloc.dma_vaddr, sc->sc_ringalloc.dma_paddr);
+ if (sc->sc_spalloc.dma_vaddr)
+ pci_free_consistent(sc->sc_pcidev,
+ SAFE_TOTAL_DPART * sizeof (struct safe_pdesc),
+ sc->sc_spalloc.dma_vaddr, sc->sc_spalloc.dma_paddr);
+ if (sc->sc_dpalloc.dma_vaddr)
+ pci_free_consistent(sc->sc_pcidev,
+ SAFE_TOTAL_DPART * sizeof (struct safe_pdesc),
+ sc->sc_dpalloc.dma_vaddr, sc->sc_dpalloc.dma_paddr);
+ kfree(sc);
+ return(-ENODEV);
+}
+
+static void safe_remove(struct pci_dev *dev)
+{
+ struct safe_softc *sc = pci_get_drvdata(dev);
+
+ DPRINTF(("%s()\n", __FUNCTION__));
+
+ /* XXX wait/abort active ops */
+
+ WRITE_REG(sc, SAFE_HI_MASK, 0); /* disable interrupts */
+
+ del_timer_sync(&sc->sc_pkto);
+
+ crypto_unregister_all(sc->sc_cid);
+
+ safe_cleanchip(sc);
+
+ if (sc->sc_irq != -1)
+ free_irq(sc->sc_irq, sc);
+ if (sc->sc_ringalloc.dma_vaddr)
+ pci_free_consistent(sc->sc_pcidev,
+ SAFE_MAX_NQUEUE * sizeof (struct safe_ringentry),
+ sc->sc_ringalloc.dma_vaddr, sc->sc_ringalloc.dma_paddr);
+ if (sc->sc_spalloc.dma_vaddr)
+ pci_free_consistent(sc->sc_pcidev,
+ SAFE_TOTAL_DPART * sizeof (struct safe_pdesc),
+ sc->sc_spalloc.dma_vaddr, sc->sc_spalloc.dma_paddr);
+ if (sc->sc_dpalloc.dma_vaddr)
+ pci_free_consistent(sc->sc_pcidev,
+ SAFE_TOTAL_DPART * sizeof (struct safe_pdesc),
+ sc->sc_dpalloc.dma_vaddr, sc->sc_dpalloc.dma_paddr);
+ sc->sc_irq = -1;
+ sc->sc_ringalloc.dma_vaddr = NULL;
+ sc->sc_spalloc.dma_vaddr = NULL;
+ sc->sc_dpalloc.dma_vaddr = NULL;
+}
+
+static struct pci_device_id safe_pci_tbl[] = {
+ { PCI_VENDOR_SAFENET, PCI_PRODUCT_SAFEXCEL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ { },
+};
+MODULE_DEVICE_TABLE(pci, safe_pci_tbl);
+
+static struct pci_driver safe_driver = {
+ .name = "safe",
+ .id_table = safe_pci_tbl,
+ .probe = safe_probe,
+ .remove = safe_remove,
+ /* add PM stuff here one day */
+};
+
+static int __init safe_init (void)
+{
+ struct safe_softc *sc = NULL;
+ int rc;
+
+ DPRINTF(("%s(%p)\n", __FUNCTION__, safe_init));
+
+ rc = pci_register_driver(&safe_driver);
+ pci_register_driver_compat(&safe_driver, rc);
+
+ return rc;
+}
+
+static void __exit safe_exit (void)
+{
+ pci_unregister_driver(&safe_driver);
+}
+
+module_init(safe_init);
+module_exit(safe_exit);
+
+MODULE_LICENSE("BSD");
+MODULE_AUTHOR("David McCullough <david_mccullough@securecomputing.com>");
+MODULE_DESCRIPTION("OCF driver for safenet PCI crypto devices");
diff --git a/crypto/ocf/safe/safereg.h b/crypto/ocf/safe/safereg.h
new file mode 100644
index 000000000000..dbaf98fe789a
--- /dev/null
+++ b/crypto/ocf/safe/safereg.h
@@ -0,0 +1,421 @@
+/*-
+ * Copyright (c) 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003 Global Technology Associates, Inc.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/safe/safereg.h,v 1.1 2003/07/21 21:46:07 sam Exp $
+ */
+#ifndef _SAFE_SAFEREG_H_
+#define _SAFE_SAFEREG_H_
+
+/*
+ * Register definitions for SafeNet SafeXcel-1141 crypto device.
+ * Definitions from revision 1.3 (Nov 6 2002) of the User's Manual.
+ */
+
+#define BS_BAR 0x10 /* DMA base address register */
+#define BS_TRDY_TIMEOUT 0x40 /* TRDY timeout */
+#define BS_RETRY_TIMEOUT 0x41 /* DMA retry timeout */
+
+#define PCI_VENDOR_SAFENET 0x16ae /* SafeNet, Inc. */
+
+/* SafeNet */
+#define PCI_PRODUCT_SAFEXCEL 0x1141 /* 1141 */
+
+#define SAFE_PE_CSR 0x0000 /* Packet Enginge Ctrl/Status */
+#define SAFE_PE_SRC 0x0004 /* Packet Engine Source */
+#define SAFE_PE_DST 0x0008 /* Packet Engine Destination */
+#define SAFE_PE_SA 0x000c /* Packet Engine SA */
+#define SAFE_PE_LEN 0x0010 /* Packet Engine Length */
+#define SAFE_PE_DMACFG 0x0040 /* Packet Engine DMA Configuration */
+#define SAFE_PE_DMASTAT 0x0044 /* Packet Engine DMA Status */
+#define SAFE_PE_PDRBASE 0x0048 /* Packet Engine Descriptor Ring Base */
+#define SAFE_PE_RDRBASE 0x004c /* Packet Engine Result Ring Base */
+#define SAFE_PE_RINGCFG 0x0050 /* Packet Engine Ring Configuration */
+#define SAFE_PE_RINGPOLL 0x0054 /* Packet Engine Ring Poll */
+#define SAFE_PE_IRNGSTAT 0x0058 /* Packet Engine Internal Ring Status */
+#define SAFE_PE_ERNGSTAT 0x005c /* Packet Engine External Ring Status */
+#define SAFE_PE_IOTHRESH 0x0060 /* Packet Engine I/O Threshold */
+#define SAFE_PE_GRNGBASE 0x0064 /* Packet Engine Gather Ring Base */
+#define SAFE_PE_SRNGBASE 0x0068 /* Packet Engine Scatter Ring Base */
+#define SAFE_PE_PARTSIZE 0x006c /* Packet Engine Particlar Ring Size */
+#define SAFE_PE_PARTCFG 0x0070 /* Packet Engine Particle Ring Config */
+#define SAFE_CRYPTO_CTRL 0x0080 /* Crypto Control */
+#define SAFE_DEVID 0x0084 /* Device ID */
+#define SAFE_DEVINFO 0x0088 /* Device Info */
+#define SAFE_HU_STAT 0x00a0 /* Host Unmasked Status */
+#define SAFE_HM_STAT 0x00a4 /* Host Masked Status (read-only) */
+#define SAFE_HI_CLR 0x00a4 /* Host Clear Interrupt (write-only) */
+#define SAFE_HI_MASK 0x00a8 /* Host Mask Control */
+#define SAFE_HI_CFG 0x00ac /* Interrupt Configuration */
+#define SAFE_HI_RD_DESCR 0x00b4 /* Force Descriptor Read */
+#define SAFE_HI_DESC_CNT 0x00b8 /* Host Descriptor Done Count */
+#define SAFE_DMA_ENDIAN 0x00c0 /* Master Endian Status */
+#define SAFE_DMA_SRCADDR 0x00c4 /* DMA Source Address Status */
+#define SAFE_DMA_DSTADDR 0x00c8 /* DMA Destination Address Status */
+#define SAFE_DMA_STAT 0x00cc /* DMA Current Status */
+#define SAFE_DMA_CFG 0x00d4 /* DMA Configuration/Status */
+#define SAFE_ENDIAN 0x00e0 /* Endian Configuration */
+#define SAFE_PK_A_ADDR 0x0800 /* Public Key A Address */
+#define SAFE_PK_B_ADDR 0x0804 /* Public Key B Address */
+#define SAFE_PK_C_ADDR 0x0808 /* Public Key C Address */
+#define SAFE_PK_D_ADDR 0x080c /* Public Key D Address */
+#define SAFE_PK_A_LEN 0x0810 /* Public Key A Length */
+#define SAFE_PK_B_LEN 0x0814 /* Public Key B Length */
+#define SAFE_PK_SHIFT 0x0818 /* Public Key Shift */
+#define SAFE_PK_FUNC 0x081c /* Public Key Function */
+#define SAFE_PK_RAM_START 0x1000 /* Public Key RAM start address */
+#define SAFE_PK_RAM_END 0x1fff /* Public Key RAM end address */
+
+#define SAFE_RNG_OUT 0x0100 /* RNG Output */
+#define SAFE_RNG_STAT 0x0104 /* RNG Status */
+#define SAFE_RNG_CTRL 0x0108 /* RNG Control */
+#define SAFE_RNG_A 0x010c /* RNG A */
+#define SAFE_RNG_B 0x0110 /* RNG B */
+#define SAFE_RNG_X_LO 0x0114 /* RNG X [31:0] */
+#define SAFE_RNG_X_MID 0x0118 /* RNG X [63:32] */
+#define SAFE_RNG_X_HI 0x011c /* RNG X [80:64] */
+#define SAFE_RNG_X_CNTR 0x0120 /* RNG Counter */
+#define SAFE_RNG_ALM_CNT 0x0124 /* RNG Alarm Count */
+#define SAFE_RNG_CNFG 0x0128 /* RNG Configuration */
+#define SAFE_RNG_LFSR1_LO 0x012c /* RNG LFSR1 [31:0] */
+#define SAFE_RNG_LFSR1_HI 0x0130 /* RNG LFSR1 [47:32] */
+#define SAFE_RNG_LFSR2_LO 0x0134 /* RNG LFSR1 [31:0] */
+#define SAFE_RNG_LFSR2_HI 0x0138 /* RNG LFSR1 [47:32] */
+
+#define SAFE_PE_CSR_READY 0x00000001 /* ready for processing */
+#define SAFE_PE_CSR_DONE 0x00000002 /* h/w completed processing */
+#define SAFE_PE_CSR_LOADSA 0x00000004 /* load SA digests */
+#define SAFE_PE_CSR_HASHFINAL 0x00000010 /* do hash pad & write result */
+#define SAFE_PE_CSR_SABUSID 0x000000c0 /* bus id for SA */
+#define SAFE_PE_CSR_SAPCI 0x00000040 /* PCI bus id for SA */
+#define SAFE_PE_CSR_NXTHDR 0x0000ff00 /* next hdr value for IPsec */
+#define SAFE_PE_CSR_FPAD 0x0000ff00 /* fixed pad for basic ops */
+#define SAFE_PE_CSR_STATUS 0x00ff0000 /* operation result status */
+#define SAFE_PE_CSR_AUTH_FAIL 0x00010000 /* ICV mismatch (inbound) */
+#define SAFE_PE_CSR_PAD_FAIL 0x00020000 /* pad verify fail (inbound) */
+#define SAFE_PE_CSR_SEQ_FAIL 0x00040000 /* sequence number (inbound) */
+#define SAFE_PE_CSR_XERROR 0x00080000 /* extended error follows */
+#define SAFE_PE_CSR_XECODE 0x00f00000 /* extended error code */
+#define SAFE_PE_CSR_XECODE_S 20
+#define SAFE_PE_CSR_XECODE_BADCMD 0 /* invalid command */
+#define SAFE_PE_CSR_XECODE_BADALG 1 /* invalid algorithm */
+#define SAFE_PE_CSR_XECODE_ALGDIS 2 /* algorithm disabled */
+#define SAFE_PE_CSR_XECODE_ZEROLEN 3 /* zero packet length */
+#define SAFE_PE_CSR_XECODE_DMAERR 4 /* bus DMA error */
+#define SAFE_PE_CSR_XECODE_PIPEABORT 5 /* secondary bus DMA error */
+#define SAFE_PE_CSR_XECODE_BADSPI 6 /* IPsec SPI mismatch */
+#define SAFE_PE_CSR_XECODE_TIMEOUT 10 /* failsafe timeout */
+#define SAFE_PE_CSR_PAD 0xff000000 /* ESP padding control/status */
+#define SAFE_PE_CSR_PAD_MIN 0x00000000 /* minimum IPsec padding */
+#define SAFE_PE_CSR_PAD_16 0x08000000 /* pad to 16-byte boundary */
+#define SAFE_PE_CSR_PAD_32 0x10000000 /* pad to 32-byte boundary */
+#define SAFE_PE_CSR_PAD_64 0x20000000 /* pad to 64-byte boundary */
+#define SAFE_PE_CSR_PAD_128 0x40000000 /* pad to 128-byte boundary */
+#define SAFE_PE_CSR_PAD_256 0x80000000 /* pad to 256-byte boundary */
+
+/*
+ * Check the CSR to see if the PE has returned ownership to
+ * the host. Note that before processing a descriptor this
+ * must be done followed by a check of the SAFE_PE_LEN register
+ * status bits to avoid premature processing of a descriptor
+ * on its way back to the host.
+ */
+#define SAFE_PE_CSR_IS_DONE(_csr) \
+ (((_csr) & (SAFE_PE_CSR_READY | SAFE_PE_CSR_DONE)) == SAFE_PE_CSR_DONE)
+
+#define SAFE_PE_LEN_LENGTH 0x000fffff /* total length (bytes) */
+#define SAFE_PE_LEN_READY 0x00400000 /* ready for processing */
+#define SAFE_PE_LEN_DONE 0x00800000 /* h/w completed processing */
+#define SAFE_PE_LEN_BYPASS 0xff000000 /* bypass offset (bytes) */
+#define SAFE_PE_LEN_BYPASS_S 24
+
+#define SAFE_PE_LEN_IS_DONE(_len) \
+ (((_len) & (SAFE_PE_LEN_READY | SAFE_PE_LEN_DONE)) == SAFE_PE_LEN_DONE)
+
+/* NB: these apply to HU_STAT, HM_STAT, HI_CLR, and HI_MASK */
+#define SAFE_INT_PE_CDONE 0x00000002 /* PE context done */
+#define SAFE_INT_PE_DDONE 0x00000008 /* PE descriptor done */
+#define SAFE_INT_PE_ERROR 0x00000010 /* PE error */
+#define SAFE_INT_PE_ODONE 0x00000020 /* PE operation done */
+
+#define SAFE_HI_CFG_PULSE 0x00000001 /* use pulse interrupt */
+#define SAFE_HI_CFG_LEVEL 0x00000000 /* use level interrupt */
+#define SAFE_HI_CFG_AUTOCLR 0x00000002 /* auto-clear pulse interrupt */
+
+#define SAFE_ENDIAN_PASS 0x000000e4 /* straight pass-thru */
+#define SAFE_ENDIAN_SWAB 0x0000001b /* swap bytes in 32-bit word */
+
+#define SAFE_PE_DMACFG_PERESET 0x00000001 /* reset packet engine */
+#define SAFE_PE_DMACFG_PDRRESET 0x00000002 /* reset PDR counters/ptrs */
+#define SAFE_PE_DMACFG_SGRESET 0x00000004 /* reset scatter/gather cache */
+#define SAFE_PE_DMACFG_FSENA 0x00000008 /* enable failsafe reset */
+#define SAFE_PE_DMACFG_PEMODE 0x00000100 /* packet engine mode */
+#define SAFE_PE_DMACFG_SAPREC 0x00000200 /* SA precedes packet */
+#define SAFE_PE_DMACFG_PKFOLL 0x00000400 /* packet follows descriptor */
+#define SAFE_PE_DMACFG_GPRBID 0x00003000 /* gather particle ring busid */
+#define SAFE_PE_DMACFG_GPRPCI 0x00001000 /* PCI gather particle ring */
+#define SAFE_PE_DMACFG_SPRBID 0x0000c000 /* scatter part. ring busid */
+#define SAFE_PE_DMACFG_SPRPCI 0x00004000 /* PCI scatter part. ring */
+#define SAFE_PE_DMACFG_ESDESC 0x00010000 /* endian swap descriptors */
+#define SAFE_PE_DMACFG_ESSA 0x00020000 /* endian swap SA data */
+#define SAFE_PE_DMACFG_ESPACKET 0x00040000 /* endian swap packet data */
+#define SAFE_PE_DMACFG_ESPDESC 0x00080000 /* endian swap particle desc. */
+#define SAFE_PE_DMACFG_NOPDRUP 0x00100000 /* supp. PDR ownership update */
+#define SAFE_PD_EDMACFG_PCIMODE 0x01000000 /* PCI target mode */
+
+#define SAFE_PE_DMASTAT_PEIDONE 0x00000001 /* PE core input done */
+#define SAFE_PE_DMASTAT_PEODONE 0x00000002 /* PE core output done */
+#define SAFE_PE_DMASTAT_ENCDONE 0x00000004 /* encryption done */
+#define SAFE_PE_DMASTAT_IHDONE 0x00000008 /* inner hash done */
+#define SAFE_PE_DMASTAT_OHDONE 0x00000010 /* outer hash (HMAC) done */
+#define SAFE_PE_DMASTAT_PADFLT 0x00000020 /* crypto pad fault */
+#define SAFE_PE_DMASTAT_ICVFLT 0x00000040 /* ICV fault */
+#define SAFE_PE_DMASTAT_SPIMIS 0x00000080 /* SPI mismatch */
+#define SAFE_PE_DMASTAT_CRYPTO 0x00000100 /* crypto engine timeout */
+#define SAFE_PE_DMASTAT_CQACT 0x00000200 /* command queue active */
+#define SAFE_PE_DMASTAT_IRACT 0x00000400 /* input request active */
+#define SAFE_PE_DMASTAT_ORACT 0x00000800 /* output request active */
+#define SAFE_PE_DMASTAT_PEISIZE 0x003ff000 /* PE input size:32-bit words */
+#define SAFE_PE_DMASTAT_PEOSIZE 0xffc00000 /* PE out. size:32-bit words */
+
+#define SAFE_PE_RINGCFG_SIZE 0x000003ff /* ring size (descriptors) */
+#define SAFE_PE_RINGCFG_OFFSET 0xffff0000 /* offset btw desc's (dwords) */
+#define SAFE_PE_RINGCFG_OFFSET_S 16
+
+#define SAFE_PE_RINGPOLL_POLL 0x00000fff /* polling frequency/divisor */
+#define SAFE_PE_RINGPOLL_RETRY 0x03ff0000 /* polling frequency/divisor */
+#define SAFE_PE_RINGPOLL_CONT 0x80000000 /* continuously poll */
+
+#define SAFE_PE_IRNGSTAT_CQAVAIL 0x00000001 /* command queue available */
+
+#define SAFE_PE_ERNGSTAT_NEXT 0x03ff0000 /* index of next packet desc. */
+#define SAFE_PE_ERNGSTAT_NEXT_S 16
+
+#define SAFE_PE_IOTHRESH_INPUT 0x000003ff /* input threshold (dwords) */
+#define SAFE_PE_IOTHRESH_OUTPUT 0x03ff0000 /* output threshold (dwords) */
+
+#define SAFE_PE_PARTCFG_SIZE 0x0000ffff /* scatter particle size */
+#define SAFE_PE_PARTCFG_GBURST 0x00030000 /* gather particle burst */
+#define SAFE_PE_PARTCFG_GBURST_2 0x00000000
+#define SAFE_PE_PARTCFG_GBURST_4 0x00010000
+#define SAFE_PE_PARTCFG_GBURST_8 0x00020000
+#define SAFE_PE_PARTCFG_GBURST_16 0x00030000
+#define SAFE_PE_PARTCFG_SBURST 0x000c0000 /* scatter particle burst */
+#define SAFE_PE_PARTCFG_SBURST_2 0x00000000
+#define SAFE_PE_PARTCFG_SBURST_4 0x00040000
+#define SAFE_PE_PARTCFG_SBURST_8 0x00080000
+#define SAFE_PE_PARTCFG_SBURST_16 0x000c0000
+
+#define SAFE_PE_PARTSIZE_SCAT 0xffff0000 /* scatter particle ring size */
+#define SAFE_PE_PARTSIZE_GATH 0x0000ffff /* gather particle ring size */
+
+#define SAFE_CRYPTO_CTRL_3DES 0x00000001 /* enable 3DES support */
+#define SAFE_CRYPTO_CTRL_PKEY 0x00010000 /* enable public key support */
+#define SAFE_CRYPTO_CTRL_RNG 0x00020000 /* enable RNG support */
+
+#define SAFE_DEVINFO_REV_MIN 0x0000000f /* minor rev for chip */
+#define SAFE_DEVINFO_REV_MAJ 0x000000f0 /* major rev for chip */
+#define SAFE_DEVINFO_REV_MAJ_S 4
+#define SAFE_DEVINFO_DES 0x00000100 /* DES/3DES support present */
+#define SAFE_DEVINFO_ARC4 0x00000200 /* ARC4 support present */
+#define SAFE_DEVINFO_AES 0x00000400 /* AES support present */
+#define SAFE_DEVINFO_MD5 0x00001000 /* MD5 support present */
+#define SAFE_DEVINFO_SHA1 0x00002000 /* SHA-1 support present */
+#define SAFE_DEVINFO_RIPEMD 0x00004000 /* RIPEMD support present */
+#define SAFE_DEVINFO_DEFLATE 0x00010000 /* Deflate support present */
+#define SAFE_DEVINFO_SARAM 0x00100000 /* on-chip SA RAM present */
+#define SAFE_DEVINFO_EMIBUS 0x00200000 /* EMI bus present */
+#define SAFE_DEVINFO_PKEY 0x00400000 /* public key support present */
+#define SAFE_DEVINFO_RNG 0x00800000 /* RNG present */
+
+#define SAFE_REV(_maj, _min) (((_maj) << SAFE_DEVINFO_REV_MAJ_S) | (_min))
+#define SAFE_REV_MAJ(_chiprev) \
+ (((_chiprev) & SAFE_DEVINFO_REV_MAJ) >> SAFE_DEVINFO_REV_MAJ_S)
+#define SAFE_REV_MIN(_chiprev) ((_chiprev) & SAFE_DEVINFO_REV_MIN)
+
+#define SAFE_PK_FUNC_MULT 0x00000001 /* Multiply function */
+#define SAFE_PK_FUNC_SQUARE 0x00000004 /* Square function */
+#define SAFE_PK_FUNC_ADD 0x00000010 /* Add function */
+#define SAFE_PK_FUNC_SUB 0x00000020 /* Subtract function */
+#define SAFE_PK_FUNC_LSHIFT 0x00000040 /* Left-shift function */
+#define SAFE_PK_FUNC_RSHIFT 0x00000080 /* Right-shift function */
+#define SAFE_PK_FUNC_DIV 0x00000100 /* Divide function */
+#define SAFE_PK_FUNC_CMP 0x00000400 /* Compare function */
+#define SAFE_PK_FUNC_COPY 0x00000800 /* Copy function */
+#define SAFE_PK_FUNC_EXP16 0x00002000 /* Exponentiate (4-bit ACT) */
+#define SAFE_PK_FUNC_EXP4 0x00004000 /* Exponentiate (2-bit ACT) */
+#define SAFE_PK_FUNC_RUN 0x00008000 /* start/status */
+
+#define SAFE_RNG_STAT_BUSY 0x00000001 /* busy, data not valid */
+
+#define SAFE_RNG_CTRL_PRE_LFSR 0x00000001 /* enable output pre-LFSR */
+#define SAFE_RNG_CTRL_TST_MODE 0x00000002 /* enable test mode */
+#define SAFE_RNG_CTRL_TST_RUN 0x00000004 /* start test state machine */
+#define SAFE_RNG_CTRL_ENA_RING1 0x00000008 /* test entropy oscillator #1 */
+#define SAFE_RNG_CTRL_ENA_RING2 0x00000010 /* test entropy oscillator #2 */
+#define SAFE_RNG_CTRL_DIS_ALARM 0x00000020 /* disable RNG alarm reports */
+#define SAFE_RNG_CTRL_TST_CLOCK 0x00000040 /* enable test clock */
+#define SAFE_RNG_CTRL_SHORTEN 0x00000080 /* shorten state timers */
+#define SAFE_RNG_CTRL_TST_ALARM 0x00000100 /* simulate alarm state */
+#define SAFE_RNG_CTRL_RST_LFSR 0x00000200 /* reset LFSR */
+
+/*
+ * Packet engine descriptor. Note that d_csr is a copy of the
+ * SAFE_PE_CSR register and all definitions apply, and d_len
+ * is a copy of the SAFE_PE_LEN register and all definitions apply.
+ * d_src and d_len may point directly to contiguous data or to a
+ * list of ``particle descriptors'' when using scatter/gather i/o.
+ */
+struct safe_desc {
+ u_int32_t d_csr; /* per-packet control/status */
+ u_int32_t d_src; /* source address */
+ u_int32_t d_dst; /* destination address */
+ u_int32_t d_sa; /* SA address */
+ u_int32_t d_len; /* length, bypass, status */
+};
+
+/*
+ * Scatter/Gather particle descriptor.
+ *
+ * NB: scatter descriptors do not specify a size; this is fixed
+ * by the setting of the SAFE_PE_PARTCFG register.
+ */
+struct safe_pdesc {
+ u_int32_t pd_addr; /* particle address */
+#ifdef __BIG_ENDIAN
+ u_int16_t pd_flags; /* control word */
+ u_int16_t pd_size; /* particle size (bytes) */
+#else
+ u_int16_t pd_flags; /* control word */
+ u_int16_t pd_size; /* particle size (bytes) */
+#endif
+};
+
+#define SAFE_PD_READY 0x0001 /* ready for processing */
+#define SAFE_PD_DONE 0x0002 /* h/w completed processing */
+
+/*
+ * Security Association (SA) Record (Rev 1). One of these is
+ * required for each operation processed by the packet engine.
+ */
+struct safe_sarec {
+ u_int32_t sa_cmd0;
+ u_int32_t sa_cmd1;
+ u_int32_t sa_resv0;
+ u_int32_t sa_resv1;
+ u_int32_t sa_key[8]; /* DES/3DES/AES key */
+ u_int32_t sa_indigest[5]; /* inner digest */
+ u_int32_t sa_outdigest[5]; /* outer digest */
+ u_int32_t sa_spi; /* SPI */
+ u_int32_t sa_seqnum; /* sequence number */
+ u_int32_t sa_seqmask[2]; /* sequence number mask */
+ u_int32_t sa_resv2;
+ u_int32_t sa_staterec; /* address of state record */
+ u_int32_t sa_resv3[2];
+ u_int32_t sa_samgmt0; /* SA management field 0 */
+ u_int32_t sa_samgmt1; /* SA management field 0 */
+};
+
+#define SAFE_SA_CMD0_OP 0x00000007 /* operation code */
+#define SAFE_SA_CMD0_OP_CRYPT 0x00000000 /* encrypt/decrypt (basic) */
+#define SAFE_SA_CMD0_OP_BOTH 0x00000001 /* encrypt-hash/hash-decrypto */
+#define SAFE_SA_CMD0_OP_HASH 0x00000003 /* hash (outbound-only) */
+#define SAFE_SA_CMD0_OP_ESP 0x00000000 /* ESP in/out (proto) */
+#define SAFE_SA_CMD0_OP_AH 0x00000001 /* AH in/out (proto) */
+#define SAFE_SA_CMD0_INBOUND 0x00000008 /* inbound operation */
+#define SAFE_SA_CMD0_OUTBOUND 0x00000000 /* outbound operation */
+#define SAFE_SA_CMD0_GROUP 0x00000030 /* operation group */
+#define SAFE_SA_CMD0_BASIC 0x00000000 /* basic operation */
+#define SAFE_SA_CMD0_PROTO 0x00000010 /* protocol/packet operation */
+#define SAFE_SA_CMD0_BUNDLE 0x00000020 /* bundled operation (resvd) */
+#define SAFE_SA_CMD0_PAD 0x000000c0 /* crypto pad method */
+#define SAFE_SA_CMD0_PAD_IPSEC 0x00000000 /* IPsec padding */
+#define SAFE_SA_CMD0_PAD_PKCS7 0x00000040 /* PKCS#7 padding */
+#define SAFE_SA_CMD0_PAD_CONS 0x00000080 /* constant padding */
+#define SAFE_SA_CMD0_PAD_ZERO 0x000000c0 /* zero padding */
+#define SAFE_SA_CMD0_CRYPT_ALG 0x00000f00 /* symmetric crypto algorithm */
+#define SAFE_SA_CMD0_DES 0x00000000 /* DES crypto algorithm */
+#define SAFE_SA_CMD0_3DES 0x00000100 /* 3DES crypto algorithm */
+#define SAFE_SA_CMD0_AES 0x00000300 /* AES crypto algorithm */
+#define SAFE_SA_CMD0_CRYPT_NULL 0x00000f00 /* null crypto algorithm */
+#define SAFE_SA_CMD0_HASH_ALG 0x0000f000 /* hash algorithm */
+#define SAFE_SA_CMD0_MD5 0x00000000 /* MD5 hash algorithm */
+#define SAFE_SA_CMD0_SHA1 0x00001000 /* SHA-1 hash algorithm */
+#define SAFE_SA_CMD0_HASH_NULL 0x0000f000 /* null hash algorithm */
+#define SAFE_SA_CMD0_HDR_PROC 0x00080000 /* header processing */
+#define SAFE_SA_CMD0_IBUSID 0x00300000 /* input bus id */
+#define SAFE_SA_CMD0_IPCI 0x00100000 /* PCI input bus id */
+#define SAFE_SA_CMD0_OBUSID 0x00c00000 /* output bus id */
+#define SAFE_SA_CMD0_OPCI 0x00400000 /* PCI output bus id */
+#define SAFE_SA_CMD0_IVLD 0x03000000 /* IV loading */
+#define SAFE_SA_CMD0_IVLD_NONE 0x00000000 /* IV no load (reuse) */
+#define SAFE_SA_CMD0_IVLD_IBUF 0x01000000 /* IV load from input buffer */
+#define SAFE_SA_CMD0_IVLD_STATE 0x02000000 /* IV load from state */
+#define SAFE_SA_CMD0_HSLD 0x0c000000 /* hash state loading */
+#define SAFE_SA_CMD0_HSLD_SA 0x00000000 /* hash state load from SA */
+#define SAFE_SA_CMD0_HSLD_STATE 0x08000000 /* hash state load from state */
+#define SAFE_SA_CMD0_HSLD_NONE 0x0c000000 /* hash state no load */
+#define SAFE_SA_CMD0_SAVEIV 0x10000000 /* save IV */
+#define SAFE_SA_CMD0_SAVEHASH 0x20000000 /* save hash state */
+#define SAFE_SA_CMD0_IGATHER 0x40000000 /* input gather */
+#define SAFE_SA_CMD0_OSCATTER 0x80000000 /* output scatter */
+
+#define SAFE_SA_CMD1_HDRCOPY 0x00000002 /* copy header to output */
+#define SAFE_SA_CMD1_PAYCOPY 0x00000004 /* copy payload to output */
+#define SAFE_SA_CMD1_PADCOPY 0x00000008 /* copy pad to output */
+#define SAFE_SA_CMD1_IPV4 0x00000000 /* IPv4 protocol */
+#define SAFE_SA_CMD1_IPV6 0x00000010 /* IPv6 protocol */
+#define SAFE_SA_CMD1_MUTABLE 0x00000020 /* mutable bit processing */
+#define SAFE_SA_CMD1_SRBUSID 0x000000c0 /* state record bus id */
+#define SAFE_SA_CMD1_SRPCI 0x00000040 /* state record from PCI */
+#define SAFE_SA_CMD1_CRMODE 0x00000300 /* crypto mode */
+#define SAFE_SA_CMD1_ECB 0x00000000 /* ECB crypto mode */
+#define SAFE_SA_CMD1_CBC 0x00000100 /* CBC crypto mode */
+#define SAFE_SA_CMD1_OFB 0x00000200 /* OFB crypto mode */
+#define SAFE_SA_CMD1_CFB 0x00000300 /* CFB crypto mode */
+#define SAFE_SA_CMD1_CRFEEDBACK 0x00000c00 /* crypto feedback mode */
+#define SAFE_SA_CMD1_64BIT 0x00000000 /* 64-bit crypto feedback */
+#define SAFE_SA_CMD1_8BIT 0x00000400 /* 8-bit crypto feedback */
+#define SAFE_SA_CMD1_1BIT 0x00000800 /* 1-bit crypto feedback */
+#define SAFE_SA_CMD1_128BIT 0x00000c00 /* 128-bit crypto feedback */
+#define SAFE_SA_CMD1_OPTIONS 0x00001000 /* HMAC/options mutable bit */
+#define SAFE_SA_CMD1_HMAC SAFE_SA_CMD1_OPTIONS
+#define SAFE_SA_CMD1_SAREV1 0x00008000 /* SA Revision 1 */
+#define SAFE_SA_CMD1_OFFSET 0x00ff0000 /* hash/crypto offset(dwords) */
+#define SAFE_SA_CMD1_OFFSET_S 16
+#define SAFE_SA_CMD1_AESKEYLEN 0x0f000000 /* AES key length */
+#define SAFE_SA_CMD1_AES128 0x02000000 /* 128-bit AES key */
+#define SAFE_SA_CMD1_AES192 0x03000000 /* 192-bit AES key */
+#define SAFE_SA_CMD1_AES256 0x04000000 /* 256-bit AES key */
+
+/*
+ * Security Associate State Record (Rev 1).
+ */
+struct safe_sastate {
+ u_int32_t sa_saved_iv[4]; /* saved IV (DES/3DES/AES) */
+ u_int32_t sa_saved_hashbc; /* saved hash byte count */
+ u_int32_t sa_saved_indigest[5]; /* saved inner digest */
+};
+#endif /* _SAFE_SAFEREG_H_ */
diff --git a/crypto/ocf/safe/safevar.h b/crypto/ocf/safe/safevar.h
new file mode 100644
index 000000000000..f5b73947cfde
--- /dev/null
+++ b/crypto/ocf/safe/safevar.h
@@ -0,0 +1,230 @@
+/*-
+ * The linux port of this code done by David McCullough
+ * Copyright (C) 2004-2007 David McCullough <david_mccullough@securecomputing.com>
+ * The license and original author are listed below.
+ *
+ * Copyright (c) 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2003 Global Technology Associates, Inc.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/safe/safevar.h,v 1.2 2006/05/17 18:34:26 pjd Exp $
+ */
+#ifndef _SAFE_SAFEVAR_H_
+#define _SAFE_SAFEVAR_H_
+
+/* Maximum queue length */
+#ifndef SAFE_MAX_NQUEUE
+#define SAFE_MAX_NQUEUE 60
+#endif
+
+#define SAFE_MAX_PART 64 /* Maximum scatter/gather depth */
+#define SAFE_DMA_BOUNDARY 0 /* No boundary for source DMA ops */
+#define SAFE_MAX_DSIZE 2048 /* MCLBYTES Fixed scatter particle size */
+#define SAFE_MAX_SSIZE 0x0ffff /* Maximum gather particle size */
+#define SAFE_MAX_DMA 0xfffff /* Maximum PE operand size (20 bits) */
+/* total src+dst particle descriptors */
+#define SAFE_TOTAL_DPART (SAFE_MAX_NQUEUE * SAFE_MAX_PART)
+#define SAFE_TOTAL_SPART (SAFE_MAX_NQUEUE * SAFE_MAX_PART)
+
+#define SAFE_RNG_MAXBUFSIZ 128 /* 32-bit words */
+
+#define SAFE_CARD(sid) (((sid) & 0xf0000000) >> 28)
+#define SAFE_SESSION(sid) ( (sid) & 0x0fffffff)
+#define SAFE_SID(crd, sesn) (((crd) << 28) | ((sesn) & 0x0fffffff))
+
+#define SAFE_DEF_RTY 0xff /* PCI Retry Timeout */
+#define SAFE_DEF_TOUT 0xff /* PCI TRDY Timeout */
+#define SAFE_DEF_CACHELINE 0x01 /* Cache Line setting */
+
+#ifdef __KERNEL__
+/*
+ * State associated with the allocation of each chunk
+ * of memory setup for DMA.
+ */
+struct safe_dma_alloc {
+ dma_addr_t dma_paddr;
+ void *dma_vaddr;
+};
+
+/*
+ * Cryptographic operand state. One of these exists for each
+ * source and destination operand passed in from the crypto
+ * subsystem. When possible source and destination operands
+ * refer to the same memory. More often they are distinct.
+ * We track the virtual address of each operand as well as
+ * where each is mapped for DMA.
+ */
+struct safe_operand {
+ union {
+ struct sk_buff *skb;
+ struct uio *io;
+ } u;
+ void *map;
+ int mapsize; /* total number of bytes in segs */
+ struct {
+ dma_addr_t ds_addr;
+ int ds_len;
+ int ds_tlen;
+ } segs[SAFE_MAX_PART];
+ int nsegs;
+};
+
+/*
+ * Packet engine ring entry and cryptographic operation state.
+ * The packet engine requires a ring of descriptors that contain
+ * pointers to various cryptographic state. However the ring
+ * configuration register allows you to specify an arbitrary size
+ * for ring entries. We use this feature to collect most of the
+ * state for each cryptographic request into one spot. Other than
+ * ring entries only the ``particle descriptors'' (scatter/gather
+ * lists) and the actual operand data are kept separate. The
+ * particle descriptors must also be organized in rings. The
+ * operand data can be located aribtrarily (modulo alignment constraints).
+ *
+ * Note that the descriptor ring is mapped onto the PCI bus so
+ * the hardware can DMA data. This means the entire ring must be
+ * contiguous.
+ */
+struct safe_ringentry {
+ struct safe_desc re_desc; /* command descriptor */
+ struct safe_sarec re_sa; /* SA record */
+ struct safe_sastate re_sastate; /* SA state record */
+
+ struct cryptop *re_crp; /* crypto operation */
+
+ struct safe_operand re_src; /* source operand */
+ struct safe_operand re_dst; /* destination operand */
+
+ int re_sesn; /* crypto session ID */
+ int re_flags;
+#define SAFE_QFLAGS_COPYOUTIV 0x1 /* copy back on completion */
+#define SAFE_QFLAGS_COPYOUTICV 0x2 /* copy back on completion */
+};
+
+#define re_src_skb re_src.u.skb
+#define re_src_io re_src.u.io
+#define re_src_map re_src.map
+#define re_src_nsegs re_src.nsegs
+#define re_src_segs re_src.segs
+#define re_src_mapsize re_src.mapsize
+
+#define re_dst_skb re_dst.u.skb
+#define re_dst_io re_dst.u.io
+#define re_dst_map re_dst.map
+#define re_dst_nsegs re_dst.nsegs
+#define re_dst_segs re_dst.segs
+#define re_dst_mapsize re_dst.mapsize
+
+struct rndstate_test;
+
+struct safe_session {
+ u_int32_t ses_used;
+ u_int32_t ses_klen; /* key length in bits */
+ u_int32_t ses_key[8]; /* DES/3DES/AES key */
+ u_int32_t ses_mlen; /* hmac length in bytes */
+ u_int32_t ses_hminner[5]; /* hmac inner state */
+ u_int32_t ses_hmouter[5]; /* hmac outer state */
+ u_int32_t ses_iv[4]; /* DES/3DES/AES iv */
+};
+
+struct safe_pkq {
+ struct list_head pkq_list;
+ struct cryptkop *pkq_krp;
+};
+
+struct safe_softc {
+ softc_device_decl sc_dev;
+ u32 sc_irq;
+
+ struct pci_dev *sc_pcidev;
+ ocf_iomem_t sc_base_addr;
+
+ u_int sc_chiprev; /* major/minor chip revision */
+ int sc_flags; /* device specific flags */
+#define SAFE_FLAGS_KEY 0x01 /* has key accelerator */
+#define SAFE_FLAGS_RNG 0x02 /* hardware rng */
+ int sc_suspended;
+ int sc_needwakeup; /* notify crypto layer */
+ int32_t sc_cid; /* crypto tag */
+
+ struct safe_dma_alloc sc_ringalloc; /* PE ring allocation state */
+ struct safe_ringentry *sc_ring; /* PE ring */
+ struct safe_ringentry *sc_ringtop; /* PE ring top */
+ struct safe_ringentry *sc_front; /* next free entry */
+ struct safe_ringentry *sc_back; /* next pending entry */
+ int sc_nqchip; /* # passed to chip */
+ spinlock_t sc_ringmtx; /* PE ring lock */
+ struct safe_pdesc *sc_spring; /* src particle ring */
+ struct safe_pdesc *sc_springtop; /* src particle ring top */
+ struct safe_pdesc *sc_spfree; /* next free src particle */
+ struct safe_dma_alloc sc_spalloc; /* src particle ring state */
+ struct safe_pdesc *sc_dpring; /* dest particle ring */
+ struct safe_pdesc *sc_dpringtop; /* dest particle ring top */
+ struct safe_pdesc *sc_dpfree; /* next free dest particle */
+ struct safe_dma_alloc sc_dpalloc; /* dst particle ring state */
+ int sc_nsessions; /* # of sessions */
+ struct safe_session *sc_sessions; /* sessions */
+
+ struct timer_list sc_pkto; /* PK polling */
+ spinlock_t sc_pkmtx; /* PK lock */
+ struct list_head sc_pkq; /* queue of PK requests */
+ struct safe_pkq *sc_pkq_cur; /* current processing request */
+ u_int32_t sc_pk_reslen, sc_pk_resoff;
+
+ int sc_max_dsize; /* maximum safe DMA size */
+};
+#endif /* __KERNEL__ */
+
+struct safe_stats {
+ u_int64_t st_ibytes;
+ u_int64_t st_obytes;
+ u_int32_t st_ipackets;
+ u_int32_t st_opackets;
+ u_int32_t st_invalid; /* invalid argument */
+ u_int32_t st_badsession; /* invalid session id */
+ u_int32_t st_badflags; /* flags indicate !(mbuf | uio) */
+ u_int32_t st_nodesc; /* op submitted w/o descriptors */
+ u_int32_t st_badalg; /* unsupported algorithm */
+ u_int32_t st_ringfull; /* PE descriptor ring full */
+ u_int32_t st_peoperr; /* PE marked error */
+ u_int32_t st_dmaerr; /* PE DMA error */
+ u_int32_t st_bypasstoobig; /* bypass > 96 bytes */
+ u_int32_t st_skipmismatch; /* enc part begins before auth part */
+ u_int32_t st_lenmismatch; /* enc length different auth length */
+ u_int32_t st_coffmisaligned; /* crypto offset not 32-bit aligned */
+ u_int32_t st_cofftoobig; /* crypto offset > 255 words */
+ u_int32_t st_iovmisaligned; /* iov op not aligned */
+ u_int32_t st_iovnotuniform; /* iov op not suitable */
+ u_int32_t st_unaligned; /* unaligned src caused copy */
+ u_int32_t st_notuniform; /* non-uniform src caused copy */
+ u_int32_t st_nomap; /* bus_dmamap_create failed */
+ u_int32_t st_noload; /* bus_dmamap_load_* failed */
+ u_int32_t st_nombuf; /* MGET* failed */
+ u_int32_t st_nomcl; /* MCLGET* failed */
+ u_int32_t st_maxqchip; /* max mcr1 ops out for processing */
+ u_int32_t st_rng; /* RNG requests */
+ u_int32_t st_rngalarm; /* RNG alarm requests */
+ u_int32_t st_noicvcopy; /* ICV data copies suppressed */
+};
+#endif /* _SAFE_SAFEVAR_H_ */
diff --git a/crypto/ocf/safe/sha1.c b/crypto/ocf/safe/sha1.c
new file mode 100644
index 000000000000..4e360e20db30
--- /dev/null
+++ b/crypto/ocf/safe/sha1.c
@@ -0,0 +1,279 @@
+/* $KAME: sha1.c,v 1.5 2000/11/08 06:13:08 itojun Exp $ */
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * FIPS pub 180-1: Secure Hash Algorithm (SHA-1)
+ * based on: http://csrc.nist.gov/fips/fip180-1.txt
+ * implemented by Jun-ichiro itojun Itoh <itojun@itojun.org>
+ */
+
+#if 0
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/crypto/sha1.c,v 1.9 2003/06/10 21:36:57 obrien Exp $");
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/time.h>
+#include <sys/systm.h>
+
+#include <crypto/sha1.h>
+#endif
+
+/* sanity check */
+#if BYTE_ORDER != BIG_ENDIAN
+# if BYTE_ORDER != LITTLE_ENDIAN
+# define unsupported 1
+# endif
+#endif
+
+#ifndef unsupported
+
+/* constant table */
+static u_int32_t _K[] = { 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 };
+#define K(t) _K[(t) / 20]
+
+#define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d)))
+#define F1(b, c, d) (((b) ^ (c)) ^ (d))
+#define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d)))
+#define F3(b, c, d) (((b) ^ (c)) ^ (d))
+
+#define S(n, x) (((x) << (n)) | ((x) >> (32 - n)))
+
+#undef H
+#define H(n) (ctxt->h.b32[(n)])
+#define COUNT (ctxt->count)
+#define BCOUNT (ctxt->c.b64[0] / 8)
+#define W(n) (ctxt->m.b32[(n)])
+
+#define PUTBYTE(x) { \
+ ctxt->m.b8[(COUNT % 64)] = (x); \
+ COUNT++; \
+ COUNT %= 64; \
+ ctxt->c.b64[0] += 8; \
+ if (COUNT % 64 == 0) \
+ sha1_step(ctxt); \
+ }
+
+#define PUTPAD(x) { \
+ ctxt->m.b8[(COUNT % 64)] = (x); \
+ COUNT++; \
+ COUNT %= 64; \
+ if (COUNT % 64 == 0) \
+ sha1_step(ctxt); \
+ }
+
+static void sha1_step(struct sha1_ctxt *);
+
+static void
+sha1_step(ctxt)
+ struct sha1_ctxt *ctxt;
+{
+ u_int32_t a, b, c, d, e;
+ size_t t, s;
+ u_int32_t tmp;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ struct sha1_ctxt tctxt;
+ bcopy(&ctxt->m.b8[0], &tctxt.m.b8[0], 64);
+ ctxt->m.b8[0] = tctxt.m.b8[3]; ctxt->m.b8[1] = tctxt.m.b8[2];
+ ctxt->m.b8[2] = tctxt.m.b8[1]; ctxt->m.b8[3] = tctxt.m.b8[0];
+ ctxt->m.b8[4] = tctxt.m.b8[7]; ctxt->m.b8[5] = tctxt.m.b8[6];
+ ctxt->m.b8[6] = tctxt.m.b8[5]; ctxt->m.b8[7] = tctxt.m.b8[4];
+ ctxt->m.b8[8] = tctxt.m.b8[11]; ctxt->m.b8[9] = tctxt.m.b8[10];
+ ctxt->m.b8[10] = tctxt.m.b8[9]; ctxt->m.b8[11] = tctxt.m.b8[8];
+ ctxt->m.b8[12] = tctxt.m.b8[15]; ctxt->m.b8[13] = tctxt.m.b8[14];
+ ctxt->m.b8[14] = tctxt.m.b8[13]; ctxt->m.b8[15] = tctxt.m.b8[12];
+ ctxt->m.b8[16] = tctxt.m.b8[19]; ctxt->m.b8[17] = tctxt.m.b8[18];
+ ctxt->m.b8[18] = tctxt.m.b8[17]; ctxt->m.b8[19] = tctxt.m.b8[16];
+ ctxt->m.b8[20] = tctxt.m.b8[23]; ctxt->m.b8[21] = tctxt.m.b8[22];
+ ctxt->m.b8[22] = tctxt.m.b8[21]; ctxt->m.b8[23] = tctxt.m.b8[20];
+ ctxt->m.b8[24] = tctxt.m.b8[27]; ctxt->m.b8[25] = tctxt.m.b8[26];
+ ctxt->m.b8[26] = tctxt.m.b8[25]; ctxt->m.b8[27] = tctxt.m.b8[24];
+ ctxt->m.b8[28] = tctxt.m.b8[31]; ctxt->m.b8[29] = tctxt.m.b8[30];
+ ctxt->m.b8[30] = tctxt.m.b8[29]; ctxt->m.b8[31] = tctxt.m.b8[28];
+ ctxt->m.b8[32] = tctxt.m.b8[35]; ctxt->m.b8[33] = tctxt.m.b8[34];
+ ctxt->m.b8[34] = tctxt.m.b8[33]; ctxt->m.b8[35] = tctxt.m.b8[32];
+ ctxt->m.b8[36] = tctxt.m.b8[39]; ctxt->m.b8[37] = tctxt.m.b8[38];
+ ctxt->m.b8[38] = tctxt.m.b8[37]; ctxt->m.b8[39] = tctxt.m.b8[36];
+ ctxt->m.b8[40] = tctxt.m.b8[43]; ctxt->m.b8[41] = tctxt.m.b8[42];
+ ctxt->m.b8[42] = tctxt.m.b8[41]; ctxt->m.b8[43] = tctxt.m.b8[40];
+ ctxt->m.b8[44] = tctxt.m.b8[47]; ctxt->m.b8[45] = tctxt.m.b8[46];
+ ctxt->m.b8[46] = tctxt.m.b8[45]; ctxt->m.b8[47] = tctxt.m.b8[44];
+ ctxt->m.b8[48] = tctxt.m.b8[51]; ctxt->m.b8[49] = tctxt.m.b8[50];
+ ctxt->m.b8[50] = tctxt.m.b8[49]; ctxt->m.b8[51] = tctxt.m.b8[48];
+ ctxt->m.b8[52] = tctxt.m.b8[55]; ctxt->m.b8[53] = tctxt.m.b8[54];
+ ctxt->m.b8[54] = tctxt.m.b8[53]; ctxt->m.b8[55] = tctxt.m.b8[52];
+ ctxt->m.b8[56] = tctxt.m.b8[59]; ctxt->m.b8[57] = tctxt.m.b8[58];
+ ctxt->m.b8[58] = tctxt.m.b8[57]; ctxt->m.b8[59] = tctxt.m.b8[56];
+ ctxt->m.b8[60] = tctxt.m.b8[63]; ctxt->m.b8[61] = tctxt.m.b8[62];
+ ctxt->m.b8[62] = tctxt.m.b8[61]; ctxt->m.b8[63] = tctxt.m.b8[60];
+#endif
+
+ a = H(0); b = H(1); c = H(2); d = H(3); e = H(4);
+
+ for (t = 0; t < 20; t++) {
+ s = t & 0x0f;
+ if (t >= 16) {
+ W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
+ }
+ tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t);
+ e = d; d = c; c = S(30, b); b = a; a = tmp;
+ }
+ for (t = 20; t < 40; t++) {
+ s = t & 0x0f;
+ W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
+ tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t);
+ e = d; d = c; c = S(30, b); b = a; a = tmp;
+ }
+ for (t = 40; t < 60; t++) {
+ s = t & 0x0f;
+ W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
+ tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t);
+ e = d; d = c; c = S(30, b); b = a; a = tmp;
+ }
+ for (t = 60; t < 80; t++) {
+ s = t & 0x0f;
+ W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^ W((s+2) & 0x0f) ^ W(s));
+ tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t);
+ e = d; d = c; c = S(30, b); b = a; a = tmp;
+ }
+
+ H(0) = H(0) + a;
+ H(1) = H(1) + b;
+ H(2) = H(2) + c;
+ H(3) = H(3) + d;
+ H(4) = H(4) + e;
+
+ bzero(&ctxt->m.b8[0], 64);
+}
+
+/*------------------------------------------------------------*/
+
+void
+sha1_init(ctxt)
+ struct sha1_ctxt *ctxt;
+{
+ bzero(ctxt, sizeof(struct sha1_ctxt));
+ H(0) = 0x67452301;
+ H(1) = 0xefcdab89;
+ H(2) = 0x98badcfe;
+ H(3) = 0x10325476;
+ H(4) = 0xc3d2e1f0;
+}
+
+void
+sha1_pad(ctxt)
+ struct sha1_ctxt *ctxt;
+{
+ size_t padlen; /*pad length in bytes*/
+ size_t padstart;
+
+ PUTPAD(0x80);
+
+ padstart = COUNT % 64;
+ padlen = 64 - padstart;
+ if (padlen < 8) {
+ bzero(&ctxt->m.b8[padstart], padlen);
+ COUNT += padlen;
+ COUNT %= 64;
+ sha1_step(ctxt);
+ padstart = COUNT % 64; /* should be 0 */
+ padlen = 64 - padstart; /* should be 64 */
+ }
+ bzero(&ctxt->m.b8[padstart], padlen - 8);
+ COUNT += (padlen - 8);
+ COUNT %= 64;
+#if BYTE_ORDER == BIG_ENDIAN
+ PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]);
+ PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[3]);
+ PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[5]);
+ PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[7]);
+#else
+ PUTPAD(ctxt->c.b8[7]); PUTPAD(ctxt->c.b8[6]);
+ PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[4]);
+ PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[2]);
+ PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[0]);
+#endif
+}
+
+void
+sha1_loop(ctxt, input, len)
+ struct sha1_ctxt *ctxt;
+ const u_int8_t *input;
+ size_t len;
+{
+ size_t gaplen;
+ size_t gapstart;
+ size_t off;
+ size_t copysiz;
+
+ off = 0;
+
+ while (off < len) {
+ gapstart = COUNT % 64;
+ gaplen = 64 - gapstart;
+
+ copysiz = (gaplen < len - off) ? gaplen : len - off;
+ bcopy(&input[off], &ctxt->m.b8[gapstart], copysiz);
+ COUNT += copysiz;
+ COUNT %= 64;
+ ctxt->c.b64[0] += copysiz * 8;
+ if (COUNT % 64 == 0)
+ sha1_step(ctxt);
+ off += copysiz;
+ }
+}
+
+void
+sha1_result(ctxt, digest0)
+ struct sha1_ctxt *ctxt;
+ caddr_t digest0;
+{
+ u_int8_t *digest;
+
+ digest = (u_int8_t *)digest0;
+ sha1_pad(ctxt);
+#if BYTE_ORDER == BIG_ENDIAN
+ bcopy(&ctxt->h.b8[0], digest, 20);
+#else
+ digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2];
+ digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0];
+ digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6];
+ digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4];
+ digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10];
+ digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8];
+ digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14];
+ digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12];
+ digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18];
+ digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16];
+#endif
+}
+
+#endif /*unsupported*/
diff --git a/crypto/ocf/safe/sha1.h b/crypto/ocf/safe/sha1.h
new file mode 100644
index 000000000000..0e19d9071f93
--- /dev/null
+++ b/crypto/ocf/safe/sha1.h
@@ -0,0 +1,72 @@
+/* $FreeBSD: src/sys/crypto/sha1.h,v 1.8 2002/03/20 05:13:50 alfred Exp $ */
+/* $KAME: sha1.h,v 1.5 2000/03/27 04:36:23 sumikawa Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * FIPS pub 180-1: Secure Hash Algorithm (SHA-1)
+ * based on: http://csrc.nist.gov/fips/fip180-1.txt
+ * implemented by Jun-ichiro itojun Itoh <itojun@itojun.org>
+ */
+
+#ifndef _NETINET6_SHA1_H_
+#define _NETINET6_SHA1_H_
+
+struct sha1_ctxt {
+ union {
+ u_int8_t b8[20];
+ u_int32_t b32[5];
+ } h;
+ union {
+ u_int8_t b8[8];
+ u_int64_t b64[1];
+ } c;
+ union {
+ u_int8_t b8[64];
+ u_int32_t b32[16];
+ } m;
+ u_int8_t count;
+};
+
+#ifdef __KERNEL__
+extern void sha1_init(struct sha1_ctxt *);
+extern void sha1_pad(struct sha1_ctxt *);
+extern void sha1_loop(struct sha1_ctxt *, const u_int8_t *, size_t);
+extern void sha1_result(struct sha1_ctxt *, caddr_t);
+
+/* compatibilty with other SHA1 source codes */
+typedef struct sha1_ctxt SHA1_CTX;
+#define SHA1Init(x) sha1_init((x))
+#define SHA1Update(x, y, z) sha1_loop((x), (y), (z))
+#define SHA1Final(x, y) sha1_result((y), (x))
+#endif /* __KERNEL__ */
+
+#define SHA1_RESULTLEN (160/8)
+
+#endif /*_NETINET6_SHA1_H_*/
diff --git a/crypto/ocf/talitos/Makefile b/crypto/ocf/talitos/Makefile
new file mode 100644
index 000000000000..2591b8aef581
--- /dev/null
+++ b/crypto/ocf/talitos/Makefile
@@ -0,0 +1,12 @@
+# for SGlinux builds
+-include $(ROOTDIR)/modules/.config
+
+obj-$(CONFIG_OCF_TALITOS) += talitos.o
+
+obj ?= .
+EXTRA_CFLAGS += -I$(obj)/.. -I$(obj)/
+
+ifdef TOPDIR
+-include $(TOPDIR)/Rules.make
+endif
+
diff --git a/crypto/ocf/talitos/talitos.c b/crypto/ocf/talitos/talitos.c
new file mode 100644
index 000000000000..eaf3f30b62f2
--- /dev/null
+++ b/crypto/ocf/talitos/talitos.c
@@ -0,0 +1,1359 @@
+/*
+ * crypto/ocf/talitos/talitos.c
+ *
+ * An OCF-Linux module that uses Freescale's SEC to do the crypto.
+ * Based on crypto/ocf/hifn and crypto/ocf/safe OCF drivers
+ *
+ * Copyright (c) 2006 Freescale Semiconductor, Inc.
+ *
+ * This code written by Kim A. B. Phillips <kim.phillips@freescale.com>
+ * some code copied from files with the following:
+ * Copyright (C) 2004-2007 David McCullough <david_mccullough@securecomputing.com
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * NOTES:
+ *
+ * The Freescale SEC (also known as 'talitos') resides on the
+ * internal bus, and runs asynchronous to the processor core. It has
+ * a wide gamut of cryptographic acceleration features, including single-
+ * pass IPsec (also known as algorithm chaining). To properly utilize
+ * all of the SEC's performance enhancing features, further reworking
+ * of higher level code (framework, applications) will be necessary.
+ *
+ * The following table shows which SEC version is present in which devices:
+ *
+ * Devices SEC version
+ *
+ * 8272, 8248 SEC 1.0
+ * 885, 875 SEC 1.2
+ * 8555E, 8541E SEC 2.0
+ * 8349E SEC 2.01
+ * 8548E SEC 2.1
+ *
+ * The following table shows the features offered by each SEC version:
+ *
+ * Max. chan-
+ * version Bus I/F Clock nels DEU AESU AFEU MDEU PKEU RNG KEU
+ *
+ * SEC 1.0 internal 64b 100MHz 4 1 1 1 1 1 1 0
+ * SEC 1.2 internal 32b 66MHz 1 1 1 0 1 0 0 0
+ * SEC 2.0 internal 64b 166MHz 4 1 1 1 1 1 1 0
+ * SEC 2.01 internal 64b 166MHz 4 1 1 1 1 1 1 0
+ * SEC 2.1 internal 64b 333MHz 4 1 1 1 1 1 1 1
+ *
+ * Each execution unit in the SEC has two modes of execution; channel and
+ * slave/debug. This driver employs the channel infrastructure in the
+ * device for convenience. Only the RNG is directly accessed due to the
+ * convenience of its random fifo pool. The relationship between the
+ * channels and execution units is depicted in the following diagram:
+ *
+ * ------- ------------
+ * ---| ch0 |---| |
+ * ------- | |
+ * | |------+-------+-------+-------+------------
+ * ------- | | | | | | |
+ * ---| ch1 |---| | | | | | |
+ * ------- | | ------ ------ ------ ------ ------
+ * |controller| |DEU | |AESU| |MDEU| |PKEU| ... |RNG |
+ * ------- | | ------ ------ ------ ------ ------
+ * ---| ch2 |---| | | | | | |
+ * ------- | | | | | | |
+ * | |------+-------+-------+-------+------------
+ * ------- | |
+ * ---| ch3 |---| |
+ * ------- ------------
+ *
+ * Channel ch0 may drive an aes operation to the aes unit (AESU),
+ * and, at the same time, ch1 may drive a message digest operation
+ * to the mdeu. Each channel has an input descriptor FIFO, and the
+ * FIFO can contain, e.g. on the 8541E, up to 24 entries, before a
+ * a buffer overrun error is triggered. The controller is responsible
+ * for fetching the data from descriptor pointers, and passing the
+ * data to the appropriate EUs. The controller also writes the
+ * cryptographic operation's result to memory. The SEC notifies
+ * completion by triggering an interrupt and/or setting the 1st byte
+ * of the hdr field to 0xff.
+ *
+ * TODO:
+ * o support more algorithms
+ * o support more versions of the SEC
+ * o add support for linux 2.4
+ * o scatter-gather (sg) support
+ * o add support for public key ops (PKEU)
+ * o add statistics
+ */
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <asm/scatterlist.h>
+#include <linux/dma-mapping.h> /* dma_map_single() */
+#include <linux/moduleparam.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+#include <linux/platform_device.h>
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+#include <linux/of_platform.h>
+#endif
+
+#include <cryptodev.h>
+#include <uio.h>
+
+#define DRV_NAME "talitos"
+
+#include "talitos_dev.h"
+#include "talitos_soft.h"
+
+#define read_random(p,l) get_random_bytes(p,l)
+
+const char talitos_driver_name[] = "Talitos OCF";
+const char talitos_driver_version[] = "0.2";
+
+static int talitos_newsession(device_t dev, u_int32_t *sidp,
+ struct cryptoini *cri);
+static int talitos_freesession(device_t dev, u_int64_t tid);
+static int talitos_process(device_t dev, struct cryptop *crp, int hint);
+static void dump_talitos_status(struct talitos_softc *sc);
+static int talitos_submit(struct talitos_softc *sc, struct talitos_desc *td,
+ int chsel);
+static void talitos_doneprocessing(struct talitos_softc *sc);
+static void talitos_init_device(struct talitos_softc *sc);
+static void talitos_reset_device_master(struct talitos_softc *sc);
+static void talitos_reset_device(struct talitos_softc *sc);
+static void talitos_errorprocessing(struct talitos_softc *sc);
+#ifdef CONFIG_PPC_MERGE
+static int talitos_probe(struct of_device *ofdev, const struct of_device_id *match);
+static int talitos_remove(struct of_device *ofdev);
+#else
+static int talitos_probe(struct platform_device *pdev);
+static int talitos_remove(struct platform_device *pdev);
+#endif
+#ifdef CONFIG_OCF_RANDOMHARVEST
+static int talitos_read_random(void *arg, u_int32_t *buf, int maxwords);
+static void talitos_rng_init(struct talitos_softc *sc);
+#endif
+
+static device_method_t talitos_methods = {
+ /* crypto device methods */
+ DEVMETHOD(cryptodev_newsession, talitos_newsession),
+ DEVMETHOD(cryptodev_freesession,talitos_freesession),
+ DEVMETHOD(cryptodev_process, talitos_process),
+};
+
+#define debug talitos_debug
+int talitos_debug = 0;
+module_param(talitos_debug, int, 0644);
+MODULE_PARM_DESC(talitos_debug, "Enable debug");
+
+static inline void talitos_write(volatile unsigned *addr, u32 val)
+{
+ out_be32(addr, val);
+}
+
+static inline u32 talitos_read(volatile unsigned *addr)
+{
+ u32 val;
+ val = in_be32(addr);
+ return val;
+}
+
+static void dump_talitos_status(struct talitos_softc *sc)
+{
+ unsigned int v, v_hi, i, *ptr;
+ v = talitos_read(sc->sc_base_addr + TALITOS_MCR);
+ v_hi = talitos_read(sc->sc_base_addr + TALITOS_MCR_HI);
+ printk(KERN_INFO "%s: MCR 0x%08x_%08x\n",
+ device_get_nameunit(sc->sc_cdev), v, v_hi);
+ v = talitos_read(sc->sc_base_addr + TALITOS_IMR);
+ v_hi = talitos_read(sc->sc_base_addr + TALITOS_IMR_HI);
+ printk(KERN_INFO "%s: IMR 0x%08x_%08x\n",
+ device_get_nameunit(sc->sc_cdev), v, v_hi);
+ v = talitos_read(sc->sc_base_addr + TALITOS_ISR);
+ v_hi = talitos_read(sc->sc_base_addr + TALITOS_ISR_HI);
+ printk(KERN_INFO "%s: ISR 0x%08x_%08x\n",
+ device_get_nameunit(sc->sc_cdev), v, v_hi);
+ for (i = 0; i < sc->sc_num_channels; i++) {
+ v = talitos_read(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
+ TALITOS_CH_CDPR);
+ v_hi = talitos_read(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
+ TALITOS_CH_CDPR_HI);
+ printk(KERN_INFO "%s: CDPR ch%d 0x%08x_%08x\n",
+ device_get_nameunit(sc->sc_cdev), i, v, v_hi);
+ }
+ for (i = 0; i < sc->sc_num_channels; i++) {
+ v = talitos_read(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
+ TALITOS_CH_CCPSR);
+ v_hi = talitos_read(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
+ TALITOS_CH_CCPSR_HI);
+ printk(KERN_INFO "%s: CCPSR ch%d 0x%08x_%08x\n",
+ device_get_nameunit(sc->sc_cdev), i, v, v_hi);
+ }
+ ptr = sc->sc_base_addr + TALITOS_CH_DESCBUF;
+ for (i = 0; i < 16; i++) {
+ v = talitos_read(ptr++); v_hi = talitos_read(ptr++);
+ printk(KERN_INFO "%s: DESCBUF ch0 0x%08x_%08x (tdp%02d)\n",
+ device_get_nameunit(sc->sc_cdev), v, v_hi, i);
+ }
+ return;
+}
+
+
+#ifdef CONFIG_OCF_RANDOMHARVEST
+/*
+ * pull random numbers off the RNG FIFO, not exceeding amount available
+ */
+static int
+talitos_read_random(void *arg, u_int32_t *buf, int maxwords)
+{
+ struct talitos_softc *sc = (struct talitos_softc *) arg;
+ int rc;
+ u_int32_t v;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ /* check for things like FIFO underflow */
+ v = talitos_read(sc->sc_base_addr + TALITOS_RNGISR_HI);
+ if (unlikely(v)) {
+ printk(KERN_ERR "%s: RNGISR_HI error %08x\n",
+ device_get_nameunit(sc->sc_cdev), v);
+ return 0;
+ }
+ /*
+ * OFL is number of available 64-bit words,
+ * shift and convert to a 32-bit word count
+ */
+ v = talitos_read(sc->sc_base_addr + TALITOS_RNGSR_HI);
+ v = (v & TALITOS_RNGSR_HI_OFL) >> (16 - 1);
+ if (maxwords > v)
+ maxwords = v;
+ for (rc = 0; rc < maxwords; rc++) {
+ buf[rc] = talitos_read(sc->sc_base_addr +
+ TALITOS_RNG_FIFO + rc*sizeof(u_int32_t));
+ }
+ if (maxwords & 1) {
+ /*
+ * RNG will complain with an AE in the RNGISR
+ * if we don't complete the pairs of 32-bit reads
+ * to its 64-bit register based FIFO
+ */
+ v = talitos_read(sc->sc_base_addr +
+ TALITOS_RNG_FIFO + rc*sizeof(u_int32_t));
+ }
+
+ return rc;
+}
+
+static void
+talitos_rng_init(struct talitos_softc *sc)
+{
+ u_int32_t v;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+ /* reset RNG EU */
+ v = talitos_read(sc->sc_base_addr + TALITOS_RNGRCR_HI);
+ v |= TALITOS_RNGRCR_HI_SR;
+ talitos_write(sc->sc_base_addr + TALITOS_RNGRCR_HI, v);
+ while ((talitos_read(sc->sc_base_addr + TALITOS_RNGSR_HI)
+ & TALITOS_RNGSR_HI_RD) == 0)
+ cpu_relax();
+ /*
+ * we tell the RNG to start filling the RNG FIFO
+ * by writing the RNGDSR
+ */
+ v = talitos_read(sc->sc_base_addr + TALITOS_RNGDSR_HI);
+ talitos_write(sc->sc_base_addr + TALITOS_RNGDSR_HI, v);
+ /*
+ * 64 bits of data will be pushed onto the FIFO every
+ * 256 SEC cycles until the FIFO is full. The RNG then
+ * attempts to keep the FIFO full.
+ */
+ v = talitos_read(sc->sc_base_addr + TALITOS_RNGISR_HI);
+ if (v) {
+ printk(KERN_ERR "%s: RNGISR_HI error %08x\n",
+ device_get_nameunit(sc->sc_cdev), v);
+ return;
+ }
+ /*
+ * n.b. we need to add a FIPS test here - if the RNG is going
+ * to fail, it's going to fail at reset time
+ */
+ return;
+}
+#endif /* CONFIG_OCF_RANDOMHARVEST */
+
+/*
+ * Generate a new software session.
+ */
+static int
+talitos_newsession(device_t dev, u_int32_t *sidp, struct cryptoini *cri)
+{
+ struct cryptoini *c, *encini = NULL, *macini = NULL;
+ struct talitos_softc *sc = device_get_softc(dev);
+ struct talitos_session *ses = NULL;
+ int sesn;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+ if (sidp == NULL || cri == NULL || sc == NULL) {
+ DPRINTF("%s,%d - EINVAL\n", __FILE__, __LINE__);
+ return EINVAL;
+ }
+ for (c = cri; c != NULL; c = c->cri_next) {
+ if (c->cri_alg == CRYPTO_MD5 ||
+ c->cri_alg == CRYPTO_MD5_HMAC ||
+ c->cri_alg == CRYPTO_SHA1 ||
+ c->cri_alg == CRYPTO_SHA1_HMAC ||
+ c->cri_alg == CRYPTO_NULL_HMAC) {
+ if (macini)
+ return EINVAL;
+ macini = c;
+ } else if (c->cri_alg == CRYPTO_DES_CBC ||
+ c->cri_alg == CRYPTO_3DES_CBC ||
+ c->cri_alg == CRYPTO_AES_CBC ||
+ c->cri_alg == CRYPTO_NULL_CBC) {
+ if (encini)
+ return EINVAL;
+ encini = c;
+ } else {
+ DPRINTF("UNKNOWN c->cri_alg %d\n", encini->cri_alg);
+ return EINVAL;
+ }
+ }
+ if (encini == NULL && macini == NULL)
+ return EINVAL;
+ if (encini) {
+ /* validate key length */
+ switch (encini->cri_alg) {
+ case CRYPTO_DES_CBC:
+ if (encini->cri_klen != 64)
+ return EINVAL;
+ break;
+ case CRYPTO_3DES_CBC:
+ if (encini->cri_klen != 192) {
+ return EINVAL;
+ }
+ break;
+ case CRYPTO_AES_CBC:
+ if (encini->cri_klen != 128 &&
+ encini->cri_klen != 192 &&
+ encini->cri_klen != 256)
+ return EINVAL;
+ break;
+ default:
+ DPRINTF("UNKNOWN encini->cri_alg %d\n",
+ encini->cri_alg);
+ return EINVAL;
+ }
+ }
+
+ if (sc->sc_sessions == NULL) {
+ ses = sc->sc_sessions = (struct talitos_session *)
+ kmalloc(sizeof(struct talitos_session), SLAB_ATOMIC);
+ if (ses == NULL)
+ return ENOMEM;
+ memset(ses, 0, sizeof(struct talitos_session));
+ sesn = 0;
+ sc->sc_nsessions = 1;
+ } else {
+ for (sesn = 0; sesn < sc->sc_nsessions; sesn++) {
+ if (sc->sc_sessions[sesn].ses_used == 0) {
+ ses = &sc->sc_sessions[sesn];
+ break;
+ }
+ }
+
+ if (ses == NULL) {
+ /* allocating session */
+ sesn = sc->sc_nsessions;
+ ses = (struct talitos_session *) kmalloc(
+ (sesn + 1) * sizeof(struct talitos_session),
+ SLAB_ATOMIC);
+ if (ses == NULL)
+ return ENOMEM;
+ memset(ses, 0,
+ (sesn + 1) * sizeof(struct talitos_session));
+ memcpy(ses, sc->sc_sessions,
+ sesn * sizeof(struct talitos_session));
+ memset(sc->sc_sessions, 0,
+ sesn * sizeof(struct talitos_session));
+ kfree(sc->sc_sessions);
+ sc->sc_sessions = ses;
+ ses = &sc->sc_sessions[sesn];
+ sc->sc_nsessions++;
+ }
+ }
+
+ ses->ses_used = 1;
+
+ if (encini) {
+ /* get an IV */
+ /* XXX may read fewer than requested */
+ read_random(ses->ses_iv, sizeof(ses->ses_iv));
+
+ ses->ses_klen = (encini->cri_klen + 7) / 8;
+ memcpy(ses->ses_key, encini->cri_key, ses->ses_klen);
+ if (macini) {
+ /* doing hash on top of cipher */
+ ses->ses_hmac_len = (macini->cri_klen + 7) / 8;
+ memcpy(ses->ses_hmac, macini->cri_key,
+ ses->ses_hmac_len);
+ }
+ } else if (macini) {
+ /* doing hash */
+ ses->ses_klen = (macini->cri_klen + 7) / 8;
+ memcpy(ses->ses_key, macini->cri_key, ses->ses_klen);
+ }
+
+ /* back compat way of determining MSC result len */
+ if (macini) {
+ ses->ses_mlen = macini->cri_mlen;
+ if (ses->ses_mlen == 0) {
+ if (macini->cri_alg == CRYPTO_MD5_HMAC)
+ ses->ses_mlen = MD5_HASH_LEN;
+ else
+ ses->ses_mlen = SHA1_HASH_LEN;
+ }
+ }
+
+ /* really should make up a template td here,
+ * and only fill things like i/o and direction in process() */
+
+ /* assign session ID */
+ *sidp = TALITOS_SID(sc->sc_num, sesn);
+ return 0;
+}
+
+/*
+ * Deallocate a session.
+ */
+static int
+talitos_freesession(device_t dev, u_int64_t tid)
+{
+ struct talitos_softc *sc = device_get_softc(dev);
+ int session, ret;
+ u_int32_t sid = ((u_int32_t) tid) & 0xffffffff;
+
+ if (sc == NULL)
+ return EINVAL;
+ session = TALITOS_SESSION(sid);
+ if (session < sc->sc_nsessions) {
+ memset(&sc->sc_sessions[session], 0,
+ sizeof(sc->sc_sessions[session]));
+ ret = 0;
+ } else
+ ret = EINVAL;
+ return ret;
+}
+
+/*
+ * launch device processing - it will come back with done notification
+ * in the form of an interrupt and/or HDR_DONE_BITS in header
+ */
+static int
+talitos_submit(
+ struct talitos_softc *sc,
+ struct talitos_desc *td,
+ int chsel)
+{
+ u_int32_t v;
+
+ v = dma_map_single(NULL, td, sizeof(*td), DMA_TO_DEVICE);
+ talitos_write(sc->sc_base_addr +
+ chsel*TALITOS_CH_OFFSET + TALITOS_CH_FF, 0);
+ talitos_write(sc->sc_base_addr +
+ chsel*TALITOS_CH_OFFSET + TALITOS_CH_FF_HI, v);
+ return 0;
+}
+
+static int
+talitos_process(device_t dev, struct cryptop *crp, int hint)
+{
+ int i, err = 0, ivsize;
+ struct talitos_softc *sc = device_get_softc(dev);
+ struct cryptodesc *crd1, *crd2, *maccrd, *enccrd;
+ caddr_t iv;
+ struct talitos_session *ses;
+ struct talitos_desc *td;
+ unsigned long flags;
+ /* descriptor mappings */
+ int hmac_key, hmac_data, cipher_iv, cipher_key,
+ in_fifo, out_fifo, cipher_iv_out;
+ static int chsel = -1;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ if (crp == NULL || crp->crp_callback == NULL || sc == NULL) {
+ return EINVAL;
+ }
+ crp->crp_etype = 0;
+ if (TALITOS_SESSION(crp->crp_sid) >= sc->sc_nsessions) {
+ return EINVAL;
+ }
+
+ ses = &sc->sc_sessions[TALITOS_SESSION(crp->crp_sid)];
+
+ /* enter the channel scheduler */
+ spin_lock_irqsave(&sc->sc_chnfifolock[sc->sc_num_channels], flags);
+
+ /* reuse channel that already had/has requests for the required EU */
+ for (i = 0; i < sc->sc_num_channels; i++) {
+ if (sc->sc_chnlastalg[i] == crp->crp_desc->crd_alg)
+ break;
+ }
+ if (i == sc->sc_num_channels) {
+ /*
+ * haven't seen this algo the last sc_num_channels or more
+ * use round robin in this case
+ * nb: sc->sc_num_channels must be power of 2
+ */
+ chsel = (chsel + 1) & (sc->sc_num_channels - 1);
+ } else {
+ /*
+ * matches channel with same target execution unit;
+ * use same channel in this case
+ */
+ chsel = i;
+ }
+ sc->sc_chnlastalg[chsel] = crp->crp_desc->crd_alg;
+
+ /* release the channel scheduler lock */
+ spin_unlock_irqrestore(&sc->sc_chnfifolock[sc->sc_num_channels], flags);
+
+ /* acquire the selected channel fifo lock */
+ spin_lock_irqsave(&sc->sc_chnfifolock[chsel], flags);
+
+ /* find and reserve next available descriptor-cryptop pair */
+ for (i = 0; i < sc->sc_chfifo_len; i++) {
+ if (sc->sc_chnfifo[chsel][i].cf_desc.hdr == 0) {
+ /*
+ * ensure correct descriptor formation by
+ * avoiding inadvertently setting "optional" entries
+ * e.g. not using "optional" dptr2 for MD/HMAC descs
+ */
+ memset(&sc->sc_chnfifo[chsel][i].cf_desc,
+ 0, sizeof(*td));
+ /* reserve it with done notification request bit */
+ sc->sc_chnfifo[chsel][i].cf_desc.hdr |=
+ TALITOS_DONE_NOTIFY;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&sc->sc_chnfifolock[chsel], flags);
+
+ if (i == sc->sc_chfifo_len) {
+ /* fifo full */
+ err = ERESTART;
+ goto errout;
+ }
+
+ td = &sc->sc_chnfifo[chsel][i].cf_desc;
+ sc->sc_chnfifo[chsel][i].cf_crp = crp;
+
+ crd1 = crp->crp_desc;
+ if (crd1 == NULL) {
+ err = EINVAL;
+ goto errout;
+ }
+ crd2 = crd1->crd_next;
+ /* prevent compiler warning */
+ hmac_key = 0;
+ hmac_data = 0;
+ if (crd2 == NULL) {
+ td->hdr |= TD_TYPE_COMMON_NONSNOOP_NO_AFEU;
+ /* assign descriptor dword ptr mappings for this desc. type */
+ cipher_iv = 1;
+ cipher_key = 2;
+ in_fifo = 3;
+ cipher_iv_out = 5;
+ if (crd1->crd_alg == CRYPTO_MD5_HMAC ||
+ crd1->crd_alg == CRYPTO_SHA1_HMAC ||
+ crd1->crd_alg == CRYPTO_SHA1 ||
+ crd1->crd_alg == CRYPTO_MD5) {
+ out_fifo = 5;
+ maccrd = crd1;
+ enccrd = NULL;
+ } else if (crd1->crd_alg == CRYPTO_DES_CBC ||
+ crd1->crd_alg == CRYPTO_3DES_CBC ||
+ crd1->crd_alg == CRYPTO_AES_CBC ||
+ crd1->crd_alg == CRYPTO_ARC4) {
+ out_fifo = 4;
+ maccrd = NULL;
+ enccrd = crd1;
+ } else {
+ DPRINTF("UNKNOWN crd1->crd_alg %d\n", crd1->crd_alg);
+ err = EINVAL;
+ goto errout;
+ }
+ } else {
+ if (sc->sc_desc_types & TALITOS_HAS_DT_IPSEC_ESP) {
+ td->hdr |= TD_TYPE_IPSEC_ESP;
+ } else {
+ DPRINTF("unimplemented: multiple descriptor ipsec\n");
+ err = EINVAL;
+ goto errout;
+ }
+ /* assign descriptor dword ptr mappings for this desc. type */
+ hmac_key = 0;
+ hmac_data = 1;
+ cipher_iv = 2;
+ cipher_key = 3;
+ in_fifo = 4;
+ out_fifo = 5;
+ cipher_iv_out = 6;
+ if ((crd1->crd_alg == CRYPTO_MD5_HMAC ||
+ crd1->crd_alg == CRYPTO_SHA1_HMAC ||
+ crd1->crd_alg == CRYPTO_MD5 ||
+ crd1->crd_alg == CRYPTO_SHA1) &&
+ (crd2->crd_alg == CRYPTO_DES_CBC ||
+ crd2->crd_alg == CRYPTO_3DES_CBC ||
+ crd2->crd_alg == CRYPTO_AES_CBC ||
+ crd2->crd_alg == CRYPTO_ARC4) &&
+ ((crd2->crd_flags & CRD_F_ENCRYPT) == 0)) {
+ maccrd = crd1;
+ enccrd = crd2;
+ } else if ((crd1->crd_alg == CRYPTO_DES_CBC ||
+ crd1->crd_alg == CRYPTO_ARC4 ||
+ crd1->crd_alg == CRYPTO_3DES_CBC ||
+ crd1->crd_alg == CRYPTO_AES_CBC) &&
+ (crd2->crd_alg == CRYPTO_MD5_HMAC ||
+ crd2->crd_alg == CRYPTO_SHA1_HMAC ||
+ crd2->crd_alg == CRYPTO_MD5 ||
+ crd2->crd_alg == CRYPTO_SHA1) &&
+ (crd1->crd_flags & CRD_F_ENCRYPT)) {
+ enccrd = crd1;
+ maccrd = crd2;
+ } else {
+ /* We cannot order the SEC as requested */
+ printk("%s: cannot do the order\n",
+ device_get_nameunit(sc->sc_cdev));
+ err = EINVAL;
+ goto errout;
+ }
+ }
+ /* assign in_fifo and out_fifo based on input/output struct type */
+ if (crp->crp_flags & CRYPTO_F_SKBUF) {
+ /* using SKB buffers */
+ struct sk_buff *skb = (struct sk_buff *)crp->crp_buf;
+ if (skb_shinfo(skb)->nr_frags) {
+ printk("%s: skb frags unimplemented\n",
+ device_get_nameunit(sc->sc_cdev));
+ err = EINVAL;
+ goto errout;
+ }
+ td->ptr[in_fifo].ptr = dma_map_single(NULL, skb->data,
+ skb->len, DMA_TO_DEVICE);
+ td->ptr[in_fifo].len = skb->len;
+ td->ptr[out_fifo].ptr = dma_map_single(NULL, skb->data,
+ skb->len, DMA_TO_DEVICE);
+ td->ptr[out_fifo].len = skb->len;
+ td->ptr[hmac_data].ptr = dma_map_single(NULL, skb->data,
+ skb->len, DMA_TO_DEVICE);
+ } else if (crp->crp_flags & CRYPTO_F_IOV) {
+ /* using IOV buffers */
+ struct uio *uiop = (struct uio *)crp->crp_buf;
+ if (uiop->uio_iovcnt > 1) {
+ printk("%s: iov frags unimplemented\n",
+ device_get_nameunit(sc->sc_cdev));
+ err = EINVAL;
+ goto errout;
+ }
+ td->ptr[in_fifo].ptr = dma_map_single(NULL,
+ uiop->uio_iov->iov_base, crp->crp_ilen, DMA_TO_DEVICE);
+ td->ptr[in_fifo].len = crp->crp_ilen;
+ /* crp_olen is never set; always use crp_ilen */
+ td->ptr[out_fifo].ptr = dma_map_single(NULL,
+ uiop->uio_iov->iov_base,
+ crp->crp_ilen, DMA_TO_DEVICE);
+ td->ptr[out_fifo].len = crp->crp_ilen;
+ } else {
+ /* using contig buffers */
+ td->ptr[in_fifo].ptr = dma_map_single(NULL,
+ crp->crp_buf, crp->crp_ilen, DMA_TO_DEVICE);
+ td->ptr[in_fifo].len = crp->crp_ilen;
+ td->ptr[out_fifo].ptr = dma_map_single(NULL,
+ crp->crp_buf, crp->crp_ilen, DMA_TO_DEVICE);
+ td->ptr[out_fifo].len = crp->crp_ilen;
+ }
+ if (enccrd) {
+ switch (enccrd->crd_alg) {
+ case CRYPTO_3DES_CBC:
+ td->hdr |= TALITOS_MODE0_DEU_3DES;
+ /* FALLTHROUGH */
+ case CRYPTO_DES_CBC:
+ td->hdr |= TALITOS_SEL0_DEU
+ | TALITOS_MODE0_DEU_CBC;
+ if (enccrd->crd_flags & CRD_F_ENCRYPT)
+ td->hdr |= TALITOS_MODE0_DEU_ENC;
+ ivsize = 2*sizeof(u_int32_t);
+ DPRINTF("%cDES ses %d ch %d len %d\n",
+ (td->hdr & TALITOS_MODE0_DEU_3DES)?'3':'1',
+ (u32)TALITOS_SESSION(crp->crp_sid),
+ chsel, td->ptr[in_fifo].len);
+ break;
+ case CRYPTO_AES_CBC:
+ td->hdr |= TALITOS_SEL0_AESU
+ | TALITOS_MODE0_AESU_CBC;
+ if (enccrd->crd_flags & CRD_F_ENCRYPT)
+ td->hdr |= TALITOS_MODE0_AESU_ENC;
+ ivsize = 4*sizeof(u_int32_t);
+ DPRINTF("AES ses %d ch %d len %d\n",
+ (u32)TALITOS_SESSION(crp->crp_sid),
+ chsel, td->ptr[in_fifo].len);
+ break;
+ default:
+ printk("%s: unimplemented enccrd->crd_alg %d\n",
+ device_get_nameunit(sc->sc_cdev), enccrd->crd_alg);
+ err = EINVAL;
+ goto errout;
+ }
+ /*
+ * Setup encrypt/decrypt state. When using basic ops
+ * we can't use an inline IV because hash/crypt offset
+ * must be from the end of the IV to the start of the
+ * crypt data and this leaves out the preceding header
+ * from the hash calculation. Instead we place the IV
+ * in the state record and set the hash/crypt offset to
+ * copy both the header+IV.
+ */
+ if (enccrd->crd_flags & CRD_F_ENCRYPT) {
+ td->hdr |= TALITOS_DIR_OUTBOUND;
+ if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
+ iv = enccrd->crd_iv;
+ else
+ iv = (caddr_t) ses->ses_iv;
+ if ((enccrd->crd_flags & CRD_F_IV_PRESENT) == 0) {
+ crypto_copyback(crp->crp_flags, crp->crp_buf,
+ enccrd->crd_inject, ivsize, iv);
+ }
+ } else {
+ td->hdr |= TALITOS_DIR_INBOUND;
+ if (enccrd->crd_flags & CRD_F_IV_EXPLICIT) {
+ iv = enccrd->crd_iv;
+ bcopy(enccrd->crd_iv, iv, ivsize);
+ } else {
+ iv = (caddr_t) ses->ses_iv;
+ crypto_copydata(crp->crp_flags, crp->crp_buf,
+ enccrd->crd_inject, ivsize, iv);
+ }
+ }
+ td->ptr[cipher_iv].ptr = dma_map_single(NULL, iv, ivsize,
+ DMA_TO_DEVICE);
+ td->ptr[cipher_iv].len = ivsize;
+ /*
+ * we don't need the cipher iv out length/pointer
+ * field to do ESP IPsec. Therefore we set the len field as 0,
+ * which tells the SEC not to do anything with this len/ptr
+ * field. Previously, when length/pointer as pointing to iv,
+ * it gave us corruption of packets.
+ */
+ td->ptr[cipher_iv_out].len = 0;
+ }
+ if (enccrd && maccrd) {
+ /* this is ipsec only for now */
+ td->hdr |= TALITOS_SEL1_MDEU
+ | TALITOS_MODE1_MDEU_INIT
+ | TALITOS_MODE1_MDEU_PAD;
+ switch (maccrd->crd_alg) {
+ case CRYPTO_MD5:
+ td->hdr |= TALITOS_MODE1_MDEU_MD5;
+ break;
+ case CRYPTO_MD5_HMAC:
+ td->hdr |= TALITOS_MODE1_MDEU_MD5_HMAC;
+ break;
+ case CRYPTO_SHA1:
+ td->hdr |= TALITOS_MODE1_MDEU_SHA1;
+ break;
+ case CRYPTO_SHA1_HMAC:
+ td->hdr |= TALITOS_MODE1_MDEU_SHA1_HMAC;
+ break;
+ default:
+ /* We cannot order the SEC as requested */
+ printk("%s: cannot do the order\n",
+ device_get_nameunit(sc->sc_cdev));
+ err = EINVAL;
+ goto errout;
+ }
+ if ((maccrd->crd_alg == CRYPTO_MD5_HMAC) ||
+ (maccrd->crd_alg == CRYPTO_SHA1_HMAC)) {
+ /*
+ * The offset from hash data to the start of
+ * crypt data is the difference in the skips.
+ */
+ /* ipsec only for now */
+ td->ptr[hmac_key].ptr = dma_map_single(NULL,
+ ses->ses_hmac, ses->ses_hmac_len, DMA_TO_DEVICE);
+ td->ptr[hmac_key].len = ses->ses_hmac_len;
+ td->ptr[in_fifo].ptr += enccrd->crd_skip;
+ td->ptr[in_fifo].len = enccrd->crd_len;
+ td->ptr[out_fifo].ptr += enccrd->crd_skip;
+ td->ptr[out_fifo].len = enccrd->crd_len;
+ /* bytes of HMAC to postpend to ciphertext */
+ td->ptr[out_fifo].extent = ses->ses_mlen;
+ td->ptr[hmac_data].ptr += maccrd->crd_skip;
+ td->ptr[hmac_data].len = enccrd->crd_skip - maccrd->crd_skip;
+ }
+ if (enccrd->crd_flags & CRD_F_KEY_EXPLICIT) {
+ printk("%s: CRD_F_KEY_EXPLICIT unimplemented\n",
+ device_get_nameunit(sc->sc_cdev));
+ }
+ }
+ if (!enccrd && maccrd) {
+ /* single MD5 or SHA */
+ td->hdr |= TALITOS_SEL0_MDEU
+ | TALITOS_MODE0_MDEU_INIT
+ | TALITOS_MODE0_MDEU_PAD;
+ switch (maccrd->crd_alg) {
+ case CRYPTO_MD5:
+ td->hdr |= TALITOS_MODE0_MDEU_MD5;
+ DPRINTF("MD5 ses %d ch %d len %d\n",
+ (u32)TALITOS_SESSION(crp->crp_sid),
+ chsel, td->ptr[in_fifo].len);
+ break;
+ case CRYPTO_MD5_HMAC:
+ td->hdr |= TALITOS_MODE0_MDEU_MD5_HMAC;
+ break;
+ case CRYPTO_SHA1:
+ td->hdr |= TALITOS_MODE0_MDEU_SHA1;
+ DPRINTF("SHA1 ses %d ch %d len %d\n",
+ (u32)TALITOS_SESSION(crp->crp_sid),
+ chsel, td->ptr[in_fifo].len);
+ break;
+ case CRYPTO_SHA1_HMAC:
+ td->hdr |= TALITOS_MODE0_MDEU_SHA1_HMAC;
+ break;
+ default:
+ /* We cannot order the SEC as requested */
+ DPRINTF("cannot do the order\n");
+ err = EINVAL;
+ goto errout;
+ }
+
+ if (crp->crp_flags & CRYPTO_F_IOV)
+ td->ptr[out_fifo].ptr += maccrd->crd_inject;
+
+ if ((maccrd->crd_alg == CRYPTO_MD5_HMAC) ||
+ (maccrd->crd_alg == CRYPTO_SHA1_HMAC)) {
+ td->ptr[hmac_key].ptr = dma_map_single(NULL,
+ ses->ses_hmac, ses->ses_hmac_len,
+ DMA_TO_DEVICE);
+ td->ptr[hmac_key].len = ses->ses_hmac_len;
+ }
+ }
+ else {
+ /* using process key (session data has duplicate) */
+ td->ptr[cipher_key].ptr = dma_map_single(NULL,
+ enccrd->crd_key, (enccrd->crd_klen + 7) / 8,
+ DMA_TO_DEVICE);
+ td->ptr[cipher_key].len = (enccrd->crd_klen + 7) / 8;
+ }
+ /* descriptor complete - GO! */
+ return talitos_submit(sc, td, chsel);
+
+errout:
+ if (err != ERESTART) {
+ crp->crp_etype = err;
+ crypto_done(crp);
+ }
+ return err;
+}
+
+/* go through all channels descriptors, notifying OCF what has
+ * _and_hasn't_ successfully completed and reset the device
+ * (otherwise it's up to decoding desc hdrs!)
+ */
+static void talitos_errorprocessing(struct talitos_softc *sc)
+{
+ unsigned long flags;
+ int i, j;
+
+ /* disable further scheduling until under control */
+ spin_lock_irqsave(&sc->sc_chnfifolock[sc->sc_num_channels], flags);
+
+ if (debug) dump_talitos_status(sc);
+ /* go through descriptors, try and salvage those successfully done,
+ * and EIO those that weren't
+ */
+ for (i = 0; i < sc->sc_num_channels; i++) {
+ spin_lock_irqsave(&sc->sc_chnfifolock[i], flags);
+ for (j = 0; j < sc->sc_chfifo_len; j++) {
+ if (sc->sc_chnfifo[i][j].cf_desc.hdr) {
+ if ((sc->sc_chnfifo[i][j].cf_desc.hdr
+ & TALITOS_HDR_DONE_BITS)
+ != TALITOS_HDR_DONE_BITS) {
+ /* this one didn't finish */
+ /* signify in crp->etype */
+ sc->sc_chnfifo[i][j].cf_crp->crp_etype
+ = EIO;
+ }
+ } else
+ continue; /* free entry */
+ /* either way, notify ocf */
+ crypto_done(sc->sc_chnfifo[i][j].cf_crp);
+ /* and tag it available again
+ *
+ * memset to ensure correct descriptor formation by
+ * avoiding inadvertently setting "optional" entries
+ * e.g. not using "optional" dptr2 MD/HMAC processing
+ */
+ memset(&sc->sc_chnfifo[i][j].cf_desc,
+ 0, sizeof(struct talitos_desc));
+ }
+ spin_unlock_irqrestore(&sc->sc_chnfifolock[i], flags);
+ }
+ /* reset and initialize the SEC h/w device */
+ talitos_reset_device(sc);
+ talitos_init_device(sc);
+#ifdef CONFIG_OCF_RANDOMHARVEST
+ if (sc->sc_exec_units & TALITOS_HAS_EU_RNG)
+ talitos_rng_init(sc);
+#endif
+
+ /* Okay. Stand by. */
+ spin_unlock_irqrestore(&sc->sc_chnfifolock[sc->sc_num_channels], flags);
+
+ return;
+}
+
+/* go through all channels descriptors, notifying OCF what's been done */
+static void talitos_doneprocessing(struct talitos_softc *sc)
+{
+ unsigned long flags;
+ int i, j;
+
+ /* go through descriptors looking for done bits */
+ for (i = 0; i < sc->sc_num_channels; i++) {
+ spin_lock_irqsave(&sc->sc_chnfifolock[i], flags);
+ for (j = 0; j < sc->sc_chfifo_len; j++) {
+ /* descriptor has done bits set? */
+ if ((sc->sc_chnfifo[i][j].cf_desc.hdr
+ & TALITOS_HDR_DONE_BITS)
+ == TALITOS_HDR_DONE_BITS) {
+ /* notify ocf */
+ crypto_done(sc->sc_chnfifo[i][j].cf_crp);
+ /* and tag it available again
+ *
+ * memset to ensure correct descriptor formation by
+ * avoiding inadvertently setting "optional" entries
+ * e.g. not using "optional" dptr2 MD/HMAC processing
+ */
+ memset(&sc->sc_chnfifo[i][j].cf_desc,
+ 0, sizeof(struct talitos_desc));
+ }
+ }
+ spin_unlock_irqrestore(&sc->sc_chnfifolock[i], flags);
+ }
+ return;
+}
+
+static irqreturn_t
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+talitos_intr(int irq, void *arg)
+#else
+talitos_intr(int irq, void *arg, struct pt_regs *regs)
+#endif
+{
+ struct talitos_softc *sc = arg;
+ u_int32_t v, v_hi;
+
+ /* ack */
+ v = talitos_read(sc->sc_base_addr + TALITOS_ISR);
+ v_hi = talitos_read(sc->sc_base_addr + TALITOS_ISR_HI);
+ talitos_write(sc->sc_base_addr + TALITOS_ICR, v);
+ talitos_write(sc->sc_base_addr + TALITOS_ICR_HI, v_hi);
+
+ if (unlikely(v & TALITOS_ISR_ERROR)) {
+ /* Okay, Houston, we've had a problem here. */
+ printk(KERN_DEBUG "%s: got error interrupt - ISR 0x%08x_%08x\n",
+ device_get_nameunit(sc->sc_cdev), v, v_hi);
+ talitos_errorprocessing(sc);
+ } else
+ if (likely(v & TALITOS_ISR_DONE)) {
+ talitos_doneprocessing(sc);
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * Initialize registers we need to touch only once.
+ */
+static void
+talitos_init_device(struct talitos_softc *sc)
+{
+ u_int32_t v;
+ int i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ /* init all channels */
+ for (i = 0; i < sc->sc_num_channels; i++) {
+ v = talitos_read(sc->sc_base_addr +
+ i*TALITOS_CH_OFFSET + TALITOS_CH_CCCR_HI);
+ v |= TALITOS_CH_CCCR_HI_CDWE
+ | TALITOS_CH_CCCR_HI_CDIE; /* invoke interrupt if done */
+ talitos_write(sc->sc_base_addr +
+ i*TALITOS_CH_OFFSET + TALITOS_CH_CCCR_HI, v);
+ }
+ /* enable all interrupts */
+ v = talitos_read(sc->sc_base_addr + TALITOS_IMR);
+ v |= TALITOS_IMR_ALL;
+ talitos_write(sc->sc_base_addr + TALITOS_IMR, v);
+ v = talitos_read(sc->sc_base_addr + TALITOS_IMR_HI);
+ v |= TALITOS_IMR_HI_ERRONLY;
+ talitos_write(sc->sc_base_addr + TALITOS_IMR_HI, v);
+ return;
+}
+
+/*
+ * set the master reset bit on the device.
+ */
+static void
+talitos_reset_device_master(struct talitos_softc *sc)
+{
+ u_int32_t v;
+
+ /* Reset the device by writing 1 to MCR:SWR and waiting 'til cleared */
+ v = talitos_read(sc->sc_base_addr + TALITOS_MCR);
+ talitos_write(sc->sc_base_addr + TALITOS_MCR, v | TALITOS_MCR_SWR);
+
+ while (talitos_read(sc->sc_base_addr + TALITOS_MCR) & TALITOS_MCR_SWR)
+ cpu_relax();
+
+ return;
+}
+
+/*
+ * Resets the device. Values in the registers are left as is
+ * from the reset (i.e. initial values are assigned elsewhere).
+ */
+static void
+talitos_reset_device(struct talitos_softc *sc)
+{
+ u_int32_t v;
+ int i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ /*
+ * Master reset
+ * errata documentation: warning: certain SEC interrupts
+ * are not fully cleared by writing the MCR:SWR bit,
+ * set bit twice to completely reset
+ */
+ talitos_reset_device_master(sc); /* once */
+ talitos_reset_device_master(sc); /* and once again */
+
+ /* reset all channels */
+ for (i = 0; i < sc->sc_num_channels; i++) {
+ v = talitos_read(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
+ TALITOS_CH_CCCR);
+ talitos_write(sc->sc_base_addr + i*TALITOS_CH_OFFSET +
+ TALITOS_CH_CCCR, v | TALITOS_CH_CCCR_RESET);
+ }
+}
+
+/* Set up the crypto device structure, private data,
+ * and anything else we need before we start */
+#ifdef CONFIG_PPC_MERGE
+static int talitos_probe(struct of_device *ofdev, const struct of_device_id *match)
+#else
+static int talitos_probe(struct platform_device *pdev)
+#endif
+{
+ struct talitos_softc *sc = NULL;
+ struct resource *r;
+#ifdef CONFIG_PPC_MERGE
+ struct device *device = &ofdev->dev;
+ struct device_node *np = ofdev->node;
+ const unsigned int *prop;
+ int err;
+ struct resource res;
+#endif
+ static int num_chips = 0;
+ int rc;
+ int i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+
+ sc = (struct talitos_softc *) kmalloc(sizeof(*sc), GFP_KERNEL);
+ if (!sc)
+ return -ENOMEM;
+ memset(sc, 0, sizeof(*sc));
+
+ softc_device_init(sc, DRV_NAME, num_chips, talitos_methods);
+
+ sc->sc_irq = -1;
+ sc->sc_cid = -1;
+#ifndef CONFIG_PPC_MERGE
+ sc->sc_dev = pdev;
+#endif
+ sc->sc_num = num_chips++;
+
+#ifdef CONFIG_PPC_MERGE
+ dev_set_drvdata(device, sc);
+#else
+ platform_set_drvdata(sc->sc_dev, sc);
+#endif
+
+ /* get the irq line */
+#ifdef CONFIG_PPC_MERGE
+ err = of_address_to_resource(np, 0, &res);
+ if (err)
+ return -EINVAL;
+ r = &res;
+
+ sc->sc_irq = irq_of_parse_and_map(np, 0);
+#else
+ /* get a pointer to the register memory */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ sc->sc_irq = platform_get_irq(pdev, 0);
+#endif
+ rc = request_irq(sc->sc_irq, talitos_intr, 0,
+ device_get_nameunit(sc->sc_cdev), sc);
+ if (rc) {
+ printk(KERN_ERR "%s: failed to hook irq %d\n",
+ device_get_nameunit(sc->sc_cdev), sc->sc_irq);
+ sc->sc_irq = -1;
+ goto out;
+ }
+
+ sc->sc_base_addr = (ocf_iomem_t) ioremap(r->start, (r->end - r->start));
+ if (!sc->sc_base_addr) {
+ printk(KERN_ERR "%s: failed to ioremap\n",
+ device_get_nameunit(sc->sc_cdev));
+ goto out;
+ }
+
+ /* figure out our SEC's properties and capabilities */
+ sc->sc_chiprev = (u64)talitos_read(sc->sc_base_addr + TALITOS_ID) << 32
+ | talitos_read(sc->sc_base_addr + TALITOS_ID_HI);
+ DPRINTF("sec id 0x%llx\n", sc->sc_chiprev);
+
+#ifdef CONFIG_PPC_MERGE
+ /* get SEC properties from device tree, defaulting to SEC 2.0 */
+
+ prop = of_get_property(np, "num-channels", NULL);
+ sc->sc_num_channels = prop ? *prop : TALITOS_NCHANNELS_SEC_2_0;
+
+ prop = of_get_property(np, "channel-fifo-len", NULL);
+ sc->sc_chfifo_len = prop ? *prop : TALITOS_CHFIFOLEN_SEC_2_0;
+
+ prop = of_get_property(np, "exec-units-mask", NULL);
+ sc->sc_exec_units = prop ? *prop : TALITOS_HAS_EUS_SEC_2_0;
+
+ prop = of_get_property(np, "descriptor-types-mask", NULL);
+ sc->sc_desc_types = prop ? *prop : TALITOS_HAS_DESCTYPES_SEC_2_0;
+#else
+ /* bulk should go away with openfirmware flat device tree support */
+ if (sc->sc_chiprev & TALITOS_ID_SEC_2_0) {
+ sc->sc_num_channels = TALITOS_NCHANNELS_SEC_2_0;
+ sc->sc_chfifo_len = TALITOS_CHFIFOLEN_SEC_2_0;
+ sc->sc_exec_units = TALITOS_HAS_EUS_SEC_2_0;
+ sc->sc_desc_types = TALITOS_HAS_DESCTYPES_SEC_2_0;
+ } else {
+ printk(KERN_ERR "%s: failed to id device\n",
+ device_get_nameunit(sc->sc_cdev));
+ goto out;
+ }
+#endif
+
+ /* + 1 is for the meta-channel lock used by the channel scheduler */
+ sc->sc_chnfifolock = (spinlock_t *) kmalloc(
+ (sc->sc_num_channels + 1) * sizeof(spinlock_t), GFP_KERNEL);
+ if (!sc->sc_chnfifolock)
+ goto out;
+ for (i = 0; i < sc->sc_num_channels + 1; i++) {
+ spin_lock_init(&sc->sc_chnfifolock[i]);
+ }
+
+ sc->sc_chnlastalg = (int *) kmalloc(
+ sc->sc_num_channels * sizeof(int), GFP_KERNEL);
+ if (!sc->sc_chnlastalg)
+ goto out;
+ memset(sc->sc_chnlastalg, 0, sc->sc_num_channels * sizeof(int));
+
+ sc->sc_chnfifo = (struct desc_cryptop_pair **) kmalloc(
+ sc->sc_num_channels * sizeof(struct desc_cryptop_pair *),
+ GFP_KERNEL);
+ if (!sc->sc_chnfifo)
+ goto out;
+ for (i = 0; i < sc->sc_num_channels; i++) {
+ sc->sc_chnfifo[i] = (struct desc_cryptop_pair *) kmalloc(
+ sc->sc_chfifo_len * sizeof(struct desc_cryptop_pair),
+ GFP_KERNEL);
+ if (!sc->sc_chnfifo[i])
+ goto out;
+ memset(sc->sc_chnfifo[i], 0,
+ sc->sc_chfifo_len * sizeof(struct desc_cryptop_pair));
+ }
+
+ /* reset and initialize the SEC h/w device */
+ talitos_reset_device(sc);
+ talitos_init_device(sc);
+
+ sc->sc_cid = crypto_get_driverid(softc_get_device(sc),CRYPTOCAP_F_HARDWARE);
+ if (sc->sc_cid < 0) {
+ printk(KERN_ERR "%s: could not get crypto driver id\n",
+ device_get_nameunit(sc->sc_cdev));
+ goto out;
+ }
+
+ /* register algorithms with the framework */
+ printk("%s:", device_get_nameunit(sc->sc_cdev));
+
+ if (sc->sc_exec_units & TALITOS_HAS_EU_RNG) {
+ printk(" rng");
+#ifdef CONFIG_OCF_RANDOMHARVEST
+ talitos_rng_init(sc);
+ crypto_rregister(sc->sc_cid, talitos_read_random, sc);
+#endif
+ }
+ if (sc->sc_exec_units & TALITOS_HAS_EU_DEU) {
+ printk(" des/3des");
+ crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0);
+ crypto_register(sc->sc_cid, CRYPTO_DES_CBC, 0, 0);
+ }
+ if (sc->sc_exec_units & TALITOS_HAS_EU_AESU) {
+ printk(" aes");
+ crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0);
+ }
+ if (sc->sc_exec_units & TALITOS_HAS_EU_MDEU) {
+ printk(" md5");
+ crypto_register(sc->sc_cid, CRYPTO_MD5, 0, 0);
+ /* HMAC support only with IPsec for now */
+ crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0);
+ printk(" sha1");
+ crypto_register(sc->sc_cid, CRYPTO_SHA1, 0, 0);
+ /* HMAC support only with IPsec for now */
+ crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0);
+ }
+ printk("\n");
+ return 0;
+
+out:
+#ifndef CONFIG_PPC_MERGE
+ talitos_remove(pdev);
+#endif
+ return -ENOMEM;
+}
+
+#ifdef CONFIG_PPC_MERGE
+static int talitos_remove(struct of_device *ofdev)
+#else
+static int talitos_remove(struct platform_device *pdev)
+#endif
+{
+#ifdef CONFIG_PPC_MERGE
+ struct talitos_softc *sc = dev_get_drvdata(&ofdev->dev);
+#else
+ struct talitos_softc *sc = platform_get_drvdata(pdev);
+#endif
+ int i;
+
+ DPRINTF("%s()\n", __FUNCTION__);
+ if (sc->sc_cid >= 0)
+ crypto_unregister_all(sc->sc_cid);
+ if (sc->sc_chnfifo) {
+ for (i = 0; i < sc->sc_num_channels; i++)
+ if (sc->sc_chnfifo[i])
+ kfree(sc->sc_chnfifo[i]);
+ kfree(sc->sc_chnfifo);
+ }
+ if (sc->sc_chnlastalg)
+ kfree(sc->sc_chnlastalg);
+ if (sc->sc_chnfifolock)
+ kfree(sc->sc_chnfifolock);
+ if (sc->sc_irq != -1)
+ free_irq(sc->sc_irq, sc);
+ if (sc->sc_base_addr)
+ iounmap((void *) sc->sc_base_addr);
+ kfree(sc);
+ return 0;
+}
+
+#ifdef CONFIG_PPC_MERGE
+static struct of_device_id talitos_match[] = {
+ {
+ .type = "crypto",
+ .compatible = "talitos",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, talitos_match);
+
+static struct of_platform_driver talitos_driver = {
+ .name = DRV_NAME,
+ .match_table = talitos_match,
+ .probe = talitos_probe,
+ .remove = talitos_remove,
+};
+
+static int __init talitos_init(void)
+{
+ return of_register_platform_driver(&talitos_driver);
+}
+
+static void __exit talitos_exit(void)
+{
+ of_unregister_platform_driver(&talitos_driver);
+}
+#else
+/* Structure for a platform device driver */
+static struct platform_driver talitos_driver = {
+ .probe = talitos_probe,
+ .remove = talitos_remove,
+ .driver = {
+ .name = "fsl-sec2",
+ }
+};
+
+static int __init talitos_init(void)
+{
+ return platform_driver_register(&talitos_driver);
+}
+
+static void __exit talitos_exit(void)
+{
+ platform_driver_unregister(&talitos_driver);
+}
+#endif
+
+module_init(talitos_init);
+module_exit(talitos_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("kim.phillips@freescale.com");
+MODULE_DESCRIPTION("OCF driver for Freescale SEC (talitos)");
diff --git a/crypto/ocf/talitos/talitos_dev.h b/crypto/ocf/talitos/talitos_dev.h
new file mode 100644
index 000000000000..a8b04799fa68
--- /dev/null
+++ b/crypto/ocf/talitos/talitos_dev.h
@@ -0,0 +1,277 @@
+/*
+ * Freescale SEC (talitos) device dependent data structures
+ *
+ * Copyright (c) 2006 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* device ID register values */
+#define TALITOS_ID_SEC_2_0 0x40
+#define TALITOS_ID_SEC_2_1 0x40 /* cross ref with IP block revision reg */
+
+/*
+ * following num_channels, channel-fifo-depth, exec-unit-mask, and
+ * descriptor-types-mask are for forward-compatibility with openfirmware
+ * flat device trees
+ */
+
+/*
+ * num_channels : the number of channels available in each SEC version.
+ */
+
+/* n.b. this driver requires these values be a power of 2 */
+#define TALITOS_NCHANNELS_SEC_1_0 4
+#define TALITOS_NCHANNELS_SEC_1_2 1
+#define TALITOS_NCHANNELS_SEC_2_0 4
+#define TALITOS_NCHANNELS_SEC_2_01 4
+#define TALITOS_NCHANNELS_SEC_2_1 4
+#define TALITOS_NCHANNELS_SEC_2_4 4
+
+/*
+ * channel-fifo-depth : The number of descriptor
+ * pointers a channel fetch fifo can hold.
+ */
+#define TALITOS_CHFIFOLEN_SEC_1_0 1
+#define TALITOS_CHFIFOLEN_SEC_1_2 1
+#define TALITOS_CHFIFOLEN_SEC_2_0 24
+#define TALITOS_CHFIFOLEN_SEC_2_01 24
+#define TALITOS_CHFIFOLEN_SEC_2_1 24
+#define TALITOS_CHFIFOLEN_SEC_2_4 24
+
+/*
+ * exec-unit-mask : The bitmask representing what Execution Units (EUs)
+ * are available. EU information should be encoded following the SEC's
+ * EU_SEL0 bitfield documentation, i.e. as follows:
+ *
+ * bit 31 = set if SEC permits no-EU selection (should be always set)
+ * bit 30 = set if SEC has the ARC4 EU (AFEU)
+ * bit 29 = set if SEC has the des/3des EU (DEU)
+ * bit 28 = set if SEC has the message digest EU (MDEU)
+ * bit 27 = set if SEC has the random number generator EU (RNG)
+ * bit 26 = set if SEC has the public key EU (PKEU)
+ * bit 25 = set if SEC has the aes EU (AESU)
+ * bit 24 = set if SEC has the Kasumi EU (KEU)
+ *
+ */
+#define TALITOS_HAS_EU_NONE (1<<0)
+#define TALITOS_HAS_EU_AFEU (1<<1)
+#define TALITOS_HAS_EU_DEU (1<<2)
+#define TALITOS_HAS_EU_MDEU (1<<3)
+#define TALITOS_HAS_EU_RNG (1<<4)
+#define TALITOS_HAS_EU_PKEU (1<<5)
+#define TALITOS_HAS_EU_AESU (1<<6)
+#define TALITOS_HAS_EU_KEU (1<<7)
+
+/* the corresponding masks for each SEC version */
+#define TALITOS_HAS_EUS_SEC_1_0 0x7f
+#define TALITOS_HAS_EUS_SEC_1_2 0x4d
+#define TALITOS_HAS_EUS_SEC_2_0 0x7f
+#define TALITOS_HAS_EUS_SEC_2_01 0x7f
+#define TALITOS_HAS_EUS_SEC_2_1 0xff
+#define TALITOS_HAS_EUS_SEC_2_4 0x7f
+
+/*
+ * descriptor-types-mask : The bitmask representing what descriptors
+ * are available. Descriptor type information should be encoded
+ * following the SEC's Descriptor Header Dword DESC_TYPE field
+ * documentation, i.e. as follows:
+ *
+ * bit 0 = set if SEC supports the aesu_ctr_nonsnoop desc. type
+ * bit 1 = set if SEC supports the ipsec_esp descriptor type
+ * bit 2 = set if SEC supports the common_nonsnoop desc. type
+ * bit 3 = set if SEC supports the 802.11i AES ccmp desc. type
+ * bit 4 = set if SEC supports the hmac_snoop_no_afeu desc. type
+ * bit 5 = set if SEC supports the srtp descriptor type
+ * bit 6 = set if SEC supports the non_hmac_snoop_no_afeu desc.type
+ * bit 7 = set if SEC supports the pkeu_assemble descriptor type
+ * bit 8 = set if SEC supports the aesu_key_expand_output desc.type
+ * bit 9 = set if SEC supports the pkeu_ptmul descriptor type
+ * bit 10 = set if SEC supports the common_nonsnoop_afeu desc. type
+ * bit 11 = set if SEC supports the pkeu_ptadd_dbl descriptor type
+ *
+ * ..and so on and so forth.
+ */
+#define TALITOS_HAS_DT_AESU_CTR_NONSNOOP (1<<0)
+#define TALITOS_HAS_DT_IPSEC_ESP (1<<1)
+#define TALITOS_HAS_DT_COMMON_NONSNOOP (1<<2)
+
+/* the corresponding masks for each SEC version */
+#define TALITOS_HAS_DESCTYPES_SEC_2_0 0x01010ebf
+#define TALITOS_HAS_DESCTYPES_SEC_2_1 0x012b0ebf
+
+/*
+ * a TALITOS_xxx_HI address points to the low data bits (32-63) of the register
+ */
+
+/* global register offset addresses */
+#define TALITOS_ID 0x1020
+#define TALITOS_ID_HI 0x1024
+#define TALITOS_MCR 0x1030 /* master control register */
+#define TALITOS_MCR_HI 0x1038 /* master control register */
+#define TALITOS_MCR_SWR 0x1
+#define TALITOS_IMR 0x1008 /* interrupt mask register */
+#define TALITOS_IMR_ALL 0x00010fff /* enable all interrupts mask */
+#define TALITOS_IMR_ERRONLY 0x00010aaa /* enable error interrupts */
+#define TALITOS_IMR_HI 0x100C /* interrupt mask register */
+#define TALITOS_IMR_HI_ALL 0x00323333 /* enable all interrupts mask */
+#define TALITOS_IMR_HI_ERRONLY 0x00222222 /* enable error interrupts */
+#define TALITOS_ISR 0x1010 /* interrupt status register */
+#define TALITOS_ISR_ERROR 0x00010faa /* errors mask */
+#define TALITOS_ISR_DONE 0x00000055 /* channel(s) done mask */
+#define TALITOS_ISR_HI 0x1014 /* interrupt status register */
+#define TALITOS_ICR 0x1018 /* interrupt clear register */
+#define TALITOS_ICR_HI 0x101C /* interrupt clear register */
+
+/* channel register address stride */
+#define TALITOS_CH_OFFSET 0x100
+
+/* channel register offset addresses and bits */
+#define TALITOS_CH_CCCR 0x1108 /* Crypto-Channel Config Register */
+#define TALITOS_CH_CCCR_RESET 0x1 /* Channel Reset bit */
+#define TALITOS_CH_CCCR_HI 0x110c /* Crypto-Channel Config Register */
+#define TALITOS_CH_CCCR_HI_CDWE 0x10 /* Channel done writeback enable bit */
+#define TALITOS_CH_CCCR_HI_NT 0x4 /* Notification type bit */
+#define TALITOS_CH_CCCR_HI_CDIE 0x2 /* Channel Done Interrupt Enable bit */
+#define TALITOS_CH_CCPSR 0x1110 /* Crypto-Channel Pointer Status Reg */
+#define TALITOS_CH_CCPSR_HI 0x1114 /* Crypto-Channel Pointer Status Reg */
+#define TALITOS_CH_FF 0x1148 /* Fetch FIFO */
+#define TALITOS_CH_FF_HI 0x114c /* Fetch FIFO's FETCH_ADRS */
+#define TALITOS_CH_CDPR 0x1140 /* Crypto-Channel Pointer Status Reg */
+#define TALITOS_CH_CDPR_HI 0x1144 /* Crypto-Channel Pointer Status Reg */
+#define TALITOS_CH_DESCBUF 0x1180 /* (thru 11bf) Crypto-Channel
+ * Descriptor Buffer (debug) */
+
+/* execution unit register offset addresses and bits */
+#define TALITOS_DEUSR 0x2028 /* DEU status register */
+#define TALITOS_DEUSR_HI 0x202c /* DEU status register */
+#define TALITOS_DEUISR 0x2030 /* DEU interrupt status register */
+#define TALITOS_DEUISR_HI 0x2034 /* DEU interrupt status register */
+#define TALITOS_DEUICR 0x2038 /* DEU interrupt control register */
+#define TALITOS_DEUICR_HI 0x203c /* DEU interrupt control register */
+#define TALITOS_AESUISR 0x4030 /* AESU interrupt status register */
+#define TALITOS_AESUISR_HI 0x4034 /* AESU interrupt status register */
+#define TALITOS_AESUICR 0x4038 /* AESU interrupt control register */
+#define TALITOS_AESUICR_HI 0x403c /* AESU interrupt control register */
+#define TALITOS_MDEUISR 0x6030 /* MDEU interrupt status register */
+#define TALITOS_MDEUISR_HI 0x6034 /* MDEU interrupt status register */
+#define TALITOS_RNGSR 0xa028 /* RNG status register */
+#define TALITOS_RNGSR_HI 0xa02c /* RNG status register */
+#define TALITOS_RNGSR_HI_RD 0x1 /* RNG Reset done */
+#define TALITOS_RNGSR_HI_OFL 0xff0000/* number of dwords in RNG output FIFO*/
+#define TALITOS_RNGDSR 0xa010 /* RNG data size register */
+#define TALITOS_RNGDSR_HI 0xa014 /* RNG data size register */
+#define TALITOS_RNG_FIFO 0xa800 /* RNG FIFO - pool of random numbers */
+#define TALITOS_RNGISR 0xa030 /* RNG Interrupt status register */
+#define TALITOS_RNGISR_HI 0xa034 /* RNG Interrupt status register */
+#define TALITOS_RNGRCR 0xa018 /* RNG Reset control register */
+#define TALITOS_RNGRCR_HI 0xa01c /* RNG Reset control register */
+#define TALITOS_RNGRCR_HI_SR 0x1 /* RNG RNGRCR:Software Reset */
+
+/* descriptor pointer entry */
+struct talitos_desc_ptr {
+ u16 len; /* length */
+ u8 extent; /* jump (to s/g link table) and extent */
+ u8 res; /* reserved */
+ u32 ptr; /* pointer */
+};
+
+/* descriptor */
+struct talitos_desc {
+ u32 hdr; /* header */
+ u32 res; /* reserved */
+ struct talitos_desc_ptr ptr[7]; /* ptr/len pair array */
+};
+
+/* talitos descriptor header (hdr) bits */
+
+/* primary execution unit select */
+#define TALITOS_SEL0_AFEU 0x10000000
+#define TALITOS_SEL0_DEU 0x20000000
+#define TALITOS_SEL0_MDEU 0x30000000
+#define TALITOS_SEL0_RNG 0x40000000
+#define TALITOS_SEL0_PKEU 0x50000000
+#define TALITOS_SEL0_AESU 0x60000000
+
+/* primary execution unit mode (MODE0) and derivatives */
+#define TALITOS_MODE0_AESU_CBC 0x00200000
+#define TALITOS_MODE0_AESU_ENC 0x00100000
+#define TALITOS_MODE0_DEU_CBC 0x00400000
+#define TALITOS_MODE0_DEU_3DES 0x00200000
+#define TALITOS_MODE0_DEU_ENC 0x00100000
+#define TALITOS_MODE0_MDEU_INIT 0x01000000 /* init starting regs */
+#define TALITOS_MODE0_MDEU_HMAC 0x00800000
+#define TALITOS_MODE0_MDEU_PAD 0x00400000 /* PD */
+#define TALITOS_MODE0_MDEU_MD5 0x00200000
+#define TALITOS_MODE0_MDEU_SHA256 0x00100000
+#define TALITOS_MODE0_MDEU_SHA1 0x00000000 /* SHA-160 */
+#define TALITOS_MODE0_MDEU_MD5_HMAC \
+ (TALITOS_MODE0_MDEU_MD5 | TALITOS_MODE0_MDEU_HMAC)
+#define TALITOS_MODE0_MDEU_SHA256_HMAC \
+ (TALITOS_MODE0_MDEU_SHA256 | TALITOS_MODE0_MDEU_HMAC)
+#define TALITOS_MODE0_MDEU_SHA1_HMAC \
+ (TALITOS_MODE0_MDEU_SHA1 | TALITOS_MODE0_MDEU_HMAC)
+
+/* secondary execution unit select (SEL1) */
+/* it's MDEU or nothing */
+#define TALITOS_SEL1_MDEU 0x00030000
+
+/* secondary execution unit mode (MODE1) and derivatives */
+#define TALITOS_MODE1_MDEU_INIT 0x00001000 /* init starting regs */
+#define TALITOS_MODE1_MDEU_HMAC 0x00000800
+#define TALITOS_MODE1_MDEU_PAD 0x00000400 /* PD */
+#define TALITOS_MODE1_MDEU_MD5 0x00000200
+#define TALITOS_MODE1_MDEU_SHA256 0x00000100
+#define TALITOS_MODE1_MDEU_SHA1 0x00000000 /* SHA-160 */
+#define TALITOS_MODE1_MDEU_MD5_HMAC \
+ (TALITOS_MODE1_MDEU_MD5 | TALITOS_MODE1_MDEU_HMAC)
+#define TALITOS_MODE1_MDEU_SHA256_HMAC \
+ (TALITOS_MODE1_MDEU_SHA256 | TALITOS_MODE1_MDEU_HMAC)
+#define TALITOS_MODE1_MDEU_SHA1_HMAC \
+ (TALITOS_MODE1_MDEU_SHA1 | TALITOS_MODE1_MDEU_HMAC)
+
+/* direction of overall data flow (DIR) */
+#define TALITOS_DIR_OUTBOUND 0x00000000
+#define TALITOS_DIR_INBOUND 0x00000002
+
+/* done notification (DN) */
+#define TALITOS_DONE_NOTIFY 0x00000001
+
+/* descriptor types */
+/* odd numbers here are valid on SEC2 and greater only (e.g. ipsec_esp) */
+#define TD_TYPE_AESU_CTR_NONSNOOP (0 << 3)
+#define TD_TYPE_IPSEC_ESP (1 << 3)
+#define TD_TYPE_COMMON_NONSNOOP_NO_AFEU (2 << 3)
+#define TD_TYPE_HMAC_SNOOP_NO_AFEU (4 << 3)
+
+#define TALITOS_HDR_DONE_BITS 0xff000000
+
+#define DPRINTF(a...) do { \
+ if (debug) { \
+ printk("%s: ", sc ? \
+ device_get_nameunit(sc->sc_cdev) : "talitos"); \
+ printk(a); \
+ } \
+ } while (0)
diff --git a/crypto/ocf/talitos/talitos_soft.h b/crypto/ocf/talitos/talitos_soft.h
new file mode 100644
index 000000000000..79efdbd314df
--- /dev/null
+++ b/crypto/ocf/talitos/talitos_soft.h
@@ -0,0 +1,77 @@
+/*
+ * Freescale SEC data structures for integration with ocf-linux
+ *
+ * Copyright (c) 2006 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * paired descriptor and associated crypto operation
+ */
+struct desc_cryptop_pair {
+ struct talitos_desc cf_desc; /* descriptor ptr */
+ struct cryptop *cf_crp; /* cryptop ptr */
+};
+
+/*
+ * Holds data specific to a single talitos device.
+ */
+struct talitos_softc {
+ softc_device_decl sc_cdev;
+ struct platform_device *sc_dev; /* device backpointer */
+ ocf_iomem_t sc_base_addr;
+ int sc_irq;
+ int sc_num; /* if we have multiple chips */
+ int32_t sc_cid; /* crypto tag */
+ u64 sc_chiprev; /* major/minor chip revision */
+ int sc_nsessions;
+ struct talitos_session *sc_sessions;
+ int sc_num_channels;/* number of crypto channels */
+ int sc_chfifo_len; /* channel fetch fifo len */
+ int sc_exec_units; /* execution units mask */
+ int sc_desc_types; /* descriptor types mask */
+ /*
+ * mutual exclusion for intra-channel resources, e.g. fetch fifos
+ * the last entry is a meta-channel lock used by the channel scheduler
+ */
+ spinlock_t *sc_chnfifolock;
+ /* sc_chnlastalgo contains last algorithm for that channel */
+ int *sc_chnlastalg;
+ /* sc_chnfifo holds pending descriptor--crypto operation pairs */
+ struct desc_cryptop_pair **sc_chnfifo;
+};
+
+struct talitos_session {
+ u_int32_t ses_used;
+ u_int32_t ses_klen; /* key length in bits */
+ u_int32_t ses_key[8]; /* DES/3DES/AES key */
+ u_int32_t ses_hmac[5]; /* hmac inner state */
+ u_int32_t ses_hmac_len; /* hmac length */
+ u_int32_t ses_iv[4]; /* DES/3DES/AES iv */
+ u_int32_t ses_mlen; /* desired hash result len (12=ipsec or 16) */
+};
+
+#define TALITOS_SESSION(sid) ((sid) & 0x0fffffff)
+#define TALITOS_SID(crd, sesn) (((crd) << 28) | ((sesn) & 0x0fffffff))
diff --git a/crypto/ocf/uio.h b/crypto/ocf/uio.h
new file mode 100644
index 000000000000..e82e46cbadb0
--- /dev/null
+++ b/crypto/ocf/uio.h
@@ -0,0 +1,54 @@
+#ifndef _OCF_UIO_H_
+#define _OCF_UIO_H_
+
+#include <linux/uio.h>
+
+/*
+ * The linux uio.h doesn't have all we need. To be fully api compatible
+ * with the BSD cryptodev, we need to keep this around. Perhaps this can
+ * be moved back into the linux/uio.h
+ *
+ * Linux port done by David McCullough <david_mccullough@securecomputing.com>
+ * Copyright (C) 2006-2007 David McCullough
+ * Copyright (C) 2004-2005 Intel Corporation.
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ * ---------------------------------------------------------------------------
+ */
+
+struct uio {
+ struct iovec *uio_iov;
+ int uio_iovcnt;
+ off_t uio_offset;
+ int uio_resid;
+#if 0
+ enum uio_seg uio_segflg;
+ enum uio_rw uio_rw;
+ struct thread *uio_td;
+#endif
+};
+
+#endif
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 2f557f570ade..fbd497190e31 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -106,5 +106,7 @@ source "drivers/uio/Kconfig"
source "drivers/xen/Kconfig"
+source "drivers/fims/Kconfig"
+
source "drivers/staging/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index fceb71a741c3..259f3d31cc9b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_INPUT) += input/
obj-$(CONFIG_I2O) += message/
obj-$(CONFIG_RTC_LIB) += rtc/
obj-y += i2c/
+obj-$(CONFIG_I2C_SLAVE) += i2c-slave/
obj-$(CONFIG_W1) += w1/
obj-$(CONFIG_POWER_SUPPLY) += power/
obj-$(CONFIG_HWMON) += hwmon/
@@ -84,6 +85,7 @@ obj-y += lguest/
obj-$(CONFIG_CPU_FREQ) += cpufreq/
obj-$(CONFIG_CPU_IDLE) += cpuidle/
obj-y += idle/
+obj-$(CONFIG_ARCH_MXC) += mxc/
obj-$(CONFIG_MMC) += mmc/
obj-$(CONFIG_MEMSTICK) += memstick/
obj-$(CONFIG_NEW_LEDS) += leds/
@@ -97,8 +99,10 @@ obj-$(CONFIG_DMA_ENGINE) += dma/
obj-$(CONFIG_DCA) += dca/
obj-$(CONFIG_HID) += hid/
obj-$(CONFIG_PPC_PS3) += ps3/
+obj-$(CONFIG_GENERIC_PWM) += pwm/
obj-$(CONFIG_OF) += of/
obj-$(CONFIG_SSB) += ssb/
obj-$(CONFIG_VIRTIO) += virtio/
+obj-$(CONFIG_FIM_CORE) += fims/
obj-$(CONFIG_REGULATOR) += regulator/
obj-$(CONFIG_STAGING) += staging/
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 421b7c71e72d..f7f4c9d3e774 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -724,5 +724,14 @@ config PATA_BF54X
If unsure, say N.
+config PATA_FSL
+ tristate "Freescale on-chip PATA support"
+ depends on (ARCH_MX51 || ARCH_MX37 || ARCH_MX35 || ARCH_MX3 || ARCH_MX27)
+ help
+ On Freescale processors, say Y here if you wish to use the on-chip
+ ATA interface.
+ If you are unsure, say N to this.
+
endif # ATA_SFF
+
endif # ATA
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 674965fa326d..a2405a4bc7c4 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_PATA_BF54X) += pata_bf54x.o
obj-$(CONFIG_PATA_PLATFORM) += pata_platform.o
obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o
obj-$(CONFIG_PATA_ICSIDE) += pata_icside.o
+obj-$(CONFIG_PATA_FSL) += pata_fsl.o
# Should be last but two libata driver
obj-$(CONFIG_PATA_ACPI) += pata_acpi.o
# Should be last but one libata driver
diff --git a/drivers/ata/pata_fsl.c b/drivers/ata/pata_fsl.c
new file mode 100644
index 000000000000..379fc2deb830
--- /dev/null
+++ b/drivers/ata/pata_fsl.c
@@ -0,0 +1,1042 @@
+/*
+ * Freescale integrated PATA driver
+ */
+
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_host.h>
+#include <linux/ata.h>
+#include <linux/libata.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <asm/dma.h>
+
+#define DRV_NAME "pata_fsl"
+
+struct pata_fsl_priv {
+ int ultra;
+ u8 *fsl_ata_regs;
+ struct clk *clk;
+ int dma_rchan;
+ int dma_wchan;
+ int dma_done;
+ int dma_dir;
+ unsigned int adma_des_paddr;
+ unsigned int *adma_des_tp;
+};
+
+struct adma_bd {
+ unsigned char *sg_buf;
+ unsigned char *work_buf;
+ unsigned int dma_address;
+ int length;
+};
+
+struct adma_bulk {
+ struct adma_bd adma_bd_table[MXC_IDE_DMA_BD_NR];
+ struct ata_queued_cmd *qc;
+ int sg_ents;
+ int reserved[2];
+};
+
+enum {
+ /* various constants */
+ FSL_ATA_MAX_SG_LEN = ATA_DMA_BOUNDARY << 1,
+
+ /* offsets to registers */
+ FSL_ATA_TIMING_REGS = 0x00,
+ FSL_ATA_FIFO_FILL = 0x20,
+ FSL_ATA_CONTROL = 0x24,
+ FSL_ATA_INT_PEND = 0x28,
+ FSL_ATA_INT_EN = 0x2C,
+ FSL_ATA_INT_CLEAR = 0x30,
+ FSL_ATA_FIFO_ALARM = 0x34,
+ FSL_ATA_ADMA_ERROR_STATUS = 0x38,
+ FSL_ATA_SYS_DMA_BADDR = 0x3C,
+ FSL_ATA_ADMA_SYS_ADDR = 0x40,
+ FSL_ATA_BLOCK_COUNT = 0x48,
+ FSL_ATA_BURST_LENGTH = 0x4C,
+ FSL_ATA_SECTOR_SIZE = 0x50,
+ FSL_ATA_DRIVE_DATA = 0xA0,
+ FSL_ATA_DRIVE_CONTROL = 0xD8,
+
+ /* bits within FSL_ATA_CONTROL */
+ FSL_ATA_CTRL_DMA_SRST = 0x1000,
+ FSL_ATA_CTRL_DMA_64ADMA = 0x800,
+ FSL_ATA_CTRL_DMA_32ADMA = 0x400,
+ FSL_ATA_CTRL_DMA_STAT_STOP = 0x200,
+ FSL_ATA_CTRL_DMA_ENABLE = 0x100,
+ FSL_ATA_CTRL_FIFO_RST_B = 0x80,
+ FSL_ATA_CTRL_ATA_RST_B = 0x40,
+ FSL_ATA_CTRL_FIFO_TX_EN = 0x20,
+ FSL_ATA_CTRL_FIFO_RCV_EN = 0x10,
+ FSL_ATA_CTRL_DMA_PENDING = 0x08,
+ FSL_ATA_CTRL_DMA_ULTRA = 0x04,
+ FSL_ATA_CTRL_DMA_WRITE = 0x02,
+ FSL_ATA_CTRL_IORDY_EN = 0x01,
+
+ /* bits within the interrupt control registers */
+ FSL_ATA_INTR_ATA_INTRQ1 = 0x80,
+ FSL_ATA_INTR_FIFO_UNDERFLOW = 0x40,
+ FSL_ATA_INTR_FIFO_OVERFLOW = 0x20,
+ FSL_ATA_INTR_CTRL_IDLE = 0x10,
+ FSL_ATA_INTR_ATA_INTRQ2 = 0x08,
+ FSL_ATA_INTR_DMA_ERR = 0x04,
+ FSL_ATA_INTR_DMA_TRANS_OVER = 0x02,
+
+ /* ADMA Addr Descriptor Attribute Filed */
+ FSL_ADMA_DES_ATTR_VALID = 0x01,
+ FSL_ADMA_DES_ATTR_END = 0x02,
+ FSL_ADMA_DES_ATTR_INT = 0x04,
+ FSL_ADMA_DES_ATTR_SET = 0x10,
+ FSL_ADMA_DES_ATTR_TRAN = 0x20,
+ FSL_ADMA_DES_ATTR_LINK = 0x30,
+};
+
+/*
+ * This structure contains the timing parameters for
+ * ATA bus timing in the 5 PIO modes. The timings
+ * are in nanoseconds, and are converted to clock
+ * cycles before being stored in the ATA controller
+ * timing registers.
+ */
+static struct {
+ short t0, t1, t2_8, t2_16, t2i, t4, t9, tA;
+} pio_specs[] = {
+ [0] = {
+ .t0 = 600, .t1 = 70, .t2_8 = 290, .t2_16 = 165, .t2i = 0, .t4 =
+ 30, .t9 = 20, .tA = 50,},
+ [1] = {
+ .t0 = 383, .t1 = 50, .t2_8 = 290, .t2_16 = 125, .t2i = 0, .t4 =
+ 20, .t9 = 15, .tA = 50,},
+ [2] = {
+ .t0 = 240, .t1 = 30, .t2_8 = 290, .t2_16 = 100, .t2i = 0, .t4 =
+ 15, .t9 = 10, .tA = 50,},
+ [3] = {
+ .t0 = 180, .t1 = 30, .t2_8 = 80, .t2_16 = 80, .t2i = 0, .t4 =
+ 10, .t9 = 10, .tA = 50,},
+ [4] = {
+ .t0 = 120, .t1 = 25, .t2_8 = 70, .t2_16 = 70, .t2i = 0, .t4 =
+ 10, .t9 = 10, .tA = 50,},
+ };
+
+#define NR_PIO_SPECS (sizeof pio_specs / sizeof pio_specs[0])
+
+/*
+ * This structure contains the timing parameters for
+ * ATA bus timing in the 3 MDMA modes. The timings
+ * are in nanoseconds, and are converted to clock
+ * cycles before being stored in the ATA controller
+ * timing registers.
+ */
+static struct {
+ short t0M, tD, tH, tJ, tKW, tM, tN, tJNH;
+} mdma_specs[] = {
+ [0] = {
+ .t0M = 480, .tD = 215, .tH = 20, .tJ = 20, .tKW = 215, .tM = 50, .tN =
+ 15, .tJNH = 20,},
+ [1] = {
+ .t0M = 150, .tD = 80, .tH = 15, .tJ = 5, .tKW = 50, .tM = 30, .tN =
+ 10, .tJNH = 15,},
+ [2] = {
+ .t0M = 120, .tD = 70, .tH = 10, .tJ = 5, .tKW = 25, .tM = 25, .tN =
+ 10, .tJNH = 10,},
+ };
+
+#define NR_MDMA_SPECS (sizeof mdma_specs / sizeof mdma_specs[0])
+
+/*
+ * This structure contains the timing parameters for
+ * ATA bus timing in the 6 UDMA modes. The timings
+ * are in nanoseconds, and are converted to clock
+ * cycles before being stored in the ATA controller
+ * timing registers.
+ */
+static struct {
+ short t2CYC, tCYC, tDS, tDH, tDVS, tDVH, tCVS, tCVH, tFS_min, tLI_max,
+ tMLI, tAZ, tZAH, tENV_min, tSR, tRFS, tRP, tACK, tSS, tDZFS;
+} udma_specs[] = {
+ [0] = {
+ .t2CYC = 235, .tCYC = 114, .tDS = 15, .tDH = 5, .tDVS = 70, .tDVH =
+ 6, .tCVS = 70, .tCVH = 6, .tFS_min = 0, .tLI_max =
+ 100, .tMLI = 20, .tAZ = 10, .tZAH = 20, .tENV_min =
+ 20, .tSR = 50, .tRFS = 75, .tRP = 160, .tACK = 20, .tSS =
+ 50, .tDZFS = 80,},
+ [1] = {
+ .t2CYC = 156, .tCYC = 75, .tDS = 10, .tDH = 5, .tDVS = 48, .tDVH =
+ 6, .tCVS = 48, .tCVH = 6, .tFS_min = 0, .tLI_max =
+ 100, .tMLI = 20, .tAZ = 10, .tZAH = 20, .tENV_min =
+ 20, .tSR = 30, .tRFS = 70, .tRP = 125, .tACK = 20, .tSS =
+ 50, .tDZFS = 63,},
+ [2] = {
+ .t2CYC = 117, .tCYC = 55, .tDS = 7, .tDH = 5, .tDVS = 34, .tDVH =
+ 6, .tCVS = 34, .tCVH = 6, .tFS_min = 0, .tLI_max =
+ 100, .tMLI = 20, .tAZ = 10, .tZAH = 20, .tENV_min =
+ 20, .tSR = 20, .tRFS = 60, .tRP = 100, .tACK = 20, .tSS =
+ 50, .tDZFS = 47,},
+ [3] = {
+ .t2CYC = 86, .tCYC = 39, .tDS = 7, .tDH = 5, .tDVS = 20, .tDVH =
+ 6, .tCVS = 20, .tCVH = 6, .tFS_min = 0, .tLI_max =
+ 100, .tMLI = 20, .tAZ = 10, .tZAH = 20, .tENV_min =
+ 20, .tSR = 20, .tRFS = 60, .tRP = 100, .tACK = 20, .tSS =
+ 50, .tDZFS = 35,},
+ [4] = {
+ .t2CYC = 57, .tCYC = 25, .tDS = 5, .tDH = 5, .tDVS = 7, .tDVH =
+ 6, .tCVS = 7, .tCVH = 6, .tFS_min = 0, .tLI_max =
+ 100, .tMLI = 20, .tAZ = 10, .tZAH = 20, .tENV_min =
+ 20, .tSR = 50, .tRFS = 60, .tRP = 100, .tACK = 20, .tSS =
+ 50, .tDZFS = 25,},
+ [5] = {
+ .t2CYC = 38, .tCYC = 17, .tDS = 4, .tDH = 5, .tDVS = 5, .tDVH =
+ 6, .tCVS = 10, .tCVH = 10, .tFS_min =
+ 0, .tLI_max = 75, .tMLI = 20, .tAZ = 10, .tZAH =
+ 20, .tENV_min = 20, .tSR = 20, .tRFS =
+ 50, .tRP = 85, .tACK = 20, .tSS = 50, .tDZFS = 40,},
+};
+
+#define NR_UDMA_SPECS (sizeof udma_specs / sizeof udma_specs[0])
+
+struct fsl_ata_time_regs {
+ u8 time_off, time_on, time_1, time_2w;
+ u8 time_2r, time_ax, time_pio_rdx, time_4;
+ u8 time_9, time_m, time_jn, time_d;
+ u8 time_k, time_ack, time_env, time_rpx;
+ u8 time_zah, time_mlix, time_dvh, time_dzfs;
+ u8 time_dvs, time_cvh, time_ss, time_cyc;
+};
+
+static struct regulator *io_reg;
+static struct regulator *core_reg;
+static struct adma_bulk adma_info;
+
+static void
+update_timing_config(struct fsl_ata_time_regs *tp, struct ata_host *host)
+{
+ u32 *lp = (u32 *) tp;
+ struct pata_fsl_priv *priv = host->private_data;
+ u32 *ctlp = (u32 *) priv->fsl_ata_regs;
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ __raw_writel(*lp, ctlp);
+ lp++;
+ ctlp++;
+ }
+}
+
+/*!
+ * Calculate values for the ATA bus timing registers and store
+ * them into the hardware.
+ *
+ * @param xfer_mode specifies XFER xfer_mode
+ * @param pdev specifies platform_device
+ *
+ * @return EINVAL speed out of range, or illegal mode
+ */
+static int set_ata_bus_timing(u8 xfer_mode, struct platform_device *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct pata_fsl_priv *priv = host->private_data;
+
+ /* get the bus clock cycle time, in ns */
+ int T = 1 * 1000 * 1000 * 1000 / clk_get_rate(priv->clk);
+ struct fsl_ata_time_regs tr = { 0 };
+
+ /*
+ * every mode gets the same t_off and t_on
+ */
+ tr.time_off = 3;
+ tr.time_on = 3;
+
+ if (xfer_mode >= XFER_UDMA_0) {
+ int speed = xfer_mode - XFER_UDMA_0;
+ if (speed >= NR_UDMA_SPECS)
+ return -EINVAL;
+
+ tr.time_ack = (udma_specs[speed].tACK + T) / T;
+ tr.time_env = (udma_specs[speed].tENV_min + T) / T;
+ tr.time_rpx = (udma_specs[speed].tRP + T) / T + 2;
+ tr.time_zah = (udma_specs[speed].tZAH + T) / T;
+ tr.time_mlix = (udma_specs[speed].tMLI + T) / T;
+ tr.time_dvh = (udma_specs[speed].tDVH + T) / T + 1;
+ tr.time_dzfs = (udma_specs[speed].tDZFS + T) / T;
+
+ tr.time_dvs = (udma_specs[speed].tDVS + T) / T;
+ tr.time_cvh = (udma_specs[speed].tCVH + T) / T;
+ tr.time_ss = (udma_specs[speed].tSS + T) / T;
+ tr.time_cyc = (udma_specs[speed].tCYC + T) / T;
+ } else if (xfer_mode >= XFER_MW_DMA_0) {
+ int speed = xfer_mode - XFER_MW_DMA_0;
+ if (speed >= NR_MDMA_SPECS)
+ return -EINVAL;
+
+ tr.time_m = (mdma_specs[speed].tM + T) / T;
+ tr.time_jn = (mdma_specs[speed].tJNH + T) / T;
+ tr.time_d = (mdma_specs[speed].tD + T) / T;
+
+ tr.time_k = (mdma_specs[speed].tKW + T) / T;
+ } else {
+ int speed = xfer_mode - XFER_PIO_0;
+ if (speed >= NR_PIO_SPECS)
+ return -EINVAL;
+
+ tr.time_1 = (pio_specs[speed].t1 + T) / T;
+ tr.time_2w = (pio_specs[speed].t2_8 + T) / T;
+
+ tr.time_2r = (pio_specs[speed].t2_8 + T) / T;
+ tr.time_ax = (pio_specs[speed].tA + T) / T + 2;
+ tr.time_pio_rdx = 1;
+ tr.time_4 = (pio_specs[speed].t4 + T) / T;
+
+ tr.time_9 = (pio_specs[speed].t9 + T) / T;
+ }
+
+ update_timing_config(&tr, host);
+
+ return 0;
+}
+
+static void pata_fsl_set_piomode(struct ata_port *ap, struct ata_device *adev)
+{
+ set_ata_bus_timing(adev->pio_mode, to_platform_device(ap->dev));
+}
+
+static void pata_fsl_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+ struct pata_fsl_priv *priv = ap->host->private_data;
+
+ priv->ultra = adev->dma_mode >= XFER_UDMA_0;
+
+ set_ata_bus_timing(adev->dma_mode, to_platform_device(ap->dev));
+}
+
+static int pata_fsl_port_start(struct ata_port *ap)
+{
+ return 0;
+}
+
+static void pata_adma_bulk_unmap(struct ata_queued_cmd *qc)
+{
+ int i;
+ struct adma_bd *bdp = adma_info.adma_bd_table;
+ if (adma_info.qc == NULL)
+ return;
+ BUG_ON(adma_info.qc != qc);
+
+ adma_info.qc = NULL;
+
+ for (i = 0; i < adma_info.sg_ents; i++) {
+ if (bdp->work_buf != bdp->sg_buf) {
+ if (qc->dma_dir == DMA_FROM_DEVICE) {
+ memcpy(bdp->sg_buf, bdp->work_buf, bdp->length);
+ dma_cache_maint(bdp->sg_buf, bdp->length,
+ DMA_FROM_DEVICE);
+ }
+ dma_free_coherent(qc->ap->dev, bdp->length,
+ bdp->work_buf, bdp->dma_address);
+ }
+ bdp->work_buf = bdp->sg_buf = NULL;
+ bdp++;
+ }
+}
+
+static int pata_adma_bulk_map(struct ata_queued_cmd *qc)
+{
+ unsigned int si;
+ struct scatterlist *sg;
+ struct adma_bd *bdp = adma_info.adma_bd_table;
+
+ BUG_ON(adma_info.qc);
+
+ adma_info.qc = qc;
+ adma_info.sg_ents = 0;
+
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
+ /*
+ * The ADMA mode is used setup the ADMA descriptor table
+ */
+ bdp->sg_buf = sg_virt(sg);
+ bdp->length = sg->length;
+ if (sg->dma_address & 0xFFF) {
+ bdp->work_buf =
+ dma_alloc_coherent(qc->ap->dev, bdp->length,
+ &bdp->dma_address, GFP_KERNEL);
+ if (!bdp->work_buf) {
+ printk(KERN_WARNING
+ "can not allocate aligned buffer\n");
+ goto fail;
+ }
+ if (qc->dma_dir == DMA_TO_DEVICE)
+ memcpy(bdp->work_buf, bdp->sg_buf, bdp->length);
+ } else {
+ bdp->work_buf = bdp->sg_buf;
+ bdp->dma_address = sg->dma_address;
+ }
+
+ adma_info.sg_ents++;
+ bdp++;
+ }
+ return 0;
+ fail:
+ pata_adma_bulk_unmap(qc);
+ return -1;
+}
+
+static void dma_callback(void *arg, int error_status, unsigned int count)
+{
+ struct ata_port *ap = arg;
+ struct pata_fsl_priv *priv = ap->host->private_data;
+ u8 *ata_regs = priv->fsl_ata_regs;
+
+ priv->dma_done = 1;
+ /*
+ * DMA is finished, so unmask INTRQ from the drive to allow the
+ * normal ISR to fire.
+ */
+ __raw_writel(FSL_ATA_INTR_ATA_INTRQ2, ata_regs + FSL_ATA_INT_EN);
+}
+
+static irqreturn_t pata_fsl_adma_intr(int irq, void *dev_instance)
+{
+ struct ata_host *host = dev_instance;
+ struct pata_fsl_priv *priv = host->private_data;
+ u8 *ata_regs = priv->fsl_ata_regs;
+ unsigned int handled = 0;
+ unsigned int i;
+ unsigned long flags;
+ unsigned int pending = __raw_readl(ata_regs + FSL_ATA_INT_PEND);
+
+ if (FSL_ATA_INTR_DMA_TRANS_OVER & pending) {
+ priv->dma_done = 1;
+ __raw_writel(pending, ata_regs + FSL_ATA_INT_CLEAR);
+ handled = 1;
+ } else if (FSL_ATA_INTR_DMA_ERR & pending) {
+ printk(KERN_ERR "dma err status 0x%x ...\n",
+ __raw_readl(ata_regs + FSL_ATA_ADMA_ERROR_STATUS));
+ __raw_writel(pending, ata_regs + FSL_ATA_INT_CLEAR);
+ handled = 1;
+ i = __raw_readl(ata_regs + FSL_ATA_CONTROL) && 0xFF;
+ i |= FSL_ATA_CTRL_DMA_SRST | FSL_ATA_CTRL_DMA_32ADMA |
+ FSL_ATA_CTRL_DMA_ENABLE;
+ __raw_writel(i, ata_regs + FSL_ATA_CONTROL);
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap;
+
+ ap = host->ports[i];
+ if (ap && !(ap->flags & ATA_FLAG_DISABLED)) {
+ struct ata_queued_cmd *qc;
+
+ qc = ata_qc_from_tag(ap, ap->link.active_tag);
+ raw_local_irq_restore(flags);
+ pata_adma_bulk_unmap(qc);
+ raw_local_irq_save(flags);
+ if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) &&
+ (qc->flags & ATA_QCFLAG_ACTIVE))
+ handled |= ata_sff_host_intr(ap, qc);
+ }
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return IRQ_RETVAL(handled);
+}
+
+static int pata_fsl_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ return 1; /* ATAPI DMA not yet supported */
+}
+
+unsigned long pata_fsl_bmdma_mode_filter(struct ata_device *adev,
+ unsigned long xfer_mask)
+{
+ /* Capability of the controller has been specified in the
+ * platform data. Do not filter any modes, just return
+ * the xfer_mask */
+ return xfer_mask;
+}
+
+static void pata_fsl_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ int chan, i;
+ int dma_mode = 0, dma_ultra;
+ u32 ata_control;
+ struct ata_port *ap = qc->ap;
+ struct pata_fsl_priv *priv = ap->host->private_data;
+ u8 *ata_regs = priv->fsl_ata_regs;
+ struct fsl_ata_platform_data *plat = ap->dev->platform_data;
+ int err;
+ unsigned int si;
+
+ priv->dma_dir = qc->dma_dir;
+
+ /*
+ * Configure the on-chip ATA interface hardware.
+ */
+ dma_ultra = priv->ultra ? FSL_ATA_CTRL_DMA_ULTRA : 0;
+
+ ata_control = FSL_ATA_CTRL_FIFO_RST_B |
+ FSL_ATA_CTRL_ATA_RST_B | FSL_ATA_CTRL_DMA_PENDING | dma_ultra;
+ if (plat->adma_flag)
+ ata_control |= FSL_ATA_CTRL_DMA_32ADMA |
+ FSL_ATA_CTRL_DMA_ENABLE;
+
+ if (qc->dma_dir == DMA_TO_DEVICE) {
+ chan = priv->dma_wchan;
+ ata_control |= FSL_ATA_CTRL_FIFO_TX_EN | FSL_ATA_CTRL_DMA_WRITE;
+ dma_mode = DMA_MODE_WRITE;
+ } else {
+ chan = priv->dma_rchan;
+ ata_control |= FSL_ATA_CTRL_FIFO_RCV_EN;
+ dma_mode = DMA_MODE_READ;
+ }
+
+ __raw_writel(ata_control, ata_regs + FSL_ATA_CONTROL);
+ __raw_writel(plat->fifo_alarm, ata_regs + FSL_ATA_FIFO_ALARM);
+
+ if (plat->adma_flag) {
+ i = FSL_ATA_INTR_DMA_TRANS_OVER | FSL_ATA_INTR_DMA_ERR;
+ __raw_writel(FSL_ATA_INTR_ATA_INTRQ2 | i,
+ ata_regs + FSL_ATA_INT_EN);
+ } else {
+ __raw_writel(FSL_ATA_INTR_ATA_INTRQ1,
+ ata_regs + FSL_ATA_INT_EN);
+ /*
+ * Set up the DMA completion callback.
+ */
+ mxc_dma_callback_set(chan, dma_callback, (void *)ap);
+ }
+
+ /*
+ * Copy the sg list to an array.
+ */
+ if (plat->adma_flag) {
+ struct adma_bd *bdp = adma_info.adma_bd_table;
+ pata_adma_bulk_map(qc);
+ for (i = 0; i < adma_info.sg_ents; i++) {
+ priv->adma_des_tp[i << 1] = bdp->length << 12;
+ priv->adma_des_tp[i << 1] |= FSL_ADMA_DES_ATTR_SET;
+ priv->adma_des_tp[i << 1] |= FSL_ADMA_DES_ATTR_VALID;
+ priv->adma_des_tp[(i << 1) + 1] = bdp->dma_address;
+ priv->adma_des_tp[(i << 1) + 1] |=
+ FSL_ADMA_DES_ATTR_TRAN;
+ priv->adma_des_tp[(i << 1) + 1] |=
+ FSL_ADMA_DES_ATTR_VALID;
+ if (adma_info.sg_ents == (i + 1))
+ priv->adma_des_tp[(i << 1) + 1] |=
+ FSL_ADMA_DES_ATTR_END;
+ bdp++;
+ }
+ __raw_writel((qc->nbytes / qc->sect_size), ata_regs +
+ FSL_ATA_BLOCK_COUNT);
+ __raw_writel(plat->fifo_alarm, ata_regs + FSL_ATA_BURST_LENGTH);
+ __raw_writel(priv->adma_des_paddr,
+ ata_regs + FSL_ATA_ADMA_SYS_ADDR);
+ } else {
+ int nr_sg = 0;
+ struct scatterlist tmp[MXC_IDE_DMA_BD_NR], *tsg, *sg;
+ tsg = tmp;
+ for_each_sg(qc->sg, sg, qc->n_elem, si) {
+ memcpy(tsg, sg, sizeof(*sg));
+ tsg++;
+ nr_sg++;
+ }
+ err = mxc_dma_sg_config(chan, tmp, nr_sg, 0, dma_mode);
+ if (err)
+ printk(KERN_ERR "pata_fsl_bmdma_setup: error %d\n",
+ err);
+ }
+}
+
+static void pata_fsl_bmdma_start(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct pata_fsl_priv *priv = ap->host->private_data;
+ u8 *ata_regs = priv->fsl_ata_regs;
+ struct fsl_ata_platform_data *plat = ap->dev->platform_data;
+ int chan;
+ int err;
+ unsigned i;
+
+ if (1 == plat->adma_flag) {
+ i = FSL_ATA_CTRL_DMA_32ADMA | FSL_ATA_CTRL_DMA_ENABLE;
+ /* The adma mode is used, set dma_start_stop to 1 */
+ __raw_writel(i | __raw_readl(ata_regs + FSL_ATA_CONTROL) |
+ FSL_ATA_CTRL_DMA_STAT_STOP,
+ ata_regs + FSL_ATA_CONTROL);
+ } else {
+ /*
+ * Start the channel.
+ */
+ chan = qc->dma_dir == DMA_TO_DEVICE ? priv->dma_wchan :
+ priv->dma_rchan;
+
+ err = mxc_dma_enable(chan);
+ if (err)
+ printk(KERN_ERR "%s: : error %d\n", __func__, err);
+ }
+
+ priv->dma_done = 0;
+
+ ata_sff_exec_command(ap, &qc->tf);
+}
+
+static void pata_fsl_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct pata_fsl_priv *priv = ap->host->private_data;
+ u8 *ata_regs = priv->fsl_ata_regs;
+ struct fsl_ata_platform_data *plat = ap->dev->platform_data;
+ unsigned i;
+
+ if (plat->adma_flag) {
+ /* The adma mode is used, set dma_start_stop to 0 */
+ i = FSL_ATA_CTRL_DMA_32ADMA | FSL_ATA_CTRL_DMA_ENABLE;
+ __raw_writel((i | __raw_readl(ata_regs + FSL_ATA_CONTROL)) &
+ (~FSL_ATA_CTRL_DMA_STAT_STOP),
+ ata_regs + FSL_ATA_CONTROL);
+ }
+
+ /* do a dummy read as in ata_bmdma_stop */
+#if 0
+ ata_sff_dma_pause(ap);
+#endif
+}
+
+static u8 pata_fsl_bmdma_status(struct ata_port *ap)
+{
+ struct pata_fsl_priv *priv = ap->host->private_data;
+
+ return priv->dma_done ? ATA_DMA_INTR : 0;
+}
+
+static void pata_fsl_dma_init(struct ata_port *ap)
+{
+ struct pata_fsl_priv *priv = ap->host->private_data;
+
+ priv->dma_rchan = -1;
+ priv->dma_wchan = -1;
+
+ priv->dma_rchan = mxc_dma_request(MXC_DMA_ATA_RX, "MXC ATA RX");
+ if (priv->dma_rchan < 0) {
+ dev_printk(KERN_ERR, ap->dev, "couldn't get RX DMA channel\n");
+ goto err_out;
+ }
+
+ priv->dma_wchan = mxc_dma_request(MXC_DMA_ATA_TX, "MXC ATA TX");
+ if (priv->dma_wchan < 0) {
+ dev_printk(KERN_ERR, ap->dev, "couldn't get TX DMA channel\n");
+ goto err_out;
+ }
+
+ dev_printk(KERN_ERR, ap->dev, "rchan=%d wchan=%d\n", priv->dma_rchan,
+ priv->dma_wchan);
+ return;
+
+ err_out:
+ ap->mwdma_mask = 0;
+ ap->udma_mask = 0;
+ mxc_dma_free(priv->dma_rchan);
+ mxc_dma_free(priv->dma_wchan);
+ kfree(priv);
+}
+
+#if 0
+static u8 pata_fsl_irq_ack(struct ata_port *ap, unsigned int chk_drq)
+{
+ unsigned int bits = chk_drq ? ATA_BUSY | ATA_DRQ : ATA_BUSY;
+ u8 status;
+
+ status = ata_sff_busy_wait(ap, bits, 1000);
+ if (status & bits)
+ if (ata_msg_err(ap))
+ printk(KERN_ERR "abnormal status 0x%X\n", status);
+
+ return status;
+}
+#endif
+
+static void ata_dummy_noret(struct ata_port *ap)
+{
+ return;
+}
+
+static struct scsi_host_template pata_fsl_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .ioctl = ata_scsi_ioctl,
+ .queuecommand = ata_scsi_queuecmd,
+ .can_queue = ATA_DEF_QUEUE,
+ .this_id = ATA_SHT_THIS_ID,
+ .sg_tablesize = LIBATA_MAX_PRD,
+ .cmd_per_lun = ATA_SHT_CMD_PER_LUN,
+ .emulated = ATA_SHT_EMULATED,
+ .use_clustering = ATA_SHT_USE_CLUSTERING,
+ .proc_name = DRV_NAME,
+ .dma_boundary = FSL_ATA_MAX_SG_LEN,
+ .slave_configure = ata_scsi_slave_config,
+ .slave_destroy = ata_scsi_slave_destroy,
+ .bios_param = ata_std_bios_param,
+};
+
+static struct ata_port_operations pata_fsl_port_ops = {
+ .inherits = &ata_bmdma_port_ops,
+ .set_piomode = pata_fsl_set_piomode,
+ .set_dmamode = pata_fsl_set_dmamode,
+
+ .check_atapi_dma = pata_fsl_check_atapi_dma,
+ .cable_detect = ata_cable_unknown,
+ .mode_filter = pata_fsl_bmdma_mode_filter,
+
+ .bmdma_setup = pata_fsl_bmdma_setup,
+ .bmdma_start = pata_fsl_bmdma_start,
+ .bmdma_stop = pata_fsl_bmdma_stop,
+ .bmdma_status = pata_fsl_bmdma_status,
+
+ .qc_prep = ata_noop_qc_prep,
+
+ .sff_data_xfer = ata_sff_data_xfer_noirq,
+ .sff_irq_clear = ata_dummy_noret,
+ .sff_irq_on = ata_sff_irq_on,
+
+ .port_start = pata_fsl_port_start,
+};
+
+static void fsl_setup_port(struct ata_ioports *ioaddr)
+{
+ unsigned int shift = 2;
+
+ ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << shift);
+ ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << shift);
+ ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << shift);
+ ioaddr->nsect_addr = ioaddr->cmd_addr + (ATA_REG_NSECT << shift);
+ ioaddr->lbal_addr = ioaddr->cmd_addr + (ATA_REG_LBAL << shift);
+ ioaddr->lbam_addr = ioaddr->cmd_addr + (ATA_REG_LBAM << shift);
+ ioaddr->lbah_addr = ioaddr->cmd_addr + (ATA_REG_LBAH << shift);
+ ioaddr->device_addr = ioaddr->cmd_addr + (ATA_REG_DEVICE << shift);
+ ioaddr->status_addr = ioaddr->cmd_addr + (ATA_REG_STATUS << shift);
+ ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << shift);
+}
+
+/**
+ * pata_fsl_probe - attach a platform interface
+ * @pdev: platform device
+ *
+ * Register a platform bus integrated ATA host controller
+ *
+ * The 3 platform device resources are used as follows:
+ *
+ * - I/O Base (IORESOURCE_MEM) virt. addr. of ATA controller regs
+ * - CTL Base (IORESOURCE_MEM) unused
+ * - IRQ (IORESOURCE_IRQ) platform IRQ assigned to ATA
+ *
+ */
+static int __devinit pata_fsl_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct resource *io_res;
+ struct ata_host *host;
+ struct ata_port *ap;
+ struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *)
+ pdev->dev.platform_data;
+ struct pata_fsl_priv *priv;
+ u8 *ata_regs;
+ unsigned int int_enable;
+
+ /*
+ * Set up resources
+ */
+ if (unlikely(pdev->num_resources != 3)) {
+ dev_err(&pdev->dev, "invalid number of resources\n");
+ return -EINVAL;
+ }
+ /*
+ * Get an ata_host structure for this device
+ */
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+ ap = host->ports[0];
+
+ /*
+ * Allocate private data
+ */
+ priv = kzalloc(sizeof(struct pata_fsl_priv), GFP_KERNEL);
+ if (priv == NULL) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+ host->private_data = priv;
+
+ /*
+ * Set up resources
+ */
+ io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ata_regs =
+ (u8 *) ioremap(io_res->start, io_res->end - io_res->start + 1);
+ priv->fsl_ata_regs = ata_regs;
+ ap->ioaddr.cmd_addr = (void *)(ata_regs + FSL_ATA_DRIVE_DATA);
+ ap->ioaddr.ctl_addr = (void *)(ata_regs + FSL_ATA_DRIVE_CONTROL);
+ ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
+ ap->ops = &pata_fsl_port_ops;
+ ap->pio_mask = plat->pio_mask; /* support pio 0~4 */
+ ap->mwdma_mask = plat->mwdma_mask; /* support mdma 0~2 */
+ ap->udma_mask = plat->udma_mask;
+ pata_fsl_sht.sg_tablesize = plat->max_sg;
+ fsl_setup_port(&ap->ioaddr);
+
+ if (plat->adma_flag) {
+ priv->adma_des_tp =
+ dma_alloc_coherent(&(pdev->dev),
+ (2 * MXC_IDE_DMA_BD_NR) *
+ sizeof(unsigned int),
+ &(priv->adma_des_paddr), GFP_DMA);
+ if (priv->adma_des_tp == NULL) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+ }
+ /*
+ * Do platform-specific initialization (e.g. allocate pins,
+ * turn on clock). After this call it is assumed that
+ * plat->get_clk_rate() can be called to calculate
+ * timing.
+ */
+ if (plat->init && plat->init(pdev)) {
+ ret = -ENODEV;
+ goto err2;
+ }
+
+ priv->clk = clk_get(&pdev->dev, "ata_clk");
+ clk_enable(priv->clk);
+
+ /* Deassert the reset bit to enable the interface */
+ __raw_writel(FSL_ATA_CTRL_ATA_RST_B, ata_regs + FSL_ATA_CONTROL);
+
+ /* Enable Core regulator & IO Regulator */
+ if (plat->core_reg != NULL) {
+ core_reg = regulator_get(&pdev->dev, plat->core_reg);
+ if (regulator_enable(core_reg))
+ printk(KERN_INFO "enable core regulator error.\n");
+ msleep(100);
+
+ } else
+ core_reg = NULL;
+
+ if (plat->io_reg != NULL) {
+ io_reg = regulator_get(&pdev->dev, plat->io_reg);
+ if (regulator_enable(io_reg))
+ printk(KERN_INFO "enable io regulator error.\n");
+ msleep(100);
+
+ } else
+ io_reg = NULL;
+
+ /* Set initial timing and mode */
+ set_ata_bus_timing(XFER_PIO_4, pdev);
+
+ /* get DMA ready */
+ if (plat->adma_flag == 0)
+ pata_fsl_dma_init(ap);
+
+ /*
+ * Enable the ATA INTRQ interrupt from the bus, but
+ * only allow the CPU to see it (INTRQ2) at this point.
+ * INTRQ1, which goes to the DMA, will be enabled later.
+ */
+ int_enable = FSL_ATA_INTR_DMA_TRANS_OVER | FSL_ATA_INTR_DMA_ERR |
+ FSL_ATA_INTR_ATA_INTRQ2;
+ if (plat->adma_flag)
+ __raw_writel(int_enable, ata_regs + FSL_ATA_INT_EN);
+ else
+ __raw_writel(FSL_ATA_INTR_ATA_INTRQ2,
+ ata_regs + FSL_ATA_INT_EN);
+
+ /* activate */
+ if (plat->adma_flag)
+ ret = ata_host_activate(host, platform_get_irq(pdev, 0),
+ pata_fsl_adma_intr, 0, &pata_fsl_sht);
+ else
+ ret = ata_host_activate(host, platform_get_irq(pdev, 0),
+ ata_sff_interrupt, 0, &pata_fsl_sht);
+
+ if (!ret)
+ return ret;
+
+ clk_disable(priv->clk);
+ regulator_disable(core_reg);
+ regulator_disable(io_reg);
+ err2:
+ if (plat->adma_flag && priv->adma_des_tp)
+ dma_free_coherent(&(pdev->dev),
+ (2 * MXC_IDE_DMA_BD_NR +
+ 1) * sizeof(unsigned int), priv->adma_des_tp,
+ priv->adma_des_paddr);
+ err1:
+ iounmap(ata_regs);
+ kfree(priv);
+ err0:
+ ata_host_detach(host);
+ return ret;
+
+}
+
+/**
+ * pata_fsl_remove - unplug a platform interface
+ * @pdev: platform device
+ *
+ * A platform bus ATA device has been unplugged. Perform the needed
+ * cleanup. Also called on module unload for any active devices.
+ */
+static int __devexit pata_fsl_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct pata_fsl_priv *priv = host->private_data;
+ struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *)
+ pdev->dev.platform_data;
+ u8 *ata_regs = priv->fsl_ata_regs;
+
+ __raw_writel(0, ata_regs + FSL_ATA_INT_EN); /* Disable interrupts */
+
+ ata_host_detach(host);
+
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+ priv->clk = NULL;
+
+ /* Disable Core regulator & IO Regulator */
+ if (plat->core_reg != NULL) {
+ regulator_disable(core_reg);
+ regulator_put(core_reg);
+ }
+ if (plat->io_reg != NULL) {
+ regulator_disable(io_reg);
+ regulator_put(io_reg);
+ }
+
+ if (plat->exit)
+ plat->exit();
+
+ if (plat->adma_flag && priv->adma_des_tp)
+ dma_free_coherent(&(pdev->dev),
+ (2 * MXC_IDE_DMA_BD_NR) *
+ sizeof(unsigned int), priv->adma_des_tp,
+ priv->adma_des_paddr);
+ iounmap(ata_regs);
+
+ kfree(priv);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pata_fsl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct pata_fsl_priv *priv = host->private_data;
+ struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *)
+ pdev->dev.platform_data;
+ u8 *ata_regs = priv->fsl_ata_regs;
+
+ ata_host_suspend(host, state);
+
+ /* Disable interrupts. */
+ __raw_writel(0, ata_regs + FSL_ATA_INT_EN);
+
+ clk_disable(priv->clk);
+
+ if (plat->exit)
+ plat->exit();
+
+ return 0;
+}
+
+static int pata_fsl_resume(struct platform_device *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct pata_fsl_priv *priv = host->private_data;
+ struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *)
+ pdev->dev.platform_data;
+ u8 *ata_regs = priv->fsl_ata_regs;
+ unsigned char int_enable;
+
+ if (plat->init && plat->init(pdev))
+ return -ENODEV;
+
+ clk_enable(priv->clk);
+
+ /* Deassert the reset bit to enable the interface */
+ __raw_writel(FSL_ATA_CTRL_ATA_RST_B, ata_regs + FSL_ATA_CONTROL);
+
+ /* Set initial timing and mode */
+ set_ata_bus_timing(XFER_PIO_4, pdev);
+
+ /*
+ * Enable hardware interrupts.
+ */
+ int_enable = FSL_ATA_INTR_DMA_TRANS_OVER | FSL_ATA_INTR_DMA_ERR |
+ FSL_ATA_INTR_ATA_INTRQ2;
+ if (1 == plat->adma_flag)
+ __raw_writel(int_enable, ata_regs + FSL_ATA_INT_EN);
+ else
+ __raw_writel(FSL_ATA_INTR_ATA_INTRQ2,
+ ata_regs + FSL_ATA_INT_EN);
+
+ ata_host_resume(host);
+
+ return 0;
+}
+#endif
+
+static struct platform_driver pata_fsl_driver = {
+ .probe = pata_fsl_probe,
+ .remove = __devexit_p(pata_fsl_remove),
+#ifdef CONFIG_PM
+ .suspend = pata_fsl_suspend,
+ .resume = pata_fsl_resume,
+#endif
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pata_fsl_init(void)
+{
+ return platform_driver_register(&pata_fsl_driver);
+
+ return 0;
+}
+
+static void __exit pata_fsl_exit(void)
+{
+ platform_driver_unregister(&pata_fsl_driver);
+}
+
+module_init(pata_fsl_init);
+module_exit(pata_fsl_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("low-level driver for Freescale ATA");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 43d6ba83a191..21beac3c04aa 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -418,6 +418,29 @@ config SGI_MBCS
If you have an SGI Altix with an attached SABrick
say Y or M here, otherwise say N.
+config FM_SI4702
+ tristate "SI4702 FM device driver"
+ depends on (MACH_MX31_3DS || MACH_MX35_3DS || MACH_MX37_3DS || MACH_MX51_3DS)
+ default n
+
+config MXC_IIM
+ tristate "MXC IIM device driver"
+ depends on ARCH_MXC
+ help
+ Support for access to MXC IIM device, most people should say N here.
+
+config MXS_VIIM
+ tristate "MXS Virtual IIM device driver"
+ depends on ARCH_STMP3XXX
+ help
+ Support for access to MXS Virtual IIM device, most people should say N here.
+
+config IMX_SIM
+ tristate "IMX SIM support"
+ depends on (ARCH_MX51 || MACH_MX25_3DS)
+ ---help---
+ Say Y to enable the SIM driver support.
+
source "drivers/serial/Kconfig"
config UNIX98_PTYS
@@ -1073,5 +1096,25 @@ config DEVPORT
source "drivers/s390/char/Kconfig"
+config ADC_NS9215
+ tristate "Digi ns9215 On-Chip ADC"
+ depends on PROCESSOR_NS9215
+ help
+ Driver for the internal ADC (Analog Digital Converter) module
+ found on Digi ns9215 CPUs.
+
+ This driver can also be built as a module. If so, the module
+ will be called adc-ns9215.
+
+config ADC_S3C2443
+ tristate "ADC driver for S3C2443 SoC, internal ADC"
+ depends on CPU_S3C2443
+ help
+ Driver for the internal ADC (Analog Digital Converter) module
+ found on S3C2443 CPU.
+
+ This driver can also be built as a module. If so, the module
+ will be called adc-s3c2443.
+
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 438f71317c5c..ce2067b792b0 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -9,6 +9,11 @@ FONTMAPFILE = cp437.uni
obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o
+obj-$(CONFIG_FM_SI4702) += mxc_si4702.o
+obj-$(CONFIG_MXC_IIM) += mxc_iim.o
+obj-$(CONFIG_MXS_VIIM) += mxs_viim.o
+obj-$(CONFIG_IMX_SIM) += imx_sim.o
+
obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
obj-y += misc.o
@@ -95,7 +100,6 @@ obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o
obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
obj-$(CONFIG_GPIO_TB0219) += tb0219.o
obj-$(CONFIG_TELCLOCK) += tlclk.o
-
obj-$(CONFIG_MWAVE) += mwave/
obj-$(CONFIG_AGP) += agp/
obj-$(CONFIG_PCMCIA) += pcmcia/
@@ -109,6 +113,9 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
+obj-$(CONFIG_ADC_NS9215) += adc-ns9215.o
+obj-$(CONFIG_ADC_S3C2443) += adc-s3c2443.o
+
# Files generated that shall be removed upon make clean
clean-files := consolemap_deftbl.c defkeymap.c
diff --git a/drivers/char/adc-ns9215.c b/drivers/char/adc-ns9215.c
new file mode 100644
index 000000000000..3c2b12c39f4d
--- /dev/null
+++ b/drivers/char/adc-ns9215.c
@@ -0,0 +1,257 @@
+/*
+ * linux/drivers/char/adc-ns9215.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+/* registers */
+#define ADC_CONFIG (0x0)
+#define ADC_CLOCK (0x4)
+#define ADC_OUTPUT (0x8)
+#define ADC_OUTPUT_OFFS (0x4)
+
+/* configuration bit fields */
+#define ADC_CONFIG_DISABLE (0 << 31)
+#define ADC_CONFIG_ENABLE (1 << 31)
+#define ADC_CONFIG_ENABLECHANNELS (7)
+
+#define DRIVER_NAME "adc-ns9215"
+#define CHANNELS 8
+#define MAXCLOCK 14000000 /* max 14MHz */
+#define MAXCLOCKN 0x3FF
+
+struct ns9215_adc_channel {
+ struct semaphore sem;
+ int pos;
+ unsigned char lb;
+};
+
+struct ns9215_adc_pdata {
+ void __iomem *ioaddr;
+ struct resource *mem;
+ struct clk *clk;
+ struct cdev cdev;
+ struct ns9215_adc_channel channel[CHANNELS];
+ dev_t dev_id;
+};
+
+static struct ns9215_adc_pdata pdata;
+
+ssize_t ns9215_adc_read(struct file *filep, char __user *buff,
+ size_t count, loff_t *offp)
+{
+ unsigned int val;
+ unsigned char buf[2];
+ int channel = (int)filep->private_data;
+
+ if (pdata.channel[channel].pos == 1) {
+ copy_to_user(buff, &pdata.channel[channel].lb, 1);
+ pdata.channel[channel].pos = 0;
+ return 1;
+ }
+
+ val = ioread32(pdata.ioaddr + ADC_OUTPUT + (channel * ADC_OUTPUT_OFFS));
+ buf[1] = (val & 0xf00) >> 8;
+ buf[0] = val & 0xff;
+
+ if (count == 1) {
+ copy_to_user(buff, buf , 1);
+ pdata.channel[channel].pos = 1;
+ pdata.channel[channel].lb = buf[1];
+ return 1;
+ }
+
+ copy_to_user(buff, buf, 2);
+ return 2;
+}
+
+int ns9215_adc_open(struct inode *inode, struct file *filep)
+{
+ int channel = iminor(inode);
+
+ if (down_trylock(&pdata.channel[channel].sem) < 0)
+ return -EBUSY;
+
+ filep->private_data = (void *)channel;
+ pdata.channel[channel].pos = 0;
+
+ return 0;
+}
+
+int ns9215_adc_release(struct inode *inode, struct file *filep)
+{
+ int channel = iminor(inode);
+
+ up(&pdata.channel[channel].sem);
+
+ return 0;
+}
+
+struct file_operations ns9215_adc_fops = {
+ .owner = THIS_MODULE,
+ .read = ns9215_adc_read,
+ .open = ns9215_adc_open,
+ .release = ns9215_adc_release,
+};
+
+static int __init ns9215_adc_probe(struct platform_device *pdev)
+{
+ int i, ret;
+ unsigned int clockn;
+
+ pdata.mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pdata.mem) {
+ pr_err(DRIVER_NAME ": memory not available\n");
+ return -ENOENT;
+ }
+
+ if (!request_mem_region(pdata.mem->start,
+ pdata.mem->end - pdata.mem->start + 1,
+ DRIVER_NAME)) {
+ pr_err(DRIVER_NAME ": memory already mapped\n");
+ goto ns9215_err_mem;
+ }
+
+ pdata.ioaddr = ioremap(pdata.mem->start,
+ pdata.mem->end - pdata.mem->start + 1);
+ if (pdata.ioaddr <= 0) {
+ pr_err(DRIVER_NAME ": unable to remap IO memory\n");
+ goto ns9215_err_remap;
+ }
+ pr_debug(DRIVER_NAME ": mapped ADC to virtual address 0x%x\n",
+ (int)pdata.ioaddr);
+
+ if (alloc_chrdev_region(&pdata.dev_id, 0, CHANNELS, DRIVER_NAME) < 0) {
+ pr_err(DRIVER_NAME ": requesting chrdev_region fails\n");
+ goto ns9215_err_cdev_reg;
+ }
+
+ pdata.clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(pdata.clk)) {
+ ret = PTR_ERR(pdata.clk);
+ goto ns9215_err_clk_get;
+ }
+
+ ret = clk_enable(pdata.clk);
+ if (ret) {
+ pr_err(DRIVER_NAME ": cannot enable clock\n");
+ goto ns9215_err_clk_enable;
+ }
+
+ /* set clock */
+ clockn = ((clk_get_rate(pdata.clk)) - 2 * MAXCLOCK)
+ / (2 * MAXCLOCK) + 1;
+ if (clockn > MAXCLOCKN) {
+ pr_warning(DRIVER_NAME ": cannot set a proper ADC clock,"
+ " ADC will not work correctly!\n");
+ clockn = MAXCLOCKN;
+ }
+
+ iowrite32(clockn, pdata.ioaddr + ADC_CLOCK);
+
+ /* initialize mutexes for every channel */
+ for (i = 0; i < CHANNELS; i++) {
+ init_MUTEX(&pdata.channel[i].sem);
+ pdata.channel[i].pos = 0;
+ }
+
+ /* enable adc & all channels */
+ iowrite32(ADC_CONFIG_ENABLE | ADC_CONFIG_ENABLECHANNELS,
+ pdata.ioaddr + ADC_CONFIG);
+
+ cdev_init(&pdata.cdev, &ns9215_adc_fops);
+ if (cdev_add(&pdata.cdev, pdata.dev_id, CHANNELS) < 0) {
+ pr_err(DRIVER_NAME ": cannot add character device\n");
+ goto ns9215_err_cdev_add;
+ }
+
+ pr_info(DRIVER_NAME ": ADC available on MAJOR %i\n",
+ MAJOR(pdata.dev_id));
+
+ return 0;
+
+ns9215_err_cdev_add:
+ clk_disable(pdata.clk);
+ns9215_err_clk_enable:
+ clk_put(pdata.clk);
+ns9215_err_clk_get:
+ unregister_chrdev_region(pdata.dev_id, CHANNELS);
+ns9215_err_cdev_reg:
+ iounmap(pdata.ioaddr);
+ns9215_err_remap:
+ release_mem_region(pdata.mem->start,
+ pdata.mem->end - pdata.mem->start + 1);
+ns9215_err_mem:
+
+ return -EIO;
+}
+
+static int ns9215_adc_remove(struct platform_device *pdev)
+{
+ int i;
+
+ /* lock/check all channels */
+ for (i = 0; i < CHANNELS; i++)
+ if (down_trylock(&pdata.channel[i].sem))
+ /* channel still locked! */
+ goto ns9215_adc_remove_unlock;
+
+ /* disable ADC */
+ iowrite32(ADC_CONFIG_DISABLE, pdata.ioaddr + ADC_CONFIG);
+
+ cdev_del(&pdata.cdev);
+ clk_disable(pdata.clk);
+ clk_put(pdata.clk);
+ unregister_chrdev_region(pdata.dev_id, CHANNELS);
+ iounmap(pdata.ioaddr);
+ release_mem_region(pdata.mem->start,
+ pdata.mem->end - pdata.mem->start + 1);
+
+ return 0;
+
+ns9215_adc_remove_unlock:
+ for (i--; i >= 0; i--)
+ up(&pdata.channel[i].sem);
+ return -EBUSY;
+}
+
+static struct platform_driver ns9215_adc_driver = {
+ .probe = ns9215_adc_probe,
+ .remove = ns9215_adc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ /* TODO: power management */
+};
+
+static int __init ns9215_adc_init(void)
+{
+ return platform_driver_register(&ns9215_adc_driver);
+}
+
+static void __exit ns9215_adc_exit(void)
+{
+ platform_driver_unregister(&ns9215_adc_driver);
+}
+
+module_init(ns9215_adc_init);
+module_exit(ns9215_adc_exit);
+
+MODULE_AUTHOR("Matthias Ludwig");
+MODULE_DESCRIPTION("ADC driver for Digi NS9215");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/adc-s3c2443.c b/drivers/char/adc-s3c2443.c
new file mode 100644
index 000000000000..34de79e20045
--- /dev/null
+++ b/drivers/char/adc-s3c2443.c
@@ -0,0 +1,323 @@
+/*
+ * linux/drivers/char/adc-s3c2443.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+/*
+ * In future, this driver should be replaced by the general adc core support
+ * for the Samsung platforms, plus the corresponding hwmon driver and the touch
+ * driver that uses that common adc support.
+ */
+
+/*
+ * TODO, split from this driver and from the touch driver the common stuff and
+ * move it to a core adc module.
+ */
+
+
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+
+#include <plat/regs-adc.h>
+#include <plat/ts.h>
+
+#define DRIVER_NAME "s3c2443-adc"
+#ifndef NUM_CHANNELS
+# define NUM_CHANNELS 10
+#endif
+
+struct s3c2443_adc_channel {
+ struct semaphore sem;
+};
+
+struct s3c2443_adc_dev {
+ void __iomem *ioaddr;
+ struct resource *res_iomem;
+ struct clk *clk;
+ struct cdev cdev;
+ struct s3c2443_adc_channel channel[NUM_CHANNELS];
+ dev_t dev_id;
+ spinlock_t lock;
+ u32 adccon;
+ u32 adctsc;
+ u32 adcdly;
+};
+
+static struct s3c2443_adc_dev *adc;
+
+static u16 adc_read_sample(struct s3c2443_adc_dev *adcdev, int channel)
+{
+ u32 adccon, timeout = 5000;
+
+ /* Select channel, start conversion and wait until its completed */
+ writel(channel, adcdev->ioaddr + S3C2410_ADCMUX);
+ adccon = readl(adcdev->ioaddr + S3C2410_ADCCON);
+ writel(adccon | S3C2410_ADCCON_ENABLE_START, adcdev->ioaddr + S3C2410_ADCCON);
+
+ while (timeout--) {
+ adccon = readl(adcdev->ioaddr + S3C2410_ADCCON);
+ if (adccon & S3C2410_ADCCON_ECFLG)
+ break;
+ ndelay(100);
+ }
+
+ return readl(adcdev->ioaddr + S3C2410_ADCDAT0) & S3C2410_ADCDAT0_XPDATA_MASK;
+}
+
+
+static ssize_t s3c2443_adc_read(struct file *filep, char __user *buff,
+ size_t count, loff_t *offp)
+{
+ struct s3c2443_adc_dev *adcdev = (struct s3c2443_adc_dev *)filep->private_data;
+ int ch = iminor(filep->f_dentry->d_inode);
+ u16 val12;
+ u8 val8;
+
+ val12 = adc_read_sample(adcdev, ch);
+ val8 = val12 >> 4;
+
+ count = (count > 2) ? 2 : count;
+
+ if (count == 1)
+ copy_to_user(buff, &val8, count);
+ else
+ copy_to_user(buff, &val12, count);
+
+ return count;
+}
+
+static int s3c2443_adc_open(struct inode *inode, struct file *filep)
+{
+ struct s3c2443_adc_dev *adcdev = container_of(inode->i_cdev,
+ struct s3c2443_adc_dev, cdev);
+ int ch = iminor(inode);
+
+ if (ch >= NUM_CHANNELS)
+ return -EINVAL;
+
+ if (down_trylock(&adcdev->channel[ch].sem) < 0)
+ return -EBUSY;
+
+ filep->private_data = (void *)adcdev;
+
+ return 0;
+}
+
+static int s3c2443_adc_release(struct inode *inode, struct file *filep)
+{
+ struct s3c2443_adc_dev *adcdev = container_of(inode->i_cdev,
+ struct s3c2443_adc_dev, cdev);
+ int ch = iminor(inode);
+
+ up(&adcdev->channel[ch].sem);
+
+ return 0;
+}
+
+struct file_operations s3c2443_adc_fops = {
+ .owner = THIS_MODULE,
+ .read = s3c2443_adc_read,
+ .open = s3c2443_adc_open,
+ .release = s3c2443_adc_release,
+};
+
+static int __devinit s3c2443_adc_probe(struct platform_device *pdev)
+{
+ int ret, i;
+ struct s3c_ts_mach_info *info;
+
+ info = pdev->dev.platform_data;
+ if (!info) {
+ pr_err(DRIVER_NAME ": no platform data provided\n");
+ return -EINVAL;
+ }
+
+ adc = kzalloc(sizeof(*adc), GFP_KERNEL);
+ if (!adc) {
+ pr_err(DRIVER_NAME ": mem alloc error\n");
+ return -ENOMEM;
+ }
+
+ adc->res_iomem = platform_get_resource_byname(pdev, IORESOURCE_MEM, DRIVER_NAME);
+ if (!adc->res_iomem) {
+ pr_err(DRIVER_NAME ": unable to find iomem resource\n");
+ ret = -ENOENT;
+ goto error_get_res;
+ }
+ /*
+ * Dont request mem region becasue the address space is shared with the touch
+ * driver. This will be resolved in future when both drivers use the same core
+ * adc funcionality
+ */
+ adc->ioaddr = ioremap(adc->res_iomem->start,
+ adc->res_iomem->end - adc->res_iomem->start + 1);
+ if (!adc->ioaddr) {
+ pr_err(DRIVER_NAME ": unable to remap IO memory\n");
+ ret = -EBUSY;
+ goto error_remap;
+ }
+ pr_debug(DRIVER_NAME ": mapped ADC to virtual address 0x%x\n", (u32)adc->ioaddr);
+
+ ret = alloc_chrdev_region(&adc->dev_id, 0, NUM_CHANNELS, DRIVER_NAME);
+ if (ret < 0) {
+ pr_err(DRIVER_NAME ": error requesting chrdev_region for %d\n", adc->dev_id);
+ goto error_cdev_reg;
+ }
+
+ /* Get the required source clock */
+ adc->clk = clk_get(&pdev->dev, "adc");
+ if (IS_ERR(adc->clk)) {
+ pr_err(DRIVER_NAME ": error getting adc clock source\n");
+ ret = PTR_ERR(adc->clk);
+ goto error_get_clk;
+ }
+ clk_enable(adc->clk);
+
+ /* initialize mutexes for every channel */
+ for (i = 0; i < NUM_CHANNELS; i++)
+ init_MUTEX(&adc->channel[i].sem);
+
+ cdev_init(&adc->cdev, &s3c2443_adc_fops);
+ ret = cdev_add(&adc->cdev, adc->dev_id, NUM_CHANNELS);
+ if (ret < 0) {
+ pr_err(DRIVER_NAME ": cannot add character device\n");
+ goto error_cdev_add;
+ }
+
+ /* Prepare the passed user configuration values */
+ if ((info->presc & 0xff) > 0)
+ writel(S3C2410_ADCCON_PRSCEN |
+ S3C2410_ADCCON_PRSCVL(info->presc & 0xFF),
+ adc->ioaddr + S3C2410_ADCCON);
+ else
+ writel(0, adc->ioaddr + S3C2410_ADCCON);
+
+
+ /* Initialise the start delay register (Manual: Do not use zero value) */
+ if ((info->delay & 0xffff) > 0)
+ writel(info->delay & 0xffff, adc->ioaddr + S3C2410_ADCDLY);
+ else
+ writel(10000, adc->ioaddr + S3C2410_ADCDLY);
+
+ platform_set_drvdata(pdev, adc);
+
+ pr_info(DRIVER_NAME ": ADC available on MAJOR %i\n", MAJOR(adc->dev_id));
+
+ return 0;
+
+error_cdev_add:
+ clk_put(adc->clk);
+error_get_clk:
+ unregister_chrdev_region(adc->dev_id, NUM_CHANNELS);
+error_cdev_reg:
+ iounmap(adc->ioaddr);
+error_remap:
+error_get_res:
+ kfree(adc);
+ return ret;
+}
+
+int s3c2443_adc_remove(struct platform_device *pdev)
+{
+ int i;
+
+ if (!adc)
+ return -EIO;
+
+ /* lock/check all channels */
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if (down_trylock(&adc->channel[i].sem))
+ /* channel still locked! */
+ goto adc_remove_unlock;
+ }
+
+ cdev_del(&adc->cdev);
+ clk_disable(adc->clk);
+ clk_put(adc->clk);
+ unregister_chrdev_region(adc->dev_id, NUM_CHANNELS);
+ iounmap(adc->ioaddr);
+ kfree(adc);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+
+adc_remove_unlock:
+ for (i--; i >= 0; i--)
+ up(&adc->channel[i].sem);
+ return -EBUSY;
+}
+
+#ifdef CONFIG_PM
+static int s3c2443_adc_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct s3c2443_adc_dev *adc;
+
+ adc = platform_get_drvdata(dev);
+
+ /* save registers and disable the clock */
+ adc->adccon = readl(adc->ioaddr + S3C2410_ADCCON);
+ adc->adctsc = readl(adc->ioaddr + S3C2410_ADCTSC);
+ adc->adcdly = readl(adc->ioaddr + S3C2410_ADCDLY);
+ clk_disable(adc->clk);
+
+ return 0;
+}
+
+static int s3c2443_adc_resume(struct platform_device *pdev)
+{
+ struct s3c2443_adc_dev *adc;
+
+ adc = platform_get_drvdata(pdev);
+
+ /* restore registers and enable the clock */
+ clk_enable(adc->clk);
+ writel(adc->adccon, adc->ioaddr + S3C2410_ADCCON);
+ writel(adc->adctsc, adc->ioaddr + S3C2410_ADCTSC);
+ writel(adc->adcdly, adc->ioaddr + S3C2410_ADCDLY);
+
+ return 0;
+}
+#else
+#define s3c2443_adc_suspend NULL
+#define s3c2443_adc_resume NULL
+#endif
+
+static struct platform_driver s3c2443_adc_driver = {
+ .probe = s3c2443_adc_probe,
+ .remove = __devexit_p(s3c2443_adc_remove),
+ .suspend = s3c2443_adc_suspend,
+ .resume = s3c2443_adc_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s3c2443_adc_init(void)
+{
+ return platform_driver_register(&s3c2443_adc_driver);
+}
+
+static void __exit s3c2443_adc_exit(void)
+{
+ platform_driver_unregister(&s3c2443_adc_driver);
+}
+
+module_init(s3c2443_adc_init);
+module_exit(s3c2443_adc_exit);
+
+MODULE_AUTHOR("Digi International");
+MODULE_DESCRIPTION("ADC driver for S3C2443");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index 8822eca58ffa..c6703b94324c 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -46,6 +46,30 @@ config HW_RANDOM_AMD
If unsure, say Y.
+config HW_RANDOM_FSL_RNGA
+ tristate "Freescale RNGA Random Number Generator"
+ depends on HW_RANDOM && ARCH_HAS_RNGA && !MXC_SECURITY_RNG
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Freescale i.MX processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fsl-rnga.
+
+ If unsure, say Y.
+
+config HW_RANDOM_FSL_RNGC
+ tristate "Freescale RNGC Random Number Generator"
+ depends on HW_RANDOM && ARCH_HAS_RNGC && !MXC_SECURITY_RNG
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Freescale i.MX processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fsl-rngc.
+
+ If unsure, say Y.
+
config HW_RANDOM_GEODE
tristate "AMD Geode HW Random Number Generator support"
depends on HW_RANDOM && X86_32 && PCI
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index b6effb7522c2..bea544b26e53 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -6,6 +6,8 @@ obj-$(CONFIG_HW_RANDOM) += rng-core.o
rng-core-y := core.o
obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
+obj-$(CONFIG_HW_RANDOM_FSL_RNGA) += fsl-rnga.o
+obj-$(CONFIG_HW_RANDOM_FSL_RNGC) += fsl-rngc.o
obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o
n2-rng-y := n2-drv.o n2-asm.o
diff --git a/drivers/char/hw_random/fsl-rnga.c b/drivers/char/hw_random/fsl-rnga.c
new file mode 100644
index 000000000000..a5c8065604d4
--- /dev/null
+++ b/drivers/char/hw_random/fsl-rnga.c
@@ -0,0 +1,238 @@
+/*
+ * RNG driver for Freescale RNGA
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+ * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for the AMD 768 Random Number Generator (RNG)
+ * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for Intel i810 Random Number Generator (RNG)
+ * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.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/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+
+/* RNGA Registers */
+#define RNGA_CONTROL 0x00
+#define RNGA_STATUS 0x04
+#define RNGA_ENTROPY 0x08
+#define RNGA_OUTPUT_FIFO 0x0c
+#define RNGA_MODE 0x10
+#define RNGA_VERIFICATION_CONTROL 0x14
+#define RNGA_OSC_CONTROL_COUNTER 0x18
+#define RNGA_OSC1_COUNTER 0x1c
+#define RNGA_OSC2_COUNTER 0x20
+#define RNGA_OSC_COUNTER_STATUS 0x24
+
+/* RNGA Registers Range */
+#define RNG_ADDR_RANGE 0x28
+
+/* RNGA Control Register */
+#define RNGA_CONTROL_SLEEP 0x00000010
+#define RNGA_CONTROL_CLEAR_INT 0x00000008
+#define RNGA_CONTROL_MASK_INTS 0x00000004
+#define RNGA_CONTROL_HIGH_ASSURANCE 0x00000002
+#define RNGA_CONTROL_GO 0x00000001
+
+#define RNGA_STATUS_LEVEL_MASK 0x0000ff00
+
+/* RNGA Status Register */
+#define RNGA_STATUS_OSC_DEAD 0x80000000
+#define RNGA_STATUS_SLEEP 0x00000010
+#define RNGA_STATUS_ERROR_INT 0x00000008
+#define RNGA_STATUS_FIFO_UNDERFLOW 0x00000004
+#define RNGA_STATUS_LAST_READ_STATUS 0x00000002
+#define RNGA_STATUS_SECURITY_VIOLATION 0x00000001
+
+static struct platform_device *rng_dev;
+
+static int fsl_rnga_data_present(struct hwrng *rng)
+{
+ int level;
+ u32 rng_base = (u32) rng->priv;
+
+ /* how many random numbers is in FIFO? [0-16] */
+ level = ((__raw_readl(rng_base + RNGA_STATUS) &
+ RNGA_STATUS_LEVEL_MASK) >> 8);
+
+ return level > 0 ? 1 : 0;
+}
+
+static int fsl_rnga_data_read(struct hwrng *rng, u32 * data)
+{
+ int err;
+ u32 ctrl, rng_base = (u32) rng->priv;
+
+ /* retrieve a random number from FIFO */
+ *data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO);
+
+ /* some error while reading this random number? */
+ err = __raw_readl(rng_base + RNGA_STATUS) & RNGA_STATUS_ERROR_INT;
+
+ /* if error: clear error interrupt, but doesn't return random number */
+ if (err) {
+ dev_dbg(&rng_dev->dev, "Error while reading random number!\n");
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT,
+ rng_base + RNGA_CONTROL);
+ return 0;
+ } else
+ return 4;
+}
+
+static int fsl_rnga_init(struct hwrng *rng)
+{
+ u32 ctrl, osc, rng_base = (u32) rng->priv;
+
+ /* wake up */
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL);
+
+ /* verify if oscillator is working */
+ osc = __raw_readl(rng_base + RNGA_STATUS);
+ if (osc & RNGA_STATUS_OSC_DEAD) {
+ dev_err(&rng_dev->dev, "RNGA Oscillator is dead!\n");
+ return -ENODEV;
+ }
+
+ /* go running */
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl | RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
+
+ return 0;
+}
+
+static void fsl_rnga_cleanup(struct hwrng *rng)
+{
+ u32 ctrl, rng_base = (u32) rng->priv;
+
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+
+ /* stop rnga */
+ __raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
+}
+
+static struct hwrng fsl_rnga = {
+ .name = "fsl-rnga",
+ .init = fsl_rnga_init,
+ .cleanup = fsl_rnga_cleanup,
+ .data_present = fsl_rnga_data_present,
+ .data_read = fsl_rnga_data_read
+};
+
+static int __init fsl_rnga_probe(struct platform_device *pdev)
+{
+ int err = -ENODEV;
+ struct clk *clk;
+ struct resource *res, *mem;
+ void __iomem *rng_base = NULL;
+
+ if (rng_dev)
+ return -EBUSY;
+
+ clk = clk_get(NULL, "rng_clk");
+
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Could not get rng_clk!\n");
+ err = PTR_ERR(clk);
+ return err;
+ }
+
+ clk_enable(clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENOENT;
+
+ mem = request_mem_region(res->start, res->end - res->start, pdev->name);
+
+ if (mem == NULL)
+ return -EBUSY;
+
+ dev_set_drvdata(&pdev->dev, mem);
+ rng_base = ioremap(res->start, res->end - res->start);
+
+ fsl_rnga.priv = (unsigned long)rng_base;
+
+ err = hwrng_register(&fsl_rnga);
+ if (err) {
+ dev_err(&pdev->dev, "FSL RNGA registering failed (%d)\n", err);
+ return err;
+ }
+
+ rng_dev = pdev;
+
+ dev_info(&pdev->dev, "FSL RNGA Registered.\n");
+
+ return 0;
+}
+
+static int __exit fsl_rnga_remove(struct platform_device *pdev)
+{
+ struct resource *mem = dev_get_drvdata(&pdev->dev);
+ void __iomem *rng_base = (void __iomem *)fsl_rnga.priv;
+
+ hwrng_unregister(&fsl_rnga);
+
+ release_resource(mem);
+
+ iounmap(rng_base);
+
+ return 0;
+}
+
+static struct platform_driver fsl_rnga_driver = {
+ .driver = {
+ .name = "fsl_rnga",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(fsl_rnga_remove),
+};
+
+static int __init mod_init(void)
+{
+ return platform_driver_probe(&fsl_rnga_driver, fsl_rnga_probe);
+}
+
+static void __exit mod_exit(void)
+{
+ platform_driver_unregister(&fsl_rnga_driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("H/W RNGA driver for i.MX");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/fsl-rngc.c b/drivers/char/hw_random/fsl-rngc.c
new file mode 100644
index 000000000000..9bf78e846fa0
--- /dev/null
+++ b/drivers/char/hw_random/fsl-rngc.c
@@ -0,0 +1,372 @@
+/*
+ * RNG driver for Freescale RNGC
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+ * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for the AMD 768 Random Number Generator (RNG)
+ * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for Intel i810 Random Number Generator (RNG)
+ * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.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/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <asm/hardware.h>
+
+#define RNGC_VERSION_MAJOR3 3
+
+#define RNGC_VERSION_ID 0x0000
+#define RNGC_COMMAND 0x0004
+#define RNGC_CONTROL 0x0008
+#define RNGC_STATUS 0x000C
+#define RNGC_ERROR 0x0010
+#define RNGC_FIFO 0x0014
+#define RNGC_VERIF_CTRL 0x0020
+#define RNGC_OSC_CTRL_COUNT 0x0028
+#define RNGC_OSC_COUNT 0x002C
+#define RNGC_OSC_COUNT_STATUS 0x0030
+
+#define RNGC_VERID_ZEROS_MASK 0x0f000000
+#define RNGC_VERID_RNG_TYPE_MASK 0xf0000000
+#define RNGC_VERID_RNG_TYPE_SHIFT 28
+#define RNGC_VERID_CHIP_VERSION_MASK 0x00ff0000
+#define RNGC_VERID_CHIP_VERSION_SHIFT 16
+#define RNGC_VERID_VERSION_MAJOR_MASK 0x0000ff00
+#define RNGC_VERID_VERSION_MAJOR_SHIFT 8
+#define RNGC_VERID_VERSION_MINOR_MASK 0x000000ff
+#define RNGC_VERID_VERSION_MINOR_SHIFT 0
+
+#define RNGC_CMD_ZEROS_MASK 0xffffff8c
+#define RNGC_CMD_SW_RST 0x00000040
+#define RNGC_CMD_CLR_ERR 0x00000020
+#define RNGC_CMD_CLR_INT 0x00000010
+#define RNGC_CMD_SEED 0x00000002
+#define RNGC_CMD_SELF_TEST 0x00000001
+
+#define RNGC_CTRL_ZEROS_MASK 0xfffffc8c
+#define RNGC_CTRL_CTL_ACC 0x00000200
+#define RNGC_CTRL_VERIF_MODE 0x00000100
+#define RNGC_CTRL_MASK_ERROR 0x00000040
+
+#define RNGC_CTRL_MASK_DONE 0x00000020
+#define RNGC_CTRL_AUTO_SEED 0x00000010
+#define RNGC_CTRL_FIFO_UFLOW_MASK 0x00000003
+#define RNGC_CTRL_FIFO_UFLOW_SHIFT 0
+
+#define RNGC_CTRL_FIFO_UFLOW_ZEROS_ERROR 0
+#define RNGC_CTRL_FIFO_UFLOW_ZEROS_ERROR2 1
+#define RNGC_CTRL_FIFO_UFLOW_BUS_XFR 2
+#define RNGC_CTRL_FIFO_UFLOW_ZEROS_INTR 3
+
+#define RNGC_STATUS_ST_PF_MASK 0x00c00000
+#define RNGC_STATUS_ST_PF_SHIFT 22
+#define RNGC_STATUS_ST_PF_TRNG 0x00800000
+#define RNGC_STATUS_ST_PF_PRNG 0x00400000
+#define RNGC_STATUS_ERROR 0x00010000
+#define RNGC_STATUS_FIFO_SIZE_MASK 0x0000f000
+#define RNGC_STATUS_FIFO_SIZE_SHIFT 12
+#define RNGC_STATUS_FIFO_LEVEL_MASK 0x00000f00
+#define RNGC_STATUS_FIFO_LEVEL_SHIFT 8
+#define RNGC_STATUS_NEXT_SEED_DONE 0x00000040
+#define RNGC_STATUS_SEED_DONE 0x00000020
+#define RNGC_STATUS_ST_DONE 0x00000010
+#define RNGC_STATUS_RESEED 0x00000008
+#define RNGC_STATUS_SLEEP 0x00000004
+#define RNGC_STATUS_BUSY 0x00000002
+#define RNGC_STATUS_SEC_STATE 0x00000001
+
+#define RNGC_ERROR_STATUS_ZEROS_MASK 0xffffffc0
+#define RNGC_ERROR_STATUS_BAD_KEY 0x00000040
+#define RNGC_ERROR_STATUS_RAND_ERR 0x00000020
+#define RNGC_ERROR_STATUS_FIFO_ERR 0x00000010
+#define RNGC_ERROR_STATUS_STAT_ERR 0x00000008
+#define RNGC_ERROR_STATUS_ST_ERR 0x00000004
+#define RNGC_ERROR_STATUS_OSC_ERR 0x00000002
+#define RNGC_ERROR_STATUS_LFSR_ERR 0x00000001
+
+#define RNG_ADDR_RANGE 0x34
+
+static DECLARE_COMPLETION(rng_self_testing);
+static DECLARE_COMPLETION(rng_seed_done);
+
+static struct platform_device *rng_dev;
+
+int irq_rng;
+
+static int fsl_rngc_data_present(struct hwrng *rng)
+{
+ int level;
+ u32 rngc_base = (u32) rng->priv;
+
+ /* how many random numbers are in FIFO? [0-16] */
+ level = (__raw_readl(rngc_base + RNGC_STATUS) &
+ RNGC_STATUS_FIFO_LEVEL_MASK) >> RNGC_STATUS_FIFO_LEVEL_SHIFT;
+
+ return level > 0 ? 1 : 0;
+}
+
+static int fsl_rngc_data_read(struct hwrng *rng, u32 * data)
+{
+ int err;
+ u32 rngc_base = (u32) rng->priv;
+
+ /* retrieve a random number from FIFO */
+ *data = __raw_readl(rngc_base + RNGC_FIFO);
+
+ /* is there some error while reading this random number? */
+ err = __raw_readl(rngc_base + RNGC_STATUS) & RNGC_STATUS_ERROR;
+
+ /* if error happened doesn't return random number */
+ return err ? 0 : 4;
+}
+
+static irqreturn_t rngc_irq(int irq, void *dev)
+{
+ int handled = 0;
+ u32 rngc_base = (u32) dev;
+
+ /* is the seed creation done? */
+ if (__raw_readl(rngc_base + RNGC_STATUS) & RNGC_STATUS_SEED_DONE) {
+ complete(&rng_seed_done);
+ __raw_writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
+ rngc_base + RNGC_COMMAND);
+ handled = 1;
+ }
+
+ /* is the self test done? */
+ if (__raw_readl(rngc_base + RNGC_STATUS) & RNGC_STATUS_ST_DONE) {
+ complete(&rng_self_testing);
+ __raw_writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
+ rngc_base + RNGC_COMMAND);
+ handled = 1;
+ }
+
+ /* is there any error? */
+ if (__raw_readl(rngc_base + RNGC_STATUS) & RNGC_STATUS_ERROR) {
+ /* clear interrupt */
+ __raw_writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
+ rngc_base + RNGC_COMMAND);
+ handled = 1;
+ }
+
+ return handled;
+}
+
+static int fsl_rngc_init(struct hwrng *rng)
+{
+ int err;
+ u32 cmd, ctrl, osc, rngc_base = (u32) rng->priv;
+
+ INIT_COMPLETION(rng_self_testing);
+ INIT_COMPLETION(rng_seed_done);
+
+ err = __raw_readl(rngc_base + RNGC_STATUS) & RNGC_STATUS_ERROR;
+ if (err) {
+ /* is this a bad keys error ? */
+ if (__raw_readl(rngc_base + RNGC_ERROR) &
+ RNGC_ERROR_STATUS_BAD_KEY) {
+ dev_err(&rng_dev->dev, "Can't start, Bad Keys.\n");
+ return -EIO;
+ }
+ }
+
+ /* mask all interrupts, will be unmasked soon */
+ ctrl = __raw_readl(rngc_base + RNGC_CONTROL);
+ __raw_writel(ctrl | RNGC_CTRL_MASK_DONE | RNGC_CTRL_MASK_ERROR,
+ rngc_base + RNGC_CONTROL);
+
+ /* verify if oscillator is working */
+ osc = __raw_readl(rngc_base + RNGC_ERROR);
+ if (osc & RNGC_ERROR_STATUS_OSC_ERR) {
+ dev_err(&rng_dev->dev, "RNGC Oscillator is dead!\n");
+ return -EIO;
+ }
+
+ err = request_irq(irq_rng, rngc_irq, 0, "fsl_rngc", (void *)rng->priv);
+ if (err) {
+ dev_err(&rng_dev->dev, "Can't get interrupt working.\n");
+ return -EIO;
+ }
+
+ /* do self test, repeat until get success */
+ do {
+ /* clear error */
+ cmd = __raw_readl(rngc_base + RNGC_COMMAND);
+ __raw_writel(cmd | RNGC_CMD_CLR_ERR, rngc_base + RNGC_COMMAND);
+
+ /* unmask all interrupt */
+ ctrl = __raw_readl(rngc_base + RNGC_CONTROL);
+ __raw_writel(ctrl & ~(RNGC_CTRL_MASK_DONE |
+ RNGC_CTRL_MASK_ERROR), rngc_base + RNGC_CONTROL);
+
+ /* run self test */
+ cmd = __raw_readl(rngc_base + RNGC_COMMAND);
+ __raw_writel(cmd | RNGC_CMD_SELF_TEST,
+ rngc_base + RNGC_COMMAND);
+
+ wait_for_completion(&rng_self_testing);
+
+ } while (__raw_readl(rngc_base + RNGC_ERROR) &
+ RNGC_ERROR_STATUS_ST_ERR);
+
+ /* clear interrupt. Is it really necessary here? */
+ __raw_writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
+ rngc_base + RNGC_COMMAND);
+
+ /* create seed, repeat while there is some statistical error */
+ do {
+ /* clear error */
+ cmd = __raw_readl(rngc_base + RNGC_COMMAND);
+ __raw_writel(cmd | RNGC_CMD_CLR_ERR, rngc_base + RNGC_COMMAND);
+
+ /* seed creation */
+ cmd = __raw_readl(rngc_base + RNGC_COMMAND);
+ __raw_writel(cmd | RNGC_CMD_SEED, rngc_base + RNGC_COMMAND);
+
+ wait_for_completion(&rng_seed_done);
+
+ } while (__raw_readl(rngc_base + RNGC_ERROR) &
+ RNGC_ERROR_STATUS_STAT_ERR);
+
+ err = __raw_readl(rngc_base + RNGC_ERROR) &
+ (RNGC_ERROR_STATUS_STAT_ERR |
+ RNGC_ERROR_STATUS_RAND_ERR |
+ RNGC_ERROR_STATUS_FIFO_ERR |
+ RNGC_ERROR_STATUS_ST_ERR |
+ RNGC_ERROR_STATUS_OSC_ERR |
+ RNGC_ERROR_STATUS_LFSR_ERR);
+
+ if (err) {
+ dev_err(&rng_dev->dev, "FSL RNGC appears inoperable.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static struct hwrng fsl_rngc = {
+ .name = "fsl-rngc",
+ .init = fsl_rngc_init,
+ .data_present = fsl_rngc_data_present,
+ .data_read = fsl_rngc_data_read
+};
+
+static int __init fsl_rngc_probe(struct platform_device *pdev)
+{
+ int err = -ENODEV;
+ struct clk *clk;
+ struct resource *res, *mem;
+ void __iomem *rngc_base = NULL;
+
+ if (rng_dev)
+ return -EBUSY;
+
+ clk = clk_get(NULL, "rng_clk");
+
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Can not get rng_clk\n");
+ err = PTR_ERR(clk);
+ return err;
+ }
+
+ clk_enable(clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENOENT;
+
+ mem = request_mem_region(res->start, res->end - res->start, pdev->name);
+
+ if (mem == NULL)
+ return -EBUSY;
+
+ dev_set_drvdata(&pdev->dev, mem);
+ rngc_base = ioremap(res->start, res->end - res->start);
+
+ fsl_rngc.priv = (unsigned long)rngc_base;
+
+ irq_rng = platform_get_irq(pdev, 0);
+
+ err = hwrng_register(&fsl_rngc);
+ if (err) {
+ dev_err(&pdev->dev, "FSL RNGC registering failed (%d)\n", err);
+ return err;
+ }
+
+ rng_dev = pdev;
+
+ dev_info(&pdev->dev, "FSL RNGC Registered.\n");
+
+ return 0;
+}
+
+static int __exit fsl_rngc_remove(struct platform_device *pdev)
+{
+ struct resource *mem = dev_get_drvdata(&pdev->dev);
+ void __iomem *rngc_base = (void __iomem *)fsl_rngc.priv;
+
+ hwrng_unregister(&fsl_rngc);
+
+ release_resource(mem);
+
+ iounmap(rngc_base);
+
+ return 0;
+}
+
+static struct platform_driver fsl_rngc_driver = {
+ .driver = {
+ .name = "fsl_rngc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(fsl_rngc_remove),
+};
+
+static int __init mod_init(void)
+{
+ return platform_driver_probe(&fsl_rngc_driver, fsl_rngc_probe);
+}
+
+static void __exit mod_exit(void)
+{
+ platform_driver_unregister(&fsl_rngc_driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("H/W RNGC driver for i.MX");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/imx_sim.c b/drivers/char/imx_sim.c
new file mode 100644
index 000000000000..41bee5d7cdcb
--- /dev/null
+++ b/drivers/char/imx_sim.c
@@ -0,0 +1,1495 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_sim.c
+ *
+ * @brief Driver for Freescale IMX SIM interface
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/clk.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mxc_sim_interface.h>
+
+#include <asm/io.h>
+
+#define SIM_INTERNAL_CLK 0
+#define SIM_RFU -1
+
+/* Default communication parameters: FI=372, DI=1, PI1=5V, II=50mA, WWT=10 */
+#define SIM_PARAM_DEFAULT { 0, 1, 1, 5, 1, 0, 0, 0, 10 }
+
+/* Transmit and receive buffer sizes */
+#define SIM_XMT_BUFFER_SIZE 256
+#define SIM_RCV_BUFFER_SIZE 256
+
+/* Interface character references */
+#define SIM_IFC_TXI(letter, number) (letter + number * 4)
+#define SIM_IFC_TA1 SIM_IFC_TXI(0, 0)
+#define SIM_IFC_TB1 SIM_IFC_TXI(0, 1)
+#define SIM_IFC_TC1 SIM_IFC_TXI(0, 2)
+#define SIM_IFC_TD1 SIM_IFC_TXI(0, 3)
+#define SIM_IFC_TA2 SIM_IFC_TXI(1, 0)
+#define SIM_IFC_TB2 SIM_IFC_TXI(1, 1)
+#define SIM_IFC_TC2 SIM_IFC_TXI(1, 2)
+#define SIM_IFC_TD2 SIM_IFC_TXI(1, 3)
+#define SIM_IFC_TA3 SIM_IFC_TXI(2, 0)
+#define SIM_IFC_TB3 SIM_IFC_TXI(2, 1)
+#define SIM_IFC_TC3 SIM_IFC_TXI(2, 2)
+#define SIM_IFC_TD3 SIM_IFC_TXI(2, 3)
+#define SIM_IFC_TA4 SIM_IFC_TXI(3, 0)
+#define SIM_IFC_TB4 SIM_IFC_TXI(3, 1)
+#define SIM_IFC_TC4 SIM_IFC_TXI(3, 2)
+#define SIM_IFC_TD4 SIM_IFC_TXI(3, 3)
+
+/* ATR and OPS states */
+#define SIM_STATE_REMOVED 0
+#define SIM_STATE_OPERATIONAL_IDLE 1
+#define SIM_STATE_OPERATIONAL_COMMAND 2
+#define SIM_STATE_OPERATIONAL_RESPONSE 3
+#define SIM_STATE_OPERATIONAL_STATUS1 4
+#define SIM_STATE_OPERATIONAL_STATUS2 5
+#define SIM_STATE_OPERATIONAL_PTS 6
+#define SIM_STATE_DETECTED_ATR_T0 7
+#define SIM_STATE_DETECTED_ATR_TS 8
+#define SIM_STATE_DETECTED_ATR_TXI 9
+#define SIM_STATE_DETECTED_ATR_THB 10
+#define SIM_STATE_DETECTED_ATR_TCK 11
+
+/* Definitions of the offset of the SIM hardware registers */
+#define PORT1_CNTL 0x00 /* 00 */
+#define SETUP 0x04 /* 04 */
+#define PORT1_DETECT 0x08 /* 08 */
+#define PORT1_XMT_BUF 0x0C /* 0c */
+#define PORT1_RCV_BUF 0x10 /* 10 */
+#define PORT0_CNTL 0x14 /* 14 */
+#define CNTL 0x18 /* 18 */
+#define CLK_PRESCALER 0x1C /* 1c */
+#define RCV_THRESHOLD 0x20 /* 20 */
+#define ENABLE 0x24 /* 24 */
+#define XMT_STATUS 0x28 /* 28 */
+#define RCV_STATUS 0x2C /* 2c */
+#define INT_MASK 0x30 /* 30 */
+#define PORTO_XMT_BUF 0x34 /* 34 */
+#define PORT0_RCV_BUF 0x38 /* 38 */
+#define PORT0_DETECT 0x3C /* 3c */
+#define DATA_FORMAT 0x40 /* 40 */
+#define XMT_THRESHOLD 0x44 /* 44 */
+#define GUARD_CNTL 0x48 /* 48 */
+#define OD_CONFIG 0x4C /* 4c */
+#define RESET_CNTL 0x50 /* 50 */
+#define CHAR_WAIT 0x54 /* 54 */
+#define GPCNT 0x58 /* 58 */
+#define DIVISOR 0x5C /* 5c */
+#define BWT 0x60 /* 60 */
+#define BGT 0x64 /* 64 */
+#define BWT_H 0x68 /* 68 */
+#define XMT_FIFO_STAT 0x6C /* 6c */
+#define RCV_FIFO_CNT 0x70 /* 70 */
+#define RCV_FIFO_WPTR 0x74 /* 74 */
+#define RCV_FIFO_RPTR 0x78 /* 78 */
+
+/* SIM port[0|1]_cntl register bits */
+#define SIM_PORT_CNTL_SFPD (1<<7)
+#define SIM_PORT_CNTL_3VOLT (1<<6)
+#define SIM_PORT_CNTL_SCSP (1<<5)
+#define SIM_PORT_CNTL_SCEN (1<<4)
+#define SIM_PORT_CNTL_SRST (1<<3)
+#define SIM_PORT_CNTL_STEN (1<<2)
+#define SIM_PORT_CNTL_SVEN (1<<1)
+#define SIM_PORT_CNTL_SAPD (1<<0)
+
+/* SIM od_config register bits */
+#define SIM_OD_CONFIG_OD_P1 (1<<1)
+#define SIM_OD_CONFIG_OD_P0 (1<<0)
+
+/* SIM enable register bits */
+#define SIM_ENABLE_XMTEN (1<<1)
+#define SIM_ENABLE_RCVEN (1<<0)
+
+/* SIM int_mask register bits */
+#define SIM_INT_MASK_RFEM (1<<13)
+#define SIM_INT_MASK_BGTM (1<<12)
+#define SIM_INT_MASK_BWTM (1<<11)
+#define SIM_INT_MASK_RTM (1<<10)
+#define SIM_INT_MASK_CWTM (1<<9)
+#define SIM_INT_MASK_GPCM (1<<8)
+#define SIM_INT_MASK_TDTFM (1<<7)
+#define SIM_INT_MASK_TFOM (1<<6)
+#define SIM_INT_MASK_XTM (1<<5)
+#define SIM_INT_MASK_TFEIM (1<<4)
+#define SIM_INT_MASK_ETCIM (1<<3)
+#define SIM_INT_MASK_OIM (1<<2)
+#define SIM_INT_MASK_TCIM (1<<1)
+#define SIM_INT_MASK_RIM (1<<0)
+
+/* SIM xmt_status register bits */
+#define SIM_XMT_STATUS_GPCNT (1<<8)
+#define SIM_XMT_STATUS_TDTF (1<<7)
+#define SIM_XMT_STATUS_TFO (1<<6)
+#define SIM_XMT_STATUS_TC (1<<5)
+#define SIM_XMT_STATUS_ETC (1<<4)
+#define SIM_XMT_STATUS_TFE (1<<3)
+#define SIM_XMT_STATUS_XTE (1<<0)
+
+/* SIM rcv_status register bits */
+#define SIM_RCV_STATUS_BGT (1<<11)
+#define SIM_RCV_STATUS_BWT (1<<10)
+#define SIM_RCV_STATUS_RTE (1<<9)
+#define SIM_RCV_STATUS_CWT (1<<8)
+#define SIM_RCV_STATUS_CRCOK (1<<7)
+#define SIM_RCV_STATUS_LRCOK (1<<6)
+#define SIM_RCV_STATUS_RDRF (1<<5)
+#define SIM_RCV_STATUS_RFD (1<<4)
+#define SIM_RCV_STATUS_RFE (1<<1)
+#define SIM_RCV_STATUS_OEF (1<<0)
+
+/* SIM cntl register bits */
+#define SIM_CNTL_BWTEN (1<<15)
+#define SIM_CNTL_XMT_CRC_LRC (1<<14)
+#define SIM_CNTL_CRCEN (1<<13)
+#define SIM_CNTL_LRCEN (1<<12)
+#define SIM_CNTL_CWTEN (1<<11)
+#define SIM_CNTL_SAMPLE12 (1<<4)
+#define SIM_CNTL_ONACK (1<<3)
+#define SIM_CNTL_ANACK (1<<2)
+#define SIM_CNTL_ICM (1<<1)
+#define SIM_CNTL_GPCNT_CLK_SEL(x) ((x&0x03)<<9)
+#define SIM_CNTL_GPCNT_CLK_SEL_MASK (0x03<<9)
+#define SIM_CNTL_BAUD_SEL(x) ((x&0x07)<<6)
+#define SIM_CNTL_BAUD_SEL_MASK (0x07<<6)
+
+/* SIM rcv_threshold register bits */
+#define SIM_RCV_THRESHOLD_RTH(x) ((x&0x0f)<<9)
+#define SIM_RCV_THRESHOLD_RTH_MASK (0x0f<<9)
+#define SIM_RCV_THRESHOLD_RDT(x) ((x&0x1ff)<<0)
+#define SIM_RCV_THRESHOLD_RDT_MASK (0x1ff<<0)
+
+/* SIM xmt_threshold register bits */
+#define SIM_XMT_THRESHOLD_XTH(x) ((x&0x0f)<<4)
+#define SIM_XMT_THRESHOLD_XTH_MASK (0x0f<<4)
+#define SIM_XMT_THRESHOLD_TDT(x) ((x&0x0f)<<0)
+#define SIM_XMT_THRESHOLD_TDT_MASK (0x0f<<0)
+
+/* SIM guard_cntl register bits */
+#define SIM_GUARD_CNTL_RCVR11 (1<<8)
+#define SIM_GIARD_CNTL_GETU(x) (x&0xff)
+#define SIM_GIARD_CNTL_GETU_MASK (0xff)
+
+/* SIM port[0|]_detect register bits */
+#define SIM_PORT_DETECT_SPDS (1<<3)
+#define SIM_PORT_DETECT_SPDP (1<<2)
+#define SIM_PORT_DETECT_SDI (1<<1)
+#define SIM_PORT_DETECT_SDIM (1<<0)
+
+/* END of REGS definitions */
+
+/* ATR parser data (the parser state is stored in the main device structure) */
+typedef struct {
+ uint8_t T0; /* ATR T0 */
+ uint8_t TS; /* ATR TS */
+ /* ATR TA1, TB1, TC1, TD1, TB1, ... , TD4 */
+ uint8_t TXI[16];
+ uint8_t THB[15]; /* ATR historical bytes */
+ uint8_t TCK; /* ATR checksum */
+ uint16_t ifc_valid; /* valid interface characters */
+ uint8_t ifc_current_valid; /* calid ifcs in the current batch */
+ uint8_t cnt; /* number of current batch */
+ uint8_t num_hb; /* number of historical bytes */
+} sim_atrparser_t;
+
+/* Main SIM driver structure */
+typedef struct {
+ /* card inserted = 1, ATR received = 2, card removed = 0 */
+ int present;
+ /* current ATR or OPS state */
+ int state;
+ /* current power state */
+ int power;
+ /* error code occured during transfer */
+ int errval;
+ struct clk *clk; /* Clock id */
+ uint8_t clk_flag;
+ struct resource *res; /* IO map memory */
+ void __iomem *ioaddr; /* Mapped address */
+ int ipb_irq; /* sim ipb IRQ num */
+ int dat_irq; /* sim dat IRQ num */
+ /* parser for incoming ATR stream */
+ sim_atrparser_t atrparser;
+ /* raw ATR stream received */
+ sim_atr_t atr;
+ /* communication parameters according to ATR */
+ sim_param_t param_atr;
+ /* current communication parameters */
+ sim_param_t param;
+ /* current TPDU or PTS transfer */
+ sim_xfer_t xfer;
+ /* transfer is on the way = 1, idle = 2 */
+ int xfer_ongoing;
+ /* remaining bytes to transmit for the current transfer */
+ int xmt_remaining;
+ /* transmit position */
+ int xmt_pos;
+ /* receive position / number of bytes received */
+ int rcv_count;
+ uint8_t rcv_buffer[SIM_RCV_BUFFER_SIZE];
+ uint8_t xmt_buffer[SIM_XMT_BUFFER_SIZE];
+ /* transfer completion notifier */
+ struct completion xfer_done;
+ /* async notifier for card and ATR detection */
+ struct fasync_struct *fasync;
+ /* Platform specific data */
+ struct mxc_sim_platform_data *plat_data;
+} sim_t;
+
+static int sim_param_F[] = {
+ SIM_INTERNAL_CLK, 372, 558, 744, 1116, 1488, 1860, SIM_RFU,
+ SIM_RFU, 512, 768, 1024, 1536, 2048, SIM_RFU, SIM_RFU
+};
+
+static int sim_param_D[] = {
+ SIM_RFU, 64 * 1, 64 * 2, 64 * 4, 64 * 8, 64 * 16, SIM_RFU, SIM_RFU,
+ SIM_RFU, SIM_RFU, 64 * 1 / 2, 64 * 1 / 4, 64 * 1 / 8, 64 * 1 / 16,
+ 64 * 1 / 32, 64 * 1 / 64
+};
+
+static struct miscdevice sim_dev;
+
+/* Function: sim_calc_param
+ *
+ * Description: determine register values depending on communication parameters
+ *
+ * Parameters:
+ * uint32_t fi ATR frequency multiplier index
+ * uint32_t di ATR frequency divider index
+ * uint32_t* ptr_divisor location to store divisor result
+ * uint32_t* ptr_sample12 location to store sample12 result
+ *
+ * Return Values:
+ * SIM_OK calculation finished without errors
+ * -SIM_E_PARAM_DIVISOR_RANGE calculated divisor > 255
+ * -SIM_E_PARAM_FBYD_NOTDIVBY8OR12 F/D not divisable by 12 (as required)
+ * -SIM_E_PARAM_FBYD_WITHFRACTION F/D has a remainder
+ * -SIM_E_PARAM_DI_INVALID frequency multiplyer index not supported
+ * -SIM_E_PARAM_FI_INVALID frequency divider index not supported
+ */
+
+static int sim_calc_param(uint32_t fi, uint32_t di, uint32_t *ptr_divisor,
+ uint32_t *ptr_sample12)
+{
+ int32_t errval = SIM_OK;
+ int32_t f = sim_param_F[fi];
+ int32_t d = sim_param_D[di];
+ int32_t stage2_fra = (64 * f) % d;
+ int32_t stage2_div = (64 * f) / d;
+ uint32_t sample12 = 1;
+ uint32_t divisor = 31;
+
+ pr_debug("%s entering.\n", __func__);
+ if ((f > 0) || (d > 0)) {
+ if (stage2_fra == 0) {
+ if ((stage2_div % 12) == 0) {
+ sample12 = 1;
+ divisor = stage2_div / 12;
+ } else if ((stage2_div % 8) == 0) {
+ sample12 = 0;
+ divisor = stage2_div / 8;
+ } else
+ sample12 = -1;
+ if (sample12 >= 0) {
+ if (divisor < 256) {
+ pr_debug("fi=%i", fi);
+ pr_debug("di=%i", di);
+ pr_debug("f=%i", f);
+ pr_debug("d=%i/64", d);
+ pr_debug("div=%i", stage2_div);
+ pr_debug("divisor=%i", divisor);
+ pr_debug("sample12=%i\n", sample12);
+
+ *ptr_divisor = divisor;
+ *ptr_sample12 = sample12;
+ errval = SIM_OK;
+ } else
+ errval = -SIM_E_PARAM_DIVISOR_RANGE;
+ } else
+ errval = -SIM_E_PARAM_FBYD_NOTDIVBY8OR12;
+ } else
+ errval = -SIM_E_PARAM_FBYD_WITHFRACTION;
+ } else
+ errval = -SIM_E_PARAM_FI_INVALID;
+
+ return errval;
+};
+
+/* Function: sim_set_param
+ *
+ * Description: apply communication parameters (setup devisor and sample12)
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ * sim_param_t* param pointer to communication parameters
+ *
+ * Return Values:
+ * see function sim_calc_param
+ */
+
+static int sim_set_param(sim_t *sim, sim_param_t *param)
+{
+ uint32_t divisor, sample12, reg_data;
+ int errval;
+
+ pr_debug("%s entering.\n", __func__);
+ errval = sim_calc_param(param->FI, param->DI, &divisor, &sample12);
+ if (errval == SIM_OK) {
+ __raw_writel(divisor, sim->ioaddr + DIVISOR);
+ if (sample12) {
+ reg_data = __raw_readl(sim->ioaddr + CNTL);
+ reg_data |= SIM_CNTL_SAMPLE12;
+ __raw_writel(reg_data, sim->ioaddr + CNTL);
+ } else {
+ reg_data = __raw_readl(sim->ioaddr + CNTL);
+ reg_data &= ~SIM_CNTL_SAMPLE12;
+ __raw_writel(reg_data, sim->ioaddr + CNTL);
+ }
+ }
+
+ return errval;
+};
+
+/* Function: sim_atr_received
+ *
+ * Description: this function is called whenever a valid ATR has been received.
+ * It determines the communication parameters from the ATR received and notifies
+ * the user space application with SIGIO.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_atr_received(sim_t *sim)
+{
+ sim_param_t param_default = SIM_PARAM_DEFAULT;
+ sim->param_atr = param_default;
+
+ pr_debug("%s entering.\n", __func__);
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TA1))) {
+ sim->param_atr.FI = sim->atrparser.TXI[SIM_IFC_TA1] >> 4;
+ sim->param_atr.DI = sim->atrparser.TXI[SIM_IFC_TA1] & 0x0f;
+ }
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TB1))) {
+ sim->param_atr.PI1 = (sim->atrparser.TXI[SIM_IFC_TB1] >> 4)
+ & 0x07;
+ sim->param_atr.II = sim->atrparser.TXI[SIM_IFC_TB1] & 0x07f;
+ }
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TC1)))
+ sim->param_atr.N = sim->atrparser.TXI[SIM_IFC_TC1];
+
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TD1)))
+ sim->param_atr.T = sim->atrparser.TXI[SIM_IFC_TD1] & 0x0f;
+
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TB2)))
+ sim->param_atr.PI2 = sim->atrparser.TXI[SIM_IFC_TB2];
+
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TC2)))
+ sim->param_atr.WWT = sim->atrparser.TXI[SIM_IFC_TC2];
+
+ if (sim->fasync)
+ kill_fasync(&sim->fasync, SIGIO, POLL_IN);
+
+};
+
+/* Function: sim_xmt_fill
+ *
+ * Description: fill the transmit FIFO until the FIFO is full or
+ * the end of the transmission has been reached.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_xmt_fill(sim_t *sim)
+{
+ uint32_t reg_data;
+ int bytesleft;
+
+ reg_data = __raw_readl(sim->ioaddr + XMT_FIFO_STAT);
+ bytesleft = 16 - ((reg_data >> 8) & 0x0f);
+
+ pr_debug("txfill: remaining=%i bytesleft=%i\n",
+ sim->xmt_remaining, bytesleft);
+ if (bytesleft > sim->xmt_remaining)
+ bytesleft = sim->xmt_remaining;
+
+ sim->xmt_remaining -= bytesleft;
+ for (; bytesleft > 0; bytesleft--) {
+ __raw_writel(sim->xmt_buffer[sim->xmt_pos],
+ sim->ioaddr + PORT1_XMT_BUF);
+ sim->xmt_pos++;
+ };
+/* FIXME: optimization - keep filling until fifo full */
+};
+
+/* Function: sim_xmt_start
+ *
+ * Description: initiate a transfer
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ * int pos position in the xfer transmit buffer
+ * int count number of bytes to be transmitted
+ */
+
+static void sim_xmt_start(sim_t *sim, int pos, int count)
+{
+ uint32_t reg_data;
+
+ pr_debug("tx\n");
+ sim->xmt_remaining = count;
+ sim->xmt_pos = pos;
+ sim_xmt_fill(sim);
+
+ if (sim->xmt_remaining) {
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data &= ~SIM_INT_MASK_TDTFM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ } else {
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data &= ~SIM_INT_MASK_TCIM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ __raw_writel(SIM_XMT_STATUS_TC | SIM_XMT_STATUS_TDTF,
+ sim->ioaddr + XMT_STATUS);
+ reg_data = __raw_readl(sim->ioaddr + ENABLE);
+ reg_data |= SIM_ENABLE_XMTEN;
+ __raw_writel(reg_data, sim->ioaddr + ENABLE);
+ }
+};
+
+/* Function: sim_atr_add
+ *
+ * Description: add a byte to the raw ATR string
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ * uint8_t data byte to be added
+ */
+
+static void sim_atr_add(sim_t *sim, uint8_t data)
+{
+ pr_debug("%s entering.\n", __func__);
+ if (sim->atr.size < SIM_ATR_LENGTH_MAX)
+ sim->atr.t[sim->atr.size++] = data;
+ else
+ printk(KERN_ERR "sim.c: ATR received is too big!\n");
+};
+
+/* Function: sim_fsm
+ *
+ * Description: main finite state machine running in ISR context.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ * uint8_t data byte received
+ */
+
+static void sim_fsm(sim_t *sim, uint16_t data)
+{
+ uint32_t temp, i = 0;
+ switch (sim->state) {
+
+ pr_debug("%s stat is %d \n", __func__, sim->state);
+ /* OPS FSM */
+
+ case SIM_STATE_OPERATIONAL_IDLE:
+ printk(KERN_INFO "data received unexpectidly (%04x)\n", data);
+ break;
+
+ case SIM_STATE_OPERATIONAL_COMMAND:
+ if (data == sim->xmt_buffer[1]) {
+ if (sim->xfer.rcv_length) {
+ sim->state = SIM_STATE_OPERATIONAL_RESPONSE;
+ } else {
+ sim->state = SIM_STATE_OPERATIONAL_STATUS1;
+ if (sim->xfer.xmt_length > 5)
+ sim_xmt_start(sim, 5,
+ sim->xfer.xmt_length - 5);
+ };
+ } else if (((data & 0xf0) == 0x60) | ((data & 0xf0) == 0x90)) {
+ sim->xfer.sw1 = data;
+ sim->state = SIM_STATE_OPERATIONAL_STATUS2;
+ } else {
+ sim->errval = -SIM_E_NACK;
+ complete(&sim->xfer_done);
+ };
+ break;
+
+ case SIM_STATE_OPERATIONAL_RESPONSE:
+ sim->rcv_buffer[sim->rcv_count] = data;
+ sim->rcv_count++;
+ if (sim->rcv_count == sim->xfer.rcv_length)
+ sim->state = SIM_STATE_OPERATIONAL_STATUS1;
+ break;
+
+ case SIM_STATE_OPERATIONAL_STATUS1:
+ sim->xfer.sw1 = data;
+ sim->state = SIM_STATE_OPERATIONAL_STATUS2;
+ break;
+
+ case SIM_STATE_OPERATIONAL_STATUS2:
+ sim->xfer.sw2 = data;
+ sim->state = SIM_STATE_OPERATIONAL_IDLE;
+ complete(&sim->xfer_done);
+ break;
+
+ case SIM_STATE_OPERATIONAL_PTS:
+ sim->rcv_buffer[sim->rcv_count] = data;
+ sim->rcv_count++;
+ if (sim->rcv_count == sim->xfer.rcv_length)
+ sim->state = SIM_STATE_OPERATIONAL_IDLE;
+ break;
+
+ /* ATR FSM */
+
+ case SIM_STATE_DETECTED_ATR_T0:
+ sim_atr_add(sim, data);
+ pr_debug("T0 %02x\n", data);
+ sim->atrparser.T0 = data;
+ sim->state = SIM_STATE_DETECTED_ATR_TS;
+ break;
+
+ case SIM_STATE_DETECTED_ATR_TS:
+ sim_atr_add(sim, data);
+ pr_debug("TS %02x\n", data);
+ sim->atrparser.TS = data;
+ if (data & 0xf0) {
+ sim->atrparser.ifc_current_valid = (data >> 4) & 0x0f;
+ sim->atrparser.num_hb = data & 0x0f;
+ sim->atrparser.ifc_valid = 0;
+ sim->state = SIM_STATE_DETECTED_ATR_TXI;
+ sim->atrparser.cnt = 0;
+ } else {
+ goto sim_fsm_atr_thb;
+ };
+ break;
+
+ case SIM_STATE_DETECTED_ATR_TXI:
+ sim_atr_add(sim, data);
+ i = ffs(sim->atrparser.ifc_current_valid) - 1;
+ pr_debug("T%c%i %02x\n", 'A' + i, sim->atrparser.cnt + 1, data);
+ sim->atrparser.TXI[SIM_IFC_TXI(i, sim->atrparser.cnt)] = data;
+ sim->atrparser.ifc_valid |= 1 << SIM_IFC_TXI(i,
+ sim->atrparser.
+ cnt);
+ sim->atrparser.ifc_current_valid &= ~(1 << i);
+
+ if (sim->atrparser.ifc_current_valid == 0) {
+ if (i == 3) {
+ sim->atrparser.ifc_current_valid = (data >> 4)
+ & 0x0f;
+ sim->atrparser.cnt++;
+
+ if (sim->atrparser.cnt >= 4) {
+ /* error */
+ printk(KERN_ERR "ERROR !\n");
+ break;
+ };
+
+ if (sim->atrparser.ifc_current_valid == 0)
+ goto sim_fsm_atr_thb;
+ } else {
+sim_fsm_atr_thb:
+ if (sim->atrparser.num_hb) {
+ sim->state = SIM_STATE_DETECTED_ATR_THB;
+ sim->atrparser.cnt = 0;
+ } else {
+ goto sim_fsm_atr_tck;
+ };
+ };
+ };
+ break;
+
+ case SIM_STATE_DETECTED_ATR_THB:
+ sim_atr_add(sim, data);
+ pr_debug("THB%i %02x\n", i, data);
+ sim->atrparser.THB[sim->atrparser.cnt] = data;
+ sim->atrparser.cnt++;
+
+ if (sim->atrparser.cnt == sim->atrparser.num_hb) {
+sim_fsm_atr_tck:
+ i = sim->atrparser.ifc_valid & (1 << (SIM_IFC_TD1));
+ temp = sim->atrparser.TXI[SIM_IFC_TD1] & 0x0f;
+ if ((i && temp) == SIM_PROTOCOL_T1)
+ sim->state = SIM_STATE_DETECTED_ATR_TCK;
+ else
+ goto sim_fsm_atr_received;
+ };
+ break;
+
+ case SIM_STATE_DETECTED_ATR_TCK:
+ sim_atr_add(sim, data);
+ /* checksum not required for T=0 */
+ sim->atrparser.TCK = data;
+sim_fsm_atr_received:
+ sim->state = SIM_STATE_OPERATIONAL_IDLE;
+ sim->present = SIM_PRESENT_OPERATIONAL;
+ sim_atr_received(sim);
+ break;
+ };
+};
+
+/* Function: sim_irq_handler
+ *
+ * Description: interrupt service routine.
+ *
+ * Parameters:
+ * int irq interrupt number
+ * void *dev_id pointer to SIM device handler
+ *
+ * Return values:
+ * IRQ_HANDLED OS specific
+ */
+
+static irqreturn_t sim_irq_handler(int irq, void *dev_id)
+{
+ uint32_t reg_data, reg_data0, reg_data1;
+
+ sim_t *sim = (sim_t *) dev_id;
+
+ pr_debug("%s entering\n", __func__);
+
+ reg_data0 = __raw_readl(sim->ioaddr + XMT_STATUS);
+ reg_data1 = __raw_readl(sim->ioaddr + INT_MASK);
+ if ((reg_data0 & SIM_XMT_STATUS_TC)
+ && (!(reg_data1 & SIM_INT_MASK_TCIM))) {
+ pr_debug("TC_IRQ\n");
+ __raw_writel(SIM_XMT_STATUS_TC, sim->ioaddr + XMT_STATUS);
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data |= SIM_INT_MASK_TCIM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ reg_data = __raw_readl(sim->ioaddr + ENABLE);
+ reg_data &= ~SIM_ENABLE_XMTEN;
+ __raw_writel(reg_data, sim->ioaddr + ENABLE);
+ };
+
+ reg_data0 = __raw_readl(sim->ioaddr + XMT_STATUS);
+ reg_data1 = __raw_readl(sim->ioaddr + INT_MASK);
+ if ((reg_data0 & SIM_XMT_STATUS_TDTF)
+ && (!(reg_data1 & SIM_INT_MASK_TDTFM))) {
+ pr_debug("TDTF_IRQ\n");
+ __raw_writel(SIM_XMT_STATUS_TDTF, sim->ioaddr + XMT_STATUS);
+ sim_xmt_fill(sim);
+
+ if (sim->xmt_remaining == 0) {
+ __raw_writel(SIM_XMT_STATUS_TC,
+ sim->ioaddr + XMT_STATUS);
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data &= ~SIM_INT_MASK_TCIM;
+ reg_data |= SIM_INT_MASK_TDTFM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ };
+ };
+
+ reg_data0 = __raw_readl(sim->ioaddr + RCV_STATUS);
+ reg_data1 = __raw_readl(sim->ioaddr + INT_MASK);
+ if ((reg_data0 & SIM_RCV_STATUS_RDRF)
+ && (!(reg_data1 & SIM_INT_MASK_RIM))) {
+ pr_debug("%s RDRF_IRQ\n", __func__);
+ __raw_writel(SIM_RCV_STATUS_RDRF, sim->ioaddr + RCV_STATUS);
+
+ while (__raw_readl(sim->ioaddr + RCV_FIFO_CNT)) {
+ uint32_t data;
+ data = __raw_readl(sim->ioaddr + PORT1_RCV_BUF);
+ pr_debug("RX = %02x state = %i\n", data, sim->state);
+ if (data & 0x700) {
+ if (sim->xfer_ongoing) {
+ /* error */
+ printk(KERN_ERR "ERROR !\n");
+ return IRQ_HANDLED;
+ };
+ } else
+ sim_fsm(sim, data);
+ };
+ };
+
+ reg_data0 = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ if (reg_data0 & SIM_PORT_DETECT_SDI) {
+ pr_debug("%s PD_IRQ\n", __func__);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data |= SIM_PORT_DETECT_SDI;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+
+ reg_data0 = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ if (reg_data0 & SIM_PORT_DETECT_SPDP) {
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data &= ~SIM_PORT_DETECT_SPDS;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+
+ if (sim->present != SIM_PRESENT_REMOVED) {
+ pr_debug("Removed sim card\n");
+ sim->present = SIM_PRESENT_REMOVED;
+ sim->state = SIM_STATE_REMOVED;
+
+ if (sim->fasync)
+ kill_fasync(&sim->fasync,
+ SIGIO, POLL_IN);
+ };
+ } else {
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data |= SIM_PORT_DETECT_SPDS;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+
+ if (sim->present == SIM_PRESENT_REMOVED) {
+ pr_debug("Inserted sim card\n");
+ sim->state = SIM_STATE_DETECTED_ATR_T0;
+ sim->present = SIM_PRESENT_DETECTED;
+
+ if (sim->fasync)
+ kill_fasync(&sim->fasync,
+ SIGIO, POLL_IN);
+ };
+ };
+ };
+
+ return IRQ_HANDLED;
+};
+
+/* Function: sim_power_on
+ *
+ * Description: run the power on sequence
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_power_on(sim_t *sim)
+{
+ uint32_t reg_data;
+
+ /* power on sequence */
+ pr_debug("%s Powering on the sim port.\n", __func__);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data |= SIM_PORT_CNTL_SVEN;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ msleep(10);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data |= SIM_PORT_CNTL_SCEN;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ msleep(10);
+ reg_data = SIM_RCV_THRESHOLD_RTH(0) | SIM_RCV_THRESHOLD_RDT(1);
+ __raw_writel(reg_data, sim->ioaddr + RCV_THRESHOLD);
+ __raw_writel(SIM_RCV_STATUS_RDRF, sim->ioaddr + RCV_STATUS);
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data &= ~SIM_INT_MASK_RIM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ __raw_writel(31, sim->ioaddr + DIVISOR);
+ reg_data = __raw_readl(sim->ioaddr + CNTL);
+ reg_data |= SIM_CNTL_SAMPLE12;
+ __raw_writel(reg_data, sim->ioaddr + CNTL);
+ reg_data = __raw_readl(sim->ioaddr + ENABLE);
+ reg_data |= SIM_ENABLE_RCVEN;
+ __raw_writel(reg_data, sim->ioaddr + ENABLE);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data |= SIM_PORT_CNTL_SRST;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ pr_debug("%s port0_ctl is 0x%x.\n", __func__,
+ __raw_readl(sim->ioaddr + PORT0_CNTL));
+ sim->power = SIM_POWER_ON;
+};
+
+/* Function: sim_power_off
+ *
+ * Description: run the power off sequence
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_power_off(sim_t *sim)
+{
+ uint32_t reg_data;
+
+ pr_debug("%s entering.\n", __func__);
+ /* sim_power_off sequence */
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data &= ~SIM_PORT_CNTL_SCEN;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ reg_data = __raw_readl(sim->ioaddr + ENABLE);
+ reg_data &= ~SIM_ENABLE_RCVEN;
+ __raw_writel(reg_data, sim->ioaddr + ENABLE);
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data |= SIM_INT_MASK_RIM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ __raw_writel(0, sim->ioaddr + RCV_THRESHOLD);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data &= ~SIM_PORT_CNTL_SRST;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data &= ~SIM_PORT_CNTL_SVEN;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ sim->power = SIM_POWER_OFF;
+};
+
+/* Function: sim_start
+ *
+ * Description: ramp up the SIM interface
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_start(sim_t *sim)
+{
+ uint32_t reg_data, clk_rate, clk_div = 0;
+
+ pr_debug("%s entering.\n", __func__);
+ /* Configuring SIM for Operation */
+ reg_data = SIM_XMT_THRESHOLD_XTH(0) | SIM_XMT_THRESHOLD_TDT(4);
+ __raw_writel(reg_data, sim->ioaddr + XMT_THRESHOLD);
+ __raw_writel(0, sim->ioaddr + SETUP);
+ /* ~ 4 MHz */
+ clk_rate = clk_get_rate(sim->clk);
+ clk_div = clk_rate / sim->plat_data->clk_rate;
+ if (clk_rate % sim->plat_data->clk_rate)
+ clk_div++;
+ pr_debug("%s prescaler is 0x%x.\n", __func__, clk_div);
+ __raw_writel(clk_div, sim->ioaddr + CLK_PRESCALER);
+
+ reg_data = SIM_CNTL_GPCNT_CLK_SEL(0) | SIM_CNTL_BAUD_SEL(7)
+ | SIM_CNTL_SAMPLE12 | SIM_CNTL_ANACK | SIM_CNTL_ICM;
+ __raw_writel(reg_data, sim->ioaddr + CNTL);
+ __raw_writel(31, sim->ioaddr + DIVISOR);
+ reg_data = __raw_readl(sim->ioaddr + OD_CONFIG);
+ reg_data |= SIM_OD_CONFIG_OD_P0;
+ __raw_writel(reg_data, sim->ioaddr + OD_CONFIG);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data |= SIM_PORT_CNTL_3VOLT | SIM_PORT_CNTL_STEN;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+
+ /* presense detect */
+ pr_debug("%s p0_det is 0x%x \n", __func__,
+ __raw_readl(sim->ioaddr + PORT0_DETECT));
+ if (__raw_readl(sim->ioaddr + PORT0_DETECT) & SIM_PORT_DETECT_SPDP) {
+ pr_debug("%s card removed \n", __func__);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data &= ~SIM_PORT_DETECT_SPDS;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+ sim->present = SIM_PRESENT_REMOVED;
+ sim->state = SIM_STATE_REMOVED;
+ } else {
+ pr_debug("%s card inserted \n", __func__);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data |= SIM_PORT_DETECT_SPDS;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+ sim->present = SIM_PRESENT_DETECTED;
+ sim->state = SIM_STATE_DETECTED_ATR_T0;
+ };
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data |= SIM_PORT_DETECT_SDI;
+ reg_data &= ~SIM_PORT_DETECT_SDIM;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+
+ /*
+ * Since there is no PD0 layout on MX51, assume
+ * that there is a SIM card in slot defaulty.
+ * */
+ if (0 == (sim->plat_data->detect)) {
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data |= SIM_PORT_DETECT_SPDS;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+ sim->present = SIM_PRESENT_DETECTED;
+ sim->state = SIM_STATE_DETECTED_ATR_T0;
+ }
+
+ if (sim->present == SIM_PRESENT_DETECTED)
+ sim_power_on(sim);
+
+};
+
+/* Function: sim_stop
+ *
+ * Description: shut down the SIM interface
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_stop(sim_t *sim)
+{
+ pr_debug("%s entering.\n", __func__);
+ __raw_writel(0, sim->ioaddr + SETUP);
+ __raw_writel(0, sim->ioaddr + ENABLE);
+ __raw_writel(0, sim->ioaddr + PORT0_CNTL);
+ __raw_writel(0x06, sim->ioaddr + CNTL);
+ __raw_writel(0, sim->ioaddr + CLK_PRESCALER);
+ __raw_writel(0, sim->ioaddr + SETUP);
+ __raw_writel(0, sim->ioaddr + OD_CONFIG);
+ __raw_writel(0, sim->ioaddr + XMT_THRESHOLD);
+ __raw_writel(0xb8, sim->ioaddr + XMT_STATUS);
+ __raw_writel(4, sim->ioaddr + RESET_CNTL);
+ mdelay(1);
+};
+
+/* Function: sim_data_reset
+ *
+ * Description: reset a SIM structure to default values
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_data_reset(sim_t *sim)
+{
+ sim_param_t param_default = SIM_PARAM_DEFAULT;
+ sim->present = SIM_PRESENT_REMOVED;
+ sim->state = SIM_STATE_REMOVED;
+ sim->power = SIM_POWER_OFF;
+ sim->errval = SIM_OK;
+ memset(&sim->atrparser, 0, sizeof(sim->atrparser));
+ memset(&sim->atr, 0, sizeof(sim->atr));
+ sim->param_atr = param_default;
+ memset(&sim->param, 0, sizeof(sim->param));
+ memset(&sim->xfer, 0, sizeof(sim->xfer));
+ sim->xfer_ongoing = 0;
+ sim->xmt_remaining = 0;
+ sim->xmt_pos = 0;
+ sim->rcv_count = 0;
+ memset(sim->rcv_buffer, 0, SIM_RCV_BUFFER_SIZE);
+ memset(sim->xmt_buffer, 0, SIM_XMT_BUFFER_SIZE);
+};
+
+/* Function: sim_cold_reset
+ *
+ * Description: cold reset the SIM interface, including card
+ * power down and interface hardware reset.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_cold_reset(sim_t *sim)
+{
+ pr_debug("%s entering.\n", __func__);
+ if (sim->present != SIM_PRESENT_REMOVED) {
+ sim_power_off(sim);
+ sim_stop(sim);
+ sim_data_reset(sim);
+ sim->state = SIM_STATE_DETECTED_ATR_T0;
+ sim->present = SIM_PRESENT_DETECTED;
+ msleep(50);
+ sim_start(sim);
+ sim_power_on(sim);
+ };
+};
+
+/* Function: sim_warm_reset
+ *
+ * Description: warm reset the SIM interface: just invoke the
+ * reset signal and reset the SIM structure for the interface.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_warm_reset(sim_t *sim)
+{
+ uint32_t reg_data;
+
+ pr_debug("%s entering.\n", __func__);
+ if (sim->present != SIM_PRESENT_REMOVED) {
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data |= SIM_PORT_CNTL_SRST;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ sim_data_reset(sim);
+ msleep(50);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data &= ~SIM_PORT_CNTL_SRST;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ };
+};
+
+/* Function: sim_card_lock
+ *
+ * Description: physically lock the SIM card.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static int sim_card_lock(sim_t *sim)
+{
+ int errval;
+
+ pr_debug("%s entering.\n", __func__);
+ /* place holder for true physcial locking */
+ if (sim->present != SIM_PRESENT_REMOVED)
+ errval = SIM_OK;
+ else
+ errval = -SIM_E_NOCARD;
+ return errval;
+};
+
+/* Function: sim_card_eject
+ *
+ * Description: physically unlock and eject the SIM card.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static int sim_card_eject(sim_t *sim)
+{
+ int errval;
+
+ pr_debug("%s entering.\n", __func__);
+ /* place holder for true physcial locking */
+ if (sim->present != SIM_PRESENT_REMOVED)
+ errval = SIM_OK;
+ else
+ errval = -SIM_E_NOCARD;
+ return errval;
+};
+
+/* Function: sim_ioctl
+ *
+ * Description: handle ioctl calls
+ *
+ * Parameters: OS specific
+ */
+
+static int sim_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret, errval = SIM_OK;
+ unsigned long timeout;
+
+ sim_t *sim = (sim_t *) file->private_data;
+
+ pr_debug("%s entering.\n", __func__);
+ switch (cmd) {
+ pr_debug("ioctl cmd %d is issued...\n", cmd);
+
+ case SIM_IOCTL_GET_ATR:
+ if (sim->present != SIM_PRESENT_OPERATIONAL) {
+ errval = -SIM_E_NOCARD;
+ break;
+ };
+ ret = copy_to_user((sim_atr_t *) arg, &sim->atr,
+ sizeof(sim_atr_t));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_GET_PARAM_ATR:
+ if (sim->present != SIM_PRESENT_OPERATIONAL) {
+ errval = -SIM_E_NOCARD;
+ break;
+ };
+ ret = copy_to_user((sim_param_t *) arg, &sim->param_atr,
+ sizeof(sim_param_t));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_GET_PARAM:
+ ret = copy_to_user((sim_param_t *) arg, &sim->param,
+ sizeof(sim_param_t));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_SET_PARAM:
+ ret = copy_from_user(&sim->param, (sim_param_t *) arg,
+ sizeof(sim_param_t));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ else
+ errval = sim_set_param(sim, &sim->param);
+ break;
+
+ case SIM_IOCTL_POWER_ON:
+ if (sim->power == SIM_POWER_ON) {
+ errval = -SIM_E_POWERED_ON;
+ break;
+ };
+ sim_power_on(sim);
+ break;
+
+ case SIM_IOCTL_POWER_OFF:
+ if (sim->power == SIM_POWER_OFF) {
+ errval = -SIM_E_POWERED_OFF;
+ break;
+ };
+ sim_power_off(sim);
+ break;
+
+ case SIM_IOCTL_COLD_RESET:
+ if (sim->power == SIM_POWER_OFF) {
+ errval = -SIM_E_POWERED_OFF;
+ break;
+ };
+ sim_cold_reset(sim);
+ break;
+
+ case SIM_IOCTL_WARM_RESET:
+ sim_warm_reset(sim);
+ if (sim->power == SIM_POWER_OFF) {
+ errval = -SIM_E_POWERED_OFF;
+ break;
+ };
+ break;
+
+ case SIM_IOCTL_XFER:
+ if (sim->present != SIM_PRESENT_OPERATIONAL) {
+ errval = -SIM_E_NOCARD;
+ break;
+ };
+
+ ret = copy_from_user(&sim->xfer, (sim_xfer_t *) arg,
+ sizeof(sim_xfer_t));
+ if (ret) {
+ errval = -SIM_E_ACCESS;
+ break;
+ };
+
+ ret = copy_from_user(sim->xmt_buffer, sim->xfer.xmt_buffer,
+ sim->xfer.xmt_length);
+ if (ret) {
+ errval = -SIM_E_ACCESS;
+ break;
+ };
+
+ sim->rcv_count = 0;
+ sim->xfer.sw1 = 0;
+ sim->xfer.sw2 = 0;
+
+ if (sim->xfer.type == SIM_XFER_TYPE_TPDU) {
+ if (sim->xfer.xmt_length < 5) {
+ errval = -SIM_E_TPDUSHORT;
+ break;
+ }
+ sim->state = SIM_STATE_OPERATIONAL_COMMAND;
+ } else if (sim->xfer.type == SIM_XFER_TYPE_PTS) {
+ if (sim->xfer.xmt_length == 0) {
+ errval = -SIM_E_PTSEMPTY;
+ break;
+ }
+ sim->state = SIM_STATE_OPERATIONAL_PTS;
+ } else {
+ errval = -SIM_E_INVALIDXFERTYPE;
+ break;
+ };
+
+ if (sim->xfer.xmt_length > SIM_XMT_BUFFER_SIZE) {
+ errval = -SIM_E_INVALIDXMTLENGTH;
+ break;
+ };
+
+ if (sim->xfer.rcv_length > SIM_XMT_BUFFER_SIZE) {
+ errval = -SIM_E_INVALIDRCVLENGTH;
+ break;
+ };
+
+ sim->errval = 0;
+ sim->xfer_ongoing = 1;
+ init_completion(&sim->xfer_done);
+ sim_xmt_start(sim, 0, 5);
+ timeout =
+ wait_for_completion_interruptible_timeout(&sim->xfer_done,
+ sim->xfer.
+ timeout);
+ sim->xfer_ongoing = 0;
+
+ if (sim->errval) {
+ errval = sim->errval;
+ break;
+ };
+
+ if (timeout == 0) {
+ errval = -SIM_E_TIMEOUT;
+ break;
+ }
+
+ ret = copy_to_user(sim->xfer.rcv_buffer, sim->rcv_buffer,
+ sim->xfer.rcv_length);
+ if (ret) {
+ errval = -SIM_E_ACCESS;
+ break;
+ };
+
+ ret = copy_to_user((sim_xfer_t *) arg, &sim->xfer,
+ sizeof(sim_xfer_t));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_GET_PRESENSE:
+ if (put_user(sim->present, (int *)arg))
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_CARD_LOCK:
+ errval = sim_card_lock(sim);
+ break;
+
+ case SIM_IOCTL_CARD_EJECT:
+ errval = sim_card_eject(sim);
+ break;
+
+ };
+
+ return errval;
+};
+
+/* Function: sim_fasync
+ *
+ * Description: async handler
+ *
+ * Parameters: OS specific
+ */
+
+static int sim_fasync(int fd, struct file *file, int mode)
+{
+ sim_t *sim = (sim_t *) file->private_data;
+ pr_debug("%s entering.\n", __func__);
+ return fasync_helper(fd, file, mode, &sim->fasync);
+}
+
+/* Function: sim_open
+ *
+ * Description: ramp up interface when being opened
+ *
+ * Parameters: OS specific
+ */
+
+static int sim_open(struct inode *inode, struct file *file)
+{
+ int errval = SIM_OK;
+
+ sim_t *sim = dev_get_drvdata(sim_dev.parent);
+ file->private_data = sim;
+
+ pr_debug("%s entering.\n", __func__);
+ if (!sim->ioaddr) {
+ errval = -ENOMEM;
+ return errval;
+ }
+
+ if (!(sim->clk_flag)) {
+ pr_debug("\n%s enable the clock\n", __func__);
+ clk_enable(sim->clk);
+ sim->clk_flag = 1;
+ }
+
+ sim_start(sim);
+
+ return errval;
+};
+
+/* Function: sim_release
+ *
+ * Description: shut down interface when being closed
+ *
+ * Parameters: OS specific
+ */
+
+static int sim_release(struct inode *inode, struct file *file)
+{
+ uint32_t reg_data;
+
+ sim_t *sim = (sim_t *) file->private_data;
+
+ pr_debug("%s entering.\n", __func__);
+ if (sim->clk_flag) {
+ pr_debug("\n%s disable the clock\n", __func__);
+ clk_disable(sim->clk);
+ sim->clk_flag = 0;
+ }
+
+ /* disable presense detection */
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ __raw_writel(reg_data | SIM_PORT_DETECT_SDIM,
+ sim->ioaddr + PORT0_DETECT);
+
+ if (sim->present != SIM_PRESENT_REMOVED) {
+ sim_power_off(sim);
+ if (sim->fasync)
+ kill_fasync(&sim->fasync, SIGIO, POLL_IN);
+ };
+
+ sim_stop(sim);
+
+ sim_fasync(-1, file, 0);
+
+ pr_debug("exit\n");
+ return 0;
+};
+
+static const struct file_operations sim_fops = {
+ .open = sim_open,
+ .ioctl = sim_ioctl,
+ .fasync = sim_fasync,
+ .release = sim_release
+};
+
+static struct miscdevice sim_dev = {
+ MISC_DYNAMIC_MINOR,
+ "mxc_sim",
+ &sim_fops
+};
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int sim_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mxc_sim_platform_data *sim_plat = pdev->dev.platform_data;
+
+ sim_t *sim = kzalloc(sizeof(sim_t), GFP_KERNEL);
+
+ if (sim == 0) {
+ ret = -ENOMEM;
+ printk(KERN_ERR "Can't get the MEMORY\n");
+ return ret;
+ };
+
+ BUG_ON(pdev == NULL);
+
+ sim->plat_data = sim_plat;
+ sim->clk_flag = 0;
+
+ sim->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!sim->res) {
+ ret = -ENOMEM;
+ printk(KERN_ERR "Can't get the MEMORY\n");
+ goto out;
+ }
+
+ /* request the sim clk and sim_serial_clk */
+ sim->clk = clk_get(NULL, sim->plat_data->clock_sim);
+ if (IS_ERR(sim->clk)) {
+ ret = PTR_ERR(sim->clk);
+ printk(KERN_ERR "Get CLK ERROR !\n");
+ goto out;
+ }
+ pr_debug("sim clock:%lu\n", clk_get_rate(sim->clk));
+
+ sim->ipb_irq = platform_get_irq(pdev, 0);
+ sim->dat_irq = platform_get_irq(pdev, 1);
+ if (!(sim->ipb_irq | sim->dat_irq)) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ if (!request_mem_region(sim->res->start,
+ sim->res->end -
+ sim->res->start + 1, pdev->name)) {
+ printk(KERN_ERR "request_mem_region failed\n");
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ sim->ioaddr = (void *)ioremap(sim->res->start, sim->res->end -
+ sim->res->start + 1);
+ if (sim->ipb_irq)
+ ret = request_irq(sim->ipb_irq, sim_irq_handler,
+ 0, "mxc_sim_ipb", sim);
+ if (sim->dat_irq)
+ ret |= request_irq(sim->dat_irq, sim_irq_handler,
+ 0, "mxc_sim_dat", sim);
+
+ if (ret) {
+ printk(KERN_ERR "Can't get the irq\n");
+ goto out2;
+ };
+
+ platform_set_drvdata(pdev, sim);
+ sim_dev.parent = &(pdev->dev);
+
+ misc_register(&sim_dev);
+
+ return ret;
+out2:
+ if (sim->ipb_irq)
+ free_irq(sim->ipb_irq, sim);
+ if (sim->dat_irq)
+ free_irq(sim->dat_irq, sim);
+ release_mem_region(sim->res->start,
+ sim->res->end - sim->res->start + 1);
+out1:
+ clk_put(sim->clk);
+out:
+ kfree(sim);
+ return ret;
+}
+
+static int sim_remove(struct platform_device *pdev)
+{
+ sim_t *sim = platform_get_drvdata(pdev);
+
+ clk_put(sim->clk);
+
+ if (sim->ipb_irq)
+ free_irq(sim->ipb_irq, sim);
+ if (sim->dat_irq)
+ free_irq(sim->dat_irq, sim);
+
+ iounmap(sim->ioaddr);
+
+ kfree(sim);
+ release_mem_region(sim->res->start,
+ sim->res->end - sim->res->start + 1);
+
+ misc_deregister(&sim_dev);
+ return 0;
+}
+
+static struct platform_driver sim_driver = {
+ .driver = {
+ .name = "mxc_sim",
+ },
+ .probe = sim_probe,
+ .remove = sim_remove,
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static int __init sim_drv_init(void)
+{
+ return platform_driver_register(&sim_driver);
+}
+
+static void __exit sim_drv_exit(void)
+{
+ platform_driver_unregister(&sim_driver);
+}
+
+module_init(sim_drv_init);
+module_exit(sim_drv_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC SIM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index de26a978fbdd..5e34577309b7 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -1123,7 +1123,9 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode,
#define HW_RAW(dev) 0
+#if defined CONFIG_INPUT_KEYBOARD
#warning "Cannot generate rawmode keyboard for your architecture yet."
+#endif
static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char up_flag)
{
diff --git a/drivers/char/mxc_iim.c b/drivers/char/mxc_iim.c
new file mode 100755
index 000000000000..b407d34759a9
--- /dev/null
+++ b/drivers/char/mxc_iim.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/clk.h>
+#include <linux/miscdevice.h>
+
+static unsigned long iim_reg_base, iim_reg_end, iim_reg_size;
+static struct clk *iim_clk;
+static struct device *iim_dev;
+
+/*!
+ * MXC IIM interface - memory map function
+ * This function maps 4KB IIM registers from IIM base address.
+ *
+ * @param file struct file *
+ * @param vma structure vm_area_struct *
+ *
+ * @return Return 0 on success or negative error code on error
+ */
+static int mxc_iim_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
+ if (remap_pfn_range(vma,
+ vma->vm_start,
+ iim_reg_base >> PAGE_SHIFT,
+ iim_reg_size,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
+/*!
+ * MXC IIM interface - open function
+ *
+ * @param inode struct inode *
+ * @param filp struct file *
+ *
+ * @return Return 0 on success or negative error code on error
+ */
+static int mxc_iim_open(struct inode *inode, struct file *filp)
+{
+ iim_clk = clk_get(NULL, "iim_clk");
+ if (IS_ERR(iim_clk)) {
+ dev_err(iim_dev, "No IIM clock defined\n");
+ return -ENODEV;
+ }
+ clk_enable(iim_clk);
+
+ return 0;
+}
+
+/*!
+ * MXC IIM interface - release function
+ *
+ * @param inode struct inode *
+ * @param filp struct file *
+ *
+ * @return Return 0 on success or negative error code on error
+ */
+static int mxc_iim_release(struct inode *inode, struct file *filp)
+{
+ clk_disable(iim_clk);
+ clk_put(iim_clk);
+ return 0;
+}
+
+static const struct file_operations mxc_iim_fops = {
+ .mmap = mxc_iim_mmap,
+ .open = mxc_iim_open,
+ .release = mxc_iim_release,
+};
+
+static struct miscdevice mxc_iim_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mxc_iim",
+ .fops = &mxc_iim_fops,
+};
+
+/*!
+ * This function is called by the driver framework to get iim base/end address
+ * and register iim misc device.
+ *
+ * @param dev The device structure for IIM passed in by the driver
+ * framework.
+ *
+ * @return Returns 0 on success or negative error code on error
+ */
+static int mxc_iim_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ iim_dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res)) {
+ dev_err(iim_dev, "Unable to get IIM resource\n");
+ return -ENODEV;
+ }
+
+ iim_reg_base = res->start;
+ iim_reg_end = res->end;
+ iim_reg_size = iim_reg_end - iim_reg_base + 1;
+
+ ret = misc_register(&mxc_iim_miscdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mxc_iim_remove(struct platform_device *pdev)
+{
+ misc_deregister(&mxc_iim_miscdev);
+ return 0;
+}
+
+static struct platform_driver mxc_iim_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mxc_iim",
+ },
+ .probe = mxc_iim_probe,
+ .remove = mxc_iim_remove,
+};
+
+static int __init mxc_iim_dev_init(void)
+{
+ return platform_driver_register(&mxc_iim_driver);
+}
+
+static void __exit mxc_iim_dev_cleanup(void)
+{
+ platform_driver_unregister(&mxc_iim_driver);
+}
+
+module_init(mxc_iim_dev_init);
+module_exit(mxc_iim_dev_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC IIM driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);
diff --git a/drivers/char/mxc_si4702.c b/drivers/char/mxc_si4702.c
new file mode 100644
index 000000000000..b4b0809e458f
--- /dev/null
+++ b/drivers/char/mxc_si4702.c
@@ -0,0 +1,1220 @@
+/*
+ * linux/drivers/char/mxc_si4702.c
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/cdev.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/regulator/consumer.h>
+#include <asm/uaccess.h>
+#include <linux/err.h>
+#include <linux/mxc_si4702.h>
+
+#define SI4702_DEV_NAME "si4702"
+#define DEV_MAJOR 0 /* this could be module param */
+#define DEV_MINOR 0
+#define DEV_BASE_MINOR 0
+#define DEV_MINOR_COUNT 256
+#define SI4702_I2C_ADDR 0x10 /* 7bits I2C address */
+#define DELAY_WAIT 0xffff /* loop_counter max value */
+/* register define */
+#define SI4702_DEVICEID 0x00
+#define SI4702_CHIPID 0x01
+#define SI4702_POWERCFG 0x02
+#define SI4702_CHANNEL 0x03
+#define SI4702_SYSCONFIG1 0x04
+#define SI4702_SYSCONFIG2 0x05
+#define SI4702_SYSCONFIG3 0x06
+#define SI4702_TEST1 0x07
+#define SI4702_TEST2 0x08
+#define SI4702_B00TCONFIG 0x09
+#define SI4702_STATUSRSSI 0x0A
+#define SI4702_READCHAN 0x0B
+#define SI4702_REG_NUM 0x10
+#define SI4702_REG_BYTE (SI4702_REG_NUM * 2)
+#define SI4702_DEVICE_ID 0x1242
+#define SI4702_RW_REG_NUM (SI4702_STATUSRSSI - SI4702_POWERCFG)
+#define SI4702_RW_OFFSET \
+ (SI4702_REG_NUM - SI4702_STATUSRSSI + SI4702_POWERCFG)
+
+#define SI4702_SPACE_MASK 0x0030
+#define SI4702_SPACE_200K 0x0
+#define SI4702_SPACE_100K 0x10
+#define SI4702_SPACE_50K 0x20
+
+#define SI4702_BAND_MASK 0x00c0
+#define SI4702_BAND_LSB 6
+
+#define SI4702_SEEKTH_MASK 0xff00
+#define SI4702_SEEKTH_LSB 8
+
+#define SI4702_SNR_MASK 0x00f0
+#define SI4702_SNR_LSB 4
+
+#define SI4702_CNT_MASK 0x000f
+#define SI4702_CNT_LSB 0
+
+#define SI4702_VOL_MASK 0x000f
+#define SI4702_VOL_LSB 0
+
+#define SI4702_CHAN_MASK 0x03ff
+#define SI4702_TUNE_BIT 0x8000
+#define SI4702_STC_BIT 0x4000
+#define SI4702_DMUTE_BIT 0x4000
+#define SI4702_SEEKUP_BIT 0x0200
+#define SI4702_SEEK_BIT 0x0100
+#define SI4702_SF_BIT 0x2000
+#define SI4702_ENABLE_BIT 0x0001
+#define SI4702_DISABLE_BIT 0x0040
+
+enum {
+ BAND_USA = 0,
+ BAND_JAP_W,
+ BAND_JAP
+};
+
+struct si4702_info {
+ int min_band;
+ int max_band;
+ int space;
+ int volume;
+ int channel;
+ int mute;
+};
+
+struct si4702_drvdata {
+ struct regulator *vio;
+ struct regulator *vdd;
+ struct class *radio_class;
+ struct si4702_info info;
+ /*by default, dev major is zero, and it's alloc dynamicaly. */
+ int major;
+ int minor;
+ struct cdev *cdev;
+ int count; /* open count */
+ struct i2c_client *client;
+ unsigned char reg_rw_buf[SI4702_REG_BYTE];
+ struct mxc_fm_platform_data *plat_data;
+};
+
+static struct si4702_drvdata *si4702_drvdata;
+
+DEFINE_SPINLOCK(count_lock);
+
+#ifdef DEBUG
+static void si4702_dump_reg(void)
+{
+ int i, j;
+ unsigned char *reg_rw_buf;
+
+ if (NULL == si4702_drvdata)
+ return;
+
+ reg_rw_buf = si4702_drvdata->reg_rw_buf;
+
+ for (i = 0; i < 10; i++) {
+ j = i * 2 + 12;
+ pr_debug("reg[%02d] = %04x\n", i,
+ ((reg_rw_buf[j] << 8) & 0xFF00) +
+ (reg_rw_buf[j + 1] & 0x00FF));
+ }
+ for (; i < 16; i++) {
+ j = (i - 10) * 2;
+ pr_debug("reg[%02d] = %04x\n", i,
+ ((reg_rw_buf[j] << 8) & 0xFF00) +
+ (reg_rw_buf[j + 1] & 0x00FF));
+ }
+}
+#else
+static void si4702_dump_reg(void)
+{
+}
+#endif /* DEBUG */
+
+/*
+ *check the si4702 spec for the read/write concequence.
+ *
+ *0 2 A F0 A F
+ *-------------------------------
+ * buf:0 2 A F
+ */
+#define REG_to_BUF(reg) (((reg >= 0) && (reg < SI4702_STATUSRSSI))?\
+ (reg - SI4702_STATUSRSSI + SI4702_REG_NUM):\
+ ((reg >= SI4702_STATUSRSSI) && (reg < SI4702_REG_NUM))?\
+ (reg - SI4702_STATUSRSSI) : -1)
+
+static int si4702_read_reg(const int reg, u16 *value)
+{
+ int ret, index;
+ unsigned char *reg_rw_buf;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ reg_rw_buf = si4702_drvdata->reg_rw_buf;
+
+ index = REG_to_BUF(reg);
+
+ if (-1 == index)
+ return -1;
+
+ ret =
+ i2c_master_recv(si4702_drvdata->client, reg_rw_buf,
+ SI4702_REG_BYTE);
+
+ *value = (reg_rw_buf[index * 2] << 8) & 0xFF00;
+ *value |= reg_rw_buf[index * 2 + 1] & 0x00FF;
+
+ return ret < 0 ? ret : 0;
+}
+
+static int si4702_write_reg(const int reg, const u16 value)
+{
+ int index, ret;
+ unsigned char *reg_rw_buf;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ reg_rw_buf = si4702_drvdata->reg_rw_buf;
+
+ index = REG_to_BUF(reg);
+
+ if (-1 == index)
+ return -1;
+
+ reg_rw_buf[index * 2] = (value & 0xFF00) >> 8;
+ reg_rw_buf[index * 2 + 1] = value & 0x00FF;
+
+ ret = i2c_master_send(si4702_drvdata->client,
+ &reg_rw_buf[SI4702_RW_OFFSET * 2],
+ (SI4702_STATUSRSSI - SI4702_POWERCFG) * 2);
+ return ret < 0 ? ret : 0;
+}
+
+static void si4702_gpio_get(void)
+{
+ if (NULL == si4702_drvdata)
+ return;
+
+ si4702_drvdata->plat_data->gpio_get();
+}
+
+static void si4702_gpio_put(void)
+{
+ if (NULL == si4702_drvdata)
+ return;
+
+ si4702_drvdata->plat_data->gpio_put();
+}
+
+static void si4702_reset(void)
+{
+ if (NULL == si4702_drvdata)
+ return;
+
+ si4702_drvdata->plat_data->reset();
+}
+
+static void si4702_clock_en(int flag)
+{
+ if (NULL == si4702_drvdata)
+ return;
+
+ si4702_drvdata->plat_data->clock_ctl(flag);
+}
+
+static int si4702_id_detect(struct i2c_client *client)
+{
+ int ret, index;
+ unsigned int ID = 0;
+ unsigned char reg_rw_buf[SI4702_REG_BYTE];
+
+ si4702_gpio_get();
+ si4702_reset();
+ si4702_clock_en(1);
+
+ ret = i2c_master_recv(client, (char *)reg_rw_buf, SI4702_REG_BYTE);
+
+ si4702_gpio_put();
+
+ if (ret < 0)
+ return ret;
+
+ index = REG_to_BUF(SI4702_DEVICEID);
+ if (index < 0)
+ return index;
+
+ ID = (reg_rw_buf[index * 2] << 8) & 0xFF00;
+ ID |= reg_rw_buf[index * 2 + 1] & 0x00FF;
+
+ return ID;
+}
+
+/* valid args 50/100/200 */
+static int si4702_set_space(int space)
+{
+ u16 reg;
+ int ret;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG2, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg &= ~SI4702_SPACE_MASK;
+ switch (space) {
+ case 50:
+ reg |= SI4702_SPACE_50K;
+ break;
+ case 100:
+ reg |= SI4702_SPACE_100K;
+ break;
+ case 200:
+ ret |= SI4702_SPACE_200K;
+ break;
+ default:
+ return -1;
+ }
+
+ ret = si4702_write_reg(SI4702_SYSCONFIG2, reg);
+ if (ret == -1)
+ return ret;
+
+ info = &si4702_drvdata->info;
+ info->space = space;
+ return 0;
+}
+
+static int si4702_set_band_range(int band)
+{
+ u16 reg;
+ int ret, band_min, band_max;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ switch (band) {
+ case BAND_USA:
+ band_min = 87500;
+ band_max = 108000;
+ break;
+ case BAND_JAP_W:
+ band_min = 76000;
+ band_max = 108000;
+ break;
+ case BAND_JAP:
+ band_min = 76000;
+ band_max = 90000;
+ break;
+ default:
+ return -1;
+ }
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG2, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg = (reg & ~SI4702_BAND_MASK)
+ | ((band << SI4702_BAND_LSB) & SI4702_BAND_MASK);
+ ret = si4702_write_reg(SI4702_SYSCONFIG2, reg);
+ if (ret == -1)
+ return ret;
+
+ info = &si4702_drvdata->info;
+ info->min_band = band_min;
+ info->max_band = band_max;
+ return 0;
+}
+
+static int si4702_set_seekth(u8 seekth)
+{
+ u16 reg;
+ int ret;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG2, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg =
+ (reg & ~SI4702_SEEKTH_MASK) | ((seekth << SI4702_SEEKTH_LSB) &
+ SI4702_SEEKTH_MASK);
+ ret = si4702_write_reg(SI4702_SYSCONFIG2, reg);
+ if (ret == -1)
+ return ret;
+
+ return 0;
+}
+
+static int si4702_set_sksnr(u8 sksnr)
+{
+ u16 reg;
+ int ret;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG3, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg =
+ (reg & ~SI4702_SNR_MASK) | ((sksnr << SI4702_SNR_LSB) &
+ SI4702_SNR_MASK);
+ ret = si4702_write_reg(SI4702_SYSCONFIG3, reg);
+ if (ret == -1)
+ return ret;
+
+ return 0;
+}
+
+static int si4702_set_skcnt(u8 skcnt)
+{
+ u16 reg;
+ int ret;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG3, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg = (reg & ~SI4702_CNT_MASK) | (skcnt & SI4702_CNT_MASK);
+ ret = si4702_write_reg(SI4702_SYSCONFIG3, reg);
+ if (ret == -1)
+ return ret;
+
+ return 0;
+}
+
+static int si4702_set_vol(int vol)
+{
+ u16 reg;
+ int ret;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG2, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg = (reg & ~SI4702_VOL_MASK) | (vol & SI4702_VOL_MASK);
+ ret = si4702_write_reg(SI4702_SYSCONFIG2, reg);
+ if (ret == -1)
+ return ret;
+
+ info = &si4702_drvdata->info;
+ info->volume = vol;
+
+ return 0;
+}
+
+static u8 si4702_channel_select(u32 freq)
+{
+ u16 loop_counter = 0;
+ s16 channel;
+ u16 si4702_reg_data;
+ u8 error_ind = 0;
+ struct i2c_client *client;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ info = &si4702_drvdata->info;
+ client = si4702_drvdata->client;
+
+ dev_info(&client->dev, "Input frequnce is %d\n", freq);
+ if (freq < 76000 || freq > 108000) {
+ dev_err(&client->dev, "Input frequnce is invalid\n");
+ return -1;
+ }
+ /* convert freq to channel */
+ channel = (freq - info->min_band) / info->space;
+
+ si4702_reg_data = SI4702_TUNE_BIT | (channel & SI4702_CHAN_MASK);
+ /* set channel */
+ error_ind = si4702_write_reg(SI4702_CHANNEL, si4702_reg_data);
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to set channel\n");
+ return -1;
+ }
+ dev_info(&client->dev, "Set channel to %d\n", channel);
+
+ /* wait for STC == 1 */
+ do {
+ error_ind =
+ si4702_read_reg(SI4702_STATUSRSSI, &si4702_reg_data);
+
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to read setted STC\n");
+ return -1;
+ }
+ if ((si4702_reg_data & SI4702_STC_BIT) != 0)
+ break;
+ } while (++loop_counter < DELAY_WAIT);
+
+ /* check loop_counter */
+ if (loop_counter >= DELAY_WAIT) {
+ dev_err(&client->dev, "Can't wait for STC bit set");
+ return -1;
+ }
+ dev_info(&client->dev, "loop counter %d\n", loop_counter);
+
+ loop_counter = 0;
+ /* clear tune bit */
+ error_ind = si4702_write_reg(SI4702_CHANNEL, 0);
+
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to set stop tune\n");
+ return -1;
+ }
+
+ /* wait for STC == 0 */
+ do {
+ error_ind =
+ si4702_read_reg(SI4702_STATUSRSSI, &si4702_reg_data);
+
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to set read STC\n");
+ return -1;
+ }
+ if ((si4702_reg_data & SI4702_STC_BIT) == 0)
+ break;
+ } while (++loop_counter < DELAY_WAIT);
+
+ /* check loop_counter */
+ if (loop_counter >= DELAY_WAIT) {
+ dev_err(&client->dev, "Can't wait for STC bit clean");
+ return -1;
+ }
+ dev_info(&client->dev, "loop counter %d\n", loop_counter);
+
+ /* read RSSI */
+ error_ind = si4702_read_reg(SI4702_READCHAN, &si4702_reg_data);
+
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to read RSSI\n");
+ return -1;
+ }
+
+ channel = si4702_reg_data & SI4702_CHAN_MASK;
+ dev_info(&client->dev, "seek finish: channel(%d)\n", channel);
+
+ return 0;
+}
+
+static s32 si4702_channel_seek(s16 dir)
+{
+ u16 loop_counter = 0;
+ u16 si4702_reg_data, reg_power_cfg;
+ u8 error_ind = 0;
+ u32 channel, freq;
+ struct i2c_client *client;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ info = &si4702_drvdata->info;
+ client = si4702_drvdata->client;
+
+ error_ind = si4702_read_reg(SI4702_POWERCFG, &reg_power_cfg);
+
+ if (info->mute) {
+ /* check disable mute */
+ reg_power_cfg &= ~SI4702_DMUTE_BIT;
+ } else {
+ reg_power_cfg |= SI4702_DMUTE_BIT;
+ }
+
+ if (dir) {
+ reg_power_cfg |= SI4702_SEEKUP_BIT;
+ } else {
+ reg_power_cfg &= ~SI4702_SEEKUP_BIT;
+ }
+ /* start seek */
+ reg_power_cfg |= SI4702_SEEK_BIT;
+ error_ind = si4702_write_reg(SI4702_POWERCFG, reg_power_cfg);
+
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to set seek start bit\n");
+ return -1;
+ }
+
+ /* wait STC == 1 */
+ do {
+ error_ind =
+ si4702_read_reg(SI4702_STATUSRSSI, &si4702_reg_data);
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to read STC bit\n");
+ return -1;
+ }
+
+ if ((si4702_reg_data & SI4702_STC_BIT) != 0)
+ break;
+ } while (++loop_counter < DELAY_WAIT);
+
+ /* clear seek bit */
+ reg_power_cfg &= ~SI4702_SEEK_BIT;
+ error_ind = si4702_write_reg(SI4702_POWERCFG, reg_power_cfg);
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to stop seek\n");
+ return -1;
+ }
+
+ if (loop_counter >= DELAY_WAIT) {
+ dev_err(&client->dev, "Can't wait for STC bit set\n");
+ return -1;
+ }
+
+ /* check whether SF==1 (seek failed bit) */
+ if ((si4702_reg_data & SI4702_SF_BIT) != 0) {
+ dev_err(&client->dev, "Failed to seek any channel\n");
+ return -1;
+ }
+
+ loop_counter = 0;
+ /* wait STC == 0 */
+ do {
+ error_ind =
+ si4702_read_reg(SI4702_STATUSRSSI, &si4702_reg_data);
+
+ if (error_ind) {
+ dev_err(&client->dev,
+ "Failed to wait STC bit to clear\n");
+ return -1;
+ }
+ if ((si4702_reg_data & SI4702_STC_BIT) == 0)
+ break;
+ } while (++loop_counter < DELAY_WAIT);
+
+ /* check loop_counter */
+ if (loop_counter >= DELAY_WAIT) {
+ dev_err(&client->dev, "Can't wait for STC bit clean");
+ return -1;
+ }
+
+ error_ind = si4702_read_reg(SI4702_READCHAN, &si4702_reg_data);
+
+ if (error_ind) {
+ dev_err(&client->dev, "I2C simulate failed\n");
+ return -1;
+ }
+
+ channel = si4702_reg_data & SI4702_CHAN_MASK;
+ freq = channel * info->space + info->min_band;
+ dev_err(&client->dev,
+ "seek finish: channel(%d), freq(%dKHz)\n", channel, freq);
+
+ return 0;
+}
+
+static int si4702_startup(void)
+{
+ u16 magic = 0, id;
+ struct i2c_client *client;
+ struct mxc_fm_platform_data *data;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ if (si4702_drvdata->vio)
+ regulator_enable(si4702_drvdata->vio);
+ if (si4702_drvdata->vdd)
+ regulator_enable(si4702_drvdata->vdd);
+ data = si4702_drvdata->plat_data;
+ client = si4702_drvdata->client;
+
+ /* read prior to write, otherwise write op will fail */
+ si4702_read_reg(SI4702_DEVICEID, &id);
+ dev_err(&client->dev, "si4702: DEVICEID: 0x%x\n", id);
+
+ si4702_clock_en(1);
+ msleep(100);
+
+ /* disable mute, stereo, seek down, powerup */
+ si4702_write_reg(SI4702_POWERCFG, SI4702_DMUTE_BIT | SI4702_ENABLE_BIT);
+ msleep(500);
+ si4702_read_reg(SI4702_TEST1, &magic);
+ if (magic != 0x3C04)
+ dev_err(&client->dev, "magic number 0x%x.\n", magic);
+ /* close tune, set channel to 0 */
+ si4702_write_reg(SI4702_CHANNEL, 0);
+ /* disable interrupt, disable GPIO */
+ si4702_write_reg(SI4702_SYSCONFIG1, 0);
+ /* set volume to middle level */
+ si4702_set_vol(0xf);
+
+ si4702_set_space(data->space);
+ si4702_set_band_range(data->band);
+ si4702_set_seekth(data->seekth);
+ si4702_set_skcnt(data->skcnt);
+ si4702_set_sksnr(data->sksnr);
+
+ return 0;
+}
+
+static void si4702_shutdown(void)
+{
+ if (NULL == si4702_drvdata)
+ return;
+
+ si4702_write_reg(SI4702_POWERCFG, SI4702_DMUTE_BIT |
+ SI4702_ENABLE_BIT | SI4702_DISABLE_BIT);
+ msleep(100);
+ si4702_clock_en(0);
+
+ if (si4702_drvdata->vdd)
+ regulator_disable(si4702_drvdata->vdd);
+ if (si4702_drvdata->vio)
+ regulator_disable(si4702_drvdata->vio);
+}
+
+enum {
+ FM_STARTUP = 0,
+ FM_SHUTDOWN,
+ FM_RESET,
+ FM_VOLUP,
+ FM_VOLDOWN,
+ FM_SEEK_UP,
+ FM_SEEK_DOWN,
+ FM_MUTEON,
+ FM_MUTEDIS,
+ FM_SEL,
+ FM_SEEKTH,
+ FM_DL,
+ FM_CTL_MAX
+};
+
+static const char *const fm_control[FM_CTL_MAX] = {
+ [FM_STARTUP] = "start",
+ [FM_SHUTDOWN] = "halt",
+ [FM_RESET] = "reset",
+ [FM_VOLUP] = "volup",
+ [FM_VOLDOWN] = "voldown",
+ [FM_SEEK_UP] = "seeku",
+ [FM_SEEK_DOWN] = "seekd",
+ [FM_MUTEON] = "mute",
+ [FM_MUTEDIS] = "muted",
+ [FM_SEL] = "select",
+ [FM_SEEKTH] = "seekth",
+ [FM_DL] = "delay"
+};
+
+static int cmd(unsigned int index, int arg)
+{
+ struct i2c_client *client;
+ struct mxc_fm_platform_data *plat_data;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ client = si4702_drvdata->client;
+ plat_data = si4702_drvdata->plat_data;
+
+ switch (index) {
+ case FM_SHUTDOWN:
+ dev_err(&client->dev, "FM_SHUTDOWN\n");
+ si4702_shutdown();
+ break;
+ case FM_STARTUP:
+ dev_err(&client->dev, "FM_STARTUP\n");
+ si4702_reset();
+ si4702_startup();
+ break;
+ case FM_RESET:
+ dev_err(&client->dev, "FM_RESET\n");
+ si4702_reset();
+ break;
+ case FM_SEEK_DOWN:
+ dev_err(&client->dev, "SEEK DOWN\n");
+ si4702_channel_seek(0);
+ break;
+ case FM_SEEK_UP:
+ dev_err(&client->dev, "SEEK UP\n");
+ si4702_channel_seek(1);
+ break;
+ case FM_SEL:
+ dev_err(&client->dev, "select %d\n", arg * 100);
+ si4702_channel_select(arg * 100);
+ break;
+ case FM_SEEKTH:
+ dev_err(&client->dev, "seekth = %d\n", arg);
+ si4702_set_seekth(arg);
+ break;
+ case FM_DL:
+ dev_err(&client->dev, "delay = %d\n", arg);
+ break;
+ default:
+ dev_err(&client->dev, "error command\n");
+ break;
+ }
+ return 0;
+}
+
+static ssize_t si4702_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct si4702_drvdata *drv_data = dev_get_drvdata(dev);
+ u16 device_id;
+
+ dev_err(&(drv_data->client->dev), "si4702 show\n");
+ si4702_read_reg(SI4702_DEVICEID, &device_id);
+ pr_info("device id %x\n", device_id);
+ si4702_dump_reg();
+ return 0;
+}
+
+static ssize_t si4702_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int state = 0;
+ const char *const *s;
+ char *p = NULL;
+ int error;
+ int len, arg = 0;
+ struct si4702_drvdata *drv_data = dev_get_drvdata(dev);
+ struct i2c_client *client = drv_data->client;
+
+ dev_err(&client->dev, "si4702 store %d\n", count);
+
+ p = memchr(buf, ' ', count);
+ if (p) {
+ len = p - buf;
+ *p = '\0';
+ } else
+ len = count;
+
+ len -= 1;
+ dev_err(&client->dev, "cmd %s\n", buf);
+
+ for (s = &fm_control[state]; state < FM_CTL_MAX; s++, state++) {
+ if (*s && !strncmp(buf, *s, len)) {
+ break;
+ }
+ }
+ if (state < FM_CTL_MAX && *s) {
+ if (p)
+ arg = simple_strtoul(p + 1, NULL, 0);
+ dev_err(&client->dev, "arg = %d\n", arg);
+ error = cmd(state, arg);
+ } else {
+ dev_err(&client->dev, "error cmd\n");
+ error = -EINVAL;
+ }
+
+ return error ? error : count;
+}
+
+static int ioctl_si4702(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int mute = 0;
+ u16 data;
+ int error;
+ u8 volume;
+ unsigned int freq;
+ int dir;
+ struct i2c_client *client;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ info = &si4702_drvdata->info;
+ client = si4702_drvdata->client;
+
+ dev_err(&client->dev, "ioctl, cmd: 0x%x, arg: 0x%lx\n", cmd, arg);
+
+ switch (cmd) {
+ case SI4702_SETVOLUME:
+ /* get volume from user */
+ if (copy_from_user(&volume, argp, sizeof(u8))) {
+
+ dev_err(&client->dev,
+ "ioctl, copy volume value from user failed\n");
+ return -EFAULT;
+ }
+ dev_err(&client->dev, "volume %d\n", volume);
+ /* refill the register value */
+ volume &= 0x0f;
+ if (info->mute)
+ error = si4702_write_reg(SI4702_POWERCFG, 0x0001);
+ else
+ error = si4702_write_reg(SI4702_POWERCFG, 0x4001);
+
+ error = si4702_write_reg(SI4702_CHANNEL, 0);
+ error = si4702_write_reg(SI4702_SYSCONFIG1, 0);
+ error = si4702_write_reg(SI4702_SYSCONFIG2, 0x0f10 | volume);
+ if (error) {
+ dev_err(&client->dev, "ioctl, set volume failed\n");
+ return -EFAULT;
+ }
+ /* renew the device info */
+ info->volume = volume;
+
+ break;
+ case SI4702_GETVOLUME:
+ /* just copy volume value to user */
+ if (copy_to_user(argp, &(info->volume), sizeof(unsigned int))) {
+ dev_err(&client->dev, "ioctl, copy to user failed\n");
+ return -EFAULT;
+ }
+ break;
+ case SI4702_MUTEON:
+ mute = 1;
+ case SI4702_MUTEOFF:
+ if (mute) {
+ /* enable mute */
+ si4702_read_reg(SI4702_POWERCFG, &data);
+ data &= 0x00FF;
+ error = si4702_write_reg(SI4702_POWERCFG, data);
+ } else {
+ si4702_read_reg(SI4702_POWERCFG, &data);
+ data &= 0x00FF;
+ data |= 0x4000;
+ error = si4702_write_reg(SI4702_POWERCFG, data);
+ }
+ if (error) {
+ dev_err(&client->dev, "ioctl, set mute failed\n");
+ return -EFAULT;
+ }
+ break;
+ case SI4702_SELECT:
+ if (copy_from_user(&freq, argp, sizeof(unsigned int))) {
+
+ dev_err(&client->dev,
+ "ioctl, copy frequence from user failed\n");
+ return -EFAULT;
+ }
+ /* check frequence */
+ if (freq > info->max_band || freq < info->min_band) {
+ dev_err(&client->dev,
+ "the frequence select is out of band\n");
+ return -EINVAL;
+ }
+ if (si4702_channel_select(freq)) {
+ dev_err(&client->dev,
+ "ioctl, failed to select channel\n");
+ return -EFAULT;
+ }
+ break;
+ case SI4702_SEEK:
+ if (copy_from_user(&dir, argp, sizeof(int))) {
+
+ dev_err(&client->dev, "ioctl, copy from user failed\n");
+ return -EFAULT;
+ }
+ /* get seeked channel */
+ dir = si4702_channel_seek(dir);
+ if (dir == -1) {
+ return -EAGAIN;
+ } else if (dir == -2) {
+ return -EFAULT;
+ }
+ if (copy_to_user(argp, &dir, sizeof(int))) {
+
+ dev_err(&client->dev,
+ "ioctl, copy seek frequnce to user failed\n");
+ return -EFAULT;
+ }
+ break;
+ default:
+ dev_err(&client->dev, "SI4702: Invalid ioctl command\n");
+ return -EINVAL;
+
+ }
+ return 0;
+}
+
+static int open_si4702(struct inode *inode, struct file *file)
+{
+ struct i2c_client *client;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ client = si4702_drvdata->client;
+
+ spin_lock(&count_lock);
+ if (si4702_drvdata->count != 0) {
+ dev_err(&client->dev, "device has been open already\n");
+ spin_unlock(&count_lock);
+ return -EBUSY;
+ }
+ si4702_drvdata->count++;
+ spin_unlock(&count_lock);
+
+ /* request and active GPIO */
+ si4702_gpio_get();
+ /* reset the si4702 from it's reset pin */
+ si4702_reset();
+
+ /* startup si4702 */
+ if (si4702_startup()) {
+ spin_lock(&count_lock);
+ si4702_drvdata->count--;
+ spin_unlock(&count_lock);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int release_si4702(struct inode *inode, struct file *file)
+{
+ struct i2c_client *client;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ client = si4702_drvdata->client;
+
+ dev_err(&client->dev, "release\n");
+ /* software shutdown */
+ si4702_shutdown();
+ /* inactive, free GPIO, cut power */
+ si4702_gpio_put();
+
+ spin_lock(&count_lock);
+ si4702_drvdata->count--;
+ spin_unlock(&count_lock);
+
+ return 0;
+}
+
+static int si4702_suspend(struct i2c_client *client, pm_message_t state)
+{
+ return 0;
+}
+
+static int si4702_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+static struct device_attribute si4702_dev_attr = {
+ .attr = {
+ .name = "si4702_ctl",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .show = si4702_show,
+ .store = si4702_store,
+};
+
+static struct file_operations si4702_fops = {
+ .owner = THIS_MODULE,
+ .open = open_si4702,
+ .release = release_si4702,
+ .ioctl = ioctl_si4702,
+};
+
+static int __devinit si4702_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct mxc_fm_platform_data *plat_data;
+ struct si4702_drvdata *drv_data;
+ struct device *dev;
+
+ dev_info(&client->dev, "si4702 device probe process start.\n");
+
+ plat_data = (struct mxc_fm_platform_data *)client->dev.platform_data;
+ if (plat_data == NULL) {
+ dev_err(&client->dev, "lack of platform data!\n");
+ return -ENODEV;
+ }
+
+ drv_data = kmalloc(sizeof(struct si4702_drvdata), GFP_KERNEL);
+ if (drv_data == NULL) {
+ dev_err(&client->dev, "lack of kernel memory!\n");
+ return -ENOMEM;
+ }
+ memset(drv_data, 0, sizeof(struct si4702_drvdata));
+ drv_data->plat_data = plat_data;
+ drv_data->major = DEV_MAJOR;
+ drv_data->minor = DEV_MINOR;
+ drv_data->count = 0;
+
+ /*enable power supply */
+ if (plat_data->reg_vio != NULL) {
+ drv_data->vio = regulator_get(&client->dev, plat_data->reg_vio);
+ if (drv_data->vio == ERR_PTR(-ENOENT))
+ goto free_drv_data;
+ regulator_enable(drv_data->vio);
+ }
+
+ /* here, we assume that vio and vdd are not the same */
+ if (plat_data->reg_vdd != NULL) {
+ drv_data->vdd = regulator_get(&client->dev, plat_data->reg_vdd);
+ if (drv_data->vdd == ERR_PTR(-ENOENT))
+ goto disable_vio;
+ regulator_enable(drv_data->vdd);
+ }
+
+ /*attach client and check device id */
+ if (SI4702_DEVICE_ID != si4702_id_detect(client)) {
+ dev_err(&client->dev, "id wrong.\n");
+ goto disable_vdd;
+ }
+ dev_info(&client->dev, "chip id %x detect.\n", SI4702_DEVICE_ID);
+ drv_data->client = client;
+
+ /*user interface begain */
+ /*create device file in sysfs as a user interface,
+ * also for debug support */
+ ret = device_create_file(&client->dev, &si4702_dev_attr);
+ if (ret) {
+ dev_err(&client->dev, "create device file failed!\n");
+ goto gpio_put; /* shall i use some meanful error code? */
+ }
+
+ /*create a char dev for application code access */
+ if (drv_data->major) {
+ ret = register_chrdev(drv_data->major, "si4702", &si4702_fops);;
+ } else {
+ ret = register_chrdev(0, "si4702", &si4702_fops);
+ }
+
+ if (drv_data->major == 0)
+ drv_data->major = ret;
+
+ /* create class and device for udev information */
+ drv_data->radio_class = class_create(THIS_MODULE, "radio");
+ if (IS_ERR(drv_data->radio_class)) {
+ dev_err(&client->dev, "SI4702: failed to create radio class\n");
+ goto char_dev_remove;
+ }
+
+ dev = device_create(drv_data->radio_class, NULL,
+ MKDEV(drv_data->major, drv_data->minor), NULL, "si4702");
+ if (IS_ERR(dev)) {
+ dev_err(&client->dev,
+ "SI4702: failed to create radio class device\n");
+ goto class_remove;
+ }
+ /*User interface end */
+ dev_set_drvdata(&client->dev, drv_data);
+ si4702_drvdata = drv_data;
+
+ si4702_gpio_get();
+ dev_info(&client->dev, "si4702 device probe successfully.\n");
+ si4702_shutdown();
+
+ return 0;
+
+class_remove:
+ class_destroy(drv_data->radio_class);
+char_dev_remove:
+ unregister_chrdev(drv_data->major, "si4702");
+ device_remove_file(&client->dev, &si4702_dev_attr);
+gpio_put:
+ si4702_gpio_put();
+disable_vdd:
+ if (plat_data->reg_vdd) {
+ regulator_disable(drv_data->vdd);
+ regulator_put(drv_data->vdd);
+ }
+disable_vio:
+ if (plat_data->reg_vio) {
+ regulator_disable(drv_data->vio);
+ regulator_put(drv_data->vio);
+ }
+
+free_drv_data:
+ kfree(drv_data);
+
+ return -ENODEV;
+}
+
+static int __devexit si4702_remove(struct i2c_client *client)
+{
+ struct mxc_fm_platform_data *plat_data;
+ struct si4702_drvdata *drv_data = dev_get_drvdata(&client->dev);
+
+ plat_data = (struct mxc_fm_platform_data *)client->dev.platform_data;
+
+ device_destroy(drv_data->radio_class,
+ MKDEV(drv_data->major, drv_data->minor));
+ class_destroy(drv_data->radio_class);
+
+ unregister_chrdev(drv_data->major, "si4702");
+ device_remove_file(&client->dev, &si4702_dev_attr);
+ si4702_gpio_put();
+
+ if (plat_data->reg_vdd)
+ regulator_put(drv_data->vdd);
+
+ if (plat_data->reg_vio)
+ regulator_put(drv_data->vio);
+
+ kfree(si4702_drvdata);
+ si4702_drvdata = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id si4702_id[] = {
+ {"si4702", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, si4702_id);
+
+static struct i2c_driver i2c_si4702_driver = {
+ .driver = {
+ .name = "si4702",
+ },
+ .probe = si4702_probe,
+ .remove = si4702_remove,
+ .suspend = si4702_suspend,
+ .resume = si4702_resume,
+ .id_table = si4702_id,
+};
+
+static int __init init_si4702(void)
+{
+ /*add to i2c driver */
+ pr_info("add si4702 i2c driver\n");
+ return i2c_add_driver(&i2c_si4702_driver);
+}
+
+static void __exit exit_si4702(void)
+{
+ i2c_del_driver(&i2c_si4702_driver);
+}
+
+module_init(init_si4702);
+module_exit(exit_si4702);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SI4702 FM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/mxs_viim.c b/drivers/char/mxs_viim.c
new file mode 100755
index 000000000000..2510afa6c13e
--- /dev/null
+++ b/drivers/char/mxs_viim.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+
+static unsigned long iim_reg_base0, iim_reg_end0, iim_reg_size0;
+static unsigned long iim_reg_base1, iim_reg_end1, iim_reg_size1;
+static struct device *iim_dev;
+
+/*!
+ * MXS Virtual IIM interface - memory map function
+ * This function maps one page size VIIM registers from VIIM base address0
+ * if the size of the required virtual memory space is less than or equal to
+ * one page size, otherwise this function will also map one page size VIIM
+ * registers from VIIM base address1.
+ *
+ * @param file struct file *
+ * @param vma structure vm_area_struct *
+ *
+ * @return Return 0 on success or negative error code on error
+ */
+static int mxs_viim_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ size_t size = vma->vm_end - vma->vm_start;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
+ if (remap_pfn_range(vma,
+ vma->vm_start,
+ iim_reg_base0 >> PAGE_SHIFT,
+ iim_reg_size0,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ if (size > iim_reg_size0) {
+ if (remap_pfn_range(vma,
+ vma->vm_start + iim_reg_size0,
+ iim_reg_base1 >> PAGE_SHIFT,
+ iim_reg_size1,
+ vma->vm_page_prot))
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/*!
+ * MXS Virtual IIM interface - open function
+ *
+ * @param inode struct inode *
+ * @param filp struct file *
+ *
+ * @return Return 0 on success or negative error code on error
+ */
+static int mxs_viim_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+/*!
+ * MXS Virtual IIM interface - release function
+ *
+ * @param inode struct inode *
+ * @param filp struct file *
+ *
+ * @return Return 0 on success or negative error code on error
+ */
+static int mxs_viim_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static const struct file_operations mxs_viim_fops = {
+ .mmap = mxs_viim_mmap,
+ .open = mxs_viim_open,
+ .release = mxs_viim_release,
+};
+
+static struct miscdevice mxs_viim_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mxs_viim",
+ .fops = &mxs_viim_fops,
+};
+
+/*!
+ * This function is called by the driver framework to get virtual iim base/end
+ * address and register iim misc device.
+ *
+ * @param dev The device structure for Virtual IIM passed in by the
+ * driver framework.
+ *
+ * @return Returns 0 on success or negative error code on error
+ */
+static int mxs_viim_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ iim_dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res)) {
+ dev_err(iim_dev, "Unable to get Virtual IIM resource 0\n");
+ return -ENODEV;
+ }
+
+ iim_reg_base0 = res->start;
+ iim_reg_end0 = res->end;
+ iim_reg_size0 = iim_reg_end0 - iim_reg_base0 + 1;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (IS_ERR(res)) {
+ dev_err(iim_dev, "Unable to get Virtual IIM resource 1\n");
+ return -ENODEV;
+ }
+
+ iim_reg_base1 = res->start;
+ iim_reg_end1 = res->end;
+ iim_reg_size1 = iim_reg_end1 - iim_reg_base1 + 1;
+
+ ret = misc_register(&mxs_viim_miscdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mxs_viim_remove(struct platform_device *pdev)
+{
+ misc_deregister(&mxs_viim_miscdev);
+ return 0;
+}
+
+static struct platform_driver mxs_viim_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mxs_viim",
+ },
+ .probe = mxs_viim_probe,
+ .remove = mxs_viim_remove,
+};
+
+static int __init mxs_viim_dev_init(void)
+{
+ return platform_driver_register(&mxs_viim_driver);
+}
+
+static void __exit mxs_viim_dev_cleanup(void)
+{
+ platform_driver_unregister(&mxs_viim_driver);
+}
+
+module_init(mxs_viim_dev_init);
+module_exit(mxs_viim_dev_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXS Virtual IIM driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 675076f5fca8..626e95dab938 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -129,6 +129,9 @@
* unsigned int value);
* void add_interrupt_randomness(int irq);
*
+ * void random_input_words(__u32 *buf, size_t wordcount, int ent_count)
+ * int random_input_wait(void);
+ *
* add_input_randomness() uses the input layer interrupt timing, as well as
* the event type information from the hardware.
*
@@ -140,6 +143,13 @@
* a better measure, since the timing of the disk interrupts are more
* unpredictable.
*
+ * random_input_words() just provides a raw block of entropy to the input
+ * pool, such as from a hardware entropy generator.
+ *
+ * random_input_wait() suspends the caller until such time as the
+ * entropy pool falls below the write threshold, and returns a count of how
+ * much entropy (in bits) is needed to sustain the pool.
+ *
* All of these routines try to estimate how many bits of randomness a
* particular randomness source. They do this by keeping track of the
* first and second order deltas of the event timings.
@@ -689,6 +699,61 @@ void add_disk_randomness(struct gendisk *disk)
}
#endif
+/*
+ * random_input_words - add bulk entropy to pool
+ *
+ * @buf: buffer to add
+ * @wordcount: number of __u32 words to add
+ * @ent_count: total amount of entropy (in bits) to credit
+ *
+ * this provides bulk input of entropy to the input pool
+ *
+ */
+void random_input_words(__u32 *buf, size_t wordcount, int ent_count)
+{
+ mix_pool_bytes(&input_pool, buf, wordcount*4);
+
+ credit_entropy_bits(&input_pool, ent_count);
+
+ DEBUG_ENT("crediting %d bits => %d\n",
+ ent_count, input_pool.entropy_count);
+ /*
+ * Wake up waiting processes if we have enough
+ * entropy.
+ */
+ if (input_pool.entropy_count >= random_read_wakeup_thresh)
+ wake_up_interruptible(&random_read_wait);
+}
+EXPORT_SYMBOL(random_input_words);
+
+/*
+ * random_input_wait - wait until random needs entropy
+ *
+ * this function sleeps until the /dev/random subsystem actually
+ * needs more entropy, and then return the amount of entropy
+ * that it would be nice to have added to the system.
+ */
+int random_input_wait(void)
+{
+ int count;
+
+ wait_event_interruptible(random_write_wait,
+ input_pool.entropy_count < random_write_wakeup_thresh);
+
+ count = random_write_wakeup_thresh - input_pool.entropy_count;
+
+ /* likely we got woken up due to a signal */
+ if (count <= 0) count = random_read_wakeup_thresh;
+
+ DEBUG_ENT("requesting %d bits from input_wait()er %d<%d\n",
+ count,
+ input_pool.entropy_count, random_write_wakeup_thresh);
+
+ return count;
+}
+EXPORT_SYMBOL(random_input_wait);
+
+
#define EXTRACT_SIZE 10
/*********************************************************************
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index e522144cba3a..c5de66aba591 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -83,6 +83,14 @@ config ZCRYPT_MONOLITHIC
that contains all parts of the crypto device driver (ap bus,
request router and all the card drivers).
+config CRYPTO_DEV_NS921X_AES
+ tristate "Driver for Digi ns921x AES algorithm"
+ depends on PROCESSOR_NS921X
+ select CRYPTO_BLKCIPHER
+ help
+ Use Digi hardware acceleration for AES algorithm
+ available in Digi ns921x CPUs.
+
config CRYPTO_SHA1_S390
tristate "SHA1 digest algorithm"
depends on S390
@@ -200,4 +208,16 @@ config CRYPTO_DEV_IXP4XX
help
Driver for the IXP4xx NPE crypto engine.
+config CRYPTO_DEV_STMP3XXX_DCP
+ tristate "Support for the STMP3xxx DCP engine"
+ depends on ARCH_STMP3XXX
+ select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
+ help
+ Say 'Y' here to use the STMP3XXX DCP AES
+ engine for the CryptoAPI AES algorithm.
+
+ To compile this driver as a module, choose M here: the module
+ will be called geode-aes.
+
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index 73557b2968d3..6c9b0b4c62d1 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -2,5 +2,7 @@ obj-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o
obj-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o
obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
+obj-$(CONFIG_CRYPTO_DEV_NS921X_AES) += ns921x-aes.o
obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
+obj-$(CONFIG_CRYPTO_DEV_STMP3XXX_DCP) += stmp3xxx_dcp.o
diff --git a/drivers/crypto/ns921x-aes.c b/drivers/crypto/ns921x-aes.c
new file mode 100644
index 000000000000..e26b86572405
--- /dev/null
+++ b/drivers/crypto/ns921x-aes.c
@@ -0,0 +1,636 @@
+/*
+ * drivers/crypto/ns921x-aes.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <mach/dma-ns921x.h>
+
+#define DMA_BDPOINTER 0x0
+
+#define DMA_CONTROL 0x4
+#define DMA_CONTROL_SW32 (1 << 28)
+#define DMA_CONTROL_DW32 (1 << 26)
+#define DMA_CONTROL_RST (1 << 16)
+
+#define DMA_STATUS 0x8
+#define DMA_MAX_BUFFERS 256
+#define DRIVER_NAME "ns921x-aes"
+
+union ns9xxx_dma_desc {
+ struct {
+ u32 source;
+ u16 source_len;
+ u16 dest_len;
+ u32 dest;
+
+#define ENCMODE_CBC 0
+#define ENCMODE_CFB 1
+#define ENCMODE_OFB 2
+#define ENCMODE_CTR 3
+#define ENCMODE_ECB 4
+#define ENCMODE_CCM 5
+#define ENCMODE_KEYEXP 7
+
+#define ENCACTION_ENC (0 << 3)
+#define ENCACTION_DEC (1 << 3)
+#define ECCKEYSIZE_128 (0 << 4)
+#define ENCKEYSIZE_192 (1 << 4)
+#define ENCKEYSIZE_256 (2 << 4)
+#define ENCKEYSIZE(i) ((i == 16) ? ECCKEYSIZE_128 : \
+ (i == 24) ? ENCKEYSIZE_192 : ENCKEYSIZE_256)
+ u16 control;
+#define ENCOP_NONAES 0
+#define ENCOP_KEY 1
+#define ENCOP_IV 2
+#define ENCOP_NONCE 3
+#define ENCOP_ADD 4
+#define ENCOP_DATA 5
+ u16 flags;
+ };
+ u32 data[4];
+};
+
+#define KEYEXP_SIZE128 0x176
+#define KEYEXP_SIZE192 0x208
+#define KEYEXP_SIZE256 0x240
+#define KEYEXP_SIZE(i) ((i == 16) ? KEYEXP_SIZE128 : \
+ (i == 24) ? KEYEXP_SIZE192 : KEYEXP_SIZE256)
+
+struct ns921x_aes_data {
+ struct resource *mem;
+ struct clk *clk;
+
+ struct platform_device *pdev;
+
+ wait_queue_head_t waitq;
+
+ void __iomem *membase;
+ dma_addr_t p_dma_descr;
+ union ns9xxx_dma_desc *v_dma_descr;
+
+ dma_addr_t p_key;
+ u8 *v_key;
+ int keylen;
+
+ int irq;
+ int irq_pending;
+};
+
+struct ns921x_aes_data dev_data;
+
+static inline void cpu_to_be(u32 *ptr, unsigned num)
+{
+ unsigned i;
+
+ for (i = 0; i < num; i++)
+ ptr[i] = cpu_to_be32(ptr[i]);
+}
+
+static inline void be_to_cpu(u32 *ptr, unsigned num)
+{
+ unsigned i;
+
+ for (i = 0; i < num; i++)
+ ptr[i] = be32_to_cpu(ptr[i]);
+}
+
+static irqreturn_t ns921x_aes_int(int irq, void *dev_id)
+{
+ u32 status;
+
+ status = ioread32(dev_data.membase + DMA_STATUS);
+ if (!(status & NS921X_DMA_STIE_NCIP) ) {
+ /* check dma errors */
+ if ( status & (NS921X_DMA_STIE_ECIP |
+ NS921X_DMA_STIE_NRIP | NS921X_DMA_STIE_CAIP |
+ NS921X_DMA_STIE_PCIP) )
+ dev_dbg(&dev_data.pdev->dev,
+ "DMA error. Status = 0x%08x\n", status);
+ }
+ /* ack interrupt */
+ iowrite32(status, dev_data.membase + DMA_STATUS);
+
+ /* disable dma channel*/
+ iowrite32(0, dev_data.membase + DMA_CONTROL);
+
+ /* wake up caller*/
+ dev_data.irq_pending = 0;
+ wake_up(&dev_data.waitq);
+
+ return IRQ_HANDLED;
+}
+
+static int ns921x_aes_cra_init(struct crypto_tfm *tfm)
+{
+ if (dev_data.irq_pending)
+ return -EBUSY;
+
+ memset(dev_data.v_dma_descr, 0,
+ DMA_MAX_BUFFERS * sizeof(*dev_data.v_dma_descr));
+
+ return 0;
+}
+
+static int ns921x_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ int ret;
+
+ if (!(keylen == 16 || keylen == 24 || keylen == 32)) {
+ ret = -EINVAL;
+ dev_dbg(&dev_data.pdev->dev, "err_keylen\n");
+ goto err_keylen;
+ }
+
+ /* Free DMA allocation, just in case this function was
+ * called but there was not a call to crypt or decrypt */
+ if (NULL != dev_data.v_key) {
+ dma_free_coherent(&dev_data.pdev->dev, dev_data.keylen,
+ dev_data.v_key, dev_data.p_key);
+ dev_data.v_key = NULL;
+ }
+
+ dev_data.v_key = dma_alloc_coherent(&dev_data.pdev->dev,
+ keylen, &dev_data.p_key, GFP_KERNEL);
+ if (!dev_data.v_key) {
+ ret = -ENOMEM;
+ dev_dbg(&dev_data.pdev->dev, "err_alloc\n");
+ goto err_alloc;
+ }
+
+ memcpy(dev_data.v_key, key, keylen);
+ dev_data.keylen = keylen;
+
+ cpu_to_be((u32 *)dev_data.v_key, dev_data.keylen / 4);
+
+ /* setup dma descr. for key buffer, .control is set in
+ * de-/encryption routine as it depends on the mode */
+ dev_data.v_dma_descr[0].source = (u32)dev_data.p_key;
+ dev_data.v_dma_descr[0].source_len = dev_data.keylen;
+ dev_data.v_dma_descr[0].flags =
+ ENCOP_KEY | EXT_DMA_DESC_CTRL_FULL;
+
+ return 0;
+
+err_alloc:
+err_keylen:
+ return ret;
+}
+
+int ns921x_aes_dma(unsigned timeout)
+{
+ u32 ctrl;
+
+ /* fire up dma */
+ iowrite32(NS921X_DMA_STIE_NCIE | NS921X_DMA_STIE_ECIE |
+ NS921X_DMA_STIE_NRIE | NS921X_DMA_STIE_NRIE |
+ NS921X_DMA_STIE_CAIE |NS921X_DMA_STIE_PCIE,
+ dev_data.membase + DMA_STATUS);
+ ctrl = DMA_CONTROL_SW32 | NS921X_DMA_CR_SW_32b |
+ NS921X_DMA_CR_CE | NS921X_DMA_CR_CG;
+ iowrite32(ctrl, dev_data.membase + DMA_CONTROL);
+
+ /* wait for isr */
+ dev_data.irq_pending = 1;
+ if (!wait_event_timeout(dev_data.waitq,
+ (dev_data.irq_pending == 0), timeout)) {
+ dev_err(&dev_data.pdev->dev, "interrupt timed out! Retrying\n" );
+ dev_dbg(&dev_data.pdev->dev, "DMA_STATUS = 0x%x\n",
+ ioread32(dev_data.membase + DMA_STATUS) );
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+int ns921x_aes_crypt(struct ablkcipher_request *req, int iv)
+{
+ int ret = 0, i, n;
+ int v_buffers, p_buffers;
+
+ /* get number of used pages */
+ for (v_buffers = 0, n = 0; n < req->nbytes; v_buffers++) {
+ /* ensure big endian mode of input data */
+ cpu_to_be((u32 *)page_address(sg_page(&req->src[v_buffers]))
+ + req->src[v_buffers].offset / 4,
+ req->src[v_buffers].length / 4);
+
+ n += req->src[v_buffers].length;
+ }
+
+ /* src must be same page as dst */
+ if (req->src->page_link != req->dst->page_link) {
+ ret = -EINVAL;
+ goto err_mismatch;
+ }
+
+
+ /* map pages */
+map: p_buffers = dma_map_sg(&dev_data.pdev->dev, req->dst,
+ v_buffers, DMA_BIDIRECTIONAL);
+
+ /* create dma descriptors for every used page */
+ for (i = 0; i < p_buffers; i++) {
+ /* setup dma descriptors */
+ dev_data.v_dma_descr[1 + iv + i].source =
+ (u32)sg_dma_address(req->src + i);
+ dev_data.v_dma_descr[1 + iv + i].source_len =
+ (u16)sg_dma_len(req->src + i);
+ dev_data.v_dma_descr[1 + iv + i].dest =
+ (u32)sg_dma_address(req->src + i);
+ dev_data.v_dma_descr[1 + iv + i].flags =
+ ENCOP_DATA | EXT_DMA_DESC_CTRL_FULL;
+ }
+
+ /* add additional dma flags to last descriptor */
+ dev_data.v_dma_descr[iv + p_buffers].flags |=
+ EXT_DMA_DESC_CTRL_WRAP | EXT_DMA_DESC_CTRL_LAST;
+
+ /* let hardware do its work */
+ ret = ns921x_aes_dma(HZ * req->src->length / 100);
+
+ /* release dma mappings */
+ dma_sync_sg_for_cpu(&dev_data.pdev->dev, req->src,
+ v_buffers, DMA_BIDIRECTIONAL);
+ dma_unmap_sg(&dev_data.pdev->dev, req->src, i, DMA_BIDIRECTIONAL);
+ /* If DMA transmission did not complete, try again */
+ if (-EAGAIN == ret )
+ goto map;
+
+err_mismatch:
+ /* overwrite key buffer to avoid security breakage! */
+ memset(dev_data.v_key, 0, dev_data.keylen);
+ dma_free_coherent(&dev_data.pdev->dev, dev_data.keylen,
+ dev_data.v_key, dev_data.p_key);
+ dev_data.v_key = NULL;
+
+ /* convert output back to cpu endianes */
+ for (i = 0; i < v_buffers; i++)
+ be_to_cpu((u32 *)page_address(sg_page(&req->dst[i])) +
+ req->dst[i].offset / 4, req->dst[i].length / 4);
+
+ return ret;
+}
+
+static int ns921x_aes_keyexpander(void)
+{
+ int ret;
+ dma_addr_t p_expkey;
+ u8 *v_expkey;
+
+ v_expkey = dma_alloc_coherent(&dev_data.pdev->dev,
+ KEYEXP_SIZE(dev_data.keylen),
+ &p_expkey, GFP_KERNEL);
+ if (!v_expkey)
+ return -ENOMEM;
+
+buff: dev_data.v_dma_descr[0].control =
+ ENCMODE_KEYEXP | ENCKEYSIZE(dev_data.keylen);
+
+ dev_data.v_dma_descr[1].source_len = KEYEXP_SIZE(dev_data.keylen);
+ dev_data.v_dma_descr[1].dest = p_expkey;
+ dev_data.v_dma_descr[1].flags =
+ ENCOP_DATA | EXT_DMA_DESC_CTRL_WRAP |
+ EXT_DMA_DESC_CTRL_FULL | EXT_DMA_DESC_CTRL_LAST;
+
+ ret = ns921x_aes_dma(HZ/10);
+ if (!ret) {
+ switch (dev_data.keylen) {
+ case 16:
+ memcpy(dev_data.v_key, (u8 *)v_expkey + 160, 16);
+ break;
+ case 24:
+ memcpy(dev_data.v_key, (u8 *)v_expkey + 192, 16);
+ memcpy(dev_data.v_key + 16, (u8 *)v_expkey + 184, 8);
+ break;
+ case 32:
+ memcpy(dev_data.v_key, (u8 *)v_expkey + 224, 16);
+ memcpy(dev_data.v_key + 16, (u8 *)v_expkey + 208, 16);
+ break;
+ }
+
+ /* reset used dma_descriptors */
+ memset(dev_data.v_dma_descr, 0,
+ 2 * sizeof(*dev_data.v_dma_descr));
+ dev_data.v_dma_descr[0].source = (u32)dev_data.p_key;
+ dev_data.v_dma_descr[0].source_len = dev_data.keylen;
+ dev_data.v_dma_descr[0].flags =
+ ENCOP_KEY | EXT_DMA_DESC_CTRL_FULL ;
+ }
+ else if (-EAGAIN == ret)
+ goto buff; /* retry dma */
+
+ /* destroy expanded key */
+ memset(v_expkey, 0, KEYEXP_SIZE(dev_data.keylen));
+ dma_free_coherent(&dev_data.pdev->dev, KEYEXP_SIZE(dev_data.keylen),
+ v_expkey, p_expkey);
+
+ /*
+ * We need the key for the next operations, or? Fixed Vantive 31365
+ * (Luis Galdos)
+ */
+ /* dev_data.v_key = NULL; */
+
+ return ret;
+}
+
+static int ns921x_aes_ecb_encrypt(struct ablkcipher_request *req)
+{
+ dev_data.v_dma_descr[0].control =
+ ENCMODE_ECB | ENCKEYSIZE(dev_data.keylen) | ENCACTION_ENC;
+ return ns921x_aes_crypt(req, 0);
+}
+
+static int ns921x_aes_ecb_decrypt(struct ablkcipher_request *req)
+{
+ int ret;
+
+ ret = ns921x_aes_keyexpander();
+ if (!ret) {
+ dev_data.v_dma_descr[0].control = ENCMODE_ECB |
+ ENCKEYSIZE(dev_data.keylen) | ENCACTION_DEC;
+ ret = ns921x_aes_crypt(req, 0);
+ }
+
+ return ret;
+}
+
+static int ns921x_aes_cbc_encrypt(struct ablkcipher_request *req)
+{
+ int ret, ivsize;
+ u8 *v_iv;
+ dma_addr_t p_iv;
+
+ dev_data.v_dma_descr[0].control =
+ ENCMODE_CBC | ENCKEYSIZE(dev_data.keylen) | ENCACTION_ENC;
+
+ ivsize = 16;
+ /* XXX: crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(req)); */
+ v_iv = dma_alloc_coherent(&dev_data.pdev->dev, ivsize,
+ &p_iv, GFP_KERNEL);
+
+ memcpy(v_iv, req->info, ivsize);
+ cpu_to_be((u32 *)v_iv, ivsize);
+
+ dev_data.v_dma_descr[1].source = (u32)p_iv;
+ dev_data.v_dma_descr[1].source_len = ivsize;
+ dev_data.v_dma_descr[1].flags =
+ EXT_DMA_DESC_CTRL_FULL | ENCOP_IV;
+
+ ret = ns921x_aes_crypt(req, 1);
+
+ dma_free_coherent(&dev_data.pdev->dev, ivsize, v_iv, p_iv);
+
+ return ret;
+return -ENOMEM;
+}
+
+static int ns921x_aes_cbc_decrypt(struct ablkcipher_request *req)
+{
+ int ret, ivsize;
+ u8 *v_iv;
+ dma_addr_t p_iv;
+
+ ret = ns921x_aes_keyexpander();
+ if (!ret) {
+ dev_data.v_dma_descr[0].control = ENCMODE_CBC |
+ ENCKEYSIZE(dev_data.keylen) | ENCACTION_DEC;
+
+ ivsize = 16;
+ /* XXX: crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(req)); */
+ v_iv = dma_alloc_coherent(&dev_data.pdev->dev, ivsize,
+ &p_iv, GFP_KERNEL);
+
+ memcpy(v_iv, req->info, ivsize);
+ cpu_to_be((u32 *)v_iv, ivsize);
+
+ dev_data.v_dma_descr[1].source = (u32)p_iv;
+ dev_data.v_dma_descr[1].source_len = ivsize;
+ dev_data.v_dma_descr[1].flags =
+ EXT_DMA_DESC_CTRL_FULL | ENCOP_IV;
+
+ ret = ns921x_aes_crypt(req, 1);
+
+ dma_free_coherent(&dev_data.pdev->dev, ivsize, v_iv, p_iv);
+ }
+
+ return ret;
+return -ENOMEM;
+}
+
+static struct ablkcipher_alg ciphers[] = {
+ {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = ns921x_aes_setkey,
+ .encrypt = ns921x_aes_ecb_encrypt,
+ .decrypt = ns921x_aes_ecb_decrypt,
+ }, {
+
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = ns921x_aes_setkey,
+ .encrypt = ns921x_aes_cbc_encrypt,
+ .decrypt = ns921x_aes_cbc_decrypt,
+ },
+};
+
+static struct crypto_alg ns921x_aes_algs[] = {
+ {
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = DRIVER_NAME,
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = 16,
+ .cra_alignmask = 15,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = ns921x_aes_cra_init,
+ .cra_list = LIST_HEAD_INIT(ns921x_aes_algs[0].cra_list),
+ }, {
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = DRIVER_NAME,
+ .cra_priority = 0,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = 16,
+ .cra_alignmask = 15,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = ns921x_aes_cra_init,
+ .cra_list = LIST_HEAD_INIT(ns921x_aes_algs[1].cra_list),
+ },
+};
+
+static int __init ns921x_aes_probe(struct platform_device *pdev)
+{
+ int i, ret = -ENOMEM;
+
+ memset(&dev_data, 0, sizeof(dev_data));
+ dev_data.pdev = pdev;
+
+ dev_data.mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!dev_data.mem) {
+ ret = -ENODEV;
+ dev_info(&pdev->dev, "err_get_mem\n");
+ goto err_get_mem;
+ }
+
+ if (!request_mem_region(dev_data.mem->start,
+ dev_data.mem->end - dev_data.mem->start + 1,
+ DRIVER_NAME)) {
+ ret = -EBUSY;
+ dev_info(&pdev->dev, "err_request_mem\n");
+ goto err_request_mem;
+ }
+
+ dev_data.membase = ioremap(dev_data.mem->start,
+ dev_data.mem->end - dev_data.mem->start + 1);
+ if (!dev_data.membase) {
+ ret = -EBUSY;
+ dev_info(&pdev->dev, "err_ioremap\n");
+ goto err_ioremap;
+ }
+
+ dev_data.irq = platform_get_irq(pdev, 0);
+ if (dev_data.irq <= 0) {
+ ret = -EINVAL;
+ dev_info(&pdev->dev, "err_get_irq\n");
+ goto err_get_irq;
+ }
+
+ ret = request_irq(dev_data.irq, ns921x_aes_int, 0, DRIVER_NAME, pdev);
+ if (ret) {
+ dev_info(&pdev->dev, "err_request_irq\n");
+ goto err_request_irq;
+ }
+
+ dev_data.clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(dev_data.clk)) {
+ ret = PTR_ERR(dev_data.clk);
+ dev_info(&pdev->dev, "err_clk_get\n");
+ goto err_clk_get;
+ }
+
+ ret = clk_enable(dev_data.clk);
+ if (ret) {
+ ret = -EBUSY;
+ dev_info(&pdev->dev, "err_clk_enable\n");
+ goto err_clk_enable;
+ }
+
+ /* register memory for maximal number of dma descriptors */
+ pdev->dev.coherent_dma_mask = (u32)-1;
+ dev_data.v_dma_descr = dma_alloc_coherent(&pdev->dev,
+ sizeof(*dev_data.v_dma_descr) * DMA_MAX_BUFFERS,
+ &dev_data.p_dma_descr, GFP_KERNEL);
+ if (!dev_data.v_dma_descr) {
+ ret = -ENOMEM;
+ goto err_dma_descr;
+ }
+
+ iowrite32(DMA_CONTROL_RST, dev_data.membase + DMA_CONTROL);
+ iowrite32((u32)dev_data.p_dma_descr, dev_data.membase + DMA_BDPOINTER);
+
+ for (i = 0; i < ARRAY_SIZE(ns921x_aes_algs); i++) {
+ ns921x_aes_algs[i].cra_u.ablkcipher = ciphers[i];
+ ret = crypto_register_alg(&ns921x_aes_algs[i]);
+ if (ret) {
+ dev_info(&pdev->dev, "err_register\n");
+ goto err_register;
+ }
+ }
+
+ init_waitqueue_head(&dev_data.waitq);
+ dev_info(&pdev->dev, "NS921x AES encryption/decryption module at 0x%p (irq: %d)\n",
+ dev_data.membase, dev_data.irq);
+
+ return 0;
+
+err_register:
+ while (--i >= 0)
+ crypto_unregister_alg(&ns921x_aes_algs[i]);
+ dma_free_coherent(&pdev->dev,
+ sizeof(*dev_data.v_dma_descr) * DMA_MAX_BUFFERS,
+ dev_data.v_dma_descr, dev_data.p_dma_descr);
+err_dma_descr:
+ clk_disable(dev_data.clk);
+err_clk_enable:
+ clk_put(dev_data.clk);
+err_clk_get:
+ free_irq(dev_data.irq, pdev);
+err_request_irq:
+err_get_irq:
+ iounmap(dev_data.membase);
+err_ioremap:
+ release_mem_region(dev_data.mem->start,
+ dev_data.mem->end - dev_data.mem->start + 1);
+err_request_mem:
+err_get_mem:
+
+ return ret;
+}
+
+static int __exit ns921x_aes_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ns921x_aes_algs); i++)
+ crypto_unregister_alg(&ns921x_aes_algs[i]);
+
+ dma_free_coherent(&pdev->dev,
+ sizeof(*dev_data.v_dma_descr) * DMA_MAX_BUFFERS,
+ dev_data.v_dma_descr, dev_data.p_dma_descr);
+
+ clk_disable(dev_data.clk);
+ clk_put(dev_data.clk);
+
+ free_irq(dev_data.irq, pdev);
+
+ iounmap(dev_data.membase);
+ release_mem_region(dev_data.mem->start,
+ dev_data.mem->end - dev_data.mem->start + 1);
+
+ return 0;
+}
+
+static struct platform_driver ns921x_aes_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ns921x_aes_probe,
+ .remove = __exit_p(ns921x_aes_remove),
+};
+
+static int __init ns921x_aes_init(void)
+{
+ return platform_driver_register(&ns921x_aes_driver);
+}
+
+static void __exit ns921x_aes_exit(void)
+{
+ platform_driver_unregister(&ns921x_aes_driver);
+}
+
+module_init(ns921x_aes_init);
+module_exit(ns921x_aes_exit);
+
+MODULE_DESCRIPTION("Digi ns921x AES algorithm support");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Digi International Inc.");
+
+MODULE_ALIAS("aes");
diff --git a/drivers/crypto/stmp3xxx_dcp.c b/drivers/crypto/stmp3xxx_dcp.c
new file mode 100644
index 000000000000..ff02ec807ec0
--- /dev/null
+++ b/drivers/crypto/stmp3xxx_dcp.c
@@ -0,0 +1,1401 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*
+ * Based on geode-aes.c
+ * Copyright (C) 2004-2006, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/crypto.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include <crypto/sha.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include <asm/cacheflush.h>
+
+#include "stmp3xxx_dcp.h"
+
+/* SHA1 is busted */
+#define DISABLE_SHA1
+
+struct stmp3xxx_dcp {
+ struct device *dev;
+ spinlock_t lock;
+ struct mutex op_mutex[STMP3XXX_DCP_NUM_CHANNELS];
+ struct completion op_wait[STMP3XXX_DCP_NUM_CHANNELS];
+ int wait[STMP3XXX_DCP_NUM_CHANNELS];
+ int dcp_vmi_irq;
+ int dcp_irq;
+};
+
+/* cipher flags */
+#define STMP3XXX_DCP_ENC 0x0001
+#define STMP3XXX_DCP_DEC 0x0002
+#define STMP3XXX_DCP_ECB 0x0004
+#define STMP3XXX_DCP_CBC 0x0008
+#define STMP3XXX_DCP_CBC_INIT 0x0010
+#define STMP3XXX_DCP_OTPKEY 0x0020
+
+/* hash flags */
+#define STMP3XXX_DCP_INIT 0x0001
+#define STMP3XXX_DCP_UPDATE 0x0002
+#define STMP3XXX_DCP_FINAL 0x0004
+
+#define STMP3XXX_DCP_AES 0x1000
+#define STMP3XXX_DCP_SHA1 0x2000
+#define STMP3XXX_DCP_CRC32 0x3000
+#define STMP3XXX_DCP_COPY 0x4000
+#define STMP3XXX_DCP_FILL 0x5000
+#define STMP3XXX_DCP_MODE_MASK 0xf000
+
+struct stmp3xxx_dcp_op {
+
+ unsigned int flags;
+
+ void *src;
+ dma_addr_t src_phys;
+
+ void *dst;
+ dma_addr_t dst_phys;
+
+ int len;
+
+ /* the key contains the IV for block modes */
+ union {
+ struct {
+ u8 key[2 * AES_KEYSIZE_128]
+ __attribute__ ((__aligned__(32)));
+ dma_addr_t key_phys;
+ int keylen;
+ } cipher;
+ struct {
+ u8 digest[SHA1_DIGEST_SIZE]
+ __attribute__ ((__aligned__(32)));
+ dma_addr_t digest_phys;
+ int digestlen;
+ int init;
+ } hash;
+ };
+
+ union {
+ struct crypto_blkcipher *blk;
+ struct crypto_cipher *cip;
+ struct crypto_hash *hash;
+ } fallback;
+
+ struct stmp3xxx_dcp_hw_packet pkt
+ __attribute__ ((__aligned__(32)));
+};
+
+struct stmp3xxx_dcp_hash_coherent_block {
+ struct stmp3xxx_dcp_hw_packet pkt[2]
+ __attribute__ ((__aligned__(32)));
+ u8 digest[SHA1_DIGEST_SIZE]
+ __attribute__ ((__aligned__(32)));
+ u8 rembuf[32];
+};
+
+struct stmp3xxx_dcp_hash_op {
+
+ unsigned int flags;
+
+ void *src;
+ dma_addr_t src_phys;
+
+ void *dst;
+ dma_addr_t dst_phys;
+
+ int len;
+
+ /* the key contains the IV for block modes */
+ union {
+ struct {
+ u8 key[2 * AES_KEYSIZE_128]
+ __attribute__ ((__aligned__(32)));
+ dma_addr_t key_phys;
+ int keylen;
+ } cipher;
+ struct {
+ u8 digest[SHA1_DIGEST_SIZE]
+ __attribute__ ((__aligned__(32)));
+ dma_addr_t digest_phys;
+ int digestlen;
+ int init;
+ } hash;
+ };
+
+ union {
+ struct crypto_blkcipher *blk;
+ struct crypto_cipher *cip;
+ struct crypto_hash *hash;
+ } fallback;
+
+ struct stmp3xxx_dcp_hash_coherent_block *hw;
+ dma_addr_t hw_phys;
+};
+
+/* only one */
+static struct stmp3xxx_dcp *global_sdcp;
+
+static void dcp_perform_op(struct stmp3xxx_dcp_op *op)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct mutex *mutex;
+ struct stmp3xxx_dcp_hw_packet *pkt;
+ int chan;
+ u32 pkt1, pkt2;
+ unsigned long timeout;
+ dma_addr_t pkt_phys;
+ u32 stat;
+
+ pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE | BM_DCP_PACKET1_INTERRUPT;
+
+ switch (op->flags & STMP3XXX_DCP_MODE_MASK) {
+
+ case STMP3XXX_DCP_AES:
+
+ chan = CIPHER_CHAN;
+
+ /* key is at the payload */
+ pkt1 |= BM_DCP_PACKET1_ENABLE_CIPHER;
+ if ((op->flags & STMP3XXX_DCP_OTPKEY) == 0)
+ pkt1 |= BM_DCP_PACKET1_PAYLOAD_KEY;
+ if (op->flags & STMP3XXX_DCP_ENC)
+ pkt1 |= BM_DCP_PACKET1_CIPHER_ENCRYPT;
+ if (op->flags & STMP3XXX_DCP_CBC_INIT)
+ pkt1 |= BM_DCP_PACKET1_CIPHER_INIT;
+
+ pkt2 = BF_DCP_PACKET2_CIPHER_CFG(0) |
+ BF_DCP_PACKET2_KEY_SELECT(0) |
+ BF_DCP_PACKET2_CIPHER_SELECT(
+ BV_DCP_PACKET2_CIPHER_SELECT__AES128);
+
+ if (op->flags & STMP3XXX_DCP_ECB)
+ pkt2 |= BF_DCP_PACKET2_CIPHER_MODE(
+ BV_DCP_PACKET2_CIPHER_MODE__ECB);
+ else if (op->flags & STMP3XXX_DCP_CBC)
+ pkt2 |= BF_DCP_PACKET2_CIPHER_MODE(
+ BV_DCP_PACKET2_CIPHER_MODE__CBC);
+
+ break;
+
+ case STMP3XXX_DCP_SHA1:
+
+ chan = HASH_CHAN;
+
+ pkt1 |= BM_DCP_PACKET1_ENABLE_HASH;
+ if (op->flags & STMP3XXX_DCP_INIT)
+ pkt1 |= BM_DCP_PACKET1_HASH_INIT;
+ if (op->flags & STMP3XXX_DCP_FINAL) {
+ pkt1 |= BM_DCP_PACKET1_HASH_TERM;
+ BUG_ON(op->hash.digest == NULL);
+ }
+
+ pkt2 = BF_DCP_PACKET2_HASH_SELECT(
+ BV_DCP_PACKET2_HASH_SELECT__SHA1);
+ break;
+
+ default:
+ dev_err(sdcp->dev, "Unsupported mode\n");
+ return;
+ }
+
+ mutex = &sdcp->op_mutex[chan];
+ pkt = &op->pkt;
+
+ pkt->pNext = 0;
+ pkt->pkt1 = pkt1;
+ pkt->pkt2 = pkt2;
+ pkt->pSrc = (u32)op->src_phys;
+ pkt->pDst = (u32)op->dst_phys;
+ pkt->size = op->len;
+ pkt->pPayload = chan == CIPHER_CHAN ?
+ (u32)op->cipher.key_phys : (u32)op->hash.digest_phys;
+ pkt->stat = 0;
+
+ pkt_phys = dma_map_single(sdcp->dev, pkt, sizeof(*pkt),
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(sdcp->dev, pkt_phys)) {
+ dev_err(sdcp->dev, "Unable to map packet descriptor\n");
+ return;
+ }
+
+ /* submit the work */
+ mutex_lock(mutex);
+
+ HW_DCP_CHnSTAT_CLR(chan, -1);
+
+ /* Load the work packet pointer and bump the channel semaphore */
+ HW_DCP_CHnCMDPTR_WR(chan, (u32)pkt_phys);
+
+ /* XXX wake from interrupt instead of looping */
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ sdcp->wait[chan] = 0;
+ HW_DCP_CHnSEMA_WR(chan, BF_DCP_CHnSEMA_INCREMENT(1));
+ while (time_before(jiffies, timeout) && sdcp->wait[chan] == 0)
+ cpu_relax();
+
+ if (!time_before(jiffies, timeout)) {
+ dev_err(sdcp->dev, "Timeout while waiting STAT 0x%08x\n",
+ HW_DCP_STAT_RD());
+ goto out;
+ }
+
+ stat = HW_DCP_CHnSTAT_RD(chan);
+ if ((stat & 0xff) != 0)
+ dev_err(sdcp->dev, "Channel stat error 0x%02x\n",
+ HW_DCP_CHnSTAT_RD(chan) & 0xff);
+out:
+ mutex_unlock(mutex);
+
+ dma_unmap_single(sdcp->dev, pkt_phys, sizeof(*pkt), DMA_TO_DEVICE);
+}
+
+static int dcp_aes_setkey_cip(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int len)
+{
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+ unsigned int ret;
+
+ op->cipher.keylen = len;
+
+ if (len == AES_KEYSIZE_128) {
+ memcpy(op->cipher.key, key, len);
+ return 0;
+ }
+
+ if (len != AES_KEYSIZE_192 && len != AES_KEYSIZE_256) {
+ /* not supported at all */
+ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ /*
+ * The requested key size is not supported by HW, do a fallback
+ */
+ op->fallback.blk->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
+ op->fallback.blk->base.crt_flags |= (tfm->crt_flags &
+ CRYPTO_TFM_REQ_MASK);
+
+ ret = crypto_cipher_setkey(op->fallback.cip, key, len);
+ if (ret) {
+ tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+ tfm->crt_flags |= (op->fallback.blk->base.crt_flags &
+ CRYPTO_TFM_RES_MASK);
+ }
+ return ret;
+}
+
+static void dcp_aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128)) {
+ crypto_cipher_encrypt_one(op->fallback.cip, out, in);
+ return;
+ }
+
+ op->src = (void *) in;
+ op->dst = (void *) out;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_ENC | STMP3XXX_DCP_ECB;
+ op->len = AES_KEYSIZE_128;
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, (void *)in, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dev_err(sdcp->dev, "Unable to map source\n");
+ return;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, out, AES_KEYSIZE_128,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ goto err_unmap_src;
+ }
+
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128, DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ goto err_unmap_dst;
+ }
+
+ /* perform the operation */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+err_unmap_dst:
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len, DMA_FROM_DEVICE);
+err_unmap_src:
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len, DMA_TO_DEVICE);
+}
+
+static void dcp_aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128)) {
+ crypto_cipher_decrypt_one(op->fallback.cip, out, in);
+ return;
+ }
+
+ op->src = (void *) in;
+ op->dst = (void *) out;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_DEC | STMP3XXX_DCP_ECB;
+ op->len = AES_KEYSIZE_128;
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, (void *)in, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dev_err(sdcp->dev, "Unable to map source\n");
+ return;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, out, AES_KEYSIZE_128,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ goto err_unmap_src;
+ }
+
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128, DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ goto err_unmap_dst;
+ }
+
+ /* perform the operation */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+err_unmap_dst:
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len, DMA_FROM_DEVICE);
+err_unmap_src:
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len, DMA_TO_DEVICE);
+}
+
+static int fallback_init_cip(struct crypto_tfm *tfm)
+{
+ const char *name = tfm->__crt_alg->cra_name;
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ op->fallback.cip = crypto_alloc_cipher(name, 0,
+ CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
+
+ if (IS_ERR(op->fallback.cip)) {
+ printk(KERN_ERR "Error allocating fallback algo %s\n", name);
+ return PTR_ERR(op->fallback.cip);
+ }
+
+ return 0;
+}
+
+static void fallback_exit_cip(struct crypto_tfm *tfm)
+{
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ crypto_free_cipher(op->fallback.cip);
+ op->fallback.cip = NULL;
+}
+
+static struct crypto_alg dcp_aes_alg = {
+ .cra_name = "aes",
+ .cra_driver_name = "dcp-aes",
+ .cra_priority = 300,
+ .cra_alignmask = 15,
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_init = fallback_init_cip,
+ .cra_exit = fallback_exit_cip,
+ .cra_blocksize = AES_KEYSIZE_128,
+ .cra_ctxsize = sizeof(struct stmp3xxx_dcp_op),
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(dcp_aes_alg.cra_list),
+ .cra_u = {
+ .cipher = {
+ .cia_min_keysize = AES_MIN_KEY_SIZE,
+ .cia_max_keysize = AES_MAX_KEY_SIZE,
+ .cia_setkey = dcp_aes_setkey_cip,
+ .cia_encrypt = dcp_aes_encrypt,
+ .cia_decrypt = dcp_aes_decrypt
+ }
+ }
+};
+
+static int dcp_aes_setkey_blk(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int len)
+{
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+ unsigned int ret;
+
+ op->cipher.keylen = len;
+
+ if (len == AES_KEYSIZE_128) {
+ memcpy(op->cipher.key, key, len);
+ return 0;
+ }
+
+ if (len != AES_KEYSIZE_192 && len != AES_KEYSIZE_256) {
+ /* not supported at all */
+ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ /*
+ * The requested key size is not supported by HW, do a fallback
+ */
+ op->fallback.blk->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
+ op->fallback.blk->base.crt_flags |= (tfm->crt_flags &
+ CRYPTO_TFM_REQ_MASK);
+
+ ret = crypto_blkcipher_setkey(op->fallback.blk, key, len);
+ if (ret) {
+ tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+ tfm->crt_flags |= (op->fallback.blk->base.crt_flags &
+ CRYPTO_TFM_RES_MASK);
+ }
+ return ret;
+}
+
+static int fallback_blk_dec(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ unsigned int ret;
+ struct crypto_blkcipher *tfm;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+
+ tfm = desc->tfm;
+ desc->tfm = op->fallback.blk;
+
+ ret = crypto_blkcipher_decrypt_iv(desc, dst, src, nbytes);
+
+ desc->tfm = tfm;
+ return ret;
+}
+
+static int fallback_blk_enc(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ unsigned int ret;
+ struct crypto_blkcipher *tfm;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+
+ tfm = desc->tfm;
+ desc->tfm = op->fallback.blk;
+
+ ret = crypto_blkcipher_encrypt_iv(desc, dst, src, nbytes);
+
+ desc->tfm = tfm;
+ return ret;
+}
+
+static int fallback_init_blk(struct crypto_tfm *tfm)
+{
+ const char *name = tfm->__crt_alg->cra_name;
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ op->fallback.blk = crypto_alloc_blkcipher(name, 0,
+ CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
+
+ if (IS_ERR(op->fallback.blk)) {
+ printk(KERN_ERR "Error allocating fallback algo %s\n", name);
+ return PTR_ERR(op->fallback.blk);
+ }
+
+ return 0;
+}
+
+static void fallback_exit_blk(struct crypto_tfm *tfm)
+{
+ struct stmp3xxx_dcp_op *op = crypto_tfm_ctx(tfm);
+
+ crypto_free_blkcipher(op->fallback.blk);
+ op->fallback.blk = NULL;
+}
+
+static int
+dcp_aes_ecb_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+ struct blkcipher_walk walk;
+ int err;
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
+ return fallback_blk_dec(desc, dst, src, nbytes);
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+
+ /* key needs to be mapped only once */
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128, DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ return -ENOMEM;
+ }
+
+ err = blkcipher_walk_virt(desc, &walk);
+ while (err == 0 && (nbytes = walk.nbytes) > 0) {
+ op->src = walk.src.virt.addr,
+ op->dst = walk.dst.virt.addr;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_DEC |
+ STMP3XXX_DCP_ECB;
+ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dev_err(sdcp->dev, "Unable to map source\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ /* perform! */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+
+ nbytes -= op->len;
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+
+ return err;
+}
+
+static int
+dcp_aes_ecb_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+ struct blkcipher_walk walk;
+ int err, ret;
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
+ return fallback_blk_enc(desc, dst, src, nbytes);
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+
+ /* key needs to be mapped only once */
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128, DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ return -ENOMEM;
+ }
+
+ err = blkcipher_walk_virt(desc, &walk);
+
+ err = 0;
+ while (err == 0 && (nbytes = walk.nbytes) > 0) {
+ op->src = walk.src.virt.addr,
+ op->dst = walk.dst.virt.addr;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_ENC |
+ STMP3XXX_DCP_ECB;
+ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dev_err(sdcp->dev, "Unable to map source\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ /* perform! */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+
+ nbytes -= op->len;
+ ret = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys, AES_KEYSIZE_128,
+ DMA_TO_DEVICE);
+
+ return err;
+}
+
+
+static struct crypto_alg dcp_aes_ecb_alg = {
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "dcp-ecb-aes",
+ .cra_priority = 400,
+ .cra_alignmask = 15,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_init = fallback_init_blk,
+ .cra_exit = fallback_exit_blk,
+ .cra_blocksize = AES_KEYSIZE_128,
+ .cra_ctxsize = sizeof(struct stmp3xxx_dcp_op),
+ .cra_type = &crypto_blkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(dcp_aes_ecb_alg.cra_list),
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = dcp_aes_setkey_blk,
+ .encrypt = dcp_aes_ecb_encrypt,
+ .decrypt = dcp_aes_ecb_decrypt
+ }
+ }
+};
+
+static int
+dcp_aes_cbc_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+ struct blkcipher_walk walk;
+ int err, blockno;
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
+ return fallback_blk_dec(desc, dst, src, nbytes);
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+
+ blockno = 0;
+ err = blkcipher_walk_virt(desc, &walk);
+ while (err == 0 && (nbytes = walk.nbytes) > 0) {
+ op->src = walk.src.virt.addr,
+ op->dst = walk.dst.virt.addr;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_DEC |
+ STMP3XXX_DCP_CBC;
+ if (blockno == 0) {
+ op->flags |= STMP3XXX_DCP_CBC_INIT;
+ memcpy(op->cipher.key + AES_KEYSIZE_128, walk.iv,
+ AES_KEYSIZE_128);
+ }
+ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
+
+ /* key (+iv) needs to be mapped only once */
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dev_err(sdcp->dev, "Unable to map source\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ /* perform! */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+
+ nbytes -= op->len;
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+
+ blockno++;
+ }
+
+ return err;
+}
+
+static int
+dcp_aes_cbc_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_op *op = crypto_blkcipher_ctx(desc->tfm);
+ struct blkcipher_walk walk;
+ int err, ret, blockno;
+
+ if (unlikely(op->cipher.keylen != AES_KEYSIZE_128))
+ return fallback_blk_enc(desc, dst, src, nbytes);
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+
+ blockno = 0;
+
+ err = blkcipher_walk_virt(desc, &walk);
+ while (err == 0 && (nbytes = walk.nbytes) > 0) {
+ op->src = walk.src.virt.addr,
+ op->dst = walk.dst.virt.addr;
+ op->flags = STMP3XXX_DCP_AES | STMP3XXX_DCP_ENC |
+ STMP3XXX_DCP_CBC;
+ if (blockno == 0) {
+ op->flags |= STMP3XXX_DCP_CBC_INIT;
+ memcpy(op->cipher.key + AES_KEYSIZE_128, walk.iv,
+ AES_KEYSIZE_128);
+ }
+ op->len = nbytes - (nbytes % AES_KEYSIZE_128);
+
+ /* key needs to be mapped only once */
+ op->cipher.key_phys = dma_map_single(sdcp->dev, op->cipher.key,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(sdcp->dev, op->cipher.key_phys)) {
+ dev_err(sdcp->dev, "Unable to map key\n");
+ return -ENOMEM;
+ }
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dev_err(sdcp->dev, "Unable to map source\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ op->dst_phys = dma_map_single(sdcp->dev, op->dst, op->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->dst_phys)) {
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+ dev_err(sdcp->dev, "Unable to map dest\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ /* perform! */
+ dcp_perform_op(op);
+
+ dma_unmap_single(sdcp->dev, op->cipher.key_phys,
+ AES_KEYSIZE_128 * 2, DMA_BIDIRECTIONAL);
+ dma_unmap_single(sdcp->dev, op->dst_phys, op->len,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len,
+ DMA_TO_DEVICE);
+
+ nbytes -= op->len;
+ ret = blkcipher_walk_done(desc, &walk, nbytes);
+
+ blockno++;
+ }
+
+ return err;
+}
+
+static struct crypto_alg dcp_aes_cbc_alg = {
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "dcp-cbc-aes",
+ .cra_priority = 400,
+ .cra_alignmask = 15,
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_init = fallback_init_blk,
+ .cra_exit = fallback_exit_blk,
+ .cra_blocksize = AES_KEYSIZE_128,
+ .cra_ctxsize = sizeof(struct stmp3xxx_dcp_op),
+ .cra_type = &crypto_blkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(dcp_aes_cbc_alg.cra_list),
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = dcp_aes_setkey_blk,
+ .encrypt = dcp_aes_cbc_encrypt,
+ .decrypt = dcp_aes_cbc_decrypt,
+ .ivsize = AES_KEYSIZE_128,
+ }
+ }
+};
+
+#ifndef DISABLE_SHA1
+
+static void dcp_perform_hash_op(struct stmp3xxx_dcp_hash_op *op)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct mutex *mutex;
+ int chan;
+ struct stmp3xxx_dcp_hw_packet *pkt;
+ unsigned long timeout;
+ u32 stat;
+ int size1, descno;
+
+ BUG_ON((op->flags & STMP3XXX_DCP_MODE_MASK) != STMP3XXX_DCP_SHA1);
+
+ chan = HASH_CHAN;
+
+ switch (op->flags & (STMP3XXX_DCP_INIT | STMP3XXX_DCP_UPDATE |
+ STMP3XXX_DCP_FINAL)) {
+
+ case STMP3XXX_DCP_INIT | STMP3XXX_DCP_UPDATE:
+
+ BUG_ON(op->len <= 1);
+
+ pkt = &op->hw->pkt[0];
+ pkt->pNext = 0;
+ pkt->pkt1 = BM_DCP_PACKET1_HASH_INIT |
+ BM_DCP_PACKET1_DECR_SEMAPHORE |
+ BM_DCP_PACKET1_INTERRUPT |
+ BM_DCP_PACKET1_ENABLE_HASH;
+ pkt->pkt2 = BF_DCP_PACKET2_HASH_SELECT(
+ BV_DCP_PACKET2_HASH_SELECT__SHA1);
+ pkt->pSrc = (u32)op->src_phys;
+ pkt->pDst = 0;
+ pkt->size = op->len - 1;
+ pkt->pPayload = 0;
+ pkt->stat = 0;
+
+ /* save last byte */
+ op->hw->rembuf[1] = ((u8 *)op->src)[op->len - 1];
+
+ descno = 1;
+ break;
+
+ case STMP3XXX_DCP_UPDATE:
+
+ BUG_ON(op->len <= 1);
+
+ op->hw->rembuf[0] = op->hw->rembuf[1];
+
+ pkt = &op->hw->pkt[0];
+ pkt->pNext = 0;
+ pkt->pkt1 = BM_DCP_PACKET1_CHAIN_CONTIGUOUS |
+ BM_DCP_PACKET1_ENABLE_HASH;
+ pkt->pkt2 = BF_DCP_PACKET2_HASH_SELECT(
+ BV_DCP_PACKET2_HASH_SELECT__SHA1);
+ pkt->pSrc = (u32)op->hw_phys + offsetof(
+ struct stmp3xxx_dcp_hash_coherent_block, rembuf);
+ pkt->pDst = 0;
+ pkt->size = 1;
+ pkt->pPayload = 0;
+ pkt->stat = 0;
+
+ pkt = &op->hw->pkt[1];
+ pkt->pNext = 0;
+ pkt->pkt1 = BM_DCP_PACKET1_DECR_SEMAPHORE |
+ BM_DCP_PACKET1_INTERRUPT |
+ BM_DCP_PACKET1_ENABLE_HASH;
+ pkt->pkt2 = BF_DCP_PACKET2_HASH_SELECT(
+ BV_DCP_PACKET2_HASH_SELECT__SHA1);
+ pkt->pSrc = (u32)op->src_phys;
+ pkt->pDst = 0;
+ pkt->size = op->len - 1;
+ pkt->pPayload = 0;
+ pkt->stat = 0;
+
+ /* save last byte */
+ op->hw->rembuf[1] = ((u8 *)op->src)[op->len - 1];
+
+ descno = 2;
+
+ break;
+
+ case STMP3XXX_DCP_FINAL:
+
+ op->hw->rembuf[0] = op->hw->rembuf[1];
+
+ pkt = &op->hw->pkt[0];
+ pkt->pNext = 0;
+ pkt->pkt1 = BM_DCP_PACKET1_HASH_TERM |
+ BM_DCP_PACKET1_DECR_SEMAPHORE |
+ BM_DCP_PACKET1_INTERRUPT |
+ BM_DCP_PACKET1_ENABLE_HASH;
+ pkt->pkt2 = BF_DCP_PACKET2_HASH_SELECT(
+ BV_DCP_PACKET2_HASH_SELECT__SHA1);
+ pkt->pSrc = (u32)op->hw_phys + offsetof(
+ struct stmp3xxx_dcp_hash_coherent_block, rembuf);
+ pkt->pDst = 0;
+ pkt->size = 1;
+ pkt->pPayload = (u32)op->hw_phys + offsetof(
+ struct stmp3xxx_dcp_hash_coherent_block, digest);
+ pkt->stat = 0;
+
+ descno = 1;
+
+ break;
+
+ /* all others BUG */
+ default:
+ BUG();
+ return;
+ }
+
+ mutex = &sdcp->op_mutex[chan];
+
+ /* submit the work */
+ mutex_lock(mutex);
+
+ HW_DCP_CHnSTAT_CLR(chan, -1);
+
+ mb();
+ /* Load the work packet pointer and bump the channel semaphore */
+ HW_DCP_CHnCMDPTR_WR(chan, (u32)op->hw_phys +
+ offsetof(struct stmp3xxx_dcp_hash_coherent_block, pkt[0]));
+
+ /* XXX wake from interrupt instead of looping */
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ HW_DCP_CHnSEMA_WR(chan, BF_DCP_CHnSEMA_INCREMENT(descno));
+
+ while (time_before(jiffies, timeout) &&
+ ((HW_DCP_CHnSEMA_RD(chan) >> 16) & 0xff) != 0)
+ cpu_relax();
+
+ if (!time_before(jiffies, timeout)) {
+ dev_err(sdcp->dev, "Timeout while waiting STAT 0x%08x\n",
+ HW_DCP_STAT_RD());
+ goto out;
+ }
+
+ stat = HW_DCP_CHnSTAT_RD(chan);
+ if ((stat & 0xff) != 0)
+ dev_err(sdcp->dev, "Channel stat error 0x%02x\n",
+ HW_DCP_CHnSTAT_RD(chan) & 0xff);
+
+out:
+ mutex_unlock(mutex);
+
+ mdelay(500);
+}
+
+static int fallback_init_hash(struct crypto_tfm *tfm)
+{
+ const char *name = tfm->__crt_alg->cra_name;
+ struct stmp3xxx_dcp_hash_op *op = crypto_tfm_ctx(tfm);
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+
+ op->fallback.hash = crypto_alloc_hash(name, 0,
+ CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(op->fallback.hash)) {
+ printk(KERN_ERR "Error allocating fallback algo %s\n", name);
+ return PTR_ERR(op->fallback.hash);
+ }
+
+ op->hw = dma_alloc_coherent(sdcp->dev, sizeof(*op->hw), &op->hw_phys,
+ GFP_KERNEL);
+ if (op->hw == NULL) {
+ printk(KERN_ERR "Error allocating coherent block for %s\n",
+ name);
+ crypto_free_hash(op->fallback.hash);
+ op->fallback.hash = NULL;
+ return -ENOMEM;
+ }
+ memset(op->hw, 0, sizeof(*op->hw));
+
+ return 0;
+}
+
+static void fallback_exit_hash(struct crypto_tfm *tfm)
+{
+ struct stmp3xxx_dcp_hash_op *op = crypto_tfm_ctx(tfm);
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+
+ dma_free_coherent(sdcp->dev, sizeof(*op->hw), op->hw, op->hw_phys);
+ crypto_free_hash(op->fallback.hash);
+ op->fallback.hash = NULL;
+}
+
+static void dcp_sha1_init(struct crypto_tfm *tfm)
+{
+ struct stmp3xxx_dcp_hash_op *op = crypto_tfm_ctx(tfm);
+
+ op->hash.init = 1;
+ memset(op->hw->rembuf, 0, sizeof(op->hw->rembuf));
+ memset(op->hw->digest, 0, sizeof(op->hw->digest));
+}
+
+static void dcp_sha1_update(struct crypto_tfm *tfm,
+ const uint8_t *data, unsigned int length)
+{
+ struct stmp3xxx_dcp *sdcp = global_sdcp;
+ struct stmp3xxx_dcp_hash_op *op = crypto_tfm_ctx(tfm);
+
+ op->src = (void *)data;
+ op->dst = NULL;
+ op->flags = STMP3XXX_DCP_SHA1 | STMP3XXX_DCP_UPDATE;
+ if (op->hash.init) {
+ op->hash.init = 0;
+ op->flags |= STMP3XXX_DCP_INIT;
+ }
+ op->len = length;
+
+ /* map the data */
+ op->src_phys = dma_map_single(sdcp->dev, op->src, op->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(sdcp->dev, op->src_phys)) {
+ dev_err(sdcp->dev, "Unable to map source\n");
+ return;
+ }
+ op->dst_phys = 0;
+
+ /* perform! */
+ dcp_perform_hash_op(op);
+
+ dma_unmap_single(sdcp->dev, op->src_phys, op->len, DMA_TO_DEVICE);
+}
+
+static void dcp_sha1_final(struct crypto_tfm *tfm, uint8_t *out)
+{
+ struct stmp3xxx_dcp_hash_op *op = crypto_tfm_ctx(tfm);
+ int i;
+ const uint8_t *digest;
+
+ op->src = NULL;
+ op->dst = NULL;
+ op->flags = STMP3XXX_DCP_SHA1 | STMP3XXX_DCP_FINAL;
+ op->len = 0;
+
+ /* perform! */
+ dcp_perform_hash_op(op);
+
+ /* hardware reverses the digest (for some unexplicable reason) */
+ digest = op->hw->digest + SHA1_DIGEST_SIZE;
+ for (i = 0; i < SHA1_DIGEST_SIZE; i++)
+ *out++ = *--digest;
+ memcpy(out, op->hw->digest, SHA1_DIGEST_SIZE);
+}
+
+static struct crypto_alg dcp_sha1_alg = {
+ .cra_name = "sha1",
+ .cra_driver_name = "dcp-sha1",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct stmp3xxx_dcp_hash_op),
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(dcp_sha1_alg.cra_list),
+ .cra_init = fallback_init_hash,
+ .cra_exit = fallback_exit_hash,
+ .cra_u = {
+ .digest = {
+ .dia_digestsize = SHA1_DIGEST_SIZE,
+ .dia_init = dcp_sha1_init,
+ .dia_update = dcp_sha1_update,
+ .dia_final = dcp_sha1_final,
+ }
+ }
+};
+#endif
+
+static irqreturn_t dcp_common_irq(int irq, void *context)
+{
+ struct stmp3xxx_dcp *sdcp = context;
+ u32 msk;
+
+ /* check */
+ msk = HW_DCP_STAT_RD() & BF_DCP_STAT_IRQ(0x0f);
+ if (msk == 0)
+ return IRQ_NONE;
+
+ /* clear this channel */
+ HW_DCP_STAT_CLR(msk);
+ if (msk & BF_DCP_STAT_IRQ(0x01))
+ sdcp->wait[0]++;
+ if (msk & BF_DCP_STAT_IRQ(0x02))
+ sdcp->wait[1]++;
+ if (msk & BF_DCP_STAT_IRQ(0x04))
+ sdcp->wait[2]++;
+ if (msk & BF_DCP_STAT_IRQ(0x08))
+ sdcp->wait[3]++;
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dcp_vmi_irq(int irq, void *context)
+{
+ return dcp_common_irq(irq, context);
+}
+
+static irqreturn_t dcp_irq(int irq, void *context)
+{
+ return dcp_common_irq(irq, context);
+}
+
+static int stmp3xxx_dcp_probe(struct platform_device *pdev)
+{
+ struct stmp3xxx_dcp *sdcp = NULL;
+ struct resource *r;
+ int i, ret;
+
+ if (global_sdcp != NULL) {
+ dev_err(&pdev->dev, "Only one instance allowed\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* allocate memory */
+ sdcp = kzalloc(sizeof(*sdcp), GFP_KERNEL);
+ if (sdcp == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate structure\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ sdcp->dev = &pdev->dev;
+ spin_lock_init(&sdcp->lock);
+
+ for (i = 0; i < STMP3XXX_DCP_NUM_CHANNELS; i++) {
+ mutex_init(&sdcp->op_mutex[i]);
+ init_completion(&sdcp->op_wait[i]);
+ }
+
+ platform_set_drvdata(pdev, sdcp);
+
+ /* Soft reset and remove the clock gate */
+ HW_DCP_CTRL_SET(BM_DCP_CTRL_SFTRST);
+
+ /* At 24Mhz, it takes no more than 4 clocks (160 ns) Maximum for
+ * the part to reset, reading the register twice should
+ * be sufficient to get 4 clks delay.
+ */
+ HW_DCP_CTRL_RD();
+ HW_DCP_CTRL_RD();
+
+ HW_DCP_CTRL_CLR(BM_DCP_CTRL_SFTRST | BM_DCP_CTRL_CLKGATE);
+
+ /* Initialize control registers */
+ HW_DCP_CTRL_WR(STMP3XXX_DCP_CTRL_INIT);
+ HW_DCP_CHANNELCTRL_WR(STMP3XXX_DCP_CHANNELCTRL_INIT);
+
+ /* We do not enable context switching. Give the context
+ * buffer pointer an illegal address so if context switching is
+ * inadvertantly enabled, the dcp will return an error instead of
+ * trashing good memory. The dcp dma cannot access rom, so any rom
+ * address will do.
+ */
+ HW_DCP_CONTEXT_WR(0xFFFF0000);
+
+ for (i = 0; i < STMP3XXX_DCP_NUM_CHANNELS; i++)
+ HW_DCP_CHnSTAT_CLR(i, -1);
+ HW_DCP_STAT_CLR(-1);
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "can't get IRQ resource (0)\n");
+ ret = -EIO;
+ goto err_unregister_sha1;
+ }
+ sdcp->dcp_vmi_irq = r->start;
+ ret = request_irq(sdcp->dcp_vmi_irq, dcp_vmi_irq, 0, "stmp3xxx-dcp",
+ sdcp);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't request_irq (0)\n");
+ goto err_unregister_sha1;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!r) {
+ dev_err(&pdev->dev, "can't get IRQ resource (1)\n");
+ ret = -EIO;
+ goto err_free_irq0;
+ }
+ sdcp->dcp_irq = r->start;
+ ret = request_irq(sdcp->dcp_irq, dcp_irq, 0, "stmp3xxx-dcp", sdcp);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't request_irq (1)\n");
+ goto err_free_irq0;
+ }
+
+ global_sdcp = sdcp;
+
+ ret = crypto_register_alg(&dcp_aes_alg);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register aes crypto\n");
+ goto err_kfree;
+ }
+
+ ret = crypto_register_alg(&dcp_aes_ecb_alg);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register aes ecb crypto\n");
+ goto err_unregister_aes;
+ }
+
+ ret = crypto_register_alg(&dcp_aes_cbc_alg);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register aes cbc crypto\n");
+ goto err_unregister_aes_ecb;
+ }
+
+#ifndef DISABLE_SHA1
+ ret = crypto_register_alg(&dcp_sha1_alg);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to register aes cbc crypto\n");
+ goto err_unregister_aes_cbc;
+ }
+#endif
+
+ dev_notice(&pdev->dev, "DCP crypto enabled.!\n");
+ return 0;
+
+err_free_irq0:
+ free_irq(sdcp->dcp_vmi_irq, sdcp);
+err_unregister_sha1:
+#ifndef DISABLE_SHA1
+ crypto_unregister_alg(&dcp_sha1_alg);
+#endif
+err_unregister_aes_cbc:
+ crypto_unregister_alg(&dcp_aes_cbc_alg);
+err_unregister_aes_ecb:
+ crypto_unregister_alg(&dcp_aes_ecb_alg);
+err_unregister_aes:
+ crypto_unregister_alg(&dcp_aes_alg);
+err_kfree:
+ kfree(sdcp);
+err:
+
+ return ret;
+}
+
+static int stmp3xxx_dcp_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_dcp *sdcp;
+
+ sdcp = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(sdcp->dcp_irq, sdcp);
+ free_irq(sdcp->dcp_vmi_irq, sdcp);
+#ifndef DISABLE_SHA1
+ crypto_unregister_alg(&dcp_sha1_alg);
+#endif
+ crypto_unregister_alg(&dcp_aes_cbc_alg);
+ crypto_unregister_alg(&dcp_aes_ecb_alg);
+ crypto_unregister_alg(&dcp_aes_alg);
+ kfree(sdcp);
+ global_sdcp = NULL;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int stmp3xxx_dcp_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int stmp3xxx_dcp_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define stmp3xxx_dcp_suspend NULL
+#define stmp3xxx_dcp_resume NULL
+#endif
+
+static struct platform_driver stmp3xxx_dcp_driver = {
+ .probe = stmp3xxx_dcp_probe,
+ .remove = stmp3xxx_dcp_remove,
+ .suspend = stmp3xxx_dcp_suspend,
+ .resume = stmp3xxx_dcp_resume,
+ .driver = {
+ .name = "stmp3xxx-dcp",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init
+stmp3xxx_dcp_init(void)
+{
+ return platform_driver_register(&stmp3xxx_dcp_driver);
+}
+
+static void __exit
+stmp3xxx_dcp_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_dcp_driver);
+}
+
+MODULE_AUTHOR("Pantelis Antoniou <pantelis@embeddedalley.com>");
+MODULE_DESCRIPTION("STMP3XXX DCP Crypto Driver");
+MODULE_LICENSE("GPL");
+
+module_init(stmp3xxx_dcp_init);
+module_exit(stmp3xxx_dcp_exit);
diff --git a/drivers/crypto/stmp3xxx_dcp.h b/drivers/crypto/stmp3xxx_dcp.h
new file mode 100644
index 000000000000..32ae0c704daf
--- /dev/null
+++ b/drivers/crypto/stmp3xxx_dcp.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 _STMP3XXX_DCP_H_
+#define _STMP3XXX_DCP_H_
+
+#include <mach/stmp3xxx_regs.h>
+#include <mach/stmp3xxx.h>
+#include <mach/regs-dcp.h>
+
+#define CIPHER_CHAN 1
+#define CIPHER_MASK (1 << CIPHER_CHAN)
+
+#define HASH_CHAN 0
+#define HASH_MASK (1 << HASH_CHAN)
+
+#define ALL_MASK (CIPHER_MASK | HASH_MASK)
+
+/* Defines the initialization value for the dcp control register */
+#define STMP3XXX_DCP_CTRL_INIT \
+ (BM_DCP_CTRL_GATHER_RESIDUAL_WRITES | \
+ BM_DCP_CTRL_ENABLE_CONTEXT_CACHING | \
+ BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH0 | \
+ BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH1 | \
+ BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH2 | \
+ BV_DCP_CTRL_CHANNEL_INTERRUPT_ENABLE__CH3)
+
+/* Defines the initialization value for the dcp channel control register */
+#define STMP3XXX_DCP_CHANNELCTRL_INIT \
+ BF_DCP_CHANNELCTRL_ENABLE_CHANNEL(ALL_MASK)
+
+/* DCP work packet 1 value for encryption */
+#define STMP3XXX_DCP_PKT1_ENCRYPT \
+ (BM_DCP_PACKET1_DECR_SEMAPHORE | \
+ BM_DCP_PACKET1_ENABLE_CIPHER | \
+ BM_DCP_PACKET1_CIPHER_ENCRYPT | \
+ BM_DCP_PACKET1_CIPHER_INIT)
+
+/* DCP work packet 1 value for decryption */
+#define DCP_PKT1_DECRYPT \
+ (BM_DCP_PACKET1_DECR_SEMAPHORE | \
+ BM_DCP_PACKET1_ENABLE_CIPHER | \
+ BM_DCP_PACKET1_CIPHER_INIT)
+
+/* DCP (decryption) work packet definition */
+struct stmp3xxx_dcp_hw_packet {
+ uint32_t pNext; /* next dcp work packet address */
+ uint32_t pkt1; /* dcp work packet 1 (control 0) */
+ uint32_t pkt2; /* dcp work packet 2 (control 1) */
+ uint32_t pSrc; /* source buffer address */
+ uint32_t pDst; /* destination buffer address */
+ uint32_t size; /* buffer size in bytes */
+ uint32_t pPayload; /* payload buffer address */
+ uint32_t stat; /* dcp status (written by dcp) */
+};
+
+#define STMP3XXX_DCP_NUM_CHANNELS 4
+
+#endif
diff --git a/drivers/fims/Kconfig b/drivers/fims/Kconfig
new file mode 100644
index 000000000000..f2462f7096cd
--- /dev/null
+++ b/drivers/fims/Kconfig
@@ -0,0 +1,251 @@
+#
+# FIM devices configuration
+#
+
+menuconfig FIM_CORE
+ tristate "FIM drivers support"
+ depends on PROCESSOR_NS921X
+ select FW_LOADER
+ select HOTPLUG
+ default n
+ help
+ This option adds the support for the Flexible Interfaces Modules (FIM).
+ Currently only the NS921X processors are supporting the FIM-devices.
+ The number of available FIMs is two for NS9215 and one for NS9210.
+
+ For installing the specific FIM-firmware, the FIM core driver depends on
+ Hotplug and the firmware-subsystem too.
+
+## If a FIM-driver is selected as loadable module, then is required
+## to have installed the user-space application 'mdev' (Busybox).
+
+
+if FIM_CORE
+
+config FIM_ZERO
+ bool "FIM 0"
+ depends on PROCESSOR_NS9210 || PROCESSOR_NS9215 && FIM_CORE=y
+ help
+ Enables FIM 0
+
+choice
+ prompt "Choose FIM 0 driver"
+ depends on FIM_ZERO
+ help
+ Only one of the following drivers can run in the FIM. Please pick one.
+
+config FIM_ZERO_SDIO_SELECTED
+ bool "SDIO driver"
+ depends on MMC && MMC_BLOCK && !(CME9210JS_SERIAL_PORTA_FULL || CME9210JS_SERIAL_PORTA_CTSRTSRXTX)
+ select FIM_ZERO_SDIO
+ help
+ Enables the FIM-SDIO driver.
+
+ For testing the SDIO functionality is required to choose an additional
+ SDIO-driver (GPS/UART) from the MMC-core.
+
+config FIM_ZERO_SERIAL_RXTX
+ bool "Serial driver 2-wires (RX/TX)"
+ depends on !(CME9210JS_SERIAL_PORTA_FULL || CME9210JS_SERIAL_PORTA_CTSRTSRXTX)
+ select FIM_ZERO_SERIAL_SELECTED
+ help
+ Enables the FIM-serial driver without the HW flow control (CTS and RTS
+ lines are not requested by the driver).
+
+config FIM_ZERO_SERIAL_CTSRTS
+ bool "Serial driver 4-wires (RX/TX/CTS/RTS)"
+ depends on !(CME9210JS_SERIAL_PORTA || CME9210JS_SERIAL_PORTA_FULL || CME9210JS_SERIAL_PORTA_CTSRTSRXTX)
+ select FIM_ZERO_SERIAL_SELECTED
+ help
+ Enables the FIM-serial driver with HW flow control support.
+
+config FIM_ZERO_CAN_SELECTED
+ bool "CAN-bus driver"
+ depends on CAN && CAN_DEV && (!MACH_CME9210 && !MACH_CME9210JS)
+ select FIM_ZERO_CAN
+ help
+ Enables the FIM-CAN bus driver.
+
+config FIM_ZERO_W1_SELECTED
+ bool "1-wire driver"
+ depends on !(CME9210JS_SERIAL_PORTA_FULL || CME9210JS_SERIAL_PORTA_CTSRTSRXTX)
+ select FIM_ZERO_W1
+ select W1
+ select W1_MASTER_GPIO
+ help
+ Enables the 1-wire GPIO driver on the FIM-board. For the selection of the
+ connected slaves you need to enter the config menu of the 1-wire subsystem.
+
+config FIM_ZERO_USB_SELECTED
+ tristate "USB serial port driver"
+ select FIM_ZERO_USB
+ select SERIAL_CORE
+ help
+ Enables the USB serial port driver (low speed).
+
+endchoice # FIM_ZERO
+
+config FIM_ONE
+ bool "FIM 1"
+ depends on PROCESSOR_NS9210 || PROCESSOR_NS9215 && FIM_CORE=y
+ help
+ Enables FIM 1
+
+choice
+ prompt "Choose FIM 1 driver"
+ depends on FIM_ONE
+ help
+ Only one of the following drivers can run in the FIM. Please pick one.
+
+config FIM_ONE_SDIO_SELECTED
+ bool "SDIO driver"
+ depends on MMC && MMC_BLOCK && (!MACH_CME9210 && !MACH_CME9210JS) && !FIM_ZERO_SDIO_SELECTED
+ select FIM_ONE_SDIO
+ help
+ Enables the FIM-SDIO serial driver.
+
+ For testing the SDIO functionality is required to choose an additional
+ SDIO-driver (GPS/UART) from the MMC-core.
+
+config FIM_ONE_SERIAL_RXTX
+ bool "Serial driver 2-wires (RX/TX)"
+ depends on !MACH_CME9210 && !MACH_CME9210JS
+ select FIM_ONE_SERIAL_SELECTED
+ help
+ Enables the FIM-serial driver without the HW flow control (CTS and RTS
+ lines are not requested by the driver).
+
+config FIM_ONE_SERIAL_CTSRTS
+ bool "Serial driver 4-wires (RX/TX/CTS/RTS)"
+ depends on !MACH_CME9210 && !MACH_CME9210JS
+ select FIM_ONE_SERIAL_SELECTED
+ help
+ Enables the FIM-serial driver with HW flow control support.
+
+config FIM_ONE_CAN_SELECTED
+ bool "CAN-bus driver"
+ depends on CAN && CAN_DEV && !((MACH_CME9210 || MACH_CME9210JS) && (CME9210JS_SERIAL_PORTA_FULL || FIM_ZERO_SERIAL_SELECTED || FIM_ZERO_SDIO_SELECTED))
+ select FIM_ONE_CAN
+ help
+ Enables the FIM-CAN bus driver.
+
+config FIM_ONE_W1_SELECTED
+ bool "1-wire driver"
+ depends on !MACH_CME9210 && !MACH_CME9210JS && !FIM_ZERO_W1_SELECTED
+ select FIM_ONE_W1
+ select W1_MASTER_GPIO
+ help
+ Enables the 1-wire GPIO driver on the FIM-board. For the selection of the
+ connected slaves you need to enter the config menu of the 1-wire subsystem.
+
+config FIM_ONE_USB_SELECTED
+ tristate "USB serial port driver"
+ depends on !MACH_CME9210JS
+ select FIM_ONE_USB
+ select SERIAL_CORE
+ help
+ Enables the USB serial port driver (low speed).
+
+endchoice # FIM_ONE
+
+# -- Special configuration options for some FIM-drivers
+config SERIAL_FIM_CONSOLE
+ bool "Enable the FIM Serial Console"
+ depends on FIM_ZERO_SERIAL_SELECTED || FIM_ONE_SERIAL_SELECTED
+ select SERIAL_CORE_CONSOLE
+ help
+ Enables the console functionality on the FIM serial ports.
+
+config FIM_MODULES
+ bool "Enable FIM drivers as loadable modules"
+ depends on !FIM_ZERO || !FIM_ONE
+ help
+ This option allows to build the FIM drivers as loadable modules
+ rather than as built-in features.
+
+if FIM_MODULES
+
+# -- These are the options for enabling the FIM-drivers as loadable modules
+config FIM_SDIO_MODULE
+ bool "SDIO as loadable module"
+ depends on MMC && MMC_BLOCK && !FIM_ZERO_SDIO_SELECTED && !FIM_ONE_SDIO_SELECTED && (!FIM_ZERO || !FIM_ONE)
+ select FIM_ZERO_SDIO
+ select FIM_ONE_SDIO if !(MACH_CME9210JS || MACH_CME9210)
+ help
+ Enables the FIM SDIO as loadable module. The number of FIMs to be
+ handled by this driver can be passed with the module parameter 'fims'
+
+config FIM_SERIAL_MODULE
+ bool "Serial as loadable module"
+ depends on !FIM_ZERO_SERIAL_SELECTED && !FIM_ONE_SERIAL_SELECTED && (!FIM_ZERO || !FIM_ONE)
+ select FIM_ZERO_SERIAL
+ select FIM_ONE_SERIAL if !(MACH_CME9210JS || MACH_CME9210)
+ select SERIAL_CORE
+ help
+ Enables the FIM Serial loadable module. The number of FIMs to be
+ handled by this driver can be passed with the module parameter 'fims'
+
+config FIM_CAN_MODULE
+ bool "CAN as loadable module"
+ depends on CAN && CAN_DEV && !FIM_ZERO_CAN_SELECTED && !FIM_ONE_CAN_SELECTED && (!FIM_ZERO || !FIM_ONE)
+ select FIM_ZERO_CAN if !(MACH_CME9210JS || MACH_CME9210)
+ select FIM_ONE_CAN
+ help
+ Enables the FIM CAN as loadable module. The number of FIMs to be
+ handled by this driver can be passed with the module parameter 'fims'
+
+config FIM_USB_MODULE
+ bool "USB serial port driver as loadable module"
+ depends on SERIAL_CORE && !FIM_ZERO_USB_SELECTED && !FIM_ONE_USB_SELECTED
+ select FIM_ZERO_USB
+ select FIM_ONE_USB if !(MACH_CME9210JS || MACH_CME9210)
+ help
+ Enables the FIM USB Device Controller as loadable module.
+ The number of FIMs to be handled by this driver can be passed with the
+ module parameter 'fims'
+
+endif # FIM_MODULES
+
+# -- Variables for selection the platform devices
+config FIM_ZERO_SDIO
+ bool
+
+config FIM_ONE_SDIO
+ bool
+
+config FIM_ZERO_SERIAL
+ bool
+
+config FIM_ZERO_SERIAL_SELECTED
+ select FIM_ZERO_SERIAL
+ select SERIAL_CORE
+ bool
+
+config FIM_ONE_SERIAL
+ bool
+
+config FIM_ONE_SERIAL_SELECTED
+ select FIM_ONE_SERIAL
+ select SERIAL_CORE
+ bool
+
+config FIM_ZERO_CAN
+ bool
+
+config FIM_ONE_CAN
+ bool
+
+config FIM_ZERO_W1
+ bool
+
+config FIM_ONE_W1
+ bool
+
+config FIM_ZERO_USB
+ bool
+
+config FIM_ONE_USB
+ bool
+
+endif # FIM_CORE
+
diff --git a/drivers/fims/Makefile b/drivers/fims/Makefile
new file mode 100644
index 000000000000..a6db651aa225
--- /dev/null
+++ b/drivers/fims/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the kernel FIM device drivers
+#
+
+obj-$(CONFIG_FIM_CORE) += fim.o
+obj-$(CONFIG_FIM_ZERO_SERIAL) += serial/
+obj-$(CONFIG_FIM_ZERO_SDIO) += sdio/
+obj-$(CONFIG_FIM_ZERO_CAN) += can/
+obj-$(CONFIG_FIM_ZERO_USB) += usb/
+obj-$(CONFIG_FIM_ONE_SERIAL) += serial/
+obj-$(CONFIG_FIM_ONE_SDIO) += sdio/
+obj-$(CONFIG_FIM_ONE_CAN) += can/
+obj-$(CONFIG_FIM_ONE_USB) += usb/ \ No newline at end of file
diff --git a/drivers/fims/can/Makefile b/drivers/fims/can/Makefile
new file mode 100644
index 000000000000..b48226c93b9f
--- /dev/null
+++ b/drivers/fims/can/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the kernel FIM device drivers
+#
+
+ifneq ($(CONFIG_FIM_CAN_MODULE),)
+ obj-m += fim_can.o
+else
+ obj-$(CONFIG_FIM_ZERO_CAN) += fim_can.o
+ obj-$(CONFIG_FIM_ONE_CAN) += fim_can.o
+endif
diff --git a/drivers/fims/can/README b/drivers/fims/can/README
new file mode 100644
index 000000000000..265258be3183
--- /dev/null
+++ b/drivers/fims/can/README
@@ -0,0 +1,3 @@
+You must license the CAN firmware driver from Digi before you can use it.
+Please register at http://www.digi.com/canbus to receive the required
+CAN bus support files.
diff --git a/drivers/fims/can/fim_can.c b/drivers/fims/can/fim_can.c
new file mode 100644
index 000000000000..f71eae26357b
--- /dev/null
+++ b/drivers/fims/can/fim_can.c
@@ -0,0 +1,1483 @@
+/* -*- linux-c -*-
+ *
+ * drivers/fims/fim_can.c
+ *
+ * Copyright (C) 2008-2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * !Revision: $Revision: 1.2 $
+ * !Author: Luis Galdos, Hector Oron
+ * !Descr:
+ * !References: Based on the virtual CAN driver (Copyright (c) 2002-2007
+ * Volkswagen Group Electronic Research)
+ * All rights reserved.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+/* Header files for the CAN-stack */
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+/* For registering the FIM-driver */
+#include <mach/fim-ns921x.h>
+#include <mach/gpio.h>
+
+/* If the driver is being to be loaded as a built-in driver, then include the header */
+#if !defined(MODULE)
+#include "fim_can.h"
+extern const unsigned char fim_can_firmware[];
+#define FIM_CAN_FIRMWARE_FILE (NULL)
+#define FIM_CAN_FIRMWARE_CODE fim_can_firmware
+#else
+const unsigned char *fim_can_firmware = NULL;
+#define FIM_CAN_FIRMWARE_FILE "fim_can.bin"
+#define FIM_CAN_FIRMWARE_CODE (NULL)
+#endif
+
+/* Driver informations */
+#define DRIVER_VERSION "1.2"
+#define DRIVER_AUTHOR "Luis Galdos, Hector Oron"
+#define DRIVER_DESC "FIM CAN bus driver"
+#define FIM_DRIVER_NAME "fim-can"
+#define FIM_DRIVER_FIRMWARE_NAME "fim_can.bin"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+/* Module parameter for selection the FIMs */
+NS921X_FIM_NUMBERS_PARAM(fims_number);
+
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] fim-can: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "fim-can: " fmt, ## args)
+#define printk_dbg(fmt, args...) printk(KERN_DEBUG "fim-can: " fmt, ## args)
+
+#if 0
+#define FIM_CAN_DEBUG
+#endif
+
+#ifdef FIM_CAN_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "fim-can: " fmt, ## args)
+#else
+# define printk_debug(fmt, args...) do { } while (0)
+#endif
+
+#define FIM_PRIVATE_BUFFER ((void *)0xffffffff)
+
+/* Available commands for the PIC */
+#define FIM_CAN_CMD_TX 0x00
+#define FIM_CAN_CMD_SHUTDOWN 0x40
+#define FIM_CAN_CMD_CONFIG 0x41
+#define FIM_CAN_CMD_FILTER(i) (0x80 + i)
+#define FIM_CAN_ERR_FILTER(i) (i > 0x87 || i < 0x80)
+#define FIM_CAN_NR_FILTERS (8)
+
+/* Default timing values (see Net+OS driver: nacbus.c) */
+#define FIM_CAN_DEFAULT_SJW (7)
+#define FIM_CAN_DEFAULT_PROPSEG (8)
+#define FIM_CAN_DEFAULT_PHASE1 (8)
+#define FIM_CAN_DEFAULT_PHASE2 (8)
+#define FIM_CAN_DEFAULT_BITRATE CAN_BITRATE_DEFAULT
+
+/* Maximal values from the Net+OS driver */
+#define FIM_CAN_MAX_BITRATE (1000000)
+
+#define CAN_BUS_RECESSIVE (0xffffffff)
+#define CAN_BUS_DOMINANT (0x00000000)
+
+/* Status register according to the FIM-firmware specification (page 9) */
+#define FIM_CAN_BUSSTATE_REG 0
+#define FIM_CAN_TXERRCNT_REG 1
+#define FIM_CAN_RXERRCNT_REG 2
+#define FIM_CAN_FATALERR_REG 3
+
+/* Control bits of the bus state register */
+#define FIM_CAN_BUSSTATE_ERRPAS 0x01
+#define FIM_CAN_BUSSTATE_OFF 0x02
+
+/* Control bits of the fatal error register */
+#define FIM_CAN_FATAL_CONTROLLER 0x01
+#define FIM_CAN_FATAL_CMDERR 0x40
+#define FIM_CAN_FATAL_HW 0x41
+#define FIM_CAN_FATAL_FILTER 0x42
+#define FIM_CAN_FATAL_CONFIG 0x43
+
+/* Interrupts from the FIM to the ARM (see can_defs.inc) */
+#define FIM_CAN_INT_RESET (0x01)
+#define FIM_CAN_INT_CMD_ERR (0x45)
+#define FIM_CAN_INT_HW_FAILURE (0x41)
+#define FIM_CAN_INT_FILTER_ERR (0x42)
+#define FIM_CAN_INT_CFG_ERR (0x43)
+
+/*
+ * Macros for the configuration of the DMA-channel
+ */
+#define FIM_CAN_DMA_BUFFER_SIZE (256)
+#define FIM_CAN_DMA_BUFFERS (60)
+
+/*
+ * @XXX: Remove this ugly macro and use a structure for the PIC-message according
+ * to the specification, page 8 (Transmit message)
+ */
+#define FIRST_DATA_BYTE_IN_MESSAGE (3)
+
+/*
+ * GPIO configuration
+ */
+#define FIM_CAN_MAX_GPIOS (2)
+
+/* Structure for setting the ID-filters */
+#define FIM_CAN_FILTER_SIZE (8)
+struct fim_can_filter_t {
+ u8 code;
+ u8 filter[FIM_CAN_FILTER_SIZE];
+};
+
+/*
+ * Initializing some needed variables. This is important step
+ * as CAN driver might not work upon this values
+ */
+static struct can_bittiming_const fim_bittiming_const = {
+ .tseg1_min = 1,
+ .tseg1_max = 200,
+ .tseg2_min = 1,
+ .tseg2_max = 200,
+ .sjw_max = 40,
+ .brp_min = 1,
+ .brp_max = 2048,
+ .brp_inc = 1,
+};
+
+/*
+ * This structure provides the interface for configuring the timing parameters of
+ * the CAN-controller.
+ */
+struct fim_can_timing_t {
+ u8 code;
+ u16 sjw;
+ u16 cspl;
+ u16 cp2pl;
+ u16 csi;
+ u16 cp2i;
+ u16 csp;
+} __attribute__ ((packed));
+
+/*
+ * Structure for sending the shutdown command to the FIM
+ * This command is required before stopping the FIM, otherwise a transmitted
+ * frame can be corrupted
+ */
+struct fim_can_shutdown_t {
+ u8 code;
+};
+
+/*
+ * Structure for sending user-data to the controller
+ * @XXX: The maximal frame length is just an arbitrary value (can be modified)
+ */
+#define FIM_CAN_FRAME_LENGTH 128
+struct fim_can_txframe {
+ u16 crc;
+ u32 bytepos;
+ u32 bitcnt;
+ u32 bitstuff;
+ u32 last5;
+ u32 stuffed;
+ u32 len;
+ u32 result;
+ u8 buffer[FIM_CAN_FRAME_LENGTH];
+};
+
+/* @FIXME: Use the generic structure for the CAN-frames from <include/linux/can.h> */
+struct fim_can_rxframe {
+ u8 id[4];
+ u8 ctrl;
+ u8 data[8];
+} __attribute__ ((packed));
+
+struct fim_can_t {
+ struct can_priv can;
+ int index;
+ struct fim_driver fim;
+ struct semaphore sem;
+ struct net_device *dev;
+ int reg;
+ struct fim_gpio_t gpios[FIM_CAN_MAX_GPIOS];
+ struct clk *cpu_clk;
+ int opened;
+ spinlock_t lock;
+};
+
+/* Function prototypes */
+static int fim_can_send_skb(struct fim_can_t *port, unsigned char *data, int len,
+ struct sk_buff *skb);
+static int fim_can_send_buffer(struct fim_can_t *port, unsigned char *data, int len);
+static int fim_can_restart_fim(struct fim_can_t *port);
+static int fim_can_start_fim(struct fim_can_t *port);
+static int fim_can_stop_fim(struct fim_can_t *port);
+
+/* Debug function for dumping the CAN-frames for the FIM-firmware */
+#if defined(FIM_CAN_DEBUG)
+#define fim_can_dump_frame(f, m) fim_can_do_dump_frame(f, m)
+#else
+#define fim_can_dump_frame(f, m) do { } while (0)
+#endif
+inline static void fim_can_do_dump_frame(struct fim_can_txframe *frame, char *marke)
+{
+ int cnt;
+ char buffer[512];
+ unsigned int pos;
+ int len;
+ unsigned char *data;
+
+ len = 3 + (frame->stuffed + 7) / 8;
+ data = (unsigned char *)(frame->buffer);
+ pos = 0;
+ for (cnt = 0; cnt < len; cnt++)
+ pos += snprintf(buffer + pos, sizeof(buffer), "0x%02x ", *(data + cnt));
+
+ printk_debug("%s buffer : %s\n", marke, buffer);
+ printk_debug("%s infos :\n\
+\tCRC : 0x%x\n\
+\tbytePos : 0x%x\n\
+\tbitCount : 0x%x\n\
+\tbitStuff : 0x%x\n\
+\tlast 5 : 0x%x\n\
+\tstuffed count : 0x%x\n",
+ marke,
+ frame->crc,
+ frame->bytepos,
+ frame->bitcnt,
+ frame->bitstuff,
+ frame->last5,
+ frame->stuffed);
+}
+
+/* Debug function for dumping the CAN-timing parameters */
+#if defined(FIM_CAN_DEBUG)
+#define fim_can_dump_timing(cfg, s, p, y, b) fim_can_do_dump_timing(cfg, s, p, y, b)
+#else
+#define fim_can_dump_timing(cfg, s, p, y, b) do { } while (0)
+#endif
+
+/* Print the timing values for the controller */
+inline static void fim_can_do_dump_timing(struct fim_can_timing_t *cfg,
+ unsigned int sjw, unsigned int sample_point,
+ unsigned int sync_period, unsigned int bitrate)
+{
+ printk_debug("SJW %u | Sample point %u | Sync period %u | Bitrate %u\n\
+\tSJW : 0x%04x\n\
+\tCSPL : 0x%04x\n\
+\tCP2PL : 0x%04x\n\
+\tCSI : 0x%04x\n\
+\tCP2I : 0x%04x\n\
+\tCSP : 0x%04x\n\
+",
+ sjw,
+ sample_point,
+ sync_period,
+ bitrate,
+ cfg->sjw,
+ cfg->cspl,
+ cfg->cp2pl,
+ cfg->csi,
+ cfg->cp2i,
+ cfg->csp);
+}
+
+inline static void fim_can_parse_filter(struct fim_can_filter_t *flt, canid_t id,
+ canid_t mask)
+{
+ flt->filter[0] = (id >> 24);
+ flt->filter[1] = (mask >> 24);
+ flt->filter[2] = (id >> 16);
+ flt->filter[3] = (mask >> 16);
+ flt->filter[4] = (id >> 8);
+ flt->filter[5] = (mask >> 8);
+ flt->filter[6] = id;
+ flt->filter[7] = mask;
+}
+
+/* Calculate the latency times for the passed bitrate */
+static int fim_can_calculate_latency(u32 bitrate, u16 *rx, u16 *tx)
+{
+ int ret;
+
+ if (bitrate <= 0 || bitrate > FIM_CAN_MAX_BITRATE)
+ return -ERANGE;
+
+ /*
+ * In the future we will probably need different latency times for the
+ * bitrates. Let us keep this function for this purpose.
+ */
+#define FIM_CAN_RXIRQ_LATENCY (1)
+#define FIM_CAN_TXIRQ_LATENCY (100)
+ *rx = FIM_CAN_RXIRQ_LATENCY;
+ *tx = FIM_CAN_TXIRQ_LATENCY;
+ ret = 0;
+
+ return ret;
+}
+
+static int fim_can_shutdown(struct fim_can_t *port)
+{
+ struct fim_buffer_t buf;
+ struct fim_can_shutdown_t cmd;
+
+ cmd.code = FIM_CAN_CMD_SHUTDOWN;
+
+ buf.private = FIM_PRIVATE_BUFFER;
+ buf.length = sizeof(struct fim_can_shutdown_t);
+ buf.data = (unsigned char *)&cmd;
+
+ return 0;
+}
+
+/* Configures all timing values */
+static void fim_can_fill_timing(struct fim_can_t *port, struct fim_can_timing_t *tim,
+ u16 sjw, u16 sample_point, u16 sync_period)
+{
+ unsigned int ten_per;
+ u16 larx, latx;
+ struct can_bittiming *bt;
+
+ bt = &port->can.bittiming;
+ printk_debug("New timing: %u bps | sjw %u | sample %u | sync %u | clock %u\n",
+ bt->bitrate,
+ sjw,
+ sample_point,
+ sync_period,
+ bt->clock);
+
+ if (fim_can_calculate_latency(bt->bitrate, &larx, &latx)) {
+ printk_err("Failed latency calculation for rate %u\n", bt->bitrate);
+ return;
+ }
+
+ /* Calculate how many clocks are in the ten percent of the bit rate */
+ ten_per = (clk_get_rate(port->cpu_clk) + (5 * bt->bitrate)) / (10 * bt->bitrate);
+ printk_debug("Ten percent by %i Bps is aprox. %i\n", bt->bitrate, ten_per);
+
+ tim->sjw = (sjw * ten_per + 5) / 10;
+ tim->cspl = (sample_point * ten_per + 5) / 10;
+ tim->cp2pl = (((100 - sample_point) * ten_per) + 5) / 10;
+ tim->csi = larx;
+ tim->cp2i = latx;
+ tim->csp = ((sync_period * ten_per) + 5) / 10;
+
+ printk_debug("sjw: %d \tcspl: %d \tcp2pl: %d \tcsi: %d \tcp2i: %d \tcsp: %d\n",
+ tim->sjw, tim->cspl, tim->cp2pl,
+ tim->csi, tim->cp2i, tim->csp);
+}
+
+/* Configure the filter for accepting all the incoming messages */
+inline static void fim_can_filter_accept(struct fim_can_filter_t *filter)
+{
+ memset(filter->filter, 0x00, FIM_CAN_FILTER_SIZE);
+}
+
+/* Configure the filter for rejecting all the incoming messages */
+inline static void fim_can_filter_reject(struct fim_can_filter_t *filter)
+{
+ memset(filter->filter, 0x20, FIM_CAN_FILTER_SIZE);
+}
+
+/*
+ * This function inits the CAN-controller according to its specification.
+ * Unfortunately the firmware expects the configuration data and the filters in
+ * only ONE message. For this reason we need a separated function only for the
+ * init-configuration
+ */
+static int fim_can_set_init_config(struct fim_can_t *port)
+{
+ struct fim_can_timing_t cfg;
+ struct fim_can_filter_t filters[FIM_CAN_NR_FILTERS];
+ int cnt, retval;
+ unsigned char *ptr;
+ unsigned char data[sizeof(struct fim_can_timing_t) + (FIM_CAN_NR_FILTERS * 8)];
+ struct can_bittiming *bt;
+
+ /* Init the timing parameters */
+ cfg.code = FIM_CAN_CMD_CONFIG;
+ bt = &port->can.bittiming;
+
+ if (bt->bitrate == 0) {
+ printk("Sorry, bitrate required for\n");
+ return -EINVAL;
+ }
+
+ /* We must use the default bit rates for our FIM-firmware */
+ if (!bt->sjw)
+ bt->sjw = FIM_CAN_DEFAULT_SJW;
+ if (!bt->prop_seg)
+ bt->prop_seg = FIM_CAN_DEFAULT_PROPSEG;
+
+ if (!bt->phase_seg1)
+ bt->phase_seg1 = FIM_CAN_DEFAULT_PHASE1;
+
+ if (!bt->phase_seg2)
+ bt->phase_seg2 = FIM_CAN_DEFAULT_PHASE2;
+
+ fim_can_fill_timing(port, &cfg,
+ bt->sjw,
+ bt->prop_seg + bt->phase_seg1 + 1,
+ bt->phase_seg2);
+
+ fim_can_do_dump_timing(&cfg,
+ bt->sjw,
+ bt->prop_seg + bt->phase_seg1 + 1,
+ bt->phase_seg2,
+ bt->bitrate);
+
+ /* Not init the filter with the default values */
+ for (cnt = 0; cnt < FIM_CAN_NR_FILTERS; cnt++)
+ fim_can_filter_accept(&filters[cnt]);
+
+ /* Now setup the init message */
+ memcpy(data, &cfg, sizeof(struct fim_can_timing_t));
+ ptr = data + sizeof(struct fim_can_timing_t);
+ for (cnt = 0; cnt < FIM_CAN_NR_FILTERS; cnt++)
+ memcpy(ptr, filters[cnt].filter, FIM_CAN_FILTER_SIZE);
+
+ retval = fim_can_send_buffer(port, data, sizeof(data));
+ if (retval)
+ printk_err("Couldn't send the init config.\n");
+
+ return retval;
+}
+
+/*
+ * Implement the interface for chaning the state of the CAN-controller. The user
+ * have access to this function over the sysfs attributes:
+ * echo 2 > sys/class/net/can0/can_restart : mode = 1 (CAN_MODE_START)
+ *
+ */
+static int fim_can_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ struct fim_can_t *port;
+ struct fim_driver *fim;
+ int retval;
+
+ port = netdev_priv(dev);
+ fim = &port->fim;
+
+ printk_debug("Set mode command %x called (FIM %i)\n", mode, fim->picnr);
+
+ if (!port->opened) {
+ printk_err("CAN-port %i was not opened up now\n", fim->picnr);
+ return -ERESTARTSYS;
+ }
+
+ /*
+ * The different modes can be set using the sysfs attributes:
+ * start : /sys/class/net/can0/can_restart
+ */
+ switch (mode) {
+ case CAN_MODE_START:
+ printk_dbg("FIM%i: Starting the controller\n", fim->picnr);
+ retval = fim_can_restart_fim(port);
+ break;
+ case CAN_MODE_STOP:
+ printk_dbg("FIM%i: Stopping the controller\n", fim->picnr);
+ retval = fim_can_stop_fim(port);
+ break;
+ default:
+ retval = -EOPNOTSUPP;
+ break;
+ }
+
+ return retval;
+}
+
+/*
+ * Read the status registers of the FIM for obtaining the status information.
+ */
+static int fim_can_get_state(struct net_device *dev, enum can_state *state)
+{
+ struct fim_can_t *port;
+ struct fim_driver *fim;
+ int retval;
+ unsigned int bus, fatal;
+
+ port = netdev_priv(dev);
+ fim = &port->fim;
+ printk_debug("Get state called (FIM %i)\n", fim->picnr);
+
+ retval = fim_get_exp_reg(fim, FIM_CAN_BUSSTATE_REG, &bus);
+ if (retval) {
+ printk_err("Reading the bus state register (%i)\n", retval);
+ goto exit_all;
+ }
+
+ retval = fim_get_exp_reg(fim, FIM_CAN_FATALERR_REG, &fatal);
+ if (retval) {
+ printk_err("Reading the fatal error register (%i)\n", retval);
+ goto exit_all;
+ }
+
+ /* @XXX: Are we using the correct flag for informing about the error? */
+ if (fatal) {
+ *state = CAN_STATE_BUS_OFF;
+ goto exit_all;
+ }
+
+ /* Check the current status of the BUS */
+ if (port->can.state == CAN_STATE_STOPPED) {
+ printk_debug("Controller was stopped\n");
+ *state = CAN_STATE_STOPPED;
+ } else if (bus & FIM_CAN_BUSSTATE_OFF) {
+ printk_debug("Current Bus state seems to be OFF\n");
+ *state = CAN_STATE_BUS_OFF;
+ } else if (bus & FIM_CAN_BUSSTATE_ERRPAS) {
+ printk_debug("Bus seems to be in the passive error state\n");
+ *state = CAN_STATE_BUS_PASSIVE;
+ } else
+ *state = CAN_STATE_ACTIVE;
+
+ retval = 0;
+
+exit_all:
+ return retval;
+}
+
+/* Return zero if the FIM was successful stopped */
+static int fim_can_stop_fim(struct fim_can_t *port)
+{
+ int retval;
+ struct fim_driver *fim;
+
+ if (!port)
+ return -ENODEV;
+
+ /*
+ * We will disable the IRQ anyway (otherwise is too dangerous having the
+ * active interrupt)
+ */
+ retval = 0;
+ fim = &port->fim;
+ if (fim_is_running(fim)) {
+ retval = fim_send_stop(fim);
+ printk_debug("%s: Disabling interrupt\n",__func__);
+ fim_disable_irq(fim);
+ }
+
+ return retval;
+}
+
+/*
+ * This function will download the firmware, start and reconfigure the CAN-controller
+ * For starting the FIM we assume that it was already stopped!
+ */
+static int fim_can_start_fim(struct fim_can_t *port)
+{
+ struct fim_driver *fim;
+ int retval;
+
+ if (!port)
+ return -ENODEV;
+
+ fim = &port->fim;
+ if (fim_is_running(fim)) {
+ printk_debug("The FIM %i is already running\n", fim->picnr);
+ return 0;
+ }
+
+ /*
+ * The function for downloading the firmware will stop the FIM if
+ * its still running
+ */
+ retval = fim_download_firmware(fim);
+ if (retval)
+ return -EAGAIN;
+
+ retval = fim_send_start(fim);
+ if (retval)
+ return -EAGAIN;
+
+ printk_debug("Enable interrupt\n");
+ fim_enable_irq(fim);
+
+ /*
+ * Disable the interrupt if an error is detected, otherwise the system
+ * can hang up
+ */
+ retval = fim_can_set_init_config(port);
+ if (retval) {
+ printk_debug("%s: Disabling interrupt\n",__func__);
+ fim_disable_irq(fim);
+ return retval;
+ }
+
+ return retval;
+}
+
+/*
+ * This function restart the FIM
+ * It includes the reconfiguration of the CAN-port too (it will send the init
+ * configuration to the port too)
+ */
+static int fim_can_restart_fim(struct fim_can_t *port)
+{
+ int retval;
+
+ if (!port)
+ return -ENODEV;
+
+ /* First try to stop the FIM */
+ retval = fim_can_stop_fim(port);
+ if (retval)
+ return retval;
+
+ return fim_can_start_fim(port);
+}
+
+/*
+ * This function is called when the interface is going up (ifconfig <iface> up)
+ * Only enable the interrupt and restart the netdev queue.
+ */
+static int fim_can_open(struct net_device *dev)
+{
+ struct fim_can_t *port;
+ struct fim_driver *fim;
+ int retval;
+
+ /* So, lets start the PIC with our default data */
+ port = netdev_priv(dev);
+ fim = &port->fim;
+ printk_debug("Open function called (FIM %i)\n", fim->picnr);
+
+ /* Always set the CAN-interface to the active state! */
+ retval = fim_can_restart_fim(port);
+ if (!retval) {
+ netif_start_queue(dev);
+ port->can.state = CAN_STATE_ACTIVE;
+ port->opened = 1;
+ }
+
+ return retval;
+}
+
+/*
+ * This function is called when the interface is going down (ifconfig <iface> down)
+ * Disable the IRQ and the netif queue.
+ */
+static int fim_can_stop(struct net_device *dev)
+{
+ struct fim_can_t *port;
+ struct fim_driver *fim;
+ int retval = 0;
+
+ port = netdev_priv(dev);
+ fim = &port->fim;
+ printk_debug("Close function called (FIM %i)\n", fim->picnr);
+
+ /* Paranoic sanity check */
+ if (!port->opened)
+ return 0;
+
+ netif_stop_queue(dev);
+ can_close_cleanup(dev);
+ port->can.state = CAN_STATE_STOPPED;
+ port->opened = 0;
+
+ /* Transmit a shutdown command to the FIM before stopping it */
+ if (fim_is_running(fim)) {
+ retval = fim_can_shutdown(port);
+ if (retval) {
+ printk_err("Unable to shutdown the FIM %i\n", fim->picnr);
+ goto err_exit;
+ }
+
+ /*
+ * The below delay is arbitrary and should give the FIM some time
+ * for processing the last CAN-frame on the bus
+ */
+ udelay(1000);
+
+ /* Check if need to stop the FIM */
+ printk_debug("%s: Disabling interrupt\n", __func__);
+ fim_disable_irq(fim);
+ fim_send_stop(fim);
+ }
+
+err_exit:
+ return retval;
+}
+
+/*
+ * This function will save the SKB-pointer inside the FIM-buffer. By this way
+ * is possible to free the SKB inside the TX-callback
+ */
+static int fim_can_send_skb(struct fim_can_t *port, unsigned char *data, int len,
+ struct sk_buff *skb)
+{
+ struct fim_buffer_t buf;
+
+ buf.private = skb;
+ buf.length = len;
+ buf.data = data;
+ return fim_send_buffer(&port->fim, &buf);
+}
+
+/*
+ * This function will mask the FIM-buffer so that in the TX-callback we can
+ * know that it doesn't contain any important information for us
+ */
+static int fim_can_send_buffer(struct fim_can_t *port, unsigned char *data, int len)
+{
+ struct fim_buffer_t buf;
+
+ buf.private = FIM_PRIVATE_BUFFER;
+ buf.length = len;
+ buf.data = data;
+ return fim_send_buffer(&port->fim, &buf);
+}
+
+/*
+ * Write one bit to the frame, update the CRC, and bit stuff if we have
+ * 5 consecutive bits all of the same value.
+ */
+#define LOWEST_FIVE_BITS_MASK (0x1f)
+static void fim_can_write_stuffbit(struct fim_can_txframe *frame, unsigned bit)
+{
+ bit &= 1;
+
+ frame->buffer[frame->bytepos] = (frame->buffer[frame->bytepos] << 1) | bit;
+ frame->last5 = ((frame->last5 << 1) | bit) & LOWEST_FIVE_BITS_MASK;
+ frame->bitcnt++;
+ frame->stuffed++;
+
+ if (frame->bitcnt == 8) {
+ frame->bytepos += 1;
+ frame->bitcnt = 0;
+ }
+
+ if (frame->bitstuff && ((frame->last5 == LOWEST_FIVE_BITS_MASK) ||
+ (frame->last5 == 0)))
+ fim_can_write_stuffbit(frame, ~frame->last5);
+}
+
+/*
+ * Real CAN polynomial is 0xc599, but this value used used
+ * do to the way the algorithm works. The high bit is
+ * xor'ed in the first line.
+ */
+#define CAN_CRC_POLYNOMIAL_LESS_HIGH_BIT (0x4599)
+inline static void fim_can_add_crc(struct fim_can_txframe *frame, unsigned short bit)
+{
+ unsigned short crcnxt;
+
+ frame->crc <<= 1;
+ crcnxt = ((frame->crc & 0x8000) != 0) ^ (bit);
+ frame->crc &= 0x7ffe;
+ if (crcnxt)
+ frame->crc ^= CAN_CRC_POLYNOMIAL_LESS_HIGH_BIT;
+}
+
+inline static void fim_can_frame_writeone(struct fim_can_txframe *frame, u32 bit)
+{
+ fim_can_add_crc(frame, bit);
+ fim_can_write_stuffbit(frame, bit);
+}
+
+/*
+ * Write one or more bits to the frame.
+ */
+inline static void fim_can_frame_write(struct fim_can_txframe *frame, unsigned int data,
+ int bits)
+{
+ int idx;
+ for (idx = bits - 1; idx >= 0; idx--)
+ fim_can_frame_writeone(frame, (data & (1 << idx)) != 0);
+}
+
+inline static void fim_can_frame_stop_stuff(struct fim_can_txframe *frame)
+{
+ frame->bitstuff = 0;
+}
+
+inline static void fim_can_frame_start(struct fim_can_txframe *frame)
+{
+ frame->crc = 0;
+ frame->bytepos = FIRST_DATA_BYTE_IN_MESSAGE;
+
+ frame->last5 = CAN_BUS_RECESSIVE;
+ frame->bitcnt = 0;
+ frame->bitstuff = 1;
+ frame->stuffed = 0;
+ fim_can_frame_writeone(frame, CAN_BUS_DOMINANT);
+}
+
+inline static void fim_can_frame_stop(struct fim_can_txframe *frame)
+{
+ frame->buffer[frame->bytepos] <<= (8 - frame->bitcnt);
+ frame->buffer[frame->bytepos] |= (0xFF >> frame->bitcnt);
+}
+
+/*
+ * Function that checks if the CAN-controller registered an error
+ * If an error is detected, then a error frame will be passed to the socket-layer
+ * Return zero if no error was detected, otherwise an value different than zero
+ */
+static int fim_can_check_error(struct net_device *dev)
+{
+ struct sk_buff *skb;
+ struct fim_can_t *port;
+ struct fim_driver *fim;
+ struct can_frame *cf;
+ unsigned int bus, fatal;
+ struct net_device_stats *stats;
+
+ port = netdev_priv(dev);
+ fim = &port->fim;
+ fim_get_exp_reg(fim, FIM_CAN_BUSSTATE_REG, &bus);
+ fim_get_exp_reg(fim, FIM_CAN_FATALERR_REG, &fatal);
+
+ if ((!bus) && (!fatal))
+ return 0;
+
+ printk_debug("Calling the error handling function (Bus 0x%x)\n", bus);
+
+ /* Stop the FIM if it's running */
+ if (fim_is_running(fim)) {
+ printk_debug("%s: Disabling interrupt\n",__func__);
+ fim_disable_irq(fim);
+ fim_send_stop(fim);
+ }
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ printk_err("Couldn't create the ERROR frame\n");
+ goto end_exit;
+ }
+
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0x00, sizeof(struct can_frame));
+ cf->can_id = CAN_ERR_FLAG;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ /*
+ * These are the possible errors defined in the FIM-CAN specification
+ * The corresponding error flags are under: include/linux/can/error.h
+ */
+ if (bus & FIM_CAN_BUSSTATE_OFF)
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ /* @XXX: This seems to be the correct flags for our controller, or? */
+ if (bus & FIM_CAN_BUSSTATE_ERRPAS) {
+ cf->can_id |= CAN_ERR_BUSERROR;
+ cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+ port->can.can_stats.error_passive++;
+ }
+
+ /* Pass the error frame to the socket layer */
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats = dev->get_stats(dev);
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+end_exit:
+ return 1;
+}
+
+static int fim_can_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct fim_can_t *port;
+ struct net_device_stats *stats;
+ struct can_frame *cf;
+ struct fim_can_txframe frame;
+ u8 dlc, cnt;
+ int retval;
+ canid_t id;
+
+ port = netdev_priv(dev);
+ spin_lock(&port->lock);
+ netif_stop_queue(dev);
+ spin_unlock(&port->lock);
+
+ stats = dev->get_stats(dev);
+ cf = (struct can_frame *)skb->data;
+
+ id = cf->can_id;
+ dlc = cf->can_dlc;
+
+ /*
+ * Check for the bus state before sending the data
+ * @FIXME: The controller fails if its state is the error passive
+ */
+ if (fim_can_check_error(dev)) {
+ printk_debug("Bus OFF or unknown error. Aborting Xmit %p\n", skb);
+ stats->tx_dropped++;
+ spin_lock(&port->lock);
+ dev_kfree_skb(skb);
+ netif_wake_queue(dev);
+ spin_unlock(&port->lock);
+ return NETDEV_TX_OK;
+ }
+
+ /* Reset our frame first */
+ memset(&frame, 0x0, sizeof(struct fim_can_txframe));
+
+ fim_can_frame_start(&frame);
+ fim_can_dump_frame(&frame, "START");
+
+ /* Check for the extended ID (18 bits length) */
+ if (id & CAN_EFF_FLAG)
+ fim_can_frame_write(&frame, id, 18);
+ else
+ fim_can_frame_write(&frame, id, 11);
+
+ fim_can_dump_frame(&frame, "ID");
+
+ /* Check if we have a RTR message */
+ if (id & CAN_RTR_FLAG)
+ fim_can_frame_write(&frame, 1, 1);
+ else
+ fim_can_frame_write(&frame, 0, 1);
+ fim_can_dump_frame(&frame, "RTR");
+
+ /* Write the identifier bit which is dominant in the standard frame */
+ fim_can_frame_write(&frame, 0, 1);
+ fim_can_dump_frame(&frame, "IDE");
+
+ /* Write the R0 bit */
+ fim_can_frame_write(&frame, 0, 1);
+ fim_can_dump_frame(&frame, "RESERVE");
+
+ /* Write the control field (data length passed by the user) */
+ fim_can_frame_write(&frame, dlc, 4);
+ fim_can_dump_frame(&frame, "CONTROL");
+
+ /*
+ * Now write the data bytes (maximal eight bytes)
+ * According to the CAN-specification, don't add the data bytes to the frame
+ * although the data length code is greter than zero
+ */
+ for (cnt = 0; cnt < (dlc & 0x0F) && !(id & CAN_RTR_FLAG); cnt++)
+ fim_can_frame_write(&frame, cf->data[cnt], 8);
+
+ fim_can_dump_frame(&frame, "DATA");
+
+ /* Write the CRC */
+ fim_can_frame_write(&frame, frame.crc, 15);
+ fim_can_dump_frame(&frame, "CRC");
+
+ /* Stop the bit stuffing for this frame */
+ fim_can_frame_stop_stuff(&frame);
+
+ /* Write the CRC delimiter, CRC ACK and ACK delimiter */
+ fim_can_frame_write(&frame, CAN_BUS_RECESSIVE, 3);
+ fim_can_dump_frame(&frame, "CRC/ACK");
+
+ fim_can_frame_stop(&frame);
+
+ /*
+ * Set the first three byte of the frame as described in the specification
+ * (see page 8, transmit message structure)
+ */
+ frame.buffer[0] = FIM_CAN_CMD_TX;
+ frame.buffer[1] = ((id & CAN_EFF_FLAG) ? 18 : 11) + 1;
+ frame.buffer[2] = frame.stuffed;
+ fim_can_dump_frame(&frame, "END");
+
+ printk_debug("Sending a new SKB %p\n", skb);
+ retval = fim_can_send_skb(port, frame.buffer, 3 + (frame.stuffed + 7) / 8, skb);
+ if (retval) {
+ stats->tx_fifo_errors++;
+ stats->tx_dropped++;
+ printk_err("Couldn't send a frame with the ID 0x%X\n", id);
+ goto exit_free_skb;
+ }
+
+ stats->tx_bytes += dlc;
+ stats->tx_packets++;
+ dev->trans_start = jiffies;
+
+exit_free_skb:
+ return NETDEV_TX_OK;
+}
+
+/*
+ * Only errors are being generated by the firmware
+ * If an error ocurrs the FIM must be resetted (it hangs up in a endless loop)
+ */
+static void fim_can_isr(struct fim_driver *driver, int irq, unsigned char code,
+ unsigned int rx_fifo)
+{
+ struct net_device *dev;
+ struct fim_can_t *port;
+
+ printk_debug("FIM IRQ %x | %08x\n", code, rx_fifo);
+
+ port = driver->driver_data;
+ dev = port->dev;
+
+ if (code && code != FIM_CAN_INT_RESET) {
+ printk_debug("%s: Disabling interrupt\n",__func__);
+ fim_disable_irq(driver);
+ fim_send_stop(driver);
+ }
+
+ switch (code) {
+ case FIM_CAN_INT_CMD_ERR:
+ printk_err("Command error (FIM %i)\n", driver->picnr);
+ break;
+ case FIM_CAN_INT_HW_FAILURE:
+ printk_err("HW failure (FIM %i). Please restart the controller.\n",
+ driver->picnr);
+ break;
+ case FIM_CAN_INT_FILTER_ERR:
+ printk_err("Filter error (FIM %i)\n", driver->picnr);
+ break;
+ case FIM_CAN_INT_CFG_ERR:
+ printk_err("Config error (FIM %i)\n", driver->picnr);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Check if we have a stop condition of the CAN-controller
+ * If we need to stop the controller, restart the netif-queue anyway, then
+ * otherwise we will have problems when freeing the SKBs
+ */
+ if (code && code != FIM_CAN_INT_RESET) {
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ }
+}
+
+static void fim_can_tx_isr(struct fim_driver *driver, int irq,
+ struct fim_buffer_t *pdata)
+{
+ struct fim_can_t *port;
+ struct fim_buffer_t *buf;
+ struct net_device *dev;
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ port = driver->driver_data;
+ buf = pdata->private;
+ dev = port->dev;
+ skb = pdata->private;
+
+ spin_lock_irqsave(&port->lock, flags);
+ printk_debug("TX callback | SKB %p | dev %p\n", skb, dev);
+
+ /*
+ * @FIXME: If we remove this delay, then we will not catch the errors
+ * that can ocurrs during the frame transmission
+ */
+ udelay(200);
+
+ /* Restart the TX-queue if it was stopped for some reason */
+ if (fim_can_check_error(dev))
+ printk_debug("The udelay is working correctly\n");
+
+ /*
+ * Check if we have a SKB to free
+ * IMPORTANT: Allways free the SKB, otherwise we will have a memory leak,
+ * although in the template CAN-drivers they are not freeing the SKB
+ */
+ if (skb && skb != FIM_PRIVATE_BUFFER) {
+ printk_debug("Freeing the SKB %p\n", skb);
+ dev_kfree_skb_irq(skb);
+ }
+
+ /*
+ * Although we had a failure, we must restart the TX-queue, otherwise
+ * we will have a kernel panic when freeing the SKBs
+ */
+ if (dev && netif_queue_stopped(dev)) {
+ printk_debug("Restarting the netif-queue\n");
+ netif_wake_queue(dev);
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/*
+ * This function is called when the FIM has a new CAN-frame for us
+ * According to the configured filters we will receive only the filtered messages
+ */
+static void fim_can_rx_isr(struct fim_driver *driver, int irq,
+ struct fim_buffer_t *pdata)
+{
+ struct sk_buff *skb;
+ struct fim_can_t *port;
+ struct net_device *dev;
+ struct can_frame *cf;
+ struct net_device_stats *stats;
+ struct fim_can_rxframe *rx;
+
+ /* Set our internal data */
+ rx = (struct fim_can_rxframe *)pdata->data;
+ port = driver->driver_data;
+ dev = port->dev;
+
+ printk_debug("New CAN-frame (%i bytes) | ID 0x%02x%02x%02x%02x\n",
+ pdata->length, rx->id[3], rx->id[2], rx->id[1], rx->id[0]);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ printk_err("No memory available? Dropping a CAN-frame!\n");
+ return;
+ }
+
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0x00, sizeof(struct can_frame));
+
+ /* Start processing the arrived frame */
+
+ /* Set the CAN-ID corresponding to the FIM-controller specification */
+ if (rx->id[0] & 0x80)
+ cf->can_id |= CAN_RTR_FLAG;
+
+ if (rx->id[0] & 0x40)
+ cf->can_id |= CAN_EFF_FLAG;
+
+ /* Remove the control bits and set to the correct ID */
+ rx->id[0] &= 0x1F;
+ if (cf->can_id & CAN_EFF_FLAG)
+ cf->can_id |= rx->id[3] + (rx->id[2] << 8) + (rx->id[1] << 16) +
+ (rx->id[0] << 24);
+ else
+ cf->can_id |= (rx->id[1] >> 2) + (rx->id[0] << 6);
+
+ /* Copy the data */
+ cf->can_dlc = rx->ctrl & 0x0F;
+ memcpy(cf->data, rx->data, cf->can_dlc);
+
+ /* Pass the data to the sockets-layer */
+ netif_rx(skb);
+
+ /* Update the device statics */
+ stats = dev->get_stats(dev);
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static int unregister_fim_can(struct fim_can_t *port)
+{
+ struct fim_gpio_t gpios[FIM_CAN_MAX_GPIOS];
+ int cnt;
+ struct fim_driver *fim;
+
+ if (!port || !port->reg)
+ return -ENODEV;
+
+ //unregister_netdev(port->dev);
+ unregister_candev(port->dev);
+ free_candev(port->dev);
+
+ /* Activate the interrupt (@BUG in the IRQ-subsystem?) */
+ fim = &port->fim;
+
+ printk_debug("%s: Disabling interrupt\n", __func__);
+ fim_disable_irq(fim);
+
+ /* Stop the FIM first */
+ if (!fim_is_running(fim))
+ fim_send_stop(fim);
+
+ printk_debug("Going to unregister the FIM %i (running %i)\n",
+ fim->picnr, fim_is_running(fim));
+ fim_unregister_driver(fim);
+
+ /* And free the GPIOs */
+ memcpy(gpios, port->gpios, sizeof(struct fim_gpio_t) * FIM_CAN_MAX_GPIOS);
+ for (cnt = 0; cnt < FIM_CAN_MAX_GPIOS; cnt++) {
+
+ if (gpios[cnt].nr != FIM_GPIO_DONT_USE) {
+ printk_debug("Freeing the GPIO %i\n", gpios[cnt].nr);
+ gpio_free(gpios[cnt].nr);
+ }
+ }
+
+ /* Free the obtained clock */
+ if (!IS_ERR(port->cpu_clk))
+ clk_put(port->cpu_clk);
+
+ port->reg = 0;
+
+ return 0;
+}
+
+/*
+ * IMPORTANT: First register the FIM-driver, and at last the CAN-device, then
+ * it will automatically start with the bit time configuration.
+ */
+static int register_fim_can(struct device *devi, int picnr, struct fim_gpio_t gpios[],
+ int bitrate)
+{
+ int retval, cnt;
+ int func;
+ struct net_device *dev;
+ struct fim_can_t *port;
+ struct fim_dma_cfg_t dma_cfg;
+
+ /* Now create the net device */
+ dev = alloc_candev(sizeof(struct fim_can_t));
+ if (!dev) {
+ printk_err("Couldn't alloc a new CAN device\n");
+ return -ENOMEM;
+ }
+
+ /* Set our port structure as private data */
+ port = netdev_priv(dev);
+ port->dev = dev;
+
+ /* Get a reference to the SYS clock for setting the baudrate */
+ if (IS_ERR(port->cpu_clk = clk_get(&dev->dev, "systemclock"))) {
+ printk_err("Couldn't get the SYS clock.\n");
+ goto err_free_candev;
+ }
+
+ /* Request the corresponding GPIOs (@XXX: Check the returned values) */
+ for (cnt=0; cnt < FIM_CAN_MAX_GPIOS; cnt++) {
+ if (gpios[cnt].nr == FIM_LAST_GPIO)
+ break;
+ if (gpios[cnt].nr == FIM_GPIO_DONT_USE)
+ continue;
+ printk_debug("Requesting the GPIO %i (Function %i)\n",
+ gpios[cnt].nr, gpios[cnt].func);
+ retval = gpio_request(gpios[cnt].nr, FIM_DRIVER_NAME);
+ if (!retval) {
+ func = gpios[cnt].func;
+ gpio_configure_ns921x_unlocked(gpios[cnt].nr,
+ NS921X_GPIO_INPUT,
+ NS921X_GPIO_DONT_INVERT,
+ func,
+ NS921X_GPIO_ENABLE_PULLUP);
+ } else {
+ /* Free the already requested GPIOs */
+ printk_err("Couldn't request the GPIO %i\n", gpios[cnt].nr);
+ while (cnt)
+ gpio_free(gpios[--cnt].nr);
+ goto err_free_candev;
+ }
+ }
+
+ /* Now try to register the FIM-driver */
+ port->fim.picnr = picnr;
+ port->fim.driver.name = FIM_DRIVER_NAME;
+ port->fim.fim_isr = fim_can_isr;
+ port->fim.dma_tx_isr = fim_can_tx_isr;
+ port->fim.dma_rx_isr = fim_can_rx_isr;
+ port->fim.driver_data = port;
+
+ /* Specific DMA configuration for the CAN-controller */
+ dma_cfg.rxnr = FIM_CAN_DMA_BUFFERS;
+ dma_cfg.txnr = FIM_CAN_DMA_BUFFERS;
+ dma_cfg.rxsz = FIM_CAN_DMA_BUFFER_SIZE;
+ dma_cfg.txsz = FIM_CAN_DMA_BUFFER_SIZE;
+ port->fim.dma_cfg = &dma_cfg;
+
+ /* Check if have a firmware code for using to */
+ port->fim.fw_name = FIM_CAN_FIRMWARE_FILE;
+ port->fim.fw_code = FIM_CAN_FIRMWARE_CODE;
+ retval = fim_register_driver(&port->fim);
+ if (retval) {
+ printk_err("Couldn't register the FIM %i CAN driver.\n", picnr);
+ goto err_free_gpios;
+ }
+
+ /* Stop the FIM then it will be started in the open-function */
+ fim_send_stop(&port->fim);
+
+ /* Configure the net device with the default values */
+ dev->flags |= IFF_ECHO;
+ dev->open = fim_can_open;
+ dev->stop = fim_can_stop;
+ dev->hard_start_xmit = fim_can_xmit;
+
+ /* Special attributes for the CAN-stack */
+ port->can.do_get_state = fim_can_get_state;
+ port->can.do_set_mode = fim_can_set_mode;
+
+ port->can.bittiming.clock = clk_get_rate(port->cpu_clk);
+ printk_debug("CPU clock is %luHz\n", clk_get_rate(port->cpu_clk));
+
+ /*
+ * @TODO: Set the correct maximal BRP for the controller.
+ * DEFAULT_MAX_BRP 64
+ * DEFAULT_MAX_SJW 4
+ */
+ port->can.bittiming_const = &fim_bittiming_const;
+ if (!port->can.bittiming_const) {
+ printk_debug("bittiming_const is not initialized.\n");
+ goto err_unreg_fim;
+ }
+
+ /*
+ * Only store the passed bitrate. The corresponding timing parameters
+ * will be setup in the function that sends the configuration to the FIM.
+ * (Luis Galdos)
+ */
+ port->can.bittiming.bitrate = bitrate;
+
+ /* Now register the new CAN-net device */
+ retval = register_candev(dev);
+ if (retval) {
+ printk_err("Registering the net device for the FIM %i\n", picnr);
+ goto err_unreg_fim;
+ }
+
+ spin_lock_init(&port->lock);
+ port->dev = dev;
+ memcpy(port->gpios, gpios, sizeof(struct fim_gpio_t) * FIM_CAN_MAX_GPIOS);
+ port->reg = 1;
+ dev_set_drvdata(devi, port);
+
+ return 0;
+
+err_unreg_fim:
+ fim_unregister_driver(&port->fim);
+
+err_free_gpios:
+ for (cnt = 0; cnt < FIM_CAN_MAX_GPIOS; cnt++) {
+
+ if (gpios[cnt].nr != FIM_GPIO_DONT_USE) {
+ printk_debug("Freeing the GPIO %i\n", gpios[cnt].nr);
+ gpio_free(gpios[cnt].nr);
+ }
+ }
+
+err_free_candev:
+ free_candev(dev);
+
+ return -EINVAL;
+}
+
+static __init int fim_can_probe(struct platform_device *pdev)
+{
+ int nrpics;
+ int retval;
+ struct fim_gpio_t gpios[FIM_CAN_MAX_GPIOS];
+ struct fim_can_platform_data *pdata;
+ int bitrate;
+
+ printk_debug("Starting the FIM CAN bus driver.\n");
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -ENODEV;
+
+ /* If no bitrate passed, then use the default bitrate */
+ bitrate = pdata->fim_can_bitrate ? pdata->fim_can_bitrate :
+ FIM_CAN_DEFAULT_BITRATE;
+
+ /* Sanity check for the passed bit rate */
+ if (bitrate <= 0 || bitrate > FIM_CAN_MAX_BITRATE) {
+ printk_err("Invalid bit rate %i (max. rate is %i)\n",
+ bitrate, FIM_CAN_MAX_BITRATE);
+ return -EINVAL;
+ }
+
+ /* Get the number of available PICs from the FIM-core */
+ nrpics = fim_number_pics();
+
+ if (fim_check_device_id(fims_number, pdata->fim_nr)) {
+#if defined(MODULE)
+ printk_dbg("Skipping FIM%i (not selected)\n", pdata->fim_nr);
+#else
+ printk_err("Invalid FIM number '%i' in platform data\n", pdata->fim_nr);
+#endif
+ return -ENODEV;
+ }
+
+ /*
+ * Start with the registration of the CAN-ports
+ */
+ gpios[0].nr = pdata->rx_gpio_nr;
+ gpios[0].func = pdata->rx_gpio_func;
+ gpios[1].nr = pdata->tx_gpio_nr;
+ gpios[1].func = pdata->tx_gpio_func;
+ printk_debug("%s: GPIO RX: %d | GPIO TX: %d \n", __func__,
+ gpios[0].nr, gpios[1].nr );
+
+ /* XXX SDIO code approach */
+ retval = register_fim_can(&pdev->dev, pdata->fim_nr, gpios, bitrate);
+
+ return retval;
+}
+
+static __devexit int fim_can_remove(struct platform_device *pdev)
+{
+ struct fim_can_t *port;
+
+ port = dev_get_drvdata(&pdev->dev);
+ if (!port) {
+ printk_err("remove: NULL pointer found!\n");
+ return -ENODEV;
+ }
+
+ unregister_fim_can(port);
+
+ return 0;
+}
+
+static struct platform_driver fim_can_platform_driver = {
+ .probe = fim_can_probe,
+ .remove = __devexit_p(fim_can_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = FIM_DRIVER_NAME,
+ },
+};
+
+/*
+ * This is the function that will be called when the module is loaded
+ * into the kernel space
+ */
+static __init int fim_can_init(void)
+{
+ printk_info(DRIVER_DESC " " DRIVER_VERSION "\n");
+
+ /* Check for the passed number parameter */
+ if (fim_check_numbers_param(fims_number)) {
+ printk_err("Invalid number '%i' of FIMs to handle\n", fims_number);
+ return -EINVAL;
+ }
+
+ return platform_driver_register(&fim_can_platform_driver);
+}
+
+static __exit void fim_can_exit(void)
+{
+ printk_info("Removing the FIM CAN driver\n");
+ platform_driver_unregister(&fim_can_platform_driver);
+}
+
+module_init(fim_can_init);
+module_exit(fim_can_exit);
diff --git a/drivers/fims/can/fim_can.h b/drivers/fims/can/fim_can.h
new file mode 100644
index 000000000000..f954330205be
--- /dev/null
+++ b/drivers/fims/can/fim_can.h
@@ -0,0 +1,28 @@
+/* -*- linux-c -*-
+ *
+ * drivers/fims/can/fim_can.h
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Dummy header file for building the kernel with the FIM-CAN driver.
+ * For obtaining a valid fimrware please refer to the FIMs documentation of the
+ * Digi ESP.
+ *
+ */
+
+#warning "Using a dummy FIM CAN firmware"
+
+static const unsigned char fim_can_firmware[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
diff --git a/drivers/fims/dma.h b/drivers/fims/dma.h
new file mode 100644
index 000000000000..9806e53f66e7
--- /dev/null
+++ b/drivers/fims/dma.h
@@ -0,0 +1,177 @@
+/* -*- linux-c -*-
+ *
+ * drivers/fims/dma.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * !Revision: $Revision: 1.3 $
+ * !Author: Luis Galdos
+ * !Descr:
+ * !References:
+ */
+
+
+#ifndef __FIM_API_DMA_H
+#define __FIM_API_DMA_H
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+
+#include <mach/dma-ns921x.h>
+
+
+/*
+ * The access to the DMA-channels of the FIMs is only provided by the FIM-API
+ * The FIM-drivers should not use the functions of this source code file
+ */
+
+
+inline static void fim_dma_reset_fifo(struct iohub_dma_fifo_t *fifo)
+{
+ if (!fifo)
+ return;
+
+ fifo->next_free = fifo->first;
+ fifo->dma_first = fifo->first;
+ fifo->dma_last = fifo->dma_next = NULL;
+}
+
+
+inline int fim_dma_init_fifo(struct iohub_dma_fifo_t *fifo, int length,
+ struct iohub_dma_desc_t **descs)
+{
+ if (!fifo || !length || !descs)
+ return -EINVAL;
+
+ /* @XXX: These are the start values for the API */
+ fifo->length = length;
+ fifo->first = (struct iohub_dma_desc_t *)descs;
+ fifo->last = fifo->first + fifo->length - 1;
+
+ fifo->next_free = fifo->first;
+ fifo->dma_first = fifo->first;
+ fifo->dma_last = fifo->dma_next = NULL;
+ return 0;
+}
+
+
+inline static struct iohub_dma_desc_t *fim_dma_get_next(struct iohub_dma_fifo_t *fifo,
+ struct iohub_dma_desc_t *ptr)
+{
+ if (ptr == fifo->last)
+ return fifo->first;
+ else
+ return ptr + 1;
+}
+
+
+/*
+ * Return the index of a passed DMA-descriptor
+ */
+inline static struct iohub_dma_desc_t *fim_dma_get_by_index(struct iohub_dma_fifo_t *fifo,
+ int index)
+{
+ if (index < 0)
+ return fifo->first;
+ else if (index >= fifo->length)
+ return fifo->last;
+ else
+ return fifo->first + index;
+}
+
+
+inline static int fim_dma_length(struct iohub_dma_fifo_t *fifo)
+{
+ return fifo->length;
+}
+
+
+/*
+ * Returns the number of free DMA-descriptors
+ */
+inline static int fim_dma_frees(struct iohub_dma_fifo_t *fifo)
+{
+ int retval;
+
+ /* The first is the INIT state of the FIFO */
+ if (!fifo->dma_last && !fifo->dma_next)
+ retval = fifo->length;
+ else if (fifo->dma_last == fifo->dma_next)
+ retval = fifo->length;
+ else if (!fifo->dma_last)
+ retval = fifo->last - fifo->dma_next;
+ else if (fifo->dma_next == fifo->dma_first)
+ retval = (fifo->last - fifo->first);
+ else if (fifo->dma_next > fifo->dma_first)
+ retval = (fifo->last - fifo->dma_next) +
+ (fifo->dma_first - fifo->first);
+ else
+ retval = (fifo->dma_first - fifo->dma_next);
+
+ return retval;
+}
+
+
+/*
+ * Check if the FIFO contains descriptors or is empty
+ */
+inline static int fim_dma_is_empty(struct iohub_dma_fifo_t *fifo)
+{
+ /* @XXX: That's only for FIFOs with one pointer */
+ if (fifo->length == 1 && fifo->dma_next)
+ return 0;
+
+ if (fim_dma_frees(fifo) == fifo->length)
+ return 1;
+ else
+ return 0;
+}
+
+
+inline static int fim_dma_get_index(struct iohub_dma_fifo_t *fifo,
+ struct iohub_dma_desc_t *desc)
+{
+ return desc - fifo->first;
+}
+
+
+/*
+ * Check if there is enough space for a specific number of buffer descriptors.
+ * Return a pointer to the first buffer descriptor is the FIFO has enough space
+ */
+inline static struct iohub_dma_desc_t *fim_dma_alloc(struct iohub_dma_fifo_t *fifo,
+ int count)
+{
+ int len, diff;
+ struct iohub_dma_desc_t *tmp, *retval;
+
+ len = fim_dma_frees(fifo);
+ if (len < count)
+ return NULL;
+
+ if (!fifo->dma_next) {
+ retval = fifo->first;
+ fifo->dma_next = fifo->first + count - 1;
+ } else {
+ retval = fim_dma_get_next(fifo, fifo->dma_next);
+ tmp = fifo->dma_next + count;
+ if (tmp <= fifo->last)
+ fifo->dma_next = tmp;
+ else {
+ diff = tmp - fifo->last - 1;
+ fifo->dma_next = fifo->first + diff;
+ }
+ }
+
+ return retval;
+}
+
+
+
+#endif /* ifndef __FIM_API_DMA_H */
diff --git a/drivers/fims/fim.c b/drivers/fims/fim.c
new file mode 100644
index 000000000000..8df1db902a88
--- /dev/null
+++ b/drivers/fims/fim.c
@@ -0,0 +1,2216 @@
+/* -*- linux-c -*-
+ *
+ * drivers/fim.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * !Revision: $Revision: 1.45 $
+ * !Author: Silvano Najera, Luis Galdos
+ * !Descr:
+ * !References:
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+#include <asm/signal.h>
+#include <linux/kfifo.h>
+#include <linux/sysfs.h>
+
+/* Arch-dependent header files */
+#include <mach/regs-sys-ns921x.h>
+#include <mach/regs-iohub-ns921x.h>
+#include <mach/irqs.h>
+
+/* Include the structure for the FIM-firmware */
+#include <mach/fim-ns921x.h>
+#include <mach/fim-firmware.h>
+
+#include "fim_reg.h"
+#include "dma.h"
+
+#define DRIVER_VERSION "v0.2"
+#define DRIVER_AUTHOR "Silvano Najera, Luis Galdos"
+#define DRIVER_DESC "FIMs driver"
+
+/* Module information */
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
+
+#define DRIVER_NAME "fim"
+#define FIM_DRIVER_NAME "fims"
+#define FIM_BUS_TYPE_NAME "fim-bus"
+
+#define IRQ_NS921X_PIC IRQ_NS921X_PIC0
+#define FIM0_SHIFT 6
+
+#if 0
+#define FIM_CORE_DEBUG
+#endif
+
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] fims: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "fims: " fmt, ## args)
+#define printk_warn(fmt, args...) printk(KERN_DEBUG "fims: " fmt, ## args)
+
+#ifdef FIM_CORE_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "fims: %s() " fmt, __FUNCTION__ , ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+
+#define PIC_IS_LOCKED(pic) (atomic_read(&pic->requested))
+#define PIC_LOCK_REQUEST(pic) do { atomic_set(&pic->requested, 1); } while (0)
+#define PIC_FREE_REQUEST(pic) do { atomic_set(&pic->requested, 0); } while (0)
+
+#define PIC_TX_LOCK(pic) do { atomic_set(&pic->tx_tasked, 1); } while (0)
+#define PIC_TX_FREE(pic) do { atomic_set(&pic->tx_tasked, 0); } while (0)
+#define PIC_TX_IS_FREE(pic) (!atomic_read(&pic->tx_tasked))
+
+
+/* Use the same function for all the binary attributes */
+#define FIM_SYSFS_ATTR(_name) \
+{ \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = S_IRUGO, \
+ .owner = THIS_MODULE, \
+ }, \
+ .read = fim_sysfs_attr_read, \
+}
+
+
+struct fims_t {
+ struct pic_t pics[FIM_NR_PICS];
+ struct bus_type *bus_type;
+ struct device *bus_dev;
+ struct device_driver *driver;
+};
+
+static struct fims_t *the_fims;
+
+
+static int pic_config_output_clock_divisor(struct pic_t *pic,
+ struct fim_program_t *program);
+static int pic_send_interrupt(struct pic_t *pic, unsigned int code);
+
+
+static int pic_download_firmware(struct pic_t *pic, const unsigned char *buffer);
+static int pic_stop_and_reset(struct pic_t *pic);
+static int pic_start_at_zero(struct pic_t *pic);
+
+
+static int fim_remove(struct device *dev);
+static int fim_probe(struct device *dev);
+static irqreturn_t pic_irq(int irq, void *tpic);
+static int pic_dma_init(struct pic_t *pic, struct fim_dma_cfg_t *cfg);
+static void pic_dma_stop(struct pic_t *pic);
+static int pic_is_running(struct pic_t *pic);
+
+
+/* Interrupt handlers */
+static void isr_from_pic(struct pic_t *pic, int irqnr);
+static void isr_dma_tx(struct pic_t *pic, int irqnr);
+static void isr_dma_rx(struct pic_t *pic, int irqnr);
+static int fim_start_tx_dma(struct pic_t *pic);
+
+
+/*
+ * Be aware with this function, we have max. one PAGE available for writing into
+ * the return buffer
+ * When the module is unregistered it's NOT required to reset the attributes
+ */
+static ssize_t fim_sysfs_attr_read(struct kobject *kobj, struct bin_attribute *attr,
+ char *buffer, loff_t off, size_t count)
+{
+ int size, cnt;
+ struct device *dev;
+ struct pic_t *pic;
+ struct attribute *bin;
+ struct iohub_dma_desc_t *desc;
+ unsigned int ctrlreg;
+
+ dev = container_of(kobj, struct device, kobj);
+ bin = &attr->attr;
+ pic = (struct pic_t *)dev->driver_data;
+ if (!pic)
+ return 0;
+
+ /* Check for the correct attribute */
+ if (!strcmp(bin->name, "irq"))
+ size = snprintf(buffer, PAGE_SIZE, "%i", pic->irq);
+
+ else if (pic->driver && !strcmp(bin->name, "module"))
+ size = snprintf(buffer, PAGE_SIZE, "%s", pic->driver->driver.name);
+
+ else if (!strcmp(bin->name, "running"))
+ size = snprintf(buffer, PAGE_SIZE, "%i", pic_is_running(pic));
+
+ else if (pic->driver && !strcmp(bin->name, "version"))
+ size = snprintf(buffer, PAGE_SIZE, "@TODO: Version of the firmware");
+
+ else if (pic->driver && !strcmp(bin->name, "fwname"))
+ size = snprintf(buffer, PAGE_SIZE, "%s", pic->fw_name);
+
+ else if (pic->driver && !strcmp(bin->name, "fwlength"))
+ size = snprintf(buffer, PAGE_SIZE, "%i", pic->fw_length);
+
+ else if (pic->driver && !strcmp(bin->name, "txdma")) {
+ ctrlreg = readl(pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ size = snprintf(buffer, PAGE_SIZE, "CTRL 0x%08x\n", ctrlreg);
+ ctrlreg = readl(pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ for (cnt=0; cnt < pic->tx_fifo.length; cnt++) {
+ desc = fim_dma_get_by_index(&pic->tx_fifo, cnt);
+ size += snprintf(buffer + size,
+ PAGE_SIZE - size,
+ "[ TX %02i ] 0x%04x | %i\n",
+ cnt, desc->control, desc->length);
+ }
+ }
+
+ else if (pic->driver && !strcmp(bin->name, "rxdma")) {
+ ctrlreg = readl(pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+ size = snprintf(buffer, PAGE_SIZE, "CTRL 0x%08x\n", ctrlreg);
+ for (cnt=0; cnt < pic->rx_fifo.length; cnt++) {
+ desc = fim_dma_get_by_index(&pic->rx_fifo, cnt);
+ size += snprintf(buffer + size,
+ PAGE_SIZE - size,
+ "[ RX %02i ] 0x%04x | %i\n",
+ cnt, desc->control, desc->length);
+ }
+ }
+
+ else
+ size = 0;
+
+ /* Only allows a single read for this binary attributes */
+ if (off || count < size)
+ return 0;
+
+ return size;
+}
+
+
+/* These are the non-default binary attributes for the FIMs */
+static struct bin_attribute fim_sysfs_attrs[] = {
+ FIM_SYSFS_ATTR(irq),
+ FIM_SYSFS_ATTR(running),
+ FIM_SYSFS_ATTR(module),
+ FIM_SYSFS_ATTR(version),
+ FIM_SYSFS_ATTR(fwname),
+ FIM_SYSFS_ATTR(fwlength),
+ FIM_SYSFS_ATTR(txdma),
+ FIM_SYSFS_ATTR(rxdma),
+};
+
+
+
+/*
+ * This tasklet will search for full receive DMA-buffers
+ * Only if the PIC has an associated driver the corresponding callback will be started
+ */
+static void pic_rx_tasklet_func(unsigned long data)
+{
+ struct pic_t *pic;
+ struct iohub_dma_desc_t *desc;
+ struct fim_driver *driver;
+ struct fim_buffer_t buffer;
+ struct iohub_dma_fifo_t *fifo;
+
+ /* If no driver is attached, then the DMA-channel is normally disabled */
+ pic = (struct pic_t *)data;
+ if (!pic || !pic->driver)
+ return;
+
+ driver = pic->driver;
+ fifo = &pic->rx_fifo;
+
+ /* The pointer to DMA-first will be updated in the locked segment! */
+ spin_lock(&pic->rx_lock);
+ desc = fifo->dma_first;
+
+ /*
+ * If a driver is waiting for the data then create a callback buffer,
+ * otherwise only restore the DMA-buffer descriptor
+ */
+ while (desc->control & IOHUB_DMA_DESC_CTRL_FULL) {
+ /*
+ * If the driver doesn't have a RX-callback then only free
+ * the DMA-descriptors
+ */
+ if (driver->dma_rx_isr) {
+ buffer.data = phys_to_virt(desc->src);
+ buffer.length = desc->length;
+ pic->driver->dma_rx_isr(driver, pic->irq, &buffer);
+ }
+
+ /* And free the DMA-descriptor for a next transfer */
+ desc->length = pic->dma_cfg.rxsz;
+ desc->control &= ~(IOHUB_DMA_DESC_CTRL_FULL |
+ IOHUB_DMA_DESC_CTRL_LAST);
+ desc = fim_dma_get_next(fifo, desc);
+ }
+
+ /* Update the buffer index for the next tasklet */
+ fifo->dma_first = desc;
+
+ /* Check for previously detected errors */
+ if (fifo->rx_error) {
+
+ if (pic->driver->dma_error_isr)
+ pic->driver->dma_error_isr(pic->driver, fifo->rx_error, 0);
+
+ /* fim_dma_reset_fifo(fifo); */
+ fifo->rx_error = 0;
+ }
+
+ spin_unlock(&pic->rx_lock);
+}
+
+
+
+inline static struct pic_t *get_pic_by_index(int index)
+{
+ if (index < FIM_MIN_PIC_INDEX || index > FIM_MAX_PIC_INDEX)
+ return NULL;
+
+
+ return &the_fims->pics[index];
+}
+
+
+static struct pic_t *get_pic_from_driver(struct fim_driver *driver)
+{
+ if (!driver)
+ return NULL;
+
+ return get_pic_by_index(driver->picnr);
+}
+
+
+static int pic_get_ctrl_reg(struct pic_t *pic, int reg, unsigned int *val)
+{
+ if (NS92XX_FIM_CTRL_REG_CHECK(reg))
+ return -EINVAL;
+
+ *val = readl(pic->reg_addr + NS92XX_FIM_CTRL_REG(reg));
+ return 0;
+}
+
+
+static void pic_set_ctrl_reg(struct pic_t *pic, int reg, unsigned int val)
+{
+ if (NS92XX_FIM_CTRL_REG_CHECK(reg))
+ return;
+
+ writel(val, pic->reg_addr + NS92XX_FIM_CTRL_REG(reg));
+}
+
+
+static int pic_is_running(struct pic_t *pic)
+{
+ unsigned int regval;
+
+ regval = readl(pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+ if (regval & NS92XX_FIM_GEN_CTRL_PROGMEM)
+ return 1;
+ else
+ return 0;
+}
+
+int fim_is_running(struct fim_driver *driver)
+{
+ struct pic_t *pic;
+
+ if (!(pic = get_pic_from_driver(driver)))
+ return -ENODEV;
+
+ return pic_is_running(pic);
+}
+
+
+static void fim_pic_release(struct device *dev)
+{
+ /* @TODO: Nothing to do here? */
+}
+
+
+/*
+ * Returns the number of available bytes that can be theoretically requested
+ * for a TX-transfer with more than one buffer descriptor
+ */
+int fim_tx_buffers_room(struct fim_driver *driver)
+{
+ int retval, cnt;
+ struct pic_dma_desc_t *pic_desc;
+ struct pic_t *pic;
+
+ if (!(pic = get_pic_by_index(driver->picnr)))
+ return -ENODEV;
+
+ retval = 0;
+ for (cnt=0; cnt < pic->tx_fifo.length; cnt++) {
+ pic_desc = pic->tx_desc + cnt;
+ if (atomic_read(&pic_desc->tasked))
+ continue;
+
+ retval += pic_desc->length;
+ }
+
+ return retval;
+}
+
+
+
+int fim_tx_buffers_level(struct fim_driver *driver)
+{
+ int retval, cnt;
+ struct pic_dma_desc_t *pic_desc;
+ struct pic_t *pic;
+
+ if (!(pic = get_pic_by_index(driver->picnr)))
+ return -ENODEV;
+
+ retval = 0;
+ for (cnt=0; cnt < pic->tx_fifo.length; cnt++) {
+ pic_desc = pic->tx_desc + cnt;
+ if (atomic_read(&pic_desc->tasked))
+ retval += pic_desc->length;
+ }
+
+ return retval;
+}
+
+
+int fim_number_pics(void)
+{
+ return FIM_NR_PICS;
+}
+
+
+
+/*
+ * Allocate a new FIM-buffer
+ * @TODO: Bind this buffer to an internal list of the driver, so that we can free
+ * all the buffers if the driver is being unloaded
+ */
+struct fim_buffer_t *fim_alloc_buffer(struct fim_driver *driver, int length,
+ unsigned int gfp_flags)
+{
+ struct fim_buffer_t *retval;
+
+ if (!driver || length < 0)
+ return NULL;
+
+ if (!(retval = kmalloc(sizeof(struct fim_buffer_t) + length , gfp_flags)))
+ return NULL;
+
+ retval->sent = 0;
+ retval->length = length;
+ retval->data = (void *)retval + sizeof(struct fim_buffer_t);
+ retval->private = NULL;
+ return retval;
+}
+
+
+/*
+ * Free an already requested FIM-buffer
+ * @TODO: Use the internal list for removing this buffer
+ */
+void fim_free_buffer(struct fim_driver *driver, struct fim_buffer_t *buffer)
+{
+ if (!driver || !buffer)
+ return;
+
+ kfree(buffer);
+}
+
+
+/*
+ * Be sure that you protect this function correctly.
+ * Use the spinlock tx_lock for this purpose.
+ */
+inline static void pic_reset_tx_fifo(struct pic_t *pic)
+{
+ int cnt;
+ struct iohub_dma_desc_t *desc;
+ struct pic_dma_desc_t *pic_desc;
+
+ fim_dma_reset_fifo(&pic->tx_fifo);
+ for(cnt=0; cnt < pic->tx_fifo.length; cnt++) {
+ desc = fim_dma_get_by_index(&pic->tx_fifo, cnt);
+ desc->control &= ~IOHUB_DMA_DESC_CTRL_FULL;
+ desc->length = 0;
+ pic_desc = pic->tx_desc + cnt;
+ atomic_set(&pic_desc->tasked, 0);
+ }
+}
+
+
+/*
+ * Check if there are enough buffers for the requested memory size
+ * In success then will try to restart the DMA-channel
+ * The return value is different then ZERO by errors.
+ * This function can be called from an interrupt context too.
+ */
+int fim_send_buffer(struct fim_driver *driver, const struct fim_buffer_t *bufdesc)
+{
+ struct pic_t *pic;
+ int cnt, len, last_len, pos;
+ struct pic_dma_desc_t *pic_desc;
+ struct pic_dma_desc_t *pic_descs[IOHUB_MAX_DMA_BUFFERS] = { NULL }; /* @XXX */
+ struct iohub_dma_desc_t *desc;
+ int backup_len;
+ int count, retval;
+ unsigned char *buffer;
+
+ if (!bufdesc || !driver || (bufdesc->length <= 0) || !bufdesc->data ||
+ !bufdesc->private)
+ return -EINVAL;
+
+ if (!(pic = get_pic_from_driver(driver)))
+ return -ENODEV;
+
+ /*
+ * Lock this section so that the PIC can be unregistered now
+ */
+ spin_lock(&pic->tx_lock);
+ if (!pic->driver) {
+ retval = -ENODEV;
+ goto exit_ret;
+ }
+
+ /* Check for the available buffers */
+ len = count = bufdesc->length;
+ buffer = bufdesc->data;
+ pos = 0;
+ for (cnt=0; cnt < pic->tx_fifo.length; cnt++) {
+ pic_desc = pic->tx_desc + cnt;
+ if (atomic_read(&pic_desc->tasked))
+ continue;
+
+ atomic_set(&pic_desc->tasked, 1);
+ pic_descs[pos++] = pic_desc;
+
+ last_len = len;
+ len -= (int)pic_desc->length;
+ if (len <= 0) {
+ /* Try to get the correct number of FIFO descriptors */
+ if(!(desc = fim_dma_alloc(&pic->tx_fifo, pos))) {
+ printk_err(" Couldn't get the FIFO descriptors.\n");
+ break;
+ }
+
+ /* Copy the transfer data to the corresponding DMA-buffers */
+ backup_len = pic_desc->length;
+ pic_desc->length = last_len;
+ for (cnt=0; pic_descs[cnt] && cnt < pos; cnt++) {
+ len = pic_descs[cnt]->length;
+ memcpy(phys_to_virt(pic_descs[cnt]->src), buffer, len);
+ desc->src = pic_descs[cnt]->src;
+ desc->reserved = 0;
+ desc->length = len;
+ buffer += len;
+ desc = fim_dma_get_next(&pic->tx_fifo, desc);
+ }
+ pic_desc->length = backup_len;
+
+ /*
+ * Save the private data into the first PIC-DMA descriptor
+ * This private data will be used inside the TX-callback function
+ * @BUG: We must save the requested length for the TX-callback,
+ * then the DMA-controller set the length to zero.
+ */
+ pic_descs[0]->private = bufdesc->private;
+ pic_descs[0]->total_length = bufdesc->length;
+ fim_start_tx_dma(pic);
+ retval = 0;
+ goto exit_ret;
+ }
+ }
+
+ printk_debug("No free TX-buffers available (%i). Aborting.\n", bufdesc->length);
+ for (cnt=0; pic_descs[cnt] && cnt < pic->tx_fifo.length; cnt++)
+ atomic_dec(&pic_descs[cnt]->tasked);
+ retval = -ENOMEM;
+
+ exit_ret:
+ spin_unlock(&pic->tx_lock);
+ return retval;
+}
+
+
+/* @TODO: Please test this function before using it */
+void fim_flush_rx(struct fim_driver *driver)
+{
+ unsigned int cnt;
+ struct pic_t *pic;
+ struct iohub_dma_desc_t *desc;
+ struct iohub_dma_fifo_t *fifo;
+
+ if (!(pic = get_pic_by_index(driver->picnr)))
+ return;
+
+ writel(0x00, pic->iohub_addr + IOHUB_RX_DMA_ICTRL_REG);
+ writel(IOHUB_RX_DMA_CTRL_CA, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+ for (cnt=0; cnt < pic->rx_fifo.length; cnt++) {
+ desc = fim_dma_get_by_index(&pic->rx_fifo, cnt);
+ desc->control = IOHUB_DMA_DESC_CTRL_INT;
+ }
+
+ writel(IOHUB_ICTRL_RXNCIE | IOHUB_ICTRL_RXNRIE | IOHUB_ICTRL_RXPCIE,
+ pic->iohub_addr + IOHUB_RX_DMA_ICTRL_REG);
+ writel(pic->rx_fifo.phys_descs, pic->iohub_addr + IOHUB_RX_DMA_BUFPTR_REG);
+ writel(IOHUB_RX_DMA_CTRL_CE, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+
+ /* Reset the internal fifo too */
+ fifo = &pic->rx_fifo;
+ fim_dma_reset_fifo(fifo);
+}
+
+
+/*
+ * Drain the TX-buffers by first aborting the DMA-channel. The DMA-descriptors
+ * will be reseted inside the interrupt routine
+ */
+void fim_flush_tx(struct fim_driver *driver)
+{
+ struct pic_t *pic;
+
+ if (!(pic = get_pic_by_index(driver->picnr)))
+ return;
+
+ pic_reset_tx_fifo(pic);
+
+ writel(IOHUB_RX_DMA_CTRL_CA, pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+}
+
+
+
+/*
+ * This function is responsible for restarting the DMA-channel with the correct index
+ * If the DMA-channel is transferring data it will returns inmediately, but the
+ * callback of the DMA-TX will recall this function and send the requested data buffers
+ */
+static int fim_start_tx_dma(struct pic_t *pic)
+{
+ unsigned long txctrl;
+ struct iohub_dma_desc_t *desc = NULL;
+ struct iohub_dma_fifo_t *fifo;
+ int retval = 0;
+ unsigned int channel_index, fifo_index;
+
+ if (fim_dma_is_empty(&pic->tx_fifo)) {
+ printk(KERN_DEBUG "Nothing to do, the TX-FIFO is empty.\n");
+ return 0;
+ }
+
+ /* @XXX: Need to protect this section */
+ if (atomic_read(&pic->tx_tasked)) {
+ printk_debug("The DMA-Controller seems to be tasked.\n");
+ return 0;
+ }
+
+ /* Lock the next DMA-transfer and get the internal data */
+ atomic_set(&pic->tx_tasked, 1);
+
+ spin_lock(&pic->tx_lock);
+ fifo = &pic->tx_fifo;
+ fifo->dma_last = fifo->dma_next;
+
+ /*
+ * Write the control bit fields for the new TX-transfer
+ * The close-buffer interrupt will be enabled only for the last descriptor!
+ */
+ for (desc=fifo->dma_first; ; desc=fim_dma_get_next(fifo, desc)) {
+ if (desc->length <= 0) {
+ printk_err("Invalid FIFO descriptor 0x%p length, %u\n",
+ desc, desc->length);
+ retval = -EINVAL;
+ goto exit_unlock;
+ }
+
+ /* IMPORTANT: Enable the interrupt only for the last buffer! */
+ desc->control = IOHUB_DMA_DESC_CTRL_FULL;
+ if (desc == fifo->last)
+ desc->control |= IOHUB_DMA_DESC_CTRL_WRAP;
+ if (desc == fifo->dma_last) {
+ desc->control |= (IOHUB_DMA_DESC_CTRL_INT |
+ IOHUB_DMA_DESC_CTRL_LAST);
+ break;
+ }
+ }
+
+ /* Get the index of the first FIFO descriptor for the DMA-controller */
+ fifo_index = fim_dma_get_index(&pic->tx_fifo, fifo->dma_first);
+ txctrl = readl(pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ txctrl |= IOHUB_TX_DMA_CTRL_INDEXEN | IOHUB_TX_DMA_CTRL_INDEX(fifo_index);
+ writel(txctrl, pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+
+ /*
+ * If the TX-FIFO and the DMA-Channel have different index then we have
+ * an error, then normally they must have the same value.
+ * @FIXME: Workaround for this problem, or remove the paranoic sanity check
+ */
+ channel_index = readl(pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG) & 0x3FF;
+ if (fifo_index && (fifo_index != channel_index)) {
+ printk_warn("FIFO index 0x%X mismatch DMA index 0x%X\n",
+ fifo_index, channel_index);
+ } else {
+ printk_debug("DMA index %i for next DMA buffer\n", channel_index);
+ }
+
+ /*
+ * Check if an abort interrupt was executed in the time we have
+ * configured the DMA-descriptors. In that skip the channel restart
+ */
+ if (!atomic_read(&pic->tx_aborted)) {
+ writel(txctrl & ~IOHUB_TX_DMA_CTRL_CE,
+ pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ writel(txctrl | IOHUB_TX_DMA_CTRL_CE,
+ pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ } else
+ atomic_set(&pic->tx_tasked, 0);
+
+ exit_unlock:
+ spin_unlock(&pic->tx_lock);
+ return retval;
+}
+
+
+
+
+/* DONT poll with this function. We need another function for this purpose. */
+int fim_get_exp_reg(struct fim_driver *driver, int nr, unsigned int *value)
+{
+ struct pic_t *pic;
+
+ if (!driver)
+ return -EINVAL;
+
+ if (!(pic = get_pic_by_index(driver->picnr)))
+ return -EINVAL;
+
+ *value = readl(pic->reg_addr + NS92XX_FIM_EXP_REG(nr));
+
+ return 0;
+}
+
+
+
+/* Called when the PIC interrupts the ARM-processor */
+static void isr_from_pic(struct pic_t *pic, int irqnr)
+{
+ unsigned int status;
+ struct fim_driver *driver;
+ unsigned int rx_fifo;
+ unsigned int timeout;
+
+ status = readl(pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+ rx_fifo = readl(pic->iohub_addr + IOHUB_RX_DIR_FIFO_REG);
+
+ driver = pic->driver;
+ if (driver && driver->fim_isr)
+ driver->fim_isr(driver, pic->irq, status & 0xFF, rx_fifo);
+
+ /* @TEST */
+ writel(status, pic->reg_addr + NS92XX_FIM_CTRL7_REG);
+
+ writel(status | NS92XX_FIM_GEN_CTRL_INTACKWR,
+ pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+
+ timeout = 0xFFFF;
+ do {
+ timeout--;
+ status = readl(pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+ } while (timeout && (status & NS92XX_FIM_GEN_CTRL_INTFROMPIC));
+
+ /* @XXX: Should we stop the PIC for avoiding more timeout errors? */
+ if (!timeout) {
+ printk_err("FIM %i interrupt handshake timeout.\n", pic->index);
+ return;
+ }
+
+ writel(status & ~NS92XX_FIM_GEN_CTRL_INTACKWR, pic->reg_addr +
+ NS92XX_FIM_GEN_CTRL_REG);
+}
+
+
+
+static void isr_dma_tx(struct pic_t *pic, int irqnr)
+{
+ struct iohub_dma_fifo_t *fifo;
+ unsigned int ifs, txctrl, ictrl;
+ int cnt;
+ struct iohub_dma_desc_t *desc;
+ struct pic_dma_desc_t *pic_desc;
+ struct fim_driver *driver;
+ struct fim_buffer_t buffer= { 0, NULL, NULL };
+
+ fifo = &pic->tx_fifo;
+ ifs = readl(pic->iohub_addr + IOHUB_IFS_REG);
+ if (ifs & IOHUB_IFS_TXNRIP) {
+ /*
+ * From the Net+OS driver seems to be that the DMA controller is
+ * setting the TXNRIP status bit although no error happened. But for
+ * being sure that no error happened, we check the full control bit
+ * of the last buffer descriptor: If this bit is high then the
+ * last buffer was not transferred, ERROR!
+ */
+ txctrl = readl(pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG) - 1;
+ desc = fifo->dma_last;
+ if(desc->control & IOHUB_DMA_DESC_CTRL_FULL) {
+ ictrl = readl(pic->iohub_addr + IOHUB_TX_ICTRL_REG);
+ printk_err("TXNRIP: ICTRL 0x%08x | CTRL 0x%08x | DESC 0x%p\n",
+ ictrl, txctrl, desc);
+ }
+ }
+
+ /*
+ * The channel abort is normally being called for flushing the TX-buffers.
+ */
+ if (ifs & IOHUB_IFS_TXCAIP) {
+
+ atomic_set(&pic->tx_aborted, 1);
+ spin_lock(&pic->tx_lock);
+ printk_debug("TXCAIP: Freeing the TX-buffers\n");
+ pic_reset_tx_fifo(pic);
+
+ /* Reset the index of the TX-channel to zero */
+ writel(IOHUB_RX_DMA_CTRL_CA | IOHUB_TX_DMA_CTRL_INDEXEN,
+ pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ writel(0x00, pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+
+ /* Reset the atomic variables for enabling the TX-requests */
+ atomic_set(&pic->tx_aborted, 0);
+ atomic_set(&pic->tx_tasked, 0);
+ spin_unlock(&pic->tx_lock);
+ return;
+ }
+
+
+ /*
+ * If no NC interrupt pending then return immediately.
+ * @FIXME: Is possible that a driver is waiting for the TXNCIP on this case?
+ */
+ if(!(ifs & IOHUB_IFS_TXNCIP))
+ return;
+
+ /* Now free the allocated PIC-DMA descriptors */
+ desc = fifo->dma_first;
+ driver = pic->driver;
+ do {
+ for(cnt=0; cnt < pic->tx_fifo.length; cnt++) {
+ pic_desc = pic->tx_desc + cnt;
+ if(pic_desc->src == desc->src) {
+ if(!buffer.data) {
+ buffer.data = phys_to_virt(desc->src);
+ buffer.private = pic_desc->private;
+ }
+
+ /*
+ * @BUG: The DMA-channel seems to reset the buffer length
+ * otherwise use: buffer.length += desc->length;
+ */
+ buffer.length = pic_desc->total_length;
+ desc->src = 0;
+ atomic_set(&pic_desc->tasked, 0);
+ break;
+ }
+ }
+ desc = fim_dma_get_next(fifo, desc);
+ } while (desc != fim_dma_get_next(fifo, fifo->dma_last));
+
+
+ /* Now give the control to the driver's callback function */
+ if (driver->dma_tx_isr)
+ driver->dma_tx_isr(driver, pic->irq, &buffer);
+
+ /* Free the DMA-controller */
+ fifo->dma_first = fim_dma_get_next(fifo, fifo->dma_last);
+ atomic_set(&pic->tx_tasked, 0);
+
+ /* Check if another descriptors are waiting in the FIFO */
+ if (fifo->dma_next != fifo->dma_last)
+ fim_start_tx_dma(pic);
+
+}
+
+
+
+/* Only check that no errors ocurred by the last buffer descriptor */
+static void isr_dma_rx(struct pic_t *pic, int irqnr)
+{
+ unsigned int ifs;
+ struct iohub_dma_fifo_t *fifo;
+ unsigned int rxctrl, ictrl;
+ unsigned long error;
+ int verbose;
+
+ /* Get the selected verbosity */
+ verbose = (pic->driver) ? (pic->driver->verbose) : (0);
+
+ /* If we have an error, then always restart the DMA-channel */
+ ifs = readl(pic->iohub_addr + IOHUB_IFS_REG);
+ error = 0;
+
+ if (ifs & IOHUB_IFS_RXNRIP) {
+ ictrl = readl(pic->iohub_addr + IOHUB_RX_DMA_ICTRL_REG);
+ rxctrl = readl(pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+
+ if (verbose && printk_ratelimit())
+ printk_warn("RXNRIP: ictrl 0x%08x | rxctrl 0x%08x\n", ictrl, rxctrl);
+ error |= IOHUB_IFS_RXNRIP;
+ }
+
+ if (!(ifs & IOHUB_IFS_RXNCIP)) {
+
+ if (verbose && printk_ratelimit())
+ printk_warn("RXNCIP: Unexpected state (ifs: %x)\n", ifs);
+ error |= IOHUB_IFS_RXNCIP;
+ }
+
+ /* If we have a failure, then reset the DMA-controller and -FIFO */
+ if (error) {
+ fifo = &pic->rx_fifo;
+ writel(fifo->phys_descs, pic->iohub_addr + IOHUB_RX_DMA_BUFPTR_REG);
+ writel(0x00, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+ writel(ifs, pic->iohub_addr + IOHUB_IFS_REG);
+ writel(IOHUB_RX_DMA_CTRL_CE, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+
+ /* This is for the RX tasklet that will inform the FIM-driver about this failure */
+ fifo->rx_error = error;
+ }
+
+ /*
+ * @IMPORTANT: The API of Net+OS is restarting the RX-channel after each
+ * transfer, but we don't know exactly why. Since our API is working
+ * without the restart, we don't touch the RX-channel.
+ */
+#if 0
+ rxctrl = readl(pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+ rxctrl &= ~IOHUB_RX_DMA_CTRL_CE;
+ writel(rxctrl, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+ writel(rxctrl | IOHUB_RX_DMA_CTRL_CE, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+#endif
+
+ /* Schedule the task which will free the RX-DMA buffer descriptors */
+ tasklet_hi_schedule(&pic->rx_tasklet);
+}
+
+
+/* This is the main ISR for the PIC-interrupts */
+static irqreturn_t pic_irq(int irq, void *tpic)
+{
+ unsigned int ifs;
+ struct pic_t *pic = (struct pic_t *)tpic;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pic->lock, flags);
+ ifs = readl(pic->iohub_addr + IOHUB_IFS_REG);
+
+ if (ifs & IOHUB_IFS_MODIP)
+ isr_from_pic(pic, irq);
+ if (ifs & IOHUB_IFS_DMA_TX)
+ isr_dma_tx(pic, irq);
+ if (ifs & IOHUB_IFS_DMA_RX)
+ isr_dma_rx(pic, irq);
+
+ writel(ifs, pic->iohub_addr + IOHUB_IFS_REG);
+ spin_unlock_irqrestore(&pic->lock, flags);
+ return IRQ_HANDLED;
+}
+
+
+
+int fim_enable_irq(struct fim_driver *driver)
+{
+ struct pic_t *pic;
+
+ if (!driver)
+ return -EINVAL;
+
+ if (!(pic = get_pic_from_driver(driver)))
+ return -EINVAL;
+
+ /* IRQ enable if required */
+ if (!atomic_read(&pic->irq_enabled)) {
+ printk_debug("Enabling the PIC IRQ %i\n", pic->irq);
+ enable_irq(pic->irq);
+ atomic_set(&pic->irq_enabled, 1);
+ }
+ return 0;
+}
+
+
+int fim_disable_irq(struct fim_driver *driver)
+{
+ struct pic_t *pic;
+
+ if (!driver)
+ return -EINVAL;
+
+ if (!(pic = get_pic_from_driver(driver)))
+ return -EINVAL;
+
+ /* Disable the IRQ */
+ if (atomic_read(&pic->irq_enabled)) {
+ printk_debug("Disabling the PIC IRQ %i\n", pic->irq);
+ disable_irq(pic->irq);
+ atomic_set(&pic->irq_enabled, 0);
+ }
+ return 0;
+}
+
+/*
+ * Function for downloading the FIM-firmware
+ * IMPORTANT: This function will automatically stop the FIM if it's running,
+ * additionally it will (try) to restore the current state of the DMA-channels
+ */
+int fim_download_firmware(struct fim_driver *driver)
+{
+ struct pic_t *pic;
+ int retval;
+ const struct firmware *fw;
+ const unsigned char *fwbuf;
+ unsigned int txctrl, rxctrl;
+
+ if (!(pic = get_pic_from_driver(driver)))
+ return -EINVAL;
+
+ /* Stop the FIM first */
+ if (pic_is_running(pic)) {
+ retval = pic_stop_and_reset(pic);
+ if (retval) {
+ printk_err("Couldn't stop the PIC %i\n", pic->index);
+ return retval;
+ }
+ }
+
+ /*
+ * Now download the firmware using the firmware-subsystem or from
+ * the passed array with the firmware code
+ */
+ if (driver->fw_name) {
+ printk_debug("Requesting the firmware '%s'\n", driver->fw_name);
+ snprintf(pic->fw_name, FIM_MAX_FIRMWARE_NAME, "%s", driver->fw_name);
+ retval = request_firmware(&fw, driver->fw_name, pic->dev);
+ if (retval) {
+ printk_err("request_firmware() failed, %i\n", retval);
+ goto exit_download;
+ }
+ fwbuf = fw->data;
+ } else if (driver->fw_code) {
+ printk_debug("Using the built-in firmware code\n");
+ fwbuf = driver->fw_code;
+ } else {
+ printk_err("No code and no firmware name passed? Aborting\n");
+ retval = -EINVAL;
+ goto exit_download;
+ }
+
+ /*
+ * Since the firmware download should be executed on the fly, disable
+ * the DMA-channels first, otherwise the channels will run like a
+ * spree killer
+ */
+ spin_lock(&pic->tx_lock);
+ txctrl = readl(pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ rxctrl = readl(pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+ writel(txctrl & ~IOHUB_TX_DMA_CTRL_CE, pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ writel(rxctrl & ~IOHUB_RX_DMA_CTRL_CE, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+ retval = pic_download_firmware(pic, fwbuf);
+ writel(txctrl, pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ writel(rxctrl, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+ spin_unlock(&pic->tx_lock);
+
+ /* Release the obtained data from the firmware-subsystem */
+ if (driver->fw_name)
+ release_firmware(fw);
+
+ if (retval)
+ printk_err("Firmware install in the PIC %i failed.\n", pic->index);
+
+ exit_download:
+ return retval;
+}
+
+
+int fim_register_driver(struct fim_driver *driver)
+{
+ struct pic_t *pic;
+ int retval;
+ const struct firmware *fw;
+ const unsigned char *fwbuf;
+
+ /* Sanity checks */
+ if (!driver || !driver->driver.name)
+ return -EINVAL;
+
+ if (!(pic = get_pic_by_index(driver->picnr)))
+ return -ENODEV;
+
+ if (pic->driver || PIC_IS_LOCKED(pic)) {
+ printk_err("PIC %i already requested.\n", pic->index);
+ return -EBUSY;
+ }
+
+ /* Lock the PIC request */
+ PIC_LOCK_REQUEST(pic);
+
+ /* Stop the PIC before any other action */
+ retval = pic_stop_and_reset(pic);
+ if (retval) {
+ printk_err("Couldn't stop and reset the FIM.\n");
+ goto exit_free_pic;
+ }
+
+ /* Try to get the firmware from the user space */
+ if (driver->fw_name) {
+ printk_debug("Requesting the firmware '%s'\n", driver->fw_name);
+ snprintf(pic->fw_name, FIM_MAX_FIRMWARE_NAME, "%s", driver->fw_name);
+ retval = request_firmware(&fw, driver->fw_name, pic->dev);
+ if (retval) {
+ printk_err("request_firmware() failed, %i\n", retval);
+ goto exit_free_pic;
+ }
+ fwbuf = fw->data;
+ } else if (driver->fw_code) {
+ printk_debug("Using the built-in firmware code\n");
+ fwbuf = driver->fw_code;
+ } else {
+ printk_err("%s: Neither code nor firmware passed. Aborting request.\n",
+ driver->driver.name);
+ goto exit_free_pic;
+ }
+
+ retval = pic_download_firmware(pic, fwbuf);
+
+ /* Release the obtained data from the firmware-subsystem */
+ if (driver->fw_name)
+ release_firmware(fw);
+
+ if (retval) {
+ printk_err("%s: Firmware install by the FIM%i failed.\n",
+ driver->driver.name, pic->index);
+ goto exit_free_pic;
+ }
+
+ /* Start the PIC at zero */
+ retval = pic_start_at_zero(pic);
+ if (retval) {
+ printk_err("%s: FIM%i couldn't be started at zero.\n",
+ driver->driver.name, pic->index);
+ goto exit_free_pic;
+ }
+
+ /*
+ * The DMA channels will be controlled by the CPU. Pass the driver-config
+ * values to the init-function
+ */
+ retval = pic_dma_init(pic, driver->dma_cfg);
+ if (retval) {
+ printk_err("%s: DMA channel init failed for the FIM%i\n",
+ driver->driver.name, pic->index);
+ goto goto_stop_pic;
+ }
+
+ /*
+ * Enable the tasklet before requesting the IRQ, then in some cases the
+ * FIM sends an interrupt before its initialization through the FIM-driver.
+ */
+ pic->driver = driver;
+ driver->dev = pic->dev;
+ tasklet_init(&pic->rx_tasklet, pic_rx_tasklet_func, (unsigned long)pic);
+
+ /* Request the IRQ for this FIM */
+ retval = request_irq(pic->irq, pic_irq, 0, driver->driver.name, pic);
+ if (retval) {
+ printk_err("%s: Couldn't request the IRQ %i\n",
+ driver->driver.name, pic->irq);
+ goto exit_kill_tasklet;
+ }
+
+ /* Disable the IRQ and done */
+ disable_irq(pic->irq);
+ atomic_set(&pic->irq_enabled, 0);
+ printk_info("FIM%i registered for driver '%s' [IRQ %i]\n",
+ pic->index, driver->driver.name, pic->irq);
+
+ return 0;
+
+ exit_kill_tasklet:
+ tasklet_kill(&pic->rx_tasklet);
+
+ goto_stop_pic:
+ pic_stop_and_reset(pic);
+
+ exit_free_pic:
+ PIC_FREE_REQUEST(pic);
+
+ return retval;
+}
+
+
+int fim_unregister_driver(struct fim_driver *driver)
+{
+ int ret;
+ struct pic_t *pic;
+
+ if (!driver)
+ return -EINVAL;
+
+ if (!(pic = get_pic_from_driver(driver)))
+ return -EINVAL;
+
+ if (!pic->driver || !PIC_IS_LOCKED(pic))
+ return -EINVAL;
+
+ /* Stop the PIC but wait for another running operations first */
+ spin_lock(&pic->tx_lock);
+ ret = pic_stop_and_reset(pic);
+ if (ret)
+ printk_err("Couldn't stop the PIC %i\n", pic->index);
+
+ fim_disable_irq(pic->driver);
+
+ /* Free the requested IRQ */
+ printk_debug("Freeing the IRQ %i\n", pic->irq);
+ free_irq(pic->irq, pic);
+
+ /* Free the DMA-resources */
+ tasklet_kill(&pic->rx_tasklet);
+ pic_dma_stop(pic);
+
+ /* And free the driver field */
+ pic->driver = NULL;
+ PIC_FREE_REQUEST(pic);
+ spin_unlock(&pic->tx_lock);
+ return 0;
+}
+
+
+/*
+ * Return a PIC-pointer for low level operations
+ * The driver structure contains the number of the pointer
+ */
+struct pic_t *fim_request_pic(int picnr)
+{
+ struct pic_t *pic;
+
+ if (!(pic = get_pic_by_index(picnr)))
+ return NULL;
+
+ if (pic->driver || PIC_IS_LOCKED(pic))
+ return NULL;
+
+ PIC_LOCK_REQUEST(pic);
+ writel(0x00, pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+ printk_info("PIC %i successful requested\n", pic->index);
+ return pic;
+}
+
+
+/*
+ * Free the PIC that was requested with the above function fim_request_pic()
+ */
+void fim_free_pic(struct pic_t *pic)
+{
+ if (!pic)
+ return;
+
+ if (!(get_pic_by_index(pic->index)))
+ return;
+
+ PIC_FREE_REQUEST(pic);
+}
+
+
+/*
+ * This function can be used for downloading a firmware to the PIC.
+ * Please note that this function will reset the complete IOHUB-module, included the
+ * DMA-configuration registers. That's important when the FIM-driver
+ * is using the index of the DMA-channel, then this will have the index zero after
+ * the download.
+ */
+static int pic_download_firmware(struct pic_t *pic, const unsigned char *buffer)
+{
+ int mode, ret;
+ unsigned int status;
+ struct fim_program_t *program = (struct fim_program_t *)buffer;
+ int offset;
+
+ if (!program || !pic)
+ return -EINVAL;
+
+ if (!(FORMAT_TYPE_VALID(program->format)))
+ return -EINVAL;
+
+ printk_debug("Starting the download of the PIC firmware.\n");
+
+ /* Check if the PIC is running, before starting the firmware update */
+ if (pic_is_running(pic)) {
+ printk_err("The PIC %i is running. Aborting the download.\n",
+ pic->index);
+ return -EBUSY;
+ }
+
+ /* Check if the firmware has the correct header */
+ if (!(PROCESSOR_TYPE_VALID(program->processor))) {
+ printk_err("Invalid processor type. Aborting firmware download.\n");
+ return -EINVAL;
+ }
+
+ /* Enable the clock to IO processor and reset the module */
+ status = readl(SYS_CLOCK);
+ writel(status | (1 << (pic->index + FIM0_SHIFT)), SYS_CLOCK);
+ status = readl(SYS_RESET);
+ writel(status & ~(1 << (pic->index + FIM0_SHIFT)), SYS_RESET);
+ writel(status | (1 << (pic->index + FIM0_SHIFT)), SYS_RESET);
+
+ /* Configure the output clock */
+ ret = pic_config_output_clock_divisor(pic, program);
+ if (ret) {
+ printk_err("Couldn't set the clock output divisor.\n");
+ return ret;
+ }
+
+ switch (program->hw_mode) {
+ case FIM_HW_ASSIST_MODE_NONE:
+ mode = 0x00;
+ break;
+ case FIM_HW_ASSIST_MODE_GENERIC:
+ mode = 0x01;
+ break;
+ case FIM_HW_ASSIST_MODE_CAN:
+ mode = 0x02;
+ break;
+ default:
+ printk_err("Invalid HWA mode %i\n", program->hw_mode);
+ return -EINVAL;
+ }
+
+ status = readl(pic->hwa_addr + NS92XX_FIM_HWA_GEN_CONF_REG);
+ writel(mode | status, pic->hwa_addr + NS92XX_FIM_HWA_GEN_CONF_REG);
+
+ /* Update the HW assist config registers */
+ for (offset = 0; offset < FIM_NUM_HWA_CONF_REGS; offset++) {
+ status = program->hwa_cfg[offset];
+ writel(status, pic->hwa_addr + NS92XX_FIM_HWA_SIGNAL(offset));
+ }
+
+ /* Check for the maximal supported number of instructions */
+ if (program->length > FIM_NS9215_MAX_INSTRUCTIONS) {
+ printk_err("Invalid firmware length %i (too long)\n", program->length);
+ return -EINVAL;
+ }
+
+ /* Start programming the PIC (the program size is in 16bit-words) */
+ printk_debug("Starting to program the firmware (%i Bytes)\n",
+ program->length);
+ for (offset = 0; offset < program->length; offset++)
+ writel(program->data[offset] & NS92XX_FIM_INSTRUCTION_MASK,
+ pic->instr_addr + 4*offset);
+
+ /* Save the firmware length into the internal structure for the sysfs */
+ pic->fw_length = program->length;
+ return 0;
+}
+
+
+
+static int pic_start_at_zero(struct pic_t *pic)
+{
+ unsigned int regval;
+
+ if (!pic)
+ return -EINVAL;
+ if (pic->index < 0)
+ return -ENODEV;
+
+ printk_debug("Starting the PIC %i at zero.\n", pic->index);
+
+ regval = readl(pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+ writel(regval | NS92XX_FIM_GEN_CTRL_START_PIC, pic->reg_addr +
+ NS92XX_FIM_GEN_CTRL_REG);
+
+ /* Check if the PIC is really running */
+ if (!pic_is_running(pic)) {
+ printk_err("Unable to start the PIC %i\n", pic->index);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+
+static int pic_stop_and_reset(struct pic_t *pic)
+{
+ unsigned int regval;
+ int retval;
+
+ if (!pic)
+ return -EINVAL;
+
+ if (pic_is_running(pic))
+ printk_debug("The PIC %i is running, need to stop it.\n", pic->index);
+
+ regval = readl(pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+
+ /* Clear the interrupt flags too! */
+ regval &= ~NS92XX_FIM_INT_MASK(0xff);
+
+ writel(regval & NS92XX_FIM_GEN_CTRL_STOP_PIC,
+ pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+
+ retval = 0;
+ if (pic_is_running(pic)) {
+ printk_err("Unabled to stop the PIC %i\n", pic->index);
+ retval = -EAGAIN;
+ }
+
+ /* Reset the HWA generial register too */
+ writel(0x00, pic->hwa_addr + NS92XX_FIM_HWA_GEN_CONF_REG);
+
+ return retval;
+}
+
+/*
+ * This function stops the FIM and check if the DMA-channel still has a buffer
+ * for the FIM.
+ */
+int fim_send_reset(struct fim_driver *driver)
+{
+ struct pic_t *pic;
+ int retval, cnt, fifo_reset;
+ struct iohub_dma_fifo_t *fifo;
+ struct iohub_dma_desc_t *desc;
+
+ if (!(pic = get_pic_by_index(driver->picnr)))
+ return -EINVAL;
+
+ retval = pic_stop_and_reset(pic);
+ if (retval)
+ return retval;
+
+ /*
+ * For avoiding errors by the restart of the FIM, check if a DMA buffer
+ * is waiting for being transmitted. In that case, reset the buffer and
+ * reset the index of the DMA-controller.
+ */
+ fifo = &pic->tx_fifo;
+ fifo_reset = 0;
+ for (desc = fifo->first, cnt = 0; cnt < fifo->length; cnt++) {
+
+ /*
+ * If we are stopping the FIM but have some DMA-buffers pending, then
+ * we need to reset the DMA-channel before the next restart.
+ */
+ if (!fifo_reset && (desc->control & IOHUB_DMA_DESC_CTRL_FULL))
+ fifo_reset = 1;
+
+ desc->control = 0;
+ desc = fim_dma_get_next(fifo, desc);
+ }
+
+ if (fifo_reset) {
+ unsigned long txctrl;
+
+ printk_warn("Stopping FIM %i with a full DMA buffer!\n",
+ pic->index);
+ fim_dma_reset_fifo(fifo);
+ txctrl = IOHUB_TX_DMA_CTRL_INDEXEN | IOHUB_TX_DMA_CTRL_INDEX(0);
+ writel(txctrl, pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ }
+
+ /*
+ * Reset the RX-channel too.
+ * IMPORTANT: Set the wrap control bit by the last DMA-buffer, otherwise
+ * the FIFO will overflow!
+ */
+ fifo = &pic->rx_fifo;
+ for (desc = fifo->first, cnt = 0; cnt < fifo->length; cnt++) {
+ desc->control = IOHUB_DMA_DESC_CTRL_INT;
+ desc = fim_dma_get_next(fifo, desc);
+ }
+ fifo->last->control |= IOHUB_DMA_DESC_CTRL_WRAP;
+ fim_dma_reset_fifo(fifo);
+
+ /* Free the internal resources */
+ atomic_set(&pic->tx_tasked, 0);
+ atomic_set(&pic->tx_aborted, 0);
+
+ return retval;
+}
+
+
+int fim_send_start(struct fim_driver *driver)
+{
+ ulong reg;
+
+ struct pic_t *pic;
+ if (!(pic = get_pic_by_index(driver->picnr)))
+ return -EINVAL;
+
+ /* Connect the clock to the FIM */
+ reg = readl(SYS_CLOCK);
+ writel(reg | (1 << (pic->index + FIM0_SHIFT)), SYS_CLOCK);
+
+ reg = readl(SYS_RESET);
+ writel(reg | (1 << (pic->index + FIM0_SHIFT)), SYS_RESET);
+
+ return pic_start_at_zero(pic);
+}
+
+int fim_send_stop(struct fim_driver *driver)
+{
+ struct pic_t *pic;
+ int ret;
+ ulong reg;
+
+ if (!(pic = get_pic_by_index(driver->picnr)))
+ return -EINVAL;
+
+ reg = readl(pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+
+ /* Clear the interrupt flags too! */
+ reg &= ~NS92XX_FIM_INT_MASK(0xff);
+
+ writel(reg & ~NS92XX_FIM_GEN_CTRL_START_PIC,
+ pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+
+ ret = 0;
+ if (pic_is_running(pic)) {
+ printk_err("Unabled to stop the PIC %i\n", pic->index);
+ ret = -EAGAIN;
+ }
+
+ /* Disable the clock and reset the module */
+ reg = readl(SYS_CLOCK);
+ writel(reg & ~(1 << (pic->index + FIM0_SHIFT)), SYS_CLOCK);
+ reg = readl(SYS_RESET);
+ writel(reg & ~(1 << (pic->index + FIM0_SHIFT)), SYS_RESET);
+
+ return ret;
+}
+
+int fim_send_interrupt2(struct fim_driver *driver, unsigned int code)
+{
+ struct pic_t *pic;
+ if (!driver || !(pic = get_pic_by_index(driver->picnr)))
+ return -EINVAL;
+
+ return pic_send_interrupt(pic, code);
+}
+
+
+/*
+ * This function provides the access to the control registers of the PICs
+ * reg : Number of the control register (from 0 to 15)
+ * val : Value to write into the control register
+ */
+void fim_set_ctrl_reg(struct fim_driver *driver, int reg, unsigned int val)
+{
+ struct pic_t *pic;
+
+ if (!(pic = get_pic_from_driver(driver)))
+ return;
+
+ writel(val, pic->reg_addr + NS92XX_FIM_CTRL_REG(reg));
+}
+
+
+/* Provides the read access to the control registers of the PICs */
+int fim_get_ctrl_reg(struct fim_driver *driver, int reg, unsigned int *val)
+{
+ struct pic_t *pic;
+
+ if (!(pic = get_pic_from_driver(driver)) || !val)
+ return -EINVAL;
+
+ if (NS92XX_FIM_CTRL_REG_CHECK(reg))
+ return -EINVAL;
+
+ *val = readl(pic->reg_addr + NS92XX_FIM_CTRL_REG(reg));
+ return 0;
+}
+
+
+
+int fim_get_stat_reg(struct fim_driver *driver, int reg, unsigned int *val)
+{
+ struct pic_t *pic;
+
+ if (!(pic = get_pic_from_driver(driver)) || !val)
+ return -EINVAL;
+
+ if (NS92XX_FIM_STAT_REG_CHECK(reg))
+ return -EINVAL;
+
+ *val = readl(pic->reg_addr + NS92XX_FIM_STAT_REG(reg));
+ return 0;
+}
+
+
+
+/* Interrupt the PIC sending the interrput with the number `code' */
+static int pic_send_interrupt(struct pic_t *pic, u32 code)
+{
+ unsigned int stopcnt;
+ u32 status;
+
+ if (!pic || !code || (code & ~0x7f))
+ return -EINVAL;
+
+ if (!pic_is_running(pic)) {
+ printk_err("The PIC %i isn't running. Aborting the IRQ request.\n",
+ pic->index);
+ return -EAGAIN;
+ }
+
+ code = NS92XX_FIM_INT_MASK(code);
+ status = readl(pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+ writel(status | code, pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+
+ /* This loop is perhaps problematic, exit with a timeout */
+ stopcnt = 0xFFFF;
+ do {
+ status = readl(pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+ stopcnt--;
+ } while (!(status & NS92XX_FIM_GEN_CTRL_INTACKRD) && stopcnt);
+
+ if (!stopcnt) {
+ printk_err("Timeout waiting for RDACK from the PIC %i\n", pic->index);
+ return -EINVAL;
+ }
+
+ /* Reset the interrupt bits for the PIC acknowledge */
+ status &= ~NS92XX_FIM_GEN_CTRL_INTTOPIC;
+ writel(status, pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+
+ stopcnt = 0xFFFF;
+ do {
+ status = readl(pic->reg_addr + NS92XX_FIM_GEN_CTRL_REG);
+ stopcnt--;
+ } while ((status & NS92XX_FIM_GEN_CTRL_INTACKRD) && stopcnt);
+
+ if (!stopcnt) {
+ printk_err("Timeout by the second RDACK from the PIC %i\n", pic->index);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+/* Set the HWA PIC clock (see PIC module specification, page 19) */
+static int pic_config_output_clock_divisor(struct pic_t *pic,
+ struct fim_program_t *program)
+{
+ int div;
+ int clkd;
+ unsigned int val;
+
+ if (!pic || !program) return -EINVAL;
+
+ div = program->clkdiv;
+ switch (div) {
+ case FIM_CLK_DIV_2:
+ clkd = FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_2;
+ break;
+
+ case FIM_CLK_DIV_4:
+ clkd = FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_4;
+ break;
+
+ case FIM_CLK_DIV_8:
+ clkd = FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_8;
+ break;
+
+ case FIM_CLK_DIV_16:
+ clkd = FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_16;
+ break;
+
+ case FIM_CLK_DIV_32:
+ clkd = FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_32;
+ break;
+
+ case FIM_CLK_DIV_64:
+ clkd = FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_64;
+ break;
+
+ case FIM_CLK_DIV_128:
+ clkd = FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_128;
+ break;
+
+ case FIM_CLK_DIV_256:
+ clkd = FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_256;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ val = readl(pic->hwa_addr + NS92XX_FIM_HWA_GEN_CONF_REG);
+ writel(val | clkd, pic->hwa_addr + NS92XX_FIM_HWA_GEN_CONF_REG);
+ return 0;
+}
+
+
+static int fim_probe(struct device *dev)
+{
+ int i, cnt, retval;
+ struct pic_t *pic;
+
+ if (!dev)
+ return -EINVAL;
+
+ printk_debug("Starting the FIM-probe of the device `%s'\n", dev->bus_id);
+
+ /* Use the get_pic_by_index function for verifying that we have a FIM attached */
+ pic = (struct pic_t *)dev->driver_data;
+ pic = get_pic_by_index(pic->index);
+ if (!pic) {
+ printk_err("Invalid PIC index %i\n", pic->index);
+ return -ENODEV;
+ }
+
+ i = pic->index;
+ pic->dev = dev;
+ pic->reg_addr = ioremap(NS92XX_FIM_REG_BASE_PA + i*NS92XX_FIM_REG_OFFSET,
+ NS92XX_FIM_REG_SIZE);
+ pic->instr_addr = ioremap(NS92XX_FIM_INSTR_BASE_PA + i*NS92XX_FIM_INSTR_OFFSET,
+ NS92XX_FIM_INSTR_SIZE);
+ pic->hwa_addr = ioremap(NS92XX_FIM_HWA_BASE_PA + i*NS92XX_FIM_HWA_OFFSET,
+ NS92XX_FIM_HWA_SIZE);
+ pic->iohub_addr = ioremap(NS92XX_FIM_IOHUB_BASE_PA + i*NS92XX_FIM_IOHUB_OFFSET,
+ NS92XX_FIM_IOHUB_SIZE);
+
+ /* @FIXME: Unmap the already registered memory addresses */
+ if (!pic->reg_addr || !pic->instr_addr || !pic->hwa_addr || !pic->iohub_addr) {
+ printk_err("ioremap() failed by the PIC %i\n", pic->index);
+ retval = -EAGAIN;
+ goto exit_unmap;
+ }
+
+ spin_lock_init(&pic->lock);
+
+ /* Create the file attributes under the sysfs */
+ retval = 0;
+ for (cnt=0; cnt < ARRAY_SIZE(fim_sysfs_attrs); cnt++) {
+ retval = sysfs_create_bin_file(&dev->kobj, &fim_sysfs_attrs[cnt]);
+ if (retval) {
+ while (cnt)
+ sysfs_remove_bin_file(&dev->kobj,
+ &fim_sysfs_attrs[--cnt]);
+ goto exit_unmap;
+ }
+ }
+
+ /* Set the function pointers for providing low level access to the PICs */
+ pic->is_running = pic_is_running;
+ pic->start_at_zero = pic_start_at_zero;
+ pic->stop_and_reset = pic_stop_and_reset;
+ pic->download_firmware = pic_download_firmware;
+ pic->get_ctrl_reg = pic_get_ctrl_reg;
+ pic->set_ctrl_reg = pic_set_ctrl_reg;
+ pic->send_interrupt = pic_send_interrupt;
+ pic->ack_interrupt = isr_from_pic;
+ return 0;
+
+ exit_unmap:
+ if (pic->reg_addr) iounmap(pic->reg_addr);
+ if (pic->instr_addr) iounmap(pic->instr_addr);
+ if (pic->hwa_addr) iounmap(pic->hwa_addr);
+ if (pic->iohub_addr) iounmap(pic->iohub_addr);
+ pic->reg_addr = pic->instr_addr = pic->hwa_addr = pic->iohub_addr = NULL;
+
+
+ return retval;
+}
+
+
+/* This function will be called for each PIC-device (but not for the parent device) */
+static int fim_remove(struct device *dev)
+{
+ int retval, cnt;
+ struct pic_t *pic = (struct pic_t *)dev->driver_data;
+ if (!pic)
+ return -EINVAL;
+
+ printk_info("Starting to remove the PIC %s\n", dev->bus_id);
+
+ if (pic->driver) {
+ retval = fim_unregister_driver(pic->driver);
+ if (retval) {
+ printk_err("Can unregister the PIC %i, %i\n", pic->index,
+ retval);
+ }
+ pic->driver = NULL;
+ }
+
+ /* Remove the created sysfs attributes */
+ for (cnt=0; cnt < ARRAY_SIZE(fim_sysfs_attrs); cnt++)
+ sysfs_remove_bin_file(&dev->kobj, &fim_sysfs_attrs[cnt]);
+
+ /* Free the mapped pages of the IO memory */
+ if (pic->reg_addr) iounmap(pic->reg_addr);
+ if (pic->instr_addr) iounmap(pic->instr_addr);
+ if (pic->hwa_addr) iounmap(pic->hwa_addr);
+ if (pic->iohub_addr) iounmap(pic->iohub_addr);
+ pic->dev = NULL;
+ return 0;
+}
+
+
+/* This function returns a non-zero value if the device correspond to our fim-bus */
+static int fim_bus_match(struct device *dev, struct device_driver *driver)
+{
+ printk_debug("@TODO: Create a match mechanism for the FIMs.\n");
+ return 1;
+}
+
+
+struct bus_type fim_bus_type = {
+ .name = FIM_BUS_TYPE_NAME,
+ .match = fim_bus_match,
+};
+
+
+/*
+ * Bus parent that will be registered without calling the probe function
+ * the sys file is under: /sys/devices/fims/
+ */
+struct device fim_bus_dev = {
+ .bus_id = "fims",
+ .release = fim_pic_release
+};
+
+
+static struct device_driver fims_driver = {
+ .probe = fim_probe,
+ .remove = fim_remove,
+ .name = FIM_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .bus = &fim_bus_type,
+};
+
+
+
+/*
+ * These are the two PIC devices that we currently have in the NS9215
+ * With the help of the `bus type' the probe function will be called when
+ * the devices are registered
+ * Sys path: /sys/bus/fim-bus/devices/fim[01..]
+ */
+static struct device fim_pics[] = {
+ {
+ .bus_id = "fim0",
+ .release = fim_pic_release,
+ .bus = &fim_bus_type,
+ .parent = &fim_bus_dev,
+ },
+ {
+ .bus_id = "fim1",
+ .release = fim_pic_release,
+ .bus = &fim_bus_type,
+ .parent = &fim_bus_dev,
+ }
+};
+
+
+/*
+ * If no configuration for the DMA-buffer descriptors is passed (cfg = NULL),
+ * then use the default values. These are defined under the header file 'fim.h'
+ */
+inline static int pic_dma_check_config(struct pic_t *pic, struct fim_dma_cfg_t *cfg)
+{
+ if (cfg) {
+ if (cfg->rxnr <= 0 || cfg->rxnr > IOHUB_MAX_DMA_BUFFERS ||
+ cfg->txnr <= 0 || cfg->txnr > IOHUB_MAX_DMA_BUFFERS) {
+ printk_err("Invalid number of RX/TX-DMA buffers (%i | %i)\n",
+ cfg->rxnr, cfg->txnr);
+ return -EINVAL;
+ }
+
+ if (cfg->rxsz > IOHUB_MAX_DMA_LENGTH || cfg->rxsz <= 0 ||
+ cfg->txsz > IOHUB_MAX_DMA_LENGTH || cfg->txsz <= 0) {
+ printk_err("Invalid DMA-buffer length (%i | %i)\n",
+ cfg->rxsz, cfg->txsz);
+ return -EINVAL;
+ }
+ }
+
+ pic->dma_cfg.rxnr = (!cfg) ? PIC_DMA_RX_BUFFERS : cfg->rxnr;
+ pic->dma_cfg.txnr = (!cfg) ? PIC_DMA_TX_BUFFERS : cfg->txnr;
+ pic->dma_cfg.rxsz = (!cfg) ? PIC_DMA_BUFFER_SIZE : cfg->rxsz;
+ pic->dma_cfg.txsz = (!cfg) ? PIC_DMA_BUFFER_SIZE : cfg->txsz;
+ return 0;
+}
+
+
+/*
+ * This function starts the DMA-buffers and -fifos for the passed PIC
+ * rxs : Number of RX-buffer descriptors to use
+ * txs : Number of TX-buffer descriptors to create
+ * rxlen : Length of each RX-DMA buffer
+ * txlen : Length of each TX-DMA buffer
+ * @IMPORTANT: No sanity check will be executed in this function
+ */
+static int pic_dma_init(struct pic_t *pic, struct fim_dma_cfg_t *cfg)
+{
+ int retval;
+ struct iohub_dma_desc_t *desc;
+ struct pic_dma_desc_t *pic_desc;
+ int cnt;
+ dma_addr_t phys_buffers;
+
+ if (!pic || !pic->dev)
+ return -EINVAL;
+
+ /* Sanity checks for the passed DMA-descriptors configuration */
+ if ((retval = pic_dma_check_config(pic, cfg)))
+ return retval;
+
+ /* Get the DMA-memory for the RX- and TX-buffers */
+ cfg = &pic->dma_cfg;
+ pic->dma_size = (cfg->rxnr * cfg->rxsz) + (cfg->txnr * cfg->txsz);
+ pic->dma_virt = dma_alloc_coherent(NULL, pic->dma_size,
+ &pic->dma_phys, GFP_KERNEL);
+ if (!pic->dma_virt)
+ return -ENOMEM;
+
+ /*
+ * Get the DMA-memory for the RX-descriptors (FIFOs)
+ */
+ pic->rx_fifo.descs = dma_alloc_coherent(NULL,
+ cfg->rxnr * IOHUB_DMA_DESC_LENGTH,
+ &pic->rx_fifo.phys_descs,
+ GFP_KERNEL);
+ if (!pic->rx_fifo.descs) {
+ retval = -ENOMEM;
+ goto free_dma_bufs;
+ }
+
+ fim_dma_init_fifo(&pic->rx_fifo, cfg->rxnr, pic->rx_fifo.descs);
+ printk_debug("RX FIFO descriptors range is 0x%p to 0x%p [0x%08x]\n",
+ pic->rx_fifo.first, pic->rx_fifo.last, pic->rx_fifo.phys_descs);
+
+ /* Setup the RX-descriptors with the corresponding DMA-buffers */
+ phys_buffers = pic->dma_phys;
+ desc = pic->rx_fifo.first;
+ do {
+ desc->length = cfg->rxsz;
+ desc->src = phys_buffers;
+ desc->control = IOHUB_DMA_DESC_CTRL_INT;
+ printk_debug("[ RX %p ] 0x%p (0x%08x) | 0x%08x | 0x%04x\n",
+ desc, phys_to_virt(desc->src), desc->src, desc->length,
+ desc->control);
+ phys_buffers += cfg->rxsz;
+ desc = fim_dma_get_next(&pic->rx_fifo, desc);
+ } while (desc != pic->rx_fifo.first);
+ pic->rx_fifo.last->control |= IOHUB_DMA_DESC_CTRL_WRAP;
+
+ /*
+ * Now create the DMA-memory for the TX-buffer descriptors
+ */
+ pic->tx_fifo.descs = dma_alloc_coherent(NULL,
+ cfg->txnr * IOHUB_DMA_DESC_LENGTH,
+ &pic->tx_fifo.phys_descs,
+ GFP_KERNEL);
+ if (!pic->tx_fifo.descs) {
+ retval = -ENOMEM;
+ goto free_dma_rxfifo;
+ }
+
+ fim_dma_init_fifo(&pic->tx_fifo, cfg->txnr, pic->tx_fifo.descs);
+ printk_debug("TX FIFO descriptors range is 0x%p to 0x%p [0x%08x]\n",
+ pic->tx_fifo.first, pic->tx_fifo.last, pic->tx_fifo.phys_descs);
+ if(!(pic->tx_desc = kzalloc(cfg->txsz *
+ sizeof(struct pic_dma_desc_t), GFP_KERNEL))) {
+ printk_err("Couldn't create the TX-dma descriptors\n");
+ retval = -ENOMEM;
+ goto free_dma_rxfifo;
+ }
+
+ /* Setup the TX-buffers and -descriptors */
+ desc = pic->tx_fifo.first;
+ cnt = 0;
+ do {
+ desc->length = cfg->txsz;
+ desc->src = phys_buffers;
+ desc->control = 0;
+ pic_desc = pic->tx_desc + cnt++;
+ pic_desc->src = phys_buffers;
+ pic_desc->length = cfg->txsz;
+ printk_debug("[ TX %p ] 0x%p (0x%08x) | 0x%08x | 0x%04x\n",
+ desc, phys_to_virt(desc->src), desc->src, desc->length,
+ desc->control);
+
+ phys_buffers += cfg->txsz;
+ desc = fim_dma_get_next(&pic->tx_fifo, desc);
+ } while (desc != pic->tx_fifo.first);
+
+ /* Set the last buffer descriptor with the WRAP bit */
+ pic->tx_fifo.last->control = IOHUB_DMA_DESC_CTRL_WRAP;
+
+ /*
+ * Enable the interrupts for the TX-DMA channel and set the buf desc pointer
+ * TXCAIP : used for flushing the TX-buffers
+ */
+ spin_lock_init(&pic->tx_lock);
+ writel(0x00, pic->iohub_addr + IOHUB_TX_ICTRL_REG);
+ writel(IOHUB_TX_DMA_CTRL_CA, pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ writel(0x00, pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ writel(IOHUB_ICTRL_TXNCIE | IOHUB_ICTRL_TXNRIE | IOHUB_ICTRL_TXECIE |
+ IOHUB_IFS_TXCAIP, pic->iohub_addr + IOHUB_TX_ICTRL_REG);
+ writel(pic->tx_fifo.phys_descs, pic->iohub_addr + IOHUB_TX_DMA_BUFPTR_REG);
+
+ /* Enable the interrupts for the RX-DMA channel */
+ spin_lock_init(&pic->rx_lock);
+ writel(0x00, pic->iohub_addr + IOHUB_RX_DMA_ICTRL_REG);
+ writel(IOHUB_RX_DMA_CTRL_CA, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+ writel(IOHUB_ICTRL_RXNCIE | IOHUB_ICTRL_RXNRIE | IOHUB_ICTRL_RXPCIE,
+ pic->iohub_addr + IOHUB_RX_DMA_ICTRL_REG);
+ writel(pic->rx_fifo.phys_descs, pic->iohub_addr + IOHUB_RX_DMA_BUFPTR_REG);
+ writel(IOHUB_RX_DMA_CTRL_CE, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+
+ return 0;
+
+ free_dma_rxfifo:
+ dma_free_coherent(NULL, pic->rx_fifo.length, pic->rx_fifo.descs,
+ pic->rx_fifo.phys_descs);
+ pic->rx_fifo.descs = NULL;
+ pic->rx_fifo.phys_descs = 0;
+
+ free_dma_bufs:
+ dma_free_coherent(NULL, pic->dma_size, pic->dma_virt, pic->dma_phys);
+ pic->dma_virt = NULL;
+ pic->dma_phys = 0;
+
+ return retval;
+}
+
+
+static void pic_dma_stop(struct pic_t *pic)
+{
+ int cnt;
+ struct pic_dma_desc_t *pic_desc;
+
+ if (!pic || !pic->dev)
+ return;
+
+ printk_debug("Freeing the DMA resources of the PIC %i\n", pic->index);
+
+ /* Disable all the interrupts and abort all the DMA-transfers */
+ writel(0x00, pic->iohub_addr + IOHUB_TX_ICTRL_REG);
+ writel(IOHUB_TX_DMA_CTRL_CA, pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+ writel(0x00, pic->iohub_addr + IOHUB_TX_DMA_CTRL_REG);
+
+ writel(0x00, pic->iohub_addr + IOHUB_RX_DMA_ICTRL_REG);
+ writel(IOHUB_RX_DMA_CTRL_CA, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+ writel(0x00, pic->iohub_addr + IOHUB_RX_DMA_CTRL_REG);
+
+ /* Reset the DMA-channel data */
+ atomic_set(&pic->tx_tasked, 0);
+ for (cnt=0; cnt < pic->tx_fifo.length; cnt++) {
+ pic_desc = pic->tx_desc + cnt;
+ atomic_set(&pic_desc->tasked, 0);
+ }
+
+ /* Free the internal TX-descriptors */
+ if (pic->tx_desc) {
+ kfree(pic->tx_desc);
+ pic->tx_desc = NULL;
+ }
+
+ /* Free the FIFOs */
+ if (pic->tx_fifo.descs && pic->tx_fifo.phys_descs) {
+ dma_free_coherent(NULL, pic->tx_fifo.length, pic->tx_fifo.descs,
+ pic->tx_fifo.phys_descs);
+ pic->tx_fifo.descs = NULL;
+ pic->tx_fifo.phys_descs = 0;
+ }
+
+ if (pic->rx_fifo.descs && pic->rx_fifo.phys_descs) {
+ dma_free_coherent(NULL, pic->rx_fifo.length, pic->rx_fifo.descs,
+ pic->rx_fifo.phys_descs);
+ pic->rx_fifo.descs = NULL;
+ pic->rx_fifo.phys_descs = 0;
+ }
+
+ /* Free the DMA-buffers */
+ if (pic->dma_virt && pic->dma_phys) {
+ dma_free_coherent(NULL, pic->dma_size, pic->dma_virt, pic->dma_phys);
+ pic->dma_virt = NULL;
+ pic->dma_phys = 0;
+ }
+}
+
+int fim_dma_stop(struct fim_driver *fim)
+{
+ struct pic_t *pic;
+
+ if (!(pic = get_pic_from_driver(fim)))
+ return -ENODEV;
+
+ pic_dma_stop(pic);
+ return 0;
+}
+
+int fim_dma_start(struct fim_driver *fim, struct fim_dma_cfg_t *cfg)
+{
+ struct pic_t *pic;
+
+ if (!(pic = get_pic_from_driver(fim)))
+ return -ENODEV;
+
+ /* Per default Use the already configured DMA configuration */
+ if (!cfg)
+ cfg = &pic->dma_cfg;
+
+ return pic_dma_init(pic, cfg);
+}
+
+/*
+ * @TODO: We wanted originally to separate the PICs and the virtual devices
+ * initialization, but now we have a part of the PICs-init in this init-function
+ */
+static int __devinit fim_init_module(void)
+{
+ int ret, i;
+ struct pic_t *pic;
+
+ printk_info("Starting to register the FIMs module.\n");
+
+ if (the_fims)
+ return -EINVAL;
+ if (!(the_fims = kzalloc(sizeof(struct fims_t), GFP_KERNEL))) {
+ printk_err("kmalloc() failed.\n");
+ return -ENOMEM;
+ }
+
+ /* Register the internal bus type */
+ ret = bus_register(&fim_bus_type);
+ if (ret) {
+ printk_err("Registering the PICs bus, %i\n", ret);
+ goto goto_free_mem;
+ }
+
+ /* Register the bus parent */
+ ret = device_register(&fim_bus_dev);
+ if (ret) {
+ printk_err( "Couldn't register the parent PIC device, %i\n", ret);
+ goto goto_driver_unreg;
+ }
+
+ ret = driver_register(&fims_driver);
+ if (ret) {
+ printk_err("Couldn't register the FIMs driver, %i\n", ret);
+ goto goto_bus_unreg;
+ }
+
+ /* Start registering the PIC devices */
+ for (i=0; i <= FIM_MAX_PIC_INDEX; i++) {
+ pic = &the_fims->pics[i];
+ pic->index = i;
+ pic->irq = IRQ_NS921X_PIC0 + i;
+ fim_pics[i].driver_data = pic;
+ ret = device_register(&fim_pics[i]);
+ if (ret) {
+ printk_err("Registering the PIC device %i, %i\n", i, ret);
+ while(i) device_unregister(&fim_pics[--i]);
+ goto goto_parent_unreg;
+ }
+ }
+
+ the_fims->driver = &fims_driver;
+ the_fims->bus_dev = &fim_bus_dev;
+ the_fims->bus_type = &fim_bus_type;
+ printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "\n");
+ return 0;
+
+ goto_parent_unreg:
+ device_unregister(&fim_bus_dev);
+
+ goto_driver_unreg:
+ driver_unregister(&fims_driver);
+
+ goto_bus_unreg:
+ bus_unregister(&fim_bus_type);
+
+ goto_free_mem:
+ if(the_fims) kfree(the_fims);
+
+ return ret;
+}
+
+
+static void __devexit fim_exit_module(void)
+{
+ int i;
+
+ printk_info("Unregistering the FIMs module.\n");
+ for (i=0; i <= FIM_MAX_PIC_INDEX; i++) {
+ device_unregister(&fim_pics[i]);
+ }
+
+ device_unregister(&fim_bus_dev);
+ driver_unregister(&fims_driver);
+
+ bus_unregister(the_fims->bus_type);
+ kfree(the_fims);
+ the_fims = NULL;
+}
+
+
+module_init(fim_init_module);
+module_exit(fim_exit_module);
+
+
+EXPORT_SYMBOL(fim_register_driver);
+EXPORT_SYMBOL(fim_unregister_driver);
+EXPORT_SYMBOL(fim_get_exp_reg);
+EXPORT_SYMBOL(fim_send_interrupt2);
+EXPORT_SYMBOL(fim_enable_irq);
+EXPORT_SYMBOL(fim_disable_irq);
+EXPORT_SYMBOL(fim_send_buffer);
+EXPORT_SYMBOL(fim_tx_buffers_room);
+EXPORT_SYMBOL(fim_tx_buffers_level);
+EXPORT_SYMBOL(fim_number_pics);
+EXPORT_SYMBOL(fim_send_reset);
+EXPORT_SYMBOL(fim_send_start);
+EXPORT_SYMBOL(fim_send_stop);
+EXPORT_SYMBOL(fim_flush_rx);
+EXPORT_SYMBOL(fim_flush_tx);
+EXPORT_SYMBOL(fim_alloc_buffer);
+EXPORT_SYMBOL(fim_free_buffer);
+EXPORT_SYMBOL(fim_set_ctrl_reg);
+EXPORT_SYMBOL(fim_get_ctrl_reg);
+EXPORT_SYMBOL(fim_get_stat_reg);
+EXPORT_SYMBOL(fim_request_pic);
+EXPORT_SYMBOL(fim_free_pic);
+EXPORT_SYMBOL(fim_download_firmware);
+EXPORT_SYMBOL(fim_is_running);
+EXPORT_SYMBOL(fim_dma_stop);
+EXPORT_SYMBOL(fim_dma_start);
diff --git a/drivers/fims/fim_reg.h b/drivers/fims/fim_reg.h
new file mode 100644
index 000000000000..b70d434150d3
--- /dev/null
+++ b/drivers/fims/fim_reg.h
@@ -0,0 +1,279 @@
+/*
+ * include/asm-arm/arch-ns9xxx/fim_reg.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * !Revision: $Revision: 1.8 $
+ * !Author: Silvano Najera, Luis Galdos
+ * !Descr:
+ * !References:
+ */
+
+
+#ifndef __FIM_REG_H
+#define __FIM_REG_H
+
+
+/* @FIXME: We must use the IOHUB base address*/
+#define NS92XX_FIM_IOHUB_BASE_PA (0x90000000)
+#define NS92XX_FIM_IOHUB_SIZE (0x1000)
+#define NS92XX_FIM_IOHUB_OFFSET (0x8000)
+
+
+/* Base addresses of all memory regions */
+#define NS92XX_FIM_REG_BASE_PA (0x90001000)
+#define NS92XX_FIM_REG_OFFSET (0x8000)
+#define NS92XX_FIM_REG_SIZE (0x3000)
+
+#define NS92XX_FIM_INSTR_BASE_PA (0x90004000)
+#define NS92XX_FIM_INSTR_OFFSET (0x8000)
+#define NS92XX_FIM_INSTR_SIZE (0x5000)
+
+#define NS92XX_FIM_HWA_BASE_PA (0x90068000)
+#define NS92XX_FIM_HWA_OFFSET (0x8000)
+#define NS92XX_FIM_HWA_SIZE (0x8000)
+
+#define NS92XX_FIM_IO_SPACE_BASE_PA (0x90078000)
+#define NS92XX_FIM_IO_SPACE_OFFSET (0x8000)
+#define NS92XX_FIM_IO_SPACE_SIZE (0x8000)
+
+
+/* FIM register offsets */
+#define NS92XX_FIM_GEN_CTRL_REG (0x00)
+
+#define NS92XX_FIM_CTRL0_REG (0x10)
+#define NS92XX_FIM_CTRL1_REG (0x14)
+#define NS92XX_FIM_CTRL2_REG (0x18)
+#define NS92XX_FIM_CTRL3_REG (0x1c)
+#define NS92XX_FIM_CTRL4_REG (0x20)
+#define NS92XX_FIM_CTRL5_REG (0x24)
+#define NS92XX_FIM_CTRL6_REG (0x28)
+#define NS92XX_FIM_CTRL7_REG (0x2c)
+#define NS92XX_FIM_CTRL8_REG (0x30)
+#define NS92XX_FIM_CTRL9_REG (0x34)
+#define NS92XX_FIM_CTRL10_REG (0x38)
+#define NS92XX_FIM_CTRL11_REG (0x3c)
+#define NS92XX_FIM_CTRL12_REG (0x40)
+#define NS92XX_FIM_CTRL13_REG (0x44)
+#define NS92XX_FIM_CTRL14_REG (0x48)
+#define NS92XX_FIM_CTRL15_REG (0x4c)
+#define NS92XX_FIM_CTRL_REG(i) (NS92XX_FIM_CTRL0_REG + 4*i)
+#define NS92XX_FIM_CTRL_REG_CHECK(i) ((i<0 || i>15)?1:0)
+
+
+/*
+ * According to the HW-reference manual of the FIMs (page 16)
+ * The macros have as offset the address 0x90001000 (NS92XX_FIM_REG_BASE_PA)
+ */
+#define NS92XX_FIM_STAT0_REG (0x50)
+#define NS92XX_FIM_STAT1_REG (0x54)
+#define NS92XX_FIM_STAT2_REG (0x58)
+#define NS92XX_FIM_STAT3_REG (0x5c)
+#define NS92XX_FIM_STAT4_REG (0x60)
+#define NS92XX_FIM_STAT5_REG (0x64)
+#define NS92XX_FIM_STAT6_REG (0x68)
+#define NS92XX_FIM_STAT7_REG (0x6c)
+#define NS92XX_FIM_STAT8_REG (0x70)
+#define NS92XX_FIM_STAT9_REG (0x74)
+#define NS92XX_FIM_STAT10_REG (0x78)
+#define NS92XX_FIM_STAT11_REG (0x7c)
+#define NS92XX_FIM_STAT12_REG (0x80)
+#define NS92XX_FIM_STAT13_REG (0x84)
+#define NS92XX_FIM_STAT14_REG (0x88)
+#define NS92XX_FIM_STAT_REG(i) (NS92XX_FIM_STAT0_REG + 4*i)
+#define NS92XX_FIM_STAT_REG_CHECK(i) ((i<0 || i>14)?1:0)
+
+
+/*
+ * According to the HW-reference manual of the FIMs (page 16)
+ * The macros have as offset the address 0x90001000 (NS92XX_FIM_REG_BASE_PA)
+ */
+#define NS92XX_FIM_EXP0_REG (0x50)
+#define NS92XX_FIM_EXP1_REG (0x54)
+#define NS92XX_FIM_EXP2_REG (0x58)
+#define NS92XX_FIM_EXP3_REG (0x5c)
+#define NS92XX_FIM_EXP4_REG (0x60)
+#define NS92XX_FIM_EXP5_REG (0x64)
+#define NS92XX_FIM_EXP6_REG (0x68)
+#define NS92XX_FIM_EXP7_REG (0x6c)
+#define NS92XX_FIM_EXP8_REG (0x70)
+#define NS92XX_FIM_EXP9_REG (0x74)
+#define NS92XX_FIM_EXP10_REG (0x78)
+#define NS92XX_FIM_EXP11_REG (0x7c)
+#define NS92XX_FIM_EXP12_REG (0x80)
+#define NS92XX_FIM_EXP13_REG (0x84)
+#define NS92XX_FIM_EXP14_REG (0x88)
+#define NS92XX_FIM_EXP15_REG (0x8c)
+#define NS92XX_FIM_EXP_REG(i) (NS92XX_FIM_EXP0_REG + 4*i)
+#define NS92XX_FIM_EXP_REG_CHECK(i) ((i<0 || i>15)?1:0)
+
+
+/* FIM intruction memory offset */
+#define NS92XX_FIM_INSTRUCTION_ADDR (0x00)
+
+/* Hardware assist register offsets */
+#define NS92XX_FIM_HWA_GEN_CONF_REG (0x00)
+
+#define NS92XX_FIM_HWA_SIGNAL0 (0x04)
+#define NS92XX_FIM_HWA_SIGNAL1 (0x08)
+#define NS92XX_FIM_HWA_SIGNAL2 (0x0c)
+#define NS92XX_FIM_HWA_SIGNAL3 (0x10)
+#define NS92XX_FIM_HWA_SIGNAL4 (0x14)
+#define NS92XX_FIM_HWA_SIGNAL5 (0x18)
+#define NS92XX_FIM_HWA_SIGNAL6 (0x1c)
+#define NS92XX_FIM_HWA_SIGNAL7 (0x20)
+#define NS92XX_FIM_HWA_CONTROL0 (0x24)
+#define NS92XX_FIM_HWA_CONTROL1 (0x28)
+#define NS92XX_FIM_HWA_CONTROL2 (0x2c)
+#define NS92XX_FIM_HWA_CONTROL3 (0x30)
+#define NS92XX_FIM_HWA_16_BIT_BUS (0x34)
+#define NS92XX_FIM_HWA_32_BIT_BUS (0x38)
+#define NS92XX_FIM_HWA_SIGNAL(index) (NS92XX_FIM_HWA_GEN_CONF_REG + NS92XX_FIM_HWA_SIGNAL0 + 4*index)
+
+
+/* Fim register bit fields */
+#define NS92XX_FIM_GEN_CTRL_PROGMEM (0x80000000)
+#define NS92XX_FIM_GEN_CTRL_RESERVED1 (0x7fff0000)
+#define NS92XX_FIM_GEN_CTRL_INTACKWR (0x00008000)
+#define NS92XX_FIM_GEN_CTRL_INTTOPIC (0x00007f00)
+#define NS92XX_FIM_GEN_CTRL_INTACKRD (0x00000080)
+#define NS92XX_FIM_GEN_CTRL_INTFROMPIC (0x0000007f)
+
+/* Mask used for the interrupt codes from the ARM to the PICs */
+#define NS92XX_FIM_INT_MASK(code) (code<<8)
+
+
+/* FIM intructions bit fields*/
+#define NS92XX_FIM_INSTRUCTION_MASK (0x00003fff)
+
+/* Hardware assist register bit fields */
+#define NS92XX_FIM_HWA_GEN_CONF_RESERVED1 (0xffffffe0)
+#define NS92XX_FIM_HWA_GEN_CONF_CLKSEL (0x0000001c)
+#define NS92XX_FIM_HWA_GEN_CONF_ENABLE (0x00000003)
+
+#define NS92XX_FIM_HWA_SIGNAL0_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_SIGNAL0_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_SIGNAL0_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_SIGNAL0_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_SIGNAL0_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_SIGNAL0_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_SIGNAL1_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_SIGNAL1_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_SIGNAL1_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_SIGNAL1_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_SIGNAL1_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_SIGNAL1_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_SIGNAL2_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_SIGNAL2_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_SIGNAL2_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_SIGNAL2_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_SIGNAL2_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_SIGNAL2_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_SIGNAL3_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_SIGNAL3_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_SIGNAL3_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_SIGNAL3_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_SIGNAL3_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_SIGNAL3_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_SIGNAL4_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_SIGNAL4_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_SIGNAL4_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_SIGNAL4_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_SIGNAL4_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_SIGNAL4_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_SIGNAL5_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_SIGNAL5_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_SIGNAL5_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_SIGNAL5_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_SIGNAL5_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_SIGNAL5_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_SIGNAL6_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_SIGNAL6_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_SIGNAL6_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_SIGNAL6_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_SIGNAL6_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_SIGNAL6_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_SIGNAL7_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_SIGNAL7_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_SIGNAL7_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_SIGNAL7_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_SIGNAL7_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_SIGNAL7_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_CONTROL0_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_CONTROL0_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_CONTROL0_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_CONTROL0_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_CONTROL0_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_CONTROL0_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_CONTROL1_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_CONTROL1_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_CONTROL1_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_CONTROL1_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_CONTROL1_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_CONTROL1_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_CONTROL2_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_CONTROL2_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_CONTROL2_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_CONTROL2_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_CONTROL2_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_CONTROL2_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_CONTROL3_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_CONTROL3_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_CONTROL3_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_CONTROL3_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_CONTROL3_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_CONTROL3_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_16_BIT_BUS_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_16_BIT_BUS_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_16_BIT_BUS_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_16_BIT_BUS_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_16_BIT_BUS_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_16_BIT_BUS_CLKS (0x00000003)
+
+#define NS92XX_FIM_HWA_32_BIT_BUS_RESERVED (0xfffffc00)
+#define NS92XX_FIM_HWA_32_BIT_BUS_OMODE (0x00000300)
+#define NS92XX_FIM_HWA_32_BIT_BUS_IMODE (0x000000e0)
+#define NS92XX_FIM_HWA_32_BIT_BUS_CLKE (0x00000010)
+#define NS92XX_FIM_HWA_32_BIT_BUS_CNTRL (0x0000000c)
+#define NS92XX_FIM_HWA_32_BIT_BUS_CLKS (0x00000003)
+
+
+
+/* H/W Assist General Configuration Register Codes */
+#define FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_2 (0)
+#define FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_4 (1<<2)
+#define FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_8 (2<<2)
+#define FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_16 (3<<2)
+#define FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_32 (4<<2)
+#define FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_64 (5<<2)
+#define FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_128 (6<<2)
+#define FIM_HWA_GEN_CFG_CLKSEL_DIVIDE_BY_256 (7<<2)
+
+
+
+#endif /* __FIM_REG_H */
+
+
+
+
+
+
+
+
diff --git a/drivers/fims/sdio/Makefile b/drivers/fims/sdio/Makefile
new file mode 100644
index 000000000000..cd2f1149fde2
--- /dev/null
+++ b/drivers/fims/sdio/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the kernel FIM device drivers
+#
+
+ifneq ($(CONFIG_FIM_SDIO_MODULE),)
+ obj-m += fim_sdio.o
+else
+ obj-$(CONFIG_FIM_ZERO_SDIO) += fim_sdio.o
+ obj-$(CONFIG_FIM_ONE_SDIO) += fim_sdio.o
+endif
diff --git a/drivers/fims/sdio/fim_sdio.c b/drivers/fims/sdio/fim_sdio.c
new file mode 100644
index 000000000000..3b618dbeea98
--- /dev/null
+++ b/drivers/fims/sdio/fim_sdio.c
@@ -0,0 +1,1616 @@
+/* -*- linux-c -*-
+ *
+ * drivers/fims/fim_sdio.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * !Revision: $Revision: 1.20 $
+ * !Author: Luis Galdos
+ * !Descr:
+ * !References:
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/card.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+#include <linux/scatterlist.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+
+#include <asm/scatterlist.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <mach/gpio.h>
+#include <mach/regs-sys-ns921x.h>
+#include <mach/regs-iohub-ns921x.h>
+#include <mach/regs-io-ns921x.h>
+#include <asm/delay.h>
+
+
+/* For registering the FIM-driver */
+#include <mach/fim-ns921x.h>
+
+
+/*
+ * If the driver is going to be loaded as a built-in driver, then include the header
+ * file with the firmware, otherwise set the name of the binary file that should
+ * be read with the help of the firmware-subsystem
+ */
+#if !defined(MODULE)
+# if defined(CONFIG_PROCESSOR_NS9215)
+# include "fim_sdio0.h"
+# include "fim_sdio1.h"
+# elif defined(CONFIG_PROCESSOR_NS9210)
+# include "fim_sdio0_9210.h"
+# include "fim_sdio1_9210.h"
+# else
+# error "FIM-SDIO: Unsupported processor type."
+# endif /* CONFIG_PROCESSOR_NS9215 */
+extern const unsigned char fim_sdio_firmware[];
+#define FIM_SDIO_FW_FILE (NULL)
+#define FIM_SDIO_FW_CODE0 fim_sdio_firmware0
+#define FIM_SDIO_FW_CODE1 fim_sdio_firmware1
+#else
+const unsigned char *fim_sdio_firmware = NULL;
+# if defined(CONFIG_PROCESSOR_NS9215)
+# define FIM_SDIO_FW_FILE_PAT "fim_sdio%i.bin"
+# elif defined(CONFIG_PROCESSOR_NS9210)
+# define FIM_SDIO_FW_FILE_PAT "fim_sdio%i_9210.bin"
+# else
+# error "FIM-SDIO: Unsupported processor type."
+# endif /* CONFIG_PROCESSOR_NS9215 */
+#define FIM_SDIO_FW_LEN sizeof(FIM_SDIO_FW_FILE_PAT) + 10
+#define FIM_SDIO_FW_CODE (NULL)
+#endif
+
+/* Driver informations */
+#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Luis Galdos"
+#define DRIVER_DESC "FIM SDIO driver"
+#define FIM_SDIO_DRIVER_NAME "fim-sdio"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+/* Module parameter for selection the FIMs */
+NS921X_FIM_NUMBERS_PARAM(fims_number);
+
+/* Registers with status information */
+#define FIM_SDIO_GPIOS_REG 0x02
+#define FIM_SDIO_GPIOS_REG_CD 0x01
+#define FIM_SDIO_GPIOS_REG_WP 0x02
+#define FIM_SDIO_CARD_STATREG 0x00
+
+/* Interrupts from the FIM to the driver */
+#define FIM_SDIO_INTARM_CARD_DAT1 0x01
+#define FIM_SDIO_INTARM_CARD_DETECTED 0x02
+
+
+/* Macros for the SDIO-interface to the FIM-firmware */
+#define SDIO_HOST_TX_HDR 0x40
+#define SDIO_HOST_CMD_MASK 0x3f
+#define SDIO_FIFO_TX_48RSP 0x01
+#define SDIO_FIFO_TX_136RSP 0x02
+#define SDIO_FIFO_TX_BW4 0x04
+#define SDIO_FIFO_TX_BLKWR 0x08
+#define SDIO_FIFO_TX_BLKRD 0x10
+#define SDIO_FIFO_TX_DISCRC 0x20
+
+/* User specified macros */
+#define FIM_SDIO_TIMEOUT_MS 500
+#define FIM_SDIO_TX_CMD_LEN 5
+#define FIM_SDIO_MAX_RESP_LENGTH 17
+
+/* Status bits from the PIC-firmware */
+#define FIM_SDIO_RX_RSP 0x01
+#define FIM_SDIO_RX_BLKRD 0x02
+#define FIM_SDIO_RX_TIMEOUT 0x04
+
+/*
+ * Firmware specific control registers
+ * IMPORTANT: The control bit for the card detect is enabled wih the main control!
+ */
+#define FIM_SDIO_CLKDIV_REG 0
+#define FIM_SDIO_INTCFG_REG 1
+#define FIM_SDIO_INTCFG_MAIN (1 << 0)
+#define FIM_SDIO_INTCFG_CARD (1 << 1)
+#define FIM_SDIO_INTCFG_SDIO (1 << 2)
+#define FIM_SDIO_BLOCKS_REG 2
+#define FIM_SDIO_BLKSZ_LSB_REG 3
+#define FIM_SDIO_BLKSZ_MSB_REG 4
+#define FIM_SDIO_MAIN_REG 5
+#define FIM_SDIO_MAIN_START (1 << 0)
+
+/* Firmware specific status registers */
+#define FIM_SDIO_VERSION_SREG 0
+
+/* Internal flags for the request function */
+#define FIM_SDIO_REQUEST_NEW 0x00
+#define FIM_SDIO_REQUEST_CMD 0x01
+#define FIM_SDIO_REQUEST_STOP 0x02
+#define FIM_SDIO_SET_BUS_WIDTH 0x04
+
+/* Macros for the DMA-configuraton */
+#define FIM_SDIO_DMA_BUFFER_SIZE PAGE_SIZE
+#define FIM_SDIO_DMA_RX_BUFFERS 21
+#define FIM_SDIO_DMA_TX_BUFFERS 10
+
+/* Used for the Card Detect timer */
+#define FIM_SDIO_CD_POLLING_TIMER (HZ / 2)
+
+/*
+ * The below macro force the use of the timer for polling the state of the card
+ * detection line
+ */
+#if 0
+#define FIM_SDIO_FORCE_CD_POLLING
+#endif
+
+/* Enable the multipple block transfer (in development) */
+#if 0
+# define FIM_SDIO_MULTI_BLOCK
+# define FIM_SDIO_MAX_BLOCKS (FIM_SDIO_DMA_RX_BUFFERS - 10)
+# define FIM_SDIO_SKIP_CRC_CHECK
+#else
+# define FIM_SDIO_MAX_BLOCKS 1
+#endif
+
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] fim-sdio: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "fim-sdio: " fmt, ## args)
+#define printk_dbg(fmt, args...) printk(KERN_DEBUG "fim-sdio: " fmt, ## args)
+
+#if 0
+#define FIM_SDIO_DEBUG
+#define FIM_SDIO_DEBUG_CRC
+#endif
+
+#ifdef FIM_SDIO_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "fim-sdio: %s() " fmt, __FUNCTION__ , ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+/* If enabled will generate an CRC error in the function that checks it */
+#if 0
+#define FIM_SDIO_FORCE_CRC
+#endif
+
+/*
+ * GPIO configuration
+ * Please note that the write protect GPIO must be under the index [4], then
+ * the driver will read its status (the firmware doesn't support it)
+ */
+#define FIM_SDIO_D0_GPIO 0
+#define FIM_SDIO_D1_GPIO 1
+#define FIM_SDIO_D2_GPIO 2
+#define FIM_SDIO_D3_GPIO 3
+#define FIM_SDIO_WP_GPIO 4
+#define FIM_SDIO_CD_GPIO 5
+#define FIM_SDIO_CLK_GPIO 6
+#define FIM_SDIO_CMD_GPIO 7
+#define FIM_SDIO_MAX_GPIOS 8
+
+/* Values for the block read state machine */
+enum fim_blkrd_state {
+ BLKRD_STATE_IDLE = 0,
+ BLKRD_STATE_WAIT_ACK = 1, /* Waiting for the block read ACK */
+ BLKRD_STATE_WAIT_DATA = 2, /* Waiting for the block read data */
+ BLKRD_STATE_WAIT_CRC = 3, /* Waiting for the CRC */
+ BLKRD_STATE_HAVE_DATA = 4, /* Have block read data with the CRC */
+ BLKRD_STATE_TIMEOUTED = 5, /* Timeout response from the PIC */
+ BLKRD_STATE_CRC_ERR = 6, /* Compared CRC (PIC and card) differs */
+};
+
+
+/* Values for the command state machine */
+enum fim_cmd_state {
+ CMD_STATE_IDLE = 0,
+ CMD_STATE_WAIT_ACK = 1, /* Waiting for the response ACK */
+ CMD_STATE_WAIT_DATA = 2, /* Waiting for the response data */
+ CMD_STATE_HAVE_RSP = 3, /* Have response data */
+ CMD_STATE_TIMEOUTED = 4, /* Timeout response from the PIC */
+ CMD_STATE_CRC_ERR = 5, /* Compared CRC (PIC and card) differs */
+};
+
+/*
+ * Internal port structure for the FIM-SDIO host controller
+ * wp_gpio : GPIO to use for reading the write protect line
+ */
+struct fim_sdio_t {
+ struct fim_driver fim;
+ unsigned int flags;
+ struct device *device;
+ int index;
+
+ struct fim_gpio_t gpios[FIM_SDIO_MAX_GPIOS];
+ struct fim_buffer_t *buf;
+ struct mmc_command *mmc_cmd;
+ struct timer_list mmc_timer;
+ struct mmc_host *mmc;
+ struct mmc_request *mmc_req;
+
+ enum fim_cmd_state cmd_state;
+ enum fim_blkrd_state blkrd_state;
+
+ int trans_blocks;
+ int trans_sg;
+ int reg;
+
+ int wp_gpio;
+ struct clk *sys_clk;
+ int cd_gpio;
+ int cd_irq;
+ int cd_value;
+ struct timer_list cd_timer;
+
+ /* struct scatterlist *sg; */
+ u8 *sg_virt;
+
+ /* Members used for restarting the FIM */
+ atomic_t fim_is_down;
+ struct work_struct restart_work;
+};
+
+/*
+ * Transfer command structure for the card
+ * opctl : Control byte for the PIC
+ * blksz : Block size
+ * cmd : Command to send to the card
+ */
+struct fim_sd_tx_cmd_t {
+ unsigned char opctl;
+ unsigned char blksz_msb;
+ unsigned char blksz_lsb;
+ unsigned char cmd[FIM_SDIO_TX_CMD_LEN];
+}__attribute__((__packed__));
+
+
+/*
+ * Response receive structure from the Card
+ * resp : Card response, with a length of 5 or 17 as appropriate
+ * stat : Opcode of the executed command
+ * crc : CRC
+ */
+struct fim_sd_rx_resp_t {
+ unsigned char stat;
+ unsigned char resp[FIM_SDIO_MAX_RESP_LENGTH];
+ unsigned char crc;
+}__attribute__((__packed__));
+
+
+/* Main structure for the available ports */
+struct fim_sdios_t {
+ int fims;
+ struct fim_sdio_t *ports;
+};
+
+
+static struct fim_sdios_t *fim_sdios;
+
+/* Internal functions */
+static void fim_sd_process_next(struct fim_sdio_t *port);
+inline static struct fim_sdio_t *get_port_from_mmc(struct mmc_host *mmc);
+inline static void *fim_sd_dma_to_sg(struct fim_sdio_t *port, struct mmc_data *data,
+ unsigned char *dma_buf, int dma_len);
+static void fim_sd_set_clock(struct fim_sdio_t *port, long int clockrate);
+static struct fim_buffer_t *fim_sd_alloc_cmd(struct fim_sdio_t *port);
+static int fim_sd_send_command(struct fim_sdio_t *port, struct mmc_command *cmd);
+
+
+/* Return the corresponding port structure */
+inline static struct fim_sdio_t *get_port_from_mmc(struct mmc_host *mmc)
+{
+ if (!mmc)
+ return NULL;
+
+ return (struct fim_sdio_t *)mmc->private[0];
+}
+
+/*
+ * This function is called when the FIM didn't respond to our requests. This normally
+ * happens when the card was unplugged during a data transfer or by another similar
+ * errors. So, for avoiding some additional failure we stop any further transfers to
+ * the FIM.
+ */
+static void fim_sd_cmd_timeout(unsigned long data)
+{
+ struct fim_sdio_t *port;
+
+ port = (struct fim_sdio_t *)data;
+
+ /* If the command pointer isn't NULL then a timeout has ocurred */
+ if (port->mmc_cmd) {
+ atomic_set(&port->fim_is_down, 1);
+ port->mmc_cmd->error = -ENOMEDIUM;
+ fim_sd_process_next(port);
+ }
+}
+
+/* Workqueue for restarting a FIM */
+static void fim_sd_restart_work_func(struct work_struct *work)
+{
+ struct fim_sdio_t *port;
+ struct fim_driver *fim;
+ int ret;
+
+ port = container_of(work, struct fim_sdio_t, restart_work);
+ fim = &port->fim;
+
+ printk_dbg("Going to restart the FIM%i\n", fim->picnr);
+ fim_disable_irq(&port->fim);
+
+ ret = fim_send_stop(&port->fim);
+ if (ret) {
+ printk_err("Couldn't stop the FIM%i\n", fim->picnr);
+ return;
+ }
+
+ ret = fim_download_firmware(&port->fim);
+ if (ret) {
+ printk_err("FIM%i download failed\n", fim->picnr);
+ return;
+ }
+
+ ret = fim_send_start(&port->fim);
+ if (ret) {
+ printk_err("FIM%i start failed\n", fim->picnr);
+ return;
+ }
+
+ /* Reset the internal values */
+ printk_dbg("Re-enabling the IRQ of FIM%u\n", fim->picnr);
+ atomic_set(&port->fim_is_down, 0);
+ fim_enable_irq(&port->fim);
+ port->mmc_cmd = NULL;
+ mmc_detect_change(port->mmc, msecs_to_jiffies(100));
+}
+
+/* Check the status of the card detect line if no external IRQ supported */
+static void fim_sd_cd_timer_func(unsigned long _port)
+{
+ struct fim_sdio_t *port;
+ int val;
+
+ port = (struct fim_sdio_t *)_port;
+ val = gpio_get_value_ns921x(port->cd_gpio);
+
+ if (val != port->cd_value) {
+ port->cd_value = val;
+
+ if (atomic_read(&port->fim_is_down))
+ schedule_work(&port->restart_work);
+ else
+ mmc_detect_change(port->mmc, msecs_to_jiffies(100));
+ }
+
+ mod_timer(&port->cd_timer, jiffies + FIM_SDIO_CD_POLLING_TIMER);
+}
+
+/*
+ * Configure the trigger edge of the CD interrupt depending on the current
+ * state of the GPIO
+ */
+static inline int fim_sd_prepare_cd_irq(struct fim_sdio_t *port)
+{
+ int val;
+ unsigned int type;
+
+ val = gpio_get_value_ns921x(port->cd_gpio);
+ printk_debug("CD interrupt received (val %i)\n", val);
+
+ if (!val)
+ type = IRQF_TRIGGER_RISING;
+ else
+ type = IRQF_TRIGGER_FALLING;
+
+ return set_irq_type(port->cd_irq, type);
+}
+
+/*
+ * The external interrupt line only supports one edge detection! That means, we must
+ * check the status of the line and reconfigure the interrupt trigger type.
+ */
+static irqreturn_t fim_sd_cd_irq(int irq, void *_port)
+{
+ struct fim_sdio_t *port;
+ int ret;
+
+ port = (struct fim_sdio_t *)_port;
+ if ((ret = fim_sd_prepare_cd_irq(port)))
+ printk_err("Failed CD IRQ reconfiguration (%i)\n", ret);
+
+ /*
+ * If the FIM was stopped, then only schedule the workqueue for restarting it
+ * again. This worker will call the detect change function.
+ */
+ if (atomic_read(&port->fim_is_down))
+ schedule_work(&port->restart_work);
+ else
+ mmc_detect_change(port->mmc, msecs_to_jiffies(100));
+
+ return IRQ_HANDLED;
+}
+
+inline static int fim_sd_card_plugged(struct fim_sdio_t *port)
+{
+ struct fim_driver *fim;
+ unsigned int val;
+
+ fim = &port->fim;
+ fim_get_stat_reg(fim, FIM_SDIO_CARD_STATREG, &val);
+ return !val;
+}
+
+/*
+ * Handler for the incoming FIM-interrupts. Available interrupts:
+ * - Card detection
+ * - SDIO interrupts from the cards
+ */
+static void fim_sd_isr(struct fim_driver *driver, int irq, unsigned char code,
+ unsigned int rx_fifo)
+{
+ struct fim_sdio_t *port;
+
+ port = driver->driver_data;
+ if (!port || !port->mmc) {
+ printk_dbg("Null pointer by unexpected IRQ 0x%02x\n", code);
+ return;
+ }
+
+ switch (code) {
+
+ case FIM_SDIO_INTARM_CARD_DETECTED:
+ if (fim_sd_card_plugged(port)) {
+ fim_set_ctrl_reg(&port->fim, FIM_SDIO_INTCFG_REG, 0);
+ printk_debug("SD card detected\n");
+ } else {
+ printk_debug("SD card removed\n");
+ }
+ mmc_detect_change(port->mmc, msecs_to_jiffies(100));
+ break;
+
+ case FIM_SDIO_INTARM_CARD_DAT1:
+ printk_debug("SDIO IRQ\n");
+
+ /*
+ * Normally we don't need this sanity check but the FIM is reporting
+ * some SDIO-interrupts where no SDIO-card was initialized.
+ */
+ if (port->mmc->sdio_irq_thread)
+ mmc_signal_sdio_irq(port->mmc);
+ else
+ printk_dbg("Premature SDIO IRQ received!\n");
+ break;
+ default:
+ printk_err("Unknown IRQ %i | FIM %i | %x\n",
+ code, port->fim.picnr, rx_fifo);
+ break;
+ }
+}
+
+/*
+ * This is the TX-callback that the API call after a DMA-package was closed
+ * The fim buffer structure contains our internal private data
+ * Free the allocated FIM-buffer that was used for sending the DMA-data
+ */
+static void fim_sd_tx_isr(struct fim_driver *driver, int irq,
+ struct fim_buffer_t *pdata)
+{
+ struct fim_buffer_t *buf;
+ struct fim_sdio_t *port;
+
+ port = (struct fim_sdio_t *)driver->driver_data;
+ if (pdata->private) {
+ buf = pdata->private;
+ fim_free_buffer(&port->fim, buf);
+ }
+}
+
+/* @XXX: Remove this ugly code! */
+inline static void fim_sd_parse_resp(struct mmc_command *cmd,
+ struct fim_sd_rx_resp_t *resp)
+{
+ unsigned char *ptr;
+ ptr = (unsigned char *)cmd->resp;
+ if (cmd->flags & MMC_RSP_136) {
+ *ptr++ = resp->resp[3];
+ *ptr++ = resp->resp[2];
+ *ptr++ = resp->resp[1];
+ *ptr++ = resp->resp[0];
+ *ptr++ = resp->resp[7];
+ *ptr++ = resp->resp[6];
+ *ptr++ = resp->resp[5];
+ *ptr++ = resp->resp[4];
+ *ptr++ = resp->resp[11];
+ *ptr++ = resp->resp[10];
+ *ptr++ = resp->resp[9];
+ *ptr++ = resp->resp[8];
+ *ptr++ = resp->resp[15];
+ *ptr++ = resp->resp[14];
+ *ptr++ = resp->resp[13];
+ *ptr++ = resp->resp[12];
+ } else {
+ *ptr++ = resp->resp[3];
+ *ptr++ = resp->resp[2];
+ *ptr++ = resp->resp[1];
+ *ptr++ = resp->resp[0];
+ *ptr++ = resp->resp[4];
+ *ptr++ = resp->stat;
+ }
+}
+
+/*
+ * This function checks the CRC by block read transfer
+ * The information about the length and content of the CRC was obtained
+ * from the firmware-source code (sd.asm)
+ */
+inline static int fim_sd_check_blkrd_crc(struct fim_sdio_t *port, unsigned char *data,
+ int length)
+{
+ int crc_len;
+ unsigned char *pic_crc;
+
+ /*
+ * The CRC length depends on the bus width (see sd.asm)
+ * No CRC enabled : One byte (0x00)
+ * One bit bus : Four bytes
+ * Four bit bus : Eight bytes
+ */
+ if (!(port->mmc_cmd->flags & MMC_RSP_CRC)) {
+ crc_len = 1;
+ pic_crc = data;
+ } else if (port->mmc->ios.bus_width == MMC_BUS_WIDTH_1) {
+ crc_len = 4;
+ pic_crc = data + 2;
+ } else {
+ crc_len = 16;
+ pic_crc = data + 8;
+ }
+
+ if (crc_len != length) {
+ printk_err("Unexpected CRC length %i (expected %i)\n",
+ length, crc_len);
+ return -EINVAL;
+ }
+
+ /*
+ * Code for forcing a CRC-error and the behavior of the MMC-layer
+ * crc_error = 10 : Error reading the partition table
+ * crc_error = 40 : Error by a block read transfer
+ */
+#ifdef FIM_SDIO_FORCE_CRC
+ static int crc_error = 0;
+ if (crc_error == 40) {
+ crc_error++;
+ return 1;
+ } else
+ crc_error++;
+#endif
+
+ /* If the CRC is disabled, the PIC only appended a dummy Byte */
+ if (crc_len == 1)
+ return 0;
+
+#if defined(FIM_SDIO_DEBUG_CRC)
+#define FIM_SDIO_CRC_PATTERN "%02x %02x %02x %02x %02x %02x %02x %02x"
+ {
+ int retval, len;
+
+ len = crc_len >> 1;
+ retval = memcmp(data, pic_crc, len);
+ if (retval) {
+
+ printk_dbg("Data len %i | CRC len %i\n", length, len);
+
+ printk_dbg("CRC FIM : " FIM_SDIO_CRC_PATTERN "\n",
+ *pic_crc, *(pic_crc + 1),
+ *(pic_crc + 2), *(pic_crc + 3),
+ *(pic_crc + 4), *(pic_crc + 5),
+ *(pic_crc + 6), *(pic_crc + 7));
+ printk_dbg("CRC MMC : " FIM_SDIO_CRC_PATTERN "\n",
+ *data, *(data + 1),
+ *(data + 2), *(data + 3),
+ *(data + 4), *(data + 5),
+ *(data + 6), *(data + 7));
+ }
+
+ return retval;
+ }
+#else
+ return memcmp(data, pic_crc, crc_len >> 1);
+#endif
+}
+
+inline static void fim_sd_print_crc(int length, unsigned char *crc)
+{
+ /* Only four and six as length supported */
+ if (length == 4)
+ printk_info("CRC: %u\n", *((unsigned int *)crc));
+ else
+ printk_info("CRC: %lu\n", *((unsigned long *)crc));
+}
+
+/*
+ * Called when a receive DMA-buffer was closed.
+ * Unfortunately the data received from the PIC has different formats. Sometimes it
+ * contains a response, sometimes data of a block read request and sometimes the CRC
+ * of the read data. In the case of a read transfer it is really amazing, then
+ * the transfer consists in four DMA-buffers.
+ */
+static void fim_sd_rx_isr(struct fim_driver *driver, int irq,
+ struct fim_buffer_t *pdata)
+{
+ struct fim_sdio_t *port;
+ struct mmc_command *mmc_cmd;
+ struct fim_sd_rx_resp_t *resp;
+ int len, crc_len;
+ unsigned char *crc_ptr;
+ int is_ack;
+
+ /* Get the correct port from the FIM-driver structure */
+ len = pdata->length;
+ port = (struct fim_sdio_t *)driver->driver_data;
+
+ /*
+ * The timeout function can set the command structure to NULL, for this reason
+ * check here is we can handle the response correctly
+ */
+ if ((mmc_cmd = port->mmc_cmd) == NULL) {
+ printk_err("Timeouted command response?\n");
+ goto exit_unlock;
+ }
+
+ /*
+ * Check the current state of the command and update it if required
+ * IMPORTANT: The buffer can contain response data or the data from a block
+ * read too, for this reason was implemented the state machine
+ */
+ resp = (struct fim_sd_rx_resp_t *)pdata->data;
+ is_ack = (pdata->length == 1) ? 1 : 0;
+
+ printk_debug("CMD%i | RESP stat %x | CMD stat %i | BLKRD stat %i | Len %i\n",
+ mmc_cmd->opcode, resp->stat, port->cmd_state,
+ port->blkrd_state, pdata->length);
+
+ /*
+ * By the ACKs the PIC will NOT send a timeout. Timeouts are only
+ * set by the response and and block read data
+ */
+ if (is_ack && resp->stat & FIM_SDIO_RX_TIMEOUT) {
+ mmc_cmd->error = -ETIMEDOUT;
+ port->blkrd_state = BLKRD_STATE_HAVE_DATA;
+ port->cmd_state = CMD_STATE_HAVE_RSP;
+
+ /* Check the conditions for the BLOCK READ state machine */
+ } else if (port->blkrd_state == BLKRD_STATE_WAIT_ACK && is_ack &&
+ resp->stat & FIM_SDIO_RX_BLKRD) {
+ port->blkrd_state = BLKRD_STATE_WAIT_DATA;
+
+ /* Check if the block read data has arrived */
+ } else if (port->blkrd_state == BLKRD_STATE_WAIT_DATA && !is_ack) {
+ crc_len = len - mmc_cmd->data->blksz;
+ crc_ptr = pdata->data + mmc_cmd->data->blksz;
+ port->blkrd_state = BLKRD_STATE_HAVE_DATA;
+
+#if !defined(FIM_SDIO_SKIP_CRC_CHECK)
+#define FIM_SDIO_RDBLK_PATTERN "%02x %02x %02x %02x %02x %02x " \
+ "%02x %02x %02x %02x %02x %02x"
+ if (fim_sd_check_blkrd_crc(port, crc_ptr, crc_len)) {
+ printk_err("CRC failure | Data %i | CRC %i | Retries %i\n",
+ len, crc_len, mmc_cmd->retries);
+
+ printk_dbg(FIM_SDIO_RDBLK_PATTERN "\n",
+ *pdata->data , *(pdata->data + 1),
+ *(pdata->data + 2), *(pdata->data + 3),
+ *(pdata->data + 4), *(pdata->data + 5),
+ *(pdata->data + 6), *(pdata->data + 7),
+ *(pdata->data + 8), *(pdata->data + 9),
+ *(pdata->data + 10), *(pdata->data + 11));
+
+ mmc_cmd->error = -EILSEQ;
+ } else {
+ fim_sd_dma_to_sg(port, mmc_cmd->data,
+ pdata->data, pdata->length - crc_len);
+ }
+#else
+ fim_sd_dma_to_sg(port, mmc_cmd->data,
+ pdata->data, pdata->length - crc_len);
+#endif
+
+ /* Check if we have a multiple transfer read */
+ port->trans_blocks -= 1;
+ if (port->trans_blocks > 0) {
+ printk_debug("Wait for next block %i\n", port->trans_blocks);
+ port->blkrd_state = BLKRD_STATE_WAIT_ACK;
+ }
+
+ /* Check the conditions for the COMMAND state machine */
+ } else if (is_ack && port->cmd_state == CMD_STATE_WAIT_ACK &&
+ resp->stat & FIM_SDIO_RX_RSP) {
+ port->cmd_state = CMD_STATE_WAIT_DATA;
+ } else if (!is_ack && port->cmd_state == CMD_STATE_WAIT_DATA) {
+ fim_sd_parse_resp(mmc_cmd, resp);
+ port->cmd_state = CMD_STATE_HAVE_RSP;
+
+ /* Check for unexpected acks or opcodes */
+ } else {
+
+ /* @FIXME: Need a correct errror handling for this condition */
+ if (mmc_cmd->data && mmc_cmd->data->flags & MMC_DATA_READ)
+ printk_err("Failed multi RX (CMD%u | PIC stat %x | State %x)\n",
+ mmc_cmd->opcode, resp->stat, port->blkrd_state);
+ else
+ printk_err("Unexpected RX stat (CMD%i | PIC stat %x | Leng %i)\n",
+ mmc_cmd->opcode, resp->stat, pdata->length);
+ }
+
+ /*
+ * By errors set the two states machines to the end position for sending
+ * the error to the MMC-layer
+ */
+ if (mmc_cmd->error) {
+ port->cmd_state = CMD_STATE_HAVE_RSP;
+ port->blkrd_state = BLKRD_STATE_HAVE_DATA;
+ }
+
+ /*
+ * Now evaluate if need to wait for another RX-interrupt or
+ * can send the request done to the MMC-layer
+ */
+ if (port->cmd_state == CMD_STATE_HAVE_RSP &&
+ port->blkrd_state == BLKRD_STATE_HAVE_DATA) {
+
+#if defined(FIM_SDIO_MULTI_BLOCK)
+ if (mmc_cmd->data) {
+ uint blocks;
+
+ fim_get_ctrl_reg(&port->fim, FIM_SDIO_BLOCKS_REG, &blocks);
+ printk_debug("CMD%u: End code %i | Blocks %u\n",
+ mmc_cmd->opcode, mmc_cmd->error, blocks);
+ }
+#endif /* FIM_SDIO_MULTI_BLOCK */
+
+ fim_sd_process_next(port);
+ }
+
+ exit_unlock:
+ return;
+}
+
+/* Send a buffer over the FIM-API */
+static int fim_sd_send_buffer(struct fim_sdio_t *port, struct fim_buffer_t *buf)
+{
+ struct fim_driver *fim;
+
+ if (!buf || !port)
+ return -EINVAL;
+
+ fim = &port->fim;
+ buf->private = buf;
+ return fim_send_buffer(fim, buf);
+}
+
+/* Returns a command buffer allocated from the FIM-API */
+static struct fim_buffer_t *fim_sd_alloc_cmd(struct fim_sdio_t *port)
+{
+ struct fim_driver *fim;
+ int length;
+
+ if(!port)
+ return NULL;
+
+ fim = &port->fim;
+ length = sizeof(struct fim_sd_tx_cmd_t);
+
+ return fim_alloc_buffer(fim, length, GFP_KERNEL);
+}
+
+/* Returns a buffer allocated from the FIM-API */
+static struct fim_buffer_t *fim_sd_alloc_buffer(struct fim_sdio_t *port, int length)
+{
+ struct fim_driver *fim;
+
+ if (!port || length <= 0)
+ return NULL;
+
+ fim = &port->fim;
+ return fim_alloc_buffer(fim, length, GFP_KERNEL);
+}
+
+static void fim_sd_free_buffer(struct fim_sdio_t *port, struct fim_buffer_t *buf)
+{
+ struct fim_driver *fim;
+
+ if (!port || !buf)
+ return;
+
+ fim = &port->fim;
+ fim_free_buffer(fim, buf);
+}
+
+/*
+ * Copy the data from the MMC-layer (scatter list) to the DMA-buffer for the FIM-API
+ * Since we have only support for single block reads, some sanity checks
+ * are not implemented
+ */
+inline static void fim_sd_sg_to_dma(struct fim_sdio_t *port, struct mmc_data *data,
+ struct fim_buffer_t *buf)
+{
+ unsigned int len, cnt, dma_len;
+ struct scatterlist *sg;
+ unsigned char *sg_buf;
+ unsigned char *dma_buf;
+ int process;
+
+ sg = data->sg;
+ len = data->sg_len;
+ dma_buf = buf->data;
+ dma_len = 0;
+
+ /* Need a correct error handling */
+ if (len > 1) {
+ printk_err("The FIM-SD host only supports single block\n");
+ len = 1;
+ }
+
+ /* @XXX: Check the correctness of the memcpy operation */
+ for (cnt = 0; cnt < len && dma_len <= buf->length; cnt++) {
+ sg_buf = sg_virt(&sg[cnt]);
+ process = (buf->length >= sg[cnt].length) ? sg[cnt].length : buf->length;
+ memcpy(dma_buf, sg_buf, process);
+ dma_buf += process;
+ dma_len += process;
+ data->bytes_xfered += process;
+ }
+}
+
+/*
+ * Function called when RD data has arrived
+ * Return value is the pointer of the last byte copied to the scatterlist, it can
+ * be used for appending more data (e.g. in multiple block read transfers)
+ */
+inline static void *fim_sd_dma_to_sg(struct fim_sdio_t *port, struct mmc_data *data,
+ unsigned char *dma_buf, int dma_len)
+{
+ char *sg_buf;
+
+ /* This loop was tested only with single block transfers */
+ sg_buf = port->sg_virt;
+ printk_debug("RX: %i bytes from %p to %p\n", dma_len, dma_buf, sg_buf);
+ memcpy(sg_buf, dma_buf, dma_len);
+ data->bytes_xfered += dma_len;
+
+ /* Update the pointer inside the SG buffer for the next transfer */
+ port->sg_virt += dma_len;
+
+#if 0
+ for (cnt = port->trans_sg; cnt < len && dma_len > 0; cnt++) {
+
+ if (sg[cnt].length != 512)
+ printk_dbg("Unexpected block length %u\n", sg[cnt].length);
+
+ process = dma_len > sg[cnt].length ? sg[cnt].length : dma_len;
+ sg_buf = sg_virt(&sg[cnt]);
+ memcpy(sg_buf, dma_buf, process);
+ dma_buf += process;
+ dma_len -= process;
+ data->bytes_xfered += process;
+ sg_buf += process;
+ port->trans_sg += 1;
+ }
+
+ port->sg = &sg[cnt];
+#endif
+
+ return sg_buf;
+}
+
+/* This function will send the command to the PIC using the TX-DMA buffers */
+static int fim_sd_send_command(struct fim_sdio_t *port, struct mmc_command *cmd)
+{
+ struct mmc_data *data;
+ struct fim_buffer_t *buf;
+ struct fim_sd_tx_cmd_t *txcmd;
+ unsigned int block_length, blocks;
+ int retval, length;
+
+ /* @TODO: Send an error response to the MMC-core */
+ if (!(buf = fim_sd_alloc_cmd(port))) {
+ printk_err("No memory available for a new CMD?\n");
+ return -ENOMEM;
+ }
+
+ /* Use the buffer data for the TX-command */
+ txcmd = (struct fim_sd_tx_cmd_t *)buf->data;
+ txcmd->opctl = 0;
+
+ /*
+ * Set the internal flags for the next response sequences
+ * Assume that we will wait for a command response (not block read).
+ * By block reads the flag will be modified inside the if-condition
+ */
+ port->cmd_state = CMD_STATE_WAIT_ACK;
+ port->blkrd_state = BLKRD_STATE_HAVE_DATA;
+ if ((data = cmd->data) != NULL) {
+ block_length = data->blksz;
+ blocks = data->blocks;
+
+#if !defined(FIM_SDIO_MULTI_BLOCK)
+ if (blocks != 1) {
+ printk_err("Only supports single block transfer (%i)\n", blocks);
+ cmd->error = -EILSEQ;
+ fim_sd_process_next(port);
+ return -EILSEQ;
+ }
+#endif
+
+ printk_debug("CMD%u: %s %i blks | %i blksz | SG len %u\n",
+ cmd->opcode,
+ (data->flags & MMC_DATA_READ) ? "RX" : "TX",
+ data->blocks, data->blksz,
+ data->sg_len);
+
+ /* Reset the scatter list position */
+ port->trans_sg = 0;
+ port->sg_virt = sg_virt(&data->sg[0]);
+ port->trans_blocks = blocks;
+
+ /* Setup the FIM registers (number of blocks and block size) */
+#if defined(FIM_SDIO_MULTI_BLOCK)
+ fim_set_ctrl_reg(&port->fim, FIM_SDIO_BLOCKS_REG, blocks);
+ fim_set_ctrl_reg(&port->fim, FIM_SDIO_BLKSZ_LSB_REG, block_length);
+ fim_set_ctrl_reg(&port->fim, FIM_SDIO_BLKSZ_MSB_REG,
+ (block_length >> 8));
+#endif
+
+ /* Check if the transfer request is for reading or writing */
+ if (cmd->data->flags & MMC_DATA_READ) {
+ txcmd->opctl |= SDIO_FIFO_TX_BLKRD;
+ port->blkrd_state = BLKRD_STATE_WAIT_ACK;
+ } else
+ txcmd->opctl |= SDIO_FIFO_TX_BLKWR;
+ } else {
+ block_length = 0;
+ blocks = 0;
+ }
+
+ /* Set the correct expected response length */
+ if (cmd->flags & MMC_RSP_136)
+ txcmd->opctl |= SDIO_FIFO_TX_136RSP;
+ else
+ txcmd->opctl |= SDIO_FIFO_TX_48RSP;
+
+ /* Set the correct CRC configuration */
+ if (!(cmd->flags & MMC_RSP_CRC)) {
+ printk_debug("CRC is disabled\n");
+ txcmd->opctl |= SDIO_FIFO_TX_DISCRC;
+ }
+
+ /* Set the correct bus width */
+ if (port->mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
+ printk_debug("Bus width has four bits\n");
+ txcmd->opctl |= SDIO_FIFO_TX_BW4;
+ }
+
+ txcmd->blksz_msb = (block_length >> 8);
+ txcmd->blksz_lsb = block_length;
+ txcmd->cmd[0] = SDIO_HOST_TX_HDR | (cmd->opcode & SDIO_HOST_CMD_MASK);
+ txcmd->cmd[1] = cmd->arg >> 24;
+ txcmd->cmd[2] = cmd->arg >> 16;
+ txcmd->cmd[3] = cmd->arg >> 8;
+ txcmd->cmd[4] = cmd->arg;
+
+ /*
+ * Store the private data for the callback function
+ * If an error ocurrs when sending the buffer, the timeout function will
+ * send the error to the MMC-layer
+ */
+ port->buf = buf;
+ port->mmc_cmd = cmd;
+ buf->private = port;
+ if ((retval = fim_sd_send_buffer(port, buf))) {
+/* printk_err("MMC command %i (err %i)\n", cmd->opcode, */
+/* retval); */
+ fim_sd_free_buffer(port, buf);
+ mod_timer(&port->mmc_timer,
+ jiffies + msecs_to_jiffies(1/* FIM_SDIO_TIMEOUT_MS */));
+ goto exit_ok;
+ } else
+ mod_timer(&port->mmc_timer,
+ jiffies + msecs_to_jiffies(FIM_SDIO_TIMEOUT_MS));
+
+ /*
+ * If we have a write command then fill a next buffer and send it
+ * @TODO: We need here an error handling, then otherwise we have started a
+ * WR-transfer but have no transfer data (perhaps not too critical?)
+ */
+ if (data && data->flags & MMC_DATA_WRITE) {
+ length = data->blksz * data->blocks;
+ if (!(buf = fim_sd_alloc_buffer(port, length))) {
+ printk_err("Buffer alloc BLKWR failed, %i\n", length);
+ goto exit_ok;
+ }
+
+ buf->private = port;
+ fim_sd_sg_to_dma(port, data, buf);
+ if ((retval = fim_sd_send_buffer(port, buf))) {
+ printk_err("Send BLKWR-buffer failed, %i\n", retval);
+ fim_sd_free_buffer(port, buf);
+ }
+ }
+
+ exit_ok:
+ return 0;
+}
+
+/*
+ * This function will be called from the request function and from the ISR callback
+ * when a command was executed
+ * In some cases we don't need to pass the command response to the Linux-layer (e.g.
+ * by the configuration of the bus width)
+ */
+static void fim_sd_process_next(struct fim_sdio_t *port)
+{
+ if (port->flags == FIM_SDIO_REQUEST_NEW) {
+ port->flags = FIM_SDIO_REQUEST_CMD;
+ fim_sd_send_command(port, port->mmc_req->cmd);
+ } else if ((!(port->flags & FIM_SDIO_REQUEST_STOP)) && port->mmc_req->stop) {
+ port->flags = FIM_SDIO_REQUEST_STOP;
+ fim_sd_send_command(port, port->mmc_req->stop);
+ } else {
+ /* By timeouts the core might retry sending another command */
+ port->mmc_cmd = NULL;
+ mmc_request_done(port->mmc, port->mmc_req);
+ }
+}
+
+/*
+ * Called for processing three main request types:
+ * command : Submit the command to the PIC and returns inmediately
+ * stop : Request for stopping an already started request?
+ * data : For data transfer
+ */
+static void fim_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct fim_sdio_t *port;
+
+ if (!(port = get_port_from_mmc(mmc)))
+ return;
+
+ /* In the case that the FIM was shutdown return at this point */
+ if (atomic_read(&port->fim_is_down)) {
+ mrq->cmd->error = -ENOMEDIUM;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+ /*
+ * Wait if the timeout function is running or the RX-callback is active
+ */
+ port->mmc_req = mrq;
+ port->flags = FIM_SDIO_REQUEST_NEW;
+ fim_sd_process_next(port);
+}
+
+/* Set the transfer clock using the pre-defined values */
+static void fim_sd_set_clock(struct fim_sdio_t *port, long int clockrate)
+{
+ unsigned long clkdiv;
+ unsigned long clk;
+
+ if (clockrate) {
+ clk = clk_get_rate(port->sys_clk) / 4;
+ clkdiv = clk /clockrate + 0x02;
+
+ /* @XXX: If the configuration failed disable the clock */
+ if (clkdiv < 0x4 || clkdiv > 0xff) {
+ printk_err("Unsupported clock %luHz (div out of range 0x%4lx)\n",
+ clockrate, clkdiv);
+ clockrate = -EINVAL;
+ }
+ else
+ printk_debug("Calculated divisor is 0x%02lx for %lu\n",
+ clkdiv, clockrate);
+ } else {
+ printk_debug("@TODO: Disable the clock (set to 0Hz)\n");
+ clkdiv = 0;
+ clockrate = -EINVAL;
+ }
+
+ /* Now write the value to the corresponding FIM-register */
+ if (clockrate >= 0) {
+ printk_debug("Setting the clock to %ld (%lx)\n",
+ clockrate, clkdiv);
+ fim_set_ctrl_reg(&port->fim, FIM_SDIO_CLKDIV_REG, clkdiv);
+ }
+}
+
+/*
+ * Called by the core system for setting the available modes and clock speed
+ *
+ */
+static void fim_sd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct fim_sdio_t *port;
+ unsigned long ireg;
+
+ if (!(port = get_port_from_mmc(mmc)))
+ return;
+
+ /*
+ * The FIM-board doesn't have a power switch for the card, but probably the
+ * next revision will include it, so that we can control the switch from here.
+ */
+ ireg = 0;
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ case MMC_POWER_UP:
+ case MMC_POWER_ON:
+ ireg |= FIM_SDIO_INTCFG_MAIN | FIM_SDIO_INTCFG_CARD;
+ break;
+ }
+
+ fim_set_ctrl_reg(&port->fim, FIM_SDIO_INTCFG_REG, ireg);
+
+ fim_sd_set_clock(port, ios->clock);
+}
+
+/*
+ * Return the read only status of the plugged card
+ * Since the FIM-firmware doesn't include this GPIO, we will read it at this point
+ */
+static int fim_sd_get_ro(struct mmc_host *mmc)
+{
+ struct fim_sdio_t *port;
+
+ if (!(port = get_port_from_mmc(mmc))) {
+ printk_err("No FIM-port by a registered MMC-host?\n");
+ return -ENODEV;
+ }
+
+ return gpio_get_value_ns921x(port->wp_gpio);
+}
+
+
+static void fim_sd_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct fim_sdio_t *port;
+ unsigned int ireg;
+
+ printk_debug("%s the SDIO IRQ\n", enable ? "Enabling" : "Disabling");
+ if (!(port = get_port_from_mmc(mmc))) {
+ printk_err("NULL port pointer found!\n");
+ return;
+ }
+
+ fim_get_ctrl_reg(&port->fim, FIM_SDIO_INTCFG_REG, &ireg);
+ if (enable)
+ ireg |= FIM_SDIO_INTCFG_SDIO;
+ else
+ ireg &= ~FIM_SDIO_INTCFG_SDIO;
+
+ fim_set_ctrl_reg(&port->fim, FIM_SDIO_INTCFG_REG, ireg);
+}
+
+
+/* Available driver host operations */
+static const struct mmc_host_ops fim_sd_ops = {
+ .request = fim_sd_request,
+ .set_ios = fim_sd_set_ios,
+ .get_ro = fim_sd_get_ro,
+ .enable_sdio_irq = fim_sd_enable_sdio_irq,
+};
+
+
+/* Called normally only when exiting the driver */
+static int fim_sdio_unregister_port(struct fim_sdio_t *port)
+{
+ int cnt;
+
+ if (!port || !port->reg)
+ return -ENODEV;
+
+ printk_info("Removing the FIM MMC host %i\n", port->index);
+ mmc_remove_host(port->mmc);
+ mmc_free_host(port->mmc);
+
+ del_timer_sync(&port->mmc_timer);
+
+ /* Reset the main control register */
+ fim_set_ctrl_reg(&port->fim, FIM_SDIO_MAIN_REG, 0);
+
+ fim_unregister_driver(&port->fim);
+
+ for (cnt=0; cnt < FIM_SDIO_MAX_GPIOS; cnt++) {
+ printk_debug("Freeing GPIO %i\n", port->gpios[cnt].nr);
+ if (port->gpios[cnt].nr == FIM_LAST_GPIO)
+ break;
+ else if (port->gpios[cnt].nr == FIM_GPIO_DONT_USE)
+ continue;
+ else
+ gpio_free(port->gpios[cnt].nr);
+ }
+
+ if (port->sys_clk && !IS_ERR(port->sys_clk)) {
+ clk_put(port->sys_clk);
+ port->sys_clk = NULL;
+ }
+
+ if (port->cd_irq > 0) {
+ free_irq(port->cd_irq, port);
+ port->cd_irq = -1;
+ }
+
+ port->reg = 0;
+ return 0;
+}
+
+
+
+/* Register the new FIM driver by the FIM-API */
+static int fim_sdio_register_port(struct device *dev, struct fim_sdio_t *port,
+ struct fim_sdio_platform_data *pdata,
+ struct fim_gpio_t gpios[])
+{
+ int retval;
+ int cnt;
+ struct fim_dma_cfg_t dma_cfg;
+ int picnr = pdata->fim_nr;
+ unsigned long hcaps = pdata->host_caps;
+ unsigned int fwver;
+
+#if defined(MODULE)
+ const char *fwcode = NULL;
+ char fwname[FIM_SDIO_FW_LEN];
+ snprintf(fwname, FIM_SDIO_FW_LEN, FIM_SDIO_FW_FILE_PAT, picnr);
+#else
+ char *fwname = NULL;
+ const char *fwcode = (picnr == 0) ? FIM_SDIO_FW_CODE0 : FIM_SDIO_FW_CODE1;
+#endif
+
+ /* Specific DMA configuration for the SD-host driver */
+ dma_cfg.rxnr = FIM_SDIO_DMA_RX_BUFFERS;
+ dma_cfg.txnr = FIM_SDIO_DMA_TX_BUFFERS;
+ dma_cfg.rxsz = FIM_SDIO_DMA_BUFFER_SIZE;
+ dma_cfg.txsz = FIM_SDIO_DMA_BUFFER_SIZE;
+
+ port->index = picnr;
+ port->fim.picnr = picnr;
+ port->fim.driver.name = FIM_SDIO_DRIVER_NAME;
+ port->fim.driver_data = port;
+ port->fim.fim_isr = fim_sd_isr;
+ port->fim.dma_tx_isr = fim_sd_tx_isr;
+ port->fim.dma_rx_isr = fim_sd_rx_isr;
+ port->fim.driver_data = port;
+ port->fim.dma_cfg = &dma_cfg;
+
+ /* Check if have a firmware code for using to */
+ port->fim.fw_name = fwname;
+ port->fim.fw_code = fwcode;
+ retval = fim_register_driver(&port->fim);
+ if (retval) {
+ printk_err("Couldn't register the FIM driver.\n");
+ return retval;
+ }
+
+ /* Request the corresponding GPIOs (@XXX: Check the returned values) */
+ for (cnt=0; cnt < FIM_SDIO_MAX_GPIOS; cnt++) {
+
+ if (gpios[cnt].nr == FIM_LAST_GPIO)
+ break;
+
+ if (gpios[cnt].nr == FIM_GPIO_DONT_USE)
+ continue;
+
+ printk_debug("Requesting the GPIO %i (Function %i)\n",
+ gpios[cnt].nr, gpios[cnt].func);
+ retval = gpio_request(gpios[cnt].nr, FIM_SDIO_DRIVER_NAME);
+ if (!retval) {
+ gpio_configure_ns921x_unlocked(gpios[cnt].nr,
+ NS921X_GPIO_INPUT,
+ NS921X_GPIO_DONT_INVERT,
+ gpios[cnt].func,
+ NS921X_GPIO_DISABLE_PULLUP);
+ } else {
+ /* Free the already requested GPIOs */
+ printk_err("Couldn't request the GPIO %i\n", gpios[cnt].nr);
+ while (cnt)
+ gpio_free(gpios[--cnt].nr);
+
+ goto exit_unreg_fim;
+ }
+ }
+
+ /* Configure and init the timer for the command timeouts */
+ init_timer(&port->mmc_timer);
+ port->mmc_timer.function = fim_sd_cmd_timeout;
+ port->mmc_timer.data = (unsigned long)port;
+
+ /* This is the timer for checking the state of the card detect line */
+ init_timer(&port->cd_timer);
+ port->cd_timer.function = fim_sd_cd_timer_func;
+ port->cd_timer.data = (unsigned long)port;
+
+ port->mmc = mmc_alloc_host(sizeof(struct fim_sdio_t *), port->fim.dev);
+ if (!port->mmc) {
+ printk_err("Alloc MMC host by the FIM %i failed.\n", picnr);
+ retval = -ENOMEM;
+ goto exit_free_gpios;
+ }
+
+ /* Get a reference to the SYS clock for setting the clock */
+ if (IS_ERR(port->sys_clk = clk_get(port->fim.dev, "systemclock"))) {
+ printk_err("Couldn't get the SYS clock.\n");
+ goto exit_free_host;
+ }
+
+ /* These are the default values for this SD-host */
+ port->mmc->ops = &fim_sd_ops;
+
+ /* Supported physical properties of the FIM-host (see the PIC-firmware code) */
+ port->mmc->f_min = 320000;
+ port->mmc->f_max = 25000000;
+ port->mmc->ocr_avail = MMC_VDD_33_34 | MMC_VDD_32_33;
+ port->mmc->caps = hcaps;
+
+ /* Maximum number of blocks in one req */
+ port->mmc->max_blk_count = FIM_SDIO_MAX_BLOCKS;
+ port->mmc->max_blk_size = FIM_SDIO_DMA_BUFFER_SIZE;
+ /* The maximum per SG entry depends on the buffer size */
+ port->mmc->max_seg_size = FIM_SDIO_DMA_BUFFER_SIZE;
+
+ port->mmc->max_req_size = 4095 * FIM_SDIO_MAX_BLOCKS;
+ port->mmc->max_seg_size = port->mmc->max_req_size;
+ port->mmc->max_phys_segs = FIM_SDIO_MAX_BLOCKS;
+ port->mmc->max_hw_segs = FIM_SDIO_MAX_BLOCKS;
+
+ /* Save our port structure into the private pointer */
+ port->mmc->private[0] = (unsigned long)port;
+ retval = mmc_add_host(port->mmc);
+ if (retval) {
+ printk_err("Couldn't add the MMC host\n");
+ goto exit_put_clk;
+ }
+
+ memcpy(port->gpios, gpios, sizeof(struct fim_gpio_t) * FIM_SDIO_MAX_GPIOS);
+ port->reg = 1;
+ dev_set_drvdata(dev, port);
+
+ /* Fist disable the SDIO-IRQ */
+ fim_sd_enable_sdio_irq(port->mmc, 0);
+
+ /*
+ * If no interrupt line is available for the card detection, then use the timer
+ * for the polling.
+ */
+ port->cd_irq = gpio_to_irq(port->cd_gpio);
+
+#if defined(FIM_SDIO_FORCE_CD_POLLING)
+ port->cd_irq = -1;
+#endif
+
+ if (port->cd_irq > 0) {
+ int iret;
+
+ /* First check if we have an IRQ at this line */
+ printk_info("Got IRQ %i for GPIO %i\n", port->cd_irq,
+ port->cd_gpio);
+
+ iret = request_irq(port->cd_irq, fim_sd_cd_irq,
+ IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+ "fim-sdio-cd", port);
+ if (iret) {
+ printk_err("Failed IRQ %i request (%i)\n", port->cd_irq, iret);
+ port->cd_irq = -1;
+ }
+ }
+
+ /*
+ * By errors (e.g. in the case that we couldn't request the IRQ) use the
+ * polling function
+ */
+ if (port->cd_irq < 0) {
+ printk(KERN_DEBUG "Polling the Card Detect line (no IRQ)\n");
+ port->cd_value = 1; /* @XXX: Use an invert value for this purpose */
+ mod_timer(&port->cd_timer, jiffies + HZ / 2);
+ } else {
+ /*
+ * Setup the card detect interrupt at this point, otherwise the first
+ * event detection will not work when the system is booted with a
+ * plugged card.
+ */
+ if ((retval = fim_sd_prepare_cd_irq(port))) {
+ printk_err("Failed card detect IRQ setup\n");
+ goto exit_free_cdirq;
+ }
+ }
+
+ INIT_WORK(&port->restart_work, fim_sd_restart_work_func);
+
+ /* And enable the FIM-interrupt */
+ fim_enable_irq(&port->fim);
+ fim_set_ctrl_reg(&port->fim, FIM_SDIO_MAIN_REG, FIM_SDIO_MAIN_START);
+
+ /* Print the firmware version */
+ fim_get_stat_reg(&port->fim, FIM_SDIO_VERSION_SREG, &fwver);
+ printk_dbg("FIM%d running [fw rev 0x%02x]\n", port->fim.picnr, fwver);
+ return 0;
+
+ exit_free_cdirq:
+ free_irq(port->cd_irq, port);
+
+ exit_put_clk:
+ clk_put(port->sys_clk);
+
+ exit_free_host:
+ mmc_free_host(port->mmc);
+
+ exit_free_gpios:
+ for (cnt=0; gpios[cnt].nr < FIM_SDIO_MAX_GPIOS; cnt++) {
+ if (gpios[cnt].nr == FIM_LAST_GPIO)
+ break;
+ if (gpios[cnt].nr != FIM_GPIO_DONT_USE)
+ gpio_free(gpios[cnt].nr);
+ }
+
+ exit_unreg_fim:
+ fim_unregister_driver(&port->fim);
+
+ return retval;
+}
+
+
+
+static int __devinit fim_sdio_probe(struct platform_device *pdev)
+{
+ struct fim_sdio_t *port;
+ struct fim_sdio_platform_data *pdata;
+ struct fim_gpio_t gpios[FIM_SDIO_MAX_GPIOS];
+ int retval;
+
+ printk_debug("Probing a new device with ID %i\n", pdev->id);
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -ENXIO;
+
+ if (fim_check_device_id(fims_number, pdata->fim_nr)) {
+#if defined(MODULE)
+ printk_dbg("Skipping FIM%i (not selected)\n", pdata->fim_nr);
+#else
+ printk_err("Invalid FIM number '%i' in platform data\n", pdata->fim_nr);
+#endif
+ return -ENODEV;
+ }
+
+ port = fim_sdios->ports + pdata->fim_nr;
+
+ /* Get the GPIOs-table from the platform data structure */
+ gpios[FIM_SDIO_D0_GPIO].nr = pdata->d0_gpio_nr;
+ gpios[FIM_SDIO_D0_GPIO].func = pdata->d0_gpio_func;
+ gpios[FIM_SDIO_D1_GPIO].nr = pdata->d1_gpio_nr;
+ gpios[FIM_SDIO_D1_GPIO].func = pdata->d1_gpio_func;
+ gpios[FIM_SDIO_D2_GPIO].nr = pdata->d2_gpio_nr;
+ gpios[FIM_SDIO_D2_GPIO].func = pdata->d2_gpio_func;
+ gpios[FIM_SDIO_D3_GPIO].nr = pdata->d3_gpio_nr;
+ gpios[FIM_SDIO_D3_GPIO].func = pdata->d3_gpio_func;
+ gpios[FIM_SDIO_WP_GPIO].nr = pdata->wp_gpio_nr;
+ gpios[FIM_SDIO_WP_GPIO].func = pdata->wp_gpio_func;
+ gpios[FIM_SDIO_CD_GPIO].nr = pdata->cd_gpio_nr;
+ gpios[FIM_SDIO_CD_GPIO].func = pdata->cd_gpio_func;
+ gpios[FIM_SDIO_CLK_GPIO].nr = pdata->clk_gpio_nr;
+ gpios[FIM_SDIO_CLK_GPIO].func = pdata->clk_gpio_func;
+ gpios[FIM_SDIO_CMD_GPIO].nr = pdata->cmd_gpio_nr;
+ gpios[FIM_SDIO_CMD_GPIO].func = pdata->cmd_gpio_func;
+ port->wp_gpio = pdata->wp_gpio_nr;
+ port->cd_gpio = pdata->cd_gpio_nr;
+
+ retval = fim_sdio_register_port(&pdev->dev, port, pdata, gpios);
+
+ return retval;
+}
+
+static int __devexit fim_sdio_remove(struct platform_device *pdev)
+{
+ struct fim_sdio_t *port;
+ int retval;
+
+ port = dev_get_drvdata(&pdev->dev);
+
+ retval = fim_sdio_unregister_port(port);
+ if (!retval)
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return retval;
+}
+
+static struct platform_driver fim_sdio_platform_driver = {
+ .probe = fim_sdio_probe,
+ .remove = __devexit_p(fim_sdio_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = FIM_SDIO_DRIVER_NAME,
+ },
+};
+
+/*
+ * This is the function that will be called when the module is loaded
+ * into the kernel space
+ */
+static int __init fim_sdio_init(void)
+{
+ int retval;
+ int nrpics;
+
+ printk_debug("Starting the FIM SDIO driver.\n");
+
+ /* Get the number of available FIMs */
+ nrpics = fim_number_pics();
+
+ /* Check for the passed number parameter */
+ if (fim_check_numbers_param(fims_number)) {
+ printk_err("Invalid number '%i' of FIMs to handle\n", fims_number);
+ return -EINVAL;
+ }
+
+ fim_sdios = kzalloc(sizeof(struct fim_sdios_t) +
+ (nrpics * sizeof(struct fim_sdio_t)), GFP_KERNEL);
+ if (!fim_sdios)
+ return -ENOMEM;
+
+ fim_sdios->fims = nrpics;
+ fim_sdios->ports = (void *)fim_sdios + sizeof(struct fim_sdios_t);
+
+ retval = platform_driver_register(&fim_sdio_platform_driver);
+ if (retval)
+ goto exit_free_ports;
+
+ printk_info(DRIVER_DESC " v" DRIVER_VERSION "\n");
+ return 0;
+
+ exit_free_ports:
+ kfree(fim_sdios);
+
+ return retval;
+}
+
+
+
+/*
+ * Free the requested resources (GPIOs, memory, drivers, etc.)
+ * The following steps MUST be followed when unregistering the driver:
+ * - First remove and free the MMC-host
+ * - Unregister the FIM-driver (will free the DMA-channel)
+ * - Free the GPIOs at last
+ */
+static void __exit fim_sdio_exit(void)
+{
+ printk_info("Removing the FIM SDIO driver\n");
+ platform_driver_unregister(&fim_sdio_platform_driver);
+ kfree(fim_sdios);
+}
+
+
+module_init(fim_sdio_init);
+module_exit(fim_sdio_exit);
+
diff --git a/drivers/fims/sdio/fim_sdio0.h b/drivers/fims/sdio/fim_sdio0.h
new file mode 100644
index 000000000000..12c99a074c5d
--- /dev/null
+++ b/drivers/fims/sdio/fim_sdio0.h
@@ -0,0 +1,225 @@
+
+/*
+ * Automatic generated header file with the firmware code for the FIM
+ * Input binary : fim_firmware.bin
+ * Output header : fim_sdio.h
+ * Structure : fim_sdio_firmware0
+ */
+
+static const unsigned char fim_sdio_firmware0[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2f, 0x03, 0x00, 0x00, 0x0e, 0x28, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x03, 0x08,
+ 0x83, 0x12, 0xa1, 0x00, 0x8b, 0x10, 0x21, 0x08,
+ 0x83, 0x00, 0xa0, 0x0e, 0x20, 0x0e, 0x09, 0x00,
+ 0x1d, 0x23, 0x83, 0x12, 0x9f, 0x01, 0x9f, 0x14,
+ 0x83, 0x16, 0xff, 0x30, 0x99, 0x00, 0x83, 0x12,
+ 0x83, 0x12, 0x15, 0x1c, 0x16, 0x28, 0x00, 0x30,
+ 0xa6, 0x00, 0x1c, 0x28, 0xa4, 0x01, 0xbb, 0x01,
+ 0xbc, 0x01, 0xbd, 0x01, 0xbe, 0x01, 0xbf, 0x01,
+ 0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01, 0x83, 0x16,
+ 0xa1, 0x01, 0x83, 0x12, 0xb9, 0x01, 0x3d, 0x20,
+ 0x8c, 0x1d, 0x54, 0x28, 0x2e, 0x20, 0x1c, 0x28,
+ 0x83, 0x16, 0x1a, 0x14, 0x83, 0x12, 0x10, 0x08,
+ 0xac, 0x00, 0xac, 0x0b, 0x33, 0x28, 0x83, 0x16,
+ 0x1a, 0x10, 0x83, 0x12, 0x10, 0x08, 0xac, 0x00,
+ 0xac, 0x0b, 0x3a, 0x28, 0x08, 0x00, 0x22, 0x1c,
+ 0x48, 0x28, 0x83, 0x16, 0x89, 0x1f, 0x46, 0x28,
+ 0x83, 0x12, 0x89, 0x01, 0x22, 0x10, 0x08, 0x00,
+ 0x83, 0x12, 0x08, 0x00, 0x11, 0x1c, 0x08, 0x00,
+ 0x83, 0x16, 0x90, 0x1c, 0x4f, 0x28, 0x83, 0x12,
+ 0x08, 0x00, 0x83, 0x12, 0x22, 0x14, 0x01, 0x30,
+ 0x89, 0x00, 0x08, 0x00, 0x9f, 0x10, 0xa7, 0x01,
+ 0x0f, 0x23, 0xa3, 0x00, 0x0f, 0x23, 0xb5, 0x00,
+ 0x0f, 0x23, 0xb4, 0x00, 0xff, 0x39, 0x03, 0x19,
+ 0x62, 0x28, 0x35, 0x08, 0x01, 0x3e, 0xb5, 0x00,
+ 0x0f, 0x23, 0x75, 0x20, 0x0f, 0x23, 0x75, 0x20,
+ 0x0f, 0x23, 0x75, 0x20, 0x0f, 0x23, 0x75, 0x20,
+ 0x0f, 0x23, 0x75, 0x20, 0x8e, 0x20, 0x27, 0x08,
+ 0x75, 0x20, 0x9f, 0x14, 0x23, 0x18, 0x9b, 0x28,
+ 0xa3, 0x18, 0x9b, 0x28, 0x46, 0x29, 0xaa, 0x00,
+ 0x08, 0x30, 0xad, 0x00, 0xaa, 0x0d, 0x83, 0x16,
+ 0x03, 0x18, 0x7e, 0x28, 0x9a, 0x10, 0x7f, 0x28,
+ 0x9a, 0x14, 0x83, 0x12, 0x85, 0x20, 0x2e, 0x20,
+ 0xad, 0x0b, 0x78, 0x28, 0x08, 0x00, 0xa7, 0x13,
+ 0x03, 0x18, 0xa7, 0x17, 0xa7, 0x0d, 0xa7, 0x1f,
+ 0x08, 0x00, 0x09, 0x30, 0xa7, 0x06, 0x08, 0x00,
+ 0x07, 0x30, 0xa9, 0x00, 0x03, 0x10, 0xa7, 0x0d,
+ 0xa7, 0x1f, 0x96, 0x28, 0x09, 0x30, 0xa7, 0x06,
+ 0xa9, 0x0b, 0x90, 0x28, 0x03, 0x14, 0xa7, 0x0d,
+ 0x08, 0x00, 0x64, 0x30, 0xab, 0x00, 0xb0, 0x01,
+ 0xa7, 0x01, 0xab, 0x0b, 0xa2, 0x28, 0x1c, 0x29,
+ 0x1f, 0x21, 0x03, 0x18, 0x9f, 0x28, 0x23, 0x1a,
+ 0xe0, 0x28, 0x01, 0x30, 0x18, 0x23, 0x03, 0x10,
+ 0x2d, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0xa3, 0x18, 0xc2, 0x28,
+ 0x8e, 0x20, 0x27, 0x08, 0xa8, 0x00, 0x2c, 0x21,
+ 0x13, 0x23, 0x28, 0x08, 0xa3, 0x19, 0xc0, 0x28,
+ 0x18, 0x23, 0x46, 0x29, 0x13, 0x23, 0x46, 0x29,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x8e, 0x20, 0x27, 0x08,
+ 0xa8, 0x00, 0x2c, 0x21, 0x13, 0x23, 0x28, 0x08,
+ 0x18, 0x23, 0x46, 0x29, 0x50, 0x30, 0x84, 0x00,
+ 0x2d, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a, 0xa3, 0x1c,
+ 0x14, 0x29, 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x8e, 0x20, 0x27, 0x08,
+ 0xa8, 0x00, 0x2c, 0x21, 0x80, 0x00, 0x24, 0x18,
+ 0xfc, 0x29, 0x0b, 0x2a, 0x04, 0x30, 0x18, 0x23,
+ 0x1c, 0x28, 0x23, 0x1a, 0x21, 0x22, 0x83, 0x16,
+ 0x92, 0x18, 0x28, 0x29, 0x83, 0x12, 0x2e, 0x20,
+ 0x03, 0x10, 0x08, 0x00, 0x83, 0x12, 0x2e, 0x20,
+ 0x03, 0x14, 0x08, 0x00, 0x1f, 0x21, 0x85, 0x20,
+ 0xb0, 0x0d, 0x1f, 0x21, 0x85, 0x20, 0xb0, 0x0d,
+ 0x1f, 0x21, 0x85, 0x20, 0xb0, 0x0d, 0x1f, 0x21,
+ 0x85, 0x20, 0xb0, 0x0d, 0x1f, 0x21, 0x85, 0x20,
+ 0xb0, 0x0d, 0x1f, 0x21, 0x85, 0x20, 0xb0, 0x0d,
+ 0x1f, 0x21, 0x85, 0x20, 0xb0, 0x0d, 0x1f, 0x21,
+ 0x85, 0x20, 0xb0, 0x0d, 0x30, 0x08, 0x08, 0x00,
+ 0x23, 0x1a, 0xfc, 0x29, 0xa3, 0x19, 0x4f, 0x29,
+ 0x2e, 0x20, 0x2e, 0x20, 0x2e, 0x20, 0x3d, 0x20,
+ 0x1c, 0x28, 0x2e, 0x20, 0x2e, 0x20, 0xff, 0x30,
+ 0x23, 0x1d, 0x11, 0x30, 0x83, 0x16, 0x98, 0x00,
+ 0xff, 0x3a, 0x99, 0x00, 0x83, 0x12, 0x2e, 0x20,
+ 0x83, 0x16, 0x98, 0x01, 0x83, 0x12, 0x2e, 0x20,
+ 0x34, 0x08, 0xb6, 0x00, 0x35, 0x08, 0xb7, 0x00,
+ 0x0f, 0x23, 0xb8, 0x00, 0x23, 0x19, 0x79, 0x29,
+ 0x08, 0x30, 0xba, 0x00, 0xb8, 0x1b, 0x6f, 0x29,
+ 0x83, 0x16, 0x18, 0x10, 0x83, 0x12, 0x00, 0x30,
+ 0x73, 0x29, 0x83, 0x16, 0x18, 0x14, 0x83, 0x12,
+ 0x01, 0x30, 0xe7, 0x22, 0x2e, 0x20, 0xb8, 0x0d,
+ 0xba, 0x0b, 0x68, 0x29, 0x9e, 0x29, 0x26, 0x18,
+ 0x8c, 0x29, 0xb8, 0x0e, 0x38, 0x08, 0x83, 0x16,
+ 0x98, 0x00, 0x83, 0x12, 0xa3, 0x1e, 0xe7, 0x22,
+ 0x2e, 0x20, 0xb8, 0x0e, 0x38, 0x08, 0x83, 0x16,
+ 0x98, 0x00, 0x83, 0x12, 0xa3, 0x1e, 0xe7, 0x22,
+ 0x2e, 0x20, 0x9e, 0x29, 0x38, 0x08, 0x83, 0x16,
+ 0x98, 0x00, 0x83, 0x12, 0xb8, 0x0e, 0x38, 0x08,
+ 0xa3, 0x1e, 0xe7, 0x22, 0x2e, 0x20, 0x38, 0x08,
+ 0x83, 0x16, 0x98, 0x00, 0x83, 0x12, 0xb8, 0x0e,
+ 0x38, 0x08, 0xa3, 0x1e, 0xe7, 0x22, 0x2e, 0x20,
+ 0xb6, 0x0b, 0x62, 0x29, 0xb7, 0x0b, 0x62, 0x29,
+ 0xa3, 0x1a, 0xaa, 0x29, 0x10, 0x30, 0xc4, 0x00,
+ 0x03, 0x01, 0xe7, 0x22, 0xc4, 0x0b, 0xa6, 0x29,
+ 0x10, 0x30, 0xc4, 0x00, 0xc5, 0x01, 0xbb, 0x0d,
+ 0xbc, 0x0d, 0x03, 0x18, 0x45, 0x14, 0xbd, 0x0d,
+ 0xbe, 0x0d, 0x03, 0x18, 0xc5, 0x14, 0xbf, 0x0d,
+ 0xc0, 0x0d, 0x03, 0x18, 0x45, 0x15, 0xc1, 0x0d,
+ 0xc2, 0x0d, 0x03, 0x18, 0xc5, 0x15, 0x26, 0x18,
+ 0xc5, 0x0e, 0x45, 0x08, 0x83, 0x16, 0x98, 0x00,
+ 0x83, 0x12, 0x2e, 0x20, 0xc4, 0x0b, 0xac, 0x29,
+ 0x83, 0x16, 0xff, 0x30, 0x98, 0x00, 0x83, 0x12,
+ 0x2e, 0x20, 0x83, 0x16, 0xff, 0x30, 0x99, 0x00,
+ 0x83, 0x12, 0x2e, 0x20, 0x2e, 0x20, 0xc6, 0x01,
+ 0x83, 0x16, 0x10, 0x1c, 0xd7, 0x29, 0x83, 0x12,
+ 0xc6, 0x15, 0x83, 0x12, 0x2e, 0x20, 0x83, 0x16,
+ 0x10, 0x1c, 0xde, 0x29, 0x83, 0x12, 0x46, 0x15,
+ 0x83, 0x12, 0x2e, 0x20, 0x83, 0x16, 0x10, 0x1c,
+ 0xe5, 0x29, 0x83, 0x12, 0xc6, 0x14, 0x83, 0x12,
+ 0x2e, 0x20, 0x83, 0x16, 0x10, 0x1c, 0xec, 0x29,
+ 0x83, 0x12, 0x46, 0x14, 0x83, 0x12, 0x46, 0x08,
+ 0x18, 0x23, 0x2e, 0x20, 0x2e, 0x20, 0x2e, 0x20,
+ 0x83, 0x12, 0x2e, 0x20, 0x83, 0x16, 0x10, 0x1c,
+ 0xf2, 0x29, 0x83, 0x12, 0x2e, 0x20, 0x2e, 0x20,
+ 0x2e, 0x20, 0x1c, 0x28, 0x24, 0x18, 0x01, 0x2a,
+ 0x21, 0x22, 0x2e, 0x20, 0xfc, 0x29, 0x21, 0x22,
+ 0x2e, 0x20, 0x24, 0x18, 0x01, 0x2a, 0x24, 0x1d,
+ 0x0b, 0x2a, 0x2e, 0x20, 0x2e, 0x20, 0x2e, 0x20,
+ 0x1c, 0x28, 0x24, 0x15, 0x01, 0x30, 0x18, 0x23,
+ 0x50, 0x30, 0x84, 0x00, 0x06, 0x30, 0xa3, 0x18,
+ 0x11, 0x30, 0xcf, 0x00, 0x00, 0x08, 0x13, 0x23,
+ 0x84, 0x0a, 0xcf, 0x0b, 0x14, 0x2a, 0x28, 0x08,
+ 0x18, 0x23, 0x23, 0x1a, 0xfc, 0x29, 0x2e, 0x20,
+ 0x2e, 0x20, 0x2e, 0x20, 0x1c, 0x28, 0x24, 0x18,
+ 0x32, 0x2a, 0x83, 0x16, 0x10, 0x18, 0x30, 0x2a,
+ 0x83, 0x12, 0x24, 0x14, 0xa4, 0x14, 0x02, 0x30,
+ 0x18, 0x23, 0x34, 0x08, 0xb6, 0x00, 0x35, 0x08,
+ 0xb7, 0x00, 0x08, 0x00, 0x83, 0x12, 0x08, 0x00,
+ 0x23, 0x19, 0x4d, 0x2a, 0x83, 0x16, 0x03, 0x10,
+ 0x10, 0x18, 0x03, 0x14, 0xa1, 0x0d, 0x03, 0x01,
+ 0x10, 0x18, 0x01, 0x30, 0x83, 0x12, 0xa3, 0x1a,
+ 0x41, 0x2a, 0xa4, 0x18, 0xe7, 0x22, 0xb9, 0x0a,
+ 0xb9, 0x1d, 0x08, 0x00, 0x83, 0x16, 0x21, 0x08,
+ 0x83, 0x12, 0x13, 0x23, 0xb9, 0x01, 0x83, 0x16,
+ 0xa1, 0x01, 0x83, 0x12, 0x73, 0x2a, 0x39, 0x19,
+ 0x62, 0x2a, 0x83, 0x16, 0xa1, 0x01, 0x90, 0x19,
+ 0xa1, 0x15, 0x10, 0x19, 0x21, 0x15, 0x90, 0x18,
+ 0xa1, 0x14, 0x10, 0x18, 0x21, 0x14, 0x21, 0x08,
+ 0xa1, 0x0e, 0x83, 0x12, 0x39, 0x15, 0xa3, 0x1a,
+ 0x08, 0x00, 0xa4, 0x18, 0xe7, 0x22, 0x08, 0x00,
+ 0x83, 0x16, 0x90, 0x19, 0xa1, 0x15, 0x10, 0x19,
+ 0x21, 0x15, 0x90, 0x18, 0xa1, 0x14, 0x10, 0x18,
+ 0x21, 0x14, 0x21, 0x08, 0x83, 0x12, 0x13, 0x23,
+ 0xa3, 0x1a, 0x73, 0x2a, 0xa4, 0x18, 0xe7, 0x22,
+ 0xb9, 0x01, 0xb6, 0x0b, 0x08, 0x00, 0xb7, 0x0b,
+ 0x08, 0x00, 0xa4, 0x1c, 0x81, 0x2a, 0xa4, 0x10,
+ 0x08, 0x30, 0x23, 0x1d, 0x02, 0x30, 0xb6, 0x00,
+ 0x01, 0x30, 0xb7, 0x00, 0x08, 0x00, 0x24, 0x10,
+ 0x23, 0x12, 0xa3, 0x1a, 0xe4, 0x2a, 0x10, 0x30,
+ 0xc4, 0x00, 0x03, 0x01, 0xe7, 0x22, 0xc4, 0x0b,
+ 0x87, 0x2a, 0x23, 0x19, 0x92, 0x2a, 0x3c, 0x08,
+ 0x13, 0x23, 0x3b, 0x08, 0x18, 0x23, 0x08, 0x00,
+ 0x04, 0x08, 0xe7, 0x00, 0x47, 0x30, 0x84, 0x00,
+ 0x08, 0x30, 0xc4, 0x00, 0x80, 0x01, 0x84, 0x0a,
+ 0xc4, 0x0b, 0x98, 0x2a, 0x47, 0x30, 0x84, 0x00,
+ 0x04, 0x30, 0xc4, 0x00, 0xc2, 0x0d, 0x03, 0x18,
+ 0x80, 0x17, 0xc2, 0x0d, 0x03, 0x18, 0x80, 0x15,
+ 0xc0, 0x0d, 0x03, 0x18, 0x00, 0x17, 0xc0, 0x0d,
+ 0x03, 0x18, 0x00, 0x15, 0xbe, 0x0d, 0x03, 0x18,
+ 0x80, 0x16, 0xbe, 0x0d, 0x03, 0x18, 0x80, 0x14,
+ 0xbc, 0x0d, 0x03, 0x18, 0x00, 0x16, 0xbc, 0x0d,
+ 0x03, 0x18, 0x00, 0x14, 0x84, 0x0a, 0xc4, 0x0b,
+ 0xa0, 0x2a, 0x04, 0x30, 0xc4, 0x00, 0xc1, 0x0d,
+ 0x03, 0x18, 0x80, 0x17, 0xc1, 0x0d, 0x03, 0x18,
+ 0x80, 0x15, 0xbf, 0x0d, 0x03, 0x18, 0x00, 0x17,
+ 0xbf, 0x0d, 0x03, 0x18, 0x00, 0x15, 0xbd, 0x0d,
+ 0x03, 0x18, 0x80, 0x16, 0xbd, 0x0d, 0x03, 0x18,
+ 0x80, 0x14, 0xbb, 0x0d, 0x03, 0x18, 0x00, 0x16,
+ 0xbb, 0x0d, 0x03, 0x18, 0x00, 0x14, 0x84, 0x0a,
+ 0xc4, 0x0b, 0xbd, 0x2a, 0x47, 0x30, 0x84, 0x00,
+ 0x07, 0x30, 0xc4, 0x00, 0x00, 0x08, 0x13, 0x23,
+ 0x84, 0x0a, 0xc4, 0x0b, 0xdc, 0x2a, 0x00, 0x08,
+ 0x18, 0x23, 0x08, 0x00, 0x03, 0x01, 0x18, 0x23,
+ 0x08, 0x00, 0xc3, 0x00, 0xc3, 0x0c, 0xbb, 0x0d,
+ 0xbc, 0x0d, 0x03, 0x1c, 0xf1, 0x2a, 0x21, 0x30,
+ 0xbb, 0x06, 0x10, 0x30, 0xbc, 0x06, 0x23, 0x1d,
+ 0x08, 0x00, 0xc3, 0x0c, 0xbd, 0x0d, 0xbe, 0x0d,
+ 0x03, 0x1c, 0xfc, 0x2a, 0x21, 0x30, 0xbd, 0x06,
+ 0x10, 0x30, 0xbe, 0x06, 0xc3, 0x0c, 0xbf, 0x0d,
+ 0xc0, 0x0d, 0x03, 0x1c, 0x05, 0x2b, 0x21, 0x30,
+ 0xbf, 0x06, 0x10, 0x30, 0xc0, 0x06, 0xc3, 0x0c,
+ 0xc1, 0x0d, 0xc2, 0x0d, 0x03, 0x1c, 0x08, 0x00,
+ 0x21, 0x30, 0xc1, 0x06, 0x10, 0x30, 0xc2, 0x06,
+ 0x08, 0x00, 0x8c, 0x19, 0x0f, 0x2b, 0x0f, 0x08,
+ 0x08, 0x00, 0x8c, 0x1b, 0x13, 0x2b, 0x8e, 0x01,
+ 0x8d, 0x00, 0x08, 0x00, 0x8c, 0x1b, 0x18, 0x2b,
+ 0x8e, 0x0a, 0x8d, 0x00, 0x08, 0x00, 0x83, 0x12,
+ 0x20, 0x30, 0x84, 0x00, 0x80, 0x01, 0x84, 0x0a,
+ 0x04, 0x08, 0x80, 0x3c, 0x03, 0x1d, 0x20, 0x2b,
+ 0xa0, 0x30, 0x84, 0x00, 0x80, 0x01, 0x84, 0x0a,
+ 0x04, 0x08, 0xff, 0x3c, 0x03, 0x1d, 0x28, 0x2b,
+ 0x08, 0x00
+};
+
diff --git a/drivers/fims/sdio/fim_sdio0_9210.h b/drivers/fims/sdio/fim_sdio0_9210.h
new file mode 100644
index 000000000000..540583e9f999
--- /dev/null
+++ b/drivers/fims/sdio/fim_sdio0_9210.h
@@ -0,0 +1,197 @@
+
+/*
+ * Automatic generated header file with the firmware code for the FIM
+ * Input binary : fim_firmware.bin
+ * Output header : fim_sdio_9210.h
+ * Structure : fim_sdio_firmware0
+ */
+
+static const unsigned char fim_sdio_firmware0[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xbf, 0x02, 0x00, 0x00, 0x0e, 0x28, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x03, 0x08,
+ 0x83, 0x12, 0xa1, 0x00, 0x8b, 0x10, 0x21, 0x08,
+ 0x83, 0x00, 0xa0, 0x0e, 0x20, 0x0e, 0x09, 0x00,
+ 0x01, 0x30, 0x98, 0x00, 0xad, 0x22, 0x83, 0x16,
+ 0x99, 0x01, 0x19, 0x15, 0x19, 0x14, 0x83, 0x12,
+ 0x17, 0x28, 0xa4, 0x01, 0xbb, 0x01, 0xbc, 0x01,
+ 0xbd, 0x01, 0xbe, 0x01, 0xbf, 0x01, 0xc0, 0x01,
+ 0xc1, 0x01, 0xc2, 0x01, 0x83, 0x16, 0xa1, 0x01,
+ 0x83, 0x12, 0xb9, 0x01, 0x8c, 0x1d, 0x37, 0x28,
+ 0x28, 0x20, 0x17, 0x28, 0x83, 0x16, 0x98, 0x14,
+ 0x83, 0x12, 0x10, 0x08, 0xac, 0x00, 0xac, 0x0b,
+ 0x2d, 0x28, 0x83, 0x16, 0x98, 0x10, 0x83, 0x12,
+ 0x10, 0x08, 0xac, 0x00, 0xac, 0x0b, 0x34, 0x28,
+ 0x08, 0x00, 0x83, 0x16, 0x19, 0x11, 0x83, 0x12,
+ 0xa7, 0x01, 0x93, 0x22, 0xa3, 0x00, 0x93, 0x22,
+ 0xb5, 0x00, 0x93, 0x22, 0xb4, 0x00, 0xff, 0x39,
+ 0x03, 0x19, 0x47, 0x28, 0x35, 0x08, 0x01, 0x3e,
+ 0xb5, 0x00, 0x93, 0x22, 0x5c, 0x20, 0x93, 0x22,
+ 0x5c, 0x20, 0x93, 0x22, 0x5c, 0x20, 0x93, 0x22,
+ 0x5c, 0x20, 0x93, 0x22, 0x5c, 0x20, 0x75, 0x20,
+ 0x27, 0x08, 0x5c, 0x20, 0x83, 0x16, 0x19, 0x15,
+ 0x83, 0x12, 0x23, 0x18, 0x82, 0x28, 0xa3, 0x18,
+ 0x82, 0x28, 0x2d, 0x29, 0xaa, 0x00, 0x08, 0x30,
+ 0xad, 0x00, 0xaa, 0x0d, 0x83, 0x16, 0x03, 0x18,
+ 0x65, 0x28, 0x18, 0x11, 0x66, 0x28, 0x18, 0x15,
+ 0x83, 0x12, 0x6c, 0x20, 0x28, 0x20, 0xad, 0x0b,
+ 0x5f, 0x28, 0x08, 0x00, 0xa7, 0x13, 0x03, 0x18,
+ 0xa7, 0x17, 0xa7, 0x0d, 0xa7, 0x1f, 0x08, 0x00,
+ 0x09, 0x30, 0xa7, 0x06, 0x08, 0x00, 0x07, 0x30,
+ 0xa9, 0x00, 0x03, 0x10, 0xa7, 0x0d, 0xa7, 0x1f,
+ 0x7d, 0x28, 0x09, 0x30, 0xa7, 0x06, 0xa9, 0x0b,
+ 0x77, 0x28, 0x03, 0x14, 0xa7, 0x0d, 0x08, 0x00,
+ 0x64, 0x30, 0xab, 0x00, 0xb0, 0x01, 0xa7, 0x01,
+ 0xab, 0x0b, 0x89, 0x28, 0x03, 0x29, 0x06, 0x21,
+ 0x03, 0x18, 0x86, 0x28, 0x23, 0x1a, 0xc7, 0x28,
+ 0x01, 0x30, 0xa4, 0x22, 0x03, 0x10, 0x14, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0xa3, 0x18, 0xa9, 0x28, 0x75, 0x20,
+ 0x27, 0x08, 0xa8, 0x00, 0x13, 0x21, 0x9b, 0x22,
+ 0x28, 0x08, 0xa3, 0x19, 0xa7, 0x28, 0xa4, 0x22,
+ 0x2d, 0x29, 0x9b, 0x22, 0x2d, 0x29, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x75, 0x20, 0x27, 0x08, 0xa8, 0x00,
+ 0x13, 0x21, 0x9b, 0x22, 0x28, 0x08, 0xa4, 0x22,
+ 0x2d, 0x29, 0x50, 0x30, 0x84, 0x00, 0x14, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0xa3, 0x1c, 0xfb, 0x28,
+ 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x75, 0x20, 0x27, 0x08, 0xa8, 0x00,
+ 0x13, 0x21, 0x80, 0x00, 0x24, 0x18, 0xac, 0x29,
+ 0xbb, 0x29, 0x04, 0x30, 0xa4, 0x22, 0x17, 0x28,
+ 0x23, 0x1a, 0xd1, 0x21, 0x83, 0x16, 0x10, 0x19,
+ 0x0f, 0x29, 0x83, 0x12, 0x28, 0x20, 0x03, 0x10,
+ 0x08, 0x00, 0x83, 0x12, 0x28, 0x20, 0x03, 0x14,
+ 0x08, 0x00, 0x06, 0x21, 0x6c, 0x20, 0xb0, 0x0d,
+ 0x06, 0x21, 0x6c, 0x20, 0xb0, 0x0d, 0x06, 0x21,
+ 0x6c, 0x20, 0xb0, 0x0d, 0x06, 0x21, 0x6c, 0x20,
+ 0xb0, 0x0d, 0x06, 0x21, 0x6c, 0x20, 0xb0, 0x0d,
+ 0x06, 0x21, 0x6c, 0x20, 0xb0, 0x0d, 0x06, 0x21,
+ 0x6c, 0x20, 0xb0, 0x0d, 0x06, 0x21, 0x6c, 0x20,
+ 0xb0, 0x0d, 0x30, 0x08, 0x08, 0x00, 0x23, 0x1a,
+ 0xac, 0x29, 0xa3, 0x19, 0x35, 0x29, 0x28, 0x20,
+ 0x28, 0x20, 0x28, 0x20, 0x17, 0x28, 0x28, 0x20,
+ 0x28, 0x20, 0xff, 0x30, 0x01, 0x30, 0x83, 0x16,
+ 0x18, 0x14, 0x19, 0x10, 0x83, 0x12, 0x28, 0x20,
+ 0x83, 0x16, 0x18, 0x10, 0x83, 0x12, 0x28, 0x20,
+ 0x34, 0x08, 0xb6, 0x00, 0x35, 0x08, 0xb7, 0x00,
+ 0x93, 0x22, 0xb8, 0x00, 0x08, 0x30, 0xba, 0x00,
+ 0xb8, 0x1b, 0x51, 0x29, 0x83, 0x16, 0x18, 0x10,
+ 0x83, 0x12, 0x00, 0x30, 0x55, 0x29, 0x83, 0x16,
+ 0x18, 0x14, 0x83, 0x12, 0x01, 0x30, 0x6c, 0x22,
+ 0x28, 0x20, 0xb8, 0x0d, 0xba, 0x0b, 0x4a, 0x29,
+ 0x5b, 0x29, 0xb6, 0x0b, 0x46, 0x29, 0xb7, 0x0b,
+ 0x46, 0x29, 0xa3, 0x1a, 0x67, 0x29, 0x10, 0x30,
+ 0xc4, 0x00, 0x03, 0x01, 0x6c, 0x22, 0xc4, 0x0b,
+ 0x63, 0x29, 0x10, 0x30, 0xc4, 0x00, 0xc5, 0x01,
+ 0xbb, 0x0d, 0xbc, 0x0d, 0x03, 0x18, 0x72, 0x29,
+ 0x83, 0x16, 0x18, 0x10, 0x83, 0x12, 0x75, 0x29,
+ 0x83, 0x16, 0x18, 0x14, 0x83, 0x12, 0x28, 0x20,
+ 0xc4, 0x0b, 0x69, 0x29, 0x83, 0x16, 0x18, 0x14,
+ 0x83, 0x12, 0x28, 0x20, 0x83, 0x16, 0x19, 0x14,
+ 0x83, 0x12, 0x28, 0x20, 0x28, 0x20, 0xc6, 0x01,
+ 0x83, 0x16, 0x10, 0x1c, 0x87, 0x29, 0x83, 0x12,
+ 0xc6, 0x15, 0x83, 0x12, 0x28, 0x20, 0x83, 0x16,
+ 0x10, 0x1c, 0x8e, 0x29, 0x83, 0x12, 0x46, 0x15,
+ 0x83, 0x12, 0x28, 0x20, 0x83, 0x16, 0x10, 0x1c,
+ 0x95, 0x29, 0x83, 0x12, 0xc6, 0x14, 0x83, 0x12,
+ 0x28, 0x20, 0x83, 0x16, 0x10, 0x1c, 0x9c, 0x29,
+ 0x83, 0x12, 0x46, 0x14, 0x83, 0x12, 0x46, 0x08,
+ 0xa4, 0x22, 0x28, 0x20, 0x28, 0x20, 0x28, 0x20,
+ 0x83, 0x12, 0x28, 0x20, 0x83, 0x16, 0x10, 0x1c,
+ 0xa2, 0x29, 0x83, 0x12, 0x28, 0x20, 0x28, 0x20,
+ 0x28, 0x20, 0x17, 0x28, 0x24, 0x18, 0xb1, 0x29,
+ 0xd1, 0x21, 0x28, 0x20, 0xac, 0x29, 0xd1, 0x21,
+ 0x28, 0x20, 0x24, 0x18, 0xb1, 0x29, 0x24, 0x1d,
+ 0xbb, 0x29, 0x28, 0x20, 0x28, 0x20, 0x28, 0x20,
+ 0x17, 0x28, 0x24, 0x15, 0x01, 0x30, 0xa4, 0x22,
+ 0x50, 0x30, 0x84, 0x00, 0x06, 0x30, 0xa3, 0x18,
+ 0x11, 0x30, 0xcf, 0x00, 0x00, 0x08, 0x9b, 0x22,
+ 0x84, 0x0a, 0xcf, 0x0b, 0xc4, 0x29, 0x28, 0x08,
+ 0xa4, 0x22, 0x23, 0x1a, 0xac, 0x29, 0x28, 0x20,
+ 0x28, 0x20, 0x28, 0x20, 0x17, 0x28, 0x24, 0x18,
+ 0xe2, 0x29, 0x83, 0x16, 0x10, 0x18, 0xe0, 0x29,
+ 0x83, 0x12, 0x24, 0x14, 0xa4, 0x14, 0x02, 0x30,
+ 0xa4, 0x22, 0x34, 0x08, 0xb6, 0x00, 0x35, 0x08,
+ 0xb7, 0x00, 0x08, 0x00, 0x83, 0x12, 0x08, 0x00,
+ 0x83, 0x16, 0x03, 0x10, 0x10, 0x18, 0x03, 0x14,
+ 0xa1, 0x0d, 0x03, 0x01, 0x10, 0x18, 0x01, 0x30,
+ 0x83, 0x12, 0xa3, 0x1a, 0xef, 0x29, 0xa4, 0x18,
+ 0x6c, 0x22, 0xb9, 0x0a, 0xb9, 0x1d, 0x08, 0x00,
+ 0x83, 0x16, 0x21, 0x08, 0x83, 0x12, 0x9b, 0x22,
+ 0xb9, 0x01, 0x83, 0x16, 0xa1, 0x01, 0x83, 0x12,
+ 0xfb, 0x29, 0xb6, 0x0b, 0x08, 0x00, 0xb7, 0x0b,
+ 0x08, 0x00, 0xa4, 0x1c, 0x08, 0x2a, 0xa4, 0x10,
+ 0x08, 0x30, 0x02, 0x30, 0xb6, 0x00, 0x01, 0x30,
+ 0xb7, 0x00, 0x08, 0x00, 0x24, 0x10, 0x23, 0x12,
+ 0xa3, 0x1a, 0x69, 0x2a, 0x10, 0x30, 0xc4, 0x00,
+ 0x03, 0x01, 0x6c, 0x22, 0xc4, 0x0b, 0x0e, 0x2a,
+ 0x3c, 0x08, 0x9b, 0x22, 0x3b, 0x08, 0xa4, 0x22,
+ 0x08, 0x00, 0x04, 0x08, 0xe7, 0x00, 0x47, 0x30,
+ 0x84, 0x00, 0x08, 0x30, 0xc4, 0x00, 0x80, 0x01,
+ 0x84, 0x0a, 0xc4, 0x0b, 0x1d, 0x2a, 0x47, 0x30,
+ 0x84, 0x00, 0x04, 0x30, 0xc4, 0x00, 0xc2, 0x0d,
+ 0x03, 0x18, 0x80, 0x17, 0xc2, 0x0d, 0x03, 0x18,
+ 0x80, 0x15, 0xc0, 0x0d, 0x03, 0x18, 0x00, 0x17,
+ 0xc0, 0x0d, 0x03, 0x18, 0x00, 0x15, 0xbe, 0x0d,
+ 0x03, 0x18, 0x80, 0x16, 0xbe, 0x0d, 0x03, 0x18,
+ 0x80, 0x14, 0xbc, 0x0d, 0x03, 0x18, 0x00, 0x16,
+ 0xbc, 0x0d, 0x03, 0x18, 0x00, 0x14, 0x84, 0x0a,
+ 0xc4, 0x0b, 0x25, 0x2a, 0x04, 0x30, 0xc4, 0x00,
+ 0xc1, 0x0d, 0x03, 0x18, 0x80, 0x17, 0xc1, 0x0d,
+ 0x03, 0x18, 0x80, 0x15, 0xbf, 0x0d, 0x03, 0x18,
+ 0x00, 0x17, 0xbf, 0x0d, 0x03, 0x18, 0x00, 0x15,
+ 0xbd, 0x0d, 0x03, 0x18, 0x80, 0x16, 0xbd, 0x0d,
+ 0x03, 0x18, 0x80, 0x14, 0xbb, 0x0d, 0x03, 0x18,
+ 0x00, 0x16, 0xbb, 0x0d, 0x03, 0x18, 0x00, 0x14,
+ 0x84, 0x0a, 0xc4, 0x0b, 0x42, 0x2a, 0x47, 0x30,
+ 0x84, 0x00, 0x07, 0x30, 0xc4, 0x00, 0x00, 0x08,
+ 0x9b, 0x22, 0x84, 0x0a, 0xc4, 0x0b, 0x61, 0x2a,
+ 0x00, 0x08, 0xa4, 0x22, 0x08, 0x00, 0x03, 0x01,
+ 0xa4, 0x22, 0x08, 0x00, 0xc3, 0x00, 0xc3, 0x0c,
+ 0xbb, 0x0d, 0xbc, 0x0d, 0x03, 0x1c, 0x76, 0x2a,
+ 0x21, 0x30, 0xbb, 0x06, 0x10, 0x30, 0xbc, 0x06,
+ 0x08, 0x00, 0xc3, 0x0c, 0xbd, 0x0d, 0xbe, 0x0d,
+ 0x03, 0x1c, 0x80, 0x2a, 0x21, 0x30, 0xbd, 0x06,
+ 0x10, 0x30, 0xbe, 0x06, 0xc3, 0x0c, 0xbf, 0x0d,
+ 0xc0, 0x0d, 0x03, 0x1c, 0x89, 0x2a, 0x21, 0x30,
+ 0xbf, 0x06, 0x10, 0x30, 0xc0, 0x06, 0xc3, 0x0c,
+ 0xc1, 0x0d, 0xc2, 0x0d, 0x03, 0x1c, 0x08, 0x00,
+ 0x21, 0x30, 0xc1, 0x06, 0x10, 0x30, 0xc2, 0x06,
+ 0x08, 0x00, 0x8c, 0x19, 0x93, 0x2a, 0x8b, 0x13,
+ 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00, 0x8b, 0x17,
+ 0x08, 0x00, 0x8c, 0x1b, 0x9b, 0x2a, 0x8b, 0x13,
+ 0x00, 0x00, 0x8e, 0x01, 0x8d, 0x00, 0x00, 0x00,
+ 0x8b, 0x17, 0x08, 0x00, 0x8c, 0x1b, 0xa4, 0x2a,
+ 0x8b, 0x13, 0x00, 0x00, 0x8e, 0x0a, 0x8d, 0x00,
+ 0x00, 0x00, 0x8b, 0x17, 0x08, 0x00, 0x83, 0x12,
+ 0x20, 0x30, 0x84, 0x00, 0x80, 0x01, 0x84, 0x0a,
+ 0x04, 0x08, 0x80, 0x3c, 0x03, 0x1d, 0xb0, 0x2a,
+ 0xa0, 0x30, 0x84, 0x00, 0x80, 0x01, 0x84, 0x0a,
+ 0x04, 0x08, 0xff, 0x3c, 0x03, 0x1d, 0xb8, 0x2a,
+ 0x08, 0x00
+};
+
diff --git a/drivers/fims/sdio/fim_sdio1.h b/drivers/fims/sdio/fim_sdio1.h
new file mode 100644
index 000000000000..7f73a0c5fc3e
--- /dev/null
+++ b/drivers/fims/sdio/fim_sdio1.h
@@ -0,0 +1,225 @@
+
+/*
+ * Automatic generated header file with the firmware code for the FIM
+ * Input binary : fim_firmware.bin
+ * Output header : fim_sdio.h
+ * Structure : fim_sdio_firmware1
+ */
+
+static const unsigned char fim_sdio_firmware1[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2f, 0x03, 0x00, 0x00, 0x0e, 0x28, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x03, 0x08,
+ 0x83, 0x12, 0xa1, 0x00, 0x8b, 0x10, 0x21, 0x08,
+ 0x83, 0x00, 0xa0, 0x0e, 0x20, 0x0e, 0x09, 0x00,
+ 0x1d, 0x23, 0x83, 0x12, 0x9f, 0x01, 0x9f, 0x15,
+ 0x83, 0x16, 0xff, 0x30, 0x99, 0x00, 0x83, 0x12,
+ 0x83, 0x12, 0x15, 0x1c, 0x16, 0x28, 0x01, 0x30,
+ 0xa6, 0x00, 0x1c, 0x28, 0xa4, 0x01, 0xbb, 0x01,
+ 0xbc, 0x01, 0xbd, 0x01, 0xbe, 0x01, 0xbf, 0x01,
+ 0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01, 0x83, 0x16,
+ 0xa1, 0x01, 0x83, 0x12, 0xb9, 0x01, 0x3d, 0x20,
+ 0x8c, 0x1d, 0x54, 0x28, 0x2e, 0x20, 0x1c, 0x28,
+ 0x83, 0x16, 0x1a, 0x15, 0x83, 0x12, 0x10, 0x08,
+ 0xac, 0x00, 0xac, 0x0b, 0x33, 0x28, 0x83, 0x16,
+ 0x1a, 0x11, 0x83, 0x12, 0x10, 0x08, 0xac, 0x00,
+ 0xac, 0x0b, 0x3a, 0x28, 0x08, 0x00, 0x22, 0x1c,
+ 0x48, 0x28, 0x83, 0x16, 0x89, 0x1f, 0x46, 0x28,
+ 0x83, 0x12, 0x89, 0x01, 0x22, 0x10, 0x08, 0x00,
+ 0x83, 0x12, 0x08, 0x00, 0x11, 0x1c, 0x08, 0x00,
+ 0x83, 0x16, 0x90, 0x1e, 0x4f, 0x28, 0x83, 0x12,
+ 0x08, 0x00, 0x83, 0x12, 0x22, 0x14, 0x01, 0x30,
+ 0x89, 0x00, 0x08, 0x00, 0x9f, 0x11, 0xa7, 0x01,
+ 0x0f, 0x23, 0xa3, 0x00, 0x0f, 0x23, 0xb5, 0x00,
+ 0x0f, 0x23, 0xb4, 0x00, 0xff, 0x39, 0x03, 0x19,
+ 0x62, 0x28, 0x35, 0x08, 0x01, 0x3e, 0xb5, 0x00,
+ 0x0f, 0x23, 0x75, 0x20, 0x0f, 0x23, 0x75, 0x20,
+ 0x0f, 0x23, 0x75, 0x20, 0x0f, 0x23, 0x75, 0x20,
+ 0x0f, 0x23, 0x75, 0x20, 0x8e, 0x20, 0x27, 0x08,
+ 0x75, 0x20, 0x9f, 0x15, 0x23, 0x18, 0x9b, 0x28,
+ 0xa3, 0x18, 0x9b, 0x28, 0x46, 0x29, 0xaa, 0x00,
+ 0x08, 0x30, 0xad, 0x00, 0xaa, 0x0d, 0x83, 0x16,
+ 0x03, 0x18, 0x7e, 0x28, 0x9a, 0x11, 0x7f, 0x28,
+ 0x9a, 0x15, 0x83, 0x12, 0x85, 0x20, 0x2e, 0x20,
+ 0xad, 0x0b, 0x78, 0x28, 0x08, 0x00, 0xa7, 0x13,
+ 0x03, 0x18, 0xa7, 0x17, 0xa7, 0x0d, 0xa7, 0x1f,
+ 0x08, 0x00, 0x09, 0x30, 0xa7, 0x06, 0x08, 0x00,
+ 0x07, 0x30, 0xa9, 0x00, 0x03, 0x10, 0xa7, 0x0d,
+ 0xa7, 0x1f, 0x96, 0x28, 0x09, 0x30, 0xa7, 0x06,
+ 0xa9, 0x0b, 0x90, 0x28, 0x03, 0x14, 0xa7, 0x0d,
+ 0x08, 0x00, 0x64, 0x30, 0xab, 0x00, 0xb0, 0x01,
+ 0xa7, 0x01, 0xab, 0x0b, 0xa2, 0x28, 0x1c, 0x29,
+ 0x1f, 0x21, 0x03, 0x18, 0x9f, 0x28, 0x23, 0x1a,
+ 0xe0, 0x28, 0x01, 0x30, 0x18, 0x23, 0x03, 0x10,
+ 0x2d, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0xa3, 0x18, 0xc2, 0x28,
+ 0x8e, 0x20, 0x27, 0x08, 0xa8, 0x00, 0x2c, 0x21,
+ 0x13, 0x23, 0x28, 0x08, 0xa3, 0x19, 0xc0, 0x28,
+ 0x18, 0x23, 0x46, 0x29, 0x13, 0x23, 0x46, 0x29,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x2c, 0x21, 0x13, 0x23,
+ 0x2c, 0x21, 0x13, 0x23, 0x8e, 0x20, 0x27, 0x08,
+ 0xa8, 0x00, 0x2c, 0x21, 0x13, 0x23, 0x28, 0x08,
+ 0x18, 0x23, 0x46, 0x29, 0x50, 0x30, 0x84, 0x00,
+ 0x2d, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a, 0xa3, 0x1c,
+ 0x14, 0x29, 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x2c, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x2c, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x8e, 0x20, 0x27, 0x08,
+ 0xa8, 0x00, 0x2c, 0x21, 0x80, 0x00, 0x24, 0x18,
+ 0xfc, 0x29, 0x0b, 0x2a, 0x04, 0x30, 0x18, 0x23,
+ 0x1c, 0x28, 0x23, 0x1a, 0x21, 0x22, 0x83, 0x16,
+ 0x92, 0x19, 0x28, 0x29, 0x83, 0x12, 0x2e, 0x20,
+ 0x03, 0x10, 0x08, 0x00, 0x83, 0x12, 0x2e, 0x20,
+ 0x03, 0x14, 0x08, 0x00, 0x1f, 0x21, 0x85, 0x20,
+ 0xb0, 0x0d, 0x1f, 0x21, 0x85, 0x20, 0xb0, 0x0d,
+ 0x1f, 0x21, 0x85, 0x20, 0xb0, 0x0d, 0x1f, 0x21,
+ 0x85, 0x20, 0xb0, 0x0d, 0x1f, 0x21, 0x85, 0x20,
+ 0xb0, 0x0d, 0x1f, 0x21, 0x85, 0x20, 0xb0, 0x0d,
+ 0x1f, 0x21, 0x85, 0x20, 0xb0, 0x0d, 0x1f, 0x21,
+ 0x85, 0x20, 0xb0, 0x0d, 0x30, 0x08, 0x08, 0x00,
+ 0x23, 0x1a, 0xfc, 0x29, 0xa3, 0x19, 0x4f, 0x29,
+ 0x2e, 0x20, 0x2e, 0x20, 0x2e, 0x20, 0x3d, 0x20,
+ 0x1c, 0x28, 0x2e, 0x20, 0x2e, 0x20, 0xff, 0x30,
+ 0x23, 0x1d, 0x11, 0x30, 0x83, 0x16, 0x98, 0x00,
+ 0xff, 0x3a, 0x99, 0x00, 0x83, 0x12, 0x2e, 0x20,
+ 0x83, 0x16, 0x98, 0x01, 0x83, 0x12, 0x2e, 0x20,
+ 0x34, 0x08, 0xb6, 0x00, 0x35, 0x08, 0xb7, 0x00,
+ 0x0f, 0x23, 0xb8, 0x00, 0x23, 0x19, 0x79, 0x29,
+ 0x08, 0x30, 0xba, 0x00, 0xb8, 0x1b, 0x6f, 0x29,
+ 0x83, 0x16, 0x18, 0x12, 0x83, 0x12, 0x00, 0x30,
+ 0x73, 0x29, 0x83, 0x16, 0x18, 0x16, 0x83, 0x12,
+ 0x01, 0x30, 0xe7, 0x22, 0x2e, 0x20, 0xb8, 0x0d,
+ 0xba, 0x0b, 0x68, 0x29, 0x9e, 0x29, 0x26, 0x18,
+ 0x8c, 0x29, 0xb8, 0x0e, 0x38, 0x08, 0x83, 0x16,
+ 0x98, 0x00, 0x83, 0x12, 0xa3, 0x1e, 0xe7, 0x22,
+ 0x2e, 0x20, 0xb8, 0x0e, 0x38, 0x08, 0x83, 0x16,
+ 0x98, 0x00, 0x83, 0x12, 0xa3, 0x1e, 0xe7, 0x22,
+ 0x2e, 0x20, 0x9e, 0x29, 0x38, 0x08, 0x83, 0x16,
+ 0x98, 0x00, 0x83, 0x12, 0xb8, 0x0e, 0x38, 0x08,
+ 0xa3, 0x1e, 0xe7, 0x22, 0x2e, 0x20, 0x38, 0x08,
+ 0x83, 0x16, 0x98, 0x00, 0x83, 0x12, 0xb8, 0x0e,
+ 0x38, 0x08, 0xa3, 0x1e, 0xe7, 0x22, 0x2e, 0x20,
+ 0xb6, 0x0b, 0x62, 0x29, 0xb7, 0x0b, 0x62, 0x29,
+ 0xa3, 0x1a, 0xaa, 0x29, 0x10, 0x30, 0xc4, 0x00,
+ 0x03, 0x01, 0xe7, 0x22, 0xc4, 0x0b, 0xa6, 0x29,
+ 0x10, 0x30, 0xc4, 0x00, 0xc5, 0x01, 0xbb, 0x0d,
+ 0xbc, 0x0d, 0x03, 0x18, 0x45, 0x14, 0xbd, 0x0d,
+ 0xbe, 0x0d, 0x03, 0x18, 0xc5, 0x14, 0xbf, 0x0d,
+ 0xc0, 0x0d, 0x03, 0x18, 0x45, 0x15, 0xc1, 0x0d,
+ 0xc2, 0x0d, 0x03, 0x18, 0xc5, 0x15, 0x26, 0x18,
+ 0xc5, 0x0e, 0x45, 0x08, 0x83, 0x16, 0x98, 0x00,
+ 0x83, 0x12, 0x2e, 0x20, 0xc4, 0x0b, 0xac, 0x29,
+ 0x83, 0x16, 0xff, 0x30, 0x98, 0x00, 0x83, 0x12,
+ 0x2e, 0x20, 0x83, 0x16, 0xff, 0x30, 0x99, 0x00,
+ 0x83, 0x12, 0x2e, 0x20, 0x2e, 0x20, 0xc6, 0x01,
+ 0x83, 0x16, 0x10, 0x1e, 0xd7, 0x29, 0x83, 0x12,
+ 0xc6, 0x15, 0x83, 0x12, 0x2e, 0x20, 0x83, 0x16,
+ 0x10, 0x1e, 0xde, 0x29, 0x83, 0x12, 0x46, 0x15,
+ 0x83, 0x12, 0x2e, 0x20, 0x83, 0x16, 0x10, 0x1e,
+ 0xe5, 0x29, 0x83, 0x12, 0xc6, 0x14, 0x83, 0x12,
+ 0x2e, 0x20, 0x83, 0x16, 0x10, 0x1e, 0xec, 0x29,
+ 0x83, 0x12, 0x46, 0x14, 0x83, 0x12, 0x46, 0x08,
+ 0x18, 0x23, 0x2e, 0x20, 0x2e, 0x20, 0x2e, 0x20,
+ 0x83, 0x12, 0x2e, 0x20, 0x83, 0x16, 0x10, 0x1e,
+ 0xf2, 0x29, 0x83, 0x12, 0x2e, 0x20, 0x2e, 0x20,
+ 0x2e, 0x20, 0x1c, 0x28, 0x24, 0x18, 0x01, 0x2a,
+ 0x21, 0x22, 0x2e, 0x20, 0xfc, 0x29, 0x21, 0x22,
+ 0x2e, 0x20, 0x24, 0x18, 0x01, 0x2a, 0x24, 0x1d,
+ 0x0b, 0x2a, 0x2e, 0x20, 0x2e, 0x20, 0x2e, 0x20,
+ 0x1c, 0x28, 0x24, 0x15, 0x01, 0x30, 0x18, 0x23,
+ 0x50, 0x30, 0x84, 0x00, 0x06, 0x30, 0xa3, 0x18,
+ 0x11, 0x30, 0xcf, 0x00, 0x00, 0x08, 0x13, 0x23,
+ 0x84, 0x0a, 0xcf, 0x0b, 0x14, 0x2a, 0x28, 0x08,
+ 0x18, 0x23, 0x23, 0x1a, 0xfc, 0x29, 0x2e, 0x20,
+ 0x2e, 0x20, 0x2e, 0x20, 0x1c, 0x28, 0x24, 0x18,
+ 0x32, 0x2a, 0x83, 0x16, 0x10, 0x1a, 0x30, 0x2a,
+ 0x83, 0x12, 0x24, 0x14, 0xa4, 0x14, 0x02, 0x30,
+ 0x18, 0x23, 0x34, 0x08, 0xb6, 0x00, 0x35, 0x08,
+ 0xb7, 0x00, 0x08, 0x00, 0x83, 0x12, 0x08, 0x00,
+ 0x23, 0x19, 0x4d, 0x2a, 0x83, 0x16, 0x03, 0x10,
+ 0x10, 0x1a, 0x03, 0x14, 0xa1, 0x0d, 0x03, 0x01,
+ 0x10, 0x1a, 0x01, 0x30, 0x83, 0x12, 0xa3, 0x1a,
+ 0x41, 0x2a, 0xa4, 0x18, 0xe7, 0x22, 0xb9, 0x0a,
+ 0xb9, 0x1d, 0x08, 0x00, 0x83, 0x16, 0x21, 0x08,
+ 0x83, 0x12, 0x13, 0x23, 0xb9, 0x01, 0x83, 0x16,
+ 0xa1, 0x01, 0x83, 0x12, 0x73, 0x2a, 0x39, 0x19,
+ 0x62, 0x2a, 0x83, 0x16, 0xa1, 0x01, 0x90, 0x1b,
+ 0xa1, 0x15, 0x10, 0x1b, 0x21, 0x15, 0x90, 0x1a,
+ 0xa1, 0x14, 0x10, 0x1a, 0x21, 0x14, 0x21, 0x08,
+ 0xa1, 0x0e, 0x83, 0x12, 0x39, 0x15, 0xa3, 0x1a,
+ 0x08, 0x00, 0xa4, 0x18, 0xe7, 0x22, 0x08, 0x00,
+ 0x83, 0x16, 0x90, 0x1b, 0xa1, 0x15, 0x10, 0x1b,
+ 0x21, 0x15, 0x90, 0x1a, 0xa1, 0x14, 0x10, 0x1a,
+ 0x21, 0x14, 0x21, 0x08, 0x83, 0x12, 0x13, 0x23,
+ 0xa3, 0x1a, 0x73, 0x2a, 0xa4, 0x18, 0xe7, 0x22,
+ 0xb9, 0x01, 0xb6, 0x0b, 0x08, 0x00, 0xb7, 0x0b,
+ 0x08, 0x00, 0xa4, 0x1c, 0x81, 0x2a, 0xa4, 0x10,
+ 0x08, 0x30, 0x23, 0x1d, 0x02, 0x30, 0xb6, 0x00,
+ 0x01, 0x30, 0xb7, 0x00, 0x08, 0x00, 0x24, 0x10,
+ 0x23, 0x12, 0xa3, 0x1a, 0xe4, 0x2a, 0x10, 0x30,
+ 0xc4, 0x00, 0x03, 0x01, 0xe7, 0x22, 0xc4, 0x0b,
+ 0x87, 0x2a, 0x23, 0x19, 0x92, 0x2a, 0x3c, 0x08,
+ 0x13, 0x23, 0x3b, 0x08, 0x18, 0x23, 0x08, 0x00,
+ 0x04, 0x08, 0xe7, 0x00, 0x47, 0x30, 0x84, 0x00,
+ 0x08, 0x30, 0xc4, 0x00, 0x80, 0x01, 0x84, 0x0a,
+ 0xc4, 0x0b, 0x98, 0x2a, 0x47, 0x30, 0x84, 0x00,
+ 0x04, 0x30, 0xc4, 0x00, 0xc2, 0x0d, 0x03, 0x18,
+ 0x80, 0x17, 0xc2, 0x0d, 0x03, 0x18, 0x80, 0x15,
+ 0xc0, 0x0d, 0x03, 0x18, 0x00, 0x17, 0xc0, 0x0d,
+ 0x03, 0x18, 0x00, 0x15, 0xbe, 0x0d, 0x03, 0x18,
+ 0x80, 0x16, 0xbe, 0x0d, 0x03, 0x18, 0x80, 0x14,
+ 0xbc, 0x0d, 0x03, 0x18, 0x00, 0x16, 0xbc, 0x0d,
+ 0x03, 0x18, 0x00, 0x14, 0x84, 0x0a, 0xc4, 0x0b,
+ 0xa0, 0x2a, 0x04, 0x30, 0xc4, 0x00, 0xc1, 0x0d,
+ 0x03, 0x18, 0x80, 0x17, 0xc1, 0x0d, 0x03, 0x18,
+ 0x80, 0x15, 0xbf, 0x0d, 0x03, 0x18, 0x00, 0x17,
+ 0xbf, 0x0d, 0x03, 0x18, 0x00, 0x15, 0xbd, 0x0d,
+ 0x03, 0x18, 0x80, 0x16, 0xbd, 0x0d, 0x03, 0x18,
+ 0x80, 0x14, 0xbb, 0x0d, 0x03, 0x18, 0x00, 0x16,
+ 0xbb, 0x0d, 0x03, 0x18, 0x00, 0x14, 0x84, 0x0a,
+ 0xc4, 0x0b, 0xbd, 0x2a, 0x47, 0x30, 0x84, 0x00,
+ 0x07, 0x30, 0xc4, 0x00, 0x00, 0x08, 0x13, 0x23,
+ 0x84, 0x0a, 0xc4, 0x0b, 0xdc, 0x2a, 0x00, 0x08,
+ 0x18, 0x23, 0x08, 0x00, 0x03, 0x01, 0x18, 0x23,
+ 0x08, 0x00, 0xc3, 0x00, 0xc3, 0x0c, 0xbb, 0x0d,
+ 0xbc, 0x0d, 0x03, 0x1c, 0xf1, 0x2a, 0x21, 0x30,
+ 0xbb, 0x06, 0x10, 0x30, 0xbc, 0x06, 0x23, 0x1d,
+ 0x08, 0x00, 0xc3, 0x0c, 0xbd, 0x0d, 0xbe, 0x0d,
+ 0x03, 0x1c, 0xfc, 0x2a, 0x21, 0x30, 0xbd, 0x06,
+ 0x10, 0x30, 0xbe, 0x06, 0xc3, 0x0c, 0xbf, 0x0d,
+ 0xc0, 0x0d, 0x03, 0x1c, 0x05, 0x2b, 0x21, 0x30,
+ 0xbf, 0x06, 0x10, 0x30, 0xc0, 0x06, 0xc3, 0x0c,
+ 0xc1, 0x0d, 0xc2, 0x0d, 0x03, 0x1c, 0x08, 0x00,
+ 0x21, 0x30, 0xc1, 0x06, 0x10, 0x30, 0xc2, 0x06,
+ 0x08, 0x00, 0x8c, 0x19, 0x0f, 0x2b, 0x0f, 0x08,
+ 0x08, 0x00, 0x8c, 0x1b, 0x13, 0x2b, 0x8e, 0x01,
+ 0x8d, 0x00, 0x08, 0x00, 0x8c, 0x1b, 0x18, 0x2b,
+ 0x8e, 0x0a, 0x8d, 0x00, 0x08, 0x00, 0x83, 0x12,
+ 0x20, 0x30, 0x84, 0x00, 0x80, 0x01, 0x84, 0x0a,
+ 0x04, 0x08, 0x80, 0x3c, 0x03, 0x1d, 0x20, 0x2b,
+ 0xa0, 0x30, 0x84, 0x00, 0x80, 0x01, 0x84, 0x0a,
+ 0x04, 0x08, 0xff, 0x3c, 0x03, 0x1d, 0x28, 0x2b,
+ 0x08, 0x00
+};
+
diff --git a/drivers/fims/sdio/fim_sdio1_9210.h b/drivers/fims/sdio/fim_sdio1_9210.h
new file mode 100644
index 000000000000..cfb920660d69
--- /dev/null
+++ b/drivers/fims/sdio/fim_sdio1_9210.h
@@ -0,0 +1,197 @@
+
+/*
+ * Automatic generated header file with the firmware code for the FIM
+ * Input binary : fim_firmware.bin
+ * Output header : fim_sdio_9210.h
+ * Structure : fim_sdio_firmware1
+ */
+
+static const unsigned char fim_sdio_firmware1[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xbf, 0x02, 0x00, 0x00, 0x0e, 0x28, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x03, 0x08,
+ 0x83, 0x12, 0xa1, 0x00, 0x8b, 0x10, 0x21, 0x08,
+ 0x83, 0x00, 0xa0, 0x0e, 0x20, 0x0e, 0x09, 0x00,
+ 0x01, 0x30, 0x98, 0x00, 0xad, 0x22, 0x83, 0x16,
+ 0x99, 0x01, 0x19, 0x15, 0x19, 0x14, 0x83, 0x12,
+ 0x17, 0x28, 0xa4, 0x01, 0xbb, 0x01, 0xbc, 0x01,
+ 0xbd, 0x01, 0xbe, 0x01, 0xbf, 0x01, 0xc0, 0x01,
+ 0xc1, 0x01, 0xc2, 0x01, 0x83, 0x16, 0xa1, 0x01,
+ 0x83, 0x12, 0xb9, 0x01, 0x8c, 0x1d, 0x37, 0x28,
+ 0x28, 0x20, 0x17, 0x28, 0x83, 0x16, 0x98, 0x14,
+ 0x83, 0x12, 0x10, 0x08, 0xac, 0x00, 0xac, 0x0b,
+ 0x2d, 0x28, 0x83, 0x16, 0x98, 0x10, 0x83, 0x12,
+ 0x10, 0x08, 0xac, 0x00, 0xac, 0x0b, 0x34, 0x28,
+ 0x08, 0x00, 0x83, 0x16, 0x19, 0x11, 0x83, 0x12,
+ 0xa7, 0x01, 0x93, 0x22, 0xa3, 0x00, 0x93, 0x22,
+ 0xb5, 0x00, 0x93, 0x22, 0xb4, 0x00, 0xff, 0x39,
+ 0x03, 0x19, 0x47, 0x28, 0x35, 0x08, 0x01, 0x3e,
+ 0xb5, 0x00, 0x93, 0x22, 0x5c, 0x20, 0x93, 0x22,
+ 0x5c, 0x20, 0x93, 0x22, 0x5c, 0x20, 0x93, 0x22,
+ 0x5c, 0x20, 0x93, 0x22, 0x5c, 0x20, 0x75, 0x20,
+ 0x27, 0x08, 0x5c, 0x20, 0x83, 0x16, 0x19, 0x15,
+ 0x83, 0x12, 0x23, 0x18, 0x82, 0x28, 0xa3, 0x18,
+ 0x82, 0x28, 0x2d, 0x29, 0xaa, 0x00, 0x08, 0x30,
+ 0xad, 0x00, 0xaa, 0x0d, 0x83, 0x16, 0x03, 0x18,
+ 0x65, 0x28, 0x18, 0x11, 0x66, 0x28, 0x18, 0x15,
+ 0x83, 0x12, 0x6c, 0x20, 0x28, 0x20, 0xad, 0x0b,
+ 0x5f, 0x28, 0x08, 0x00, 0xa7, 0x13, 0x03, 0x18,
+ 0xa7, 0x17, 0xa7, 0x0d, 0xa7, 0x1f, 0x08, 0x00,
+ 0x09, 0x30, 0xa7, 0x06, 0x08, 0x00, 0x07, 0x30,
+ 0xa9, 0x00, 0x03, 0x10, 0xa7, 0x0d, 0xa7, 0x1f,
+ 0x7d, 0x28, 0x09, 0x30, 0xa7, 0x06, 0xa9, 0x0b,
+ 0x77, 0x28, 0x03, 0x14, 0xa7, 0x0d, 0x08, 0x00,
+ 0x64, 0x30, 0xab, 0x00, 0xb0, 0x01, 0xa7, 0x01,
+ 0xab, 0x0b, 0x89, 0x28, 0x03, 0x29, 0x06, 0x21,
+ 0x03, 0x18, 0x86, 0x28, 0x23, 0x1a, 0xc7, 0x28,
+ 0x01, 0x30, 0xa4, 0x22, 0x03, 0x10, 0x14, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0xa3, 0x18, 0xa9, 0x28, 0x75, 0x20,
+ 0x27, 0x08, 0xa8, 0x00, 0x13, 0x21, 0x9b, 0x22,
+ 0x28, 0x08, 0xa3, 0x19, 0xa7, 0x28, 0xa4, 0x22,
+ 0x2d, 0x29, 0x9b, 0x22, 0x2d, 0x29, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x13, 0x21, 0x9b, 0x22, 0x13, 0x21,
+ 0x9b, 0x22, 0x75, 0x20, 0x27, 0x08, 0xa8, 0x00,
+ 0x13, 0x21, 0x9b, 0x22, 0x28, 0x08, 0xa4, 0x22,
+ 0x2d, 0x29, 0x50, 0x30, 0x84, 0x00, 0x14, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0xa3, 0x1c, 0xfb, 0x28,
+ 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a,
+ 0x13, 0x21, 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21,
+ 0x80, 0x00, 0x84, 0x0a, 0x13, 0x21, 0x80, 0x00,
+ 0x84, 0x0a, 0x75, 0x20, 0x27, 0x08, 0xa8, 0x00,
+ 0x13, 0x21, 0x80, 0x00, 0x24, 0x18, 0xac, 0x29,
+ 0xbb, 0x29, 0x04, 0x30, 0xa4, 0x22, 0x17, 0x28,
+ 0x23, 0x1a, 0xd1, 0x21, 0x83, 0x16, 0x10, 0x19,
+ 0x0f, 0x29, 0x83, 0x12, 0x28, 0x20, 0x03, 0x10,
+ 0x08, 0x00, 0x83, 0x12, 0x28, 0x20, 0x03, 0x14,
+ 0x08, 0x00, 0x06, 0x21, 0x6c, 0x20, 0xb0, 0x0d,
+ 0x06, 0x21, 0x6c, 0x20, 0xb0, 0x0d, 0x06, 0x21,
+ 0x6c, 0x20, 0xb0, 0x0d, 0x06, 0x21, 0x6c, 0x20,
+ 0xb0, 0x0d, 0x06, 0x21, 0x6c, 0x20, 0xb0, 0x0d,
+ 0x06, 0x21, 0x6c, 0x20, 0xb0, 0x0d, 0x06, 0x21,
+ 0x6c, 0x20, 0xb0, 0x0d, 0x06, 0x21, 0x6c, 0x20,
+ 0xb0, 0x0d, 0x30, 0x08, 0x08, 0x00, 0x23, 0x1a,
+ 0xac, 0x29, 0xa3, 0x19, 0x35, 0x29, 0x28, 0x20,
+ 0x28, 0x20, 0x28, 0x20, 0x17, 0x28, 0x28, 0x20,
+ 0x28, 0x20, 0xff, 0x30, 0x01, 0x30, 0x83, 0x16,
+ 0x18, 0x14, 0x19, 0x10, 0x83, 0x12, 0x28, 0x20,
+ 0x83, 0x16, 0x18, 0x10, 0x83, 0x12, 0x28, 0x20,
+ 0x34, 0x08, 0xb6, 0x00, 0x35, 0x08, 0xb7, 0x00,
+ 0x93, 0x22, 0xb8, 0x00, 0x08, 0x30, 0xba, 0x00,
+ 0xb8, 0x1b, 0x51, 0x29, 0x83, 0x16, 0x18, 0x10,
+ 0x83, 0x12, 0x00, 0x30, 0x55, 0x29, 0x83, 0x16,
+ 0x18, 0x14, 0x83, 0x12, 0x01, 0x30, 0x6c, 0x22,
+ 0x28, 0x20, 0xb8, 0x0d, 0xba, 0x0b, 0x4a, 0x29,
+ 0x5b, 0x29, 0xb6, 0x0b, 0x46, 0x29, 0xb7, 0x0b,
+ 0x46, 0x29, 0xa3, 0x1a, 0x67, 0x29, 0x10, 0x30,
+ 0xc4, 0x00, 0x03, 0x01, 0x6c, 0x22, 0xc4, 0x0b,
+ 0x63, 0x29, 0x10, 0x30, 0xc4, 0x00, 0xc5, 0x01,
+ 0xbb, 0x0d, 0xbc, 0x0d, 0x03, 0x18, 0x72, 0x29,
+ 0x83, 0x16, 0x18, 0x10, 0x83, 0x12, 0x75, 0x29,
+ 0x83, 0x16, 0x18, 0x14, 0x83, 0x12, 0x28, 0x20,
+ 0xc4, 0x0b, 0x69, 0x29, 0x83, 0x16, 0x18, 0x14,
+ 0x83, 0x12, 0x28, 0x20, 0x83, 0x16, 0x19, 0x14,
+ 0x83, 0x12, 0x28, 0x20, 0x28, 0x20, 0xc6, 0x01,
+ 0x83, 0x16, 0x10, 0x1c, 0x87, 0x29, 0x83, 0x12,
+ 0xc6, 0x15, 0x83, 0x12, 0x28, 0x20, 0x83, 0x16,
+ 0x10, 0x1c, 0x8e, 0x29, 0x83, 0x12, 0x46, 0x15,
+ 0x83, 0x12, 0x28, 0x20, 0x83, 0x16, 0x10, 0x1c,
+ 0x95, 0x29, 0x83, 0x12, 0xc6, 0x14, 0x83, 0x12,
+ 0x28, 0x20, 0x83, 0x16, 0x10, 0x1c, 0x9c, 0x29,
+ 0x83, 0x12, 0x46, 0x14, 0x83, 0x12, 0x46, 0x08,
+ 0xa4, 0x22, 0x28, 0x20, 0x28, 0x20, 0x28, 0x20,
+ 0x83, 0x12, 0x28, 0x20, 0x83, 0x16, 0x10, 0x1c,
+ 0xa2, 0x29, 0x83, 0x12, 0x28, 0x20, 0x28, 0x20,
+ 0x28, 0x20, 0x17, 0x28, 0x24, 0x18, 0xb1, 0x29,
+ 0xd1, 0x21, 0x28, 0x20, 0xac, 0x29, 0xd1, 0x21,
+ 0x28, 0x20, 0x24, 0x18, 0xb1, 0x29, 0x24, 0x1d,
+ 0xbb, 0x29, 0x28, 0x20, 0x28, 0x20, 0x28, 0x20,
+ 0x17, 0x28, 0x24, 0x15, 0x01, 0x30, 0xa4, 0x22,
+ 0x50, 0x30, 0x84, 0x00, 0x06, 0x30, 0xa3, 0x18,
+ 0x11, 0x30, 0xcf, 0x00, 0x00, 0x08, 0x9b, 0x22,
+ 0x84, 0x0a, 0xcf, 0x0b, 0xc4, 0x29, 0x28, 0x08,
+ 0xa4, 0x22, 0x23, 0x1a, 0xac, 0x29, 0x28, 0x20,
+ 0x28, 0x20, 0x28, 0x20, 0x17, 0x28, 0x24, 0x18,
+ 0xe2, 0x29, 0x83, 0x16, 0x10, 0x18, 0xe0, 0x29,
+ 0x83, 0x12, 0x24, 0x14, 0xa4, 0x14, 0x02, 0x30,
+ 0xa4, 0x22, 0x34, 0x08, 0xb6, 0x00, 0x35, 0x08,
+ 0xb7, 0x00, 0x08, 0x00, 0x83, 0x12, 0x08, 0x00,
+ 0x83, 0x16, 0x03, 0x10, 0x10, 0x18, 0x03, 0x14,
+ 0xa1, 0x0d, 0x03, 0x01, 0x10, 0x18, 0x01, 0x30,
+ 0x83, 0x12, 0xa3, 0x1a, 0xef, 0x29, 0xa4, 0x18,
+ 0x6c, 0x22, 0xb9, 0x0a, 0xb9, 0x1d, 0x08, 0x00,
+ 0x83, 0x16, 0x21, 0x08, 0x83, 0x12, 0x9b, 0x22,
+ 0xb9, 0x01, 0x83, 0x16, 0xa1, 0x01, 0x83, 0x12,
+ 0xfb, 0x29, 0xb6, 0x0b, 0x08, 0x00, 0xb7, 0x0b,
+ 0x08, 0x00, 0xa4, 0x1c, 0x08, 0x2a, 0xa4, 0x10,
+ 0x08, 0x30, 0x02, 0x30, 0xb6, 0x00, 0x01, 0x30,
+ 0xb7, 0x00, 0x08, 0x00, 0x24, 0x10, 0x23, 0x12,
+ 0xa3, 0x1a, 0x69, 0x2a, 0x10, 0x30, 0xc4, 0x00,
+ 0x03, 0x01, 0x6c, 0x22, 0xc4, 0x0b, 0x0e, 0x2a,
+ 0x3c, 0x08, 0x9b, 0x22, 0x3b, 0x08, 0xa4, 0x22,
+ 0x08, 0x00, 0x04, 0x08, 0xe7, 0x00, 0x47, 0x30,
+ 0x84, 0x00, 0x08, 0x30, 0xc4, 0x00, 0x80, 0x01,
+ 0x84, 0x0a, 0xc4, 0x0b, 0x1d, 0x2a, 0x47, 0x30,
+ 0x84, 0x00, 0x04, 0x30, 0xc4, 0x00, 0xc2, 0x0d,
+ 0x03, 0x18, 0x80, 0x17, 0xc2, 0x0d, 0x03, 0x18,
+ 0x80, 0x15, 0xc0, 0x0d, 0x03, 0x18, 0x00, 0x17,
+ 0xc0, 0x0d, 0x03, 0x18, 0x00, 0x15, 0xbe, 0x0d,
+ 0x03, 0x18, 0x80, 0x16, 0xbe, 0x0d, 0x03, 0x18,
+ 0x80, 0x14, 0xbc, 0x0d, 0x03, 0x18, 0x00, 0x16,
+ 0xbc, 0x0d, 0x03, 0x18, 0x00, 0x14, 0x84, 0x0a,
+ 0xc4, 0x0b, 0x25, 0x2a, 0x04, 0x30, 0xc4, 0x00,
+ 0xc1, 0x0d, 0x03, 0x18, 0x80, 0x17, 0xc1, 0x0d,
+ 0x03, 0x18, 0x80, 0x15, 0xbf, 0x0d, 0x03, 0x18,
+ 0x00, 0x17, 0xbf, 0x0d, 0x03, 0x18, 0x00, 0x15,
+ 0xbd, 0x0d, 0x03, 0x18, 0x80, 0x16, 0xbd, 0x0d,
+ 0x03, 0x18, 0x80, 0x14, 0xbb, 0x0d, 0x03, 0x18,
+ 0x00, 0x16, 0xbb, 0x0d, 0x03, 0x18, 0x00, 0x14,
+ 0x84, 0x0a, 0xc4, 0x0b, 0x42, 0x2a, 0x47, 0x30,
+ 0x84, 0x00, 0x07, 0x30, 0xc4, 0x00, 0x00, 0x08,
+ 0x9b, 0x22, 0x84, 0x0a, 0xc4, 0x0b, 0x61, 0x2a,
+ 0x00, 0x08, 0xa4, 0x22, 0x08, 0x00, 0x03, 0x01,
+ 0xa4, 0x22, 0x08, 0x00, 0xc3, 0x00, 0xc3, 0x0c,
+ 0xbb, 0x0d, 0xbc, 0x0d, 0x03, 0x1c, 0x76, 0x2a,
+ 0x21, 0x30, 0xbb, 0x06, 0x10, 0x30, 0xbc, 0x06,
+ 0x08, 0x00, 0xc3, 0x0c, 0xbd, 0x0d, 0xbe, 0x0d,
+ 0x03, 0x1c, 0x80, 0x2a, 0x21, 0x30, 0xbd, 0x06,
+ 0x10, 0x30, 0xbe, 0x06, 0xc3, 0x0c, 0xbf, 0x0d,
+ 0xc0, 0x0d, 0x03, 0x1c, 0x89, 0x2a, 0x21, 0x30,
+ 0xbf, 0x06, 0x10, 0x30, 0xc0, 0x06, 0xc3, 0x0c,
+ 0xc1, 0x0d, 0xc2, 0x0d, 0x03, 0x1c, 0x08, 0x00,
+ 0x21, 0x30, 0xc1, 0x06, 0x10, 0x30, 0xc2, 0x06,
+ 0x08, 0x00, 0x8c, 0x19, 0x93, 0x2a, 0x8b, 0x13,
+ 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00, 0x8b, 0x17,
+ 0x08, 0x00, 0x8c, 0x1b, 0x9b, 0x2a, 0x8b, 0x13,
+ 0x00, 0x00, 0x8e, 0x01, 0x8d, 0x00, 0x00, 0x00,
+ 0x8b, 0x17, 0x08, 0x00, 0x8c, 0x1b, 0xa4, 0x2a,
+ 0x8b, 0x13, 0x00, 0x00, 0x8e, 0x0a, 0x8d, 0x00,
+ 0x00, 0x00, 0x8b, 0x17, 0x08, 0x00, 0x83, 0x12,
+ 0x20, 0x30, 0x84, 0x00, 0x80, 0x01, 0x84, 0x0a,
+ 0x04, 0x08, 0x80, 0x3c, 0x03, 0x1d, 0xb0, 0x2a,
+ 0xa0, 0x30, 0x84, 0x00, 0x80, 0x01, 0x84, 0x0a,
+ 0x04, 0x08, 0xff, 0x3c, 0x03, 0x1d, 0xb8, 0x2a,
+ 0x08, 0x00
+};
+
diff --git a/drivers/fims/serial/Makefile b/drivers/fims/serial/Makefile
new file mode 100644
index 000000000000..936afd535a80
--- /dev/null
+++ b/drivers/fims/serial/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the kernel FIM device drivers
+#
+
+ifneq ($(CONFIG_FIM_SERIAL_MODULE),)
+ obj-m += fim_serial.o
+else
+ obj-$(CONFIG_FIM_ZERO_SERIAL) += fim_serial.o
+ obj-$(CONFIG_FIM_ONE_SERIAL) += fim_serial.o
+endif \ No newline at end of file
diff --git a/drivers/fims/serial/fim_serial.c b/drivers/fims/serial/fim_serial.c
new file mode 100644
index 000000000000..8795e19020c6
--- /dev/null
+++ b/drivers/fims/serial/fim_serial.c
@@ -0,0 +1,1654 @@
+/* -*- linux-c -*-
+ *
+ * drivers/fims/serial/fim_serial.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * !Revision: $Revision: 1.1.1.1 $
+ * !Author: Luis Galdos
+ * !Descr:
+ * !References:
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/clk.h>
+#include <asm/delay.h>
+
+#include <mach/gpio.h>
+#include <mach/regs-sys-ns921x.h>
+#include <mach/regs-iohub-ns921x.h>
+
+/* For registering the FIM-driver */
+#include <mach/fim-ns921x.h>
+
+/*
+ * If the driver is being compiled as a built-in driver, then include the header file
+ * which contains the firmware for this FIM-device
+ */
+#if !defined(MODULE)
+#include "fim_serial.h"
+extern const unsigned char fim_serial_firmware[];
+#define FIM_SERIAL_FIRMWARE_FILE (NULL)
+#define FIM_SERIAL_FIRMWARE_CODE fim_serial_firmware
+#else
+const unsigned char *fim_serial_firmware = NULL;
+#define FIM_SERIAL_FIRMWARE_FILE "fim_serial.bin"
+#define FIM_SERIAL_FIRMWARE_CODE (NULL)
+#endif
+
+/* Driver informations */
+#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Silvano Najera, Luis Galdos"
+#define DRIVER_DESC "FIM serial driver"
+#define FIM_DRIVER_NAME "fim-serial"
+#define FIM_PLATFORM_DRIVER_NAME "platform:fim-serial"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+/* Module parameter for selection the FIMs */
+NS921X_FIM_NUMBERS_PARAM(fims_number);
+
+/* Firmware-dependent interrupts from the ARM to the FIM */
+#define FIM_SERIAL_INT_INSERT_CHAR 0x01
+#define FIM_SERIAL_INT_MATCH_CHAR 0x02
+#define FIM_SERIAL_INT_BITS_CHAR 0x04
+#define FIM_SERIAL_INT_CHAR_GAP 0x08
+#define FIM_SERIAL_INT_BIT_POS 0x10
+#define FIM_SERIAL_INT_PRESCALE 0x20
+#define FIM_SERIAL_INT_BIT_TIME 0x40
+
+/* Interrupts from the FIM to the driver */
+#define FIM_INT_FROM_MATCH_CHAR1 0x01
+#define FIM_INT_FROM_MATCH_CHAR2 0x02
+#define FIM_INT_FROM_RX_OVERFLOW 0x04
+
+/* FIM status */
+#define FIM_SERIAL_STAT_COMPLETE 0x01
+#define FIM_SERIAL_STAT_TX_ENABLE 0x02
+#define FIM_SERIAL_STAT_HW_FLOW 0x04
+#define FIM_SERIAL_STAT_MATCH_CHAR1 0x08
+#define FIM_SERIAL_STAT_MATCH_CHAR2 0x10
+
+/* Special control registers */
+#define FIM_SERIAL_TXIO_REG 0
+#define FIM_SERIAL_RXIO_REG 1
+#define FIM_SERIAL_RTSIO_REG 2
+#define FIM_SERIAL_CTSIO_REG 3
+#define FIM_SERIAL_CTRL_REG 6
+
+/* This is used only for the internal FIM-buffers */
+#define FIM_SERIAL_BUF_PRIVATE (void *)(0xffffffff)
+
+/*
+ * IMPORTANT: The number of chars per DMA-transfer must be low, otherwise
+ * the latency times by user-interrupts (CTRL-C) will be too high (some seconds!)
+ * The problem is that we can't stop the DMA-transfer in an easy way and the
+ * FIM-firmware can't be stopped on the fly.
+ */
+#define FIM_SERIAL_TX_CHARS_PER_DMA 32
+#define FIM_SERIAL_TX_DMA_BUFFER_SIZE (FIM_SERIAL_TX_CHARS_PER_DMA * 2)
+#define FIM_SERIAL_RX_DMA_BUFFER_SIZE (PAGE_SIZE / 8)
+#define FIM_SERIAL_TX_DMA_BUFFERS 64
+#define FIM_SERIAL_RX_DMA_BUFFERS 32
+
+#define FIM_SERIAL_GPIO_RX (0)
+#define FIM_SERIAL_GPIO_TX (1)
+#define FIM_SERIAL_GPIO_CTS (2)
+#define FIM_SERIAL_GPIO_RTS (3)
+#define FIM_SERIAL_GPIO_LAST (4)
+
+#define printk_err(fmt, args...) printk(KERN_DEBUG "[ ERROR ] fim-serial: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_DEBUG "fim-serial: " fmt, ## args)
+#define printk_dbg(fmt, args...) printk(KERN_DEBUG "fim-serial: " fmt, ## args)
+
+#define print_parse_err(parse, port, raw, pos) printk_debug(parse " | Byte %i | Raw %x | Data %i | Total %i | Mask %x\n", pos, raw, port->numbits, port->totalbits, port->bitsmask)
+
+#if 0
+#define FIM_SERIAL_DEBUG
+#endif
+
+#ifdef FIM_SERIAL_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "fim-serial: " fmt, ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+#define FIM_CONSOLE_NAME "ttyFIM"
+#define FIM_SERIAL_DEV_NAME "ttyFIM"
+
+/* @XXX: Remove this macro (currently used for the aximal number of GPIOs) */
+#define FIM_SERIAL_MAX_GPIOS (8)
+
+/*
+ * Masks for the incoming PIC-data. The definition are coming from the Net+OS driver
+ * and are in some cases unknown, then there is no exact documentation in the
+ * PIC-code that defines the bit fields of the data bytes
+ */
+#define FIM_DATA_PARENB(data, port) ((data >> port->numbits) & 0x01)
+#define FIM_DATA_STOPB(data, port) ((data >> (port->totalbits - 2)) & 0x03)
+#define FIM_DATA_BITS(data, port) ((data >> (port->totalbits - 1)) & 0x01)
+
+
+/* Due to the fact that we can handle only one port per FIM */
+struct fim_serial_t {
+ dev_t devnr;
+ struct fim_driver fim;
+ struct uart_port uart;
+ struct uart_driver *driver;
+ struct device *device;
+ int minor;
+ int index;
+ int open_count;
+ int configured;
+ unsigned int totalbits;
+ unsigned short bitsmask;
+ unsigned short numbits;
+ unsigned int cflag;
+ unsigned int baud;
+ unsigned int last_totalbits;
+ struct fim_gpio_t gpios[FIM_SERIAL_MAX_GPIOS];
+ struct clk *sys_clk;
+ spinlock_t tx_lock;
+ struct tasklet_struct tasklet;
+ int reg;
+ struct ktermios *termios;
+};
+
+/* Main structure for the port-handling */
+struct fim_serials_t {
+ struct uart_driver driver;
+ struct fim_serial_t *ports;
+ int fims;
+};
+
+static struct fim_serials_t *fim_serials;
+
+/* Firmware-dependent interrupts from the ARM to the FIM */
+#define FIM_SERIAL_INT_INSERT_CHAR 0x01
+#define FIM_SERIAL_INT_MATCH_CHAR 0x02
+#define FIM_SERIAL_INT_BITS_CHAR 0x04
+#define FIM_SERIAL_INT_CHAR_GAP 0x08
+#define FIM_SERIAL_INT_BIT_POS 0x10
+#define FIM_SERIAL_INT_PRESCALE 0x20
+#define FIM_SERIAL_INT_BIT_TIME 0x40
+
+/* Interrupts from the FIM to the driver */
+#define FIM_INT_FROM_MATCH_CHAR1 0x01
+#define FIM_INT_FROM_MATCH_CHAR2 0x02
+#define FIM_INT_FROM_RX_OVERFLOW 0x04
+
+
+/* FIM status */
+#define FIM_SERIAL_STAT_COMPLETE 0x01
+#define FIM_SERIAL_STAT_TX_ENABLE 0x02
+#define FIM_SERIAL_STAT_HW_FLOW 0x04
+#define FIM_SERIAL_STAT_MATCH_CHAR1 0x08
+#define FIM_SERIAL_STAT_MATCH_CHAR2 0x10
+
+inline static struct fim_serial_t *get_port_from_uart(struct uart_port *uart)
+{
+ return dev_get_drvdata(uart->dev);
+}
+
+
+inline static struct uart_port *get_uart_from_port(struct fim_serial_t *port)
+{
+ return &port->uart;
+}
+
+inline static struct tty_struct *get_tty_from_port(struct fim_serial_t *port)
+{
+ struct uart_info *info;
+
+ info = port->uart.info;
+ return (info) ? info->port.tty : NULL;
+}
+
+inline static struct fim_serial_t *get_port_by_index(int index)
+{
+ if (index < 0 || index > fim_serials->fims)
+ return NULL;
+
+ return fim_serials->ports + index;
+}
+
+inline static int fim_serial_reset_matchs(struct fim_driver *fim)
+{
+ fim_set_ctrl_reg(fim, 0, 0xFF);
+ fim_set_ctrl_reg(fim, 1, 0xFF);
+ fim_set_ctrl_reg(fim, 2, 0xFF);
+ fim_set_ctrl_reg(fim, 3, 0xFF);
+ fim_set_ctrl_reg(fim, 4, 0x00);
+ fim_set_ctrl_reg(fim, 5, 0x00);
+ return fim_send_interrupt2(fim, FIM_SERIAL_INT_MATCH_CHAR);
+}
+
+inline static int fim_serial_reset_all(struct fim_driver *fim)
+{
+ int retval;
+
+ fim_set_ctrl_reg(fim, 0, 0);
+ fim_set_ctrl_reg(fim, 1, 0);
+ fim_set_ctrl_reg(fim, 2, 0);
+ fim_set_ctrl_reg(fim, 3, 0);
+ retval = fim_send_interrupt2(fim, FIM_SERIAL_INT_BIT_POS);
+ if (!retval)
+ retval = fim_serial_reset_matchs(fim);
+
+ return retval;
+}
+
+
+static unsigned char get_parity(unsigned char data, unsigned char mask, int odd)
+{
+ unsigned char ones = 0;
+ mask = (mask >> 1) + 1;
+ do {
+ ones += (data & mask) ? 1 : 0;
+ mask >>= 1;
+ } while (mask);
+ if (odd)
+ return ((ones & 1) ^ 1);
+ else
+ return (ones & 1);
+}
+
+/*
+ * Send a char over an interrupt
+ * Only implemented for sending the XOFF/XON chars. Otherwise use the send buffer
+ * function, which uses the DMA-channel for a performanter data transfer
+ */
+static int fim_serial_send_char(struct fim_serial_t *port, unsigned char ch)
+{
+ unsigned int status;
+ int retval;
+ struct fim_driver *fim;
+ unsigned char data = 1;
+ unsigned int timeout;
+ struct tty_struct *tty;
+
+ fim = &port->fim;
+ if (!(tty = get_tty_from_port(port))) {
+ printk_err("No TTY-port found for the FIM %i\n", fim->picnr);
+ return -ENODEV;
+ }
+
+ /* First check if the FIM is tasked with another send-char request */
+ timeout = 0xFFFF;
+ do {
+ timeout--;
+ fim_get_exp_reg(fim, 0, &status);
+ } while (timeout && (status & FIM_SERIAL_INT_INSERT_CHAR));
+
+ if (!timeout) {
+ printk_err("Timeout by sending a char over the FIM %i\n", fim->picnr);
+ return -EAGAIN;
+ }
+
+ if (C_CSTOPB(tty))
+ data = (data << 1) | 0x01;
+
+ if (C_PARENB(tty))
+ data = (data << 1) | get_parity(ch, port->bitsmask, C_PARODD(tty));
+
+ data = (data << port->numbits) | (ch & port->bitsmask);
+
+ /* And send the char using the interrupt function */
+ fim_set_ctrl_reg(fim, 0, data & 0xFF);
+ fim_set_ctrl_reg(fim, 1, (data >> 8) & 0xFF);
+ retval = fim_send_interrupt2(fim, FIM_SERIAL_INT_INSERT_CHAR);
+ if (retval)
+ printk_err("Send char by the FIM %i failed, %i\n", fim->picnr, retval);
+
+ return retval;
+}
+
+/*
+ * Return value of this function is the number of copied bytes from `src' to `dest'
+ */
+static int fim_serial_parse_data(struct tty_struct *tty,
+ struct fim_serial_t *port,
+ unsigned char *src, int length,
+ unsigned char *dest)
+{
+ unsigned short data;
+ int copied;
+ struct uart_port *uart;
+
+ uart = get_uart_from_port(port);
+ for (copied=0; length > 1; length -= 2) {
+ if (port->totalbits < 8) {
+ data = *src >> (8 - port->totalbits);
+ src++;
+ } else {
+ data = *src;
+ src++;
+ data |= (((unsigned short)*src >> (16 - port->totalbits)) << 8);
+ }
+
+ /* Ignore VSTART and VSTOP characters if hadshake is enabled */
+ if (I_IXOFF(tty)) {
+ if ((data & port->bitsmask) == START_CHAR(tty) ||
+ (data & port->bitsmask) == STOP_CHAR(tty)) {
+ src++;
+ length -= 2;
+ continue;
+ }
+ }
+
+ *dest = data & port->bitsmask;
+
+ if (C_PARENB(tty)) {
+ if (FIM_DATA_PARENB(data, port) != get_parity(*dest,
+ port->bitsmask,
+ C_PARODD(tty))) {
+ uart->icount.parity++;
+ print_parse_err("Parity", port, data, copied);
+ continue;
+ }
+ }
+
+ if (C_CSTOPB(tty)) {
+ if (FIM_DATA_STOPB(data, port) != 0x03) {
+
+ /* @BUG: With 5N2 the stop bit flag is incorrect! */
+ if (port->totalbits == 8) {
+ uart->icount.frame++;
+ print_parse_err("Stop ", port, data, copied);
+ continue;
+ }
+ }
+ } else {
+ if (!FIM_DATA_BITS(data, port)) {
+ uart->icount.frame++;
+ print_parse_err("Data ", port, data, copied);
+ continue;
+ }
+ }
+
+ src++;
+ dest++;
+ copied++;
+ }
+
+ return copied;
+}
+
+/*
+ * The following function is coming from the Net+OS driver
+ * This functions depends on the number of total bits too. For this reason is
+ * required to call it if the number of bits changes
+ */
+static int fim_serial_baudrate(struct fim_serial_t *port, struct ktermios *termios)
+{
+ unsigned int div, prescale = 1;
+ unsigned long clock;
+ int baud, cnt, retval;
+ struct fim_driver *fim;
+ unsigned int gap;
+ unsigned int bit_time;
+ unsigned int cflag;
+
+ fim = &port->fim;
+ cflag = termios->c_cflag;
+ baud = tty_termios_baud_rate(termios);
+
+ /* Check if really need to change something */
+ if (port->baud == baud) {
+ if (port->last_totalbits != port->totalbits) {
+ printk_debug("Setting only the gap timer (%iBps | %i | %i)\n",
+ baud, port->totalbits, port->last_totalbits);
+ clock = clk_get_rate(port->sys_clk) / baud;
+ goto set_char_gap;
+ }
+ return 0;
+ }
+
+ clock = clk_get_rate(port->sys_clk);
+ printk_debug("Obtained SYS clock is %luHz\n", clock);
+ clock = clock / baud;
+ div = (clock / 256) + 1;
+
+ /* Must round up to next power of 2 (see NET+OS driver) */
+ for (cnt = 1; cnt <= 8; cnt++) {
+ if (div < (unsigned int)(1 << cnt)) {
+ div = 1 << cnt;
+ prescale = cnt - 1;
+ break;
+ }
+ }
+
+ /* This sanity check is coming from the Net+OS driver */
+ if (cnt > 8) {
+ printk_debug("Wrong prescale value %i by the baud %i\n", cnt, baud);
+ return -EINVAL;
+ }
+
+ /* The Net+OS driver has another calculation of the bit time */
+ bit_time = (clock / div) - 1;
+
+ printk_debug("%iB | Pre %i | Btime %u | Tbits %i | Dbits %i | S %i | Pa %i\n",
+ baud, prescale, bit_time, port->totalbits, port->numbits,
+ cflag & CSTOPB, cflag & PARENB);
+
+ /* Set the bit time */
+ fim_set_ctrl_reg(fim, 0, bit_time);
+ retval = fim_send_interrupt2(fim, FIM_SERIAL_INT_BIT_TIME);
+ if (retval) {
+ printk_err("Couldn't set the bit time of the FIM %i\n", fim->picnr);
+ return -EAGAIN;
+ }
+
+ /* Set the prescale value */
+ fim_set_ctrl_reg(fim, 0, prescale);
+ retval = fim_send_interrupt2(fim, FIM_SERIAL_INT_PRESCALE);
+ if (retval) {
+ printk_err("Prescale configuration of the FIM %i failed.\n",
+ fim->picnr);
+ return retval;
+ }
+
+ set_char_gap:
+ gap = (2 * clock * port->totalbits)/30;
+ fim_set_ctrl_reg(fim, 0, (gap & 0xFF) + 1);
+ fim_set_ctrl_reg(fim, 1, ((gap >> 8) & 0xFF) + 1);
+ retval = fim_send_interrupt2(fim, FIM_SERIAL_INT_CHAR_GAP);
+ if (retval) {
+ printk_err("GAP timer by the FIM %i (Total bytes %i)\n",
+ fim->picnr, port->totalbits);
+ return retval;
+ }
+
+ port->baud = baud;
+ port->last_totalbits = port->totalbits;
+ return 0;
+}
+
+static int fim_sw_flowctrl(struct fim_serial_t *port, struct ktermios *termios)
+{
+ struct fim_driver *fim;
+ int retval;
+ unsigned short start_match, stop_match;
+ unsigned short mask;
+ unsigned int cflag, iflag;
+
+ cflag = termios->c_cflag;
+ iflag = termios->c_iflag;
+
+ fim = &port->fim;
+ start_match = 1;
+ stop_match = 1;
+
+ printk_debug("Calling %s\n", __func__);
+
+ /* Check if need to set the SW flow control, otherwise reset it */
+ if (iflag & IXON || iflag & IXOFF) {
+ printk_debug("Active SW flow control? @XXX: Test it first.\n");
+ if (cflag & CSTOPB) {
+ start_match = (start_match << 1) | 1;
+ stop_match = (stop_match << 1) | 1;
+ }
+
+ if (cflag & PARENB) {
+
+ start_match = (start_match << 1) |
+ get_parity(termios->c_cc[VSTART],
+ port->bitsmask,
+ cflag & PARENB & PARODD);
+
+ stop_match = (stop_match << 1) |
+ get_parity(termios->c_cc[VSTOP],
+ port->bitsmask,
+ cflag & PARENB & PARODD);
+ }
+
+ mask = start_match | port->bitsmask;
+
+ if (port->totalbits < 8) {
+ start_match = start_match << (8 - port->totalbits);
+ stop_match = stop_match << (8 - port->totalbits);
+ mask = port->bitsmask << (8 - port->totalbits);
+ }
+
+ fim_set_ctrl_reg(fim, 0, start_match & 0xff);
+ fim_set_ctrl_reg(fim, 1, 0x00);
+ fim_set_ctrl_reg(fim, 2, stop_match & 0xff);
+ fim_set_ctrl_reg(fim, 3, 0x00);
+ fim_set_ctrl_reg(fim, 4, mask & 0xff);
+ fim_set_ctrl_reg(fim, 5, 0x00);
+
+ } else {
+ fim_serial_reset_matchs(fim);
+ }
+
+ /* Now send the interrupt for the SW flow control */
+ retval = fim_send_interrupt2(fim, FIM_SERIAL_INT_MATCH_CHAR);
+ if (retval) {
+ printk_err("Couldn't set the bits match\n");
+ return retval;
+ }
+
+ return 0;
+}
+
+/* Function for the setup of the bit positions in the FIM-firmware */
+static int fim_serial_setup_bitpos(struct fim_serial_t *port, ulong hwflow)
+{
+ struct fim_driver *fim;
+ struct fim_gpio_t *gpios;
+ unsigned int cts, rts;
+ int offset;
+
+ /* Depending on the processor we have different offset */
+ if (processor_is_ns9215())
+ offset = 68;
+ else if (processor_is_ns9210())
+ offset = 0;
+ else
+ return -EINVAL;
+
+ fim = &port->fim;
+ gpios = port->gpios;
+
+ /* Set the TX- and RX-line */
+ printk_debug("TX %i | RX %i | RTS %i | CTS %i | Offset %i\n",
+ gpios[FIM_SERIAL_GPIO_TX].nr, gpios[FIM_SERIAL_GPIO_RX].nr,
+ gpios[FIM_SERIAL_GPIO_RTS].nr, gpios[FIM_SERIAL_GPIO_CTS].nr,
+ offset);
+
+ fim_set_ctrl_reg(fim, FIM_SERIAL_TXIO_REG,
+ 1 << (gpios[FIM_SERIAL_GPIO_TX].nr - offset));
+
+ fim_set_ctrl_reg(fim, FIM_SERIAL_RXIO_REG,
+ 1 << (gpios[FIM_SERIAL_GPIO_RX].nr - offset));
+
+ /* Check if the GPIOs are available for the HW flow control */
+ if (hwflow && (gpios[FIM_SERIAL_GPIO_CTS].nr == FIM_GPIO_DONT_USE ||
+ gpios[FIM_SERIAL_GPIO_RTS].nr == FIM_GPIO_DONT_USE)) {
+ printk_dbg("HW flow control not supported (GPIOs not defined)\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* Setup the CTS and RTS if HW flow controls is requested */
+ cts = hwflow ? 1 << (gpios[FIM_SERIAL_GPIO_CTS].nr - offset) : 0;
+ rts = hwflow ? 1 << (gpios[FIM_SERIAL_GPIO_RTS].nr - offset) : 0;
+ fim_set_ctrl_reg(fim, FIM_SERIAL_CTSIO_REG, cts);
+ fim_set_ctrl_reg(fim, FIM_SERIAL_RTSIO_REG, rts);
+
+ return fim_send_interrupt2(fim, FIM_SERIAL_INT_BIT_POS);
+}
+
+/*
+ * The ktermios structure is declared under: include/asm-arm/termbits.h
+ */
+static int fim_serial_configure_port(struct fim_serial_t *port,
+ struct ktermios *termios,
+ struct ktermios *old_termios)
+{
+ struct fim_driver *fim;
+ unsigned int cflag, old_cflag;
+ int databits, stopbits, parbit;
+ int retval;
+ unsigned int regval;
+ int timeout;
+ unsigned long flags;
+
+ /* That's really bad but it can happen */
+ if (!termios)
+ return -EINVAL;
+
+ /* @BUG: The info pointer is NULL when the console is started! */
+ fim = &port->fim;
+ cflag = termios->c_cflag;
+ old_cflag = 0;
+ if (old_termios)
+ old_cflag = old_termios->c_cflag;
+
+ /* Check the number of bits to set */
+ switch (cflag & CSIZE) {
+ case CS5:
+ databits = 5;
+ break;
+ case CS6:
+ databits = 6;
+ break;
+ case CS7:
+ databits = 7;
+ break;
+ case CS8:
+ databits = 8;
+ break;
+ default:
+ printk_err("Invalid number of data bits flag 0x%04X\n", cflag & CSIZE);
+ return -EINVAL;
+ }
+
+ /* Check if the STOPB flag is set, in that case use two stop bits */
+ stopbits = 1;
+ if (cflag & CSTOPB)
+ stopbits = 2;
+
+ parbit = 0;
+ if (cflag & PARENB)
+ parbit = 1;
+
+ /* Lock the below code segment first */
+ spin_lock_irqsave(&port->tx_lock, flags);
+
+ /* Now, we must wait for the empty DMA-buffers */
+ timeout = 1000;
+ while (timeout && fim_tx_buffers_level(&port->fim)) {
+ udelay(1000);
+ cpu_relax();
+ timeout--;
+ }
+
+ if (!timeout) {
+ printk_err("Timeout waiting for an empty TX-DMA buffer!\n");
+ retval = -ETIME;
+ goto exit_unlock;
+ }
+
+ /* Ok, we are save, we can try to configure the FIM with the valid byte length */
+ if (port->totalbits != databits + stopbits + parbit) {
+ fim_set_ctrl_reg(fim, 0, databits + stopbits + parbit);
+ retval = fim_send_interrupt2(fim, FIM_SERIAL_INT_BITS_CHAR);
+ if (retval) {
+ printk_err("Failed config of the data bits by the FIM %i\n",
+ fim->picnr);
+ goto exit_unlock;
+ }
+ }
+
+ /* Save the number of bits for the future operations */
+ port->numbits = databits;
+ port->totalbits = databits + stopbits + parbit;
+ port->bitsmask = (1 << databits) - 1;
+ port->cflag = cflag;
+
+ /* Configure the software flow control */
+ retval = fim_sw_flowctrl(port, termios);
+ if (retval) {
+ printk_err("SW flow control of the FIM %i\n", fim->picnr);
+ goto exit_unlock;
+ }
+
+ /* Now configure the baudrate and bit timing parameters */
+ retval = fim_serial_baudrate(port, termios);
+ if (retval) {
+ printk_err("Couldn't set the baud rate by the FIM %i\n", fim->picnr);
+ goto exit_unlock;
+ }
+
+ /*
+ * Check if need to enable the CRTSCTS-lines. In that case first configure
+ * the bits position and then enable the HW-flow control in the config register
+ */
+ if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
+
+ retval = fim_serial_setup_bitpos(port, cflag & CRTSCTS);
+ if (retval) {
+
+ /*
+ * This return value means that the HW flow control could not
+ * be enabled. Reset this flag so that the higher TTY layer
+ * knows that a requested operation failed and informs the user
+ * space about this.
+ */
+ if (retval == -EOPNOTSUPP)
+ termios->c_cflag &= ~CRTSCTS;
+
+ printk_err("Couldn't setup the FIM port %i\n",
+ fim->picnr);
+ goto exit_unlock;
+ }
+
+ /* Now set the control status register to the correct value */
+ fim_get_ctrl_reg(fim, FIM_SERIAL_CTRL_REG, &regval);
+ if (cflag & CRTSCTS)
+ regval |= FIM_SERIAL_STAT_HW_FLOW;
+ else
+ regval &= ~FIM_SERIAL_STAT_HW_FLOW;
+ fim_set_ctrl_reg(fim, FIM_SERIAL_CTRL_REG, regval);
+ }
+
+ /* After each reconfiguration we need to re-init the FIM-firmware */
+ fim_get_ctrl_reg(fim, FIM_SERIAL_CTRL_REG, &regval);
+ fim_set_ctrl_reg(fim, FIM_SERIAL_CTRL_REG,
+ regval | FIM_SERIAL_STAT_TX_ENABLE | FIM_SERIAL_STAT_COMPLETE);
+
+ retval = 0;
+ port->termios = termios;
+
+ exit_unlock:
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+ return retval;
+}
+
+/*
+ * Send a buffer over the FIM-core and the DMA-controller
+ * For being faster we use an internal private FIM-buffer for sending the data
+ * to the DMA-channel
+ */
+static int fim_serial_send_buffer(struct fim_serial_t *port,
+ struct uart_port *uart)
+{
+ struct fim_buffer_t buf;
+ unsigned char *pdest;
+ unsigned short data;
+ unsigned char ch;
+ struct fim_driver *fim;
+ int bytes_per_char, stop_bits;
+ int len, chars_to_send;
+ struct circ_buf *xmit;
+ unsigned char buf_data[FIM_SERIAL_TX_DMA_BUFFER_SIZE];
+ int retval;
+
+ if (!port || !uart) {
+ printk_err("NULL pointer passed (port %p | uart %p)\n", port, uart);
+ return -EINVAL;
+ }
+
+ spin_lock(&port->tx_lock);
+
+ xmit = &uart->info->xmit;
+ len = uart_circ_chars_pending(xmit);
+ data = 1;
+ if (port->cflag & CSTOPB)
+ data = (data << 1) | 0x01;
+ stop_bits = data;
+
+ if (port->cflag & PARENB)
+ data = (data << 1);
+ data = (data << port->numbits);
+
+ bytes_per_char = 1;
+ if (data & 0xFF00)
+ bytes_per_char = 2;
+
+ fim = &port->fim;
+ len = (len > FIM_SERIAL_TX_CHARS_PER_DMA) ? FIM_SERIAL_TX_CHARS_PER_DMA : len;
+
+ /* Now copy the data into the FIM-buffer */
+ pdest = buf_data;
+ chars_to_send = 0;
+ while (!uart_circ_empty(xmit) && chars_to_send < len) {
+ ch = xmit->buf[xmit->tail];
+ data = stop_bits;
+ if (port->cflag & PARENB)
+ data = (data << 1) | get_parity(ch, port->bitsmask,
+ port->cflag & PARODD);
+
+ data = (data << port->numbits) | (ch & port->bitsmask);
+
+ /* For char length greater than 8 bits use two bytes for the TX-data */
+ *pdest++ = data & 0xFF;
+ if(bytes_per_char == 2)
+ *pdest++ = (data >> 8) & 0xFF;
+
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ uart->icount.tx++;
+ chars_to_send += 1;
+ }
+
+
+ /* Check a last time if we need to send the data */
+ if (uart_tx_stopped(uart) || !chars_to_send) {
+ printk_debug("Aborting TX due a stopped queue!\n");
+ goto exit_unlock;
+ }
+
+ /*
+ * Setup the FIM-buffer with the information that we have a private
+ * buffer and don't want to free it inside the TX-callback
+ */
+ retval = 0;
+ buf.private = FIM_SERIAL_BUF_PRIVATE;
+ buf.length = chars_to_send * bytes_per_char;
+ buf.data = buf_data;
+ if (fim_send_buffer(fim, &buf)) {
+ printk_err("FIM send buffer request failed (len %i)\n", buf.length);
+ retval = -ENOMEM;
+ }
+
+
+ exit_unlock:
+ spin_unlock(&port->tx_lock);
+
+ return retval;
+}
+
+static int fim_serial_transmit(struct uart_port *uart)
+{
+ struct circ_buf *xmit;
+ struct fim_serial_t *port;
+ struct fim_driver *fim;
+
+ port = get_port_from_uart(uart);
+ fim = &port->fim;
+ xmit = &uart->info->xmit;
+
+ /*
+ * Check if we have enough space for the request, if not then the tasklet
+ * will be responsible for recalling this function and for resending the data
+ * of the UART buffer
+ */
+ if (fim_tx_buffers_room(fim) < uart_circ_chars_pending(xmit)) {
+ printk_debug("Skipping transmit request with length %lu\n",
+ uart_circ_chars_pending(xmit));
+ return 0;
+ }
+
+ printk_debug("Request to send %lu chars | stopped %i | empty %i\n",
+ uart_circ_chars_pending(xmit),
+ uart_tx_stopped(uart), uart_circ_empty(xmit));
+
+ /* Check if need to send only one char */
+ if (uart->x_char) {
+ printk_debug("Sending only one char\n");
+ fim_serial_send_char(port, uart->x_char);
+ uart->x_char = 0;
+ uart->icount.tx++;
+ return 1;
+ }
+
+ /* Check if have something to send */
+ if (uart_circ_empty(xmit) || uart_tx_stopped(uart)) {
+ printk_debug("Circular buffer empty or TX stopped\n");
+ return 0;
+ }
+
+ /* IMPORTANT: Always use the "tail" of the xmit-buffer */
+ fim_serial_send_buffer(port, uart);
+
+ /* Tell the higher layer that we can "probably" send more data */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(uart);
+
+ return 0;
+}
+
+/* Function for transmitting data to the FIM-port */
+static void fim_serial_start_tx(struct uart_port *uart)
+{
+ printk_debug("Calling %s\n", __func__);
+ fim_serial_transmit(uart);
+}
+
+/* Return zero if the TX-DMA buffers are NOT empty */
+static unsigned int fim_serial_tx_empty(struct uart_port *uart)
+{
+ struct fim_driver *fim;
+ unsigned int level;
+ struct fim_serial_t *port;
+
+ port = get_port_from_uart(uart);
+ fim = &port->fim;
+ level = fim_tx_buffers_level(fim);
+
+ printk_debug("TX empty called (Level %i)\n", level);
+
+ return (level) ? 0 : TIOCSER_TEMT;
+}
+
+/* Handler for the incoming FIM-interrupts */
+static void fim_serial_isr(struct fim_driver *driver, int irq, unsigned char code,
+ unsigned int rx_fifo)
+{
+ struct uart_port *uart;
+ struct fim_serial_t *port;
+
+ if(!code)
+ return;
+
+ port = (struct fim_serial_t *)driver->driver_data;
+ uart = get_uart_from_port(port);
+
+ switch (code) {
+ case FIM_INT_FROM_MATCH_CHAR1:
+ printk_err("@TODO: Match char1 interrupt received.\n");
+ break;
+ case FIM_INT_FROM_MATCH_CHAR2:
+ printk_err("@TODO: Match char2 interrupt received.\n");
+ break;
+ case FIM_INT_FROM_RX_OVERFLOW:
+ uart->icount.overrun++;
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+/* This tasklet will restart the function for transmitting data to the FIM */
+static void fim_serial_tasklet_func(unsigned long data)
+{
+ struct fim_serial_t *port;
+ struct circ_buf *xmit;
+ struct uart_port *uart;
+
+ port = (struct fim_serial_t *)data;
+ printk_debug("Tasklet for port %p\n", port);
+
+ if (!port)
+ return;
+
+ uart = get_uart_from_port(port);
+ if (!uart)
+ return;
+
+ xmit = &uart->info->xmit;
+ if (uart_circ_chars_pending(xmit))
+ fim_serial_transmit(uart);
+}
+
+/*
+ * This is the TX-callback that the FIM-core call after a DMA-buffer was closed
+ * The fim buffer structure contains our internal private data
+ */
+static void fim_serial_tx_isr(struct fim_driver *driver, int irq,
+ struct fim_buffer_t *pdata)
+{
+ struct fim_buffer_t *buf;
+ struct fim_serial_t *port;
+
+ port = (struct fim_serial_t *)driver->driver_data;
+ buf = (struct fim_buffer_t *)pdata->private;
+ printk_debug("TX-callback | FIM %i\n", driver->picnr);
+
+ /*
+ * Free the allocated FIM-buffer
+ */
+ if (buf && buf != FIM_SERIAL_BUF_PRIVATE)
+ fim_free_buffer(&port->fim, buf);
+
+ /* Schedule the tasklet for continuing with the data transmission */
+ tasklet_schedule(&port->tasklet);
+
+ return;
+}
+
+/* Called when a receive DMA-buffer was closed */
+static void fim_serial_rx_isr(struct fim_driver *driver, int irq,
+ struct fim_buffer_t *pdata)
+{
+ struct fim_serial_t *port;
+ struct uart_port *uart;
+ struct tty_struct *tty;
+ int length;
+
+ /* Get the correct port from the FIM-driver structure */
+ port = (struct fim_serial_t *)driver->driver_data;
+ uart = &port->uart;
+ tty = get_tty_from_port(port);
+
+ /* If the port is closed then the tty structure will be NULL */
+ if (!port || !tty) {
+ printk_debug("uart %p | tty %p | port %p\n", uart, tty, port);
+ return;
+ }
+
+ /* By errors pass nothing to the TTY-layer */
+ if ((length = fim_serial_parse_data(tty, port, pdata->data, pdata->length,
+ pdata->data)) <= 0) {
+ printk_err("Parsing the RX-DMA data (len %i)\n", length);
+ return;
+ }
+
+ tty_insert_flip_string(tty, pdata->data, length);
+ tty_flip_buffer_push(tty);
+ return;
+}
+
+static unsigned int fim_serial_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void fim_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* @TODO? */
+}
+
+/* @TODO: This function isn't called for stopping the TX */
+static void fim_serial_stop_tx(struct uart_port *port)
+{
+ printk_debug("Calling %s\n", __func__);
+}
+
+static void fim_serial_stop_rx(struct uart_port *port)
+{
+ printk_debug("Calling %s\n", __func__);
+ /* @TODO? */
+}
+
+static void fim_serial_enable_ms(struct uart_port *port)
+{
+ /* @TODO? */
+}
+
+static void fim_serial_break_ctl(struct uart_port *port, int ctl)
+{
+ /* @TODO? */
+}
+
+/*
+ * Called when the port is opened. By the boot-console it will be called before
+ * the port configuration using set_termios
+ */
+static int fim_serial_startup(struct uart_port *uart)
+{
+ struct fim_serial_t *port;
+ unsigned int regval;
+
+ printk_debug("Calling %s\n", __func__);
+ port = get_port_from_uart(uart);
+
+ fim_enable_irq(&port->fim);
+
+ fim_get_ctrl_reg(&port->fim, FIM_SERIAL_CTRL_REG, &regval);
+ fim_set_ctrl_reg(&port->fim,
+ FIM_SERIAL_CTRL_REG,
+ regval | FIM_SERIAL_STAT_TX_ENABLE | FIM_SERIAL_STAT_COMPLETE);
+
+ return 0;
+}
+
+/* Normally we must free the IRQ here, but the current FIM-firmware doesn't allow it */
+static void fim_serial_shutdown(struct uart_port *port)
+{
+ printk_debug("@TODO: Extend the firmware for %s\n", __func__);
+}
+
+/* Called for setting the termios config */
+static void fim_serial_set_termios(struct uart_port *uart, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct fim_serial_t *port;
+ unsigned int cflag;
+
+ printk_debug("Calling %s | termios %p | old %p\n", __func__, termios, old);
+
+ cflag = (old) ? old->c_cflag : 0;
+ if (cflag == termios->c_cflag) {
+ printk_debug("Skipping the termios configuration\n");
+ return;
+ }
+
+ port = get_port_from_uart(uart);
+ fim_serial_configure_port(port, termios, old);
+}
+
+/* Return a string describing the type of the port */
+static const char *fim_serial_type(struct uart_port *port)
+{
+ return FIM_DRIVER_NAME;
+}
+
+static void fim_serial_release_port(struct uart_port *port)
+{
+ printk_debug("Calling %s\n", __func__);
+}
+
+static int fim_serial_request_port(struct uart_port *port)
+{
+ printk_debug("Calling %s\n", __func__);
+ return 0;
+}
+
+/* @TODO: Get more infos about the UART configuration */
+static void fim_serial_config_port(struct uart_port *port, int flags)
+{
+ printk_debug("Calling %s\n", __func__);
+ port->type = UPIO_MEM;
+}
+
+static int fim_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ printk_debug("Verify port called\n");
+ return 0;
+}
+
+static struct uart_ops fim_serial_ops = {
+ .tx_empty = fim_serial_tx_empty,
+ .set_mctrl = fim_serial_set_mctrl,
+ .get_mctrl = fim_serial_get_mctrl,
+ .stop_tx = fim_serial_stop_tx,
+ .start_tx = fim_serial_start_tx,
+ .stop_rx = fim_serial_stop_rx,
+ .enable_ms = fim_serial_enable_ms,
+ .break_ctl = fim_serial_break_ctl,
+ .startup = fim_serial_startup,
+ .shutdown = fim_serial_shutdown,
+ .set_termios = fim_serial_set_termios,
+ .type = fim_serial_type,
+ .release_port = fim_serial_release_port,
+ .request_port = fim_serial_request_port,
+ .config_port = fim_serial_config_port,
+ .verify_port = fim_serial_verify_port
+};
+
+#ifdef CONFIG_SERIAL_FIM_CONSOLE
+static void fim_serial_wait_tx(struct fim_driver *fim)
+{
+ unsigned int timeout;
+ unsigned int status;
+
+ /* First check if the FIM is tasked with another send-char request */
+ timeout = 0xFFFF;
+ do {
+ timeout--;
+ fim_get_exp_reg(fim, 0, &status);
+ cpu_relax();
+ } while (timeout && (status & FIM_SERIAL_INT_INSERT_CHAR));
+}
+
+static void fim_serial_console_putchar(struct uart_port *uart, int ch)
+{
+ struct fim_driver *fim;
+ struct fim_serial_t *port;
+ unsigned short data = 1;
+ unsigned int cflag;
+
+ port = get_port_from_uart(uart);
+ fim = &port->fim;
+ cflag = port->cflag;
+
+ /* First wait for a free TX-FIFO */
+ fim_serial_wait_tx(fim);
+
+ if (cflag & CSTOPB)
+ data = (data << 1) | 0x01;
+
+ if (cflag & PARENB)
+ data = (data << 1) | get_parity(ch, port->bitsmask,
+ cflag & PARENB & PARODD);
+
+ data = (data << port->numbits) | (ch & port->bitsmask);
+
+ /* And send the char using the interrupt function */
+ fim_set_ctrl_reg(fim, 0, data & 0xFF);
+ fim_set_ctrl_reg(fim, 1, (data >> 8) & 0xFF);
+ fim_send_interrupt2(fim, FIM_SERIAL_INT_INSERT_CHAR);
+}
+
+static void fim_serial_console_write(struct console *co, const char *str,
+ unsigned int count)
+{
+ struct fim_serial_t *port;
+ struct uart_port *uart;
+ unsigned long flags;
+
+ port = get_port_by_index(co->index);
+ uart = get_uart_from_port(port);
+
+/* if (oops_in_progress) @TODO? */
+ spin_lock_irqsave(&port->tx_lock, flags);
+
+ /* @TODO: Test this function more intensive! */
+
+ uart_console_write(uart, str, count, fim_serial_console_putchar);
+
+ fim_serial_wait_tx(&port->fim);
+
+ spin_unlock_irqrestore(&port->tx_lock, flags);
+}
+
+static int __init fim_serial_console_setup(struct console *co, char *options)
+{
+ struct fim_serial_t *port;
+ struct uart_port *uart;
+ int baud = 9600;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ printk_debug("Calling %s for index %i\n", __func__, co->index);
+
+ /* @FIXME: Sanity checks required */
+ port = get_port_by_index(co->index);
+ if (!port->reg)
+ return -ENODEV;
+
+ uart = get_uart_from_port(port);
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ printk_debug("Console %p: B%i | P%i | B%i\n", uart, baud, parity, bits);
+ fim_serial_startup(uart);
+ return uart_set_options(uart, co, baud, parity, bits, flow);
+}
+
+/* The data of the console is being initialized inside the init-function */
+static struct console fim_console = {
+ .name = FIM_SERIAL_DEV_NAME,
+ .write = fim_serial_console_write,
+ .device = uart_console_device,
+ .setup = fim_serial_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+#define fim_console_ptr (&fim_console)
+#else
+#define fim_console_ptr (NULL)
+#endif /* CONFIG_SERIAL_FIM_CONSOLE */
+
+static int fim_serial_unregister_port(struct fim_serial_t *port)
+{
+ int cnt;
+ struct fim_driver *fim;
+ struct uart_port *uart;
+
+ if (!port || !port->reg)
+ return -ENODEV;
+
+ tasklet_kill(&port->tasklet);
+
+ uart = &port->uart;
+ fim = &port->fim;
+ fim_set_ctrl_reg(fim, FIM_SERIAL_CTRL_REG, 0x00);
+
+ /* Reset all the control register */
+ fim_serial_reset_all(fim);
+
+ fim_unregister_driver(fim);
+ uart_remove_one_port(&fim_serials->driver, uart);
+
+ /* Free all the requested GPIOs */
+ for (cnt=0; cnt < FIM_SERIAL_MAX_GPIOS; cnt++) {
+ printk_debug("Freeing GPIO %i\n", port->gpios[cnt].nr);
+ if (port->gpios[cnt].nr == FIM_LAST_GPIO)
+ break;
+ else if (port->gpios[cnt].nr == FIM_GPIO_DONT_USE)
+ continue;
+ else
+ gpio_free(port->gpios[cnt].nr);
+ }
+
+ /* Free the clock */
+ clk_put(port->sys_clk);
+
+ port->reg = 0;
+ return 0;
+}
+
+static int fim_serial_register_port(struct device *dev,
+ struct fim_serial_t *port, int minor, int picnr,
+ struct fim_gpio_t gpios[])
+{
+ int retval;
+ int cnt, func;
+ struct fim_driver *fim;
+ struct uart_port *uart;
+ struct fim_dma_cfg_t dma_cfg;
+
+ fim = &port->fim;
+ uart = &port->uart;
+
+ port->index = minor;
+ fim->picnr = picnr;
+ fim->driver.name = FIM_DRIVER_NAME;
+ fim->fim_isr = fim_serial_isr;
+ fim->dma_tx_isr = fim_serial_tx_isr;
+ fim->dma_rx_isr = fim_serial_rx_isr;
+ fim->driver_data = port;
+
+ /* Set our desired DMA-channel configuration */
+ dma_cfg.rxnr = FIM_SERIAL_RX_DMA_BUFFERS;
+ dma_cfg.txnr = FIM_SERIAL_TX_DMA_BUFFERS;
+ dma_cfg.rxsz = FIM_SERIAL_RX_DMA_BUFFER_SIZE;
+ dma_cfg.txsz = FIM_SERIAL_TX_DMA_BUFFER_SIZE;
+ fim->dma_cfg = &dma_cfg;
+
+ /* Check if have a firmware code for using to */
+ fim->fw_name = FIM_SERIAL_FIRMWARE_FILE;
+ fim->fw_code = FIM_SERIAL_FIRMWARE_CODE;
+ retval = fim_register_driver(fim);
+ if (retval) {
+ printk_err("Couldn't register the FIM driver.\n");
+ return retval;
+ }
+
+ /* This is required for the FIM-firmware */
+ fim_set_ctrl_reg(fim, FIM_SERIAL_CTRL_REG, 0x00);
+
+ /* Setup the bit positions but disable the HW flow control first */
+ memcpy(port->gpios, gpios, FIM_SERIAL_MAX_GPIOS * sizeof(struct fim_gpio_t));
+ retval = fim_serial_setup_bitpos(port, 0);
+ if (retval) {
+ printk_err("Setting the default GPIOs\n");
+ goto err_unreg_fim;
+ }
+
+ retval = fim_serial_reset_matchs(fim);
+ if (retval) {
+ printk_err("Resetting the match registers\n");
+ goto err_unreg_fim;
+ }
+
+ /* Request the corresponding GPIOs (@XXX: Check the returned values) */
+ printk_debug("Requesting and configuring the GPIO's\n");
+ for (cnt=0; gpios[cnt].nr != FIM_LAST_GPIO; cnt++) {
+
+ if (gpios[cnt].nr == FIM_GPIO_DONT_USE)
+ continue;
+
+ printk_debug("Going to request the GPIO %i\n", gpios[cnt].nr);
+ retval = gpio_request(gpios[cnt].nr, FIM_DRIVER_NAME);
+ if (!retval) {
+
+ func = gpios[cnt].func;
+
+ gpio_configure_ns921x_unlocked(gpios[cnt].nr,
+ NS921X_GPIO_INPUT,
+ NS921X_GPIO_DONT_INVERT,
+ func,
+ NS921X_GPIO_ENABLE_PULLUP);
+ } else {
+ /* Free the already requested GPIOs */
+ printk_err("Couldn't request the GPIO %i\n", gpios[cnt].nr);
+ while (cnt) gpio_free(gpios[--cnt].nr);
+ goto err_free_gpios;
+ }
+ }
+
+ /* Get a reference to the SYS clock for setting the baudrate */
+ if (IS_ERR(port->sys_clk = clk_get(port->fim.dev, "systemclock"))) {
+ printk_err("Couldn't get the SYS clock.\n");
+ goto err_free_gpios;
+ }
+
+ /* Init the internal data before registering the UART port */
+ tasklet_init(&port->tasklet, fim_serial_tasklet_func, (unsigned long)port);
+
+ spin_lock_init(&port->tx_lock);
+ port->minor = minor;
+ port->reg = 1;
+ port->driver = &fim_serials->driver;
+
+ /*
+ * Register with the corresponding minor number
+ * IMPORTANT: Set the internal fim-serial structure as driver-data before
+ * calling the function for adding a new uart port, then in some functions
+ * the pointer to the FIM-port is obtained from the driver data
+ */
+ uart->ops = &fim_serial_ops;
+ uart->flags = UPF_BOOT_AUTOCONF;
+ uart->dev = dev;
+ uart->type = PORT_UNKNOWN;
+ uart->line = minor;
+ uart->iotype = UPIO_PORT;
+ uart->mapbase = 0x1234;
+ dev_set_drvdata(uart->dev, port);
+ if ((retval = uart_add_one_port(&fim_serials->driver, uart))) {
+ dev_set_drvdata(uart->dev, NULL);
+ printk_err("Couldn't register the UART port %i\n", minor);
+ goto err_put_clk;
+ }
+
+ return 0;
+
+ err_put_clk:
+ clk_put(port->sys_clk);
+
+ err_free_gpios:
+ for (cnt=0; gpios[cnt].nr < FIM_SERIAL_MAX_GPIOS; cnt++) {
+ if (gpios[cnt].nr == FIM_LAST_GPIO)
+ break;
+
+ if (gpios[cnt].nr != FIM_GPIO_DONT_USE)
+ gpio_free(gpios[cnt].nr);
+ }
+
+ err_unreg_fim:
+ fim_unregister_driver(&port->fim);
+
+ return retval;
+}
+
+/*
+ * Probe function
+ */
+static int __devinit fim_serial_probe(struct platform_device *pdev)
+{
+ struct fim_serial_t *port;
+ struct fim_gpio_t gpios[FIM_SERIAL_MAX_GPIOS];
+ int retval;
+ struct fim_serial_platform_data *pdata;
+
+ printk_info("Probe function called for device ID %i\n", pdev->id);
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -ENXIO;
+
+ if (fim_check_device_id(fims_number, pdata->fim_nr)) {
+#if defined(MODULE)
+ printk_dbg("Skipping FIM%i (not selected)\n", pdata->fim_nr);
+#else
+ printk_err("Invalid FIM number '%i' in platform data\n", pdata->fim_nr);
+#endif
+ return -ENODEV;
+ }
+
+ port = fim_serials->ports + pdata->fim_nr;
+
+ /* @XXX: The below code is really ugly, remove it! */
+ gpios[FIM_SERIAL_GPIO_RX].nr = pdata->rx_gpio_nr;
+ gpios[FIM_SERIAL_GPIO_RX].func = pdata->rx_gpio_func;
+ gpios[FIM_SERIAL_GPIO_TX].nr = pdata->tx_gpio_nr;
+ gpios[FIM_SERIAL_GPIO_TX].func = pdata->tx_gpio_func;
+ gpios[FIM_SERIAL_GPIO_CTS].nr = pdata->cts_gpio_nr;
+ gpios[FIM_SERIAL_GPIO_CTS].func = pdata->cts_gpio_func;
+ gpios[FIM_SERIAL_GPIO_RTS].nr = pdata->rts_gpio_nr;
+ gpios[FIM_SERIAL_GPIO_RTS].func = pdata->rts_gpio_func;
+ gpios[FIM_SERIAL_GPIO_LAST].nr = FIM_LAST_GPIO;
+
+ /* And try to register the FIM */
+ printk_debug("FIM %i | Port %p | Uart %p)\n", pdata->fim_nr, port, &port->uart);
+ retval = fim_serial_register_port(&pdev->dev, port,
+ pdata->fim_nr, pdata->fim_nr,
+ gpios);
+
+ return retval;
+}
+
+static int __devexit fim_serial_remove(struct platform_device *pdev)
+{
+ struct fim_serial_t *port;
+ int retval;
+
+ port = dev_get_drvdata(&pdev->dev);
+
+ retval = fim_serial_unregister_port(port);
+ if (!retval)
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return retval;
+}
+
+#if defined(CONFIG_PM)
+
+static int fim_serial_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ int ret;
+ struct fim_serial_t *port;
+
+ /* Only stop the FIM before entering the suspend mode */
+ port = dev_get_drvdata(&pdev->dev);
+ ret = fim_send_stop(&port->fim);
+ if (ret)
+ printk_err("Couldn't stop the FIM\n");
+
+ return ret;
+}
+
+static int fim_serial_resume(struct platform_device *pdev)
+{
+ struct fim_serial_t *port;
+ int retval;
+ struct fim_driver *fim;
+
+ port = dev_get_drvdata(&pdev->dev);
+ fim = &port->fim;
+
+ /*
+ * Reset the internal values for being able to restore the port settings,
+ * otherwise the port will not work correctly after the wakeup reset.
+ */
+ port->totalbits = 0;
+ port->numbits = 0;
+ port->last_totalbits = 0;
+ port->baud = 0;
+
+ retval = fim_download_firmware(&port->fim);
+ if (retval) {
+ printk_err("FIM download failed\n");
+ goto exit_resume;
+ }
+
+ /* This is required for the FIM-firmware */
+ fim_set_ctrl_reg(fim, FIM_SERIAL_CTRL_REG, 0xff);
+
+ retval = fim_send_start(&port->fim);
+ if (retval) {
+ printk_err("FIM start failed\n");
+ goto exit_resume;
+ }
+
+ fim_set_ctrl_reg(fim, FIM_SERIAL_CTRL_REG, 0x00);
+
+ retval = fim_serial_setup_bitpos(port, 0);
+ if (retval) {
+ printk_err("Setting the default GPIOs\n");
+ goto exit_resume;
+ }
+
+ retval = fim_serial_reset_matchs(fim);
+ if (retval) {
+ printk_err("Resetting the match registers\n");
+ goto exit_resume;
+ }
+
+ /*
+ * If the port was not opened yet, then no termios will be available, so
+ * only return at this point.
+ */
+ if (port->termios) {
+ retval = fim_serial_configure_port(port, port->termios, NULL);
+ if (retval)
+ printk_err("Port configuration failed\n");
+ }
+
+ exit_resume:
+ return retval;
+}
+#else
+#define fim_serial_suspend NULL
+#define fim_serial_resume NULL
+#endif /* CONFIG_PM */
+
+/* @XXX: Work with hotplug and coldplug? */
+MODULE_ALIAS(FIM_PLATFORM_DRIVER_NAME);
+
+static struct platform_driver fim_serial_platform_driver = {
+ .probe = fim_serial_probe,
+ .remove = __devexit_p(fim_serial_remove),
+ .suspend = fim_serial_suspend,
+ .resume = fim_serial_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = FIM_DRIVER_NAME,
+ },
+};
+
+static int __init fim_serial_init(void)
+{
+ int retval;
+ int nrpics;
+
+ printk_debug("Starting the FIM serial driver.\n");
+
+ /* Get the number of maximal available FIMs */
+ nrpics = fim_number_pics();
+
+ /* Check for the passed number parameter */
+ if (fim_check_numbers_param(fims_number)) {
+ printk_err("Invalid number '%i' of FIMs to handle\n", fims_number);
+ return -EINVAL;
+ }
+
+ fim_serials = kzalloc(sizeof(struct fim_serials_t) +
+ (nrpics * sizeof(struct fim_serial_t)), GFP_KERNEL);
+ if (!fim_serials)
+ return -ENOMEM;
+
+ fim_serials->fims = nrpics;
+ fim_serials->ports = (void *)fim_serials + sizeof(struct fim_serials_t);
+
+ /* Init the UART driver */
+ fim_serials->driver.owner = THIS_MODULE;
+ fim_serials->driver.driver_name = FIM_DRIVER_NAME;
+ fim_serials->driver.dev_name = FIM_SERIAL_DEV_NAME;
+ fim_serials->driver.nr = nrpics;
+
+ /* @FIXME: This is ugly! */
+#if defined(CONFIG_SERIAL_FIM_CONSOLE)
+ fim_console_ptr->data = &fim_serials->driver;
+#endif
+
+ fim_serials->driver.cons = fim_console_ptr;
+
+ printk_debug("Going to register the driver\n");
+ retval = uart_register_driver(&fim_serials->driver);
+ if (retval)
+ goto err_free_mem;
+
+ printk_debug("Trying to register the platform driver\n");
+ retval = platform_driver_register(&fim_serial_platform_driver);
+ if (retval)
+ goto err_unreg_uart;
+
+ printk_info(DRIVER_DESC " v" DRIVER_VERSION "\n");
+ return 0;
+
+ err_unreg_uart:
+ uart_unregister_driver(&fim_serials->driver);
+
+ err_free_mem:
+ kfree(fim_serials);
+
+ return retval;
+}
+
+static void __exit fim_serial_exit(void)
+{
+ printk_info("Removing the FIM serial driver\n");
+
+ platform_driver_unregister(&fim_serial_platform_driver);
+ uart_unregister_driver(&fim_serials->driver);
+
+ kfree(fim_serials);
+}
+
+module_init(fim_serial_init);
+module_exit(fim_serial_exit);
+
diff --git a/drivers/fims/serial/fim_serial.h b/drivers/fims/serial/fim_serial.h
new file mode 100644
index 000000000000..7ddf3f243b30
--- /dev/null
+++ b/drivers/fims/serial/fim_serial.h
@@ -0,0 +1,155 @@
+
+/*
+ * Automatic generated header file with the firmware code for the FIM
+ * Input binary : fim_firmware.bin
+ * Output header : fim_uart.h
+ * Structure : fim_serial_firmware
+ */
+
+static const unsigned char fim_serial_firmware[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x16, 0x02, 0x00, 0x00, 0x62, 0x28, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x03, 0x08,
+ 0x83, 0x12, 0xa1, 0x00, 0x8b, 0x10, 0x83, 0x16,
+ 0x09, 0x18, 0x19, 0x28, 0x89, 0x18, 0x21, 0x28,
+ 0x09, 0x19, 0x2f, 0x28, 0x89, 0x19, 0x33, 0x28,
+ 0x09, 0x1a, 0x39, 0x28, 0x89, 0x1a, 0x4f, 0x28,
+ 0x09, 0x1b, 0x57, 0x28, 0x18, 0x28, 0x83, 0x12,
+ 0x18, 0x14, 0x10, 0x08, 0xc0, 0x00, 0x11, 0x08,
+ 0xc1, 0x00, 0x25, 0x14, 0x5b, 0x28, 0x83, 0x12,
+ 0x10, 0x08, 0xc2, 0x00, 0x11, 0x08, 0xc3, 0x00,
+ 0x12, 0x08, 0xc4, 0x00, 0x13, 0x08, 0xc5, 0x00,
+ 0x14, 0x08, 0xc6, 0x00, 0x15, 0x08, 0xc7, 0x00,
+ 0x5b, 0x28, 0x83, 0x12, 0x10, 0x08, 0xc8, 0x00,
+ 0x5b, 0x28, 0x83, 0x12, 0x10, 0x08, 0xb5, 0x00,
+ 0x11, 0x08, 0xb6, 0x00, 0x5b, 0x28, 0x83, 0x12,
+ 0x10, 0x08, 0xbc, 0x00, 0x11, 0x08, 0xbd, 0x00,
+ 0x12, 0x08, 0xbe, 0x00, 0x13, 0x08, 0xbf, 0x00,
+ 0x3c, 0x08, 0x83, 0x16, 0x98, 0x00, 0x83, 0x12,
+ 0xa9, 0x00, 0x3e, 0x08, 0xa9, 0x04, 0x29, 0x08,
+ 0xff, 0x3a, 0x83, 0x16, 0x99, 0x00, 0x83, 0x12,
+ 0x5b, 0x28, 0x83, 0x12, 0x10, 0x08, 0x07, 0x39,
+ 0x83, 0x16, 0x81, 0x00, 0x01, 0x17, 0x83, 0x12,
+ 0x5b, 0x28, 0x83, 0x12, 0x10, 0x08, 0xc9, 0x00,
+ 0x5b, 0x28, 0x89, 0x17, 0xa4, 0x14, 0x21, 0x08,
+ 0x83, 0x00, 0xa0, 0x0e, 0x20, 0x0e, 0x09, 0x00,
+ 0x8b, 0x17, 0x0b, 0x16, 0x83, 0x16, 0x01, 0x17,
+ 0x83, 0x12, 0x04, 0x22, 0x98, 0x01, 0xb0, 0x30,
+ 0xb8, 0x00, 0xb9, 0x00, 0xa4, 0x18, 0xf1, 0x21,
+ 0x16, 0x1c, 0x6c, 0x28, 0x71, 0x28, 0x7e, 0x20,
+ 0x86, 0x28, 0x4e, 0x21, 0x7e, 0x20, 0x4e, 0x21,
+ 0x7e, 0x20, 0x4e, 0x21, 0x24, 0x18, 0xe8, 0x21,
+ 0xa4, 0x18, 0xf1, 0x21, 0x7e, 0x20, 0x08, 0x00,
+ 0x22, 0x1c, 0x82, 0x28, 0xa2, 0x20, 0x08, 0x00,
+ 0xa2, 0x19, 0x21, 0x21, 0xfd, 0x20, 0x08, 0x00,
+ 0x23, 0x1c, 0x8a, 0x28, 0x79, 0x21, 0x71, 0x28,
+ 0x73, 0x20, 0x16, 0x1d, 0x93, 0x28, 0x3f, 0x08,
+ 0x83, 0x16, 0x10, 0x05, 0x83, 0x12, 0x03, 0x1d,
+ 0x71, 0x28, 0x25, 0x1c, 0x9d, 0x28, 0x40, 0x08,
+ 0xaa, 0x00, 0x41, 0x08, 0xb7, 0x00, 0x25, 0x10,
+ 0x18, 0x10, 0xc7, 0x21, 0x71, 0x28, 0x96, 0x1c,
+ 0x71, 0x28, 0x8c, 0x1d, 0xbf, 0x21, 0x71, 0x28,
+ 0x01, 0x08, 0xa8, 0x00, 0x22, 0x1d, 0xab, 0x28,
+ 0xa8, 0x1b, 0xab, 0x28, 0xa2, 0x1c, 0xb5, 0x28,
+ 0xa2, 0x10, 0x22, 0x11, 0xa8, 0x1b, 0x22, 0x15,
+ 0xa2, 0x18, 0x08, 0x00, 0x31, 0x08, 0x28, 0x02,
+ 0x03, 0x18, 0xb5, 0x28, 0x08, 0x00, 0x22, 0x11,
+ 0xb1, 0x1b, 0x22, 0x15, 0x49, 0x08, 0xb1, 0x07,
+ 0x03, 0x1c, 0xbd, 0x28, 0xa2, 0x14, 0x22, 0x1e,
+ 0xc1, 0x28, 0x22, 0x12, 0x08, 0x00, 0x03, 0x10,
+ 0x3d, 0x08, 0x83, 0x16, 0x10, 0x05, 0x83, 0x12,
+ 0x03, 0x19, 0xc9, 0x28, 0x03, 0x14, 0xab, 0x0c,
+ 0xad, 0x0b, 0xe1, 0x28, 0x23, 0x18, 0x79, 0x21,
+ 0x2b, 0x08, 0xae, 0x00, 0x96, 0x1d, 0xd7, 0x28,
+ 0x2b, 0x08, 0x46, 0x05, 0x42, 0x02, 0x03, 0x19,
+ 0x26, 0x14, 0x16, 0x1e, 0xde, 0x28, 0x2b, 0x08,
+ 0x46, 0x05, 0x44, 0x02, 0x03, 0x19, 0xa6, 0x14,
+ 0xab, 0x01, 0x08, 0x30, 0xad, 0x00, 0xac, 0x0b,
+ 0x08, 0x00, 0x23, 0x18, 0x79, 0x21, 0xa2, 0x15,
+ 0x35, 0x08, 0xb3, 0x00, 0x36, 0x08, 0xb4, 0x00,
+ 0x22, 0x10, 0x26, 0x1c, 0xf4, 0x28, 0x26, 0x10,
+ 0x2b, 0x08, 0x47, 0x05, 0x43, 0x02, 0x03, 0x19,
+ 0xe2, 0x21, 0x08, 0x00, 0xa6, 0x1c, 0x08, 0x00,
+ 0xa6, 0x10, 0x2b, 0x08, 0x47, 0x05, 0x45, 0x02,
+ 0x03, 0x19, 0xe5, 0x21, 0x08, 0x00, 0x3d, 0x08,
+ 0x83, 0x16, 0x10, 0x05, 0x83, 0x12, 0x03, 0x1d,
+ 0x20, 0x29, 0x01, 0x08, 0xa8, 0x00, 0x23, 0x18,
+ 0x79, 0x21, 0xa2, 0x1d, 0x0a, 0x29, 0x29, 0x21,
+ 0xa2, 0x01, 0x22, 0x14, 0x22, 0x16, 0xa8, 0x1b,
+ 0x22, 0x15, 0x48, 0x08, 0xac, 0x00, 0xac, 0x19,
+ 0x08, 0x30, 0xad, 0x00, 0xab, 0x01, 0x03, 0x10,
+ 0x49, 0x0c, 0xa9, 0x00, 0x0a, 0x30, 0x29, 0x02,
+ 0x28, 0x07, 0xb1, 0x00, 0x03, 0x1c, 0x1f, 0x29,
+ 0xa2, 0x14, 0x08, 0x00, 0x08, 0x00, 0xb3, 0x0b,
+ 0x08, 0x00, 0xb4, 0x0b, 0x08, 0x00, 0xa2, 0x11,
+ 0x2b, 0x14, 0x29, 0x21, 0x08, 0x00, 0x38, 0x08,
+ 0xba, 0x00, 0xba, 0x0a, 0x03, 0x1d, 0x30, 0x29,
+ 0xb0, 0x30, 0xba, 0x00, 0x3a, 0x08, 0x39, 0x02,
+ 0x03, 0x1d, 0x36, 0x29, 0xdd, 0x21, 0x08, 0x00,
+ 0x3a, 0x08, 0xbb, 0x00, 0xbb, 0x0a, 0x03, 0x1d,
+ 0x3d, 0x29, 0xb0, 0x30, 0xbb, 0x00, 0x3b, 0x08,
+ 0x39, 0x02, 0x03, 0x1d, 0x43, 0x29, 0xdd, 0x21,
+ 0x08, 0x00, 0x38, 0x08, 0x84, 0x00, 0x2e, 0x08,
+ 0x80, 0x00, 0x3a, 0x08, 0x84, 0x00, 0x2b, 0x08,
+ 0x80, 0x00, 0x3b, 0x08, 0xb8, 0x00, 0x08, 0x00,
+ 0x8c, 0x1b, 0x74, 0x29, 0x3e, 0x08, 0xff, 0x3a,
+ 0x83, 0x16, 0x98, 0x05, 0x83, 0x12, 0x39, 0x08,
+ 0x38, 0x02, 0x03, 0x19, 0x08, 0x00, 0x39, 0x08,
+ 0x84, 0x00, 0x00, 0x08, 0x39, 0x1c, 0x67, 0x29,
+ 0x00, 0x1c, 0x67, 0x29, 0x8b, 0x13, 0x00, 0x00,
+ 0x8e, 0x0a, 0x8d, 0x00, 0x00, 0x00, 0x8b, 0x17,
+ 0x6e, 0x29, 0x8b, 0x13, 0x00, 0x00, 0x8e, 0x01,
+ 0x8d, 0x00, 0x00, 0x00, 0x8b, 0x17, 0x6e, 0x29,
+ 0xb9, 0x0a, 0x03, 0x1d, 0x08, 0x00, 0xb0, 0x30,
+ 0xb9, 0x00, 0x08, 0x00, 0x3e, 0x08, 0x83, 0x16,
+ 0x98, 0x04, 0x83, 0x12, 0x08, 0x00, 0x01, 0x08,
+ 0xa7, 0x00, 0x23, 0x1d, 0x82, 0x29, 0xa7, 0x1b,
+ 0x82, 0x29, 0xa3, 0x1c, 0x8c, 0x29, 0xa3, 0x10,
+ 0x23, 0x11, 0xa7, 0x1b, 0x23, 0x15, 0xa3, 0x18,
+ 0x08, 0x00, 0x27, 0x08, 0x32, 0x02, 0x03, 0x1c,
+ 0x8c, 0x29, 0x08, 0x00, 0xa3, 0x1d, 0xa0, 0x29,
+ 0x3c, 0x08, 0x83, 0x16, 0x98, 0x04, 0x83, 0x12,
+ 0x23, 0x1a, 0x9e, 0x29, 0x23, 0x16, 0x23, 0x11,
+ 0xb2, 0x1b, 0x23, 0x15, 0x49, 0x08, 0xb2, 0x07,
+ 0x03, 0x1c, 0x9d, 0x29, 0xa3, 0x14, 0x08, 0x00,
+ 0x23, 0x10, 0x08, 0x00, 0x3c, 0x08, 0x2a, 0x1c,
+ 0xa7, 0x29, 0x83, 0x16, 0x98, 0x04, 0x83, 0x12,
+ 0xab, 0x29, 0xff, 0x3a, 0x83, 0x16, 0x98, 0x05,
+ 0x83, 0x12, 0xaa, 0x0c, 0x23, 0x11, 0xb2, 0x1b,
+ 0x23, 0x15, 0x49, 0x08, 0xb2, 0x07, 0x03, 0x1c,
+ 0xb4, 0x29, 0xa3, 0x14, 0xaf, 0x0b, 0xb8, 0x29,
+ 0xa3, 0x15, 0x08, 0x00, 0xb0, 0x0b, 0x08, 0x00,
+ 0x37, 0x08, 0xaa, 0x00, 0x08, 0x30, 0xb0, 0x00,
+ 0x08, 0x00, 0xfc, 0x21, 0xaa, 0x00, 0x09, 0x30,
+ 0x48, 0x02, 0x03, 0x1c, 0xc7, 0x29, 0xfc, 0x21,
+ 0xb7, 0x00, 0x48, 0x08, 0xaf, 0x00, 0x08, 0x30,
+ 0xb0, 0x00, 0xa3, 0x01, 0x23, 0x14, 0x01, 0x08,
+ 0xb2, 0x00, 0x23, 0x11, 0xb2, 0x1b, 0x23, 0x15,
+ 0x49, 0x08, 0xb2, 0x07, 0x03, 0x1c, 0xd7, 0x29,
+ 0xa3, 0x14, 0x3c, 0x08, 0xff, 0x3a, 0x83, 0x16,
+ 0x98, 0x05, 0x83, 0x12, 0x08, 0x00, 0x24, 0x18,
+ 0x08, 0x00, 0x09, 0x15, 0x24, 0x14, 0x08, 0x00,
+ 0x09, 0x14, 0x24, 0x14, 0x08, 0x00, 0x89, 0x14,
+ 0x24, 0x14, 0x08, 0x00, 0x83, 0x16, 0x89, 0x1f,
+ 0xfa, 0x29, 0x83, 0x12, 0x17, 0x08, 0xff, 0x3a,
+ 0x89, 0x05, 0x24, 0x10, 0x08, 0x00, 0x83, 0x16,
+ 0x09, 0x08, 0x7f, 0x39, 0x03, 0x1d, 0xfa, 0x29,
+ 0x83, 0x12, 0x89, 0x13, 0xa4, 0x10, 0x08, 0x00,
+ 0x83, 0x12, 0x08, 0x00, 0x8c, 0x19, 0xfc, 0x29,
+ 0x8b, 0x13, 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00,
+ 0x8b, 0x17, 0x08, 0x00, 0x83, 0x12, 0x20, 0x30,
+ 0x84, 0x00, 0x80, 0x01, 0x84, 0x0a, 0x04, 0x08,
+ 0x80, 0x3c, 0x03, 0x1d, 0x07, 0x2a, 0xa0, 0x30,
+ 0x84, 0x00, 0x80, 0x01, 0x84, 0x0a, 0x04, 0x08,
+ 0xff, 0x3c, 0x03, 0x1d, 0x0f, 0x2a, 0x08, 0x00
+
+};
+
diff --git a/drivers/fims/usb/Makefile b/drivers/fims/usb/Makefile
new file mode 100644
index 000000000000..b9b97fa4d6b1
--- /dev/null
+++ b/drivers/fims/usb/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the kernel FIM device drivers
+#
+
+ifneq ($(CONFIG_FIM_USB_MODULE),)
+ obj-m += fim_usb.o
+else
+ obj-$(CONFIG_FIM_ZERO_USB) += fim_usb.o
+ obj-$(CONFIG_FIM_ONE_USB) += fim_usb.o
+endif
diff --git a/drivers/fims/usb/README b/drivers/fims/usb/README
new file mode 100644
index 000000000000..55d7091ab467
--- /dev/null
+++ b/drivers/fims/usb/README
@@ -0,0 +1,12 @@
+
+README
+------
+
+[HOST]
+* Need to add a rule to udev for changing the write access to the
+ different device nodes (per default only root has write access)
+* Use the python script for writing to the EP by using libusb
+
+[TARGET]
+* Add a hotplug rull for starting an application that reads the
+ data from the serial port
diff --git a/drivers/fims/usb/fim_usb.c b/drivers/fims/usb/fim_usb.c
new file mode 100644
index 000000000000..2292c4c406a9
--- /dev/null
+++ b/drivers/fims/usb/fim_usb.c
@@ -0,0 +1,2030 @@
+/* -*- linux-c -*-
+ *
+ * driver/fims/usb/fim_usb.c
+ *
+ * Copyright (C) 2009 Digi International 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/reboot.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/completion.h>
+
+#include <mach/fim-ns921x.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/regs-iohub-ns921x.h>
+
+/* #include "fim_usb.h" */
+/*
+ * If the driver is being compiled as a built-in driver, then include the header file
+ * which contains the firmware for this FIM-device
+ */
+#if !defined(MODULE)
+#include "fim_usb.h"
+extern const unsigned char fim_usb_firmware[];
+#define FIM_USB_FIRMWARE_FILE (NULL)
+#define FIM_USB_FIRMWARE_CODE fim_usb_firmware
+#else
+const unsigned char *fim_usb_firmware = NULL;
+#define FIM_USB_FIRMWARE_FILE "fim_usb.bin"
+#define FIM_USB_FIRMWARE_CODE fim_usb_firmware
+#endif
+
+/* Driver informations */
+#define DRIVER_VERSION "1.0"
+#define FIM_DRIVER_AUTHOR "Hector Oron, Luis Galdos"
+#define FIM_DRIVER_DESC "FIM USB serial device driver"
+#define FIM_DRIVER_NAME "fim-usb"
+#define FIM_USB_UART_DEV_NAME "ttyFUSB"
+
+/* Module parameters */
+NS921X_FIM_NUMBERS_PARAM(fims_number);
+
+/* Macros used for the USB descriptors and resources */
+#define FIM_USB_MANU "Digi"
+#define FIM_USB_PROD_DESC "FIM-USBx" /* @FIXME: Cant change to an odd length */
+#define FIM_USB_SERIALNR "NS921X"
+#define FIM_USB_IN_EPNR (USB_DIR_IN | FIM_USB_INT_EP_NR)
+#define FIM_USB_OUT_EPNR (USB_DIR_OUT | FIM_USB_INT_EP_NR)
+
+/* MAX GPIO NUMBER */
+#define FIM_USB_MAX_GPIOS (6)
+
+#define FIM_USB_GPIO_DP (0)
+#define FIM_USB_GPIO_DM (1)
+#define FIM_USB_GPIO_RCV (2)
+#define FIM_USB_GPIO_OE (3)
+#define FIM_USB_GPIO_ENUM (4)
+#define FIM_USB_GPIO_SPND (5)
+
+/* Macros for controlling the enumeration GPIO of the MAX */
+#define FIM_USB_GPIO_ENUM_ENABLE (1)
+#define FIM_USB_GPIO_ENUM_DISABLE (0)
+
+
+#define FIM_USB_MAX_PACK_SIZE (8)
+#define FIM_USB_INT_EP_NR (1)
+
+
+/* Firmware dependent status/error flags */
+#define FIM_USB_BIT_STUFF_ERROR (1 << 0)
+#define FIM_USB_CRC_ERROR (1 << 1)
+#define FIM_USB_RX_OVERFLOW (1 << 2)
+#define FIM_USB_DEV_ADDR_NO_MATCH (1 << 3)
+#define FIM_USB_SYNC_ERROR (1 << 4)
+#define FIM_USB_IN_DATA_SENT_MARKER (1 << 5)
+#define FIM_USB_BUS_RESET (1 << 6)
+#define FIM_USB_KEEP_ALIVE (1 << 7)
+
+/* When a data packet is received it includes the sync, token, CRC, etc. */
+#define FIM_USB_DATA_OVERHEAD (5)
+#define FIM_USB_DATA_HEAD (2)
+#define FIM_USB_DATA_TAIL (3)
+
+/*
+ * Since the FIMs doesn't support the CRC calculation and other additional features
+ * (like the setup of the different tokens), we must create the complete USB frame
+ * before putting it into the DMA-buffer (sucks!). So, we need the below token values
+ * for each frame.
+ *
+ * (Luis Galdos)
+ */
+#define USB_TOKEN_IN (0x69)
+#define USB_TOKEN_OUT (0xe1)
+#define USB_TOKEN_DATA0 (0xc3)
+#define USB_TOKEN_DATA1 (0x4b)
+#define USB_TOKEN_SETUP (0x2d)
+#define USB_TOKEN_SYNC (0x80)
+
+/* Configuration status registers of the FIM firmware */
+#define FIM_USB_ADDR_REG 2
+#define FIM_USB_CONTROL3 3
+#define FIM_USB_CONTROL4 4
+#define FIM_USB_CONTROL5 5
+#define FIM_USB_CONTROL6 6
+
+#define FIM_USB_CTRL10_REG (10)
+#define FIM_USB_STAT10_REG (10)
+#define FIM_USB_CTRL3_REG (3)
+#define FIM_USB_STAT3_REG (3)
+#define FIM_USB_ALIVE_COUNT_REG FIM_USB_CTRL10_REG
+#define FIM_USB_ALIVE_COUNT_STAT FIM_USB_STAT10_REG
+
+/* Main control register */
+#define FIM_USB_MAIN_REG (7)
+#define FIM_USB_MAIN_START (1 << 0)
+#define FIM_USB_MAIN_NOKEEPALIVE (1 << 1)
+#define FIM_USB_MAIN_NOINIRQ (1 << 2)
+#define FIM_USB_MAIN_ALIVE_COUNT (1 << 3)
+
+#define FIM_USB_REVISION_REG (0)
+
+/* Macros for the configuration of the DMA-channel */
+#define FIM_USB_DMA_BUFFER_SIZE (512)
+#define FIM_USB_DMA_RX_BUFFERS (61)
+#define FIM_USB_DMA_TX_BUFFERS (4)
+
+/* For printing messages (errors, warning, etc.) */
+#define pk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] fim-usb: " fmt, ## args)
+#define pk_info(fmt, args...) printk(KERN_INFO "fim-usb: " fmt, ## args)
+#define pk_dbg(fmt, args...) printk(KERN_DEBUG "fim-usb: " fmt, ## args)
+#define pk_dump_ctrl_packet(p) printk(KERN_DEBUG "bRequestType 0x%02x | bRequest 0x%02x | " \
+ "wValue 0x%04x | wIndex 0x%04x | wLength %u\n", \
+ p->bRequestType, p->bRequest, \
+ p->wValue, p->wIndex, p->wLength);
+
+#if 0
+#define FIM_USB_DEBUG
+#endif
+
+/* Macros for printing debug messages */
+#ifdef FIM_USB_DEBUG
+#define dbg_func(fmt, args...) printk(KERN_DEBUG "fim-usb: %s() " fmt, __func__, ## args)
+#define dbg_naked(fmt, args...) printk(KERN_DEBUG "fim-usb: " fmt, ## args)
+#define dbg_pr(fmt, args...) printk(KERN_DEBUG "fim-usb: " fmt, ## args)
+#else
+#define dbg_pr(fmt, args...) do { } while (0)
+#define dbg_naked(fmt, args...) do { } while (0)
+#define dbg_func(fmt, args...) do { } while (0)
+#endif
+
+/* Enable the debug messages for the UART operations/functions */
+#if 0
+#define FIM_USB_UART_DEBUG
+#endif
+
+#ifdef FIM_USB_UART_DEBUG
+#define dbg_uart(fmt, args...) printk(KERN_DEBUG "fim-usb UART: " fmt, ## args)
+#else
+#define dbg_uart(fmt, args...) do { } while (0)
+#endif
+
+/* Enable the debug messages for the enumeration sequence */
+#if 0
+#define FIM_USB_ENUM_DEBUG
+#endif
+
+#ifdef FIM_USB_ENUM_DEBUG
+#define dbg_enum(fmt, args...) printk(KERN_DEBUG "fim-usb ENUM: " fmt, ## args)
+#else
+#define dbg_enum(fmt, args...) do { } while (0)
+#endif
+
+/* Enable the debug messages for the received tokens */
+#if 0
+#define FIM_USB_TOKEN_DEBUG
+#endif
+
+#ifdef FIM_USB_TOKEN_DEBUG
+#define dbg_token(fmt, args...) printk(KERN_DEBUG "fim-usb TN: " fmt, ## args)
+#else
+#define dbg_token(fmt, args...) do { } while (0)
+#endif
+
+/* Enable the debug messages for the received tokens */
+#if 0
+#define FIM_USB_WRITE_DEBUG
+#endif
+
+#ifdef FIM_USB_WRITE_DEBUG
+#define dbg_write(fmt, args...) printk(KERN_DEBUG "fim-usb WRITE: " fmt, ## args)
+#else
+#define dbg_write(fmt, args...) do { } while (0)
+#endif
+
+#if 0
+#define FIM_USB_FORCE_ENUM_TEST
+#endif
+
+/* Data structure used for passing a data buffer to the FIM core */
+struct fim_usb_in_frame {
+ u8 epnr;
+ u8 bytes;
+ u8 token;
+ u8 data[32];
+ u16 *crc;
+}__attribute__((__packed__));
+
+/*
+ * Let's use this structure for handling the received FIM-buffers
+ *
+ */
+struct fim_usb_token {
+ unsigned long type:8;
+ unsigned long addr:7;
+ unsigned long ep:4;
+} __attribute__((packed));
+
+/* Structure for the data received after the OUT tokens */
+struct fim_usb_data {
+ u8 sync; /* Sync token 0x80 */
+ u8 token; /* DATA0 or DATA1 */
+ u8 data[32]; /* @XXX: Quick and dirty! Need a macro for this value */
+} __attribute__((packed));
+
+struct fim_usb_ep {
+ u8 nr;
+ u8 addr;
+
+ u16 bytes;
+ u16 requested;
+ u8 *data;
+
+ u8 zlp;
+ u8 last_data_token;
+
+ struct fim_usb_port *port;
+
+ struct work_struct tx_work; /* Worker for the IN transfers */
+ struct uart_port *tx_uart;
+ struct semaphore tx_sem;
+};
+
+struct fim_usb_port {
+ int index;
+ struct fim_driver fim;
+ struct device *dev;
+ struct fim_gpio_t gpios[FIM_USB_MAX_GPIOS];
+ struct clk *clk;
+ int reg;
+
+ /* We have three endpoints: control and two interrupts */
+#define FIM_USB_NR_EP_CTRL (0)
+#define FIM_USB_NR_EP_OUT (1)
+#define FIM_USB_NR_EP_IN (2)
+#define FIM_USB_NR_EPS (3)
+ struct fim_usb_ep eps[FIM_USB_NR_EPS];
+ int ep_next_data;
+
+ /* Data used for the emulation of the serial port */
+ struct uart_port uart;
+ struct work_struct uart_work;
+ int uart_created;
+ struct semaphore uart_sem;
+
+ /* Used for restarting the FIM after heavy failures */
+ struct work_struct restart_work;
+ struct completion restart_completed;
+
+ /* Timer used for detecting the hotplug events */
+ struct delayed_work hotplug_work;
+ int unplug_detected;
+#define FIM_USB_HOTPLUG_ALIVE_TIMER (HZ / 5)
+#define FIM_USB_HOTPLUG_ALIVE_COUNTER (150)
+};
+
+#define FIM_USB_STRING_MAX_LEN (32)
+
+struct fim_usb_string {
+ u8 id;
+ const char *s;
+
+ /* This data will be send to the host */
+ struct usb_descriptor_header desc;
+ u16 data[FIM_USB_STRING_MAX_LEN];
+} __attribute__((packed));
+
+/* The default endpoint descriptors include the members for the audio extension (see include/linux/usb/ch9.h) */
+struct usb_endpoint_descriptor2 {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u8 bEndpointAddress;
+ __u8 bmAttributes;
+ __le16 wMaxPacketSize;
+ __u8 bInterval;
+} __attribute__ ((packed));
+
+/* Let's keep it simple by using a static configuration descriptor */
+struct fim_usb_config_descriptor {
+ struct usb_config_descriptor config;
+ struct usb_interface_descriptor interface;
+ struct usb_endpoint_descriptor2 ep_in;
+ struct usb_endpoint_descriptor2 ep_out;
+} __attribute__((__packed__));
+
+
+/* Prototypes */
+static void fim_usb_regs_reset(struct fim_usb_port *port);
+static void fim_usb_uart_tx_work_func(struct work_struct *work);
+static int fim_usb_reboot_notifier_func(struct notifier_block *this, unsigned long code,
+ void *unused);
+static int fim_usb_restart_fim(struct fim_usb_port *port);
+
+static struct uart_driver fim_usb_uart_driver;
+static struct fim_usb_port **fim_usb_ports;
+static int fim_usb_ports_max;
+static struct notifier_block fim_usb_reboot_notifier = {
+ .notifier_call = fim_usb_reboot_notifier_func,
+ .next = NULL,
+ .priority = 0
+};
+
+static inline struct fim_usb_port *port_from_fim(struct fim_driver *fim)
+{
+ return fim->driver_data;
+}
+
+static inline struct tty_struct *tty_from_port(struct fim_usb_port *port)
+{
+ struct uart_info *info;
+
+ info = port->uart.info;
+ return (info) ? info->port.tty : NULL;
+}
+
+static inline struct fim_usb_port *port_from_uart(struct uart_port *uart)
+{
+ return dev_get_drvdata(uart->dev);
+}
+
+static void fim_usb_enable_pullup(struct fim_usb_port *port)
+{
+ dbg_func("FIM %i\n", port->index);
+
+ /*
+ * Enable the pulldown for the enumeration process and remove the suspend
+ * control for starting the externa PHY. The other GPIOs are controller by
+ * the FIM, specially the Output Enable.
+ */
+ gpio_set_value(port->gpios[FIM_USB_GPIO_SPND].nr, 0);
+ gpio_set_value(port->gpios[FIM_USB_GPIO_ENUM].nr, 1);
+}
+
+static void fim_usb_disable_pullup(struct fim_usb_port *port)
+{
+ dbg_func("FIM %i\n", port->index);
+
+ /* Disable the pulldown and set the PHY in the suspend mode */
+ gpio_set_value(port->gpios[FIM_USB_GPIO_ENUM].nr, 0);
+ gpio_set_value(port->gpios[FIM_USB_GPIO_SPND].nr, 1);
+}
+
+static void fim_usb_regs_reset(struct fim_usb_port *port)
+{
+ /* FIM control registers configuration */
+ fim_set_ctrl_reg(&port->fim, 0, 0);
+ fim_set_ctrl_reg(&port->fim, 1, 0);
+ fim_set_ctrl_reg(&port->fim, 2, 0);
+ fim_set_ctrl_reg(&port->fim, 3, 0);
+ fim_set_ctrl_reg(&port->fim, 4, 0);
+ fim_set_ctrl_reg(&port->fim, 5, 0);
+ fim_set_ctrl_reg(&port->fim, 6, 0);
+ fim_set_ctrl_reg(&port->fim, FIM_USB_MAIN_REG, 0);
+}
+
+/*
+ * According to the function[1] of the NetOS driver, we must first configure the
+ * dedicated GPIOs
+ *
+ * [1] iop_config.c:naIopUsbInit()
+ */
+static int fim_usb_config(struct fim_usb_port *port)
+{
+ unsigned long clks_per_bit;
+ unsigned long timer0_init_value;
+ struct fim_driver *fim;
+ int offset;
+ struct fim_gpio_t *gpios;
+
+ fim = &port->fim;
+ gpios = port->gpios;
+
+ /* Depending on the processor we have different offset */
+ if (processor_is_ns9215())
+ offset = 68;
+ else if (processor_is_ns9210())
+ offset = 0;
+ else
+ return -EINVAL;
+
+ /* FIM control registers configuration */
+ fim_set_ctrl_reg(fim, 3, 1 << (gpios[FIM_USB_GPIO_DP].nr - offset));
+ fim_set_ctrl_reg(fim, 4, 1 << (gpios[FIM_USB_GPIO_DM].nr - offset));
+ fim_set_ctrl_reg(fim, 5, 1 << (gpios[FIM_USB_GPIO_RCV].nr - offset));
+ fim_set_ctrl_reg(fim, 6, 1 << (gpios[FIM_USB_GPIO_OE].nr - offset));
+
+ /* Calculate timer setting */
+ /* FIM init Configuration. XXX port->clk must be MHz */
+ clks_per_bit = clk_get_rate(port->clk) / 1000000;
+ clks_per_bit = (clks_per_bit * 2) / 3;
+ dbg_pr("Clocks Per Bit: 0x%lx [Sysclock %lu]\n", clks_per_bit,
+ clk_get_rate(port->clk));
+
+ /* Sanity check for clock */
+ if ((clks_per_bit > 270) || (clks_per_bit < 0)) {
+ pk_err("Invalid calculated clocks per bit %lu\n", clks_per_bit);
+ return -EINVAL;
+ }
+
+ /* Load values according to the NetOS driver */
+ fim_set_ctrl_reg(&port->fim, 0, 231);
+
+ timer0_init_value = 256 - (clks_per_bit - 11);
+ fim_set_ctrl_reg(&port->fim, 1, timer0_init_value);
+
+ /* Set the initial device address to zero */
+ fim_set_ctrl_reg(&port->fim, FIM_USB_ADDR_REG, 0);
+
+ return 0;
+}
+
+/* We only provide two interrupt endpoints and one control EP */
+static void fim_usb_init_eps(struct fim_usb_port *port)
+{
+ struct fim_usb_ep *ep;
+
+ memset(&port->eps, 0, sizeof(port->eps));
+
+ /* Setup the control EP0 */
+ ep = &port->eps[FIM_USB_NR_EP_CTRL];
+ ep->addr = 0;
+ ep->port = port;
+ ep->nr = FIM_USB_NR_EP_CTRL;
+
+ /* Setup the OUT EP1 */
+ ep = &port->eps[FIM_USB_NR_EP_OUT];
+ ep->addr = FIM_USB_INT_EP_NR;
+ ep->port = port;
+ ep->nr = FIM_USB_NR_EP_OUT;
+
+ /* Setup the IN EP1. Init the worker for the IN transfers too! */
+ ep = &port->eps[FIM_USB_NR_EP_IN];
+ ep->addr = FIM_USB_INT_EP_NR;
+ INIT_WORK(&ep->tx_work, fim_usb_uart_tx_work_func);
+ ep->port = port;
+ ep->nr = FIM_USB_NR_EP_IN;
+
+ /* @XXX: Define a correct reset value for this member */
+ port->ep_next_data = 0;
+}
+
+/* Be sure that you have disconnected the pull-up first */
+static void fim_usb_init_port(struct fim_usb_port *port)
+{
+ /* Reset the registers */
+ fim_usb_regs_reset(port);
+
+ /* Download config to enpoint buffer */
+ fim_usb_config(port);
+ fim_usb_init_eps(port);
+}
+
+static int fim_usb_reboot_notifier_func(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ struct fim_usb_port *port;
+ int cnt;
+
+ for (cnt = 0; cnt < fim_usb_ports_max; cnt++) {
+ port = fim_usb_ports[cnt];
+ if (!port)
+ continue;
+
+ fim_usb_disable_pullup(port);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static void fim_usb_hotplug_work_func(struct work_struct *work)
+{
+ struct fim_driver *fim;
+ int reg;
+ int cnt_curr, cnt_init;
+ struct fim_usb_port *port;
+
+ port = container_of(work, struct fim_usb_port, hotplug_work.work);
+ fim = &port->fim;
+
+ /* Get the status of the main register and the current countdown value */
+ fim_get_ctrl_reg(fim, FIM_USB_MAIN_REG, &reg);
+ fim_get_stat_reg(fim, FIM_USB_ALIVE_COUNT_STAT, &cnt_init);
+
+ /* Set the initial value for the countdown register */
+ fim_set_ctrl_reg(fim, FIM_USB_ALIVE_COUNT_REG, FIM_USB_HOTPLUG_ALIVE_COUNTER);
+
+ /* Inform the FIM about the new reloaded value */
+ fim_set_ctrl_reg(fim, FIM_USB_MAIN_REG, reg | FIM_USB_MAIN_ALIVE_COUNT);
+
+ /* We need to wait for the status register */
+ msleep_interruptible(50);
+ fim_get_stat_reg(fim, FIM_USB_ALIVE_COUNT_STAT, &cnt_curr);
+
+ /* Reset the flag of the control register */
+ fim_set_ctrl_reg(fim, FIM_USB_MAIN_REG, reg);
+
+ /* Re-trigger the timer function if all looks OK */
+ if (cnt_init != cnt_curr) {
+ port->unplug_detected = 0;
+ schedule_delayed_work(&port->hotplug_work, FIM_USB_HOTPLUG_ALIVE_TIMER);
+ } else {
+ /* In the case that the timer has expired we need to wait for the RXNRIP */
+ port->unplug_detected = 1;
+
+ /* Remove the serial port for canceling the UART operations */
+ if (port->uart_created) {
+ struct uart_port *uart;
+
+ uart = &port->uart;
+ uart_remove_one_port(&fim_usb_uart_driver, uart);
+ port->uart_created = 0;
+ }
+
+ fim_usb_restart_fim(port);
+
+ pk_dbg("Keep alive counter expired (init 0x%x | curr 0x%x). Port %i removed\n",
+ cnt_init, cnt_curr, fim->picnr);
+ }
+}
+
+/* Function used for creating or removing the UART port of this FIM */
+static struct uart_ops fim_usb_uart_ops;
+static void fim_usb_uart_work_func(struct work_struct *work)
+{
+ struct fim_usb_port *port;
+ int ret;
+ struct uart_port *uart;
+ struct fim_driver *fim;
+
+ port = container_of(work, struct fim_usb_port, uart_work);
+ uart = &port->uart;
+ fim = &port->fim;
+
+ /*
+ * This can happen when the device entered the suspend mode and restarts the
+ * enumeration sequence
+ */
+ if (port->uart_created) {
+ pk_dbg("UART port %i already created.\n", fim->picnr);
+ goto exit_sched;
+ }
+
+ /* We really need to restart the structure at this place */
+ uart->ops = &fim_usb_uart_ops;
+ uart->flags = UPF_BOOT_AUTOCONF;
+ uart->dev = port->dev;
+ uart->type = UPIO_MEM;
+ uart->line = fim->picnr; /* Use the FIM number as minor */
+ uart->iotype = UPIO_PORT;
+ dev_set_drvdata(uart->dev, port);
+
+ /*
+ * @FIXME: Lock this section, otherwise we will have some problems by the plug-unplug
+ * tests of the SALAB.
+ */
+ pk_dbg("Adding the new port %i\n", fim->picnr);
+ ret = uart_add_one_port(&fim_usb_uart_driver, uart);
+ if (ret) {
+ dev_set_drvdata(uart->dev, NULL);
+ pk_err("Couldn't register the UART port %i\n", fim->picnr);
+ goto err_add;
+ }
+
+ port->uart_created = 1;
+
+ exit_sched:
+ /* @FIXME: Is this the correct place for our FIM timer loader? */
+ schedule_delayed_work(&port->hotplug_work, 0);
+
+ err_add:
+ return;
+}
+
+/*
+ * Send a buffer over the FIM-API. We are using static buffers for sending the data
+ * to the FIMs, we don't need to have a new allocated buffer, since the FIM only accepts
+ * frames with a maximal sizo of 13 bytes (8 with the EP data)
+ * (Luis Galdos)
+ */
+static int __fim_usb_ep_send_buffer(struct fim_usb_port *port, void *buffer, int len, void *priv)
+{
+ struct fim_driver *fim;
+ int retval;
+ struct fim_buffer_t buf;
+
+ if (!port || !len)
+ return -EINVAL;
+
+ fim = &port->fim;
+
+ buf.private = priv;
+ buf.data = buffer;
+ buf.length = len;
+
+ retval = fim_send_buffer(fim, &buf);
+ if (retval) {
+ int level;
+
+ level = fim_tx_buffers_level(&port->fim);
+ pk_dbg("FIM%i send %i bytes failed (err %i, level %i)\n",
+ fim->picnr, buf.length, retval, level);
+ schedule_work(&port->restart_work);
+ }
+
+ return retval;
+}
+
+/* Calculate the CRC16 for the passed buffer */
+static inline u16 fim_usb_calc_buffer_crc(const u8 *buffer, unsigned long length)
+{
+ u16 retval, bx;
+ unsigned char crc16;
+ int i;
+ ulong cnt;
+
+ retval = 0xffff;
+ for (cnt = 0; cnt <length; cnt++) {
+ for (i=0; i < 8; i++) {
+ crc16 = retval >> (15 - i);
+ bx = crc16 ^ buffer[cnt];
+ retval <<= 1;
+
+ if (bx & (0x1 << i))
+ retval ^= 0x8005;
+ }
+ }
+
+ bx = ~retval;
+ retval = 0;
+ for (i=0; i < 8; i++)
+ retval |= ((bx & (0x1 << i)) << (15 - (i * 2)) |
+ (bx & (0x100 << i)) >> (1 + (i * 2)));
+
+ return retval;
+}
+
+#define fim_usb_ep_next_data_token(ep) ((ep->last_data_token == USB_TOKEN_DATA0) ? \
+ USB_TOKEN_DATA1 : USB_TOKEN_DATA0)
+
+/*
+ * Call this function for sending a ZLP by the passed EP
+ * If force is true, the function will ignore the current TX fifo level and will queue
+ * the new IN data packet.
+ */
+static int fim_usb_write_zlp(struct fim_usb_port *port, struct fim_usb_ep *ep, int force)
+{
+ struct fim_usb_in_frame fin;
+ int level;
+
+ dbg_write("EP%u: Sending ZLP\n", ep->nr);
+
+ level = fim_tx_buffers_level(&port->fim);
+ if (level && !force) {
+ pk_dbg("ZLP: TX level > 0 (%i). FIM restart.\n", level);
+ fim_usb_restart_fim(port);
+ return -ERESTART;
+ }
+
+ memset(&fin, 0, sizeof(fin));
+ fin.epnr = ep->addr & ~USB_DIR_IN;
+ fin.bytes = FIM_USB_DATA_TAIL;
+ fin.token = fim_usb_ep_next_data_token(ep);
+
+ /* @XXX: Reset this flag depending on the return value of the send buffer function */
+ ep->zlp = 0;
+
+ return __fim_usb_ep_send_buffer(port, &fin, FIM_USB_DATA_OVERHEAD, ep);
+}
+
+/* IMPORTANT: This function returns the number of transferred bytes! */
+static int fim_usb_write_packet(struct fim_usb_port *port, struct fim_usb_ep *ep, u8 *buf, u16 len)
+{
+ u16 crc, val;
+ struct fim_usb_in_frame fin;
+ int ret, level;
+
+ /* @FIXME: Use a correct sanity check! */
+ level = fim_tx_buffers_level(&port->fim);
+ if (level) {
+ pk_dbg("write packet: TX level = %i. FIM restart.\n", level);
+ fim_usb_restart_fim(port);
+ return -EINVAL;
+ }
+
+ val = min(len, (u16)FIM_USB_MAX_PACK_SIZE);
+ ep->bytes = len - val;
+ ep->data = buf + val;
+ ep->requested -= val;
+ dbg_enum("EP%u: Sending %u bytes [%p], pending %u [%p], requested %u\n",
+ ep->nr, val, buf, ep->bytes, ep->data, ep->requested);
+
+ /*
+ * The FIM expects that the IN-data is placed in the TX-FIFO.
+ *
+ */
+ crc = fim_usb_calc_buffer_crc(buf, val);
+#if defined(FIM_USB_DEBUG_SEND_BUFFER)
+ dbg_func("Calculated CRC 0x%04x\n", crc);
+#endif
+
+ /* Format: Endpoint number | Number of bytes | Token | Data | CRC16 */
+ fin.epnr = ep->addr & ~USB_DIR_IN;
+ fin.bytes = val + FIM_USB_DATA_TAIL; /* Token and CRC16 */
+
+ fin.token = fim_usb_ep_next_data_token(ep);
+ ep->last_data_token = fin.token;
+
+ fin.crc = (u16 *)(fin.data + val);
+ memcpy(fin.data, buf, val);
+
+ *fin.crc = crc;
+ ret = __fim_usb_ep_send_buffer(port, &fin, val + FIM_USB_DATA_OVERHEAD, ep);
+ return ret ? ret : val;
+}
+
+#define FIM_USB_VENDOR_ID (0x0210)
+#define FIM_USB_PRODUCT_ID (0xbe13)
+
+#define STRING_MANUFACTURER 1
+#define STRING_PRODUCT 2
+#define STRING_SERIALNUM 3
+
+#define DEV_CONFIG_VALUE 1
+#define FIM_USB_INTERFACE 0
+
+static struct usb_device_descriptor fim_usb_device_desc = {
+ .bLength = sizeof(fim_usb_device_desc),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0110),
+ .bDeviceClass = 0,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .bMaxPacketSize0 = 8,
+ .idVendor = __constant_cpu_to_le16(FIM_USB_VENDOR_ID),
+ .idProduct = __constant_cpu_to_le16(FIM_USB_PRODUCT_ID),
+ .iManufacturer = STRING_MANUFACTURER,
+ .iProduct = STRING_PRODUCT,
+ .iSerialNumber = STRING_SERIALNUM,
+ .bNumConfigurations = 1
+};
+
+static struct fim_usb_config_descriptor fim_usb_config_desc2 = {
+ .config = {
+ .bLength = USB_DT_CONFIG_SIZE,
+ .bDescriptorType = USB_DT_CONFIG,
+
+ /* compute wTotalLength on the fly */
+ .bNumInterfaces = 1,
+ .bConfigurationValue = DEV_CONFIG_VALUE,
+ .iConfiguration = 0,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = 0
+ },
+ .interface = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = FIM_USB_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = 0
+ },
+ .ep_out = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = FIM_USB_OUT_EPNR,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(FIM_USB_MAX_PACK_SIZE),
+ .bInterval = 1,
+ },
+ .ep_in = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = FIM_USB_IN_EPNR,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(FIM_USB_MAX_PACK_SIZE),
+ .bInterval = 1,
+ }
+};
+
+static struct fim_usb_string fim_usb_strings [] = {
+ {
+ .id = STRING_MANUFACTURER,
+ .s = FIM_USB_MANU
+ }, {
+ .id = STRING_PRODUCT,
+ .s = FIM_USB_PROD_DESC
+ }, {
+ .id = STRING_SERIALNUM,
+ .s = FIM_USB_SERIALNR
+ }
+};
+
+/* Send the requested string descriptor to the host */
+static int fim_usb_send_string_descriptor(struct fim_usb_port *port, struct fim_usb_ep *ep,
+ struct usb_ctrlrequest *ctrl)
+{
+ int ret, cnt;
+ u8 value;
+
+ ret = -EINVAL;
+ value = le16_to_cpu(ctrl->wValue);
+
+ if (value == 0) {
+ struct usb_string_descriptor desc;
+
+ desc.bLength = sizeof(desc);
+ desc.bDescriptorType = USB_DT_STRING;
+ desc.wData[0] = __constant_cpu_to_le16(0x0409);
+ ret = fim_usb_write_packet(port, ep, (u8 *)&desc, sizeof(desc));
+
+ } else {
+ struct fim_usb_string *pstr;
+
+ pstr = NULL;
+ for (cnt = 0; cnt < ARRAY_SIZE(fim_usb_strings); cnt++) {
+ if (fim_usb_strings[cnt].id == value) {
+ pstr = &fim_usb_strings[cnt];
+ break;
+ }
+ }
+
+ if (!pstr) {
+ pk_dbg("Unavailable string descriptor 0x%04x request!\n", value);
+ ret = -EINVAL;
+ goto exit_str_desc;
+ }
+
+ /* OK, we have a string descriptor, send it now! */
+ ret = fim_usb_write_packet(port, ep, (u8 *)&pstr->desc, pstr->desc.bLength);
+ }
+
+ exit_str_desc:
+ return ret;
+}
+
+static int fim_usb_handle_ep0_get_descriptor(struct fim_usb_port *port, struct usb_ctrlrequest *ctrl)
+{
+ u8 desc;
+ u16 wvalue, len, total, val;
+ struct fim_usb_ep *ep;
+ int ret;
+
+ len = le16_to_cpu(ctrl->wLength);
+ wvalue = le16_to_cpu(ctrl->wValue);
+ desc = wvalue >> 8;
+ ep = &port->eps[FIM_USB_NR_EP_CTRL];
+
+ ep->requested = len;
+ dbg_enum("Handling get descriptor 0x%02x (len %u)\n", desc, len);
+
+ ret = 0;
+ switch (desc) {
+
+ /* Descriptor type device */
+ case USB_DT_DEVICE:
+ ep->last_data_token = USB_TOKEN_DATA0;
+ ret = fim_usb_write_packet(port, ep, (u8 *)&fim_usb_device_desc, sizeof(fim_usb_device_desc));
+ break;
+
+ /* Descriptor type configuration */
+ case USB_DT_CONFIG:
+ ep->last_data_token = USB_TOKEN_DATA0;
+
+ /* Setup the total length of the configuration descriptor */
+ total = sizeof(fim_usb_config_desc2);
+ val = min(len, total);
+ fim_usb_config_desc2.config.wTotalLength = cpu_to_le16(total);
+
+ ret = fim_usb_write_packet(port, ep, (u8 *)&fim_usb_config_desc2, val);
+ dbg_enum("Configuration desc. size = %u | sent %u | requested %u\n",
+ total, ret, ep->requested);
+ break;
+
+ /* Descriptor type string */
+ case USB_DT_STRING:
+
+ ep->last_data_token = USB_TOKEN_DATA0;
+
+ /* According to the spec: by index zero return the table with the supported languages */
+ ret = fim_usb_send_string_descriptor(port, ep, ctrl);
+ break;
+
+ default:
+ pk_err("Unhandled GET descriptor request 0x%02x\n", desc);
+ break;
+ }
+
+ return ret;
+}
+
+static int fim_usb_handle_ep0_standard(struct fim_usb_port *port, struct usb_ctrlrequest *ctrl)
+{
+ int ret = 0;
+ struct fim_usb_ep *ep;
+ u8 addr;
+ u16 value, idx, type;
+ int level;
+
+ ep = &port->eps[FIM_USB_NR_EP_CTRL];
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ break;
+
+ dbg_enum("Get descriptor 0x%02x\n", le16_to_cpu(ctrl->wValue));
+ ret = fim_usb_handle_ep0_get_descriptor(port, ctrl);
+ break;
+
+ /* We must responde with a zero packet length */
+ case USB_REQ_SET_ADDRESS:
+ addr = le16_to_cpu(ctrl->wValue);
+ ep->last_data_token = USB_TOKEN_DATA0;
+ fim_usb_write_zlp(port, ep, 0);
+
+ /* @FIXME: Arrgghhh...! Give the FIM some time for sending the ZLP */
+ udelay(1500);
+
+ fim_set_ctrl_reg(&port->fim, FIM_USB_ADDR_REG, addr);
+ dbg_enum("Set address request (%d)\n", addr);
+ break;
+
+ /* @FIXME: Check for the configuration number */
+ case USB_REQ_SET_CONFIGURATION:
+ value = le16_to_cpu(ctrl->wValue);
+ fim_usb_write_zlp(port, ep, 0);
+ dbg_enum("Set configuration %u request\n", value);
+
+ /* So, let's create the serial device port */
+ schedule_work(&port->uart_work);
+ break;
+
+ /* @FIXME: Check for the interface number */
+ case USB_REQ_SET_INTERFACE:
+
+ level = fim_tx_buffers_level(&port->fim);
+ if (level) {
+
+ /*
+ * That's really bad, cause the host wants to start a new data transfer but
+ * the TX FIFO is not free!
+ */
+ pk_dbg("TX FIFO restart for SET interface (level = %i)\n", level);
+ schedule_work(&port->restart_work);
+ return -ERESTART;
+ }
+
+ value = le16_to_cpu(ctrl->wValue);
+ fim_usb_write_zlp(port, ep, 0);
+ dbg_enum("Set configuration %u request\n", value);
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ idx = le16_to_cpu(ctrl->wIndex);
+ value = le16_to_cpu(ctrl->wValue);
+ type = le16_to_cpu(ctrl->bRequest);
+ dbg_enum("Clear feature type 0x%x | index 0x%x | value 0x%0x\n",
+ type, idx, value);
+
+ /* Only EPs supported now */
+ if (type != 2 && (!idx || idx == FIM_USB_IN_EPNR || idx == FIM_USB_OUT_EPNR))
+ fim_usb_write_zlp(port, ep, 0);
+ else {
+ /* @FIXME: Send a STALL at this point */
+ pk_err("Invalid clear feature request (idx 0x%x | type 0x%x)\n", idx, type);
+ ret = -EINVAL;
+ }
+
+ break;
+
+ default:
+ pk_err("Unhandled standard request 0x%02x\n", ctrl->bRequest);
+ pk_dump_ctrl_packet(ctrl);
+ /* @FIXME: Send a STALL to the host! */
+ break;
+ }
+
+ return ret;
+}
+
+static int fim_usb_handle_ep1_out(struct fim_usb_port *port, u8 *data, int len)
+{
+ struct tty_struct *tty;
+ int count;
+
+ dbg_uart("FIM%i: Writing %i bytes into UART fifo\n", port->index, len);
+
+ tty = tty_from_port(port);
+ if (!port->uart_created || !tty) {
+
+ /* This happens when the serial port was not opened yet */
+ dbg_uart("Found an uninitialized port %p | tty %p\n", port, tty);
+ return -ENODEV;
+ }
+
+ /* Don't copy more bytes than there is room for in the buffer */
+ count = tty_buffer_request_room(tty, len);
+ if (count < len) {
+ pk_dbg("Dropping %i chars [ len %i > %i count ]\n", len - count, len, count);
+ len = count;
+
+ if (len == 0xfffe)
+ return 0;
+ }
+
+ tty_insert_flip_string(tty, data, len);
+ tty_flip_buffer_push(tty);
+
+ return 0;
+}
+
+/* Handle the requests arrived for the EP0 (control EP) */
+static int fim_usb_handle_ep0(struct fim_usb_port *port,
+ struct usb_ctrlrequest *pctrl)
+{
+ int ret = 0;
+ u16 windex;
+
+ if (!pctrl || !port)
+ return -ENODEV;
+
+ windex = le16_to_cpu(pctrl->wIndex);
+
+ switch (pctrl->bRequestType & USB_TYPE_MASK) {
+
+ case USB_TYPE_STANDARD:
+ ret = fim_usb_handle_ep0_standard(port, pctrl);
+ break;
+
+ default:
+ pk_err("bRequestType 0x%02x | bRequest 0x%02x | "
+ "wValue 0x%04x | wIndex 0x%04x | wLength %u\n",
+ pctrl->bRequestType, pctrl->bRequest,
+ pctrl->wValue, pctrl->wIndex, pctrl->wLength);
+ break;
+ }
+
+ return ret;
+}
+
+static void fim_usb_error_isr(struct fim_driver *driver, ulong rx_err, ulong tx_err)
+{
+ struct fim_usb_port *port;
+
+ port = driver->driver_data;
+
+ if ((rx_err & IOHUB_IFS_RXNRIP) || port->unplug_detected) {
+
+ if (!work_pending(&port->restart_work)) {
+ /* pk_dbg("Scheduling restart worker\n"); */
+ schedule_work(&port->restart_work);
+ }
+ } else
+ pk_dbg("Unhandled FIM error (rx 0x%lx | tx 0x%lx)\n", rx_err, tx_err);
+}
+
+/*
+ * Handler for the incoming FIM-interrupts. Available interrupts:
+ * -
+ */
+static void fim_usb_isr(struct fim_driver *driver, int irq, unsigned char code,
+ unsigned int rx_fifo)
+{
+ struct fim_usb_port *port;
+
+ dbg_func("FIM IRQ %i\n", irq);
+ port = port_from_fim(driver);
+
+ switch (code) {
+ default:
+ pk_err("Unknown IRQ %i | FIM %i | %x\n",
+ code, port->fim.picnr, rx_fifo);
+ break;
+ }
+}
+
+/*
+ * This is the TX-callback that the API call after a DMA-package was closed
+ * The fim buffer structure contains our internal private data
+ * Free the allocated FIM-buffer that was used for sending the DMA-data
+ */
+static void fim_usb_tx_isr(struct fim_driver *fim, int irq,
+ struct fim_buffer_t *pdata)
+{
+ struct fim_usb_port *port;
+ struct fim_usb_ep *ep;
+ struct uart_port *uart;
+ struct circ_buf *xmit;
+
+ dbg_write("TX IRQ\n");
+
+ /* Reset the internal buffer descriptor of the EP */
+ port = fim->driver_data;
+
+ /* Here we can start the next data transfer if there is something pending */
+ ep = &port->eps[FIM_USB_NR_EP_CTRL];
+ if (!ep->addr && ep->bytes) {
+ int sent;
+
+ sent = fim_usb_write_packet(port, ep, ep->data, ep->bytes);
+ if (sent == FIM_USB_MAX_PACK_SIZE && !ep->bytes && ep->requested) {
+ fim_usb_write_zlp(port, ep, 1);
+ dbg_enum("ZLP for big packet (request pending %u)\n", ep->requested);
+ }
+
+ /* @XXX: Should we return at this point? */
+ /* return; */
+ }
+
+ /* Schedule the work queue at this point if there is some remaining data */
+ ep = &port->eps[FIM_USB_NR_EP_IN];
+ uart = ep->tx_uart;
+ if (uart) {
+
+ /* The worker will wakeup the device if the circular buffer is empty */
+ xmit = &uart->info->xmit;
+ if (xmit && !uart_circ_empty(xmit) && !uart_tx_stopped(uart)) {
+ dbg_uart("EP%02x Scheduling TX work\n", ep->addr);
+ schedule_work(&ep->tx_work);
+ }
+ }
+}
+
+static int fim_usb_restart_fim(struct fim_usb_port *port)
+{
+ int ret;
+ struct fim_driver *fim;
+
+ if (!port)
+ return -ENODEV;
+
+ fim = &port->fim;
+
+ fim_usb_disable_pullup(port);
+
+ /* Reset the main register */
+ fim_usb_regs_reset(port);
+
+ /* First try to stop the FIM */
+ fim_disable_irq(fim);
+ ret = fim_send_stop(fim);
+ if (ret) {
+ pk_err("Couldn't stop the FIM%i\n", fim->picnr);
+ return ret;
+ }
+
+ fim_dma_stop(&port->fim);
+ fim_dma_start(&port->fim, NULL);
+
+ ret = fim_send_start(fim);
+ if (ret) {
+ pk_err("Couldn't start the FIM%i\n", fim->picnr);
+ return -EAGAIN;
+ }
+
+ /* Restart the DMA channel when we know that we are safe! */
+ fim_enable_irq(fim);
+
+ /* This function resets the address number to zero */
+ fim_usb_init_port(port);
+
+ /* This will enable the start of the FIM in the firmware */
+ fim_set_ctrl_reg(&port->fim, FIM_USB_MAIN_REG,
+ FIM_USB_MAIN_START | FIM_USB_MAIN_NOKEEPALIVE | FIM_USB_MAIN_NOINIRQ);
+
+ fim_usb_enable_pullup(port);
+
+ return ret;
+}
+
+static void fim_usb_restart_work_func(struct work_struct *work)
+{
+ struct fim_usb_port *port;
+
+ port = container_of(work, struct fim_usb_port, restart_work);
+ fim_usb_restart_fim(port);
+ complete(&port->restart_completed);
+}
+
+/* Handle the bus resets reported by the FIM */
+static void fim_usb_bus_reset(struct fim_usb_port *port)
+{
+ unsigned int addr;
+ int level;
+
+ dbg_enum("Bus reset (addr = 0)\n");
+
+ /* We need to restore the TX buffers at this point */
+ fim_get_ctrl_reg(&port->fim, FIM_USB_ADDR_REG, &addr);
+ level = fim_tx_buffers_level(&port->fim);
+ if (level && addr) {
+ dbg_enum("Need to restart the FIM (level %i, curr addr. %u)\n",
+ level, addr);
+ schedule_work(&port->restart_work);
+ } else {
+ addr = 0;
+ fim_set_ctrl_reg(&port->fim, FIM_USB_ADDR_REG, addr);
+ }
+}
+
+/*
+ * Called when a receive DMA-buffer was closed.
+ * The first byte contains the status of the closed buffer.
+ */
+static void fim_usb_rx_isr(struct fim_driver *fim, int irq,
+ struct fim_buffer_t *pdata)
+{
+ u8 status, err;
+ struct usb_ctrlrequest *pctrl;
+ struct fim_usb_port *port;
+ struct fim_usb_ep *ep;
+ struct fim_usb_token *token;
+
+ port = fim->driver_data;
+ status = *(pdata->data);
+ err = *(pdata->data + pdata->length);
+ token = (struct fim_usb_token *)(pdata->data + 1);
+
+ if (pdata->length == 1) {
+
+ switch (status) {
+
+ case FIM_USB_BUS_RESET:
+ dbg_pr("Bus reset received!\n");
+ fim_usb_bus_reset(port);
+ break;
+
+ case FIM_USB_KEEP_ALIVE:
+ dbg_pr("Alive\n");
+ break;
+
+ case FIM_USB_IN_DATA_SENT_MARKER:
+ dbg_write("FIM sending IN frame\n");
+ break;
+
+ default:
+ dbg_pr("Unexpected status 0x%x\n", status);
+ break;
+ }
+
+ } else {
+
+ switch (token->type) {
+
+ case USB_TOKEN_DATA0:
+ case USB_TOKEN_DATA1:
+ dbg_token("DATA%c %u bytes for EP%u\n",
+ (token->type == USB_TOKEN_DATA0) ? '0' : '1',
+ pdata->length, port->ep_next_data);
+
+ /* The complete frame contains the sync byte, the CRC and the error */
+ if (port->ep_next_data == 0) {
+
+ /* @XXX: Stupid sanity check for let it be for now */
+ if (pdata->length != FIM_USB_DATA_OVERHEAD && pdata->length != FIM_USB_DATA_TAIL) {
+ pctrl = (struct usb_ctrlrequest *)(pdata->data + 2);
+ fim_usb_handle_ep0(port, pctrl);
+ }
+
+ } else {
+ struct fim_usb_data *fdata;
+ u16 bytes;
+
+ /* Remove the overhead from the data frame */
+ bytes = pdata->length - FIM_USB_DATA_OVERHEAD;
+ fdata = (struct fim_usb_data *)pdata->data;
+
+ /* We have only one EP, so let us call it directly from here */
+ fim_usb_handle_ep1_out(port, pdata->data + FIM_USB_DATA_HEAD, bytes);
+ }
+ break;
+
+ /* Get the endpoint number for the incoming data */
+ case USB_TOKEN_OUT:
+ dbg_token("OUT token for EP %x\n", token->ep);
+ port->ep_next_data = token->ep;
+
+ /* Reset the data token of the IN EP81 */
+ ep = &port->eps[FIM_USB_NR_EP_IN];
+ ep->last_data_token = USB_TOKEN_DATA1;
+ break;
+
+ /* DONT print the IN tokens cause they are generated continously */
+ case USB_TOKEN_IN:
+ /* dbg_token("IN token for EP %x\n", token->ep); */
+ port->ep_next_data = token->ep;
+ break;
+
+ case USB_TOKEN_SETUP:
+ port->ep_next_data = 0;
+ /* dbg_token("SETUP token for EP %x\n", token->ep); */
+
+ /* Reset the data token of the IN EP81 */
+ ep = &port->eps[FIM_USB_NR_EP_IN];
+ ep->last_data_token = USB_TOKEN_DATA1;
+ break;
+
+ /* Here the tokens that only generate noise */
+ case USB_TOKEN_SYNC:
+ default:
+ break;
+ }
+ }
+}
+
+static int fim_usb_unregister_port(struct fim_usb_port *port)
+{
+ int cnt, ret;
+ struct fim_driver *fim;
+ struct uart_port *uart;
+
+ if (!port || !port->reg)
+ return -ENODEV;
+
+ fim = &port->fim;
+ pk_dbg("Going to unregister the FIM USB port %i\n", fim->picnr);
+
+ flush_work(&port->uart_work);
+ cancel_work_sync(&port->restart_work);
+ cancel_delayed_work_sync(&port->hotplug_work);
+
+ /* Disable the FIM from the connected host */
+ fim_usb_disable_pullup(port);
+
+ ret = fim_unregister_driver(fim);
+ if (ret)
+ goto exit_unreg;
+
+ /* Unregister the UART port */
+ if (port->uart_created) {
+ dbg_uart("Removing the port %i\n", fim->picnr);
+ uart = &port->uart;
+ uart_remove_one_port(&fim_usb_uart_driver, uart);
+ port->uart_created = 0;
+ }
+
+ /* Free the requested GPIO */
+ for (cnt = 0; cnt < FIM_USB_MAX_GPIOS; cnt++) {
+ dbg_pr("Freeing GPIO %i\n", port->gpios[cnt].nr);
+ if (port->gpios[cnt].nr == FIM_LAST_GPIO)
+ break;
+ else if (port->gpios[cnt].nr == FIM_GPIO_DONT_USE)
+ continue;
+ else
+ gpio_free(port->gpios[cnt].nr);
+ }
+
+ /* Free the requested clock */
+ if (!IS_ERR(port->clk))
+ clk_put(port->clk);
+
+ fim_usb_ports[fim->picnr] = NULL;
+ kfree(port);
+ ret = 0;
+
+ exit_unreg:
+ return ret;
+}
+
+/*
+ * Called when the port is opened. By the boot-console it will be called before
+ * the port configuration using set_termios
+ */
+static int fim_usb_uart_startup(struct uart_port *uart)
+{
+ struct fim_usb_port *port;
+ int ret = 0;
+
+ dbg_uart("Calling %s()\n", __func__);
+ port = port_from_uart(uart);
+ if (!port)
+ return -ENODEV;
+
+#if defined(FIM_USB_FORCE_ENUM_TEST)
+ /* This will restart the enumeration sequence */
+ pk_info("Forcing the enumeration test!\n");
+ schedule_work(&port->restart_work);
+ ret = -ENODEV;
+#else
+ /*
+ * We must flush the TX FIFO when there is some data pending.
+ * @XXX: Use a waitqueue with timeout for returning a failure,
+ * otherwise the user must reopen the port (annoying)
+ */
+ if (fim_tx_buffers_level(&port->fim)) {
+ pk_dbg("TX FIFO contains data, FIM restart required.\n");
+ schedule_work(&port->restart_work);
+ wait_for_completion(&port->restart_completed);
+
+ pk_dbg("Restart completed. Re-checking TX FIFO state.\n");
+ if (fim_tx_buffers_level(&port->fim)) {
+ pk_dbg("TX FIFO invalid state. Retry required.\n");
+ ret = -EAGAIN;
+ }
+ }
+#endif /* FIM_USB_FORCE_ENUM_TEST */
+
+ return ret;
+}
+
+/* Return a string describing the type of the port */
+static const char *fim_usb_uart_type(struct uart_port *port)
+{
+ dbg_uart("Calling %s\n", __func__);
+ return FIM_DRIVER_NAME;
+}
+
+static void fim_usb_uart_release_port(struct uart_port *port)
+{
+ dbg_uart("Calling %s\n", __func__);
+}
+
+static int fim_usb_uart_request_port(struct uart_port *port)
+{
+ dbg_uart("Calling %s\n", __func__);
+ return 0;
+}
+
+/* @TODO: Get more infos about the UART configuration */
+static void fim_usb_uart_config_port(struct uart_port *port, int flags)
+{
+ dbg_uart("Calling %s\n", __func__);
+ port->type = UPIO_MEM;
+}
+
+static int fim_usb_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ dbg_uart("Verify port called\n");
+ return 0;
+}
+
+/* Called for setting the termios config */
+static void fim_usb_uart_set_termios(struct uart_port *uart, struct ktermios *termios,
+ struct ktermios *old)
+{
+ dbg_uart("Calling %s\n", __func__);
+}
+
+static unsigned int fim_usb_uart_tx_empty(struct uart_port *uart)
+{
+ return TIOCSER_TEMT;
+}
+
+/* Handle the data transfer from the UART circular buffer to the USB host (IN transfers) */
+static void fim_usb_uart_tx_work_func(struct work_struct *work)
+{
+ struct fim_usb_ep *ep;
+ struct circ_buf *xmit;
+ u8 buf[FIM_USB_MAX_PACK_SIZE];
+ struct uart_port *uart;
+ ulong pending, pos;
+ int ret, tail;
+ struct fim_usb_port *port;
+ __u32 tx;
+
+ ep = container_of(work, struct fim_usb_ep, tx_work);
+ port = ep->port;
+
+ if (down_interruptible(&port->uart_sem))
+ return;
+
+ uart = ep->tx_uart;
+ xmit = &uart->info->xmit;
+ tail = xmit->tail;
+ tx = uart->icount.tx;
+ pending = uart_circ_chars_pending(xmit);
+ pos = ret = 0;
+
+ while (pending--) {
+
+ buf[pos++] = xmit->buf[tail];
+
+ if (pos == FIM_USB_MAX_PACK_SIZE || pending == 0) {
+
+ /*
+ * Check if there is enough space in the DMA buffers for the next
+ * transfer request. Otherwise we must wait for the completion of
+ * some TX transfer and continue sending the data
+ */
+ while (fim_tx_buffers_level(&port->fim)) {
+ schedule_timeout_interruptible(HZ / 1000);
+ if (signal_pending(current))
+ goto exit_tx_worker;
+ }
+
+ /* Send the data to the FIM */
+ dbg_uart("EP%02x Sending %lu bytes | remaining %lu\n", ep->addr, pos, pending);
+ ret = fim_usb_write_packet(port, ep, buf, pos);
+ if (ret <= 0) {
+ pr_err("Couldn't send the buffer (err %i)\n", ret);
+ goto exit_tx_worker;
+ }
+ }
+
+ tail = (tail + 1) & (UART_XMIT_SIZE - 1);
+ tx++;
+
+ /* Only one messages per call! */
+ if (pos == FIM_USB_MAX_PACK_SIZE || pending == 0)
+ break;
+ }
+
+ /* OK, it looks we have sent some data, so update the UART structure */
+ xmit->tail = tail;
+ uart->icount.tx = tx;
+
+ /* Check for the ZLP condition on this EP */
+ if (uart_circ_empty(xmit) && pos == FIM_USB_MAX_PACK_SIZE) {
+ dbg_uart("Need to send a ZLP\n");
+
+ /* @XXX: Quick and dirty! */
+ /* Wait for an empty TX FIFO cause we can't queue messages */
+ while (fim_tx_buffers_level(&port->fim)) {
+ schedule_timeout_interruptible(HZ / 1000);
+ if (signal_pending(current))
+ goto exit_tx_worker;
+ }
+
+ fim_usb_write_zlp(port, ep, 0);
+ }
+
+ /*
+ * Tell the higher layer that we can send more data
+ * IMPORTANT: We need to check if TX was stopped before calling the wakeup cause
+ * the bove ZLP write function seems to take a lot of time and we have probably
+ * received a signal during this time
+ */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS && !uart_tx_stopped(uart)) {
+ uart_write_wakeup(uart);
+ }
+
+ exit_tx_worker:
+ up(&port->uart_sem);
+}
+
+/* This function is called when the TTY layer has some data to be sent */
+static void fim_usb_uart_start_tx(struct uart_port *uart)
+{
+ ulong pending;
+ struct fim_usb_ep *ep;
+ struct fim_usb_port *port;
+ struct circ_buf *xmit;
+
+ port = port_from_uart(uart);
+
+ /*
+ * IMPORTANT: The data token is modified in the function that puts the data
+ * buffer to the FIM-core.
+ */
+ ep = &port->eps[FIM_USB_NR_EP_IN];
+ ep->tx_uart = uart;
+
+ xmit = &uart->info->xmit;
+ pending = uart_circ_chars_pending(xmit);
+
+ dbg_uart("Scheduling TX worker | %lu bytes\n", pending);
+ schedule_work(&ep->tx_work);
+}
+
+static unsigned int fim_usb_uart_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void fim_usb_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* @TODO? */
+ dbg_uart("Calling %s\n", __func__);
+}
+
+static void fim_usb_uart_stop_tx(struct uart_port *port)
+{
+ /* flush_work(&port->uart_work); */
+ dbg_uart("Calling %s\n", __func__);
+}
+
+static void fim_usb_uart_stop_rx(struct uart_port *port)
+{
+ dbg_uart("Calling %s\n", __func__);
+}
+
+static void fim_usb_uart_enable_ms(struct uart_port *port)
+{
+ /* @TODO? */
+ dbg_uart("Calling %s\n", __func__);
+}
+
+static void fim_usb_uart_break_ctl(struct uart_port *port, int ctl)
+{
+ /* @TODO? */
+ dbg_uart("Calling %s\n", __func__);
+}
+
+static void fim_usb_uart_shutdown(struct uart_port *uart)
+{
+ struct fim_usb_port *port;
+ struct fim_usb_ep *ep;
+
+ port = port_from_uart(uart);
+ ep = &port->eps[FIM_USB_NR_EP_IN];
+
+ flush_work(&ep->tx_work);
+ dbg_uart("Calling %s\n", __func__);
+}
+
+static struct uart_ops fim_usb_uart_ops = {
+ .tx_empty = fim_usb_uart_tx_empty,
+ .set_mctrl = fim_usb_uart_set_mctrl,
+ .get_mctrl = fim_usb_uart_get_mctrl,
+ .stop_tx = fim_usb_uart_stop_tx,
+ .start_tx = fim_usb_uart_start_tx,
+ .stop_rx = fim_usb_uart_stop_rx,
+ .enable_ms = fim_usb_uart_enable_ms,
+ .break_ctl = fim_usb_uart_break_ctl,
+ .startup = fim_usb_uart_startup,
+ .shutdown = fim_usb_uart_shutdown,
+ .set_termios = fim_usb_uart_set_termios,
+ .type = fim_usb_uart_type,
+ .release_port = fim_usb_uart_release_port,
+ .request_port = fim_usb_uart_request_port,
+ .config_port = fim_usb_uart_config_port,
+ .verify_port = fim_usb_uart_verify_port
+};
+
+static int fim_usb_register_port(struct device *dev, int picnr, struct fim_gpio_t gpios[])
+{
+ int ret, cnt;
+ struct fim_dma_cfg_t dma_cfg;
+ struct fim_usb_port *port;
+ unsigned int fwver;
+
+ port = kzalloc(sizeof(* port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ /* Get a reference to the SYS clock for setting CLK rate */
+ ret = IS_ERR(port->clk = clk_get(port->fim.dev, "systemclock"));
+ if (ret) {
+ pk_err("Couldn't get the SYS clock.\n");
+ goto err_free_port;
+ }
+
+ /* Request GPIO */
+ for (cnt = 0; cnt < FIM_USB_MAX_GPIOS; cnt++) {
+
+ if (gpios[cnt].nr == FIM_LAST_GPIO)
+ break;
+ if (gpios[cnt].nr == FIM_GPIO_DONT_USE)
+ continue;
+
+ dbg_pr("Requesting the GPIO %i (Function %i)\n",
+ gpios[cnt].nr, gpios[cnt].func);
+ ret = gpio_request(gpios[cnt].nr, FIM_DRIVER_NAME);
+ if (!ret) {
+ int dir, pullup;
+
+ /*
+ * According to the documentation, the pins ENUM and SUSPEND
+ * must be controlled by the driver (they are not by the
+ * FIM-firmware).
+ */
+ if (cnt == FIM_USB_GPIO_ENUM || cnt == FIM_USB_GPIO_SPND) {
+ dir = NS921X_GPIO_OUTPUT;
+ pullup = NS921X_GPIO_DISABLE_PULLUP;
+ } else {
+ dir = NS921X_GPIO_INPUT;
+ pullup = NS921X_GPIO_ENABLE_PULLUP;
+ }
+
+ gpio_configure_ns921x_unlocked(gpios[cnt].nr,
+ dir,
+ NS921X_GPIO_DONT_INVERT,
+ gpios[cnt].func,
+ pullup);
+ } else {
+ /* Free the already requested GPIOs */
+ pk_err("Couldn't request the GPIO %i\n", gpios[cnt].nr);
+ while (cnt)
+ gpio_free(gpios[--cnt].nr);
+ goto err_free_clk;
+ }
+ }
+
+ /* Try to register FIM driver */
+ port->index = picnr;
+ port->fim.picnr = picnr;
+ port->fim.driver.name = FIM_DRIVER_NAME;
+ port->fim.driver_data = port;
+ port->fim.fim_isr = fim_usb_isr;
+ port->fim.dma_tx_isr = fim_usb_tx_isr;
+ port->fim.dma_rx_isr = fim_usb_rx_isr;
+ port->fim.dma_error_isr = fim_usb_error_isr;
+ port->fim.driver_data = port;
+
+ /* Specific DMA config */
+ dma_cfg.rxnr = FIM_USB_DMA_RX_BUFFERS;
+ dma_cfg.txnr = FIM_USB_DMA_TX_BUFFERS;
+ dma_cfg.rxsz = FIM_USB_DMA_BUFFER_SIZE;
+ dma_cfg.txsz = FIM_USB_DMA_BUFFER_SIZE;
+ port->fim.dma_cfg = &dma_cfg;
+
+ /* Check if have a firmware code for using to */
+ port->fim.fw_name = FIM_USB_FIRMWARE_FILE;
+ port->fim.fw_code = FIM_USB_FIRMWARE_CODE;
+ ret = fim_register_driver(&port->fim);
+ if (ret) {
+ pk_err("Couldn't register the FIM %i USB driver.\n", picnr);
+ goto err_free_gpios;
+ }
+
+ port->dev = dev;
+ memcpy(port->gpios, gpios, sizeof(port->gpios));
+
+ port->reg = 1;
+ dev_set_drvdata(dev, port);
+
+ INIT_WORK(&port->uart_work, fim_usb_uart_work_func);
+ INIT_WORK(&port->restart_work, fim_usb_restart_work_func);
+ init_completion(&port->restart_completed);
+ init_MUTEX(&port->uart_sem);
+
+ /* This is the timer for checking the state of the card detect line */
+ INIT_DELAYED_WORK(&port->hotplug_work, fim_usb_hotplug_work_func);
+
+ /* And enable the FIM-interrupt */
+ ret = fim_enable_irq(&port->fim);
+ if (ret) {
+ pk_err("Couldn't enable the FIM IRQ\n");
+ goto err_unreg_fim;
+ }
+
+ /* First do it at this point! */
+ fim_usb_disable_pullup(port);
+ fim_usb_init_port(port);
+
+ /* Print the firmware version */
+ fim_get_stat_reg(&port->fim, FIM_USB_REVISION_REG, &fwver);
+ pk_dbg("FIM%d running [fw rev 0x%02x]\n", picnr, fwver);
+
+ fim_usb_ports[picnr] = port;
+
+ /* This will enable the start of the FIM in the firmware */
+ fim_set_ctrl_reg(&port->fim, FIM_USB_MAIN_REG,
+ FIM_USB_MAIN_START | FIM_USB_MAIN_NOKEEPALIVE | FIM_USB_MAIN_NOINIRQ);
+ fim_usb_enable_pullup(port);
+ return 0;
+
+ err_unreg_fim:
+ fim_unregister_driver(&port->fim);
+
+ err_free_gpios:
+ for (cnt = 0; cnt < FIM_USB_MAX_GPIOS; cnt++) {
+
+ if (gpios[cnt].nr == FIM_GPIO_DONT_USE)
+ continue;
+
+ if (gpios[cnt].nr == FIM_LAST_GPIO)
+ break;
+
+ gpio_free(gpios[cnt].nr);
+ }
+
+ err_free_clk:
+ clk_put(port->clk);
+
+ err_free_port:
+ kfree(port);
+
+ return ret;
+}
+
+static int __init fim_usb_probe(struct platform_device *pdev)
+{
+ int nrpics;
+ struct fim_usb_platform_data *pdata;
+ struct fim_gpio_t gpios[FIM_USB_MAX_GPIOS];
+
+ dbg_func("New device with ID %i\n", pdev->id);
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -ENXIO;
+
+ /* Get the number of available PICs from the FIM-core */
+ nrpics = fim_number_pics();
+
+ if (fim_check_device_id(fims_number, pdata->fim_nr)) {
+#if defined(MODULE)
+ pk_dbg("Skipping FIM%i (not selected)\n", pdata->fim_nr);
+#else
+ pk_err("Invalid FIM number '%i' in platform data\n", pdata->fim_nr);
+#endif
+
+ return -ENODEV;
+ }
+
+ /*
+ * Start with the registration of the pdata ports
+ */
+ gpios[FIM_USB_GPIO_DP].nr = pdata->vp_gpio_nr;
+ gpios[FIM_USB_GPIO_DP].func = pdata->vp_gpio_func;
+ gpios[FIM_USB_GPIO_DM].nr = pdata->vm_gpio_nr;
+ gpios[FIM_USB_GPIO_DM].func = pdata->vm_gpio_func;
+ gpios[FIM_USB_GPIO_RCV].nr = pdata->rcv_gpio_nr;
+ gpios[FIM_USB_GPIO_RCV].func = pdata->rcv_gpio_func;
+ gpios[FIM_USB_GPIO_OE].nr = pdata->oe_l_gpio_nr;
+ gpios[FIM_USB_GPIO_OE].func = pdata->oe_l_gpio_func;
+ gpios[FIM_USB_GPIO_ENUM].nr = pdata->enum_gpio_nr;
+ gpios[FIM_USB_GPIO_ENUM].func = pdata->enum_gpio_func;
+ gpios[FIM_USB_GPIO_SPND].nr = pdata->spnd_gpio_nr;
+ gpios[FIM_USB_GPIO_SPND].func = pdata->spnd_gpio_func;
+
+ dbg_pr("Pins: DP %d | DM %d | RCV %d | OE %d | ENUM %d | SPND %d\n",
+ gpios[0].nr, gpios[1].nr, gpios[2].nr,
+ gpios[3].nr, gpios[4].nr, gpios[5].nr );
+
+ return fim_usb_register_port(&pdev->dev, pdata->fim_nr, gpios);
+}
+
+static int __exit fim_usb_remove(struct platform_device *pdev)
+{
+ struct fim_usb_port *port;
+ struct fim_usb_platform_data *pdata;
+ int retval;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -ENXIO;
+
+ port = dev_get_drvdata(&pdev->dev);
+ if (!port)
+ return -ENXIO;
+
+ retval = fim_usb_unregister_port(port);
+ if (retval)
+ goto exit_remove;
+
+ platform_set_drvdata(pdev, NULL);
+
+ exit_remove:
+ return retval;
+}
+
+#ifdef CONFIG_PM
+/* Here we need to desconnect from the host and stop the FIM */
+static int fim_usb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct fim_usb_port *port;
+ int ret;
+ struct fim_driver *fim;
+
+ port = dev_get_drvdata(&pdev->dev);
+ if (!port)
+ return -ENXIO;
+
+ cancel_delayed_work_sync(&port->hotplug_work);
+
+ /* Disable the device and stop the FIM */
+ fim_usb_disable_pullup(port);
+
+ /* Stop the FIM now */
+ fim = &port->fim;
+ ret = fim_send_stop(fim);
+ if (ret)
+ pk_err("Couldn't stop the FIM %i\n", fim->picnr);
+
+ return ret;
+}
+
+static int fim_usb_resume(struct platform_device *pdev)
+{
+ struct fim_usb_port *port;
+
+ port = dev_get_drvdata(&pdev->dev);
+ if (!port)
+ return -ENXIO;
+
+ return fim_usb_restart_fim(port);
+}
+#else
+#define fim_usb_suspend NULL
+#define fim_usb_resume NULL
+#endif
+
+static struct platform_driver fim_usb_driver = {
+ .driver = {
+ .name = FIM_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = fim_usb_probe,
+ .remove = __exit_p(fim_usb_remove),
+ .suspend = fim_usb_suspend,
+ .resume = fim_usb_resume,
+};
+
+static int __init fim_usb_init(void)
+{
+ int cnt, pos, ret, pics;
+ struct fim_usb_string *pstr;
+ u16 *pdata;
+
+ /* Check for the passed number parameter */
+ if (fim_check_numbers_param(fims_number)) {
+ pk_err("Invalid number '%i' of FIMs to handle\n", fims_number);
+ return -EINVAL;
+ }
+
+ pk_info("%s: version %s [%s | %s]\n", FIM_DRIVER_NAME, DRIVER_VERSION,
+ __DATE__, __TIME__);
+
+ /* For having an easier descriptors handling we only want to use static data buffers */
+ for (cnt = 0; cnt < ARRAY_SIZE(fim_usb_strings); cnt++) {
+ pstr = &fim_usb_strings[cnt];
+ pdata = pstr->data;
+
+ for (pos = 0; pos < strlen(pstr->s); pos++)
+ *(pdata + pos) = cpu_to_le16(*(pstr->s + pos));
+
+ pstr->desc.bLength = sizeof(pstr->desc) + (2 * pos);
+ }
+
+ pics = fim_number_pics();
+ fim_usb_ports_max = pics;
+ fim_usb_ports = kzalloc(pics, sizeof(*fim_usb_ports));
+ if (!fim_usb_ports)
+ return -ENOMEM;
+
+ /* We need a reboot notifier for disabling the pullup */
+ ret = register_reboot_notifier(&fim_usb_reboot_notifier);
+ if (ret) {
+ pk_err("Reboot notifier register failed, %i\n", ret);
+ goto err_exit_init;
+ }
+
+ /* Register the internal UART driver */
+ /* Get the number of maximal available FIMs */
+ fim_usb_uart_driver.owner = THIS_MODULE;
+ fim_usb_uart_driver.driver_name = FIM_DRIVER_NAME;
+ fim_usb_uart_driver.dev_name = FIM_USB_UART_DEV_NAME;
+ fim_usb_uart_driver.nr = pics;
+ ret = uart_register_driver(&fim_usb_uart_driver);
+ if (ret)
+ goto err_unreg_noti;
+
+ ret = platform_driver_register(&fim_usb_driver);
+ if (ret)
+ goto err_unreg_uart;
+
+ return 0;
+
+ err_unreg_uart:
+ uart_unregister_driver(&fim_usb_uart_driver);
+
+ err_unreg_noti:
+ unregister_reboot_notifier(&fim_usb_reboot_notifier);
+
+ err_exit_init:
+ kfree(fim_usb_ports);
+ return ret;
+}
+
+static void __exit fim_usb_exit(void)
+{
+ pk_info("Removing the FIM USB driver [%s | %s]\n", __DATE__, __TIME__);
+ platform_driver_unregister(&fim_usb_driver);
+ uart_unregister_driver(&fim_usb_uart_driver);
+
+ /* Free the resources for the reboot notifier */
+ unregister_reboot_notifier(&fim_usb_reboot_notifier);
+ kfree(fim_usb_ports);
+}
+
+module_init(fim_usb_init);
+module_exit(fim_usb_exit);
+
+MODULE_DESCRIPTION(FIM_DRIVER_DESC);
+MODULE_AUTHOR(FIM_DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:fim_usb");
+
diff --git a/drivers/fims/usb/fim_usb.h b/drivers/fims/usb/fim_usb.h
new file mode 100644
index 000000000000..a650ebcf9d10
--- /dev/null
+++ b/drivers/fims/usb/fim_usb.h
@@ -0,0 +1,185 @@
+
+/*
+ * Automatic generated header file with the firmware code for the FIM
+ * Input binary : fim_firmware.bin
+ * Output header : fim_usb.h
+ * Structure : fim_usb_firmware
+ */
+
+static const unsigned char fim_usb_firmware[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x91, 0x02, 0x00, 0x00, 0x4a, 0x2a, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x03, 0x08,
+ 0x83, 0x12, 0xa1, 0x00, 0x11, 0x08, 0x81, 0x00,
+ 0x0b, 0x11, 0x8b, 0x17, 0x27, 0x19, 0x4e, 0x28,
+ 0xa4, 0x1b, 0x55, 0x29, 0x83, 0x16, 0x22, 0x08,
+ 0x10, 0x05, 0x03, 0x1d, 0x2d, 0x28, 0x23, 0x08,
+ 0x10, 0x05, 0x03, 0x1d, 0x3e, 0x28, 0x83, 0x12,
+ 0x25, 0x1c, 0x51, 0x29, 0x83, 0x16, 0x81, 0x16,
+ 0x83, 0x12, 0x25, 0x16, 0x83, 0x16, 0xa1, 0x14,
+ 0x83, 0x16, 0x22, 0x08, 0x10, 0x05, 0x03, 0x1d,
+ 0x2c, 0x28, 0x23, 0x08, 0x10, 0x05, 0x03, 0x1d,
+ 0x2c, 0x28, 0x22, 0x28, 0x76, 0x28, 0x23, 0x08,
+ 0x10, 0x05, 0x03, 0x1d, 0x74, 0x2a, 0x83, 0x12,
+ 0xab, 0x14, 0x2b, 0x18, 0x45, 0x28, 0x06, 0x30,
+ 0x2e, 0x02, 0x03, 0x19, 0x43, 0x28, 0xae, 0x01,
+ 0x83, 0x16, 0x21, 0x10, 0xa1, 0x14, 0x76, 0x28,
+ 0x83, 0x12, 0xab, 0x10, 0x2b, 0x1c, 0x45, 0x28,
+ 0x35, 0x28, 0xae, 0x01, 0x76, 0x28, 0xae, 0x0a,
+ 0x07, 0x30, 0x2e, 0x02, 0x03, 0x19, 0x24, 0x14,
+ 0x83, 0x16, 0x21, 0x14, 0xa1, 0x14, 0x76, 0x28,
+ 0xa7, 0x10, 0xa7, 0x19, 0x65, 0x28, 0x27, 0x18,
+ 0x64, 0x28, 0x83, 0x16, 0x22, 0x08, 0x10, 0x05,
+ 0x03, 0x19, 0x5e, 0x28, 0x22, 0x08, 0xff, 0x3a,
+ 0x98, 0x05, 0x23, 0x08, 0x98, 0x04, 0x76, 0x28,
+ 0x22, 0x08, 0x98, 0x04, 0x23, 0x08, 0xff, 0x3a,
+ 0x98, 0x05, 0x76, 0x28, 0x76, 0x28, 0x27, 0x1a,
+ 0x6f, 0x28, 0x83, 0x16, 0x22, 0x08, 0xff, 0x3a,
+ 0x98, 0x05, 0x23, 0x08, 0xff, 0x3a, 0x98, 0x05,
+ 0x76, 0x28, 0x83, 0x16, 0x22, 0x08, 0xff, 0x3a,
+ 0x98, 0x05, 0x23, 0x08, 0x98, 0x04, 0x76, 0x28,
+ 0x83, 0x12, 0xab, 0x0c, 0x21, 0x08, 0x83, 0x00,
+ 0xa0, 0x0e, 0x20, 0x0e, 0x09, 0x00, 0x83, 0x16,
+ 0xa1, 0x01, 0x83, 0x12, 0xae, 0x01, 0xab, 0x01,
+ 0xa6, 0x01, 0xa5, 0x01, 0xa4, 0x01, 0x97, 0x1d,
+ 0x8b, 0x28, 0x83, 0x16, 0x12, 0x08, 0x9a, 0x00,
+ 0x83, 0x12, 0x39, 0x21, 0x80, 0x30, 0x30, 0x02,
+ 0x03, 0x19, 0x93, 0x28, 0x24, 0x16, 0x24, 0x08,
+ 0x36, 0x22, 0x30, 0x08, 0x2b, 0x22, 0x25, 0x14,
+ 0x39, 0x21, 0x30, 0x08, 0x2b, 0x22, 0x69, 0x30,
+ 0x30, 0x02, 0x03, 0x1d, 0x9f, 0x28, 0xa6, 0x14,
+ 0xef, 0x28, 0xe1, 0x30, 0x30, 0x02, 0x03, 0x1d,
+ 0xa5, 0x28, 0x26, 0x15, 0xef, 0x28, 0x2d, 0x30,
+ 0x30, 0x02, 0x03, 0x1d, 0xab, 0x28, 0x26, 0x14,
+ 0xef, 0x28, 0xc3, 0x30, 0x30, 0x02, 0x03, 0x1d,
+ 0xb1, 0x28, 0xa6, 0x15, 0xbc, 0x28, 0x4b, 0x30,
+ 0x30, 0x02, 0x03, 0x1d, 0xb7, 0x28, 0xa6, 0x15,
+ 0xbc, 0x28, 0x34, 0x21, 0x24, 0x08, 0x36, 0x22,
+ 0xb6, 0x01, 0x7d, 0x28, 0xc2, 0x01, 0x43, 0x30,
+ 0x84, 0x00, 0x39, 0x21, 0x25, 0x1a, 0xc8, 0x28,
+ 0x30, 0x08, 0x80, 0x00, 0x84, 0x0a, 0xc2, 0x0a,
+ 0x2b, 0x22, 0xbf, 0x28, 0xff, 0x30, 0xbd, 0x00,
+ 0xbe, 0x00, 0xc2, 0x03, 0xc2, 0x03, 0x03, 0x19,
+ 0xdb, 0x28, 0x43, 0x30, 0x84, 0x00, 0x00, 0x08,
+ 0x08, 0x30, 0xbf, 0x00, 0x06, 0x22, 0x80, 0x0c,
+ 0xbf, 0x0b, 0xd4, 0x28, 0x84, 0x0a, 0xc2, 0x0b,
+ 0xd1, 0x28, 0x3e, 0x08, 0xff, 0x3a, 0x41, 0x02,
+ 0x03, 0x1d, 0xe6, 0x28, 0x3d, 0x08, 0xff, 0x3a,
+ 0x40, 0x02, 0x03, 0x1d, 0xe6, 0x28, 0xe7, 0x28,
+ 0xa4, 0x14, 0x24, 0x08, 0x36, 0x22, 0xb6, 0x1c,
+ 0x7d, 0x28, 0xa4, 0x1c, 0xad, 0x29, 0xb6, 0x01,
+ 0x7d, 0x28, 0x1f, 0x30, 0xbb, 0x00, 0xa5, 0x16,
+ 0xbc, 0x01, 0x39, 0x21, 0x30, 0x08, 0x2b, 0x22,
+ 0xba, 0x00, 0x7f, 0x39, 0x12, 0x02, 0x03, 0x1d,
+ 0xfd, 0x28, 0xa5, 0x17, 0xfd, 0x28, 0x39, 0x21,
+ 0x30, 0x08, 0x2b, 0x22, 0xb9, 0x00, 0x34, 0x21,
+ 0x3b, 0x08, 0x1f, 0x3a, 0x1f, 0x39, 0xbb, 0x00,
+ 0x40, 0x08, 0x1f, 0x39, 0x3b, 0x02, 0x03, 0x1d,
+ 0xa4, 0x14, 0xa5, 0x1f, 0xa4, 0x15, 0x24, 0x08,
+ 0x36, 0x22, 0xa5, 0x1f, 0x7d, 0x28, 0xa4, 0x18,
+ 0x7d, 0x28, 0xa6, 0x18, 0x17, 0x29, 0xb6, 0x14,
+ 0x7d, 0x28, 0x8c, 0x1d, 0x1a, 0x29, 0xb4, 0x29,
+ 0x28, 0x18, 0x1f, 0x29, 0x25, 0x22, 0xa9, 0x00,
+ 0x28, 0x14, 0xba, 0x0d, 0xb9, 0x0d, 0x0f, 0x30,
+ 0x39, 0x05, 0x29, 0x02, 0x03, 0x1d, 0xb4, 0x29,
+ 0xa4, 0x16, 0x24, 0x08, 0x17, 0x1d, 0x36, 0x22,
+ 0x28, 0x10, 0xd6, 0x21, 0x25, 0x22, 0xb5, 0x00,
+ 0x25, 0x22, 0xaf, 0x00, 0xbb, 0x21, 0xb5, 0x0b,
+ 0x2e, 0x29, 0xee, 0x29, 0x83, 0x21, 0x83, 0x12,
+ 0x25, 0x1e, 0xa4, 0x14, 0x08, 0x00, 0x08, 0x30,
+ 0xb2, 0x00, 0x83, 0x21, 0x83, 0x12, 0x25, 0x1a,
+ 0x08, 0x00, 0xb0, 0x0c, 0xb0, 0x13, 0xc0, 0x0d,
+ 0xc1, 0x0d, 0x40, 0x10, 0x83, 0x16, 0xa1, 0x10,
+ 0x21, 0x1c, 0x4b, 0x29, 0x83, 0x12, 0xb0, 0x17,
+ 0x40, 0x14, 0x83, 0x12, 0xa5, 0x1a, 0x14, 0x22,
+ 0xb2, 0x0b, 0x3b, 0x29, 0x08, 0x00, 0x83, 0x12,
+ 0xa4, 0x17, 0xb7, 0x01, 0x76, 0x28, 0x83, 0x12,
+ 0xb7, 0x0a, 0x37, 0x19, 0x72, 0x29, 0x22, 0x08,
+ 0x83, 0x16, 0x10, 0x05, 0x03, 0x1d, 0x64, 0x29,
+ 0x23, 0x08, 0x10, 0x05, 0x03, 0x1d, 0x64, 0x29,
+ 0x83, 0x12, 0x76, 0x28, 0x83, 0x16, 0x81, 0x16,
+ 0x83, 0x12, 0x24, 0x08, 0x83, 0x16, 0x00, 0x30,
+ 0x1a, 0x04, 0x03, 0x19, 0x6e, 0x29, 0x9a, 0x03,
+ 0x83, 0x12, 0x97, 0x1c, 0x36, 0x22, 0x7d, 0x28,
+ 0x83, 0x16, 0x81, 0x16, 0x83, 0x12, 0xa4, 0x13,
+ 0x24, 0x17, 0x24, 0x08, 0x36, 0x22, 0x83, 0x16,
+ 0x22, 0x08, 0x10, 0x05, 0x03, 0x1d, 0x7d, 0x28,
+ 0x23, 0x08, 0x10, 0x05, 0x03, 0x1d, 0x7d, 0x28,
+ 0x79, 0x29, 0x83, 0x16, 0x24, 0x08, 0x10, 0x05,
+ 0x03, 0x19, 0x90, 0x29, 0x89, 0x29, 0x24, 0x08,
+ 0x10, 0x05, 0x03, 0x19, 0x9b, 0x29, 0xa1, 0x1c,
+ 0x89, 0x29, 0x08, 0x00, 0x24, 0x08, 0x10, 0x05,
+ 0x03, 0x1d, 0x9b, 0x29, 0x23, 0x08, 0x10, 0x05,
+ 0x03, 0x19, 0x9b, 0x29, 0xa1, 0x1c, 0x90, 0x29,
+ 0x08, 0x00, 0x83, 0x12, 0x10, 0x08, 0x81, 0x00,
+ 0x83, 0x16, 0x81, 0x12, 0xa1, 0x1c, 0xa0, 0x29,
+ 0x08, 0x00, 0x83, 0x16, 0x22, 0x08, 0x10, 0x05,
+ 0x03, 0x19, 0x08, 0x00, 0x23, 0x08, 0x10, 0x05,
+ 0x03, 0x19, 0x08, 0x00, 0x74, 0x2a, 0x83, 0x12,
+ 0xd6, 0x21, 0xd2, 0x30, 0xaf, 0x00, 0xbb, 0x21,
+ 0xb6, 0x01, 0xee, 0x29, 0x83, 0x12, 0xd6, 0x21,
+ 0x5a, 0x30, 0xaf, 0x00, 0xbb, 0x21, 0xb6, 0x01,
+ 0xee, 0x29, 0x08, 0x30, 0xb4, 0x00, 0xaf, 0x0c,
+ 0x03, 0x1c, 0xc6, 0x29, 0xce, 0x21, 0x06, 0x30,
+ 0x2e, 0x02, 0x03, 0x19, 0xca, 0x21, 0xc7, 0x29,
+ 0xca, 0x21, 0xb4, 0x0b, 0xbd, 0x29, 0x08, 0x00,
+ 0xae, 0x01, 0x27, 0x10, 0xd2, 0x21, 0x08, 0x00,
+ 0x27, 0x14, 0xd2, 0x21, 0xae, 0x0a, 0x08, 0x00,
+ 0xa7, 0x14, 0xa7, 0x18, 0xd3, 0x29, 0x08, 0x00,
+ 0xae, 0x01, 0x27, 0x15, 0x11, 0x08, 0x81, 0x00,
+ 0x83, 0x16, 0x81, 0x12, 0x83, 0x12, 0xce, 0x21,
+ 0xce, 0x21, 0x83, 0x16, 0x22, 0x08, 0xff, 0x3a,
+ 0x99, 0x05, 0x23, 0x08, 0xff, 0x3a, 0x99, 0x05,
+ 0x25, 0x08, 0xff, 0x3a, 0x98, 0x05, 0x83, 0x12,
+ 0x80, 0x30, 0xaf, 0x00, 0xbb, 0x21, 0x08, 0x00,
+ 0xa7, 0x15, 0xca, 0x21, 0xca, 0x21, 0x27, 0x16,
+ 0xca, 0x21, 0x0a, 0x30, 0xaa, 0x00, 0x00, 0x00,
+ 0xaa, 0x0b, 0xf5, 0x29, 0x83, 0x16, 0x25, 0x08,
+ 0x98, 0x04, 0x83, 0x12, 0xca, 0x21, 0x83, 0x16,
+ 0x81, 0x16, 0x22, 0x08, 0x99, 0x04, 0x23, 0x08,
+ 0x99, 0x04, 0x83, 0x12, 0xa7, 0x01, 0x7d, 0x28,
+ 0x03, 0x10, 0xbd, 0x0d, 0xbe, 0x0d, 0xaa, 0x0d,
+ 0x2a, 0x08, 0x00, 0x06, 0x01, 0x39, 0x03, 0x19,
+ 0x13, 0x2a, 0x80, 0x30, 0xbe, 0x06, 0x05, 0x30,
+ 0xbd, 0x06, 0x08, 0x00, 0x03, 0x10, 0xbb, 0x0d,
+ 0x03, 0x01, 0xb0, 0x1b, 0x20, 0x30, 0xbb, 0x06,
+ 0xbb, 0x1e, 0x1e, 0x2a, 0x05, 0x30, 0xbb, 0x06,
+ 0xbc, 0x0a, 0x3c, 0x08, 0x0b, 0x3c, 0x03, 0x19,
+ 0xa5, 0x12, 0x08, 0x00, 0x24, 0x2a, 0x8c, 0x19,
+ 0x25, 0x2a, 0x00, 0x00, 0x0f, 0x08, 0x00, 0x00,
+ 0x08, 0x00, 0x8c, 0x1b, 0x24, 0x15, 0x8c, 0x1b,
+ 0x2b, 0x2a, 0x8b, 0x13, 0x00, 0x00, 0x8e, 0x01,
+ 0x8d, 0x00, 0x00, 0x00, 0x8b, 0x17, 0x08, 0x00,
+ 0x8c, 0x1b, 0x24, 0x15, 0x8c, 0x1b, 0x36, 0x2a,
+ 0x8b, 0x13, 0x00, 0x00, 0x8e, 0x0a, 0x8d, 0x00,
+ 0x00, 0x00, 0x8b, 0x17, 0x08, 0x00, 0x24, 0x15,
+ 0x09, 0x14, 0x83, 0x16, 0x89, 0x1f, 0x44, 0x2a,
+ 0x83, 0x12, 0x03, 0x01, 0x89, 0x00, 0x49, 0x2a,
+ 0x7f, 0x22, 0x03, 0x30, 0x98, 0x00, 0x17, 0x1c,
+ 0x4d, 0x2a, 0x64, 0x00, 0x08, 0x30, 0xb2, 0x00,
+ 0x83, 0x12, 0x13, 0x08, 0x83, 0x16, 0xa2, 0x00,
+ 0x83, 0x12, 0x14, 0x08, 0x83, 0x16, 0xa3, 0x00,
+ 0x83, 0x12, 0x15, 0x08, 0x83, 0x16, 0xa4, 0x00,
+ 0x83, 0x12, 0x16, 0x08, 0x83, 0x16, 0xa5, 0x00,
+ 0x83, 0x16, 0x98, 0x01, 0x23, 0x08, 0x98, 0x04,
+ 0x25, 0x08, 0x98, 0x04, 0x99, 0x01, 0x22, 0x08,
+ 0x99, 0x04, 0x23, 0x08, 0x99, 0x04, 0x24, 0x08,
+ 0x99, 0x04, 0x83, 0x12, 0x8b, 0x17, 0x8b, 0x16,
+ 0x83, 0x12, 0x7d, 0x28, 0x83, 0x16, 0x81, 0x16,
+ 0x22, 0x08, 0x10, 0x05, 0x03, 0x19, 0x7d, 0x28,
+ 0x23, 0x08, 0x10, 0x05, 0x03, 0x19, 0x7d, 0x28,
+ 0x74, 0x2a, 0x83, 0x12, 0x20, 0x30, 0x84, 0x00,
+ 0x80, 0x01, 0x84, 0x0a, 0x04, 0x08, 0x80, 0x3c,
+ 0x03, 0x1d, 0x82, 0x2a, 0xa0, 0x30, 0x84, 0x00,
+ 0x80, 0x01, 0x84, 0x0a, 0x04, 0x08, 0xff, 0x3c,
+ 0x03, 0x1d, 0x8a, 0x2a, 0x08, 0x00
+};
+
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 82020abc329e..4ec50128aa50 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -49,6 +49,7 @@ struct gpio_desc {
#define FLAG_RESERVED 2
#define FLAG_EXPORT 3 /* protected by sysfs_lock */
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
+#define FLAG_IS_WAKEUP 5
#ifdef CONFIG_DEBUG_FS
const char *label;
@@ -236,6 +237,52 @@ static ssize_t gpio_direction_store(struct device *dev,
static const DEVICE_ATTR(direction, 0644,
gpio_direction_show, gpio_direction_store);
+static ssize_t gpio_wakeup_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else {
+ status = sprintf(buf, "%s\n",
+ test_bit(FLAG_IS_WAKEUP, &desc->flags)
+ ? "[enabled] disabled" : "enabled [disabled]");
+ }
+
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+/* Enables the wakeup-capability on one GPIO */
+static ssize_t gpio_wakeup_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ const struct gpio_desc *desc = dev_get_drvdata(dev);
+ unsigned gpio = desc - gpio_desc;
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else if (sysfs_streq(buf, "enabled"))
+ status = gpio_wakeup_configure(gpio, 1);
+ else if (sysfs_streq(buf, "disabled"))
+ status = gpio_wakeup_configure(gpio, 0);
+ else
+ status = -EINVAL;
+
+ mutex_unlock(&sysfs_lock);
+ return status ? : size;
+}
+
+static const DEVICE_ATTR(wakeup, 0644,
+ gpio_wakeup_show, gpio_wakeup_store);
+
static ssize_t gpio_value_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -287,6 +334,7 @@ static /*const*/ DEVICE_ATTR(value, 0644,
static const struct attribute *gpio_attrs[] = {
&dev_attr_direction.attr,
&dev_attr_value.attr,
+ &dev_attr_wakeup.attr,
NULL,
};
@@ -995,6 +1043,62 @@ fail:
}
EXPORT_SYMBOL_GPL(gpio_direction_output);
+int gpio_wakeup_configure(unsigned gpio, int value)
+{
+ unsigned long flags;
+ struct gpio_chip *chip;
+ struct gpio_desc *desc = &gpio_desc[gpio];
+ int status = -EINVAL;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+
+ if (!gpio_is_valid(gpio))
+ goto fail;
+ chip = desc->chip;
+ if (!chip || !chip->set || !chip->wakeup_configure)
+ goto fail;
+ gpio -= chip->base;
+ if (gpio >= chip->ngpio)
+ goto fail;
+ status = gpio_ensure_requested(desc, gpio);
+ if (status < 0)
+ goto fail;
+
+ /* now we know the gpio is valid and chip won't vanish */
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ might_sleep_if(extra_checks && chip->can_sleep);
+
+ if (status) {
+ status = chip->request(chip, gpio);
+ if (status < 0) {
+ pr_debug("GPIO-%d: chip request fail, %d\n",
+ chip->base + gpio, status);
+ /* and it's not available to anyone else ...
+ * gpio_request() is the fully clean solution.
+ */
+ goto lose;
+ }
+ }
+
+ status = chip->wakeup_configure(chip, gpio, value);
+ if (status == 0) {
+ if (value)
+ set_bit(FLAG_IS_WAKEUP, &desc->flags);
+ else
+ clear_bit(FLAG_IS_WAKEUP, &desc->flags);
+ }
+lose:
+ return status;
+fail:
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ if (status)
+ pr_debug("%s: gpio-%d status %d\n",
+ __func__, gpio, status);
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpio_wakeup_configure);
/* I/O calls are only valid after configuration completed; the relevant
* "is this a valid GPIO" error checks should already have been done.
@@ -1049,7 +1153,6 @@ EXPORT_SYMBOL_GPL(__gpio_get_value);
void __gpio_set_value(unsigned gpio, int value)
{
struct gpio_chip *chip;
-
chip = gpio_to_chip(gpio);
WARN_ON(extra_checks && chip->can_sleep);
chip->set(chip, gpio - chip->base, value);
@@ -1093,7 +1196,21 @@ int __gpio_to_irq(unsigned gpio)
}
EXPORT_SYMBOL_GPL(__gpio_to_irq);
+/**
+ * __gpio_set_pullupdown() - set pull up/down resistor
+ * Context: any
+ *
+ * This is used, where available, to set the internal
+ * GPIO pull up/down resistor.
+ */
+void __gpio_set_pullupdown(unsigned gpio, int value)
+{
+ struct gpio_chip *chip;
+ chip = gpio_to_chip(gpio);
+ chip->pullupdown(chip, gpio - chip->base, value);
+}
+EXPORT_SYMBOL_GPL(__gpio_set_pullupdown);
/* There's no value in making it easy to inline GPIO calls that may sleep.
* Common examples include ones connected to I2C or SPI chips.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index c709e821f04b..a92dc21ddf2c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -888,4 +888,14 @@ config HWMON_DEBUG_CHIP
a problem with I2C support and want to see more of what is going
on.
+config MXC_MMA7450
+ tristate "MMA7450 device driver"
+ depends on MACH_MX31_3DS
+ default n
+
+config SENSORS_ISL29003
+ tristate "ISL29003 Light Sensor"
+ depends on MACH_MX37_3DS || MACH_MX51_3DS
+ default y
+
endif # HWMON
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 58fc5be5355d..f2d69799c424 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -77,7 +77,9 @@ obj-$(CONFIG_SENSORS_VT1211) += vt1211.o
obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o
obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
+obj-$(CONFIG_MXC_MMA7450) += mxc_mma7450.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
+obj-$(CONFIG_SENSORS_ISL29003) += isl29003.o
ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/hwmon/isl29003.c b/drivers/hwmon/isl29003.c
new file mode 100644
index 000000000000..be3f9b90a183
--- /dev/null
+++ b/drivers/hwmon/isl29003.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file drivers/hwmon/isl29003.c
+ *
+ * @brief ISL29003 light sensor Driver
+ *
+ * @ingroup
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+
+enum isl29003_width {
+ ISL29003_WIDTH_16 = 0,
+ ISL29003_WIDTH_12,
+ ISL29003_WIDTH_8,
+ ISL29003_WIDTH_4,
+};
+
+enum isl29003_gain {
+ ISL29003_GAIN_1000 = 0,
+ ISL29003_GAIN_4000,
+ ISL29003_GAIN_16000,
+ ISL29003_GAIN_64000,
+};
+
+enum isl29003_mode {
+ ISL29003_MODE_DIODE1 = 0,
+ ISL29003_MODE_DIODE2,
+ ISL29003_MODE_DIODE1_2,
+};
+
+struct isl29003_param {
+ enum isl29003_width width;
+ enum isl29003_gain gain;
+ enum isl29003_mode mode;
+};
+
+/* bit definition for ISL29003_CMD reg */
+#define ENABLE 7
+#define ADCPD 6
+#define TIMEING_MODE 5
+#define MODE 2
+#define WIDTH 0
+
+/* bit definition for ISL29003_CTRL reg */
+#define INT_FLAG 5
+#define GAIN 2
+#define INT_PERSIST 0
+
+enum isl29003_reg {
+ ISL29003_CMD = 0,
+ ISL29003_CTRL,
+ ISL29003_THRS_HI,
+ ISL29003_THRS_LO,
+ ISL29003_LSB_S,
+ ISL29003_MSB_S,
+ ISL29003_LSB_T,
+ ISL29003_MSB_T,
+ ISL29003_SYNC_IIC = 0x80,
+ ISL29003_CLAR_INT = 0x40
+};
+
+/* default configure for ISL29003 */
+#define ISL29003_WIDTH_DEFAULT ISL29003_WIDTH_16
+#define ISL29003_GAIN_DEFAULT ISL29003_GAIN_16000
+#define ISL29003_MODE_DEFAULT ISL29003_MODE_DIODE1
+
+/* range table for different GAIN settings */
+int range[4] = { 973, 3892, 15568, 62272 };
+
+/* width table for different WIDTH settings */
+int width[4] = { 16, 1, 256, 16 };
+
+struct isl29003_data {
+ struct i2c_client *client;
+ struct device *hwmon_dev;
+ struct regulator *vdd_reg;
+ struct isl29003_param param;
+ int lux_coeff;
+ unsigned char enable;
+};
+
+static struct i2c_client *isl29003_client;
+
+/*!
+ * This function do the isl29003 register read.
+ */
+int isl29003_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/*!
+ * This function do the isl29003 register write.
+ */
+int isl29003_write(struct i2c_client *client, u8 reg, char value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/*!
+ * This function do the isl29003 config and enable.
+ */
+static int isl29003_on(void)
+{
+ unsigned char cmd;
+ int err = 0;
+ struct mxc_lightsensor_platform_data *ls_data;
+ struct isl29003_data *data = i2c_get_clientdata(isl29003_client);
+
+ if (data->enable)
+ goto exit;
+
+ ls_data = (struct mxc_lightsensor_platform_data *)
+ (isl29003_client->dev).platform_data;
+
+ /* coeff=range*100k/rext/2^n */
+ data->lux_coeff = range[data->param.gain] * 100 /
+ ls_data->rext / width[data->param.width];
+
+ if (data->vdd_reg)
+ regulator_enable(data->vdd_reg);
+ msleep(100);
+
+ cmd = data->param.gain << GAIN;
+ if (isl29003_write(isl29003_client, ISL29003_CTRL, cmd)) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ cmd = (data->param.width << WIDTH) | (data->param.mode << MODE) |
+ (1 << ENABLE);
+ if (isl29003_write(isl29003_client, ISL29003_CMD, cmd)) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ data->enable = 1;
+
+ pr_info("isl29003 on\n");
+ return 0;
+exit:
+ return err;
+}
+
+/*!
+ * This function shut down the isl29003.
+ */
+static int isl29003_off(void)
+{
+ struct isl29003_data *data = i2c_get_clientdata(isl29003_client);
+ int cmd;
+
+ if (!data->enable)
+ return 0;
+
+ cmd = isl29003_read(isl29003_client, ISL29003_CMD);
+ if (cmd < 0)
+ return -ENODEV;
+
+ cmd = ((cmd | (1 << ADCPD)) & (~(1 << ENABLE)));
+ if (isl29003_write(isl29003_client, ISL29003_CMD, (char)cmd))
+ return -ENODEV;
+
+ if (data->vdd_reg)
+ regulator_disable(data->vdd_reg);
+
+ data->enable = 0;
+
+ pr_info("isl29003 off\n");
+ return 0;
+}
+
+/*!
+ * This function read the isl29003 lux registers and convert them to the lux
+ * value.
+ *
+ * @output buffer this param holds the lux value, when =-1, read fail
+ *
+ * @return 0
+ */
+static int isl29003_read_lux(void)
+{
+ int d;
+ int lux;
+ struct isl29003_data *data = i2c_get_clientdata(isl29003_client);
+
+ d = isl29003_read(isl29003_client, ISL29003_MSB_S);
+ if (d < 0)
+ goto err;
+
+ lux = d;
+ d = isl29003_read(isl29003_client, ISL29003_LSB_S);
+ if (d < 0)
+ goto err;
+
+ lux = (lux << 8) + d;
+
+ if (data->param.width < ISL29003_WIDTH_8)
+ lux = (data->lux_coeff * lux) >> 12;
+ else
+ lux = data->lux_coeff * lux;
+
+ return lux;
+err:
+ return -1;
+}
+
+static ssize_t ls_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ char *endp;
+ int enable = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ if (enable == 1) {
+ if (isl29003_on())
+ pr_info("device open fail\n");
+ }
+ if (enable == 0) {
+ if (isl29003_off())
+ pr_info("device powerdown fail\n");
+ }
+
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(enable, S_IWUGO, NULL, ls_enable, 0);
+
+static ssize_t show_lux(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", isl29003_read_lux());
+}
+
+static SENSOR_DEVICE_ATTR(lux, S_IRUGO, show_lux, NULL, 0);
+
+static int isl29003_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ int err = 0;
+ struct isl29003_data *data;
+ struct regulator *vdd_reg;
+ struct mxc_lightsensor_platform_data *ls_data;
+
+ ls_data = (struct mxc_lightsensor_platform_data *)
+ (client->dev).platform_data;
+
+ if (ls_data && ls_data->vdd_reg)
+ vdd_reg = regulator_get(&client->dev, ls_data->vdd_reg);
+ else
+ vdd_reg = NULL;
+
+ /* check the existence of the device */
+ if (vdd_reg)
+ regulator_enable(vdd_reg);
+ msleep(100);
+
+ if (isl29003_write(client, ISL29003_CMD, 0))
+ err = -ENODEV;
+
+ if (!err)
+ if (isl29003_read(client, ISL29003_CMD))
+ err = -ENODEV;
+
+ if (vdd_reg)
+ regulator_disable(vdd_reg);
+ if (err < 0)
+ goto exit1;
+
+ isl29003_client = client;
+ data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL);
+ if (data == NULL) {
+ err = -ENOMEM;
+ goto exit1;
+ }
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+
+ data->param.width = ISL29003_WIDTH_DEFAULT;
+ data->param.gain = ISL29003_GAIN_DEFAULT;
+ data->param.mode = ISL29003_MODE_DEFAULT;
+
+ data->enable = 0;
+
+ err = device_create_file(&client->dev,
+ &sensor_dev_attr_enable.dev_attr);
+ if (err)
+ goto exit2;
+
+ err = device_create_file(&client->dev, &sensor_dev_attr_lux.dev_attr);
+ if (err)
+ goto exit_remove1;
+
+ /* Register sysfs hooks */
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove2;
+ }
+
+ data->vdd_reg = vdd_reg;
+ return 0;
+
+exit_remove2:
+ device_remove_file(&client->dev, &sensor_dev_attr_lux.dev_attr);
+exit_remove1:
+ device_remove_file(&client->dev, &sensor_dev_attr_enable.dev_attr);
+exit2:
+ kfree(data);
+exit1:
+ if (vdd_reg) {
+ regulator_put(vdd_reg);
+ vdd_reg = NULL;
+ }
+ isl29003_client = NULL;
+ return err;
+}
+
+static int isl29003_i2c_remove(struct i2c_client *client)
+{
+ int err;
+ struct isl29003_data *data = i2c_get_clientdata(client);
+
+ if (data->vdd_reg) {
+ regulator_put(data->vdd_reg);
+ data->vdd_reg = NULL;
+ }
+ hwmon_device_unregister(data->hwmon_dev);
+ device_remove_file(&client->dev, &sensor_dev_attr_enable.dev_attr);
+ device_remove_file(&client->dev, &sensor_dev_attr_lux.dev_attr);
+ err = i2c_detach_client(client);
+ if (err)
+ return err;
+ kfree(client);
+ return 0;
+}
+
+static int isl29003_suspend(struct i2c_client *client, pm_message_t message)
+{
+ int cmd;
+
+ struct isl29003_data *data = i2c_get_clientdata(client);
+
+ if (!data->enable)
+ goto exit;
+
+ cmd = isl29003_read(client, ISL29003_CMD);
+ if (cmd < 0)
+ goto err;
+
+ cmd = (cmd | (1 << ADCPD));
+ if (isl29003_write(client, ISL29003_CMD, (char)cmd))
+ goto err;
+exit:
+ return 0;
+err:
+ return -ENODEV;
+}
+
+static int isl29003_resume(struct i2c_client *client)
+{
+ int cmd;
+
+ struct isl29003_data *data = i2c_get_clientdata(client);
+
+ if (!data->enable)
+ goto exit;
+
+ cmd = isl29003_read(client, ISL29003_CMD);
+ if (cmd < 0)
+ goto err;
+
+ cmd = (cmd & (~(1 << ADCPD)));
+ if (isl29003_write(client, ISL29003_CMD, (char)cmd))
+ goto err;
+exit:
+ return 0;
+err:
+ return -ENODEV;
+}
+
+static const struct i2c_device_id isl29003_id[] = {
+ {"isl29003", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, isl29003_id);
+
+static struct i2c_driver isl29003_driver = {
+ .driver = {
+ .name = "isl29003",
+ },
+ .probe = isl29003_i2c_probe,
+ .remove = isl29003_i2c_remove,
+ .suspend = isl29003_suspend,
+ .resume = isl29003_resume,
+ .id_table = isl29003_id,
+};
+
+static int __init isl29003_init(void)
+{
+ return i2c_add_driver(&isl29003_driver);;
+}
+
+static void __exit isl29003_cleanup(void)
+{
+ i2c_del_driver(&isl29003_driver);
+}
+
+module_init(isl29003_init);
+module_exit(isl29003_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("ISL29003 light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/mxc_mma7450.c b/drivers/hwmon/mxc_mma7450.c
new file mode 100644
index 000000000000..1c78631044b9
--- /dev/null
+++ b/drivers/hwmon/mxc_mma7450.c
@@ -0,0 +1,788 @@
+/*
+ * linux/drivers/hwmon/mma7450.c
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*include file*/
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/input-polldev.h>
+#include <linux/hwmon.h>
+#include <linux/regulator/consumer.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <mach/mxc.h>
+
+/*macro define*/
+#define MMA7450_I2C_ADDR 0x1D
+#define DEVICE_NAME "mma7450"
+#define POLL_INTERVAL 100
+#define DEBUG
+
+#define INPUT_FUZZ 4
+#define INPUT_FLAT 4
+
+enum {
+ REG_XOUTL = 0x00,
+ REG_XOUTH,
+ REG_YOUTL,
+ REG_YOUTH,
+ REG_ZOUTL,
+ REG_ZOUTH,
+ REG_XOUT8,
+ REG_YOUT8,
+ REG_ZOUT8,
+ REG_STATUS,
+ REG_DETSRC,
+ REG_TOUT,
+ REG_RESERVED_0,
+ REG_I2CAD,
+ REG_USRINF,
+ REG_WHOAMI,
+ REG_XOFFL,
+ REG_XOFFH,
+ REG_YOFFL,
+ REG_YOFFH,
+ REG_ZOFFL,
+ REG_ZOFFH,
+ REG_MCTL,
+ REG_INTRST,
+ REG_CTL1,
+ REG_CTL2,
+ REG_LDTH,
+ REG_PDTH,
+ REG_PD,
+ REG_LT,
+ REG_TW,
+ REG_REVERVED_1,
+};
+
+enum {
+ MOD_STANDBY = 0,
+ MOD_MEASURE,
+ MOD_LEVEL_D,
+ MOD_PULSE_D,
+};
+
+enum {
+ INT_1L_2P = 0,
+ INT_1P_2L,
+ INT_1SP_2P,
+};
+
+struct mma7450_status {
+ u8 mod;
+ u8 ctl1;
+ u8 ctl2;
+};
+
+/*forward declear*/
+static ssize_t mma7450_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t mma7450_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count);
+static int mma7450_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int mma7450_remove(struct i2c_client *client);
+static int mma7450_suspend(struct i2c_client *client, pm_message_t state);
+static int mma7450_resume(struct i2c_client *client);
+static void mma_bh_handler(struct work_struct *work);
+
+/*definition*/
+static struct regulator *reg_dvdd_io;
+static struct regulator *reg_avdd;
+static struct i2c_client *mma7450_client;
+static struct device *hwmon_dev;
+static struct input_polled_dev *mma7450_idev;
+static struct mxc_mma7450_platform_data *plat_data;
+static u8 mma7450_mode;
+static struct device_attribute mma7450_dev_attr = {
+ .attr = {
+ .name = "mma7450_ctl",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .show = mma7450_show,
+ .store = mma7450_store,
+};
+
+static const struct i2c_device_id mma7450_id[] = {
+ {"mma7450", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mma7450_id);
+
+static struct i2c_driver i2c_mma7450_driver = {
+ .driver = {
+ .name = "mma7450",
+ },
+ .probe = mma7450_probe,
+ .remove = mma7450_remove,
+ .suspend = mma7450_suspend,
+ .resume = mma7450_resume,
+ .id_table = mma7450_id,
+};
+
+static struct mma7450_status mma_status = {
+ .mod = 0,
+ .ctl1 = 0,
+ .ctl2 = 0,
+};
+
+DECLARE_WORK(mma_work, mma_bh_handler);
+
+#ifdef DEBUG
+enum {
+ MMA_REG_R = 0,
+ MMA_REG_W,
+ MMA_SET_MOD,
+ MMA_SET_L_THR,
+ MMA_SET_P_THR,
+ MMA_SET_INTP,
+ MMA_SET_INTB,
+ MMA_SET_G,
+ MMA_I2C_EABLE,
+ MMA_OFF_X,
+ MMA_OFF_Y,
+ MMA_OFF_Z,
+ MMA_SELF_TEST,
+ MMA_SET_LDPL,
+ MMA_SET_PDPL,
+ MMA_SET_PDV,
+ MMA_SET_LTV,
+ MMA_SET_TW,
+ MMA_CMD_MAX
+};
+
+static char *command[MMA_CMD_MAX] = {
+ [MMA_REG_R] = "readreg",
+ [MMA_REG_W] = "writereg",
+ [MMA_SET_MOD] = "setmod",
+ [MMA_SET_L_THR] = "setlt",
+ [MMA_SET_P_THR] = "setpt",
+ [MMA_SET_INTP] = "setintp",
+ [MMA_SET_INTB] = "setintb",
+ [MMA_SET_G] = "setg",
+ [MMA_I2C_EABLE] = "setie",
+ [MMA_OFF_X] = "setxo",
+ [MMA_OFF_Y] = "setyo",
+ [MMA_OFF_Z] = "setzo",
+ [MMA_SELF_TEST] = "selft",
+ [MMA_SET_LDPL] = "setldp",
+ [MMA_SET_PDPL] = "setpdp",
+ [MMA_SET_PDV] = "setpdv",
+ [MMA_SET_LTV] = "setltv",
+ [MMA_SET_TW] = "settw",
+};
+
+static void set_mod(u8 mode)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(mma7450_client, REG_MCTL);
+ /* shall I test the ret value? */
+ ret = (ret & ~0x3) | (mode & 0x3);
+ mma_status.mod = ret;
+ i2c_smbus_write_byte_data(mma7450_client, REG_MCTL, ret);
+}
+
+static void set_level_thr(u8 lth)
+{
+ i2c_smbus_write_byte_data(mma7450_client, REG_LDTH, lth);
+}
+
+static void set_pulse_thr(u8 pth)
+{
+ i2c_smbus_write_byte_data(mma7450_client, REG_PDTH, pth);
+}
+
+static void set_int_pin(u8 pin)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(mma7450_client, REG_CTL1);
+ ret = (ret & ~0x1) | (pin & 0x1);
+ mma_status.ctl1 = ret;
+ i2c_smbus_write_byte_data(mma7450_client, REG_CTL1, ret);
+}
+
+static void set_int_bit(u8 bit)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(mma7450_client, REG_CTL1);
+ ret = (ret & ~0x6) | ((bit << 1) & 0x6);
+ mma_status.ctl1 = ret;
+ i2c_smbus_write_byte_data(mma7450_client, REG_CTL1, ret);
+}
+
+static void set_g_level(u8 gl)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(mma7450_client, REG_MCTL);
+ ret = (ret & ~0xC) | ((gl << 2) & 0xC);
+ i2c_smbus_write_byte_data(mma7450_client, REG_MCTL, ret);
+}
+
+static void set_i2c_enable(u8 i2c_e)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(mma7450_client, REG_I2CAD);
+ ret = (ret & ~0x80) | ((i2c_e << 7) & 0x80);
+ i2c_smbus_write_byte_data(mma7450_client, REG_I2CAD, ret);
+}
+
+static void set_x_offset(u16 xo)
+{
+ u8 data;
+
+ data = (xo & 0xFF);
+ i2c_smbus_write_byte_data(mma7450_client, REG_XOFFL, data);
+ data = (xo & 0xFF00) >> 8;
+ i2c_smbus_write_byte_data(mma7450_client, REG_XOFFH, data);
+}
+
+static void set_y_offset(u16 yo)
+{
+ u8 data;
+
+ data = (yo & 0xFF);
+ i2c_smbus_write_byte_data(mma7450_client, REG_YOFFL, data);
+ data = (yo & 0xFF00) >> 8;
+ i2c_smbus_write_byte_data(mma7450_client, REG_YOFFH, data);
+}
+
+static void set_z_offset(u16 zo)
+{
+ u8 data;
+
+ data = (zo & 0xFF);
+ i2c_smbus_write_byte_data(mma7450_client, REG_ZOFFL, data);
+ data = (zo & 0xFF00) >> 8;
+ i2c_smbus_write_byte_data(mma7450_client, REG_ZOFFH, data);
+}
+
+static void selftest(u8 st)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(mma7450_client, REG_MCTL);
+ ret = (ret & ~0x10) | ((st << 4) & 0x10);
+ i2c_smbus_write_byte_data(mma7450_client, REG_MCTL, ret);
+}
+
+static void set_level_det_p(u8 ldp)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(mma7450_client, REG_CTL2);
+ ret = (ret & ~0x1) | ((ldp << 0) & 0x1);
+ mma_status.ctl2 = ret;
+ i2c_smbus_write_byte_data(mma7450_client, REG_CTL2, ret);
+}
+
+static void set_pulse_det_p(u8 pdp)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(mma7450_client, REG_CTL2);
+ ret = (ret & ~0x2) | ((pdp << 1) & 0x2);
+ mma_status.ctl2 = ret;
+ i2c_smbus_write_byte_data(mma7450_client, REG_CTL2, ret);
+}
+
+static void set_pulse_duration(u8 pd)
+{
+ i2c_smbus_write_byte_data(mma7450_client, REG_PD, pd);
+}
+
+static void set_latency_time(u8 lt)
+{
+ i2c_smbus_write_byte_data(mma7450_client, REG_LT, lt);
+}
+
+static void set_time_window(u8 tw)
+{
+ i2c_smbus_write_byte_data(mma7450_client, REG_TW, tw);
+}
+
+static void parse_arg(const char *arg, int *reg, int *value)
+{
+ const char *p;
+
+ for (p = arg;; p++) {
+ if (*p == ' ' || *p == '\0')
+ break;
+ }
+
+ p++;
+
+ *reg = simple_strtoul(arg, NULL, 16);
+ *value = simple_strtoul(p, NULL, 16);
+}
+
+static void cmd_read_reg(const char *arg)
+{
+ int reg, value, ret;
+
+ parse_arg(arg, &reg, &value);
+ ret = i2c_smbus_read_byte_data(mma7450_client, reg);
+ dev_info(&mma7450_client->dev, "read reg0x%x = %x\n", reg, ret);
+}
+
+static void cmd_write_reg(const char *arg)
+{
+ int reg, value, ret;
+
+ parse_arg(arg, &reg, &value);
+ ret = i2c_smbus_write_byte_data(mma7450_client, reg, value);
+ dev_info(&mma7450_client->dev, "write reg result %s\n",
+ ret ? "failed" : "success");
+}
+
+static int exec_command(const char *buf, size_t count)
+{
+ const char *p, *s;
+ const char *arg;
+ int i, value = 0;
+
+ for (p = buf;; p++) {
+ if (*p == ' ' || *p == '\0' || p - buf >= count)
+ break;
+ }
+ arg = p + 1;
+
+ for (i = MMA_REG_R; i < MMA_CMD_MAX; i++) {
+ s = command[i];
+ if (s && !strncmp(buf, s, p - buf)) {
+ dev_info(&mma7450_client->dev, "command %s\n", s);
+ goto mma_exec_command;
+ }
+ }
+
+ dev_err(&mma7450_client->dev, "command is not found\n");
+ return -1;
+
+ mma_exec_command:
+ if (i != MMA_REG_R && i != MMA_REG_W)
+ value = simple_strtoul(arg, NULL, 16);
+
+ switch (i) {
+ case MMA_REG_R:
+ cmd_read_reg(arg);
+ break;
+ case MMA_REG_W:
+ cmd_write_reg(arg);
+ break;
+ case MMA_SET_MOD:
+ set_mod(value);
+ break;
+ case MMA_SET_L_THR:
+ set_level_thr(value);
+ break;
+ case MMA_SET_P_THR:
+ set_pulse_thr(value);
+ break;
+ case MMA_SET_INTP:
+ set_int_pin(value);
+ break;
+ case MMA_SET_INTB:
+ set_int_bit(value);
+ break;
+ case MMA_SET_G:
+ set_g_level(value);
+ break;
+ case MMA_I2C_EABLE:
+ set_i2c_enable(value);
+ break;
+ case MMA_OFF_X:
+ set_x_offset(value);
+ break;
+ case MMA_OFF_Y:
+ set_y_offset(value);
+ break;
+ case MMA_OFF_Z:
+ set_z_offset(value);
+ break;
+ case MMA_SELF_TEST:
+ selftest(value);
+ break;
+ case MMA_SET_LDPL:
+ set_level_det_p(value);
+ break;
+ case MMA_SET_PDPL:
+ set_pulse_det_p(value);
+ break;
+ case MMA_SET_PDV:
+ set_pulse_duration(value);
+ break;
+ case MMA_SET_LTV:
+ set_latency_time(value);
+ break;
+ case MMA_SET_TW:
+ set_time_window(value);
+ break;
+ default:
+ dev_err(&mma7450_client->dev, "command is not found\n");
+ break;
+ }
+
+ return 0;
+}
+
+static ssize_t mma7450_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, reg;
+
+ for (reg = REG_XOUTL; reg < REG_REVERVED_1; reg++) {
+ ret = i2c_smbus_read_byte_data(mma7450_client, reg);
+ dev_info(&mma7450_client->dev, "reg0x%02x:\t%03d\t0x%02x\n",
+ reg, (s8) ret, ret);
+ }
+
+ return 0;
+}
+
+static ssize_t mma7450_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ exec_command(buf, count);
+
+ return count;
+}
+
+#else
+
+static ssize_t mma7450_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+static ssize_t mma7450_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ return count;
+}
+
+#endif
+
+static void report_abs(void)
+{
+ u8 status, mod = mma_status.mod;
+ s16 x, y, z, tmp;
+
+ status = i2c_smbus_read_byte_data(mma7450_client, REG_STATUS);
+ if (!(status & 0x01)) { /* data ready in measurement mode? */
+ return;
+ }
+ if ((mod & 0x0c) == 0) { /* 8g range */
+ x = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_XOUTL);
+ x |= 0xFF00 &
+ (i2c_smbus_read_byte_data(mma7450_client, REG_XOUTH) << 8);
+ y = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_YOUTL);
+ y |= 0xFF00 &
+ (i2c_smbus_read_byte_data(mma7450_client, REG_YOUTH) << 8);
+ z = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_ZOUTL);
+ z |= 0xFF00 &
+ (i2c_smbus_read_byte_data(mma7450_client, REG_ZOUTH) << 8);
+ } else { /* 2g/4g range */
+ x = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_XOUT8);
+ y = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_YOUT8);
+ z = 0xFF & i2c_smbus_read_byte_data(mma7450_client, REG_ZOUT8);
+ }
+
+ status = i2c_smbus_read_byte_data(mma7450_client, REG_STATUS);
+ if (status & 0x02) { /* data is overwrite */
+ return;
+ }
+
+ /* convert signed 10bits to signed 16bits */
+ x = (short)(x << 6) >> 6;
+ y = (short)(y << 6) >> 6;
+ z = (short)(z << 6) >> 6;
+
+ input_report_abs(mma7450_idev->input, ABS_X, x);
+ input_report_abs(mma7450_idev->input, ABS_Y, y);
+ input_report_abs(mma7450_idev->input, ABS_Z, z);
+ input_sync(mma7450_idev->input);
+}
+
+static void mma_bh_handler(struct work_struct *work)
+{
+}
+
+static void mma7450_dev_poll(struct input_polled_dev *dev)
+{
+ report_abs();
+}
+
+static irqreturn_t mma7450_interrupt(int irq, void *dev_id)
+{
+ struct input_dev *input_dev = dev_id;
+ u8 int_bit, int_pin;
+
+ int_bit = mma_status.ctl1 & 0x6;
+ int_pin = mma_status.ctl1 & 0x1;
+
+ switch (mma_status.mod & 0x03) {
+ case 1:
+ /*only int1 report data ready int */
+ if (plat_data->int1 != irq)
+ goto error_bad_int;
+ schedule_work(&mma_work);
+ break;
+ case 2:
+ /* for level and pulse detection mode,
+ * choice tasklet to handle interrupt quickly.
+ * Currently, leave it doing nothing*/
+ if (plat_data->int1 == irq) {
+ if ((int_bit == 0) && (int_pin != 0))
+ goto error_bad_int;
+ if ((int_bit == 0x2) && (int_pin != 0x1))
+ goto error_bad_int;
+ if (int_bit == 0x4)
+ goto error_bad_int;
+ }
+ if (plat_data->int2 == irq) {
+ if ((int_bit == 0) && (int_pin != 0x1))
+ goto error_bad_int;
+ if ((int_bit == 0x2) && (int_pin != 0))
+ goto error_bad_int;
+ if (int_bit == 0x4)
+ goto error_bad_int;
+ }
+
+ dev_info(&input_dev->dev, "motion detected in level mod\n");
+
+ break;
+ case 3:
+ if (plat_data->int1 == irq) {
+ if ((int_bit == 0) && (int_pin != 0x1))
+ goto error_bad_int;
+ if ((int_bit == 0x2) && (int_pin != 0))
+ goto error_bad_int;
+ if ((int_bit == 0x4) && (int_pin != 0x1))
+ goto error_bad_int;
+ }
+ if (plat_data->int2 == irq) {
+ if ((int_bit == 0) && (int_pin != 0))
+ goto error_bad_int;
+ if ((int_bit == 0x2) && (int_pin != 0x1))
+ goto error_bad_int;
+ if ((int_bit == 0x4) && (int_pin != 0))
+ goto error_bad_int;
+ }
+
+ if (mma_status.ctl2 & 0x02)
+ dev_info(&input_dev->dev,
+ "freefall detected in pulse mod\n");
+ else
+ dev_info(&input_dev->dev,
+ "motion detected in pulse mod\n");
+
+ break;
+ case 0:
+ default:
+ break;
+ }
+ error_bad_int:
+ return IRQ_RETVAL(1);
+}
+
+static int mma7450_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct input_dev *idev;
+
+ plat_data =
+ (struct mxc_mma7450_platform_data *)client->dev.platform_data;
+ if (plat_data == NULL) {
+ dev_err(&client->dev, "lack of platform data!\n");
+ return -ENODEV;
+ }
+
+ /*enable power supply */
+ /*when to power on/off the power is to be considered later */
+ /*shall I check the return value */
+ reg_dvdd_io = regulator_get(&client->dev, plat_data->reg_dvdd_io);
+ if (reg_dvdd_io != ERR_PTR(-ENOENT))
+ regulator_enable(reg_dvdd_io);
+ else
+ return -EINVAL;
+
+ reg_avdd = regulator_get(&client->dev, plat_data->reg_avdd);
+ if (reg_avdd != ERR_PTR(-ENOENT))
+ regulator_enable(reg_avdd);
+ else {
+ regulator_put(reg_dvdd_io);
+ return -EINVAL;
+ }
+
+ /*bind the right device to the driver */
+ ret = i2c_smbus_read_byte_data(client, REG_I2CAD);
+ if (MMA7450_I2C_ADDR != (0x7F & ret)) { /*compare the address value */
+ dev_err(&client->dev,
+ "read chip ID 0x%x is not equal to 0x%x!\n", ret,
+ MMA7450_I2C_ADDR);
+ goto error_disable_power;
+ }
+ mma7450_client = client;
+
+ /*interrupt register */
+ /*when to register interrupt is to be considered later */
+
+ /*create device file in sysfs as user interface */
+ ret = device_create_file(&client->dev, &mma7450_dev_attr);
+ if (ret) {
+ dev_err(&client->dev, "create device file failed!\n");
+ goto error_disable_power;
+ }
+
+ /*register to hwmon device */
+ hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(hwmon_dev)) {
+ dev_err(&client->dev, "hwmon register failed!\n");
+ ret = PTR_ERR(hwmon_dev);
+ goto error_rm_dev_file;
+ }
+
+ /*input poll device register */
+ mma7450_idev = input_allocate_polled_device();
+ if (!mma7450_idev) {
+ dev_err(&client->dev, "alloc poll device failed!\n");
+ ret = -ENOMEM;
+ goto error_rm_hwmon_dev;
+ }
+ mma7450_idev->poll = mma7450_dev_poll;
+ mma7450_idev->poll_interval = POLL_INTERVAL;
+ idev = mma7450_idev->input;
+ idev->name = DEVICE_NAME;
+ idev->id.bustype = BUS_I2C;
+ idev->dev.parent = &client->dev;
+ idev->evbit[0] = BIT_MASK(EV_ABS);
+
+ input_set_abs_params(idev, ABS_X, -512, 512, INPUT_FUZZ, INPUT_FLAT);
+ input_set_abs_params(idev, ABS_Y, -512, 512, INPUT_FUZZ, INPUT_FLAT);
+ input_set_abs_params(idev, ABS_Z, -512, 512, INPUT_FUZZ, INPUT_FLAT);
+ ret = input_register_polled_device(mma7450_idev);
+ if (ret) {
+ dev_err(&client->dev, "register poll device failed!\n");
+ goto error_free_poll_dev;
+ }
+
+ /* configure gpio as input for interrupt monitor */
+ plat_data->gpio_pin_get();
+
+ set_irq_type(plat_data->int1, IRQF_TRIGGER_RISING);
+ /* register interrupt handle */
+ ret = request_irq(plat_data->int1, mma7450_interrupt,
+ IRQF_TRIGGER_RISING, DEVICE_NAME, idev);
+
+ if (ret) {
+ dev_err(&client->dev, "request_irq(%d) returned error %d\n",
+ plat_data->int1, ret);
+ goto error_rm_poll_dev;
+ }
+
+ set_irq_type(plat_data->int2, IRQF_TRIGGER_RISING);
+ ret = request_irq(plat_data->int2, mma7450_interrupt,
+ IRQF_TRIGGER_RISING, DEVICE_NAME, idev);
+ if (ret) {
+ dev_err(&client->dev, "request_irq(%d) returned error %d\n",
+ plat_data->int2, ret);
+ goto error_free_irq1;
+ }
+
+ dev_info(&client->dev, "mma7450 device is probed successfully.\n");
+
+ set_mod(1);
+ return 0; /*what value shall be return */
+
+ /*error handle */
+ error_free_irq1:
+ free_irq(plat_data->int1, 0);
+ error_rm_poll_dev:
+ input_unregister_polled_device(mma7450_idev);
+ error_free_poll_dev:
+ input_free_polled_device(mma7450_idev);
+ error_rm_hwmon_dev:
+ hwmon_device_unregister(hwmon_dev);
+ error_rm_dev_file:
+ device_remove_file(&client->dev, &mma7450_dev_attr);
+ error_disable_power:
+ regulator_disable(reg_dvdd_io); /*shall I check the return value */
+ regulator_disable(reg_avdd);
+ regulator_put(reg_dvdd_io);
+ regulator_put(reg_avdd);
+
+ return ret;
+}
+
+static int mma7450_remove(struct i2c_client *client)
+{
+ free_irq(plat_data->int2, mma7450_idev->input);
+ free_irq(plat_data->int1, mma7450_idev->input);
+ plat_data->gpio_pin_put();
+ input_unregister_polled_device(mma7450_idev);
+ input_free_polled_device(mma7450_idev);
+ hwmon_device_unregister(hwmon_dev);
+ device_remove_file(&client->dev, &mma7450_dev_attr);
+ regulator_disable(reg_dvdd_io); /*shall I check the return value */
+ regulator_disable(reg_avdd);
+ regulator_put(reg_dvdd_io);
+ regulator_put(reg_avdd);
+ return 0;
+}
+
+static int mma7450_suspend(struct i2c_client *client, pm_message_t state)
+{
+ mma7450_mode = i2c_smbus_read_byte_data(mma7450_client, REG_MCTL);
+ i2c_smbus_write_byte_data(mma7450_client, REG_MCTL,
+ mma7450_mode & ~0x3);
+ return 0;
+}
+
+static int mma7450_resume(struct i2c_client *client)
+{
+ i2c_smbus_write_byte_data(mma7450_client, REG_MCTL, mma7450_mode);
+ return 0;
+}
+
+static int __init init_mma7450(void)
+{
+ /*register driver */
+ printk(KERN_INFO "add mma i2c driver\n");
+ return i2c_add_driver(&i2c_mma7450_driver);
+}
+
+static void __exit exit_mma7450(void)
+{
+ printk(KERN_INFO "del mma i2c driver.\n");
+ return i2c_del_driver(&i2c_mma7450_driver);
+}
+
+module_init(init_mma7450);
+module_exit(exit_mma7450);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MMA7450 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c-slave/Kconfig b/drivers/i2c-slave/Kconfig
new file mode 100644
index 000000000000..6907a2ab6688
--- /dev/null
+++ b/drivers/i2c-slave/Kconfig
@@ -0,0 +1,40 @@
+#
+# I2C slave subsystem configuration
+#
+
+menuconfig I2C_SLAVE
+ bool "I2C Slave support"
+ depends on HAS_IOMEM
+ ---help---
+ I2C (pronounce: I-square-C) is a slow serial bus protocol used in
+ many micro controller applications and developed by Philips.
+
+ If you want I2C Slave support, you should say Y here.
+
+ This I2C Slave support can also be built as a module. If so, the module
+ will be called i2c-slave.
+
+if I2C_SLAVE
+
+config I2C_SLAVE_CORE
+ tristate "I2C SLAVE CORE"
+ default y
+ ---help---
+ i2c slave core.
+
+config MXC_I2C_SLAVE
+ tristate "MXC I2C SLAVE"
+ depends on I2C_SLAVE_CORE
+ default y
+ ---help---
+ mxc i2c slave support.
+
+config I2C_SLAVE_CLIENT
+ tristate "I2C SLAVE CLIENT"
+ default y
+ ---help---
+ i2c slave client that used when the master is on the same board.
+ this is for i2c master which is on the same board with the slave.
+
+endif # I2C_SLAVE
+
diff --git a/drivers/i2c-slave/Makefile b/drivers/i2c-slave/Makefile
new file mode 100644
index 000000000000..675407f405cf
--- /dev/null
+++ b/drivers/i2c-slave/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the i2c slave.
+#
+
+i2c_slave-objs := i2c_slave_ring_buffer.o i2c_slave_device.o i2c_slave_core.o
+obj-$(CONFIG_I2C_SLAVE_CORE) += i2c_slave.o
+obj-$(CONFIG_MXC_I2C_SLAVE) += mxc_i2c_slave.o
+obj-$(CONFIG_I2C_SLAVE_CLIENT) += i2c_slave_client.o
+
+
diff --git a/drivers/i2c-slave/i2c_slave_client.c b/drivers/i2c-slave/i2c_slave_client.c
new file mode 100644
index 000000000000..4068fe172b83
--- /dev/null
+++ b/drivers/i2c-slave/i2c_slave_client.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+
+struct i2c_client *i2c_slave_client;
+static int i2c_slave_client_probe(struct i2c_client *adapter);
+static int i2c_slave_client_remove(struct i2c_client *client);
+static struct i2c_driver i2c_slave_client_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "i2c-slave-client",
+ },
+ .probe = i2c_slave_client_probe,
+ .remove = i2c_slave_client_remove,
+};
+
+/*!
+ * ov2640 I2C attach function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int i2c_slave_client_probe(struct i2c_client *client)
+{
+ i2c_slave_client = client;
+ return 0;
+}
+
+/*!
+ * ov2640 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int i2c_slave_client_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+/*!
+ * ov2640 init function
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int i2c_slave_client_init(void)
+{
+ return i2c_add_driver(&i2c_slave_client_driver);
+}
+
+/*!
+ * OV2640 cleanup function
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit i2c_slave_client_clean(void)
+{
+ i2c_del_driver(&i2c_slave_client_driver);
+}
+
+module_init(i2c_slave_client_init);
+module_exit(i2c_slave_client_clean);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("I2c Slave Client Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c-slave/i2c_slave_core.c b/drivers/i2c-slave/i2c_slave_core.c
new file mode 100644
index 000000000000..0592d53a6fad
--- /dev/null
+++ b/drivers/i2c-slave/i2c_slave_core.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+#include "i2c_slave_device.h"
+
+int i2c_slave_major;
+
+static ssize_t i2c_slave_read(struct file *fd, char __user *buf, size_t len,
+ loff_t *ptr)
+{
+ i2c_slave_device_t *dev;
+ void *kbuf;
+ int ret;
+
+ if (len == 0)
+ return 0;
+
+ kbuf = kmalloc(len, GFP_KERNEL);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto error0;
+ }
+
+ dev = (i2c_slave_device_t *) fd->private_data;
+ if (!dev) {
+ ret = -ENODEV;
+ goto error1;
+ }
+
+ ret = i2c_slave_device_read(dev, len, kbuf);
+ if (ret <= 0 || copy_to_user(buf, kbuf, len)) {
+ ret = -EFAULT;
+ }
+
+ error1:
+ kfree(kbuf);
+ error0:
+ return ret;
+}
+
+static ssize_t i2c_slave_write(struct file *fd, const char __user *buf,
+ size_t len, loff_t *ptr)
+{
+ i2c_slave_device_t *dev;
+ void *kbuf;
+ int ret;
+
+ if (len == 0)
+ return 0;
+
+ kbuf = kmalloc(len, GFP_KERNEL);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto error0;
+ }
+ if (copy_from_user(kbuf, buf, len)) {
+ ret = -EFAULT;
+ goto error1;
+ }
+
+ dev = (i2c_slave_device_t *) fd->private_data;
+ if (!dev) {
+ ret = -ENODEV;
+ goto error1;
+ }
+
+ ret = i2c_slave_device_write(dev, len, (u8 *) kbuf);
+
+ error1:
+ kfree(kbuf);
+ error0:
+ return ret;
+}
+
+static int i2c_slave_ioctl(struct inode *inode, struct file *fd,
+ unsigned code, unsigned long value)
+{
+ /*todo */
+ return 0;
+}
+
+static int i2c_slave_open(struct inode *inode, struct file *fd)
+{
+ int ret;
+ unsigned int id;
+ i2c_slave_device_t *dev;
+ id = iminor(inode);
+
+ if (id >= I2C_SLAVE_DEVICE_MAX) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ dev = i2c_slave_device_find(id);
+ if (!dev) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ i2c_slave_rb_clear(dev->receive_buffer);
+ i2c_slave_rb_clear(dev->send_buffer);
+
+ if (i2c_slave_device_start(dev)) {
+ ret = -EBUSY;
+ goto error;
+ }
+
+ fd->private_data = (void *)dev;
+ ret = 0;
+
+ error:
+ return ret;
+}
+
+static int i2c_slave_release(struct inode *inode, struct file *fd)
+{
+ int ret;
+ unsigned int id;
+ i2c_slave_device_t *dev;
+ id = iminor(inode);
+
+ if (id >= I2C_SLAVE_DEVICE_MAX) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ dev = i2c_slave_device_find(id);
+ if (!dev) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ if (i2c_slave_device_stop(dev)) {
+ ret = -EBUSY;
+ goto error;
+ }
+
+ ret = 0;
+
+ error:
+ return ret;
+}
+
+static const struct file_operations i2c_slave_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = i2c_slave_read,
+ .write = i2c_slave_write,
+ .ioctl = i2c_slave_ioctl,
+ .open = i2c_slave_open,
+ .release = i2c_slave_release,
+};
+
+static int i2c_slave_bus_match(struct device *dev, struct device_driver *drv)
+{
+ return 0;
+}
+
+/*
+static int i2c_slave_bus_probe(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+
+ if (driver) {
+ return driver->probe(dev);
+ } else {
+ dev_err(dev, "%s: no driver\n", __func__);
+ return -ENODEV;
+ }
+}
+*/
+
+static int i2c_slave_bus_remove(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+
+ if (driver) {
+ if (!driver->remove) {
+ return 0;
+ } else {
+ return driver->remove(dev);
+ }
+ } else {
+
+ dev_err(dev, "%s: no driver\n", __func__);
+ return -ENODEV;
+ }
+}
+
+static void i2c_slave_bus_shutdown(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+
+ if (driver) {
+
+ driver->shutdown(dev);
+ } else {
+
+ dev_err(dev, "%s: no driver\n", __func__);
+ return;
+ }
+}
+static int i2c_slave_bus_suspend(struct device *dev, pm_message_t state)
+{
+ struct device_driver *driver = dev->driver;
+
+ if (driver) {
+
+ if (!driver->suspend) {
+ return 0;
+ } else {
+ return driver->suspend(dev, state);
+ }
+ } else {
+
+ dev_err(dev, "%s: no driver\n", __func__);
+ return -ENODEV;
+ }
+}
+
+static int i2c_slave_bus_resume(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+
+ if (driver) {
+
+ if (!driver->resume) {
+ return 0;
+ } else {
+ return driver->resume(dev);
+ }
+ } else {
+
+ dev_err(dev, "%s: no driver\n", __func__);
+ return -ENODEV;
+ }
+}
+
+struct bus_type i2c_slave_bus_type = {
+ .name = "i2c-slave",
+ .match = i2c_slave_bus_match,
+ .remove = i2c_slave_bus_remove,
+ .shutdown = i2c_slave_bus_shutdown,
+ .suspend = i2c_slave_bus_suspend,
+ .resume = i2c_slave_bus_resume,
+};
+
+EXPORT_SYMBOL_GPL(i2c_slave_bus_type);
+
+static int i2c_slave_driver_probe(struct device *dev)
+{
+ return 0;
+}
+
+static int i2c_slave_driver_remove(struct device *dev)
+{
+ return 0;
+}
+
+static int i2c_slave_driver_shutdown(struct device *dev)
+{
+ return 0;
+}
+
+static int i2c_slave_driver_suspend(struct device *dev, pm_message_t state)
+{
+ return 0;
+}
+
+static int i2c_slave_driver_resume(struct device *dev)
+{
+ return 0;
+}
+
+extern struct class *i2c_slave_class;
+
+static struct device_driver i2c_slave_driver = {
+ .name = "i2c-slave",
+ .owner = THIS_MODULE,
+ .bus = &i2c_slave_bus_type,
+ .probe = i2c_slave_driver_probe,
+ .remove = i2c_slave_driver_remove,
+ .shutdown = i2c_slave_driver_shutdown,
+ .suspend = i2c_slave_driver_suspend,
+ .resume = i2c_slave_driver_resume,
+};
+
+static int __init i2c_slave_dev_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "i2c slave /dev entries driver\n");
+
+ ret = bus_register(&i2c_slave_bus_type);
+ if (ret) {
+ printk(KERN_ERR "%s: bus_register error\n", __func__);
+ goto out;
+ }
+
+ i2c_slave_class = class_create(THIS_MODULE, "i2c-slave");
+ if (IS_ERR(i2c_slave_class)) {
+ pr_err("%s: class_create error\n", __func__);
+ goto out_unreg_bus;
+ }
+
+ i2c_slave_major = register_chrdev(0, "i2c-slave", &i2c_slave_fops);
+ if (i2c_slave_major <= 0) {
+ pr_err("%s: register_chrdev error\n", __func__);
+ goto out_unreg_class;
+ }
+
+ ret = driver_register(&i2c_slave_driver);
+ if (ret) {
+ pr_err("%s: driver_register error\n", __func__);
+ goto out_unreg_chrdev;
+ }
+
+ return 0;
+
+ out_unreg_chrdev:
+ unregister_chrdev(i2c_slave_major, "i2c-slave");
+ out_unreg_class:
+ class_destroy(i2c_slave_class);
+ out_unreg_bus:
+ bus_unregister(&i2c_slave_bus_type);
+ out:
+ pr_err("%s: init error\n", __func__);
+ return ret;
+}
+
+static void __exit i2c_dev_exit(void)
+{
+ driver_unregister(&i2c_slave_driver);
+ class_destroy(i2c_slave_class);
+ unregister_chrdev(i2c_slave_major, "i2c-slave");
+ bus_unregister(&i2c_slave_bus_type);
+}
+
+module_init(i2c_slave_dev_init);
+module_exit(i2c_dev_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("I2C Slave Driver Core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c-slave/i2c_slave_device.c b/drivers/i2c-slave/i2c_slave_device.c
new file mode 100644
index 000000000000..bfe39809928e
--- /dev/null
+++ b/drivers/i2c-slave/i2c_slave_device.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/kdev_t.h>
+#include "i2c_slave_device.h"
+static i2c_slave_device_t *i2c_slave_devices[I2C_SLAVE_DEVICE_MAX];
+struct class *i2c_slave_class;
+static int i2c_slave_device_get_id(void)
+{
+ int i;
+ for (i = 0; i < I2C_SLAVE_DEVICE_MAX; i++) {
+ if (!i2c_slave_devices[i])
+ return i;
+ }
+ return -1;
+}
+
+i2c_slave_device_t *i2c_slave_device_find(int id)
+{
+ if (id >= 0 && id < I2C_SLAVE_DEVICE_MAX)
+ return i2c_slave_devices[id];
+
+ else
+ return NULL;
+}
+void i2c_slave_device_set_name(i2c_slave_device_t *device, char *name)
+{
+ device->name = name;
+}
+
+void i2c_slave_device_set_address(i2c_slave_device_t *device, u8 address)
+{
+ device->address = address;
+}
+
+u8 i2c_slave_device_get_addr(i2c_slave_device_t *device)
+{
+ return device->address;
+}
+
+int i2c_slave_device_set_freq(i2c_slave_device_t *device, u32 freq)
+{
+ /*TODO: freq check */
+ device->scl_freq = freq;
+ return 0;
+}
+
+u32 i2c_slave_device_get_freq(i2c_slave_device_t *device)
+{
+ return device->scl_freq;
+}
+
+/*used by the specific i2c device to register itself to the core.*/
+i2c_slave_device_t *i2c_slave_device_alloc(void)
+{
+ int id;
+ i2c_slave_device_t *device;
+ id = i2c_slave_device_get_id();
+ if (id < 0) {
+ goto error;
+ }
+ device =
+ (i2c_slave_device_t *) kzalloc(sizeof(i2c_slave_device_t),
+ GFP_KERNEL);
+ if (!device) {
+ printk(KERN_ERR "%s: alloc device error\n", __func__);
+ goto error_device;
+ }
+ device->receive_buffer = i2c_slave_rb_alloc(PAGE_SIZE);
+ if (!device->receive_buffer) {
+ printk(KERN_ERR "%s: alloc receive buffer error\n", __func__);
+ goto error_receive_buffer;
+ }
+ device->send_buffer = i2c_slave_rb_alloc(PAGE_SIZE);
+ if (!device->send_buffer) {
+ printk(KERN_ERR "%s: alloc send buffer error\n", __func__);
+ goto error_send_buffer;
+ }
+ device->id = id;
+ return device;
+
+ error_send_buffer:
+ kfree(device->receive_buffer);
+ error_receive_buffer:
+ kfree((void *)device);
+ error_device:
+ pr_debug(KERN_ERR "%s: no memory\n", __func__);
+ error:
+ return 0;
+}
+
+void i2c_slave_device_free(i2c_slave_device_t *dev)
+{
+ i2c_slave_rb_release(dev->receive_buffer);
+ i2c_slave_rb_release(dev->send_buffer);
+ kfree(dev);
+}
+
+int i2c_slave_device_register(i2c_slave_device_t *device)
+{
+ device->dev = device_create(i2c_slave_class, NULL,
+ MKDEV(i2c_slave_major, device->id),
+ NULL, "slave-i2c-%d", device->id);
+ if (!device->dev) {
+ return -1;
+ }
+ i2c_slave_devices[device->id] = device;
+ return 0;
+}
+
+void i2c_slave_device_unregister(i2c_slave_device_t *device)
+{
+ device_destroy(i2c_slave_class, MKDEV(i2c_slave_major, device->id));
+ i2c_slave_devices[device->id] = 0;
+ i2c_slave_device_free(device);
+}
+
+/*
+ this two functions are used by i2c slave core to start or stop the specific i2c device.
+*/
+int i2c_slave_device_start(i2c_slave_device_t *device)
+{
+ return device->start(device);
+}
+
+int i2c_slave_device_stop(i2c_slave_device_t *device)
+{
+ return device->stop(device);
+}
+
+/*
+ this two functions are used by i2c slave core to get data by the specific i2c slave device
+ or send data to it to feed i2c master's need.
+
+ @mod: async(1) or sync(0) mode.
+*/
+int i2c_slave_device_read(i2c_slave_device_t *device, int num, u8 *data)
+{
+ int read_num, read_total = 0;
+ int step = 1000;
+ u8 *read_buf = data;
+ printk(KERN_INFO "%s: device id=%d, num=%d\n", __func__, device->id,
+ num);
+ read_num = i2c_slave_rb_consume(device->receive_buffer, num, read_buf);
+ read_total += read_num;
+ read_buf += read_num;
+ step--;
+ while ((read_total < num) && step) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ if (!signal_pending(current)) {
+ } else {
+ /*TODO*/ break;
+ }
+ read_num =
+ i2c_slave_rb_consume(device->receive_buffer,
+ num - read_total, read_buf);
+ num -= read_num;
+ read_buf += read_num;
+ step--;
+ }
+ return read_total;
+}
+int i2c_slave_device_write(i2c_slave_device_t *device, int num, u8 *data)
+{
+ int write_num, write_total = 0;
+ int step = 1000;
+ u8 *buf_index = data;
+ write_num = i2c_slave_rb_produce(device->send_buffer, num, buf_index);
+ write_total += write_num;
+ buf_index += write_num;
+ step--;
+ while (write_total < num && step) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ if (!signal_pending(current)) {
+ } else {
+ /*TODO*/ step = 0;
+ break;
+ }
+ write_num =
+ i2c_slave_rb_produce(device->send_buffer, num - write_total,
+ buf_index);
+ write_total += write_num;
+ buf_index += write_num;
+ step--;
+ }
+ while (step && i2c_slave_rb_num(device->send_buffer)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 10);
+ if (!signal_pending(current)) {
+ step--;
+ } else {
+ /*TODO*/ step = 0;
+ break;
+ }
+ }
+ if (!step) {
+ write_total -= i2c_slave_rb_num(device->send_buffer);
+ i2c_slave_rb_clear(device->send_buffer);
+ }
+ return write_total;
+}
+
+/*
+ * this 2 functions used by the specific i2c slave device when they got data from master(produce),
+ * or is request by master(consume).
+ */
+int i2c_slave_device_produce(i2c_slave_device_t *device, int num, u8 *data)
+{
+ int ret;
+ ret = i2c_slave_rb_produce(device->receive_buffer, num, data);
+ return ret;
+}
+int i2c_slave_device_consume(i2c_slave_device_t *device, int num, u8 *data)
+{
+ return i2c_slave_rb_consume(device->send_buffer, num, data);
+}
+
+EXPORT_SYMBOL(i2c_slave_device_set_name);
+EXPORT_SYMBOL(i2c_slave_device_set_address);
+EXPORT_SYMBOL(i2c_slave_device_get_addr);
+EXPORT_SYMBOL(i2c_slave_device_find);
+EXPORT_SYMBOL(i2c_slave_device_set_freq);
+EXPORT_SYMBOL(i2c_slave_device_get_freq);
+
+/*
+* used by the specific i2c device to register itself to the core.
+*/
+EXPORT_SYMBOL(i2c_slave_device_alloc);
+EXPORT_SYMBOL(i2c_slave_device_free);
+EXPORT_SYMBOL(i2c_slave_device_register);
+EXPORT_SYMBOL(i2c_slave_device_unregister);
+
+/*
+ this two functions are used by i2c slave core to start or stop the specific i2c device.
+*/
+EXPORT_SYMBOL(i2c_slave_device_start);
+EXPORT_SYMBOL(i2c_slave_device_stop);
+
+/*
+ this two functions are used by i2c slave core to get data by the specific i2c slave device
+ or send data to it for it to feed i2c master's need.
+
+ @mod: async(1) or sync(0) mode.
+*/
+EXPORT_SYMBOL(i2c_slave_device_read);
+EXPORT_SYMBOL(i2c_slave_device_write);
+
+/*
+* this 2 functions used by the specific i2c slave device when they got data from master,
+* or is request by master.
+*/
+EXPORT_SYMBOL(i2c_slave_device_produce);
+EXPORT_SYMBOL(i2c_slave_device_consume);
diff --git a/drivers/i2c-slave/i2c_slave_device.h b/drivers/i2c-slave/i2c_slave_device.h
new file mode 100644
index 000000000000..e08ea569e030
--- /dev/null
+++ b/drivers/i2c-slave/i2c_slave_device.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __I2C_SLAVE_H__
+#define __I2C_SLAVE_H__
+
+#include <linux/list.h>
+#include "i2c_slave_ring_buffer.h"
+
+#define I2C_SLAVE_DEVICE_MAX 256
+extern int i2c_slave_major;
+
+typedef struct i2c_slave_device {
+ struct list_head list;
+ u8 address;
+ u32 scl_freq;
+ char *name;
+ i2c_slave_ring_buffer_t *receive_buffer;
+ i2c_slave_ring_buffer_t *send_buffer;
+ int (*start) (struct i2c_slave_device *);
+ int (*stop) (struct i2c_slave_device *);
+ /*int (*set_freq)(struct i2c_slave_device*);
+ int (*set_addr)(struct i2c_slave_device*);*/
+ void *private_data;
+ struct device *dev;
+ int id;
+} i2c_slave_device_t;
+
+/*
+ used by the specific device to set some infomations.
+*/
+void i2c_slave_device_set_name(i2c_slave_device_t *device, char *name);
+void i2c_slave_device_set_address(i2c_slave_device_t *device, u8 address);
+i2c_slave_device_t *i2c_slave_device_find(int id);
+u8 i2c_slave_device_get_addr(i2c_slave_device_t *device);
+int i2c_slave_device_set_freq(i2c_slave_device_t *device, u32 freq);
+u32 i2c_slave_device_get_freq(i2c_slave_device_t *device);
+
+/*
+**used by the specific i2c device to register itself to the core.
+*/
+i2c_slave_device_t *i2c_slave_device_alloc(void);
+void i2c_slave_device_free(i2c_slave_device_t *);
+int i2c_slave_device_register(i2c_slave_device_t *device);
+void i2c_slave_device_unregister(i2c_slave_device_t *device);
+
+/*
+ this two functions are used by i2c slave core to start or stop the specific i2c device.
+*/
+int i2c_slave_device_start(i2c_slave_device_t *device);
+int i2c_slave_device_stop(i2c_slave_device_t *device);
+
+/*
+ this two functions are used by i2c slave core to get data by the specific i2c slave device
+ or send data to it for it to feed i2c master's need.
+
+ @mod: async(1) or sync(0) mode.
+*/
+int i2c_slave_device_read(i2c_slave_device_t *device, int num, u8 *data);
+int i2c_slave_device_write(i2c_slave_device_t *device, int num, u8 *data);
+
+/*
+*this 2 functions used by the specific i2c slave device when they got data from master,
+*or is requested by master.
+*/
+int i2c_slave_device_produce(i2c_slave_device_t *device, int num, u8 *data);
+int i2c_slave_device_consume(i2c_slave_device_t *device, int num, u8 *data);
+
+#endif
diff --git a/drivers/i2c-slave/i2c_slave_ring_buffer.c b/drivers/i2c-slave/i2c_slave_ring_buffer.c
new file mode 100644
index 000000000000..4a55e099235f
--- /dev/null
+++ b/drivers/i2c-slave/i2c_slave_ring_buffer.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/slab.h>
+#include <linux/hardirq.h>
+#include "i2c_slave_ring_buffer.h"
+
+i2c_slave_ring_buffer_t *i2c_slave_rb_alloc(int size)
+{
+ i2c_slave_ring_buffer_t *ring_buf;
+
+ ring_buf =
+ (i2c_slave_ring_buffer_t *) kzalloc(sizeof(i2c_slave_ring_buffer_t),
+ GFP_KERNEL);
+ if (!ring_buf) {
+ pr_debug("%s: alloc ring_buf error\n", __func__);
+ goto error;
+ }
+
+ ring_buf->buffer = kmalloc(size, GFP_KERNEL);
+ if (!ring_buf->buffer) {
+ pr_debug("%s: alloc buffer error\n", __func__);
+ goto error1;
+ }
+
+ ring_buf->total = size;
+
+ ring_buf->lock = __SPIN_LOCK_UNLOCKED(ring_buf->lock);
+ return ring_buf;
+
+ error1:
+ kfree(ring_buf);
+ error:
+ return NULL;
+}
+
+void i2c_slave_rb_release(i2c_slave_ring_buffer_t *ring)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(ring->lock, flags);
+ kfree(ring->buffer);
+ spin_unlock_irqrestore(ring->lock, flags);
+ kfree(ring);
+}
+
+int i2c_slave_rb_produce(i2c_slave_ring_buffer_t *ring, int num, char *data)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(ring->lock, flags);
+
+ if (ring->start < ring->end) {
+ if ((ring->start + num) < ring->end) { /*have enough space */
+ memcpy(&ring->buffer[ring->start], data, num);
+ ring->start += num;
+ ret = num;
+ } else { /*space not enough, just copy part of it. */
+ ret = ring->end - ring->start;
+ memcpy(&ring->buffer[ring->start], data, ret);
+ ring->start += ret;
+ ring->full = 1;
+ }
+ } else if (ring->start >= ring->end && !ring->full) {
+ if (ring->start + num <= ring->total) { /*space enough */
+ ret = num;
+ memcpy(&ring->buffer[ring->start], data, ret);
+ ring->start += ret;
+ } else { /*turn ring->start around */
+ ret = ring->total - ring->start;
+ memcpy(&ring->buffer[ring->start], data, ret);
+ ring->start = 0;
+ num -= ret;
+ data += ret;
+ if (num < ring->end) { /*space enough */
+ ret += num;
+ memcpy(&ring->buffer[ring->start], data, num);
+ } else { /*space not enough, just copy part of it. */
+ ret += ring->end;
+ memcpy(&ring->buffer[ring->start], data,
+ ring->end);
+ ring->start = ring->end;
+ ring->full = 1;
+ }
+ }
+ } else { /*(ring->data == ring->end) && ring->full ) : full */
+ ret = 0;
+ }
+
+ spin_unlock_irqrestore(ring->lock, flags);
+ return ret;
+}
+
+int i2c_slave_rb_consume(i2c_slave_ring_buffer_t *ring, int num, char *data)
+{
+ int ret;
+ unsigned long flags;
+ spin_lock_irqsave(ring->lock, flags);
+ if (num <= 0) {
+ ret = 0;
+ goto out;
+ }
+
+ if (ring->start > ring->end) {
+ if (num <= ring->start - ring->end) { /*enough */
+ ret = num;
+ memcpy(data, &ring->buffer[ring->end], ret);
+ ring->end += ret;
+ } else { /*not enough */
+ ret = ring->start - ring->end;
+ memcpy(data, &ring->buffer[ring->end], ret);
+ ring->end += ret;
+ }
+ } else if (ring->start < ring->end || ring->full) {
+ if (num <= ring->total - ring->end) {
+ ret = ring->total - ring->end;
+ memcpy(data, &ring->buffer[ring->end], ret);
+ ring->end += ret;
+ } else if (num <= ring->total - ring->end + ring->start) {
+ ret = ring->total - ring->end;
+ memcpy(data, &ring->buffer[ring->end], ret);
+ ring->end = 0;
+ data += ret;
+ num -= ret;
+ memcpy(data, &ring->buffer[ring->end], num);
+ ring->end = num;
+ ret += num;
+ } else {
+ ret = ring->total - ring->end;
+ memcpy(data, &ring->buffer[ring->end], ret);
+ ring->end = 0;
+ data += ret;
+ num -= ret;
+ memcpy(data, &ring->buffer[ring->end], ring->start);
+ ring->end = ring->start;
+ ret += ring->start;
+ }
+ ring->full = 0;
+ } else { /*empty */
+ ret = 0;
+ }
+
+ out:
+ spin_unlock_irqrestore(ring->lock, flags);
+
+ return ret;
+}
+
+int i2c_slave_rb_num(i2c_slave_ring_buffer_t *ring)
+{
+ int ret;
+ unsigned long flags;
+ spin_lock_irqsave(ring->lock, flags);
+ if (ring->start > ring->end) {
+ ret = ring->start - ring->end;
+ } else if (ring->start < ring->end) {
+ ret = ring->total - ring->end + ring->start;
+ } else if (ring->full) {
+ ret = ring->total;
+ } else {
+ ret = 0;
+ }
+ spin_unlock_irqrestore(ring->lock, flags);
+ return ret;
+}
+
+void i2c_slave_rb_clear(i2c_slave_ring_buffer_t *ring)
+{
+ unsigned long flags;
+ spin_lock_irqsave(ring->lock, flags);
+
+ ring->start = ring->end = 0;
+ ring->full = 0;
+ spin_unlock_irqrestore(ring->lock, flags);
+}
diff --git a/drivers/i2c-slave/i2c_slave_ring_buffer.h b/drivers/i2c-slave/i2c_slave_ring_buffer.h
new file mode 100644
index 000000000000..1068e5f1b527
--- /dev/null
+++ b/drivers/i2c-slave/i2c_slave_ring_buffer.h
@@ -0,0 +1,39 @@
+#ifndef __BUFFER_MANAGER_H__
+#define __BUFFER_MANAGER_H__
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+typedef struct i2c_slave_ring_buffer {
+ int start;
+ int end;
+ int total;
+ bool full;
+ char *buffer;
+ spinlock_t lock;
+} i2c_slave_ring_buffer_t;
+
+i2c_slave_ring_buffer_t *i2c_slave_rb_alloc(int size);
+
+void i2c_slave_rb_release(i2c_slave_ring_buffer_t *ring);
+
+int i2c_slave_rb_produce(i2c_slave_ring_buffer_t *ring, int num, char *data);
+
+int i2c_slave_rb_consume(i2c_slave_ring_buffer_t *ring, int num, char *data);
+
+int i2c_slave_rb_num(i2c_slave_ring_buffer_t *ring);
+
+void i2c_slave_rb_clear(i2c_slave_ring_buffer_t *ring);
+
+#endif
diff --git a/drivers/i2c-slave/mxc_i2c_slave.c b/drivers/i2c-slave/mxc_i2c_slave.c
new file mode 100644
index 000000000000..39e067303f6b
--- /dev/null
+++ b/drivers/i2c-slave/mxc_i2c_slave.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <asm/io.h>
+#include <linux/pm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include "i2c_slave_device.h"
+#include "mxc_i2c_slave.h"
+#include "mxc_i2c_slave_reg.h"
+
+struct mxc_i2c_slave_clk {
+ int reg_value;
+ int div;
+};
+
+static const struct mxc_i2c_slave_clk i2c_clk_table[] = {
+ {0x20, 22}, {0x21, 24}, {0x22, 26}, {0x23, 28},
+ {0, 30}, {1, 32}, {0x24, 32}, {2, 36},
+ {0x25, 36}, {0x26, 40}, {3, 42}, {0x27, 44},
+ {4, 48}, {0x28, 48}, {5, 52}, {0x29, 56},
+ {6, 60}, {0x2A, 64}, {7, 72}, {0x2B, 72},
+ {8, 80}, {0x2C, 80}, {9, 88}, {0x2D, 96},
+ {0xA, 104}, {0x2E, 112}, {0xB, 128}, {0x2F, 128},
+ {0xC, 144}, {0xD, 160}, {0x30, 160}, {0xE, 192},
+ {0x31, 192}, {0x32, 224}, {0xF, 240}, {0x33, 256},
+ {0x10, 288}, {0x11, 320}, {0x34, 320}, {0x12, 384},
+ {0x35, 384}, {0x36, 448}, {0x13, 480}, {0x37, 512},
+ {0x14, 576}, {0x15, 640}, {0x38, 640}, {0x16, 768},
+ {0x39, 768}, {0x3A, 896}, {0x17, 960}, {0x3B, 1024},
+ {0x18, 1152}, {0x19, 1280}, {0x3C, 1280}, {0x1A, 1536},
+ {0x3D, 1536}, {0x3E, 1792}, {0x1B, 1920}, {0x3F, 2048},
+ {0x1C, 2304}, {0x1D, 2560}, {0x1E, 3072}, {0x1F, 3840},
+ {0, 0}
+};
+
+extern void gpio_i2c_active(int i2c_num);
+extern void gpio_i2c_inactive(int i2c_num);
+
+static irqreturn_t interrupt_handler(int irq, void *dev_id)
+{
+ u16 status, ctl;
+ int num;
+ u16 data;
+ struct mxc_i2c_slave_device *mxc_i2c =
+ (struct mxc_i2c_slave_device *)dev_id;
+
+ status = readw(mxc_i2c->reg_base + MXC_I2SR);
+ ctl = readw(mxc_i2c->reg_base + MXC_I2CR);
+
+ dev_dbg(mxc_i2c->dev->dev, "status=%x, ctl=%x\n", status, ctl);
+
+ if (status & MXC_I2SR_IAAS) {
+ if (status & MXC_I2SR_SRW) {
+ /*slave send */
+ num =
+ i2c_slave_device_consume(mxc_i2c->dev, 1,
+ (u8 *) &data);
+ if (num < 1) {
+ /*FIXME:not ready to send data */
+ printk(KERN_ERR
+ " i2c-slave:%s:data not ready\n",
+ __func__);
+ } else {
+ ctl |= MXC_I2CR_MTX;
+ writew(ctl, mxc_i2c->reg_base + MXC_I2CR);
+ /*send data */
+ writew(data, mxc_i2c->reg_base + MXC_I2DR);
+ }
+
+ } else {
+ /*slave receive */
+ ctl &= ~MXC_I2CR_MTX;
+ writew(ctl, mxc_i2c->reg_base + MXC_I2CR);
+
+ /*dummy read */
+ data = readw(mxc_i2c->reg_base + MXC_I2DR);
+ }
+ } else {
+ /*slave send */
+ if (ctl & MXC_I2CR_MTX) {
+ if (!(status & MXC_I2SR_RXAK)) { /*ACK received */
+ num =
+ i2c_slave_device_consume(mxc_i2c->dev, 1,
+ (u8 *) &data);
+ if (num < 1) {
+ /*FIXME:not ready to send data */
+ printk(KERN_ERR
+ " i2c-slave:%s:data not ready\n",
+ __func__);
+ } else {
+ ctl |= MXC_I2CR_MTX;
+ writew(ctl,
+ mxc_i2c->reg_base + MXC_I2CR);
+ writew(data,
+ mxc_i2c->reg_base + MXC_I2DR);
+ }
+ } else {
+ /*no ACK. */
+ /*dummy read */
+ ctl &= ~MXC_I2CR_MTX;
+ writew(ctl, mxc_i2c->reg_base + MXC_I2CR);
+ data = readw(mxc_i2c->reg_base + MXC_I2DR);
+ }
+ } else { /*read */
+ ctl &= ~MXC_I2CR_MTX;
+ writew(ctl, mxc_i2c->reg_base + MXC_I2CR);
+
+ /*read */
+ data = readw(mxc_i2c->reg_base + MXC_I2DR);
+ i2c_slave_device_produce(mxc_i2c->dev, 1,
+ (u8 *) &data);
+ }
+
+ }
+
+ writew(0x0, mxc_i2c->reg_base + MXC_I2SR);
+
+ return IRQ_HANDLED;
+}
+
+static int start(i2c_slave_device_t *device)
+{
+ volatile unsigned int cr;
+ unsigned int addr;
+ struct mxc_i2c_slave_device *mxc_dev;
+
+ mxc_dev = (struct mxc_i2c_slave_device *)device->private_data;
+ if (!mxc_dev) {
+ dev_err(device->dev, "%s: get mxc_dev error\n", __func__);
+ return -ENODEV;
+ }
+
+ clk_enable(mxc_dev->clk);
+ /* Set the frequency divider */
+ writew(mxc_dev->clkdiv, mxc_dev->reg_base + MXC_IFDR);
+
+ /* Set the Slave bit */
+ cr = readw(mxc_dev->reg_base + MXC_I2CR);
+ cr &= (!MXC_I2CR_MSTA);
+ writew(cr, mxc_dev->reg_base + MXC_I2CR);
+
+ /*Set Slave Address */
+ addr = mxc_dev->dev->address << 1;
+ writew(addr, mxc_dev->reg_base + MXC_IADR);
+
+ /* Clear the status register */
+ writew(0x0, mxc_dev->reg_base + MXC_I2SR);
+
+ /* Enable I2C and its interrupts */
+ writew(MXC_I2CR_IEN, mxc_dev->reg_base + MXC_I2CR);
+ writew(MXC_I2CR_IEN | MXC_I2CR_IIEN, mxc_dev->reg_base + MXC_I2CR);
+
+ return 0;
+
+}
+
+static int stop(i2c_slave_device_t *device)
+{
+ struct mxc_i2c_slave_device *mxc_dev;
+
+ mxc_dev = (struct mxc_i2c_slave_device *)device->private_data;
+
+ writew(0x0, mxc_dev->reg_base + MXC_I2CR);
+ clk_disable(mxc_dev->clk);
+
+ return 0;
+}
+
+static int mxc_i2c_slave_probe(struct platform_device *pdev)
+{
+ int i;
+ u32 clk_freq;
+ struct resource *res;
+ struct mxc_i2c_slave_device *mxc_dev;
+
+ mxc_dev = kzalloc(sizeof(struct mxc_i2c_slave_device), GFP_KERNEL);
+ if (!mxc_dev) {
+ goto error0;
+ }
+ mxc_dev->dev = i2c_slave_device_alloc();
+ if (mxc_dev->dev == 0) {
+ dev_err(&pdev->dev, "%s: i2c_slave_device_alloc error\n",
+ __func__);
+ goto error1;
+ }
+
+ i2c_slave_device_set_address(mxc_dev->dev, MXC_I2C_SLAVE_ADDRESS);
+ i2c_slave_device_set_freq(mxc_dev->dev, MXC_I2C_SLAVE_FREQ);
+ i2c_slave_device_set_name(mxc_dev->dev, MXC_I2C_SLAVE_NAME);
+ mxc_dev->dev->start = start;
+ mxc_dev->dev->stop = stop;
+
+ mxc_dev->dev->private_data = mxc_dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "%s: get resource error\n", __func__);
+ goto error2;
+ }
+ mxc_dev->reg_base = IO_ADDRESS(res->start);
+
+ mxc_dev->irq = platform_get_irq(pdev, 0);
+ if (mxc_dev->irq < 0) {
+ dev_err(&pdev->dev, "%s: get irq error\n", __func__);
+ goto error2;
+ }
+ if (request_irq(mxc_dev->irq, interrupt_handler,
+ 0, mxc_dev->dev->name, mxc_dev) < 0) {
+ dev_err(&pdev->dev, "%s: request_irq error\n", __func__);
+ goto error2;
+ }
+
+ /*i2c id on soc */
+ mxc_dev->id = pdev->id;
+
+ gpio_i2c_active(mxc_dev->id);
+
+ /*clock */
+ mxc_dev->clk = clk_get(&pdev->dev, "i2c_clk");
+ clk_freq = clk_get_rate(mxc_dev->clk);
+ mxc_dev->clkdiv = -1;
+ if (mxc_dev->dev->scl_freq) {
+ /* Calculate divider and round up any fractional part */
+ int div = (clk_freq + mxc_dev->dev->scl_freq - 1) /
+ mxc_dev->dev->scl_freq;
+ for (i = 0; i2c_clk_table[i].div != 0; i++) {
+ if (i2c_clk_table[i].div >= div) {
+ mxc_dev->clkdiv = i2c_clk_table[i].reg_value;
+ break;
+ }
+ }
+ }
+ if (mxc_dev->clkdiv == -1) {
+ i--;
+ mxc_dev->clkdiv = 0x1F; /* Use max divider */
+ }
+ dev_dbg(&pdev->dev,
+ "i2c slave speed is %d/%d = %d bps, reg val = 0x%02X\n",
+ clk_freq, i2c_clk_table[i].div, clk_freq / i2c_clk_table[i].div,
+ mxc_dev->clkdiv);
+
+ if (i2c_slave_device_register(mxc_dev->dev) < 0) {
+ dev_err(&pdev->dev, "%s: i2c_slave_device_register error\n",
+ __func__);
+ goto error3;
+ }
+
+ platform_set_drvdata(pdev, (void *)mxc_dev);
+
+ /*start(mxc_dev->dev); */
+ return 0;
+
+ error3:
+ error2:
+ i2c_slave_device_free(mxc_dev->dev);
+ error1:
+ kfree(mxc_dev);
+ error0:
+ return -ENODEV;
+}
+
+static int mxc_i2c_slave_remove(struct platform_device *pdev)
+{
+ struct mxc_i2c_slave_device *mxc_dev;
+ mxc_dev = (struct mxc_i2c_slave_device *)platform_get_drvdata(pdev);
+
+ i2c_slave_device_unregister(mxc_dev->dev);
+ kfree((void *)mxc_dev);
+
+ return 0;
+}
+
+static int mxc_i2c_slave_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int mxc_i2c_slave_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver mxci2c_slave_driver = {
+ .driver = {
+ .name = "mxc_i2c_slave",
+ .owner = THIS_MODULE,
+ },
+ .probe = mxc_i2c_slave_probe,
+ .remove = mxc_i2c_slave_remove,
+ .suspend = mxc_i2c_slave_suspend,
+ .resume = mxc_i2c_slave_resume,
+};
+
+static int __init mxc_i2c_slave_init(void)
+{
+ /* Register the device driver structure. */
+ return platform_driver_register(&mxci2c_slave_driver);
+}
+
+/*!
+ * This function is used to cleanup all resources before the driver exits.
+ */
+static void __exit mxc_i2c_slave_exit(void)
+{
+ platform_driver_unregister(&mxci2c_slave_driver);
+}
+
+module_init(mxc_i2c_slave_init);
+module_exit(mxc_i2c_slave_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC I2C Slave Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c-slave/mxc_i2c_slave.h b/drivers/i2c-slave/mxc_i2c_slave.h
new file mode 100644
index 000000000000..7b8d5143588e
--- /dev/null
+++ b/drivers/i2c-slave/mxc_i2c_slave.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MXC_I2C_SLAVE_H__
+#define __MXC_I2C_SLAVE_H__
+
+#include <linux/clk.h>
+#include "i2c_slave_device.h"
+
+#define MXC_I2C_SLAVE_NAME "MXC_I2C_SLAVE"
+#define MXC_I2C_SLAVE_ADDRESS 0x55
+#define MXC_I2C_SLAVE_FREQ 1000*100
+
+struct mxc_i2c_slave_device {
+ /*!
+ * The default clock divider value to be used.
+ */
+ unsigned int clkdiv;
+
+ /*!
+ * The clock source for the device.
+ */
+ struct clk *clk;
+
+ /*i2c id on soc */
+ int id;
+
+ int irq;
+ unsigned long reg_base;
+ bool state; /*0:stop, 1:start */
+ i2c_slave_device_t *dev;
+};
+
+#endif
diff --git a/drivers/i2c-slave/mxc_i2c_slave_reg.h b/drivers/i2c-slave/mxc_i2c_slave_reg.h
new file mode 100644
index 000000000000..6450ad6e3db9
--- /dev/null
+++ b/drivers/i2c-slave/mxc_i2c_slave_reg.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MXC_I2C_SLAVE_REG_H__
+#define __MXC_I2C_SLAVE_REG_H__
+
+/* Address offsets of the I2C registers */
+#define MXC_IADR 0x00 /* Address Register */
+#define MXC_IFDR 0x04 /* Freq div register */
+#define MXC_I2CR 0x08 /* Control regsiter */
+#define MXC_I2SR 0x0C /* Status register */
+#define MXC_I2DR 0x10 /* Data I/O register */
+
+/* Bit definitions of I2CR */
+#define MXC_I2CR_IEN 0x0080
+#define MXC_I2CR_IIEN 0x0040
+#define MXC_I2CR_MSTA 0x0020
+#define MXC_I2CR_MTX 0x0010
+#define MXC_I2CR_TXAK 0x0008
+#define MXC_I2CR_RSTA 0x0004
+
+/* Bit definitions of I2SR */
+#define MXC_I2SR_ICF 0x0080
+#define MXC_I2SR_IAAS 0x0040
+#define MXC_I2SR_IBB 0x0020
+#define MXC_I2SR_IAL 0x0010
+#define MXC_I2SR_SRW 0x0004
+#define MXC_I2SR_IIF 0x0002
+#define MXC_I2SR_RXAK 0x0001
+
+#endif /* __MXC_I2C_REG_H__ */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 7f95905bbb9d..0d9ad8e73026 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -160,6 +160,16 @@ config I2C_NFORCE2_S4985
This driver can also be built as a module. If so, the module
will be called i2c-nforce2-s4985.
+config I2C_NS9XXX
+ tristate "Digi ns9360, ns921x"
+ depends on PROCESSOR_NS9360 || PROCESSOR_NS921X
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface included in Digi ns9360 and ns921x CPUs.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-ns9xxx.
+
config I2C_SIS5595
tristate "SiS 5595"
depends on PCI
@@ -328,6 +338,10 @@ config I2C_DAVINCI
config I2C_GPIO
tristate "GPIO-based bitbanging I2C"
depends on GENERIC_GPIO
+
+config I2C_PARPORT
+ tristate "Parallel port adapter"
+ depends on PARPORT
select I2C_ALGOBIT
help
This is a very simple bitbanging I2C driver utilizing the
@@ -391,9 +405,34 @@ config I2C_MPC
This driver can also be built as a module. If so, the module
will be called i2c-mpc.
+config I2C_MXC
+ tristate "MXC I2C support"
+ depends on I2C && ARCH_MXC
+ help
+ If you say yes to this option, support will be included for Freescale
+ MXC I2C modules.
+
+ This driver can also be built as a module.
+
+config I2C_MXC_HS
+ tristate "MXC HIGH SPEED I2C support"
+ depends on I2C && ARCH_MXC
+ help
+ If you say yes to this option, support will be included for Freescale
+ MXC HIGH SPEED I2C modules.
+
+ This driver can also be built as a module.
+
+config I2C_STMP378X
+ tristate "STMP378x I2C adapter"
+ depends on MACH_STMP378X
+ help
+ TBD
+
config I2C_MV64XXX
tristate "Marvell mv64xxx I2C Controller"
depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL
+
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Marvell 64xxx line of host bridges.
@@ -461,6 +500,19 @@ config I2C_S3C2410
Say Y here to include support for I2C controller in the
Samsung S3C2410 based System-on-Chip devices.
+config I2C_S3C2410_ADAPTER_NR
+ int "S3C bus number for the I2C-adapter"
+ depends on I2C_S3C2410
+ default 1
+ help
+ This is the adapter/bus number for the I2C-adapter. This number can
+ be used by pre-been declared I2C-devices that will be probed
+ first when the I2C-bus driver is loaded.
+ For declaring this kind of static I2C-devices use the function
+ i2c_register_board_info (see drivers/i2c/i2c-core.c).
+
+
+
config I2C_SH7760
tristate "Renesas SH7760 I2C Controller"
depends on CPU_SUBTYPE_SH7760
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 0c2c4b26cdf1..2e0c0302c40c 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_I2C_I801) += i2c-i801.o
obj-$(CONFIG_I2C_ISCH) += i2c-isch.o
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o
+obj-$(CONFIG_I2C_NS9XXX) += i2c-ns9xxx.o
obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o
obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o
@@ -67,6 +68,9 @@ obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
+obj-$(CONFIG_I2C_MXC) += mxc_i2c.o
+obj-$(CONFIG_I2C_MXC_HS) += mxc_i2c_hs.o
+obj-$(CONFIG_I2C_STMP378X) += i2c-stmp378x.o
ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/busses/i2c-ns9xxx.c b/drivers/i2c/busses/i2c-ns9xxx.c
new file mode 100644
index 000000000000..0b6d1eb3239b
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ns9xxx.c
@@ -0,0 +1,558 @@
+/*
+ * linux/drivers/i2c/busses/i2c-ns9xxx.c
+ *
+ * based on old i2c-ns9xxx.c by Digi International Inc.
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/i2c-ns9xxx.h>
+#include <linux/platform_device.h>
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+
+/* registers */
+#define I2C_CMD 0x00
+#define I2C_STATUS 0x00
+#define I2C_MASTERADDR 0x04
+#define I2C_SLAVEADDR 0x08
+#define I2C_CONFIG 0x0c
+
+/* command bit fields */
+#define I2C_CMD_TXVAL (1 << 13)
+#define I2C_MASTERADDR_7BIT 0
+#define I2C_MASTERADDR_10BIT 1
+
+/* configuration masks */
+#define I2C_CONFIG_CLREFMASK 0x000001ff
+#define I2C_MASTERADDR_ADDRMASK 0x000007ff
+
+/* configuration shifts */
+#define I2C_MASTERADDR_ADDRSHIFT 1
+
+/* shifted i2c commands */
+#define I2C_CMD_NOP 0
+#define I2C_CMD_READ (4 << 8)
+#define I2C_CMD_WRITE (5 << 8)
+#define I2C_CMD_STOP (6 << 8)
+
+/* interrupt causes */
+#define I2C_IRQ_MASK (0xf << 8)
+#define I2C_IRQ_ARBITLOST (1 << 8)
+#define I2C_IRQ_NOACK (2 << 8)
+#define I2C_IRQ_TXDATA (3 << 8)
+#define I2C_IRQ_RXDATA (4 << 8)
+#define I2C_IRQ_CMDACK (5 << 8)
+
+#define I2C_NORMALSPEED 100000
+#define I2C_HIGHSPEED 400000
+
+#define DRIVER_NAME "i2c-ns9xxx"
+
+enum i2c_int_state {
+ I2C_INT_AWAITING,
+ I2C_INT_OK,
+ I2C_INT_RETRY,
+ I2C_INT_ERROR,
+ I2C_INT_ABORT
+};
+
+struct ns9xxx_i2c {
+ struct i2c_adapter adap;
+ struct resource *mem;
+ struct clk *clk;
+
+ void __iomem *ioaddr;
+
+ spinlock_t lock;
+ wait_queue_head_t wait_q;
+
+ struct plat_ns9xxx_i2c *pdata;
+
+ char *buf;
+ int irq;
+ enum i2c_int_state state;
+};
+
+static int ns9xxx_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num);
+
+static u32 ns9xxx_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR
+ | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE
+ | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA;
+}
+
+static struct i2c_algorithm ns9xxx_i2c_algo = {
+ .master_xfer = ns9xxx_i2c_xfer,
+ .functionality = ns9xxx_i2c_func,
+};
+
+static irqreturn_t ns9xxx_i2c_irq(int irqnr, void *dev_id)
+{
+ struct ns9xxx_i2c *dev_data = (struct ns9xxx_i2c *)dev_id;
+ unsigned int status;
+
+ /* acknowledge by reading */
+ status = readl(dev_data->ioaddr + I2C_CMD);
+
+ if (dev_data->state != I2C_INT_AWAITING)
+ return IRQ_HANDLED;
+
+ switch (status & I2C_IRQ_MASK) {
+ case I2C_IRQ_RXDATA:
+ spin_lock(&dev_data->lock);
+ if (dev_data->buf)
+ *dev_data->buf = status & 0xff;
+ spin_unlock(&dev_data->lock);
+ case I2C_IRQ_CMDACK:
+ case I2C_IRQ_TXDATA:
+ dev_data->state = I2C_INT_OK;
+ break;
+ case I2C_IRQ_NOACK:
+ writel(I2C_CMD_STOP, dev_data->ioaddr + I2C_CMD);
+ dev_data->state = I2C_INT_ABORT;
+ break;
+ case I2C_IRQ_ARBITLOST:
+ dev_data->state = I2C_INT_RETRY;
+ break;
+ default:
+ dev_data->state = I2C_INT_ERROR;
+ }
+
+ wake_up_interruptible(&dev_data->wait_q);
+
+ return IRQ_HANDLED;
+}
+
+static int ns9xxx_i2c_send_cmd(struct ns9xxx_i2c *dev_data, unsigned int cmd)
+{
+ dev_data->state = I2C_INT_AWAITING;
+ do {
+ writel(cmd, dev_data->ioaddr + I2C_CMD);
+ if (!wait_event_interruptible_timeout(dev_data->wait_q,
+ dev_data->state != I2C_INT_AWAITING,
+ dev_data->adap.timeout))
+ return -ETIMEDOUT;
+ } while (dev_data->state == I2C_INT_AWAITING);
+
+ if (dev_data->state != I2C_INT_OK)
+ return -EIO;
+
+ return 0;
+}
+
+static int ns9xxx_i2c_read(struct ns9xxx_i2c *dev_data, int count)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ while (count-- > 1) {
+ spin_lock_irqsave(&dev_data->lock, flags);
+ dev_data->buf++;
+ spin_unlock_irqrestore(&dev_data->lock, flags);
+
+ ret = ns9xxx_i2c_send_cmd(dev_data, I2C_CMD_NOP);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int ns9xxx_i2c_write(struct ns9xxx_i2c *dev_data,
+ const char *buf, int count)
+{
+ int ret = 0;
+
+ while (count--) {
+ ret = ns9xxx_i2c_send_cmd(dev_data,
+ I2C_CMD_NOP | I2C_CMD_TXVAL | *buf);
+ if (ret)
+ break;
+ buf++;
+ }
+
+ return ret;
+}
+
+static int ns9xxx_i2c_bitbang(struct ns9xxx_i2c *dev_data, struct i2c_msg *msg)
+{
+ int i, nr_bits, ret;
+
+ gpio_direction_output(dev_data->pdata->gpio_sda, 1);
+ gpio_direction_output(dev_data->pdata->gpio_scl, 1);
+ mdelay(10);
+
+ /* start */
+ gpio_set_value(dev_data->pdata->gpio_sda, 0);
+ mdelay(1);
+ gpio_set_value(dev_data->pdata->gpio_scl, 0);
+ mdelay(1);
+
+ nr_bits = (msg->flags & I2C_M_TEN) ? 10 : 7;
+ for (i = 0; i < nr_bits; i++) {
+ /* set data */
+ if (msg->addr & (1 << (nr_bits - i - 1)))
+ gpio_set_value(dev_data->pdata->gpio_sda, 1);
+ else
+ gpio_set_value(dev_data->pdata->gpio_sda, 1);
+ mdelay(1);
+
+ /* toggle clock */
+ gpio_set_value(dev_data->pdata->gpio_scl, 1);
+ mdelay(1);
+ gpio_set_value(dev_data->pdata->gpio_scl, 0);
+ mdelay(1);
+ }
+
+ /* read ack */
+ gpio_direction_input(dev_data->pdata->gpio_sda);
+ gpio_set_value(dev_data->pdata->gpio_scl, 1);
+ mdelay(1);
+ ret = gpio_get_value(dev_data->pdata->gpio_sda);
+
+ /* stop */
+ gpio_direction_output(dev_data->pdata->gpio_sda, 1);
+
+ return ret ? 0 : -ENODEV;
+}
+
+static int ns9xxx_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct ns9xxx_i2c *dev_data = (struct ns9xxx_i2c *)adap->algo_data;
+ int len, i, ret = 0, retry = 10;
+ unsigned long flags = 0;
+ unsigned int cmd, reg;
+ char *buf = NULL;
+
+ dev_data->state = I2C_INT_OK;
+
+ for (i = 0; i < num; i++) {
+ if (dev_data->state == I2C_INT_RETRY) {
+ ret = ns9xxx_i2c_send_cmd(dev_data, I2C_CMD_STOP);
+ --retry;
+ if (ret || !retry)
+ return -EIO;
+ }
+
+ len = msgs[i].len;
+ buf = msgs[i].buf;
+
+ spin_lock_irqsave(&dev_data->lock, flags);
+ dev_data->buf = buf;
+ spin_unlock_irqrestore(&dev_data->lock, flags);
+
+ if (msgs[i].len == 0) {
+ /* send using use bitbang mode */
+ ret = ns9xxx_i2c_bitbang(dev_data, &msgs[i]);
+ /* reset gpios to hardware i2c */
+ dev_data->pdata->gpio_configuration_func();
+ } else {
+ if (!(msgs[i].flags & I2C_M_NOSTART)) {
+ /* set device address */
+ reg = ((msgs[i].addr & I2C_MASTERADDR_ADDRMASK)
+ << I2C_MASTERADDR_ADDRSHIFT);
+
+ if (msgs[i].flags & I2C_M_TEN)
+ reg |= I2C_MASTERADDR_10BIT;
+ else
+ reg |= I2C_MASTERADDR_7BIT;
+
+ writel(reg, dev_data->ioaddr +
+ I2C_MASTERADDR);
+
+ if (msgs[i].flags & I2C_M_RD)
+ cmd = I2C_CMD_READ;
+ else {
+ cmd = I2C_CMD_WRITE | I2C_CMD_TXVAL;
+ cmd |= *buf;
+ len--;
+ buf++;
+ }
+
+ ret = ns9xxx_i2c_send_cmd(dev_data, cmd);
+ if (ret) {
+ if (dev_data->state == I2C_INT_RETRY) {
+ i = 0;
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (msgs[i].flags & I2C_M_RD)
+ ret = ns9xxx_i2c_read(dev_data, len);
+ else
+ ret = ns9xxx_i2c_write(dev_data, buf, len);
+ if (ret) {
+ if (dev_data->state == I2C_INT_RETRY) {
+ i = 0;
+ continue;
+ }
+ break;
+ }
+ }
+ }
+
+ if (ns9xxx_i2c_send_cmd(dev_data, I2C_CMD_STOP)) {
+ /* sometimes interface gets stucked
+ * try to fix this by send "start, nop, start" */
+ ns9xxx_i2c_send_cmd(dev_data, I2C_CMD_NOP);
+ ns9xxx_i2c_send_cmd(dev_data, I2C_CMD_STOP);
+ }
+
+ spin_lock_irqsave(&dev_data->lock, flags);
+ dev_data->buf = NULL;
+ spin_unlock_irqrestore(&dev_data->lock, flags);
+
+ /* return ERROR or number of transmits */
+ return ((ret < 0) ? ret : i);
+}
+
+static int ns9xxx_i2c_set_clock(struct ns9xxx_i2c *dev_data, unsigned int freq)
+{
+ u32 config;
+
+ config = readl(dev_data->ioaddr + I2C_CONFIG) & ~I2C_CONFIG_CLREFMASK;
+
+ switch (freq) {
+ case I2C_NORMALSPEED:
+ config |= (((clk_get_rate(dev_data->clk) / (4 * freq))
+ - 4 - 3) / 2) & I2C_CONFIG_CLREFMASK;
+ break;
+ case I2C_HIGHSPEED:
+ config |= (((clk_get_rate(dev_data->clk) / (4 * freq))
+ - 4 - 24) * 2 / 3) & I2C_CONFIG_CLREFMASK;
+ break;
+ default:
+ pr_warning(DRIVER_NAME ": wrong clock configuration,"
+ " i2c won't work!\n");
+ return -EINVAL;
+ }
+
+ writel(config, dev_data->ioaddr + I2C_CONFIG);
+
+ return 0;
+}
+
+static int __devinit ns9xxx_i2c_probe(struct platform_device *pdev)
+{
+ struct ns9xxx_i2c *dev_data;
+ int ret;
+
+ dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
+ if (!dev_data) {
+ dev_dbg(&pdev->dev, "%s: err_alloc_dd\n", __func__);
+ ret = -ENOMEM;
+ goto err_alloc_dd;
+ }
+ platform_set_drvdata(pdev, dev_data);
+
+ dev_data->pdata = pdev->dev.platform_data;
+ if (!dev_data->pdata) {
+ dev_dbg(&pdev->dev, "%s: err_pdata\n", __func__);
+ ret = -ENOENT;
+ goto err_pdata;
+ }
+
+ snprintf(dev_data->adap.name, ARRAY_SIZE(dev_data->adap.name),
+ DRIVER_NAME);
+ dev_data->adap.owner = THIS_MODULE;
+ dev_data->adap.algo = &ns9xxx_i2c_algo;
+ dev_data->adap.algo_data = dev_data;
+ dev_data->adap.retries = 1;
+ dev_data->adap.timeout = HZ / 10;
+ dev_data->adap.class = I2C_CLASS_HWMON;
+ dev_data->buf = NULL;
+
+ spin_lock_init(&dev_data->lock);
+ init_waitqueue_head(&dev_data->wait_q);
+
+ dev_data->irq = platform_get_irq(pdev, 0);
+ if (dev_data->irq <= 0) {
+ dev_dbg(&pdev->dev, "%s: err_irq\n", __func__);
+ ret = -ENOENT;
+ goto err_irq;
+ }
+
+ dev_data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!dev_data->mem) {
+ dev_dbg(&pdev->dev, "%s: err_mem\n", __func__);
+ ret = -ENOENT;
+ goto err_mem;
+ }
+
+ if (!request_mem_region(dev_data->mem->start,
+ dev_data->mem->end - dev_data->mem->start + 1,
+ DRIVER_NAME)) {
+ dev_dbg(&pdev->dev, "%s: err_req_mem\n", __func__);
+ ret = -EBUSY;
+ goto err_req_mem;
+ }
+
+ dev_data->ioaddr = ioremap(dev_data->mem->start,
+ dev_data->mem->end - dev_data->mem->start + 1);
+ if (dev_data->ioaddr <= 0) {
+ dev_dbg(&pdev->dev, "%s: err_map_mem\n", __func__);
+ ret = -EBUSY;
+ goto err_map_mem;
+ } else
+ dev_dbg(&pdev->dev, "mapped I2C interface to virtual address"
+ "0x%x\n", (int)dev_data->ioaddr);
+
+ if (gpio_request(dev_data->pdata->gpio_scl, DRIVER_NAME)) {
+ dev_dbg(&pdev->dev, "%s: err_gpio_scl\n", __func__);
+ ret = -EBUSY;
+ goto err_gpio_scl;
+ }
+
+ if (gpio_request(dev_data->pdata->gpio_sda, DRIVER_NAME)) {
+ dev_dbg(&pdev->dev, "%s: err_gpio_sda\n", __func__);
+ ret = -EBUSY;
+ goto err_gpio_sda;
+ }
+
+ dev_data->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(dev_data->clk)) {
+ dev_dbg(&pdev->dev, "%s: err_clk_get\n", __func__);
+ ret = PTR_ERR(dev_data->clk);
+ goto err_clk_get;
+ }
+
+ ret = clk_enable(dev_data->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_clk_enable\n", __func__);
+ goto err_clk_enable;
+ }
+
+ /* configure i2c interface */
+ if (!dev_data->pdata->gpio_configuration_func) {
+ dev_dbg(&pdev->dev, "%s: err_cfg_gpio\n", __func__);
+ ret = -ENOENT;
+ goto err_cfg_gpio;
+ }
+ dev_data->pdata->gpio_configuration_func();
+
+ /* Set spike filter width to maximum value to workaround communication
+ * problems on the cc9p9360 module */
+ writel(readl(dev_data->ioaddr + I2C_CONFIG) | (0xf << 9),
+ dev_data->ioaddr + I2C_CONFIG);
+
+ if (dev_data->pdata->speed)
+ ret = ns9xxx_i2c_set_clock(dev_data, dev_data->pdata->speed);
+ else
+ ret = ns9xxx_i2c_set_clock(dev_data, I2C_NORMALSPEED);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_set_clk\n", __func__);
+ goto err_set_clk;
+ }
+
+ ret = request_irq(dev_data->irq, ns9xxx_i2c_irq, 0,
+ DRIVER_NAME, dev_data);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_req_irq\n", __func__);
+ ret = -EBUSY;
+ goto err_req_irq;
+ }
+
+ ret = i2c_add_numbered_adapter(&dev_data->adap);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "%s: err_add_adap\n", __func__);
+ goto err_add_adap;
+ }
+
+ dev_info(&pdev->dev, "NS9XXX I2C adapter\n");
+
+ return 0;
+
+err_add_adap:
+ free_irq(dev_data->irq, dev_data);
+err_req_irq:
+err_set_clk:
+err_cfg_gpio:
+ clk_disable(dev_data->clk);
+err_clk_enable:
+ clk_put(dev_data->clk);
+err_clk_get:
+ gpio_free(dev_data->pdata->gpio_sda);
+err_gpio_sda:
+ gpio_free(dev_data->pdata->gpio_scl);
+err_gpio_scl:
+ iounmap(dev_data->ioaddr);
+err_map_mem:
+ release_mem_region(dev_data->mem->start,
+ dev_data->mem->end - dev_data->mem->start + 1);
+err_req_mem:
+err_mem:
+err_irq:
+err_pdata:
+err_alloc_dd:
+ kfree(dev_data);
+
+ return ret;
+}
+
+static int __devexit ns9xxx_i2c_remove(struct platform_device *pdev)
+{
+ struct ns9xxx_i2c *dev_data = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&dev_data->adap);
+
+ free_irq(dev_data->irq, dev_data);
+
+ clk_disable(dev_data->clk);
+ clk_put(dev_data->clk);
+
+ gpio_free(dev_data->pdata->gpio_sda);
+ gpio_free(dev_data->pdata->gpio_scl);
+
+ iounmap(dev_data->ioaddr);
+ release_mem_region(dev_data->mem->start,
+ dev_data->mem->end - dev_data->mem->start + 1);
+
+ kfree(dev_data);
+
+ return 0;
+}
+
+static struct platform_driver ns9xxx_i2c_driver = {
+ .probe = ns9xxx_i2c_probe,
+ .remove = __devexit_p(ns9xxx_i2c_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init ns9xxx_i2c_init(void)
+{
+ return platform_driver_register(&ns9xxx_i2c_driver);
+}
+
+static void __exit ns9xxx_i2c_exit(void)
+{
+ platform_driver_unregister(&ns9xxx_i2c_driver);
+}
+
+module_init(ns9xxx_i2c_init);
+module_exit(ns9xxx_i2c_exit);
+
+MODULE_AUTHOR("Matthias Ludwig");
+MODULE_DESCRIPTION("Digi NS9xxx I2C Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index b7434d24904e..622caefd6f50 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -666,7 +666,7 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
pdata = s3c24xx_i2c_get_platformdata(i2c->adap.dev.parent);
clkin /= 1000; /* clkin now in KHz */
-
+
dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n",
pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq);
@@ -689,6 +689,13 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
for (; start > end; start--) {
freq = s3c24xx_i2c_calcdivisor(clkin, start, &div1, &divs);
+ /*
+ * If the clock is higher than the requested, continue searching
+ * (Luis Galdos)
+ */
+ if (pdata->bus_freq && (freq * 1000 > pdata->bus_freq))
+ continue;
+
if (freq_acceptable(freq, start))
goto found;
}
diff --git a/drivers/i2c/busses/i2c-stmp378x.c b/drivers/i2c/busses/i2c-stmp378x.c
new file mode 100644
index 000000000000..4a01b55f5f07
--- /dev/null
+++ b/drivers/i2c/busses/i2c-stmp378x.c
@@ -0,0 +1,337 @@
+/*
+ * Freescale STMP378X I2C bus driver
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/* #define DEBUG */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/regs-i2c.h>
+#include <mach/regs-apbx.h>
+#include <mach/i2c.h>
+
+static void reset_i2c_module(void)
+{
+ int count;
+ count = 1000;
+ HW_I2C_CTRL0_SET(BM_I2C_CTRL0_SFTRST);
+ udelay(10); /* Reseting the module can take multiple clocks.*/
+ while (--count && (!(HW_I2C_CTRL0_RD() & BM_I2C_CTRL0_CLKGATE)))
+ udelay(1);
+
+ if (!count) {
+ printk(KERN_ERR "timeout reseting the module\n");
+ BUG();
+ }
+
+ /* take controller out of reset */
+ HW_I2C_CTRL0_CLR(BM_I2C_CTRL0_SFTRST | BM_I2C_CTRL0_CLKGATE);
+ udelay(10);
+ HW_I2C_CTRL1_SET(0x0000FF00); /* Wil catch all error (IRQ mask) */
+
+}
+
+/*
+ * Low level master read/write transaction.
+ */
+static int stmp378x_i2c_xfer_msg(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int stop)
+{
+ struct stmp378x_i2c_dev *dev = i2c_get_adapdata(adap);
+ int err;
+
+ init_completion(&dev->cmd_complete);
+ dev->cmd_err = 0;
+
+ dev_dbg(dev->dev, " Start XFER ===>\n");
+ dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
+ msg->addr, msg->len, msg->flags, stop);
+
+ if ((msg->len == 0) || (msg->len > (PAGE_SIZE - 1)))
+ return -EINVAL;
+
+ if (msg->flags & I2C_M_RD) {
+ hw_i2c_setup_read(msg->addr ,
+ msg->buf ,
+ msg->len,
+ stop ? BM_I2C_CTRL0_POST_SEND_STOP : 0);
+
+ hw_i2c_run(1); /* read */
+ } else {
+ hw_i2c_setup_write(msg->addr ,
+ msg->buf ,
+ msg->len,
+ stop ? BM_I2C_CTRL0_POST_SEND_STOP : 0);
+
+ hw_i2c_run(0); /* write */
+ }
+
+ err = wait_for_completion_interruptible_timeout(
+ &dev->cmd_complete,
+ msecs_to_jiffies(1000)
+ );
+
+ if (err < 0) {
+ dev_dbg(dev->dev, "controler is timed out\n");
+ return -ETIMEDOUT;
+ }
+ if ((!dev->cmd_err) && (msg->flags & I2C_M_RD))
+ hw_i2c_finish_read(msg->buf, msg->len);
+
+ dev_dbg(dev->dev, "<============= Done with err=%d\n", dev->cmd_err);
+
+
+ return dev->cmd_err;
+}
+
+
+static int
+stmp378x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ int i;
+ int err;
+
+ if (!msgs->len)
+ return -EINVAL;
+
+ for (i = 0; i < num; i++) {
+ err = stmp378x_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ if (err)
+ break;
+ }
+
+ if (err == 0)
+ err = num;
+
+ return err;
+}
+
+static u32
+stmp378x_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+/*
+ * Debug. Don't need dma_irq for the final version
+ */
+
+static irqreturn_t
+stmp378x_i2c_dma_isr(int this_irq, void *dev_id)
+{
+ hw_i2c_clear_dma_interrupt();
+ return IRQ_HANDLED;
+
+}
+
+#define I2C_IRQ_MASK 0x000000FF
+
+static irqreturn_t
+stmp378x_i2c_isr(int this_irq, void *dev_id)
+{
+ struct stmp378x_i2c_dev *dev = dev_id;
+ u32 stat;
+ u32 done_mask =
+ BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ |
+ BM_I2C_CTRL1_BUS_FREE_IRQ ;
+
+ stat = HW_I2C_CTRL1_RD() & I2C_IRQ_MASK;
+ if (!stat)
+ return IRQ_NONE;
+
+ if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
+ dev->cmd_err = -EREMOTEIO;
+
+ /*
+ * Stop DMA
+ * Clear NAK
+ */
+ HW_I2C_CTRL1_SET(BM_I2C_CTRL1_CLR_GOT_A_NAK);
+ hw_i2c_reset_dma();
+ reset_i2c_module();
+
+ complete(&dev->cmd_complete);
+
+ goto done;
+ }
+
+/* Don't care about BM_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ */
+ if (stat & (
+ BM_I2C_CTRL1_EARLY_TERM_IRQ |
+ BM_I2C_CTRL1_MASTER_LOSS_IRQ |
+ BM_I2C_CTRL1_SLAVE_STOP_IRQ |
+ BM_I2C_CTRL1_SLAVE_IRQ
+ )) {
+ dev->cmd_err = -EIO;
+ complete(&dev->cmd_complete);
+ goto done;
+ }
+ if ((stat & done_mask) == done_mask)
+ complete(&dev->cmd_complete);
+
+
+done:
+ HW_I2C_CTRL1_CLR(stat);
+ return IRQ_HANDLED;
+}
+
+static const struct i2c_algorithm stmp378x_i2c_algo = {
+ .master_xfer = stmp378x_i2c_xfer,
+ .functionality = stmp378x_i2c_func,
+};
+
+
+static int
+stmp378x_i2c_probe(struct platform_device *pdev)
+{
+ struct stmp378x_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ struct resource *irq;
+ int err = 0;
+
+ /* NOTE: driver uses the static register mapping */
+ dev = kzalloc(sizeof(struct stmp378x_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev, "no mem \n");
+ return -ENOMEM;
+ }
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); /* Error */
+ if (!irq) {
+ dev_err(&pdev->dev, "no err_irq resource\n");
+ err = -ENODEV;
+ goto nores;
+ }
+ dev->irq_err = irq->start;
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1); /* DMA */
+ if (!irq) {
+ dev_err(&pdev->dev, "no dma_irq resource\n");
+ err = -ENODEV;
+ goto nores;
+ }
+
+ dev->irq_dma = irq->start;
+ dev->dev = &pdev->dev;
+
+ err = request_irq(dev->irq_err, stmp378x_i2c_isr, 0, pdev->name, dev);
+ if (err) {
+ dev_err(&pdev->dev, "Can't get IRQ\n");
+ goto no_err_irq;
+ }
+
+ err = request_irq(dev->irq_dma,
+ stmp378x_i2c_dma_isr,
+ 0, pdev->name, dev);
+ if (err) {
+ dev_err(&pdev->dev, "Can't get IRQ\n");
+ goto no_dma_irq;
+ }
+
+ err = hw_i2c_init(&pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "HW Init failed\n");
+ goto init_failed;
+ }
+
+ HW_I2C_CTRL1_SET(0x0000FF00); /* Will catch all error (IRQ mask) */
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ strncpy(adap->name, "378x I2C adapter", sizeof(adap->name));
+ adap->algo = &stmp378x_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+
+ adap->nr = pdev->id;
+ err = i2c_add_numbered_adapter(adap);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add adapter\n");
+ goto no_i2c_adapter;
+
+ }
+
+ return 0;
+
+no_i2c_adapter:
+ hw_i2c_stop(dev->dev);
+init_failed:
+ free_irq(dev->irq_dma, dev);
+no_dma_irq:
+ free_irq(dev->irq_err, dev);
+no_err_irq:
+nores:
+ kfree(dev);
+ return err;
+}
+
+static int
+stmp378x_i2c_remove(struct platform_device *pdev)
+{
+ struct stmp378x_i2c_dev *dev = platform_get_drvdata(pdev);
+ int res;
+
+ res = i2c_del_adapter(&dev->adapter);
+ if (res)
+ return -EBUSY;
+
+ hw_i2c_stop(dev->dev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(dev->irq_err, dev);
+ free_irq(dev->irq_dma, dev);
+
+ kfree(dev);
+ return 0;
+}
+
+static struct platform_driver stmp378x_i2c_driver = {
+ .probe = stmp378x_i2c_probe,
+ .remove = __devexit_p(stmp378x_i2c_remove),
+ .driver = {
+ .name = "i2c_stmp",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* I2C may be needed to bring up other drivers */
+
+static int __init stmp378x_i2c_init_driver(void)
+{
+ return platform_driver_register(&stmp378x_i2c_driver);
+}
+subsys_initcall(stmp378x_i2c_init_driver);
+
+static void __exit stmp378x_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&stmp378x_i2c_driver);
+}
+module_exit(stmp378x_i2c_exit_driver);
+
+MODULE_AUTHOR("old_chap@embeddedalley.com");
+MODULE_DESCRIPTION("IIC for Freescale STMP378x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/mxc_i2c.c b/drivers/i2c/busses/mxc_i2c.c
new file mode 100644
index 000000000000..f051419f02bc
--- /dev/null
+++ b/drivers/i2c/busses/mxc_i2c.c
@@ -0,0 +1,799 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_i2c.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC I2C buses.
+ *
+ * Based on i2c driver algorithm for PCF8584 adapters
+ *
+ * @ingroup MXCI2C
+ */
+
+/*
+ * Include Files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include "mxc_i2c_reg.h"
+
+/*!
+ * In case the MXC device has multiple I2C modules, this structure is used to
+ * store information specific to each I2C module.
+ */
+typedef struct {
+ /*!
+ * This structure is used to identify the physical i2c bus along with
+ * the access algorithms necessary to access it.
+ */
+ struct i2c_adapter adap;
+
+ /*!
+ * This waitqueue is used to wait for the data transfer to complete.
+ */
+ wait_queue_head_t wq;
+
+ /*!
+ * The base address of the I2C device.
+ */
+ unsigned long membase;
+
+ /*!
+ * The interrupt number used by the I2C device.
+ */
+ int irq;
+
+ /*!
+ * The default clock divider value to be used.
+ */
+ unsigned int clkdiv;
+
+ /*!
+ * The clock source for the device.
+ */
+ struct clk *clk;
+
+ /*!
+ * The current power state of the device
+ */
+ bool low_power;
+
+ /*!
+ * Boolean to indicate if data was transferred
+ */
+ bool transfer_done;
+
+ /*!
+ * Boolean to indicate if we received an ACK for the data transmitted
+ */
+ bool tx_success;
+} mxc_i2c_device;
+
+struct clk_div_table {
+ int reg_value;
+ int div;
+};
+
+static const struct clk_div_table i2c_clk_table[] = {
+ {0x20, 22}, {0x21, 24}, {0x22, 26}, {0x23, 28},
+ {0, 30}, {1, 32}, {0x24, 32}, {2, 36},
+ {0x25, 36}, {0x26, 40}, {3, 42}, {0x27, 44},
+ {4, 48}, {0x28, 48}, {5, 52}, {0x29, 56},
+ {6, 60}, {0x2A, 64}, {7, 72}, {0x2B, 72},
+ {8, 80}, {0x2C, 80}, {9, 88}, {0x2D, 96},
+ {0xA, 104}, {0x2E, 112}, {0xB, 128}, {0x2F, 128},
+ {0xC, 144}, {0xD, 160}, {0x30, 160}, {0xE, 192},
+ {0x31, 192}, {0x32, 224}, {0xF, 240}, {0x33, 256},
+ {0x10, 288}, {0x11, 320}, {0x34, 320}, {0x12, 384},
+ {0x35, 384}, {0x36, 448}, {0x13, 480}, {0x37, 512},
+ {0x14, 576}, {0x15, 640}, {0x38, 640}, {0x16, 768},
+ {0x39, 768}, {0x3A, 896}, {0x17, 960}, {0x3B, 1024},
+ {0x18, 1152}, {0x19, 1280}, {0x3C, 1280}, {0x1A, 1536},
+ {0x3D, 1536}, {0x3E, 1792}, {0x1B, 1920}, {0x3F, 2048},
+ {0x1C, 2304}, {0x1D, 2560}, {0x1E, 3072}, {0x1F, 3840},
+ {0, 0}
+};
+
+extern void gpio_i2c_active(int i2c_num);
+extern void gpio_i2c_inactive(int i2c_num);
+
+/*!
+ * Transmit a \b STOP signal to the slave device.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ */
+static void mxc_i2c_stop(mxc_i2c_device * dev)
+{
+ unsigned int cr, sr;
+ int retry = 16;
+
+ cr = readw(dev->membase + MXC_I2CR);
+ cr &= ~(MXC_I2CR_MSTA | MXC_I2CR_MTX);
+ writew(cr, dev->membase + MXC_I2CR);
+
+ /* Wait till the Bus Busy bit is reset */
+ sr = readw(dev->membase + MXC_I2SR);
+ while (retry-- && ((sr & MXC_I2SR_IBB))) {
+ udelay(3);
+ sr = readw(dev->membase + MXC_I2SR);
+ }
+ if (retry <= 0)
+ dev_err(&dev->adap.dev, "Could not set I2C Bus Busy bit"
+ " to zero.\n");
+}
+
+/*!
+ * Wait for the transmission of the data byte to complete. This function waits
+ * till we get a signal from the interrupt service routine indicating completion
+ * of the address cycle or we time out.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param trans_flag transfer flag
+ *
+ *
+ * @return The function returns 0 on success or -1 if an ack was not received
+ */
+
+static int mxc_i2c_wait_for_tc(mxc_i2c_device * dev, int trans_flag)
+{
+ int retry = 16;
+
+ while (retry-- && !dev->transfer_done) {
+ wait_event_interruptible_timeout(dev->wq,
+ dev->transfer_done,
+ dev->adap.timeout);
+ }
+ dev->transfer_done = false;
+
+ if (retry <= 0) {
+ /* Unable to send data */
+ dev_err(&dev->adap.dev, "Data not transmitted\n");
+ return -1;
+ }
+
+ if (!dev->tx_success) {
+ /* An ACK was not received for transmitted byte */
+ dev_err(&dev->adap.dev, "ACK not received \n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * Transmit a \b START signal to the slave device.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param *msg pointer to a message structure that contains the slave
+ * address
+ *
+ * @return The function returns EBUSY on failure, 0 on success.
+ */
+static int mxc_i2c_start(mxc_i2c_device *dev, struct i2c_msg *msg)
+{
+ volatile unsigned int cr, sr;
+ unsigned int addr_trans;
+ int retry = 16;
+
+ /*
+ * Set the slave address and the requested transfer mode
+ * in the data register
+ */
+ addr_trans = msg->addr << 1;
+ if (msg->flags & I2C_M_RD) {
+ addr_trans |= 0x01;
+ }
+
+ /* Set the Master bit */
+ cr = readw(dev->membase + MXC_I2CR);
+ cr |= MXC_I2CR_MSTA;
+ writew(cr, dev->membase + MXC_I2CR);
+
+ /* Wait till the Bus Busy bit is set */
+ sr = readw(dev->membase + MXC_I2SR);
+ while (retry-- && (!(sr & MXC_I2SR_IBB))) {
+ udelay(3);
+ sr = readw(dev->membase + MXC_I2SR);
+ }
+ if (retry <= 0) {
+ dev_err(&dev->adap.dev, "Could not grab Bus ownership\n");
+ return -EBUSY;
+ }
+
+ /* Set the Transmit bit */
+ cr = readw(dev->membase + MXC_I2CR);
+ cr |= MXC_I2CR_MTX;
+ writew(cr, dev->membase + MXC_I2CR);
+
+ writew(addr_trans, dev->membase + MXC_I2DR);
+ return 0;
+}
+
+/*!
+ * Transmit a \b REPEAT START to the slave device
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param *msg pointer to a message structure that contains the slave
+ * address
+ */
+static void mxc_i2c_repstart(mxc_i2c_device *dev, struct i2c_msg *msg)
+{
+ volatile unsigned int cr, sr;
+ unsigned int addr_trans;
+ int retry = 16;
+
+ /*
+ * Set the slave address and the requested transfer mode
+ * in the data register
+ */
+ addr_trans = msg->addr << 1;
+ if (msg->flags & I2C_M_RD) {
+ addr_trans |= 0x01;
+ }
+ cr = readw(dev->membase + MXC_I2CR);
+ cr |= MXC_I2CR_RSTA;
+ writew(cr, dev->membase + MXC_I2CR);
+ /* Wait till the Bus Busy bit is set */
+ sr = readw(dev->membase + MXC_I2SR);
+ while (retry-- && (!(sr & MXC_I2SR_IBB))) {
+ udelay(3);
+ sr = readw(dev->membase + MXC_I2SR);
+ }
+ if (retry <= 0) {
+ dev_err(&dev->adap.dev, "Could not grab Bus ownership\n");
+ return;
+ }
+ writew(addr_trans, dev->membase + MXC_I2DR);
+}
+
+/*!
+ * Read the received data. The function waits till data is available or times
+ * out. Generates a stop signal if this is the last message to be received.
+ * Sends an ack for all the bytes received except the last byte.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param *msg pointer to a message structure that contains the slave
+ * address and a pointer to the receive buffer
+ * @param last indicates that this is the last message to be received
+ * @param addr_comp flag indicates that we just finished the address cycle
+ *
+ * @return The function returns the number of bytes read or -1 on time out.
+ */
+static int mxc_i2c_readbytes(mxc_i2c_device * dev, struct i2c_msg *msg,
+ int last, int addr_comp)
+{
+ int i;
+ char *buf = msg->buf;
+ int len = msg->len;
+ volatile unsigned int cr;
+
+ cr = readw(dev->membase + MXC_I2CR);
+ /*
+ * Clear MTX to switch to receive mode.
+ */
+ cr &= ~MXC_I2CR_MTX;
+ /*
+ * Clear the TXAK bit to gen an ack when receiving only one byte.
+ */
+ if (len == 1) {
+ cr |= MXC_I2CR_TXAK;
+ } else {
+ cr &= ~MXC_I2CR_TXAK;
+ }
+ writew(cr, dev->membase + MXC_I2CR);
+ /*
+ * Dummy read only at the end of an address cycle
+ */
+ if (addr_comp > 0) {
+ readw(dev->membase + MXC_I2DR);
+ }
+
+ for (i = 0; i < len; i++) {
+ /*
+ * Wait for data transmission to complete
+ */
+ if (mxc_i2c_wait_for_tc(dev, msg->flags)) {
+ mxc_i2c_stop(dev);
+ return -1;
+ }
+ /*
+ * Do not generate an ACK for the last byte
+ */
+ if (i == (len - 2)) {
+ cr = readw(dev->membase + MXC_I2CR);
+ cr |= MXC_I2CR_TXAK;
+ writew(cr, dev->membase + MXC_I2CR);
+ } else if (i == (len - 1)) {
+ if (last) {
+ mxc_i2c_stop(dev);
+ }
+ }
+ /* Read the data */
+ *buf++ = readw(dev->membase + MXC_I2DR);
+ }
+
+ return i;
+}
+
+/*!
+ * Write the data to the data register. Generates a stop signal if this is
+ * the last message to be sent or if no ack was received for the data sent.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param *msg pointer to a message structure that contains the slave
+ * address and data to be sent
+ * @param last indicates that this is the last message to be received
+ *
+ * @return The function returns the number of bytes written or -1 on time out
+ * or if no ack was received for the data that was sent.
+ */
+static int mxc_i2c_writebytes(mxc_i2c_device * dev, struct i2c_msg *msg,
+ int last)
+{
+ int i;
+ char *buf = msg->buf;
+ int len = msg->len;
+ volatile unsigned int cr;
+
+ cr = readw(dev->membase + MXC_I2CR);
+ /* Set MTX to switch to transmit mode */
+ cr |= MXC_I2CR_MTX;
+ writew(cr, dev->membase + MXC_I2CR);
+
+ for (i = 0; i < len; i++) {
+ /*
+ * Write the data
+ */
+ writew(*buf++, dev->membase + MXC_I2DR);
+ if (mxc_i2c_wait_for_tc(dev, msg->flags)) {
+ mxc_i2c_stop(dev);
+ return -1;
+ }
+ }
+ if (last > 0) {
+ mxc_i2c_stop(dev);
+ }
+
+ return i;
+}
+
+/*!
+ * Function enables the I2C module and initializes the registers.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ * @param trans_flag transfer flag
+ */
+static void mxc_i2c_module_en(mxc_i2c_device * dev, int trans_flag)
+{
+ clk_enable(dev->clk);
+ /* Set the frequency divider */
+ writew(dev->clkdiv, dev->membase + MXC_IFDR);
+ /* Clear the status register */
+ writew(0x0, dev->membase + MXC_I2SR);
+ /* Enable I2C and its interrupts */
+ writew(MXC_I2CR_IEN, dev->membase + MXC_I2CR);
+ writew(MXC_I2CR_IEN | MXC_I2CR_IIEN, dev->membase + MXC_I2CR);
+}
+
+/*!
+ * Disables the I2C module.
+ *
+ * @param dev the mxc i2c structure used to get to the right i2c device
+ */
+static void mxc_i2c_module_dis(mxc_i2c_device * dev)
+{
+ writew(0x0, dev->membase + MXC_I2CR);
+ clk_disable(dev->clk);
+}
+
+/*!
+ * The function is registered in the adapter structure. It is called when an MXC
+ * driver wishes to transfer data to a device connected to the I2C device.
+ *
+ * @param adap adapter structure for the MXC i2c device
+ * @param msgs[] array of messages to be transferred to the device
+ * @param num number of messages to be transferred to the device
+ *
+ * @return The function returns the number of messages transferred,
+ * \b -EREMOTEIO on I2C failure and a 0 if the num argument is
+ * less than 0.
+ */
+static int mxc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ mxc_i2c_device *dev = (mxc_i2c_device *) (i2c_get_adapdata(adap));
+ int i, ret = 0, addr_comp = 0;
+ volatile unsigned int sr;
+ int retry = 5;
+
+ if (dev->low_power) {
+ dev_err(&dev->adap.dev, "I2C Device in low power mode\n");
+ return -EREMOTEIO;
+ }
+
+ if (num < 1) {
+ return 0;
+ }
+
+ mxc_i2c_module_en(dev, msgs[0].flags);
+ sr = readw(dev->membase + MXC_I2SR);
+ /*
+ * Check bus state
+ */
+
+ while ((sr & MXC_I2SR_IBB) && retry--) {
+ udelay(5);
+ sr = readw(dev->membase + MXC_I2SR);
+ }
+
+ if ((sr & MXC_I2SR_IBB) && retry < 0) {
+ mxc_i2c_module_dis(dev);
+ dev_err(&dev->adap.dev, "Bus busy\n");
+ return -EREMOTEIO;
+ }
+
+ //gpio_i2c_active(dev->adap.id);
+ dev->transfer_done = false;
+ dev->tx_success = false;
+ for (i = 0; i < num && ret >= 0; i++) {
+ addr_comp = 0;
+ /*
+ * Send the slave address and transfer direction in the
+ * address cycle
+ */
+ if (i == 0) {
+ /*
+ * Send a start or repeat start signal
+ */
+ if (mxc_i2c_start(dev, &msgs[0]))
+ return -EREMOTEIO;
+ /* Wait for the address cycle to complete */
+ if (mxc_i2c_wait_for_tc(dev, msgs[0].flags)) {
+ mxc_i2c_stop(dev);
+ //gpio_i2c_inactive(dev->adap.id);
+ mxc_i2c_module_dis(dev);
+ return -EREMOTEIO;
+ }
+ addr_comp = 1;
+ } else {
+ /*
+ * Generate repeat start only if required i.e the address
+ * changed or the transfer direction changed
+ */
+ if ((msgs[i].addr != msgs[i - 1].addr) ||
+ ((msgs[i].flags & I2C_M_RD) !=
+ (msgs[i - 1].flags & I2C_M_RD))) {
+ mxc_i2c_repstart(dev, &msgs[i]);
+ /* Wait for the address cycle to complete */
+ if (mxc_i2c_wait_for_tc(dev, msgs[i].flags)) {
+ mxc_i2c_stop(dev);
+ //gpio_i2c_inactive(dev->adap.id);
+ mxc_i2c_module_dis(dev);
+ return -EREMOTEIO;
+ }
+ addr_comp = 1;
+ }
+ }
+
+ /* Transfer the data */
+ if (msgs[i].flags & I2C_M_RD) {
+ /* Read the data */
+ ret = mxc_i2c_readbytes(dev, &msgs[i], (i + 1 == num),
+ addr_comp);
+ if (ret < 0) {
+ dev_err(&dev->adap.dev, "mxc_i2c_readbytes:"
+ " fail.\n");
+ break;
+ }
+ } else {
+ /* Write the data */
+ ret = mxc_i2c_writebytes(dev, &msgs[i], (i + 1 == num));
+ if (ret < 0) {
+ dev_err(&dev->adap.dev, "mxc_i2c_writebytes:"
+ " fail.\n");
+ break;
+ }
+ }
+ }
+
+ //gpio_i2c_inactive(dev->adap.id);
+ mxc_i2c_module_dis(dev);
+ /*
+ * Decrease by 1 as we do not want Start message to be included in
+ * the count
+ */
+ return (i < 0 ? ret : i);
+}
+
+/*!
+ * Returns the i2c functionality supported by this driver.
+ *
+ * @param adap adapter structure for this i2c device
+ *
+ * @return Returns the functionality that is supported.
+ */
+static u32 mxc_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+/*!
+ * Stores the pointers for the i2c algorithm functions. The algorithm functions
+ * is used by the i2c bus driver to talk to the i2c bus
+ */
+static struct i2c_algorithm mxc_i2c_algorithm = {
+ .master_xfer = mxc_i2c_xfer,
+ .functionality = mxc_i2c_func
+};
+
+/*!
+ * Interrupt Service Routine. It signals to the process about the data transfer
+ * completion. Also sets a flag if bus arbitration is lost.
+ * @param irq the interrupt number
+ * @param dev_id driver private data
+ *
+ * @return The function returns \b IRQ_HANDLED.
+ */
+static irqreturn_t mxc_i2c_handler(int irq, void *dev_id)
+{
+ mxc_i2c_device *dev = dev_id;
+ volatile unsigned int sr, cr;
+
+ sr = readw(dev->membase + MXC_I2SR);
+ cr = readw(dev->membase + MXC_I2CR);
+
+ /*
+ * Clear the interrupt bit
+ */
+ writew(0x0, dev->membase + MXC_I2SR);
+
+ if (sr & MXC_I2SR_IAL) {
+ dev_err(&dev->adap.dev, "Bus Arbitration lost\n");
+ } else {
+ /* Interrupt due byte transfer completion */
+ dev->tx_success = true;
+ /* Check if RXAK is received in Transmit mode */
+ if ((cr & MXC_I2CR_MTX) && (sr & MXC_I2SR_RXAK)) {
+ dev->tx_success = false;
+ }
+ dev->transfer_done = true;
+ wake_up_interruptible(&dev->wq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function is called to put the I2C adapter in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure.
+ */
+static int mxci2c_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ mxc_i2c_device *mxcdev = platform_get_drvdata(pdev);
+ volatile unsigned int sr = 0;
+
+ if (mxcdev == NULL) {
+ return -1;
+ }
+
+ /* Prevent further calls to be processed */
+ mxcdev->low_power = true;
+ /* Wait till we finish the current transfer */
+ sr = readw(mxcdev->membase + MXC_I2SR);
+ while (sr & MXC_I2SR_IBB) {
+ msleep(10);
+ sr = readw(mxcdev->membase + MXC_I2SR);
+ }
+ gpio_i2c_inactive(mxcdev->adap.id);
+
+ return 0;
+}
+
+/*!
+ * This function is called to bring the I2C adapter back from a low power state. Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to resume
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxci2c_resume(struct platform_device *pdev)
+{
+ mxc_i2c_device *mxcdev = platform_get_drvdata(pdev);
+
+ if (mxcdev == NULL)
+ return -1;
+
+ mxcdev->low_power = false;
+ gpio_i2c_active(mxcdev->adap.id);
+
+ return 0;
+}
+
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions
+ *
+ * @return The function always returns 0.
+ */
+static int mxci2c_probe(struct platform_device *pdev)
+{
+ mxc_i2c_device *mxc_i2c;
+ struct mxc_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data;
+ struct resource *res;
+ int id = pdev->id;
+ u32 clk_freq;
+ int ret = 0;
+ int i;
+
+ mxc_i2c = kzalloc(sizeof(mxc_i2c_device), GFP_KERNEL);
+ if (!mxc_i2c) {
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ ret = -ENODEV;
+ goto err1;
+ }
+ mxc_i2c->membase = IO_ADDRESS(res->start);
+
+ /*
+ * Request the I2C interrupt
+ */
+ mxc_i2c->irq = platform_get_irq(pdev, 0);
+ if (mxc_i2c->irq < 0) {
+ ret = mxc_i2c->irq;
+ goto err1;
+ }
+
+ ret = request_irq(mxc_i2c->irq, mxc_i2c_handler,
+ 0, pdev->name, mxc_i2c);
+ if (ret < 0) {
+ goto err1;
+ }
+
+ init_waitqueue_head(&mxc_i2c->wq);
+
+ mxc_i2c->low_power = false;
+
+ gpio_i2c_active(id);
+
+ mxc_i2c->clk = clk_get(&pdev->dev, "i2c_clk");
+ clk_freq = clk_get_rate(mxc_i2c->clk);
+ mxc_i2c->clkdiv = -1;
+ if (i2c_plat_data->i2c_clk) {
+ /* Calculate divider and round up any fractional part */
+ int div = (clk_freq + i2c_plat_data->i2c_clk - 1) /
+ i2c_plat_data->i2c_clk;
+ for (i = 0; i2c_clk_table[i].div != 0; i++) {
+ if (i2c_clk_table[i].div >= div) {
+ mxc_i2c->clkdiv = i2c_clk_table[i].reg_value;
+ break;
+ }
+ }
+ }
+ if (mxc_i2c->clkdiv == -1) {
+ i--;
+ mxc_i2c->clkdiv = 0x1F; /* Use max divider */
+ }
+ dev_dbg(&pdev->dev, "i2c speed is %d/%d = %d bps, reg val = 0x%02X\n",
+ clk_freq, i2c_clk_table[i].div,
+ clk_freq / i2c_clk_table[i].div, mxc_i2c->clkdiv);
+
+ /*
+ * Set the adapter information
+ */
+ strlcpy(mxc_i2c->adap.name, pdev->name, 48);
+ mxc_i2c->adap.id = mxc_i2c->adap.nr = id;
+ mxc_i2c->adap.algo = &mxc_i2c_algorithm;
+ mxc_i2c->adap.timeout = 1;
+ platform_set_drvdata(pdev, mxc_i2c);
+ i2c_set_adapdata(&mxc_i2c->adap, mxc_i2c);
+ if ((ret = i2c_add_numbered_adapter(&mxc_i2c->adap)) < 0) {
+ goto err2;
+ }
+
+ printk(KERN_INFO "MXC I2C driver\n");
+ return 0;
+
+ err2:
+ free_irq(mxc_i2c->irq, mxc_i2c);
+ gpio_i2c_inactive(id);
+ err1:
+ dev_err(&pdev->dev, "failed to probe i2c adapter\n");
+ kfree(mxc_i2c);
+ return ret;
+}
+
+/*!
+ * Dissociates the driver from the I2C device.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mxci2c_remove(struct platform_device *pdev)
+{
+ mxc_i2c_device *mxc_i2c = platform_get_drvdata(pdev);
+ int id = pdev->id;
+
+ free_irq(mxc_i2c->irq, mxc_i2c);
+ i2c_del_adapter(&mxc_i2c->adap);
+ gpio_i2c_inactive(id);
+ clk_put(mxc_i2c->clk);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxci2c_driver = {
+ .driver = {
+ .name = "mxc_i2c",
+ .owner = THIS_MODULE,
+ },
+ .probe = mxci2c_probe,
+ .remove = mxci2c_remove,
+ .suspend_late = mxci2c_suspend,
+ .resume_early = mxci2c_resume,
+};
+
+/*!
+ * Function requests the interrupts and registers the i2c adapter structures.
+ *
+ * @return The function returns 0 on success and a non-zero value on failure.
+ */
+static int __init mxc_i2c_init(void)
+{
+ /* Register the device driver structure. */
+ return platform_driver_register(&mxci2c_driver);
+}
+
+/*!
+ * This function is used to cleanup all resources before the driver exits.
+ */
+static void __exit mxc_i2c_exit(void)
+{
+ platform_driver_unregister(&mxci2c_driver);
+}
+
+subsys_initcall(mxc_i2c_init);
+module_exit(mxc_i2c_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC I2C driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/mxc_i2c_hs.c b/drivers/i2c/busses/mxc_i2c_hs.c
new file mode 100644
index 000000000000..b9132351359e
--- /dev/null
+++ b/drivers/i2c/busses/mxc_i2c_hs.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include "mxc_i2c_hs_reg.h"
+
+typedef struct {
+ struct device *dev;
+
+ unsigned long reg_base_virt;
+ unsigned long reg_base_phy;
+ int irq;
+ unsigned int speed;
+ struct clk *ipg_clk;
+ struct clk *serial_clk;
+ bool low_power;
+
+ struct i2c_msg *msg;
+ int index;
+} mxc_i2c_hs;
+
+struct clk_div_table {
+ int reg_value;
+ int div;
+};
+
+static const struct clk_div_table i2c_clk_table[] = {
+ {0x0, 16}, {0x1, 18}, {0x2, 20}, {0x3, 22},
+ {0x20, 24}, {0x21, 26}, {0x22, 28}, {0x23, 30},
+ {0x4, 32}, {0x5, 36}, {0x6, 40}, {0x7, 44},
+ {0x24, 48}, {0x25, 52}, {0x26, 56}, {0x27, 60},
+ {0x8, 64}, {0x9, 72}, {0xa, 80}, {0xb, 88},
+ {0x28, 96}, {0x29, 104}, {0x2a, 112}, {0x2b, 120},
+ {0xc, 128}, {0xd, 144}, {0xe, 160}, {0xf, 176},
+ {0x2c, 192}, {0x2d, 208}, {0x2e, 224}, {0x2f, 240},
+ {0x10, 256}, {0x11, 288}, {0x12, 320}, {0x13, 352},
+ {0x30, 384}, {0x31, 416}, {0x32, 448}, {0x33, 480},
+ {0x14, 512}, {0x15, 576}, {0x16, 640}, {0x17, 704},
+ {0x34, 768}, {0x35, 832}, {0x36, 896}, {0x37, 960},
+ {0x18, 1024}, {0x19, 1152}, {0x1a, 1280}, {0x1b, 1408},
+ {0x38, 1536}, {0x39, 1664}, {0x3a, 1792}, {0x3b, 1920},
+ {0x1c, 2048}, {0x1d, 2304}, {0x1e, 2560}, {0x1f, 2816},
+ {0x3c, 3072}, {0x3d, 3328}, {0x3E, 3584}, {0x3F, 3840},
+ {-1, -1}
+};
+
+static struct i2c_adapter *adap;
+
+extern void gpio_i2c_hs_inactive(void);
+extern void gpio_i2c_hs_active(void);
+
+static u16 reg_read(mxc_i2c_hs *i2c_hs, u32 reg_offset)
+{
+ return __raw_readw(i2c_hs->reg_base_virt + reg_offset);
+}
+
+static void reg_write(mxc_i2c_hs *i2c_hs, u32 reg_offset, u16 data)
+{
+ __raw_writew(data, i2c_hs->reg_base_virt + reg_offset);
+}
+
+static void reg_set_mask(mxc_i2c_hs *i2c_hs, u32 reg_offset, u16 mask)
+{
+ u16 value;
+
+ value = reg_read(i2c_hs, reg_offset);
+ value |= mask;
+ reg_write(i2c_hs, reg_offset, value);
+}
+static void reg_clear_mask(mxc_i2c_hs *i2c_hs, u32 reg_offset, u16 mask)
+{
+ u16 value;
+
+ value = reg_read(i2c_hs, reg_offset);
+ value &= ~mask;
+ reg_write(i2c_hs, reg_offset, value);
+}
+
+static void mxci2c_hs_set_div(mxc_i2c_hs *i2c_hs)
+{
+ unsigned long clk_freq;
+ int i;
+ int div = -1;;
+
+ clk_freq = clk_get_rate(i2c_hs->serial_clk);
+ if (i2c_hs->speed) {
+ div = (clk_freq + i2c_hs->speed - 1) / i2c_hs->speed;
+ for (i = 0; i2c_clk_table[i].div >= 0; i++) {
+ if (i2c_clk_table[i].div >= div) {
+ div = i2c_clk_table[i].reg_value;
+ reg_write(i2c_hs, HIFSFDR, div);
+ break;
+ }
+ }
+ }
+}
+
+static int mxci2c_hs_enable(mxc_i2c_hs *i2c_hs)
+{
+ gpio_i2c_hs_active();
+ clk_enable(i2c_hs->ipg_clk);
+ clk_enable(i2c_hs->serial_clk);
+ mxci2c_hs_set_div(i2c_hs);
+ reg_write(i2c_hs, HICR, reg_read(i2c_hs, HICR) | HICR_HIEN);
+
+ return 0;
+}
+
+static int mxci2c_hs_disable(mxc_i2c_hs *i2c_hs)
+{
+ reg_write(i2c_hs, HICR, reg_read(i2c_hs, HICR) & (~HICR_HIEN));
+ clk_disable(i2c_hs->ipg_clk);
+ clk_disable(i2c_hs->serial_clk);
+
+ return 0;
+}
+
+static int mxci2c_hs_bus_busy(mxc_i2c_hs *i2c_hs)
+{
+ u16 value;
+ int retry = 1000;
+
+ while (retry--) {
+ value = reg_read(i2c_hs, HISR);
+ if (value & HISR_HIBB) {
+ udelay(1);
+ } else {
+ break;
+ }
+ }
+
+ if (retry <= 0) {
+ dev_dbg(NULL, "%s: Bus Busy!\n", __func__);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int mxci2c_hs_start(mxc_i2c_hs *i2c_hs, int repeat_start, u16 address)
+{
+ u16 mask;
+ int ret = 0;
+
+ mxci2c_hs_bus_busy(i2c_hs);
+
+ /*7 bit address */
+ reg_clear_mask(i2c_hs, HICR, HICR_ADDR_MODE);
+
+ /*send start */
+ if (repeat_start)
+ mask = HICR_RSTA;
+ else
+ mask = HICR_MSTA;
+ reg_set_mask(i2c_hs, HICR, mask);
+
+ return ret;
+}
+
+static int mxci2c_hs_stop(mxc_i2c_hs *i2c_hs)
+{
+ reg_clear_mask(i2c_hs, HICR, HICR_MSTA);
+ reg_clear_mask(i2c_hs, HICR, HICR_HIIEN);
+
+ return 0;
+}
+
+static int mxci2c_wait_writefifo(mxc_i2c_hs *i2c_hs)
+{
+ int i, num, left;
+ int retry, ret = 0;
+
+ retry = 10000;
+ while (retry--) {
+ udelay(10);
+ if (reg_read(i2c_hs, HISR) & (HISR_TDE | HISR_TDC_ZERO)) {
+ if (i2c_hs->index < i2c_hs->msg->len) {
+ left = i2c_hs->msg->len - i2c_hs->index;
+ num =
+ (left >
+ HITFR_MAX_COUNT) ? HITFR_MAX_COUNT : left;
+ for (i = 0; i < num; i++) {
+ reg_write(i2c_hs, HITDR,
+ i2c_hs->msg->buf[i2c_hs->
+ index + i]);
+ }
+ i2c_hs->index += num;
+ } else {
+ if (reg_read(i2c_hs, HISR) & HISR_TDC_ZERO) {
+ msleep(1);
+ break;
+ }
+ }
+ }
+ }
+
+ if (retry <= 0) {
+ printk(KERN_ERR "%s:wait error\n", __func__);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int mxci2c_wait_readfifo(mxc_i2c_hs *i2c_hs)
+{
+ int i, num, left;
+ int retry, ret = 0;
+ u16 value;
+
+ retry = 10000;
+ while (retry--) {
+ udelay(10);
+ value = reg_read(i2c_hs, HISR);
+ if (value & (HISR_RDF | HISR_RDC_ZERO)) {
+ if (i2c_hs->index < i2c_hs->msg->len) {
+ left = i2c_hs->msg->len - i2c_hs->index;
+ num =
+ (left >
+ HITFR_MAX_COUNT) ? HITFR_MAX_COUNT : left;
+ for (i = 0; i < num; i++) {
+ i2c_hs->msg->buf[i2c_hs->index + i] =
+ reg_read(i2c_hs, HIRDR);
+ }
+ i2c_hs->index += num;
+ } else {
+ if (value & HISR_RDC_ZERO) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (retry <= 0) {
+ printk(KERN_ERR "%s:wait error\n", __func__);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int mxci2c_hs_read(mxc_i2c_hs *i2c_hs, int repeat_start,
+ struct i2c_msg *msg)
+{
+ int ret;
+
+ if (msg->len > HIRDCR_MAX_COUNT) {
+ printk(KERN_ERR "%s: error: msg too long, max longth 256\n",
+ __func__);
+ return -1;
+ }
+
+ ret = 0;
+ i2c_hs->msg = msg;
+ i2c_hs->index = 0;
+
+ /*set address */
+ reg_write(i2c_hs, HIMADR, HIMADR_LSB_ADR(msg->addr));
+
+ /*receive mode */
+ reg_clear_mask(i2c_hs, HICR, HICR_MTX);
+
+ reg_clear_mask(i2c_hs, HICR, HICR_HIIEN);
+
+ /*FIFO*/ reg_set_mask(i2c_hs, HIRFR, HIRFR_RFEN | HIRFR_RFWM(7));
+ reg_set_mask(i2c_hs, HIRFR, HIRFR_RFLSH);
+
+ /*TDCR*/
+ reg_write(i2c_hs, HIRDCR, HIRDCR_RDC_EN | HIRDCR_RDC(msg->len));
+
+ mxci2c_hs_start(i2c_hs, repeat_start, msg->addr);
+
+ ret = mxci2c_wait_readfifo(i2c_hs);
+
+ if (ret < 0)
+ return ret;
+ else
+ return msg->len;
+}
+
+static int mxci2c_hs_write(mxc_i2c_hs *i2c_hs, int repeat_start,
+ struct i2c_msg *msg)
+{
+ int ret, i;
+
+ if (msg->len > HITDCR_MAX_COUNT) {
+ printk(KERN_ERR "%s: error: msg too long, max longth 256\n",
+ __func__);
+ return -1;
+ }
+
+ ret = 0;
+ i2c_hs->msg = msg;
+ i2c_hs->index = 0;
+
+ /*set address */
+ reg_write(i2c_hs, HIMADR, HIMADR_LSB_ADR(msg->addr));
+
+ /*transmit mode */
+ reg_set_mask(i2c_hs, HICR, HICR_MTX);
+
+ reg_clear_mask(i2c_hs, HICR, HICR_HIIEN);
+
+ /* TDCR */
+ reg_write(i2c_hs, HITDCR, HITDCR_TDC_EN | HITDCR_TDC(msg->len));
+
+ /* FIFO */
+ reg_set_mask(i2c_hs, HITFR, HITFR_TFEN);
+ reg_set_mask(i2c_hs, HITFR, HITFR_TFLSH);
+
+ if (msg->len > HITFR_MAX_COUNT)
+ i2c_hs->index = HITFR_MAX_COUNT;
+ else {
+ i2c_hs->index = msg->len;
+ }
+
+ for (i = 0; i < i2c_hs->index; i++) {
+ reg_write(i2c_hs, HITDR, msg->buf[i]);
+ }
+
+ mxci2c_hs_start(i2c_hs, repeat_start, msg->addr);
+
+ ret = mxci2c_wait_writefifo(i2c_hs);
+
+ if (ret < 0)
+ return ret;
+ else
+ return msg->len;
+}
+
+static int mxci2c_hs_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ int i;
+ int ret = -EIO;
+
+ mxc_i2c_hs *i2c_hs = (mxc_i2c_hs *) (i2c_get_adapdata(adap));
+
+ if (i2c_hs->low_power) {
+ dev_err(&adap->dev, "I2C Device in low power mode\n");
+ return -EREMOTEIO;
+ }
+
+ if (num < 1) {
+ return 0;
+ }
+
+ mxci2c_hs_enable(i2c_hs);
+
+ for (i = 0; i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD) {
+ ret = mxci2c_hs_read(i2c_hs, 0, &msgs[i]);
+ if (ret < 0)
+ break;
+ } else {
+ ret = mxci2c_hs_write(i2c_hs, 0, &msgs[i]);
+ if (ret < 0)
+ break;
+ }
+ mxci2c_hs_stop(i2c_hs);
+ }
+ mxci2c_hs_stop(i2c_hs);
+
+ mxci2c_hs_disable(i2c_hs);
+
+ if (ret < 0)
+ return ret;
+
+ return i;
+}
+
+static u32 mxci2c_hs_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+/*!
+ * Stores the pointers for the i2c algorithm functions. The algorithm functions
+ * is used by the i2c bus driver to talk to the i2c bus
+ */
+static struct i2c_algorithm mxci2c_hs_algorithm = {
+ .master_xfer = mxci2c_hs_xfer,
+ .functionality = mxci2c_hs_func
+};
+
+static int mxci2c_hs_probe(struct platform_device *pdev)
+{
+ mxc_i2c_hs *i2c_hs;
+ struct mxc_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data;
+ struct resource *res;
+ int id = pdev->id;
+ int ret = 0;
+
+ i2c_hs = kzalloc(sizeof(mxc_i2c_hs), GFP_KERNEL);
+ if (!i2c_hs) {
+ return -ENOMEM;
+ }
+
+ i2c_hs->dev = &pdev->dev;
+
+ i2c_hs->speed = i2c_plat_data->i2c_clk;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ ret = -ENODEV;
+ goto err1;
+ }
+ i2c_hs->reg_base_virt = IO_ADDRESS(res->start);
+ i2c_hs->reg_base_phy = res->start;
+
+ i2c_hs->ipg_clk = clk_get(&pdev->dev, "hsi2c_clk");
+ i2c_hs->serial_clk = clk_get(&pdev->dev, "hsi2c_serial_clk");
+
+ /*
+ * Request the I2C interrupt
+ */
+ i2c_hs->irq = platform_get_irq(pdev, 0);
+ if (i2c_hs->irq < 0) {
+ ret = i2c_hs->irq;
+ goto err1;
+ }
+
+ i2c_hs->low_power = false;
+
+ /*
+ * Set the adapter information
+ */
+ adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+ if (!adap) {
+ ret = -ENODEV;
+ goto err1;
+ }
+ strlcpy(adap->name, pdev->name, 48);
+ adap->id = adap->nr = id;
+ adap->algo = &mxci2c_hs_algorithm;
+ adap->timeout = 1;
+ platform_set_drvdata(pdev, i2c_hs);
+ i2c_set_adapdata(adap, i2c_hs);
+ ret = i2c_add_numbered_adapter(adap);
+ if (ret < 0) {
+ goto err2;
+ }
+
+ printk(KERN_INFO "MXC HS I2C driver\n");
+ return 0;
+
+ err2:
+ kfree(adap);
+ err1:
+ dev_err(&pdev->dev, "failed to probe high speed i2c adapter\n");
+ kfree(i2c_hs);
+ return ret;
+}
+
+static int mxci2c_hs_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ mxc_i2c_hs *i2c_hs = platform_get_drvdata(pdev);
+
+ if (i2c_hs == NULL) {
+ return -1;
+ }
+
+ /* Prevent further calls to be processed */
+ i2c_hs->low_power = true;
+
+ gpio_i2c_hs_inactive();
+
+ return 0;
+}
+
+static int mxci2c_hs_resume(struct platform_device *pdev)
+{
+ mxc_i2c_hs *i2c_hs = platform_get_drvdata(pdev);
+
+ if (i2c_hs == NULL)
+ return -1;
+
+ i2c_hs->low_power = false;
+ gpio_i2c_hs_active();
+
+ return 0;
+}
+
+static int mxci2c_hs_remove(struct platform_device *pdev)
+{
+ mxc_i2c_hs *i2c_hs = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(adap);
+ gpio_i2c_hs_inactive();
+ platform_set_drvdata(pdev, NULL);
+ kfree(i2c_hs);
+ return 0;
+}
+
+static struct platform_driver mxci2c_hs_driver = {
+ .driver = {
+ .name = "mxc_i2c_hs",
+ .owner = THIS_MODULE,
+ },
+ .probe = mxci2c_hs_probe,
+ .remove = mxci2c_hs_remove,
+ .suspend = mxci2c_hs_suspend,
+ .resume = mxci2c_hs_resume,
+};
+
+/*!
+ * Function requests the interrupts and registers the i2c adapter structures.
+ *
+ * @return The function returns 0 on success and a non-zero value on failure.
+ */
+static int __init mxci2c_hs_init(void)
+{
+ /* Register the device driver structure. */
+ return platform_driver_register(&mxci2c_hs_driver);
+}
+
+/*!
+ * This function is used to cleanup all resources before the driver exits.
+ */
+static void __exit mxci2c_hs_exit(void)
+{
+ platform_driver_unregister(&mxci2c_hs_driver);
+}
+
+subsys_initcall(mxci2c_hs_init);
+module_exit(mxci2c_hs_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC HIGH SPEED I2C driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/mxc_i2c_hs_reg.h b/drivers/i2c/busses/mxc_i2c_hs_reg.h
new file mode 100644
index 000000000000..fe6bb9a8f1ff
--- /dev/null
+++ b/drivers/i2c/busses/mxc_i2c_hs_reg.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MXC_I2C_HS_REG_H__
+#define __MXC_I2C_HS_REG_H__
+
+#define HISADR 0x00
+
+#define HIMADR 0x04
+#define HIMADR_LSB_ADR(x) ((x) << 1)
+#define HIMADR_MSB_ADR(x) (((x) & 0x7) << 8)
+
+#define HICR 0x08
+#define HICR_HIEN 0x1
+#define HICR_DMA_EN_RX 0x2
+#define HICR_DMA_EN_TR 0x4
+#define HICR_RSTA 0x8
+#define HICR_TXAK 0x10
+#define HICR_MTX 0x20
+#define HICR_MSTA 0x40
+#define HICR_HIIEN 0x80
+#define HICR_ADDR_MODE 0x100
+#define HICR_MST_CODE(x) (((x)&0x7) << 9)
+#define HICR_HSM_EN 0x1000
+#define HICR_SAMC(x) (((x)&0x3) << 13)
+#define SAMC_7_10 0
+#define SMAC_7 1
+#define SMAC_10 2
+
+#define HISR 0x0c
+#define HISR_RDF 0x1
+#define HISR_TDE 0x2
+#define HISR_HIAAS 0x4
+#define HISR_HIAL 0x8
+#define HISR_BTD 0x10
+#define HISR_RDC_ZERO 0x20
+#define HISR_TDC_ZERO 0x40
+#define HISR_RXAK 0x80
+#define HISR_HIBB 0x100
+#define HISR_SRW 0x200
+#define HISR_SADDR_MODE 0x400
+#define HISR_SHS_MODE 0x800
+
+#define HIIMR 0x10
+#define HIIMR_RDF 0x1
+#define HIIMR_TDE 0x2
+#define HIIMR_AAS 0x4
+#define HIIMR_AL 0x8
+#define HIIMR_BTD 0x10
+#define HIIMR_RDC 0x20
+#define HIIMR_TDC 0x40
+#define HIIMR_RXAK 0x80
+
+#define HITDR 0x14
+
+#define HIRDR 0x18
+
+#define HIFSFDR 0x1c
+
+#define HIHSFDR 0x20
+
+#define HITFR 0x24
+#define HITFR_TFEN 0x1
+#define HITFR_TFLSH 0x2
+#define HITFR_TFWM(x) (((x) & 0x7) << 2)
+#define HITFR_TFC(x) (((x) >> 8) & 0xF)
+#define HITFR_MAX_COUNT 8
+
+#define HIRFR 0x28
+#define HIRFR_RFEN 0x1
+#define HIRFR_RFLSH 0x2
+#define HIRFR_RFWM(x) (((x) & 0x7) << 2)
+#define HIRFR_RFC(x) (((x) >> 8) & 0xF)
+#define HIRFR_MAX_COUNT 8
+
+#define HITDCR 0x2c
+#define HITDCR_TDC(x) ((x) & 0xFF)
+#define HITDCR_TDC_EN 0x100
+#define HITDCR_TDC_RSTA 0x200
+#define HITDCR_MAX_COUNT 0xFF
+
+#define HIRDCR 0x30
+#define HIRDCR_RDC(x) ((x) & 0xFF)
+#define HIRDCR_RDC_EN 0x100
+#define HIRDCR_RDC_RSTA 0x200
+#define HIRDCR_MAX_COUNT 0xFF
+
+#endif
diff --git a/drivers/i2c/busses/mxc_i2c_reg.h b/drivers/i2c/busses/mxc_i2c_reg.h
new file mode 100644
index 000000000000..4fc76c8aa811
--- /dev/null
+++ b/drivers/i2c/busses/mxc_i2c_reg.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MXC_I2C_REG_H__
+#define __MXC_I2C_REG_H__
+
+/* Address offsets of the I2C registers */
+#define MXC_IADR 0x00 /* Address Register */
+#define MXC_IFDR 0x04 /* Freq div register */
+#define MXC_I2CR 0x08 /* Control regsiter */
+#define MXC_I2SR 0x0C /* Status register */
+#define MXC_I2DR 0x10 /* Data I/O register */
+
+/* Bit definitions of I2CR */
+#define MXC_I2CR_IEN 0x0080
+#define MXC_I2CR_IIEN 0x0040
+#define MXC_I2CR_MSTA 0x0020
+#define MXC_I2CR_MTX 0x0010
+#define MXC_I2CR_TXAK 0x0008
+#define MXC_I2CR_RSTA 0x0004
+
+/* Bit definitions of I2SR */
+#define MXC_I2SR_ICF 0x0080
+#define MXC_I2SR_IAAS 0x0040
+#define MXC_I2SR_IBB 0x0020
+#define MXC_I2SR_IAL 0x0010
+#define MXC_I2SR_SRW 0x0004
+#define MXC_I2SR_IIF 0x0002
+#define MXC_I2SR_RXAK 0x0001
+
+#endif /* __MXC_I2C_REG_H__ */
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index e6857e01d1ba..79d216fa3919 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -749,6 +749,13 @@ config BLK_DEV_IDE_RAPIDE
Say Y here if you want to support the Yellowstone RapIDE controller
manufactured for use with Acorn computers.
+config BLK_DEV_IDE_S3C2443
+ tristate "S3C2443 ATA driver support"
+ depends on ARM && CPU_S3C2443
+ help
+ Say Y here if you want to support the onboard IDE interface on
+ S3C2443 boards
+
config IDE_H8300
tristate "H8300 IDE support"
depends on H8300
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index 7818d402b188..67be799e6c80 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -107,6 +107,7 @@ obj-$(CONFIG_BLK_DEV_PLATFORM) += ide_platform.o
obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o
obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o
+obj-$(CONFIG_BLK_DEV_IDE_S3C2443) += s3c2443-ide.o
obj-$(CONFIG_BLK_DEV_PALMCHIP_BK3710) += palm_bk3710.o
obj-$(CONFIG_BLK_DEV_IDE_AU1XXX) += au1xxx-ide.o
diff --git a/drivers/ide/s3c2443-ide.c b/drivers/ide/s3c2443-ide.c
new file mode 100644
index 000000000000..4de1a537fb4e
--- /dev/null
+++ b/drivers/ide/s3c2443-ide.c
@@ -0,0 +1,477 @@
+/* linux/drivers/ide/s3c2443-ide.c
+ *
+ * Copyright (c) 2009 Digi Internationa Inc.
+ * http://www.digi.com
+ *
+ * Partially based on previous driver by Seung-Chull, Suh <sc.suh@samsung.com>
+ * Copyright(C) Samsung Electronics 2006
+ *
+ * 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>
+#include <linux/errno.h>
+#include <linux/ide.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <mach/map.h>
+#include <mach/gpio.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-gpioj.h>
+#include <mach/regs-bus.h>
+#include <mach/regs-cfata.h>
+#include <mach/gpio.h>
+
+#define DRV_NAME "s3c2443-ide"
+
+MODULE_AUTHOR("Pedro Perez de Heredia <pedro.perez@digi.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("S3C2443 IDE driver");
+
+struct _s3c2443_ide_hwif {
+ ide_hwif_t *hwif;
+ int irq;
+ void __iomem *membase;
+ struct platform_device *dev;
+ struct clk *clk;
+};
+
+static struct _s3c2443_ide_hwif s3c2443_ide_hwif;
+
+static inline void s3c2443_wait_until_ready(void)
+{
+ u32 i, reg;
+
+ for (i = 0; i < 100000; i++) {
+ reg = readl(s3c2443_ide_hwif.membase + S3C2443_ATA_FIFO_STATUS);
+ if ((reg >> 28) == 0)
+ return;
+ }
+}
+
+static __inline__ u8 s3c2443_ide_readb(unsigned long port)
+{
+ s3c2443_wait_until_ready();
+ (void)readb(port);
+ s3c2443_wait_until_ready();
+ return readb(s3c2443_ide_hwif.membase + S3C2443_ATA_PIO_RDATA);
+}
+
+static __inline__ u16 s3c2443_ide_readw(unsigned long port)
+{
+ s3c2443_wait_until_ready();
+ (void)readw(port);
+ s3c2443_wait_until_ready();
+ return readw(s3c2443_ide_hwif.membase + S3C2443_ATA_PIO_RDATA);
+}
+
+static __inline__ void s3c2443_ide_writeb(u8 val, unsigned long port)
+{
+ s3c2443_wait_until_ready();
+ writeb(val, port);
+}
+
+static __inline__ void s3c2443_ide_writew(u16 val, unsigned long port)
+{
+ s3c2443_wait_until_ready();
+ writew(val, port);
+}
+
+static void s3c2443_ide_exec_command(ide_hwif_t *hwif, u8 cmd)
+{
+ s3c2443_ide_writeb(cmd, hwif->io_ports.command_addr);
+}
+
+static u8 s3c2443_read_status(ide_hwif_t *hwif)
+{
+ return s3c2443_ide_readb(hwif->io_ports.status_addr);
+}
+
+static u8 s3c2443_read_altstatus(ide_hwif_t *hwif)
+{
+ return s3c2443_ide_readb(hwif->io_ports.ctl_addr);
+}
+
+static void s3c2443_set_irq(ide_hwif_t *hwif, int on)
+{
+ u8 ctl = ATA_DEVCTL_OBS;
+
+ if (on == 4) { /* hack for SRST */
+ ctl |= 4;
+ on &= ~4;
+ }
+
+ ctl |= on ? 0 : 2;
+ s3c2443_ide_writeb(ctl, hwif->io_ports.ctl_addr);
+}
+
+static void s3c2443_tf_load(ide_drive_t *drive, ide_task_t *task)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ struct ide_taskfile *tf = &task->tf;
+ u8 HIHI = (task->tf_flags & IDE_TFLAG_LBA48) ? 0xE0 : 0xEF;
+
+ if (task->tf_flags & IDE_TFLAG_FLAGGED)
+ HIHI = 0xFF;
+
+ if (task->tf_flags & IDE_TFLAG_OUT_DATA) {
+ u16 data = (tf->hob_data << 8) | tf->data;
+ s3c2443_ide_writew (data, io_ports->data_addr);
+ }
+
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE)
+ s3c2443_ide_writeb(tf->hob_feature, io_ports->feature_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_NSECT)
+ s3c2443_ide_writeb(tf->hob_nsect, io_ports->nsect_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAL)
+ s3c2443_ide_writeb(tf->hob_lbal, io_ports->lbal_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAM)
+ s3c2443_ide_writeb(tf->hob_lbam, io_ports->lbam_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAH)
+ s3c2443_ide_writeb(tf->hob_lbah, io_ports->lbah_addr);
+
+ if (task->tf_flags & IDE_TFLAG_OUT_FEATURE)
+ s3c2443_ide_writeb(tf->feature, io_ports->feature_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_NSECT)
+ s3c2443_ide_writeb(tf->nsect, io_ports->nsect_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_LBAL)
+ s3c2443_ide_writeb(tf->lbal, io_ports->lbal_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_LBAM)
+ s3c2443_ide_writeb(tf->lbam, io_ports->lbam_addr);
+ if (task->tf_flags & IDE_TFLAG_OUT_LBAH)
+ s3c2443_ide_writeb(tf->lbah, io_ports->lbah_addr);
+
+ if (task->tf_flags & IDE_TFLAG_OUT_DEVICE)
+ s3c2443_ide_writeb((tf->device & HIHI) | drive->select,
+ io_ports->device_addr);
+}
+
+static void s3c2443_tf_read(ide_drive_t *drive, ide_task_t *task)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ struct ide_taskfile *tf = &task->tf;
+
+ if (task->tf_flags & IDE_TFLAG_IN_DATA) {
+ u16 data;
+
+ data = s3c2443_ide_readw(io_ports->data_addr);
+
+ tf->data = data & 0xff;
+ tf->hob_data = (data >> 8) & 0xff;
+ }
+
+ /* be sure we're looking at the low order bits */
+ s3c2443_ide_writeb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr);
+
+ if (task->tf_flags & IDE_TFLAG_IN_FEATURE)
+ tf->feature = s3c2443_ide_readb(io_ports->feature_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_NSECT)
+ tf->nsect = s3c2443_ide_readb(io_ports->nsect_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_LBAL)
+ tf->lbal = s3c2443_ide_readb(io_ports->lbal_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_LBAM)
+ tf->lbam = s3c2443_ide_readb(io_ports->lbam_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_LBAH)
+ tf->lbah = s3c2443_ide_readb(io_ports->lbah_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_DEVICE)
+ tf->device = s3c2443_ide_readb(io_ports->device_addr);
+
+ if (task->tf_flags & IDE_TFLAG_LBA48) {
+ s3c2443_ide_writeb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr);
+
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE)
+ tf->hob_feature = s3c2443_ide_readb(io_ports->feature_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_NSECT)
+ tf->hob_nsect = s3c2443_ide_readb(io_ports->nsect_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAL)
+ tf->hob_lbal = s3c2443_ide_readb(io_ports->lbal_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAM)
+ tf->hob_lbam = s3c2443_ide_readb(io_ports->lbam_addr);
+ if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAH)
+ tf->hob_lbah = s3c2443_ide_readb(io_ports->lbah_addr);
+ }
+}
+
+static void s3c2443_input_data(ide_drive_t *drive, struct request *rq, void *buf,
+ unsigned int len)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ u16 *buffer = (u16 *)buf;
+
+ /* round up, it should be power of 2 */
+ len = (len + 1) >> 1;
+
+ while (len--) {
+ *buffer++ = s3c2443_ide_readw(io_ports->data_addr);
+ }
+}
+
+static void s3c2443_output_data(ide_drive_t *drive, struct request *rq, void *buf,
+ unsigned int len)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ u16 *buffer = (u16 *)buf;
+
+ len >>= 1;
+
+ while (len--) {
+ s3c2443_ide_writew(*buffer++, io_ports->data_addr);
+ }
+}
+
+static const struct ide_tp_ops s3c2443_tp_ops = {
+ .exec_command = s3c2443_ide_exec_command,
+ .read_status = s3c2443_read_status,
+ .read_altstatus = s3c2443_read_altstatus,
+ .set_irq = s3c2443_set_irq,
+ .tf_load = s3c2443_tf_load,
+ .tf_read = s3c2443_tf_read,
+ .input_data = s3c2443_input_data,
+ .output_data = s3c2443_output_data,
+};
+
+static const struct ide_port_info s3c2443_port_info = {
+ .tp_ops = &s3c2443_tp_ops,
+ .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA | \
+ IDE_HFLAG_NO_IO_32BIT,
+};
+
+static int s3c2443_ide_ack_intr(ide_hwif_t *hwif)
+{
+ u32 reg = 0, tout = 100;
+
+ while (tout--) {
+ reg = readl(s3c2443_ide_hwif.membase + S3C2443_ATA_IRQ);
+ if (reg) {
+ writel(reg, s3c2443_ide_hwif.membase + S3C2443_ATA_IRQ);
+ break;
+ }
+ }
+
+ return (int)reg;
+}
+
+static int s3c2443_ide_hw_config(struct _s3c2443_ide_hwif *hw)
+{
+ int ret = 0;
+ u32 reg;
+ void __iomem *ebicon;
+
+ ebicon = ioremap(S3C2443_PA_EBI + 0x8, 0x4);
+ if (ebicon == NULL) {
+ ret =-EBUSY;
+ goto err_unmap;
+ }
+
+ /* Configure EBI bank 2 and 3 for the CF interface */
+ reg = readl(ebicon);
+ reg |= S3C2443_EBICON_BANK3_CFG | S3C2443_EBICON_BANK2_CFG;
+ writel(reg, ebicon);
+
+ /* Configure the IO lines*/
+ s3c2443_gpio_cfgpin(S3C2410_GPG15, S3C2443_GPG15_CF_PWR);
+ s3c2443_gpio_cfgpin(S3C2410_GPG14, S3C2443_GPG14_CF_RESET);
+ s3c2443_gpio_cfgpin(S3C2410_GPG13, S3C2443_GPG13_CF_nREG);
+ s3c2443_gpio_cfgpin(S3C2410_GPG12, S3C2443_GPG12_nINPACK);
+ s3c2443_gpio_cfgpin(S3C2410_GPG11, S3C2443_GPG11_CF_nIREQ);
+
+ s3c2443_gpio_cfgpin(S3C2410_GPA10, 0);
+ s3c2443_gpio_cfgpin(S3C2410_GPA11, 1);
+ s3c2443_gpio_cfgpin(S3C2410_GPA12, 1);
+ s3c2443_gpio_cfgpin(S3C2410_GPA15, 1);
+
+ /* Clear the card detect condition */
+ reg = readl(S3C24XX_MISCCR) & ~S3C2443_MISCCR_nCD_CF;
+ writel(reg, S3C24XX_MISCCR);
+
+ /* Output Port disabled, Card power off, ATA mode */
+ writel(0x07, hw->membase + S3C2443_MUX_REG);
+ mdelay(10);
+ /* Output Port enable, Card power off, ATA mode */
+ writel(0x03, hw->membase + S3C2443_MUX_REG);
+ mdelay(10);
+ /* Output Port enable, Card power on, ATA mode */
+ writel(0x01, hw->membase + S3C2443_MUX_REG);
+ mdelay(500); /* wait for 500ms */
+
+ writel(0x1C238, hw->membase + S3C2443_ATA_PIO_TIME);
+ writel(0x20B1362, hw->membase + S3C2443_ATA_UDMA_TIME);
+
+ /* Enable ATA */
+ reg = readl(hw->membase + S3C2443_ATA_CONTROL);
+ writel(reg | 0x1, hw->membase + S3C2443_ATA_CONTROL);
+
+ mdelay(200);
+ /* remove IRQ Status and enable only ATA device interrupt */
+ writel(0x1f, hw->membase + S3C2443_ATA_IRQ);
+ writel(0x1b, hw->membase + S3C2443_ATA_IRQ_MASK);
+
+ mdelay(200);
+
+err_unmap:
+ iounmap(ebicon);
+ return ret;
+}
+
+
+static int __devinit s3c2443_ide_probe(struct platform_device *pdev)
+{
+ int ret = 0, i;
+ struct _s3c2443_ide_hwif *shwif = &s3c2443_ide_hwif;
+ hw_regs_t hw, *hws[] = {&hw, NULL, NULL, NULL};
+ struct resource *res;
+ struct ide_host *host;
+
+ memset(&s3c2443_ide_hwif, 0, sizeof(struct _s3c2443_ide_hwif));
+
+ shwif->dev = pdev;
+
+ /* find and map our resources */
+ shwif->irq = platform_get_irq(pdev, 0);
+ if (shwif->irq < 0) {
+ pr_debug("%s: %s, can not get IORESOURCE_IRQ\n", DRV_NAME, __func__);
+ ret =-ENOENT;
+ goto error;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ pr_debug("%s: %s, can not get IORESOURCE_MEM\n", DRV_NAME, __func__);
+ ret = -ENOENT;
+ goto error;
+ }
+
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name)) {
+ pr_debug("%s: %s,request_mem_region failed\n", DRV_NAME, __func__);
+ ret = -EBUSY;
+ goto error;
+ }
+
+ shwif->membase = ioremap(res->start, res->end - res->start + 1);
+ if (shwif->membase == NULL) {
+ pr_debug("%s: %s, can not map IO space at 0x%08x\n", DRV_NAME, __func__, res->start);
+ ret = -ENOMEM;
+ goto error_map;
+ }
+
+ shwif->clk = clk_get(&pdev->dev, "cfc");
+ if (IS_ERR(shwif->clk)) {
+ pr_debug("%s: %s, failed to find clock source\n", DRV_NAME, __func__);
+ ret = PTR_ERR(shwif->clk);
+ goto error_clock;
+ }
+
+ if ((ret = clk_enable(shwif->clk))) {
+ pr_debug("%s: %s, failed to enable clock\n", DRV_NAME, __func__);
+ goto error_clk_en;
+ }
+
+ ret = s3c2443_ide_hw_config(shwif);
+ if (ret) {
+ pr_debug("%s: %s, failed to configure IDE hardware\n", DRV_NAME, __func__);
+ goto error_hwcfg;
+ }
+
+ memset(&hw, 0, sizeof(hw));
+
+ for (i = 0; i < 9; i++)
+ hw.io_ports_array[i] = (unsigned long)(shwif->membase + S3C2443_ATA_PIO_DTR + (i * 4));
+ hw.io_ports.ctl_addr = (unsigned long)(shwif->membase + S3C2443_ATA_PIO_DTR + 8 * 4);
+ hw.irq = shwif->irq;
+ hw.ack_intr = s3c2443_ide_ack_intr;
+ hw.chipset = ide_generic;
+ hw.dev = &pdev->dev;
+
+ ret = ide_host_add(&s3c2443_port_info, hws, &host);
+ if (ret) {
+ pr_debug("%s: %s, failed add IDE host device\n", DRV_NAME, __func__);
+ goto error_add;
+ }
+
+ shwif->hwif = host->ports[0];
+ platform_set_drvdata(pdev, host);
+
+ printk(KERN_INFO "S3C2443 IDE driver\n");
+ return 0;
+
+error_add:
+ /* XXX unconfig hardware here ? */
+error_hwcfg:
+ clk_disable(shwif->clk);
+error_clk_en:
+ clk_put(shwif->clk);
+error_clock:
+ iounmap(shwif->membase);
+error_map:
+ release_mem_region(res->start, res->end - res->start + 1);
+error:
+ return ret;
+}
+
+static int __devexit s3c2443_ide_remove(struct platform_device *pdev)
+{
+ struct ide_host *host = dev_get_drvdata(&pdev->dev);
+ struct _s3c2443_ide_hwif *shwif = &s3c2443_ide_hwif;
+ struct resource *res;
+
+ ide_host_remove(host);
+
+ clk_disable(shwif->clk);
+ clk_put(shwif->clk);
+
+ iounmap(shwif->membase);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res != NULL)
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c2443_ide_resume(struct platform_device *dev)
+{
+ struct _s3c2443_ide_hwif *shwif = &s3c2443_ide_hwif;
+
+ return s3c2443_ide_hw_config(shwif);
+}
+#else
+# define s3c2443_ide_suspend NULL
+# define s3c2443_ide_resume NULL
+#endif
+
+
+static struct platform_driver s3c2443_ide_driver = {
+ .probe = s3c2443_ide_probe,
+ .remove = __devexit_p(s3c2443_ide_remove),
+ .resume = s3c2443_ide_resume,
+ .driver = {
+ .name = "s3c2443-ide",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s3c2443_ide_init(void)
+{
+ return platform_driver_register(&s3c2443_ide_driver);
+}
+
+static void __exit s3c2443_ide_exit(void)
+{
+ platform_driver_unregister(&s3c2443_ide_driver);
+}
+
+module_init(s3c2443_ide_init);
+module_exit(s3c2443_ide_exit);
+
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index efd70a974591..1db764a066ee 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -268,6 +268,13 @@ config KEYBOARD_PXA27x
To compile this driver as a module, choose M here: the
module will be called pxa27x_keypad.
+config KEYBOARD_MXC
+ tristate "MXC Keypad Driver"
+ depends on ARCH_MXC
+ help
+ This is the Keypad driver for the Freescale MXC application
+ processors.
+
config KEYBOARD_AAED2000
tristate "AAED-2000 keyboard"
depends on MACH_AAED2000
@@ -314,6 +321,13 @@ config KEYBOARD_BFIN
To compile this driver as a module, choose M here: the
module will be called bf54x-keys.
+config KEYBOARD_MPR084
+ tristate "Freescale MPR084 Touch Keypad Driver"
+ depends on ARCH_MX37
+ help
+ This is the Keypad driver for the Freescale Proximity Capacitive
+ Touch Sensor controller chip.
+
config KEYBOARD_SH_KEYSC
tristate "SuperH KEYSC keypad support"
depends on SUPERH
@@ -321,6 +335,21 @@ config KEYBOARD_SH_KEYSC
Say Y here if you want to use a keypad attached to the KEYSC block
on SuperH processors such as sh7722 and sh7343.
+
To compile this driver as a module, choose M here: the
module will be called sh_keysc.
+
+config KEYBOARD_STMP3XXX
+ tristate "STMP3xxx keyboard"
+ depends on ARCH_STMP3XXX
+ help
+ -to be written-
+
+
+config KEYBOARD_MC9S08DZ60
+ tristate "mc9s08dz60 keyboard"
+ depends on MXC_PMIC_MC9S08DZ60
+ help
+ -to be written-
+
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 0edc8f285d1c..c877b8bcfbea 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -18,6 +18,9 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+obj-$(CONFIG_KEYBOARD_MXC) += mxc_keyb.o
+obj-$(CONFIG_KEYBOARD_MPR084) += mpr084.o
+obj-$(CONFIG_KEYBOARD_STMP3XXX) += stmp3xxx-kbd.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
@@ -27,3 +30,4 @@ obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
+obj-$(CONFIG_KEYBOARD_MC9S08DZ60) += mc9s08dz60_keyb.o
diff --git a/drivers/input/keyboard/mc9s08dz60_keyb.c b/drivers/input/keyboard/mc9s08dz60_keyb.c
new file mode 100644
index 000000000000..8ec9f646c7c7
--- /dev/null
+++ b/drivers/input/keyboard/mc9s08dz60_keyb.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc9s08dz60keyb.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC keypad port.
+ *
+ * The keypad driver is designed as a standard Input driver which interacts
+ * with low level keypad port hardware. Upon opening, the Keypad driver
+ * initializes the keypad port. When the keypad interrupt happens the driver
+ * calles keypad polling timer and scans the keypad matrix for key
+ * press/release. If all key press/release happened it comes out of timer and
+ * waits for key press interrupt. The scancode for key press and release events
+ * are passed to Input subsytem.
+ *
+ * @ingroup keypad
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <linux/kd.h>
+#include <linux/fs.h>
+#include <linux/kbd_kern.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/input.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include <asm/mach/keypad.h>
+
+#define MOD_NAME "mc9s08dz60-keyb"
+/*
+ * Module header file
+ */
+
+/*! Input device structure. */
+static struct input_dev *mc9s08dz60kbd_dev;
+static unsigned int key_status;
+static int keypad_irq;
+static unsigned int key_code_map[8] = {
+ KEY_LEFT,
+ KEY_DOWN,
+ 0,
+ 0,
+ KEY_UP,
+ KEY_RIGHT,
+ 0,
+ 0,
+};
+static unsigned int keycodes_size = 8;
+
+static void read_key_handler(struct work_struct *work);
+static DECLARE_WORK(key_pad_event, read_key_handler);
+
+
+static void read_key_handler(struct work_struct *work)
+{
+ unsigned int val1, val2;
+ int pre_val, curr_val, i;
+ val1 = val2 = 0xff;
+ mcu_pmic_read_reg(REG_MCU_KPD_1, &val1, 0xff);
+ mcu_pmic_read_reg(REG_MCU_KPD_2, &val2, 0xff);
+ pr_debug("key pressed, 0x%02x%02x\n", val2, val1);
+ for (i = 0; i < 8; i++) {
+ curr_val = (val1 >> i) & 0x1;
+ if (curr_val > 0)
+ input_event(mc9s08dz60kbd_dev, EV_KEY,
+ key_code_map[i], 1);
+ else {
+ pre_val = (key_status >> i) & 0x1;
+ if (pre_val > 0)
+ input_event(mc9s08dz60kbd_dev, EV_KEY,
+ key_code_map[i], 0);
+ }
+ }
+ key_status = val1;
+
+}
+
+static irqreturn_t mc9s08dz60kpp_interrupt(int irq, void *dev_id)
+{
+ schedule_work(&key_pad_event);
+ return IRQ_RETVAL(1);
+}
+
+/*!
+ * This function is called when the keypad driver is opened.
+ * Since keypad initialization is done in __init, nothing is done in open.
+ *
+ * @param dev Pointer to device inode
+ *
+ * @result The function always return 0
+ */
+static int mc9s08dz60kpp_open(struct input_dev *dev)
+{
+ return 0;
+}
+
+/*!
+ * This function is called close the keypad device.
+ * Nothing is done in this function, since every thing is taken care in
+ * __exit function.
+ *
+ * @param dev Pointer to device inode
+ *
+ */
+static void mc9s08dz60kpp_close(struct input_dev *dev)
+{
+}
+
+
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions.
+ *
+ * @return The function returns 0 on successful registration. Otherwise returns
+ * specific error code.
+ */
+static int mc9s08dz60kpp_probe(struct platform_device *pdev)
+{
+ int i, irq;
+ int retval;
+
+ retval = mcu_pmic_write_reg(REG_MCU_KPD_CONTROL, 0x1, 0x1);
+ if (retval != 0) {
+ pr_info("mc9s08dz60 keypad: mcu not detected!\n");
+ return retval;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ retval = request_irq(irq, mc9s08dz60kpp_interrupt,
+ 0, MOD_NAME, MOD_NAME);
+ if (retval) {
+ pr_debug("KPP: request_irq(%d) returned error %d\n",
+ irq, retval);
+ return retval;
+ }
+ keypad_irq = irq;
+
+ mc9s08dz60kbd_dev = input_allocate_device();
+ if (!mc9s08dz60kbd_dev) {
+ pr_info(KERN_ERR "mc9s08dz60kbd_dev: \
+ not enough memory for input device\n");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ mc9s08dz60kbd_dev->name = "mc9s08dz60kpd";
+ mc9s08dz60kbd_dev->id.bustype = BUS_HOST;
+ mc9s08dz60kbd_dev->open = mc9s08dz60kpp_open;
+ mc9s08dz60kbd_dev->close = mc9s08dz60kpp_close;
+
+ retval = input_register_device(mc9s08dz60kbd_dev);
+ if (retval < 0) {
+ pr_info(KERN_ERR
+ "mc9s08dz60kbd_dev: failed to register input device\n");
+ goto err2;
+ }
+
+ __set_bit(EV_KEY, mc9s08dz60kbd_dev->evbit);
+
+ for (i = 0; i < keycodes_size; i++)
+ __set_bit(key_code_map[i], mc9s08dz60kbd_dev->keybit);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ pr_info("mc9s08dz60 keypad probed\n");
+
+ return 0;
+
+err2:
+ input_free_device(mc9s08dz60kbd_dev);
+err1:
+ free_irq(irq, MOD_NAME);
+ return retval;
+}
+
+/*!
+ * Dissociates the driver from the kpp device.
+ *
+ * @param pdev the device structure used to give information on which SDHC
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mc9s08dz60kpp_remove(struct platform_device *pdev)
+{
+ free_irq(keypad_irq, MOD_NAME);
+ input_unregister_device(mc9s08dz60kbd_dev);
+
+ if (mc9s08dz60kbd_dev)
+ input_free_device(mc9s08dz60kbd_dev);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mc9s08dz60kpd_driver = {
+ .driver = {
+ .name = "mc9s08dz60keypad",
+ .bus = &platform_bus_type,
+ },
+ .probe = mc9s08dz60kpp_probe,
+ .remove = mc9s08dz60kpp_remove
+};
+
+static int __init mc9s08dz60kpp_init(void)
+{
+ pr_info(KERN_INFO "mc9s08dz60 keypad loaded\n");
+ platform_driver_register(&mc9s08dz60kpd_driver);
+ return 0;
+}
+
+static void __exit mc9s08dz60kpp_cleanup(void)
+{
+ platform_driver_unregister(&mc9s08dz60kpd_driver);
+}
+
+module_init(mc9s08dz60kpp_init);
+module_exit(mc9s08dz60kpp_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Keypad Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/mpr084.c b/drivers/input/keyboard/mpr084.c
new file mode 100644
index 000000000000..0c0c5d8709e7
--- /dev/null
+++ b/drivers/input/keyboard/mpr084.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file linux/drivers/input/keyboard/mpr084.c
+ *
+ * @brief Driver for the Freescale MPR084 I2C Touch Sensor KeyPad module.
+ *
+ *
+ *
+ * @ingroup Keypad
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/bcd.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <asm/mach/irq.h>
+#include <mach/gpio.h>
+
+/*
+ * Definitions
+ */
+#define DEBUG 0
+
+#define KEY_COUNT 8
+
+/*
+ *Registers in MPR084
+ */
+#define MPR084_FIFO_ADDR 0x00
+#define MPR084_FAULT_ADDR 0x01
+#define MPR084_TPS_ADDR 0x02
+#define MPR084_TPC_ADDR 0x03
+#define MPR084_STR1_ADDR 0x04
+#define MPR084_STR2_ADDR 0x05
+#define MPR084_STR3_ADDR 0x06
+#define MPR084_STR4_ADDR 0x07
+#define MPR084_STR5_ADDR 0x08
+#define MPR084_STR6_ADDR 0x09
+#define MPR084_STR7_ADDR 0x0A
+#define MPR084_STR8_ADDR 0x0B
+#define MPR084_ECEM_ADDR 0x0C
+#define MPR084_MNTP_ADDR 0x0D
+#define MPR084_MTC_ADDR 0x0E
+#define MPR084_TASP_ADDR 0x0F
+#define MPR084_SC_ADDR 0x10
+#define MPR084_LPC_ADDR 0x11
+#define MPR084_SKT_ADDR 0x12
+#define MPR084_CONFIG_ADDR 0x13
+#define MPR084_SI_ADDR 0x14
+#define MPR084_ADDR_MINI MPR084_FIFO_ADDR
+#define MPR084_ADDR_MAX MPR084_SI_ADDR
+
+/* FIFO registers */
+#define MPR084_FIFO_MORE_DATA_FLAG 0x80
+#define MPR084_FIFO_NO_DATA_FLAG 0x40
+#define MPR084_FIFO_OVERFLOW_FLAG 0x20
+#define MPR084_FIFO_PAD_IS_TOUCHED 0x10
+#define MPR084_FIFO_POSITION_MASK 0x0F
+
+#define DRIVER_NAME "mpr084"
+
+struct mpr084_data {
+ struct i2c_client *client;
+ struct device_driver driver;
+ struct input_dev *idev;
+ struct task_struct *tstask;
+ struct completion kpirq_completion;
+ int kpirq;
+ int kp_thread_cnt;
+ int opened;
+};
+
+static int kpstatus[KEY_COUNT];
+static struct mxc_keyp_platform_data *keypad;
+static const unsigned short *mxckpd_keycodes;
+static struct regulator *vdd_reg;
+
+static int mpr084_read_register(struct mpr084_data *data,
+ unsigned char regaddr, int *value)
+{
+ int ret = 0;
+ unsigned char regvalue;
+
+ ret = i2c_master_send(data->client, &regaddr, 1);
+ if (ret < 0)
+ goto err;
+ udelay(20);
+ ret = i2c_master_recv(data->client, &regvalue, 1);
+ if (ret < 0)
+ goto err;
+ *value = regvalue;
+
+ return ret;
+err:
+ return -ENODEV;
+}
+
+static int mpr084_write_register(struct mpr084_data *data,
+ u8 regaddr, u8 regvalue)
+{
+ int ret = 0;
+ unsigned char msgbuf[2];
+
+ msgbuf[0] = regaddr;
+ msgbuf[1] = regvalue;
+ ret = i2c_master_send(data->client, msgbuf, 2);
+ if (ret < 0) {
+ printk(KERN_ERR "%s - Error in writing to I2C Register %d \n",
+ __func__, regaddr);
+ return ret;
+ }
+
+ return ret;
+}
+
+
+static irqreturn_t mpr084_keypadirq(int irq, void *v)
+{
+ struct mpr084_data *d = v;
+
+ disable_irq(d->kpirq);
+ complete(&d->kpirq_completion);
+ return IRQ_HANDLED;
+}
+
+static int mpr084ts_thread(void *v)
+{
+ struct mpr084_data *d = v;
+ int ret = 0, fifo = 0;
+ int index = 0, currentstatus = 0;
+
+ if (d->kp_thread_cnt)
+ return -EINVAL;
+ d->kp_thread_cnt = 1;
+ while (1) {
+
+ if (kthread_should_stop())
+ break;
+ /* Wait for keypad interrupt */
+ if (wait_for_completion_interruptible_timeout
+ (&d->kpirq_completion, HZ) <= 0)
+ continue;
+
+ ret = mpr084_read_register(d, MPR084_FIFO_ADDR, &fifo);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "%s: Err in reading keypad FIFO register \n\n",
+ __func__);
+ } else {
+ if (fifo & MPR084_FIFO_OVERFLOW_FLAG)
+ printk(KERN_ERR
+ "%s: FIFO overflow \n\n", __func__);
+ while (!(fifo & MPR084_FIFO_NO_DATA_FLAG)) {
+ index = fifo & MPR084_FIFO_POSITION_MASK;
+ currentstatus =
+ fifo & MPR084_FIFO_PAD_IS_TOUCHED;
+ /*Scan key map for changes */
+ if ((currentstatus) ^ (kpstatus[index])) {
+ if (!(currentstatus)) {
+ /*Key released. */
+ input_event(d->idev, EV_KEY,
+ mxckpd_keycodes
+ [index], 0);
+ } else {
+ /* Key pressed. */
+ input_event(d->idev, EV_KEY,
+ mxckpd_keycodes
+ [index], 1);
+ }
+ /*Store current keypad status */
+ kpstatus[index] = currentstatus;
+ }
+ mpr084_read_register(d, MPR084_FIFO_ADDR,
+ &fifo);
+ if (fifo & MPR084_FIFO_OVERFLOW_FLAG)
+ printk(KERN_ERR
+ "%s: FIFO overflow \n\n",
+ __func__);
+ }
+ }
+ /* Re-enable interrupts */
+ enable_irq(d->kpirq);
+ }
+
+ d->kp_thread_cnt = 0;
+ return 0;
+}
+
+/*!
+ * This function puts the Keypad controller in low-power mode/state.
+ *
+ * @param pdev the device structure used to give information on Keypad
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mpr084_suspend(struct i2c_client *client, pm_message_t state)
+{
+ struct mpr084_data *d = i2c_get_clientdata(client);
+
+ if (!IS_ERR(d->tstask) && d->opened)
+ kthread_stop(d->tstask);
+
+ return 0;
+}
+
+/*!
+ * This function brings the Keypad controller back from low-power state.
+ *
+ * @param pdev the device structure used to give information on Keypad
+ * to resume
+ *
+ * @return The function always returns 0.
+ */
+static int mpr084_resume(struct i2c_client *client)
+{
+ struct mpr084_data *d = i2c_get_clientdata(client);
+
+ if (d->opened)
+ d->tstask = kthread_run(mpr084ts_thread, d, DRIVER_NAME "kpd");
+
+ return 0;
+}
+
+static int mpr084_idev_open(struct input_dev *idev)
+{
+ struct mpr084_data *d = input_get_drvdata(idev);
+ int ret = 0;
+
+ d->tstask = kthread_run(mpr084ts_thread, d, DRIVER_NAME "kpd");
+ if (IS_ERR(d->tstask))
+ ret = PTR_ERR(d->tstask);
+ else
+ d->opened++;
+ return ret;
+}
+
+static void mpr084_idev_close(struct input_dev *idev)
+{
+ struct mpr084_data *d = input_get_drvdata(idev);
+
+ if (!IS_ERR(d->tstask))
+ kthread_stop(d->tstask);
+ if (d->opened > 0)
+ d->opened--;
+}
+
+static int mpr084_driver_register(struct mpr084_data *data)
+{
+ struct input_dev *idev;
+ int ret = 0;
+
+ if (data->kpirq) {
+ ret =
+ request_irq(data->kpirq, mpr084_keypadirq,
+ IRQF_TRIGGER_FALLING, DRIVER_NAME, data);
+ if (!ret) {
+ init_completion(&data->kpirq_completion);
+ set_irq_wake(data->kpirq, 1);
+ } else {
+ printk(KERN_ERR "%s: cannot grab irq %d\n",
+ __func__, data->kpirq);
+ }
+
+ }
+ idev = input_allocate_device();
+ data->idev = idev;
+ input_set_drvdata(idev, data);
+ idev->name = DRIVER_NAME;
+ idev->open = mpr084_idev_open;
+ idev->close = mpr084_idev_close;
+ if (!ret)
+ ret = input_register_device(idev);
+
+ return ret;
+}
+
+static int mpr084_i2c_remove(struct i2c_client *client)
+{
+ int err;
+ struct mpr084_data *d = i2c_get_clientdata(client);
+
+ free_irq(d->kpirq, d);
+ input_unregister_device(d->idev);
+ if (keypad->inactive)
+ keypad->inactive();
+
+ /*Disable the Regulator*/
+ if (keypad->vdd_reg) {
+ regulator_disable(vdd_reg);
+ regulator_put(vdd_reg);
+ }
+
+ err = i2c_detach_client(client);
+ if (err) {
+ dev_err(&client->dev, "Client deregistration failed, "
+ "client not detached.\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int mpr084_configure(struct mpr084_data *data)
+{
+ int ret = 0, regValue = 0;
+
+ ret = mpr084_write_register(data, MPR084_TPC_ADDR, 0x1d);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR1_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR2_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR3_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR4_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR5_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR6_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR7_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR8_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ /* channel enable mask: enable all */
+ ret = mpr084_write_register(data, MPR084_ECEM_ADDR, 0xff);
+ if (ret < 0)
+ goto err;
+ /*two conccurrent touch position allowed */
+ ret = mpr084_write_register(data, MPR084_MNTP_ADDR, 0x02);
+ if (ret < 0)
+ goto err;
+
+ /* master tick period*/
+ ret = mpr084_write_register(data, MPR084_MTC_ADDR, 0x05);
+ if (ret < 0)
+ goto err;
+
+
+ /*Sample period */
+ ret = mpr084_write_register(data, MPR084_TASP_ADDR, 0x02);
+ if (ret < 0)
+ goto err;
+
+
+ /* disable sournder*/
+ ret = mpr084_write_register(data, MPR084_SC_ADDR, 0x00);
+ if (ret < 0)
+ goto err;
+
+ /* stuck key timeout */
+ ret = mpr084_write_register(data, MPR084_SKT_ADDR, 0x01);
+ if (ret < 0)
+ goto err;
+
+ /*enabled IRQEN, RUNE, IRQR */
+ ret = mpr084_read_register(data, MPR084_CONFIG_ADDR, &regValue);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "%s: Err in reading keypad CONFIGADDR register \n\n",
+ __func__);
+ goto err;
+ }
+ regValue |= 0x03;
+ ret = mpr084_write_register(data, MPR084_CONFIG_ADDR, regValue);
+ if (ret < 0)
+ goto err;
+ return ret;
+err:
+ return -ENODEV;
+}
+
+static int mpr084_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct mpr084_data *data;
+ int err = 0, i = 0;
+#if DEBUG
+ int regValue = 0;
+#endif
+ data = kzalloc(sizeof(struct mpr084_data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ data->kpirq = client->irq;
+ err = mpr084_driver_register(data);
+ if (err < 0)
+ goto exit_free;
+ keypad = (struct mxc_keyp_platform_data *)(client->dev).platform_data;
+ if (keypad->active)
+ keypad->active();
+
+ /*Enable the Regulator*/
+ if (keypad && keypad->vdd_reg) {
+ vdd_reg = regulator_get(&client->dev, keypad->vdd_reg);
+ if (!IS_ERR(vdd_reg))
+ regulator_enable(vdd_reg);
+ else
+ vdd_reg = NULL;
+ } else
+ vdd_reg = NULL;
+
+ mxckpd_keycodes = keypad->matrix;
+ data->idev->keycode = &mxckpd_keycodes;
+ data->idev->keycodesize = sizeof(unsigned char);
+ data->idev->keycodemax = KEY_COUNT;
+ data->idev->id.bustype = BUS_I2C;
+ __set_bit(EV_KEY, data->idev->evbit);
+ for (i = 0; i < 8; i++)
+ __set_bit(mxckpd_keycodes[i], data->idev->keybit);
+ err = mpr084_configure(data);
+ if (err == -ENODEV) {
+ free_irq(data->kpirq, data);
+ input_unregister_device(data->idev);
+ goto exit_free;
+ }
+
+#if DEBUG
+ for (i = MPR084_ADDR_MINI; i <= MPR084_ADDR_MAX; i++) {
+ err = mpr084_read_register(data, i, &regValue);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Err in reading keypad CONFIGADDR register \n\n",
+ __func__);
+ goto exit_free;
+ }
+ printk("MPR084 Register id: %d, Value:%d \n", i, regValue);
+
+ }
+#endif
+ memset(kpstatus, 0, sizeof(kpstatus));
+ printk(KERN_INFO "%s: Device Attached\n", __func__);
+ return 0;
+exit_free:
+ /*disable the Regulator*/
+ if (vdd_reg) {
+ regulator_disable(vdd_reg);
+ regulator_put(vdd_reg);
+ vdd_reg = NULL;
+ }
+ kfree(data);
+ return err;
+}
+
+static const struct i2c_device_id mpr084_id[] = {
+ { "mpr084", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mpr084_id);
+
+static struct i2c_driver mpr084_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = mpr084_i2c_probe,
+ .remove = mpr084_i2c_remove,
+ .suspend = mpr084_suspend,
+ .resume = mpr084_resume,
+ .command = NULL,
+ .id_table = mpr084_id,
+};
+static int __init mpr084_init(void)
+{
+ return i2c_add_driver(&mpr084_driver);
+}
+
+static void __exit mpr084_exit(void)
+{
+ i2c_del_driver(&mpr084_driver);
+}
+
+MODULE_AUTHOR("Freescale Semiconductor Inc");
+MODULE_DESCRIPTION("MPR084 Touch KeyPad Controller driver");
+MODULE_LICENSE("GPL");
+module_init(mpr084_init);
+module_exit(mpr084_exit);
diff --git a/drivers/input/keyboard/mxc_keyb.c b/drivers/input/keyboard/mxc_keyb.c
new file mode 100644
index 000000000000..248b8e560903
--- /dev/null
+++ b/drivers/input/keyboard/mxc_keyb.c
@@ -0,0 +1,1036 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_keyb.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC keypad port.
+ *
+ * The keypad driver is designed as a standard Input driver which interacts
+ * with low level keypad port hardware. Upon opening, the Keypad driver
+ * initializes the keypad port. When the keypad interrupt happens the driver
+ * calles keypad polling timer and scans the keypad matrix for key
+ * press/release. If all key press/release happened it comes out of timer and
+ * waits for key press interrupt. The scancode for key press and release events
+ * are passed to Input subsytem.
+ *
+ * @ingroup keypad
+ */
+
+/*!
+ * Comment KPP_DEBUG to disable debug messages
+ */
+#define KPP_DEBUG 0
+
+#if KPP_DEBUG
+#define DEBUG
+#include <linux/kernel.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <mach/hardware.h>
+#include <linux/kd.h>
+#include <linux/fs.h>
+#include <linux/kbd_kern.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/input.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <asm/mach/keypad.h>
+
+/*
+ * Module header file
+ */
+#include "mxc_keyb.h"
+
+/*!
+ * This structure holds the keypad private data structure.
+ */
+static struct keypad_priv kpp_dev;
+
+/*! Indicates if the key pad device is enabled. */
+static unsigned int key_pad_enabled;
+
+/*! Input device structure. */
+static struct input_dev *mxckbd_dev = NULL;
+
+/*! KPP clock handle. */
+static struct clk *kpp_clk;
+
+/*! This static variable indicates whether a key event is pressed/released. */
+static unsigned short KPress;
+
+/*! cur_rcmap and prev_rcmap array is used to detect key press and release. */
+static unsigned short *cur_rcmap; /* max 64 bits (8x8 matrix) */
+static unsigned short *prev_rcmap;
+
+/*!
+ * Debounce polling period(10ms) in system ticks.
+ */
+static unsigned short KScanRate = (10 * HZ) / 1000;
+
+static struct keypad_data *keypad;
+
+static int has_leaning_key;
+/*!
+ * These arrays are used to store press and release scancodes.
+ */
+static short **press_scancode;
+static short **release_scancode;
+
+static const unsigned short *mxckpd_keycodes;
+static unsigned short mxckpd_keycodes_size;
+
+#define press_left_code 30
+#define press_right_code 29
+#define press_up_code 28
+#define press_down_code 27
+
+#define rel_left_code 158
+#define rel_right_code 157
+#define rel_up_code 156
+#define rel_down_code 155
+/*!
+ * These functions are used to configure and the GPIO pins for keypad to
+ * activate and deactivate it.
+ */
+extern void gpio_keypad_active(void);
+extern void gpio_keypad_inactive(void);
+
+/*!
+ * This function is called for generating scancodes for key press and
+ * release on keypad for the board.
+ *
+ * @param row Keypad row pressed on the keypad matrix.
+ * @param col Keypad col pressed on the keypad matrix.
+ * @param press Indicated key press/release.
+ *
+ * @return Key press/release Scancode.
+ */
+static signed short mxc_scan_matrix_leaning_key(int row, int col, int press)
+{
+ static unsigned first_row;
+ static unsigned first_set = 0, flag = 0;
+ signed short scancode = -1;
+
+ if (press) {
+ if ((3 == col) && ((3 == row) ||
+ (4 == row) || (5 == row) || (6 == row))) {
+ if (first_set == 0) {
+ first_set = 1;
+ first_row = row;
+ } else {
+ first_set = 0;
+ if (((first_row == 6) || (first_row == 3))
+ && ((row == 6) || (row == 3)))
+ scancode = press_down_code;
+ else if (((first_row == 3) || (first_row == 5))
+ && ((row == 3) || (row == 5)))
+ scancode = press_left_code;
+ else if (((first_row == 6) || (first_row == 4))
+ && ((row == 6) || (row == 4)))
+ scancode = press_right_code;
+ else if (((first_row == 4) || (first_row == 5))
+ && ((row == 4) || (row == 5)))
+ scancode = press_up_code;
+ KPress = 1;
+ kpp_dev.iKeyState = KStateUp;
+ pr_debug("Press (%d, %d) scan=%d Kpress=%d\n",
+ row, col, scancode, KPress);
+ }
+ } else {
+ /*
+ * check for other keys only
+ * if the cursor key presses
+ * are not detected may be
+ * this needs better logic
+ */
+ if ((0 == (cur_rcmap[3] & BITSET(0, 3))) &&
+ (0 == (cur_rcmap[4] & BITSET(0, 3))) &&
+ (0 == (cur_rcmap[5] & BITSET(0, 3))) &&
+ (0 == (cur_rcmap[6] & BITSET(0, 3)))) {
+ scancode = ((col * kpp_dev.kpp_rows) + row);
+ KPress = 1;
+ kpp_dev.iKeyState = KStateUp;
+ flag = 1;
+ pr_debug("Press (%d, %d) scan=%d Kpress=%d\n",
+ row, col, scancode, KPress);
+ }
+ }
+ } else {
+ if ((flag == 0) && (3 == col)
+ && ((3 == row) || (4 == row) || (5 == row)
+ || (6 == row))) {
+ if (first_set == 0) {
+ first_set = 1;
+ first_row = row;
+ } else {
+ first_set = 0;
+ if (((first_row == 6) || (first_row == 3))
+ && ((row == 6) || (row == 3)))
+ scancode = rel_down_code;
+ else if (((first_row == 3) || (first_row == 5))
+ && ((row == 3) || (row == 5)))
+ scancode = rel_left_code;
+ else if (((first_row == 6) || (first_row == 4))
+ && ((row == 6) || (row == 4)))
+ scancode = rel_right_code;
+ else if (((first_row == 4) || (first_row == 5))
+ && ((row == 4) || (row == 5)))
+ scancode = rel_up_code;
+ KPress = 0;
+ kpp_dev.iKeyState = KStateDown;
+ pr_debug("Release (%d, %d) scan=%d Kpress=%d\n",
+ row, col, scancode, KPress);
+ }
+ } else {
+ /*
+ * check for other keys only
+ * if the cursor key presses
+ * are not detected may be
+ * this needs better logic
+ */
+ if ((0 == (prev_rcmap[3] & BITSET(0, 3))) &&
+ (0 == (prev_rcmap[4] & BITSET(0, 3))) &&
+ (0 == (cur_rcmap[5] & BITSET(0, 3))) &&
+ (0 == (cur_rcmap[6] & BITSET(0, 3)))) {
+ scancode = ((col * kpp_dev.kpp_rows) + row) +
+ MXC_KEYRELEASE;
+ KPress = 0;
+ flag = 0;
+ kpp_dev.iKeyState = KStateDown;
+ pr_debug("Release (%d, %d) scan=%d Kpress=%d\n",
+ row, col, scancode, KPress);
+ }
+ }
+ }
+ return scancode;
+}
+
+/*!
+ * This function is called to scan the keypad matrix to find out the key press
+ * and key release events. Make scancode and break scancode are generated for
+ * key press and key release events.
+ *
+ * The following scanning sequence are done for
+ * keypad row and column scanning,
+ * -# Write 1's to KPDR[15:8], setting column data to 1's
+ * -# Configure columns as totem pole outputs(for quick discharging of keypad
+ * capacitance)
+ * -# Configure columns as open-drain
+ * -# Write a single column to 0, others to 1.
+ * -# Sample row inputs and save data. Multiple key presses can be detected on
+ * a single column.
+ * -# Repeat steps the above steps for remaining columns.
+ * -# Return all columns to 0 in preparation for standby mode.
+ * -# Clear KPKD and KPKR status bit(s) by writing to a 1,
+ * Set the KPKR synchronizer chain by writing "1" to KRSS register,
+ * Clear the KPKD synchronizer chain by writing "1" to KDSC register
+ *
+ * @result Number of key pressed/released.
+ */
+static int mxc_kpp_scan_matrix(void)
+{
+ unsigned short reg_val;
+ int col, row;
+ short scancode = 0;
+ int keycnt = 0; /* How many keys are still pressed */
+
+ /*
+ * wmb() linux kernel function which guarantees orderings in write
+ * operations
+ */
+ wmb();
+
+ /* save cur keypad matrix to prev */
+
+ memcpy(prev_rcmap, cur_rcmap, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
+ memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
+
+ for (col = 0; col < kpp_dev.kpp_cols; col++) { /* Col */
+ /* 2. Write 1.s to KPDR[15:8] setting column data to 1.s */
+ reg_val = __raw_readw(KPDR);
+ reg_val |= 0xff00;
+ __raw_writew(reg_val, KPDR);
+
+ /*
+ * 3. Configure columns as totem pole outputs(for quick
+ * discharging of keypad capacitance)
+ */
+ reg_val = __raw_readw(KPCR);
+ reg_val &= 0x00ff;
+ __raw_writew(reg_val, KPCR);
+
+ udelay(2);
+
+ /*
+ * 4. Configure columns as open-drain
+ */
+ reg_val = __raw_readw(KPCR);
+ reg_val |= ((1 << kpp_dev.kpp_cols) - 1) << 8;
+ __raw_writew(reg_val, KPCR);
+
+ /*
+ * 5. Write a single column to 0, others to 1.
+ * 6. Sample row inputs and save data. Multiple key presses
+ * can be detected on a single column.
+ * 7. Repeat steps 2 - 6 for remaining columns.
+ */
+
+ /* Col bit starts at 8th bit in KPDR */
+ reg_val = __raw_readw(KPDR);
+ reg_val &= ~(1 << (8 + col));
+ __raw_writew(reg_val, KPDR);
+
+ /* Delay added to avoid propagating the 0 from column to row
+ * when scanning. */
+
+ udelay(5);
+
+ /* Read row input */
+ reg_val = __raw_readw(KPDR);
+ for (row = 0; row < kpp_dev.kpp_rows; row++) { /* sample row */
+ if (TEST_BIT(reg_val, row) == 0) {
+ cur_rcmap[row] = BITSET(cur_rcmap[row], col);
+ keycnt++;
+ }
+ }
+ }
+
+ /*
+ * 8. Return all columns to 0 in preparation for standby mode.
+ * 9. Clear KPKD and KPKR status bit(s) by writing to a .1.,
+ * set the KPKR synchronizer chain by writing "1" to KRSS register,
+ * clear the KPKD synchronizer chain by writing "1" to KDSC register
+ */
+ reg_val = 0x00;
+ __raw_writew(reg_val, KPDR);
+ reg_val = __raw_readw(KPDR);
+ reg_val = __raw_readw(KPSR);
+ reg_val |= KBD_STAT_KPKD | KBD_STAT_KPKR | KBD_STAT_KRSS |
+ KBD_STAT_KDSC;
+ __raw_writew(reg_val, KPSR);
+
+ /* Check key press status change */
+
+ /*
+ * prev_rcmap array will contain the previous status of the keypad
+ * matrix. cur_rcmap array will contains the present status of the
+ * keypad matrix. If a bit is set in the array, that (row, col) bit is
+ * pressed, else it is not pressed.
+ *
+ * XORing these two variables will give us the change in bit for
+ * particular row and column. If a bit is set in XOR output, then that
+ * (row, col) has a change of status from the previous state. From
+ * the diff variable the key press and key release of row and column
+ * are found out.
+ *
+ * If the key press is determined then scancode for key pressed
+ * can be generated using the following statement:
+ * scancode = ((row * 8) + col);
+ *
+ * If the key release is determined then scancode for key release
+ * can be generated using the following statement:
+ * scancode = ((row * 8) + col) + MXC_KEYRELEASE;
+ */
+ for (row = 0; row < kpp_dev.kpp_rows; row++) {
+ unsigned char diff;
+
+ /*
+ * Calculate the change in the keypad row status
+ */
+ diff = prev_rcmap[row] ^ cur_rcmap[row];
+
+ for (col = 0; col < kpp_dev.kpp_cols; col++) {
+ if ((diff >> col) & 0x1) {
+ /* There is a status change on col */
+ if ((prev_rcmap[row] & BITSET(0, col)) == 0) {
+ /*
+ * Previous state is 0, so now
+ * a key is pressed
+ */
+ if (has_leaning_key) {
+ scancode =
+ mxc_scan_matrix_leaning_key
+ (row, col, 1);
+ } else {
+ scancode =
+ ((row * kpp_dev.kpp_cols) +
+ col);
+ KPress = 1;
+ kpp_dev.iKeyState = KStateUp;
+ }
+ pr_debug("Press (%d, %d) scan=%d "
+ "Kpress=%d\n",
+ row, col, scancode, KPress);
+ press_scancode[row][col] =
+ (short)scancode;
+ } else {
+ /*
+ * Previous state is not 0, so
+ * now a key is released
+ */
+ if (has_leaning_key) {
+ scancode =
+ mxc_scan_matrix_leaning_key
+ (row, col, 0);
+ } else {
+ scancode =
+ (row * kpp_dev.kpp_cols) +
+ col + MXC_KEYRELEASE;
+ KPress = 0;
+ kpp_dev.iKeyState = KStateDown;
+ }
+
+ pr_debug
+ ("Release (%d, %d) scan=%d Kpress=%d\n",
+ row, col, scancode, KPress);
+ release_scancode[row][col] =
+ (short)scancode;
+ keycnt++;
+ }
+ }
+ }
+ }
+
+ /*
+ * This switch case statement is the
+ * implementation of state machine of debounce
+ * logic for key press/release.
+ * The explaination of state machine is as
+ * follows:
+ *
+ * KStateUp State:
+ * This is in intial state of the state machine
+ * this state it checks for any key presses.
+ * The key press can be checked using the
+ * variable KPress. If KPress is set, then key
+ * press is identified and switches the to
+ * KStateFirstDown state for key press to
+ * debounce.
+ *
+ * KStateFirstDown:
+ * After debounce delay(10ms), if the KPress is
+ * still set then pass scancode generated to
+ * input device and change the state to
+ * KStateDown, else key press debounce is not
+ * satisfied so change the state to KStateUp.
+ *
+ * KStateDown:
+ * In this state it checks for any key release.
+ * If KPress variable is cleared, then key
+ * release is indicated and so, switch the
+ * state to KStateFirstUp else to state
+ * KStateDown.
+ *
+ * KStateFirstUp:
+ * After debounce delay(10ms), if the KPress is
+ * still reset then pass the key release
+ * scancode to input device and change
+ * the state to KStateUp else key release is
+ * not satisfied so change the state to
+ * KStateDown.
+ */
+ switch (kpp_dev.iKeyState) {
+ case KStateUp:
+ if (KPress) {
+ /* First Down (must debounce). */
+ kpp_dev.iKeyState = KStateFirstDown;
+ } else {
+ /* Still UP.(NO Changes) */
+ kpp_dev.iKeyState = KStateUp;
+ }
+ break;
+
+ case KStateFirstDown:
+ if (KPress) {
+ for (row = 0; row < kpp_dev.kpp_rows; row++) {
+ for (col = 0; col < kpp_dev.kpp_cols; col++) {
+ if ((press_scancode[row][col] != -1)) {
+ /* Still Down, so add scancode */
+ scancode =
+ press_scancode[row][col];
+ input_event(mxckbd_dev, EV_KEY,
+ mxckpd_keycodes
+ [scancode], 1);
+ if (mxckpd_keycodes[scancode] ==
+ KEY_LEFTSHIFT) {
+ input_event(mxckbd_dev,
+ EV_KEY,
+ KEY_3, 1);
+ }
+ kpp_dev.iKeyState = KStateDown;
+ press_scancode[row][col] = -1;
+ }
+ }
+ }
+ } else {
+ /* Just a bounce */
+ kpp_dev.iKeyState = KStateUp;
+ }
+ break;
+
+ case KStateDown:
+ if (KPress) {
+ /* Still down (no change) */
+ kpp_dev.iKeyState = KStateDown;
+ } else {
+ /* First Up. Must debounce */
+ kpp_dev.iKeyState = KStateFirstUp;
+ }
+ break;
+
+ case KStateFirstUp:
+ if (KPress) {
+ /* Just a bounce */
+ kpp_dev.iKeyState = KStateDown;
+ } else {
+ for (row = 0; row < kpp_dev.kpp_rows; row++) {
+ for (col = 0; col < kpp_dev.kpp_cols; col++) {
+ if ((release_scancode[row][col] != -1)) {
+ scancode =
+ release_scancode[row][col];
+ scancode =
+ scancode - MXC_KEYRELEASE;
+ input_event(mxckbd_dev, EV_KEY,
+ mxckpd_keycodes
+ [scancode], 0);
+ if (mxckpd_keycodes[scancode] ==
+ KEY_LEFTSHIFT) {
+ input_event(mxckbd_dev,
+ EV_KEY,
+ KEY_3, 0);
+ }
+ kpp_dev.iKeyState = KStateUp;
+ release_scancode[row][col] = -1;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ return -EBADRQC;
+ break;
+ }
+
+ return keycnt;
+}
+
+/*!
+ * This function is called to start the timer for scanning the keypad if there
+ * is any key press. Currently this interval is set to 10 ms. When there are
+ * no keys pressed on the keypad we return back, waiting for a keypad key
+ * press interrupt.
+ *
+ * @param data Opaque data passed back by kernel. Not used.
+ */
+static void mxc_kpp_handle_timer(unsigned long data)
+{
+ unsigned short reg_val;
+ int i;
+
+ if (key_pad_enabled == 0) {
+ return;
+ }
+ if (mxc_kpp_scan_matrix() == 0) {
+ /*
+ * Stop scanning and wait for interrupt.
+ * Enable press interrupt and disable release interrupt.
+ */
+ __raw_writew(0x00FF, KPDR);
+ reg_val = __raw_readw(KPSR);
+ reg_val |= (KBD_STAT_KPKR | KBD_STAT_KPKD);
+ reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
+ __raw_writew(reg_val, KPSR);
+ reg_val |= KBD_STAT_KDIE;
+ reg_val &= ~KBD_STAT_KRIE;
+ __raw_writew(reg_val, KPSR);
+
+ /*
+ * No more keys pressed... make sure unwanted key codes are
+ * not given upstairs
+ */
+ for (i = 0; i < kpp_dev.kpp_rows; i++) {
+ memset(press_scancode[i], -1,
+ sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
+ memset(release_scancode[i], -1,
+ sizeof(release_scancode[0][0]) *
+ kpp_dev.kpp_cols);
+ }
+ return;
+ }
+
+ /*
+ * There are still some keys pressed, continue to scan.
+ * We shall scan again in 10 ms. This has to be tuned according
+ * to the requirement.
+ */
+ kpp_dev.poll_timer.expires = jiffies + KScanRate;
+ kpp_dev.poll_timer.function = mxc_kpp_handle_timer;
+ add_timer(&kpp_dev.poll_timer);
+}
+
+/*!
+ * This function is the keypad Interrupt handler.
+ * This function checks for keypad status register (KPSR) for key press
+ * and interrupt. If key press interrupt has occurred, then the key
+ * press interrupt in the KPSR are disabled.
+ * It then calls mxc_kpp_scan_matrix to check for any key pressed/released.
+ * If any key is found to be pressed, then a timer is set to call
+ * mxc_kpp_scan_matrix function for every 10 ms.
+ *
+ * @param irq The Interrupt number
+ * @param dev_id Driver private data
+ *
+ * @result The function returns \b IRQ_RETVAL(1) if interrupt was handled,
+ * returns \b IRQ_RETVAL(0) if the interrupt was not handled.
+ * \b IRQ_RETVAL is defined in include/linux/interrupt.h.
+ */
+static irqreturn_t mxc_kpp_interrupt(int irq, void *dev_id)
+{
+ unsigned short reg_val;
+
+ /* Delete the polling timer */
+ del_timer(&kpp_dev.poll_timer);
+ reg_val = __raw_readw(KPSR);
+
+ /* Check if it is key press interrupt */
+ if (reg_val & KBD_STAT_KPKD) {
+ /*
+ * Disable key press(KDIE status bit) interrupt
+ */
+ reg_val &= ~KBD_STAT_KDIE;
+ __raw_writew(reg_val, KPSR);
+ } else {
+ /* spurious interrupt */
+ return IRQ_RETVAL(0);
+ }
+ /*
+ * Check if any keys are pressed, if so start polling.
+ */
+ mxc_kpp_handle_timer(0);
+
+ return IRQ_RETVAL(1);
+}
+
+/*!
+ * This function is called when the keypad driver is opened.
+ * Since keypad initialization is done in __init, nothing is done in open.
+ *
+ * @param dev Pointer to device inode
+ *
+ * @result The function always return 0
+ */
+static int mxc_kpp_open(struct input_dev *dev)
+{
+ return 0;
+}
+
+/*!
+ * This function is called close the keypad device.
+ * Nothing is done in this function, since every thing is taken care in
+ * __exit function.
+ *
+ * @param dev Pointer to device inode
+ *
+ */
+static void mxc_kpp_close(struct input_dev *dev)
+{
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function puts the Keypad controller in low-power mode/state.
+ * If Keypad is enabled as a wake source(i.e. it can resume the system
+ * from suspend mode), the Keypad controller doesn't enter low-power state.
+ *
+ * @param pdev the device structure used to give information on Keypad
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return return -1 when the keypad is pressed. Otherwise, return 0
+ */
+static int mxc_kpp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ /* When the keypad is still pressed, clean up registers and timers */
+ if (timer_pending(&kpp_dev.poll_timer))
+ return -1;
+
+ if (device_may_wakeup(&pdev->dev)) {
+ enable_irq_wake(keypad->irq);
+ } else {
+ disable_irq(keypad->irq);
+ key_pad_enabled = 0;
+ clk_disable(kpp_clk);
+ gpio_keypad_inactive();
+ }
+
+ return 0;
+}
+
+/*!
+ * This function brings the Keypad controller back from low-power state.
+ * If Keypad is enabled as a wake source(i.e. it can resume the system
+ * from suspend mode), the Keypad controller doesn't enter low-power state.
+ *
+ * @param pdev the device structure used to give information on Keypad
+ * to resume
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_kpp_resume(struct platform_device *pdev)
+{
+ if (device_may_wakeup(&pdev->dev)) {
+ disable_irq_wake(keypad->irq);
+ } else {
+ gpio_keypad_active();
+ clk_enable(kpp_clk);
+ key_pad_enabled = 1;
+ enable_irq(keypad->irq);
+ }
+
+ return 0;
+}
+
+#else
+#define mxc_kpp_suspend NULL
+#define mxc_kpp_resume NULL
+#endif /* CONFIG_PM */
+
+/*!
+ * This function is called to free the allocated memory for local arrays
+ */
+static void mxc_kpp_free_allocated(void)
+{
+
+ int i;
+
+ if (press_scancode) {
+ for (i = 0; i < kpp_dev.kpp_rows; i++) {
+ if (press_scancode[i])
+ kfree(press_scancode[i]);
+ }
+ kfree(press_scancode);
+ }
+
+ if (release_scancode) {
+ for (i = 0; i < kpp_dev.kpp_rows; i++) {
+ if (release_scancode[i])
+ kfree(release_scancode[i]);
+ }
+ kfree(release_scancode);
+ }
+
+ if (cur_rcmap)
+ kfree(cur_rcmap);
+
+ if (prev_rcmap)
+ kfree(prev_rcmap);
+
+ if (mxckbd_dev)
+ input_free_device(mxckbd_dev);
+}
+
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions.
+ *
+ * @return The function returns 0 on successful registration. Otherwise returns
+ * specific error code.
+ */
+static int mxc_kpp_probe(struct platform_device *pdev)
+{
+ int i, irq;
+ int retval;
+ unsigned int reg_val;
+
+ keypad = (struct keypad_data *)pdev->dev.platform_data;
+
+ kpp_dev.kpp_cols = keypad->colmax;
+ kpp_dev.kpp_rows = keypad->rowmax;
+ key_pad_enabled = 0;
+
+ /*
+ * Request for IRQ number for keypad port. The Interrupt handler
+ * function (mxc_kpp_interrupt) is called when ever interrupt occurs on
+ * keypad port.
+ */
+ irq = platform_get_irq(pdev, 0);
+ keypad->irq = irq;
+ retval = request_irq(irq, mxc_kpp_interrupt, 0, MOD_NAME, MOD_NAME);
+ if (retval) {
+ pr_debug("KPP: request_irq(%d) returned error %d\n",
+ MXC_INT_KPP, retval);
+ return retval;
+ }
+
+ /* Enable keypad clock */
+ kpp_clk = clk_get(&pdev->dev, "kpp_clk");
+ clk_enable(kpp_clk);
+
+ /* IOMUX configuration for keypad */
+ gpio_keypad_active();
+
+ /* Configure keypad */
+
+ /* Enable number of rows in keypad (KPCR[7:0])
+ * Configure keypad columns as open-drain (KPCR[15:8])
+ *
+ * Configure the rows/cols in KPP
+ * LSB nibble in KPP is for 8 rows
+ * MSB nibble in KPP is for 8 cols
+ */
+ reg_val = __raw_readw(KPCR);
+ reg_val |= (1 << keypad->rowmax) - 1; /* LSB */
+ reg_val |= ((1 << keypad->colmax) - 1) << 8; /* MSB */
+ __raw_writew(reg_val, KPCR);
+
+ /* Write 0's to KPDR[15:8] */
+ reg_val = __raw_readw(KPDR);
+ reg_val &= 0x00ff;
+ __raw_writew(reg_val, KPDR);
+
+ /* Configure columns as output, rows as input (KDDR[15:0]) */
+ reg_val = __raw_readw(KDDR);
+ reg_val |= 0xff00;
+ reg_val &= 0xff00;
+ __raw_writew(reg_val, KDDR);
+
+ reg_val = __raw_readw(KPSR);
+ reg_val &= ~(KBD_STAT_KPKR | KBD_STAT_KPKD);
+ reg_val |= KBD_STAT_KPKD;
+ reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
+ __raw_writew(reg_val, KPSR);
+ reg_val |= KBD_STAT_KDIE;
+ reg_val &= ~KBD_STAT_KRIE;
+ __raw_writew(reg_val, KPSR);
+
+ has_leaning_key = keypad->learning;
+ mxckpd_keycodes = keypad->matrix;
+ mxckpd_keycodes_size = keypad->rowmax * keypad->colmax;
+
+ if ((keypad->matrix == (void *)0)
+ || (mxckpd_keycodes_size == 0)) {
+ retval = -ENODEV;
+ goto err1;
+ }
+
+ mxckbd_dev = input_allocate_device();
+ if (!mxckbd_dev) {
+ printk(KERN_ERR
+ "mxckbd_dev: not enough memory for input device\n");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ mxckbd_dev->keycode = (void *)mxckpd_keycodes;
+ mxckbd_dev->keycodesize = sizeof(mxckpd_keycodes[0]);
+ mxckbd_dev->keycodemax = mxckpd_keycodes_size;
+ mxckbd_dev->name = "mxckpd";
+ mxckbd_dev->id.bustype = BUS_HOST;
+ mxckbd_dev->open = mxc_kpp_open;
+ mxckbd_dev->close = mxc_kpp_close;
+
+ retval = input_register_device(mxckbd_dev);
+ if (retval < 0) {
+ printk(KERN_ERR
+ "mxckbd_dev: failed to register input device\n");
+ goto err2;
+ }
+
+ /* allocate required memory */
+ press_scancode = kmalloc(kpp_dev.kpp_rows * sizeof(press_scancode[0]),
+ GFP_KERNEL);
+ release_scancode =
+ kmalloc(kpp_dev.kpp_rows * sizeof(release_scancode[0]), GFP_KERNEL);
+
+ if (!press_scancode || !release_scancode) {
+ retval = -ENOMEM;
+ goto err3;
+ }
+
+ for (i = 0; i < kpp_dev.kpp_rows; i++) {
+ press_scancode[i] = kmalloc(kpp_dev.kpp_cols
+ * sizeof(press_scancode[0][0]),
+ GFP_KERNEL);
+ release_scancode[i] =
+ kmalloc(kpp_dev.kpp_cols * sizeof(release_scancode[0][0]),
+ GFP_KERNEL);
+
+ if (!press_scancode[i] || !release_scancode[i]) {
+ retval = -ENOMEM;
+ goto err3;
+ }
+ }
+
+ cur_rcmap =
+ kmalloc(kpp_dev.kpp_rows * sizeof(cur_rcmap[0]), GFP_KERNEL);
+ prev_rcmap =
+ kmalloc(kpp_dev.kpp_rows * sizeof(prev_rcmap[0]), GFP_KERNEL);
+
+ if (!cur_rcmap || !prev_rcmap) {
+ retval = -ENOMEM;
+ goto err3;
+ }
+
+ __set_bit(EV_KEY, mxckbd_dev->evbit);
+
+ for (i = 0; i < mxckpd_keycodes_size; i++)
+ __set_bit(mxckpd_keycodes[i], mxckbd_dev->keybit);
+
+ for (i = 0; i < kpp_dev.kpp_rows; i++) {
+ memset(press_scancode[i], -1,
+ sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
+ memset(release_scancode[i], -1,
+ sizeof(release_scancode[0][0]) * kpp_dev.kpp_cols);
+ }
+ memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
+ memset(prev_rcmap, 0, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
+
+ key_pad_enabled = 1;
+ /* Initialize the polling timer */
+ init_timer(&kpp_dev.poll_timer);
+
+ /* By default, devices should wakeup if they can */
+ /* So keypad is set as "should wakeup" as it can */
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+ err3:
+ mxc_kpp_free_allocated();
+ err2:
+ input_free_device(mxckbd_dev);
+ err1:
+ free_irq(irq, MOD_NAME);
+ clk_disable(kpp_clk);
+ clk_put(kpp_clk);
+ return retval;
+}
+
+/*!
+ * Dissociates the driver from the kpp device.
+ *
+ * @param pdev the device structure used to give information on which SDHC
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_kpp_remove(struct platform_device *pdev)
+{
+ unsigned short reg_val;
+
+ /*
+ * Clear the KPKD status flag (write 1 to it) and synchronizer chain.
+ * Set KDIE control bit, clear KRIE control bit (avoid false release
+ * events. Disable the keypad GPIO pins.
+ */
+ __raw_writew(0x00, KPCR);
+ __raw_writew(0x00, KPDR);
+ __raw_writew(0x00, KDDR);
+
+ reg_val = __raw_readw(KPSR);
+ reg_val |= KBD_STAT_KPKD;
+ reg_val &= ~KBD_STAT_KRSS;
+ reg_val |= KBD_STAT_KDIE;
+ reg_val &= ~KBD_STAT_KRIE;
+ __raw_writew(reg_val, KPSR);
+
+ gpio_keypad_inactive();
+ clk_disable(kpp_clk);
+ clk_put(kpp_clk);
+
+ KPress = 0;
+
+ del_timer(&kpp_dev.poll_timer);
+
+ free_irq(keypad->irq, MOD_NAME);
+ input_unregister_device(mxckbd_dev);
+
+ mxc_kpp_free_allocated();
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_kpd_driver = {
+ .driver = {
+ .name = "mxc_keypad",
+ .bus = &platform_bus_type,
+ },
+ .suspend = mxc_kpp_suspend,
+ .resume = mxc_kpp_resume,
+ .probe = mxc_kpp_probe,
+ .remove = mxc_kpp_remove
+};
+
+/*!
+ * This function is called for module initialization.
+ * It registers keypad char driver and requests for KPP irq number. This
+ * function does the initialization of the keypad device.
+ *
+ * The following steps are used for keypad configuration,\n
+ * -# Enable number of rows in the keypad control register (KPCR[7:0}).\n
+ * -# Write 0's to KPDR[15:8]\n
+ * -# Configure keypad columns as open-drain (KPCR[15:8])\n
+ * -# Configure columns as output, rows as input (KDDR[15:0])\n
+ * -# Clear the KPKD status flag (write 1 to it) and synchronizer chain\n
+ * -# Set KDIE control bit, clear KRIE control bit\n
+ * In this function the keypad queue initialization is done.
+ * The keypad IOMUX configuration are done here.*
+
+ *
+ * @return 0 on success and a non-zero value on failure.
+ */
+static int __init mxc_kpp_init(void)
+{
+ printk(KERN_INFO "MXC keypad loaded\n");
+ platform_driver_register(&mxc_kpd_driver);
+ return 0;
+}
+
+/*!
+ * This function is called whenever the module is removed from the kernel. It
+ * unregisters the keypad driver from kernel and frees the irq number.
+ * This function puts the keypad to standby mode. The keypad interrupts are
+ * disabled. It calls gpio_keypad_inactive function to switch gpio
+ * configuration into default state.
+ *
+ */
+static void __exit mxc_kpp_cleanup(void)
+{
+ platform_driver_unregister(&mxc_kpd_driver);
+}
+
+module_init(mxc_kpp_init);
+module_exit(mxc_kpp_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Keypad Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/mxc_keyb.h b/drivers/input/keyboard/mxc_keyb.h
new file mode 100644
index 000000000000..d231eef50088
--- /dev/null
+++ b/drivers/input/keyboard/mxc_keyb.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup keypad Keypad Driver
+ */
+
+/*!
+ * @file mxc_keyb.h
+ *
+ * @brief MXC keypad header file.
+ *
+ * @ingroup keypad
+ */
+#ifndef __MXC_KEYB_H__
+#define __MXC_KEYB_H__
+
+/*!
+ * Keypad Module Name
+ */
+#define MOD_NAME "mxckpd"
+
+/*!
+ * Keypad irq number
+ */
+#define KPP_IRQ MXC_INT_KPP
+
+/*!
+ * XLATE mode selection
+ */
+#define KEYPAD_XLATE 0
+
+/*!
+ * RAW mode selection
+ */
+#define KEYPAD_RAW 1
+
+/*!
+ * Maximum number of keys.
+ */
+#define MAXROW 8
+#define MAXCOL 8
+#define MXC_MAXKEY (MAXROW * MAXCOL)
+
+/*!
+ * This define indicates break scancode for every key release. A constant
+ * of 128 is added to the key press scancode.
+ */
+#define MXC_KEYRELEASE 128
+
+/*
+ * _reg_KPP_KPCR _reg_KPP_KPSR _reg_KPP_KDDR _reg_KPP_KPDR
+ * Keypad Control Register Address
+ */
+#define KPCR IO_ADDRESS(KPP_BASE_ADDR + 0x00)
+
+/*
+ * Keypad Status Register Address
+ */
+#define KPSR IO_ADDRESS(KPP_BASE_ADDR + 0x02)
+
+/*
+ * Keypad Data Direction Address
+ */
+#define KDDR IO_ADDRESS(KPP_BASE_ADDR + 0x04)
+
+/*
+ * Keypad Data Register
+ */
+#define KPDR IO_ADDRESS(KPP_BASE_ADDR + 0x06)
+
+/*
+ * Key Press Interrupt Status bit
+ */
+#define KBD_STAT_KPKD 0x01
+
+/*
+ * Key Release Interrupt Status bit
+ */
+#define KBD_STAT_KPKR 0x02
+
+/*
+ * Key Depress Synchronizer Chain Status bit
+ */
+#define KBD_STAT_KDSC 0x04
+
+/*
+ * Key Release Synchronizer Status bit
+ */
+#define KBD_STAT_KRSS 0x08
+
+/*
+ * Key Depress Interrupt Enable Status bit
+ */
+#define KBD_STAT_KDIE 0x100
+
+/*
+ * Key Release Interrupt Enable
+ */
+#define KBD_STAT_KRIE 0x200
+
+/*
+ * Keypad Clock Enable
+ */
+#define KBD_STAT_KPPEN 0x400
+
+/*!
+ * Buffer size of keypad queue. Should be a power of 2.
+ */
+#define KPP_BUF_SIZE 128
+
+/*!
+ * Test whether bit is set for integer c
+ */
+#define TEST_BIT(c, n) ((c) & (0x1 << (n)))
+
+/*!
+ * Set nth bit in the integer c
+ */
+#define BITSET(c, n) ((c) | (1 << (n)))
+
+/*!
+ * Reset nth bit in the integer c
+ */
+#define BITRESET(c, n) ((c) & ~(1 << (n)))
+
+/*!
+ * This enum represents the keypad state machine to maintain debounce logic
+ * for key press/release.
+ */
+enum KeyState {
+
+ /*!
+ * Key press state.
+ */
+ KStateUp,
+
+ /*!
+ * Key press debounce state.
+ */
+ KStateFirstDown,
+
+ /*!
+ * Key release state.
+ */
+ KStateDown,
+
+ /*!
+ * Key release debounce state.
+ */
+ KStateFirstUp
+};
+
+/*!
+ * Keypad Private Data Structure
+ */
+typedef struct keypad_priv {
+
+ /*!
+ * Keypad state machine.
+ */
+ enum KeyState iKeyState;
+
+ /*!
+ * Number of rows configured in the keypad matrix
+ */
+ unsigned long kpp_rows;
+
+ /*!
+ * Number of Columns configured in the keypad matrix
+ */
+ unsigned long kpp_cols;
+
+ /*!
+ * Timer used for Keypad polling.
+ */
+ struct timer_list poll_timer;
+
+} keypad_priv;
+
+#endif /* __MXC_KEYB_H__ */
diff --git a/drivers/input/keyboard/stmp3xxx-kbd.c b/drivers/input/keyboard/stmp3xxx-kbd.c
new file mode 100644
index 000000000000..f70c2f024401
--- /dev/null
+++ b/drivers/input/keyboard/stmp3xxx-kbd.c
@@ -0,0 +1,300 @@
+/*
+ * Keypad ladder driver for Freescale STMP37XX/STMP378X boards
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#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/pm.h>
+#include <mach/regs-lradc.h>
+#include <mach/lradc.h>
+#include <mach/stmp3xxx.h>
+
+#define BUTTON_PRESS_THRESHOLD 3300
+#define LRADC_NOISE_MARGIN 100
+
+/* this value represents the the lradc value at 3.3V ( 3.3V / 0.000879 V/b ) */
+#define TARGET_VDDIO_LRADC_VALUE 3754
+
+struct stmpkbd_data {
+ struct input_dev *input;
+ int last_button;
+ int irq;
+ struct stmpkbd_keypair *keycodes;
+};
+
+static int delay1 = 500;
+static int delay2 = 200;
+
+static int stmpkbd_open(struct input_dev *dev);
+static void stmpkbd_close(struct input_dev *dev);
+
+static struct stmpkbd_data *stmpkbd_data_alloc(struct platform_device *pdev,
+ struct stmpkbd_keypair *keys)
+{
+ struct stmpkbd_data *d = kzalloc(sizeof(*d), GFP_KERNEL);
+
+ if (!d)
+ return NULL;
+
+ if (!keys) {
+ dev_err(&pdev->dev,
+ "No keycodes in platform_data, bailing out.\n");
+ kfree(d);
+ return NULL;
+ }
+ d->keycodes = keys;
+
+ d->input = input_allocate_device();
+ if (!d->input) {
+ kfree(d);
+ return NULL;
+ }
+
+ d->input->phys = "onboard";
+ d->input->uniq = "0000'0000";
+ d->input->name = pdev->name;
+ d->input->id.bustype = BUS_HOST;
+ d->input->open = stmpkbd_open;
+ d->input->close = stmpkbd_close;
+ d->input->dev.parent = &pdev->dev;
+
+ set_bit(EV_KEY, d->input->evbit);
+ set_bit(EV_REL, d->input->evbit);
+ set_bit(EV_REP, d->input->evbit);
+
+
+ d->last_button = -1;
+
+ while (keys->raw >= 0) {
+ set_bit(keys->kcode, d->input->keybit);
+ keys++;
+ }
+
+ return d;
+}
+
+static inline struct input_dev *GET_INPUT_DEV(struct stmpkbd_data *d)
+{
+ BUG_ON(!d);
+ return d->input;
+}
+
+static void stmpkbd_data_free(struct stmpkbd_data *d)
+{
+ if (!d)
+ return;
+ if (d->input)
+ input_free_device(d->input);
+ kfree(d);
+}
+
+static unsigned stmpkbd_decode_button(struct stmpkbd_keypair *codes,
+ int raw_button)
+
+{
+ pr_debug("Decoding %d\n", raw_button);
+ while (codes->raw != -1) {
+ if ((raw_button > (codes->raw - LRADC_NOISE_MARGIN)) &&
+ (raw_button < (codes->raw + LRADC_NOISE_MARGIN))) {
+ pr_debug("matches code 0x%x = %d\n",
+ codes->kcode, codes->kcode);
+ return codes->kcode;
+ }
+ codes++;
+ }
+ return (unsigned)-1; /* invalid key */
+}
+
+
+static irqreturn_t stmpkbd_irq_handler(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct stmpkbd_data *devdata = platform_get_drvdata(pdev);
+ u16 raw_button, normalized_button, vddio;
+ unsigned btn;
+
+ raw_button = HW_LRADC_CHn_RD(LRADC_CH0) & BM_LRADC_CHn_VALUE;
+ vddio = hw_lradc_vddio();
+ BUG_ON(vddio == 0);
+
+ normalized_button = (raw_button * TARGET_VDDIO_LRADC_VALUE) /
+ vddio;
+
+ if (normalized_button < BUTTON_PRESS_THRESHOLD &&
+ devdata->last_button < 0) {
+
+ btn = stmpkbd_decode_button(devdata->keycodes,
+ normalized_button);
+
+ if (btn < KEY_MAX) {
+ devdata->last_button = btn;
+ input_report_key(GET_INPUT_DEV(devdata),
+ devdata->last_button, !0);
+ } else
+ dev_err(&pdev->dev, "Invalid button: raw = %d, "
+ "normalized = %d, vddio = %d\n",
+ raw_button, normalized_button, vddio);
+ } else if (devdata->last_button > 0 &&
+ normalized_button >= BUTTON_PRESS_THRESHOLD) {
+
+ input_report_key(GET_INPUT_DEV(devdata),
+ devdata->last_button, 0);
+ devdata->last_button = -1;
+
+ }
+
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC0_IRQ);
+ return IRQ_HANDLED;
+}
+
+static int stmpkbd_open(struct input_dev *dev)
+{
+ /* enable clock */
+ return 0;
+}
+
+static void stmpkbd_close(struct input_dev *dev)
+{
+ /* disable clock */
+}
+
+static void stmpkbd_hwinit(struct platform_device *pdev)
+{
+ hw_lradc_init_ladder(LRADC_CH0, LRADC_DELAY_TRIGGER_BUTTON, 200);
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC0_IRQ);
+ HW_LRADC_CTRL1_SET(BM_LRADC_CTRL1_LRADC0_IRQ_EN);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_BUTTON, !0);
+}
+
+static int stmpkbd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+#ifdef CONFIG_PM
+ struct input_dev *idev = platform_get_drvdata(pdev);
+
+ hw_lradc_stop_ladder(LRADC_CH0, LRADC_DELAY_TRIGGER_BUTTON);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_BUTTON, 0);
+ hw_lradc_unuse_channel(LRADC_CH0);
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC0_IRQ_EN);
+ stmpkbd_close(idev);
+#endif
+ return 0;
+}
+
+static int stmpkbd_resume(struct platform_device *pdev)
+{
+#ifdef CONFIG_PM
+ struct input_dev *idev = platform_get_drvdata(pdev);
+
+ HW_LRADC_CTRL1_SET(BM_LRADC_CTRL1_LRADC0_IRQ_EN);
+ stmpkbd_open(idev);
+ hw_lradc_use_channel(LRADC_CH0);
+ stmpkbd_hwinit(pdev);
+#endif
+ return 0;
+}
+
+static int __devinit stmpkbd_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ int irq = platform_get_irq(pdev, 0);
+ struct stmpkbd_data *d;
+
+ /* Create and register the input driver. */
+ d = stmpkbd_data_alloc(pdev,
+ (struct stmpkbd_keypair *)pdev->dev.platform_data);
+ if (!d) {
+ dev_err(&pdev->dev, "Cannot allocate driver structures\n");
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ d->irq = irq;
+ err = request_irq(irq, stmpkbd_irq_handler,
+ IRQF_DISABLED, pdev->name, pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot request keypad IRQ\n");
+ goto err_free_dev;
+ }
+
+ platform_set_drvdata(pdev, d);
+
+ /* Register the input device */
+ err = input_register_device(GET_INPUT_DEV(d));
+ if (err)
+ goto err_free_irq;
+
+ /* these two have to be set after registering the input device */
+ d->input->rep[REP_DELAY] = delay1;
+ d->input->rep[REP_PERIOD] = delay2;
+
+ hw_lradc_use_channel(LRADC_CH0);
+ stmpkbd_hwinit(pdev);
+
+ return 0;
+
+err_free_irq:
+ platform_set_drvdata(pdev, NULL);
+ free_irq(irq, pdev);
+err_free_dev:
+ stmpkbd_data_free(d);
+err_out:
+ return err;
+}
+
+static int __devexit stmpkbd_remove(struct platform_device *pdev)
+{
+ struct stmpkbd_data *d = platform_get_drvdata(pdev);
+
+ hw_lradc_unuse_channel(LRADC_CH0);
+ input_unregister_device(GET_INPUT_DEV(d));
+ free_irq(d->irq, pdev);
+ stmpkbd_data_free(d);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver stmpkbd_driver = {
+ .probe = stmpkbd_probe,
+ .remove = __devexit_p(stmpkbd_remove),
+ .suspend = stmpkbd_suspend,
+ .resume = stmpkbd_resume,
+ .driver = {
+ .name = "stmp3xxx-keyboard",
+ },
+};
+
+static int __init stmpkbd_init(void)
+{
+ return platform_driver_register(&stmpkbd_driver);
+}
+
+static void __exit stmpkbd_exit(void)
+{
+ platform_driver_unregister(&stmpkbd_driver);
+}
+
+module_init(stmpkbd_init);
+module_exit(stmpkbd_exit);
+MODULE_DESCRIPTION("Freescale STMP3xxxx keyboard driver");
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 199055db5082..16eaf1408090 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -40,6 +40,15 @@ config INPUT_M68K_BEEP
tristate "M68k Beeper support"
depends on M68K
+config INPUT_STMP3XXX_ROTDEC
+ tristate "STMP3xxx Rotary Decoder support"
+ depends on MACH_STMP378X
+ select INPUT_POLLDEV
+ help
+ Say Y here for support for the rotary decoder on STMP3xxx
+
+ If compiled as a module, it will be called stmp3xxx_rotdec
+
config INPUT_APANEL
tristate "Fujitsu Lifebook Application Panel buttons"
depends on X86 && I2C && LEDS_CLASS
@@ -220,4 +229,13 @@ config HP_SDC_RTC
Say Y here if you want to support the built-in real time clock
of the HP SDC controller.
+config INPUT_MMA7455L
+ tristate "Freescale MMA7455L 3-axis accelerometer"
+ depends on I2C
+ help
+ I2C driver for the Freescale MMA7455L 3-axis accelerometer.
+
+ The userspace interface is a 3-axis (X/Y/Z) relative movement
+ Linux input device, reporting REL_[XYZ] events.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index d7db2aeb8a98..c3bd6d5c5f11 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
+obj-$(CONFIG_INPUT_STMP3XXX_ROTDEC) += stmp3xxx_rotdec.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
@@ -21,3 +22,4 @@ obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
+obj-$(CONFIG_INPUT_MMA7455L) += mma7455l.o
diff --git a/drivers/input/misc/mma7455l.c b/drivers/input/misc/mma7455l.c
new file mode 100644
index 000000000000..f5113139e528
--- /dev/null
+++ b/drivers/input/misc/mma7455l.c
@@ -0,0 +1,647 @@
+/* Linux kernel driver for the Freescale MMA7455L 3-axis accelerometer
+ *
+ * Copyright (C) 2009 by Always Innovating, Inc.
+ * Copyright 2009 Digi International, Inc. All Rights Reserved.
+ * Author: Gregoire Gentil <gregoire@gentil.com>
+ * Author: Tim Yamin <plasm@roo.me.uk>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 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
+ *
+ */
+
+/*
+ * What this driver doesn't yet support:
+ *
+ * - INT2 handling
+ * - Pulse detection (and the sysctls to control it)
+ * - 10-bit measurement
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/gpio.h>
+
+#include <linux/mma7455l.h>
+#include <linux/i2c.h>
+
+#define MMA7455L_WHOAMI_MAGIC 0x55
+
+enum mma7455l_reg {
+ MMA7455L_REG_XOUTL = 0x00,
+ MMA7455L_REG_XOUTH = 0x01,
+ MMA7455L_REG_YOUTL = 0x02,
+ MMA7455L_REG_YOUTH = 0x03,
+ MMA7455L_REG_ZOUTL = 0x04,
+ MMA7455L_REG_ZOUTH = 0x05,
+ MMA7455L_REG_XOUT8 = 0x06,
+ MMA7455L_REG_YOUT8 = 0x07,
+ MMA7455L_REG_ZOUT8 = 0x08,
+ MMA7455L_REG_STATUS = 0x09,
+ MMA7455L_REG_DETSRC = 0x0a,
+ MMA7455L_REG_TOUT = 0x0b,
+ MMA7455L_REG_RESERVED1 = 0x0c,
+ MMA7455L_REG_I2CAD = 0x0d,
+ MMA7455L_REG_USRINF = 0x0e,
+ MMA7455L_REG_WHOAMI = 0x0f,
+ MMA7455L_REG_XOFFL = 0x10,
+ MMA7455L_REG_XOFFH = 0x11,
+ MMA7455L_REG_YOFFL = 0x12,
+ MMA7455L_REG_YOFFH = 0x13,
+ MMA7455L_REG_ZOFFL = 0x14,
+ MMA7455L_REG_ZOFFH = 0x15,
+ MMA7455L_REG_MCTL = 0x16,
+ MMA7455L_REG_INTRST = 0x17,
+ MMA7455L_REG_CTL1 = 0x18,
+ MMA7455L_REG_CTL2 = 0x19,
+ MMA7455L_REG_LDTH = 0x1a,
+ MMA7455L_REG_PDTH = 0x1b,
+ MMA7455L_REG_PW = 0x1c,
+ MMA7455L_REG_LT = 0x1d,
+ MMA7455L_REG_TW = 0x1e,
+ MMA7455L_REG_RESERVED2 = 0x1f,
+};
+
+enum mma7455l_reg_status {
+ MMA7455L_STATUS_XDA = 0x08,
+ MMA7455L_STATUS_YDA = 0x10,
+ MMA7455L_STATUS_ZDA = 0x20,
+};
+
+enum mma7455l_mode {
+ MMA7455L_MODE_STANDBY = 0,
+ MMA7455L_MODE_MEASUREMENT = 1,
+ MMA7455L_MODE_LEVELDETECTION = 0x42, /* Set DRPD to on */
+ MMA7455L_MODE_PULSEDETECTION = 0x43, /* Set DRPD to on */
+ MMA7455L_MODE_MASK = 0x43,
+};
+
+enum mma7455l_gselect {
+ MMA7455L_GSELECT_8 = 0x0,
+ MMA7455L_GSELECT_2 = 0x4,
+ MMA7455L_GSELECT_4 = 0x8,
+ MMA7455L_GSELECT_MASK = 0xC,
+};
+
+/* FIXME */
+#define MMA7455L_F_FS 0x0020 /* ADC full scale */
+
+
+struct mma7455l_info {
+ /* Calibration data */
+ s16 calibration_x;
+ s16 calibration_y;
+ s16 calibration_z;
+
+ /* Defaults to I2c access */
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct mutex lock; /* Struct mutex lock */
+ struct delayed_work work;
+
+ u8 mode;
+ u8 gSelect;
+
+ u8 flags;
+ u8 working;
+};
+
+/* lowlevel register access functions */
+
+#define WRITE_BIT (1 << 7)
+#define ADDR_SHIFT 1
+
+/* Defaults to I2c access */
+static inline u_int8_t __reg_read(struct mma7455l_info *mma, u_int8_t reg)
+{
+ unsigned char buf;
+
+ struct i2c_msg msgs[] = {
+ {mma->client->addr, 0, 1, &reg}, /* setup read ptr */
+ {mma->client->addr, I2C_M_RD, 1, &buf}, /* read date */
+ };
+
+ /* read register */
+ if ((i2c_transfer(mma->client->adapter, &msgs[0], 2)) != 2) {
+ dev_err(&mma->client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+ return buf;
+}
+
+static inline int __reg_write(struct mma7455l_info *mma,
+ u_int8_t reg, u_int8_t val)
+{
+
+ struct i2c_msg msgs[] = {
+ {mma->client->addr, 0, 1, &reg}, /* setup read ptr */
+ {mma->client->addr, 0, 1, &val}, /* write value */
+ };
+
+ /* Write register */
+ if ((i2c_transfer(mma->client->adapter, &msgs[0], 2)) != 2) {
+ dev_err(&mma->client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+ return 0;
+}
+
+static u_int8_t reg_read(struct mma7455l_info *mma, u_int8_t reg)
+{
+ u_int8_t ret;
+
+ mutex_lock(&mma->lock);
+ ret = __reg_read(mma, reg);
+ mutex_unlock(&mma->lock);
+
+ return ret;
+}
+
+static s16 __reg_read_10(struct mma7455l_info *mma, u8 reg1, u8 reg2)
+{
+ u8 v1, v2;
+
+ v1 = __reg_read(mma, reg1);
+ v2 = __reg_read(mma, reg2);
+
+ return (v2 & 0x4) << 13 | (v2 & 0x3) << 8 | v1;
+}
+
+static int reg_write(struct mma7455l_info *mma, u_int8_t reg, u_int8_t val)
+{
+ int ret;
+
+ mutex_lock(&mma->lock);
+ ret = __reg_write(mma, reg, val);
+ mutex_unlock(&mma->lock);
+
+ return ret;
+}
+
+static s16 __reg_write_10(struct mma7455l_info *mma,
+ u8 reg1, u8 reg2, s16 value)
+{
+ int ret;
+ u8 v1, v2;
+
+ v1 = value & 0xFF;
+ if (value < 0)
+ v2 = ((value >> 8) & 0x3) | 0x4;
+ else
+ v2 = 0;
+
+ ret = __reg_write(mma, reg1, v1);
+ ret = __reg_write(mma, reg2, v2);
+ return ret;
+}
+
+static void mma7455l_work(struct work_struct *work)
+{
+ struct mma7455l_info *mma =
+ container_of(work, struct mma7455l_info, work.work);
+
+ s8 val;
+ mma->working = 1;
+
+ /* FIXME: 10 bit accuracy? */
+ if (!(mma->flags & MMA7455L_STATUS_XDA)) {
+ val = reg_read(mma, MMA7455L_REG_XOUT8);
+ input_report_abs(mma->input_dev, ABS_X, val);
+ }
+ if (!(mma->flags & MMA7455L_STATUS_YDA)) {
+ val = reg_read(mma, MMA7455L_REG_YOUT8);
+ input_report_abs(mma->input_dev, ABS_Y, val);
+ }
+ if (!(mma->flags & MMA7455L_STATUS_ZDA)) {
+ val = reg_read(mma, MMA7455L_REG_ZOUT8);
+ input_report_abs(mma->input_dev, ABS_Z, val);
+ }
+
+ mma->working = 0;
+ input_sync(mma->input_dev);
+ put_device(&mma->client->dev);
+
+ /* Enable IRQ and clear out interrupt */
+ reg_write(mma, MMA7455L_REG_INTRST, 0x3);
+ reg_write(mma, MMA7455L_REG_INTRST, 0x0);
+
+ enable_irq(mma->client->irq);
+}
+
+static void mma7455l_schedule_work(struct mma7455l_info *mma)
+{
+ int status;
+
+ get_device(&mma->client->dev);
+ status = schedule_delayed_work(&mma->work, HZ / 10);
+}
+
+static irqreturn_t mma7455l_interrupt(int irq, void *_mma)
+{
+ struct mma7455l_info *mma = _mma;
+ mma7455l_schedule_work(mma);
+
+ /* Disable any further interrupts until we have processed
+ * the current one */
+ disable_irq(mma->client->irq);
+ return IRQ_HANDLED;
+}
+
+/* sysfs */
+
+static void get_mode(struct mma7455l_info *mma, u8 *mode, u8 *gSelect)
+{
+ u8 tmp = reg_read(mma, MMA7455L_REG_MCTL);
+
+ *mode = tmp & MMA7455L_MODE_MASK;
+ *gSelect = tmp & MMA7455L_GSELECT_MASK;
+}
+
+static void set_mode(struct mma7455l_info *mma, u8 mode, u8 gSelect)
+{
+ reg_write(mma, MMA7455L_REG_MCTL, mode | gSelect);
+}
+
+static void update_mode(struct mma7455l_info *mma, u8 mode, u8 gSelect)
+{
+ mma->mode = mode;
+ mma->gSelect = gSelect;
+
+ reg_write(mma, MMA7455L_REG_MCTL, mma->mode | mma->gSelect);
+}
+
+static ssize_t show_measure(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mma7455l_info *mma = dev_get_drvdata(dev);
+ s8 x, y, z;
+ u8 old_Mode, old_gSelect;
+
+ get_mode(mma, &old_Mode, &old_gSelect);
+ set_mode(mma, MMA7455L_MODE_MEASUREMENT, MMA7455L_GSELECT_2);
+
+ while (reg_read(mma, MMA7455L_REG_STATUS) == 0)
+ msleep(10);
+
+ x = reg_read(mma, MMA7455L_REG_XOUT8);
+ y = reg_read(mma, MMA7455L_REG_YOUT8);
+ z = reg_read(mma, MMA7455L_REG_ZOUT8);
+
+ set_mode(mma, old_Mode, old_gSelect);
+ return sprintf(buf, "%d %d %d\n", x, y, z);
+}
+
+static ssize_t show_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mma7455l_info *mma = dev_get_drvdata(dev);
+
+ switch (mma->mode) {
+ case MMA7455L_MODE_STANDBY:
+ return sprintf(buf, "Standby\n");
+ break;
+ case MMA7455L_MODE_MEASUREMENT:
+ return sprintf(buf, "Measurement\n");
+ break;
+ case MMA7455L_MODE_LEVELDETECTION:
+ return sprintf(buf, "Level Detection\n");
+ break;
+ case MMA7455L_MODE_PULSEDETECTION:
+ return sprintf(buf, "Pulse Detection\n");
+ break;
+ }
+
+ return sprintf(buf, "Unknown mode!\n");
+}
+
+static ssize_t show_gSelect(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mma7455l_info *mma = dev_get_drvdata(dev);
+
+ switch (mma->gSelect) {
+ case MMA7455L_GSELECT_8:
+ return sprintf(buf, "8\n");
+ break;
+ case MMA7455L_GSELECT_4:
+ return sprintf(buf, "4\n");
+ break;
+ case MMA7455L_GSELECT_2:
+ return sprintf(buf, "2\n");
+ break;
+ }
+
+ return sprintf(buf, "Unknown gSelect!\n");
+}
+
+static ssize_t show_level_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mma7455l_info *mma = dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", reg_read(mma, MMA7455L_REG_LDTH));
+}
+
+static ssize_t show_calibration(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ s16 x, y, z;
+ struct mma7455l_info *mma = dev_get_drvdata(dev);
+
+ mutex_lock(&mma->lock);
+ x = __reg_read_10(mma, MMA7455L_REG_XOFFL, MMA7455L_REG_XOFFH);
+ y = __reg_read_10(mma, MMA7455L_REG_YOFFL, MMA7455L_REG_YOFFH);
+ z = __reg_read_10(mma, MMA7455L_REG_ZOFFL, MMA7455L_REG_ZOFFH);
+ mutex_unlock(&mma->lock);
+
+ return sprintf(buf, "%d %d %d\n", x, y, z);
+}
+
+static ssize_t write_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mma7455l_info *mma = dev_get_drvdata(dev);
+
+ if (!strncmp(buf, "Standby", count))
+ update_mode(mma, MMA7455L_MODE_STANDBY, mma->gSelect);
+ else if (!strncmp(buf, "Measurement", count))
+ update_mode(mma, MMA7455L_MODE_MEASUREMENT, mma->gSelect);
+ else if (!strncmp(buf, "Level Detection", count))
+ update_mode(mma, MMA7455L_MODE_LEVELDETECTION, mma->gSelect);
+ else if (!strncmp(buf, "Pulse Detection", count))
+ update_mode(mma, MMA7455L_MODE_PULSEDETECTION, mma->gSelect);
+
+ return count;
+}
+
+static ssize_t write_gSelect(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long v;
+ struct mma7455l_info *mma = dev_get_drvdata(dev);
+
+ if (strict_strtoul(buf, 10, &v) == 0) {
+ switch (v) {
+ case 8:
+ update_mode(mma, mma->mode, MMA7455L_GSELECT_8);
+ break;
+ case 4:
+ update_mode(mma, mma->mode, MMA7455L_GSELECT_4);
+ break;
+ case 2:
+ update_mode(mma, mma->mode, MMA7455L_GSELECT_2);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return count;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t write_level_threshold(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long v;
+ struct mma7455l_info *mma = dev_get_drvdata(dev);
+
+ if (strict_strtoul(buf, 10, &v) == 0) {
+ if (v <= 0xFF) {
+ reg_write(mma, MMA7455L_REG_LDTH, v);
+ return count;
+ } else
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t write_calibration(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int x, y, z;
+ struct mma7455l_info *mma = dev_get_drvdata(dev);
+
+ if (sscanf(buf, "%d %d %d", &x, &y, &z) == 3) {
+ mutex_lock(&mma->lock);
+ __reg_write_10(mma, MMA7455L_REG_XOFFL, MMA7455L_REG_XOFFH, x);
+ __reg_write_10(mma, MMA7455L_REG_YOFFL, MMA7455L_REG_YOFFH, y);
+ __reg_write_10(mma, MMA7455L_REG_ZOFFL, MMA7455L_REG_ZOFFH, z);
+ mutex_unlock(&mma->lock);
+
+ return count;
+ }
+
+ return -EINVAL;
+}
+
+static DEVICE_ATTR(measure, S_IRUGO, show_measure, NULL);
+static DEVICE_ATTR(mode, S_IRUGO | S_IWUGO, show_mode, write_mode);
+static DEVICE_ATTR(gSelect, S_IRUGO | S_IWUGO, show_gSelect, write_gSelect);
+static DEVICE_ATTR(level_threshold, S_IRUGO | S_IWUGO, show_level_threshold,
+ write_level_threshold);
+static DEVICE_ATTR(calibration, S_IRUGO | S_IWUGO, show_calibration,
+ write_calibration);
+
+static struct attribute *mma7455l_sysfs_entries[] = {
+ &dev_attr_measure.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_gSelect.attr,
+ &dev_attr_level_threshold.attr,
+ &dev_attr_calibration.attr,
+ NULL
+};
+
+static struct attribute_group mma7455l_attr_group = {
+ .attrs = mma7455l_sysfs_entries,
+};
+
+/* input device handling and driver core interaction */
+static int mma7455l_input_open(struct input_dev *inp)
+{
+ struct mma7455l_info *mma = input_get_drvdata(inp);
+ if (mma->mode == MMA7455L_MODE_STANDBY)
+ update_mode(mma, MMA7455L_MODE_MEASUREMENT, mma->gSelect);
+
+ return 0;
+}
+
+static void mma7455l_input_close(struct input_dev *inp)
+{
+ struct mma7455l_info *mma = input_get_drvdata(inp);
+ update_mode(mma, MMA7455L_MODE_STANDBY, MMA7455L_GSELECT_2);
+}
+
+static int __devinit mma7455l_probe(struct i2c_client * client , const struct i2c_device_id * id )
+{
+ int rc;
+ struct mma7455l_info *mma;
+ u_int8_t wai;
+
+ mma = kzalloc(sizeof(*mma), GFP_KERNEL);
+ if (!mma)
+ return -ENOMEM;
+
+ mutex_init(&mma->lock);
+ INIT_DELAYED_WORK(&mma->work, mma7455l_work);
+ mma->client = client;
+ mma->flags = 0;
+ mma->working = 0;
+
+ /* Set initial calibration data */
+ mma->calibration_x = 0;
+ mma->calibration_y = 0;
+ mma->calibration_z = 0;
+
+ i2c_set_clientdata(client, mma);
+
+ wai = reg_read(mma, MMA7455L_REG_WHOAMI);
+ if (wai != MMA7455L_WHOAMI_MAGIC) {
+ printk(KERN_ERR
+ "mma7455l unknown whoami signature 0x%02x\n", wai);
+ dev_set_drvdata(&client->dev, NULL);
+ kfree(mma);
+ return -ENODEV;
+ }
+
+ rc = request_irq(client->irq, mma7455l_interrupt,
+ IRQF_TRIGGER_HIGH, "mma7455l", mma);
+ if (rc < 0) {
+ dev_err(&client->dev, "mma7455l error requesting IRQ %d\n",
+ client->irq);
+ /* FIXME */
+ return rc;
+ }
+
+ rc = sysfs_create_group(&client->dev.kobj, &mma7455l_attr_group);
+ if (rc) {
+ dev_err(&client->dev, "error creating sysfs group\n");
+ return rc;
+ }
+
+ /* initialize input layer details */
+ mma->input_dev = input_allocate_device();
+ if (!mma->input_dev) {
+ dev_err(&client->dev,
+ "mma7455l Unable to allocate input device\n");
+ /* FIXME */
+ }
+
+ set_bit(EV_ABS, mma->input_dev->evbit);
+ set_bit(ABS_X, mma->input_dev->absbit);
+ set_bit(ABS_Y, mma->input_dev->absbit);
+ set_bit(ABS_Z, mma->input_dev->absbit);
+
+ input_set_drvdata(mma->input_dev, mma);
+ mma->input_dev->name = "MMA7455L";
+ mma->input_dev->open = mma7455l_input_open;
+ mma->input_dev->close = mma7455l_input_close;
+
+ rc = input_register_device(mma->input_dev);
+ if (!rc) {
+ update_mode(mma, MMA7455L_MODE_STANDBY, MMA7455L_GSELECT_2);
+
+ mutex_lock(&mma->lock);
+ __reg_write_10(mma, MMA7455L_REG_XOFFL,
+ MMA7455L_REG_XOFFH, mma->calibration_x);
+ __reg_write_10(mma, MMA7455L_REG_YOFFL,
+ MMA7455L_REG_YOFFH, mma->calibration_y);
+ __reg_write_10(mma, MMA7455L_REG_ZOFFL,
+ MMA7455L_REG_ZOFFH, mma->calibration_z);
+ mutex_unlock(&mma->lock);
+
+ return 0;
+ }
+
+ input_free_device(mma->input_dev);
+ return rc;
+}
+
+static int __devexit mma7455l_remove(struct i2c_client *client)
+{
+ struct mma7455l_info *mma = dev_get_drvdata(&client->dev);
+
+ sysfs_remove_group(&client->dev.kobj, &mma7455l_attr_group);
+ input_unregister_device(mma->input_dev);
+ dev_set_drvdata(&client->dev, NULL);
+ kfree(mma);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mma7455l_suspend(struct i2c_client *client, pm_message_t message)
+{
+ struct mma7455l_info *mma = dev_get_drvdata(&client->dev);
+ get_mode(mma, &mma->mode, &mma->gSelect);
+ set_mode(mma, MMA7455L_MODE_STANDBY, MMA7455L_GSELECT_2);
+
+ return 0;
+}
+
+static int mma7455l_resume(struct i2c_client *client)
+{
+ struct mma7455l_info *mma = dev_get_drvdata(&client->dev);
+ update_mode(mma, mma->mode, mma->gSelect);
+
+ return 0;
+}
+#else
+#define mma7455l_suspend NULL
+#define mma7455l_resume NULL
+#endif
+
+static const struct i2c_device_id mma7455l_id[] = {
+ { "mma7455l", 1 },
+ { },
+};
+
+static struct i2c_driver mma7455l_driver = {
+ .driver = {
+ .name = "mma7455l",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = mma7455l_probe,
+ .remove = __devexit_p(mma7455l_remove),
+ .suspend = mma7455l_suspend,
+ .resume = mma7455l_resume,
+ .id_table = mma7455l_id,
+};
+
+static int __init mma7455l_init(void)
+{
+ return i2c_add_driver(&mma7455l_driver);
+}
+
+static void __exit mma7455l_exit(void)
+{
+ i2c_del_driver(&mma7455l_driver);
+}
+
+MODULE_AUTHOR("Gregoire Gentil <gregoire@gentil.com>");
+MODULE_LICENSE("GPL");
+
+module_init(mma7455l_init);
diff --git a/drivers/input/misc/stmp3xxx_rotdec.c b/drivers/input/misc/stmp3xxx_rotdec.c
new file mode 100644
index 000000000000..505a79b25769
--- /dev/null
+++ b/drivers/input/misc/stmp3xxx_rotdec.c
@@ -0,0 +1,171 @@
+/*
+ * Freescale STMP3XXX Rotary Encoder Driver
+ *
+ * Author: Drew Benedetti <drewb@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/input-polldev.h>
+#include <mach/regs-timrot.h>
+#include <mach/rotdec.h>
+
+static int relative;
+static unsigned int poll_interval = 500;
+
+void stmp3xxx_rotdec_flush(struct input_polled_dev *dev)
+{
+ /* in relative mode, reading the counter resets it */
+ if (relative)
+ HW_TIMROT_ROTCOUNT_RD();
+}
+
+void stmp3xxx_rotdec_poll(struct input_polled_dev *dev)
+{
+ s16 cnt = HW_TIMROT_ROTCOUNT_RD() & BM_TIMROT_ROTCOUNT_UPDOWN;
+ if (relative)
+ input_report_rel(dev->input, REL_WHEEL, cnt);
+ else
+ input_report_abs(dev->input, ABS_WHEEL, cnt);
+}
+
+struct input_polled_dev *rotdec;
+static u32 rotctrl;
+
+static int stmp3xxx_rotdec_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ /* save original state of HW_TIMROT_ROTCTRL */
+ rotctrl = HW_TIMROT_ROTCTRL_RD();
+
+ if (!(rotctrl & BM_TIMROT_ROTCTRL_ROTARY_PRESENT)) {
+ dev_info(&pdev->dev, "No rotary decoder present\n");
+ rc = -ENODEV;
+ goto err_rotdec_present;
+ } else {
+ /* I had to add some extra line breaks in here
+ * to avoid lines >80 chars wide
+ */
+ HW_TIMROT_ROTCTRL_WR(
+ BF_TIMROT_ROTCTRL_DIVIDER(0x0) | /* 32kHz divider - 1 */
+ BF_TIMROT_ROTCTRL_OVERSAMPLE(
+ BV_TIMROT_ROTCTRL_OVERSAMPLE__2X) |
+ BF_TIMROT_ROTCTRL_SELECT_B(
+ BV_TIMROT_ROTCTRL_SELECT_B__ROTARYB) |
+ BF_TIMROT_ROTCTRL_SELECT_A(
+ BV_TIMROT_ROTCTRL_SELECT_A__ROTARYA)
+ );
+ HW_TIMROT_ROTCTRL_CLR(
+ BM_TIMROT_ROTCTRL_POLARITY_B |
+ BM_TIMROT_ROTCTRL_POLARITY_A
+ );
+
+ if (relative)
+ HW_TIMROT_ROTCTRL_SET(BM_TIMROT_ROTCTRL_RELATIVE);
+ else
+ HW_TIMROT_ROTCTRL_CLR(BM_TIMROT_ROTCTRL_RELATIVE);
+
+ rc = rotdec_pinmux_request();
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Pin request failed (err=%d)\n", rc);
+ goto err_pinmux;
+ }
+
+ /* set up input_polled_dev */
+ rotdec = input_allocate_polled_device();
+ if (!rotdec) {
+ dev_err(&pdev->dev,
+ "Unable to allocate polled device\n");
+ rc = -ENOMEM;
+ goto err_alloc_polldev;
+ }
+ rotdec->flush = stmp3xxx_rotdec_flush;
+ rotdec->poll = stmp3xxx_rotdec_poll;
+ rotdec->poll_interval = poll_interval; /* msec */
+
+ rotdec->input->name = "stmp3xxx-rotdec";
+ if (relative)
+ input_set_capability(rotdec->input, EV_REL, REL_WHEEL);
+ else {
+ input_set_capability(rotdec->input, EV_ABS, ABS_WHEEL);
+ input_set_abs_params(rotdec->input, ABS_WHEEL,
+ -32768, 32767, 0, 0);
+ }
+
+ rc = input_register_polled_device(rotdec);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Unable to register rotary decoder (err=%d)\n",
+ rc);
+ goto err_reg_polldev;
+ }
+ }
+
+ return 0;
+
+err_reg_polldev:
+ input_free_polled_device(rotdec);
+err_alloc_polldev:
+ rotdec_pinmux_free();
+err_pinmux:
+ /* restore original register state */
+ HW_TIMROT_ROTCTRL_WR(rotctrl);
+
+err_rotdec_present:
+ return rc;
+}
+
+static int stmp3xxx_rotdec_remove(struct platform_device *pdev)
+{
+ input_unregister_polled_device(rotdec);
+ input_free_polled_device(rotdec);
+
+ rotdec_pinmux_free();
+
+ /* restore original register state */
+ HW_TIMROT_ROTCTRL_WR(rotctrl);
+
+ return 0;
+}
+
+static struct platform_driver stmp3xxx_rotdec_driver = {
+ .probe = stmp3xxx_rotdec_probe,
+ .remove = stmp3xxx_rotdec_remove,
+ .driver = {
+ .name = "stmp3xxx-rotdec",
+ },
+};
+
+static int __init stmp3xxx_rotdec_init(void)
+{
+ return platform_driver_register(&stmp3xxx_rotdec_driver);
+}
+
+static void __exit stmp3xxx_rotdec_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_rotdec_driver);
+}
+
+module_init(stmp3xxx_rotdec_init);
+module_exit(stmp3xxx_rotdec_exit);
+
+module_param(relative, bool, 0600);
+module_param(poll_interval, uint, 0600);
+
+MODULE_AUTHOR("Drew Benedetti <drewb@embeddedalley.com>");
+MODULE_DESCRIPTION("STMP3xxx rotary decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3d1ab8fa9acc..98c57a981139 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -71,6 +71,18 @@ config TOUCHSCREEN_FUJITSU
To compile this driver as a module, choose M here: the
module will be called fujitsu-ts.
+config TOUCHSCREEN_S3C
+ tristate "S3C touchscreen driver"
+ depends on ARCH_S3C2410
+ help
+ Say Y here to enable the driver for the touchscreen on the
+ S3C SMDK board.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s3c24xx_ts.
+
config TOUCHSCREEN_GUNZE
tristate "Gunze AHL-51S touchscreen"
select SERIO
@@ -150,6 +162,47 @@ config TOUCHSCREEN_HP7XX
To compile this driver as a module, choose M here: the
module will be called jornada720_ts.
+config TOUCHSCREEN_MXC
+ tristate "MXC touchscreen input driver"
+ depends on MXC_MC13783_ADC || MXC_MC13892_ADC
+ help
+ Say Y here if you have an MXC based board with touchscreen
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxc_ts.
+
+config TOUCHSCREEN_IMX_ADC
+ tristate "Freescale i.MX ADC touchscreen input driver"
+ depends on IMX_ADC
+ help
+ Say Y here if you have a Freescale i.MX based board with a
+ touchscreen interfaced to the processor's integrated ADC.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx_adc_ts.
+
+config TOUCHSCREEN_TSC2007
+ tristate "TI Touch Screen Controller Chip TSC2007"
+ depends on ARCH_MX51 || ARCH_MX37 || MACH_MX35_3DS
+ help
+ If you say yes here you get support for TSC2007 touch screen controller chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called tsc2007.
+
+config TOUCHSCREEN_STMP3XXX
+ tristate "STMP3XXX LRADC-based touchscreen"
+ depends on ARCH_STMP3XXX
+ select SERIO
+ help
+ Say Y here if you want to enable TMP3XXX LRADC-based touchscreen.
+ module will be called stmp3xxx_ts.
+
config TOUCHSCREEN_HTCPEN
tristate "HTC Shift X9500 touchscreen"
depends on ISA
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 15cf29079489..44febf8308bc 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -20,14 +20,19 @@ obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
+obj-$(CONFIG_TOUCHSCREEN_MXC) += mxc_ts.o
+obj-$(CONFIG_TOUCHSCREEN_IMX_ADC) += imx_adc_ts.o
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_S3C) += s3c24xx_ts.o
+obj-$(CONFIG_TOUCHSCREEN_STMP3XXX) += stmp3xxx_ts.o
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index b9b7fc6ff1eb..5ebfda209fb4 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -17,14 +17,11 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <linux/hwmon.h>
+#include <linux/device.h>
#include <linux/init.h>
-#include <linux/err.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
#include <asm/irq.h>
@@ -33,9 +30,7 @@
/*
* This code has been heavily tested on a Nokia 770, and lightly
* tested on other ads7846 devices (OSK/Mistral, Lubbock).
- * TSC2046 is just newer ads7846 silicon.
- * Support for ads7843 tested on Atmel at91sam926x-EK.
- * Support for ads7845 has only been stubbed in.
+ * Support for ads7843 and ads7845 has only been stubbed in.
*
* IRQ handling needs a workaround because of a shortcoming in handling
* edge triggered IRQs on some platforms like the OMAP1/2. These
@@ -51,8 +46,7 @@
* files.
*/
-#define TS_POLL_DELAY (1 * 1000000) /* ns delay before the first sample */
-#define TS_POLL_PERIOD (5 * 1000000) /* ns delay between samples */
+#define TS_POLL_PERIOD msecs_to_jiffies(10)
/* this driver doesn't aim at the peak continuous sample rate */
#define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */)
@@ -61,72 +55,57 @@ struct ts_event {
/* For portability, we can't read 12 bit values using SPI (which
* would make the controller deliver them as native byteorder u16
* with msbs zeroed). Instead, we read them as two 8-bit values,
- * *** WHICH NEED BYTESWAPPING *** and range adjustment.
+ * which need byteswapping then range adjustment.
*/
- u16 x;
- u16 y;
- u16 z1, z2;
- int ignore;
-};
-
-/*
- * We allocate this separately to avoid cache line sharing issues when
- * driver is used with DMA-based SPI controllers (like atmel_spi) on
- * systems where main memory is not DMA-coherent (most non-x86 boards).
- */
-struct ads7846_packet {
- u8 read_x, read_y, read_z1, read_z2, pwrdown;
- u16 dummy; /* for the pwrdown read */
- struct ts_event tc;
+ __be16 x;
+ __be16 y;
+ __be16 z1, z2;
+ int ignore;
};
struct ads7846 {
struct input_dev *input;
char phys[32];
- struct spi_device *spi;
-
-#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
- struct attribute_group *attr_group;
- struct device *hwmon;
-#endif
+ u32 *txbuf;
+ u32 *rxbuf;
+ u8 buflen;
+ u8 skip_samples;
+ u16 rotate;
+ struct spi_device *spi;
u16 model;
- u16 vref_mv;
u16 vref_delay_usecs;
u16 x_plate_ohms;
u16 pressure_max;
- struct ads7846_packet *packet;
+ u8 read_x, read_y, read_z1, read_z2, pwrdown;
+ u16 zerro; /* to send zerros while receiving */
+ u16 dummy; /* for the pwrdown read */
+ struct ts_event tc;
- struct spi_transfer xfer[18];
+ struct spi_transfer xfer[10];
struct spi_message msg[5];
struct spi_message *last_msg;
int msg_idx;
int read_cnt;
int read_rep;
int last_read;
+ int skip_this_sample;
u16 debounce_max;
u16 debounce_tol;
u16 debounce_rep;
- u16 penirq_recheck_delay_usecs;
-
spinlock_t lock;
- struct hrtimer timer;
+ struct timer_list timer; /* P: lock */
unsigned pendown:1; /* P: lock */
unsigned pending:1; /* P: lock */
// FIXME remove "irq_disabled"
unsigned irq_disabled:1; /* P: lock */
unsigned disabled:1;
- unsigned is_suspended:1;
- int (*filter)(void *data, int data_idx, int *val);
- void *filter_data;
- void (*filter_cleanup)(void *data);
int (*get_pendown_state)(void);
- int gpio_pendown;
};
/* leave chip selected when we're done, for quicker re-select? */
@@ -136,6 +115,7 @@ struct ads7846 {
#define CS_CHANGE(xfer) ((xfer).cs_change = 0)
#endif
+
/*--------------------------------------------------------------------------*/
/* The ADS7846 has touchscreen and other sensors.
@@ -162,16 +142,19 @@ struct ads7846 {
#define MAX_12BIT ((1<<12)-1)
/* leave ADC powered up (disables penirq) between differential samples */
-#define READ_12BIT_DFR(x, adc, vref) (ADS_START | ADS_A2A1A0_d_ ## x \
- | ADS_12_BIT | ADS_DFR | \
- (adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0))
+#define READ_12BIT_DFR(x) (ADS_START | ADS_A2A1A0_d_ ## x \
+ | ADS_12_BIT | ADS_DFR)
+
+#define READ_Y (READ_12BIT_DFR(y) | ADS_PD10_ADC_ON)
+#define READ_Z1 (READ_12BIT_DFR(z1) | ADS_PD10_ADC_ON)
+#define READ_Z2 (READ_12BIT_DFR(z2) | ADS_PD10_ADC_ON)
-#define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref))
-#define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref))
-#define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref))
+#define READ_X (READ_12BIT_DFR(x) | ADS_PD10_ADC_ON)
+#define PWRDOWN (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) /* LAST */
-#define READ_X(vref) (READ_12BIT_DFR(x, 1, vref))
-#define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */
+/* alternate ads7843 commands */
+#define ALT_READ_Y (READ_12BIT_DFR(y) | ADS_PD10_ALL_ON)
+#define ALT_READ_X (READ_12BIT_DFR(x) | ADS_PD10_ALL_ON)
/* single-ended samples need to first power up reference voltage;
* we leave both ADC and VREF powered
@@ -179,15 +162,20 @@ struct ads7846 {
#define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \
| ADS_12_BIT | ADS_SER)
-#define REF_ON (READ_12BIT_DFR(x, 1, 1))
-#define REF_OFF (READ_12BIT_DFR(y, 0, 0))
+#define REF_ON (READ_12BIT_DFR(x) | ADS_PD10_ALL_ON)
+#define REF_OFF (READ_12BIT_DFR(y) | ADS_PD10_PDOWN)
+
+#define MAX_BUF_SAMPLE_LEN (20)
+/* Following configuration should be done in the platform configuration */
+#define SCREEN_LANDSCAPE 1
+#undef SCREEN_PORTRAIT
+#define MAX_DIFF_BETWEEN_SAMPLES 100
+
/*--------------------------------------------------------------------------*/
/*
* Non-touchscreen sensors only use single-ended conversions.
- * The range is GND..vREF. The ads7843 and ads7835 must use external vREF;
- * ads7846 lets that pin be unconnected, to use internal vREF.
*/
struct ser_req {
@@ -195,6 +183,7 @@ struct ser_req {
u8 command;
u8 ref_off;
u16 scratch;
+ u16 zerro;
__be16 sample;
struct spi_message msg;
struct spi_transfer xfer[6];
@@ -206,62 +195,94 @@ static void ads7846_disable(struct ads7846 *ts);
static int device_suspended(struct device *dev)
{
struct ads7846 *ts = dev_get_drvdata(dev);
- return ts->is_suspended || ts->disabled;
+ return dev->power.power_state.event != PM_EVENT_ON || ts->disabled;
+}
+
+static int ads7843_setup_buffers(struct device *dev)
+{
+ struct ads7846 *ts = dev_get_drvdata(dev);
+ int i;
+
+ ts->txbuf = kzalloc(sizeof(u32) * ts->buflen * 3, GFP_KERNEL);
+ if (!ts->txbuf)
+ return -ENOMEM;
+
+ ts->rxbuf = kzalloc(sizeof(u32) * ts->buflen * 3, GFP_KERNEL);
+ if (!ts->rxbuf) {
+ kfree(ts->txbuf);
+ return -ENOMEM;
+ }
+ for (i = 0; i < ((ts->buflen * 3) / 2); i++)
+#if defined( SCREEN_LANDSCAPE )
+ ts->txbuf[i] = (READ_12BIT_DFR(x) | ADS_PD10_PDOWN) << 8;
+#else
+ ts->txbuf[i] = (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) << 8;
+#endif
+ for (; i < ts->buflen * 3; i++)
+#if defined( SCREEN_LANDSCAPE )
+ ts->txbuf[i] = (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) << 8;
+#else
+ ts->txbuf[i] = (READ_12BIT_DFR(x) | ADS_PD10_PDOWN) << 8;
+#endif
+ return 0;
}
+
static int ads7846_read12_ser(struct device *dev, unsigned command)
{
struct spi_device *spi = to_spi_device(dev);
struct ads7846 *ts = dev_get_drvdata(dev);
struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL);
int status;
- int use_internal;
+ int sample;
+ int i;
if (!req)
return -ENOMEM;
spi_message_init(&req->msg);
- /* FIXME boards with ads7846 might use external vref instead ... */
- use_internal = (ts->model == 7846);
-
- /* maybe turn on internal vREF, and let it settle */
- if (use_internal) {
- req->ref_on = REF_ON;
- req->xfer[0].tx_buf = &req->ref_on;
- req->xfer[0].len = 1;
- spi_message_add_tail(&req->xfer[0], &req->msg);
-
- req->xfer[1].rx_buf = &req->scratch;
- req->xfer[1].len = 2;
-
- /* for 1uF, settle for 800 usec; no cap, 100 usec. */
- req->xfer[1].delay_usecs = ts->vref_delay_usecs;
- spi_message_add_tail(&req->xfer[1], &req->msg);
- }
+ /* activate reference, so it has time to settle; */
+ req->ref_on = REF_ON;
+ req->xfer[0].tx_buf = &req->ref_on;
+ req->xfer[0].len = 1;
+ req->xfer[1].tx_buf = &req->zerro;
+ req->xfer[1].rx_buf = &req->scratch;
+ req->xfer[1].len = 2;
+
+ /*
+ * for external VREF, 0 usec (and assume it's always on);
+ * for 1uF, use 800 usec;
+ * no cap, 100 usec.
+ */
+ req->xfer[1].delay_usecs = ts->vref_delay_usecs;
/* take sample */
req->command = (u8) command;
req->xfer[2].tx_buf = &req->command;
req->xfer[2].len = 1;
- spi_message_add_tail(&req->xfer[2], &req->msg);
-
+ req->xfer[3].tx_buf = &req->zerro;
req->xfer[3].rx_buf = &req->sample;
req->xfer[3].len = 2;
- spi_message_add_tail(&req->xfer[3], &req->msg);
/* REVISIT: take a few more samples, and compare ... */
- /* converter in low power mode & enable PENIRQ */
- req->ref_off = PWRDOWN;
+ /* turn off reference */
+ req->ref_off = REF_OFF;
req->xfer[4].tx_buf = &req->ref_off;
req->xfer[4].len = 1;
- spi_message_add_tail(&req->xfer[4], &req->msg);
-
+ // TODO req->xfer[3].tx_buf = &req->zerro;
+ req->xfer[5].tx_buf = &req->zerro;
req->xfer[5].rx_buf = &req->scratch;
req->xfer[5].len = 2;
+
CS_CHANGE(req->xfer[5]);
- spi_message_add_tail(&req->xfer[5], &req->msg);
+
+ /* group all the transfers together, so we can't interfere with
+ * reading touchscreen state; disable penirq while sampling
+ */
+ for (i = 0; i < 6; i++)
+ spi_message_add_tail(&req->xfer[i], &req->msg);
ts->irq_disabled = 1;
disable_irq(spi->irq);
@@ -269,184 +290,37 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
ts->irq_disabled = 0;
enable_irq(spi->irq);
- if (status == 0) {
- /* on-wire is a must-ignore bit, a BE12 value, then padding */
- status = be16_to_cpu(req->sample);
- status = status >> 3;
- status &= 0x0fff;
- }
+ if (req->msg.status)
+ status = req->msg.status;
+
+ /* on-wire is a must-ignore bit, a BE12 value, then padding */
+ sample = be16_to_cpu(req->sample);
+ sample = sample >> 3;
+ sample &= 0x0fff;
kfree(req);
- return status;
+ return status ? status : sample;
}
-#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
-
-#define SHOW(name, var, adjust) static ssize_t \
+#define SHOW(name) static ssize_t \
name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
- struct ads7846 *ts = dev_get_drvdata(dev); \
ssize_t v = ads7846_read12_ser(dev, \
- READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \
+ READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \
if (v < 0) \
return v; \
- return sprintf(buf, "%u\n", adjust(ts, v)); \
+ return sprintf(buf, "%u\n", (unsigned) v); \
} \
static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
-
-/* Sysfs conventions report temperatures in millidegrees Celcius.
- * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high
- * accuracy scheme without calibration data. For now we won't try either;
- * userspace sees raw sensor values, and must scale/calibrate appropriately.
- */
-static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v)
-{
- return v;
-}
-
-SHOW(temp0, temp0, null_adjust) /* temp1_input */
-SHOW(temp1, temp1, null_adjust) /* temp2_input */
-
-
-/* sysfs conventions report voltages in millivolts. We can convert voltages
- * if we know vREF. userspace may need to scale vAUX to match the board's
- * external resistors; we assume that vBATT only uses the internal ones.
- */
-static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v)
-{
- unsigned retval = v;
-
- /* external resistors may scale vAUX into 0..vREF */
- retval *= ts->vref_mv;
- retval = retval >> 12;
- return retval;
-}
-
-static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)
-{
- unsigned retval = vaux_adjust(ts, v);
-
- /* ads7846 has a resistor ladder to scale this signal down */
- if (ts->model == 7846)
- retval *= 4;
- return retval;
-}
-
-SHOW(in0_input, vaux, vaux_adjust)
-SHOW(in1_input, vbatt, vbatt_adjust)
-
-
-static struct attribute *ads7846_attributes[] = {
- &dev_attr_temp0.attr,
- &dev_attr_temp1.attr,
- &dev_attr_in0_input.attr,
- &dev_attr_in1_input.attr,
- NULL,
-};
-
-static struct attribute_group ads7846_attr_group = {
- .attrs = ads7846_attributes,
-};
-
-static struct attribute *ads7843_attributes[] = {
- &dev_attr_in0_input.attr,
- &dev_attr_in1_input.attr,
- NULL,
-};
-
-static struct attribute_group ads7843_attr_group = {
- .attrs = ads7843_attributes,
-};
-
-static struct attribute *ads7845_attributes[] = {
- &dev_attr_in0_input.attr,
- NULL,
-};
-
-static struct attribute_group ads7845_attr_group = {
- .attrs = ads7845_attributes,
-};
-
-static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
-{
- struct device *hwmon;
- int err;
-
- /* hwmon sensors need a reference voltage */
- switch (ts->model) {
- case 7846:
- if (!ts->vref_mv) {
- dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");
- ts->vref_mv = 2500;
- }
- break;
- case 7845:
- case 7843:
- if (!ts->vref_mv) {
- dev_warn(&spi->dev,
- "external vREF for ADS%d not specified\n",
- ts->model);
- return 0;
- }
- break;
- }
-
- /* different chips have different sensor groups */
- switch (ts->model) {
- case 7846:
- ts->attr_group = &ads7846_attr_group;
- break;
- case 7845:
- ts->attr_group = &ads7845_attr_group;
- break;
- case 7843:
- ts->attr_group = &ads7843_attr_group;
- break;
- default:
- dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model);
- return 0;
- }
-
- err = sysfs_create_group(&spi->dev.kobj, ts->attr_group);
- if (err)
- return err;
-
- hwmon = hwmon_device_register(&spi->dev);
- if (IS_ERR(hwmon)) {
- sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
- return PTR_ERR(hwmon);
- }
-
- ts->hwmon = hwmon;
- return 0;
-}
-
-static void ads784x_hwmon_unregister(struct spi_device *spi,
- struct ads7846 *ts)
-{
- if (ts->hwmon) {
- sysfs_remove_group(&spi->dev.kobj, ts->attr_group);
- hwmon_device_unregister(ts->hwmon);
- }
-}
-
-#else
-static inline int ads784x_hwmon_register(struct spi_device *spi,
- struct ads7846 *ts)
-{
- return 0;
-}
-
-static inline void ads784x_hwmon_unregister(struct spi_device *spi,
- struct ads7846 *ts)
-{
-}
-#endif
+SHOW(temp0)
+SHOW(temp1)
+SHOW(vaux)
+SHOW(vbatt)
static int is_pen_down(struct device *dev)
{
- struct ads7846 *ts = dev_get_drvdata(dev);
+ struct ads7846 *ts = dev_get_drvdata(dev);
return ts->pendown;
}
@@ -472,11 +346,10 @@ static ssize_t ads7846_disable_store(struct device *dev,
const char *buf, size_t count)
{
struct ads7846 *ts = dev_get_drvdata(dev);
- long i;
-
- if (strict_strtoul(buf, 10, &i))
- return -EINVAL;
+ char *endp;
+ int i;
+ i = simple_strtoul(buf, &endp, 10);
spin_lock_irq(&ts->lock);
if (i)
@@ -491,26 +364,8 @@ static ssize_t ads7846_disable_store(struct device *dev,
static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store);
-static struct attribute *ads784x_attributes[] = {
- &dev_attr_pen_down.attr,
- &dev_attr_disable.attr,
- NULL,
-};
-
-static struct attribute_group ads784x_attr_group = {
- .attrs = ads784x_attributes,
-};
-
/*--------------------------------------------------------------------------*/
-static int get_pendown_state(struct ads7846 *ts)
-{
- if (ts->get_pendown_state)
- return ts->get_pendown_state();
-
- return !gpio_get_value(ts->gpio_pendown);
-}
-
/*
* PENIRQ only kicks the timer. The timer only reissues the SPI transfer,
* to retrieve touchscreen status.
@@ -522,25 +377,25 @@ static int get_pendown_state(struct ads7846 *ts)
static void ads7846_rx(void *ads)
{
struct ads7846 *ts = ads;
- struct ads7846_packet *packet = ts->packet;
+ struct input_dev *input_dev = ts->input;
unsigned Rt;
+ unsigned sync = 0;
u16 x, y, z1, z2;
+ unsigned long flags;
- /* ads7846_rx_val() did in-place conversion (including byteswap) from
- * on-the-wire format as part of debouncing to get stable readings.
+ /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding;
+ * built from two 8 bit values written msb-first.
*/
- x = packet->tc.x;
- y = packet->tc.y;
- z1 = packet->tc.z1;
- z2 = packet->tc.z2;
+ x = (be16_to_cpu(ts->tc.x) >> 3) & 0x0fff;
+ y = (be16_to_cpu(ts->tc.y) >> 3) & 0x0fff;
+ z1 = (be16_to_cpu(ts->tc.z1) >> 3) & 0x0fff;
+ z2 = (be16_to_cpu(ts->tc.z2) >> 3) & 0x0fff;
/* range filtering */
if (x == MAX_12BIT)
x = 0;
- if (ts->model == 7843) {
- Rt = ts->pressure_max / 2;
- } else if (likely(x && z1)) {
+ if (likely(x && z1 && !device_suspended(&ts->spi->dev))) {
/* compute touch pressure resistance using equation #2 */
Rt = z2;
Rt -= z1;
@@ -548,143 +403,224 @@ static void ads7846_rx(void *ads)
Rt *= ts->x_plate_ohms;
Rt /= z1;
Rt = (Rt + 2047) >> 12;
- } else {
+ } else
Rt = 0;
- }
/* 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
- */
- if (packet->tc.ignore || Rt > ts->pressure_max) {
-#ifdef VERBOSE
- pr_debug("%s: ignored %d pressure %d\n",
- ts->spi->dev.bus_id, packet->tc.ignore, Rt);
-#endif
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
- HRTIMER_MODE_REL);
+ * the maximum. Don't report it to user space, repeat at least
+ * once more the measurement */
+ if (ts->tc.ignore || Rt > ts->pressure_max) {
+ mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);
return;
}
- /* Maybe check the pendown state before reporting. This discards
- * false readings when the pen is lifted.
+ /* NOTE: "pendown" is inferred from pressure; we don't rely on
+ * being able to check nPENIRQ status, or "friendly" trigger modes
+ * (both-edges is much better than just-falling or low-level).
+ *
+ * REVISIT: some boards may require reading nPENIRQ; it's
+ * needed on 7843. and 7845 reads pressure differently...
+ *
+ * REVISIT: the touchscreen might not be connected; this code
+ * won't notice that, even if nPENIRQ never fires ...
*/
- if (ts->penirq_recheck_delay_usecs) {
- udelay(ts->penirq_recheck_delay_usecs);
- if (!get_pendown_state(ts))
- Rt = 0;
+ if (!ts->pendown && Rt != 0) {
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ sync = 1;
+ } else if (ts->pendown && Rt == 0) {
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ sync = 1;
}
- /* 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;
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y, y);
+ sync = 1;
+ }
- if (!ts->pendown) {
- input_report_key(input, BTN_TOUCH, 1);
- ts->pendown = 1;
-#ifdef VERBOSE
- dev_dbg(&ts->spi->dev, "DOWN\n");
+ if (sync) {
+ input_report_abs(input_dev, ABS_PRESSURE, Rt);
+ input_sync(input_dev);
+ }
+
+#ifdef VERBOSE
+ if (Rt || ts->pendown)
+ pr_debug("%s: %d/%d/%d%s\n", ts->spi->dev.bus_id,
+ x, y, Rt, Rt ? "" : " UP");
#endif
+
+ spin_lock_irqsave(&ts->lock, flags);
+
+ ts->pendown = (Rt != 0);
+ mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);
+
+ spin_unlock_irqrestore(&ts->lock, flags);
+}
+
+static inline u16 ad7843_get_sample_val(u32 sample)
+{
+ return (((((sample & 0x00ff0000) >> 8) | (sample >> 24)) >> 3) & 0x0fff);
+}
+
+static u32 ad7843_get_better_values(struct ads7846 *ts, int index)
+{
+ u32 diff12, diff23, diff31;
+ u32 vals[3];
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ vals[i] = ad7843_get_sample_val(ts->rxbuf[index+i]);
+ if (vals[i] == 0x0fff) {
+ ts->skip_this_sample = 1;
+ return 0;
}
- input_report_abs(input, ABS_X, x);
- input_report_abs(input, ABS_Y, y);
- input_report_abs(input, ABS_PRESSURE, Rt);
+ }
- input_sync(input);
-#ifdef VERBOSE
- dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
-#endif
+ diff12 = (vals[0] > vals[1]) ? vals[0] - vals[1] : vals[1] - vals[0];
+ if (diff12 > MAX_DIFF_BETWEEN_SAMPLES) {
+ ts->skip_this_sample = 1;
+ return 0;
+ }
+
+ diff23 = (vals[1] > vals[2]) ? vals[1] - vals[2] : vals[2] - vals[1];
+ if (diff23 > MAX_DIFF_BETWEEN_SAMPLES) {
+ ts->skip_this_sample = 1;
+ return 0;
}
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
- HRTIMER_MODE_REL);
+ diff31 = (vals[2] > vals[0]) ? vals[2] - vals[0] : vals[0] - vals[2];
+ if (diff31 > MAX_DIFF_BETWEEN_SAMPLES) {
+ ts->skip_this_sample = 1;
+ return 0;
+ }
+
+ if (diff12 < diff23 && diff12 < diff31)
+ return (vals[0] + vals[1]) / 2;
+ if (diff23 < diff12 && diff23 < diff31)
+ return (vals[1] + vals[2]) / 2;
+
+ return (vals[0] + vals[2]) / 2;
}
-static int ads7846_debounce(void *ads, int data_idx, int *val)
+static void ads7843_rx_average(void *ads)
+{
+ struct ads7846 *ts = ads;
+ struct input_dev *input_dev = ts->input;
+ u16 x, y, temp;
+ unsigned long flags;
+ int i, sample_count;
+
+ dev_dbg(&ts->spi->dev, "%s\n", __FUNCTION__);
+
+ for (i = 0, y = 0, sample_count = 0; i < (ts->buflen * 3 / 2); i+=3) {
+ if (i >= ts->skip_samples*3) {
+ temp = ad7843_get_better_values(ts, i);
+ if (!ts->skip_this_sample) {
+ if (ts->rotate == 180) {
+ y += MAX_12BIT - temp;
+ } else if (ts->rotate == 0) {
+ y += temp;
+ } else {
+ dev_info(&ts->spi->dev, "Rotate mode %d, not implemented yet\n", ts->rotate);
+ }
+ sample_count++;
+ }
+ }
+ ts->skip_this_sample = 0;
+ }
+
+ if (!sample_count)
+ goto sample_taken;
+
+ y /= sample_count;
+
+ for (x = 0, sample_count = 0; i < (ts->buflen * 3); i+=3) {
+ if (i >= (ts->skip_samples + ts->buflen / 2)*3) {
+ temp = ad7843_get_better_values(ts, i);
+ if (!ts->skip_this_sample) {
+ if (ts->rotate == 180) {
+ x += MAX_12BIT - temp;
+ } else if (ts->rotate == 0) {
+ x += temp;
+ } else {
+ dev_info(&ts->spi->dev, "Rotate mode %d, not implemented yet\n", ts->rotate);
+ }
+ sample_count++;
+ }
+ }
+ ts->skip_this_sample = 0;
+ }
+
+ if (!sample_count)
+ goto sample_taken;
+
+ x /= sample_count;
+
+ if (ts->pendown) {
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_report_abs(input_dev, ABS_PRESSURE, ts->pressure_max / 2);
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y, y);
+ } else {
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ }
+
+sample_taken:
+ input_sync(input_dev);
+ dev_dbg(&ts->spi->dev, "%d/%d %s\n", x, y, ts->pendown ? "" : " UP");
+
+ if (ts->pendown) {
+ spin_lock_irqsave(&ts->lock, flags);
+ mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);
+ spin_unlock_irqrestore(&ts->lock, flags);
+ }
+}
+
+static void ads7846_debounce(void *ads)
{
struct ads7846 *ts = ads;
+ struct spi_message *m;
+ struct spi_transfer *t;
+ int val;
+ int status;
- if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) {
- /* Start over collecting consistent readings. */
- ts->read_rep = 0;
+ m = &ts->msg[ts->msg_idx];
+ t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+ val = (be16_to_cpu(*(__be16 *)t->rx_buf) >> 3) & 0x0fff;
+ if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol)) {
/* Repeat it, if this was the first read or the read
* wasn't consistent enough. */
if (ts->read_cnt < ts->debounce_max) {
- ts->last_read = *val;
+ ts->last_read = val;
ts->read_cnt++;
- return ADS7846_FILTER_REPEAT;
} else {
/* Maximum number of debouncing reached and still
* not enough number of consistent readings. Abort
* the whole sample, repeat it in the next sampling
* period.
*/
+ ts->tc.ignore = 1;
ts->read_cnt = 0;
- return ADS7846_FILTER_IGNORE;
+ /* Last message will contain ads7846_rx() as the
+ * completion function.
+ */
+ m = ts->last_msg;
}
+ /* Start over collecting consistent readings. */
+ ts->read_rep = 0;
} else {
if (++ts->read_rep > ts->debounce_rep) {
/* Got a good reading for this coordinate,
* go for the next one. */
+ ts->tc.ignore = 0;
+ ts->msg_idx++;
ts->read_cnt = 0;
ts->read_rep = 0;
- return ADS7846_FILTER_OK;
- } else {
+ m++;
+ } else
/* Read more values that are consistent. */
ts->read_cnt++;
- return ADS7846_FILTER_REPEAT;
- }
- }
-}
-
-static int ads7846_no_filter(void *ads, int data_idx, int *val)
-{
- return ADS7846_FILTER_OK;
-}
-
-static void ads7846_rx_val(void *ads)
-{
- struct ads7846 *ts = ads;
- struct ads7846_packet *packet = ts->packet;
- struct spi_message *m;
- struct spi_transfer *t;
- int val;
- int action;
- int status;
-
- m = &ts->msg[ts->msg_idx];
- t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
-
- /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding;
- * built from two 8 bit values written msb-first.
- */
- val = be16_to_cpup((__be16 *)t->rx_buf) >> 3;
-
- action = ts->filter(ts->filter_data, ts->msg_idx, &val);
- switch (action) {
- case ADS7846_FILTER_REPEAT:
- break;
- case ADS7846_FILTER_IGNORE:
- packet->tc.ignore = 1;
- /* Last message will contain ads7846_rx() as the
- * completion function.
- */
- m = ts->last_msg;
- break;
- case ADS7846_FILTER_OK:
- *(u16 *)t->rx_buf = val;
- packet->tc.ignore = 0;
- m = &ts->msg[++ts->msg_idx];
- break;
- default:
- BUG();
}
status = spi_async(ts->spi, m);
if (status)
@@ -692,44 +628,51 @@ static void ads7846_rx_val(void *ads)
status);
}
-static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
+static void ads7846_timer(unsigned long handle)
{
- struct ads7846 *ts = container_of(handle, struct ads7846, timer);
- int status = 0;
+ struct ads7846 *ts = (void *)handle;
+ struct input_dev *input_dev = ts->input;
+ int status = 0;
+ /* get sample */
+ ts->pendown = ts->get_pendown_state();
spin_lock_irq(&ts->lock);
+ if (ts->model == 7843) {
+ ts->pending = 0;
+ if (unlikely(!ts->pendown)) {
- if (unlikely(!get_pendown_state(ts) ||
- device_suspended(&ts->spi->dev))) {
- if (ts->pendown) {
- struct input_dev *input = ts->input;
-
- input_report_key(input, BTN_TOUCH, 0);
- input_report_abs(input, ABS_PRESSURE, 0);
- input_sync(input);
-
- ts->pendown = 0;
-#ifdef VERBOSE
- dev_dbg(&ts->spi->dev, "UP\n");
-#endif
- }
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_sync(input_dev);
- /* measurement cycle ended */
- if (!device_suspended(&ts->spi->dev)) {
- ts->irq_disabled = 0;
- enable_irq(ts->spi->irq);
+ if (!device_suspended(&ts->spi->dev)) {
+ ts->irq_disabled = 0;
+ enable_irq(ts->spi->irq);
+ }
+ } else {
+ /* pen is still down, continue with the measurement */
+ status = spi_async(ts->spi, &ts->msg[0]);
+ if (status)
+ dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
}
- ts->pending = 0;
} else {
- /* pen is still down, continue with the measurement */
- ts->msg_idx = 0;
- status = spi_async(ts->spi, &ts->msg[0]);
- if (status)
- dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
+ if (unlikely(ts->msg_idx && !ts->pendown)) {
+ /* measurement cycle ended */
+ if (!device_suspended(&ts->spi->dev)) {
+ ts->irq_disabled = 0;
+ enable_irq(ts->spi->irq);
+ }
+ ts->pending = 0;
+ ts->msg_idx = 0;
+ } else {
+ /* pen is still down, continue with the measurement */
+ ts->msg_idx = 0;
+ status = spi_async(ts->spi, &ts->msg[0]);
+ if (status)
+ dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
+ }
}
-
spin_unlock_irq(&ts->lock);
- return HRTIMER_NORESTART;
}
static irqreturn_t ads7846_irq(int irq, void *handle)
@@ -738,7 +681,8 @@ static irqreturn_t ads7846_irq(int irq, void *handle)
unsigned long flags;
spin_lock_irqsave(&ts->lock, flags);
- if (likely(get_pendown_state(ts))) {
+
+ if (likely(ts->get_pendown_state())) {
if (!ts->irq_disabled) {
/* The ARM do_simple_IRQ() dispatcher doesn't act
* like the other dispatchers: it will report IRQs
@@ -748,8 +692,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle)
ts->irq_disabled = 1;
disable_irq(ts->spi->irq);
ts->pending = 1;
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
- HRTIMER_MODE_REL);
+ mod_timer(&ts->timer, jiffies);
}
}
spin_unlock_irqrestore(&ts->lock, flags);
@@ -785,6 +728,7 @@ static void ads7846_disable(struct ads7846 *ts)
/* we know the chip's in lowpower mode since we always
* leave it that way after every request
*/
+
}
/* Must be called with ts->lock held */
@@ -804,7 +748,7 @@ static int ads7846_suspend(struct spi_device *spi, pm_message_t message)
spin_lock_irq(&ts->lock);
- ts->is_suspended = 1;
+ spi->dev.power.power_state = message;
ads7846_disable(ts);
spin_unlock_irq(&ts->lock);
@@ -819,7 +763,7 @@ static int ads7846_resume(struct spi_device *spi)
spin_lock_irq(&ts->lock);
- ts->is_suspended = 0;
+ spi->dev.power.power_state = PMSG_ON;
ads7846_enable(ts);
spin_unlock_irq(&ts->lock);
@@ -827,45 +771,13 @@ static int ads7846_resume(struct spi_device *spi)
return 0;
}
-static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts)
-{
- struct ads7846_platform_data *pdata = spi->dev.platform_data;
- int err;
-
- /* REVISIT when the irq can be triggered active-low, or if for some
- * reason the touchscreen isn't hooked up, we don't need to access
- * the pendown state.
- */
- if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) {
- dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n");
- return -EINVAL;
- }
-
- if (pdata->get_pendown_state) {
- ts->get_pendown_state = pdata->get_pendown_state;
- return 0;
- }
-
- err = gpio_request(pdata->gpio_pendown, "ads7846_pendown");
- if (err) {
- dev_err(&spi->dev, "failed to request pendown GPIO%d\n",
- pdata->gpio_pendown);
- return err;
- }
-
- ts->gpio_pendown = pdata->gpio_pendown;
- return 0;
-}
-
static int __devinit ads7846_probe(struct spi_device *spi)
{
struct ads7846 *ts;
- struct ads7846_packet *packet;
struct input_dev *input_dev;
struct ads7846_platform_data *pdata = spi->dev.platform_data;
struct spi_message *m;
struct spi_transfer *x;
- int vref;
int err;
if (!spi->irq) {
@@ -885,32 +797,46 @@ static int __devinit ads7846_probe(struct spi_device *spi)
return -EINVAL;
}
+ /* REVISIT when the irq can be triggered active-low, or if for some
+ * reason the touchscreen isn't hooked up, we don't need to access
+ * the pendown state.
+ */
+ if (pdata->get_pendown_state == NULL) {
+ dev_dbg(&spi->dev, "no get_pendown_state function?\n");
+ return -EINVAL;
+ }
+
/* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except
* that even if the hardware can do that, the SPI controller driver
* may not. So we stick to very-portable 8 bit words, both RX and TX.
*/
spi->bits_per_word = 8;
- spi->mode = SPI_MODE_0;
- err = spi_setup(spi);
- if (err < 0)
- return err;
ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
- packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL);
input_dev = input_allocate_device();
- if (!ts || !packet || !input_dev) {
+ if (!ts || !input_dev) {
err = -ENOMEM;
goto err_free_mem;
}
dev_set_drvdata(&spi->dev, ts);
- ts->packet = packet;
+ spi->dev.power.power_state = PMSG_ON;
+
ts->spi = spi;
ts->input = input_dev;
- ts->vref_mv = pdata->vref_mv;
- hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ts->buflen = pdata->buflen ? : MAX_BUF_SAMPLE_LEN;
+ ts->buflen = ts->buflen & ~0x1; /* must be even */
+
+ if (ads7843_setup_buffers(&spi->dev)) {
+ dev_dbg(&spi->dev, "error allocating memory for sample buffers\n");
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ init_timer(&ts->timer);
+ ts->timer.data = (unsigned long) ts;
ts->timer.function = ads7846_timer;
spin_lock_init(&ts->lock);
@@ -919,33 +845,18 @@ static int __devinit ads7846_probe(struct spi_device *spi)
ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
ts->pressure_max = pdata->pressure_max ? : ~0;
+ ts->skip_samples = pdata->skip_samples ? : 0;
+ ts->rotate = pdata->rotate ? : 0;
- if (pdata->filter != NULL) {
- if (pdata->filter_init != NULL) {
- err = pdata->filter_init(pdata, &ts->filter_data);
- if (err < 0)
- goto err_free_mem;
- }
- ts->filter = pdata->filter;
- ts->filter_cleanup = pdata->filter_cleanup;
- } else if (pdata->debounce_max) {
+ if (pdata->debounce_max) {
ts->debounce_max = pdata->debounce_max;
- if (ts->debounce_max < 2)
- ts->debounce_max = 2;
ts->debounce_tol = pdata->debounce_tol;
ts->debounce_rep = pdata->debounce_rep;
- ts->filter = ads7846_debounce;
- ts->filter_data = ts;
+ if (ts->debounce_rep > ts->debounce_max + 1)
+ ts->debounce_rep = ts->debounce_max - 1;
} else
- ts->filter = ads7846_no_filter;
-
- err = setup_pendown(spi, ts);
- if (err)
- goto err_cleanup_filter;
-
- if (pdata->penirq_recheck_delay_usecs)
- ts->penirq_recheck_delay_usecs =
- pdata->penirq_recheck_delay_usecs;
+ ts->debounce_tol = ~0;
+ ts->get_pendown_state = pdata->get_pendown_state;
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id);
@@ -964,9 +875,8 @@ static int __devinit ads7846_probe(struct spi_device *spi)
pdata->y_max ? : MAX_12BIT,
0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE,
- pdata->pressure_min, pdata->pressure_max, 0, 0);
-
- vref = pdata->keep_vref_on;
+ pdata->pressure_min ? : 0,
+ pdata->pressure_max ? : 1, 0, 0);
/* set up the transfers to read touchscreen state; this assumes we
* use formula #2 for pressure, not #3.
@@ -976,203 +886,176 @@ static int __devinit ads7846_probe(struct spi_device *spi)
spi_message_init(m);
- /* y- still on; turn on only y+ (and ADC) */
- packet->read_y = READ_Y(vref);
- x->tx_buf = &packet->read_y;
- x->len = 1;
- spi_message_add_tail(x, m);
-
- x++;
- x->rx_buf = &packet->tc.y;
- x->len = 2;
- spi_message_add_tail(x, m);
-
- /* the first sample after switching drivers can be low quality;
- * optionally discard it, using a second one after the signals
- * have had enough time to stabilize.
- */
- if (pdata->settle_delay_usecs) {
- x->delay_usecs = pdata->settle_delay_usecs;
-
- x++;
- x->tx_buf = &packet->read_y;
- x->len = 1;
- spi_message_add_tail(x, m);
-
- x++;
- x->rx_buf = &packet->tc.y;
- x->len = 2;
+ if (ts->model == 7843) {
+ x->tx_buf = ts->txbuf;
+ x->rx_buf = ts->rxbuf;
+ x->len = ts->buflen * sizeof(u32) * 3; /* For every sample we take 3 samples and choose the better 2 */
spi_message_add_tail(x, m);
- }
- m->complete = ads7846_rx_val;
- m->context = ts;
-
- m++;
- spi_message_init(m);
-
- /* turn y- off, x+ on, then leave in lowpower */
- x++;
- packet->read_x = READ_X(vref);
- x->tx_buf = &packet->read_x;
- x->len = 1;
- spi_message_add_tail(x, m);
-
- x++;
- x->rx_buf = &packet->tc.x;
- x->len = 2;
- spi_message_add_tail(x, m);
-
- /* ... maybe discard first sample ... */
- if (pdata->settle_delay_usecs) {
- x->delay_usecs = pdata->settle_delay_usecs;
+ m->complete = ads7843_rx_average;
+ m->context = ts;
- x++;
- x->tx_buf = &packet->read_x;
+ ts->last_msg = m;
+ } else {
+ /* y- still on; turn on only y+ (and ADC) */
+ ts->read_y = READ_Y;
+ x->tx_buf = &ts->read_y;
x->len = 1;
spi_message_add_tail(x, m);
x++;
- x->rx_buf = &packet->tc.x;
+ x->rx_buf = &ts->tc.y;
x->len = 2;
spi_message_add_tail(x, m);
- }
- m->complete = ads7846_rx_val;
- m->context = ts;
+ m->complete = ads7846_debounce;
+ m->context = ts;
- /* turn y+ off, x- on; we'll use formula #2 */
- if (ts->model == 7846) {
m++;
spi_message_init(m);
+ /* turn y- off, x+ on, then leave in lowpower */
x++;
- packet->read_z1 = READ_Z1(vref);
- x->tx_buf = &packet->read_z1;
+ ts->read_x = READ_X;
+ x->tx_buf = &ts->read_x;
x->len = 1;
spi_message_add_tail(x, m);
x++;
- x->rx_buf = &packet->tc.z1;
+ x->rx_buf = &ts->tc.x;
x->len = 2;
spi_message_add_tail(x, m);
- /* ... maybe discard first sample ... */
- if (pdata->settle_delay_usecs) {
- x->delay_usecs = pdata->settle_delay_usecs;
+ m->complete = ads7846_debounce;
+ m->context = ts;
+
+ /* turn y+ off, x- on; we'll use formula #2 */
+ if (ts->model == 7846) {
+ m++;
+ spi_message_init(m);
x++;
- x->tx_buf = &packet->read_z1;
+ ts->read_z1 = READ_Z1;
+ x->tx_buf = &ts->read_z1;
x->len = 1;
spi_message_add_tail(x, m);
x++;
- x->rx_buf = &packet->tc.z1;
+ x->rx_buf = &ts->tc.z1;
x->len = 2;
spi_message_add_tail(x, m);
- }
-
- m->complete = ads7846_rx_val;
- m->context = ts;
-
- m++;
- spi_message_init(m);
-
- x++;
- packet->read_z2 = READ_Z2(vref);
- x->tx_buf = &packet->read_z2;
- x->len = 1;
- spi_message_add_tail(x, m);
- x++;
- x->rx_buf = &packet->tc.z2;
- x->len = 2;
- spi_message_add_tail(x, m);
+ m->complete = ads7846_debounce;
+ m->context = ts;
- /* ... maybe discard first sample ... */
- if (pdata->settle_delay_usecs) {
- x->delay_usecs = pdata->settle_delay_usecs;
+ m++;
+ spi_message_init(m);
x++;
- x->tx_buf = &packet->read_z2;
+ ts->read_z2 = READ_Z2;
+ x->tx_buf = &ts->read_z2;
x->len = 1;
spi_message_add_tail(x, m);
x++;
- x->rx_buf = &packet->tc.z2;
+ x->rx_buf = &ts->tc.z2;
x->len = 2;
spi_message_add_tail(x, m);
- }
- m->complete = ads7846_rx_val;
- m->context = ts;
- }
+ m->complete = ads7846_debounce;
+ m->context = ts;
+ }
- /* power down */
- m++;
- spi_message_init(m);
+ /* power down */
+ m++;
+ spi_message_init(m);
- x++;
- packet->pwrdown = PWRDOWN;
- x->tx_buf = &packet->pwrdown;
- x->len = 1;
- spi_message_add_tail(x, m);
+ x++;
+ ts->pwrdown = PWRDOWN;
+ x->tx_buf = &ts->pwrdown;
+ x->len = 1;
+ spi_message_add_tail(x, m);
- x++;
- x->rx_buf = &packet->dummy;
- x->len = 2;
- CS_CHANGE(*x);
- spi_message_add_tail(x, m);
+ x++;
+ x->rx_buf = &ts->dummy;
+ x->len = 2;
+ CS_CHANGE(*x);
+ spi_message_add_tail(x, m);
- m->complete = ads7846_rx;
- m->context = ts;
+ m->complete = ads7846_rx;
+ m->context = ts;
- ts->last_msg = m;
+ ts->last_msg = m;
+ }
if (request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING,
spi->dev.driver->name, ts)) {
dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
err = -EBUSY;
- goto err_free_gpio;
+ goto err_free_buf;
}
- err = ads784x_hwmon_register(spi, ts);
- if (err)
- goto err_free_irq;
-
dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
- /* take a first sample, leaving nPENIRQ active and vREF off; avoid
+ /* take a first sample, leaving nPENIRQ active; avoid
* the touchscreen, in case it's not connected.
*/
- (void) ads7846_read12_ser(&spi->dev,
- READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
+ if (ts->model != 7843) {
+ /* take a first sample, leaving nPENIRQ active; avoid
+ * the touchscreen, in case it's not connected.
+ */
+ (void) ads7846_read12_ser(&spi->dev,
+ READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
+ }
+
+ /* ads7843/7845 don't have temperature sensors, and
+ * use the other sensors a bit differently too
+ */
+ if (ts->model == 7846) {
+ device_create_file(&spi->dev, &dev_attr_temp0);
+ device_create_file(&spi->dev, &dev_attr_temp1);
+ }
- err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
- if (err)
- goto err_remove_hwmon;
+ if (ts->model != 7845 && ts->model != 7843)
+ device_create_file(&spi->dev, &dev_attr_vbatt);
+
+ if (ts->model != 7843) {
+ device_create_file(&spi->dev, &dev_attr_vaux);
+ }
+
+ device_create_file(&spi->dev, &dev_attr_pen_down);
+ device_create_file(&spi->dev, &dev_attr_disable);
err = input_register_device(input_dev);
if (err)
- goto err_remove_attr_group;
+ goto err_remove_attr;
return 0;
- err_remove_attr_group:
- sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
- err_remove_hwmon:
- ads784x_hwmon_unregister(spi, ts);
- err_free_irq:
+ err_remove_attr:
+ device_remove_file(&spi->dev, &dev_attr_disable);
+ device_remove_file(&spi->dev, &dev_attr_pen_down);
+ if (ts->model == 7846) {
+ device_remove_file(&spi->dev, &dev_attr_temp1);
+ device_remove_file(&spi->dev, &dev_attr_temp0);
+ }
+
+ if (ts->model != 7845 && ts->model != 7843)
+ device_remove_file(&spi->dev, &dev_attr_vbatt);
+
+ if (ts->model != 7843) {
+ device_remove_file(&spi->dev, &dev_attr_vaux);
+ }
+
free_irq(spi->irq, ts);
- err_free_gpio:
- if (ts->gpio_pendown != -1)
- gpio_free(ts->gpio_pendown);
- err_cleanup_filter:
- if (ts->filter_cleanup)
- ts->filter_cleanup(ts->filter_data);
+
+ err_free_buf:
+ if (ts->txbuf)
+ kfree(ts->txbuf);
+ if (ts->rxbuf)
+ kfree(ts->rxbuf);
err_free_mem:
input_free_device(input_dev);
- kfree(packet);
kfree(ts);
return err;
}
@@ -1181,24 +1064,33 @@ static int __devexit ads7846_remove(struct spi_device *spi)
{
struct ads7846 *ts = dev_get_drvdata(&spi->dev);
- ads784x_hwmon_unregister(spi, ts);
input_unregister_device(ts->input);
ads7846_suspend(spi, PMSG_SUSPEND);
- sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
+ if (ts->txbuf)
+ kfree(ts->txbuf);
+ if (ts->rxbuf)
+ kfree(ts->rxbuf);
- free_irq(ts->spi->irq, ts);
- /* suspend left the IRQ disabled */
- enable_irq(ts->spi->irq);
+ device_remove_file(&spi->dev, &dev_attr_disable);
+ device_remove_file(&spi->dev, &dev_attr_pen_down);
+ if (ts->model == 7846) {
+ device_remove_file(&spi->dev, &dev_attr_temp1);
+ device_remove_file(&spi->dev, &dev_attr_temp0);
+ }
- if (ts->gpio_pendown != -1)
- gpio_free(ts->gpio_pendown);
+ if (ts->model != 7845 && ts->model != 7843)
+ device_remove_file(&spi->dev, &dev_attr_vbatt);
- if (ts->filter_cleanup)
- ts->filter_cleanup(ts->filter_data);
+ if (ts->model != 7843) {
+ device_remove_file(&spi->dev, &dev_attr_vaux);
+ }
+
+ free_irq(ts->spi->irq, ts);
+ /* suspend left the IRQ disabled */
+ disable_irq(ts->spi->irq);
- kfree(ts->packet);
kfree(ts);
dev_dbg(&spi->dev, "unregistered touchscreen\n");
diff --git a/drivers/input/touchscreen/imx_adc_ts.c b/drivers/input/touchscreen/imx_adc_ts.c
new file mode 100644
index 000000000000..ec43a16213ae
--- /dev/null
+++ b/drivers/input/touchscreen/imx_adc_ts.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file imx_adc_ts.c
+ *
+ * @brief Driver for the Freescale Semiconductor i.MX ADC touchscreen.
+ *
+ * This touchscreen driver is designed as a standard input driver. It is a
+ * wrapper around the low level ADC driver. Much of the hardware configuration
+ * and touchscreen functionality is implemented in the low level ADC driver.
+ * During initialization, this driver creates a kernel thread. This thread
+ * then calls the ADC driver to obtain touchscreen values continously. These
+ * values are then passed to the input susbsystem.
+ *
+ * @ingroup touchscreen
+ */
+
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/imx_adc.h>
+
+#define IMX_ADC_TS_NAME "imx_adc_ts"
+
+static struct input_dev *imx_inputdev;
+static u32 input_ts_installed;
+
+static int ts_thread(void *arg)
+{
+ struct t_touch_screen ts_sample;
+ int wait = 0;
+ daemonize("imx_adc_ts");
+ while (input_ts_installed) {
+ try_to_freeze();
+
+ memset(&ts_sample, 0, sizeof(ts_sample));
+ if (0 != imx_adc_get_touch_sample(&ts_sample, !wait))
+ continue;
+
+ input_report_abs(imx_inputdev, ABS_X, ts_sample.x_position);
+ input_report_abs(imx_inputdev, ABS_Y, ts_sample.y_position);
+ input_report_abs(imx_inputdev, ABS_PRESSURE,
+ ts_sample.contact_resistance);
+ input_sync(imx_inputdev);
+ wait = ts_sample.contact_resistance;
+ msleep(10);
+ }
+
+ return 0;
+}
+
+static int __init imx_adc_ts_init(void)
+{
+ int retval;
+
+ if (!is_imx_adc_ready())
+ return -ENODEV;
+
+ imx_inputdev = input_allocate_device();
+ if (!imx_inputdev) {
+ pr_err("imx_ts_init: not enough memory for input device\n");
+ return -ENOMEM;
+ }
+
+ imx_inputdev->name = IMX_ADC_TS_NAME;
+ imx_inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ imx_inputdev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
+ imx_inputdev->absbit[0] =
+ BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_PRESSURE);
+ retval = input_register_device(imx_inputdev);
+ if (retval < 0) {
+ input_free_device(imx_inputdev);
+ return retval;
+ }
+
+ input_ts_installed = 1;
+ kthread_run(ts_thread, NULL, "ts_thread");
+ pr_info("i.MX ADC input touchscreen loaded.\n");
+ return 0;
+}
+
+static void __exit imx_adc_ts_exit(void)
+{
+ input_ts_installed = 0;
+ input_unregister_device(imx_inputdev);
+
+ if (imx_inputdev) {
+ input_free_device(imx_inputdev);
+ imx_inputdev = NULL;
+ }
+}
+
+late_initcall(imx_adc_ts_init);
+module_exit(imx_adc_ts_exit);
+
+MODULE_DESCRIPTION("i.MX ADC input touchscreen driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mxc_ts.c b/drivers/input/touchscreen/mxc_ts.c
new file mode 100644
index 000000000000..b354d81c5465
--- /dev/null
+++ b/drivers/input/touchscreen/mxc_ts.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_ts.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC touchscreen.
+ *
+ * The touchscreen driver is designed as a standard input driver which is a
+ * wrapper over low level PMIC driver. Most of the hardware configuration and
+ * touchscreen functionality is implemented in the low level PMIC driver. During
+ * initialization, this driver creates a kernel thread. This thread then calls
+ * PMIC driver to obtain touchscreen values continously. These values are then
+ * passed to the input susbsystem.
+ *
+ * @ingroup touchscreen
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_adc.h>
+
+#define MXC_TS_NAME "mxc_ts"
+
+static struct input_dev *mxc_inputdev = NULL;
+static u32 input_ts_installed;
+
+static int ts_thread(void *arg)
+{
+ t_touch_screen ts_sample;
+ s32 wait = 0;
+
+ daemonize("mxc_ts");
+ while (input_ts_installed) {
+ try_to_freeze();
+ memset(&ts_sample, 0, sizeof(t_touch_screen));
+ if (0 != pmic_adc_get_touch_sample(&ts_sample, !wait))
+ continue;
+ if (!(ts_sample.contact_resistance || wait))
+ continue;
+
+ input_report_abs(mxc_inputdev, ABS_X, ts_sample.x_position);
+ input_report_abs(mxc_inputdev, ABS_Y, ts_sample.y_position);
+ input_report_abs(mxc_inputdev, ABS_PRESSURE,
+ ts_sample.contact_resistance);
+ input_sync(mxc_inputdev);
+
+ wait = ts_sample.contact_resistance;
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int __init mxc_ts_init(void)
+{
+ int retval;
+
+ if (!is_pmic_adc_ready())
+ return -ENODEV;
+
+ mxc_inputdev = input_allocate_device();
+ if (!mxc_inputdev) {
+ printk(KERN_ERR
+ "mxc_ts_init: not enough memory for input device\n");
+ return -ENOMEM;
+ }
+
+ mxc_inputdev->name = MXC_TS_NAME;
+ mxc_inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ mxc_inputdev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
+ mxc_inputdev->absbit[0] =
+ BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_PRESSURE);
+ retval = input_register_device(mxc_inputdev);
+ if (retval < 0) {
+ input_free_device(mxc_inputdev);
+ return retval;
+ }
+
+ input_ts_installed = 1;
+ kernel_thread(ts_thread, NULL, CLONE_VM | CLONE_FS);
+ printk("mxc input touchscreen loaded\n");
+ return 0;
+}
+
+static void __exit mxc_ts_exit(void)
+{
+ input_ts_installed = 0;
+ input_unregister_device(mxc_inputdev);
+
+ if (mxc_inputdev) {
+ input_free_device(mxc_inputdev);
+ mxc_inputdev = NULL;
+ }
+}
+
+late_initcall(mxc_ts_init);
+module_exit(mxc_ts_exit);
+
+MODULE_DESCRIPTION("MXC input touchscreen driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/s3c24xx_ts.c b/drivers/input/touchscreen/s3c24xx_ts.c
new file mode 100644
index 000000000000..8d4cb5fa34ef
--- /dev/null
+++ b/drivers/input/touchscreen/s3c24xx_ts.c
@@ -0,0 +1,627 @@
+/* linux/drivers/input/touchscreen/s3c-ts.c
+ *
+ * $Id: s3c-ts.c,v 1.7 2007/02/01 05:08:41 yongkal Exp $
+ *
+ * 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
+ *
+ * Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * iPAQ H1940 touchscreen support
+ *
+ * ChangeLog
+ *
+ * 2004-09-05: Herbert Potzl <herbert@13thfloor.at>
+ * - added clock (de-)allocation code
+ *
+ * 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org>
+ * - h1940_ -> s3c24xx (this driver is now also used on the n30
+ * machines :P)
+ * - Debug messages are now enabled with the config option
+ * TOUCHSCREEN_S3C_DEBUG
+ * - Changed the way the value are read
+ * - Input subsystem should now work
+ * - Use ioremap and readl/writel
+ *
+ * 2005-03-23: Arnaud Patard <arnaud.patard@rtp-net.org>
+ * - Make use of some undocumented features of the touchscreen
+ * controller
+ *
+ * 2006-09-05: Ryu Euiyoul <ryu.real@gmail.com>
+ * - added power management suspend and resume code
+ *
+ * 2008-10-20: Luis Galdos <luis.galdos@digi.com>
+ * - Modifications for using a platform device
+ * - Close and open function implemented
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/signal.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <mach/hardware.h>
+
+#include <mach/irqs.h>
+
+#include <mach/map.h>
+#include <plat/ts.h>
+#include <plat/regs-adc.h>
+
+
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] s3c24xx-ts: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "s3c24xx-ts: " fmt, ## args)
+
+#if 0
+#define S3C24XX_TS_DEBUG
+#endif
+
+#ifdef S3C24XX_TS_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "s3c24xx-ts: %s() " fmt, __FUNCTION__ , ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+
+
+/* @TODO: Remove the below macro */
+#define S3C24XX_SAMPLE_NUM_MAX (20)
+
+
+/* For ts->dev.id.version */
+#define S3C_TSVERSION 0x0101
+
+#define WAIT4INT(x) (((x)<<8) | \
+ S3C2410_ADCTSC_YM_SEN | \
+ S3C2410_ADCTSC_YP_SEN | \
+ S3C2410_ADCTSC_XP_SEN | \
+ S3C2410_ADCTSC_XY_PST(3))
+
+#define AUTOPST (S3C2410_ADCTSC_YM_SEN | \
+ S3C2410_ADCTSC_YP_SEN | \
+ S3C2410_ADCTSC_XP_SEN | \
+ S3C2410_ADCTSC_AUTO_PST | \
+ S3C2410_ADCTSC_XY_PST(0))
+
+/*
+ * Definitions & global arrays.
+ */
+static char *s3c_ts_name = "s3c TouchScreen";
+
+
+/*
+ * Per-touchscreen data.
+ */
+struct s3c_ts {
+ ulong xp;
+ ulong yp;
+ char phys[32];
+ struct input_dev *indev;
+ struct clk *clk;
+ void __iomem *base;
+ struct resource *mem;
+ struct timer_list timer;
+ int timer_enabled;
+ int irq_adc;
+ int irq_tc;
+ atomic_t closed;
+ unsigned int adccon;
+ unsigned int adctsc;
+ unsigned int adcdly;
+
+ ulong xps[S3C24XX_SAMPLE_NUM_MAX];
+ ulong yps[S3C24XX_SAMPLE_NUM_MAX];
+ int sample;
+ struct s3c_ts_mach_info *info;
+ int skip_this_sample;
+};
+
+
+#define MAX_DIFF_BETWEEN_SAMPLES 200
+static ulong filter_raw_values(struct s3c_ts *ts, ulong *samples)
+{
+ ulong diff12, diff23, diff31;
+ ulong vals[3];
+ int cnt;
+
+ /* First reset the internal flag */
+ ts->skip_this_sample = 0;
+
+ for (cnt = 0; cnt < 3; cnt++)
+ vals[cnt] = samples[cnt];
+
+ diff12 = (vals[0] > vals[1]) ? vals[0] - vals[1] : vals[1] - vals[0];
+ if (diff12 > MAX_DIFF_BETWEEN_SAMPLES) {
+ ts->skip_this_sample = 1;
+ return 0;
+ }
+
+ diff23 = (vals[1] > vals[2]) ? vals[1] - vals[2] : vals[2] - vals[1];
+ if (diff23 > MAX_DIFF_BETWEEN_SAMPLES) {
+ ts->skip_this_sample = 1;
+ return 0;
+ }
+
+ diff31 = (vals[2] > vals[0]) ? vals[2] - vals[0] : vals[0] - vals[2];
+ if (diff31 > MAX_DIFF_BETWEEN_SAMPLES) {
+ ts->skip_this_sample = 1;
+ return 0;
+ }
+
+ if (diff12 < diff23 && diff12 < diff31)
+ return (vals[0] + vals[1]) / 2;
+ if (diff23 < diff12 && diff23 < diff31)
+ return (vals[1] + vals[2]) / 2;
+
+ return (vals[0] + vals[2]) / 2;
+}
+
+static inline int s3c_ts_calc_sample(struct s3c_ts *ts)
+{
+ int offset;
+ struct s3c_ts_mach_info *info;
+ unsigned long px, py, tx, ty;
+ int cnt;
+
+ info = ts->info;
+ px = py = 0;
+ for (cnt = 0, offset = 0; offset < info->probes / 3; offset++) {
+
+ tx = filter_raw_values(ts, &ts->xps[offset * 3]);
+ if (ts->skip_this_sample)
+ continue;
+
+ ty = filter_raw_values(ts, &ts->yps[offset * 3]);
+ if (ts->skip_this_sample)
+ continue;
+
+ cnt++;
+ px += tx;
+ py += ty;
+ }
+
+ if (cnt) {
+ ts->xp = px / cnt;
+ ts->yp = py / cnt;
+ }
+
+ return cnt;
+}
+
+/* static struct s3c_ts *ts; */
+
+static void touch_timer_fire(unsigned long data)
+{
+ unsigned long data0;
+ unsigned long data1;
+ int updown;
+ struct s3c_ts *ts;
+ unsigned int adccon;
+
+ ts = (struct s3c_ts *)data;
+
+ data0 = readl(ts->base + S3C2410_ADCDAT0);
+ data1 = readl(ts->base + S3C2410_ADCDAT1);
+
+ updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
+ (!(data1 & S3C2410_ADCDAT1_UPDOWN));
+
+ if (updown) {
+ if (ts->timer_enabled) {
+ struct s3c_ts_mach_info *info;
+ info = ts->info;
+
+ ts->xps[ts->sample] = ts->xp;
+ ts->yps[ts->sample] = ts->yp;
+ ts->sample += 1;
+
+ if (ts->sample == info->probes) {
+ if (s3c_ts_calc_sample(ts)) {
+ printk_debug("XP %lu | YP %lu\n",
+ ts->xp, ts->yp);
+ input_report_abs(ts->indev, ABS_X, ts->xp);
+ input_report_abs(ts->indev, ABS_Y, ts->yp);
+
+ input_report_key(ts->indev, BTN_TOUCH, 1);
+ input_report_abs(ts->indev, ABS_PRESSURE, 1);
+ input_sync(ts->indev);
+ }
+
+ /* Reset the number of samples */
+ ts->sample = 0;
+ }
+ }
+
+ ts->xp = 0;
+ ts->yp = 0;
+ writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
+ ts->base + S3C2410_ADCTSC);
+ adccon = readl(ts->base + S3C2410_ADCCON);
+ writel(adccon | S3C2410_ADCCON_ENABLE_START, ts->base + S3C2410_ADCCON);
+ } else {
+ input_report_key(ts->indev, BTN_TOUCH, 0);
+ input_report_abs(ts->indev, ABS_PRESSURE, 0);
+ input_sync(ts->indev);
+ writel(WAIT4INT(0), ts->base + S3C2410_ADCTSC);
+ }
+
+ ts->timer_enabled = 0;
+}
+
+
+static irqreturn_t stylus_updown(int irq, void *dev_id)
+{
+ unsigned long data0;
+ unsigned long data1;
+ int updown;
+ struct s3c_ts *ts;
+
+ ts = (struct s3c_ts *)dev_id;
+
+ disable_irq(ts->irq_adc);
+ data0 = readl(ts->base + S3C2410_ADCDAT0);
+ data1 = readl(ts->base + S3C2410_ADCDAT1);
+
+ updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
+ (!(data1 & S3C2410_ADCDAT1_UPDOWN));
+
+ printk_debug(" %c\n", updown ? 'D' : 'U');
+
+ /* TODO we should never get an interrupt with updown set while
+ * the timer is running, but maybe we ought to verify that the
+ * timer isn't running anyways. */
+
+ if (updown) {
+ ts->sample = 0;
+ touch_timer_fire((unsigned long)ts);
+ }
+
+#ifdef CONFIG_ARCH_S3C6400
+ __raw_writel(0x0, ts->base + S3C_ADCCLRWK);
+ __raw_writel(0x0, ts->base + S3C_ADCCLRINT);
+#endif
+
+ if (!atomic_read(&ts->closed))
+ enable_irq(ts->irq_adc);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t stylus_action(int irq, void *dev_id)
+{
+ unsigned long data0;
+ unsigned long data1;
+ struct s3c_ts *ts;
+ unsigned int adccon;
+ struct s3c_ts_mach_info *info;
+
+ ts = (struct s3c_ts *)dev_id;
+ info = ts->info;
+
+ disable_irq(ts->irq_tc);
+
+ data0 = readl(ts->base + S3C2410_ADCDAT0);
+ data1 = readl(ts->base + S3C2410_ADCDAT1);
+ adccon = readl(ts->base + S3C2410_ADCCON);
+
+ ts->xp = (data0 & S3C2410_ADCDAT0_XPDATA_MASK);
+ ts->yp = (data1 & S3C2410_ADCDAT1_YPDATA_MASK);
+
+ ts->timer_enabled = 1;
+
+ /* Use the correct values for updating the timer */
+ mod_timer(&ts->timer, jiffies + msecs_to_jiffies(info->trigger_ms));
+ writel(WAIT4INT(1), ts->base + S3C2410_ADCTSC);
+
+#ifdef CONFIG_ARCH_S3C6400
+ __raw_writel(0x0, ts->base + S3C_ADCCLRWK);
+ __raw_writel(0x0, ts->base + S3C_ADCCLRINT);
+#endif
+
+ if (!atomic_read(&ts->closed))
+ enable_irq(ts->irq_tc);
+
+ return IRQ_HANDLED;
+}
+
+
+static void s3c_ts_close(struct input_dev *dev)
+{
+ struct s3c_ts *ts;
+
+ printk_debug("Close function called\n");
+
+ ts = input_get_drvdata(dev);
+ atomic_set(&ts->closed, 1);
+ disable_irq(ts->irq_adc);
+ disable_irq(ts->irq_tc);
+}
+
+
+
+/* Only disable the interrupts */
+static int s3c_ts_open(struct input_dev *dev)
+{
+ struct s3c_ts *ts;
+
+ printk_debug("Open function called\n");
+
+ ts = input_get_drvdata(dev);
+ atomic_set(&ts->closed, 0);
+ enable_irq(ts->irq_adc);
+ enable_irq(ts->irq_tc);
+ return 0;
+}
+
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+static int __devinit s3c_ts_probe(struct platform_device *pdev)
+{
+ struct s3c_ts_mach_info *info;
+ struct input_dev *indev;
+ int err;
+ struct s3c_ts *ts;
+
+ info = pdev->dev.platform_data;
+ if (!info) {
+ printk_err("Too bad: no platform data attached. Aborting.\n");
+ return -EINVAL;
+ }
+
+ /* Sanity checks */
+ if (!info->trigger_ms || info->probes > S3C24XX_SAMPLE_NUM_MAX) {
+ printk_err("Found an invalid configuration.\n");
+ return -EINVAL;
+ }
+
+
+ ts = kzalloc(sizeof(struct s3c_ts), GFP_KERNEL);
+ ts->info = info;
+ indev = input_allocate_device();
+ if (!ts || !indev) {
+ if (ts)
+ kfree(ts);
+ if (indev)
+ kfree(indev);
+ return -ENOMEM;
+ } else
+ ts->indev = indev;
+
+ /* Get the required source clock */
+ ts->clk = clk_get(&pdev->dev, "adc");
+ if (!ts->clk) {
+ printk_err("Failed to get adc clock source\n");
+ err = -ENOENT;
+ goto err_free_ts;
+ }
+ clk_enable(ts->clk);
+
+ /* @XXX: Get the memory area from the configuration structure */
+ ts->base = ioremap(S3C2410_PA_ADC, 0x20);
+ if (!ts->base) {
+ printk_err("Failed to ioremap register block\n");
+ err = -EINVAL;
+ goto err_put_clock;
+ }
+
+ /* Prepare the passed user configuration values */
+ if ((info->presc & 0xff) > 0)
+ writel(S3C2410_ADCCON_PRSCEN |
+ S3C2410_ADCCON_PRSCVL(info->presc & 0xFF),
+ ts->base + S3C2410_ADCCON);
+ else
+ writel(0, ts->base + S3C2410_ADCCON);
+
+
+ /* Initialise the start delay register (Manual: Do not use zero value) */
+ if ((info->delay & 0xffff) > 0)
+ writel(info->delay & 0xffff, ts->base + S3C2410_ADCDLY);
+ else
+ writel(10000, ts->base + S3C2410_ADCDLY);
+
+ /* Configure the touch screen controller for "Waiting for Interrupt Mode" */
+ writel(WAIT4INT(0), ts->base + S3C2410_ADCTSC);
+
+ ts->indev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ ts->indev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ /*
+ * The data values for the position conversion are coming from the data
+ * sheet of the touch controller (S3C2443: 24-10)
+ */
+ input_set_abs_params(ts->indev, ABS_X, info->xmin, info->xmax, 0, 0);
+ input_set_abs_params(ts->indev, ABS_Y, info->ymin, info->ymax, 0, 0);
+ input_set_abs_params(ts->indev, ABS_PRESSURE, 0, 10, 0, 0);
+
+ sprintf(ts->phys, "ts0");
+
+ indev->open = s3c_ts_open;
+ indev->close = s3c_ts_close;
+ indev->name = s3c_ts_name;
+ indev->phys = ts->phys;
+ indev->id.bustype = BUS_RS232;
+ indev->id.vendor = 0xDEAD;
+ indev->id.product = 0xBEEF;
+ indev->id.version = S3C_TSVERSION;
+
+ /* Get the irqs from the platform device data */
+ ts->irq_adc = platform_get_irq(pdev, 0);
+ err = request_irq(ts->irq_adc, stylus_action, 0, "s3c_action", ts);
+ if (err) {
+ printk_err("Requesting the ADC IRQ %i\n", ts->irq_adc);
+ goto err_put_clock;
+ }
+ printk_debug("Got ADC IRQ %i\n", ts->irq_adc);
+
+ ts->irq_tc = platform_get_irq(pdev, 1);
+ err = request_irq(ts->irq_tc, stylus_updown, 0, "s3c_updown", ts);
+ if (err) {
+ printk_err("Requesting the touch screen IRQ %i\n", ts->irq_tc);
+ goto err_free_adcirq;
+ }
+ printk_debug("Got TC IRQ %i\n", ts->irq_tc);
+
+
+ /* Disable the interrupts first */
+ disable_irq(ts->irq_adc);
+ disable_irq(ts->irq_tc);
+
+ printk_info("%s successfully loaded\n", s3c_ts_name);
+
+ /* All went ok, so register to the input system */
+ err = input_register_device(ts->indev);
+ if (err)
+ goto err_free_tcirq;
+
+
+ /* Configure and init the timer for the command timeouts */
+ init_timer(&ts->timer);
+ ts->timer.function = touch_timer_fire;
+ ts->timer.data = (unsigned long)ts;
+
+ input_set_drvdata(indev, ts);
+ platform_set_drvdata(pdev, ts);
+
+ return 0;
+
+ err_free_tcirq:
+ free_irq(ts->irq_tc, ts);
+
+ err_free_adcirq:
+ free_irq(ts->irq_adc, ts);
+
+ err_put_clock:
+ clk_disable(ts->clk);
+ clk_put(ts->clk);
+
+ err_free_ts:
+ input_free_device(ts->indev);
+ kfree(ts);
+
+ return err;
+}
+
+static int __devexit s3c_ts_remove(struct platform_device *pdev)
+{
+ struct s3c_ts *ts;
+
+ ts = platform_get_drvdata(pdev);
+
+ printk_debug("Removing the device with the ID %i\n", pdev->id);
+
+ if (!atomic_read(&ts->closed)) {
+ disable_irq(ts->irq_adc);
+ disable_irq(ts->irq_tc);
+ }
+
+ free_irq(ts->irq_adc, ts);
+ free_irq(ts->irq_tc, ts);
+
+ if (ts->clk) {
+ clk_disable(ts->clk);
+ clk_put(ts->clk);
+ ts->clk = NULL;
+ }
+
+ input_unregister_device(ts->indev);
+ iounmap(ts->base);
+
+ input_free_device(ts->indev);
+ kfree(ts);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int s3c_ts_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct s3c_ts *ts;
+
+ ts = platform_get_drvdata(dev);
+
+ ts->adccon = readl(ts->base + S3C2410_ADCCON);
+ ts->adctsc = readl(ts->base + S3C2410_ADCTSC);
+ ts->adcdly = readl(ts->base + S3C2410_ADCDLY);
+
+ /* Don't forget to re-enable the interrupts! */
+ disable_irq(ts->irq_adc);
+ disable_irq(ts->irq_tc);
+ clk_disable(ts->clk);
+ return 0;
+}
+
+static int s3c_ts_resume(struct platform_device *pdev)
+{
+ struct s3c_ts *ts;
+
+ ts = platform_get_drvdata(pdev);
+
+ clk_enable(ts->clk);
+ writel(ts->adccon, ts->base + S3C2410_ADCCON);
+ writel(ts->adctsc, ts->base + S3C2410_ADCTSC);
+ writel(ts->adcdly, ts->base + S3C2410_ADCDLY);
+ writel(WAIT4INT(0), ts->base + S3C2410_ADCTSC);
+
+ enable_irq(ts->irq_adc);
+ enable_irq(ts->irq_tc);
+
+ return 0;
+}
+#else
+#define s3c_ts_suspend NULL
+#define s3c_ts_resume NULL
+#endif
+
+static struct platform_driver s3c_ts_driver = {
+ .probe = s3c_ts_probe,
+ .remove = __devexit_p(s3c_ts_remove),
+ .suspend = s3c_ts_suspend,
+ .resume = s3c_ts_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "s3c24xx-ts",
+ },
+};
+
+static char banner[] __initdata = KERN_INFO "S3C Touchscreen driver, (c) 2006 Samsung Electronics\n";
+
+static int __init s3c_ts_init(void)
+{
+ printk(banner);
+ return platform_driver_register(&s3c_ts_driver);
+}
+
+static void __exit s3c_ts_exit(void)
+{
+ platform_driver_unregister(&s3c_ts_driver);
+}
+
+module_init(s3c_ts_init);
+module_exit(s3c_ts_exit);
+
+MODULE_AUTHOR("Samsung AP");
+MODULE_DESCRIPTION("s3c touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/stmp3xxx_ts.c b/drivers/input/touchscreen/stmp3xxx_ts.c
new file mode 100644
index 000000000000..05f787b9d9d4
--- /dev/null
+++ b/drivers/input/touchscreen/stmp3xxx_ts.c
@@ -0,0 +1,386 @@
+/*
+ * Freesclae STMP37XX/STMP378X Touchscreen driver
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/* #define DEBUG*/
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <mach/lradc.h>
+#include <mach/hardware.h>
+#include <mach/regs-lradc.h>
+
+#define TOUCH_DEBOUNCE_TOLERANCE 100
+
+struct stmp3xxx_ts_info {
+ int touch_irq;
+ int device_irq;
+ struct input_dev *idev;
+ enum {
+ TS_STATE_DISABLED,
+ TS_STATE_TOUCH_DETECT,
+ TS_STATE_TOUCH_VERIFY,
+ TS_STATE_X_PLANE,
+ TS_STATE_Y_PLANE,
+ } state;
+ u16 x;
+ u16 y;
+ int sample_count;
+};
+
+static inline void enter_state_touch_detect(struct stmp3xxx_ts_info *info)
+{
+ HW_LRADC_CHn_CLR(2, 0xFFFFFFFF);
+ HW_LRADC_CHn_CLR(3, 0xFFFFFFFF);
+ HW_LRADC_CHn_CLR(4, 0xFFFFFFFF);
+ HW_LRADC_CHn_CLR(5, 0xFFFFFFFF);
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC5_IRQ);
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ);
+ /*
+ * turn off the yplus and yminus pullup and pulldown, and turn off touch
+ * detect (enables yminus, and xplus through a resistor.On a press,
+ * xplus is pulled down)
+ */
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_YMINUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_YPLUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_XMINUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_XPLUS_ENABLE);
+ HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
+ info->state = TS_STATE_TOUCH_DETECT;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_disabled(struct stmp3xxx_ts_info *info)
+{
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_YMINUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_YPLUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_XMINUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_XPLUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
+ info->state = TS_STATE_DISABLED;
+ info->sample_count = 0;
+}
+
+
+static inline void enter_state_x_plane(struct stmp3xxx_ts_info *info)
+{
+ HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_YMINUS_ENABLE);
+ HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_YPLUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_XMINUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_XPLUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+
+ info->state = TS_STATE_X_PLANE;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_y_plane(struct stmp3xxx_ts_info *info)
+{
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_YMINUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_YPLUS_ENABLE);
+ HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_XMINUS_ENABLE);
+ HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_XPLUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ info->state = TS_STATE_Y_PLANE;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_touch_verify(struct stmp3xxx_ts_info *info)
+{
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_YMINUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_YPLUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_XMINUS_ENABLE);
+ HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_XPLUS_ENABLE);
+ HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE);
+
+ info->state = TS_STATE_TOUCH_VERIFY;
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ info->sample_count = 0;
+}
+
+static void process_lradc(struct stmp3xxx_ts_info *info, u16 x, u16 y,
+ int pressure)
+{
+ switch (info->state) {
+ case TS_STATE_X_PLANE:
+ pr_debug("%s: x plane state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (info->sample_count < 2) {
+ info->x = x;
+ info->sample_count++;
+ } else {
+ if (abs(info->x - x) > TOUCH_DEBOUNCE_TOLERANCE)
+ info->sample_count = 1;
+ else {
+ u16 x_c = info->x * (info->sample_count - 1);
+ info->x = (x_c + x) / info->sample_count;
+ info->sample_count++;
+ }
+ }
+ if (info->sample_count > 4)
+ enter_state_y_plane(info);
+ else
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ break;
+
+ case TS_STATE_Y_PLANE:
+ pr_debug("%s: y plane state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (info->sample_count < 2) {
+ info->y = y;
+ info->sample_count++;
+ } else {
+ if (abs(info->y - y) > TOUCH_DEBOUNCE_TOLERANCE)
+ info->sample_count = 1;
+ else {
+ u16 y_c = info->y * (info->sample_count - 1);
+ info->y = (y_c + y) / info->sample_count;
+ info->sample_count++;
+ }
+ }
+ if (info->sample_count > 4)
+ enter_state_touch_verify(info);
+ else
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ break;
+
+ case TS_STATE_TOUCH_VERIFY:
+ pr_debug("%s: touch verify state, sample_count %d\n", __func__,
+ info->sample_count);
+ pr_debug("%s: x %d, y %d\n", __func__, info->x, info->y);
+ input_report_abs(info->idev, ABS_X, info->x);
+ input_report_abs(info->idev, ABS_Y, info->y);
+ input_report_abs(info->idev, ABS_PRESSURE, pressure);
+ input_sync(info->idev);
+ /* fall through */
+ case TS_STATE_TOUCH_DETECT:
+ pr_debug("%s: touch detect state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (pressure) {
+ input_report_abs(info->idev, ABS_PRESSURE, pressure);
+ enter_state_x_plane(info);
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ } else
+ enter_state_touch_detect(info);
+ break;
+
+ default:
+ printk(KERN_ERR "%s: unknown touchscreen state %d\n", __func__,
+ info->state);
+ }
+}
+
+static irqreturn_t ts_handler(int irq, void *dev_id)
+{
+ struct stmp3xxx_ts_info *info = dev_id;
+ u16 x_plus, y_plus;
+ int pressure = 0;
+
+ if (irq == info->touch_irq)
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ);
+ else if (irq == info->device_irq)
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC5_IRQ);
+
+ /* get x, y values */
+ x_plus = HW_LRADC_CHn_RD(LRADC_TOUCH_X_PLUS) & BM_LRADC_CHn_VALUE;
+ y_plus = HW_LRADC_CHn_RD(LRADC_TOUCH_Y_PLUS) & BM_LRADC_CHn_VALUE;
+
+ /* pressed? */
+ if (HW_LRADC_STATUS_RD() & BM_LRADC_STATUS_TOUCH_DETECT_RAW)
+ pressure = 1;
+
+ pr_debug("%s: irq %d, x_plus %d, y_plus %d, pressure %d\n",
+ __func__, irq, x_plus, y_plus, pressure);
+
+ process_lradc(info, x_plus, y_plus, pressure);
+
+ return IRQ_HANDLED;
+}
+
+static int stmp3xxx_ts_probe(struct platform_device *pdev)
+{
+ struct input_dev *idev;
+ struct stmp3xxx_ts_info *info;
+ int ret = 0;
+ struct resource *res;
+
+ idev = input_allocate_device();
+ info = kzalloc(sizeof(struct stmp3xxx_ts_info), GFP_KERNEL);
+ if (idev == NULL || info == NULL) {
+ ret = -ENOMEM;
+ goto out_nomem;
+ }
+
+ idev->name = "STMP3XXX touchscreen";
+ idev->evbit[0] = BIT(EV_ABS);
+ input_set_abs_params(idev, ABS_X, 0, 0xFFF, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, 0xFFF, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
+
+ ret = input_register_device(idev);
+ if (ret)
+ goto out_nomem;
+
+ info->idev = idev;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get IRQ resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->touch_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get IRQ resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->device_irq = res->start;
+
+ ret = request_irq(info->touch_irq, ts_handler, IRQF_DISABLED,
+ "stmp3xxx_ts_touch", info);
+ if (ret)
+ goto out_nodev;
+
+ ret = request_irq(info->device_irq, ts_handler, IRQF_DISABLED,
+ "stmp3xxx_ts_dev", info);
+ if (ret) {
+ free_irq(info->touch_irq, info);
+ goto out_nodev;
+ }
+ enter_state_touch_detect(info);
+
+ hw_lradc_use_channel(LRADC_CH2);
+ hw_lradc_use_channel(LRADC_CH3);
+ hw_lradc_use_channel(LRADC_CH5);
+ hw_lradc_configure_channel(LRADC_CH2, 0, 0, 0);
+ hw_lradc_configure_channel(LRADC_CH3, 0, 0, 0);
+ hw_lradc_configure_channel(LRADC_CH5, 0, 0, 0);
+
+ /* Clear the accumulator & NUM_SAMPLES for the channels */
+ HW_LRADC_CHn_CLR(LRADC_CH2, 0xFFFFFFFF);
+ HW_LRADC_CHn_CLR(LRADC_CH3, 0xFFFFFFFF);
+ HW_LRADC_CHn_CLR(LRADC_CH5, 0xFFFFFFFF);
+
+ hw_lradc_set_delay_trigger(LRADC_DELAY_TRIGGER_TOUCHSCREEN,
+ 0x3c, 0, 0, 8);
+
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC5_IRQ);
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ);
+
+ HW_LRADC_CTRL1_SET(BM_LRADC_CTRL1_LRADC5_IRQ_EN);
+ HW_LRADC_CTRL1_SET(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN);
+
+ platform_set_drvdata(pdev, info);
+ device_init_wakeup(&pdev->dev, 1);
+ goto out;
+
+out_nodev:
+ input_free_device(idev);
+out_nomem:
+ kfree(idev);
+ kfree(info);
+out:
+ return ret;
+}
+
+static int stmp3xxx_ts_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_ts_info *info = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ hw_lradc_unuse_channel(LRADC_CH2);
+ hw_lradc_unuse_channel(LRADC_CH3);
+ hw_lradc_unuse_channel(LRADC_CH5);
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC5_IRQ_EN);
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN);
+
+ free_irq(info->device_irq, info);
+ free_irq(info->touch_irq, info);
+ input_free_device(info->idev);
+
+ enter_state_disabled(info);
+ kfree(info->idev);
+ kfree(info);
+ return 0;
+}
+
+static int stmp3xxx_ts_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+#ifdef CONFIG_PM
+ if (!device_may_wakeup(&pdev->dev)) {
+ hw_lradc_unuse_channel(LRADC_CH2);
+ hw_lradc_unuse_channel(LRADC_CH3);
+ hw_lradc_unuse_channel(LRADC_CH5);
+ }
+#endif
+ return 0;
+}
+
+static int stmp3xxx_ts_resume(struct platform_device *pdev)
+{
+#ifdef CONFIG_PM
+ if (!device_may_wakeup(&pdev->dev)) {
+ hw_lradc_use_channel(LRADC_CH2);
+ hw_lradc_use_channel(LRADC_CH3);
+ hw_lradc_use_channel(LRADC_CH5);
+ }
+#endif
+ return 0;
+}
+
+static struct platform_driver stmp3xxx_ts_driver = {
+ .probe = stmp3xxx_ts_probe,
+ .remove = stmp3xxx_ts_remove,
+ .suspend = stmp3xxx_ts_suspend,
+ .resume = stmp3xxx_ts_resume,
+ .driver = {
+ .name = "stmp3xxx_ts",
+ },
+};
+
+static int __init stmp3xxx_ts_init(void)
+{
+ return platform_driver_register(&stmp3xxx_ts_driver);
+}
+
+static void __exit stmp3xxx_ts_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_ts_driver);
+}
+
+module_init(stmp3xxx_ts_init);
+module_exit(stmp3xxx_ts_exit);
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
new file mode 100644
index 000000000000..6e18ffbebd47
--- /dev/null
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file tsc2007.c
+ *
+ * @brief Driver for TI's tsc2007 I2C Touch Screen Controller.
+ *
+ * This driver is based on the driver written by Bill Gatliff
+ * Copyright (C) 2005 Bill Gatliff <bgat at billgatliff.com>
+ * Changes for 2.6.20 kernel by Nicholas Chen <nchen at cs.umd.edu>
+ *
+ * @ingroup touchscreen
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/bcd.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <asm/mach/irq.h>
+
+#define DRIVER_NAME "tsc2007"
+
+enum tsc2007_pd {
+ PD_POWERDOWN = 0, /* penirq */
+ PD_IREFOFF_ADCON = 1, /* no penirq */
+ PD_IREFON_ADCOFF = 2, /* penirq */
+ PD_IREFON_ADCON = 3, /* no penirq */
+ PD_PENIRQ_ARM = PD_IREFON_ADCOFF,
+ PD_PENIRQ_DISARM = PD_IREFON_ADCON,
+};
+
+enum tsc2007_m {
+ M_12BIT = 0,
+ M_8BIT = 1
+};
+
+enum tsc2007_cmd {
+ MEAS_TEMP0 = 0,
+ MEAS_IN1 = 2,
+ MEAS_XPOS = 12,
+ MEAS_YPOS = 13,
+ MEAS_Z1POS = 14,
+ MEAS_Z2POS = 15
+};
+
+#define tsc2007_CMD(cn, pdn, m) (((cn) << 4) | ((pdn) << 2) | ((m) << 1))
+
+#define ADC_MAX ((1 << 12) - 1)
+
+struct tsc2007_data {
+ struct i2c_client *client;
+ struct input_dev *idev;
+ struct timer_list penirq_timer;
+ struct task_struct *tstask;
+ u32 ts_thread_cnt;
+ struct completion penirq_completion;
+ struct completion penup_completion;
+ enum tsc2007_m m;
+ int penirq;
+ int penup_threshold;
+ struct regulator *vdd_reg;
+ int opened;
+};
+
+static int tsc2007_read(struct tsc2007_data *data,
+ enum tsc2007_cmd cmd, enum tsc2007_pd pd, int *val)
+{
+ unsigned char c;
+ unsigned char d[2];
+ int ret;
+
+ c = tsc2007_CMD(cmd, pd, data->m);
+
+ ret = i2c_master_send(data->client, &c, 1);
+
+ if (ret < 0)
+ goto err;
+
+ udelay(20);
+ ret = i2c_master_recv(data->client, d, data->m == M_12BIT ? 2 : 1);
+ if (ret < 0)
+ goto err;
+
+ if (val) {
+ *val = d[0];
+ *val <<= 4;
+ if (data->m == M_12BIT)
+ *val += (d[1] >> 4);
+ }
+
+ return 0;
+ err:
+ return -ENODEV;
+}
+
+static inline int tsc2007_read_xpos(struct tsc2007_data *d, enum
+ tsc2007_pd pd, int *x)
+{
+ return tsc2007_read(d, MEAS_XPOS, pd, x);
+}
+
+static inline int tsc2007_read_ypos(struct tsc2007_data *d, enum
+ tsc2007_pd pd, int *y)
+{
+ return tsc2007_read(d, MEAS_YPOS, pd, y);
+}
+
+static inline int tsc2007_read_pressure(struct tsc2007_data *d, enum
+ tsc2007_pd pd, int *p)
+{
+ return tsc2007_read(d, MEAS_Z1POS, pd, p);
+}
+
+static inline int tsc2007_powerdown(struct tsc2007_data *d)
+{
+ /* we don't have a distinct powerdown command,
+ so do a benign read with the PD bits cleared */
+ return tsc2007_read(d, MEAS_IN1, PD_POWERDOWN, 0);
+}
+
+#define PENUP_TIMEOUT 10
+
+static irqreturn_t tsc2007_penirq(int irq, void *v)
+{
+ struct tsc2007_data *d = v;
+
+ disable_irq(d->penirq);
+ complete(&d->penirq_completion);
+ return IRQ_HANDLED;
+}
+
+static void tsc2007_pen_up(unsigned long v)
+{
+ struct tsc2007_data *d = (struct tsc2007_data *)v;
+
+ complete(&d->penup_completion);
+ return;
+}
+
+static inline void tsc2007_restart_pen_up_timer(struct tsc2007_data *d)
+{
+ mod_timer(&d->penirq_timer, jiffies + (PENUP_TIMEOUT * HZ) / 1000);
+}
+
+static int tsc2007ts_thread(void *v)
+{
+ struct tsc2007_data *d = v;
+
+ if (d->ts_thread_cnt)
+ return -EINVAL;
+ d->ts_thread_cnt = 1;
+
+ while (1) {
+ unsigned int x = 0, y = 0, p = 0;
+
+ if (kthread_should_stop())
+ break;
+ /* Wait for an Pen down interrupt */
+ if (wait_for_completion_interruptible_timeout
+ (&d->penirq_completion, HZ) <= 0)
+ continue;
+
+ tsc2007_read_xpos(d, PD_PENIRQ_DISARM, &x);
+ tsc2007_read_ypos(d, PD_PENIRQ_DISARM, &y);
+ tsc2007_read_pressure(d, PD_PENIRQ_DISARM, &p);
+ input_report_abs(d->idev, ABS_X, 4096 - x);
+ input_report_abs(d->idev, ABS_Y, 4096 - y);
+ input_report_abs(d->idev, ABS_PRESSURE, p);
+ input_sync(d->idev);
+
+ while (p > d->penup_threshold) {
+ tsc2007_restart_pen_up_timer(d);
+ wait_for_completion_interruptible(&d->penup_completion);
+ /* Pen Down */
+ tsc2007_read_xpos(d, PD_PENIRQ_DISARM, &x);
+ tsc2007_read_ypos(d, PD_PENIRQ_DISARM, &y);
+ tsc2007_read_pressure(d, PD_PENIRQ_DISARM, &p);
+ if (p <= d->penup_threshold)
+ break;
+
+ input_report_abs(d->idev, ABS_X, 4096 - x);
+ input_report_abs(d->idev, ABS_Y, 4096 - y);
+ input_report_abs(d->idev, ABS_PRESSURE, p);
+ input_sync(d->idev);
+ };
+
+ /* Pen Up */
+ input_report_abs(d->idev, ABS_X, 4096 - x);
+ input_report_abs(d->idev, ABS_Y, 4096 - y);
+ input_report_abs(d->idev, ABS_PRESSURE, 0);
+ input_sync(d->idev);
+
+ tsc2007_read(d, MEAS_TEMP0, PD_PENIRQ_ARM, 0);
+ enable_irq(d->penirq);
+
+ }
+
+ d->ts_thread_cnt = 0;
+ return 0;
+}
+
+/*!
+ * This function puts the touch screen controller in low-power mode/state.
+ *
+ * @param pdev the device structure used to give information on touch screen
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int tsc2007_suspend(struct i2c_client *client, pm_message_t state)
+{
+ struct tsc2007_data *d = i2c_get_clientdata(client);
+
+ if (!IS_ERR(d->tstask) && d->opened)
+ kthread_stop(d->tstask);
+
+ return 0;
+}
+
+/*!
+ * This function brings the touch screen controller back from low-power state.
+ *
+ * @param pdev the device structure used to give information on touch screen
+ * to resume
+ *
+ * @return The function always returns 0.
+ */
+static int tsc2007_resume(struct i2c_client *client)
+{
+ struct tsc2007_data *d = i2c_get_clientdata(client);
+
+ if (d->opened)
+ d->tstask = kthread_run(tsc2007ts_thread, d, DRIVER_NAME "tsd");
+
+ return 0;
+}
+
+static int tsc2007_idev_open(struct input_dev *idev)
+{
+ struct tsc2007_data *d = input_get_drvdata(idev);
+ int ret = 0;
+
+ d->penirq_timer.data = (unsigned long)d;
+ d->penirq_timer.function = tsc2007_pen_up;
+
+ init_completion(&d->penup_completion);
+
+ d->tstask = kthread_run(tsc2007ts_thread, d, DRIVER_NAME "tsd");
+ if (IS_ERR(d->tstask))
+ ret = PTR_ERR(d->tstask);
+ else
+ d->opened++;
+
+ return ret;
+}
+
+static void tsc2007_idev_close(struct input_dev *idev)
+{
+ struct tsc2007_data *d = input_get_drvdata(idev);
+ if (!IS_ERR(d->tstask))
+ kthread_stop(d->tstask);
+
+ del_timer_sync(&d->penirq_timer);
+
+ if (d->opened > 0)
+ d->opened--;
+}
+
+static int tsc2007_driver_register(struct tsc2007_data *data)
+{
+ struct input_dev *idev;
+ int ret = 0;
+
+ init_timer(&data->penirq_timer);
+ data->penirq_timer.data = (unsigned long)data;
+ data->penirq_timer.function = tsc2007_pen_up;
+
+ init_completion(&data->penirq_completion);
+
+ if (data->penirq) {
+ ret =
+ request_irq(data->penirq, tsc2007_penirq, IRQF_TRIGGER_LOW,
+ DRIVER_NAME, data);
+ if (!ret) {
+ printk(KERN_INFO "%s: Registering Touchscreen device\n",
+ __func__);
+ set_irq_wake(data->penirq, 1);
+ } else {
+ printk(KERN_ERR "%s: Cannot grab irq %d\n",
+ __func__, data->penirq);
+ }
+ }
+ idev = input_allocate_device();
+ data->idev = idev;
+ input_set_drvdata(idev, data);
+ idev->name = DRIVER_NAME;
+ idev->evbit[0] = BIT(EV_ABS);
+ idev->open = tsc2007_idev_open;
+ idev->close = tsc2007_idev_close;
+ idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+ input_set_abs_params(idev, ABS_X, 0, ADC_MAX, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, ADC_MAX, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0);
+
+ if (!ret)
+ ret = input_register_device(idev);
+
+ return ret;
+}
+
+static int tsc2007_i2c_remove(struct i2c_client *client)
+{
+ int err;
+ struct tsc2007_data *d = i2c_get_clientdata(client);
+ struct mxc_tsc_platform_data *tsc_data;
+
+ free_irq(d->penirq, d);
+ input_unregister_device(d->idev);
+
+ err = i2c_detach_client(client);
+ if (err) {
+ dev_err(&client->dev, "Client deregistration failed, "
+ "client not detached.\n");
+ return err;
+ }
+
+ tsc_data = (struct mxc_tsc_platform_data *)(client->dev).platform_data;
+ if (tsc_data && tsc_data->inactive)
+ tsc_data->inactive();
+
+ if (d->vdd_reg) {
+ regulator_disable(d->vdd_reg);
+ regulator_put(d->vdd_reg);
+ d->vdd_reg = NULL;
+ }
+ return 0;
+}
+
+static int tsc2007_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct tsc2007_data *data;
+ struct mxc_tsc_platform_data *tsc_data;
+ int err = 0;
+
+ data = kzalloc(sizeof(struct tsc2007_data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ data->penirq = client->irq;
+
+ tsc_data = (struct mxc_tsc_platform_data *)(client->dev).platform_data;
+ if (tsc_data && tsc_data->vdd_reg) {
+ if (tsc_data->penup_threshold > (ADC_MAX >> 3))
+ data->penup_threshold = (ADC_MAX >> 3);
+ else if (tsc_data->penup_threshold > 0)
+ data->penup_threshold = tsc_data->penup_threshold;
+ else
+ data->penup_threshold = 10;
+
+ data->vdd_reg = regulator_get(&client->dev, tsc_data->vdd_reg);
+ if (!IS_ERR(data->vdd_reg))
+ regulator_enable(data->vdd_reg);
+ else
+ data->vdd_reg = NULL;
+ if (tsc_data->active)
+ tsc_data->active();
+ } else {
+ data->vdd_reg = NULL;
+ data->penup_threshold = 10;
+ }
+
+ err = tsc2007_powerdown(data);
+ if (err >= 0) {
+ data->m = M_12BIT;
+
+ err = tsc2007_driver_register(data);
+ if (err < 0)
+ goto exit;
+
+ return 0;
+ }
+
+ exit:
+ return err;
+}
+
+static const struct i2c_device_id tsc2007_id[] = {
+ { "tsc2007", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, tsc2007_id);
+
+static struct i2c_driver tsc2007_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = tsc2007_i2c_probe,
+ .remove = tsc2007_i2c_remove,
+ .suspend = tsc2007_suspend,
+ .resume = tsc2007_resume,
+ .command = NULL,
+ .id_table = tsc2007_id,
+};
+
+static int __init tsc2007_init(void)
+{
+ return i2c_add_driver(&tsc2007_driver);
+}
+
+static void __exit tsc2007_exit(void)
+{
+ i2c_del_driver(&tsc2007_driver);
+}
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("tsc2007 Touch Screen Controller driver");
+MODULE_LICENSE("GPL");
+
+module_init(tsc2007_init);
+module_exit(tsc2007_exit);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index e7fb7d2fcbfc..f7aa808bdc37 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -17,6 +17,13 @@ config LEDS_CLASS
comment "LED drivers"
+config LEDS_STMP378X
+ tristate "Support for PWM LEDs on STMP378X"
+ depends on LEDS_CLASS && MACH_STMP378X
+ help
+ This option enables support for the LEDs connected to PWM
+ outputs on the Freescale STMP378X.
+
config LEDS_ATMEL_PWM
tristate "LED Support using Atmel PWM outputs"
depends on LEDS_CLASS && ATMEL_PWM
@@ -24,6 +31,10 @@ config LEDS_ATMEL_PWM
This option enables support for LEDs driven using outputs
of the dedicated PWM controller found on newer Atmel SOCs.
+config LEDS_MC13892
+ tristate "LED Support for mc13892 pmic"
+ depends on LEDS_CLASS && MXC_MC13892_LIGHT
+
config LEDS_LOCOMO
tristate "LED Support for Locomo device"
depends on LEDS_CLASS && SHARP_LOCOMO
@@ -113,6 +124,12 @@ config LEDS_GPIO
outputs. To be useful the particular board must have LEDs
and they must be connected to the GPIO lines.
+config LEDS_PWM
+ tristate "LED Support for PWM connected LEDs"
+ depends on LEDS_CLASS && GENERIC_PWM
+ help
+ Enables support for LEDs connected to PWM outputs.
+
config LEDS_HP_DISK
tristate "LED Support for disk protection LED on HP notebooks"
depends on LEDS_CLASS && ACPI
@@ -192,6 +209,14 @@ config LEDS_TRIGGER_IDE_DISK
This allows LEDs to be controlled by IDE disk activity.
If unsure, say Y.
+config LEDS_TRIGGER_DIM
+ tristate "LED Dimmer Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ Regulates the brightness of an LED based on the 1-minute CPU
+ load average. Ideal for PWM-driven LEDs.
+ If unsure, say Y.
+
config LEDS_TRIGGER_HEARTBEAT
tristate "LED Heartbeat Trigger"
depends on LEDS_TRIGGERS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index e1967a29850e..18a8a10bb8c2 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -5,7 +5,9 @@ obj-$(CONFIG_LEDS_CLASS) += led-class.o
obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
# LED Platform Drivers
+obj-$(CONFIG_LEDS_STMP378X) += leds-stmp378x-pwm.o
obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
+obj-$(CONFIG_LEDS_MC13892) += leds-mc13892.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
@@ -17,6 +19,7 @@ obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
+obj-$(CONFIG_LEDS_PWM) += leds-pwm.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
@@ -27,6 +30,7 @@ obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o
# LED Triggers
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
+obj-$(CONFIG_LEDS_TRIGGER_DIM) += ledtrig-dim.o
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
diff --git a/drivers/leds/leds-mc13892.c b/drivers/leds/leds-mc13892.c
new file mode 100644
index 000000000000..9edd20446235
--- /dev/null
+++ b/drivers/leds/leds-mc13892.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/pmic_light.h>
+
+static void mc13892_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct platform_device *dev = to_platform_device(led_cdev->dev->parent);
+ int led_ch;
+
+ switch (dev->id) {
+ case 'r':
+ led_ch = LIT_RED;
+ break;
+ case 'g':
+ led_ch = LIT_GREEN;
+ break;
+ case 'b':
+ led_ch = LIT_BLUE;
+ break;
+ default:
+ return;
+ }
+
+ /* set current with medium value, in case current is too large */
+ mc13892_bklit_set_current(led_ch, LIT_CURR_12);
+ /* max duty cycle is 63, brightness needs to be divided by 4 */
+ mc13892_bklit_set_dutycycle(led_ch, value / 4);
+
+}
+
+static int mc13892_led_remove(struct platform_device *dev)
+{
+ struct led_classdev *led_cdev = platform_get_drvdata(dev);
+
+ led_classdev_unregister(led_cdev);
+ kfree(led_cdev->name);
+ kfree(led_cdev);
+
+ return 0;
+}
+
+#define LED_NAME_LEN 16
+
+static int mc13892_led_probe(struct platform_device *dev)
+{
+ int ret;
+ struct led_classdev *led_cdev;
+ char *name;
+
+ led_cdev = kzalloc(sizeof(struct led_classdev), GFP_KERNEL);
+ if (led_cdev == NULL) {
+ dev_err(&dev->dev, "No memory for device\n");
+ return -ENOMEM;
+ }
+ name = kzalloc(LED_NAME_LEN, GFP_KERNEL);
+ if (name == NULL) {
+ dev_err(&dev->dev, "No memory for device\n");
+ ret = -ENOMEM;
+ goto exit_err;
+ }
+
+ strcpy(name, dev->name);
+ ret = strlen(dev->name);
+ if (ret > LED_NAME_LEN - 2) {
+ dev_err(&dev->dev, "led name is too long\n");
+ goto exit_err1;
+ }
+ name[ret] = dev->id;
+ name[ret + 1] = '\0';
+ led_cdev->name = name;
+ led_cdev->brightness_set = mc13892_led_set;
+
+ ret = led_classdev_register(&dev->dev, led_cdev);
+ if (ret < 0) {
+ dev_err(&dev->dev, "led_classdev_register failed\n");
+ goto exit_err1;
+ }
+
+ platform_set_drvdata(dev, led_cdev);
+
+ return 0;
+ exit_err1:
+ kfree(led_cdev->name);
+ exit_err:
+ kfree(led_cdev);
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int mc13892_led_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct led_classdev *led_cdev = platform_get_drvdata(dev);
+
+ led_classdev_suspend(led_cdev);
+ return 0;
+}
+
+static int mc13892_led_resume(struct platform_device *dev)
+{
+ struct led_classdev *led_cdev = platform_get_drvdata(dev);
+
+ led_classdev_resume(led_cdev);
+ return 0;
+}
+#else
+#define mc13892_led_suspend NULL
+#define mc13892_led_resume NULL
+#endif
+
+static struct platform_driver mc13892_led_driver = {
+ .probe = mc13892_led_probe,
+ .remove = mc13892_led_remove,
+ .suspend = mc13892_led_suspend,
+ .resume = mc13892_led_resume,
+ .driver = {
+ .name = "pmic_leds",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mc13892_led_init(void)
+{
+ return platform_driver_register(&mc13892_led_driver);
+}
+
+static void __exit mc13892_led_exit(void)
+{
+ platform_driver_unregister(&mc13892_led_driver);
+}
+
+module_init(mc13892_led_init);
+module_exit(mc13892_led_exit);
+
+MODULE_DESCRIPTION("Led driver for PMIC mc13892");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
new file mode 100644
index 000000000000..fe2477e777e2
--- /dev/null
+++ b/drivers/leds/leds-pwm.c
@@ -0,0 +1,189 @@
+/*
+ * drivers/leds/leds-pwm.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * Code rebased on original leds-pwm.c from Bill Gatliff
+ *
+ * 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/platform_device.h>
+#include <linux/leds.h>
+#include <linux/io.h>
+#include <linux/pwm.h>
+#include <linux/pwm-led.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+
+struct led_pwm {
+ struct led_classdev led;
+ struct pwm_channel *pwm;
+ unsigned long period;
+};
+
+static void
+led_pwm_brightness_set(struct led_classdev *c,
+ enum led_brightness b)
+{
+ struct led_pwm *led;
+ unsigned long period;
+
+ period = 1000000000UL;
+ led = container_of(c, struct led_pwm, led);
+ led->period = period;
+ pwm_set_period_ns(led->pwm, period);
+}
+
+
+static enum led_brightness
+led_pwm_brightness_get(struct led_classdev *c)
+{
+ struct led_pwm *led;
+ led = container_of(c, struct led_pwm, led);
+ return led->period;
+}
+
+static int
+led_pwm_blink_set(struct led_classdev *c,
+ unsigned long *on_ms,
+ unsigned long *off_ms)
+{
+ struct led_pwm *led;
+ struct pwm_channel_config cfg;
+
+ led = container_of(c, struct led_pwm, led);
+
+ if (*on_ms == 0 && *off_ms == 0) {
+ *on_ms = 1000UL;
+ *off_ms = 1000UL;
+ }
+
+ cfg.config_mask = PWM_CONFIG_DUTY_NS
+ | PWM_CONFIG_PERIOD_NS;
+
+ cfg.duty_ns = *on_ms * 1000000000UL;
+ cfg.period_ns = (*on_ms + *off_ms) * 1000000000UL;
+
+ return pwm_config(led->pwm, &cfg);
+}
+
+
+static int __init
+led_pwm_probe(struct platform_device *pdev)
+{
+ struct pwm_led_platform_data *pdata = pdev->dev.platform_data;
+ struct led_pwm *led;
+ int ret;
+
+ if (!pdata || !pdata->led_info) {
+ return -EINVAL;
+ }
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->pwm = pwm_request(pdata->bus_id, pdata->chan,
+ pdata->led_info->name);
+
+ if (!led->pwm) {
+ ret = -EINVAL;
+ goto err_pwm_request;
+ }
+
+ platform_set_drvdata(pdev, led);
+
+ led->led.name = pdata->led_info->name;
+ led->led.default_trigger = pdata->led_info->default_trigger;
+ led->led.brightness_set = led_pwm_brightness_set;
+ led->led.brightness_get = led_pwm_brightness_get;
+ led->led.blink_set = led_pwm_blink_set;
+ led->led.brightness = LED_OFF;
+
+ ret = pwm_config(led->pwm, pdata->config);
+ if (ret)
+ goto err_pwm_config;
+
+ pwm_start(led->pwm);
+
+ ret = led_classdev_register(&pdev->dev, &led->led);
+ if (ret < 0)
+ goto err_classdev_register;
+
+ return 0;
+
+err_classdev_register:
+ pwm_stop(led->pwm);
+err_pwm_config:
+ pwm_free(led->pwm);
+err_pwm_request:
+ kfree(led);
+
+ return ret;
+}
+
+static int
+led_pwm_remove(struct platform_device *pdev)
+{
+ struct led_pwm *led = platform_get_drvdata(pdev);
+
+ led_classdev_unregister(&led->led);
+
+ if (led->pwm) {
+ pwm_stop(led->pwm);
+ pwm_free(led->pwm);
+ }
+
+ kfree(led);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+
+static struct platform_driver led_pwm_driver = {
+ .driver = {
+ .name = "leds-pwm",
+ .owner = THIS_MODULE,
+ },
+ .probe = led_pwm_probe,
+ .remove = led_pwm_remove,
+};
+
+
+static int __init led_pwm_modinit(void)
+{
+ return platform_driver_register(&led_pwm_driver);
+}
+late_initcall(led_pwm_modinit);
+
+
+static void __exit led_pwm_modexit(void)
+{
+ platform_driver_unregister(&led_pwm_driver);
+}
+module_exit(led_pwm_modexit);
+
+
+MODULE_AUTHOR("Hector Oron <Hector.Oron <at> digi.com>");
+MODULE_DESCRIPTION("Driver for LEDs with PWM-controlled brightness");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:leds-pwm");
+
diff --git a/drivers/leds/leds-stmp378x-pwm.c b/drivers/leds/leds-stmp378x-pwm.c
new file mode 100644
index 000000000000..f0865db4eb90
--- /dev/null
+++ b/drivers/leds/leds-stmp378x-pwm.c
@@ -0,0 +1,190 @@
+/*
+ * Freescale STMP378X PWM LED driver
+ *
+ * Author: Drew Benedetti <drewb@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <mach/hardware.h>
+#include <mach/regs-pwm.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/pwm-led.h>
+#include <mach/stmp3xxx.h>
+
+/* Up to 5 PWM lines are available. */
+#define PWM_MAX 5
+
+/* PWM enables are the lowest PWM_MAX bits of HW_PWM_CTRL register */
+#define BM_PWM_CTRL_PWM_ENABLE(n) ((1<<(n)) & ((1<<(PWM_MAX))-1))
+#define BF_PWM_PERIODn_SETTINGS \
+ (BF_PWM_PERIODn_CDIV(5) | /* divide by 64 */ \
+ BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */ \
+ BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */ \
+ BF_PWM_PERIODn_PERIOD(LED_FULL)) /* 255 cycles */
+
+struct stmp378x_led {
+ struct led_classdev led_dev;
+ int in_use;
+};
+
+static struct stmp378x_led leds[PWM_MAX];
+
+static struct clk *pwm_clk;
+
+static void stmp378x_pwm_led_brightness_set(struct led_classdev *pled,
+ enum led_brightness value)
+{
+ unsigned int pwmn;
+
+ pwmn = container_of(pled, struct stmp378x_led, led_dev) - leds;
+
+ if (pwmn < PWM_MAX && leds[pwmn].in_use) {
+ HW_PWM_CTRL_CLR(BM_PWM_CTRL_PWM_ENABLE(pwmn));
+ HW_PWM_ACTIVEn_WR(pwmn, BF_PWM_ACTIVEn_INACTIVE(value) |
+ BF_PWM_ACTIVEn_ACTIVE(0));
+ HW_PWM_PERIODn_WR(pwmn, BF_PWM_PERIODn_SETTINGS);
+ HW_PWM_CTRL_SET(BM_PWM_CTRL_PWM_ENABLE(pwmn));
+ }
+}
+
+static int stmp378x_pwm_led_probe(struct platform_device *pdev)
+{
+ struct led_classdev *led;
+ unsigned int pwmn;
+ int leds_in_use = 0, rc = 0;
+ int i;
+
+ stmp3xxx_reset_block(REGS_PWM_BASE, 1);
+
+ pwm_clk = clk_get(&pdev->dev, "pwm");
+ if (IS_ERR(pwm_clk)) {
+ rc = PTR_ERR(pwm_clk);
+ return rc;
+ }
+
+ clk_enable(pwm_clk);
+
+ for (i = 0; i < pdev->num_resources; i++) {
+
+ if (pdev->resource[i].flags & IORESOURCE_DISABLED)
+ continue;
+
+ pwmn = pdev->resource[i].start;
+ if (pwmn >= PWM_MAX) {
+ dev_err(&pdev->dev, "PWM %d doesn't exist\n", pwmn);
+ continue;
+ }
+
+ rc = pwm_led_pinmux_request(pwmn, "stmp378x_pwm_led");
+ if (rc) {
+ dev_err(&pdev->dev,
+ "PWM %d is not available (err=%d)\n",
+ pwmn, rc);
+ continue;
+ }
+
+ led = &leds[pwmn].led_dev;
+
+ led->flags = pdev->resource[i].flags;
+ led->name = pdev->resource[i].name;
+ led->brightness = LED_HALF;
+ led->flags = 0;
+ led->brightness_set = stmp378x_pwm_led_brightness_set;
+ led->default_trigger = 0;
+
+ rc = led_classdev_register(&pdev->dev, led);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Unable to register LED device %d (err=%d)\n",
+ pwmn, rc);
+ pwm_led_pinmux_free(pwmn, "stmp378x_pwm_led");
+ continue;
+ }
+
+ /* PWM LED is available now */
+ leds[pwmn].in_use = !0;
+ leds_in_use++;
+
+ /* Set default brightness */
+ stmp378x_pwm_led_brightness_set(led, LED_HALF);
+ }
+
+ if (leds_in_use == 0) {
+ dev_info(&pdev->dev, "No PWM LEDs available\n");
+ clk_disable(pwm_clk);
+ clk_put(pwm_clk);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int stmp378x_pwm_led_remove(struct platform_device *pdev)
+{
+ unsigned int pwmn;
+
+ for (pwmn = 0; pwmn < PWM_MAX; pwmn++) {
+
+ if (!leds[pwmn].in_use)
+ continue;
+
+ /* Disable LED */
+ HW_PWM_CTRL_CLR(BM_PWM_CTRL_PWM_ENABLE(pwmn));
+ HW_PWM_ACTIVEn_WR(pwmn, BF_PWM_ACTIVEn_INACTIVE(0) |
+ BF_PWM_ACTIVEn_ACTIVE(0));
+ HW_PWM_PERIODn_WR(pwmn, BF_PWM_PERIODn_SETTINGS);
+
+ led_classdev_unregister(&leds[pwmn].led_dev);
+ pwm_led_pinmux_free(pwmn, "stmp378x_pwm_led");
+
+ leds[pwmn].led_dev.name = 0;
+ leds[pwmn].in_use = 0;
+ }
+
+ clk_disable(pwm_clk);
+ clk_put(pwm_clk);
+
+ return 0;
+}
+
+
+static struct platform_driver stmp378x_pwm_led_driver = {
+ .probe = stmp378x_pwm_led_probe,
+ .remove = stmp378x_pwm_led_remove,
+ .driver = {
+ .name = "stmp378x-pwm-led",
+ },
+};
+
+static int __init stmp378x_pwm_led_init(void)
+{
+ return platform_driver_register(&stmp378x_pwm_led_driver);
+}
+
+static void __exit stmp378x_pwm_led_exit(void)
+{
+ platform_driver_unregister(&stmp378x_pwm_led_driver);
+}
+
+module_init(stmp378x_pwm_led_init);
+module_exit(stmp378x_pwm_led_exit);
+
+MODULE_AUTHOR("Drew Benedetti <drewb@embeddedalley.com>");
+MODULE_DESCRIPTION("STMP378X PWM LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-dim.c b/drivers/leds/ledtrig-dim.c
new file mode 100644
index 000000000000..04dbaf66a49e
--- /dev/null
+++ b/drivers/leds/ledtrig-dim.c
@@ -0,0 +1,95 @@
+/*
+ * LED Dim Trigger
+ *
+ * Copyright (C) 2008 Bill Gatliff <bgat <at> billgatliff.com>
+ *
+ * "Dims" an LED based on system load. Derived from Atsushi Nemoto's
+ * ledtrig-heartbeat.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/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/leds.h>
+
+#include "leds.h"
+
+struct dim_trig_data {
+ struct timer_list timer;
+};
+
+
+static void
+led_dim_function(unsigned long data)
+{
+ struct led_classdev *led_cdev = (struct led_classdev *)data;
+ struct dim_trig_data *dim_data = led_cdev->trigger_data;
+ unsigned int brightness;
+
+ brightness = ((LED_FULL - LED_OFF) * avenrun[0]) / EXP_1;
+ if (brightness > LED_FULL)
+ brightness = LED_FULL;
+
+ led_set_brightness(led_cdev, brightness);
+ mod_timer(&dim_data->timer, jiffies + msecs_to_jiffies(500));
+}
+
+
+static void
+dim_trig_activate(struct led_classdev *led_cdev)
+{
+ struct dim_trig_data *dim_data;
+
+ dim_data = kzalloc(sizeof(*dim_data), GFP_KERNEL);
+ if (!dim_data)
+ return;
+
+ led_cdev->trigger_data = dim_data;
+ setup_timer(&dim_data->timer,
+ led_dim_function, (unsigned long)led_cdev);
+ led_dim_function(dim_data->timer.data);
+}
+
+
+static void
+dim_trig_deactivate(struct led_classdev *led_cdev)
+{
+ struct dim_trig_data *dim_data = led_cdev->trigger_data;
+
+ if (dim_data) {
+ del_timer_sync(&dim_data->timer);
+ kfree(dim_data);
+ }
+}
+
+
+static struct led_trigger dim_led_trigger = {
+ .name = "dim",
+ .activate = dim_trig_activate,
+ .deactivate = dim_trig_deactivate,
+};
+
+
+static int __init dim_trig_init(void)
+{
+ return led_trigger_register(&dim_led_trigger);
+}
+module_init(dim_trig_init);
+
+
+static void __exit dim_trig_exit(void)
+{
+ led_trigger_unregister(&dim_led_trigger);
+}
+module_exit(dim_trig_exit);
+
+
+MODULE_AUTHOR("Bill Gatliff <bgat <at> billgatliff.com>");
+MODULE_DESCRIPTION("Dim LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 5189c4eb439f..92990ac87b39 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -387,4 +387,6 @@ config USB_MR800
To compile this driver as a module, choose M here: the
module will be called radio-mr800.
+source "drivers/media/radio/stfm1000/Kconfig"
+
endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 240ec63cdafc..00cf526d8e19 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -20,4 +20,6 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_USB_SI470X) += radio-si470x.o
obj-$(CONFIG_USB_MR800) += radio-mr800.o
+obj-$(CONFIG_RADIO_STFM1000) += stfm1000/
+
EXTRA_CFLAGS += -Isound
diff --git a/drivers/media/radio/stfm1000/Kconfig b/drivers/media/radio/stfm1000/Kconfig
new file mode 100644
index 000000000000..ef30bf87de0b
--- /dev/null
+++ b/drivers/media/radio/stfm1000/Kconfig
@@ -0,0 +1,26 @@
+config RADIO_STFM1000
+ tristate "STFM1000 support"
+ depends on I2C && VIDEO_V4L2 && ARCH_STMP3XXX
+ select I2C_ALGOBIT
+ ---help---
+ Choose Y here if you have this FM radio card, and then fill in the
+ port address below.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stfm1000.
+
+config RADIO_STFM1000_ALSA
+ tristate "STFM1000 audio support"
+ depends on RADIO_STFM1000 && SND
+ select SND_PCM
+ ---help---
+ This is a video4linux driver for direct (DMA) audio in
+ STFM1000 using ALSA
+
+ To compile this driver as a module, choose M here: the
+ module will be called stfm1000-alsa.
diff --git a/drivers/media/radio/stfm1000/Makefile b/drivers/media/radio/stfm1000/Makefile
new file mode 100644
index 000000000000..01f354001a64
--- /dev/null
+++ b/drivers/media/radio/stfm1000/Makefile
@@ -0,0 +1,14 @@
+stfm1000-objs := stfm1000-core.o stfm1000-i2c.o stfm1000-precalc.o stfm1000-filter.o stfm1000-rds.o
+
+clean-files += stfm1000-precalc.o
+
+obj-$(CONFIG_RADIO_STFM1000) += stfm1000.o
+obj-$(CONFIG_RADIO_STFM1000_ALSA) += stfm1000-alsa.o
+
+stfm1000-core.o: $(obj)/stfm1000-precalc.h
+
+hostprogs-$(CONFIG_RADIO_STFM1000) := gen-precalc
+$(obj)/stfm1000-precalc.c: $(obj)/gen-precalc $(src)/stfm1000-regs.h
+ $(obj)/gen-precalc >$@
+
+EXTRA_CFLAGS += -Idrivers/media/radio
diff --git a/drivers/media/radio/stfm1000/gen-precalc.c b/drivers/media/radio/stfm1000/gen-precalc.c
new file mode 100644
index 000000000000..d3797dbef815
--- /dev/null
+++ b/drivers/media/radio/stfm1000/gen-precalc.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/* generate precalculated tables */
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "stfm1000-regs.h"
+
+static void generate_tune1(void)
+{
+ int start, end;
+ int ndiv; // N Divider in PLL
+ int incr; // Increment in PLL
+ int cicosr; // CIC oversampling ratio
+ int sdnominal; // value to serve pilot/interpolator loop in SD
+ int i, temp; // used in tuning table construction
+
+ start = STFM1000_FREQUENCY_100KHZ_MIN;
+ end = start + STFM1000_FREQUENCY_100KHZ_RANGE;
+
+ printf("const struct stfm1000_tune1\n"
+ "stfm1000_tune1_table[STFM1000_FREQUENCY_100KHZ_RANGE] = {\n");
+
+ for (i = start; i < end; i++) {
+
+ ndiv = (int)((i+14)/15) - 48;
+ incr = i - (int)(i/15)*15;
+ cicosr = (int)(i*2/3.0/16.0 + 0.5);
+ sdnominal = (int)(i*100.0e3/1.5/(double)cicosr/2.0/2.0*2.0*8.0*256.0/228.0e3*65536);
+
+ temp = 0x00000000; // clear
+ temp = temp | ((cicosr<<9) & STFM1000_TUNE1_CICOSR); // bits[14:9] 0x00007E00
+ temp = temp | ((ndiv<<4) & STFM1000_TUNE1_PLL_DIV); // bits[8:4] 0x000001F0
+ temp = temp | ((incr) & STFM1000_TUNE1_PLL_DIV); // bits[3:0] 0x0000000F
+
+ printf("\t[%d - STFM1000_FREQUENCY_100KHZ_MIN] = "
+ "{ .tune1 = 0x%08x, .sdnom = 0x%08x },\n",
+ i, temp, sdnominal);
+ }
+ printf("};\n");
+
+}
+
+int main(int argc, char *argv[])
+{
+ printf("#include \"stfm1000-regs.h\"\n\n");
+
+ generate_tune1();
+
+ return 0;
+}
diff --git a/drivers/media/radio/stfm1000/stfm1000-alsa.c b/drivers/media/radio/stfm1000/stfm1000-alsa.c
new file mode 100644
index 000000000000..d1da4475bc07
--- /dev/null
+++ b/drivers/media/radio/stfm1000/stfm1000-alsa.c
@@ -0,0 +1,660 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/math64.h>
+
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <mach/regs-dri.h>
+#include <mach/regs-apbx.h>
+#include <mach/regs-clkctrl.h>
+
+#include "stfm1000.h"
+
+#define STFM1000_PERIODS 16
+
+static int stfm1000_snd_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2; /* two channels */
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 20;
+ return 0;
+}
+
+static int stfm1000_snd_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct stfm1000 *stfm1000 = snd_kcontrol_chip(kcontrol);
+
+ (void)stfm1000;
+ ucontrol->value.integer.value[0] = 0; /* left */
+ ucontrol->value.integer.value[1] = 0; /* right */
+ return 0;
+}
+
+static int stfm1000_snd_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct stfm1000 *stfm1000 = snd_kcontrol_chip(kcontrol);
+ int change;
+ int left, right;
+
+ (void)stfm1000;
+
+ left = ucontrol->value.integer.value[0];
+ if (left < 0)
+ left = 0;
+ if (left > 20)
+ left = 20;
+ right = ucontrol->value.integer.value[1];
+ if (right < 0)
+ right = 0;
+ if (right > 20)
+ right = 20;
+
+ change = 1;
+ return change;
+}
+
+static struct snd_kcontrol_new stfm1000_snd_controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Radio Volume",
+ .index = 0,
+ .info = stfm1000_snd_volume_info,
+ .get = stfm1000_snd_volume_get,
+ .put = stfm1000_snd_volume_put,
+ .private_value = 0,
+ },
+};
+
+static struct snd_pcm_hardware stfm1000_snd_capture = {
+
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 44100,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = SZ_256K,
+ .period_bytes_min = SZ_4K,
+ .period_bytes_max = SZ_4K,
+ .periods_min = STFM1000_PERIODS,
+ .periods_max = STFM1000_PERIODS,
+};
+
+static int stfm1000_snd_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stfm1000 *stfm1000 = snd_pcm_substream_chip(substream);
+ int err;
+
+ /* should never happen, just a sanity check */
+ BUG_ON(stfm1000 == NULL);
+
+ mutex_lock(&stfm1000->deffered_work_lock);
+ stfm1000->read_count = 0;
+ stfm1000->read_offset = 0;
+
+ stfm1000->substream = substream;
+ runtime->private_data = stfm1000;
+ runtime->hw = stfm1000_snd_capture;
+
+ mutex_unlock(&stfm1000->deffered_work_lock);
+
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0) {
+ printk(KERN_ERR "%s: snd_pcm_hw_constraint_integer "
+ "SNDRV_PCM_HW_PARAM_PERIODS failed\n", __func__);
+ return err;
+ }
+
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS, 2);
+ if (err < 0) {
+ printk(KERN_ERR "%s: snd_pcm_hw_constraint_integer "
+ "SNDRV_PCM_HW_PARAM_PERIODS failed\n", __func__);
+ return err;
+ }
+
+ return 0;
+}
+
+static int stfm1000_snd_capture_close(struct snd_pcm_substream *substream)
+{
+ struct stfm1000 *stfm1000 = snd_pcm_substream_chip(substream);
+
+ (void)stfm1000; /* nothing */
+ return 0;
+}
+
+static int stfm1000_snd_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct stfm1000 *stfm1000 = snd_pcm_substream_chip(substream);
+ unsigned int period_size, periods;
+ int ret;
+
+ periods = params_periods(hw_params);
+ period_size = params_period_bytes(hw_params);
+
+ if (period_size < 0x100 || period_size > 0x10000)
+ return -EINVAL;
+ if (periods < STFM1000_PERIODS)
+ return -EINVAL;
+ if (period_size * periods > 1024 * 1024)
+ return -EINVAL;
+
+ stfm1000->blocks = periods;
+ stfm1000->blksize = period_size;
+ stfm1000->bufsize = params_buffer_bytes(hw_params);
+
+ ret = snd_pcm_lib_malloc_pages(substream, stfm1000->bufsize);
+ if (ret < 0) { /* 0 & 1 are valid returns */
+ printk(KERN_ERR "%s: snd_pcm_lib_malloc_pages() failed\n",
+ __func__);
+ return ret;
+ }
+
+ /* the dri buffer is twice as large as the audio buffer */
+ stfm1000->dri_bufsz = (stfm1000->bufsize / 4) *
+ sizeof(struct stfm1000_dri_sample);
+ stfm1000->dri_buf = dma_alloc_coherent(&stfm1000->radio.dev,
+ stfm1000->dri_bufsz, &stfm1000->dri_phys, GFP_KERNEL);
+ if (stfm1000->dri_buf == NULL) {
+ printk(KERN_ERR "%s: dma_alloc_coherent() failed\n", __func__);
+ snd_pcm_lib_free_pages(substream);
+ return -ENOMEM;
+ }
+
+ return ret;
+}
+
+static int stfm1000_snd_hw_free(struct snd_pcm_substream *substream)
+{
+ struct stfm1000 *stfm1000 = snd_pcm_substream_chip(substream);
+
+ if (stfm1000->dri_buf) {
+ dma_free_coherent(&stfm1000->radio.dev,
+ (stfm1000->bufsize / 4) *
+ sizeof(struct stfm1000_dri_sample),
+ stfm1000->dri_buf, stfm1000->dri_phys);
+ stfm1000->dri_buf = NULL;
+ stfm1000->dri_phys = 0;
+ }
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+
+static int stfm1000_snd_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stfm1000 *stfm1000 = snd_pcm_substream_chip(substream);
+
+ stfm1000->substream = substream;
+
+ if (snd_pcm_format_width(runtime->format) != 16 ||
+ !snd_pcm_format_signed(runtime->format) ||
+ snd_pcm_format_big_endian(runtime->format)) {
+ printk(KERN_INFO "STFM1000: ALSA capture_prepare illegal format\n");
+ return -EINVAL;
+ }
+
+ /* really shouldn't happen */
+ BUG_ON(stfm1000->blocks > stfm1000->desc_num);
+
+ mutex_lock(&stfm1000->deffered_work_lock);
+
+ if (stfm1000->now_recording != 0) {
+ printk(KERN_INFO "STFM1000: ALSA capture_prepare still running\n");
+ mutex_unlock(&stfm1000->deffered_work_lock);
+ return -EBUSY;
+ }
+ stfm1000->now_recording = 1;
+
+ mutex_unlock(&stfm1000->deffered_work_lock);
+
+ return 0;
+
+}
+
+static void stfm1000_snd_capture_trigger_start(struct work_struct *work)
+{
+ struct stfm1000 *stfm1000;
+
+ stfm1000 = container_of(work, struct stfm1000,
+ snd_capture_start_work.work);
+
+ mutex_lock(&stfm1000->deffered_work_lock);
+
+ BUG_ON(stfm1000->now_recording != 1);
+
+ stfm1000_bring_up(stfm1000);
+
+ mutex_unlock(&stfm1000->deffered_work_lock);
+}
+
+static void stfm1000_snd_capture_trigger_stop(struct work_struct *work)
+{
+ struct stfm1000 *stfm1000;
+
+ stfm1000 = container_of(work, struct stfm1000,
+ snd_capture_stop_work.work);
+
+ mutex_lock(&stfm1000->deffered_work_lock);
+
+ stfm1000->stopping_recording = 1;
+
+ stfm1000_take_down(stfm1000);
+
+ BUG_ON(stfm1000->now_recording != 1);
+ stfm1000->now_recording = 0;
+
+ stfm1000->stopping_recording = 0;
+
+ mutex_unlock(&stfm1000->deffered_work_lock);
+}
+
+static int execute_non_atomic(work_func_t fn, struct execute_work *ew)
+{
+ if (!in_atomic() && !in_interrupt()) {
+ fn(&ew->work);
+ return 0;
+ }
+
+ INIT_WORK(&ew->work, fn);
+ schedule_work(&ew->work);
+
+ return 1;
+}
+
+static int stfm1000_snd_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stfm1000 *stfm1000 = runtime->private_data;
+ int err = 0;
+
+ (void)stfm1000;
+
+ switch (cmd) {
+
+ case SNDRV_PCM_TRIGGER_START:
+ execute_non_atomic(stfm1000_snd_capture_trigger_start,
+ &stfm1000->snd_capture_start_work);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ execute_non_atomic(stfm1000_snd_capture_trigger_stop,
+ &stfm1000->snd_capture_stop_work);
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ stmp3xxx_dma_unfreeze(stfm1000->dma_ch);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ stmp3xxx_dma_freeze(stfm1000->dma_ch);
+ break;
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static snd_pcm_uframes_t
+stfm1000_snd_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stfm1000 *stfm1000 = runtime->private_data;
+
+ if (stfm1000->read_count) {
+ stfm1000->read_count -= snd_pcm_lib_period_bytes(substream);
+ stfm1000->read_offset += snd_pcm_lib_period_bytes(substream);
+ if (stfm1000->read_offset == substream->runtime->dma_bytes)
+ stfm1000->read_offset = 0;
+ }
+
+ return bytes_to_frames(runtime, stfm1000->read_offset);
+}
+
+static struct snd_pcm_ops stfm1000_snd_capture_ops = {
+ .open = stfm1000_snd_capture_open,
+ .close = stfm1000_snd_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = stfm1000_snd_hw_params,
+ .hw_free = stfm1000_snd_hw_free,
+ .prepare = stfm1000_snd_capture_prepare,
+ .trigger = stfm1000_snd_capture_trigger,
+ .pointer = stfm1000_snd_capture_pointer,
+};
+
+static void stfm1000_snd_free(struct snd_card *card)
+{
+ struct stfm1000 *stfm1000 = card->private_data;
+
+ free_irq(IRQ_DRI_ATTENTION, stfm1000);
+ free_irq(IRQ_DRI_DMA, stfm1000);
+}
+
+static int stfm1000_alsa_instance_init(struct stfm1000 *stfm1000)
+{
+ int ret, i;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_kcontrol *ctl;
+
+ mutex_init(&stfm1000->deffered_work_lock);
+
+ /* request dma channel */
+ stfm1000->desc_num = STFM1000_PERIODS;
+ stfm1000->dma_ch = STMP3xxx_DMA(5, STMP3XXX_BUS_APBX);
+ ret = stmp3xxx_dma_request(stfm1000->dma_ch, &stfm1000->radio.dev,
+ "stmp3xxx dri");
+ if (ret != 0) {
+ printk(KERN_ERR "%s: stmp3xxx_dma_request failed\n", __func__);
+ goto err;
+ }
+
+ stfm1000->dma = kzalloc(sizeof(*stfm1000->dma) * stfm1000->desc_num,
+ GFP_KERNEL);
+ if (stfm1000->dma == NULL) {
+ printk(KERN_ERR "%s: stmp3xxx_dma_request failed\n", __func__);
+ ret = -ENOMEM;
+ goto err_rel_dma;
+ }
+
+ for (i = 0; i < stfm1000->desc_num; i++) {
+ ret = stmp3xxx_dma_allocate_command(stfm1000->dma_ch,
+ &stfm1000->dma[i]);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: stmp3xxx_dma_allocate_command "
+ "failed\n", __func__);
+ goto err_free_dma;
+ }
+ }
+
+ /* allocate ALSA card structure (we only need an extra pointer
+ * back to stfm1000) */
+ card = snd_card_new(-1, NULL, THIS_MODULE, 0);
+ if (card == NULL) {
+ ret = -ENOMEM;
+ printk(KERN_ERR "%s: snd_card_new failed\n", __func__);
+ goto err_free_dma;
+ }
+ stfm1000->card = card;
+ card->private_data = stfm1000; /* point back */
+
+ /* mixer controls */
+ strcpy(card->driver, "stfm1000");
+ card->private_free = stfm1000_snd_free;
+
+ strcpy(card->mixername, "stfm1000 mixer");
+ for (i = 0; i < ARRAY_SIZE(stfm1000_snd_controls); i++) {
+ ctl = snd_ctl_new1(&stfm1000_snd_controls[i], stfm1000);
+ if (ctl == NULL) {
+ printk(KERN_ERR "%s: snd_ctl_new1 failed\n", __func__);
+ goto err_free_controls;
+ }
+ ret = snd_ctl_add(card, ctl);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: snd_ctl_add failed\n", __func__);
+ goto err_free_controls;
+ }
+ }
+
+ /* PCM */
+ ret = snd_pcm_new(card, "STFM1000 PCM", 0, 0, 1, &pcm);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: snd_ctl_add failed\n", __func__);
+ goto err_free_controls;
+ }
+ stfm1000->pcm = pcm;
+ pcm->private_data = stfm1000; /* point back */
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &stfm1000_snd_capture_ops);
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "STFM1000 PCM");
+
+ snd_card_set_dev(card, &stfm1000->radio.dev);
+ strcpy(card->shortname, "STFM1000");
+
+ ret = snd_pcm_lib_preallocate_pages_for_all(stfm1000->pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS, card->dev, SZ_256K, SZ_256K);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: snd_pcm_lib_preallocate_pages_for_all "
+ "failed\n", __func__);
+ goto err_free_pcm;
+ }
+
+ ret = request_irq(IRQ_DRI_DMA, stfm1000_dri_dma_irq, 0, "stfm1000",
+ stfm1000);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: request_irq failed\n", __func__);
+ goto err_free_prealloc;
+ }
+
+ ret = request_irq(IRQ_DRI_ATTENTION, stfm1000_dri_attn_irq, 0,
+ "stfm1000", stfm1000);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: request_irq failed\n", __func__);
+ goto err_rel_irq;
+ }
+
+ ret = snd_card_register(stfm1000->card);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: snd_card_register failed\n", __func__);
+ goto err_rel_irq2;
+ }
+
+ /* Enable completion interrupt */
+ stmp3xxx_dma_clear_interrupt(stfm1000->dma_ch);
+ stmp3xxx_dma_enable_interrupt(stfm1000->dma_ch);
+
+ printk(KERN_INFO "%s/alsa: %s registered\n", "STFM1000",
+ card->longname);
+
+ return 0;
+
+err_rel_irq2:
+ free_irq(IRQ_DRI_ATTENTION, stfm1000);
+
+err_rel_irq:
+ free_irq(IRQ_DRI_DMA, stfm1000);
+
+err_free_prealloc:
+ snd_pcm_lib_preallocate_free_for_all(stfm1000->pcm);
+
+err_free_pcm:
+ /* XXX TODO */
+
+err_free_controls:
+ /* XXX TODO */
+
+/* err_free_card: */
+ snd_card_free(stfm1000->card);
+
+err_free_dma:
+ for (i = stfm1000->desc_num - 1; i >= 0; i--) {
+ if (stfm1000->dma[i].command != NULL)
+ stmp3xxx_dma_free_command(stfm1000->dma_ch,
+ &stfm1000->dma[i]);
+ }
+
+err_rel_dma:
+ stmp3xxx_dma_release(stfm1000->dma_ch);
+err:
+ return ret;
+}
+
+static void stfm1000_alsa_instance_release(struct stfm1000 *stfm1000)
+{
+ int i;
+
+ stmp3xxx_dma_clear_interrupt(stfm1000->dma_ch);
+ stmp3xxx_arch_dma_reset_channel(stfm1000->dma_ch);
+
+ snd_card_free(stfm1000->card);
+
+ for (i = stfm1000->desc_num - 1; i >= 0; i--)
+ stmp3xxx_dma_free_command(stfm1000->dma_ch, &stfm1000->dma[i]);
+
+ kfree(stfm1000->dma);
+
+ stmp3xxx_dma_release(stfm1000->dma_ch);
+}
+
+static void stfm1000_alsa_dma_irq(struct stfm1000 *stfm1000)
+{
+ struct snd_pcm_runtime *runtime;
+ int desc;
+ s16 *src, *dst;
+
+ if (stfm1000->stopping_recording)
+ return;
+
+ if (stfm1000->read_count >= stfm1000->blksize *
+ (stfm1000->blocks - 2)) {
+ printk(KERN_ERR "irq: overrun %d - Blocks in %d\n",
+ stfm1000->read_count, stfm1000->blocks);
+ return;
+ }
+
+ /* someone has brutally killed user-space */
+ if (stfm1000->substream == NULL ||
+ stfm1000->substream->runtime == NULL)
+ return;
+
+ BUG_ON(stfm1000->substream == NULL);
+ BUG_ON(stfm1000->substream->runtime == NULL);
+
+ desc = stfm1000->read_offset / stfm1000->blksize;
+ runtime = stfm1000->substream->runtime;
+
+ if (runtime->dma_area == NULL)
+ printk(KERN_INFO "runtime->dma_area = NULL\n");
+ BUG_ON(runtime->dma_area == NULL);
+ if (stfm1000->dri_buf == NULL)
+ printk(KERN_INFO "stfm1000->dri_buf = NULL\n");
+ BUG_ON(stfm1000->dri_buf == NULL);
+
+ if (desc >= stfm1000->blocks) {
+ printk(KERN_INFO "desc=%d ->blocks=%d\n",
+ desc, stfm1000->blocks);
+ printk(KERN_INFO "->read_offset=%x ->blksize=%x\n",
+ stfm1000->read_offset, stfm1000->blksize);
+ }
+ BUG_ON(desc >= stfm1000->blocks);
+
+ src = stfm1000->dri_buf + desc * (stfm1000->blksize * 2);
+ dst = (void *)runtime->dma_area + desc * stfm1000->blksize;
+
+ /* perform filtering */
+ stfm1000_decode_block(stfm1000, src, dst, stfm1000->blksize / 4);
+
+ stfm1000->read_count += stfm1000->blksize;
+
+ if (stfm1000->read_count >=
+ snd_pcm_lib_period_bytes(stfm1000->substream))
+ snd_pcm_period_elapsed(stfm1000->substream);
+}
+
+static void stfm1000_alsa_attn_irq(struct stfm1000 *stfm1000)
+{
+ /* nothing */
+}
+
+struct stfm1000_alsa_ops stfm1000_default_alsa_ops = {
+ .init = stfm1000_alsa_instance_init,
+ .release = stfm1000_alsa_instance_release,
+ .dma_irq = stfm1000_alsa_dma_irq,
+ .attn_irq = stfm1000_alsa_attn_irq,
+};
+
+static int stfm1000_alsa_init(void)
+{
+ struct stfm1000 *stfm1000 = NULL;
+ struct list_head *list;
+ int ret;
+
+ stfm1000_alsa_ops = &stfm1000_default_alsa_ops;
+
+ list_for_each(list, &stfm1000_devlist) {
+ stfm1000 = list_entry(list, struct stfm1000, devlist);
+ ret = (*stfm1000_alsa_ops->init)(stfm1000);
+ if (ret != 0) {
+ printk(KERN_ERR "stfm1000 ALSA driver for DMA sound "
+ "failed init.\n");
+ return ret;
+ }
+ stfm1000->alsa_initialized = 1;
+ }
+
+ printk(KERN_INFO "stfm1000 ALSA driver for DMA sound loaded\n");
+
+ return 0;
+}
+
+static void stfm1000_alsa_exit(void)
+{
+ struct stfm1000 *stfm1000 = NULL;
+ struct list_head *list;
+
+ list_for_each(list, &stfm1000_devlist) {
+ stfm1000 = list_entry(list, struct stfm1000, devlist);
+
+ if (!stfm1000->alsa_initialized)
+ continue;
+
+ stfm1000_take_down(stfm1000);
+ (*stfm1000_alsa_ops->release)(stfm1000);
+ stfm1000->alsa_initialized = 0;
+ }
+
+ printk(KERN_INFO "stfm1000 ALSA driver for DMA sound unloaded\n");
+}
+
+/* We initialize this late, to make sure the sound system is up and running */
+late_initcall(stfm1000_alsa_init);
+module_exit(stfm1000_alsa_exit);
+
+MODULE_AUTHOR("Pantelis Antoniou");
+MODULE_DESCRIPTION("An ALSA PCM driver for the STFM1000 chip.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/radio/stfm1000/stfm1000-core.c b/drivers/media/radio/stfm1000/stfm1000-core.c
new file mode 100644
index 000000000000..5086100bb480
--- /dev/null
+++ b/drivers/media/radio/stfm1000/stfm1000-core.c
@@ -0,0 +1,2459 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/math64.h>
+
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/device.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <mach/regs-dri.h>
+#include <mach/regs-apbx.h>
+#include <mach/regs-clkctrl.h>
+
+#include "stfm1000.h"
+
+static DEFINE_MUTEX(devlist_lock);
+static unsigned int stfm1000_devcount;
+
+LIST_HEAD(stfm1000_devlist);
+EXPORT_SYMBOL(stfm1000_devlist);
+
+/* alsa interface */
+struct stfm1000_alsa_ops *stfm1000_alsa_ops;
+EXPORT_SYMBOL(stfm1000_alsa_ops);
+
+/* region, 0=US, 1=europe */
+static int georegion = 1; /* default is europe */
+static int rds_enable = 1; /* default is enabled */
+
+static int sw_tune(struct stfm1000 *stfm1000, u32 freq);
+
+static const const char *stfm1000_get_rev_txt(u32 id)
+{
+ switch (id) {
+ case 0x01: return "TA1";
+ case 0x02: return "TA2";
+ case 0x11: return "TB1";
+ case 0x12: return "TB2";
+ }
+ return NULL;
+}
+
+static const struct stfm1000_reg stfm1000_tb2_powerup[] = {
+ STFM1000_REG(REF, 0x00200000),
+ STFM1000_DELAY(20),
+ STFM1000_REG(DATAPATH, 0x00010210),
+ STFM1000_REG(TUNE1, 0x0004CF01),
+ STFM1000_REG(SDNOMINAL, 0x1C5EBCF0),
+ STFM1000_REG(PILOTTRACKING, 0x000001B6),
+ STFM1000_REG(INITIALIZATION1, 0x9fb80008),
+ STFM1000_REG(INITIALIZATION2, 0x8516e444 | STFM1000_DEEMPH_50_75B),
+ STFM1000_REG(INITIALIZATION3, 0x1402190b),
+ STFM1000_REG(INITIALIZATION4, 0x525bf052),
+ STFM1000_REG(INITIALIZATION5, 0x1000d106),
+ STFM1000_REG(INITIALIZATION6, 0x000062cb),
+ STFM1000_REG(AGC_CONTROL1, 0x1BCB2202),
+ STFM1000_REG(AGC_CONTROL2, 0x000020F0),
+ STFM1000_REG(CLK1, 0x10000000),
+ STFM1000_REG(CLK1, 0x20000000),
+ STFM1000_REG(CLK1, 0x00000000),
+ STFM1000_REG(CLK2, 0x7f000000),
+ STFM1000_REG(REF, 0x00B8222D),
+ STFM1000_REG(CLK1, 0x30000000),
+ STFM1000_REG(CLK1, 0x30002000),
+ STFM1000_REG(CLK1, 0x10002000),
+ STFM1000_REG(LNA, 0x0D080009),
+ STFM1000_DELAY(10),
+ STFM1000_REG(MIXFILT, 0x00008000),
+ STFM1000_REG(MIXFILT, 0x00000000),
+ STFM1000_REG(MIXFILT, 0x00007205),
+ STFM1000_REG(ADC, 0x001B3282),
+ STFM1000_REG(ATTENTION, 0x0000003F),
+ STFM1000_END,
+};
+
+static const struct stfm1000_reg stfm1000_ta2_powerup[] = {
+ STFM1000_REG(REF, 0x00200000),
+ STFM1000_DELAY(20),
+ STFM1000_REG(DATAPATH, 0x00010210),
+ STFM1000_REG(TUNE1, 0x00044F01),
+ STFM1000_REG(SDNOMINAL, 0x1C5EBCF0),
+ STFM1000_REG(PILOTTRACKING, 0x000001B6),
+ STFM1000_REG(INITIALIZATION1, 0x9fb80008),
+ STFM1000_REG(INITIALIZATION2, 0x8506e444),
+ STFM1000_REG(INITIALIZATION3, 0x1402190b),
+ STFM1000_REG(INITIALIZATION4, 0x525bf052),
+ STFM1000_REG(INITIALIZATION5, 0x7000d106),
+ STFM1000_REG(INITIALIZATION6, 0x0000c2cb),
+ STFM1000_REG(AGC_CONTROL1, 0x002c8402),
+ STFM1000_REG(AGC_CONTROL2, 0x00140050),
+ STFM1000_REG(CLK1, 0x10000000),
+ STFM1000_REG(CLK1, 0x20000000),
+ STFM1000_REG(CLK1, 0x00000000),
+ STFM1000_REG(CLK2, 0x7f000000),
+ STFM1000_REG(REF, 0x0030222D),
+ STFM1000_REG(CLK1, 0x30000000),
+ STFM1000_REG(CLK1, 0x30002000),
+ STFM1000_REG(CLK1, 0x10002000),
+ STFM1000_REG(LNA, 0x05080009),
+ STFM1000_REG(MIXFILT, 0x00008000),
+ STFM1000_REG(MIXFILT, 0x00000000),
+ STFM1000_REG(MIXFILT, 0x00007200),
+ STFM1000_REG(ADC, 0x00033000),
+ STFM1000_REG(ATTENTION, 0x0000003F),
+ STFM1000_END,
+};
+
+static const struct stfm1000_reg stfm1000_powerdown[] = {
+ STFM1000_REG(DATAPATH, 0x00010210),
+ STFM1000_REG(REF, 0),
+ STFM1000_REG(LNA, 0),
+ STFM1000_REG(MIXFILT, 0),
+ STFM1000_REG(CLK1, 0x20000000),
+ STFM1000_REG(CLK1, 0),
+ STFM1000_REG(CLK2, 0),
+ STFM1000_REG(ADC, 0),
+ STFM1000_REG(TUNE1, 0),
+ STFM1000_REG(SDNOMINAL, 0),
+ STFM1000_REG(PILOTTRACKING, 0),
+ STFM1000_REG(INITIALIZATION1, 0),
+ STFM1000_REG(INITIALIZATION2, 0),
+ STFM1000_REG(INITIALIZATION3, 0),
+ STFM1000_REG(INITIALIZATION4, 0),
+ STFM1000_REG(INITIALIZATION5, 0),
+ STFM1000_REG(INITIALIZATION6, 0x00007E00),
+ STFM1000_REG(AGC_CONTROL1, 0),
+ STFM1000_REG(AGC_CONTROL2, 0),
+ STFM1000_REG(DATAPATH, 0x00000200),
+};
+
+struct stfm1000_tuner_pmi {
+ u32 min;
+ u32 max;
+ u32 freq;
+ u32 pll_xtal; /* 1 = pll, 0 = xtal */
+};
+
+#define PLL 1
+#define XTAL 0
+
+static const struct stfm1000_tuner_pmi stfm1000_pmi_lookup[] = {
+ { .min = 76100, .max = 76500, .freq = 19200, .pll_xtal = PLL },
+ { .min = 79700, .max = 79900, .freq = 19200, .pll_xtal = PLL },
+ { .min = 80800, .max = 81200, .freq = 19200, .pll_xtal = PLL },
+ { .min = 82100, .max = 82600, .freq = 19200, .pll_xtal = PLL },
+ { .min = 86800, .max = 87200, .freq = 19200, .pll_xtal = PLL },
+ { .min = 88100, .max = 88600, .freq = 19200, .pll_xtal = PLL },
+ { .min = 89800, .max = 90500, .freq = 19200, .pll_xtal = PLL },
+ { .min = 91400, .max = 91900, .freq = 19200, .pll_xtal = PLL },
+ { .min = 92800, .max = 93300, .freq = 19200, .pll_xtal = PLL },
+ { .min = 97400, .max = 97900, .freq = 19200, .pll_xtal = PLL },
+ { .min = 98800, .max = 99200, .freq = 19200, .pll_xtal = PLL },
+ { .min = 100200, .max = 100400, .freq = 19200, .pll_xtal = PLL },
+ { .min = 103500, .max = 103900, .freq = 19200, .pll_xtal = PLL },
+ { .min = 104800, .max = 105200, .freq = 19200, .pll_xtal = PLL },
+ { .min = 106100, .max = 106500, .freq = 19200, .pll_xtal = PLL },
+
+ { .min = 76600, .max = 77000, .freq = 20000, .pll_xtal = PLL },
+ { .min = 77800, .max = 78300, .freq = 20000, .pll_xtal = PLL },
+ { .min = 79200, .max = 79600, .freq = 20000, .pll_xtal = PLL },
+ { .min = 80600, .max = 80700, .freq = 20000, .pll_xtal = PLL },
+ { .min = 83900, .max = 84400, .freq = 20000, .pll_xtal = PLL },
+ { .min = 85300, .max = 85800, .freq = 20000, .pll_xtal = PLL },
+ { .min = 94200, .max = 94700, .freq = 20000, .pll_xtal = PLL },
+ { .min = 95600, .max = 96100, .freq = 20000, .pll_xtal = PLL },
+ { .min = 100500, .max = 100800, .freq = 20000, .pll_xtal = PLL },
+ { .min = 101800, .max = 102200, .freq = 20000, .pll_xtal = PLL },
+ { .min = 103100, .max = 103400, .freq = 20000, .pll_xtal = PLL },
+ { .min = 106600, .max = 106900, .freq = 20000, .pll_xtal = PLL },
+ { .min = 107800, .max = 108000, .freq = 20000, .pll_xtal = PLL },
+
+ { .min = 0, .max = 0, .freq = 24000, .pll_xtal = XTAL }
+};
+
+int stfm1000_power_up(struct stfm1000 *stfm1000)
+{
+ struct stfm1000_reg *reg, *pwrup_reg;
+ const struct stfm1000_reg *orig_reg, *treg;
+ int ret, size;
+
+ mutex_lock(&stfm1000->state_lock);
+
+ /* Enable DRI clock for 24Mhz. */
+ HW_CLKCTRL_XTAL_CLR(BM_CLKCTRL_XTAL_DRI_CLK24M_GATE);
+
+ orig_reg = stfm1000->revid == STFM1000_CHIP_REV_TA2 ?
+ stfm1000_ta2_powerup : stfm1000_tb2_powerup;
+
+ /* find size of the set */
+ for (treg = orig_reg; treg->regno != STFM1000_REG_END; treg++)
+ ;
+ size = (treg + 1 - orig_reg) * sizeof(*treg);
+
+ /* allocate copy */
+ pwrup_reg = kmalloc(size, GFP_KERNEL);
+ if (pwrup_reg == NULL) {
+ printk(KERN_ERR "%s: out of memory\n", __func__);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* copy it */
+ memcpy(pwrup_reg, orig_reg, size);
+
+ /* fixup region of INITILIZATION2 */
+ for (reg = pwrup_reg; reg->regno != STFM1000_REG_END; reg++) {
+
+ /* we only care for INITIALIZATION2 register */
+ if (reg->regno != STFM1000_INITIALIZATION2)
+ continue;
+
+ /* geographic region select */
+ if (stfm1000->georegion == 0) /* USA */
+ reg->value &= ~STFM1000_DEEMPH_50_75B;
+ else /* Europe */
+ reg->value |= STFM1000_DEEMPH_50_75B;
+
+ /* RDS enabled */
+ if (stfm1000->revid == STFM1000_CHIP_REV_TB2) {
+ if (stfm1000->rds_enable)
+ reg->value |= STFM1000_RDS_ENABLE;
+ else
+ reg->value &= ~STFM1000_RDS_ENABLE;
+ }
+ }
+
+ ret = stfm1000_write_regs(stfm1000, pwrup_reg);
+
+ kfree(pwrup_reg);
+out:
+ mutex_unlock(&stfm1000->state_lock);
+
+ return ret;
+}
+
+int stfm1000_power_down(struct stfm1000 *stfm1000)
+{
+ int ret;
+
+ mutex_lock(&stfm1000->state_lock);
+
+ /* Disable DRI clock for 24Mhz. */
+ HW_CLKCTRL_XTAL_CLR(BM_CLKCTRL_XTAL_DRI_CLK24M_GATE);
+
+ ret = stfm1000_write_regs(stfm1000, stfm1000_powerdown);
+
+ /* Disable DRI clock for 24Mhz. */
+ /* XXX bug warning, disabling the DRI clock is bad news */
+ /* doing so causes noise to be received from the DRI */
+ /* interface. Leave it on for now */
+ /* HW_CLKCTRL_XTAL_CLR(BM_CLKCTRL_XTAL_DRI_CLK24M_GATE); */
+
+ mutex_unlock(&stfm1000->state_lock);
+
+ return ret;
+}
+
+int stfm1000_dcdc_update(struct stfm1000 *stfm1000, u32 freq)
+{
+ const struct stfm1000_tuner_pmi *pmi;
+ int i;
+
+ /* search for DCDC frequency */
+ pmi = stfm1000_pmi_lookup;
+ for (i = 0; i < ARRAY_SIZE(stfm1000_pmi_lookup); i++, pmi++) {
+ if (freq >= pmi->min && freq <= pmi->max)
+ break;
+ }
+ if (i >= ARRAY_SIZE(stfm1000_pmi_lookup))
+ return -1;
+
+ /* adjust DCDC frequency so that it is out of Tuner PLL range */
+ /* XXX there is no adjustment API (os_pmi_SetDcdcFreq)*/
+ return 0;
+}
+
+static void Mute_Audio(struct stfm1000 *stfm1000)
+{
+ stfm1000->mute = 1;
+}
+
+static void Unmute_Audio(struct stfm1000 *stfm1000)
+{
+ stfm1000->mute = 0;
+}
+
+static const struct stfm1000_reg sd_dp_on_regs[] = {
+ STFM1000_REG_SETBITS(DATAPATH, STFM1000_DP_EN),
+ STFM1000_DELAY(3),
+ STFM1000_REG_SETBITS(DATAPATH, STFM1000_DB_ACCEPT),
+ STFM1000_REG_CLRBITS(AGC_CONTROL1, STFM1000_B2_BYPASS_AGC_CTL),
+ STFM1000_REG_CLRBITS(DATAPATH, STFM1000_DB_ACCEPT),
+ STFM1000_END,
+};
+
+static int SD_DP_On(struct stfm1000 *stfm1000)
+{
+ int ret;
+
+ ret = stfm1000_write_regs(stfm1000, sd_dp_on_regs);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct stfm1000_reg sd_dp_off_regs[] = {
+ STFM1000_REG_SETBITS(DATAPATH, STFM1000_DB_ACCEPT),
+ STFM1000_REG_CLRBITS(DATAPATH, STFM1000_DP_EN),
+ STFM1000_REG_SETBITS(AGC_CONTROL1, STFM1000_B2_BYPASS_AGC_CTL),
+ STFM1000_REG_CLRBITS(PILOTTRACKING, STFM1000_B2_PILOTTRACKING_EN),
+ STFM1000_REG_CLRBITS(DATAPATH, STFM1000_DB_ACCEPT),
+ STFM1000_END,
+};
+
+static int SD_DP_Off(struct stfm1000 *stfm1000)
+{
+ int ret;
+
+ ret = stfm1000_write_regs(stfm1000, sd_dp_off_regs);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static int DRI_Start_Stream(struct stfm1000 *stfm1000)
+{
+ dma_addr_t dma_buffer_phys;
+ int i, next;
+ u32 cmd;
+
+ /* we must not be gated */
+ BUG_ON(HW_CLKCTRL_XTAL_RD() & BM_CLKCTRL_XTAL_DRI_CLK24M_GATE);
+
+ /* hw_dri_SetReset */
+ HW_DRI_CTRL_CLR(BM_DRI_CTRL_SFTRST | BM_DRI_CTRL_CLKGATE);
+ HW_DRI_CTRL_SET(BM_DRI_CTRL_SFTRST);
+ while ((HW_DRI_CTRL_RD() & BM_DRI_CTRL_CLKGATE) == 0)
+ cpu_relax();
+ HW_DRI_CTRL_CLR(BM_DRI_CTRL_SFTRST | BM_DRI_CTRL_CLKGATE);
+
+ /* DRI enable/config */
+ HW_DRI_TIMING_WR(BF_DRI_TIMING_GAP_DETECTION_INTERVAL(0x10) |
+ BF_DRI_TIMING_PILOT_REP_RATE(0x08));
+
+ /* XXX SDK bug */
+ /* While the SDK enables the gate here, everytime the stream */
+ /* is started, doing so, causes the DRI to input audio noise */
+ /* at any subsequent starts */
+ /* Enable DRI clock for 24Mhz. */
+ /* HW_CLKCTRL_XTAL_CLR(BM_CLKCTRL_XTAL_DRI_CLK24M_GATE); */
+
+ stmp3xxx_arch_dma_reset_channel(stfm1000->dma_ch);
+
+ dma_buffer_phys = stfm1000->dri_phys;
+
+ for (i = 0; i < stfm1000->blocks; i++) {
+ next = (i + 1) % stfm1000->blocks;
+
+ /* link */
+ stfm1000->dma[i].command->next = stfm1000->dma[next].handle;
+ stfm1000->dma[i].next_descr = &stfm1000->dma[next];
+
+ /* receive DRI is 8 bytes per 4 samples */
+ cmd = BF_APBX_CHn_CMD_XFER_COUNT(stfm1000->blksize * 2) |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BM_APBX_CHn_CMD_CHAIN |
+ BF_APBX_CHn_CMD_COMMAND(
+ BV_APBX_CHn_CMD_COMMAND__DMA_WRITE);
+
+ stfm1000->dma[i].command->cmd = cmd;
+ stfm1000->dma[i].command->buf_ptr = dma_buffer_phys;
+ stfm1000->dma[i].command->pio_words[0] =
+ BM_DRI_CTRL_OVERFLOW_IRQ_EN |
+ BM_DRI_CTRL_PILOT_SYNC_LOSS_IRQ_EN |
+ BM_DRI_CTRL_ATTENTION_IRQ_EN |
+ /* BM_DRI_CTRL_STOP_ON_OFLOW_ERROR | */
+ /* BM_DRI_CTRL_STOP_ON_PILOT_ERROR | */
+ BM_DRI_CTRL_ENABLE_INPUTS;
+
+ dma_buffer_phys += stfm1000->blksize * 2;
+
+ }
+
+ /* Enable completion interrupt */
+ stmp3xxx_dma_clear_interrupt(stfm1000->dma_ch);
+ stmp3xxx_dma_enable_interrupt(stfm1000->dma_ch);
+
+ /* clear DRI interrupts pending */
+ HW_DRI_CTRL_CLR(BM_DRI_CTRL_OVERFLOW_IRQ |
+ BM_DRI_CTRL_PILOT_SYNC_LOSS_IRQ |
+ BM_DRI_CTRL_ATTENTION_IRQ);
+
+ /* Stop DRI on error */
+ HW_DRI_CTRL_CLR(BM_DRI_CTRL_STOP_ON_OFLOW_ERROR |
+ BM_DRI_CTRL_STOP_ON_PILOT_ERROR);
+
+ /* Reacquire data stream */
+ HW_DRI_CTRL_SET(BM_DRI_CTRL_REACQUIRE_PHASE |
+ BM_DRI_CTRL_OVERFLOW_IRQ_EN |
+ BM_DRI_CTRL_PILOT_SYNC_LOSS_IRQ_EN |
+ BM_DRI_CTRL_ATTENTION_IRQ_EN |
+ BM_DRI_CTRL_ENABLE_INPUTS);
+
+ stmp3xxx_dma_go(stfm1000->dma_ch, stfm1000->dma, 1);
+
+ /* Turn on DRI hardware (don't forget to leave RUN bit ON) */
+ HW_DRI_CTRL_SET(BM_DRI_CTRL_RUN);
+
+ return 0;
+}
+
+static int DRI_Stop_Stream(struct stfm1000 *stfm1000)
+{
+ int desc;
+
+ /* disable interrupts */
+ HW_DRI_CTRL_CLR(BM_DRI_CTRL_OVERFLOW_IRQ_EN |
+ BM_DRI_CTRL_PILOT_SYNC_LOSS_IRQ_EN |
+ BM_DRI_CTRL_ATTENTION_IRQ_EN);
+
+ /* Freeze DMA channel for a moment */
+ stmp3xxx_dma_freeze(stfm1000->dma_ch);
+
+ /* all descriptors, set sema bit */
+ for (desc = 0; desc < stfm1000->blocks; desc++)
+ stfm1000->dma[desc].command->cmd |= BM_APBX_CHn_CMD_SEMAPHORE;
+
+ /* Let the current DMA transaction finish */
+ stmp3xxx_dma_unfreeze(stfm1000->dma_ch);
+ msleep(5);
+
+ /* dma shutdown */
+ stmp3xxx_arch_dma_reset_channel(stfm1000->dma_ch);
+
+ /* Turn OFF data lines and stop controller */
+ HW_DRI_CTRL_CLR(BM_DRI_CTRL_ENABLE_INPUTS | BM_DRI_CTRL_RUN);
+
+ /* hw_dri_SetReset */
+ HW_DRI_CTRL_SET(BM_DRI_CTRL_SFTRST | BM_DRI_CTRL_CLKGATE);
+
+ /* XXX SDK bug */
+ /* While the SDK enables the gate here, everytime the stream */
+ /* is started, doing so, causes the DRI to input audio noise */
+ /* at any subsequent starts */
+ /* Enable DRI clock for 24Mhz. */
+ /* Disable DRI clock for 24Mhz. */
+ /* HW_CLKCTRL_XTAL_SET(BM_CLKCTRL_XTAL_DRI_CLK24M_GATE); */
+
+ return 0;
+}
+
+static int DRI_On(struct stfm1000 *stfm1000)
+{
+ int ret;
+
+ if (stfm1000->active)
+ DRI_Start_Stream(stfm1000);
+
+ ret = stfm1000_set_bits(stfm1000, STFM1000_DATAPATH,
+ STFM1000_SAI_EN);
+ return ret;
+}
+
+static int DRI_Off(struct stfm1000 *stfm1000)
+{
+ int ret;
+
+ if (stfm1000->active)
+ DRI_Stop_Stream(stfm1000);
+
+ ret = stfm1000_clear_bits(stfm1000, STFM1000_DATAPATH,
+ STFM1000_SAI_EN);
+
+ return 0;
+}
+
+static int SD_Set_Channel_Filter(struct stfm1000 *stfm1000)
+{
+ int bypass_setting;
+ int sig_qual;
+ u32 tmp;
+ int ret;
+
+ /*
+ * set channel filter
+ *
+ * B2_NEAR_CHAN_MIX_REG_MASK values from T-Spec
+ * 000 : 0 kHz mix.
+ * 001 : +100 kHz mix.
+ * 010 : +200 kHz mix.
+ * 011 : +300 kHz mix.
+ * 100 : -400 kHz mix.
+ * 101 : -300 kHz mix.
+ * 110 : -200 kHz mix.
+ * 111 : -100 kHz mix.
+ */
+
+ /* get near channel amplitude */
+ ret = stfm1000_write_masked(stfm1000, STFM1000_INITIALIZATION3,
+ STFM1000_B2_NEAR_CHAN_MIX(0x01),
+ STFM1000_B2_NEAR_CHAN_MIX_MASK);
+ if (ret != 0)
+ return ret;
+
+ msleep(10); /* wait for the signal quality to settle */
+
+ ret = stfm1000_read(stfm1000, STFM1000_SIGNALQUALITY, &tmp);
+ if (ret != 0)
+ return ret;
+
+ sig_qual = (tmp & STFM1000_NEAR_CHAN_AMPLITUDE_MASK) >>
+ STFM1000_NEAR_CHAN_AMPLITUDE_SHIFT;
+
+ bypass_setting = 0;
+
+ /* check near channel amplitude vs threshold */
+ if (sig_qual < stfm1000->adj_chan_th) {
+ /* get near channel amplitude again */
+ ret = stfm1000_write_masked(stfm1000, STFM1000_INITIALIZATION3,
+ STFM1000_B2_NEAR_CHAN_MIX(0x05),
+ STFM1000_B2_NEAR_CHAN_MIX_MASK);
+ if (ret != 0)
+ return ret;
+
+ msleep(10); /* wait for the signal quality to settle */
+
+ ret = stfm1000_read(stfm1000, STFM1000_SIGNALQUALITY, &tmp);
+ if (ret != 0)
+ return ret;
+
+ sig_qual = (tmp & STFM1000_NEAR_CHAN_AMPLITUDE_MASK) >>
+ STFM1000_NEAR_CHAN_AMPLITUDE_SHIFT;
+
+ if (sig_qual < stfm1000->adj_chan_th)
+ bypass_setting = 2;
+ }
+
+ /* set filter settings */
+ ret = stfm1000_write_masked(stfm1000, STFM1000_INITIALIZATION1,
+ STFM1000_B2_BYPASS_FILT(bypass_setting),
+ STFM1000_B2_BYPASS_FILT_MASK);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static int SD_Look_For_Pilot_TA2(struct stfm1000 *stfm1000)
+{
+ int i;
+ u32 pilot;
+ int ret;
+
+ /* assume pilot */
+ stfm1000->pilot_present = 1;
+
+ for (i = 0; i < 3; i++) {
+
+ ret = stfm1000_read(stfm1000, STFM1000_PILOTCORRECTION,
+ &pilot);
+ if (ret != 0)
+ return ret;
+
+ pilot &= STFM1000_PILOTEST_TA2_MASK;
+ pilot >>= STFM1000_PILOTEST_TA2_SHIFT;
+
+ /* out of range? */
+ if (pilot < 0xe2 || pilot >= 0xb5) {
+ stfm1000->pilot_present = 0;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int SD_Look_For_Pilot_TB2(struct stfm1000 *stfm1000)
+{
+ int i;
+ u32 pilot;
+ int ret;
+
+ /* assume pilot */
+ stfm1000->pilot_present = 1;
+
+ for (i = 0; i < 3; i++) {
+
+ ret = stfm1000_read(stfm1000, STFM1000_PILOTCORRECTION,
+ &pilot);
+ if (ret != 0)
+ return ret;
+
+ pilot &= STFM1000_PILOTEST_TB2_MASK;
+ pilot >>= STFM1000_PILOTEST_TB2_SHIFT;
+
+ /* out of range? */
+ if (pilot < 0x1e || pilot >= 0x7f) {
+ stfm1000->pilot_present = 0;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int SD_Look_For_Pilot(struct stfm1000 *stfm1000)
+{
+ int ret;
+
+ if (stfm1000->revid == STFM1000_CHIP_REV_TA2)
+ ret = SD_Look_For_Pilot_TA2(stfm1000);
+ else
+ ret = SD_Look_For_Pilot_TB2(stfm1000);
+
+ if (ret != 0)
+ return ret;
+
+ if (!stfm1000->pilot_present) {
+ ret = stfm1000_clear_bits(stfm1000, STFM1000_PILOTTRACKING,
+ STFM1000_B2_PILOTTRACKING_EN);
+ if (ret != 0)
+ return ret;
+
+ /* set force mono parameters for the filter */
+ stfm1000->filter_parms.pCoefForcedMono = 1;
+
+ /* yeah, I know, it's stupid */
+ stfm1000->rds_state.demod.pCoefForcedMono =
+ stfm1000->filter_parms.pCoefForcedMono;
+ }
+
+ return 0;
+}
+
+static int SD_Gear_Shift_Pilot_Tracking(struct stfm1000 *stfm1000)
+{
+ static const struct {
+ int delay;
+ u32 value;
+ } track_table[] = {
+ { .delay = 10, .value = 0x81b6 },
+ { .delay = 6, .value = 0x82a5 },
+ { .delay = 6, .value = 0x8395 },
+ { .delay = 8, .value = 0x8474 },
+ { .delay = 20, .value = 0x8535 },
+ { .delay = 50, .value = 0x8632 },
+ { .delay = 0, .value = 0x8810 },
+ };
+ int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(track_table); i++) {
+ ret = stfm1000_write(stfm1000, STFM1000_PILOTTRACKING,
+ track_table[i].value);
+ if (ret != 0)
+ return ret;
+
+ if (i < ARRAY_SIZE(track_table) - 1) /* last one no delay */
+ msleep(track_table[i].delay);
+ }
+
+ return 0;
+}
+
+static int SD_Optimize_Channel(struct stfm1000 *stfm1000)
+{
+ int ret;
+
+ ret = stfm1000_set_bits(stfm1000, STFM1000_DATAPATH,
+ STFM1000_DB_ACCEPT);
+ if (ret != 0)
+ return ret;
+
+ ret = stfm1000_write(stfm1000, STFM1000_PILOTTRACKING,
+ STFM1000_B2_PILOTTRACKING_EN |
+ STFM1000_B2_PILOTLPF_TIMECONSTANT(0x01) |
+ STFM1000_B2_PFDSCALE(0x0B) |
+ STFM1000_B2_PFDFILTER_SPEEDUP(0x06)); /* 0x000081B6 */
+ if (ret != 0)
+ return ret;
+
+ ret = SD_Set_Channel_Filter(stfm1000);
+ if (ret != 0)
+ return ret;
+
+ ret = SD_Look_For_Pilot(stfm1000);
+ if (ret != 0)
+ return ret;
+
+ if (stfm1000->pilot_present) {
+ ret = SD_Gear_Shift_Pilot_Tracking(stfm1000);
+ if (ret != 0)
+ return ret;
+ }
+
+ ret = stfm1000_clear_bits(stfm1000, STFM1000_DATAPATH,
+ STFM1000_DB_ACCEPT);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static int Monitor_STFM_Quality(struct stfm1000 *stfm1000)
+{
+ u32 tmp, rssi_dc_est, tone_data;
+ u32 lna_rms, bias, agc_out, lna_th, lna, ref;
+ u16 rssi_mantissa, rssi_exponent, rssi_decoded;
+ u16 prssi;
+ s16 mpx_dc;
+ int rssi_log;
+ int bypass_filter;
+ int ret;
+
+ ret = stfm1000_set_bits(stfm1000, STFM1000_DATAPATH,
+ STFM1000_DB_ACCEPT);
+ if (ret != 0)
+ return ret;
+
+ /* Get Rssi register readings from STFM1000 */
+ stfm1000_read(stfm1000, STFM1000_RSSI_TONE, &tmp);
+ rssi_dc_est = tmp & 0xffff;
+ tone_data = (tmp >> 16) & 0x0fff;
+
+ rssi_mantissa = (rssi_dc_est & 0xffe0) >> 5; /* 11Msb */
+ rssi_exponent = rssi_dc_est & 0x001f; /* 5 lsb */
+ rssi_decoded = (u32)rssi_mantissa << rssi_exponent;
+
+ /* Convert Rsst to 10log(Rssi) */
+ for (prssi = 20; prssi > 0; prssi--)
+ if (rssi_decoded >= (1 << prssi))
+ break;
+
+ rssi_log = (3 * rssi_decoded >> prssi) + (3 * prssi - 3);
+ /* clamp to positive */
+ if (rssi_log < 0)
+ rssi_log = 0;
+ /* Compensate for errors in truncation/approximation by adding 1 */
+ rssi_log++;
+
+ stfm1000->rssi_dc_est_log = rssi_log;
+ stfm1000->signal_strength = stfm1000->rssi_dc_est_log;
+
+ /* determine absolute value */
+ if (tmp & 0x0800)
+ mpx_dc = ((tmp >> 16) & 0x0fff) | 0xf000;
+ else
+ mpx_dc = (tmp >> 16) & 0x0fff;
+ stfm1000->mpx_dc = mpx_dc;
+ mpx_dc = mpx_dc < 0 ? -mpx_dc : mpx_dc;
+
+ if (stfm1000->tuning_grid_50KHz)
+ stfm1000->is_station = rssi_log > stfm1000->tune_rssi_th;
+ else
+ stfm1000->is_station = rssi_log > stfm1000->tune_rssi_th &&
+ mpx_dc > stfm1000->tune_mpx_dc_th;
+
+ /* weak signal? */
+ if (stfm1000->rssi_dc_est_log <
+ (stfm1000->filter_parms.pCoefLmrGaTh - 20)) {
+
+ if (stfm1000->pilot_present)
+ bypass_filter = 1; /* Filter settings #2 */
+ else
+ bypass_filter = 0;
+
+ /* configure filter for narrow band */
+ ret = stfm1000_write_masked(stfm1000, STFM1000_AGC_CONTROL1,
+ STFM1000_B2_BYPASS_FILT(bypass_filter),
+ STFM1000_B2_BYPASS_FILT_MASK);
+ if (ret != 0)
+ return ret;
+
+ /* Turn off pilot tracking */
+ ret = stfm1000_clear_bits(stfm1000, STFM1000_PILOTTRACKING,
+ STFM1000_B2_PILOTTRACKING_EN);
+ if (ret != 0)
+ return ret;
+
+ /* enable "forced mono" in black box */
+ stfm1000->filter_parms.pCoefForcedMono = 1;
+
+ /* yeah, I know, it's stupid */
+ stfm1000->rds_state.demod.pCoefForcedMono =
+ stfm1000->filter_parms.pCoefForcedMono;
+
+ /* Set weak signal flag */
+ stfm1000->weak_signal = 1;
+
+ if (stfm1000->revid == STFM1000_CHIP_REV_TA2) {
+
+ /* read AGC_STAT register */
+ ret = stfm1000_read(stfm1000, STFM1000_AGC_STAT, &tmp);
+ if (ret != 0)
+ return ret;
+
+ lna_rms = (tmp & STFM1000_LNA_RMS_MASK) >>
+ STFM1000_LNA_RMS_SHIFT;
+
+ /* Check the energy level from LNA Power Meter A/D */
+ if (lna_rms == 0)
+ bias = STFM1000_IBIAS2_DN | STFM1000_IBIAS1_UP;
+ else
+ bias = STFM1000_IBIAS2_UP | STFM1000_IBIAS1_DN;
+
+ if (lna_rms == 0 || lna_rms > 2) {
+ ret = stfm1000_write_masked(stfm1000,
+ STFM1000_LNA, bias,
+ STFM1000_IBIAS2_UP |
+ STFM1000_IBIAS2_DN |
+ STFM1000_IBIAS1_UP |
+ STFM1000_IBIAS1_DN);
+ if (ret != 0)
+ return ret;
+ }
+
+ } else {
+
+ /* Set LNA bias */
+
+ /* read AGC_STAT register */
+ ret = stfm1000_read(stfm1000, STFM1000_AGC_STAT, &tmp);
+ if (ret != 0)
+ return ret;
+
+ agc_out = (tmp & STFM1000_AGCOUT_STAT_MASK) >>
+ STFM1000_AGCOUT_STAT_SHIFT;
+
+ /* read LNA register (this is a cached register) */
+ ret = stfm1000_read(stfm1000, STFM1000_LNA, &lna);
+ if (ret != 0)
+ return ret;
+
+ /* read REF register (this is a cached register) */
+ ret = stfm1000_read(stfm1000, STFM1000_REF, &ref);
+ if (ret != 0)
+ return ret;
+
+/* work around the 80 line width problem */
+#undef LNADEF
+#define LNADEF STFM1000_LNA_AMP1_IMPROVE_DISTORTION
+ if (agc_out == 31) {
+ if (rssi_log <= 16) {
+ if (lna & STFM1000_IBIAS1_DN)
+ lna &= ~STFM1000_IBIAS1_DN;
+ else {
+ lna |= STFM1000_IBIAS1_UP;
+ ref &= ~LNADEF;
+ }
+ }
+ if (rssi_log >= 26) {
+ if (lna & STFM1000_IBIAS1_UP) {
+ lna &= ~STFM1000_IBIAS1_UP;
+ ref |= LNADEF;
+ } else
+ lna |= STFM1000_IBIAS1_DN;
+ }
+ } else {
+ lna &= ~STFM1000_IBIAS1_UP;
+ lna |= STFM1000_IBIAS1_DN;
+ ref |= LNADEF;
+ }
+#undef LNADEF
+
+ ret = stfm1000_write_masked(stfm1000, STFM1000_LNA,
+ lna, STFM1000_IBIAS1_UP | STFM1000_IBIAS1_DN);
+ if (ret != 0)
+ return ret;
+
+ ret = stfm1000_write_masked(stfm1000, STFM1000_REF,
+ ref, STFM1000_LNA_AMP1_IMPROVE_DISTORTION);
+ if (ret != 0)
+ return ret;
+ }
+
+ } else if (stfm1000->rssi_dc_est_log >
+ (stfm1000->filter_parms.pCoefLmrGaTh - 17)) {
+
+ bias = STFM1000_IBIAS2_UP | STFM1000_IBIAS1_DN;
+
+ ret = stfm1000_write_masked(stfm1000, STFM1000_LNA,
+ bias, STFM1000_IBIAS2_UP | STFM1000_IBIAS2_DN |
+ STFM1000_IBIAS1_UP | STFM1000_IBIAS1_DN);
+ if (ret != 0)
+ return ret;
+
+ ret = SD_Set_Channel_Filter(stfm1000);
+ if (ret != 0)
+ return ret;
+
+ ret = SD_Look_For_Pilot(stfm1000);
+ if (ret != 0)
+ return ret;
+
+ if (stfm1000->pilot_present) {
+ if (stfm1000->prev_pilot_present ||
+ stfm1000->weak_signal) {
+
+ /* gear shift pilot tracking */
+ ret = SD_Gear_Shift_Pilot_Tracking(
+ stfm1000);
+ if (ret != 0)
+ return ret;
+
+ /* set force mono parameters for the
+ * filter */
+ stfm1000->filter_parms.
+ pCoefForcedMono = stfm1000->
+ force_mono;
+
+ /* yeah, I know, it's stupid */
+ stfm1000->rds_state.demod.
+ pCoefForcedMono = stfm1000->
+ filter_parms.
+ pCoefForcedMono;
+ }
+ } else {
+ ret = stfm1000_clear_bits(stfm1000,
+ STFM1000_PILOTTRACKING,
+ STFM1000_B2_PILOTTRACKING_EN);
+ if (ret != 0)
+ return ret;
+
+ /* set force mono parameters for the filter */
+ stfm1000->filter_parms.pCoefForcedMono = 1;
+
+ /* yeah, I know, it's stupid */
+ stfm1000->rds_state.demod.pCoefForcedMono =
+ stfm1000->filter_parms.pCoefForcedMono;
+ }
+
+ /* Reset weak signal flag */
+ stfm1000->weak_signal = 0;
+ stfm1000->prev_pilot_present = stfm1000->pilot_present;
+
+ } else {
+
+ ret = SD_Look_For_Pilot(stfm1000);
+ if (ret != 0)
+ return ret;
+
+ if (!stfm1000->pilot_present) {
+ ret = stfm1000_clear_bits(stfm1000,
+ STFM1000_PILOTTRACKING,
+ STFM1000_B2_PILOTTRACKING_EN);
+ if (ret != 0)
+ return ret;
+
+ /* set force mono parameters for the filter */
+ stfm1000->filter_parms.pCoefForcedMono = 1;
+
+ /* yeah, I know, it's stupid */
+ stfm1000->rds_state.demod.pCoefForcedMono =
+ stfm1000->filter_parms.pCoefForcedMono;
+
+ /* Reset weak signal flag */
+ stfm1000->weak_signal = 0;
+ stfm1000->prev_pilot_present = stfm1000->pilot_present;
+ }
+
+ }
+
+ if (stfm1000->revid == STFM1000_CHIP_REV_TA2) {
+
+ /* read AGC_STAT register */
+ ret = stfm1000_read(stfm1000, STFM1000_AGC_STAT, &tmp);
+ if (ret != 0)
+ return ret;
+
+ agc_out = (tmp & STFM1000_AGCOUT_STAT_MASK) >>
+ STFM1000_AGCOUT_STAT_SHIFT;
+ lna_rms = (tmp & STFM1000_LNA_RMS_MASK) >>
+ STFM1000_LNA_RMS_SHIFT;
+
+ ret = stfm1000_read(stfm1000, STFM1000_AGC_CONTROL1, &tmp);
+ if (ret != 0)
+ return ret;
+
+ /* extract LNATH */
+ lna_th = (tmp & STFM1000_B2_LNATH_MASK) >>
+ STFM1000_B2_LNATH_SHIFT;
+
+ if (lna_rms > lna_th && agc_out <= 1) {
+
+ ret = stfm1000_write_masked(stfm1000, STFM1000_LNA,
+ STFM1000_USEATTEN(1), STFM1000_USEATTEN_MASK);
+ if (ret != 0)
+ return ret;
+
+ } else if (agc_out > 15) {
+
+ ret = stfm1000_write_masked(stfm1000, STFM1000_LNA,
+ STFM1000_USEATTEN(0), STFM1000_USEATTEN_MASK);
+ if (ret != 0)
+ return ret;
+ }
+ }
+
+ /* disable buffered writes */
+ ret = stfm1000_clear_bits(stfm1000, STFM1000_DATAPATH,
+ STFM1000_DB_ACCEPT);
+ if (ret != 0)
+ return ret;
+
+ return ret;
+}
+
+static int Is_Station(struct stfm1000 *stfm1000)
+{
+ u32 tmp, rssi_dc_est, tone_data;
+ u16 rssi_mantissa, rssi_exponent, rssi_decoded;
+ u16 prssi;
+ s16 mpx_dc;
+ int rssi_log;
+
+ /* Get Rssi register readings from STFM1000 */
+ stfm1000_read(stfm1000, STFM1000_RSSI_TONE, &tmp);
+ rssi_dc_est = tmp & 0xffff;
+ tone_data = (tmp >> 16) & 0x0fff;
+
+ rssi_mantissa = (rssi_dc_est & 0xffe0) >> 5; /* 11Msb */
+ rssi_exponent = rssi_dc_est & 0x001f; /* 5 lsb */
+ rssi_decoded = (u32)rssi_mantissa << rssi_exponent;
+
+ /* Convert Rsst to 10log(Rssi) */
+ for (prssi = 20; prssi > 0; prssi--)
+ if (rssi_decoded >= (1 << prssi))
+ break;
+
+ rssi_log = (3 * rssi_decoded >> prssi) + (3 * prssi - 3);
+ /* clamp to positive */
+ if (rssi_log < 0)
+ rssi_log = 0;
+ /* Compensate for errors in truncation/approximation by adding 1 */
+ rssi_log++;
+
+ stfm1000->rssi_dc_est_log = rssi_log;
+ stfm1000->signal_strength = stfm1000->rssi_dc_est_log;
+
+ /* determine absolute value */
+ if (tmp & 0x0800)
+ mpx_dc = ((tmp >> 16) & 0x0fff) | 0xf000;
+ else
+ mpx_dc = (tmp >> 16) & 0x0fff;
+ stfm1000->mpx_dc = mpx_dc;
+ mpx_dc = mpx_dc < 0 ? -mpx_dc : mpx_dc;
+
+ if (stfm1000->tuning_grid_50KHz)
+ stfm1000->is_station = rssi_log > stfm1000->tune_rssi_th;
+ else
+ stfm1000->is_station = rssi_log > stfm1000->tune_rssi_th &&
+ mpx_dc > stfm1000->tune_mpx_dc_th;
+
+ return 0;
+}
+
+int Monitor_STFM_AGC(struct stfm1000 *stfm1000)
+{
+ /* we don't do any AGC for now */
+ return 0;
+}
+
+static int Take_Down(struct stfm1000 *stfm1000)
+{
+ Mute_Audio(stfm1000);
+
+ DRI_Off(stfm1000);
+
+ SD_DP_Off(stfm1000);
+
+ return 0;
+}
+
+static int Bring_Up(struct stfm1000 *stfm1000)
+{
+ SD_DP_On(stfm1000);
+
+ SD_Optimize_Channel(stfm1000);
+
+ DRI_On(stfm1000);
+
+ Unmute_Audio(stfm1000);
+
+ if (stfm1000->rds_enable)
+ stfm1000_rds_reset(&stfm1000->rds_state);
+
+ stfm1000->rds_sync = stfm1000->rds_enable; /* force sync (if RDS) */
+ stfm1000->rds_demod_running = 0;
+ stfm1000->rssi_dc_est_log = 0;
+ stfm1000->signal_strength = 0;
+
+ stfm1000->next_quality_monitor = jiffies + msecs_to_jiffies(
+ stfm1000->quality_monitor_period);
+ stfm1000->next_agc_monitor = jiffies + msecs_to_jiffies(
+ stfm1000->agc_monitor_period);
+ stfm1000->rds_pkt_bad = 0;
+ stfm1000->rds_pkt_good = 0;
+ stfm1000->rds_pkt_recovered = 0;
+ stfm1000->rds_pkt_lost_sync = 0;
+ stfm1000->rds_bit_overruns = 0;
+
+ return 0;
+}
+
+/* These are not used yet */
+
+static int Lock_Station(struct stfm1000 *stfm1000)
+{
+ int ret;
+
+ ret = SD_Optimize_Channel(stfm1000);
+ if (ret != 0)
+ return ret;
+
+ /* AGC monitor start? */
+
+ return ret;
+}
+
+static const struct stfm1000_reg sd_unlock_regs[] = {
+ STFM1000_REG_SETBITS(DATAPATH, STFM1000_DB_ACCEPT),
+ STFM1000_REG_CLRBITS(PILOTTRACKING, STFM1000_B2_PILOTTRACKING_EN),
+ STFM1000_REG_CLRBITS(DATAPATH, STFM1000_DB_ACCEPT),
+ STFM1000_END,
+};
+
+static int Unlock_Station(struct stfm1000 *stfm1000)
+{
+ int ret;
+
+ ret = stfm1000_write_regs(stfm1000, sd_unlock_regs);
+ return ret;
+}
+
+irqreturn_t stfm1000_dri_dma_irq(int irq, void *dev_id)
+{
+ struct stfm1000 *stfm1000 = dev_id;
+ u32 err_mask, irq_mask;
+ u32 ctrl;
+ int handled = 0;
+
+#ifdef CONFIG_ARCH_STMP37XX
+ err_mask = 1 << (16 + stfm1000->dma_ch);
+#endif
+#ifdef CONFIG_ARCH_STMP378X
+ err_mask = 1 << stfm1000->dma_ch;
+#endif
+ irq_mask = 1 << stfm1000->dma_ch;
+
+#ifdef CONFIG_ARCH_STMP37XX
+ ctrl = HW_APBX_CTRL1_RD();
+#endif
+#ifdef CONFIG_ARCH_STMP378X
+ ctrl = HW_APBX_CTRL2_RD();
+#endif
+
+ if (ctrl & err_mask) {
+ handled = 1;
+ printk(KERN_WARNING "%s: DMA audio channel %d error\n",
+ __func__, stfm1000->dma_ch);
+#ifdef CONFIG_ARCH_STMP37XX
+ HW_APBX_CTRL1_CLR(err_mask);
+#endif
+#ifdef CONFIG_ARCH_STMP378X
+ HW_APBX_CTRL2_CLR(err_mask);
+#endif
+ }
+
+ if (HW_APBX_CTRL1_RD() & irq_mask) {
+ handled = 1;
+ stmp3xxx_dma_clear_interrupt(stfm1000->dma_ch);
+
+ if (stfm1000->alsa_initialized) {
+ BUG_ON(stfm1000_alsa_ops->dma_irq == NULL);
+ (*stfm1000_alsa_ops->dma_irq)(stfm1000);
+ }
+ }
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+EXPORT_SYMBOL(stfm1000_dri_dma_irq);
+
+irqreturn_t stfm1000_dri_attn_irq(int irq, void *dev_id)
+{
+ struct stfm1000 *stfm1000 = dev_id;
+ int handled = 1;
+ u32 mask;
+
+ (void)stfm1000;
+ mask = HW_DRI_CTRL_RD();
+ mask &= BM_DRI_CTRL_OVERFLOW_IRQ | BM_DRI_CTRL_PILOT_SYNC_LOSS_IRQ |
+ BM_DRI_CTRL_ATTENTION_IRQ;
+
+ HW_DRI_CTRL_CLR(mask);
+
+ printk(KERN_INFO "DRI_ATTN:%s%s%s\n",
+ (mask & BM_DRI_CTRL_OVERFLOW_IRQ) ? " OV" : "",
+ (mask & BM_DRI_CTRL_PILOT_SYNC_LOSS_IRQ) ? " SL" : "",
+ (mask & BM_DRI_CTRL_ATTENTION_IRQ) ? " AT" : "");
+
+ if (stfm1000->alsa_initialized) {
+ BUG_ON(stfm1000_alsa_ops->attn_irq == NULL);
+ (*stfm1000_alsa_ops->attn_irq)(stfm1000);
+ }
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+EXPORT_SYMBOL(stfm1000_dri_attn_irq);
+
+void stfm1000_decode_block(struct stfm1000 *stfm1000, const s16 *src, s16 *dst,
+ int count)
+{
+ int i;
+
+ if (stfm1000->mute) {
+ memset(dst, 0, count * sizeof(s16) * 2);
+ return;
+
+ }
+
+ for (i = 0; i < count; i++, dst += 2, src += 4) {
+
+ stfm1000_filter_decode(&stfm1000->filter_parms,
+ src[0], src[1], src[2]);
+
+ dst[0] = stfm1000_filter_value_left(&stfm1000->filter_parms);
+ dst[1] = stfm1000_filter_value_right(&stfm1000->filter_parms);
+ }
+
+ stfm1000->rssi = stfm1000->filter_parms.RssiDecoded;
+ stfm1000->stereo = stfm1000->pilot_present &&
+ !stfm1000->filter_parms.pCoefForcedMono;
+
+ /* RDS processing */
+ if (stfm1000->rds_demod_running) {
+ /* rewind */
+ src -= count * 4;
+ stfm1000_rds_demod(&stfm1000->rds_state, src, count);
+ }
+
+}
+EXPORT_SYMBOL(stfm1000_decode_block);
+
+void stfm1000_take_down(struct stfm1000 *stfm1000)
+{
+ mutex_lock(&stfm1000->state_lock);
+ stfm1000->active = 0;
+ Take_Down(stfm1000);
+ mutex_unlock(&stfm1000->state_lock);
+}
+EXPORT_SYMBOL(stfm1000_take_down);
+
+void stfm1000_bring_up(struct stfm1000 *stfm1000)
+{
+ mutex_lock(&stfm1000->state_lock);
+
+ stfm1000->active = 1;
+
+ stfm1000_filter_reset(&stfm1000->filter_parms);
+
+ Bring_Up(stfm1000);
+
+ mutex_unlock(&stfm1000->state_lock);
+}
+EXPORT_SYMBOL(stfm1000_bring_up);
+
+void stfm1000_tune_current(struct stfm1000 *stfm1000)
+{
+ mutex_lock(&stfm1000->state_lock);
+ sw_tune(stfm1000, stfm1000->freq);
+ mutex_unlock(&stfm1000->state_lock);
+}
+EXPORT_SYMBOL(stfm1000_tune_current);
+
+/* Alternate ZIF Tunings to avoid EMI */
+const struct stfm1000_tune1
+stfm1000_board_emi_tuneups[STFM1000_FREQUENCY_100KHZ_RANGE] = {
+#undef TUNE_ENTRY
+#define TUNE_ENTRY(f, t1, sd) \
+ [(f) - STFM1000_FREQUENCY_100KHZ_MIN] = \
+ { .tune1 = (t1), .sdnom = (sd) }
+ TUNE_ENTRY(765, 0x84030, 0x1BF5E50D), /* 061215 Jon, IF +0kHz */
+ TUNE_ENTRY(780, 0x84240, 0x1BA5162F), /* 061215 Jon, IF +0kHz */
+ TUNE_ENTRY(795, 0x84250, 0x1C2D2F39), /* 061215 Jon, IF +0kHz */
+ TUNE_ENTRY(810, 0x84460, 0x1BDD207E), /* 061215 Jon, IF +0kHz */
+ TUNE_ENTRY(825, 0x84470, 0x1C6138CD), /* 061215 Jon, IF +0kHz */
+ TUNE_ENTRY(839, 0xC4680, 0x1C11F704), /* 061215 Jon, IF +100kHz */
+ TUNE_ENTRY(840, 0x84680, 0x1c11f704),
+ TUNE_ENTRY(855, 0x84890, 0x1BC71C71), /* 061215 Jon, IF +0kHz */
+ TUNE_ENTRY(870, 0x848A0, 0x1C43DE10), /* 061215 Jon, IF +0kHz */
+ TUNE_ENTRY(885, 0x84AB0, 0x1BF9B021), /* 061101 Arthur, IF +0kHz */
+ TUNE_ENTRY(899, 0xC4CC0, 0x1BB369A9), /* 061025 Arthur, IF +100kHz */
+ TUNE_ENTRY(900, 0x84CC0, 0x1BB369A9), /* 061025 Arthur, IF 0kHz */
+ TUNE_ENTRY(915, 0x84CD0, 0x1C299A5B), /* 061101 Arthur, IF +0kHz */
+ TUNE_ENTRY(930, 0x84ee0, 0x1be3e6aa), /* 061101 Arthur, IF +0kHz */
+ TUNE_ENTRY(945, 0x84ef0, 0x1c570f8b), /* 061101 Arthur, IF +0kHz */
+ TUNE_ENTRY(959, 0xC5100, 0x1c11f704),
+ TUNE_ENTRY(960, 0x85100, 0x1c11f704),
+ TUNE_ENTRY(975, 0x85310, 0x1bd03d57), /* 061101 Arthur, IF +0kHz */
+ TUNE_ENTRY(990, 0x85320, 0x1c3dc822), /* 061101 Arthur, IF +0kHz */
+ TUNE_ENTRY(1005, 0x85530, 0x1bfc93ff), /* 061101 Arthur, IF +0kHz */
+ TUNE_ENTRY(1019, 0xC5740, 0x1BBE683C), /* 061025 Arthur, IF +100kHz */
+ TUNE_ENTRY(1020, 0x85740, 0x1bbe683c), /* 061025 Arthur, IF +0kHz */
+ TUNE_ENTRY(1035, 0x85750, 0x1c26dab6), /* 061101 Arthur, IF +0kHz */
+ TUNE_ENTRY(1050, 0x85960, 0x1be922b4), /* 061101 Arthur, IF +0kHz */
+ TUNE_ENTRY(1065, 0x85970, 0x1c4f357c), /* 061101 Arthur, IF +0kHz */
+ TUNE_ENTRY(1079, 0xC5B80, 0x1c11f704),
+ TUNE_ENTRY(1080, 0x85B80, 0x1c11f704),
+#undef TUNE_ENTRY
+};
+
+static const struct stfm1000_tune1 *stfm1000_board_emi_tune(int freq100)
+{
+ const struct stfm1000_tune1 *tune1;
+
+ if ((unsigned int)(freq100 - STFM1000_FREQUENCY_100KHZ_MIN) >=
+ STFM1000_FREQUENCY_100KHZ_RANGE)
+ return NULL;
+
+ tune1 = &stfm1000_board_emi_tuneups[freq100 -
+ STFM1000_FREQUENCY_100KHZ_MIN];
+ if (tune1->tune1 == 0 && tune1->sdnom == 0)
+ return NULL;
+ return tune1;
+}
+
+/* freq in kHz */
+static int sw_tune(struct stfm1000 *stfm1000, u32 freq)
+{
+ u32 freq100 = freq / 100;
+ int tune_cap;
+ int i2s_clock;
+ int mix_reg;
+ int if_freq, fe_freq;
+ u32 tune1, sdnom, agc1;
+ const struct stfm1000_tune1 *tp;
+ int ret;
+
+ if_freq = 0;
+ mix_reg = 1;
+ switch (mix_reg) {
+ case 0: if_freq = -2; break;
+ case 1: if_freq = -1; break;
+ case 2: if_freq = 0; break;
+ case 3: if_freq = 1; break;
+ case 4: if_freq = 2; break;
+ }
+
+ /* handle board specific EMI tuning */
+ tp = stfm1000_board_emi_tune(freq100);
+ if (tp != NULL) {
+ tune1 = tp->tune1;
+ sdnom = tp->sdnom;
+ } else {
+ fe_freq = freq100 + if_freq;
+
+ /* clamp into range */
+ if (fe_freq < STFM1000_FREQUENCY_100KHZ_MIN)
+ fe_freq = STFM1000_FREQUENCY_100KHZ_MIN;
+ else if (fe_freq > STFM1000_FREQUENCY_100KHZ_MAX)
+ fe_freq = STFM1000_FREQUENCY_100KHZ_MAX;
+
+ tp = &stfm1000_tune1_table[fe_freq -
+ STFM1000_FREQUENCY_100KHZ_MIN];
+
+ /* bits [14:0], [20:18] */
+ tune1 = (tp->tune1 & 0x7fff) | (mix_reg << 18);
+ sdnom = tp->sdnom;
+ }
+
+ agc1 = stfm1000->revid == STFM1000_CHIP_REV_TA2 ? 0x0400 : 0x2200;
+
+ ret = stfm1000_write_masked(stfm1000, STFM1000_AGC_CONTROL1,
+ agc1, 0x3f00);
+ if (ret != 0)
+ goto err;
+
+ ret = stfm1000_write_masked(stfm1000, STFM1000_TUNE1, tune1,
+ 0xFFFF7FFF); /* do not set bit-15 */
+ if (ret != 0)
+ goto err;
+
+ /* keep this around */
+ stfm1000->sdnominal_pivot = sdnom;
+
+ ret = stfm1000_write(stfm1000, STFM1000_SDNOMINAL, sdnom);
+ if (ret != 0)
+ goto err;
+
+ /* fix for seek-not-stopping on alternate tunings */
+ ret = stfm1000_set_bits(stfm1000, STFM1000_DATAPATH,
+ STFM1000_DB_ACCEPT);
+ if (ret != 0)
+ goto err;
+
+ ret = stfm1000_clear_bits(stfm1000, STFM1000_DATAPATH,
+ STFM1000_DB_ACCEPT);
+ if (ret != 0)
+ goto err;
+
+ ret = stfm1000_set_bits(stfm1000, STFM1000_INITIALIZATION2,
+ STFM1000_DRI_CLK_EN);
+ if (ret != 0)
+ goto err;
+
+ /* 6MHz spur fix */
+ if ((freq100 >= 778 && freq100 <= 782) ||
+ (freq100 >= 838 && freq100 <= 842) ||
+ (freq100 >= 898 && freq100 <= 902) ||
+ (freq100 >= 958 && freq100 <= 962) ||
+ (freq100 >= 1018 && freq100 <= 1022) ||
+ (freq100 >= 1078 && freq100 <= 1080))
+ i2s_clock = 5; /* 4.8MHz */
+ else
+ i2s_clock = 4;
+
+ ret = stfm1000_write_masked(stfm1000, STFM1000_DATAPATH,
+ STFM1000_SAI_CLK_DIV(i2s_clock), STFM1000_SAI_CLK_DIV_MASK);
+ if (ret != 0)
+ goto err;
+
+ ret = stfm1000_set_bits(stfm1000, STFM1000_INITIALIZATION2,
+ STFM1000_DRI_CLK_EN);
+ if (ret != 0)
+ goto err;
+
+ if (tune1 & 0xf)
+ ret = stfm1000_set_bits(stfm1000, STFM1000_CLK1,
+ STFM1000_ENABLE_TAPDELAYFIX);
+ else
+ ret = stfm1000_clear_bits(stfm1000, STFM1000_CLK1,
+ STFM1000_ENABLE_TAPDELAYFIX);
+
+ if (ret != 0)
+ goto err;
+
+ tune_cap = (int)(stfm1000->tune_cap_a_f -
+ stfm1000->tune_cap_b_f * freq100);
+ if (tune_cap < 4)
+ tune_cap = 4;
+ ret = stfm1000_write_masked(stfm1000, STFM1000_LNA,
+ STFM1000_ANTENNA_TUNECAP(tune_cap),
+ STFM1000_ANTENNA_TUNECAP_MASK);
+ if (ret != 0)
+ goto err;
+
+ /* set signal strenth to 0 */
+ /* stfm1000_dcdc_update(); */
+
+ /* cmp_rds_setRdsStatus(0) */
+ /* cmp_rds_ResetGroupCallbacks(); */
+ stfm1000->freq = freq;
+
+ return 0;
+err:
+ return -1;
+}
+
+static const struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+};
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ strlcpy(v->driver, "radio-stfm1000", sizeof(v->driver));
+ strlcpy(v->card, "STFM1000 Radio", sizeof(v->card));
+ sprintf(v->bus_info, "i2c");
+ v->version = KERNEL_VERSION(0, 0, 1);
+ v->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+ u32 tmp, rssi_dc_est, tone_data;
+ u16 rssi_mantissa, rssi_exponent, rssi_decoded;
+ u16 prssi;
+ s16 mpx_dc;
+ int rssi_log;
+ int ret;
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ mutex_lock(&stfm1000->state_lock);
+
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = (u32)(87.5 * 16000);
+ v->rangehigh = (u32)(108 * 16000);
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ v->signal = 0; /* tr_getsigstr(); */
+
+ msleep(50);
+
+ ret = stfm1000_read(stfm1000, STFM1000_RSSI_TONE, &tmp);
+ if (ret != 0)
+ goto out;
+
+ rssi_dc_est = tmp & 0xffff;
+ tone_data = (tmp >> 16) & 0x0fff;
+
+ rssi_mantissa = (rssi_dc_est & 0xffe0) >> 5; /* 11Msb */
+ rssi_exponent = rssi_dc_est & 0x001f; /* 5 lsb */
+ rssi_decoded = (u32)rssi_mantissa << rssi_exponent;
+
+ /* Convert Rsst to 10log(Rssi) */
+ for (prssi = 20; prssi > 0; prssi--)
+ if (rssi_decoded >= (1 << prssi))
+ break;
+
+ rssi_log = (3 * rssi_decoded >> prssi) + (3 * prssi - 3);
+ /* clamp to positive */
+ if (rssi_log < 0)
+ rssi_log = 0;
+ /* Compensate for errors in truncation/approximation by adding 1 */
+ rssi_log++;
+
+ stfm1000->rssi_dc_est_log = rssi_log;
+ stfm1000->signal_strength = stfm1000->rssi_dc_est_log;
+
+ /* determine absolute value */
+ if (tmp & 0x0800)
+ mpx_dc = ((tmp >> 16) & 0x0fff) | 0xf000;
+ else
+ mpx_dc = (tmp >> 16) & 0x0fff;
+ stfm1000->mpx_dc = mpx_dc;
+ mpx_dc = mpx_dc < 0 ? -mpx_dc : mpx_dc;
+
+ v->signal = rssi_decoded & 0xffff;
+
+out:
+ mutex_unlock(&stfm1000->state_lock);
+
+ return ret;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+
+ (void)stfm1000;
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+
+ mutex_lock(&stfm1000->state_lock);
+
+ /* convert from the crazy linux value to our decimal based values */
+ stfm1000->freq = (u32)div_u64((u64)(125 * (u64)f->frequency), 2000);
+
+ if (stfm1000->active)
+ Take_Down(stfm1000);
+
+ sw_tune(stfm1000, stfm1000->freq);
+
+ if (stfm1000->active)
+ Bring_Up(stfm1000);
+
+ mutex_unlock(&stfm1000->state_lock);
+
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = stfm1000->freq * 16;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+ int i;
+
+ (void)stfm1000;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &radio_qctrl[i], sizeof(*qc));
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+
+ switch (ctrl->id) {
+
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = stfm1000->mute;
+ return 0;
+
+ }
+ return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+ int ret;
+
+ mutex_lock(&stfm1000->state_lock);
+
+ ret = -EINVAL;
+
+ switch (ctrl->id) {
+
+ case V4L2_CID_AUDIO_MUTE:
+ stfm1000->mute = ctrl->value;
+ ret = 0;
+ break;
+ }
+
+ mutex_unlock(&stfm1000->state_lock);
+
+ return ret;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+
+ (void)stfm1000;
+
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index > 1)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+
+ (void)stfm1000;
+
+ *i = 0;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+
+ (void)stfm1000;
+
+ if (i != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct v4l2_ioctl_ops stfm_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+};
+
+static int stfm1000_open(struct inode *inode, struct file *file)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+
+ mutex_lock(&stfm1000->state_lock);
+ stfm1000->users = 1;
+ mutex_unlock(&stfm1000->state_lock);
+
+ return 0;
+}
+static int stfm1000_close(struct inode *inode, struct file *file)
+{
+ struct stfm1000 *stfm1000 = stfm1000_from_file(file);
+
+ if (!stfm1000)
+ return -ENODEV;
+
+ stfm1000->users = 0;
+ if (stfm1000->removed)
+ kfree(stfm1000);
+ return 0;
+}
+
+static const struct file_operations stfm1000_fops = {
+ .owner = THIS_MODULE,
+ .open = stfm1000_open,
+ .release = stfm1000_close,
+ .ioctl = video_ioctl2,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l_compat_ioctl32,
+#endif
+ .llseek = no_llseek,
+};
+
+/* sysfs */
+
+#define STFM1000_RO_ATTR(var) \
+static ssize_t stfm1000_show_ ## var(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct i2c_client *client = to_i2c_client(d); \
+ struct stfm1000 *stfm1000 = i2c_get_clientdata(client); \
+ return sprintf(buf, "%d\n", stfm1000->var); \
+} \
+static DEVICE_ATTR(var, 0444, stfm1000_show_ ##var, NULL)
+
+#define STFM1000_RW_ATTR(var) \
+static ssize_t stfm1000_show_ ## var(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct i2c_client *client = to_i2c_client(d); \
+ struct stfm1000 *stfm1000 = i2c_get_clientdata(client); \
+ return sprintf(buf, "%u\n", stfm1000->var); \
+} \
+static ssize_t stfm1000_store_ ## var(struct device *d, \
+ struct device_attribute *attr, const char *buf, size_t size) \
+{ \
+ struct i2c_client *client = to_i2c_client(d); \
+ struct stfm1000 *stfm1000 = i2c_get_clientdata(client); \
+ unsigned long v; \
+ \
+ strict_strtoul(buf, 0, &v); \
+ stfm1000_commit_ ## var(stfm1000, v); \
+ return size; \
+} \
+static DEVICE_ATTR(var, 0644, stfm1000_show_ ##var, stfm1000_store_ ##var)
+
+#define STFM1000_RW_ATTR_SIMPLE(var) \
+static void stfm1000_commit_ ## var(struct stfm1000 *stfm1000, \
+ unsigned long value) \
+{ \
+ stfm1000->var = value; \
+} \
+STFM1000_RW_ATTR(var)
+
+STFM1000_RO_ATTR(weak_signal);
+STFM1000_RO_ATTR(pilot_present);
+STFM1000_RO_ATTR(stereo);
+STFM1000_RO_ATTR(rssi);
+STFM1000_RO_ATTR(mpx_dc);
+STFM1000_RO_ATTR(signal_strength);
+STFM1000_RW_ATTR_SIMPLE(rds_signal_th);
+STFM1000_RO_ATTR(rds_present);
+STFM1000_RO_ATTR(is_station);
+
+static void stfm1000_commit_georegion(struct stfm1000 *stfm1000,
+ unsigned long value)
+{
+ /* don't do anything for illegal region */
+ if (value != 0 && value != 1)
+ return;
+
+ mutex_lock(&stfm1000->state_lock);
+
+ stfm1000->georegion = value;
+ if (stfm1000->georegion == 0)
+ stfm1000_clear_bits(stfm1000, STFM1000_INITIALIZATION2,
+ STFM1000_DEEMPH_50_75B);
+ else
+ stfm1000_set_bits(stfm1000, STFM1000_INITIALIZATION2,
+ STFM1000_DEEMPH_50_75B);
+
+ mutex_unlock(&stfm1000->state_lock);
+}
+STFM1000_RW_ATTR(georegion);
+
+static void stfm1000_commit_freq(struct stfm1000 *stfm1000,
+ unsigned long value)
+{
+ mutex_lock(&stfm1000->state_lock);
+
+ /* clamp */
+ if (value < STFM1000_FREQUENCY_100KHZ_MIN * 100)
+ value = STFM1000_FREQUENCY_100KHZ_MIN * 100;
+ else if (value > STFM1000_FREQUENCY_100KHZ_MAX * 100)
+ value = STFM1000_FREQUENCY_100KHZ_MAX * 100;
+
+ stfm1000->freq = value;
+
+ if (stfm1000->active)
+ Take_Down(stfm1000);
+
+ sw_tune(stfm1000, stfm1000->freq);
+
+ if (stfm1000->active)
+ Bring_Up(stfm1000);
+
+ mutex_unlock(&stfm1000->state_lock);
+}
+STFM1000_RW_ATTR(freq);
+
+static void stfm1000_commit_mute(struct stfm1000 *stfm1000,
+ unsigned long value)
+{
+ stfm1000->mute = !!value;
+}
+STFM1000_RW_ATTR(mute);
+
+static void stfm1000_commit_force_mono(struct stfm1000 *stfm1000,
+ unsigned long value)
+{
+ stfm1000->force_mono = !!value;
+ /* set force mono parameters for the filter */
+ stfm1000->filter_parms.pCoefForcedMono = stfm1000->force_mono;
+
+ /* yeah, I know, it's stupid */
+ stfm1000->rds_state.demod.pCoefForcedMono =
+ stfm1000->filter_parms.pCoefForcedMono;
+}
+STFM1000_RW_ATTR(force_mono);
+
+STFM1000_RW_ATTR_SIMPLE(monitor_period);
+STFM1000_RW_ATTR_SIMPLE(quality_monitor);
+STFM1000_RW_ATTR_SIMPLE(quality_monitor_period);
+STFM1000_RW_ATTR_SIMPLE(agc_monitor_period);
+STFM1000_RW_ATTR_SIMPLE(tune_rssi_th);
+STFM1000_RW_ATTR_SIMPLE(tune_mpx_dc_th);
+
+static void stfm1000_commit_rds_enable(struct stfm1000 *stfm1000,
+ unsigned long value)
+{
+ /* don't do anything for illegal values (or for not TB2) */
+ if ((value != 0 && value != 1) ||
+ stfm1000->revid == STFM1000_CHIP_REV_TA2)
+ return;
+
+ mutex_lock(&stfm1000->state_lock);
+
+ stfm1000->rds_enable = value;
+ if (stfm1000->rds_enable == 0)
+ stfm1000_clear_bits(stfm1000, STFM1000_INITIALIZATION2,
+ STFM1000_RDS_ENABLE);
+ else
+ stfm1000_set_bits(stfm1000, STFM1000_INITIALIZATION2,
+ STFM1000_RDS_ENABLE);
+
+ mutex_unlock(&stfm1000->state_lock);
+}
+STFM1000_RW_ATTR(rds_enable);
+
+static void stfm1000_commit_rds_sync(struct stfm1000 *stfm1000,
+ unsigned long value)
+{
+ stfm1000->rds_sync = stfm1000->rds_enable && !!value;
+}
+STFM1000_RW_ATTR(rds_sync);
+
+STFM1000_RW_ATTR_SIMPLE(rds_pkt_good);
+STFM1000_RW_ATTR_SIMPLE(rds_pkt_bad);
+STFM1000_RW_ATTR_SIMPLE(rds_pkt_recovered);
+STFM1000_RW_ATTR_SIMPLE(rds_pkt_lost_sync);
+STFM1000_RW_ATTR_SIMPLE(rds_bit_overruns);
+STFM1000_RW_ATTR_SIMPLE(rds_info);
+
+static void stfm1000_commit_rds_sdnominal_adapt(struct stfm1000 *stfm1000,
+ unsigned long value)
+{
+ stfm1000->rds_sdnominal_adapt = !!value;
+ stfm1000->rds_state.demod.sdnom_adapt = stfm1000->rds_sdnominal_adapt;
+}
+STFM1000_RW_ATTR(rds_sdnominal_adapt);
+
+static void stfm1000_commit_rds_phase_pop(struct stfm1000 *stfm1000,
+ unsigned long value)
+{
+ stfm1000->rds_phase_pop = !!value;
+ stfm1000->rds_state.demod.PhasePoppingEnabled =
+ stfm1000->rds_phase_pop;
+}
+STFM1000_RW_ATTR(rds_phase_pop);
+
+STFM1000_RW_ATTR_SIMPLE(tuning_grid_50KHz);
+
+static ssize_t stfm1000_show_rds_ps(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(d);
+ struct stfm1000 *stfm1000 = i2c_get_clientdata(client);
+ char ps[9];
+
+ if (stfm1000_rds_get_ps(&stfm1000->rds_state, ps, sizeof(ps)) <= 0)
+ ps[0] = '\0';
+
+ return sprintf(buf, "%s\n", ps);
+}
+static DEVICE_ATTR(rds_ps, 0444, stfm1000_show_rds_ps, NULL);
+
+static ssize_t stfm1000_show_rds_text(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(d);
+ struct stfm1000 *stfm1000 = i2c_get_clientdata(client);
+ char text[65];
+
+ if (stfm1000_rds_get_text(&stfm1000->rds_state, text,
+ sizeof(text)) <= 0)
+ text[0] = '\0';
+
+ return sprintf(buf, "%s\n", text);
+}
+static DEVICE_ATTR(rds_text, 0444, stfm1000_show_rds_text, NULL);
+
+static struct device_attribute *stfm1000_attrs[] = {
+ &dev_attr_agc_monitor_period,
+ &dev_attr_force_mono,
+ &dev_attr_freq,
+ &dev_attr_georegion,
+ &dev_attr_is_station,
+ &dev_attr_monitor_period,
+ &dev_attr_mpx_dc,
+ &dev_attr_mute,
+ &dev_attr_pilot_present,
+ &dev_attr_quality_monitor,
+ &dev_attr_quality_monitor_period,
+ &dev_attr_rds_bit_overruns,
+ &dev_attr_rds_enable,
+ &dev_attr_rds_info,
+ &dev_attr_rds_phase_pop,
+ &dev_attr_rds_pkt_bad,
+ &dev_attr_rds_pkt_good,
+ &dev_attr_rds_pkt_lost_sync,
+ &dev_attr_rds_pkt_recovered,
+ &dev_attr_rds_present,
+ &dev_attr_rds_ps,
+ &dev_attr_rds_sdnominal_adapt,
+ &dev_attr_rds_signal_th,
+ &dev_attr_rds_sync,
+ &dev_attr_rds_text,
+ &dev_attr_rssi,
+ &dev_attr_signal_strength,
+ &dev_attr_stereo,
+ &dev_attr_tune_mpx_dc_th,
+ &dev_attr_tune_rssi_th,
+ &dev_attr_tuning_grid_50KHz,
+ &dev_attr_weak_signal,
+ NULL,
+};
+
+/* monitor thread */
+
+static void rds_process(struct stfm1000 *stfm1000)
+{
+ int count, bit;
+ int mix_reg, sdnominal_reg;
+ u32 sdnom, sdnom_new, limit;
+ u8 buf[8];
+
+ if (!stfm1000->rds_enable)
+ return;
+
+ if (stfm1000->rds_sync &&
+ stfm1000->rssi_dc_est_log > stfm1000->rds_signal_th) {
+ if (stfm1000->rds_info)
+ printk(KERN_INFO "RDS: sync\n");
+ stfm1000_rds_reset(&stfm1000->rds_state);
+ stfm1000->rds_demod_running = 1;
+ stfm1000->rds_sync = 0;
+ }
+
+ if (!stfm1000->rds_demod_running)
+ return;
+
+ /* process mix reg requests */
+ spin_lock_irq(&stfm1000->rds_lock);
+ mix_reg = stfm1000_rds_mix_msg_get(&stfm1000->rds_state);
+ spin_unlock_irq(&stfm1000->rds_lock);
+
+ if (mix_reg != -1) {
+
+ if (stfm1000->rds_info)
+ printk(KERN_INFO "RDS: new RDS_MIXOFFSET %d\n",
+ mix_reg & 1);
+
+ /* update register */
+ if (mix_reg & 1)
+ stfm1000_set_bits(stfm1000, STFM1000_INITIALIZATION2,
+ STFM1000_RDS_MIXOFFSET);
+ else
+ stfm1000_clear_bits(stfm1000, STFM1000_INITIALIZATION2,
+ STFM1000_RDS_MIXOFFSET);
+
+ /* signal it's processed */
+ spin_lock_irq(&stfm1000->rds_lock);
+ stfm1000_rds_mix_msg_processed(&stfm1000->rds_state, mix_reg);
+ spin_unlock_irq(&stfm1000->rds_lock);
+ }
+
+ /* process sdnominal reg requests */
+ spin_lock_irq(&stfm1000->rds_lock);
+ sdnominal_reg = stfm1000_rds_sdnominal_msg_get(&stfm1000->rds_state);
+ spin_unlock_irq(&stfm1000->rds_lock);
+
+ /* any change? */
+ if (sdnominal_reg != 0) {
+
+ stfm1000_read(stfm1000, STFM1000_SDNOMINAL, &sdnom);
+
+ sdnom_new = sdnom + sdnominal_reg;
+
+ /* Limit SDNOMINAL to within 244 ppm of its ideal value */
+ limit = stfm1000->sdnominal_pivot +
+ (stfm1000->sdnominal_pivot >> 12);
+ if (sdnom_new > limit)
+ sdnom_new = limit;
+
+ limit = stfm1000->sdnominal_pivot -
+ (stfm1000->sdnominal_pivot >> 12);
+ if (sdnom_new < limit)
+ sdnom_new = limit;
+
+ /* write the register */
+ stfm1000_write(stfm1000, STFM1000_SDNOMINAL, sdnom_new);
+
+ /* signal it's processed */
+ spin_lock_irq(&stfm1000->rds_lock);
+ stfm1000_rds_sdnominal_msg_processed(&stfm1000->rds_state,
+ sdnominal_reg);
+ spin_unlock_irq(&stfm1000->rds_lock);
+ }
+
+ /* pump bits out & pass them to the process function */
+ spin_lock_irq(&stfm1000->rds_lock);
+ while (stfm1000_rds_bits_available(&stfm1000->rds_state) > 128) {
+ count = 0;
+ while (count++ < 128 &&
+ (bit = stmf1000_rds_get_bit(
+ &stfm1000->rds_state)) >= 0) {
+ spin_unlock_irq(&stfm1000->rds_lock);
+
+ /* push bit for packet processing */
+ stfm1000_rds_packet_bit(&stfm1000->rds_state, bit);
+
+ spin_lock_irq(&stfm1000->rds_lock);
+ }
+ }
+ spin_unlock_irq(&stfm1000->rds_lock);
+
+ /* now we're free to process non-interrupt related work */
+ while (stfm1000_rds_packet_dequeue(&stfm1000->rds_state, buf) == 0) {
+
+ if (stfm1000->rds_info)
+ printk(KERN_INFO "RDS-PKT: %02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+
+ stfm1000_rds_process_packet(&stfm1000->rds_state, buf);
+ }
+
+ /* update our own counters */
+ stfm1000->rds_pkt_good += stfm1000->rds_state.pkt.good_packets;
+ stfm1000->rds_pkt_bad += stfm1000->rds_state.pkt.bad_packets;
+ stfm1000->rds_pkt_recovered +=
+ stfm1000->rds_state.pkt.recovered_packets;
+ stfm1000->rds_pkt_lost_sync +=
+ stfm1000->rds_state.pkt.sync_lost_packets;
+ stfm1000->rds_bit_overruns +=
+ stfm1000->rds_state.demod.RdsDemodSkippedBitCnt;
+
+ /* zero them now */
+ stfm1000->rds_state.pkt.good_packets = 0;
+ stfm1000->rds_state.pkt.bad_packets = 0;
+ stfm1000->rds_state.pkt.recovered_packets = 0;
+ stfm1000->rds_state.pkt.sync_lost_packets = 0;
+ stfm1000->rds_state.demod.RdsDemodSkippedBitCnt = 0;
+
+ /* reset requested from RDS handler? */
+ if (stfm1000_rds_get_reset_req(&stfm1000->rds_state)) {
+ if (stfm1000->rds_info)
+ printk(KERN_INFO "RDS: reset requested\n");
+ stfm1000_rds_reset(&stfm1000->rds_state);
+
+ stfm1000->rds_sync = stfm1000->rds_enable; /* force sync (if RDS) */
+ stfm1000->rds_demod_running = 0;
+ stfm1000->rssi_dc_est_log = 0;
+ stfm1000->signal_strength = 0;
+ }
+}
+
+void stfm1000_monitor_signal(struct stfm1000 *stfm1000, int bit)
+{
+ set_bit(bit, &stfm1000->thread_events);
+ return wake_up_interruptible(&stfm1000->thread_wait);
+}
+
+static int stfm1000_monitor_thread(void *data)
+{
+ struct stfm1000 *stfm1000 = data;
+ int ret;
+
+ printk(KERN_INFO "stfm1000: monitor thread started\n");
+
+ set_freezable();
+
+ /* Hmm, linux becomes *very* unhappy without this ... */
+ while (!kthread_should_stop()) {
+
+ ret = wait_event_interruptible_timeout(stfm1000->thread_wait,
+ stfm1000->thread_events == 0,
+ msecs_to_jiffies(stfm1000->monitor_period));
+
+ stfm1000->thread_events = 0;
+
+ if (kthread_should_stop())
+ break;
+
+ try_to_freeze();
+
+ mutex_lock(&stfm1000->state_lock);
+
+ /* we must be active */
+ if (!stfm1000->active)
+ goto next;
+
+ if (stfm1000->rds_enable)
+ rds_process(stfm1000);
+
+ /* perform quality monitor */
+ if (time_after_eq(jiffies, stfm1000->next_quality_monitor)) {
+
+ /* full quality monitor? */
+ if (stfm1000->quality_monitor)
+ Monitor_STFM_Quality(stfm1000);
+ else /* simple */
+ Is_Station(stfm1000);
+
+ while (time_after_eq(jiffies,
+ stfm1000->next_quality_monitor))
+ stfm1000->next_quality_monitor +=
+ msecs_to_jiffies(
+ stfm1000->quality_monitor_period);
+ }
+
+ /* perform AGC monitor (if enabled) */
+ if (stfm1000->agc_monitor && time_after_eq(jiffies,
+ stfm1000->next_agc_monitor)) {
+ Monitor_STFM_AGC(stfm1000);
+ while (time_after_eq(jiffies,
+ stfm1000->next_agc_monitor))
+ stfm1000->next_agc_monitor +=
+ msecs_to_jiffies(
+ stfm1000->agc_monitor_period);
+ }
+next:
+ mutex_unlock(&stfm1000->state_lock);
+ }
+
+ printk(KERN_INFO "stfm1000: monitor thread stopped\n");
+
+ return 0;
+}
+
+static u64 stfm1000_dma_mask = DMA_32BIT_MASK;
+
+static int stfm1000_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct device *dev;
+ struct stfm1000 *stfm1000;
+ struct video_device *vd;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+ u32 id;
+ const char *idtxt;
+ int i;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C doesn't support I2C_FUNC_SMBUS_BYTE_DATA\n");
+ return -EIO;
+ }
+
+ /* make sure the dma masks are set correctly */
+ dev = &client->dev;
+ if (!dev->dma_mask)
+ dev->dma_mask = &stfm1000_dma_mask;
+ if (!dev->coherent_dma_mask)
+ dev->coherent_dma_mask = DMA_32BIT_MASK;
+
+ stfm1000 = kzalloc(sizeof(*stfm1000), GFP_KERNEL);
+ if (!stfm1000)
+ return -ENOMEM;
+
+ stfm1000->client = client;
+ i2c_set_clientdata(client, stfm1000);
+
+ mutex_init(&stfm1000->xfer_lock);
+ mutex_init(&stfm1000->state_lock);
+
+ vd = &stfm1000->radio;
+
+ strcpy(vd->name, "stfm1000");
+ vd->vfl_type = VID_TYPE_TUNER;
+ vd->fops = &stfm1000_fops;
+ vd->ioctl_ops = &stfm_ioctl_ops;
+
+ /* vd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; */
+
+ vd->parent = &client->dev;
+
+ ret = video_register_device(vd, VFL_TYPE_RADIO, -1);
+ if (ret != 0) {
+ dev_warn(&adapter->dev,
+ "Cannot register radio device\n");
+ goto out;
+ }
+
+ spin_lock_init(&stfm1000->rds_lock);
+
+ stfm1000_setup_reg_set(stfm1000);
+
+ /* stfm1000->dbgflg |= STFM1000_DBGFLG_I2C; */
+
+ ret = stfm1000_read(stfm1000, STFM1000_CHIPID, &id);
+ if (ret < 0) {
+ dev_warn(&adapter->dev,
+ "Cannot read ID register\n");
+ goto out;
+ }
+ stfm1000->revid = id & 0xff;
+
+ /* NOTE: the tables are precalculated */
+ stfm1000->tune_rssi_th = 28;
+ stfm1000->tune_mpx_dc_th = 300;
+ stfm1000->adj_chan_th = 100;
+ stfm1000->pilot_est_th = 25;
+ stfm1000->agc_monitor = 0; /* AGC monitor disabled */
+ stfm1000->quality_monitor = 1;
+ stfm1000->weak_signal = 0;
+ stfm1000->prev_pilot_present = 0;
+ stfm1000->tune_cap_a_f = (u32)(72.4 * 65536);
+ stfm1000->tune_cap_b_f = (u32)(0.07 * 65536);
+
+ /* only TB2 supports RDS */
+ stfm1000->rds_enable = stfm1000->revid == STFM1000_CHIP_REV_TB2 &&
+ rds_enable;
+ stfm1000->rds_present = 0;
+ stfm1000->rds_signal_th = 33;
+
+ stfm1000->freq = 92600;
+
+ stfm1000->georegion = georegion;
+ stfm1000->rssi = 0;
+ stfm1000->stereo = 0;
+ stfm1000->force_mono = 0;
+ stfm1000->monitor_period = 100;
+ stfm1000->quality_monitor_period = 1000;
+ stfm1000->agc_monitor_period = 200;
+
+ stfm1000->rds_sdnominal_adapt = 0;
+ stfm1000->rds_phase_pop = 1;
+
+ /* enable info about RDS */
+ stfm1000->rds_info = 0;
+
+ ret = stfm1000_power_up(stfm1000);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: stfm1000_power_up failed\n",
+ __func__);
+ goto out;
+ }
+
+ if (stfm1000_alsa_ops && stfm1000_alsa_ops->init) {
+ ret = (*stfm1000_alsa_ops->init)(stfm1000);
+ if (ret != 0)
+ goto out;
+ stfm1000->alsa_initialized = 1;
+ }
+
+ ret = 0;
+ for (i = 0; stfm1000_attrs[i]; i++) {
+ ret = device_create_file(dev, stfm1000_attrs[i]);
+ if (ret)
+ break;
+ }
+ if (ret) {
+ while (--i >= 0)
+ device_remove_file(dev, stfm1000_attrs[i]);
+ goto out;
+ }
+
+ /* add it to the list */
+ mutex_lock(&devlist_lock);
+ stfm1000->idx = stfm1000_devcount++;
+ list_add_tail(&stfm1000->devlist, &stfm1000_devlist);
+ mutex_unlock(&devlist_lock);
+
+ init_waitqueue_head(&stfm1000->thread_wait);
+ stfm1000->thread = kthread_run(stfm1000_monitor_thread, stfm1000,
+ "stfm1000-%d", stfm1000->idx);
+ if (stfm1000->thread == NULL) {
+ printk(KERN_ERR "stfm1000: kthread_run failed\n");
+ goto out;
+ }
+
+ idtxt = stfm1000_get_rev_txt(stfm1000->revid);
+ if (idtxt == NULL)
+ printk(KERN_INFO "STFM1000: Loaded for unknown revision id "
+ "0x%02x\n", stfm1000->revid);
+ else
+ printk(KERN_INFO "STFM1000: Loaded for revision %s\n", idtxt);
+
+ return 0;
+
+out:
+ kfree(stfm1000);
+ return ret;
+}
+
+static int stfm1000_remove(struct i2c_client *client)
+{
+ struct stfm1000 *stfm1000 = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+ int i;
+
+ kthread_stop(stfm1000->thread);
+
+ for (i = 0; stfm1000_attrs[i]; i++)
+ device_remove_file(dev, stfm1000_attrs[i]);
+
+ if (stfm1000->alsa_initialized) {
+ BUG_ON(stfm1000_alsa_ops->release == NULL);
+ (*stfm1000_alsa_ops->release)(stfm1000);
+ stfm1000->alsa_initialized = 0;
+ }
+
+ stfm1000_power_down(stfm1000);
+
+ video_unregister_device(&stfm1000->radio);
+
+ mutex_lock(&devlist_lock);
+ list_del(&stfm1000->devlist);
+ mutex_unlock(&devlist_lock);
+
+ kfree(stfm1000);
+ return 0;
+}
+
+static const struct i2c_device_id stfm1000_id[] = {
+ { "stfm1000", 0xC0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, stfm1000_id);
+
+static struct i2c_driver stfm1000_i2c_driver = {
+ .driver = {
+ .name = "stfm1000",
+ },
+ .probe = stfm1000_probe,
+ .remove = stfm1000_remove,
+ .id_table = stfm1000_id,
+};
+
+static int __init
+stfm1000_init(void)
+{
+ /* pull those in */
+ (void)Lock_Station;
+ (void)Unlock_Station;
+ return i2c_add_driver(&stfm1000_i2c_driver);
+}
+
+static void __exit
+stfm1000_exit(void)
+{
+ i2c_del_driver(&stfm1000_i2c_driver);
+
+ stfm1000_alsa_ops = NULL;
+}
+
+module_init(stfm1000_init);
+module_exit(stfm1000_exit);
+
+MODULE_AUTHOR("Pantelis Antoniou");
+MODULE_DESCRIPTION("A driver for the STFM1000 chip.");
+MODULE_LICENSE("GPL");
+
+module_param(georegion, int, 0400);
+module_param(rds_enable, int, 0400);
diff --git a/drivers/media/radio/stfm1000/stfm1000-filter.c b/drivers/media/radio/stfm1000/stfm1000-filter.c
new file mode 100644
index 000000000000..df42524a5da7
--- /dev/null
+++ b/drivers/media/radio/stfm1000/stfm1000-filter.c
@@ -0,0 +1,860 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+
+#include "stfm1000.h"
+
+void stfm1000_filter_reset(struct stfm1000_filter_parms *sdf)
+{
+ sdf->Left = 0;
+ sdf->Right = 0;
+ sdf->RssiDecoded = 0;
+ sdf->RssiMant = 0;
+ sdf->RssiExp = 0;
+ sdf->RssiLb = 0;
+ sdf->TrueRssi = 0;
+ sdf->Prssi = 0;
+ sdf->RssiLog = 0;
+ sdf->ScaledTrueRssi = 0;
+ sdf->FilteredRssi = 0;
+ sdf->PrevFilteredRssi = 0;
+ sdf->DecRssi = 0;
+ sdf->ScaledRssiDecoded = 0;
+ sdf->ScaledRssiDecodedZ = 0;
+ sdf->ScaledRssiDecodedZz = 0;
+ sdf->Echo = 0;
+ sdf->EchoLb = 0;
+ sdf->TrueEcho = 0;
+ sdf->FilteredEchoLpr = 0;
+ sdf->PrevFilteredEchoLpr = 0;
+ sdf->FilteredEchoLmr = 0;
+ sdf->PrevFilteredEchoLmr = 0;
+ sdf->GatedEcho = 0;
+ sdf->ControlLpr = 0;
+ sdf->ControlLmr = 0;
+ sdf->LprBw = 0;
+ sdf->LmrBw = 0;
+
+ sdf->LprXz = 0;
+ sdf->LprXzz = 0;
+ sdf->LprYz = 0;
+ sdf->LprYzz = 0;
+ sdf->LmrXz = 0;
+ sdf->LmrXzz = 0;
+ sdf->LmrYz = 0;
+ sdf->LmrYzz = 0;
+ sdf->FilteredLpr = 0;
+ sdf->FilteredLmr = 0;
+
+ sdf->B0B = 0;
+ sdf->B0S = 0;
+ sdf->B0M = 0;
+ sdf->B1over2B = 0;
+ sdf->B1over2S = 0;
+ sdf->B1over2M = 0;
+ sdf->A1over2B = 0;
+ sdf->A1over2S = 0;
+ sdf->A1over2M = 0;
+ sdf->A2B = 0;
+ sdf->A2S = 0;
+ sdf->A2M = 0;
+
+ sdf->AdjBw = 0;
+
+ sdf->pCoefLprBwThLo = 20 << 8;
+ sdf->pCoefLprBwThHi = 30 << 8;
+ sdf->pCoefLmrBwThLo = 40 << 8;
+ sdf->pCoefLmrBwThHi = 50 << 8;
+ sdf->pCoefLprBwSlSc = 4800; /* SDK-2287 */
+ sdf->pCoefLprBwSlSh = 10; /* SDK-2287 */
+ sdf->pCoefLmrBwSlSc = 4800; /* SDK-2287 */
+ sdf->pCoefLmrBwSlSh = 10; /* SDK-2287 */
+ sdf->pCoefLprGaSlSc = 0;
+ sdf->pCoefLprGaSlSh = 0;
+
+ sdf->ScaledControlLmr = 0;
+
+ sdf->LprGa = 32767;
+ sdf->LmrGa = 32767;
+
+ sdf->pCoefLprGaTh = 20; /* 25 */
+ sdf->pCoefLmrGaTh = 55; /* 60 50 */
+
+ sdf->MuteAudio = 0;
+ sdf->PrevMuteAudio = 0;
+ sdf->MuteActionFlag = 0;
+ sdf->ScaleAudio = 0;
+
+ /* *** Programmable initial setup for stereo path filters */
+ sdf->LprB0 = 18806; /* -3dB cutoff = 17 kHz */
+ sdf->LprB1over2 = 18812; /* -3dB cutoff = 17 kHz */
+ sdf->LprA1over2 = -16079; /* -3dB cutoff = 17 kHz */
+ sdf->LprA2 = -11125; /* -3dB cutoff = 17 kHz */
+ sdf->LmrB0 = 18806; /* -3dB cutoff = 17 kHz */
+ sdf->LmrB1over2 = 18812; /* -3dB cutoff = 17 kHz */
+ sdf->LmrA1over2 = -16079; /* -3dB cutoff = 17 kHz */
+ sdf->LmrA2 = -11125; /* -3dB cutoff = 17 kHz */
+
+ sdf->pCoefForceLockLmrBw = 0; /* Force Lock LMR BW = LPR BW
+ * XXX BUG WARNING -
+ * This control doesn't work! */
+
+ sdf->pCoefForcedMono = 0; /* Do not set this =
+ * Quality Monitor will overwrite it */
+ sdf->pCoefBypassBlend = 0; /* BUG WARNING -
+ * This control doesn't work! */
+ sdf->pCoefBypassSoftmute = 0; /* BUG WARNING -
+ * This control doesn't work! */
+ sdf->pCoefBypassBwCtl = 0; /* BUG WARNING -
+ * This control doesn't work! */
+
+ /* There's a bug or something in the attack/decay section b/c
+ * setting these coef's to anything */
+ /* higher than 100ms or so causes the RSSI to be artificially low -
+ * Needs investigation! 15DEC06 */
+ sdf->pCoefRssiAttack = 65386; /* changed to 100ms to avoid
+ * stereo crackling
+ * 60764 corresponds to 3 */
+ sdf->pCoefRssiDecay = 65386; /* changed to 100ms to avoid
+ * stereo crackling
+ * 65530 corresponds to 10 */
+ sdf->pCoefEchoLprAttack = 52239; /* corresponds to 1 */
+ sdf->pCoefEchoLprDecay = 64796; /* corresponds to 20 */
+ sdf->pCoefEchoLmrAttack = 52239; /* corresponds to 1 */
+ sdf->pCoefEchoLmrDecay = 65520; /* corresponds to 20 */
+ sdf->pCoefEchoTh = 100;
+ sdf->pCoefEchoScLpr = (u16) (0.9999 * 32767.0);
+ sdf->pCoefEchoScLmr = (u16) (0.9999 * 32767.0);
+}
+
+void stfm1000_filter_decode(struct stfm1000_filter_parms *sdf, s16 Lpr,
+ s16 Lmr, u16 Rssi)
+{
+ s16 temp1_reg; /* mimics 16 bit register */
+ s16 temp2_reg; /* mimics 16 bit register */
+ s16 temp3_reg; /* mimics 16 bit register */
+ s16 temp4_reg; /* mimics 16 bit register */
+#ifndef _TUNER_STFM_MUTE
+ s16 temp5_reg; /* mimics 16 bit register */
+#endif
+ s32 temp2_reg_32; /*eI 108 27th Feb 06 temp variables. */
+
+ /* **************************************************************** */
+ /* *** Stereo Processing ****************************************** */
+ /* **************************************************************** */
+ /* *** This block operates at Fs = 44.1kHz */
+ /* ******** */
+ /* *** LPR path filter (2nd order IIR) */
+
+ sdf->Acc_signed = sdf->LprB0 * Lpr + 2 * (sdf->LprB1over2 * sdf->LprXz)
+ + sdf->LprB0 * sdf->LprXzz + 2 * (sdf->LprA1over2 * sdf->LprYz)
+ + sdf->LprA2 * sdf->LprYzz;
+
+ sdf->FilteredLpr = sdf->Acc_signed >> 15;
+
+ sdf->LprXzz = sdf->LprXz; /* update taps */
+ sdf->LprXz = Lpr;
+ sdf->LprYzz = sdf->LprYz;
+ sdf->LprYz = sdf->FilteredLpr;
+
+ /* *** LMR path filter (2nd order IIR) */
+ sdf->Acc_signed = sdf->LmrB0 * Lmr + 2 * (sdf->LmrB1over2 * sdf->LmrXz)
+ + sdf->LmrB0 * sdf->LmrXzz + 2 * (sdf->LmrA1over2 * sdf->LmrYz)
+ + sdf->LmrA2 * sdf->LmrYzz;
+
+ sdf->FilteredLmr = sdf->Acc_signed >> 15;
+
+ sdf->LmrXzz = sdf->LmrXz; /* update taps */
+ sdf->LmrXz = Lmr;
+ sdf->LmrYzz = sdf->LmrYz;
+ sdf->LmrYz = sdf->FilteredLmr;
+
+ /* *** Stereo Matrix */
+ if (0 == sdf->pCoefBypassBlend)
+ temp1_reg = sdf->LmrGa * sdf->FilteredLmr >> 15; /* Blend */
+ else
+ temp1_reg = sdf->FilteredLmr;
+
+ if (sdf->pCoefForcedMono) /* Forced Mono */
+ temp1_reg = 0;
+
+ if (0 == sdf->pCoefBypassSoftmute) {
+ temp2_reg = sdf->LprGa * sdf->FilteredLpr >> 15; /* LPR */
+ temp3_reg = sdf->LprGa * temp1_reg >> 15; /* LMR */
+ } else {
+ temp2_reg = sdf->FilteredLpr;
+ temp3_reg = temp1_reg;
+ }
+
+ temp4_reg = (temp2_reg + temp3_reg) / 2; /* Matrix */
+
+#ifndef _TUNER_STFM_MUTE
+ temp5_reg = (temp2_reg - temp3_reg) / 2;
+#endif
+
+#if 0
+ /* *** DC Cut Filter (leaky bucket estimate) */
+ if (0 == sdf->pCoefBypassDcCut) {
+ sdf->LeftLb_i32 =
+ sdf->LeftLb_i32 + temp4_reg - (sdf->LeftLb_i32 >> 8);
+ temp2_reg = temp4_reg - (sdf->LeftLb_i32 >> 8); /* signal -
+ dc_estimate */
+
+ sdf->RightLb_i32 =
+ sdf->RightLb_i32 + temp5_reg - (sdf->RightLb_i32 >> 8);
+ temp3_reg = temp5_reg - (sdf->RightLb_i32 >> 8); /* signal -
+ dc_estimate */
+ } else {
+ temp2_reg = temp4_reg;
+ temp3_reg = temp5_reg;
+ }
+#endif
+#ifdef _TUNER_STFM_MUTE
+ /* *** Mute Audio */
+ if (sdf->MuteAudio != sdf->PrevMuteAudio) /* Mute transition */
+ sdf->MuteActionFlag = 1; /* set flag */
+ sdf->PrevMuteAudio = sdf->MuteAudio; /* update history */
+
+ if (sdf->MuteActionFlag) {
+ if (0 == sdf->MuteAudio) { /* Mute to zero */
+ /* gradual mute down */
+ sdf->ScaleAudio = sdf->ScaleAudio - sdf->pCoefMuteStep;
+
+ /* eI-117:Oct28:as per C++ code */
+ /* if (0 < sdf->ScaleAudio) */
+ if (0 > sdf->ScaleAudio) {
+ sdf->ScaleAudio = 0; /* Minimum scale
+ * factor */
+ sdf->MuteActionFlag = 0; /* End Mute Action */
+ }
+ } else { /* Un-Mute to one */
+ /* gradual mute up */
+ sdf->ScaleAudio = sdf->ScaleAudio + sdf->pCoefMuteStep;
+ if (0 > sdf->ScaleAudio) { /* look for rollover
+ * beyong 32767 */
+ sdf->ScaleAudio = 32767; /* Maximum scale
+ * factor */
+ sdf->MuteActionFlag = 0; /* End Mute Action */
+ }
+ } /* end else */
+ } /* end if (sdf->MuteActionFlag) */
+
+/*! Output Processed Sample */
+
+ sdf->Left = (temp2_reg * sdf->ScaleAudio) >> 15; /* Scale */
+ sdf->Right = (temp3_reg * sdf->ScaleAudio) >> 15; /* Scale */
+
+#else /* !_TUNER_STFM_MUTE */
+
+ sdf->Left = temp4_reg;
+ sdf->Right = temp5_reg;
+
+#endif /* !_TUNER_STFM_MUTE */
+
+ /* *** End Stereo Processing ************************************** */
+ /* **************************************************************** */
+
+ /* **************************************************************** */
+ /* *** Signal Quality Indicators ********************************** */
+ /* **************************************************************** */
+ /* *** This block operates at Fs = 44.1kHz */
+ /* ******** */
+ /* *** RSSI */
+ /* ******** */
+ /* Decode Floating Point RSSI data */
+ /*! Input RSSI sample */
+ sdf->RssiMant = (Rssi & 0xFFE0) >> 5; /* 11 msb's */
+ sdf->RssiExp = Rssi & 0x001F; /* 5 lsb's */
+ sdf->RssiDecoded = sdf->RssiMant << sdf->RssiExp;
+
+ /* *** Convert RSSI to 10*Log10(RSSI) */
+ /* This is easily accomplished in DSP code using the CLZ instruction */
+ /* rather than using all these comparisons. */
+ /* The basic idea is this: */
+ /* if x >= 2^P */
+ /* f(x) = 3*x>>P + (3*P-3) */
+ /* Approx. is valid over the range of sdf->RssiDecoded in [0, 2^21] */
+ /* *** */
+ if (sdf->RssiDecoded >= 1048576)
+ sdf->Prssi = 20;
+ else if (sdf->RssiDecoded >= 524288)
+ sdf->Prssi = 19;
+ else if (sdf->RssiDecoded >= 262144)
+ sdf->Prssi = 18;
+ else if (sdf->RssiDecoded >= 131072)
+ sdf->Prssi = 17;
+ else if (sdf->RssiDecoded >= 65536)
+ sdf->Prssi = 16;
+ else if (sdf->RssiDecoded >= 32768)
+ sdf->Prssi = 15;
+ else if (sdf->RssiDecoded >= 16384)
+ sdf->Prssi = 14;
+ else if (sdf->RssiDecoded >= 8192)
+ sdf->Prssi = 13;
+ else if (sdf->RssiDecoded >= 4096)
+ sdf->Prssi = 12;
+ else if (sdf->RssiDecoded >= 2048)
+ sdf->Prssi = 11;
+ else if (sdf->RssiDecoded >= 1024)
+ sdf->Prssi = 10;
+ else if (sdf->RssiDecoded >= 512)
+ sdf->Prssi = 9;
+ else if (sdf->RssiDecoded >= 256)
+ sdf->Prssi = 8;
+ else if (sdf->RssiDecoded >= 128)
+ sdf->Prssi = 7;
+ else if (sdf->RssiDecoded >= 64)
+ sdf->Prssi = 6;
+ else if (sdf->RssiDecoded >= 32)
+ sdf->Prssi = 5;
+ else if (sdf->RssiDecoded >= 16)
+ sdf->Prssi = 4;
+ else if (sdf->RssiDecoded >= 8)
+ sdf->Prssi = 3;
+ else if (sdf->RssiDecoded >= 4)
+ sdf->Prssi = 2;
+ else if (sdf->RssiDecoded >= 2)
+ sdf->Prssi = 1;
+ else
+ sdf->Prssi = 0;
+ sdf->RssiLog =
+ (3 * sdf->RssiDecoded >> sdf->Prssi) + (3 * sdf->Prssi - 3);
+
+ if (0 > sdf->RssiLog) /* Clamp to positive */
+ sdf->RssiLog = 0;
+
+ /* Compensate for errors in truncation/approximation by adding 1 */
+ sdf->RssiLog = sdf->RssiLog + 1;
+
+ /* Leaky Bucket Filter DC estimate of RSSI */
+ sdf->RssiLb = sdf->RssiLb + sdf->RssiLog - (sdf->RssiLb >> 3);
+ sdf->TrueRssi = sdf->RssiLb >> 3;
+
+ /* Scale up so we have some room for precision */
+ sdf->ScaledTrueRssi = sdf->TrueRssi << 8;
+ /* ************ */
+ /* *** end RSSI */
+ /* ************ */
+
+ /* ******** */
+ /* *** Echo */
+ /* ******** */
+ /* *** Isolate Echo information as higher frequency info */
+ /* using [1 -2 1] highpass FIR */
+ sdf->ScaledRssiDecoded = sdf->RssiDecoded >> 4;
+ sdf->Echo =
+ (s16) ((sdf->ScaledRssiDecoded -
+ 2 * sdf->ScaledRssiDecodedZ + sdf->ScaledRssiDecodedZz));
+ sdf->ScaledRssiDecodedZz = sdf->ScaledRssiDecodedZ;
+ sdf->ScaledRssiDecodedZ = sdf->ScaledRssiDecoded;
+ /* ************ */
+ /* *** end Echo */
+ /* ************ */
+ /* *** End Signal Quality Indicators ******************************* */
+ /* ***************************************************************** */
+
+ /* ***************************************************************** */
+ /* *** Weak Signal Processing ************************************** */
+ /* ***************************************************************** */
+ /* *** This block operates at Fs = 44.1/16 = 2.75 Khz
+ * *eI 108 28th Feb 06 WSP and SM executes at 2.75Khz */
+ /* decimate by 16 STFM_FILTER_BLOCK_MULTIPLE is 16 */
+ if (0 == sdf->DecRssi) {
+ /* *** Filter RSSI via attack/decay structure */
+ if (sdf->ScaledTrueRssi > sdf->PrevFilteredRssi)
+ sdf->Acc =
+ sdf->pCoefRssiAttack *
+ sdf->PrevFilteredRssi + (65535 -
+ sdf->pCoefRssiAttack)
+ * sdf->ScaledTrueRssi;
+ else
+ sdf->Acc =
+ sdf->pCoefRssiDecay *
+ sdf->PrevFilteredRssi + (65535 -
+ sdf->pCoefRssiDecay)
+ * sdf->ScaledTrueRssi;
+
+ sdf->FilteredRssi = sdf->Acc >> 16;
+ sdf->PrevFilteredRssi = sdf->FilteredRssi;
+
+ /* *** Form Echo "energy" representation */
+ if (0 > sdf->Echo)
+ sdf->Echo = -sdf->Echo; /* ABS() */
+
+ /* Threshold compare */
+ sdf->GatedEcho = (s16) (sdf->Echo - sdf->pCoefEchoTh);
+ if (0 > sdf->GatedEcho) /* Clamp to (+)ve */
+ sdf->GatedEcho = 0;
+
+ /* *** Leaky bucket DC estimate of Echo energy */
+ sdf->EchoLb = sdf->EchoLb + sdf->GatedEcho -
+ (sdf->EchoLb >> 3);
+ sdf->TrueEcho = sdf->EchoLb >> 3;
+
+ /* *** Filter Echo via attack/decay structure for LPR */
+ if (sdf->TrueEcho > sdf->PrevFilteredEchoLpr)
+ sdf->Acc =
+ sdf->pCoefEchoLprAttack *
+ sdf->PrevFilteredEchoLpr +
+ (65535 - sdf->pCoefEchoLprAttack) *
+ sdf->TrueEcho;
+ else
+ sdf->Acc =
+ sdf->pCoefEchoLprDecay *
+ sdf->PrevFilteredEchoLpr +
+ (65535 - sdf->pCoefEchoLprDecay) *
+ sdf->TrueEcho;
+
+ sdf->FilteredEchoLpr = sdf->Acc >> 16;
+ sdf->PrevFilteredEchoLpr = sdf->FilteredEchoLpr;
+
+ /* *** Filter Echo via attack/decay structure for LMR */
+ if (sdf->TrueEcho > sdf->PrevFilteredEchoLmr)
+ sdf->Acc = sdf->pCoefEchoLmrAttack *
+ sdf->PrevFilteredEchoLmr +
+ (65535 - sdf->pCoefEchoLmrAttack)
+ * sdf->TrueEcho;
+ else
+ sdf->Acc =
+ sdf->pCoefEchoLmrDecay *
+ sdf->PrevFilteredEchoLmr + (65535 -
+ sdf->pCoefEchoLmrDecay)
+ * sdf->TrueEcho;
+
+ sdf->FilteredEchoLmr = sdf->Acc >> 16;
+ sdf->PrevFilteredEchoLmr = sdf->FilteredEchoLmr;
+
+ /* *** Form control variables */
+ /* Generically speaking, ctl = f(RSSI, Echo) =
+ * RSSI - (a*Echo)<<b, where a,b are programmable */
+ sdf->ControlLpr = sdf->FilteredRssi -
+ ((sdf->pCoefEchoScLpr *
+ sdf->FilteredEchoLpr << sdf->pCoefEchoShLpr) >> 15);
+ if (0 > sdf->ControlLpr)
+ sdf->ControlLpr = 0; /* Clamp to positive */
+
+ sdf->ControlLmr = sdf->FilteredRssi -
+ ((sdf->pCoefEchoScLmr *
+ sdf->FilteredEchoLmr << sdf->pCoefEchoShLmr) >> 15);
+ if (0 > sdf->ControlLmr)
+ sdf->ControlLmr = 0; /* Clamp to positive */
+
+ /* *** Define LPR_BW = f(control LPR) */
+ /* Assume that 5 kHz and 17 kHz are limits of LPR_BW control */
+ if (sdf->ControlLpr <= sdf->pCoefLprBwThLo)
+ sdf->LprBw = 5000; /* lower limit is 5 kHz */
+ else if (sdf->ControlLpr >= sdf->pCoefLprBwThHi)
+ sdf->LprBw = 17000; /* upper limit is 17 kHz */
+ else
+ sdf->LprBw = 17000 -
+ ((sdf->pCoefLprBwSlSc *
+ (sdf->pCoefLprBwThHi -
+ sdf->ControlLpr)) >> sdf->pCoefLprBwSlSh);
+
+ /* *** Define LMR_BW = f(control LMR) */
+ /* Assume that 5 kHz and 17 kHz are limits of LPR_BW control */
+ if (0 == sdf->pCoefForceLockLmrBw) { /* only do these calc's
+ * if LMR BW not
+ * ForceLocked */
+ if (sdf->ControlLmr <= sdf->pCoefLmrBwThLo)
+ sdf->LmrBw = 5000; /* lower limit is
+ * 5 kHz */
+ else if (sdf->ControlLmr >= sdf->pCoefLmrBwThHi)
+ sdf->LmrBw = 17000; /* upper limit is
+ * 17 kHz */
+ else
+ sdf->LmrBw = 17000 -
+ ((sdf->pCoefLmrBwSlSc *
+ (sdf->pCoefLmrBwThHi -
+ sdf->ControlLmr)) >>
+ sdf->pCoefLmrBwSlSh);
+ }
+ /* *** Define LMR_Gain = f(control LMR)
+ * Assume that Blending occurs across 20 dB range of
+ * control LMR. For sake of listenability, approximate
+ * antilog blending curve
+ * To simplify antilog approx, scale control LMR back into
+ * "RSSI in dB range" [0,60] */
+ sdf->ScaledControlLmr = sdf->ControlLmr >> 8;
+
+ /* how far below blend threshold are we? */
+ temp1_reg = sdf->pCoefLmrGaTh - sdf->ScaledControlLmr;
+ if (0 > temp1_reg) /* We're not below threshold,
+ * so no blending needed */
+ temp1_reg = 0;
+ temp2_reg = 20 - temp1_reg; /* Blend range = 20 dB */
+ if (0 > temp2_reg)
+ temp2_reg = 0; /* if beyond that range,
+ * then clamp to 0 */
+
+ /* We want stereo separation (n dB) to rolloff linearly over
+ * the 20 dB wide blend region.
+ * this necessitates a particular rolloff for the blend
+ * parameter, which is not obvious.
+ * See sw_audio/log_approx.m for calculation of this rolloff,
+ * implemented below...
+ * Note that stereo_separation (in dB) = 20*log10((1+a)/(1-a)),
+ * where a = blend scaler
+ * appropriately scaled for 2^15. This relationship sits at
+ * the heart of why this curve is needed. */
+ if (15 <= temp2_reg)
+ temp3_reg = 264 * temp2_reg + 27487;
+ else if (10 <= temp2_reg)
+ temp3_reg = 650 * temp2_reg + 21692;
+ else if (5 <= temp2_reg)
+ temp3_reg = 1903 * temp2_reg + 9166;
+ else
+ temp3_reg = 3736 * temp2_reg;
+
+ sdf->LmrGa = temp3_reg;
+
+ if (32767 < sdf->LmrGa)
+ sdf->LmrGa = 32767; /* Clamp to '1' */
+
+ /* *** Define LPR_Gain = f(control LPR)
+ * Assume that SoftMuting occurs across 20 dB range of
+ * control LPR
+ * For sake of listenability, approximate antilog softmute
+ * curve To simplify antilog approx, scale control LPR back
+ * into "RSSI in dB range" [0,60] */
+ sdf->ScaledControlLpr = sdf->ControlLpr >> 8;
+ /* how far below softmute threshold are we? */
+ temp1_reg = sdf->pCoefLprGaTh - sdf->ScaledControlLpr;
+ if (0 > temp1_reg) /* We're not below threshold,
+ * so no softmute needed */
+ temp1_reg = 0;
+ temp2_reg = 20 - temp1_reg; /* SoftmMute range = 20 dB */
+ if (0 > temp2_reg)
+ temp2_reg = 0; /* if beyond that range,
+ * then clamp to 0 */
+ /* Form 100*10^((temp2_reg-20)/20) approximation (antilog)
+ * over range [0,20] dB
+ * approximation in range [0,100], but we only want to
+ * softmute down to -20 dB, no further */
+ if (16 < temp2_reg)
+ temp3_reg = 9 * temp2_reg - 80;
+ else if (12 < temp2_reg)
+ temp3_reg = 6 * temp2_reg - 33;
+ else if (8 < temp2_reg)
+ temp3_reg = 4 * temp2_reg - 8;
+ else
+ temp3_reg = 2 * temp2_reg + 9;
+
+ sdf->LprGa = 328 * temp3_reg; /* close to 32767*(1/100) */
+
+ if (32767 < sdf->LprGa)
+ sdf->LprGa = 32767; /* Clamp to '1' */
+
+ if (3277 > sdf->LprGa)
+ sdf->LprGa = 3277; /* Clamp to 0.1*32767 =
+ * -20 dB min gain */
+
+ /* *************** Bandwidth Sweep Algorithm ************ */
+ /* *** Calculate 2nd order filter coefficients as function
+ * of desired BW. We do this by constructing piece-wise
+ * linear filter coef's as f(BW), which is why we break the
+ * calc's into different BW regions below.
+ * coef(BW) = S*(M*BW + B)
+ * For more info, see sw_audio/ws_filter.m checked into CVS */
+ if (0 == sdf->pCoefBypassBwCtl) { /* if ==1, then we just go
+ * with default coef set */
+ /* determine if we run thru loop once or twice... */
+ if (1 == sdf->pCoefForceLockLmrBw)
+ temp4_reg = 1; /* run thru once only to calc.
+ * LPR coef's */
+ else
+ temp4_reg = 2; /* run thru twice to calc.
+ * LPR and LMR coef's */
+
+ /* Here's the big coef. calc. loop */
+ for (temp1_reg = 0; temp1_reg < temp4_reg;
+ temp1_reg++) {
+
+ if (0 == temp1_reg)
+ temp2_reg = (s16) sdf->LprBw;
+ else
+ temp2_reg = (s16) sdf->LmrBw;
+
+
+ if (6000 > temp2_reg) {
+ /* interval = [4.4kHz, 6.0kHz) */
+ sdf->B0M = 22102;
+ sdf->B0B = -2209;
+ sdf->B0S = 1;
+
+ sdf->B1over2M = 22089;
+ sdf->B1over2B = -2205;
+ sdf->B1over2S = 1;
+
+ sdf->A1over2M = 31646;
+ sdf->A1over2B = -15695;
+ sdf->A1over2S = 2;
+
+ sdf->A2M = -24664;
+ sdf->A2B = 11698;
+ sdf->A2S = 2;
+ } else if (8000 > temp2_reg) {
+ /* interval = [6.0kHz, 8.0kHz) */
+ sdf->B0M = 22102;
+ sdf->B0B = -2209;
+ sdf->B0S = 1;
+
+ sdf->B1over2M = 22089;
+ sdf->B1over2B = -2205;
+ sdf->B1over2S = 1;
+
+ sdf->A1over2M = 31646;
+ sdf->A1over2B = -15695;
+ sdf->A1over2S = 2;
+
+ sdf->A2M = -31231;
+ sdf->A2B = 18468;
+ sdf->A2S = 1;
+ } else if (10000 > temp2_reg) {
+ /* interval = [8.0kHz, 10.0kHz) */
+ sdf->B0M = 28433;
+ sdf->B0B = -4506;
+ sdf->B0S = 1;
+
+ sdf->B1over2M = 28462;
+ sdf->B1over2B = -4584;
+ sdf->B1over2S = 1;
+
+ sdf->A1over2M = 31646;
+ sdf->A1over2B = -15695;
+ sdf->A1over2S = 2;
+
+ sdf->A2M = -14811;
+ sdf->A2B = 12511;
+ sdf->A2S = 1;
+ } else if (12000 > temp2_reg) {
+ /* interval = [10.0kHz, 12.0kHz) */
+ sdf->B0M = 28433;
+ sdf->B0B = -4506;
+ sdf->B0S = 1;
+
+ sdf->B1over2M = 28462;
+ sdf->B1over2B = -4584;
+ sdf->B1over2S = 1;
+
+ sdf->A1over2M = 31646;
+ sdf->A1over2B = -15695;
+ sdf->A1over2S = 2;
+
+ sdf->A2M = -181;
+ sdf->A2B = 5875;
+ sdf->A2S = 1;
+ } else if (14000 > temp2_reg) {
+ /* interval = [12.0kHz, 14.0kHz) */
+ sdf->B0M = 18291;
+ sdf->B0B = -4470;
+ sdf->B0S = 2;
+
+ sdf->B1over2M = 18461;
+ sdf->B1over2B = -4597;
+ sdf->B1over2S = 2;
+
+ sdf->A1over2M = 31646;
+ sdf->A1over2B = -15695;
+ sdf->A1over2S = 2;
+
+ sdf->A2M = 14379;
+ sdf->A2B = -2068;
+ sdf->A2S = 1;
+ } else if (16000 > temp2_reg) {
+ /* interval = [14.0kHz, 16.0kHz) */
+ sdf->B0M = 18291;
+ sdf->B0B = -4470;
+ sdf->B0S = 2;
+
+ sdf->B1over2M = 18461;
+ sdf->B1over2B = -4597;
+ sdf->B1over2S = 2;
+
+ sdf->A1over2M = 31646;
+ sdf->A1over2B = -15695;
+ sdf->A1over2S = 2;
+
+ sdf->A2M = 30815;
+ sdf->A2B = -12481;
+ sdf->A2S = 1;
+ } else if (18000 > temp2_reg) {
+ /* interval = [16.0kHz, 18.0kHz) */
+ sdf->B0M = 24740;
+ sdf->B0B = -9152;
+ sdf->B0S = 2;
+
+ sdf->B1over2M = 24730;
+ sdf->B1over2B = -9142;
+ sdf->B1over2S = 2;
+
+ sdf->A1over2M = 31646;
+ sdf->A1over2B = -15695;
+ sdf->A1over2S = 2;
+
+ sdf->A2M = 25631;
+ sdf->A2B = -13661;
+ sdf->A2S = 2;
+ } else {
+ /* interval = [18.0kHz, 19.845kHz) */
+ sdf->B0M = 24740;
+ sdf->B0B = -9152;
+ sdf->B0S = 2;
+
+ sdf->B1over2M = 24730;
+ sdf->B1over2B = -9142;
+ sdf->B1over2S = 2;
+
+ sdf->A1over2M = 31646;
+ sdf->A1over2B = -15695;
+ sdf->A1over2S = 2;
+
+ sdf->A2M = 19382;
+ sdf->A2B = -12183;
+ sdf->A2S = 4;
+ }
+
+ if (0 == temp1_reg) {
+ /* The piece-wise linear eq's are
+ * based on a scaled version
+ * (32768/22050) of BW */
+
+ /* Note 32768/22050 <-> 2*(16384/22050)
+ * <-> 2*((16384/22050)*32768)>>15 */
+ sdf->AdjBw = ((temp2_reg << 1) *
+ 24348) >> 15;
+
+ /* temp = mx */
+ temp3_reg = (sdf->B0M *
+ sdf->AdjBw) >> 15;
+
+ /* y = S*(mx + b) */
+ sdf->LprB0 = sdf->B0S *
+ (temp3_reg + sdf->B0B);
+
+ /* temp = mx */
+ temp3_reg = (sdf->B1over2M *
+ sdf->AdjBw) >> 15;
+
+ /* y = S*(mx + b) */
+ sdf->LprB1over2 = sdf->B1over2S *
+ (temp3_reg + sdf->B1over2B);
+
+ /* temp = mx */
+ temp3_reg = (sdf->A1over2M *
+ sdf->AdjBw) >> 15;
+
+ /* y = S*(mx + b) */
+ sdf->LprA1over2 = -sdf->A1over2S *
+ (temp3_reg + sdf->A1over2B);
+
+ /* temp = mx */
+ temp3_reg = (sdf->A2M *
+ sdf->AdjBw) >> 15;
+
+ /* y = S*(mx + b) */
+ sdf->LprA2 = -sdf->A2S *
+ (temp3_reg + sdf->A2B);
+ /* *** end LPR channel --
+ * LPR coefficients now ready for
+ * Stereo Path next time */
+ } else {
+ /* The piece-wise linear eq's are
+ * based on a scaled version
+ * (32768/22050) of BW */
+
+ /* Note 32768/22050 <-> 2*(16384/22050)
+ * <-> 2*((16384/22050)*32768)>>15 */
+ sdf->AdjBw = ((temp2_reg << 1) *
+ 24348) >> 15;
+
+ /* temp = mx */
+ temp3_reg = (sdf->B0M *
+ sdf->AdjBw) >> 15;
+
+ /* y = S*(mx + b) */
+ sdf->LmrB0 = sdf->B0S *
+ (temp3_reg + sdf->B0B);
+
+ /* temp = mx */
+ temp3_reg = (sdf->B1over2M *
+ sdf->AdjBw) >> 15;
+
+ /* y = S*(mx + b) */
+ sdf->LmrB1over2 = sdf->B1over2S *
+ (temp3_reg + sdf->B1over2B);
+
+ /* temp = mx */
+ temp3_reg = (sdf->A1over2M *
+ sdf->AdjBw) >> 15;
+
+ /* y = S*(mx + b) */
+ sdf->LmrA1over2 = -sdf->A1over2S *
+ (temp3_reg + sdf->A1over2B);
+
+ /* temp = mx */
+ temp3_reg = (sdf->A2M *
+ sdf->AdjBw) >> 15;
+
+ /* y = S*(mx + b) */
+ sdf->LmrA2 = -sdf->A2S *
+ (temp3_reg + sdf->A2B);
+ /* *** end LMR channel -- LMR
+ * coefficients now ready for Stereo
+ * Path next time */
+ }
+ } /* end for (temp1_reg=0... */
+ if (1 == sdf->pCoefForceLockLmrBw) {
+ /* if Force Lock LMR BW = LPR BW */
+ /* then set LMR coef's = LPR coef's */
+ sdf->LmrB0 = sdf->LprB0;
+ sdf->LmrB1over2 = sdf->LprB1over2;
+ sdf->LmrA1over2 = sdf->LprA1over2;
+ sdf->LmrA2 = sdf->LprA2;
+ }
+
+ } /* end if (0 == sdf->pCoef_BypassBwCtl) */
+ /* eI 108 24th Feb 06 Streo Matrix part moved after
+ * weak signal processing. */
+ if (0 == sdf->pCoefBypassBlend)
+ temp1_reg = sdf->LmrGa; /* Blend */
+ else
+ temp1_reg = 1;
+
+ if (sdf->pCoefForcedMono) /* Forced Mono */
+ temp1_reg = 0;
+
+ if (0 == sdf->pCoefBypassSoftmute) {
+
+ /* SoftMute applied to LPR */
+ sdf->temp2_reg_sm = sdf->LprGa;
+
+ temp2_reg_32 = sdf->LprGa * temp1_reg;
+
+ /* SoftMute applied to LMR */
+ sdf->temp3_reg_sm = (temp2_reg_32) >> 15;
+ } else {
+ sdf->temp2_reg_sm = 1; /* eI 108 24th Feb 06 update
+ * global variable for IIR
+ * filter. */
+ sdf->temp3_reg_sm = temp1_reg;
+ }
+
+ } /* end if (0 == sdf->DecRssi) */
+
+ sdf->DecRssi = ((sdf->DecRssi + 1) % 16); /* end decimation
+ * by 16 */
+
+ /* *** End Weak Signal Processing ********************************** */
+ /* ***************************************************************** */
+}
diff --git a/drivers/media/radio/stfm1000/stfm1000-filter.h b/drivers/media/radio/stfm1000/stfm1000-filter.h
new file mode 100644
index 000000000000..d24e3e9244b8
--- /dev/null
+++ b/drivers/media/radio/stfm1000/stfm1000-filter.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 STFM1000_FILTER_H
+#define STFM1000_FILTER_H
+
+/* STFM1000 Black Box Filter parameters */
+struct stfm1000_filter_parms {
+ s16 LprXzz; /* LPR x(n-2) stereo filter */
+ s16 LmrXzz; /* LMR x(n-2) stereo filter */
+ s16 LprYzz; /* LPR y(n-2) stereo filter */
+ s16 LmrYzz; /* LMR y(n-2) stereo filter */
+
+ s16 LprXz; /* LPR x(n-1) stereo filter */
+ s16 LmrXz; /* LMR x(n-1) stereo filter */
+ s16 FilteredLpr; /* LPR filter output */
+ s16 FilteredLmr; /* LMR filter output */
+
+ s16 LprB0; /* LPR stereo filter coef */
+ s16 LprB1over2; /* LPR stereo filter coef */
+ s16 LprA1over2; /* LPR stereo filter coef */
+ s16 LprA2; /* LPR stereo filter coef */
+
+ s16 LmrB0; /* LMR stereo filter coef */
+ s16 LmrB1over2; /* LMR stereo filter coef */
+ s16 LmrA1over2; /* LMR stereo filter coef */
+ s16 LmrA2; /* LMR stereo filter coef */
+
+ s16 LprYz; /* LPR y(n-1) stereo filter */
+ s16 LmrYz; /* LMR y(n-1) stereo filter */
+
+ s16 Left; /* left channel audio out */
+ s16 Right; /* right channel audio out */
+ s32 LeftLb; /* left channel dc estimate */
+ s32 RightLb; /* right channel dc estimate */
+
+ u32 RssiDecoded; /* integer decoded RSSI */
+
+ u16 RssiMant; /* mantissa of float-coded RSSI */
+ s16 RssiLog; /* 10log10(decoded RSSI) */
+
+ u16 RssiExp; /* exponent of float-coded RSSI */
+ u16 RssiLb; /* leaky bucket dc of rssi */
+
+ u16 Prssi; /* power of 2 for RSSI */
+ u16 TrueRssi; /* DC estimate of log RSSI */
+
+ u16 ScaledRssiDecoded; /* scaled log RSSI */
+ s16 Echo; /* Echo info from HiPass(RSSI) */
+ u16 ScaledRssiDecodedZ; /* history buffer for above */
+ u16 ScaledRssiDecodedZz;/* ditto */
+
+ u16 ScaledTrueRssi; /* scaled version for precision */
+ u16 FilteredRssi; /* Attack/Decay filtered RSSI */
+ u16 PrevFilteredRssi; /* previous version of above */
+
+ u16 EchoLb; /* DC estimate of Echo energy */
+ u16 TrueEcho; /* scaled version of above */
+ u16 FilteredEchoLpr; /* Attack/Decay filt. Echo */
+ u16 PrevFilteredEchoLpr;/* previous version of above */
+ u16 FilteredEchoLmr; /* Attack/Decay filt. Echo */
+ u16 PrevFilteredEchoLmr;/* previous version of above */
+ s16 GatedEcho; /* Echo gated by threshold */
+
+ s16 ControlLpr; /* master control for LPR */
+ s16 ControlLmr; /* master control for LMR */
+ u16 LprBw; /* LPR Bandwidth desired */
+ u16 LmrBw; /* LMR Bandwidth desired */
+ u16 LprGa; /* LPR Gain (SoftMute) desired */
+ u16 LmrGa; /* LMR Gain (Blend) desired */
+ u16 ScaledControlLmr; /* Scaled down version Ctl LMR */
+ u16 ScaledControlLpr; /* Scaled down version Ctl LPR */
+
+ s16 B0M; /* BW ctl B0 coef slope */
+ s16 B0B; /* BW ctl B0 coef y-intercept */
+
+ u16 B0S; /* BW ctl B0 coef scale */
+ s16 B1over2M; /* BW ctl B1/2 coef slope */
+
+ s16 B1over2B; /* BW ctl B1/2 coef y-intercept */
+ s16 A1over2B; /* BW ctl A1/2 coef y-intercept */
+
+ u16 B1over2S; /* BW ctl B1/2 coef scale */
+ u16 A1over2S; /* BW ctl A1/2 coef scale */
+
+ s16 A1over2M; /* BW ctl A1/2 coef slope */
+ u16 A2S; /* BW ctl A2 coef scale */
+
+ s16 A2M; /* BW ctl A2 coef slope */
+ s16 A2B; /* BW ctl A2 coef y-intercept */
+
+ u16 AdjBw; /* Desired Filter BW scaled into range */
+
+ u16 DecRssi; /*! Decimation modulo counter */
+
+ s16 ScaleAudio; /*! Scale factor for Audio Mute */
+ u8 MuteAudio; /*! Control for muting audio */
+ u8 PrevMuteAudio; /*! History of control for muting audio */
+ u8 MuteActionFlag; /*! Indicator of when mute ramping occurs */
+
+ u32 Acc; /* mimics H/W accumulator */
+ s32 Acc_signed;
+ s16 temp1_reg; /* mimics 16 bit register */
+ s16 temp2_reg; /* mimics 16 bit register */
+ s16 temp3_reg; /* mimics 16 bit register */
+ s16 temp4_reg; /* mimics 16 bit register */
+ s16 temp5_reg; /* mimics 16 bit register */
+
+ /* *** Programmable Coefficients */
+ u16 pCoefRssiAttack; /* prog coef RSSI attack */
+ u16 pCoefRssiDecay; /* prog coef RSSI decay */
+ u16 pCoefEchoLprAttack; /* prog coef Echo LPR attack */
+ u16 pCoefEchoLprDecay; /* prog coef Echo LPR decay */
+ u16 pCoefEchoLmrAttack; /* prog coef Echo LMR attack */
+ u16 pCoefEchoLmrDecay; /* prog coef Echo LMR decay */
+
+ u16 pCoefEchoTh; /* prog coef Echo threshold */
+
+ u16 pCoefEchoScLpr; /* prog coef scale Echo LPR infl. */
+ u16 pCoefEchoScLmr; /* prog coef scale Echo LMR infl. */
+ u16 pCoefEchoShLpr; /* prog coef shift Echo LPR infl. */
+ u16 pCoefEchoShLmr; /* prog coef shift Echo LMR infl. */
+
+ u16 pCoefLprBwThLo; /* prog coef Low Th LPR BW */
+ u16 pCoefLprBwThHi; /* prog coef High Th LPR BW */
+ u16 pCoefLmrBwThLo; /* prog coef Low Th LMR BW */
+ u16 pCoefLmrBwThHi; /* prog coef High Th LMR BW */
+
+ u16 pCoefLprGaTh; /* prog coef Th LPR Gain (SoftMute) */
+ u16 pCoefLmrGaTh; /* prog coef Th LMR Gain (Blend) */
+
+ u16 pCoefLprBwSlSc; /* prog coef Slope scale LPR BW */
+ u16 pCoefLprBwSlSh; /* prog coef Slope shift LPR BW */
+ u16 pCoefLmrBwSlSc; /* prog coef Slope scale LMR BW */
+ u16 pCoefLmrBwSlSh; /* prog coef Slope shift LMR BW */
+ u16 pCoefLprGaSlSc; /* prog coef Slope scale LPR Gain */
+ u16 pCoefLprGaSlSh; /* prog coef Slope shift LPR Gain */
+
+ u8 pCoefForcedMono; /* Forced Mono control bit */
+ u8 pCoefBypassBlend; /* Forced bypass of stereo blend */
+ u8 pCoefBypassSoftmute; /* Forced bypass of softmute */
+ u8 pCoefBypassDcCut; /* Forced bypass of audio DC Cut filter */
+
+ u8 pCoefBypassBwCtl; /* Forced bypass of bandwidth control */
+ u8 pCoefForceLockLmrBw; /* prog flag to force LMR BW=LPR BW */
+
+ /* XXX added here, they were global */
+ s16 temp2_reg_sm;
+ s16 temp3_reg_sm;
+
+};
+
+/* STFM1000 Black Box Filter Function prototypes */
+void stfm1000_filter_reset(struct stfm1000_filter_parms *sdf);
+void stfm1000_filter_decode(struct stfm1000_filter_parms *sdf, s16 Lpr,
+ s16 Lmr, u16 Rssi);
+
+static inline s16
+stfm1000_filter_value_left(struct stfm1000_filter_parms *sdf)
+{
+ return sdf->Left;
+}
+
+static inline s16
+stfm1000_filter_value_right(struct stfm1000_filter_parms *sdf)
+{
+ return sdf->Right;
+}
+
+static inline u32
+stfm1000_filter_value_rssi(struct stfm1000_filter_parms *sdf)
+{
+ return sdf->RssiDecoded;
+}
+
+#endif
diff --git a/drivers/media/radio/stfm1000/stfm1000-i2c.c b/drivers/media/radio/stfm1000/stfm1000-i2c.c
new file mode 100644
index 000000000000..50a047ecfda6
--- /dev/null
+++ b/drivers/media/radio/stfm1000/stfm1000-i2c.c
@@ -0,0 +1,453 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+
+#include "stfm1000.h"
+
+#define stfm1000_i2c_debug(p, fmt, arg...) \
+ do { \
+ if ((p)->dbgflg & STFM1000_DBGFLG_I2C) \
+ printk(KERN_INFO "stfm1000: " fmt, ##arg); \
+ } while (0)
+
+static const char *reg_names[STFM1000_NUM_REGS] = {
+#undef REGNAME
+#define REGNAME(x) \
+ [STFM1000_ ## x / 4] = #x ""
+
+ REGNAME(TUNE1),
+ REGNAME(SDNOMINAL),
+ REGNAME(PILOTTRACKING),
+ REGNAME(INITIALIZATION1),
+ REGNAME(INITIALIZATION2),
+ REGNAME(INITIALIZATION3),
+ REGNAME(INITIALIZATION4),
+ REGNAME(INITIALIZATION5),
+ REGNAME(INITIALIZATION6),
+ REGNAME(REF),
+ REGNAME(LNA),
+ REGNAME(MIXFILT),
+ REGNAME(CLK1),
+ REGNAME(CLK2),
+ REGNAME(ADC),
+ REGNAME(AGC_CONTROL1),
+ REGNAME(AGC_CONTROL2),
+ REGNAME(DATAPATH),
+ REGNAME(RMS),
+ REGNAME(AGC_STAT),
+ REGNAME(SIGNALQUALITY),
+ REGNAME(DCEST),
+ REGNAME(RSSI_TONE),
+ REGNAME(PILOTCORRECTION),
+ REGNAME(ATTENTION),
+ REGNAME(CLK3),
+ REGNAME(CHIPID),
+#undef REGNAME
+};
+
+static const int stfm1000_rw_regs[] = {
+ STFM1000_TUNE1,
+ STFM1000_SDNOMINAL,
+ STFM1000_PILOTTRACKING,
+ STFM1000_INITIALIZATION1,
+ STFM1000_INITIALIZATION2,
+ STFM1000_INITIALIZATION3,
+ STFM1000_INITIALIZATION4,
+ STFM1000_INITIALIZATION5,
+ STFM1000_INITIALIZATION6,
+ STFM1000_REF,
+ STFM1000_LNA,
+ STFM1000_MIXFILT,
+ STFM1000_CLK1,
+ STFM1000_CLK2,
+ STFM1000_ADC,
+ STFM1000_AGC_CONTROL1,
+ STFM1000_AGC_CONTROL2,
+ STFM1000_DATAPATH,
+ STFM1000_ATTENTION, /* it's both WR/RD */
+};
+
+static const int stfm1000_ra_regs[] = {
+ STFM1000_RMS,
+ STFM1000_AGC_STAT,
+ STFM1000_SIGNALQUALITY,
+ STFM1000_DCEST,
+ STFM1000_RSSI_TONE,
+ STFM1000_PILOTCORRECTION,
+ STFM1000_ATTENTION, /* it's both WR/RD - always read */
+ STFM1000_CLK3,
+ STFM1000_CHIPID
+};
+
+static int verify_writes;
+
+void stfm1000_setup_reg_set(struct stfm1000 *stfm1000)
+{
+ int i, reg;
+
+ /* set up register sets (read/write) */
+ for (i = 0; i < ARRAY_SIZE(stfm1000_rw_regs); i++) {
+ reg = stfm1000_rw_regs[i] / 4;
+ stfm1000->reg_rw_set[reg / 32] |= 1U << (reg & 31);
+ /* printk(KERN_INFO "STFM1000: rw <= %d\n", reg); */
+ }
+
+ /* for (i = 0; i < ARRAY_SIZE(stfm1000->reg_rw_set); i++)
+ printk("RW[%d] = 0x%08x\n", i, stfm1000->reg_rw_set[i]); */
+
+ /* set up register sets (read only) */
+ for (i = 0; i < ARRAY_SIZE(stfm1000_ra_regs); i++) {
+ reg = stfm1000_ra_regs[i] / 4;
+ stfm1000->reg_ra_set[reg / 32] |= 1U << (reg & 31);
+ /* printk(KERN_INFO "STFM1000: rw <= %d\n", reg); */
+ }
+ /* for (i = 0; i < ARRAY_SIZE(stfm1000->reg_ra_set); i++)
+ printk("RO[%d] = 0x%08x\n", i, stfm1000->reg_ra_set[i]); */
+
+ /* clear dirty */
+ memset(stfm1000->reg_dirty_set, 0, sizeof(stfm1000->reg_dirty_set));
+}
+
+static int stfm1000_reg_is_rw(struct stfm1000 *stfm1000, int reg)
+{
+ reg >>= 2;
+ return !!(stfm1000->reg_rw_set[reg / 32] & (1 << (reg & 31)));
+}
+
+static int stfm1000_reg_is_ra(struct stfm1000 *stfm1000, int reg)
+{
+ reg >>= 2;
+ return !!(stfm1000->reg_ra_set[reg / 32] & (1 << (reg & 31)));
+}
+
+static int stfm1000_reg_is_dirty(struct stfm1000 *stfm1000, int reg)
+{
+ reg >>= 2;
+ return !!(stfm1000->reg_dirty_set[reg / 32] & (1 << (reg & 31)));
+}
+
+static void stfm1000_reg_set_dirty(struct stfm1000 *stfm1000, int reg)
+{
+ reg >>= 2;
+ stfm1000->reg_dirty_set[reg / 32] |= 1 << (reg & 31);
+}
+
+static inline int stfm1000_reg_is_writeable(struct stfm1000 *stfm1000, int reg)
+{
+ return stfm1000_reg_is_rw(stfm1000, reg);
+}
+
+static inline int stfm1000_reg_is_readable(struct stfm1000 *stfm1000, int reg)
+{
+ return stfm1000_reg_is_rw(stfm1000, reg) ||
+ stfm1000_reg_is_ra(stfm1000, reg);
+}
+
+/********************************************************/
+
+static int write_reg_internal(struct stfm1000 *stfm1000, int reg, u32 value)
+{
+ u8 values[5];
+ int ret;
+
+ stfm1000_i2c_debug(stfm1000, "%s(%s - 0x%02x, 0x%08x)\n", __func__,
+ reg_names[reg / 4], reg, value);
+
+ values[0] = (u8)reg;
+ values[1] = (u8)value;
+ values[2] = (u8)(value >> 8);
+ values[3] = (u8)(value >> 16);
+ values[4] = (u8)(value >> 24);
+ ret = i2c_master_send(stfm1000->client, values, 5);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int read_reg_internal(struct stfm1000 *stfm1000, int reg, u32 *value)
+{
+ u8 regb = reg;
+ u8 values[4];
+ int ret;
+
+ ret = i2c_master_send(stfm1000->client, &regb, 1);
+ if (ret < 0)
+ goto out;
+ ret = i2c_master_recv(stfm1000->client, values, 4);
+ if (ret < 0)
+ goto out;
+ *value = (u32)values[0] | ((u32)values[1] << 8) |
+ ((u32)values[2] << 16) | ((u32)values[3] << 24);
+ ret = 0;
+
+ stfm1000_i2c_debug(stfm1000, "%s(%s - 0x%02x, 0x%08x)\n", __func__,
+ reg_names[reg / 4], reg, *value);
+out:
+ return ret;
+}
+
+int stfm1000_raw_write(struct stfm1000 *stfm1000, int reg, u32 value)
+{
+ int ret;
+
+ mutex_lock(&stfm1000->xfer_lock);
+ ret = write_reg_internal(stfm1000, reg, value);
+ mutex_unlock(&stfm1000->xfer_lock);
+
+ if (ret < 0)
+ dev_err(&stfm1000->client->dev, "%s: failed", __func__);
+
+ return ret;
+}
+
+int stfm1000_raw_read(struct stfm1000 *stfm1000, int reg, u32 *value)
+{
+ int ret;
+
+ mutex_lock(&stfm1000->xfer_lock);
+ ret = read_reg_internal(stfm1000, reg, value);
+ mutex_unlock(&stfm1000->xfer_lock);
+
+ if (ret < 0)
+ dev_err(&stfm1000->client->dev, "%s: failed", __func__);
+
+ return ret;
+}
+
+static inline void stfm1000_set_shadow_reg(struct stfm1000 *stfm1000,
+ int reg, u32 val)
+{
+ stfm1000->shadow_regs[reg / 4] = val;
+}
+
+static inline u32 stfm1000_get_shadow_reg(struct stfm1000 *stfm1000, int reg)
+{
+ return stfm1000->shadow_regs[reg / 4];
+}
+
+int stfm1000_write(struct stfm1000 *stfm1000, int reg, u32 value)
+{
+ int ret;
+
+ if (!stfm1000_reg_is_writeable(stfm1000, reg)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mutex_lock(&stfm1000->xfer_lock);
+
+ /* same value as last one written? */
+ if (stfm1000_reg_is_dirty(stfm1000, reg) &&
+ stfm1000_get_shadow_reg(stfm1000, reg) == value) {
+ ret = 0;
+
+ stfm1000_i2c_debug(stfm1000, "%s - HIT "
+ "(%s - 0x%02x, 0x%08x)\n", __func__,
+ reg_names[reg / 4], reg, value);
+
+ goto out_unlock;
+ }
+
+ /* actually write the register */
+ ret = write_reg_internal(stfm1000, reg, value);
+ if (ret < 0)
+ goto out_unlock;
+
+ /* update shadow register & mark it as dirty */
+ /* only if register is not read always */
+ if (!stfm1000_reg_is_ra(stfm1000, reg)) {
+ stfm1000_set_shadow_reg(stfm1000, reg, value);
+ stfm1000_reg_set_dirty(stfm1000, reg);
+ }
+
+out_unlock:
+ mutex_unlock(&stfm1000->xfer_lock);
+
+out:
+ if (ret < 0)
+ dev_err(&stfm1000->client->dev, "%s: failed", __func__);
+
+ if (verify_writes) {
+ u32 value2 = ~0;
+
+ stfm1000_raw_read(stfm1000, reg, &value2);
+
+ stfm1000_i2c_debug(stfm1000, "%s - VER "
+ "(%s - 0x%02x, W=0x%08x V=0x%08x) %s\n", __func__,
+ reg_names[reg / 4], reg, value, value2,
+ value == value2 ? "OK" : "** differs **");
+ }
+
+ return ret;
+}
+
+int stfm1000_read(struct stfm1000 *stfm1000, int reg, u32 *value)
+{
+ int ret = 0;
+
+ if (!stfm1000_reg_is_readable(stfm1000, reg)) {
+ ret = -EINVAL;
+ printk(KERN_INFO "%s: !readable(%d)\n", __func__, reg);
+ goto out;
+ }
+
+ mutex_lock(&stfm1000->xfer_lock);
+
+ /* if the register can be written & is dirty, use the shadow */
+ if (stfm1000_reg_is_writeable(stfm1000, reg) &&
+ stfm1000_reg_is_dirty(stfm1000, reg)) {
+
+ *value = stfm1000_get_shadow_reg(stfm1000, reg);
+ ret = 0;
+
+ stfm1000_i2c_debug(stfm1000, "%s - HIT "
+ "(%s - 0x%02x, 0x%08x)\n", __func__,
+ reg_names[reg / 4], reg, *value);
+
+ goto out_unlock;
+ }
+
+ /* register must be read */
+ ret = read_reg_internal(stfm1000, reg, value);
+ if (ret < 0)
+ goto out;
+
+ /* if the register is writeable, update shadow */
+ if (stfm1000_reg_is_writeable(stfm1000, reg)) {
+ stfm1000_set_shadow_reg(stfm1000, reg, *value);
+ stfm1000_reg_set_dirty(stfm1000, reg);
+ }
+
+out_unlock:
+ mutex_unlock(&stfm1000->xfer_lock);
+
+out:
+ if (ret < 0)
+ dev_err(&stfm1000->client->dev, "%s: failed", __func__);
+
+ return ret;
+}
+
+int stfm1000_write_masked(struct stfm1000 *stfm1000, int reg, u32 value,
+ u32 mask)
+{
+ int ret = 0;
+ u32 old_value;
+
+ if (!stfm1000_reg_is_writeable(stfm1000, reg)) {
+ ret = -EINVAL;
+ printk(KERN_ERR "%s: !writeable(%d)\n", __func__, reg);
+ goto out;
+ }
+
+ mutex_lock(&stfm1000->xfer_lock);
+
+ /* if the register wasn't written before, read it */
+ if (!stfm1000_reg_is_dirty(stfm1000, reg)) {
+ ret = read_reg_internal(stfm1000, reg, &old_value);
+ if (ret != 0)
+ goto out_unlock;
+ } else /* register was written, use the last value */
+ old_value = stfm1000_get_shadow_reg(stfm1000, reg);
+
+ /* perform masking */
+ value = (old_value & ~mask) | (value & mask);
+
+ /* if we write the same value, don't bother */
+ if (stfm1000_reg_is_dirty(stfm1000, reg) && value == old_value) {
+ ret = 0;
+
+ stfm1000_i2c_debug(stfm1000, "%s - HIT "
+ "(%s - 0x%02x, 0x%08x)\n", __func__,
+ reg_names[reg / 4], reg, value);
+
+ goto out_unlock;
+ }
+
+ /* actually write the register to the chip */
+ ret = write_reg_internal(stfm1000, reg, value);
+ if (ret < 0)
+ goto out_unlock;
+
+ /* if no error, update the shadow register and mark it as dirty */
+ stfm1000_set_shadow_reg(stfm1000, reg, value);
+ stfm1000_reg_set_dirty(stfm1000, reg);
+
+out_unlock:
+ mutex_unlock(&stfm1000->xfer_lock);
+
+out:
+ if (ret < 0)
+ dev_err(&stfm1000->client->dev, "%s: failed", __func__);
+
+ if (verify_writes) {
+ u32 value2 = ~0;
+
+ stfm1000_raw_read(stfm1000, reg, &value2);
+
+ stfm1000_i2c_debug(stfm1000, "%s - VER "
+ "(%s - 0x%02x, W=0x%08x V=0x%08x) %s\n", __func__,
+ reg_names[reg / 4], reg, value, value2,
+ value == value2 ? "OK" : "** differs **");
+ }
+
+ return ret;
+}
+
+int stfm1000_set_bits(struct stfm1000 *stfm1000, int reg, u32 value)
+{
+ return stfm1000_write_masked(stfm1000, reg, value, value);
+}
+
+int stfm1000_clear_bits(struct stfm1000 *stfm1000, int reg, u32 value)
+{
+ return stfm1000_write_masked(stfm1000, reg, ~value, value);
+}
+
+int stfm1000_write_regs(struct stfm1000 *stfm1000,
+ const struct stfm1000_reg *reg)
+{
+ int ret;
+
+ for (; reg && reg->regno != STFM1000_REG_END; reg++) {
+
+ if (reg->regno == STFM1000_REG_DELAY) {
+ msleep(reg->value);
+ continue;
+ }
+
+ if (reg->regno & STFM1000_REG_SET_BITS_MASK)
+ ret = stfm1000_set_bits(stfm1000, reg->regno & 0xff,
+ reg->value);
+ else if (reg->regno & STFM1000_REG_CLEAR_BITS_MASK)
+ ret = stfm1000_clear_bits(stfm1000, reg->regno & 0xff,
+ reg->value);
+ else
+ ret = stfm1000_write(stfm1000, reg->regno, reg->value);
+
+ if (ret != 0) {
+ printk(KERN_ERR "%s: failed to write reg 0x%x "
+ "with 0x%08x\n",
+ __func__, reg->regno, reg->value);
+ return ret;
+ }
+ }
+ return 0;
+}
+
diff --git a/drivers/media/radio/stfm1000/stfm1000-rds.c b/drivers/media/radio/stfm1000/stfm1000-rds.c
new file mode 100644
index 000000000000..5f63052d00c2
--- /dev/null
+++ b/drivers/media/radio/stfm1000/stfm1000-rds.c
@@ -0,0 +1,1529 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+
+#include "stfm1000.h"
+
+#include "stfm1000-rds.h"
+
+#define bitstream_to_rds_state(b) \
+ container_of(b, struct stfm1000_rds_state, bitstream)
+#define demod_to_rds_state(d) \
+ container_of(d, struct stfm1000_rds_state, demod)
+#define pkt_to_rds_state(p) \
+ container_of(p, struct stfm1000_rds_state, pkt)
+#define text_to_rds_state(t) \
+ container_of(t, struct stfm1000_rds_state, text)
+#define rds_state_to_stfm1000(r) \
+ container_of(r, struct stfm1000, rds_state)
+
+#define TADJSH 8 /*Shifts used in bitslice loop filter */
+
+/* Reverse of Matlab's Fquant (see MatchedFilterDecomposition.m), so that */
+/* mixandsum code is easy; Used by rds_bitstream_stfmdemod.arm */
+const s16 u16_rds_basis[2*RDS_BASISLENGTH+8] = {
+ 14, 24, 34, 43, 50, 56, 60, 62, 62,
+ 60, 55, 49, 41, 32, 22, 11, 14, 24,
+ 34, 43, 50, 56, 60, 62, 62, 60, 55,
+ 49, 41, 32, 22, 11, 14, 24, 34, 43,
+ 50, 56, 60, 62
+};
+
+static int bits_free(struct stfm1000_rds_bitstream *rdsb)
+{
+ /* Do not show the last one word free. */
+ int FreeSpace = rdsb->TailBitCount - rdsb->HeadBitCount - 32;
+
+ if (FreeSpace < 0)
+ FreeSpace = (RDS_BITBUFSIZE * 32) + FreeSpace;
+ return FreeSpace;
+}
+
+static void put1bit(struct stfm1000_rds_bitstream *rdsb, int bit)
+{
+ int index = (rdsb->HeadBitCount >> 5);
+ u32 CurBit = (rdsb->HeadBitCount & 0x1f);
+ u32 CurWord = rdsb->buf[index];
+
+ if (CurBit == 0)
+ CurWord = 0;
+
+ CurWord = CurWord | (((u32)bit & 1) << CurBit);
+ rdsb->buf[index] = CurWord;
+ rdsb->HeadBitCount++;
+ if (rdsb->HeadBitCount >= RDS_BITBUFSIZE * 32)
+ rdsb->HeadBitCount = 0;
+}
+
+static int get1bit(struct stfm1000_rds_bitstream *rdsb)
+{
+ int Bit = 0;
+ int index = (rdsb->TailBitCount >> 5);
+ int CurBit = (rdsb->TailBitCount & 0x1f);
+ u32 CurWord = rdsb->buf[index];
+
+ Bit = (CurWord >> CurBit) & 1;
+ rdsb->TailBitCount++;
+ if (rdsb->TailBitCount == RDS_BITBUFSIZE*32)
+ rdsb->TailBitCount = 0;
+
+ return Bit;
+}
+
+static int bits_filled(struct stfm1000_rds_bitstream *rdsb)
+{
+ int FilledSpace = rdsb->HeadBitCount - rdsb->TailBitCount;
+
+ if (FilledSpace < 0)
+ FilledSpace = (RDS_BITBUFSIZE * 32) + FilledSpace;
+ return FilledSpace;
+}
+
+static void rds_mix_msg(struct stfm1000_rds_demod *rdsd, u8 MixSetting)
+{
+ if (rdsd->mix_msg_pending)
+ rdsd->mix_msg_overrun++;
+ rdsd->mix_msg = MixSetting;
+ rdsd->mix_msg_pending = 1;
+
+ /* signal monitor thread */
+ stfm1000_monitor_signal(
+ rds_state_to_stfm1000(demod_to_rds_state(rdsd)),
+ EVENT_RDS_MIXFILT);
+}
+
+/* call with interrupts disabled please */
+int stfm1000_rds_mix_msg_get(struct stfm1000_rds_state *rds)
+{
+ struct stfm1000_rds_demod *rdsd = &rds->demod;
+
+ if (!rdsd->mix_msg_pending)
+ return -1;
+
+ return rdsd->mix_msg;
+}
+
+/* call with interrupts disabled please */
+int stfm1000_rds_mix_msg_processed(struct stfm1000_rds_state *rds, int mix_msg)
+{
+ struct stfm1000_rds_demod *rdsd = &rds->demod;
+
+ if (!rdsd->mix_msg_pending)
+ return -1;
+
+ rdsd->mix_msg_pending = 0;
+
+ /* update the completion indication bit */
+ if ((mix_msg & 0x8) == 0)
+ rdsd->MixPopDone = 1;
+
+ /* this is reflected off the hardware register */
+ rdsd->rds_mix_offset = mix_msg & 1;
+
+ if (rdsd->mix_msg != mix_msg) {
+ rdsd->mix_msg_processed_changed++;
+ return -1;
+ }
+ return 0;
+}
+
+static void rds_sdnominal_msg(struct stfm1000_rds_demod *rdsd, int sdnominal)
+{
+ if (rdsd->sdnominal_msg_pending)
+ rdsd->sdnominal_msg_overrun++;
+ rdsd->sdnominal_msg = sdnominal;
+ rdsd->sdnominal_msg_pending = 1;
+
+ /* signal monitor thread */
+ stfm1000_monitor_signal(
+ rds_state_to_stfm1000(demod_to_rds_state(rdsd)),
+ EVENT_RDS_SDNOMINAL);
+}
+
+/* call with interrupts disabled please */
+int stfm1000_rds_sdnominal_msg_get(struct stfm1000_rds_state *rds)
+{
+ struct stfm1000_rds_demod *rdsd = &rds->demod;
+
+ if (!rdsd->sdnominal_msg_pending)
+ return 0;
+
+ return rdsd->sdnominal_msg;
+}
+
+/* call with interrupts disabled please */
+int stfm1000_rds_sdnominal_msg_processed(struct stfm1000_rds_state *rds,
+ int sdnominal_msg)
+{
+ struct stfm1000_rds_demod *rdsd = &rds->demod;
+
+ if (!rdsd->sdnominal_msg_pending)
+ return -1;
+
+ rdsd->sdnominal_msg_pending = 0;
+ return 0;
+}
+
+void demod_loop(struct stfm1000_rds_bitstream *rdsb,
+ struct stfm1000_rds_demod *rdsd)
+{
+ s32 filter_out;
+ u32 freeSpace;
+ s32 decomp_hist_pp;
+ u8 phase;
+
+ /* Check if we're at a half-basis point */
+ if ((rdsd->i & (RDS_BASISLENGTH/2 - 1)) != 0)
+ return; /* Nope, return */
+
+ /* Yes, time to do our work */
+ /* Rotate the length 3 history buffer */
+ decomp_hist_pp = rdsd->decomp_hist_p;
+ rdsd->decomp_hist_p = rdsd->decomp_hist;
+ if ((rdsd->i & (RDS_BASISLENGTH-1)) == 0) {
+ rdsd->decomp_hist = rdsd->mixandsum1>>9; /* Grab output of
+ * mixandsum1/512 */
+ rdsd->mixandsum1 = 0; /* Reset mixandsum #1 */
+ } else {
+ rdsd->decomp_hist = rdsd->mixandsum2>>9; /*Grab output of
+ * mixandsum2/512 */
+ rdsd->mixandsum2 = 0; /* Reset mixandsum #2 */
+ }
+
+ /* Form correlator/decimator output by convolving with the
+ * decomposition coefficients, DecompQuant from Matlab work. */
+ filter_out = (-58*rdsd->decomp_hist + 59*decomp_hist_pp)>>7;
+
+ /*Figure out which half-basis we are in (out of a bit-length cycle) */
+ phase = rdsd->i*2/RDS_BASISLENGTH;
+ /*Now what we do depends on the phase variable */
+ /*Phase 0: Bitslice and do timing alignment */
+ /*others (1-3): Keep value for timing alignment */
+
+ if (phase == 0) { /*Main processing (bitslice) */
+ u32 Ph;
+ u8 OldBit = rdsd->sliced_data; /* Save the previous value */
+
+ rdsd->return_num = 1;
+ if (filter_out >= 0) { /*This bit is "1" */
+ /*return value is XOR of previous bit (still in
+ * sliced_data) w/ this */
+ /* bit (1), which equals (NOT of the previous bit) */
+ rdsd->return_rdsdemod = !OldBit;
+ rdsd->sliced_data = 1; /*Newest bit value is 1 */
+ } else { /*This bit is "0" */
+ /*return value is XOR of previous bit (still in
+ * sliced_data) w/ this */
+ /* bit (0), which equals the previous bit */
+ rdsd->return_rdsdemod = OldBit;
+ rdsd->sliced_data = 0; /*Newest bit value is 0 */
+ }
+
+ freeSpace = bits_free(rdsb);
+
+ if (freeSpace > 0)
+ put1bit(rdsb, rdsd->return_rdsdemod);
+ else
+ rdsd->RdsDemodSkippedBitCnt++;
+
+ /*Increment bits received counter */
+ rdsd->BitAlignmentCounter++;
+ /*If mixer phase determination hasn't been done, start it */
+ if ((rdsd->MixPhaseState == 0) && (!rdsd->MixPhaseDetInProg)) {
+ rdsd->MixPhaseDetInProg = 1;
+ /*Go to first mixer setting (0) */
+ rds_mix_msg(rdsd, 0);
+ }
+
+ /* Do bit-slicing time adaption after the mixer phase
+ * determination */
+ if (!(rdsd->MixPhaseDetInProg) && !(rdsd->Synchronous)) {
+
+ /* Bitslice Timing Adjust Code (runs after
+ * MixPhaseDetInProg and if RDS is not synchronous to
+ * the FM pilot. */
+
+ u8 BigPh2; /* Expecting a large value in
+ * PhaseValue[2] */
+ u32 MaxRMS = 0; /*Largest phase RMS */
+ s8 MaxPh = 0; /*Index of largest phase RMS */
+ s32 zerocross;
+
+ /* Locate the largest phase RMS
+ * (should be at phase zero) */
+ for (Ph = 0; Ph < 4; Ph++)
+ if (rdsd->Ph_RMS[Ph] > MaxRMS) {
+ MaxRMS = rdsd->Ph_RMS[Ph];
+ MaxPh = Ph;
+ }
+
+ /* During each bit time we expect the four phases to
+ * take one of the following patterns, where 1
+ * corresponds to maximum modulation:
+ * 1, 0, -1, 0 Case I
+ * -1, 0, 1, 0 Case II
+ * 1, 1/2, 0, -1/2 Case III
+ * -1, -1/2, 0, 1/2 Case IV
+ * We need to distinguish between cases in order to do
+ * the timing adjustment. Below we compare the
+ * correlation of the samples with Case I and Case III
+ * to see which has a bigger abs(correlation). Thus
+ * BigPh2, if set, means that we decided on Case I or
+ * Case II; if BigPh2 clear, we decided Case III or IV.
+ */
+ BigPh2 = abs(rdsd->PhaseValue[0]-rdsd->PhaseValue[2]) >
+ abs(rdsd->PhaseValue[0] +
+ ((rdsd->PhaseValue[1]-
+ rdsd->PhaseValue[3])>>1));
+ /* If BigPh2, use the difference between phase 1 value
+ * (downgoing for Case I, upgoing for Case II) and
+ * phase 3 value (upgoing for Case I, downgoing for
+ * Case II, thus the subtraction) to indicate timing
+ * error. If not BigPh2, use the sum of the phase 1
+ * value (downgoing for Case III, upgoing for Case IV)
+ * and phase 3 value (downgoing for Case III, upgoing
+ * for Case IV, thus the addition) to indicate timing
+ * error. If BigPh2, the slopes at phase 1 & phase 3
+ * are approximately double that if not BigPh2.
+ * Since we are trying to measure timing, scale
+ * by 1/2 in the BigPh2 case. */
+ if (BigPh2)
+ zerocross = (rdsd->PhaseValue[1]-
+ rdsd->PhaseValue[3])>>1;
+ else
+ zerocross = rdsd->PhaseValue[1]+
+ rdsd->PhaseValue[3];
+ /* Now if the prev bit was a "1", then the first zero
+ * crossing (phase 1 if BigPh2, phase 2 if !BigPh2)
+ * was a falling one, and if we were late then
+ * zerocross should be negative. If the prev bit was a
+ * "0", then the first zero crossing was a rising one,
+ * and if we were late then zerocross would be
+ * positive. If we are "late" it means that we need to
+ * do a shorter cycle of, say, 15 samples instead of
+ * 16, to "catch up" so that in the future we will be
+ * sampling earlier. We shorten the cycle by adding
+ * to i, so "late" is going to mean "increment i".
+ * Therefore "late" should be positive, which is done
+ * here by inverting zerocross if the previous bit was
+ * 1. You could say that this step reflects cases I
+ * and III into II and IV, respectively. */
+ if (OldBit)
+ zerocross = -zerocross;
+ if (!rdsd->DisablePushing) {
+ /*The algorithm so far has a stable operating
+ * point 17 phases away from the correct one.
+ * The following code is experimental and may
+ * be deleterious in low SNR conditions, but is
+ * an attempt to move off of the incorrect
+ * operating point. */
+
+ if (MaxPh != 0) {
+ /* If it isn't the same MaxPh as the
+ * last non-zero one, clear the counter
+ */
+ if (MaxPh != rdsd->PushLastMaxPh) {
+ /*Reset the counter */
+ rdsd->PushCounter = 0;
+ /*Record which phase we're now
+ * counting */
+ rdsd->PushLastMaxPh = MaxPh;
+ }
+ /* If the Max RMS is on the same
+ * non-zero phase, count up */
+ rdsd->PushCounter++;
+ }
+ /* Once every 128 bits, check and then reset
+ * PushCounter */
+ if (!(rdsd->BitAlignmentCounter & 0x0FF)) {
+ /*If 90% of the time the max phase has
+ * been from the same non-zero phase,
+ * decide that we are latched onto a 0
+ * lock point. Do a large push of the
+ * timing. */
+ if (rdsd->PushCounter > 230) {
+ s32 pshiph;
+ /*Convert from phase number to
+ * the number of filter
+ * output samples that we need
+ * to shift */
+ if (rdsd->PushLastMaxPh >= 2)
+ pshiph =
+ 4 - (s8)rdsd->
+ PushLastMaxPh;
+ else
+ pshiph =
+ -(s8)rdsd->
+ PushLastMaxPh;
+ /* Scale by the number of i-
+ * phases per output sample */
+ pshiph <<=
+ RDS_BASISSHIFTS-1;
+ /* Perform big pop to get near
+ * correct timing */
+ rdsd->i += (RDS_BASISLENGTH<<1)
+ + pshiph;
+ /* Set status indicating big
+ * pop was needed. Reset all
+ * leaky-bucket and summation
+ * variables because the big
+ * timing shift has invalidated
+ * them. Ph_RMS values don't
+ * need to be reset because
+ * they will shift over to
+ * reasonable values again
+ * before their erroneous
+ * values could have effect. */
+ rdsd->rds_big_timeshift = 1;
+ /*rdsd->Ph_RMS[0] = 0; */
+ /*rdsd->Ph_RMS[1] = 0; */
+ /*rdsd->Ph_RMS[2] = 0; */
+ /*rdsd->Ph_RMS[3] = 0; */
+ rdsd->mixandsum1 = 0;
+ rdsd->mixandsum2 = 0;
+ rdsd->SkipsAccum +=
+ pshiph;
+
+ /* Make adjustments in other
+ * values because of the push
+ * (they wouldn't otherwise be
+ * able to use the information
+ * that a push was needed in
+ * their future control
+ * decisions). */
+ if (rdsd->PushLastMaxPh != 2) {
+ /* If we weren't
+ * pushing from phase
+ * two, accumulate (for
+ * use in adapting
+ * SDNOMINAL) the
+ * phases moved by
+ * pushing. Phase two
+ * pushes are not used;
+ * the push direction
+ * is arbitrary since
+ * Phase 2 is 180
+ * degrees out. Also,
+ * phase 2 pushes don't
+ * result from
+ * reasonable slippage.
+ * */
+
+ if (rdsd->sdnom_adapt)
+ rdsd->SdnomSk
+ += pshiph;
+
+ /* Modify timing_adj to
+ * account for half of
+ * the DC response that
+ * would have occurred
+ * in timing_adj if
+ * that control loop
+ * had seen the push
+ * happen. (Why half?
+ * Because the loop has
+ * already seen a
+ * history of zerocross
+ * values that heads it
+ * in the same
+ * direction as this
+ * adjustment, but may
+ * have seen as few as
+ * half of what it
+ * should have.) */
+ rdsd->timing_adj +=
+ pshiph <<
+ (TADJSH+1);
+ }
+ /*Set countdown timer that will
+ * prevent any mixer popping
+ * until the Ph_RMS variables
+ * have had enough time to
+ * stabilize */
+
+ /* 2.5 time constants */
+ rdsd->PushSafetyZone = 5;
+ }
+ /*Reset the push counter */
+ rdsd->PushCounter = 0;
+ } /*end once every 128 bits */
+ } /*end if !DisablePushing */
+
+ /* Further possible additions:
+ *
+ * 1. Pushes modify timing_adj to decrease convergence
+ * time.
+ * 2. Separate timing_adj into pilottracking and non-pt
+ * cases (avoids convergence time after stereo/mono
+ * transitions)
+ *
+ * Old loop filter was a leaky bucket integrator, and
+ * it always lagged behind if the FM station had RDS
+ * asynchronous to the pilot, because the control loop
+ * needs another integrator to converge on a frequency
+ * error.
+ * New loop filter = 1/(1-1/z) * (a-1/z) * k,
+ * where a = 1+1/256 and k = 1/1024.
+ * You can narrow the loop bandwidth by making "a"
+ * twice as close to 1 and halving k, e.g. a = 1+1/512
+ * and k = 1/2048.
+ * (The way implemented, that narrowing loop BW by
+ * a factor of 2 can be done by incrementing TADJSH.)
+ *
+ * TGR 8/31/2007 */
+
+ /*Integrator, 1/(1-1/z) */
+ rdsd->timing_adj += zerocross;
+ /*Limit to 1 phase every 8 samples */
+ if (rdsd->SkipSafetyZone) {
+ rdsd->SkipSafetyZone--;
+ rdsd->sampskip = 0;
+ } else {
+ /*sampskip of non-zero is allowed,
+ * calculate what it really is */
+
+ /*Saturate timing_adj to 2's comp
+ * (2*TADJSH+4)-bit range. */
+ if (rdsd->timing_adj > (1<<(2*TADJSH+3))-1)
+ rdsd->timing_adj = (1<<(2*TADJSH+3))-1;
+ if (rdsd->timing_adj < -(1<<(2*TADJSH+3)))
+ rdsd->timing_adj = -(1<<(2*TADJSH+3));
+
+ /* Zero, implemented after the integrator
+ * output.
+ * (a-1/z) = (1+1/256) - 1/z = (1-1/z) + 1/256.
+ * But (1 - 1/z) is timing_adj-
+ * prev_timing_adj = zerocross. */
+ rdsd->sampskip = zerocross /* 1 - 1/z */
+ /* 1/256 (with rounding) */
+ + ((rdsd->timing_adj
+ + (1<<(TADJSH-1)))>>TADJSH);
+ /*Round and apply k */
+ rdsd->sampskip += (1<<(TADJSH+1));
+ rdsd->sampskip >>= (TADJSH+2);
+ /*Limit to [-1,+1] inclusive */
+ if (rdsd->sampskip > 1)
+ rdsd->sampskip = 1;
+ if (rdsd->sampskip < -1)
+ rdsd->sampskip = -1;
+ /* If non-zero, start the skip safety zone,
+ * which excludes more sample skipping for a
+ * while. Note that the safety zone only
+ * applies to the skips -- pushes can still
+ * happen inside a SkipSafetyZone. */
+ if (rdsd->sampskip)
+ rdsd->SkipSafetyZone = 8-1;
+ }
+ /**********************************************
+ * End Timing Adjust Code
+ **********************************************/
+
+ /**********************************************
+ * Begin Phase Popper Code
+ **********************************************/
+ /* If Phase Popping is enabled and 1/2 of a
+ * time constant has gone by... */
+ if (rdsd->PhasePoppingEnabled &&
+ !(rdsd->BitAlignmentCounter &
+ ((1<<(RMSALPHASHIFTS-1))-1))) {
+
+ u8 ForcePop = 0; /* Used to force a pop */
+
+ /*Record the maximum of the envelope */
+ if (MaxRMS > rdsd->PhasePopMaxRMS)
+ rdsd->PhasePopMaxRMS = MaxRMS;
+ /* Also track MaxRMS into MixPhase0/1Mag, so
+ * that we can see what the largest RMS on each
+ * of those phases is. On synchronous stations
+ * (meaning the RDS carrier and bit rate are
+ * synchronized with the pilot), the right mix
+ * phase will always be big and the wrong phase
+ * small. On asynchronous stations (and
+ * stations without RDS), both phases will at
+ * some time or other have about the
+ * same amplitude on each of the phases. */
+ if (rdsd->rds_mix_offset) {
+ if (MaxRMS > rdsd->MixPhase1Mag)
+ rdsd->MixPhase1Mag = MaxRMS;
+ } else {
+ if (MaxRMS > rdsd->MixPhase0Mag)
+ rdsd->MixPhase0Mag = MaxRMS;
+ }
+ /* Update PopSafetyZone and PushSafetyZone
+ * counters. With RMSALPHASHIFTS = 5, each
+ * tick is 16/1187.5 =~ 13.5 ms. */
+ if (rdsd->PopSafetyZone) {
+ rdsd->PopSafetyZone--;
+ /* If safety zone just ended and this
+ * mix phase is giving smaller RMS than
+ * before the pop, then the pop was a
+ * mistake. Go back to previous mixer
+ * phase */
+ if (!(rdsd->PopSafetyZone)
+ && (rdsd->PhasePopMaxRMS <
+ rdsd->PrePopRMS))
+ ForcePop = 1;
+ }
+ /* If there is no recent push, and Phase 0 has
+ * the maximum RMS, and at least 1/7th of a
+ * second has passed since the last phase pop,
+ * and ((the RMS is less than 1/2 of
+ * PhasePopMaxRMS) or (the RMS is less than
+ * 100)), then try a phase pop. */
+ if (/* (rdsd->Ph_RMS[0] == MaxRMS) &&
+ * Phase 0 has maximum RMS */
+ !(rdsd->PopSafetyZone)) {
+ /* and Long enough since last
+ * phase pop */
+
+ /* Eligible for a pop, see if one of
+ * the pop conditions is met */
+ if ((MaxRMS<<1) <
+ rdsd->PhasePopMaxRMS) {
+ /*RMS decline from its peak */
+ ForcePop = 1;
+ } else if ((MaxRMS>>RMSALPHASHIFTS)
+ < 50) {
+ /*RMS too small to receive,
+ * either there's no RDS or
+ * this is the wrong phase */
+ ForcePop = 1;
+ }
+ }
+ if (ForcePop) {
+
+ /*Pop to opposite setting */
+ rds_mix_msg(rdsd, 0x8 |
+ !rdsd->rds_mix_offset);
+
+ /*Save the pre-pop RMS so that later we
+ * can see if the pop was actually
+ * effective */
+ rdsd->PrePopRMS = MaxRMS;
+ /*Reset the PhasePopMaxRMS. We rely on
+ * the PopSafetyZone to give time to
+ * get a new valid max RMS before we're
+ * eligible for the next phase pop. If
+ * there were no reset we'd be forever
+ * incrementing PhasePopMaxRMS due
+ * to just happenstance large-noise
+ * samples and it might eventually get
+ * some freakish large value causing
+ * frequent erroneous pops. */
+ rdsd->PhasePopMaxRMS = 0;
+ /* Pop Safety zone length is decided by
+ * how much of an asynchronous
+ * frequency can be supported. Allowing
+ * 50 ppm of transmitter error (error
+ * between their own pilot, that we
+ * should be locked to, and their RDS
+ * carrier (which by RDS spec should be
+ * locked to their pilot, but we've
+ * recently found frequently isn't).
+ * 50ppm * 57kHz = 2.85Hz.
+ * (2.85 cycles/sec)(4 pops/cycle)
+ * = 11.4 pops/second.
+ * Safety zone = (1/11.4) seconds =~ 104
+ * bits, round down to 96 bits which
+ * yields 6 ticks if RMSALPHASHIFTS = 5.
+ * */
+ rdsd->PopSafetyZone = 96>>
+ (RMSALPHASHIFTS-1);
+ }
+ }
+ /******************************************************
+ * End Phase Popper Code
+ ******************************************************/
+
+ /* SDNOMINAL adaption */
+ if (rdsd->sdnom_adapt) {
+ rdsd->SdnomSk += rdsd->sampskip;
+ if (rdsd->pCoefForcedMono &&
+ (rdsd->BitAlignmentCounter & 0xFFF) ==
+ 0x800) {
+
+ rds_sdnominal_msg(rdsd,
+ -(rdsd->SdnomSk<<9));
+
+ /*Reset skips counter */
+ rdsd->SdnomSk = 0;
+ }
+ }
+
+ rdsd->SkipsAccum += rdsd->sampskip;
+ /* Once per 3.45 seconds, print out signal strength,
+ * skips and pops. Then reset the variables totalling
+ * those occurrences */
+ if (!(rdsd->BitAlignmentCounter & 0xFFF)) {
+ /* During very noisy input (or if no RDS, or no
+ * station present), timing_adj can go crazy,
+ * since it is the integral of noise. Although
+ * it is a saturated value (earlier, in the
+ * timing adjust code), the level at which we
+ * can saturate still leaves room for
+ * timing_adj to get too big. A large value of
+ * timing_adj is a persistent pathology because
+ * the phase is shifting so quickly that the
+ * push detector (which relies on stable
+ * phase-RMS values) never triggers, thus there
+ * is no implemented rescue besides this
+ * clearing that restores proper function. */
+ if (abs(rdsd->SkipsAccum) > 300)
+ rdsd->timing_adj = 0;
+ /*Reset the accumulations. */
+ rdsd->SkipsAccum = 0;
+ }
+ } /*End of bit timing adaption */
+
+ /* If mixer phase determination in progress,
+ * perform actions at certain times */
+ if (rdsd->MixPhaseDetInProg) {
+ /*~10ms settling time after mixer phase change */
+ #define MIXPHASE_STARTMEAS 12
+ /*~20ms measurement window */
+ #define MIXPHASE_ENDMEAS (MIXPHASE_STARTMEAS+24)
+ if (rdsd->BitAlignmentCounter == MIXPHASE_STARTMEAS) {
+ /*Reset the RMS variables */
+ rdsd->Ph_RMS[0] = 0;
+ rdsd->Ph_RMS[1] = 0;
+ rdsd->Ph_RMS[2] = 0;
+ rdsd->Ph_RMS[3] = 0;
+ /* Don't reset mixandsum values because at
+ * least they have filtered continuously. All
+ * we really need for the mixer phase decision
+ * is a constant measurement window. */
+ } else if (rdsd->BitAlignmentCounter ==
+ MIXPHASE_ENDMEAS) {
+ /*Measurement = mean of RMS values */
+ u32 Ndx, MeasVal = 0;
+ for (Ndx = 0; Ndx < 4;
+ MeasVal += rdsd->Ph_RMS[Ndx++]>>2);
+ /*Store measurement in correct place */
+ if (rdsd->MixPhaseState == 1) {
+ rdsd->MixPhase0Mag = MeasVal;
+ /*Go to next mixer setting */
+ rds_mix_msg(rdsd, 1);
+ } else if (rdsd->MixPhaseState == 2) {
+ u8 NextMixSetting;
+ rdsd->MixPhase1Mag = MeasVal;
+ /* Both measurements done now, see what
+ * mixer setting we need to use.
+ * 0 if MixPhase0Mag > MixPhase1Mag,
+ * 1 otherwise. */
+ NextMixSetting = (rdsd->MixPhase0Mag
+ <= rdsd->MixPhase1Mag);
+ /* If the mixer setting needed is 1,
+ * that is already the current setting.
+ * Terminate mixer phase determination.
+ * Otherwise send message to switch the
+ * mixer phase setting. */
+ if (NextMixSetting) {
+ rdsd->MixPhaseState = 3;
+ rdsd->MixPhaseDetInProg = 0;
+ } else
+ rds_mix_msg(rdsd, 0);
+ }
+ }
+ /* Reset BitAlignmentCounter if the Mixer just popped
+ * Change state, if required. States are:
+ * 0: Initial state, send msg causing RDS_MIXOFFSET=>0
+ * 1: Measure with RDS_MIXOFFSET = 0.
+ * Lasts just over 30 ms.
+ * 2: Measure with RDS_MIXOFFSET = 1.
+ * Lasts just over 30 ms.
+ * 3: At final RDS_MIXOFFSET value.
+ * Lasts as long as RDS continues. */
+ if (rdsd->MixPopDone) {
+ rdsd->MixPopDone = 0;
+ rdsd->BitAlignmentCounter = 0;
+ rdsd->MixPhaseState++; /*Go to next state */
+ /* If we got to state 3, turn off mixer phase
+ * determination code */
+ if (rdsd->MixPhaseState == 3)
+ rdsd->MixPhaseDetInProg = 0;
+ }
+ }
+
+ /* Update status variables */
+ rdsd->RDS_BIT_AMP_STAT_REG9 = rdsd->Ph_RMS[0]>>RMSALPHASHIFTS;
+ /*Saturate */
+ if (rdsd->RDS_BIT_AMP_STAT_REG9 > 511)
+ rdsd->RDS_BIT_AMP_STAT_REG9 = 511;
+ } /*End phase 0 code */
+
+ /***************************************************
+ * Actions common to all phases
+ ***************************************************/
+
+ /* Save the output of each phase for possible
+ * calculations during phase 0 */
+ rdsd->PhaseValue[phase] = filter_out;
+
+ /*So that we can measure signal amplitude and/or determine what (if */
+ /* any) big jump is needed, maintain the RMS of each phase. Phase */
+ /* 0 RMS is already in Ph_RMS[0] (see bitslicing code, earlier). */
+ rdsd->Ph_RMS[phase] += abs(filter_out) -
+ (rdsd->Ph_RMS[phase]>>RMSALPHASHIFTS);
+}
+
+#if defined(CONFIG_ARM)
+
+/* assembly version for ARM */
+#define RDS_MAC(_acc, _x, _y) \
+ __asm__ __volatile__ ( \
+ "smlabb %0, %1, %2, %0\n" \
+ : "=&r" (_acc) \
+ : "r" (_x), "r" (_y) \
+ : "cc")
+
+#else
+
+/* all others, use standard C */
+#define RDS_MAC(_acc, _x, _y) \
+ do { \
+ (_acc) += (s16)(_x) * (s16)(_y); \
+ } while (0)
+
+#endif
+
+static void rds_demod(const u16 *data, struct stfm1000_rds_demod *rdsd,
+ struct stfm1000_rds_bitstream *rbit, int total)
+{
+ register const s16 *basis0;
+ register const s16 *basis1;
+ register s16 val;
+ register int i;
+ register int sampskip;
+ register s32 acc1;
+ register s32 acc2;
+
+ /* point to the table */
+ basis0 = u16_rds_basis;
+ basis1 = basis0 + 8;
+
+ rdsd->return_num = 0;
+
+ /* restore state */
+ i = rdsd->i;
+ acc1 = rdsd->mixandsum1;
+ acc2 = rdsd->mixandsum2; /* 64 bit */
+ sampskip = rdsd->sampskip;
+
+ while (total-- > 0) {
+
+ val = data[3]; /* load RDS data */
+ data += 4;
+ if (val == 0x7fff) /* illegal RDS sample */
+ continue;
+
+ RDS_MAC(acc1, val, basis0[i]);
+ RDS_MAC(acc2, val, basis1[i]);
+
+ if (i == 4) {
+ i += sampskip;
+ sampskip = 0;
+ }
+
+ if ((i & (RDS_BASISLENGTH / 2 - 1)) == 0) {
+
+ /* save state */
+ rdsd->mixandsum1 = acc1;
+ rdsd->mixandsum2 = acc2;
+ rdsd->i = i;
+ rdsd->sampskip = sampskip;
+
+ demod_loop(rbit, rdsd);
+
+ /* restore state */
+ acc1 = rdsd->mixandsum1;
+ acc2 = rdsd->mixandsum2;
+ i = rdsd->i;
+ sampskip = rdsd->sampskip;
+ }
+ i = (i + 1) & 31;
+ }
+
+ /* save state */
+ rdsd->mixandsum1 = acc1;
+ rdsd->mixandsum2 = acc2;
+ rdsd->i = i;
+ rdsd->sampskip = sampskip;
+}
+
+void stfm1000_rds_demod(struct stfm1000_rds_state *rds, const u16 *dri_data,
+ int total)
+{
+ rds_demod(dri_data, &rds->demod, &rds->bitstream, total);
+
+ /* signal only when we have enough */
+ if (bits_filled(&rds->bitstream) > 128)
+ stfm1000_monitor_signal(rds_state_to_stfm1000(rds),
+ EVENT_RDS_BITS);
+}
+
+static void bitstream_reset(struct stfm1000_rds_bitstream *rdsb)
+{
+ memset(rdsb, 0, sizeof(*rdsb));
+}
+
+static void demod_reset(struct stfm1000_rds_demod *rdsd)
+{
+ memset(rdsd, 0, sizeof(*rdsd));
+ rdsd->sdnom_adapt = 0; /* XXX this doesn't really work right */
+ /* it causes underruns at ALSA */
+ rdsd->PhasePoppingEnabled = 1; /* does this? */
+}
+
+static void packet_reset(struct stfm1000_rds_pkt *rdsp)
+{
+ memset(rdsp, 0, sizeof(*rdsp));
+ rdsp->state = SYNC_OFFSET_A;
+}
+
+static void text_reset(struct stfm1000_rds_text *rdst)
+{
+ memset(rdst, 0, sizeof(*rdst));
+}
+
+void stfm1000_rds_reset(struct stfm1000_rds_state *rds)
+{
+ bitstream_reset(&rds->bitstream);
+ demod_reset(&rds->demod);
+ packet_reset(&rds->pkt);
+ text_reset(&rds->text);
+ rds->reset_req = 0;
+}
+
+int stfm1000_rds_bits_available(struct stfm1000_rds_state *rds)
+{
+ return bits_filled(&rds->bitstream);
+}
+
+int stmf1000_rds_get_bit(struct stfm1000_rds_state *rds)
+{
+ if (bits_filled(&rds->bitstream) == 0)
+ return -1;
+ return get1bit(&rds->bitstream);
+}
+
+int stmf1000_rds_avail_bits(struct stfm1000_rds_state *rds)
+{
+ return bits_filled(&rds->bitstream);
+}
+
+static const u32 rds_ParityCheck[] = {
+ 0x31B, 0x38F, 0x2A7, 0x0F7, 0x1EE,
+ 0x3DC, 0x201, 0x1BB, 0x376, 0x355,
+ 0x313, 0x39F, 0x287, 0x0B7, 0x16E,
+ 0x2DC, 0x001, 0x002, 0x004, 0x008,
+ 0x010, 0x020, 0x040, 0x080, 0x100,
+ 0x200
+};
+
+static int calc_syndrome(u32 rdscrc)
+{
+ int i;
+ u32 syndrome = 0;
+ int word = 0x1;
+
+ for (i = 0; i < 26; i++) {
+ if (rdscrc & word)
+ syndrome ^= rds_ParityCheck[i];
+ word <<= 1;
+ }
+ return syndrome;
+}
+
+static u32 ecc_table[1024];
+static int ecc_table_generated;
+
+static void generate_ecc_table(void)
+{
+ int i, j, size;
+ u32 syndrome, word;
+
+ for (i = 0; i < ECC_TBL_SIZE; i++)
+ ecc_table[i] = 0xFFFFFFFF;
+ ecc_table[0] = 0x0;
+
+ for (j = 0; j < 5; j++) {
+ word = (1 << (j + 1)) - 1; /* 0x01 0x03 0x07 0x0f 0x1f */
+ size = 26 - j; /* 26, 25, 24, 23, 22 */
+ syndrome = 0;
+ for (i = 0; i < size; i++) {
+ syndrome = calc_syndrome(word);
+ ecc_table[syndrome] = word;
+ word <<= 1;
+ }
+ }
+}
+
+static u32 ecc_correct(u32 rdsBits, int *recovered)
+{
+ u32 syndrome;
+ u32 errorBits;
+
+ if (recovered)
+ *recovered = 0;
+
+ /* Calculate Syndrome on Received Packet */
+ syndrome = calc_syndrome(rdsBits);
+
+ if (syndrome == 0)
+ return rdsBits; /* block is clean */
+
+ /* generate table first time we get here */
+ if (!ecc_table_generated) {
+ generate_ecc_table();
+ ecc_table_generated = 1;
+ }
+
+ /* Attempt to recover block */
+ errorBits = ecc_table[syndrome];
+ if (errorBits == UNRECOVERABLE_RDS_BLOCK)
+ return UNRECOVERABLE_RDS_BLOCK; /* Block can not be recovered.
+ * it is bad packet */
+
+ rdsBits = rdsBits ^ errorBits;
+ if (recovered)
+ (*recovered)++;
+ return rdsBits; /* ECC correct */
+}
+
+/* The following table lists the RDS and RBDS Program Type codes
+ * and their meanings:
+ * PTY code RDS Program type RBDS Program type */
+static const struct stfm1000_rds_pty stc_tss_pty_tab[] = {
+ { 0, "No program type", "No program type"},
+ { 1, "News", "News"},
+ { 2, "Current affairs", "Information"},
+ { 3, "Information", "Sports"},
+ { 4, "Sports", "Talk"},
+ { 5, "Education", "Rock"},
+ { 6, "Drama", "Classic Rock"},
+ { 7, "Culture", "Adult Hits"},
+ { 8, "Science", "Soft Rock"},
+ { 9, "Varied", "Top 40"},
+ { 10, "Pop", "Music Country"},
+ { 11, "Rock", "Music Oldies"},
+ { 12, "M.O.R.", "Music Soft"},
+ { 13, "Light classical", "Nostalgia"},
+ { 14, "Serious", "Classical Jazz"},
+ { 15, "Other Music", "Classical"},
+ { 16, "Weather", "Rhythm and Blues"},
+ { 17, "Finance", "Soft Rhythm and Blues"},
+ { 18, "Children's programs", "Language"},
+ { 19, "Social Affairs", "Religious Music"},
+ { 20, "Religion", "Religious Talk"},
+ { 21, "Phone In", "Personality"},
+ { 22, "Travel", "Public"},
+ { 23, "Leisure", "College"},
+ { 24, "Jazz Music", "Unassigned"},
+ { 25, "Country Music", "Unassigned"},
+ { 26, "National Music", "Unassigned"},
+ { 27, "Oldies Music", "Unassigned"},
+ { 28, "Folk Music", "Unassigned"},
+ { 29, "Documentary", "Weather"},
+ { 30, "Alarm Test", "Emergency Test"},
+ { 31, "Alarm", "Emergency"},
+};
+
+#if 0
+static const char *rds_group_txt[] = {
+ [RDS_GROUP_TYPE_0A] = "Basic tuning and switching information (0A)",
+ [RDS_GROUP_TYPE_0B] = "Basic tuning and switching information (0B)",
+ [RDS_GROUP_TYPE_1A] = "Program item number and slow labeling codes",
+ [RDS_GROUP_TYPE_1B] = "Program item number",
+ [RDS_GROUP_TYPE_2A] = "Radio Text (2A)",
+ [RDS_GROUP_TYPE_2B] = "Radio Text (2B)",
+ [RDS_GROUP_TYPE_3A] = "Application identification for ODA only",
+ [RDS_GROUP_TYPE_3B] = "Open data applications",
+ [RDS_GROUP_TYPE_4A] = "Clock-time and date",
+ [RDS_GROUP_TYPE_4B] = "Open data applications",
+ [RDS_GROUP_TYPE_5A] = "Transparent Data Channels (32 ch.) or ODA (5A)",
+ [RDS_GROUP_TYPE_5B] = "Transparent Data Channels (32 ch.) or ODA (5B)",
+ [RDS_GROUP_TYPE_6A] = "In House Applications or ODA (6A)",
+ [RDS_GROUP_TYPE_6B] = "In House Applications or ODA (6B)",
+ [RDS_GROUP_TYPE_7A] = "Radio Paging or ODA",
+ [RDS_GROUP_TYPE_7B] = "Open Data Applications",
+ [RDS_GROUP_TYPE_8A] = "Traffic Message Channel or ODA",
+ [RDS_GROUP_TYPE_8B] = "Open Data Applications",
+ [RDS_GROUP_TYPE_9A] = "Emergency warning system or ODA",
+ [RDS_GROUP_TYPE_9B] = "Open Data Applications",
+ [RDS_GROUP_TYPE_10A] = "Program Type Name",
+ [RDS_GROUP_TYPE_10B] = "Open Data Applications (10B)",
+ [RDS_GROUP_TYPE_11A] = "Open Data Applications (11A)",
+ [RDS_GROUP_TYPE_11B] = "Open Data Applications (11B)",
+ [RDS_GROUP_TYPE_12A] = "Open Data Applications (12A)",
+ [RDS_GROUP_TYPE_12B] = "Open Data Applications (12B)",
+ [RDS_GROUP_TYPE_13A] = "Enhanced Radio Paging or ODA",
+ [RDS_GROUP_TYPE_13B] = "Open Data Applications",
+ [RDS_GROUP_TYPE_14A] = "Enhanced Other Networks information (14A)",
+ [RDS_GROUP_TYPE_14B] = "Enhanced Other Networks information (14B)",
+ [RDS_GROUP_TYPE_15A] = "Defined in RBDS",
+ [RDS_GROUP_TYPE_15B] = "Fast switching information",
+};
+#endif
+
+static void dump_rds_packet(u8 *buf)
+{
+ u16 pi, offb;
+
+ pi = (u16)(buf[0] << 8) | buf[1];
+ offb = (u16)(buf[1] << 8) | buf[2];
+
+ printk(KERN_INFO "GRP: "
+ "PI=0x%04x "
+ "GT=%2d VER=%d TP=%d PTY=%2d "
+ "PS_SEG=%2d RT_AB=%2d RT_SEG=%2d\n", pi,
+ RDS_GROUP_TYPE(offb), RDS_VERSION(offb), RDS_TP(offb),
+ RDS_PTY(offb),
+ RDS_PS_SEG(offb), RDS_RT_AB(offb), RDS_RT_SEG(offb));
+}
+
+void stfm1000_rds_process_packet(struct stfm1000_rds_state *rds, u8 *buffer)
+{
+ struct stfm1000_rds_text *rdst = &rds->text;
+ /* char tempCallLetters[5] = {0}; */
+ struct rds_group_data grp;
+ int grpno;
+ u32 offset;
+ char tps[9];
+ int i, seg, idx;
+
+ grp.piCode = ((u16)buffer[0] << 8) | buffer[1];
+ grp.offsetB = ((u16)buffer[2] << 8) | buffer[3];
+ grp.offsetC = ((u16)buffer[4] << 8) | buffer[5];
+ grp.offsetD = ((u16)buffer[6] << 8) | buffer[7];
+
+ grpno = (grp.offsetB >> (8 + 3)) & 0x1f;
+
+ if (rds_state_to_stfm1000(rds)->rds_info)
+ dump_rds_packet(buffer);
+
+ /* Is this the first time through? */
+ if (!rdst->bRds_detected) {
+ rdst->pi = grp.piCode;
+ rdst->tp = RDS_TP(grp.offsetB);
+ rdst->version = RDS_VERSION(grp.offsetB);
+ rdst->pty.id = RDS_PTY(grp.offsetB);
+ rdst->pty.pRds = stc_tss_pty_tab[rdst->pty.id].pRds;
+ rdst->pty.pRdbs = stc_tss_pty_tab[rdst->pty.id].pRdbs;
+ rdst->bRds_detected = 1;
+ }
+
+ /* Have we process too many PI errors? */
+ if (grp.piCode != rdst->pi) {
+ if (rdst->mismatch++ > 10) {
+
+ /* requested reset of RDS */
+ rds->reset_req = 1;
+
+ /* signal monitor thread */
+ stfm1000_monitor_signal(rds_state_to_stfm1000(rds),
+ EVENT_RDS_RESET);
+
+ if (rds_state_to_stfm1000(rds)->rds_info)
+ printk(KERN_INFO "RDS: RESET!!!\n");
+
+ text_reset(rdst);
+ }
+ rdst->consecutiveGood = 0;
+ return;
+ }
+
+ if (rdst->consecutiveGood++ > 10)
+ rdst->mismatch = 0; /* reset bad count */
+
+ if (rdst->consecutiveGood > rdst->consecutiveGoodMax)
+ rdst->consecutiveGoodMax = rdst->consecutiveGood;
+
+ switch (grpno) {
+ case RDS_GROUP_TYPE_0A:
+ case RDS_GROUP_TYPE_0B:
+ /* Extract Service Name information */
+ offset = RDS_PS_SEG(grp.offsetB) * 2;
+ rdst->wk_ps[offset] = buffer[6]; /* better */
+ rdst->wk_ps[offset + 1] = buffer[7];
+ rdst->wk_ps_mask |= 1 << RDS_PS_SEG(grp.offsetB);
+
+ if (rds_state_to_stfm1000(rds)->rds_info) {
+ for (i = 0; i < 8; i++) {
+ if (rdst->wk_ps_mask & (1 << i)) {
+ tps[i * 2] =
+ rdst->wk_ps[i * 2];
+ tps[i * 2 + 1] =
+ rdst->wk_ps[i * 2 + 1];
+ } else {
+ tps[i * 2] = '_';
+ tps[i * 2 + 1] = '_';
+ }
+ }
+ tps[ARRAY_SIZE(tps) - 1] = '\0';
+ if (rds_state_to_stfm1000(rds)->rds_info)
+ printk(KERN_INFO "RDS-PS (curr): %s\n", tps);
+ }
+
+ if (rdst->wk_ps_mask != ALL_SEGMENT_BITS)
+ break;
+
+ if (rdst->ps_valid) {
+ if (memcmp(rdst->ps, rdst->wk_ps, 8) != 0) {
+ memset(rdst->cp_ps, 0, 8);
+ memset(rdst->wk_ps, 0, 8);
+ rdst->wk_ps_mask = 0;
+ }
+
+ memset(rdst->ps, 0, 8);
+ rdst->ps_valid = 0;
+ break;
+ }
+
+ /* does working buffer == compare buffer */
+ if (memcmp(rdst->cp_ps, rdst->wk_ps, 8) != 0) {
+ /* just copy from working to compare buffer */
+ memcpy(rdst->cp_ps, rdst->wk_ps, 8);
+ rdst->wk_ps_mask = 0;
+ break;
+ }
+
+ /* working buffer matches compare buffer, send to UI */
+ memcpy(rdst->ps, rdst->cp_ps, 8);
+ rdst->ps_valid = 1;
+
+ if (rds_state_to_stfm1000(rds)->rds_info)
+ printk(KERN_INFO "RDS: PS '%s'\n", rdst->ps);
+
+ /* clear working mask-only */
+ rdst->wk_ps_mask = 0;
+ break;
+
+ case RDS_GROUP_TYPE_2A:
+
+ /* Clear buffer */
+ if (rdst->textAB_flag != RDS_RT_AB(grp.offsetB)) {
+ memset(rdst->wk_text, 0, 64);
+ rdst->wk_text_mask = 0;
+ rdst->textAB_flag = RDS_RT_AB(grp.offsetB);
+ }
+
+ /* Extract Text */
+ seg = RDS_RT_SEG(grp.offsetB);
+ idx = seg * 4;
+
+ #define CNVT_EOT(x) ((x) != RDS_EOT ? (x) : 0)
+ rdst->wk_text[idx++] = CNVT_EOT(buffer[4]);
+ rdst->wk_text[idx++] = CNVT_EOT(buffer[5]);
+ rdst->wk_text[idx++] = CNVT_EOT(buffer[6]);
+ rdst->wk_text[idx++] = CNVT_EOT(buffer[7]);
+
+ rdst->wk_text_mask |= 1 << seg;
+ /* scan msg data for EOT. If found set all higher
+ * mask bits */
+ for (idx = 0; idx < 4; idx++) {
+ if (rdst->text[idx] == RDS_EOT)
+ break;
+ }
+ if (idx < 4) {
+ /* set current and all higher bits */
+ for (idx = RDS_RT_SEG(grp.offsetB); idx < 16;
+ idx++)
+ rdst->wk_text_mask |= 1 << idx;
+ }
+
+ /* Process buffer when filled */
+ if (rdst->wk_text_mask != ALL_TEXT_BITS)
+ break;
+
+ if (!rdst->text_valid)
+ rdst->text_valid = 1;
+ else if (memcmp(rdst->text, rdst->wk_text, 64) == 0)
+ break;
+
+ memcpy(rdst->text, rdst->wk_text, 64);
+
+ if (rds_state_to_stfm1000(rds)->rds_info)
+ printk(KERN_INFO "RDS: TEXT '%s'\n", rdst->text);
+
+ memset(rdst->wk_text, 0, 64);
+ rdst->wk_text_mask = 0;
+ break;
+
+ default:
+ break;
+ }
+}
+
+int stfm1000_rds_packet_dequeue(struct stfm1000_rds_state *rds, u8 *buf)
+{
+ struct stfm1000_rds_pkt *pkt = &rds->pkt;
+
+ if (pkt->buf_cnt == 0)
+ return -1;
+
+ memcpy(buf, &pkt->buf_queue[pkt->buf_tail][0], 8);
+ if (++pkt->buf_tail >= RDS_PKT_QUEUE)
+ pkt->buf_tail = 0;
+ pkt->buf_cnt--;
+
+ return 0;
+}
+
+void stfm1000_rds_packet_bit(struct stfm1000_rds_state *rds, int bit)
+{
+ struct stfm1000_rds_pkt *pkt = &rds->pkt;
+ u32 rdsdata, rdscrc, rdscrc_c, rdscrc_cp;
+ int correct, correct2, recovered, recovered2;
+ int RetVal;
+
+ /* Stick into shift register */
+ pkt->rdsstream = ((pkt->rdsstream << 1) | bit) & 0x03ffffff;
+ pkt->bitsinfifo++;
+ pkt->bitcount++;
+
+ /* wait for 26 bits of block */
+ if (pkt->bitsinfifo < 26)
+ return;
+
+ rdsdata = pkt->rdsstream & 0x03fffc00; /* 16 bits of Info. word */
+ rdscrc = pkt->rdsstream & 0x3ff; /* 10 bits of Checkword */
+
+ switch (pkt->state) {
+ case SYNC_OFFSET_A:
+
+ RetVal = calc_syndrome(pkt->rdsstream);
+
+ switch (RetVal) {
+ case RDS_SYNDROME_OFFSETA:
+ pkt->state = OFFSET_B;
+ break;
+ case RDS_SYNDROME_OFFSETB:
+ pkt->state = OFFSET_C_CP;
+ break;
+ case RDS_SYNDROME_OFFSETC:
+ pkt->state = OFFSET_D;
+ break;
+ case RDS_SYNDROME_OFFSETCP:
+ pkt->state = OFFSET_D;
+ break;
+ case RDS_SYNDROME_OFFSETD:
+ pkt->state = OFFSET_A;
+ break;
+ default:
+ pkt->state = SYNC_OFFSET_A;
+ break;
+ }
+ if (pkt->state == SYNC_OFFSET_A) {
+ pkt->sync_lost_packets++;
+ /* XXX send info? */
+ break;
+ }
+
+ pkt->good_packets++;
+
+ rdsdata = pkt->rdsstream & 0x03fffc00;
+
+ /* Save type A packet in buffer */
+ rdsdata >>= 10;
+ pkt->buffer[0] = (rdsdata >> 8);
+ pkt->buffer[1] = (rdsdata & 0xff);
+ pkt->bitsinfifo = 0;
+
+ /* We found a block with zero errors, but it is not at the
+ * start of the group. */
+ if (pkt->state == OFFSET_B)
+ pkt->discardpacket = 0;
+ else
+ pkt->discardpacket = 1;
+ break;
+
+ case OFFSET_A: /* Type A: we are in sync now */
+ rdscrc ^= RDS_OFFSETA;
+ correct = ecc_correct(rdsdata | rdscrc, &recovered);
+ if (correct == UNRECOVERABLE_RDS_BLOCK) {
+ pkt->bad_packets++;
+ pkt->discardpacket++;
+ pkt->state++;
+ pkt->bitsinfifo = 0;
+ break;
+ }
+
+ if (recovered)
+ pkt->recovered_packets++;
+ pkt->good_packets++;
+
+ /* Attempt to see, if we can get the entire group.
+ * Don't discard. */
+ pkt->discardpacket = 0;
+ rdsdata = correct & 0x03fffc00;
+
+ /* Save type A packet in buffer */
+ rdsdata >>= 10;
+ pkt->buffer[0] = (rdsdata >> 8);
+ pkt->buffer[1] = (rdsdata & 0xff);
+ pkt->bitsinfifo = 0;
+ pkt->state++;
+ break;
+
+ case OFFSET_B: /* Waiting for type B */
+ rdscrc ^= RDS_OFFSETB;
+ correct = ecc_correct(rdsdata | rdscrc, &recovered);
+ if (correct == UNRECOVERABLE_RDS_BLOCK) {
+ pkt->bad_packets++;
+ pkt->discardpacket++;
+ pkt->state++;
+ pkt->bitsinfifo = 0;
+ break;
+ }
+ if (recovered)
+ pkt->recovered_packets++;
+ pkt->good_packets++;
+
+ rdsdata = correct & 0x03fffc00;
+
+ /* Save type B packet in buffer */
+ rdsdata >>= 10;
+ pkt->buffer[2] = (rdsdata >> 8);
+ pkt->buffer[3] = (rdsdata & 0xff);
+ pkt->bitsinfifo = 0;
+ pkt->state++;
+ break;
+
+ case OFFSET_C_CP: /* Waiting for type C or C' */
+ rdscrc_c = rdscrc ^ RDS_OFFSETC;
+ rdscrc_cp = rdscrc ^ RDS_OFFSETCP;
+ correct = ecc_correct(rdsdata | rdscrc_c, &recovered);
+ correct2 = ecc_correct(rdsdata | rdscrc_cp, &recovered2);
+ if (correct == UNRECOVERABLE_RDS_BLOCK
+ && correct2 == UNRECOVERABLE_RDS_BLOCK) {
+ pkt->bad_packets++;
+ pkt->discardpacket++;
+ pkt->state++;
+ pkt->bitsinfifo = 0;
+ break;
+ }
+
+ if (recovered || recovered2)
+ pkt->recovered_packets++;
+ pkt->good_packets++;
+
+ if (correct == UNRECOVERABLE_RDS_BLOCK)
+ correct = correct2;
+
+ rdsdata = correct & 0x03fffc00;
+
+ /* Save type C packet in buffer */
+ rdsdata >>= 10;
+ pkt->buffer[4] = (rdsdata >> 8);
+ pkt->buffer[5] = (rdsdata & 0xff);
+ pkt->bitsinfifo = 0;
+ pkt->state++;
+ break;
+
+ case OFFSET_D: /* Waiting for type D */
+ rdscrc ^= RDS_OFFSETD;
+ correct = ecc_correct(rdsdata | rdscrc, &recovered);
+ if (correct == UNRECOVERABLE_RDS_BLOCK) {
+ pkt->bad_packets++;
+ pkt->discardpacket++;
+ pkt->state = OFFSET_A;
+ pkt->bitsinfifo = 0;
+ break;
+ }
+
+ if (recovered)
+ pkt->recovered_packets++;
+ pkt->good_packets++;
+
+ rdsdata = correct & 0x03fffc00;
+
+ /* Save type D packet in buffer */
+ rdsdata >>= 10;
+ pkt->buffer[6] = (rdsdata >> 8);
+ pkt->buffer[7] = (rdsdata & 0xff);
+
+ /* buffer it if all segments were ok */
+ if (pkt->discardpacket) {
+ /* We're still in sync, so back to state 1 */
+ pkt->state = OFFSET_A;
+ pkt->bitsinfifo = 0;
+ pkt->discardpacket = 0;
+ break;
+ }
+
+ pkt->state++;
+ /* fall-through */
+
+ case PACKET_OUT:
+ pkt->GroupDropOnce = 1;
+
+ /* queue packet */
+ if (pkt->buf_cnt < RDS_PKT_QUEUE) {
+ memcpy(&pkt->buf_queue[pkt->buf_head][0],
+ pkt->buffer, 8);
+ if (++pkt->buf_head >= RDS_PKT_QUEUE)
+ pkt->buf_head = 0;
+ pkt->buf_cnt++;
+ } else
+ pkt->buf_overruns++;
+
+ /* We're still in sync, so back to state 1 */
+ pkt->state = OFFSET_A;
+ pkt->bitsinfifo = 0;
+ pkt->discardpacket = 0;
+ break;
+
+ }
+
+ /* Lots of errors? If so, go back to resync mode */
+ if (pkt->discardpacket >= 10) {
+ pkt->state = SYNC_OFFSET_A; /* reset sync state */
+ pkt->bitsinfifo = 26; /* resync a bit faster */
+ }
+}
+
+/* GROUP_TYPE 0A-0B (buffer must have enough space for 9 bytes) */
+int stfm1000_rds_get_ps(struct stfm1000_rds_state *rds, u8 *buffer,
+ int bufsize)
+{
+ struct stfm1000_rds_text *rdst = &rds->text;
+
+ if (bufsize < 9)
+ return -1;
+
+ if (!rdst->ps_valid)
+ return -1;
+
+ memcpy(buffer, rdst->ps, 8);
+ buffer[8] = '\0';
+
+ return 8;
+}
+
+/* GROUP_TYPE 2A (buffer must have enough space for 65 bytes) */
+int stfm1000_rds_get_text(struct stfm1000_rds_state *rds, u8 *buffer,
+ int bufsize)
+{
+ struct stfm1000_rds_text *rdst = &rds->text;
+
+ if (bufsize < 9)
+ return -1;
+
+ if (!rdst->text_valid)
+ return -1;
+
+ memcpy(buffer, rdst->text, 64);
+ buffer[64] = '\0';
+
+ return 64;
+}
diff --git a/drivers/media/radio/stfm1000/stfm1000-rds.h b/drivers/media/radio/stfm1000/stfm1000-rds.h
new file mode 100644
index 000000000000..44b9a610f86e
--- /dev/null
+++ b/drivers/media/radio/stfm1000/stfm1000-rds.h
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 STFM1000_RDS_H
+#define STFM1000_RDS_H
+
+#include <linux/types.h>
+
+/* log2(number of samples in a filter basis) */
+#define RDS_BASISSHIFTS 4
+
+/* number of samples in a filter basis */
+#define RDS_BASISLENGTH (1 << RDS_BASISSHIFTS)
+
+#define TIME_ADAPT_OVER 100
+
+/* 2^(-this) is the RMS leaky bucket time constant */
+#define RMSALPHASHIFTS 5
+
+#define PROCESS_RDS_BITS 128
+
+#define RDS_BITBUFSIZE 1024 /* was 128 */
+struct stfm1000_rds_bitstream {
+ u32 buf[RDS_BITBUFSIZE]; /* bit buffer */
+ int HeadBitCount; /* bit buffer head counter */
+ int TailBitCount; /* bit buffer tail counter */
+};
+
+struct stfm1000_rds_demod {
+ u32 mixandsum1; /* Accumulator for first
+ * basis filter */
+ u32 mixandsum2; /* Accumulator for 2nd
+ * basis filter */
+ u32 i; /* Phase Index, 32 phases per
+ * RDS bit */
+ u32 return_num; /* Set if there is a new RDS bit */
+ u32 BitAlignmentCounter; /* Counts bits for timing purposes */
+ int sampskip; /* Requested timing shift (on i) */
+
+ int DisablePushing; /* Disables phase push algorithm
+ * (phase push happens when Ph_RMS[x],
+ * x != 0, is consistently the maximum
+ * Ph_RMS) */
+ int MixPopDone; /* Last mixer phase set request is
+ * done */
+ u8 rds_big_timeshift; /* If set, indicates a push or large
+ * timing shift occurred */
+ int return_rdsdemod; /* Output, (most recent bit) XOR
+ * (prev bit) */
+ u32 RDS_BIT_AMP_STAT_REG9; /* Size of bit (RMS of RDS signal at
+ * bitslicing instant, typically 220
+ * to 270) */
+ s32 decomp_hist; /* Most recent basis filter output */
+ s32 decomp_hist_p; /* Previous basis filter output */
+ s32 PhaseValue[4]; /* Half-basis phase samples over the
+ * most recent bit */
+ u32 Ph_RMS[4]; /* RMS of the four half-basis phases */
+ s32 timing_adj; /* Timing loop leaky-bucket
+ * accumulator */
+ u32 MixPhase0Mag; /* Magnitude of RDS signal with RDS
+ * mixer phase 0 (from mixer phase
+ * determination) */
+ u32 MixPhase1Mag; /* Magnitude of RDS signal with RDS
+ * mixer phase 1 (from mixer phase
+ * determination) */
+ u32 PhasePopMaxRMS; /* Maximum RMS observed since last
+ * phase pop */
+ u32 PrePopRMS; /* Max of Ph_RMS array right before the
+ * most recent phase pop */
+ u8 MixPhaseState; /* State of RDS mixer phase
+ * determination state machine */
+ int MixPhaseDetInProg; /* Set if RDS mix phase determination
+ * is in progress */
+ int sliced_data; /* The most recent bit decision */
+ u8 PopSafetyZone; /* Countdown timer, holds off next
+ * phase pop after a recent one */
+ u8 PushSafetyZone; /* Countdown timer, holds off next
+ * phase pop after a timing push (b/c
+ * timing push resets Ph_RMS vars) */
+ u8 SkipSafetyZone; /* Countdown timer, holds off next
+ * phase skip (small timing adj) */
+ int Synchronous; /* RDS has been determined to be
+ * synchronous to pilot */
+ u8 PushLastMaxPh; /* The index at which Ph_RMS is
+ * maximum ("x" in the above two
+ * comments) */
+ s32 PushCounter; /* Counts instances of Ph_RMS[x], x!=0,
+ * being the maximum Ph_RMS */
+ s32 SkipsAccum; /* Accumulation of all timing skips
+ * since RDS demod started */
+ s32 SdnomSk; /* Skips counter used for SDNOMINAL
+ * adaption */
+
+ /* update this everytime it's changed & put it here */
+ unsigned int rds_mix_offset : 1;
+
+ unsigned int sdnom_adapt : 1;
+ unsigned int pCoefForcedMono : 1; /* copy of filter parameter */
+ unsigned int PhasePoppingEnabled : 1;
+
+ unsigned int mix_msg_pending : 1;
+ u8 mix_msg;
+ unsigned int mix_msg_overrun;
+ unsigned int mix_msg_processed_changed;
+
+ unsigned int sdnominal_msg_pending : 1;
+ int sdnominal_msg;
+ unsigned int sdnominal_msg_overrun;
+
+ u32 RdsDemodSkippedBitCnt; /* bit skipped by RDS demodulator due
+ * to unavailable space in buf[]
+ * (bit buffer) */
+};
+
+#define RDS_OFFSETA 0x0fc
+#define RDS_OFFSETB 0x198
+#define RDS_OFFSETC 0x168
+#define RDS_OFFSETCP 0x350
+#define RDS_OFFSETD 0x1b4
+
+#define RDS_SYNDROME_OFFSETA 0x3d8
+#define RDS_SYNDROME_OFFSETB 0x3d4
+#define RDS_SYNDROME_OFFSETC 0x25c
+#define RDS_SYNDROME_OFFSETCP 0x3cc
+#define RDS_SYNDROME_OFFSETD 0x258
+
+#define SYNC_OFFSET_A 0 /* default state */
+#define OFFSET_A 1
+#define OFFSET_B 2
+#define OFFSET_C_CP 3
+#define OFFSET_D 4
+#define PACKET_OUT 5
+
+#define ECC_TBL_SIZE 1024
+#define UNRECOVERABLE_RDS_BLOCK 0xffffffff
+
+#define RDS_PKT_QUEUE 16
+
+struct stfm1000_rds_pkt {
+ int state; /* Current state */
+ u32 rdsstream; /* Current RDS data */
+ u8 buffer[8]; /* temporary storage of RDS data */
+ int discardpacket; /* discard packet count */
+ int sync_lost_packets; /* sync lost */
+ int good_packets; /* good packet */
+ int bad_packets; /* bad packet */
+ int recovered_packets; /* recovered packet */
+ int bitsinfifo; /* bits count */
+ int GroupDropOnce; /* Send Group Drop Message once */
+ int bitcount; /* Counter for Number of Bits read */
+
+ /* queue the packets here */
+ int buf_overruns;
+ int buf_head;
+ int buf_tail;
+ int buf_cnt;
+ int buf_queue[RDS_PKT_QUEUE][8];
+};
+
+#define AUDIT 0
+#define ALL_SEGMENT_BITS 0xF
+#define ALL_TEXT_BITS 0xFFFF
+
+struct stfm1000_rds_pty {
+ u8 id; /* Program Type ID */
+ u8 *pRds; /* RDS description */
+ u8 *pRdbs; /* RDBS description */
+};
+
+struct stfm1000_rds_text {
+ u8 bRds_detected; /* Has the first packet come in yet? */
+ u16 pi; /* Program Identification Code (PI) */
+ struct stfm1000_rds_pty pty; /* Program Type (PTY)) */
+ u8 tp; /* Traffic Program (TP) identification
+ * code */
+ u8 ps[9]; /* Program Service Name Sent to UI */
+ u8 altFreq[2]; /* Alternate frequency (AF) */
+ u8 callLetters[5]; /* For US, stations call letters */
+
+ u8 text[65]; /* Radio Text A */
+
+ unsigned int version : 1; /* Is station broadcasting version
+ * A or B (B0) */
+ unsigned int ps_valid : 1; /* station name is valid */
+ unsigned int text_valid : 1; /* Text is valid */
+ unsigned int textAB_flag : 1; /* Current flag setting, reset if flag
+ * changes */
+
+ /*------------------Working area--------------------------- */
+ u8 cp_ps[8]; /* Compare buffer for PS */
+ u8 wk_ps[8]; /* Program Service buffer */
+ u8 wk_ps_mask; /* lower 4 bits must be set
+ * before copy */
+ u8 wk_text[64]; /* Radio Text buffer */
+ u16 wk_text_mask; /* all bits must be set before copy */
+
+ /*-------------------Counters------------------------------ */
+ u32 messages; /* total number of messages recieved */
+ u32 unsupported; /* call to unsupported group type */
+ u32 mismatch; /* Mismatched values */
+ u32 consecutiveGood; /* Consecutive good will clear bad */
+ u32 consecutiveGoodMax; /* Max counter for paramaters */
+};
+
+/* Maximum number of RDS groups described in the U.S. RBDS Standard. */
+#define MAX_RDS_GROUPS_SUPPORTED 32
+
+/* Common Constants */
+#define RDS_LINE_FEED 0xA
+#define RDS_EOT 0xD
+
+/* Offsets into OFFSETB */
+#define RDS_GROUP_TYPE(x) (((x) >> 12) & 0xF)
+#define RDS_VERSION(x) (((x) >> 11) & 0x1)
+#define RDS_TP(x) (((x) >> 10) & 0x1)
+#define RDS_PTY(x) (((x) >> 5) & 0x1F)
+#define RDS_PS_SEG(x) ((x) & 0x3)
+#define RDS_RT_AB(x) (((x) >> 4) & 0x1)
+#define RDS_RT_SEG(x) ((x) & 0xF)
+
+/* This values corresond to the Group Types defined */
+/* In the U.S. RBDS standard. */
+#define RDS_GROUP_TYPE_0A 0 /* Basic tuning and switching information */
+#define RDS_GROUP_TYPE_0B 1 /* Basic tuning and switching information */
+#define RDS_GROUP_TYPE_1A 2 /* Program item number and slow labeling
+ * codes */
+#define RDS_GROUP_TYPE_1B 3 /* Program item number */
+#define RDS_GROUP_TYPE_2A 4 /* Radio Text */
+#define RDS_GROUP_TYPE_2B 5 /* Radio Text */
+#define RDS_GROUP_TYPE_3A 6 /* Application identification for ODA
+ * only */
+#define RDS_GROUP_TYPE_3B 7 /* Open data applications */
+#define RDS_GROUP_TYPE_4A 8 /* Clock-time and date */
+#define RDS_GROUP_TYPE_4B 9 /* Open data applications */
+#define RDS_GROUP_TYPE_5A 10 /* Transparent Data Channels (32 channels)
+ * or ODA */
+#define RDS_GROUP_TYPE_5B 11 /* Transparent Data Channels (32 channels)
+ * or ODA */
+#define RDS_GROUP_TYPE_6A 12 /* In House Applications or ODA */
+#define RDS_GROUP_TYPE_6B 13 /* In House Applications or ODA */
+#define RDS_GROUP_TYPE_7A 14 /* Radio Paging or ODA */
+#define RDS_GROUP_TYPE_7B 15 /* Open Data Applications */
+#define RDS_GROUP_TYPE_8A 16 /* Traffic Message Channel or ODA */
+#define RDS_GROUP_TYPE_8B 17 /* Open Data Applications */
+#define RDS_GROUP_TYPE_9A 18 /* Emergency warning system or ODA */
+#define RDS_GROUP_TYPE_9B 19 /* Open Data Applications */
+#define RDS_GROUP_TYPE_10A 20 /* Program Type Name */
+#define RDS_GROUP_TYPE_10B 21 /* Open Data Applications */
+#define RDS_GROUP_TYPE_11A 22 /* Open Data Applications */
+#define RDS_GROUP_TYPE_11B 23 /* Open Data Applications */
+#define RDS_GROUP_TYPE_12A 24 /* Open Data Applications */
+#define RDS_GROUP_TYPE_12B 25 /* Open Data Applications */
+#define RDS_GROUP_TYPE_13A 26 /* Enhanced Radio Paging or ODA */
+#define RDS_GROUP_TYPE_13B 27 /* Open Data Applications */
+#define RDS_GROUP_TYPE_14A 28 /* Enhanced Other Networks information */
+#define RDS_GROUP_TYPE_14B 29 /* Enhanced Other Networks information */
+#define RDS_GROUP_TYPE_15A 30 /* Defined in RBDS */
+#define RDS_GROUP_TYPE_15B 31 /* Fast switching information */
+#define NUM_DEFINED_RDS_GROUPS 32 /* Number of groups defined in RBDS
+ * standard */
+
+/* Structure representing Generic packet of 64 bits. */
+struct rds_group_data {
+ u16 piCode; /* * Program ID */
+ u16 offsetB; /* subject to group type */
+ u16 offsetC; /* subject to group type */
+ u16 offsetD; /* subject to group type */
+};
+
+/* Structure representing Group 0A (Service Name) */
+struct rds_group0A {
+ u16 piCode; /* * Program ID */
+ u16 offsetB; /* subject to group type */
+ u8 freq[2]; /* alt frequency 0=1 */
+ u8 text[2]; /* Name segment */
+};
+
+/* Structure representing Group 0B (Service Name) */
+struct rds_group0B {
+ u16 piCode; /* * Program ID */
+ u16 offsetB; /* subject to group type */
+ u16 piCode_dup; /* Duplicate PI Code */
+ u8 text[2]; /* station text */
+};
+
+/* Structure representing Group 2A (Radio Text) (64 char) */
+struct rds_group2A {
+ u16 piCode; /* * Program ID */
+ u16 offsetB; /* subject to group type */
+ u8 text[4];
+};
+
+/* Structure representing Group 2B (Radio Text) (32 char) */
+struct rds_group2B {
+ u16 piCode; /* * Program ID */
+ u16 offsetB; /* subject to group type */
+ u16 piCode_dup; /* Duplicate PI Code */
+ u8 text[2];
+};
+
+/* Structure representing all groups */
+union rds_msg {
+ struct rds_group2B gt2B;
+ struct rds_group2A gt2A;
+ struct rds_group0B gt0B;
+ struct rds_group0A gt0A;
+ struct rds_group_data gt00;
+};
+
+struct stfm1000_rds_state {
+ struct stfm1000_rds_bitstream bitstream;
+ struct stfm1000_rds_demod demod;
+ struct stfm1000_rds_pkt pkt;
+ struct stfm1000_rds_text text;
+ unsigned int reset_req : 1;
+};
+
+/* callback from rds etc. */
+void stfm1000_rds_reset(struct stfm1000_rds_state *rds);
+void stfm1000_rds_start(struct stfm1000_rds_state *rds);
+void stfm1000_rds_stop(struct stfm1000_rds_state *rds);
+
+/* call these from the monitor thread, but with interrupts disabled */
+int stfm1000_rds_mix_msg_get(struct stfm1000_rds_state *rds);
+int stfm1000_rds_mix_msg_processed(struct stfm1000_rds_state *rds,
+ int mix_msg);
+int stfm1000_rds_sdnominal_msg_get(struct stfm1000_rds_state *rds);
+int stfm1000_rds_sdnominal_msg_processed(struct stfm1000_rds_state *rds,
+ int sdnominal_msg);
+int stfm1000_rds_bits_available(struct stfm1000_rds_state *rds);
+int stmf1000_rds_get_bit(struct stfm1000_rds_state *rds);
+
+/* called from audio handler (interrupt) */
+void stfm1000_rds_demod(struct stfm1000_rds_state *rds, const u16 *dri_data,
+ int total);
+
+/* call these from monitor thread, interrupts enabled */
+void stfm1000_rds_packet_bit(struct stfm1000_rds_state *rds, int bit);
+int stfm1000_rds_packet_dequeue(struct stfm1000_rds_state *rds, u8 *buf);
+void stfm1000_rds_process_packet(struct stfm1000_rds_state *rds, u8 *buffer);
+
+static inline int stfm1000_rds_get_reset_req(struct stfm1000_rds_state *rds)
+{
+ return rds->reset_req;
+}
+
+/* GROUP_TYPE 0A-0B */
+int stfm1000_rds_get_ps(struct stfm1000_rds_state *rds, u8 *buffer,
+ int bufsize);
+
+/* GROUP_TYPE 2A */
+int stfm1000_rds_get_text(struct stfm1000_rds_state *rds, u8 *buffer,
+ int bufsize);
+
+#endif
diff --git a/drivers/media/radio/stfm1000/stfm1000-regs.h b/drivers/media/radio/stfm1000/stfm1000-regs.h
new file mode 100644
index 000000000000..c9476b7d67e1
--- /dev/null
+++ b/drivers/media/radio/stfm1000/stfm1000-regs.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 STFM1000_REGS_H
+#define STFM1000_REGS_H
+
+/* registers */
+#define STFM1000_TUNE1 0x00
+#define STFM1000_SDNOMINAL 0x04
+#define STFM1000_PILOTTRACKING 0x08
+#define STFM1000_INITIALIZATION1 0x10
+#define STFM1000_INITIALIZATION2 0x14
+#define STFM1000_INITIALIZATION3 0x18
+#define STFM1000_INITIALIZATION4 0x1C
+#define STFM1000_INITIALIZATION5 0x20
+#define STFM1000_INITIALIZATION6 0x24
+#define STFM1000_REF 0x28
+#define STFM1000_LNA 0x2C
+#define STFM1000_MIXFILT 0x30
+#define STFM1000_CLK1 0x34
+#define STFM1000_CLK2 0x38
+#define STFM1000_ADC 0x3C
+#define STFM1000_AGC_CONTROL1 0x44
+#define STFM1000_AGC_CONTROL2 0x48
+#define STFM1000_DATAPATH 0x5C
+#define STFM1000_RMS 0x60
+#define STFM1000_AGC_STAT 0x64
+#define STFM1000_SIGNALQUALITY 0x68
+#define STFM1000_DCEST 0x6C
+#define STFM1000_RSSI_TONE 0x70
+#define STFM1000_PILOTCORRECTION 0x74
+#define STFM1000_ATTENTION 0x78
+#define STFM1000_CLK3 0x7C
+#define STFM1000_CHIPID 0x80
+
+/* number of registers */
+#define STFM1000_NUM_REGS ((0x80 + 4) / 4)
+
+#define STFM1000_FREQUENCY_100KHZ_MIN 758
+#define STFM1000_FREQUENCY_100KHZ_RANGE 325
+#define STFM1000_FREQUENCY_100KHZ_MAX (STFM1000_FREQUENCY_100KHZ_MIN + \
+ STFM1000_FREQUENCY_100KHZ_RANGE)
+
+#define STFM1000_TUNE1_B2_MIX 0x001C0000
+#define STFM1000_TUNE1_CICOSR 0x00007E00
+#define STFM1000_TUNE1_PLL_DIV 0x000001FF
+
+#define STFM1000_CHIP_REV_TA1 0x00000001
+#define STFM1000_CHIP_REV_TA2 0x00000002
+#define STFM1000_CHIP_REV_TB1 0x00000011
+#define STFM1000_CHIP_REV_TB2 0x00000012
+
+/* DATAPATH bits we use */
+#define STFM1000_DP_EN 0x01000000
+#define STFM1000_DB_ACCEPT 0x00010000
+#define STFM1000_SAI_CLK_DIV_MASK 0x7c
+#define STFM1000_SAI_CLK_DIV_SHIFT 2
+#define STFM1000_SAI_CLK_DIV(x) \
+ (((x) << STFM1000_SAI_CLK_DIV_SHIFT) & STFM1000_SAI_CLK_DIV_MASK)
+#define STFM1000_SAI_EN 0x00000001
+
+/* AGC_CONTROL1 bits we use */
+#define STFM1000_B2_BYPASS_AGC_CTL 0x00004000
+#define STFM1000_B2_BYPASS_FILT_MASK 0x0000000C
+#define STFM1000_B2_BYPASS_FILT_SHIFT 2
+#define STFM1000_B2_BYPASS_FILT(x) \
+ (((x) << STFM1000_B2_BYPASS_FILT_SHIFT) & STFM1000_B2_BYPASS_FILT_MASK)
+#define STFM1000_B2_LNATH_MASK 0x001F0000
+#define STFM1000_B2_LNATH_SHIFT 16
+#define STFM1000_B2_LNATH(x) \
+ (((x) << STFM1000_B2_LNATH_SHIFT) & STFM1000_B2_LNATH_MASK)
+
+/* AGC_STAT bits we use */
+#define STFM1000_AGCOUT_STAT_MASK 0x1F000000
+#define STFM1000_AGCOUT_STAT_SHIFT 24
+#define STFM1000_LNA_RMS_MASK 0x00001F00
+#define STFM1000_LNA_RMS_SHIFT 8
+
+/* PILOTTRACKING bits we use */
+#define STFM1000_B2_PILOTTRACKING_EN 0x00008000
+#define STFM1000_B2_PILOTLPF_TIMECONSTANT_MASK 0x00000f00
+#define STFM1000_B2_PILOTLPF_TIMECONSTANT_SHIFT 8
+#define STFM1000_B2_PILOTLPF_TIMECONSTANT(x) \
+ (((x) << STFM1000_B2_PILOTLPF_TIMECONSTANT_SHIFT) & \
+ STFM1000_B2_PILOTLPF_TIMECONSTANT_MASK)
+#define STFM1000_B2_PFDSCALE_MASK 0x000000f0
+#define STFM1000_B2_PFDSCALE_SHIFT 4
+#define STFM1000_B2_PFDSCALE(x) \
+ (((x) << STFM1000_B2_PFDSCALE_SHIFT) & STFM1000_B2_PFDSCALE_MASK)
+#define STFM1000_B2_PFDFILTER_SPEEDUP_MASK 0x0000000f
+#define STFM1000_B2_PFDFILTER_SPEEDUP_SHIFT 0
+#define STFM1000_B2_PFDFILTER_SPEEDUP(x) \
+ (((x) << STFM1000_B2_PFDFILTER_SPEEDUP_SHIFT) & \
+ STFM1000_B2_PFDFILTER_SPEEDUP_MASK)
+
+/* PILOTCORRECTION bits we use */
+#define STFM1000_PILOTEST_TA2_MASK 0xff000000
+#define STFM1000_PILOTEST_TA2_SHIFT 24
+#define STFM1000_PILOTEST_TB2_MASK 0xfe000000
+#define STFM1000_PILOTEST_TB2_SHIFT 25
+
+/* INITIALIZATION1 bits we use */
+#define STFM1000_B2_BYPASS_FILT_MASK 0x0000000C
+#define STFM1000_B2_BYPASS_FILT_SHIFT 2
+#define STFM1000_B2_BYPASS_FILT(x) \
+ (((x) << STFM1000_B2_BYPASS_FILT_SHIFT) & STFM1000_B2_BYPASS_FILT_MASK)
+
+/* INITIALIZATION2 bits we use */
+#define STFM1000_DRI_CLK_EN 0x80000000
+#define STFM1000_DEEMPH_50_75B 0x00000100
+#define STFM1000_RDS_ENABLE 0x00100000
+#define STFM1000_RDS_MIXOFFSET 0x00200000
+
+/* INITIALIZATION3 bits we use */
+#define STFM1000_B2_NEAR_CHAN_MIX_MASK 0x1c000000
+#define STFM1000_B2_NEAR_CHAN_MIX_SHIFT 26
+#define STFM1000_B2_NEAR_CHAN_MIX(x) \
+ (((x) << STFM1000_B2_NEAR_CHAN_MIX_SHIFT) & \
+ STFM1000_B2_NEAR_CHAN_MIX_MASK)
+
+/* CLK1 bits we use */
+#define STFM1000_ENABLE_TAPDELAYFIX 0x00000020
+
+/* REF bits we use */
+#define STFM1000_LNA_AMP1_IMPROVE_DISTORTION 0x08000000
+
+/* LNA bits we use */
+#define STFM1000_AMP2_IMPROVE_DISTORTION 0x08000000
+#define STFM1000_ANTENNA_TUNECAP_MASK 0x001F0000
+#define STFM1000_ANTENNA_TUNECAP_SHIFT 16
+#define STFM1000_ANTENNA_TUNECAP(x) \
+ (((x) << STFM1000_ANTENNA_TUNECAP_SHIFT) & \
+ STFM1000_ANTENNA_TUNECAP_MASK)
+#define STFM1000_IBIAS2_UP 0x00000008
+#define STFM1000_IBIAS2_DN 0x00000004
+#define STFM1000_IBIAS1_UP 0x00000002
+#define STFM1000_IBIAS1_DN 0x00000001
+#define STFM1000_USEATTEN_MASK 0x00600000
+#define STFM1000_USEATTEN_SHIFT 21
+#define STFM1000_USEATTEN(x) \
+ (((x) << STFM1000_USEATTEN_SHIFT) & STFM1000_USEATTEN_MASK)
+
+/* SIGNALQUALITY bits we use */
+#define STFM1000_NEAR_CHAN_AMPLITUDE_MASK 0x0000007F
+#define STFM1000_NEAR_CHAN_AMPLITUDE_SHIFT 0
+#define STFM1000_NEAR_CHAN_AMPLITUDE(x) \
+ (((x) << STFM1000_NEAR_CHAN_AMPLITUDE_SHIFT) & \
+ STFM1000_NEAR_CHAN_AMPLITUDE_MASK)
+
+/* precalc tables elements */
+struct stfm1000_tune1 {
+ unsigned int tune1; /* at least 32 bit */
+ unsigned int sdnom;
+};
+
+#endif
diff --git a/drivers/media/radio/stfm1000/stfm1000.h b/drivers/media/radio/stfm1000/stfm1000.h
new file mode 100644
index 000000000000..5ae6f38db3b0
--- /dev/null
+++ b/drivers/media/radio/stfm1000/stfm1000.h
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 STFM1000_H
+#define STFM1000_H
+
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/i2c.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/irq.h>
+#include <media/videobuf-dma-sg.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <mach/dma.h>
+
+#include "stfm1000-regs.h"
+
+#include "stfm1000-filter.h"
+#include "stfm1000-rds.h"
+
+struct stfm1000 {
+ struct list_head devlist;
+ int idx;
+
+ struct i2c_client *client;
+ struct video_device radio;
+
+ /* alsa */
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct snd_pcm_substream *substream;
+ struct stmp3xxx_dma_descriptor *dma;
+ int desc_num;
+ int dma_ch;
+ int dma_irq;
+ int attn_irq;
+
+ struct mutex state_lock;
+ int read_count;
+ int read_offset;
+ int blocks;
+ int blksize;
+ int bufsize;
+
+ struct mutex deffered_work_lock;
+ struct execute_work snd_capture_start_work;
+ struct execute_work snd_capture_stop_work;
+
+ int now_recording;
+ int alsa_initialized;
+ int stopping_recording;
+
+ /* actual DRI buffer */
+ dma_addr_t dri_phys;
+ void *dri_buf;
+ int dri_bufsz;
+
+ /* various */
+ u16 curvol;
+ int users;
+ int removed;
+ struct mutex xfer_lock;
+ u8 revid;
+
+ unsigned int dbgflg;
+
+ /* shadow registers */
+ u32 shadow_regs[STFM1000_NUM_REGS];
+ u32 reg_rw_set[(STFM1000_NUM_REGS + 31) / 32];
+ u32 reg_ra_set[(STFM1000_NUM_REGS + 31) / 32];
+ u32 reg_dirty_set[(STFM1000_NUM_REGS + 31) / 32];
+
+ /* tuning parameters (not everything is used for now) */
+ u16 tune_rssi_th; /* sd_ctl_TuneRssiTh_u16 */
+ u16 tune_mpx_dc_th; /* sd_ctl_TuneMpxDcTh_u16 */
+ u16 adj_chan_th; /* sd_ctl_AdjChanTh_u16 */
+ u16 pilot_est_th; /* sd_ctl_PilotEstTh_u16 */
+ u16 coef_lna_turn_off_th; /* sd_ctl_pCoefLnaTurnOffTh_u16 */
+ u16 coef_lna_turn_on_th; /* sd_ctl_pCoefLnaTurnOnTh_u16 */
+ u16 reg_agc_ref_lna_off; /* sd_ctl_pRegAgcRefLnaOff_u16 */
+ u16 reg_agc_ref_lna_on; /* sd_ctl_pRegAgcRefLnaOn_u16 */
+
+ u32 sdnominal_pivot; /* sd_ctl_SdnominalData_u32 */
+
+ /* jiffies of the next monitor cycle */
+ unsigned long next_quality_monitor;
+ unsigned long next_agc_monitor;
+
+ unsigned int mute : 1; /* XXX */
+ unsigned int lna_driving : 1; /* sd_ctl_LnaDriving_u1 */
+ unsigned int weak_signal : 1; /* sd_ctl_WeakSignal_u1 */
+ unsigned int is_station : 1; /* XXX */
+ unsigned int force_mono : 1; /* XXX */
+ unsigned int signal_indicator : 1; /* XXX */
+ unsigned int stereo_indicator : 1; /* XXX */
+ unsigned int agc_monitor : 1; /* XXX */
+ unsigned int quality_monitor : 1; /* XXX */
+ unsigned int pilot_present : 1; /* sd_ctl_PilotPresent_u1 */
+ unsigned int prev_pilot_present : 1; /* XXX */
+ unsigned int stereo : 1;
+ unsigned int active : 1; /* set when audio enabled */
+ unsigned int rds_enable : 1; /* set when rds is enabled */
+ unsigned int rds_present : 1; /* RDS info present */
+ unsigned int rds_sync : 1; /* RDS force sync */
+ unsigned int rds_demod_running : 1; /* RDS demod is running ATM */
+ unsigned int rds_sdnominal_adapt : 1; /* adapt for better recept. */
+ unsigned int rds_phase_pop : 1; /* enable phase pop */
+ unsigned int rds_info : 1; /* print debugging info RDS */
+ unsigned int tuning_grid_50KHz : 1; /* tuning grid of 50Khz */
+ u32 rssi; /* rssi last decoded frame */
+ u16 rssi_dc_est_log;
+ u16 signal_strength; /* is rssi_dc_est_log */
+ u16 rds_signal_th; /* RDS threshold */
+ s16 mpx_dc; /* sd_ctl_ShadowToneData_i16 */
+
+ u32 tune_cap_a_f; /* float! sd_ctl_TuneCapA_f */
+ u32 tune_cap_b_f; /* float! sd_ctl_TuneCapB_f */
+
+ int monitor_period; /* period of the monitor */
+ int quality_monitor_period; /* update period in ms */
+ int agc_monitor_period; /* update period in ms */
+
+ int georegion; /* current graphical region */
+
+ /* last tuned frequency */
+ int freq; /* 88.0 = 8800 */
+
+ /* weak signal processing filter state */
+ struct stfm1000_filter_parms filter_parms;
+
+ /* state of rds */
+ spinlock_t rds_lock;
+ struct stfm1000_rds_state rds_state;
+ unsigned int rds_pkt_bad;
+ unsigned int rds_pkt_good;
+ unsigned int rds_pkt_recovered;
+ unsigned int rds_pkt_lost_sync;
+ unsigned int rds_bit_overruns;
+
+ /* monitor thread */
+ wait_queue_head_t thread_wait;
+ unsigned long thread_events;
+ struct task_struct *thread;
+};
+
+#define EVENT_RDS_BITS 0
+#define EVENT_RDS_MIXFILT 1
+#define EVENT_RDS_SDNOMINAL 2
+#define EVENT_RDS_RESET 3
+
+#define STFM1000_DBGFLG_I2C (1 << 0)
+
+static inline struct stfm1000 *stfm1000_from_file(struct file *file)
+{
+ return container_of(video_devdata(file), struct stfm1000, radio);
+}
+
+/* in stfm1000-i2c.c */
+
+/* setup reg set */
+void stfm1000_setup_reg_set(struct stfm1000 *stfm1000);
+
+/* direct access to registers bypassing the shadow register set */
+int stfm1000_raw_read(struct stfm1000 *stfm1000, int reg, u32 *value);
+int stfm1000_raw_write(struct stfm1000 *stfm1000, int reg, u32 value);
+
+/* access using the shadow register set */
+int stfm1000_write(struct stfm1000 *stfm1000, int reg, u32 value);
+int stfm1000_read(struct stfm1000 *stfm1000, int reg, u32 *value);
+int stfm1000_write_masked(struct stfm1000 *stfm1000, int reg, u32 value,
+ u32 mask);
+int stfm1000_set_bits(struct stfm1000 *stfm1000, int reg, u32 value);
+int stfm1000_clear_bits(struct stfm1000 *stfm1000, int reg, u32 value);
+
+struct stfm1000_reg {
+ unsigned int regno;
+ u32 value;
+};
+
+#define STFM1000_REG_END -1
+#define STFM1000_REG_DELAY -2
+
+#define STFM1000_REG_SET_BITS_MASK 0x1000
+#define STFM1000_REG_CLEAR_BITS_MASK 0x2000
+
+#define STFM1000_REG(r, v) \
+ { .regno = STFM1000_ ## r , .value = (v) }
+
+#define STFM1000_END \
+ { .regno = STFM1000_REG_END }
+
+#define STFM1000_DELAY(x) \
+ { .regno = STFM1000_REG_DELAY, .value = (x) }
+
+#define STFM1000_REG_SETBITS(r, v) \
+ { .regno = STFM1000_ ## r | STFM1000_REG_SET_BITS_MASK, \
+ .value = (v) }
+
+#define STFM1000_REG_CLRBITS(r, v) \
+ { .regno = STFM1000_ ## r | STFM1000_REG_CLEAR_BITS_MASK, \
+ .value = (v) }
+
+int stfm1000_write_regs(struct stfm1000 *stfm1000,
+ const struct stfm1000_reg *reg);
+
+/* in stfm1000-precalc.c */
+extern const struct stfm1000_tune1
+stfm1000_tune1_table[STFM1000_FREQUENCY_100KHZ_RANGE];
+
+/* exported for use by alsa driver */
+
+struct stfm1000_dri_sample {
+ /* L+R */
+ u16 l_plus_r;
+ /* L-R */
+ u16 l_minus_r;
+ /* Rx signal strength channel */
+ u16 rssi;
+ /* Radio data service channel */
+ u16 rds;
+};
+
+struct stfm1000_alsa_ops {
+ int (*init)(struct stfm1000 *stfm1000);
+ void (*release)(struct stfm1000 *stfm1000);
+ void (*dma_irq)(struct stfm1000 *stfm1000);
+ void (*attn_irq)(struct stfm1000 *stfm1000);
+};
+
+extern struct list_head stfm1000_devlist;
+extern struct stfm1000_alsa_ops *stfm1000_alsa_ops;
+
+/* needed for setting the interrupt handlers from alsa */
+irqreturn_t stfm1000_dri_attn_irq(int irq, void *dev_id);
+irqreturn_t stfm1000_dri_dma_irq(int irq, void *dev_id);
+void stfm1000_decode_block(struct stfm1000 *stfm1000, const s16 *src, s16 *dst, int count);
+void stfm1000_take_down(struct stfm1000 *stfm1000);
+void stfm1000_bring_up(struct stfm1000 *stfm1000);
+void stfm1000_tune_current(struct stfm1000 *stfm1000);
+
+void stfm1000_monitor_signal(struct stfm1000 *stfm1000, int bit);
+
+#endif
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 47102c2c8250..3e1feb550e4b 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -531,6 +531,45 @@ config VIDEO_W9966
Check out <file:Documentation/video4linux/w9966.txt> for more
information.
+config VIDEO_MXC_CAMERA
+ tristate "MXC Video For Linux Camera"
+ depends on VIDEO_DEV && ARCH_MXC
+ default y
+ ---help---
+ This is the video4linux2 capture driver based on MXC IPU/eMMA module.
+
+source "drivers/media/video/mxc/capture/Kconfig"
+
+config VIDEO_MXC_OUTPUT
+ tristate "MXC Video For Linux Video Output"
+ depends on VIDEO_DEV && ARCH_MXC
+ default y
+ ---help---
+ This is the video4linux2 output driver based on MXC IPU/eMMA module.
+
+source "drivers/media/video/mxc/output/Kconfig"
+
+config VIDEO_PXP
+ tristate "STMP3xxx PxP"
+ depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_STMP3XXX
+ select VIDEOBUF_DMA_CONTIG
+ ---help---
+ This is a video4linux driver for the Freescale PxP
+ (Pixel Pipeline). This module supports output overlay of
+ the STMP3xxx framebuffer on a video stream.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pxp.
+
+config VIDEO_MXC_OPL
+ tristate
+ depends on VIDEO_DEV && ARCH_MXC
+ default n
+ ---help---
+ This is the ARM9-optimized OPL (Open Primitives Library) software
+ rotation/mirroring implementation. It may be used by eMMA video
+ capture or output device.
+
config VIDEO_CPIA
tristate "CPiA Video For Linux"
depends on VIDEO_V4L1
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 16962f3aa157..fe2152b79743 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -57,6 +57,14 @@ obj-$(CONFIG_VIDEO_ZORAN) += zoran/
obj-$(CONFIG_VIDEO_PMS) += pms.o
obj-$(CONFIG_VIDEO_VINO) += vino.o indycam.o
obj-$(CONFIG_VIDEO_STRADIS) += stradis.o
+obj-$(CONFIG_VIDEO_MXC_IPU_CAMERA) += mxc/capture/
+obj-$(CONFIG_VIDEO_MXC_EMMA_CAMERA) += mxc/capture/
+obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += mxc/capture/
+obj-$(CONFIG_VIDEO_MXC_IPU_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_EMMA_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_OPL) += mxc/opl/
+obj-$(CONFIG_VIDEO_PXP) += pxp.o
obj-$(CONFIG_VIDEO_CPIA) += cpia.o
obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
diff --git a/drivers/media/video/mxc/capture/Kconfig b/drivers/media/video/mxc/capture/Kconfig
new file mode 100644
index 000000000000..adab0a886f36
--- /dev/null
+++ b/drivers/media/video/mxc/capture/Kconfig
@@ -0,0 +1,123 @@
+if VIDEO_MXC_CAMERA
+
+menu "MXC Camera/V4L2 PRP Features support"
+config VIDEO_MXC_IPU_CAMERA
+ bool
+ depends on VIDEO_MXC_CAMERA && MXC_IPU
+ default y
+
+config VIDEO_MXC_EMMA_CAMERA
+ tristate "MX27 eMMA support"
+ depends on VIDEO_MXC_CAMERA && MXC_EMMA && FB_MXC_SYNC_PANEL
+ select VIDEO_MXC_OPL
+ default y
+
+config VIDEO_MXC_CSI_CAMERA
+ tristate "MX25 CSI camera support"
+ depends on !VIDEO_MXC_EMMA_CAMERA
+
+config VIDEO_MXC_CSI_DMA
+ bool "CSI-DMA Still Image Capture support"
+ depends on VIDEO_MXC_EMMA_CAMERA
+ default n
+ ---help---
+ Use CSI-DMA method instead of CSI-PrP link to capture still image. This allows
+ to use less physical contiguous memory to capture big resolution still image. But
+ with this method the CSC (Color Space Conversion) and resize are not supported.
+ If unsure, say N.
+
+choice
+ prompt "Select Camera/TV Decoder"
+ default MXC_CAMERA_OV3640
+ depends on VIDEO_MXC_CAMERA
+
+config MXC_CAMERA_MC521DA
+ tristate "Magnachip mc521da camera support"
+ select I2C_MXC
+ depends on VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the mc521da Camera with your MXC system, say Y here.
+
+config MXC_EMMA_CAMERA_MICRON111
+ tristate "Micron mt9v111 camera support with eMMA"
+ select I2C_MXC
+ depends on VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the mt9v111 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV2640_EMMA
+ tristate "OmniVision ov2640 camera support with eMMA"
+ depends on VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the ov2640 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_MICRON111
+ tristate "Micron mt9v111 camera support"
+ select I2C_MXC
+ depends on ! VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the mt9v111 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV2640
+ tristate "OmniVision ov2640 camera support"
+ depends on !VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the ov2640 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_OV3640
+ tristate "OmniVision ov3640 camera support"
+ depends on !VIDEO_MXC_EMMA_CAMERA
+ ---help---
+ If you plan to use the ov3640 Camera with your MXC system, say Y here.
+
+config MXC_TVIN_ADV7180
+ tristate "Analog Device adv7180 TV Decoder Input support"
+ depends on MACH_MX35_3DS
+ ---help---
+ If you plan to use the adv7180 video decoder with your MXC system, say Y here.
+
+endchoice
+
+config MXC_IPU_PRP_VF_SDC
+ tristate "Pre-Processor VF SDC library"
+ depends on VIDEO_MXC_IPU_CAMERA && FB_MXC_SYNC_PANEL
+ default y
+ ---help---
+ Use case PRP_VF_SDC:
+ Preprocessing image from smart sensor for viewfinder and
+ displaying it on synchronous display with SDC use case.
+ If SDC BG is selected, Rotation will not be supported.
+ CSI -> IC (PRP VF) -> MEM
+ MEM -> IC (ROT) -> MEM
+ MEM -> SDC (FG/BG)
+
+config MXC_IPU_PRP_VF_ADC
+ tristate "Pre-Processor VF ADC library"
+ depends on VIDEO_MXC_IPU_CAMERA && FB_MXC_ASYNC_PANEL
+ default y
+ ---help---
+ Use case PRP_VF_ADC:
+ Preprocessing image from smart sensor for viewfinder and
+ displaying it on asynchronous display.
+ CSI -> IC (PRP VF) -> ADC2
+
+config MXC_IPU_PRP_ENC
+ tristate "Pre-processor Encoder library"
+ depends on VIDEO_MXC_IPU_CAMERA
+ default y
+ ---help---
+ Use case PRP_ENC:
+ Preprocessing image from smart sensor for encoder.
+ CSI -> IC (PRP ENC) -> MEM
+
+config MXC_IPU_CSI_ENC
+ tristate "IPU CSI Encoder library"
+ depends on VIDEO_MXC_IPU_CAMERA
+ default y
+ ---help---
+ Use case IPU_CSI_ENC:
+ Get raw image with CSI from smart sensor for encoder.
+ CSI -> MEM
+endmenu
+
+endif
diff --git a/drivers/media/video/mxc/capture/Makefile b/drivers/media/video/mxc/capture/Makefile
new file mode 100644
index 000000000000..112923c8fc8f
--- /dev/null
+++ b/drivers/media/video/mxc/capture/Makefile
@@ -0,0 +1,39 @@
+ifeq ($(CONFIG_VIDEO_MXC_IPU_CAMERA),y)
+ obj-$(CONFIG_VIDEO_MXC_CAMERA) += mxc_v4l2_capture.o
+ obj-$(CONFIG_MXC_IPU_PRP_VF_ADC) += ipu_prp_vf_adc.o
+ obj-$(CONFIG_MXC_IPU_PRP_VF_SDC) += ipu_prp_vf_sdc.o ipu_prp_vf_sdc_bg.o
+ obj-$(CONFIG_MXC_IPU_PRP_ENC) += ipu_prp_enc.o ipu_still.o
+ obj-$(CONFIG_MXC_IPU_CSI_ENC) += ipu_csi_enc.o ipu_still.o
+endif
+
+obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += fsl_csi.o csi_v4l2_capture.o
+
+mx27_capture-objs := mx27_prphw.o mx27_prpsw.o emma_v4l2_capture.o
+obj-$(CONFIG_VIDEO_MXC_EMMA_CAMERA) += mx27_csi.o mx27_capture.o
+
+mc521da_camera-objs := mc521da.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_MC521DA) += mc521da_camera.o
+
+emma_mt9v111_camera-objs := emma_mt9v111.o sensor_clock.o
+obj-$(CONFIG_MXC_EMMA_CAMERA_MICRON111) += emma_mt9v111_camera.o
+
+mt9v111_camera-objs := mt9v111.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_MICRON111) += mt9v111_camera.o
+
+hv7161_camera-objs := hv7161.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_HV7161) += hv7161_camera.o
+
+s5k3aaex_camera-objs := s5k3aaex.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_S5K3AAEX) += s5k3aaex_camera.o
+
+emma_ov2640_camera-objs := emma_ov2640.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV2640_EMMA) += emma_ov2640_camera.o
+
+ov2640_camera-objs := ov2640.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV2640) += ov2640_camera.o
+
+ov3640_camera-objs := ov3640.o sensor_clock.o
+obj-$(CONFIG_MXC_CAMERA_OV3640) += ov3640_camera.o
+
+adv7180_tvin-objs := adv7180.o sensor_clock.o
+obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o
diff --git a/drivers/media/video/mxc/capture/adv7180.c b/drivers/media/video/mxc/capture/adv7180.c
new file mode 100644
index 000000000000..07a68ecaa0a5
--- /dev/null
+++ b/drivers/media/video/mxc/capture/adv7180.c
@@ -0,0 +1,981 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file adv7180.c
+ *
+ * @brief Analog Device ADV7180 video decoder functions
+ *
+ * @ingroup Camera
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/wait.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+
+static struct regulator *dvddio_regulator;
+static struct regulator *dvdd_regulator;
+static struct regulator *avdd_regulator;
+static struct regulator *pvdd_regulator;
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+static int adv7180_probe(struct i2c_client *adapter,
+ const struct i2c_device_id *id);
+static int adv7180_detach(struct i2c_client *client);
+
+static const struct i2c_device_id adv7180_id[] = {
+ {"adv7180", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, adv7180_id);
+
+static struct i2c_driver adv7180_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7180",
+ },
+ .probe = adv7180_probe,
+ .remove = adv7180_detach,
+ .id_table = adv7180_id,
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+ struct v4l2_int_device *v4l2_int_device;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ struct v4l2_captureparm streamcap;
+ bool on;
+
+ /* control settings */
+ int brightness;
+ int hue;
+ int contrast;
+ int saturation;
+ int red;
+ int green;
+ int blue;
+ int ae_mode;
+
+ v4l2_std_id std_id;
+} adv7180_data;
+
+/*! List of input video formats supported. The video formats is corresponding
+ * with v4l2 id in video_fmt_t
+ */
+typedef enum {
+ ADV7180_NTSC = 0, /*!< Locked on (M) NTSC video signal. */
+ ADV7180_PAL, /*!< (B, G, H, I, N)PAL video signal. */
+ ADV7180_NOT_LOCKED, /*!< Not locked on a signal. */
+} video_fmt_idx;
+
+/*! Number of video standards supported (including 'not locked' signal). */
+#define ADV7180_STD_MAX (ADV7180_PAL + 1)
+
+/*! Video format structure. */
+typedef struct {
+ int v4l2_id; /*!< Video for linux ID. */
+ char name[16]; /*!< Name (e.g., "NTSC", "PAL", etc.) */
+ u16 raw_width; /*!< Raw width. */
+ u16 raw_height; /*!< Raw height. */
+ u16 active_width; /*!< Active width. */
+ u16 active_height; /*!< Active height. */
+} video_fmt_t;
+
+/*! Description of video formats supported.
+ *
+ * PAL: raw=720x625, active=720x576.
+ * NTSC: raw=720x525, active=720x480.
+ */
+static video_fmt_t video_fmts[] = {
+ { /*! NTSC */
+ .v4l2_id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .raw_width = 720 - 1, /* SENS_FRM_WIDTH */
+ .raw_height = 288 - 1, /* SENS_FRM_HEIGHT */
+ .active_width = 720, /* ACT_FRM_WIDTH plus 1 */
+ .active_height = (480 / 2), /* ACT_FRM_WIDTH plus 1 */
+ },
+ { /*! (B, G, H, I, N) PAL */
+ .v4l2_id = V4L2_STD_PAL,
+ .name = "PAL",
+ .raw_width = 720 - 1,
+ .raw_height = (576 / 2) + 24 * 2 - 1,
+ .active_width = 720,
+ .active_height = (576 / 2),
+ },
+ { /*! Unlocked standard */
+ .v4l2_id = V4L2_STD_ALL,
+ .name = "Autodetect",
+ .raw_width = 720 - 1,
+ .raw_height = (576 / 2) + 24 * 2 - 1,
+ .active_width = 720,
+ .active_height = (576 / 2),
+ },
+};
+
+/*!* Standard index of ADV7180. */
+static video_fmt_idx video_idx = ADV7180_PAL;
+
+/*! @brief This mutex is used to provide mutual exclusion.
+ *
+ * Create a mutex that can be used to provide mutually exclusive
+ * read/write access to the globally accessible data structures
+ * and variables that were defined above.
+ */
+static DECLARE_MUTEX(mutex);
+
+#define IF_NAME "adv7180"
+#define ADV7180_INPUT_CTL 0x00 /* Input Control */
+#define ADV7180_STATUS_1 0x10 /* Status #1 */
+#define ADV7180_BRIGHTNESS 0x0a /* Brightness */
+#define ADV7180_IDENT 0x11 /* IDENT */
+#define ADV7180_VSYNC_FIELD_CTL_1 0x31 /* VSYNC Field Control #1 */
+#define ADV7180_MANUAL_WIN_CTL 0x3d /* Manual Window Control */
+#define ADV7180_SD_SATURATION_CB 0xe3 /* SD Saturation Cb */
+#define ADV7180_SD_SATURATION_CR 0xe4 /* SD Saturation Cr */
+#define ADV7180_PWR_MNG 0x0f /* Power Management */
+
+/* supported controls */
+/* This hasn't been fully implemented yet.
+ * This is how it should work, though. */
+static struct v4l2_queryctrl adv7180_qctrl[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0, /* check this value */
+ .maximum = 255, /* check this value */
+ .step = 1, /* check this value */
+ .default_value = 127, /* check this value */
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0, /* check this value */
+ .maximum = 255, /* check this value */
+ .step = 0x1, /* check this value */
+ .default_value = 127, /* check this value */
+ .flags = 0,
+ }
+};
+
+/***********************************************************************
+ * I2C transfert.
+ ***********************************************************************/
+
+/*! Read one register from a ADV7180 i2c slave device.
+ *
+ * @param *reg register in the device we wish to access.
+ *
+ * @return 0 if success, an error code otherwise.
+ */
+static inline int adv7180_read(u8 reg)
+{
+ int val;
+ val = i2c_smbus_read_byte_data(adv7180_data.i2c_client, reg);
+ if (val < 0) {
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "%s:read reg error: reg=%2x \n", __func__, reg);
+ return -1;
+ }
+ return val;
+}
+
+/*! Write one register of a ADV7180 i2c slave device.
+ *
+ * @param *reg register in the device we wish to access.
+ *
+ * @return 0 if success, an error code otherwise.
+ */
+static int adv7180_write_reg(u8 reg, u8 val)
+{
+ if (i2c_smbus_write_byte_data(adv7180_data.i2c_client, reg, val) < 0) {
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "%s:write reg error:reg=%2x,val=%2x\n", __func__,
+ reg, val);
+ return -1;
+ }
+ return 0;
+}
+
+/***********************************************************************
+ * mxc_v4l2_capture interface.
+ ***********************************************************************/
+
+/*!
+ * Return attributes of current video standard.
+ * Since this device autodetects the current standard, this function also
+ * sets the values that need to be changed if the standard changes.
+ * There is no set std equivalent function.
+ *
+ * @return None.
+ */
+static void adv7180_get_std(v4l2_std_id *std)
+{
+ int tmp;
+ int idx;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_get_std\n");
+
+ /* Read the AD_RESULT to get the detect output video standard */
+ tmp = adv7180_read(ADV7180_STATUS_1) & 0x70;
+
+ down(&mutex);
+ if (tmp == 0x40) {
+ /* PAL */
+ *std = V4L2_STD_PAL;
+ idx = ADV7180_PAL;
+ } else if (tmp == 0) {
+ /*NTSC*/
+ *std = V4L2_STD_NTSC;
+ idx = ADV7180_NTSC;
+ } else {
+ *std = V4L2_STD_ALL;
+ idx = ADV7180_NOT_LOCKED;
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "Got invalid video standard! \n");
+ }
+ up(&mutex);
+
+ /* This assumes autodetect which this device uses. */
+ if (*std != adv7180_data.std_id) {
+ video_idx = idx;
+ adv7180_data.std_id = *std;
+ adv7180_data.pix.width = video_fmts[video_idx].raw_width;
+ adv7180_data.pix.height = video_fmts[video_idx].raw_height;
+ }
+}
+
+/***********************************************************************
+ * IOCTL Functions from v4l2_int_ioctl_desc.
+ ***********************************************************************/
+
+/*!
+ * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num
+ * s: pointer to standard V4L2 device structure
+ * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p. This value is returned in the p
+ * parameter.
+ *
+ * vidioc_int_g_ifparm returns platform-specific information about the
+ * interface settings used by the sensor.
+ *
+ * Called on open.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_ifparm\n");
+
+ if (s == NULL) {
+ pr_err(" ERROR!! no slave device set!\n");
+ return -1;
+ }
+
+ /* Initialize structure to 0s then set any non-0 values. */
+ memset(p, 0, sizeof(*p));
+ p->if_type = V4L2_IF_TYPE_BT656; /* This is the only possibility. */
+ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+ p->u.bt656.nobt_hs_inv = 1;
+
+ /* ADV7180 has a dedicated clock so no clock settings needed. */
+
+ return 0;
+}
+
+/*!
+ * Sets the camera power.
+ *
+ * s pointer to the camera device
+ * on if 1, power is to be turned on. 0 means power is to be turned off
+ *
+ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ * This is called on open, close, suspend and resume.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct sensor *sensor = s->priv;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_s_power\n");
+
+ if (on && !sensor->on) {
+ gpio_sensor_active();
+ if (adv7180_write_reg(ADV7180_PWR_MNG, 0) != 0)
+ return -EIO;
+ } else if (!on && sensor->on) {
+ if (adv7180_write_reg(ADV7180_PWR_MNG, 0x24) != 0)
+ return -EIO;
+ gpio_sensor_inactive();
+ }
+
+ sensor->on = on;
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_parm\n");
+
+ switch (a->type) {
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cparm->capability = sensor->streamcap.capability;
+ cparm->timeperframe = sensor->streamcap.timeperframe;
+ cparm->capturemode = sensor->streamcap.capturemode;
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ break;
+
+ default:
+ pr_debug("ioctl_g_parm:type is unknown %d\n", a->type);
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible. If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ *
+ * This driver cannot change these settings.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_s_parm\n");
+
+ switch (a->type) {
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ break;
+
+ default:
+ pr_debug(" type is unknown - %d\n", a->type);
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct sensor *sensor = s->priv;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_fmt_cap\n");
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" Returning size of %dx%d\n",
+ sensor->pix.width, sensor->pix.height);
+ f->fmt.pix = sensor->pix;
+ break;
+
+ case V4L2_BUF_TYPE_PRIVATE: {
+ v4l2_std_id std;
+ adv7180_get_std(&std);
+ f->fmt.pix.pixelformat = (u32)std;
+ }
+ break;
+
+ default:
+ f->fmt.pix = sensor->pix;
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control information
+ * from the video_control[] array. Otherwise, returns -EINVAL if the
+ * control is not supported.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_queryctrl\n");
+
+ for (i = 0; i < ARRAY_SIZE(adv7180_qctrl); i++)
+ if (qc->id && qc->id == adv7180_qctrl[i].id) {
+ memcpy(qc, &(adv7180_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+
+ return -EINVAL;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array. Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int ret = 0;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_g_ctrl\n");
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_BRIGHTNESS\n");
+ adv7180_data.brightness = adv7180_read(ADV7180_BRIGHTNESS);
+ vc->value = adv7180_data.brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_CONTRAST\n");
+ vc->value = adv7180_data.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_SATURATION\n");
+ adv7180_data.saturation = adv7180_read(ADV7180_SD_SATURATION_CB);
+ vc->value = adv7180_data.saturation;
+ break;
+ case V4L2_CID_HUE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_HUE\n");
+ vc->value = adv7180_data.hue;
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_RED_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_RED_BALANCE\n");
+ vc->value = adv7180_data.red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_BLUE_BALANCE\n");
+ vc->value = adv7180_data.blue;
+ break;
+ case V4L2_CID_GAMMA:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_GAMMA\n");
+ break;
+ case V4L2_CID_EXPOSURE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_EXPOSURE\n");
+ vc->value = adv7180_data.ae_mode;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_AUTOGAIN\n");
+ break;
+ case V4L2_CID_GAIN:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_GAIN\n");
+ break;
+ case V4L2_CID_HFLIP:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_HFLIP\n");
+ break;
+ case V4L2_CID_VFLIP:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_VFLIP\n");
+ break;
+ default:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " Default case\n");
+ vc->value = 0;
+ ret = -EPERM;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array). Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int retval = 0;
+ u8 tmp;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_s_ctrl\n");
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_BRIGHTNESS\n");
+ tmp = vc->value;
+ adv7180_write_reg(ADV7180_BRIGHTNESS, tmp);
+ adv7180_data.brightness = vc->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_CONTRAST\n");
+ break;
+ case V4L2_CID_SATURATION:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_SATURATION\n");
+ tmp = vc->value;
+ adv7180_write_reg(ADV7180_SD_SATURATION_CB, tmp);
+ adv7180_write_reg(ADV7180_SD_SATURATION_CR, tmp);
+ adv7180_data.saturation = vc->value;
+ break;
+ case V4L2_CID_HUE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_HUE\n");
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_RED_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_RED_BALANCE\n");
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_BLUE_BALANCE\n");
+ break;
+ case V4L2_CID_GAMMA:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_GAMMA\n");
+ break;
+ case V4L2_CID_EXPOSURE:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_EXPOSURE\n");
+ break;
+ case V4L2_CID_AUTOGAIN:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_AUTOGAIN\n");
+ break;
+ case V4L2_CID_GAIN:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_GAIN\n");
+ break;
+ case V4L2_CID_HFLIP:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_HFLIP\n");
+ break;
+ case V4L2_CID_VFLIP:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " V4L2_CID_VFLIP\n");
+ break;
+ default:
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ " Default case\n");
+ retval = -EPERM;
+ break;
+ }
+
+ return retval;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_init\n");
+ return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180:ioctl_dev_init\n");
+ return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module.
+ */
+static struct v4l2_int_ioctl_desc adv7180_ioctl_desc[] = {
+
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+
+ /*!
+ * Delinitialise the dev. at slave detach.
+ * The complement of ioctl_dev_init.
+ */
+/* {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *)ioctl_dev_exit}, */
+
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
+/* {vidioc_int_g_needs_reset_num,
+ (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+ {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
+
+ /*!
+ * VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+ */
+/* {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */
+
+ /*!
+ * VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.
+ * This ioctl is used to negotiate the image capture size and
+ * pixel format without actually making it take effect.
+ */
+/* {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+
+ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
+
+ /*!
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
+ {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl},
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
+};
+
+static struct v4l2_int_slave adv7180_slave = {
+ .ioctls = adv7180_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(adv7180_ioctl_desc),
+};
+
+static struct v4l2_int_device adv7180_int_device = {
+ .module = THIS_MODULE,
+ .name = "adv7180",
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &adv7180_slave,
+ },
+};
+
+
+/***********************************************************************
+ * I2C client and driver.
+ ***********************************************************************/
+
+/*! ADV7180 Reset function.
+ *
+ * @return None.
+ */
+static void adv7180_hard_reset(void)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "In adv7180:adv7180_hard_reset\n");
+
+ /*! Driver works fine without explicit register
+ * initialization. Furthermore, initializations takes about 2 seconds
+ * at startup...
+ */
+
+ /*! Set YPbPr input on AIN1,4,5 and normal
+ * operations(autodection of all stds).
+ */
+ adv7180_write_reg(ADV7180_INPUT_CTL, 0x09);
+
+ /*! Datasheet recommends: */
+ adv7180_write_reg(ADV7180_VSYNC_FIELD_CTL_1, 0x02);
+ adv7180_write_reg(ADV7180_MANUAL_WIN_CTL, 0xa2);
+}
+
+/*! ADV7180 I2C attach function.
+ *
+ * @param *adapter struct i2c_adapter *.
+ *
+ * @return Error code indicating success or failure.
+ */
+
+/*!
+ * ADV7180 I2C probe function.
+ * Function set in i2c_driver struct.
+ * Called by insmod.
+ *
+ * @param *adapter I2C adapter descriptor.
+ *
+ * @return Error code indicating success or failure.
+ */
+static int adv7180_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rev_id;
+ int ret = 0;
+ struct mxc_tvin_platform_data *plat_data = client->dev.platform_data;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_probe\n");
+
+ if (plat_data->dvddio_reg) {
+ dvddio_regulator =
+ regulator_get(&client->dev, plat_data->dvddio_reg);
+ if (!IS_ERR_VALUE((unsigned long)dvddio_regulator)) {
+ regulator_set_voltage(dvddio_regulator, 3300000, 3300000);
+ if (regulator_enable(dvddio_regulator) != 0)
+ return -ENODEV;
+ }
+ }
+
+ if (plat_data->dvdd_reg) {
+ dvdd_regulator =
+ regulator_get(&client->dev, plat_data->dvdd_reg);
+ if (!IS_ERR_VALUE((unsigned long)dvdd_regulator)) {
+ regulator_set_voltage(dvdd_regulator, 1800000, 1800000);
+ if (regulator_enable(dvdd_regulator) != 0)
+ return -ENODEV;
+ }
+ }
+
+ if (plat_data->avdd_reg) {
+ avdd_regulator =
+ regulator_get(&client->dev, plat_data->avdd_reg);
+ if (!IS_ERR_VALUE((unsigned long)avdd_regulator)) {
+ regulator_set_voltage(avdd_regulator, 1800000, 1800000);
+ if (regulator_enable(avdd_regulator) != 0)
+ return -ENODEV;
+ }
+ }
+
+ if (plat_data->pvdd_reg) {
+ pvdd_regulator =
+ regulator_get(&client->dev, plat_data->pvdd_reg);
+ if (!IS_ERR_VALUE((unsigned long)pvdd_regulator)) {
+ regulator_set_voltage(pvdd_regulator, 1800000, 1800000);
+ if (regulator_enable(pvdd_regulator) != 0)
+ return -ENODEV;
+ }
+ }
+
+ if (plat_data->reset)
+ plat_data->reset();
+
+ if (plat_data->pwdn)
+ plat_data->pwdn(1);
+
+ msleep(1);
+
+ /* Set initial values for the sensor struct. */
+ memset(&adv7180_data, 0, sizeof(adv7180_data));
+ adv7180_data.i2c_client = client;
+ adv7180_data.streamcap.timeperframe.denominator = 30;
+ adv7180_data.streamcap.timeperframe.numerator = 1;
+ adv7180_data.std_id = V4L2_STD_ALL;
+ video_idx = ADV7180_NOT_LOCKED;
+ adv7180_data.pix.width = video_fmts[video_idx].raw_width;
+ adv7180_data.pix.height = video_fmts[video_idx].raw_height;
+ adv7180_data.pix.pixelformat = V4L2_PIX_FMT_UYVY; /* YUV422 */
+ adv7180_data.pix.priv = 1; /* 1 is used to indicate TV in */
+ adv7180_data.on = true;
+
+ gpio_sensor_active();
+
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "%s:adv7180 probe i2c address is 0x%02X \n",
+ __func__, adv7180_data.i2c_client->addr);
+
+ /*! Read the revision ID of the tvin chip */
+ rev_id = adv7180_read(ADV7180_IDENT);
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "%s:Analog Device adv7%2X0 detected! \n", __func__,
+ rev_id);
+
+ /*! ADV7180 initialization. */
+ adv7180_hard_reset();
+
+ pr_debug(" type is %d (expect %d)\n",
+ adv7180_int_device.type, v4l2_int_type_slave);
+ pr_debug(" num ioctls is %d\n",
+ adv7180_int_device.u.slave->num_ioctls);
+
+ /* This function attaches this structure to the /dev/video0 device.
+ * The pointer in priv points to the mt9v111_data structure here.*/
+ adv7180_int_device.priv = &adv7180_data;
+ ret = v4l2_int_device_register(&adv7180_int_device);
+
+ return ret;
+}
+
+/*!
+ * ADV7180 I2C detach function.
+ * Called on rmmod.
+ *
+ * @param *client struct i2c_client*.
+ *
+ * @return Error code indicating success or failure.
+ */
+static int adv7180_detach(struct i2c_client *client)
+{
+ struct mxc_tvin_platform_data *plat_data = client->dev.platform_data;
+
+ dev_dbg(&adv7180_data.i2c_client->dev,
+ "%s:Removing %s video decoder @ 0x%02X from adapter %s \n",
+ __func__, IF_NAME, client->addr << 1, client->adapter->name);
+
+ if (plat_data->pwdn)
+ plat_data->pwdn(0);
+
+ if (dvddio_regulator) {
+ regulator_disable(dvddio_regulator);
+ regulator_put(dvddio_regulator);
+ }
+
+ if (dvdd_regulator) {
+ regulator_disable(dvdd_regulator);
+ regulator_put(dvdd_regulator);
+ }
+
+ if (avdd_regulator) {
+ regulator_disable(avdd_regulator);
+ regulator_put(avdd_regulator);
+ }
+
+ if (pvdd_regulator) {
+ regulator_disable(pvdd_regulator);
+ regulator_put(pvdd_regulator);
+ }
+
+ v4l2_int_device_unregister(&adv7180_int_device);
+
+ return 0;
+}
+
+/*!
+ * ADV7180 init function.
+ * Called on insmod.
+ *
+ * @return Error code indicating success or failure.
+ */
+static __init int adv7180_init(void)
+{
+ u8 err = 0;
+
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_init\n");
+
+ /* Tells the i2c driver what functions to call for this driver. */
+ err = i2c_add_driver(&adv7180_i2c_driver);
+ if (err != 0)
+ pr_err("%s:driver registration failed, error=%d \n",
+ __func__, err);
+
+ return err;
+}
+
+/*!
+ * ADV7180 cleanup function.
+ * Called on rmmod.
+ *
+ * @return Error code indicating success or failure.
+ */
+static void __exit adv7180_clean(void)
+{
+ dev_dbg(&adv7180_data.i2c_client->dev, "In adv7180_clean\n");
+ i2c_del_driver(&adv7180_i2c_driver);
+ gpio_sensor_inactive();
+}
+
+module_init(adv7180_init);
+module_exit(adv7180_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("Anolog Device ADV7180 video decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/csi_v4l2_capture.c b/drivers/media/video/mxc/capture/csi_v4l2_capture.c
new file mode 100644
index 000000000000..3266d2500081
--- /dev/null
+++ b/drivers/media/video/mxc/capture/csi_v4l2_capture.c
@@ -0,0 +1,1024 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file drivers/media/video/mxc/capture/csi_v4l2_capture.c
+ * This file is derived from mxc_v4l2_capture.c
+ *
+ * @brief MX25 Video For Linux 2 driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-int-device.h>
+#include <linux/mxcfb.h>
+#include "mxc_v4l2_capture.h"
+#include "fsl_csi.h"
+
+static int video_nr = -1;
+static cam_data *g_cam;
+
+static int csi_v4l2_master_attach(struct v4l2_int_device *slave);
+static void csi_v4l2_master_detach(struct v4l2_int_device *slave);
+static u8 camera_power(cam_data *cam, bool cameraOn);
+
+/*! Information about this driver. */
+static struct v4l2_int_master csi_v4l2_master = {
+ .attach = csi_v4l2_master_attach,
+ .detach = csi_v4l2_master_detach,
+};
+
+static struct v4l2_int_device csi_v4l2_int_device = {
+ .module = THIS_MODULE,
+ .name = "csi_v4l2_cap",
+ .type = v4l2_int_type_master,
+ .u = {
+ .master = &csi_v4l2_master,
+ },
+};
+
+/*!
+ * Indicates whether the palette is supported.
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_UYVY or V4L2_PIX_FMT_YUV420
+ *
+ * @return 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ return (palette == V4L2_PIX_FMT_RGB565) ||
+ (palette == V4L2_PIX_FMT_UYVY) || (palette == V4L2_PIX_FMT_YUV420);
+}
+
+/*!
+ * start the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int start_preview(cam_data *cam)
+{
+ unsigned long fb_addr = (unsigned long)cam->v4l2_fb.base;
+
+ __raw_writel(fb_addr, CSI_CSIDMASA_FB1);
+ __raw_writel(fb_addr, CSI_CSIDMASA_FB2);
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+
+ csi_enable_int(0);
+
+ return 0;
+}
+
+/*!
+ * shut down the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int stop_preview(cam_data *cam)
+{
+ csi_disable_int();
+
+ /* set CSI_CSIDMASA_FB1 and CSI_CSIDMASA_FB2 to default value */
+ __raw_writel(0, CSI_CSIDMASA_FB1);
+ __raw_writel(0, CSI_CSIDMASA_FB2);
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+
+ return 0;
+}
+
+/***************************************************************************
+ * VIDIOC Functions.
+ **************************************************************************/
+
+/*!
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int csi_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ f->fmt.pix = cam->v2f.fmt.pix;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+ f->fmt.win = cam->win;
+ break;
+ default:
+ pr_debug(" type is invalid\n");
+ retval = -EINVAL;
+ }
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+
+ return retval;
+}
+
+/*!
+ * V4L2 - csi_v4l2_s_fmt function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int csi_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+ int size = 0;
+ int bytesperline = 0;
+ int *width, *height;
+
+ pr_debug("In MVC: %s\n", __func__);
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ pr_err("ERROR: v4l2 capture: %s: format "
+ "not supported\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Handle case where size requested is larger than cuurent
+ * camera setting. */
+ if ((f->fmt.pix.width > cam->crop_bounds.width)
+ || (f->fmt.pix.height > cam->crop_bounds.height)) {
+ /* Need the logic here, calling vidioc_s_param if
+ * camera can change. */
+ pr_debug("csi_v4l2_s_fmt size changed\n");
+ }
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ height = &f->fmt.pix.width;
+ width = &f->fmt.pix.height;
+ } else {
+ width = &f->fmt.pix.width;
+ height = &f->fmt.pix.height;
+ }
+
+ if ((cam->crop_bounds.width / *width > 8) ||
+ ((cam->crop_bounds.width / *width == 8) &&
+ (cam->crop_bounds.width % *width))) {
+ *width = cam->crop_bounds.width / 8;
+ if (*width % 8)
+ *width += 8 - *width % 8;
+ pr_err("ERROR: v4l2 capture: width exceeds limit "
+ "resize to %d.\n", *width);
+ }
+
+ if ((cam->crop_bounds.height / *height > 8) ||
+ ((cam->crop_bounds.height / *height == 8) &&
+ (cam->crop_bounds.height % *height))) {
+ *height = cam->crop_bounds.height / 8;
+ if (*height % 8)
+ *height += 8 - *height % 8;
+ pr_err("ERROR: v4l2 capture: height exceeds limit "
+ "resize to %d.\n", *height);
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ csi_set_16bit_imagpara(f->fmt.pix.width,
+ f->fmt.pix.height);
+ bytesperline = f->fmt.pix.width * 2;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ csi_set_16bit_imagpara(f->fmt.pix.width,
+ f->fmt.pix.height);
+ bytesperline = f->fmt.pix.width * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+ csi_set_12bit_imagpara(f->fmt.pix.width,
+ f->fmt.pix.height);
+ bytesperline = f->fmt.pix.width;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_NV12:
+ default:
+ pr_debug(" case not supported\n");
+ break;
+ }
+
+ if (f->fmt.pix.bytesperline < bytesperline)
+ f->fmt.pix.bytesperline = bytesperline;
+ else
+ bytesperline = f->fmt.pix.bytesperline;
+
+ if (f->fmt.pix.sizeimage < size)
+ f->fmt.pix.sizeimage = size;
+ else
+ size = f->fmt.pix.sizeimage;
+
+ cam->v2f.fmt.pix = f->fmt.pix;
+
+ if (cam->v2f.fmt.pix.priv != 0) {
+ if (copy_from_user(&cam->offset,
+ (void *)cam->v2f.fmt.pix.priv,
+ sizeof(cam->offset))) {
+ retval = -EFAULT;
+ break;
+ }
+ }
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+ cam->win = f->fmt.win;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+
+ return retval;
+}
+
+/*!
+ * V4L2 - csi_v4l2_s_param function
+ * Allows setting of capturemode and frame rate.
+ *
+ * @param cam structure cam_data *
+ * @param parm structure v4l2_streamparm *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int csi_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)
+{
+ struct v4l2_ifparm ifparm;
+ struct v4l2_format cam_fmt;
+ struct v4l2_streamparm currentparm;
+ int err = 0;
+
+ pr_debug("In %s\n", __func__);
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_err(KERN_ERR "%s invalid type\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Stop the viewfinder */
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+
+ currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* First check that this device can support the changes requested. */
+ err = vidioc_int_g_parm(cam->sensor, &currentparm);
+ if (err) {
+ pr_err("%s: vidioc_int_g_parm returned an error %d\n",
+ __func__, err);
+ goto exit;
+ }
+
+ pr_debug(" Current capabilities are %x\n",
+ currentparm.parm.capture.capability);
+ pr_debug(" Current capturemode is %d change to %d\n",
+ currentparm.parm.capture.capturemode,
+ parm->parm.capture.capturemode);
+ pr_debug(" Current framerate is %d change to %d\n",
+ currentparm.parm.capture.timeperframe.denominator,
+ parm->parm.capture.timeperframe.denominator);
+
+ err = vidioc_int_s_parm(cam->sensor, parm);
+ if (err) {
+ pr_err("%s: vidioc_int_s_parm returned an error %d\n",
+ __func__, err);
+ goto exit;
+ }
+
+ vidioc_int_g_ifparm(cam->sensor, &ifparm);
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n",
+ cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
+
+exit:
+ return err;
+}
+
+/*!
+ * V4L interface - open function
+ *
+ * @param inode structure inode *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int csi_v4l_open(struct inode *inode, struct file *file)
+{
+ struct v4l2_ifparm ifparm;
+ struct v4l2_format cam_fmt;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int err = 0;
+
+ pr_debug(" device name is %s\n", dev->name);
+
+ if (!cam) {
+ pr_err("ERROR: v4l2 capture: Internal error, "
+ "cam_data not found!\n");
+ return -EBADF;
+ }
+
+ down(&cam->busy_lock);
+ err = 0;
+ if (signal_pending(current))
+ goto oops;
+
+ if (cam->open_count++ == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+
+ vidioc_int_g_ifparm(cam->sensor, &ifparm);
+
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ vidioc_int_init(cam->sensor);
+ }
+
+ file->private_data = dev;
+
+oops:
+ up(&cam->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L interface - close function
+ *
+ * @param inode struct inode *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int csi_v4l_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ int err = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ pr_debug("In MVC:%s\n", __func__);
+
+ if (!cam) {
+ pr_err("ERROR: v4l2 capture: Internal error, "
+ "cam_data not found!\n");
+ return -EBADF;
+ }
+
+ /* for the case somebody hit the ctrl C */
+ if (cam->overlay_pid == current->pid) {
+ err = stop_preview(cam);
+ cam->overlay_on = false;
+ }
+
+ if (--cam->open_count == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+ file->private_data = NULL;
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ }
+
+ return err;
+}
+
+/*
+ * V4L interface - read function
+ *
+ * @param file struct file *
+ * @param read buf char *
+ * @param count size_t
+ * @param ppos structure loff_t *
+ *
+ * @return bytes read
+ */
+static ssize_t csi_v4l_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos)
+{
+ int err = 0;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ /* Stop the viewfinder */
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+
+ if (cam->still_buf_vaddr == NULL) {
+ cam->still_buf_vaddr = dma_alloc_coherent(0,
+ PAGE_ALIGN
+ (cam->v2f.fmt.
+ pix.sizeimage),
+ &cam->
+ still_buf,
+ GFP_DMA | GFP_KERNEL);
+ if (cam->still_buf_vaddr == NULL) {
+ pr_err("alloc dma memory failed\n");
+ return -ENOMEM;
+ }
+ cam->still_counter = 0;
+ __raw_writel(cam->still_buf, CSI_CSIDMASA_FB2);
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_DMA_REFLASH_RFF,
+ CSI_CSICR3);
+ __raw_writel(__raw_readl(CSI_CSISR), CSI_CSISR);
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST,
+ CSI_CSICR3);
+ csi_enable_int(1);
+ }
+
+ wait_event_interruptible(cam->still_queue, cam->still_counter);
+ csi_disable_int();
+ err = copy_to_user(buf, cam->still_buf_vaddr,
+ cam->v2f.fmt.pix.sizeimage);
+
+ if (cam->still_buf_vaddr != NULL) {
+ dma_free_coherent(0, PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+ cam->still_buf_vaddr, cam->still_buf);
+ cam->still_buf = 0;
+ cam->still_buf_vaddr = NULL;
+ }
+
+ if (cam->overlay_on == true)
+ start_preview(cam);
+
+ up(&cam->busy_lock);
+ if (err < 0)
+ return err;
+
+ return cam->v2f.fmt.pix.sizeimage - err;
+}
+
+/*!
+ * V4L interface - ioctl function
+ *
+ * @param inode struct inode*
+ *
+ * @param file struct file*
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void*
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static int csi_v4l_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int retval = 0;
+
+ pr_debug("In MVC: %s, %x\n", __func__, ioctlnr);
+ wait_event_interruptible(cam->power_queue, cam->low_power == false);
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ /*!
+ * V4l2 VIDIOC_G_FMT ioctl
+ */
+ case VIDIOC_G_FMT:{
+ struct v4l2_format *gf = arg;
+ pr_debug(" case VIDIOC_G_FMT\n");
+ retval = csi_v4l2_g_fmt(cam, gf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FMT ioctl
+ */
+ case VIDIOC_S_FMT:{
+ struct v4l2_format *sf = arg;
+ pr_debug(" case VIDIOC_S_FMT\n");
+ retval = csi_v4l2_s_fmt(cam, sf);
+ vidioc_int_s_fmt_cap(cam->sensor, sf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_OVERLAY ioctl
+ */
+ case VIDIOC_OVERLAY:{
+ int *on = arg;
+ pr_debug(" case VIDIOC_OVERLAY\n");
+ if (*on) {
+ cam->overlay_on = true;
+ cam->overlay_pid = current->pid;
+ start_preview(cam);
+ }
+ if (!*on) {
+ stop_preview(cam);
+ cam->overlay_on = false;
+ }
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_FBUF ioctl
+ */
+ case VIDIOC_G_FBUF:{
+ struct v4l2_framebuffer *fb = arg;
+ *fb = cam->v4l2_fb;
+ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FBUF ioctl
+ */
+ case VIDIOC_S_FBUF:{
+ struct v4l2_framebuffer *fb = arg;
+ cam->v4l2_fb = *fb;
+ break;
+ }
+
+ case VIDIOC_G_PARM:{
+ struct v4l2_streamparm *parm = arg;
+ pr_debug(" case VIDIOC_G_PARM\n");
+ vidioc_int_g_parm(cam->sensor, parm);
+ break;
+ }
+
+ case VIDIOC_S_PARM:{
+ struct v4l2_streamparm *parm = arg;
+ pr_debug(" case VIDIOC_S_PARM\n");
+ retval = csi_v4l2_s_param(cam, parm);
+ break;
+ }
+
+ case VIDIOC_QUERYCAP:{
+ struct v4l2_capability *cap = arg;
+ pr_debug(" case VIDIOC_QUERYCAP\n");
+ strcpy(cap->driver, "csi_v4l2");
+ cap->version = KERNEL_VERSION(0, 1, 11);
+ cap->capabilities = V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_READWRITE;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ break;
+ }
+
+ case VIDIOC_S_CROP:
+ pr_debug(" case not supported\n");
+ break;
+
+ case VIDIOC_S_CTRL:
+ case VIDIOC_G_STD:
+ case VIDIOC_QBUF:
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_REQBUFS:
+ case VIDIOC_DQBUF:
+ case VIDIOC_G_OUTPUT:
+ case VIDIOC_S_OUTPUT:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_CROP:
+ case VIDIOC_CROPCAP:
+ case VIDIOC_S_STD:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_STREAMOFF:
+ case VIDIOC_STREAMON:
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_ENUMINPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ case VIDIOC_ENUMOUTPUT:
+ default:
+ pr_debug(" case not supported\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&cam->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L interface - ioctl function
+ *
+ * @return None
+ */
+static int csi_v4l_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, csi_v4l_do_ioctl);
+}
+
+/*!
+ * V4L interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int csi_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = video_devdata(file);
+ unsigned long size;
+ int res = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ pr_debug("%s\n", __func__);
+ pr_debug("\npgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ size = vma->vm_end - vma->vm_start;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ pr_err("ERROR: v4l2 capture: %s : "
+ "remap_pfn_range failed\n", __func__);
+ res = -ENOBUFS;
+ goto csi_mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+csi_mmap_exit:
+ up(&cam->busy_lock);
+ return res;
+}
+
+/*!
+ * This structure defines the functions to be called in this driver.
+ */
+static struct file_operations csi_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = csi_v4l_open,
+ .release = csi_v4l_close,
+ .read = csi_v4l_read,
+ .ioctl = csi_v4l_ioctl,
+ .mmap = csi_mmap,
+};
+
+static struct video_device csi_v4l_template = {
+ .name = "Mx25 Camera",
+ .vfl_type = VID_TYPE_CAPTURE,
+ .fops = &csi_v4l_fops,
+ .release = video_device_release,
+};
+
+/*!
+ * This function can be used to release any platform data on closing.
+ */
+static void camera_platform_release(struct device *device)
+{
+}
+
+/*! Device Definition for csi v4l2 device */
+static struct platform_device csi_v4l2_devices = {
+ .name = "csi_v4l2",
+ .dev = {
+ .release = camera_platform_release,
+ },
+ .id = 0,
+};
+
+/*!
+ * initialize cam_data structure
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static void init_camera_struct(cam_data *cam)
+{
+ pr_debug("In MVC: %s\n", __func__);
+
+ /* Default everything to 0 */
+ memset(cam, 0, sizeof(cam_data));
+
+ init_MUTEX(&cam->param_lock);
+ init_MUTEX(&cam->busy_lock);
+
+ cam->video_dev = video_device_alloc();
+ if (cam->video_dev == NULL)
+ return;
+
+ *(cam->video_dev) = csi_v4l_template;
+
+ video_set_drvdata(cam->video_dev, cam);
+ dev_set_drvdata(&csi_v4l2_devices.dev, (void *)cam);
+ cam->video_dev->minor = -1;
+
+ init_waitqueue_head(&cam->still_queue);
+
+ cam->streamparm.parm.capture.capturemode = 0;
+
+ cam->standard.index = 0;
+ cam->standard.id = V4L2_STD_UNKNOWN;
+ cam->standard.frameperiod.denominator = 30;
+ cam->standard.frameperiod.numerator = 1;
+ cam->standard.framelines = 480;
+ cam->standard_autodetect = true;
+ cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;
+ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ cam->overlay_on = false;
+ cam->capture_on = false;
+ cam->skip_frame = 0;
+ cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY;
+
+ cam->v2f.fmt.pix.sizeimage = 480 * 640 * 2;
+ cam->v2f.fmt.pix.bytesperline = 640 * 2;
+ cam->v2f.fmt.pix.width = 640;
+ cam->v2f.fmt.pix.height = 480;
+ cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ cam->win.w.width = 160;
+ cam->win.w.height = 160;
+ cam->win.w.left = 0;
+ cam->win.w.top = 0;
+ cam->still_counter = 0;
+
+ csi_start_callback(cam);
+ init_waitqueue_head(&cam->power_queue);
+ spin_lock_init(&cam->int_lock);
+}
+
+/*!
+ * camera_power function
+ * Turns Sensor power On/Off
+ *
+ * @param cam cam data struct
+ * @param cameraOn true to turn camera on, false to turn off power.
+ *
+ * @return status
+ */
+static u8 camera_power(cam_data *cam, bool cameraOn)
+{
+ pr_debug("In MVC: %s on=%d\n", __func__, cameraOn);
+
+ if (cameraOn == true) {
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ vidioc_int_s_power(cam->sensor, 1);
+ } else {
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ vidioc_int_s_power(cam->sensor, 0);
+ }
+ return 0;
+}
+
+/*!
+ * This function is called to put the sensor in a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure.
+ */
+static int csi_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ pr_debug("In MVC: %s\n", __func__);
+
+ if (cam == NULL)
+ return -1;
+
+ cam->low_power = true;
+
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+
+ camera_power(cam, false);
+
+ return 0;
+}
+
+/*!
+ * This function is called to bring the sensor back from a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param pdev the device structure
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int csi_v4l2_resume(struct platform_device *pdev)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ pr_debug("In MVC: %s\n", __func__);
+
+ if (cam == NULL)
+ return -1;
+
+ cam->low_power = false;
+ wake_up_interruptible(&cam->power_queue);
+ camera_power(cam, true);
+
+ if (cam->overlay_on == true)
+ start_preview(cam);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver csi_v4l2_driver = {
+ .driver = {
+ .name = "csi_v4l2",
+ },
+ .probe = NULL,
+ .remove = NULL,
+#ifdef CONFIG_PM
+ .suspend = csi_v4l2_suspend,
+ .resume = csi_v4l2_resume,
+#endif
+ .shutdown = NULL,
+};
+
+/*!
+ * Initializes the camera driver.
+ */
+static int csi_v4l2_master_attach(struct v4l2_int_device *slave)
+{
+ cam_data *cam = slave->u.slave->master->priv;
+ struct v4l2_format cam_fmt;
+
+ pr_debug("In MVC: %s\n", __func__);
+ pr_debug(" slave.name = %s\n", slave->name);
+ pr_debug(" master.name = %s\n", slave->u.slave->master->name);
+
+ cam->sensor = slave;
+ if (slave == NULL) {
+ pr_err("ERROR: v4l2 capture: slave parameter not valid.\n");
+ return -1;
+ }
+
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ vidioc_int_dev_init(slave);
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* Used to detect TV in (type 1) vs. camera (type 0) */
+ cam->device_type = cam_fmt.fmt.pix.priv;
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+
+ return 0;
+}
+
+/*!
+ * Disconnects the camera driver.
+ */
+static void csi_v4l2_master_detach(struct v4l2_int_device *slave)
+{
+ pr_debug("In MVC: %s\n", __func__);
+
+ vidioc_int_dev_exit(slave);
+}
+
+/*!
+ * Entry point for the V4L2
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int camera_init(void)
+{
+ u8 err = 0;
+
+ /* Register the device driver structure. */
+ err = platform_driver_register(&csi_v4l2_driver);
+ if (err != 0) {
+ pr_err("ERROR: v4l2 capture:camera_init: "
+ "platform_driver_register failed.\n");
+ return err;
+ }
+
+ /* Create g_cam and initialize it. */
+ g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
+ if (g_cam == NULL) {
+ pr_err("ERROR: v4l2 capture: failed to register camera\n");
+ platform_driver_unregister(&csi_v4l2_driver);
+ return -1;
+ }
+ init_camera_struct(g_cam);
+
+ /* Set up the v4l2 device and register it */
+ csi_v4l2_int_device.priv = g_cam;
+ /* This function contains a bug that won't let this be rmmod'd. */
+ v4l2_int_device_register(&csi_v4l2_int_device);
+
+ /* Register the platform device */
+ err = platform_device_register(&csi_v4l2_devices);
+ if (err != 0) {
+ pr_err("ERROR: v4l2 capture: camera_init: "
+ "platform_device_register failed.\n");
+ platform_driver_unregister(&csi_v4l2_driver);
+ kfree(g_cam);
+ g_cam = NULL;
+ return err;
+ }
+
+ /* register v4l video device */
+ if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr)
+ == -1) {
+ platform_device_unregister(&csi_v4l2_devices);
+ platform_driver_unregister(&csi_v4l2_driver);
+ kfree(g_cam);
+ g_cam = NULL;
+ pr_err("ERROR: v4l2 capture: video_register_device failed\n");
+ return -1;
+ }
+ pr_debug(" Video device registered: %s #%d\n",
+ g_cam->video_dev->name, g_cam->video_dev->minor);
+
+ return err;
+}
+
+/*!
+ * Exit and cleanup for the V4L2
+ */
+static void __exit camera_exit(void)
+{
+ pr_debug("In MVC: %s\n", __func__);
+
+ if (g_cam->open_count) {
+ pr_err("ERROR: v4l2 capture:camera open "
+ "-- setting ops to NULL\n");
+ } else {
+ pr_info("V4L2 freeing image input device\n");
+ v4l2_int_device_unregister(&csi_v4l2_int_device);
+ csi_stop_callback(g_cam);
+ video_unregister_device(g_cam->video_dev);
+ platform_driver_unregister(&csi_v4l2_driver);
+ platform_device_unregister(&csi_v4l2_devices);
+
+ kfree(g_cam);
+ g_cam = NULL;
+ }
+}
+
+module_init(camera_init);
+module_exit(camera_exit);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2 capture driver for Mx25 based cameras");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/capture/emma_mt9v111.c b/drivers/media/video/mxc/capture/emma_mt9v111.c
new file mode 100644
index 000000000000..73e9bba36d1e
--- /dev/null
+++ b/drivers/media/video/mxc/capture/emma_mt9v111.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mt9v111.c
+ *
+ * @brief mt9v111 camera driver functions
+ *
+ * @ingroup Camera
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include "mxc_v4l2_capture.h"
+#include "mt9v111.h"
+
+#ifdef MT9V111_DEBUG
+static u16 testpattern;
+#endif
+
+static sensor_interface *interface_param;
+static mt9v111_conf mt9v111_device;
+static int reset_frame_rate = 30;
+
+#define MT9V111_FRAME_RATE_NUM 20
+
+static mt9v111_image_format format[2] = {
+ {
+ .index = 0,
+ .width = 640,
+ .height = 480,
+ },
+ {
+ .index = 1,
+ .width = 352,
+ .height = 288,
+ },
+};
+
+static int mt9v111_attach(struct i2c_adapter *adapter);
+static int mt9v111_detach(struct i2c_client *client);
+
+static struct i2c_driver mt9v111_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "MT9V111 Client",
+ },
+ .attach_adapter = mt9v111_attach,
+ .detach_client = mt9v111_detach,
+};
+
+static struct i2c_client mt9v111_i2c_client = {
+ .name = "mt9v111 I2C dev",
+ .addr = MT9V111_I2C_ADDRESS,
+ .driver = &mt9v111_i2c_driver,
+};
+
+/*
+ * Function definitions
+ */
+
+#ifdef MT9V111_DEBUG
+static inline int mt9v111_read_reg(u8 reg)
+{
+ int val = i2c_smbus_read_word_data(&mt9v111_i2c_client, reg);
+ if (val != -1)
+ val = cpu_to_be16(val);
+ return val;
+}
+#endif
+
+static inline int mt9v111_write_reg(u8 reg, u16 val)
+{
+ pr_debug("write reg %x val %x.\n", reg, val);
+ return i2c_smbus_write_word_data(&mt9v111_i2c_client, reg,
+ cpu_to_be16(val));
+}
+
+/*!
+ * Initialize mt9v111_sensor_lib
+ * Libarary for Sensor configuration through I2C
+ *
+ * @param coreReg Core Registers
+ * @param ifpReg IFP Register
+ *
+ * @return status
+ */
+static u8 mt9v111_sensor_lib(mt9v111_coreReg * coreReg, mt9v111_IFPReg * ifpReg)
+{
+ u8 reg;
+ u16 data;
+ u8 error = 0;
+
+ /*
+ * setup to IFP registers
+ */
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ /* Operation Mode Control */
+ reg = MT9V111I_MODE_CONTROL;
+ data = ifpReg->modeControl;
+ mt9v111_write_reg(reg, data);
+
+ /* Output format */
+ reg = MT9V111I_FORMAT_CONTROL;
+ data = ifpReg->formatControl; /* Set bit 12 */
+ mt9v111_write_reg(reg, data);
+
+ /* AE limit 4 */
+ reg = MT9V111I_SHUTTER_WIDTH_LIMIT_AE;
+ data = ifpReg->gainLimitAE;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_OUTPUT_FORMAT_CTRL2;
+ data = ifpReg->outputFormatCtrl2;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_AE_SPEED;
+ data = ifpReg->AESpeed;
+ mt9v111_write_reg(reg, data);
+
+ /* output image size */
+ reg = MT9V111i_H_PAN;
+ data = 0x8000 | ifpReg->HPan;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_ZOOM;
+ data = 0x8000 | ifpReg->HZoom;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_SIZE;
+ data = 0x8000 | ifpReg->HSize;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_PAN;
+ data = 0x8000 | ifpReg->VPan;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_ZOOM;
+ data = 0x8000 | ifpReg->VZoom;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_SIZE;
+ data = 0x8000 | ifpReg->VSize;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_PAN;
+ data = ~0x8000 & ifpReg->HPan;
+ mt9v111_write_reg(reg, data);
+#if 0
+ reg = MT9V111I_UPPER_SHUTTER_DELAY_LIM;
+ data = ifpReg->upperShutterDelayLi;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_SHUTTER_60;
+ data = ifpReg->shutter_width_60;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_SEARCH_FLICK_60;
+ data = ifpReg->search_flicker_60;
+ mt9v111_write_reg(reg, data);
+#endif
+
+ /*
+ * setup to sensor core registers
+ */
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = coreReg->addressSelect;
+ mt9v111_write_reg(reg, data);
+
+ /* enable changes and put the Sync bit on */
+ reg = MT9V111S_OUTPUT_CTRL;
+ data = MT9V111S_OUTCTRL_SYNC | MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000;
+ mt9v111_write_reg(reg, data);
+
+ /* min PIXCLK - Default */
+ reg = MT9V111S_PIXEL_CLOCK_SPEED;
+ data = coreReg->pixelClockSpeed;
+ mt9v111_write_reg(reg, data);
+
+ /* Setup image flipping / Dark rows / row/column skip */
+ reg = MT9V111S_READ_MODE;
+ data = coreReg->readMode;
+ mt9v111_write_reg(reg, data);
+
+ /*zoom 0 */
+ reg = MT9V111S_DIGITAL_ZOOM;
+ data = coreReg->digitalZoom;
+ mt9v111_write_reg(reg, data);
+
+ /* min H-blank */
+ reg = MT9V111S_HOR_BLANKING;
+ data = coreReg->horizontalBlanking;
+ mt9v111_write_reg(reg, data);
+
+ /* min V-blank */
+ reg = MT9V111S_VER_BLANKING;
+ data = coreReg->verticalBlanking;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_SHUTTER_WIDTH;
+ data = coreReg->shutterWidth;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_SHUTTER_DELAY;
+ data = ifpReg->upperShutterDelayLi;
+ mt9v111_write_reg(reg, data);
+
+ /* changes become effective */
+ reg = MT9V111S_OUTPUT_CTRL;
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000;
+ mt9v111_write_reg(reg, data);
+
+ return error;
+}
+
+/*!
+ * mt9v111 sensor interface Initialization
+ * @param param sensor_interface *
+ * @param width u32
+ * @param height u32
+ * @return None
+ */
+static void mt9v111_interface(sensor_interface *param, u32 width, u32 height)
+{
+ param->Vsync_pol = 0x0;
+ param->clk_mode = 0x0; /*gated */
+ param->pixclk_pol = 0x0;
+ param->data_width = 0x1;
+ param->data_pol = 0x0;
+ param->ext_vsync = 0x0;
+ param->Vsync_pol = 0x0;
+ param->Hsync_pol = 0x0;
+ param->width = width - 1;
+ param->height = height - 1;
+ param->active_width = width;
+ param->active_height = height;
+ param->pixel_fmt = IPU_PIX_FMT_UYVY;
+ param->mclk = 27000000;
+}
+
+/*!
+ * MT9V111 frame rate calculate
+ *
+ * @param frame_rate int *
+ * @param mclk int
+ * @return None
+ */
+static void mt9v111_rate_cal(int *frame_rate, int mclk)
+{
+ int num_clock_per_row;
+ int max_rate = 0;
+
+ mt9v111_device.coreReg->horizontalBlanking = MT9V111_HORZBLANK_MIN;
+
+ num_clock_per_row = (format[0].width + 114 + MT9V111_HORZBLANK_MIN) * 2;
+ max_rate = mclk / (num_clock_per_row *
+ (format[0].height + MT9V111_VERTBLANK_DEFAULT));
+
+ if ((*frame_rate > max_rate) || (*frame_rate == 0)) {
+ *frame_rate = max_rate;
+ }
+
+ mt9v111_device.coreReg->verticalBlanking
+ = mclk / (*frame_rate * num_clock_per_row) - format[0].height;
+
+ reset_frame_rate = *frame_rate;
+}
+
+/*!
+ * MT9V111 sensor configuration
+ *
+ * @param frame_rate int *
+ * @param high_quality int
+ * @return sensor_interface *
+ */
+sensor_interface *mt9v111_config(int *frame_rate, int high_quality)
+{
+ u32 out_width, out_height;
+
+ if (interface_param == NULL)
+ return NULL;
+
+ mt9v111_device.coreReg->addressSelect = MT9V111I_SEL_SCA;
+ mt9v111_device.ifpReg->addrSpaceSel = MT9V111I_SEL_IFP;
+
+ mt9v111_device.coreReg->windowHeight = MT9V111_WINHEIGHT;
+ mt9v111_device.coreReg->windowWidth = MT9V111_WINWIDTH;
+ mt9v111_device.coreReg->zoomColStart = 0;
+ mt9v111_device.coreReg->zomRowStart = 0;
+ mt9v111_device.coreReg->digitalZoom = 0x0;
+
+ mt9v111_device.coreReg->verticalBlanking = MT9V111_VERTBLANK_DEFAULT;
+ mt9v111_device.coreReg->horizontalBlanking = MT9V111_HORZBLANK_MIN;
+ mt9v111_device.coreReg->pixelClockSpeed = 0;
+ mt9v111_device.coreReg->readMode = 0xd0a1;
+
+ mt9v111_device.ifpReg->outputFormatCtrl2 = 0;
+ mt9v111_device.ifpReg->gainLimitAE = 0x300;
+ mt9v111_device.ifpReg->AESpeed = 0x80;
+
+ /* here is the default value */
+ mt9v111_device.ifpReg->formatControl = 0xc800;
+ mt9v111_device.ifpReg->modeControl = 0x708e;
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ mt9v111_device.coreReg->shutterWidth = 0xf8;
+
+ out_width = 640;
+ out_height = 480;
+
+ /*output size */
+ mt9v111_device.ifpReg->HPan = 0;
+ mt9v111_device.ifpReg->HZoom = 640;
+ mt9v111_device.ifpReg->HSize = out_width;
+ mt9v111_device.ifpReg->VPan = 0;
+ mt9v111_device.ifpReg->VZoom = 480;
+ mt9v111_device.ifpReg->VSize = out_height;
+
+ mt9v111_interface(interface_param, out_width, out_height);
+ set_mclk_rate(&interface_param->mclk);
+ mt9v111_rate_cal(frame_rate, interface_param->mclk);
+ mt9v111_sensor_lib(mt9v111_device.coreReg, mt9v111_device.ifpReg);
+
+ return interface_param;
+}
+
+/*!
+ * mt9v111 sensor set color configuration
+ *
+ * @param bright int
+ * @param saturation int
+ * @param red int
+ * @param green int
+ * @param blue int
+ * @return None
+ */
+static void
+mt9v111_set_color(int bright, int saturation, int red, int green, int blue)
+{
+ u8 reg;
+ u16 data;
+
+ switch (saturation) {
+ case 100:
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ break;
+ case 150:
+ mt9v111_device.ifpReg->awbSpeed = 0x6D14;
+ break;
+ case 75:
+ mt9v111_device.ifpReg->awbSpeed = 0x4D14;
+ break;
+ case 50:
+ mt9v111_device.ifpReg->awbSpeed = 0x5514;
+ break;
+ case 37:
+ mt9v111_device.ifpReg->awbSpeed = 0x5D14;
+ break;
+ case 25:
+ mt9v111_device.ifpReg->awbSpeed = 0x6514;
+ break;
+ default:
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ break;
+ }
+
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = mt9v111_device.ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ /* Operation Mode Control */
+ reg = MT9V111I_AWB_SPEED;
+ data = mt9v111_device.ifpReg->awbSpeed;
+ mt9v111_write_reg(reg, data);
+}
+
+/*!
+ * mt9v111 sensor get color configuration
+ *
+ * @param bright int *
+ * @param saturation int *
+ * @param red int *
+ * @param green int *
+ * @param blue int *
+ * @return None
+ */
+static void
+mt9v111_get_color(int *bright, int *saturation, int *red, int *green, int *blue)
+{
+ *saturation = (mt9v111_device.ifpReg->awbSpeed & 0x3800) >> 11;
+ switch (*saturation) {
+ case 0:
+ *saturation = 100;
+ break;
+ case 1:
+ *saturation = 75;
+ break;
+ case 2:
+ *saturation = 50;
+ break;
+ case 3:
+ *saturation = 37;
+ break;
+ case 4:
+ *saturation = 25;
+ break;
+ case 5:
+ *saturation = 150;
+ break;
+ case 6:
+ *saturation = 0;
+ break;
+ default:
+ *saturation = 0;
+ break;
+ }
+}
+
+/*!
+ * mt9v111 sensor set AE measurement window mode configuration
+ *
+ * @param ae_mode int
+ * @return None
+ */
+static void mt9v111_set_ae_mode(int ae_mode)
+{
+ u8 reg;
+ u16 data;
+
+ mt9v111_device.ifpReg->modeControl &= 0xfff3;
+ mt9v111_device.ifpReg->modeControl |= (ae_mode & 0x03) << 2;
+
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = mt9v111_device.ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_MODE_CONTROL;
+ data = mt9v111_device.ifpReg->modeControl;
+ mt9v111_write_reg(reg, data);
+}
+
+/*!
+ * mt9v111 sensor get AE measurement window mode configuration
+ *
+ * @param ae_mode int *
+ * @return None
+ */
+static void mt9v111_get_ae_mode(int *ae_mode)
+{
+ if (ae_mode != NULL) {
+ *ae_mode = (mt9v111_device.ifpReg->modeControl & 0xc) >> 2;
+ }
+}
+
+/*!
+ * mt9v111 Reset function
+ *
+ * @return None
+ */
+static sensor_interface *mt9v111_reset(void)
+{
+ return mt9v111_config(&reset_frame_rate, 0);
+}
+
+struct camera_sensor camera_sensor_if = {
+ .set_color = mt9v111_set_color,
+ .get_color = mt9v111_get_color,
+ .set_ae_mode = mt9v111_set_ae_mode,
+ .get_ae_mode = mt9v111_get_ae_mode,
+ .config = mt9v111_config,
+ .reset = mt9v111_reset,
+};
+
+#ifdef MT9V111_DEBUG
+/*!
+ * Set sensor to test mode, which will generate test pattern.
+ *
+ * @return none
+ */
+static void mt9v111_test_pattern(bool flag)
+{
+ u16 data;
+
+ /* switch to sensor registers */
+ mt9v111_write_reg(MT9V111I_ADDR_SPACE_SEL, MT9V111I_SEL_SCA);
+
+ if (flag == true) {
+ testpattern = MT9V111S_OUTCTRL_TEST_MODE;
+
+ data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) & 0xBF;
+ mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data);
+
+ mt9v111_write_reg(MT9V111S_TEST_DATA, 0);
+
+ /* changes take effect */
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+ mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data);
+ } else {
+ testpattern = 0;
+
+ data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) | 0x40;
+ mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data);
+
+ /* changes take effect */
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+ mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data);
+ }
+}
+#endif
+
+/*!
+ * mt9v111 I2C detect_client function
+ *
+ * @param adapter struct i2c_adapter *
+ * @param address int
+ * @param kind int
+ *
+ * @return Error code indicating success or failure
+ */
+static int mt9v111_detect_client(struct i2c_adapter *adapter, int address,
+ int kind)
+{
+ mt9v111_i2c_client.adapter = adapter;
+ if (i2c_attach_client(&mt9v111_i2c_client)) {
+ mt9v111_i2c_client.adapter = NULL;
+ printk(KERN_ERR "mt9v111_attach: i2c_attach_client failed\n");
+ return -1;
+ }
+
+ interface_param = (sensor_interface *)
+ kmalloc(sizeof(sensor_interface), GFP_KERNEL);
+ if (!interface_param) {
+ printk(KERN_ERR "mt9v111_attach: kmalloc failed \n");
+ return -1;
+ }
+
+ printk(KERN_INFO "MT9V111 Detected\n");
+
+ return 0;
+}
+
+static unsigned short normal_i2c[] = { MT9V111_I2C_ADDRESS, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+/*!
+ * mt9v111 I2C attach function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int mt9v111_attach(struct i2c_adapter *adap)
+{
+ uint32_t mclk = 27000000;
+ struct clk *clk;
+ int err;
+
+ clk = clk_get(NULL, "csi_clk");
+ clk_enable(clk);
+ set_mclk_rate(&mclk);
+
+ err = i2c_probe(adap, &addr_data, &mt9v111_detect_client);
+
+ clk_disable(clk);
+ clk_put(clk);
+
+ return err;
+}
+
+/*!
+ * mt9v111 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int mt9v111_detach(struct i2c_client *client)
+{
+ int err;
+
+ if (!mt9v111_i2c_client.adapter)
+ return -1;
+
+ err = i2c_detach_client(&mt9v111_i2c_client);
+ mt9v111_i2c_client.adapter = NULL;
+
+ if (interface_param)
+ kfree(interface_param);
+ interface_param = NULL;
+
+ return err;
+}
+
+extern void gpio_sensor_active(void);
+
+/*!
+ * MT9V111 init function
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int mt9v111_init(void)
+{
+ u8 err;
+
+ gpio_sensor_active();
+
+ mt9v111_device.coreReg = (mt9v111_coreReg *)
+ kmalloc(sizeof(mt9v111_coreReg), GFP_KERNEL);
+ if (!mt9v111_device.coreReg)
+ return -1;
+
+ memset(mt9v111_device.coreReg, 0, sizeof(mt9v111_coreReg));
+
+ mt9v111_device.ifpReg = (mt9v111_IFPReg *)
+ kmalloc(sizeof(mt9v111_IFPReg), GFP_KERNEL);
+ if (!mt9v111_device.ifpReg) {
+ kfree(mt9v111_device.coreReg);
+ mt9v111_device.coreReg = NULL;
+ return -1;
+ }
+
+ memset(mt9v111_device.ifpReg, 0, sizeof(mt9v111_IFPReg));
+
+ err = i2c_add_driver(&mt9v111_i2c_driver);
+
+ return err;
+}
+
+extern void gpio_sensor_inactive(void);
+/*!
+ * MT9V111 cleanup function
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit mt9v111_clean(void)
+{
+ if (mt9v111_device.coreReg) {
+ kfree(mt9v111_device.coreReg);
+ mt9v111_device.coreReg = NULL;
+ }
+
+ if (mt9v111_device.ifpReg) {
+ kfree(mt9v111_device.ifpReg);
+ mt9v111_device.ifpReg = NULL;
+ }
+
+ i2c_del_driver(&mt9v111_i2c_driver);
+
+ gpio_sensor_inactive();
+}
+
+module_init(mt9v111_init);
+module_exit(mt9v111_clean);
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(camera_sensor_if);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Mt9v111 Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/emma_ov2640.c b/drivers/media/video/mxc/capture/emma_ov2640.c
new file mode 100644
index 000000000000..ceffea4d52a9
--- /dev/null
+++ b/drivers/media/video/mxc/capture/emma_ov2640.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+
+#include "mxc_v4l2_capture.h"
+
+enum ov2640_mode {
+ ov2640_mode_1600_1120,
+ ov2640_mode_800_600
+};
+
+struct reg_value {
+ u8 reg;
+ u8 value;
+ int delay_ms;
+};
+
+static struct reg_value ov2640_setting_1600_1120[] = {
+ {0xff, 0x1, 0}, {0x12, 0x80, 1}, {0xff, 0, 0}, {0x2c, 0xff, 0},
+ {0x2e, 0xdf, 0}, {0xff, 0x1, 0}, {0x3c, 0x32, 0}, {0x11, 0x01, 0},
+ {0x09, 0x00, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+ {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+ {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x82, 0},
+ {0x35, 0x88, 0}, {0x22, 0x0a, 0}, {0x37, 0x40, 0}, {0x23, 0x00, 0},
+ {0x34, 0xa0, 0}, {0x36, 0x1a, 0}, {0x06, 0x02, 0}, {0x07, 0xc0, 0},
+ {0x0d, 0xb7, 0}, {0x0e, 0x01, 0}, {0x4c, 0x00, 0}, {0x4a, 0x81, 0},
+ {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+ {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x3f, 0}, {0x0c, 0x3c, 0},
+ {0x5d, 0x55, 0}, {0x5e, 0x7d, 0}, {0x5f, 0x7d, 0}, {0x60, 0x55, 0},
+ {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0},
+ {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 00, 0},
+ {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x3d, 0x34, 0},
+ {0x5a, 0x57, 0}, {0x4f, 0xbb, 0}, {0x50, 0x9c, 0}, {0xff, 0x00, 0},
+ {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0x44, 0x06, 0},
+ {0xe0, 0x14, 0}, {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0},
+ {0x43, 0x18, 0}, {0x4c, 0x00, 0}, {0x87, 0xd0, 0}, {0xd7, 0x03, 0},
+ {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0},
+ {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0},
+ {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0},
+ {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0},
+ {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0},
+ {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0},
+ {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0},
+ {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0},
+ {0x93, 0xe3, 0}, {0x93, 0x03, 0}, {0x93, 0x03, 0}, {0x93, 0x00, 0},
+ {0x93, 0x02, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0},
+ {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0},
+ {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0},
+ {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xa4, 0x00, 0}, {0xa8, 0x00, 0},
+ {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0}, {0xc7, 0x10, 0},
+ {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0}, {0xb9, 0x7c, 0},
+ {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0}, {0xb0, 0xc5, 0},
+ {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0}, {0xa6, 0x00, 0},
+ {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x1b, 0}, {0xa7, 0x31, 0},
+ {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0},
+ {0xa7, 0x19, 0}, {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0},
+ {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x19, 0}, {0xa7, 0x31, 0},
+ {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xc0, 0xc8, 0}, {0xc1, 0x96, 0},
+ {0x86, 0x3d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0}, {0x52, 0x18, 0},
+ {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0}, {0x57, 0x00, 0},
+ {0x5a, 0x90, 0}, {0x5b, 0x18, 0}, {0x5c, 0x05, 0}, {0xc3, 0xef, 0},
+ {0x7f, 0x00, 0}, {0xda, 0x01, 0}, {0xe5, 0x1f, 0}, {0xe1, 0x67, 0},
+ {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0}
+};
+
+static struct reg_value ov2640_setting_800_600[] = {
+ {0xff, 0, 0}, {0xff, 1, 0}, {0x12, 0x80, 1}, {0xff, 00, 0},
+ {0x2c, 0xff, 0}, {0x2e, 0xdf, 0}, {0xff, 0x1, 0}, {0x3c, 0x32, 0},
+ {0x11, 0x01, 0}, {0x09, 0x00, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0},
+ {0x14, 0x48, 0}, {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0},
+ {0x3b, 0xfb, 0}, {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0},
+ {0x39, 0x92, 0}, {0x35, 0xda, 0}, {0x22, 0x1a, 0}, {0x37, 0xc3, 0},
+ {0x23, 0x00, 0}, {0x34, 0xc0, 0}, {0x36, 0x1a, 0}, {0x06, 0x88, 0},
+ {0x07, 0xc0, 0}, {0x0d, 0x87, 0}, {0x0e, 0x41, 0}, {0x4c, 0x00, 0},
+ {0x4a, 0x81, 0}, {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0},
+ {0x26, 0x82, 0}, {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x22, 0},
+ {0x0c, 0x3c, 0}, {0x5d, 0x55, 0}, {0x5e, 0x7d, 0}, {0x5f, 0x7d, 0},
+ {0x60, 0x55, 0}, {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0},
+ {0x20, 0x80, 0}, {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0},
+ {0x6e, 00, 0}, {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0},
+ {0x12, 0x40, 0}, {0x17, 0x11, 0}, {0x18, 0x43, 0}, {0x19, 0x00, 0},
+ {0x1a, 0x4b, 0}, {0x32, 0x09, 0}, {0x37, 0xc0, 0}, {0x4f, 0xca, 0},
+ {0x50, 0xa8, 0}, {0x6d, 0x00, 0}, {0x3d, 0x38, 0}, {0xff, 0x00, 0},
+ {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0x44, 0x06, 0},
+ {0xe0, 0x14, 0}, {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0},
+ {0x43, 0x18, 0}, {0x4c, 0x00, 0}, {0x87, 0xd0, 0}, {0x88, 0x3f, 0},
+ {0xd7, 0x03, 0}, {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0},
+ {0xc9, 0x80, 0}, {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0},
+ {0x7d, 0x48, 0}, {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0},
+ {0x7d, 0x10, 0}, {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0},
+ {0x91, 0x1a, 0}, {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0},
+ {0x91, 0x75, 0}, {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0},
+ {0x91, 0x96, 0}, {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0},
+ {0x91, 0xd7, 0}, {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0},
+ {0x93, 0x06, 0}, {0x93, 0xe3, 0}, {0x93, 0x03, 0}, {0x93, 0x03, 0},
+ {0x93, 0x00, 0}, {0x93, 0x02, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0},
+ {0x97, 0x02, 0}, {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0},
+ {0x97, 0x28, 0}, {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0},
+ {0x97, 0x80, 0}, {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xa4, 0x00, 0},
+ {0xa8, 0x00, 0}, {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0},
+ {0xc7, 0x10, 0}, {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0},
+ {0xb9, 0x7c, 0}, {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0},
+ {0xb0, 0xc5, 0}, {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0},
+ {0xa6, 0x00, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x1b, 0},
+ {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xa7, 0x20, 0},
+ {0xa7, 0xd8, 0}, {0xa7, 0x19, 0}, {0xa7, 0x31, 0}, {0xa7, 0x00, 0},
+ {0xa7, 0x18, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x19, 0},
+ {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xc0, 0x64, 0},
+ {0xc1, 0x4b, 0}, {0x86, 0x1d, 0}, {0x50, 0x00, 0}, {0x51, 0xc8, 0},
+ {0x52, 0x96, 0}, {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x00, 0},
+ {0x57, 0x00, 0}, {0x5a, 0xc8, 0}, {0x5b, 0x96, 0}, {0x5c, 0x00, 0},
+ {0xc3, 0xef, 0}, {0x7f, 0x00, 0}, {0xda, 0x01, 0}, {0xe5, 0x1f, 0},
+ {0xe1, 0x67, 0}, {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0}
+};
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+u32 mclk = 24000000;
+
+struct i2c_client *ov2640_i2c_client;
+
+static sensor_interface *interface_param;
+static int reset_frame_rate = 30;
+static int ov2640_probe(struct i2c_client *adapter,
+ const struct i2c_device_id *id);
+static int ov2640_remove(struct i2c_client *client);
+
+static const struct i2c_device_id ov2640_id[] = {
+ {"ov2640", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov2640_id);
+
+static struct i2c_driver ov2640_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ov2640",
+ },
+ .probe = ov2640_probe,
+ .remove = ov2640_remove,
+ .id_table = ov2640_id,
+};
+
+/*!
+ * ov2640 I2C attach function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int ov2640_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mxc_camera_platform_data *plat_data = client->dev.platform_data;
+
+ ov2640_i2c_client = client;
+ mclk = plat_data->mclk;
+
+ io_regulator = regulator_get(&client->dev, plat_data->io_regulator);
+ core_regulator = regulator_get(&client->dev, plat_data->core_regulator);
+ analog_regulator =
+ regulator_get(&client->dev, plat_data->analog_regulator);
+ gpo_regulator = regulator_get(&client->dev, plat_data->gpo_regulator);
+
+ interface_param = (sensor_interface *)
+ kmalloc(sizeof(sensor_interface), GFP_KERNEL);
+ if (!interface_param) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "ov2640_probe: kmalloc failed \n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * ov2640 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int ov2640_remove(struct i2c_client *client)
+{
+ kfree(interface_param);
+ interface_param = NULL;
+
+ if (!IS_ERR_VALUE((unsigned long)io_regulator)) {
+ regulator_disable(io_regulator);
+ regulator_put(io_regulator);
+ }
+
+ if (!IS_ERR_VALUE((unsigned long)core_regulator)) {
+ regulator_disable(core_regulator);
+ regulator_put(core_regulator);
+ }
+
+ if (!IS_ERR_VALUE((unsigned long)gpo_regulator)) {
+ regulator_disable(gpo_regulator);
+ regulator_put(gpo_regulator);
+ }
+
+ if (!IS_ERR_VALUE((unsigned long)analog_regulator)) {
+ regulator_disable(analog_regulator);
+ regulator_put(analog_regulator);
+ }
+
+ return 0;
+}
+
+static int ov2640_write_reg(u8 reg, u8 val)
+{
+ if (i2c_smbus_write_byte_data(ov2640_i2c_client, reg, val) < 0) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:write reg errorr:reg=%x,val=%x\n", __func__, reg,
+ val);
+ return -1;
+ }
+ return 0;
+}
+
+static int ov2640_init_mode(enum ov2640_mode mode)
+{
+ struct reg_value *setting;
+ int i, num;
+
+ switch (mode) {
+ case ov2640_mode_1600_1120:
+ setting = ov2640_setting_1600_1120;
+ num = ARRAY_SIZE(ov2640_setting_1600_1120);
+ break;
+ case ov2640_mode_800_600:
+ setting = ov2640_setting_800_600;
+ num = ARRAY_SIZE(ov2640_setting_800_600);
+ break;
+ default:
+ return 0;
+ }
+
+ for (i = 0; i < num; i++) {
+ ov2640_write_reg(setting[i].reg, setting[i].value);
+ if (setting[i].delay_ms > 0)
+ msleep(setting[i].delay_ms);
+ }
+
+ return 0;
+}
+
+/*!
+ * ov2640 sensor interface Initialization
+ * @param param sensor_interface *
+ * @param width u32
+ * @param height u32
+ * @return None
+ */
+static void ov2640_interface(sensor_interface *param, u32 width, u32 height)
+{
+ param->Vsync_pol = 0x0;
+ param->clk_mode = 0x0; /*gated */
+ param->pixclk_pol = 0x0;
+ param->data_width = 0x1;
+ param->data_pol = 0x0;
+ param->ext_vsync = 0x0;
+ param->Vsync_pol = 0x0;
+ param->Hsync_pol = 0x0;
+ param->width = width - 1;
+ param->height = height - 1;
+ param->active_width = width;
+ param->active_height = height;
+ param->pixel_fmt = IPU_PIX_FMT_UYVY;
+ param->mclk = mclk;
+}
+
+static void ov2640_set_color(int bright, int saturation, int red, int green,
+ int blue)
+{
+
+}
+
+static void ov2640_get_color(int *bright, int *saturation, int *red, int *green,
+ int *blue)
+{
+
+}
+static void ov2640_set_ae_mode(int ae_mode)
+{
+
+}
+static void ov2640_get_ae_mode(int *ae_mode)
+{
+
+}
+
+extern void gpio_sensor_active(void);
+
+static sensor_interface *ov2640_config(int *frame_rate, int high_quality)
+{
+
+ u32 out_width, out_height;
+
+ /*set io votage */
+ if (!IS_ERR_VALUE((unsigned long)io_regulator)) {
+ regulator_set_voltage(io_regulator, 2800000, 2800000);
+ if (regulator_enable(io_regulator) != 0) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:io set voltage error\n", __func__);
+ return NULL;
+ } else {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:io set voltage ok\n", __func__);
+ }
+ }
+
+ /*core votage */
+ if (!IS_ERR_VALUE((unsigned long)core_regulator)) {
+ regulator_set_voltage(core_regulator, 1300000, 1300000);
+ if (regulator_enable(core_regulator) != 0) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:core set voltage error\n", __func__);
+ return NULL;
+ } else {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:core set voltage ok\n", __func__);
+ }
+ }
+
+ /*GPO 3 */
+ if (!IS_ERR_VALUE((unsigned long)gpo_regulator)) {
+ if (regulator_enable(gpo_regulator) != 0) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:gpo3 enable error\n", __func__);
+ return NULL;
+ } else {
+ dev_dbg(&ov2640_i2c_client->dev, "%s:gpo3 enable ok\n",
+ __func__);
+ }
+ }
+
+ if (!IS_ERR_VALUE((unsigned long)analog_regulator)) {
+ regulator_set_voltage(analog_regulator, 2000000, 2000000);
+ if (regulator_enable(analog_regulator) != 0) {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:analog set voltage error\n", __func__);
+ return NULL;
+ } else {
+ dev_dbg(&ov2640_i2c_client->dev,
+ "%s:analog set voltage ok\n", __func__);
+ }
+ }
+
+ gpio_sensor_active();
+
+ if (high_quality) {
+ out_width = 1600;
+ out_height = 1120;
+ } else {
+ out_width = 800;
+ out_height = 600;
+ }
+ ov2640_interface(interface_param, out_width, out_height);
+ set_mclk_rate(&interface_param->mclk);
+
+ if (high_quality)
+ ov2640_init_mode(ov2640_mode_1600_1120);
+ else
+ ov2640_init_mode(ov2640_mode_800_600);
+
+ msleep(300);
+
+ return interface_param;
+}
+
+static sensor_interface *ov2640_reset(void)
+{
+ return ov2640_config(&reset_frame_rate, 0);
+}
+
+struct camera_sensor camera_sensor_if = {
+ .set_color = ov2640_set_color,
+ .get_color = ov2640_get_color,
+ .set_ae_mode = ov2640_set_ae_mode,
+ .get_ae_mode = ov2640_get_ae_mode,
+ .config = ov2640_config,
+ .reset = ov2640_reset,
+};
+
+EXPORT_SYMBOL(camera_sensor_if);
+
+/*!
+ * ov2640 init function
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int ov2640_init(void)
+{
+ u8 err;
+
+ err = i2c_add_driver(&ov2640_i2c_driver);
+
+ return err;
+}
+
+extern void gpio_sensor_inactive(void);
+/*!
+ * OV2640 cleanup function
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit ov2640_clean(void)
+{
+ i2c_del_driver(&ov2640_i2c_driver);
+
+ gpio_sensor_inactive();
+}
+
+module_init(ov2640_init);
+module_exit(ov2640_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV2640 Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/emma_v4l2_capture.c b/drivers/media/video/mxc/capture/emma_v4l2_capture.c
new file mode 100644
index 000000000000..9cb08b26f1cd
--- /dev/null
+++ b/drivers/media/video/mxc/capture/emma_v4l2_capture.c
@@ -0,0 +1,2074 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx27_v4l2_capture.c
+ *
+ * @brief MX27 Video For Linux 2 driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/semaphore.h>
+#include <linux/version.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include "mxc_v4l2_capture.h"
+#include "mx27_prp.h"
+#include "mx27_csi.h"
+
+static int csi_mclk_flag_backup;
+static int video_nr = -1;
+static cam_data *g_cam;
+
+/*!
+ * Free frame buffers
+ *
+ * @param cam Structure cam_data *
+ *
+ * @return status 0 success.
+ */
+static int mxc_free_frame_buf(cam_data *cam)
+{
+ int i;
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ if (cam->frame[i].vaddress != 0) {
+ dma_free_coherent(0,
+ cam->frame[i].buffer.length,
+ cam->frame[i].vaddress,
+ cam->frame[i].paddress);
+ cam->frame[i].vaddress = 0;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * Allocate frame buffers
+ *
+ * @param cam Structure cam_data *
+ *
+ * @param count int number of buffer need to allocated
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_frame_buf(cam_data *cam, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ cam->frame[i].vaddress =
+ dma_alloc_coherent(0,
+ PAGE_ALIGN(cam->v2f. fmt.pix.sizeimage),
+ &cam->frame[i].paddress,
+ GFP_DMA | GFP_KERNEL);
+ if (cam->frame[i].vaddress == 0) {
+ pr_debug("mxc_allocate_frame_buf failed.\n");
+ mxc_free_frame_buf(cam);
+ return -ENOBUFS;
+ }
+ cam->frame[i].buffer.index = i;
+ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+ cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buffer.length =
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+ cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buffer.m.offset = cam->frame[i].paddress;
+ cam->frame[i].index = i;
+ }
+
+ return 0;
+}
+
+/*!
+ * Free frame buffers status
+ *
+ * @param cam Structure cam_data *
+ *
+ * @return none
+ */
+static void mxc_free_frames(cam_data *cam)
+{
+ int i;
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+ }
+
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+}
+
+/*!
+ * Return the buffer status
+ *
+ * @param cam Structure cam_data *
+ * @param buf Structure v4l2_buffer *
+ *
+ * @return status 0 success, EINVAL failed.
+ */
+static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf)
+{
+ /* check range */
+ if (buf->index < 0 || buf->index >= FRAME_NUM) {
+ pr_debug("mxc_v4l2_buffer_status buffers not allocated\n");
+ return -EINVAL;
+ }
+
+ memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));
+ return 0;
+}
+
+/*!
+ * start the encoder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_streamon(cam_data *cam)
+{
+ struct mxc_v4l_frame *frame;
+ int err = 0;
+
+ if (!cam)
+ return -EIO;
+
+ if (list_empty(&cam->ready_q)) {
+ printk(KERN_ERR "mxc_streamon buffer not been queued yet\n");
+ return -EINVAL;
+ }
+
+ cam->capture_pid = current->pid;
+
+ if (cam->enc_enable) {
+ err = cam->enc_enable(cam);
+ if (err != 0) {
+ return err;
+ }
+ }
+
+ cam->ping_pong_csi = 0;
+ if (cam->enc_update_eba) {
+ frame =
+ list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&frame->queue, &cam->working_q);
+ err = cam->enc_update_eba(frame->paddress, &cam->ping_pong_csi);
+
+ frame =
+ list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&frame->queue, &cam->working_q);
+ err |=
+ cam->enc_update_eba(frame->paddress, &cam->ping_pong_csi);
+ } else {
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+/*!
+ * Shut down the encoder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_streamoff(cam_data *cam)
+{
+ int err = 0;
+
+ if (!cam)
+ return -EIO;
+
+ if (cam->enc_disable) {
+ err = cam->enc_disable(cam);
+ }
+ mxc_free_frames(cam);
+ return err;
+}
+
+/*!
+ * Valid whether the palette is supported
+ *
+ * @param palette pixel format
+ *
+ * @return 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ /*
+ * MX27 PrP channel 2 supports YUV444, but YUV444 is not
+ * defined by V4L2 :(
+ */
+ return ((palette == V4L2_PIX_FMT_YUYV) ||
+ (palette == V4L2_PIX_FMT_YUV420));
+}
+
+/*!
+ * Valid and adjust the overlay window size, position
+ *
+ * @param cam structure cam_data *
+ * @param win struct v4l2_window *
+ *
+ * @return 0
+ */
+static int verify_preview(cam_data *cam, struct v4l2_window *win)
+{
+ if (cam->output >= num_registered_fb) {
+ pr_debug("verify_preview No matched.\n");
+ return -1;
+ }
+ cam->overlay_fb = (struct fb_info *)registered_fb[cam->output];
+
+ /* TODO: suppose 16bpp, 4 bytes alignment */
+ win->w.left &= ~0x1;
+
+ if (win->w.width + win->w.left > cam->overlay_fb->var.xres)
+ win->w.width = cam->overlay_fb->var.xres - win->w.left;
+ if (win->w.height + win->w.top > cam->overlay_fb->var.yres)
+ win->w.height = cam->overlay_fb->var.yres - win->w.top;
+
+ /*
+ * TODO: suppose 16bpp. Rounded down to a multiple of 2 pixels for
+ * width according to PrP limitations.
+ */
+ if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT))
+ win->w.height &= ~0x1;
+ else
+ win->w.width &= ~0x1;
+
+ return 0;
+}
+
+/*!
+ * start the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int start_preview(cam_data *cam)
+{
+ int err = 0;
+
+ err = prp_vf_select(cam);
+ if (err != 0)
+ return err;
+
+ cam->overlay_pid = current->pid;
+ err = cam->vf_start_sdc(cam);
+
+ return err;
+}
+
+/*!
+ * shut down the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int stop_preview(cam_data *cam)
+{
+ int err = 0;
+
+ err = prp_vf_deselect(cam);
+ return err;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_g_fmt function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ f->fmt.pix.width = cam->v2f.fmt.pix.width;
+ f->fmt.pix.height = cam->v2f.fmt.pix.height;
+ f->fmt.pix.sizeimage = cam->v2f.fmt.pix.sizeimage;
+ f->fmt.pix.pixelformat = cam->v2f.fmt.pix.pixelformat;
+ f->fmt.pix.bytesperline = cam->v2f.fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ retval = 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ f->fmt.win = cam->win;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_fmt function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+ int size = 0;
+ int bytesperline = 0;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ pr_debug("mxc_v4l2_s_fmt: format not supported\n");
+ retval = -EINVAL;
+ }
+
+ if (cam->rotation != V4L2_MXC_ROTATE_NONE)
+ pr_debug("mxc_v4l2_s_fmt: capture rotation ignored\n");
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ f->fmt.pix.width &= ~0x1; /* Multiple of 2 */
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ bytesperline = f->fmt.pix.width * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ f->fmt.pix.width &= ~0x7; /* Multiple of 8 */
+ f->fmt.pix.height &= ~0x1; /* Multiple of 2 */
+ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+ bytesperline = f->fmt.pix.width * 3 / 2;
+ break;
+ default:
+ /* Suppose it's YUV444 or 32bpp */
+ size = f->fmt.pix.width * f->fmt.pix.height * 4;
+ bytesperline = f->fmt.pix.width * 4;
+ pr_info("mxc_v4l2_s_fmt: default assume"
+ " to be YUV444 interleaved.\n");
+ break;
+ }
+
+ if (f->fmt.pix.bytesperline < bytesperline) {
+ f->fmt.pix.bytesperline = bytesperline;
+ } else {
+ bytesperline = f->fmt.pix.bytesperline;
+ }
+
+ if (f->fmt.pix.sizeimage > size) {
+ pr_debug("mxc_v4l2_s_fmt: sizeimage bigger than"
+ " needed.\n");
+ size = f->fmt.pix.sizeimage;
+ }
+ f->fmt.pix.sizeimage = size;
+
+ cam->v2f.fmt.pix.sizeimage = size;
+ cam->v2f.fmt.pix.bytesperline = bytesperline;
+ cam->v2f.fmt.pix.width = f->fmt.pix.width;
+ cam->v2f.fmt.pix.height = f->fmt.pix.height;
+ cam->v2f.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
+ retval = 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ retval = verify_preview(cam, &f->fmt.win);
+ cam->win = f->fmt.win;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/*!
+ * get control param
+ *
+ * @param cam structure cam_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_get_v42l_control(cam_data *cam, struct v4l2_control *c)
+{
+ int status = 0;
+
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ c->value = cam->rotation;
+ break;
+ case V4L2_CID_VFLIP:
+ c->value = cam->rotation;
+ break;
+ case V4L2_CID_MXC_ROT:
+ c->value = cam->rotation;
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ c->value = cam->bright;
+ break;
+ case V4L2_CID_HUE:
+ c->value = cam->hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = cam->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = cam->saturation;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ c->value = cam->red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ c->value = cam->blue;
+ break;
+ case V4L2_CID_BLACK_LEVEL:
+ c->value = cam->ae_mode;
+ break;
+ default:
+ status = -EINVAL;
+ }
+ return status;
+}
+
+/*!
+ * V4L2 - set_control function
+ * V4L2_CID_MXC_ROT is the extention for rotation/mirroring.
+ *
+ * @param cam structure cam_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_set_v42l_control(cam_data *cam, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ if (c->value == 1) {
+ if ((cam->rotation != V4L2_MXC_ROTATE_VERT_FLIP) &&
+ (cam->rotation != V4L2_MXC_ROTATE_180))
+ cam->rotation = V4L2_MXC_ROTATE_HORIZ_FLIP;
+ else
+ cam->rotation = V4L2_MXC_ROTATE_180;
+ } else {
+ if (cam->rotation == V4L2_MXC_ROTATE_HORIZ_FLIP)
+ cam->rotation = V4L2_MXC_ROTATE_NONE;
+ else if (cam->rotation == V4L2_MXC_ROTATE_180)
+ cam->rotation = V4L2_MXC_ROTATE_VERT_FLIP;
+ }
+ break;
+ case V4L2_CID_VFLIP:
+ if (c->value == 1) {
+ if ((cam->rotation != V4L2_MXC_ROTATE_HORIZ_FLIP) &&
+ (cam->rotation != V4L2_MXC_ROTATE_180))
+ cam->rotation = V4L2_MXC_ROTATE_VERT_FLIP;
+ else
+ cam->rotation = V4L2_MXC_ROTATE_180;
+ } else {
+ if (cam->rotation == V4L2_MXC_ROTATE_VERT_FLIP)
+ cam->rotation = V4L2_MXC_ROTATE_NONE;
+ if (cam->rotation == V4L2_MXC_ROTATE_180)
+ cam->rotation = V4L2_MXC_ROTATE_HORIZ_FLIP;
+ }
+ break;
+ case V4L2_CID_MXC_ROT:
+ switch (c->value) {
+ case V4L2_MXC_ROTATE_NONE:
+ case V4L2_MXC_ROTATE_VERT_FLIP:
+ case V4L2_MXC_ROTATE_HORIZ_FLIP:
+ case V4L2_MXC_ROTATE_180:
+ case V4L2_MXC_ROTATE_90_RIGHT:
+ case V4L2_MXC_ROTATE_90_RIGHT_VFLIP:
+ case V4L2_MXC_ROTATE_90_RIGHT_HFLIP:
+ case V4L2_MXC_ROTATE_90_LEFT:
+ cam->rotation = c->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case V4L2_CID_HUE:
+ cam->hue = c->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ cam->contrast = c->value;
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ cam->bright = c->value;
+ case V4L2_CID_SATURATION:
+ cam->saturation = c->value;
+ case V4L2_CID_RED_BALANCE:
+ cam->red = c->value;
+ case V4L2_CID_BLUE_BALANCE:
+ cam->blue = c->value;
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ cam->cam_sensor->set_color(cam->bright, cam->saturation,
+ cam->red, cam->green, cam->blue);
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ break;
+ case V4L2_CID_BLACK_LEVEL:
+ cam->ae_mode = c->value & 0x03;
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ if (cam->cam_sensor->set_ae_mode)
+ cam->cam_sensor->set_ae_mode(cam->ae_mode);
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ break;
+ case V4L2_CID_MXC_FLASH:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_param function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param parm structure v4l2_streamparm *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)
+{
+ sensor_interface *param;
+ csi_signal_cfg_t csi_param;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_debug("mxc_v4l2_s_param invalid type\n");
+ return -EINVAL;
+ }
+
+ if (parm->parm.capture.timeperframe.denominator >
+ cam->standard.frameperiod.denominator) {
+ pr_debug("mxc_v4l2_s_param frame rate %d larger "
+ "than standard supported %d\n",
+ parm->parm.capture.timeperframe.denominator,
+ cam->standard.frameperiod.denominator);
+ return -EINVAL;
+ }
+
+ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ param = cam->cam_sensor->config
+ (&parm->parm.capture.timeperframe.denominator,
+ parm->parm.capture.capturemode);
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+
+ cam->streamparm.parm.capture.timeperframe =
+ parm->parm.capture.timeperframe;
+
+ if ((parm->parm.capture.capturemode != 0) &&
+ (parm->parm.capture.capturemode != V4L2_MODE_HIGHQUALITY)) {
+ pr_debug("mxc_v4l2_s_param frame un-supported capture mode\n");
+ return -EINVAL;
+ }
+
+ if (parm->parm.capture.capturemode ==
+ cam->streamparm.parm.capture.capturemode) {
+ return 0;
+ }
+
+ /* resolution changed, so need to re-program the CSI */
+ csi_param.sens_clksrc = 0;
+ csi_param.clk_mode = param->clk_mode;
+ csi_param.pixclk_pol = param->pixclk_pol;
+ csi_param.data_width = param->data_width;
+ csi_param.data_pol = param->data_pol;
+ csi_param.ext_vsync = param->ext_vsync;
+ csi_param.Vsync_pol = param->Vsync_pol;
+ csi_param.Hsync_pol = param->Hsync_pol;
+ csi_init_interface(param->width, param->height, param->pixel_fmt,
+ csi_param);
+
+ if (parm->parm.capture.capturemode != V4L2_MODE_HIGHQUALITY) {
+ cam->streamparm.parm.capture.capturemode = 0;
+ } else {
+ cam->streamparm.parm.capture.capturemode =
+ V4L2_MODE_HIGHQUALITY;
+ cam->streamparm.parm.capture.extendedmode =
+ parm->parm.capture.extendedmode;
+ cam->streamparm.parm.capture.readbuffers = 1;
+ }
+ return 0;
+}
+
+/*!
+ * Dequeue one V4L capture buffer
+ *
+ * @param cam structure cam_data *
+ * @param buf structure v4l2_buffer *
+ *
+ * @return status 0 success, EINVAL invalid frame number,
+ * ETIME timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)
+{
+ int retval = 0;
+ struct mxc_v4l_frame *frame;
+
+ if (!wait_event_interruptible_timeout(cam->enc_queue,
+ cam->enc_counter != 0, 10 * HZ)) {
+ printk(KERN_ERR "mxc_v4l_dqueue timeout enc_counter %x\n",
+ cam->enc_counter);
+ return -ETIME;
+ } else if (signal_pending(current)) {
+ printk(KERN_ERR "mxc_v4l_dqueue() interrupt received\n");
+ return -ERESTARTSYS;
+ }
+
+ cam->enc_counter--;
+
+ frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->done_q.next);
+ if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) {
+ frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE;
+ } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ printk(KERN_ERR "VIDIOC_DQBUF: Buffer not filled.\n");
+ frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+ retval = -EINVAL;
+ } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {
+ printk(KERN_ERR "VIDIOC_DQBUF: Buffer not queued.\n");
+ retval = -EINVAL;
+ }
+
+ buf->bytesused = cam->v2f.fmt.pix.sizeimage;
+ buf->index = frame->index;
+ buf->flags = frame->buffer.flags;
+
+ return retval;
+}
+
+/*!
+ * V4L interface - open function
+ *
+ * @param inode structure inode *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l_open(struct inode *inode, struct file *file)
+{
+ sensor_interface *param;
+ csi_signal_cfg_t csi_param;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int err = 0;
+
+ if (!cam) {
+ pr_info("Internal error, cam_data not found!\n");
+ return -ENODEV;
+ }
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ if (signal_pending(current))
+ goto oops;
+
+ if (cam->open_count++ == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+
+ err = prp_enc_select(cam);
+
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+
+ csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ param = cam->cam_sensor->reset();
+ if (param == NULL) {
+ cam->open_count--;
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ err = -ENODEV;
+ goto oops;
+ }
+ csi_param.sens_clksrc = 0;
+ csi_param.clk_mode = param->clk_mode;
+ csi_param.pixclk_pol = param->pixclk_pol;
+ csi_param.data_width = param->data_width;
+ csi_param.data_pol = param->data_pol;
+ csi_param.ext_vsync = param->ext_vsync;
+ csi_param.Vsync_pol = param->Vsync_pol;
+ csi_param.Hsync_pol = param->Hsync_pol;
+ csi_init_interface(param->width, param->height,
+ param->pixel_fmt, csi_param);
+ cam->cam_sensor->get_color(&cam->bright, &cam->saturation,
+ &cam->red, &cam->green, &cam->blue);
+ if (cam->cam_sensor->get_ae_mode)
+ cam->cam_sensor->get_ae_mode(&cam->ae_mode);
+ csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ prp_init(cam);
+
+ }
+
+ file->private_data = dev;
+ oops:
+ up(&cam->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L interface - close function
+ *
+ * @param inode struct inode *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int mxc_v4l_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ int err = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ /* for the case somebody hit the ctrl C */
+ if (cam->overlay_pid == current->pid) {
+ err = stop_preview(cam);
+ cam->overlay_on = false;
+ }
+ if (cam->capture_pid == current->pid) {
+ err |= mxc_streamoff(cam);
+ cam->capture_on = false;
+ wake_up_interruptible(&cam->enc_queue);
+ }
+
+ if (--cam->open_count == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+ pr_debug("mxc_v4l_close: release resource\n");
+
+ err |= prp_enc_deselect(cam);
+
+ mxc_free_frame_buf(cam);
+ file->private_data = NULL;
+
+ /* capture off */
+ wake_up_interruptible(&cam->enc_queue);
+ mxc_free_frames(cam);
+ cam->enc_counter++;
+ prp_exit(cam);
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_VIDEO_MXC_CSI_DMA
+#include <mach/dma.h>
+
+#define CSI_DMA_STATUS_IDLE 0 /* DMA is not started */
+#define CSI_DMA_STATUS_WORKING 1 /* DMA is transfering the data */
+#define CSI_DMA_STATUS_DONE 2 /* One frame completes successfully */
+#define CSI_DMA_STATUS_ERROR 3 /* Error occurs during the DMA */
+
+/*
+ * Sometimes the start of the DMA is not synchronized with the CSI
+ * SOF (Start of Frame) interrupt which will lead to incorrect
+ * captured image. In this case the driver will re-try capturing
+ * another frame. The following macro defines the maximum re-try
+ * times.
+ */
+#define CSI_DMA_RETRY 8
+
+/*
+ * Size of the physical contiguous memory area used to hold image data
+ * transfered by DMA. It can be less than the size of the image data.
+ */
+#define CSI_MEM_SIZE (1024 * 600)
+
+/* Number of bytes for one DMA transfer */
+#define CSI_DMA_LENGTH (1024 * 200)
+
+static int g_dma_channel;
+static int g_dma_status = CSI_DMA_STATUS_DONE;
+static volatile int g_dma_completed; /* number of completed DMA transfers */
+static volatile int g_dma_copied; /* number of copied DMA transfers */
+static struct tasklet_struct g_dma_tasklet;
+static char *g_user_buf; /* represents the buf passed by read() */
+static int g_user_count; /* represents the count passed by read() */
+
+/*!
+ * @brief setup the DMA to transfer data
+ * There may be more than one DMA to transfer the whole image. Those
+ * DMAs work like chain. This function is used to setup the DMA in
+ * case there is enough space to hold the data.
+ * @param data pointer to the cam structure
+ */
+static void mxc_csi_dma_chaining(void *data)
+{
+ cam_data *cam = (cam_data *) data;
+ int count, chained = 0;
+ int max_dma = CSI_MEM_SIZE / CSI_DMA_LENGTH;
+ mxc_dma_requestbuf_t dma_request;
+
+ while (chained * CSI_DMA_LENGTH < g_user_count) {
+ /*
+ * Calculate how many bytes the DMA should transfer. It may
+ * be less than CSI_DMA_LENGTH if the DMA is the last one.
+ */
+ if ((chained + 1) * CSI_DMA_LENGTH > g_user_count)
+ count = g_user_count - chained * CSI_DMA_LENGTH;
+ else
+ count = CSI_DMA_LENGTH;
+ pr_debug("%s() DMA chained count = %d\n", __FUNCTION__, count);
+
+ /* Config DMA */
+ memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));
+ dma_request.dst_addr = cam->still_buf
+ + (chained % max_dma) * CSI_DMA_LENGTH;
+ dma_request.src_addr = (dma_addr_t) CSI_CSIRXFIFO_PHYADDR;
+ dma_request.num_of_bytes = count;
+ mxc_dma_config(g_dma_channel, &dma_request, 1,
+ MXC_DMA_MODE_READ);
+
+ chained++;
+ }
+}
+
+/*!
+ * @brief Copy image data from physical contiguous memory to user space buffer
+ * Once the data are copied, there will be more spare space in the
+ * physical contiguous memory to receive data from DMA.
+ * @param data pointer to the cam structure
+ */
+static void mxc_csi_dma_task(unsigned long data)
+{
+ cam_data *cam = (cam_data *) data;
+ int count;
+ int max_dma = CSI_MEM_SIZE / CSI_DMA_LENGTH;
+
+ while (g_dma_copied < g_dma_completed) {
+ /*
+ * Calculate how many bytes the DMA has transfered. It may
+ * be less than CSI_DMA_LENGTH if the DMA is the last one.
+ */
+ if ((g_dma_copied + 1) * CSI_DMA_LENGTH > g_user_count)
+ count = g_user_count - g_dma_copied * CSI_DMA_LENGTH;
+ else
+ count = CSI_DMA_LENGTH;
+ if (copy_to_user(g_user_buf + g_dma_copied * CSI_DMA_LENGTH,
+ cam->still_buf_vaddr + (g_dma_copied % max_dma)
+ * CSI_DMA_LENGTH, count))
+ pr_debug("Warning: some bytes not copied\n");
+
+ g_dma_copied++;
+ }
+
+ /* If the whole image has been captured */
+ if (g_dma_copied * CSI_DMA_LENGTH >= g_user_count) {
+ cam->still_counter++;
+ wake_up_interruptible(&cam->still_queue);
+ }
+
+ pr_debug("%s() DMA completed = %d copied = %d\n",
+ __FUNCTION__, g_dma_completed, g_dma_copied);
+}
+
+/*!
+ * @brief DMA interrupt callback function
+ * @param data pointer to the cam structure
+ * @param error DMA error flag
+ * @param count number of bytes transfered by the DMA
+ */
+static void mxc_csi_dma_callback(void *data, int error, unsigned int count)
+{
+ cam_data *cam = (cam_data *) data;
+ int max_dma = CSI_MEM_SIZE / CSI_DMA_LENGTH;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&cam->int_lock, lock_flags);
+
+ g_dma_completed++;
+
+ if (error != MXC_DMA_DONE) {
+ g_dma_status = CSI_DMA_STATUS_ERROR;
+ pr_debug("%s() DMA error\n", __FUNCTION__);
+ }
+
+ /* If the whole image has been captured */
+ if ((g_dma_status != CSI_DMA_STATUS_ERROR)
+ && (g_dma_completed * CSI_DMA_LENGTH >= g_user_count))
+ g_dma_status = CSI_DMA_STATUS_DONE;
+
+ if ((g_dma_status == CSI_DMA_STATUS_WORKING) &&
+ (g_dma_completed >= g_dma_copied + max_dma)) {
+ g_dma_status = CSI_DMA_STATUS_ERROR;
+ pr_debug("%s() Previous buffer over written\n", __FUNCTION__);
+ }
+
+ /* Schedule the tasklet */
+ tasklet_schedule(&g_dma_tasklet);
+
+ spin_unlock_irqrestore(&cam->int_lock, lock_flags);
+
+ pr_debug("%s() count = %d bytes\n", __FUNCTION__, count);
+}
+
+/*!
+ * @brief CSI interrupt callback function
+ * @param data pointer to the cam structure
+ * @param status CSI interrupt status
+ */
+static void mxc_csi_irq_callback(void *data, unsigned long status)
+{
+ cam_data *cam = (cam_data *) data;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&cam->int_lock, lock_flags);
+
+ /* Wait for SOF (Start of Frame) interrupt to sync the image */
+ if (status & BIT_SOF_INT) {
+ if (g_dma_status == CSI_DMA_STATUS_IDLE) {
+ /* Start DMA transfer to capture image */
+ mxc_dma_enable(g_dma_channel);
+ g_dma_status = CSI_DMA_STATUS_WORKING;
+ pr_debug("%s() DMA started.\n", __FUNCTION__);
+ } else if (g_dma_status == CSI_DMA_STATUS_WORKING) {
+ /*
+ * Another SOF occurs during DMA transfer. In this
+ * case the image is not synchronized so need to
+ * report error and probably try again.
+ */
+ g_dma_status = CSI_DMA_STATUS_ERROR;
+ pr_debug("%s() Image is not synchronized with DMA - "
+ "SOF before DMA completes\n", __FUNCTION__);
+ }
+ }
+
+ spin_unlock_irqrestore(&cam->int_lock, lock_flags);
+
+ pr_debug("%s() g_dma_status = %d\n", __FUNCTION__, g_dma_status);
+}
+
+/*!
+ * V4L interface - read function
+ *
+ * @param file struct file *
+ * @param read buf char *
+ * @param count size_t
+ * @param ppos structure loff_t *
+ *
+ * @return bytes read
+ */
+static ssize_t
+mxc_v4l_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ int err = 0;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int retry = CSI_DMA_RETRY;
+
+ g_user_buf = buf;
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ /* Video capture and still image capture are exclusive */
+ if (cam->capture_on == true) {
+ err = -EBUSY;
+ goto exit0;
+ }
+
+ /* The CSI-DMA can not do CSC */
+ if (cam->v2f.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) {
+ pr_info("mxc_v4l_read support YUYV pixel format only\n");
+ err = -EINVAL;
+ goto exit0;
+ }
+
+ /* The CSI-DMA can not do resize or crop */
+ if ((cam->v2f.fmt.pix.width != cam->crop_bounds.width)
+ || (cam->v2f.fmt.pix.height != cam->crop_bounds.height)) {
+ pr_info("mxc_v4l_read resize is not supported\n");
+ pr_info("supported image size width = %d height = %d\n",
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ err = -EINVAL;
+ goto exit0;
+ }
+ if ((cam->crop_current.left != cam->crop_bounds.left)
+ || (cam->crop_current.width != cam->crop_bounds.width)
+ || (cam->crop_current.top != cam->crop_bounds.top)
+ || (cam->crop_current.height != cam->crop_bounds.height)) {
+ pr_info("mxc_v4l_read cropping is not supported\n");
+ err = -EINVAL;
+ goto exit0;
+ }
+
+ cam->still_buf_vaddr = dma_alloc_coherent(0,
+ PAGE_ALIGN(CSI_MEM_SIZE),
+ &cam->still_buf,
+ GFP_DMA | GFP_KERNEL);
+
+ if (!cam->still_buf_vaddr) {
+ pr_info("mxc_v4l_read failed at allocate still_buf\n");
+ err = -ENOBUFS;
+ goto exit0;
+ }
+
+ /* Initialize DMA */
+ g_dma_channel = mxc_dma_request(MXC_DMA_CSI_RX, "CSI RX DMA");
+ if (g_dma_channel < 0) {
+ pr_debug("mxc_v4l_read failed to request DMA channel\n");
+ err = -EIO;
+ goto exit1;
+ }
+
+ err = mxc_dma_callback_set(g_dma_channel,
+ (mxc_dma_callback_t) mxc_csi_dma_callback,
+ (void *)cam);
+ if (err != 0) {
+ pr_debug("mxc_v4l_read failed to set DMA callback\n");
+ err = -EIO;
+ goto exit2;
+ }
+
+ g_user_buf = buf;
+ if (cam->v2f.fmt.pix.sizeimage < count)
+ g_user_count = cam->v2f.fmt.pix.sizeimage;
+ else
+ g_user_count = count & ~0x3;
+
+ tasklet_init(&g_dma_tasklet, mxc_csi_dma_task, (unsigned long)cam);
+ g_dma_status = CSI_DMA_STATUS_DONE;
+ csi_set_callback(mxc_csi_irq_callback, cam);
+ csi_enable_prpif(0);
+
+ /* clear current SOF first */
+ csi_clear_status(BIT_SOF_INT);
+ csi_enable_mclk(CSI_MCLK_RAW, true, true);
+
+ do {
+ g_dma_completed = g_dma_copied = 0;
+ mxc_csi_dma_chaining(cam);
+ cam->still_counter = 0;
+ g_dma_status = CSI_DMA_STATUS_IDLE;
+
+ if (!wait_event_interruptible_timeout(cam->still_queue,
+ cam->still_counter != 0,
+ 10 * HZ)) {
+ pr_info("mxc_v4l_read timeout counter %x\n",
+ cam->still_counter);
+ err = -ETIME;
+ goto exit3;
+ }
+
+ if (g_dma_status == CSI_DMA_STATUS_DONE)
+ break;
+
+ if (retry-- == 0)
+ break;
+
+ pr_debug("Now retry image capture\n");
+ } while (1);
+
+ if (g_dma_status != CSI_DMA_STATUS_DONE)
+ err = -EIO;
+
+ exit3:
+ csi_enable_prpif(1);
+ g_dma_status = CSI_DMA_STATUS_DONE;
+ csi_set_callback(0, 0);
+ csi_enable_mclk(CSI_MCLK_RAW, false, false);
+ tasklet_kill(&g_dma_tasklet);
+
+ exit2:
+ mxc_dma_free(g_dma_channel);
+
+ exit1:
+ dma_free_coherent(0, PAGE_ALIGN(CSI_MEM_SIZE),
+ cam->still_buf_vaddr, cam->still_buf);
+ cam->still_buf = 0;
+
+ exit0:
+ up(&cam->busy_lock);
+ if (err < 0)
+ return err;
+ else
+ return g_user_count;
+}
+#else
+/*!
+ * V4L interface - read function
+ *
+ * @param file struct file *
+ * @param read buf char *
+ * @param count size_t
+ * @param ppos structure loff_t *
+ *
+ * @return bytes read
+ */
+static ssize_t
+mxc_v4l_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ int err = 0;
+ u8 *v_address;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ /* Video capture and still image capture are exclusive */
+ if (cam->capture_on == true) {
+ err = -EBUSY;
+ goto exit0;
+ }
+
+ v_address = dma_alloc_coherent(0,
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+ &cam->still_buf, GFP_DMA | GFP_KERNEL);
+
+ if (!v_address) {
+ pr_info("mxc_v4l_read failed at allocate still_buf\n");
+ err = -ENOBUFS;
+ goto exit0;
+ }
+
+ if (prp_still_select(cam)) {
+ err = -EIO;
+ goto exit1;
+ }
+
+ cam->still_counter = 0;
+ if (cam->csi_start(cam)) {
+ err = -EIO;
+ goto exit2;
+ }
+
+ if (!wait_event_interruptible_timeout(cam->still_queue,
+ cam->still_counter != 0,
+ 10 * HZ)) {
+ pr_info("mxc_v4l_read timeout counter %x\n",
+ cam->still_counter);
+ err = -ETIME;
+ goto exit2;
+ }
+ err = copy_to_user(buf, v_address, cam->v2f.fmt.pix.sizeimage);
+
+ exit2:
+ prp_still_deselect(cam);
+
+ exit1:
+ dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address,
+ cam->still_buf);
+ cam->still_buf = 0;
+
+ exit0:
+ up(&cam->busy_lock);
+ if (err < 0)
+ return err;
+ else
+ return (cam->v2f.fmt.pix.sizeimage - err);
+}
+#endif /* CONFIG_VIDEO_MXC_CSI_DMA */
+
+/*!
+ * V4L interface - ioctl function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void *
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static int
+mxc_v4l_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int retval = 0;
+ unsigned long lock_flags;
+
+ if (!cam)
+ return -EBADF;
+
+ wait_event_interruptible(cam->power_queue, cam->low_power == false);
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ /*!
+ * V4l2 VIDIOC_QUERYCAP ioctl
+ */
+ case VIDIOC_QUERYCAP:{
+ struct v4l2_capability *cap = arg;
+ strcpy(cap->driver, "mxc_v4l2");
+ cap->version = KERNEL_VERSION(0, 1, 11);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING
+ | V4L2_CAP_READWRITE;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ retval = 0;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_FMT ioctl
+ */
+ case VIDIOC_G_FMT:{
+ struct v4l2_format *gf = arg;
+ retval = mxc_v4l2_g_fmt(cam, gf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FMT ioctl
+ */
+ case VIDIOC_S_FMT:{
+ struct v4l2_format *sf = arg;
+ retval = mxc_v4l2_s_fmt(cam, sf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_REQBUFS ioctl
+ */
+ case VIDIOC_REQBUFS:{
+ struct v4l2_requestbuffers *req = arg;
+ if (req->count > FRAME_NUM) {
+ pr_info("VIDIOC_REQBUFS: not enough buffer\n");
+ req->count = FRAME_NUM;
+ }
+
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ pr_debug("VIDIOC_REQBUFS: wrong buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ mxc_streamoff(cam);
+ mxc_free_frame_buf(cam);
+
+ retval = mxc_allocate_frame_buf(cam, req->count);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_QUERYBUF ioctl
+ */
+ case VIDIOC_QUERYBUF:{
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_debug
+ ("VIDIOC_QUERYBUFS: wrong buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ buf->index = index;
+
+ down(&cam->param_lock);
+ retval = mxc_v4l2_buffer_status(cam, buf);
+ up(&cam->param_lock);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_QBUF ioctl
+ */
+ case VIDIOC_QBUF:{
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+
+ pr_debug("VIDIOC_QBUF: %d\n", buf->index);
+
+ spin_lock_irqsave(&cam->int_lock, lock_flags);
+ if ((cam->frame[index].buffer.flags & 0x7) ==
+ V4L2_BUF_FLAG_MAPPED) {
+ cam->frame[index].buffer.flags |=
+ V4L2_BUF_FLAG_QUEUED;
+ if (cam->skip_frame > 0) {
+ list_add_tail(&cam->frame[index].queue,
+ &cam->working_q);
+ retval =
+ cam->enc_update_eba(cam->
+ frame[index].
+ paddress,
+ &cam->
+ ping_pong_csi);
+ cam->skip_frame = 0;
+ } else {
+ list_add_tail(&cam->frame[index].queue,
+ &cam->ready_q);
+ }
+ } else if (cam->frame[index].buffer.flags &
+ V4L2_BUF_FLAG_QUEUED) {
+ pr_debug
+ ("VIDIOC_QBUF: buffer already queued\n");
+ } else if (cam->frame[index].buffer.
+ flags & V4L2_BUF_FLAG_DONE) {
+ pr_debug
+ ("VIDIOC_QBUF: overwrite done buffer.\n");
+ cam->frame[index].buffer.flags &=
+ ~V4L2_BUF_FLAG_DONE;
+ cam->frame[index].buffer.flags |=
+ V4L2_BUF_FLAG_QUEUED;
+ }
+ buf->flags = cam->frame[index].buffer.flags;
+ spin_unlock_irqrestore(&cam->int_lock, lock_flags);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_DQBUF ioctl
+ */
+ case VIDIOC_DQBUF:{
+ struct v4l2_buffer *buf = arg;
+
+ retval = mxc_v4l_dqueue(cam, buf);
+
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_STREAMON ioctl
+ */
+ case VIDIOC_STREAMON:{
+ cam->capture_on = true;
+ retval = mxc_streamon(cam);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_STREAMOFF ioctl
+ */
+ case VIDIOC_STREAMOFF:{
+ retval = mxc_streamoff(cam);
+ cam->capture_on = false;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_CTRL ioctl
+ */
+ case VIDIOC_G_CTRL:{
+ retval = mxc_get_v42l_control(cam, arg);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_CTRL ioctl
+ */
+ case VIDIOC_S_CTRL:{
+ retval = mxc_set_v42l_control(cam, arg);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_CROPCAP ioctl
+ */
+ case VIDIOC_CROPCAP:{
+ struct v4l2_cropcap *cap = arg;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+ cap->bounds = cam->crop_bounds;
+ cap->defrect = cam->crop_defrect;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_CROP ioctl
+ */
+ case VIDIOC_G_CROP:{
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+ crop->c = cam->crop_current;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_CROP ioctl
+ */
+ case VIDIOC_S_CROP:{
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b = &cam->crop_bounds;
+ int i;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+
+ crop->c.top = (crop->c.top < b->top) ? b->top
+ : crop->c.top;
+ if (crop->c.top > b->top + b->height)
+ crop->c.top = b->top + b->height - 1;
+ if (crop->c.height > b->top + b->height - crop->c.top)
+ crop->c.height =
+ b->top + b->height - crop->c.top;
+
+ crop->c.left = (crop->c.left < b->left) ? b->left
+ : crop->c.left;
+ if (crop->c.left > b->left + b->width)
+ crop->c.left = b->left + b->width - 1;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width =
+ b->left - crop->c.left + b->width;
+
+ crop->c.width &= ~0x1;
+
+ /*
+ * MX27 PrP limitation:
+ * The right spare space (CSI_FRAME_X_SIZE
+ * - SOURCE_LINE_STRIDE - PICTURE_X_SIZE)) must be
+ * multiple of 32.
+ * So we tune the crop->c.left value to the closest
+ * desired cropping value and meet the PrP requirement.
+ */
+ i = ((b->left + b->width)
+ - (crop->c.left + crop->c.width)) % 32;
+ if (i <= 16) {
+ if (crop->c.left + crop->c.width + i
+ <= b->left + b->width)
+ crop->c.left += i;
+ else if (crop->c.left - (32 - i) >= b->left)
+ crop->c.left -= 32 - i;
+ else {
+ retval = -EINVAL;
+ break;
+ }
+ } else {
+ if (crop->c.left - (32 - i) >= b->left)
+ crop->c.left -= 32 - i;
+ else if (crop->c.left + crop->c.width + i
+ <= b->left + b->width)
+ crop->c.left += i;
+ else {
+ retval = -EINVAL;
+ break;
+ }
+ }
+
+ cam->crop_current = crop->c;
+
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_OVERLAY ioctl
+ */
+ case VIDIOC_OVERLAY:{
+ int *on = arg;
+ if (*on) {
+ cam->overlay_on = true;
+ retval = start_preview(cam);
+ }
+ if (!*on) {
+ retval = stop_preview(cam);
+ cam->overlay_on = false;
+ }
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_FBUF ioctl
+ */
+ case VIDIOC_G_FBUF:{
+ struct v4l2_framebuffer *fb = arg;
+ struct fb_var_screeninfo *var;
+
+ if (cam->output >= num_registered_fb) {
+ retval = -EINVAL;
+ break;
+ }
+
+ var = &registered_fb[cam->output]->var;
+ cam->v4l2_fb.fmt.width = var->xres;
+ cam->v4l2_fb.fmt.height = var->yres;
+ cam->v4l2_fb.fmt.bytesperline =
+ var->xres_virtual * var->bits_per_pixel;
+ cam->v4l2_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB;
+ *fb = cam->v4l2_fb;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FBUF ioctl
+ */
+ case VIDIOC_S_FBUF:{
+ struct v4l2_framebuffer *fb = arg;
+ cam->v4l2_fb.flags = fb->flags;
+ cam->v4l2_fb.fmt.pixelformat = fb->fmt.pixelformat;
+ break;
+ }
+
+ case VIDIOC_G_PARM:{
+ struct v4l2_streamparm *parm = arg;
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_debug("VIDIOC_G_PARM invalid type\n");
+ retval = -EINVAL;
+ break;
+ }
+ parm->parm.capture = cam->streamparm.parm.capture;
+ break;
+ }
+ case VIDIOC_S_PARM:{
+ struct v4l2_streamparm *parm = arg;
+ retval = mxc_v4l2_s_param(cam, parm);
+ break;
+ }
+
+ /* linux v4l2 bug, kernel c0485619 user c0405619 */
+ case VIDIOC_ENUMSTD:{
+ struct v4l2_standard *e = arg;
+ *e = cam->standard;
+ pr_debug("VIDIOC_ENUMSTD call\n");
+ retval = 0;
+ break;
+ }
+
+ case VIDIOC_G_STD:{
+ v4l2_std_id *e = arg;
+ *e = cam->standard.id;
+ break;
+ }
+
+ case VIDIOC_S_STD:{
+ break;
+ }
+
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *output = arg;
+
+ if (output->index >= num_registered_fb) {
+ retval = -EINVAL;
+ break;
+ }
+
+ strncpy(output->name,
+ registered_fb[output->index]->fix.id, 31);
+ output->type = V4L2_OUTPUT_TYPE_ANALOG;
+ output->audioset = 0;
+ output->modulator = 0;
+ output->std = V4L2_STD_UNKNOWN;
+
+ break;
+ }
+ case VIDIOC_G_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ *p_output_num = cam->output;
+ break;
+ }
+ case VIDIOC_S_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ if (*p_output_num >= num_registered_fb) {
+ retval = -EINVAL;
+ break;
+ }
+
+ cam->output = *p_output_num;
+ break;
+ }
+
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_ENUMINPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ default:
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&cam->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L interface - ioctl function
+ *
+ * @return None
+ */
+static int
+mxc_v4l_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, mxc_v4l_do_ioctl);
+}
+
+/*!
+ * V4L interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = video_devdata(file);
+ unsigned long size;
+ int res = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ pr_debug("pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ size = vma->vm_end - vma->vm_start;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ pr_debug("mxc_mmap: remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+ mxc_mmap_exit:
+ up(&cam->busy_lock);
+ return res;
+}
+
+/*!
+ * V4L interface - poll function
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_poll(struct file *file, poll_table * wait)
+{
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ queue = &cam->enc_queue;
+ poll_wait(file, queue, wait);
+
+ up(&cam->busy_lock);
+ return res;
+}
+
+static struct
+file_operations mxc_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_v4l_open,
+ .release = mxc_v4l_close,
+ .read = mxc_v4l_read,
+ .ioctl = mxc_v4l_ioctl,
+ .mmap = mxc_mmap,
+ .poll = mxc_poll,
+};
+
+static struct video_device mxc_v4l_template = {
+ .name = "Mxc Camera",
+ .vfl_type = VID_TYPE_CAPTURE,
+ .fops = &mxc_v4l_fops,
+ .release = video_device_release,
+};
+
+static void camera_platform_release(struct device *device)
+{
+}
+
+/*! Device Definition for Mt9v111 devices */
+static struct platform_device mxc_v4l2_devices = {
+ .name = "mxc_v4l2",
+ .dev = {
+ .release = camera_platform_release,
+ },
+ .id = 0,
+};
+
+extern struct camera_sensor camera_sensor_if;
+
+/*!
+* Camera V4l2 callback function.
+*
+* @return status
+*/
+static void camera_callback(u32 mask, void *dev)
+{
+ struct mxc_v4l_frame *done_frame;
+ struct mxc_v4l_frame *ready_frame;
+
+ cam_data *cam = (cam_data *) dev;
+ if (cam == NULL)
+ return;
+
+ if (list_empty(&cam->working_q)) {
+ printk(KERN_ERR "camera_callback: working queue empty\n");
+ return;
+ }
+
+ done_frame =
+ list_entry(cam->working_q.next, struct mxc_v4l_frame, queue);
+ if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE;
+ done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+
+ if (list_empty(&cam->ready_q)) {
+ cam->skip_frame++;
+ } else {
+ ready_frame =
+ list_entry(cam->ready_q.next, struct mxc_v4l_frame,
+ queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&ready_frame->queue, &cam->working_q);
+ cam->enc_update_eba(ready_frame->paddress,
+ &cam->ping_pong_csi);
+ }
+
+ /* Added to the done queue */
+ list_del(cam->working_q.next);
+ list_add_tail(&done_frame->queue, &cam->done_q);
+
+ /* Wake up the queue */
+ cam->enc_counter++;
+ wake_up_interruptible(&cam->enc_queue);
+ } else {
+ printk(KERN_ERR "camera_callback :buffer not queued\n");
+ }
+}
+
+/*!
+ * initialize cam_data structure
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static void init_camera_struct(cam_data *cam)
+{
+ int i;
+
+ /* Default everything to 0 */
+ memset(cam, 0, sizeof(cam_data));
+
+ init_MUTEX(&cam->param_lock);
+ init_MUTEX(&cam->busy_lock);
+
+ cam->video_dev = video_device_alloc();
+ if (cam->video_dev == NULL)
+ return;
+
+ *(cam->video_dev) = mxc_v4l_template;
+
+ video_set_drvdata(cam->video_dev, cam);
+ dev_set_drvdata(&mxc_v4l2_devices.dev, (void *)cam);
+ cam->video_dev->minor = -1;
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ cam->frame[i].width = 0;
+ cam->frame[i].height = 0;
+ cam->frame[i].paddress = 0;
+ }
+
+ init_waitqueue_head(&cam->enc_queue);
+ init_waitqueue_head(&cam->still_queue);
+
+ /* setup cropping */
+ cam->crop_bounds.left = 0;
+ cam->crop_bounds.width = 640;
+ cam->crop_bounds.top = 0;
+ cam->crop_bounds.height = 480;
+ cam->crop_current = cam->crop_defrect = cam->crop_bounds;
+ cam->streamparm.parm.capture.capturemode = 0;
+
+ cam->standard.index = 0;
+ cam->standard.id = V4L2_STD_UNKNOWN;
+ cam->standard.frameperiod.denominator = 30;
+ cam->standard.frameperiod.numerator = 1;
+ cam->standard.framelines = 480;
+ cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;
+ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ cam->overlay_on = false;
+ cam->capture_on = false;
+ cam->skip_frame = 0;
+ cam->v4l2_fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ cam->v4l2_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
+
+ cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2;
+ cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2;
+ cam->v2f.fmt.pix.width = 288;
+ cam->v2f.fmt.pix.height = 352;
+ cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+ cam->win.w.width = 160;
+ cam->win.w.height = 160;
+ cam->win.w.left = 0;
+ cam->win.w.top = 0;
+
+ cam->cam_sensor = &camera_sensor_if;
+ cam->enc_callback = camera_callback;
+
+ init_waitqueue_head(&cam->power_queue);
+ cam->int_lock = __SPIN_LOCK_UNLOCKED(cam->int_lock);
+ spin_lock_init(&cam->int_lock);
+}
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+/*!
+ * camera_power function
+ * Turn Sensor power On/Off
+ *
+ * @param cameraOn true to turn camera on, otherwise shut down
+ *
+ * @return status
+ */
+static u8 camera_power(bool cameraOn)
+{
+ if (cameraOn == true) {
+ gpio_sensor_active();
+ csi_enable_mclk(csi_mclk_flag_backup, true, true);
+ } else {
+ csi_mclk_flag_backup = csi_read_mclk_flag();
+ csi_enable_mclk(csi_mclk_flag_backup, false, false);
+ gpio_sensor_inactive();
+ }
+ return 0;
+}
+
+/*!
+ * This function is called to put the sensor in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure.
+ */
+static int mxc_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ if (cam == NULL) {
+ return -1;
+ }
+
+ cam->low_power = true;
+
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+ if ((cam->capture_on == true) && cam->enc_disable) {
+ cam->enc_disable(cam);
+ }
+ camera_power(false);
+
+ return 0;
+}
+
+/*!
+ * This function is called to bring the sensor back from a low power state.Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxc_v4l2_resume(struct platform_device *pdev)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ if (cam == NULL) {
+ return -1;
+ }
+
+ cam->low_power = false;
+ wake_up_interruptible(&cam->power_queue);
+
+ if (cam->overlay_on == true)
+ start_preview(cam);
+ if (cam->capture_on == true)
+ mxc_streamon(cam);
+ camera_power(true);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2_driver = {
+ .driver = {
+ .name = "mxc_v4l2",
+ .owner = THIS_MODULE,
+ .bus = &platform_bus_type,
+ },
+ .probe = NULL,
+ .remove = NULL,
+ .suspend = mxc_v4l2_suspend,
+ .resume = mxc_v4l2_resume,
+ .shutdown = NULL,
+};
+
+/*!
+ * Entry point for the V4L2
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int camera_init(void)
+{
+ u8 err = 0;
+ cam_data *cam;
+
+ g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL);
+ if (g_cam == NULL) {
+ pr_debug("failed to mxc_v4l_register_camera\n");
+ return -1;
+ }
+
+ cam = g_cam;
+ init_camera_struct(cam);
+
+ /* Register the I2C device */
+ err = platform_device_register(&mxc_v4l2_devices);
+ if (err != 0) {
+ pr_debug("camera_init: platform_device_register failed.\n");
+ video_device_release(cam->video_dev);
+ kfree(cam);
+ g_cam = NULL;
+ }
+
+ /* Register the device driver structure. */
+ err = platform_driver_register(&mxc_v4l2_driver);
+ if (err != 0) {
+ platform_device_unregister(&mxc_v4l2_devices);
+ pr_debug("camera_init: driver_register failed.\n");
+ video_device_release(cam->video_dev);
+ kfree(cam);
+ g_cam = NULL;
+ return err;
+ }
+
+ /* register v4l device */
+ if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr)
+ == -1) {
+ platform_driver_unregister(&mxc_v4l2_driver);
+ platform_device_unregister(&mxc_v4l2_devices);
+ video_device_release(cam->video_dev);
+ kfree(cam);
+ g_cam = NULL;
+ pr_debug("video_register_device failed\n");
+ return -1;
+ }
+
+ return err;
+}
+
+/*!
+ * Exit and cleanup for the V4L2
+ *
+ */
+static void __exit camera_exit(void)
+{
+ pr_debug("unregistering video\n");
+
+ video_unregister_device(g_cam->video_dev);
+
+ platform_driver_unregister(&mxc_v4l2_driver);
+ platform_device_unregister(&mxc_v4l2_devices);
+
+ if (g_cam->open_count) {
+ pr_debug("camera open -- setting ops to NULL\n");
+ } else {
+ pr_debug("freeing camera\n");
+ mxc_free_frame_buf(g_cam);
+ kfree(g_cam);
+ g_cam = NULL;
+ }
+}
+
+module_init(camera_init);
+module_exit(camera_exit);
+
+module_param(video_nr, int, 0444);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2 capture driver for Mxc based cameras");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/capture/fsl_csi.c b/drivers/media/video/mxc/capture/fsl_csi.c
new file mode 100644
index 000000000000..4f14f26eede1
--- /dev/null
+++ b/drivers/media/video/mxc/capture/fsl_csi.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_csi.c, this file is derived from mx27_csi.c
+ *
+ * @brief mx25 CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+
+#include "mxc_v4l2_capture.h"
+#include "fsl_csi.h"
+
+static bool g_csi_mclk_on;
+static csi_irq_callback_t g_callback;
+static void *g_callback_data;
+static struct clk csi_mclk;
+
+static irqreturn_t csi_irq_handler(int irq, void *data)
+{
+ cam_data *cam = (cam_data *) data;
+ unsigned long status = __raw_readl(CSI_CSISR);
+ unsigned long cr3 = __raw_readl(CSI_CSICR3);
+ unsigned int frame_count = (cr3 >> 16) & 0xFFFF;
+
+ __raw_writel(status, CSI_CSISR);
+
+ if (status & BIT_SOF_INT) {
+ /* reflash the embeded DMA controller */
+ if (frame_count % 2 == 1)
+ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+ }
+
+ if (status & BIT_DMA_TSF_DONE_FB2) {
+ if (frame_count == 2) {
+ cam->still_counter++;
+ wake_up_interruptible(&cam->still_queue);
+ }
+ }
+
+ if (g_callback)
+ g_callback(g_callback_data, status);
+
+ pr_debug("CSI status = 0x%08lX\n", status);
+
+ return IRQ_HANDLED;
+}
+
+static void csihw_reset_frame_count(void)
+{
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, CSI_CSICR3);
+}
+
+static void csihw_reset(void)
+{
+ csihw_reset_frame_count();
+ __raw_writel(CSICR1_RESET_VAL, CSI_CSICR1);
+ __raw_writel(CSICR2_RESET_VAL, CSI_CSICR2);
+ __raw_writel(CSICR3_RESET_VAL, CSI_CSICR3);
+}
+
+/*!
+ * csi_init_interface
+ * Init csi interface
+ */
+void csi_init_interface(void)
+{
+ unsigned int val = 0;
+ unsigned int imag_para;
+
+ val |= BIT_SOF_POL;
+ val |= BIT_REDGE;
+ val |= BIT_GCLK_MODE;
+ val |= BIT_HSYNC_POL;
+ val |= BIT_PACK_DIR;
+ val |= BIT_FCC;
+ val |= BIT_SWAP16_EN;
+ val |= 1 << SHIFT_MCLKDIV;
+ __raw_writel(val, CSI_CSICR1);
+
+ imag_para = (640 << 16) | 960;
+ __raw_writel(imag_para, CSI_CSIIMAG_PARA);
+
+ val = 0x1010;
+ val |= BIT_DMA_REFLASH_RFF;
+ __raw_writel(val, CSI_CSICR3);
+}
+EXPORT_SYMBOL(csi_init_interface);
+
+/*!
+ * csi_enable_mclk
+ *
+ * @param src enum define which source to control the clk
+ * CSI_MCLK_VF CSI_MCLK_ENC CSI_MCLK_RAW CSI_MCLK_I2C
+ * @param flag true to enable mclk, false to disable mclk
+ * @param wait true to wait 100ms make clock stable, false not wait
+ *
+ * @return 0 for success
+ */
+int32_t csi_enable_mclk(int src, bool flag, bool wait)
+{
+ if (flag == true) {
+ csi_mclk_enable();
+ if (wait == true)
+ msleep(10);
+ pr_debug("Enable csi clock from source %d\n", src);
+ g_csi_mclk_on = true;
+ } else {
+ csi_mclk_disable();
+ pr_debug("Disable csi clock from source %d\n", src);
+ g_csi_mclk_on = false;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(csi_enable_mclk);
+
+/*!
+ * csi_read_mclk_flag
+ *
+ * @return gcsi_mclk_source
+ */
+int csi_read_mclk_flag(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(csi_read_mclk_flag);
+
+void csi_start_callback(void *data)
+{
+ cam_data *cam = (cam_data *) data;
+
+ if (request_irq(MXC_INT_CSI, csi_irq_handler, 0, "csi", cam) < 0)
+ pr_debug("CSI error: irq request fail\n");
+
+}
+EXPORT_SYMBOL(csi_start_callback);
+
+void csi_stop_callback(void *data)
+{
+ cam_data *cam = (cam_data *) data;
+
+ free_irq(MXC_INT_CSI, cam);
+}
+EXPORT_SYMBOL(csi_stop_callback);
+
+void csi_enable_int(int arg)
+{
+ unsigned long cr1 = __raw_readl(CSI_CSICR1);
+
+ cr1 |= BIT_SOF_INTEN;
+ if (arg == 1) {
+ /* still capture needs DMA intterrupt */
+ cr1 |= BIT_FB1_DMA_DONE_INTEN;
+ cr1 |= BIT_FB2_DMA_DONE_INTEN;
+ }
+ __raw_writel(cr1, CSI_CSICR1);
+}
+EXPORT_SYMBOL(csi_enable_int);
+
+void csi_disable_int(void)
+{
+ unsigned long cr1 = __raw_readl(CSI_CSICR1);
+
+ cr1 &= ~BIT_SOF_INTEN;
+ cr1 &= ~BIT_FB1_DMA_DONE_INTEN;
+ cr1 &= ~BIT_FB2_DMA_DONE_INTEN;
+ __raw_writel(cr1, CSI_CSICR1);
+}
+EXPORT_SYMBOL(csi_disable_int);
+
+void csi_set_16bit_imagpara(int width, int height)
+{
+ int imag_para = 0;
+ unsigned long cr3 = __raw_readl(CSI_CSICR3);
+
+ imag_para = (width << 16) | (height * 2);
+ __raw_writel(imag_para, CSI_CSIIMAG_PARA);
+
+ /* reflash the embeded DMA controller */
+ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+}
+EXPORT_SYMBOL(csi_set_16bit_imagpara);
+
+void csi_set_12bit_imagpara(int width, int height)
+{
+ int imag_para = 0;
+ unsigned long cr3 = __raw_readl(CSI_CSICR3);
+
+ imag_para = (width << 16) | (height * 3 / 2);
+ __raw_writel(imag_para, CSI_CSIIMAG_PARA);
+
+ /* reflash the embeded DMA controller */
+ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, CSI_CSICR3);
+}
+EXPORT_SYMBOL(csi_set_12bit_imagpara);
+
+static void csi_mclk_recalc(struct clk *clk)
+{
+ u32 div;
+
+ div = (__raw_readl(CSI_CSICR1) & BIT_MCLKDIV) >> SHIFT_MCLKDIV;
+ if (div == 0)
+ div = 1;
+ else
+ div = div * 2;
+
+ clk->rate = clk->parent->rate / div;
+}
+
+void csi_mclk_enable(void)
+{
+ __raw_writel(__raw_readl(CSI_CSICR1) | BIT_MCLKEN, CSI_CSICR1);
+}
+
+void csi_mclk_disable(void)
+{
+ __raw_writel(__raw_readl(CSI_CSICR1) & ~BIT_MCLKEN, CSI_CSICR1);
+}
+
+int32_t __init csi_init_module(void)
+{
+ int ret = 0;
+ struct clk *per_clk;
+
+ csihw_reset();
+ csi_init_interface();
+
+ per_clk = clk_get(NULL, "csi_clk");
+ if (IS_ERR(per_clk))
+ return PTR_ERR(per_clk);
+
+ clk_put(per_clk);
+ csi_mclk.name = "csi_mclk";
+ csi_mclk.parent = per_clk;
+ clk_register(&csi_mclk);
+ clk_enable(per_clk);
+ csi_mclk_recalc(&csi_mclk);
+
+ return ret;
+}
+
+void __exit csi_cleanup_module(void)
+{
+ clk_disable(&csi_mclk);
+ clk_unregister(&csi_mclk);
+}
+
+module_init(csi_init_module);
+module_exit(csi_cleanup_module);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("fsl CSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/fsl_csi.h b/drivers/media/video/mxc/capture/fsl_csi.h
new file mode 100644
index 000000000000..41bfff03f07e
--- /dev/null
+++ b/drivers/media/video/mxc/capture/fsl_csi.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_csi.h
+ *
+ * @brief mx25 CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+
+#ifndef MX25_CSI_H
+#define MX25_CSI_H
+
+#include <linux/io.h>
+
+/* reset values */
+#define CSICR1_RESET_VAL 0x40000800
+#define CSICR2_RESET_VAL 0x0
+#define CSICR3_RESET_VAL 0x0
+
+/* csi control reg 1 */
+#define BIT_SWAP16_EN (0x1 << 31)
+#define BIT_EXT_VSYNC (0x1 << 30)
+#define BIT_EOF_INT_EN (0x1 << 29)
+#define BIT_PRP_IF_EN (0x1 << 28)
+#define BIT_CCIR_MODE (0x1 << 27)
+#define BIT_COF_INT_EN (0x1 << 26)
+#define BIT_SF_OR_INTEN (0x1 << 25)
+#define BIT_RF_OR_INTEN (0x1 << 24)
+#define BIT_SFF_DMA_DONE_INTEN (0x1 << 22)
+#define BIT_STATFF_INTEN (0x1 << 21)
+#define BIT_FB2_DMA_DONE_INTEN (0x1 << 20)
+#define BIT_FB1_DMA_DONE_INTEN (0x1 << 19)
+#define BIT_RXFF_INTEN (0x1 << 18)
+#define BIT_SOF_POL (0x1 << 17)
+#define BIT_SOF_INTEN (0x1 << 16)
+#define BIT_MCLKDIV (0xF << 12)
+#define BIT_HSYNC_POL (0x1 << 11)
+#define BIT_CCIR_EN (0x1 << 10)
+#define BIT_MCLKEN (0x1 << 9)
+#define BIT_FCC (0x1 << 8)
+#define BIT_PACK_DIR (0x1 << 7)
+#define BIT_CLR_STATFIFO (0x1 << 6)
+#define BIT_CLR_RXFIFO (0x1 << 5)
+#define BIT_GCLK_MODE (0x1 << 4)
+#define BIT_INV_DATA (0x1 << 3)
+#define BIT_INV_PCLK (0x1 << 2)
+#define BIT_REDGE (0x1 << 1)
+#define BIT_PIXEL_BIT (0x1 << 0)
+
+#define SHIFT_MCLKDIV 12
+
+/* control reg 3 */
+#define BIT_FRMCNT (0xFFFF << 16)
+#define BIT_FRMCNT_RST (0x1 << 15)
+#define BIT_DMA_REFLASH_RFF (0x1 << 14)
+#define BIT_DMA_REFLASH_SFF (0x1 << 13)
+#define BIT_DMA_REQ_EN_RFF (0x1 << 12)
+#define BIT_DMA_REQ_EN_SFF (0x1 << 11)
+#define BIT_STATFF_LEVEL (0x7 << 8)
+#define BIT_HRESP_ERR_EN (0x1 << 7)
+#define BIT_RXFF_LEVEL (0x7 << 4)
+#define BIT_TWO_8BIT_SENSOR (0x1 << 3)
+#define BIT_ZERO_PACK_EN (0x1 << 2)
+#define BIT_ECC_INT_EN (0x1 << 1)
+#define BIT_ECC_AUTO_EN (0x1 << 0)
+
+#define SHIFT_FRMCNT 16
+
+/* csi status reg */
+#define BIT_SFF_OR_INT (0x1 << 25)
+#define BIT_RFF_OR_INT (0x1 << 24)
+#define BIT_DMA_TSF_DONE_SFF (0x1 << 22)
+#define BIT_STATFF_INT (0x1 << 21)
+#define BIT_DMA_TSF_DONE_FB2 (0x1 << 20)
+#define BIT_DMA_TSF_DONE_FB1 (0x1 << 19)
+#define BIT_RXFF_INT (0x1 << 18)
+#define BIT_EOF_INT (0x1 << 17)
+#define BIT_SOF_INT (0x1 << 16)
+#define BIT_F2_INT (0x1 << 15)
+#define BIT_F1_INT (0x1 << 14)
+#define BIT_COF_INT (0x1 << 13)
+#define BIT_HRESP_ERR_INT (0x1 << 7)
+#define BIT_ECC_INT (0x1 << 1)
+#define BIT_DRDY (0x1 << 0)
+
+#define CSI_MCLK_VF 1
+#define CSI_MCLK_ENC 2
+#define CSI_MCLK_RAW 4
+#define CSI_MCLK_I2C 8
+#endif
+
+#define CSI_CSICR1 (IO_ADDRESS(CSI_BASE_ADDR))
+#define CSI_CSICR2 (IO_ADDRESS(CSI_BASE_ADDR + 0x4))
+#define CSI_CSICR3 (IO_ADDRESS(CSI_BASE_ADDR + 0x8))
+#define CSI_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0xC))
+#define CSI_CSIRXFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x10))
+#define CSI_CSIRXCNT (IO_ADDRESS(CSI_BASE_ADDR + 0x14))
+#define CSI_CSISR (IO_ADDRESS(CSI_BASE_ADDR + 0x18))
+
+#define CSI_CSIDBG (IO_ADDRESS(CSI_BASE_ADDR + 0x1C))
+#define CSI_CSIDMASA_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x20))
+#define CSI_CSIDMATS_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x24))
+#define CSI_CSIDMASA_FB1 (IO_ADDRESS(CSI_BASE_ADDR + 0x28))
+#define CSI_CSIDMASA_FB2 (IO_ADDRESS(CSI_BASE_ADDR + 0x2C))
+#define CSI_CSIFBUF_PARA (IO_ADDRESS(CSI_BASE_ADDR + 0x30))
+#define CSI_CSIIMAG_PARA (IO_ADDRESS(CSI_BASE_ADDR + 0x34))
+
+#define CSI_CSIRXFIFO_PHYADDR (CSI_BASE_ADDR + 0x10)
+
+static inline void csi_clear_status(unsigned long status)
+{
+ __raw_writel(status, CSI_CSISR);
+}
+
+struct csi_signal_cfg_t {
+ unsigned data_width:3;
+ unsigned clk_mode:2;
+ unsigned ext_vsync:1;
+ unsigned Vsync_pol:1;
+ unsigned Hsync_pol:1;
+ unsigned pixclk_pol:1;
+ unsigned data_pol:1;
+ unsigned sens_clksrc:1;
+};
+
+struct csi_config_t {
+ /* control reg 1 */
+ unsigned int swap16_en:1;
+ unsigned int ext_vsync:1;
+ unsigned int eof_int_en:1;
+ unsigned int prp_if_en:1;
+ unsigned int ccir_mode:1;
+ unsigned int cof_int_en:1;
+ unsigned int sf_or_inten:1;
+ unsigned int rf_or_inten:1;
+ unsigned int sff_dma_done_inten:1;
+ unsigned int statff_inten:1;
+ unsigned int fb2_dma_done_inten:1;
+ unsigned int fb1_dma_done_inten:1;
+ unsigned int rxff_inten:1;
+ unsigned int sof_pol:1;
+ unsigned int sof_inten:1;
+ unsigned int mclkdiv:4;
+ unsigned int hsync_pol:1;
+ unsigned int ccir_en:1;
+ unsigned int mclken:1;
+ unsigned int fcc:1;
+ unsigned int pack_dir:1;
+ unsigned int gclk_mode:1;
+ unsigned int inv_data:1;
+ unsigned int inv_pclk:1;
+ unsigned int redge:1;
+ unsigned int pixel_bit:1;
+
+ /* control reg 3 */
+ unsigned int frmcnt:16;
+ unsigned int frame_reset:1;
+ unsigned int dma_reflash_rff:1;
+ unsigned int dma_reflash_sff:1;
+ unsigned int dma_req_en_rff:1;
+ unsigned int dma_req_en_sff:1;
+ unsigned int statff_level:3;
+ unsigned int hresp_err_en:1;
+ unsigned int rxff_level:3;
+ unsigned int two_8bit_sensor:1;
+ unsigned int zero_pack_en:1;
+ unsigned int ecc_int_en:1;
+ unsigned int ecc_auto_en:1;
+ /* fifo counter */
+ unsigned int rxcnt;
+};
+
+typedef void (*csi_irq_callback_t) (void *data, unsigned long status);
+
+int32_t csi_enable_mclk(int src, bool flag, bool wait);
+void csi_init_interface(void);
+void csi_set_16bit_imagpara(int width, int height);
+void csi_set_12bit_imagpara(int width, int height);
+int csi_read_mclk_flag(void);
+void csi_start_callback(void *data);
+void csi_stop_callback(void *data);
+void csi_enable_int(int arg);
+void csi_disable_int(void);
+void csi_mclk_enable(void);
+void csi_mclk_disable(void);
diff --git a/drivers/media/video/mxc/capture/ipu_csi_enc.c b/drivers/media/video/mxc/capture/ipu_csi_enc.c
new file mode 100644
index 000000000000..fb3d0d7d5ec6
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_csi_enc.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file csi_enc.c
+ *
+ * @brief CSI Use case for video capture
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+#ifdef CAMERA_DBG
+ #define CAMERA_TRACE(x) (printk)x
+#else
+ #define CAMERA_TRACE(x)
+#endif
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * csi ENC callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t csi_enc_callback(int irq, void *dev_id)
+{
+ cam_data *cam = (cam_data *) dev_id;
+
+ if (cam->enc_callback == NULL)
+ return IRQ_HANDLED;
+
+ cam->enc_callback(irq, dev_id);
+ return IRQ_HANDLED;
+}
+
+/*!
+ * CSI ENC enable channel setup function
+ *
+ * @param cam struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int csi_enc_setup(cam_data *cam)
+{
+ ipu_channel_params_t params;
+ u32 pixel_fmt;
+ int err = 0;
+ dma_addr_t dummy = 0xdeadbeaf;
+
+ CAMERA_TRACE("In csi_enc_setup\n");
+ if (!cam) {
+ printk(KERN_ERR "cam private is NULL\n");
+ return -ENXIO;
+ }
+
+ memset(&params, 0, sizeof(ipu_channel_params_t));
+ params.csi_mem.csi = cam->csi;
+
+ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ pixel_fmt = IPU_PIX_FMT_YUV420P;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P)
+ pixel_fmt = IPU_PIX_FMT_YUV422P;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
+ pixel_fmt = IPU_PIX_FMT_UYVY;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)
+ pixel_fmt = IPU_PIX_FMT_NV12;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
+ pixel_fmt = IPU_PIX_FMT_BGR24;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
+ pixel_fmt = IPU_PIX_FMT_RGB24;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
+ pixel_fmt = IPU_PIX_FMT_RGB565;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32)
+ pixel_fmt = IPU_PIX_FMT_BGR32;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32)
+ pixel_fmt = IPU_PIX_FMT_RGB32;
+ else {
+ printk(KERN_ERR "format not supported\n");
+ return -EINVAL;
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, true, true);
+
+ err = ipu_init_channel(CSI_MEM, &params);
+ if (err != 0) {
+ printk(KERN_ERR "ipu_init_channel %d\n", err);
+ return err;
+ }
+
+ err = ipu_init_channel_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+ pixel_fmt, cam->v2f.fmt.pix.width,
+ cam->v2f.fmt.pix.height,
+ cam->v2f.fmt.pix.width, IPU_ROTATE_NONE,
+ dummy, dummy,
+ cam->offset.u_offset,
+ cam->offset.v_offset);
+ if (err != 0) {
+ printk(KERN_ERR "CSI_MEM output buffer\n");
+ return err;
+ }
+ err = ipu_enable_channel(CSI_MEM);
+ if (err < 0) {
+ printk(KERN_ERR "ipu_enable_channel CSI_MEM\n");
+ return err;
+ }
+
+ return err;
+}
+
+/*!
+ * function to update physical buffer address for encorder IDMA channel
+ *
+ * @param eba physical buffer address for encorder IDMA channel
+ * @param buffer_num int buffer 0 or buffer 1
+ *
+ * @return status
+ */
+static int csi_enc_eba_update(dma_addr_t eba, int *buffer_num)
+{
+ int err = 0;
+
+ pr_debug("eba %x\n", eba);
+ err = ipu_update_channel_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+ *buffer_num, eba);
+ if (err != 0) {
+ printk(KERN_ERR "err %d buffer_num %d\n", err, *buffer_num);
+ return err;
+ }
+
+ ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER, *buffer_num);
+
+ *buffer_num = (*buffer_num == 0) ? 1 : 0;
+
+ return 0;
+}
+
+/*!
+ * Enable encoder task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int csi_enc_enabling_tasks(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+ CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n");
+
+ err = ipu_request_irq(IPU_IRQ_CSI0_OUT_EOF,
+ csi_enc_callback, 0, "Mxc Camera", cam);
+ if (err != 0) {
+ printk(KERN_ERR "Error registering rot irq\n");
+ return err;
+ }
+
+ err = csi_enc_setup(cam);
+ if (err != 0) {
+ printk(KERN_ERR "csi_enc_setup %d\n", err);
+ return err;
+ }
+
+ return err;
+}
+
+/*!
+ * Disable encoder task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+static int csi_enc_disabling_tasks(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ ipu_free_irq(IPU_IRQ_CSI0_OUT_EOF, cam);
+
+ err = ipu_disable_channel(CSI_MEM, true);
+
+ ipu_uninit_channel(CSI_MEM);
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, false, false);
+
+ return err;
+}
+
+/*!
+ * function to select CSI ENC as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+int csi_enc_select(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ if (cam) {
+ cam->enc_update_eba = csi_enc_eba_update;
+ cam->enc_enable = csi_enc_enabling_tasks;
+ cam->enc_disable = csi_enc_disabling_tasks;
+ } else {
+ err = -EIO;
+ }
+
+ return err;
+}
+
+/*!
+ * function to de-select CSI ENC as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+int csi_enc_deselect(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ if (cam) {
+ cam->enc_update_eba = NULL;
+ cam->enc_enable = NULL;
+ cam->enc_disable = NULL;
+ }
+
+ return err;
+}
+
+/*!
+ * Init the Encorder channels
+ *
+ * @return Error code indicating success or failure
+ */
+__init int csi_enc_init(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit the Encorder channels
+ *
+ */
+void __exit csi_enc_exit(void)
+{
+}
+
+module_init(csi_enc_init);
+module_exit(csi_enc_exit);
+
+EXPORT_SYMBOL(csi_enc_select);
+EXPORT_SYMBOL(csi_enc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("CSI ENC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_enc.c b/drivers/media/video/mxc/capture/ipu_prp_enc.c
new file mode 100644
index 000000000000..79eb8a615ee8
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_enc.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_prp_enc.c
+ *
+ * @brief IPU Use case for PRP-ENC
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+#ifdef CAMERA_DBG
+ #define CAMERA_TRACE(x) (printk)x
+#else
+ #define CAMERA_TRACE(x)
+#endif
+
+static ipu_rotate_mode_t grotation = IPU_ROTATE_NONE;
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * IPU ENC callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t prp_enc_callback(int irq, void *dev_id)
+{
+ cam_data *cam = (cam_data *) dev_id;
+
+ if (cam->enc_callback == NULL)
+ return IRQ_HANDLED;
+
+ cam->enc_callback(irq, dev_id);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * PrpENC enable channel setup function
+ *
+ * @param cam struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_enc_setup(cam_data * cam)
+{
+ ipu_channel_params_t enc;
+ int err = 0;
+ dma_addr_t dummy = 0xdeadbeaf;
+
+ CAMERA_TRACE("In prp_enc_setup\n");
+ if (!cam) {
+ printk(KERN_ERR "cam private is NULL\n");
+ return -ENXIO;
+ }
+ memset(&enc, 0, sizeof(ipu_channel_params_t));
+
+ ipu_csi_get_window_size(&enc.csi_prp_enc_mem.in_width,
+ &enc.csi_prp_enc_mem.in_height, cam->csi);
+
+ enc.csi_prp_enc_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+ enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.width;
+ enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.height;
+ enc.csi_prp_enc_mem.csi = cam->csi;
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.height;
+ enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.width;
+ }
+
+ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV420P;
+ pr_info("YUV420\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV422P;
+ pr_info("YUV422P\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_NV12;
+ pr_info("NV12\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR24;
+ pr_info("BGR24\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB24;
+ pr_info("RGB24\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB565;
+ pr_info("RGB565\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR32;
+ pr_info("BGR32\n");
+ } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) {
+ enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB32;
+ pr_info("RGB32\n");
+ } else {
+ printk(KERN_ERR "format not supported\n");
+ return -EINVAL;
+ }
+
+ err = ipu_init_channel(CSI_PRP_ENC_MEM, &enc);
+ if (err != 0) {
+ printk(KERN_ERR "ipu_init_channel %d\n", err);
+ return err;
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, true, true);
+
+ grotation = cam->rotation;
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ if (cam->rot_enc_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_enc_buf_size[0],
+ cam->rot_enc_bufs_vaddr[0],
+ cam->rot_enc_bufs[0]);
+ }
+ if (cam->rot_enc_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_enc_buf_size[1],
+ cam->rot_enc_bufs_vaddr[1],
+ cam->rot_enc_bufs[1]);
+ }
+ cam->rot_enc_buf_size[0] =
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+ cam->rot_enc_bufs_vaddr[0] =
+ (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[0],
+ &cam->rot_enc_bufs[0],
+ GFP_DMA | GFP_KERNEL);
+ if (!cam->rot_enc_bufs_vaddr[0]) {
+ printk(KERN_ERR "alloc enc_bufs0\n");
+ return -ENOMEM;
+ }
+ cam->rot_enc_buf_size[1] =
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+ cam->rot_enc_bufs_vaddr[1] =
+ (void *)dma_alloc_coherent(0, cam->rot_enc_buf_size[1],
+ &cam->rot_enc_bufs[1],
+ GFP_DMA | GFP_KERNEL);
+ if (!cam->rot_enc_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_enc_buf_size[0],
+ cam->rot_enc_bufs_vaddr[0],
+ cam->rot_enc_bufs[0]);
+ cam->rot_enc_bufs_vaddr[0] = NULL;
+ cam->rot_enc_bufs[0] = 0;
+ printk(KERN_ERR "alloc enc_bufs1\n");
+ return -ENOMEM;
+ }
+
+ err = ipu_init_channel_buffer(CSI_PRP_ENC_MEM,
+ IPU_OUTPUT_BUFFER,
+ enc.csi_prp_enc_mem.out_pixel_fmt,
+ enc.csi_prp_enc_mem.out_width,
+ enc.csi_prp_enc_mem.out_height,
+ enc.csi_prp_enc_mem.out_width,
+ IPU_ROTATE_NONE,
+ cam->rot_enc_bufs[0],
+ cam->rot_enc_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "CSI_PRP_ENC_MEM err\n");
+ return err;
+ }
+
+ err = ipu_init_channel(MEM_ROT_ENC_MEM, NULL);
+ if (err != 0) {
+ printk(KERN_ERR "MEM_ROT_ENC_MEM channel err\n");
+ return err;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_ENC_MEM, IPU_INPUT_BUFFER,
+ enc.csi_prp_enc_mem.out_pixel_fmt,
+ enc.csi_prp_enc_mem.out_width,
+ enc.csi_prp_enc_mem.out_height,
+ enc.csi_prp_enc_mem.out_width,
+ cam->rotation,
+ cam->rot_enc_bufs[0],
+ cam->rot_enc_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "MEM_ROT_ENC_MEM input buffer\n");
+ return err;
+ }
+
+ err =
+ ipu_init_channel_buffer(MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER,
+ enc.csi_prp_enc_mem.out_pixel_fmt,
+ enc.csi_prp_enc_mem.out_height,
+ enc.csi_prp_enc_mem.out_width,
+ cam->v2f.fmt.pix.bytesperline /
+ bytes_per_pixel(enc.csi_prp_enc_mem.
+ out_pixel_fmt),
+ IPU_ROTATE_NONE, dummy, dummy,
+ cam->offset.u_offset,
+ cam->offset.v_offset);
+ if (err != 0) {
+ printk(KERN_ERR "MEM_ROT_ENC_MEM output buffer\n");
+ return err;
+ }
+
+ err = ipu_link_channels(CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM);
+ if (err < 0) {
+ printk(KERN_ERR
+ "link CSI_PRP_ENC_MEM-MEM_ROT_ENC_MEM\n");
+ return err;
+ }
+
+ err = ipu_enable_channel(CSI_PRP_ENC_MEM);
+ if (err < 0) {
+ printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n");
+ return err;
+ }
+ err = ipu_enable_channel(MEM_ROT_ENC_MEM);
+ if (err < 0) {
+ printk(KERN_ERR "ipu_enable_channel MEM_ROT_ENC_MEM\n");
+ return err;
+ }
+
+ ipu_select_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER, 1);
+ } else {
+ err =
+ ipu_init_channel_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER,
+ enc.csi_prp_enc_mem.out_pixel_fmt,
+ enc.csi_prp_enc_mem.out_width,
+ enc.csi_prp_enc_mem.out_height,
+ cam->v2f.fmt.pix.bytesperline /
+ bytes_per_pixel(enc.csi_prp_enc_mem.
+ out_pixel_fmt),
+ cam->rotation, dummy, dummy,
+ cam->offset.u_offset,
+ cam->offset.v_offset);
+ if (err != 0) {
+ printk(KERN_ERR "CSI_PRP_ENC_MEM output buffer\n");
+ return err;
+ }
+ err = ipu_enable_channel(CSI_PRP_ENC_MEM);
+ if (err < 0) {
+ printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n");
+ return err;
+ }
+ }
+
+ return err;
+}
+
+/*!
+ * function to update physical buffer address for encorder IDMA channel
+ *
+ * @param eba physical buffer address for encorder IDMA channel
+ * @param buffer_num int buffer 0 or buffer 1
+ *
+ * @return status
+ */
+static int prp_enc_eba_update(dma_addr_t eba, int *buffer_num)
+{
+ int err = 0;
+
+ pr_debug("eba %x\n", eba);
+ if (grotation >= IPU_ROTATE_90_RIGHT) {
+ err = ipu_update_channel_buffer(MEM_ROT_ENC_MEM,
+ IPU_OUTPUT_BUFFER, *buffer_num,
+ eba);
+ } else {
+ err = ipu_update_channel_buffer(CSI_PRP_ENC_MEM,
+ IPU_OUTPUT_BUFFER, *buffer_num,
+ eba);
+ }
+ if (err != 0) {
+ printk(KERN_ERR "err %d buffer_num %d\n", err, *buffer_num);
+ return err;
+ }
+
+ if (grotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_select_buffer(MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER,
+ *buffer_num);
+ } else {
+ ipu_select_buffer(CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER,
+ *buffer_num);
+ }
+
+ *buffer_num = (*buffer_num == 0) ? 1 : 0;
+ return 0;
+}
+
+/*!
+ * Enable encoder task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_enc_enabling_tasks(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+ CAMERA_TRACE("IPU:In prp_enc_enabling_tasks\n");
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ err = ipu_request_irq(IPU_IRQ_PRP_ENC_ROT_OUT_EOF,
+ prp_enc_callback, 0, "Mxc Camera", cam);
+ } else {
+ err = ipu_request_irq(IPU_IRQ_PRP_ENC_OUT_EOF,
+ prp_enc_callback, 0, "Mxc Camera", cam);
+ }
+ if (err != 0) {
+ printk(KERN_ERR "Error registering rot irq\n");
+ return err;
+ }
+
+ err = prp_enc_setup(cam);
+ if (err != 0) {
+ printk(KERN_ERR "prp_enc_setup %d\n", err);
+ return err;
+ }
+
+ return err;
+}
+
+/*!
+ * Disable encoder task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+static int prp_enc_disabling_tasks(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_free_irq(IPU_IRQ_PRP_ENC_ROT_OUT_EOF, cam);
+ } else {
+ ipu_free_irq(IPU_IRQ_PRP_ENC_OUT_EOF, cam);
+ }
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_unlink_channels(CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM);
+ }
+
+ err = ipu_disable_channel(CSI_PRP_ENC_MEM, true);
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ err |= ipu_disable_channel(MEM_ROT_ENC_MEM, true);
+ }
+
+ ipu_uninit_channel(CSI_PRP_ENC_MEM);
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_uninit_channel(MEM_ROT_ENC_MEM);
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_ENC, cam->csi, false, false);
+
+ return err;
+}
+
+/*!
+ * function to select PRP-ENC as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+int prp_enc_select(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ if (cam) {
+ cam->enc_update_eba = prp_enc_eba_update;
+ cam->enc_enable = prp_enc_enabling_tasks;
+ cam->enc_disable = prp_enc_disabling_tasks;
+ } else {
+ err = -EIO;
+ }
+
+ return err;
+}
+
+/*!
+ * function to de-select PRP-ENC as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return int
+ */
+int prp_enc_deselect(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ //err = prp_enc_disabling_tasks(cam);
+
+ if (cam) {
+ cam->enc_update_eba = NULL;
+ cam->enc_enable = NULL;
+ cam->enc_disable = NULL;
+ if (cam->rot_enc_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_enc_buf_size[0],
+ cam->rot_enc_bufs_vaddr[0],
+ cam->rot_enc_bufs[0]);
+ cam->rot_enc_bufs_vaddr[0] = NULL;
+ cam->rot_enc_bufs[0] = 0;
+ }
+ if (cam->rot_enc_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_enc_buf_size[1],
+ cam->rot_enc_bufs_vaddr[1],
+ cam->rot_enc_bufs[1]);
+ cam->rot_enc_bufs_vaddr[1] = NULL;
+ cam->rot_enc_bufs[1] = 0;
+ }
+ }
+
+ return err;
+}
+
+/*!
+ * Init the Encorder channels
+ *
+ * @return Error code indicating success or failure
+ */
+__init int prp_enc_init(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit the Encorder channels
+ *
+ */
+void __exit prp_enc_exit(void)
+{
+}
+
+module_init(prp_enc_init);
+module_exit(prp_enc_exit);
+
+EXPORT_SYMBOL(prp_enc_select);
+EXPORT_SYMBOL(prp_enc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP ENC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_sw.h b/drivers/media/video/mxc/capture/ipu_prp_sw.h
new file mode 100644
index 000000000000..0d51e2ae8670
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_sw.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_prp_sw.h
+ *
+ * @brief This file contains the IPU PRP use case driver header.
+ *
+ * @ingroup IPU
+ */
+
+#ifndef _INCLUDE_IPU__PRP_SW_H_
+#define _INCLUDE_IPU__PRP_SW_H_
+
+int csi_enc_select(void *private);
+int csi_enc_deselect(void *private);
+int prp_enc_select(void *private);
+int prp_enc_deselect(void *private);
+int prp_vf_adc_select(void *private);
+int prp_vf_sdc_select(void *private);
+int prp_vf_sdc_select_bg(void *private);
+int prp_vf_adc_deselect(void *private);
+int prp_vf_sdc_deselect(void *private);
+int prp_vf_sdc_deselect_bg(void *private);
+int prp_still_select(void *private);
+int prp_still_deselect(void *private);
+
+#endif
diff --git a/drivers/media/video/mxc/capture/ipu_prp_vf_adc.c b/drivers/media/video/mxc/capture/ipu_prp_vf_adc.c
new file mode 100644
index 000000000000..a7ac09ae87d4
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_vf_adc.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_prp_vf_adc.c
+ *
+ * @brief IPU Use case for PRP-VF
+ *
+ * @ingroup IPU
+ */
+
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+#include <mach/mxcfb.h>
+#include <mach/ipu.h>
+#include <linux/dma-mapping.h>
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * prpvf_start - start the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_start(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ ipu_channel_params_t vf;
+ ipu_channel_params_t params;
+ u32 format = IPU_PIX_FMT_RGB565;
+ u32 size = 2;
+ int err = 0;
+
+ if (!cam) {
+ printk(KERN_ERR "prpvf_start private is NULL\n");
+ return -ENXIO;
+ }
+
+ if (cam->overlay_active == true) {
+ printk(KERN_ERR "prpvf_start already start.\n");
+ return 0;
+ }
+
+ mxcfb_set_refresh_mode(cam->overlay_fb, MXCFB_REFRESH_OFF, 0);
+
+ memset(&vf, 0, sizeof(ipu_channel_params_t));
+ ipu_csi_get_window_size(&vf.csi_prp_vf_adc.in_width,
+ &vf.csi_prp_vf_adc.in_height);
+ vf.csi_prp_vf_adc.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+ vf.csi_prp_vf_adc.out_width = cam->win.w.width;
+ vf.csi_prp_vf_adc.out_height = cam->win.w.height;
+ vf.csi_prp_vf_adc.graphics_combine_en = 0;
+ vf.csi_prp_vf_adc.out_left = cam->win.w.left;
+
+ /* hope to be removed when those offset taken cared by adc driver. */
+#ifdef CONFIG_FB_MXC_EPSON_QVGA_PANEL
+ vf.csi_prp_vf_adc.out_left += 12;
+#endif
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ vf.csi_prp_vf_adc.out_left += 2;
+#endif
+
+ vf.csi_prp_vf_adc.out_top = cam->win.w.top;
+
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ vf.csi_prp_vf_adc.out_width = cam->win.w.height;
+ vf.csi_prp_vf_adc.out_height = cam->win.w.width;
+
+ size = cam->win.w.width * cam->win.w.height * size;
+ vf.csi_prp_vf_adc.out_pixel_fmt = format;
+ err = ipu_init_channel(CSI_PRP_VF_MEM, &vf);
+ if (err != 0)
+ return err;
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0],
+ cam->vf_bufs[0]);
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1],
+ cam->vf_bufs[1]);
+ }
+ cam->vf_bufs_size[0] = size;
+ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+ cam->
+ vf_bufs_size
+ [0],
+ &cam->
+ vf_bufs[0],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[0] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ cam->vf_bufs_size[1] = size;
+ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+ cam->
+ vf_bufs_size
+ [1],
+ &cam->
+ vf_bufs[1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ IPU_ROTATE_NONE,
+ cam->vf_bufs[0], cam->vf_bufs[1],
+ 0, 0);
+ if (err != 0)
+ goto out_3;
+
+ if (cam->rot_vf_bufs[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ cam->rot_vf_bufs[0]);
+ }
+ if (cam->rot_vf_bufs[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ cam->rot_vf_bufs[1]);
+ }
+ cam->rot_vf_buf_size[0] = PAGE_ALIGN(size);
+ cam->rot_vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+ cam->
+ rot_vf_buf_size
+ [0],
+ &cam->
+ rot_vf_bufs
+ [0],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->rot_vf_bufs_vaddr[0] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate rot_vf_bufs\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ cam->rot_vf_buf_size[1] = PAGE_ALIGN(size);
+ cam->rot_vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+ cam->
+ rot_vf_buf_size
+ [1],
+ &cam->
+ rot_vf_bufs
+ [1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->rot_vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate rot_vf_bufs\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ err = ipu_init_channel(MEM_ROT_VF_MEM, NULL);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start :Error "
+ "MEM_ROT_VF_MEM channel\n");
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ cam->vf_rotation, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "MEM_ROT_VF_MEM input buffer\n");
+ goto out_2;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ IPU_ROTATE_NONE,
+ cam->rot_vf_bufs[0],
+ cam->rot_vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "MEM_ROT_VF_MEM output buffer\n");
+ goto out_2;
+ }
+
+ err = ipu_link_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+ if (err < 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "linking CSI_PRP_VF_MEM-MEM_ROT_VF_MEM\n");
+ goto out_2;
+ }
+
+ ipu_disable_channel(ADC_SYS2, false);
+ ipu_uninit_channel(ADC_SYS2);
+
+ params.adc_sys2.disp = DISP0;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys2.out_left = cam->win.w.left;
+ /* going to be removed when those offset taken cared by adc driver. */
+#ifdef CONFIG_FB_MXC_EPSON_QVGA_PANEL
+ params.adc_sys2.out_left += 12;
+#endif
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ params.adc_sys2.out_left += 2;
+#endif
+ params.adc_sys2.out_top = cam->win.w.top;
+ err = ipu_init_channel(ADC_SYS2, &params);
+ if (err != 0) {
+ printk(KERN_ERR
+ "prpvf_start: Error initializing ADC SYS1\n");
+ goto out_2;
+ }
+
+ err = ipu_init_channel_buffer(ADC_SYS2, IPU_INPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ IPU_ROTATE_NONE,
+ cam->rot_vf_bufs[0],
+ cam->rot_vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error initializing ADC SYS1 buffer\n");
+ goto out_1;
+ }
+
+ err = ipu_link_channels(MEM_ROT_VF_MEM, ADC_SYS2);
+ if (err < 0) {
+ printk(KERN_ERR
+ "Error linking MEM_ROT_VF_MEM-ADC_SYS2\n");
+ goto out_1;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+ ipu_enable_channel(MEM_ROT_VF_MEM);
+ ipu_enable_channel(ADC_SYS2);
+
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ }
+#ifndef CONFIG_MXC_IPU_PRP_VF_SDC
+ else if (cam->vf_rotation == IPU_ROTATE_NONE) {
+ vf.csi_prp_vf_adc.out_pixel_fmt = IPU_PIX_FMT_BGR32;
+ err = ipu_init_channel(CSI_PRP_VF_ADC, &vf);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing CSI_PRP_VF_ADC\n");
+ return err;
+ }
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+ err = ipu_init_channel_buffer(CSI_PRP_VF_ADC, IPU_OUTPUT_BUFFER,
+ format, cam->win.w.width,
+ cam->win.w.height,
+ cam->win.w.width, IPU_ROTATE_NONE,
+ 0, 0, 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing CSI_PRP_VF_MEM\n");
+ return err;
+ }
+ ipu_enable_channel(CSI_PRP_VF_ADC);
+ }
+#endif
+ else {
+ size = cam->win.w.width * cam->win.w.height * size;
+ vf.csi_prp_vf_adc.out_pixel_fmt = format;
+ err = ipu_init_channel(CSI_PRP_VF_MEM, &vf);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing CSI_PRP_VF_MEM\n");
+ return err;
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+
+ if (cam->vf_bufs[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0],
+ cam->vf_bufs[0]);
+ }
+ if (cam->vf_bufs[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1],
+ cam->vf_bufs[1]);
+ }
+ cam->vf_bufs_size[0] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+ cam->
+ vf_bufs_size
+ [0],
+ &cam->
+ vf_bufs[0],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[0] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate vf_bufs\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ cam->vf_bufs_size[1] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+ cam->
+ vf_bufs_size
+ [1],
+ &cam->
+ vf_bufs[1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR
+ "prpvf_start: Error to allocate vf_bufs\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ cam->vf_rotation,
+ cam->vf_bufs[0], cam->vf_bufs[1],
+ 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing CSI_PRP_VF_MEM\n");
+ goto out_3;
+ }
+
+ ipu_disable_channel(ADC_SYS2, false);
+ ipu_uninit_channel(ADC_SYS2);
+
+ params.adc_sys2.disp = DISP0;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys2.out_left = cam->win.w.left;
+ // going to be removed when those offset taken cared by adc driver.
+#ifdef CONFIG_FB_MXC_EPSON_QVGA_PANEL
+ params.adc_sys2.out_left += 12;
+#endif
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ params.adc_sys2.out_left += 2;
+#endif
+ params.adc_sys2.out_top = cam->win.w.top;
+ err = ipu_init_channel(ADC_SYS2, &params);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing ADC_SYS2\n");
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(ADC_SYS2, IPU_INPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ IPU_ROTATE_NONE, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "initializing ADC SYS1 buffer\n");
+ goto out_1;
+ }
+
+ err = ipu_link_channels(CSI_PRP_VF_MEM, ADC_SYS2);
+ if (err < 0) {
+ printk(KERN_ERR "prpvf_start: Error "
+ "linking MEM_ROT_VF_MEM-ADC_SYS2\n");
+ goto out_1;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+ ipu_enable_channel(ADC_SYS2);
+
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ }
+
+ cam->overlay_active = true;
+ return err;
+
+ out_1:
+ ipu_uninit_channel(ADC_SYS2);
+ out_2:
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ }
+ out_3:
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+ if (cam->rot_vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ cam->rot_vf_bufs[0]);
+ cam->rot_vf_bufs_vaddr[0] = NULL;
+ cam->rot_vf_bufs[0] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ cam->rot_vf_bufs[1]);
+ cam->rot_vf_bufs_vaddr[1] = NULL;
+ cam->rot_vf_bufs[1] = 0;
+ }
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+ return err;
+}
+
+/*!
+ * prpvf_stop - stop the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_stop(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ if (cam->overlay_active == false)
+ return 0;
+
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+ ipu_unlink_channels(MEM_ROT_VF_MEM, ADC_SYS2);
+
+ ipu_disable_channel(CSI_PRP_VF_MEM, true);
+ ipu_disable_channel(MEM_ROT_VF_MEM, true);
+ ipu_disable_channel(ADC_SYS2, true);
+
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ ipu_uninit_channel(ADC_SYS2);
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+ }
+#ifndef CONFIG_MXC_IPU_PRP_VF_SDC
+ else if (cam->vf_rotation == IPU_ROTATE_NONE) {
+ ipu_disable_channel(CSI_PRP_VF_ADC, false);
+ ipu_uninit_channel(CSI_PRP_VF_ADC);
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+ }
+#endif
+ else {
+ ipu_unlink_channels(CSI_PRP_VF_MEM, ADC_SYS2);
+
+ ipu_disable_channel(CSI_PRP_VF_MEM, true);
+ ipu_disable_channel(ADC_SYS2, true);
+
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+ ipu_uninit_channel(ADC_SYS2);
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+ }
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ cam->rot_vf_bufs[0]);
+ cam->rot_vf_bufs_vaddr[0] = NULL;
+ cam->rot_vf_bufs[0] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ cam->rot_vf_bufs[1]);
+ cam->rot_vf_bufs_vaddr[1] = NULL;
+ cam->rot_vf_bufs[1] = 0;
+ }
+
+ cam->overlay_active = false;
+
+ mxcfb_set_refresh_mode(cam->overlay_fb, MXCFB_REFRESH_PARTIAL, 0);
+ return err;
+}
+
+/*!
+ * function to select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return status
+ */
+int prp_vf_adc_select(void *private)
+{
+ cam_data *cam;
+ if (private) {
+ cam = (cam_data *) private;
+ cam->vf_start_adc = prpvf_start;
+ cam->vf_stop_adc = prpvf_stop;
+ cam->overlay_active = false;
+ } else {
+ return -EIO;
+ }
+ return 0;
+}
+
+/*!
+ * function to de-select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return status
+ */
+int prp_vf_adc_deselect(void *private)
+{
+ cam_data *cam;
+ int err = 0;
+ err = prpvf_stop(private);
+
+ if (private) {
+ cam = (cam_data *) private;
+ cam->vf_start_adc = NULL;
+ cam->vf_stop_adc = NULL;
+ }
+ return err;
+}
+
+/*!
+ * Init viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+__init int prp_vf_adc_init(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+void __exit prp_vf_adc_exit(void)
+{
+}
+
+module_init(prp_vf_adc_init);
+module_exit(prp_vf_adc_exit);
+
+EXPORT_SYMBOL(prp_vf_adc_select);
+EXPORT_SYMBOL(prp_vf_adc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP VF ADC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c
new file mode 100644
index 000000000000..369facdf29c2
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_prp_vf_sdc.c
+ *
+ * @brief IPU Use case for PRP-VF
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/console.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * prpvf_start - start the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_start(void *private)
+{
+ struct fb_var_screeninfo fbvar;
+ struct fb_info *fbi = NULL;
+ cam_data *cam = (cam_data *) private;
+ ipu_channel_params_t vf;
+ u32 format = IPU_PIX_FMT_RGB565;
+ u32 size = 2, temp = 0;
+ int err = 0, i = 0;
+
+ if (!cam) {
+ printk(KERN_ERR "private is NULL\n");
+ return -EIO;
+ }
+
+ if (cam->overlay_active == true) {
+ pr_debug("already started.\n");
+ return 0;
+ }
+
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strcmp(idstr, "DISP3 FG") == 0)
+ fbi = registered_fb[i];
+ }
+
+ if (fbi == NULL) {
+ printk(KERN_ERR "DISP3 FG fb not found\n");
+ return -EPERM;
+ }
+
+ fbvar = fbi->var;
+ fbvar.bits_per_pixel = 16;
+ fbvar.nonstd = 0;
+ fbvar.xres = fbvar.xres_virtual = cam->win.w.width;
+ fbvar.yres = cam->win.w.height;
+ fbvar.yres_virtual = cam->win.w.height * 2;
+ fbvar.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbvar);
+
+ ipu_disp_set_window_pos(MEM_FG_SYNC, cam->win.w.left,
+ cam->win.w.top);
+
+ memset(&vf, 0, sizeof(ipu_channel_params_t));
+ ipu_csi_get_window_size(&vf.csi_prp_vf_mem.in_width,
+ &vf.csi_prp_vf_mem.in_height, cam->csi);
+ vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+ vf.csi_prp_vf_mem.out_width = cam->win.w.width;
+ vf.csi_prp_vf_mem.out_height = cam->win.w.height;
+ vf.csi_prp_vf_mem.csi = cam->csi;
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ vf.csi_prp_vf_mem.out_width = cam->win.w.height;
+ vf.csi_prp_vf_mem.out_height = cam->win.w.width;
+ }
+ vf.csi_prp_vf_mem.out_pixel_fmt = format;
+ size = cam->win.w.width * cam->win.w.height * size;
+
+ err = ipu_init_channel(CSI_PRP_VF_MEM, &vf);
+ if (err != 0)
+ goto out_5;
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0],
+ (dma_addr_t) cam->vf_bufs[0]);
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1],
+ (dma_addr_t) cam->vf_bufs[1]);
+ }
+ cam->vf_bufs_size[0] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+ cam->vf_bufs_size[0],
+ (dma_addr_t *) &
+ cam->vf_bufs[0],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[0] == NULL) {
+ printk(KERN_ERR "Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_4;
+ }
+ cam->vf_bufs_size[1] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+ cam->vf_bufs_size[1],
+ (dma_addr_t *) &
+ cam->vf_bufs[1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR "Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]);
+
+ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+ err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ IPU_ROTATE_NONE, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ goto out_3;
+ }
+
+ err = ipu_init_channel(MEM_ROT_VF_MEM, NULL);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n");
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ cam->vf_rotation, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n");
+ goto out_2;
+ }
+
+ if (cam->vf_rotation < IPU_ROTATE_90_RIGHT) {
+ temp = vf.csi_prp_vf_mem.out_width;
+ vf.csi_prp_vf_mem.out_width =
+ vf.csi_prp_vf_mem.out_height;
+ vf.csi_prp_vf_mem.out_height = temp;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start +
+ (fbi->fix.line_length *
+ fbi->var.yres),
+ fbi->fix.smem_start, 0, 0);
+
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
+ goto out_2;
+ }
+
+ err = ipu_link_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+ if (err < 0) {
+ printk(KERN_ERR
+ "Error link CSI_PRP_VF_MEM-MEM_ROT_VF_MEM\n");
+ goto out_2;
+ }
+
+ err = ipu_link_channels(MEM_ROT_VF_MEM, MEM_FG_SYNC);
+ if (err < 0) {
+ printk(KERN_ERR
+ "Error link MEM_ROT_VF_MEM-MEM_FG_SYNC\n");
+ goto out_1;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+ ipu_enable_channel(MEM_ROT_VF_MEM);
+
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ } else {
+ err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+ format, cam->win.w.width,
+ cam->win.w.height,
+ cam->win.w.width,
+ cam->vf_rotation,
+ fbi->fix.smem_start +
+ (fbi->fix.line_length *
+ fbi->var.yres),
+ fbi->fix.smem_start, 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n");
+ goto out_4;
+ }
+
+ err = ipu_link_channels(CSI_PRP_VF_MEM, MEM_FG_SYNC);
+ if (err < 0) {
+ printk(KERN_ERR "Error linking ipu channels\n");
+ goto out_4;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+ }
+
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_UNBLANK);
+ release_console_sem();
+
+ cam->overlay_active = true;
+ return err;
+
+out_1:
+ ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+out_2:
+ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ }
+out_3:
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0],
+ (dma_addr_t) cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1],
+ (dma_addr_t) cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+out_4:
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+out_5:
+ return err;
+}
+
+/*!
+ * prpvf_stop - stop the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_stop(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0, i = 0;
+ struct fb_info *fbi = NULL;
+
+ if (cam->overlay_active == false)
+ return 0;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strcmp(idstr, "DISP3 FG") == 0)
+ fbi = registered_fb[i];
+ }
+
+ if (fbi == NULL) {
+ printk(KERN_ERR "DISP3 FG fb not found\n");
+ return -EPERM;
+ }
+
+ ipu_disp_set_window_pos(MEM_FG_SYNC, 0, 0);
+
+ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+ ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+ ipu_unlink_channels(MEM_ROT_VF_MEM, MEM_FG_SYNC);
+ } else {
+ ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_FG_SYNC);
+ }
+
+ ipu_disable_channel(CSI_PRP_VF_MEM, true);
+
+ if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
+ ipu_disable_channel(MEM_ROT_VF_MEM, true);
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ }
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0],
+ (dma_addr_t) cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1],
+ (dma_addr_t) cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+
+ cam->overlay_active = false;
+ return err;
+}
+
+/*!
+ * function to select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return status
+ */
+int prp_vf_sdc_select(void *private)
+{
+ cam_data *cam;
+ int err = 0;
+ if (private) {
+ cam = (cam_data *) private;
+ cam->vf_start_sdc = prpvf_start;
+ cam->vf_stop_sdc = prpvf_stop;
+ cam->overlay_active = false;
+ } else
+ err = -EIO;
+
+ return err;
+}
+
+/*!
+ * function to de-select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return int
+ */
+int prp_vf_sdc_deselect(void *private)
+{
+ cam_data *cam;
+ int err = 0;
+ err = prpvf_stop(private);
+
+ if (private) {
+ cam = (cam_data *) private;
+ cam->vf_start_sdc = NULL;
+ cam->vf_stop_sdc = NULL;
+ }
+ return err;
+}
+
+/*!
+ * Init viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+__init int prp_vf_sdc_init(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+void __exit prp_vf_sdc_exit(void)
+{
+}
+
+module_init(prp_vf_sdc_init);
+module_exit(prp_vf_sdc_exit);
+
+EXPORT_SYMBOL(prp_vf_sdc_select);
+EXPORT_SYMBOL(prp_vf_sdc_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP VF SDC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c
new file mode 100644
index 000000000000..4035c7664022
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_prp_vf_sdc_bg.c
+ *
+ * @brief IPU Use case for PRP-VF back-ground
+ *
+ * @ingroup IPU
+ */
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+static int buffer_num = 0;
+static int buffer_ready = 0;
+
+/*
+ * Function definitions
+ */
+
+/*!
+ * SDC V-Sync callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t prpvf_sdc_vsync_callback(int irq, void *dev_id)
+{
+ pr_debug("buffer_ready %d buffer_num %d\n", buffer_ready, buffer_num);
+ if (buffer_ready > 0) {
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ buffer_ready--;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * VF EOF callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t prpvf_vf_eof_callback(int irq, void *dev_id)
+{
+ pr_debug("buffer_ready %d buffer_num %d\n", buffer_ready, buffer_num);
+
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER, buffer_num);
+
+ buffer_num = (buffer_num == 0) ? 1 : 0;
+
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, buffer_num);
+ buffer_ready++;
+ return IRQ_HANDLED;
+}
+
+/*!
+ * prpvf_start - start the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_start(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ ipu_channel_params_t vf;
+ u32 format;
+ u32 offset;
+ u32 bpp, size = 3;
+ int err = 0;
+
+ if (!cam) {
+ printk(KERN_ERR "private is NULL\n");
+ return -EIO;
+ }
+
+ if (cam->overlay_active == true) {
+ pr_debug("already start.\n");
+ return 0;
+ }
+
+ format = cam->v4l2_fb.fmt.pixelformat;
+ if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) {
+ bpp = 3, size = 3;
+ pr_info("BGR24\n");
+ } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) {
+ bpp = 2, size = 2;
+ pr_info("RGB565\n");
+ } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) {
+ bpp = 4, size = 4;
+ pr_info("BGR32\n");
+ } else {
+ printk(KERN_ERR
+ "unsupported fix format from the framebuffer.\n");
+ return -EINVAL;
+ }
+
+ offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top +
+ size * cam->win.w.left;
+
+ if (cam->v4l2_fb.base == 0) {
+ printk(KERN_ERR "invalid frame buffer address.\n");
+ } else {
+ offset += (u32) cam->v4l2_fb.base;
+ }
+
+ memset(&vf, 0, sizeof(ipu_channel_params_t));
+ ipu_csi_get_window_size(&vf.csi_prp_vf_mem.in_width,
+ &vf.csi_prp_vf_mem.in_height, cam->csi);
+ vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
+ vf.csi_prp_vf_mem.out_width = cam->win.w.width;
+ vf.csi_prp_vf_mem.out_height = cam->win.w.height;
+ vf.csi_prp_vf_mem.csi = cam->csi;
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ vf.csi_prp_vf_mem.out_width = cam->win.w.height;
+ vf.csi_prp_vf_mem.out_height = cam->win.w.width;
+ }
+ vf.csi_prp_vf_mem.out_pixel_fmt = format;
+ size = cam->win.w.width * cam->win.w.height * size;
+
+ err = ipu_init_channel(CSI_PRP_VF_MEM, &vf);
+ if (err != 0)
+ goto out_4;
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, true, true);
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+ }
+ cam->vf_bufs_size[0] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(0,
+ cam->vf_bufs_size[0],
+ &cam->vf_bufs[0],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[0] == NULL) {
+ printk(KERN_ERR "Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ cam->vf_bufs_size[1] = PAGE_ALIGN(size);
+ cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(0,
+ cam->vf_bufs_size[1],
+ &cam->vf_bufs[1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR "Error to allocate vf buffer\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER,
+ format, vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ IPU_ROTATE_NONE, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n");
+ goto out_3;
+ }
+ err = ipu_init_channel(MEM_ROT_VF_MEM, NULL);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n");
+ goto out_3;
+ }
+
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_INPUT_BUFFER,
+ format, vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ cam->vf_rotation, cam->vf_bufs[0],
+ cam->vf_bufs[1], 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n");
+ goto out_2;
+ }
+
+ if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_height,
+ vf.csi_prp_vf_mem.out_width,
+ cam->overlay_fb->var.xres * bpp,
+ IPU_ROTATE_NONE, offset, 0, 0, 0);
+
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
+ goto out_2;
+ }
+ } else {
+ err = ipu_init_channel_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER,
+ format,
+ vf.csi_prp_vf_mem.out_width,
+ vf.csi_prp_vf_mem.out_height,
+ cam->overlay_fb->var.xres * bpp,
+ IPU_ROTATE_NONE, offset, 0, 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
+ goto out_2;
+ }
+ }
+
+ ipu_clear_irq(IPU_IRQ_PRP_VF_OUT_EOF);
+ err = ipu_request_irq(IPU_IRQ_PRP_VF_OUT_EOF, prpvf_vf_eof_callback,
+ 0, "Mxc Camera", cam);
+ if (err != 0) {
+ printk(KERN_ERR
+ "Error registering IPU_IRQ_PRP_VF_OUT_EOF irq.\n");
+ goto out_2;
+ }
+
+ ipu_clear_irq(IPU_IRQ_BG_SF_END);
+ err = ipu_request_irq(IPU_IRQ_BG_SF_END, prpvf_sdc_vsync_callback,
+ 0, "Mxc Camera", NULL);
+ if (err != 0) {
+ printk(KERN_ERR "Error registering IPU_IRQ_BG_SF_END irq.\n");
+ goto out_1;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+ ipu_enable_channel(MEM_ROT_VF_MEM);
+
+ buffer_num = 0;
+ buffer_ready = 0;
+ ipu_select_buffer(CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+
+ cam->overlay_active = true;
+ return err;
+
+ out_1:
+ ipu_free_irq(IPU_IRQ_PRP_VF_OUT_EOF, NULL);
+ out_2:
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ out_3:
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+ out_4:
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ cam->rot_vf_bufs[0]);
+ cam->rot_vf_bufs_vaddr[0] = NULL;
+ cam->rot_vf_bufs[0] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ cam->rot_vf_bufs[1]);
+ cam->rot_vf_bufs_vaddr[1] = NULL;
+ cam->rot_vf_bufs[1] = 0;
+ }
+ return err;
+}
+
+/*!
+ * prpvf_stop - stop the vf task
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ */
+static int prpvf_stop(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (cam->overlay_active == false)
+ return 0;
+
+ ipu_free_irq(IPU_IRQ_BG_SF_END, NULL);
+
+ ipu_free_irq(IPU_IRQ_PRP_VF_OUT_EOF, cam);
+
+ ipu_disable_channel(CSI_PRP_VF_MEM, true);
+ ipu_disable_channel(MEM_ROT_VF_MEM, true);
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ ipu_csi_enable_mclk_if(CSI_MCLK_VF, cam->csi, false, false);
+
+ if (cam->vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->vf_bufs_size[0],
+ cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
+ cam->vf_bufs_vaddr[0] = NULL;
+ cam->vf_bufs[0] = 0;
+ }
+ if (cam->vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->vf_bufs_size[1],
+ cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
+ cam->vf_bufs_vaddr[1] = NULL;
+ cam->vf_bufs[1] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ cam->rot_vf_bufs[0]);
+ cam->rot_vf_bufs_vaddr[0] = NULL;
+ cam->rot_vf_bufs[0] = 0;
+ }
+ if (cam->rot_vf_bufs_vaddr[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ cam->rot_vf_bufs[1]);
+ cam->rot_vf_bufs_vaddr[1] = NULL;
+ cam->rot_vf_bufs[1] = 0;
+ }
+
+ buffer_num = 0;
+ buffer_ready = 0;
+ cam->overlay_active = false;
+ return 0;
+}
+
+/*!
+ * function to select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return status
+ */
+int prp_vf_sdc_select_bg(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (cam) {
+ cam->vf_start_sdc = prpvf_start;
+ cam->vf_stop_sdc = prpvf_stop;
+ cam->overlay_active = false;
+ }
+
+ return 0;
+}
+
+/*!
+ * function to de-select PRP-VF as the working path
+ *
+ * @param private cam_data * mxc v4l2 main structure
+ *
+ * @return status
+ */
+int prp_vf_sdc_deselect_bg(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+ err = prpvf_stop(private);
+
+ if (cam) {
+ cam->vf_start_sdc = NULL;
+ cam->vf_stop_sdc = NULL;
+ }
+ return err;
+}
+
+/*!
+ * Init viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+__init int prp_vf_sdc_init_bg(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit viewfinder task.
+ *
+ * @return Error code indicating success or failure
+ */
+void __exit prp_vf_sdc_exit_bg(void)
+{
+}
+
+module_init(prp_vf_sdc_init_bg);
+module_exit(prp_vf_sdc_exit_bg);
+
+EXPORT_SYMBOL(prp_vf_sdc_select_bg);
+EXPORT_SYMBOL(prp_vf_sdc_deselect_bg);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ipu_still.c b/drivers/media/video/mxc/capture/ipu_still.c
new file mode 100644
index 000000000000..34cea8609c95
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_still.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_still.c
+ *
+ * @brief IPU Use case for still image capture
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/ipu.h>
+#include <linux/semaphore.h>
+#include <linux/ipu.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+static int callback_eof_flag;
+
+#ifdef CONFIG_MXC_IPU_V1
+static int callback_flag;
+/*
+ * Function definitions
+ */
+/*!
+ * CSI EOF callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t prp_csi_eof_callback(int irq, void *dev_id)
+{
+ if (callback_flag == 2) {
+ ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_enable_channel(CSI_MEM);
+ }
+
+ callback_flag++;
+ return IRQ_HANDLED;
+}
+#endif
+
+/*!
+ * CSI callback function.
+ *
+ * @param irq int irq line
+ * @param dev_id void * device id
+ *
+ * @return status IRQ_HANDLED for handled
+ */
+static irqreturn_t prp_still_callback(int irq, void *dev_id)
+{
+ cam_data *cam = (cam_data *) dev_id;
+
+ callback_eof_flag++;
+ if (callback_eof_flag < 5)
+ ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER, 0);
+ else {
+ cam->still_counter++;
+ wake_up_interruptible(&cam->still_queue);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * start csi->mem task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_still_start(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ u32 pixel_fmt;
+ int err;
+ ipu_channel_params_t params;
+
+ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ pixel_fmt = IPU_PIX_FMT_YUV420P;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P)
+ pixel_fmt = IPU_PIX_FMT_YUV422P;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
+ pixel_fmt = IPU_PIX_FMT_UYVY;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
+ pixel_fmt = IPU_PIX_FMT_BGR24;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
+ pixel_fmt = IPU_PIX_FMT_RGB24;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
+ pixel_fmt = IPU_PIX_FMT_RGB565;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32)
+ pixel_fmt = IPU_PIX_FMT_BGR32;
+ else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32)
+ pixel_fmt = IPU_PIX_FMT_RGB32;
+ else {
+ printk(KERN_ERR "format not supported\n");
+ return -EINVAL;
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_RAW, cam->csi, true, true);
+
+ memset(&params, 0, sizeof(params));
+ err = ipu_init_channel(CSI_MEM, &params);
+ if (err != 0)
+ return err;
+
+ err = ipu_init_channel_buffer(CSI_MEM, IPU_OUTPUT_BUFFER,
+ pixel_fmt, cam->v2f.fmt.pix.width,
+ cam->v2f.fmt.pix.height,
+ cam->v2f.fmt.pix.width, IPU_ROTATE_NONE,
+ cam->still_buf, 0, 0, 0);
+ if (err != 0)
+ return err;
+
+#ifdef CONFIG_MXC_IPU_V1
+ err = ipu_request_irq(IPU_IRQ_SENSOR_OUT_EOF, prp_still_callback,
+ 0, "Mxc Camera", cam);
+ if (err != 0) {
+ printk(KERN_ERR "Error registering irq.\n");
+ return err;
+ }
+ callback_flag = 0;
+ callback_eof_flag = 0;
+ err = ipu_request_irq(IPU_IRQ_SENSOR_EOF, prp_csi_eof_callback,
+ 0, "Mxc Camera", NULL);
+ if (err != 0) {
+ printk(KERN_ERR "Error IPU_IRQ_SENSOR_EOF \n");
+ return err;
+ }
+#else
+ ipu_clear_irq(IPU_IRQ_CSI0_OUT_EOF);
+ err = ipu_request_irq(IPU_IRQ_CSI0_OUT_EOF, prp_still_callback,
+ 0, "Mxc Camera", cam);
+ if (err != 0) {
+ printk(KERN_ERR "Error registering irq.\n");
+ return err;
+ }
+
+ callback_eof_flag = 0;
+
+ ipu_select_buffer(CSI_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_enable_channel(CSI_MEM);
+#endif
+
+ return err;
+}
+
+/*!
+ * stop csi->mem encoder task
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+static int prp_still_stop(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+#ifdef CONFIG_MXC_IPU_V1
+ ipu_free_irq(IPU_IRQ_SENSOR_EOF, NULL);
+ ipu_free_irq(IPU_IRQ_SENSOR_OUT_EOF, cam);
+#else
+ ipu_free_irq(IPU_IRQ_CSI0_OUT_EOF, cam);
+#endif
+
+ ipu_disable_channel(CSI_MEM, true);
+ ipu_uninit_channel(CSI_MEM);
+ ipu_csi_enable_mclk_if(CSI_MCLK_RAW, cam->csi, false, false);
+
+ return err;
+}
+
+/*!
+ * function to select CSI_MEM as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+int prp_still_select(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (cam) {
+ cam->csi_start = prp_still_start;
+ cam->csi_stop = prp_still_stop;
+ }
+
+ return 0;
+}
+
+/*!
+ * function to de-select CSI_MEM as the working path
+ *
+ * @param private struct cam_data * mxc capture instance
+ *
+ * @return status
+ */
+int prp_still_deselect(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ err = prp_still_stop(cam);
+
+ if (cam) {
+ cam->csi_start = NULL;
+ cam->csi_stop = NULL;
+ }
+
+ return err;
+}
+
+/*!
+ * Init the Encorder channels
+ *
+ * @return Error code indicating success or failure
+ */
+__init int prp_still_init(void)
+{
+ return 0;
+}
+
+/*!
+ * Deinit the Encorder channels
+ *
+ */
+void __exit prp_still_exit(void)
+{
+}
+
+module_init(prp_still_init);
+module_exit(prp_still_exit);
+
+EXPORT_SYMBOL(prp_still_select);
+EXPORT_SYMBOL(prp_still_deselect);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IPU PRP STILL IMAGE Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/mc521da.c b/drivers/media/video/mxc/capture/mc521da.c
new file mode 100644
index 000000000000..a8ff84fb4c84
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mc521da.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright 2006-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc521da.c
+ *
+ * @brief MC521DA camera driver functions
+ *
+ * @ingroup Camera
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include "mxc_v4l2_capture.h"
+
+#define MC521DA_I2C_ADDRESS 0x22
+#define MC521DA_TERM 0xFF
+
+typedef struct {
+ u16 width;
+ u16 height;
+} mc521da_image_format;
+
+struct mc521da_reg {
+ u8 reg;
+ u8 val;
+};
+
+static sensor_interface *interface_param = NULL;
+
+static mc521da_image_format format[2] = {
+ {
+ .width = 1600,
+ .height = 1200,
+ },
+ {
+ .width = 640,
+ .height = 480,
+ },
+};
+
+const static struct mc521da_reg mc521da_initial[] = {
+ /*----------------------------------------------------------
+ * Sensor Setting Start
+ *----------------------------------------------------------
+ */
+ {0xff, 0x01}, /* Sensor setting start */
+ {0x01, 0x10}, /* Wavetable script, generated by waveman */
+ {0x10, 0x64},
+ {0x03, 0x00}, {0x04, 0x06}, {0x05, 0x30}, {0x06, 0x02}, {0x08, 0x00},
+ {0x03, 0x01}, {0x04, 0x41}, {0x05, 0x70}, {0x06, 0x03}, {0x08, 0x00},
+ {0x03, 0x02}, {0x04, 0x55}, {0x05, 0x30}, {0x06, 0x03}, {0x08, 0x00},
+ {0x03, 0x03}, {0x04, 0x5A}, {0x05, 0x30}, {0x06, 0x02}, {0x08, 0x00},
+ {0x03, 0x04}, {0x04, 0x7A}, {0x05, 0x30}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x05}, {0x04, 0x9C}, {0x05, 0x30}, {0x06, 0x0F}, {0x08, 0x00},
+ {0x03, 0x06}, {0x04, 0x73}, {0x05, 0x31}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x07}, {0x04, 0x2D}, {0x05, 0x3B}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x08}, {0x04, 0x32}, {0x05, 0x33}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x09}, {0x04, 0x67}, {0x05, 0x63}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x0a}, {0x04, 0x6C}, {0x05, 0x23}, {0x06, 0x0E}, {0x08, 0x00},
+ {0x03, 0x0b}, {0x04, 0x71}, {0x05, 0x23}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x0c}, {0x04, 0x30}, {0x05, 0x2F}, {0x06, 0x06}, {0x08, 0x00},
+ {0x03, 0x0d}, {0x04, 0x00}, {0x05, 0x00}, {0x06, 0x06}, {0x08, 0x00},
+ {0x07, 0x0e},
+
+ /* Start Address */
+ {0x10, 0x64}, {0x14, 0x10}, {0x15, 0x00},
+
+ /* SYNC */
+ {0x18, 0x40}, {0x19, 0x00}, {0x1A, 0x03}, {0x1B, 0x00},
+
+ /* X-Y Mirror */
+ {0x11, 0x00}, {0xda, 0x00}, /* X mirror OFF, Y Mirror OFF */
+
+ /* Frame height */
+ {0x1c, 0x13}, {0x1d, 0x04}, {0x0e, 0x4b}, {0x0f, 0x05},
+ {0x9e, 0x04}, {0x9d, 0xc6}, {0xcc, 0x14}, {0xcd, 0x05},
+
+ /* Frame width */
+ {0x0c, 0x35}, {0x0d, 0x07}, {0x9b, 0x10}, {0x9c, 0x07},
+ {0x93, 0x21},
+
+ {0x01, 0x01}, {0x40, 0x00}, {0x41, 0x00}, {0x42, 0xf0},
+ {0x43, 0x03}, {0x44, 0x0a}, {0x45, 0x00}, {0x3b, 0x40},
+ {0x38, 0x18}, {0x3c, 0x00}, {0x20, 0x00}, {0x21, 0x01},
+ {0x22, 0x00}, {0x23, 0x01}, {0x24, 0x00}, {0x25, 0x01},
+ {0x26, 0x00}, {0x27, 0x01}, {0xb9, 0x04}, {0xb8, 0xc3},
+ {0xbb, 0x04}, {0xba, 0xc3}, {0xbf, 0x04}, {0xbe, 0xc3},
+
+ /* Ramp */
+ {0x57, 0x07}, {0x56, 0xd6}, {0x55, 0x03}, {0x54, 0x74},
+ {0x9f, 0x99}, {0x94, 0x80}, {0x91, 0x78}, {0x92, 0x8b},
+
+ /* Output Mode */
+ {0x52, 0x10}, {0x51, 0x00},
+
+ /* Analog Gain and Output driver */
+ {0x28, 0x00}, {0xdd, 0x82}, {0xdb, 0x00}, {0xdc, 0x00},
+
+ /* Update */
+ {0x00, 0x84},
+
+ /* PLL ADC clock = 75 MHz */
+ {0xb5, 0x60}, {0xb4, 0x02}, {0xb5, 0x20},
+
+ /*----------------------------------------------*/
+ /* ISP Setting Start */
+ /*----------------------------------------------*/
+ {0xff, 0x02},
+ {0x01, 0xbd}, {0x02, 0xf8}, {0x03, 0x3a}, {0x04, 0x00}, {0x0e, 0x00},
+
+ /* Output mode */
+ {0x88, 0x00}, {0x87, 0x11},
+
+ /* Threshold */
+ {0xb6, 0x1b}, {0x0d, 0xc0}, {0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00},
+
+ /* Image Effect */
+ {0x3f, 0x80}, {0x40, 0x00}, {0x41, 0x00}, {0x42, 0x80}, {0x43, 0x00},
+ {0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x56, 0x80}, {0x57, 0x20},
+ {0x58, 0x20}, {0x59, 0x02}, {0x5a, 0x00}, {0x5b, 0x78}, {0x5c, 0x7c},
+ {0x5d, 0x84}, {0x5e, 0x85}, {0x5f, 0x78}, {0x60, 0x7e}, {0x61, 0x82},
+ {0x62, 0x85}, {0x63, 0x00}, {0x64, 0x80}, {0x65, 0x00}, {0x66, 0x80},
+ {0x67, 0x80}, {0x68, 0x80},
+
+ /* Auto Focus */
+ {0x6e, 0x02}, {0x6f, 0xe5}, {0x70, 0x08}, {0x71, 0x01}, {0x72, 0x00},
+
+ /* Decimator */
+ {0x78, 0xff}, {0x79, 0xff}, {0x7a, 0x70}, {0x7b, 0x00}, {0x7c, 0x00},
+ {0x7d, 0x00}, {0x7e, 0xc8}, {0x7f, 0xc8}, {0x80, 0x96}, {0x81, 0x96},
+ {0x82, 0x00}, {0x83, 0x00}, {0x84, 0x00}, {0x85, 0x00}, {0x86, 0x00},
+
+ /* Luminance Info */
+ {0xf9, 0x20}, {0xb7, 0x7f}, {0xb8, 0x28}, {0xb9, 0x08},
+ {0xf9, 0xa0}, {0xb7, 0x10}, {0xb9, 0x00},
+ {0xf9, 0x40}, {0xb7, 0x7f}, {0xb8, 0x28}, {0xb9, 0x08},
+ {0xf9, 0xc0}, {0xb7, 0x08}, {0xb9, 0x00},
+ {0xf9, 0x60}, {0xb7, 0x7f}, {0xb8, 0x28}, {0xb9, 0x08},
+ {0xf9, 0xe0}, {0xb7, 0x05}, {0xb9, 0x00},
+ {0xf9, 0x00}, {0xb7, 0x03}, {0xb8, 0x2d}, {0xb9, 0xcd},
+ {0xf9, 0x80}, {0xb7, 0x02}, {0xb9, 0x00},
+
+ /* AE */
+ {0x8a, 0x00}, {0x89, 0xc0}, {0x8c, 0x32}, {0x8d, 0x96}, {0x8e, 0x25},
+ {0x8f, 0x70}, {0x90, 0x12}, {0x91, 0x41}, {0x9e, 0x2e}, {0x9f, 0x2e},
+ {0xa0, 0x0b}, {0xa1, 0x71}, {0xa2, 0xb0}, {0xa3, 0x09}, {0xa4, 0x89},
+ {0xa5, 0x68}, {0xa6, 0x1a}, {0xa7, 0xb3}, {0xa8, 0xf0}, {0xa9, 0x19},
+ {0xaa, 0x6a}, {0xab, 0x6b}, {0xac, 0x01}, {0xad, 0xe8}, {0xae, 0x48},
+ {0xaf, 0x01}, {0xb0, 0x96}, {0xb1, 0xe6}, {0xb2, 0x03}, {0xb3, 0x00},
+ {0xb4, 0x10}, {0xb5, 0x00}, {0xb6, 0x04}, {0xba, 0x44}, {0xbb, 0x3a},
+ {0xbc, 0x01}, {0xbd, 0x08}, {0xbe, 0xa0}, {0xbf, 0x01}, {0xc0, 0x82},
+ {0x8a, 0xe1}, {0x8b, 0x8c},
+
+ /* AWB */
+ {0xc8, 0x00}, {0xc9, 0x00}, {0xca, 0x40}, {0xcb, 0xB0}, {0xcc, 0x40},
+ {0xcd, 0xff}, {0xce, 0x19}, {0xcf, 0x40}, {0xd0, 0x01}, {0xd1, 0x43},
+ {0xd2, 0x80}, {0xd3, 0x80}, {0xd4, 0xf1}, {0xdf, 0x00}, {0xe0, 0x8f},
+ {0xe1, 0x8f}, {0xe2, 0x53}, {0xe3, 0x97}, {0xe4, 0x1f}, {0xe5, 0x3b},
+ {0xe6, 0x9c}, {0xe7, 0x2e}, {0xe8, 0x03}, {0xe9, 0x02},
+
+ /* Neutral CCM */
+ {0xfa, 0x00}, {0xd5, 0x3f}, {0xd6, 0x8c}, {0xd7, 0x43}, {0xd8, 0x08},
+ {0xd9, 0x27}, {0xda, 0x7e}, {0xdb, 0x17}, {0xdc, 0x1a}, {0xdd, 0x47},
+ {0xde, 0xa1},
+
+ /* Blue CCM */
+ {0xfa, 0x01}, {0xd5, 0x3f}, {0xd6, 0x77}, {0xd7, 0x34}, {0xd8, 0x03},
+ {0xd9, 0x18}, {0xda, 0x6e}, {0xdb, 0x16}, {0xdc, 0x0f}, {0xdd, 0x29},
+ {0xde, 0x77},
+
+ /* Red CCM */
+ {0xfa, 0x02}, {0xd5, 0x3f}, {0xd6, 0x7d}, {0xd7, 0x2f}, {0xd8, 0x0e},
+ {0xd9, 0x1e}, {0xda, 0x76}, {0xdb, 0x18}, {0xdc, 0x29}, {0xdd, 0x51},
+ {0xde, 0xba},
+
+ /* AWB */
+ {0xea, 0x00}, {0xeb, 0x1a}, {0xc8, 0x33}, {0xc9, 0xc2},
+
+ {0xed, 0x02}, {0xee, 0x02},
+
+ /* AFD */
+ {0xf0, 0x11}, {0xf1, 0x03}, {0xf2, 0x05}, {0xf5, 0x05}, {0xf6, 0x32},
+ {0xf7, 0x32},
+
+ /* Lens Shading */
+ {0xf9, 0x00}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0xf2}, {0x08, 0x00},
+ {0x09, 0x00}, {0x0a, 0xf2}, {0x0b, 0xff}, {0x0c, 0xff},
+ {0xf9, 0x01}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0x8b}, {0x08, 0x16},
+ {0x09, 0x16}, {0x0a, 0x8b}, {0x0b, 0xff}, {0x0c, 0xe0},
+ {0xf9, 0x02}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0x8b}, {0x08, 0x16},
+ {0x09, 0x16}, {0x0a, 0x8b}, {0x0b, 0xff}, {0x0c, 0xe0},
+ {0xf9, 0x03}, {0x05, 0x04}, {0x06, 0xff}, {0x07, 0x7c}, {0x08, 0x26},
+ {0x09, 0x26}, {0x0a, 0x7c}, {0x0b, 0xd0}, {0x0c, 0xe0},
+ {0xf9, 0x04}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00},
+ {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xe0},
+ {0xf9, 0x05}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00},
+ {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xa0},
+ {0xf9, 0x06}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00},
+ {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xa0},
+ {0xf9, 0x07}, {0x05, 0x0d}, {0x06, 0x40}, {0x07, 0xa0}, {0x08, 0x00},
+ {0x09, 0x00}, {0x0a, 0xa0}, {0x0b, 0x40}, {0x0c, 0xa0},
+
+ /* Edge setting */
+ {0x73, 0x68}, {0x74, 0x40}, {0x75, 0x00}, {0x76, 0xff}, {0x77, 0x80},
+ {0x4f, 0x80}, {0x50, 0x82}, {0x51, 0x82}, {0x52, 0x08},
+
+ /* Interpolation Setting */
+ {0x23, 0x7f}, {0x22, 0x08}, {0x18, 0xff}, {0x19, 0x00},
+ {0x40, 0x00}, {0x53, 0xff}, {0x54, 0x0a}, {0x55, 0xc2},
+ {0x1b, 0x18},
+
+ {0xfa, 0x00}, {0x15, 0x0c}, {0x22, 0x00}, {0x0e, 0xef}, {0x1f, 0x1d},
+ {0x20, 0x2d}, {0x1c, 0x01}, {0x1d, 0x02}, {0x1e, 0x03}, {0x0e, 0xee},
+ {0x12, 0x10}, {0x16, 0x10}, {0x17, 0x02}, {0x1a, 0x01},
+ {0xfa, 0x04}, {0x0e, 0xef}, {0x1c, 0x01}, {0x1d, 0x02}, {0x1e, 0x03},
+ {0x1f, 0x11}, {0x20, 0x11}, {0x0e, 0xee}, {0x12, 0x03}, {0x16, 0x10},
+ {0x17, 0x02}, {0x1a, 0xee},
+ {0xfa, 0x08}, {0x0e, 0xef}, {0x1c, 0x01}, {0x1d, 0x02}, {0x1e, 0x03},
+ {0x1f, 0x00}, {0x20, 0x00}, {0x0e, 0xee}, {0x12, 0x03}, {0x16, 0x10},
+ {0x17, 0x02}, {0x1a, 0x22},
+
+ /* Gamma Correction */
+ {0x27, 0x62}, {0x28, 0x00}, {0x27, 0x62}, {0x28, 0x00}, {0x29, 0x00},
+ {0x2a, 0x00}, {0x2f, 0x03}, {0x30, 0x10}, {0x31, 0x2b}, {0x32, 0x50},
+ {0x33, 0x70}, {0x34, 0x90}, {0x35, 0xB0}, {0x36, 0xD0}, {0x37, 0x00},
+ {0x38, 0x18}, {0x39, 0x57}, {0x3a, 0x89}, {0x3b, 0xac}, {0x3c, 0xc9},
+ {0x3d, 0xde}, {0x3e, 0xef}, {0x2b, 0x00}, {0x2c, 0x00}, {0x2d, 0x40},
+ {0x2e, 0xab},
+
+ /* Contrast */
+ {0x47, 0x10}, {0x48, 0x1f}, {0x49, 0xe3}, {0x4a, 0xf0}, {0x4b, 0x08},
+ {0x4c, 0x14}, {0x4d, 0xe9}, {0x4e, 0xf5}, {0x98, 0x8a},
+
+ {0xfa, 0x00},
+ {MC521DA_TERM, MC521DA_TERM}
+};
+
+static int mc521da_attach(struct i2c_adapter *adapter);
+static int mc521da_detach(struct i2c_client *client);
+
+static struct i2c_driver mc521da_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "MC521DA Client",
+ },
+ .attach_adapter = mc521da_attach,
+ .detach_client = mc521da_detach,
+};
+
+static struct i2c_client mc521da_i2c_client = {
+ .name = "MC521DA I2C dev",
+ .addr = MC521DA_I2C_ADDRESS,
+ .driver = &mc521da_i2c_driver,
+};
+
+static inline int mc521da_read_reg(u8 reg)
+{
+ return i2c_smbus_read_byte_data(&mc521da_i2c_client, reg);
+}
+
+static inline int mc521da_write_reg(u8 reg, u8 val)
+{
+ return i2c_smbus_write_byte_data(&mc521da_i2c_client, reg, val);
+}
+
+static int mc521da_write_regs(const struct mc521da_reg reglist[])
+{
+ int err;
+ const struct mc521da_reg *next = reglist;
+
+ while (!((next->reg == MC521DA_TERM) && (next->val == MC521DA_TERM))) {
+ err = mc521da_write_reg(next->reg, next->val);
+ if (err) {
+ return err;
+ }
+ next++;
+ }
+ return 0;
+}
+
+/*!
+ * mc521da sensor downscale function
+ * @param downscale bool
+ * @return Error code indicating success or failure
+ */
+static u8 mc521da_sensor_downscale(bool downscale)
+{
+ u8 data;
+ u32 i = 0;
+
+ if (downscale == true) {
+ // VGA
+ mc521da_write_reg(0xff, 0x01);
+
+ mc521da_write_reg(0x52, 0x30);
+ mc521da_write_reg(0x51, 0x00);
+
+ mc521da_write_reg(0xda, 0x01);
+ mc521da_write_reg(0x00, 0x8C);
+
+ /* Wait for changes to take effect */
+ while (i < 256) {
+ i++;
+ data = mc521da_read_reg(0x00);
+ if ((data & 0x80) == 0)
+ break;
+ msleep(5);
+ }
+
+ /* ISP */
+ mc521da_write_reg(0xff, 0x02);
+
+ mc521da_write_reg(0x03, 0x3b); /* Enable Decimator */
+
+ mc521da_write_reg(0x7a, 0x74);
+ mc521da_write_reg(0x7b, 0x01);
+ mc521da_write_reg(0x7e, 0x50);
+ mc521da_write_reg(0x7f, 0x50);
+ mc521da_write_reg(0x80, 0x3c);
+ mc521da_write_reg(0x81, 0x3c);
+ } else {
+ //UXGA
+ mc521da_write_reg(0xff, 0x01);
+ mc521da_write_reg(0x52, 0x10);
+ mc521da_write_reg(0x51, 0x00);
+ mc521da_write_reg(0xda, 0x00);
+
+ /* update */
+ mc521da_write_reg(0x00, 0x84);
+
+ /* Wait for changes to take effect */
+ while (i < 256) {
+ i++;
+ data = mc521da_read_reg(0x00);
+ if ((data & 0x80) == 0)
+ break;
+ msleep(5);
+ }
+
+ /* ISP */
+ mc521da_write_reg(0xff, 0x02);
+
+ mc521da_write_reg(0x03, 0x3a);
+
+ mc521da_write_reg(0x7a, 0x70);
+ mc521da_write_reg(0x7b, 0x00);
+ mc521da_write_reg(0x7e, 0xc8);
+ mc521da_write_reg(0x7f, 0xc8);
+ mc521da_write_reg(0x80, 0x96);
+ mc521da_write_reg(0x81, 0x96);
+ }
+
+ return 0;
+}
+
+/*!
+ * mc521da sensor interface Initialization
+ * @param param sensor_interface *
+ * @param width u32
+ * @param height u32
+ * @return None
+ */
+static void mc521da_interface(sensor_interface * param, u32 width, u32 height)
+{
+ param->clk_mode = 0x0; //gated
+ param->pixclk_pol = 0x0;
+ param->data_width = 0x1;
+ param->data_pol = 0x0;
+ param->ext_vsync = 0x0;
+ param->Vsync_pol = 0x0;
+ param->Hsync_pol = 0x0;
+ param->width = width - 1;
+ param->height = height - 1;
+ param->active_width = width;
+ param->active_height = height;
+ param->pixel_fmt = IPU_PIX_FMT_UYVY;
+}
+
+extern void gpio_sensor_reset(bool flag);
+
+/*!
+ * mc521da Reset function
+ *
+ * @return None
+ */
+static sensor_interface *mc521da_reset(void)
+{
+ if (interface_param == NULL)
+ return NULL;
+
+ mc521da_interface(interface_param, format[1].width, format[1].height);
+ set_mclk_rate(&interface_param->mclk);
+
+ gpio_sensor_reset(true);
+ msleep(10);
+ gpio_sensor_reset(false);
+ msleep(50);
+
+ return interface_param;
+}
+
+/*!
+ * mc521da sensor configuration
+ *
+ * @param frame_rate int *
+ * @param high_quality int
+ * @return sensor_interface *
+ */
+static sensor_interface *mc521da_config(int *frame_rate, int high_quality)
+{
+ int num_clock_per_row, err;
+ int max_rate = 0;
+ int index = 1;
+ u16 frame_height;
+
+ if (high_quality == 1)
+ index = 0;
+
+ err = mc521da_write_regs(mc521da_initial);
+ if (err) {
+ /* Reduce the MCLK */
+ interface_param->mclk = 20000000;
+ mc521da_reset();
+
+ printk(KERN_INFO "mc521da: mclk reduced\n");
+ mc521da_write_regs(mc521da_initial);
+ }
+
+ mc521da_interface(interface_param, format[index].width,
+ format[index].height);
+
+ if (index == 0) {
+ mc521da_sensor_downscale(false);
+ } else {
+ mc521da_sensor_downscale(true);
+ }
+
+ num_clock_per_row = 1845;
+ max_rate = interface_param->mclk * 3 * (index + 1)
+ / (2 * num_clock_per_row * 1300);
+
+ if ((*frame_rate > max_rate) || (*frame_rate == 0)) {
+ *frame_rate = max_rate;
+ }
+
+ frame_height = 1300 * max_rate / (*frame_rate);
+
+ *frame_rate = interface_param->mclk * 3 * (index + 1)
+ / (2 * num_clock_per_row * frame_height);
+
+ mc521da_write_reg(0xff, 0x01);
+ mc521da_write_reg(0xE, frame_height & 0xFF);
+ mc521da_write_reg(0xF, (frame_height & 0xFF00) >> 8);
+ mc521da_write_reg(0xCC, frame_height & 0xFF);
+ mc521da_write_reg(0xCD, (frame_height & 0xFF00) >> 8);
+
+ return interface_param;
+}
+
+/*!
+ * mc521da sensor set color configuration
+ *
+ * @param bright int
+ * @param saturation int
+ * @param red int
+ * @param green int
+ * @param blue int
+ * @return None
+ */
+static void
+mc521da_set_color(int bright, int saturation, int red, int green, int blue)
+{
+ /* Select ISP */
+ mc521da_write_reg(0xff, 0x02);
+
+ mc521da_write_reg(0x41, bright);
+ mc521da_write_reg(0xca, red);
+ mc521da_write_reg(0xcb, green);
+ mc521da_write_reg(0xcc, blue);
+}
+
+/*!
+ * mc521da sensor get color configuration
+ *
+ * @param bright int *
+ * @param saturation int *
+ * @param red int *
+ * @param green int *
+ * @param blue int *
+ * @return None
+ */
+static void
+mc521da_get_color(int *bright, int *saturation, int *red, int *green, int *blue)
+{
+ *saturation = 0;
+
+ /* Select ISP */
+ mc521da_write_reg(0xff, 0x02);
+
+ *bright = mc521da_read_reg(0x41);
+ *red = mc521da_read_reg(0xCA);
+ *green = mc521da_read_reg(0xCB);
+ *blue = mc521da_read_reg(0xCC);
+}
+
+struct camera_sensor camera_sensor_if = {
+ set_color:mc521da_set_color,
+ get_color:mc521da_get_color,
+ config:mc521da_config,
+ reset:mc521da_reset,
+};
+
+/*!
+ * mc521da I2C detect_client function
+ *
+ * @param adapter struct i2c_adapter *
+ * @param address int
+ * @param kind int
+ *
+ * @return Error code indicating success or failure
+ */
+static int mc521da_detect_client(struct i2c_adapter *adapter, int address,
+ int kind)
+{
+ mc521da_i2c_client.adapter = adapter;
+ if (i2c_attach_client(&mc521da_i2c_client)) {
+ mc521da_i2c_client.adapter = NULL;
+ printk(KERN_ERR "mc521da_attach: i2c_attach_client failed\n");
+ return -1;
+ }
+
+ interface_param = (sensor_interface *)
+ kmalloc(sizeof(sensor_interface), GFP_KERNEL);
+ if (!interface_param) {
+ printk(KERN_ERR "mc521da_attach: kmalloc failed \n");
+ return -1;
+ }
+
+ interface_param->mclk = 25000000;
+
+ printk(KERN_INFO "mc521da Detected\n");
+
+ return 0;
+}
+
+static unsigned short normal_i2c[] = { MC521DA_I2C_ADDRESS, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static int mc521da_attach(struct i2c_adapter *adap)
+{
+ uint32_t mclk = 25000000;
+ struct clk *clk;
+ int err;
+
+ clk = clk_get(NULL, "csi_clk");
+ clk_enable(clk);
+ set_mclk_rate(&mclk);
+
+ gpio_sensor_reset(true);
+ msleep(10);
+ gpio_sensor_reset(false);
+ msleep(100);
+
+ err = i2c_probe(adap, &addr_data, &mc521da_detect_client);
+
+ clk_disable(clk);
+ clk_put(clk);
+
+ return err;
+}
+
+/*!
+ * mc521da I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int mc521da_detach(struct i2c_client *client)
+{
+ int err;
+
+ if (!mc521da_i2c_client.adapter)
+ return -1;
+
+ err = i2c_detach_client(&mc521da_i2c_client);
+ mc521da_i2c_client.adapter = NULL;
+
+ if (interface_param)
+ kfree(interface_param);
+ interface_param = NULL;
+
+ return err;
+}
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+/*!
+ * mc521da init function
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int mc521da_init(void)
+{
+ gpio_sensor_active();
+ return i2c_add_driver(&mc521da_i2c_driver);
+}
+
+/*!
+ * mc521da cleanup function
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit mc521da_clean(void)
+{
+ i2c_del_driver(&mc521da_i2c_driver);
+ gpio_sensor_inactive();
+}
+
+module_init(mc521da_init);
+module_exit(mc521da_clean);
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(camera_sensor_if);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MC521DA Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/mt9v111.c b/drivers/media/video/mxc/capture/mt9v111.c
new file mode 100644
index 000000000000..c95a20683924
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mt9v111.c
@@ -0,0 +1,1076 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mt9v111.c
+ *
+ * @brief mt9v111 camera driver functions
+ *
+ * @ingroup Camera
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+#include "mt9v111.h"
+
+#ifdef MT9V111_DEBUG
+static u16 testpattern = 0;
+#endif
+
+static mt9v111_conf mt9v111_device;
+
+/*!
+ * Holds the current frame rate.
+ */
+static int reset_frame_rate = MT9V111_FRAME_RATE;
+
+struct sensor {
+ const struct mt9v111_platform_data *platform_data;
+ struct v4l2_int_device *v4l2_int_device;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ struct v4l2_captureparm streamcap;
+ bool on;
+
+ /* control settings */
+ int brightness;
+ int hue;
+ int contrast;
+ int saturation;
+ int red;
+ int green;
+ int blue;
+ int ae_mode;
+
+} mt9v111_data;
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+static int mt9v111_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int mt9v111_remove(struct i2c_client *client);
+
+static const struct i2c_device_id mt9v111_id[] = {
+ {"mt9v111", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mt9v111_id);
+
+static struct i2c_driver mt9v111_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mt9v111",
+ },
+ .probe = mt9v111_probe,
+ .remove = mt9v111_remove,
+ .id_table = mt9v111_id,
+/* To add power management add .suspend and .resume functions */
+};
+
+/*
+ * Function definitions
+ */
+
+#ifdef MT9V111_DEBUG
+static inline int mt9v111_read_reg(u8 reg)
+{
+ int val = i2c_smbus_read_word_data(mt9v111_data.i2c_client, reg);
+ if (val != -1)
+ val = cpu_to_be16(val);
+ return val;
+}
+#endif
+
+/*!
+ * Writes to the register via I2C.
+ */
+static inline int mt9v111_write_reg(u8 reg, u16 val)
+{
+ pr_debug("In mt9v111_write_reg (0x%x, 0x%x)\n", reg, val);
+ pr_debug(" write reg %x val %x.\n", reg, val);
+
+ return i2c_smbus_write_word_data(mt9v111_data.i2c_client,
+ reg, cpu_to_be16(val));
+}
+
+/*!
+ * Initialize mt9v111_sensor_lib
+ * Libarary for Sensor configuration through I2C
+ *
+ * @param coreReg Core Registers
+ * @param ifpReg IFP Register
+ *
+ * @return status
+ */
+static u8 mt9v111_sensor_lib(mt9v111_coreReg * coreReg, mt9v111_IFPReg * ifpReg)
+{
+ u8 reg;
+ u16 data;
+ u8 error = 0;
+
+ pr_debug("In mt9v111_sensor_lib\n");
+
+ /*
+ * setup to IFP registers
+ */
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ /* Operation Mode Control */
+ reg = MT9V111I_MODE_CONTROL;
+ data = ifpReg->modeControl;
+ mt9v111_write_reg(reg, data);
+
+ /* Output format */
+ reg = MT9V111I_FORMAT_CONTROL;
+ data = ifpReg->formatControl; /* Set bit 12 */
+ mt9v111_write_reg(reg, data);
+
+ /* AE limit 4 */
+ reg = MT9V111I_SHUTTER_WIDTH_LIMIT_AE;
+ data = ifpReg->gainLimitAE;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_OUTPUT_FORMAT_CTRL2;
+ data = ifpReg->outputFormatCtrl2;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_AE_SPEED;
+ data = ifpReg->AESpeed;
+ mt9v111_write_reg(reg, data);
+
+ /* output image size */
+ reg = MT9V111i_H_PAN;
+ data = 0x8000 | ifpReg->HPan;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_ZOOM;
+ data = 0x8000 | ifpReg->HZoom;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_SIZE;
+ data = 0x8000 | ifpReg->HSize;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_PAN;
+ data = 0x8000 | ifpReg->VPan;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_ZOOM;
+ data = 0x8000 | ifpReg->VZoom;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_V_SIZE;
+ data = 0x8000 | ifpReg->VSize;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111i_H_PAN;
+ data = ~0x8000 & ifpReg->HPan;
+ mt9v111_write_reg(reg, data);
+#if 0
+ reg = MT9V111I_UPPER_SHUTTER_DELAY_LIM;
+ data = ifpReg->upperShutterDelayLi;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_SHUTTER_60;
+ data = ifpReg->shutter_width_60;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_SEARCH_FLICK_60;
+ data = ifpReg->search_flicker_60;
+ mt9v111_write_reg(reg, data);
+#endif
+
+ /*
+ * setup to sensor core registers
+ */
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = coreReg->addressSelect;
+ mt9v111_write_reg(reg, data);
+
+ /* enable changes and put the Sync bit on */
+ reg = MT9V111S_OUTPUT_CTRL;
+ data = MT9V111S_OUTCTRL_SYNC | MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000;
+ mt9v111_write_reg(reg, data);
+
+ /* min PIXCLK - Default */
+ reg = MT9V111S_PIXEL_CLOCK_SPEED;
+ data = coreReg->pixelClockSpeed;
+ mt9v111_write_reg(reg, data);
+
+ /* Setup image flipping / Dark rows / row/column skip */
+ reg = MT9V111S_READ_MODE;
+ data = coreReg->readMode;
+ mt9v111_write_reg(reg, data);
+
+ /* zoom 0 */
+ reg = MT9V111S_DIGITAL_ZOOM;
+ data = coreReg->digitalZoom;
+ mt9v111_write_reg(reg, data);
+
+ /* min H-blank */
+ reg = MT9V111S_HOR_BLANKING;
+ data = coreReg->horizontalBlanking;
+ mt9v111_write_reg(reg, data);
+
+ /* min V-blank */
+ reg = MT9V111S_VER_BLANKING;
+ data = coreReg->verticalBlanking;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_SHUTTER_WIDTH;
+ data = coreReg->shutterWidth;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_SHUTTER_DELAY;
+ data = ifpReg->upperShutterDelayLi;
+ mt9v111_write_reg(reg, data);
+
+ /* changes become effective */
+ reg = MT9V111S_OUTPUT_CTRL;
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | 0x3000;
+ mt9v111_write_reg(reg, data);
+
+ return error;
+}
+
+/*!
+ * MT9V111 frame rate calculate
+ *
+ * @param frame_rate int *
+ * @param mclk int
+ * @return None
+ */
+static void mt9v111_rate_cal(int *frame_rate, int mclk)
+{
+ int num_clock_per_row;
+ int max_rate = 0;
+
+ pr_debug("In mt9v111_rate_cal\n");
+
+ num_clock_per_row = (MT9V111_MAX_WIDTH + 114 + MT9V111_HORZBLANK_MIN)
+ * 2;
+ max_rate = mclk / (num_clock_per_row *
+ (MT9V111_MAX_HEIGHT + MT9V111_VERTBLANK_DEFAULT));
+
+ if ((*frame_rate > max_rate) || (*frame_rate == 0)) {
+ *frame_rate = max_rate;
+ }
+
+ mt9v111_device.coreReg->verticalBlanking
+ = mclk / (*frame_rate * num_clock_per_row) - MT9V111_MAX_HEIGHT;
+
+ reset_frame_rate = *frame_rate;
+}
+
+/*!
+ * MT9V111 sensor configuration
+ */
+void mt9v111_config(void)
+{
+ pr_debug("In mt9v111_config\n");
+
+ mt9v111_device.coreReg->addressSelect = MT9V111I_SEL_SCA;
+ mt9v111_device.ifpReg->addrSpaceSel = MT9V111I_SEL_IFP;
+
+ mt9v111_device.coreReg->windowHeight = MT9V111_WINHEIGHT;
+ mt9v111_device.coreReg->windowWidth = MT9V111_WINWIDTH;
+ mt9v111_device.coreReg->zoomColStart = 0;
+ mt9v111_device.coreReg->zomRowStart = 0;
+ mt9v111_device.coreReg->digitalZoom = 0x0;
+
+ mt9v111_device.coreReg->verticalBlanking = MT9V111_VERTBLANK_DEFAULT;
+ mt9v111_device.coreReg->horizontalBlanking = MT9V111_HORZBLANK_MIN;
+ mt9v111_device.coreReg->pixelClockSpeed = 0;
+ mt9v111_device.coreReg->readMode = 0xd0a1;
+
+ mt9v111_device.ifpReg->outputFormatCtrl2 = 0;
+ mt9v111_device.ifpReg->gainLimitAE = 0x300;
+ mt9v111_device.ifpReg->AESpeed = 0x80;
+
+ /* here is the default value */
+ mt9v111_device.ifpReg->formatControl = 0xc800;
+ mt9v111_device.ifpReg->modeControl = 0x708e;
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ mt9v111_device.coreReg->shutterWidth = 0xf8;
+
+ /* output size */
+ mt9v111_device.ifpReg->HPan = 0;
+ mt9v111_device.ifpReg->HZoom = MT9V111_MAX_WIDTH;
+ mt9v111_device.ifpReg->HSize = MT9V111_MAX_WIDTH;
+ mt9v111_device.ifpReg->VPan = 0;
+ mt9v111_device.ifpReg->VZoom = MT9V111_MAX_HEIGHT;
+ mt9v111_device.ifpReg->VSize = MT9V111_MAX_HEIGHT;
+}
+
+/*!
+ * mt9v111 sensor set saturtionn
+ *
+ * @param saturation int
+
+ * @return Error code of 0.
+ */
+static int mt9v111_set_saturation(int saturation)
+{
+ u8 reg;
+ u16 data;
+ pr_debug("In mt9v111_set_saturation(%d)\n",
+ saturation);
+
+ switch (saturation) {
+ case 150:
+ mt9v111_device.ifpReg->awbSpeed = 0x6D14;
+ break;
+ case 100:
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ break;
+ case 75:
+ mt9v111_device.ifpReg->awbSpeed = 0x4D14;
+ break;
+ case 50:
+ mt9v111_device.ifpReg->awbSpeed = 0x5514;
+ break;
+ case 37:
+ mt9v111_device.ifpReg->awbSpeed = 0x5D14;
+ break;
+ case 25:
+ mt9v111_device.ifpReg->awbSpeed = 0x6514;
+ break;
+ default:
+ mt9v111_device.ifpReg->awbSpeed = 0x4514;
+ break;
+ }
+
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = mt9v111_device.ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ /* Operation Mode Control */
+ reg = MT9V111I_AWB_SPEED;
+ data = mt9v111_device.ifpReg->awbSpeed;
+ mt9v111_write_reg(reg, data);
+
+ return 0;
+}
+
+/*!
+ * mt9v111 sensor set Auto Exposure measurement window mode configuration
+ *
+ * @param ae_mode int
+ * @return Error code of 0 (no Error)
+ */
+static int mt9v111_set_ae_mode(int ae_mode)
+{
+ u8 reg;
+ u16 data;
+
+ pr_debug("In mt9v111_set_ae_mode(%d)\n",
+ ae_mode);
+
+ /* Currently this driver only supports auto and manual exposure
+ * modes. */
+ if ((ae_mode > 1) || (ae_mode << 0))
+ return -EPERM;
+
+ /*
+ * The auto exposure is set in bit 14.
+ * Other values are set for:
+ * -on the fly defect correction is on (bit 13).
+ * -aperature correction knee enabled (bit 12).
+ * -ITU_R BT656 synchronization codes are embedded in the image (bit 7)
+ * -AE measurement window is weighted sum of large and center windows
+ * (bits 2-3).
+ * -auto white balance is on (bit 1).
+ * -normal color processing (bit 4 = 0).
+ */
+ /* V4L2_EXPOSURE_AUTO = 0; needs register setting of 0x708E */
+ /* V4L2_EXPOSURE_MANUAL = 1 needs register setting of 0x308E */
+ mt9v111_device.ifpReg->modeControl &= 0x3fff;
+ mt9v111_device.ifpReg->modeControl |= (ae_mode & 0x03) << 14;
+ mt9v111_data.ae_mode = ae_mode;
+
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = mt9v111_device.ifpReg->addrSpaceSel;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111I_MODE_CONTROL;
+ data = mt9v111_device.ifpReg->modeControl;
+ mt9v111_write_reg(reg, data);
+
+ return 0;
+}
+
+/*!
+ * mt9v111 sensor get AE measurement window mode configuration
+ *
+ * @param ae_mode int *
+ * @return None
+ */
+static void mt9v111_get_ae_mode(int *ae_mode)
+{
+ pr_debug("In mt9v111_get_ae_mode(%d)\n", *ae_mode);
+
+ if (ae_mode != NULL) {
+ *ae_mode = (mt9v111_device.ifpReg->modeControl & 0xc) >> 2;
+ }
+}
+
+#ifdef MT9V111_DEBUG
+/*!
+ * Set sensor to test mode, which will generate test pattern.
+ *
+ * @return none
+ */
+static void mt9v111_test_pattern(bool flag)
+{
+ u16 data;
+
+ /* switch to sensor registers */
+ mt9v111_write_reg(MT9V111I_ADDR_SPACE_SEL, MT9V111I_SEL_SCA);
+
+ if (flag == true) {
+ testpattern = MT9V111S_OUTCTRL_TEST_MODE;
+
+ data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) & 0xBF;
+ mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data);
+
+ mt9v111_write_reg(MT9V111S_TEST_DATA, 0);
+
+ /* changes take effect */
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+ mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data);
+ } else {
+ testpattern = 0;
+
+ data = mt9v111_read_reg(MT9V111S_ROW_NOISE_CTRL) | 0x40;
+ mt9v111_write_reg(MT9V111S_ROW_NOISE_CTRL, data);
+
+ /* changes take effect */
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+ mt9v111_write_reg(MT9V111S_OUTPUT_CTRL, data);
+ }
+}
+#endif
+
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+/*!
+ * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num
+ * s: pointer to standard V4L2 device structure
+ * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p. This value is returned in the p
+ * parameter.
+ *
+ * vidioc_int_g_ifparm returns platform-specific information about the
+ * interface settings used by the sensor.
+ *
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency.
+ *
+ * Called on open.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ pr_debug("In mt9v111:ioctl_g_ifparm\n");
+
+ if (s == NULL) {
+ pr_err(" ERROR!! no slave device set!\n");
+ return -1;
+ }
+
+ memset(p, 0, sizeof(*p));
+ p->u.bt656.clock_curr = MT9V111_MCLK;
+ p->if_type = V4L2_IF_TYPE_BT656;
+ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+ p->u.bt656.clock_min = MT9V111_CLK_MIN;
+ p->u.bt656.clock_max = MT9V111_CLK_MAX;
+
+ return 0;
+}
+
+/*!
+ * Sets the camera power.
+ *
+ * s pointer to the camera device
+ * on if 1, power is to be turned on. 0 means power is to be turned off
+ *
+ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ * This is called on suspend and resume.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct sensor *sensor = s->priv;
+
+ pr_debug("In mt9v111:ioctl_s_power\n");
+
+ sensor->on = on;
+
+ if (on)
+ gpio_sensor_active();
+ else
+ gpio_sensor_inactive();
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ int ret = 0;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+ /* s->priv points to mt9v111_data */
+
+ pr_debug("In mt9v111:ioctl_g_parm\n");
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cparm->capability = mt9v111_data.streamcap.capability;
+ cparm->timeperframe =
+ mt9v111_data.streamcap.timeperframe;
+ cparm->capturemode = mt9v111_data.streamcap.capturemode;
+ ret = 0;
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ pr_err(" type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+ "but %d\n", a->type);
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_err(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible. If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ int ret = 0;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+ /* s->priv points to mt9v111_data */
+
+ pr_debug("In mt9v111:ioctl_s_parm\n");
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+
+ /* Check that the new frame rate is allowed.
+ * Changing the frame rate is not allowed on this
+ *camera. */
+ if (cparm->timeperframe.denominator !=
+ mt9v111_data.streamcap.timeperframe.denominator) {
+ pr_err("ERROR: mt9v111: ioctl_s_parm: " \
+ "This camera does not allow frame rate "
+ "changes.\n");
+ ret = -EINVAL;
+ } else {
+ mt9v111_data.streamcap.timeperframe =
+ cparm->timeperframe;
+ /* Call any camera functions to match settings. */
+ }
+
+ /* Check that new capture mode is supported. */
+ if ((cparm->capturemode != 0) &&
+ !(cparm->capturemode & V4L2_MODE_HIGHQUALITY)) {
+ pr_err("ERROR: mt9v111: ioctl_s_parm: " \
+ "unsupported capture mode\n");
+ ret = -EINVAL;
+ } else {
+ mt9v111_data.streamcap.capturemode =
+ cparm->capturemode;
+ /* Call any camera functions to match settings. */
+ /* Right now this camera only supports 1 mode. */
+ }
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ pr_err(" type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+ "but %d\n", a->type);
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_err(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct sensor *sensor = s->priv;
+ /* s->priv points to mt9v111_data */
+
+ pr_debug("In mt9v111:ioctl_g_fmt_cap.\n");
+ pr_debug(" Returning size of %dx%d\n",
+ sensor->pix.width, sensor->pix.height);
+
+ f->fmt.pix = sensor->pix;
+
+ return 0;
+}
+
+/*!
+ * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control information
+ * from the video_control[] array. Otherwise, returns -EINVAL if the
+ * control is not supported.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qc)
+{
+ pr_debug("In mt9v111:ioctl_queryctrl\n");
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array. Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ pr_debug("In mt9v111:ioctl_g_ctrl\n");
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ pr_debug(" V4L2_CID_BRIGHTNESS\n");
+ vc->value = mt9v111_data.brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ pr_debug(" V4L2_CID_CONTRAST\n");
+ vc->value = mt9v111_data.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ pr_debug(" V4L2_CID_SATURATION\n");
+ vc->value = mt9v111_data.saturation;
+ break;
+ case V4L2_CID_HUE:
+ pr_debug(" V4L2_CID_HUE\n");
+ vc->value = mt9v111_data.hue;
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ pr_debug(" V4L2_CID_RED_BALANCE\n");
+ vc->value = mt9v111_data.red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ pr_debug(" V4L2_CID_BLUE_BALANCE\n");
+ vc->value = mt9v111_data.blue;
+ break;
+ case V4L2_CID_GAMMA:
+ pr_debug(" V4L2_CID_GAMMA\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_EXPOSURE:
+ pr_debug(" V4L2_CID_EXPOSURE\n");
+ vc->value = mt9v111_data.ae_mode;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ pr_debug(" V4L2_CID_AUTOGAIN\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_GAIN:
+ pr_debug(" V4L2_CID_GAIN\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_HFLIP:
+ pr_debug(" V4L2_CID_HFLIP\n");
+ vc->value = 0;
+ break;
+ case V4L2_CID_VFLIP:
+ pr_debug(" V4L2_CID_VFLIP\n");
+ vc->value = 0;
+ break;
+ default:
+ pr_debug(" Default case\n");
+ return -EPERM;
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array). Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int retval = 0;
+
+ pr_debug("In mt9v111:ioctl_s_ctrl %d\n",
+ vc->id);
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ pr_debug(" V4L2_CID_BRIGHTNESS\n");
+ break;
+ case V4L2_CID_CONTRAST:
+ pr_debug(" V4L2_CID_CONTRAST\n");
+ break;
+ case V4L2_CID_SATURATION:
+ pr_debug(" V4L2_CID_SATURATION\n");
+ retval = mt9v111_set_saturation(vc->value);
+ break;
+ case V4L2_CID_HUE:
+ pr_debug(" V4L2_CID_HUE\n");
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_RED_BALANCE:
+ pr_debug(" V4L2_CID_RED_BALANCE\n");
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ pr_debug(" V4L2_CID_BLUE_BALANCE\n");
+ break;
+ case V4L2_CID_GAMMA:
+ pr_debug(" V4L2_CID_GAMMA\n");
+ break;
+ case V4L2_CID_EXPOSURE:
+ pr_debug(" V4L2_CID_EXPOSURE\n");
+ retval = mt9v111_set_ae_mode(vc->value);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ pr_debug(" V4L2_CID_AUTOGAIN\n");
+ break;
+ case V4L2_CID_GAIN:
+ pr_debug(" V4L2_CID_GAIN\n");
+ break;
+ case V4L2_CID_HFLIP:
+ pr_debug(" V4L2_CID_HFLIP\n");
+ break;
+ case V4L2_CID_VFLIP:
+ pr_debug(" V4L2_CID_VFLIP\n");
+ break;
+ default:
+ pr_debug(" Default case\n");
+ retval = -EPERM;
+ break;
+ }
+
+ return retval;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ pr_debug("In mt9v111:ioctl_init\n");
+
+ return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ uint32_t clock_rate = MT9V111_MCLK;
+
+ pr_debug("In mt9v111:ioctl_dev_init\n");
+
+ gpio_sensor_active();
+
+ set_mclk_rate(&clock_rate);
+ mt9v111_rate_cal(&reset_frame_rate, clock_rate);
+ mt9v111_sensor_lib(mt9v111_device.coreReg, mt9v111_device.ifpReg);
+
+ return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc mt9v111_ioctl_desc[] = {
+
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+
+ /*!
+ * Delinitialise the dev. at slave detach.
+ * The complement of ioctl_dev_init.
+ */
+/* {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *) ioctl_dev_exit}, */
+
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func *) ioctl_s_power},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *) ioctl_g_ifparm},
+/* {vidioc_int_g_needs_reset_num,
+ (v4l2_int_ioctl_func *) ioctl_g_needs_reset}, */
+/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *) ioctl_reset}, */
+ {vidioc_int_init_num, (v4l2_int_ioctl_func *) ioctl_init},
+
+ /*!
+ * VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+ */
+/* {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, */
+
+ /*!
+ * VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.
+ * This ioctl is used to negotiate the image capture size and
+ * pixel format without actually making it take effect.
+ */
+/* {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, */
+
+ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_g_fmt_cap},
+
+ /*!
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm},
+/* {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *) ioctl_queryctrl}, */
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl},
+};
+
+static struct v4l2_int_slave mt9v111_slave = {
+ .ioctls = mt9v111_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(mt9v111_ioctl_desc),
+};
+
+static struct v4l2_int_device mt9v111_int_device = {
+ .module = THIS_MODULE,
+ .name = "mt9v111",
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &mt9v111_slave,
+ },
+};
+
+/*!
+ * mt9v111 I2C probe function
+ * Function set in i2c_driver struct.
+ * Called by insmod mt9v111_camera.ko.
+ *
+ * @return Error code indicating success or failure
+ */
+static int mt9v111_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int retval;
+
+ pr_debug("In mt9v111_probe device id is %s\n", id->name);
+
+ /* Set initial values for the sensor struct. */
+ memset(&mt9v111_data, 0, sizeof(mt9v111_data));
+ mt9v111_data.i2c_client = client;
+ pr_debug(" client name is %s\n", client->name);
+ mt9v111_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ mt9v111_data.pix.width = MT9V111_MAX_WIDTH;
+ mt9v111_data.pix.height = MT9V111_MAX_HEIGHT;
+ mt9v111_data.streamcap.capability = 0; /* No higher resolution or frame
+ * frame rate changes supported.
+ */
+ mt9v111_data.streamcap.timeperframe.denominator = MT9V111_FRAME_RATE;
+ mt9v111_data.streamcap.timeperframe.numerator = 1;
+
+ mt9v111_int_device.priv = &mt9v111_data;
+
+ pr_debug(" type is %d (expect %d)\n",
+ mt9v111_int_device.type, v4l2_int_type_slave);
+ pr_debug(" num ioctls is %d\n",
+ mt9v111_int_device.u.slave->num_ioctls);
+
+ /* This function attaches this structure to the /dev/video0 device.
+ * The pointer in priv points to the mt9v111_data structure here.*/
+ retval = v4l2_int_device_register(&mt9v111_int_device);
+
+ return retval;
+}
+
+/*!
+ * Function set in i2c_driver struct.
+ * Called on rmmod mt9v111_camera.ko
+ */
+static int mt9v111_remove(struct i2c_client *client)
+{
+ pr_debug("In mt9v111_remove\n");
+
+ v4l2_int_device_unregister(&mt9v111_int_device);
+ return 0;
+}
+
+/*!
+ * MT9V111 init function.
+ * Called by insmod mt9v111_camera.ko.
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int mt9v111_init(void)
+{
+ u8 err;
+
+ pr_debug("In mt9v111_init\n");
+
+ /* Allocate memory for state structures. */
+ mt9v111_device.coreReg = (mt9v111_coreReg *)
+ kmalloc(sizeof(mt9v111_coreReg), GFP_KERNEL);
+ if (!mt9v111_device.coreReg)
+ return -1;
+ memset(mt9v111_device.coreReg, 0, sizeof(mt9v111_coreReg));
+
+ mt9v111_device.ifpReg = (mt9v111_IFPReg *)
+ kmalloc(sizeof(mt9v111_IFPReg), GFP_KERNEL);
+ if (!mt9v111_device.ifpReg) {
+ kfree(mt9v111_device.coreReg);
+ mt9v111_device.coreReg = NULL;
+ return -1;
+ }
+ memset(mt9v111_device.ifpReg, 0, sizeof(mt9v111_IFPReg));
+
+ /* Set contents of the just created structures. */
+ mt9v111_config();
+
+ /* Tells the i2c driver what functions to call for this driver. */
+ err = i2c_add_driver(&mt9v111_i2c_driver);
+ if (err != 0)
+ pr_err("%s:driver registration failed, error=%d \n",
+ __func__, err);
+
+ return err;
+}
+
+/*!
+ * MT9V111 cleanup function.
+ * Called on rmmod mt9v111_camera.ko
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit mt9v111_clean(void)
+{
+ pr_debug("In mt9v111_clean()\n");
+
+ i2c_del_driver(&mt9v111_i2c_driver);
+ gpio_sensor_inactive();
+
+ if (mt9v111_device.coreReg) {
+ kfree(mt9v111_device.coreReg);
+ mt9v111_device.coreReg = NULL;
+ }
+
+ if (mt9v111_device.ifpReg) {
+ kfree(mt9v111_device.ifpReg);
+ mt9v111_device.ifpReg = NULL;
+ }
+}
+
+module_init(mt9v111_init);
+module_exit(mt9v111_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Mt9v111 Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/mt9v111.h b/drivers/media/video/mxc/capture/mt9v111.h
new file mode 100644
index 000000000000..cf38cec4757c
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mt9v111.h
@@ -0,0 +1,431 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup Camera Sensor Drivers
+ */
+
+/*!
+ * @file mt9v111.h
+ *
+ * @brief MT9V111 Camera Header file
+ *
+ * This header file contains defines and structures for the iMagic mi8012
+ * aka the Micron mt9v111 camera.
+ *
+ * @ingroup Camera
+ */
+
+#ifndef MT9V111_H_
+#define MT9V111_H_
+
+/*!
+ * Basic camera values
+ */
+#define MT9V111_FRAME_RATE 30
+#define MT9V111_MCLK 27000000 /* Desired clock rate */
+#define MT9V111_CLK_MIN 12000000 /* This clock rate yields 15 fps */
+#define MT9V111_CLK_MAX 27000000
+#define MT9V111_MAX_WIDTH 640 /* Max width for this camera */
+#define MT9V111_MAX_HEIGHT 480 /* Max height for this camera */
+
+/*!
+ * mt9v111 IFP REGISTER BANK MAP
+ */
+#define MT9V111I_ADDR_SPACE_SEL 0x1
+#define MT9V111I_BASE_MAXTRIX_SIGN 0x2
+#define MT9V111I_BASE_MAXTRIX_SCALE15 0x3
+#define MT9V111I_BASE_MAXTRIX_SCALE69 0x4
+#define MT9V111I_APERTURE_GAIN 0x5
+#define MT9V111I_MODE_CONTROL 0x6
+#define MT9V111I_SOFT_RESET 0x7
+#define MT9V111I_FORMAT_CONTROL 0x8
+#define MT9V111I_BASE_MATRIX_CFK1 0x9
+#define MT9V111I_BASE_MATRIX_CFK2 0xa
+#define MT9V111I_BASE_MATRIX_CFK3 0xb
+#define MT9V111I_BASE_MATRIX_CFK4 0xc
+#define MT9V111I_BASE_MATRIX_CFK5 0xd
+#define MT9V111I_BASE_MATRIX_CFK6 0xe
+#define MT9V111I_BASE_MATRIX_CFK7 0xf
+#define MT9V111I_BASE_MATRIX_CFK8 0x10
+#define MT9V111I_BASE_MATRIX_CFK9 0x11
+#define MT9V111I_AWB_POSITION 0x12
+#define MT9V111I_AWB_RED_GAIN 0x13
+#define MT9V111I_AWB_BLUE_GAIN 0x14
+#define MT9V111I_DELTA_MATRIX_CF_SIGN 0x15
+#define MT9V111I_DELTA_MATRIX_CF_D1 0x16
+#define MT9V111I_DELTA_MATRIX_CF_D2 0x17
+#define MT9V111I_DELTA_MATRIX_CF_D3 0x18
+#define MT9V111I_DELTA_MATRIX_CF_D4 0x19
+#define MT9V111I_DELTA_MATRIX_CF_D5 0x1a
+#define MT9V111I_DELTA_MATRIX_CF_D6 0x1b
+#define MT9V111I_DELTA_MATRIX_CF_D7 0x1c
+#define MT9V111I_DELTA_MATRIX_CF_D8 0x1d
+#define MT9V111I_DELTA_MATRIX_CF_D9 0x1e
+#define MT9V111I_LUMINANCE_LIMIT_WB 0x20
+#define MT9V111I_RBG_MANUUAL_WB 0x21
+#define MT9V111I_AWB_RED_LIMIT 0x22
+#define MT9V111I_AWB_BLUE_LIMIT 0x23
+#define MT9V111I_MATRIX_ADJUST_LIMIT 0x24
+#define MT9V111I_AWB_SPEED 0x25
+#define MT9V111I_H_BOUND_AE 0x26
+#define MT9V111I_V_BOUND_AE 0x27
+#define MT9V111I_H_BOUND_AE_CEN_WIN 0x2b
+#define MT9V111I_V_BOUND_AE_CEN_WIN 0x2c
+#define MT9V111I_BOUND_AWB_WIN 0x2d
+#define MT9V111I_AE_PRECISION_TARGET 0x2e
+#define MT9V111I_AE_SPEED 0x2f
+#define MT9V111I_RED_AWB_MEASURE 0x30
+#define MT9V111I_LUMA_AWB_MEASURE 0x31
+#define MT9V111I_BLUE_AWB_MEASURE 0x32
+#define MT9V111I_LIMIT_SHARP_SATU_CTRL 0x33
+#define MT9V111I_LUMA_OFFSET 0x34
+#define MT9V111I_CLIP_LIMIT_OUTPUT_LUMI 0x35
+#define MT9V111I_GAIN_LIMIT_AE 0x36
+#define MT9V111I_SHUTTER_WIDTH_LIMIT_AE 0x37
+#define MT9V111I_UPPER_SHUTTER_DELAY_LIM 0x39
+#define MT9V111I_OUTPUT_FORMAT_CTRL2 0x3a
+#define MT9V111I_IPF_BLACK_LEVEL_SUB 0x3b
+#define MT9V111I_IPF_BLACK_LEVEL_ADD 0x3c
+#define MT9V111I_ADC_LIMIT_AE_ADJ 0x3d
+#define MT9V111I_GAIN_THRE_CCAM_ADJ 0x3e
+#define MT9V111I_LINEAR_AE 0x3f
+#define MT9V111I_THRESHOLD_EDGE_DEFECT 0x47
+#define MT9V111I_LUMA_SUM_MEASURE 0x4c
+#define MT9V111I_TIME_ADV_SUM_LUMA 0x4d
+#define MT9V111I_MOTION 0x52
+#define MT9V111I_GAMMA_KNEE_Y12 0x53
+#define MT9V111I_GAMMA_KNEE_Y34 0x54
+#define MT9V111I_GAMMA_KNEE_Y56 0x55
+#define MT9V111I_GAMMA_KNEE_Y78 0x56
+#define MT9V111I_GAMMA_KNEE_Y90 0x57
+#define MT9V111I_GAMMA_VALUE_Y0 0x58
+#define MT9V111I_SHUTTER_60 0x59
+#define MT9V111I_SEARCH_FLICK_60 0x5c
+#define MT9V111I_RATIO_IMAGE_GAIN_BASE 0x5e
+#define MT9V111I_RATIO_IMAGE_GAIN_DELTA 0x5f
+#define MT9V111I_SIGN_VALUE_REG5F 0x60
+#define MT9V111I_AE_GAIN 0x62
+#define MT9V111I_MAX_GAIN_AE 0x67
+#define MT9V111I_LENS_CORRECT_CTRL 0x80
+#define MT9V111I_SHADING_PARAMETER1 0x81
+#define MT9V111I_SHADING_PARAMETER2 0x82
+#define MT9V111I_SHADING_PARAMETER3 0x83
+#define MT9V111I_SHADING_PARAMETER4 0x84
+#define MT9V111I_SHADING_PARAMETER5 0x85
+#define MT9V111I_SHADING_PARAMETER6 0x86
+#define MT9V111I_SHADING_PARAMETER7 0x87
+#define MT9V111I_SHADING_PARAMETER8 0x88
+#define MT9V111I_SHADING_PARAMETER9 0x89
+#define MT9V111I_SHADING_PARAMETER10 0x8A
+#define MT9V111I_SHADING_PARAMETER11 0x8B
+#define MT9V111I_SHADING_PARAMETER12 0x8C
+#define MT9V111I_SHADING_PARAMETER13 0x8D
+#define MT9V111I_SHADING_PARAMETER14 0x8E
+#define MT9V111I_SHADING_PARAMETER15 0x8F
+#define MT9V111I_SHADING_PARAMETER16 0x90
+#define MT9V111I_SHADING_PARAMETER17 0x91
+#define MT9V111I_SHADING_PARAMETER18 0x92
+#define MT9V111I_SHADING_PARAMETER19 0x93
+#define MT9V111I_SHADING_PARAMETER20 0x94
+#define MT9V111I_SHADING_PARAMETER21 0x95
+#define MT9V111i_FLASH_CTRL 0x98
+#define MT9V111i_LINE_COUNTER 0x99
+#define MT9V111i_FRAME_COUNTER 0x9A
+#define MT9V111i_H_PAN 0xA5
+#define MT9V111i_H_ZOOM 0xA6
+#define MT9V111i_H_SIZE 0xA7
+#define MT9V111i_V_PAN 0xA8
+#define MT9V111i_V_ZOOM 0xA9
+#define MT9V111i_V_SIZE 0xAA
+
+#define MT9V111I_SEL_IFP 0x1
+#define MT9V111I_SEL_SCA 0x4
+#define MT9V111I_FC_RGB_OR_YUV 0x1000
+
+/*!
+ * Mt9v111 SENSOR CORE REGISTER BANK MAP
+ */
+#define MT9V111S_ADDR_SPACE_SEL 0x1
+#define MT9V111S_COLUMN_START 0x2
+#define MT9V111S_WIN_HEIGHT 0x3
+#define MT9V111S_WIN_WIDTH 0x4
+#define MT9V111S_HOR_BLANKING 0x5
+#define MT9V111S_VER_BLANKING 0x6
+#define MT9V111S_OUTPUT_CTRL 0x7
+#define MT9V111S_ROW_START 0x8
+#define MT9V111S_SHUTTER_WIDTH 0x9
+#define MT9V111S_PIXEL_CLOCK_SPEED 0xa
+#define MT9V111S_RESTART 0xb
+#define MT9V111S_SHUTTER_DELAY 0xc
+#define MT9V111S_RESET 0xd
+#define MT9V111S_COLUMN_START_IN_ZOOM 0x12
+#define MT9V111S_ROW_START_IN_ZOOM 0x13
+#define MT9V111S_DIGITAL_ZOOM 0x1e
+#define MT9V111S_READ_MODE 0x20
+#define MT9V111S_DAC_CTRL 0x27
+#define MT9V111S_GREEN1_GAIN 0x2b
+#define MT9V111S_BLUE_GAIN 0x2c
+#define MT9V111S_READ_GAIN 0x2d
+#define MT9V111S_GREEN2_GAIN 0x2e
+#define MT9V111S_ROW_NOISE_CTRL 0x30
+#define MT9V111S_DARK_TARGET_W 0x31
+#define MT9V111S_TEST_DATA 0x32
+#define MT9V111S_GLOBAL_GAIN 0x35
+#define MT9V111S_SENSOR_CORE_VERSION 0x36
+#define MT9V111S_DARK_TARGET_WO 0x37
+#define MT9V111S_VERF_DAC 0x41
+#define MT9V111S_VCM_VCL 0x42
+#define MT9V111S_DISABLE_BYPASS 0x58
+#define MT9V111S_CALIB_MEAN_TEST 0x59
+#define MT9V111S_DARK_G1_AVE 0x5B
+#define MT9V111S_DARK_G2_AVE 0x5C
+#define MT9V111S_DARK_R_AVE 0x5D
+#define MT9V111S_DARK_B_AVE 0x5E
+#define MT9V111S_CAL_THRESHOLD 0x5f
+#define MT9V111S_CAL_G1 0x60
+#define MT9V111S_CAL_G2 0x61
+#define MT9V111S_CAL_CTRL 0x62
+#define MT9V111S_CAL_R 0x63
+#define MT9V111S_CAL_B 0x64
+#define MT9V111S_CHIP_ENABLE 0xF1
+#define MT9V111S_CHIP_VERSION 0xFF
+
+/* OUTPUT_CTRL */
+#define MT9V111S_OUTCTRL_SYNC 0x1
+#define MT9V111S_OUTCTRL_CHIP_ENABLE 0x2
+#define MT9V111S_OUTCTRL_TEST_MODE 0x40
+
+/* READ_MODE */
+#define MT9V111S_RM_NOBADFRAME 0x1
+#define MT9V111S_RM_NODESTRUCT 0x2
+#define MT9V111S_RM_COLUMNSKIP 0x4
+#define MT9V111S_RM_ROWSKIP 0x8
+#define MT9V111S_RM_BOOSTEDRESET 0x1000
+#define MT9V111S_RM_COLUMN_LATE 0x10
+#define MT9V111S_RM_ROW_LATE 0x80
+#define MT9V111S_RM_RIGTH_TO_LEFT 0x4000
+#define MT9V111S_RM_BOTTOM_TO_TOP 0x8000
+
+/*! I2C Slave Address */
+#define MT9V111_I2C_ADDRESS 0x48
+
+/*!
+ * The image resolution enum for the mt9v111 sensor
+ */
+typedef enum {
+ MT9V111_OutputResolution_VGA = 0, /*!< VGA size */
+ MT9V111_OutputResolution_QVGA, /*!< QVGA size */
+ MT9V111_OutputResolution_CIF, /*!< CIF size */
+ MT9V111_OutputResolution_QCIF, /*!< QCIF size */
+ MT9V111_OutputResolution_QQVGA, /*!< QQVGA size */
+ MT9V111_OutputResolution_SXGA /*!< SXGA size */
+} MT9V111_OutputResolution;
+
+enum {
+ MT9V111_WINWIDTH = 0x287,
+ MT9V111_WINWIDTH_DEFAULT = 0x287,
+ MT9V111_WINWIDTH_MIN = 0x9,
+
+ MT9V111_WINHEIGHT = 0x1E7,
+ MT9V111_WINHEIGHT_DEFAULT = 0x1E7,
+
+ MT9V111_HORZBLANK_DEFAULT = 0x26,
+ MT9V111_HORZBLANK_MIN = 0x9,
+ MT9V111_HORZBLANK_MAX = 0x3FF,
+
+ MT9V111_VERTBLANK_DEFAULT = 0x4,
+ MT9V111_VERTBLANK_MIN = 0x3,
+ MT9V111_VERTBLANK_MAX = 0xFFF,
+};
+
+/*!
+ * Mt9v111 Core Register structure.
+ */
+typedef struct {
+ u32 addressSelect; /*!< select address bank for Core Register 0x4 */
+ u32 columnStart; /*!< Starting Column */
+ u32 windowHeight; /*!< Window Height */
+ u32 windowWidth; /*!< Window Width */
+ u32 horizontalBlanking; /*!< Horizontal Blank time, in pixels */
+ u32 verticalBlanking; /*!< Vertical Blank time, in pixels */
+ u32 outputControl; /*!< Register to control sensor output */
+ u32 rowStart; /*!< Starting Row */
+ u32 shutterWidth;
+ u32 pixelClockSpeed; /*!< pixel date rate */
+ u32 restart; /*!< Abandon the readout of current frame */
+ u32 shutterDelay;
+ u32 reset; /*!< reset the sensor to the default mode */
+ u32 zoomColStart; /*!< Column start in the Zoom mode */
+ u32 zomRowStart; /*!< Row start in the Zoom mode */
+ u32 digitalZoom; /*!< 1 means zoom by 2 */
+ u32 readMode; /*!< Readmode: aspects of the readout of the sensor */
+ u32 dACStandbyControl;
+ u32 green1Gain; /*!< Gain Settings */
+ u32 blueGain;
+ u32 redGain;
+ u32 green2Gain;
+ u32 rowNoiseControl;
+ u32 darkTargetwNC;
+ u32 testData; /*!< test mode */
+ u32 globalGain;
+ u32 chipVersion;
+ u32 darkTargetwoNC;
+ u32 vREFDACs;
+ u32 vCMandVCL;
+ u32 disableBypass;
+ u32 calibMeanTest;
+ u32 darkG1average;
+ u32 darkG2average;
+ u32 darkRaverage;
+ u32 darkBaverage;
+ u32 calibThreshold;
+ u32 calibGreen1;
+ u32 calibGreen2;
+ u32 calibControl;
+ u32 calibRed;
+ u32 calibBlue;
+ u32 chipEnable; /*!< Image core Registers written by image flow processor */
+} mt9v111_coreReg;
+
+/*!
+ * Mt9v111 IFP Register structure.
+ */
+typedef struct {
+ u32 addrSpaceSel; /*!< select address bank for Core Register 0x1 */
+ u32 baseMaxtrixSign; /*!< sign of coefficient for base color correction matrix */
+ u32 baseMaxtrixScale15; /*!< scaling of color correction coefficient K1-5 */
+ u32 baseMaxtrixScale69; /*!< scaling of color correction coefficient K6-9 */
+ u32 apertureGain; /*!< sharpening */
+ u32 modeControl; /*!< bit 7 CCIR656 sync codes are embedded in the image */
+ u32 softReset; /*!< Image processing mode: 1 reset mode, 0 operational mode */
+ u32 formatControl; /*!< bit12 1 for RGB565, 0 for YcrCb */
+ u32 baseMatrixCfk1; /*!< K1 Color correction coefficient */
+ u32 baseMatrixCfk2; /*!< K2 Color correction coefficient */
+ u32 baseMatrixCfk3; /*!< K3 Color correction coefficient */
+ u32 baseMatrixCfk4; /*!< K4 Color correction coefficient */
+ u32 baseMatrixCfk5; /*!< K5 Color correction coefficient */
+ u32 baseMatrixCfk6; /*!< K6 Color correction coefficient */
+ u32 baseMatrixCfk7; /*!< K7 Color correction coefficient */
+ u32 baseMatrixCfk8; /*!< K8 Color correction coefficient */
+ u32 baseMatrixCfk9; /*!< K9 Color correction coefficient */
+ u32 awbPosition; /*!< Current position of AWB color correction matrix */
+ u32 awbRedGain; /*!< Current value of AWB red channel gain */
+ u32 awbBlueGain; /*!< Current value of AWB blue channel gain */
+ u32 deltaMatrixCFSign; /*!< Sign of coefficients of delta color correction matrix register */
+ u32 deltaMatrixCFD1; /*!< D1 Delta coefficient */
+ u32 deltaMatrixCFD2; /*!< D2 Delta coefficient */
+ u32 deltaMatrixCFD3; /*!< D3 Delta coefficient */
+ u32 deltaMatrixCFD4; /*!< D4 Delta coefficient */
+ u32 deltaMatrixCFD5; /*!< D5 Delta coefficient */
+ u32 deltaMatrixCFD6; /*!< D6 Delta coefficient */
+ u32 deltaMatrixCFD7; /*!< D7 Delta coefficient */
+ u32 deltaMatrixCFD8; /*!< D8 Delta coefficient */
+ u32 deltaMatrixCFD9; /*!< D9 Delta coefficient */
+ u32 lumLimitWB; /*!< Luminance range of pixels considered in WB statistics */
+ u32 RBGManualWB; /*!< Red and Blue color channel gains for manual white balance */
+ u32 awbRedLimit; /*!< Limits on Red channel gain adjustment through AWB */
+ u32 awbBlueLimit; /*!< Limits on Blue channel gain adjustment through AWB */
+ u32 matrixAdjLimit; /*!< Limits on color correction matrix adjustment through AWB */
+ u32 awbSpeed; /*!< AWB speed and color saturation control */
+ u32 HBoundAE; /*!< Horizontal boundaries of AWB measurement window */
+ u32 VBoundAE; /*!< Vertical boundaries of AWB measurement window */
+ u32 HBoundAECenWin; /*!< Horizontal boundaries of AE measurement window for backlight compensation */
+ u32 VBoundAECenWin; /*!< Vertical boundaries of AE measurement window for backlight compensation */
+ u32 boundAwbWin; /*!< Boundaries of AWB measurement window */
+ u32 AEPrecisionTarget; /*!< Auto exposure target and precision control */
+ u32 AESpeed; /*!< AE speed and sensitivity control register */
+ u32 redAWBMeasure; /*!< Measure of the red channel value used by AWB */
+ u32 lumaAWBMeasure; /*!< Measure of the luminance channel value used by AWB */
+ u32 blueAWBMeasure; /*!< Measure of the blue channel value used by AWB */
+ u32 limitSharpSatuCtrl; /*!< Automatic control of sharpness and color saturation */
+ u32 lumaOffset; /*!< Luminance offset control (brightness control) */
+ u32 clipLimitOutputLumi; /*!< Clipping limits for output luminance */
+ u32 gainLimitAE; /*!< Imager gain limits for AE adjustment */
+ u32 shutterWidthLimitAE; /*!< Shutter width (exposure time) limits for AE adjustment */
+ u32 upperShutterDelayLi; /*!< Upper Shutter Delay Limit */
+ u32 outputFormatCtrl2; /*!< Output Format Control 2
+ 00 = 16-bit RGB565.
+ 01 = 15-bit RGB555.
+ 10 = 12-bit RGB444x.
+ 11 = 12-bit RGBx444. */
+ u32 ipfBlackLevelSub; /*!< IFP black level subtraction */
+ u32 ipfBlackLevelAdd; /*!< IFP black level addition */
+ u32 adcLimitAEAdj; /*!< ADC limits for AE adjustment */
+ u32 agimnThreCamAdj; /*!< Gain threshold for CCM adjustment */
+ u32 linearAE;
+ u32 thresholdEdgeDefect; /*!< Edge threshold for interpolation and defect correction */
+ u32 lumaSumMeasure; /*!< Luma measured by AE engine */
+ u32 timeAdvSumLuma; /*!< Time-averaged luminance value tracked by auto exposure */
+ u32 motion; /*!< 1 when motion is detected */
+ u32 gammaKneeY12; /*!< Gamma knee points Y1 and Y2 */
+ u32 gammaKneeY34; /*!< Gamma knee points Y3 and Y4 */
+ u32 gammaKneeY56; /*!< Gamma knee points Y5 and Y6 */
+ u32 gammaKneeY78; /*!< Gamma knee points Y7 and Y8 */
+ u32 gammaKneeY90; /*!< Gamma knee points Y9 and Y10 */
+ u32 gammaKneeY0; /*!< Gamma knee point Y0 */
+ u32 shutter_width_60;
+ u32 search_flicker_60;
+ u32 ratioImageGainBase;
+ u32 ratioImageGainDelta;
+ u32 signValueReg5F;
+ u32 aeGain;
+ u32 maxGainAE;
+ u32 lensCorrectCtrl;
+ u32 shadingParameter1; /*!< Shade Parameters */
+ u32 shadingParameter2;
+ u32 shadingParameter3;
+ u32 shadingParameter4;
+ u32 shadingParameter5;
+ u32 shadingParameter6;
+ u32 shadingParameter7;
+ u32 shadingParameter8;
+ u32 shadingParameter9;
+ u32 shadingParameter10;
+ u32 shadingParameter11;
+ u32 shadingParameter12;
+ u32 shadingParameter13;
+ u32 shadingParameter14;
+ u32 shadingParameter15;
+ u32 shadingParameter16;
+ u32 shadingParameter17;
+ u32 shadingParameter18;
+ u32 shadingParameter19;
+ u32 shadingParameter20;
+ u32 shadingParameter21;
+ u32 flashCtrl; /*!< Flash control */
+ u32 lineCounter; /*!< Line counter */
+ u32 frameCounter; /*!< Frame counter */
+ u32 HPan; /*!< Horizontal pan in decimation */
+ u32 HZoom; /*!< Horizontal zoom in decimation */
+ u32 HSize; /*!< Horizontal output size iIn decimation */
+ u32 VPan; /*!< Vertical pan in decimation */
+ u32 VZoom; /*!< Vertical zoom in decimation */
+ u32 VSize; /*!< Vertical output size in decimation */
+} mt9v111_IFPReg;
+
+/*!
+ * mt9v111 Config structure
+ */
+typedef struct {
+ mt9v111_coreReg *coreReg; /*!< Sensor Core Register Bank */
+ mt9v111_IFPReg *ifpReg; /*!< IFP Register Bank */
+} mt9v111_conf;
+
+typedef struct {
+ u8 index;
+ u16 width;
+ u16 height;
+} mt9v111_image_format;
+
+#endif /* MT9V111_H_ */
diff --git a/drivers/media/video/mxc/capture/mx27_csi.c b/drivers/media/video/mxc/capture/mx27_csi.c
new file mode 100644
index 000000000000..24fce05be110
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_csi.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx27_csi.c
+ *
+ * @brief CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <mach/clock.h>
+#include <mach/hardware.h>
+
+#include "mx27_csi.h"
+
+static csi_config_t g_csi_cfg; /* csi hardware configuration */
+static bool gcsi_mclk_on = false;
+static csi_irq_callback_t g_callback = 0;
+static void *g_callback_data = 0;
+static struct clk csi_mclk;
+
+static irqreturn_t csi_irq_handler(int irq, void *data)
+{
+ unsigned long status = __raw_readl(CSI_CSISR);
+
+ __raw_writel(status, CSI_CSISR);
+ if (g_callback)
+ g_callback(g_callback_data, status);
+
+ pr_debug("CSI status = 0x%08lX\n", status);
+
+ return IRQ_HANDLED;
+}
+
+static void csihw_set_config(csi_config_t * cfg)
+{
+ unsigned val = 0;
+
+ /* control reg 1 */
+ val |= cfg->swap16_en ? BIT_SWAP16_EN : 0;
+ val |= cfg->ext_vsync ? BIT_EXT_VSYNC : 0;
+ val |= cfg->eof_int_en ? BIT_EOF_INT_EN : 0;
+ val |= cfg->prp_if_en ? BIT_PRP_IF_EN : 0;
+ val |= cfg->ccir_mode ? BIT_CCIR_MODE : 0;
+ val |= cfg->cof_int_en ? BIT_COF_INT_EN : 0;
+ val |= cfg->sf_or_inten ? BIT_SF_OR_INTEN : 0;
+ val |= cfg->rf_or_inten ? BIT_RF_OR_INTEN : 0;
+ val |= cfg->statff_level << SHIFT_STATFF_LEVEL;
+ val |= cfg->staff_inten ? BIT_STATFF_INTEN : 0;
+ val |= cfg->rxff_level << SHIFT_RXFF_LEVEL;
+ val |= cfg->rxff_inten ? BIT_RXFF_INTEN : 0;
+ val |= cfg->sof_pol ? BIT_SOF_POL : 0;
+ val |= cfg->sof_inten ? BIT_SOF_INTEN : 0;
+ val |= cfg->mclkdiv << SHIFT_MCLKDIV;
+ val |= cfg->hsync_pol ? BIT_HSYNC_POL : 0;
+ val |= cfg->ccir_en ? BIT_CCIR_EN : 0;
+ val |= cfg->mclken ? BIT_MCLKEN : 0;
+ val |= cfg->fcc ? BIT_FCC : 0;
+ val |= cfg->pack_dir ? BIT_PACK_DIR : 0;
+ val |= cfg->gclk_mode ? BIT_GCLK_MODE : 0;
+ val |= cfg->inv_data ? BIT_INV_DATA : 0;
+ val |= cfg->inv_pclk ? BIT_INV_PCLK : 0;
+ val |= cfg->redge ? BIT_REDGE : 0;
+
+ __raw_writel(val, CSI_CSICR1);
+
+ /* control reg 3 */
+ val = 0x0;
+ val |= cfg->csi_sup ? BIT_CSI_SUP : 0;
+ val |= cfg->zero_pack_en ? BIT_ZERO_PACK_EN : 0;
+ val |= cfg->ecc_int_en ? BIT_ECC_INT_EN : 0;
+ val |= cfg->ecc_auto_en ? BIT_ECC_AUTO_EN : 0;
+
+ __raw_writel(val, CSI_CSICR3);
+
+ /* rxfifo counter */
+ __raw_writel(cfg->rxcnt, CSI_CSIRXCNT);
+
+ /* update global config */
+ memcpy(&g_csi_cfg, cfg, sizeof(csi_config_t));
+}
+
+static void csihw_reset_frame_count(void)
+{
+ __raw_writel(__raw_readl(CSI_CSICR3) | BIT_FRMCNT_RST, CSI_CSICR3);
+}
+
+static void csihw_reset(void)
+{
+ csihw_reset_frame_count();
+ __raw_writel(CSICR1_RESET_VAL, CSI_CSICR1);
+ __raw_writel(CSICR2_RESET_VAL, CSI_CSICR2);
+ __raw_writel(CSICR3_RESET_VAL, CSI_CSICR3);
+}
+
+/*!
+ * csi_init_interface
+ * Sets initial values for the CSI registers.
+ * The width and height of the sensor and the actual frame size will be
+ * set to the same values.
+ * @param width Sensor width
+ * @param height Sensor height
+ * @param pixel_fmt pixel format
+ * @param sig csi_signal_cfg_t
+ *
+ * @return 0 for success, -EINVAL for error
+ */
+int32_t csi_init_interface(uint16_t width, uint16_t height,
+ uint32_t pixel_fmt, csi_signal_cfg_t sig)
+{
+ csi_config_t cfg;
+
+ /* Set the CSI_SENS_CONF register remaining fields */
+ cfg.swap16_en = 1;
+ cfg.ext_vsync = sig.ext_vsync;
+ cfg.eof_int_en = 0;
+ cfg.prp_if_en = 1;
+ cfg.ccir_mode = 0;
+ cfg.cof_int_en = 0;
+ cfg.sf_or_inten = 0;
+ cfg.rf_or_inten = 0;
+ cfg.statff_level = 0;
+ cfg.staff_inten = 0;
+ cfg.rxff_level = 2;
+ cfg.rxff_inten = 0;
+ cfg.sof_pol = 1;
+ cfg.sof_inten = 0;
+ cfg.mclkdiv = 0;
+ cfg.hsync_pol = 1;
+ cfg.ccir_en = 0;
+ cfg.mclken = gcsi_mclk_on ? 1 : 0;
+ cfg.fcc = 1;
+ cfg.pack_dir = 0;
+ cfg.gclk_mode = 1;
+ cfg.inv_data = sig.data_pol;
+ cfg.inv_pclk = sig.pixclk_pol;
+ cfg.redge = 1;
+ cfg.csicnt1_rsv = 0;
+
+ /* control reg 3 */
+ cfg.frmcnt = 0;
+ cfg.frame_reset = 0;
+ cfg.csi_sup = 0;
+ cfg.zero_pack_en = 0;
+ cfg.ecc_int_en = 0;
+ cfg.ecc_auto_en = 0;
+
+ csihw_set_config(&cfg);
+
+ return 0;
+}
+
+/*!
+ * csi_enable_prpif
+ * Enable or disable CSI-PrP interface
+ * @param enable Non-zero to enable, zero to disable
+ */
+void csi_enable_prpif(uint32_t enable)
+{
+ if (enable) {
+ g_csi_cfg.prp_if_en = 1;
+ g_csi_cfg.sof_inten = 0;
+ g_csi_cfg.pack_dir = 0;
+ } else {
+ g_csi_cfg.prp_if_en = 0;
+ g_csi_cfg.sof_inten = 1;
+ g_csi_cfg.pack_dir = 1;
+ }
+
+ csihw_set_config(&g_csi_cfg);
+}
+
+/*!
+ * csi_enable_mclk
+ *
+ * @param src enum define which source to control the clk
+ * CSI_MCLK_VF CSI_MCLK_ENC CSI_MCLK_RAW CSI_MCLK_I2C
+ * @param flag true to enable mclk, false to disable mclk
+ * @param wait true to wait 100ms make clock stable, false not wait
+ *
+ * @return 0 for success
+ */
+int32_t csi_enable_mclk(int src, bool flag, bool wait)
+{
+ if (flag == true) {
+ clk_enable(&csi_mclk);
+ if (wait == true)
+ msleep(10);
+ pr_debug("Enable csi clock from source %d\n", src);
+ gcsi_mclk_on = true;
+ } else {
+ clk_disable(&csi_mclk);
+ pr_debug("Disable csi clock from source %d\n", src);
+ gcsi_mclk_on = false;
+ }
+
+ return 0;
+}
+
+/*!
+ * csi_read_mclk_flag
+ *
+ * @return gcsi_mclk_source
+ */
+int csi_read_mclk_flag(void)
+{
+ return 0;
+}
+
+void csi_set_callback(csi_irq_callback_t callback, void *data)
+{
+ g_callback = callback;
+ g_callback_data = data;
+}
+
+static void _mclk_recalc(struct clk *clk)
+{
+ u32 div;
+
+ div = (__raw_readl(CSI_CSICR1) & BIT_MCLKDIV) >> SHIFT_MCLKDIV;
+ div = (div + 1) * 2;
+
+ clk->rate = clk->parent->rate / div;
+}
+
+static unsigned long _mclk_round_rate(struct clk *clk, unsigned long rate)
+{
+ /* Keep CSI divider and change parent clock */
+ if (clk->parent->round_rate) {
+ return clk->parent->round_rate(clk->parent, rate * 2);
+ }
+ return 0;
+}
+
+static int _mclk_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret = -EINVAL;
+
+ /* Keep CSI divider and change parent clock */
+ if (clk->parent->set_rate) {
+ ret = clk->parent->set_rate(clk->parent, rate * 2);
+ if (ret == 0) {
+ clk->rate = clk->parent->rate / 2;
+ }
+ }
+
+ return ret;
+}
+
+static int _mclk_enable(struct clk *clk)
+{
+ __raw_writel(__raw_readl(CSI_CSICR1) | BIT_MCLKEN, CSI_CSICR1);
+ return 0;
+}
+
+static void _mclk_disable(struct clk *clk)
+{
+ __raw_writel(__raw_readl(CSI_CSICR1) & ~BIT_MCLKEN, CSI_CSICR1);
+}
+
+static struct clk csi_mclk = {
+ .name = "csi_clk",
+ .recalc = _mclk_recalc,
+ .round_rate = _mclk_round_rate,
+ .set_rate = _mclk_set_rate,
+ .enable = _mclk_enable,
+ .disable = _mclk_disable,
+};
+
+int32_t __init csi_init_module(void)
+{
+ int ret = 0;
+ struct clk *per_clk;
+
+ per_clk = clk_get(NULL, "csi_perclk");
+ if (IS_ERR(per_clk))
+ return PTR_ERR(per_clk);
+ clk_put(per_clk);
+ csi_mclk.parent = per_clk;
+ clk_register(&csi_mclk);
+ clk_enable(per_clk);
+ csi_mclk.recalc(&csi_mclk);
+
+ csihw_reset();
+
+ /* interrupt enable */
+ ret = request_irq(MXC_INT_CSI, csi_irq_handler, 0, "csi", 0);
+ if (ret)
+ pr_debug("CSI error: irq request fail\n");
+
+ return ret;
+}
+
+void __exit csi_cleanup_module(void)
+{
+ /* free irq */
+ free_irq(MXC_INT_CSI, 0);
+
+ clk_disable(&csi_mclk);
+}
+
+module_init(csi_init_module);
+module_exit(csi_cleanup_module);
+
+EXPORT_SYMBOL(csi_init_interface);
+EXPORT_SYMBOL(csi_enable_mclk);
+EXPORT_SYMBOL(csi_read_mclk_flag);
+EXPORT_SYMBOL(csi_set_callback);
+EXPORT_SYMBOL(csi_enable_prpif);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MX27 CSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/mx27_csi.h b/drivers/media/video/mxc/capture/mx27_csi.h
new file mode 100644
index 000000000000..9bd99781e626
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_csi.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx27_csi.h
+ *
+ * @brief CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+
+#ifndef MX27_CSI_H
+#define MX27_CSI_H
+
+#include <linux/io.h>
+
+/* reset values */
+#define CSICR1_RESET_VAL 0x40000800
+#define CSICR2_RESET_VAL 0x0
+#define CSICR3_RESET_VAL 0x0
+
+/* csi control reg 1 */
+#define BIT_SWAP16_EN (0x1 << 31)
+#define BIT_EXT_VSYNC (0x1 << 30)
+#define BIT_EOF_INT_EN (0x1 << 29)
+#define BIT_PRP_IF_EN (0x1 << 28)
+#define BIT_CCIR_MODE (0x1 << 27)
+#define BIT_COF_INT_EN (0x1 << 26)
+#define BIT_SF_OR_INTEN (0x1 << 25)
+#define BIT_RF_OR_INTEN (0x1 << 24)
+#define BIT_STATFF_LEVEL (0x3 << 22)
+#define BIT_STATFF_INTEN (0x1 << 21)
+#define BIT_RXFF_LEVEL (0x3 << 19)
+#define BIT_RXFF_INTEN (0x1 << 18)
+#define BIT_SOF_POL (0x1 << 17)
+#define BIT_SOF_INTEN (0x1 << 16)
+#define BIT_MCLKDIV (0xF << 12)
+#define BIT_HSYNC_POL (0x1 << 11)
+#define BIT_CCIR_EN (0x1 << 10)
+#define BIT_MCLKEN (0x1 << 9)
+#define BIT_FCC (0x1 << 8)
+#define BIT_PACK_DIR (0x1 << 7)
+#define BIT_CLR_STATFIFO (0x1 << 6)
+#define BIT_CLR_RXFIFO (0x1 << 5)
+#define BIT_GCLK_MODE (0x1 << 4)
+#define BIT_INV_DATA (0x1 << 3)
+#define BIT_INV_PCLK (0x1 << 2)
+#define BIT_REDGE (0x1 << 1)
+
+#define SHIFT_STATFF_LEVEL 22
+#define SHIFT_RXFF_LEVEL 19
+#define SHIFT_MCLKDIV 12
+
+/* control reg 3 */
+#define BIT_FRMCNT (0xFFFF << 16)
+#define BIT_FRMCNT_RST (0x1 << 15)
+#define BIT_CSI_SUP (0x1 << 3)
+#define BIT_ZERO_PACK_EN (0x1 << 2)
+#define BIT_ECC_INT_EN (0x1 << 1)
+#define BIT_ECC_AUTO_EN (0x1)
+
+#define SHIFT_FRMCNT 16
+
+/* csi status reg */
+#define BIT_SFF_OR_INT (0x1 << 25)
+#define BIT_RFF_OR_INT (0x1 << 24)
+#define BIT_STATFF_INT (0x1 << 21)
+#define BIT_RXFF_INT (0x1 << 18)
+#define BIT_EOF_INT (0x1 << 17)
+#define BIT_SOF_INT (0x1 << 16)
+#define BIT_F2_INT (0x1 << 15)
+#define BIT_F1_INT (0x1 << 14)
+#define BIT_COF_INT (0x1 << 13)
+#define BIT_ECC_INT (0x1 << 1)
+#define BIT_DRDY (0x1 << 0)
+
+#define CSI_MCLK_VF 1
+#define CSI_MCLK_ENC 2
+#define CSI_MCLK_RAW 4
+#define CSI_MCLK_I2C 8
+
+#define CSI_CSICR1 (IO_ADDRESS(CSI_BASE_ADDR))
+#define CSI_CSICR2 (IO_ADDRESS(CSI_BASE_ADDR + 0x4))
+#define CSI_CSISR (IO_ADDRESS(CSI_BASE_ADDR + 0x8))
+#define CSI_STATFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0xC))
+#define CSI_CSIRXFIFO (IO_ADDRESS(CSI_BASE_ADDR + 0x10))
+#define CSI_CSIRXCNT (IO_ADDRESS(CSI_BASE_ADDR + 0x14))
+#define CSI_CSICR3 (IO_ADDRESS(CSI_BASE_ADDR + 0x1C))
+
+#define CSI_CSIRXFIFO_PHYADDR (CSI_BASE_ADDR + 0x10)
+
+static __inline void csi_clear_status(unsigned long status)
+{
+ __raw_writel(status, CSI_CSISR);
+}
+
+typedef struct {
+ unsigned data_width:3;
+ unsigned clk_mode:2;
+ unsigned ext_vsync:1;
+ unsigned Vsync_pol:1;
+ unsigned Hsync_pol:1;
+ unsigned pixclk_pol:1;
+ unsigned data_pol:1;
+ unsigned sens_clksrc:1;
+} csi_signal_cfg_t;
+
+typedef struct {
+ /* control reg 1 */
+ unsigned int swap16_en:1;
+ unsigned int ext_vsync:1;
+ unsigned int eof_int_en:1;
+ unsigned int prp_if_en:1;
+ unsigned int ccir_mode:1;
+ unsigned int cof_int_en:1;
+ unsigned int sf_or_inten:1;
+ unsigned int rf_or_inten:1;
+ unsigned int statff_level:2;
+ unsigned int staff_inten:1;
+ unsigned int rxff_level:2;
+ unsigned int rxff_inten:1;
+ unsigned int sof_pol:1;
+ unsigned int sof_inten:1;
+ unsigned int mclkdiv:4;
+ unsigned int hsync_pol:1;
+ unsigned int ccir_en:1;
+ unsigned int mclken:1;
+ unsigned int fcc:1;
+ unsigned int pack_dir:1;
+ unsigned int gclk_mode:1;
+ unsigned int inv_data:1;
+ unsigned int inv_pclk:1;
+ unsigned int redge:1;
+ unsigned int csicnt1_rsv:1;
+
+ /* control reg 3 */
+ unsigned int frmcnt:16;
+ unsigned int frame_reset:1;
+ unsigned int csi_sup:1;
+ unsigned int zero_pack_en:1;
+ unsigned int ecc_int_en:1;
+ unsigned int ecc_auto_en:1;
+
+ /* fifo counter */
+ unsigned int rxcnt;
+} csi_config_t;
+
+typedef void (*csi_irq_callback_t) (void *data, unsigned long status);
+
+int32_t csi_enable_mclk(int src, bool flag, bool wait);
+int32_t csi_init_interface(uint16_t width, uint16_t height,
+ uint32_t pixel_fmt, csi_signal_cfg_t sig);
+int csi_read_mclk_flag(void);
+void csi_set_callback(csi_irq_callback_t callback, void *data);
+void csi_enable_prpif(uint32_t enable);
+
+#endif
diff --git a/drivers/media/video/mxc/capture/mx27_prp.h b/drivers/media/video/mxc/capture/mx27_prp.h
new file mode 100644
index 000000000000..e32e9029daff
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_prp.h
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx27_prp.h
+ *
+ * @brief Header file for MX27 V4L2 capture driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#ifndef __MX27_PRP_H__
+#define __MX27_PRP_H__
+
+#define PRP_REG(ofs) (IO_ADDRESS(EMMA_BASE_ADDR) + ofs)
+
+/* Register definitions of PrP */
+#define PRP_CNTL PRP_REG(0x00)
+#define PRP_INTRCNTL PRP_REG(0x04)
+#define PRP_INTRSTATUS PRP_REG(0x08)
+#define PRP_SOURCE_Y_PTR PRP_REG(0x0C)
+#define PRP_SOURCE_CB_PTR PRP_REG(0x10)
+#define PRP_SOURCE_CR_PTR PRP_REG(0x14)
+#define PRP_DEST_RGB1_PTR PRP_REG(0x18)
+#define PRP_DEST_RGB2_PTR PRP_REG(0x1C)
+#define PRP_DEST_Y_PTR PRP_REG(0x20)
+#define PRP_DEST_CB_PTR PRP_REG(0x24)
+#define PRP_DEST_CR_PTR PRP_REG(0x28)
+#define PRP_SOURCE_FRAME_SIZE PRP_REG(0x2C)
+#define PRP_CH1_LINE_STRIDE PRP_REG(0x30)
+#define PRP_SRC_PIXEL_FORMAT_CNTL PRP_REG(0x34)
+#define PRP_CH1_PIXEL_FORMAT_CNTL PRP_REG(0x38)
+#define PRP_CH1_OUT_IMAGE_SIZE PRP_REG(0x3C)
+#define PRP_CH2_OUT_IMAGE_SIZE PRP_REG(0x40)
+#define PRP_SOURCE_LINE_STRIDE PRP_REG(0x44)
+#define PRP_CSC_COEF_012 PRP_REG(0x48)
+#define PRP_CSC_COEF_345 PRP_REG(0x4C)
+#define PRP_CSC_COEF_678 PRP_REG(0x50)
+#define PRP_CH1_RZ_HORI_COEF1 PRP_REG(0x54)
+#define PRP_CH1_RZ_HORI_COEF2 PRP_REG(0x58)
+#define PRP_CH1_RZ_HORI_VALID PRP_REG(0x5C)
+#define PRP_CH1_RZ_VERT_COEF1 PRP_REG(0x60)
+#define PRP_CH1_RZ_VERT_COEF2 PRP_REG(0x64)
+#define PRP_CH1_RZ_VERT_VALID PRP_REG(0x68)
+#define PRP_CH2_RZ_HORI_COEF1 PRP_REG(0x6C)
+#define PRP_CH2_RZ_HORI_COEF2 PRP_REG(0x70)
+#define PRP_CH2_RZ_HORI_VALID PRP_REG(0x74)
+#define PRP_CH2_RZ_VERT_COEF1 PRP_REG(0x78)
+#define PRP_CH2_RZ_VERT_COEF2 PRP_REG(0x7C)
+#define PRP_CH2_RZ_VERT_VALID PRP_REG(0x80)
+
+#define B_SET(b) (1 << (b))
+
+/* Bit definitions for PrP control register */
+#define PRP_CNTL_RSTVAL 0x28
+#define PRP_CNTL_CH1EN B_SET(0)
+#define PRP_CNTL_CH2EN B_SET(1)
+#define PRP_CNTL_CSI B_SET(2)
+#define PRP_CNTL_IN_32 B_SET(3)
+#define PRP_CNTL_IN_RGB B_SET(4)
+#define PRP_CNTL_IN_YUV420 0
+#define PRP_CNTL_IN_YUV422 PRP_CNTL_IN_32
+#define PRP_CNTL_IN_RGB16 PRP_CNTL_IN_RGB
+#define PRP_CNTL_IN_RGB32 (PRP_CNTL_IN_RGB | PRP_CNTL_IN_32)
+#define PRP_CNTL_CH1_RGB8 0
+#define PRP_CNTL_CH1_RGB16 B_SET(5)
+#define PRP_CNTL_CH1_RGB32 B_SET(6)
+#define PRP_CNTL_CH1_YUV422 (B_SET(5) | B_SET(6))
+#define PRP_CNTL_CH2_YUV420 0
+#define PRP_CNTL_CH2_YUV422 B_SET(7)
+#define PRP_CNTL_CH2_YUV444 B_SET(8)
+#define PRP_CNTL_CH1_LOOP B_SET(9)
+#define PRP_CNTL_CH2_LOOP B_SET(10)
+#define PRP_CNTL_AUTODROP B_SET(11)
+#define PRP_CNTL_RST B_SET(12)
+#define PRP_CNTL_CNTREN B_SET(13)
+#define PRP_CNTL_WINEN B_SET(14)
+#define PRP_CNTL_UNCHAIN B_SET(15)
+#define PRP_CNTL_IN_SKIP_NONE 0
+#define PRP_CNTL_IN_SKIP_1_2 B_SET(16)
+#define PRP_CNTL_IN_SKIP_1_3 B_SET(17)
+#define PRP_CNTL_IN_SKIP_2_3 (B_SET(16) | B_SET(17))
+#define PRP_CNTL_IN_SKIP_1_4 B_SET(18)
+#define PRP_CNTL_IN_SKIP_3_4 (B_SET(16) | B_SET(18))
+#define PRP_CNTL_IN_SKIP_2_5 (B_SET(17) | B_SET(18))
+#define PRP_CNTL_IN_SKIP_3_5 (B_SET(16) | B_SET(17) | B_SET(18))
+#define PRP_CNTL_CH1_SKIP_NONE 0
+#define PRP_CNTL_CH1_SKIP_1_2 B_SET(19)
+#define PRP_CNTL_CH1_SKIP_1_3 B_SET(20)
+#define PRP_CNTL_CH1_SKIP_2_3 (B_SET(19) | B_SET(20))
+#define PRP_CNTL_CH1_SKIP_1_4 B_SET(21)
+#define PRP_CNTL_CH1_SKIP_3_4 (B_SET(19) | B_SET(21))
+#define PRP_CNTL_CH1_SKIP_2_5 (B_SET(20) | B_SET(21))
+#define PRP_CNTL_CH1_SKIP_3_5 (B_SET(19) | B_SET(20) | B_SET(21))
+#define PRP_CNTL_CH2_SKIP_NONE 0
+#define PRP_CNTL_CH2_SKIP_1_2 B_SET(22)
+#define PRP_CNTL_CH2_SKIP_1_3 B_SET(23)
+#define PRP_CNTL_CH2_SKIP_2_3 (B_SET(22) | B_SET(23))
+#define PRP_CNTL_CH2_SKIP_1_4 B_SET(24)
+#define PRP_CNTL_CH2_SKIP_3_4 (B_SET(22) | B_SET(24))
+#define PRP_CNTL_CH2_SKIP_2_5 (B_SET(23) | B_SET(24))
+#define PRP_CNTL_CH2_SKIP_3_5 (B_SET(22) | B_SET(23) | B_SET(24))
+#define PRP_CNTL_FIFO_I128 0
+#define PRP_CNTL_FIFO_I96 B_SET(25)
+#define PRP_CNTL_FIFO_I64 B_SET(26)
+#define PRP_CNTL_FIFO_I32 (B_SET(25) | B_SET(26))
+#define PRP_CNTL_FIFO_O64 0
+#define PRP_CNTL_FIFO_O48 B_SET(27)
+#define PRP_CNTL_FIFO_O32 B_SET(28)
+#define PRP_CNTL_FIFO_O16 (B_SET(27) | B_SET(28))
+#define PRP_CNTL_CH2B1 B_SET(29)
+#define PRP_CNTL_CH2B2 B_SET(30)
+#define PRP_CNTL_CH2_FLOWEN B_SET(31)
+
+/* Bit definitions for PrP interrupt control register */
+#define PRP_INTRCNTL_RDERR B_SET(0)
+#define PRP_INTRCNTL_CH1WERR B_SET(1)
+#define PRP_INTRCNTL_CH2WERR B_SET(2)
+#define PRP_INTRCNTL_CH1FC B_SET(3)
+#define PRP_INTRCNTL_CH2FC B_SET(5)
+#define PRP_INTRCNTL_LBOVF B_SET(7)
+#define PRP_INTRCNTL_CH2OVF B_SET(8)
+
+/* Bit definitions for PrP interrupt status register */
+#define PRP_INTRSTAT_RDERR B_SET(0)
+#define PRP_INTRSTAT_CH1WERR B_SET(1)
+#define PRP_INTRSTAT_CH2WERR B_SET(2)
+#define PRP_INTRSTAT_CH2BUF2 B_SET(3)
+#define PRP_INTRSTAT_CH2BUF1 B_SET(4)
+#define PRP_INTRSTAT_CH1BUF2 B_SET(5)
+#define PRP_INTRSTAT_CH1BUF1 B_SET(6)
+#define PRP_INTRSTAT_LBOVF B_SET(7)
+#define PRP_INTRSTAT_CH2OVF B_SET(8)
+
+#define PRP_CHANNEL_1 0x1
+#define PRP_CHANNEL_2 0x2
+
+/* PRP-CSI config */
+#define PRP_CSI_EN 0x80
+#define PRP_CSI_LOOP (0x40 | PRP_CSI_EN)
+#define PRP_CSI_IRQ_FRM (0x08 | PRP_CSI_LOOP)
+#define PRP_CSI_IRQ_CH1ERR (0x10 | PRP_CSI_LOOP)
+#define PRP_CSI_IRQ_CH2ERR (0x20 | PRP_CSI_LOOP)
+#define PRP_CSI_IRQ_ALL (0x38 | PRP_CSI_LOOP)
+#define PRP_CSI_SKIP_NONE 0
+#define PRP_CSI_SKIP_1OF2 1
+#define PRP_CSI_SKIP_1OF3 2
+#define PRP_CSI_SKIP_2OF3 3
+#define PRP_CSI_SKIP_1OF4 4
+#define PRP_CSI_SKIP_3OF4 5
+#define PRP_CSI_SKIP_2OF5 6
+#define PRP_CSI_SKIP_4OF5 7
+
+#define PRP_PIXIN_RGB565 0x2CA00565
+#define PRP_PIXIN_RGB888 0x41000888
+#define PRP_PIXIN_YUV420 0
+#define PRP_PIXIN_YUYV 0x22000888
+#define PRP_PIXIN_YVYU 0x20100888
+#define PRP_PIXIN_UYVY 0x03080888
+#define PRP_PIXIN_VYUY 0x01180888
+#define PRP_PIXIN_YUV422 0x62080888
+
+#define PRP_PIX1_RGB332 0x14400322
+#define PRP_PIX1_RGB565 0x2CA00565
+#define PRP_PIX1_RGB888 0x41000888
+#define PRP_PIX1_YUYV 0x62000888
+#define PRP_PIX1_YVYU 0x60100888
+#define PRP_PIX1_UYVY 0x43080888
+#define PRP_PIX1_VYUY 0x41180888
+#define PRP_PIX1_UNUSED 0
+
+#define PRP_PIX2_YUV420 0
+#define PRP_PIX2_YUV422 1
+#define PRP_PIX2_YUV444 4
+#define PRP_PIX2_UNUSED 8
+
+#define PRP_ALGO_WIDTH_ANY 0
+#define PRP_ALGO_HEIGHT_ANY 0
+#define PRP_ALGO_WIDTH_BIL 1
+#define PRP_ALGO_WIDTH_AVG 2
+#define PRP_ALGO_HEIGHT_BIL 4
+#define PRP_ALGO_HEIGHT_AVG 8
+#define PRP_ALGO_BYPASS 0x10
+
+typedef struct _emma_prp_ratio {
+ unsigned short num;
+ unsigned short den;
+} emma_prp_ratio;
+
+/*
+ * The following definitions are for resizing. Definition values must not
+ * be changed otherwise decision logic will be wrong.
+ */
+#define SCALE_RETRY 16 /* retry times if ratio is not supported */
+
+#define BC_COEF 3
+#define MAX_TBL 20
+#define SZ_COEF (1 << BC_COEF)
+
+#define ALGO_AUTO 0
+#define ALGO_BIL 1
+#define ALGO_AVG 2
+
+typedef struct {
+ char tbl[20]; /* table entries */
+ char len; /* table length used */
+ char algo; /* ALGO_xxx */
+ char ratio[20]; /* ratios used */
+} scale_t;
+
+/*
+ * structure for prp scaling.
+ * algorithm - bilinear or averaging for each axis
+ * PRP_ALGO_WIDTH_x | PRP_ALGO_HEIGHT_x | PRP_ALGO_BYPASS
+ * PRP_ALGO_BYPASS - Ch1 will not use Ch2 scaling with this flag
+ */
+typedef struct _emma_prp_scale {
+ unsigned char algo;
+ emma_prp_ratio width;
+ emma_prp_ratio height;
+} emma_prp_scale;
+
+typedef struct emma_prp_cfg {
+ unsigned int in_pix; /* PRP_PIXIN_xxx */
+ unsigned short in_width; /* image width, 32 - 2044 */
+ unsigned short in_height; /* image height, 32 - 2044 */
+ unsigned char in_csi; /* PRP_CSI_SKIP_x | PRP_CSI_LOOP */
+ unsigned short in_line_stride; /* in_line_stride and in_line_skip */
+ unsigned short in_line_skip; /* allow cropping from CSI */
+ unsigned int in_ptr; /* bus address */
+ /*
+ * in_csc[9] = 1 -> Y-16
+ * if in_csc[1..9] == 0
+ * in_csc[0] represents YUV range 0-3 = A0,A1,B0,B1;
+ * else
+ * in_csc[0..9] represents either format
+ */
+ unsigned short in_csc[10];
+
+ unsigned char ch2_pix; /* PRP_PIX2_xxx */
+ emma_prp_scale ch2_scale; /* resizing paramters */
+ unsigned short ch2_width; /* 4-2044, 0 = scaled */
+ unsigned short ch2_height; /* 4-2044, 0 = scaled */
+ unsigned int ch2_ptr; /* bus addr */
+ unsigned int ch2_ptr2; /* bus addr for 2nd buf (loop mode) */
+ unsigned char ch2_csi; /* PRP_CSI_SKIP_x | PRP_CSI_LOOP */
+
+ unsigned int ch1_pix; /* PRP_PIX1_xxx */
+ emma_prp_scale ch1_scale; /* resizing parameters */
+ unsigned short ch1_width; /* 4-2044, 0 = scaled */
+ unsigned short ch1_height; /* 4-2044, 0 = scaled */
+ unsigned short ch1_stride; /* 4-4088, 0 = ch1_width */
+ unsigned int ch1_ptr; /* bus addr */
+ unsigned int ch1_ptr2; /* bus addr for 2nd buf (loop mode) */
+ unsigned char ch1_csi; /* PRP_CSI_SKIP_x | PRP_CSI_LOOP */
+
+ /*
+ * channel resizing coefficients
+ * scale[0] for channel 1 width
+ * scale[1] for channel 1 height
+ * scale[2] for channel 2 width
+ * scale[3] for channel 2 height
+ */
+ scale_t scale[4];
+} emma_prp_cfg;
+
+int prphw_reset(void);
+int prphw_enable(int channel);
+int prphw_disable(int channel);
+int prphw_inptr(emma_prp_cfg *);
+int prphw_ch1ptr(emma_prp_cfg *);
+int prphw_ch1ptr2(emma_prp_cfg *);
+int prphw_ch2ptr(emma_prp_cfg *);
+int prphw_ch2ptr2(emma_prp_cfg *);
+int prphw_cfg(emma_prp_cfg *);
+int prphw_isr(void);
+void prphw_init(void);
+void prphw_exit(void);
+
+/*
+ * scale out coefficient table
+ * din in scale numerator
+ * dout in scale denominator
+ * inv in pre-scale dimension
+ * vout in/out post-scale output dimension
+ * pout out post-scale internal dimension [opt]
+ * retry in retry times (round the output length) when need
+ */
+int prp_scale(scale_t * pscale, int din, int dout, int inv,
+ unsigned short *vout, unsigned short *pout, int retry);
+
+int prp_init(void *dev_id);
+void prp_exit(void *dev_id);
+int prp_enc_select(void *data);
+int prp_enc_deselect(void *data);
+int prp_vf_select(void *data);
+int prp_vf_deselect(void *data);
+int prp_still_select(void *data);
+int prp_still_deselect(void *data);
+
+#endif /* __MX27_PRP_H__ */
diff --git a/drivers/media/video/mxc/capture/mx27_prphw.c b/drivers/media/video/mxc/capture/mx27_prphw.c
new file mode 100644
index 000000000000..c56a6df1716e
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_prphw.c
@@ -0,0 +1,1099 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx27_prphw.c
+ *
+ * @brief MX27 Video For Linux 2 capture driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/clk.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+
+#include "mx27_prp.h"
+
+#define PRP_MIN_IN_WIDTH 32
+#define PRP_MAX_IN_WIDTH 2044
+#define PRP_MIN_IN_HEIGHT 32
+#define PRP_MAX_IN_HEIGHT 2044
+
+typedef struct _coeff_t {
+ unsigned long coeff[2];
+ unsigned long cntl;
+} coeff_t[2][2];
+
+static coeff_t *PRP_RSZ_COEFF = (coeff_t *) PRP_CH1_RZ_HORI_COEF1;
+
+static unsigned char scale_get(scale_t * t,
+ unsigned char *i, unsigned char *out);
+static int gcd(int x, int y);
+static int ratio(int x, int y, int *den);
+static int prp_scale_bilinear(scale_t * t, int coeff, int base, int nxt);
+static int prp_scale_ave(scale_t * t, unsigned char base);
+static int ave_scale(scale_t * t, int inv, int outv);
+static int scale(scale_t * t, int inv, int outv);
+
+/*!
+ * @param t table
+ * @param i table index
+ * @param out bilinear # input pixels to advance
+ * average whether result is ready for output
+ * @return coefficient
+*/
+static unsigned char scale_get(scale_t * t, unsigned char *i,
+ unsigned char *out)
+{
+ unsigned char c;
+
+ c = t->tbl[*i];
+ (*i)++;
+ *i %= t->len;
+
+ if (out) {
+ if (t->algo == ALGO_BIL) {
+ for ((*out) = 1;
+ (*i) && ((*i) < t->len) && !t->tbl[(*i)]; (*i)++) {
+ (*out)++;
+ }
+ if ((*i) == t->len)
+ (*i) = 0;
+ } else
+ *out = c >> BC_COEF;
+ }
+
+ c &= SZ_COEF - 1;
+
+ if (c == SZ_COEF - 1)
+ c = SZ_COEF;
+
+ return c;
+}
+
+/*!
+ * @brief Get maximum common divisor.
+ * @param x First input value
+ * @param y Second input value
+ * @return Maximum common divisor of x and y
+ */
+static int gcd(int x, int y)
+{
+ int k;
+
+ if (x < y) {
+ k = x;
+ x = y;
+ y = k;
+ }
+
+ while ((k = x % y)) {
+ x = y;
+ y = k;
+ }
+
+ return y;
+}
+
+/*!
+ * @brief Get ratio.
+ * @param x First input value
+ * @param y Second input value
+ * @param den Denominator of the ratio (corresponding to y)
+ * @return Numerator of the ratio (corresponding to x)
+ */
+static int ratio(int x, int y, int *den)
+{
+ int g;
+
+ if (!x || !y)
+ return 0;
+
+ g = gcd(x, y);
+ *den = y / g;
+
+ return x / g;
+}
+
+/*!
+ * @brief Build PrP coefficient entry based on bilinear algorithm
+ *
+ * @param t The pointer to scale_t structure
+ * @param coeff The weighting coefficient
+ * @param base The base of the coefficient
+ * @param nxt Number of pixels to be read
+ *
+ * @return The length of current coefficient table on success
+ * -1 on failure
+ */
+static int prp_scale_bilinear(scale_t * t, int coeff, int base, int nxt)
+{
+ int i;
+
+ if (t->len >= sizeof(t->tbl))
+ return -1;
+
+ coeff = ((coeff << BC_COEF) + (base >> 1)) / base;
+ if (coeff >= SZ_COEF - 1)
+ coeff--;
+
+ coeff |= SZ_COEF;
+ t->tbl[(int)t->len++] = (unsigned char)coeff;
+
+ for (i = 1; i < nxt; i++) {
+ if (t->len >= MAX_TBL)
+ return -1;
+
+ t->tbl[(int)t->len++] = 0;
+ }
+
+ return t->len;
+}
+
+#define _bary(name) static const unsigned char name[]
+
+_bary(c1) = {
+7};
+
+_bary(c2) = {
+4, 4};
+
+_bary(c3) = {
+2, 4, 2};
+
+_bary(c4) = {
+2, 2, 2, 2};
+
+_bary(c5) = {
+1, 2, 2, 2, 1};
+
+_bary(c6) = {
+1, 1, 2, 2, 1, 1};
+
+_bary(c7) = {
+1, 1, 1, 2, 1, 1, 1};
+
+_bary(c8) = {
+1, 1, 1, 1, 1, 1, 1, 1};
+
+_bary(c9) = {
+1, 1, 1, 1, 1, 1, 1, 1, 0};
+
+_bary(c10) = {
+0, 1, 1, 1, 1, 1, 1, 1, 1, 0};
+
+_bary(c11) = {
+0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0};
+
+_bary(c12) = {
+0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0};
+
+_bary(c13) = {
+0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0};
+
+_bary(c14) = {
+0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0};
+
+_bary(c15) = {
+0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0};
+
+_bary(c16) = {
+1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0};
+
+_bary(c17) = {
+0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0};
+
+_bary(c18) = {
+0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0};
+
+_bary(c19) = {
+0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0};
+
+_bary(c20) = {
+0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0};
+
+static const unsigned char *ave_coeff[] = {
+ c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
+ c11, c12, c13, c14, c15, c16, c17, c18, c19, c20
+};
+
+/*!
+ * @brief Build PrP coefficient table based on average algorithm
+ *
+ * @param t The pointer to scale_t structure
+ * @param base The base of the coefficient
+ *
+ * @return The length of current coefficient table on success
+ * -1 on failure
+ */
+static int prp_scale_ave(scale_t * t, unsigned char base)
+{
+ if (t->len + base > sizeof(t->tbl))
+ return -1;
+
+ memcpy(&t->tbl[(int)t->len], ave_coeff[(int)base - 1], base);
+ t->len = (unsigned char)(t->len + base);
+ t->tbl[t->len - 1] |= SZ_COEF;
+
+ return t->len;
+}
+
+/*!
+ * @brief Build PrP coefficient table based on average algorithm
+ *
+ * @param t The pointer to scale_t structure
+ * @param inv Input resolution
+ * @param outv Output resolution
+ *
+ * @return The length of current coefficient table on success
+ * -1 on failure
+ */
+static int ave_scale(scale_t * t, int inv, int outv)
+{
+ int ratio_count;
+
+ ratio_count = 0;
+ if (outv != 1) {
+ unsigned char a[20];
+ int v;
+
+ /* split n:m into multiple n[i]:1 */
+ for (v = 0; v < outv; v++)
+ a[v] = (unsigned char)(inv / outv);
+
+ inv %= outv;
+ if (inv) {
+ /* find start of next layer */
+ v = (outv - inv) >> 1;
+ inv += v;
+ for (; v < inv; v++)
+ a[v]++;
+ }
+
+ for (v = 0; v < outv; v++) {
+ if (prp_scale_ave(t, a[v]) < 0)
+ return -1;
+
+ t->ratio[ratio_count] = a[v];
+ ratio_count++;
+ }
+ } else if (prp_scale_ave(t, inv) < 0) {
+ return -1;
+ } else {
+ t->ratio[ratio_count++] = (char)inv;
+ ratio_count++;
+ }
+
+ return t->len;
+}
+
+/*!
+ * @brief Build PrP coefficient table
+ *
+ * @param t The pointer to scale_t structure
+ * @param inv input resolution reduced ratio
+ * @param outv output resolution reduced ratio
+ *
+ * @return The length of current coefficient table on success
+ * -1 on failure
+ */
+static int scale(scale_t * t, int inv, int outv)
+{
+ int v; /* overflow counter */
+ int coeff, nxt; /* table output */
+
+ t->len = 0;
+ if (t->algo == ALGO_AUTO) {
+ /* automatic choice - bilinear for shrinking less than 2:1 */
+ t->algo = ((outv != inv) && ((2 * outv) > inv)) ?
+ ALGO_BIL : ALGO_AVG;
+ }
+
+ /* 1:1 resize must use averaging, bilinear will hang */
+ if ((inv == outv) && (t->algo == ALGO_BIL)) {
+ pr_debug("Warning: 1:1 resize must use averaging algo\n");
+ t->algo = ALGO_AVG;
+ }
+
+ memset(t->tbl, 0, sizeof(t->tbl));
+ if (t->algo == ALGO_BIL) {
+ t->ratio[0] = (char)inv;
+ t->ratio[1] = (char)outv;
+ } else
+ memset(t->ratio, 0, sizeof(t->ratio));
+
+ if (inv == outv) {
+ /* force scaling */
+ t->ratio[0] = 1;
+ if (t->algo == ALGO_BIL)
+ t->ratio[1] = 1;
+
+ return prp_scale_ave(t, 1);
+ }
+
+ if (inv < outv) {
+ pr_debug("Upscaling not supported %d:%d\n", inv, outv);
+ return -1;
+ }
+
+ if (t->algo != ALGO_BIL)
+ return ave_scale(t, inv, outv);
+
+ v = 0;
+ if (inv >= 2 * outv) {
+ /* downscale: >=2:1 bilinear approximation */
+ coeff = inv - 2 * outv;
+ v = 0;
+ nxt = 0;
+ do {
+ v += coeff;
+ nxt = 2;
+ while (v >= outv) {
+ v -= outv;
+ nxt++;
+ }
+
+ if (prp_scale_bilinear(t, 1, 2, nxt) < 0)
+ return -1;
+ } while (v);
+ } else {
+ /* downscale: bilinear */
+ int in_pos_inc = 2 * outv;
+ int out_pos = inv;
+ int out_pos_inc = 2 * inv;
+ int init_carry = inv - outv;
+ int carry = init_carry;
+
+ v = outv + in_pos_inc;
+ do {
+ coeff = v - out_pos;
+ out_pos += out_pos_inc;
+ carry += out_pos_inc;
+ for (nxt = 0; v < out_pos; nxt++) {
+ v += in_pos_inc;
+ carry -= in_pos_inc;
+ }
+ if (prp_scale_bilinear(t, coeff, in_pos_inc, nxt) < 0)
+ return -1;
+ } while (carry != init_carry);
+ }
+ return t->len;
+}
+
+/*!
+ * @brief Build PrP coefficient table
+ *
+ * @param pscale The pointer to scale_t structure which holdes
+ * coefficient tables
+ * @param din Scale ratio numerator
+ * @param dout Scale ratio denominator
+ * @param inv Input resolution
+ * @param vout Output resolution
+ * @param pout Internal output resolution
+ * @param retry Retry times (round the output length) when need
+ *
+ * @return Zero on success, others on failure
+ */
+int prp_scale(scale_t * pscale, int din, int dout, int inv,
+ unsigned short *vout, unsigned short *pout, int retry)
+{
+ int num;
+ int den;
+ unsigned short outv;
+
+ /* auto-generation of values */
+ if (!(dout && din)) {
+ if (!*vout)
+ dout = din = 1;
+ else {
+ din = inv;
+ dout = *vout;
+ }
+ }
+
+ if (din < dout) {
+ pr_debug("Scale err, unsupported ratio %d : %d\n", din, dout);
+ return -1;
+ }
+
+ lp_retry:
+ num = ratio(din, dout, &den);
+ if (!num) {
+ pr_debug("Scale err, unsupported ratio %d : %d\n", din, dout);
+ return -1;
+ }
+
+ if (num > MAX_TBL || scale(pscale, num, den) < 0) {
+ dout++;
+ if (retry--)
+ goto lp_retry;
+
+ pr_debug("Scale err, unsupported ratio %d : %d\n", num, den);
+ return -1;
+ }
+
+ if (pscale->algo == ALGO_BIL) {
+ unsigned char i, j, k;
+
+ outv =
+ (unsigned short)(inv / pscale->ratio[0] * pscale->ratio[1]);
+ inv %= pscale->ratio[0];
+ for (i = j = 0; inv > 0; j++) {
+ unsigned char nxt;
+
+ k = scale_get(pscale, &i, &nxt);
+ if (inv == 1 && k < SZ_COEF) {
+ /* needs 2 pixels for this output */
+ break;
+ }
+ inv -= nxt;
+ }
+ outv = outv + j;
+ } else {
+ unsigned char i, tot;
+
+ for (tot = i = 0; pscale->ratio[i]; i++)
+ tot = tot + pscale->ratio[i];
+
+ outv = (unsigned short)(inv / tot) * i;
+ inv %= tot;
+ for (i = 0; inv > 0; i++, outv++)
+ inv -= pscale->ratio[i];
+ }
+
+ if (!(*vout) || ((*vout) > outv))
+ *vout = outv;
+
+ if (pout)
+ *pout = outv;
+
+ return 0;
+}
+
+/*!
+ * @brief Reset PrP block
+ */
+int prphw_reset(void)
+{
+ unsigned long val;
+ unsigned long flag;
+ int i;
+
+ flag = PRP_CNTL_RST;
+ val = PRP_CNTL_RSTVAL;
+
+ __raw_writel(flag, PRP_CNTL);
+
+ /* timeout */
+ for (i = 0; i < 1000; i++) {
+ if (!(__raw_readl(PRP_CNTL) & flag)) {
+ pr_debug("PrP reset over\n");
+ break;
+ }
+ msleep(1);
+ }
+
+ /* verify reset value */
+ if (__raw_readl(PRP_CNTL) != val) {
+ pr_info("PrP reset err, val = 0x%08X\n", __raw_readl(PRP_CNTL));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Enable PrP channel.
+ * @param channel Channel number to be enabled
+ * @return Zero on success, others on failure
+ */
+int prphw_enable(int channel)
+{
+ unsigned long val;
+
+ val = __raw_readl(PRP_CNTL);
+ if (channel & PRP_CHANNEL_1)
+ val |= PRP_CNTL_CH1EN;
+ if (channel & PRP_CHANNEL_2)
+ val |= (PRP_CNTL_CH2EN | PRP_CNTL_CH2_FLOWEN);
+
+ __raw_writel(val, PRP_CNTL);
+
+ return 0;
+}
+
+/*!
+ * @brief Disable PrP channel.
+ * @param channel Channel number to be disable
+ * @return Zero on success, others on failure
+ */
+int prphw_disable(int channel)
+{
+ unsigned long val;
+
+ val = __raw_readl(PRP_CNTL);
+ if (channel & PRP_CHANNEL_1)
+ val &= ~PRP_CNTL_CH1EN;
+ if (channel & PRP_CHANNEL_2)
+ val &= ~(PRP_CNTL_CH2EN | PRP_CNTL_CH2_FLOWEN);
+
+ __raw_writel(val, PRP_CNTL);
+
+ return 0;
+}
+
+/*!
+ * @brief Set PrP input buffer address.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_inptr(emma_prp_cfg * cfg)
+{
+ if (cfg->in_csi & PRP_CSI_EN)
+ return -1;
+
+ __raw_writel(cfg->in_ptr, PRP_SOURCE_Y_PTR);
+ if (cfg->in_pix == PRP_PIXIN_YUV420) {
+ u32 size;
+
+ size = cfg->in_line_stride * cfg->in_height;
+ __raw_writel(cfg->in_ptr + size, PRP_SOURCE_CB_PTR);
+ __raw_writel(cfg->in_ptr + size + (size >> 2),
+ PRP_SOURCE_CR_PTR);
+ }
+ return 0;
+}
+
+/*!
+ * @brief Set PrP channel 1 output buffer 1 address.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_ch1ptr(emma_prp_cfg * cfg)
+{
+ if (cfg->ch1_pix == PRP_PIX1_UNUSED)
+ return -1;
+
+ __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB1_PTR);
+
+ /* support double buffer in loop mode only */
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) {
+ if (cfg->ch1_ptr2)
+ __raw_writel(cfg->ch1_ptr2, PRP_DEST_RGB2_PTR);
+ else
+ __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB2_PTR);
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Set PrP channel 1 output buffer 2 address.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_ch1ptr2(emma_prp_cfg * cfg)
+{
+ if (cfg->ch1_pix == PRP_PIX1_UNUSED ||
+ (cfg->in_csi & PRP_CSI_LOOP) != PRP_CSI_LOOP)
+ return -1;
+
+ if (cfg->ch1_ptr2)
+ __raw_writel(cfg->ch1_ptr2, PRP_DEST_RGB2_PTR);
+ else
+ return -1;
+
+ return 0;
+}
+
+/*!
+ * @brief Set PrP channel 2 output buffer 1 address.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_ch2ptr(emma_prp_cfg * cfg)
+{
+ u32 size;
+
+ if (cfg->ch2_pix == PRP_PIX2_UNUSED)
+ return -1;
+
+ __raw_writel(cfg->ch2_ptr, PRP_DEST_Y_PTR);
+
+ if (cfg->ch2_pix == PRP_PIX2_YUV420) {
+ size = cfg->ch2_width * cfg->ch2_height;
+ __raw_writel(cfg->ch2_ptr + size, PRP_DEST_CB_PTR);
+ __raw_writel(cfg->ch2_ptr + size + (size >> 2),
+ PRP_DEST_CR_PTR);
+ }
+
+ __raw_writel(__raw_readl(PRP_CNTL) | PRP_CNTL_CH2B1, PRP_CNTL);
+ return 0;
+}
+
+/*!
+ * @brief Set PrP channel 2 output buffer 2 address.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_ch2ptr2(emma_prp_cfg * cfg)
+{
+ u32 size;
+
+ if (cfg->ch2_pix == PRP_PIX2_UNUSED ||
+ (cfg->in_csi & PRP_CSI_LOOP) != PRP_CSI_LOOP)
+ return -1;
+
+ __raw_writel(cfg->ch2_ptr2, PRP_SOURCE_Y_PTR);
+ if (cfg->ch2_pix == PRP_PIX2_YUV420) {
+ size = cfg->ch2_width * cfg->ch2_height;
+ __raw_writel(cfg->ch2_ptr2 + size, PRP_SOURCE_CB_PTR);
+ __raw_writel(cfg->ch2_ptr2 + size + (size >> 2),
+ PRP_SOURCE_CR_PTR);
+ }
+
+ __raw_writel(__raw_readl(PRP_CNTL) | PRP_CNTL_CH2B2, PRP_CNTL);
+ return 0;
+}
+
+/*!
+ * @brief Build CSC table
+ * @param csc CSC table
+ * in csc[0]=index 0..3 : A.1 A.0 B.1 B.0
+ * csc[1]=direction 0 : YUV2RGB 1 : RGB2YUV
+ * out csc[0..4] are coefficients c[9] is offset
+ * csc[0..8] are coefficients c[9] is offset
+ */
+void csc_tbl(short csc[10])
+{
+ static const unsigned short _r2y[][9] = {
+ {0x4D, 0x4B, 0x3A, 0x57, 0x55, 0x40, 0x40, 0x6B, 0x29},
+ {0x42, 0x41, 0x32, 0x4C, 0x4A, 0x38, 0x38, 0x5E, 0x24},
+ {0x36, 0x5C, 0x25, 0x3B, 0x63, 0x40, 0x40, 0x74, 0x18},
+ {0x2F, 0x4F, 0x20, 0x34, 0x57, 0x38, 0x38, 0x66, 0x15},
+ };
+ static const unsigned short _y2r[][5] = {
+ {0x80, 0xb4, 0x2c, 0x5b, 0x0e4},
+ {0x95, 0xcc, 0x32, 0x68, 0x104},
+ {0x80, 0xca, 0x18, 0x3c, 0x0ec},
+ {0x95, 0xe5, 0x1b, 0x44, 0x1e0},
+ };
+ unsigned short *_csc;
+ int _csclen;
+
+ csc[9] = csc[0] & 1;
+ _csclen = csc[0] & 3;
+
+ if (csc[1]) {
+ _csc = (unsigned short *)_r2y[_csclen];
+ _csclen = sizeof(_r2y[0]);
+ } else {
+ _csc = (unsigned short *)_y2r[_csclen];
+ _csclen = sizeof(_y2r[0]);
+ memset(csc + 5, 0, sizeof(short) * 4);
+ }
+ memcpy(csc, _csc, _csclen);
+}
+
+/*!
+ * @brief Setup PrP resize coefficient registers
+ *
+ * @param ch PrP channel number
+ * @param dir Direction, 0 - horizontal, 1 - vertical
+ * @param scale The pointer to scale_t structure
+ */
+static void prp_set_scaler(int ch, int dir, scale_t * scale)
+{
+ int i;
+ unsigned int coeff[2];
+ unsigned int valid;
+
+ for (coeff[0] = coeff[1] = valid = 0, i = 19; i >= 0; i--) {
+ int j;
+
+ j = i > 9 ? 1 : 0;
+ coeff[j] = (coeff[j] << BC_COEF) |
+ (scale->tbl[i] & (SZ_COEF - 1));
+
+ if (i == 5 || i == 15)
+ coeff[j] <<= 1;
+
+ valid = (valid << 1) | (scale->tbl[i] >> BC_COEF);
+ }
+
+ valid |= (scale->len << 24) | ((2 - scale->algo) << 31);
+
+ for (i = 0; i < 2; i++)
+ (*PRP_RSZ_COEFF)[1 - ch][dir].coeff[i] = coeff[i];
+
+ (*PRP_RSZ_COEFF)[1 - ch][dir].cntl = valid;
+}
+
+/*!
+ * @brief Setup PrP registers relevant to input.
+ * @param cfg Pointer to PrP configuration parameter
+ * @param prp_cntl Holds the value for PrP control register
+ * @return Zero on success, others on failure
+ */
+static int prphw_input_cfg(emma_prp_cfg * cfg, unsigned long *prp_cntl)
+{
+ unsigned long mask;
+
+ switch (cfg->in_pix) {
+ case PRP_PIXIN_YUV420:
+ *prp_cntl |= PRP_CNTL_IN_YUV420;
+ mask = 0x7;
+ break;
+ case PRP_PIXIN_YUYV:
+ case PRP_PIXIN_YVYU:
+ case PRP_PIXIN_UYVY:
+ case PRP_PIXIN_VYUY:
+ *prp_cntl |= PRP_CNTL_IN_YUV422;
+ mask = 0x1;
+ break;
+ case PRP_PIXIN_RGB565:
+ *prp_cntl |= PRP_CNTL_IN_RGB16;
+ mask = 0x1;
+ break;
+ case PRP_PIXIN_RGB888:
+ *prp_cntl |= PRP_CNTL_IN_RGB32;
+ mask = 0;
+ break;
+ default:
+ pr_debug("Unsupported input pix format 0x%08X\n", cfg->in_pix);
+ return -1;
+ }
+
+ /* align the input image width */
+ if (cfg->in_width & mask) {
+ pr_debug("in_width misaligned. in_width=%d\n", cfg->in_width);
+ return -1;
+ }
+
+ if ((cfg->in_width < PRP_MIN_IN_WIDTH)
+ || (cfg->in_width > PRP_MAX_IN_WIDTH)) {
+ pr_debug("Unsupported input width %d\n", cfg->in_width);
+ return -1;
+ }
+
+ cfg->in_height &= ~1; /* truncate to make even */
+
+ if ((cfg->in_height < PRP_MIN_IN_HEIGHT)
+ || (cfg->in_height > PRP_MAX_IN_HEIGHT)) {
+ pr_debug("Unsupported input height %d\n", cfg->in_height);
+ return -1;
+ }
+
+ if (!(cfg->in_csi & PRP_CSI_EN))
+ if (!cfg->in_line_stride)
+ cfg->in_line_stride = cfg->in_width;
+
+ __raw_writel(cfg->in_pix, PRP_SRC_PIXEL_FORMAT_CNTL);
+ __raw_writel((cfg->in_width << 16) | cfg->in_height,
+ PRP_SOURCE_FRAME_SIZE);
+ __raw_writel((cfg->in_line_skip << 16) | cfg->in_line_stride,
+ PRP_SOURCE_LINE_STRIDE);
+
+ if (!(cfg->in_csi & PRP_CSI_EN)) {
+ __raw_writel(cfg->in_ptr, PRP_SOURCE_Y_PTR);
+ if (cfg->in_pix == PRP_PIXIN_YUV420) {
+ unsigned int size;
+
+ size = cfg->in_line_stride * cfg->in_height;
+ __raw_writel(cfg->in_ptr + size, PRP_SOURCE_CB_PTR);
+ __raw_writel(cfg->in_ptr + size + (size >> 2),
+ PRP_SOURCE_CR_PTR);
+ }
+ }
+
+ /* always cropping */
+ *prp_cntl |= PRP_CNTL_WINEN;
+
+ /* color space conversion */
+ if (!cfg->in_csc[1]) {
+ if (cfg->in_csc[0] > 3) {
+ pr_debug("in_csc invalid 0x%X\n", cfg->in_csc[0]);
+ return -1;
+ }
+ if ((cfg->in_pix == PRP_PIXIN_RGB565)
+ || (cfg->in_pix == PRP_PIXIN_RGB888))
+ cfg->in_csc[1] = 1;
+ else
+ cfg->in_csc[0] = 0;
+ csc_tbl(cfg->in_csc);
+ }
+
+ __raw_writel((cfg->in_csc[0] << 21) | (cfg->in_csc[1] << 11)
+ | cfg->in_csc[2], PRP_CSC_COEF_012);
+ __raw_writel((cfg->in_csc[3] << 21) | (cfg->in_csc[4] << 11)
+ | cfg->in_csc[5], PRP_CSC_COEF_345);
+ __raw_writel((cfg->in_csc[6] << 21) | (cfg->in_csc[7] << 11)
+ | cfg->in_csc[8] | (cfg->in_csc[9] << 31),
+ PRP_CSC_COEF_678);
+
+ if (cfg->in_csi & PRP_CSI_EN) {
+ *prp_cntl |= PRP_CNTL_CSI;
+
+ /* loop mode enable, ch1 ch2 together */
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP)
+ *prp_cntl |= (PRP_CNTL_CH1_LOOP | PRP_CNTL_CH2_LOOP);
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Setup PrP registers relevant to channel 2.
+ * @param cfg Pointer to PrP configuration parameter
+ * @param prp_cntl Holds the value for PrP control register
+ * @return Zero on success, others on failure
+ */
+static int prphw_ch2_cfg(emma_prp_cfg * cfg, unsigned long *prp_cntl)
+{
+ switch (cfg->ch2_pix) {
+ case PRP_PIX2_YUV420:
+ *prp_cntl |= PRP_CNTL_CH2_YUV420;
+ break;
+ case PRP_PIX2_YUV422:
+ *prp_cntl |= PRP_CNTL_CH2_YUV422;
+ break;
+ case PRP_PIX2_YUV444:
+ *prp_cntl |= PRP_CNTL_CH2_YUV444;
+ break;
+ case PRP_PIX2_UNUSED:
+ return 0;
+ default:
+ pr_debug("Unsupported channel 2 pix format 0x%08X\n",
+ cfg->ch2_pix);
+ return -1;
+ }
+
+ if (cfg->ch2_pix == PRP_PIX2_YUV420) {
+ cfg->ch2_height &= ~1; /* ensure U/V presence */
+ cfg->ch2_width &= ~7; /* ensure U/V word aligned */
+ } else if (cfg->ch2_pix == PRP_PIX2_YUV422) {
+ cfg->ch2_width &= ~1; /* word aligned */
+ }
+
+ __raw_writel((cfg->ch2_width << 16) | cfg->ch2_height,
+ PRP_CH2_OUT_IMAGE_SIZE);
+
+ if (cfg->ch2_pix == PRP_PIX2_YUV420) {
+ u32 size;
+
+ /* Luminanance band start address */
+ __raw_writel(cfg->ch2_ptr, PRP_DEST_Y_PTR);
+
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) {
+ if (!cfg->ch2_ptr2)
+ __raw_writel(cfg->ch2_ptr, PRP_SOURCE_Y_PTR);
+ else
+ __raw_writel(cfg->ch2_ptr2, PRP_SOURCE_Y_PTR);
+ }
+
+ /* Cb and Cr band start address */
+ size = cfg->ch2_width * cfg->ch2_height;
+ __raw_writel(cfg->ch2_ptr + size, PRP_DEST_CB_PTR);
+ __raw_writel(cfg->ch2_ptr + size + (size >> 2),
+ PRP_DEST_CR_PTR);
+
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) {
+ if (!cfg->ch2_ptr2) {
+ __raw_writel(cfg->ch2_ptr + size,
+ PRP_SOURCE_CB_PTR);
+ __raw_writel(cfg->ch2_ptr + size + (size >> 2),
+ PRP_SOURCE_CR_PTR);
+ } else {
+ __raw_writel(cfg->ch2_ptr2 + size,
+ PRP_SOURCE_CB_PTR);
+ __raw_writel(cfg->ch2_ptr2 + size + (size >> 2),
+ PRP_SOURCE_CR_PTR);
+ }
+ }
+ } else { /* Pixel interleaved YUV422 or YUV444 */
+ __raw_writel(cfg->ch2_ptr, PRP_DEST_Y_PTR);
+
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) {
+ if (!cfg->ch2_ptr2)
+ __raw_writel(cfg->ch2_ptr, PRP_SOURCE_Y_PTR);
+ else
+ __raw_writel(cfg->ch2_ptr2, PRP_SOURCE_Y_PTR);
+ }
+ }
+ *prp_cntl |= PRP_CNTL_CH2B1 | PRP_CNTL_CH2B2;
+
+ return 0;
+}
+
+/*!
+ * @brief Setup PrP registers relevant to channel 1.
+ * @param cfg Pointer to PrP configuration parameter
+ * @param prp_cntl Holds the value for PrP control register
+ * @return Zero on success, others on failure
+ */
+static int prphw_ch1_cfg(emma_prp_cfg * cfg, unsigned long *prp_cntl)
+{
+ int ch1_bpp = 0;
+
+ switch (cfg->ch1_pix) {
+ case PRP_PIX1_RGB332:
+ *prp_cntl |= PRP_CNTL_CH1_RGB8;
+ ch1_bpp = 1;
+ break;
+ case PRP_PIX1_RGB565:
+ *prp_cntl |= PRP_CNTL_CH1_RGB16;
+ ch1_bpp = 2;
+ break;
+ case PRP_PIX1_RGB888:
+ *prp_cntl |= PRP_CNTL_CH1_RGB32;
+ ch1_bpp = 4;
+ break;
+ case PRP_PIX1_YUYV:
+ case PRP_PIX1_YVYU:
+ case PRP_PIX1_UYVY:
+ case PRP_PIX1_VYUY:
+ *prp_cntl |= PRP_CNTL_CH1_YUV422;
+ ch1_bpp = 2;
+ break;
+ case PRP_PIX1_UNUSED:
+ return 0;
+ default:
+ pr_debug("Unsupported channel 1 pix format 0x%08X\n",
+ cfg->ch1_pix);
+ return -1;
+ }
+
+ /* parallel or cascade resize */
+ if (cfg->ch1_scale.algo & PRP_ALGO_BYPASS)
+ *prp_cntl |= PRP_CNTL_UNCHAIN;
+
+ /* word align */
+ if (ch1_bpp == 2)
+ cfg->ch1_width &= ~1;
+ else if (ch1_bpp == 1)
+ cfg->ch1_width &= ~3;
+
+ if (!cfg->ch1_stride)
+ cfg->ch1_stride = cfg->ch1_width;
+
+ __raw_writel(cfg->ch1_pix, PRP_CH1_PIXEL_FORMAT_CNTL);
+ __raw_writel((cfg->ch1_width << 16) | cfg->ch1_height,
+ PRP_CH1_OUT_IMAGE_SIZE);
+ __raw_writel(cfg->ch1_stride * ch1_bpp, PRP_CH1_LINE_STRIDE);
+ __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB1_PTR);
+
+ /* double buffer for loop mode */
+ if ((cfg->in_csi & PRP_CSI_LOOP) == PRP_CSI_LOOP) {
+ if (cfg->ch1_ptr2)
+ __raw_writel(cfg->ch1_ptr2, PRP_DEST_RGB2_PTR);
+ else
+ __raw_writel(cfg->ch1_ptr, PRP_DEST_RGB2_PTR);
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Setup PrP registers.
+ * @param cfg Pointer to PrP configuration parameter
+ * @return Zero on success, others on failure
+ */
+int prphw_cfg(emma_prp_cfg * cfg)
+{
+ unsigned long prp_cntl = 0;
+ unsigned long val;
+
+ /* input pixel format checking */
+ if (prphw_input_cfg(cfg, &prp_cntl))
+ return -1;
+
+ if (prphw_ch2_cfg(cfg, &prp_cntl))
+ return -1;
+
+ if (prphw_ch1_cfg(cfg, &prp_cntl))
+ return -1;
+
+ /* register setting */
+ __raw_writel(prp_cntl, PRP_CNTL);
+
+ /* interrupt configuration */
+ val = PRP_INTRCNTL_RDERR | PRP_INTRCNTL_LBOVF;
+ if (cfg->ch1_pix != PRP_PIX1_UNUSED)
+ val |= PRP_INTRCNTL_CH1FC | PRP_INTRCNTL_CH1WERR;
+ if (cfg->ch2_pix != PRP_PIX2_UNUSED)
+ val |=
+ PRP_INTRCNTL_CH2FC | PRP_INTRCNTL_CH2WERR |
+ PRP_INTRCNTL_CH2OVF;
+ __raw_writel(val, PRP_INTRCNTL);
+
+ prp_set_scaler(1, 0, &cfg->scale[0]); /* Channel 1 width */
+ prp_set_scaler(1, 1, &cfg->scale[1]); /* Channel 1 height */
+ prp_set_scaler(0, 0, &cfg->scale[2]); /* Channel 2 width */
+ prp_set_scaler(0, 1, &cfg->scale[3]); /* Channel 2 height */
+
+ return 0;
+}
+
+/*!
+ * @brief Check PrP interrupt status.
+ * @return PrP interrupt status
+ */
+int prphw_isr(void)
+{
+ int status;
+
+ status = __raw_readl(PRP_INTRSTATUS) & 0x1FF;
+
+ if (status & (PRP_INTRSTAT_RDERR | PRP_INTRSTAT_CH1WERR |
+ PRP_INTRSTAT_CH2WERR))
+ pr_debug("isr bus error. status= 0x%08X\n", status);
+ else if (status & PRP_INTRSTAT_CH2OVF)
+ pr_debug("isr ch 2 buffer overflow. status= 0x%08X\n", status);
+ else if (status & PRP_INTRSTAT_LBOVF)
+ pr_debug("isr line buffer overflow. status= 0x%08X\n", status);
+
+ /* silicon bug?? enable bit does not self clear? */
+ if (!(__raw_readl(PRP_CNTL) & PRP_CNTL_CH1_LOOP))
+ __raw_writel(__raw_readl(PRP_CNTL) & (~PRP_CNTL_CH1EN),
+ PRP_CNTL);
+ if (!(__raw_readl(PRP_CNTL) & PRP_CNTL_CH2_LOOP))
+ __raw_writel(__raw_readl(PRP_CNTL) & (~PRP_CNTL_CH2EN),
+ PRP_CNTL);
+
+ __raw_writel(status, PRP_INTRSTATUS); /* clr irq */
+
+ return status;
+}
+
+static struct clk *emma_clk;
+
+/*!
+ * @brief PrP module clock enable
+ */
+void prphw_init(void)
+{
+ emma_clk = clk_get(NULL, "emma_clk");
+ clk_enable(emma_clk);
+}
+
+/*!
+ * @brief PrP module clock disable
+ */
+void prphw_exit(void)
+{
+ clk_disable(emma_clk);
+ clk_put(emma_clk);
+}
diff --git a/drivers/media/video/mxc/capture/mx27_prpsw.c b/drivers/media/video/mxc/capture/mx27_prpsw.c
new file mode 100644
index 000000000000..ce7db16913ec
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_prpsw.c
@@ -0,0 +1,1042 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx27_prpsw.c
+ *
+ * @brief MX27 Video For Linux 2 capture driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "mxc_v4l2_capture.h"
+#include "mx27_prp.h"
+#include "mx27_csi.h"
+#include "../drivers/video/mxc/mx2fb.h"
+#include "../opl/opl.h"
+
+#define MEAN_COEF (SZ_COEF >> 1)
+
+static char prp_dev[] = "emma_prp";
+static int g_still_on = 0;
+static emma_prp_cfg g_prp_cfg;
+static int g_vfbuf, g_rotbuf;
+static struct tasklet_struct prp_vf_tasklet;
+
+/*
+ * The following variables represents the virtual address for the cacheable
+ * buffers accessed by SW rotation/mirroring. The rotation/mirroring in
+ * cacheable buffers has significant performance improvement than it in
+ * non-cacheable buffers.
+ */
+static char *g_vaddr_vfbuf[2] = { 0, 0 };
+static char *g_vaddr_rotbuf[2] = { 0, 0 };
+static char *g_vaddr_fb = 0;
+
+static int set_ch1_addr(emma_prp_cfg * cfg, cam_data * cam);
+static int prp_v4l2_cfg(emma_prp_cfg * cfg, cam_data * cam);
+static int prp_vf_mem_alloc(cam_data * cam);
+static void prp_vf_mem_free(cam_data * cam);
+static int prp_rot_mem_alloc(cam_data * cam);
+static void prp_rot_mem_free(cam_data * cam);
+static int prp_enc_update_eba(u32 eba, int *buffer_num);
+static int prp_enc_enable(void *private);
+static int prp_enc_disable(void *private);
+static int prp_vf_start(void *private);
+static int prp_vf_stop(void *private);
+static int prp_still_start(void *private);
+static int prp_still_stop(void *private);
+static irqreturn_t prp_isr(int irq, void *dev_id);
+static void rotation(unsigned long private);
+static int prp_resize_check_ch1(emma_prp_cfg * cfg);
+static int prp_resize_check_ch2(emma_prp_cfg * cfg);
+
+#define PRP_DUMP(val) pr_debug("%s\t = 0x%08X\t%d\n", #val, val, val)
+
+/*!
+ * @brief Dump PrP configuration parameters.
+ * @param cfg The pointer to PrP configuration parameter
+ */
+static void prp_cfg_dump(emma_prp_cfg * cfg)
+{
+ PRP_DUMP(cfg->in_pix);
+ PRP_DUMP(cfg->in_width);
+ PRP_DUMP(cfg->in_height);
+ PRP_DUMP(cfg->in_csi);
+ PRP_DUMP(cfg->in_line_stride);
+ PRP_DUMP(cfg->in_line_skip);
+ PRP_DUMP(cfg->in_ptr);
+
+ PRP_DUMP(cfg->ch1_pix);
+ PRP_DUMP(cfg->ch1_width);
+ PRP_DUMP(cfg->ch1_height);
+ PRP_DUMP(cfg->ch1_scale.algo);
+ PRP_DUMP(cfg->ch1_scale.width.num);
+ PRP_DUMP(cfg->ch1_scale.width.den);
+ PRP_DUMP(cfg->ch1_scale.height.num);
+ PRP_DUMP(cfg->ch1_scale.height.den);
+ PRP_DUMP(cfg->ch1_stride);
+ PRP_DUMP(cfg->ch1_ptr);
+ PRP_DUMP(cfg->ch1_ptr2);
+ PRP_DUMP(cfg->ch1_csi);
+
+ PRP_DUMP(cfg->ch2_pix);
+ PRP_DUMP(cfg->ch2_width);
+ PRP_DUMP(cfg->ch2_height);
+ PRP_DUMP(cfg->ch2_scale.algo);
+ PRP_DUMP(cfg->ch2_scale.width.num);
+ PRP_DUMP(cfg->ch2_scale.width.den);
+ PRP_DUMP(cfg->ch2_scale.height.num);
+ PRP_DUMP(cfg->ch2_scale.height.den);
+ PRP_DUMP(cfg->ch2_ptr);
+ PRP_DUMP(cfg->ch2_ptr2);
+ PRP_DUMP(cfg->ch2_csi);
+}
+
+/*!
+ * @brief Set PrP channel 1 output address.
+ * @param cfg Pointer to emma_prp_cfg structure
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int set_ch1_addr(emma_prp_cfg * cfg, cam_data * cam)
+{
+ if (cam->rotation != V4L2_MXC_ROTATE_NONE) {
+ cfg->ch1_ptr = (unsigned int)cam->rot_vf_bufs[0];
+ cfg->ch1_ptr2 = (unsigned int)cam->rot_vf_bufs[1];
+ if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT))
+ cfg->ch1_stride = cam->win.w.height;
+ else
+ cfg->ch1_stride = cam->win.w.width;
+
+ if (cam->v4l2_fb.flags != V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_info *fb = cam->overlay_fb;
+ if (!fb)
+ return -1;
+ if (g_vaddr_fb)
+ iounmap(g_vaddr_fb);
+ g_vaddr_fb = ioremap_cached(fb->fix.smem_start,
+ fb->fix.smem_len);
+ if (!g_vaddr_fb)
+ return -1;
+ }
+ } else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ cfg->ch1_ptr = (unsigned int)cam->vf_bufs[0];
+ cfg->ch1_ptr2 = (unsigned int)cam->vf_bufs[1];
+ cfg->ch1_stride = cam->win.w.width;
+ } else {
+ struct fb_info *fb = cam->overlay_fb;
+
+ if (!fb)
+ return -1;
+
+ cfg->ch1_ptr = fb->fix.smem_start;
+ cfg->ch1_ptr += cam->win.w.top * fb->var.xres_virtual
+ * (fb->var.bits_per_pixel >> 3)
+ + cam->win.w.left * (fb->var.bits_per_pixel >> 3);
+ cfg->ch1_ptr2 = cfg->ch1_ptr;
+ cfg->ch1_stride = fb->var.xres_virtual;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Setup PrP configuration parameters.
+ * @param cfg Pointer to emma_prp_cfg structure
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_v4l2_cfg(emma_prp_cfg * cfg, cam_data * cam)
+{
+ cfg->in_pix = PRP_PIXIN_YUYV;
+ cfg->in_width = cam->crop_current.width;
+ cfg->in_height = cam->crop_current.height;
+ cfg->in_line_stride = cam->crop_current.left;
+ cfg->in_line_skip = cam->crop_current.top;
+ cfg->in_ptr = 0;
+ cfg->in_csi = PRP_CSI_LOOP;
+ memset(cfg->in_csc, 0, sizeof(cfg->in_csc));
+
+ if (cam->overlay_on) {
+ /* Convert V4L2 pixel format to PrP pixel format */
+ switch (cam->v4l2_fb.fmt.pixelformat) {
+ case V4L2_PIX_FMT_RGB332:
+ cfg->ch1_pix = PRP_PIX1_RGB332;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ cfg->ch1_pix = PRP_PIX1_RGB888;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ cfg->ch1_pix = PRP_PIX1_YUYV;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ cfg->ch1_pix = PRP_PIX1_UYVY;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ default:
+ cfg->ch1_pix = PRP_PIX1_RGB565;
+ break;
+ }
+ if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT)) {
+ cfg->ch1_width = cam->win.w.height;
+ cfg->ch1_height = cam->win.w.width;
+ } else {
+ cfg->ch1_width = cam->win.w.width;
+ cfg->ch1_height = cam->win.w.height;
+ }
+
+ if (set_ch1_addr(cfg, cam))
+ return -1;
+ } else {
+ cfg->ch1_pix = PRP_PIX1_UNUSED;
+ cfg->ch1_width = cfg->in_width;
+ cfg->ch1_height = cfg->in_height;
+ }
+ cfg->ch1_scale.algo = 0;
+ cfg->ch1_scale.width.num = cfg->in_width;
+ cfg->ch1_scale.width.den = cfg->ch1_width;
+ cfg->ch1_scale.height.num = cfg->in_height;
+ cfg->ch1_scale.height.den = cfg->ch1_height;
+ cfg->ch1_csi = PRP_CSI_EN;
+
+ if (cam->capture_on || g_still_on) {
+ switch (cam->v2f.fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ cfg->ch2_pix = PRP_PIX2_YUV422;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ cfg->ch2_pix = PRP_PIX2_YUV420;
+ break;
+ /*
+ * YUV444 is not defined by V4L2.
+ * We support it in default case.
+ */
+ default:
+ cfg->ch2_pix = PRP_PIX2_YUV444;
+ break;
+ }
+ cfg->ch2_width = cam->v2f.fmt.pix.width;
+ cfg->ch2_height = cam->v2f.fmt.pix.height;
+ } else {
+ cfg->ch2_pix = PRP_PIX2_UNUSED;
+ cfg->ch2_width = cfg->in_width;
+ cfg->ch2_height = cfg->in_height;
+ }
+ cfg->ch2_scale.algo = 0;
+ cfg->ch2_scale.width.num = cfg->in_width;
+ cfg->ch2_scale.width.den = cfg->ch2_width;
+ cfg->ch2_scale.height.num = cfg->in_height;
+ cfg->ch2_scale.height.den = cfg->ch2_height;
+ cfg->ch2_csi = PRP_CSI_EN;
+
+ memset(cfg->scale, 0, sizeof(cfg->scale));
+ cfg->scale[0].algo = cfg->ch1_scale.algo & 3;
+ cfg->scale[1].algo = (cfg->ch1_scale.algo >> 2) & 3;
+ cfg->scale[2].algo = cfg->ch2_scale.algo & 3;
+ cfg->scale[3].algo = (cfg->ch2_scale.algo >> 2) & 3;
+
+ prp_cfg_dump(cfg);
+
+ if (prp_resize_check_ch2(cfg))
+ return -1;
+
+ if (prp_resize_check_ch1(cfg))
+ return -1;
+
+ return 0;
+}
+
+/*!
+ * @brief PrP interrupt handler
+ */
+static irqreturn_t prp_isr(int irq, void *dev_id)
+{
+ int status;
+ cam_data *cam = (cam_data *) dev_id;
+
+ status = prphw_isr();
+
+ if (g_still_on && (status & PRP_INTRSTAT_CH2BUF1)) {
+ prp_still_stop(cam);
+ cam->still_counter++;
+ wake_up_interruptible(&cam->still_queue);
+ /*
+ * Still & video capture use the same PrP channel 2.
+ * They are execlusive.
+ */
+ } else if (cam->capture_on) {
+ if (status & (PRP_INTRSTAT_CH2BUF1 | PRP_INTRSTAT_CH2BUF2)) {
+ cam->enc_callback(0, cam);
+ }
+ }
+ if (cam->overlay_on
+ && (status & (PRP_INTRSTAT_CH1BUF1 | PRP_INTRSTAT_CH1BUF2))) {
+ if (cam->rotation != V4L2_MXC_ROTATE_NONE) {
+ g_rotbuf = (status & PRP_INTRSTAT_CH1BUF1) ? 0 : 1;
+ tasklet_schedule(&prp_vf_tasklet);
+ } else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_gwinfo gwinfo;
+
+ gwinfo.enabled = 1;
+ gwinfo.alpha_value = 255;
+ gwinfo.ck_enabled = 0;
+ gwinfo.xpos = cam->win.w.left;
+ gwinfo.ypos = cam->win.w.top;
+ gwinfo.xres = cam->win.w.width;
+ gwinfo.yres = cam->win.w.height;
+ gwinfo.xres_virtual = cam->win.w.width;
+ gwinfo.vs_reversed = 0;
+ if (status & PRP_INTRSTAT_CH1BUF1)
+ gwinfo.base = (unsigned long)cam->vf_bufs[0];
+ else
+ gwinfo.base = (unsigned long)cam->vf_bufs[1];
+
+ mx2_gw_set(&gwinfo);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * @brief PrP initialization.
+ * @param dev_id Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_init(void *dev_id)
+{
+ enable_irq(MXC_INT_EMMAPRP);
+ if (request_irq(MXC_INT_EMMAPRP, prp_isr, 0, prp_dev, dev_id))
+ return -1;
+ prphw_init();
+
+ return 0;
+}
+
+/*!
+ * @brief PrP initialization.
+ * @param dev_id Pointer to cam_data structure
+ */
+void prp_exit(void *dev_id)
+{
+ prphw_exit();
+ disable_irq(MXC_INT_EMMAPRP);
+ free_irq(MXC_INT_EMMAPRP, dev_id);
+}
+
+/*!
+ * @brief Update PrP channel 2 output buffer address.
+ * @param eba Physical address for PrP output buffer
+ * @param buffer_num The PrP channel 2 buffer number to be updated
+ * @return Zero on success, others on failure
+ */
+static int prp_enc_update_eba(u32 eba, int *buffer_num)
+{
+ if (*buffer_num) {
+ g_prp_cfg.ch2_ptr2 = eba;
+ prphw_ch2ptr2(&g_prp_cfg);
+ *buffer_num = 0;
+ } else {
+ g_prp_cfg.ch2_ptr = eba;
+ prphw_ch2ptr(&g_prp_cfg);
+ *buffer_num = 1;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Enable PrP for encoding.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_enc_enable(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (prp_v4l2_cfg(&g_prp_cfg, cam))
+ return -1;
+
+ csi_enable_mclk(CSI_MCLK_ENC, true, true);
+ prphw_reset();
+
+ if (prphw_cfg(&g_prp_cfg))
+ return -1;
+
+ prphw_enable(cam->overlay_on ? (PRP_CHANNEL_1 | PRP_CHANNEL_2)
+ : PRP_CHANNEL_2);
+
+ return 0;
+}
+
+/*!
+ * @brief Disable PrP for encoding.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_enc_disable(void *private)
+{
+ prphw_disable(PRP_CHANNEL_2);
+ csi_enable_mclk(CSI_MCLK_ENC, false, false);
+
+ return 0;
+}
+
+/*!
+ * @brief Setup encoding functions.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_enc_select(void *private)
+{
+ int ret = 0;
+ cam_data *cam = (cam_data *) private;
+
+ if (cam) {
+ cam->enc_update_eba = prp_enc_update_eba;
+ cam->enc_enable = prp_enc_enable;
+ cam->enc_disable = prp_enc_disable;
+ } else
+ ret = -EIO;
+
+ return ret;
+}
+
+/*!
+ * @brief Uninstall encoding functions.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_enc_deselect(void *private)
+{
+ int ret = 0;
+ cam_data *cam = (cam_data *) private;
+
+ ret = prp_enc_disable(private);
+
+ if (cam) {
+ cam->enc_update_eba = NULL;
+ cam->enc_enable = NULL;
+ cam->enc_disable = NULL;
+ }
+
+ return ret;
+}
+
+/*!
+ * @brief Allocate memory for overlay.
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_vf_mem_alloc(cam_data * cam)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ cam->vf_bufs_size[i] = cam->win.w.width * cam->win.w.height * 2;
+ cam->vf_bufs_vaddr[i] = dma_alloc_coherent(0,
+ cam->vf_bufs_size[i],
+ &cam->vf_bufs[i],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (!cam->vf_bufs_vaddr[i]) {
+ pr_debug("Failed to alloc memory for vf.\n");
+ prp_vf_mem_free(cam);
+ return -1;
+ }
+
+ g_vaddr_vfbuf[i] =
+ ioremap_cached(cam->vf_bufs[i], cam->vf_bufs_size[i]);
+ if (!g_vaddr_vfbuf[i]) {
+ pr_debug("Failed to ioremap_cached() for vf.\n");
+ prp_vf_mem_free(cam);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Free memory for overlay.
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static void prp_vf_mem_free(cam_data * cam)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (cam->vf_bufs_vaddr[i]) {
+ dma_free_coherent(0,
+ cam->vf_bufs_size[i],
+ cam->vf_bufs_vaddr[i],
+ cam->vf_bufs[i]);
+ }
+ cam->vf_bufs[i] = 0;
+ cam->vf_bufs_vaddr[i] = 0;
+ cam->vf_bufs_size[i] = 0;
+ if (g_vaddr_vfbuf[i]) {
+ iounmap(g_vaddr_vfbuf[i]);
+ g_vaddr_vfbuf[i] = 0;
+ }
+ }
+}
+
+/*!
+ * @brief Allocate intermediate memory for overlay rotation/mirroring.
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_rot_mem_alloc(cam_data * cam)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ cam->rot_vf_buf_size[i] =
+ cam->win.w.width * cam->win.w.height * 2;
+ cam->rot_vf_bufs_vaddr[i] =
+ dma_alloc_coherent(0, cam->rot_vf_buf_size[i],
+ &cam->rot_vf_bufs[i],
+ GFP_DMA | GFP_KERNEL);
+ if (!cam->rot_vf_bufs_vaddr[i]) {
+ pr_debug("Failed to alloc memory for vf rotation.\n");
+ prp_rot_mem_free(cam);
+ return -1;
+ }
+
+ g_vaddr_rotbuf[i] =
+ ioremap_cached(cam->rot_vf_bufs[i],
+ cam->rot_vf_buf_size[i]);
+ if (!g_vaddr_rotbuf[i]) {
+ pr_debug
+ ("Failed to ioremap_cached() for rotation buffer.\n");
+ prp_rot_mem_free(cam);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Free intermedaite memory for overlay rotation/mirroring.
+ * @param cam Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static void prp_rot_mem_free(cam_data * cam)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (cam->rot_vf_bufs_vaddr[i]) {
+ dma_free_coherent(0,
+ cam->rot_vf_buf_size[i],
+ cam->rot_vf_bufs_vaddr[i],
+ cam->rot_vf_bufs[i]);
+ }
+ cam->rot_vf_bufs[i] = 0;
+ cam->rot_vf_bufs_vaddr[i] = 0;
+ cam->rot_vf_buf_size[i] = 0;
+ if (g_vaddr_rotbuf[i]) {
+ iounmap(g_vaddr_rotbuf[i]);
+ g_vaddr_rotbuf[i] = 0;
+ }
+ }
+}
+
+/*!
+ * @brief Start overlay (view finder).
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_vf_start(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ prp_vf_mem_free(cam);
+ if (prp_vf_mem_alloc(cam)) {
+ pr_info("Error to allocate vf buffer\n");
+ return -ENOMEM;
+ }
+ }
+
+ if (cam->rotation != V4L2_MXC_ROTATE_NONE) {
+ prp_rot_mem_free(cam);
+ if (prp_rot_mem_alloc(cam)) {
+ pr_info("Error to allocate rotation buffer\n");
+ prp_vf_mem_free(cam);
+ return -ENOMEM;
+ }
+ }
+
+ if (prp_v4l2_cfg(&g_prp_cfg, cam)) {
+ prp_vf_mem_free(cam);
+ prp_rot_mem_free(cam);
+ return -1;
+ }
+
+ csi_enable_mclk(CSI_MCLK_VF, true, true);
+ prphw_reset();
+
+ if (prphw_cfg(&g_prp_cfg)) {
+ prp_vf_mem_free(cam);
+ prp_rot_mem_free(cam);
+ return -1;
+ }
+ g_vfbuf = g_rotbuf = 0;
+ tasklet_init(&prp_vf_tasklet, rotation, (unsigned long)private);
+
+ prphw_enable(cam->capture_on ? (PRP_CHANNEL_1 | PRP_CHANNEL_2)
+ : PRP_CHANNEL_1);
+
+ return 0;
+}
+
+/*!
+ * @brief Stop overlay (view finder).
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_vf_stop(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ prphw_disable(PRP_CHANNEL_1);
+
+ csi_enable_mclk(CSI_MCLK_VF, false, false);
+ tasklet_kill(&prp_vf_tasklet);
+
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_gwinfo gwinfo;
+
+ /* Disable graphic window */
+ gwinfo.enabled = 0;
+ mx2_gw_set(&gwinfo);
+
+ prp_vf_mem_free(cam);
+ }
+ prp_rot_mem_free(cam);
+ if (g_vaddr_fb) {
+ iounmap(g_vaddr_fb);
+ g_vaddr_fb = 0;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Setup overlay functions.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_vf_select(void *private)
+{
+ int ret = 0;
+ cam_data *cam = (cam_data *) private;
+
+ if (cam) {
+ cam->vf_start_sdc = prp_vf_start;
+ cam->vf_stop_sdc = prp_vf_stop;
+ cam->overlay_active = false;
+ } else
+ ret = -EIO;
+
+ return ret;
+}
+
+/*!
+ * @brief Uninstall overlay functions.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_vf_deselect(void *private)
+{
+ int ret = 0;
+ cam_data *cam = (cam_data *) private;
+
+ ret = prp_vf_stop(private);
+
+ if (cam) {
+ cam->vf_start_sdc = NULL;
+ cam->vf_stop_sdc = NULL;
+ }
+
+ return ret;
+}
+
+/*!
+ * @brief Start still picture capture.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_still_start(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ g_still_on = 1;
+ g_prp_cfg.ch2_ptr = (unsigned int)cam->still_buf;
+ g_prp_cfg.ch2_ptr2 = 0;
+
+ if (prp_v4l2_cfg(&g_prp_cfg, cam))
+ return -1;
+
+ csi_enable_mclk(CSI_MCLK_RAW, true, true);
+ prphw_reset();
+
+ if (prphw_cfg(&g_prp_cfg)) {
+ g_still_on = 0;
+ return -1;
+ }
+
+ prphw_enable(cam->overlay_on ? (PRP_CHANNEL_1 | PRP_CHANNEL_2)
+ : PRP_CHANNEL_2);
+
+ return 0;
+}
+
+/*!
+ * @brief Stop still picture capture.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+static int prp_still_stop(void *private)
+{
+ prphw_disable(PRP_CHANNEL_2);
+
+ csi_enable_mclk(CSI_MCLK_RAW, false, false);
+
+ g_still_on = 0;
+
+ return 0;
+}
+
+/*!
+ * @brief Setup functions for still picture capture.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_still_select(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+
+ if (cam) {
+ cam->csi_start = prp_still_start;
+ cam->csi_stop = prp_still_stop;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Uninstall functions for still picture capture.
+ * @param private Pointer to cam_data structure
+ * @return Zero on success, others on failure
+ */
+int prp_still_deselect(void *private)
+{
+ cam_data *cam = (cam_data *) private;
+ int err = 0;
+
+ err = prp_still_stop(cam);
+
+ if (cam) {
+ cam->csi_start = NULL;
+ cam->csi_stop = NULL;
+ }
+
+ return err;
+}
+
+/*!
+ * @brief Perform software rotation or mirroring
+ * @param private Argument passed to the tasklet
+ */
+static void rotation(unsigned long private)
+{
+ char *src, *dst;
+ int width, height, s_stride, d_stride;
+ int size;
+ cam_data *cam = (cam_data *) private;
+
+ src = g_vaddr_rotbuf[g_rotbuf];
+ size = cam->rot_vf_buf_size[g_rotbuf];
+
+ if ((cam->rotation == V4L2_MXC_ROTATE_90_RIGHT)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_VFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_RIGHT_HFLIP)
+ || (cam->rotation == V4L2_MXC_ROTATE_90_LEFT)) {
+ width = cam->win.w.height;
+ height = cam->win.w.width;
+ s_stride = cam->win.w.height << 1;
+ } else {
+ width = cam->win.w.width;
+ height = cam->win.w.height;
+ s_stride = cam->win.w.width << 1;
+ }
+
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ dst = g_vaddr_vfbuf[g_vfbuf];
+ d_stride = cam->win.w.width << 1;
+ } else { /* The destination is the framebuffer */
+ struct fb_info *fb = cam->overlay_fb;
+ if (!fb)
+ return;
+ dst = g_vaddr_fb;
+ dst += cam->win.w.top * fb->var.xres_virtual
+ * (fb->var.bits_per_pixel >> 3)
+ + cam->win.w.left * (fb->var.bits_per_pixel >> 3);
+ d_stride = fb->var.xres_virtual << 1;
+ }
+
+ /*
+ * Invalidate the data in cache before performing the SW rotaion
+ * or mirroring in case the image size is less than QVGA. For image
+ * larger than QVGA it is not invalidated becase the invalidation
+ * will consume much time while we don't see any artifacts on the
+ * output if we don't perform invalidation for them.
+ * Similarly we don't flush the data after SW rotation/mirroring.
+ */
+ if (size < 320 * 240 * 2)
+ dmac_inv_range(src, src + size);
+ switch (cam->rotation) {
+ case V4L2_MXC_ROTATE_VERT_FLIP:
+ opl_vmirror_u16(src, s_stride, width, height, dst, d_stride);
+ break;
+ case V4L2_MXC_ROTATE_HORIZ_FLIP:
+ opl_hmirror_u16(src, s_stride, width, height, dst, d_stride);
+ break;
+ case V4L2_MXC_ROTATE_180:
+ opl_rotate180_u16(src, s_stride, width, height, dst, d_stride);
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT:
+ opl_rotate90_u16(src, s_stride, width, height, dst, d_stride);
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT_VFLIP:
+ opl_rotate90_vmirror_u16(src, s_stride, width, height, dst,
+ d_stride);
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT_HFLIP:
+ /* ROTATE_90_RIGHT_HFLIP = ROTATE_270_RIGHT_VFLIP */
+ opl_rotate270_vmirror_u16(src, s_stride, width, height, dst,
+ d_stride);
+ break;
+ case V4L2_MXC_ROTATE_90_LEFT:
+ opl_rotate270_u16(src, s_stride, width, height, dst, d_stride);
+ break;
+ default:
+ return;
+ }
+
+ /* Config and display the graphic window */
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_gwinfo gwinfo;
+
+ gwinfo.enabled = 1;
+ gwinfo.alpha_value = 255;
+ gwinfo.ck_enabled = 0;
+ gwinfo.xpos = cam->win.w.left;
+ gwinfo.ypos = cam->win.w.top;
+ gwinfo.xres = cam->win.w.width;
+ gwinfo.yres = cam->win.w.height;
+ gwinfo.xres_virtual = cam->win.w.width;
+ gwinfo.vs_reversed = 0;
+ gwinfo.base = (unsigned long)cam->vf_bufs[g_vfbuf];
+ mx2_gw_set(&gwinfo);
+
+ g_vfbuf = g_vfbuf ? 0 : 1;
+ }
+}
+
+/*
+ * @brief Check if the resize ratio is supported based on the input and output
+ * dimension
+ * @param input input dimension
+ * @param output output dimension
+ * @return output dimension (should equal the parameter *output*)
+ * -1 on failure
+ */
+static int check_simple(scale_t * scale, int input, int output)
+{
+ unsigned short int_out; /* PrP internel width or height */
+ unsigned short orig_out = output;
+
+ if (prp_scale(scale, input, output, input, &orig_out, &int_out, 0))
+ return -1; /* resize failed */
+ else
+ return int_out;
+}
+
+/*
+ * @brief Check if the resize ratio is supported based on the input and output
+ * dimension
+ * @param input input dimension
+ * @param output output dimension
+ * @return output dimension, may be rounded.
+ * -1 on failure
+ */
+static int check_simple_retry(scale_t * scale, int input, int output)
+{
+ unsigned short int_out; /* PrP internel width or height */
+ unsigned short orig_out = output;
+
+ if (prp_scale(scale, input, output, input, &orig_out, &int_out,
+ SCALE_RETRY))
+ return -1; /* resize failed */
+ else
+ return int_out;
+}
+
+/*!
+ * @brief Check if the resize ratio is supported by PrP channel 1
+ * @param cfg Pointer to emma_prp_cfg structure
+ * @return Zero on success, others on failure
+ */
+static int prp_resize_check_ch1(emma_prp_cfg * cfg)
+{
+ int in_w, in_h, ch1_w, ch1_h, ch2_w, ch2_h, w, h;
+ scale_t *pscale = &cfg->scale[0]; /* Ch1 width resize coeff */
+
+ if (cfg->ch1_pix == PRP_PIX1_UNUSED)
+ return 0;
+
+ in_w = cfg->in_width;
+ in_h = cfg->in_height;
+ ch1_w = cfg->ch1_width;
+ ch1_h = cfg->ch1_height;
+ ch2_w = cfg->ch2_width;
+ ch2_h = cfg->ch2_height;
+
+ /*
+ * For channel 1, try parallel resize first. If the resize
+ * ratio is not exactly supported, try cascade resize. If it
+ * still fails, use parallel resize but with rounded value.
+ */
+ w = check_simple(pscale, in_w, ch1_w);
+ h = check_simple(pscale + 1, in_h, ch1_h);
+ if ((w == ch1_w) && (h == ch1_h))
+ goto exit_parallel;
+
+ if (cfg->ch2_pix != PRP_PIX2_UNUSED) {
+ /*
+ * Channel 2 is already used. The pscale is still pointing
+ * to ch1 resize coeff for temporary use.
+ */
+ w = check_simple(pscale, in_w, ch2_w);
+ h = check_simple(pscale + 1, in_h, ch2_h);
+ if ((w == ch2_w) && (h == ch2_h)) {
+ /* Try cascade resize now */
+ w = check_simple(pscale, ch2_w, ch1_w);
+ h = check_simple(pscale + 1, ch2_h, ch1_h);
+ if ((w == ch1_w) && (h == ch1_h))
+ goto exit_cascade;
+ }
+ } else {
+ /*
+ * Try cascade resize for width, width is multiple of 2.
+ * Channel 2 is not used. So we have more values to pick
+ * for channel 2 resize.
+ */
+ for (w = in_w - 2; w > ch1_w; w -= 2) {
+ /* Ch2 width resize */
+ if (check_simple(pscale + 2, in_w, w) != w)
+ continue;
+ /* Ch1 width resize */
+ if (check_simple(pscale, w, ch1_w) != ch1_w)
+ continue;
+ break;
+ }
+ if ((ch2_w = w) > ch1_w) {
+ /* try cascade resize for height */
+ for (h = in_h - 1; h > ch1_h; h--) {
+ /* Ch2 height resize */
+ if (check_simple(pscale + 3, in_h, h) != h)
+ continue;
+ /* Ch1 height resize */
+ if (check_simple(pscale + 1, h, ch1_h) != ch1_h)
+ continue;
+ break;
+ }
+ if ((ch2_h = h) > ch1_h)
+ goto exit_cascade;
+ }
+ }
+
+ /* Have to try parallel resize again and round the dimensions */
+ w = check_simple_retry(pscale, in_w, ch1_w);
+ h = check_simple_retry(pscale + 1, in_h, ch1_h);
+ if ((w != -1) && (h != -1))
+ goto exit_parallel;
+
+ pr_debug("Ch1 resize error.\n");
+ return -1;
+
+ exit_parallel:
+ cfg->ch1_scale.algo |= PRP_ALGO_BYPASS;
+ pr_debug("ch1 parallel resize.\n");
+ pr_debug("original width = %d internel width = %d\n", ch1_w, w);
+ pr_debug("original height = %d internel height = %d\n", ch1_h, h);
+ return 0;
+
+ exit_cascade:
+ cfg->ch1_scale.algo &= ~PRP_ALGO_BYPASS;
+ pr_debug("ch1 cascade resize.\n");
+ pr_debug("[width] in : ch2 : ch1=%d : %d : %d\n", in_w, ch2_w, ch1_w);
+ pr_debug("[height] in : ch2 : ch1=%d : %d : %d\n", in_h, ch2_h, ch1_h);
+ return 0;
+}
+
+/*!
+ * @brief Check if the resize ratio is supported by PrP channel 2
+ * @param cfg Pointer to emma_prp_cfg structure
+ * @return Zero on success, others on failure
+ */
+static int prp_resize_check_ch2(emma_prp_cfg * cfg)
+{
+ int w, h;
+ scale_t *pscale = &cfg->scale[2]; /* Ch2 width resize coeff */
+
+ if (cfg->ch2_pix == PRP_PIX2_UNUSED)
+ return 0;
+
+ w = check_simple_retry(pscale, cfg->in_width, cfg->ch2_width);
+ h = check_simple_retry(pscale + 1, cfg->in_height, cfg->ch2_height);
+ if ((w != -1) && (h != -1)) {
+ pr_debug("Ch2 resize.\n");
+ pr_debug("Original width = %d internel width = %d\n",
+ cfg->ch2_width, w);
+ pr_debug("Original height = %d internel height = %d\n",
+ cfg->ch2_height, h);
+ return 0;
+ } else {
+ pr_debug("Ch2 resize error.\n");
+ return -1;
+ }
+}
diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.c b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c
new file mode 100644
index 000000000000..1852d702e505
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c
@@ -0,0 +1,2593 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file drivers/media/video/mxc/capture/mxc_v4l2_capture.c
+ *
+ * @brief Mxc Video For Linux 2 driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <linux/mxcfb.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+static int video_nr = -1;
+static cam_data *g_cam;
+
+/*! This data is used for the output to the display. */
+#define MXC_V4L2_CAPTURE_NUM_OUTPUTS 3
+#define MXC_V4L2_CAPTURE_NUM_INPUTS 2
+static struct v4l2_output mxc_capture_outputs[MXC_V4L2_CAPTURE_NUM_OUTPUTS] = {
+ {
+ .index = 0,
+ .name = "DISP3 BG",
+ .type = V4L2_OUTPUT_TYPE_ANALOG,
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN,
+ },
+ {
+ .index = 1,
+ .name = "DISP0",
+ .type = V4L2_OUTPUT_TYPE_ANALOG,
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN,
+ },
+ {
+ .index = 2,
+ .name = "DISP3 FG",
+ .type = V4L2_OUTPUT_TYPE_ANALOG,
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN,
+ },
+};
+
+static struct v4l2_input mxc_capture_inputs[MXC_V4L2_CAPTURE_NUM_INPUTS] = {
+ {
+ .index = 0,
+ .name = "CSI IC MEM",
+ .type = V4L2_INPUT_TYPE_CAMERA,
+ .audioset = 0,
+ .tuner = 0,
+ .std = V4L2_STD_UNKNOWN,
+ .status = 0,
+ },
+ {
+ .index = 1,
+ .name = "CSI MEM",
+ .type = V4L2_INPUT_TYPE_CAMERA,
+ .audioset = 0,
+ .tuner = 0,
+ .std = V4L2_STD_UNKNOWN,
+ .status = V4L2_IN_ST_NO_POWER,
+ },
+};
+
+/*! List of TV input video formats supported. The video formats is corresponding
+ * to the v4l2_id in video_fmt_t.
+ * Currently, only PAL and NTSC is supported. Needs to be expanded in the
+ * future.
+ */
+typedef enum {
+ TV_NTSC = 0, /*!< Locked on (M) NTSC video signal. */
+ TV_PAL, /*!< (B, G, H, I, N)PAL video signal. */
+ TV_NOT_LOCKED, /*!< Not locked on a signal. */
+} video_fmt_idx;
+
+/*! Number of video standards supported (including 'not locked' signal). */
+#define TV_STD_MAX (TV_NOT_LOCKED + 1)
+
+/*! Video format structure. */
+typedef struct {
+ int v4l2_id; /*!< Video for linux ID. */
+ char name[16]; /*!< Name (e.g., "NTSC", "PAL", etc.) */
+ u16 raw_width; /*!< Raw width. */
+ u16 raw_height; /*!< Raw height. */
+ u16 active_width; /*!< Active width. */
+ u16 active_height; /*!< Active height. */
+ u16 active_top; /*!< Active top. */
+ u16 active_left; /*!< Active left. */
+} video_fmt_t;
+
+/*!
+ * Description of video formats supported.
+ *
+ * PAL: raw=720x625, active=720x576.
+ * NTSC: raw=720x525, active=720x480.
+ */
+static video_fmt_t video_fmts[] = {
+ { /*! NTSC */
+ .v4l2_id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .raw_width = 720 - 1, /* SENS_FRM_WIDTH */
+ .raw_height = 288 - 1, /* SENS_FRM_HEIGHT */
+ .active_width = 720, /* ACT_FRM_WIDTH plus 1 */
+ .active_height = (480 / 2), /* ACT_FRM_HEIGHT plus 1 */
+ .active_top = 12,
+ .active_left = 0,
+ },
+ { /*! (B, G, H, I, N) PAL */
+ .v4l2_id = V4L2_STD_PAL,
+ .name = "PAL",
+ .raw_width = 720 - 1,
+ .raw_height = (576 / 2) + 24 * 2 - 1,
+ .active_width = 720,
+ .active_height = (576 / 2),
+ .active_top = 0,
+ .active_left = 0,
+ },
+ { /*! Unlocked standard */
+ .v4l2_id = V4L2_STD_ALL,
+ .name = "Autodetect",
+ .raw_width = 720 - 1,
+ .raw_height = (576 / 2) + 24 * 2 - 1,
+ .active_width = 720,
+ .active_height = (576 / 2),
+ .active_top = 0,
+ .active_left = 0,
+ },
+};
+
+/*!* Standard index of TV. */
+static video_fmt_idx video_index = TV_NOT_LOCKED;
+
+static int mxc_v4l2_master_attach(struct v4l2_int_device *slave);
+static void mxc_v4l2_master_detach(struct v4l2_int_device *slave);
+static u8 camera_power(cam_data *cam, bool cameraOn);
+
+/*! Information about this driver. */
+static struct v4l2_int_master mxc_v4l2_master = {
+ .attach = mxc_v4l2_master_attach,
+ .detach = mxc_v4l2_master_detach,
+};
+
+static struct v4l2_int_device mxc_v4l2_int_device = {
+ .module = THIS_MODULE,
+ .name = "mxc_v4l2_cap",
+ .type = v4l2_int_type_master,
+ .u = {
+ .master = &mxc_v4l2_master,
+ },
+};
+
+/***************************************************************************
+ * Functions for handling Frame buffers.
+ **************************************************************************/
+
+/*!
+ * Free frame buffers
+ *
+ * @param cam Structure cam_data *
+ *
+ * @return status 0 success.
+ */
+static int mxc_free_frame_buf(cam_data *cam)
+{
+ int i;
+
+ pr_debug("MVC: In mxc_free_frame_buf\n");
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ if (cam->frame[i].vaddress != 0) {
+ dma_free_coherent(0, cam->frame[i].buffer.length,
+ cam->frame[i].vaddress,
+ cam->frame[i].paddress);
+ cam->frame[i].vaddress = 0;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * Allocate frame buffers
+ *
+ * @param cam Structure cam_data*
+ * @param count int number of buffer need to allocated
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_frame_buf(cam_data *cam, int count)
+{
+ int i;
+
+ pr_debug("In MVC:mxc_allocate_frame_buf - size=%d\n",
+ cam->v2f.fmt.pix.sizeimage);
+
+ for (i = 0; i < count; i++) {
+ cam->frame[i].vaddress =
+ dma_alloc_coherent(0,
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+ &cam->frame[i].paddress,
+ GFP_DMA | GFP_KERNEL);
+ if (cam->frame[i].vaddress == 0) {
+ pr_err("ERROR: v4l2 capture: "
+ "mxc_allocate_frame_buf failed.\n");
+ mxc_free_frame_buf(cam);
+ return -ENOBUFS;
+ }
+ cam->frame[i].buffer.index = i;
+ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+ cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buffer.length =
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
+ cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buffer.m.offset = cam->frame[i].paddress;
+ cam->frame[i].index = i;
+ }
+
+ return 0;
+}
+
+/*!
+ * Free frame buffers status
+ *
+ * @param cam Structure cam_data *
+ *
+ * @return none
+ */
+static void mxc_free_frames(cam_data *cam)
+{
+ int i;
+
+ pr_debug("In MVC:mxc_free_frames\n");
+
+ for (i = 0; i < FRAME_NUM; i++) {
+ cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED;
+ }
+
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+}
+
+/*!
+ * Return the buffer status
+ *
+ * @param cam Structure cam_data *
+ * @param buf Structure v4l2_buffer *
+ *
+ * @return status 0 success, EINVAL failed.
+ */
+static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf)
+{
+ pr_debug("In MVC:mxc_v4l2_buffer_status\n");
+
+ if (buf->index < 0 || buf->index >= FRAME_NUM) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l2_buffer_status buffers "
+ "not allocated\n");
+ return -EINVAL;
+ }
+
+ memcpy(buf, &(cam->frame[buf->index].buffer), sizeof(*buf));
+ return 0;
+}
+
+/***************************************************************************
+ * Functions for handling the video stream.
+ **************************************************************************/
+
+/*!
+ * Indicates whether the palette is supported.
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ return ((palette == V4L2_PIX_FMT_RGB565) ||
+ (palette == V4L2_PIX_FMT_BGR24) ||
+ (palette == V4L2_PIX_FMT_RGB24) ||
+ (palette == V4L2_PIX_FMT_BGR32) ||
+ (palette == V4L2_PIX_FMT_RGB32) ||
+ (palette == V4L2_PIX_FMT_YUV422P) ||
+ (palette == V4L2_PIX_FMT_UYVY) ||
+ (palette == V4L2_PIX_FMT_YUV420) ||
+ (palette == V4L2_PIX_FMT_NV12));
+}
+
+/*!
+ * Start the encoder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_streamon(cam_data *cam)
+{
+ struct mxc_v4l_frame *frame;
+ int err = 0;
+
+ pr_debug("In MVC:mxc_streamon\n");
+
+ if (NULL == cam) {
+ pr_err("ERROR! cam parameter is NULL\n");
+ return -1;
+ }
+
+ if (list_empty(&cam->ready_q)) {
+ pr_err("ERROR: v4l2 capture: mxc_streamon buffer has not been "
+ "queued yet\n");
+ return -EINVAL;
+ }
+
+ cam->capture_pid = current->pid;
+
+ if (cam->enc_enable) {
+ err = cam->enc_enable(cam);
+ if (err != 0) {
+ return err;
+ }
+ }
+
+ cam->ping_pong_csi = 0;
+ if (cam->enc_update_eba) {
+ frame =
+ list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&frame->queue, &cam->working_q);
+ err = cam->enc_update_eba(frame->buffer.m.offset,
+ &cam->ping_pong_csi);
+
+ frame =
+ list_entry(cam->ready_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&frame->queue, &cam->working_q);
+ err |= cam->enc_update_eba(frame->buffer.m.offset,
+ &cam->ping_pong_csi);
+ } else {
+ return -EINVAL;
+ }
+
+ cam->capture_on = true;
+
+ return err;
+}
+
+/*!
+ * Shut down the encoder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_streamoff(cam_data *cam)
+{
+ int err = 0;
+
+ pr_debug("In MVC:mxc_streamoff\n");
+
+ if (cam->capture_on == false)
+ return 0;
+
+ if (cam->enc_disable) {
+ err = cam->enc_disable(cam);
+ }
+ mxc_free_frames(cam);
+ mxc_capture_inputs[cam->current_input].status |= V4L2_IN_ST_NO_POWER;
+ cam->capture_on = false;
+ return err;
+}
+
+/*!
+ * Valid and adjust the overlay window size, position
+ *
+ * @param cam structure cam_data *
+ * @param win struct v4l2_window *
+ *
+ * @return 0
+ */
+static int verify_preview(cam_data *cam, struct v4l2_window *win)
+{
+ int i = 0, width_bound = 0, height_bound = 0;
+ int *width, *height;
+ struct fb_info *bg_fbi = NULL;
+ bool foregound_fb;
+
+ pr_debug("In MVC: verify_preview\n");
+
+ do {
+ cam->overlay_fb = (struct fb_info *)registered_fb[i];
+ if (cam->overlay_fb == NULL) {
+ pr_err("ERROR: verify_preview frame buffer NULL.\n");
+ return -1;
+ }
+ if (strcmp(cam->overlay_fb->fix.id, "DISP3 BG") == 0)
+ bg_fbi = cam->overlay_fb;
+ if (strcmp(cam->overlay_fb->fix.id,
+ mxc_capture_outputs[cam->output].name) == 0) {
+ if (strcmp(cam->overlay_fb->fix.id, "DISP3 FG") == 0)
+ foregound_fb = true;
+ break;
+ }
+ } while (++i < FB_MAX);
+
+ if (foregound_fb) {
+ width_bound = bg_fbi->var.xres;
+ height_bound = bg_fbi->var.yres;
+
+ if (win->w.width + win->w.left > bg_fbi->var.xres ||
+ win->w.height + win->w.top > bg_fbi->var.yres) {
+ pr_err("ERROR: FG window position exceeds.\n");
+ return -1;
+ }
+ } else {
+ /* 4 bytes alignment for BG */
+ width_bound = cam->overlay_fb->var.xres;
+ height_bound = cam->overlay_fb->var.yres;
+
+ if (cam->overlay_fb->var.bits_per_pixel == 24) {
+ win->w.left -= win->w.left % 4;
+ } else if (cam->overlay_fb->var.bits_per_pixel == 16) {
+ win->w.left -= win->w.left % 2;
+ }
+
+ if (win->w.width + win->w.left > cam->overlay_fb->var.xres)
+ win->w.width = cam->overlay_fb->var.xres - win->w.left;
+ if (win->w.height + win->w.top > cam->overlay_fb->var.yres)
+ win->w.height = cam->overlay_fb->var.yres - win->w.top;
+ }
+
+ /* stride line limitation */
+ win->w.height -= win->w.height % 8;
+ win->w.width -= win->w.width % 8;
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ height = &win->w.width;
+ width = &win->w.height;
+ } else {
+ width = &win->w.width;
+ height = &win->w.height;
+ }
+
+ if ((cam->crop_bounds.width / *width > 8) ||
+ ((cam->crop_bounds.width / *width == 8) &&
+ (cam->crop_bounds.width % *width))) {
+ *width = cam->crop_bounds.width / 8;
+ if (*width % 8)
+ *width += 8 - *width % 8;
+ if (*width + win->w.left > width_bound) {
+ pr_err("ERROR: v4l2 capture: width exceeds "
+ "resize limit.\n");
+ return -1;
+ }
+ pr_err("ERROR: v4l2 capture: width exceeds limit. "
+ "Resize to %d.\n",
+ *width);
+ }
+
+ if ((cam->crop_bounds.height / *height > 8) ||
+ ((cam->crop_bounds.height / *height == 8) &&
+ (cam->crop_bounds.height % *height))) {
+ *height = cam->crop_bounds.height / 8;
+ if (*height % 8)
+ *height += 8 - *height % 8;
+ if (*height + win->w.top > height_bound) {
+ pr_err("ERROR: v4l2 capture: height exceeds "
+ "resize limit.\n");
+ return -1;
+ }
+ pr_err("ERROR: v4l2 capture: height exceeds limit "
+ "resize to %d.\n",
+ *height);
+ }
+
+ return 0;
+}
+
+/*!
+ * start the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int start_preview(cam_data *cam)
+{
+ int err = 0;
+
+ pr_debug("MVC: start_preview\n");
+
+#if defined(CONFIG_MXC_IPU_PRP_VF_SDC) || defined(CONFIG_MXC_IPU_PRP_VF_SDC_MODULE)
+ pr_debug(" This is an SDC display\n");
+ if (cam->output == 0 || cam->output == 2) {
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY)
+ err = prp_vf_sdc_select(cam);
+ else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY)
+ err = prp_vf_sdc_select_bg(cam);
+ if (err != 0)
+ return err;
+
+ err = cam->vf_start_sdc(cam);
+ }
+#endif
+
+#if defined(CONFIG_MXC_IPU_PRP_VF_ADC) || defined(CONFIG_MXC_IPU_PRP_VF_ADC_MODULE)
+ pr_debug(" This is an ADC display\n");
+ if (cam->output == 1) {
+ err = prp_vf_adc_select(cam);
+ if (err != 0)
+ return err;
+
+ err = cam->vf_start_adc(cam);
+ }
+#endif
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__,
+ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+ __func__,
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+ __func__,
+ cam->crop_defrect.width, cam->crop_defrect.height);
+ pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+ __func__,
+ cam->crop_current.width, cam->crop_current.height);
+
+ return err;
+}
+
+/*!
+ * shut down the viewfinder job
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static int stop_preview(cam_data *cam)
+{
+ int err = 0;
+
+ pr_debug("MVC: stop preview\n");
+
+#if defined(CONFIG_MXC_IPU_PRP_VF_ADC) || defined(CONFIG_MXC_IPU_PRP_VF_ADC_MODULE)
+ if (cam->output == 1) {
+ err = prp_vf_adc_deselect(cam);
+ }
+#endif
+
+#if defined(CONFIG_MXC_IPU_PRP_VF_SDC) || defined(CONFIG_MXC_IPU_PRP_VF_SDC_MODULE)
+ if (cam->output == 0 || cam->output == 2) {
+ if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY)
+ err = prp_vf_sdc_deselect(cam);
+ else if (cam->v4l2_fb.flags == V4L2_FBUF_FLAG_PRIMARY)
+ err = prp_vf_sdc_deselect_bg(cam);
+ }
+#endif
+
+ return err;
+}
+
+/***************************************************************************
+ * VIDIOC Functions.
+ **************************************************************************/
+
+/*!
+ * V4L2 - mxc_v4l2_g_fmt function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+
+ pr_debug("In MVC: mxc_v4l2_g_fmt type=%d\n", f->type);
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ f->fmt.pix = cam->v2f.fmt.pix;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+ f->fmt.win = cam->win;
+ break;
+ default:
+ pr_debug(" type is invalid\n");
+ retval = -EINVAL;
+ }
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__,
+ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+ __func__,
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+ __func__,
+ cam->crop_defrect.width, cam->crop_defrect.height);
+ pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+ __func__,
+ cam->crop_current.width, cam->crop_current.height);
+
+ return retval;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_fmt function
+ *
+ * @param cam structure cam_data *
+ *
+ * @param f structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f)
+{
+ int retval = 0;
+ int size = 0;
+ int bytesperline = 0;
+ int *width, *height;
+
+ pr_debug("In MVC: mxc_v4l2_s_fmt\n");
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l2_s_fmt: format "
+ "not supported\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Force the capture window resolution to be crop bounds
+ * for CSI MEM input mode.
+ */
+ if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI MEM") == 0) {
+ f->fmt.pix.width = cam->crop_bounds.width;
+ f->fmt.pix.height = cam->crop_bounds.height;
+ }
+
+ /* Handle case where size requested is larger than cuurent
+ * camera setting. */
+ if ((f->fmt.pix.width > cam->crop_bounds.width)
+ || (f->fmt.pix.height > cam->crop_bounds.height)) {
+ /* Need the logic here, calling vidioc_s_param if
+ * camera can change. */
+ /* For the moment, just return an error. */
+ return -EINVAL;
+ }
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ height = &f->fmt.pix.width;
+ width = &f->fmt.pix.height;
+ } else {
+ width = &f->fmt.pix.width;
+ height = &f->fmt.pix.height;
+ }
+
+ /* stride line limitation */
+ *width -= *width % 8;
+ *height -= *height % 8;
+
+ if ((cam->crop_bounds.width / *width > 8) ||
+ ((cam->crop_bounds.width / *width == 8) &&
+ (cam->crop_bounds.width % *width))) {
+ *width = cam->crop_bounds.width / 8;
+ if (*width % 8)
+ *width += 8 - *width % 8;
+ pr_err("ERROR: v4l2 capture: width exceeds limit "
+ "resize to %d.\n",
+ *width);
+ }
+
+ if ((cam->crop_bounds.height / *height > 8) ||
+ ((cam->crop_bounds.height / *height == 8) &&
+ (cam->crop_bounds.height % *height))) {
+ *height = cam->crop_bounds.height / 8;
+ if (*height % 8)
+ *height += 8 - *height % 8;
+ pr_err("ERROR: v4l2 capture: height exceeds limit "
+ "resize to %d.\n",
+ *height);
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ bytesperline = f->fmt.pix.width * 2;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ size = f->fmt.pix.width * f->fmt.pix.height * 3;
+ bytesperline = f->fmt.pix.width * 3;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ size = f->fmt.pix.width * f->fmt.pix.height * 3;
+ bytesperline = f->fmt.pix.width * 3;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ size = f->fmt.pix.width * f->fmt.pix.height * 4;
+ bytesperline = f->fmt.pix.width * 4;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ size = f->fmt.pix.width * f->fmt.pix.height * 4;
+ bytesperline = f->fmt.pix.width * 4;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ bytesperline = f->fmt.pix.width;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ size = f->fmt.pix.width * f->fmt.pix.height * 2;
+ bytesperline = f->fmt.pix.width * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+ bytesperline = f->fmt.pix.width;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2;
+ bytesperline = f->fmt.pix.width;
+ break;
+ default:
+ break;
+ }
+
+ if (f->fmt.pix.bytesperline < bytesperline) {
+ f->fmt.pix.bytesperline = bytesperline;
+ } else {
+ bytesperline = f->fmt.pix.bytesperline;
+ }
+
+ if (f->fmt.pix.sizeimage < size) {
+ f->fmt.pix.sizeimage = size;
+ } else {
+ size = f->fmt.pix.sizeimage;
+ }
+
+ cam->v2f.fmt.pix = f->fmt.pix;
+
+ if (cam->v2f.fmt.pix.priv != 0) {
+ if (copy_from_user(&cam->offset,
+ (void *)cam->v2f.fmt.pix.priv,
+ sizeof(cam->offset))) {
+ retval = -EFAULT;
+ break;
+ }
+ }
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n");
+ retval = verify_preview(cam, &f->fmt.win);
+ cam->win = f->fmt.win;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__,
+ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+ __func__,
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+ __func__,
+ cam->crop_defrect.width, cam->crop_defrect.height);
+ pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+ __func__,
+ cam->crop_current.width, cam->crop_current.height);
+
+ return retval;
+}
+
+/*!
+ * get control param
+ *
+ * @param cam structure cam_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_ctrl(cam_data *cam, struct v4l2_control *c)
+{
+ int status = 0;
+
+ pr_debug("In MVC:mxc_v4l2_g_ctrl\n");
+
+ /* probably don't need to store the values that can be retrieved,
+ * locally, but they are for now. */
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ /* This is handled in the ipu. */
+ if (cam->rotation == IPU_ROTATE_HORIZ_FLIP)
+ c->value = 1;
+ break;
+ case V4L2_CID_VFLIP:
+ /* This is handled in the ipu. */
+ if (cam->rotation == IPU_ROTATE_VERT_FLIP)
+ c->value = 1;
+ break;
+ case V4L2_CID_MXC_ROT:
+ /* This is handled in the ipu. */
+ c->value = cam->rotation;
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ c->value = cam->bright;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->bright = c->value;
+ break;
+ case V4L2_CID_HUE:
+ c->value = cam->hue;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->hue = c->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = cam->contrast;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->contrast = c->value;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = cam->saturation;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->saturation = c->value;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ c->value = cam->red;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->red = c->value;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ c->value = cam->blue;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->blue = c->value;
+ break;
+ case V4L2_CID_BLACK_LEVEL:
+ c->value = cam->ae_mode;
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ cam->ae_mode = c->value;
+ break;
+ default:
+ status = vidioc_int_g_ctrl(cam->sensor, c);
+ }
+
+ return status;
+}
+
+/*!
+ * V4L2 - set_control function
+ * V4L2_CID_PRIVATE_BASE is the extention for IPU preprocessing.
+ * 0 for normal operation
+ * 1 for vertical flip
+ * 2 for horizontal flip
+ * 3 for horizontal and vertical flip
+ * 4 for 90 degree rotation
+ * @param cam structure cam_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c)
+{
+ int ret = 0;
+ int tmp_rotation = IPU_ROTATE_NONE;
+
+ pr_debug("In MVC:mxc_v4l2_s_ctrl\n");
+
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ /* This is done by the IPU */
+ if (c->value == 1) {
+ if ((cam->rotation != IPU_ROTATE_VERT_FLIP) &&
+ (cam->rotation != IPU_ROTATE_180))
+ cam->rotation = IPU_ROTATE_HORIZ_FLIP;
+ else
+ cam->rotation = IPU_ROTATE_180;
+ } else {
+ if (cam->rotation == IPU_ROTATE_HORIZ_FLIP)
+ cam->rotation = IPU_ROTATE_NONE;
+ if (cam->rotation == IPU_ROTATE_180)
+ cam->rotation = IPU_ROTATE_VERT_FLIP;
+ }
+ break;
+ case V4L2_CID_VFLIP:
+ /* This is done by the IPU */
+ if (c->value == 1) {
+ if ((cam->rotation != IPU_ROTATE_HORIZ_FLIP) &&
+ (cam->rotation != IPU_ROTATE_180))
+ cam->rotation = IPU_ROTATE_VERT_FLIP;
+ else
+ cam->rotation = IPU_ROTATE_180;
+ } else {
+ if (cam->rotation == IPU_ROTATE_VERT_FLIP)
+ cam->rotation = IPU_ROTATE_NONE;
+ if (cam->rotation == IPU_ROTATE_180)
+ cam->rotation = IPU_ROTATE_HORIZ_FLIP;
+ }
+ break;
+ case V4L2_CID_MXC_ROT:
+ case V4L2_CID_MXC_VF_ROT:
+ /* This is done by the IPU */
+ switch (c->value) {
+ case V4L2_MXC_ROTATE_NONE:
+ tmp_rotation = IPU_ROTATE_NONE;
+ break;
+ case V4L2_MXC_ROTATE_VERT_FLIP:
+ tmp_rotation = IPU_ROTATE_VERT_FLIP;
+ break;
+ case V4L2_MXC_ROTATE_HORIZ_FLIP:
+ tmp_rotation = IPU_ROTATE_HORIZ_FLIP;
+ break;
+ case V4L2_MXC_ROTATE_180:
+ tmp_rotation = IPU_ROTATE_180;
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT:
+ tmp_rotation = IPU_ROTATE_90_RIGHT;
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT_VFLIP:
+ tmp_rotation = IPU_ROTATE_90_RIGHT_VFLIP;
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT_HFLIP:
+ tmp_rotation = IPU_ROTATE_90_RIGHT_HFLIP;
+ break;
+ case V4L2_MXC_ROTATE_90_LEFT:
+ tmp_rotation = IPU_ROTATE_90_LEFT;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (c->id == V4L2_CID_MXC_VF_ROT)
+ cam->vf_rotation = tmp_rotation;
+ else
+ cam->rotation = tmp_rotation;
+
+ break;
+ case V4L2_CID_HUE:
+ cam->hue = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_CONTRAST:
+ cam->contrast = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ cam->bright = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_SATURATION:
+ cam->saturation = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ cam->red = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ cam->blue = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_EXPOSURE:
+ cam->ae_mode = c->value;
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ ret = vidioc_int_s_ctrl(cam->sensor, c);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ break;
+ case V4L2_CID_MXC_FLASH:
+#ifdef CONFIG_MXC_IPU_V1
+ ipu_csi_flash_strobe(true);
+#endif
+ break;
+ default:
+ pr_debug(" default case\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_param function
+ * Allows setting of capturemode and frame rate.
+ *
+ * @param cam structure cam_data *
+ * @param parm structure v4l2_streamparm *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm)
+{
+ struct v4l2_ifparm ifparm;
+ struct v4l2_format cam_fmt;
+ struct v4l2_streamparm currentparm;
+ ipu_csi_signal_cfg_t csi_param;
+ int err = 0;
+
+ pr_debug("In mxc_v4l2_s_param\n");
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_err(KERN_ERR "mxc_v4l2_s_param invalid type\n");
+ return -EINVAL;
+ }
+
+ /* Stop the viewfinder */
+ if (cam->overlay_on == true) {
+ stop_preview(cam);
+ }
+
+ currentparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* First check that this device can support the changes requested. */
+ err = vidioc_int_g_parm(cam->sensor, &currentparm);
+ if (err) {
+ pr_err("%s: vidioc_int_g_parm returned an error %d\n",
+ __func__, err);
+ goto exit;
+ }
+
+ pr_debug(" Current capabilities are %x\n",
+ currentparm.parm.capture.capability);
+ pr_debug(" Current capturemode is %d change to %d\n",
+ currentparm.parm.capture.capturemode,
+ parm->parm.capture.capturemode);
+ pr_debug(" Current framerate is %d change to %d\n",
+ currentparm.parm.capture.timeperframe.denominator,
+ parm->parm.capture.timeperframe.denominator);
+
+ /* This will change any camera settings needed. */
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ err = vidioc_int_s_parm(cam->sensor, parm);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ if (err) {
+ pr_err("%s: vidioc_int_s_parm returned an error %d\n",
+ __func__, err);
+ goto exit;
+ }
+
+ /* If resolution changed, need to re-program the CSI */
+ /* Get new values. */
+ vidioc_int_g_ifparm(cam->sensor, &ifparm);
+
+ csi_param.data_width = 0;
+ csi_param.clk_mode = 0;
+ csi_param.ext_vsync = 0;
+ csi_param.Vsync_pol = 0;
+ csi_param.Hsync_pol = 0;
+ csi_param.pixclk_pol = 0;
+ csi_param.data_pol = 0;
+ csi_param.sens_clksrc = 0;
+ csi_param.pack_tight = 0;
+ csi_param.force_eof = 0;
+ csi_param.data_en_pol = 0;
+ csi_param.data_fmt = 0;
+ csi_param.csi = 0;
+ csi_param.mclk = 0;
+
+ /* This may not work on other platforms. Check when adding a new one.*/
+ pr_debug(" clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr);
+ if (ifparm.u.bt656.clock_curr == 0) {
+ csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE;
+ } else {
+ csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK;
+ }
+
+ csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;
+
+ if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) {
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+ } else if (ifparm.u.bt656.mode
+ == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) {
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_10;
+ } else {
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+ }
+
+ csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;
+ csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;
+ csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct;
+
+ /* if the capturemode changed, the size bounds will have changed. */
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
+ pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n",
+ cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
+
+ csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;
+
+ cam->crop_bounds.top = cam->crop_bounds.left = 0;
+ cam->crop_bounds.width = cam_fmt.fmt.pix.width;
+ cam->crop_bounds.height = cam_fmt.fmt.pix.height;
+
+ /* This essentially loses the data at the left and bottom of the image
+ * giving a digital zoom image, if crop_current is less than the full
+ * size of the image. */
+ ipu_csi_set_window_size(cam->crop_current.width,
+ cam->crop_current.height, cam->csi);
+ ipu_csi_set_window_pos(cam->crop_current.left,
+ cam->crop_current.top,
+ cam->csi);
+ ipu_csi_init_interface(cam->crop_bounds.width,
+ cam->crop_bounds.height,
+ cam_fmt.fmt.pix.pixelformat, csi_param);
+
+
+exit:
+ if (cam->overlay_on == true)
+ start_preview(cam);
+
+ return err;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_s_std function
+ *
+ * Sets the TV standard to be used.
+ *
+ * @param cam structure cam_data *
+ * @param parm structure v4l2_streamparm *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_s_std(cam_data *cam, v4l2_std_id e)
+{
+ bool change = false;
+
+ if (e != cam->standard.id) {
+ change = true;
+ }
+
+ pr_debug("In mxc_v4l2_s_std %Lx\n", e);
+ if (e == V4L2_STD_PAL) {
+ pr_debug(" Setting standard to PAL %Lx\n", V4L2_STD_PAL);
+ cam->standard.id = V4L2_STD_PAL;
+ video_index = TV_PAL;
+ cam->crop_current.top = 0;
+ } else if (e == V4L2_STD_NTSC) {
+ pr_debug(" Setting standard to NTSC %Lx\n",
+ V4L2_STD_NTSC);
+ /* Get rid of the white dot line in NTSC signal input */
+ cam->standard.id = V4L2_STD_NTSC;
+ video_index = TV_NTSC;
+ cam->crop_current.top = 12;
+ } else {
+ cam->standard.id = V4L2_STD_ALL;
+ video_index = TV_NOT_LOCKED;
+ cam->crop_current.top = 0;
+ pr_err("ERROR: unrecognized std! %Lx (PAL=%Lx, NTSC=%Lx\n",
+ e, V4L2_STD_PAL, V4L2_STD_NTSC);
+ }
+
+ cam->standard.index = video_index;
+ strcpy(cam->standard.name, video_fmts[video_index].name);
+ cam->crop_bounds.width = video_fmts[video_index].raw_width;
+ cam->crop_bounds.height = video_fmts[video_index].raw_height;
+ cam->crop_current.width = video_fmts[video_index].active_width;
+ cam->crop_current.height = video_fmts[video_index].active_height;
+ cam->crop_current.left = 0;
+
+ return 0;
+}
+
+/*!
+ * V4L2 - mxc_v4l2_g_std function
+ *
+ * Gets the TV standard from the TV input device.
+ *
+ * @param cam structure cam_data *
+ *
+ * @param e structure v4l2_streamparm *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2_g_std(cam_data *cam, v4l2_std_id *e)
+{
+ struct v4l2_format tv_fmt;
+
+ pr_debug("In mxc_v4l2_g_std\n");
+
+ if (cam->device_type == 1) {
+ /* Use this function to get what the TV-In device detects the
+ * format to be. pixelformat is used to return the std value
+ * since the interface has no vidioc_g_std.*/
+ tv_fmt.type = V4L2_BUF_TYPE_PRIVATE;
+ vidioc_int_g_fmt_cap(cam->sensor, &tv_fmt);
+
+ /* If the TV-in automatically detects the standard, then if it
+ * changes, the settings need to change. */
+ if (cam->standard_autodetect) {
+ if (cam->standard.id != tv_fmt.fmt.pix.pixelformat) {
+ pr_debug("MVC: mxc_v4l2_g_std: "
+ "Changing standard\n");
+ mxc_v4l2_s_std(cam, tv_fmt.fmt.pix.pixelformat);
+ }
+ }
+
+ *e = tv_fmt.fmt.pix.pixelformat;
+ }
+
+ return 0;
+}
+
+/*!
+ * Dequeue one V4L capture buffer
+ *
+ * @param cam structure cam_data *
+ * @param buf structure v4l2_buffer *
+ *
+ * @return status 0 success, EINVAL invalid frame number,
+ * ETIME timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)
+{
+ int retval = 0;
+ struct mxc_v4l_frame *frame;
+
+ pr_debug("In MVC:mxc_v4l_dqueue\n");
+
+ if (!wait_event_interruptible_timeout(cam->enc_queue,
+ cam->enc_counter != 0, 10 * HZ)) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout "
+ "enc_counter %x\n",
+ cam->enc_counter);
+ return -ETIME;
+ } else if (signal_pending(current)) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue() "
+ "interrupt received\n");
+ return -ERESTARTSYS;
+ }
+
+ cam->enc_counter--;
+
+ frame = list_entry(cam->done_q.next, struct mxc_v4l_frame, queue);
+ list_del(cam->done_q.next);
+ if (frame->buffer.flags & V4L2_BUF_FLAG_DONE) {
+ frame->buffer.flags &= ~V4L2_BUF_FLAG_DONE;
+ } else if (frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
+ "Buffer not filled.\n");
+ frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+ retval = -EINVAL;
+ } else if ((frame->buffer.flags & 0x7) == V4L2_BUF_FLAG_MAPPED) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_DQBUF: "
+ "Buffer not queued.\n");
+ retval = -EINVAL;
+ }
+
+ buf->bytesused = cam->v2f.fmt.pix.sizeimage;
+ buf->index = frame->index;
+ buf->flags = frame->buffer.flags;
+ buf->m = cam->frame[frame->index].buffer.m;
+
+ return retval;
+}
+
+/*!
+ * V4L interface - open function
+ *
+ * @param inode structure inode *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l_open(struct inode *inode, struct file *file)
+{
+ struct v4l2_ifparm ifparm;
+ struct v4l2_format cam_fmt;
+ ipu_csi_signal_cfg_t csi_param;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int err = 0;
+
+ pr_debug("\nIn MVC: mxc_v4l_open\n");
+ pr_debug(" device name is %s\n", dev->name);
+
+ if (!cam) {
+ pr_err("ERROR: v4l2 capture: Internal error, "
+ "cam_data not found!\n");
+ return -EBADF;
+ }
+
+ down(&cam->busy_lock);
+ err = 0;
+ if (signal_pending(current))
+ goto oops;
+
+ if (cam->open_count++ == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+
+ if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+ err = csi_enc_select(cam);
+#endif
+ } else if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI IC MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
+ err = prp_enc_select(cam);
+#endif
+ }
+
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+
+ vidioc_int_g_ifparm(cam->sensor, &ifparm);
+
+ csi_param.sens_clksrc = 0;
+
+ csi_param.clk_mode = 0;
+ csi_param.data_pol = 0;
+ csi_param.ext_vsync = 0;
+
+ csi_param.pack_tight = 0;
+ csi_param.force_eof = 0;
+ csi_param.data_en_pol = 0;
+ csi_param.mclk = ifparm.u.bt656.clock_curr;
+
+ csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv;
+
+ /* Once we handle multiple inputs this will need to change. */
+ csi_param.csi = 0;
+
+ if (ifparm.u.bt656.mode
+ == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT)
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+ else if (ifparm.u.bt656.mode
+ == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT)
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_10;
+ else
+ csi_param.data_width = IPU_CSI_DATA_WIDTH_8;
+
+
+ csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv;
+ csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv;
+
+ csi_param.csi = cam->csi;
+
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
+
+ /* Reset the sizes. Needed to prevent carryover of last
+ * operation.*/
+ cam->crop_bounds.top = cam->crop_bounds.left = 0;
+ cam->crop_bounds.width = cam_fmt.fmt.pix.width;
+ cam->crop_bounds.height = cam_fmt.fmt.pix.height;
+
+ /* This also is the max crop size for this device. */
+ cam->crop_defrect.top = cam->crop_defrect.left = 0;
+ cam->crop_defrect.width = cam_fmt.fmt.pix.width;
+ cam->crop_defrect.height = cam_fmt.fmt.pix.height;
+
+ /* At this point, this is also the current image size. */
+ cam->crop_current.top = cam->crop_current.left = 0;
+ cam->crop_current.width = cam_fmt.fmt.pix.width;
+ cam->crop_current.height = cam_fmt.fmt.pix.height;
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__,
+ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+ __func__,
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+ __func__,
+ cam->crop_defrect.width, cam->crop_defrect.height);
+ pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+ __func__,
+ cam->crop_current.width, cam->crop_current.height);
+
+ csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat;
+ pr_debug("On Open: Input to ipu size is %d x %d\n",
+ cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height);
+ ipu_csi_set_window_size(cam->crop_current.width,
+ cam->crop_current.width,
+ cam->csi);
+ ipu_csi_set_window_pos(cam->crop_current.left,
+ cam->crop_current.top,
+ cam->csi);
+ ipu_csi_init_interface(cam->crop_bounds.width,
+ cam->crop_bounds.height,
+ cam_fmt.fmt.pix.pixelformat,
+ csi_param);
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+ true, true);
+ vidioc_int_init(cam->sensor);
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi,
+ false, false);
+}
+
+ file->private_data = dev;
+
+ oops:
+ up(&cam->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L interface - close function
+ *
+ * @param inode struct inode *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int mxc_v4l_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ int err = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ pr_debug("In MVC:mxc_v4l_close\n");
+
+ if (!cam) {
+ pr_err("ERROR: v4l2 capture: Internal error, "
+ "cam_data not found!\n");
+ return -EBADF;
+ }
+
+ /* for the case somebody hit the ctrl C */
+ if (cam->overlay_pid == current->pid) {
+ err = stop_preview(cam);
+ cam->overlay_on = false;
+ }
+ if (cam->capture_pid == current->pid) {
+ err |= mxc_streamoff(cam);
+ wake_up_interruptible(&cam->enc_queue);
+ }
+
+ if (--cam->open_count == 0) {
+ wait_event_interruptible(cam->power_queue,
+ cam->low_power == false);
+ pr_info("mxc_v4l_close: release resource\n");
+
+ if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+ err |= csi_enc_deselect(cam);
+#endif
+ } else if (strcmp(mxc_capture_inputs[cam->current_input].name,
+ "CSI IC MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
+ err |= prp_enc_deselect(cam);
+#endif
+ }
+
+ mxc_free_frame_buf(cam);
+ file->private_data = NULL;
+
+ /* capture off */
+ wake_up_interruptible(&cam->enc_queue);
+ mxc_free_frames(cam);
+ cam->enc_counter++;
+ }
+
+ return err;
+}
+
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC) || \
+ defined(CONFIG_MXC_IPU_PRP_ENC_MODULE) || \
+ defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+/*
+ * V4L interface - read function
+ *
+ * @param file struct file *
+ * @param read buf char *
+ * @param count size_t
+ * @param ppos structure loff_t *
+ *
+ * @return bytes read
+ */
+static ssize_t mxc_v4l_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos)
+{
+ int err = 0;
+ u8 *v_address;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ /* Stop the viewfinder */
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+
+ v_address = dma_alloc_coherent(0,
+ PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
+ &cam->still_buf, GFP_DMA | GFP_KERNEL);
+
+ if (!v_address) {
+ err = -ENOBUFS;
+ goto exit0;
+ }
+
+ err = prp_still_select(cam);
+ if (err != 0) {
+ err = -EIO;
+ goto exit1;
+ }
+
+ cam->still_counter = 0;
+ err = cam->csi_start(cam);
+ if (err != 0) {
+ err = -EIO;
+ goto exit2;
+ }
+
+ if (!wait_event_interruptible_timeout(cam->still_queue,
+ cam->still_counter != 0,
+ 10 * HZ)) {
+ pr_err("ERROR: v4l2 capture: mxc_v4l_read timeout counter %x\n",
+ cam->still_counter);
+ err = -ETIME;
+ goto exit2;
+ }
+ err = copy_to_user(buf, v_address, cam->v2f.fmt.pix.sizeimage);
+
+ exit2:
+ prp_still_deselect(cam);
+
+ exit1:
+ dma_free_coherent(0, cam->v2f.fmt.pix.sizeimage, v_address,
+ cam->still_buf);
+ cam->still_buf = 0;
+
+ exit0:
+ if (cam->overlay_on == true) {
+ start_preview(cam);
+ }
+
+ up(&cam->busy_lock);
+ if (err < 0)
+ return err;
+
+ return (cam->v2f.fmt.pix.sizeimage - err);
+}
+#endif
+
+/*!
+ * V4L interface - ioctl function
+ *
+ * @param inode struct inode*
+ *
+ * @param file struct file*
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void*
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static int mxc_v4l_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ int retval = 0;
+ unsigned long lock_flags;
+
+ pr_debug("In MVC: mxc_v4l_do_ioctl %x\n", ioctlnr);
+ wait_event_interruptible(cam->power_queue, cam->low_power == false);
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ /*!
+ * V4l2 VIDIOC_QUERYCAP ioctl
+ */
+ case VIDIOC_QUERYCAP: {
+ struct v4l2_capability *cap = arg;
+ pr_debug(" case VIDIOC_QUERYCAP\n");
+ strcpy(cap->driver, "mxc_v4l2");
+ cap->version = KERNEL_VERSION(0, 1, 11);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_FMT ioctl
+ */
+ case VIDIOC_G_FMT: {
+ struct v4l2_format *gf = arg;
+ pr_debug(" case VIDIOC_G_FMT\n");
+ retval = mxc_v4l2_g_fmt(cam, gf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FMT ioctl
+ */
+ case VIDIOC_S_FMT: {
+ struct v4l2_format *sf = arg;
+ pr_debug(" case VIDIOC_S_FMT\n");
+ retval = mxc_v4l2_s_fmt(cam, sf);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_REQBUFS ioctl
+ */
+ case VIDIOC_REQBUFS: {
+ struct v4l2_requestbuffers *req = arg;
+ pr_debug(" case VIDIOC_REQBUFS\n");
+
+ if (req->count > FRAME_NUM) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
+ "not enough buffers\n");
+ req->count = FRAME_NUM;
+ }
+
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_REQBUFS: "
+ "wrong buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ mxc_streamoff(cam);
+ mxc_free_frame_buf(cam);
+ cam->enc_counter = 0;
+ cam->skip_frame = 0;
+ INIT_LIST_HEAD(&cam->ready_q);
+ INIT_LIST_HEAD(&cam->working_q);
+ INIT_LIST_HEAD(&cam->done_q);
+
+ retval = mxc_allocate_frame_buf(cam, req->count);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_QUERYBUF ioctl
+ */
+ case VIDIOC_QUERYBUF: {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ pr_debug(" case VIDIOC_QUERYBUF\n");
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pr_err("ERROR: v4l2 capture: "
+ "VIDIOC_QUERYBUFS: "
+ "wrong buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ buf->index = index;
+
+ down(&cam->param_lock);
+ retval = mxc_v4l2_buffer_status(cam, buf);
+ up(&cam->param_lock);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_QBUF ioctl
+ */
+ case VIDIOC_QBUF: {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ pr_debug(" case VIDIOC_QBUF\n");
+
+ spin_lock_irqsave(&cam->int_lock, lock_flags);
+ cam->frame[index].buffer.m.offset = buf->m.offset;
+ if ((cam->frame[index].buffer.flags & 0x7) ==
+ V4L2_BUF_FLAG_MAPPED) {
+ cam->frame[index].buffer.flags |=
+ V4L2_BUF_FLAG_QUEUED;
+ if (cam->skip_frame > 0) {
+ list_add_tail(&cam->frame[index].queue,
+ &cam->working_q);
+ retval =
+ cam->enc_update_eba(cam->
+ frame[index].
+ buffer.m.offset,
+ &cam->
+ ping_pong_csi);
+ cam->skip_frame = 0;
+ } else {
+ list_add_tail(&cam->frame[index].queue,
+ &cam->ready_q);
+ }
+ } else if (cam->frame[index].buffer.
+ flags & V4L2_BUF_FLAG_QUEUED) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
+ "buffer already queued\n");
+ retval = -EINVAL;
+ } else if (cam->frame[index].buffer.
+ flags & V4L2_BUF_FLAG_DONE) {
+ pr_err("ERROR: v4l2 capture: VIDIOC_QBUF: "
+ "overwrite done buffer.\n");
+ cam->frame[index].buffer.flags &=
+ ~V4L2_BUF_FLAG_DONE;
+ cam->frame[index].buffer.flags |=
+ V4L2_BUF_FLAG_QUEUED;
+ retval = -EINVAL;
+ }
+
+ buf->flags = cam->frame[index].buffer.flags;
+ spin_unlock_irqrestore(&cam->int_lock, lock_flags);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_DQBUF ioctl
+ */
+ case VIDIOC_DQBUF: {
+ struct v4l2_buffer *buf = arg;
+ pr_debug(" case VIDIOC_DQBUF\n");
+
+ if ((cam->enc_counter == 0) &&
+ (file->f_flags & O_NONBLOCK)) {
+ retval = -EAGAIN;
+ break;
+ }
+
+ retval = mxc_v4l_dqueue(cam, buf);
+
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_STREAMON ioctl
+ */
+ case VIDIOC_STREAMON: {
+ pr_debug(" case VIDIOC_STREAMON\n");
+ retval = mxc_streamon(cam);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_STREAMOFF ioctl
+ */
+ case VIDIOC_STREAMOFF: {
+ pr_debug(" case VIDIOC_STREAMOFF\n");
+ retval = mxc_streamoff(cam);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_CTRL ioctl
+ */
+ case VIDIOC_G_CTRL: {
+ pr_debug(" case VIDIOC_G_CTRL\n");
+ retval = mxc_v4l2_g_ctrl(cam, arg);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_CTRL ioctl
+ */
+ case VIDIOC_S_CTRL: {
+ pr_debug(" case VIDIOC_S_CTRL\n");
+ retval = mxc_v4l2_s_ctrl(cam, arg);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_CROPCAP ioctl
+ */
+ case VIDIOC_CROPCAP: {
+ struct v4l2_cropcap *cap = arg;
+ pr_debug(" case VIDIOC_CROPCAP\n");
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+ cap->bounds = cam->crop_bounds;
+ cap->defrect = cam->crop_defrect;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_CROP ioctl
+ */
+ case VIDIOC_G_CROP: {
+ struct v4l2_crop *crop = arg;
+ pr_debug(" case VIDIOC_G_CROP\n");
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+ crop->c = cam->crop_current;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_CROP ioctl
+ */
+ case VIDIOC_S_CROP: {
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b = &cam->crop_bounds;
+ pr_debug(" case VIDIOC_S_CROP\n");
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) {
+ retval = -EINVAL;
+ break;
+ }
+
+ crop->c.top = (crop->c.top < b->top) ? b->top
+ : crop->c.top;
+ if (crop->c.top > b->top + b->height)
+ crop->c.top = b->top + b->height - 1;
+ if (crop->c.height > b->top + b->height - crop->c.top)
+ crop->c.height =
+ b->top + b->height - crop->c.top;
+
+ crop->c.left = (crop->c.left < b->left) ? b->left
+ : crop->c.left;
+ if (crop->c.left > b->left + b->width)
+ crop->c.left = b->left + b->width - 1;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width =
+ b->left - crop->c.left + b->width;
+
+ crop->c.width -= crop->c.width % 8;
+ crop->c.left -= crop->c.left % 4;
+ cam->crop_current = crop->c;
+
+ pr_debug(" Cropping Input to ipu size %d x %d\n",
+ cam->crop_current.width,
+ cam->crop_current.height);
+ ipu_csi_set_window_size(cam->crop_current.width,
+ cam->crop_current.height,
+ cam->csi);
+ ipu_csi_set_window_pos(cam->crop_current.left,
+ cam->crop_current.top,
+ cam->csi);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_OVERLAY ioctl
+ */
+ case VIDIOC_OVERLAY: {
+ int *on = arg;
+ pr_debug(" VIDIOC_OVERLAY on=%d\n", *on);
+ if (*on) {
+ cam->overlay_on = true;
+ cam->overlay_pid = current->pid;
+ retval = start_preview(cam);
+ }
+ if (!*on) {
+ retval = stop_preview(cam);
+ cam->overlay_on = false;
+ }
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_G_FBUF ioctl
+ */
+ case VIDIOC_G_FBUF: {
+ struct v4l2_framebuffer *fb = arg;
+ pr_debug(" case VIDIOC_G_FBUF\n");
+ *fb = cam->v4l2_fb;
+ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_S_FBUF ioctl
+ */
+ case VIDIOC_S_FBUF: {
+ struct v4l2_framebuffer *fb = arg;
+ pr_debug(" case VIDIOC_S_FBUF\n");
+ cam->v4l2_fb = *fb;
+ break;
+ }
+
+ case VIDIOC_G_PARM: {
+ struct v4l2_streamparm *parm = arg;
+ pr_debug(" case VIDIOC_G_PARM\n");
+ vidioc_int_g_parm(cam->sensor, parm);
+ break;
+ }
+
+ case VIDIOC_S_PARM: {
+ struct v4l2_streamparm *parm = arg;
+ pr_debug(" case VIDIOC_S_PARM\n");
+ retval = mxc_v4l2_s_param(cam, parm);
+ break;
+ }
+
+ /* linux v4l2 bug, kernel c0485619 user c0405619 */
+ case VIDIOC_ENUMSTD: {
+ struct v4l2_standard *e = arg;
+ pr_debug(" case VIDIOC_ENUMSTD\n");
+ *e = cam->standard;
+ break;
+ }
+
+ case VIDIOC_G_STD: {
+ v4l2_std_id *e = arg;
+ pr_debug(" case VIDIOC_G_STD\n");
+ retval = mxc_v4l2_g_std(cam, e);
+ break;
+ }
+
+ case VIDIOC_S_STD: {
+ v4l2_std_id *e = arg;
+ pr_debug(" case VIDIOC_S_STD\n");
+ retval = mxc_v4l2_s_std(cam, *e);
+
+ break;
+ }
+
+ case VIDIOC_ENUMOUTPUT: {
+ struct v4l2_output *output = arg;
+ pr_debug(" case VIDIOC_ENUMOUTPUT\n");
+ if (output->index >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) {
+ retval = -EINVAL;
+ break;
+ }
+ *output = mxc_capture_outputs[output->index];
+
+ break;
+ }
+ case VIDIOC_G_OUTPUT: {
+ int *p_output_num = arg;
+ pr_debug(" case VIDIOC_G_OUTPUT\n");
+ *p_output_num = cam->output;
+ break;
+ }
+
+ case VIDIOC_S_OUTPUT: {
+ int *p_output_num = arg;
+ pr_debug(" case VIDIOC_S_OUTPUT\n");
+ if (*p_output_num >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) {
+ retval = -EINVAL;
+ break;
+ }
+ cam->output = *p_output_num;
+ break;
+ }
+
+ case VIDIOC_ENUMINPUT: {
+ struct v4l2_input *input = arg;
+ pr_debug(" case VIDIOC_ENUMINPUT\n");
+ if (input->index >= MXC_V4L2_CAPTURE_NUM_INPUTS) {
+ retval = -EINVAL;
+ break;
+ }
+ *input = mxc_capture_inputs[input->index];
+ break;
+ }
+
+ case VIDIOC_G_INPUT: {
+ int *index = arg;
+ pr_debug(" case VIDIOC_G_INPUT\n");
+ *index = cam->current_input;
+ break;
+ }
+
+ case VIDIOC_S_INPUT: {
+ int *index = arg;
+ pr_debug(" case VIDIOC_S_INPUT\n");
+ if (*index >= MXC_V4L2_CAPTURE_NUM_INPUTS) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (*index == cam->current_input)
+ break;
+
+ if ((mxc_capture_inputs[cam->current_input].status &
+ V4L2_IN_ST_NO_POWER) == 0) {
+ retval = mxc_streamoff(cam);
+ if (retval)
+ break;
+ mxc_capture_inputs[cam->current_input].status |=
+ V4L2_IN_ST_NO_POWER;
+ }
+
+ if (strcmp(mxc_capture_inputs[*index].name, "CSI MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_CSI_ENC) || defined(CONFIG_MXC_IPU_CSI_ENC_MODULE)
+ retval = csi_enc_select(cam);
+ if (retval)
+ break;
+#endif
+ } else if (strcmp(mxc_capture_inputs[*index].name,
+ "CSI IC MEM") == 0) {
+#if defined(CONFIG_MXC_IPU_PRP_ENC) || defined(CONFIG_MXC_IPU_PRP_ENC_MODULE)
+ retval = prp_enc_select(cam);
+ if (retval)
+ break;
+#endif
+ }
+
+ mxc_capture_inputs[*index].status &= ~V4L2_IN_ST_NO_POWER;
+ cam->current_input = *index;
+ break;
+ }
+
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ default:
+ pr_debug(" case default or not supported\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&cam->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L interface - ioctl function
+ *
+ * @return None
+ */
+static int mxc_v4l_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ pr_debug("In MVC:mxc_v4l_ioctl\n");
+ return video_usercopy(inode, file, cmd, arg, mxc_v4l_do_ioctl);
+}
+
+/*!
+ * V4L interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = video_devdata(file);
+ unsigned long size;
+ int res = 0;
+ cam_data *cam = video_get_drvdata(dev);
+
+ pr_debug("In MVC:mxc_mmap\n");
+ pr_debug(" pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ size = vma->vm_end - vma->vm_start;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ pr_err("ERROR: v4l2 capture: mxc_mmap: "
+ "remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+ mxc_mmap_exit:
+ up(&cam->busy_lock);
+ return res;
+}
+
+/*!
+ * V4L interface - poll function
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_poll(struct file *file, poll_table *wait)
+{
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = video_get_drvdata(dev);
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ pr_debug("In MVC:mxc_poll\n");
+
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ queue = &cam->enc_queue;
+ poll_wait(file, queue, wait);
+
+ up(&cam->busy_lock);
+
+ return res;
+}
+
+/*!
+ * This structure defines the functions to be called in this driver.
+ */
+static struct file_operations mxc_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_v4l_open,
+ .release = mxc_v4l_close,
+ .read = mxc_v4l_read,
+ .ioctl = mxc_v4l_ioctl,
+ .mmap = mxc_mmap,
+ .poll = mxc_poll,
+};
+
+static struct video_device mxc_v4l_template = {
+ .name = "Mxc Camera",
+ .vfl_type = VID_TYPE_CAPTURE,
+ .fops = &mxc_v4l_fops,
+ .release = video_device_release,
+};
+
+/*!
+ * This function can be used to release any platform data on closing.
+ */
+static void camera_platform_release(struct device *device)
+{
+}
+
+/*! Device Definition for Mt9v111 devices */
+static struct platform_device mxc_v4l2_devices = {
+ .name = "mxc_v4l2",
+ .dev = {
+ .release = camera_platform_release,
+ },
+ .id = 0,
+};
+
+/*!
+ * Camera V4l2 callback function.
+ *
+ * @param mask u32
+ *
+ * @param dev void device structure
+ *
+ * @return status
+ */
+static void camera_callback(u32 mask, void *dev)
+{
+ struct mxc_v4l_frame *done_frame;
+ struct mxc_v4l_frame *ready_frame;
+
+ cam_data *cam = (cam_data *) dev;
+ if (cam == NULL)
+ return;
+
+ pr_debug("In MVC:camera_callback\n");
+
+ if (list_empty(&cam->working_q)) {
+ pr_err("ERROR: v4l2 capture: camera_callback: "
+ "working queue empty\n");
+ return;
+ }
+
+ done_frame =
+ list_entry(cam->working_q.next, struct mxc_v4l_frame, queue);
+ if (done_frame->buffer.flags & V4L2_BUF_FLAG_QUEUED) {
+ done_frame->buffer.flags |= V4L2_BUF_FLAG_DONE;
+ done_frame->buffer.flags &= ~V4L2_BUF_FLAG_QUEUED;
+
+ if (list_empty(&cam->ready_q)) {
+ cam->skip_frame++;
+ } else {
+ ready_frame = list_entry(cam->ready_q.next,
+ struct mxc_v4l_frame,
+ queue);
+ list_del(cam->ready_q.next);
+ list_add_tail(&ready_frame->queue, &cam->working_q);
+ cam->enc_update_eba(ready_frame->buffer.m.offset,
+ &cam->ping_pong_csi);
+ }
+
+ /* Added to the done queue */
+ list_del(cam->working_q.next);
+ list_add_tail(&done_frame->queue, &cam->done_q);
+
+ /* Wake up the queue */
+ cam->enc_counter++;
+ wake_up_interruptible(&cam->enc_queue);
+ } else {
+ pr_err("ERROR: v4l2 capture: camera_callback: "
+ "buffer not queued\n");
+ }
+}
+
+/*!
+ * initialize cam_data structure
+ *
+ * @param cam structure cam_data *
+ *
+ * @return status 0 Success
+ */
+static void init_camera_struct(cam_data *cam)
+{
+ pr_debug("In MVC: init_camera_struct\n");
+
+ /* Default everything to 0 */
+ memset(cam, 0, sizeof(cam_data));
+
+ init_MUTEX(&cam->param_lock);
+ init_MUTEX(&cam->busy_lock);
+
+ cam->video_dev = video_device_alloc();
+ if (cam->video_dev == NULL)
+ return;
+
+ *(cam->video_dev) = mxc_v4l_template;
+
+ video_set_drvdata(cam->video_dev, cam);
+ dev_set_drvdata(&mxc_v4l2_devices.dev, (void *)cam);
+ cam->video_dev->minor = -1;
+
+ init_waitqueue_head(&cam->enc_queue);
+ init_waitqueue_head(&cam->still_queue);
+
+ /* setup cropping */
+ cam->crop_bounds.left = 0;
+ cam->crop_bounds.width = 640;
+ cam->crop_bounds.top = 0;
+ cam->crop_bounds.height = 480;
+ cam->crop_current = cam->crop_defrect = cam->crop_bounds;
+ ipu_csi_set_window_size(cam->crop_current.width,
+ cam->crop_current.height, cam->csi);
+ ipu_csi_set_window_pos(cam->crop_current.left,
+ cam->crop_current.top, cam->csi);
+ cam->streamparm.parm.capture.capturemode = 0;
+
+ cam->standard.index = 0;
+ cam->standard.id = V4L2_STD_UNKNOWN;
+ cam->standard.frameperiod.denominator = 30;
+ cam->standard.frameperiod.numerator = 1;
+ cam->standard.framelines = 480;
+ cam->standard_autodetect = true;
+ cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod;
+ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ cam->overlay_on = false;
+ cam->capture_on = false;
+ cam->skip_frame = 0;
+ cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY;
+
+ cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2;
+ cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2;
+ cam->v2f.fmt.pix.width = 288;
+ cam->v2f.fmt.pix.height = 352;
+ cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+ cam->win.w.width = 160;
+ cam->win.w.height = 160;
+ cam->win.w.left = 0;
+ cam->win.w.top = 0;
+
+ cam->csi = 0; /* Need to determine how to set this correctly with
+ * multiple video input devices. */
+
+ cam->enc_callback = camera_callback;
+ init_waitqueue_head(&cam->power_queue);
+ spin_lock_init(&cam->int_lock);
+}
+
+/*!
+ * camera_power function
+ * Turns Sensor power On/Off
+ *
+ * @param cam cam data struct
+ * @param cameraOn true to turn camera on, false to turn off power.
+ *
+ * @return status
+ */
+static u8 camera_power(cam_data *cam, bool cameraOn)
+{
+ pr_debug("In MVC:camera_power on=%d\n", cameraOn);
+
+ if (cameraOn == true) {
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ vidioc_int_s_power(cam->sensor, 1);
+ } else {
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ vidioc_int_s_power(cam->sensor, 0);
+ }
+ return 0;
+}
+
+/*!
+ * This function is called to put the sensor in a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param pdev the device structure used to give information on which I2C
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure.
+ */
+static int mxc_v4l2_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ pr_debug("In MVC:mxc_v4l2_suspend\n");
+
+ if (cam == NULL) {
+ return -1;
+ }
+
+ cam->low_power = true;
+
+ if (cam->overlay_on == true)
+ stop_preview(cam);
+ if ((cam->capture_on == true) && cam->enc_disable) {
+ cam->enc_disable(cam);
+ }
+ camera_power(cam, false);
+
+ return 0;
+}
+
+/*!
+ * This function is called to bring the sensor back from a low power state.
+ * Refer to the document driver-model/driver.txt in the kernel source tree
+ * for more information.
+ *
+ * @param pdev the device structure
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxc_v4l2_resume(struct platform_device *pdev)
+{
+ cam_data *cam = platform_get_drvdata(pdev);
+
+ pr_debug("In MVC:mxc_v4l2_resume\n");
+
+ if (cam == NULL) {
+ return -1;
+ }
+
+ cam->low_power = false;
+ wake_up_interruptible(&cam->power_queue);
+ camera_power(cam, true);
+
+ if (cam->overlay_on == true)
+ start_preview(cam);
+ if (cam->capture_on == true)
+ mxc_streamon(cam);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2_driver = {
+ .driver = {
+ .name = "mxc_v4l2",
+ },
+ .probe = NULL,
+ .remove = NULL,
+ .suspend = mxc_v4l2_suspend,
+ .resume = mxc_v4l2_resume,
+ .shutdown = NULL,
+};
+
+/*!
+ * Initializes the camera driver.
+ */
+static int mxc_v4l2_master_attach(struct v4l2_int_device *slave)
+{
+ cam_data *cam = slave->u.slave->master->priv;
+ struct v4l2_format cam_fmt;
+
+ pr_debug("In MVC: mxc_v4l2_master_attach\n");
+ pr_debug(" slave.name = %s\n", slave->name);
+ pr_debug(" master.name = %s\n", slave->u.slave->master->name);
+
+ cam->sensor = slave;
+ if (slave == NULL) {
+ pr_err("ERROR: v4l2 capture: slave parameter not valid.\n");
+ return -1;
+ }
+
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
+ vidioc_int_dev_init(slave);
+ ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
+ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
+
+ /* Used to detect TV in (type 1) vs. camera (type 0)*/
+ cam->device_type = cam_fmt.fmt.pix.priv;
+
+ /* Set the input size to the ipu for this device */
+ cam->crop_bounds.top = cam->crop_bounds.left = 0;
+ cam->crop_bounds.width = cam_fmt.fmt.pix.width;
+ cam->crop_bounds.height = cam_fmt.fmt.pix.height;
+
+ /* This also is the max crop size for this device. */
+ cam->crop_defrect.top = cam->crop_defrect.left = 0;
+ cam->crop_defrect.width = cam_fmt.fmt.pix.width;
+ cam->crop_defrect.height = cam_fmt.fmt.pix.height;
+
+ /* At this point, this is also the current image size. */
+ cam->crop_current.top = cam->crop_current.left = 0;
+ cam->crop_current.width = cam_fmt.fmt.pix.width;
+ cam->crop_current.height = cam_fmt.fmt.pix.height;
+
+ pr_debug("End of %s: v2f pix widthxheight %d x %d\n",
+ __func__,
+ cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height);
+ pr_debug("End of %s: crop_bounds widthxheight %d x %d\n",
+ __func__,
+ cam->crop_bounds.width, cam->crop_bounds.height);
+ pr_debug("End of %s: crop_defrect widthxheight %d x %d\n",
+ __func__,
+ cam->crop_defrect.width, cam->crop_defrect.height);
+ pr_debug("End of %s: crop_current widthxheight %d x %d\n",
+ __func__,
+ cam->crop_current.width, cam->crop_current.height);
+
+ return 0;
+}
+
+/*!
+ * Disconnects the camera driver.
+ */
+static void mxc_v4l2_master_detach(struct v4l2_int_device *slave)
+{
+ pr_debug("In MVC:mxc_v4l2_master_detach\n");
+ vidioc_int_dev_exit(slave);
+}
+
+/*!
+ * Entry point for the V4L2
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int camera_init(void)
+{
+ u8 err = 0;
+
+ pr_debug("In MVC:camera_init\n");
+
+ /* Register the device driver structure. */
+ err = platform_driver_register(&mxc_v4l2_driver);
+ if (err != 0) {
+ pr_err("ERROR: v4l2 capture:camera_init: "
+ "platform_driver_register failed.\n");
+ return err;
+ }
+
+ /* Create g_cam and initialize it. */
+ if ((g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL)) == NULL) {
+ pr_err("ERROR: v4l2 capture: failed to register camera\n");
+ platform_driver_unregister(&mxc_v4l2_driver);
+ return -1;
+ }
+ init_camera_struct(g_cam);
+
+ /* Set up the v4l2 device and register it*/
+ mxc_v4l2_int_device.priv = g_cam;
+ /* This function contains a bug that won't let this be rmmod'd. */
+ v4l2_int_device_register(&mxc_v4l2_int_device);
+
+ /* Register the I2C device */
+ err = platform_device_register(&mxc_v4l2_devices);
+ if (err != 0) {
+ pr_err("ERROR: v4l2 capture: camera_init: "
+ "platform_device_register failed.\n");
+ platform_driver_unregister(&mxc_v4l2_driver);
+ kfree(g_cam);
+ g_cam = NULL;
+ return err;
+ }
+
+ /* register v4l video device */
+ if (video_register_device(g_cam->video_dev, VFL_TYPE_GRABBER, video_nr)
+ == -1) {
+ platform_device_unregister(&mxc_v4l2_devices);
+ platform_driver_unregister(&mxc_v4l2_driver);
+ kfree(g_cam);
+ g_cam = NULL;
+ pr_err("ERROR: v4l2 capture: video_register_device failed\n");
+ return -1;
+ }
+ pr_debug(" Video device registered: %s #%d\n",
+ g_cam->video_dev->name, g_cam->video_dev->minor);
+
+ return err;
+}
+
+/*!
+ * Exit and cleanup for the V4L2
+ */
+static void __exit camera_exit(void)
+{
+ pr_debug("In MVC: camera_exit\n");
+
+ pr_info("V4L2 unregistering video\n");
+
+ if (g_cam->open_count) {
+ pr_err("ERROR: v4l2 capture:camera open "
+ "-- setting ops to NULL\n");
+ } else {
+ pr_info("V4L2 freeing image input device\n");
+ v4l2_int_device_unregister(&mxc_v4l2_int_device);
+ video_unregister_device(g_cam->video_dev);
+ platform_driver_unregister(&mxc_v4l2_driver);
+ platform_device_unregister(&mxc_v4l2_devices);
+
+ mxc_free_frame_buf(g_cam);
+ kfree(g_cam);
+ g_cam = NULL;
+ }
+}
+
+module_init(camera_init);
+module_exit(camera_exit);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2 capture driver for Mxc based cameras");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.h b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
new file mode 100644
index 000000000000..a9c0c4d159da
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup MXC_V4L2_CAPTURE MXC V4L2 Video Capture Driver
+ */
+/*!
+ * @file mxc_v4l2_capture.h
+ *
+ * @brief mxc V4L2 capture device API Header file
+ *
+ * It include all the defines for frame operations, also three structure defines
+ * use case ops structure, common v4l2 driver structure and frame structure.
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+#ifndef __MXC_V4L2_CAPTURE_H__
+#define __MXC_V4L2_CAPTURE_H__
+
+#include <asm/uaccess.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/ipu.h>
+#include <linux/mxc_v4l2.h>
+
+#include <media/v4l2-dev.h>
+
+#define FRAME_NUM 3
+
+/*!
+ * v4l2 frame structure.
+ */
+struct mxc_v4l_frame {
+ u32 paddress;
+ void *vaddress;
+ int count;
+ int width;
+ int height;
+
+ struct v4l2_buffer buffer;
+ struct list_head queue;
+ int index;
+};
+
+/* Only for old version. Will go away soon. */
+typedef struct {
+ u8 clk_mode;
+ u8 ext_vsync;
+ u8 Vsync_pol;
+ u8 Hsync_pol;
+ u8 pixclk_pol;
+ u8 data_pol;
+ u8 data_width;
+ u8 pack_tight;
+ u8 force_eof;
+ u8 data_en_pol;
+ u16 width;
+ u16 height;
+ u32 pixel_fmt;
+ u32 mclk;
+ u16 active_width;
+ u16 active_height;
+} sensor_interface;
+
+/* Sensor control function */
+/* Only for old version. Will go away soon. */
+struct camera_sensor {
+ void (*set_color) (int bright, int saturation, int red, int green,
+ int blue);
+ void (*get_color) (int *bright, int *saturation, int *red, int *green,
+ int *blue);
+ void (*set_ae_mode) (int ae_mode);
+ void (*get_ae_mode) (int *ae_mode);
+ sensor_interface *(*config) (int *frame_rate, int high_quality);
+ sensor_interface *(*reset) (void);
+ void (*get_std) (v4l2_std_id *std);
+ void (*set_std) (v4l2_std_id std);
+ unsigned int csi;
+};
+
+/*!
+ * common v4l2 driver structure.
+ */
+typedef struct _cam_data {
+ struct video_device *video_dev;
+ int device_type;
+
+ /* semaphore guard against SMP multithreading */
+ struct semaphore busy_lock;
+
+ int open_count;
+
+ /* params lock for this camera */
+ struct semaphore param_lock;
+
+ /* Encoder */
+ struct list_head ready_q;
+ struct list_head done_q;
+ struct list_head working_q;
+ int ping_pong_csi;
+ spinlock_t int_lock;
+ struct mxc_v4l_frame frame[FRAME_NUM];
+ int skip_frame;
+ wait_queue_head_t enc_queue;
+ int enc_counter;
+ dma_addr_t rot_enc_bufs[2];
+ void *rot_enc_bufs_vaddr[2];
+ int rot_enc_buf_size[2];
+ enum v4l2_buf_type type;
+
+ /* still image capture */
+ wait_queue_head_t still_queue;
+ int still_counter;
+ dma_addr_t still_buf;
+ void *still_buf_vaddr;
+
+ /* overlay */
+ struct v4l2_window win;
+ struct v4l2_framebuffer v4l2_fb;
+ dma_addr_t vf_bufs[2];
+ void *vf_bufs_vaddr[2];
+ int vf_bufs_size[2];
+ dma_addr_t rot_vf_bufs[2];
+ void *rot_vf_bufs_vaddr[2];
+ int rot_vf_buf_size[2];
+ bool overlay_active;
+ int output;
+ struct fb_info *overlay_fb;
+
+ /* v4l2 format */
+ struct v4l2_format v2f;
+ int rotation; /* for IPUv1 and IPUv3, this means encoder rotation */
+ int vf_rotation; /* viewfinder rotation only for IPUv1 and IPUv3 */
+ struct v4l2_mxc_offset offset;
+
+ /* V4l2 control bit */
+ int bright;
+ int hue;
+ int contrast;
+ int saturation;
+ int red;
+ int green;
+ int blue;
+ int ae_mode;
+
+ /* standard */
+ struct v4l2_streamparm streamparm;
+ struct v4l2_standard standard;
+ bool standard_autodetect;
+
+ /* crop */
+ struct v4l2_rect crop_bounds;
+ struct v4l2_rect crop_defrect;
+ struct v4l2_rect crop_current;
+
+ int (*enc_update_eba) (dma_addr_t eba, int *bufferNum);
+ int (*enc_enable) (void *private);
+ int (*enc_disable) (void *private);
+ void (*enc_callback) (u32 mask, void *dev);
+ int (*vf_start_adc) (void *private);
+ int (*vf_stop_adc) (void *private);
+ int (*vf_start_sdc) (void *private);
+ int (*vf_stop_sdc) (void *private);
+ int (*csi_start) (void *private);
+ int (*csi_stop) (void *private);
+
+ /* misc status flag */
+ bool overlay_on;
+ bool capture_on;
+ int overlay_pid;
+ int capture_pid;
+ bool low_power;
+ wait_queue_head_t power_queue;
+ unsigned int csi;
+ int current_input;
+
+ /* camera sensor interface */
+ struct camera_sensor *cam_sensor; /* old version */
+ struct v4l2_int_device *sensor;
+} cam_data;
+
+#if defined(CONFIG_MXC_IPU_V1) || defined(CONFIG_VIDEO_MXC_EMMA_CAMERA) \
+ || defined(CONFIG_VIDEO_MXC_CSI_CAMERA_MODULE) \
+ || defined(CONFIG_VIDEO_MXC_CSI_CAMERA)
+void set_mclk_rate(uint32_t *p_mclk_freq);
+#else
+void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi);
+#endif
+#endif /* __MXC_V4L2_CAPTURE_H__ */
diff --git a/drivers/media/video/mxc/capture/ov2640.c b/drivers/media/video/mxc/capture/ov2640.c
new file mode 100644
index 000000000000..a1329b0b431d
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ov2640.c
@@ -0,0 +1,1080 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ov2640.c
+ *
+ * @brief ov2640 camera driver functions
+ *
+ * @ingroup Camera
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+
+#define MIN_FPS 5
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV2640_XCLK_MIN 6000000
+#define OV2640_XCLK_MAX 27000000
+
+/*
+enum ov2640_mode {
+ ov2640_mode_1600_1120,
+ ov2640_mode_800_600
+};
+*/
+
+struct reg_value {
+ u8 reg;
+ u8 value;
+ int delay_ms;
+};
+
+static struct reg_value ov2640_setting_1600_1120[] = {
+#ifdef CONFIG_MACH_MX25_3DS
+ {0xff, 0x01, 0}, {0x12, 0x80, 5}, {0xff, 0x00, 0}, {0x2c, 0xff, 0},
+ {0x2e, 0xdf, 0}, {0xff, 0x01, 0}, {0x3c, 0x32, 0}, {0x11, 0x00, 0},
+ {0x09, 0x02, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+ {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+ {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x02, 0},
+ {0x35, 0x58, 0}, {0x22, 0x0a, 0}, {0x37, 0x40, 0}, {0x23, 0x00, 0},
+ {0x34, 0xa0, 0}, {0x36, 0x1a, 0}, {0x06, 0x02, 0}, {0x07, 0xc0, 0},
+ {0x0d, 0xb7, 0}, {0x0e, 0x01, 0}, {0x4c, 0x00, 0}, {0x4a, 0x81, 0},
+ {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+ {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x3f, 0}, {0x61, 0x70, 0},
+ {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0}, {0x28, 0x30, 0},
+ {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 0x00, 0}, {0x70, 0x02, 0},
+ {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x3d, 0x34, 0}, {0x5a, 0x57, 0},
+ {0x4f, 0xbb, 0}, {0x50, 0x9c, 0}, {0xff, 0x00, 0}, {0xe5, 0x7f, 0},
+ {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0xe0, 0x14, 0}, {0x76, 0xff, 0},
+ {0x33, 0xa0, 0}, {0x42, 0x20, 0}, {0x43, 0x18, 0}, {0x4c, 0x00, 0},
+ {0x87, 0xd0, 0}, {0x88, 0x3f, 0}, {0xd7, 0x01, 0}, {0xd9, 0x10, 0},
+ {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0}, {0x7c, 0x00, 0},
+ {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0}, {0x7d, 0x48, 0},
+ {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0}, {0x7d, 0x0e, 0},
+ {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0}, {0x91, 0x31, 0},
+ {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0}, {0x91, 0x7e, 0},
+ {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0}, {0x91, 0xa3, 0},
+ {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0}, {0x91, 0xe8, 0},
+ {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0}, {0x93, 0xe3, 0},
+ {0x93, 0x05, 0}, {0x93, 0x05, 0}, {0x93, 0x00, 0}, {0x93, 0x04, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x96, 0x00, 0},
+ {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0}, {0x97, 0x0c, 0},
+ {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0}, {0x97, 0x26, 0},
+ {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0}, {0x97, 0x00, 0},
+ {0x97, 0x00, 0}, {0xc3, 0xed, 0}, {0xa4, 0x00, 0}, {0xa8, 0x00, 0},
+ {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0}, {0xc7, 0x10, 0},
+ {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0}, {0xb9, 0x7c, 0},
+ {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0}, {0xb0, 0xc5, 0},
+ {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0}, {0xc0, 0xc8, 0},
+ {0xc1, 0x96, 0}, {0x86, 0x1d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0},
+ {0x52, 0x2c, 0}, {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0},
+ {0x57, 0x00, 0}, {0x5a, 0x90, 0}, {0x5b, 0x2c, 0}, {0x5c, 0x05, 0},
+ {0xc3, 0xed, 0}, {0x7f, 0x00, 0}, {0xda, 0x00, 0}, {0xe5, 0x1f, 0},
+ {0xe1, 0x77, 0}, {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0},
+ {0xff, 0x00, 0}, {0xe0, 0x04, 0}, {0xc0, 0xc8, 0}, {0xc1, 0x96, 0},
+ {0x86, 0x3d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0}, {0x52, 0x2c, 0},
+ {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0}, {0x57, 0x00, 0},
+ {0x5a, 0x40, 0}, {0x5b, 0xf0, 0}, {0x5c, 0x01, 0}, {0xd3, 0x82, 0},
+ {0xe0, 0x00, 1000}
+#else
+ {0xff, 0x1, 0}, {0x12, 0x80, 1}, {0xff, 0, 0}, {0x2c, 0xff, 0},
+ {0x2e, 0xdf, 0}, {0xff, 0x1, 0}, {0x3c, 0x32, 0}, {0x11, 0x01, 0},
+ {0x09, 0x00, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+ {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+ {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x82, 0},
+ {0x35, 0x88, 0}, {0x22, 0x0a, 0}, {0x37, 0x40, 0}, {0x23, 0x00, 0},
+ {0x34, 0xa0, 0}, {0x36, 0x1a, 0}, {0x06, 0x02, 0}, {0x07, 0xc0, 0},
+ {0x0d, 0xb7, 0}, {0x0e, 0x01, 0}, {0x4c, 0x00, 0}, {0x4a, 0x81, 0},
+ {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+ {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x3f, 0}, {0x0c, 0x3c, 0},
+ {0x5d, 0x55, 0}, {0x5e, 0x7d, 0}, {0x5f, 0x7d, 0}, {0x60, 0x55, 0},
+ {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0},
+ {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 00, 0},
+ {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x3d, 0x34, 0},
+ {0x5a, 0x57, 0}, {0x4f, 0xbb, 0}, {0x50, 0x9c, 0}, {0xff, 0x00, 0},
+ {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0x44, 0x06, 0},
+ {0xe0, 0x14, 0}, {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0},
+ {0x43, 0x18, 0}, {0x4c, 0x00, 0}, {0x87, 0xd0, 0}, {0xd7, 0x03, 0},
+ {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0},
+ {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0},
+ {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0},
+ {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0},
+ {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0},
+ {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0},
+ {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0},
+ {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0},
+ {0x93, 0xe3, 0}, {0x93, 0x03, 0}, {0x93, 0x03, 0}, {0x93, 0x00, 0},
+ {0x93, 0x02, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0},
+ {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0},
+ {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0},
+ {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xa4, 0x00, 0}, {0xa8, 0x00, 0},
+ {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0}, {0xc7, 0x10, 0},
+ {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0}, {0xb9, 0x7c, 0},
+ {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0}, {0xb0, 0xc5, 0},
+ {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0}, {0xa6, 0x00, 0},
+ {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x1b, 0}, {0xa7, 0x31, 0},
+ {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0},
+ {0xa7, 0x19, 0}, {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0},
+ {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x19, 0}, {0xa7, 0x31, 0},
+ {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xc0, 0xc8, 0}, {0xc1, 0x96, 0},
+ {0x86, 0x3d, 0}, {0x50, 0x00, 0}, {0x51, 0x90, 0}, {0x52, 0x18, 0},
+ {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x88, 0}, {0x57, 0x00, 0},
+ {0x5a, 0x90, 0}, {0x5b, 0x18, 0}, {0x5c, 0x05, 0}, {0xc3, 0xef, 0},
+ {0x7f, 0x00, 0}, {0xda, 0x01, 0}, {0xe5, 0x1f, 0}, {0xe1, 0x67, 0},
+ {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0}
+#endif
+};
+
+static struct reg_value ov2640_setting_800_600[] = {
+#ifdef CONFIG_MACH_MX25_3DS
+ {0xff, 0x01, 0}, {0x12, 0x80, 5}, {0xff, 0x00, 0}, {0x2c, 0xff, 0},
+ {0x2e, 0xdf, 0}, {0xff, 0x01, 0}, {0x3c, 0x32, 0}, {0x11, 0x00, 0},
+ {0x09, 0x02, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0}, {0x14, 0x48, 0},
+ {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0}, {0x3b, 0xfb, 0},
+ {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0}, {0x39, 0x92, 0},
+ {0x35, 0xda, 0}, {0x22, 0x1a, 0}, {0x37, 0xc3, 0}, {0x23, 0x00, 0},
+ {0x34, 0xc0, 0}, {0x36, 0x1a, 0}, {0x06, 0x88, 0}, {0x07, 0xc0, 0},
+ {0x0d, 0x87, 0}, {0x0e, 0x41, 0}, {0x4c, 0x00, 0},
+ {0x48, 0x00, 0}, {0x5b, 0x00, 0}, {0x42, 0x03, 0}, {0x4a, 0x81, 0},
+ {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0}, {0x26, 0x82, 0},
+ {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x22, 0}, {0x0c, 0x3c, 0},
+ {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0}, {0x20, 0x80, 0},
+ {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0}, {0x6e, 0x00, 0},
+ {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0}, {0x12, 0x40, 0},
+ {0x17, 0x11, 0}, {0x18, 0x43, 0}, {0x19, 0x00, 0}, {0x1a, 0x4b, 0},
+ {0x32, 0x09, 0}, {0x37, 0xc0, 0}, {0x4f, 0xca, 0}, {0x50, 0xa8, 0},
+ {0x5a, 0x23, 0}, {0x6d, 0x00, 0}, {0x3d, 0x38, 0}, {0xff, 0x00, 0},
+ {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0xe0, 0x14, 0},
+ {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0}, {0x43, 0x18, 0},
+ {0x4c, 0x00, 0}, {0x87, 0xd5, 0}, {0x88, 0x3f, 0}, {0xd7, 0x01, 0},
+ {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0}, {0xc9, 0x80, 0},
+ {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0}, {0x7d, 0x48, 0},
+ {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0}, {0x7d, 0x10, 0},
+ {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0}, {0x91, 0x1a, 0},
+ {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0}, {0x91, 0x75, 0},
+ {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0}, {0x91, 0x96, 0},
+ {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0}, {0x91, 0xd7, 0},
+ {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0}, {0x93, 0x06, 0},
+ {0x93, 0xe3, 0}, {0x93, 0x05, 0}, {0x93, 0x05, 0}, {0x93, 0x00, 0},
+ {0x93, 0x04, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0}, {0x97, 0x02, 0},
+ {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0}, {0x97, 0x28, 0},
+ {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0}, {0x97, 0x80, 0},
+ {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xc3, 0xed, 0}, {0xa4, 0x00, 0},
+ {0xa8, 0x00, 0}, {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0},
+ {0xc7, 0x10, 0}, {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0},
+ {0xb9, 0x7c, 0}, {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0},
+ {0xb0, 0xc5, 0}, {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0},
+ {0xc0, 0x64, 0}, {0xc1, 0x4b, 0}, {0x8c, 0x00, 0}, {0x86, 0x3d, 0},
+ {0x50, 0x00, 0}, {0x51, 0xc8, 0}, {0x52, 0x96, 0}, {0x53, 0x00, 0},
+ {0x54, 0x00, 0}, {0x55, 0x00, 0}, {0x5a, 0xc8, 0}, {0x5b, 0x96, 0},
+ {0x5c, 0x00, 0}, {0xd3, 0x82, 0}, {0xc3, 0xed, 0}, {0x7f, 0x00, 0},
+ {0xda, 0x00, 0}, {0xe5, 0x1f, 0}, {0xe1, 0x67, 0}, {0xe0, 0x00, 0},
+ {0xdd, 0x7f, 0}, {0x05, 0x00, 0}, {0xff, 0x00, 0}, {0xe0, 0x04, 0},
+ {0xc0, 0x64, 0}, {0xc1, 0x4b, 0}, {0x8c, 0x00, 0}, {0x86, 0x3d, 0},
+ {0x50, 0x00, 0}, {0x51, 0xc8, 0}, {0x52, 0x96, 0}, {0x53, 0x00, 0},
+ {0x54, 0x00, 0}, {0x55, 0x00, 0}, {0x5a, 0xa0, 0}, {0x5b, 0x78, 0},
+ {0x5c, 0x00, 0}, {0xd3, 0x82, 0}, {0xe0, 0x00, 1000}
+#else
+ {0xff, 0, 0}, {0xff, 1, 0}, {0x12, 0x80, 1}, {0xff, 00, 0},
+ {0x2c, 0xff, 0}, {0x2e, 0xdf, 0}, {0xff, 0x1, 0}, {0x3c, 0x32, 0},
+ {0x11, 0x01, 0}, {0x09, 0x00, 0}, {0x04, 0x28, 0}, {0x13, 0xe5, 0},
+ {0x14, 0x48, 0}, {0x2c, 0x0c, 0}, {0x33, 0x78, 0}, {0x3a, 0x33, 0},
+ {0x3b, 0xfb, 0}, {0x3e, 0x00, 0}, {0x43, 0x11, 0}, {0x16, 0x10, 0},
+ {0x39, 0x92, 0}, {0x35, 0xda, 0}, {0x22, 0x1a, 0}, {0x37, 0xc3, 0},
+ {0x23, 0x00, 0}, {0x34, 0xc0, 0}, {0x36, 0x1a, 0}, {0x06, 0x88, 0},
+ {0x07, 0xc0, 0}, {0x0d, 0x87, 0}, {0x0e, 0x41, 0}, {0x4c, 0x00, 0},
+ {0x4a, 0x81, 0}, {0x21, 0x99, 0}, {0x24, 0x40, 0}, {0x25, 0x38, 0},
+ {0x26, 0x82, 0}, {0x5c, 0x00, 0}, {0x63, 0x00, 0}, {0x46, 0x22, 0},
+ {0x0c, 0x3c, 0}, {0x5d, 0x55, 0}, {0x5e, 0x7d, 0}, {0x5f, 0x7d, 0},
+ {0x60, 0x55, 0}, {0x61, 0x70, 0}, {0x62, 0x80, 0}, {0x7c, 0x05, 0},
+ {0x20, 0x80, 0}, {0x28, 0x30, 0}, {0x6c, 0x00, 0}, {0x6d, 0x80, 0},
+ {0x6e, 00, 0}, {0x70, 0x02, 0}, {0x71, 0x94, 0}, {0x73, 0xc1, 0},
+ {0x12, 0x40, 0}, {0x17, 0x11, 0}, {0x18, 0x43, 0}, {0x19, 0x00, 0},
+ {0x1a, 0x4b, 0}, {0x32, 0x09, 0}, {0x37, 0xc0, 0}, {0x4f, 0xca, 0},
+ {0x50, 0xa8, 0}, {0x6d, 0x00, 0}, {0x3d, 0x38, 0}, {0xff, 0x00, 0},
+ {0xe5, 0x7f, 0}, {0xf9, 0xc0, 0}, {0x41, 0x24, 0}, {0x44, 0x06, 0},
+ {0xe0, 0x14, 0}, {0x76, 0xff, 0}, {0x33, 0xa0, 0}, {0x42, 0x20, 0},
+ {0x43, 0x18, 0}, {0x4c, 0x00, 0}, {0x87, 0xd0, 0}, {0x88, 0x3f, 0},
+ {0xd7, 0x03, 0}, {0xd9, 0x10, 0}, {0xd3, 0x82, 0}, {0xc8, 0x08, 0},
+ {0xc9, 0x80, 0}, {0x7c, 0x00, 0}, {0x7d, 0x00, 0}, {0x7c, 0x03, 0},
+ {0x7d, 0x48, 0}, {0x7d, 0x48, 0}, {0x7c, 0x08, 0}, {0x7d, 0x20, 0},
+ {0x7d, 0x10, 0}, {0x7d, 0x0e, 0}, {0x90, 0x00, 0}, {0x91, 0x0e, 0},
+ {0x91, 0x1a, 0}, {0x91, 0x31, 0}, {0x91, 0x5a, 0}, {0x91, 0x69, 0},
+ {0x91, 0x75, 0}, {0x91, 0x7e, 0}, {0x91, 0x88, 0}, {0x91, 0x8f, 0},
+ {0x91, 0x96, 0}, {0x91, 0xa3, 0}, {0x91, 0xaf, 0}, {0x91, 0xc4, 0},
+ {0x91, 0xd7, 0}, {0x91, 0xe8, 0}, {0x91, 0x20, 0}, {0x92, 0x00, 0},
+ {0x93, 0x06, 0}, {0x93, 0xe3, 0}, {0x93, 0x03, 0}, {0x93, 0x03, 0},
+ {0x93, 0x00, 0}, {0x93, 0x02, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0}, {0x93, 0x00, 0},
+ {0x93, 0x00, 0}, {0x96, 0x00, 0}, {0x97, 0x08, 0}, {0x97, 0x19, 0},
+ {0x97, 0x02, 0}, {0x97, 0x0c, 0}, {0x97, 0x24, 0}, {0x97, 0x30, 0},
+ {0x97, 0x28, 0}, {0x97, 0x26, 0}, {0x97, 0x02, 0}, {0x97, 0x98, 0},
+ {0x97, 0x80, 0}, {0x97, 0x00, 0}, {0x97, 0x00, 0}, {0xa4, 0x00, 0},
+ {0xa8, 0x00, 0}, {0xc5, 0x11, 0}, {0xc6, 0x51, 0}, {0xbf, 0x80, 0},
+ {0xc7, 0x10, 0}, {0xb6, 0x66, 0}, {0xb8, 0xa5, 0}, {0xb7, 0x64, 0},
+ {0xb9, 0x7c, 0}, {0xb3, 0xaf, 0}, {0xb4, 0x97, 0}, {0xb5, 0xff, 0},
+ {0xb0, 0xc5, 0}, {0xb1, 0x94, 0}, {0xb2, 0x0f, 0}, {0xc4, 0x5c, 0},
+ {0xa6, 0x00, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x1b, 0},
+ {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xa7, 0x20, 0},
+ {0xa7, 0xd8, 0}, {0xa7, 0x19, 0}, {0xa7, 0x31, 0}, {0xa7, 0x00, 0},
+ {0xa7, 0x18, 0}, {0xa7, 0x20, 0}, {0xa7, 0xd8, 0}, {0xa7, 0x19, 0},
+ {0xa7, 0x31, 0}, {0xa7, 0x00, 0}, {0xa7, 0x18, 0}, {0xc0, 0x64, 0},
+ {0xc1, 0x4b, 0}, {0x86, 0x1d, 0}, {0x50, 0x00, 0}, {0x51, 0xc8, 0},
+ {0x52, 0x96, 0}, {0x53, 0x00, 0}, {0x54, 0x00, 0}, {0x55, 0x00, 0},
+ {0x57, 0x00, 0}, {0x5a, 0xc8, 0}, {0x5b, 0x96, 0}, {0x5c, 0x00, 0},
+ {0xc3, 0xef, 0}, {0x7f, 0x00, 0}, {0xda, 0x01, 0}, {0xe5, 0x1f, 0},
+ {0xe1, 0x67, 0}, {0xe0, 0x00, 0}, {0xdd, 0x7f, 0}, {0x05, 0x00, 0}
+#endif
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+ const struct ov2640_platform_data *platform_data;
+ struct v4l2_int_device *v4l2_int_device;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ struct v4l2_captureparm streamcap;
+ bool on;
+
+ /* control settings */
+ int brightness;
+ int hue;
+ int contrast;
+ int saturation;
+ int red;
+ int green;
+ int blue;
+ int ae_mode;
+
+ u32 csi;
+ u32 mclk;
+
+} ov2640_data;
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+
+extern void gpio_sensor_active(void);
+extern void gpio_sensor_inactive(void);
+
+/* list of image formats supported by this sensor */
+/*
+const static struct v4l2_fmtdesc ov2640_formats[] = {
+ {
+ .description = "YUYV (YUV 4:2:2), packed",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+};
+ */
+
+static int ov2640_init_mode(struct sensor *s)
+{
+ int ret = -1;
+ struct reg_value *setting;
+ int i, num;
+
+ pr_debug("In ov2640:ov2640_init_mode capturemode is %d\n",
+ s->streamcap.capturemode);
+
+ if (s->streamcap.capturemode & V4L2_MODE_HIGHQUALITY) {
+ s->pix.width = 1600;
+ s->pix.height = 1120;
+ setting = ov2640_setting_1600_1120;
+ num = ARRAY_SIZE(ov2640_setting_1600_1120);
+ } else {
+ s->pix.width = 800;
+ s->pix.height = 600;
+ setting = ov2640_setting_800_600;
+ num = ARRAY_SIZE(ov2640_setting_800_600);
+ }
+
+ for (i = 0; i < num; i++) {
+ ret = i2c_smbus_write_byte_data(s->i2c_client,
+ setting[i].reg,
+ setting[i].value);
+ if (ret < 0) {
+ pr_err("write reg error: reg=%x, val=%x\n",
+ setting[i].reg, setting[i].value);
+ return ret;
+ }
+ if (setting[i].delay_ms > 0)
+ msleep(setting[i].delay_ms);
+ }
+
+ return ret;
+}
+
+/* At present only support change to 15fps(only for SVGA mode) */
+static int ov2640_set_fps(struct sensor *s, int fps)
+{
+ int ret = 0;
+
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xff, 0x01) < 0) {
+ pr_err("in %s,change to sensor addr failed\n", __func__);
+ ret = -EPERM;
+ }
+
+ /* change the camera framerate to 15fps(only for SVGA mode) */
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0x11, 0x01) < 0) {
+ pr_err("change camera to 15fps failed\n");
+ ret = -EPERM;
+ }
+
+ return ret;
+}
+
+static int ov2640_set_format(struct sensor *s, int format)
+{
+ int ret = 0;
+
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xff, 0x00) < 0)
+ ret = -EPERM;
+
+ if (format == V4L2_PIX_FMT_RGB565) {
+ /* set RGB565 format */
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xda, 0x08) < 0)
+ ret = -EPERM;
+
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xd7, 0x03) < 0)
+ ret = -EPERM;
+ } else if (format == V4L2_PIX_FMT_YUV420) {
+ /* set YUV420 format */
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xda, 0x00) < 0)
+ ret = -EPERM;
+
+ if (i2c_smbus_write_byte_data(s->i2c_client, 0xd7, 0x1b) < 0)
+ ret = -EPERM;
+ } else {
+ pr_debug("format not supported\n");
+ }
+
+ return ret;
+}
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+/*!
+ * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num
+ * s: pointer to standard V4L2 device structure
+ * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p. This value is returned in the p
+ * parameter.
+ *
+ * vidioc_int_g_ifparm returns platform-specific information about the
+ * interface settings used by the sensor.
+ *
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency.
+ *
+ * Called on open.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ pr_debug("In ov2640:ioctl_g_ifparm\n");
+
+ if (s == NULL) {
+ pr_err(" ERROR!! no slave device set!\n");
+ return -1;
+ }
+
+ memset(p, 0, sizeof(*p));
+ p->u.bt656.clock_curr = ov2640_data.mclk;
+ p->if_type = V4L2_IF_TYPE_BT656;
+ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+ p->u.bt656.clock_min = OV2640_XCLK_MIN;
+ p->u.bt656.clock_max = OV2640_XCLK_MAX;
+
+ return 0;
+}
+
+/*!
+ * Sets the camera power.
+ *
+ * s pointer to the camera device
+ * on if 1, power is to be turned on. 0 means power is to be turned off
+ *
+ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ * This is called on open, close, suspend and resume.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct sensor *sensor = s->priv;
+
+ pr_debug("In ov2640:ioctl_s_power\n");
+
+ if (on && !sensor->on) {
+ gpio_sensor_active();
+ if (io_regulator)
+ if (regulator_enable(io_regulator) != 0)
+ return -EIO;
+ if (core_regulator)
+ if (regulator_enable(core_regulator) != 0)
+ return -EIO;
+ if (gpo_regulator)
+ if (regulator_enable(gpo_regulator) != 0)
+ return -EIO;
+ if (analog_regulator)
+ if (regulator_enable(analog_regulator) != 0)
+ return -EIO;
+ } else if (!on && sensor->on) {
+ if (analog_regulator)
+ regulator_disable(analog_regulator);
+ if (core_regulator)
+ regulator_disable(core_regulator);
+ if (io_regulator)
+ regulator_disable(io_regulator);
+ if (gpo_regulator)
+ regulator_disable(gpo_regulator);
+ gpio_sensor_inactive();
+ }
+
+ sensor->on = on;
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+ int ret = 0;
+
+ pr_debug("In ov2640:ioctl_g_parm\n");
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cparm->capability = sensor->streamcap.capability;
+ cparm->timeperframe = sensor->streamcap.timeperframe;
+ cparm->capturemode = sensor->streamcap.capturemode;
+ ret = 0;
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ pr_err(" type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+ "but %d\n", a->type);
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_err(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible. If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+ u32 tgt_fps; /* target frames per secound */
+ int ret = 0;
+
+ pr_debug("In ov2640:ioctl_s_parm\n");
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+
+ /* Check that the new frame rate is allowed. */
+ if ((timeperframe->numerator == 0)
+ || (timeperframe->denominator == 0)) {
+ timeperframe->denominator = DEFAULT_FPS;
+ timeperframe->numerator = 1;
+ }
+ tgt_fps = timeperframe->denominator
+ / timeperframe->numerator;
+
+ if (tgt_fps > MAX_FPS) {
+ timeperframe->denominator = MAX_FPS;
+ timeperframe->numerator = 1;
+ } else if (tgt_fps < MIN_FPS) {
+ timeperframe->denominator = MIN_FPS;
+ timeperframe->numerator = 1;
+ }
+ sensor->streamcap.timeperframe = *timeperframe;
+ sensor->streamcap.capturemode =
+ (u32)a->parm.capture.capturemode;
+
+ ret = ov2640_init_mode(sensor);
+ if (tgt_fps == 15)
+ ov2640_set_fps(sensor, tgt_fps);
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ pr_err(" type is not V4L2_BUF_TYPE_VIDEO_CAPTURE " \
+ "but %d\n", a->type);
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_err(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_fmt_cap - V4L2 sensor interface handler for ioctl_s_fmt_cap
+ * set camera output format and resolution format
+ *
+ * @s: pointer to standard V4L2 device structure
+ * @arg: pointer to parameter, according this to set camera
+ *
+ * Returns 0 if set succeed, else return -1
+ */
+static int ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct sensor *sensor = s->priv;
+ u32 format = f->fmt.pix.pixelformat;
+ int size = 0, ret = 0;
+
+ size = f->fmt.pix.width * f->fmt.pix.height;
+ switch (format) {
+ case V4L2_PIX_FMT_RGB565:
+ if (size > 640 * 480)
+ sensor->streamcap.capturemode = V4L2_MODE_HIGHQUALITY;
+ else
+ sensor->streamcap.capturemode = 0;
+ ret = ov2640_init_mode(sensor);
+
+ ret = ov2640_set_format(sensor, V4L2_PIX_FMT_RGB565);
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ if (size > 640 * 480)
+ sensor->streamcap.capturemode = V4L2_MODE_HIGHQUALITY;
+ else
+ sensor->streamcap.capturemode = 0;
+ ret = ov2640_init_mode(sensor);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ if (size > 640 * 480)
+ sensor->streamcap.capturemode = V4L2_MODE_HIGHQUALITY;
+ else
+ sensor->streamcap.capturemode = 0;
+ ret = ov2640_init_mode(sensor);
+
+ /* YUYV: width * 2, YY: width */
+ ret = ov2640_set_format(sensor, V4L2_PIX_FMT_YUV420);
+ break;
+ default:
+ pr_debug("case not supported\n");
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct sensor *sensor = s->priv;
+
+ pr_debug("In ov2640:ioctl_g_fmt_cap.\n");
+
+ f->fmt.pix = sensor->pix;
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array. Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int ret = 0;
+
+ pr_debug("In ov2640:ioctl_g_ctrl\n");
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ vc->value = ov2640_data.brightness;
+ break;
+ case V4L2_CID_HUE:
+ vc->value = ov2640_data.hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ vc->value = ov2640_data.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ vc->value = ov2640_data.saturation;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ vc->value = ov2640_data.red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ vc->value = ov2640_data.blue;
+ break;
+ case V4L2_CID_EXPOSURE:
+ vc->value = ov2640_data.ae_mode;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array). Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int retval = 0;
+
+ pr_debug("In ov2640:ioctl_s_ctrl %d\n", vc->id);
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ pr_debug(" V4L2_CID_BRIGHTNESS\n");
+ break;
+ case V4L2_CID_CONTRAST:
+ pr_debug(" V4L2_CID_CONTRAST\n");
+ break;
+ case V4L2_CID_SATURATION:
+ pr_debug(" V4L2_CID_SATURATION\n");
+ break;
+ case V4L2_CID_HUE:
+ pr_debug(" V4L2_CID_HUE\n");
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ pr_debug(
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_RED_BALANCE:
+ pr_debug(" V4L2_CID_RED_BALANCE\n");
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ pr_debug(" V4L2_CID_BLUE_BALANCE\n");
+ break;
+ case V4L2_CID_GAMMA:
+ pr_debug(" V4L2_CID_GAMMA\n");
+ break;
+ case V4L2_CID_EXPOSURE:
+ pr_debug(" V4L2_CID_EXPOSURE\n");
+ break;
+ case V4L2_CID_AUTOGAIN:
+ pr_debug(" V4L2_CID_AUTOGAIN\n");
+ break;
+ case V4L2_CID_GAIN:
+ pr_debug(" V4L2_CID_GAIN\n");
+ break;
+ case V4L2_CID_HFLIP:
+ pr_debug(" V4L2_CID_HFLIP\n");
+ break;
+ case V4L2_CID_VFLIP:
+ pr_debug(" V4L2_CID_VFLIP\n");
+ break;
+ default:
+ pr_debug(" Default case\n");
+ retval = -EPERM;
+ break;
+ }
+
+ return retval;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ pr_debug("In ov2640:ioctl_init\n");
+
+ return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ struct sensor *sensor = s->priv;
+ u32 tgt_xclk; /* target xclk */
+
+ pr_debug("In ov2640:ioctl_dev_init\n");
+
+ gpio_sensor_active();
+ ov2640_data.on = true;
+
+ tgt_xclk = ov2640_data.mclk;
+ tgt_xclk = min(tgt_xclk, (u32)OV2640_XCLK_MAX);
+ tgt_xclk = max(tgt_xclk, (u32)OV2640_XCLK_MIN);
+ ov2640_data.mclk = tgt_xclk;
+
+ pr_debug(" Setting mclk to %d MHz\n",
+ tgt_xclk / 1000000);
+ set_mclk_rate(&ov2640_data.mclk);
+
+ return ov2640_init_mode(sensor);
+}
+
+/*!
+ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the device when slave detaches to the master.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+ pr_debug("In ov2640:ioctl_dev_exit\n");
+
+ gpio_sensor_inactive();
+
+ return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc ov2640_ioctl_desc[] = {
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+ {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*)ioctl_dev_exit},
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
+/* {vidioc_int_g_needs_reset_num,
+ (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+ {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
+/* {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */
+/* {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
+ {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_s_fmt_cap},
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
+/* {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
+};
+
+static struct v4l2_int_slave ov2640_slave = {
+ .ioctls = ov2640_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(ov2640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov2640_int_device = {
+ .module = THIS_MODULE,
+ .name = "ov2640",
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &ov2640_slave,
+ },
+};
+
+/*!
+ * ov2640 I2C attach function
+ * Function set in i2c_driver struct.
+ * Called by insmod ov2640_camera.ko.
+ *
+ * @param client struct i2c_client*
+ * @return Error code indicating success or failure
+ */
+static int ov2640_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int retval;
+ struct mxc_camera_platform_data *plat_data = client->dev.platform_data;
+
+ pr_debug("In ov2640_probe (RH_BT565)\n");
+
+ /* Set initial values for the sensor struct. */
+ memset(&ov2640_data, 0, sizeof(ov2640_data));
+ ov2640_data.i2c_client = client;
+ ov2640_data.mclk = 24000000;
+ ov2640_data.mclk = plat_data->mclk;
+ ov2640_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ ov2640_data.pix.width = 800;
+ ov2640_data.pix.height = 600;
+ ov2640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY
+ | V4L2_CAP_TIMEPERFRAME;
+ ov2640_data.streamcap.capturemode = 0;
+ ov2640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+ ov2640_data.streamcap.timeperframe.numerator = 1;
+
+ if (plat_data->io_regulator) {
+ io_regulator =
+ regulator_get(&client->dev, plat_data->io_regulator);
+ if (!IS_ERR(io_regulator)) {
+ regulator_set_voltage(io_regulator, 2800000, 2800000);
+ if (regulator_enable(io_regulator) != 0) {
+ pr_err("%s:io set voltage error\n", __func__);
+ goto err1;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:io set voltage ok\n", __func__);
+ }
+ } else
+ io_regulator = NULL;
+ }
+
+ if (plat_data->core_regulator) {
+ core_regulator =
+ regulator_get(&client->dev, plat_data->core_regulator);
+ if (!IS_ERR(core_regulator)) {
+ regulator_set_voltage(core_regulator,
+ 1300000, 1300000);
+ if (regulator_enable(core_regulator) != 0) {
+ pr_err("%s:core set voltage error\n", __func__);
+ goto err2;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:core set voltage ok\n", __func__);
+ }
+ } else
+ core_regulator = NULL;
+ }
+
+ if (plat_data->analog_regulator) {
+ analog_regulator =
+ regulator_get(&client->dev, plat_data->analog_regulator);
+ if (!IS_ERR(analog_regulator)) {
+ regulator_set_voltage(analog_regulator, 2000000, 2000000);
+ if (regulator_enable(analog_regulator) != 0) {
+ pr_err("%s:analog set voltage error\n",
+ __func__);
+ goto err3;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:analog set voltage ok\n", __func__);
+ }
+ } else
+ analog_regulator = NULL;
+ }
+
+ if (plat_data->gpo_regulator) {
+ gpo_regulator =
+ regulator_get(&client->dev, plat_data->gpo_regulator);
+ if (!IS_ERR(gpo_regulator)) {
+ if (regulator_enable(gpo_regulator) != 0) {
+ pr_err("%s:gpo3 set voltage error\n", __func__);
+ goto err4;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:gpo3 set voltage ok\n", __func__);
+ }
+ } else
+ gpo_regulator = NULL;
+ }
+
+ /* This function attaches this structure to the /dev/video0 device.
+ * The pointer in priv points to the ov2640_data structure here.*/
+ ov2640_int_device.priv = &ov2640_data;
+ retval = v4l2_int_device_register(&ov2640_int_device);
+
+ return retval;
+
+err4:
+ if (analog_regulator) {
+ regulator_disable(analog_regulator);
+ regulator_put(analog_regulator);
+ }
+err3:
+ if (core_regulator) {
+ regulator_disable(core_regulator);
+ regulator_put(core_regulator);
+ }
+err2:
+ if (io_regulator) {
+ regulator_disable(io_regulator);
+ regulator_put(io_regulator);
+ }
+err1:
+ return -1;
+}
+
+/*!
+ * ov2640 I2C detach function
+ * Called on rmmod ov2640_camera.ko
+ *
+ * @param client struct i2c_client*
+ * @return Error code indicating success or failure
+ */
+static int ov2640_remove(struct i2c_client *client)
+{
+ pr_debug("In ov2640_remove\n");
+
+ v4l2_int_device_unregister(&ov2640_int_device);
+
+ if (gpo_regulator) {
+ regulator_disable(gpo_regulator);
+ regulator_put(gpo_regulator);
+ }
+
+ if (analog_regulator) {
+ regulator_disable(analog_regulator);
+ regulator_put(analog_regulator);
+ }
+
+ if (core_regulator) {
+ regulator_disable(core_regulator);
+ regulator_put(core_regulator);
+ }
+
+ if (io_regulator) {
+ regulator_disable(io_regulator);
+ regulator_put(io_regulator);
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id ov2640_id[] = {
+ {"ov2640", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov2640_id);
+
+static struct i2c_driver ov2640_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ov2640",
+ },
+ .probe = ov2640_probe,
+ .remove = ov2640_remove,
+ .id_table = ov2640_id,
+/* To add power management add .suspend and .resume functions */
+};
+
+/*!
+ * ov2640 init function
+ * Called by insmod ov2640_camera.ko.
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int ov2640_init(void)
+{
+ u8 err;
+
+ pr_debug("In ov2640_init\n");
+
+ err = i2c_add_driver(&ov2640_i2c_driver);
+ if (err != 0)
+ pr_err("%s:driver registration failed, error=%d \n",
+ __func__, err);
+
+ return err;
+}
+
+/*!
+ * OV2640 cleanup function
+ * Called on rmmod ov2640_camera.ko
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit ov2640_clean(void)
+{
+ pr_debug("In ov2640_clean\n");
+ i2c_del_driver(&ov2640_i2c_driver);
+}
+
+module_init(ov2640_init);
+module_exit(ov2640_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV2640 Camera Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/ov3640.c b/drivers/media/video/mxc/capture/ov3640.c
new file mode 100644
index 000000000000..3ff269dda924
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ov3640.c
@@ -0,0 +1,1086 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-int-device.h>
+#include "mxc_v4l2_capture.h"
+
+#define OV3640_VOLTAGE_ANALOG 2800000
+#define OV3640_VOLTAGE_DIGITAL_CORE 1500000
+#define OV3640_VOLTAGE_DIGITAL_IO 1800000
+
+
+/* Check these values! */
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+#define OV3640_XCLK_MIN 6000000
+#define OV3640_XCLK_MAX 24000000
+
+enum ov3640_mode {
+ ov3640_mode_MIN = 0,
+ ov3640_mode_VGA_640_480 = 0,
+ ov3640_mode_QVGA_320_240 = 1,
+ ov3640_mode_QXGA_2048_1536 = 2,
+ ov3640_mode_XGA_1024_768 = 3,
+ ov3640_mode_MAX = 3
+};
+
+enum ov3640_frame_rate {
+ ov3640_15_fps,
+ ov3640_30_fps
+};
+
+struct reg_value {
+ u16 u16RegAddr;
+ u8 u8Val;
+ u8 u8Mask;
+ u32 u32Delay_ms;
+};
+
+struct ov3640_mode_info {
+ enum ov3640_mode mode;
+ u32 width;
+ u32 height;
+ struct reg_value *init_data_ptr;
+ u32 init_data_size;
+};
+
+/*!
+ * Maintains the information on the current state of the sesor.
+ */
+struct sensor {
+ const struct ov3640_platform_data *platform_data;
+ struct v4l2_int_device *v4l2_int_device;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ struct v4l2_captureparm streamcap;
+ bool on;
+
+ /* control settings */
+ int brightness;
+ int hue;
+ int contrast;
+ int saturation;
+ int red;
+ int green;
+ int blue;
+ int ae_mode;
+
+ u32 mclk;
+ int csi;
+} ov3640_data;
+
+static struct reg_value ov3640_setting_15fps_QXGA_2048_1536[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+ {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+ {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+ {0x3400, 0x00, 0, 0}, {0x3404, 0x02, 0, 0}, {0x3600, 0xc4, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+ {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+ {0x3025, 0x00, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x00, 0, 0},
+ {0x335f, 0x68, 0, 0}, {0x3360, 0x00, 0, 0}, {0x3361, 0x00, 0, 0},
+ {0x3362, 0x68, 0, 0}, {0x3363, 0x00, 0, 0}, {0x3364, 0x00, 0, 0},
+ {0x3403, 0x00, 0, 0}, {0x3088, 0x08, 0, 0}, {0x3089, 0x00, 0, 0},
+ {0x308a, 0x06, 0, 0}, {0x308b, 0x00, 0, 0}, {0x307c, 0x10, 0, 0},
+ {0x3090, 0xc0, 0, 0}, {0x304c, 0x84, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3012, 0x00, 0, 0},
+ {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0}, {0x3022, 0x00, 0, 0},
+ {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0}, {0x3025, 0x18, 0, 0},
+ {0x3026, 0x06, 0, 0}, {0x3027, 0x0c, 0, 0}, {0x302a, 0x06, 0, 0},
+ {0x302b, 0x20, 0, 0}, {0x3075, 0x44, 0, 0}, {0x300d, 0x00, 0, 0},
+ {0x30d7, 0x00, 0, 0}, {0x3069, 0x40, 0, 0}, {0x303e, 0x01, 0, 0},
+ {0x303f, 0x80, 0, 0}, {0x3302, 0x20, 0, 0}, {0x335f, 0x68, 0, 0},
+ {0x3360, 0x18, 0, 0}, {0x3361, 0x0c, 0, 0}, {0x3362, 0x68, 0, 0},
+ {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0}, {0x3403, 0x42, 0, 0},
+ {0x3088, 0x08, 0, 0}, {0x3089, 0x00, 0, 0}, {0x308a, 0x06, 0, 0},
+ {0x308b, 0x00, 0, 0},
+};
+
+static struct reg_value ov3640_setting_15fps_XGA_1024_768[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+ {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+ {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+ {0x3400, 0x01, 0, 0}, {0x3404, 0x1d, 0, 0}, {0x3600, 0xc4, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+ {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+ {0x3025, 0x00, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x00, 0, 0},
+ {0x335f, 0x68, 0, 0}, {0x3360, 0x00, 0, 0}, {0x3361, 0x00, 0, 0},
+ {0x3362, 0x34, 0, 0}, {0x3363, 0x00, 0, 0}, {0x3364, 0x00, 0, 0},
+ {0x3403, 0x00, 0, 0}, {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0},
+ {0x308a, 0x03, 0, 0}, {0x308b, 0x00, 0, 0}, {0x307c, 0x10, 0, 0},
+ {0x3090, 0xc0, 0, 0}, {0x304c, 0x84, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3011, 0x01, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_XGA_1024_768[] = {
+ {0x0, 0x0, 0}
+};
+
+static struct reg_value ov3640_setting_15fps_VGA_640_480[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+ {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+ {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+ {0x3400, 0x00, 0, 0}, {0x3404, 0x42, 0, 0}, {0x3600, 0xc4, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+ {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+ {0x3025, 0x00, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x00, 0, 0},
+ {0x335f, 0x68, 0, 0}, {0x3360, 0x00, 0, 0}, {0x3361, 0x00, 0, 0},
+ {0x3362, 0x12, 0, 0}, {0x3363, 0x80, 0, 0}, {0x3364, 0xe0, 0, 0},
+ {0x3403, 0x00, 0, 0}, {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0},
+ {0x308a, 0x01, 0, 0}, {0x308b, 0xe0, 0, 0}, {0x307c, 0x10, 0, 0},
+ {0x3090, 0xc0, 0, 0}, {0x304c, 0x84, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3011, 0x00, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_VGA_640_480[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+ {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x0c, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x3300, 0x13, 0, 0}, {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0},
+ {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0x10, 0x30, 0}, {0x3304, 0x00, 0x03, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x02, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3012, 0x10, 0, 0},
+ {0x3023, 0x06, 0, 0}, {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0},
+ {0x302a, 0x03, 0, 0}, {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0},
+ {0x300d, 0x01, 0, 0}, {0x30d7, 0x80, 0x80, 0}, {0x3069, 0x00, 0x40, 0},
+ {0x303e, 0x00, 0, 0}, {0x303f, 0xc0, 0, 0}, {0x3302, 0x20, 0x20, 0},
+ {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0},
+ {0x3362, 0x12, 0, 0}, {0x3363, 0x88, 0, 0}, {0x3364, 0xe4, 0, 0},
+ {0x3403, 0x42, 0, 0}, {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0},
+ {0x308a, 0x01, 0, 0}, {0x308b, 0xe0, 0, 0}, {0x3362, 0x12, 0, 0},
+ {0x3363, 0x88, 0, 0}, {0x3364, 0xe4, 0, 0}, {0x3403, 0x42, 0, 0},
+ {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0}, {0x308a, 0x01, 0, 0},
+ {0x308b, 0xe0, 0, 0}, {0x300e, 0x37, 0, 0}, {0x300f, 0xe1, 0, 0},
+ {0x3010, 0x22, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x84, 0, 0},
+ {0x3014, 0x04, 0, 0}, {0x3015, 0x02, 0, 0}, {0x302e, 0x00, 0, 0},
+ {0x302d, 0x00, 0, 0},
+};
+
+static struct reg_value ov3640_setting_15fps_QVGA_320_240[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x00, 0, 0}, {0x304c, 0x81, 0, 0},
+ {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x04, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0}, {0x30ba, 0x04, 0, 0},
+ {0x30bb, 0x08, 0, 0}, {0x3507, 0x06, 0, 0}, {0x350a, 0x4f, 0, 0},
+ {0x3100, 0x02, 0, 0}, {0x3301, 0xde, 0, 0}, {0x3304, 0x00, 0, 0},
+ {0x3400, 0x00, 0, 0}, {0x3404, 0x42, 0, 0}, {0x3600, 0xc4, 0, 0},
+ {0x3302, 0xef, 0, 0}, {0x3020, 0x01, 0, 0}, {0x3021, 0x1d, 0, 0},
+ {0x3022, 0x00, 0, 0}, {0x3023, 0x0a, 0, 0}, {0x3024, 0x08, 0, 0},
+ {0x3025, 0x00, 0, 0}, {0x3026, 0x06, 0, 0}, {0x3027, 0x00, 0, 0},
+ {0x335f, 0x68, 0, 0}, {0x3360, 0x00, 0, 0}, {0x3361, 0x00, 0, 0},
+ {0x3362, 0x01, 0, 0}, {0x3363, 0x40, 0, 0}, {0x3364, 0xf0, 0, 0},
+ {0x3403, 0x00, 0, 0}, {0x3088, 0x01, 0, 0}, {0x3089, 0x40, 0, 0},
+ {0x308a, 0x00, 0, 0}, {0x308b, 0xf0, 0, 0}, {0x307c, 0x10, 0, 0},
+ {0x3090, 0xc0, 0, 0}, {0x304c, 0x84, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3011, 0x01, 0, 0},
+};
+
+static struct reg_value ov3640_setting_30fps_QVGA_320_240[] = {
+ {0x3012, 0x80, 0, 0}, {0x304d, 0x45, 0, 0}, {0x30a7, 0x5e, 0, 0},
+ {0x3087, 0x16, 0, 0}, {0x309c, 0x1a, 0, 0}, {0x30a2, 0xe4, 0, 0},
+ {0x30aa, 0x42, 0, 0}, {0x30b0, 0xff, 0, 0}, {0x30b1, 0xff, 0, 0},
+ {0x30b2, 0x10, 0, 0}, {0x300e, 0x32, 0, 0}, {0x300f, 0x21, 0, 0},
+ {0x3010, 0x20, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x82, 0, 0},
+ {0x30d7, 0x10, 0, 0}, {0x30d9, 0x0d, 0, 0}, {0x30db, 0x08, 0, 0},
+ {0x3016, 0x82, 0, 0}, {0x3018, 0x38, 0, 0}, {0x3019, 0x30, 0, 0},
+ {0x301a, 0x61, 0, 0}, {0x307d, 0x00, 0, 0}, {0x3087, 0x02, 0, 0},
+ {0x3082, 0x20, 0, 0}, {0x3015, 0x12, 0, 0}, {0x3014, 0x0c, 0, 0},
+ {0x3013, 0xf7, 0, 0}, {0x303c, 0x08, 0, 0}, {0x303d, 0x18, 0, 0},
+ {0x303e, 0x06, 0, 0}, {0x303f, 0x0c, 0, 0}, {0x3030, 0x62, 0, 0},
+ {0x3031, 0x26, 0, 0}, {0x3032, 0xe6, 0, 0}, {0x3033, 0x6e, 0, 0},
+ {0x3034, 0xea, 0, 0}, {0x3035, 0xae, 0, 0}, {0x3036, 0xa6, 0, 0},
+ {0x3037, 0x6a, 0, 0}, {0x3104, 0x02, 0, 0}, {0x3105, 0xfd, 0, 0},
+ {0x3106, 0x00, 0, 0}, {0x3107, 0xff, 0, 0}, {0x3300, 0x12, 0, 0},
+ {0x3301, 0xde, 0, 0}, {0x3302, 0xcf, 0, 0}, {0x3312, 0x26, 0, 0},
+ {0x3314, 0x42, 0, 0}, {0x3313, 0x2b, 0, 0}, {0x3315, 0x42, 0, 0},
+ {0x3310, 0xd0, 0, 0}, {0x3311, 0xbd, 0, 0}, {0x330c, 0x18, 0, 0},
+ {0x330d, 0x18, 0, 0}, {0x330e, 0x56, 0, 0}, {0x330f, 0x5c, 0, 0},
+ {0x330b, 0x1c, 0, 0}, {0x3306, 0x5c, 0, 0}, {0x3307, 0x11, 0, 0},
+ {0x336a, 0x52, 0, 0}, {0x3370, 0x46, 0, 0}, {0x3376, 0x38, 0, 0},
+ {0x3300, 0x13, 0, 0}, {0x30b8, 0x20, 0, 0}, {0x30b9, 0x17, 0, 0},
+ {0x30ba, 0x04, 0, 0}, {0x30bb, 0x08, 0, 0}, {0x3100, 0x02, 0, 0},
+ {0x3301, 0x10, 0x30, 0}, {0x3304, 0x00, 0x03, 0}, {0x3400, 0x00, 0, 0},
+ {0x3404, 0x02, 0, 0}, {0x3600, 0xc0, 0, 0}, {0x308d, 0x04, 0, 0},
+ {0x3086, 0x03, 0, 0}, {0x3086, 0x00, 0, 0}, {0x3012, 0x10, 0, 0},
+ {0x3023, 0x06, 0, 0}, {0x3026, 0x03, 0, 0}, {0x3027, 0x04, 0, 0},
+ {0x302a, 0x03, 0, 0}, {0x302b, 0x10, 0, 0}, {0x3075, 0x24, 0, 0},
+ {0x300d, 0x01, 0, 0}, {0x30d7, 0x80, 0x80, 0}, {0x3069, 0x00, 0x40, 0},
+ {0x303e, 0x00, 0, 0}, {0x303f, 0xc0, 0, 0}, {0x3302, 0x20, 0x20, 0},
+ {0x335f, 0x34, 0, 0}, {0x3360, 0x0c, 0, 0}, {0x3361, 0x04, 0, 0},
+ {0x3362, 0x34, 0, 0}, {0x3363, 0x08, 0, 0}, {0x3364, 0x04, 0, 0},
+ {0x3403, 0x42, 0, 0}, {0x3088, 0x04, 0, 0}, {0x3089, 0x00, 0, 0},
+ {0x308a, 0x03, 0, 0}, {0x308b, 0x00, 0, 0}, {0x3362, 0x12, 0, 0},
+ {0x3363, 0x88, 0, 0}, {0x3364, 0xe4, 0, 0}, {0x3403, 0x42, 0, 0},
+ {0x3088, 0x02, 0, 0}, {0x3089, 0x80, 0, 0}, {0x308a, 0x01, 0, 0},
+ {0x308b, 0xe0, 0, 0}, {0x300e, 0x37, 0, 0}, {0x300f, 0xe1, 0, 0},
+ {0x3010, 0x22, 0, 0}, {0x3011, 0x01, 0, 0}, {0x304c, 0x84, 0, 0},
+};
+
+static struct ov3640_mode_info ov3640_mode_info_data[2][ov3640_mode_MAX + 1] = {
+ {
+ {ov3640_mode_VGA_640_480, 640, 480,
+ ov3640_setting_15fps_VGA_640_480,
+ ARRAY_SIZE(ov3640_setting_15fps_VGA_640_480)},
+ {ov3640_mode_QVGA_320_240, 320, 240,
+ ov3640_setting_15fps_QVGA_320_240,
+ ARRAY_SIZE(ov3640_setting_15fps_QVGA_320_240)},
+ {ov3640_mode_XGA_1024_768, 1024, 768,
+ ov3640_setting_15fps_XGA_1024_768,
+ ARRAY_SIZE(ov3640_setting_15fps_XGA_1024_768)},
+ {ov3640_mode_QXGA_2048_1536, 2048, 1536,
+ ov3640_setting_15fps_QXGA_2048_1536,
+ ARRAY_SIZE(ov3640_setting_15fps_QXGA_2048_1536)},
+ },
+ {
+ {ov3640_mode_VGA_640_480, 640, 480,
+ ov3640_setting_30fps_VGA_640_480,
+ ARRAY_SIZE(ov3640_setting_30fps_VGA_640_480)},
+ {ov3640_mode_QVGA_320_240, 320, 240,
+ ov3640_setting_30fps_QVGA_320_240,
+ ARRAY_SIZE(ov3640_setting_30fps_QVGA_320_240)},
+ {ov3640_mode_XGA_1024_768, 1024, 768,
+ ov3640_setting_30fps_XGA_1024_768,
+ ARRAY_SIZE(ov3640_setting_30fps_XGA_1024_768)},
+ {ov3640_mode_QXGA_2048_1536, 0, 0, NULL, 0},
+ },
+};
+
+static struct regulator *io_regulator;
+static struct regulator *core_regulator;
+static struct regulator *analog_regulator;
+static struct regulator *gpo_regulator;
+
+static int ov3640_probe(struct i2c_client *adapter,
+ const struct i2c_device_id *device_id);
+static int ov3640_remove(struct i2c_client *client);
+
+static s32 ov3640_read_reg(u16 reg, u8 *val);
+static s32 ov3640_write_reg(u16 reg, u8 val);
+
+static const struct i2c_device_id ov3640_id[] = {
+ {"ov3640", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ov3640_id);
+
+static struct i2c_driver ov3640_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ov3640",
+ },
+ .probe = ov3640_probe,
+ .remove = ov3640_remove,
+ .id_table = ov3640_id,
+};
+
+extern void gpio_sensor_active(unsigned int csi_index);
+extern void gpio_sensor_inactive(unsigned int csi);
+
+static s32 ov3640_write_reg(u16 reg, u8 val)
+{
+ u8 au8Buf[3] = {0};
+
+ au8Buf[0] = reg >> 8;
+ au8Buf[1] = reg & 0xff;
+ au8Buf[2] = val;
+
+ if (i2c_master_send(ov3640_data.i2c_client, au8Buf, 3) < 0) {
+ pr_err("%s:write reg error:reg=%x,val=%x\n",
+ __func__, reg, val);
+ return -1;
+ }
+
+ return 0;
+}
+
+static s32 ov3640_read_reg(u16 reg, u8 *val)
+{
+ u8 au8RegBuf[2] = {0};
+ u8 u8RdVal = 0;
+
+ au8RegBuf[0] = reg >> 8;
+ au8RegBuf[1] = reg & 0xff;
+
+ if (2 != i2c_master_send(ov3640_data.i2c_client, au8RegBuf, 2)) {
+ pr_err("%s:write reg error:reg=%x\n",
+ __func__, reg);
+ return -1;
+ }
+
+ if (1 != i2c_master_recv(ov3640_data.i2c_client, &u8RdVal, 1)) {
+ pr_err("%s:read reg error:reg=%x,val=%x\n",
+ __func__, reg, u8RdVal);
+ return -1;
+ }
+
+ *val = u8RdVal;
+
+ return u8RdVal;
+}
+
+static int ov3640_init_mode(enum ov3640_frame_rate frame_rate,
+ enum ov3640_mode mode)
+{
+ struct reg_value *pModeSetting = NULL;
+ s32 i = 0;
+ s32 iModeSettingArySize = 0;
+ register u32 Delay_ms = 0;
+ register u16 RegAddr = 0;
+ register u8 Mask = 0;
+ register u8 Val = 0;
+ u8 RegVal = 0;
+ int retval = 0;
+
+ if (mode > ov3640_mode_MAX || mode < ov3640_mode_MIN) {
+ pr_err("Wrong ov3640 mode detected!\n");
+ return -1;
+ }
+
+ pModeSetting = ov3640_mode_info_data[frame_rate][mode].init_data_ptr;
+ iModeSettingArySize =
+ ov3640_mode_info_data[frame_rate][mode].init_data_size;
+
+ ov3640_data.pix.width = ov3640_mode_info_data[frame_rate][mode].width;
+ ov3640_data.pix.height = ov3640_mode_info_data[frame_rate][mode].height;
+
+ for (i = 0; i < iModeSettingArySize; ++i, ++pModeSetting) {
+ Delay_ms = pModeSetting->u32Delay_ms;
+ RegAddr = pModeSetting->u16RegAddr;
+ Val = pModeSetting->u8Val;
+ Mask = pModeSetting->u8Mask;
+
+ if (Mask) {
+ retval = ov3640_read_reg(RegAddr, &RegVal);
+ if (retval < 0)
+ goto err;
+
+ RegVal &= ~(u8)Mask;
+ Val &= Mask;
+ Val |= RegVal;
+ }
+
+ retval = ov3640_write_reg(RegAddr, Val);
+ if (retval < 0)
+ goto err;
+
+ if (Delay_ms)
+ msleep(Delay_ms);
+ }
+err:
+ return retval;
+}
+
+/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ if (s == NULL) {
+ pr_err(" ERROR!! no slave device set!\n");
+ return -1;
+ }
+
+ memset(p, 0, sizeof(*p));
+ p->u.bt656.clock_curr = ov3640_data.mclk;
+ pr_debug(" clock_curr=mclk=%d\n", ov3640_data.mclk);
+ p->if_type = V4L2_IF_TYPE_BT656;
+ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+ p->u.bt656.clock_min = OV3640_XCLK_MIN;
+ p->u.bt656.clock_max = OV3640_XCLK_MAX;
+ p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */
+
+ return 0;
+}
+
+/*!
+ * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @on: indicates power mode (on or off)
+ *
+ * Turns the power on or off, depending on the value of on and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct sensor *sensor = s->priv;
+
+ if (on && !sensor->on) {
+ gpio_sensor_active(ov3640_data.csi);
+ if (io_regulator)
+ if (regulator_enable(io_regulator) != 0)
+ return -EIO;
+ if (core_regulator)
+ if (regulator_enable(core_regulator) != 0)
+ return -EIO;
+ if (gpo_regulator)
+ if (regulator_enable(gpo_regulator) != 0)
+ return -EIO;
+ if (analog_regulator)
+ if (regulator_enable(analog_regulator) != 0)
+ return -EIO;
+ } else if (!on && sensor->on) {
+ if (analog_regulator)
+ regulator_disable(analog_regulator);
+ if (core_regulator)
+ regulator_disable(core_regulator);
+ if (io_regulator)
+ regulator_disable(io_regulator);
+ if (gpo_regulator)
+ regulator_disable(gpo_regulator);
+ gpio_sensor_inactive(ov3640_data.csi);
+ }
+
+ sensor->on = on;
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+ int ret = 0;
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cparm->capability = sensor->streamcap.capability;
+ cparm->timeperframe = sensor->streamcap.timeperframe;
+ cparm->capturemode = sensor->streamcap.capturemode;
+ ret = 0;
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_debug(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible. If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+ u32 tgt_fps; /* target frames per secound */
+ enum ov3640_frame_rate frame_rate;
+ int ret = 0;
+
+ switch (a->type) {
+ /* This is the only case currently handled. */
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ /* Check that the new frame rate is allowed. */
+ if ((timeperframe->numerator == 0) ||
+ (timeperframe->denominator == 0)) {
+ timeperframe->denominator = DEFAULT_FPS;
+ timeperframe->numerator = 1;
+ }
+
+ tgt_fps = timeperframe->denominator /
+ timeperframe->numerator;
+
+ if (tgt_fps > MAX_FPS) {
+ timeperframe->denominator = MAX_FPS;
+ timeperframe->numerator = 1;
+ } else if (tgt_fps < MIN_FPS) {
+ timeperframe->denominator = MIN_FPS;
+ timeperframe->numerator = 1;
+ }
+
+ /* Actual frame rate we use */
+ tgt_fps = timeperframe->denominator /
+ timeperframe->numerator;
+
+ if (tgt_fps == 15)
+ frame_rate = ov3640_15_fps;
+ else if (tgt_fps == 30)
+ frame_rate = ov3640_30_fps;
+ else {
+ pr_err(" The camera frame rate is not supported!\n");
+ return -EINVAL;
+ }
+
+ sensor->streamcap.timeperframe = *timeperframe;
+ sensor->streamcap.capturemode =
+ (u32)a->parm.capture.capturemode;
+
+ ret = ov3640_init_mode(frame_rate,
+ sensor->streamcap.capturemode);
+ break;
+
+ /* These are all the possible cases. */
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ pr_debug(" type is not " \
+ "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n",
+ a->type);
+ ret = -EINVAL;
+ break;
+
+ default:
+ pr_debug(" type is unknown - %d\n", a->type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct sensor *sensor = s->priv;
+
+ f->fmt.pix = sensor->pix;
+
+ return 0;
+}
+
+/*!
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array. Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int ret = 0;
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ vc->value = ov3640_data.brightness;
+ break;
+ case V4L2_CID_HUE:
+ vc->value = ov3640_data.hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ vc->value = ov3640_data.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ vc->value = ov3640_data.saturation;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ vc->value = ov3640_data.red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ vc->value = ov3640_data.blue;
+ break;
+ case V4L2_CID_EXPOSURE:
+ vc->value = ov3640_data.ae_mode;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/*!
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array). Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int retval = 0;
+
+ pr_debug("In ov3640:ioctl_s_ctrl %d\n",
+ vc->id);
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ break;
+ case V4L2_CID_CONTRAST:
+ break;
+ case V4L2_CID_SATURATION:
+ break;
+ case V4L2_CID_HUE:
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ break;
+ case V4L2_CID_RED_BALANCE:
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ break;
+ case V4L2_CID_GAMMA:
+ break;
+ case V4L2_CID_EXPOSURE:
+ break;
+ case V4L2_CID_AUTOGAIN:
+ break;
+ case V4L2_CID_GAIN:
+ break;
+ case V4L2_CID_HFLIP:
+ break;
+ case V4L2_CID_VFLIP:
+ break;
+ default:
+ retval = -EPERM;
+ break;
+ }
+
+ return retval;
+}
+
+/*!
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+
+ return 0;
+}
+
+/*!
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ struct sensor *sensor = s->priv;
+ u32 tgt_xclk; /* target xclk */
+ u32 tgt_fps; /* target frames per secound */
+ enum ov3640_frame_rate frame_rate;
+
+ gpio_sensor_active(ov3640_data.csi);
+ ov3640_data.on = true;
+
+ /* mclk */
+ tgt_xclk = ov3640_data.mclk;
+ tgt_xclk = min(tgt_xclk, (u32)OV3640_XCLK_MAX);
+ tgt_xclk = max(tgt_xclk, (u32)OV3640_XCLK_MIN);
+ ov3640_data.mclk = tgt_xclk;
+
+ pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000);
+ set_mclk_rate(&ov3640_data.mclk, ov3640_data.csi);
+
+ /* Default camera frame rate is set in probe */
+ tgt_fps = sensor->streamcap.timeperframe.denominator /
+ sensor->streamcap.timeperframe.numerator;
+
+ if (tgt_fps == 15)
+ frame_rate = ov3640_15_fps;
+ else if (tgt_fps == 30)
+ frame_rate = ov3640_30_fps;
+ else
+ return -EINVAL; /* Only support 15fps or 30fps now. */
+
+ return ov3640_init_mode(frame_rate,
+ sensor->streamcap.capturemode);
+}
+
+/*!
+ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the device when slave detaches to the master.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+ gpio_sensor_inactive(ov3640_data.csi);
+
+ return 0;
+}
+
+/*!
+ * This structure defines all the ioctls for this module and links them to the
+ * enumeration.
+ */
+static struct v4l2_int_ioctl_desc ov3640_ioctl_desc[] = {
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
+ {vidioc_int_dev_exit_num, ioctl_dev_exit},
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
+/* {vidioc_int_g_needs_reset_num,
+ (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
+/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
+ {vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
+/* {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */
+/* {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
+ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
+/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_fmt_cap}, */
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
+/* {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
+};
+
+static struct v4l2_int_slave ov3640_slave = {
+ .ioctls = ov3640_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(ov3640_ioctl_desc),
+};
+
+static struct v4l2_int_device ov3640_int_device = {
+ .module = THIS_MODULE,
+ .name = "ov3640",
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &ov3640_slave,
+ },
+};
+
+/*!
+ * ov3640 I2C probe function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int ov3640_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int retval;
+ struct mxc_camera_platform_data *plat_data = client->dev.platform_data;
+
+ /* Set initial values for the sensor struct. */
+ memset(&ov3640_data, 0, sizeof(ov3640_data));
+ ov3640_data.mclk = 24000000; /* 6 - 54 MHz, typical 24MHz */
+ ov3640_data.mclk = plat_data->mclk;
+ ov3640_data.csi = plat_data->csi;
+
+ ov3640_data.i2c_client = client;
+ ov3640_data.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ ov3640_data.pix.width = 640;
+ ov3640_data.pix.height = 480;
+ ov3640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
+ V4L2_CAP_TIMEPERFRAME;
+ ov3640_data.streamcap.capturemode = 0;
+ ov3640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
+ ov3640_data.streamcap.timeperframe.numerator = 1;
+
+ if (plat_data->io_regulator) {
+ io_regulator = regulator_get(&client->dev,
+ plat_data->io_regulator);
+ if (!IS_ERR(io_regulator)) {
+ regulator_set_voltage(io_regulator,
+ OV3640_VOLTAGE_DIGITAL_IO,
+ OV3640_VOLTAGE_DIGITAL_IO);
+ if (regulator_enable(io_regulator) != 0) {
+ pr_err("%s:io set voltage error\n", __func__);
+ goto err1;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:io set voltage ok\n", __func__);
+ }
+ } else
+ io_regulator = NULL;
+ }
+
+ if (plat_data->core_regulator) {
+ core_regulator = regulator_get(&client->dev,
+ plat_data->core_regulator);
+ if (!IS_ERR(core_regulator)) {
+ regulator_set_voltage(core_regulator,
+ OV3640_VOLTAGE_DIGITAL_CORE,
+ OV3640_VOLTAGE_DIGITAL_CORE);
+ if (regulator_enable(core_regulator) != 0) {
+ pr_err("%s:core set voltage error\n", __func__);
+ goto err2;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:core set voltage ok\n", __func__);
+ }
+ } else
+ core_regulator = NULL;
+ }
+
+ if (plat_data->analog_regulator) {
+ analog_regulator = regulator_get(&client->dev,
+ plat_data->analog_regulator);
+ if (!IS_ERR(analog_regulator)) {
+ regulator_set_voltage(analog_regulator,
+ OV3640_VOLTAGE_ANALOG,
+ OV3640_VOLTAGE_ANALOG);
+ if (regulator_enable(analog_regulator) != 0) {
+ pr_err("%s:analog set voltage error\n",
+ __func__);
+ goto err3;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:analog set voltage ok\n", __func__);
+ }
+ } else
+ analog_regulator = NULL;
+ }
+
+ if (plat_data->gpo_regulator) {
+ gpo_regulator = regulator_get(&client->dev,
+ plat_data->gpo_regulator);
+ if (!IS_ERR(gpo_regulator)) {
+ if (regulator_enable(gpo_regulator) != 0) {
+ pr_err("%s:gpo3 enable error\n", __func__);
+ goto err4;
+ } else {
+ dev_dbg(&client->dev,
+ "%s:gpo3 enable ok\n", __func__);
+ }
+ } else
+ gpo_regulator = NULL;
+ }
+
+ ov3640_int_device.priv = &ov3640_data;
+ retval = v4l2_int_device_register(&ov3640_int_device);
+
+ return retval;
+
+err4:
+ if (analog_regulator) {
+ regulator_disable(analog_regulator);
+ regulator_put(analog_regulator);
+ }
+err3:
+ if (core_regulator) {
+ regulator_disable(core_regulator);
+ regulator_put(core_regulator);
+ }
+err2:
+ if (io_regulator) {
+ regulator_disable(io_regulator);
+ regulator_put(io_regulator);
+ }
+err1:
+ return -1;
+}
+
+/*!
+ * ov3640 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int ov3640_remove(struct i2c_client *client)
+{
+ v4l2_int_device_unregister(&ov3640_int_device);
+
+ if (gpo_regulator) {
+ regulator_disable(gpo_regulator);
+ regulator_put(gpo_regulator);
+ }
+
+ if (analog_regulator) {
+ regulator_disable(analog_regulator);
+ regulator_put(analog_regulator);
+ }
+
+ if (core_regulator) {
+ regulator_disable(core_regulator);
+ regulator_put(core_regulator);
+ }
+
+ if (io_regulator) {
+ regulator_disable(io_regulator);
+ regulator_put(io_regulator);
+ }
+
+ return 0;
+}
+
+/*!
+ * ov3640 init function
+ * Called by insmod ov3640_camera.ko.
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int ov3640_init(void)
+{
+ u8 err;
+
+ err = i2c_add_driver(&ov3640_i2c_driver);
+ if (err != 0)
+ pr_err("%s:driver registration failed, error=%d \n",
+ __func__, err);
+
+ return err;
+}
+
+/*!
+ * OV3640 cleanup function
+ * Called on rmmod ov3640_camera.ko
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit ov3640_clean(void)
+{
+ i2c_del_driver(&ov3640_i2c_driver);
+}
+
+module_init(ov3640_init);
+module_exit(ov3640_clean);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV3640 Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("CSI");
diff --git a/drivers/media/video/mxc/capture/sensor_clock.c b/drivers/media/video/mxc/capture/sensor_clock.c
new file mode 100644
index 000000000000..c15cf27c57cd
--- /dev/null
+++ b/drivers/media/video/mxc/capture/sensor_clock.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file sensor_clock.c
+ *
+ * @brief camera clock function
+ *
+ * @ingroup Camera
+ */
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+
+#if defined(CONFIG_MXC_IPU_V1) || defined(CONFIG_VIDEO_MXC_EMMA_CAMERA) \
+ || defined(CONFIG_VIDEO_MXC_CSI_CAMERA_MODULE) \
+ || defined(CONFIG_VIDEO_MXC_CSI_CAMERA)
+/*
+ * set_mclk_rate
+ *
+ * @param p_mclk_freq mclk frequence
+ *
+ */
+void set_mclk_rate(uint32_t * p_mclk_freq)
+{
+ struct clk *clk;
+ uint32_t freq = 0;
+
+ clk = clk_get(NULL, "csi_clk");
+
+ freq = clk_round_rate(clk, *p_mclk_freq);
+ clk_set_rate(clk, freq);
+
+ *p_mclk_freq = freq;
+
+ clk_put(clk);
+ pr_debug("mclk frequency = %d\n", *p_mclk_freq);
+}
+#else
+/*
+ * set_mclk_rate
+ *
+ * @param p_mclk_freq mclk frequence
+ * @param csi csi 0 or csi 1
+ *
+ */
+void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi)
+{
+ struct clk *clk;
+ uint32_t freq = 0;
+ char *mclk;
+
+ if (csi == 0) {
+ mclk = "csi_mclk1";
+ } else if (csi == 1) {
+ mclk = "csi_mclk2";
+ } else {
+ pr_debug("invalid csi num %d\n", csi);
+ return;
+ }
+
+ clk = clk_get(NULL, mclk);
+
+ freq = clk_round_rate(clk, *p_mclk_freq);
+ clk_set_rate(clk, freq);
+
+ *p_mclk_freq = freq;
+
+ clk_put(clk);
+ pr_debug("%s frequency = %d\n", mclk, *p_mclk_freq);
+}
+#endif
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(set_mclk_rate);
diff --git a/drivers/media/video/mxc/opl/Makefile b/drivers/media/video/mxc/opl/Makefile
new file mode 100644
index 000000000000..092a62c5ac4a
--- /dev/null
+++ b/drivers/media/video/mxc/opl/Makefile
@@ -0,0 +1,5 @@
+opl-objs := opl_mod.o rotate90_u16.o rotate270_u16.o \
+ rotate90_u16_qcif.o rotate270_u16_qcif.o \
+ vmirror_u16.o hmirror_rotate180_u16.o
+
+obj-$(CONFIG_VIDEO_MXC_OPL) += opl.o
diff --git a/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c b/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c
new file mode 100644
index 000000000000..3119a128c1f2
--- /dev/null
+++ b/drivers/media/video/mxc/opl/hmirror_rotate180_u16.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include "opl.h"
+
+static inline u32 rot_left_u16(u16 x, unsigned int n)
+{
+ return (x << n) | (x >> (16 - n));
+}
+
+static inline u32 rot_left_u32(u32 x, unsigned int n)
+{
+ return (x << n) | (x >> (32 - n));
+}
+
+static inline u32 byte_swap_u32(u32 x)
+{
+ u32 t1, t2, t3;
+
+ t1 = x ^ ((x << 16) | x >> 16);
+ t2 = t1 & 0xff00ffff;
+ t3 = (x >> 8) | (x << 24);
+ return t3 ^ (t2 >> 8);
+}
+
+static int opl_hmirror_u16_by1(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_hmirror_u16_by2(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_hmirror_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_hmirror_u16_by8(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+
+int opl_hmirror_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride)
+{
+ if (!src || !dst)
+ return OPLERR_NULL_PTR;
+
+ if (width == 0 || height == 0 || src_line_stride == 0
+ || dst_line_stride == 0)
+ return OPLERR_BAD_ARG;
+
+ if (width % 8 == 0)
+ return opl_hmirror_u16_by8(src, src_line_stride, width, height,
+ dst, dst_line_stride, 0);
+ else if (width % 4 == 0)
+ return opl_hmirror_u16_by4(src, src_line_stride, width, height,
+ dst, dst_line_stride, 0);
+ else if (width % 2 == 0)
+ return opl_hmirror_u16_by2(src, src_line_stride, width, height,
+ dst, dst_line_stride, 0);
+ else /* (width % 1) */
+ return opl_hmirror_u16_by1(src, src_line_stride, width, height,
+ dst, dst_line_stride, 0);
+}
+
+int opl_rotate180_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride)
+{
+ if (!src || !dst)
+ return OPLERR_NULL_PTR;
+
+ if (width == 0 || height == 0 || src_line_stride == 0
+ || dst_line_stride == 0)
+ return OPLERR_BAD_ARG;
+
+ if (width % 8 == 0)
+ return opl_hmirror_u16_by8(src, src_line_stride, width, height,
+ dst, dst_line_stride, 1);
+ else if (width % 4 == 0)
+ return opl_hmirror_u16_by4(src, src_line_stride, width, height,
+ dst, dst_line_stride, 1);
+ else if (width % 2 == 0)
+ return opl_hmirror_u16_by2(src, src_line_stride, width, height,
+ dst, dst_line_stride, 1);
+ else /* (width % 1) */
+ return opl_hmirror_u16_by1(src, src_line_stride, width, height,
+ dst, dst_line_stride, 1);
+}
+
+static int opl_hmirror_u16_by1(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const u8 *src_row_addr;
+ const u8 *psrc;
+ u8 *dst_row_addr, *pdst;
+ int i, j;
+ u16 pixel;
+
+ src_row_addr = src;
+ if (vmirror) {
+ dst_row_addr = dst + dst_line_stride * (height - 1);
+ dst_line_stride = -dst_line_stride;
+ } else
+ dst_row_addr = dst;
+
+ /* Loop over all rows */
+ for (i = 0; i < height; i++) {
+ /* Loop over each pixel */
+ psrc = src_row_addr;
+ pdst = dst_row_addr + (width - 1) * BYTES_PER_PIXEL
+ - (BYTES_PER_PIXEL - BYTES_PER_PIXEL);
+ for (j = 0; j < width; j++) {
+ pixel = *(u16 *) psrc;
+ *(u16 *) pdst = pixel;
+ psrc += BYTES_PER_PIXEL;
+ pdst -= BYTES_PER_PIXEL;
+ }
+ src_row_addr += src_line_stride;
+ dst_row_addr += dst_line_stride;
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+static int opl_hmirror_u16_by2(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const u8 *src_row_addr;
+ const u8 *psrc;
+ u8 *dst_row_addr, *pdst;
+ int i, j;
+ u32 pixelsin, pixelsout;
+
+ src_row_addr = src;
+ if (vmirror) {
+ dst_row_addr = dst + dst_line_stride * (height - 1);
+ dst_line_stride = -dst_line_stride;
+ } else
+ dst_row_addr = dst;
+
+ /* Loop over all rows */
+ for (i = 0; i < height; i++) {
+ /* Loop over each pixel */
+ psrc = src_row_addr;
+ pdst = dst_row_addr + (width - 2) * BYTES_PER_PIXEL;
+ for (j = 0; j < (width >> 1); j++) {
+ pixelsin = *(u32 *) psrc;
+ pixelsout = rot_left_u32(pixelsin, 16);
+ *(u32 *) pdst = pixelsout;
+ psrc += BYTES_PER_2PIXEL;
+ pdst -= BYTES_PER_2PIXEL;
+ }
+ src_row_addr += src_line_stride;
+ dst_row_addr += dst_line_stride;
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+static int opl_hmirror_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const u8 *src_row_addr;
+ const u8 *psrc;
+ u8 *dst_row_addr, *pdst;
+ int i, j;
+
+ union doubleword {
+ u64 dw;
+ u32 w[2];
+ };
+
+ union doubleword inbuf;
+ union doubleword outbuf;
+
+ src_row_addr = src;
+ if (vmirror) {
+ dst_row_addr = dst + dst_line_stride * (height - 1);
+ dst_line_stride = -dst_line_stride;
+ } else
+ dst_row_addr = dst;
+
+ /* Loop over all rows */
+ for (i = 0; i < height; i++) {
+ /* Loop over each pixel */
+ psrc = src_row_addr;
+ pdst = dst_row_addr + (width - 4) * BYTES_PER_PIXEL;
+ for (j = 0; j < (width >> 2); j++) {
+ inbuf.dw = *(u64 *) psrc;
+ outbuf.w[0] = rot_left_u32(inbuf.w[1], 16);
+ outbuf.w[1] = rot_left_u32(inbuf.w[0], 16);
+ *(u64 *) pdst = outbuf.dw;
+ psrc += BYTES_PER_4PIXEL;
+ pdst -= BYTES_PER_4PIXEL;
+ }
+ src_row_addr += src_line_stride;
+ dst_row_addr += dst_line_stride;
+ }
+ return OPLERR_SUCCESS;
+}
+
+static int opl_hmirror_u16_by8(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const u8 *src_row_addr;
+ const u8 *psrc;
+ u8 *dst_row_addr, *pdst;
+ int i, j;
+
+ src_row_addr = src;
+ if (vmirror) {
+ dst_row_addr = dst + dst_line_stride * (height - 1);
+ dst_line_stride = -dst_line_stride;
+ } else
+ dst_row_addr = dst;
+
+ /* Loop over all rows */
+ for (i = 0; i < height; i++) {
+ /* Loop over each pixel */
+ psrc = src_row_addr;
+ pdst = dst_row_addr + (width - 1) * BYTES_PER_PIXEL - 2;
+ for (j = (width >> 3); j > 0; j--) {
+ __asm__ volatile (
+ "ldmia %0!,{r2-r5}\n\t"
+ "mov r6, r2\n\t"
+ "mov r7, r3\n\t"
+ "mov r2, r5, ROR #16\n\t"
+ "mov r3, r4, ROR #16\n\t"
+ "mov r4, r7, ROR #16\n\t"
+ "mov r5, r6, ROR #16\n\t"
+ "stmda %1!,{r2-r5}\n\t"
+
+ :"+r"(psrc), "+r"(pdst)
+ :"0"(psrc), "1"(pdst)
+ :"r2", "r3", "r4", "r5", "r6", "r7",
+ "memory"
+ );
+ }
+ src_row_addr += src_line_stride;
+ dst_row_addr += dst_line_stride;
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_hmirror_u16);
+EXPORT_SYMBOL(opl_rotate180_u16);
diff --git a/drivers/media/video/mxc/opl/opl.h b/drivers/media/video/mxc/opl/opl.h
new file mode 100644
index 000000000000..24644c8e78fa
--- /dev/null
+++ b/drivers/media/video/mxc/opl/opl.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup OPLIP OPL Image Processing
+ */
+/*!
+ * @file opl.h
+ *
+ * @brief The OPL (Open Primitives Library) Image Processing library defines
+ * efficient functions for rotation and mirroring.
+ *
+ * It includes ARM9-optimized rotation and mirroring functions. It is derived
+ * from the original OPL project which is found at sourceforge.freescale.net.
+ *
+ * @ingroup OPLIP
+ */
+#ifndef __OPL_H__
+#define __OPL_H__
+
+#include <linux/types.h>
+
+#define BYTES_PER_PIXEL 2
+#define CACHE_LINE_WORDS 8
+#define BYTES_PER_WORD 4
+
+#define BYTES_PER_2PIXEL (BYTES_PER_PIXEL * 2)
+#define BYTES_PER_4PIXEL (BYTES_PER_PIXEL * 4)
+#define BYTES_PER_8PIXEL (BYTES_PER_PIXEL * 8)
+
+#define QCIF_Y_WIDTH 176
+#define QCIF_Y_HEIGHT 144
+
+/*! Enumerations of opl error code */
+enum opl_error {
+ OPLERR_SUCCESS = 0,
+ OPLERR_NULL_PTR,
+ OPLERR_BAD_ARG,
+ OPLERR_DIV_BY_ZERO,
+ OPLERR_OVER_FLOW,
+ OPLERR_UNDER_FLOW,
+ OPLERR_MISALIGNED,
+};
+
+/*!
+ * @brief Rotate a 16bbp buffer 90 degrees clockwise.
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate90_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 180 degrees clockwise.
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate180_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 270 degrees clockwise
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate270_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Mirror a 16bpp buffer horizontally
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_hmirror_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Mirror a 16bpp buffer vertically
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_vmirror_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 90 degrees clockwise and mirror vertically
+ * It is equivalent to rotate 270 degree and mirror horizontally
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate90_vmirror_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride);
+
+/*!
+ * @brief Rotate a 16bbp buffer 270 degrees clockwise and mirror vertically
+ * It is equivalent to rotate 90 degree and mirror horizontally
+ *
+ * @param src Pointer to the input buffer
+ * @param src_line_stride Length in bytes of a raster line of the input buffer
+ * @param width Width in pixels of the region in the input buffer
+ * @param height Height in pixels of the region in the input buffer
+ * @param dst Pointer to the output buffer
+ * @param dst_line_stride Length in bytes of a raster line of the output buffer
+ *
+ * @return Standard OPL error code. See enumeration for possible result codes.
+ */
+int opl_rotate270_vmirror_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride);
+
+#endif /* __OPL_H__ */
diff --git a/drivers/media/video/mxc/opl/opl_mod.c b/drivers/media/video/mxc/opl/opl_mod.c
new file mode 100644
index 000000000000..a581aadda252
--- /dev/null
+++ b/drivers/media/video/mxc/opl/opl_mod.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+
+static __init int opl_init(void)
+{
+ return 0;
+}
+
+static void __exit opl_exit(void)
+{
+}
+
+module_init(opl_init);
+module_exit(opl_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OPL Software Rotation/Mirroring");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/opl/rotate270_u16.c b/drivers/media/video/mxc/opl/rotate270_u16.c
new file mode 100644
index 000000000000..add87f1a9e44
--- /dev/null
+++ b/drivers/media/video/mxc/opl/rotate270_u16.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include "opl.h"
+
+static int opl_rotate270_u16_by16(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror);
+static int opl_rotate270_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_rotate270_vmirror_u16_both(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror);
+int opl_rotate270_u16_qcif(const u8 * src, u8 * dst);
+
+int opl_rotate270_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride)
+{
+ return opl_rotate270_vmirror_u16_both(src, src_line_stride, width,
+ height, dst, dst_line_stride, 0);
+}
+
+int opl_rotate270_vmirror_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride)
+{
+ return opl_rotate270_vmirror_u16_both(src, src_line_stride, width,
+ height, dst, dst_line_stride, 1);
+}
+
+static int opl_rotate270_vmirror_u16_both(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL;
+ const int BLOCK_SIZE_PIXELS_BY4 = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL / 4;
+
+ if (!src || !dst)
+ return OPLERR_NULL_PTR;
+
+ if (width == 0 || height == 0 || src_line_stride == 0
+ || dst_line_stride == 0)
+ return OPLERR_BAD_ARG;
+
+ /* The QCIF algorithm doesn't support vertical mirroring */
+ if (vmirror == 0 && width == QCIF_Y_WIDTH && height == QCIF_Y_HEIGHT
+ && src_line_stride == QCIF_Y_WIDTH * 2
+ && src_line_stride == QCIF_Y_HEIGHT * 2)
+ return opl_rotate270_u16_qcif(src, dst);
+ else if (width % BLOCK_SIZE_PIXELS == 0
+ && height % BLOCK_SIZE_PIXELS == 0)
+ return opl_rotate270_u16_by16(src, src_line_stride, width,
+ height, dst, dst_line_stride,
+ vmirror);
+ else if (width % BLOCK_SIZE_PIXELS_BY4 == 0
+ && height % BLOCK_SIZE_PIXELS_BY4 == 0)
+ return opl_rotate270_u16_by4(src, src_line_stride, width,
+ height, dst, dst_line_stride,
+ vmirror);
+ else
+ return OPLERR_BAD_ARG;
+}
+
+/*
+ * Rotate Counter Clockwise, divide RGB component into 16 row strips, read
+ * non sequentially and write sequentially. This is done in 16 line strips
+ * so that the cache is used better. Cachelines are 8 words = 32 bytes. Pixels
+ * are 2 bytes. The 16 reads will be cache misses, but the next 240 should
+ * be from cache. The writes to the output buffer will be sequential for 16
+ * writes.
+ *
+ * Example:
+ * Input data matrix: output matrix
+ *
+ * 0 | 1 | 2 | 3 | 4 | 4 | 0 | 0 | 3 |
+ * 4 | 3 | 2 | 1 | 0 | 3 | 1 | 9 | 6 |
+ * 6 | 7 | 8 | 9 | 0 | 2 | 2 | 8 | 2 |
+ * 5 | 3 | 2 | 6 | 3 | 1 | 3 | 7 | 3 |
+ * ^ 0 | 4 | 6 | 5 | < Write the input data sequentially
+ * Read first column
+ * Start at the bottom
+ * Move to next column and repeat
+ *
+ * Loop over k decreasing (blocks)
+ * in_block_ptr = src + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ * * BLOCK_SIZE_PIXELS) * (RGB_WIDTH_BYTES)
+ * out_block_ptr = dst + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ * * BLOCK_SIZE_BYTES) + (RGB_WIDTH_PIXELS - 1)
+ * * RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL
+ *
+ * Loop over i decreasing (width)
+ * Each pix:
+ * in_block_ptr += RGB_WIDTH_BYTES
+ * out_block_ptr += 4
+ *
+ * Each row of block:
+ * in_block_ptr -= RGB_WIDTH_BYTES * BLOCK_SIZE_PIXELS - 2
+ * out_block_ptr -= RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL + 2 * BLOCK_SIZE_PIXELS;
+ *
+ * It may perform vertical mirroring too depending on the vmirror flag.
+ */
+static int opl_rotate270_u16_by16(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL;
+ const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+ - BYTES_PER_PIXEL;
+ const int OUT_INDEX = vmirror ?
+ -dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS
+ : dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+ const u8 *in_block_ptr;
+ u8 *out_block_ptr;
+ int i, k;
+
+ for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+ in_block_ptr = src + (((height / BLOCK_SIZE_PIXELS) - k)
+ * BLOCK_SIZE_PIXELS) * src_line_stride;
+ out_block_ptr = dst + (((height / BLOCK_SIZE_PIXELS) - k)
+ * BLOCK_SIZE_PIXELS * BYTES_PER_PIXEL) +
+ (width - 1) * dst_line_stride;
+
+ /*
+ * For vertical mirroring the writing starts from the
+ * first line
+ */
+ if (vmirror)
+ out_block_ptr -= dst_line_stride * (width - 1);
+
+ for (i = width; i > 0; i--) {
+ __asm__ volatile (
+ "ldrh r2, [%0], %4\n\t"
+ "ldrh r3, [%0], %4\n\t"
+ "ldrh r4, [%0], %4\n\t"
+ "ldrh r5, [%0], %4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], %4\n\t"
+ "ldrh r3, [%0], %4\n\t"
+ "ldrh r4, [%0], %4\n\t"
+ "ldrh r5, [%0], %4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], %4\n\t"
+ "ldrh r3, [%0], %4\n\t"
+ "ldrh r4, [%0], %4\n\t"
+ "ldrh r5, [%0], %4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], %4\n\t"
+ "ldrh r3, [%0], %4\n\t"
+ "ldrh r4, [%0], %4\n\t"
+ "ldrh r5, [%0], %4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */
+ :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */
+ :"r2", "r3", "r4", "r5", "memory" /* modify */
+ );
+ in_block_ptr -= IN_INDEX;
+ out_block_ptr -= OUT_INDEX;
+ }
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+/*
+ * Rotate Counter Clockwise, divide RGB component into 4 row strips, read
+ * non sequentially and write sequentially. This is done in 4 line strips
+ * so that the cache is used better. Cachelines are 8 words = 32 bytes. Pixels
+ * are 2 bytes. The 4 reads will be cache misses, but the next 60 should
+ * be from cache. The writes to the output buffer will be sequential for 4
+ * writes.
+ *
+ * Example:
+ * Input data matrix: output matrix
+ *
+ * 0 | 1 | 2 | 3 | 4 | 4 | 0 | 0 | 3 |
+ * 4 | 3 | 2 | 1 | 0 | 3 | 1 | 9 | 6 |
+ * 6 | 7 | 8 | 9 | 0 | 2 | 2 | 8 | 2 |
+ * 5 | 3 | 2 | 6 | 3 | 1 | 3 | 7 | 3 |
+ * ^ 0 | 4 | 6 | 5 | < Write the input data sequentially
+ * Read first column
+ * Start at the bottom
+ * Move to next column and repeat
+ *
+ * Loop over k decreasing (blocks)
+ * in_block_ptr = src + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ * * BLOCK_SIZE_PIXELS) * (RGB_WIDTH_BYTES)
+ * out_block_ptr = dst + (((RGB_HEIGHT_PIXELS / BLOCK_SIZE_PIXELS) - k)
+ * * BLOCK_SIZE_BYTES) + (RGB_WIDTH_PIXELS - 1)
+ * * RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL
+ *
+ * Loop over i decreasing (width)
+ * Each pix:
+ * in_block_ptr += RGB_WIDTH_BYTES
+ * out_block_ptr += 4
+ *
+ * Each row of block:
+ * in_block_ptr -= RGB_WIDTH_BYTES * BLOCK_SIZE_PIXELS - 2
+ * out_block_ptr -= RGB_HEIGHT_PIXELS * BYTES_PER_PIXEL + 2 * BLOCK_SIZE_PIXELS;
+ *
+ * It may perform vertical mirroring too depending on the vmirror flag.
+ */
+static int opl_rotate270_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL / 4;
+ const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+ - BYTES_PER_PIXEL;
+ const int OUT_INDEX = vmirror ?
+ -dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS
+ : dst_line_stride + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+ const u8 *in_block_ptr;
+ u8 *out_block_ptr;
+ int i, k;
+
+ for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+ in_block_ptr = src + (((height / BLOCK_SIZE_PIXELS) - k)
+ * BLOCK_SIZE_PIXELS) * src_line_stride;
+ out_block_ptr = dst + (((height / BLOCK_SIZE_PIXELS) - k)
+ * BLOCK_SIZE_PIXELS * BYTES_PER_PIXEL)
+ + (width - 1) * dst_line_stride;
+
+ /*
+ * For vertical mirroring the writing starts from the
+ * first line
+ */
+ if (vmirror)
+ out_block_ptr -= dst_line_stride * (width - 1);
+
+ for (i = width; i > 0; i--) {
+ __asm__ volatile (
+ "ldrh r2, [%0], %4\n\t"
+ "ldrh r3, [%0], %4\n\t"
+ "ldrh r4, [%0], %4\n\t"
+ "ldrh r5, [%0], %4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */
+ :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */
+ :"r2", "r3", "r4", "r5", "memory" /* modify */
+ );
+ in_block_ptr -= IN_INDEX;
+ out_block_ptr -= OUT_INDEX;
+ }
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_rotate270_u16);
+EXPORT_SYMBOL(opl_rotate270_vmirror_u16);
diff --git a/drivers/media/video/mxc/opl/rotate270_u16_qcif.S b/drivers/media/video/mxc/opl/rotate270_u16_qcif.S
new file mode 100644
index 000000000000..4101eaac4554
--- /dev/null
+++ b/drivers/media/video/mxc/opl/rotate270_u16_qcif.S
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/linkage.h>
+
+ .text
+ .align 2
+ENTRY(opl_rotate270_u16_qcif)
+ STMFD sp!,{r4-r10}
+ MOV r12,#0x160
+ MOV r10,#0x90
+ MOV r3,r10,LSR #4
+.L1.16:
+ RSB r2,r3,r10,LSR #4
+ MOV r5,r2,LSL #5
+ MOV r4,r12,LSR #1
+ SMULBB r4,r5,r4
+ ADD r2,r1,r2,LSL #5
+ ADD r5,r2,#0xc000
+ ADD r5,r5,#0x4e0
+ MOV r2,r12,LSR #1
+ ADD r4,r0,r4
+.L1.52:
+ LDRH r6,[r4],r12
+ LDRH r7,[r4],r12
+ LDRH r8,[r4],r12
+ LDRH r9,[r4],r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ SUBS r2,r2,#1
+ LDRH r6,[r4],r12
+ LDRH r7,[r4],r12
+ LDRH r8,[r4],r12
+ LDRH r9,[r4],r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ LDRH r6,[r4],r12
+ LDRH r7,[r4],r12
+ LDRH r8,[r4],r12
+ LDRH r9,[r4],r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ LDRH r6,[r4],r12
+ LDRH r7,[r4],r12
+ LDRH r8,[r4],r12
+ LDRH r9,[r4],r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ SUB r4,r4,#0x1500
+ STMIA r5,{r6,r7}
+ SUB r5,r5,#0x138
+ SUB r4,r4,#0xfe
+ BGT .L1.52
+ SUBS r3,r3,#1
+ BGT .L1.16
+ LDMFD sp!,{r4-r10}
+ BX lr
+ .size opl_rotate270_u16_qcif, . - opl_rotate270_u16_qcif
diff --git a/drivers/media/video/mxc/opl/rotate90_u16.c b/drivers/media/video/mxc/opl/rotate90_u16.c
new file mode 100644
index 000000000000..dd7d445aa952
--- /dev/null
+++ b/drivers/media/video/mxc/opl/rotate90_u16.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include "opl.h"
+
+static int opl_rotate90_u16_by16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_rotate90_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror);
+static int opl_rotate90_vmirror_u16_both(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror);
+int opl_rotate90_u16_qcif(const u8 * src, u8 * dst);
+
+int opl_rotate90_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride)
+{
+ return opl_rotate90_vmirror_u16_both(src, src_line_stride, width,
+ height, dst, dst_line_stride, 0);
+}
+
+int opl_rotate90_vmirror_u16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride)
+{
+ return opl_rotate90_vmirror_u16_both(src, src_line_stride, width,
+ height, dst, dst_line_stride, 1);
+}
+
+static int opl_rotate90_vmirror_u16_both(const u8 * src, int src_line_stride,
+ int width, int height, u8 * dst,
+ int dst_line_stride, int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL;
+ const int BLOCK_SIZE_PIXELS_BY4 = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL / 4;
+
+ if (!src || !dst)
+ return OPLERR_NULL_PTR;
+
+ if (width == 0 || height == 0 || src_line_stride == 0
+ || dst_line_stride == 0)
+ return OPLERR_BAD_ARG;
+
+ /* The QCIF algorithm doesn't support vertical mirroring */
+ if (vmirror == 0 && width == QCIF_Y_WIDTH && height == QCIF_Y_HEIGHT
+ && src_line_stride == QCIF_Y_WIDTH * 2
+ && src_line_stride == QCIF_Y_HEIGHT * 2)
+ return opl_rotate90_u16_qcif(src, dst);
+ else if (width % BLOCK_SIZE_PIXELS == 0
+ && height % BLOCK_SIZE_PIXELS == 0)
+ return opl_rotate90_u16_by16(src, src_line_stride, width,
+ height, dst, dst_line_stride,
+ vmirror);
+ else if (width % BLOCK_SIZE_PIXELS_BY4 == 0
+ && height % BLOCK_SIZE_PIXELS_BY4 == 0)
+ return opl_rotate90_u16_by4(src, src_line_stride, width, height,
+ dst, dst_line_stride, vmirror);
+ else
+ return OPLERR_BAD_ARG;
+}
+
+/*
+ * Performs clockwise rotation (and possibly vertical mirroring depending
+ * on the vmirror flag) using block sizes of 16x16
+ * The algorithm is similar to 270 degree clockwise rotation algorithm
+ */
+static int opl_rotate90_u16_by16(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL;
+ const int BLOCK_SIZE_BYTES = BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+ const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+ + BYTES_PER_PIXEL;
+ const int OUT_INDEX = vmirror ?
+ -dst_line_stride - BLOCK_SIZE_BYTES
+ : dst_line_stride - BLOCK_SIZE_BYTES;
+ const u8 *in_block_ptr;
+ u8 *out_block_ptr;
+ int i, k;
+
+ for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+ in_block_ptr = src + src_line_stride * (height - 1)
+ - (src_line_stride * BLOCK_SIZE_PIXELS *
+ (height / BLOCK_SIZE_PIXELS - k));
+ out_block_ptr = dst + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS *
+ ((height / BLOCK_SIZE_PIXELS) - k);
+
+ /*
+ * For vertical mirroring the writing starts from the
+ * bottom line
+ */
+ if (vmirror)
+ out_block_ptr += dst_line_stride * (width - 1);
+
+ for (i = width; i > 0; i--) {
+ __asm__ volatile (
+ "ldrh r2, [%0], -%4\n\t"
+ "ldrh r3, [%0], -%4\n\t"
+ "ldrh r4, [%0], -%4\n\t"
+ "ldrh r5, [%0], -%4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], -%4\n\t"
+ "ldrh r3, [%0], -%4\n\t"
+ "ldrh r4, [%0], -%4\n\t"
+ "ldrh r5, [%0], -%4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], -%4\n\t"
+ "ldrh r3, [%0], -%4\n\t"
+ "ldrh r4, [%0], -%4\n\t"
+ "ldrh r5, [%0], -%4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ "ldrh r2, [%0], -%4\n\t"
+ "ldrh r3, [%0], -%4\n\t"
+ "ldrh r4, [%0], -%4\n\t"
+ "ldrh r5, [%0], -%4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */
+ :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */
+ :"r2", "r3", "r4", "r5", "memory" /* modify */
+ );
+ in_block_ptr += IN_INDEX;
+ out_block_ptr += OUT_INDEX;
+ }
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+/*
+ * Performs clockwise rotation (and possibly vertical mirroring depending
+ * on the vmirror flag) using block sizes of 4x4
+ * The algorithm is similar to 270 degree clockwise rotation algorithm
+ */
+static int opl_rotate90_u16_by4(const u8 * src, int src_line_stride, int width,
+ int height, u8 * dst, int dst_line_stride,
+ int vmirror)
+{
+ const int BLOCK_SIZE_PIXELS = CACHE_LINE_WORDS * BYTES_PER_WORD
+ / BYTES_PER_PIXEL / 4;
+ const int BLOCK_SIZE_BYTES = BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS;
+ const int IN_INDEX = src_line_stride * BLOCK_SIZE_PIXELS
+ + BYTES_PER_PIXEL;
+ const int OUT_INDEX = vmirror ?
+ -dst_line_stride - BLOCK_SIZE_BYTES
+ : dst_line_stride - BLOCK_SIZE_BYTES;
+ const u8 *in_block_ptr;
+ u8 *out_block_ptr;
+ int i, k;
+
+ for (k = height / BLOCK_SIZE_PIXELS; k > 0; k--) {
+ in_block_ptr = src + src_line_stride * (height - 1)
+ - (src_line_stride * BLOCK_SIZE_PIXELS *
+ (height / BLOCK_SIZE_PIXELS - k));
+ out_block_ptr = dst + BYTES_PER_PIXEL * BLOCK_SIZE_PIXELS
+ * ((height / BLOCK_SIZE_PIXELS) - k);
+
+ /*
+ * For horizontal mirroring the writing starts from the
+ * bottom line
+ */
+ if (vmirror)
+ out_block_ptr += dst_line_stride * (width - 1);
+
+ for (i = width; i > 0; i--) {
+ __asm__ volatile (
+ "ldrh r2, [%0], -%4\n\t"
+ "ldrh r3, [%0], -%4\n\t"
+ "ldrh r4, [%0], -%4\n\t"
+ "ldrh r5, [%0], -%4\n\t"
+ "orr r2, r2, r3, lsl #16\n\t"
+ "orr r4, r4, r5, lsl #16\n\t"
+ "str r2, [%1], #4\n\t"
+ "str r4, [%1], #4\n\t"
+
+ :"+r" (in_block_ptr), "+r"(out_block_ptr) /* output */
+ :"0"(in_block_ptr), "1"(out_block_ptr), "r"(src_line_stride) /* input */
+ :"r2", "r3", "r4", "r5", "memory" /* modify */
+ );
+ in_block_ptr += IN_INDEX;
+ out_block_ptr += OUT_INDEX;
+ }
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_rotate90_u16);
+EXPORT_SYMBOL(opl_rotate90_vmirror_u16);
diff --git a/drivers/media/video/mxc/opl/rotate90_u16_qcif.S b/drivers/media/video/mxc/opl/rotate90_u16_qcif.S
new file mode 100644
index 000000000000..8568a9e629e5
--- /dev/null
+++ b/drivers/media/video/mxc/opl/rotate90_u16_qcif.S
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/linkage.h>
+
+ .text
+ .align 2
+ENTRY(opl_rotate90_u16_qcif)
+ STMFD sp!,{r4-r10}
+ MOV r12,#0x160
+ MOV r10,#0x90
+ MOV r3,r10,LSR #4
+.L1.216:
+ RSB r2,r3,r10,LSR #4
+ MOV r4,#0x20
+ SMULBB r5,r4,r2
+ MOV r4,#0x1600
+ SMULBB r2,r4,r2
+ ADD r4,r0,#0xc000
+ ADD r4,r4,#0x4a0
+ SUB r4,r4,r2
+ MOV r2,r12,LSR #1
+ ADD r5,r1,r5
+.L1.256:
+ LDRH r6,[r4],-r12
+ LDRH r7,[r4],-r12
+ LDRH r8,[r4],-r12
+ LDRH r9,[r4],-r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ SUBS r2,r2,#1
+ LDRH r6,[r4],-r12
+ LDRH r7,[r4],-r12
+ LDRH r8,[r4],-r12
+ LDRH r9,[r4],-r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ LDRH r6,[r4],-r12
+ LDRH r7,[r4],-r12
+ LDRH r8,[r4],-r12
+ LDRH r9,[r4],-r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ STMIA r5!,{r6,r7}
+ LDRH r6,[r4],-r12
+ LDRH r7,[r4],-r12
+ LDRH r8,[r4],-r12
+ LDRH r9,[r4],-r12
+ ORR r6,r6,r7,LSL #16
+ ORR r7,r8,r9,LSL #16
+ ADD r4,r4,#0x1600
+ STMIA r5!,{r6,r7}
+ ADD r5,r5,#0x100
+ ADD r4,r4,#2
+ BGT .L1.256
+ SUBS r3,r3,#1
+ BGT .L1.216
+ LDMFD sp!,{r4-r10}
+ BX lr
+ .size opl_rotate90_u16_qcif, . - opl_rotate90_u16_qcif
diff --git a/drivers/media/video/mxc/opl/vmirror_u16.c b/drivers/media/video/mxc/opl/vmirror_u16.c
new file mode 100644
index 000000000000..57f805c08a81
--- /dev/null
+++ b/drivers/media/video/mxc/opl/vmirror_u16.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include "opl.h"
+
+int opl_vmirror_u16(const u8 * src, int src_line_stride, int width, int height,
+ u8 * dst, int dst_line_stride)
+{
+ const u8 *src_row_addr;
+ u8 *dst_row_addr;
+ int i;
+
+ if (!src || !dst)
+ return OPLERR_NULL_PTR;
+
+ if (width == 0 || height == 0 || src_line_stride == 0
+ || dst_line_stride == 0)
+ return OPLERR_BAD_ARG;
+
+ src_row_addr = src;
+ dst_row_addr = dst + (height - 1) * dst_line_stride;
+
+ /* Loop over all rows */
+ for (i = 0; i < height; i++) {
+ /* memcpy each row */
+ memcpy(dst_row_addr, src_row_addr, BYTES_PER_PIXEL * width);
+ src_row_addr += src_line_stride;
+ dst_row_addr -= dst_line_stride;
+ }
+
+ return OPLERR_SUCCESS;
+}
+
+EXPORT_SYMBOL(opl_vmirror_u16);
diff --git a/drivers/media/video/mxc/output/Kconfig b/drivers/media/video/mxc/output/Kconfig
new file mode 100644
index 000000000000..2153ad248907
--- /dev/null
+++ b/drivers/media/video/mxc/output/Kconfig
@@ -0,0 +1,28 @@
+config VIDEO_MXC_IPU_OUTPUT
+ bool "IPU v4l2 support"
+ depends on VIDEO_MXC_OUTPUT && MXC_IPU
+ default y
+ ---help---
+ This is the video4linux2 driver for IPU post processing video output.
+
+config VIDEO_MXC_IPUV1_WVGA_OUTPUT
+ bool "IPUv1 WVGA v4l2 display support"
+ depends on VIDEO_MXC_OUTPUT && MXC_IPU
+ default n
+ ---help---
+ This is the video4linux2 driver for IPUv1 WVGA post processing video output.
+
+config VIDEO_MXC_EMMA_OUTPUT
+ bool
+ depends on VIDEO_MXC_OUTPUT && MXC_EMMA && FB_MXC_SYNC_PANEL
+ default y
+ ---help---
+ This is the video4linux2 driver for EMMA post processing video output.
+
+config VIDEO_MXC_OUTPUT_FBSYNC
+ bool "Synchronize the output with LCDC refresh"
+ depends on VIDEO_MXC_EMMA_OUTPUT
+ default y
+ ---help---
+ Synchronize the post-processing with LCDC EOF (End of Frame) to
+ prevent tearing issue. If unsure, say Y.
diff --git a/drivers/media/video/mxc/output/Makefile b/drivers/media/video/mxc/output/Makefile
new file mode 100644
index 000000000000..1713fa3bf3ab
--- /dev/null
+++ b/drivers/media/video/mxc/output/Makefile
@@ -0,0 +1,11 @@
+ifeq ($(CONFIG_VIDEO_MXC_EMMA_OUTPUT),y)
+ mx27_output-objs := mx27_v4l2_output.o mx27_pp.o
+ obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mx27_output.o
+endif
+
+ifeq ($(CONFIG_VIDEO_MXC_IPU_OUTPUT),y)
+ obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc_v4l2_output.o
+endif
+ifeq ($(CONFIG_VIDEO_MXC_IPUV1_WVGA_OUTPUT),y)
+ obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mx31_v4l2_wvga_output.o
+endif
diff --git a/drivers/media/video/mxc/output/mx27_pp.c b/drivers/media/video/mxc/output/mx27_pp.c
new file mode 100644
index 000000000000..a82328015fe2
--- /dev/null
+++ b/drivers/media/video/mxc/output/mx27_pp.c
@@ -0,0 +1,904 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx27_pp.c
+ *
+ * @brief MX27 V4L2 Video Output Driver
+ *
+ * Video4Linux2 Output Device using MX27 eMMA Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include "mx27_pp.h"
+#include "mxc_v4l2_output.h"
+
+#define SCALE_RETRY 32 /* to be more relax, less precise */
+#define PP_SKIP 1
+#define PP_TBL_MAX 40
+
+static unsigned short scale_tbl[PP_TBL_MAX];
+static int g_hlen, g_vlen;
+
+static emma_pp_cfg g_pp_cfg;
+static int g_disp_num = 0;
+static char pp_dev[] = "emma_pp";
+
+/*!
+ * @brief PP resizing routines
+ */
+static int gcd(int x, int y);
+static int ratio(int x, int y, int *den);
+static int scale_0d(int k, int coeff, int base, int nxt);
+static int scale_1d(int inv, int outv, int k);
+static int scale_1d_smart(int *inv, int *outv, int index);
+static int scale_2d(emma_pp_scale * sz);
+
+static irqreturn_t pp_isr(int irq, void *dev_id);
+static int set_output_addr(emma_pp_cfg * cfg, vout_data * vout);
+static int pphw_reset(void);
+static int pphw_enable(int flag);
+static int pphw_ptr(emma_pp_cfg * cfg);
+static int pphw_outptr(emma_pp_cfg * cfg);
+static int pphw_cfg(emma_pp_cfg * cfg);
+static int pphw_isr(void);
+static void pphw_init(void);
+static void pphw_exit(void);
+
+#define PP_DUMP(reg) pr_debug("%s\t = 0x%08X\n", #reg, __raw_readl(reg))
+void pp_dump(void)
+{
+ PP_DUMP(PP_CNTL);
+ PP_DUMP(PP_INTRCNTL);
+ PP_DUMP(PP_INTRSTATUS);
+ PP_DUMP(PP_SOURCE_Y_PTR);
+ PP_DUMP(PP_SOURCE_CB_PTR);
+ PP_DUMP(PP_SOURCE_CR_PTR);
+ PP_DUMP(PP_DEST_RGB_PTR);
+ PP_DUMP(PP_QUANTIZER_PTR);
+ PP_DUMP(PP_PROCESS_FRAME_PARA);
+ PP_DUMP(PP_SOURCE_FRAME_WIDTH);
+ PP_DUMP(PP_DEST_DISPLAY_WIDTH);
+ PP_DUMP(PP_DEST_IMAGE_SIZE);
+ PP_DUMP(PP_DEST_FRAME_FMT_CNTL);
+ PP_DUMP(PP_RESIZE_INDEX);
+ PP_DUMP(PP_CSC_COEF_0123);
+ PP_DUMP(PP_CSC_COEF_4);
+}
+
+/*!
+ * @brief Set PP input address.
+ * @param ptr The pointer to the Y value of input
+ * @return Zero on success, others on failure
+ */
+int pp_ptr(unsigned long ptr)
+{
+ g_pp_cfg.ptr.y = ptr;
+ g_pp_cfg.ptr.u = g_pp_cfg.ptr.v = g_pp_cfg.ptr.qp = 0;
+
+ return pphw_ptr(&g_pp_cfg);
+}
+
+/*!
+ * @brief Enable or disable PP.
+ * @param flag Zero to disable PP, others to enable PP
+ * @return Zero on success, others on failure
+ */
+int pp_enable(int flag)
+{
+ return pphw_enable(flag);
+}
+
+/*!
+ * @brief Get the display No. of last completed PP frame.
+ * @return The display No. of last completed PP frame.
+ */
+int pp_num_last(void)
+{
+ return (g_disp_num ? 0 : 1);
+}
+
+/*!
+ * @brief Initialize PP.
+ * @param vout Pointer to _vout_data structure
+ * @return Zero on success, others on failure
+ */
+int pp_init(vout_data * vout)
+{
+ pphw_init();
+ pphw_enable(0);
+ enable_irq(MXC_INT_EMMAPP);
+ return request_irq(MXC_INT_EMMAPP, pp_isr, 0, pp_dev, vout);
+}
+
+/*!
+ * @brief Deinitialize PP.
+ * @param vout Pointer to _vout_data structure
+ */
+void pp_exit(vout_data * vout)
+{
+ disable_irq(MXC_INT_EMMAPP);
+ free_irq(MXC_INT_EMMAPP, vout);
+ pphw_enable(0);
+ pphw_exit();
+}
+
+/*!
+ * @brief Configure PP.
+ * @param vout Pointer to _vout_data structure
+ * @return Zero on success, others on failure
+ */
+int pp_cfg(vout_data * vout)
+{
+ if (!vout)
+ return -1;
+
+ /* PP accepts YUV420 input only */
+ if (vout->v2f.fmt.pix.pixelformat != V4L2_PIX_FMT_YUV420) {
+ pr_debug("unsupported pixel format.\n");
+ return -1;
+ }
+
+ g_pp_cfg.operation = 0;
+
+ memset(g_pp_cfg.csc_table, 0, sizeof(g_pp_cfg.csc_table));
+
+ /* Convert output pixel format to PP required format */
+ switch (vout->v4l2_fb.fmt.pixelformat) {
+ case V4L2_PIX_FMT_BGR32:
+ g_pp_cfg.red_width = 8;
+ g_pp_cfg.green_width = 8;
+ g_pp_cfg.blue_width = 8;
+ g_pp_cfg.red_offset = 8;
+ g_pp_cfg.green_offset = 16;
+ g_pp_cfg.blue_offset = 24;
+ g_pp_cfg.rgb_resolution = 32;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ g_pp_cfg.red_width = 8;
+ g_pp_cfg.green_width = 8;
+ g_pp_cfg.blue_width = 8;
+ g_pp_cfg.red_offset = 24;
+ g_pp_cfg.green_offset = 16;
+ g_pp_cfg.blue_offset = 8;
+ g_pp_cfg.rgb_resolution = 32;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ g_pp_cfg.red_width = 0;
+ g_pp_cfg.green_width = 0;
+ g_pp_cfg.blue_width = 0;
+ g_pp_cfg.red_offset = 0;
+ g_pp_cfg.green_offset = 0;
+ g_pp_cfg.blue_offset = PP_PIX_YUYV;
+ g_pp_cfg.rgb_resolution = 16;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ g_pp_cfg.red_width = 0;
+ g_pp_cfg.green_width = 0;
+ g_pp_cfg.blue_width = 0;
+ g_pp_cfg.red_offset = 0;
+ g_pp_cfg.green_offset = 0;
+ g_pp_cfg.blue_offset = PP_PIX_UYVY;
+ g_pp_cfg.rgb_resolution = 16;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ default:
+ g_pp_cfg.red_width = 5;
+ g_pp_cfg.green_width = 6;
+ g_pp_cfg.blue_width = 5;
+ g_pp_cfg.red_offset = 11;
+ g_pp_cfg.green_offset = 5;
+ g_pp_cfg.blue_offset = 0;
+ g_pp_cfg.rgb_resolution = 16;
+ break;
+ }
+
+ if (vout->ipu_buf[0] != -1)
+ g_pp_cfg.ptr.y =
+ (unsigned int)vout->queue_buf_paddr[vout->ipu_buf[0]];
+ else
+ g_pp_cfg.ptr.y = 0;
+
+ g_pp_cfg.ptr.u = g_pp_cfg.ptr.v = g_pp_cfg.ptr.qp = 0;
+
+ g_pp_cfg.dim.in.width = vout->v2f.fmt.pix.width;
+ g_pp_cfg.dim.in.height = vout->v2f.fmt.pix.height;
+ g_pp_cfg.dim.out.width = vout->crop_current.width;
+ g_pp_cfg.dim.out.height = vout->crop_current.height;
+ g_pp_cfg.dim.num.width = 0;
+ g_pp_cfg.dim.num.height = 0;
+ g_pp_cfg.dim.den.width = 0;
+ g_pp_cfg.dim.den.height = 0;
+
+ if (scale_2d(&g_pp_cfg.dim)) {
+ pr_debug("unsupported resize ratio.\n");
+ return -1;
+ }
+
+ g_pp_cfg.dim.out.width = vout->crop_current.width;
+ g_pp_cfg.dim.out.height = vout->crop_current.height;
+
+ g_pp_cfg.in_y_stride = 0;
+ if (set_output_addr(&g_pp_cfg, vout)) {
+ pr_debug("failed to set pp output address.\n");
+ return -1;
+ }
+
+ return pphw_cfg(&g_pp_cfg);
+}
+
+irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id);
+
+/*!
+ * @brief PP IRQ handler.
+ */
+static irqreturn_t pp_isr(int irq, void *dev_id)
+{
+ int status;
+ vout_data *vout = dev_id;
+
+ status = pphw_isr();
+ if ((status & 0x1) == 0) { /* Not frame complete interrupt */
+ pr_debug("not pp frame complete interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ g_disp_num = g_disp_num ? 0 : 1;
+ g_pp_cfg.outptr = (unsigned int)vout->display_bufs[g_disp_num];
+ pphw_outptr(&g_pp_cfg);
+ }
+
+ return mxc_v4l2out_pp_in_irq_handler(irq, dev_id);
+}
+
+/*!
+ * @brief Set PP output address.
+ * @param cfg Pointer to emma_pp_cfg structure
+ * @param vout Pointer to _vout_data structure
+ * @return Zero on success, others on failure
+ */
+static int set_output_addr(emma_pp_cfg * cfg, vout_data * vout)
+{
+ if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ g_disp_num = 0;
+ cfg->outptr = (unsigned int)vout->display_bufs[g_disp_num];
+ cfg->out_stride = vout->crop_current.width;
+ return 0;
+ } else {
+ struct fb_info *fb;
+
+ fb = registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ if (!fb)
+ return -1;
+
+ cfg->outptr = fb->fix.smem_start;
+ cfg->outptr += vout->crop_current.top * fb->var.xres_virtual
+ * (fb->var.bits_per_pixel >> 3)
+ + vout->crop_current.left * (fb->var.bits_per_pixel >> 3);
+ cfg->out_stride = fb->var.xres_virtual;
+
+ return 0;
+ }
+}
+
+/*!
+ * @brief Get maximum common divisor.
+ * @param x First input value
+ * @param y Second input value
+ * @return Maximum common divisor of x and y
+ */
+static int gcd(int x, int y)
+{
+ int k;
+
+ if (x < y) {
+ k = x;
+ x = y;
+ y = k;
+ }
+
+ while ((k = x % y)) {
+ x = y;
+ y = k;
+ }
+
+ return y;
+}
+
+/*!
+ * @brief Get ratio.
+ * @param x First input value
+ * @param y Second input value
+ * @param den Denominator of the ratio (corresponding to y)
+ * @return Numerator of the ratio (corresponding to x)
+ */
+static int ratio(int x, int y, int *den)
+{
+ int g;
+
+ if (!x || !y)
+ return 0;
+
+ g = gcd(x, y);
+ *den = y / g;
+
+ return x / g;
+}
+
+/*!
+ * @brief Build PP coefficient entry
+ * Build one or more coefficient entries for PP coefficient table based
+ * on given coefficient.
+ *
+ * @param k The index of the coefficient in coefficient table
+ * @param coeff The weighting coefficient
+ * @param base The base of the coefficient
+ * @param nxt Number of pixels to be read
+ *
+ * @return The index of the next coefficient entry on success
+ * -1 on failure
+ */
+static int scale_0d(int k, int coeff, int base, int nxt)
+{
+ if (k >= PP_TBL_MAX) {
+ /* no more space in table */
+ pr_debug("no space in scale table, k = %d\n", k);
+ return -1;
+ }
+
+ coeff = ((coeff << BC_COEF) + (base >> 1)) / base;
+
+ /*
+ * Valid values for weighting coefficient are 0, 2 to 30, and 31.
+ * A value of 31 is treated as 32 and therefore 31 is an
+ * invalid co-efficient.
+ */
+ if (coeff >= SZ_COEF - 1)
+ coeff--;
+ else if (coeff == 1)
+ coeff++;
+ coeff = coeff << BC_NXT;
+
+ if (nxt < SZ_NXT) {
+ coeff |= nxt;
+ coeff <<= 1;
+ coeff |= 1;
+ } else {
+ /*
+ * src inc field is 2 bit wide, for 4+, use special
+ * code 0:0:1 to prevent dest inc
+ */
+ coeff |= PP_SKIP;
+ coeff <<= 1;
+ coeff |= 1;
+ nxt -= PP_SKIP;
+ do {
+ pr_debug("tbl = %03X\n", coeff);
+ scale_tbl[k++] = coeff;
+ coeff = (nxt > PP_SKIP) ? PP_SKIP : nxt;
+ coeff <<= 1;
+ } while ((nxt -= PP_SKIP) > 0);
+ }
+ pr_debug("tbl = %03X\n", coeff);
+ scale_tbl[k++] = coeff;
+
+ return k;
+}
+
+/*
+ * @brief Build PP coefficient table
+ * Build PP coefficient table for one dimension (width or height)
+ * based on given input and output resolution
+ *
+ * @param inv input resolution
+ * @param outv output resolution
+ * @param k index of free table entry
+ *
+ * @return The index of the next free coefficient entry on success
+ * -1 on failure
+ */
+static int scale_1d(int inv, int outv, int k)
+{
+ int v; /* overflow counter */
+ int coeff, nxt; /* table output */
+
+ if (inv == outv)
+ return scale_0d(k, 1, 1, 1); /* force scaling */
+
+ if (inv * 4 < outv) {
+ pr_debug("upscale err: ratio should be in range 1:1 to 1:4\n");
+ return -1;
+ }
+
+ v = 0;
+ if (inv < outv) {
+ /* upscale: mix <= 2 input pixels per output pixel */
+ do {
+ coeff = outv - v;
+ v += inv;
+ if (v >= outv) {
+ v -= outv;
+ nxt = 1;
+ } else
+ nxt = 0;
+ pr_debug("upscale: coeff = %d/%d nxt = %d\n", coeff,
+ outv, nxt);
+ k = scale_0d(k, coeff, outv, nxt);
+ if (k < 0)
+ return -1;
+ } while (v);
+ } else if (inv >= 2 * outv) {
+ /* PP doesn't support resize ratio > 2:1 except 4:1. */
+ if ((inv != 2 * outv) && (inv != 4 * outv))
+ return -1;
+ /* downscale: >=2:1 bilinear approximation */
+ coeff = inv - 2 * outv;
+ v = 0;
+ nxt = 0;
+ do {
+ v += coeff;
+ nxt = 2;
+ while (v >= outv) {
+ v -= outv;
+ nxt++;
+ }
+ pr_debug("downscale: coeff = 1/2 nxt = %d\n", nxt);
+ k = scale_0d(k, 1, 2, nxt);
+ if (k < 0)
+ return -1;
+ } while (v);
+ } else {
+ /* downscale: bilinear */
+ int in_pos_inc = 2 * outv;
+ int out_pos = inv;
+ int out_pos_inc = 2 * inv;
+ int init_carry = inv - outv;
+ int carry = init_carry;
+
+ v = outv + in_pos_inc;
+ do {
+ coeff = v - out_pos;
+ out_pos += out_pos_inc;
+ carry += out_pos_inc;
+ for (nxt = 0; v < out_pos; nxt++) {
+ v += in_pos_inc;
+ carry -= in_pos_inc;
+ }
+ pr_debug("downscale: coeff = %d/%d nxt = %d\n", coeff,
+ in_pos_inc, nxt);
+ k = scale_0d(k, coeff, in_pos_inc, nxt);
+ if (k < 0)
+ return -1;
+ } while (carry != init_carry);
+ }
+ return k;
+}
+
+/*
+ * @brief Build PP coefficient table
+ * Build PP coefficient table for one dimension (width or height)
+ * based on given input and output resolution. The given input
+ * and output resolution might be not supported due to hardware
+ * limits. In this case this functin rounds the input and output
+ * to closest possible values and return them to caller.
+ *
+ * @param inv input resolution, might be modified after the call
+ * @param outv output resolution, might be modified after the call
+ * @param k index of free table entry
+ *
+ * @return The index of the next free coefficient entry on success
+ * -1 on failure
+ */
+static int scale_1d_smart(int *inv, int *outv, int index)
+{
+ int len, num, den, retry;
+ static int num1, den1;
+
+ if (!inv || !outv)
+ return -1;
+
+ /* Both should be non-zero */
+ if (!(*inv) || !(*outv))
+ return -1;
+
+ retry = SCALE_RETRY;
+
+ do {
+ num = ratio(*inv, *outv, &den);
+ pr_debug("num = %d, den = %d\n", num, den);
+ if (!num)
+ continue;
+
+ if (index != 0) {
+ /*
+ * We are now resizing height. Check to see if the
+ * resize ratio for width can be reused by height
+ */
+ if ((num == num1) && (den == den1))
+ return index;
+ }
+
+ if ((len = scale_1d(num, den, index)) < 0)
+ /* increase output dimension to try another ratio */
+ (*outv)++;
+ else {
+ if (index == 0) {
+ /*
+ * We are now resizing width. The same resize
+ * ratio may be reused by height, so save the
+ * ratio.
+ */
+ num1 = num;
+ den1 = den;
+ }
+ return len;
+ }
+ } while (retry--);
+
+ pr_debug("pp scale err\n");
+ return -1;
+}
+
+/*
+ * @brief Build PP coefficient table for both width and height
+ * Build PP coefficient table for both width and height based on
+ * given resizing ratios.
+ *
+ * @param sz Structure contains resizing ratio informations
+ *
+ * @return 0 on success, others on failure
+ */
+static int scale_2d(emma_pp_scale * sz)
+{
+ int inv, outv;
+
+ /* horizontal resizing. parameter check - must provide in size */
+ if (!sz->in.width)
+ return -1;
+
+ /* Resizing based on num:den */
+ inv = sz->num.width;
+ outv = sz->den.width;
+
+ if ((g_hlen = scale_1d_smart(&inv, &outv, 0)) > 0) {
+ /* Resizing succeeded */
+ sz->den.width = outv;
+ sz->out.width = (sz->in.width * outv) / inv;
+ } else {
+ /* Resizing based on in:out */
+ inv = sz->in.width;
+ outv = sz->out.width;
+
+ if ((g_hlen = scale_1d_smart(&inv, &outv, 0)) > 0) {
+ /* Resizing succeeded */
+ sz->out.width = outv;
+ sz->num.width = ratio(sz->in.width, sz->out.width,
+ &sz->den.width);
+ } else
+ return -1;
+ }
+
+ sz->out.width &= ~1;
+
+ /* vertical resizing. parameter check - must provide in size */
+ if (!sz->in.height)
+ return -1;
+
+ /* Resizing based on num:den */
+ inv = sz->num.height;
+ outv = sz->den.height;
+
+ if ((g_vlen = scale_1d_smart(&inv, &outv, g_hlen)) > 0) {
+ /* Resizing succeeded */
+ sz->den.height = outv;
+ sz->out.height = (sz->in.height * outv) / inv;
+ } else {
+ /* Resizing based on in:out */
+ inv = sz->in.height;
+ outv = sz->out.height;
+
+ if ((g_vlen = scale_1d_smart(&inv, &outv, g_hlen)) > 0) {
+ /* Resizing succeeded */
+ sz->out.height = outv;
+ sz->num.height = ratio(sz->in.height, sz->out.height,
+ &sz->den.height);
+ } else
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Set PP resizing registers.
+ * @param sz Pointer to pp scaling structure
+ * @return Zero on success, others on failure
+ */
+static int pphw_scale(emma_pp_scale * sz)
+{
+ __raw_writel((sz->out.width << 16) | sz->out.height,
+ PP_DEST_IMAGE_SIZE);
+ __raw_writel(((g_hlen - 1) << 16) | (g_vlen ==
+ g_hlen ? 0 : (g_hlen << 8)) |
+ (g_vlen - 1), PP_RESIZE_INDEX);
+ for (g_hlen = 0; g_hlen < g_vlen; g_hlen++)
+ __raw_writel(scale_tbl[g_hlen],
+ PP_RESIZE_COEF_TBL + g_hlen * 4);
+
+ return 0;
+}
+
+/*!
+ * @brief Reset PP.
+ * @return Zero on success, others on failure
+ */
+static int pphw_reset(void)
+{
+ int i;
+
+ __raw_writel(0x100, PP_CNTL);
+
+ /* timeout */
+ for (i = 0; i < 1000; i++) {
+ if (!(__raw_readl(PP_CNTL) & 0x100)) {
+ pr_debug("pp reset over\n");
+ break;
+ }
+ }
+
+ /* check reset value */
+ if (__raw_readl(PP_CNTL) != 0x876) {
+ pr_debug("pp reset value err = 0x%08X\n", __raw_readl(PP_CNTL));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Enable or disable PP.
+ * @param flag Zero to disable PP, others to enable PP
+ * @return Zero on success, others on failure
+ */
+static int pphw_enable(int flag)
+{
+ int ret = 0;
+
+ if (flag)
+ __raw_writel(__raw_readl(PP_CNTL) | 1, PP_CNTL);
+ else
+ ret = pphw_reset();
+
+ return ret;
+}
+
+/*!
+ * @brief Set PP input address.
+ * @param cfg The pointer to PP configuration parameter
+ * @return Zero on success, others on failure
+ */
+static int pphw_ptr(emma_pp_cfg * cfg)
+{
+ if (!cfg->ptr.u) {
+ int size;
+
+ /* yuv - packed */
+ size = PP_CALC_Y_SIZE(cfg);
+ cfg->ptr.u = cfg->ptr.y + size;
+ cfg->ptr.v = cfg->ptr.u + (size >> 2);
+
+ /* yuv packed with qp appended */
+ if (!cfg->ptr.qp)
+ cfg->ptr.qp = cfg->ptr.v + (size >> 2);
+ }
+ __raw_writel(cfg->ptr.y, PP_SOURCE_Y_PTR);
+ __raw_writel(cfg->ptr.u, PP_SOURCE_CB_PTR);
+ __raw_writel(cfg->ptr.v, PP_SOURCE_CR_PTR);
+ __raw_writel(cfg->ptr.qp, PP_QUANTIZER_PTR);
+
+ return 0;
+}
+
+/*!
+ * @brief Set PP output address.
+ * @param cfg The pointer to PP configuration parameter
+ * @return Zero on success, others on failure
+ */
+static int pphw_outptr(emma_pp_cfg * cfg)
+{
+ __raw_writel(cfg->outptr, PP_DEST_RGB_PTR);
+ return 0;
+}
+
+/*!
+ * @brief Configuration PP.
+ * @param cfg The pointer to PP configuration parameter
+ * @return Zero on success, others on failure
+ */
+static int pphw_cfg(emma_pp_cfg * cfg)
+{
+ int rt;
+ register int r;
+
+ pphw_scale(&cfg->dim);
+
+ if (!cfg->in_y_stride)
+ cfg->in_y_stride = cfg->dim.in.width;
+
+ if (!cfg->out_stride)
+ cfg->out_stride = cfg->dim.out.width;
+
+ r = __raw_readl(PP_CNTL) & ~EN_MASK;
+
+ /* config parms */
+ r |= cfg->operation & EN_MASK;
+ if (cfg->operation & EN_MACROBLOCK) {
+ /* Macroblock Mode */
+ r |= 0x0200;
+ __raw_writel(0x06, PP_INTRCNTL);
+ } else {
+ /* Frame mode */
+ __raw_writel(0x05, PP_INTRCNTL);
+ }
+
+ if (cfg->red_width | cfg->green_width | cfg->blue_width) {
+ /* color conversion to be performed */
+ r |= EN_CSC;
+ if (!(cfg->red_offset | cfg->green_offset)) {
+ /* auto offset B:G:R LSb to Msb */
+ cfg->green_offset = cfg->blue_offset + cfg->blue_width;
+ cfg->red_offset = cfg->green_offset + cfg->green_width;
+ }
+ if (!cfg->rgb_resolution) {
+ /* derive minimum resolution required */
+ int w, w2;
+
+ w = cfg->red_offset + cfg->red_width;
+ w2 = cfg->blue_offset + cfg->blue_width;
+ if (w < w2)
+ w = w2;
+ w2 = cfg->green_offset + cfg->green_width;
+ if (w < w2)
+ w = w2;
+ if (w > 16)
+ w = 24;
+ else if (w > 8)
+ w = 16;
+ else
+ w = 8;
+ cfg->rgb_resolution = w;
+ }
+ /* 00,11 - 32 bpp, 10 - 16 bpp, 01 - 8 bpp */
+ r &= ~0xC00;
+ if (cfg->rgb_resolution < 32)
+ r |= (cfg->rgb_resolution << 7);
+ __raw_writel((cfg->red_offset << 26) |
+ (cfg->green_offset << 21) |
+ (cfg->blue_offset << 16) |
+ (cfg->red_width << 8) |
+ (cfg->green_width << 4) |
+ cfg->blue_width, PP_DEST_FRAME_FMT_CNTL);
+ } else {
+ /* add YUV422 formatting */
+ static const unsigned int _422[] = {
+ 0x62000888,
+ 0x60100888,
+ 0x43080888,
+ 0x41180888
+ };
+
+ __raw_writel(_422[(cfg->blue_offset >> 3) & 3],
+ PP_DEST_FRAME_FMT_CNTL);
+ cfg->rgb_resolution = 16;
+ r &= ~0xC00;
+ r |= (cfg->rgb_resolution << 7);
+ }
+
+ /* add csc formatting */
+ if (!cfg->csc_table[1]) {
+ static const unsigned short _csc[][6] = {
+ {0x80, 0xb4, 0x2c, 0x5b, 0x0e4, 0},
+ {0x95, 0xcc, 0x32, 0x68, 0x104, 1},
+ {0x80, 0xca, 0x18, 0x3c, 0x0ec, 0},
+ {0x95, 0xe5, 0x1b, 0x44, 0x10e, 1},
+ };
+ memcpy(cfg->csc_table, _csc[cfg->csc_table[0]],
+ sizeof(_csc[0]));
+ }
+ __raw_writel((cfg->csc_table[0] << 24) |
+ (cfg->csc_table[1] << 16) |
+ (cfg->csc_table[2] << 8) |
+ cfg->csc_table[3], PP_CSC_COEF_0123);
+ __raw_writel((cfg->csc_table[5] ? (1 << 9) : 0) | cfg->csc_table[4],
+ PP_CSC_COEF_4);
+
+ __raw_writel(r, PP_CNTL);
+
+ pphw_ptr(cfg);
+ pphw_outptr(cfg);
+
+ /*
+ * #MB in a row = input_width / 16pix
+ * 1 byte per QP per MB
+ * QP must be formatted to be 4-byte aligned
+ * YUV lines are to be 4-byte aligned as well
+ * So Y is 8 byte aligned, as U = V = Y/2 for 420
+ * MPEG MBs are 16x16 anyway
+ */
+ __raw_writel((cfg->dim.in.width << 16) | cfg->dim.in.height,
+ PP_PROCESS_FRAME_PARA);
+ __raw_writel(cfg->in_y_stride | (PP_CALC_QP_WIDTH(cfg) << 16),
+ PP_SOURCE_FRAME_WIDTH);
+
+ /* in bytes */
+ rt = cfg->rgb_resolution >> 3;
+ if (rt == 3)
+ rt = 4;
+ __raw_writel(cfg->out_stride * rt, PP_DEST_DISPLAY_WIDTH);
+
+ pp_dump();
+ return 0;
+}
+
+/*!
+ * @brief Check PP interrupt status.
+ * @return PP interrupt status
+ */
+static int pphw_isr(void)
+{
+ unsigned long status;
+
+ pr_debug("pp: in isr.\n");
+ status = __raw_readl(PP_INTRSTATUS) & 7;
+ if (!status) {
+ pr_debug("pp: not my isr err.\n");
+ return status;
+ }
+
+ if (status & 4)
+ pr_debug("pp: isr state error.\n");
+
+ /* clear interrupt status */
+ __raw_writel(status, PP_INTRSTATUS);
+
+ return status;
+}
+
+static struct clk *emma_clk;
+
+/*!
+ * @brief PP module clock enable
+ */
+static void pphw_init(void)
+{
+ emma_clk = clk_get(NULL, "emma_clk");
+ clk_enable(emma_clk);
+}
+
+/*!
+ * @brief PP module clock disable
+ */
+static void pphw_exit(void)
+{
+ clk_disable(emma_clk);
+ clk_put(emma_clk);
+}
diff --git a/drivers/media/video/mxc/output/mx27_pp.h b/drivers/media/video/mxc/output/mx27_pp.h
new file mode 100644
index 000000000000..7bc65ddda3a1
--- /dev/null
+++ b/drivers/media/video/mxc/output/mx27_pp.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx27_pp.h
+ *
+ * @brief Header file for MX27 V4L2 Video Output Driver
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#ifndef __MX27_PP_H__
+#define __MX27_PP_H__
+
+#include "mxc_v4l2_output.h"
+
+/* PP register definitions */
+#define PP_REG(ofs) (IO_ADDRESS(EMMA_BASE_ADDR) - 0x400 + ofs)
+
+/* Register offsets */
+#define PP_CNTL PP_REG(0x00)
+#define PP_INTRCNTL PP_REG(0x04)
+#define PP_INTRSTATUS PP_REG(0x08)
+#define PP_SOURCE_Y_PTR PP_REG(0x0C)
+#define PP_SOURCE_CB_PTR PP_REG(0x10)
+#define PP_SOURCE_CR_PTR PP_REG(0x14)
+#define PP_DEST_RGB_PTR PP_REG(0x18)
+#define PP_QUANTIZER_PTR PP_REG(0x1C)
+#define PP_PROCESS_FRAME_PARA PP_REG(0x20)
+#define PP_SOURCE_FRAME_WIDTH PP_REG(0x24)
+#define PP_DEST_DISPLAY_WIDTH PP_REG(0x28)
+#define PP_DEST_IMAGE_SIZE PP_REG(0x2C)
+#define PP_DEST_FRAME_FMT_CNTL PP_REG(0x30)
+#define PP_RESIZE_INDEX PP_REG(0x34)
+#define PP_CSC_COEF_0123 PP_REG(0x38)
+#define PP_CSC_COEF_4 PP_REG(0x3C)
+#define PP_RESIZE_COEF_TBL PP_REG(0x100)
+
+/* resize table dimensions
+ dest pixel index left/32 right/32 #src pixels to read
+ 0 [BC_COEF] [BC_COEF] [BC_NXT]
+ :
+ pp_tbl_max-1
+*/
+#define BC_NXT 2
+#define BC_COEF 5
+#define SZ_COEF (1 << BC_COEF)
+#define SZ_NXT (1 << BC_NXT)
+
+/* PP operations */
+#define EN_DEBLOCK 0x02
+#define EN_DERING 0x04
+#define EN_CSC 0x10
+#define EN_MACROBLOCK 0x20
+#define EN_DEF 0x16
+#define EN_MASK 0x36
+#define EN_BIGDATA 0x1000
+#define EN_BIGQP 0x2000
+
+/* PP CSC tables */
+#define CSC_TBL_NONE 0x80
+#define CSC_TBL_REUSE 0x81
+#define CSC_TBL_A1 0x00
+#define CSC_TBL_A0 0x20
+#define CSC_TBL_B1 0x40
+#define CSC_TBL_B0 0x60
+/* converts from 4 decimal fixed point to hw setting & vice versa */
+#define PP_CSC_FP4_2_HW(coeff) ((((coeff) << 7) + 5000) / 10000)
+#define PP_CSC_HW_2_FP4(coeff) ((((coeff) * 10000) + 64) >> 7)
+
+#define PP_PIX_YUYV 0
+#define PP_PIX_YVYU 8
+#define PP_PIX_UYVY 16
+#define PP_PIX_VYUY 24
+
+/* PP size & width calculation macros */
+#define PP_CALC_QP_WIDTH(cfg) \
+ (!((cfg)->operation & (EN_DEBLOCK | EN_DERING)) ? 0 : \
+ (((((cfg)->dim.in.width + 15) >> 4) + 3) & ~3))
+#define PP_CALC_Y_SIZE(cfg) \
+ ((cfg)->in_y_stride * (cfg)->dim.in.height)
+#define PP_CALC_CH_SIZE(cfg) (PP_CALC_Y_SIZE(cfg) >> 2)
+#define PP_CALC_BPP(cfg) \
+ ((cfg)->rgb_resolution > 16 ? 4 : ((cfg)->rgb_resolution >> 3))
+#define PP_CALC_YUV_SIZE(cfg) \
+ ((PP_CALC_Y_SIZE(cfg) * 3) >> 1)
+#define PP_CALC_QP_SIZE(cfg) \
+ (PP_CALC_QP_WIDTH(cfg) * (((cfg)->dim.in.height + 15) >> 4))
+#define PP_CALC_DEST_WIDTH(cfg) \
+ (((cfg)->out_stride & ~1) * PP_CALC_BPP(cfg))
+#define PP_CALC_DEST_SIZE(cfg) \
+ ((cfg)->dim.out.height * PP_CALC_DEST_WIDTH(cfg))
+
+/*
+ * physical addresses for bus mastering
+ * v=0 -> yuv packed
+ * v=0 & qp=0 -> yuv packed with qp appended
+ */
+typedef struct _emma_pp_ptr {
+ unsigned int y; /* Y data (line align8) */
+ unsigned int u; /* U data (line align4) */
+ unsigned int v; /* V data (line align4) */
+ unsigned int qp; /* Quantization (line align4) */
+} emma_pp_ptr;
+
+typedef struct _emma_pp_size {
+ int width;
+ int height;
+} emma_pp_size;
+
+/*
+ * if num.width != 0
+ * resize ratio = num.width : den.width
+ * else
+ * resize ratio = in.width : out.width
+ * same for height
+ */
+typedef struct _emma_pp_scale {
+ emma_pp_size num;
+ emma_pp_size den;
+ emma_pp_size in; /* clip */
+ emma_pp_size out; /* 0 -> same as in */
+} emma_pp_scale;
+
+typedef struct _emma_pp_cfg {
+ unsigned char operation; /* OR of EN_xx defines */
+
+ /*
+ * input color coeff
+ * fixed pt 8 bits, steps of 1/128
+ * csc[5] is 1 or 0 to indicate Y + 16
+ * csc[0] is matrix id 0-3 while csc[1-5]=0
+ */
+ unsigned short csc_table[6];
+
+ /*
+ * Output color (shade width, shade offset, pixel resolution)
+ * Eg. 16bpp RGB565 resolution, the values could be:
+ * red_width = 5, green_width = 6, blue_width = 6
+ * red_offset = 11, green_offset = 5, blue_offset = 0 (defaults)
+ * rgb_resolution = 16 (default)
+ * For YUV422: xxx_width=0, blue_offset=PP_PIX_xxx
+ */
+ unsigned short red_width;
+ unsigned short green_width;
+ unsigned short blue_width;
+ /* if offsets are 0, the offsets are by width LSb to MSb B:G:R */
+ unsigned short red_offset;
+ unsigned short blue_offset;
+ unsigned short green_offset;
+ /* if resolution is 0, the minimum for the sum of widths is chosen */
+ short rgb_resolution; /* 8,16,24 bpp only */
+
+ emma_pp_ptr ptr; /* dma buffer pointers */
+ unsigned int outptr; /* RGB/YUV output */
+ emma_pp_scale dim; /* in/out dimensions */
+
+ /* pixels between two adjacent input Y rows */
+ unsigned short in_y_stride; /* 0 = in_width */
+ /* PIXELS between two adjacent output rows */
+ unsigned short out_stride; /* 0 = out_width */
+} emma_pp_cfg;
+
+int pp_ptr(unsigned long ptr);
+int pp_enable(int flag);
+int pp_cfg(vout_data * vout);
+int pp_init(vout_data * vout);
+int pp_num_last(void);
+void pp_exit(vout_data * vout);
+
+#endif /* __MX27_PP_H__ */
diff --git a/drivers/media/video/mxc/output/mx27_v4l2_output.c b/drivers/media/video/mxc/output/mx27_v4l2_output.c
new file mode 100644
index 000000000000..a00f92d8e979
--- /dev/null
+++ b/drivers/media/video/mxc/output/mx27_v4l2_output.c
@@ -0,0 +1,1442 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx27_v4l2_output.c
+ *
+ * @brief MX27 V4L2 Video Output Driver
+ *
+ * Video4Linux2 Output Device using MX27 eMMA Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/semaphore.h>
+#include <linux/poll.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include "mxc_v4l2_output.h"
+#include "mx27_pp.h"
+#include "../drivers/video/mxc/mx2fb.h"
+
+#define SDC_FG_FB_FORMAT V4L2_PIX_FMT_RGB565
+
+struct v4l2_output mxc_outputs[1] = {
+ {
+ .index = 0,
+ .name = "DISP0 Video Out",
+ .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct,
+ but no other choice */
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN},
+};
+
+static int video_nr = 16;
+static spinlock_t g_lock = SPIN_LOCK_UNLOCKED;
+vout_data *g_vout;
+
+/* debug counters */
+uint32_t g_irq_cnt;
+uint32_t g_buf_output_cnt;
+uint32_t g_buf_q_cnt;
+uint32_t g_buf_dq_cnt;
+
+#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC
+static uint32_t g_output_fb = -1;
+static uint32_t g_fb_enabled = 0;
+static uint32_t g_pp_ready = 0;
+
+static int fb_event_notify(struct notifier_block *self,
+ unsigned long action, void *data)
+{
+ struct fb_event *event = data;
+ struct fb_info *info = event->info;
+ unsigned long lock_flags;
+ int blank, i;
+
+ for (i = 0; i < num_registered_fb; i++)
+ if (registered_fb[i] == info)
+ break;
+
+ /*
+ * Check if the event is sent by the framebuffer in which
+ * the video is displayed.
+ */
+ if (i != g_output_fb)
+ return 0;
+
+ switch (action) {
+ case FB_EVENT_BLANK:
+ blank = *(int *)event->data;
+ spin_lock_irqsave(&g_lock, lock_flags);
+ g_fb_enabled = !blank;
+ if (blank && g_pp_ready) {
+ if (pp_enable(1))
+ pr_debug("unable to enable PP\n");
+ g_pp_ready = 0;
+ }
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ break;
+ case FB_EVENT_MXC_EOF:
+ spin_lock_irqsave(&g_lock, lock_flags);
+ g_fb_enabled = 1;
+ if (g_pp_ready) {
+ if (pp_enable(1))
+ pr_debug("unable to enable PP\n");
+ g_pp_ready = 0;
+ }
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block fb_event_notifier = {
+ .notifier_call = fb_event_notify,
+};
+
+static struct notifier_block mx2fb_event_notifier = {
+ .notifier_call = fb_event_notify,
+};
+#endif
+
+#define QUEUE_SIZE (MAX_FRAME_NUM + 1)
+static __inline int queue_size(v4l_queue * q)
+{
+ if (q->tail >= q->head)
+ return (q->tail - q->head);
+ else
+ return ((q->tail + QUEUE_SIZE) - q->head);
+}
+
+static __inline int queue_buf(v4l_queue * q, int idx)
+{
+ if (((q->tail + 1) % QUEUE_SIZE) == q->head)
+ return -1; /* queue full */
+ q->list[q->tail] = idx;
+ q->tail = (q->tail + 1) % QUEUE_SIZE;
+ return 0;
+}
+
+static __inline int dequeue_buf(v4l_queue * q)
+{
+ int ret;
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ ret = q->list[q->head];
+ q->head = (q->head + 1) % QUEUE_SIZE;
+ return ret;
+}
+
+static __inline int peek_next_buf(v4l_queue * q)
+{
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ return q->list[q->head];
+}
+
+static __inline unsigned long get_jiffies(struct timeval *t)
+{
+ struct timeval cur;
+
+ if (t->tv_usec >= 1000000) {
+ t->tv_sec += t->tv_usec / 1000000;
+ t->tv_usec = t->tv_usec % 1000000;
+ }
+
+ do_gettimeofday(&cur);
+ if ((t->tv_sec < cur.tv_sec)
+ || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec)))
+ return jiffies;
+
+ if (t->tv_usec < cur.tv_usec) {
+ cur.tv_sec = t->tv_sec - cur.tv_sec - 1;
+ cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec;
+ } else {
+ cur.tv_sec = t->tv_sec - cur.tv_sec;
+ cur.tv_usec = t->tv_usec - cur.tv_usec;
+ }
+
+ return jiffies + timeval_to_jiffies(&cur);
+}
+
+/*!
+ * Private function to free buffers
+ *
+ * @param bufs_paddr Array of physical address of buffers to be freed
+ *
+ * @param bufs_vaddr Array of virtual address of buffers to be freed
+ *
+ * @param num_buf Number of buffers to be freed
+ *
+ * @param size Size for each buffer to be free
+ *
+ * @return status 0 success.
+ */
+static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ if (bufs_vaddr[i] != 0) {
+ dma_free_coherent(0, size, bufs_vaddr[i],
+ bufs_paddr[i]);
+ pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]);
+ bufs_paddr[i] = 0;
+ bufs_vaddr[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Private function to allocate buffers
+ *
+ * @param bufs_paddr Output array of physical address of buffers allocated
+ *
+ * @param bufs_vaddr Output array of virtual address of buffers allocated
+ *
+ * @param num_buf Input number of buffers to allocate
+ *
+ * @param size Input size for each buffer to allocate
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ bufs_vaddr[i] = dma_alloc_coherent(0, size,
+ &bufs_paddr[i],
+ GFP_DMA | GFP_KERNEL);
+
+ if (bufs_vaddr[i] == 0) {
+ mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size);
+ pr_debug("dma_alloc_coherent failed.\n");
+ return -ENOBUFS;
+ }
+ pr_debug("allocated @ paddr=0x%08X, size=%d.\n",
+ (u32) bufs_paddr[i], size);
+ }
+
+ return 0;
+}
+
+static void mxc_v4l2out_timer_handler(unsigned long arg)
+{
+ int index;
+ unsigned long timeout;
+ unsigned long lock_flags;
+ vout_data *vout = (vout_data *) arg;
+
+ pr_debug("timer handler: %lu\n", jiffies);
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ if ((vout->state == STATE_STREAM_OFF)
+ || (vout->state == STATE_STREAM_STOPPING)) {
+ pr_debug("stream has stopped\n");
+ goto exit0;
+ }
+
+ /*
+ * If timer occurs before PP h/w is ready, then set the state to
+ * paused and the timer will be set again when next buffer is queued
+ * or PP completes.
+ */
+ if (vout->ipu_buf[0] != -1) {
+ pr_debug("buffer is busy\n");
+ vout->state = STATE_STREAM_PAUSED;
+ goto exit0;
+ }
+
+ /* Dequeue buffer and pass to PP */
+ index = dequeue_buf(&vout->ready_q);
+ if (index == -1) { /* no buffers ready, should never occur */
+ pr_debug("mxc_v4l2out: timer - no queued buffers ready\n");
+ goto exit0;
+ }
+
+ g_buf_dq_cnt++;
+ vout->frame_count++;
+ vout->ipu_buf[0] = index;
+
+ if (pp_ptr((unsigned int)vout->queue_buf_paddr[index])) {
+ pr_debug("unable to update buffer\n");
+ goto exit0;
+ }
+#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC
+ if (g_fb_enabled && (vout->v4l2_fb.flags != V4L2_FBUF_FLAG_OVERLAY))
+ g_pp_ready = 1;
+ else if (pp_enable(1)) {
+ pr_debug("unable to enable PP\n");
+ goto exit0;
+ }
+#else
+ if (pp_enable(1)) {
+ pr_debug("unable to enable PP\n");
+ goto exit0;
+ }
+#endif
+ pr_debug("enabled index %d\n", index);
+
+ /* Setup timer for next buffer */
+ index = peek_next_buf(&vout->ready_q);
+ pr_debug("next index %d\n", index);
+ if (index != -1) {
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout =
+ vout->start_jiffies + vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug("warning: timer timeout already expired.\n");
+ }
+
+ if (mod_timer(&vout->output_timer, timeout))
+ pr_debug("warning: timer was already set\n");
+
+ pr_debug("timer handler next schedule: %lu\n", timeout);
+ } else {
+ vout->state = STATE_STREAM_PAUSED;
+ pr_debug("timer handler paused\n");
+ }
+
+ exit0:
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+}
+
+irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id)
+{
+ int last_buf;
+ int index;
+ unsigned long timeout;
+ unsigned long lock_flags;
+ vout_data *vout = dev_id;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ g_irq_cnt++;
+
+ if ((vout->state == STATE_STREAM_OFF)
+ || (vout->state == STATE_STREAM_STOPPING)) {
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ return IRQ_HANDLED;
+ }
+
+ if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_gwinfo gwinfo;
+
+ gwinfo.enabled = 1;
+ gwinfo.alpha_value = 255;
+ gwinfo.ck_enabled = 0;
+ gwinfo.xpos = vout->crop_current.left;
+ gwinfo.ypos = vout->crop_current.top;
+ gwinfo.base = (unsigned long)vout->display_bufs[pp_num_last()];
+ gwinfo.xres = vout->crop_current.width;
+ gwinfo.yres = vout->crop_current.height;
+ gwinfo.xres_virtual = vout->crop_current.width;
+ gwinfo.vs_reversed = 0;
+
+ mx2_gw_set(&gwinfo);
+ }
+
+ /* Process previous buffer */
+ last_buf = vout->ipu_buf[0];
+ pr_debug("last_buf %d g_irq_cnt %d\n", last_buf, g_irq_cnt);
+ if (last_buf != -1) {
+ g_buf_output_cnt++;
+ vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE;
+ queue_buf(&vout->done_q, last_buf);
+ vout->ipu_buf[0] = -1;
+ wake_up_interruptible(&vout->v4l_bufq);
+ }
+
+ /* Setup timer for next buffer, when stream has been paused */
+ if ((vout->state == STATE_STREAM_PAUSED)
+ && ((index = peek_next_buf(&vout->ready_q)) != -1)) {
+ pr_debug("next index %d\n", index);
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout =
+ vout->start_jiffies + vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug("warning: timer timeout already expired.\n");
+ }
+
+ vout->state = STATE_STREAM_ON;
+
+ if (mod_timer(&vout->output_timer, timeout))
+ pr_debug("warning: timer was already set\n");
+
+ pr_debug("timer handler next schedule: %lu\n", timeout);
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * Start the output stream
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamon(vout_data * vout)
+{
+ unsigned long timeout;
+ int index;
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state != STATE_STREAM_OFF)
+ return -EBUSY;
+
+ if (queue_size(&vout->ready_q) < 1) {
+ pr_debug("no buffers queued yet!\n");
+ return -EINVAL;
+ }
+
+ vout->ipu_buf[0] = -1;
+
+ if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ /* Free previously allocated buffer */
+ mxc_free_buffers(vout->display_bufs, vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+ /* Allocate buffers for foreground */
+ if (mxc_allocate_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr, 2,
+ vout->display_buf_size) < 0) {
+ pr_debug("unable to allocate SDC FG buffers\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* Configure PP */
+ if (pp_cfg(vout)) {
+ /* Free previously allocated buffer */
+ mxc_free_buffers(vout->display_bufs, vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+ pr_debug("failed to config PP.\n");
+ return -EINVAL;
+ }
+#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC
+ g_output_fb = vout->output_fb_num[vout->cur_disp_output];
+ g_fb_enabled = 0;
+ g_pp_ready = 0;
+ fb_register_client(&fb_event_notifier);
+ mx2fb_register_client(&mx2fb_event_notifier);
+#endif
+ vout->frame_count = 0;
+ vout->state = STATE_STREAM_ON;
+ index = peek_next_buf(&vout->ready_q);
+
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout = jiffies;
+ else
+ timeout = get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug("warning: timer timeout already expired.\n");
+ }
+
+ vout->start_jiffies = vout->output_timer.expires = timeout;
+ pr_debug("STREAMON:Add timer %d timeout @ %lu jiffies, current = %lu\n",
+ index, timeout, jiffies);
+ add_timer(&vout->output_timer);
+
+ return 0;
+}
+
+/*!
+ * Shut down the voutera
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamoff(vout_data * vout)
+{
+ int i, retval = 0;
+ unsigned long lock_flag = 0;
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state == STATE_STREAM_OFF) {
+ return 0;
+ }
+
+ spin_lock_irqsave(&g_lock, lock_flag);
+
+ del_timer(&vout->output_timer);
+ pp_enable(0); /* Disable PP */
+
+ if (vout->state == STATE_STREAM_ON) {
+ vout->state = STATE_STREAM_STOPPING;
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flag);
+
+ vout->ready_q.head = vout->ready_q.tail = 0;
+ vout->done_q.head = vout->done_q.tail = 0;
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+
+ vout->state = STATE_STREAM_OFF;
+
+ if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
+ struct fb_gwinfo gwinfo;
+
+ /* Disable graphic window */
+ gwinfo.enabled = 0;
+ mx2_gw_set(&gwinfo);
+ }
+#ifdef CONFIG_VIDEO_MXC_OUTPUT_FBSYNC
+ g_output_fb = -1;
+ g_fb_enabled = 0;
+ g_pp_ready = 0;
+ fb_unregister_client(&fb_event_notifier);
+ mx2fb_unregister_client(&mx2fb_event_notifier);
+#endif
+
+ mxc_free_buffers(vout->display_bufs, vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+
+ return retval;
+}
+
+/*
+ * Valid whether the palette is supported
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 1 if supported, 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ return (palette == V4L2_PIX_FMT_YUV420);
+}
+
+/*
+ * Returns bits per pixel for given pixel format
+ *
+ * @param pixelformat V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return bits per pixel of pixelformat
+ */
+static u32 fmt_to_bpp(u32 pixelformat)
+{
+ u32 bpp;
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ bpp = 16;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB24:
+ bpp = 24;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB32:
+ bpp = 32;
+ break;
+ default:
+ bpp = 8;
+ break;
+ }
+ return bpp;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_g_fmt(vout_data * vout, struct v4l2_format *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ return -EINVAL;
+ }
+ *f = vout->v2f;
+ return 0;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_s_fmt(vout_data * vout, struct v4l2_format *f)
+{
+ int retval = 0;
+ u32 size = 0;
+ u32 bytesperline;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ goto err0;
+ }
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ pr_debug("pixel format not supported\n");
+ retval = -EINVAL;
+ goto err0;
+ }
+
+ bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) /
+ 8;
+ if (f->fmt.pix.bytesperline < bytesperline) {
+ f->fmt.pix.bytesperline = bytesperline;
+ } else {
+ bytesperline = f->fmt.pix.bytesperline;
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV422P:
+ /* byteperline for YUV planar formats is for
+ Y plane only */
+ size = bytesperline * f->fmt.pix.height * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ size = (bytesperline * f->fmt.pix.height * 3) / 2;
+ break;
+ default:
+ size = bytesperline * f->fmt.pix.height;
+ break;
+ }
+
+ /* Return the actual size of the image to the app */
+ f->fmt.pix.sizeimage = size;
+
+ vout->v2f.fmt.pix.sizeimage = size;
+ vout->v2f.fmt.pix.width = f->fmt.pix.width;
+ vout->v2f.fmt.pix.height = f->fmt.pix.height;
+ vout->v2f.fmt.pix.pixelformat = f->fmt.pix.pixelformat;
+ vout->v2f.fmt.pix.bytesperline = f->fmt.pix.bytesperline;
+
+ retval = 0;
+ err0:
+ return retval;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_get_v42lout_control(vout_data * vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ return (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0;
+ case V4L2_CID_VFLIP:
+ return (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0;
+ case (V4L2_CID_PRIVATE_BASE + 1):
+ return vout->rotate;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_set_v42lout_control(vout_data * vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_MXC_ROT:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * V4L2 interface - open function
+ *
+ * @param inode structure inode *
+ *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l2out_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+ int err;
+
+ if (!vout) {
+ pr_info("Internal error, vout_data not found!\n");
+ return -ENODEV;
+ }
+
+ down(&vout->busy_lock);
+
+ err = -EINTR;
+ if (signal_pending(current))
+ goto oops;
+
+ if (vout->open_count++ == 0) {
+ pp_init(vout);
+
+ init_waitqueue_head(&vout->v4l_bufq);
+
+ init_timer(&vout->output_timer);
+ vout->output_timer.function = mxc_v4l2out_timer_handler;
+ vout->output_timer.data = (unsigned long)vout;
+
+ vout->state = STATE_STREAM_OFF;
+ g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0;
+
+ }
+
+ file->private_data = dev;
+ up(&vout->busy_lock);
+ return 0;
+
+ oops:
+ up(&vout->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L2 interface - close function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int mxc_v4l2out_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = file->private_data;
+ vout_data *vout = video_get_drvdata(dev);
+
+ if (--vout->open_count == 0) {
+ pr_debug("release resource\n");
+
+ pp_exit(vout);
+ if (vout->state != STATE_STREAM_OFF)
+ mxc_v4l2out_streamoff(vout);
+
+ file->private_data = NULL;
+
+ mxc_free_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt, vout->queue_buf_size);
+ vout->buffer_cnt = 0;
+ mxc_free_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+
+ /* capture off */
+ wake_up_interruptible(&vout->v4l_bufq);
+ }
+
+ return 0;
+}
+
+/*!
+ * V4L2 interface - ioctl function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void *
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static int
+mxc_v4l2out_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *dev = file->private_data;
+ vout_data *vout = video_get_drvdata(dev);
+ int retval = 0;
+ int i = 0;
+
+ if (!vout)
+ return -EBADF;
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+ strcpy(cap->driver, "mxc_v4l2_output");
+ cap->version = 0;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *gf = arg;
+ retval = mxc_v4l2out_g_fmt(vout, gf);
+ break;
+ }
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *sf = arg;
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+ retval = mxc_v4l2out_s_fmt(vout, sf);
+ break;
+ }
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *req = arg;
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ pr_debug
+ ("VIDIOC_REQBUFS: incorrect buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ if (req->count == 0)
+ mxc_v4l2out_streamoff(vout);
+
+ if (vout->state == STATE_STREAM_OFF) {
+ if (vout->queue_buf_paddr[0] != 0) {
+ mxc_free_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ pr_debug
+ ("VIDIOC_REQBUFS: freed buffers\n");
+ }
+ vout->buffer_cnt = 0;
+ } else {
+ pr_debug("VIDIOC_REQBUFS: Buffer is in use\n");
+ retval = -EBUSY;
+ break;
+ }
+
+ if (req->count == 0)
+ break;
+
+ if (req->count < MIN_FRAME_NUM) {
+ req->count = MIN_FRAME_NUM;
+ } else if (req->count > MAX_FRAME_NUM) {
+ req->count = MAX_FRAME_NUM;
+ }
+ vout->buffer_cnt = req->count;
+ vout->queue_buf_size =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+
+ retval = mxc_allocate_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ if (retval < 0)
+ break;
+
+ /* Init buffer queues */
+ vout->done_q.head = 0;
+ vout->done_q.tail = 0;
+ vout->ready_q.head = 0;
+ vout->ready_q.tail = 0;
+
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ memset(&(vout->v4l2_bufs[i]), 0,
+ sizeof(vout->v4l2_bufs[i]));
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].memory = V4L2_MEMORY_MMAP;
+ vout->v4l2_bufs[i].index = i;
+ vout->v4l2_bufs[i].type =
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ vout->v4l2_bufs[i].length =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+ vout->v4l2_bufs[i].m.offset =
+ (unsigned long)vout->queue_buf_paddr[i];
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+ break;
+ }
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ u32 type = buf->type;
+ int index = buf->index;
+
+ if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt)) {
+ pr_debug
+ ("VIDIOC_QUERYBUFS: incorrect buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+ down(&vout->param_lock);
+ memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf));
+ up(&vout->param_lock);
+ break;
+ }
+ case VIDIOC_QBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ unsigned long lock_flags;
+ unsigned long timeout;
+
+ if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt) || (buf->flags != 0)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ pr_debug("VIDIOC_QBUF: %d\n", buf->index);
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf));
+ vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED;
+
+ g_buf_q_cnt++;
+ queue_buf(&vout->ready_q, index);
+
+ if (vout->state == STATE_STREAM_PAUSED) {
+ index = peek_next_buf(&vout->ready_q);
+
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec ==
+ 0)
+ && (vout->v4l2_bufs[index].timestamp.
+ tv_usec == 0))
+ timeout =
+ vout->start_jiffies +
+ vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].
+ timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug
+ ("warning: timer timeout already expired.\n");
+ }
+
+ vout->output_timer.expires = timeout;
+ pr_debug
+ ("QBUF:Add timer %d timeout @ %lu jiffies, "
+ "current = %lu\n", index, timeout,
+ jiffies);
+ add_timer(&vout->output_timer);
+ vout->state = STATE_STREAM_ON;
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ break;
+ }
+ case VIDIOC_DQBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int idx;
+
+ pr_debug("VIDIOC_DQBUF: q size = %d\n",
+ queue_size(&vout->done_q));
+
+ if ((queue_size(&vout->done_q) == 0) &&
+ (file->f_flags & O_NONBLOCK)) {
+ retval = -EAGAIN;
+ break;
+ }
+
+ if (!wait_event_interruptible_timeout(vout->v4l_bufq,
+ queue_size(&vout->
+ done_q)
+ != 0, 10 * HZ)) {
+ pr_debug("VIDIOC_DQBUF: timeout\n");
+ retval = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ pr_debug("VIDIOC_DQBUF: interrupt received\n");
+ retval = -ERESTARTSYS;
+ break;
+ }
+ idx = dequeue_buf(&vout->done_q);
+ if (idx == -1) { /* No frame free */
+ pr_debug
+ ("VIDIOC_DQBUF: no free buffers, returning\n");
+ retval = -EAGAIN;
+ break;
+ }
+ if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) ==
+ 0)
+ pr_debug
+ ("VIDIOC_DQBUF: buffer in done q, but not "
+ "flagged as done\n");
+
+ vout->v4l2_bufs[idx].flags = 0;
+ memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf));
+ pr_debug("VIDIOC_DQBUF: %d\n", buf->index);
+ break;
+ }
+ case VIDIOC_STREAMON:
+ {
+ retval = mxc_v4l2out_streamon(vout);
+ break;
+ }
+ case VIDIOC_STREAMOFF:
+ {
+ retval = mxc_v4l2out_streamoff(vout);
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ retval = mxc_get_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ retval = mxc_set_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cap = arg;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ cap->bounds = vout->crop_bounds[vout->cur_disp_output];
+ cap->defrect = vout->crop_bounds[vout->cur_disp_output];
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ crop->c = vout->crop_current;
+ break;
+ }
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b =
+ &(vout->crop_bounds[vout->cur_disp_output]);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.height < 0) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.width < 0) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (crop->c.top < b->top)
+ crop->c.top = b->top;
+ if (crop->c.top > b->top + b->height)
+ crop->c.top = b->top + b->height;
+ if (crop->c.height > b->top - crop->c.top + b->height)
+ crop->c.height =
+ b->top - crop->c.top + b->height;
+
+ if (crop->c.left < b->left)
+ crop->c.top = b->left;
+ if (crop->c.left > b->left + b->width)
+ crop->c.top = b->left + b->width;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width =
+ b->left - crop->c.left + b->width;
+
+ /* stride line limitation */
+ crop->c.height -= crop->c.height % 8;
+ crop->c.width -= crop->c.width % 8;
+
+ vout->crop_current = crop->c;
+
+ vout->display_buf_size = vout->crop_current.width *
+ vout->crop_current.height;
+ vout->display_buf_size *=
+ fmt_to_bpp(SDC_FG_FB_FORMAT) / 8;
+ break;
+ }
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *output = arg;
+
+ if ((output->index >= 2) ||
+ (vout->output_enabled[output->index] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ *output = mxc_outputs[0];
+ output->name[4] = '0' + output->index;
+ break;
+ }
+ case VIDIOC_G_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ *p_output_num = vout->cur_disp_output;
+ break;
+ }
+ case VIDIOC_S_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ if ((*p_output_num >= 2) ||
+ (vout->output_enabled[*p_output_num] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+
+ vout->cur_disp_output = *p_output_num;
+ break;
+ }
+ case VIDIOC_G_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+ *fb = vout->v4l2_fb;
+ break;
+ }
+ case VIDIOC_S_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+ vout->v4l2_fb = *fb;
+ vout->v4l2_fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ break;
+ }
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_G_PARM:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ default:
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&vout->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L2 interface - ioctl function
+ *
+ * @return None
+ */
+static int
+mxc_v4l2out_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, mxc_v4l2out_do_ioctl);
+}
+
+/*!
+ * V4L2 interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error,
+ * ENOBUFS remap_page error
+ */
+static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = file->private_data;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ int res = 0;
+ vout_data *vout = video_get_drvdata(dev);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ /* make buffers write-thru cacheable */
+ vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) &
+ ~L_PTE_BUFFERABLE);
+
+ if (remap_pfn_range(vma, start, vma->vm_pgoff, size, vma->vm_page_prot)) {
+ pr_debug("mxc_mmap(V4L)i - remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ mxc_mmap_exit:
+ up(&vout->busy_lock);
+ return res;
+}
+
+/*!
+ * V4L2 interface - poll function
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_v4l2out_poll(struct file *file, poll_table * wait)
+{
+ struct video_device *dev = file->private_data;
+ vout_data *vout = video_get_drvdata(dev);
+
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ queue = &vout->v4l_bufq;
+ poll_wait(file, queue, wait);
+
+ up(&vout->busy_lock);
+ return res;
+}
+
+static struct file_operations mxc_v4l2out_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_v4l2out_open,
+ .release = mxc_v4l2out_close,
+ .ioctl = mxc_v4l2out_ioctl,
+ .mmap = mxc_v4l2out_mmap,
+ .poll = mxc_v4l2out_poll,
+};
+
+static struct video_device mxc_v4l2out_template = {
+ .name = "MXC Video Output",
+ .vfl_type = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING,
+ .fops = &mxc_v4l2out_fops,
+ .release = video_device_release,
+};
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxc_v4l2out_probe(struct platform_device *pdev)
+{
+ int i;
+ vout_data *vout;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ g_vout = vout = kmalloc(sizeof(vout_data), GFP_KERNEL);
+
+ if (!vout)
+ return 0;
+
+ memset(vout, 0, sizeof(vout_data));
+
+ vout->video_dev = video_device_alloc();
+ if (vout->video_dev == NULL)
+ return -1;
+ vout->video_dev->minor = -1;
+
+ *(vout->video_dev) = mxc_v4l2out_template;
+
+ /* register v4l device */
+ if (video_register_device(vout->video_dev,
+ VFL_TYPE_GRABBER, video_nr) == -1) {
+ pr_debug("video_register_device failed\n");
+ return 0;
+ }
+ pr_debug("mxc_v4l2out: registered device video%d\n",
+ vout->video_dev->minor & 0x1f);
+
+ video_set_drvdata(vout->video_dev, vout);
+
+ init_MUTEX(&vout->param_lock);
+ init_MUTEX(&vout->busy_lock);
+
+ /* setup outputs and cropping */
+ vout->cur_disp_output = -1;
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strncmp(idstr, "DISP", 4) == 0) {
+ int disp_num = i;
+ vout->crop_bounds[disp_num].left = 0;
+ vout->crop_bounds[disp_num].top = 0;
+ vout->crop_bounds[disp_num].width =
+ registered_fb[i]->var.xres;
+ vout->crop_bounds[disp_num].height =
+ registered_fb[i]->var.yres;
+ vout->output_enabled[disp_num] = true;
+ vout->output_fb_num[disp_num] = i;
+ if (vout->cur_disp_output == -1)
+ vout->cur_disp_output = disp_num;
+ }
+
+ }
+ vout->crop_current = vout->crop_bounds[vout->cur_disp_output];
+
+ /* Setup framebuffer parameters */
+ vout->v4l2_fb.capability = V4L2_FBUF_CAP_EXTERNOVERLAY;
+ vout->v4l2_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2out_driver = {
+ .driver = {
+ .name = "MXC Video Output",
+ .owner = THIS_MODULE,
+ .bus = &platform_bus_type,
+ },
+ .probe = mxc_v4l2out_probe,
+ .remove = NULL,
+};
+
+static void camera_platform_release(struct device *device)
+{
+}
+
+static struct platform_device mxc_v4l2out_device = {
+ .name = "MXC Video Output",
+ .dev = {
+ .release = camera_platform_release,
+ },
+ .id = 0,
+};
+
+/*!
+ * mxc v4l2 init function
+ *
+ */
+static int mxc_v4l2out_init(void)
+{
+ u8 err = 0;
+
+ err = platform_driver_register(&mxc_v4l2out_driver);
+ if (err == 0) {
+ platform_device_register(&mxc_v4l2out_device);
+ }
+ return err;
+}
+
+/*!
+ * mxc v4l2 cleanup function
+ *
+ */
+static void mxc_v4l2out_clean(void)
+{
+ pr_debug("unregistering video\n");
+
+ video_unregister_device(g_vout->video_dev);
+
+ platform_driver_unregister(&mxc_v4l2out_driver);
+ platform_device_unregister(&mxc_v4l2out_device);
+ kfree(g_vout);
+ g_vout = NULL;
+}
+
+module_init(mxc_v4l2out_init);
+module_exit(mxc_v4l2out_clean);
+
+module_param(video_nr, int, 0444);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2-driver for MXC video output");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c b/drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c
new file mode 100644
index 000000000000..4a82da2d7d00
--- /dev/null
+++ b/drivers/media/video/mxc/output/mx31_v4l2_wvga_output.c
@@ -0,0 +1,1926 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file drivers/media/video/mxc/output/mxc_v4l2_output.c
+ *
+ * @brief MXC V4L2 Video Output Driver
+ *
+ * Video4Linux2 Output Device using MXC IPU Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/mxcfb.h>
+#include <mach/ipu.h>
+
+#include "mxc_v4l2_output.h"
+
+vout_data *g_vout;
+#define SDC_FG_FB_FORMAT IPU_PIX_FMT_RGB565
+
+struct v4l2_output mxc_outputs[2] = {
+ {
+ .index = MXC_V4L2_OUT_2_SDC,
+ .name = "DISP3 Video Out",
+ .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct,
+ but no other choice */
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN},
+ {
+ .index = MXC_V4L2_OUT_2_ADC,
+ .name = "DISPx Video Out",
+ .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct,
+ but no other choice */
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN}
+};
+
+static int video_nr = 16;
+static DEFINE_SPINLOCK(g_lock);
+static unsigned int g_pp_out_number;
+static unsigned int g_pp_in_number;
+
+/* debug counters */
+uint32_t g_irq_cnt;
+uint32_t g_buf_output_cnt;
+uint32_t g_buf_q_cnt;
+uint32_t g_buf_dq_cnt;
+
+static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type)
+{
+ return ((type == IPU_INPUT_BUFFER) ? ((uint32_t) ch & 0xFF) :
+ ((type == IPU_OUTPUT_BUFFER) ? (((uint32_t) ch >> 8) & 0xFF)
+ : (((uint32_t) ch >> 16) & 0xFF)));
+};
+
+static inline uint32_t DMAParamAddr(uint32_t dma_ch)
+{
+ return (0x10000 | (dma_ch << 4));
+};
+
+#define QUEUE_SIZE (MAX_FRAME_NUM + 1)
+static inline int queue_size(v4l_queue * q)
+{
+ if (q->tail >= q->head)
+ return (q->tail - q->head);
+ else
+ return ((q->tail + QUEUE_SIZE) - q->head);
+}
+
+static inline int queue_buf(v4l_queue * q, int idx)
+{
+ if (((q->tail + 1) % QUEUE_SIZE) == q->head)
+ return -1; /* queue full */
+ q->list[q->tail] = idx;
+ q->tail = (q->tail + 1) % QUEUE_SIZE;
+ return 0;
+}
+
+static inline int dequeue_buf(v4l_queue * q)
+{
+ int ret;
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ ret = q->list[q->head];
+ q->head = (q->head + 1) % QUEUE_SIZE;
+ return ret;
+}
+
+static inline int peek_next_buf(v4l_queue * q)
+{
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ return q->list[q->head];
+}
+
+static inline unsigned long get_jiffies(struct timeval *t)
+{
+ struct timeval cur;
+
+ if (t->tv_usec >= 1000000) {
+ t->tv_sec += t->tv_usec / 1000000;
+ t->tv_usec = t->tv_usec % 1000000;
+ }
+
+ do_gettimeofday(&cur);
+ if ((t->tv_sec < cur.tv_sec)
+ || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec)))
+ return jiffies;
+
+ if (t->tv_usec < cur.tv_usec) {
+ cur.tv_sec = t->tv_sec - cur.tv_sec - 1;
+ cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec;
+ } else {
+ cur.tv_sec = t->tv_sec - cur.tv_sec;
+ cur.tv_usec = t->tv_usec - cur.tv_usec;
+ }
+
+ return jiffies + timeval_to_jiffies(&cur);
+}
+
+/*!
+ * Private function to free buffers
+ *
+ * @param bufs_paddr Array of physical address of buffers to be freed
+ *
+ * @param bufs_vaddr Array of virtual address of buffers to be freed
+ *
+ * @param num_buf Number of buffers to be freed
+ *
+ * @param size Size for each buffer to be free
+ *
+ * @return status 0 success.
+ */
+static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ if (bufs_vaddr[i] != 0) {
+ dma_free_coherent(0, size, bufs_vaddr[i],
+ bufs_paddr[i]);
+ pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]);
+ bufs_paddr[i] = 0;
+ bufs_vaddr[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Private function to allocate buffers
+ *
+ * @param bufs_paddr Output array of physical address of buffers allocated
+ *
+ * @param bufs_vaddr Output array of virtual address of buffers allocated
+ *
+ * @param num_buf Input number of buffers to allocate
+ *
+ * @param size Input size for each buffer to allocate
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ bufs_vaddr[i] = dma_alloc_coherent(0, size,
+ &bufs_paddr[i],
+ GFP_DMA | GFP_KERNEL);
+
+ if (bufs_vaddr[i] == 0) {
+ mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size);
+ printk(KERN_ERR "dma_alloc_coherent failed.\n");
+ return -ENOBUFS;
+ }
+ pr_debug("allocated @ paddr=0x%08X, size=%d.\n",
+ (u32) bufs_paddr[i], size);
+ }
+
+ return 0;
+}
+
+/*
+ * Returns bits per pixel for given pixel format
+ *
+ * @param pixelformat V4L2_PIX_FMT_RGB565,
+ * V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return bits per pixel of pixelformat
+ */
+static u32 fmt_to_bpp(u32 pixelformat)
+{
+ u32 bpp;
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_RGB565:
+ bpp = 16;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB24:
+ bpp = 24;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB32:
+ bpp = 32;
+ break;
+ default:
+ bpp = 8;
+ break;
+ }
+ return bpp;
+}
+
+static u32 bpp_to_fmt(struct fb_info *fbi)
+{
+ if (fbi->var.nonstd)
+ return fbi->var.nonstd;
+
+ if (fbi->var.bits_per_pixel == 24)
+ return V4L2_PIX_FMT_BGR24;
+ else if (fbi->var.bits_per_pixel == 32)
+ return V4L2_PIX_FMT_BGR32;
+ else if (fbi->var.bits_per_pixel == 16)
+ return V4L2_PIX_FMT_RGB565;
+
+ return 0;
+}
+
+static void mxc_v4l2out_timer_handler(unsigned long arg)
+{
+ int index;
+ unsigned long timeout;
+ unsigned long lock_flags = 0;
+ vout_data *vout = (vout_data *) arg;
+
+ dev_dbg(vout->video_dev->dev, "timer handler: %lu\n", jiffies);
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ if ((vout->state == STATE_STREAM_STOPPING)
+ || (vout->state == STATE_STREAM_OFF))
+ goto exit0;
+ /*
+ * If timer occurs before IPU h/w is ready, then set the state to
+ * paused and the timer will be set again when next buffer is queued
+ * or PP comletes
+ */
+ if (vout->ipu_buf[0] != -1) {
+ dev_dbg(vout->video_dev->dev, "IPU buffer busy\n");
+ vout->state = STATE_STREAM_PAUSED;
+ goto exit0;
+ }
+
+ /* One frame buffer should be ready here */
+ if (vout->frame_count % 2 == 1) {
+ /* set BUF0 rdy */
+ if (ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0) <
+ 0)
+ pr_debug("error selecting display buf 0");
+ } else {
+ if (ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 1) <
+ 0)
+ pr_debug("error selecting display buf 1");
+ }
+
+ /* Dequeue buffer and pass to IPU */
+ index = dequeue_buf(&vout->ready_q);
+ if (index == -1) { /* no buffers ready, should never occur */
+ dev_err(vout->video_dev->dev,
+ "mxc_v4l2out: timer - no queued buffers ready\n");
+ goto exit0;
+ }
+
+ g_buf_dq_cnt++;
+ vout->frame_count++;
+ vout->ipu_buf[1] = vout->ipu_buf[0] = index;
+
+ if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ 0,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.
+ offset) < 0)
+ goto exit0;
+
+ if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ 1,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.
+ offset + vout->v2f.fmt.pix.width / 2) < 0)
+ goto exit0;
+
+ /* All buffer should now ready in IPU out, tranfer to display buf */
+ if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+ 0,
+ vout->
+ display_bufs[(vout->frame_count -
+ 1) % 2]) < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to update buffer %d address\n",
+ vout->next_rdy_ipu_buf);
+ goto exit0;
+ }
+ if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+ 1,
+ vout->
+ display_bufs[(vout->frame_count -
+ 1) % 2] +
+ vout->crop_current.width / 2 *
+ bytes_per_pixel(SDC_FG_FB_FORMAT)) < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to update buffer %d address\n",
+ vout->next_rdy_ipu_buf);
+ goto exit0;
+ }
+
+ if (ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0) < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ goto exit0;
+ }
+
+ if (ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0) < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ goto exit0;
+ }
+
+ /* Setup timer for next buffer */
+ index = peek_next_buf(&vout->ready_q);
+ if (index != -1) {
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout =
+ vout->start_jiffies + vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ dev_dbg(vout->video_dev->dev,
+ "warning: timer timeout already expired.\n");
+ }
+ if (mod_timer(&vout->output_timer, timeout))
+ dev_dbg(vout->video_dev->dev,
+ "warning: timer was already set\n");
+
+ dev_dbg(vout->video_dev->dev,
+ "timer handler next schedule: %lu\n", timeout);
+ } else {
+ vout->state = STATE_STREAM_PAUSED;
+ }
+
+exit0:
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+}
+
+extern void _ipu_write_param_mem(uint32_t addr, uint32_t *data,
+ uint32_t numWords);
+
+static irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id)
+{
+ unsigned long lock_flags = 0;
+ vout_data *vout = dev_id;
+ uint32_t u_offset;
+ uint32_t v_offset;
+ uint32_t local_params[4];
+ uint32_t width, height;
+ uint32_t dma_chan;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+ g_irq_cnt++;
+
+ dma_chan = channel_2_dma(vout->post_proc_ch, IPU_INPUT_BUFFER);
+ memset(&local_params, 0, sizeof(local_params));
+
+ if (g_pp_in_number % 2 == 1) {
+ u_offset = vout->offset.u_offset - vout->v2f.fmt.pix.width / 4;
+ v_offset = vout->offset.v_offset - vout->v2f.fmt.pix.width / 4;
+ width = vout->v2f.fmt.pix.width / 2;
+ height = vout->v2f.fmt.pix.height;
+ local_params[3] =
+ (uint32_t) ((width - 1) << 12) | ((uint32_t) (height -
+ 1) << 24);
+ local_params[1] = (1UL << (46 - 32)) | (u_offset << (53 - 32));
+ local_params[2] = u_offset >> (64 - 53);
+ local_params[2] |= v_offset << (79 - 64);
+ local_params[3] |= v_offset >> (96 - 79);
+ _ipu_write_param_mem(DMAParamAddr(dma_chan), local_params, 4);
+
+ if (ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1) <
+ 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ }
+ } else {
+ u_offset = vout->offset.u_offset;
+ v_offset = vout->offset.v_offset;
+ width = vout->v2f.fmt.pix.width / 2;
+ height = vout->v2f.fmt.pix.height;
+ local_params[3] =
+ (uint32_t) ((width - 1) << 12) | ((uint32_t) (height -
+ 1) << 24);
+ local_params[1] = (1UL << (46 - 32)) | (u_offset << (53 - 32));
+ local_params[2] = u_offset >> (64 - 53);
+ local_params[2] |= v_offset << (79 - 64);
+ local_params[3] |= v_offset >> (96 - 79);
+ _ipu_write_param_mem(DMAParamAddr(dma_chan), local_params, 4);
+ }
+ g_pp_in_number++;
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxc_v4l2out_pp_out_irq_handler(int irq, void *dev_id)
+{
+ vout_data *vout = dev_id;
+ int index;
+ unsigned long timeout;
+ u32 lock_flags = 0;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ if (g_pp_out_number % 2 == 1) {
+ if (ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1)
+ < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ }
+ } else {
+ if (vout->ipu_buf[0] != -1) {
+ vout->v4l2_bufs[vout->ipu_buf[0]].flags =
+ V4L2_BUF_FLAG_DONE;
+ queue_buf(&vout->done_q, vout->ipu_buf[0]);
+ wake_up_interruptible(&vout->v4l_bufq);
+ vout->ipu_buf[0] = -1;
+ }
+ if (vout->state == STATE_STREAM_STOPPING) {
+ if ((vout->ipu_buf[0] == -1)
+ && (vout->ipu_buf[1] == -1))
+ vout->state = STATE_STREAM_OFF;
+ } else if ((vout->state == STATE_STREAM_PAUSED)
+ && ((index = peek_next_buf(&vout->ready_q)) != -1)) {
+ /*!
+ * Setup timer for next buffer,
+ * when stream has been paused
+ */
+ pr_debug("next index %d\n", index);
+
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout =
+ vout->start_jiffies +
+ vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].
+ timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug
+ ("warning: timer timeout"
+ "already expired.\n");
+ }
+
+ vout->state = STATE_STREAM_ON;
+
+ if (mod_timer(&vout->output_timer, timeout))
+ pr_debug("warning: timer was already set\n");
+
+ pr_debug("timer handler next schedule: %lu\n", timeout);
+ }
+ }
+ g_pp_out_number++;
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ return IRQ_HANDLED;
+}
+
+/*!
+ * Start the output stream
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamon(vout_data *vout)
+{
+ struct device *dev = vout->video_dev->dev;
+ ipu_channel_params_t params;
+ struct mxcfb_pos fb_pos;
+ struct fb_var_screeninfo fbvar;
+ struct fb_info *fbi =
+ registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ int pp_in_buf[2];
+ u16 out_width;
+ u16 out_height;
+ ipu_channel_t display_input_ch = MEM_PP_MEM;
+ bool use_direct_adc = false;
+ mm_segment_t old_fs;
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state != STATE_STREAM_OFF)
+ return -EBUSY;
+
+ if (queue_size(&vout->ready_q) < 2) {
+ dev_err(dev, "2 buffers not been queued yet!\n");
+ return -EINVAL;
+ }
+
+ out_width = vout->crop_current.width;
+ out_height = vout->crop_current.height;
+
+ vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0;
+ vout->ipu_buf[0] = pp_in_buf[0] = dequeue_buf(&vout->ready_q);
+ vout->ipu_buf[1] = pp_in_buf[1] = vout->ipu_buf[0];
+ vout->frame_count = 1;
+ g_pp_out_number = 1;
+ g_pp_in_number = 1;
+
+ ipu_enable_irq(IPU_IRQ_PP_IN_EOF);
+ ipu_enable_irq(IPU_IRQ_PP_OUT_EOF);
+
+ /* Init Display Channel */
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL
+ if (vout->cur_disp_output < DISP3) {
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, 0);
+ fbi = NULL;
+ if (ipu_can_rotate_in_place(vout->rotate)) {
+ dev_dbg(dev, "Using PP direct to ADC channel\n");
+ use_direct_adc = true;
+ vout->display_ch = MEM_PP_ADC;
+ vout->post_proc_ch = MEM_PP_ADC;
+
+ memset(&params, 0, sizeof(params));
+ params.mem_pp_adc.in_width = vout->v2f.fmt.pix.width;
+ params.mem_pp_adc.in_height = vout->v2f.fmt.pix.height;
+ params.mem_pp_adc.in_pixel_fmt =
+ vout->v2f.fmt.pix.pixelformat;
+ params.mem_pp_adc.out_width = out_width;
+ params.mem_pp_adc.out_height = out_height;
+ params.mem_pp_adc.out_pixel_fmt = SDC_FG_FB_FORMAT;
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ params.mem_pp_adc.out_left =
+ 2 + vout->crop_current.left;
+#else
+ params.mem_pp_adc.out_left =
+ 12 + vout->crop_current.left;
+#endif
+ params.mem_pp_adc.out_top = vout->crop_current.top;
+ if (ipu_init_channel(
+ vout->post_proc_ch, &params) != 0) {
+ dev_err(dev, "Error initializing PP chan\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ params.mem_pp_adc.
+ in_pixel_fmt,
+ params.mem_pp_adc.in_width,
+ params.mem_pp_adc.in_height,
+ vout->v2f.fmt.pix.
+ bytesperline /
+ bytes_per_pixel(params.
+ mem_pp_adc.
+ in_pixel_fmt),
+ vout->rotate,
+ vout->
+ v4l2_bufs[pp_in_buf[0]].m.
+ offset,
+ vout->
+ v4l2_bufs[pp_in_buf[1]].m.
+ offset,
+ vout->offset.u_offset,
+ vout->offset.v_offset) !=
+ 0) {
+ dev_err(dev, "Error initializing PP in buf\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_pp_adc.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ vout->rotate, 0, 0, 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP"
+ "output buffer\n");
+ return -EINVAL;
+ }
+
+ } else {
+ dev_dbg(dev, "Using ADC SYS2 channel\n");
+ vout->display_ch = ADC_SYS2;
+ vout->post_proc_ch = MEM_PP_MEM;
+
+ if (vout->display_bufs[0]) {
+ mxc_free_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+ }
+
+ vout->display_buf_size = vout->crop_current.width *
+ vout->crop_current.height *
+ fmt_to_bpp(SDC_FG_FB_FORMAT) / 8;
+ mxc_allocate_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+
+ memset(&params, 0, sizeof(params));
+ params.adc_sys2.disp = vout->cur_disp_output;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ params.adc_sys2.out_left = 2 + vout->crop_current.left;
+#else
+ params.adc_sys2.out_left = 12 + vout->crop_current.left;
+#endif
+ params.adc_sys2.out_top = vout->crop_current.top;
+ if (ipu_init_channel(ADC_SYS2, &params) < 0)
+ return -EINVAL;
+
+ if (ipu_init_channel_buffer(vout->display_ch,
+ IPU_INPUT_BUFFER,
+ SDC_FG_FB_FORMAT,
+ out_width, out_height,
+ out_width, IPU_ROTATE_NONE,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing SDC FG buffer\n");
+ return -EINVAL;
+ }
+ }
+ } else
+#endif
+ { /* Use SDC */
+ dev_dbg(dev, "Using SDC channel\n");
+
+ fbvar = fbi->var;
+ if (vout->cur_disp_output == 3) {
+ vout->display_ch = MEM_FG_SYNC;
+ fbvar.bits_per_pixel = 16;
+ fbvar.nonstd = IPU_PIX_FMT_UYVY;
+
+ fbvar.xres = fbvar.xres_virtual = out_width;
+ fbvar.yres = out_height;
+ fbvar.yres_virtual = out_height * 2;
+ } else if (vout->cur_disp_output == 5) {
+ vout->display_ch = MEM_DC_SYNC;
+ fbvar.bits_per_pixel = 16;
+ fbvar.nonstd = IPU_PIX_FMT_UYVY;
+
+ fbvar.xres = fbvar.xres_virtual = out_width;
+ fbvar.yres = out_height;
+ fbvar.yres_virtual = out_height * 2;
+ } else {
+ vout->display_ch = MEM_BG_SYNC;
+ }
+
+ fbvar.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbvar);
+
+ fb_pos.x = vout->crop_current.left;
+ fb_pos.y = vout->crop_current.top;
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
+ (unsigned long)&fb_pos);
+ set_fs(old_fs);
+ }
+
+ vout->display_bufs[1] = fbi->fix.smem_start;
+ vout->display_bufs[0] = fbi->fix.smem_start +
+ (fbi->fix.line_length * fbi->var.yres);
+ vout->display_buf_size = vout->crop_current.width *
+ vout->crop_current.height * fbi->var.bits_per_pixel / 8;
+
+ vout->post_proc_ch = MEM_PP_MEM;
+ }
+
+ /* Init PP */
+ if (use_direct_adc == false) {
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_width = vout->crop_current.height;
+ out_height = vout->crop_current.width;
+ }
+ memset(&params, 0, sizeof(params));
+ params.mem_pp_mem.in_width = vout->v2f.fmt.pix.width / 2;
+ params.mem_pp_mem.in_height = vout->v2f.fmt.pix.height;
+ params.mem_pp_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat;
+ params.mem_pp_mem.out_width = out_width / 2;
+ params.mem_pp_mem.out_height = out_height;
+ if (vout->display_ch == ADC_SYS2)
+ params.mem_pp_mem.out_pixel_fmt = SDC_FG_FB_FORMAT;
+ else
+ params.mem_pp_mem.out_pixel_fmt = bpp_to_fmt(fbi);
+ if (ipu_init_channel(vout->post_proc_ch, &params) != 0) {
+ dev_err(dev, "Error initializing PP channel\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ params.mem_pp_mem.in_pixel_fmt,
+ params.mem_pp_mem.in_width,
+ params.mem_pp_mem.in_height,
+ vout->v2f.fmt.pix.bytesperline /
+ bytes_per_pixel(params.mem_pp_mem.
+ in_pixel_fmt),
+ IPU_ROTATE_NONE,
+ vout->v4l2_bufs[pp_in_buf[0]].m.
+ offset,
+ vout->v4l2_bufs[pp_in_buf[0]].m.
+ offset + params.mem_pp_mem.in_width,
+ vout->offset.u_offset,
+ vout->offset.v_offset) != 0) {
+ dev_err(dev, "Error initializing PP input buffer\n");
+ return -EINVAL;
+ }
+
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ if (mxc_allocate_buffers
+ (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size) < 0)
+ return -ENOBUFS;
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ IPU_ROTATE_NONE,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing"
+ "PP output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel(MEM_ROT_PP_MEM, NULL) != 0) {
+ dev_err(dev,
+ "Error initializing PP ROT channel\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+ IPU_INPUT_BUFFER,
+ params.mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ vout->rotate,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP ROT"
+ "input buffer\n");
+ return -EINVAL;
+ }
+
+ /* swap width and height */
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_width = vout->crop_current.width;
+ out_height = vout->crop_current.height;
+ }
+
+ if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+ IPU_OUTPUT_BUFFER,
+ params.mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ IPU_ROTATE_NONE,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP"
+ "output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_link_channels(vout->post_proc_ch,
+ MEM_ROT_PP_MEM) < 0)
+ return -EINVAL;
+
+ ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 1);
+
+ ipu_enable_channel(MEM_ROT_PP_MEM);
+
+ display_input_ch = MEM_ROT_PP_MEM;
+ } else {
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_pp_mem.
+ out_pixel_fmt,
+ out_width / 2,
+ out_height,
+ out_width,
+ vout->rotate,
+ vout->display_bufs[0],
+ vout->display_bufs[0]
+ +
+ out_width / 2 *
+ bytes_per_pixel
+ (SDC_FG_FB_FORMAT), 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP"
+ "output buffer\n");
+ return -EINVAL;
+ }
+ }
+ if (ipu_unlink_channels(
+ display_input_ch, vout->display_ch) < 0) {
+ dev_err(dev, "Error linking ipu channels\n");
+ return -EINVAL;
+ }
+ }
+
+ vout->state = STATE_STREAM_PAUSED;
+
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+
+ if (use_direct_adc == false) {
+ ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0);
+ ipu_enable_channel(vout->post_proc_ch);
+
+ if (fbi) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_UNBLANK);
+ release_console_sem();
+ } else {
+ ipu_enable_channel(vout->display_ch);
+ }
+ } else {
+ ipu_enable_channel(vout->post_proc_ch);
+ }
+
+ vout->start_jiffies = jiffies;
+ dev_dbg(dev,
+ "streamon: start time = %lu jiffies\n", vout->start_jiffies);
+
+ return 0;
+}
+
+/*!
+ * Shut down the voutera
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamoff(vout_data *vout)
+{
+ struct fb_info *fbi =
+ registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ int i, retval = 0;
+ unsigned long lockflag = 0;
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state == STATE_STREAM_OFF)
+ return 0;
+
+ spin_lock_irqsave(&g_lock, lockflag);
+
+ del_timer(&vout->output_timer);
+
+ if (vout->state == STATE_STREAM_ON)
+ vout->state = STATE_STREAM_STOPPING;
+
+ ipu_disable_irq(IPU_IRQ_PP_IN_EOF);
+ ipu_disable_irq(IPU_IRQ_PP_OUT_EOF);
+
+ spin_unlock_irqrestore(&g_lock, lockflag);
+
+ if (vout->post_proc_ch == MEM_PP_MEM) { /* SDC or ADC with Rotation */
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ ipu_unlink_channels(MEM_PP_MEM, MEM_ROT_PP_MEM);
+ ipu_unlink_channels(MEM_ROT_PP_MEM, vout->display_ch);
+ ipu_disable_channel(MEM_ROT_PP_MEM, true);
+
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ } else {
+ ipu_unlink_channels(MEM_PP_MEM, vout->display_ch);
+ }
+ ipu_disable_channel(MEM_PP_MEM, true);
+
+ if (vout->display_ch == ADC_SYS2) {
+ ipu_disable_channel(vout->display_ch, true);
+ ipu_uninit_channel(vout->display_ch);
+ } else {
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbi->var);
+
+ if (vout->display_ch == MEM_FG_SYNC) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+ }
+
+ vout->display_bufs[0] = 0;
+ vout->display_bufs[1] = 0;
+ }
+
+ ipu_uninit_channel(MEM_PP_MEM);
+ if (!ipu_can_rotate_in_place(vout->rotate))
+ ipu_uninit_channel(MEM_ROT_PP_MEM);
+ } else { /* ADC Direct */
+ ipu_disable_channel(MEM_PP_ADC, true);
+ ipu_uninit_channel(MEM_PP_ADC);
+ }
+ vout->ready_q.head = vout->ready_q.tail = 0;
+ vout->done_q.head = vout->done_q.tail = 0;
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+
+ vout->state = STATE_STREAM_OFF;
+
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL
+ if (vout->cur_disp_output < DISP3) {
+ if (vout->display_bufs[0] != 0) {
+ mxc_free_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+
+ mxcfb_set_refresh_mode(registered_fb
+ [vout->
+ output_fb_num[vout->cur_disp_output]],
+ MXCFB_REFRESH_PARTIAL, 0);
+ }
+#endif
+
+ return retval;
+}
+
+/*
+ * Valid whether the palette is supported
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 1 if supported, 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ return ((palette == V4L2_PIX_FMT_RGB565) ||
+ (palette == V4L2_PIX_FMT_BGR24) ||
+ (palette == V4L2_PIX_FMT_RGB24) ||
+ (palette == V4L2_PIX_FMT_BGR32) ||
+ (palette == V4L2_PIX_FMT_RGB32) ||
+ (palette == V4L2_PIX_FMT_NV12) ||
+ (palette == V4L2_PIX_FMT_YUV422P) ||
+ (palette == V4L2_PIX_FMT_YUV420));
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_g_fmt(vout_data *vout, struct v4l2_format *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ *f = vout->v2f;
+ return 0;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_s_fmt(vout_data *vout, struct v4l2_format *f)
+{
+ int retval = 0;
+ u32 size = 0;
+ u32 bytesperline;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ goto err0;
+ }
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ dev_err(vout->video_dev->dev, "pixel format not supported\n");
+ retval = -EINVAL;
+ goto err0;
+ }
+
+ bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) /
+ 8;
+ if (f->fmt.pix.bytesperline < bytesperline) {
+ f->fmt.pix.bytesperline = bytesperline;
+ } else {
+ bytesperline = f->fmt.pix.bytesperline;
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV422P:
+ /* byteperline for YUV planar formats is for
+ Y plane only */
+ size = bytesperline * f->fmt.pix.height * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV12:
+ size = (bytesperline * f->fmt.pix.height * 3) / 2;
+ break;
+ default:
+ size = bytesperline * f->fmt.pix.height;
+ break;
+ }
+
+ /* Return the actual size of the image to the app */
+ if (f->fmt.pix.sizeimage < size)
+ f->fmt.pix.sizeimage = size;
+ else
+ size = f->fmt.pix.sizeimage;
+
+ vout->v2f.fmt.pix = f->fmt.pix;
+ if (vout->v2f.fmt.pix.priv != 0) {
+ if (copy_from_user(&vout->offset,
+ (void *)vout->v2f.fmt.pix.priv,
+ sizeof(vout->offset))) {
+ retval = -EFAULT;
+ goto err0;
+ }
+ } else {
+ vout->offset.u_offset = 0;
+ vout->offset.v_offset = 0;
+ }
+
+ retval = 0;
+err0:
+ return retval;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_get_v42lout_control(vout_data *vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ return (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0;
+ case V4L2_CID_VFLIP:
+ return (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0;
+ case (V4L2_CID_PRIVATE_BASE + 1):
+ return vout->rotate;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_set_v42lout_control(vout_data *vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ vout->rotate |= c->value ? IPU_ROTATE_HORIZ_FLIP :
+ IPU_ROTATE_NONE;
+ break;
+ case V4L2_CID_VFLIP:
+ vout->rotate |= c->value ? IPU_ROTATE_VERT_FLIP :
+ IPU_ROTATE_NONE;
+ break;
+ case V4L2_CID_MXC_ROT:
+ vout->rotate = c->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * V4L2 interface - open function
+ *
+ * @param inode structure inode *
+ *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l2out_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+ int err;
+
+ if (!vout)
+ return -ENODEV;
+
+ down(&vout->busy_lock);
+
+ err = -EINTR;
+ if (signal_pending(current))
+ goto oops;
+
+ if (vout->open_count++ == 0) {
+ ipu_request_irq(IPU_IRQ_PP_IN_EOF,
+ mxc_v4l2out_pp_in_irq_handler,
+ 0, dev->name, vout);
+ ipu_request_irq(IPU_IRQ_PP_OUT_EOF,
+ mxc_v4l2out_pp_out_irq_handler,
+ 0, dev->name, vout);
+
+ init_waitqueue_head(&vout->v4l_bufq);
+
+ init_timer(&vout->output_timer);
+ vout->output_timer.function = mxc_v4l2out_timer_handler;
+ vout->output_timer.data = (unsigned long)vout;
+
+ vout->state = STATE_STREAM_OFF;
+ vout->rotate = IPU_ROTATE_NONE;
+ g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0;
+
+ }
+
+ file->private_data = dev;
+
+ up(&vout->busy_lock);
+
+ return 0;
+
+oops:
+ up(&vout->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L2 interface - close function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int mxc_v4l2out_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+
+ if (--vout->open_count == 0) {
+ if (vout->state != STATE_STREAM_OFF)
+ mxc_v4l2out_streamoff(vout);
+
+ ipu_free_irq(IPU_IRQ_PP_IN_EOF, vout);
+ ipu_free_irq(IPU_IRQ_PP_OUT_EOF, vout);
+
+ file->private_data = NULL;
+
+ mxc_free_buffers(vout->queue_buf_paddr, vout->queue_buf_vaddr,
+ vout->buffer_cnt, vout->queue_buf_size);
+ vout->buffer_cnt = 0;
+ mxc_free_buffers(vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+
+ /* capture off */
+ wake_up_interruptible(&vout->v4l_bufq);
+ }
+
+ return 0;
+}
+
+/*!
+ * V4L2 interface - ioctl function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void *
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static int
+mxc_v4l2out_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ vout_data *vout = video_get_drvdata(vdev);
+ int retval = 0;
+ int i = 0;
+
+ if (!vout)
+ return -EBADF;
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+ strcpy(cap->driver, "mxc_v4l2_output");
+ cap->version = 0;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *gf = arg;
+ retval = mxc_v4l2out_g_fmt(vout, gf);
+ break;
+ }
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *sf = arg;
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+ retval = mxc_v4l2out_s_fmt(vout, sf);
+ break;
+ }
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *req = arg;
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ dev_dbg(vdev->dev,
+ "VIDIOC_REQBUFS: incorrect"
+ "buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ if (req->count == 0)
+ mxc_v4l2out_streamoff(vout);
+
+ if (vout->state == STATE_STREAM_OFF) {
+ if (vout->queue_buf_paddr[0] != 0) {
+ mxc_free_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ dev_dbg(vdev->dev,
+ "VIDIOC_REQBUFS:"
+ "freed buffers\n");
+ }
+ vout->buffer_cnt = 0;
+ } else {
+ dev_dbg(vdev->dev,
+ "VIDIOC_REQBUFS: Buffer is in use\n");
+ retval = -EBUSY;
+ break;
+ }
+
+ if (req->count == 0)
+ break;
+
+ if (req->count < MIN_FRAME_NUM)
+ req->count = MIN_FRAME_NUM;
+ else if (req->count > MAX_FRAME_NUM)
+ req->count = MAX_FRAME_NUM;
+ vout->buffer_cnt = req->count;
+ vout->queue_buf_size =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+
+ retval = mxc_allocate_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ if (retval < 0)
+ break;
+
+ /* Init buffer queues */
+ vout->done_q.head = 0;
+ vout->done_q.tail = 0;
+ vout->ready_q.head = 0;
+ vout->ready_q.tail = 0;
+
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ memset(&(vout->v4l2_bufs[i]), 0,
+ sizeof(vout->v4l2_bufs[i]));
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].memory = V4L2_MEMORY_MMAP;
+ vout->v4l2_bufs[i].index = i;
+ vout->v4l2_bufs[i].type =
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ vout->v4l2_bufs[i].length =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+ vout->v4l2_bufs[i].m.offset =
+ (unsigned long)vout->queue_buf_paddr[i];
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+ break;
+ }
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ u32 type = buf->type;
+ int index = buf->index;
+
+ if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt)) {
+ dev_dbg(vdev->dev,
+ "VIDIOC_QUERYBUFS: incorrect"
+ "buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+ down(&vout->param_lock);
+ memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf));
+ up(&vout->param_lock);
+ break;
+ }
+ case VIDIOC_QBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ unsigned long lock_flags;
+
+ if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ dev_dbg(vdev->dev, "VIDIOC_QBUF: %d\n", buf->index);
+
+ /* mmapped buffers are L1 WB cached,
+ * so we need to clean them */
+ if (buf->flags & V4L2_BUF_FLAG_MAPPED)
+ flush_cache_all();
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf));
+ vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED;
+
+ g_buf_q_cnt++;
+ queue_buf(&vout->ready_q, index);
+ if (vout->state == STATE_STREAM_PAUSED) {
+ unsigned long timeout;
+
+ index = peek_next_buf(&vout->ready_q);
+
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec ==
+ 0)
+ && (vout->v4l2_bufs[index].timestamp.
+ tv_usec == 0))
+ timeout =
+ vout->start_jiffies +
+ vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].
+ timestamp);
+
+ if (jiffies >= timeout) {
+ dev_dbg(vout->video_dev->dev,
+ "warning: timer timeout"
+ "already expired.\n");
+ }
+ vout->output_timer.expires = timeout;
+ dev_dbg(vdev->dev,
+ "QBUF: frame #%u timeout @"
+ " %lu jiffies, current = %lu\n",
+ vout->frame_count, timeout, jiffies);
+ add_timer(&vout->output_timer);
+ vout->state = STATE_STREAM_ON;
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ break;
+ }
+ case VIDIOC_DQBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int idx;
+
+ if ((queue_size(&vout->done_q) == 0) &&
+ (file->f_flags & O_NONBLOCK)) {
+ retval = -EAGAIN;
+ break;
+ }
+
+ if (!wait_event_interruptible_timeout(vout->v4l_bufq,
+ queue_size(&vout->
+ done_q)
+ != 0, 10 * HZ)) {
+ dev_dbg(vdev->dev, "VIDIOC_DQBUF: timeout\n");
+ retval = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ dev_dbg(vdev->dev,
+ "VIDIOC_DQBUF: interrupt received\n");
+ retval = -ERESTARTSYS;
+ break;
+ }
+ idx = dequeue_buf(&vout->done_q);
+ if (idx == -1) { /* No frame free */
+ dev_dbg(vdev->dev,
+ "VIDIOC_DQBUF: no free buffers\n");
+ retval = -EAGAIN;
+ break;
+ }
+ if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) ==
+ 0)
+ dev_dbg(vdev->dev,
+ "VIDIOC_DQBUF: buffer in done q, "
+ "but not flagged as done\n");
+
+ vout->v4l2_bufs[idx].flags = 0;
+ memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf));
+ dev_dbg(vdev->dev, "VIDIOC_DQBUF: %d\n", buf->index);
+ break;
+ }
+ case VIDIOC_STREAMON:
+ {
+ retval = mxc_v4l2out_streamon(vout);
+ break;
+ }
+ case VIDIOC_STREAMOFF:
+ {
+ retval = mxc_v4l2out_streamoff(vout);
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ retval = mxc_get_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ retval = mxc_set_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cap = arg;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+
+ cap->bounds = vout->crop_bounds[vout->cur_disp_output];
+ cap->defrect = vout->crop_bounds[vout->cur_disp_output];
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ crop->c = vout->crop_current;
+ break;
+ }
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b =
+ &(vout->crop_bounds[vout->cur_disp_output]);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.height < 0) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.width < 0) {
+ retval = -EINVAL;
+ break;
+ }
+
+ /* only full screen supported for SDC BG */
+ if (vout->cur_disp_output == 4) {
+ crop->c = vout->crop_current;
+ break;
+ }
+
+ if (crop->c.top < b->top)
+ crop->c.top = b->top;
+ if (crop->c.top >= b->top + b->height)
+ crop->c.top = b->top + b->height - 1;
+ if (crop->c.height > b->top - crop->c.top + b->height)
+ crop->c.height =
+ b->top - crop->c.top + b->height;
+
+ if (crop->c.left < b->left)
+ crop->c.left = b->left;
+ if (crop->c.left >= b->left + b->width)
+ crop->c.left = b->left + b->width - 1;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width =
+ b->left - crop->c.left + b->width;
+
+ /* stride line limitation */
+ crop->c.height -= crop->c.height % 8;
+ crop->c.width -= crop->c.width % 8;
+
+ vout->crop_current = crop->c;
+ break;
+ }
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *output = arg;
+
+ if ((output->index >= 5) ||
+ (vout->output_enabled[output->index] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (output->index < 3) {
+ *output = mxc_outputs[MXC_V4L2_OUT_2_ADC];
+ output->name[4] = '0' + output->index;
+ } else {
+ *output = mxc_outputs[MXC_V4L2_OUT_2_SDC];
+ }
+ break;
+ }
+ case VIDIOC_G_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ *p_output_num = vout->cur_disp_output;
+ break;
+ }
+ case VIDIOC_S_OUTPUT:
+ {
+ int *p_output_num = arg;
+ int fbnum;
+ struct v4l2_rect *b;
+
+ if ((*p_output_num >= MXC_V4L2_OUT_NUM_OUTPUTS) ||
+ (vout->output_enabled[*p_output_num] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+
+ vout->cur_disp_output = *p_output_num;
+
+ /* Update bounds in case they have changed */
+ b = &vout->crop_bounds[vout->cur_disp_output];
+
+ fbnum = vout->output_fb_num[vout->cur_disp_output];
+ if (vout->cur_disp_output == 3)
+ fbnum = vout->output_fb_num[4];
+
+ b->width = registered_fb[fbnum]->var.xres;
+ b->height = registered_fb[fbnum]->var.yres;
+
+ vout->crop_current = *b;
+ break;
+ }
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_G_PARM:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ default:
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&vout->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L2 interface - ioctl function
+ *
+ * @return None
+ */
+static int
+mxc_v4l2out_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, mxc_v4l2out_do_ioctl);
+}
+
+/*!
+ * V4L2 interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error,
+ * ENOBUFS remap_page error
+ */
+static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ unsigned long size = vma->vm_end - vma->vm_start;
+ int res = 0;
+ int i;
+ vout_data *vout = video_get_drvdata(vdev);
+
+ dev_dbg(vdev->dev, "pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ if ((vout->v4l2_bufs[i].m.offset ==
+ (vma->vm_pgoff << PAGE_SHIFT)) &&
+ (vout->v4l2_bufs[i].length >= size)) {
+ vout->v4l2_bufs[i].flags |= V4L2_BUF_FLAG_MAPPED;
+ break;
+ }
+ }
+ if (i == vout->buffer_cnt) {
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ /* make buffers inner write-back, outer write-thru cacheable */
+ vma->vm_page_prot = pgprot_outer_wrthru(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ dev_dbg(vdev->dev, "mmap remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+mxc_mmap_exit:
+ up(&vout->busy_lock);
+ return res;
+}
+
+/*!
+ * V4L2 interface - poll function
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_v4l2out_poll(struct file *file, poll_table * wait)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ queue = &vout->v4l_bufq;
+ poll_wait(file, queue, wait);
+
+ up(&vout->busy_lock);
+ return res;
+}
+
+static struct
+file_operations mxc_v4l2out_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_v4l2out_open,
+ .release = mxc_v4l2out_close,
+ .ioctl = mxc_v4l2out_ioctl,
+ .mmap = mxc_v4l2out_mmap,
+ .poll = mxc_v4l2out_poll,
+};
+
+static struct video_device mxc_v4l2out_template = {
+ .owner = THIS_MODULE,
+ .name = "MXC Video Output",
+ .type = 0,
+ .type2 = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING,
+ .fops = &mxc_v4l2out_fops,
+ .release = video_device_release,
+};
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxc_v4l2out_probe(struct platform_device *pdev)
+{
+ int i;
+ vout_data *vout;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ g_vout = vout = kmalloc(sizeof(vout_data), GFP_KERNEL);
+
+ if (!vout)
+ return 0;
+
+ memset(vout, 0, sizeof(vout_data));
+
+ vout->video_dev = video_device_alloc();
+ if (vout->video_dev == NULL)
+ return -1;
+ vout->video_dev->dev = &pdev->dev;
+ vout->video_dev->minor = -1;
+
+ *(vout->video_dev) = mxc_v4l2out_template;
+
+ /* register v4l device */
+ if (video_register_device(vout->video_dev,
+ VFL_TYPE_GRABBER, video_nr) == -1) {
+ dev_dbg(&pdev->dev, "video_register_device failed\n");
+ return 0;
+ }
+ dev_info(&pdev->dev, "Registered device video%d\n",
+ vout->video_dev->minor & 0x1f);
+ vout->video_dev->dev = &pdev->dev;
+
+ video_set_drvdata(vout->video_dev, vout);
+
+ init_MUTEX(&vout->param_lock);
+ init_MUTEX(&vout->busy_lock);
+
+ /* setup outputs and cropping */
+ vout->cur_disp_output = -1;
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strncmp(idstr, "DISP", 4) == 0) {
+ int disp_num = idstr[4] - '0';
+ if (disp_num == 3) {
+ if (strcmp(idstr, "DISP3 BG - DI1") == 0)
+ disp_num = 5;
+ else if (strncmp(idstr, "DISP3 BG", 8) == 0)
+ disp_num = 4;
+ }
+ vout->crop_bounds[disp_num].left = 0;
+ vout->crop_bounds[disp_num].top = 0;
+ vout->crop_bounds[disp_num].width =
+ registered_fb[i]->var.xres;
+ vout->crop_bounds[disp_num].height =
+ registered_fb[i]->var.yres;
+ vout->output_enabled[disp_num] = true;
+ vout->output_fb_num[disp_num] = i;
+ if (vout->cur_disp_output == -1)
+ vout->cur_disp_output = disp_num;
+ }
+
+ }
+ vout->crop_current = vout->crop_bounds[vout->cur_disp_output];
+
+ platform_set_drvdata(pdev, vout);
+
+ return 0;
+}
+
+static int mxc_v4l2out_remove(struct platform_device *pdev)
+{
+ vout_data *vout = platform_get_drvdata(pdev);
+
+ if (vout->video_dev) {
+ if (-1 != vout->video_dev->minor)
+ video_unregister_device(vout->video_dev);
+ else
+ video_device_release(vout->video_dev);
+ vout->video_dev = NULL;
+ }
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(vout);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2out_driver = {
+ .driver = {
+ .name = "MXC Video Output",
+ },
+ .probe = mxc_v4l2out_probe,
+ .remove = mxc_v4l2out_remove,
+};
+
+static struct platform_device mxc_v4l2out_device = {
+ .name = "MXC Video Output",
+ .id = 0,
+};
+
+/*!
+ * mxc v4l2 init function
+ *
+ */
+static int mxc_v4l2out_init(void)
+{
+ u8 err = 0;
+
+ err = platform_driver_register(&mxc_v4l2out_driver);
+ if (err == 0)
+ platform_device_register(&mxc_v4l2out_device);
+ return err;
+}
+
+/*!
+ * mxc v4l2 cleanup function
+ *
+ */
+static void mxc_v4l2out_clean(void)
+{
+ video_unregister_device(g_vout->video_dev);
+
+ platform_driver_unregister(&mxc_v4l2out_driver);
+ platform_device_unregister(&mxc_v4l2out_device);
+ kfree(g_vout);
+ g_vout = NULL;
+}
+
+module_init(mxc_v4l2out_init);
+module_exit(mxc_v4l2out_clean);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2-driver for MXC video output");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.c b/drivers/media/video/mxc/output/mxc_v4l2_output.c
new file mode 100644
index 000000000000..9254db5980c2
--- /dev/null
+++ b/drivers/media/video/mxc/output/mxc_v4l2_output.c
@@ -0,0 +1,2602 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file drivers/media/video/mxc/output/mxc_v4l2_output.c
+ *
+ * @brief MXC V4L2 Video Output Driver
+ *
+ * Video4Linux2 Output Device using MXC IPU Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/console.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mxcfb.h>
+#include <media/v4l2-ioctl.h>
+#include <asm/cacheflush.h>
+
+#include "mxc_v4l2_output.h"
+
+vout_data *g_vout;
+#define INTERLACED_CONTENT(vout) ((cpu_is_mx51_rev(CHIP_REV_2_0) >= 1) && \
+ (((vout)->field_fmt == V4L2_FIELD_INTERLACED_TB) || \
+ ((vout)->field_fmt == V4L2_FIELD_INTERLACED_BT)))
+#define LOAD_3FIELDS(vout) ((INTERLACED_CONTENT(vout)) && \
+ ((vout)->motion_sel != HIGH_MOTION))
+
+#define SDC_FG_FB_FORMAT IPU_PIX_FMT_RGB565
+
+struct v4l2_output mxc_outputs[2] = {
+ {
+ .index = MXC_V4L2_OUT_2_SDC,
+ .name = "DISP3 Video Out",
+ .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct,
+ but no other choice */
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN},
+ {
+ .index = MXC_V4L2_OUT_2_ADC,
+ .name = "DISPx Video Out",
+ .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct,
+ but no other choice */
+ .audioset = 0,
+ .modulator = 0,
+ .std = V4L2_STD_UNKNOWN}
+};
+
+static int video_nr = 16;
+static int pending_buffer;
+static int pp_eof;
+static spinlock_t g_lock = SPIN_LOCK_UNLOCKED;
+static int last_index_n;
+static int last_index_c;
+
+/* debug counters */
+uint32_t g_irq_cnt;
+uint32_t g_buf_output_cnt;
+uint32_t g_buf_q_cnt;
+uint32_t g_buf_dq_cnt;
+
+#define QUEUE_SIZE (MAX_FRAME_NUM + 1)
+static __inline int queue_size(v4l_queue * q)
+{
+ if (q->tail >= q->head)
+ return (q->tail - q->head);
+ else
+ return ((q->tail + QUEUE_SIZE) - q->head);
+}
+
+static __inline int queue_buf(v4l_queue * q, int idx)
+{
+ if (((q->tail + 1) % QUEUE_SIZE) == q->head)
+ return -1; /* queue full */
+ q->list[q->tail] = idx;
+ q->tail = (q->tail + 1) % QUEUE_SIZE;
+ return 0;
+}
+
+static __inline int dequeue_buf(v4l_queue * q)
+{
+ int ret;
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ ret = q->list[q->head];
+ q->head = (q->head + 1) % QUEUE_SIZE;
+ return ret;
+}
+
+static __inline int peek_next_buf(v4l_queue * q)
+{
+ if (q->tail == q->head)
+ return -1; /* queue empty */
+ return q->list[q->head];
+}
+
+static __inline unsigned long get_jiffies(struct timeval *t)
+{
+ struct timeval cur;
+
+ if (t->tv_usec >= 1000000) {
+ t->tv_sec += t->tv_usec / 1000000;
+ t->tv_usec = t->tv_usec % 1000000;
+ }
+
+ do_gettimeofday(&cur);
+ if ((t->tv_sec < cur.tv_sec)
+ || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec)))
+ return jiffies;
+
+ if (t->tv_usec < cur.tv_usec) {
+ cur.tv_sec = t->tv_sec - cur.tv_sec - 1;
+ cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec;
+ } else {
+ cur.tv_sec = t->tv_sec - cur.tv_sec;
+ cur.tv_usec = t->tv_usec - cur.tv_usec;
+ }
+
+ return jiffies + timeval_to_jiffies(&cur);
+}
+
+/*!
+ * Private function to free buffers
+ *
+ * @param bufs_paddr Array of physical address of buffers to be freed
+ *
+ * @param bufs_vaddr Array of virtual address of buffers to be freed
+ *
+ * @param num_buf Number of buffers to be freed
+ *
+ * @param size Size for each buffer to be free
+ *
+ * @return status 0 success.
+ */
+static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ if (bufs_vaddr[i] != 0) {
+ dma_free_coherent(0, size, bufs_vaddr[i],
+ bufs_paddr[i]);
+ pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]);
+ bufs_paddr[i] = 0;
+ bufs_vaddr[i] = NULL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Private function to allocate buffers
+ *
+ * @param bufs_paddr Output array of physical address of buffers allocated
+ *
+ * @param bufs_vaddr Output array of virtual address of buffers allocated
+ *
+ * @param num_buf Input number of buffers to allocate
+ *
+ * @param size Input size for each buffer to allocate
+ *
+ * @return status -0 Successfully allocated a buffer, -ENOBUFS failed.
+ */
+static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[],
+ int num_buf, int size)
+{
+ int i;
+
+ for (i = 0; i < num_buf; i++) {
+ bufs_vaddr[i] = dma_alloc_coherent(0, size,
+ &bufs_paddr[i],
+ GFP_DMA | GFP_KERNEL);
+
+ if (bufs_vaddr[i] == 0) {
+ mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size);
+ printk(KERN_ERR "dma_alloc_coherent failed.\n");
+ return -ENOBUFS;
+ }
+ pr_debug("allocated @ paddr=0x%08X, size=%d.\n",
+ (u32) bufs_paddr[i], size);
+ }
+
+ return 0;
+}
+
+/*
+ * Returns bits per pixel for given pixel format
+ *
+ * @param pixelformat V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return bits per pixel of pixelformat
+ */
+static u32 fmt_to_bpp(u32 pixelformat)
+{
+ u32 bpp;
+
+ bpp = 8 * bytes_per_pixel(pixelformat);
+ return bpp;
+}
+
+
+
+static bool format_is_yuv(u32 pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_NV12:
+ return true;
+ break;
+ }
+ return false;
+}
+
+static u32 bpp_to_fmt(struct fb_info *fbi)
+{
+ if (fbi->var.nonstd)
+ return fbi->var.nonstd;
+
+ if (fbi->var.bits_per_pixel == 24)
+ return V4L2_PIX_FMT_BGR24;
+ else if (fbi->var.bits_per_pixel == 32)
+ return V4L2_PIX_FMT_BGR32;
+ else if (fbi->var.bits_per_pixel == 16)
+ return V4L2_PIX_FMT_RGB565;
+
+ return 0;
+}
+
+static irqreturn_t mxc_v4l2out_disp_refresh_irq_handler(int irq, void *dev_id)
+{
+ vout_data *vout = dev_id;
+ int index, last_buf, ret;
+ unsigned long timeout;
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ g_irq_cnt++;
+
+ if (vout->ic_bypass && (pending_buffer || vout->frame_count < 3)) {
+ last_buf = vout->ipu_buf[vout->next_done_ipu_buf];
+ if (last_buf != -1) {
+ g_buf_output_cnt++;
+ vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE;
+ queue_buf(&vout->done_q, last_buf);
+ vout->ipu_buf[vout->next_done_ipu_buf] = -1;
+ wake_up_interruptible(&vout->v4l_bufq);
+ vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
+ }
+ }
+
+ if ((pending_buffer) && (pp_eof || vout->ic_bypass)) {
+ pp_eof = 0;
+ if (vout->ic_bypass) {
+ ret = ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf);
+ } else {
+ if (LOAD_3FIELDS(vout)) {
+ ret = ipu_select_multi_vdi_buffer(vout->next_rdy_ipu_buf);
+ } else {
+ ret = ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf);
+ }
+ }
+ if (ret < 0) {
+ dev_err(&vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ }
+ /* Non IC split action */
+ if (!vout->pp_split)
+ vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
+
+ pending_buffer = 0;
+
+ /* Setup timer for next buffer */
+ index = peek_next_buf(&vout->ready_q);
+ if (index != -1) {
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0)
+ && vout->start_jiffies)
+ timeout =
+ vout->start_jiffies + vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ dev_dbg(&vout->video_dev->dev,
+ "warning: timer timeout already expired.\n");
+ }
+ if (mod_timer(&vout->output_timer, timeout))
+ dev_dbg(&vout->video_dev->dev,
+ "warning: timer was already set\n");
+
+ dev_dbg(&vout->video_dev->dev,
+ "timer handler next schedule: %lu\n", timeout);
+ } else {
+ vout->state = STATE_STREAM_PAUSED;
+ }
+ }
+
+ if (vout->state == STATE_STREAM_STOPPING) {
+ if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) {
+ vout->state = STATE_STREAM_OFF;
+ }
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+
+ return IRQ_HANDLED;
+}
+
+static int get_display_irq(vout_data *vout)
+{
+
+ int disp_irq = 0;
+
+ switch (vout->display_ch) {
+ case MEM_FG_SYNC:
+ case MEM_BG_SYNC:
+ disp_irq = IPU_IRQ_BG_SF_END;
+ break;
+ case MEM_DC_SYNC:
+ disp_irq = IPU_IRQ_DC_FC_1;
+ break;
+ default:
+ dev_err(&vout->video_dev->dev,
+ "not support display channel\n");
+ }
+
+ return disp_irq;
+}
+
+static void mxc_v4l2out_timer_handler(unsigned long arg)
+{
+ int index, ret;
+ unsigned long lock_flags = 0;
+ vout_data *vout = (vout_data *) arg;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ if ((vout->state == STATE_STREAM_STOPPING)
+ || (vout->state == STATE_STREAM_OFF))
+ goto exit0;
+ /*
+ * If timer occurs before IPU h/w is ready, then set the state to
+ * paused and the timer will be set again when next buffer is queued
+ * or PP comletes
+ */
+ if (vout->ipu_buf[vout->next_rdy_ipu_buf] != -1) {
+ dev_dbg(&vout->video_dev->dev, "IPU buffer busy\n");
+ vout->state = STATE_STREAM_PAUSED;
+ goto exit0;
+ }
+
+ /* Dequeue buffer and pass to IPU */
+ unsigned int aid_field_offset, current_field_offset;
+ if (INTERLACED_CONTENT(vout)) {
+ if (((LOAD_3FIELDS(vout)) && (vout->next_rdy_ipu_buf)) ||
+ ((!LOAD_3FIELDS(vout)) && !(vout->next_rdy_ipu_buf))) {
+ aid_field_offset = vout->bytesperline;
+ current_field_offset = 0;
+ index = last_index_n;
+ } else {
+ aid_field_offset = 0;
+ current_field_offset = vout->bytesperline;
+ index = dequeue_buf(&vout->ready_q);
+ if (index == -1) { /* no buffers ready, should never occur */
+ dev_err(&vout->video_dev->dev,
+ "mxc_v4l2out: timer - no queued buffers ready\n");
+ goto exit0;
+ }
+ g_buf_dq_cnt++;
+ vout->frame_count++;
+ last_index_n = index;
+ }
+ } else {
+ current_field_offset = 0;
+ index = dequeue_buf(&vout->ready_q);
+ if (index == -1) { /* no buffers ready, should never occur */
+ dev_err(&vout->video_dev->dev,
+ "mxc_v4l2out: timer - no queued buffers ready\n");
+ goto exit0;
+ }
+ g_buf_dq_cnt++;
+ vout->frame_count++;
+ }
+
+ if (vout->ic_bypass) {
+ vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
+ ret = ipu_update_channel_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index].m.offset);
+ } else {
+ if (LOAD_3FIELDS(vout)) {
+ int index_n = index;
+ int index_p = last_index_c;
+ index = last_index_n;
+ vout->ipu_buf_p[vout->next_rdy_ipu_buf] = index_p;
+ vout->ipu_buf[vout->next_rdy_ipu_buf] = last_index_c = index;
+ vout->ipu_buf_n[vout->next_rdy_ipu_buf] = last_index_n = index_n;
+ last_index_n = vout->ipu_buf_n[vout->next_rdy_ipu_buf];
+ last_index_c = vout->ipu_buf[vout->next_rdy_ipu_buf];
+ ret = ipu_update_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index].m.offset+current_field_offset);
+ ret += ipu_update_channel_buffer(MEM_VDI_PRP_VF_MEM_P,
+ IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index_p].m.offset+aid_field_offset);
+ ret += ipu_update_channel_buffer(MEM_VDI_PRP_VF_MEM_N,
+ IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index_n].m.offset+aid_field_offset);
+ } else {
+ vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
+ if (vout->pp_split) {
+ vout->ipu_buf[!vout->next_rdy_ipu_buf] = index;
+ /* always left stripe */
+ ret = ipu_update_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ 0,/* vout->next_rdy_ipu_buf,*/
+ (vout->v4l2_bufs[index].m.offset) +
+ vout->pp_left_stripe.input_column +
+ current_field_offset);
+
+ /* the U/V offset has to be updated inside of IDMAC */
+ /* according to stripe offset */
+ ret += ipu_update_channel_offset(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ vout->v2f.fmt.pix.pixelformat,
+ vout->v2f.fmt.pix.width,
+ vout->v2f.fmt.pix.height,
+ vout->v2f.fmt.pix.width,
+ vout->offset.u_offset,
+ vout->offset.v_offset,
+ 0,
+ vout->pp_left_stripe.input_column + current_field_offset);
+
+ } else
+ ret = ipu_update_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index].m.offset +
+ current_field_offset);
+ }
+ }
+ if (ret < 0) {
+ dev_err(&vout->video_dev->dev,
+ "unable to update buffer %d address rc=%d\n",
+ vout->next_rdy_ipu_buf, ret);
+ goto exit0;
+ }
+
+ pending_buffer = 1;
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+
+ return;
+
+ exit0:
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+}
+
+static irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id)
+{
+ int last_buf;
+ int index;
+ unsigned long timeout;
+ unsigned long lock_flags = 0;
+ vout_data *vout = dev_id;
+ int pp_out_buf_num = 0;
+ int disp_buf_num = 0;
+ int disp_buf_num_next = 1;
+ int pp_out_buf_offset = 0;
+ int release_buffer = 1;
+ int eba_offset;
+ int ret = -1;
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ g_irq_cnt++;
+
+ /* Process previous buffer */
+ if (LOAD_3FIELDS(vout))
+ last_buf = vout->ipu_buf_p[vout->next_done_ipu_buf];
+ else
+ last_buf = vout->ipu_buf[vout->next_done_ipu_buf];
+
+ /* If IC split mode on, update output buffer number */
+ if (last_buf != -1) {
+ if (vout->pp_split) {
+ pp_out_buf_num = vout->pp_split_buf_num & 1;/* left/right stripe */
+ disp_buf_num = vout->pp_split_buf_num >> 1;
+ disp_buf_num_next = ((vout->pp_split_buf_num+2) & 3) >> 1;
+ if (!pp_out_buf_num) {/* next buffer is right stripe*/
+ eba_offset = vout->pp_right_stripe.input_column;/*always right stripe*/
+ ret = ipu_update_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ 1, /* right stripe */
+ (vout->v4l2_bufs[vout->ipu_buf[disp_buf_num]].m.offset)
+ + eba_offset);
+
+ ret += ipu_update_channel_offset(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ vout->v2f.fmt.pix.pixelformat,
+ vout->v2f.fmt.pix.width,
+ vout->v2f.fmt.pix.height,
+ vout->v2f.fmt.pix.width,
+ vout->offset.u_offset,
+ vout->offset.v_offset,
+ 0,
+ vout->pp_right_stripe.input_column);
+
+ /* select right stripe */
+ ret += ipu_select_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER, 1);
+ if (ret < 0)
+ dev_err(&vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+
+ vout->ipu_buf[vout->next_done_ipu_buf] = -1;
+ vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
+
+ } else /* right stripe is done, run display refresh */
+ ret = ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+ disp_buf_num);
+
+ vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
+
+ /* offset for next buffer's EBA */
+ pp_out_buf_offset = pp_out_buf_num ? vout->pp_right_stripe.output_column :
+ vout->pp_left_stripe.output_column;
+
+ /* next buffer update */
+ eba_offset = vout->display_bufs[disp_buf_num_next] +
+ pp_out_buf_offset;
+
+ ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+ pp_out_buf_num, eba_offset);
+
+ /* next buffer ready */
+ ret = ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, pp_out_buf_num);
+
+ /* next stripe_buffer index 0..3 */
+ vout->pp_split_buf_num = (vout->pp_split_buf_num + 1) & 3;
+ }
+
+ /* release buffer if second stripe is done */
+ release_buffer = vout->pp_split ? pp_out_buf_num : 1;
+ if (release_buffer) {
+ if ((!INTERLACED_CONTENT(vout)) || (vout->next_done_ipu_buf)) {
+ g_buf_output_cnt++;
+ vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE;
+ queue_buf(&vout->done_q, last_buf);
+ wake_up_interruptible(&vout->v4l_bufq);
+ }
+ vout->ipu_buf[vout->next_done_ipu_buf] = -1;
+ if (LOAD_3FIELDS(vout)) {
+ vout->ipu_buf_p[vout->next_done_ipu_buf] = -1;
+ vout->ipu_buf_n[vout->next_done_ipu_buf] = -1;
+ }
+ vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
+ }
+ } /* end of last_buf != -1 */
+
+ if (release_buffer)
+ pp_eof = 1;
+
+ if (vout->state == STATE_STREAM_STOPPING) {
+ if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) {
+ vout->state = STATE_STREAM_OFF;
+ }
+ } else if ((vout->state == STATE_STREAM_PAUSED)
+ && ((index = peek_next_buf(&vout->ready_q)) != -1)) {
+ /* Setup timer for next buffer, when stream has been paused */
+ pr_debug("next index %d\n", index);
+
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0))
+ timeout =
+ vout->start_jiffies + vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ pr_debug("warning: timer timeout already expired.\n");
+ }
+
+ vout->state = STATE_STREAM_ON;
+
+ if (mod_timer(&vout->output_timer, timeout))
+ pr_debug("warning: timer was already set\n");
+
+ pr_debug("timer handler next schedule: %lu\n", timeout);
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * Initialize VDI channels
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int init_VDI_channel(vout_data *vout, ipu_channel_params_t params)
+{
+ struct device *dev = &vout->video_dev->dev;
+
+ if (ipu_init_channel(MEM_VDI_PRP_VF_MEM, &params) != 0) {
+ dev_dbg(dev, "Error initializing VDI current channel\n");
+ return -EINVAL;
+ }
+ if (LOAD_3FIELDS(vout)) {
+ if (ipu_init_channel(MEM_VDI_PRP_VF_MEM_P, &params) != 0) {
+ dev_err(dev, "Error initializing VDI previous channel\n");
+ return -EINVAL;
+ }
+ if (ipu_init_channel(MEM_VDI_PRP_VF_MEM_N, &params) != 0) {
+ dev_err(dev, "Error initializing VDI next channel\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Initialize VDI channel buffers
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int init_VDI_in_channel_buffer(vout_data *vout, uint32_t in_pixel_fmt,
+ uint16_t in_width, uint16_t in_height,
+ uint32_t stride,
+ dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+ uint32_t u_offset, uint32_t v_offset)
+{
+ struct device *dev = &vout->video_dev->dev;
+
+ if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER,
+ in_pixel_fmt, in_width, in_height, stride,
+ IPU_ROTATE_NONE,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset+vout->bytesperline,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+ u_offset, v_offset) != 0) {
+ dev_err(dev, "Error initializing VDI current input buffer\n");
+ return -EINVAL;
+ }
+ if (LOAD_3FIELDS(vout)) {
+ if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM_P,
+ IPU_INPUT_BUFFER,
+ in_pixel_fmt, in_width, in_height,
+ stride, IPU_ROTATE_NONE,
+ vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset,
+ vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset+vout->bytesperline,
+ u_offset, v_offset) != 0) {
+ dev_err(dev, "Error initializing VDI previous input buffer\n");
+ return -EINVAL;
+ }
+ if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM_N,
+ IPU_INPUT_BUFFER,
+ in_pixel_fmt, in_width, in_height,
+ stride, IPU_ROTATE_NONE,
+ vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset,
+ vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset+vout->bytesperline,
+ u_offset, v_offset) != 0) {
+ dev_err(dev, "Error initializing VDI next input buffer\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Initialize VDI path
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int init_VDI(ipu_channel_params_t params, vout_data *vout,
+ struct device *dev, struct fb_info *fbi,
+ ipu_channel_t *display_input_ch, u16 out_width,
+ u16 out_height)
+{
+ params.mem_prp_vf_mem.in_width = vout->v2f.fmt.pix.width;
+ params.mem_prp_vf_mem.in_height = vout->v2f.fmt.pix.height;
+ params.mem_prp_vf_mem.motion_sel = vout->motion_sel;
+ params.mem_prp_vf_mem.field_fmt = vout->field_fmt;
+ params.mem_prp_vf_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat;
+ params.mem_prp_vf_mem.out_width = out_width;
+ params.mem_prp_vf_mem.out_height = out_height;
+ if (vout->display_ch == ADC_SYS2)
+ params.mem_prp_vf_mem.out_pixel_fmt = SDC_FG_FB_FORMAT;
+ else
+ params.mem_prp_vf_mem.out_pixel_fmt = bpp_to_fmt(fbi);
+
+ if (init_VDI_channel(vout, params) != 0) {
+ dev_err(dev, "Error init_VDI_channel channel\n");
+ return -EINVAL;
+ }
+
+
+ if (init_VDI_in_channel_buffer(vout,
+ params.mem_prp_vf_mem.in_pixel_fmt,
+ params.mem_prp_vf_mem.in_width,
+ params.mem_prp_vf_mem.in_height,
+ bytes_per_pixel(params.mem_prp_vf_mem.
+ in_pixel_fmt),
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+ vout->v4l2_bufs[vout->ipu_buf[1]].m.offset,
+ vout->offset.u_offset,
+ vout->offset.v_offset) != 0) {
+ return -EINVAL;
+ }
+
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ if (mxc_allocate_buffers
+ (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size) < 0) {
+ return -ENOBUFS;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_prp_vf_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ IPU_ROTATE_NONE,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0, 0) != 0) {
+ dev_err(dev, "Error initializing PRP output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel(MEM_ROT_VF_MEM, NULL) != 0) {
+ dev_err(dev, "Error initializing PP ROT channel\n");
+ return -EINVAL;
+ }
+ if (ipu_init_channel_buffer(MEM_ROT_VF_MEM,
+ IPU_INPUT_BUFFER,
+ params.mem_prp_vf_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ vout->rotate,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0, 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP ROT input buffer\n");
+ return -EINVAL;
+ }
+
+ /* swap width and height */
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_width = vout->crop_current.width;
+ out_height = vout->crop_current.height;
+ }
+
+ if (ipu_init_channel_buffer(MEM_ROT_VF_MEM,
+ IPU_OUTPUT_BUFFER,
+ params.mem_prp_vf_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ IPU_ROTATE_NONE,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0, 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP-VDI output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_link_channels(vout->post_proc_ch, MEM_ROT_VF_MEM) < 0)
+ return -EINVAL;
+
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 1);
+
+ ipu_enable_channel(MEM_ROT_VF_MEM);
+ *display_input_ch = MEM_ROT_VF_MEM;
+
+ } else {
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_prp_vf_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ vout->rotate,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0, 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP-VDI output buffer\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * Initialize PP path
+ *
+ * @param params structure ipu_channel_params_t
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int init_PP(ipu_channel_params_t *params, vout_data *vout,
+ struct device *dev, struct fb_info *fbi,
+ ipu_channel_t *display_input_ch, u16 out_width,
+ u16 out_height)
+{
+ u16 in_width, out_stride; /* stride of output channel */
+ unsigned int ipu_ic_out_max_width_size;
+
+ if (vout->display_ch == ADC_SYS2)
+ params->mem_pp_mem.out_pixel_fmt = SDC_FG_FB_FORMAT;
+ else
+ params->mem_pp_mem.out_pixel_fmt = bpp_to_fmt(fbi);
+
+ out_stride = out_width;
+ in_width = params->mem_pp_mem.in_width = vout->v2f.fmt.pix.width;
+ params->mem_pp_mem.in_height = vout->v2f.fmt.pix.height;
+ params->mem_pp_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat;
+ params->mem_pp_mem.out_width = out_width;
+ params->mem_pp_mem.out_height = out_height;
+ params->mem_pp_mem.out_resize_ratio = 0; /* 0 means unused */
+
+#ifdef CONFIG_MXC_IPU_V1
+ ipu_ic_out_max_width_size = 800;
+#else
+ ipu_ic_out_max_width_size = 1024;
+#endif
+ /* split IC by two stripes, the by pass is impossible*/
+ if (out_width > ipu_ic_out_max_width_size) {
+ vout->pp_split = 1;
+ out_stride = 2*out_width;
+
+
+ ipu_calc_stripes_sizes(
+ params->mem_pp_mem.in_width, /* input frame width;>1 */
+ params->mem_pp_mem.out_width, /* output frame width; >1 */
+ ipu_ic_out_max_width_size,
+ (((unsigned long long)1) << 32), /* 32bit for fractional*/
+ 1, /* equal stripes */
+ params->mem_pp_mem.in_pixel_fmt,
+ params->mem_pp_mem.out_pixel_fmt,
+ &(vout->pp_left_stripe),
+ &(vout->pp_right_stripe));
+
+
+ vout->pp_left_stripe.input_column = vout->pp_left_stripe.input_column *
+ fmt_to_bpp(vout->v2f.fmt.pix.pixelformat) / 8;
+ vout->pp_left_stripe.output_column = vout->pp_left_stripe.output_column *
+ fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt) / 8;
+ vout->pp_right_stripe.input_column = vout->pp_right_stripe.input_column *
+ fmt_to_bpp(vout->v2f.fmt.pix.pixelformat) / 8;
+
+
+ vout->pp_right_stripe.output_column = vout->pp_right_stripe.output_column *
+ fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt) / 8;
+
+
+
+ /* updare parameters */
+ params->mem_pp_mem.in_width = vout->pp_left_stripe.input_width;
+ params->mem_pp_mem.out_width = vout->pp_left_stripe.output_width;
+ out_width = vout->pp_left_stripe.output_width;
+ /* for using in ic_init*/
+ params->mem_pp_mem.out_resize_ratio = vout->pp_left_stripe.irr;
+
+ vout->pp_split_buf_num = 0;
+ } else
+ vout->pp_split = 0;
+
+
+ if (ipu_init_channel(vout->post_proc_ch, params) != 0) {
+ dev_err(dev, "Error initializing PP channel\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ params->mem_pp_mem.in_pixel_fmt,
+ params->mem_pp_mem.in_width,
+ params->mem_pp_mem.in_height,
+ vout->v2f.fmt.pix.bytesperline /
+ bytes_per_pixel(params->mem_pp_mem.
+ in_pixel_fmt),
+ IPU_ROTATE_NONE,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+ vout->v4l2_bufs[vout->ipu_buf[1]].m.offset,
+ vout->offset.u_offset,
+ vout->offset.v_offset) != 0) {
+ dev_err(dev, "Error initializing PP input buffer\n");
+ return -EINVAL;
+ }
+
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ if (mxc_allocate_buffers
+ (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size) < 0) {
+ return -ENOBUFS;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params->mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_stride,
+ IPU_ROTATE_NONE,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0, 0) != 0) {
+ dev_err(dev, "Error initializing PP output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel(MEM_ROT_PP_MEM, NULL) != 0) {
+ dev_err(dev, "Error initializing PP ROT channel\n");
+ return -EINVAL;
+ }
+ if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+ IPU_INPUT_BUFFER,
+ params->mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_stride,
+ vout->rotate,
+ vout->rot_pp_bufs[0],
+ vout->rot_pp_bufs[1], 0, 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP ROT input buffer\n");
+ return -EINVAL;
+ }
+
+ /* swap width and height */
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_stride = out_width = vout->crop_current.width;
+ out_height = vout->crop_current.height;
+ }
+
+ if (ipu_init_channel_buffer(MEM_ROT_PP_MEM,
+ IPU_OUTPUT_BUFFER,
+ params->mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_stride,
+ IPU_ROTATE_NONE,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0, 0) != 0) {
+ dev_err(dev, "Error initializing PP output buffer\n");
+ return -EINVAL;
+ }
+
+ if (ipu_link_channels(vout->post_proc_ch, MEM_ROT_PP_MEM) < 0)
+ return -EINVAL;
+
+ ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 1);
+
+ ipu_enable_channel(MEM_ROT_PP_MEM);
+ *display_input_ch = MEM_ROT_PP_MEM;
+
+ } else {
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params->mem_pp_mem.
+ out_pixel_fmt, out_width,
+ out_height, out_stride,
+ vout->rotate,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0, 0) != 0) {
+ dev_err(dev, "Error initializing PP output buffer\n");
+ return -EINVAL;
+ }
+ }
+
+ /* fix EBAs for IDMAC channels */
+ if (vout->pp_split) {
+ ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ 0,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset +
+ vout->pp_left_stripe.input_column);
+ ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ 1,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset +
+ vout->pp_right_stripe.input_column);
+ ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+ 0,
+ vout->display_bufs[0] +
+ vout->pp_left_stripe.output_column);
+
+ ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER,
+ 1,
+ vout->display_bufs[0] +
+ vout->pp_right_stripe.output_column);
+ }
+
+ return 0;
+}
+
+/*!
+ * Start the output stream
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamon(vout_data * vout)
+{
+ struct device *dev = &vout->video_dev->dev;
+ ipu_channel_params_t params;
+ struct mxcfb_pos fb_pos;
+ struct fb_var_screeninfo fbvar;
+ struct fb_info *fbi =
+ registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ u16 out_width;
+ u16 out_height;
+ int disp_irq = 0;
+ ipu_channel_t display_input_ch;
+ bool use_direct_adc = false;
+ mm_segment_t old_fs;
+
+ dev_dbg(dev, "mxc_v4l2out_streamon: field format=%d\n",
+ vout->field_fmt);
+ if (INTERLACED_CONTENT(vout)) {
+ ipu_request_irq(IPU_IRQ_PRP_VF_OUT_EOF,
+ mxc_v4l2out_pp_in_irq_handler,
+ 0, &vout->video_dev->name, vout);
+ display_input_ch = MEM_VDI_PRP_VF_MEM;
+ } else {
+ ipu_request_irq(IPU_IRQ_PP_IN_EOF,
+ mxc_v4l2out_pp_in_irq_handler,
+ 0, &vout->video_dev->name, vout);
+ display_input_ch = MEM_PP_MEM;
+ }
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state != STATE_STREAM_OFF)
+ return -EBUSY;
+
+ if (queue_size(&vout->ready_q) < 2) {
+ dev_err(dev, "2 buffers not been queued yet!\n");
+ return -EINVAL;
+ }
+
+ if ((vout->field_fmt == V4L2_FIELD_BOTTOM) || (vout->field_fmt == V4L2_FIELD_TOP)) {
+ dev_err(dev, "4 queued buffers need, not supported yet!\n");
+ return -EINVAL;
+ }
+
+ pending_buffer = 0;
+
+ out_width = vout->crop_current.width;
+ out_height = vout->crop_current.height;
+ vout->next_done_ipu_buf = 0;
+ vout->next_rdy_ipu_buf = 1;
+
+ if (!INTERLACED_CONTENT(vout)) {
+ vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0;
+ vout->ipu_buf[0] = dequeue_buf(&vout->ready_q);
+ if (out_width != vout->v2f.fmt.pix.width && /*pp_split*/
+ out_height != vout->v2f.fmt.pix.height &&
+ out_width > 1024) {
+ vout->ipu_buf[1] = vout->ipu_buf[0];
+ vout->frame_count = 1;
+ } else {
+ vout->ipu_buf[1] = dequeue_buf(&vout->ready_q);
+ vout->frame_count = 2;
+ }
+ } else if (!LOAD_3FIELDS(vout)) {
+ vout->ipu_buf[0] = dequeue_buf(&vout->ready_q);
+ vout->ipu_buf[1] = -1;
+ vout->frame_count = 1;
+ last_index_n = vout->ipu_buf[0];
+ } else {
+ vout->ipu_buf_p[0] = dequeue_buf(&vout->ready_q);
+ vout->ipu_buf[0] = vout->ipu_buf_p[0];
+ vout->ipu_buf_n[0] = dequeue_buf(&vout->ready_q);
+ vout->ipu_buf_p[1] = -1;
+ vout->ipu_buf[1] = -1;
+ vout->ipu_buf_n[1] = -1;
+ last_index_c = vout->ipu_buf[0];
+ last_index_n = vout->ipu_buf_n[0];
+ vout->frame_count = 2;
+ }
+
+ /* Init Display Channel */
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL
+ if (INTERLACED_CONTENT(vout))
+ ipu_enable_irq(IPU_IRQ_PRP_VF_OUT_EOF);
+ else
+ ipu_enable_irq(IPU_IRQ_PP_IN_EOF);
+
+ if (vout->cur_disp_output < DISP3) {
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, 0);
+ fbi = NULL;
+ if (ipu_can_rotate_in_place(vout->rotate)) {
+ dev_dbg(dev, "Using PP direct to ADC channel\n");
+ use_direct_adc = true;
+ vout->display_ch = MEM_PP_ADC;
+ vout->post_proc_ch = MEM_PP_ADC;
+
+ memset(&params, 0, sizeof(params));
+ params.mem_pp_adc.in_width = vout->v2f.fmt.pix.width;
+ params.mem_pp_adc.in_height = vout->v2f.fmt.pix.height;
+ params.mem_pp_adc.in_pixel_fmt =
+ vout->v2f.fmt.pix.pixelformat;
+ params.mem_pp_adc.out_width = out_width;
+ params.mem_pp_adc.out_height = out_height;
+ params.mem_pp_adc.out_pixel_fmt = SDC_FG_FB_FORMAT;
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ params.mem_pp_adc.out_left =
+ 2 + vout->crop_current.left;
+#else
+ params.mem_pp_adc.out_left =
+ 12 + vout->crop_current.left;
+#endif
+ params.mem_pp_adc.out_top = vout->crop_current.top;
+ if (ipu_init_channel(vout->post_proc_ch, &params) != 0) {
+ dev_err(dev, "Error initializing PP chan\n");
+ return -EINVAL;
+ }
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_INPUT_BUFFER,
+ params.mem_pp_adc.
+ in_pixel_fmt,
+ params.mem_pp_adc.in_width,
+ params.mem_pp_adc.in_height,
+ vout->v2f.fmt.pix.
+ bytesperline /
+ bytes_per_pixel(params.
+ mem_pp_adc.
+ in_pixel_fmt),
+ vout->rotate,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+ vout->v4l2_bufs[vout->ipu_buf[1]].m.offset,
+ vout->offset.u_offset,
+ vout->offset.v_offset) !=
+ 0) {
+ dev_err(dev, "Error initializing PP in buf\n");
+ return -EINVAL;
+ }
+
+ if (ipu_init_channel_buffer(vout->post_proc_ch,
+ IPU_OUTPUT_BUFFER,
+ params.mem_pp_adc.
+ out_pixel_fmt, out_width,
+ out_height, out_width,
+ vout->rotate, 0, 0, 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing PP output buffer\n");
+ return -EINVAL;
+ }
+
+ } else {
+ dev_dbg(dev, "Using ADC SYS2 channel\n");
+ vout->display_ch = ADC_SYS2;
+ vout->post_proc_ch = MEM_PP_MEM;
+
+ if (vout->display_bufs[0]) {
+ mxc_free_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+ }
+
+ vout->display_buf_size = vout->crop_current.width *
+ vout->crop_current.height *
+ fmt_to_bpp(SDC_FG_FB_FORMAT) / 8;
+ mxc_allocate_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr,
+ 2, vout->display_buf_size);
+
+ memset(&params, 0, sizeof(params));
+ params.adc_sys2.disp = vout->cur_disp_output;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+#ifdef CONFIG_FB_MXC_EPSON_PANEL
+ params.adc_sys2.out_left = 2 + vout->crop_current.left;
+#else
+ params.adc_sys2.out_left = 12 + vout->crop_current.left;
+#endif
+ params.adc_sys2.out_top = vout->crop_current.top;
+ if (ipu_init_channel(ADC_SYS2, &params) < 0)
+ return -EINVAL;
+
+ if (ipu_init_channel_buffer(vout->display_ch,
+ IPU_INPUT_BUFFER,
+ SDC_FG_FB_FORMAT,
+ out_width, out_height,
+ out_width, IPU_ROTATE_NONE,
+ vout->display_bufs[0],
+ vout->display_bufs[1], 0,
+ 0) != 0) {
+ dev_err(dev,
+ "Error initializing SDC FG buffer\n");
+ return -EINVAL;
+ }
+ }
+ } else
+#endif
+ { /* Use SDC */
+ unsigned int ipu_ch = CHAN_NONE;
+
+ dev_dbg(dev, "Using SDC channel\n");
+
+ /*
+ * Bypass IC if resizing and rotation are not needed
+ * Meanwhile, apply IC bypass to SDC only
+ */
+ vout->pp_split = 0;/* no pp_split by default */
+ if (out_width == vout->v2f.fmt.pix.width &&
+ out_height == vout->v2f.fmt.pix.height &&
+ ipu_can_rotate_in_place(vout->rotate)) {
+ vout->ic_bypass = 1;
+ ipu_disable_irq(IPU_IRQ_PP_IN_EOF);
+ } else {
+ vout->ic_bypass = 0;
+ }
+
+#ifdef CONFIG_MXC_IPU_V1
+ /* IPUv1 needs IC to do CSC */
+ if (format_is_yuv(vout->v2f.fmt.pix.pixelformat) !=
+ format_is_yuv(bpp_to_fmt(fbi)))
+ vout->ic_bypass = 0;
+#endif
+
+ /* We are using IC to do input cropping */
+ if (vout->queue_buf_paddr[vout->ipu_buf[0]] !=
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset ||
+ vout->queue_buf_paddr[vout->ipu_buf[1]] !=
+ vout->v4l2_bufs[vout->ipu_buf[1]].m.offset)
+ vout->ic_bypass = 0;
+
+ if (vout->ic_bypass)
+ pr_debug("Bypassing IC\n");
+
+ fbvar = fbi->var;
+
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN,
+ (unsigned long)&ipu_ch);
+ set_fs(old_fs);
+ }
+
+ if (ipu_ch == CHAN_NONE) {
+ dev_err(dev,
+ "Can not get display ipu channel\n");
+ return -EINVAL;
+ }
+
+ vout->display_ch = ipu_ch;
+
+ if (vout->cur_disp_output == 3 || vout->cur_disp_output == 5) {
+ fbvar.bits_per_pixel = 16;
+ if (format_is_yuv(vout->v2f.fmt.pix.pixelformat))
+ fbvar.nonstd = IPU_PIX_FMT_UYVY;
+ else
+ fbvar.nonstd = 0;
+
+ fbvar.xres = fbvar.xres_virtual = out_width;
+ fbvar.yres = out_height;
+ fbvar.yres_virtual = out_height * 2;
+ }
+
+ if (vout->ic_bypass) {
+ fbvar.bits_per_pixel = 8*
+ bytes_per_pixel(vout->v2f.fmt.pix.pixelformat);
+ fbvar.nonstd = vout->v2f.fmt.pix.pixelformat;
+ }
+
+ fbvar.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbvar);
+
+ fb_pos.x = vout->crop_current.left;
+ fb_pos.y = vout->crop_current.top;
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
+ (unsigned long)&fb_pos);
+ set_fs(old_fs);
+ }
+
+ vout->display_bufs[1] = fbi->fix.smem_start;
+ vout->display_bufs[0] = fbi->fix.smem_start +
+ (fbi->fix.line_length * fbi->var.yres);
+ vout->display_buf_size = vout->crop_current.width *
+ vout->crop_current.height * fbi->var.bits_per_pixel / 8;
+ if (INTERLACED_CONTENT(vout))
+ vout->post_proc_ch = MEM_VDI_PRP_VF_MEM;
+ else
+ vout->post_proc_ch = MEM_PP_MEM;
+ }
+
+ /* Init PP */
+ if (use_direct_adc == false && !vout->ic_bypass) {
+ if (INTERLACED_CONTENT(vout)) {
+ vout->post_proc_ch = MEM_VDI_PRP_VF_MEM;
+ ipu_enable_irq(IPU_IRQ_PRP_VF_OUT_EOF);
+ } else {
+ vout->post_proc_ch = MEM_PP_MEM;
+ ipu_enable_irq(IPU_IRQ_PP_IN_EOF);
+ }
+
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_width = vout->crop_current.height;
+ out_height = vout->crop_current.width;
+ }
+ memset(&params, 0, sizeof(params));
+ int rc;
+ if (INTERLACED_CONTENT(vout)) {
+ rc = init_VDI(params, vout, dev, fbi, &display_input_ch,
+ out_width, out_height);
+ } else {
+ rc = init_PP(&params, vout, dev, fbi, &display_input_ch,
+ out_width, out_height);
+ }
+ if (rc < 0)
+ return rc;
+ if (!vout->pp_split) { /* display channel link */
+ if (ipu_link_channels(display_input_ch, vout->display_ch) < 0) {
+ dev_err(dev, "Error linking ipu channels\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ vout->state = STATE_STREAM_PAUSED;
+
+ if (use_direct_adc == false) {
+ if (!vout->ic_bypass) {
+#ifndef CONFIG_MXC_IPU_V1
+ ipu_enable_channel(vout->post_proc_ch);
+#endif
+ if (LOAD_3FIELDS(vout)) {
+ ipu_enable_channel(MEM_VDI_PRP_VF_MEM_P);
+ ipu_enable_channel(MEM_VDI_PRP_VF_MEM_N);
+ ipu_select_multi_vdi_buffer(0);
+ } else if (INTERLACED_CONTENT(vout)) {
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+ } else {
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+ if (!vout->pp_split)
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1);
+ }
+ ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1);
+#ifdef CONFIG_MXC_IPU_V1
+ ipu_enable_channel(vout->post_proc_ch);
+#endif
+ } else {
+ ipu_update_channel_buffer(vout->display_ch,
+ IPU_INPUT_BUFFER,
+ 0, vout->v4l2_bufs[vout->ipu_buf[0]].m.offset);
+ ipu_update_channel_buffer(vout->display_ch,
+ IPU_INPUT_BUFFER,
+ 1, vout->v4l2_bufs[vout->ipu_buf[1]].m.offset);
+ ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0);
+ ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 1);
+ }
+ disp_irq = get_display_irq(vout);
+ ipu_request_irq(disp_irq, mxc_v4l2out_disp_refresh_irq_handler,
+ 0, NULL, vout);
+
+ if (fbi) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_UNBLANK);
+ release_console_sem();
+ } else {
+ ipu_enable_channel(vout->display_ch);
+ }
+ } else {
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1);
+ ipu_enable_channel(vout->post_proc_ch);
+ }
+ vout->start_jiffies = jiffies;
+
+ msleep(1);
+
+ dev_dbg(dev,
+ "streamon: start time = %lu jiffies\n", vout->start_jiffies);
+
+ return 0;
+}
+
+/*!
+ * Shut down the voutera
+ *
+ * @param vout structure vout_data *
+ *
+ * @return status 0 Success
+ */
+static int mxc_v4l2out_streamoff(vout_data * vout)
+{
+ struct fb_info *fbi =
+ registered_fb[vout->output_fb_num[vout->cur_disp_output]];
+ int i, retval = 0, disp_irq = 0;
+ unsigned long lockflag = 0;
+
+ if (!vout)
+ return -EINVAL;
+
+ if (vout->state == STATE_STREAM_OFF) {
+ return 0;
+ }
+
+ if (INTERLACED_CONTENT(vout))
+ ipu_free_irq(IPU_IRQ_PRP_VF_OUT_EOF, vout);
+ else
+ ipu_free_irq(IPU_IRQ_PP_IN_EOF, vout);
+
+ spin_lock_irqsave(&g_lock, lockflag);
+
+ del_timer(&vout->output_timer);
+
+ if (vout->state == STATE_STREAM_ON) {
+ vout->state = STATE_STREAM_STOPPING;
+ }
+
+ if (!vout->ic_bypass) {
+ if (INTERLACED_CONTENT(vout))
+ ipu_disable_irq(IPU_IRQ_PRP_VF_OUT_EOF);
+ else
+ ipu_disable_irq(IPU_IRQ_PP_IN_EOF);
+ }
+
+ spin_unlock_irqrestore(&g_lock, lockflag);
+
+ pending_buffer = 0;
+ disp_irq = get_display_irq(vout);
+ ipu_free_irq(disp_irq, vout);
+
+ if (vout->display_ch == MEM_FG_SYNC) {
+ struct mxcfb_pos fb_pos;
+ mm_segment_t old_fs;
+
+ fb_pos.x = 0;
+ fb_pos.y = 0;
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
+ (unsigned long)&fb_pos);
+ set_fs(old_fs);
+ }
+ }
+
+ if (vout->post_proc_ch == MEM_PP_MEM ||
+ vout->post_proc_ch == MEM_PRP_VF_MEM) {
+ /* SDC or ADC with Rotation */
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ ipu_unlink_channels(MEM_PP_MEM, MEM_ROT_PP_MEM);
+ ipu_unlink_channels(MEM_ROT_PP_MEM,
+ vout->display_ch);
+ ipu_disable_channel(MEM_ROT_PP_MEM, true);
+
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ } else {
+ ipu_unlink_channels(MEM_PP_MEM, vout->display_ch);
+ }
+ ipu_disable_channel(MEM_PP_MEM, true);
+
+ if (vout->display_ch == ADC_SYS2 ||
+ vout->display_ch == MEM_FG_SYNC) {
+ ipu_disable_channel(vout->display_ch, true);
+ ipu_uninit_channel(vout->display_ch);
+ } else {
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbi->var);
+
+ if (vout->display_ch == MEM_FG_SYNC) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+ }
+
+ vout->display_bufs[0] = 0;
+ vout->display_bufs[1] = 0;
+ }
+
+ ipu_uninit_channel(MEM_PP_MEM);
+ if (!ipu_can_rotate_in_place(vout->rotate))
+ ipu_uninit_channel(MEM_ROT_PP_MEM);
+ } else if (INTERLACED_CONTENT(vout) && (vout->post_proc_ch == MEM_VDI_PRP_VF_MEM)) {
+ if (!ipu_can_rotate_in_place(vout->rotate)) {
+ ipu_unlink_channels(MEM_VDI_PRP_VF_MEM,
+ MEM_ROT_VF_MEM);
+ ipu_unlink_channels(MEM_ROT_VF_MEM,
+ vout->display_ch);
+ ipu_disable_channel(MEM_ROT_VF_MEM, true);
+
+ if (vout->rot_pp_bufs[0]) {
+ mxc_free_buffers(vout->rot_pp_bufs,
+ vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+ } else {
+ ipu_unlink_channels(MEM_VDI_PRP_VF_MEM,
+ vout->display_ch);
+ }
+
+ ipu_disable_channel(MEM_VDI_PRP_VF_MEM, true);
+
+ if (vout->display_ch == ADC_SYS2 ||
+ vout->display_ch == MEM_FG_SYNC) {
+ ipu_disable_channel(vout->display_ch, true);
+ ipu_uninit_channel(vout->display_ch);
+ } else {
+ fbi->var.activate |= FB_ACTIVATE_FORCE;
+ fb_set_var(fbi, &fbi->var);
+
+ if (vout->display_ch == MEM_FG_SYNC) {
+ acquire_console_sem();
+ fb_blank(fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+ }
+
+ vout->display_bufs[0] = 0;
+ vout->display_bufs[1] = 0;
+ }
+
+ ipu_uninit_channel(MEM_VDI_PRP_VF_MEM);
+ if (!ipu_can_rotate_in_place(vout->rotate))
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ } else { /* ADC Direct */
+ ipu_disable_channel(MEM_PP_ADC, true);
+ ipu_uninit_channel(MEM_PP_ADC);
+ }
+ vout->ready_q.head = vout->ready_q.tail = 0;
+ vout->done_q.head = vout->done_q.tail = 0;
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+
+ vout->state = STATE_STREAM_OFF;
+
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL
+ if (vout->cur_disp_output < DISP3) {
+ if (vout->display_bufs[0] != 0) {
+ mxc_free_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+
+ mxcfb_set_refresh_mode(registered_fb
+ [vout->
+ output_fb_num[vout->cur_disp_output]],
+ MXCFB_REFRESH_PARTIAL, 0);
+ }
+#endif
+
+ return retval;
+}
+
+/*
+ * Valid whether the palette is supported
+ *
+ * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32
+ *
+ * @return 1 if supported, 0 if failed
+ */
+static inline int valid_mode(u32 palette)
+{
+ return ((palette == V4L2_PIX_FMT_RGB565) ||
+ (palette == V4L2_PIX_FMT_BGR24) ||
+ (palette == V4L2_PIX_FMT_RGB24) ||
+ (palette == V4L2_PIX_FMT_BGR32) ||
+ (palette == V4L2_PIX_FMT_RGB32) ||
+ (palette == V4L2_PIX_FMT_NV12) ||
+ (palette == V4L2_PIX_FMT_YUV422P) ||
+ (palette == V4L2_PIX_FMT_YUV420));
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_g_fmt(vout_data * vout, struct v4l2_format *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ return -EINVAL;
+ }
+ *f = vout->v2f;
+ return 0;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_FMT Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param v4l2_format structure v4l2_format *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_v4l2out_s_fmt(vout_data * vout, struct v4l2_format *f)
+{
+ int retval = 0;
+ u32 size = 0;
+ u32 bytesperline;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ goto err0;
+ }
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ dev_err(&vout->video_dev->dev, "pixel format not supported\n");
+ retval = -EINVAL;
+ goto err0;
+ }
+
+ bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) /
+ 8;
+ if (f->fmt.pix.bytesperline < bytesperline) {
+ f->fmt.pix.bytesperline = bytesperline;
+ } else {
+ bytesperline = f->fmt.pix.bytesperline;
+ }
+ vout->bytesperline = bytesperline;
+
+ /* Based on http://v4l2spec.bytesex.org/spec/x6386.htm#V4L2-FIELD */
+ vout->field_fmt = f->fmt.pix.field;
+ switch (vout->field_fmt) {
+ /* Images are in progressive format, not interlaced */
+ case V4L2_FIELD_NONE:
+ break;
+ /* The two fields of a frame are passed in separate buffers,
+ in temporal order, i. e. the older one first. */
+ case V4L2_FIELD_ALTERNATE:
+ dev_err(&vout->video_dev->dev,
+ "V4L2_FIELD_ALTERNATE field format not supported yet!\n");
+ break;
+ case V4L2_FIELD_INTERLACED_TB:
+ if (cpu_is_mx51())
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ dev_err(&vout->video_dev->dev,
+ "V4L2_FIELD_INTERLACED_BT field format not supported yet!\n");
+ default:
+ vout->field_fmt = V4L2_FIELD_NONE;
+ break;
+ }
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV422P:
+ /* byteperline for YUV planar formats is for
+ Y plane only */
+ size = bytesperline * f->fmt.pix.height * 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV12:
+ size = (bytesperline * f->fmt.pix.height * 3) / 2;
+ break;
+ default:
+ size = bytesperline * f->fmt.pix.height;
+ break;
+ }
+
+ /* Return the actual size of the image to the app */
+ if (f->fmt.pix.sizeimage < size) {
+ f->fmt.pix.sizeimage = size;
+ } else {
+ size = f->fmt.pix.sizeimage;
+ }
+
+ vout->v2f.fmt.pix = f->fmt.pix;
+ if (vout->v2f.fmt.pix.priv != 0) {
+ if (copy_from_user(&vout->offset,
+ (void *)vout->v2f.fmt.pix.priv,
+ sizeof(vout->offset))) {
+ retval = -EFAULT;
+ goto err0;
+ }
+ } else {
+ vout->offset.u_offset = 0;
+ vout->offset.v_offset = 0;
+ }
+
+ retval = 0;
+ err0:
+ return retval;
+}
+
+/*
+ * V4L2 - Handles VIDIOC_G_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_get_v42lout_control(vout_data * vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ return (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0;
+ case V4L2_CID_VFLIP:
+ return (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0;
+ case (V4L2_CID_PRIVATE_BASE + 1):
+ return vout->rotate;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * V4L2 - Handles VIDIOC_S_CTRL Ioctl
+ *
+ * @param vout structure vout_data *
+ *
+ * @param c structure v4l2_control *
+ *
+ * @return status 0 success, EINVAL failed
+ */
+static int mxc_set_v42lout_control(vout_data * vout, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ vout->rotate |= c->value ? IPU_ROTATE_HORIZ_FLIP :
+ IPU_ROTATE_NONE;
+ break;
+ case V4L2_CID_VFLIP:
+ vout->rotate |= c->value ? IPU_ROTATE_VERT_FLIP :
+ IPU_ROTATE_NONE;
+ break;
+ case V4L2_CID_MXC_ROT:
+ vout->rotate = c->value;
+ break;
+ case V4L2_CID_MXC_MOTION:
+ vout->motion_sel = c->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * V4L2 interface - open function
+ *
+ * @param inode structure inode *
+ *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENODEV timeout, ERESTARTSYS interrupted by user
+ */
+static int mxc_v4l2out_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+ int err;
+
+ if (!vout) {
+ return -ENODEV;
+ }
+
+ down(&vout->busy_lock);
+
+ err = -EINTR;
+ if (signal_pending(current))
+ goto oops;
+
+
+ if (vout->open_count++ == 0) {
+ init_waitqueue_head(&vout->v4l_bufq);
+
+ init_timer(&vout->output_timer);
+ vout->output_timer.function = mxc_v4l2out_timer_handler;
+ vout->output_timer.data = (unsigned long)vout;
+
+ vout->state = STATE_STREAM_OFF;
+ vout->rotate = IPU_ROTATE_NONE;
+ g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0;
+
+ }
+
+ file->private_data = dev;
+
+ up(&vout->busy_lock);
+
+ return 0;
+
+ oops:
+ up(&vout->busy_lock);
+ return err;
+}
+
+/*!
+ * V4L2 interface - close function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @return 0 success
+ */
+static int mxc_v4l2out_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+
+ if (--vout->open_count == 0) {
+ if (vout->state != STATE_STREAM_OFF)
+ mxc_v4l2out_streamoff(vout);
+
+ file->private_data = NULL;
+
+ mxc_free_buffers(vout->queue_buf_paddr, vout->queue_buf_vaddr,
+ vout->buffer_cnt, vout->queue_buf_size);
+ vout->buffer_cnt = 0;
+ mxc_free_buffers(vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2,
+ vout->display_buf_size);
+
+ /* capture off */
+ wake_up_interruptible(&vout->v4l_bufq);
+
+ }
+
+ return 0;
+}
+
+/*!
+ * V4L2 interface - ioctl function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @param ioctlnr unsigned int
+ *
+ * @param arg void *
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static int
+mxc_v4l2out_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ vout_data *vout = video_get_drvdata(vdev);
+ int retval = 0;
+ int i = 0;
+
+ if (!vout)
+ return -EBADF;
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EBUSY;
+
+ switch (ioctlnr) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+ strcpy(cap->driver, "mxc_v4l2_output");
+ cap->version = 0;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->card[0] = '\0';
+ cap->bus_info[0] = '\0';
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *gf = arg;
+ retval = mxc_v4l2out_g_fmt(vout, gf);
+ break;
+ }
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *sf = arg;
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+ retval = mxc_v4l2out_s_fmt(vout, sf);
+ break;
+ }
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *req = arg;
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ dev_dbg(&vdev->dev,
+ "VIDIOC_REQBUFS: incorrect buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ if (req->count == 0)
+ mxc_v4l2out_streamoff(vout);
+
+ if (vout->state == STATE_STREAM_OFF) {
+ if (vout->queue_buf_paddr[0] != 0) {
+ mxc_free_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ dev_dbg(&vdev->dev,
+ "VIDIOC_REQBUFS: freed buffers\n");
+ }
+ vout->buffer_cnt = 0;
+ } else {
+ dev_dbg(&vdev->dev,
+ "VIDIOC_REQBUFS: Buffer is in use\n");
+ retval = -EBUSY;
+ break;
+ }
+
+ if (req->count == 0)
+ break;
+
+ if (req->count < MIN_FRAME_NUM) {
+ req->count = MIN_FRAME_NUM;
+ } else if (req->count > MAX_FRAME_NUM) {
+ req->count = MAX_FRAME_NUM;
+ }
+ vout->buffer_cnt = req->count;
+ vout->queue_buf_size =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+
+ retval = mxc_allocate_buffers(vout->queue_buf_paddr,
+ vout->queue_buf_vaddr,
+ vout->buffer_cnt,
+ vout->queue_buf_size);
+ if (retval < 0)
+ break;
+
+ /* Init buffer queues */
+ vout->done_q.head = 0;
+ vout->done_q.tail = 0;
+ vout->ready_q.head = 0;
+ vout->ready_q.tail = 0;
+
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ memset(&(vout->v4l2_bufs[i]), 0,
+ sizeof(vout->v4l2_bufs[i]));
+ vout->v4l2_bufs[i].flags = 0;
+ vout->v4l2_bufs[i].memory = V4L2_MEMORY_MMAP;
+ vout->v4l2_bufs[i].index = i;
+ vout->v4l2_bufs[i].type =
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ vout->v4l2_bufs[i].length =
+ PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage);
+ vout->v4l2_bufs[i].m.offset =
+ (unsigned long)vout->queue_buf_paddr[i];
+ vout->v4l2_bufs[i].timestamp.tv_sec = 0;
+ vout->v4l2_bufs[i].timestamp.tv_usec = 0;
+ }
+ break;
+ }
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ u32 type = buf->type;
+ int index = buf->index;
+
+ if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt)) {
+ dev_dbg(&vdev->dev,
+ "VIDIOC_QUERYBUFS: incorrect buffer type\n");
+ retval = -EINVAL;
+ break;
+ }
+ down(&vout->param_lock);
+ memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf));
+ up(&vout->param_lock);
+ break;
+ }
+ case VIDIOC_QBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+ unsigned long lock_flags;
+ int param[5][3];
+
+ if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (index >= vout->buffer_cnt)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ dev_dbg(&vdev->dev, "VIDIOC_QBUF: %d field = %d\n", buf->index, buf->field);
+
+ /* mmapped buffers are L1 WB cached,
+ * so we need to clean them */
+ if (buf->memory & V4L2_MEMORY_MMAP) {
+ flush_cache_all();
+ }
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf));
+ vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED;
+
+ g_buf_q_cnt++;
+ if (vout->v4l2_bufs[index].reserved)
+ if (!copy_from_user(&param[0][0],
+ (void *)vout->
+ v4l2_bufs[index]
+ .reserved, sizeof(param)))
+ ipu_set_csc_coefficients(vout->
+ display_ch,
+ param);
+ queue_buf(&vout->ready_q, index);
+ if (vout->state == STATE_STREAM_PAUSED) {
+ unsigned long timeout;
+
+ index = peek_next_buf(&vout->ready_q);
+
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec ==
+ 0)
+ && (vout->v4l2_bufs[index].timestamp.
+ tv_usec == 0))
+ timeout =
+ vout->start_jiffies +
+ vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].
+ timestamp);
+
+ if (jiffies >= timeout) {
+ dev_dbg(&vout->video_dev->dev,
+ "warning: timer timeout already expired.\n");
+ }
+ vout->output_timer.expires = timeout;
+ dev_dbg(&vdev->dev,
+ "QBUF: frame #%u timeout @ %lu jiffies, current = %lu\n",
+ vout->frame_count, timeout, jiffies);
+ add_timer(&vout->output_timer);
+ vout->state = STATE_STREAM_ON;
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
+ break;
+ }
+ case VIDIOC_DQBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int idx;
+
+ if ((queue_size(&vout->done_q) == 0) &&
+ (file->f_flags & O_NONBLOCK)) {
+ retval = -EAGAIN;
+ break;
+ }
+
+ if (!wait_event_interruptible_timeout(vout->v4l_bufq,
+ queue_size(&vout->
+ done_q)
+ != 0, 10 * HZ)) {
+ dev_dbg(&vdev->dev, "VIDIOC_DQBUF: timeout\n");
+ retval = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ dev_dbg(&vdev->dev,
+ "VIDIOC_DQBUF: interrupt received\n");
+ retval = -ERESTARTSYS;
+ break;
+ }
+ idx = dequeue_buf(&vout->done_q);
+ if (idx == -1) { /* No frame free */
+ dev_dbg(&vdev->dev,
+ "VIDIOC_DQBUF: no free buffers, returning\n");
+ retval = -EAGAIN;
+ break;
+ }
+ if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) ==
+ 0)
+ dev_dbg(&vdev->dev,
+ "VIDIOC_DQBUF: buffer in done q, but not "
+ "flagged as done\n");
+
+ vout->v4l2_bufs[idx].flags = 0;
+ memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf));
+ dev_dbg(&vdev->dev, "VIDIOC_DQBUF: %d\n", buf->index);
+ break;
+ }
+ case VIDIOC_STREAMON:
+ {
+ retval = mxc_v4l2out_streamon(vout);
+ break;
+ }
+ case VIDIOC_STREAMOFF:
+ {
+ retval = mxc_v4l2out_streamoff(vout);
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ retval = mxc_get_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ retval = mxc_set_v42lout_control(vout, arg);
+ break;
+ }
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cap = arg;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+
+ cap->bounds = vout->crop_bounds[vout->cur_disp_output];
+ cap->defrect = vout->crop_bounds[vout->cur_disp_output];
+ retval = 0;
+ break;
+ }
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ crop->c = vout->crop_current;
+ break;
+ }
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+ struct v4l2_rect *b =
+ &(vout->crop_bounds[vout->cur_disp_output]);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.height < 0) {
+ retval = -EINVAL;
+ break;
+ }
+ if (crop->c.width < 0) {
+ retval = -EINVAL;
+ break;
+ }
+
+ /* only full screen supported for SDC BG and SDC DC */
+ if (vout->cur_disp_output == 4 ||
+ vout->cur_disp_output == 5) {
+ crop->c = vout->crop_current;
+ break;
+ }
+
+ if (crop->c.top < b->top)
+ crop->c.top = b->top;
+ if (crop->c.top >= b->top + b->height)
+ crop->c.top = b->top + b->height - 1;
+ if (crop->c.height > b->top - crop->c.top + b->height)
+ crop->c.height =
+ b->top - crop->c.top + b->height;
+
+ if (crop->c.left < b->left)
+ crop->c.left = b->left;
+ if (crop->c.left >= b->left + b->width)
+ crop->c.left = b->left + b->width - 1;
+ if (crop->c.width > b->left - crop->c.left + b->width)
+ crop->c.width =
+ b->left - crop->c.left + b->width;
+
+ /* stride line limitation */
+ crop->c.height -= crop->c.height % 8;
+ crop->c.width -= crop->c.width % 8;
+
+ vout->crop_current = crop->c;
+ break;
+ }
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *output = arg;
+
+ if ((output->index >= 5) ||
+ (vout->output_enabled[output->index] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (output->index < 3) {
+ *output = mxc_outputs[MXC_V4L2_OUT_2_ADC];
+ output->name[4] = '0' + output->index;
+ } else {
+ *output = mxc_outputs[MXC_V4L2_OUT_2_SDC];
+ }
+ break;
+ }
+ case VIDIOC_G_OUTPUT:
+ {
+ int *p_output_num = arg;
+
+ *p_output_num = vout->cur_disp_output;
+ break;
+ }
+ case VIDIOC_S_OUTPUT:
+ {
+ int *p_output_num = arg;
+ int fbnum;
+ struct v4l2_rect *b;
+
+ if ((*p_output_num >= MXC_V4L2_OUT_NUM_OUTPUTS) ||
+ (vout->output_enabled[*p_output_num] == false)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (vout->state != STATE_STREAM_OFF) {
+ retval = -EBUSY;
+ break;
+ }
+
+ vout->cur_disp_output = *p_output_num;
+
+ /* Update bounds in case they have changed */
+ b = &vout->crop_bounds[vout->cur_disp_output];
+
+ fbnum = vout->output_fb_num[vout->cur_disp_output];
+
+ /*
+ * For FG overlay, it uses BG window parameter as
+ * limitation reference; and BG must be enabled to
+ * support FG.
+ */
+ if (vout->cur_disp_output == 3) {
+ unsigned int i, ipu_ch = CHAN_NONE;
+ struct fb_info *fbi;
+ mm_segment_t old_fs;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ fbi = registered_fb[i];
+ if (fbi->fbops->fb_ioctl) {
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ fbi->fbops->fb_ioctl(fbi,
+ MXCFB_GET_FB_IPU_CHAN,
+ (unsigned long)&ipu_ch);
+ set_fs(old_fs);
+ }
+ if (ipu_ch == CHAN_NONE) {
+ dev_err(&vdev->dev,
+ "Can't get disp ipu channel\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ if (ipu_ch == MEM_BG_SYNC) {
+ fbnum = i;
+ break;
+ }
+ }
+ }
+
+ b->width = registered_fb[fbnum]->var.xres;
+ b->height = registered_fb[fbnum]->var.yres;
+
+ vout->crop_current = *b;
+ break;
+ }
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_G_PARM:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY:
+ default:
+ retval = -EINVAL;
+ break;
+ }
+
+ up(&vout->busy_lock);
+ return retval;
+}
+
+/*
+ * V4L2 interface - ioctl function
+ *
+ * @return None
+ */
+static int
+mxc_v4l2out_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, mxc_v4l2out_do_ioctl);
+}
+
+/*!
+ * V4L2 interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error,
+ * ENOBUFS remap_page error
+ */
+static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ unsigned long size = vma->vm_end - vma->vm_start;
+ int res = 0;
+ int i;
+ vout_data *vout = video_get_drvdata(vdev);
+
+ dev_dbg(&vdev->dev, "pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ for (i = 0; i < vout->buffer_cnt; i++) {
+ if ((vout->v4l2_bufs[i].m.offset ==
+ (vma->vm_pgoff << PAGE_SHIFT)) &&
+ (vout->v4l2_bufs[i].length >= size)) {
+ vout->v4l2_bufs[i].flags |= V4L2_BUF_FLAG_MAPPED;
+ break;
+ }
+ }
+ if (i == vout->buffer_cnt) {
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ /* make buffers inner write-back, outer write-thru cacheable */
+ /* vma->vm_page_prot = pgprot_outer_wrthru(vma->vm_page_prot);*/
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ dev_dbg(&vdev->dev, "mmap remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mxc_mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+ mxc_mmap_exit:
+ up(&vout->busy_lock);
+ return res;
+}
+
+/*!
+ * V4L2 interface - poll function
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_v4l2out_poll(struct file *file, poll_table * wait)
+{
+ struct video_device *dev = video_devdata(file);
+ vout_data *vout = video_get_drvdata(dev);
+
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ if (down_interruptible(&vout->busy_lock))
+ return -EINTR;
+
+ queue = &vout->v4l_bufq;
+ poll_wait(file, queue, wait);
+
+ up(&vout->busy_lock);
+ return res;
+}
+
+static struct
+file_operations mxc_v4l2out_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_v4l2out_open,
+ .release = mxc_v4l2out_close,
+ .ioctl = mxc_v4l2out_ioctl,
+ .mmap = mxc_v4l2out_mmap,
+ .poll = mxc_v4l2out_poll,
+};
+
+static struct video_device mxc_v4l2out_template = {
+ .name = "MXC Video Output",
+ .vfl_type = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING,
+ .fops = &mxc_v4l2out_fops,
+ .release = video_device_release,
+};
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxc_v4l2out_probe(struct platform_device *pdev)
+{
+ int i;
+ vout_data *vout;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ g_vout = vout = kmalloc(sizeof(vout_data), GFP_KERNEL);
+
+ if (!vout)
+ return 0;
+
+ memset(vout, 0, sizeof(vout_data));
+
+ vout->video_dev = video_device_alloc();
+ if (vout->video_dev == NULL)
+ return -1;
+ vout->video_dev->minor = -1;
+
+ *(vout->video_dev) = mxc_v4l2out_template;
+
+ /* register v4l device */
+ if (video_register_device(vout->video_dev,
+ VFL_TYPE_GRABBER, video_nr) == -1) {
+ dev_dbg(&pdev->dev, "video_register_device failed\n");
+ return 0;
+ }
+ dev_info(&pdev->dev, "Registered device video%d\n",
+ vout->video_dev->minor & 0x1f);
+ /*vout->video_dev->dev = &pdev->dev;*/
+
+ video_set_drvdata(vout->video_dev, vout);
+
+ init_MUTEX(&vout->param_lock);
+ init_MUTEX(&vout->busy_lock);
+
+ /* setup outputs and cropping */
+ vout->cur_disp_output = -1;
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strncmp(idstr, "DISP", 4) == 0) {
+ int disp_num = idstr[4] - '0';
+ if (disp_num == 3) {
+ if (strcmp(idstr, "DISP3 BG - DI1") == 0)
+ disp_num = 5;
+ else if (strncmp(idstr, "DISP3 BG", 8) == 0)
+ disp_num = 4;
+ }
+ vout->crop_bounds[disp_num].left = 0;
+ vout->crop_bounds[disp_num].top = 0;
+ vout->crop_bounds[disp_num].width =
+ registered_fb[i]->var.xres;
+ vout->crop_bounds[disp_num].height =
+ registered_fb[i]->var.yres;
+ vout->output_enabled[disp_num] = true;
+ vout->output_fb_num[disp_num] = i;
+ if (vout->cur_disp_output == -1) {
+ vout->cur_disp_output = disp_num;
+ }
+ }
+
+ }
+ vout->crop_current = vout->crop_bounds[vout->cur_disp_output];
+
+ platform_set_drvdata(pdev, vout);
+
+ return 0;
+}
+
+static int mxc_v4l2out_remove(struct platform_device *pdev)
+{
+ vout_data *vout = platform_get_drvdata(pdev);
+
+ if (vout->video_dev) {
+ if (-1 != vout->video_dev->minor)
+ video_unregister_device(vout->video_dev);
+ else
+ video_device_release(vout->video_dev);
+ vout->video_dev = NULL;
+ }
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(vout);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_v4l2out_driver = {
+ .driver = {
+ .name = "MXC Video Output",
+ },
+ .probe = mxc_v4l2out_probe,
+ .remove = mxc_v4l2out_remove,
+};
+
+static struct platform_device mxc_v4l2out_device = {
+ .name = "MXC Video Output",
+ .id = 0,
+};
+
+/*!
+ * mxc v4l2 init function
+ *
+ */
+static int mxc_v4l2out_init(void)
+{
+ u8 err = 0;
+
+ err = platform_driver_register(&mxc_v4l2out_driver);
+ if (err == 0) {
+ platform_device_register(&mxc_v4l2out_device);
+ }
+ return err;
+}
+
+/*!
+ * mxc v4l2 cleanup function
+ *
+ */
+static void mxc_v4l2out_clean(void)
+{
+ video_unregister_device(g_vout->video_dev);
+
+ platform_driver_unregister(&mxc_v4l2out_driver);
+ platform_device_unregister(&mxc_v4l2out_device);
+ kfree(g_vout);
+ g_vout = NULL;
+}
+
+module_init(mxc_v4l2out_init);
+module_exit(mxc_v4l2out_clean);
+
+module_param(video_nr, int, 0444);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("V4L2-driver for MXC video output");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.h b/drivers/media/video/mxc/output/mxc_v4l2_output.h
new file mode 100644
index 000000000000..45d713222d86
--- /dev/null
+++ b/drivers/media/video/mxc/output/mxc_v4l2_output.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup MXC_V4L2_OUTPUT MXC V4L2 Video Output Driver
+ */
+/*!
+ * @file mxc_v4l2_output.h
+ *
+ * @brief MXC V4L2 Video Output Driver Header file
+ *
+ * Video4Linux2 Output Device using MXC IPU Post-processing functionality.
+ *
+ * @ingroup MXC_V4L2_OUTPUT
+ */
+#ifndef __MXC_V4L2_OUTPUT_H__
+#define __MXC_V4L2_OUTPUT_H__
+
+#include <media/v4l2-dev.h>
+
+#ifdef __KERNEL__
+
+#include <linux/ipu.h>
+#include <linux/mxc_v4l2.h>
+#include <linux/videodev2.h>
+
+#define MIN_FRAME_NUM 2
+#define MAX_FRAME_NUM 30
+
+#define MXC_V4L2_OUT_NUM_OUTPUTS 6
+#define MXC_V4L2_OUT_2_SDC 0
+#define MXC_V4L2_OUT_2_ADC 1
+
+
+typedef struct {
+ int list[MAX_FRAME_NUM + 1];
+ int head;
+ int tail;
+} v4l_queue;
+
+/*!
+ * States for the video stream
+ */
+typedef enum {
+ STATE_STREAM_OFF,
+ STATE_STREAM_ON,
+ STATE_STREAM_PAUSED,
+ STATE_STREAM_STOPPING,
+} v4lout_state;
+
+/*!
+ * common v4l2 driver structure.
+ */
+typedef struct _vout_data {
+ struct video_device *video_dev;
+ /*!
+ * semaphore guard against SMP multithreading
+ */
+ struct semaphore busy_lock;
+
+ /*!
+ * number of process that have device open
+ */
+ int open_count;
+
+ /*!
+ * params lock for this camera
+ */
+ struct semaphore param_lock;
+
+ struct timer_list output_timer;
+ unsigned long start_jiffies;
+ u32 frame_count;
+
+ v4l_queue ready_q;
+ v4l_queue done_q;
+
+ s8 next_rdy_ipu_buf;
+ s8 next_done_ipu_buf;
+ s8 ipu_buf[2];
+ s8 ipu_buf_p[2];
+ s8 ipu_buf_n[2];
+ volatile v4lout_state state;
+
+ int cur_disp_output;
+ int output_fb_num[MXC_V4L2_OUT_NUM_OUTPUTS];
+ int output_enabled[MXC_V4L2_OUT_NUM_OUTPUTS];
+ struct v4l2_framebuffer v4l2_fb;
+ int ic_bypass;
+ ipu_channel_t display_ch;
+ ipu_channel_t post_proc_ch;
+
+ /*!
+ * FRAME_NUM-buffering, so we need a array
+ */
+ int buffer_cnt;
+ dma_addr_t queue_buf_paddr[MAX_FRAME_NUM];
+ void *queue_buf_vaddr[MAX_FRAME_NUM];
+ u32 queue_buf_size;
+ struct v4l2_buffer v4l2_bufs[MAX_FRAME_NUM];
+ u32 display_buf_size;
+ dma_addr_t display_bufs[2];
+ void *display_bufs_vaddr[2];
+ dma_addr_t rot_pp_bufs[2];
+ void *rot_pp_bufs_vaddr[2];
+
+ /*!
+ * Poll wait queue
+ */
+ wait_queue_head_t v4l_bufq;
+
+ /*!
+ * v4l2 format
+ */
+ struct v4l2_format v2f;
+ struct v4l2_mxc_offset offset;
+ ipu_rotate_mode_t rotate;
+
+ /* crop */
+ struct v4l2_rect crop_bounds[MXC_V4L2_OUT_NUM_OUTPUTS];
+ struct v4l2_rect crop_current;
+ u32 bytesperline;
+ enum v4l2_field field_fmt;
+ ipu_motion_sel motion_sel;
+
+ /* PP split fot two stripes*/
+ int pp_split; /* 0,1 */
+ struct stripe_param pp_left_stripe;
+ struct stripe_param pp_right_stripe; /* struct for split parameters */
+ /* IC ouput buffer number. Counting from 0 to 3 */
+ int pp_split_buf_num; /* 0..3 */
+
+} vout_data;
+
+#endif
+#endif /* __MXC_V4L2_OUTPUT_H__ */
diff --git a/drivers/media/video/pxp.c b/drivers/media/video/pxp.c
new file mode 100644
index 000000000000..26e45bbd0e74
--- /dev/null
+++ b/drivers/media/video/pxp.c
@@ -0,0 +1,1318 @@
+/*
+ * Freescale STMP378X PxP driver
+ *
+ * Author: Matt Porter <mporter@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf-dma-contig.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include <mach/stmp3xxx_regs.h>
+#include <mach/regs-pxp.h>
+
+#include "pxp.h"
+
+#define PXP_DRIVER_NAME "stmp3xxx-pxp"
+#define PXP_DRIVER_MAJOR 1
+#define PXP_DRIVER_MINOR 0
+
+#define PXP_DEF_BUFS 2
+#define PXP_MIN_PIX 8
+
+#define V4L2_OUTPUT_TYPE_INTERNAL 4
+
+#define REG_OFFSET 0x10
+#define REGS1_NUMS 16
+#define REGS2_NUMS 5
+#define REGS3_NUMS 32
+static u32 regs1[REGS1_NUMS];
+static u32 regs2[REGS2_NUMS];
+static u32 regs3[REGS3_NUMS];
+
+static struct pxp_data_format pxp_s0_formats[] = {
+ {
+ .name = "24-bit RGB",
+ .bpp = 4,
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__RGB888,
+ }, {
+ .name = "16-bit RGB 5:6:5",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__RGB565,
+ }, {
+ .name = "16-bit RGB 5:5:5",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__RGB555,
+ }, {
+ .name = "YUV 4:2:0 Planar",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__YUV420,
+ }, {
+ .name = "YUV 4:2:2 Planar",
+ .bpp = 2,
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .ctrl_s0_fmt = BV_PXP_CTRL_S0_FORMAT__YUV422,
+ },
+};
+
+struct v4l2_queryctrl pxp_controls[] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_PRIVATE_BASE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Rotation",
+ .minimum = 0,
+ .maximum = 270,
+ .step = 90,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_PRIVATE_BASE + 1,
+ .name = "Background Color",
+ .minimum = 0,
+ .maximum = 0xFFFFFF,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_PRIVATE_BASE + 2,
+ .name = "Set S0 Chromakey",
+ .minimum = -1,
+ .maximum = 0xFFFFFF,
+ .step = 1,
+ .default_value = -1,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_PRIVATE_BASE + 3,
+ .name = "YUV Colorspace",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+};
+
+static void pxp_set_ctrl(struct pxps *pxp)
+{
+ u32 ctrl;
+
+ ctrl = BF_PXP_CTRL_S0_FORMAT(pxp->s0_fmt->ctrl_s0_fmt);
+ ctrl |=
+ BF_PXP_CTRL_OUTPUT_RGB_FORMAT(BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB888);
+ ctrl |= BM_PXP_CTRL_CROP;
+
+ if (pxp->scaling)
+ ctrl |= BM_PXP_CTRL_SCALE;
+ if (pxp->vflip)
+ ctrl |= BM_PXP_CTRL_VFLIP;
+ if (pxp->hflip)
+ ctrl |= BM_PXP_CTRL_HFLIP;
+ if (pxp->rotate)
+ ctrl |= BF_PXP_CTRL_ROTATE(pxp->rotate/90);
+
+ ctrl |= BM_PXP_CTRL_IRQ_ENABLE;
+ if (pxp->active)
+ ctrl |= BM_PXP_CTRL_ENABLE;
+
+ HW_PXP_CTRL_WR(ctrl);
+}
+
+static void pxp_set_rgbbuf(struct pxps *pxp)
+{
+ HW_PXP_RGBBUF_WR(pxp->outb_phys);
+ /* Always equal to the FB size */
+ HW_PXP_RGBSIZE_WR(BF_PXP_RGBSIZE_WIDTH(pxp->fb.fmt.width) |
+ BF_PXP_RGBSIZE_HEIGHT(pxp->fb.fmt.height));
+}
+
+static void pxp_set_s0colorkey(struct pxps *pxp)
+{
+ /* Low and high are set equal. V4L does not allow a chromakey range */
+ if (pxp->s0_chromakey == -1) {
+ /* disable color key */
+ HW_PXP_S0COLORKEYLOW_WR(0xFFFFFF);
+ HW_PXP_S0COLORKEYHIGH_WR(0x0);
+ } else {
+ HW_PXP_S0COLORKEYLOW_WR(pxp->s0_chromakey);
+ HW_PXP_S0COLORKEYHIGH_WR(pxp->s0_chromakey);
+ }
+}
+
+static void pxp_set_s1colorkey(struct pxps *pxp)
+{
+ /* Low and high are set equal. V4L does not allow a chromakey range */
+ if (pxp->s1_chromakey_state != 0 && pxp->s1_chromakey != -1) {
+ HW_PXP_OLCOLORKEYLOW_WR(pxp->s1_chromakey);
+ HW_PXP_OLCOLORKEYHIGH_WR(pxp->s1_chromakey);
+ } else {
+ /* disable color key */
+ HW_PXP_OLCOLORKEYLOW_WR(0xFFFFFF);
+ HW_PXP_OLCOLORKEYHIGH_WR(0x0);
+ }
+}
+
+static void pxp_set_oln(struct pxps *pxp)
+{
+ HW_PXP_OLn_WR(0, (u32)pxp->fb.base);
+ HW_PXP_OLnSIZE_WR(0, BF_PXP_OLnSIZE_WIDTH(pxp->fb.fmt.width >> 3) |
+ BF_PXP_OLnSIZE_HEIGHT(pxp->fb.fmt.height >> 3));
+}
+
+static void pxp_set_olparam(struct pxps *pxp)
+{
+ u32 olparam;
+ struct v4l2_pix_format *fmt = &pxp->fb.fmt;
+
+ olparam = BF_PXP_OLnPARAM_ALPHA(pxp->global_alpha);
+ if (fmt->pixelformat == V4L2_PIX_FMT_RGB24)
+ olparam |=
+ BF_PXP_OLnPARAM_FORMAT(BV_PXP_OLnPARAM_FORMAT__RGB888);
+ else
+ olparam |=
+ BF_PXP_OLnPARAM_FORMAT(BV_PXP_OLnPARAM_FORMAT__RGB565);
+ if (pxp->global_alpha_state)
+ olparam |= BF_PXP_OLnPARAM_ALPHA_CNTL(
+ BV_PXP_OLnPARAM_ALPHA_CNTL__Override);
+ if (pxp->s1_chromakey_state)
+ olparam |= BM_PXP_OLnPARAM_ENABLE_COLORKEY;
+ if (pxp->overlay_state)
+ olparam |= BM_PXP_OLnPARAM_ENABLE;
+ HW_PXP_OLnPARAM_WR(0, olparam);
+}
+
+static void pxp_set_s0param(struct pxps *pxp)
+{
+ u32 s0param;
+
+ s0param = BF_PXP_S0PARAM_XBASE(pxp->drect.left >> 3);
+ s0param |= BF_PXP_S0PARAM_YBASE(pxp->drect.top >> 3);
+ s0param |= BF_PXP_S0PARAM_WIDTH(pxp->s0_width >> 3);
+ s0param |= BF_PXP_S0PARAM_HEIGHT(pxp->s0_height >> 3);
+ HW_PXP_S0PARAM_WR(s0param);
+}
+
+static void pxp_set_s0crop(struct pxps *pxp)
+{
+ u32 s0crop;
+
+ s0crop = BF_PXP_S0CROP_XBASE(pxp->srect.left >> 3);
+ s0crop |= BF_PXP_S0CROP_YBASE(pxp->srect.top >> 3);
+ s0crop |= BF_PXP_S0CROP_WIDTH(pxp->drect.width >> 3);
+ s0crop |= BF_PXP_S0CROP_HEIGHT(pxp->drect.height >> 3);
+ HW_PXP_S0CROP_WR(s0crop);
+}
+
+static int pxp_set_scaling(struct pxps *pxp)
+{
+ int ret = 0;
+ u32 xscale, yscale, s0scale;
+
+ if ((pxp->s0_fmt->fourcc != V4L2_PIX_FMT_YUV420) &&
+ (pxp->s0_fmt->fourcc != V4L2_PIX_FMT_YUV422P)) {
+ pxp->scaling = 0;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if ((pxp->srect.width == pxp->drect.width) &&
+ (pxp->srect.height == pxp->drect.height)) {
+ pxp->scaling = 0;
+ goto out;
+ }
+
+ pxp->scaling = 1;
+ xscale = pxp->srect.width * 0x1000 / pxp->drect.width;
+ yscale = pxp->srect.height * 0x1000 / pxp->drect.height;
+ s0scale = BF_PXP_S0SCALE_YSCALE(yscale) |
+ BF_PXP_S0SCALE_XSCALE(xscale);
+ HW_PXP_S0SCALE_WR(s0scale);
+
+out:
+ pxp_set_ctrl(pxp);
+
+ return ret;
+}
+
+static int pxp_set_fbinfo(struct pxps *pxp)
+{
+ struct fb_var_screeninfo var;
+ struct fb_fix_screeninfo fix;
+ struct v4l2_framebuffer *fb = &pxp->fb;
+ int err;
+
+ err = stmp3xxxfb_get_info(&var, &fix);
+
+ fb->fmt.width = var.xres;
+ fb->fmt.height = var.yres;
+ if (var.bits_per_pixel == 16)
+ fb->fmt.pixelformat = V4L2_PIX_FMT_RGB565;
+ else
+ fb->fmt.pixelformat = V4L2_PIX_FMT_RGB24;
+ fb->base = (void *)fix.smem_start;
+ return err;
+}
+
+static void pxp_set_s0bg(struct pxps *pxp)
+{
+ HW_PXP_S0BACKGROUND_WR(pxp->s0_bgcolor);
+}
+
+static void pxp_set_csc(struct pxps *pxp)
+{
+ if (pxp->yuv) {
+ /* YUV colorspace */
+ HW_PXP_CSCCOEFF0_WR(0x04030000);
+ HW_PXP_CSCCOEFF1_WR(0x01230208);
+ HW_PXP_CSCCOEFF2_WR(0x076b079c);
+ } else {
+ /* YCrCb colorspace */
+ HW_PXP_CSCCOEFF0_WR(0x84ab01f0);
+ HW_PXP_CSCCOEFF1_WR(0x01230204);
+ HW_PXP_CSCCOEFF2_WR(0x0730079c);
+ }
+}
+
+static int pxp_set_cstate(struct pxps *pxp, struct v4l2_control *vc)
+{
+
+ if (vc->id == V4L2_CID_HFLIP)
+ pxp->hflip = vc->value;
+ else if (vc->id == V4L2_CID_VFLIP)
+ pxp->vflip = vc->value;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE) {
+ if (vc->value % 90)
+ return -ERANGE;
+ pxp->rotate = vc->value;
+ } else if (vc->id == V4L2_CID_PRIVATE_BASE + 1) {
+ pxp->s0_bgcolor = vc->value;
+ pxp_set_s0bg(pxp);
+ } else if (vc->id == V4L2_CID_PRIVATE_BASE + 2) {
+ pxp->s0_chromakey = vc->value;
+ pxp_set_s0colorkey(pxp);
+ } else if (vc->id == V4L2_CID_PRIVATE_BASE + 3) {
+ pxp->yuv = vc->value;
+ pxp_set_csc(pxp);
+ }
+
+ pxp_set_ctrl(pxp);
+
+ return 0;
+}
+
+static int pxp_get_cstate(struct pxps *pxp, struct v4l2_control *vc)
+{
+ if (vc->id == V4L2_CID_HFLIP)
+ vc->value = pxp->hflip;
+ else if (vc->id == V4L2_CID_VFLIP)
+ vc->value = pxp->vflip;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE)
+ vc->value = pxp->rotate;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE + 1)
+ vc->value = pxp->s0_bgcolor;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE + 2)
+ vc->value = pxp->s0_chromakey;
+ else if (vc->id == V4L2_CID_PRIVATE_BASE + 3)
+ vc->value = pxp->yuv;
+
+ return 0;
+}
+
+static int pxp_enumoutput(struct file *file, void *fh,
+ struct v4l2_output *o)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ if ((o->index < 0) || (o->index > 1))
+ return -EINVAL;
+
+ memset(o, 0, sizeof(struct v4l2_output));
+ if (o->index == 0) {
+ strcpy(o->name, "PxP Display Output");
+ pxp->output = 0;
+ } else {
+ strcpy(o->name, "PxP Virtual Output");
+ pxp->output = 1;
+ }
+ o->type = V4L2_OUTPUT_TYPE_INTERNAL;
+ o->std = 0;
+ o->reserved[0] = pxp->outb_phys;
+
+ return 0;
+}
+
+static int pxp_g_output(struct file *file, void *fh,
+ unsigned int *i)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ *i = pxp->output;
+
+ return 0;
+}
+
+static int pxp_s_output(struct file *file, void *fh,
+ unsigned int i)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_pix_format *fmt = &pxp->fb.fmt;
+ int bpp;
+
+ if ((i < 0) || (i > 1))
+ return -EINVAL;
+
+ if (pxp->outb)
+ goto out;
+
+ /* Output buffer is same format as fbdev */
+ if (fmt->pixelformat == V4L2_PIX_FMT_RGB24)
+ bpp = 4;
+ else
+ bpp = 2;
+
+ pxp->outb = kmalloc(fmt->width * fmt->height * bpp, GFP_KERNEL);
+ pxp->outb_phys = virt_to_phys(pxp->outb);
+ dma_map_single(NULL, pxp->outb,
+ fmt->width * fmt->height * bpp, DMA_TO_DEVICE);
+
+out:
+ pxp_set_rgbbuf(pxp);
+
+ return 0;
+}
+
+static int pxp_enum_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ enum v4l2_buf_type type = fmt->type;
+ int index = fmt->index;
+
+ if ((fmt->index < 0) || (fmt->index >= ARRAY_SIZE(pxp_s0_formats)))
+ return -EINVAL;
+
+ memset(fmt, 0, sizeof(struct v4l2_fmtdesc));
+ fmt->index = index;
+ fmt->type = type;
+ fmt->pixelformat = pxp_s0_formats[index].fourcc;
+ strcpy(fmt->description, pxp_s0_formats[index].name);
+
+ return 0;
+}
+
+static int pxp_g_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct pxp_data_format *fmt = pxp->s0_fmt;
+
+ pf->width = pxp->s0_width;
+ pf->height = pxp->s0_height;
+ pf->pixelformat = fmt->fourcc;
+ pf->field = V4L2_FIELD_NONE;
+ pf->bytesperline = fmt->bpp * pf->width;
+ pf->sizeimage = pf->bytesperline * pf->height;
+ pf->colorspace = fmt->colorspace;
+ pf->priv = 0;
+
+ return 0;
+}
+
+static struct pxp_data_format *pxp_get_format(struct v4l2_format *f)
+{
+ struct pxp_data_format *fmt;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pxp_s0_formats); i++) {
+ fmt = &pxp_s0_formats[i];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(pxp_s0_formats))
+ return NULL;
+
+ return &pxp_s0_formats[i];
+}
+
+static int pxp_try_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int w = f->fmt.pix.width;
+ int h = f->fmt.pix.height;
+ struct pxp_data_format *fmt = pxp_get_format(f);
+
+ if (!fmt)
+ return -EINVAL;
+
+ w = min(w, 2040);
+ w = max(w, 8);
+ h = min(h, 2040);
+ h = max(h, 8);
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = w;
+ f->fmt.pix.height = h;
+ f->fmt.pix.pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int pxp_s_fmt_video_output(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+ int ret = pxp_try_fmt_video_output(file, fh, f);
+
+ if (ret == 0) {
+ pxp->s0_fmt = pxp_get_format(f);
+ pxp->s0_width = pf->width;
+ pxp->s0_height = pf->height;
+ pxp_set_ctrl(pxp);
+ pxp_set_s0param(pxp);
+ }
+
+ return ret;
+}
+
+static int pxp_g_fmt_output_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_window *wf = &f->fmt.win;
+
+ memset(wf, 0, sizeof(struct v4l2_window));
+ wf->chromakey = pxp->s1_chromakey;
+ wf->global_alpha = pxp->global_alpha;
+ wf->field = V4L2_FIELD_NONE;
+ wf->clips = NULL;
+ wf->clipcount = 0;
+ wf->bitmap = NULL;
+ wf->w.left = pxp->srect.left;
+ wf->w.top = pxp->srect.top;
+ wf->w.width = pxp->srect.width;
+ wf->w.height = pxp->srect.height;
+
+ return 0;
+}
+
+static int pxp_try_fmt_output_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_window *wf = &f->fmt.win;
+ struct v4l2_rect srect;
+ u32 s1_chromakey = wf->chromakey;
+ u8 global_alpha = wf->global_alpha;
+
+ memcpy(&srect, &(wf->w), sizeof(struct v4l2_rect));
+
+ pxp_g_fmt_output_overlay(file, fh, f);
+
+ wf->chromakey = s1_chromakey;
+ wf->global_alpha = global_alpha;
+
+ /* Constrain parameters to the input buffer */
+ wf->w.left = srect.left;
+ wf->w.top = srect.top;
+ wf->w.width = min(srect.width, ((__s32)pxp->s0_width - wf->w.left));
+ wf->w.height = min(srect.height, ((__s32)pxp->s0_height - wf->w.top));
+
+ return 0;
+}
+
+static int pxp_s_fmt_output_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ struct v4l2_window *wf = &f->fmt.win;
+ int ret = pxp_try_fmt_output_overlay(file, fh, f);
+
+ if (ret == 0) {
+ pxp->srect.left = wf->w.left;
+ pxp->srect.top = wf->w.top;
+ pxp->srect.width = wf->w.width;
+ pxp->srect.height = wf->w.height;
+ pxp->global_alpha = wf->global_alpha;
+ pxp->s1_chromakey = wf->chromakey;
+ pxp_set_s0param(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_scaling(pxp);
+ pxp_set_olparam(pxp);
+ pxp_set_s1colorkey(pxp);
+ }
+
+ return ret;
+}
+
+static int pxp_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *r)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_reqbufs(&pxp->s0_vbq, r);
+}
+
+static int pxp_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_querybuf(&pxp->s0_vbq, b);
+}
+
+static int pxp_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_qbuf(&pxp->s0_vbq, b);
+}
+
+static int pxp_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ return videobuf_dqbuf(&pxp->s0_vbq, b, file->f_flags & O_NONBLOCK);
+}
+
+static int pxp_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type t)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret = 0;
+
+ if ((t != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ ret = videobuf_streamon(&pxp->s0_vbq);
+
+ if (!ret && (pxp->output == 0))
+ stmp3xxxfb_cfg_pxp(1, pxp->outb_phys);
+
+ return ret;
+}
+
+static int pxp_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type t)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret = 0;
+
+ if ((t != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ ret = videobuf_streamoff(&pxp->s0_vbq);
+
+ if (!ret)
+ stmp3xxxfb_cfg_pxp(0, 0);
+
+ return ret;
+}
+
+static int pxp_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned *size)
+{
+ struct pxps *pxp = q->priv_data;
+
+ *size = pxp->s0_width * pxp->s0_height * pxp->s0_fmt->bpp;
+
+ if (0 == *count)
+ *count = PXP_DEF_BUFS;
+
+ return 0;
+}
+
+static void pxp_buf_free(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ if (in_interrupt())
+ BUG();
+
+ videobuf_dma_contig_free(q, vb);
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int pxp_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct pxps *pxp = q->priv_data;
+ int ret = 0;
+
+ vb->width = pxp->s0_width;
+ vb->height = pxp->s0_height;
+ vb->size = vb->width * vb->height * pxp->s0_fmt->bpp;
+ vb->field = V4L2_FIELD_NONE;
+ vb->state = VIDEOBUF_NEEDS_INIT;
+
+
+ ret = videobuf_iolock(q, vb, NULL);
+ if (ret)
+ goto fail;
+ vb->state = VIDEOBUF_PREPARED;
+
+ return 0;
+
+fail:
+ pxp_buf_free(q, vb);
+ return ret;
+}
+
+static void pxp_buf_output(struct pxps *pxp)
+{
+ dma_addr_t Y, U, V;
+
+ if (pxp->active) {
+ pxp->active->state = VIDEOBUF_ACTIVE;
+ Y = videobuf_to_dma_contig(pxp->active);
+ HW_PXP_S0BUF_WR(Y);
+ if ((pxp->s0_fmt->fourcc == V4L2_PIX_FMT_YUV420) ||
+ (pxp->s0_fmt->fourcc == V4L2_PIX_FMT_YUV422P)) {
+ int s = 1; /* default to YUV 4:2:2 */
+ if (pxp->s0_fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ s = 2;
+ U = Y + (pxp->s0_width * pxp->s0_height);
+ V = U + ((pxp->s0_width * pxp->s0_height) >> s);
+ HW_PXP_S0UBUF_WR(U);
+ HW_PXP_S0VBUF_WR(V);
+ }
+ HW_PXP_CTRL_SET(BM_PXP_CTRL_ENABLE);
+ }
+}
+
+static void pxp_buf_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct pxps *pxp = q->priv_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pxp->lock, flags);
+
+ list_add_tail(&vb->queue, &pxp->outq);
+ vb->state = VIDEOBUF_QUEUED;
+
+ if (!pxp->active) {
+ pxp->active = vb;
+ pxp_buf_output(pxp);
+ }
+
+ spin_unlock_irqrestore(&pxp->lock, flags);
+}
+
+static void pxp_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ pxp_buf_free(q, vb);
+}
+
+static struct videobuf_queue_ops pxp_vbq_ops = {
+ .buf_setup = pxp_buf_setup,
+ .buf_prepare = pxp_buf_prepare,
+ .buf_queue = pxp_buf_queue,
+ .buf_release = pxp_buf_release,
+};
+
+static int pxp_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ memset(cap, 0, sizeof(*cap));
+ strcpy(cap->driver, "pxp");
+ strcpy(cap->card, "pxp");
+ strlcpy(cap->bus_info, pxp->pdev->dev.bus_id, sizeof(cap->bus_info));
+
+ cap->version = (PXP_DRIVER_MAJOR << 8) + PXP_DRIVER_MINOR;
+
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_OUTPUT_OVERLAY |
+ V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int pxp_g_fbuf(struct file *file, void *priv,
+ struct v4l2_framebuffer *fb)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ memset(fb, 0, sizeof(*fb));
+
+ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY |
+ V4L2_FBUF_CAP_CHROMAKEY |
+ V4L2_FBUF_CAP_LOCAL_ALPHA |
+ V4L2_FBUF_CAP_GLOBAL_ALPHA;
+
+ if (pxp->global_alpha_state)
+ fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+ if (pxp->local_alpha_state)
+ fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ if (pxp->s1_chromakey_state)
+ fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+
+ return 0;
+}
+
+static int pxp_s_fbuf(struct file *file, void *priv,
+ struct v4l2_framebuffer *fb)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ pxp->overlay_state =
+ (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0;
+ pxp->global_alpha_state =
+ (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+ pxp->local_alpha_state =
+ (fb->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) != 0;
+ /* Global alpha overrides local alpha if both are requested */
+ if (pxp->global_alpha_state && pxp->local_alpha_state)
+ pxp->local_alpha_state = 0;
+ pxp->s1_chromakey_state =
+ (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+
+ pxp_set_olparam(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_scaling(pxp);
+
+ return 0;
+}
+
+static int pxp_g_crop(struct file *file, void *fh,
+ struct v4l2_crop *c)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ if (c->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY)
+ return -EINVAL;
+
+ c->c.left = pxp->drect.left;
+ c->c.top = pxp->drect.top;
+ c->c.width = pxp->drect.width;
+ c->c.height = pxp->drect.height;
+
+ return 0;
+}
+
+static int pxp_s_crop(struct file *file, void *fh,
+ struct v4l2_crop *c)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int l = c->c.left;
+ int t = c->c.top;
+ int w = c->c.width;
+ int h = c->c.height;
+ int fbw = pxp->fb.fmt.width;
+ int fbh = pxp->fb.fmt.height;
+
+ if (c->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY)
+ return -EINVAL;
+
+ /* Constrain parameters to FB limits */
+ w = min(w, fbw);
+ w = max(w, PXP_MIN_PIX);
+ h = min(h, fbh);
+ h = max(h, PXP_MIN_PIX);
+ if ((l + w) > fbw)
+ l = 0;
+ if ((t + h) > fbh)
+ t = 0;
+
+ /* Round up values to PxP pixel block */
+ l = roundup(l, PXP_MIN_PIX);
+ t = roundup(t, PXP_MIN_PIX);
+ w = roundup(w, PXP_MIN_PIX);
+ h = roundup(h, PXP_MIN_PIX);
+
+ pxp->drect.left = l;
+ pxp->drect.top = t;
+ pxp->drect.width = w;
+ pxp->drect.height = h;
+
+ pxp_set_s0param(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_scaling(pxp);
+
+ return 0;
+}
+
+static int pxp_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+ if (qc->id && qc->id == pxp_controls[i].id) {
+ memcpy(qc, &(pxp_controls[i]), sizeof(*qc));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int pxp_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *vc)
+{
+ int i;
+
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+ if (vc->id == pxp_controls[i].id)
+ return pxp_get_cstate(pxp, vc);
+
+ return -EINVAL;
+}
+
+static int pxp_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *vc)
+{
+ int i;
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ for (i = 0; i < ARRAY_SIZE(pxp_controls); i++)
+ if (vc->id == pxp_controls[i].id) {
+ if (vc->value < pxp_controls[i].minimum ||
+ vc->value > pxp_controls[i].maximum)
+ return -ERANGE;
+ return pxp_set_cstate(pxp, vc);
+ }
+
+ return -EINVAL;
+}
+
+void pxp_release(struct video_device *vfd)
+{
+ struct pxps *pxp = video_get_drvdata(vfd);
+
+ spin_lock(&pxp->lock);
+ video_device_release(vfd);
+ spin_unlock(&pxp->lock);
+}
+
+static int pxp_hw_init(struct pxps *pxp)
+{
+ struct fb_var_screeninfo var;
+ struct fb_fix_screeninfo fix;
+ int err;
+
+ err = stmp3xxxfb_get_info(&var, &fix);
+ if (err)
+ return err;
+
+ /* Pull PxP out of reset */
+ HW_PXP_CTRL_WR(0);
+
+ /* Config defaults */
+ pxp->active = NULL;
+
+ pxp->s0_fmt = &pxp_s0_formats[0];
+ pxp->drect.left = pxp->srect.left = 0;
+ pxp->drect.top = pxp->srect.top = 0;
+ pxp->drect.width = pxp->srect.width = pxp->s0_width = var.xres;
+ pxp->drect.height = pxp->srect.height = pxp->s0_height = var.yres;
+ pxp->s0_bgcolor = 0;
+
+ pxp->output = 0;
+ err = pxp_set_fbinfo(pxp);
+ if (err)
+ return err;
+
+ pxp->scaling = 0;
+ pxp->hflip = 0;
+ pxp->vflip = 0;
+ pxp->rotate = 0;
+ pxp->yuv = 0;
+
+ pxp->overlay_state = 0;
+ pxp->global_alpha_state = 0;
+ pxp->global_alpha = 0;
+ pxp->local_alpha_state = 0;
+ pxp->s1_chromakey_state = 0;
+ pxp->s1_chromakey = -1;
+ pxp->s0_chromakey = -1;
+
+ /* Write default h/w config */
+ pxp_set_ctrl(pxp);
+ pxp_set_s0param(pxp);
+ pxp_set_s0crop(pxp);
+ pxp_set_oln(pxp);
+ pxp_set_olparam(pxp);
+ pxp_set_s0colorkey(pxp);
+ pxp_set_s1colorkey(pxp);
+ pxp_set_csc(pxp);
+
+ return 0;
+}
+
+static int pxp_open(struct inode *inode, struct file *file)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret = 0;
+
+ mutex_lock(&pxp->mutex);
+ pxp->users++;
+
+ if (pxp->users > 1) {
+ pxp->users--;
+ ret = -EBUSY;
+ goto out;
+ }
+
+out:
+ mutex_unlock(&pxp->mutex);
+ if (ret)
+ return ret;
+
+ videobuf_queue_dma_contig_init(&pxp->s0_vbq,
+ &pxp_vbq_ops,
+ &pxp->pdev->dev,
+ &pxp->lock,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer),
+ pxp);
+
+ return 0;
+}
+
+static int pxp_close(struct inode *inode, struct file *file)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+
+ videobuf_stop(&pxp->s0_vbq);
+ videobuf_mmap_free(&pxp->s0_vbq);
+
+ mutex_lock(&pxp->mutex);
+ pxp->users--;
+ mutex_unlock(&pxp->mutex);
+
+ return 0;
+}
+
+static int pxp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct pxps *pxp = video_get_drvdata(video_devdata(file));
+ int ret;
+
+ ret = videobuf_mmap_mapper(&pxp->s0_vbq, vma);
+
+ return ret;
+}
+
+static const struct file_operations pxp_fops = {
+ .owner = THIS_MODULE,
+ .open = pxp_open,
+ .release = pxp_close,
+ .ioctl = video_ioctl2,
+ .mmap = pxp_mmap,
+};
+
+static const struct v4l2_ioctl_ops pxp_ioctl_ops = {
+ .vidioc_querycap = pxp_querycap,
+
+ .vidioc_reqbufs = pxp_reqbufs,
+ .vidioc_querybuf = pxp_querybuf,
+ .vidioc_qbuf = pxp_qbuf,
+ .vidioc_dqbuf = pxp_dqbuf,
+
+ .vidioc_streamon = pxp_streamon,
+ .vidioc_streamoff = pxp_streamoff,
+
+ .vidioc_enum_output = pxp_enumoutput,
+ .vidioc_g_output = pxp_g_output,
+ .vidioc_s_output = pxp_s_output,
+
+ .vidioc_enum_fmt_vid_out = pxp_enum_fmt_video_output,
+ .vidioc_try_fmt_vid_out = pxp_try_fmt_video_output,
+ .vidioc_g_fmt_vid_out = pxp_g_fmt_video_output,
+ .vidioc_s_fmt_vid_out = pxp_s_fmt_video_output,
+
+ .vidioc_try_fmt_vid_out_overlay = pxp_try_fmt_output_overlay,
+ .vidioc_g_fmt_vid_out_overlay = pxp_g_fmt_output_overlay,
+ .vidioc_s_fmt_vid_out_overlay = pxp_s_fmt_output_overlay,
+
+ .vidioc_g_fbuf = pxp_g_fbuf,
+ .vidioc_s_fbuf = pxp_s_fbuf,
+
+ .vidioc_g_crop = pxp_g_crop,
+ .vidioc_s_crop = pxp_s_crop,
+
+ .vidioc_queryctrl = pxp_queryctrl,
+ .vidioc_g_ctrl = pxp_g_ctrl,
+ .vidioc_s_ctrl = pxp_s_ctrl,
+};
+
+static const struct video_device pxp_template = {
+ .name = "PxP",
+ .vfl_type = VID_TYPE_OVERLAY |
+ VID_TYPE_CLIPPING |
+ VID_TYPE_SCALES,
+ .fops = &pxp_fops,
+ .release = pxp_release,
+ .minor = -1,
+ .ioctl_ops = &pxp_ioctl_ops,
+};
+
+static irqreturn_t pxp_irq(int irq, void *dev_id)
+{
+ struct pxps *pxp = (struct pxps *)dev_id;
+ struct videobuf_buffer *vb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pxp->lock, flags);
+
+ HW_PXP_STAT_CLR(BM_PXP_STAT_IRQ);
+
+ vb = pxp->active;
+ vb->state = VIDEOBUF_DONE;
+ do_gettimeofday(&vb->ts);
+ vb->field_count++;
+
+ list_del_init(&vb->queue);
+
+ if (list_empty(&pxp->outq)) {
+ pxp->active = NULL;
+ goto out;
+ }
+
+ pxp->active = list_entry(pxp->outq.next,
+ struct videobuf_buffer,
+ queue);
+
+ pxp_buf_output(pxp);
+
+out:
+ wake_up(&vb->done);
+
+ spin_unlock_irqrestore(&pxp->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int pxp_probe(struct platform_device *pdev)
+{
+ struct pxps *pxp;
+ struct resource *res;
+ int irq;
+ int err = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || irq < 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ pxp = kzalloc(sizeof(*pxp), GFP_KERNEL);
+ if (!pxp) {
+ dev_err(&pdev->dev, "failed to allocate control object\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ dev_set_drvdata(&pdev->dev, pxp);
+ pxp->res = res;
+ pxp->irq = irq;
+
+ INIT_LIST_HEAD(&pxp->outq);
+ spin_lock_init(&pxp->lock);
+ mutex_init(&pxp->mutex);
+
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ PXP_DRIVER_NAME)) {
+ err = -EBUSY;
+ goto freepxp;
+ }
+
+ pxp->regs = (void __iomem *)res->start; /* it is already ioremapped */
+ pxp->pdev = pdev;
+
+ err = request_irq(pxp->irq, pxp_irq, 0, PXP_DRIVER_NAME, pxp);
+
+ if (err) {
+ dev_err(&pdev->dev, "interrupt register failed\n");
+ goto release;
+ }
+
+ pxp->vdev = video_device_alloc();
+ if (!pxp->vdev) {
+ dev_err(&pdev->dev, "video_device_alloc() failed\n");
+ err = -ENOMEM;
+ goto freeirq;
+ }
+
+ memcpy(pxp->vdev, &pxp_template, sizeof(pxp_template));
+ video_set_drvdata(pxp->vdev, pxp);
+
+ err = video_register_device(pxp->vdev, VFL_TYPE_GRABBER, 0);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register video device\n");
+ goto freevdev;
+ }
+
+ err = pxp_hw_init(pxp);
+ if (err) {
+ dev_err(&pdev->dev, "failed to initialize hardware\n");
+ goto freevdev;
+ }
+
+ dev_info(&pdev->dev, "initialized\n");
+
+exit:
+ return err;
+
+freevdev:
+ video_device_release(pxp->vdev);
+
+freeirq:
+ free_irq(pxp->irq, pxp);
+
+release:
+ release_mem_region(res->start, res->end - res->start + 1);
+
+freepxp:
+ kfree(pxp);
+
+ return err;
+}
+
+static int __devexit pxp_remove(struct platform_device *pdev)
+{
+ struct pxps *pxp = platform_get_drvdata(pdev);
+
+ video_unregister_device(pxp->vdev);
+ video_device_release(pxp->vdev);
+
+ kfree(pxp->outb);
+ kfree(pxp);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pxp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int i;
+
+ while (HW_PXP_CTRL_RD() & BM_PXP_CTRL_ENABLE)
+ ;
+
+ for (i = 0; i < REGS1_NUMS; i++)
+ regs1[i] = __raw_readl(HW_PXP_CTRL_ADDR + REG_OFFSET * i);
+
+ for (i = 0; i < REGS2_NUMS; i++)
+ regs2[i] = __raw_readl(HW_PXP_PAGETABLE_ADDR + REG_OFFSET * i);
+
+ for (i = 0; i < REGS3_NUMS; i++)
+ regs3[i] = __raw_readl(HW_PXP_OL0_ADDR + REG_OFFSET * i);
+
+ HW_PXP_CTRL_SET(BM_PXP_CTRL_SFTRST);
+
+ return 0;
+}
+
+static int pxp_resume(struct platform_device *pdev)
+{
+ int i;
+
+ /* Pull PxP out of reset */
+ HW_PXP_CTRL_WR(0);
+
+ for (i = 0; i < REGS1_NUMS; i++)
+ __raw_writel(regs1[i], HW_PXP_CTRL_ADDR + REG_OFFSET * i);
+
+ for (i = 0; i < REGS2_NUMS; i++)
+ __raw_writel(regs2[i], HW_PXP_PAGETABLE_ADDR + REG_OFFSET * i);
+
+ for (i = 0; i < REGS3_NUMS; i++)
+ __raw_writel(regs3[i], HW_PXP_OL0_ADDR + REG_OFFSET * i);
+
+ return 0;
+}
+#else
+#define pxp_suspend NULL
+#define pxp_resume NULL
+#endif
+
+static struct platform_driver pxp_driver = {
+ .driver = {
+ .name = PXP_DRIVER_NAME,
+ },
+ .probe = pxp_probe,
+ .remove = __exit_p(pxp_remove),
+ .suspend = pxp_suspend,
+ .resume = pxp_resume,
+};
+
+
+static int __devinit pxp_init(void)
+{
+ return platform_driver_register(&pxp_driver);
+}
+
+static void __exit pxp_exit(void)
+{
+ platform_driver_unregister(&pxp_driver);
+}
+
+module_init(pxp_init);
+module_exit(pxp_exit);
+
+MODULE_DESCRIPTION("STMP37xx PxP driver");
+MODULE_AUTHOR("Matt Porter <mporter@embeddedalley.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/pxp.h b/drivers/media/video/pxp.h
new file mode 100644
index 000000000000..4ac191fa7d65
--- /dev/null
+++ b/drivers/media/video/pxp.h
@@ -0,0 +1,76 @@
+/*
+ * Freescale STMP378X PxP driver
+ *
+ * Author: Matt Porter <mporter@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+struct pxps {
+ struct platform_device *pdev;
+ struct resource *res;
+ int irq;
+ void __iomem *regs;
+
+ spinlock_t lock;
+ struct mutex mutex;
+ int users;
+
+ struct video_device *vdev;
+
+ struct videobuf_queue s0_vbq;
+ struct videobuf_buffer *active;
+ struct list_head outq;
+
+ int output;
+ u32 *outb;
+ dma_addr_t outb_phys;
+
+ /* Current S0 configuration */
+ struct pxp_data_format *s0_fmt;
+ u32 s0_width;
+ u32 s0_height;
+ u32 s0_bgcolor;
+ u32 s0_chromakey;
+
+ struct v4l2_framebuffer fb;
+ struct v4l2_rect drect;
+ struct v4l2_rect srect;
+
+ /* Transformation support */
+ int scaling;
+ int hflip;
+ int vflip;
+ int rotate;
+ int yuv;
+
+ /* Output overlay support */
+ int overlay_state;
+ int global_alpha_state;
+ u8 global_alpha;
+ int local_alpha_state;
+ int s1_chromakey_state;
+ u32 s1_chromakey;
+};
+
+struct pxp_data_format {
+ char *name;
+ unsigned int bpp;
+ u32 fourcc;
+ enum v4l2_colorspace colorspace;
+ u32 ctrl_s0_fmt;
+};
+
+extern int stmp3xxxfb_get_info(struct fb_var_screeninfo *var,
+ struct fb_fix_screeninfo *fix);
+extern void stmp3xxxfb_cfg_pxp(int enable, dma_addr_t pxp_phys);
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index 31944b11e6ea..eb02786795b4 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -255,7 +255,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
mem->size = PAGE_ALIGN(q->bufs[first]->bsize);
mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
- &mem->dma_handle, GFP_KERNEL);
+ &mem->dma_handle, GFP_KERNEL | GFP_DMA);
if (!mem->vaddr) {
dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
mem->size);
@@ -269,7 +269,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
size = vma->vm_end - vma->vm_start;
size = (size < mem->size) ? size : mem->size;
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_page_prot = pgprot_writethru(vma->vm_page_prot);
retval = remap_pfn_range(vma, vma->vm_start,
mem->dma_handle >> PAGE_SHIFT,
size, vma->vm_page_prot);
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index 0d47fb9e4b3b..3a23bcb3c77e 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -80,10 +80,6 @@ static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs,
/* Cache is CPU endian */
dest[i - reg] = be16_to_cpu(dest[i - reg]);
- /* Satisfy non-volatile bits from cache */
- dest[i - reg] &= wm8350_reg_io_map[i].vol;
- dest[i - reg] |= wm8350->reg_cache[i];
-
/* Mask out non-readable bits */
dest[i - reg] &= wm8350_reg_io_map[i].readable;
}
@@ -183,9 +179,6 @@ static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
(wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable)
| src[i - reg];
- /* Don't store volatile bits */
- wm8350->reg_cache[i] &= ~wm8350_reg_io_map[i].vol;
-
src[i - reg] = cpu_to_be16(src[i - reg]);
}
@@ -1123,7 +1116,6 @@ static int wm8350_create_cache(struct wm8350 *wm8350, int mode)
}
value = be16_to_cpu(value);
value &= wm8350_reg_io_map[i].readable;
- value &= ~wm8350_reg_io_map[i].vol;
wm8350->reg_cache[i] = value;
} else
wm8350->reg_cache[i] = reg_map[i];
@@ -1186,9 +1178,14 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
dev_info(wm8350->dev, "Found Rev G device\n");
wm8350->rev = WM8350_REV_G;
break;
+ case WM8350_REV_H:
+ dev_info(wm8350->dev, "Found Rev H device\n");
+ wm8350->rev = WM8350_REV_H;
+ break;
default:
/* For safety we refuse to run on unknown hardware */
- dev_info(wm8350->dev, "Found unknown rev\n");
+ dev_info(wm8350->dev, "Found unknown rev %x\n",
+ (id2 & WM8350_CHIP_REV_MASK) >> 12);
ret = -ENODEV;
goto err;
}
@@ -1233,8 +1230,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
goto err;
}
wm8350->chip_irq = irq;
-
- wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0);
+ /*mask gpio and rtc interrupt*/
+ wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x50);
wm8350_client_dev_register(wm8350, "wm8350-codec",
&(wm8350->codec.pdev));
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 3f2a912659af..10ba9a035a58 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -50,3 +50,15 @@ config MMC_TEST
This driver is only of interest to those developing or
testing a host driver. Most people should say N here.
+
+config SDIO_UNIFI_FS
+ tristate "UniFi SDIO glue for Freescale MMC/SDIO"
+ depends on (MMC_MXC || MMC_IMX_ESDHCI)
+ depends on (MACH_MX31_3DS || MACH_MX35_3DS || MACH_MX37_3DS || MACH_MX51_3DS)
+ help
+ This provides an interface between the CSR UniFi WiFi
+ driver and the Freescale MMC/SDIO interface.
+ If you have a MXC platform with a UniFi WiFi chip,
+ say M here.
+
+ If unsure, say N.
diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile
index 0d407514f67d..bbcf742d0ea1 100644
--- a/drivers/mmc/card/Makefile
+++ b/drivers/mmc/card/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_MMC_TEST) += mmc_test.o
obj-$(CONFIG_SDIO_UART) += sdio_uart.o
+obj-$(CONFIG_SDIO_UNIFI_FS) += unifi_fs/
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 3d067c35185d..a2a81f38ec10 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -236,6 +236,19 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
brq.data.blocks = req->nr_sectors;
+ /*
+ * @BUG: The block-layer is able to modify the minimum number of
+ * transfer blocks for one request (see [1]). At this moment, it sets
+ * this number to eigth, which is a problem for controllers that only
+ * supports single-transfers.
+ *
+ * [1] block/blk-settings.c:blk_queue_max_sectors()
+ *
+ * (Luis Galdos)
+ */
+ if (brq.data.blocks > card->host->max_blk_count)
+ brq.data.blocks = card->host->max_blk_count;
+
if (brq.data.blocks > 1) {
/* SPI multiblock writes terminate using a special
* token, not a STOP_TRANSMISSION request.
@@ -266,6 +279,27 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
mmc_queue_bounce_pre(mq);
+ /*
+ * Adjust the sg list so it is the same size as the
+ * request.
+ * @BUG: See above description (Luis Galdos)
+ */
+ if (brq.data.blocks != req->nr_sectors) {
+ int data_size, i;
+ struct scatterlist *sg;
+
+ data_size = brq.data.blocks * brq.data.blksz;
+ for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) {
+ data_size -= sg->length;
+ if (data_size <= 0) {
+ sg->length += data_size;
+ i++;
+ break;
+ }
+ }
+ brq.data.sg_len = i;
+ }
+
mmc_wait_for_req(card->host, &brq.mrq);
mmc_queue_bounce_post(mq);
diff --git a/drivers/mmc/card/unifi_fs/Makefile b/drivers/mmc/card/unifi_fs/Makefile
new file mode 100644
index 000000000000..381d4a2d1fd5
--- /dev/null
+++ b/drivers/mmc/card/unifi_fs/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SDIO_UNIFI_FS) = unifi_fs.o
+unifi_fs-objs = fs_lx.o
diff --git a/drivers/mmc/card/unifi_fs/fs_lx.c b/drivers/mmc/card/unifi_fs/fs_lx.c
new file mode 100644
index 000000000000..fd5657299809
--- /dev/null
+++ b/drivers/mmc/card/unifi_fs/fs_lx.c
@@ -0,0 +1,684 @@
+/*
+ * fs_lx.c - Freescale SDIO glue module for UniFi.
+ *
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ * Important:
+ * This module does not support more than one device driver instances.
+ *
+ */
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+/*
+ * 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
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/scatterlist.h>
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <mach/mmc.h>
+#include <mach/gpio.h>
+
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include "fs_sdio_api.h"
+
+struct regulator_unifi {
+ struct regulator *reg_gpo1;
+ struct regulator *reg_gpo2;
+ struct regulator *reg_1v5_ana_bb;
+ struct regulator *reg_vdd_vpa;
+ struct regulator *reg_1v5_dd;
+};
+
+static struct sdio_driver sdio_unifi_driver;
+
+static int fs_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id);
+static void fs_sdio_remove(struct sdio_func *func);
+static void fs_sdio_irq(struct sdio_func *func);
+static int fs_sdio_suspend(struct device *dev, pm_message_t state);
+static int fs_sdio_resume(struct device *dev);
+static int do_sdio_hard_reset(struct sdio_dev *fdev);
+
+/* Globals to store the context to this module and the device driver */
+static struct sdio_dev *available_sdio_dev;
+static struct fs_driver *available_driver;
+struct mxc_unifi_platform_data *plat_data;
+
+extern void mxc_mmc_force_detect(int id);
+
+enum sdio_cmd_direction {
+ CMD_READ,
+ CMD_WRITE,
+};
+
+static int fsl_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
+ unsigned addr, u8 in, u8 *out)
+{
+ struct mmc_command cmd;
+ int err;
+
+ BUG_ON(!card);
+ BUG_ON(fn > 7);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = SD_IO_RW_DIRECT;
+ cmd.arg = write ? 0x80000000 : 0x00000000;
+ cmd.arg |= fn << 28;
+ cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;
+ cmd.arg |= addr << 9;
+ cmd.arg |= in;
+ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err)
+ return err;
+
+ if (mmc_host_is_spi(card->host)) {
+ /* host driver already reported errors */
+ } else {
+ if (cmd.resp[0] & R5_ERROR)
+ return -EIO;
+ if (cmd.resp[0] & R5_FUNCTION_NUMBER)
+ return -EINVAL;
+ if (cmd.resp[0] & R5_OUT_OF_RANGE)
+ return -ERANGE;
+ }
+
+ if (out) {
+ if (mmc_host_is_spi(card->host))
+ *out = (cmd.resp[0] >> 8) & 0xFF;
+ else
+ *out = cmd.resp[0] & 0xFF;
+ }
+
+ return 0;
+}
+
+
+int fs_sdio_readb(struct sdio_dev *fdev, int funcnum, unsigned long addr,
+ unsigned char *pdata)
+{
+ int err;
+ char val;
+
+ sdio_claim_host(fdev->func);
+ if (funcnum == 0)
+ val = sdio_f0_readb(fdev->func, (unsigned int)addr, &err);
+ else
+ val = sdio_readb(fdev->func, (unsigned int)addr, &err);
+ sdio_release_host(fdev->func);
+ if (!err)
+ *pdata = val;
+ else
+ printk(KERN_ERR "fs_lx: readb error,fun=%d,addr=%d,data=%d,"
+ "err=%d\n", funcnum, (int)addr, *pdata, err);
+
+ return err;
+}
+EXPORT_SYMBOL(fs_sdio_readb);
+
+int fs_sdio_writeb(struct sdio_dev *fdev, int funcnum, unsigned long addr,
+ unsigned char data)
+{
+ int err;
+
+ sdio_claim_host(fdev->func);
+ if (funcnum == 0)
+ err = fsl_io_rw_direct(fdev->func->card, 1, 0, addr,
+ data, NULL);
+ else
+ sdio_writeb(fdev->func, data, (unsigned int)addr, &err);
+ sdio_release_host(fdev->func);
+
+ if (err)
+ printk(KERN_ERR "fs_lx: writeb error,fun=%d,addr=%d,data=%d,"
+ "err=%d\n", funcnum, (int)addr, data, err);
+ return err;
+}
+EXPORT_SYMBOL(fs_sdio_writeb);
+
+int fs_sdio_block_rw(struct sdio_dev *fdev, int funcnum, unsigned long addr,
+ unsigned char *pdata, unsigned int count, int direction)
+{
+ int err;
+
+ sdio_claim_host(fdev->func);
+ if (direction == CMD_READ)
+ err = sdio_memcpy_fromio(fdev->func, pdata, addr, count);
+ else
+ err = sdio_memcpy_toio(fdev->func, addr, pdata, count);
+ sdio_release_host(fdev->func);
+
+ return err;
+}
+EXPORT_SYMBOL(fs_sdio_block_rw);
+
+int fs_sdio_enable_interrupt(struct sdio_dev *fdev, int enable)
+{
+ struct mmc_host *host = fdev->func->card->host;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fdev->lock, flags);
+ if (enable) {
+ if (!fdev->int_enabled) {
+ fdev->int_enabled = 1;
+ host->ops->enable_sdio_irq(host, 1);
+ }
+ } else {
+ if (fdev->int_enabled) {
+ host->ops->enable_sdio_irq(host, 0);
+ fdev->int_enabled = 0;
+ }
+ }
+ spin_unlock_irqrestore(&fdev->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(fs_sdio_enable_interrupt);
+
+int fs_sdio_disable(struct sdio_dev *fdev)
+{
+ int err;
+ sdio_claim_host(fdev->func);
+ err = sdio_disable_func(fdev->func);
+ sdio_release_host(fdev->func);
+ if (err)
+ printk(KERN_ERR "fs_lx:fs_sdio_disable error,err=%d\n", err);
+ return err;
+}
+EXPORT_SYMBOL(fs_sdio_disable);
+
+int fs_sdio_enable(struct sdio_dev *fdev)
+{
+ int err = 0;
+
+ sdio_claim_host(fdev->func);
+ err = sdio_disable_func(fdev->func);
+ err = sdio_enable_func(fdev->func);
+ sdio_release_host(fdev->func);
+ if (err)
+ printk(KERN_ERR "fs_lx:fs_sdio_enable error,err=%d\n", err);
+ return err;
+}
+EXPORT_SYMBOL(fs_sdio_enable);
+
+int fs_sdio_set_max_clock_speed(struct sdio_dev *fdev, int max_khz)
+{
+ struct mmc_card *card = fdev->func->card;
+
+ /* Respect the host controller's min-max. */
+ max_khz *= 1000;
+ if (max_khz < card->host->f_min)
+ max_khz = card->host->f_min;
+ if (max_khz > card->host->f_max)
+ max_khz = card->host->f_max;
+
+ card->host->ios.clock = max_khz;
+ card->host->ops->set_ios(card->host, &card->host->ios);
+
+ return max_khz / 1000;
+}
+EXPORT_SYMBOL(fs_sdio_set_max_clock_speed);
+
+int fs_sdio_set_block_size(struct sdio_dev *fdev, int blksz)
+{
+ return 0;
+}
+EXPORT_SYMBOL(fs_sdio_set_block_size);
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Turn on the power of WIFI card
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void fs_unifi_power_on(void)
+{
+ struct regulator_unifi *reg_unifi;
+ unsigned int tmp;
+
+ reg_unifi = plat_data->priv;
+
+ if (reg_unifi->reg_gpo1)
+ regulator_enable(reg_unifi->reg_gpo1);
+ if (reg_unifi->reg_gpo2)
+ regulator_enable(reg_unifi->reg_gpo2);
+
+ if (plat_data->enable)
+ plat_data->enable(1);
+
+ if (reg_unifi->reg_1v5_ana_bb) {
+ regulator_set_voltage(reg_unifi->reg_1v5_ana_bb,
+ 1500000, 1500000);
+ regulator_enable(reg_unifi->reg_1v5_ana_bb);
+ }
+ if (reg_unifi->reg_vdd_vpa) {
+ tmp = regulator_get_voltage(reg_unifi->reg_vdd_vpa);
+ if (tmp < 3000000 || tmp > 3600000)
+ regulator_set_voltage(reg_unifi->reg_vdd_vpa,
+ 3000000, 3000000);
+ regulator_enable(reg_unifi->reg_vdd_vpa);
+ }
+ /* WL_1V5DD should come on last, 10ms after other supplies */
+ msleep(10);
+ if (reg_unifi->reg_1v5_dd) {
+ regulator_set_voltage(reg_unifi->reg_1v5_dd,
+ 1500000, 1500000);
+ regulator_enable(reg_unifi->reg_1v5_dd);
+ }
+ msleep(10);
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ *
+ * Turn off the power of WIFI card
+ *
+ * ---------------------------------------------------------------------------
+ */
+static void fs_unifi_power_off(void)
+{
+ struct regulator_unifi *reg_unifi;
+
+ reg_unifi = plat_data->priv;
+ if (reg_unifi->reg_1v5_dd)
+ regulator_disable(reg_unifi->reg_1v5_dd);
+ if (reg_unifi->reg_vdd_vpa)
+ regulator_disable(reg_unifi->reg_vdd_vpa);
+
+ if (reg_unifi->reg_1v5_ana_bb)
+ regulator_disable(reg_unifi->reg_1v5_ana_bb);
+
+ if (plat_data->enable)
+ plat_data->enable(0);
+
+ if (reg_unifi->reg_gpo2)
+ regulator_disable(reg_unifi->reg_gpo2);
+
+ if (reg_unifi->reg_gpo1)
+ regulator_disable(reg_unifi->reg_gpo1);
+}
+
+/* This should be made conditional on being slot 2 too - so we can
+ * use a plug in card in slot 1
+ */
+int fs_sdio_hard_reset(struct sdio_dev *fdev)
+{
+ return 0;
+}
+EXPORT_SYMBOL(fs_sdio_hard_reset);
+
+static const struct sdio_device_id fs_sdio_ids[] = {
+ {SDIO_DEVICE(0x032a, 0x0001)},
+ { /* end: all zeroes */ },
+};
+
+static struct sdio_driver sdio_unifi_driver = {
+ .name = "fs_unifi",
+ .probe = fs_sdio_probe,
+ .remove = fs_sdio_remove,
+ .id_table = fs_sdio_ids,
+ .drv = {
+ .suspend = fs_sdio_suspend,
+ .resume = fs_sdio_resume,
+ }
+};
+
+int fs_sdio_register_driver(struct fs_driver *driver)
+{
+ int ret, retry;
+
+ /* Switch us on, sdio device may exist if power is on by default. */
+ plat_data->hardreset(0);
+ if (available_sdio_dev)
+ mxc_mmc_force_detect(plat_data->host_id);
+ /* Wait for card removed */
+ for (retry = 0; retry < 100; retry++) {
+ if (!available_sdio_dev)
+ break;
+ msleep(100);
+ }
+ if (retry == 100)
+ printk(KERN_ERR "fs_sdio_register_driver: sdio device exists, "
+ "timeout for card removed");
+ fs_unifi_power_on();
+ plat_data->hardreset(1);
+ msleep(500);
+ mxc_mmc_force_detect(plat_data->host_id);
+ for (retry = 0; retry < 100; retry++) {
+ if (available_sdio_dev)
+ break;
+ msleep(50);
+ }
+ if (retry == 100)
+ printk(KERN_ERR "fs_sdio_register_driver: Timeout waiting"
+ " for card added\n");
+ /* Store the context to the device driver to the global */
+ available_driver = driver;
+
+ /*
+ * If available_sdio_dev is not NULL, probe has been called,
+ * so pass the probe to the registered driver
+ */
+ if (available_sdio_dev) {
+ /* Store the context to the new device driver */
+ available_sdio_dev->driver = driver;
+
+ printk(KERN_INFO "fs_sdio_register_driver: Glue exists, add "
+ "device driver and register IRQ\n");
+ driver->probe(available_sdio_dev);
+
+ /* Register the IRQ handler to the SDIO IRQ. */
+ sdio_claim_host(available_sdio_dev->func);
+ ret = sdio_claim_irq(available_sdio_dev->func, fs_sdio_irq);
+ sdio_release_host(available_sdio_dev->func);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(fs_sdio_register_driver);
+
+void fs_sdio_unregister_driver(struct fs_driver *driver)
+{
+ int retry;
+ /*
+ * If available_sdio_dev is not NULL, probe has been called,
+ * so pass the remove to the registered driver to clean up.
+ */
+ if (available_sdio_dev) {
+ struct mmc_host *host = available_sdio_dev->func->card->host;
+
+ printk(KERN_INFO "fs_sdio_unregister_driver: Glue exists, "
+ "unregister IRQ and remove device driver\n");
+
+ /* Unregister the IRQ handler first. */
+ sdio_claim_host(available_sdio_dev->func);
+ sdio_release_irq(available_sdio_dev->func);
+ sdio_release_host(available_sdio_dev->func);
+
+ driver->remove(available_sdio_dev);
+
+ if (!available_sdio_dev->int_enabled) {
+ available_sdio_dev->int_enabled = 1;
+ host->ops->enable_sdio_irq(host, 1);
+ }
+
+ /* Invalidate the context to the device driver */
+ available_sdio_dev->driver = NULL;
+ }
+
+ /* invalidate the context to the device driver to the global */
+ available_driver = NULL;
+ /* Power down the UniFi */
+ fs_unifi_power_off();
+
+}
+EXPORT_SYMBOL(fs_sdio_unregister_driver);
+
+static void fs_sdio_irq(struct sdio_func *func)
+{
+ struct sdio_dev *fdev = (struct sdio_dev *)sdio_get_drvdata(func);
+ if (fdev->driver) {
+ if (fdev->driver->card_int_handler)
+ fdev->driver->card_int_handler(fdev);
+ }
+}
+
+#ifdef CONFIG_PM
+static int fs_sdio_suspend(struct device *dev, pm_message_t state)
+{
+ struct sdio_dev *fdev = available_sdio_dev;
+
+ /* Pass event to the registered driver. */
+ if (fdev->driver)
+ if (fdev->driver->suspend)
+ fdev->driver->suspend(fdev, state);
+
+ return 0;
+}
+
+static int fs_sdio_resume(struct device *dev)
+{
+ struct sdio_dev *fdev = available_sdio_dev;
+
+ /* Pass event to the registered driver. */
+ if (fdev->driver)
+ if (fdev->driver->resume)
+ fdev->driver->resume(fdev);
+
+ return 0;
+}
+#else
+#define fs_sdio_suspend NULL
+#define fs_sdio_resume NULL
+#endif
+
+static int fs_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct sdio_dev *fdev;
+ int ret = 0;
+
+ /* Allocate our private context */
+ fdev = kmalloc(sizeof(struct sdio_dev), GFP_KERNEL);
+ if (!fdev)
+ return -ENOMEM;
+ available_sdio_dev = fdev;
+ memset(fdev, 0, sizeof(struct sdio_dev));
+ fdev->func = func;
+ fdev->vendor_id = id->vendor;
+ fdev->device_id = id->device;
+ fdev->max_blocksize = func->max_blksize;
+ fdev->int_enabled = 1;
+ spin_lock_init(&fdev->lock);
+
+ /* Store our context in the MMC driver */
+ printk(KERN_INFO "fs_sdio_probe: Add glue driver\n");
+ sdio_set_drvdata(func, fdev);
+
+ return 0;
+}
+
+static void fs_sdio_remove(struct sdio_func *func)
+{
+ struct sdio_dev *fdev = (struct sdio_dev *)sdio_get_drvdata(func);
+ struct mmc_host *host = func->card->host;
+
+ /* If there is a registered device driver, pass on the remove */
+ if (fdev->driver) {
+ printk(KERN_INFO "fs_sdio_remove: Free IRQ and remove device "
+ "driver\n");
+ /* Unregister the IRQ handler first. */
+ sdio_claim_host(fdev->func);
+ sdio_release_irq(func);
+ sdio_release_host(fdev->func);
+
+ fdev->driver->remove(fdev);
+
+ if (!fdev->int_enabled) {
+ fdev->int_enabled = 1;
+ host->ops->enable_sdio_irq(host, 1);
+ }
+ }
+
+ /* Unregister the card context from the MMC driver. */
+ sdio_set_drvdata(func, NULL);
+
+ /* Invalidate the global to our context. */
+ available_sdio_dev = NULL;
+ kfree(fdev);
+}
+
+static int fs_unifi_init(void)
+{
+ struct regulator_unifi *reg_unifi;
+ struct regulator *reg;
+ int err = 0;
+
+ plat_data = get_unifi_plat_data();
+
+ if (!plat_data)
+ return -ENOENT;
+
+ reg_unifi = kzalloc(sizeof(struct regulator_unifi), GFP_KERNEL);
+ if (!reg_unifi)
+ return -ENOMEM;
+
+ if (plat_data->reg_gpo1) {
+ reg = regulator_get(NULL, plat_data->reg_gpo1);
+ if (!IS_ERR(reg))
+ reg_unifi->reg_gpo1 = reg;
+ else {
+ err = -EINVAL;
+ goto err_reg_gpo1;
+ }
+ }
+
+ if (plat_data->reg_gpo2) {
+ reg = regulator_get(NULL, plat_data->reg_gpo2);
+ if (!IS_ERR(reg))
+ reg_unifi->reg_gpo2 = reg;
+ else {
+ err = -EINVAL;
+ goto err_reg_gpo2;
+ }
+ }
+
+ if (plat_data->reg_1v5_ana_bb) {
+ reg = regulator_get(NULL, plat_data->reg_1v5_ana_bb);
+ if (!IS_ERR(reg))
+ reg_unifi->reg_1v5_ana_bb = reg;
+ else {
+ err = -EINVAL;
+ goto err_reg_1v5_ana_bb;
+ }
+ }
+
+ if (plat_data->reg_vdd_vpa) {
+ reg = regulator_get(NULL, plat_data->reg_vdd_vpa);
+ if (!IS_ERR(reg))
+ reg_unifi->reg_vdd_vpa = reg;
+ else {
+ err = -EINVAL;
+ goto err_reg_vdd_vpa;
+ }
+ }
+
+ if (plat_data->reg_1v5_dd) {
+ reg = regulator_get(NULL, plat_data->reg_1v5_dd);
+ if (!IS_ERR(reg))
+ reg_unifi->reg_1v5_dd = reg;
+ else {
+ err = -EINVAL;
+ goto err_reg_1v5_dd;
+ }
+ }
+ plat_data->priv = reg_unifi;
+ return 0;
+
+err_reg_1v5_dd:
+ if (reg_unifi->reg_vdd_vpa)
+ regulator_put(reg_unifi->reg_vdd_vpa);
+err_reg_vdd_vpa:
+ if (reg_unifi->reg_1v5_ana_bb)
+ regulator_put(reg_unifi->reg_1v5_ana_bb);
+err_reg_1v5_ana_bb:
+ if (reg_unifi->reg_gpo2)
+ regulator_put(reg_unifi->reg_gpo2);
+err_reg_gpo2:
+ if (reg_unifi->reg_gpo1)
+ regulator_put(reg_unifi->reg_gpo1);
+err_reg_gpo1:
+ kfree(reg_unifi);
+ return err;
+}
+
+int fs_unifi_remove(void)
+{
+ struct regulator_unifi *reg_unifi;
+
+ reg_unifi = plat_data->priv;
+ plat_data->priv = NULL;
+
+ if (reg_unifi->reg_1v5_dd)
+ regulator_put(reg_unifi->reg_1v5_dd);
+ if (reg_unifi->reg_vdd_vpa)
+ regulator_put(reg_unifi->reg_vdd_vpa);
+
+ if (reg_unifi->reg_1v5_ana_bb)
+ regulator_put(reg_unifi->reg_1v5_ana_bb);
+
+ if (reg_unifi->reg_gpo2)
+ regulator_put(reg_unifi->reg_gpo2);
+
+ if (reg_unifi->reg_gpo1)
+ regulator_put(reg_unifi->reg_gpo1);
+
+ kfree(reg_unifi);
+ return 0;
+}
+
+/* Module init and exit, register and unregister to the SDIO/MMC driver */
+static int __init fs_sdio_init(void)
+{
+ int err;
+
+ printk(KERN_INFO "Freescale: Register to MMC/SDIO driver\n");
+ /* Sleep a bit - otherwise if the mmc subsystem has just started, it
+ * will allow us to register, then immediatly remove us!
+ */
+ msleep(10);
+ err = fs_unifi_init();
+ if (err) {
+ printk(KERN_ERR "Error: fs_unifi_init failed!\n");
+ return err;
+ }
+ err = sdio_register_driver(&sdio_unifi_driver);
+ if (err) {
+ printk(KERN_ERR "Error: register sdio_unifi_driver failed!\n");
+ fs_unifi_remove();
+ }
+ return err;
+}
+
+module_init(fs_sdio_init);
+
+static void __exit fs_sdio_exit(void)
+{
+ printk(KERN_INFO "Freescale: Unregister from MMC/SDIO driver\n");
+ sdio_unregister_driver(&sdio_unifi_driver);
+ fs_unifi_remove();
+}
+
+module_exit(fs_sdio_exit);
+
+MODULE_DESCRIPTION("Freescale SDIO glue driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/card/unifi_fs/fs_sdio_api.h b/drivers/mmc/card/unifi_fs/fs_sdio_api.h
new file mode 100644
index 000000000000..ea6ebd765e28
--- /dev/null
+++ b/drivers/mmc/card/unifi_fs/fs_sdio_api.h
@@ -0,0 +1,68 @@
+/*
+ *fs_sdio_api.h - Freescale SDIO glue module API for UniFi.
+ *
+ * Copyright (C) 2008 Cambridge Silicon Radio Ltd.
+ *
+ */
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+/*
+ * 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 _FS_SDIO_API_H
+#define _FS_SDIO_API_H
+
+struct sdio_dev;
+
+struct fs_driver {
+ const char *name;
+ int (*probe)(struct sdio_dev *fdev);
+ void (*remove)(struct sdio_dev *fdev);
+ void (*card_int_handler)(struct sdio_dev *fdev);
+ void (*suspend)(struct sdio_dev *fdev, pm_message_t state);
+ void (*resume)(struct sdio_dev *fdev);
+};
+
+int fs_sdio_readb(struct sdio_dev *fdev, int funcnum,
+ unsigned long addr, unsigned char *pdata);
+int fs_sdio_writeb(struct sdio_dev *fdev, int funcnum,
+ unsigned long addr, unsigned char data);
+int fs_sdio_block_rw(struct sdio_dev *fdev, int funcnum,
+ unsigned long addr, unsigned char *pdata,
+ unsigned int count, int direction);
+
+int fs_sdio_register_driver(struct fs_driver *driver);
+void fs_sdio_unregister_driver(struct fs_driver *driver);
+int fs_sdio_set_block_size(struct sdio_dev *fdev, int blksz);
+int fs_sdio_set_max_clock_speed(struct sdio_dev *fdev, int max_khz);
+int fs_sdio_enable_interrupt(struct sdio_dev *fdev, int enable);
+int fs_sdio_enable(struct sdio_dev *fdev);
+int fs_sdio_hard_reset(struct sdio_dev *fdev);
+
+struct sdio_dev {
+ /**< Device driver for this module. */
+ struct fs_driver *driver;
+
+ struct sdio_func *func;
+
+ /**< Data private to the device driver. */
+ void *drv_data;
+
+ int int_enabled;
+ spinlock_t lock;
+
+ uint16_t vendor_id; /**< Vendor ID of the card. */
+ uint16_t device_id; /**< Device ID of the card. */
+
+ /**< Maximum block size supported. */
+ int max_blocksize;
+};
+
+
+#endif /* #ifndef _FS_SDIO_API_H */
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index fdd7c760be8c..7112a25356d5 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -208,7 +208,7 @@ static int mmc_read_ext_csd(struct mmc_card *card)
}
ext_csd_struct = ext_csd[EXT_CSD_REV];
- if (ext_csd_struct > 2) {
+ if (ext_csd_struct > 3) {
printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
"version %d\n", mmc_hostname(card->host),
ext_csd_struct);
@@ -433,15 +433,26 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
/*
* Activate wide bus (if supported).
*/
- if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
- (host->caps & MMC_CAP_4_BIT_DATA)) {
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
- if (err)
- goto free_card;
+ if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
+ (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
+ unsigned ext_csd_bit, bus_width;
- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
- }
+ if (host->caps & MMC_CAP_8_BIT_DATA) {
+ ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
+ bus_width = MMC_BUS_WIDTH_8;
+ } else {
+ ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
+ bus_width = MMC_BUS_WIDTH_4;
+ }
+
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH, ext_csd_bit);
+
+ if (err)
+ goto free_card;
+
+ mmc_set_bus_width(card->host, bus_width);
+ }
if (!oldcard)
host->card = card;
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index dfa585f7feaf..852a319ddad6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -165,6 +165,51 @@ config MMC_SPI
If unsure, or if your system has no SPI master driver, say N.
+config MMC_MXC
+ tristate "Freescale MXC Multimedia Card Interface support"
+ depends on ARCH_MXC && MMC
+ help
+ This selects the Freescale MXC Multimedia card Interface.
+ If you have a MXC platform with a Multimedia Card slot,
+ say Y or M here.
+
+config MMC_IMX_ESDHCI
+ tristate "Freescale i.MX Secure Digital Host Controller Interface support"
+ depends on ARCH_MXC && MMC
+ help
+ This selects the Freescale i.MX Multimedia card Interface.
+ If you have a i.MX platform with a Multimedia Card slot,
+ say Y or M here.
+
+ If unsure, say N.
+
+config MMC_IMX_ESDHCI_SELECT2
+ bool "Enable second ESDHCI port"
+ depends on MMC_IMX_ESDHCI && ARCH_MX25
+ default n
+ help
+ Enable the second ESDHC port
+
+config MMC_IMX_ESDHCI_PIO_MODE
+ bool "Freescale i.MX Secure Digital Host Controller Interface PIO mode"
+ depends on MMC_IMX_ESDHC != n
+ default n
+ help
+ This set the Freescale i.MX Multimedia card Interface to PIO mode.
+ If you have a i.MX platform with a Multimedia Card slot,
+ and want test it with PIO mode.
+ say Y here.
+
+ If unsure, say N.
+
+config MMC_STMP3XXX
+ tristate "STMP37xx/378x MMC support"
+ depends on MMC && ARCH_STMP3XXX
+ help
+ Select Y if you would like to access STMP37xx/378x MMC support.
+
+ If unsure, say N.
+
config MMC_S3C
tristate "Samsung S3C SD/MMC Card Interface support"
depends on ARCH_S3C2410
@@ -176,12 +221,24 @@ config MMC_S3C
If unsure, say N.
+config HSMMC_S3C
+ tristate "Samsung S3C High Speed SD/MMC Card Interface support"
+ depends on ARCH_S3C2410 && MMC
+ help
+ This selects a driver for the High Speed MMC interface found in
+ Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
+ If you have a board based on one of those and a MMC/SD
+ slot, say Y or M here.
+
+ If unsure, say N.
+
config MMC_SDRICOH_CS
tristate "MMC/SD driver for Ricoh Bay1Controllers (EXPERIMENTAL)"
depends on EXPERIMENTAL && PCI && PCMCIA
help
Say Y here if your Notebook reports a Ricoh Bay1Controller PCMCIA
card whenever you insert a MMC or SD card into the card slot.
+ say Y or M here.
To compile this driver as a module, choose M here: the
module will be called sdricoh_cs.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index c794cc5ce442..8467f9ac6d40 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -12,6 +12,9 @@ obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o
+obj-$(CONFIG_MMC_IMX_ESDHCI) += mx_sdhci.o
+obj-$(CONFIG_MMC_MXC) += mxc_mmc.o
+obj-$(CONFIG_MMC_STMP3XXX) += stmp3xxx_mmc.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
@@ -20,6 +23,7 @@ obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
obj-$(CONFIG_MMC_S3C) += s3cmci.o
+obj-$(CONFIG_HSMMC_S3C) += s3c-hsmmc.o
obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
diff --git a/drivers/mmc/host/mx_sdhci.c b/drivers/mmc/host/mx_sdhci.c
new file mode 100644
index 000000000000..c6352e9edd3b
--- /dev/null
+++ b/drivers/mmc/host/mx_sdhci.c
@@ -0,0 +1,2153 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx_sdhci.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC eSDHC modules.
+ *
+ * This driver code is based on sdhci.c, by Pierre Ossman <drzeus@drzeus.cx>");
+ * This driver supports Enhanced Secure Digital Host Controller
+ * modules eSDHC of MXC. eSDHC is also referred as enhanced MMC/SD
+ * controller.
+ *
+ * @ingroup MMC_SD
+ */
+
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+
+#include <linux/leds.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/card.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm/mach-types.h>
+#include <mach/mmc.h>
+
+#include "mx_sdhci.h"
+
+#define DRIVER_NAME "mxsdhci"
+
+#define DBG(f, x...) \
+ pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x)
+
+static unsigned int debug_quirks;
+static int last_op_dir;
+
+/*
+ * Different quirks to handle when the hardware deviates from a strict
+ * interpretation of the SDHCI specification.
+ */
+
+/* Controller doesn't honor resets unless we touch the clock register */
+#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
+/* Controller has bad caps bits, but really supports DMA */
+#define SDHCI_QUIRK_FORCE_DMA (1<<1)
+/* Controller doesn't like to be reset when there is no card inserted. */
+#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
+/* Controller doesn't like clearing the power reg before a change */
+#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
+/* Controller has flaky internal state so reset it on each ios change */
+#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
+/* Controller has an unusable DMA engine */
+#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
+/* Controller can only DMA from 32-bit aligned addresses */
+#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<6)
+/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
+#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<7)
+/* Controller needs to be reset after each request to stay stable */
+#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8)
+/* Controller needs voltage and power writes to happen separately */
+#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<9)
+/* Controller has an off-by-one issue with timeout value */
+#define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL (1<<10)
+/* Controller only support the PIO */
+#define SDHCI_QUIRK_ONLY_PIO (1<<16)
+/* Controller support the External DMA */
+#define SDHCI_QUIRK_EXTERNAL_DMA_MODE (1<<17)
+/* Controller support the Internal Simple DMA */
+#define SDHCI_QUIRK_INTERNAL_SIMPLE_DMA (1<<18)
+/* Controller support the Internal Advanced DMA */
+#define SDHCI_QUIRK_INTERNAL_ADVANCED_DMA (1<<19)
+
+/*
+ * defines the mxc flags refer to the special hw pre-conditons and behavior
+ */
+static unsigned int mxc_quirks;
+#ifdef CONFIG_MMC_IMX_ESDHCI_PIO_MODE
+static unsigned int debug_quirks = SDHCI_QUIRK_ONLY_PIO;
+#else
+static unsigned int debug_quirks;
+#endif
+static unsigned int mxc_wml_value = 512;
+static unsigned int *adma_des_table;
+
+#ifndef MXC_SDHCI_NUM
+#define MXC_SDHCI_NUM 4
+#endif
+
+static struct sdhci_chip *mxc_fix_chips[MXC_SDHCI_NUM];
+
+static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
+static void sdhci_finish_data(struct sdhci_host *);
+
+static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
+static void sdhci_finish_command(struct sdhci_host *);
+
+/* Used to active the SD bus */
+extern void gpio_sdhc_active(int module);
+extern void gpio_sdhc_inactive(int module);
+static void sdhci_dma_irq(void *devid, int error, unsigned int cnt);
+
+void mxc_mmc_force_detect(int id)
+{
+ struct sdhci_host *host;
+ if ((id < 0) || (id >= MXC_SDHCI_NUM))
+ return;
+ if (!mxc_fix_chips[id])
+ return;
+ host = mxc_fix_chips[id]->hosts[0];
+ if (host->detect_irq)
+ return;
+
+ schedule_work(&host->cd_wq);
+ return;
+}
+
+EXPORT_SYMBOL(mxc_mmc_force_detect);
+
+static void sdhci_dumpregs(struct sdhci_host *host)
+{
+ printk(KERN_DEBUG DRIVER_NAME
+ ": ============== REGISTER DUMP ==============\n");
+
+ printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_DMA_ADDRESS),
+ readl(host->ioaddr + SDHCI_HOST_VERSION));
+ printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
+ (readl(host->ioaddr + SDHCI_BLOCK_SIZE) & 0xFFFF),
+ (readl(host->ioaddr + SDHCI_BLOCK_COUNT) >> 16));
+ printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_ARGUMENT),
+ readl(host->ioaddr + SDHCI_TRANSFER_MODE));
+ printk(KERN_DEBUG DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_PRESENT_STATE),
+ readl(host->ioaddr + SDHCI_HOST_CONTROL));
+ printk(KERN_DEBUG DRIVER_NAME ": Clock: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_CLOCK_CONTROL));
+ printk(KERN_DEBUG DRIVER_NAME ": Int stat: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_INT_STATUS));
+ printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_INT_ENABLE),
+ readl(host->ioaddr + SDHCI_SIGNAL_ENABLE));
+ printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_CAPABILITIES));
+
+ printk(KERN_DEBUG DRIVER_NAME
+ ": ===========================================\n");
+}
+
+/*****************************************************************************\
+ * *
+ * Low level functions *
+ * *
+\*****************************************************************************/
+
+static void sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+ unsigned long tmp;
+ unsigned long mask_u32;
+ unsigned long reg_save = 0;
+
+ if (host->chip->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
+ if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT))
+ return;
+ }
+
+ if (mask & SDHCI_RESET_ALL)
+ host->clock = 0;
+ else if (host->flags & SDHCI_CD_PRESENT)
+ reg_save = readl(host->ioaddr + SDHCI_HOST_CONTROL);
+
+ tmp = readl(host->ioaddr + SDHCI_CLOCK_CONTROL) | (mask << 24);
+ mask_u32 = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ writel(tmp, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ /* Wait max 100 ms */
+ tmp = 5000;
+
+ /* hw clears the bit when it's done */
+ while ((readl(host->ioaddr + SDHCI_CLOCK_CONTROL) >> 24) & mask) {
+ if (tmp == 0) {
+ printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
+ mmc_hostname(host->mmc), (int)mask);
+ sdhci_dumpregs(host);
+ return;
+ }
+ tmp--;
+ udelay(20);
+ }
+ /*
+ * The INT_EN SIG_EN regs have been modified after reset.
+ * re-configure them ag.
+ */
+ if (!(mask & SDHCI_RESET_ALL) && (host->flags & SDHCI_CD_PRESENT))
+ writel(reg_save, host->ioaddr + SDHCI_HOST_CONTROL);
+ if (host->flags & SDHCI_USE_DMA)
+ mask_u32 &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL);
+ if (mxc_wml_value == 512)
+ writel(SDHCI_WML_128_WORDS, host->ioaddr + SDHCI_WML);
+ else
+ writel(SDHCI_WML_16_WORDS, host->ioaddr + SDHCI_WML);
+ writel(mask_u32 | SDHCI_INT_CARD_INT, host->ioaddr + SDHCI_INT_ENABLE);
+ writel(mask_u32, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ last_op_dir = 0;
+}
+
+static void sdhci_init(struct sdhci_host *host)
+{
+ u32 intmask;
+
+ sdhci_reset(host, SDHCI_RESET_ALL);
+
+ intmask = SDHCI_INT_ADMA_ERROR |
+ SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC |
+ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
+ SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
+ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
+ SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE;
+
+ if (host->flags & SDHCI_USE_DMA)
+ intmask &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL);
+ /* Configure the WML rege */
+ if (mxc_wml_value == 512)
+ writel(SDHCI_WML_128_WORDS, host->ioaddr + SDHCI_WML);
+ else
+ writel(SDHCI_WML_16_WORDS, host->ioaddr + SDHCI_WML);
+ writel(intmask | SDHCI_INT_CARD_INT, host->ioaddr + SDHCI_INT_ENABLE);
+ writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+}
+
+static void sdhci_activate_led(struct sdhci_host *host)
+{
+ u32 ctrl;
+
+ ctrl = readl(host->ioaddr + SDHCI_HOST_CONTROL);
+ ctrl |= SDHCI_CTRL_LED;
+ writel(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+}
+
+static void sdhci_deactivate_led(struct sdhci_host *host)
+{
+ u32 ctrl;
+
+ ctrl = readl(host->ioaddr + SDHCI_HOST_CONTROL);
+ ctrl &= ~SDHCI_CTRL_LED;
+ writel(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+}
+
+/*****************************************************************************\
+ * *
+ * Core functions *
+ * *
+\*****************************************************************************/
+
+static inline char *sdhci_sg_to_buffer(struct sdhci_host *host)
+{
+ return sg_virt(host->cur_sg);
+}
+
+static inline int sdhci_next_sg(struct sdhci_host *host)
+{
+ /*
+ * Skip to next SG entry.
+ */
+ host->cur_sg++;
+ host->num_sg--;
+
+ /*
+ * Any entries left?
+ */
+ if (host->num_sg > 0) {
+ host->offset = 0;
+ host->remain = host->cur_sg->length;
+ }
+
+ return host->num_sg;
+}
+
+static void sdhci_read_block_pio(struct sdhci_host *host)
+{
+ int blksize, chunk_remain;
+ u32 data;
+ char *buffer;
+ int size;
+
+ DBG("PIO reading\n");
+
+ blksize = host->data->blksz;
+ chunk_remain = 0;
+ data = 0;
+
+ buffer = sdhci_sg_to_buffer(host) + host->offset;
+
+ while (blksize) {
+ if (chunk_remain == 0) {
+ data = readl(host->ioaddr + SDHCI_BUFFER);
+ chunk_remain = min(blksize, 4);
+ }
+
+ size = min(host->remain, chunk_remain);
+
+ chunk_remain -= size;
+ blksize -= size;
+ host->offset += size;
+ host->remain -= size;
+
+ while (size) {
+ *buffer = data & 0xFF;
+ buffer++;
+ data >>= 8;
+ size--;
+ }
+
+ if (host->remain == 0) {
+ if (sdhci_next_sg(host) == 0) {
+ BUG_ON(blksize != 0);
+ return;
+ }
+ buffer = sdhci_sg_to_buffer(host);
+ }
+ }
+}
+
+static void sdhci_write_block_pio(struct sdhci_host *host)
+{
+ int blksize, chunk_remain;
+ u32 data;
+ char *buffer;
+ int bytes, size;
+
+ DBG("PIO writing\n");
+
+ blksize = host->data->blksz;
+ chunk_remain = 4;
+ data = 0;
+
+ bytes = 0;
+ buffer = sdhci_sg_to_buffer(host) + host->offset;
+
+ while (blksize) {
+ size = min(host->remain, chunk_remain);
+
+ chunk_remain -= size;
+ blksize -= size;
+ host->offset += size;
+ host->remain -= size;
+
+ while (size) {
+ data >>= 8;
+ data |= (u32) *buffer << 24;
+ buffer++;
+ size--;
+ }
+
+ if (chunk_remain == 0) {
+ writel(data, host->ioaddr + SDHCI_BUFFER);
+ chunk_remain = min(blksize, 4);
+ }
+
+ if (host->remain == 0) {
+ if (sdhci_next_sg(host) == 0) {
+ BUG_ON(blksize != 0);
+ return;
+ }
+ buffer = sdhci_sg_to_buffer(host);
+ }
+ }
+}
+
+static void sdhci_transfer_pio(struct sdhci_host *host)
+{
+ u32 mask;
+
+ BUG_ON(!host->data);
+
+ if (host->num_sg == 0)
+ return;
+
+ if (host->data->flags & MMC_DATA_READ)
+ mask = SDHCI_DATA_AVAILABLE;
+ else
+ mask = SDHCI_SPACE_AVAILABLE;
+
+ while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+ if (host->data->flags & MMC_DATA_READ)
+ sdhci_read_block_pio(host);
+ else
+ sdhci_write_block_pio(host);
+
+ if (host->num_sg == 0)
+ break;
+ }
+
+ DBG("PIO transfer complete.\n");
+}
+
+static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
+{
+ u32 count;
+ unsigned target_timeout, current_timeout;
+
+ WARN_ON(host->data);
+
+ if (data == NULL)
+ return;
+
+ /* Sanity checks */
+ BUG_ON(data->blksz * data->blocks > 524288);
+ BUG_ON(data->blksz > host->mmc->max_blk_size);
+ BUG_ON(data->blocks > 65535);
+
+ host->data = data;
+ host->data_early = 0;
+ if (host->data->flags & MMC_DATA_READ)
+ writel(readl(host->ioaddr + SDHCI_CLOCK_CONTROL) |
+ SDHCI_CLOCK_HLK_EN, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ /* timeout in us */
+ target_timeout = data->timeout_ns / 1000 +
+ data->timeout_clks / host->clock;
+
+ /*
+ * Figure out needed cycles.
+ * We do this in steps in order to fit inside a 32 bit int.
+ * The first step is the minimum timeout, which will have a
+ * minimum resolution of 6 bits:
+ * (1) 2^13*1000 > 2^22,
+ * (2) host->timeout_clk < 2^16
+ * =>
+ * (1) / (2) > 2^6
+ */
+ count = 0;
+ current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+ while (current_timeout < target_timeout) {
+ count++;
+ current_timeout <<= 1;
+ if (count >= 0xF)
+ break;
+ }
+
+ /*
+ * Compensate for an off-by-one error in the CaFe hardware; otherwise,
+ * a too-small count gives us interrupt timeouts.
+ */
+ if ((host->chip->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL))
+ count++;
+
+ if (count >= 0xF) {
+ DBG(KERN_WARNING "%s: Too large timeout requested!\n",
+ mmc_hostname(host->mmc));
+ count = 0xE;
+ }
+
+ /* Set the max time-out value to level up the compatibility */
+ count = 0xE;
+
+ count =
+ (count << 16) | (readl(host->ioaddr + SDHCI_CLOCK_CONTROL) &
+ 0xFFF0FFFF);
+ writel(count, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ if (host->flags & SDHCI_USE_DMA)
+ host->flags |= SDHCI_REQ_USE_DMA;
+
+ if (unlikely((host->flags & SDHCI_REQ_USE_DMA) &&
+ (host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) &&
+ ((data->blksz * data->blocks) & 0x3))) {
+ DBG("Reverting to PIO because of transfer size (%d)\n",
+ data->blksz * data->blocks);
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+ }
+
+ /*
+ * The assumption here being that alignment is the same after
+ * translation to device address space.
+ */
+ if (unlikely((host->flags & SDHCI_REQ_USE_DMA) &&
+ (host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
+ (data->sg->offset & 0x3))) {
+ DBG("Reverting to PIO because of bad alignment\n");
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+ }
+
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ int i;
+ struct scatterlist *tsg;
+
+ host->dma_size = data->blocks * data->blksz;
+ count =
+ dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (data->
+ flags & MMC_DATA_READ) ? DMA_FROM_DEVICE :
+ DMA_TO_DEVICE);
+ BUG_ON(count != data->sg_len);
+ DBG("Configure the sg DMA, %s, len is 0x%x, count is %d\n",
+ (data->flags & MMC_DATA_READ)
+ ? "DMA_FROM_DEIVCE" : "DMA_TO_DEVICE", host->dma_size,
+ count);
+
+ /* Make sure the ADMA mode is selected. */
+ i = readl(host->ioaddr + SDHCI_HOST_CONTROL);
+ i |= SDHCI_CTRL_ADMA;
+ writel(i, host->ioaddr + SDHCI_HOST_CONTROL);
+
+ tsg = data->sg;
+ /* ADMA mode is used, create the descriptor table */
+ for (i = 0; i < count; i++) {
+ if (tsg->dma_address & 0xFFF) {
+ DBG(KERN_ERR "ADMA addr isn't 4K aligned.\n");
+ DBG(KERN_ERR "0x%x\n", tsg->dma_address);
+ DBG(KERN_ERR "Changed to Single DMA mode.\n");
+ goto Single_DMA;
+ }
+ adma_des_table[2 * i] = tsg->length << 12;
+ adma_des_table[2 * i] |= FSL_ADMA_DES_ATTR_SET;
+ adma_des_table[2 * i] |= FSL_ADMA_DES_ATTR_VALID;
+ adma_des_table[2 * i + 1] = tsg->dma_address;
+ adma_des_table[2 * i + 1] |= FSL_ADMA_DES_ATTR_TRAN;
+ adma_des_table[2 * i + 1] |= FSL_ADMA_DES_ATTR_VALID;
+ if (count == (i + 1))
+ adma_des_table[2 * i + 1] |=
+ FSL_ADMA_DES_ATTR_END;
+ tsg++;
+ }
+
+ /* Write the physical address to ADMA address reg */
+ writel(virt_to_phys(adma_des_table),
+ host->ioaddr + SDHCI_ADMA_ADDRESS);
+ Single_DMA:
+ /* Rollback to the Single DMA mode */
+ i = readl(host->ioaddr + SDHCI_HOST_CONTROL);
+ i &= ~SDHCI_CTRL_ADMA;
+ writel(i, host->ioaddr + SDHCI_HOST_CONTROL);
+ /* Single DMA mode is used */
+ writel(sg_dma_address(data->sg),
+ host->ioaddr + SDHCI_DMA_ADDRESS);
+ } else if ((host->flags & SDHCI_USE_EXTERNAL_DMA) &&
+ (data->blocks * data->blksz >= mxc_wml_value)) {
+ host->dma_size = data->blocks * data->blksz;
+ DBG("Configure the External DMA, %s, len is 0x%x\n",
+ (data->flags & MMC_DATA_READ)
+ ? "DMA_FROM_DEIVCE" : "DMA_TO_DEVICE", host->dma_size);
+
+ if (data->blksz & 0x3) {
+ printk(KERN_ERR
+ "mxc_mci: block size not multiple of 4 bytes\n");
+ }
+
+ if (data->flags & MMC_DATA_READ)
+ host->dma_dir = DMA_FROM_DEVICE;
+ else
+ host->dma_dir = DMA_TO_DEVICE;
+
+ host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, host->dma_dir);
+
+ if (data->flags & MMC_DATA_READ) {
+ mxc_dma_sg_config(host->dma, data->sg, data->sg_len,
+ host->dma_size, MXC_DMA_MODE_READ);
+ } else {
+ mxc_dma_sg_config(host->dma, data->sg, data->sg_len,
+ host->dma_size, MXC_DMA_MODE_WRITE);
+ }
+ } else {
+ host->cur_sg = data->sg;
+ host->num_sg = data->sg_len;
+
+ host->offset = 0;
+ host->remain = host->cur_sg->length;
+ }
+
+ /* We do not handle DMA boundaries, so set it to max (512 KiB) */
+ writel((data->blocks << 16) | SDHCI_MAKE_BLKSZ(7, data->blksz),
+ host->ioaddr + SDHCI_BLOCK_SIZE);
+}
+
+static void sdhci_finish_data(struct sdhci_host *host)
+{
+ struct mmc_data *data;
+ u16 blocks;
+
+ BUG_ON(!host->data);
+
+ data = host->data;
+ host->data = NULL;
+
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ dma_unmap_sg(&(host->chip->pdev)->dev, data->sg, data->sg_len,
+ (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE :
+ DMA_TO_DEVICE);
+ }
+ if ((host->flags & SDHCI_USE_EXTERNAL_DMA) &&
+ (host->dma_size >= mxc_wml_value) && (data != NULL)) {
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ host->dma_len, host->dma_dir);
+ host->dma_size = 0;
+ }
+
+ /*
+ * Controller doesn't count down when in single block mode.
+ */
+ if (data->blocks == 1)
+ blocks = (data->error == 0) ? 0 : 1;
+ else
+ blocks = readl(host->ioaddr + SDHCI_BLOCK_COUNT) >> 16;
+ data->bytes_xfered = data->blksz * data->blocks;
+
+ if (data->stop) {
+ /*
+ * The controller needs a reset of internal state machines
+ * upon error conditions.
+ */
+ if (data->error) {
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ }
+
+ sdhci_send_command(host, data->stop);
+ } else
+ tasklet_schedule(&host->finish_tasklet);
+}
+
+static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ int flags;
+ u32 mask;
+ u32 mode = 0;
+ unsigned long timeout;
+
+ DBG("sdhci_send_command 0x%x is starting...\n", cmd->opcode);
+ WARN_ON(host->cmd);
+
+ /* Wait max 10 ms */
+ timeout = 5000;
+
+ mask = SDHCI_CMD_INHIBIT;
+ if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
+ mask |= SDHCI_DATA_INHIBIT;
+
+ /* We shouldn't wait for data inihibit for stop commands, even
+ though they might use busy signaling */
+ if (host->mrq->data && (cmd == host->mrq->data->stop))
+ mask &= ~SDHCI_DATA_INHIBIT;
+
+ while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: Controller never released "
+ "inhibit bit(s).\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ cmd->error = -EIO;
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+ timeout--;
+ udelay(20);
+ }
+
+ mod_timer(&host->timer, jiffies + 1 * HZ);
+
+ host->cmd = cmd;
+
+ sdhci_prepare_data(host, cmd->data);
+
+ writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
+
+ /* Set up the transfer mode */
+ if (cmd->data != NULL) {
+ mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_DPSEL;
+ if (cmd->data->blocks > 1)
+ mode |= SDHCI_TRNS_MULTI;
+ if (cmd->data->flags & MMC_DATA_READ)
+ mode |= SDHCI_TRNS_READ;
+ else
+ mode &= ~SDHCI_TRNS_READ;
+ if (host->flags & SDHCI_USE_DMA)
+ mode |= SDHCI_TRNS_DMA;
+ if (host->flags & SDHCI_USE_EXTERNAL_DMA)
+ DBG("Prepare data completely in %s transfer mode.\n",
+ "EXTTERNAL DMA");
+ }
+
+ if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
+ printk(KERN_ERR "%s: Unsupported response type!\n",
+ mmc_hostname(host->mmc));
+ cmd->error = -EINVAL;
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+
+ if (!(cmd->flags & MMC_RSP_PRESENT))
+ flags = SDHCI_CMD_RESP_NONE;
+ else if (cmd->flags & MMC_RSP_136)
+ flags = SDHCI_CMD_RESP_LONG;
+ else if (cmd->flags & MMC_RSP_BUSY)
+ flags = SDHCI_CMD_RESP_SHORT_BUSY;
+ else
+ flags = SDHCI_CMD_RESP_SHORT;
+
+ if (cmd->flags & MMC_RSP_CRC)
+ flags |= SDHCI_CMD_CRC;
+ if (cmd->flags & MMC_RSP_OPCODE)
+ flags |= SDHCI_CMD_INDEX;
+ if (cmd->data)
+ flags |= SDHCI_CMD_DATA;
+
+ mode |= SDHCI_MAKE_CMD(cmd->opcode, flags);
+ DBG("Complete sending cmd, transfer mode would be 0x%x.\n", mode);
+ writel(mode, host->ioaddr + SDHCI_TRANSFER_MODE);
+}
+
+static void sdhci_finish_command(struct sdhci_host *host)
+{
+ int i;
+
+ BUG_ON(host->cmd == NULL);
+
+ if (host->cmd->flags & MMC_RSP_PRESENT) {
+ if (host->cmd->flags & MMC_RSP_136) {
+ /* CRC is stripped so we need to do some shifting. */
+ for (i = 0; i < 4; i++) {
+ host->cmd->resp[i] = readl(host->ioaddr +
+ SDHCI_RESPONSE + (3 -
+ i)
+ * 4) << 8;
+ if (i != 3)
+ host->cmd->resp[i] |=
+ readb(host->ioaddr +
+ SDHCI_RESPONSE + (3 - i) * 4 -
+ 1);
+ }
+ } else {
+ host->cmd->resp[0] =
+ readl(host->ioaddr + SDHCI_RESPONSE);
+ }
+ }
+
+ host->cmd->error = 0;
+
+ if (host->data && host->data_early)
+ sdhci_finish_data(host);
+
+ if (!host->cmd->data)
+ tasklet_schedule(&host->finish_tasklet);
+
+ host->cmd = NULL;
+}
+
+static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ /*This variable holds the value of clock divider, prescaler */
+ int div = 0, prescaler = 0;
+ int clk_rate;
+ u32 clk;
+ unsigned long timeout;
+
+ if (clock == 0) {
+ goto out;
+ } else {
+ if (!host->plat_data->clk_flg) {
+ clk_enable(host->clk);
+ host->plat_data->clk_flg = 1;
+ }
+ }
+ if (clock == host->clock)
+ return;
+
+ clk_rate = clk_get_rate(host->clk);
+ clk = readl(host->ioaddr + SDHCI_CLOCK_CONTROL) & ~SDHCI_CLOCK_MASK;
+ writel(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ if (clock == host->min_clk)
+ prescaler = 16;
+ else
+ prescaler = 0;
+ while (prescaler <= 0x80) {
+ for (div = 0; div <= 0xF; div++) {
+ int x;
+ if (prescaler != 0)
+ x = (clk_rate / (div + 1)) / (prescaler * 2);
+ else
+ x = clk_rate / (div + 1);
+
+ DBG("x=%d, clock=%d %d\n", x, clock, div);
+ if (x <= clock)
+ break;
+ }
+ if (div < 0x10)
+ break;
+ if (prescaler == 0)
+ prescaler = 1;
+ else
+ prescaler <<= 1;
+ }
+ DBG("prescaler = 0x%x, divider = 0x%x\n", prescaler, div);
+ clk |= (prescaler << 8) | (div << 4);
+
+ /* Configure the clock control register */
+ clk |=
+ (readl(host->ioaddr + SDHCI_CLOCK_CONTROL) & (~SDHCI_CLOCK_MASK));
+ if (host->plat_data->vendor_ver < ESDHC_VENDOR_V22)
+ writel(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+ else
+ writel(clk | SDHCI_CLOCK_SD_EN,
+ host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ /* Wait max 10 ms */
+ timeout = 5000;
+ while (timeout > 0) {
+ timeout--;
+ udelay(20);
+ }
+
+ out:
+ host->clock = clock;
+}
+
+static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
+{
+ int voltage = 0;
+
+ /* There is no PWR CTL REG */
+ if (host->power == power)
+ return;
+
+ if (host->regulator_mmc) {
+ if (power == (unsigned short)-1) {
+ regulator_disable(host->regulator_mmc);
+ DBG("mmc power off\n");
+ } else {
+ if (power == 7)
+ voltage = 1800000;
+ else if (power >= 8)
+ voltage = 2000000 + (power - 8) * 100000;
+ regulator_set_voltage(host->regulator_mmc,
+ voltage, voltage);
+
+ if (regulator_enable(host->regulator_mmc) == 0) {
+ DBG("mmc power on\n");
+ msleep(1);
+ }
+ }
+ }
+
+ host->power = power;
+}
+
+/*****************************************************************************\
+ * *
+ * MMC callbacks *
+ * *
+\*****************************************************************************/
+
+static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = mmc_priv(mmc);
+
+ /* Enable the clock */
+ if (!host->plat_data->clk_flg) {
+ clk_enable(host->clk);
+ host->plat_data->clk_flg = 1;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ WARN_ON(host->mrq != NULL);
+
+ sdhci_activate_led(host);
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 0) {
+ if (mrq->cmd && mrq->data) {
+ if (mrq->data->flags & MMC_DATA_READ)
+ last_op_dir = 1;
+ else {
+ if (last_op_dir)
+ sdhci_reset(host,
+ SDHCI_RESET_CMD |
+ SDHCI_RESET_DATA);
+ }
+ }
+ }
+
+ if (host->flags & SDHCI_USE_EXTERNAL_DMA)
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ host->mrq = mrq;
+ if (!(host->flags & SDHCI_CD_PRESENT)) {
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ } else
+ sdhci_send_command(host, mrq->cmd);
+
+ if (!(host->flags & SDHCI_USE_EXTERNAL_DMA))
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ mmiowb();
+}
+
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+ u32 tmp;
+ mxc_dma_device_t dev_id = 0;
+
+ DBG("%s: clock %u, bus %lu, power %u, vdd %u\n", DRIVER_NAME,
+ ios->clock, 1UL << ios->bus_width, ios->power_mode, ios->vdd);
+
+ host = mmc_priv(mmc);
+
+ /* Configure the External DMA mode */
+ if (host->flags & SDHCI_USE_EXTERNAL_DMA) {
+ host->dma_dir = DMA_NONE;
+ if (mmc->ios.bus_width != host->mode) {
+ mxc_dma_free(host->dma);
+ if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
+ if (host->id == 0)
+ dev_id = MXC_DMA_MMC1_WIDTH_4;
+ else
+ dev_id = MXC_DMA_MMC2_WIDTH_4;
+ } else {
+ if (host->id == 0)
+ dev_id = MXC_DMA_MMC1_WIDTH_1;
+ else
+ dev_id = MXC_DMA_MMC2_WIDTH_1;
+ }
+ host->dma = mxc_dma_request(dev_id, "MXC MMC");
+ if (host->dma < 0)
+ DBG("Cannot allocate MMC DMA channel\n");
+ mxc_dma_callback_set(host->dma, sdhci_dma_irq,
+ (void *)host);
+ /* Configure the WML rege */
+ if (mxc_wml_value == 512)
+ writel(SDHCI_WML_128_WORDS,
+ host->ioaddr + SDHCI_WML);
+ else
+ writel(SDHCI_WML_16_WORDS,
+ host->ioaddr + SDHCI_WML);
+ }
+ }
+
+ host->mode = mmc->ios.bus_width;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /*
+ * Reset the chip on each power off.
+ * Should clear out any weird states.
+ */
+ if (ios->power_mode == MMC_POWER_OFF) {
+ writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ sdhci_init(host);
+ }
+
+ sdhci_set_clock(host, ios->clock);
+
+ if (ios->power_mode == MMC_POWER_OFF)
+ sdhci_set_power(host, -1);
+ else {
+ sdhci_set_power(host, ios->vdd);
+ if (!readl(host->ioaddr + SDHCI_SIGNAL_ENABLE)) {
+ tmp = readl(host->ioaddr + SDHCI_INT_ENABLE);
+ if (host->sdio_enable)
+ writel(tmp, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ else
+ writel(tmp & ~SDHCI_INT_CARD_INT,
+ host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ }
+ }
+
+ tmp = readl(host->ioaddr + SDHCI_HOST_CONTROL);
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4) {
+ tmp &= ~SDHCI_CTRL_8BITBUS;
+ tmp |= SDHCI_CTRL_4BITBUS;
+ } else if (ios->bus_width == MMC_BUS_WIDTH_8) {
+ tmp &= ~SDHCI_CTRL_4BITBUS;
+ tmp |= SDHCI_CTRL_8BITBUS;
+ } else if (ios->bus_width == MMC_BUS_WIDTH_1) {
+ tmp &= ~SDHCI_CTRL_4BITBUS;
+ tmp &= ~SDHCI_CTRL_8BITBUS;
+ }
+
+ if (host->flags & SDHCI_USE_DMA)
+ tmp |= SDHCI_CTRL_ADMA;
+
+ writel(tmp, host->ioaddr + SDHCI_HOST_CONTROL);
+
+ /*
+ * Some (ENE) controllers go apeshit on some ios operation,
+ * signalling timeout and CRC errors even on CMD0. Resetting
+ * it on each ios seems to solve the problem.
+ */
+ if (host->chip->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
+ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int sdhci_get_ro(struct mmc_host *mmc)
+{
+ struct sdhci_host *host;
+
+ host = mmc_priv(mmc);
+
+ if (host->plat_data->wp_status)
+ return host->plat_data->wp_status(mmc->parent);
+ else
+ return 0;
+}
+
+static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+ u32 ier, prot, clk, present;
+
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (enable) {
+ if (host->sdio_enable++)
+ goto exit_unlock;
+ } else {
+ if (--(host->sdio_enable))
+ goto exit_unlock;
+ }
+ /* Enable the clock */
+ if (!host->plat_data->clk_flg) {
+ clk_enable(host->clk);
+ host->plat_data->clk_flg = 1;
+ }
+ ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ prot = readl(host->ioaddr + SDHCI_HOST_CONTROL);
+ clk = readl(host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ if (enable) {
+ ier |= SDHCI_INT_CARD_INT;
+ prot |= SDHCI_CTRL_D3CD;
+ clk |= SDHCI_CLOCK_PER_EN | SDHCI_CLOCK_IPG_EN;
+ present = readl(host->ioaddr + SDHCI_PRESENT_STATE);
+ if ((present & SDHCI_CARD_INT_MASK) != SDHCI_CARD_INT_ID)
+ writel(SDHCI_INT_CARD_INT,
+ host->ioaddr + SDHCI_INT_STATUS);
+ } else {
+ ier &= ~SDHCI_INT_CARD_INT;
+ prot &= ~SDHCI_CTRL_D3CD;
+ clk &= ~(SDHCI_CLOCK_PER_EN | SDHCI_CLOCK_IPG_EN);
+ }
+
+ writel(prot, host->ioaddr + SDHCI_HOST_CONTROL);
+ writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ writel(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ mmiowb();
+ exit_unlock:
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static const struct mmc_host_ops sdhci_ops = {
+ .request = sdhci_request,
+ .set_ios = sdhci_set_ios,
+ .get_ro = sdhci_get_ro,
+ .enable_sdio_irq = sdhci_enable_sdio_irq,
+};
+
+/*****************************************************************************\
+ * *
+ * Tasklets *
+ * *
+\*****************************************************************************/
+
+static void sdhci_tasklet_card(unsigned long param)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+ unsigned int cd_status = 0;
+
+ host = (struct sdhci_host *)param;
+
+ if (host->flags & SDHCI_CD_PRESENT)
+ host->flags &= ~SDHCI_CD_PRESENT;
+ else
+ host->flags |= SDHCI_CD_PRESENT;
+ /* Detect there is a card in slot or not */
+ DBG("cd_status=%d %s\n", cd_status,
+ (host->flags & SDHCI_CD_PRESENT) ? "inserted" : "removed");
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!(host->flags & SDHCI_CD_PRESENT)) {
+ if (host->mrq) {
+ printk(KERN_ERR "%s: Card removed during transfer!\n",
+ mmc_hostname(host->mmc));
+ printk(KERN_ERR "%s: Resetting controller.\n",
+ mmc_hostname(host->mmc));
+
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ mmc_detect_change(host->mmc, msecs_to_jiffies(200));
+}
+
+static void sdhci_tasklet_finish(unsigned long param)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+ struct mmc_request *mrq;
+
+ host = (struct sdhci_host *)param;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ del_timer(&host->timer);
+
+ mrq = host->mrq;
+
+ /*
+ * The controller needs a reset of internal state machines
+ * upon error conditions.
+ */
+ if (mrq->cmd->error ||
+ (mrq->data && (mrq->data->error ||
+ (mrq->data->stop && mrq->data->stop->error))) ||
+ (host->chip->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
+
+ /* Some controllers need this kick or reset won't work here */
+ if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
+ unsigned int clock;
+
+ /* This is to force an update */
+ clock = host->clock;
+ host->clock = 0;
+ sdhci_set_clock(host, clock);
+ }
+
+ /* Spec says we should do both at the same time, but Ricoh
+ controllers do not like that. */
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ }
+
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+
+ sdhci_deactivate_led(host);
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ /* Stop the clock when the req is done */
+ flags = SDHCI_DATA_ACTIVE | SDHCI_DOING_WRITE | SDHCI_DOING_READ;
+ if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & flags)) {
+ if (host->plat_data->clk_flg) {
+ clk_disable(host->clk);
+ host->plat_data->clk_flg = 0;
+ }
+ }
+
+ mmc_request_done(host->mmc, mrq);
+}
+
+static void sdhci_timeout_timer(unsigned long data)
+{
+ struct sdhci_host *host;
+ unsigned long tmp, flags;
+
+ host = (struct sdhci_host *)data;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->mrq) {
+ printk(KERN_ERR "%s: Timeout waiting for hardware "
+ "interrupt.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+
+ if (host->data) {
+ host->data->error = -ETIMEDOUT;
+ sdhci_finish_data(host);
+ } else {
+ if (host->cmd)
+ host->cmd->error = -ETIMEDOUT;
+ else
+ host->mrq->cmd->error = -ETIMEDOUT;
+
+ tasklet_schedule(&host->finish_tasklet);
+ }
+
+ if (!readl(host->ioaddr + SDHCI_SIGNAL_ENABLE)) {
+ printk(KERN_ERR "%s, ERROR SIG_INT is 0.\n", __func__);
+ tmp = readl(host->ioaddr + SDHCI_INT_ENABLE);
+ if (host->sdio_enable)
+ writel(tmp, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ else
+ writel(tmp & ~SDHCI_INT_CARD_INT,
+ host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ if (!host->plat_data->status(host->mmc->parent))
+ schedule_work(&host->cd_wq);
+ }
+ }
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdhci_cd_timer(unsigned long data)
+{
+ struct sdhci_host *host;
+
+ host = (struct sdhci_host *)data;
+ schedule_work(&host->cd_wq);
+}
+
+/*****************************************************************************\
+ * *
+ * Interrupt handling *
+ * *
+\*****************************************************************************/
+
+static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
+{
+ BUG_ON(intmask == 0);
+
+ if (!host->cmd) {
+ printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
+ "though no command operation was in progress.\n",
+ mmc_hostname(host->mmc), (unsigned)intmask);
+ sdhci_dumpregs(host);
+ return;
+ }
+
+ if (intmask & SDHCI_INT_TIMEOUT)
+ host->cmd->error = -ETIMEDOUT;
+ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
+ SDHCI_INT_INDEX))
+ host->cmd->error = -EILSEQ;
+
+ if (host->cmd->error)
+ tasklet_schedule(&host->finish_tasklet);
+ else if (intmask & SDHCI_INT_RESPONSE)
+ sdhci_finish_command(host);
+}
+
+static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
+{
+ u32 intsave = 0;
+
+ BUG_ON(intmask == 0);
+
+ if (!host->data) {
+ /*
+ * A data end interrupt is sent together with the response
+ * for the stop command.
+ */
+ if (intmask & SDHCI_INT_DATA_END)
+ return;
+
+ printk(KERN_ERR "%s: Got data interrupt 0x%08x even "
+ "though no data operation was in progress.\n",
+ mmc_hostname(host->mmc), (unsigned)intmask);
+ sdhci_dumpregs(host);
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ return;
+ }
+
+ /* Mask the INT */
+ intsave = readl(host->ioaddr + SDHCI_INT_ENABLE);
+ writel(intsave & (~(intmask & SDHCI_INT_DATA_RE_MASK)),
+ host->ioaddr + SDHCI_INT_ENABLE);
+
+ if (intmask & SDHCI_INT_DATA_TIMEOUT)
+ host->data->error = -ETIMEDOUT;
+ else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT))
+ host->data->error = -EILSEQ;
+
+ if (host->data->error)
+ sdhci_finish_data(host);
+ else {
+ if ((host->flags & SDHCI_USE_EXTERNAL_DMA) &&
+ (host->dma_size >= mxc_wml_value)) {
+ /* Use DMA if transfer size is greater than fifo size */
+ if (intmask & (SDHCI_INT_DATA_AVAIL |
+ SDHCI_INT_SPACE_AVAIL)) {
+ intsave &= ~SDHCI_INT_DATA_RE_MASK;
+ if (mxc_dma_enable(host->dma) < 0) {
+ printk(KERN_ERR "ENABLE SDMA ERR.\n");
+ intsave |= SDHCI_INT_DATA_RE_MASK;
+ }
+ }
+ } else {
+ if (intmask & (SDHCI_INT_DATA_AVAIL |
+ SDHCI_INT_SPACE_AVAIL))
+ sdhci_transfer_pio(host);
+ }
+
+ /*
+ * We currently don't do anything fancy with DMA
+ * boundaries, but as we can't disable the feature
+ * we need to at least restart the transfer.
+ */
+ if ((intmask & SDHCI_INT_DMA_END) &&
+ (!(intmask & SDHCI_INT_DATA_END)))
+ writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS),
+ host->ioaddr + SDHCI_DMA_ADDRESS);
+
+ if (intmask & SDHCI_INT_DATA_END) {
+ if (host->data->flags & MMC_DATA_READ)
+ writel(readl(host->ioaddr + SDHCI_CLOCK_CONTROL)
+ & ~SDHCI_CLOCK_HLK_EN,
+ host->ioaddr + SDHCI_CLOCK_CONTROL);
+ if (host->cmd) {
+ /*
+ * Data managed to finish before the
+ * command completed. Make sure we do
+ * things in the proper order.
+ */
+ host->data_early = 1;
+ } else {
+
+ if (host->plat_data->vendor_ver
+ < ESDHC_VENDOR_V22) {
+ /*
+ * There are the DATA END INT when
+ * writing is not complete. Double
+ * check on it. TO2 has been fixed it.
+ */
+ intmask = readl(host->ioaddr +
+ SDHCI_PRESENT_STATE);
+ if (intmask & SDHCI_DATA_ACTIVE)
+ goto data_irq_out;
+ }
+ sdhci_finish_data(host);
+ }
+ }
+ }
+ data_irq_out:
+ /* Enable the INT */
+ writel(intsave, host->ioaddr + SDHCI_INT_ENABLE);
+}
+
+/*!
+* This function is called by DMA Interrupt Service Routine to indicate
+* requested DMA transfer is completed.
+*
+* @param devid pointer to device specific structure
+* @param error any DMA error
+* @param cnt amount of data that was transferred
+*/
+static void sdhci_dma_irq(void *devid, int error, unsigned int cnt)
+{
+ u32 intsave = 0;
+ int ret;
+ struct sdhci_host *host = devid;
+
+ DBG("%s: error: %d Transferred bytes:%d\n", DRIVER_NAME, error, cnt);
+ if (host->flags & SDHCI_USE_EXTERNAL_DMA) {
+ /*
+ * Stop the DMA transfer here, the data_irq would be called
+ * to process the others
+ */
+ ret = mxc_dma_disable(host->dma);
+ if (ret < 0)
+ printk(KERN_ERR "Disable dma channel err %d\n", ret);
+
+ if (error) {
+ DBG("Error in DMA transfer\n");
+ return;
+ }
+ intsave = readl(host->ioaddr + SDHCI_INT_ENABLE);
+ intsave |= SDHCI_INT_DATA_RE_MASK;
+ writel(intsave, host->ioaddr + SDHCI_INT_ENABLE);
+ }
+}
+
+/* woke queue handler func */
+static void esdhc_cd_callback(struct work_struct *work)
+{
+ unsigned long flags;
+ unsigned int cd_status = 0;
+ struct sdhci_host *host = container_of(work, struct sdhci_host, cd_wq);
+
+ cd_status = host->plat_data->status(host->mmc->parent);
+ if (cd_status)
+ host->flags &= ~SDHCI_CD_PRESENT;
+ else
+ host->flags |= SDHCI_CD_PRESENT;
+ /* Detect there is a card in slot or not */
+ DBG("cd_status=%d %s\n", cd_status,
+ (host->flags & SDHCI_CD_PRESENT) ? "inserted" : "removed");
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!(host->flags & SDHCI_CD_PRESENT)) {
+ printk(KERN_INFO
+ "%s: Card removed and resetting controller.\n",
+ mmc_hostname(host->mmc));
+ if (host->mrq) {
+ struct mmc_data *data;
+ data = host->data;
+ host->data = NULL;
+
+ printk(KERN_ERR
+ "%s: Card removed during transfer!\n",
+ mmc_hostname(host->mmc));
+ printk(KERN_ERR
+ "%s: Resetting controller.\n",
+ mmc_hostname(host->mmc));
+
+ if ((host->flags & SDHCI_USE_EXTERNAL_DMA) &&
+ (data != NULL)) {
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ host->dma_len, host->dma_dir);
+ host->dma_size = 0;
+ }
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+
+ if (host->init_flag > 0)
+ /* The initialization of sdhc controller has been
+ * done in the resume func */
+ host->init_flag--;
+ else
+ sdhci_init(host);
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (host->flags & SDHCI_CD_PRESENT) {
+ del_timer(&host->cd_timer);
+ mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+ } else
+ mmc_detect_change(host->mmc, 0);
+}
+
+/*!
+* Card detection interrupt service routine registered to handle
+* the SDHC interrupts. This interrupt routine handles card
+* insertion and card removal interrupts.
+*
+* @param irq the interrupt number
+* @param devid driver private data
+*
+* @return The function returns \b IRQ_RETVAL(1)
+*/
+static irqreturn_t sdhci_cd_irq(int irq, void *dev_id)
+{
+ unsigned int cd_status = 0;
+ struct sdhci_host *host = dev_id;
+
+ do {
+ if (host->detect_irq == 0)
+ break;
+ cd_status = host->plat_data->status(host->mmc->parent);
+ if (cd_status)
+ set_irq_type(host->detect_irq, IRQF_TRIGGER_FALLING);
+ else
+ set_irq_type(host->detect_irq, IRQF_TRIGGER_RISING);
+ } while (cd_status != host->plat_data->status(host->mmc->parent));
+
+ DBG("cd_status=%d %s\n", cd_status, cd_status ? "removed" : "inserted");
+
+ cd_status = host->plat_data->status(host->mmc->parent);
+ if (!cd_status)
+ /* If there is a card in the slot, the timer is start
+ * to work. Then the card detection would be carried
+ * after the timer is timeout.
+ * */
+ mod_timer(&host->cd_timer, jiffies + HZ / 2);
+ else
+ /* If there is no card, call the card detection func
+ * immediately. */
+ schedule_work(&host->cd_wq);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sdhci_irq(int irq, void *dev_id)
+{
+ irqreturn_t result;
+ struct sdhci_host *host = dev_id;
+ u32 intmask;
+ int cardint = 0;
+
+ spin_lock(&host->lock);
+
+ intmask = readl(host->ioaddr + SDHCI_INT_STATUS);
+
+ if (!intmask || intmask == 0xffffffff) {
+ result = IRQ_NONE;
+ goto out;
+ }
+
+ DBG("*** %s got interrupt: 0x%08x\n", mmc_hostname(host->mmc), intmask);
+
+ if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
+ writel(intmask &
+ (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
+ host->ioaddr + SDHCI_INT_STATUS);
+ tasklet_schedule(&host->card_tasklet);
+ }
+
+ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
+
+ if (intmask & SDHCI_INT_CMD_MASK) {
+ writel(intmask & SDHCI_INT_CMD_MASK,
+ host->ioaddr + SDHCI_INT_STATUS);
+ sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
+ }
+
+ if (intmask & SDHCI_INT_DATA_MASK) {
+ writel(intmask & SDHCI_INT_DATA_MASK,
+ host->ioaddr + SDHCI_INT_STATUS);
+ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 0) {
+ if (!
+ (readl(host->ioaddr + SDHCI_TRANSFER_MODE) &
+ SDHCI_TRNS_READ))
+ intmask &= ~SDHCI_INT_DATA_END_BIT;
+ }
+ if (intmask & SDHCI_INT_DATA_MASK)
+ sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
+ }
+
+ intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
+
+ intmask &= ~SDHCI_INT_ERROR;
+
+ if (intmask & SDHCI_INT_BUS_POWER) {
+ printk(KERN_ERR "%s: Card is consuming too much power!\n",
+ mmc_hostname(host->mmc));
+ writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);
+ }
+
+ intmask &= ~SDHCI_INT_BUS_POWER;
+
+ if (intmask & SDHCI_INT_CARD_INT)
+ cardint = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE) &
+ SDHCI_INT_CARD_INT;
+
+ intmask &= ~SDHCI_INT_CARD_INT;
+
+ if (intmask) {
+ printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
+ mmc_hostname(host->mmc), intmask);
+ sdhci_dumpregs(host);
+
+ writel(intmask, host->ioaddr + SDHCI_INT_STATUS);
+ }
+
+ result = IRQ_HANDLED;
+
+ mmiowb();
+ out:
+ spin_unlock(&host->lock);
+
+ /*
+ * We have to delay this as it calls back into the driver.
+ */
+ if (cardint)
+ mmc_signal_sdio_irq(host->mmc);
+
+ return result;
+}
+
+/*****************************************************************************\
+ * *
+ * Suspend/resume *
+ * *
+\*****************************************************************************/
+
+#ifdef CONFIG_PM
+
+static int sdhci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct sdhci_chip *chip;
+ int i, ret;
+
+ chip = dev_get_drvdata(&pdev->dev);
+ if (!chip)
+ return 0;
+
+ DBG("Suspending...\n");
+
+ for (i = 0; i < chip->num_slots; i++) {
+ if (!chip->hosts[i])
+ continue;
+ ret = mmc_suspend_host(chip->hosts[i]->mmc, state);
+ if (ret) {
+ for (i--; i >= 0; i--)
+ mmc_resume_host(chip->hosts[i]->mmc);
+ return ret;
+ }
+ }
+
+ for (i = 0; i < chip->num_slots; i++) {
+ if (!chip->hosts[i])
+ continue;
+ free_irq(chip->hosts[i]->irq, chip->hosts[i]);
+ }
+
+ return 0;
+}
+
+static int sdhci_resume(struct platform_device *pdev)
+{
+ struct sdhci_chip *chip;
+ int i, ret;
+
+ chip = dev_get_drvdata(&pdev->dev);
+ if (!chip)
+ return 0;
+
+ DBG("Resuming...\n");
+
+ for (i = 0; i < chip->num_slots; i++) {
+ if (!chip->hosts[i])
+ continue;
+ ret = request_irq(chip->hosts[i]->irq, sdhci_irq,
+ IRQF_SHARED,
+ mmc_hostname(chip->hosts[i]->mmc),
+ chip->hosts[i]);
+ if (ret)
+ return ret;
+ sdhci_init(chip->hosts[i]);
+ chip->hosts[i]->init_flag = 2;
+ mmiowb();
+ ret = mmc_resume_host(chip->hosts[i]->mmc);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+#else /* CONFIG_PM */
+
+#define sdhci_suspend NULL
+#define sdhci_resume NULL
+
+#endif /* CONFIG_PM */
+
+/*****************************************************************************\
+ * *
+ * Device probing/removal *
+ * *
+\*****************************************************************************/
+
+static int __devinit sdhci_probe_slot(struct platform_device
+ *pdev, int slot)
+{
+ struct mxc_mmc_platform_data *mmc_plat = pdev->dev.platform_data;
+ int ret = 0;
+ unsigned int version, caps;
+ struct sdhci_chip *chip;
+ struct mmc_host *mmc;
+ struct sdhci_host *host;
+ mxc_dma_device_t dev_id = 0;
+
+ if (!mmc_plat)
+ return -EINVAL;
+
+ chip = dev_get_drvdata(&pdev->dev);
+ BUG_ON(!chip);
+
+ mmc = mmc_alloc_host(sizeof(struct sdhci_host), &pdev->dev);
+ if (!mmc)
+ return -ENOMEM;
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->id = pdev->id;
+ host->dma = -1;
+ host->plat_data = mmc_plat;
+ if (!host->plat_data) {
+ ret = -EINVAL;
+ goto out0;
+ }
+
+ host->chip = chip;
+ chip->hosts[slot] = host;
+
+ /* Get pwr supply for eSDHC */
+ if (NULL != mmc_plat->power_mmc) {
+ host->regulator_mmc =
+ regulator_get(&pdev->dev, mmc_plat->power_mmc);
+ if (IS_ERR(host->regulator_mmc)) {
+ ret = PTR_ERR(host->regulator_mmc);
+ goto out1;
+ }
+ if (regulator_enable(host->regulator_mmc) == 0) {
+ DBG("mmc power on\n");
+ msleep(1);
+ }
+ }
+
+ /* Active the eSDHC bus */
+ gpio_sdhc_active(pdev->id);
+
+ /* Get the SDHC clock from clock system APIs */
+ host->clk = clk_get(&pdev->dev, mmc_plat->clock_mmc);
+ if (NULL == host->clk)
+ printk(KERN_ERR "MXC MMC can't get clock.\n");
+ DBG("SDHC:%d clock:%lu\n", pdev->id, clk_get_rate(host->clk));
+
+ host->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!host->res) {
+ ret = -ENOMEM;
+ goto out2;
+ }
+ host->irq = platform_get_irq(pdev, 0);
+ if (!host->irq) {
+ ret = -ENOMEM;
+ goto out2;
+ }
+ host->detect_irq = platform_get_irq(pdev, 1);
+ if (!host->detect_irq) {
+ host->flags &= ~SDHCI_CD_PRESENT;
+ if ((pdev->id >= 0) && (pdev->id < MXC_SDHCI_NUM))
+ mxc_fix_chips[pdev->id] = chip;
+ goto no_detect_irq;
+ }
+
+ do {
+ ret = host->plat_data->status(host->mmc->parent);
+ if (ret)
+ set_irq_type(host->detect_irq, IRQF_TRIGGER_FALLING);
+ else
+ set_irq_type(host->detect_irq, IRQF_TRIGGER_RISING);
+ } while (ret != host->plat_data->status(host->mmc->parent));
+
+ ret = host->plat_data->status(host->mmc->parent);
+ if (ret)
+ host->flags &= ~SDHCI_CD_PRESENT;
+ else
+ host->flags |= SDHCI_CD_PRESENT;
+
+ no_detect_irq:
+ DBG("slot %d at 0x%x, irq %d \n", slot, host->res->start, host->irq);
+ if (!request_mem_region(host->res->start,
+ host->res->end -
+ host->res->start + 1, pdev->name)) {
+ printk(KERN_ERR "request_mem_region failed\n");
+ ret = -ENOMEM;
+ goto out2;
+ }
+ host->ioaddr = (void *)ioremap(host->res->start, host->res->end -
+ host->res->start + 1);
+ if (!host->ioaddr) {
+ ret = -ENOMEM;
+ goto out3;
+ }
+
+ sdhci_reset(host, SDHCI_RESET_ALL);
+
+ version = readl(host->ioaddr + SDHCI_HOST_VERSION);
+ host->plat_data->vendor_ver = (version & SDHCI_VENDOR_VER_MASK) >>
+ SDHCI_VENDOR_VER_SHIFT;
+ version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
+ if (version != 1) {
+ printk(KERN_ERR "%s: Unknown controller version (%d). "
+ "You may experience problems.\n", mmc_hostname(mmc),
+ version);
+ }
+
+ caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
+
+ if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)
+ host->flags |= SDHCI_USE_DMA;
+ else if (!(caps & SDHCI_CAN_DO_DMA))
+ DBG("Controller doesn't have DMA capability\n");
+ else if (chip->
+ quirks & (SDHCI_QUIRK_INTERNAL_ADVANCED_DMA |
+ SDHCI_QUIRK_INTERNAL_SIMPLE_DMA))
+ host->flags |= SDHCI_USE_DMA;
+ else if (chip->quirks & (SDHCI_QUIRK_EXTERNAL_DMA_MODE))
+ host->flags |= SDHCI_USE_EXTERNAL_DMA;
+ else
+ host->flags &= ~SDHCI_USE_DMA;
+
+ /*
+ * These definitions of eSDHC are not compatible with the SD Host
+ * Controller Spec v2.0
+ */
+ host->min_clk = mmc_plat->min_clk;
+ host->max_clk = mmc_plat->max_clk;
+ host->timeout_clk = 1024 * 1000; /* Just set the value temply. */
+
+ /*
+ * Set host parameters.
+ */
+ mmc->ops = &sdhci_ops;
+ mmc->f_min = host->min_clk;
+ mmc->f_max = host->max_clk;
+ mmc->caps = MMC_CAP_SDIO_IRQ;
+ mmc->caps |= mmc_plat->caps;
+
+ if (caps & SDHCI_CAN_DO_HISPD)
+ mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+
+ mmc->ocr_avail = mmc_plat->ocr_mask;
+ if (caps & SDHCI_CAN_VDD_330)
+ mmc->ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
+ if (caps & SDHCI_CAN_VDD_300)
+ mmc->ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
+ if (caps & SDHCI_CAN_VDD_180)
+ mmc->ocr_avail |= MMC_VDD_165_195;
+
+ if (mmc->ocr_avail == 0) {
+ printk(KERN_ERR "%s: Hardware doesn't report any "
+ "support voltages.\n", mmc_hostname(mmc));
+ ret = -ENODEV;
+ goto out3;
+ }
+
+ spin_lock_init(&host->lock);
+
+ /*
+ * Maximum number of segments. Hardware cannot do scatter lists.
+ */
+ if (host->flags & SDHCI_USE_DMA)
+ mmc->max_hw_segs = 1;
+ else
+ mmc->max_hw_segs = 16;
+ mmc->max_phys_segs = 16;
+
+ /*
+ * Maximum number of sectors in one transfer. Limited by DMA boundary
+ * size (512KiB).
+ */
+ if (host->flags & SDHCI_USE_EXTERNAL_DMA)
+ mmc->max_req_size = 32 * 1024;
+ else
+ mmc->max_req_size = 524288;
+
+ /*
+ * Maximum segment size. Could be one segment with the maximum number
+ * of bytes.
+ */
+ mmc->max_seg_size = mmc->max_req_size;
+
+ /*
+ * Maximum block size. This varies from controller to controller and
+ * is specified in the capabilities register.
+ */
+ mmc->max_blk_size =
+ (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;
+ if (mmc->max_blk_size > 3) {
+ printk(KERN_WARNING "%s: Invalid maximum block size, "
+ "assuming 512 bytes\n", mmc_hostname(mmc));
+ mmc->max_blk_size = 512;
+ } else
+ mmc->max_blk_size = 512 << mmc->max_blk_size;
+
+ /*
+ * Maximum block count.
+ */
+ mmc->max_blk_count = 65535;
+
+ /*
+ * Apply a continous physical memory used for storing the ADMA
+ * descriptor table.
+ */
+ if (host->flags & SDHCI_USE_DMA) {
+ adma_des_table = kcalloc((2 * (mmc->max_phys_segs) + 1),
+ sizeof(unsigned int), GFP_DMA);
+ if (adma_des_table == NULL) {
+ printk(KERN_ERR "Cannot allocate ADMA memory\n");
+ ret = -ENOMEM;
+ goto out3;
+ }
+ }
+
+ /*
+ * Init tasklets.
+ */
+ tasklet_init(&host->card_tasklet,
+ sdhci_tasklet_card, (unsigned long)host);
+ tasklet_init(&host->finish_tasklet,
+ sdhci_tasklet_finish, (unsigned long)host);
+
+ /* initialize the work queue */
+ INIT_WORK(&host->cd_wq, esdhc_cd_callback);
+
+ setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
+ setup_timer(&host->cd_timer, sdhci_cd_timer, (unsigned long)host);
+
+ if (host->detect_irq) {
+ ret = request_irq(host->detect_irq, sdhci_cd_irq, 0,
+ pdev->name, host);
+ if (ret)
+ goto out4;
+ }
+
+ ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, pdev->name, host);
+ if (ret)
+ goto out5;
+
+ sdhci_init(host);
+
+ if (host->flags & SDHCI_USE_EXTERNAL_DMA) {
+ /* Apply the 1-bit SDMA channel. */
+ if (host->id == 0)
+ dev_id = MXC_DMA_MMC1_WIDTH_1;
+ else
+ dev_id = MXC_DMA_MMC2_WIDTH_1;
+ host->dma = mxc_dma_request(dev_id, "MXC MMC");
+ if (host->dma < 0) {
+ DBG("Cannot allocate MMC DMA channel\n");
+ goto out6;
+ }
+ mxc_dma_callback_set(host->dma, sdhci_dma_irq, (void *)host);
+ }
+#ifdef CONFIG_MMC_DEBUG
+ sdhci_dumpregs(host);
+#endif
+
+ mmiowb();
+
+ if (mmc_add_host(mmc) < 0)
+ goto out6;
+ if (host->flags & SDHCI_USE_EXTERNAL_DMA)
+ printk(KERN_INFO "%s: SDHCI detect irq %d irq %d %s\n",
+ mmc_hostname(mmc), host->detect_irq, host->irq,
+ "EXTERNAL DMA");
+ else
+ printk(KERN_INFO "%s: SDHCI detect irq %d irq %d %s\n",
+ mmc_hostname(mmc), host->detect_irq, host->irq,
+ (host->flags & SDHCI_USE_DMA) ? "INTERNAL DMA" : "PIO");
+
+ return 0;
+
+ out6:
+ free_irq(host->irq, host);
+ out5:
+ if (host->detect_irq)
+ free_irq(host->detect_irq, host);
+ else {
+ if ((pdev->id >= 0) && (pdev->id < MXC_SDHCI_NUM))
+ mxc_fix_chips[pdev->id] = chip;
+ }
+ out4:
+ del_timer_sync(&host->timer);
+ del_timer_sync(&host->cd_timer);
+ tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+ out3:
+ if (host->flags & SDHCI_USE_DMA)
+ kfree(adma_des_table);
+ release_mem_region(host->res->start,
+ host->res->end - host->res->start + 1);
+ out2:
+ clk_disable(host->clk);
+ host->plat_data->clk_flg = 0;
+ clk_put(host->clk);
+ out1:
+ gpio_sdhc_inactive(pdev->id);
+ out0:
+ mmc_free_host(mmc);
+ return ret;
+}
+
+static void sdhci_remove_slot(struct platform_device *pdev, int slot)
+{
+ struct sdhci_chip *chip;
+ struct mmc_host *mmc;
+ struct sdhci_host *host;
+
+ chip = dev_get_drvdata(&pdev->dev);
+ host = chip->hosts[slot];
+ mmc = host->mmc;
+
+ chip->hosts[slot] = NULL;
+
+ mmc_remove_host(mmc);
+
+ sdhci_reset(host, SDHCI_RESET_ALL);
+
+ if (host->detect_irq)
+ free_irq(host->detect_irq, host);
+ else {
+ if ((pdev->id >= 0) && (pdev->id < MXC_SDHCI_NUM))
+ mxc_fix_chips[pdev->id] = NULL;
+ }
+ free_irq(host->irq, host);
+ if (chip->quirks & SDHCI_QUIRK_EXTERNAL_DMA_MODE) {
+ host->flags &= ~SDHCI_USE_EXTERNAL_DMA;
+ mxc_dma_free(host->dma);
+ }
+
+ del_timer_sync(&host->timer);
+
+ tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+
+ if (host->flags & SDHCI_USE_DMA)
+ kfree(adma_des_table);
+ release_mem_region(host->res->start,
+ host->res->end - host->res->start + 1);
+ clk_disable(host->clk);
+ host->plat_data->clk_flg = 0;
+ clk_put(host->clk);
+ mmc_free_host(mmc);
+ gpio_sdhc_inactive(pdev->id);
+}
+
+static int sdhci_probe(struct platform_device *pdev)
+{
+ int ret = 0, i;
+ u8 slots = 1;
+ struct sdhci_chip *chip;
+
+ printk(KERN_INFO DRIVER_NAME ": MXC SDHCI Controller Driver. \n");
+ BUG_ON(pdev == NULL);
+
+ chip = kzalloc(sizeof(struct sdhci_chip) +
+ sizeof(struct sdhci_host *) * slots, GFP_KERNEL);
+ if (!chip) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Distinguish different platform */
+ if (machine_is_mx37_3ds()) {
+ mxc_quirks = SDHCI_QUIRK_EXTERNAL_DMA_MODE;
+ } else {
+ mxc_quirks = SDHCI_QUIRK_INTERNAL_ADVANCED_DMA |
+ SDHCI_QUIRK_INTERNAL_SIMPLE_DMA;
+ }
+ chip->pdev = pdev;
+ chip->quirks = mxc_quirks;
+
+ if (debug_quirks)
+ chip->quirks = debug_quirks;
+
+ chip->num_slots = slots;
+ dev_set_drvdata(&pdev->dev, chip);
+
+ for (i = 0; i < slots; i++) {
+ ret = sdhci_probe_slot(pdev, i);
+ if (ret) {
+ for (i--; i >= 0; i--)
+ sdhci_remove_slot(pdev, i);
+ goto free;
+ }
+ }
+
+ return 0;
+
+ free:
+ dev_set_drvdata(&pdev->dev, NULL);
+ kfree(chip);
+
+ err:
+ return ret;
+}
+
+static int sdhci_remove(struct platform_device *pdev)
+{
+ int i;
+ struct sdhci_chip *chip;
+
+ chip = dev_get_drvdata(&pdev->dev);
+
+ if (chip) {
+ for (i = 0; i < chip->num_slots; i++)
+ sdhci_remove_slot(pdev, i);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ kfree(chip);
+ }
+
+ return 0;
+}
+
+static struct platform_driver sdhci_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = sdhci_probe,
+ .remove = sdhci_remove,
+ .suspend = sdhci_suspend,
+ .resume = sdhci_resume,
+};
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int __init sdhci_drv_init(void)
+{
+ printk(KERN_INFO DRIVER_NAME
+ ": MXC Secure Digital Host Controller Interface driver\n");
+ return platform_driver_register(&sdhci_driver);
+}
+
+static void __exit sdhci_drv_exit(void)
+{
+ DBG("Exiting\n");
+
+ platform_driver_unregister(&sdhci_driver);
+}
+
+module_init(sdhci_drv_init);
+module_exit(sdhci_drv_exit);
+
+module_param(debug_quirks, uint, 0444);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Secure Digital Host Controller Interface driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
diff --git a/drivers/mmc/host/mx_sdhci.h b/drivers/mmc/host/mx_sdhci.h
new file mode 100644
index 000000000000..509d444a6e81
--- /dev/null
+++ b/drivers/mmc/host/mx_sdhci.h
@@ -0,0 +1,275 @@
+/*
+ * linux/drivers/mmc/host/mx_sdhci.h - Secure Digital Host
+ * Controller Interface driver
+ *
+ * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+/*
+ * Controller registers
+ */
+
+#define SDHCI_DMA_ADDRESS 0x00
+
+#define SDHCI_BLOCK_SIZE 0x04
+#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 13) | (blksz & 0x1FFF))
+
+#define SDHCI_BLOCK_COUNT 0x04
+
+#define SDHCI_ARGUMENT 0x08
+
+#define SDHCI_TRANSFER_MODE 0x0C
+#define SDHCI_TRNS_DMA 0x00000001
+#define SDHCI_TRNS_BLK_CNT_EN 0x00000002
+#define SDHCI_TRNS_ACMD12 0x00000004
+#define SDHCI_TRNS_READ 0x00000010
+#define SDHCI_TRNS_MULTI 0x00000020
+#define SDHCI_TRNS_DPSEL 0x00200000
+#define SDHCI_TRNS_MASK 0xFFFF0000
+
+#define SDHCI_COMMAND 0x0E
+#define SDHCI_CMD_RESP_MASK 0x03
+#define SDHCI_CMD_CRC 0x08
+#define SDHCI_CMD_INDEX 0x10
+#define SDHCI_CMD_DATA 0x20
+
+#define SDHCI_CMD_RESP_NONE 0x00
+#define SDHCI_CMD_RESP_LONG 0x01
+#define SDHCI_CMD_RESP_SHORT 0x02
+#define SDHCI_CMD_RESP_SHORT_BUSY 0x03
+
+#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) << 16
+
+#define SDHCI_RESPONSE 0x10
+
+#define SDHCI_BUFFER 0x20
+
+#define SDHCI_PRESENT_STATE 0x24
+#define SDHCI_CMD_INHIBIT 0x00000001
+#define SDHCI_DATA_INHIBIT 0x00000002
+#define SDHCI_DATA_ACTIVE 0x00000004
+#define SDHCI_DOING_WRITE 0x00000100
+#define SDHCI_DOING_READ 0x00000200
+#define SDHCI_SPACE_AVAILABLE 0x00000400
+#define SDHCI_DATA_AVAILABLE 0x00000800
+#define SDHCI_CARD_PRESENT 0x00010000
+#define SDHCI_WRITE_PROTECT 0x00080000
+#define SDHCI_DAT0_IDLE 0x01000000
+#define SDHCI_CARD_INT_MASK 0x0E000000
+#define SDHCI_CARD_INT_ID 0x0C000000
+
+#define SDHCI_HOST_CONTROL 0x28
+#define SDHCI_CTRL_LED 0x00000001
+#define SDHCI_CTRL_4BITBUS 0x00000002
+#define SDHCI_CTRL_8BITBUS 0x00000004
+#define SDHCI_CTRL_HISPD 0x00000004
+#define SDHCI_CTRL_DMA_MASK 0x18
+#define SDHCI_CTRL_SDMA 0x00
+#define SDHCI_CTRL_ADMA1 0x08
+#define SDHCI_CTRL_ADMA32 0x10
+#define SDHCI_CTRL_ADMA64 0x18
+#define SDHCI_CTRL_D3CD 0x00000008
+#define SDHCI_CTRL_ADMA 0x00000100
+/* wake up control */
+#define SDHCI_CTRL_WECINS 0x04000000
+
+#define SDHCI_POWER_CONTROL 0x29
+#define SDHCI_POWER_ON 0x01
+#define SDHCI_POWER_180 0x0A
+#define SDHCI_POWER_300 0x0C
+#define SDHCI_POWER_330 0x0E
+
+#define SDHCI_BLOCK_GAP_CONTROL 0x2A
+
+#define SDHCI_WAKE_UP_CONTROL 0x2B
+
+#define SDHCI_CLOCK_CONTROL 0x2C
+#define SDHCI_DIVIDER_SHIFT 8
+#define SDHCI_CLOCK_SD_EN 0x00000008
+#define SDHCI_CLOCK_PER_EN 0x00000004
+#define SDHCI_CLOCK_HLK_EN 0x00000002
+#define SDHCI_CLOCK_IPG_EN 0x00000001
+#define SDHCI_CLOCK_MASK 0x0000FFFF
+
+#define SDHCI_TIMEOUT_CONTROL 0x2E
+
+#define SDHCI_SOFTWARE_RESET 0x2F
+#define SDHCI_RESET_ALL 0x01
+#define SDHCI_RESET_CMD 0x02
+#define SDHCI_RESET_DATA 0x04
+
+#define SDHCI_INT_STATUS 0x30
+#define SDHCI_INT_ENABLE 0x34
+#define SDHCI_SIGNAL_ENABLE 0x38
+#define SDHCI_INT_RESPONSE 0x00000001
+#define SDHCI_INT_DATA_END 0x00000002
+#define SDHCI_INT_DMA_END 0x00000008
+#define SDHCI_INT_SPACE_AVAIL 0x00000010
+#define SDHCI_INT_DATA_AVAIL 0x00000020
+#define SDHCI_INT_CARD_INSERT 0x00000040
+#define SDHCI_INT_CARD_REMOVE 0x00000080
+#define SDHCI_INT_CARD_INT 0x00000100
+#define SDHCI_INT_ERROR 0x00008000
+#define SDHCI_INT_TIMEOUT 0x00010000
+#define SDHCI_INT_CRC 0x00020000
+#define SDHCI_INT_END_BIT 0x00040000
+#define SDHCI_INT_INDEX 0x00080000
+#define SDHCI_INT_DATA_TIMEOUT 0x00100000
+#define SDHCI_INT_DATA_CRC 0x00200000
+#define SDHCI_INT_DATA_END_BIT 0x00400000
+#define SDHCI_INT_BUS_POWER 0x00800000
+#define SDHCI_INT_ACMD12ERR 0x01000000
+#define SDHCI_INT_ADMA_ERROR 0x10000000
+
+#define SDHCI_INT_NORMAL_MASK 0x00007FFF
+#define SDHCI_INT_ERROR_MASK 0xFFFF8000
+
+#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
+ SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
+#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
+ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
+ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
+ SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR)
+#define SDHCI_INT_DATA_RE_MASK (SDHCI_INT_DMA_END | \
+ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)
+
+#define SDHCI_ACMD12_ERR 0x3C
+
+/* 3E-3F reserved */
+
+#define SDHCI_CAPABILITIES 0x40
+#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
+#define SDHCI_TIMEOUT_CLK_SHIFT 0
+#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
+#define SDHCI_CLOCK_BASE_MASK 0x00003F00
+#define SDHCI_CLOCK_BASE_SHIFT 8
+#define SDHCI_MAX_BLOCK_MASK 0x00030000
+#define SDHCI_MAX_BLOCK_SHIFT 16
+#define SDHCI_CAN_DO_ADMA2 0x00080000
+#define SDHCI_CAN_DO_ADMA1 0x00100000
+#define SDHCI_CAN_DO_HISPD 0x00200000
+#define SDHCI_CAN_DO_DMA 0x00400000
+#define SDHCI_CAN_VDD_330 0x01000000
+#define SDHCI_CAN_VDD_300 0x02000000
+#define SDHCI_CAN_VDD_180 0x04000000
+#define SDHCI_CAN_64BIT 0x10000000
+
+/* 44-47 reserved for more caps */
+#define SDHCI_WML 0x44
+#define SDHCI_WML_4_WORDS 0x00040004
+#define SDHCI_WML_16_WORDS 0x00100010
+#define SDHCI_WML_64_WORDS 0x00400040
+#define SDHCI_WML_128_WORDS 0x00800080
+
+#define SDHCI_MAX_CURRENT 0x48
+
+/* 4C-4F reserved for more max current */
+
+#define SDHCI_SET_ACMD12_ERROR 0x50
+#define SDHCI_SET_INT_ERROR 0x52
+
+#define SDHCI_ADMA_ERROR 0x54
+
+/* 55-57 reserved */
+
+#define SDHCI_ADMA_ADDRESS 0x58
+
+/* 60-FB reserved */
+
+/* ADMA Addr Descriptor Attribute Filed */
+enum {
+ FSL_ADMA_DES_ATTR_VALID = 0x01,
+ FSL_ADMA_DES_ATTR_END = 0x02,
+ FSL_ADMA_DES_ATTR_INT = 0x04,
+ FSL_ADMA_DES_ATTR_SET = 0x10,
+ FSL_ADMA_DES_ATTR_TRAN = 0x20,
+ FSL_ADMA_DES_ATTR_LINK = 0x30,
+};
+
+#define SDHCI_HOST_VERSION 0xFC
+#define SDHCI_VENDOR_VER_MASK 0xFF00
+#define SDHCI_VENDOR_VER_SHIFT 8
+#define SDHCI_SPEC_VER_MASK 0x00FF
+#define SDHCI_SPEC_VER_SHIFT 0
+#define SDHCI_SPEC_100 0
+#define SDHCI_SPEC_200 1
+#define ESDHC_VENDOR_V22 0x12
+
+struct sdhci_chip;
+
+struct sdhci_host {
+ struct sdhci_chip *chip;
+ struct mmc_host *mmc; /* MMC structure */
+
+#ifdef CONFIG_LEDS_CLASS
+ struct led_classdev led; /* LED control */
+#endif
+
+ spinlock_t lock; /* Mutex */
+
+ int init_flag; /* Host has been initialized */
+ int flags; /* Host attributes */
+#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */
+#define SDHCI_REQ_USE_DMA (1<<1) /* Use DMA for this req. */
+#define SDHCI_USE_EXTERNAL_DMA (1<<2) /* Use the External DMA */
+#define SDHCI_CD_PRESENT (1<<8) /* CD present */
+#define SDHCI_WP_ENABLED (1<<9) /* Write protect */
+
+ unsigned int max_clk; /* Max possible freq (MHz) */
+ unsigned int min_clk; /* Min possible freq (MHz) */
+ unsigned int timeout_clk; /* Timeout freq (KHz) */
+
+ unsigned int clock; /* Current clock (MHz) */
+ unsigned short power; /* Current voltage */
+ struct regulator *regulator_mmc; /*! Regulator */
+
+ struct mmc_request *mrq; /* Current request */
+ struct mmc_command *cmd; /* Current command */
+ struct mmc_data *data; /* Current data request */
+ unsigned int data_early:1; /* Data finished before cmd */
+
+ unsigned int id; /* Id for SD/MMC block */
+ int mode; /* SD/MMC mode */
+ int dma; /* DMA channel number. */
+ unsigned int dma_size; /* Number of Bytes in DMA */
+ unsigned int dma_len; /* Length of the s-g list */
+ unsigned int dma_dir; /* DMA transfer direction */
+
+ struct scatterlist *cur_sg; /* We're working on this */
+ int num_sg; /* Entries left */
+ int offset; /* Offset into current sg */
+ int remain; /* Bytes left in current */
+
+ struct resource *res; /* IO map memory */
+ int irq; /* Device IRQ */
+ int detect_irq; /* Card Detect IRQ number. */
+ int sdio_enable; /* sdio interrupt enable number. */
+ struct clk *clk; /* Clock id */
+ int bar; /* PCI BAR index */
+ unsigned long addr; /* Bus address */
+ void __iomem *ioaddr; /* Mapped address */
+
+ struct tasklet_struct card_tasklet; /* Tasklet structures */
+ struct tasklet_struct finish_tasklet;
+ struct work_struct cd_wq; /* card detection work queue */
+ /* Platform specific data */
+ struct mxc_mmc_platform_data *plat_data;
+
+ struct timer_list timer; /* Timer for timeouts */
+ struct timer_list cd_timer; /* Timer for cd */
+};
+
+struct sdhci_chip {
+ struct platform_device *pdev;
+
+ unsigned long quirks;
+
+ int num_slots; /* Slots on controller */
+ struct sdhci_host *hosts[0]; /* Pointers to hosts */
+};
diff --git a/drivers/mmc/host/mxc_mmc.c b/drivers/mmc/host/mxc_mmc.c
new file mode 100644
index 000000000000..064b3bffb85f
--- /dev/null
+++ b/drivers/mmc/host/mxc_mmc.c
@@ -0,0 +1,1530 @@
+/*
+ * linux/drivers/mmc/host/mxc_mmc.c - Freescale MXC/i.MX MMC driver
+ *
+ * based on imxmmc.c
+ * Copyright (C) 2004 Sascha Hauer, Pengutronix <sascha@saschahauer.de>
+ *
+ * derived from pxamci.c by Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_mmc.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC SDHC modules.
+ *
+ * This driver code is based on imxmmc.c, by Sascha Hauer,
+ * Pengutronix <sascha@saschahauer.de>. This driver supports both Secure Digital
+ * Host Controller modules (SDHC1 and SDHC2) of MXC. SDHC is also referred as
+ * MMC/SD controller. This code is not tested for SD cards.
+ *
+ * @ingroup MMC_SD
+ */
+
+/*
+ * Include Files
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sd.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/sizes.h>
+#include <asm/mach-types.h>
+#include <asm/mach/irq.h>
+#include <mach/mmc.h>
+
+#include "mxc_mmc.h"
+
+#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
+
+/*
+ * This define is used to test the driver without using DMA
+ */
+#define MXC_MMC_DMA_ENABLE
+
+/*!
+ * Maxumum length of s/g list, only length of 1 is currently supported
+ */
+#define NR_SG 1
+
+#ifdef CONFIG_MMC_DEBUG
+static void dump_cmd(struct mmc_command *cmd)
+{
+ printk(KERN_INFO "%s: CMD: opcode: %d ", DRIVER_NAME, cmd->opcode);
+ printk(KERN_INFO "arg: 0x%08x ", cmd->arg);
+ printk(KERN_INFO "flags: 0x%08x\n", cmd->flags);
+}
+
+static void dump_status(const char *func, int sts)
+{
+ unsigned int bitset;
+ printk(KERN_INFO "%s:status: ", func);
+ while (sts) {
+ /* Find the next bit set */
+ bitset = sts & ~(sts - 1);
+ switch (bitset) {
+ case STATUS_CARD_INSERTION:
+ printk(KERN_INFO "CARD_INSERTION|");
+ break;
+ case STATUS_CARD_REMOVAL:
+ printk(KERN_INFO "CARD_REMOVAL |");
+ break;
+ case STATUS_YBUF_EMPTY:
+ printk(KERN_INFO "YBUF_EMPTY |");
+ break;
+ case STATUS_XBUF_EMPTY:
+ printk(KERN_INFO "XBUF_EMPTY |");
+ break;
+ case STATUS_YBUF_FULL:
+ printk(KERN_INFO "YBUF_FULL |");
+ break;
+ case STATUS_XBUF_FULL:
+ printk(KERN_INFO "XBUF_FULL |");
+ break;
+ case STATUS_BUF_UND_RUN:
+ printk(KERN_INFO "BUF_UND_RUN |");
+ break;
+ case STATUS_BUF_OVFL:
+ printk(KERN_INFO "BUF_OVFL |");
+ break;
+ case STATUS_READ_OP_DONE:
+ printk(KERN_INFO "READ_OP_DONE |");
+ break;
+ case STATUS_WR_CRC_ERROR_CODE_MASK:
+ printk(KERN_INFO "WR_CRC_ERROR_CODE |");
+ break;
+ case STATUS_READ_CRC_ERR:
+ printk(KERN_INFO "READ_CRC_ERR |");
+ break;
+ case STATUS_WRITE_CRC_ERR:
+ printk(KERN_INFO "WRITE_CRC_ERR |");
+ break;
+ case STATUS_SDIO_INT_ACTIVE:
+ printk(KERN_INFO "SDIO_INT_ACTIVE |");
+ break;
+ case STATUS_END_CMD_RESP:
+ printk(KERN_INFO "END_CMD_RESP |");
+ break;
+ case STATUS_WRITE_OP_DONE:
+ printk(KERN_INFO "WRITE_OP_DONE |");
+ break;
+ case STATUS_CARD_BUS_CLK_RUN:
+ printk(KERN_INFO "CARD_BUS_CLK_RUN |");
+ break;
+ case STATUS_BUF_READ_RDY:
+ printk(KERN_INFO "BUF_READ_RDY |");
+ break;
+ case STATUS_BUF_WRITE_RDY:
+ printk(KERN_INFO "BUF_WRITE_RDY |");
+ break;
+ case STATUS_RESP_CRC_ERR:
+ printk(KERN_INFO "RESP_CRC_ERR |");
+ break;
+ case STATUS_TIME_OUT_RESP:
+ printk(KERN_INFO "TIME_OUT_RESP |");
+ break;
+ case STATUS_TIME_OUT_READ:
+ printk(KERN_INFO "TIME_OUT_READ |");
+ break;
+ default:
+ printk(KERN_INFO "Invalid Status Register value0x%x\n",
+ bitset);
+ break;
+ }
+ sts &= ~bitset;
+ }
+ printk(KERN_INFO "\n");
+}
+#endif
+
+/*!
+ * This structure is a way for the low level driver to define their own
+ * \b mmc_host structure. This structure includes the core \b mmc_host
+ * structure that is provided by Linux MMC/SD Bus protocol driver as an
+ * element and has other elements that are specifically required by this
+ * low-level driver.
+ */
+struct mxcmci_host {
+ /*!
+ * The mmc structure holds all the information about the device
+ * structure, current SDHC io bus settings, the current OCR setting,
+ * devices attached to this host, and so on.
+ */
+ struct mmc_host *mmc;
+
+ /*!
+ * This variable is used for locking the host data structure from
+ * multiple access.
+ */
+ spinlock_t lock;
+
+ /*!
+ * Resource structure, which will maintain base addresses and IRQs.
+ */
+ struct resource *res;
+
+ /*!
+ * Base address of SDHC, used in readl and writel.
+ */
+ void *base;
+
+ /*!
+ * SDHC IRQ number.
+ */
+ int irq;
+
+ /*!
+ * Card Detect IRQ number.
+ */
+ int detect_irq;
+
+ /*!
+ * Clock id to hold ipg_perclk.
+ */
+ struct clk *clk;
+ /*!
+ * MMC mode.
+ */
+ int mode;
+
+ /*!
+ * DMA channel number.
+ */
+ int dma;
+
+ /*!
+ * Pointer to hold MMC/SD request.
+ */
+ struct mmc_request *req;
+
+ /*!
+ * Pointer to hold MMC/SD command.
+ */
+ struct mmc_command *cmd;
+
+ /*!
+ * Pointer to hold MMC/SD data.
+ */
+ struct mmc_data *data;
+
+ /*!
+ * Holds the number of bytes to transfer using DMA.
+ */
+ unsigned int dma_size;
+
+ /*!
+ * Value to store in Command and Data Control Register
+ * - currently unused
+ */
+ unsigned int cmdat;
+
+ /*!
+ * Regulator
+ */
+ struct regulator *regulator_mmc;
+
+ /*!
+ * Current vdd settting
+ */
+ int current_vdd;
+
+ /*!
+ * Power mode - currently unused
+ */
+ unsigned int power_mode;
+
+ /*!
+ * DMA address for scatter-gather transfers
+ */
+ dma_addr_t sg_dma;
+
+ /*!
+ * Length of the scatter-gather list
+ */
+ unsigned int dma_len;
+
+ /*!
+ * Holds the direction of data transfer.
+ */
+ unsigned int dma_dir;
+
+ /*!
+ * Id for MMC block.
+ */
+ unsigned int id;
+
+ /*!
+ * Note whether this driver has been suspended.
+ */
+ unsigned int mxc_mmc_suspend_flag;
+
+ /*!
+ * sdio_irq enable/disable ref count
+ */
+ int sdio_irq_cnt;
+
+ /*!
+ * Platform specific data
+ */
+ struct mxc_mmc_platform_data *plat_data;
+};
+
+extern void gpio_sdhc_active(int module);
+extern void gpio_sdhc_inactive(int module);
+
+#ifdef MXC_MMC_DMA_ENABLE
+static void mxcmci_dma_irq(void *devid, int error, unsigned int cnt);
+#endif
+static int mxcmci_data_done(struct mxcmci_host *host, unsigned int stat);
+
+/* Wait count to start the clock */
+#define CMD_WAIT_CNT 100
+
+#define MAX_HOST 10
+static struct mmc_host *hosts[MAX_HOST];
+
+void mxc_mmc_force_detect(int id)
+{
+ if (id < MAX_HOST)
+ mmc_detect_change(hosts[id], msecs_to_jiffies(100));
+}
+
+EXPORT_SYMBOL(mxc_mmc_force_detect);
+
+/*!
+ This function sets the SDHC register to stop the clock and waits for the
+ * clock stop indication.
+ */
+static void mxcmci_stop_clock(struct mxcmci_host *host, bool wait)
+{
+ int wait_cnt = 0;
+ while (1) {
+ __raw_writel(STR_STP_CLK_STOP_CLK,
+ host->base + MMC_STR_STP_CLK);
+
+ if (!wait)
+ break;
+
+ wait_cnt = CMD_WAIT_CNT;
+ while (wait_cnt--) {
+ if (!(__raw_readl(host->base + MMC_STATUS) &
+ STATUS_CARD_BUS_CLK_RUN))
+ break;
+ }
+
+ if (!(__raw_readl(host->base + MMC_STATUS) &
+ STATUS_CARD_BUS_CLK_RUN))
+ break;
+ }
+}
+
+/*!
+ * This function sets the SDHC register to start the clock and waits for the
+ * clock start indication. When the clock starts SDHC module starts processing
+ * the command in CMD Register with arguments in ARG Register.
+ *
+ * @param host Pointer to MMC/SD host structure
+ * @param wait Boolean value to indicate whether to wait for the clock to start or come out instantly
+ */
+static void mxcmci_start_clock(struct mxcmci_host *host, bool wait)
+{
+ int wait_cnt;
+
+#ifdef CONFIG_MMC_DEBUG
+ dump_status(__FUNCTION__, __raw_readl(host->base + MMC_STATUS));
+#endif
+
+ while (1) {
+ __raw_writel(STR_STP_CLK_START_CLK,
+ host->base + MMC_STR_STP_CLK);
+ if (!wait)
+ break;
+
+ wait_cnt = CMD_WAIT_CNT;
+ while (wait_cnt--) {
+ if (__raw_readl(host->base + MMC_STATUS) &
+ STATUS_CARD_BUS_CLK_RUN) {
+ break;
+ }
+ }
+
+ if (__raw_readl(host->base + MMC_STATUS) &
+ STATUS_CARD_BUS_CLK_RUN) {
+ break;
+ }
+ }
+#ifdef CONFIG_MMC_DEBUG
+ dump_status(__FUNCTION__, __raw_readl(host->base + MMC_STATUS));
+#endif
+ pr_debug("%s:CLK_RATE: 0x%08x\n", DRIVER_NAME,
+ __raw_readl(host->base + MMC_CLK_RATE));
+}
+
+/*!
+ * This function resets the SDHC host.
+ *
+ * @param host Pointer to MMC/SD host structure
+ */
+static void mxcmci_softreset(struct mxcmci_host *host)
+{
+ /* reset sequence */
+ __raw_writel(0x8, host->base + MMC_STR_STP_CLK);
+ __raw_writel(0x9, host->base + MMC_STR_STP_CLK);
+ __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
+ __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
+ __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
+ __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
+ __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
+ __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
+ __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
+ __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
+ __raw_writel(0x3f, host->base + MMC_CLK_RATE);
+
+ __raw_writel(0xff, host->base + MMC_RES_TO);
+ __raw_writel(512, host->base + MMC_BLK_LEN);
+ __raw_writel(1, host->base + MMC_NOB);
+}
+
+/*!
+ * This function is called to setup SDHC register for data transfer.
+ * The function allocates DMA buffers, configures the DMA channel.
+ * Start the DMA channel to transfer data. When DMA is not enabled this
+ * function set ups only Number of Block and Block Length registers.
+ *
+ * @param host Pointer to MMC/SD host structure
+ * @param data Pointer to MMC/SD data structure
+ */
+static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
+{
+ unsigned int nob = data->blocks;
+
+ if (data->flags & MMC_DATA_STREAM) {
+ nob = 0xffff;
+ }
+
+ host->data = data;
+
+ __raw_writel(nob, host->base + MMC_NOB);
+ __raw_writel(data->blksz, host->base + MMC_BLK_LEN);
+
+ host->dma_size = data->blocks * data->blksz;
+ pr_debug("%s:Request bytes to transfer:%d\n", DRIVER_NAME,
+ host->dma_size);
+
+#ifdef MXC_MMC_DMA_ENABLE
+ if (host->dma_size <= (16 << host->mmc->ios.bus_width)) {
+ return;
+ }
+
+ if (data->blksz & 0x3) {
+ printk(KERN_ERR
+ "mxc_mci: block size not multiple of 4 bytes\n");
+ }
+
+ if (data->flags & MMC_DATA_READ) {
+ host->dma_dir = DMA_FROM_DEVICE;
+ } else {
+ host->dma_dir = DMA_TO_DEVICE;
+ }
+ host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ host->dma_dir);
+
+ if (data->flags & MMC_DATA_READ) {
+ mxc_dma_sg_config(host->dma, data->sg, data->sg_len,
+ host->dma_size, MXC_DMA_MODE_READ);
+ } else {
+ mxc_dma_sg_config(host->dma, data->sg, data->sg_len,
+ host->dma_size, MXC_DMA_MODE_WRITE);
+ }
+#endif
+}
+
+/*!
+ * This function is called by \b mxcmci_request() function to setup the SDHC
+ * register to issue command. This function disables the card insertion and
+ * removal detection interrupt.
+ *
+ * @param host Pointer to MMC/SD host structure
+ * @param cmd Pointer to MMC/SD command structure
+ * @param cmdat Value to store in Command and Data Control Register
+ */
+static void mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
+ unsigned int cmdat)
+{
+ WARN_ON(host->cmd != NULL);
+ host->cmd = cmd;
+
+ switch (RSP_TYPE(mmc_resp_type(cmd))) {
+ case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6 */
+ cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1;
+ break;
+ case RSP_TYPE(MMC_RSP_R3):
+ cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3;
+ break;
+ case RSP_TYPE(MMC_RSP_R2):
+ cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R2;
+ break;
+ default:
+ /* No Response required */
+ break;
+ }
+
+ if (cmd->opcode == MMC_GO_IDLE_STATE) {
+ cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */
+ }
+
+ if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
+ cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
+ }
+
+ __raw_writel(cmd->opcode, host->base + MMC_CMD);
+ __raw_writel(cmd->arg, host->base + MMC_ARG);
+
+ __raw_writel(cmdat, host->base + MMC_CMD_DAT_CONT);
+
+ if (!(__raw_readl(host->base + MMC_STATUS) & STATUS_CARD_BUS_CLK_RUN))
+ mxcmci_start_clock(host, true);
+}
+
+/*!
+ * This function is called to complete the command request.
+ * This function enables insertion or removal interrupt.
+ *
+ * @param host Pointer to MMC/SD host structure
+ * @param req Pointer to MMC/SD command request structure
+ */
+static void mxcmci_finish_request(struct mxcmci_host *host,
+ struct mmc_request *req)
+{
+
+ host->req = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+
+ mmc_request_done(host->mmc, req);
+}
+
+/*!
+ * This function is called when the requested command is completed.
+ * This function reads the response from the card and data if the command is for
+ * data transfer. This function checks for CRC error in response FIFO or
+ * data FIFO.
+ *
+ * @param host Pointer to MMC/SD host structure
+ * @param stat Content of SDHC Status Register
+ *
+ * @return This function returns 0 if there is no pending command, otherwise 1
+ * always.
+ */
+static int mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat)
+{
+ struct mmc_command *cmd = host->cmd;
+ struct mmc_data *data = host->data;
+ int i;
+ u32 a, b, c;
+ u32 temp_data;
+ unsigned int status;
+ unsigned long *buf;
+ u8 *buf8;
+ int no_of_bytes;
+ int no_of_words;
+
+ if (!cmd) {
+ /* There is no command for completion */
+ return 0;
+ }
+
+ /* As this function finishes the command, initialize cmd to NULL */
+ host->cmd = NULL;
+
+ /* check for Time out errors */
+ if (stat & STATUS_TIME_OUT_RESP) {
+ __raw_writel(STATUS_TIME_OUT_RESP, host->base + MMC_STATUS);
+ pr_debug("%s: CMD %d TIMEOUT\n", DRIVER_NAME, cmd->opcode);
+ cmd->error = -ETIMEDOUT;
+ /*
+ * Reinitialized the controller to clear the unknown
+ * error state.
+ */
+ mxcmci_softreset(host);
+ __raw_writel(READ_TO_VALUE, host->base + MMC_READ_TO);
+ __raw_writel(INT_CNTR_END_CMD_RES, host->base + MMC_INT_CNTR);
+ } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+ __raw_writel(STATUS_RESP_CRC_ERR, host->base + MMC_STATUS);
+ printk(KERN_ERR "%s: cmd %d CRC error\n", DRIVER_NAME,
+ cmd->opcode);
+ cmd->error = -EILSEQ;
+ /*
+ * Reinitialized the controller to clear the unknown
+ * error state.
+ */
+ mxcmci_softreset(host);
+ __raw_writel(READ_TO_VALUE, host->base + MMC_READ_TO);
+ __raw_writel(INT_CNTR_END_CMD_RES, host->base + MMC_INT_CNTR);
+ }
+
+ /* Read response from the card */
+ switch (RSP_TYPE(mmc_resp_type(cmd))) {
+ case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6 */
+ a = __raw_readl(host->base + MMC_RES_FIFO) & 0xffff;
+ b = __raw_readl(host->base + MMC_RES_FIFO) & 0xffff;
+ c = __raw_readl(host->base + MMC_RES_FIFO) & 0xffff;
+ cmd->resp[0] = a << 24 | b << 8 | c >> 8;
+ break;
+ case RSP_TYPE(MMC_RSP_R3): /* r3, r4 */
+ a = __raw_readl(host->base + MMC_RES_FIFO) & 0xffff;
+ b = __raw_readl(host->base + MMC_RES_FIFO) & 0xffff;
+ c = __raw_readl(host->base + MMC_RES_FIFO) & 0xffff;
+ cmd->resp[0] = a << 24 | b << 8 | c >> 8;
+ break;
+ case RSP_TYPE(MMC_RSP_R2):
+ for (i = 0; i < 4; i++) {
+ a = __raw_readl(host->base + MMC_RES_FIFO) & 0xffff;
+ b = __raw_readl(host->base + MMC_RES_FIFO) & 0xffff;
+ cmd->resp[i] = a << 16 | b;
+ }
+ break;
+ default:
+ break;
+ }
+
+ pr_debug("%s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", DRIVER_NAME,
+ cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
+
+ if (!host->data || cmd->error) {
+ /* complete the command */
+ mxcmci_finish_request(host, host->req);
+ return 1;
+ }
+
+ /* The command has a data transfer */
+#ifdef MXC_MMC_DMA_ENABLE
+ /* Use DMA if transfer size is greater than fifo size */
+ if (host->dma_size > (16 << host->mmc->ios.bus_width)) {
+ mxc_dma_enable(host->dma);
+ return 1;
+ }
+#endif
+ /* Use PIO tranfer of data */
+ buf = (unsigned long *)sg_virt(data->sg);
+ buf8 = (u8 *) buf;
+
+ /* calculate the number of bytes requested for transfer */
+ no_of_bytes = data->blocks * data->blksz;
+ no_of_words = (no_of_bytes + 3) / 4;
+ pr_debug("no_of_words=%d\n", no_of_words);
+
+ if (data->flags & MMC_DATA_READ) {
+ for (i = 0; i < no_of_words; i++) {
+ /* wait for buffers to be ready for read */
+ while (!(__raw_readl(host->base + MMC_STATUS) &
+ (STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE))) ;
+
+ pr_debug("status is 0x%x\n",
+ __raw_readl(host->base + MMC_STATUS));
+ /* read 32 bit data */
+ temp_data = __raw_readl(host->base + MMC_BUFFER_ACCESS);
+ if (SD_APP_SEND_SCR == cmd->opcode) {
+ pr_debug("CMD51 read out 0x%x\n", temp_data);
+ if (temp_data == 0xFFFFFFFF)
+ temp_data = 0;
+ }
+ if (no_of_bytes >= 4) {
+ *buf++ = temp_data;
+ no_of_bytes -= 4;
+ } else {
+ do {
+ *buf8++ = temp_data;
+ temp_data = temp_data >> 8;
+ } while (--no_of_bytes);
+ }
+ }
+
+ /* wait for read operation completion bit */
+ while (!(__raw_readl(host->base + MMC_STATUS) &
+ STATUS_READ_OP_DONE)) ;
+
+ /* check for time out and CRC errors */
+ status = __raw_readl(host->base + MMC_STATUS);
+ if (status & STATUS_TIME_OUT_READ) {
+ printk(KERN_ERR "%s: Read time out occurred\n",
+ DRIVER_NAME);
+ data->error = -ETIMEDOUT;
+ __raw_writel(STATUS_TIME_OUT_READ,
+ host->base + MMC_STATUS);
+ /*
+ * Reinitialized the controller to clear the unknown
+ * error state.
+ */
+ mxcmci_softreset(host);
+ __raw_writel(READ_TO_VALUE, host->base + MMC_READ_TO);
+ __raw_writel(INT_CNTR_END_CMD_RES,
+ host->base + MMC_INT_CNTR);
+ } else if (status & STATUS_READ_CRC_ERR) {
+ printk(KERN_ERR "%s: Read CRC error occurred\n",
+ DRIVER_NAME);
+ if (SD_APP_SEND_SCR != cmd->opcode)
+ data->error = -EILSEQ;
+ __raw_writel(STATUS_READ_CRC_ERR,
+ host->base + MMC_STATUS);
+ /*
+ * Reinitialized the controller to clear the unknown
+ * error state.
+ */
+ mxcmci_softreset(host);
+ __raw_writel(READ_TO_VALUE, host->base + MMC_READ_TO);
+ __raw_writel(INT_CNTR_END_CMD_RES,
+ host->base + MMC_INT_CNTR);
+ }
+ __raw_writel(STATUS_READ_OP_DONE, host->base + MMC_STATUS);
+
+ pr_debug("%s: Read %u words\n", DRIVER_NAME, i);
+ } else {
+ for (i = 0; i < no_of_words; i++) {
+
+ /* wait for buffers to be ready for write */
+ while (!(__raw_readl(host->base + MMC_STATUS) &
+ STATUS_BUF_WRITE_RDY)) ;
+
+ /* write 32 bit data */
+ __raw_writel(*buf++, host->base + MMC_BUFFER_ACCESS);
+ if (__raw_readl(host->base + MMC_STATUS) &
+ STATUS_WRITE_OP_DONE) {
+ break;
+ }
+ }
+
+ /* wait for write operation completion bit */
+ while (!(__raw_readl(host->base + MMC_STATUS) &
+ STATUS_WRITE_OP_DONE)) ;
+
+ /* check for CRC errors */
+ status = __raw_readl(host->base + MMC_STATUS);
+ if (status & STATUS_WRITE_CRC_ERR) {
+ printk(KERN_ERR "%s: Write CRC error occurred\n",
+ DRIVER_NAME);
+ data->error = -EILSEQ;
+ __raw_writel(STATUS_WRITE_CRC_ERR,
+ host->base + MMC_STATUS);
+ }
+ __raw_writel(STATUS_WRITE_OP_DONE, host->base + MMC_STATUS);
+ pr_debug("%s: Written %u words\n", DRIVER_NAME, i);
+ }
+
+ /* complete the data transfer request */
+ mxcmci_data_done(host, status);
+
+ return 1;
+}
+
+/*!
+ * This function is called when the data transfer is completed either by DMA
+ * or by core. This function is called to clean up the DMA buffer and to send
+ * STOP transmission command for commands to transfer data. This function
+ * completes request issued by the MMC/SD core driver.
+ *
+ * @param host pointer to MMC/SD host structure.
+ * @param stat content of SDHC Status Register
+ *
+ * @return This function returns 0 if no data transfer otherwise return 1
+ * always.
+ */
+static int mxcmci_data_done(struct mxcmci_host *host, unsigned int stat)
+{
+ struct mmc_data *data = host->data;
+
+ if (!data) {
+ return 0;
+ }
+#ifdef MXC_MMC_DMA_ENABLE
+ if (host->dma_size > (16 << host->mmc->ios.bus_width)) {
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
+ host->dma_dir);
+ }
+#endif
+ if (__raw_readl(host->base + MMC_STATUS) & STATUS_ERR_MASK) {
+ printk(KERN_ERR "%s: request failed. status: 0x%08x\n",
+ DRIVER_NAME, __raw_readl(host->base + MMC_STATUS));
+ }
+
+ host->data = NULL;
+ data->bytes_xfered = host->dma_size;
+
+ if (host->req->stop && !(data->error)) {
+ mxcmci_stop_clock(host, true);
+ mxcmci_start_cmd(host, host->req->stop, 0);
+ } else {
+ mxcmci_finish_request(host, host->req);
+ }
+
+ return 1;
+}
+
+/*!
+ * GPIO interrupt service routine registered to handle the SDHC interrupts.
+ * This interrupt routine handles card insertion and card removal interrupts.
+ *
+ * @param irq the interrupt number
+ * @param devid driver private data
+ * @param regs holds a snapshot of the processor's context before the
+ * processor entered the interrupt code
+ *
+ * @return The function returns \b IRQ_RETVAL(1)
+ */
+static irqreturn_t mxcmci_gpio_irq(int irq, void *devid)
+{
+ struct mxcmci_host *host = devid;
+ int card_gpio_status = host->plat_data->status(host->mmc->parent);
+
+ pr_debug("%s: MMC%d status=%d %s\n", DRIVER_NAME, host->id,
+ card_gpio_status, card_gpio_status ? "removed" : "inserted");
+
+ if (card_gpio_status == host->plat_data->card_inserted_state) {
+ /*
+ * Reinitialized the controller to clear the unknown
+ * error state when a card is inserted.
+ */
+ mxcmci_softreset(host);
+ __raw_writel(READ_TO_VALUE, host->base + MMC_READ_TO);
+ __raw_writel(INT_CNTR_END_CMD_RES, host->base + MMC_INT_CNTR);
+
+ mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+ } else {
+ mxcmci_cmd_done(host, STATUS_TIME_OUT_RESP);
+ mmc_detect_change(host->mmc, msecs_to_jiffies(50));
+ }
+
+ do {
+ card_gpio_status = host->plat_data->status(host->mmc->parent);
+ if (card_gpio_status) {
+ set_irq_type(host->detect_irq, IRQF_TRIGGER_FALLING);
+ } else {
+ set_irq_type(host->detect_irq, IRQF_TRIGGER_RISING);
+ }
+ } while (card_gpio_status !=
+ host->plat_data->status(host->mmc->parent));
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * Interrupt service routine registered to handle the SDHC interrupts.
+ * This interrupt routine handles end of command, card insertion and
+ * card removal interrupts. If the interrupt is card insertion or removal then
+ * inform the MMC/SD core driver to detect the change in physical connections.
+ * If the command is END_CMD_RESP read the Response FIFO. If DMA is not enabled
+ * and data transfer is associated with the command then read or write the data
+ * from or to the BUFFER_ACCESS FIFO.
+ *
+ * @param irq the interrupt number
+ * @param devid driver private data
+ * @param regs holds a snapshot of the processor's context before the
+ * processor entered the interrupt code
+ *
+ * @return The function returns \b IRQ_RETVAL(1) if interrupt was handled,
+ * returns \b IRQ_RETVAL(0) if the interrupt was not handled.
+ */
+static irqreturn_t mxcmci_irq(int irq, void *devid)
+{
+ struct mxcmci_host *host = devid;
+ struct mmc_data *data = host->data;
+ unsigned int status = 0;
+ u32 intctrl;
+
+ if (host->mxc_mmc_suspend_flag == 1) {
+ clk_enable(host->clk);
+ }
+
+ status = __raw_readl(host->base + MMC_STATUS);
+ pr_debug("MXC MMC IRQ status is 0x%x.\n", status);
+#ifdef CONFIG_MMC_DEBUG
+ dump_status(__FUNCTION__, status);
+#endif
+ if (status & STATUS_END_CMD_RESP) {
+ __raw_writel(STATUS_END_CMD_RESP, host->base + MMC_STATUS);
+ mxcmci_cmd_done(host, status);
+ }
+#ifdef MXC_MMC_DMA_ENABLE
+ /*
+ * If read length < fifo length, STATUS_END_CMD_RESP and
+ * STATUS_READ_OP_DONE may come together. In this case, it's using PIO
+ * mode, we ignore STATUS_READ_OP_DONE.
+ */
+ if ((status & (STATUS_WRITE_OP_DONE | STATUS_READ_OP_DONE)) &&
+ !(status & STATUS_END_CMD_RESP)) {
+ pr_debug(KERN_INFO "MXC MMC IO OP DONE INT.\n");
+ intctrl = __raw_readl(host->base + MMC_INT_CNTR);
+ __raw_writel((~(INT_CNTR_WRITE_OP_DONE | INT_CNTR_READ_OP_DONE)
+ & intctrl), host->base + MMC_INT_CNTR);
+
+ pr_debug("%s:READ/WRITE OPERATION DONE\n", DRIVER_NAME);
+ /* check for time out and CRC errors */
+ status = __raw_readl(host->base + MMC_STATUS);
+ if (status & STATUS_READ_OP_DONE) {
+ if (status & STATUS_TIME_OUT_READ) {
+ pr_debug("%s: Read time out occurred\n",
+ DRIVER_NAME);
+ data->error = -ETIMEDOUT;
+ __raw_writel(STATUS_TIME_OUT_READ,
+ host->base + MMC_STATUS);
+ } else if (status & STATUS_READ_CRC_ERR) {
+ pr_debug("%s: Read CRC error occurred\n",
+ DRIVER_NAME);
+ data->error = -EILSEQ;
+ __raw_writel(STATUS_READ_CRC_ERR,
+ host->base + MMC_STATUS);
+ }
+ __raw_writel(STATUS_READ_OP_DONE,
+ host->base + MMC_STATUS);
+ }
+
+ /* check for CRC errors */
+ if (status & STATUS_WRITE_OP_DONE) {
+ if (status & STATUS_WRITE_CRC_ERR) {
+ printk(KERN_ERR
+ "%s: Write CRC error occurred\n",
+ DRIVER_NAME);
+ data->error = -EILSEQ;
+ __raw_writel(STATUS_WRITE_CRC_ERR,
+ host->base + MMC_STATUS);
+ }
+ __raw_writel(STATUS_WRITE_OP_DONE,
+ host->base + MMC_STATUS);
+ }
+
+ mxcmci_data_done(host, status);
+ }
+#endif
+ status = __raw_readl(host->base + MMC_STATUS);
+ intctrl = __raw_readl(host->base + MMC_INT_CNTR);
+ if ((status & STATUS_SDIO_INT_ACTIVE)
+ && (intctrl & INT_CNTR_SDIO_IRQ_EN)) {
+ __raw_writel(STATUS_SDIO_INT_ACTIVE, host->base + MMC_STATUS);
+
+ /*Here we do not handle the sdio interrupt to client driver
+ if the host is in suspend state */
+ if (host->mxc_mmc_suspend_flag == 0) {
+ mmc_signal_sdio_irq(host->mmc);
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function is called by MMC/SD Bus Protocol driver to issue a MMC
+ * and SD commands to the SDHC.
+ *
+ * @param mmc Pointer to MMC/SD host structure
+ * @param req Pointer to MMC/SD command request structure
+ */
+static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct mxcmci_host *host = mmc_priv(mmc);
+ /* Holds the value of Command and Data Control Register */
+ unsigned long cmdat;
+
+ WARN_ON(host->req != NULL);
+
+ host->req = req;
+#ifdef CONFIG_MMC_DEBUG
+ dump_cmd(req->cmd);
+ dump_status(__FUNCTION__, __raw_readl(host->base + MMC_STATUS));
+#endif
+
+ cmdat = 0;
+ if (req->data) {
+ mxcmci_setup_data(host, req->data);
+
+ cmdat |= CMD_DAT_CONT_DATA_ENABLE;
+
+ if (req->data->flags & MMC_DATA_WRITE) {
+ cmdat |= CMD_DAT_CONT_WRITE;
+ }
+ if (req->data->flags & MMC_DATA_STREAM) {
+ printk(KERN_ERR
+ "MXC MMC does not support stream mode\n");
+ }
+ }
+ mxcmci_start_cmd(host, req->cmd, cmdat);
+}
+
+/*!
+ * This function is called by MMC/SD Bus Protocol driver to change the clock
+ * speed of MMC or SD card
+ *
+ * @param mmc Pointer to MMC/SD host structure
+ * @param ios Pointer to MMC/SD I/O type structure
+ */
+static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct mxcmci_host *host = mmc_priv(mmc);
+ /*This variable holds the value of clock prescaler */
+ int prescaler;
+ int clk_rate = clk_get_rate(host->clk);
+ int voltage = 0;
+#ifdef MXC_MMC_DMA_ENABLE
+ mxc_dma_device_t dev_id = 0;
+#endif
+
+ pr_debug("%s: clock %u, bus %lu, power %u, vdd %u\n", DRIVER_NAME,
+ ios->clock, 1UL << ios->bus_width, ios->power_mode, ios->vdd);
+
+ host->dma_dir = DMA_NONE;
+
+#ifdef MXC_MMC_DMA_ENABLE
+ if (mmc->ios.bus_width != host->mode) {
+ mxc_dma_free(host->dma);
+ if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
+ if (host->id == 0) {
+ dev_id = MXC_DMA_MMC1_WIDTH_4;
+ } else {
+ dev_id = MXC_DMA_MMC2_WIDTH_4;
+ }
+ } else {
+ if (host->id == 0) {
+ dev_id = MXC_DMA_MMC1_WIDTH_1;
+ } else {
+ dev_id = MXC_DMA_MMC2_WIDTH_1;
+ }
+ }
+ host->dma = mxc_dma_request(dev_id, "MXC MMC");
+ if (host->dma < 0) {
+ printk(KERN_ERR "Cannot allocate MMC DMA channel\n");
+ }
+ host->mode = mmc->ios.bus_width;
+ mxc_dma_callback_set(host->dma, mxcmci_dma_irq, (void *)host);
+ }
+#endif
+
+ if ((ios->vdd != host->current_vdd) && host->regulator_mmc) {
+ if (ios->vdd == 7)
+ voltage = 1800000;
+ else if (ios->vdd >= 8)
+ voltage = 2000000 + (ios->vdd - 8) * 100000;
+ regulator_set_voltage(host->regulator_mmc, voltage, voltage);
+ }
+ host->current_vdd = ios->vdd;
+
+ if (ios->power_mode != host->power_mode && host->regulator_mmc) {
+ if (ios->power_mode == MMC_POWER_UP) {
+ if (regulator_enable(host->regulator_mmc) == 0) {
+ pr_debug("mmc power on\n");
+ msleep(1);
+ }
+ } else if (ios->power_mode == MMC_POWER_OFF) {
+ regulator_disable(host->regulator_mmc);
+ pr_debug("mmc power off\n");
+ }
+ }
+ host->power_mode = ios->power_mode;
+
+ /*
+ * Vary divider first, then prescaler.
+ **/
+ if (ios->clock) {
+ unsigned int clk_dev = 0;
+
+ /*
+ * when prescaler = 16, CLK_20M = CLK_DIV / 2
+ */
+ if (ios->clock == mmc->f_min)
+ prescaler = 16;
+ else
+ prescaler = 0;
+
+ /* clk_dev =1, CLK_DIV = ipg_perclk/2 */
+ while (prescaler <= 0x800) {
+ for (clk_dev = 1; clk_dev <= 0xF; clk_dev++) {
+ int x;
+ if (prescaler != 0) {
+ x = (clk_rate / (clk_dev + 1)) /
+ (prescaler * 2);
+ } else {
+ x = clk_rate / (clk_dev + 1);
+ }
+
+ pr_debug("x=%d, clock=%d %d\n", x, ios->clock,
+ clk_dev);
+ if (x <= ios->clock) {
+ break;
+ }
+ }
+ if (clk_dev < 0x10) {
+ break;
+ }
+ if (prescaler == 0)
+ prescaler = 1;
+ else
+ prescaler <<= 1;
+ }
+
+ pr_debug("prescaler = 0x%x, divider = 0x%x\n", prescaler,
+ clk_dev);
+ mxcmci_stop_clock(host, true);
+ __raw_writel((prescaler << 4) | clk_dev,
+ host->base + MMC_CLK_RATE);
+ mxcmci_start_clock(host, false);
+ } else {
+ mxcmci_stop_clock(host, true);
+ }
+}
+
+static void mxcmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct mxcmci_host *host = mmc_priv(mmc);
+ u32 intctrl;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (enable)
+ host->sdio_irq_cnt++;
+ else
+ host->sdio_irq_cnt--;
+
+ if (host->sdio_irq_cnt == 1 || host->sdio_irq_cnt == 0) {
+ intctrl = __raw_readl(host->base + MMC_INT_CNTR);
+ intctrl &= ~INT_CNTR_SDIO_IRQ_EN;
+ if (host->sdio_irq_cnt)
+ intctrl |= INT_CNTR_SDIO_IRQ_EN;
+ __raw_writel(intctrl, host->base + MMC_INT_CNTR);
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int mxcmci_get_ro(struct mmc_host *mmc)
+{
+ struct mxcmci_host *host = mmc_priv(mmc);
+
+ if (host->plat_data->wp_status)
+ return host->plat_data->wp_status(mmc->parent);
+ else
+ return 0;
+}
+
+/*!
+ * MMC/SD host operations structure.
+ * These functions are registered with MMC/SD Bus protocol driver.
+ */
+static struct mmc_host_ops mxcmci_ops = {
+ .request = mxcmci_request,
+ .set_ios = mxcmci_set_ios,
+ .get_ro = mxcmci_get_ro,
+ .enable_sdio_irq = mxcmci_enable_sdio_irq,
+};
+
+#ifdef MXC_MMC_DMA_ENABLE
+/*!
+ * This function is called by DMA Interrupt Service Routine to indicate
+ * requested DMA transfer is completed.
+ *
+ * @param devid pointer to device specific structure
+ * @param error any DMA error
+ * @param cnt amount of data that was transferred
+ */
+static void mxcmci_dma_irq(void *devid, int error, unsigned int cnt)
+{
+ struct mxcmci_host *host = devid;
+ u32 status;
+ ulong nob, blk_size, i, blk_len;
+
+ mxc_dma_disable(host->dma);
+
+ if (error) {
+ printk(KERN_ERR "Error in DMA transfer\n");
+ status = __raw_readl(host->base + MMC_STATUS);
+#ifdef CONFIG_MMC_DEBUG
+ dump_status(__FUNCTION__, status);
+#endif
+ mxcmci_data_done(host, status);
+ return;
+ }
+ pr_debug("%s: Transfered bytes:%d\n", DRIVER_NAME, cnt);
+ nob = __raw_readl(host->base + MMC_REM_NOB);
+ blk_size = __raw_readl(host->base + MMC_REM_BLK_SIZE);
+ blk_len = __raw_readl(host->base + MMC_BLK_LEN);
+ pr_debug("%s: REM_NOB:%lu REM_BLK_SIZE:%lu\n", DRIVER_NAME, nob,
+ blk_size);
+ i = 0;
+
+ /* Enable the WRITE OP Done INT */
+ status = __raw_readl(host->base + MMC_INT_CNTR);
+ __raw_writel((INT_CNTR_READ_OP_DONE | INT_CNTR_WRITE_OP_DONE | status),
+ host->base + MMC_INT_CNTR);
+}
+#endif
+
+/*!
+ * This function is called during the driver binding process. Based on the SDHC
+ * module that is being probed this function adds the appropriate SDHC module
+ * structure in the core driver.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions.
+ *
+ * @return The function returns 0 on successful registration and initialization
+ * of SDHC module. Otherwise returns specific error code.
+ */
+static int mxcmci_probe(struct platform_device *pdev)
+{
+ struct mxc_mmc_platform_data *mmc_plat = pdev->dev.platform_data;
+ struct mmc_host *mmc;
+ struct mxcmci_host *host = NULL;
+ int card_gpio_status;
+ int ret = -ENODEV;
+
+ if (!mmc_plat) {
+ return -EINVAL;
+ }
+
+ mmc = mmc_alloc_host(sizeof(struct mxcmci_host), &pdev->dev);
+ if (!mmc) {
+ return -ENOMEM;
+ }
+ host = mmc_priv(mmc);
+ platform_set_drvdata(pdev, mmc);
+
+ mmc->ops = &mxcmci_ops;
+ mmc->ocr_avail = mmc_plat->ocr_mask;
+
+ /* Hack to work with LP1070 */
+ if (mmc->ocr_avail && ~(MMC_VDD_31_32 - 1) == 0)
+ mmc->ocr_avail |= MMC_VDD_31_32;
+
+ mmc->max_phys_segs = NR_SG;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
+
+ mmc->f_min = mmc_plat->min_clk;
+ mmc->f_max = mmc_plat->max_clk;
+ mmc->max_req_size = 32 * 1024;
+ mmc->max_seg_size = mmc->max_req_size;
+ mmc->max_blk_count = 65536;
+
+ spin_lock_init(&host->lock);
+ host->mmc = mmc;
+ host->dma = -1;
+ host->dma_dir = DMA_NONE;
+ host->id = pdev->id;
+ host->mxc_mmc_suspend_flag = 0;
+ host->mode = -1;
+ host->plat_data = mmc_plat;
+ if (!host->plat_data) {
+ ret = -EINVAL;
+ goto out0;
+ }
+
+ /* Get pwr supply for SDHC */
+ if (NULL != mmc_plat->power_mmc) {
+ host->regulator_mmc =
+ regulator_get(&pdev->dev, mmc_plat->power_mmc);
+ if (IS_ERR(host->regulator_mmc)) {
+ ret = PTR_ERR(host->regulator_mmc);
+ goto out1;
+ }
+ if (!regulator_is_enabled(host->regulator_mmc)) {
+ if (regulator_enable(host->regulator_mmc) == 0) {
+ pr_debug("mmc power on\n");
+ msleep(1);
+ }
+ }
+ }
+
+ gpio_sdhc_active(pdev->id);
+
+ host->clk = clk_get(&pdev->dev, "sdhc_clk");
+ pr_debug("SDHC:%d clock:%lu\n", pdev->id, clk_get_rate(host->clk));
+ clk_enable(host->clk);
+
+ host->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!host->res) {
+ ret = -ENOMEM;
+ goto out2;
+ }
+
+ if (!request_mem_region(host->res->start,
+ host->res->end -
+ host->res->start + 1, pdev->name)) {
+ printk(KERN_ERR "request_mem_region failed\n");
+ ret = -ENOMEM;
+ goto out2;
+ }
+ host->base = (void *)IO_ADDRESS(host->res->start);
+ if (!host->base) {
+ ret = -ENOMEM;
+ goto out3;
+ }
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (!host->irq) {
+ ret = -ENOMEM;
+ goto out3;
+ }
+
+ if (!host->plat_data->card_fixed) {
+ host->detect_irq = platform_get_irq(pdev, 1);
+ if (!host->detect_irq)
+ goto out3;
+
+ do {
+ card_gpio_status =
+ host->plat_data->status(host->mmc->parent);
+ if (card_gpio_status)
+ set_irq_type(host->detect_irq,
+ IRQF_TRIGGER_FALLING);
+ else
+ set_irq_type(host->detect_irq,
+ IRQF_TRIGGER_RISING);
+
+ } while (card_gpio_status !=
+ host->plat_data->status(host->mmc->parent));
+
+ ret = request_irq(host->detect_irq, mxcmci_gpio_irq, 0,
+ pdev->name, host);
+ if (ret)
+ goto out3;
+ }
+
+ mxcmci_softreset(host);
+
+ if (__raw_readl(host->base + MMC_REV_NO) != SDHC_REV_NO) {
+ printk(KERN_ERR "%s: wrong rev.no. 0x%08x. aborting.\n",
+ pdev->name, MMC_REV_NO);
+ goto out3;
+ }
+ __raw_writel(READ_TO_VALUE, host->base + MMC_READ_TO);
+
+ __raw_writel(INT_CNTR_END_CMD_RES, host->base + MMC_INT_CNTR);
+
+ ret = request_irq(host->irq, mxcmci_irq, 0, pdev->name, host);
+ if (ret) {
+ goto out4;
+ }
+
+ if ((ret = mmc_add_host(mmc)) < 0) {
+ goto out5;
+ }
+
+ printk(KERN_INFO "%s-%d found\n", pdev->name, pdev->id);
+ if (host->id < MAX_HOST)
+ hosts[host->id] = host->mmc;
+
+ return 0;
+
+ out5:
+ free_irq(host->irq, host);
+ out4:
+ free_irq(host->detect_irq, host);
+ out3:
+ release_mem_region(pdev->resource[0].start,
+ pdev->resource[0].end - pdev->resource[0].start + 1);
+ out2:
+ clk_disable(host->clk);
+ regulator_disable(host->regulator_mmc);
+ regulator_put(host->regulator_mmc);
+ out1:
+ gpio_sdhc_inactive(pdev->id);
+ out0:
+ mmc_free_host(mmc);
+ platform_set_drvdata(pdev, NULL);
+ return ret;
+}
+
+/*!
+ * Dissociates the driver from the SDHC device. Removes the appropriate SDHC
+ * module structure from the core driver.
+ *
+ * @param pdev the device structure used to give information on which SDHC
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mxcmci_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+ if (mmc) {
+ struct mxcmci_host *host = mmc_priv(mmc);
+
+ hosts[host->id] = NULL;
+ mmc_remove_host(mmc);
+ free_irq(host->irq, host);
+ free_irq(host->detect_irq, host);
+#ifdef MXC_MMC_DMA_ENABLE
+ mxc_dma_free(host->dma);
+#endif
+ release_mem_region(host->res->start,
+ host->res->end - host->res->start + 1);
+ mmc_free_host(mmc);
+ if (NULL != host->regulator_mmc)
+ regulator_put(host->regulator_mmc);
+ gpio_sdhc_inactive(pdev->id);
+ }
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/*!
+ * This function is called to put the SDHC in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which SDHC
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mxcmci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct mxcmci_host *host = mmc_priv(mmc);
+ int ret = 0;
+
+ if (mmc) {
+ host->mxc_mmc_suspend_flag = 1;
+ ret = mmc_suspend_host(mmc, state);
+ }
+
+ clk_disable(host->clk);
+ /*
+ * The CD INT should be disabled in the suspend
+ * and enabled in resumed.
+ * Otherwise, the system would be halt when wake
+ * up with the situation that there is a card
+ * insertion during the system is in suspend mode.
+ */
+ disable_irq(host->detect_irq);
+
+ gpio_sdhc_inactive(pdev->id);
+
+ if (host->regulator_mmc)
+ regulator_disable(host->regulator_mmc);
+
+ return ret;
+}
+
+/*!
+ * This function is called to bring the SDHC back from a low power state. Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which SDHC
+ * to resume
+ *
+ * @return The function always returns 0.
+ */
+static int mxcmci_resume(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct mxcmci_host *host = mmc_priv(mmc);
+ int ret = 0;
+
+ /*
+ * Note that a card insertion interrupt will cause this
+ * driver to resume automatically. In that case we won't
+ * actually have to do any work here. Return success.
+ */
+ if (!host->mxc_mmc_suspend_flag) {
+ return 0;
+ }
+
+ /* enable pwr supply for SDHC */
+ if (host->regulator_mmc && !regulator_is_enabled(host->regulator_mmc)) {
+ regulator_enable(host->regulator_mmc);
+ msleep(1);
+ }
+
+ gpio_sdhc_active(pdev->id);
+
+ clk_enable(host->clk);
+
+ if (mmc) {
+ ret = mmc_resume_host(mmc);
+ host->mxc_mmc_suspend_flag = 0;
+ }
+
+ enable_irq(host->detect_irq);
+
+ return ret;
+}
+#else
+#define mxcmci_suspend NULL
+#define mxcmci_resume NULL
+#endif /* CONFIG_PM */
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcmci_driver = {
+ .driver = {
+ .name = "mxcmci",
+ },
+ .probe = mxcmci_probe,
+ .remove = mxcmci_remove,
+ .suspend = mxcmci_suspend,
+ .resume = mxcmci_resume,
+};
+
+/*!
+ * This function is used to initialize the MMC/SD driver module. The function
+ * registers the power management callback functions with the kernel and also
+ * registers the MMC/SD callback functions with the core MMC/SD driver.
+ *
+ * @return The function returns 0 on success and a non-zero value on failure.
+ */
+static int __init mxcmci_init(void)
+{
+ printk(KERN_INFO "MXC MMC/SD driver\n");
+ return platform_driver_register(&mxcmci_driver);
+}
+
+/*!
+ * This function is used to cleanup all resources before the driver exits.
+ */
+static void __exit mxcmci_exit(void)
+{
+ platform_driver_unregister(&mxcmci_driver);
+}
+
+module_init(mxcmci_init);
+module_exit(mxcmci_exit);
+
+MODULE_DESCRIPTION("MXC Multimedia Card Interface Driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/mxc_mmc.h b/drivers/mmc/host/mxc_mmc.h
new file mode 100644
index 000000000000..3ad45377dde6
--- /dev/null
+++ b/drivers/mmc/host/mxc_mmc.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MXC_MMC_REG_H__
+#define __MXC_MMC_REG_H__
+
+#include <mach/hardware.h>
+
+/*!
+ * @defgroup MMC_SD MMC/SD Driver
+ */
+
+/*!
+ * @file mxc_mmc.h
+ *
+ * @brief Driver for the Freescale Semiconductor MXC SDHC modules.
+ *
+ * This file defines offsets and bits of SDHC registers. SDHC is also referred as
+ * MMC/SD controller
+ *
+ * @ingroup MMC_SD
+ */
+
+/*!
+ * Number of SDHC modules
+ */
+
+#define SDHC_MMC_WML 16
+#define SDHC_SD_WML 64
+#define DRIVER_NAME "MXCMMC"
+#define SDHC_MEM_SIZE 16384
+#define SDHC_REV_NO 0x400
+#define READ_TO_VALUE 0x2db4
+
+/* Address offsets of the SDHC registers */
+#define MMC_STR_STP_CLK 0x00 /* Clock Control Reg */
+#define MMC_STATUS 0x04 /* Status Reg */
+#define MMC_CLK_RATE 0x08 /* Clock Rate Reg */
+#define MMC_CMD_DAT_CONT 0x0C /* Command and Data Control Reg */
+#define MMC_RES_TO 0x10 /* Response Time-out Reg */
+#define MMC_READ_TO 0x14 /* Read Time-out Reg */
+#define MMC_BLK_LEN 0x18 /* Block Length Reg */
+#define MMC_NOB 0x1C /* Number of Blocks Reg */
+#define MMC_REV_NO 0x20 /* Revision Number Reg */
+#define MMC_INT_CNTR 0x24 /* Interrupt Control Reg */
+#define MMC_CMD 0x28 /* Command Number Reg */
+#define MMC_ARG 0x2C /* Command Argument Reg */
+#define MMC_RES_FIFO 0x34 /* Command Response Reg */
+#define MMC_BUFFER_ACCESS 0x38 /* Data Buffer Access Reg */
+#define MMC_REM_NOB 0x40 /* Remaining NOB Reg */
+#define MMC_REM_BLK_SIZE 0x44 /* Remaining Block Size Reg */
+
+/* Bit definitions for STR_STP_CLK */
+#define STR_STP_CLK_RESET (1<<3)
+#define STR_STP_CLK_START_CLK (1<<1)
+#define STR_STP_CLK_STOP_CLK (1<<0)
+
+/* Bit definitions for STATUS */
+#define STATUS_CARD_INSERTION (1<<31)
+#define STATUS_CARD_REMOVAL (1<<30)
+#define STATUS_YBUF_EMPTY (1<<29)
+#define STATUS_XBUF_EMPTY (1<<28)
+#define STATUS_YBUF_FULL (1<<27)
+#define STATUS_XBUF_FULL (1<<26)
+#define STATUS_BUF_UND_RUN (1<<25)
+#define STATUS_BUF_OVFL (1<<24)
+#define STATUS_SDIO_INT_ACTIVE (1<<14)
+#define STATUS_END_CMD_RESP (1<<13)
+#define STATUS_WRITE_OP_DONE (1<<12)
+#define STATUS_READ_OP_DONE (1<<11)
+#define STATUS_WR_CRC_ERROR_CODE_MASK (3<<9)
+#define STATUS_CARD_BUS_CLK_RUN (1<<8)
+#define STATUS_BUF_READ_RDY (1<<7)
+#define STATUS_BUF_WRITE_RDY (1<<6)
+#define STATUS_RESP_CRC_ERR (1<<5)
+#define STATUS_READ_CRC_ERR (1<<3)
+#define STATUS_WRITE_CRC_ERR (1<<2)
+#define STATUS_TIME_OUT_RESP (1<<1)
+#define STATUS_TIME_OUT_READ (1<<0)
+#define STATUS_ERR_MASK 0x3f
+
+/* Clock rate definitions */
+#define CLK_RATE_PRESCALER(x) ((x) & 0xF)
+#define CLK_RATE_CLK_DIVIDER(x) (((x) & 0xF) << 4)
+
+/* Bit definitions for CMD_DAT_CONT */
+#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1<<12)
+#define CMD_DAT_CONT_STOP_READWAIT (1<<11)
+#define CMD_DAT_CONT_START_READWAIT (1<<10)
+#define CMD_DAT_CONT_BUS_WIDTH_1 (0<<8)
+#define CMD_DAT_CONT_BUS_WIDTH_4 (2<<8)
+#define CMD_DAT_CONT_INIT (1<<7)
+#define CMD_DAT_CONT_WRITE (1<<4)
+#define CMD_DAT_CONT_DATA_ENABLE (1<<3)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R1 (1)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R2 (2)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R3 (3)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R4 (4)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R5 (5)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R6 (6)
+
+/* Bit definitions for INT_CNTR */
+#define INT_CNTR_SDIO_INT_WKP_EN (1<<18)
+#define INT_CNTR_CARD_INSERTION_WKP_EN (1<<17)
+#define INT_CNTR_CARD_REMOVAL_WKP_EN (1<<16)
+#define INT_CNTR_CARD_INSERTION_EN (1<<15)
+#define INT_CNTR_CARD_REMOVAL_EN (1<<14)
+#define INT_CNTR_SDIO_IRQ_EN (1<<13)
+#define INT_CNTR_DAT0_EN (1<<12)
+#define INT_CNTR_BUF_READ_EN (1<<4)
+#define INT_CNTR_BUF_WRITE_EN (1<<3)
+#define INT_CNTR_END_CMD_RES (1<<2)
+#define INT_CNTR_WRITE_OP_DONE (1<<1)
+#define INT_CNTR_READ_OP_DONE (1<<0)
+
+#endif /* __MXC_MMC_REG_H__ */
diff --git a/drivers/mmc/host/s3c-hsmmc.c b/drivers/mmc/host/s3c-hsmmc.c
new file mode 100644
index 000000000000..21986a2c72f7
--- /dev/null
+++ b/drivers/mmc/host/s3c-hsmmc.c
@@ -0,0 +1,1457 @@
+/* -*- linux-c -*-
+ *
+ * linux/drivers/mmc/s3c-hsmmc.c - Samsung S3C24XX HS-MMC driver
+ *
+ * $Id: s3c-hsmmc.c,v 1.18 2007/06/07 07:14:57 scsuh Exp $
+ *
+ * Copyright (C) 2006 Samsung Electronics, All Rights Reserved.
+ * by Seung-Chull, Suh <sc.suh@samsung.com>
+ *
+ * This driver is made for High Speed MMC interface. This interface
+ * is adopted and implemented since s3c2443 was made.
+ *
+ * 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.
+ *
+ * Modified by Ryu,Euiyoul <steven.ryu@samsung.com>
+ * Modified by Seung-chull, Suh to support s3c6400
+ * Modified by Luis Galdos, Added PM-support
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/err.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+
+#include <asm/dma.h>
+#include <asm/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/scatterlist.h>
+#include <asm/sizes.h>
+#include <asm/mach/mmc.h>
+
+#include <mach/dma.h>
+#include <plat/hsmmc.h>
+#include <mach/regs-hsmmc.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-gpioj.h>
+
+
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] hsmmc: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "hsmmc: " fmt, ## args)
+
+#if 0
+#define CONFIG_S3CMCI_DEBUG
+#endif
+
+#ifdef CONFIG_S3CMCI_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "hsmmc: %s() " fmt, __FUNCTION__ , ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+/* Enables the support for the scatterlists */
+#define CONFIG_HSMMC_PSEUDO_SCATTERGATHER (1)
+
+#define DBG(x, ...)
+
+#include "s3c-hsmmc.h"
+
+#define DRIVER_NAME "s3c-hsmmc"
+#define PFX DRIVER_NAME ": "
+
+#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
+
+#ifdef CONFIG_HSMMC_PROC_DATA
+struct s3c_hsmmc_host *global_host[3];
+#endif
+
+/*****************************************************************************\
+ * *
+ * Low level functions *
+ * *
+\*****************************************************************************/
+
+static struct s3c_hsmmc_cfg s3c_hsmmc_platform = {
+ .hwport = 0,
+ .host_caps = (MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED),
+ .base = NULL,
+ .ctrl3[0] = 0x80800000,
+ .ctrl3[1] = 0x80800000,
+ .set_gpio = NULL,
+};
+
+/* s3c_hsmmc_get_platdata
+ *
+ * get the platform data associated with the given device, or return
+ * the default if there is none
+ */
+
+static struct s3c_hsmmc_cfg *s3c_hsmmc_get_platdata (struct device *dev)
+{
+ if (dev->platform_data != NULL)
+ return (struct s3c_hsmmc_cfg *)dev->platform_data;
+
+ return &s3c_hsmmc_platform;
+}
+
+static void s3c_hsmmc_reset (struct s3c_hsmmc_host *host, u8 mask)
+{
+ unsigned long timeout;
+
+ s3c_hsmmc_writeb(mask, S3C2410_HSMMC_SWRST);
+
+ if (mask & S3C_HSMMC_RESET_ALL)
+ host->clock = (uint)-1;
+
+ /* Wait max 100 ms */
+ timeout = 100;
+
+ /* hw clears the bit when it's done */
+ while (s3c_hsmmc_readb(S3C2410_HSMMC_SWRST) & mask) {
+ if (timeout == 0) {
+ printk("%s: Reset 0x%x never completed. \n",
+ mmc_hostname(host->mmc), (int)mask);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+}
+
+static void s3c_hsmmc_ios_init (struct s3c_hsmmc_host *host)
+{
+ u32 intmask;
+
+ s3c_hsmmc_reset(host, S3C_HSMMC_RESET_ALL);
+
+ intmask = S3C_HSMMC_INT_BUS_POWER | S3C_HSMMC_INT_DATA_END_BIT |
+ S3C_HSMMC_INT_DATA_CRC | S3C_HSMMC_INT_DATA_TIMEOUT | S3C_HSMMC_INT_INDEX |
+ S3C_HSMMC_INT_END_BIT | S3C_HSMMC_INT_CRC | S3C_HSMMC_INT_TIMEOUT |
+ S3C_HSMMC_INT_CARD_REMOVE | S3C_HSMMC_INT_CARD_INSERT |
+ S3C_HSMMC_INT_DATA_AVAIL | S3C_HSMMC_INT_SPACE_AVAIL |
+ S3C_HSMMC_INT_DATA_END | S3C_HSMMC_INT_RESPONSE;
+
+#ifdef CONFIG_HSMMC_PSEUDO_SCATTERGATHER
+ intmask |= S3C_HSMMC_INT_DMA_END;
+#endif
+ s3c_hsmmc_writel(intmask, S3C2410_HSMMC_NORINTSTSEN);
+ s3c_hsmmc_writel(intmask, S3C2410_HSMMC_NORINTSIGEN);
+}
+
+/*****************************************************************************\
+ * *
+ * Tasklets *
+ * *
+\*****************************************************************************/
+
+static void s3c_hsmmc_tasklet_card (ulong param)
+{
+ struct s3c_hsmmc_host *host;
+ unsigned long iflags;
+
+ host = (struct s3c_hsmmc_host*)param;
+ spin_lock_irqsave( &host->lock, iflags);
+
+ if (!(s3c_hsmmc_readl(S3C2410_HSMMC_PRNSTS) & S3C_HSMMC_CARD_PRESENT)) {
+ if (host->mrq) {
+ printk(KERN_ERR "%s: Card removed during transfer!\n",
+ mmc_hostname(host->mmc));
+ printk(KERN_ERR "%s: Resetting controller.\n",
+ mmc_hostname(host->mmc));
+
+ s3c_hsmmc_reset(host, S3C_HSMMC_RESET_CMD);
+ s3c_hsmmc_reset(host, S3C_HSMMC_RESET_DATA);
+
+ host->mrq->cmd->error = -EILSEQ;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+ }
+
+ spin_unlock_irqrestore( &host->lock, iflags);
+
+ mmc_detect_change(host->mmc, msecs_to_jiffies(500));
+}
+
+static void s3c_hsmmc_activate_led(struct s3c_hsmmc_host *host)
+{
+ unsigned int ctrl;
+ struct s3c_hsmmc_cfg *cfg = host->plat_data;
+
+ if (cfg->gpio_led == S3C2443_GPJ13) {
+ ctrl = s3c_hsmmc_readl(S3C2410_HSMMC_HOSTCTL);
+ ctrl &= ~S3C_HSMMC_CTRL_LED;
+ s3c_hsmmc_writel(ctrl, S3C2410_HSMMC_HOSTCTL);
+ } else if (cfg->gpio_led)
+ s3c2410_gpio_setpin(cfg->gpio_led, 1);
+
+
+#if 0 // for 6400
+ s3c_gpio_cfgpin(S3C_GPJ13, S3C_GPJ13_SD0LED);
+#endif
+}
+
+static void s3c_hsmmc_deactivate_led(struct s3c_hsmmc_host *host)
+{
+ unsigned int ctrl;
+ struct s3c_hsmmc_cfg *cfg = host->plat_data;
+
+ if (cfg->gpio_led == S3C2443_GPJ13) {
+ ctrl = s3c_hsmmc_readl(S3C2410_HSMMC_HOSTCTL);
+ ctrl |= S3C_HSMMC_CTRL_LED;
+ s3c_hsmmc_writel(ctrl, S3C2410_HSMMC_HOSTCTL);
+ } else if (cfg->gpio_led)
+ s3c2410_gpio_setpin(cfg->gpio_led, 0);
+
+
+#if 0 // for 6400
+ s3c_gpio_cfgpin(S3C_GPJ13, S3C_GPJ13_INP);
+#endif
+}
+
+/*****************************************************************************\
+ * *
+ * Core functions *
+ * *
+\*****************************************************************************/
+
+#ifdef CONFIG_HSMMC_PSEUDO_SCATTERGATHER
+static inline uint s3c_hsmmc_build_dma_table (struct s3c_hsmmc_host *host,
+ struct mmc_data *data)
+{
+ uint i, j = 0, sub_num = 0;
+ dma_addr_t addr;
+ uint size, length, end;
+ int boundary, xor_bit;
+ struct scatterlist * sg = data->sg;
+
+ /* build dma table except last one */
+ for (i=0; i<(host->sg_len-1); i++) {
+ addr = sg[i].dma_address;
+ size = sg[i].length;
+ DBG("%d - addr: %08x, size: %08x\n", i, addr, size);
+
+ for (; (j<CONFIG_S3C_HSMMC_MAX_HW_SEGS*4) && size; j++) {
+ end = addr + size;
+ xor_bit = min(7+(2+8+2), fls(addr^end) -1);
+
+ DBG("%08x %08x %08x %d\n", addr, size, end, xor_bit);
+
+ host->dblk[j].dma_address = addr;
+
+ length = (end & ~((1<<xor_bit)-1)) - addr;
+ boundary = xor_bit - (2+8+2);
+ DBG("length: %x, boundary: %d\n", length, boundary);
+
+ if (length < S3C_HSMMC_MALLOC_SIZE) {
+ boundary = 0;
+
+ if ((addr+length) & (S3C_HSMMC_MALLOC_SIZE-1)) {
+ void *dest;
+
+ DBG("#########error fixing: %08x, %x\n", addr, length);
+ dest = host->sub_block[sub_num] + S3C_HSMMC_MALLOC_SIZE - length;
+ if (data->flags & MMC_DATA_WRITE) { /* write */
+ memcpy(dest, phys_to_virt(addr), length);
+ }
+
+ host->dblk[j].original = phys_to_virt(addr);
+ host->dblk[j].dma_address = dma_map_single(NULL, dest, length, host->dma_dir);
+ sub_num++;
+ }
+ }
+
+ host->dblk[j].length = length;
+ host->dblk[j].boundary = boundary;
+ DBG(" %d: %08x, %08x %x\n",
+ j, addr, length, boundary);
+ addr += length;
+ size -= length;
+ }
+ }
+
+ /* the last one */
+ host->dblk[j].dma_address = sg[i].dma_address;
+ host->dblk[j].length = sg[i].length;
+ host->dblk[j].boundary = 0x7;
+
+ return (j+1);
+}
+#endif
+
+static inline void s3c_hsmmc_prepare_data(struct s3c_hsmmc_host *host,
+ struct mmc_command *cmd)
+{
+ u32 reg;
+ struct mmc_data *data = cmd->data;
+
+ /* If no data to send, only enable the CMD complete interrupt */
+ if (data == NULL) {
+ reg = s3c_hsmmc_readl(S3C2410_HSMMC_NORINTSTSEN);
+ reg |= S3C_HSMMC_NIS_CMDCMP;
+ s3c_hsmmc_writel(reg, S3C2410_HSMMC_NORINTSTSEN);
+ return;
+ }
+
+#ifdef CONFIG_HSMMC_PROC_DATA
+ {
+ u32 total_size;
+ int bit;
+
+ total_size = data->blksz * data->blocks;
+ bit = fls(total_size) - 13;
+ if (bit < 0) bit = 0;
+ if (data->flags & MMC_DATA_WRITE) { /* write */
+ host->tx_pkt[bit]++;
+ } else { /* read */
+ host->rx_pkt[bit]++;
+ }
+ }
+#endif
+
+ reg = s3c_hsmmc_readl(S3C2410_HSMMC_NORINTSTSEN) & ~S3C_HSMMC_NIS_CMDCMP;
+ reg |= S3C_HSMMC_NIS_TRSCMP;
+ s3c_hsmmc_writel(reg, S3C2410_HSMMC_NORINTSTSEN);
+
+ host->dma_dir = (data->flags & MMC_DATA_READ)
+ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+
+
+#ifdef CONFIG_HSMMC_PSEUDO_SCATTERGATHER
+ host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ host->dma_dir);
+
+ reg = s3c_hsmmc_readl(S3C2410_HSMMC_NORINTSTSEN);
+ if (host->sg_len == 1) {
+ reg &= ~S3C_HSMMC_NIS_DMA;
+ } else {
+ reg |= S3C_HSMMC_NIS_DMA;
+ }
+ s3c_hsmmc_writel(reg, S3C2410_HSMMC_NORINTSTSEN);
+
+ DBG("data->sg_len: %d\n", data->sg_len);
+ host->dma_blk = s3c_hsmmc_build_dma_table(host, data);
+ host->next_blk = 0;
+#else
+ dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ host->dma_dir);
+#endif
+}
+
+static inline void s3c_hsmmc_set_transfer_mode (struct s3c_hsmmc_host *host,
+ struct mmc_data *data)
+{
+ u16 mode;
+
+ mode = S3C_HSMMC_TRNS_DMA;
+
+ if (data->stop)
+ mode |= S3C_HSMMC_TRNS_ACMD12;
+
+ /* Set the configuration for the number of blocks to transfer */
+ if (data->blocks > 1)
+ mode |= (S3C_HSMMC_TRNS_MULTI | S3C_HSMMC_TRNS_BLK_CNT_EN);
+
+ /* Set the transfer direction */
+ if (data->flags & MMC_DATA_READ)
+ mode |= S3C_HSMMC_TRNS_READ;
+
+ printk_debug("Setting the transfer mode to 0x%08x\n", mode);
+ s3c_hsmmc_writew(mode, S3C2410_HSMMC_TRNMOD);
+}
+
+static inline void s3c_hsmmc_send_register (struct s3c_hsmmc_host *host)
+{
+ struct mmc_command *cmd = host->cmd;
+ struct mmc_data *data = cmd->data;
+
+ u32 cmd_val;
+
+ if (data) {
+#ifdef CONFIG_HSMMC_PSEUDO_SCATTERGATHER
+ struct s3c_hsmmc_dma_blk *dblk;
+
+ dblk = &host->dblk[0];
+
+ s3c_hsmmc_writew(S3C_HSMMC_MAKE_BLKSZ(dblk->boundary, data->blksz),
+ S3C2410_HSMMC_BLKSIZE);
+
+ s3c_hsmmc_writel(dblk->dma_address, S3C2410_HSMMC_SYSAD);
+#else
+ s3c_hsmmc_writew(S3C_HSMMC_MAKE_BLKSZ(0x7, data->blksz),
+ S3C2410_HSMMC_BLKSIZE);
+
+ s3c_hsmmc_writel(sg_dma_address(data->sg), S3C2410_HSMMC_SYSAD);
+#endif
+
+ s3c_hsmmc_writew(data->blocks, S3C2410_HSMMC_BLKCNT);
+ s3c_hsmmc_set_transfer_mode(host, data);
+ }
+
+ s3c_hsmmc_writel(cmd->arg, S3C2410_HSMMC_ARGUMENT);
+
+ cmd_val = (cmd->opcode << 8);
+ if (cmd_val == (12<<8))
+ cmd_val |= (3 << 6);
+
+ if (cmd->flags & MMC_RSP_136) /* Long RSP */
+ cmd_val |= S3C_HSMMC_CMD_RESP_LONG;
+ else if (cmd->flags & MMC_RSP_BUSY) /* R1B */
+ cmd_val |= S3C_HSMMC_CMD_RESP_SHORT_BUSY;
+ else if (cmd->flags & MMC_RSP_PRESENT) /* Normal RSP */
+ cmd_val |= S3C_HSMMC_CMD_RESP_SHORT;
+
+ if (cmd->flags & MMC_RSP_OPCODE)
+ cmd_val |= S3C_HSMMC_CMD_INDEX;
+
+ if (cmd->flags & MMC_RSP_CRC)
+ cmd_val |= S3C_HSMMC_CMD_CRC;
+
+ if (data)
+ cmd_val |= S3C_HSMMC_CMD_DATA;
+
+ s3c_hsmmc_writew(cmd_val, S3C2410_HSMMC_CMDREG);
+}
+
+
+/* Send a command to the HSMMC-controller */
+static inline void s3c_hsmmc_send_command(struct s3c_hsmmc_host *host,
+ struct mmc_command *cmd)
+{
+ u32 mask;
+ ulong timeout;
+
+ /* Wait max 10 ms */
+ timeout = 10;
+
+ mask = S3C_HSMMC_CMD_INHIBIT;
+ if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
+ mask |= S3C_HSMMC_DATA_INHIBIT;
+
+ while (s3c_hsmmc_readl(S3C2410_HSMMC_PRNSTS) & mask) {
+ if (timeout == 0) {
+ printk_err("%s: Controller never released "
+ "inhibit bit(s).\n", mmc_hostname(host->mmc));
+ cmd->error = -EILSEQ;
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ mod_timer(&host->timer, jiffies + 4 * HZ);
+
+ host->cmd = cmd;
+
+ s3c_hsmmc_prepare_data(host, cmd);
+ s3c_hsmmc_send_register(host);
+}
+
+
+static void s3c_hsmmc_finish_data(struct s3c_hsmmc_host *host)
+{
+ struct mmc_data *data;
+ u16 blocks;
+
+ BUG_ON(!host->data);
+
+ data = host->data;
+ host->data = NULL;
+
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (data->flags & MMC_DATA_READ)
+ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+
+ /*
+ * Controller doesn't count down when in single block mode.
+ */
+ if ((data->blocks == 1) && (data->error == 0))
+ blocks = 0;
+ else {
+ blocks = s3c_hsmmc_readw(S3C2410_HSMMC_BLKCNT);
+ }
+ data->bytes_xfered = data->blksz * (data->blocks - blocks);
+
+ if ((data->error == 0) && blocks) {
+ printk_err("%s: Controller signalled completion even "
+ "though there were blocks left. : %d\n",
+ mmc_hostname(host->mmc), blocks);
+ data->error = -EILSEQ;
+ }
+
+ printk_debug("Ending data transfer (%d bytes)\n", data->bytes_xfered);
+
+ tasklet_schedule(&host->finish_tasklet);
+}
+
+static void s3c_hsmmc_finish_command (struct s3c_hsmmc_host *host)
+{
+ int i;
+ unsigned int resp;
+
+ BUG_ON(host->cmd == NULL);
+
+ if (host->cmd->flags & MMC_RSP_PRESENT) {
+ if (host->cmd->flags & MMC_RSP_136) {
+ /* CRC is stripped so we need to do some shifting. */
+ for (i = 0; i < 4; i++) {
+ resp = s3c_hsmmc_readl(S3C2410_HSMMC_RSPREG0 +
+ (3 - i) * 4);
+ host->cmd->resp[i] = resp << 8;
+ if (i != 3) {
+ resp = s3c_hsmmc_readb(S3C2410_HSMMC_RSPREG0 +
+ ((3 - i) * 4) - 1);
+ host->cmd->resp[i] |= resp;
+ }
+ }
+ } else {
+ resp = s3c_hsmmc_readl(S3C2410_HSMMC_RSPREG0);
+ printk_debug("Got a RSP: 0x%08x\n", resp);
+ host->cmd->resp[0] = resp;
+ }
+ }
+
+ host->cmd->error = 0;
+
+ printk_debug("Ending cmd (%u)\n", host->cmd->opcode);
+
+ if (host->cmd->data)
+ host->data = host->cmd->data;
+ else
+ tasklet_schedule(&host->finish_tasklet);
+
+ host->cmd = NULL;
+}
+
+static void s3c_hsmmc_tasklet_finish (unsigned long param)
+{
+ struct s3c_hsmmc_host *host;
+ unsigned long iflags;
+ struct mmc_request *mrq;
+
+ host = (struct s3c_hsmmc_host*)param;
+
+ spin_lock_irqsave(&host->lock, iflags);
+
+ del_timer(&host->timer);
+
+ mrq = host->mrq;
+
+ /*
+ * The controller needs a reset of internal state machines
+ * upon error conditions.
+ */
+ if ((mrq->cmd->error != 0) ||
+ (mrq->data && (mrq->data->error != 0)) ) {
+ s3c_hsmmc_reset(host, S3C_HSMMC_RESET_CMD);
+ s3c_hsmmc_reset(host, S3C_HSMMC_RESET_DATA);
+ }
+
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+
+ s3c_hsmmc_deactivate_led(host);
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, iflags);
+
+ mmc_request_done(host->mmc, mrq);
+}
+
+/*****************************************************************************
+ * *
+ * Interrupt handling *
+ * *
+ *****************************************************************************/
+
+static void s3c_hsmmc_cmd_irq (struct s3c_hsmmc_host *host, u32 intmask)
+{
+ if (intmask & S3C_HSMMC_INT_TIMEOUT)
+ host->cmd->error = -ETIMEDOUT;
+ else if (intmask & S3C_HSMMC_INT_CRC)
+ host->cmd->error = -EILSEQ;
+ else if (intmask & (S3C_HSMMC_INT_END_BIT | S3C_HSMMC_INT_INDEX))
+ host->cmd->error = -EILSEQ;
+ else
+ host->cmd->error = -EILSEQ;
+
+ tasklet_schedule(&host->finish_tasklet);
+}
+
+static void s3c_hsmmc_data_irq (struct s3c_hsmmc_host *host, u32 intmask)
+{
+ if (!host->data) {
+ /*
+ * A data end interrupt is sent together with the response
+ * for the stop command.
+ */
+ if (intmask & S3C_HSMMC_INT_DATA_END)
+ return;
+
+ printk_err("%s: Got data interrupt even though no "
+ "data operation was in progress.\n",
+ mmc_hostname(host->mmc));
+ return;
+ }
+
+ if (intmask & S3C_HSMMC_INT_DATA_TIMEOUT)
+ host->data->error = -ETIMEDOUT;
+ else if (intmask & S3C_HSMMC_INT_DATA_CRC)
+ host->data->error = -EILSEQ;
+ else if (intmask & S3C_HSMMC_INT_DATA_END_BIT)
+ host->data->error = -EILSEQ;
+
+ if (host->data->error != 0)
+ s3c_hsmmc_finish_data(host);
+}
+
+
+/*****************************************************************************\
+ * *
+ * Interrupt handling *
+ * *
+\*****************************************************************************/
+
+/*
+ * ISR for SDI Interface IRQ
+ * Communication between driver and ISR works as follows:
+ * host->mrq points to current request
+ * host->complete_what tells the ISR when the request is considered done
+ * COMPLETION_CMDSENT when the command was sent
+ * COMPLETION_RSPFIN when a response was received
+ * COMPLETION_XFERFINISH when the data transfer is finished
+ * COMPLETION_XFERFINISH_RSPFIN both of the above.
+ * host->complete_request is the completion-object the driver waits for
+ *
+ * 1) Driver sets up host->mrq and host->complete_what
+ * 2) Driver prepares the transfer
+ * 3) Driver enables interrupts
+ * 4) Driver starts transfer
+ * 5) Driver waits for host->complete_rquest
+ * 6) ISR checks for request status (errors and success)
+ * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
+ * 7) ISR completes host->complete_request
+ * 8) ISR disables interrupts
+ * 9) Driver wakes up and takes care of the request
+ */
+
+static irqreturn_t s3c_hsmmc_irq (int irq, void *dev_id)
+{
+ irqreturn_t result = 0;
+ struct s3c_hsmmc_host *host = dev_id;
+ struct mmc_request *mrq;
+ u32 intsts;
+#ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND
+ uint i, org_irq_sts;
+#endif
+ spin_lock(&host->lock);
+
+ mrq = host->mrq;
+
+ intsts = s3c_hsmmc_readw(S3C2410_HSMMC_NORINTSTS);
+
+ /* Sometimes, hsmmc does not update its status bit immediately
+ * when it generates irqs. by scsuh.
+ */
+#ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND
+ for (i=0; i<0x1000; i++) {
+ if ((intsts = s3c_hsmmc_readw(S3C2410_HSMMC_NORINTSTS)))
+ break;
+ }
+#endif
+
+ if (unlikely(!intsts)) {
+ result = IRQ_NONE;
+ goto out;
+ }
+ intsts = s3c_hsmmc_readl(S3C2410_HSMMC_NORINTSTS);
+#ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND
+ org_irq_sts = intsts;
+#endif
+
+ printk_debug("IRQ : Status 0x%08x\n", intsts);
+#if 0
+ printk(PFX "got interrupt = 0x%08x\n", intsts);
+ printk(PFX "got mask = 0x%08x\n", s3c_hsmmc_readl(S3C2410_HSMMC_NORINTSTSEN));
+ printk(PFX "got signal = 0x%08x\n", s3c_hsmmc_readl(S3C2410_HSMMC_NORINTSIGEN));
+#endif
+
+ if (unlikely(intsts & S3C_HSMMC_INT_CARD_CHANGE)) {
+ u32 reg16;
+
+ if (intsts & S3C_HSMMC_INT_CARD_INSERT)
+ printk_debug("card inserted.\n");
+ else if (intsts & S3C_HSMMC_INT_CARD_REMOVE)
+ printk_debug("card removed.\n");
+
+ reg16 = s3c_hsmmc_readw(S3C2410_HSMMC_NORINTSTSEN);
+ s3c_hsmmc_writew(reg16 & ~S3C_HSMMC_INT_CARD_CHANGE,
+ S3C2410_HSMMC_NORINTSTSEN);
+ s3c_hsmmc_writew(S3C_HSMMC_INT_CARD_CHANGE, S3C2410_HSMMC_NORINTSTS);
+ s3c_hsmmc_writew(reg16, S3C2410_HSMMC_NORINTSTSEN);
+
+ intsts &= ~S3C_HSMMC_INT_CARD_CHANGE;
+
+ tasklet_schedule(&host->card_tasklet);
+ goto insert;
+ }
+
+ if (likely(!(intsts & S3C_HSMMC_NIS_ERR))) {
+ s3c_hsmmc_writel(intsts, S3C2410_HSMMC_NORINTSTS);
+
+ if (intsts & S3C_HSMMC_NIS_CMDCMP) {
+ printk_debug("command done\n");
+ s3c_hsmmc_finish_command(host);
+ }
+
+ if (intsts & S3C_HSMMC_NIS_TRSCMP) {
+ printk_debug("transfer done\n\n");
+ s3c_hsmmc_finish_command(host);
+ s3c_hsmmc_finish_data(host);
+ intsts &= ~S3C_HSMMC_NIS_DMA;
+ }
+
+#ifdef CONFIG_HSMMC_PSEUDO_SCATTERGATHER
+ if (intsts & S3C_HSMMC_NIS_DMA) {
+ struct s3c_hsmmc_dma_blk *dblk;
+
+ dblk = &host->dblk[host->next_blk];
+ if (dblk->original) {
+ /* on read */
+ if (host->dma_dir == DMA_FROM_DEVICE) {
+ memcpy(dblk->original,
+ phys_to_virt(dblk->dma_address),
+ dblk->length);
+ }
+ dma_unmap_single(NULL, dblk->dma_address, dblk->length,
+ host->dma_dir);
+ dblk->original = 0;
+ }
+
+ host->next_blk++;
+ dblk = &host->dblk[host->next_blk];
+
+ if (host->next_blk == (host->dma_blk-1)) {
+ u32 reg;
+
+ reg = s3c_hsmmc_readl(S3C2410_HSMMC_NORINTSTSEN);
+ reg &= ~S3C_HSMMC_NIS_DMA;
+ s3c_hsmmc_writel(reg, S3C2410_HSMMC_NORINTSTSEN);
+ }
+
+ /* We do not handle DMA boundaries, so set it to max (512 KiB) */
+ s3c_hsmmc_writew((dblk->boundary<<12 | 0x200),
+ S3C2410_HSMMC_BLKSIZE);
+ s3c_hsmmc_writel(dblk->dma_address, S3C2410_HSMMC_SYSAD);
+ }
+#endif
+ } else {
+ unsigned int interr;
+ interr = s3c_hsmmc_readl(S3C2410_HSMMC_ERRINTSTS) & 0xffff;
+
+ /* Timeout errors are printed in the tasklet, or? (Luis Galdos) */
+ if (interr != S3C_HSMMC_EIS_CMDTIMEOUT)
+ printk_err("Error detected : 0x%04x\n", interr);
+
+ if (intsts & S3C_HSMMC_INT_CMD_MASK) {
+ s3c_hsmmc_writel(intsts & S3C_HSMMC_INT_CMD_MASK,
+ S3C2410_HSMMC_NORINTSTS);
+ s3c_hsmmc_cmd_irq(host, intsts & S3C_HSMMC_INT_CMD_MASK);
+ }
+
+ if (intsts & S3C_HSMMC_INT_DATA_MASK) {
+ s3c_hsmmc_writel(intsts & S3C_HSMMC_INT_DATA_MASK,
+ S3C2410_HSMMC_NORINTSTS);
+ s3c_hsmmc_finish_command(host);
+ s3c_hsmmc_data_irq(host, intsts & S3C_HSMMC_INT_DATA_MASK);
+ }
+
+ intsts &= ~(S3C_HSMMC_INT_CMD_MASK | S3C_HSMMC_INT_DATA_MASK);
+ }
+
+ /* XXX: fix later by scsuh */
+#if 0
+ if (intsts & S3C_HSMMC_INT_BUS_POWER) {
+ printk_err("%s: Card is consuming too much power!\n",
+ mmc_hostname(host->mmc));
+ s3c_hsmmc_writel(S3C_HSMMC_INT_BUS_POWER, S3C2410_HSMMC_NORINTSTS);
+ }
+
+ intsts &= S3C_HSMMC_INT_BUS_POWER;
+
+ if (intsts) {
+ printk_err("%s: Unexpected interrupt 0x%08x.\n",
+ mmc_hostname(host->mmc), intsts);
+
+ s3c_hsmmc_writel(intsts, S3C2410_HSMMC_NORINTSTS);
+ }
+#endif
+
+#ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND
+ for (i=0; i<0x1000; i++) {
+ if (org_irq_sts != s3c_hsmmc_readl(S3C2410_HSMMC_NORINTSTS))
+ break;
+ }
+#endif
+
+insert:
+ result = IRQ_HANDLED;
+ mmiowb();
+
+out:
+ spin_unlock(&host->lock);
+
+ return result;
+}
+
+static void s3c_hsmmc_check_status (unsigned long data)
+{
+ struct s3c_hsmmc_host *host = (struct s3c_hsmmc_host *)data;
+
+ s3c_hsmmc_irq(0, host);
+}
+
+static void s3c_hsmmc_request (struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct s3c_hsmmc_host *host = mmc_priv(mmc);
+ unsigned long flags;
+
+ printk_debug("[CMD%d] arg:0x%08x flags:0x%02x retries:%u\n",
+ mrq->cmd->opcode, mrq->cmd->arg,
+ mrq->cmd->flags, mrq->cmd->retries);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ WARN_ON(host->mrq != NULL);
+
+ s3c_hsmmc_activate_led(host);
+
+ host->mrq = mrq;
+
+ if (likely(s3c_hsmmc_readl(S3C2410_HSMMC_PRNSTS) & S3C_HSMMC_CARD_PRESENT)) {
+ s3c_hsmmc_send_command(host, mrq->cmd);
+ } else {
+ printk_debug("No card present? Aborting a command request.\n");
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/* return 0: OK
+ * return -1: error
+ */
+static int set_bus_width (struct s3c_hsmmc_host *host, uint width)
+{
+ u8 reg;
+
+ reg = s3c_hsmmc_readb(S3C2410_HSMMC_HOSTCTL);
+
+ switch (width) {
+ case MMC_BUS_WIDTH_1:
+ reg &= ~(S3C_HSMMC_CTRL_4BIT | S3C_HSMMC_CTRL_8BIT);
+ break;
+ case MMC_BUS_WIDTH_4:
+ reg |= S3C_HSMMC_CTRL_4BIT;
+ break;
+ case MMC_BUS_WIDTH_8:
+ reg |= S3C_HSMMC_CTRL_8BIT;
+ break;
+ default:
+ printk_err("Invalid bus width %u\n", width);
+ return -EINVAL;
+ }
+
+ s3c_hsmmc_writeb(reg, S3C2410_HSMMC_HOSTCTL);
+
+ printk_debug("HOSTCTL 0x%02x\n", s3c_hsmmc_readb(S3C2410_HSMMC_HOSTCTL));
+
+ return 0;
+}
+
+static void hsmmc_set_clock (struct s3c_hsmmc_host *host, ulong clock)
+{
+ uint i, j;
+ u32 val = 0, tmp_clk = 0, clk_src = 0;
+ ulong timeout;
+ u16 div = -1;
+ u8 ctrl;
+
+ /* if we already set, just out. */
+ if (clock == host->clock) {
+ printk("%p:host->clock0 : %d\n", host->base, host->clock);
+ return;
+ }
+
+ /* before setting clock, clkcon must be disabled. */
+ s3c_hsmmc_writew(0, S3C2410_HSMMC_CLKCON);
+
+ s3c_hsmmc_writeb(S3C_HSMMC_TIMEOUT_MAX, S3C2410_HSMMC_TIMEOUTCON);
+
+ /* change the edge type according to frequency */
+ ctrl = s3c_hsmmc_readb(S3C2410_HSMMC_HOSTCTL);
+ if (clock > 25000000)
+ ctrl |= S3C_HSMMC_CTRL_HIGHSPEED;
+ else
+ ctrl &= ~S3C_HSMMC_CTRL_HIGHSPEED;
+ s3c_hsmmc_writeb(ctrl, S3C2410_HSMMC_HOSTCTL);
+
+ if (clock == 0) {
+ return;
+ }
+
+ /* calculate optimal clock. by scsuh */
+ for (i=0; i < 3; i++) {
+ tmp_clk = clk_get_rate(host->clk[i]);
+ if (tmp_clk <= clock) {
+ if (tmp_clk >= val) {
+ val = tmp_clk;
+ div = 0;
+ clk_src = i+1;
+ }
+ }
+
+ for (j=0x1; j<=0x80; j <<= 1) {
+ tmp_clk = clk_get_rate(host->clk[i]) / (j<<1);
+ if ((val < tmp_clk) && (tmp_clk <= clock)) {
+ val = tmp_clk;
+ div = j;
+ clk_src = i+1;
+ break;
+ }
+ }
+ }
+
+ /* clk_src = 0x00; */
+ printk_debug("val: %d, div: %x, clk_src: %d\n", val, div, clk_src);
+
+ s3c_hsmmc_writel(0x0000c100 | (clk_src << 4), S3C2410_HSMMC_CONTROL2);
+ if (clock > 25000000)
+ /* 0x00008080 6400, 0x00800080 2443 */
+ s3c_hsmmc_writel(host->ctrl3[1], S3C2410_HSMMC_CONTROL3);
+ else
+ /* 0x80808080 6400, 0x00800080 2443 */
+ s3c_hsmmc_writel(host->ctrl3[0], S3C2410_HSMMC_CONTROL3);
+
+ s3c_hsmmc_writew(((div<<8) | S3C_HSMMC_CLOCK_INT_EN), S3C2410_HSMMC_CLKCON);
+
+ timeout = 10;
+ while (!((val = s3c_hsmmc_readw(S3C2410_HSMMC_CLKCON))
+ & S3C_HSMMC_CLOCK_INT_STABLE)) {
+ if (!timeout) {
+ printk_err("Clock stabilization: %08x | Div %u\n", val, div);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ s3c_hsmmc_writew(val | S3C_HSMMC_CLOCK_CARD_EN, S3C2410_HSMMC_CLKCON);
+ timeout = 10;
+ while (!((val = s3c_hsmmc_readw(S3C2410_HSMMC_CLKCON))
+ & S3C_HSMMC_CLOCK_EXT_STABLE)) {
+ if (!timeout) {
+ printk_err("Clock stabilization: %08x | Div %u\n", val, div);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+//out:
+// host->clock = clock;
+// printk("%p: host->clock1 : %d\n", host->base, host->clock);
+}
+
+
+static void s3c_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct s3c_hsmmc_host *host = mmc_priv(mmc);
+ struct s3c_hsmmc_cfg *cfg = host->plat_data;
+
+ unsigned long iflags;
+
+ spin_lock_irqsave(&host->lock, iflags);
+
+
+ switch (ios->power_mode) {
+ case MMC_POWER_ON:
+ case MMC_POWER_UP:
+ s3c2410_gpio_cfgpin(S3C2443_GPL0, S3C2443_GPL0_SD0DAT0);
+
+ if (cfg->host_caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
+ s3c2410_gpio_cfgpin(S3C2443_GPL1, S3C2443_GPL1_SD0DAT1);
+ s3c2410_gpio_cfgpin(S3C2443_GPL2, S3C2443_GPL2_SD0DAT2);
+ s3c2410_gpio_cfgpin(S3C2443_GPL3, S3C2443_GPL3_SD0DAT3);
+ }
+
+ if (cfg->host_caps & MMC_CAP_8_BIT_DATA) {
+ s3c2410_gpio_cfgpin(S3C2443_GPL4, S3C2443_GPL4_SD0DAT4);
+ s3c2410_gpio_cfgpin(S3C2443_GPL5, S3C2443_GPL5_SD0DAT5);
+ s3c2410_gpio_cfgpin(S3C2443_GPL6, S3C2443_GPL6_SD0DAT6);
+ s3c2410_gpio_cfgpin(S3C2443_GPL7, S3C2443_GPL7_SD0DAT7);
+ }
+
+ s3c2410_gpio_cfgpin(S3C2443_GPL8, S3C2443_GPL8_SD0CMD);
+ s3c2410_gpio_cfgpin(S3C2443_GPL9, S3C2443_GPL9_SD0CLK);
+
+ /* Check for the dedicated GPIOs of the HSMMC-controller */
+ if (cfg->gpio_led == S3C2443_GPJ13)
+ s3c2410_gpio_cfgpin(S3C2443_GPJ13, S3C2440_GPJ13_SD0LED);
+
+ if (cfg->gpio_detect == S3C2443_GPJ14)
+ s3c2410_gpio_cfgpin(S3C2443_GPJ14, S3C2440_GPJ14_SD0CD);
+
+ if (cfg->gpio_wprotect == S3C2443_GPJ15)
+ s3c2410_gpio_cfgpin(S3C2443_GPJ15, S3C2440_GPJ15_SD0WP);
+
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Reset the chip on each power off.
+ * Should clear out any weird states.
+ */
+ if (ios->power_mode == MMC_POWER_OFF) {
+ s3c_hsmmc_writew(0, S3C2410_HSMMC_NORINTSIGEN);
+ s3c_hsmmc_ios_init(host);
+ }
+
+ if (host->plat_data->set_gpio)
+ host->plat_data->set_gpio();
+
+ printk_debug("Clock: %d | Bus width %d\n", ios->clock, ios->bus_width);
+ hsmmc_set_clock(host, ios->clock);
+ set_bus_width(host, ios->bus_width);
+
+ printk_debug("CTRL2 0x%08x | CTRL3 0x%08x | CLKCON 0x%04x\n",
+ s3c_hsmmc_readl(S3C2410_HSMMC_CONTROL2),
+ s3c_hsmmc_readl(S3C2410_HSMMC_CONTROL3),
+ s3c_hsmmc_readw(S3C2410_HSMMC_CLKCON));
+
+ if (ios->power_mode == MMC_POWER_OFF)
+ s3c_hsmmc_writeb(S3C_HSMMC_POWER_OFF, S3C2410_HSMMC_PWRCON);
+ else
+ s3c_hsmmc_writeb(S3C_HSMMC_POWER_ON_ALL, S3C2410_HSMMC_PWRCON);
+
+ udelay(1000);
+ spin_unlock_irqrestore(&host->lock, iflags);
+}
+
+
+
+/*
+ * If no GPIO was configured for the write protect, then assume that the card
+ * is NOT write protected
+ */
+static int s3c_hsmmc_get_ro(struct mmc_host *mmc)
+{
+ struct s3c_hsmmc_host *host = mmc_priv(mmc);
+ struct s3c_hsmmc_cfg *cfg = host->plat_data;
+ unsigned int retval;
+
+ /* Depending on the configured GPIO get the RO-value */
+ if (cfg->gpio_wprotect == S3C2443_GPJ15) {
+ retval = s3c_hsmmc_readl(S3C2410_HSMMC_PRNSTS);
+ retval &= S3C_HSMMC_WRITE_PROTECT;
+ } else if (cfg->gpio_wprotect) {
+ retval = s3c2410_gpio_getpin(cfg->gpio_wprotect);
+ retval = cfg->wprotect_invert ? !retval : retval;
+ } else
+ retval = 0;
+
+ return retval;
+}
+
+
+static struct mmc_host_ops s3c_hsmmc_ops = {
+ .request = s3c_hsmmc_request,
+ .set_ios = s3c_hsmmc_set_ios,
+ .get_ro = s3c_hsmmc_get_ro,
+};
+
+
+
+static int s3c_hsmmc_probe (struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct s3c_hsmmc_host *host;
+ struct s3c_hsmmc_cfg *plat_data;
+ uint i;
+ int ret;
+
+ mmc = mmc_alloc_host(sizeof(struct s3c_hsmmc_host), &pdev->dev);
+ if (!mmc)
+ ret = -ENOMEM;
+
+ plat_data = s3c_hsmmc_get_platdata(&pdev->dev);
+
+ host = mmc_priv(mmc);
+
+ host->mmc = mmc;
+ host->plat_data = plat_data;
+ platform_set_drvdata(pdev, mmc);
+
+ host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!host->mem) {
+ printk_err("Failed to get io memory region resouce.\n");
+ ret = -ENOENT;
+ goto probe_free_host;
+ }
+
+ host->mem = request_mem_region(host->mem->start,
+ RESSIZE(host->mem), pdev->name);
+ if (!host->mem) {
+ printk_err("Failed to request io memory region.\n");
+ ret = -ENOENT;
+ goto probe_free_host;
+ }
+
+ host->base = ioremap(host->mem->start, RESSIZE(host->mem));
+ if (!host->base) {
+ printk_err("Failed to ioremap() the region %p\n", host->mem);
+ ret = -EINVAL;
+ goto err_free_mem_region;
+ }
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq == 0) {
+ printk("failed to get interrupt resouce.\n");
+ ret = -EINVAL;
+ goto untasklet;
+ }
+
+ host->flags |= S3C_HSMMC_USE_DMA;
+
+ /* every platform has different ctrl3 value. by scsuh */
+ host->ctrl3[0] = plat_data->ctrl3[0];
+ host->ctrl3[1] = plat_data->ctrl3[1];
+
+#ifdef CONFIG_HSMMC_PSEUDO_SCATTERGATHER
+ for (i=0; i<S3C_HSMMC_MAX_SUB_BUF; i++)
+ host->sub_block[i] = kmalloc(S3C_HSMMC_MALLOC_SIZE, GFP_KERNEL);
+#endif
+
+ s3c_hsmmc_reset(host, S3C_HSMMC_RESET_ALL);
+
+#ifdef CONFIG_ARCH_S3C6400
+ clk_enable(clk_get(&pdev->dev, "sclk_48m"));
+#endif
+
+ /* register 3 clock source if exist */
+ for (i=0; i<3; i++) {
+ host->clk[i] = clk_get(&pdev->dev, plat_data->clk_name[i]);
+ if (IS_ERR(host->clk[i])) {
+ ret = PTR_ERR(host->clk[i]);
+ host->clk[i] = ERR_PTR(-ENOENT);
+ }
+
+ if (clk_enable(host->clk[i])) {
+ host->clk[i] = ERR_PTR(-ENOENT);
+ }
+ }
+
+ mmc->ops = &s3c_hsmmc_ops;
+ mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+ mmc->f_min = 400 * 1000; /* at least 400kHz */
+
+ /* you must make sure that our hsmmc block can support
+ * up to 52MHz. by scsuh
+ */
+ mmc->f_max = 100 * MHZ;
+ mmc->caps = plat_data->host_caps;
+ printk_debug("mmc->caps: %08x\n", (unsigned int)mmc->caps);
+
+ spin_lock_init(&host->lock);
+
+ /*
+ * Maximum number of segments. Hardware cannot do scatter lists.
+ * XXX: must modify later. by scsuh
+ */
+#ifdef CONFIG_HSMMC_PSEUDO_SCATTERGATHER
+ mmc->max_hw_segs = CONFIG_S3C_HSMMC_MAX_HW_SEGS;
+ mmc->max_phys_segs = CONFIG_S3C_HSMMC_MAX_HW_SEGS;
+#else
+ mmc->max_hw_segs = 1;
+#endif
+
+ /*
+ * Maximum number of sectors in one transfer. Limited by DMA boundary
+ * size (512KiB), which means (512 KiB/512=) 1024 entries.
+ */
+ mmc->max_blk_count = 1024;
+
+ /*
+ * Maximum segment size. Could be one segment with the maximum number
+ * of sectors.
+ */
+ mmc->max_seg_size = mmc->max_blk_count * 512;
+ mmc->max_req_size = mmc->max_blk_count * 512;
+
+ init_timer(&host->timer);
+ host->timer.data = (unsigned long)host;
+ host->timer.function = s3c_hsmmc_check_status;
+ host->timer.expires = jiffies + HZ;
+
+ /*
+ * Init tasklets.
+ */
+ tasklet_init(&host->card_tasklet,
+ s3c_hsmmc_tasklet_card, (unsigned long)host);
+ tasklet_init(&host->finish_tasklet,
+ s3c_hsmmc_tasklet_finish, (unsigned long)host);
+
+ ret = request_irq(host->irq, s3c_hsmmc_irq, 0, DRIVER_NAME, host);
+ if (ret)
+ goto untasklet;
+
+ s3c_hsmmc_ios_init(host);
+
+ mmc_add_host(mmc);
+
+#ifdef CONFIG_HSMMC_PROC_DATA
+ global_host[plat_data->hwport] = host;
+#endif
+
+ printk(KERN_INFO "%s.%d: at 0x%p with irq %d. clk src:",
+ pdev->name, pdev->id, host->base, host->irq);
+ for (i=0; i<3; i++) {
+ if (!IS_ERR(host->clk[i]))
+ printk(" %s", plat_data->clk_name[i]);
+ }
+ printk("\n");
+
+ return 0;
+
+ untasklet:
+ tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+
+ for (i=0; i<3; i++) {
+ clk_disable(host->clk[i]);
+ clk_put(host->clk[i]);
+ }
+
+ err_free_mem_region:
+ release_mem_region(host->mem->start, RESSIZE(host->mem));
+
+ probe_free_host:
+ mmc_free_host(mmc);
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static int s3c_hsmmc_remove(struct platform_device *dev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ struct s3c_hsmmc_host *host = mmc_priv(mmc);
+ int i;
+
+ printk_debug("Removing the MMC %p | S3C host %p\n", mmc, host);
+
+ mmc_remove_host(mmc);
+
+ /* Reset the MMC controller */
+ s3c_hsmmc_reset(host, S3C_HSMMC_RESET_ALL);
+
+#ifdef CONFIG_ARCH_S3C6400
+ clk_disable(clk_get(&dev->dev, "sclk_48m"));
+#endif
+
+ /* Free only the requested clocks */
+ for (i =0; i< 3; i++) {
+ if (!PTR_ERR(host->clk[i])) {
+ clk_disable(host->clk[i]);
+ clk_put(host->clk[i]);
+ }
+ }
+
+ /* Free the IRQ and mapped memory */
+ free_irq(host->irq, host);
+ iounmap(host->base);
+ release_mem_region(host->mem->start, RESSIZE(host->mem));
+
+ del_timer_sync(&host->timer);
+
+ tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+
+#ifdef CONFIG_HSMMC_PSEUDO_SCATTERGATHER
+ kfree(host->sub_block);
+#endif
+ mmc_free_host(mmc);
+
+ return 0;
+}
+
+/* This is for the power management support */
+#ifdef CONFIG_PM
+static int s3c_hsmmc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mmc_host *mmc;
+ struct s3c_hsmmc_host *host;
+ int cnt, retval;
+
+ mmc = platform_get_drvdata(pdev);
+ host = mmc_priv(mmc);
+
+ /* Check for all the three available clocks to disable */
+ for (cnt = 0; cnt < 3; cnt++) {
+ if (!PTR_ERR(host->clk[cnt]))
+ clk_disable(host->clk[cnt]);
+ }
+
+ retval = mmc_suspend_host(mmc, state);
+
+ return retval;
+}
+
+static int s3c_hsmmc_resume(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct s3c_hsmmc_host *host;
+ int cnt, retval;
+
+ mmc = platform_get_drvdata(pdev);
+ host = mmc_priv(mmc);
+
+ /* Reenable the clocks first */
+ for (cnt = 0; cnt < 3; cnt++) {
+ if (!PTR_ERR(host->clk[cnt]))
+ clk_enable(host->clk[cnt]);
+ }
+
+ s3c_hsmmc_ios_init(host);
+
+ /*
+ * By unsafe resumes we MUST check the card state at this point, then the
+ * higher MMC-layer is probably transferring some kind of data to the
+ * block device that doesn't exist any more.
+ */
+#if defined(CONFIG_MMC_UNSAFE_RESUME)
+ if (s3c_hsmmc_readl(S3C2410_HSMMC_PRNSTS) & S3C_HSMMC_CARD_PRESENT)
+ retval = mmc_resume_host(mmc);
+ else {
+ retval = 0;
+ mmc_detect_change(mmc, msecs_to_jiffies(500));
+ }
+#else
+ retval = mmc_resume_host(mmc);
+#endif /* CONFIG_MMC_UNSAFE_RESUME */
+
+ return retval;
+}
+#else
+#define s3c_hsmmc_suspend NULL
+#define s3c_hsmmc_resume NULL
+#endif
+
+static struct platform_driver s3c_hsmmc_driver = {
+ .probe = s3c_hsmmc_probe,
+ .remove = s3c_hsmmc_remove,
+ .suspend = s3c_hsmmc_suspend,
+ .resume = s3c_hsmmc_resume,
+ .driver = {
+ .name = "s3c-hsmmc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s3c_hsmmc_drv_init(void)
+{
+ return platform_driver_register(&s3c_hsmmc_driver);
+}
+
+static void __exit s3c_hsmmc_drv_exit(void)
+{
+ platform_driver_unregister(&s3c_hsmmc_driver);
+}
+
+#ifdef CONFIG_HSMMC_PROC_DATA
+static struct proc_dir_entry *evb_resource_dump;
+
+
+static int mem_proc_read (char *buffer, char **buffer_location, off_t offset,
+ int buffer_length, int *zero, void *ptr)
+{
+ return 0;
+}
+
+
+static int mem_proc_write(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ uint i, range = 0;
+ char flag = 'l';
+ uint port = 0;
+
+#if 0
+ printk("buffers: %s\n", buffer);
+#endif
+ sscanf(buffer, "%d %c", &port, &flag);
+
+ switch (flag) {
+ case 'c':
+ printk("clean %d port\n", port);
+ for (i=0; i<7; i++) {
+ global_host[port]->rx_pkt[i] = 0;
+ global_host[port]->tx_pkt[i] = 0;
+ }
+// break;
+
+ case 'l':
+ printk("\t\t\trx\ttx\n");
+ for (i=0; i<7; i++) {
+ printk("0x%05x ~ 0x%05x\t%-7d\t%-7d\n",
+ range, 0x1000 << i,
+ global_host[port]->rx_pkt[i],
+ global_host[port]->tx_pkt[i]);
+ range = 0x1000 << i;
+
+ }
+ break;
+ }
+
+ return count;
+}
+
+int __init mem_proc_scsuh (void)
+{
+ evb_resource_dump = create_proc_entry("mmc", 0666, &proc_root);
+ evb_resource_dump->read_proc = mem_proc_read;
+ evb_resource_dump->write_proc = mem_proc_write;
+ evb_resource_dump->nlink = 1;
+ return 0;
+}
+
+module_init(mem_proc_scsuh);
+#endif
+
+module_init(s3c_hsmmc_drv_init);
+module_exit(s3c_hsmmc_drv_exit);
+
+
+MODULE_DESCRIPTION("S3C SD HOST I/F 1.0 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/s3c-hsmmc.h b/drivers/mmc/host/s3c-hsmmc.h
new file mode 100644
index 000000000000..ce12f2ebf619
--- /dev/null
+++ b/drivers/mmc/host/s3c-hsmmc.h
@@ -0,0 +1,237 @@
+/*
+ * linux/drivers/mmc/s3c-hsmmc.h - Samsung S3C SDI Interface driver
+ *
+ * $Id: s3c-hsmmc.h,v 1.9 2007/06/07 07:14:57 scsuh Exp $
+ *
+ * Copyright (C) 2004 Thomas Kleffel, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef MHZ
+#define MHZ (1000*1000)
+#endif
+
+#define s3c_hsmmc_readl(x) readl((host->base)+(x))
+#define s3c_hsmmc_readw(x) readw((host->base)+(x))
+#define s3c_hsmmc_readb(x) readb((host->base)+(x))
+
+#define s3c_hsmmc_writel(v,x) writel((v),(host->base)+(x))
+#define s3c_hsmmc_writew(v,x) writew((v),(host->base)+(x))
+#define s3c_hsmmc_writeb(v,x) writeb((v),(host->base)+(x))
+
+#define S3C_HSMMC_CLOCK_ON 1
+#define S3C_HSMMC_CLOCK_OFF 0
+
+#define SPEED_NORMAL 0
+#define SPEED_HIGH 1
+
+/*
+ * Controller registers
+ */
+
+//S3C_HSMMC_BLOCK_SIZE: 0x04
+#define S3C_HSMMC_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+//S3C_HSMMC_TRANSFER_MODE: 0x0C
+#define S3C_HSMMC_TRNS_DMA 0x01
+#define S3C_HSMMC_TRNS_BLK_CNT_EN 0x02
+#define S3C_HSMMC_TRNS_ACMD12 0x04
+#define S3C_HSMMC_TRNS_READ 0x10
+#define S3C_HSMMC_TRNS_MULTI 0x20
+
+//S3C_HSMMC_COMMAND 0x0E
+#define S3C_HSMMC_CMD_RESP_MASK 0x03
+#define S3C_HSMMC_CMD_CRC 0x08
+#define S3C_HSMMC_CMD_INDEX 0x10
+#define S3C_HSMMC_CMD_DATA 0x20
+
+#define S3C_HSMMC_CMD_RESP_NONE 0x00
+#define S3C_HSMMC_CMD_RESP_LONG 0x01
+#define S3C_HSMMC_CMD_RESP_SHORT 0x02
+#define S3C_HSMMC_CMD_RESP_SHORT_BUSY 0x03
+
+#define S3C_HSMMC_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
+
+//S3C_HSMMC_PRESENT_STATE 0x24
+#define S3C_HSMMC_CMD_INHIBIT 0x00000001
+#define S3C_HSMMC_DATA_INHIBIT 0x00000002
+#define S3C_HSMMC_DOING_WRITE 0x00000100
+#define S3C_HSMMC_DOING_READ 0x00000200
+#define S3C_HSMMC_SPACE_AVAILABLE 0x00000400
+#define S3C_HSMMC_DATA_AVAILABLE 0x00000800
+#define S3C_HSMMC_CARD_PRESENT 0x00010000
+#define S3C_HSMMC_WRITE_PROTECT 0x00080000
+
+//S3C_HSMMC_HOST_CONTROL 0x28
+#define S3C_HSMMC_CTRL_LED (0x01)
+#define S3C_HSMMC_CTRL_4BITBUS (0x02)
+#define S3C_HSMMC_CTRL_HIGHSPEED (0x04)
+#define S3C_HSMMC_CTRL_4BIT (0x02)
+#define S3C_HSMMC_CTRL_8BIT (0x20)
+
+//S3C_HSMMC_POWER_CONTROL 0x29
+#define S3C_HSMMC_POWER_OFF 0x00
+#define S3C_HSMMC_POWER_ON 0x01
+#define S3C_HSMMC_POWER_180 0x0A
+#define S3C_HSMMC_POWER_300 0x0C
+#define S3C_HSMMC_POWER_330 0x0E
+#define S3C_HSMMC_POWER_ON_ALL 0xFF
+
+//S3C_HSMMC_CLOCK_CONTROL 0x2C
+#define S3C_HSMMC_DIVIDER_SHIFT 8
+#define S3C_HSMMC_CLOCK_EXT_STABLE 0x0008
+#define S3C_HSMMC_CLOCK_CARD_EN 0x0004
+#define S3C_HSMMC_CLOCK_INT_STABLE 0x0002
+#define S3C_HSMMC_CLOCK_INT_EN 0x0001
+
+//S3C_HSMMC_TIMEOUT_CONTROL 0x2E
+#define S3C_HSMMC_TIMEOUT_MAX 0x0E
+
+//S3C_HSMMC_SOFTWARE_RESET 0x2F
+#define S3C_HSMMC_RESET_ALL 0x01
+#define S3C_HSMMC_RESET_CMD 0x02
+#define S3C_HSMMC_RESET_DATA 0x04
+
+//S3C_HSMMC_INT_STATUS 0x30
+#define S3C_HSMMC_NIS_ERR 0x00008000
+#define S3C_HSMMC_NIS_CMDCMP 0x00000001
+#define S3C_HSMMC_NIS_TRSCMP 0x00000002
+#define S3C_HSMMC_NIS_DMA 0x00000008
+
+//S3C_HSMMC_EIS_STATUS 0x32
+#define S3C_HSMMC_EIS_CMDTIMEOUT 0x00000001
+#define S3C_HSMMC_EIS_CMDERR 0x0000000E
+#define S3C_HSMMC_EIS_DATATIMEOUT 0x00000010
+#define S3C_HSMMC_EIS_DATAERR 0x00000060
+#define S3C_HSMMC_EIS_CMD12ERR 0x00000100
+
+
+//S3C_HSMMC_SIGNAL_ENABLE 0x38
+#define S3C_HSMMC_INT_MASK_ALL 0x0000
+#define S3C_HSMMC_INT_RESPONSE 0x00000001
+#define S3C_HSMMC_INT_DATA_END 0x00000002
+#define S3C_HSMMC_INT_DMA_END 0x00000008
+#define S3C_HSMMC_INT_SPACE_AVAIL 0x00000010
+#define S3C_HSMMC_INT_DATA_AVAIL 0x00000020
+#define S3C_HSMMC_INT_CARD_INSERT 0x00000040
+#define S3C_HSMMC_INT_CARD_REMOVE 0x00000080
+#define S3C_HSMMC_INT_CARD_CHANGE 0x000000c0 /* oring of above two */
+#define S3C_HSMMC_INT_CARD_INT 0x00000100
+#define S3C_HSMMC_INT_TIMEOUT 0x00010000
+#define S3C_HSMMC_INT_CRC 0x00020000
+#define S3C_HSMMC_INT_END_BIT 0x00040000
+#define S3C_HSMMC_INT_INDEX 0x00080000
+#define S3C_HSMMC_INT_DATA_TIMEOUT 0x00100000
+#define S3C_HSMMC_INT_DATA_CRC 0x00200000
+#define S3C_HSMMC_INT_DATA_END_BIT 0x00400000
+#define S3C_HSMMC_INT_BUS_POWER 0x00800000
+#define S3C_HSMMC_INT_ACMD12ERR 0x01000000
+
+#define S3C_HSMMC_INT_NORMAL_MASK 0x00007FFF
+#define S3C_HSMMC_INT_ERROR_MASK 0xFFFF8000
+
+#define S3C_HSMMC_INT_CMD_MASK (S3C_HSMMC_INT_RESPONSE | \
+ S3C_HSMMC_INT_TIMEOUT | \
+ S3C_HSMMC_INT_CRC | \
+ S3C_HSMMC_INT_END_BIT | \
+ S3C_HSMMC_INT_INDEX | \
+ S3C_HSMMC_NIS_ERR \
+ )
+#define S3C_HSMMC_INT_DATA_MASK (S3C_HSMMC_INT_DATA_END | \
+ S3C_HSMMC_INT_DMA_END | \
+ S3C_HSMMC_INT_DATA_AVAIL | \
+ S3C_HSMMC_INT_SPACE_AVAIL | \
+ S3C_HSMMC_INT_DATA_TIMEOUT | \
+ S3C_HSMMC_INT_DATA_CRC | \
+ S3C_HSMMC_INT_DATA_END_BIT \
+ )
+
+//S3C_HSMMC_CAPABILITIES 0x40
+#define S3C_HSMMC_TIMEOUT_CLK_MASK 0x0000003F
+#define S3C_HSMMC_TIMEOUT_CLK_SHIFT 0
+#define S3C_HSMMC_TIMEOUT_CLK_UNIT 0x00000080
+#define S3C_HSMMC_CLOCK_BASE_MASK 0x00003F00
+#define S3C_HSMMC_CLOCK_BASE_SHIFT 8
+#define S3C_HSMMC_MAX_BLOCK_MASK 0x00030000
+#define S3C_HSMMC_MAX_BLOCK_SHIFT 16
+#define S3C_HSMMC_CAN_DO_DMA 0x00400000
+#define S3C_HSMMC_CAN_VDD_330 0x01000000
+#define S3C_HSMMC_CAN_VDD_300 0x02000000
+#define S3C_HSMMC_CAN_VDD_180 0x04000000
+
+//S3C_HSMMC_HOST_VERSION 0xFE
+#define S3C_HSMMC_VENDOR_VER_MASK 0xFF00
+#define S3C_HSMMC_VENDOR_VER_SHIFT 8
+#define S3C_HSMMC_SPEC_VER_MASK 0x00FF
+#define S3C_HSMMC_SPEC_VER_SHIFT 0
+
+#ifndef CONFIG_S3C_HSMMC_MAX_HW_SEGS
+#define CONFIG_S3C_HSMMC_MAX_HW_SEGS 32
+#endif
+
+struct s3c_hsmmc_dma_blk {
+ dma_addr_t dma_address; /* dma address */
+ uint length; /* length */
+ uint boundary; /* Host DMA Buffer Boundary */
+ void *original;
+};
+
+struct s3c_hsmmc_host {
+ void __iomem *base;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct mmc_host *mmc;
+ struct clk *clk[3];
+ struct resource *mem;
+
+ struct timer_list timer;
+
+ struct s3c_hsmmc_cfg *plat_data;
+
+ int irq;
+ spinlock_t lock;
+
+ struct tasklet_struct card_tasklet; /* Tasklet structures */
+ struct tasklet_struct finish_tasklet;
+
+ unsigned int clock; /* Current clock (MHz) */
+ unsigned int ctrl3[2]; /* 0: normal, 1: high */
+
+#define S3C_HSMMC_USE_DMA (1<<0)
+ int flags; /* Host attributes */
+
+ uint dma_dir;
+#ifdef CONFIG_HSMMC_PSEUDO_SCATTERGATHER
+ uint sg_len; /* size of scatter list */
+ uint dma_blk; /* total dmablk number */
+ uint next_blk; /* next block to send */
+ struct s3c_hsmmc_dma_blk dblk[CONFIG_S3C_HSMMC_MAX_HW_SEGS*4];
+
+ /* when pseudo algo cannot deal with sglist */
+#define S3C_HSMMC_MALLOC_SIZE PAGE_SIZE
+#define S3C_HSMMC_MAX_SUB_BUF CONFIG_S3C_HSMMC_MAX_HW_SEGS
+ void *sub_block[S3C_HSMMC_MAX_SUB_BUF];
+#endif
+
+#ifdef CONFIG_HSMMC_PROC_DATA
+ unsigned int rx_pkt[7]; /* 0 ~ 0x1000 */
+ /* 0x1000 ~ 0x2000 */
+ /* 0x2000 ~ 0x3000 */
+ /* 0x3000 ~ 0x4000 */
+ /* 0x4000 ~ 0x8000 */
+ /* 0x8000 ~ 0x10000 */
+ /* 0x10000 ~ */
+ unsigned int tx_pkt[7]; /* 0 ~ 0x1000 */
+ /* 0x1000 ~ 0x2000 */
+ /* 0x2000 ~ 0x3000 */
+ /* 0x3000 ~ 0x4000 */
+ /* 0x4000 ~ 0x8000 */
+ /* 0x8000 ~ 0x10000 */
+ /* 0x10000 ~ */
+#endif
+};
+
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 3b2085b57769..5287be0d590a 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -3,9 +3,6 @@
*
* Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
*
- * Current driver maintained by Ben Dooks and Simtec Electronics
- * Copyright (C) 2008 Simtec Electronics <ben-linux@fluff.org>
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
@@ -16,14 +13,17 @@
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/platform_device.h>
-#include <linux/cpufreq.h>
#include <linux/irq.h>
#include <linux/io.h>
-
+#include <linux/mmc/mmc.h>
#include <asm/dma.h>
+/* Use the old headers system */
#include <mach/regs-sdi.h>
#include <mach/regs-gpio.h>
+#include <mach/gpio.h>
+#include <asm/delay.h>
+#include <linux/delay.h>
#include <asm/plat-s3c24xx/mci.h>
@@ -31,6 +31,22 @@
#define DRIVER_NAME "s3c-mci"
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] s3c2443-sdi: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "s3c2443-sdi: " fmt, ## args)
+#define printk_dbg(fmt, args...) printk(KERN_DEBUG "s3c2443-sdi: " fmt, ## args)
+
+/* Enable/disable the debug messages */
+#if 0
+#define S3C2443_SDI_DEBUG
+#endif
+
+#ifdef S3C2443_SDI_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "s3c2443-sdi: %s() " fmt, __FUNCTION__ , ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+
enum dbg_channels {
dbg_err = (1 << 0),
dbg_debug = (1 << 1),
@@ -43,9 +59,9 @@ enum dbg_channels {
dbg_conf = (1 << 8),
};
-static const int dbgmap_err = dbg_fail;
+static const int dbgmap_err = dbg_err | dbg_fail;
static const int dbgmap_info = dbg_info | dbg_conf;
-static const int dbgmap_debug = dbg_err | dbg_debug;
+static const int dbgmap_debug = dbg_debug;
#define dbg(host, channels, args...) \
do { \
@@ -189,11 +205,17 @@ static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
static inline void clear_imask(struct s3cmci_host *host)
{
- writel(0, host->base + host->sdiimsk);
+ ulong imsk, to_write = 0;
+
+ imsk = readl(host->base + host->sdiimsk);
+ if ((imsk & S3C2410_SDIIMSK_SDIOIRQ) && host->sdio_irq)
+ to_write = S3C2410_SDIIMSK_SDIOIRQ;
+
+ writel(to_write, host->base + host->sdiimsk);
}
static inline int get_data_buffer(struct s3cmci_host *host,
- u32 *bytes, u32 **pointer)
+ u32 *words, u32 **pointer)
{
struct scatterlist *sg;
@@ -210,7 +232,7 @@ static inline int get_data_buffer(struct s3cmci_host *host,
}
sg = &host->mrq->data->sg[host->pio_sgptr];
- *bytes = sg->length;
+ *words = sg->length >> 2;
*pointer = sg_virt(sg);
host->pio_sgptr++;
@@ -226,7 +248,7 @@ static inline u32 fifo_count(struct s3cmci_host *host)
u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
fifostat &= S3C2410_SDIFSTA_COUNTMASK;
- return fifostat;
+ return fifostat >> 2;
}
static inline u32 fifo_free(struct s3cmci_host *host)
@@ -234,15 +256,13 @@ static inline u32 fifo_free(struct s3cmci_host *host)
u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
fifostat &= S3C2410_SDIFSTA_COUNTMASK;
- return 63 - fifostat;
+ return (63 - fifostat) >> 2;
}
static void do_pio_read(struct s3cmci_host *host)
{
int res;
u32 fifo;
- u32 *ptr;
- u32 fifo_words;
void __iomem *from_ptr;
/* write real prescaler to host, it might be set slow to fix */
@@ -273,35 +293,14 @@ static void do_pio_read(struct s3cmci_host *host)
fifo, host->pio_bytes,
readl(host->base + S3C2410_SDIDCNT));
- /* If we have reached the end of the block, we can
- * read a word and get 1 to 3 bytes. If we in the
- * middle of the block, we have to read full words,
- * otherwise we will write garbage, so round down to
- * an even multiple of 4. */
- if (fifo >= host->pio_bytes)
+ if (fifo > host->pio_bytes)
fifo = host->pio_bytes;
- else
- fifo -= fifo & 3;
host->pio_bytes -= fifo;
host->pio_count += fifo;
- fifo_words = fifo >> 2;
- ptr = host->pio_ptr;
- while (fifo_words--)
- *ptr++ = readl(from_ptr);
- host->pio_ptr = ptr;
-
- if (fifo & 3) {
- u32 n = fifo & 3;
- u32 data = readl(from_ptr);
- u8 *p = (u8 *)host->pio_ptr;
-
- while (n--) {
- *p++ = data;
- data >>= 8;
- }
- }
+ while (fifo--)
+ *(host->pio_ptr++) = readl(from_ptr);
}
if (!host->pio_bytes) {
@@ -325,7 +324,6 @@ static void do_pio_write(struct s3cmci_host *host)
void __iomem *to_ptr;
int res;
u32 fifo;
- u32 *ptr;
to_ptr = host->base + host->sdidata;
@@ -347,23 +345,14 @@ static void do_pio_write(struct s3cmci_host *host)
}
- /* If we have reached the end of the block, we have to
- * write exactly the remaining number of bytes. If we
- * in the middle of the block, we have to write full
- * words, so round down to an even multiple of 4. */
- if (fifo >= host->pio_bytes)
+ if (fifo > host->pio_bytes)
fifo = host->pio_bytes;
- else
- fifo -= fifo & 3;
host->pio_bytes -= fifo;
host->pio_count += fifo;
- fifo = (fifo + 3) >> 2;
- ptr = host->pio_ptr;
while (fifo--)
- writel(*ptr++, to_ptr);
- host->pio_ptr = ptr;
+ writel(*(host->pio_ptr++), to_ptr);
}
enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
@@ -399,6 +388,17 @@ static void pio_tasklet(unsigned long data)
enable_irq(host->irq);
}
+/* The DMA-callback will call this function by errors or transfer completes */
+static void dma_tasklet(unsigned long data)
+{
+ struct s3cmci_host *host = (struct s3cmci_host *) data;
+
+ if (host->complete_what == COMPLETION_FINALIZE) {
+ clear_imask(host);
+ finalize_request(host);
+ }
+}
+
/*
* ISR for SDI Interface IRQ
* Communication between driver and ISR works as follows:
@@ -444,6 +444,15 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id)
mci_cclear = 0;
mci_dclear = 0;
+ printk_debug("IRQ: cmd 0x%08x | dsta 0x%08x | imsk 0x%08x\n",
+ mci_csta, mci_dsta, mci_imsk);
+
+ if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) {
+ printk_debug("SDIO IRQ detected\n");
+ mmc_signal_sdio_irq(host->mmc);
+ writel(S3C2410_SDIDSTA_SDIOIRQDETECT, host->base + S3C2410_SDIDSTA);
+ }
+
if ((host->complete_what == COMPLETION_NONE) ||
(host->complete_what == COMPLETION_FINALIZE)) {
host->status = "nothing to complete";
@@ -487,19 +496,22 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id)
}
if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
- dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n");
+ dbg(host, dbg_debug, "CMDSTAT: error CMDTIMEOUT\n");
cmd->error = -ETIMEDOUT;
host->status = "error: command timeout";
goto fail_transfer;
}
if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {
+
+ /* @FIXME: Move the write-command to a correct place! (Luis G.) */
+ mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
+ writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
+
if (host->complete_what == COMPLETION_CMDSENT) {
host->status = "ok: command sent";
goto close_transfer;
}
-
- mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
}
if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
@@ -603,8 +615,22 @@ close_transfer:
host->complete_what = COMPLETION_FINALIZE;
clear_imask(host);
- tasklet_schedule(&host->pio_tasklet);
+ /*
+ * If we have received an interrupt although we are waiting for the
+ * DMA-callback (cmd->data), then something went wrong with the last
+ * transfer. This happens when the card is removed before
+ * the card initialization was completed.
+ * (Luis Galdos)
+ */
+ if (cmd->data && host->dodma && !cmd->data->error && !cmd->error) {
+ host->dma_complete = 1;
+ cmd->error = cmd->data->error = -EILSEQ;
+ printk_debug("[SD] Waiting DMA (CMD %08x | DAT %08x)\n",
+ mci_csta, mci_dsta);
+ }
+
+ tasklet_schedule(&host->pio_tasklet);
goto irq_out;
irq_out:
@@ -621,13 +647,26 @@ irq_out:
* ISR for the CardDetect Pin
*/
+static int s3cmci_card_present(struct mmc_host *mmc);
+
static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
{
struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
-
- dbg(host, dbg_irq, "card detect\n");
-
- mmc_detect_change(host->mmc, msecs_to_jiffies(500));
+ int val;
+ static int card_present;
+
+ /*
+ * Get the current status of the GPIO for checking if the card state has
+ * really changed since the last interrupt. Otherwise it will generates
+ * more than one interrupt for the same state
+ * Luis Galdos
+ */
+ val = s3cmci_card_present(host->mmc);
+ if (val != card_present) {
+ dbg(host, dbg_debug, "Card detect IRQ: %s\n", val ? "insert" : "remove");
+ card_present = val;
+ mmc_detect_change(host->mmc, msecs_to_jiffies(500));
+ }
return IRQ_HANDLED;
}
@@ -652,29 +691,49 @@ static void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch,
spin_lock_irqsave(&host->complete_lock, iflags);
if (result != S3C2410_RES_OK) {
- dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
- "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
- mci_csta, mci_dsta, mci_fsta,
- mci_dcnt, result, host->dmatogo);
+ struct mmc_request *mrq = host->mrq;
+
+ if (mrq) {
+ struct mmc_command *cmd;
+
+ cmd = mrq->cmd;
+ dbg(host, dbg_fail, "DMA FAILED: CMD%i csta=0x%08x dsta=0x%08x "
+ "fsta=0x%08x dcnt:0x%08x result:%i toGo:%u\n",
+ cmd->opcode, mci_csta, mci_dsta, mci_fsta,
+ mci_dcnt, result, host->dmatogo);
+ } else
+ dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
+ "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
+ mci_csta, mci_dsta, mci_fsta,
+ mci_dcnt, result, host->dmatogo);
goto fail_request;
}
host->dmatogo--;
if (host->dmatogo) {
- dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] "
- "DCNT:[%08x] toGo:%u\n",
- size, mci_dsta, mci_dcnt, host->dmatogo);
+ printk_debug("DMA DONE Size:%i DSTA:[%08x] "
+ "DCNT:[%08x] toGo:%u\n",
+ size, mci_dsta, mci_dcnt, host->dmatogo);
- goto out;
+ if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {
+ printk_err("SDI has no more data, but DMA waits for more?\n");
+ goto fail_request;
+ }
+
+ spin_unlock_irqrestore(&host->complete_lock, iflags);
+ return;
}
- dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
- size, mci_dsta, mci_dcnt);
+ printk_debug("DMA ENDE Size:%i DSTA:[%08x] DCNT:[%08x]\n",
+ size, mci_dsta, mci_dcnt);
+ host->dma_complete = 1;
host->complete_what = COMPLETION_FINALIZE;
out:
+ /* @FIXME: Check the performance modification through this delay */
+ udelay(10);
tasklet_schedule(&host->pio_tasklet);
spin_unlock_irqrestore(&host->complete_lock, iflags);
return;
@@ -693,17 +752,22 @@ static void finalize_request(struct s3cmci_host *host)
struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
int debug_as_failure = 0;
- if (host->complete_what != COMPLETION_FINALIZE)
- return;
+ spin_lock(&host->complete_lock);
+
+ if (host->complete_what != COMPLETION_FINALIZE) {
+ printk_debug("Nothing to complete!\n");
+ goto exit_unlock;
+ }
- if (!mrq)
- return;
+ if (!mrq) {
+ printk_err("Empty MMC request found!\n");
+ goto exit_unlock;
+ }
- if (cmd->data && (cmd->error == 0) &&
- (cmd->data->error == 0)) {
+ if (cmd->data && (cmd->error == 0) && (cmd->data->error == 0)) {
if (host->dodma && (!host->dma_complete)) {
- dbg(host, dbg_dma, "DMA Missing!\n");
- return;
+ printk_err("DMA complete missing (%i)\n", host->dma_complete);
+ goto exit_unlock;
}
}
@@ -725,17 +789,19 @@ static void finalize_request(struct s3cmci_host *host)
/* Cleanup controller */
writel(0, host->base + S3C2410_SDICMDARG);
- writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
writel(0, host->base + S3C2410_SDICMDCON);
- writel(0, host->base + host->sdiimsk);
-
+ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
+ if (!host->sdio_irq) {
+ writel(0, host->base + host->sdiimsk);
+ }
+
if (cmd->data && cmd->error)
cmd->data->error = cmd->error;
if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) {
host->cmd_is_stop = 1;
s3cmci_send_request(host->mmc);
- return;
+ goto exit_unlock;
}
/* If we have no data transfer we are finished here */
@@ -776,28 +842,38 @@ request_done:
host->complete_what = COMPLETION_NONE;
host->mrq = NULL;
mmc_request_done(host->mmc, mrq);
+
+ exit_unlock:
+ spin_unlock(&host->complete_lock);
+
}
static void s3cmci_dma_setup(struct s3cmci_host *host,
enum s3c2410_dmasrc source)
{
static enum s3c2410_dmasrc last_source = -1;
- static int setup_ok;
+ static int setup_ok = 0;
if (last_source == source)
return;
last_source = source;
- s3c2410_dma_devconfig(host->dma, source, 3,
+ s3c2410_dma_devconfig(host->dma, source,
+ S3C2410_DISRCC_INC | S3C2410_DISRCC_APB,
host->mem->start + host->sdidata);
if (!setup_ok) {
- s3c2410_dma_config(host->dma, 4,
- (S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI));
+ printk_debug("Setting up the DMA channel!\n");
+ s3c2410_dma_config(host->dma,
+ 4,
+ S3C2410_DCON_HANDSHAKE |
+ S3C2410_DCON_SYNC_PCLK |
+ S3C2410_DCON_HWTRIG);
+
s3c2410_dma_set_buffdone_fn(host->dma,
s3cmci_dma_done_callback);
- s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
setup_ok = 1;
}
}
@@ -849,10 +925,11 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
/* We cannot deal with unaligned blocks with more than
* one block being transfered. */
- if (data->blocks > 1) {
- pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz);
+ if (data->blocks > 1)
return -EINVAL;
- }
+
+ /* No support yet for non-word block transfers. */
+ return -EINVAL;
}
while (readl(host->base + S3C2410_SDIDSTA) &
@@ -899,12 +976,15 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
writel(dcon, host->base + S3C2410_SDIDCON);
/* write BSIZE register */
-
writel(data->blksz, host->base + S3C2410_SDIBSIZE);
/* add to IMASK register */
imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
- S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
+ S3C2410_SDIIMSK_DATATIMEOUT;
+
+ /* By DMA-support we will use the DMA-callback for the transfer-complete */
+ if (!host->dodma)
+ imsk |= S3C2410_SDIIMSK_DATAFINISH;
enable_imask(host, imsk);
@@ -951,25 +1031,31 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
{
int dma_len, i;
int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
-
+ int retval;
+
BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
- s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);
+ printk_debug("New DMA transfer | %i Blks | %i Blksz\n",
+ data->blocks, data->blksz);
+
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+ s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);
dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
(rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (dma_len == 0)
- return -ENOMEM;
+ if (dma_len == 0) {
+ printk_err("dma_map_sg failed, no memory available?\n");
+ retval = -ENOMEM;
+ goto exit_all;
+ }
- host->dma_complete = 0;
host->dmatogo = dma_len;
for (i = 0; i < dma_len; i++) {
int res;
- dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
+ printk_debug("Enqueue %i @ 0x%08x | %u bytes\n", i,
sg_dma_address(&data->sg[i]),
sg_dma_len(&data->sg[i]));
@@ -979,13 +1065,31 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
if (res) {
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
- return -EBUSY;
+ printk_err("Couldn't enqueue a DMA-buffer\n");
+ retval = -EBUSY;
+ goto exit_all;
}
}
+ /*
+ * Disable the data counter interrupt, then the DMA-callback will be
+ * responsible for finalizing the request
+ */
+ disable_imask(host, S3C2410_SDIIMSK_DATAFINISH |
+ S3C2410_SDIIMSK_RXFIFOHALF |
+ S3C2410_SDIIMSK_RXFIFOFULL |
+ S3C2410_SDIIMSK_RXFIFOLAST |
+ S3C2410_SDIIMSK_TXFIFOEMPTY |
+ S3C2410_SDIIMSK_TXFIFOHALF);
+
+ host->dma_complete = 0;
s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
- return 0;
+ retval = 0;
+
+ exit_all:
+
+ return retval;
}
static void s3cmci_send_request(struct mmc_host *mmc)
@@ -997,6 +1101,10 @@ static void s3cmci_send_request(struct mmc_host *mmc)
host->ccnt++;
prepare_dbgmsg(host, cmd, host->cmd_is_stop);
+ /* @XXX: Sending the switch command with data leads to a DMA-failure, why? */
+ if (cmd->opcode == MMC_SWITCH && cmd->data)
+ udelay(200);
+
/* Clear command, data and fifo status registers
Fifo clear only necessary on 2440, but doesn't hurt on 2410
*/
@@ -1007,6 +1115,11 @@ static void s3cmci_send_request(struct mmc_host *mmc)
if (cmd->data) {
int res = s3cmci_setup_data(host, cmd->data);
+ printk_debug("CMD%u: %s transfer | %i blks | %u blksz\n",
+ cmd->opcode,
+ (cmd->data->flags & MMC_DATA_READ) ? "RX" : "TX",
+ cmd->data->blocks, cmd->data->blksz);
+
host->dcnt++;
if (res) {
@@ -1033,11 +1146,28 @@ static void s3cmci_send_request(struct mmc_host *mmc)
}
}
+ /* Enable Interrupt */
+ if (!host->dodma)
+ enable_irq(host->irq);
+
/* Send command */
s3cmci_send_command(host, cmd);
- /* Enable Interrupt */
- enable_irq(host->irq);
+ /*
+ * If the host supports DMA, then disable the data finish interrupts and
+ * the FIFO-interrupts, then the DMA-controller will handle the data
+ * transfer and will finish the transfer if no errors ocurrs
+ * @XXX: This is probably not the correct place for this operation
+ * (Luis Galdos)
+ */
+ if (cmd->data && host->dodma)
+ disable_imask(host, S3C2410_SDIIMSK_DATAFINISH |
+ S3C2410_SDIIMSK_RXFIFOHALF |
+ S3C2410_SDIIMSK_RXFIFOFULL |
+ S3C2410_SDIIMSK_RXFIFOLAST |
+ S3C2410_SDIIMSK_TXFIFOEMPTY |
+ S3C2410_SDIIMSK_TXFIFOHALF);
+
}
static int s3cmci_card_present(struct mmc_host *mmc)
@@ -1062,40 +1192,18 @@ static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq;
if (s3cmci_card_present(mmc) == 0) {
- dbg(host, dbg_err, "%s: no medium present\n", __func__);
+ dbg(host, dbg_debug, "%s: no medium present\n", __func__);
host->mrq->cmd->error = -ENOMEDIUM;
mmc_request_done(mmc, mrq);
} else
s3cmci_send_request(mmc);
}
-static void s3cmci_set_clk(struct s3cmci_host *host, struct mmc_ios *ios)
-{
- u32 mci_psc;
-
- /* Set clock */
- for (mci_psc = 0; mci_psc < 255; mci_psc++) {
- host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
-
- if (host->real_rate <= ios->clock)
- break;
- }
-
- if (mci_psc > 255)
- mci_psc = 255;
-
- host->prescaler = mci_psc;
- writel(host->prescaler, host->base + S3C2410_SDIPRE);
-
- /* If requested clock is 0, real_rate will be 0, too */
- if (ios->clock == 0)
- host->real_rate = 0;
-}
-
static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct s3cmci_host *host = mmc_priv(mmc);
- u32 mci_con;
+ u32 mci_psc, mci_con;
+ int waitclk;
/* Set the power state */
@@ -1104,13 +1212,6 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
switch (ios->power_mode) {
case MMC_POWER_ON:
case MMC_POWER_UP:
- s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
- s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
- s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
- s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
- s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
- s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
-
if (host->pdata->set_power)
host->pdata->set_power(ios->power_mode, ios->vdd);
@@ -1121,9 +1222,6 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
case MMC_POWER_OFF:
default:
- s3c2410_gpio_setpin(S3C2410_GPE5, 0);
- s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
-
if (host->is2440)
mci_con |= S3C2440_SDICON_SDRESET;
@@ -1133,8 +1231,6 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
break;
}
- s3cmci_set_clk(host, ios);
-
/* Set CLOCK_ENABLE */
if (ios->clock)
mci_con |= S3C2410_SDICON_CLOCKTYPE;
@@ -1143,12 +1239,47 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
writel(mci_con, host->base + S3C2410_SDICON);
+
+ /* Set clock */
+ for (mci_psc = 0; mci_psc < 255; mci_psc++) {
+ host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
+
+ if (host->real_rate <= ios->clock)
+ break;
+ }
+
+ if (mci_psc > 255)
+ mci_psc = 255;
+
+
+ /* If requested clock is 0, real_rate will be 0, too */
+ if (ios->clock == 0)
+ host->real_rate = 0;
+
+ host->prescaler = mci_psc;
+ writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+ /*
+ * According to the data sheet first configure the register SDICON and
+ * wait at least 74 SDCLK before initializing the SD card.
+ * (Luis Galdos)
+ */
+ waitclk = 1;
+ if (host->real_rate)
+ waitclk = 100 * (1000000 / host->real_rate);
+
+ if (waitclk > 1000)
+ mdelay(waitclk / 1000);
+ else
+ udelay(waitclk);
+
+
if ((ios->power_mode == MMC_POWER_ON) ||
(ios->power_mode == MMC_POWER_UP)) {
- dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",
+ dbg(host, dbg_debug, "running at %lukHz (requested: %ukHz).\n",
host->real_rate/1000, ios->clock/1000);
} else {
- dbg(host, dbg_conf, "powered down.\n");
+ dbg(host, dbg_debug, "powered down.\n");
}
host->bus_width = ios->bus_width;
@@ -1179,11 +1310,42 @@ static int s3cmci_get_ro(struct mmc_host *mmc)
return ret;
}
+/* Here only mask or unmask the SDIO interrupt */
+static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct s3cmci_host *host;
+ ulong con, imsk;
+
+ if (!(host = mmc_priv(mmc))) {
+ printk_err("NULL host pointer found!\n");
+ return;
+ }
+
+ printk_debug("%s SDIO IRQ\n", enable ? "Enabling" : "Disabling");
+
+ con = readl(host->base + S3C2410_SDICON);
+ imsk = readl(host->base + host->sdiimsk);
+
+ if (enable) {
+ host->sdio_irq = 1;
+ imsk |= S3C2410_SDIIMSK_SDIOIRQ;
+ con |= S3C2410_SDICON_SDIOIRQ;
+ } else {
+ host->sdio_irq = 0;
+
+ imsk |= S3C2410_SDIIMSK_SDIOIRQ;
+ con &= ~S3C2410_SDICON_SDIOIRQ;
+ }
+
+ writel(con, host->base + S3C2410_SDICON);
+ writel(imsk, host->base + host->sdiimsk);
+}
+
static struct mmc_host_ops s3cmci_ops = {
- .request = s3cmci_request,
- .set_ios = s3cmci_set_ios,
- .get_ro = s3cmci_get_ro,
- .get_cd = s3cmci_card_present,
+ .request = s3cmci_request,
+ .set_ios = s3cmci_set_ios,
+ .get_ro = s3cmci_get_ro,
+ .enable_sdio_irq = s3cmci_enable_sdio_irq,
};
static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
@@ -1191,61 +1353,6 @@ static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
* checks. Any zero fields to ensure reaonable defaults are picked. */
};
-#ifdef CONFIG_CPU_FREQ
-
-static int s3cmci_cpufreq_transition(struct notifier_block *nb,
- unsigned long val, void *data)
-{
- struct s3cmci_host *host;
- struct mmc_host *mmc;
- unsigned long newclk;
- unsigned long flags;
-
- host = container_of(nb, struct s3cmci_host, freq_transition);
- newclk = clk_get_rate(host->clk);
- mmc = host->mmc;
-
- if ((val == CPUFREQ_PRECHANGE && newclk > host->clk_rate) ||
- (val == CPUFREQ_POSTCHANGE && newclk < host->clk_rate)) {
- spin_lock_irqsave(&mmc->lock, flags);
-
- host->clk_rate = newclk;
-
- if (mmc->ios.power_mode != MMC_POWER_OFF &&
- mmc->ios.clock != 0)
- s3cmci_set_clk(host, &mmc->ios);
-
- spin_unlock_irqrestore(&mmc->lock, flags);
- }
-
- return 0;
-}
-
-static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
-{
- host->freq_transition.notifier_call = s3cmci_cpufreq_transition;
-
- return cpufreq_register_notifier(&host->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-
-static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
-{
- cpufreq_unregister_notifier(&host->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-
-#else
-static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
-{
- return 0;
-}
-
-static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
-{
-}
-#endif
-
static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
{
struct s3cmci_host *host;
@@ -1269,9 +1376,6 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
host->pdata = &s3cmci_def_pdata;
}
- spin_lock_init(&host->complete_lock);
- tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
-
if (is2440) {
host->sdiimsk = S3C2440_SDIIMSK;
host->sdidata = S3C2440_SDIDATA;
@@ -1282,12 +1386,22 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
host->clk_div = 2;
}
- host->dodma = 0;
+ /* Configure the DMA-support depending on the passed platform configuration */
+ host->dodma = (host->pdata->dma_enable) ? 1 : 0;
host->complete_what = COMPLETION_NONE;
host->pio_active = XFER_NONE;
- host->dma = S3CMCI_DMA;
+ /* DMA-channel to use (include/asm-arm/arch-s3c2410/dma.h) */
+ host->dma = DMACH_SDI;
+
+ spin_lock_init(&host->complete_lock);
+ /* Depending on the DMA-support use different tasklet-functions */
+ if (!host->dodma)
+ tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
+ else
+ tasklet_init(&host->pio_tasklet, dma_tasklet, (unsigned long) host);
+
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) {
dev_err(&pdev->dev,
@@ -1352,10 +1466,12 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect,
S3C2410_GPIO_INPUT);
- if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL) < 0) {
- dev_err(&pdev->dev, "unable to get DMA channel.\n");
- ret = -EBUSY;
- goto probe_free_irq_cd;
+ if (host->dodma) {
+ if (s3c2410_dma_request(host->dma, &s3cmci_dma_client, NULL) < 0) {
+ dev_err(&pdev->dev, "unable to get DMA channel.\n");
+ ret = -EBUSY;
+ goto probe_free_irq_cd;
+ }
}
host->clk = clk_get(&pdev->dev, "sdi");
@@ -1363,7 +1479,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
dev_err(&pdev->dev, "failed to find clock source.\n");
ret = PTR_ERR(host->clk);
host->clk = NULL;
- goto probe_free_host;
+ goto probe_free_dmach;
}
ret = clk_enable(host->clk);
@@ -1376,7 +1492,7 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
mmc->ops = &s3cmci_ops;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
mmc->f_min = host->clk_rate / (host->clk_div * 256);
mmc->f_max = host->clk_rate / host->clk_div;
@@ -1396,25 +1512,30 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
(host->is2440?"2440":""),
host->base, host->irq, host->irq_cd, host->dma);
- ret = s3cmci_cpufreq_register(host);
- if (ret) {
- dev_err(&pdev->dev, "failed to register cpufreq\n");
- goto free_dmabuf;
- }
-
ret = mmc_add_host(mmc);
if (ret) {
dev_err(&pdev->dev, "failed to add mmc host.\n");
- goto free_cpufreq;
+ goto free_dmabuf;
}
+ /* @FIXME: Get the GPIO-list from the platform-data */
+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
+ s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
+ s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
+ s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
+ s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
+ s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
+
platform_set_drvdata(pdev, mmc);
dev_info(&pdev->dev, "initialisation done.\n");
- return 0;
+ /* Enable the wakeup for this device (Luis Galdos) */
+ device_init_wakeup(&pdev->dev, 1);
+ device_set_wakeup_enable(&pdev->dev, 0);
+
+ enable_irq(host->irq);
- free_cpufreq:
- s3cmci_cpufreq_deregister(host);
+ return 0;
free_dmabuf:
clk_disable(host->clk);
@@ -1422,6 +1543,10 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
clk_free:
clk_put(host->clk);
+ probe_free_dmach:
+ if (host->dodma)
+ s3c2410_dma_free(host->dma, &s3cmci_dma_client);
+
probe_free_irq_cd:
if (host->irq_cd >= 0)
free_irq(host->irq_cd, host);
@@ -1449,11 +1574,26 @@ static void s3cmci_shutdown(struct platform_device *pdev)
if (host->irq_cd >= 0)
free_irq(host->irq_cd, host);
- s3cmci_cpufreq_deregister(host);
mmc_remove_host(mmc);
clk_disable(host->clk);
}
+/*
+ * Don't remove the MMC-host when going to shutdown the system. This can lead to some
+ * failures when the host has mounted a card with our root file system.
+ * (Luis Galdos)
+ */
+static void s3cmci_2440_shutdown(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct s3cmci_host *host = mmc_priv(mmc);
+
+ if (host->irq_cd >= 0)
+ free_irq(host->irq_cd, host);
+
+ clk_disable(host->clk);
+}
+
static int __devexit s3cmci_remove(struct platform_device *pdev)
{
struct mmc_host *mmc = platform_get_drvdata(pdev);
@@ -1464,7 +1604,10 @@ static int __devexit s3cmci_remove(struct platform_device *pdev)
clk_put(host->clk);
tasklet_disable(&host->pio_tasklet);
- s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
+
+ /* Free the DMA-channel */
+ if (host->dodma)
+ s3c2410_dma_free(host->dma, &s3cmci_dma_client);
free_irq(host->irq, host);
@@ -1492,18 +1635,94 @@ static int __devinit s3cmci_2440_probe(struct platform_device *dev)
#ifdef CONFIG_PM
-static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
+static int s3cmci_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct mmc_host *mmc = platform_get_drvdata(dev);
+ int retval;
+ struct mmc_host *mmc;
+ struct s3cmci_host *host;
+
+ mmc = platform_get_drvdata(pdev);
+ host = mmc_priv(mmc);
+
+ retval = mmc_suspend_host(mmc, state);
+ if (retval)
+ goto exit_suspend;
+
+ /* Check if the wakeup is enabled. IN that case configure it as ext wakeup */
+ if (device_may_wakeup(&pdev->dev)) {
+ struct s3c24xx_mci_pdata *pdata;
+ unsigned long detect;
+
+ pdata = host->pdata;
+
+ retval = enable_irq_wake(host->irq_cd);
+ if (retval) {
+ dev_err(&pdev->dev, "Couldn't enable wake IRQ %i\n",
+ host->irq_cd);
+ goto exit_suspend;
+ }
+
+ /*
+ * Reconfigure the card for generating the wakeup when a new
+ * card is plugged into the slot
+ */
+ detect = (pdata->detect_invert) ? (IRQF_TRIGGER_RISING) :
+ (IRQF_TRIGGER_FALLING);
+ set_irq_type(host->irq_cd, detect);
+ }
- return mmc_suspend_host(mmc, state);
+ writel(S3C2440_SDICON_SDRESET | S3C2410_SDIDCON_STOP,
+ host->base + S3C2410_SDICON);
+
+ clk_disable(host->clk);
+exit_suspend:
+ return retval;
}
-static int s3cmci_resume(struct platform_device *dev)
+static int s3cmci_resume(struct platform_device *pdev)
{
- struct mmc_host *mmc = platform_get_drvdata(dev);
+ struct s3cmci_host *host;
+ struct mmc_host *mmc;
+ int retval;
- return mmc_resume_host(mmc);
+ mmc = platform_get_drvdata(pdev);
+ host = mmc_priv(mmc);
+
+ if (device_may_wakeup(&pdev->dev)) {
+ retval = disable_irq_wake(host->irq_cd);
+ if (retval) {
+ dev_err(&pdev->dev, "Couldn't disable wake IRQ %i\n",
+ host->irq_cd);
+ goto exit_resume;
+ }
+
+ set_irq_type(host->irq_cd, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING);
+ }
+
+ clk_enable(host->clk);
+
+ /* @FIXME: Why do we need to free que DMA-channel? */
+ s3c2410_dma_free(host->dma, &s3cmci_dma_client);
+ s3c2410_dma_request(host->dma, &s3cmci_dma_client, NULL);
+
+ /*
+ * By unsafe resumes we MUST check the card state at this point, then the
+ * higher MMC-layer is probably transferring some kind of data to the
+ * block device that doesn't exist any more.
+ */
+#if defined(CONFIG_MMC_UNSAFE_RESUME)
+ if (s3cmci_card_present(mmc))
+ retval = mmc_resume_host(mmc);
+ else {
+ retval = 0;
+ mmc_detect_change(mmc, msecs_to_jiffies(500));
+ }
+#else
+ retval = mmc_resume_host(mmc);
+#endif /* CONFIG_MMC_UNSAFE_RESUME */
+
+exit_resume:
+ return retval;
}
#else /* CONFIG_PM */
@@ -1537,7 +1756,7 @@ static struct platform_driver s3cmci_2440_driver = {
.driver.owner = THIS_MODULE,
.probe = s3cmci_2440_probe,
.remove = __devexit_p(s3cmci_remove),
- .shutdown = s3cmci_shutdown,
+ .shutdown = s3cmci_2440_shutdown,
.suspend = s3cmci_suspend,
.resume = s3cmci_resume,
};
@@ -1563,7 +1782,7 @@ module_exit(s3cmci_exit);
MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>, Ben Dooks <ben-linux@fluff.org>");
+MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>");
MODULE_ALIAS("platform:s3c2410-sdi");
MODULE_ALIAS("platform:s3c2412-sdi");
MODULE_ALIAS("platform:s3c2440-sdi");
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
index ca1ba3d58cfd..25e6929a3dea 100644
--- a/drivers/mmc/host/s3cmci.h
+++ b/drivers/mmc/host/s3cmci.h
@@ -71,4 +71,6 @@ struct s3cmci_host {
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
+
+ int sdio_irq;
};
diff --git a/drivers/mmc/host/stmp3xxx_mmc.c b/drivers/mmc/host/stmp3xxx_mmc.c
new file mode 100644
index 000000000000..b5c9bc3580a4
--- /dev/null
+++ b/drivers/mmc/host/stmp3xxx_mmc.c
@@ -0,0 +1,1077 @@
+/*
+ * Copyright (C) 2007 SigmaTel, Inc., Ioannis Kappas <ikappas@sigmatel.com>
+ *
+ * Portions copyright (C) 2003 Russell King, PXA MMCI Driver
+ * Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/highmem.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/mmc/host.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <mach/regs-apbh.h>
+#include <mach/regs-ssp.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/stmp3xxx.h>
+
+#define DRIVER_NAME "stmp3xxx-mmc"
+
+#define CLOCKRATE_MIN 400000
+#define CLOCKRATE_MAX 48000000
+
+/*
+ * Card detect polling timeout
+ */
+#define STMP37XX_MMC_DETECT_TIMEOUT (HZ/2)
+
+/* Max value supported for XFER_COUNT */
+#define SSP_BUFFER_SIZE (65536 - 512)
+
+struct stmp3xxx_mmc_host {
+ struct device *dev;
+ struct mmc_host *mmc;
+
+ struct clk *clk;
+ unsigned int clkrt;
+
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ /* Whether the card is capable of 4-bit data */
+ int bus_width_4:1;
+
+ /* Whether SD card is present */
+ unsigned present:1;
+
+ /* Polling timer */
+ struct timer_list timer;
+
+ /* SSP interface which MMC/SD card slot is attached to */
+ u32 ssp_base;
+
+ /* DMA channel used for this host */
+ unsigned int dmach;
+
+ /* IRQs */
+ int dmairq, errirq;
+
+ /* DMA descriptor to transfer data over SSP interface */
+ struct stmp3xxx_dma_descriptor dma_desc;
+
+ /* DMA buffer */
+ dma_addr_t dma_buf_phys;
+ char *dma_buf;
+
+ struct completion dma_done;
+ /* status on last interrupt */
+ u32 status;
+ int read_uA, write_uA;
+ struct regulator *regulator;
+};
+
+/* Return read only state of card */
+static int stmp3xxx_mmc_get_ro(struct mmc_host *mmc)
+{
+ struct stmp3xxx_mmc_host *host = mmc_priv(mmc);
+ struct stmp3xxxmmc_platform_data *pdata = host->dev->platform_data;
+
+ if (pdata && pdata->get_wp)
+ return pdata->get_wp();
+
+ return 0;
+}
+
+/* Detect if card is plugged */
+static inline int stmp3xxx_mmc_is_plugged(struct stmp3xxx_mmc_host *host)
+{
+ u32 status = HW_SSP_STATUS_RD_NB(host->ssp_base);
+ return !(status & BM_SSP_STATUS_CARD_DETECT);
+}
+
+/* Card detection polling function */
+static void stmp3xxx_mmc_detect_poll(unsigned long arg)
+{
+ struct stmp3xxx_mmc_host *host = (struct stmp3xxx_mmc_host *)arg;
+ int card_status;
+
+ card_status = stmp3xxx_mmc_is_plugged(host);
+ if (card_status != host->present) {
+ host->present = card_status;
+ mmc_detect_change(host->mmc, 0);
+ }
+
+ mod_timer(&host->timer, jiffies + STMP37XX_MMC_DETECT_TIMEOUT);
+}
+
+#define STMP3XXX_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \
+ BM_SSP_CTRL1_RESP_ERR_IRQ | \
+ BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \
+ BM_SSP_CTRL1_DATA_TIMEOUT_IRQ | \
+ BM_SSP_CTRL1_DATA_CRC_IRQ | \
+ BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ | \
+ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \
+ BM_SSP_CTRL1_FIFO_OVERRUN_IRQ)
+
+/* SSP DMA interrupt handler */
+static irqreturn_t mmc_irq_handler(int irq, void *dev_id)
+{
+ struct stmp3xxx_mmc_host *host = dev_id;
+ u32 c1;
+
+ c1 = HW_SSP_CTRL1_RD_NB(host->ssp_base);
+ HW_SSP_CTRL1_CLR_NB(host->ssp_base, c1 & STMP3XXX_MMC_IRQ_BITS);
+ if (irq == host->dmairq)
+ stmp3xxx_dma_clear_interrupt(host->dmach);
+ host->status = HW_SSP_STATUS_RD_NB(host->ssp_base);
+
+ if (host->cmd) /* else it is a bogus interrupt */
+ complete(&host->dma_done);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Check for MMC command errors
+ * Returns error code or zerro if no errors
+ */
+static inline int stmp3xxx_mmc_cmd_error(u32 status)
+{
+ int err = 0;
+
+ if (status & BM_SSP_STATUS_TIMEOUT)
+ err = -ETIMEDOUT;
+ else if (status & BM_SSP_STATUS_RESP_TIMEOUT)
+ err = -ETIMEDOUT;
+ else if (status & BM_SSP_STATUS_RESP_CRC_ERR)
+ err = -EILSEQ;
+ else if (status & BM_SSP_STATUS_RESP_ERR)
+ err = -EIO;
+
+ return err;
+}
+
+/* Send the BC command to the device */
+static void stmp3xxx_mmc_bc(struct stmp3xxx_mmc_host *host)
+{
+ struct mmc_command *cmd = host->cmd;
+ struct stmp3xxx_dma_descriptor *dma_desc = &host->dma_desc;
+
+ dma_desc->command->cmd =
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_SEMAPHORE |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF_APBH_CHn_CMD_XFER_COUNT(0) |
+ BF_APBH_CHn_CMD_CMDWORDS(3) |
+ BF_APBH_CHn_CMD_COMMAND(0); /* NO_DMA_XFER */
+
+ dma_desc->command->pio_words[0] = BM_SSP_CTRL0_ENABLE |
+ BM_SSP_CTRL0_IGNORE_CRC;
+ dma_desc->command->pio_words[1] = BF_SSP_CMD0_CMD(cmd->opcode) |
+ BM_SSP_CMD0_APPEND_8CYC;
+ dma_desc->command->pio_words[2] = BF_SSP_CMD1_CMD_ARG(cmd->arg);
+
+ init_completion(&host->dma_done);
+ stmp3xxx_dma_reset_channel(host->dmach);
+ stmp3xxx_dma_go(host->dmach, dma_desc, 1);
+ wait_for_completion(&host->dma_done);
+
+ cmd->error = stmp3xxx_mmc_cmd_error(host->status);
+
+ if (stmp3xxx_dma_running(host->dmach))
+ dev_dbg(host->dev, "DMA command not finished\n");
+
+ if (cmd->error) {
+ dev_dbg(host->dev, "Command error 0x%x\n", cmd->error);
+ stmp3xxx_dma_reset_channel(host->dmach);
+ }
+}
+
+/* Send the ac command to the device */
+static void stmp3xxx_mmc_ac(struct stmp3xxx_mmc_host *host)
+{
+ struct mmc_command *cmd = host->cmd;
+ struct stmp3xxx_dma_descriptor *dma_desc = &host->dma_desc;
+ u32 ignore_crc, resp, long_resp;
+ u32 ssp_ctrl0;
+ u32 ssp_cmd0;
+ u32 ssp_cmd1;
+
+ ignore_crc = (mmc_resp_type(cmd) & MMC_RSP_CRC) ?
+ 0 : BM_SSP_CTRL0_IGNORE_CRC;
+ resp = (mmc_resp_type(cmd) & MMC_RSP_PRESENT) ?
+ BM_SSP_CTRL0_GET_RESP : 0;
+ long_resp = (mmc_resp_type(cmd) & MMC_RSP_136) ?
+ BM_SSP_CTRL0_LONG_RESP : 0;
+
+ dma_desc->command->cmd =
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_SEMAPHORE |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF_APBH_CHn_CMD_XFER_COUNT(0) |
+ BF_APBH_CHn_CMD_CMDWORDS(3) |
+ BF_APBH_CHn_CMD_COMMAND(0);
+
+ ssp_ctrl0 = BM_SSP_CTRL0_ENABLE | ignore_crc | long_resp | resp;
+ ssp_cmd0 = BF_SSP_CMD0_CMD(cmd->opcode);
+ ssp_cmd1 = BF_SSP_CMD1_CMD_ARG(cmd->arg);
+
+ dma_desc->command->pio_words[0] = ssp_ctrl0;
+ dma_desc->command->pio_words[1] = ssp_cmd0;
+ dma_desc->command->pio_words[2] = ssp_cmd1;
+
+ stmp3xxx_dma_reset_channel(host->dmach);
+ init_completion(&host->dma_done);
+ stmp3xxx_dma_go(host->dmach, dma_desc, 1);
+ wait_for_completion(&host->dma_done);
+
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ while (HW_SSP_CTRL0_RD_NB(host->ssp_base) & BM_SSP_CTRL0_RUN)
+ continue;
+ break;
+ case MMC_RSP_R1:
+ case MMC_RSP_R1B:
+ case MMC_RSP_R3:
+ cmd->resp[0] = HW_SSP_SDRESP0_RD_NB(host->ssp_base);
+ break;
+ case MMC_RSP_R2:
+ cmd->resp[3] = HW_SSP_SDRESP0_RD_NB(host->ssp_base);
+ cmd->resp[2] = HW_SSP_SDRESP1_RD_NB(host->ssp_base);
+ cmd->resp[1] = HW_SSP_SDRESP2_RD_NB(host->ssp_base);
+ cmd->resp[0] = HW_SSP_SDRESP3_RD_NB(host->ssp_base);
+ break;
+ default:
+ dev_warn(host->dev, "Unsupported response type 0x%x\n",
+ mmc_resp_type(cmd));
+ BUG();
+ break;
+ }
+
+ cmd->error = stmp3xxx_mmc_cmd_error(host->status);
+
+ if (stmp3xxx_dma_running(host->dmach))
+ dev_dbg(host->dev, "DMA command not finished\n");
+
+ if (cmd->error) {
+ dev_dbg(host->dev, "Command error 0x%x\n", cmd->error);
+ stmp3xxx_dma_reset_channel(host->dmach);
+ }
+}
+
+/* Copy data between sg list and dma buffer */
+static unsigned int stmp3xxx_sg_dma_copy(struct stmp3xxx_mmc_host *host,
+ unsigned int size, int to_dma)
+{
+ struct mmc_data *data = host->cmd->data;
+ unsigned int copy_size, bytes_copied = 0;
+ struct scatterlist *sg;
+ char *dmabuf = host->dma_buf;
+ char *sgbuf;
+ int len, i;
+
+ sg = data->sg;
+ len = data->sg_len;
+
+ /*
+ * Just loop through all entries. Size might not
+ * be the entire list though so make sure that
+ * we do not transfer too much.
+ */
+ for (i = 0; i < len; i++) {
+ sgbuf = kmap_atomic(sg_page(&sg[i]), KM_BIO_SRC_IRQ) +
+ sg[i].offset;
+ copy_size = size < sg[i].length ? size : sg[i].length;
+ if (to_dma)
+ memcpy(dmabuf, sgbuf, copy_size);
+ else
+ memcpy(sgbuf, dmabuf, copy_size);
+ kunmap_atomic(sgbuf, KM_BIO_SRC_IRQ);
+
+ dmabuf += sg[i].length;
+
+ bytes_copied += copy_size;
+ size -= copy_size;
+
+ if (size == 0)
+ break;
+ }
+
+ return bytes_copied;
+}
+
+/* Convert ns to tick count according to the current sclk speed */
+static unsigned short stmp3xxx_ns_to_ssp_ticks(unsigned clock_rate, unsigned ns)
+{
+ const unsigned int ssp_timeout_mul = 4096;
+ /*
+ * Calculate ticks in ms since ns are large numbers
+ * and might overflow
+ */
+ const unsigned int clock_per_ms = clock_rate / 1000;
+ const unsigned int ms = ns / 1000;
+ const unsigned int ticks = ms * clock_per_ms;
+ const unsigned int ssp_ticks = ticks / ssp_timeout_mul;
+
+ BUG_ON(ssp_ticks == 0);
+ return ssp_ticks;
+}
+
+static void __init_reg(struct device *dev, struct regulator **pp_reg)
+{
+ struct regulator *reg = *pp_reg;
+
+ if (!reg) {
+ reg = regulator_get(NULL, "mmc_ssp-1");
+ if (reg && !IS_ERR(reg))
+ regulator_set_mode(reg, REGULATOR_MODE_NORMAL);
+ else
+ reg = NULL;
+ *pp_reg = reg;
+ }
+}
+
+/* Send adtc command to the card */
+static void stmp3xxx_mmc_adtc(struct stmp3xxx_mmc_host *host)
+{
+ struct mmc_command *cmd = host->cmd;
+ struct stmp3xxx_dma_descriptor *dma_desc = &host->dma_desc;
+ int ignore_crc, resp, long_resp;
+ int is_reading = 0;
+ unsigned int copy_size;
+
+ u32 ssp_ctrl0;
+ u32 ssp_cmd0;
+ u32 ssp_cmd1;
+ u32 timeout;
+
+ u32 data_size = cmd->data->blksz * cmd->data->blocks;
+ u32 log2_block_size;
+
+ ignore_crc = mmc_resp_type(cmd) & MMC_RSP_CRC ? 0 : 1;
+ resp = mmc_resp_type(cmd) & MMC_RSP_PRESENT ? 1 : 0;
+ long_resp = mmc_resp_type(cmd) & MMC_RSP_136 ? 1 : 0;
+
+ dev_dbg(host->dev, "ADTC command:\n"
+ "response: %d, ignore crc: %d\n"
+ "data list: %u, blocksz: %u, blocks: %u, timeout: %uns %uclks, "
+ "flags: 0x%x\n", resp, ignore_crc, cmd->data->sg_len,
+ cmd->data->blksz, cmd->data->blocks, cmd->data->timeout_ns,
+ cmd->data->timeout_clks, cmd->data->flags);
+
+ if (cmd->data->flags & MMC_DATA_WRITE) {
+ dev_dbg(host->dev, "Data Write\n");
+ copy_size = stmp3xxx_sg_dma_copy(host, data_size, 1);
+ BUG_ON(copy_size < data_size);
+ is_reading = 0;
+ if (!host->regulator)
+ __init_reg(host->dev, &host->regulator);
+ if (host->regulator)
+ regulator_set_current_limit(host->regulator, host->write_uA, host->write_uA);
+ } else if (cmd->data->flags & MMC_DATA_READ) {
+ dev_dbg(host->dev, "Data Read\n");
+ is_reading = 1;
+ if (!host->regulator)
+ __init_reg(host->dev, &host->regulator);
+ if (host->regulator)
+ regulator_set_current_limit(host->regulator, host->read_uA, host->read_uA);
+ } else {
+ dev_warn(host->dev, "Unsuspported data mode, 0x%x\n",
+ cmd->data->flags);
+ BUG();
+ }
+
+ BUG_ON(cmd->data->flags & MMC_DATA_STREAM);
+ BUG_ON((data_size % 8) > 0);
+
+ dma_desc->command->cmd =
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_SEMAPHORE |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF_APBH_CHn_CMD_XFER_COUNT(data_size) |
+ BF_APBH_CHn_CMD_CMDWORDS(3);
+
+ /* when is_reading is set, DMA controller performs WRITE operation. */
+ dma_desc->command->cmd |=
+ BF_APBH_CHn_CMD_COMMAND(
+ is_reading ? BV_APBH_CHn_CMD_COMMAND__DMA_WRITE :
+ BV_APBH_CHn_CMD_COMMAND__DMA_READ);
+ ssp_ctrl0 =
+ (ignore_crc ? BM_SSP_CTRL0_IGNORE_CRC : 0) |
+ (resp ? BM_SSP_CTRL0_GET_RESP : 0) |
+ (long_resp ? BM_SSP_CTRL0_LONG_RESP : 0) |
+ (is_reading ? BM_SSP_CTRL0_READ : 0) |
+ BM_SSP_CTRL0_DATA_XFER |
+ BM_SSP_CTRL0_WAIT_FOR_IRQ |
+ BM_SSP_CTRL0_ENABLE |
+ BF_SSP_CTRL0_XFER_COUNT(data_size) |
+ BF_SSP_CTRL0_BUS_WIDTH(host->bus_width_4 ?
+ BV_SSP_CTRL0_BUS_WIDTH__FOUR_BIT :
+ BV_SSP_CTRL0_BUS_WIDTH__ONE_BIT);
+
+ /*
+ * We need to set the hardware register to the logarithm to base 2 of
+ * the block size.
+ */
+ log2_block_size = ilog2(cmd->data->blksz);
+
+ ssp_cmd0 =
+ BF_SSP_CMD0_BLOCK_SIZE(log2_block_size) |
+ BF_SSP_CMD0_CMD(cmd->opcode) |
+ BF_SSP_CMD0_BLOCK_COUNT(cmd->data->blocks - 1);
+
+ if (cmd->opcode == 12)
+ ssp_cmd0 |= BM_SSP_CMD0_APPEND_8CYC;
+
+ ssp_cmd1 = BF_SSP_CMD1_CMD_ARG(cmd->arg);
+
+ dma_desc->command->pio_words[0] = ssp_ctrl0;
+ dma_desc->command->pio_words[1] = ssp_cmd0;
+ dma_desc->command->pio_words[2] = ssp_cmd1;
+
+ /* Set the timeout count */
+ timeout = stmp3xxx_ns_to_ssp_ticks(host->clkrt, cmd->data->timeout_ns);
+ HW_SSP_TIMING_CLR_NB(host->ssp_base, BM_SSP_TIMING_TIMEOUT);
+ HW_SSP_TIMING_SET_NB(host->ssp_base, BF_SSP_TIMING_TIMEOUT(timeout));
+
+ init_completion(&host->dma_done);
+ stmp3xxx_dma_reset_channel(host->dmach);
+ stmp3xxx_dma_go(host->dmach, dma_desc, 1);
+ wait_for_completion(&host->dma_done);
+ if (host->regulator)
+ regulator_set_current_limit(host->regulator, 0, 0);
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ break;
+ case MMC_RSP_R1:
+ case MMC_RSP_R3:
+ cmd->resp[0] = HW_SSP_SDRESP0_RD_NB(host->ssp_base);
+ break;
+ case MMC_RSP_R2:
+ cmd->resp[3] = HW_SSP_SDRESP0_RD_NB(host->ssp_base);
+ cmd->resp[2] = HW_SSP_SDRESP1_RD_NB(host->ssp_base);
+ cmd->resp[1] = HW_SSP_SDRESP2_RD_NB(host->ssp_base);
+ cmd->resp[0] = HW_SSP_SDRESP3_RD_NB(host->ssp_base);
+ break;
+ default:
+ dev_warn(host->dev, "Unsupported response type 0x%x\n",
+ mmc_resp_type(cmd));
+ BUG();
+ break;
+ }
+
+ cmd->error = stmp3xxx_mmc_cmd_error(host->status);
+
+ if (stmp3xxx_dma_running(host->dmach))
+ dev_dbg(host->dev, "DMA command not finished\n");
+
+ if (cmd->error) {
+ dev_dbg(host->dev, "Command error 0x%x\n", cmd->error);
+ stmp3xxx_dma_reset_channel(host->dmach);
+ } else {
+ if (is_reading)
+ cmd->data->bytes_xfered =
+ stmp3xxx_sg_dma_copy(host, data_size, 0);
+ else
+ cmd->data->bytes_xfered = data_size;
+
+ dev_dbg(host->dev, "Transferred %u bytes\n",
+ cmd->data->bytes_xfered);
+ }
+}
+
+/* Begin sedning a command to the card */
+static void stmp3xxx_mmc_start_cmd(struct stmp3xxx_mmc_host *host,
+ struct mmc_command *cmd)
+{
+ dev_dbg(host->dev, "MMC command:\n"
+ "type: 0x%x opcode: %u, arg: %u, flags 0x%x retries: %u\n",
+ mmc_cmd_type(cmd), cmd->opcode, cmd->arg, cmd->flags,
+ cmd->retries);
+
+ host->cmd = cmd;
+
+ switch (mmc_cmd_type(cmd)) {
+ case MMC_CMD_BC:
+ stmp3xxx_mmc_bc(host);
+ break;
+ case MMC_CMD_BCR:
+ stmp3xxx_mmc_ac(host);
+ break;
+ case MMC_CMD_AC:
+ stmp3xxx_mmc_ac(host);
+ break;
+ case MMC_CMD_ADTC:
+ stmp3xxx_mmc_adtc(host);
+ break;
+ default:
+ dev_warn(host->dev, "Unknown MMC command\n");
+ BUG();
+ break;
+ }
+
+ dev_dbg(host->dev, "response: %u %u %u %u errors: %u\n",
+ cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3],
+ cmd->error);
+}
+
+/* Handle MMC request */
+static void stmp3xxx_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct stmp3xxx_mmc_host *host = mmc_priv(mmc);
+
+ dev_dbg(host->dev, "MMC request\n");
+
+ host->mrq = mrq;
+
+ stmp3xxx_mmc_start_cmd(host, mrq->cmd);
+
+ if (mrq->data && mrq->data->stop) {
+ dev_dbg(host->dev, "Stop opcode is %u\n",
+ mrq->data->stop->opcode);
+ stmp3xxx_mmc_start_cmd(host, mrq->data->stop);
+ }
+
+ host->mrq = NULL;
+ mmc_request_done(mmc, mrq);
+}
+
+/*
+ * Change divisors to reflect the rate of 'hz'. Note that we should not
+ * play with clock rate, because the same source is used to clock both
+ * SSP ports.
+ */
+static void
+stmp3xxx_set_sclk_speed(struct stmp3xxx_mmc_host *host, unsigned int hz)
+{
+ unsigned long ssp;
+ u32 div1, div2;
+ struct stmp3xxxmmc_platform_data *pdata = host->dev->platform_data;
+
+ if (get_evk_board_version() == 1) {
+ /*EVK Ver1 max clock is 12M*/
+ if (hz > 12000000)
+ hz = 12000000;
+ }
+
+ if (pdata && pdata->setclock) {
+ /*
+ if the SSP is buggy and platform provides callback...
+ well, let it be.
+ */
+ host->clkrt = pdata->setclock(hz);
+ return;
+ }
+
+ /*
+ ...but the RightIdea(tm) is to set divisors to match
+ the requested clock.
+ */
+ hz /= 1000;
+
+ ssp = clk_get_rate(host->clk);
+
+ for (div1 = 2; div1 < 254; div1 += 2) {
+ div2 = ssp / hz / div1;
+ if (div2 < 0x100)
+ break;
+ }
+ if (div1 >= 254) {
+ dev_err(host->dev, "Cannot set clock to %dkHz\n", hz);
+ return;
+ }
+
+ dev_dbg(host->dev, "Setting clock rate to %ld kHz [%x+%x] "
+ "(requested %d), source %ldk\n",
+ ssp/div1/div2, div1, div2, hz, ssp);
+ HW_SSP_TIMING_CLR_NB(host->ssp_base,
+ BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE);
+ HW_SSP_TIMING_SET_NB(host->ssp_base,
+ BF_SSP_TIMING_CLOCK_DIVIDE(div1) |
+ BF_SSP_TIMING_CLOCK_RATE(div2 - 1));
+ host->clkrt = ssp/div1/div2 * 1000;
+}
+
+/* Configure card */
+static void stmp3xxx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct stmp3xxx_mmc_host *host = mmc_priv(mmc);
+ struct stmp3xxxmmc_platform_data *pdata;
+
+ dev_dbg(host->dev, "MMC set ios:\n"
+ "Clock %u, vdd %u, bus_mode %u, chip_select %u, "
+ "power mode %u, bus_width %u\n", ios->clock, ios->vdd,
+ ios->bus_mode, ios->chip_select, ios->power_mode,
+ ios->bus_width);
+
+ pdata = host->dev->platform_data;
+
+ if (pdata->cmd_pullup) {
+ if (ios->bus_mode == MMC_BUSMODE_PUSHPULL)
+ pdata->cmd_pullup(0);
+ else
+ pdata->cmd_pullup(1);
+ } else
+ dev_warn(host->dev,
+ "Platform does not support CMD pin pullup control\n");
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4)
+ host->bus_width_4 = 1;
+ else
+ host->bus_width_4 = 0;
+
+ if (ios->clock > 0)
+ stmp3xxx_set_sclk_speed(host, ios->clock);
+}
+
+static const struct mmc_host_ops stmp3xxx_mmc_ops = {
+ .request = stmp3xxx_mmc_request,
+ .get_ro = stmp3xxx_mmc_get_ro,
+ .set_ios = stmp3xxx_mmc_set_ios,
+};
+
+
+/*
+ * STMP37XX MMC/SD driver initialization
+ */
+
+/* Reset ssp peripheral to default values */
+static void stmp3xxx_mmc_reset(struct stmp3xxx_mmc_host *host)
+{
+ u32 ssp_ctrl0;
+ u32 ssp_ctrl1;
+
+ stmp3xxx_reset_block(host->ssp_base, 0);
+
+ /* Configure SSP Control Register 0 */
+ ssp_ctrl0 =
+ BM_SSP_CTRL0_IGNORE_CRC |
+ BF_SSP_CTRL0_BUS_WIDTH(BV_SSP_CTRL0_BUS_WIDTH__ONE_BIT);
+
+ /* Configure SSP Control Register 1 */
+ ssp_ctrl1 =
+ BM_SSP_CTRL1_DMA_ENABLE |
+ BM_SSP_CTRL1_POLARITY |
+ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_DATA_CRC_IRQ_EN |
+ BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN |
+ BM_SSP_CTRL1_RESP_ERR_IRQ_EN |
+ BF_SSP_CTRL1_WORD_LENGTH(BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) |
+ BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SD_MMC);
+
+ HW_SSP_TIMING_WR(BF_SSP_TIMING_TIMEOUT(0xFFFF) |
+ BF_SSP_TIMING_CLOCK_DIVIDE(2) |
+ BF_SSP_TIMING_CLOCK_RATE(0));
+
+ /* Write the SSP Control Register 0 and 1 values out to the interface */
+ HW_SSP_CTRL0_WR_NB(host->ssp_base, ssp_ctrl0);
+ HW_SSP_CTRL1_WR_NB(host->ssp_base, ssp_ctrl1);
+}
+
+static void stmp3xxx_mmc_irq_release(struct stmp3xxx_mmc_host *host)
+{
+ free_irq(host->dmairq, host);
+ free_irq(host->errirq, host);
+}
+
+static int __init stmp3xxx_mmc_irq_init(struct stmp3xxx_mmc_host *host)
+{
+ int ret;
+
+ ret = request_irq(host->dmairq, mmc_irq_handler, 0,
+ DRIVER_NAME" dma", host);
+ if (ret) {
+ dev_err(host->dev, "Unable to set up DMA irq handler\n");
+ goto out0;
+ }
+
+ ret = request_irq(host->errirq, mmc_irq_handler, IRQF_SHARED,
+ DRIVER_NAME" error", host);
+ if (ret) {
+ dev_err(host->dev, "Unable to set up SSP error irq handler\n");
+ goto out1;
+ }
+ return 0;
+
+out1:
+ free_irq(host->dmairq, host);
+out0:
+ return ret;
+}
+
+
+/* Allocate and initialise the DMA chains */
+static int stmp3xxx_mmc_dma_init(struct stmp3xxx_mmc_host *host, int reset)
+{
+ int ret;
+
+ if (!reset) {
+ /* Allocate DMA channel */
+ ret = stmp3xxx_dma_request(host->dmach,
+ host->dev, "STMP37XX MMC/SD");
+ if (ret) {
+ dev_err(host->dev, "Unable to request DMA channel\n");
+ return ret;
+ }
+
+ host->dma_buf = dma_alloc_coherent(host->dev, SSP_BUFFER_SIZE,
+ &host->dma_buf_phys, GFP_DMA);
+ if (host->dma_buf == NULL) {
+ dev_err(host->dev, "Unable to allocate DMA memory\n");
+ ret = -ENOMEM;
+ goto out_mem;
+ }
+
+ ret = stmp3xxx_dma_allocate_command(host->dmach,
+ &host->dma_desc);
+ if (ret) {
+ dev_err(host->dev,
+ "Unable to allocate DMA descriptor\n");
+ goto out_cmd;
+ }
+
+ host->dma_desc.command->next = (u32)host->dma_desc.handle;
+ host->dma_desc.command->buf_ptr = (u32)host->dma_buf_phys;
+ host->dma_desc.virtual_buf_ptr = host->dma_buf;
+ }
+
+ /* Reset DMA channel */
+ stmp3xxx_dma_reset_channel(host->dmach);
+
+ /* Enable DMA interrupt */
+ stmp3xxx_dma_clear_interrupt(host->dmach);
+ stmp3xxx_dma_enable_interrupt(host->dmach);
+
+ return 0;
+
+out_cmd:
+ dma_free_coherent(host->dev, SSP_BUFFER_SIZE, host->dma_buf,
+ host->dma_buf_phys);
+out_mem:
+ stmp3xxx_dma_release(host->dmach);
+
+ return ret;
+}
+
+static void stmp3xxx_mmc_dma_release(struct stmp3xxx_mmc_host *host)
+{
+ stmp3xxx_dma_reset_channel(host->dmach);
+
+ dma_free_coherent(host->dev, SSP_BUFFER_SIZE, host->dma_buf,
+ host->dma_buf_phys);
+
+ stmp3xxx_dma_free_command(host->dmach, &host->dma_desc);
+ stmp3xxx_dma_release(host->dmach);
+}
+
+/* Probe peripheral for connected cards */
+static int __init stmp3xxx_mmc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stmp3xxxmmc_platform_data *mmc_data;
+ struct stmp3xxx_mmc_host *host;
+ struct mmc_host *mmc;
+ struct resource *r;
+ int err = 0;
+
+ mmc_data = dev->platform_data;
+ if (mmc_data == NULL) {
+ err = -EINVAL;
+ dev_err(dev, "Missing platform data\n");
+ goto out;
+ }
+
+ /* Allocate main MMC host structure */
+ mmc = mmc_alloc_host(sizeof(struct stmp3xxx_mmc_host), dev);
+ if (!mmc) {
+ dev_err(dev, "Unable to allocate MMC host\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ host = mmc_priv(mmc);
+
+ host->read_uA = mmc_data->read_uA;
+ host->write_uA = mmc_data->write_uA;
+ host->regulator = regulator_get(NULL, "mmc_ssp-1");
+ if (host->regulator && !IS_ERR(host->regulator))
+ regulator_set_mode(host->regulator, REGULATOR_MODE_NORMAL);
+ else
+ host->regulator = NULL;
+
+ /* get resources: */
+
+ /*
+ * 1. io memory
+ */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get IORESOURCE_MEM\n");
+ err = -ENXIO;
+ goto out_res;
+ }
+ host->ssp_base = r->start; /* it is already ioremapped */
+
+ /*
+ * 2. DMA channel
+ */
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get IORESOURCE_DMA\n");
+ err = -ENXIO;
+ goto out_res;
+ }
+ host->dmach = r->start;
+
+ /*
+ * 3. two IRQs
+ */
+ host->dmairq = platform_get_irq(pdev, 0);
+ if (host->dmairq < 0) {
+ dev_err(&pdev->dev, "failed to get IORESOURCE_IRQ/0\n");
+ err = host->dmairq;
+ goto out_res;
+ }
+
+ host->errirq = platform_get_irq(pdev, 1);
+ if (host->errirq < 0) {
+ dev_err(&pdev->dev, "failed to get IORESOURCE_IRQ/1\n");
+ err = host->errirq;
+ goto out_res;
+ }
+
+ /* Set up MMC pins */
+ if (mmc_data->hw_init) {
+ err = mmc_data->hw_init();
+ if (err) {
+ dev_err(dev, "MMC HW configuration failed\n");
+ goto out_res;
+ }
+ }
+
+ host->mmc = mmc;
+ host->dev = dev;
+
+ /* Set minimal clock rate */
+ host->clk = clk_get(dev, "ssp");
+ if (IS_ERR(host->clk)) {
+ err = PTR_ERR(host->clk);
+ dev_err(dev, "Clocks initialization failed\n");
+ goto out_clk;
+ }
+
+ clk_enable(host->clk);
+ stmp3xxx_set_sclk_speed(host, CLOCKRATE_MIN);
+
+ /* Reset MMC block */
+ stmp3xxx_mmc_reset(host);
+
+ /* Enable DMA */
+ err = stmp3xxx_mmc_dma_init(host, 0);
+ if (err) {
+ dev_err(dev, "DMA init failed\n");
+ goto out_dma;
+ }
+
+ /* Set up interrupt handlers */
+ err = stmp3xxx_mmc_irq_init(host);
+ if (err) {
+ dev_err(dev, "IRQ initialization failed\n");
+ goto out_irq;
+ }
+
+ /* Get current card status for further cnanges tracking */
+ host->present = stmp3xxx_mmc_is_plugged(host);
+
+ /* Add a card detection polling timer */
+ init_timer(&host->timer);
+ host->timer.function = stmp3xxx_mmc_detect_poll;
+ host->timer.data = (unsigned long)host;
+ host->timer.expires = jiffies + STMP37XX_MMC_DETECT_TIMEOUT;
+ add_timer(&host->timer);
+
+ mmc->ops = &stmp3xxx_mmc_ops;
+ mmc->f_min = CLOCKRATE_MIN;
+ mmc->f_max = CLOCKRATE_MAX;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+
+ /* Maximum block count requests. */
+ mmc->max_blk_size = 512;
+ mmc->max_blk_count = SSP_BUFFER_SIZE / 512;
+ mmc->max_hw_segs = SSP_BUFFER_SIZE / 512;
+ mmc->max_phys_segs = SSP_BUFFER_SIZE / 512;
+ mmc->max_req_size = SSP_BUFFER_SIZE;
+ mmc->max_seg_size = SSP_BUFFER_SIZE;
+
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ platform_set_drvdata(pdev, mmc);
+
+ err = mmc_add_host(mmc);
+ if (err) {
+ dev_err(dev, "Oh God. mmc_add_host failed\n");
+ goto out_all;
+ }
+
+ return err;
+
+out_all:
+
+out_irq:
+ stmp3xxx_mmc_dma_release(host);
+out_dma:
+ clk_disable(host->clk);
+out_clk:
+ if (mmc_data->hw_release)
+ mmc_data->hw_release();
+out_res:
+ mmc_free_host(mmc);
+out:
+ return err;
+}
+
+static int __exit stmp3xxx_mmc_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_mmc_host *host;
+ struct stmp3xxxmmc_platform_data *mmc_data;
+ struct mmc_host *mmc;
+
+ dev_info(&pdev->dev, "Removing\n");
+
+ mmc_data = pdev->dev.platform_data;
+ mmc = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, NULL);
+
+ host = mmc_priv(mmc);
+ mmc_remove_host(mmc);
+
+ /* Disable SSP clock */
+ clk_disable(host->clk);
+ clk_put(host->clk);
+
+ /* Release IRQs */
+ stmp3xxx_mmc_irq_release(host);
+
+ /* Delete card detection timer */
+ del_timer(&host->timer);
+
+ /* Release DMA */
+ stmp3xxx_mmc_dma_release(host);
+ if (host->regulator)
+ regulator_put(host->regulator);
+
+ mmc_free_host(mmc);
+
+ if (mmc_data->hw_release)
+ mmc_data->hw_release();
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stmp3xxx_mmc_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct stmp3xxx_mmc_host *host;
+ struct stmp3xxxmmc_platform_data *mmc_data;
+ struct mmc_host *mmc;
+ int ret = 0;
+
+ dev_dbg(&pdev->dev, "Suspending\n");
+
+ mmc_data = pdev->dev.platform_data;
+ mmc = platform_get_drvdata(pdev);
+ host = mmc_priv(mmc);
+
+ ret = mmc_suspend_host(mmc, state);
+ if (!ret) {
+ if (mmc_data && mmc_data->hw_release)
+ mmc_data->hw_release();
+ clk_disable(host->clk);
+ }
+ return ret;
+}
+
+static int stmp3xxx_mmc_resume(struct platform_device *pdev)
+{
+ struct stmp3xxx_mmc_host *host;
+ struct stmp3xxxmmc_platform_data *mmc_data;
+ struct mmc_host *mmc;
+
+ dev_dbg(&pdev->dev, "Resuming\n");
+
+ mmc_data = pdev->dev.platform_data;
+ mmc = platform_get_drvdata(pdev);
+ host = mmc_priv(mmc);
+
+ clk_enable(host->clk);
+
+ if (mmc_data->hw_init)
+ mmc_data->hw_init();
+ stmp3xxx_mmc_reset(host);
+ stmp3xxx_mmc_dma_init(host, 1);
+
+ return mmc_resume_host(mmc);
+}
+#else
+#define stmp3xxx_mmc_suspend NULL
+#define stmp3xxx_mmc_resume NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver stmp3xxx_mmc_driver = {
+ .probe = stmp3xxx_mmc_probe,
+ .remove = __exit_p(stmp3xxx_mmc_remove),
+ .suspend = stmp3xxx_mmc_suspend,
+ .resume = stmp3xxx_mmc_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmp3xxx_mmc_init(void)
+{
+ int ret = 0;
+
+ ret = platform_driver_register(&stmp3xxx_mmc_driver);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static void __exit stmp3xxx_mmc_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_mmc_driver);
+}
+
+module_init(stmp3xxx_mmc_init);
+module_exit(stmp3xxx_mmc_exit);
+
+MODULE_DESCRIPTION("STMP37xx/378x MMC peripheral");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 5ea169362164..868c4df8b7e4 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -540,5 +540,15 @@ config MTD_PLATRAM
This selection automatically selects the map_ram driver.
+config MTD_MXC
+ bool "Map driver for Freescale MXC boards"
+ depends on MTD && ARCH_MXC
+ default y
+ select MTD_CFI
+ select MTD_PARTITIONS
+ help
+ This enables access to the flash chips on Freescale MXC based
+ platforms. If you have such a board, say 'Y'.
+
endmenu
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 6d9ba35caf11..9070771637dc 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -61,3 +61,4 @@ obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
+obj-$(CONFIG_MTD_MXC) += mxc_nor.o
diff --git a/drivers/mtd/maps/mxc_nor.c b/drivers/mtd/maps/mxc_nor.c
new file mode 100644
index 000000000000..e89e534aa440
--- /dev/null
+++ b/drivers/mtd/maps/mxc_nor.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * (c) 2005 MontaVista Software, Inc.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/clocksource.h>
+#include <asm/mach-types.h>
+#include <asm/mach/flash.h>
+
+#define DVR_VER "2.0"
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
+#endif
+
+struct clocksource *mtd_xip_clksrc;
+
+struct mxcflash_info {
+ struct mtd_partition *parts;
+ struct mtd_info *mtd;
+ struct map_info map;
+};
+
+/*!
+ * @defgroup NOR_MTD NOR Flash MTD Driver
+ */
+
+/*!
+ * @file mxc_nor.c
+ *
+ * @brief This file contains the MTD Mapping information on the MXC.
+ *
+ * @ingroup NOR_MTD
+ */
+
+static int __devinit mxcflash_probe(struct platform_device *pdev)
+{
+ int err, nr_parts = 0;
+ struct mxcflash_info *info;
+ struct flash_platform_data *flash = pdev->dev.platform_data;
+ struct resource *res = pdev->resource;
+ unsigned long size = res->end - res->start + 1;
+
+ info = kzalloc(sizeof(struct mxcflash_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (!request_mem_region(res->start, size, "flash")) {
+ err = -EBUSY;
+ goto out_free_info;
+ }
+ info->map.virt = ioremap(res->start, size);
+ if (!info->map.virt) {
+ err = -ENOMEM;
+ goto out_release_mem_region;
+ }
+ info->map.name = pdev->dev.bus_id;
+ info->map.phys = res->start;
+ info->map.size = size;
+ info->map.bankwidth = flash->width;
+
+ mtd_xip_clksrc = clocksource_get_next();
+
+ simple_map_init(&info->map);
+ info->mtd = do_map_probe(flash->map_name, &info->map);
+ if (!info->mtd) {
+ err = -EIO;
+ goto out_iounmap;
+ }
+ info->mtd->owner = THIS_MODULE;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ nr_parts =
+ parse_mtd_partitions(info->mtd, part_probes, &info->parts, 0);
+ if (nr_parts > 0) {
+ add_mtd_partitions(info->mtd, info->parts, nr_parts);
+ } else if (flash->parts) {
+ add_mtd_partitions(info->mtd, flash->parts, flash->nr_parts);
+ } else
+#endif
+ {
+ printk(KERN_NOTICE "MXC flash: no partition info "
+ "available, registering whole flash\n");
+ add_mtd_device(info->mtd);
+ }
+
+ platform_set_drvdata(pdev, info);
+ return 0;
+
+ out_iounmap:
+ iounmap(info->map.virt);
+ out_release_mem_region:
+ release_mem_region(res->start, size);
+ out_free_info:
+ kfree(info);
+
+ return err;
+}
+
+static int __devexit mxcflash_remove(struct platform_device *pdev)
+{
+
+ struct mxcflash_info *info = platform_get_drvdata(pdev);
+ struct flash_platform_data *flash = pdev->dev.platform_data;
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (info) {
+ if (info->parts) {
+ del_mtd_partitions(info->mtd);
+ kfree(info->parts);
+ } else if (flash->parts)
+ del_mtd_partitions(info->mtd);
+ else
+ del_mtd_device(info->mtd);
+
+ map_destroy(info->mtd);
+ release_mem_region(info->map.phys, info->map.size);
+ iounmap((void __iomem *)info->map.virt);
+ kfree(info);
+ }
+ return 0;
+}
+
+static struct platform_driver mxcflash_driver = {
+ .driver = {
+ .name = "mxc_nor_flash",
+ },
+ .probe = mxcflash_probe,
+ .remove = __devexit_p(mxcflash_remove),
+};
+
+/*!
+ * This is the module's entry function. It passes board specific
+ * config details into the MTD physmap driver which then does the
+ * real work for us. After this function runs, our job is done.
+ *
+ * @return 0 if successful; non-zero otherwise
+ */
+static int __init mxc_mtd_init(void)
+{
+ pr_info("MXC MTD nor Driver %s\n", DVR_VER);
+ if (platform_driver_register(&mxcflash_driver) != 0) {
+ printk(KERN_ERR "Driver register failed for mxcflash_driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/*!
+ * This function is the module's exit function. It's empty because the
+ * MTD physmap driver is doing the real work and our job was done after
+ * mxc_mtd_init() runs.
+ */
+static void __exit mxc_mtd_exit(void)
+{
+ platform_driver_unregister(&mxcflash_driver);
+}
+
+module_init(mxc_mtd_init);
+module_exit(mxc_mtd_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MTD map and partitions for Freescale MXC boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 1c2e9450d663..95ed64f5ac94 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -365,6 +365,102 @@ config MTD_NAND_NANDSIM
The simulator may simulate various NAND flash chips for the
MTD nand layer.
+config MTD_NAND_IMX_NFC
+ tristate "i.MX NAND Flash Controller driver"
+ depends on MTD_NAND
+ help
+ Enables the i.MX NAND Flash controller driver.
+
+config MTD_NAND_MXC
+ tristate "MXC NAND support"
+ depends on MTD_NAND && ARCH_MXC_HAS_NFC_V1
+ help
+ This enables the driver for the NAND flash controller on the
+ MXC processors.
+
+config MTD_NAND_MXC_V2
+ tristate "MXC NAND Version 2 support"
+ depends on MTD_NAND && ARCH_MXC_HAS_NFC_V2
+ help
+ This enables the driver for the version 2 of NAND flash controller
+ on the MXC processors.
+
+config MTD_NAND_MXC_V3
+ tristate "MXC NAND Version 3 support"
+ depends on MTD_NAND && ARCH_MXC_HAS_NFC_V3
+ help
+ This enables the driver for the version 3 of NAND flash controller
+ on the MXC processors.
+
+config MTD_NAND_MXC_SWECC
+ bool "Software ECC support "
+ depends on MTD_NAND_MXC || MTD_NAND_MXC_V2 || MTD_NAND_MXC_V3
+ help
+ This enables the support for Software ECC handling. By
+ default MXC NAND controller Hardware ECC is supported.
+
+
+config MTD_NAND_MXC_FORCE_CE
+ bool "NAND chip select operation support"
+ depends on MTD_NAND_MXC || MTD_NAND_MXC_V2|| MTD_NAND_MXC_V3
+ help
+ This enables the NAND chip select by using CE control line. By
+ default CE operation is disabled.
+
+config MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+ bool "ECC correction in S/W"
+ depends on MTD_NAND_MXC
+ help
+ This enables the Option2 NFC ECC correction in software. By
+ default Option 1 is selected. Enable if you need option2 ECC correction.
+
+config MXC_NAND_LOW_LEVEL_ERASE
+ bool "Low level NAND erase"
+ depends on MTD_NAND_MXC || MTD_NAND_MXC_V2 || MTD_NAND_MXC_V3
+ help
+ This enables the erase of whole NAND flash. By
+ default low level erase operation is disabled.
+
+config MTD_NAND_GPMI_LBA
+ tristate "GPMI LBA NAND driver"
+ depends on MTD_NAND && ARCH_STMP3XXX
+ help
+ Enables support of LBA devices on GPMI on 37xx/378x SigmaTel
+ boards
+
+config MTD_NAND_GPMI
+ tristate "GPMI NAND driver"
+ depends on MTD_NAND && ARCH_STMP3XXX && !MTD_NAND_GPMI_LBA
+ help
+ Enables support of NAND devices on GPMI on 37xx/378x SigmaTel
+ boards
+
+config MTD_NAND_GPMI_SYSFS_ENTRIES
+ bool "Create /sys entries for GPMI device"
+ depends on MTD_NAND_GPMI
+ help
+ Check this to enable /sys entries for GPMI devices
+
+config MTD_NAND_GPMI_BCH
+ bool "Enable BCH HWECC"
+ depends on MTD_NAND_GPMI
+ depends on ARCH_STMP378X
+ default y
+ help
+ Check this to enable /sys entries for GPMI devices
+
+config MTD_NAND_GPMI_TA1
+ bool "Support for TA1 NCB format (Hamming code 22,16)"
+ depends on MTD_NAND_GPMI
+ depends on ARCH_STMP378X
+ default y
+
+config MTD_NAND_GPMI_TA3
+ bool "Support for TA3 NCB format (Hamming code 13,8)"
+ depends on MTD_NAND_GPMI
+ depends on ARCH_STMP378X
+ default y
+
config MTD_NAND_PLATFORM
tristate "Support for generic platform NAND driver"
depends on MTD_NAND
@@ -420,4 +516,11 @@ config MTD_NAND_SH_FLCTL
Several Renesas SuperH CPU has FLCTL. This option enables support
for NAND Flash using FLCTL. This driver support SH7723.
+config MTD_NAND_CCX9X
+ tristate "NAND Flash on CC9X/CCW9C"
+ depends on MTD_NAND && (MODULE_CC9P9360 || MODULE_CC9C || MODULE_CCW9C)
+ help
+ This enables the driver for the internal NAND flash on Digi NS9360
+ based modules CC9P9360, CC9C and CCW9C.
+
endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index b661586afbfc..f5021eaeb0d9 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -24,6 +24,12 @@ obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o
obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o
+obj-$(CONFIG_MTD_NAND_IMX_NFC) += imx_nfc.o
+obj-$(CONFIG_MTD_NAND_MXC) += mxc_nd.o
+obj-$(CONFIG_MTD_NAND_MXC_V2) += mxc_nd2.o
+obj-$(CONFIG_MTD_NAND_MXC_V3) += mxc_nd2.o
+obj-$(CONFIG_MTD_NAND_GPMI) += gpmi/
+obj-$(CONFIG_MTD_NAND_GPMI_LBA) += lba/
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o
@@ -36,5 +42,6 @@ obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
+obj-$(CONFIG_MTD_NAND_CCX9X) += ccx9x_nand.o
nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/ccx9x_nand.c b/drivers/mtd/nand/ccx9x_nand.c
new file mode 100644
index 000000000000..af2ace9b99b4
--- /dev/null
+++ b/drivers/mtd/nand/ccx9x_nand.c
@@ -0,0 +1,267 @@
+/*
+ * drivers/mtd/nand/ccx9x_nand.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Based on drivers/mtd/nand/ccx9x_nand.c by Markus Pietrek
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mtd/ccx9x_nand.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/platform_device.h>
+
+#include <mach/gpio.h>
+
+#define DRIVER_NAME "ccx9x_nand"
+#define DEVICE_NAME "onboard_boot"
+
+#define mi2ncd(mi) container_of(mi, struct nand_ccx9x, mtd)
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *probes[] = { "cmdlinepart", NULL };
+#endif
+
+struct nand_ccx9x {
+ struct mtd_info mtd;
+ struct nand_chip chip;
+
+ struct resource *mem;
+ struct ccx9x_nand_info *nand_info;
+};
+
+/* read in gpio ready pin */
+static int ccx9x_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_ccx9x *ncd = mi2ncd(mtd);
+
+ return gpio_get_value(ncd->nand_info->busy_pin);
+}
+
+static void ccx9x_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct nand_ccx9x *ncd = mi2ncd(mtd);
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ writeb(cmd, ncd->chip.IO_ADDR_W + ncd->nand_info->cmd_offset);
+ else
+ writeb(cmd, ncd->chip.IO_ADDR_W + ncd->nand_info->addr_offset);
+}
+
+static void ccx9x_nand_read_buf(struct mtd_info *mtd,
+ u_char *buf, int len)
+{
+ struct nand_ccx9x *ncd = mi2ncd(mtd);
+
+ while (len) {
+ *buf = ioread8(ncd->chip.IO_ADDR_R);
+ buf++;
+ len--;
+ }
+}
+
+static void ccx9x_nand_write_buf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct nand_ccx9x *ncd = mi2ncd(mtd);
+
+ while (len) {
+ iowrite8(*buf, ncd->chip.IO_ADDR_W);
+ buf++;
+ len--;
+ }
+
+ /* FIXME: 32bit burst writing */
+}
+
+static int ccx9x_nand_verify_buf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ static u_char *tmp;
+ int i, ret = 0;
+
+ tmp = kmalloc(len, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ /* read fromd device */
+ ccx9x_nand_read_buf(mtd, tmp, len);
+
+ /* compare */
+ for (i = 0; i < len; i++)
+ if (tmp[i] != buf[i]) {
+ ret = -EFAULT;
+ break;
+ }
+
+ kfree(tmp);
+ return ret;
+
+}
+
+static int ccx9x_nand_probe(struct platform_device *pdev)
+{
+ struct nand_ccx9x *ncd;
+ struct resource *mem;
+ struct mtd_partition *mtd_parts;
+ int num_parts;
+ int ret;
+
+ ncd = kzalloc(sizeof(*ncd), GFP_KERNEL);
+ if (!ncd) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc\n", __func__);
+ goto err_alloc;
+ }
+
+ ncd->nand_info = pdev->dev.platform_data;
+ if (!ncd->nand_info) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "%s: err_pdata\n", __func__);
+ goto err_pdata;
+ }
+
+ platform_set_drvdata(pdev, ncd);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "%s: err_get_mem\n", __func__);
+ goto err_get_mem;
+ }
+
+ if (!request_mem_region(mem->start, mem->end - mem->start,
+ DRIVER_NAME)) {
+ ret = -EBUSY;
+ dev_dbg(&pdev->dev, "%s: err_request_mem\n", __func__);
+ goto err_request_mem;
+ }
+
+ ncd->chip.IO_ADDR_W = ioremap(mem->start, mem->end - mem->start);
+ if (!ncd->chip.IO_ADDR_W) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_ioremap\n", __func__);
+ goto err_ioremap;
+ }
+ ncd->chip.IO_ADDR_R = ncd->chip.IO_ADDR_W;
+ ncd->mem = mem;
+
+ if (ncd->nand_info->busy_pin) {
+ ret = gpio_request(ncd->nand_info->busy_pin, DRIVER_NAME);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_gpio_request\n", __func__);
+ goto err_gpio_request;
+ }
+ gpio_direction_input(ncd->nand_info->busy_pin);
+
+ ncd->chip.dev_ready = ccx9x_nand_dev_ready;
+ }
+
+ ncd->chip.write_buf = ccx9x_nand_write_buf;
+ ncd->chip.read_buf = ccx9x_nand_read_buf;
+ ncd->chip.verify_buf = ccx9x_nand_verify_buf;
+ ncd->chip.cmd_ctrl = ccx9x_nand_cmd_ctrl;
+ ncd->chip.ecc.mode = NAND_ECC_SOFT;
+ ncd->chip.chip_delay = ncd->nand_info->delay;
+ ncd->chip.controller = NULL;
+
+ ncd->mtd.name = DEVICE_NAME;
+ ncd->mtd.priv = &ncd->chip;
+
+ if (nand_scan(&ncd->mtd, 1)) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "%s: err_scan\n", __func__);
+ goto err_scan;
+ }
+
+ dev_dbg(&pdev->dev, "NAND Flash memory mapped to virtual %p\n",
+ ncd->chip.IO_ADDR_W);
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+ /* read partition information from cmdline*/
+ num_parts = parse_mtd_partitions(&ncd->mtd, probes, &mtd_parts, 0);
+
+ /* register partitions */
+ add_mtd_partitions(&ncd->mtd, mtd_parts, num_parts);
+#endif
+
+ return 0;
+
+err_scan:
+ if (ncd->nand_info->busy_pin)
+ gpio_free(ncd->nand_info->busy_pin);
+err_gpio_request:
+ iounmap(ncd->chip.IO_ADDR_W);
+err_ioremap:
+ release_mem_region(mem->start, mem->end - mem->start);
+err_request_mem:
+ release_resource(mem);
+err_get_mem:
+err_pdata:
+ kfree(ncd);
+err_alloc:
+ return ret;
+}
+
+static int ccx9x_nand_remove(struct platform_device *pdev)
+{
+ struct nand_ccx9x *ncd = platform_get_drvdata(pdev);
+
+ if (ncd->nand_info->busy_pin)
+ gpio_free(ncd->nand_info->busy_pin);
+ iounmap(ncd->chip.IO_ADDR_W);
+ release_mem_region(ncd->mem->start,
+ ncd->mem->end - ncd->mem->start);
+ release_resource(ncd->mem);
+ kfree(ncd);
+
+ return 0;
+}
+
+static struct platform_driver ccx9x_nand_driver = {
+ .probe = ccx9x_nand_probe,
+ .remove = ccx9x_nand_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ccx9x_nand_init(void)
+{
+ int ret = -ENOMEM;
+
+ ret = platform_driver_register(&ccx9x_nand_driver);
+ if (ret) {
+ pr_debug("%s: err_pdev_register\n", __func__);
+ goto err_pdev_register;
+ }
+
+ return 0;
+
+err_pdev_register:
+ return ret;
+}
+
+static void __exit ccx9x_nand_exit(void)
+{
+ platform_driver_unregister(&ccx9x_nand_driver);
+}
+
+module_init(ccx9x_nand_init);
+module_exit(ccx9x_nand_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Matthias Ludwig");
+MODULE_DESCRIPTION("Digi CCx9x MTD NAND driver");
diff --git a/drivers/mtd/nand/gpmi/Makefile b/drivers/mtd/nand/gpmi/Makefile
new file mode 100644
index 000000000000..4a4b50d294fa
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_MTD_NAND_GPMI) += gpmi.o
+gpmi-objs += gpmi-base.o gpmi-bbt.o
+gpmi-objs += gpmi-hamming-22-16.o
+gpmi-objs += gpmi-hamming-13-8.o
+gpmi-objs += gpmi-bch.o
+gpmi-objs += gpmi-ecc8.o
diff --git a/drivers/mtd/nand/gpmi/gpmi-base.c b/drivers/mtd/nand/gpmi/gpmi-base.c
new file mode 100644
index 000000000000..547dec9f29f8
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-base.c
@@ -0,0 +1,1979 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/concat.h>
+#include <linux/dma-mapping.h>
+#include <linux/ctype.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/stmp3xxx.h>
+#include <mach/regs-ecc8.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+
+static int debug;
+static int copies;
+static int map_buffers = true;
+static int ff_writes;
+static int raw_mode;
+static int add_mtd_entire;
+static int add_mtd_chip;
+static int ignorebad;
+static int max_chips = 4;
+static long clk = -1;
+static int bch /* = 0 */;
+
+static int gpmi_nand_init_hw(struct platform_device *pdev, int request_pins);
+static void gpmi_nand_release_hw(struct platform_device *pdev);
+static int gpmi_dma_exchange(struct gpmi_nand_data *g,
+ struct stmp3xxx_dma_descriptor *dma);
+static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len);
+
+struct gpmi_nand_timing gpmi_safe_timing = {
+ .address_setup = 25,
+ .data_setup = 80,
+ .data_hold = 60,
+ .dsample_time = 6,
+};
+
+/*
+ * define OOB placement schemes for 4k and 2k page devices
+ */
+static struct nand_ecclayout gpmi_oob_128 = {
+ .oobfree = {
+ {
+ .offset = 2,
+ .length = 56,
+ }, {
+ .length = 0,
+ },
+ },
+};
+
+static struct nand_ecclayout gpmi_oob_64 = {
+ .oobfree = {
+ {
+ .offset = 2,
+ .length = 16,
+ }, {
+ .length = 0,
+ },
+ },
+};
+
+static inline u32 gpmi_cycles_ceil(u32 ntime, u32 period)
+{
+ int k;
+
+ k = (ntime + period - 1) / period;
+ if (k == 0)
+ k++;
+ return k;
+}
+
+/**
+ * gpmi_timer_expiry - timer expiry handling
+ */
+static void gpmi_timer_expiry(unsigned long d)
+{
+#ifdef CONFIG_PM
+ struct gpmi_nand_data *g = (struct gpmi_nand_data *)d;
+
+ pr_debug("%s: timer expired\n", __func__);
+ del_timer_sync(&g->timer);
+
+ if (g->use_count ||
+ HW_GPMI_CTRL0_RD() & BM_GPMI_CTRL0_RUN) {
+ g->timer.expires = jiffies + 4*HZ;
+ add_timer(&g->timer);
+ } else {
+ HW_GPMI_CTRL0_SET(BM_GPMI_CTRL0_CLKGATE);
+ clk_disable(g->clk);
+ g->self_suspended = 1;
+ }
+#endif
+}
+
+/**
+ * gpmi_self_wakeup - wakeup from self-pm light suspend
+ */
+static void gpmi_self_wakeup(struct gpmi_nand_data *g)
+{
+#ifdef CONFIG_PM
+ int i = 1000;
+ clk_enable(g->clk);
+ HW_GPMI_CTRL0_CLR(BM_GPMI_CTRL0_CLKGATE);
+ while (i-- && HW_GPMI_CTRL0_RD() & BM_GPMI_CTRL0_CLKGATE);
+
+ pr_debug("%s: i stopped at %d, data %p\n", __func__, i, g);
+ g->self_suspended = 0;
+ g->timer.expires = jiffies + 4*HZ;
+ add_timer(&g->timer);
+#endif
+}
+
+/**
+ * gpmi_set_timings - set GPMI timings
+ * @pdev: pointer to GPMI platform device
+ * @tm: pointer to structure &gpmi_nand_timing with new timings
+ *
+ * During initialization, GPMI uses safe sub-optimal timings, which
+ * can be changed after reading boot control blocks
+ */
+void gpmi_set_timings(struct platform_device *pdev, struct gpmi_nand_timing *tm)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ u32 period_ns = 1000000 / clk_get_rate(g->clk) + 1;
+ u32 address_cycles, data_setup_cycles;
+ u32 data_hold_cycles, data_sample_cycles;
+ u32 busy_timeout;
+ u32 t0;
+
+ if (g->self_suspended)
+ gpmi_self_wakeup(g);
+ g->use_count++;
+
+ g->timing = *tm;
+
+ address_cycles = gpmi_cycles_ceil(tm->address_setup, period_ns);
+ data_setup_cycles = gpmi_cycles_ceil(tm->data_setup, period_ns);
+ data_hold_cycles = gpmi_cycles_ceil(tm->data_hold, period_ns);
+ data_sample_cycles = gpmi_cycles_ceil(tm->dsample_time + period_ns / 4,
+ period_ns / 2);
+ busy_timeout = gpmi_cycles_ceil(10000000 / 4096, period_ns);
+
+ dev_dbg(&pdev->dev,
+ "%s: ADDR %u, DSETUP %u, DH %u, DSAMPLE %u, BTO %u\n",
+ __func__,
+ address_cycles, data_setup_cycles, data_hold_cycles,
+ data_sample_cycles, busy_timeout);
+
+ t0 = BF_GPMI_TIMING0_ADDRESS_SETUP(address_cycles) |
+ BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles) |
+ BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles);
+ HW_GPMI_TIMING0_WR(t0);
+
+ HW_GPMI_TIMING1_WR(BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(busy_timeout));
+
+#ifdef CONFIG_ARCH_STMP378X
+ HW_GPMI_CTRL1_CLR(BM_GPMI_CTRL1_RDN_DELAY);
+ HW_GPMI_CTRL1_SET(BF_GPMI_CTRL1_RDN_DELAY(data_sample_cycles));
+#else
+ HW_GPMI_CTRL1_CLR(BM_GPMI_CTRL1_DSAMPLE_TIME);
+ HW_GPMI_CTRL1_SET(BF_GPMI_CTRL1_DSAMPLE_TIME(data_sample_cycles));
+#endif
+
+ g->use_count--;
+}
+
+static inline u32 bch_mode(void)
+{
+ u32 c1 = 0;
+
+#ifdef CONFIG_MTD_NAND_GPMI_BCH
+ if (bch)
+ c1 |= BM_GPMI_CTRL1_BCH_MODE;
+#endif
+ return c1;
+}
+/**
+ * gpmi_nand_init_hw - initialize the hardware
+ * @pdev: pointer to platform device
+ *
+ * Initialize GPMI hardware and set default (safe) timings for NAND access.
+ * Returns error code or 0 on success
+ */
+static int gpmi_nand_init_hw(struct platform_device *pdev, int request_pins)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ char *devname = pdev->dev.bus_id;
+ int err = 0;
+
+ g->clk = clk_get(NULL, "gpmi");
+ if (IS_ERR(g->clk)) {
+ err = PTR_ERR(g->clk);
+ dev_err(&pdev->dev, "cannot set failsafe clockrate\n");
+ goto out;
+ }
+ clk_enable(g->clk);
+ if (clk <= 0)
+ clk = 24000; /* safe setting, some chips do not work on
+ speeds >= 24kHz */
+ clk_set_rate(g->clk, clk);
+
+ clk = clk_get_rate(g->clk);
+
+ if (request_pins)
+ gpmi_pinmux_request(devname);
+
+ stmp3xxx_reset_block(HW_GPMI_CTRL0_OFFSET + REGS_GPMI_BASE, 1);
+
+ /* this CLEARS reset, despite of its name */
+ HW_GPMI_CTRL1_SET(BM_GPMI_CTRL1_DEV_RESET);
+
+ /* IRQ polarity */
+ HW_GPMI_CTRL1_SET(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY);
+
+ /* ...and ECC module */
+ HW_GPMI_CTRL1_SET(bch_mode());
+
+ /* choose NAND mode (1 means ATA, 0 - NAND */
+ HW_GPMI_CTRL1_CLR(BM_GPMI_CTRL1_GPMI_MODE);
+
+out:
+ return err;
+}
+
+/**
+ * gpmi_nand_release_hw - free the hardware
+ * @pdev: pointer to platform device
+ *
+ * In opposite to gpmi_nand_init_hw, release all acquired resources
+ */
+static void gpmi_nand_release_hw(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+
+ HW_GPMI_CTRL0_SET(BM_GPMI_CTRL0_SFTRST);
+
+ clk_disable(g->clk);
+ clk_put(g->clk);
+ gpmi_pinmux_free(pdev->dev.bus_id);
+}
+
+static int gpmi_dma_is_error(struct gpmi_nand_data *g)
+{
+ /* u32 n = HW_APBH_CHn_NXTCMDAR_RD(g->dma_ch); */
+
+ /* CURrent DMA command */
+ u32 c = HW_APBH_CHn_NXTCMDAR_RD(g->cchip->dma_ch);
+
+ if (c == g->cchip->error.handle) {
+ pr_debug("%s: dma chain has reached error terminator\n",
+ __func__);
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * gpmi_dma_exchange - run DMA to exchange with NAND chip
+ *
+ * @g: structure associated with NAND chip
+ *
+ * Run DMA and wait for completion
+ */
+static int gpmi_dma_exchange(struct gpmi_nand_data *g,
+ struct stmp3xxx_dma_descriptor *d)
+{
+ struct platform_device *pdev = g->dev;
+ unsigned long timeout;
+ int err;
+
+ if (g->self_suspended)
+ gpmi_self_wakeup(g);
+ g->use_count++;
+
+ if (!g->regulator) {
+ g->regulator = regulator_get(&pdev->dev, "mmc_ssp-2");
+ if (g->regulator && !IS_ERR(g->regulator))
+ regulator_set_mode(
+ g->regulator,
+ REGULATOR_MODE_NORMAL);
+ else
+ g->regulator = NULL;
+ }
+
+ if (g->regulator)
+ regulator_set_current_limit(g->regulator, g->reg_uA, g->reg_uA);
+
+ init_completion(&g->done);
+ stmp3xxx_dma_enable_interrupt(g->cchip->dma_ch);
+ stmp3xxx_dma_go(g->cchip->dma_ch, d ? d : g->cchip->d, 1);
+
+ timeout = wait_for_completion_timeout(&g->done, msecs_to_jiffies(1000));
+ err = (timeout <= 0) ? -ETIMEDOUT : gpmi_dma_is_error(g);
+
+ if (err)
+ printk(KERN_ERR"%s: error %d, CS = %d, channel %d\n",
+ __func__, err, g->cchip->cs, g->cchip->dma_ch);
+
+ stmp3xxx_dma_reset_channel(g->cchip->dma_ch);
+ stmp3xxx_dma_clear_interrupt(g->cchip->dma_ch);
+
+ if (g->regulator)
+ regulator_set_current_limit(g->regulator, 0, 0);
+
+ mod_timer(&g->timer, jiffies + 4*HZ);
+ g->use_count--;
+
+ return err;
+}
+
+/**
+ * gpmi_ecc_read_page - replacement for nand_read_page
+ *
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ */
+static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf)
+{
+ struct gpmi_nand_data *g = chip->priv;
+ struct mtd_ecc_stats stats;
+ dma_addr_t bufphys, oobphys;
+ int err;
+
+ bufphys = oobphys = ~0;
+
+ if (map_buffers && virt_addr_valid(buf))
+ bufphys = dma_map_single(&g->dev->dev, buf,
+ mtd->writesize, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, bufphys))
+ bufphys = g->data_buffer_handle;
+
+ if (map_buffers)
+ oobphys = dma_map_single(&g->dev->dev, chip->oob_poi,
+ mtd->oobsize, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, oobphys))
+ oobphys = g->oob_buffer_handle;
+
+ /* ECC read */
+ (void)g->hc->read(g->hc, g->selected_chip, g->cchip->d,
+ g->cchip->error.handle,
+ bufphys, oobphys);
+
+ err = gpmi_dma_exchange(g, NULL);
+
+ g->hc->stat(g->hc, g->selected_chip, &stats);
+
+ if (stats.failed || stats.corrected) {
+
+ pr_debug("%s: ECC failed=%d, corrected=%d\n",
+ __func__, stats.failed, stats.corrected);
+
+ g->mtd.ecc_stats.failed += stats.failed;
+ g->mtd.ecc_stats.corrected += stats.corrected;
+ }
+
+
+ if (!dma_mapping_error(&g->dev->dev, oobphys)) {
+ if (oobphys != g->oob_buffer_handle)
+ dma_unmap_single(&g->dev->dev, oobphys,
+ mtd->oobsize, DMA_FROM_DEVICE);
+ else {
+ memcpy(chip->oob_poi, g->oob_buffer, mtd->oobsize);
+ copies++;
+ }
+ }
+
+ if (!dma_mapping_error(&g->dev->dev, bufphys)) {
+ if (bufphys != g->data_buffer_handle)
+ dma_unmap_single(&g->dev->dev, bufphys,
+ mtd->writesize, DMA_FROM_DEVICE);
+ else {
+ memcpy(buf, g->data_buffer, mtd->writesize);
+ copies++;
+ }
+ }
+
+ /* always fill the (possible ECC bytes with FF) */
+ memset(chip->oob_poi + g->oob_free, 0xff, mtd->oobsize - g->oob_free);
+
+ return err;
+}
+
+static inline int is_ff(const u8 *buffer, size_t size)
+{
+ while (size--) {
+ if (*buffer++ != 0xff)
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * gpmi_ecc_write_page - replacement for nand_write_page
+ *
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ */
+static void gpmi_ecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf)
+{
+ struct gpmi_nand_data *g = chip->priv;
+ dma_addr_t bufphys, oobphys;
+ int err;
+
+ /* if we can't map it, copy it */
+ bufphys = oobphys = ~0;
+
+ if (map_buffers && virt_addr_valid(buf))
+ bufphys = dma_map_single(&g->dev->dev,
+ (void *)buf, mtd->writesize, DMA_TO_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, bufphys)) {
+ bufphys = g->data_buffer_handle;
+ memcpy(g->data_buffer, buf, mtd->writesize);
+ copies++;
+ }
+
+ /* if OOB is all FF, leave it as such */
+ if (!is_ff(chip->oob_poi, mtd->oobsize)) {
+ if (map_buffers)
+ oobphys = dma_map_single(&g->dev->dev, chip->oob_poi,
+ mtd->oobsize, DMA_TO_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, oobphys)) {
+ oobphys = g->oob_buffer_handle;
+ memcpy(g->oob_buffer, chip->oob_poi, mtd->oobsize);
+ copies++;
+ }
+ } else
+ ff_writes++;
+
+ /* call ECC */
+ g->hc->write(g->hc, g->selected_chip, g->cchip->d,
+ g->cchip->error.handle,
+ bufphys, oobphys);
+
+ err = gpmi_dma_exchange(g, NULL);
+ if (err < 0)
+ printk(KERN_ERR"%s: dma error\n", __func__);
+
+ if (!dma_mapping_error(&g->dev->dev, oobphys)) {
+ if (oobphys != g->oob_buffer_handle)
+ dma_unmap_single(&g->dev->dev, oobphys, mtd->oobsize,
+ DMA_TO_DEVICE);
+ }
+
+ if (bufphys != g->data_buffer_handle)
+ dma_unmap_single(&g->dev->dev, bufphys, mtd->writesize,
+ DMA_TO_DEVICE);
+}
+
+/**
+ * gpmi_write_buf - replacement for nand_write_buf
+ *
+ * @mtd: MTD device
+ * @buf: data buffer
+ * @len: length of the data buffer
+ */
+static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ struct stmp3xxx_dma_descriptor *chain = g->cchip->d;
+ dma_addr_t phys;
+ int err;
+
+ BUG_ON(len > mtd->writesize);
+
+ phys = ~0;
+
+ if (map_buffers && virt_addr_valid(buf))
+ phys = dma_map_single(&g->dev->dev,
+ (void *)buf, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, phys)) {
+ phys = g->write_buffer_handle;
+ memcpy(g->write_buffer, buf, len);
+ copies++;
+ }
+
+ /* write plain data */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_XFER_COUNT(len) |
+ BF_APBH_CHn_CMD_CMDWORDS(4) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__DMA_READ);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF_GPMI_CTRL0_CS(g->selected_chip) |
+ BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) |
+ BF_GPMI_CTRL0_XFER_COUNT(len);
+
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = phys;
+
+ err = gpmi_dma_exchange(g, NULL);
+ if (err)
+ printk(KERN_ERR"%s: dma error\n", __func__);
+
+ if (phys != g->write_buffer_handle)
+ dma_unmap_single(&g->dev->dev, phys, len, DMA_TO_DEVICE);
+
+ if (debug >= 2)
+ print_hex_dump_bytes("WBUF ", DUMP_PREFIX_OFFSET, buf, len);
+}
+
+/**
+ * gpmi_read_buf - replacement for nand_read_buf
+ *
+ * @mtd: MTD device
+ * @buf: pointer to the buffer
+ * @len: size of the buffer
+ */
+static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ struct stmp3xxx_dma_descriptor *chain;
+ dma_addr_t phys;
+ int err;
+
+ phys = ~0;
+
+ if (map_buffers && virt_addr_valid(buf))
+ phys = dma_map_single(&g->dev->dev,
+ buf, len, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, phys))
+ phys = g->read_buffer_handle;
+
+ chain = g->cchip->d;
+
+ /* read data */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_XFER_COUNT(len) |
+ BF_APBH_CHn_CMD_CMDWORDS(1) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__DMA_WRITE);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF_GPMI_CTRL0_CS(g->selected_chip) |
+ BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) |
+ BF_GPMI_CTRL0_XFER_COUNT(len);
+ chain->command->buf_ptr = phys;
+ chain++;
+
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(4) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(
+ BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF_GPMI_CTRL0_CS(g->selected_chip);
+ chain->command->pio_words[1] =
+ chain->command->pio_words[2] =
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = 0;
+
+ err = gpmi_dma_exchange(g, NULL);
+ if (err)
+ printk(KERN_ERR"%s: dma error\n", __func__);
+
+ if (phys != g->read_buffer_handle)
+ dma_unmap_single(&g->dev->dev, phys, len, DMA_FROM_DEVICE);
+ else {
+ memcpy(buf, g->read_buffer, len);
+ copies++;
+ }
+
+ if (debug >= 2)
+ print_hex_dump_bytes("RBUF ", DUMP_PREFIX_OFFSET, buf, len);
+}
+
+/**
+ * gpmi_read_byte - replacement for nand_read_byte
+ * @mtd: MTD device
+ *
+ * Uses gpmi_read_buf to read 1 byte from device
+ */
+static u8 gpmi_read_byte(struct mtd_info *mtd)
+{
+ u8 b;
+
+ gpmi_read_buf(mtd, (uint8_t *)&b, 1);
+ return b;
+}
+
+/**
+ * gpmi_read_word - replacement for nand_read_word
+ * @mtd: MTD device
+ *
+ * Uses gpmi_read_buf to read 2 bytes from device
+ */
+static u16 gpmi_read_word(struct mtd_info *mtd)
+{
+ u16 w;
+
+ gpmi_read_buf(mtd, (uint8_t *)&w, sizeof(u16));
+ return w;
+}
+
+/**
+ * gpmi_erase - erase a block and update BBT table
+ *
+ * @mtd: MTD device
+ * @instr: erase instruction
+ */
+int gpmi_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ int rc;
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ struct gpmi_nand_data *data = platform_get_drvdata(g->dev);
+
+ if (g->self_suspended)
+ gpmi_self_wakeup(data);
+ g->use_count++;
+
+ rc = nand_erase_nand(mtd, instr, 0);
+
+ if (rc == -EIO) /* block cannot be erased */
+ gpmi_block_mark_as(chip,
+ (instr->addr >> chip->bbt_erase_shift),
+ 0x01);
+
+ mod_timer(&g->timer, jiffies + 4*HZ);
+ g->use_count--;
+ return rc;
+}
+
+/**
+ * gpmi_dev_ready - poll the RDY pin
+ *
+ * @mtd: MTD device
+ */
+static int gpmi_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ struct stmp3xxx_dma_descriptor *chain = g->cchip->d;
+ int ret;
+
+ /* wait for ready */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(4) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF_APBH_CHn_CMD_COMMAND(
+ BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(
+ BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_ADDRESS(
+ BV_GPMI_CTRL0_ADDRESS__NAND_DATA) |
+ BF_GPMI_CTRL0_CS(g->selected_chip);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = 0;
+ chain++;
+
+ ret = gpmi_dma_exchange(g, NULL);
+ if (ret != 0)
+ printk(KERN_ERR "gpmi: gpmi_dma_exchange() timeout!\n");
+ return ret == 0;
+}
+
+/**
+ * gpmi_hwcontrol - set command/address byte to the device
+ *
+ * @mtd: MTD device
+ * @cmd: command byte
+ * @ctrl: control flags
+ */
+static void gpmi_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ struct stmp3xxx_dma_descriptor *chain = g->cchip->d;
+ int ret;
+
+ if ((ctrl & (NAND_ALE | NAND_CLE))) {
+ if (cmd != NAND_CMD_NONE)
+ g->cmd_buffer[g->cmd_buffer_sz++] = cmd;
+ return;
+ }
+
+ if (g->cmd_buffer_sz == 0)
+ return;
+
+ /* output command */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_XFER_COUNT(g->cmd_buffer_sz) |
+ BF_APBH_CHn_CMD_CMDWORDS(3) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__DMA_READ);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF_GPMI_CTRL0_CS(g->selected_chip) |
+ BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) |
+ BF_GPMI_CTRL0_XFER_COUNT(g->cmd_buffer_sz);
+ if (g->cmd_buffer_sz > 0)
+ chain->command->pio_words[0] |= BM_GPMI_CTRL0_ADDRESS_INCREMENT;
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->buf_ptr = g->cmd_buffer_handle;
+ chain++;
+
+ /* emit IRQ */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(0) |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD;
+ chain++;
+
+ /* last in chain get the irq bit set */
+ chain[-1].command->cmd |= BM_APBH_CHn_CMD_IRQONCMPLT;
+
+ if (debug >= 3)
+ print_hex_dump(KERN_INFO, "CMD ", DUMP_PREFIX_OFFSET, 16, 1,
+ g->cmd_buffer, g->cmd_buffer_sz, 1);
+
+ ret = gpmi_dma_exchange(g, NULL);
+ if (ret != 0) {
+ printk(KERN_ERR"%s: chip %d, dma error %d on the command:\n",
+ __func__, g->selected_chip, ret);
+ print_hex_dump(KERN_INFO, "CMD ", DUMP_PREFIX_OFFSET, 16, 1,
+ g->cmd_buffer, g->cmd_buffer_sz, 1);
+ }
+
+ gpmi_dev_ready(mtd);
+
+ g->cmd_buffer_sz = 0;
+}
+
+/**
+ * gpmi_alloc_buffers - allocate DMA buffers for one chip
+ *
+ * @pdev: GPMI platform device
+ * @g: pointer to structure associated with NAND chip
+ *
+ * Allocate buffer using dma_alloc_coherent
+ */
+static int gpmi_alloc_buffers(struct platform_device *pdev,
+ struct gpmi_nand_data *g)
+{
+ g->cmd_buffer = dma_alloc_coherent(&pdev->dev,
+ g->cmd_buffer_size,
+ &g->cmd_buffer_handle, GFP_DMA);
+ if (!g->cmd_buffer)
+ goto out1;
+
+ g->write_buffer = dma_alloc_coherent(&pdev->dev,
+ g->write_buffer_size * 2,
+ &g->write_buffer_handle, GFP_DMA);
+ if (!g->write_buffer)
+ goto out2;
+
+ g->read_buffer = g->write_buffer + g->write_buffer_size;
+ g->read_buffer_handle = g->write_buffer_handle + g->write_buffer_size;
+
+ g->data_buffer = dma_alloc_coherent(&pdev->dev,
+ g->data_buffer_size,
+ &g->data_buffer_handle, GFP_DMA);
+ if (!g->data_buffer)
+ goto out3;
+
+ g->oob_buffer = dma_alloc_coherent(&pdev->dev,
+ g->oob_buffer_size,
+ &g->oob_buffer_handle, GFP_DMA);
+ if (!g->oob_buffer)
+ goto out4;
+
+ g->verify_buffer = kzalloc(2 * (g->data_buffer_size +
+ g->oob_buffer_size), GFP_KERNEL);
+ if (!g->verify_buffer)
+ goto out5;
+
+ return 0;
+
+out5:
+ dma_free_coherent(&pdev->dev, g->oob_buffer_size,
+ g->oob_buffer, g->oob_buffer_handle);
+out4:
+ dma_free_coherent(&pdev->dev, g->data_buffer_size,
+ g->data_buffer, g->data_buffer_handle);
+out3:
+ dma_free_coherent(&pdev->dev, g->write_buffer_size * 2,
+ g->write_buffer, g->write_buffer_handle);
+out2:
+ dma_free_coherent(&pdev->dev, g->cmd_buffer_size,
+ g->cmd_buffer, g->cmd_buffer_handle);
+out1:
+ return -ENOMEM;
+}
+
+/**
+ * gpmi_free_buffers - free buffers allocated by gpmi_alloc_buffers
+ *
+ * @pdev: platform device
+ * @g: pointer to structure associated with NAND chip
+ *
+ * Deallocate buffers on exit
+ */
+static void gpmi_free_buffers(struct platform_device *pdev,
+ struct gpmi_nand_data *g)
+{
+ kfree(g->verify_buffer);
+ dma_free_coherent(&pdev->dev, g->oob_buffer_size,
+ g->oob_buffer, g->oob_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->write_buffer_size * 2,
+ g->write_buffer, g->write_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->cmd_buffer_size,
+ g->cmd_buffer, g->cmd_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->data_buffer_size,
+ g->data_buffer, g->data_buffer_handle);
+}
+
+/* only used in SW-ECC or NO-ECC cases */
+static int gpmi_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+
+ chip->read_buf(mtd, g->verify_buffer, len);
+
+ if (memcmp(buf, g->verify_buffer, len))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * gpmi_ecc_read_oob - replacement for nand_read_oob
+ *
+ * @mtd: MTD device
+ * @chip: mtd->priv
+ * @page: page address
+ * @sndcmd: flag indicates that command should be sent
+ */
+int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int sndcmd)
+{
+ struct gpmi_nand_data *g = chip->priv;
+ loff_t oob_offset;
+ struct mtd_ecc_stats stats;
+ dma_addr_t oobphys;
+ int ecc;
+ int ret;
+
+ ecc = g->raw_oob_mode == 0 && raw_mode == 0;
+
+ if (sndcmd) {
+ oob_offset = mtd->writesize;
+ if (likely(ecc))
+ oob_offset += chip->ecc.bytes * chip->ecc.steps;
+ chip->cmdfunc(mtd, NAND_CMD_READ0, oob_offset, page);
+ sndcmd = 0;
+ }
+
+ if (unlikely(!ecc)) {
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ return 1;
+ }
+
+ oobphys = ~0;
+
+ if (map_buffers)
+ oobphys = dma_map_single(&g->dev->dev, chip->oob_poi,
+ mtd->oobsize, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, oobphys))
+ oobphys = g->oob_buffer_handle;
+
+ /* ECC read */
+ (void)g->hc->read(g->hc, g->selected_chip, g->cchip->d,
+ g->cchip->error.handle, ~0, oobphys);
+
+ ret = gpmi_dma_exchange(g, NULL);
+
+ g->hc->stat(g->hc, g->selected_chip, &stats);
+
+ if (stats.failed || stats.corrected) {
+
+ printk(KERN_DEBUG "%s: ECC failed=%d, corrected=%d\n",
+ __func__, stats.failed, stats.corrected);
+
+ g->mtd.ecc_stats.failed += stats.failed;
+ g->mtd.ecc_stats.corrected += stats.corrected;
+ }
+
+ if (oobphys != g->oob_buffer_handle)
+ dma_unmap_single(&g->dev->dev, oobphys, mtd->oobsize,
+ DMA_FROM_DEVICE);
+ else {
+ memcpy(chip->oob_poi, g->oob_buffer, mtd->oobsize);
+ copies++;
+ }
+
+ /* fill rest with ff */
+ memset(chip->oob_poi + g->oob_free, 0xff, mtd->oobsize - g->oob_free);
+
+ return ret ? ret : 1;
+}
+
+/**
+ * gpmi_ecc_write_oob - replacement for nand_write_oob
+ *
+ * @mtd: MTD device
+ * @chip: mtd->priv
+ * @page: page address
+ */
+static int gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int status = 0;
+ struct gpmi_nand_data *g = chip->priv;
+ loff_t oob_offset;
+ dma_addr_t oobphys;
+ int ecc;
+ int err = 0;
+
+ /* if OOB is all FF, leave it as such */
+ if (is_ff(chip->oob_poi, mtd->oobsize)) {
+ ff_writes++;
+
+ pr_debug("%s: Skipping an empty page 0x%x (0x%x)\n",
+ __func__,
+ page, page << chip->page_shift);
+ return 0;
+ }
+
+ ecc = g->raw_oob_mode == 0 && raw_mode == 0;
+
+ /* Send command to start input data */
+ oob_offset = mtd->writesize;
+ if (likely(ecc)) {
+ oob_offset += chip->ecc.bytes * chip->ecc.steps;
+ memset(chip->oob_poi + g->oob_free, 0xff,
+ mtd->oobsize - g->oob_free);
+ }
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, oob_offset, page);
+
+ /* call ECC */
+ if (likely(ecc)) {
+
+ oobphys = ~0;
+
+ if (map_buffers)
+ oobphys = dma_map_single(&g->dev->dev, chip->oob_poi,
+ mtd->oobsize, DMA_TO_DEVICE);
+ if (dma_mapping_error(&g->dev->dev, oobphys)) {
+ oobphys = g->oob_buffer_handle;
+ memcpy(g->oob_buffer, chip->oob_poi, mtd->oobsize);
+ copies++;
+ }
+
+ g->hc->write(g->hc, g->selected_chip, g->cchip->d,
+ g->cchip->error.handle, ~0, oobphys);
+
+ err = gpmi_dma_exchange(g, NULL);
+ } else
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ /* Send command to program the OOB data */
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ /* ..and wait for result */
+ status = chip->waitfunc(mtd, chip);
+
+ if (likely(ecc)) {
+ if (oobphys != g->oob_buffer_handle)
+ dma_unmap_single(&g->dev->dev, oobphys, mtd->oobsize,
+ DMA_TO_DEVICE);
+ }
+
+ if (status & NAND_STATUS_FAIL) {
+ pr_debug("%s: NAND_STATUS_FAIL\n", __func__);
+ return -EIO;
+ }
+
+ return err;
+}
+
+/**
+ * gpmi_irq - IRQ handler
+ *
+ * @irq: irq no
+ * @context: IRQ context, pointer to gpmi_nand_data
+ */
+static irqreturn_t gpmi_irq(int irq, void *context)
+{
+ struct gpmi_nand_data *g = context;
+
+ if (stmp3xxx_dma_is_interrupt(g->cchip->dma_ch)) {
+ stmp3xxx_dma_clear_interrupt(g->cchip->dma_ch);
+ complete(&g->done);
+ }
+ HW_GPMI_CTRL1_CLR(BM_GPMI_CTRL1_DEV_IRQ | BM_GPMI_CTRL1_TIMEOUT_IRQ);
+ return IRQ_HANDLED;
+}
+
+static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+
+ if (chipnr == g->selected_chip)
+ return;
+
+ g->selected_chip = chipnr;
+ g->cchip = NULL;
+
+ if (chipnr == -1)
+ return;
+
+ g->cchip = g->chips + chipnr;
+}
+
+static void gpmi_command(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+
+ g->saved_command(mtd, command, column, page_addr);
+}
+
+static int gpmi_read_oob(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ int ret;
+
+ g->raw_oob_mode = ops->mode == MTD_OOB_RAW;
+ ret = g->saved_read_oob(mtd, from, ops);
+ g->raw_oob_mode = 0;
+ return ret;
+}
+
+static int gpmi_write_oob(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ register struct nand_chip *chip = mtd->priv;
+ struct gpmi_nand_data *g = chip->priv;
+ int ret;
+
+ g->raw_oob_mode = ops->mode == MTD_OOB_RAW;
+ ret = g->saved_write_oob(mtd, to, ops);
+ g->raw_oob_mode = 0;
+ return ret;
+}
+
+/**
+ * perform the needed steps between nand_scan_ident and nand_scan_tail
+ */
+static int gpmi_scan_middle(struct gpmi_nand_data *g)
+{
+ int oobsize = 0;
+
+ /* Limit to 2G size due to Kernel larger 4G space support */
+ if (g->mtd.size == 0) {
+ g->mtd.size = 1 << 31;
+ g->chip.chipsize = g->mtd.size / g->chip.numchips;
+ }
+
+ g->ecc_oob_bytes = 9;
+ switch (g->mtd.writesize) {
+ case 2048: /* 2K page */
+ g->chip.ecc.layout = &gpmi_oob_64;
+ g->chip.ecc.bytes = 9;
+ g->oob_free = 19;
+ g->hwecc_type_read = GPMI_ECC4_RD;
+ g->hwecc_type_write = GPMI_ECC4_WR;
+ oobsize = 64;
+ break;
+ case 4096:
+ g->chip.ecc.layout = &gpmi_oob_128;
+ g->chip.ecc.bytes = 18;
+ g->oob_free = 65;
+ g->hwecc_type_read = GPMI_ECC8_RD;
+ g->hwecc_type_write = GPMI_ECC8_WR;
+ oobsize = 218;
+ break;
+ default:
+ printk(KERN_ERR "Unsupported write_size %d.",
+ g->mtd.writesize);
+ break;
+ }
+
+ g->mtd.ecclayout = g->chip.ecc.layout;
+ /* sanity check */
+ if (oobsize > NAND_MAX_OOBSIZE ||
+ g->mtd.writesize > NAND_MAX_PAGESIZE) {
+ printk(KERN_ERR "Internal error. Either page size "
+ "(%d) > max (%d) "
+ "or oob size (%d) > max(%d). Sorry.\n",
+ oobsize, NAND_MAX_OOBSIZE,
+ g->mtd.writesize, NAND_MAX_PAGESIZE);
+ return -ERANGE;
+ }
+
+ g->saved_command = g->chip.cmdfunc;
+ g->chip.cmdfunc = gpmi_command;
+
+ if (oobsize > 0) {
+ g->mtd.oobsize = oobsize;
+ /* otherwise error; oobsize should be set
+ in valid cases */
+ g->hc = gpmi_hwecc_chip_find("ecc8");
+ g->hc->setup(g->hc, 0, g->mtd.writesize, g->mtd.oobsize);
+ return 0;
+ }
+
+ return -ENXIO;
+}
+
+/**
+ * gpmi_write_page - [REPLACEABLE] write one page
+ * @mtd: MTD device structure
+ * @chip: NAND chip descriptor
+ * @buf: the data to write
+ * @page: page number to write
+ * @cached: cached programming
+ * @raw: use _raw version of write_page
+ */
+static int gpmi_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int page, int cached, int raw)
+{
+ struct gpmi_nand_data *g = chip->priv;
+ int status, empty_data, empty_oob;
+ int oobsz;
+#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
+ void *vbuf, *obuf;
+#if 0
+ void *voob, *ooob;
+#endif
+#endif
+
+ oobsz = likely(g->raw_oob_mode == 0 && raw_mode == 0) ?
+ g->oob_free : mtd->oobsize;
+
+ empty_data = is_ff(buf, mtd->writesize);
+ empty_oob = is_ff(buf, oobsz);
+
+ if (empty_data && empty_oob) {
+ ff_writes++;
+
+ pr_debug("%s: Skipping an empty page 0x%x (0x%x)\n",
+ __func__,
+ page, page << chip->page_shift);
+ return 0;
+ }
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+ if (likely(raw == 0))
+ chip->ecc.write_page(mtd, chip, buf);
+ else
+ chip->ecc.write_page_raw(mtd, chip, buf);
+
+ /*
+ * Cached progamming disabled for now, Not sure if its worth the
+ * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
+ */
+ cached = 0;
+
+ if (!cached || !(chip->options & NAND_CACHEPRG)) {
+
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+ status = chip->waitfunc(mtd, chip);
+
+ /*
+ * See if operation failed and additional status checks are
+ * available
+ */
+ if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+ status = chip->errstat(mtd, chip, FL_WRITING, status,
+ page);
+
+ if (status & NAND_STATUS_FAIL) {
+ pr_debug("%s: NAND_STATUS_FAIL\n", __func__);
+ return -EIO;
+ }
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+ }
+
+#if defined(CONFIG_MTD_NAND_VERIFY_WRITE)
+ if (empty_data)
+ return 0;
+
+ obuf = g->verify_buffer;
+#if 1 /* make vbuf aligned by mtd->writesize */
+ vbuf = obuf + mtd->writesize;
+#else
+ ooob = obuf + mtd->writesize;
+ vbuf = ooob + mtd->oobsize;
+ voob = vbuf + mtd->writesize;
+#endif
+
+ /* keep data around */
+ memcpy(obuf, buf, mtd->writesize);
+#if 0
+ memcpy(ooob, chip->oob_poi, oobsz);
+#endif
+ /* Send command to read back the data */
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+ if (likely(raw == 0))
+ chip->ecc.read_page(mtd, chip, vbuf);
+ else
+ chip->ecc.read_page_raw(mtd, chip, vbuf);
+
+#if 0
+ memcpy(voob, chip->oob_poi, oobsz);
+#endif
+
+ if (!empty_data && memcmp(obuf, vbuf, mtd->writesize) != 0)
+ return -EIO;
+#endif
+
+ return 0;
+}
+
+/**
+ * gpmi_read_page_raw - [Intern] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ */
+static int gpmi_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf)
+{
+ chip->read_buf(mtd, buf, mtd->writesize);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ return 0;
+}
+
+/**
+ * gpmi_write_page_raw - [Intern] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ */
+static void gpmi_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf)
+{
+ chip->write_buf(mtd, buf, mtd->writesize);
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
+
+static int gpmi_init_chip(struct platform_device *pdev,
+ struct gpmi_nand_data *g, int n, unsigned dma_ch)
+{
+ int err;
+
+ g->chips[n].dma_ch = dma_ch;
+ g->chips[n].cs = n;
+
+ err = stmp3xxx_dma_request(dma_ch, NULL, pdev->dev.bus_id);
+ if (err) {
+ dev_err(&pdev->dev,
+ "can't request DMA channel 0x%x\n", dma_ch);
+ goto out_all;
+ }
+
+ err = stmp3xxx_dma_make_chain(
+ dma_ch,
+ &g->chips[n].chain,
+ g->chips[n].d, ARRAY_SIZE(g->chips[n].d));
+ if (err) {
+ dev_err(&pdev->dev, "can't setup DMA chain\n");
+ goto out_all;
+ }
+
+ err = stmp3xxx_dma_allocate_command(
+ dma_ch,
+ &g->chips[n].error);
+ if (err) {
+ dev_err(&pdev->dev, "can't setup DMA chain\n");
+ goto out_all;
+ }
+
+ g->chips[n].error.command->cmd =
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+out_all:
+ return err;
+}
+
+static void gpmi_deinit_chip(struct platform_device *pdev,
+ struct gpmi_nand_data *g, int n)
+{
+ int dma_ch;
+
+ if (n < 0) {
+ for (n = 0; n < ARRAY_SIZE(g->chips); n++)
+ gpmi_deinit_chip(pdev, g, n);
+ return;
+ }
+
+ if (g->chips[n].dma_ch <= 0)
+ return;
+
+ dma_ch = g->chips[n].dma_ch;
+
+ stmp3xxx_dma_free_command(dma_ch, &g->chips[n].error);
+ stmp3xxx_dma_free_chain(&g->chips[n].chain);
+ stmp3xxx_dma_release(dma_ch);
+}
+
+static int gpmi_to_concat(struct mtd_partition *part, char **list)
+{
+ while (list && *list) {
+ if (strcmp(part->name, *list) == 0) {
+ pr_debug("Partition '%s' will be concatenated\n",
+ part->name);
+ return true;
+ }
+ list++;
+ }
+ pr_debug("Partition '%s' is left as-is", part->name);
+ return false;
+}
+
+static void gpmi_create_partitions(struct gpmi_nand_data *g,
+ struct gpmi_platform_data *gpd, uint64_t chipsize)
+{
+#ifdef CONFIG_MTD_PARTITIONS
+ int chip, p;
+ char chipname[20];
+
+ if (g->numchips == 1)
+ g->masters[0] = &g->mtd;
+ else {
+ for (chip = 0; chip < g->numchips; chip++) {
+ memset(g->chip_partitions + chip,
+ 0, sizeof(g->chip_partitions[chip]));
+ snprintf(chipname, sizeof(chipname),
+ "gpmi-chip-%d", chip);
+ g->chip_partitions[chip].name =
+ kstrdup(chipname, GFP_KERNEL);
+ g->chip_partitions[chip].size = chipsize;
+ g->chip_partitions[chip].offset = chipsize * chip;
+ g->chip_partitions[chip].mask_flags = 0;
+ g->chip_partitions[chip].mtdp = &g->masters[chip];
+ }
+ add_mtd_partitions(&g->mtd, g->chip_partitions, g->numchips);
+ }
+ g->n_concat = 0;
+ memset(g->concat, 0, sizeof(g->concat));
+ for (chip = 0; chip < g->numchips; chip++) {
+ if (add_mtd_chip) {
+ printk(KERN_NOTICE"Adding MTD for the chip %d\n", chip);
+ add_mtd_device(g->masters[chip]);
+ }
+ if (chip >= gpd->items)
+ continue;
+ for (p = 0; p < gpd->parts[chip].nr_partitions; p++) {
+ if (gpmi_to_concat(&gpd->parts[chip].partitions[p],
+ gpd->concat_parts))
+ gpd->parts[chip].partitions[p].mtdp =
+ &g->concat[g->n_concat++];
+ }
+ add_mtd_partitions(g->masters[chip],
+ gpd->parts[chip].partitions,
+ gpd->parts[chip].nr_partitions);
+ }
+ if (g->n_concat > 0) {
+#ifdef CONFIG_MTD_CONCAT
+ if (g->n_concat == 1)
+#endif
+ for (p = 0; p < g->n_concat; p++)
+ add_mtd_device(g->concat[p]);
+#ifdef CONFIG_MTD_CONCAT
+ if (g->n_concat > 1) {
+ g->concat_mtd = mtd_concat_create(g->concat,
+ g->n_concat, gpd->concat_name);
+ if (g->concat_mtd)
+ add_mtd_device(g->concat_mtd);
+ }
+#endif
+ }
+ g->custom_partitions = true;
+#endif
+}
+
+static void gpmi_delete_partitions(struct gpmi_nand_data *g)
+{
+#ifdef CONFIG_MTD_PARTITIONS
+ int chip, p;
+
+ if (!g->custom_partitions)
+ return;
+#ifdef CONFIG_MTD_CONCAT
+ if (g->concat_mtd)
+ del_mtd_device(g->concat_mtd);
+ if (g->n_concat == 1)
+#endif
+ for (p = 0; p < g->n_concat; p++)
+ del_mtd_device(g->concat[p]);
+
+ for (chip = 0; chip < g->numchips; chip++) {
+ del_mtd_partitions(g->masters[chip]);
+ if (add_mtd_chip)
+ del_mtd_device(g->masters[chip]);
+ kfree(g->chip_partitions[chip].name);
+ }
+#endif
+}
+/**
+ * gpmi_nand_probe - probe for the GPMI device
+ *
+ * Probe for GPMI device and discover NAND chips
+ */
+static int __init gpmi_nand_probe(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *g;
+ struct gpmi_platform_data *gpd;
+ const char *part_type = 0;
+ int err = 0;
+ struct resource *r;
+ int dma;
+ unsigned long long chipsize;
+
+ /* Allocate memory for the device structure (and zero it) */
+ g = kzalloc(sizeof(*g), GFP_KERNEL);
+ if (!g) {
+ dev_err(&pdev->dev, "failed to allocate gpmi_nand_data\n");
+ err = -ENOMEM;
+ goto out1;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get resource\n");
+ err = -ENXIO;
+ goto out2;
+ }
+ g->io_base = ioremap(r->start, r->end - r->start + 1);
+ if (!g->io_base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ err = -EIO;
+ goto out2;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!r) {
+ err = -EIO;
+ dev_err(&pdev->dev, "can't get IRQ resource\n");
+ goto out3;
+ }
+
+ gpd = (struct gpmi_platform_data *)pdev->dev.platform_data;
+ platform_set_drvdata(pdev, g);
+ err = gpmi_nand_init_hw(pdev, 1);
+ if (err)
+ goto out3;
+
+ init_timer(&g->timer);
+ g->timer.data = (unsigned long)g;
+ g->timer.function = gpmi_timer_expiry;
+ g->timer.expires = jiffies + 4*HZ;
+ add_timer(&g->timer);
+ dev_dbg(&pdev->dev, "%s: timer set to %ld\n",
+ __func__, jiffies + 4*HZ);
+
+ g->reg_uA = gpd->io_uA;
+ g->regulator = regulator_get(&pdev->dev, "mmc_ssp-2");
+ if (g->regulator && !IS_ERR(g->regulator)) {
+ regulator_set_mode(
+ g->regulator,
+ REGULATOR_MODE_NORMAL);
+ } else
+ g->regulator = NULL;
+
+ gpmi_set_timings(pdev, &gpmi_safe_timing);
+
+ g->irq = r->start;
+ err = request_irq(g->irq,
+ gpmi_irq, 0, pdev->dev.bus_id, g);
+ if (err) {
+ dev_err(&pdev->dev, "can't request GPMI IRQ\n");
+ goto out4;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "can't get DMA resource\n");
+ goto out5;
+ }
+
+ if (r->end - r->start > GPMI_MAX_CHIPS)
+ dev_info(&pdev->dev, "too spread resource: max %d chips\n",
+ GPMI_MAX_CHIPS);
+
+ for (dma = r->start;
+ dma < min_t(int, r->end, r->start + GPMI_MAX_CHIPS);
+ dma++) {
+ err = gpmi_init_chip(pdev, g, dma - r->start, dma);
+ if (err)
+ goto out6;
+ }
+
+ g->cmd_buffer_size = GPMI_CMD_BUF_SZ;
+ g->write_buffer_size = GPMI_WRITE_BUF_SZ;
+ g->data_buffer_size = GPMI_DATA_BUF_SZ;
+ g->oob_buffer_size = GPMI_OOB_BUF_SZ;
+
+ err = gpmi_alloc_buffers(pdev, g);
+ if (err) {
+ dev_err(&pdev->dev, "can't setup buffers\n");
+ goto out6;
+ }
+
+ g->dev = pdev;
+ g->chip.priv = g;
+ g->timing = gpmi_safe_timing;
+ g->selected_chip = -1;
+ g->ignorebad = ignorebad; /* copy global setting */
+
+ g->mtd.priv = &g->chip;
+ g->mtd.name = pdev->dev.bus_id;
+ g->mtd.owner = THIS_MODULE;
+
+ g->chip.cmd_ctrl = gpmi_hwcontrol;
+ g->chip.read_word = gpmi_read_word;
+ g->chip.read_byte = gpmi_read_byte;
+ g->chip.read_buf = gpmi_read_buf;
+ g->chip.write_buf = gpmi_write_buf;
+ g->chip.select_chip = gpmi_select_chip;
+ g->chip.verify_buf = gpmi_verify_buf;
+ g->chip.dev_ready = gpmi_dev_ready;
+
+ g->chip.ecc.mode = NAND_ECC_HW_SYNDROME;
+ g->chip.ecc.write_oob = gpmi_ecc_write_oob;
+ g->chip.ecc.read_oob = gpmi_ecc_read_oob;
+ g->chip.ecc.write_page = gpmi_ecc_write_page;
+ g->chip.ecc.read_page = gpmi_ecc_read_page;
+ g->chip.ecc.read_page_raw = gpmi_read_page_raw;
+ g->chip.ecc.write_page_raw = gpmi_write_page_raw;
+ g->chip.ecc.size = 512;
+
+ g->chip.write_page = gpmi_write_page;
+
+ g->chip.scan_bbt = gpmi_scan_bbt;
+ g->chip.block_bad = gpmi_block_bad;
+
+ g->cmd_buffer_sz = 0;
+
+ /* first scan to find the device and get the page size */
+ if (nand_scan_ident(&g->mtd, max_chips)
+ || gpmi_scan_middle(g)
+ || nand_scan_tail(&g->mtd)) {
+ dev_err(&pdev->dev, "No NAND found\n");
+ /* errors found on some step */
+ goto out7;
+ }
+
+ g->chip.options |= NAND_NO_SUBPAGE_WRITE;
+ g->chip.subpagesize = g->mtd.writesize;
+ g->mtd.subpage_sft = 0;
+
+ g->mtd.erase = gpmi_erase;
+
+ g->saved_read_oob = g->mtd.read_oob;
+ g->saved_write_oob = g->mtd.write_oob;
+ g->mtd.read_oob = gpmi_read_oob;
+ g->mtd.write_oob = gpmi_write_oob;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ if (gpd == NULL)
+ goto out_all;
+
+ if (gpd->parts[0].part_probe_types) {
+ g->nr_parts = parse_mtd_partitions(&g->mtd,
+ gpd->parts[0].part_probe_types,
+ &g->parts, 0);
+ if (g->nr_parts > 0)
+ part_type = "command line";
+ else
+ g->nr_parts = 0;
+ }
+
+ if (g->nr_parts == 0 && gpd->parts[0].partitions) {
+ g->parts = gpd->parts[0].partitions;
+ g->nr_parts = gpd->parts[0].nr_partitions;
+ part_type = "static";
+ }
+
+ if (g->nr_parts == 0) {
+ dev_err(&pdev->dev, "Neither part_probe_types nor "
+ "partitions was specified in platform_data");
+ goto out_all;
+ }
+
+ dev_info(&pdev->dev, "Using %s partition definition\n",
+ part_type);
+
+ g->numchips = g->chip.numchips;
+ chipsize = g->mtd.size;
+ do_div(chipsize, (unsigned long)g->numchips);
+
+ if (!strcmp(part_type, "command line"))
+ add_mtd_partitions(&g->mtd, g->parts, g->nr_parts);
+ else
+ gpmi_create_partitions(g, gpd, chipsize);
+
+ if (add_mtd_entire) {
+ printk(KERN_NOTICE "Adding MTD covering the whole flash\n");
+ add_mtd_device(&g->mtd);
+ }
+#else
+ add_mtd_device(&g->mtd);
+#endif
+ gpmi_uid_init("nand",
+ &g->mtd, gpd->uid_offset, gpd->uid_size);
+
+#ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES
+ gpmi_sysfs(pdev, true);
+#endif
+ return 0;
+
+out_all:
+ ecc8_exit();
+ bch_exit();
+out7:
+ nand_release(&g->mtd);
+ gpmi_free_buffers(pdev, g);
+out6:
+ gpmi_deinit_chip(pdev, g, -1);
+out5:
+ free_irq(g->irq, g);
+out4:
+ del_timer_sync(&g->timer);
+ gpmi_nand_release_hw(pdev);
+out3:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(g->io_base);
+out2:
+ kfree(g);
+out1:
+ return err;
+}
+
+/**
+ * gpmi_nand_remove - remove a GPMI device
+ *
+ */
+static int __devexit gpmi_nand_remove(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ int i = 0;
+#ifdef CONFIG_MTD_PARTITIONS
+ struct gpmi_platform_data *gpd = pdev->dev.platform_data;
+ struct mtd_partition *platf_parts;
+#endif
+
+ gpmi_delete_partitions(g);
+ del_timer_sync(&g->timer);
+ gpmi_uid_remove("nand");
+#ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES
+ gpmi_sysfs(pdev, false);
+#endif
+ nand_release(&g->mtd);
+ gpmi_free_buffers(pdev, g);
+ gpmi_deinit_chip(pdev, g, -1);
+ gpmi_nand_release_hw(pdev);
+ free_irq(g->irq, g);
+ if (g->regulator)
+ regulator_put(g->regulator);
+
+#ifdef CONFIG_MTD_PARTITIONS
+ if (i < gpd->items && gpd->parts[i].partitions)
+ platf_parts = gpd->parts[i].partitions;
+ else
+ platf_parts = NULL;
+ if (g->parts && g->parts != platf_parts)
+ kfree(g->parts);
+#endif
+ iounmap(g->io_base);
+ kfree(g);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int gpmi_nand_suspend(struct platform_device *pdev, pm_message_t pm)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ int r = 0;
+
+ if (g->self_suspended)
+ gpmi_self_wakeup(g);
+ del_timer_sync(&g->timer);
+
+ r = g->mtd.suspend(&g->mtd);
+ if (r == 0)
+ gpmi_nand_release_hw(pdev);
+
+ return r;
+}
+
+static int gpmi_nand_resume(struct platform_device *pdev)
+{
+ struct gpmi_nand_data *g = platform_get_drvdata(pdev);
+ int r;
+
+ r = gpmi_nand_init_hw(pdev, 1);
+ gpmi_set_timings(pdev, &g->timing);
+ g->mtd.resume(&g->mtd);
+ g->timer.expires = jiffies + 4*HZ;
+ add_timer(&g->timer);
+ return r;
+}
+#else
+#define gpmi_nand_suspend NULL
+#define gpmi_nand_resume NULL
+#endif
+
+#ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES
+static ssize_t show_timings(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct gpmi_nand_timing *ptm;
+ struct gpmi_nand_data *g = dev_get_drvdata(d);
+
+ ptm = &g->timing;
+ return sprintf(buf, "DATA_SETUP %d, DATA_HOLD %d, "
+ "ADDR_SETUP %d, DSAMPLE_TIME %d\n",
+ ptm->data_setup, ptm->data_hold,
+ ptm->address_setup,
+ ptm->dsample_time);
+}
+
+static ssize_t store_timings(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ const char *p, *end;
+ struct gpmi_nand_timing t;
+ struct gpmi_nand_data *g = dev_get_drvdata(d);
+ char tmps[20];
+ u8 *timings[] = {
+ &t.data_setup,
+ &t.data_hold,
+ &t.address_setup,
+ &t.dsample_time,
+ NULL,
+ };
+ u8 **timing = timings;
+
+ p = buf;
+
+ /* parse values */
+ while (*timing != NULL) {
+ unsigned long t_long;
+
+ end = strchr(p, ',');
+ memset(tmps, 0, sizeof(tmps));
+ if (end)
+ strncpy(tmps, p, min_t(int, sizeof(tmps) - 1, end - p));
+ else
+ strncpy(tmps, p, sizeof(tmps) - 1);
+
+ if (strict_strtoul(tmps, 0, &t_long) < 0)
+ return -EINVAL;
+
+ if (t_long > 255)
+ return -EINVAL;
+
+ **timing = (u8)t_long;
+ timing++;
+
+ if (!end && *timing)
+ return -EINVAL;
+ p = end + 1;
+ }
+
+ gpmi_set_timings(g->dev, &t);
+
+ return size;
+}
+
+static ssize_t show_stat(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "copies\t\t%dff pages\t%d\n", copies, ff_writes);
+}
+
+static ssize_t show_chips(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct gpmi_nand_data *g = dev_get_drvdata(d);
+ return sprintf(buf, "%d\n", g->numchips);
+}
+
+
+static ssize_t show_ignorebad(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct gpmi_nand_data *g = dev_get_drvdata(d);
+
+ return sprintf(buf, "%d\n", g->ignorebad);
+}
+
+static ssize_t store_ignorebad(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct gpmi_nand_data *g = dev_get_drvdata(d);
+ const char *p = buf;
+ unsigned long v;
+
+ if (strict_strtoul(p, 0, &v) < 0)
+ return size;
+ if (v > 0)
+ v = 1;
+ if (v != g->ignorebad) {
+ if (v) {
+ g->bbt = g->chip.bbt;
+ g->chip.bbt = NULL;
+ g->ignorebad = 1;
+ } else {
+ g->chip.bbt = g->bbt;
+ g->ignorebad = 0;
+ }
+ }
+ return size;
+}
+
+static DEVICE_ATTR(timings, 0644, show_timings, store_timings);
+static DEVICE_ATTR(stat, 0444, show_stat, NULL);
+static DEVICE_ATTR(ignorebad, 0644, show_ignorebad, store_ignorebad);
+static DEVICE_ATTR(numchips, 0444, show_chips, NULL);
+
+static struct device_attribute *gpmi_attrs[] = {
+ &dev_attr_timings,
+ &dev_attr_stat,
+ &dev_attr_ignorebad,
+ &dev_attr_numchips,
+ NULL,
+};
+
+int gpmi_sysfs(struct platform_device *pdev, int create)
+{
+ int err = 0;
+ int i;
+
+ if (create) {
+ for (i = 0; gpmi_attrs[i]; i++) {
+ err = device_create_file(&pdev->dev, gpmi_attrs[i]);
+ if (err)
+ break;
+ }
+ if (err)
+ while (--i >= 0)
+ device_remove_file(&pdev->dev, gpmi_attrs[i]);
+ } else {
+ for (i = 0; gpmi_attrs[i]; i++)
+ device_remove_file(&pdev->dev, gpmi_attrs[i]);
+ }
+ return err;
+}
+#endif
+
+static struct platform_driver gpmi_nand_driver = {
+ .probe = gpmi_nand_probe,
+ .remove = __devexit_p(gpmi_nand_remove),
+ .driver = {
+ .name = "gpmi",
+ .owner = THIS_MODULE,
+ },
+ .suspend = gpmi_nand_suspend,
+ .resume = gpmi_nand_resume,
+};
+
+static LIST_HEAD(gpmi_hwecc_chips);
+
+void gpmi_hwecc_chip_add(struct gpmi_hwecc_chip *chip)
+{
+ list_add(&chip->list, &gpmi_hwecc_chips);
+}
+EXPORT_SYMBOL_GPL(gpmi_hwecc_chip_add);
+
+void gpmi_hwecc_chip_remove(struct gpmi_hwecc_chip *chip)
+{
+ list_del(&chip->list);
+}
+EXPORT_SYMBOL_GPL(gpmi_hwecc_chip_remove);
+
+struct gpmi_hwecc_chip *gpmi_hwecc_chip_find(char *name)
+{
+ struct gpmi_hwecc_chip *c;
+
+ list_for_each_entry(c, &gpmi_hwecc_chips, list)
+ if (strncmp(c->name, name, sizeof(c->name)) == 0)
+ return c;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gpmi_hwecc_chip_find);
+
+static int __init gpmi_nand_init(void)
+{
+ bch_init();
+ ecc8_init();
+ return platform_driver_register(&gpmi_nand_driver);
+}
+
+static void __exit gpmi_nand_exit(void)
+{
+ platform_driver_unregister(&gpmi_nand_driver);
+}
+
+module_init(gpmi_nand_init);
+module_exit(gpmi_nand_exit);
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPMI NAND driver");
+module_param(max_chips, int, 0400);
+module_param(clk, long, 0400);
+module_param(bch, int, 0600);
+module_param(map_buffers, int, 0600);
+module_param(raw_mode, int, 0600);
+module_param(debug, int, 0600);
+module_param(add_mtd_entire, int, 0400);
+module_param(add_mtd_chip, int, 0400);
+module_param(ignorebad, int, 0400);
diff --git a/drivers/mtd/nand/gpmi/gpmi-bbt.c b/drivers/mtd/nand/gpmi/gpmi-bbt.c
new file mode 100644
index 000000000000..54755f870440
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-bbt.c
@@ -0,0 +1,565 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/dma-mapping.h>
+#include <linux/ctype.h>
+#include <mach/dma.h>
+#include <mach/unique-id.h>
+#include "gpmi.h"
+
+static int boot_search_count;
+static int stride = 64;
+static int ncb_version = 3;
+module_param(boot_search_count, int, 0400);
+module_param(ncb_version, int, 0400);
+
+void *gpmi_read_page(struct mtd_info *mtd, loff_t start, void *data, int raw)
+{
+ int ret;
+ struct mtd_oob_ops ops;
+
+ if (!data)
+ data = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ if (raw)
+ ops.mode = MTD_OOB_RAW;
+ else
+ ops.mode = MTD_OOB_PLACE;
+ ops.datbuf = data;
+ ops.len = mtd->writesize;
+ ops.oobbuf = data + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ops.ooboffs = 0;
+ ret = nand_do_read_ops(mtd, start, &ops);
+
+ if (ret)
+ return NULL;
+ return data;
+}
+
+int gpmi_write_ncb(struct mtd_info *mtd, struct gpmi_bcb_info *b)
+{
+ struct gpmi_ncb *ncb = NULL, *unencoded_ncb = NULL;
+ struct nand_chip *chip = mtd->priv;
+ int err;
+ loff_t start = 0;
+ struct mtd_oob_ops ops;
+ struct erase_info instr;
+ int ncb_count;
+
+ ncb = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!ncb) {
+ err = -ENOMEM;
+ goto out;
+ }
+ unencoded_ncb = kzalloc(mtd->writesize, GFP_KERNEL);
+ if (!unencoded_ncb) {
+ err = -ENOMEM;
+ goto out;
+ }
+ ops.mode = -1; /* if the value is not set in switch below,
+ this will cause BUG. Take care. */
+ if (b && b->pre_ncb)
+ memcpy(unencoded_ncb, b->pre_ncb, b->pre_ncb_size);
+ else {
+ memcpy(&unencoded_ncb->fingerprint1, SIG1, sizeof(u32));
+ memcpy(&unencoded_ncb->fingerprint2, SIG_NCB, sizeof(u32));
+ if (b)
+ unencoded_ncb->timing = b->timing;
+ }
+
+ switch (ncb_version) {
+ case 0:
+ ops.mode = MTD_OOB_AUTO;
+ memcpy(ncb, unencoded_ncb, sizeof(*unencoded_ncb));
+ break;
+#ifdef CONFIG_MTD_NAND_GPMI_TA1
+ case 1:
+ ops.mode = MTD_OOB_RAW;
+ gpmi_encode_hamming_ncb_22_16(unencoded_ncb,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ ncb, mtd->writesize + mtd->oobsize);
+ break;
+#endif
+#ifdef CONFIG_MTD_NAND_GPMI_TA3
+ case 3:
+ ops.mode = MTD_OOB_RAW;
+ gpmi_encode_hamming_ncb_13_8(unencoded_ncb,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ ncb, mtd->writesize + mtd->oobsize);
+ break;
+#endif
+
+ default:
+ printk(KERN_ERR"Incorrect ncb_version = %d\n", ncb_version);
+ err = -EINVAL;
+ goto out;
+ }
+
+ ops.datbuf = (u8 *)ncb;
+ ops.len = mtd->writesize;
+ ops.oobbuf = (u8 *)ncb + mtd->writesize;
+ ops.ooblen = mtd->oobsize;
+ ops.ooboffs = 0;
+
+ ncb_count = 0;
+ do {
+ printk(KERN_NOTICE"GPMI: Trying to store NCB at addr %lx\n",
+ (unsigned long)start);
+ memset(&instr, 0, sizeof(instr));
+ instr.mtd = mtd;
+ instr.addr = start;
+ instr.len = (1 << chip->phys_erase_shift);
+ err = nand_erase_nand(mtd, &instr, 0);
+ if (err == 0) {
+ printk(KERN_NOTICE"GPMI: Erased, storing\n");
+ err = nand_do_write_ops(mtd, start, &ops);
+ printk(KERN_NOTICE"GPMI: NCB update %s (%d).\n",
+ err ? "failed" : "succeeded", err);
+ }
+ start += (1 << chip->phys_erase_shift);
+ ncb_count++;
+ } while (err != 0 && ncb_count < 100);
+
+ if (b)
+ b->ncbblock = start >> chip->bbt_erase_shift;
+
+out:
+ kfree(ncb);
+ kfree(unencoded_ncb);
+
+ return 0;
+}
+
+static int gpmi_redundancy_check_one(u8 *pg, int dsize, int esize, int offset,
+ int o1, int o2)
+{
+ int r;
+
+ if (o1 == o2)
+ return 0;
+
+ r = memcmp(pg + o1 * dsize, pg + o2 * dsize, dsize);
+ if (r) {
+ pr_debug("DATA copies %d and %d are different: %d\n",
+ o1, o2, r);
+ return r;
+ }
+
+ r = memcmp(pg + o1 * esize + offset,
+ pg + o2 * esize + offset, esize);
+ if (r) {
+ pr_debug("ECC copies %d and %d are different: %d\n", o1, o2, r);
+ return r;
+ }
+ pr_debug("Both DATA and ECC copies %d and %d are identical\n", o1, o2);
+ return r;
+}
+
+static int gpmi_redundancy_check(u8 *pg, int dsize, int esize, int ecc_offset)
+{
+ if (gpmi_redundancy_check_one(pg, dsize, esize, ecc_offset, 0, 1) == 0)
+ return 0;
+ if (gpmi_redundancy_check_one(pg, dsize, esize, ecc_offset, 0, 2) == 0)
+ return 0;
+ if (gpmi_redundancy_check_one(pg, dsize, esize, ecc_offset, 1, 2) == 0)
+ return 1;
+ return -1;
+}
+
+static inline int gpmi_ncb1_redundancy_check(u8 *pg)
+{
+ return gpmi_redundancy_check(pg,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES,
+ NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY);
+}
+
+static int gpmi_scan_sigmatel_bbt(
+ struct mtd_info *mtd, struct gpmi_bcb_info *nfo)
+{
+ int page, r;
+ u8 *pg;
+ struct gpmi_ncb *result = NULL;
+
+ if (boot_search_count == 0)
+ boot_search_count = 1;
+ if (nfo == NULL)
+ return -EINVAL;
+
+ pg = NULL;
+ printk(KERN_NOTICE"Scanning for NCB...\n");
+ for (page = 0; page < (1<<boot_search_count); page += stride) {
+ pg = gpmi_read_page(mtd, page * mtd->writesize,
+ pg, ncb_version != 0);
+
+ printk(KERN_NOTICE"GPMI: Checking page 0x%08X\n", page);
+
+ if (ncb_version == 0) {
+ if (memcmp(pg, SIG1, SIG_SIZE) != 0)
+ continue;
+ printk(KERN_NOTICE"GPMI: Signature found at 0x%08X\n",
+ page);
+ result = (struct gpmi_ncb *)pg;
+ }
+
+#ifdef CONFIG_MTD_NAND_GPMI_TA1
+ if (ncb_version == 1) {
+ void *dptr, *eccptr;
+
+ if (memcmp(pg, SIG1, SIG_SIZE) != 0)
+ continue;
+ printk(KERN_NOTICE"GPMI: Signature found at 0x%08X\n",
+ page);
+
+ r = gpmi_ncb1_redundancy_check(pg);
+
+ if (r < 0) {
+ printk(KERN_ERR"GPMI: Oops. All three "
+ "copies of NCB are differrent!\n");
+ continue;
+ }
+
+ dptr = pg + r * NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES;
+ eccptr = pg + NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY +
+ r * NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES;
+
+ if (gpmi_verify_hamming_22_16(dptr, eccptr,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES) < 0) {
+ printk(KERN_ERR"Verification failed.\n");
+ continue;
+ }
+ result = (struct gpmi_ncb *)pg;
+ }
+#endif
+
+#ifdef CONFIG_MTD_NAND_GPMI_TA3
+ if (ncb_version == 3) {
+
+ if (memcmp(pg + 12, SIG1, SIG_SIZE) != 0)
+ continue;
+
+ printk(KERN_NOTICE"GPMI: Signature found at 0x%08X\n",
+ page);
+
+ if (gpmi_verify_hamming_13_8(
+ pg + 12,
+ pg + 524,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES) < 0) {
+ printk(KERN_ERR"Verification failed.\n");
+ continue;
+ }
+ result = (struct gpmi_ncb *)(pg + 12);
+ }
+#endif
+ if (result) {
+ printk(KERN_NOTICE"GPMI: Valid NCB found "
+ "at 0x%08x\n", page);
+ nfo->timing = result->timing;
+ nfo->ncbblock = page * mtd->writesize;
+ break;
+ }
+ }
+ kfree(pg);
+
+ return result != NULL;
+}
+
+int gpmi_scan_bbt(struct mtd_info *mtd)
+{
+ struct gpmi_bcb_info stmp_bbt;
+ struct nand_chip *this = mtd->priv;
+ struct gpmi_nand_data *g = this->priv;
+ int r;
+ int numblocks, from, i, ign;
+
+ memset(&stmp_bbt, 0, sizeof(stmp_bbt));
+ g->transcribe_bbmark = 0;
+
+ /*
+ Since NCB uses the full page, including BB pattern bits,
+ driver has to ignore result of gpmi_block_bad when reading
+ these pages.
+ */
+ ign = g->ignorebad;
+
+ g->ignorebad = true; /* strictly speaking, I'd have to hide
+ * the BBT too.
+ * But we still scanning it :) */
+ r = gpmi_scan_sigmatel_bbt(mtd, &stmp_bbt);
+
+ /* and then, driver has to restore the setting */
+ g->ignorebad = ign;
+
+ if (r) {
+ printk(KERN_NOTICE"Setting discovered timings: %d:%d:%d:%d\n",
+ stmp_bbt.timing.data_setup,
+ stmp_bbt.timing.data_hold,
+ stmp_bbt.timing.address_setup,
+ stmp_bbt.timing.dsample_time);
+
+ gpmi_set_timings(g->dev, &stmp_bbt.timing);
+ g->timing = stmp_bbt.timing;
+
+ } else {
+ g->transcribe_bbmark = !0;
+ numblocks = this->chipsize >> this->bbt_erase_shift;
+ from = 0;
+ printk(KERN_NOTICE"Checking BB on common-formatted flash\n");
+ for (i = stmp_bbt.ncbblock + 1; i < numblocks; i++) {
+ /* check the block and transcribe the bb if needed */
+ gpmi_block_bad(mtd, from, 0);
+ from += (1 << this->bbt_erase_shift);
+ }
+ }
+
+ r = nand_default_bbt(mtd);
+
+ if (g->transcribe_bbmark) {
+ /* NCB has been not found, so create NCB now */
+ g->transcribe_bbmark = 0;
+
+ stmp_bbt.timing = gpmi_safe_timing;
+ r = gpmi_write_ncb(mtd, &stmp_bbt);
+ } else {
+ /* NCB found, and its block should be marked as "good" */
+ gpmi_block_mark_as(this, stmp_bbt.ncbblock, 0x00);
+ }
+
+ return r;
+}
+
+int gpmi_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+
+{
+ int page, res = 0;
+ struct nand_chip *chip = mtd->priv;
+ u16 bad;
+ struct gpmi_nand_data *g = chip->priv;
+ int chipnr;
+
+ /* badblockpos is an offset in OOB area */
+ int badblockpos = chip->ecc.steps * chip->ecc.bytes;
+
+ if (g->ignorebad)
+ return 0;
+
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+
+ chipnr = (int)(ofs >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
+ if (g->transcribe_bbmark)
+ /* bad block marks still are on first byte of OOB */
+ badblockpos = 0;
+
+ if (chip->options & NAND_BUSWIDTH_16) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, badblockpos & 0xFE,
+ page);
+ bad = cpu_to_le16(chip->read_word(mtd));
+ if (badblockpos & 0x1)
+ bad >>= 8;
+ if ((bad & 0xFF) != 0xff)
+ res = 1;
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, badblockpos, page);
+ if (chip->read_byte(mtd) != 0xff)
+ res = 1;
+ }
+
+ if (g->transcribe_bbmark && res)
+ chip->block_markbad(mtd, ofs);
+
+ chip->select_chip(mtd, -1);
+
+ return res;
+}
+
+#if defined(CONFIG_STMP3XXX_UNIQUE_ID)
+/*
+ * UID on NAND support
+ */
+const int uid_size = 256;
+
+struct gpmi_uid_context {
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ u_int32_t start;
+ u_int32_t size;
+};
+
+static int gpmi_read_uid(struct gpmi_uid_context *ctx, void *result)
+{
+ void *pg = NULL;
+ int page, o;
+ int status = -ENOENT;
+ int h_size = gpmi_hamming_ecc_size_22_16(uid_size);
+
+ for (page = ctx->start >> ctx->nand->page_shift;
+ page < (ctx->start + ctx->size) >> ctx->nand->page_shift;) {
+ pr_debug("%s: reading page 0x%x\n", __func__, page);
+ if (gpmi_block_bad(ctx->mtd, page * ctx->mtd->writesize, 0)) {
+ pr_debug("%s: bad block %x, skipping it\n",
+ __func__, page * ctx->mtd->writesize);
+ page += (1 << ctx->nand->phys_erase_shift)
+ >> ctx->nand->page_shift;
+ continue;
+ }
+ pg = gpmi_read_page(ctx->mtd, page * ctx->mtd->writesize,
+ pg, 0);
+ if (pg)
+ break;
+ page++;
+ }
+
+ if (!pg)
+ return status;
+
+ o = gpmi_redundancy_check(pg, uid_size, h_size, 3 * uid_size);
+ if (o >= 0) {
+ if (gpmi_verify_hamming_22_16(
+ pg + o * uid_size,
+ pg + 3 * uid_size + h_size, uid_size) >= 0) {
+ memcpy(result, pg + o * uid_size, uid_size);
+ status = 0;
+ }
+ }
+ kfree(pg);
+ return status;
+}
+
+static int gpmi_write_uid(struct gpmi_uid_context *ctx, void *src)
+{
+ struct mtd_oob_ops ops;
+ struct erase_info instr;
+ u8 *data = kzalloc(ctx->mtd->writesize + ctx->mtd->oobsize, GFP_KERNEL);
+ int h_size = gpmi_hamming_ecc_size_22_16(uid_size);
+ char ecc[h_size];
+ u_int32_t start;
+ int i;
+ int err;
+
+ if (!data)
+ return -ENOMEM;
+
+ gpmi_encode_hamming_22_16(src, uid_size, ecc, h_size);
+ for (i = 0; i < 3; i++) {
+ memcpy(data + i * uid_size, src, uid_size);
+ memcpy(data + 3 * uid_size + i * h_size, ecc, h_size);
+ }
+
+ ops.mode = MTD_OOB_AUTO;
+ ops.datbuf = data;
+ ops.len = ctx->mtd->writesize;
+ ops.oobbuf = NULL;
+ ops.ooblen = ctx->mtd->oobsize;
+ ops.ooboffs = 0;
+
+ start = ctx->start;
+
+ do {
+ memset(&instr, 0, sizeof(instr));
+ instr.mtd = ctx->mtd;
+ instr.addr = start;
+ instr.len = (1 << ctx->nand->phys_erase_shift);
+ err = nand_erase_nand(ctx->mtd, &instr, 0);
+ if (err == 0)
+ err = nand_do_write_ops(ctx->mtd, start, &ops);
+ start += (1 << ctx->nand->phys_erase_shift);
+ if (start > ctx->start + ctx->size)
+ break;
+ } while (err != 0);
+
+ return err;
+}
+
+static ssize_t gpmi_uid_store(void *context, const char *page,
+ size_t count, int ascii)
+{
+ u8 data[uid_size];
+
+ memset(data, 0, sizeof(data));
+ memcpy(data, page, uid_size < count ? uid_size : count);
+ gpmi_write_uid(context, data);
+ return count;
+}
+
+static ssize_t gpmi_uid_show(void *context, char *page, int ascii)
+{
+ u8 result[uid_size];
+ int i;
+ char *p = page;
+ int r;
+
+ r = gpmi_read_uid(context, result);
+ if (r < 0)
+ return r;
+
+ if (ascii) {
+ for (i = 0; i < uid_size; i++) {
+ if (i % 16 == 0) {
+ if (i)
+ *p++ = '\n';
+ sprintf(p, "%04X: ", i);
+ p += strlen(p);
+ }
+ sprintf(p, "%02X ", result[i]);
+ p += strlen(p);
+ }
+ *p++ = '\n';
+ return p - page;
+
+ } else {
+ memcpy(page, result, uid_size);
+ return uid_size;
+ }
+}
+
+static struct uid_ops gpmi_uid_ops = {
+ .id_show = gpmi_uid_show,
+ .id_store = gpmi_uid_store,
+};
+
+static struct gpmi_uid_context gpmi_uid_context;
+
+int __init gpmi_uid_init(const char *name, struct mtd_info *mtd,
+ u_int32_t start, u_int32_t size)
+{
+ gpmi_uid_context.mtd = mtd;
+ gpmi_uid_context.nand = mtd->priv;
+ gpmi_uid_context.start = start;
+ gpmi_uid_context.size = size;
+ return uid_provider_init(name, &gpmi_uid_ops, &gpmi_uid_context) ?
+ 0 : -EFAULT;
+}
+
+void gpmi_uid_remove(const char *name)
+{
+ uid_provider_remove(name);
+}
+#endif
diff --git a/drivers/mtd/nand/gpmi/gpmi-bch.c b/drivers/mtd/nand/gpmi/gpmi-bch.c
new file mode 100644
index 000000000000..58f7354bcd18
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-bch.c
@@ -0,0 +1,289 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * STMP378X BCH hardware ECC engine
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/dma.h>
+#include <mach/stmp3xxx.h>
+#include <mach/irqs.h>
+#include "gpmi.h"
+
+#define BCH_MAX_NANDS 4
+
+static int bch_available(void *context);
+static int bch_setup(void *context, int index, int writesize, int oobsize);
+static int bch_read(void *context,
+ int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob);
+static int bch_stat(void *ctx, int index, struct mtd_ecc_stats *r);
+static int bch_reset(void *context, int index);
+
+struct bch_state_t {
+ struct gpmi_hwecc_chip chip;
+ struct {
+ struct mtd_ecc_stats stat;
+ struct completion done;
+ u32 writesize, oobsize;
+ } nands[BCH_MAX_NANDS];
+};
+
+static struct bch_state_t state = {
+ .chip = {
+ .name = "bch",
+ .setup = bch_setup,
+ .stat = bch_stat,
+ .read = bch_read,
+ .reset = bch_reset,
+ },
+};
+
+static int bch_reset(void *context, int index)
+{
+ stmp3xxx_reset_block(REGS_BCH_BASE, true);
+ HW_BCH_CTRL_SET(BM_BCH_CTRL_COMPLETE_IRQ_EN);
+ return 0;
+}
+
+static int bch_stat(void *context, int index, struct mtd_ecc_stats *r)
+{
+ struct bch_state_t *state = context;
+
+ *r = state->nands[index].stat;
+ state->nands[index].stat.failed = 0;
+ state->nands[index].stat.corrected = 0;
+ return 0;
+}
+
+static irqreturn_t bch_irq(int irq, void *context)
+{
+ u32 b0, s0;
+ struct mtd_ecc_stats stat;
+ int r;
+ struct bch_state_t *state = context;
+
+ s0 = HW_BCH_STATUS0_RD();
+ r = (s0 & BM_BCH_STATUS0_COMPLETED_CE) >> 16;
+
+ stat.corrected = stat.failed = 0;
+
+ b0 = (s0 & BM_BCH_STATUS0_STATUS_BLK0) >> 8;
+ if (b0 <= 4)
+ stat.corrected += b0;
+ if (b0 == 0xFE)
+ stat.failed++;
+
+ if (s0 & BM_BCH_STATUS0_CORRECTED)
+ stat.corrected += (s0 & BM_BCH_STATUS0_CORRECTED);
+ if (s0 & BM_BCH_STATUS0_UNCORRECTABLE)
+ stat.failed++;
+
+ HW_BCH_CTRL_CLR(BM_BCH_CTRL_COMPLETE_IRQ);
+
+ pr_debug("%s: chip %d, failed %d, corrected %d\n",
+ __func__, r,
+ state->nands[r].stat.failed,
+ state->nands[r].stat.corrected);
+ state->nands[r].stat.corrected += stat.corrected;
+ state->nands[r].stat.failed += stat.failed;
+ complete(&state->nands[r].done);
+
+ return IRQ_HANDLED;
+}
+
+static int bch_available(void *context)
+{
+ stmp3xxx_reset_block(REGS_BCH_BASE, 0);
+ return HW_BCH_BLOCKNAME_RD() == 0x20484342;
+}
+
+static int bch_setup(void *context, int index, int writesize, int oobsize)
+{
+ struct bch_state_t *state = context;
+ u32 layout = (REGS_BCH_BASE + 0x80 /* HW_BCH_FLASH0LAYOUT0_ADDR */)
+ + index * 0x20;
+ u32 ecc0, eccN;
+ int meta;
+
+ switch (writesize) {
+ case 2048:
+ ecc0 = 4;
+ eccN = 4;
+ meta = 5;
+ break;
+ case 4096:
+ ecc0 = 16;
+ eccN = 14;
+ meta = 10;
+ break;
+ default:
+ printk(KERN_ERR"%s: cannot tune BCH for page size %d\n",
+ __func__, writesize);
+ return -EINVAL;
+ }
+
+ state->nands[index].oobsize = oobsize;
+ state->nands[index].writesize = writesize;
+
+ __raw_writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(writesize/512) |
+ BF_BCH_FLASH0LAYOUT0_META_SIZE(oobsize) |
+ BF_BCH_FLASH0LAYOUT0_ECC0(ecc0 >> 1) | /* for oob */
+ BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(0x00), layout);
+ __raw_writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(writesize + oobsize) |
+ BF_BCH_FLASH0LAYOUT1_ECCN(eccN >> 1) | /* for dblock */
+ BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(512), layout + 0x10);
+
+ /*
+ * since driver only supports CS 0..3, layouts are mapped 1:1 :
+ * FLASHnLAYOUT[1,2] => LAYOUTSELECT[n*2:n2*+1]
+ */
+ HW_BCH_LAYOUTSELECT_CLR(0x03 << (index * 2));
+ HW_BCH_LAYOUTSELECT_SET(index << (index * 2));
+
+ bch_reset(context, index);
+
+ printk(KERN_DEBUG"%s: CS = %d, LAYOUT = 0x%08X, layout_reg = "
+ "0x%08x+0x%08x: 0x%08x+0x%08x\n",
+ __func__,
+ index, HW_BCH_LAYOUTSELECT_RD(),
+ layout, layout + 0x10,
+ __raw_readl(layout),
+ __raw_readl(layout+0x10));
+ return 0;
+}
+
+static int bch_read(void *context,
+ int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob)
+{
+ unsigned long readsize = 0;
+ u32 bufmask = 0;
+ struct bch_state_t *state = context;
+
+ if (!dma_mapping_error(NULL, oob)) {
+ bufmask |= BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+ readsize += state->nands[index].oobsize;
+ }
+ if (!dma_mapping_error(NULL, page)) {
+ bufmask |= (BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE
+ & ~BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY);
+ readsize += state->nands[index].writesize;
+ }
+
+ printk(KERN_DEBUG"readsize = %ld, bufmask = 0x%X\n", readsize, bufmask);
+ bch_reset(context, index);
+
+ /* wait for ready */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(1) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(
+ BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) |
+ BF_GPMI_CTRL0_CS(index);
+ chain->command->alternate = 0;
+ chain++;
+
+ /* enable BCH and read NAND data */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(6) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(index) |
+ BF_GPMI_CTRL0_XFER_COUNT(readsize);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] =
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
+ BF_GPMI_ECCCTRL_ECC_CMD(0x02) |
+ BF_GPMI_ECCCTRL_BUFFER_MASK(bufmask);
+ chain->command->pio_words[3] = readsize;
+ chain->command->pio_words[4] = !dma_mapping_error(NULL, page) ? page : 0;
+ chain->command->pio_words[5] = !dma_mapping_error(NULL, oob) ? oob : 0;
+ chain->command->alternate = 0;
+ chain++;
+
+ /* disable BCH block */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(3) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(
+ BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(index) |
+ BF_GPMI_CTRL0_XFER_COUNT(readsize);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->alternate = 0;
+ chain++;
+
+ /* and deassert nand lock */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(0) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->alternate = 0;
+
+ return 0;
+}
+
+int __init bch_init(void)
+{
+ int err;
+
+ if (!bch_available(&state.chip))
+ return -ENXIO;
+ gpmi_hwecc_chip_add(&state.chip);
+ err = request_irq(IRQ_BCH, bch_irq, 0, state.chip.name, &state);
+ if (err)
+ return err;
+
+ printk(KERN_DEBUG"%s: initialized\n", __func__);
+ return 0;
+}
+
+void bch_exit(void)
+{
+ free_irq(IRQ_BCH, &state);
+ gpmi_hwecc_chip_remove(&state.chip);
+}
diff --git a/drivers/mtd/nand/gpmi/gpmi-ecc8.c b/drivers/mtd/nand/gpmi/gpmi-ecc8.c
new file mode 100644
index 000000000000..dad14c3809fc
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-ecc8.c
@@ -0,0 +1,382 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * STMP37XX/STMP378X ECC8 hardware ECC engine
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/dma.h>
+#include <mach/stmp3xxx.h>
+#include <mach/irqs.h>
+#include "gpmi.h"
+
+#define ECC8_MAX_NANDS 4
+
+static int ecc8_available(void *context);
+static int ecc8_setup(void *context, int index, int writesize, int oobsize);
+static int ecc8_read(void *context, int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob);
+static int ecc8_write(void *context, int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob);
+static int ecc8_stat(void *ctx, int index, struct mtd_ecc_stats *r);
+static int ecc8_reset(void *context, int index);
+
+struct ecc8_nand {
+};
+
+struct ecc8_state_t {
+ struct gpmi_hwecc_chip chip;
+ struct completion done;
+ struct mtd_ecc_stats stat;
+ u32 writesize, oobsize;
+ u32 ecc_page, ecc_oob, oob_free;
+ u32 r, w;
+ int bits;
+ u32 s0_mask, s1_mask;
+};
+
+static struct ecc8_state_t state = {
+ .chip = {
+ .name = "ecc8",
+ .setup = ecc8_setup,
+ .stat = ecc8_stat,
+ .read = ecc8_read,
+ .write = ecc8_write,
+ .reset = ecc8_reset,
+ },
+};
+
+static int ecc8_reset(void *context, int index)
+{
+ stmp3xxx_reset_block(REGS_ECC8_BASE, false);
+ while (HW_ECC8_CTRL_RD() & BM_ECC8_CTRL_AHBM_SFTRST)
+ HW_ECC8_CTRL_CLR(BM_ECC8_CTRL_AHBM_SFTRST);
+ HW_ECC8_CTRL_SET(BM_ECC8_CTRL_COMPLETE_IRQ_EN);
+ return 0;
+}
+
+static int ecc8_stat(void *context, int index, struct mtd_ecc_stats *r)
+{
+ struct ecc8_state_t *state = context;
+
+ wait_for_completion(&state->done);
+
+ *r = state->stat;
+ state->stat.failed = 0;
+ state->stat.corrected = 0;
+ return 0;
+}
+
+static irqreturn_t ecc8_irq(int irq, void *context)
+{
+ int r;
+ struct mtd_ecc_stats ecc_stats;
+ struct ecc8_state_t *state = context;
+ u32 corr;
+ u32 s0 = HW_ECC8_STATUS0_RD(), s1 = HW_ECC8_STATUS1_RD();
+
+ r = (s0 & BM_ECC8_STATUS0_COMPLETED_CE) >> 16;
+
+ if (s0 & BM_ECC8_STATUS0_CORRECTED ||
+ s0 & BM_ECC8_STATUS0_UNCORRECTABLE) {
+
+ ecc_stats.failed = 0;
+ ecc_stats.corrected = 0;
+
+ s0 = (s0 & BM_ECC8_STATUS0_STATUS_AUX) >> 8;
+ if (s0 <= 4)
+ ecc_stats.corrected += s0;
+ if (s0 == 0xE)
+ ecc_stats.failed++;
+
+ for ( ; s1 != 0; s1 >>= 4) {
+ corr = s1 & 0xF;
+ if (corr == 0x0C)
+ continue;
+ if (corr == 0xE)
+ ecc_stats.failed++;
+ if (corr <= 8)
+ ecc_stats.corrected += corr;
+ s1 >>= 4;
+ }
+ state->stat.corrected += ecc_stats.corrected;
+ state->stat.failed += ecc_stats.failed;
+ }
+
+ complete(&state->done);
+
+ HW_ECC8_CTRL_CLR(BM_ECC8_CTRL_COMPLETE_IRQ);
+ return IRQ_HANDLED;
+}
+
+static int ecc8_available(void *context)
+{
+ return 1;
+}
+
+static int ecc8_setup(void *context, int index, int writesize, int oobsize)
+{
+ struct ecc8_state_t *state = context;
+
+ switch (writesize) {
+ case 2048:
+ state->ecc_page = 9;
+ state->ecc_oob = 9;
+ state->bits = 4;
+ state->r = BF_GPMI_ECCCTRL_ECC_CMD(
+ BV_GPMI_ECCCTRL_ECC_CMD__DECODE_4_BIT);
+ state->w = BF_GPMI_ECCCTRL_ECC_CMD(
+ BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_4_BIT);
+ break;
+ case 4096:
+ state->ecc_page = 18;
+ state->ecc_oob = 9;
+ state->bits = 8;
+ state->r = BF_GPMI_ECCCTRL_ECC_CMD(
+ BV_GPMI_ECCCTRL_ECC_CMD__DECODE_8_BIT);
+ state->w = BF_GPMI_ECCCTRL_ECC_CMD(
+ BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_8_BIT);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ state->oob_free = oobsize -
+ (state->bits * state->ecc_page) - state->ecc_oob;
+ state->oobsize = oobsize;
+ state->writesize = writesize;
+
+ ecc8_reset(context, index);
+
+ return 0;
+}
+
+/**
+ * ecc8_read - create dma chain to read nand page
+ *
+ * @context: context data, pointer to ecc8_state
+ * @index: CS of nand to read
+ * @chain: dma chain to be filled
+ * @page: (physical) address of page data to be read to
+ * @oob: (physical) address of OOB data to be read to
+ *
+ * Return: status of operation -- 0 on success
+ */
+static int ecc8_read(void *context, int cs,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob)
+{
+ unsigned long readsize = 0;
+ u32 bufmask = 0;
+ struct ecc8_state_t *state = context;
+
+ ecc8_reset(context, cs);
+
+ state->s0_mask = state->s1_mask = 0;
+
+ if (!dma_mapping_error(NULL, page)) {
+ bufmask |= (1 << state->bits) - 1;
+ readsize += (state->bits * state->ecc_page);
+ readsize += state->writesize;
+ }
+ if (!dma_mapping_error(NULL, oob)) {
+ bufmask |= BV_GPMI_ECCCTRL_BUFFER_MASK__AUXILIARY;
+ readsize += state->oob_free + state->ecc_oob;
+ state->s0_mask = BM_ECC8_STATUS0_STATUS_AUX;
+ if (dma_mapping_error(NULL, page))
+ page = oob;
+ }
+
+
+ /* wait for ready */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(1) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(
+ BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) |
+ BF_GPMI_CTRL0_CS(cs);
+ chain->command->buf_ptr = 0;
+ chain++;
+
+ /* enable ECC and read NAND data */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(6) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(cs) |
+ BF_GPMI_CTRL0_XFER_COUNT(readsize);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] =
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
+ state->r |
+ BF_GPMI_ECCCTRL_BUFFER_MASK(bufmask);
+ chain->command->pio_words[3] = readsize;
+ chain->command->pio_words[4] = !dma_mapping_error(NULL, page) ? page : 0;
+ chain->command->pio_words[5] = !dma_mapping_error(NULL, oob) ? oob : 0;
+ chain->command->buf_ptr = 0;
+ chain++;
+
+ /* disable ECC block */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(3) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(
+ BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_CS(cs);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->buf_ptr = 0;
+ chain++;
+
+ /* and deassert nand lock */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(0) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->buf_ptr = 0;
+
+ init_completion(&state->done);
+
+ return 0;
+}
+
+static int ecc8_write(void *context, int cs,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob)
+{
+ u32 bufmask = 0;
+ struct ecc8_state_t *state = context;
+ u32 data_w, data_w_ecc,
+ oob_w, oob_w_ecc;
+
+ ecc8_reset(context, cs);
+
+ data_w = data_w_ecc = oob_w = oob_w_ecc = 0;
+
+ if (!dma_mapping_error(NULL, oob)) {
+ bufmask |= BV_GPMI_ECCCTRL_BUFFER_MASK__AUXILIARY;
+ oob_w = state->oob_free;
+ oob_w_ecc = oob_w + state->ecc_oob;
+ }
+ if (!dma_mapping_error(NULL, page)) {
+ bufmask |= (1 << state->bits) - 1;
+ data_w = state->bits * 512;
+ data_w_ecc = data_w + state->bits * state->ecc_page;
+ }
+
+ /* enable ECC and send NAND data (page only) */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_XFER_COUNT(data_w ? data_w : oob_w) |
+ BF_APBH_CHn_CMD_CMDWORDS(4) |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__DMA_READ);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF_GPMI_CTRL0_CS(cs) |
+ BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) |
+ BF_GPMI_CTRL0_XFER_COUNT(data_w + oob_w);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] =
+ BM_GPMI_ECCCTRL_ENABLE_ECC |
+ state->w |
+ BF_GPMI_ECCCTRL_BUFFER_MASK(bufmask);
+ chain->command->pio_words[3] =
+ data_w_ecc + oob_w_ecc;
+ if (!dma_mapping_error(NULL, page))
+ chain->command->buf_ptr = page;
+ else
+ chain->command->buf_ptr = oob;
+ chain++;
+
+ if (!dma_mapping_error(NULL, page) && !dma_mapping_error(NULL, oob)) {
+ /* send NAND data (OOB only) */
+ chain->command->cmd =
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_XFER_COUNT(oob_w) |
+ BF_APBH_CHn_CMD_CMDWORDS(0) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BF_APBH_CHn_CMD_COMMAND(
+ BV_APBH_CHn_CMD_COMMAND__DMA_READ);
+ chain->command->buf_ptr = oob; /* never dma_mapping_error() */
+ chain++;
+ }
+ /* emit IRQ */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(0) |
+ BF_APBH_CHn_CMD_COMMAND(
+ BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_IRQONCMPLT;
+
+ return 0;
+}
+
+int __init ecc8_init(void)
+{
+ int err;
+
+ if (!ecc8_available(&state))
+ return -ENXIO;
+
+ gpmi_hwecc_chip_add(&state.chip);
+ err = request_irq(IRQ_ECC8_IRQ, ecc8_irq, 0, state.chip.name, &state);
+ if (err)
+ return err;
+
+ printk(KERN_INFO"%s: initialized\n", __func__);
+ return 0;
+}
+
+void ecc8_exit(void)
+{
+ free_irq(IRQ_ECC8_IRQ, &state);
+ gpmi_hwecc_chip_remove(&state.chip);
+}
diff --git a/drivers/mtd/nand/gpmi/gpmi-hamming-13-8.c b/drivers/mtd/nand/gpmi/gpmi-hamming-13-8.c
new file mode 100644
index 000000000000..c48d70b38bc0
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-hamming-13-8.c
@@ -0,0 +1,131 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * NCB software ECC Hamming code
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+
+#define BIT_VAL(v, n) (((v) >> (n)) & 0x1)
+#define B(n) (BIT_VAL(d, n))
+
+static u8 calculate_parity(u8 d)
+{
+ u8 p = 0;
+
+ if (d == 0 || d == 0xFF)
+ return 0; /* optimization :) */
+
+ p |= (B(6) ^ B(5) ^ B(3) ^ B(2)) << 0;
+ p |= (B(7) ^ B(5) ^ B(4) ^ B(2) ^ B(1)) << 1;
+ p |= (B(7) ^ B(6) ^ B(5) ^ B(1) ^ B(0)) << 2;
+ p |= (B(7) ^ B(4) ^ B(3) ^ B(0)) << 3;
+ p |= (B(6) ^ B(4) ^ B(3) ^ B(2) ^ B(1) ^ B(0)) << 4;
+ return p;
+}
+
+static inline int even_number_of_1s(u8 byte)
+{
+ int even = 1;
+
+ while (byte > 0) {
+ even ^= (byte & 0x1);
+ byte >>= 1;
+ }
+ return even;
+}
+
+static int lookup_single_error(u8 syndrome)
+{
+ int i;
+ u8 syndrome_table[] = {
+ 0x1C, 0x16, 0x13, 0x19,
+ 0x1A, 0x07, 0x15, 0x0E,
+ 0x01, 0x02, 0x04, 0x08,
+ 0x10,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(syndrome_table); i++)
+ if (syndrome_table[i] == syndrome)
+ return i;
+ return -ENOENT;
+}
+
+int gpmi_verify_hamming_13_8(void *data, u8 *parity, size_t size)
+{
+ int i;
+ u8 *pdata = data;
+ int bit_to_flip;
+ u8 np, syndrome;
+ int errors = 0;
+
+ for (i = 0; i < size; i ++, pdata++) {
+ np = calculate_parity(*pdata);
+ syndrome = np ^ parity[i];
+ if (syndrome == 0) /* cool */ {
+ continue;
+ }
+
+ if (even_number_of_1s(syndrome))
+ return -i; /* can't recover */
+
+ bit_to_flip = lookup_single_error(syndrome);
+ if (bit_to_flip < 0)
+ return -i; /* can't fix the error */
+
+ if (bit_to_flip < 8) {
+ *pdata ^= (1 << bit_to_flip);
+ errors++;
+ }
+ }
+ return errors;
+}
+
+void gpmi_encode_hamming_13_8(void *source_block, size_t src_size,
+ void *source_ecc, size_t ecc_size)
+{
+ int i;
+ u8 *src = source_block;
+ u8 *ecc = source_ecc;
+
+ for (i = 0; i < src_size && i < ecc_size; i++)
+ ecc[i] = calculate_parity(src[i]);
+}
+
+void gpmi_encode_hamming_ncb_13_8(void *source_block, size_t source_size,
+ void *target_block, size_t target_size)
+{
+ if (target_size < 12 + 2 * NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
+ return;
+ memset(target_block, 0xFF, target_size);
+ memcpy((u8 *)target_block + 12, source_block, source_size);
+ gpmi_encode_hamming_13_8(source_block,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ (u8 *)target_block + 12 + NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+}
+
+unsigned gpmi_hamming_ecc_size_13_8(int block_size)
+{
+ return block_size;
+}
+
diff --git a/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c b/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c
new file mode 100644
index 000000000000..8d3975efbc6e
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.c
@@ -0,0 +1,196 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * NCB software ECC Hamming code
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+#include "gpmi-hamming-22-16.h"
+
+#define BIT_VAL(v, n) (((v) >> (n)) & 0x1)
+#define B(n) (BIT_VAL(d, n))
+#define BSEQ(a1, a2, a3, a4, a5, a6, a7, a8) \
+ (B(a1) ^ B(a2) ^ B(a3) ^ B(a4) ^ B(a5) ^ B(a6) ^ B(a7) ^ B(a8))
+static u8 calculate_parity(u16 d)
+{
+ u8 p = 0;
+
+ if (d == 0 || d == 0xFFFF)
+ return 0; /* optimization :) */
+
+ p |= BSEQ(15, 12, 11, 8, 5, 4, 3, 2) << 0;
+ p |= BSEQ(13, 12, 11, 10, 9, 7, 3, 1) << 1;
+ p |= BSEQ(15, 14, 13, 11, 10, 9, 6, 5) << 2;
+ p |= BSEQ(15, 14, 13, 8, 7, 6, 4, 0) << 3;
+ p |= BSEQ(12, 9, 8, 7, 6, 2, 1, 0) << 4;
+ p |= BSEQ(14, 10, 5, 4, 3, 2, 1, 0) << 5;
+ return p;
+}
+
+static inline int even_number_of_1s(u8 byte)
+{
+ int even = 1;
+
+ while (byte > 0) {
+ even ^= (byte & 0x1);
+ byte >>= 1;
+ }
+ return even;
+}
+
+static int lookup_single_error(u8 syndrome)
+{
+ int i;
+ u8 syndrome_table[] = {
+ 0x38, 0x32, 0x31, 0x23, 0x29, 0x25, 0x1C, 0x1A,
+ 0x19, 0x16, 0x26, 0x07, 0x13, 0x0E, 0x2C, 0x0D,
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(syndrome_table); i++)
+ if (syndrome_table[i] == syndrome)
+ return i;
+ return -ENOENT;
+}
+
+int gpmi_verify_hamming_22_16(void *data, u8 *parity, size_t size)
+{
+ int i, j;
+ int bit_index, bit_to_flip;
+ u16 *pdata = data;
+ u8 p = 0, np, syndrome;
+ int errors = 0;
+
+ for (bit_index = i = j = 0;
+ i < size / sizeof(u16);
+ i ++, pdata++) {
+
+ switch (bit_index) {
+
+ case 0:
+ p = parity[j] & 0x3F;
+ break;
+ case 2:
+ p = (parity[j++] & 0xC0) >> 6;
+ p |= (parity[j] & 0x0F) << 2;
+ break;
+ case 4:
+ p = (parity[j++] & 0xF0) >> 4;
+ p |= (parity[j] & 0x03) << 4;
+ break;
+ case 6:
+ p = (parity[j++] & 0xFC) >> 2;
+ break;
+ default:
+ BUG(); /* how did you get this ?! */
+ break;
+ }
+ bit_index = (bit_index + 2) % 8;
+
+ np = calculate_parity(*pdata);
+ syndrome = np ^ p;
+ if (syndrome == 0) /* cool */ {
+ continue;
+ }
+
+ if (even_number_of_1s(syndrome))
+ return -i; /* can't recover */
+
+ bit_to_flip = lookup_single_error(syndrome);
+ if (bit_to_flip < 0)
+ return -i; /* can't fix the error */
+
+ if (bit_to_flip < 16) {
+ *pdata ^= (1 << bit_to_flip);
+ errors++;
+ }
+ }
+ return errors;
+}
+
+void gpmi_encode_hamming_22_16(void *source_block, size_t src_size,
+ void *source_ecc, size_t ecc_size)
+{
+ int i, j, bit_index;
+ u16 *src = source_block;
+ u8 *ecc = source_ecc;
+ u8 np;
+
+ for (bit_index = j = i = 0;
+ j < src_size/sizeof(u16) && i < ecc_size;
+ j++) {
+
+ np = calculate_parity(src[j]);
+
+ switch (bit_index) {
+
+ case 0:
+ ecc[i] = np & 0x3F;
+ break;
+ case 2:
+ ecc[i++] |= (np & 0x03) << 6;
+ ecc[i] = (np & 0x3C) >> 2;
+ break;
+ case 4:
+ ecc[i++] |= (np & 0x0F) << 4;
+ ecc[i] = (np & 0x30) >> 4;
+ break;
+ case 6:
+ ecc[i++] |= (np & 0x3F) << 2;
+ break;
+ }
+ bit_index = (bit_index + 2) % 8;
+ }
+
+}
+
+void gpmi_encode_hamming_ncb_22_16(void *source_block, size_t source_size,
+ void *target_block, size_t target_size)
+{
+ u8 *dst = target_block;
+ u8 ecc[NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES];
+
+ gpmi_encode_hamming_22_16(source_block,
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES,
+ ecc,
+ NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+ /* create THREE copies of source block */
+ memcpy(dst + NAND_HC_ECC_OFFSET_FIRST_DATA_COPY,
+ source_block, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_SECOND_DATA_COPY,
+ source_block, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_THIRD_DATA_COPY,
+ source_block, NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES);
+ /* ..and three copies of ECC block */
+ memcpy(dst + NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY,
+ ecc, NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY,
+ ecc, NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+ memcpy(dst + NAND_HC_ECC_OFFSET_THIRD_PARITY_COPY,
+ ecc, NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES);
+}
+
+unsigned gpmi_hamming_ecc_size_22_16(int block_size)
+{
+ return (((block_size * 8) / 16) * 6) / 8;
+}
+
diff --git a/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.h b/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.h
new file mode 100644
index 000000000000..6959bf3c599e
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi-hamming-22-16.h
@@ -0,0 +1,43 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * NCB software ECC Hamming code
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __LINUX_GPMI_H
+#define __LINUX_GPMI_H
+
+#define NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES (512)
+#define NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES \
+ ((((NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES*8)/16)*6)/8)
+#define NAND_HC_ECC_OFFSET_FIRST_DATA_COPY (0)
+#define NAND_HC_ECC_OFFSET_SECOND_DATA_COPY \
+ (NAND_HC_ECC_OFFSET_FIRST_DATA_COPY + \
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
+#define NAND_HC_ECC_OFFSET_THIRD_DATA_COPY \
+ (NAND_HC_ECC_OFFSET_SECOND_DATA_COPY + \
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
+#define NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY \
+ (NAND_HC_ECC_OFFSET_THIRD_DATA_COPY + \
+ NAND_HC_ECC_SIZEOF_DATA_BLOCK_IN_BYTES)
+#define NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY \
+ (NAND_HC_ECC_OFFSET_FIRST_PARITY_COPY + \
+ NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES)
+#define NAND_HC_ECC_OFFSET_THIRD_PARITY_COPY \
+ (NAND_HC_ECC_OFFSET_SECOND_PARITY_COPY + \
+ NAND_HC_ECC_SIZEOF_PARITY_BLOCK_IN_BYTES)
+
+#endif
diff --git a/drivers/mtd/nand/gpmi/gpmi.h b/drivers/mtd/nand/gpmi/gpmi.h
new file mode 100644
index 000000000000..99c26988f370
--- /dev/null
+++ b/drivers/mtd/nand/gpmi/gpmi.h
@@ -0,0 +1,291 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __DRIVERS_GPMI_H
+#define __DRIVERS_GPMI_H
+
+#include <linux/mtd/partitions.h>
+#include <mach/gpmi.h>
+#include <mach/regs-gpmi.h>
+#include <mach/regs-apbh.h>
+#ifdef CONFIG_MTD_NAND_GPMI_BCH
+#include <mach/regs-bch.h>
+#endif
+#include <mach/regs-ecc8.h>
+
+#include "gpmi-hamming-22-16.h"
+
+#define GPMI_ECC4_WR \
+ (BM_GPMI_ECCCTRL_ENABLE_ECC | \
+ BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_4_BIT))
+#define GPMI_ECC4_RD \
+ (BM_GPMI_ECCCTRL_ENABLE_ECC | \
+ BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__DECODE_4_BIT))
+#define GPMI_ECC8_WR \
+ (BM_GPMI_ECCCTRL_ENABLE_ECC | \
+ BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_8_BIT))
+#define GPMI_ECC8_RD \
+ (BM_GPMI_ECCCTRL_ENABLE_ECC | \
+ BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__DECODE_8_BIT))
+
+/* fingerprints of BCB that can be found on STMP-formatted flash */
+#define SIG1 "STMP"
+#define SIG_NCB "NCB "
+#define SIG_LDLB "LDLB"
+#define SIG_DBBT "DBBT"
+#define SIG_SIZE 4
+
+struct gpmi_nand_timing {
+ u8 data_setup;
+ u8 data_hold;
+ u8 address_setup;
+ u8 dsample_time;
+};
+
+struct gpmi_bcb_info {
+ struct gpmi_nand_timing timing;
+ loff_t ncbblock;
+ const void *pre_ncb;
+ size_t pre_ncb_size;
+};
+
+struct gpmi_ncb;
+
+int gpmi_erase(struct mtd_info *mtd, struct erase_info *instr);
+int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs);
+int gpmi_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip);
+int gpmi_scan_bbt(struct mtd_info *mtd);
+int gpmi_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip);
+#ifdef CONFIG_MTD_NAND_GPMI_SYSFS_ENTRIES
+int gpmi_sysfs(struct platform_device *p, int create);
+#endif
+int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int sndcmd);
+void gpmi_set_timings(struct platform_device *pdev,
+ struct gpmi_nand_timing *tm);
+int gpmi_write_ncb(struct mtd_info *mtd, struct gpmi_bcb_info *b);
+
+unsigned gpmi_hamming_ecc_size_22_16(int block_size);
+void gpmi_encode_hamming_ncb_22_16(void *source_block, size_t source_size,
+ void *target_block, size_t target_size);
+void gpmi_encode_hamming_22_16(void *source_block, size_t src_size,
+ void *source_ecc, size_t ecc_size);
+int gpmi_verify_hamming_22_16(void *data, u8 *parity, size_t size);
+
+unsigned gpmi_hamming_ecc_size_13_8(int block_size);
+void gpmi_encode_hamming_ncb_13_8(void *source_block, size_t source_size,
+ void *target_block, size_t target_size);
+void gpmi_encode_hamming_13_8(void *source_block, size_t src_size,
+ void *source_ecc, size_t ecc_size);
+int gpmi_verify_hamming_13_8(void *data, u8 *parity, size_t size);
+
+#define GPMI_DMA_MAX_CHAIN 20 /* max DMA commands in chain */
+
+/*
+ * Sizes of data buffers to exchange commands/data with NAND chip
+ * Default values cover 4K NAND page (4096 data bytes + 218 bytes OOB)
+ */
+#define GPMI_CMD_BUF_SZ 10
+#define GPMI_DATA_BUF_SZ NAND_MAX_PAGESIZE
+#define GPMI_WRITE_BUF_SZ NAND_MAX_PAGESIZE
+#define GPMI_OOB_BUF_SZ NAND_MAX_OOBSIZE
+
+#define GPMI_MAX_CHIPS 10
+
+struct gpmi_hwecc_chip {
+ char name[40];
+ struct list_head list;
+ int (*setup)(void *ctx, int index, int writesize, int oobsize);
+ int (*reset)(void *ctx, int index);
+ int (*read)(void *ctx, int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob);
+ int (*write)(void *ctx, int index,
+ struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t error,
+ dma_addr_t page, dma_addr_t oob);
+ int (*stat)(void *ctx, int index, struct mtd_ecc_stats *r);
+};
+
+/* HWECC chips */
+struct gpmi_hwecc_chip *gpmi_hwecc_chip_find(char *name);
+void gpmi_hwecc_chip_add(struct gpmi_hwecc_chip *chip);
+void gpmi_hwecc_chip_remove(struct gpmi_hwecc_chip *chip);
+int bch_init(void);
+int ecc8_init(void);
+void bch_exit(void);
+void ecc8_exit(void);
+
+
+struct gpmi_nand_data {
+ void __iomem *io_base;
+ struct clk *clk;
+ int irq;
+ struct timer_list timer;
+ int self_suspended;
+ int use_count;
+ struct regulator *regulator;
+ int reg_uA;
+
+ int ignorebad;
+ void *bbt;
+
+ struct nand_chip chip;
+ struct mtd_info mtd;
+ struct platform_device *dev;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ int nr_parts;
+ struct mtd_partition
+ *parts;
+#endif
+
+ struct completion done;
+
+ u8 *cmd_buffer;
+ dma_addr_t cmd_buffer_handle;
+ int cmd_buffer_size, cmd_buffer_sz;
+
+ u8 *write_buffer;
+ dma_addr_t write_buffer_handle;
+ int write_buffer_size;
+ u8 *read_buffer; /* point in write_buffer */
+ dma_addr_t read_buffer_handle;
+
+ u8 *data_buffer;
+ dma_addr_t data_buffer_handle;
+ int data_buffer_size;
+
+ u8 *oob_buffer;
+ dma_addr_t oob_buffer_handle;
+ int oob_buffer_size;
+
+ void *verify_buffer;
+
+ struct nchip {
+ unsigned dma_ch;
+ struct stmp37xx_circ_dma_chain chain;
+ struct stmp3xxx_dma_descriptor d[GPMI_DMA_MAX_CHAIN];
+ struct stmp3xxx_dma_descriptor error;
+ int cs;
+ } chips[GPMI_MAX_CHIPS];
+ struct nchip *cchip;
+ int selected_chip;
+
+ unsigned hwecc_type_read, hwecc_type_write;
+ int hwecc;
+
+ int ecc_oob_bytes, oob_free;
+
+ int transcribe_bbmark;
+ struct gpmi_nand_timing timing;
+
+ void (*saved_command)(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr);
+
+ int raw_oob_mode;
+ int (*saved_read_oob)(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops);
+ int (*saved_write_oob)(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops);
+
+ struct gpmi_hwecc_chip *hc;
+
+ int numchips;
+ int custom_partitions;
+ struct mtd_info *masters[GPMI_MAX_CHIPS];
+ struct mtd_partition chip_partitions[GPMI_MAX_CHIPS];
+ int n_concat;
+ struct mtd_info *concat[GPMI_MAX_CHIPS];
+ struct mtd_info *concat_mtd;
+};
+
+extern struct gpmi_nand_timing gpmi_safe_timing;
+
+struct gpmi_ncb {
+ u32 fingerprint1;
+ struct gpmi_nand_timing timing;
+ u32 pagesize;
+ u32 page_plus_oob_size;
+ u32 sectors_per_block;
+ u32 sector_in_page_mask;
+ u32 sector_to_page_shift;
+ u32 num_nands;
+ u32 reserved[3];
+ u32 fingerprint2; /* offset 0x2C */
+};
+
+struct gpmi_ldlb {
+ u32 fingerprint1;
+ u16 major, minor, sub, reserved;
+ u32 nand_bitmap;
+ u32 reserved1[7];
+ u32 fingerprint2;
+ struct {
+ u32 fw_starting_nand;
+ u32 fw_starting_sector;
+ u32 fw_sector_stride;
+ u32 fw_sectors_total;
+ } fw[2];
+ u16 fw_major, fw_minor, fw_sub, fw_reserved;
+ u32 bbt_blk;
+ u32 bbt_blk_backup;
+};
+
+static inline void gpmi_block_mark_as(struct nand_chip *chip,
+ int block, int mark)
+{
+ u32 o;
+ int shift = (block & 0x03) << 1,
+ index = block >> 2;
+
+ if (chip->bbt) {
+ mark &= 0x03;
+
+ o = chip->bbt[index];
+ o &= ~(0x03 << shift);
+ o |= (mark << shift);
+ chip->bbt[index] = o;
+ }
+}
+
+static inline int gpmi_block_badness(struct nand_chip *chip,
+ int block)
+{
+ u32 o;
+ int shift = (block & 0x03) << 1,
+ index = block >> 2;
+
+ if (chip->bbt) {
+ o = (chip->bbt[index] >> shift) & 0x03;
+ pr_debug("%s: block = %d, o = %d\n", __func__, block, o);
+ return o;
+ }
+ return -1;
+}
+
+#ifdef CONFIG_STMP3XXX_UNIQUE_ID
+int __init gpmi_uid_init(const char *name, struct mtd_info *mtd,
+ u_int32_t start, u_int32_t size);
+void gpmi_uid_remove(const char *name);
+#else
+#define gpmi_uid_init(name, mtd, start, size)
+#define gpmi_uid_remove(name)
+#endif
+
+#endif
diff --git a/drivers/mtd/nand/imx_nfc.c b/drivers/mtd/nand/imx_nfc.c
new file mode 100644
index 000000000000..65f72b4e468a
--- /dev/null
+++ b/drivers/mtd/nand/imx_nfc.c
@@ -0,0 +1,8286 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+
+#define DRIVER_VERSION "1.0"
+
+/* Define this macro to enable event reporting. */
+
+#define EVENT_REPORTING
+
+/*
+ * For detailed information that will be helpful in understanding this driver,
+ * see:
+ *
+ * Documentation/imx_nfc.txt
+ */
+
+/*
+ * Macros that describe NFC hardware have names of the form:
+ *
+ * NFC_*
+ *
+ * Macros that apply only to specific versions of the NFC have names of the
+ * following form:
+ *
+ * NFC_<M>_<N>_*
+ *
+ * where:
+ *
+ * <M> is the major version of the NFC hardware.
+ * <N> is the minor version of the NFC hardware.
+ *
+ * The minor version can be 'X', which means that the macro applies to *all*
+ * NFCs of the same major version.
+ *
+ * For NFC versions with only one set of registers, macros that give offsets
+ * against the base address have names of the form:
+ *
+ * *<RegisterName>_REG_OFF
+ *
+ * Macros that give the position of a field's LSB within a given register have
+ * names of the form:
+ *
+ * *<RegisterName>_<FieldName>_POS
+ *
+ * Macros that mask a field within a given register have names of the form:
+ *
+ * *<RegisterName>_<FieldName>_MSK
+ */
+
+/*
+ * Macro definitions for ALL NFC versions.
+ */
+
+#define NFC_MAIN_BUF_SIZE (512)
+
+/*
+ * Macro definitions for version 1.0 NFCs.
+ */
+
+#define NFC_1_0_BUF_SIZE_REG_OFF (0x00)
+#define NFC_1_0_BUF_ADDR_REG_OFF (0x04)
+#define NFC_1_0_FLASH_ADDR_REG_OFF (0x06)
+#define NFC_1_0_FLASH_CMD_REG_OFF (0x08)
+#define NFC_1_0_CONFIG_REG_OFF (0x0A)
+#define NFC_1_0_ECC_STATUS_RESULT_REG_OFF (0x0C)
+#define NFC_1_0_RSLTMAIN_AREA_REG_OFF (0x0E)
+#define NFC_1_0_RSLTSPARE_AREA_REG_OFF (0x10)
+#define NFC_1_0_WRPROT_REG_OFF (0x12)
+#define NFC_1_0_UNLOCKSTART_BLKADDR_REG_OFF (0x14)
+#define NFC_1_0_UNLOCKEND_BLKADDR_REG_OFF (0x16)
+#define NFC_1_0_NF_WRPRST_REG_OFF (0x18)
+
+#define NFC_1_0_CONFIG1_REG_OFF (0x1A)
+#define NFC_1_0_CONFIG1_NF_CE_POS (7)
+#define NFC_1_0_CONFIG1_NF_CE_MSK (0x1 << 7)
+
+#define NFC_1_0_CONFIG2_REG_OFF (0x1C)
+
+/*
+* Macro definitions for version 2.X NFCs.
+*/
+
+#define NFC_2_X_FLASH_ADDR_REG_OFF (0x06)
+#define NFC_2_X_FLASH_CMD_REG_OFF (0x08)
+#define NFC_2_X_CONFIG_REG_OFF (0x0A)
+
+#define NFC_2_X_WR_PROT_REG_OFF (0x12)
+
+#define NFC_2_X_NF_WR_PR_ST_REG_OFF (0x18)
+
+#define NFC_2_X_CONFIG2_REG_OFF (0x1C)
+#define NFC_2_X_CONFIG2_FCMD_POS (0)
+#define NFC_2_X_CONFIG2_FCMD_MSK (0x1 << 0)
+#define NFC_2_X_CONFIG2_FADD_POS (1)
+#define NFC_2_X_CONFIG2_FADD_MSK (0x1 << 1)
+#define NFC_2_X_CONFIG2_FDI_POS (2)
+#define NFC_2_X_CONFIG2_FDI_MSK (0x1 << 2)
+#define NFC_2_X_CONFIG2_FDO_POS (3)
+#define NFC_2_X_CONFIG2_FDO_MSK (0x7 << 3)
+#define NFC_2_X_CONFIG2_INT_POS (15)
+#define NFC_2_X_CONFIG2_INT_MSK (0x1 << 15)
+
+/*
+* Macro definitions for version 2.0 NFCs.
+*/
+
+#define NFC_2_0_BUF_ADDR_REG_OFF (0x04)
+#define NFC_2_0_BUF_ADDR_RBA_POS (0)
+#define NFC_2_0_BUF_ADDR_RBA_MSK (0xf << 0)
+
+#define NFC_2_0_ECC_STATUS_REG_OFF (0x0C)
+#define NFC_2_0_ECC_STATUS_NOSER1_POS (0)
+#define NFC_2_0_ECC_STATUS_NOSER1_MSK (0xF << 0)
+#define NFC_2_0_ECC_STATUS_NOSER2_POS (4)
+#define NFC_2_0_ECC_STATUS_NOSER2_MSK (0xF << 4)
+#define NFC_2_0_ECC_STATUS_NOSER3_POS (8)
+#define NFC_2_0_ECC_STATUS_NOSER3_MSK (0xF << 8)
+#define NFC_2_0_ECC_STATUS_NOSER4_POS (12)
+#define NFC_2_0_ECC_STATUS_NOSER4_MSK (0xF << 12)
+
+#define NFC_2_0_CONFIG1_REG_OFF (0x1A)
+#define NFC_2_0_CONFIG1_SP_EN_POS (2)
+#define NFC_2_0_CONFIG1_SP_EN_MSK (0x1 << 2)
+#define NFC_2_0_CONFIG1_ECC_EN_POS (3)
+#define NFC_2_0_CONFIG1_ECC_EN_MSK (0x1 << 3)
+#define NFC_2_0_CONFIG1_INT_MSK_POS (4)
+#define NFC_2_0_CONFIG1_INT_MSK_MSK (0x1 << 4)
+#define NFC_2_0_CONFIG1_NF_BIG_POS (5)
+#define NFC_2_0_CONFIG1_NF_BIG_MSK (0x1 << 5)
+#define NFC_2_0_CONFIG1_NFC_RST_POS (6)
+#define NFC_2_0_CONFIG1_NFC_RST_MSK (0x1 << 6)
+#define NFC_2_0_CONFIG1_NF_CE_POS (7)
+#define NFC_2_0_CONFIG1_NF_CE_MSK (0x1 << 7)
+#define NFC_2_0_CONFIG1_ONE_CYLE_POS (8)
+#define NFC_2_0_CONFIG1_ONE_CYLE_MSK (0x1 << 8)
+#define NFC_2_0_CONFIG1_MLC_POS (9)
+#define NFC_2_0_CONFIG1_MLC_MSK (0x1 << 9)
+
+#define NFC_2_0_UNLOCK_START_REG_OFF (0x14)
+#define NFC_2_0_UNLOCK_END_REG_OFF (0x16)
+
+/*
+* Macro definitions for version 2.1 NFCs.
+*/
+
+#define NFC_2_1_BUF_ADDR_REG_OFF (0x04)
+#define NFC_2_1_BUF_ADDR_RBA_POS (0)
+#define NFC_2_1_BUF_ADDR_RBA_MSK (0x7 << 0)
+#define NFC_2_1_BUF_ADDR_CS_POS (4)
+#define NFC_2_1_BUF_ADDR_CS_MSK (0x3 << 4)
+
+#define NFC_2_1_ECC_STATUS_REG_OFF (0x0C)
+#define NFC_2_1_ECC_STATUS_NOSER1_POS (0)
+#define NFC_2_1_ECC_STATUS_NOSER1_MSK (0xF << 0)
+#define NFC_2_1_ECC_STATUS_NOSER2_POS (4)
+#define NFC_2_1_ECC_STATUS_NOSER2_MSK (0xF << 4)
+#define NFC_2_1_ECC_STATUS_NOSER3_POS (8)
+#define NFC_2_1_ECC_STATUS_NOSER3_MSK (0xF << 8)
+#define NFC_2_1_ECC_STATUS_NOSER4_POS (12)
+#define NFC_2_1_ECC_STATUS_NOSER4_MSK (0xF << 12)
+
+#define NFC_2_1_CONFIG1_REG_OFF (0x1A)
+#define NFC_2_1_CONFIG1_ECC_MODE_POS (0)
+#define NFC_2_1_CONFIG1_ECC_MODE_MSK (0x1 << 0)
+#define NFC_2_1_CONFIG1_DMA_MODE_POS (1)
+#define NFC_2_1_CONFIG1_DMA_MODE_MSK (0x1 << 1)
+#define NFC_2_1_CONFIG1_SP_EN_POS (2)
+#define NFC_2_1_CONFIG1_SP_EN_MSK (0x1 << 2)
+#define NFC_2_1_CONFIG1_ECC_EN_POS (3)
+#define NFC_2_1_CONFIG1_ECC_EN_MSK (0x1 << 3)
+#define NFC_2_1_CONFIG1_INT_MSK_POS (4)
+#define NFC_2_1_CONFIG1_INT_MSK_MSK (0x1 << 4)
+#define NFC_2_1_CONFIG1_NF_BIG_POS (5)
+#define NFC_2_1_CONFIG1_NF_BIG_MSK (0x1 << 5)
+#define NFC_2_1_CONFIG1_NFC_RST_POS (6)
+#define NFC_2_1_CONFIG1_NFC_RST_MSK (0x1 << 6)
+#define NFC_2_1_CONFIG1_NF_CE_POS (7)
+#define NFC_2_1_CONFIG1_NF_CE_MSK (0x1 << 7)
+#define NFC_2_1_CONFIG1_SYM_POS (8)
+#define NFC_2_1_CONFIG1_SYM_MSK (0x1 << 8)
+#define NFC_2_1_CONFIG1_PPB_POS (9)
+#define NFC_2_1_CONFIG1_PPB_MSK (0x3 << 9)
+#define NFC_2_1_CONFIG1_FP_INT_POS (11)
+#define NFC_2_1_CONFIG1_FP_INT_MSK (0x1 << 11)
+
+#define NFC_2_1_UNLOCK_START_0_REG_OFF (0x20)
+#define NFC_2_1_UNLOCK_END_0_REG_OFF (0x22)
+#define NFC_2_1_UNLOCK_START_1_REG_OFF (0x24)
+#define NFC_2_1_UNLOCK_END_1_REG_OFF (0x26)
+#define NFC_2_1_UNLOCK_START_2_REG_OFF (0x28)
+#define NFC_2_1_UNLOCK_END_2_REG_OFF (0x2A)
+#define NFC_2_1_UNLOCK_START_3_REG_OFF (0x2C)
+#define NFC_2_1_UNLOCK_END_3_REG_OFF (0x2E)
+
+/*
+* Macro definitions for version 3.X NFCs.
+*/
+
+/*
+* Macro definitions for version 3.1 NFCs.
+*/
+
+#define NFC_3_1_FLASH_ADDR_CMD_REG_OFF (0x00)
+#define NFC_3_1_CONFIG1_REG_OFF (0x04)
+#define NFC_3_1_ECC_STATUS_RESULT_REG_OFF (0x08)
+#define NFC_3_1_LAUNCH_NFC_REG_OFF (0x0C)
+
+#define NFC_3_1_WRPROT_REG_OFF (0x00)
+#define NFC_3_1_WRPROT_UNLOCK_BLK_ADD0_REG_OFF (0x04)
+#define NFC_3_1_CONFIG2_REG_OFF (0x14)
+#define NFC_3_1_IPC_REG_OFF (0x18)
+
+/*
+* Macro definitions for version 3.2 NFCs.
+*/
+
+#define NFC_3_2_CMD_REG_OFF (0x00)
+
+#define NFC_3_2_ADD0_REG_OFF (0x04)
+#define NFC_3_2_ADD1_REG_OFF (0x08)
+#define NFC_3_2_ADD2_REG_OFF (0x0C)
+#define NFC_3_2_ADD3_REG_OFF (0x10)
+#define NFC_3_2_ADD4_REG_OFF (0x14)
+#define NFC_3_2_ADD5_REG_OFF (0x18)
+#define NFC_3_2_ADD6_REG_OFF (0x1C)
+#define NFC_3_2_ADD7_REG_OFF (0x20)
+#define NFC_3_2_ADD8_REG_OFF (0x24)
+#define NFC_3_2_ADD9_REG_OFF (0x28)
+#define NFC_3_2_ADD10_REG_OFF (0x2C)
+#define NFC_3_2_ADD11_REG_OFF (0x30)
+
+#define NFC_3_2_CONFIG1_REG_OFF (0x34)
+#define NFC_3_2_CONFIG1_SP_EN_POS (0)
+#define NFC_3_2_CONFIG1_SP_EN_MSK (0x1 << 0)
+#define NFC_3_2_CONFIG1_NF_CE_POS (1)
+#define NFC_3_2_CONFIG1_NF_CE_MSK (0x1 << 1)
+#define NFC_3_2_CONFIG1_RST_POS (2)
+#define NFC_3_2_CONFIG1_RST_MSK (0x1 << 2)
+#define NFC_3_2_CONFIG1_RBA_POS (4)
+#define NFC_3_2_CONFIG1_RBA_MSK (0x7 << 4)
+#define NFC_3_2_CONFIG1_ITER_POS (8)
+#define NFC_3_2_CONFIG1_ITER_MSK (0xf << 8)
+#define NFC_3_2_CONFIG1_CS_POS (12)
+#define NFC_3_2_CONFIG1_CS_MSK (0x7 << 12)
+#define NFC_3_2_CONFIG1_STATUS_POS (16)
+#define NFC_3_2_CONFIG1_STATUS_MSK (0xFFFF<<16)
+
+#define NFC_3_2_ECC_STATUS_REG_OFF (0x38)
+#define NFC_3_2_ECC_STATUS_NOBER1_POS (0)
+#define NFC_3_2_ECC_STATUS_NOBER1_MSK (0xF << 0)
+#define NFC_3_2_ECC_STATUS_NOBER2_POS (4)
+#define NFC_3_2_ECC_STATUS_NOBER2_MSK (0xF << 4)
+#define NFC_3_2_ECC_STATUS_NOBER3_POS (8)
+#define NFC_3_2_ECC_STATUS_NOBER3_MSK (0xF << 8)
+#define NFC_3_2_ECC_STATUS_NOBER4_POS (12)
+#define NFC_3_2_ECC_STATUS_NOBER4_MSK (0xF << 12)
+#define NFC_3_2_ECC_STATUS_NOBER5_POS (16)
+#define NFC_3_2_ECC_STATUS_NOBER5_MSK (0xF << 16)
+#define NFC_3_2_ECC_STATUS_NOBER6_POS (20)
+#define NFC_3_2_ECC_STATUS_NOBER6_MSK (0xF << 20)
+#define NFC_3_2_ECC_STATUS_NOBER7_POS (24)
+#define NFC_3_2_ECC_STATUS_NOBER7_MSK (0xF << 24)
+#define NFC_3_2_ECC_STATUS_NOBER8_POS (28)
+#define NFC_3_2_ECC_STATUS_NOBER8_MSK (0xF << 28)
+
+
+#define NFC_3_2_STATUS_SUM_REG_OFF (0x3C)
+#define NFC_3_2_STATUS_SUM_NAND_SUM_POS (0x0)
+#define NFC_3_2_STATUS_SUM_NAND_SUM_MSK (0xFF << 0)
+#define NFC_3_2_STATUS_SUM_ECC_SUM_POS (8)
+#define NFC_3_2_STATUS_SUM_ECC_SUM_MSK (0xFF << 8)
+
+#define NFC_3_2_LAUNCH_REG_OFF (0x40)
+#define NFC_3_2_LAUNCH_FCMD_POS (0)
+#define NFC_3_2_LAUNCH_FCMD_MSK (0x1 << 0)
+#define NFC_3_2_LAUNCH_FADD_POS (1)
+#define NFC_3_2_LAUNCH_FADD_MSK (0x1 << 1)
+#define NFC_3_2_LAUNCH_FDI_POS (2)
+#define NFC_3_2_LAUNCH_FDI_MSK (0x1 << 2)
+#define NFC_3_2_LAUNCH_FDO_POS (3)
+#define NFC_3_2_LAUNCH_FDO_MSK (0x7 << 3)
+#define NFC_3_2_LAUNCH_AUTO_PROG_POS (6)
+#define NFC_3_2_LAUNCH_AUTO_PROG_MSK (0x1 << 6)
+#define NFC_3_2_LAUNCH_AUTO_READ_POS (7)
+#define NFC_3_2_LAUNCH_AUTO_READ_MSK (0x1 << 7)
+#define NFC_3_2_LAUNCH_AUTO_ERASE_POS (9)
+#define NFC_3_2_LAUNCH_AUTO_ERASE_MSK (0x1 << 9)
+#define NFC_3_2_LAUNCH_COPY_BACK0_POS (10)
+#define NFC_3_2_LAUNCH_COPY_BACK0_MSK (0x1 << 10)
+#define NFC_3_2_LAUNCH_COPY_BACK1_POS (11)
+#define NFC_3_2_LAUNCH_COPY_BACK1_MSK (0x1 << 11)
+#define NFC_3_2_LAUNCH_AUTO_STATUS_POS (12)
+#define NFC_3_2_LAUNCH_AUTO_STATUS_MSK (0x1 << 12)
+
+#define NFC_3_2_WRPROT_REG_OFF (0x00)
+#define NFC_3_2_WRPROT_WPC_POS (0)
+#define NFC_3_2_WRPROT_WPC_MSK (0x7 << 0)
+#define NFC_3_2_WRPROT_CS2L_POS (3)
+#define NFC_3_2_WRPROT_CS2L_MSK (0x7 << 3)
+#define NFC_3_2_WRPROT_BLS_POS (6)
+#define NFC_3_2_WRPROT_BLS_MSK (0x3 << 6)
+#define NFC_3_2_WRPROT_LTS0_POS (8)
+#define NFC_3_2_WRPROT_LTS0_MSK (0x1 << 8)
+#define NFC_3_2_WRPROT_LS0_POS (9)
+#define NFC_3_2_WRPROT_LS0_MSK (0x1 << 9)
+#define NFC_3_2_WRPROT_US0_POS (10)
+#define NFC_3_2_WRPROT_US0_MSK (0x1 << 10)
+#define NFC_3_2_WRPROT_LTS1_POS (11)
+#define NFC_3_2_WRPROT_LTS1_MSK (0x1 << 11)
+#define NFC_3_2_WRPROT_LS1_POS (12)
+#define NFC_3_2_WRPROT_LS1_MSK (0x1 << 12)
+#define NFC_3_2_WRPROT_US1_POS (13)
+#define NFC_3_2_WRPROT_US1_MSK (0x1 << 13)
+#define NFC_3_2_WRPROT_LTS2_POS (14)
+#define NFC_3_2_WRPROT_LTS2_MSK (0x1 << 14)
+#define NFC_3_2_WRPROT_LS2_POS (15)
+#define NFC_3_2_WRPROT_LS2_MSK (0x1 << 15)
+#define NFC_3_2_WRPROT_US2_POS (16)
+#define NFC_3_2_WRPROT_US2_MSK (0x1 << 16)
+#define NFC_3_2_WRPROT_LTS3_POS (17)
+#define NFC_3_2_WRPROT_LTS3_MSK (0x1 << 17)
+#define NFC_3_2_WRPROT_LS3_POS (18)
+#define NFC_3_2_WRPROT_LS3_MSK (0x1 << 18)
+#define NFC_3_2_WRPROT_US3_POS (19)
+#define NFC_3_2_WRPROT_US3_MSK (0x1 << 19)
+#define NFC_3_2_WRPROT_LTS4_POS (20)
+#define NFC_3_2_WRPROT_LTS4_MSK (0x1 << 20)
+#define NFC_3_2_WRPROT_LS4_POS (21)
+#define NFC_3_2_WRPROT_LS4_MSK (0x1 << 21)
+#define NFC_3_2_WRPROT_US4_POS (22)
+#define NFC_3_2_WRPROT_US4_MSK (0x1 << 22)
+#define NFC_3_2_WRPROT_LTS5_POS (23)
+#define NFC_3_2_WRPROT_LTS5_MSK (0x1 << 23)
+#define NFC_3_2_WRPROT_LS5_POS (24)
+#define NFC_3_2_WRPROT_LS5_MSK (0x1 << 24)
+#define NFC_3_2_WRPROT_US5_POS (25)
+#define NFC_3_2_WRPROT_US5_MSK (0x1 << 25)
+#define NFC_3_2_WRPROT_LTS6_POS (26)
+#define NFC_3_2_WRPROT_LTS6_MSK (0x1 << 26)
+#define NFC_3_2_WRPROT_LS6_POS (27)
+#define NFC_3_2_WRPROT_LS6_MSK (0x1 << 27)
+#define NFC_3_2_WRPROT_US6_POS (28)
+#define NFC_3_2_WRPROT_US6_MSK (0x1 << 28)
+#define NFC_3_2_WRPROT_LTS7_POS (29)
+#define NFC_3_2_WRPROT_LTS7_MSK (0x1 << 29)
+#define NFC_3_2_WRPROT_LS7_POS (30)
+#define NFC_3_2_WRPROT_LS7_MSK (0x1 << 30)
+#define NFC_3_2_WRPROT_US7_POS (31)
+#define NFC_3_2_WRPROT_US7_MSK (0x1 << 31)
+
+#define NFC_3_2_UNLOCK_BLK_ADD0_REG_OFF (0x04)
+#define NFC_3_2_UNLOCK_BLK_ADD0_USBA0_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD0_USBA0_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD0_UEBA0_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD0_UEBA0_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD1_REG_OFF (0x08)
+#define NFC_3_2_UNLOCK_BLK_ADD1_USBA1_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD1_USBA1_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD1_UEBA1_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD1_UEBA1_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD2_REG_OFF (0x0C)
+#define NFC_3_2_UNLOCK_BLK_ADD2_USBA2_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD2_USBA2_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD2_UEBA2_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD2_UEBA2_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD3_REG_OFF (0x10)
+#define NFC_3_2_UNLOCK_BLK_ADD3_USBA3_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD3_USBA3_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD3_UEBA3_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD3_UEBA3_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD4_REG_OFF (0x14)
+#define NFC_3_2_UNLOCK_BLK_ADD4_USBA4_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD4_USBA4_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD4_UEBA4_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD4_UEBA4_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD5_REG_OFF (0x18)
+#define NFC_3_2_UNLOCK_BLK_ADD5_USBA5_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD5_USBA5_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD5_UEBA5_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD5_UEBA5_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD6_REG_OFF (0x1C)
+#define NFC_3_2_UNLOCK_BLK_ADD6_USBA6_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD6_USBA6_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD6_UEBA6_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD6_UEBA6_MSK (0xFFFF<<16)
+
+#define NFC_3_2_UNLOCK_BLK_ADD7_REG_OFF (0x20)
+#define NFC_3_2_UNLOCK_BLK_ADD7_USBA7_POS (0)
+#define NFC_3_2_UNLOCK_BLK_ADD7_USBA7_MSK (0xFFFF << 0)
+#define NFC_3_2_UNLOCK_BLK_ADD7_UEBA7_POS (16)
+#define NFC_3_2_UNLOCK_BLK_ADD7_UEBA7_MSK (0xFFFF<<16)
+
+#define NFC_3_2_CONFIG2_REG_OFF (0x24)
+#define NFC_3_2_CONFIG2_PS_POS (0)
+#define NFC_3_2_CONFIG2_PS_MSK (0x3 << 0)
+#define NFC_3_2_CONFIG2_SYM_POS (2)
+#define NFC_3_2_CONFIG2_SYM_MSK (0x1 << 2)
+#define NFC_3_2_CONFIG2_ECC_EN_POS (3)
+#define NFC_3_2_CONFIG2_ECC_EN_MSK (0x1 << 3)
+#define NFC_3_2_CONFIG2_CMD_PHASES_POS (4)
+#define NFC_3_2_CONFIG2_CMD_PHASES_MSK (0x1 << 4)
+#define NFC_3_2_CONFIG2_ADDR_PHASES0_POS (5)
+#define NFC_3_2_CONFIG2_ADDR_PHASES0_MSK (0x1 << 5)
+#define NFC_3_2_CONFIG2_ECC_MODE_POS (6)
+#define NFC_3_2_CONFIG2_ECC_MODE_MSK (0x1 << 6)
+#define NFC_3_2_CONFIG2_PPB_POS (7)
+#define NFC_3_2_CONFIG2_PPB_MSK (0x3 << 7)
+#define NFC_3_2_CONFIG2_EDC_POS (9)
+#define NFC_3_2_CONFIG2_EDC_MSK (0x7 << 9)
+#define NFC_3_2_CONFIG2_ADDR_PHASES1_POS (12)
+#define NFC_3_2_CONFIG2_ADDR_PHASES1_MSK (0x3 << 12)
+#define NFC_3_2_CONFIG2_AUTO_DONE_MSK_POS (14)
+#define NFC_3_2_CONFIG2_AUTO_DONE_MSK_MSK (0x1 << 14)
+#define NFC_3_2_CONFIG2_INT_MSK_POS (15)
+#define NFC_3_2_CONFIG2_INT_MSK_MSK (0x1 << 15)
+#define NFC_3_2_CONFIG2_SPAS_POS (16)
+#define NFC_3_2_CONFIG2_SPAS_MSK (0xFF << 16)
+#define NFC_3_2_CONFIG2_ST_CMD_POS (24)
+#define NFC_3_2_CONFIG2_ST_CMD_MSK (0xFF << 24)
+
+#define NFC_3_2_CONFIG3_REG_OFF (0x28)
+#define NFC_3_2_CONFIG3_ADD_OP_POS (0)
+#define NFC_3_2_CONFIG3_ADD_OP_MSK (0x3 << 0)
+#define NFC_3_2_CONFIG3_TOO_POS (2)
+#define NFC_3_2_CONFIG3_TOO_MSK (0x1 << 2)
+#define NFC_3_2_CONFIG3_FW_POS (3)
+#define NFC_3_2_CONFIG3_FW_MSK (0x1 << 3)
+#define NFC_3_2_CONFIG3_SB2R_POS (4)
+#define NFC_3_2_CONFIG3_SB2R_MSK (0x7 << 4)
+#define NFC_3_2_CONFIG3_NF_BIG_POS (7)
+#define NFC_3_2_CONFIG3_NF_BIG_MSK (0x1 << 7)
+#define NFC_3_2_CONFIG3_SBB_POS (8)
+#define NFC_3_2_CONFIG3_SBB_MSK (0x7 << 8)
+#define NFC_3_2_CONFIG3_DMA_MODE_POS (11)
+#define NFC_3_2_CONFIG3_DMA_MODE_MSK (0x1 << 11)
+#define NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS (12)
+#define NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK (0x7 << 12)
+#define NFC_3_2_CONFIG3_RBB_MODE_POS (15)
+#define NFC_3_2_CONFIG3_RBB_MODE_MSK (0x1 << 15)
+#define NFC_3_2_CONFIG3_FMP_POS (16)
+#define NFC_3_2_CONFIG3_FMP_MSK (0xF << 16)
+#define NFC_3_2_CONFIG3_NO_SDMA_POS (20)
+#define NFC_3_2_CONFIG3_NO_SDMA_MSK (0x1 << 20)
+
+#define NFC_3_2_IPC_REG_OFF (0x2C)
+#define NFC_3_2_IPC_CREQ_POS (0)
+#define NFC_3_2_IPC_CREQ_MSK (0x1 << 0)
+#define NFC_3_2_IPC_CACK_POS (1)
+#define NFC_3_2_IPC_CACK_MSK (0x1 << 1)
+#define NFC_3_2_IPC_DMA_STATUS_POS (26)
+#define NFC_3_2_IPC_DMA_STATUS_MSK (0x3 << 26)
+#define NFC_3_2_IPC_RB_B_POS (28)
+#define NFC_3_2_IPC_RB_B_MSK (0x1 << 28)
+#define NFC_3_2_IPC_LPS_POS (29)
+#define NFC_3_2_IPC_LPS_MSK (0x1 << 29)
+#define NFC_3_2_IPC_AUTO_PROG_DONE_POS (30)
+#define NFC_3_2_IPC_AUTO_PROG_DONE_MSK (0x1 << 30)
+#define NFC_3_2_IPC_INT_POS (31)
+#define NFC_3_2_IPC_INT_MSK (0x1 << 31)
+
+#define NFC_3_2_AXI_ERR_ADD_REG_OFF (0x30)
+
+/**
+ * enum override - Choices for overrides.
+ *
+ * Some functions of this driver can be overriden at run time. This is a
+ * convenient enumerated type for all such options.
+ */
+
+enum override {
+ NEVER = -1,
+ DRIVER_CHOICE = 0,
+ ALWAYS = 1,
+};
+
+/**
+ * struct physical_geometry - Physical geometry description.
+ *
+ * This structure describes the physical geometry of the medium.
+ *
+ * @chip_count: The number of chips in the medium.
+ * @chip_size: The size, in bytes, of a single chip (excluding the
+ * out-of-band bytes).
+ * @block_size: The size, in bytes, of a single block (excluding the
+ * out-of-band bytes).
+ * @page_data_size: The size, in bytes, of the data area in a page (excluding
+ * the out-of-band bytes).
+ * @page_oob_size: The size, in bytes, of the out-of-band area in a page.
+ */
+
+struct physical_geometry {
+ unsigned int chip_count;
+ uint64_t chip_size;
+ unsigned int block_size;
+ unsigned int page_data_size;
+ unsigned int page_oob_size;
+};
+
+/**
+ * struct nfc_geometry - NFC geometry description.
+ *
+ * This structure describes the NFC's view of the medium geometry, which may be
+ * different from the physical geometry (for example, we treat pages that are
+ * physically 2K+112 as if they are 2K+64).
+ *
+ * @page_data_size: The size of the data area in a page (excluding the
+ * out-of-band bytes). This is almost certain to be the same
+ * as the physical data size.
+ * @page_oob_size: The size of the out-of-band area in a page. This may be
+ * different from the physical OOB size.
+ * @ecc_algorithm: The name of the ECC algorithm (e.g., "Reed-Solomon" or
+ * "BCH").
+ * @ecc_strength: A number that describes the strength of the ECC algorithm.
+ * For example, various i.MX SoC's support ECC-1, ECC-4 or
+ * ECC-8 of the Reed-Solomon ECC algorithm.
+ * @buffer_count: The number of main/spare buffers used with this geometry.
+ * @spare_buf_size: The number of bytes held in each spare buffer.
+ * @spare_buf_spill: The number of extra bytes held in the last spare buffer.
+ * @mtd_layout: The MTD layout that best matches the geometry described by
+ * the rest of this structure. The logical layer might not use
+ * this structure, especially when interleaving.
+ */
+
+struct nfc_geometry {
+ const unsigned int page_data_size;
+ const unsigned int page_oob_size;
+ const char *ecc_algorithm;
+ const int ecc_strength;
+ const unsigned int buffer_count;
+ const unsigned int spare_buf_size;
+ const unsigned int spare_buf_spill;
+ struct nand_ecclayout mtd_layout;
+};
+
+/**
+ * struct logical_geometry - Logical geometry description.
+ *
+ * This structure describes the logical geometry we expose to MTD. This geometry
+ * may be different from the physical or NFC geometries, especially when
+ * interleaving.
+ *
+ * @chip_count: The number of chips in the medium.
+ * @chip_size: The size, in bytes, of a single chip (excluding the
+ * out-of-band bytes).
+ * @usable_size: The size, in bytes, of the medium that MTD can actually
+ * use. This may be less than the chip size multiplied by the
+ * number of chips.
+ * @block_size: The size, in bytes, of a single block (excluding the
+ * out-of-band bytes).
+ * @page_data_size: The size of the data area in a page (excluding the
+ * out-of-band bytes).
+ * @page_oob_size: The size of the out-of-band area in a page.
+ */
+
+struct logical_geometry {
+ unsigned int chip_count;
+ uint32_t chip_size;
+ uint32_t usable_size;
+ unsigned int block_size;
+ unsigned int page_data_size;
+ unsigned int page_oob_size;
+ struct nand_ecclayout *mtd_layout;
+};
+
+/**
+ * struct imx_nfc_data - i.MX NFC per-device data.
+ *
+ * Note that the "device" managed by this driver represents the NAND Flash
+ * controller *and* the NAND Flash medium behind it. Thus, the per-device data
+ * structure has information about the controller, the chips to which it is
+ * connected, and properties of the medium as a whole.
+ *
+ * @dev: A pointer to the owning struct device.
+ * @pdev: A pointer to the owning struct platform_device.
+ * @pdata: A pointer to the device's platform data.
+ * @buffers: A pointer to the NFC buffers.
+ * @primary_regs: A pointer to the NFC primary registers.
+ * @secondary_regs: A pointer to the NFC secondary registers.
+ * @clock: A pointer to the NFC struct clk.
+ * @interrupt: The NFC interrupt number.
+ * @physical_geometry: A description of the medium's physical geometry.
+ * @nfc: A pointer to the NFC HAL.
+ * @nfc_geometry: A description of the medium geometry as viewed by the
+ * NFC.
+ * @done: The struct completion we use to handle interrupts.
+ * @logical_geometry: A description of the logical geometry exposed to MTD.
+ * @interrupt_override: Can override how the driver uses interrupts when
+ * waiting for the NFC.
+ * @auto_op_override: Can override whether the driver uses automatic NFC
+ * operations.
+ * @inject_ecc_error: Indicates that the driver should inject an ECC error in
+ * the next read operation that uses ECC. User space
+ * programs can set this value through the sysfs node of
+ * the same name. If this value is less than zero, the
+ * driver will inject an uncorrectable ECC error. If this
+ * value is greater than zero, the driver will inject that
+ * number of correctable errors, capped at whatever
+ * possible maximum currently applies.
+ * @current_chip: The chip currently selected by the NAND Fash MTD HIL.
+ * A negative value indicates that no chip is selected.
+ * We use this field to detect when the HIL begins and
+ * ends essential transactions. This helps us to know when
+ * we should turn the NFC clock on or off.
+ * @command: The last command the HIL tried to send by calling
+ * cmdfunc(). Later functions use this information to
+ * adjust their behavior. The sentinel value ~0 indicates
+ * no command.
+ * @command_is_new: Indicates the command has just come down to cmdfunc()
+ * from the HIL and hasn't yet been handled. Other
+ * functions use this to adjust their behavior.
+ * @page_address: The current page address of interest. For reads, this
+ * information arrives in calls to cmdfunc(), but we don't
+ * actually use it until later.
+ * @nand: The data structure that represents this NAND Flash
+ * medium to the MTD NAND Flash system.
+ * @mtd: The data structure that represents this NAND Flash
+ * medium to MTD.
+ * @partitions: A pointer to a set of partitions collected from one of
+ * several possible sources (e.g., the boot loader, the
+ * kernel command line, etc.). See the global variable
+ * partition_source_types for the list of partition
+ * sources we examine. If this member is NULL, then no
+ * partitions were discovered.
+ * @partition_count: The number of discovered partitions.
+ */
+
+struct imx_nfc_data {
+
+ /* System Interface */
+ struct device *dev;
+ struct platform_device *pdev;
+
+ /* Platform Configuration */
+ struct imx_nfc_platform_data *pdata;
+ void *buffers;
+ void *primary_regs;
+ void *secondary_regs;
+ struct clk *clock;
+ unsigned int interrupt;
+
+ /* Flash Hardware */
+ struct physical_geometry physical_geometry;
+
+ /* NFC HAL and NFC Utilities */
+ struct nfc_hal *nfc;
+ struct nfc_geometry *nfc_geometry;
+ struct completion done;
+
+ /* Medium Abstraction Layer */
+ struct logical_geometry logical_geometry;
+ enum override interrupt_override;
+ enum override auto_op_override;
+ int inject_ecc_error;
+
+ /* MTD Interface Layer */
+ int current_chip;
+ unsigned int command;
+ int command_is_new;
+ int page_address;
+
+ /* NAND Flash MTD */
+ struct nand_chip nand;
+
+ /* MTD */
+ struct mtd_info mtd;
+ struct mtd_partition *partitions;
+ unsigned int partition_count;
+
+};
+
+/**
+ * struct nfc_hal - i.MX NFC HAL
+ *
+ * This structure embodies an abstract interface to the underlying NFC hardware.
+ *
+ * @major_version: The major version number of the NFC to which this
+ * structure applies.
+ * @minor_version: The minor version number of the NFC to which this
+ * structure applies.
+ * @max_chip_count: The maximum number of chips the NFC can possibly
+ * support. This may *not* be the actual number of chips
+ * currently connected. This value is constant for NFC's
+ * of a given version.
+ * @max_buffer_count: The number of main/spare buffers available in the NFC's
+ * memory. This value is constant for NFC's of a given
+ * version.
+ * @spare_buf_stride: The stride, in bytes, from the beginning of one spare
+ * buffer to the beginning of the next one. This value is
+ * constant for NFC's of a given version.
+ * @has_secondary_regs: Indicates if the NFC has a secondary register set that
+ * must be mapped in.
+ * @can_be_symmetric: Indicates if the NFC supports a "symmetric" clock. When
+ * the clock is "symmetric," the hardware waits one NFC
+ * clock for every read/write cycle. When the clock is
+ * "asymmetric," the hardware waits two NFC clocks for
+ * every read/write cycle.
+ * @init: Initializes the NFC and any version-specific data
+ * structures. This function will be called after
+ * everything has been set up for communication with the
+ * NFC itself, but before the platform has set up off-chip
+ * communication. Thus, this function must not attempt to
+ * communicate with the NAND Flash hardware. A non-zero
+ * return value indicates failure.
+ * @set_geometry: Based on the physical geometry, this function selects
+ * an NFC geometry structure and configures the NFC
+ * hardware to match. A non-zero return value indicates
+ * failure.
+ * @exit: Shuts down the NFC and cleans up version-specific data
+ * structures. This function will be called after the
+ * platform has shut down off-chip communication but while
+ * communication with the NFC still works.
+ * @set_closest_cycle: Configures the hardware to make the NAND Flash bus
+ * cycle period as close as possible to the given cycle
+ * period. This function is called during boot up and may
+ * assume that, at the time it's called, the parent clock
+ * is running at the highest rate it will ever run. Thus,
+ * this function need never worry that the NAND Flash bus
+ * will run faster and potentially make it impossible to
+ * communicate with the NAND Flash device -- it will only
+ * run slower.
+ * @mask_interrupt: Masks the NFC's interrupt.
+ * @unmask_interrupt: Unmasks the NFC's interrupt.
+ * @clear_interrupt: Clears the NFC's interrupt.
+ * @is_interrupting: Returns true if the NFC is interrupting.
+ * @is_ready: Returns true if all the chips in the medium are ready.
+ * This member may be set to NULL, which indicates that
+ * the underlying NFC hardware doesn't expose ready/busy
+ * signals.
+ * @set_force_ce: If passed true, forces the hardware chip enable signal
+ * for the current chip to be asserted always. If passed
+ * false, causes the chip enable signal to be asserted
+ * only during I/O.
+ * @set_ecc: Sets ECC on or off.
+ * @get_ecc_status: Examines the hardware ECC status and returns:
+ * == 0 => No errors.
+ * > 0 => The number of corrected errors.
+ * < 0 => There were uncorrectable errors.
+ * @get_symmetric: Gets the current symmetric clock setting. For versions
+ * that don't support symmetric clocks, this function
+ * always returns false.
+ * @set_symmetric: For versions that support symmetric clocks, sets
+ * whether or not the clock is symmetric.
+ * @select_chip: Selects the current chip.
+ * @command_cycle: Sends a command code and then returns immediately
+ * *without* waiting for the NFC to finish.
+ * @write_cycle: Applies a single write cycle to the current chip,
+ * sending the given byte, and waiting for the NFC to
+ * finish.
+ * @read_cycle: Applies a single read cycle to the current chip and
+ * returns the result, necessarily waiting for the NFC to
+ * finish. The width of the result is the same as the
+ * width of the Flash bus.
+ * @read_page: Applies read cycles to the current chip to read an
+ * entire page into the NFC. Note that ECC is enabled or
+ * disabled with the set_ecc function pointer (see above).
+ * This function waits for the NFC to finish before
+ * returning.
+ * @send_page: Applies write cycles to send an entire page from the
+ * NFC to the current chip. Note that ECC is enabled or
+ * disabled with the set_ecc function pointer (see above).
+ * This function waits for the NFC to finish before
+ * returning.
+ * @start_auto_read: Starts an automatic read operation. A NULL pointer
+ * indicates automatic read operations aren't available
+ * with this NFC version.
+ * @wait_for_auto_read: Blocks until an automatic read operation is ready for
+ * the CPU to copy a page out of the NFC.
+ * @resume_auto_read: Resumes an automatic read operation after the CPU has
+ * copied a page out.
+ * @start_auto_write: Starts an automatic write operation. A NULL pointer
+ * indicates automatic write operations aren't available
+ * with this NFC version.
+ * @wait_for_auto_write: Blocks until an automatic write operation is ready for
+ * the CPU to copy a page into the NFC.
+ * @start_auto_erase: Starts an automatic erase operation. A NULL pointer
+ * indicates automatic erase operations aren't available
+ * with this NFC version.
+ */
+
+struct nfc_hal {
+ const unsigned int major_version;
+ const unsigned int minor_version;
+ const unsigned int max_chip_count;
+ const unsigned int max_buffer_count;
+ const unsigned int spare_buf_stride;
+ const int has_secondary_regs;
+ const int can_be_symmetric;
+ int (*init) (struct imx_nfc_data *);
+ int (*set_geometry) (struct imx_nfc_data *);
+ void (*exit) (struct imx_nfc_data *);
+ int (*set_closest_cycle) (struct imx_nfc_data *, unsigned ns);
+ void (*mask_interrupt) (struct imx_nfc_data *);
+ void (*unmask_interrupt) (struct imx_nfc_data *);
+ void (*clear_interrupt) (struct imx_nfc_data *);
+ int (*is_interrupting) (struct imx_nfc_data *);
+ int (*is_ready) (struct imx_nfc_data *);
+ void (*set_force_ce) (struct imx_nfc_data *, int on);
+ void (*set_ecc) (struct imx_nfc_data *, int on);
+ int (*get_ecc_status) (struct imx_nfc_data *);
+ int (*get_symmetric) (struct imx_nfc_data *);
+ void (*set_symmetric) (struct imx_nfc_data *, int on);
+ void (*select_chip) (struct imx_nfc_data *, int chip);
+ void (*command_cycle) (struct imx_nfc_data *, unsigned cmd);
+ void (*write_cycle) (struct imx_nfc_data *, unsigned byte);
+ unsigned (*read_cycle) (struct imx_nfc_data *);
+ void (*read_page) (struct imx_nfc_data *);
+ void (*send_page) (struct imx_nfc_data *);
+ int (*start_auto_read) (struct imx_nfc_data *,
+ unsigned start, unsigned count, unsigned column, unsigned page);
+ int (*wait_for_auto_read) (struct imx_nfc_data *);
+ int (*resume_auto_read) (struct imx_nfc_data *);
+ int (*start_auto_write) (struct imx_nfc_data *,
+ unsigned start, unsigned count, unsigned column, unsigned page);
+ int (*wait_for_auto_write)(struct imx_nfc_data *);
+ int (*start_auto_erase) (struct imx_nfc_data *,
+ unsigned start, unsigned count, unsigned page);
+};
+
+/*
+ * This variable controls whether or not probing is enabled. If false, then
+ * the driver will refuse to probe. The "enable" module parameter controls the
+ * value of this variable.
+ */
+
+static int imx_nfc_module_enable = true;
+
+#ifdef EVENT_REPORTING
+
+/*
+ * This variable controls whether the driver reports event information by
+ * printing to the console. The "report_events" module parameter controls the
+ * value of this variable.
+ */
+
+static int imx_nfc_module_report_events; /* implicitly initialized false*/
+
+#endif
+
+/*
+ * This variable potentially overrides the driver's choice to interleave. The
+ * "interleave_override" module parameter controls the value of this variable.
+ */
+
+static enum override imx_nfc_module_interleave_override = DRIVER_CHOICE;
+
+/*
+ * When set, this variable forces the driver to use the bytewise copy functions
+ * to get data in and out of the NFC. This is intended for testing, not typical
+ * use.
+ */
+
+static int imx_nfc_module_force_bytewise_copy; /* implicitly initialized false*/
+
+/*
+ * The list of algorithms we use to get partition information from the
+ * environment.
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *partition_source_types[] = { "cmdlinepart", NULL };
+#endif
+
+/*
+ * The following structures describe the NFC geometries we support.
+ *
+ * Notice that pieces of some structure definitions are commented out and edited
+ * because various parts of the MTD system can't handle the way our hardware
+ * formats the out-of-band area.
+ *
+ * Here are the problems:
+ *
+ * - struct nand_ecclayout expects no more than 64 ECC bytes.
+ *
+ * The eccpos member of struct nand_ecclayout can't hold more than 64 ECC
+ * byte positions. Some of our formats have more so, unedited, they won't
+ * even compile. We comment out all ECC byte positions after the 64th one.
+ *
+ * - struct nand_ecclayout expects no more than 8 free spans.
+ *
+ * The oobfree member of struct nand_ecclayout can't hold more than 8 free
+ * spans. Some of our formats have more so, unedited, they won't even
+ * compile. We comment out all free spans after the eighth one.
+ *
+ * - The MEMGETOOBSEL command in the mtdchar driver.
+ *
+ * The mtdchar ioctl command MEMGETOOBSEL checks the number of ECC bytes
+ * against the length of the eccpos array in struct nand_oobinfo
+ * (see include/mtd/mtd-abi.h). This array can handle up to 32 ECC bytes,
+ * but some of our formats have more.
+ *
+ * To make this work, we cap the value assigned to eccbytes at 32.
+ *
+ * Notice that struct nand_oobinfo, used by mtdchar, is *different* from the
+ * struct nand_ecclayout that MTD uses internally. The latter structure
+ * can accomodate up to 64 ECC byte positions. Thus, we declare up to 64
+ * ECC byte positions here, even if the advertised number of ECC bytes is
+ * less.
+ *
+ * This command is obsolete and, if no one used it, we wouldn't care about
+ * this problem. Unfortunately The nandwrite program uses it, and everyone
+ * expects nandwrite to work (it's how everyone usually lays down their
+ * JFFS2 file systems).
+ */
+
+static struct nfc_geometry nfc_geometry_512_16_RS_ECC1 = {
+ .page_data_size = 512,
+ .page_oob_size = 16,
+ .ecc_algorithm = "Reed-Solomon",
+ .ecc_strength = 1,
+ .buffer_count = 1,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 5,
+ .eccpos = {6, 7, 8, 9, 10},
+ .oobavail = 11,
+ .oobfree = { {0, 6}, {11, 5} },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_512_16_RS_ECC4 = {
+ .page_data_size = 512,
+ .page_oob_size = 16,
+ .ecc_algorithm = "Reed-Solomon",
+ .ecc_strength = 4,
+ .buffer_count = 1,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 9,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 7,
+ .oobfree = { {0, 7} },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_512_16_BCH_ECC4 = {
+ .page_data_size = 512,
+ .page_oob_size = 16,
+ .ecc_algorithm = "BCH",
+ .ecc_strength = 4,
+ .buffer_count = 1,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 8,
+ .eccpos = {8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 8,
+ .oobfree = { {0, 8} },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_2K_64_RS_ECC4 = {
+ .page_data_size = 2048,
+ .page_oob_size = 64,
+ .ecc_algorithm = "Reed-Solomon",
+ .ecc_strength = 4,
+ .buffer_count = 4,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 32 /*9 * 4*/, /* See notes above. */
+ .eccpos = {
+
+ (0*16)+7 , (0*16)+8 , (0*16)+9 , (0*16)+10, (0*16)+11,
+ (0*16)+12, (0*16)+13, (0*16)+14, (0*16)+15,
+
+ (1*16)+7 , (1*16)+8 , (1*16)+9 , (1*16)+10, (1*16)+11,
+ (1*16)+12, (1*16)+13, (1*16)+14, (1*16)+15,
+
+ (2*16)+7 , (2*16)+8 , (2*16)+9 , (2*16)+10, (2*16)+11,
+ (2*16)+12, (2*16)+13, (2*16)+14, (2*16)+15,
+
+ (3*16)+7 , (3*16)+8 , (3*16)+9 , (3*16)+10, (3*16)+11,
+ (3*16)+12, (3*16)+13, (3*16)+14, (3*16)+15,
+
+ },
+ .oobavail = 7 * 4,
+ .oobfree = {
+ {(0*16)+0, 7},
+ {(1*16)+0, 7},
+ {(2*16)+0, 7},
+ {(3*16)+0, 7},
+ },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_2K_64_BCH_ECC4 = {
+ .page_data_size = 2048,
+ .page_oob_size = 64,
+ .ecc_algorithm = "BCH",
+ .ecc_strength = 4,
+ .buffer_count = 4,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 8 * 4,
+ .eccpos = {
+
+ (0*16)+8 , (0*16)+9 , (0*16)+10, (0*16)+11,
+ (0*16)+12, (0*16)+13, (0*16)+14, (0*16)+15,
+
+ (1*16)+8 , (1*16)+9 , (1*16)+10, (1*16)+11,
+ (1*16)+12, (1*16)+13, (1*16)+14, (1*16)+15,
+
+ (2*16)+8 , (2*16)+9 , (2*16)+10, (2*16)+11,
+ (2*16)+12, (2*16)+13, (2*16)+14, (2*16)+15,
+
+ (3*16)+8 , (3*16)+9 , (3*16)+10, (3*16)+11,
+ (3*16)+12, (3*16)+13, (3*16)+14, (3*16)+15,
+
+ },
+ .oobavail = 8 * 4,
+ .oobfree = {
+ {(0*16)+0, 8},
+ {(1*16)+0, 8},
+ {(2*16)+0, 8},
+ {(3*16)+0, 8},
+ },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_4K_128_BCH_ECC4 = {
+ .page_data_size = 4096,
+ .page_oob_size = 128,
+ .ecc_algorithm = "BCH",
+ .ecc_strength = 4,
+ .buffer_count = 8,
+ .spare_buf_size = 16,
+ .spare_buf_spill = 0,
+ .mtd_layout = {
+ .eccbytes = 8 * 8,
+ .eccpos = {
+
+ (0*16)+8 , (0*16)+9 , (0*16)+10, (0*16)+11,
+ (0*16)+12, (0*16)+13, (0*16)+14, (0*16)+15,
+
+ (1*16)+8 , (1*16)+9 , (1*16)+10, (1*16)+11,
+ (1*16)+12, (1*16)+13, (1*16)+14, (1*16)+15,
+
+ (2*16)+8 , (2*16)+9 , (2*16)+10, (2*16)+11,
+ (2*16)+12, (2*16)+13, (2*16)+14, (2*16)+15,
+
+ (3*16)+8 , (3*16)+9 , (3*16)+10, (3*16)+11,
+ (3*16)+12, (3*16)+13, (3*16)+14, (3*16)+15,
+
+ (4*16)+8 , (4*16)+9 , (4*16)+10, (4*16)+11,
+ (4*16)+12, (4*16)+13, (4*16)+14, (4*16)+15,
+
+ (5*16)+8 , (5*16)+9 , (5*16)+10, (5*16)+11,
+ (5*16)+12, (5*16)+13, (5*16)+14, (5*16)+15,
+
+ (6*16)+8 , (6*16)+9 , (6*16)+10, (6*16)+11,
+ (6*16)+12, (6*16)+13, (6*16)+14, (6*16)+15,
+
+ (7*16)+8 , (7*16)+9 , (7*16)+10, (7*16)+11,
+ (7*16)+12, (7*16)+13, (7*16)+14, (7*16)+15,
+
+ },
+ .oobavail = 8 * 8,
+ .oobfree = {
+ {(0*16)+0, 8},
+ {(1*16)+0, 8},
+ {(2*16)+0, 8},
+ {(3*16)+0, 8},
+ {(4*16)+0, 8},
+ {(5*16)+0, 8},
+ {(6*16)+0, 8},
+ {(7*16)+0, 8},
+ },
+ }
+};
+
+static struct nfc_geometry nfc_geometry_4K_218_BCH_ECC8 = {
+ .page_data_size = 4096,
+ .page_oob_size = 218,
+ .ecc_algorithm = "BCH",
+ .ecc_strength = 8,
+ .buffer_count = 8,
+ .spare_buf_size = 26,
+ .spare_buf_spill = 10,
+ .mtd_layout = {
+ .eccbytes = 32 /*10 * 8*/, /* See notes above. */
+ .eccpos = {
+
+ (0*26)+12, (0*26)+13, (0*26)+14, (0*26)+15, (0*26)+16,
+ (0*26)+17, (0*26)+18, (0*26)+19, (0*26)+20, (0*26)+21,
+
+ (1*26)+12, (1*26)+13, (1*26)+14, (1*26)+15, (1*26)+16,
+ (1*26)+17, (1*26)+18, (1*26)+19, (1*26)+20, (1*26)+21,
+
+ (2*26)+12, (2*26)+13, (2*26)+14, (2*26)+15, (2*26)+16,
+ (2*26)+17, (2*26)+18, (2*26)+19, (2*26)+20, (2*26)+21,
+
+ (3*26)+12, (3*26)+13, (3*26)+14, (3*26)+15, (3*26)+16,
+ (3*26)+17, (3*26)+18, (3*26)+19, (3*26)+20, (3*26)+21,
+
+ (4*26)+12, (4*26)+13, (4*26)+14, (4*26)+15, (4*26)+16,
+ (4*26)+17, (4*26)+18, (4*26)+19, (4*26)+20, (4*26)+21,
+
+ (5*26)+12, (5*26)+13, (5*26)+14, (5*26)+15, (5*26)+16,
+ (5*26)+17, (5*26)+18, (5*26)+19, (5*26)+20, (5*26)+21,
+
+ (6*26)+12, (6*26)+13, (6*26)+14, (6*26)+15,
+ /* See notes above.
+ (6*26)+16,
+ (6*26)+17, (6*26)+18, (6*26)+19, (6*26)+20, (6*26)+21,
+
+ (7*26)+12, (7*26)+13, (7*26)+14, (7*26)+15, (7*26)+16,
+ (7*26)+17, (7*26)+18, (7*26)+19, (7*26)+20, (7*26)+21,
+ */
+
+ },
+ .oobavail = 96 /*(16 * 8) + 10*/, /* See notes above. */
+ .oobfree = {
+ {(0*26)+0, 12}, {(0*26)+22, 4},
+ {(1*26)+0, 12}, {(1*26)+22, 4},
+ {(2*26)+0, 12}, {(2*26)+22, 4},
+ {(3*26)+0, 48}, /* See notes above. */
+ /* See notes above.
+ {(3*26)+0, 12}, {(3*26)+22, 4},
+ {(4*26)+0, 12}, {(4*26)+22, 4},
+ {(5*26)+0, 12}, {(5*26)+22, 4},
+ {(6*26)+0, 12}, {(6*26)+22, 4},
+ {(7*26)+0, 12}, {(7*26)+22, 4 + 10},
+ */
+ },
+ }
+};
+
+/*
+ * When the logical geometry differs from the NFC geometry (e.g.,
+ * interleaving), we synthesize a layout rather than use the one that comes with
+ * the NFC geometry. See mal_set_logical_geometry().
+ */
+
+static struct nand_ecclayout synthetic_layout;
+
+/*
+ * These structures describe how the BBT code will find block marks in the OOB
+ * area of a page. Don't be confused by the fact that this is the same type used
+ * to describe bad block tables. Some of the same information is needed, so the
+ * designers use the same structure for two conceptually distinct functions.
+ */
+
+static uint8_t block_mark_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr small_page_block_mark_descriptor = {
+ .options = NAND_BBT_SCAN2NDPAGE,
+ .offs = 5,
+ .len = 1,
+ .pattern = block_mark_pattern,
+};
+
+static struct nand_bbt_descr large_page_block_mark_descriptor = {
+ .options = NAND_BBT_SCAN2NDPAGE,
+ .offs = 0,
+ .len = 2,
+ .pattern = block_mark_pattern,
+};
+
+/*
+ * Bad block table descriptors for the main and mirror tables.
+ */
+
+static uint8_t bbt_main_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t bbt_mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descriptor = {
+ .options =
+ NAND_BBT_LASTBLOCK |
+ NAND_BBT_CREATE |
+ NAND_BBT_WRITE |
+ NAND_BBT_2BIT |
+ NAND_BBT_VERSION |
+ NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_main_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descriptor = {
+ .options =
+ NAND_BBT_LASTBLOCK |
+ NAND_BBT_CREATE |
+ NAND_BBT_WRITE |
+ NAND_BBT_2BIT |
+ NAND_BBT_VERSION |
+ NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_mirror_pattern,
+};
+
+#ifdef EVENT_REPORTING
+
+/**
+ * struct event - A single record in the event trace.
+ *
+ * @time: The time at which the event occurred.
+ * @nesting: Indicates function call nesting.
+ * @description: A description of the event.
+ */
+
+struct event {
+ ktime_t time;
+ unsigned int nesting;
+ char *description;
+};
+
+/**
+ * The event trace.
+ *
+ * @overhead: The delay to take a time stamp and nothing else.
+ * @nesting: The current nesting level.
+ * @overflow: Indicates the trace overflowed.
+ * @next: Index of the next event to write.
+ * @events: The array of events.
+ */
+
+#define MAX_EVENT_COUNT (200)
+
+static struct {
+ ktime_t overhead;
+ int nesting;
+ int overflow;
+ unsigned int next;
+ struct event events[MAX_EVENT_COUNT];
+} event_trace;
+
+/**
+ * reset_event_trace() - Resets the event trace.
+ */
+static void reset_event_trace(void)
+{
+ event_trace.nesting = 0;
+ event_trace.overflow = false;
+ event_trace.next = 0;
+}
+
+/**
+ * add_event() - Adds an event to the event trace.
+ *
+ * @description: A description of the event.
+ * @delta: A delta to the nesting level for this event [-1, 0, 1].
+ */
+static inline void add_event(char *description, int delta)
+{
+ struct event *event;
+
+ if (!imx_nfc_module_report_events)
+ return;
+
+ if (event_trace.overflow)
+ return;
+
+ if (event_trace.next >= MAX_EVENT_COUNT) {
+ event_trace.overflow = true;
+ return;
+ }
+
+ event = event_trace.events + event_trace.next;
+
+ event->time = ktime_get();
+
+ event->description = description;
+
+ if (!delta)
+ event->nesting = event_trace.nesting;
+ else if (delta < 0) {
+ event->nesting = event_trace.nesting - 1;
+ event_trace.nesting -= 2;
+ } else {
+ event->nesting = event_trace.nesting + 1;
+ event_trace.nesting += 2;
+ }
+
+ if (event_trace.nesting < 0)
+ event_trace.nesting = 0;
+
+ event_trace.next++;
+
+}
+
+/**
+ * add_state_event_l() - Adds an event to display some state.
+ *
+ * @address: The address to examine.
+ * @mask: A mask to apply to the contents of the given address.
+ * @clear: An event message to add if the result is zero.
+ * @not_zero: An event message to add if the result is not zero.
+ */
+static void add_state_event_l(void *address, uint32_t mask,
+ char *zero, char *not_zero)
+{
+ int state;
+ state = !!(__raw_readl(address) & mask);
+ if (state)
+ add_event(not_zero, 0);
+ else
+ add_event(zero, 0);
+}
+
+/**
+ * start_event_trace() - Starts an event trace and adds the first event.
+ *
+ * @description: A description of the first event.
+ */
+static void start_event_trace(char *description)
+{
+
+ ktime_t t0;
+ ktime_t t1;
+
+ if (!imx_nfc_module_report_events)
+ return;
+
+ reset_event_trace();
+
+ t0 = ktime_get();
+ t1 = ktime_get();
+
+ event_trace.overhead = ktime_sub(t1, t0);
+
+ add_event(description, 1);
+
+}
+
+/**
+ * dump_event_trace() - Dumps the event trace.
+ */
+static void dump_event_trace(void)
+{
+ unsigned int i;
+ time_t seconds;
+ long nanoseconds;
+ char line[100];
+ int o;
+ struct event *first_event;
+ struct event *last_event;
+ struct event *matching_event;
+ struct event *event;
+ ktime_t delta;
+
+ /* Check if event reporting is turned off. */
+
+ if (!imx_nfc_module_report_events)
+ return;
+
+ /* Print important facts about this event trace. */
+
+ printk(KERN_DEBUG "\n+--------------\n");
+
+ printk(KERN_DEBUG "| Overhead : [%d:%d]\n",
+ event_trace.overhead.tv.sec,
+ event_trace.overhead.tv.nsec);
+
+ if (!event_trace.next) {
+ printk(KERN_DEBUG "| No Events\n");
+ return;
+ }
+
+ first_event = event_trace.events;
+ last_event = event_trace.events + (event_trace.next - 1);
+
+ delta = ktime_sub(last_event->time, first_event->time);
+ printk(KERN_DEBUG "| Elapsed Time: [%d:%d]\n",
+ delta.tv.sec, delta.tv.nsec);
+
+ if (event_trace.overflow)
+ printk(KERN_DEBUG "| Overflow!\n");
+
+ /* Print the events in this history. */
+
+ for (i = 0, event = event_trace.events;
+ i < event_trace.next; i++, event++) {
+
+ /* Get the delta between this event and the previous event. */
+
+ if (!i) {
+ seconds = 0;
+ nanoseconds = 0;
+ } else {
+ delta = ktime_sub(event[0].time, event[-1].time);
+ seconds = delta.tv.sec;
+ nanoseconds = delta.tv.nsec;
+ }
+
+ /* Print the current event. */
+
+ o = 0;
+
+ o = snprintf(line, sizeof(line) - o, "| [%ld:% 10ld]%*s %s",
+ seconds, nanoseconds,
+ event->nesting, "",
+ event->description);
+ /* Check if this is the last event in a nested series. */
+
+ if (i && (event[0].nesting < event[-1].nesting)) {
+
+ for (matching_event = event - 1;; matching_event--) {
+
+ if (matching_event < event_trace.events) {
+ matching_event = 0;
+ break;
+ }
+
+ if (matching_event->nesting == event->nesting)
+ break;
+
+ }
+
+ if (matching_event) {
+ delta = ktime_sub(event->time,
+ matching_event->time);
+ o += snprintf(line + o, sizeof(line) - o,
+ " <%d:%d]", delta.tv.sec,
+ delta.tv.nsec);
+ }
+
+ }
+
+ /* Check if this is the first event in a nested series. */
+
+ if ((i < event_trace.next - 1) &&
+ (event[0].nesting < event[1].nesting)) {
+
+ for (matching_event = event + 1;; matching_event++) {
+
+ if (matching_event >=
+ (event_trace.events+event_trace.next)) {
+ matching_event = 0;
+ break;
+ }
+
+ if (matching_event->nesting == event->nesting)
+ break;
+
+ }
+
+ if (matching_event) {
+ delta = ktime_sub(matching_event->time,
+ event->time);
+ o += snprintf(line + o, sizeof(line) - o,
+ " [%d:%d>", delta.tv.sec,
+ delta.tv.nsec);
+ }
+
+ }
+
+ printk(KERN_DEBUG "%s\n", line);
+
+ }
+
+ printk(KERN_DEBUG "+--------------\n");
+
+}
+
+/**
+ * stop_event_trace() - Stops an event trace.
+ *
+ * @description: A description of the last event.
+ */
+static void stop_event_trace(char *description)
+{
+ struct event *event;
+
+ if (!imx_nfc_module_report_events)
+ return;
+
+ /*
+ * We want the end of the trace, no matter what happens. If the trace
+ * has already overflowed, or is about to, just jam this event into the
+ * last spot. Otherwise, add this event like any other.
+ */
+
+ if (event_trace.overflow || (event_trace.next >= MAX_EVENT_COUNT)) {
+ event = event_trace.events + (MAX_EVENT_COUNT - 1);
+ event->time = ktime_get();
+ event->description = description;
+ event->nesting = 0;
+ } else {
+ add_event(description, -1);
+ }
+
+ dump_event_trace();
+ reset_event_trace();
+
+}
+
+#else /* EVENT_REPORTING */
+
+#define start_event_trace(description) do {} while (0)
+#define add_event(description, delta) do {} while (0)
+#define add_state_event_l(address, mask, zero, not_zero) do {} while (0)
+#define stop_event_trace(description) do {} while (0)
+#define dump_event_trace() do {} while (0)
+
+#endif /* EVENT_REPORTING */
+
+/**
+ * unimplemented() - Announces intentionally unimplemented features.
+ *
+ * @this: Per-device data.
+ * @msg: A message about the unimplemented feature.
+ */
+static inline void unimplemented(struct imx_nfc_data *this, const char * msg)
+{
+ dev_err(this->dev, "Intentionally unimplemented: %s", msg);
+}
+
+/**
+ * raw_read_mask_w() - Reads masked bits in a 16-bit hardware register.
+ */
+static inline uint16_t raw_read_mask_w(uint16_t mask, void *address)
+{
+ return __raw_readw(address) & mask;
+}
+
+/**
+ * raw_set_mask_w() - Sets bits in a 16-bit hardware register.
+ */
+static inline void raw_set_mask_w(uint16_t mask, void *address)
+{
+ __raw_writew(__raw_readw(address) | mask, address);
+}
+
+/**
+ * raw_clr_mask_w() - Clears bits in a 16-bit hardware register.
+ */
+static inline void raw_clr_mask_w(uint16_t mask, void *address)
+{
+ __raw_writew(__raw_readw(address) & (~mask), address);
+}
+
+/**
+ * raw_read_mask_l() - Reads masked bits in a 32-bit hardware register.
+ */
+static inline uint32_t raw_read_mask_l(uint32_t mask, void *address)
+{
+ return __raw_readl(address) & mask;
+}
+
+/**
+ * raw_set_mask_l() - Sets bits in a 32-bit hardware register.
+ */
+static inline void raw_set_mask_l(uint32_t mask, void *address)
+{
+ __raw_writel(__raw_readl(address) | mask, address);
+}
+
+/**
+ * raw_clr_mask_l() - Clears bits in a 32-bit hardware register.
+ */
+static inline void raw_clr_mask_l(uint32_t mask, void *address)
+{
+ __raw_writel(__raw_readl(address) & (~mask), address);
+}
+
+/**
+ * is_large_page_chip() - Returns true for large page media.
+ *
+ * @this: Per-device data.
+ */
+static inline int is_large_page_chip(struct imx_nfc_data *this)
+{
+ return (this->physical_geometry.page_data_size > 512);
+}
+
+/**
+ * is_small_page_chip() - Returns true for small page media.
+ *
+ * @this: Per-device data.
+ */
+static inline int is_small_page_chip(struct imx_nfc_data *this)
+{
+ return !is_large_page_chip(this);
+}
+
+/**
+ * get_cycle_in_ns() - Returns the given device's cycle period, in ns.
+ *
+ * @this: Per-device data.
+ */
+static inline unsigned get_cycle_in_ns(struct imx_nfc_data *this)
+{
+ unsigned long cycle_in_ns;
+
+ cycle_in_ns = 1000000000 / clk_get_rate(this->clock);
+
+ if (!this->nfc->get_symmetric(this))
+ cycle_in_ns *= 2;
+
+ return cycle_in_ns;
+
+}
+
+/**
+ * nfc_util_set_best_cycle() - Sets the closest possible NAND Flash bus cycle.
+ *
+ * This function computes the clock setup that will best approximate the given
+ * target Flash bus cycle period.
+ *
+ * For some NFC versions, we can make the clock "symmetric." When the clock
+ * is "symmetric," the hardware waits one NFC clock for every read/write cycle.
+ * When the clock is "asymmetric," the hardware waits two NFC clocks for every
+ * read/write cycle. Thus, making the clock asymmetric essentially divides the
+ * NFC clock by two.
+ *
+ * We compute the target frequency that matches the given target period. We then
+ * discover the closest available match with that frequency and the closest
+ * available match with double that frequency (for use with an asymmetric
+ * clock). We implement the best choice of original clock and symmetric or
+ * asymmetric setting, preferring symmetric clocks.
+ *
+ * @this: Per-device data.
+ * @ns: The target cycle period, in nanoseconds.
+ * @no_asym: Disallow making the clock asymmetric.
+ * @no_sym: Disallow making the clock symmetric.
+ */
+static int nfc_util_set_best_cycle(struct imx_nfc_data *this,
+ unsigned int ns, int no_asym, int no_sym)
+{
+ unsigned long target_hz;
+ long symmetric_hz;
+ long symmetric_delta_hz;
+ long asymmetric_hz;
+ long asymmetric_delta_hz;
+ unsigned long best_hz;
+ int best_symmetry_setting;
+ struct device *dev = this->dev;
+
+ /* The target cycle period must be greater than zero. */
+
+ if (!ns)
+ return -EINVAL;
+
+ /* Compute the target frequency. */
+
+ target_hz = 1000000000 / ns;
+
+ /* Find out how close we can get with a symmetric clock. */
+
+ if (!no_sym && this->nfc->can_be_symmetric)
+ symmetric_hz = clk_round_rate(this->clock, target_hz);
+ else
+ symmetric_hz = -EINVAL;
+
+ /* Find out how close we can get with an asymmetric clock. */
+
+ if (!no_asym)
+ asymmetric_hz = clk_round_rate(this->clock, target_hz * 2);
+ else
+ asymmetric_hz = -EINVAL;
+
+ /* Does anything work at all? */
+
+ if ((symmetric_hz == -EINVAL) && (asymmetric_hz == -EINVAL)) {
+ dev_err(dev, "Can't support Flash bus cycle of %uns\n", ns);
+ return -EINVAL;
+ }
+
+ /* Discover the best match. */
+
+ if ((symmetric_hz != -EINVAL) && (asymmetric_hz != -EINVAL)) {
+
+ symmetric_delta_hz = target_hz - symmetric_hz;
+ asymmetric_delta_hz = target_hz - (asymmetric_hz / 2);
+
+ if (symmetric_delta_hz <= asymmetric_delta_hz)
+ best_symmetry_setting = true;
+ else
+ best_symmetry_setting = false;
+
+ } else if (symmetric_hz != -EINVAL) {
+ best_symmetry_setting = true;
+ } else {
+ best_symmetry_setting = false;
+ }
+
+ best_hz = best_symmetry_setting ? symmetric_hz : asymmetric_hz;
+
+ /* Implement the best match. */
+
+ this->nfc->set_symmetric(this, best_symmetry_setting);
+
+ return clk_set_rate(this->clock, best_hz);
+
+}
+
+/**
+ * nfc_util_wait_for_the_nfc() - Waits for the NFC to finish an operation.
+ *
+ * @this: Per-device data.
+ * @use_irq: Indicates that we should wait for an interrupt rather than polling
+ * and delaying.
+ */
+static void nfc_util_wait_for_the_nfc(struct imx_nfc_data *this, int use_irq)
+{
+ unsigned spin_count;
+ struct device *dev = this->dev;
+
+ /* Apply the override, if any. */
+
+ switch (this->interrupt_override) {
+
+ case NEVER:
+ use_irq = false;
+ break;
+
+ case DRIVER_CHOICE:
+ break;
+
+ case ALWAYS:
+ use_irq = true;
+ break;
+
+ }
+
+ /* Check if we're using interrupts. */
+
+ if (use_irq) {
+
+ /*
+ * If control arrives here, the caller wants to use interrupts.
+ * Presumably, this operation is known to take a very long time.
+ */
+
+ if (this->nfc->is_interrupting(this)) {
+ add_event("Waiting for the NFC (early interrupt)", 1);
+ this->nfc->clear_interrupt(this);
+ } else {
+ add_event("Waiting for the NFC (interrupt)", 1);
+ this->nfc->unmask_interrupt(this);
+ wait_for_completion(&this->done);
+ }
+
+ add_event("NFC done", -1);
+
+ } else {
+
+ /*
+ * If control arrives here, the caller doesn't want to use
+ * interrupts. Presumably, this operation is too quick to
+ * justify the overhead. Leave the interrupt masked, and loop
+ * until the interrupt bit lights up, or we time out.
+ *
+ * We spin for a maximum of about 2ms before declaring a time
+ * out. No operation we could possibly spin on should take that
+ * long.
+ */
+
+ spin_count = 2000;
+
+ add_event("Waiting for the NFC (polling)", 1);
+
+ for (; spin_count > 0; spin_count--) {
+
+ if (this->nfc->is_interrupting(this)) {
+ this->nfc->clear_interrupt(this);
+ add_event("NFC done", -1);
+ return;
+ }
+
+ udelay(1);
+
+ }
+
+ /* Timed out. */
+
+ add_event("Timed out", -1);
+
+ dev_err(dev, "[wait_for_the_nfc] ===== Time Out =====\n");
+ dump_event_trace();
+
+ }
+
+}
+
+/**
+ * nfc_util_bytewise_copy_from_nfc_mem() - Copies bytes from the NFC memory.
+ *
+ * @from: A pointer to the source memory.
+ * @to: A pointer to the destination memory.
+ * @size: The number of bytes to copy.
+ */
+static void nfc_util_bytewise_copy_from_nfc_mem(const void *from,
+ void *to, size_t n)
+{
+ unsigned int i;
+ const uint8_t *f = from;
+ uint8_t *t = to;
+ uint16_t *p;
+ uint16_t x;
+
+ for (i = 0; i < n; i++, f++, t++) {
+
+ p = (uint16_t *) (((unsigned long) f) & ~((unsigned long) 1));
+
+ x = __raw_readw(p);
+
+ if (((unsigned long) f) & 0x1)
+ *t = (x >> 8) & 0xff;
+ else
+ *t = (x >> 0) & 0xff;
+
+ }
+
+}
+
+/**
+ * nfc_util_bytewise_copy_to_nfc_mem() - Copies bytes to the NFC memory.
+ *
+ * @from: A pointer to the source memory.
+ * @to: A pointer to the destination memory.
+ * @size: The number of bytes to copy.
+ */
+static void nfc_util_bytewise_copy_to_nfc_mem(const void *from,
+ void *to, size_t n)
+{
+ unsigned int i;
+ const uint8_t *f = from;
+ uint8_t *t = to;
+ uint16_t *p;
+ uint16_t x;
+
+ for (i = 0; i < n; i++, f++, t++) {
+
+ p = (uint16_t *) (((unsigned long) t) & ~((unsigned long) 1));
+
+ x = __raw_readw(p);
+
+ if (((unsigned long) t) & 0x1)
+ ((uint8_t *)(&x))[1] = *f;
+ else
+ ((uint8_t *)(&x))[0] = *f;
+
+ __raw_writew(x, p);
+
+ }
+
+}
+
+/**
+ * nfc_util_copy_from_nfc_mem() - Copies from the NFC memory to main memory.
+ *
+ * @from: A pointer to the source memory.
+ * @to: A pointer to the destination memory.
+ * @size: The number of bytes to copy.
+ */
+static void nfc_util_copy_from_nfc_mem(const void *from, void *to, size_t n)
+{
+ unsigned int chunk_count;
+
+ /*
+ * Check if we're testing bytewise copies.
+ */
+
+ if (imx_nfc_module_force_bytewise_copy)
+ goto force_bytewise_copy;
+
+ /*
+ * We'd like to use memcpy to get data out of the NFC but, because that
+ * memory responds only to 16- and 32-byte reads, we can only do so
+ * safely if both the start and end of both the source and destination
+ * are perfectly aligned on 4-byte boundaries.
+ */
+
+ if (!(((unsigned long) from) & 0x3) && !(((unsigned long) to) & 0x3)) {
+
+ /*
+ * If control arrives here, both the source and destination are
+ * aligned on 4-byte boundaries. Compute the number of whole,
+ * 4-byte chunks we can move.
+ */
+
+ chunk_count = n / 4;
+
+ /*
+ * Move all the chunks we can, and then update the pointers and
+ * byte count to show what's left.
+ */
+
+ if (chunk_count) {
+ memcpy(to, from, chunk_count * 4);
+ from += chunk_count * 4;
+ to += chunk_count * 4;
+ n -= chunk_count * 4;
+ }
+
+ }
+
+ /*
+ * Move what's left.
+ */
+
+force_bytewise_copy:
+
+ nfc_util_bytewise_copy_from_nfc_mem(from, to, n);
+
+}
+
+/**
+ * nfc_util_copy_to_nfc_mem() - Copies from main memory to the NFC memory.
+ *
+ * @from: A pointer to the source memory.
+ * @to: A pointer to the destination memory.
+ * @size: The number of bytes to copy.
+ */
+static void nfc_util_copy_to_nfc_mem(const void *from, void *to, size_t n)
+{
+ unsigned int chunk_count;
+
+ /*
+ * Check if we're testing bytewise copies.
+ */
+
+ if (imx_nfc_module_force_bytewise_copy)
+ goto force_bytewise_copy;
+
+ /*
+ * We'd like to use memcpy to get data into the NFC but, because that
+ * memory responds only to 16- and 32-byte writes, we can only do so
+ * safely if both the start and end of both the source and destination
+ * are perfectly aligned on 4-byte boundaries.
+ */
+
+ if (!(((unsigned long) from) & 0x3) && !(((unsigned long) to) & 0x3)) {
+
+ /*
+ * If control arrives here, both the source and destination are
+ * aligned on 4-byte boundaries. Compute the number of whole,
+ * 4-byte chunks we can move.
+ */
+
+ chunk_count = n / 4;
+
+ /*
+ * Move all the chunks we can, and then update the pointers and
+ * byte count to show what's left.
+ */
+
+ if (chunk_count) {
+ memcpy(to, from, chunk_count * 4);
+ from += chunk_count * 4;
+ to += chunk_count * 4;
+ n -= chunk_count * 4;
+ }
+
+ }
+
+ /*
+ * Move what's left.
+ */
+
+force_bytewise_copy:
+
+ nfc_util_bytewise_copy_to_nfc_mem(from, to, n);
+
+}
+
+/**
+ * nfc_util_copy_from_the_nfc() - Copies bytes out of the NFC.
+ *
+ * This function makes the data in the NFC look like a contiguous, model page.
+ *
+ * @this: Per-device data.
+ * @start: The index of the starting byte in the NFC.
+ * @buf: A pointer to the destination buffer.
+ * @len: The number of bytes to copy out.
+ */
+static void nfc_util_copy_from_the_nfc(struct imx_nfc_data *this,
+ unsigned int start, uint8_t *buf, unsigned int len)
+{
+ unsigned int i;
+ unsigned int count;
+ unsigned int offset;
+ unsigned int data_size;
+ unsigned int oob_size;
+ unsigned int total_size;
+ void *spare_base;
+ unsigned int first_spare;
+ void *from;
+ struct nfc_geometry *geometry = this->nfc_geometry;
+
+ /*
+ * During initialization, the HIL will attempt to read ID bytes. For
+ * some NFC hardware versions, the ID bytes are deposited in the NFC
+ * memory, so this function will be called to deliver them. At that
+ * point, we won't know the NFC geometry. That's OK because we're only
+ * going to be reading a byte at a time.
+ *
+ * If we don't yet know the NFC geometry, just plug in some values that
+ * make things work for now.
+ */
+
+ if (unlikely(!geometry)) {
+ data_size = NFC_MAIN_BUF_SIZE;
+ oob_size = 0;
+ } else {
+ data_size = geometry->page_data_size;
+ oob_size = geometry->page_oob_size;
+ }
+
+ total_size = data_size + oob_size;
+
+ /* Validate. */
+
+ if ((start >= total_size) || ((start + len) > total_size)) {
+ dev_err(this->dev, "Bad copy from NFC memory: [%u, %u]\n",
+ start, len);
+ return;
+ }
+
+ /* Check if we're copying anything at all. */
+
+ if (!len)
+ return;
+
+ /* Check if anything comes from the main area. */
+
+ if (start < data_size) {
+
+ /* Compute the bytes to copy from the main area. */
+
+ count = min(len, data_size - start);
+
+ /* Copy. */
+
+ nfc_util_copy_from_nfc_mem(this->buffers + start, buf, count);
+
+ buf += count;
+ start += count;
+ len -= count;
+
+ }
+
+ /* Check if we're done. */
+
+ if (!len)
+ return;
+
+ /* Compute the base address of the spare buffers. */
+
+ spare_base = this->buffers +
+ (this->nfc->max_buffer_count * NFC_MAIN_BUF_SIZE);
+
+ /* Discover in which spare buffer the copying begins. */
+
+ first_spare = (start - data_size) / geometry->spare_buf_size;
+
+ /* Check if anything comes from the regular spare buffer area. */
+
+ if (first_spare < geometry->buffer_count) {
+
+ /* Start copying from spare buffers. */
+
+ for (i = first_spare; i < geometry->buffer_count; i++) {
+
+ /* Compute the offset into this spare area. */
+
+ offset = start -
+ (data_size + (geometry->spare_buf_size * i));
+
+ /* Compute the address of that offset. */
+
+ from = spare_base + offset +
+ (this->nfc->spare_buf_stride * i);
+
+ /* Compute the bytes to copy from this spare area. */
+
+ count = min(len, geometry->spare_buf_size - offset);
+
+ /* Copy. */
+
+ nfc_util_copy_from_nfc_mem(from, buf, count);
+
+ buf += count;
+ start += count;
+ len -= count;
+
+ }
+
+ }
+
+ /* Check if we're done. */
+
+ if (!len)
+ return;
+
+ /* Compute the offset into the extra spare area. */
+
+ offset = start -
+ (data_size + (geometry->spare_buf_size*geometry->buffer_count));
+
+ /* Compute the address of that offset. */
+
+ from = spare_base + offset +
+ (this->nfc->spare_buf_stride * geometry->buffer_count);
+
+ /* Compute the bytes to copy from the extra spare area. */
+
+ count = min(len, geometry->spare_buf_spill - offset);
+
+ /* Copy. */
+
+ nfc_util_copy_from_nfc_mem(from, buf, count);
+
+}
+
+/**
+ * nfc_util_copy_to_the_nfc() - Copies bytes into the NFC memory.
+ *
+ * This function makes the data in the NFC look like a contiguous, model page.
+ *
+ * @this: Per-device data.
+ * @buf: A pointer to the source buffer.
+ * @start: The index of the starting byte in the NFC memory.
+ * @len: The number of bytes to copy in.
+ */
+static void nfc_util_copy_to_the_nfc(struct imx_nfc_data *this,
+ const uint8_t *buf, unsigned int start, unsigned int len)
+{
+ unsigned int i;
+ unsigned int count;
+ unsigned int offset;
+ unsigned int data_size;
+ unsigned int oob_size;
+ unsigned int total_size;
+ void *spare_base;
+ unsigned int first_spare;
+ void *to;
+ struct nfc_geometry *geometry = this->nfc_geometry;
+
+ /* Establish some important facts. */
+
+ data_size = geometry->page_data_size;
+ oob_size = geometry->page_oob_size;
+ total_size = data_size + oob_size;
+
+ /* Validate. */
+
+ if ((start >= total_size) || ((start + len) > total_size)) {
+ dev_err(this->dev, "Bad copy to NFC memory: [%u, %u]\n",
+ start, len);
+ return;
+ }
+
+ /* Check if we're copying anything at all. */
+
+ if (!len)
+ return;
+
+ /* Check if anything goes to the main area. */
+
+ if (start < data_size) {
+
+ /* Compute the bytes to copy to the main area. */
+
+ count = min(len, data_size - start);
+
+ /* Copy. */
+
+ nfc_util_copy_to_nfc_mem(buf, this->buffers + start, count);
+
+ buf += count;
+ start += count;
+ len -= count;
+
+ }
+
+ /* Check if we're done. */
+
+ if (!len)
+ return;
+
+ /* Compute the base address of the spare buffers. */
+
+ spare_base = this->buffers +
+ (this->nfc->max_buffer_count * NFC_MAIN_BUF_SIZE);
+
+ /* Discover in which spare buffer the copying begins. */
+
+ first_spare = (start - data_size) / geometry->spare_buf_size;
+
+ /* Check if anything goes to the regular spare buffer area. */
+
+ if (first_spare < geometry->buffer_count) {
+
+ /* Start copying to spare buffers. */
+
+ for (i = first_spare; i < geometry->buffer_count; i++) {
+
+ /* Compute the offset into this spare area. */
+
+ offset = start -
+ (data_size + (geometry->spare_buf_size * i));
+
+ /* Compute the address of that offset. */
+
+ to = spare_base + offset +
+ (this->nfc->spare_buf_stride * i);
+
+ /* Compute the bytes to copy to this spare area. */
+
+ count = min(len, geometry->spare_buf_size - offset);
+
+ /* Copy. */
+
+ nfc_util_copy_to_nfc_mem(buf, to, count);
+
+ buf += count;
+ start += count;
+ len -= count;
+
+ }
+
+ }
+
+ /* Check if we're done. */
+
+ if (!len)
+ return;
+
+ /* Compute the offset into the extra spare area. */
+
+ offset = start -
+ (data_size + (geometry->spare_buf_size*geometry->buffer_count));
+
+ /* Compute the address of that offset. */
+
+ to = spare_base + offset +
+ (this->nfc->spare_buf_stride * geometry->buffer_count);
+
+ /* Compute the bytes to copy to the extra spare area. */
+
+ count = min(len, geometry->spare_buf_spill - offset);
+
+ /* Copy. */
+
+ nfc_util_copy_to_nfc_mem(buf, to, count);
+
+}
+
+/**
+ * nfc_util_isr() - i.MX NFC ISR.
+ *
+ * @irq: The arriving interrupt number.
+ * @context: A cookie for this ISR.
+ */
+static irqreturn_t nfc_util_isr(int irq, void *cookie)
+{
+ struct imx_nfc_data *this = cookie;
+ this->nfc->mask_interrupt(this);
+ this->nfc->clear_interrupt(this);
+ complete(&this->done);
+ return IRQ_HANDLED;
+}
+
+/**
+ * nfc_util_send_cmd() - Sends a command to the current chip, without waiting.
+ *
+ * @this: Per-device data.
+ * @command: The command code.
+ */
+
+static void nfc_util_send_cmd(struct imx_nfc_data *this, unsigned int command)
+{
+
+ add_event("Entering nfc_util_send_cmd", 1);
+
+ this->nfc->command_cycle(this, command);
+
+ add_event("Exiting nfc_util_send_cmd", -1);
+
+}
+
+/**
+ * nfc_util_send_cmd_and_addrs() - Sends a cmd and addrs to the current chip.
+ *
+ * This function conveniently combines sending a command, and then sending
+ * optional addresses, waiting for the NFC to finish will all steps.
+ *
+ * @this: Per-device data.
+ * @command: The command code.
+ * @column: The column address to send, or -1 if no column address applies.
+ * @page: The page address to send, or -1 if no page address applies.
+ */
+
+static void nfc_util_send_cmd_and_addrs(struct imx_nfc_data *this,
+ unsigned command, int column, int page)
+{
+ uint32_t page_mask;
+
+ add_event("Entering nfc_util_send_cmd_and_addrs", 1);
+
+ /* Send the command.*/
+
+ add_event("Sending the command...", 0);
+
+ nfc_util_send_cmd(this, command);
+
+ nfc_util_wait_for_the_nfc(this, false);
+
+ /* Send the addresses. */
+
+ add_event("Sending the addresses...", 0);
+
+ if (column != -1) {
+
+ this->nfc->write_cycle(this, (column >> 0) & 0xff);
+
+ if (is_large_page_chip(this))
+ this->nfc->write_cycle(this, (column >> 8) & 0xff);
+
+ }
+
+ if (page != -1) {
+
+ page_mask = this->nand.pagemask;
+
+ do {
+ this->nfc->write_cycle(this, page & 0xff);
+ page_mask >>= 8;
+ page >>= 8;
+ } while (page_mask != 0);
+
+ }
+
+ add_event("Exiting nfc_util_send_cmd_and_addrs", -1);
+
+}
+
+/**
+ * nfc_2_x_exit() - Version-specific shut down.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_x_exit(struct imx_nfc_data *this)
+{
+}
+
+/**
+ * nfc_2_x_clear_interrupt() - Clears an interrupt.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_x_clear_interrupt(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+ raw_clr_mask_w(NFC_2_X_CONFIG2_INT_MSK, base + NFC_2_X_CONFIG2_REG_OFF);
+}
+
+/**
+ * nfc_2_x_is_interrupting() - Returns the interrupt bit status.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_2_x_is_interrupting(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+ return raw_read_mask_w(NFC_2_X_CONFIG2_INT_MSK,
+ base + NFC_2_X_CONFIG2_REG_OFF);
+}
+
+/**
+ * nfc_2_x_command_cycle() - Sends a command.
+ *
+ * @this: Per-device data.
+ * @command: The command code.
+ */
+static void nfc_2_x_command_cycle(struct imx_nfc_data *this, unsigned command)
+{
+ void *base = this->primary_regs;
+
+ /* Write the command we want to send. */
+
+ __raw_writew(command, base + NFC_2_X_FLASH_CMD_REG_OFF);
+
+ /* Launch a command cycle. */
+
+ __raw_writew(NFC_2_X_CONFIG2_FCMD_MSK, base + NFC_2_X_CONFIG2_REG_OFF);
+
+}
+
+/**
+ * nfc_2_x_write_cycle() - Writes a single byte.
+ *
+ * @this: Per-device data.
+ * @byte: The byte.
+ */
+static void nfc_2_x_write_cycle(struct imx_nfc_data *this, unsigned int byte)
+{
+ void *base = this->primary_regs;
+
+ /* Give the NFC the byte we want to write. */
+
+ __raw_writew(byte, base + NFC_2_X_FLASH_ADDR_REG_OFF);
+
+ /* Launch an address cycle.
+ *
+ * This is *sort* of a hack, but not really. The intent of the NFC
+ * design is for this operation to send an address byte. In fact, the
+ * NFC neither knows nor cares what we're sending. It justs runs a write
+ * cycle.
+ */
+
+ __raw_writew(NFC_2_X_CONFIG2_FADD_MSK, base + NFC_2_X_CONFIG2_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, false);
+
+}
+
+/**
+ * nfc_2_0_init() - Version-specific hardware initialization.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_2_0_init(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+
+ /* Initialize the interrupt machinery. */
+
+ this->nfc->mask_interrupt(this);
+ this->nfc->clear_interrupt(this);
+
+ /* Unlock the NFC memory. */
+
+ __raw_writew(0x2, base + NFC_2_X_CONFIG_REG_OFF);
+
+ /* Set the unlocked block range to cover the entire medium. */
+
+ __raw_writew(0 , base + NFC_2_0_UNLOCK_START_REG_OFF);
+ __raw_writew(~0, base + NFC_2_0_UNLOCK_END_REG_OFF);
+
+ /* Unlock all blocks. */
+
+ __raw_writew(0x4, base + NFC_2_X_WR_PROT_REG_OFF);
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * nfc_2_0_mask_interrupt() - Masks interrupts.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_0_mask_interrupt(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+ raw_set_mask_w(NFC_2_0_CONFIG1_INT_MSK_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+}
+
+/**
+ * nfc_2_0_unmask_interrupt() - Unmasks interrupts.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_0_unmask_interrupt(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+ raw_clr_mask_w(NFC_2_0_CONFIG1_INT_MSK_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+}
+
+/**
+ * nfc_2_0_set_ecc() - Turns ECC on or off.
+ *
+ * @this: Per-device data.
+ * @on: Indicates if ECC should be on or off.
+ */
+static void nfc_2_0_set_ecc(struct imx_nfc_data *this, int on)
+{
+ void *base = this->primary_regs;
+
+ if (on)
+ raw_set_mask_w(NFC_2_0_CONFIG1_ECC_EN_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+ else
+ raw_clr_mask_w(NFC_2_0_CONFIG1_ECC_EN_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+
+}
+
+/**
+ * nfc_2_0_get_ecc_status() - Reports ECC errors.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_2_0_get_ecc_status(struct imx_nfc_data *this)
+{
+ unsigned int i;
+ void *base = this->primary_regs;
+ uint16_t status_reg;
+ unsigned int buffer_status[4];
+ int status;
+
+ /* Get the entire status register. */
+
+ status_reg = __raw_readw(base + NFC_2_0_ECC_STATUS_REG_OFF);
+
+ /* Pick out the status for each buffer. */
+
+ buffer_status[0] = (status_reg & NFC_2_0_ECC_STATUS_NOSER1_MSK)
+ >> NFC_2_0_ECC_STATUS_NOSER1_POS;
+
+ buffer_status[1] = (status_reg & NFC_2_0_ECC_STATUS_NOSER2_MSK)
+ >> NFC_2_0_ECC_STATUS_NOSER2_POS;
+
+ buffer_status[2] = (status_reg & NFC_2_0_ECC_STATUS_NOSER3_MSK)
+ >> NFC_2_0_ECC_STATUS_NOSER3_POS;
+
+ buffer_status[3] = (status_reg & NFC_2_0_ECC_STATUS_NOSER4_MSK)
+ >> NFC_2_0_ECC_STATUS_NOSER4_POS;
+
+ /* Loop through the buffers we're actually using. */
+
+ status = 0;
+
+ for (i = 0; i < this->nfc_geometry->buffer_count; i++) {
+
+ if (buffer_status[i] > this->nfc_geometry->ecc_strength) {
+ status = -1;
+ break;
+ }
+
+ status += buffer_status[i];
+
+ }
+
+ /* Return the final result. */
+
+ return status;
+
+}
+
+/**
+ * nfc_2_0_get_symmetric() - Indicates if the clock is symmetric.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_2_0_get_symmetric(struct imx_nfc_data *this)
+{
+ void *base = this->primary_regs;
+
+ return !!raw_read_mask_w(NFC_2_0_CONFIG1_ONE_CYLE_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+
+}
+
+/**
+ * nfc_2_0_set_symmetric() - Turns symmetric clock mode on or off.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_0_set_symmetric(struct imx_nfc_data *this, int on)
+{
+ void *base = this->primary_regs;
+
+ if (on)
+ raw_set_mask_w(NFC_2_0_CONFIG1_ONE_CYLE_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+ else
+ raw_clr_mask_w(NFC_2_0_CONFIG1_ONE_CYLE_MSK,
+ base + NFC_2_0_CONFIG1_REG_OFF);
+
+}
+
+/**
+ * nfc_2_0_set_geometry() - Configures for the medium geometry.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_2_0_set_geometry(struct imx_nfc_data *this)
+{
+ struct physical_geometry *physical = &this->physical_geometry;
+
+ /* Select an NFC geometry. */
+
+ switch (physical->page_data_size) {
+
+ case 512:
+ this->nfc_geometry = &nfc_geometry_512_16_RS_ECC4;
+ break;
+
+ case 2048:
+ this->nfc_geometry = &nfc_geometry_2K_64_RS_ECC4;
+ break;
+
+ default:
+ dev_err(this->dev, "NFC can't handle page size: %u",
+ physical->page_data_size);
+ return !0;
+ break;
+
+ }
+
+ /*
+ * This NFC version receives page size information from a register
+ * that's external to the NFC. We must rely on platform-specific code
+ * to set this register for us.
+ */
+
+ return this->pdata->set_page_size(physical->page_data_size);
+
+}
+
+/**
+ * nfc_2_0_select_chip() - Selects the current chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip number to select, or -1 to select no chip.
+ */
+static void nfc_2_0_select_chip(struct imx_nfc_data *this, int chip)
+{
+}
+
+/**
+ * nfc_2_0_read_cycle() - Applies a single read cycle to the current chip.
+ *
+ * @this: Per-device data.
+ */
+static unsigned int nfc_2_0_read_cycle(struct imx_nfc_data *this)
+{
+ uint8_t byte;
+ unsigned int result;
+ void *base = this->primary_regs;
+
+ /* Read into main buffer 0. */
+
+ __raw_writew(0x0, base + NFC_2_0_BUF_ADDR_REG_OFF);
+
+ /* Launch a "Data Out" operation. */
+
+ __raw_writew(0x4 << NFC_2_X_CONFIG2_FDO_POS,
+ base + NFC_2_X_CONFIG2_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, false);
+
+ /* Get the result from the NFC memory. */
+
+ nfc_util_copy_from_the_nfc(this, 0, &byte, 1);
+ result = byte;
+
+ /* Return the results. */
+
+ return result;
+
+}
+
+/**
+ * nfc_2_0_read_page() - Reads a page from the current chip into the NFC.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_0_read_page(struct imx_nfc_data *this)
+{
+ unsigned int i;
+ void *base = this->primary_regs;
+
+ /* Loop over the number of buffers in use. */
+
+ for (i = 0; i < this->nfc_geometry->buffer_count; i++) {
+
+ /* Make the NFC read into the current buffer. */
+
+ __raw_writew(i << NFC_2_0_BUF_ADDR_RBA_POS,
+ base + NFC_2_0_BUF_ADDR_REG_OFF);
+
+ /* Launch a page data out operation. */
+
+ __raw_writew(0x1 << NFC_2_X_CONFIG2_FDO_POS,
+ base + NFC_2_X_CONFIG2_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ }
+
+}
+
+/**
+ * nfc_2_0_send_page() - Sends a page from the NFC to the current chip.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_2_0_send_page(struct imx_nfc_data *this)
+{
+ unsigned int i;
+ void *base = this->primary_regs;
+
+ /* Loop over the number of buffers in use. */
+
+ for (i = 0; i < this->nfc_geometry->buffer_count; i++) {
+
+ /* Make the NFC send from the current buffer. */
+
+ __raw_writew(i << NFC_2_0_BUF_ADDR_RBA_POS,
+ base + NFC_2_0_BUF_ADDR_REG_OFF);
+
+ /* Launch a page data in operation. */
+
+ __raw_writew(0x1 << NFC_2_X_CONFIG2_FDI_POS,
+ base + NFC_2_X_CONFIG2_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ }
+
+}
+
+/**
+ * nfc_3_2_init() - Hardware initialization.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_init(struct imx_nfc_data *this)
+{
+ int error;
+ unsigned int no_sdma;
+ unsigned int fmp;
+ unsigned int rbb_mode;
+ unsigned int num_of_devices;
+ unsigned int dma_mode;
+ unsigned int sbb;
+ unsigned int nf_big;
+ unsigned int sb2r;
+ unsigned int fw;
+ unsigned int too;
+ unsigned int add_op;
+ uint32_t config3;
+ void *primary_base = this->primary_regs;
+ void *secondary_base = this->secondary_regs;
+
+ /* Initialize the interrupt machinery. */
+
+ this->nfc->mask_interrupt(this);
+ this->nfc->clear_interrupt(this);
+
+ /* Set up the clock. */
+
+ error = this->nfc->set_closest_cycle(this,
+ this->pdata->target_cycle_in_ns);
+
+ if (error)
+ return !0;
+
+ /* We never read the spare area alone. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_SP_EN_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Tell the NFC the "Read Status" command code. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG2_ST_CMD_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+ raw_set_mask_l(NAND_CMD_STATUS << NFC_3_2_CONFIG2_ST_CMD_POS,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+ /*
+ * According to erratum ENGcm09051, the CONFIG3 register doesn't reset
+ * correctly, so we need to re-build the entire register just in case.
+ */
+
+ /*
+ * Set the NO_SDMA bit to tell the NFC that we are NOT using SDMA. If
+ * you clear this bit (to indicates you *are* using SDMA), but you
+ * don't actually set up SDMA, the NFC has been observed to crash the
+ * hardware when it asserts its DMA request signals. In the future, we
+ * *may* use SDMA, but it's not worth the effort at this writing.
+ */
+
+ no_sdma = 0x1;
+
+ /*
+ * Set the default FIFO Mode Protection (128 bytes). FMP doesn't work if
+ * the NO_SDMA bit is set.
+ */
+
+ fmp = 0x2;
+
+ /*
+ * The rbb_mode bit determines how the NFC figures out whether chips are
+ * ready during automatic operations only (this has no effect on atomic
+ * operations). The two choices are either to monitor the ready/busy
+ * signals, or to read the status register. We monitor the ready/busy
+ * signals.
+ */
+
+ rbb_mode = 0x1;
+
+ /*
+ * We don't yet know how many devices are connected. We'll find out in
+ * out in nfc_3_2_set_geometry().
+ */
+
+ num_of_devices = 0;
+
+ /* Set the default DMA mode. */
+
+ dma_mode = 0x0;
+
+ /* Set the default status busy bit. */
+
+ sbb = 0x6;
+
+ /* Little-endian (the default). */
+
+ nf_big = 0x0;
+
+ /* Set the default (standard) status bit to record. */
+
+ sb2r = 0x0;
+
+ /* We support only 8-bit Flash bus width. */
+
+ fw = 0x1;
+
+ /* We don't support "two-on-one." */
+
+ too = 0x0;
+
+ /* Set the addressing option. */
+
+ add_op = 0x3;
+
+ /* Set the CONFIG3 register. */
+
+ config3 = 0;
+
+ config3 |= no_sdma << NFC_3_2_CONFIG3_NO_SDMA_POS;
+ config3 |= fmp << NFC_3_2_CONFIG3_FMP_POS;
+ config3 |= rbb_mode << NFC_3_2_CONFIG3_RBB_MODE_POS;
+ config3 |= num_of_devices << NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
+ config3 |= dma_mode << NFC_3_2_CONFIG3_DMA_MODE_POS;
+ config3 |= sbb << NFC_3_2_CONFIG3_SBB_POS;
+ config3 |= nf_big << NFC_3_2_CONFIG3_NF_BIG_POS;
+ config3 |= sb2r << NFC_3_2_CONFIG3_SB2R_POS;
+ config3 |= fw << NFC_3_2_CONFIG3_FW_POS;
+ config3 |= too << NFC_3_2_CONFIG3_TOO_POS;
+ config3 |= add_op << NFC_3_2_CONFIG3_ADD_OP_POS;
+
+ __raw_writel(config3, secondary_base + NFC_3_2_CONFIG3_REG_OFF);
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * nfc_3_2_set_geometry() - Configures for the medium geometry.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_set_geometry(struct imx_nfc_data *this)
+{
+ unsigned int ps;
+ unsigned int cmd_phases;
+ unsigned int pages_per_chip;
+ unsigned int addr_phases0;
+ unsigned int addr_phases1;
+ unsigned int pages_per_block;
+ unsigned int ecc_mode;
+ unsigned int ppb;
+ unsigned int spas;
+ unsigned int mask;
+ uint32_t config2;
+ unsigned int num_of_devices;
+ uint32_t config3;
+ unsigned int x;
+ unsigned int chip;
+ struct physical_geometry *physical = &this->physical_geometry;
+ void *secondary_base = this->secondary_regs;
+
+ /*
+ * Select an NFC geometry based on the physical geometry and the
+ * capabilities of this NFC.
+ */
+
+ switch (physical->page_data_size) {
+
+ case 512:
+ this->nfc_geometry = &nfc_geometry_512_16_BCH_ECC4;
+ ps = 0;
+ break;
+
+ case 2048:
+ this->nfc_geometry = &nfc_geometry_2K_64_BCH_ECC4;
+ ps = 1;
+ break;
+
+ case 4096:
+
+ switch (this->physical_geometry.page_oob_size) {
+
+ case 128:
+ this->nfc_geometry = &nfc_geometry_4K_128_BCH_ECC4;
+ break;
+
+ case 218:
+ this->nfc_geometry = &nfc_geometry_4K_218_BCH_ECC8;
+ break;
+
+ default:
+ dev_err(this->dev,
+ "NFC can't handle page geometry: %u+%u",
+ physical->page_data_size,
+ physical->page_oob_size);
+ return !0;
+ break;
+
+ }
+
+ ps = 2;
+
+ break;
+
+ default:
+ dev_err(this->dev, "NFC can't handle page size: %u",
+ physical->page_data_size);
+ return !0;
+ break;
+
+ }
+
+ /* Compute the ECC mode. */
+
+ switch (this->nfc_geometry->ecc_strength) {
+
+ case 4:
+ ecc_mode = 0;
+ break;
+
+ case 8:
+ ecc_mode = 1;
+ break;
+
+ default:
+ dev_err(this->dev, "NFC can't handle ECC strength: %u",
+ this->nfc_geometry->ecc_strength);
+ return !0;
+ break;
+
+ }
+
+ /* Compute the pages per block. */
+
+ pages_per_block = physical->block_size / physical->page_data_size;
+
+ switch (pages_per_block) {
+ case 32:
+ ppb = 0;
+ break;
+ case 64:
+ ppb = 1;
+ break;
+ case 128:
+ ppb = 2;
+ break;
+ case 256:
+ ppb = 3;
+ break;
+ default:
+ dev_err(this->dev, "NFC can't handle pages per block: %d",
+ pages_per_block);
+ return !0;
+ break;
+ }
+
+ /*
+ * The hardware needs to know the physical size of the spare area, in
+ * units of half-words (16 bits). This may be different from the amount
+ * of the spare area we actually expose to MTD. For example, for for
+ * 2K+112, we only expose 64 spare bytes, but the hardware needs to know
+ * the real facts.
+ */
+
+ spas = this->physical_geometry.page_oob_size >> 1;
+
+ /*
+ * The number of command phases needed to read a page is directly
+ * dependent on whether this is a small page or large page device. Large
+ * page devices need more address phases, terminated by a second command
+ * phase.
+ */
+
+ cmd_phases = is_large_page_chip(this) ? 1 : 0;
+
+ /*
+ * The num_adr_phases1 field contains the number of phases needed to
+ * transmit addresses for read and program operations. This is the sum
+ * of the number of phases for a page address and the number of phases
+ * for a column address.
+ *
+ * The number of phases for a page address is the number of bytes needed
+ * to contain a page address.
+ *
+ * The number of phases for a column address is the number of bytes
+ * needed to contain a column address.
+ *
+ * After computing the sum, we subtract three because a value of zero in
+ * this field indicates three address phases, and this is the minimum
+ * number of phases the hardware can comprehend.
+ *
+ * We compute the number of phases based on the *physical* geometry, not
+ * the NFC geometry. For example, even if we are treating a very large
+ * device as if it contains fewer pages than it actually does, the
+ * hardware still needs the additional address phases.
+ */
+
+ pages_per_chip =
+ physical->chip_size >> (fls(physical->page_data_size) - 1);
+
+ addr_phases1 = (fls(pages_per_chip) >> 3) + 1;
+
+ addr_phases1 += (fls(physical->page_data_size) >> 3) + 1;
+
+ addr_phases1 -= 3;
+
+ /*
+ * The num_adr_phases0 field contains the number of phases needed to
+ * transmit a page address for an erase operation. That is, this is
+ * the value of addr_phases1, less the number of phases for the column
+ * address.
+ *
+ * The hardware expresses this phase count as one or two cycles less
+ * than the count indicated by add_phases1 (see the reference manual).
+ */
+
+ addr_phases0 = is_large_page_chip(this) ? 1 : 0;
+
+ /* Set the CONFIG2 register. */
+
+ mask =
+ NFC_3_2_CONFIG2_PS_MSK |
+ NFC_3_2_CONFIG2_CMD_PHASES_MSK |
+ NFC_3_2_CONFIG2_ADDR_PHASES0_MSK |
+ NFC_3_2_CONFIG2_ECC_MODE_MSK |
+ NFC_3_2_CONFIG2_PPB_MSK |
+ NFC_3_2_CONFIG2_ADDR_PHASES1_MSK |
+ NFC_3_2_CONFIG2_SPAS_MSK ;
+
+ config2 = __raw_readl(secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+ config2 &= ~mask;
+
+ config2 |= ps << NFC_3_2_CONFIG2_PS_POS;
+ config2 |= cmd_phases << NFC_3_2_CONFIG2_CMD_PHASES_POS;
+ config2 |= addr_phases0 << NFC_3_2_CONFIG2_ADDR_PHASES0_POS;
+ config2 |= ecc_mode << NFC_3_2_CONFIG2_ECC_MODE_POS;
+ config2 |= ppb << NFC_3_2_CONFIG2_PPB_POS;
+ config2 |= addr_phases1 << NFC_3_2_CONFIG2_ADDR_PHASES1_POS;
+ config2 |= spas << NFC_3_2_CONFIG2_SPAS_POS;
+
+ config2 = __raw_writel(config2,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+ /*
+ * Compute the num_of_devices field.
+ *
+ * It's very important to set this field correctly. This controls the
+ * set of ready/busy lines to which the NFC listens with automatic
+ * transactions. If this number is too large, the NFC will listen to
+ * ready/busy signals that are electrically floating, or it will try to
+ * read the status registers of chips that don't exist. Conversely, if
+ * the number is too small, the NFC could believe an operation is
+ * finished when some chips are still busy.
+ */
+
+ num_of_devices = physical->chip_count - 1;
+
+ /* Set the CONFIG3 register. */
+
+ mask = NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK;
+
+ config3 = __raw_readl(secondary_base + NFC_3_2_CONFIG3_REG_OFF);
+
+ config3 &= ~mask;
+
+ config3 |= num_of_devices << NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
+
+ __raw_writel(config3, secondary_base + NFC_3_2_CONFIG3_REG_OFF);
+
+ /*
+ * Check if the physical chip count is a power of 2. If not, then
+ * automatic operations aren't available. This is because we use an
+ * addressing option (see the ADD_OP field of CONFIG3) that requires
+ * a number of chips that is a power of 2.
+ */
+
+ if (ffs(physical->chip_count) != fls(physical->chip_count)) {
+ this->nfc->start_auto_read = 0;
+ this->nfc->start_auto_write = 0;
+ this->nfc->start_auto_erase = 0;
+ }
+
+ /* Unlock the NFC RAM. */
+
+ x = __raw_readl(secondary_base + NFC_3_2_WRPROT_REG_OFF);
+ x &= ~NFC_3_2_WRPROT_BLS_MSK;
+ x |= 0x2 << NFC_3_2_WRPROT_BLS_POS;
+ __raw_writel(x, secondary_base + NFC_3_2_WRPROT_REG_OFF);
+
+ /* Loop over chip selects, setting the unlocked ranges. */
+
+ for (chip = 0; chip < this->nfc->max_chip_count; chip++) {
+
+ /* Set the unlocked range to cover the entire chip.*/
+
+ __raw_writel(0xffff0000, secondary_base +
+ NFC_3_2_UNLOCK_BLK_ADD0_REG_OFF + (chip * 4));
+
+ /* Unlock. */
+
+ x = __raw_readl(secondary_base + NFC_3_2_WRPROT_REG_OFF);
+ x &= ~(NFC_3_2_WRPROT_CS2L_MSK | NFC_3_2_WRPROT_WPC_MSK);
+ x |= chip << NFC_3_2_WRPROT_CS2L_POS;
+ x |= 0x4 << NFC_3_2_WRPROT_WPC_POS ;
+ __raw_writel(x, secondary_base + NFC_3_2_WRPROT_REG_OFF);
+
+ }
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * nfc_3_2_exit() - Hardware cleanup.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_exit(struct imx_nfc_data *this)
+{
+}
+
+/**
+ * nfc_3_2_set_closest_cycle() - Version-specific hardware cleanup.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_set_closest_cycle(struct imx_nfc_data *this, unsigned ns)
+{
+ struct clk *parent_clock;
+ unsigned long parent_clock_rate_in_hz;
+ unsigned long sym_low_clock_rate_in_hz;
+ unsigned long asym_low_clock_rate_in_hz;
+ unsigned int sym_high_cycle_in_ns;
+ unsigned int asym_high_cycle_in_ns;
+
+ /*
+ * According to ENGcm09121:
+ *
+ * - If the NFC is set to SYMMETRIC mode, the NFC clock divider must
+ * divide the EMI Slow Clock by NO MORE THAN 4.
+ *
+ * - If the NFC is set for ASYMMETRIC mode, the NFC clock divider must
+ * divide the EMI Slow Clock by NO MORE THAN 3.
+ *
+ * We need to compute the corresponding cycle time constraints. Start
+ * by getting information about the parent clock.
+ */
+
+ parent_clock = clk_get_parent(this->clock);
+ parent_clock_rate_in_hz = clk_get_rate(parent_clock);
+
+ /* Compute the limit frequencies. */
+
+ sym_low_clock_rate_in_hz = parent_clock_rate_in_hz / 4;
+ asym_low_clock_rate_in_hz = parent_clock_rate_in_hz / 3;
+
+ /* Compute the corresponding limit cycle periods. */
+
+ sym_high_cycle_in_ns = 1000000000 / sym_low_clock_rate_in_hz;
+ asym_high_cycle_in_ns = (1000000000 / asym_low_clock_rate_in_hz) * 2;
+
+ /* Attempt to implement the given cycle. */
+
+ return nfc_util_set_best_cycle(this, ns,
+ ns > asym_high_cycle_in_ns, ns > sym_high_cycle_in_ns);
+
+}
+
+/**
+ * nfc_3_2_mask_interrupt() - Masks interrupts.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_mask_interrupt(struct imx_nfc_data *this)
+{
+ void *secondary_base = this->secondary_regs;
+ raw_set_mask_l(NFC_3_2_CONFIG2_INT_MSK_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+}
+
+/**
+ * nfc_3_2_unmask_interrupt() - Unmasks interrupts.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_unmask_interrupt(struct imx_nfc_data *this)
+{
+ void *secondary_base = this->secondary_regs;
+ raw_clr_mask_l(NFC_3_2_CONFIG2_INT_MSK_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+}
+
+/**
+ * nfc_3_2_clear_interrupt() - Clears an interrupt.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_clear_interrupt(struct imx_nfc_data *this)
+{
+ int done;
+ void *secondary_base = this->secondary_regs;
+
+ /* Request IP bus interface access. */
+
+ raw_set_mask_l(NFC_3_2_IPC_CREQ_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+ /* Wait for access. */
+
+ do
+ done = !!raw_read_mask_l(NFC_3_2_IPC_CACK_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+ while (!done);
+
+ /* Clear the interrupt. */
+
+ raw_clr_mask_l(NFC_3_2_IPC_INT_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+ /* Release the IP bus interface. */
+
+ raw_clr_mask_l(NFC_3_2_IPC_CREQ_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_is_interrupting() - Returns the interrupt bit status.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_is_interrupting(struct imx_nfc_data *this)
+{
+ void *secondary_base = this->secondary_regs;
+ return !!raw_read_mask_l(NFC_3_2_IPC_INT_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+}
+
+/**
+ * nfc_3_2_is_ready() - Returns the ready/busy status.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_is_ready(struct imx_nfc_data *this)
+{
+ void *secondary_base = this->secondary_regs;
+ return !!raw_read_mask_l(NFC_3_2_IPC_RB_B_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+}
+
+/**
+ * nfc_3_2_set_force_ce() - Can force CE to be asserted always.
+ *
+ * @this: Per-device data.
+ * @on: Indicates if the hardware CE signal should be asserted always.
+ */
+static void nfc_3_2_set_force_ce(struct imx_nfc_data *this, int on)
+{
+ void *primary_base = this->primary_regs;
+
+ if (on)
+ raw_set_mask_l(NFC_3_2_CONFIG1_NF_CE_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+ else
+ raw_clr_mask_l(NFC_3_2_CONFIG1_NF_CE_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_set_ecc() - Turns ECC on or off.
+ *
+ * @this: Per-device data.
+ * @on: Indicates if ECC should be on or off.
+ */
+static void nfc_3_2_set_ecc(struct imx_nfc_data *this, int on)
+{
+ void *secondary_base = this->secondary_regs;
+
+ if (on)
+ raw_set_mask_l(NFC_3_2_CONFIG2_ECC_EN_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+ else
+ raw_clr_mask_l(NFC_3_2_CONFIG2_ECC_EN_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_get_ecc_status() - Reports ECC errors.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_get_ecc_status(struct imx_nfc_data *this)
+{
+ unsigned int i;
+ void *base = this->primary_regs;
+ uint16_t status_reg;
+ unsigned int buffer_status[8];
+ int status;
+
+ /* Get the entire status register. */
+
+ status_reg = __raw_readw(base + NFC_3_2_ECC_STATUS_REG_OFF);
+
+ /* Pick out the status for each buffer. */
+
+ buffer_status[0] = (status_reg & NFC_3_2_ECC_STATUS_NOBER1_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER1_POS;
+
+ buffer_status[1] = (status_reg & NFC_3_2_ECC_STATUS_NOBER2_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER2_POS;
+
+ buffer_status[2] = (status_reg & NFC_3_2_ECC_STATUS_NOBER3_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER3_POS;
+
+ buffer_status[3] = (status_reg & NFC_3_2_ECC_STATUS_NOBER4_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER4_POS;
+
+ buffer_status[4] = (status_reg & NFC_3_2_ECC_STATUS_NOBER5_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER5_POS;
+
+ buffer_status[5] = (status_reg & NFC_3_2_ECC_STATUS_NOBER6_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER6_POS;
+
+ buffer_status[6] = (status_reg & NFC_3_2_ECC_STATUS_NOBER7_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER7_POS;
+
+ buffer_status[7] = (status_reg & NFC_3_2_ECC_STATUS_NOBER8_MSK)
+ >> NFC_3_2_ECC_STATUS_NOBER8_POS;
+
+ /* Loop through the buffers we're actually using. */
+
+ status = 0;
+
+ for (i = 0; i < this->nfc_geometry->buffer_count; i++) {
+
+ if (buffer_status[i] > this->nfc_geometry->ecc_strength) {
+ status = -1;
+ break;
+ }
+
+ status += buffer_status[i];
+
+ }
+
+ /* Return the final result. */
+
+ return status;
+
+}
+
+/**
+ * nfc_3_2_get_symmetric() - Indicates if the clock is symmetric.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_get_symmetric(struct imx_nfc_data *this)
+{
+ void *secondary_base = this->secondary_regs;
+
+ return !!raw_read_mask_w(NFC_3_2_CONFIG2_SYM_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_set_symmetric() - Turns symmetric clock mode on or off.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_set_symmetric(struct imx_nfc_data *this, int on)
+{
+ void *secondary_base = this->secondary_regs;
+
+ if (on)
+ raw_set_mask_l(NFC_3_2_CONFIG2_SYM_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+ else
+ raw_clr_mask_l(NFC_3_2_CONFIG2_SYM_MSK,
+ secondary_base + NFC_3_2_CONFIG2_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_select_chip() - Selects the current chip.
+ *
+ * @this: Per-device data.
+ * @chip: The chip number to select, or -1 to select no chip.
+ */
+static void nfc_3_2_select_chip(struct imx_nfc_data *this, int chip)
+{
+ unsigned long x;
+ void *primary_base = this->primary_regs;
+
+ if (chip < 0)
+ return;
+
+ x = __raw_readl(primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ x &= ~NFC_3_2_CONFIG1_CS_MSK;
+
+ x |= (chip << NFC_3_2_CONFIG1_CS_POS) & NFC_3_2_CONFIG1_CS_MSK;
+
+ __raw_writel(x, primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_command_cycle() - Sends a command.
+ *
+ * @this: Per-device data.
+ * @command: The command code.
+ */
+static void nfc_3_2_command_cycle(struct imx_nfc_data *this, unsigned command)
+{
+ void *primary_base = this->primary_regs;
+
+ /* Write the command we want to send. */
+
+ __raw_writel(command, primary_base + NFC_3_2_CMD_REG_OFF);
+
+ /* Launch a command cycle. */
+
+ __raw_writel(NFC_3_2_LAUNCH_FCMD_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_write_cycle() - writes a single byte.
+ *
+ * @this: Per-device data.
+ * @byte: The byte.
+ */
+static void nfc_3_2_write_cycle(struct imx_nfc_data *this, unsigned int byte)
+{
+ void *primary_base = this->primary_regs;
+
+ /* Give the NFC the byte we want to write. */
+
+ __raw_writel(byte, primary_base + NFC_3_2_ADD0_REG_OFF);
+
+ /* Launch an address cycle.
+ *
+ * This is *sort* of a hack, but not really. The intent of the NFC
+ * design is for this operation to send an address byte. In fact, the
+ * NFC neither knows nor cares what we're sending. It justs runs a write
+ * cycle.
+ */
+
+ __raw_writel(NFC_3_2_LAUNCH_FADD_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, false);
+
+}
+
+/**
+ * nfc_3_2_read_cycle() - Applies a single read cycle to the current chip.
+ *
+ * @this: Per-device data.
+ */
+static unsigned int nfc_3_2_read_cycle(struct imx_nfc_data *this)
+{
+ unsigned int result;
+ void *primary_base = this->primary_regs;
+
+ /* Launch a "Data Out" operation. */
+
+ __raw_writel(0x4 << NFC_3_2_LAUNCH_FDO_POS,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, false);
+
+ /* Get the result. */
+
+ result = __raw_readl(primary_base + NFC_3_2_CONFIG1_REG_OFF)
+ >> NFC_3_2_CONFIG1_STATUS_POS;
+ result &= 0xff;
+
+ /* Return the result. */
+
+ return result;
+
+}
+
+/**
+ * nfc_3_2_read_page() - Reads a page into the NFC memory.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_read_page(struct imx_nfc_data *this)
+{
+ void *primary_base = this->primary_regs;
+
+ /* Start reading into buffer 0. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Launch a page data out operation. */
+
+ __raw_writel(0x1 << NFC_3_2_LAUNCH_FDO_POS,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+}
+
+/**
+ * nfc_3_2_send_page() - Sends a page from the NFC to the current chip.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_send_page(struct imx_nfc_data *this)
+{
+ void *primary_base = this->primary_regs;
+
+ /* Start sending from buffer 0. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Launch a page data in operation. */
+
+ __raw_writel(NFC_3_2_LAUNCH_FDI_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+}
+
+/**
+ * nfc_3_2_add_state_events() - Adds events to display important state.
+ *
+ * @this: Per-device data.
+ */
+static void nfc_3_2_add_state_events(struct imx_nfc_data *this)
+{
+#ifdef EVENT_REPORTING
+ void *secondary_base = this->secondary_regs;
+
+ add_state_event_l
+ (
+ secondary_base + NFC_3_2_IPC_REG_OFF,
+ NFC_3_2_IPC_INT_MSK,
+ " Interrupt : 0",
+ " Interrupt : X"
+ );
+
+ add_state_event_l
+ (
+ secondary_base + NFC_3_2_IPC_REG_OFF,
+ NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
+ " auto_prog_done: 0",
+ " auto_prog_done: X"
+ );
+
+ add_state_event_l
+ (
+ secondary_base + NFC_3_2_IPC_REG_OFF,
+ NFC_3_2_IPC_RB_B_MSK,
+ " Medium : Busy",
+ " Medium : Ready"
+ );
+#endif
+}
+
+/**
+ * nfc_3_2_get_auto_loop_params() - Gets automatic operation loop parameters.
+ *
+ * This function and the corresponding "setter" enable the automatic operations
+ * to keep some state as they iterate over chips.
+ *
+ * The most "obvious" way to save state would be to allocate a private data
+ * structure and hang it off the owning struct nfc_hal. On the other hand,
+ * writing the code to allocate the memory and then release it when the NFC
+ * shuts down is annoying - and we have some perfectly good memory in the NFC
+ * hardware that we can use. Since we only use two commands at a time, we can
+ * stash our loop limits and loop index in the top 16 bits of the NAND_CMD
+ * register. To paraphrase the reference manual:
+ *
+ *
+ * NAND_CMD
+ *
+ * |<-- 4 bits -->|<-- 4 bits -->|<-- 8 bits -->|
+ * +----------------+---------------+--------------------------------+
+ * | First | Last | Loop Index |
+ * +----------------+---------------+--------------------------------+
+ * | NAND COMMAND1 | NAND COMMAND0 |
+ * +--------------------------------+--------------------------------+
+ * |<-- 16 bits -->|<-- 16 bits -->|
+ *
+ *
+ * @this: Per-device data.
+ * @first: A pointer to a variable that will receive the first chip number.
+ * @last: A pointer to a variable that will receive the last chip number.
+ * @index: A pointer to a variable that will receive the current chip number.
+ */
+static void nfc_3_2_get_auto_loop_params(struct imx_nfc_data *this,
+ unsigned *first, unsigned *last, unsigned *index)
+{
+ uint32_t x;
+ void *primary_base = this->primary_regs;
+
+ x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);
+
+ *first = (x >> 28) & 0x0f;
+ *last = (x >> 24) & 0x0f;
+ *index = (x >> 16) & 0xff;
+
+}
+
+/**
+ * nfc_3_2_set_auto_loop_params() - Sets automatic operation loop parameters.
+ *
+ * See nfc_3_2_get_auto_loop_params() for detailed information about these
+ * functions.
+ *
+ * @this: Per-device data.
+ * @first: The first chip number.
+ * @last: The last chip number.
+ * @index: The current chip number.
+ */
+static void nfc_3_2_set_auto_loop_params(struct imx_nfc_data *this,
+ unsigned first, unsigned last, unsigned index)
+{
+ uint32_t x;
+ void *primary_base = this->primary_regs;
+
+ x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);
+
+ x &= 0x0000ffff;
+ x |= (first & 0x0f) << 28;
+ x |= (last & 0x0f) << 24;
+ x |= (index & 0xff) << 16;
+
+ __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);
+
+}
+
+/**
+ * nfc_3_2_get_auto_addresses() - Gets automatic operation addresses.
+ *
+ * @this: Per-device data.
+ * @group: The address group number.
+ * @chip: A pointer to a variable that will receive the chip number.
+ * @column: A pointer to a variable that will receive the column address.
+ * A NULL pointer indicates there is no column address.
+ * @page: A pointer to a variable that will receive the page address.
+ */
+static void nfc_3_2_get_auto_addresses(struct imx_nfc_data *this,
+ unsigned group, unsigned *chip, unsigned *column, unsigned *page)
+{
+ uint32_t x;
+ unsigned int chip_count;
+ unsigned int cs_width;
+ unsigned int cs_mask;
+ unsigned int page_lsbs;
+ unsigned int page_msbs;
+ uint32_t *low;
+ uint16_t *high;
+ void *primary_base = this->primary_regs;
+ void *secondary_base = this->secondary_regs;
+
+ /*
+ * The width of the chip select field depends on the number of connected
+ * chips.
+ *
+ * Notice that these computations work only if the number of chips is a
+ * power of 2. In fact, that is a fundamental limitation for using
+ * automatic operations.
+ */
+
+ x = __raw_readl(secondary_base + NFC_3_2_CONFIG3_REG_OFF);
+
+ chip_count =
+ (x & NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK) >>
+ NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
+ chip_count++;
+
+ cs_width = ffs(chip_count) - 1;
+ cs_mask = chip_count - 1;
+
+ /* Construct pointers to the pieces of the given address group. */
+
+ low = primary_base + NFC_3_2_ADD0_REG_OFF;
+ low += group;
+
+ high = primary_base + NFC_3_2_ADD8_REG_OFF;
+ high += group;
+
+ /* Check if there's a column address. */
+
+ if (column) {
+
+ /*
+ * The low 32 bits of the address group look like this:
+ *
+ * 16 - n n
+ * | <- bits ->|<->|<- 16 bits ->|
+ * +-------------+---+----------------+
+ * | Page LSBs |CS | Column |
+ * +-------------+---+----------------+
+ */
+
+ x = __raw_readl(low);
+
+ *column = x & 0xffff;
+ *chip = (x >> 16) & cs_mask;
+ page_lsbs = x >> (16 + cs_width);
+
+ /* The high 16 bits contain the MSB's of the page address. */
+
+ page_msbs = __raw_readw(high);
+
+ *page = (page_msbs << (16 - cs_width)) | page_lsbs;
+
+ } else {
+
+ /*
+ * The low 32 bits of the address group look like this:
+ *
+ * n
+ * | <- (32 - n) bits ->|<->|
+ * +-----------------------------+---+
+ * | Page LSBs |CS |
+ * +-----------------------------+---+
+ */
+
+ x = __raw_readl(low);
+
+ *chip = x & cs_mask;
+ page_lsbs = x >> cs_width;
+
+ /* The high 16 bits contain the MSB's of the page address. */
+
+ page_msbs = __raw_readw(high);
+
+ *page = (page_msbs << (32 - cs_width)) | page_lsbs;
+
+ }
+
+}
+
+/**
+ * nfc_3_2_set_auto_addresses() - Sets automatic operation addresses.
+ *
+ * @this: Per-device data.
+ * @group: The address group number.
+ * @chip: The chip number.
+ * @column: The column address. The sentinel value ~0 indicates that there is
+ * no column address.
+ * @page: The page address.
+ */
+static void nfc_3_2_set_auto_addresses(struct imx_nfc_data *this,
+ unsigned group, unsigned chip, unsigned column, unsigned page)
+{
+ uint32_t x;
+ unsigned chip_count;
+ unsigned int cs_width;
+ unsigned int cs_mask;
+ uint32_t *low;
+ uint16_t *high;
+ void *primary_base = this->primary_regs;
+ void *secondary_base = this->secondary_regs;
+
+ /*
+ * The width of the chip select field depends on the number of connected
+ * chips.
+ *
+ * Notice that these computations work only if the number of chips is a
+ * power of 2. In fact, that is a fundamental limitation for using
+ * automatic operations.
+ */
+
+ x = __raw_readl(secondary_base + NFC_3_2_CONFIG3_REG_OFF);
+
+ chip_count =
+ (x & NFC_3_2_CONFIG3_NUM_OF_DEVICES_MSK) >>
+ NFC_3_2_CONFIG3_NUM_OF_DEVICES_POS;
+ chip_count++;
+
+ cs_width = ffs(chip_count) - 1;
+ cs_mask = chip_count - 1;
+
+ /* Construct pointers to the pieces of the given address group. */
+
+ low = primary_base + NFC_3_2_ADD0_REG_OFF;
+ low += group;
+
+ high = primary_base + NFC_3_2_ADD8_REG_OFF;
+ high += group;
+
+ /* Check if we have a column address. */
+
+ if (column != ~0) {
+
+ /*
+ * The low 32 bits of the address group look like this:
+ *
+ * 16 - n n
+ * | <- bits ->|<->|<- 16 bits ->|
+ * +-------------+---+----------------+
+ * | Page LSBs |CS | Column |
+ * +-------------+---+----------------+
+ */
+
+ x = 0;
+ x |= column & 0xffff;
+ x |= (chip & cs_mask) << 16;
+ x |= page << (16 + cs_width);
+
+ __raw_writel(x, low);
+
+ /* The high 16 bits contain the MSB's of the page address. */
+
+ x = (page >> (16 - cs_width)) & 0xffff;
+
+ __raw_writew(x, high);
+
+ } else {
+
+ /*
+ * The low 32 bits of the address group look like this:
+ *
+ * n
+ * | <- (32 - n) bits ->|<->|
+ * +-----------------------------+---+
+ * | Page LSBs |CS |
+ * +-----------------------------+---+
+ */
+
+ x = 0;
+ x |= chip & cs_mask;
+ x |= page << cs_width;
+
+ __raw_writel(x, low);
+
+ /* The high 16 bits contain the MSB's of the page address. */
+
+ x = (page >> (32 - cs_width)) & 0xffff;
+
+ __raw_writew(x, high);
+
+ }
+
+}
+
+/**
+ * nfc_3_2_start_auto_read() - Starts an automatic read.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ * @start: The first physical chip number on which to operate.
+ * @count: The number of physical chips on which to operate.
+ * @column: The column address.
+ * @page: The page address.
+ */
+static int nfc_3_2_start_auto_read(struct imx_nfc_data *this,
+ unsigned start, unsigned count, unsigned column, unsigned page)
+{
+ uint32_t x;
+ int return_value = 0;
+ void *primary_base = this->primary_regs;
+
+ add_event("Entering nfc_3_2_start_auto_read", 1);
+
+ /* Check for nonsense. */
+
+ if ((start > 7) || (!count) || (count > 8)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Set state. */
+
+ nfc_3_2_set_auto_loop_params(this, start, start + count - 1, start);
+ nfc_3_2_set_auto_addresses(this, 0, start, column, page);
+
+ /* Set up for ONE iteration at a time. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_ITER_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Reset to buffer 0. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /*
+ * Set up the commands. Note that the number of command phases was
+ * configured in the set_geometry() function so, even though we're
+ * giving both commands here, they won't necessarily both be used.
+ */
+
+ x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);
+
+ x &= 0xffff0000;
+ x |= NAND_CMD_READ0 << 0;
+ x |= NAND_CMD_READSTART << 8;
+
+ __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);
+
+ /* Launch the operation. */
+
+ add_event("Launching", 0);
+
+ __raw_writel(NFC_3_2_LAUNCH_AUTO_READ_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_start_auto_read", -1);
+
+ return return_value;
+
+}
+
+/**
+ * nfc_3_2_wait_for_auto_read() - Waits until auto read is ready for the CPU.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_wait_for_auto_read(struct imx_nfc_data *this)
+{
+ unsigned int first;
+ unsigned int last;
+ unsigned int index;
+ int return_value = 0;
+
+ add_event("Entering nfc_3_2_wait_for_auto_read", 1);
+
+ /* Get state. */
+
+ nfc_3_2_get_auto_loop_params(this, &first, &last, &index);
+
+ /* This function should be called for every chip. */
+
+ if ((index < first) || (index > last)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Wait for the NFC to completely finish and interrupt. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_wait_for_auto_read", -1);
+
+ return return_value;
+
+}
+
+/**
+ * nfc_3_2_resume_auto_read() - Resumes auto read after CPU intervention.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_resume_auto_read(struct imx_nfc_data *this)
+{
+ unsigned int first;
+ unsigned int last;
+ unsigned int index;
+ unsigned int chip;
+ unsigned int column;
+ unsigned int page;
+ int return_value = 0;
+ void *primary_base = this->primary_regs;
+
+ add_event("Entering nfc_3_2_resume_auto_read", 1);
+
+ /* Get state. */
+
+ nfc_3_2_get_auto_loop_params(this, &first, &last, &index);
+ nfc_3_2_get_auto_addresses(this, 0, &chip, &column, &page);
+
+ /* This function should be called for every chip, except the last. */
+
+ if ((index < first) || (index >= last)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Move to the next chip. */
+
+ index++;
+
+ /* Update state. */
+
+ nfc_3_2_set_auto_loop_params(this, first, last, index);
+ nfc_3_2_set_auto_addresses(this, 0, index, column, page);
+
+ /* Reset to buffer 0. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Launch the operation. */
+
+ add_event("Launching", 0);
+
+ __raw_writel(NFC_3_2_LAUNCH_AUTO_READ_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_resume_auto_read", -1);
+
+ return return_value;
+
+}
+
+/**
+ * nfc_3_2_start_auto_write() - Starts an automatic write.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ * @start: The first physical chip number on which to operate.
+ * @count: The number of physical chips on which to operate.
+ * @column: The column address.
+ * @page: The page address.
+ */
+static int nfc_3_2_start_auto_write(struct imx_nfc_data *this,
+ unsigned start, unsigned count, unsigned column, unsigned page)
+{
+ uint32_t x;
+ int return_value = 0;
+ void *primary_base = this->primary_regs;
+ void *secondary_base = this->secondary_regs;
+
+ add_event("Entering nfc_3_2_start_auto_write", 1);
+
+ /* Check for nonsense. */
+
+ if ((start > 7) || (!count) || (count > 8)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Set state. */
+
+ nfc_3_2_set_auto_loop_params(this, start, start + count - 1, start);
+ nfc_3_2_set_auto_addresses(this, 0, start, column, page);
+
+ /* Set up for ONE iteration at a time. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_ITER_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Set up the commands. */
+
+ x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);
+
+ x &= 0xffff0000;
+ x |= NAND_CMD_SEQIN << 0;
+ x |= NAND_CMD_PAGEPROG << 8;
+
+ __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);
+
+ /* Clear the auto_prog_done bit. */
+
+ raw_clr_mask_l(NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_start_auto_write", -1);
+
+ return return_value;
+
+}
+
+/**
+ * nfc_3_2_wait_for_auto_write() - Waits for auto write to be writey for the CPU.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ */
+static int nfc_3_2_wait_for_auto_write(struct imx_nfc_data *this)
+{
+ unsigned int first;
+ unsigned int last;
+ unsigned int index;
+ unsigned int chip;
+ unsigned int column;
+ unsigned int page;
+ uint32_t x;
+ int interrupt;
+ int transmitted;
+ int ready;
+ int return_value = 0;
+ void *primary_base = this->primary_regs;
+ void *secondary_base = this->secondary_regs;
+
+ add_event("Entering nfc_3_2_wait_for_auto_write", 1);
+
+ /* Get state. */
+
+ nfc_3_2_get_auto_loop_params(this, &first, &last, &index);
+ nfc_3_2_get_auto_addresses(this, 0, &chip, &column, &page);
+
+ /* This function should be called for every chip. */
+
+ if ((index < first) || (index > last)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Reset to buffer 0. */
+
+ raw_clr_mask_l(NFC_3_2_CONFIG1_RBA_MSK,
+ primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Launch the operation. */
+
+ nfc_3_2_add_state_events(this);
+
+ add_event("Launching", 0);
+
+ __raw_writel(NFC_3_2_LAUNCH_AUTO_PROG_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+ nfc_3_2_add_state_events(this);
+
+ /* Wait for the NFC to transmit the page. */
+
+ add_event("Spinning while the NFC transmits the page...", 0);
+
+ do
+ transmitted = !!raw_read_mask_l(NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+ while (!transmitted);
+
+ /*
+ * When control arrives here, the auto_prog_done bit is set. This
+ * indicates the NFC has finished transmitting the current page. The CPU
+ * is now free to write the next page into the NFC's memory. The Flash
+ * hardware is still busy programming the page into its storage array.
+ *
+ * Clear the auto_prog_done bit. This is analogous to acknowledging an
+ * interrupt.
+ */
+
+ nfc_3_2_add_state_events(this);
+
+ add_event("Acknowledging the page...", 0);
+
+ raw_clr_mask_l(NFC_3_2_IPC_AUTO_PROG_DONE_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+ nfc_3_2_add_state_events(this);
+
+ /*
+ * If this is *not* the last iteration, move to the next chip and return
+ * to the caller so he can put the next page in the NFC buffer.
+ */
+
+ if (index < last) {
+
+ add_event("Moving to the next chip...", 0);
+
+ index++;
+
+ nfc_3_2_set_auto_loop_params(this, first, last, index);
+ nfc_3_2_set_auto_addresses(this, 0, index, column, page);
+
+ goto exit;
+
+ }
+
+ /*
+ * If control arrives here, this is the last iteration, so it's time to
+ * close out the entire operation. We need to wait for the medium to be
+ * ready and then acknowledge the final interrupt.
+ *
+ * Because of the way the NFC hardware works, the code here requires a
+ * bit of explanation. The most important rule is:
+ *
+ * During automatic operations, the NFC sets its
+ * interrupt bit *whenever* it sees the ready/busy
+ * signal transition from "Busy" to "Ready".
+ *
+ * Recall that the ready/busy signals from all the chips in the medium
+ * are "wire-anded." Thus, the NFC will only see that the medium is
+ * ready if *all* chips are ready.
+ *
+ * Because of variability in NAND Flash timing, the medium *may* have
+ * become ready during previous iterations, which means the interrupt
+ * bit *may* be set at this moment. This is a "left-over" interrupt, and
+ * can complicate our logic.
+ *
+ * The two bits of state that interest us here are the interrupt bit
+ * and the ready/busy bit. It boils down to the following truth table:
+ *
+ * | Interrupt | Ready/Busy | Description
+ * +------------+------------+---------------
+ * | | | Busy medium and no left-over interrupt.
+ * | 0 | 0 | The final interrupt will arrive in the
+ * | | | future.
+ * +------------+------------+---------------
+ * | | | Ready medium and no left-over interrupt.
+ * | 0 | 1 | There will be no final interrupt. This
+ * | | | case should be impossible.
+ * +------------+------------+---------------
+ * | | | Busy medium and left-over interrupt.
+ * | 1 | 0 | The final interrupt will arrive in the
+ * | | | future. This is the hard case.
+ * +------------+------------+---------------
+ * | | | Ready medium and left-over interrupt.
+ * | 1 | 1 | The final interrupt has already
+ * | | | arrived. Acknowledge it and exit.
+ * +------------+------------+---------------
+ *
+ * Case #3 is a small problem. If we clear the interrupt, we may or may
+ * not have another interrupt following.
+ */
+
+ /* Sample the IPC register. */
+
+ x = __raw_readl(secondary_base + NFC_3_2_IPC_REG_OFF);
+
+ interrupt = !!(x & NFC_3_2_IPC_INT_MSK);
+ ready = !!(x & NFC_3_2_IPC_RB_B_MSK);
+
+ /* Check for the easy cases. */
+
+ if (!interrupt && !ready) {
+ add_event("Waiting for the final interrupt..." , 0);
+ nfc_util_wait_for_the_nfc(this, true);
+ goto exit;
+ } else if (!interrupt && ready) {
+ add_event("Done." , 0);
+ goto exit;
+ } else if (interrupt && ready) {
+ add_event("Acknowledging the final interrupt..." , 0);
+ nfc_util_wait_for_the_nfc(this, false);
+ goto exit;
+
+ }
+
+ /*
+ * If control arrives here, we hit case #3. Begin by acknowledging the
+ * interrupt we have right now.
+ */
+
+ add_event("Clearing the left-over interrupt..." , 0);
+ nfc_util_wait_for_the_nfc(this, false);
+
+ /*
+ * Check the ready/busy bit again. If the medium is still busy, then
+ * we're going to get one more interrupt.
+ */
+
+ ready = !!raw_read_mask_l(NFC_3_2_IPC_RB_B_MSK,
+ secondary_base + NFC_3_2_IPC_REG_OFF);
+
+ if (!ready) {
+ add_event("Waiting for the final interrupt..." , 0);
+ nfc_util_wait_for_the_nfc(this, true);
+ }
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_wait_for_auto_write", -1);
+
+ return return_value;
+
+}
+
+/**
+ * nfc_3_2_start_auto_erase() - Starts an automatic erase.
+ *
+ * This function returns 0 if everything went well.
+ *
+ * @this: Per-device data.
+ * @start: The first physical chip number on which to operate.
+ * @count: The number of physical chips on which to operate.
+ * @page: The page address.
+ */
+static int nfc_3_2_start_auto_erase(struct imx_nfc_data *this,
+ unsigned start, unsigned count, unsigned page)
+{
+ uint32_t x;
+ unsigned i;
+ int return_value = 0;
+ void *primary_base = this->primary_regs;
+
+ add_event("Entering nfc_3_2_start_auto_erase", 1);
+
+ /* Check for nonsense. */
+
+ if ((start > 7) || (!count) || (count > 8)) {
+ return_value = !0;
+ goto exit;
+ }
+
+ /* Set up the commands. */
+
+ x = __raw_readl(primary_base + NFC_3_2_CMD_REG_OFF);
+
+ x &= 0xffff0000;
+ x |= NAND_CMD_ERASE1 << 0;
+ x |= NAND_CMD_ERASE2 << 8;
+
+ __raw_writel(x, primary_base + NFC_3_2_CMD_REG_OFF);
+
+ /* Set the iterations. */
+
+ x = __raw_readl(primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ x &= ~NFC_3_2_CONFIG1_ITER_MSK;
+ x |= ((count - 1) << NFC_3_2_CONFIG1_ITER_POS) &
+ NFC_3_2_CONFIG1_ITER_MSK;
+
+ __raw_writel(x, primary_base + NFC_3_2_CONFIG1_REG_OFF);
+
+ /* Loop over chips, setting up the address groups. */
+
+ for (i = 0; i < count; i++)
+ nfc_3_2_set_auto_addresses(this, i, start + i, ~0, page);
+
+ /* Launch the operation. */
+
+ add_event("Launching", 0);
+
+ __raw_writel(NFC_3_2_LAUNCH_AUTO_ERASE_MSK,
+ primary_base + NFC_3_2_LAUNCH_REG_OFF);
+
+exit: /* Return. */
+
+ add_event("Exiting nfc_3_2_start_auto_erase", -1);
+
+ return return_value;
+
+}
+
+/*
+ * At this point, we've defined all the version-specific primitives. We're now
+ * ready to construct the NFC HAL structures for every version.
+ */
+
+struct nfc_hal nfc_1_0_hal = {
+ .major_version = 1,
+ .minor_version = 0,
+ .max_chip_count = 1,
+ .max_buffer_count = 4,
+ .spare_buf_stride = 16,
+ .has_secondary_regs = 0,
+ .can_be_symmetric = 0,
+ };
+
+struct nfc_hal nfc_2_0_hal = {
+ .major_version = 2,
+ .minor_version = 0,
+ .max_chip_count = 1,
+ .max_buffer_count = 4,
+ .spare_buf_stride = 16,
+ .has_secondary_regs = false,
+ .can_be_symmetric = true,
+ .init = nfc_2_0_init,
+ .set_geometry = nfc_2_0_set_geometry,
+ .exit = nfc_2_x_exit,
+ .mask_interrupt = nfc_2_0_mask_interrupt,
+ .unmask_interrupt = nfc_2_0_unmask_interrupt,
+ .clear_interrupt = nfc_2_x_clear_interrupt,
+ .is_interrupting = nfc_2_x_is_interrupting,
+ .is_ready = 0, /* Ready/Busy not exposed. */
+ .set_ecc = nfc_2_0_set_ecc,
+ .get_ecc_status = nfc_2_0_get_ecc_status,
+ .get_symmetric = nfc_2_0_get_symmetric,
+ .set_symmetric = nfc_2_0_set_symmetric,
+ .select_chip = nfc_2_0_select_chip,
+ .command_cycle = nfc_2_x_command_cycle,
+ .write_cycle = nfc_2_x_write_cycle,
+ .read_cycle = nfc_2_0_read_cycle,
+ .read_page = nfc_2_0_read_page,
+ .send_page = nfc_2_0_send_page,
+ .start_auto_read = 0, /* Not supported. */
+ .wait_for_auto_read = 0, /* Not supported. */
+ .resume_auto_read = 0, /* Not supported. */
+ .start_auto_write = 0, /* Not supported. */
+ .wait_for_auto_write = 0, /* Not supported. */
+ .start_auto_erase = 0, /* Not supported. */
+ };
+
+struct nfc_hal nfc_2_1_hal = {
+ .major_version = 2,
+ .minor_version = 1,
+ .max_chip_count = 4,
+ .max_buffer_count = 8,
+ .spare_buf_stride = 64,
+ .has_secondary_regs = 0,
+ .can_be_symmetric = !0,
+ };
+
+struct nfc_hal nfc_3_1_hal = {
+ .major_version = 3,
+ .minor_version = 1,
+ .max_chip_count = 4,
+ .max_buffer_count = 8,
+ .spare_buf_stride = 64,
+ .has_secondary_regs = !0,
+ .can_be_symmetric = !0,
+ };
+
+struct nfc_hal nfc_3_2_hal = {
+ .major_version = 3,
+ .minor_version = 2,
+ .max_chip_count = 8,
+ .max_buffer_count = 8,
+ .spare_buf_stride = 64,
+ .has_secondary_regs = true,
+ .can_be_symmetric = true,
+ .init = nfc_3_2_init,
+ .set_geometry = nfc_3_2_set_geometry,
+ .exit = nfc_3_2_exit,
+ .set_closest_cycle = nfc_3_2_set_closest_cycle,
+ .mask_interrupt = nfc_3_2_mask_interrupt,
+ .unmask_interrupt = nfc_3_2_unmask_interrupt,
+ .clear_interrupt = nfc_3_2_clear_interrupt,
+ .is_interrupting = nfc_3_2_is_interrupting,
+ .is_ready = nfc_3_2_is_ready,
+ .set_force_ce = nfc_3_2_set_force_ce,
+ .set_ecc = nfc_3_2_set_ecc,
+ .get_ecc_status = nfc_3_2_get_ecc_status,
+ .get_symmetric = nfc_3_2_get_symmetric,
+ .set_symmetric = nfc_3_2_set_symmetric,
+ .select_chip = nfc_3_2_select_chip,
+ .command_cycle = nfc_3_2_command_cycle,
+ .write_cycle = nfc_3_2_write_cycle,
+ .read_cycle = nfc_3_2_read_cycle,
+ .read_page = nfc_3_2_read_page,
+ .send_page = nfc_3_2_send_page,
+ .start_auto_read = nfc_3_2_start_auto_read,
+ .wait_for_auto_read = nfc_3_2_wait_for_auto_read,
+ .resume_auto_read = nfc_3_2_resume_auto_read,
+ .start_auto_write = nfc_3_2_start_auto_write,
+ .wait_for_auto_write = nfc_3_2_wait_for_auto_write,
+ .start_auto_erase = nfc_3_2_start_auto_erase,
+ };
+
+/*
+ * This array has a pointer to every NFC HAL structure. The probing process will
+ * find the one that matches the version given by the platform.
+ */
+
+struct nfc_hal *(nfc_hals[]) = {
+ &nfc_1_0_hal,
+ &nfc_2_0_hal,
+ &nfc_2_1_hal,
+ &nfc_3_1_hal,
+ &nfc_3_2_hal,
+};
+
+/**
+ * mal_init() - Initialize the Medium Abstraction Layer.
+ *
+ * @this: Per-device data.
+ */
+static void mal_init(struct imx_nfc_data *this)
+{
+ this->interrupt_override = DRIVER_CHOICE;
+ this->auto_op_override = DRIVER_CHOICE;
+ this->inject_ecc_error = 0;
+}
+
+/**
+ * mal_set_physical_geometry() - Set up the physical medium geometry.
+ *
+ * This function retrieves the physical geometry information discovered by
+ * nand_scan(), corrects it, and records it in the per-device data structure.
+ *
+ * @this: Per-device data.
+ */
+static int mal_set_physical_geometry(struct imx_nfc_data *this)
+{
+ struct mtd_info *mtd = &this->mtd;
+ struct nand_chip *nand = &this->nand;
+ struct device *dev = this->dev;
+ uint8_t manufacturer_id;
+ uint8_t device_id;
+ unsigned int block_size_in_pages;
+ unsigned int chip_size_in_blocks;
+ unsigned int chip_size_in_pages;
+ uint64_t medium_size_in_bytes;
+ struct physical_geometry *physical = &this->physical_geometry;
+
+ /*
+ * Begin by transcribing exactly what the MTD code discovered. If there
+ * are any mistakes, we'll fix them in a moment.
+ */
+
+ physical->chip_count = nand->numchips;
+ physical->chip_size = nand->chipsize;
+ physical->block_size = mtd->erasesize;
+ physical->page_data_size = mtd->writesize;
+ physical->page_oob_size = mtd->oobsize;
+
+ /* Read some of the ID bytes from the first NAND Flash chip. */
+
+ nand->select_chip(mtd, 0);
+
+ nfc_util_send_cmd_and_addrs(this, NAND_CMD_READID, 0x00, -1);
+
+ manufacturer_id = nand->read_byte(mtd);
+ device_id = nand->read_byte(mtd);
+
+ /*
+ * Most manufacturers sell 4K page devices with 218 out-of-band bytes
+ * per page to accomodate ECC-8.
+ *
+ * Samsung and Hynix claim their parts have better reliability, so they
+ * only need ECC-4 and they have only 128 out-of-band bytes.
+ *
+ * The MTD code pays no attention to the manufacturer ID (something that
+ * eventually will have to change), so it believes that all 4K pages
+ * have 218 out-of-band bytes.
+ *
+ * We correct that mistake here.
+ */
+
+ if (physical->page_data_size == 4096) {
+ if ((manufacturer_id == NAND_MFR_SAMSUNG) ||
+ (manufacturer_id == NAND_MFR_HYNIX)) {
+ physical->page_oob_size = 128;
+ }
+ }
+
+ /* Compute some interesting facts. */
+
+ block_size_in_pages =
+ physical->block_size / physical->page_data_size;
+ chip_size_in_pages =
+ physical->chip_size >> (fls(physical->page_data_size) - 1);
+ chip_size_in_blocks =
+ physical->chip_size >> (fls(physical->block_size) - 1);
+ medium_size_in_bytes =
+ physical->chip_size * physical->chip_count;
+
+ /* Report. */
+
+ dev_dbg(dev, "-----------------\n");
+ dev_dbg(dev, "Physical Geometry\n");
+ dev_dbg(dev, "-----------------\n");
+ dev_dbg(dev, "Chip Count : %d\n", physical->chip_count);
+ dev_dbg(dev, "Page Data Size in Bytes: %u (0x%x)\n",
+ physical->page_data_size, physical->page_data_size);
+ dev_dbg(dev, "Page OOB Size in Bytes : %u\n",
+ physical->page_oob_size);
+ dev_dbg(dev, "Block Size in Bytes : %u (0x%x)\n",
+ physical->block_size, physical->block_size);
+ dev_dbg(dev, "Block Size in Pages : %u (0x%x)\n",
+ block_size_in_pages, block_size_in_pages);
+ dev_dbg(dev, "Chip Size in Bytes : %llu (0x%llx)\n",
+ physical->chip_size, physical->chip_size);
+ dev_dbg(dev, "Chip Size in Pages : %u (0x%x)\n",
+ chip_size_in_pages, chip_size_in_pages);
+ dev_dbg(dev, "Chip Size in Blocks : %u (0x%x)\n",
+ chip_size_in_blocks, chip_size_in_blocks);
+ dev_dbg(dev, "Medium Size in Bytes : %llu (0x%llx)\n",
+ medium_size_in_bytes, medium_size_in_bytes);
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * mal_set_nfc_geometry() - Set up the NFC geometry.
+ *
+ * This function calls the NFC HAL to select an NFC geometry that is compatible
+ * with the medium's physical geometry.
+ *
+ * @this: Per-device data.
+ */
+static int mal_set_nfc_geometry(struct imx_nfc_data *this)
+{
+ struct device *dev = this->dev;
+ struct nfc_geometry *nfc;
+
+ /* Set the NFC geometry. */
+
+ if (this->nfc->set_geometry(this))
+ return !0;
+
+ /* Get a pointer to the new NFC geometry information. */
+
+ nfc = this->nfc_geometry;
+
+ /* Report. */
+
+ dev_dbg(dev, "------------\n");
+ dev_dbg(dev, "NFC Geometry\n");
+ dev_dbg(dev, "------------\n");
+ dev_dbg(dev, "Page Data Size in Bytes: %u (0x%x)\n",
+ nfc->page_data_size, nfc->page_data_size);
+ dev_dbg(dev, "Page OOB Size in Bytes : %u\n", nfc->page_oob_size);
+ dev_dbg(dev, "ECC Algorithm : %s\n", nfc->ecc_algorithm);
+ dev_dbg(dev, "ECC Strength : %d\n", nfc->ecc_strength);
+ dev_dbg(dev, "Buffer Count : %u\n", nfc->buffer_count);
+ dev_dbg(dev, "Spare Buffer Size : %u\n", nfc->spare_buf_size);
+ dev_dbg(dev, "Spare Buffer Spillover : %u\n", nfc->spare_buf_spill);
+ dev_dbg(dev, "Auto Read Available : %s\n",
+ this->nfc->start_auto_read ? "Yes" : "No");
+ dev_dbg(dev, "Auto Write Available : %s\n",
+ this->nfc->start_auto_write ? "Yes" : "No");
+ dev_dbg(dev, "Auto Erase Available : %s\n",
+ this->nfc->start_auto_erase ? "Yes" : "No");
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * mal_set_logical_geometry() - Set up the logical medium geometry.
+ *
+ * This function constructs the logical geometry that we will expose to MTD,
+ * based on the physical and NFC geometries, and whether or not interleaving is
+ * on.
+ *
+ * @this: Per-device data.
+ */
+static int mal_set_logical_geometry(struct imx_nfc_data *this)
+{
+ const uint32_t max_medium_size_in_bytes = ~0;
+ int we_are_interleaving;
+ uint64_t physical_medium_size_in_bytes;
+ unsigned int usable_blocks;
+ unsigned int block_size_in_pages;
+ unsigned int chip_size_in_blocks;
+ unsigned int chip_size_in_pages;
+ unsigned int usable_medium_size_in_pages;
+ unsigned int usable_medium_size_in_blocks;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct nfc_geometry *nfc = this->nfc_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+ struct device *dev = this->dev;
+
+ /* Figure out if we're interleaving. */
+
+ we_are_interleaving = this->pdata->interleave;
+
+ switch (imx_nfc_module_interleave_override) {
+
+ case NEVER:
+ we_are_interleaving = false;
+ break;
+
+ case DRIVER_CHOICE:
+ break;
+
+ case ALWAYS:
+ we_are_interleaving = true;
+ break;
+
+ }
+
+ /* Compute the physical size of the medium. */
+
+ physical_medium_size_in_bytes =
+ physical->chip_count * physical->chip_size;
+
+ /* Compute the logical geometry. */
+
+ if (!we_are_interleaving) {
+
+ /*
+ * At this writing, MTD uses unsigned 32-bit variables to
+ * represent the size of the medium. If the physical medium is
+ * larger than that, the logical medium must be smaller. Here,
+ * we compute the total number of physical blocks in the medium
+ * that we can actually use.
+ */
+
+ if (physical_medium_size_in_bytes <= max_medium_size_in_bytes) {
+ usable_blocks =
+ physical_medium_size_in_bytes >>
+ (ffs(physical->block_size) - 1);
+ } else {
+ usable_blocks =
+ max_medium_size_in_bytes / physical->block_size;
+ }
+
+ /* Set up the logical geometry.
+ *
+ * Notice that the usable medium size is not necessarily the
+ * same as the chip size multiplied by the number of physical
+ * chips. We can't afford to touch the physical chip size
+ * because the NAND Flash MTD code *requires* it to be a power
+ * of 2.
+ */
+
+ logical->chip_count = physical->chip_count;
+ logical->chip_size = physical->chip_size;
+ logical->usable_size = usable_blocks * physical->block_size;
+ logical->block_size = physical->block_size;
+ logical->page_data_size = nfc->page_data_size;
+
+ /* Use the MTD layout that best matches the NFC geometry. */
+
+ logical->mtd_layout = &nfc->mtd_layout;
+ logical->page_oob_size = nfc->mtd_layout.eccbytes +
+ nfc->mtd_layout.oobavail;
+
+ } else {
+
+ /*
+ * If control arrives here, we are interleaving. Specifically,
+ * we are "horizontally concatenating" the pages in all the
+ * physical chips.
+ *
+ * - A logical page will be the size of a physical page
+ * multiplied by the number of physical chips.
+ *
+ * - A logical block will have the same number of pages as a
+ * physical block but, since the logical page size is larger,
+ * the logical block size is larger.
+ *
+ * - The entire medium will appear to be a single chip.
+ *
+ * At this writing, MTD uses unsigned 32-bit variables to
+ * represent the size of the medium. If the physical medium is
+ * larger than that, the logical medium must be smaller.
+ *
+ * The NAND Flash MTD code represents the size of a single chip
+ * as an unsigned 32-bit value. It also *requires* that the size
+ * of a chip be a power of two. Thus, the largest possible chip
+ * size is 2GiB.
+ *
+ * When interleaving, the entire medium appears to be one chip.
+ * Thus, when interleaving, the largest possible medium size is
+ * 2GiB.
+ */
+
+ if (physical_medium_size_in_bytes <= max_medium_size_in_bytes) {
+ logical->chip_size =
+ 0x1 << (fls(physical_medium_size_in_bytes) - 1);
+ } else {
+ logical->chip_size =
+ 0x1 << (fls(max_medium_size_in_bytes) - 1);
+ }
+
+ /*
+ * If control arrives here, we're interleaving. The logical
+ * geometry is very different from the physical geometry.
+ */
+
+ logical->chip_count = 1;
+ logical->usable_size = logical->chip_size;
+ logical->block_size =
+ physical->block_size * physical->chip_count;
+ logical->page_data_size =
+ nfc->page_data_size * physical->chip_count;
+
+ /*
+ * Since the logical geometry doesn't match the physical
+ * geometry, we can't use the MTD layout that matches the
+ * NFC geometry. We synthesize one here.
+ *
+ * Our "logical" OOB will be the concatenation of the first 5
+ * bytes of the "physical" OOB of every chip. This has some
+ * important properties:
+ *
+ * - This will make the block mark of every physical chip
+ * visible (even for small page chips, which put their block
+ * mark in the 5th OOB byte).
+ *
+ * - None of the NFC controllers put ECC in the first 5 OOB
+ * bytes, so this layout exposes no ECC.
+ */
+
+ logical->page_oob_size = 5 * physical->chip_count;
+
+ synthetic_layout.eccbytes = 0;
+ synthetic_layout.oobavail = 5 * physical->chip_count;
+ synthetic_layout.oobfree[0].offset = 0;
+ synthetic_layout.oobfree[0].length = synthetic_layout.oobavail;
+
+ /* Install the synthetic layout. */
+
+ logical->mtd_layout = &synthetic_layout;
+
+ }
+
+ /* Compute some interesting facts. */
+
+ block_size_in_pages = logical->block_size / logical->page_data_size;
+ chip_size_in_pages = logical->chip_size / logical->page_data_size;
+ chip_size_in_blocks = logical->chip_size / logical->block_size;
+ usable_medium_size_in_pages =
+ logical->usable_size / logical->page_data_size;
+ usable_medium_size_in_blocks =
+ logical->usable_size / logical->block_size;
+
+ /* Report. */
+
+ dev_dbg(dev, "----------------\n");
+ dev_dbg(dev, "Logical Geometry\n");
+ dev_dbg(dev, "----------------\n");
+ dev_dbg(dev, "Chip Count : %d\n", logical->chip_count);
+ dev_dbg(dev, "Page Data Size in Bytes: %u (0x%x)\n",
+ logical->page_data_size, logical->page_data_size);
+ dev_dbg(dev, "Page OOB Size in Bytes : %u\n",
+ logical->page_oob_size);
+ dev_dbg(dev, "Block Size in Bytes : %u (0x%x)\n",
+ logical->block_size, logical->block_size);
+ dev_dbg(dev, "Block Size in Pages : %u (0x%x)\n",
+ block_size_in_pages, block_size_in_pages);
+ dev_dbg(dev, "Chip Size in Bytes : %u (0x%x)\n",
+ logical->chip_size, logical->chip_size);
+ dev_dbg(dev, "Chip Size in Pages : %u (0x%x)\n",
+ chip_size_in_pages, chip_size_in_pages);
+ dev_dbg(dev, "Chip Size in Blocks : %u (0x%x)\n",
+ chip_size_in_blocks, chip_size_in_blocks);
+ dev_dbg(dev, "Physical Size in Bytes : %llu (0x%llx)\n",
+ physical_medium_size_in_bytes, physical_medium_size_in_bytes);
+ dev_dbg(dev, "Usable Size in Bytes : %u (0x%x)\n",
+ logical->usable_size, logical->usable_size);
+ dev_dbg(dev, "Usable Size in Pages : %u (0x%x)\n",
+ usable_medium_size_in_pages, usable_medium_size_in_pages);
+ dev_dbg(dev, "Usable Size in Blocks : %u (0x%x)\n",
+ usable_medium_size_in_blocks, usable_medium_size_in_blocks);
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * mal_set_mtd_geometry() - Set up the MTD geometry.
+ *
+ * This function adjusts the owning MTD data structures to match the logical
+ * geometry we've chosen.
+ *
+ * @this: Per-device data.
+ */
+static int mal_set_mtd_geometry(struct imx_nfc_data *this)
+{
+ struct logical_geometry *logical = &this->logical_geometry;
+ struct mtd_info *mtd = &this->mtd;
+ struct nand_chip *nand = &this->nand;
+
+ /* Configure the struct mtd_info. */
+
+ mtd->size = logical->usable_size;
+ mtd->erasesize = logical->block_size;
+ mtd->writesize = logical->page_data_size;
+ mtd->ecclayout = logical->mtd_layout;
+ mtd->oobavail = mtd->ecclayout->oobavail;
+ mtd->oobsize = mtd->ecclayout->oobavail + mtd->ecclayout->eccbytes;
+ mtd->subpage_sft = 0; /* We don't support sub-page writing. */
+
+ /* Configure the struct nand_chip. */
+
+ nand->numchips = logical->chip_count;
+ nand->chipsize = logical->chip_size;
+ nand->page_shift = ffs(logical->page_data_size) - 1;
+ nand->pagemask = (nand->chipsize >> nand->page_shift) - 1;
+ nand->subpagesize = mtd->writesize >> mtd->subpage_sft;
+ nand->phys_erase_shift = ffs(logical->block_size) - 1;
+ nand->bbt_erase_shift = nand->phys_erase_shift;
+ nand->chip_shift = ffs(logical->chip_size) - 1;
+ nand->oob_poi = nand->buffers->databuf+logical->page_data_size;
+ nand->ecc.layout = logical->mtd_layout;
+
+ /* Set up the pattern that describes block marks. */
+
+ if (is_small_page_chip(this))
+ nand->badblock_pattern = &small_page_block_mark_descriptor;
+ else
+ nand->badblock_pattern = &large_page_block_mark_descriptor;
+
+ /* Return success. */
+
+ return 0;
+}
+
+/**
+ * mal_set_geometry() - Set up the medium geometry.
+ *
+ * @this: Per-device data.
+ */
+static int mal_set_geometry(struct imx_nfc_data *this)
+{
+
+ /* Set up the various layers of geometry, in this specific order. */
+
+ if (mal_set_physical_geometry(this))
+ return !0;
+
+ if (mal_set_nfc_geometry(this))
+ return !0;
+
+ if (mal_set_logical_geometry(this))
+ return !0;
+
+ if (mal_set_mtd_geometry(this))
+ return !0;
+
+ /* Return success. */
+
+ return 0;
+
+}
+
+/**
+ * mal_reset() - Resets the given chip.
+ *
+ * This is the fully-generalized reset operation, including support for
+ * interleaving. All reset operations funnel through here.
+ *
+ * @this: Per-device data.
+ * @chip: The logical chip of interest.
+ */
+static void mal_reset(struct imx_nfc_data *this, unsigned chip)
+{
+ int we_are_interleaving;
+ unsigned int start;
+ unsigned int end;
+ unsigned int i;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ add_event("Entering mal_get_status", 1);
+
+ /* Establish some important facts. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+
+ /* Choose the loop bounds. */
+
+ if (we_are_interleaving) {
+ start = 0;
+ end = physical->chip_count;
+ } else {
+ start = chip;
+ end = start + 1;
+ }
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (i = start; i < end; i++) {
+
+ /* Select the current chip. */
+
+ this->nfc->select_chip(this, i);
+
+ /* Reset the current chip. */
+
+ add_event("Resetting...", 0);
+
+ nfc_util_send_cmd(this, NAND_CMD_RESET);
+ nfc_util_wait_for_the_nfc(this, false);
+
+ }
+
+ add_event("Exiting mal_get_status", -1);
+
+}
+
+/**
+ * mal_get_status() - Abstracted status retrieval.
+ *
+ * For media with a single chip, or concatenated chips, the HIL explicitly
+ * addresses a single chip at a time and wants the status from that chip only.
+ *
+ * For interleaved media, we must combine the individual chip states. At this
+ * writing, the NAND MTD system knows about the following bits in status
+ * registers:
+ *
+ * +------------------------+-------+---------+
+ * | | | Combine |
+ * | Macro | Value | With |
+ * +------------------------+-------+---------+
+ * | NAND_STATUS_FAIL | 0x01 | OR |
+ * | NAND_STATUS_FAIL_N1 | 0x02 | OR |
+ * | NAND_STATUS_TRUE_READY | 0x20 | AND |
+ * | NAND_STATUS_READY | 0x40 | AND |
+ * | NAND_STATUS_WP | 0x80 | AND |
+ * +------------------------+-------+---------+
+ *
+ * @this: Per-device data.
+ * @chip: The logical chip of interest.
+ */
+static uint8_t mal_get_status(struct imx_nfc_data *this, unsigned chip)
+{
+ int we_are_interleaving;
+ unsigned int start;
+ unsigned int end;
+ unsigned int i;
+ unsigned int x;
+ unsigned int or_mask;
+ unsigned int and_mask;
+ uint8_t status;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ add_event("Entering mal_get_status", 1);
+
+ /* Establish some important facts. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+
+ /* Compute the masks we need. */
+
+ or_mask = NAND_STATUS_FAIL | NAND_STATUS_FAIL_N1;
+ and_mask = NAND_STATUS_TRUE_READY | NAND_STATUS_READY | NAND_STATUS_WP;
+
+ /* Assume the chip is successful, ready and writeable. */
+
+ status = and_mask & ~or_mask;
+
+ /* Choose the loop bounds. */
+
+ if (we_are_interleaving) {
+ start = 0;
+ end = physical->chip_count;
+ } else {
+ start = chip;
+ end = start + 1;
+ }
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (i = start; i < end; i++) {
+
+ /* Select the current chip. */
+
+ this->nfc->select_chip(this, i);
+
+ /* Get the current chip's status. */
+
+ add_event("Sending the command...", 0);
+
+ nfc_util_send_cmd(this, NAND_CMD_STATUS);
+ nfc_util_wait_for_the_nfc(this, false);
+
+ add_event("Reading the status...", 0);
+
+ x = this->nfc->read_cycle(this);
+
+ /* Fold this chip's status into the combined status. */
+
+ status |= (x & or_mask);
+ status &= (x & and_mask) | or_mask;
+
+ }
+
+ add_event("Exiting mal_get_status", -1);
+
+ return status;
+
+}
+
+/**
+ * mal_read_a_page() - Abstracted page read.
+ *
+ * This function returns the ECC status for the entire read operation. A
+ * positive return value indicates the number of errors that were corrected
+ * (symbol errors for Reed-Solomon hardware engines, bit errors for BCH hardware
+ * engines). A negative return value indicates that the ECC engine failed to
+ * correct all errors and the data is corrupted. A zero return value indicates
+ * there were no errors at all.
+ *
+ * @this: Per-device data.
+ * @use_ecc: Indicates if we're to use ECC.
+ * @chip: The logical chip of interest.
+ * @page: The logical page number to read.
+ * @data: A pointer to the destination data buffer. If this pointer is null,
+ * that indicates the caller doesn't want the data.
+ * @oob: A pointer to the destination OOB buffer. If this pointer is null,
+ * that indicates the caller doesn't want the OOB.
+ */
+static int mal_read_a_page(struct imx_nfc_data *this, int use_ecc,
+ unsigned chip, unsigned page, uint8_t *data, uint8_t *oob)
+{
+ int we_are_interleaving;
+ int use_automatic_op;
+ unsigned int start;
+ unsigned int end;
+ unsigned int current_chip;
+ unsigned int oob_bytes_to_copy;
+ unsigned int data_bytes_to_copy;
+ int status;
+ unsigned int worst_case_ecc_status;
+ int return_value = 0;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct nfc_geometry *nfc = this->nfc_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ add_event("Entering mal_read_a_page", 1);
+
+ /* Establish some important facts. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+ use_automatic_op = !!this->nfc->start_auto_read;
+
+ /* Apply the automatic operation override, if any. */
+
+ switch (this->auto_op_override) {
+
+ case NEVER:
+ use_automatic_op = false;
+ break;
+
+ case DRIVER_CHOICE:
+ break;
+
+ case ALWAYS:
+ if (this->nfc->start_auto_read)
+ use_automatic_op = true;
+ break;
+
+ }
+
+ /* Set up ECC. */
+
+ this->nfc->set_ecc(this, use_ecc);
+
+ /* Check if we're interleaving and set up the loop iterations. */
+
+ if (we_are_interleaving) {
+
+ start = 0;
+ end = physical->chip_count;
+
+ data_bytes_to_copy =
+ this->logical_geometry.page_data_size /
+ this->physical_geometry.chip_count;
+ oob_bytes_to_copy =
+ this->logical_geometry.page_oob_size /
+ this->physical_geometry.chip_count;
+
+ } else {
+
+ start = chip;
+ end = start + 1;
+
+ data_bytes_to_copy = this->logical_geometry.page_data_size;
+ oob_bytes_to_copy = this->logical_geometry.page_oob_size;
+
+ }
+
+ /* If we're using the automatic operation, start it now. */
+
+ if (use_automatic_op) {
+ add_event("Starting the automatic operation...", 0);
+ this->nfc->start_auto_read(this, start, end - start, 0, page);
+ }
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (current_chip = start; current_chip < end; current_chip++) {
+
+ /* Check if we're using the automatic operation. */
+
+ if (use_automatic_op) {
+
+ add_event("Waiting...", 0);
+ this->nfc->wait_for_auto_read(this);
+
+ } else {
+
+ /* Select the current chip. */
+
+ this->nfc->select_chip(this, current_chip);
+
+ /* Set up the chip. */
+
+ add_event("Sending the command and addresses...", 0);
+
+ nfc_util_send_cmd_and_addrs(this,
+ NAND_CMD_READ0, 0, page);
+
+ if (is_large_page_chip(this)) {
+ add_event("Sending the final command...", 0);
+ nfc_util_send_cmd(this, NAND_CMD_READSTART);
+ }
+
+ /* Wait till the page is ready. */
+
+ add_event("Waiting for the page to arrive...", 0);
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ /* Read the page. */
+
+ add_event("Reading the page...", 0);
+
+ this->nfc->read_page(this);
+
+ }
+
+ /* Copy a page out of the NFC. */
+
+ add_event("Copying from the NFC...", 0);
+
+ if (oob) {
+ nfc_util_copy_from_the_nfc(this,
+ nfc->page_data_size, oob, oob_bytes_to_copy);
+ oob += oob_bytes_to_copy;
+ }
+
+ if (data) {
+ nfc_util_copy_from_the_nfc(this,
+ 0, data, data_bytes_to_copy);
+ data += data_bytes_to_copy;
+ }
+
+ /*
+ * If we're using ECC, and we haven't already seen an ECC
+ * failure, continue to gather ECC status. Note that, if we
+ * *do* see an ECC failure, we continue to read because the
+ * client might want the data for forensic purposes.
+ */
+
+ if (use_ecc && (return_value >= 0)) {
+
+ add_event("Getting ECC status...", 0);
+
+ status = this->nfc->get_ecc_status(this);
+
+ if (status >= 0)
+ return_value += status;
+ else
+ return_value = -1;
+
+ }
+
+ /* Check if we're using the automatic operation. */
+
+ if (use_automatic_op) {
+
+ /*
+ * If this is not the last iteration, resume the
+ * automatic operation.
+ */
+
+ if (current_chip < (end - 1)) {
+ add_event("Resuming...", 0);
+ this->nfc->resume_auto_read(this);
+ }
+
+ }
+
+ }
+
+ /* Check if we're supposed to inject an ECC error. */
+
+ if (use_ecc && this->inject_ecc_error) {
+
+ /* Inject the appropriate error. */
+
+ if (this->inject_ecc_error < 0) {
+
+ add_event("Injecting an uncorrectable error...", 0);
+
+ return_value = -1;
+
+ } else {
+
+ add_event("Injecting correctable errors...", 0);
+
+ worst_case_ecc_status =
+ physical->chip_count *
+ nfc->buffer_count *
+ nfc->ecc_strength;
+
+ if (this->inject_ecc_error > worst_case_ecc_status)
+ return_value = worst_case_ecc_status;
+ else
+ return_value = this->inject_ecc_error;
+
+ }
+
+ /* Stop injecting further errors. */
+
+ this->inject_ecc_error = 0;
+
+ }
+
+ /* Return. */
+
+ add_event("Exiting mal_read_a_page", -1);
+
+ return return_value;
+
+}
+
+/**
+ * mal_write_a_page() - Abstracted page write.
+ *
+ * This function returns zero if the operation succeeded, or -EIO if the
+ * operation failed.
+ *
+ * @this: Per-device data.
+ * @use_ecc: Indicates if we're to use ECC.
+ * @chip: The logical chip of interest.
+ * @page: The logical page number to write.
+ * @data: A pointer to the source data buffer.
+ * @oob: A pointer to the source OOB buffer.
+ */
+static int mal_write_a_page(struct imx_nfc_data *this, int use_ecc,
+ unsigned chip, unsigned page, const uint8_t *data, const uint8_t *oob)
+{
+ int we_are_interleaving;
+ int use_automatic_op;
+ unsigned int start;
+ unsigned int end;
+ unsigned int current_chip;
+ unsigned int oob_bytes_to_copy;
+ unsigned int data_bytes_to_copy;
+ int return_value = 0;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct nfc_geometry *nfc = this->nfc_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ add_event("Entering mal_write_a_page", 1);
+
+ /* Establish some important facts. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+ use_automatic_op = !!this->nfc->start_auto_write;
+
+ /* Apply the automatic operation override, if any. */
+
+ switch (this->auto_op_override) {
+
+ case NEVER:
+ use_automatic_op = false;
+ break;
+
+ case DRIVER_CHOICE:
+ break;
+
+ case ALWAYS:
+ if (this->nfc->start_auto_write)
+ use_automatic_op = true;
+ break;
+
+ }
+
+ /* Set up ECC. */
+
+ this->nfc->set_ecc(this, use_ecc);
+
+ /* Check if we're interleaving and set up the loop iterations. */
+
+ if (we_are_interleaving) {
+
+ start = 0;
+ end = physical->chip_count;
+
+ data_bytes_to_copy =
+ this->logical_geometry.page_data_size /
+ this->physical_geometry.chip_count;
+ oob_bytes_to_copy =
+ this->logical_geometry.page_oob_size /
+ this->physical_geometry.chip_count;
+
+ } else {
+
+ start = chip;
+ end = start + 1;
+
+ data_bytes_to_copy = this->logical_geometry.page_data_size;
+ oob_bytes_to_copy = this->logical_geometry.page_oob_size;
+
+ }
+
+ /* If we're using the automatic operation, start the hardware now. */
+
+ if (use_automatic_op) {
+ add_event("Starting the automatic operation...", 0);
+ this->nfc->start_auto_write(this, start, end - start, 0, page);
+ }
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (current_chip = start; current_chip < end; current_chip++) {
+
+ /* Copy a page into the NFC. */
+
+ add_event("Copying to the NFC...", 0);
+
+ nfc_util_copy_to_the_nfc(this, oob, nfc->page_data_size,
+ oob_bytes_to_copy);
+ oob += oob_bytes_to_copy;
+
+ nfc_util_copy_to_the_nfc(this, data, 0, data_bytes_to_copy);
+
+ data += data_bytes_to_copy;
+
+ /* Check if we're using the automatic operation. */
+
+ if (use_automatic_op) {
+
+ /* Wait for the write operation to finish. */
+
+ add_event("Waiting...", 0);
+
+ this->nfc->wait_for_auto_write(this);
+
+ } else {
+
+ /* Select the current chip. */
+
+ this->nfc->select_chip(this, current_chip);
+
+ /* Set up the chip. */
+
+ add_event("Sending the command and addresses...", 0);
+
+ nfc_util_send_cmd_and_addrs(this,
+ NAND_CMD_SEQIN, 0, page);
+
+ /* Send the page. */
+
+ add_event("Sending the page...", 0);
+
+ this->nfc->send_page(this);
+
+ /* Start programming the page. */
+
+ add_event("Programming the page...", 0);
+
+ nfc_util_send_cmd(this, NAND_CMD_PAGEPROG);
+
+ /* Wait until the page is finished. */
+
+ add_event("Waiting...", 0);
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ }
+
+ }
+
+ /* Get status. */
+
+ add_event("Gathering status...", 0);
+
+ if (mal_get_status(this, chip) & NAND_STATUS_FAIL) {
+ add_event("Bad status", 0);
+ return_value = -EIO;
+ } else {
+ add_event("Good status", 0);
+ }
+
+ /* Return. */
+
+ add_event("Exiting mal_write_a_page", -1);
+
+ return return_value;
+
+}
+
+/**
+ * mal_erase_a_block() - Abstract block erase operation.
+ *
+ * Note that this function does *not* wait for the operation to finish. The
+ * caller is expected to call waitfunc() at some later time.
+ *
+ * @this: Per-device data.
+ * @chip: The logical chip of interest.
+ * @page: A logical page address that identifies the block to erase.
+ */
+static void mal_erase_a_block(struct imx_nfc_data *this,
+ unsigned chip, unsigned page)
+{
+ int we_are_interleaving;
+ int use_automatic_op;
+ unsigned int start;
+ unsigned int end;
+ unsigned int i;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ add_event("Entering mal_erase_a_block", 1);
+
+ /* Establish some important facts. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+ use_automatic_op = !!this->nfc->start_auto_erase;
+
+ /* Apply the automatic operation override, if any. */
+
+ switch (this->auto_op_override) {
+
+ case NEVER:
+ use_automatic_op = false;
+ break;
+
+ case DRIVER_CHOICE:
+ break;
+
+ case ALWAYS:
+ if (this->nfc->start_auto_erase)
+ use_automatic_op = true;
+ break;
+
+ }
+
+ /* Choose the loop bounds. */
+
+ if (we_are_interleaving) {
+ start = 0;
+ end = physical->chip_count;
+ } else {
+ start = chip;
+ end = start + 1;
+ }
+
+ /* Check if we're using the automatic operation. */
+
+ if (use_automatic_op) {
+
+ /*
+ * Start the operation. Note that we don't wait for it to
+ * finish because the HIL will call our waitfunc().
+ */
+
+ add_event("Starting the automatic operation...", 0);
+
+ this->nfc->start_auto_erase(this, start, end - start, page);
+
+ } else {
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (i = start; i < end; i++) {
+
+ /* Select the current chip. */
+
+ this->nfc->select_chip(this, i);
+
+ /* Set up the chip. */
+
+ nfc_util_send_cmd_and_addrs(this,
+ NAND_CMD_ERASE1, -1, page);
+
+ /* Start the erase. */
+
+ nfc_util_send_cmd(this, NAND_CMD_ERASE2);
+
+ /*
+ * If this is the last time through the loop, break out
+ * now so we don't try to wait (the HIL will call our
+ * waitfunc() for the final wait).
+ */
+
+ if (i >= (end - 1))
+ break;
+
+ /* Wait for the erase on the current chip to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ }
+
+ }
+
+ add_event("Exiting mal_erase_a_block", -1);
+
+}
+
+/**
+ * mal_is_block_bad() - Abstract bad block check.
+ *
+ * @this: Per-device data.
+ * @chip: The logical chip of interest.
+ * @page: The logical page number to read.
+ */
+ #if 0
+
+/* TODO: Finish this function and plug it in. */
+
+static int mal_is_block_bad(struct imx_nfc_data *this,
+ unsigned chip, unsigned page)
+{
+ int we_are_interleaving;
+ unsigned int start;
+ unsigned int end;
+ unsigned int i;
+ uint8_t *p;
+ int return_value = 0;
+ struct nand_chip *nand = &this->nand;
+ struct physical_geometry *physical = &this->physical_geometry;
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ /* Figure out if we're interleaving. */
+
+ we_are_interleaving = logical->chip_count != physical->chip_count;
+
+ /*
+ * We're about to use the NAND Flash MTD layer's buffer, so invalidate
+ * the page cache.
+ */
+
+ this->nand.pagebuf = -1;
+
+ /*
+ * Read the OOB of the given page, using the NAND Flash MTD's buffer.
+ *
+ * Notice that ECC is off, which it *must* be when scanning block marks.
+ */
+
+ mal_read_a_page(this, false,
+ this->current_chip, this->page_address, 0, nand->oob_poi);
+
+ /* Choose the loop bounds. */
+
+ if (we_are_interleaving) {
+ start = 0;
+ end = physical->chip_count;
+ } else {
+ start = chip;
+ end = start + 1;
+ }
+
+ /* Start scanning at the beginning of the OOB data. */
+
+ p = nand->oob_poi;
+
+ /* Loop over physical chips. */
+
+ add_event("Looping over physical chips...", 0);
+
+ for (i = start; i < end; i++, p += 5) {
+
+ /* Examine the OOB for this chip. */
+
+ if (p[nand->badblockpos] != 0xff) {
+ return_value = !0;
+ break;
+ }
+
+ }
+
+ /* Return. */
+
+ return return_value;
+
+}
+#endif
+
+/**
+ * mil_init() - Initializes the MTD Interface Layer.
+ *
+ * @this: Per-device data.
+ */
+static void mil_init(struct imx_nfc_data *this)
+{
+ this->current_chip = -1; /* No chip is selected yet. */
+ this->command_is_new = false; /* No command yet. */
+}
+
+/**
+ * mil_cmd_ctrl() - MTD Interface cmd_ctrl()
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @dat: The data signals to present to the chip.
+ * @ctrl: The control signals to present to the chip.
+ */
+static void mil_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_dev_ready() - MTD Interface dev_ready()
+ *
+ * @mtd: A pointer to the owning MTD.
+ */
+static int mil_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc dev_ready]\n");
+
+ add_event("Entering mil_dev_ready", 1);
+
+ if (this->nfc->is_ready(this)) {
+ add_event("Exiting mil_dev_ready - Returning ready", -1);
+ return !0;
+ } else {
+ add_event("Exiting mil_dev_ready - Returning busy", -1);
+ return 0;
+ }
+
+}
+
+/**
+ * mil_select_chip() - MTD Interface select_chip()
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @chip: The chip number to select, or -1 to select no chip.
+ */
+static void mil_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc select_chip] chip: %d\n", chip);
+
+ /* Figure out what kind of transition this is. */
+
+ if ((this->current_chip < 0) && (chip >= 0)) {
+ start_event_trace("Entering mil_select_chip");
+ if (this->pdata->force_ce)
+ this->nfc->set_force_ce(this, true);
+ clk_enable(this->clock);
+ add_event("Exiting mil_select_chip", -1);
+ } else if ((this->current_chip >= 0) && (chip < 0)) {
+ add_event("Entering mil_select_chip", 1);
+ if (this->pdata->force_ce)
+ this->nfc->set_force_ce(this, false);
+ clk_disable(this->clock);
+ stop_event_trace("Exiting mil_select_chip");
+ } else {
+ add_event("Entering mil_select_chip", 1);
+ add_event("Exiting mil_select_chip", -1);
+ }
+
+ this->current_chip = chip;
+
+}
+
+/**
+ * mil_cmdfunc() - MTD Interface cmdfunc()
+ *
+ * This function handles NAND Flash command codes from the HIL. Since only the
+ * HIL calls this function (none of the reference implementations we use do), it
+ * needs to handle very few command codes.
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @command: The command code.
+ * @column: The column address associated with this command code, or -1 if no
+ * column address applies.
+ * @page: The page address associated with this command code, or -1 if no
+ * page address applies.
+ */
+static void mil_cmdfunc(struct mtd_info *mtd,
+ unsigned command, int column, int page)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc cmdfunc] command: 0x%02x, "
+ "column: 0x%04x, page: 0x%06x\n", command, column, page);
+
+ add_event("Entering mil_cmdfunc", 1);
+
+ /* Record the command and the fact that it hasn't yet been sent. */
+
+ this->command = command;
+ this->command_is_new = true;
+
+ /*
+ * Process the command code.
+ *
+ * Note the default case to trap unrecognized codes. Thus, every command
+ * we support must have a case here, even if we don't have to do any
+ * pre-processing work. If the HIL changes and starts sending commands
+ * we haven't explicitly implemented, this will warn us.
+ */
+
+ switch (command) {
+
+ case NAND_CMD_READ0:
+ add_event("NAND_CMD_READ0", 0);
+ /*
+ * After calling this function to send the command and
+ * addresses, the HIL will call ecc.read_page() or
+ * ecc.read_page_raw() to collect the data.
+ *
+ * The column address from the HIL is always zero. The only
+ * information we need to keep from this call is the page
+ * address.
+ */
+ this->page_address = page;
+ break;
+
+ case NAND_CMD_STATUS:
+ add_event("NAND_CMD_STATUS", 0);
+ /*
+ * After calling this function to send the command, the HIL
+ * will call read_byte() once to collect the status.
+ */
+ break;
+
+ case NAND_CMD_READID:
+ add_event("NAND_CMD_READID", 0);
+ /*
+ * After calling this function to send the command, the HIL
+ * will call read_byte() repeatedly to collect ID bytes.
+ */
+ break;
+
+ case NAND_CMD_RESET:
+ add_event("NAND_CMD_RESET", 0);
+ mal_reset(this, this->current_chip);
+ break;
+
+ default:
+ dev_emerg(this->dev, "Unsupported NAND Flash command code: "
+ "0x%02x\n", command);
+ BUG();
+ break;
+
+ }
+
+ add_event("Exiting mil_cmdfunc", -1);
+
+}
+
+/**
+ * mil_waitfunc() - MTD Interface waifunc()
+ *
+ * This function blocks until the current chip is ready and then returns the
+ * contents of the chip's status register. The HIL only calls this function
+ * after starting an erase operation.
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ */
+static int mil_waitfunc(struct mtd_info *mtd, struct nand_chip *nand)
+{
+ int status;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc waitfunc]\n");
+
+ add_event("Entering mil_waitfunc", 1);
+
+ /* Wait for the NFC to finish. */
+
+ nfc_util_wait_for_the_nfc(this, true);
+
+ /* Get the status. */
+
+ status = mal_get_status(this, this->current_chip);
+
+ add_event("Exiting mil_waitfunc", -1);
+
+ return status;
+
+}
+
+/**
+ * mil_read_byte() - MTD Interface read_byte().
+ *
+ * @mtd: A pointer to the owning MTD.
+ */
+static uint8_t mil_read_byte(struct mtd_info *mtd)
+{
+ uint8_t byte = 0;
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ add_event("Entering mil_read_byte", 1);
+
+ /*
+ * The command sent by the HIL before it called this function determines
+ * how we get the byte we're going to return.
+ */
+
+ switch (this->command) {
+
+ case NAND_CMD_STATUS:
+ add_event("NAND_CMD_STATUS", 0);
+ byte = mal_get_status(this, this->current_chip);
+ break;
+
+ case NAND_CMD_READID:
+ add_event("NAND_CMD_READID", 0);
+
+ /*
+ * Check if the command is new. If so, then the HIL just
+ * recently called cmdfunc(), so the current chip isn't selected
+ * and the command hasn't been sent to the chip.
+ */
+
+ if (this->command_is_new) {
+ add_event("Sending the \"Read ID\" command...", 0);
+ this->nfc->select_chip(this, this->current_chip);
+ nfc_util_send_cmd_and_addrs(this,
+ NAND_CMD_READID, 0, -1);
+ this->command_is_new = false;
+ }
+
+ /* Read the ID byte. */
+
+ add_event("Reading the ID byte...", 0);
+
+ byte = this->nfc->read_cycle(this);
+
+ break;
+
+ default:
+ dev_emerg(this->dev, "Unsupported NAND Flash command code: "
+ "0x%02x\n", this->command);
+ BUG();
+ break;
+
+ }
+
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "[imx_nfc read_byte] Returning: 0x%02x\n", byte);
+
+ add_event("Exiting mil_read_byte", -1);
+
+ return byte;
+
+}
+
+/**
+ * mil_read_word() - MTD Interface read_word().
+ *
+ * @mtd: A pointer to the owning MTD.
+ */
+static uint16_t mil_read_word(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+ return 0;
+}
+
+/**
+ * mil_read_buf() - MTD Interface read_buf().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @buf: The destination buffer.
+ * @len: The number of bytes to read.
+ */
+static void mil_read_buf(struct mtd_info *mtd, uint8_t * buf, int len)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_write_buf() - MTD Interface write_buf().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @buf: The source buffer.
+ * @len: The number of bytes to read.
+ */
+static void mil_write_buf(struct mtd_info *mtd, const uint8_t * buf, int len)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_verify_buf() - MTD Interface verify_buf().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @buf: The destination buffer.
+ * @len: The number of bytes to read.
+ */
+static int mil_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+ return 0;
+}
+
+/**
+ * mil_ecc_hwctl() - MTD Interface ecc.hwctl().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @mode: The ECC mode.
+ */
+static void mil_ecc_hwctl(struct mtd_info *mtd, int mode)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_ecc_calculate() - MTD Interface ecc.calculate().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @dat: A pointer to the source data.
+ * @ecc_code: A pointer to a buffer that will receive the resulting ECC.
+ */
+static int mil_ecc_calculate(struct mtd_info *mtd,
+ const uint8_t *dat, uint8_t *ecc_code)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+ return 0;
+}
+
+/**
+ * mil_ecc_correct() - MTD Interface ecc.correct().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @dat: A pointer to the source data.
+ * @read_ecc: A pointer to the ECC that was read from the medium.
+ * @calc_ecc: A pointer to the ECC that was calculated for the source data.
+ */
+static int mil_ecc_correct(struct mtd_info *mtd,
+ uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+ return 0;
+}
+
+/**
+ * mil_ecc_read_page() - MTD Interface ecc.read_page().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @buf: A pointer to the destination buffer.
+ */
+static int mil_ecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *nand, uint8_t *buf)
+{
+ int ecc_status;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_read_page]\n");
+
+ add_event("Entering mil_ecc_read_page", 1);
+
+ /* Read the page. */
+
+ ecc_status =
+ mal_read_a_page(this, true, this->current_chip,
+ this->page_address, buf, nand->oob_poi);
+
+ /* Propagate ECC information. */
+
+ if (ecc_status < 0) {
+ add_event("ECC Failure", 0);
+ mtd->ecc_stats.failed++;
+ } else if (ecc_status > 0) {
+ add_event("ECC Corrections", 0);
+ mtd->ecc_stats.corrected += ecc_status;
+ }
+
+ add_event("Exiting mil_ecc_read_page", -1);
+
+ return 0;
+
+}
+
+/**
+ * mil_ecc_read_page_raw() - MTD Interface ecc.read_page_raw().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @buf: A pointer to the destination buffer.
+ */
+static int mil_ecc_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *nand, uint8_t *buf)
+{
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_read_page_raw]\n");
+
+ add_event("Entering mil_ecc_read_page_raw", 1);
+
+ mal_read_a_page(this, false, this->current_chip,
+ this->page_address, buf, nand->oob_poi);
+
+ add_event("Exiting mil_ecc_read_page_raw", -1);
+
+ return 0;
+
+}
+
+/**
+ * mil_ecc_write_page() - MTD Interface ecc.write_page().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @buf: A pointer to the source buffer.
+ */
+static void mil_ecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *nand, const uint8_t *buf)
+{
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_ecc_write_page_raw() - MTD Interface ecc.write_page_raw().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @buf: A pointer to the source buffer.
+ */
+static void mil_ecc_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *nand, const uint8_t *buf)
+{
+ struct imx_nfc_data *this = nand->priv;
+ unimplemented(this, __func__);
+}
+
+/**
+ * mil_write_page() - MTD Interface ecc.write_page().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @buf: A pointer to the source buffer.
+ * @page: The page number to write.
+ * @cached: Indicates cached programming (ignored).
+ * @raw: Indicates not to use ECC.
+ */
+static int mil_write_page(struct mtd_info *mtd,
+ struct nand_chip *nand, const uint8_t *buf,
+ int page, int cached, int raw)
+{
+ int return_value;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc write_page]\n");
+
+ add_event("Entering mil_write_page", 1);
+
+ return_value = mal_write_a_page(this, !raw,
+ this->current_chip, page, buf, nand->oob_poi);
+
+ add_event("Exiting mil_write_page", -1);
+
+ return return_value;
+
+}
+
+/**
+ * mil_ecc_read_oob() - MTD Interface read_oob().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @page: The page number to read.
+ * @sndcmd: Indicates this function should send a command to the chip before
+ * reading the out-of-band bytes. This is only false for small page
+ * chips that support auto-increment.
+ */
+static int mil_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
+ int page, int sndcmd)
+{
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_read_oob] "
+ "page: 0x%06x, sndcmd: %s\n", page, sndcmd ? "Yes" : "No");
+
+ add_event("Entering mil_ecc_read_oob", 1);
+
+ mal_read_a_page(this, false,
+ this->current_chip, page, 0, nand->oob_poi);
+
+ add_event("Exiting mil_ecc_read_oob", -1);
+
+ /*
+ * Return true, indicating that the next call to this function must send
+ * a command.
+ */
+
+ return true;
+
+}
+
+/**
+ * mil_ecc_write_oob() - MTD Interface write_oob().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @nand: A pointer to the owning NAND Flash MTD.
+ * @page: The page number to write.
+ */
+static int mil_ecc_write_oob(struct mtd_info *mtd,
+ struct nand_chip *nand, int page)
+{
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc ecc_write_oob] page: 0x%06x\n", page);
+
+ /*
+ * There are fundamental incompatibilities between the i.MX NFC and the
+ * NAND Flash MTD model that make it essentially impossible to write the
+ * out-of-band bytes.
+ */
+
+ dev_emerg(this->dev, "This driver doesn't support writing the OOB\n");
+ WARN_ON(1);
+
+ /* Return status. */
+
+ return -EIO;
+
+}
+
+/**
+ * mil_erase_cmd() - MTD Interface erase_cmd().
+ *
+ * We set the erase_cmd pointer in struct nand_chip to point to this function.
+ * Thus, the HIL will call here for all erase operations.
+ *
+ * Strictly speaking, since the erase_cmd pointer is marked "Internal," we
+ * shouldn't be doing this. However, the only reason the HIL uses that pointer
+ * is to install a different function for erasing conventional NAND Flash or AND
+ * Flash. Since AND Flash is obsolete and we don't support it, this isn't
+ * important.
+ *
+ * Furthermore, to cleanly implement interleaving (which is critical to speeding
+ * up erase operations), we want to "hook into" the operation at the highest
+ * semantic level possible. If we don't hook this function, then the only way
+ * we'll know that an erase is happening is when the HIL calls cmdfunc() with
+ * an erase command. Implementing interleaving at that level is roughly a
+ * billion times less desirable.
+ *
+ * This function does *not* wait for the operation to finish. The HIL will call
+ * waitfunc() later to wait for the operation to finish.
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @page: A page address that identifies the block to erase.
+ */
+static void mil_erase_cmd(struct mtd_info *mtd, int page)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc erase_cmd] page: 0x%06x\n", page);
+
+ add_event("Entering mil_erase_cmd", 1);
+
+ mal_erase_a_block(this, this->current_chip, page);
+
+ add_event("Exiting mil_erase_cmd", -1);
+
+}
+
+/**
+ * mil_block_bad() - MTD Interface block_bad().
+ *
+ * @mtd: A pointer to the owning MTD.
+ * @ofs: The offset of the block of interest, from the start of the medium.
+ * @getchip: Indicates this function must acquire the MTD before using it.
+ */
+#if 0
+
+/* TODO: Finish this function and plug it in. */
+
+static int mil_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+ unsigned int chip;
+ unsigned int page;
+ int return_value;
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc block_bad] page: 0x%06x\n", page);
+
+ add_event("Entering mil_block_bad", 1);
+
+ /* Compute the logical chip number that contains the given offset. */
+
+ chip = (unsigned int) (ofs >> nand->chip_shift);
+
+ /* Compute the logical page address within the logical chip. */
+
+ page = ((unsigned int) (ofs >> nand->page_shift)) & nand->pagemask;
+
+ /* Check if the block is bad. */
+
+ return_value = mal_is_block_bad(this, chip, page);
+
+ if (return_value)
+ add_event("Bad block", 0);
+
+ /* Return. */
+
+ add_event("Exiting mil_block_bad", -1);
+
+ return return_value;
+
+}
+#endif
+
+/**
+ * mil_scan_bbt() - MTD Interface scan_bbt().
+ *
+ * The HIL calls this function once, when it initializes the NAND Flash MTD.
+ *
+ * Nominally, the purpose of this function is to look for or create the bad
+ * block table. In fact, since the HIL calls this function at the very end of
+ * the initialization process started by nand_scan(), and the HIL doesn't have a
+ * more formal mechanism, everyone "hooks" this function to continue the
+ * initialization process.
+ *
+ * At this point, the physical NAND Flash chips have been identified and
+ * counted, so we know the physical geometry. This enables us to make some
+ * important configuration decisions.
+ *
+ * The return value of this function propogates directly back to this driver's
+ * call to nand_scan(). Anything other than zero will cause this driver to
+ * tear everything down and declare failure.
+ *
+ * @mtd: A pointer to the owning MTD.
+ */
+static int mil_scan_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd->priv;
+ struct imx_nfc_data *this = nand->priv;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "[imx_nfc scan_bbt] \n");
+
+ add_event("Entering mil_scan_bbt", 1);
+
+ /*
+ * We replace the erase_cmd() function that the MTD NAND Flash system
+ * has installed with our own. See mil_erase_cmd() for the reasons.
+ */
+
+ nand->erase_cmd = mil_erase_cmd;
+
+ /*
+ * Tell MTD users that the out-of-band area can't be written.
+ *
+ * This flag is not part of the standard kernel source tree. It comes
+ * from a patch that touches both MTD and JFFS2.
+ *
+ * The problem is that, without this patch, JFFS2 believes it can write
+ * the data area and the out-of-band area separately. This is wrong for
+ * two reasons:
+ *
+ * 1) Our NFC distributes out-of-band bytes throughout the page,
+ * intermingled with the data, and covered by the same ECC.
+ * Thus, it's not possible to write the out-of-band bytes and
+ * data bytes separately.
+ *
+ * 2) Large page (MLC) Flash chips don't support partial page
+ * writes. You must write the entire page at a time. Thus, even
+ * if our NFC didn't force you to write out-of-band and data
+ * bytes together, it would *still* be a bad idea to do
+ * otherwise.
+ */
+
+ mtd->flags &= ~MTD_OOB_WRITEABLE;
+
+ /* Set up geometry. */
+
+ mal_set_geometry(this);
+
+ /* We use the reference implementation for bad block management. */
+
+ add_event("Exiting mil_scan_bbt", -1);
+
+ return nand_scan_bbt(mtd, nand->badblock_pattern);
+
+}
+
+/**
+ * parse_bool_param() - Parses the value of a boolean parameter string.
+ *
+ * @s: The string to parse.
+ */
+static int parse_bool_param(const char *s)
+{
+
+ if (!strcmp(s, "1") || !strcmp(s, "on") ||
+ !strcmp(s, "yes") || !strcmp(s, "true")) {
+ return 1;
+ } else if (!strcmp(s, "0") || !strcmp(s, "off") ||
+ !strcmp(s, "no") || !strcmp(s, "false")) {
+ return 0;
+ } else {
+ return -1;
+ }
+
+}
+
+/**
+ * set_module_enable() - Controls whether this driver is enabled.
+ *
+ * Note that this state can be controlled from the command line. Disabling this
+ * driver is sometimes useful for debugging.
+ *
+ * @s: The new value of the parameter.
+ * @kp: The owning kernel parameter.
+ */
+static int set_module_enable(const char *s, struct kernel_param *kp)
+{
+
+ switch (parse_bool_param(s)) {
+
+ case 1:
+ imx_nfc_module_enable = true;
+ break;
+
+ case 0:
+ imx_nfc_module_enable = false;
+ break;
+
+ default:
+ return -EINVAL;
+ break;
+
+ }
+
+ return 0;
+
+}
+
+/**
+ * get_module_enable() - Indicates whether this driver is enabled.
+ *
+ * @p: A pointer to a (small) buffer that will receive the response.
+ * @kp: The owning kernel parameter.
+ */
+static int get_module_enable(char *p, struct kernel_param *kp)
+{
+ p[0] = imx_nfc_module_enable ? '1' : '0';
+ p[1] = 0;
+ return 1;
+}
+
+#ifdef EVENT_REPORTING
+
+/**
+ * set_module_report_events() - Controls whether this driver reports events.
+ *
+ * @s: The new value of the parameter.
+ * @kp: The owning kernel parameter.
+ */
+static int set_module_report_events(const char *s, struct kernel_param *kp)
+{
+
+ switch (parse_bool_param(s)) {
+
+ case 1:
+ imx_nfc_module_report_events = true;
+ break;
+
+ case 0:
+ imx_nfc_module_report_events = false;
+ reset_event_trace();
+ break;
+
+ default:
+ return -EINVAL;
+ break;
+
+ }
+
+ return 0;
+
+}
+
+/**
+ * get_module_report_events() - Indicates whether the driver reports events.
+ *
+ * @p: A pointer to a (small) buffer that will receive the response.
+ * @kp: The owning kernel parameter.
+ */
+static int get_module_report_events(char *p, struct kernel_param *kp)
+{
+ p[0] = imx_nfc_module_report_events ? '1' : '0';
+ p[1] = 0;
+ return 1;
+}
+
+/**
+ * set_module_dump_events() - Forces the driver to dump current events.
+ *
+ * @s: The new value of the parameter.
+ * @kp: The owning kernel parameter.
+ */
+static int set_module_dump_events(const char *s, struct kernel_param *kp)
+{
+ dump_event_trace();
+ return 0;
+}
+
+#endif /*EVENT_REPORTING*/
+
+/**
+ * set_module_interleave_override() - Controls the interleave override.
+ *
+ * @s: The new value of the parameter.
+ * @kp: The owning kernel parameter.
+ */
+static int set_module_interleave_override(const char *s,
+ struct kernel_param *kp)
+{
+
+ if (!strcmp(s, "-1"))
+ imx_nfc_module_interleave_override = NEVER;
+ else if (!strcmp(s, "0"))
+ imx_nfc_module_interleave_override = DRIVER_CHOICE;
+ else if (!strcmp(s, "1"))
+ imx_nfc_module_interleave_override = ALWAYS;
+ else
+ return -EINVAL;
+
+ return 0;
+
+}
+
+/**
+ * get_module_interleave_override() - Indicates the interleave override state.
+ *
+ * @p: A pointer to a (small) buffer that will receive the response.
+ * @kp: The owning kernel parameter.
+ */
+static int get_module_interleave_override(char *p, struct kernel_param *kp)
+{
+ return sprintf(p, "%d", imx_nfc_module_interleave_override);
+}
+
+/**
+ * set_force_bytewise_copy() - Controls forced bytewise copy from/to the NFC.
+ *
+ * @s: The new value of the parameter.
+ * @kp: The owning kernel parameter.
+ */
+static int set_module_force_bytewise_copy(const char *s,
+ struct kernel_param *kp)
+{
+
+ switch (parse_bool_param(s)) {
+
+ case 1:
+ imx_nfc_module_force_bytewise_copy = true;
+ break;
+
+ case 0:
+ imx_nfc_module_force_bytewise_copy = false;
+ break;
+
+ default:
+ return -EINVAL;
+ break;
+
+ }
+
+ return 0;
+
+}
+
+/**
+ * get_force_bytewise_copy() - Indicates whether bytewise copy is being forced.
+ *
+ * @p: A pointer to a (small) buffer that will receive the response.
+ * @kp: The owning kernel parameter.
+ */
+static int get_module_force_bytewise_copy(char *p, struct kernel_param *kp)
+{
+ p[0] = imx_nfc_module_force_bytewise_copy ? '1' : '0';
+ p[1] = 0;
+ return 1;
+}
+
+/* Module attributes that appear in sysfs. */
+
+module_param_call(enable, set_module_enable, get_module_enable, 0, 0444);
+MODULE_PARM_DESC(enable, "enables/disables probing");
+
+#ifdef EVENT_REPORTING
+module_param_call(report_events,
+ set_module_report_events, get_module_report_events, 0, 0644);
+MODULE_PARM_DESC(report_events, "enables/disables event reporting");
+
+module_param_call(dump_events, set_module_dump_events, 0, 0, 0644);
+MODULE_PARM_DESC(dump_events, "forces current event dump");
+#endif
+
+module_param_call(interleave_override, set_module_interleave_override,
+ get_module_interleave_override, 0, 0444);
+MODULE_PARM_DESC(interleave_override, "overrides interleaving choice");
+
+module_param_call(force_bytewise_copy, set_module_force_bytewise_copy,
+ get_module_force_bytewise_copy, 0, 0644);
+MODULE_PARM_DESC(force_bytewise_copy, "forces bytewise copy from/to NFC");
+
+/**
+ * show_device_platform_info() - Shows the device's platform information.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_platform_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int o = 0;
+ unsigned int i;
+ void *buffer_base;
+ void *primary_base;
+ void *secondary_base;
+ unsigned int interrupt_number;
+ struct resource *r;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct platform_device *pdev = this->pdev;
+ struct imx_nfc_platform_data *pdata = this->pdata;
+ struct mtd_partition *partition;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_BUFFERS_ADDR_RES_NAME);
+
+ buffer_base = (void *) r->start;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME);
+
+ primary_base = (void *) r->start;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);
+
+ secondary_base = (void *) r->start;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ IMX_NFC_INTERRUPT_RES_NAME);
+
+ interrupt_number = r->start;
+
+ o += sprintf(buf,
+ "NFC Major Version : %u\n"
+ "NFC Minor Version : %u\n"
+ "Force CE : %s\n"
+ "Target Cycle in ns : %u\n"
+ "Clock Name : %s\n"
+ "Interleave : %s\n"
+ "Buffer Base : 0x%p\n"
+ "Primary Registers Base : 0x%p\n"
+ "Secondary Registers Base: 0x%p\n"
+ "Interrupt Number : %u\n"
+ ,
+ pdata->nfc_major_version,
+ pdata->nfc_minor_version,
+ pdata->force_ce ? "Yes" : "No",
+ pdata->target_cycle_in_ns,
+ pdata->clock_name,
+ pdata->interleave ? "Yes" : "No",
+ buffer_base,
+ primary_base,
+ secondary_base,
+ interrupt_number
+ );
+
+ #ifdef CONFIG_MTD_PARTITIONS
+
+ o += sprintf(buf + o,
+ "Partition Count : %u\n"
+ ,
+ pdata->partition_count
+ );
+
+ /* Loop over partitions. */
+
+ for (i = 0; i < pdata->partition_count; i++) {
+
+ partition = pdata->partitions + i;
+
+ o += sprintf(buf+o, " [%d]\n", i);
+ o += sprintf(buf+o, " Name : %s\n", partition->name);
+
+ switch (partition->offset) {
+
+ case MTDPART_OFS_NXTBLK:
+ o += sprintf(buf+o, " Offset: "
+ "MTDPART_OFS_NXTBLK\n");
+ break;
+ case MTDPART_OFS_APPEND:
+ o += sprintf(buf+o, " Offset: "
+ "MTDPART_OFS_APPEND\n");
+ break;
+ default:
+ o += sprintf(buf+o, " Offset: %u (%u MiB)\n",
+ partition->offset,
+ partition->offset / (1024 * 1024));
+ break;
+
+ }
+
+ if (partition->size == MTDPART_SIZ_FULL) {
+ o += sprintf(buf+o, " Size : "
+ "MTDPART_SIZ_FULL\n");
+ } else {
+ o += sprintf(buf+o, " Size : %u (%u MiB)\n",
+ partition->size,
+ partition->size / (1024 * 1024));
+ }
+
+ }
+
+ #endif
+
+ return o;
+
+}
+
+/**
+ * show_device_physical_geometry() - Shows the physical Flash device geometry.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_physical_geometry(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct physical_geometry *physical = &this->physical_geometry;
+
+ return sprintf(buf,
+ "Chip Count : %u\n"
+ "Chip Size in Bytes : %llu\n"
+ "Block Size in Bytes : %u\n"
+ "Page Data Size in Bytes: %u\n"
+ "Page OOB Size in Bytes : %u\n"
+ ,
+ physical->chip_count,
+ physical->chip_size,
+ physical->block_size,
+ physical->page_data_size,
+ physical->page_oob_size
+ );
+
+}
+
+/**
+ * show_device_nfc_info() - Shows the NFC-specific information.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_nfc_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long parent_clock_rate_in_hz;
+ unsigned long clock_rate_in_hz;
+ struct clk *parent_clock;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct nfc_hal *nfc = this->nfc;
+
+ parent_clock = clk_get_parent(this->clock);
+ parent_clock_rate_in_hz = clk_get_rate(parent_clock);
+ clock_rate_in_hz = clk_get_rate(this->clock);
+
+ return sprintf(buf,
+ "Major Version : %u\n"
+ "Minor Version : %u\n"
+ "Max Chip Count : %u\n"
+ "Max Buffer Count : %u\n"
+ "Spare Buffer Stride : %u\n"
+ "Has Secondary Registers : %s\n"
+ "Can Be Symmetric : %s\n"
+ "Exposes Ready/Busy : %s\n"
+ "Parent Clock Rate in Hz : %lu\n"
+ "Clock Rate in Hz : %lu\n"
+ "Symmetric Clock : %s\n"
+ ,
+ nfc->major_version,
+ nfc->minor_version,
+ nfc->max_chip_count,
+ nfc->max_buffer_count,
+ nfc->spare_buf_stride,
+ nfc->has_secondary_regs ? "Yes" : "No",
+ nfc->can_be_symmetric ? "Yes" : "No",
+ nfc->is_ready ? "Yes" : "No",
+ parent_clock_rate_in_hz,
+ clock_rate_in_hz,
+ this->nfc->get_symmetric(this) ? "Yes" : "No"
+ );
+
+}
+
+/**
+ * show_device_nfc_geometry() - Shows the NFC view of the device geometry.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_nfc_geometry(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ return sprintf(buf,
+ "Page Data Size in Bytes : %u\n"
+ "Page OOB Size in Bytes : %u\n"
+ "ECC Algorithm : %s\n"
+ "ECC Strength : %u\n"
+ "Buffers in Use : %u\n"
+ "Spare Buffer Size in Use: %u\n"
+ "Spare Buffer Spillover : %u\n"
+ ,
+ this->nfc_geometry->page_data_size,
+ this->nfc_geometry->page_oob_size,
+ this->nfc_geometry->ecc_algorithm,
+ this->nfc_geometry->ecc_strength,
+ this->nfc_geometry->buffer_count,
+ this->nfc_geometry->spare_buf_size,
+ this->nfc_geometry->spare_buf_spill
+ );
+
+}
+
+/**
+ * show_device_logical_geometry() - Shows the logical device geometry.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_logical_geometry(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct logical_geometry *logical = &this->logical_geometry;
+
+ return sprintf(buf,
+ "Chip Count : %u\n"
+ "Chip Size in Bytes : %u\n"
+ "Usable Size in Bytes : %u\n"
+ "Block Size in Bytes : %u\n"
+ "Page Data Size in Bytes: %u\n"
+ "Page OOB Size in Bytes : %u\n"
+ ,
+ logical->chip_count,
+ logical->chip_size,
+ logical->usable_size,
+ logical->block_size,
+ logical->page_data_size,
+ logical->page_oob_size
+ );
+
+}
+
+/**
+ * show_device_mtd_nand_info() - Shows the device's MTD NAND-specific info.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_mtd_nand_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int o = 0;
+ unsigned int i;
+ unsigned int j;
+ static const unsigned int columns = 8;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct nand_chip *nand = &this->nand;
+
+ o += sprintf(buf + o,
+ "Options : 0x%08x\n"
+ "Chip Count : %u\n"
+ "Chip Size : %lu\n"
+ "Minimum Writable Size: %u\n"
+ "Page Shift : %u\n"
+ "Page Mask : 0x%x\n"
+ "Block Shift : %u\n"
+ "BBT Block Shift : %u\n"
+ "Chip Shift : %u\n"
+ "Block Mark Offset : %u\n"
+ "Cached Page Number : %d\n"
+ ,
+ nand->options,
+ nand->numchips,
+ nand->chipsize,
+ nand->subpagesize,
+ nand->page_shift,
+ nand->pagemask,
+ nand->phys_erase_shift,
+ nand->bbt_erase_shift,
+ nand->chip_shift,
+ nand->badblockpos,
+ nand->pagebuf
+ );
+
+ o += sprintf(buf + o,
+ "ECC Byte Count : %u\n"
+ ,
+ nand->ecc.layout->eccbytes
+ );
+
+ /* Loop over rows. */
+
+ for (i = 0; (i * columns) < nand->ecc.layout->eccbytes; i++) {
+
+ /* Loop over columns within rows. */
+
+ for (j = 0; j < columns; j++) {
+
+ if (((i * columns) + j) >= nand->ecc.layout->eccbytes)
+ break;
+
+ o += sprintf(buf + o, " %3u",
+ nand->ecc.layout->eccpos[(i * columns) + j]);
+
+ }
+
+ o += sprintf(buf + o, "\n");
+
+ }
+
+ o += sprintf(buf + o,
+ "OOB Available Bytes : %u\n"
+ ,
+ nand->ecc.layout->oobavail
+ );
+
+ j = 0;
+
+ for (i = 0; j < nand->ecc.layout->oobavail; i++) {
+
+ j += nand->ecc.layout->oobfree[i].length;
+
+ o += sprintf(buf + o,
+ " [%3u, %2u]\n"
+ ,
+ nand->ecc.layout->oobfree[i].offset,
+ nand->ecc.layout->oobfree[i].length
+ );
+
+ }
+
+ return o;
+
+}
+
+/**
+ * show_device_mtd_info() - Shows the device's MTD-specific information.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_mtd_info(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int o = 0;
+ unsigned int i;
+ unsigned int j;
+ static const unsigned int columns = 8;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct mtd_info *mtd = &this->mtd;
+
+ o += sprintf(buf + o,
+ "Name : %s\n"
+ "Type : %u\n"
+ "Flags : 0x%08x\n"
+ "Size in Bytes : %u\n"
+ "Erase Region Count : %d\n"
+ "Erase Size in Bytes: %u\n"
+ "Write Size in Bytes: %u\n"
+ "OOB Size in Bytes : %u\n"
+ "Errors Corrected : %u\n"
+ "Failed Reads : %u\n"
+ "Bad Block Count : %u\n"
+ "BBT Block Count : %u\n"
+ ,
+ mtd->name,
+ mtd->type,
+ mtd->flags,
+ mtd->size,
+ mtd->numeraseregions,
+ mtd->erasesize,
+ mtd->writesize,
+ mtd->oobsize,
+ mtd->ecc_stats.corrected,
+ mtd->ecc_stats.failed,
+ mtd->ecc_stats.badblocks,
+ mtd->ecc_stats.bbtblocks
+ );
+
+ o += sprintf(buf + o,
+ "ECC Byte Count : %u\n"
+ ,
+ mtd->ecclayout->eccbytes
+ );
+
+ /* Loop over rows. */
+
+ for (i = 0; (i * columns) < mtd->ecclayout->eccbytes; i++) {
+
+ /* Loop over columns within rows. */
+
+ for (j = 0; j < columns; j++) {
+
+ if (((i * columns) + j) >= mtd->ecclayout->eccbytes)
+ break;
+
+ o += sprintf(buf + o, " %3u",
+ mtd->ecclayout->eccpos[(i * columns) + j]);
+
+ }
+
+ o += sprintf(buf + o, "\n");
+
+ }
+
+ o += sprintf(buf + o,
+ "OOB Available Bytes: %u\n"
+ ,
+ mtd->ecclayout->oobavail
+ );
+
+ j = 0;
+
+ for (i = 0; j < mtd->ecclayout->oobavail; i++) {
+
+ j += mtd->ecclayout->oobfree[i].length;
+
+ o += sprintf(buf + o,
+ " [%3u, %2u]\n"
+ ,
+ mtd->ecclayout->oobfree[i].offset,
+ mtd->ecclayout->oobfree[i].length
+ );
+
+ }
+
+ return o;
+
+}
+
+/**
+ * show_device_bbt_pages() - Shows the pages in which BBT's appear.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_bbt_pages(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int o = 0;
+ unsigned int i;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ struct nand_chip *nand = &this->nand;
+
+ /* Loop over main BBT pages. */
+
+ if (nand->bbt_td)
+ for (i = 0; i < NAND_MAX_CHIPS; i++)
+ o += sprintf(buf + o, "%d: 0x%08x\n",
+ i, nand->bbt_td->pages[i]);
+
+ /* Loop over mirror BBT pages. */
+
+ if (nand->bbt_md)
+ for (i = 0; i < NAND_MAX_CHIPS; i++)
+ o += sprintf(buf + o, "%d: 0x%08x\n",
+ i, nand->bbt_md->pages[i]);
+
+ return o;
+
+}
+
+/**
+ * show_device_cycle_in_ns() - Shows the device's cycle in ns.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_cycle_in_ns(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+ return sprintf(buf, "%u\n", get_cycle_in_ns(this));
+}
+
+/**
+ * store_device_cycle_in_ns() - Sets the device's cycle in ns.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_cycle_in_ns(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int error;
+ unsigned long new_cycle_in_ns;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ /* Look for nonsense. */
+
+ if (!size)
+ return -EINVAL;
+
+ /* Try to understand the new cycle period. */
+
+ if (strict_strtoul(buf, 0, &new_cycle_in_ns))
+ return -EINVAL;
+
+ /* Try to implement the new cycle period. */
+
+ error = this->nfc->set_closest_cycle(this, new_cycle_in_ns);
+
+ if (error)
+ return -EINVAL;
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/**
+ * show_device_interrupt_override() - Shows the device's interrupt override.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_interrupt_override(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ switch (this->interrupt_override) {
+
+ case NEVER:
+ return sprintf(buf, "-1\n");
+ break;
+
+ case DRIVER_CHOICE:
+ return sprintf(buf, "0\n");
+ break;
+
+ case ALWAYS:
+ return sprintf(buf, "1\n");
+ break;
+
+ default:
+ return sprintf(buf, "?\n");
+ break;
+
+ }
+
+}
+
+/**
+ * store_device_interrupt_override() - Sets the device's interrupt override.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_interrupt_override(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ if (!strcmp(buf, "-1"))
+ this->interrupt_override = NEVER;
+ else if (!strcmp(buf, "0"))
+ this->interrupt_override = DRIVER_CHOICE;
+ else if (!strcmp(buf, "1"))
+ this->interrupt_override = ALWAYS;
+ else
+ return -EINVAL;
+
+ return size;
+
+}
+
+/**
+ * show_device_auto_op_override() - Shows the device's automatic op override.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_auto_op_override(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ switch (this->auto_op_override) {
+
+ case NEVER:
+ return sprintf(buf, "-1\n");
+ break;
+
+ case DRIVER_CHOICE:
+ return sprintf(buf, "0\n");
+ break;
+
+ case ALWAYS:
+ return sprintf(buf, "1\n");
+ break;
+
+ default:
+ return sprintf(buf, "?\n");
+ break;
+
+ }
+
+}
+
+/**
+ * store_device_auto_op_override() - Sets the device's automatic op override.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_auto_op_override(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ if (!strcmp(buf, "-1"))
+ this->auto_op_override = NEVER;
+ else if (!strcmp(buf, "0"))
+ this->auto_op_override = DRIVER_CHOICE;
+ else if (!strcmp(buf, "1"))
+ this->auto_op_override = ALWAYS;
+ else
+ return -EINVAL;
+
+ return size;
+
+}
+
+/**
+ * show_device_inject_ecc_error() - Shows the device's error injection flag.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer that will receive a representation of the attribute.
+ */
+static ssize_t show_device_inject_ecc_error(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", this->inject_ecc_error);
+
+}
+
+/**
+ * store_device_inject_ecc_error() - Sets the device's error injection flag.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_inject_ecc_error(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ unsigned long new_inject_ecc_error;
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ /* Look for nonsense. */
+
+ if (!size)
+ return -EINVAL;
+
+ /* Try to understand the new cycle period. */
+
+ if (strict_strtol(buf, 0, &new_inject_ecc_error))
+ return -EINVAL;
+
+ /* Store the value. */
+
+ this->inject_ecc_error = new_inject_ecc_error;
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/**
+ * store_device_invalidate_page_cache() - Invalidates the device's page cache.
+ *
+ * @dev: The device of interest.
+ * @attr: The attribute of interest.
+ * @buf: A buffer containing a new attribute value.
+ * @size: The size of the buffer.
+ */
+static ssize_t store_device_invalidate_page_cache(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct imx_nfc_data *this = dev_get_drvdata(dev);
+
+ /* Invalidate the page cache. */
+
+ this->nand.pagebuf = -1;
+
+ /* Return success. */
+
+ return size;
+
+}
+
+/* Device attributes that appear in sysfs. */
+
+static DEVICE_ATTR(platform_info , 0444, show_device_platform_info , 0);
+static DEVICE_ATTR(physical_geometry, 0444, show_device_physical_geometry, 0);
+static DEVICE_ATTR(nfc_info , 0444, show_device_nfc_info , 0);
+static DEVICE_ATTR(nfc_geometry , 0444, show_device_nfc_geometry , 0);
+static DEVICE_ATTR(logical_geometry , 0444, show_device_logical_geometry , 0);
+static DEVICE_ATTR(mtd_nand_info , 0444, show_device_mtd_nand_info , 0);
+static DEVICE_ATTR(mtd_info , 0444, show_device_mtd_info , 0);
+static DEVICE_ATTR(bbt_pages , 0444, show_device_bbt_pages , 0);
+
+static DEVICE_ATTR(cycle_in_ns, 0644,
+ show_device_cycle_in_ns, store_device_cycle_in_ns);
+
+static DEVICE_ATTR(interrupt_override, 0644,
+ show_device_interrupt_override, store_device_interrupt_override);
+
+static DEVICE_ATTR(auto_op_override, 0644,
+ show_device_auto_op_override, store_device_auto_op_override);
+
+static DEVICE_ATTR(inject_ecc_error, 0644,
+ show_device_inject_ecc_error, store_device_inject_ecc_error);
+
+static DEVICE_ATTR(invalidate_page_cache, 0644,
+ 0, store_device_invalidate_page_cache);
+
+static struct device_attribute *device_attributes[] = {
+ &dev_attr_platform_info,
+ &dev_attr_physical_geometry,
+ &dev_attr_nfc_info,
+ &dev_attr_nfc_geometry,
+ &dev_attr_logical_geometry,
+ &dev_attr_mtd_nand_info,
+ &dev_attr_mtd_info,
+ &dev_attr_bbt_pages,
+ &dev_attr_cycle_in_ns,
+ &dev_attr_interrupt_override,
+ &dev_attr_auto_op_override,
+ &dev_attr_inject_ecc_error,
+ &dev_attr_invalidate_page_cache,
+};
+
+/**
+ * validate_the_platform() - Validates information about the platform.
+ *
+ * Note that this function doesn't validate the NFC version. That's done when
+ * the probing process attempts to configure for the specific hardware version.
+ *
+ * @pdev: A pointer to the platform device.
+ */
+static int validate_the_platform(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imx_nfc_platform_data *pdata = pdev->dev.platform_data;
+
+ /* Validate the clock name. */
+
+ if (!pdata->clock_name) {
+ dev_err(dev, "No clock name\n");
+ return -ENXIO;
+ }
+
+ /* Validate the partitions. */
+
+ if ((pdata->partitions && (!pdata->partition_count)) ||
+ (!pdata->partitions && (pdata->partition_count))) {
+ dev_err(dev, "Bad partition data\n");
+ return -ENXIO;
+ }
+
+ /* Return success */
+
+ return 0;
+
+}
+
+/**
+ * set_up_the_nfc_hal() - Sets up for the specific NFC hardware HAL.
+ *
+ * @this: Per-device data.
+ */
+static int set_up_the_nfc_hal(struct imx_nfc_data *this)
+{
+ unsigned int i;
+ struct nfc_hal *p;
+ struct imx_nfc_platform_data *pdata = this->pdata;
+
+ for (i = 0; i < ARRAY_SIZE(nfc_hals); i++) {
+
+ p = nfc_hals[i];
+
+ /*
+ * Restrict to 3.2 until others are fully implemented.
+ *
+ * TODO: Remove this.
+ */
+
+ if ((p->major_version != 3) && (p->minor_version != 2))
+ continue;
+
+ if ((p->major_version == pdata->nfc_major_version) &&
+ (p->minor_version == pdata->nfc_minor_version)) {
+ this->nfc = p;
+ return 0;
+ break;
+ }
+
+ }
+
+ dev_err(this->dev, "Unkown NFC version %d.%d\n",
+ pdata->nfc_major_version, pdata->nfc_minor_version);
+
+ return !0;
+
+}
+
+/**
+ * acquire_resources() - Tries to acquire resources.
+ *
+ * @this: Per-device data.
+ */
+static int acquire_resources(struct imx_nfc_data *this)
+{
+
+ int error = 0;
+ struct platform_device *pdev = this->pdev;
+ struct device *dev = this->dev;
+ struct imx_nfc_platform_data *pdata = this->pdata;
+ struct resource *r;
+
+ /* Find the buffers and map them. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_BUFFERS_ADDR_RES_NAME);
+
+ if (!r) {
+ dev_err(dev, "Can't get '%s'\n", IMX_NFC_BUFFERS_ADDR_RES_NAME);
+ error = -ENXIO;
+ goto exit_buffers;
+ }
+
+ this->buffers = ioremap(r->start, r->end - r->start + 1);
+
+ if (!this->buffers) {
+ dev_err(dev, "Can't remap buffers\n");
+ error = -EIO;
+ goto exit_buffers;
+ }
+
+ /* Find the primary registers and map them. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME);
+
+ if (!r) {
+ dev_err(dev, "Can't get '%s'\n",
+ IMX_NFC_PRIMARY_REGS_ADDR_RES_NAME);
+ error = -ENXIO;
+ goto exit_primary_registers;
+ }
+
+ this->primary_regs = ioremap(r->start, r->end - r->start + 1);
+
+ if (!this->primary_regs) {
+ dev_err(dev, "Can't remap the primary registers\n");
+ error = -EIO;
+ goto exit_primary_registers;
+ }
+
+ /* Check for secondary registers. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);
+
+ if (r && !this->nfc->has_secondary_regs) {
+
+ dev_err(dev, "Resource '%s' should not be present\n",
+ IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);
+ error = -ENXIO;
+ goto exit_secondary_registers;
+
+ }
+
+ if (this->nfc->has_secondary_regs) {
+
+ if (!r) {
+ dev_err(dev, "Can't get '%s'\n",
+ IMX_NFC_SECONDARY_REGS_ADDR_RES_NAME);
+ error = -ENXIO;
+ goto exit_secondary_registers;
+ }
+
+ this->secondary_regs = ioremap(r->start, r->end - r->start + 1);
+
+ if (!this->secondary_regs) {
+ dev_err(dev,
+ "Can't remap the secondary registers\n");
+ error = -EIO;
+ goto exit_secondary_registers;
+ }
+
+ }
+
+ /* Find out what our interrupt is and try to own it. */
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+ IMX_NFC_INTERRUPT_RES_NAME);
+
+ if (!r) {
+ dev_err(dev, "Can't get '%s'\n", IMX_NFC_INTERRUPT_RES_NAME);
+ error = -ENXIO;
+ goto exit_irq;
+ }
+
+ this->interrupt = r->start;
+
+ error = request_irq(this->interrupt,
+ nfc_util_isr, 0, this->dev->bus_id, this);
+
+ if (error) {
+ dev_err(dev, "Can't own interrupt %d\n", this->interrupt);
+ goto exit_irq;
+ }
+
+ /* Find out the name of our clock and try to own it. */
+
+ this->clock = clk_get(dev, pdata->clock_name);
+
+ if (this->clock == ERR_PTR(-ENOENT)) {
+ dev_err(dev, "Can't get clock '%s'\n", pdata->clock_name);
+ error = -ENXIO;
+ goto exit_clock;
+ }
+
+ /* Return success. */
+
+ return 0;
+
+ /* Error return paths begin here. */
+
+exit_clock:
+ free_irq(this->interrupt, this);
+exit_irq:
+ if (this->secondary_regs)
+ iounmap(this->secondary_regs);
+exit_secondary_registers:
+ iounmap(this->primary_regs);
+exit_primary_registers:
+ iounmap(this->buffers);
+exit_buffers:
+ return error;
+
+}
+
+/**
+ * release_resources() - Releases resources.
+ *
+ * @this: Per-device data.
+ */
+static void release_resources(struct imx_nfc_data *this)
+{
+
+ /* Release our clock. */
+
+ clk_disable(this->clock);
+ clk_put(this->clock);
+
+ /* Release our interrupt. */
+
+ free_irq(this->interrupt, this);
+
+ /* Release mapped memory. */
+
+ iounmap(this->buffers);
+ iounmap(this->primary_regs);
+ if (this->secondary_regs)
+ iounmap(this->secondary_regs);
+
+}
+
+/**
+ * register_with_mtd() - Registers this medium with MTD.
+ *
+ * @this: Per-device data.
+ */
+static int register_with_mtd(struct imx_nfc_data *this)
+{
+ int error = 0;
+ struct mtd_info *mtd = &this->mtd;
+ struct nand_chip *nand = &this->nand;
+ struct device *dev = this->dev;
+ struct imx_nfc_platform_data *pdata = this->pdata;
+
+ /* Link the MTD structures together, along with our own data. */
+
+ mtd->priv = nand;
+ nand->priv = this;
+
+ /* Prepare the MTD structure. */
+
+ mtd->owner = THIS_MODULE;
+
+ /*
+ * Signal Control Functions
+ */
+
+ nand->cmd_ctrl = mil_cmd_ctrl;
+
+ /*
+ * Chip Control Functions
+ *
+ * Not all of our NFC hardware versions expose Ready/Busy signals. For
+ * versions that don't, the is_ready function pointer will be NULL. In
+ * those cases, we leave the dev_ready member unassigned, which will
+ * cause the HIL to use a reference implementation's algorithm to
+ * discover when the hardware is ready.
+ */
+
+ nand->select_chip = mil_select_chip;
+ nand->cmdfunc = mil_cmdfunc;
+ nand->waitfunc = mil_waitfunc;
+
+ if (this->nfc->is_ready)
+ nand->dev_ready = mil_dev_ready;
+
+ /*
+ * Low-level I/O Functions
+ */
+
+ nand->read_byte = mil_read_byte;
+ nand->read_word = mil_read_word;
+ nand->read_buf = mil_read_buf;
+ nand->write_buf = mil_write_buf;
+ nand->verify_buf = mil_verify_buf;
+
+ /*
+ * ECC Control Functions
+ */
+
+ nand->ecc.hwctl = mil_ecc_hwctl;
+ nand->ecc.calculate = mil_ecc_calculate;
+ nand->ecc.correct = mil_ecc_correct;
+
+ /*
+ * ECC-Aware I/O Functions
+ */
+
+ nand->ecc.read_page = mil_ecc_read_page;
+ nand->ecc.read_page_raw = mil_ecc_read_page_raw;
+ nand->ecc.write_page = mil_ecc_write_page;
+ nand->ecc.write_page_raw = mil_ecc_write_page_raw;
+
+ /*
+ * High-level I/O Functions
+ */
+
+ nand->write_page = mil_write_page;
+ nand->ecc.read_oob = mil_ecc_read_oob;
+ nand->ecc.write_oob = mil_ecc_write_oob;
+
+ /*
+ * Bad Block Marking Functions
+ *
+ * We want to use the reference block_bad and block_markbad
+ * implementations, so we don't assign those members.
+ */
+
+ nand->scan_bbt = mil_scan_bbt;
+
+ /*
+ * Error Recovery Functions
+ *
+ * We don't fill in the errstat function pointer because it's optional
+ * and we don't have a need for it.
+ */
+
+ /*
+ * Device Attributes
+ *
+ * We don't fill in the chip_delay member because we don't have a need
+ * for it.
+ *
+ * We support only 8-bit Flash bus width.
+ */
+
+ /*
+ * ECC Attributes
+ *
+ * Members that aren't set here are configured by a version-specific
+ * set_geometry() function.
+ */
+
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.size = NFC_MAIN_BUF_SIZE;
+ nand->ecc.prepad = 0;
+ nand->ecc.postpad = 0;
+
+ /*
+ * Bad Block Management Attributes
+ *
+ * We don't fill in the following attributes:
+ *
+ * badblockpos
+ * bbt
+ * badblock_pattern
+ * bbt_erase_shift
+ *
+ * These attributes aren't hardware-specific, and the HIL makes fine
+ * choices without our help.
+ */
+
+ nand->options |= NAND_USE_FLASH_BBT;
+ nand->bbt_td = &bbt_main_descriptor;
+ nand->bbt_md = &bbt_mirror_descriptor;
+
+ /*
+ * Device Control
+ *
+ * We don't fill in the controller attribute. In principle, we could set
+ * up a structure to represent the controller. However, since it's
+ * vanishingly improbable that we'll have more than one medium behind
+ * the controller, it's not worth the effort. We let the HIL handle it.
+ */
+
+ /*
+ * Memory-mapped I/O
+ *
+ * We don't fill in the following attributes:
+ *
+ * IO_ADDR_R
+ * IO_ADDR_W
+ *
+ * None of these are necessary because we don't have a memory-mapped
+ * implementation.
+ */
+
+ /*
+ * Install a "fake" ECC layout.
+ *
+ * We'll be calling nand_scan() to do the final MTD setup. If we haven't
+ * already chosen an ECC layout, then nand_scan() will choose one based
+ * on the part geometry it discovers. Unfortunately, it doesn't make
+ * good choices. It would be best if we could install the correct ECC
+ * layout now, before we call nand_scan(). We can't do that because we
+ * don't know the medium geometry yet. Here, we install a "fake" ECC
+ * layout just to stop nand_scan() from trying to pick on for itself.
+ * Later, in imx_nfc_scan_bbt(), when we know the medium geometry, we'll
+ * install the correct choice.
+ *
+ * Of course, this tactic depends critically on nand_scan() not using
+ * the fake layout before we can install a good one. This is in fact the
+ * case.
+ */
+
+ nand->ecc.layout = &nfc_geometry_512_16_RS_ECC1.mtd_layout;
+
+ /*
+ * Ask the NAND Flash system to scan for chips.
+ *
+ * This will fill in reference implementations for all the members of
+ * the MTD structures that we didn't set, and will make the medium fully
+ * usable.
+ */
+
+ error = nand_scan(mtd, this->nfc->max_chip_count);
+
+ if (error) {
+ dev_err(dev, "Chip scan failed\n");
+ error = -ENXIO;
+ goto exit_scan;
+ }
+
+ /* Register the MTD that represents the entire medium. */
+
+ mtd->name = "NAND Flash Medium";
+
+ add_mtd_device(mtd);
+
+ /* Check if we're doing partitions and register MTD's accordingly. */
+
+ #ifdef CONFIG_MTD_PARTITIONS
+
+ /*
+ * Look for partition information. If we find some, install
+ * them. Otherwise, use the partitions handed to us by the
+ * platform.
+ */
+
+ this->partition_count =
+ parse_mtd_partitions(mtd, partition_source_types,
+ &this->partitions, 0);
+
+ if ((this->partition_count <= 0) && (pdata->partitions)) {
+ this->partition_count = pdata->partition_count;
+ this->partitions = pdata->partitions;
+ }
+
+ if (this->partitions)
+ add_mtd_partitions(mtd, this->partitions,
+ this->partition_count);
+
+ #endif
+
+ /* Return success. */
+
+ return 0;
+
+ /* Error return paths begin here. */
+
+exit_scan:
+ return error;
+
+}
+
+/**
+ * unregister_with_mtd() - Unregisters this medium with MTD.
+ *
+ * @this: Per-device data.
+ */
+static void unregister_with_mtd(struct imx_nfc_data *this)
+{
+
+ /* Get MTD to let go. */
+
+ nand_release(&this->mtd);
+
+}
+
+/**
+ * manage_sysfs_files() - Creates/removes sysfs files for this device.
+ *
+ * @this: Per-device data.
+ */
+static void manage_sysfs_files(struct imx_nfc_data *this, int create)
+{
+ int error;
+ unsigned int i;
+ struct device_attribute **attr;
+
+ for (i = 0, attr = device_attributes;
+ i < ARRAY_SIZE(device_attributes); i++, attr++) {
+
+ if (create) {
+ error = device_create_file(this->dev, *attr);
+ if (error) {
+ while (--attr >= device_attributes)
+ device_remove_file(this->dev, *attr);
+ return;
+ }
+ } else {
+ device_remove_file(this->dev, *attr);
+ }
+
+ }
+
+}
+
+/**
+ * imx_nfc_probe() - Probes for a device and, if possible, takes ownership.
+ *
+ * @pdev: A pointer to the platform device.
+ */
+static int imx_nfc_probe(struct platform_device *pdev)
+{
+ int error = 0;
+ char *symmetric_clock;
+ struct clk *parent_clock;
+ unsigned long parent_clock_rate_in_hz;
+ unsigned long parent_clock_rate_in_mhz;
+ unsigned long nfc_clock_rate_in_hz;
+ unsigned long nfc_clock_rate_in_mhz;
+ unsigned long clock_divisor;
+ unsigned long cycle_in_ns;
+ struct device *dev = &pdev->dev;
+ struct imx_nfc_platform_data *pdata = pdev->dev.platform_data;
+ struct imx_nfc_data *this = 0;
+
+ /* Say hello. */
+
+ dev_info(dev, "Probing...\n");
+
+ /* Check if we're enabled. */
+
+ if (!imx_nfc_module_enable) {
+ dev_info(dev, "Disabled\n");
+ return -ENXIO;
+ }
+
+ /* Validate the platform device data. */
+
+ error = validate_the_platform(pdev);
+
+ if (error)
+ goto exit_validate_platform;
+
+ /* Allocate memory for the per-device data. */
+
+ this = kzalloc(sizeof(*this), GFP_KERNEL);
+
+ if (!this) {
+ dev_err(dev, "Failed to allocate per-device memory\n");
+ error = -ENOMEM;
+ goto exit_allocate_this;
+ }
+
+ /* Link our per-device data to the owning device. */
+
+ platform_set_drvdata(pdev, this);
+
+ /* Fill in the convenience pointers in our per-device data. */
+
+ this->pdev = pdev;
+ this->dev = &pdev->dev;
+ this->pdata = pdata;
+
+ /* Initialize the interrupt service pathway. */
+
+ init_completion(&this->done);
+
+ /* Set up the NFC HAL. */
+
+ error = set_up_the_nfc_hal(this);
+
+ if (error)
+ goto exit_set_up_nfc_hal;
+
+ /* Attempt to acquire the resources we need. */
+
+ error = acquire_resources(this);
+
+ if (error)
+ goto exit_acquire_resources;
+
+ /* Initialize the NFC HAL. */
+
+ if (this->nfc->init(this)) {
+ error = -ENXIO;
+ goto exit_nfc_init;
+ }
+
+ /* Tell the platform we're bringing this device up. */
+
+ if (pdata->init)
+ error = pdata->init();
+
+ if (error)
+ goto exit_platform_init;
+
+ /* Report. */
+
+ parent_clock = clk_get_parent(this->clock);
+ parent_clock_rate_in_hz = clk_get_rate(parent_clock);
+ parent_clock_rate_in_mhz = parent_clock_rate_in_hz / 1000000;
+ nfc_clock_rate_in_hz = clk_get_rate(this->clock);
+ nfc_clock_rate_in_mhz = nfc_clock_rate_in_hz / 1000000;
+
+ clock_divisor = parent_clock_rate_in_hz / nfc_clock_rate_in_hz;
+ symmetric_clock = this->nfc->get_symmetric(this) ? "Yes" : "No";
+ cycle_in_ns = get_cycle_in_ns(this);
+
+ dev_dbg(dev, "-------------\n");
+ dev_dbg(dev, "Configuration\n");
+ dev_dbg(dev, "-------------\n");
+ dev_dbg(dev, "NFC Version : %d.%d\n" , this->nfc->major_version,
+ this->nfc->minor_version);
+ dev_dbg(dev, "Buffers : 0x%p\n" , this->buffers);
+ dev_dbg(dev, "Primary Regs : 0x%p\n" , this->primary_regs);
+ dev_dbg(dev, "Secondary Regs : 0x%p\n" , this->secondary_regs);
+ dev_dbg(dev, "Interrupt : %u\n" , this->interrupt);
+ dev_dbg(dev, "Clock Name : %s\n" , pdata->clock_name);
+ dev_dbg(dev, "Parent Clock Rate: %lu Hz (%lu MHz)\n",
+ parent_clock_rate_in_hz,
+ parent_clock_rate_in_mhz);
+ dev_dbg(dev, "Clock Divisor : %lu\n", clock_divisor);
+ dev_dbg(dev, "NFC Clock Rate : %lu Hz (%lu MHz)\n",
+ nfc_clock_rate_in_hz,
+ nfc_clock_rate_in_mhz);
+ dev_dbg(dev, "Symmetric Clock : %s\n" , symmetric_clock);
+ dev_dbg(dev, "Actual Cycle : %lu ns\n" , cycle_in_ns);
+ dev_dbg(dev, "Target Cycle : %u ns\n" , pdata->target_cycle_in_ns);
+
+ /* Initialize the Medium Abstraction Layer. */
+
+ mal_init(this);
+
+ /* Initialize the MTD Interface Layer. */
+
+ mil_init(this);
+
+ /* Register this medium with MTD. */
+
+ error = register_with_mtd(this);
+
+ if (error)
+ goto exit_mtd_registration;
+
+ /* Create sysfs entries for this device. */
+
+ manage_sysfs_files(this, true);
+
+ /* Return success. */
+
+ return 0;
+
+ /* Error return paths begin here. */
+
+exit_mtd_registration:
+ if (pdata->exit)
+ pdata->exit();
+exit_platform_init:
+ this->nfc->exit(this);
+exit_nfc_init:
+exit_acquire_resources:
+exit_set_up_nfc_hal:
+ platform_set_drvdata(pdev, NULL);
+ kfree(this);
+exit_allocate_this:
+exit_validate_platform:
+ return error;
+
+}
+
+/**
+ * imx_nfc_remove() - Dissociates this driver from the given device.
+ *
+ * @pdev: A pointer to the device.
+ */
+static int __exit imx_nfc_remove(struct platform_device *pdev)
+{
+ struct imx_nfc_data *this = platform_get_drvdata(pdev);
+
+ /* Remove sysfs entries for this device. */
+
+ manage_sysfs_files(this, false);
+
+ /* Unregister with the NAND Flash MTD system. */
+
+ unregister_with_mtd(this);
+
+ /* Tell the platform we're shutting down this device. */
+
+ if (this->pdata->exit)
+ this->pdata->exit();
+
+ /* Shut down the NFC. */
+
+ this->nfc->exit(this);
+
+ /* Release our resources. */
+
+ release_resources(this);
+
+ /* Unlink our per-device data from the platform device. */
+
+ platform_set_drvdata(pdev, NULL);
+
+ /* Free our per-device data. */
+
+ kfree(this);
+
+ /* Return success. */
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/**
+ * suspend() - Puts the NFC in a low power state.
+ *
+ * Refer to Documentation/driver-model/driver.txt for more information.
+ *
+ * @pdev: A pointer to the device.
+ * @state: The new power state.
+ */
+
+static int imx_nfc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int error = 0;
+ struct imx_nfc_data *this = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = &this->mtd;
+ struct device *dev = &this->pdev->dev;
+
+ dev_dbg(dev, "Suspending...\n");
+
+ /* Suspend MTD's use of this device. */
+
+ error = mtd->suspend(mtd);
+
+ /* Suspend the actual hardware. */
+
+ clk_disable(this->clock);
+
+ return error;
+
+}
+
+/**
+ * resume() - Brings the NFC back from a low power state.
+ *
+ * Refer to Documentation/driver-model/driver.txt for more information.
+ *
+ * @pdev: A pointer to the device.
+ */
+static int imx_nfc_resume(struct platform_device *pdev)
+{
+ struct imx_nfc_data *this = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = &this->mtd;
+ struct device *dev = &this->pdev->dev;
+
+ dev_dbg(dev, "Resuming...\n");
+
+ /* Resume MTD's use of this device. */
+
+ mtd->resume(mtd);
+
+ return 0;
+
+}
+
+#else
+
+#define suspend NULL
+#define resume NULL
+
+#endif /* CONFIG_PM */
+
+/*
+ * This structure represents this driver to the platform management system.
+ */
+static struct platform_driver imx_nfc_driver = {
+ .driver = {
+ .name = IMX_NFC_DRIVER_NAME,
+ },
+ .probe = imx_nfc_probe,
+ .remove = __exit_p(imx_nfc_remove),
+ .suspend = imx_nfc_suspend,
+ .resume = imx_nfc_resume,
+};
+
+/**
+ * imx_nfc_init() - Initializes this module.
+ */
+static int __init imx_nfc_init(void)
+{
+
+ pr_info("i.MX NFC driver %s\n", DRIVER_VERSION);
+
+ /* Register this driver with the platform management system. */
+
+ if (platform_driver_register(&imx_nfc_driver) != 0) {
+ pr_err("i.MX NFC driver registration failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+
+}
+
+/**
+ * imx_nfc_exit() - Deactivates this module.
+ */
+static void __exit imx_nfc_exit(void)
+{
+ pr_debug("i.MX NFC driver exiting...\n");
+ platform_driver_unregister(&imx_nfc_driver);
+}
+
+module_init(imx_nfc_init);
+module_exit(imx_nfc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX NAND Flash Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/lba/Makefile b/drivers/mtd/nand/lba/Makefile
new file mode 100644
index 000000000000..0e576bfa856d
--- /dev/null
+++ b/drivers/mtd/nand/lba/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MTD_NAND_GPMI_LBA) += gpmi_lba.o
+gpmi_lba-objs += gpmi-transport.o lba-core.o lba-blk.o
diff --git a/drivers/mtd/nand/lba/gpmi-transport.c b/drivers/mtd/nand/lba/gpmi-transport.c
new file mode 100644
index 000000000000..4a2f9301e756
--- /dev/null
+++ b/drivers/mtd/nand/lba/gpmi-transport.c
@@ -0,0 +1,828 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI transport layer for LBA driver
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ * Clock settings and hw init comes from
+ * gpmi driver by Dmitry Pervushin.
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/ctype.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <asm/div64.h>
+#include <mach/stmp3xxx.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+#include "lba.h"
+
+struct lba_data *g_data;
+static int max_chips = 1;
+static long clk = -1;
+
+struct gpmi_nand_timing gpmi_safe_timing = {
+ .address_setup = 25,
+ .data_setup = 80,
+ .data_hold = 60,
+ .dsample_time = 6,
+};
+
+/******************************************************************************
+ * HW init part
+ ******************************************************************************/
+
+/**
+ * gpmi_irq - IRQ handler
+ *
+ * @irq: irq no
+ * @context: IRQ context, pointer to gpmi_nand_data
+ */
+static irqreturn_t gpmi_irq(int irq, void *context)
+{
+ struct lba_data *data = context;
+ int i;
+
+ for (i = 0; i < max_chips; i++) {
+ if (stmp3xxx_dma_is_interrupt(data->nand[i].dma_ch)) {
+ stmp3xxx_dma_clear_interrupt(data->nand[i].dma_ch);
+ complete(&data->nand[i].done);
+ }
+
+ }
+ HW_GPMI_CTRL1_CLR(BM_GPMI_CTRL1_DEV_IRQ | BM_GPMI_CTRL1_TIMEOUT_IRQ);
+ return IRQ_HANDLED;
+}
+
+static inline u32 gpmi_cycles_ceil(u32 ntime, u32 period)
+{
+ int k;
+
+ k = (ntime + period - 1) / period;
+ if (k == 0)
+ k++;
+ return k ;
+}
+
+
+/**
+ * gpmi_set_timings - set GPMI timings
+ * @pdev: pointer to GPMI platform device
+ * @tm: pointer to structure &gpmi_nand_timing with new timings
+ *
+ * During initialization, GPMI uses safe sub-optimal timings, which
+ * can be changed after reading boot control blocks
+ */
+void gpmi_set_timings(struct lba_data *data, struct gpmi_nand_timing *tm)
+{
+ u32 period_ns = 1000000 / clk_get_rate(data->clk) + 1;
+ u32 address_cycles, data_setup_cycles;
+ u32 data_hold_cycles, data_sample_cycles;
+ u32 busy_timeout;
+ u32 t0;
+
+ address_cycles = gpmi_cycles_ceil(tm->address_setup, period_ns);
+ data_setup_cycles = gpmi_cycles_ceil(tm->data_setup, period_ns);
+ data_hold_cycles = gpmi_cycles_ceil(tm->data_hold, period_ns);
+ data_sample_cycles = gpmi_cycles_ceil(tm->dsample_time + period_ns / 4,
+ period_ns / 2);
+ busy_timeout = gpmi_cycles_ceil(10000000 / 4096, period_ns);
+
+ t0 = BF_GPMI_TIMING0_ADDRESS_SETUP(address_cycles) |
+ BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles) |
+ BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles);
+ HW_GPMI_TIMING0_WR(t0);
+
+ HW_GPMI_TIMING1_WR(BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(busy_timeout));
+
+#ifdef CONFIG_ARCH_STMP378X
+ HW_GPMI_CTRL1_CLR(BM_GPMI_CTRL1_RDN_DELAY);
+ HW_GPMI_CTRL1_SET(BF_GPMI_CTRL1_RDN_DELAY(data_sample_cycles));
+#else
+ HW_GPMI_CTRL1_CLR(BM_GPMI_CTRL1_DSAMPLE_TIME);
+ HW_GPMI_CTRL1_SET(BF_GPMI_CTRL1_DSAMPLE_TIME(data_sample_cycles));
+#endif
+
+}
+
+void queue_plug(struct lba_data *data)
+{
+ clk_enable(data->clk);
+ if (clk <= 0)
+ clk = 24000; /* safe setting, some chips do not work on
+ speeds >= 24kHz */
+ clk_set_rate(data->clk, clk);
+
+ clk = clk_get_rate(data->clk);
+
+
+ stmp3xxx_reset_block(HW_GPMI_CTRL0_OFFSET + REGS_GPMI_BASE, 1);
+
+ /* write protection OFF */
+ HW_GPMI_CTRL1_SET(BM_GPMI_CTRL1_DEV_RESET);
+
+ /* IRQ polarity */
+ HW_GPMI_CTRL1_SET(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY);
+
+ /* ...and ECC module */
+ /*HW_GPMI_CTRL1_SET(bch_mode());*/
+
+ /* choose NAND mode (1 means ATA, 0 - NAND */
+ HW_GPMI_CTRL1_CLR(BM_GPMI_CTRL1_GPMI_MODE);
+
+ gpmi_set_timings(data, &gpmi_safe_timing);
+}
+
+void queue_release(struct lba_data *data)
+{
+ HW_GPMI_CTRL0_SET(BM_GPMI_CTRL0_SFTRST);
+
+ clk_disable(data->clk);
+}
+
+
+/**
+ * gpmi_init_hw - initialize the hardware
+ * @pdev: pointer to platform device
+ *
+ * Initialize GPMI hardware and set default (safe) timings for NAND access.
+ * Returns error code or 0 on success
+ */
+static int gpmi_init_hw(struct platform_device *pdev, int request_pins)
+{
+ struct lba_data *data = platform_get_drvdata(pdev);
+ char *devname = pdev->dev.bus_id;
+ int err = 0;
+
+ data->clk = clk_get(NULL, "gpmi");
+ if (IS_ERR(data->clk)) {
+ err = PTR_ERR(data->clk);
+ dev_err(&pdev->dev, "cannot set failsafe clockrate\n");
+ goto out;
+ }
+ if (request_pins)
+ gpmi_pinmux_request(devname);
+
+ queue_plug(data);
+
+out:
+ return err;
+}
+/**
+ * gpmi_release_hw - free the hardware
+ * @pdev: pointer to platform device
+ *
+ * In opposite to gpmi_init_hw, release all acquired resources
+ */
+static void gpmi_release_hw(struct platform_device *pdev)
+{
+ struct lba_data *data = platform_get_drvdata(pdev);
+
+ queue_release(data);
+ clk_put(data->clk);
+ gpmi_pinmux_free(pdev->dev.bus_id);
+}
+
+
+/**
+ * gpmi_alloc_buffers - allocate DMA buffers for one chip
+ *
+ * @pdev: GPMI platform device
+ * @g: pointer to structure associated with NAND chip
+ *
+ * Allocate buffer using dma_alloc_coherent
+ */
+static int gpmi_alloc_buffers(struct platform_device *pdev,
+ struct gpmi_perchip_data *g)
+{
+ g->cmd_buffer = dma_alloc_coherent(&pdev->dev,
+ g->cmd_buffer_size,
+ &g->cmd_buffer_handle, GFP_DMA);
+ if (!g->cmd_buffer)
+ goto out1;
+
+ g->write_buffer = dma_alloc_coherent(&pdev->dev,
+ g->write_buffer_size,
+ &g->write_buffer_handle, GFP_DMA);
+ if (!g->write_buffer)
+ goto out2;
+
+ g->data_buffer = dma_alloc_coherent(&pdev->dev,
+ g->data_buffer_size,
+ &g->data_buffer_handle, GFP_DMA);
+ if (!g->data_buffer)
+ goto out3;
+
+ g->oob_buffer = dma_alloc_coherent(&pdev->dev,
+ g->oob_buffer_size,
+ &g->oob_buffer_handle, GFP_DMA);
+ if (!g->oob_buffer)
+ goto out4;
+
+ g->cmdtail_buffer = dma_alloc_coherent(&pdev->dev,
+ g->cmdtail_buffer_size,
+ &g->cmdtail_buffer_handle, GFP_DMA);
+ if (!g->oob_buffer)
+ goto out5;
+
+ return 0;
+
+out5:
+ dma_free_coherent(&pdev->dev, g->oob_buffer_size,
+ g->oob_buffer, g->oob_buffer_handle);
+
+out4:
+ dma_free_coherent(&pdev->dev, g->data_buffer_size,
+ g->data_buffer, g->data_buffer_handle);
+out3:
+ dma_free_coherent(&pdev->dev, g->write_buffer_size,
+ g->write_buffer, g->write_buffer_handle);
+out2:
+ dma_free_coherent(&pdev->dev, g->cmd_buffer_size,
+ g->cmd_buffer, g->cmd_buffer_handle);
+out1:
+ return -ENOMEM;
+}
+
+/**
+ * gpmi_free_buffers - free buffers allocated by gpmi_alloc_buffers
+ *
+ * @pdev: platform device
+ * @g: pointer to structure associated with NAND chip
+ *
+ * Deallocate buffers on exit
+ */
+static void gpmi_free_buffers(struct platform_device *pdev,
+ struct gpmi_perchip_data *g)
+{
+ dma_free_coherent(&pdev->dev, g->oob_buffer_size,
+ g->oob_buffer, g->oob_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->write_buffer_size,
+ g->write_buffer, g->write_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->cmd_buffer_size,
+ g->cmd_buffer, g->cmd_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->cmdtail_buffer_size,
+ g->cmdtail_buffer, g->cmdtail_buffer_handle);
+ dma_free_coherent(&pdev->dev, g->data_buffer_size,
+ g->data_buffer, g->data_buffer_handle);
+}
+
+
+/******************************************************************************
+ * Arch specific chain_* callbaks
+ ******************************************************************************/
+
+/**
+ * chain_w4r - Initialize descriptor to perform W4R operation
+ *
+ * @chain: Descriptor to use
+ * @cs: CS for this operation
+ *
+ * Due to HW bug we have to put W4R into separate desc.
+ */
+static void chain_w4r(struct stmp3xxx_dma_descriptor *chain, int cs)
+{
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_CMDWORDS(4) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDWAIT4READY |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(
+ BV_APBH_CHn_CMD_COMMAND__NO_DMA_XFER);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(
+ BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BF_GPMI_CTRL0_ADDRESS(
+ BV_GPMI_CTRL0_ADDRESS__NAND_DATA) |
+ BF_GPMI_CTRL0_CS(cs);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = 0;
+}
+
+/**
+ * chain_cmd - Initialize descriptor to push CMD to the bus
+ *
+ * @chain: Descriptor to use
+ * @cmd_handle: dma_addr_t pointer that holds the command
+ * @lba_cmd: flags and lenghth of this command.
+ * @cs: CS for this operation
+ *
+ * CLE || CLE+ALE
+ */
+static void chain_cmd(struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t cmd_handle,
+ struct lba_cmd *lba_cmd,
+ int cs)
+{
+ /* output command */
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_XFER_COUNT(lba_cmd->len) |
+ BF_APBH_CHn_CMD_CMDWORDS(3) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BF_APBH_CHn_CMD_COMMAND(BV_APBH_CHn_CMD_COMMAND__DMA_READ);
+ chain->command->cmd |= BM_APBH_CHn_CMD_CHAIN;
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF_GPMI_CTRL0_CS(cs) |
+ BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) |
+ BF_GPMI_CTRL0_XFER_COUNT(lba_cmd->len);
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->buf_ptr = cmd_handle;
+
+ if (lba_cmd->flag & FE_CMD_INC)
+ chain->command->pio_words[0] |= BM_GPMI_CTRL0_ADDRESS_INCREMENT;
+/*BUG if (lba_cmd->flag & FE_W4R) */
+/* chain->command->cmd |= BM_APBH_CHn_CMD_NANDWAIT4READY; */
+}
+
+/**
+ * chain_cmd - Initialize descriptor to read data from the bus
+ *
+ * @chain: Descriptor to use
+ * @data_handle: dma_addr_t pointer to buffer to store data
+ * @data_len: the size of the data buffer to read
+ * @cmd_handle: dma_addr_t pointer that holds the command
+ * @lba_cmd: flags and lenghth of this command.
+ * @cs: CS for this operation
+ */
+static void chain_read_data(struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t data_handle,
+ dma_addr_t data_len,
+ struct lba_cmd *lba_cmd,
+ int cs)
+{
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_XFER_COUNT(data_len) |
+ BF_APBH_CHn_CMD_CMDWORDS(4) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(
+ BV_APBH_CHn_CMD_COMMAND__DMA_WRITE);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(
+ BV_GPMI_CTRL0_COMMAND_MODE__READ) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF_GPMI_CTRL0_CS(cs) |
+ BF_GPMI_CTRL0_ADDRESS(
+ BV_GPMI_CTRL0_ADDRESS__NAND_DATA) |
+ BF_GPMI_CTRL0_XFER_COUNT(data_len);
+
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = data_handle;
+
+ if (lba_cmd->flag & FE_CMD_INC)
+ chain->command->pio_words[0] |= BM_GPMI_CTRL0_ADDRESS_INCREMENT;
+/*BUG if (lba_cmd->flag & FE_W4R) */
+/* chain->command->cmd |= BM_APBH_CHn_CMD_NANDWAIT4READY; */
+
+}
+
+/**
+ * chain_cmd - Initialize descriptor to read data from the bus
+ *
+ * @chain: Descriptor to use
+ * @data_handle: dma_addr_t pointer to buffer to read data from
+ * @data_len: the size of the data buffer to write
+ * @cmd_handle: dma_addr_t pointer that holds the command
+ * @lba_cmd: flags and lenghth of this command.
+ * @cs: CS for this operation
+ */
+static void chain_write_data(struct stmp3xxx_dma_descriptor *chain,
+ dma_addr_t data_handle,
+ int data_len,
+ struct lba_cmd *lba_cmd,
+ int cs)
+{
+
+ chain->command->cmd =
+ BF_APBH_CHn_CMD_XFER_COUNT(data_len) |
+ BF_APBH_CHn_CMD_CMDWORDS(4) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN |
+ BF_APBH_CHn_CMD_COMMAND(
+ BV_APBH_CHn_CMD_COMMAND__DMA_READ);
+ chain->command->pio_words[0] =
+ BF_GPMI_CTRL0_COMMAND_MODE(
+ BV_GPMI_CTRL0_COMMAND_MODE__WRITE) |
+ BM_GPMI_CTRL0_WORD_LENGTH |
+ BM_GPMI_CTRL0_LOCK_CS |
+ BF_GPMI_CTRL0_CS(cs) |
+ BF_GPMI_CTRL0_ADDRESS(
+ BV_GPMI_CTRL0_ADDRESS__NAND_DATA) |
+ BF_GPMI_CTRL0_XFER_COUNT(data_len);
+
+ chain->command->pio_words[1] = 0;
+ chain->command->pio_words[2] = 0;
+ chain->command->pio_words[3] = 0;
+ chain->command->buf_ptr = data_handle;
+
+ if (lba_cmd->flag & FE_CMD_INC)
+ chain->command->pio_words[0] |= BM_GPMI_CTRL0_ADDRESS_INCREMENT;
+/*BUG if (lba_cmd->flag & FE_W4R) */
+/* chain->command->cmd |= BM_APBH_CHn_CMD_NANDWAIT4READY; */
+
+}
+
+
+/******************************************************************************
+ * Interface to arch independent part
+ ******************************************************************************/
+/**
+ * queue_cmd - Setup a chain of descriptors
+ *
+ * @priv: private data passed
+ * @cmd_buf: pointer to command buffer (to be removed)
+ * @cmd_handle: dma_addr_t pointer that holds the command
+ * @cmd_len: the size of the command buffer (to be removed)
+ * @data_handle: dma_addr_t pointer to a data buffer
+ * @data_len: the size of the data buffer
+ * @cmd_flags: commands flags
+ */
+int queue_cmd(void *priv,
+ uint8_t *cmd_buf, dma_addr_t cmd_handle, int cmd_len,
+ dma_addr_t data, int data_len,
+ struct lba_cmd *cmd_flags)
+{
+
+ struct gpmi_perchip_data *g = priv;
+ unsigned long flags;
+ int idx;
+ int ret = 0;
+ struct stmp3xxx_dma_descriptor *chain ;
+ int i;
+
+ if (!g || !(cmd_buf || cmd_handle))
+ BUG();
+
+ spin_lock_irqsave(&g->lock, flags);
+
+ /* Keep it for debug purpose */
+ chain = g->d;
+ for (i = g->d_tail; i < GPMI_DMA_MAX_CHAIN; i++) {
+ chain[i].command->cmd = 0;
+ chain[i].command->buf_ptr = 0;
+ }
+ /* End */
+
+ if (!cmd_handle) {
+ if (!cmd_buf)
+ BUG();
+ memcpy(g->cmd_buffer, cmd_buf, cmd_len);
+ cmd_handle = g->cmd_buffer_handle;
+ }
+
+ idx = g->d_tail;
+ chain = &g->d[idx];
+
+ do {
+ if (!cmd_flags)
+ BUG();
+
+ if (cmd_flags->flag & FE_W4R) {
+ /* there seems to be HW BUG with W4R flag.
+ * IRQ controller hangs forever when it's combined
+ * with real operation.
+ */
+ chain_w4r(chain, g->cs);
+ chain++; idx++;
+ }
+
+
+ switch (cmd_flags->flag & F_MASK) {
+
+ case F_CMD:
+ chain_cmd(chain, cmd_handle, cmd_flags, g->cs);
+ break;
+ case F_DATA_READ:
+ chain_read_data(chain, data, data_len,
+ cmd_flags, g->cs);
+ break;
+ case F_DATA_WRITE:
+ chain_write_data(chain, data, data_len,
+ cmd_flags, g->cs);
+ break;
+ default:{
+ if (cmd_flags->flag & FE_END)
+ goto out;
+ else{
+ printk(KERN_ERR "uknown cmd\n");
+ BUG();
+ }
+ }
+ }
+
+
+ chain++; idx++;
+ cmd_handle += cmd_flags->len;
+
+ if (idx >= GPMI_DMA_MAX_CHAIN) {
+ printk(KERN_ERR "to many chains; idx is 0x%x\n", idx);
+ BUG();
+ }
+
+ } while (!((cmd_flags++)->flag & FE_END));
+
+out:
+ if (idx < GPMI_DMA_MAX_CHAIN) {
+ ret = idx;
+ g->d_tail = idx;
+ }
+ spin_unlock_irqrestore(g->lock, flags);
+
+ return ret;
+
+}
+
+dma_addr_t queue_get_cmd_handle(void *priv)
+{
+ struct gpmi_perchip_data *g = priv;
+ return g->cmd_buffer_handle;
+}
+
+uint8_t *queue_get_cmd_ptr(void *priv)
+{
+ struct gpmi_perchip_data *g = priv;
+ return g->cmd_buffer;
+}
+
+dma_addr_t queue_get_data_handle(void *priv)
+{
+ struct gpmi_perchip_data *g = priv;
+ return g->data_buffer_handle;
+}
+
+uint8_t *queue_get_data_ptr(void *priv)
+{
+ struct gpmi_perchip_data *g = priv;
+ return g->data_buffer;
+}
+
+
+/**
+ * queue_run - run the chain
+ *
+ * @priv: private data.
+ */
+int queue_run(void *priv)
+{
+ struct gpmi_perchip_data *g = priv;
+
+ if (!g->d_tail)
+ return 0;
+ stmp3xxx_dma_reset_channel(g->dma_ch);
+ stmp3xxx_dma_clear_interrupt(g->dma_ch);
+ stmp3xxx_dma_enable_interrupt(g->dma_ch);
+
+ g->d[g->d_tail-1].command->cmd &= ~(BM_APBH_CHn_CMD_NANDLOCK |
+ BM_APBH_CHn_CMD_CHAIN);
+ g->d[g->d_tail-1].command->cmd |= BM_APBH_CHn_CMD_IRQONCMPLT ;
+
+ g->d[g->d_tail-1].command->pio_words[0] &= ~BM_GPMI_CTRL0_LOCK_CS;
+
+#ifdef DEBUG
+ /*stmp37cc_dma_print_chain(&g->chain);*/
+#endif
+
+ init_completion(&g->done);
+ stmp3xxx_dma_go(g->dma_ch, g->d, 1);
+ wait_for_completion(&g->done);
+
+ g->d_tail = 0;
+
+ return 0;
+
+}
+
+/******************************************************************************
+ * Platform specific part / chard driver and misc functions
+ ******************************************************************************/
+
+
+
+static int __init lba_probe(struct platform_device *pdev)
+{
+ struct lba_data *data;
+ struct resource *r;
+ struct gpmi_perchip_data *g;
+ int err;
+
+ /* Allocate memory for the device structure (and zero it) */
+ data = kzalloc(sizeof(*data) + sizeof(struct gpmi_perchip_data),
+ GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev, "failed to allocate gpmi_nand_data\n");
+ err = -ENOMEM;
+ goto out1;
+ }
+ g_data = data;
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get resource\n");
+ err = -ENXIO;
+ goto out2;
+ }
+ data->io_base = ioremap(r->start, r->end - r->start + 1);
+ if (!data->io_base) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ err = -EIO;
+ goto out2;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!r) {
+ err = -EIO;
+ dev_err(&pdev->dev, "can't get IRQ resource\n");
+ goto out3;
+ }
+ data->irq = r->start;
+
+ platform_set_drvdata(pdev, data);
+ err = gpmi_init_hw(pdev, 1);
+ if (err)
+ goto out3;
+
+
+ err = request_irq(data->irq,
+ gpmi_irq, 0, pdev->dev.bus_id, data);
+ if (err) {
+ dev_err(&pdev->dev, "can't request GPMI IRQ\n");
+ goto out4;
+ }
+
+ g = data->nand;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "can't get DMA resource\n");
+ goto out_res;
+ }
+ g->cs = 0;
+ g->dma_ch = r->start;
+
+ err = stmp3xxx_dma_request(g->dma_ch, NULL, pdev->dev.bus_id);
+ if (err) {
+ dev_err(&pdev->dev, "can't request DMA channel 0x%x\n",
+ g->dma_ch);
+ goto out_res;
+ }
+
+ err = stmp3xxx_dma_make_chain(g->dma_ch, &g->chain,
+ g->d, ARRAY_SIZE(g->d));
+ if (err) {
+ dev_err(&pdev->dev, "can't setup DMA chain\n");
+ stmp3xxx_dma_release(g->dma_ch);
+ goto out_res;
+ }
+
+ g->cmd_buffer_size = GPMI_CMD_BUF_SZ;
+ g->cmdtail_buffer_size = GPMI_CMD_BUF_SZ;
+ g->write_buffer_size = GPMI_WRITE_BUF_SZ;
+ g->data_buffer_size = GPMI_DATA_BUF_SZ;
+ g->oob_buffer_size = GPMI_OOB_BUF_SZ;
+
+ err = gpmi_alloc_buffers(pdev, g);
+ if (err) {
+ dev_err(&pdev->dev, "can't setup buffers\n");
+ stmp3xxx_dma_free_chain(&g->chain);
+ stmp3xxx_dma_release(g->dma_ch);
+ goto out_res;
+ }
+
+ g->dev = pdev;
+ g->chip.priv = g;
+ g->index = 0;
+ g->timing = gpmi_safe_timing;
+
+ g->cmd_buffer_sz =
+ g->write_buffer_sz =
+ g->data_buffer_sz =
+ 0;
+ g->valid = !0; /* mark the data as valid */
+
+
+ lba_core_init(data);
+
+ return 0;
+
+out_res:
+ free_irq(data->irq, data);
+out4:
+ gpmi_release_hw(pdev);
+out3:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(data->io_base);
+out2:
+ kfree(data);
+out1:
+ return err;
+}
+
+static int gpmi_suspend(struct platform_device *pdev, pm_message_t pm)
+{
+ struct lba_data *data = platform_get_drvdata(pdev);
+ int err;
+
+ printk(KERN_INFO "%s: %d\n", __func__, __LINE__);
+ err = lba_core_suspend(pdev, data);
+ if (!err)
+ gpmi_release_hw(pdev);
+
+ return err;
+}
+
+static int gpmi_resume(struct platform_device *pdev)
+{
+ struct lba_data *data = platform_get_drvdata(pdev);
+ int r;
+
+ printk(KERN_INFO "%s: %d\n", __func__, __LINE__);
+ r = gpmi_init_hw(pdev, 1);
+ lba_core_resume(pdev, data);
+ return r;
+}
+
+/**
+ * gpmi_nand_remove - remove a GPMI device
+ *
+ */
+static int __devexit lba_remove(struct platform_device *pdev)
+{
+ struct lba_data *data = platform_get_drvdata(pdev);
+ int i;
+
+ lba_core_remove(data);
+ gpmi_release_hw(pdev);
+ free_irq(data->irq, data);
+
+ for (i = 0; i < max_chips; i++) {
+ if (!data->nand[i].valid)
+ continue;
+ gpmi_free_buffers(pdev, &data->nand[i]);
+ stmp3xxx_dma_free_chain(&data->nand[i].chain);
+ stmp3xxx_dma_release(data->nand[i].dma_ch);
+ }
+ iounmap(data->io_base);
+ kfree(data);
+
+ return 0;
+}
+
+static struct platform_driver lba_driver = {
+ .probe = lba_probe,
+ .remove = __devexit_p(lba_remove),
+ .driver = {
+ .name = "gpmi",
+ .owner = THIS_MODULE,
+ },
+ .suspend = gpmi_suspend,
+ .resume = gpmi_resume,
+
+};
+
+
+static int __init lba_init(void)
+{
+ return platform_driver_register(&lba_driver);
+}
+
+static void lba_exit(void)
+{
+ platform_driver_unregister(&lba_driver);
+}
+
+module_init(lba_init);
+module_exit(lba_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/lba/gpmi.h b/drivers/mtd/nand/lba/gpmi.h
new file mode 100644
index 000000000000..a647c948040f
--- /dev/null
+++ b/drivers/mtd/nand/lba/gpmi.h
@@ -0,0 +1,103 @@
+/*
+ * Freescale STMP37XX/STMP378X GPMI (General-Purpose-Media-Interface)
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __DRIVERS_GPMI_H
+#define __DRIVERS_GPMI_H
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <mach/stmp3xxx.h>
+#include <mach/dma.h>
+
+#include <mach/gpmi.h>
+#include <mach/regs-gpmi.h>
+#include <mach/regs-apbh.h>
+#ifdef CONFIG_MTD_NAND_GPMI_BCH
+#include <mach/regs-bch.h>
+#endif
+
+
+struct gpmi_nand_timing {
+ u8 data_setup;
+ u8 data_hold;
+ u8 address_setup;
+ u8 dsample_time;
+};
+
+#define GPMI_DMA_MAX_CHAIN 20 /* max DMA commands in chain */
+
+#define GPMI_CMD_BUF_SZ 10
+#define GPMI_DATA_BUF_SZ 4096
+#define GPMI_WRITE_BUF_SZ 4096
+#define GPMI_OOB_BUF_SZ 218
+
+
+struct gpmi_perchip_data {
+ int valid;
+ struct nand_chip chip;
+ struct platform_device *dev;
+ int index;
+
+ spinlock_t lock; /* protect chain operations */
+ struct stmp37xx_circ_dma_chain chain;
+ struct stmp3xxx_dma_descriptor d[GPMI_DMA_MAX_CHAIN];
+ int d_tail;
+
+ struct completion done;
+
+ u8 *cmd_buffer;
+ dma_addr_t cmd_buffer_handle;
+ int cmd_buffer_size, cmd_buffer_sz;
+
+ u8 *cmdtail_buffer;
+ dma_addr_t cmdtail_buffer_handle;
+ int cmdtail_buffer_size, cmdtail_buffer_sz;
+
+ u8 *write_buffer;
+ dma_addr_t write_buffer_handle;
+ int write_buffer_size, write_buffer_sz;
+
+ u8 *data_buffer;
+ dma_addr_t data_buffer_handle;
+ u8 *data_buffer_cptr;
+ int data_buffer_size, data_buffer_sz, bytes2read;
+
+ u8 *oob_buffer;
+ dma_addr_t oob_buffer_handle;
+ int oob_buffer_size;
+
+ int cs;
+ unsigned dma_ch;
+
+ int ecc_oob_bytes, oob_free;
+
+ struct gpmi_nand_timing timing;
+
+ void *p2w, *oob2w, *p2r, *oob2r;
+ size_t p2w_size, oob2w_size, p2r_size, oob2r_size;
+ dma_addr_t p2w_dma, oob2w_dma, p2r_dma, oob2r_dma;
+ unsigned read_memcpy:1, write_memcpy:1,
+ read_oob_memcpy:1, write_oob_memcpy:1;
+};
+
+
+extern struct gpmi_nand_timing gpmi_safe_timing;
+
+
+#endif
diff --git a/drivers/mtd/nand/lba/lba-blk.c b/drivers/mtd/nand/lba/lba-blk.c
new file mode 100644
index 000000000000..1991fad4f05d
--- /dev/null
+++ b/drivers/mtd/nand/lba/lba-blk.c
@@ -0,0 +1,345 @@
+/*
+ * Freescale STMP37XX/STMP378X LBA/block driver
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/slab.h> /* kmalloc() */
+#include <linux/fs.h> /* everything... */
+#include <linux/errno.h> /* error codes */
+#include <linux/kthread.h>
+#include <linux/timer.h>
+#include <linux/types.h> /* size_t */
+#include <linux/fcntl.h> /* O_ACCMODE */
+#include <linux/hdreg.h> /* HDIO_GETGEO */
+#include <linux/kdev_t.h>
+#include <linux/vmalloc.h>
+#include <linux/genhd.h>
+#include <linux/blkdev.h>
+#include <linux/buffer_head.h> /* invalidate_bdev */
+#include <linux/bio.h>
+#include <linux/dma-mapping.h>
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+#include "lba.h"
+
+static int lba_major;
+
+#define LBA_NAME "lba"
+
+#if 0
+#define TAG() printk(KERNE_ERR "%s: %d\n", __func__, __LINE__)
+#else
+#define TAG()
+#endif
+
+/*
+ * The internal representation of our device.
+ */
+struct lba_blk_dev {
+ int size; /* Device size in sectors */
+ spinlock_t lock; /* For mutual exclusion */
+ int users;
+ struct request_queue *queue; /* The device request queue */
+ struct gendisk *gd; /* The gendisk structure */
+ struct lba_data *data; /* pointer from lba core */
+
+ struct task_struct *thread;
+ struct bio *bio_head;
+ struct bio *bio_tail;
+ wait_queue_head_t wait_q;
+ struct semaphore busy;
+
+};
+
+static struct lba_blk_dev *g_lba_blk;
+
+static void blk_add_bio(struct lba_blk_dev *dev, struct bio *bio);
+
+
+/*
+ * Transfer a single BIO.
+ */
+static int lba_blk_xfer_bio(struct lba_blk_dev *dev, struct bio *bio)
+{
+ int i;
+ struct bio_vec *bvec;
+ sector_t sector = bio->bi_sector;
+ enum dma_data_direction dir;
+ int status = 0;
+ int (*lba_xfer)(void *priv,
+ unsigned int sector,
+ unsigned int count,
+ void *buffer,
+ dma_addr_t handle);
+
+ if (bio_data_dir(bio) == WRITE) {
+ lba_xfer = lba_write_sectors;
+ dir = DMA_TO_DEVICE;
+ } else {
+ lba_xfer = lba_read_sectors;
+ dir = DMA_FROM_DEVICE;
+ }
+
+ /* Fixme: merge segments */
+ bio_for_each_segment(bvec, bio, i) {
+ void *buffer = page_address(bvec->bv_page);
+ dma_addr_t handle ;
+ if (!buffer)
+ BUG();
+ buffer += bvec->bv_offset;
+ handle = dma_map_single(&dev->data->nand->dev->dev,
+ buffer,
+ bvec->bv_len,
+ dir);
+ status = lba_xfer(dev->data->nand, sector,
+ bvec->bv_len >> 9,
+ buffer,
+ handle);
+
+ dma_unmap_single(&dev->data->nand->dev->dev,
+ handle,
+ bvec->bv_len,
+ dir);
+ if (status)
+ break;
+
+ sector += bio_cur_sectors(bio);
+ }
+
+ return status;
+}
+
+
+/*
+ * The direct make request version.
+ */
+static int lba_make_request(struct request_queue *q, struct bio *bio)
+{
+ struct lba_blk_dev *dev = q->queuedata;
+
+ blk_add_bio(dev, bio);
+ return 0;
+}
+
+/*
+ * Open and close.
+ */
+
+static int lba_blk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct lba_blk_dev *dev = bdev->bd_disk->private_data;
+
+ TAG();
+
+ spin_lock_irq(&dev->lock);
+ dev->users++;
+ spin_unlock_irq(&dev->lock);
+ return 0;
+}
+
+
+static int lba_blk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct lba_blk_dev *dev = gd->private_data;
+
+ spin_lock(&dev->lock);
+ dev->users--;
+ spin_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int lba_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+ /*
+ * get geometry: we have to fake one... trim the size to a
+ * multiple of 2048 (1M): tell we have 32 sectors, 64 heads,
+ * whatever cylinders.
+ */
+ geo->heads = 1 << 6;
+ geo->sectors = 1 << 5;
+ geo->cylinders = get_capacity(bdev->bd_disk) >> 11;
+ return 0;
+}
+
+/*
+ * Add bio to back of pending list
+ */
+static void blk_add_bio(struct lba_blk_dev *dev, struct bio *bio)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->bio_tail) {
+ dev->bio_tail->bi_next = bio;
+ dev->bio_tail = bio;
+ } else
+ dev->bio_head = dev->bio_tail = bio;
+ wake_up(&dev->wait_q);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ * Grab first pending buffer
+ */
+static struct bio *blk_get_bio(struct lba_blk_dev *dev)
+{
+ struct bio *bio;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ bio = dev->bio_head;
+ if (bio) {
+ if (bio == dev->bio_tail) {
+ dev->bio_tail = NULL;
+ dev->bio_head = NULL;
+ }
+ dev->bio_head = bio->bi_next;
+ bio->bi_next = NULL;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return bio;
+}
+
+static int lba_thread(void *data)
+{
+ struct lba_blk_dev *dev = data;
+ struct bio *bio;
+ int status;
+
+ set_user_nice(current, -20);
+
+ while (!kthread_should_stop() || dev->bio_head) {
+
+ wait_event_interruptible(dev->wait_q,
+ dev->bio_head || kthread_should_stop());
+
+ if (!dev->bio_head)
+ continue;
+
+ if (lba_core_lock_mode(dev->data, LBA_MODE_MDP))
+ continue;
+
+ bio = blk_get_bio(dev);
+ status = lba_blk_xfer_bio(dev, bio);
+ bio_endio(bio, status);
+
+ lba_core_unlock_mode(dev->data);
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * The device operations structure.
+ */
+static struct block_device_operations lba_blk_ops = {
+ .owner = THIS_MODULE,
+ .open = lba_blk_open,
+ .release = lba_blk_release,
+ .getgeo = lba_getgeo,
+};
+
+
+int lba_blk_init(struct lba_data *data)
+{
+
+ struct lba_blk_dev *dev;
+ int err;
+ if (!data)
+ BUG();
+
+ printk(KERN_INFO "LBA block driver v0.1\n");
+ lba_major = LBA_MAJOR;
+ dev = g_lba_blk = kzalloc(sizeof(struct lba_blk_dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->data = data;
+ register_blkdev(lba_major, "lba");
+
+ spin_lock_init(&dev->lock);
+ init_waitqueue_head(&dev->wait_q);
+ sema_init(&dev->busy, 1);
+
+ dev->queue = blk_alloc_queue(GFP_KERNEL);
+ if (!dev->queue)
+ goto out2;
+ blk_queue_make_request(dev->queue, lba_make_request);
+ /*dev->queue->unplug_fn = lba_unplug_device;*/
+ blk_queue_hardsect_size(dev->queue, 512);
+
+ dev->queue->queuedata = dev;
+ dev->gd = alloc_disk(32);
+ if (!dev->gd) {
+ printk(KERN_ERR "failed to alloc disk\n");
+ goto out3;
+ }
+ dev->size = data->mdp_size ;
+ printk(KERN_INFO "%s: set capacity of the device to 0x%x\n",
+ __func__, dev->size);
+ dev->gd->major = lba_major;
+ dev->gd->first_minor = 0;
+ dev->gd->fops = &lba_blk_ops;
+ dev->gd->queue = dev->queue;
+ dev->gd->private_data = dev;
+ snprintf(dev->gd->disk_name, 8, LBA_NAME);
+ set_capacity(dev->gd, dev->size);
+
+ dev->thread = kthread_create(lba_thread, dev, "lba-%d", 1);
+ if (IS_ERR(dev->thread)) {
+ err = PTR_ERR(dev->thread);
+ goto out3;
+ }
+ wake_up_process(dev->thread);
+
+ add_disk(dev->gd);
+
+
+ TAG();
+
+ return 0;
+out3:
+out2:
+ unregister_blkdev(lba_major, "lba");
+ return -ENOMEM;
+}
+
+int lba_blk_remove(struct lba_data *data)
+{
+
+ struct lba_blk_dev *dev = g_lba_blk;
+
+ del_gendisk(dev->gd);
+ kthread_stop(dev->thread);
+ blk_cleanup_queue(dev->queue);
+ put_disk(dev->gd);
+
+ unregister_blkdev(lba_major, LBA_NAME);
+ kfree(dev);
+ return 0;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_BLOCKDEV_MAJOR(254);
diff --git a/drivers/mtd/nand/lba/lba-core.c b/drivers/mtd/nand/lba/lba-core.c
new file mode 100644
index 000000000000..cdf28ca42b2a
--- /dev/null
+++ b/drivers/mtd/nand/lba/lba-core.c
@@ -0,0 +1,619 @@
+/*
+ * Freescale STMP37XX/STMP378X LBA/core driver
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/dma-mapping.h>
+#include <linux/ctype.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <mach/stmp3xxx.h>
+#include <mach/dma.h>
+#include "gpmi.h"
+#include "lba.h"
+
+#define LBA_SELFPM_TIMEOUT 2000 /* msecs */
+dma_addr_t g_cmd_handle;
+dma_addr_t g_data_handle;
+uint8_t *g_data_buffer;
+uint8_t *g_cmd_buffer;
+
+uint8_t lba_get_status1(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x70 } ;
+ struct lba_cmd lba_flags[] = {
+ {1 , F_CMD | FE_W4R},
+ {0, F_DATA_READ | FE_END},
+ };
+ *g_data_buffer = 0;
+ queue_cmd(priv, cmd_buf, 0, 1, g_data_handle, 1, lba_flags);
+ queue_run(priv);
+ return *g_data_buffer;
+}
+
+
+int lba_wait_for_ready(void *priv)
+{
+ int stat;
+ unsigned long j_start = jiffies;
+
+ stat = lba_get_status1(priv);
+ if ((stat & 0x60) != 0x60) {
+ while (((stat & 0x60) != 0x60) &&
+ (jiffies - j_start < msecs_to_jiffies(2000))) {
+ schedule();
+ stat = lba_get_status1(priv);
+ }
+ }
+ if (stat != 0x60)
+ return stat;
+
+ return 0;
+}
+
+int lba_write_sectors(void *priv, unsigned int sector, unsigned int count,
+ void *buffer, dma_addr_t handle)
+{
+ uint8_t cmd_buf[] = {
+ 0x80,
+ count & 0xff, (count >> 8) & 0xff, /* Count */
+ (sector & 0xff), (sector >> 8) & 0xff, /* Address */
+ (sector >> 16) & 0xff, (sector >> 24) & 0xff, /* Addres */
+ /* Data goes here */
+ 0x10
+ };
+
+ struct lba_cmd flags_t1[] = { /* Transmition mode 1/A */
+ {7 , F_CMD | FE_CMD_INC | FE_W4R},
+ {0, F_DATA_WRITE},
+ {1 , F_CMD | FE_END}
+ };
+
+ if (count > 8)
+ return -EINVAL;
+
+ if (lba_wait_for_ready(priv))
+ return -EIO;
+
+ while (count) {
+ int cnt = (count < 8) ? count : 8;
+ int data_len = cnt * 512;
+
+ queue_cmd(priv, cmd_buf, 0, 8,
+ handle, data_len, flags_t1);
+
+ handle += data_len;
+ count -= cnt;
+
+ }
+
+ queue_run(priv);
+
+ return count;
+
+}
+
+int lba_read_sectors(void *priv, unsigned int sector, unsigned int count,
+ void *buffer, dma_addr_t handle)
+{
+
+ int data_len;
+ int cnt;
+ uint8_t cmd_buf[] = {
+ 0x00,
+ count & 0xff, (count >> 8) & 0xff, /* Count */
+ (sector & 0xff), (sector >> 8) & 0xff, /* Addr */
+ (sector >> 16) & 0xff, (sector >> 24) & 0xff, /* Addr */
+ 0x30
+ /* Data goes here <data> */
+
+ };
+
+ struct lba_cmd flags_r3[] = { /* Read mode 3/A */
+ {7 , F_CMD | FE_CMD_INC | FE_W4R},
+ {1 , F_CMD },
+ {0 , F_DATA_READ | FE_W4R | FE_END },
+ };
+ struct lba_cmd flags_r3c[] = { /* Read mode 3/A */
+ {0 , F_DATA_READ | FE_W4R | FE_END },
+ };
+ struct lba_cmd *flags = flags_r3;
+ int flags_len = 8;
+
+ if (count > 8)
+ return -EINVAL;
+
+ if (lba_wait_for_ready(priv))
+ return -EIO;
+
+ while (count) {
+ cnt = (count < 8) ? count : 8;
+ data_len = cnt * 512;
+ queue_cmd(priv, cmd_buf, 0, flags_len, handle, data_len, flags);
+ handle += data_len;
+ count -= cnt;
+ flags = flags_r3c;
+ flags_len = 0;
+ }
+
+ queue_run(priv);
+
+ return count;
+
+}
+
+
+uint8_t lba_get_id1(void *priv, uint8_t *ret_buffer)
+{
+ uint8_t cmd_buf[] = { 0x90 , 0x00, /* Data read 5bytes*/ };
+ struct lba_cmd lba_flags[] = {
+ {2 , F_CMD | FE_CMD_INC | FE_W4R},
+ {0, F_DATA_READ | FE_END},
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 2, g_data_handle, 5, lba_flags);
+ queue_run(priv);
+ memcpy(ret_buffer, g_data_buffer, 5);
+
+ return 0;
+}
+
+uint8_t lba_get_id2(void *priv, uint8_t *ret_buffer)
+{
+ uint8_t cmd_buf[] = { 0x92 , 0x00, /* Data read 5bytes*/ };
+ struct lba_cmd lba_flags[] = {
+ {2 , F_CMD | FE_CMD_INC | FE_W4R},
+ {0, F_DATA_READ | FE_END},
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 2, g_data_handle, 5, lba_flags);
+ queue_run(priv);
+ memcpy(ret_buffer, g_data_buffer, 5);
+ return 0;
+}
+
+uint8_t lba_get_status2(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x71 };
+ struct lba_cmd lba_flags[] = {
+ {1 , F_CMD | FE_CMD_INC | FE_W4R},
+ {0 , F_DATA_READ | FE_END},
+ };
+ *g_data_buffer = 0;
+ queue_cmd(priv, cmd_buf, 0, 1, g_data_handle, 1, lba_flags);
+ queue_run(priv);
+ return *g_data_buffer;
+}
+
+static uint8_t lba_parse_status2(void *priv)
+{
+ uint8_t stat;
+
+ stat = lba_get_status2(priv);
+ printk(KERN_INFO "Status2:|");
+ if (stat & 0x40)
+ printk(" C.PAR.ERR |"); /* no KERN_ here */
+ if (stat & 0x20)
+ printk(" NO spare |");
+ if (stat & 0x10)
+ printk(" ADDR OoRange |");
+ if (stat & 0x8)
+ printk(" high speed |");
+ if ((stat & 0x6) == 6)
+ printk(" MDP |");
+ if ((stat & 0x6) == 4)
+ printk(" VFP |");
+ if ((stat & 0x6) == 2)
+ printk(" PNP |");
+ if (stat & 1)
+ printk(" PSW |");
+
+ printk("\n");
+ return 0;
+}
+
+
+int lba_2mdp(void *priv)
+{
+ uint8_t cmd_buf[] = { 0xFC };
+ struct lba_cmd lba_flags[] = {
+ {1 , F_CMD | FE_W4R | FE_END}
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 1, 0, 0, lba_flags);
+ queue_run(priv);
+ return 0;
+}
+
+void _lba_misc_cmd_set(void *priv, uint8_t *cmd_buf)
+{
+ struct lba_cmd lba_flags[] = {
+ {6 , F_CMD | FE_CMD_INC | FE_W4R },
+ {1 , F_CMD | FE_END },
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 7, 0, 0, lba_flags);
+ queue_run(priv);
+}
+
+uint8_t _lba_misc_cmd_get(void *priv, uint8_t *cmd_buf)
+{
+ struct lba_cmd lba_flags[] = {
+ {6 , F_CMD | FE_CMD_INC | FE_W4R },
+ {1 , F_CMD },
+ {0 , F_DATA_READ | FE_W4R | FE_END}
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 7, g_data_handle, 1, lba_flags);
+ queue_run(priv);
+ return *g_data_buffer;
+}
+void lba_mdp2vfp(void *priv, uint8_t pass[2])
+{
+ uint8_t cmd_buf[] = { 0x0, 0xbe, pass[0], pass[1], 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+void lba_bcm2vfp(void *priv, uint8_t pass[2])
+{
+ lba_mdp2vfp(priv, pass);
+}
+
+void lba_powersave_enable(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xba, 0, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+void lba_powersave_disable(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xbb, 0, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+void lba_highspeed_enable(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xbc, 0, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+void lba_highspeed_disable(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xbd, 0, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+void lba_prot1_set(void *priv, uint8_t mode)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xa2, mode, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+void lba_prot2_set(void *priv, uint8_t mode)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xa3, mode, 0, 0, 0, 0x57 };
+ _lba_misc_cmd_set(priv, cmd_buf);
+}
+
+uint8_t lba_prot1_get(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xb2, 0, 0, 0, 0, 0x57 };
+ return _lba_misc_cmd_get(priv, cmd_buf);
+}
+
+uint8_t lba_prot2_get(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xb3, 0, 0, 0, 0, 0x57 };
+ return _lba_misc_cmd_get(priv, cmd_buf);
+}
+
+uint64_t lba_mdp_size_get(void *priv)
+{
+ uint8_t cmd_buf[] = { 0x0, 0xb0, 0, 0, 0, 0, 0x57 };
+ struct lba_cmd lba_flags[] = {
+ {6 , F_CMD | FE_CMD_INC | FE_W4R },
+ {1 , F_CMD },
+ {0 , F_DATA_READ | FE_W4R | FE_END}
+ };
+
+ memset((void *)g_data_buffer, 0, 8);
+ queue_cmd(priv, cmd_buf, 0, 7, g_data_handle, 5, lba_flags);
+ queue_run(priv);
+ return le64_to_cpu(*(long long *)g_data_buffer);
+}
+
+void lba_cache_flush(void *priv)
+{
+ uint8_t cmd_buf[] = { 0xF9 };
+ struct lba_cmd lba_flags[] = {
+ {1 , F_CMD | FE_W4R },
+ {0 , FE_W4R | FE_END}
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 7, g_data_handle, 5, lba_flags);
+ queue_run(priv);
+}
+
+void lba_reboot(void *priv)
+{
+ uint8_t cmd_buf[] = { 0xFD };
+ struct lba_cmd lba_flags[] = {
+ {1 , F_CMD | FE_W4R },
+ {0 , FE_W4R | FE_END}
+ };
+
+ queue_cmd(priv, cmd_buf, 0, 7, g_data_handle, 5, lba_flags);
+ queue_run(priv);
+}
+
+void lba_def_state(void *priv)
+{
+ lba_wait_for_ready(priv);
+ lba_reboot(priv);
+
+ lba_wait_for_ready(priv);
+ lba_parse_status2(priv);
+
+ lba_wait_for_ready(priv);
+ lba_2mdp(priv);
+
+ lba_wait_for_ready(priv);
+ lba_prot1_set(priv, LBA_T_SIZE8); /* 512 * 8 */
+
+ lba_wait_for_ready(priv);
+/* Type C read; Type A write; */
+ lba_prot2_set(priv, LBA_P_WRITE_A | LBA_P_READ_C);
+}
+
+/*
+ * Should be called with mode locked
+ */
+void lba_core_setvfp_passwd(struct lba_data *data, uint8_t pass[2])
+{
+ memcpy(data->pass, pass, 2);
+}
+
+int lba_core_lock_mode(struct lba_data *data, int mode)
+{
+ void *priv = &data->nand;
+
+ if (down_interruptible(&data->mode_lock))
+ return -EAGAIN;
+ /*
+ * MDP and VFP are the only supported
+ * modes for now.
+ */
+ if ((mode != LBA_MODE_MDP) &&
+ (mode != LBA_MODE_VFP)) {
+ up(&data->mode_lock);
+ return -EINVAL;
+ }
+
+ while ((data->mode & LBA_MODE_MASK) == LBA_MODE_SUSP) {
+ up(&data->mode_lock);
+
+ if (wait_event_interruptible(
+ data->suspend_q,
+ (data->mode & LBA_MODE_MASK) != LBA_MODE_SUSP))
+ return -EAGAIN;
+
+ if (down_interruptible(&data->mode_lock))
+ return -EAGAIN;
+
+ data->last_access = jiffies;
+ }
+
+ if (data->mode & LBA_MODE_SELFPM) {
+ queue_plug(data);
+ data->mode &= ~LBA_MODE_SELFPM;
+ }
+
+ if (mode == data->mode)
+ return 0;
+
+ /*
+ * mode = VFP || MDP only
+ * Revisit when more modes are added
+ */
+ switch (data->mode) {
+ case LBA_MODE_RST:
+ case LBA_MODE_PNR:
+ case LBA_MODE_BCM:
+ lba_def_state(priv);
+ if (mode == LBA_MODE_MDP) {
+ data->mode = LBA_MODE_MDP;
+ break;
+ }
+ /*no break -> fall down to set VFP mode*/
+ case LBA_MODE_MDP:
+ lba_wait_for_ready(priv);
+ lba_mdp2vfp(priv, data->pass);
+ data->mode = LBA_MODE_VFP;
+ break;
+ case LBA_MODE_VFP:
+ lba_wait_for_ready(priv);
+ lba_2mdp(priv);
+ data->mode = LBA_MODE_MDP;
+ break;
+ default:
+ up(&data->mode_lock);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int lba_core_unlock_mode(struct lba_data *data)
+{
+ data->last_access = jiffies;
+ up(&data->mode_lock);
+ wake_up(&data->selfpm_q);
+ return 0;
+}
+
+static int selfpm_timeout_expired(struct lba_data *data)
+{
+ return jiffies_to_msecs(jiffies - data->last_access) > 2000;
+}
+
+static int lba_selfpm_thread(void *d)
+{
+ struct lba_data *data = d;
+
+ set_user_nice(current, -5);
+
+ while (!kthread_should_stop()) {
+
+ if (wait_event_interruptible(data->selfpm_q,
+ kthread_should_stop() ||
+ !(data->mode & LBA_MODE_SELFPM)))
+ continue;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(LBA_SELFPM_TIMEOUT));
+
+ if (down_trylock(&data->mode_lock))
+ continue;
+
+ if (!selfpm_timeout_expired(data)) {
+ up(&data->mode_lock);
+ continue;
+ }
+ data->mode |= LBA_MODE_SELFPM;
+ lba_wait_for_ready((void *)data->nand);
+ lba_cache_flush((void *)data->nand);
+ queue_release(data);
+ up(&data->mode_lock);
+
+ }
+
+ return 0;
+}
+
+int lba_core_init(struct lba_data *data)
+{
+ uint8_t id_buf[5];
+ uint8_t capacity;
+ uint8_t id1_template[5] = {0x98, 0xDC, 0x00, 0x15, 0x00};
+ uint8_t id2_template[5] = {0x98, 0x21, 0x00, 0x55, 0xAA};
+ void *priv = (void *)data->nand;
+
+
+ g_data = data;
+ g_cmd_handle = queue_get_cmd_handle(priv);
+ g_data_handle = queue_get_data_handle(priv);
+ g_data_buffer = queue_get_data_ptr(priv);
+ g_cmd_buffer = queue_get_cmd_ptr(priv);
+
+ spin_lock_init(&data->lock);
+ sema_init(&data->mode_lock, 1);
+ init_waitqueue_head(&data->suspend_q);
+ init_waitqueue_head(&data->selfpm_q);
+
+
+ lba_get_id1(data->nand, id_buf);
+ if (!memcmp(id_buf, id1_template, 5))
+ printk(KERN_INFO
+ "LBA: Found LBA/SLC NAND emulated ID\n");
+ else
+ return -ENODEV;
+
+ lba_get_id2(data->nand, id_buf);
+ capacity = id_buf[2];
+ id_buf[2] = 0;
+
+ if (memcmp(id_buf, id2_template, 5)) {
+ printk(KERN_INFO
+ "LBA: Uknown LBA device\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO
+ "LBA: Found %dGbytes LBA NAND device\n",
+ 1 << capacity);
+
+ lba_wait_for_ready(priv);
+ lba_parse_status2(priv);
+
+ lba_def_state(priv);
+ data->mode = LBA_MODE_MDP;
+
+ g_data->pnp_size = 0xff;
+ g_data->vfp_size = 16384;
+
+ lba_wait_for_ready(priv);
+ g_data->mdp_size = lba_mdp_size_get(priv);
+
+ lba_wait_for_ready(priv);
+ /*lba_powersave_enable(priv);*/
+ /*lba_highspeed_enable(priv);*/
+
+ lba_wait_for_ready(priv);
+ lba_parse_status2(priv);
+
+ data->thread = kthread_create(lba_selfpm_thread,
+ data, "lba-selfpm-%d", 1);
+ if (IS_ERR(data->thread))
+ return PTR_ERR(data->thread);
+
+ lba_blk_init(g_data);
+
+ wake_up_process(data->thread);
+ return 0;
+
+};
+
+int lba_core_remove(struct lba_data *data)
+{
+ kthread_stop(data->thread);
+ lba_blk_remove(data);
+ lba_wait_for_ready((void *)data->nand);
+ lba_cache_flush((void *)data->nand);
+ return 0;
+}
+
+int lba_core_suspend(struct platform_device *pdev, struct lba_data *data)
+{
+ BUG_ON((data->mode & 0xffff) == LBA_MODE_SUSP);
+ if (down_interruptible(&data->mode_lock))
+ return -EAGAIN;
+ if (data->mode & LBA_MODE_SELFPM)
+ queue_plug(data);
+
+ data->mode = LBA_MODE_SUSP | LBA_MODE_SELFPM;
+ up(&data->mode_lock);
+ lba_wait_for_ready((void *)data->nand);
+ lba_cache_flush((void *)data->nand);
+ return 0;
+}
+
+int lba_core_resume(struct platform_device *pdev, struct lba_data *data)
+{
+ BUG_ON((data->mode & 0xffff) != LBA_MODE_SUSP);
+ lba_def_state((void *)data->nand);
+ data->last_access = jiffies;
+ data->mode = LBA_MODE_MDP;
+ wake_up(&data->suspend_q);
+ return 0;
+}
diff --git a/drivers/mtd/nand/lba/lba.h b/drivers/mtd/nand/lba/lba.h
new file mode 100644
index 000000000000..4ad37ecbc44f
--- /dev/null
+++ b/drivers/mtd/nand/lba/lba.h
@@ -0,0 +1,141 @@
+/*
+ * Freescale STMP37XX/STMP378X LBA interface
+ *
+ * Author: Dmitrij Frasenyak <sed@embeddedalley.com>
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __INCLUDE_LBA_H__
+#define __INCLUDE_LBA_H__
+
+
+#include <linux/spinlock.h>
+#include <linux/kthread.h>
+#include "gpmi.h"
+
+struct lba_cmd {
+ uint8_t len;
+#define F_MASK 0x0f
+#define F_ALE 0x01
+#define F_CMD 0x02
+#define F_DATA_READ 0x04
+#define F_DATA_WRITE 0x08
+
+#define FE_W4R 0x10
+#define FE_CMD_INC 0x20
+#define FE_END 0x40
+
+ uint8_t flag;
+};
+
+#define LBA_P_READ_A 0
+#define LBA_P_READ_B 2
+#define LBA_P_READ_C 3
+#define LBA_P_WRITE_A 0
+#define LBA_P_WRITE_B 4
+#define LBA_T_SIZE1 1
+#define LBA_T_SIZE4 2
+#define LBA_T_SIZE8 4
+#define LBA_T_CRC (1 << 6)
+#define LBA_T_ECC_CHECK (2 << 6)
+#define LBA_T_ECC_CORRECT (3 << 6)
+
+struct lba_data {
+ void __iomem *io_base;
+ struct clk *clk;
+ int irq;
+
+ spinlock_t lock;
+ int use_count;
+ int mode;
+ struct semaphore mode_lock;
+#define LBA_MODE_MASK 0x0000ffff
+#define LBA_FLAG_MASK 0xffff0000
+#define LBA_MODE_RST 0
+#define LBA_MODE_PNR 1
+#define LBA_MODE_BCM 2
+#define LBA_MODE_MDP 3
+#define LBA_MODE_VFP 4
+#define LBA_MODE_SUSP 5
+#define LBA_MODE_SELFPM 0x80000000
+ wait_queue_head_t suspend_q;
+ wait_queue_head_t selfpm_q;
+ struct task_struct *thread;
+ long long last_access;
+ /* PNR specific */
+ /* BCM specific */
+ /* VFP specific */
+ uint8_t pass[2];
+
+ /* Size of the partiotions: pages for PNP; sectors for others */
+ unsigned int pnp_size;
+ unsigned int vfp_size;
+ long long mdp_size;
+ void *priv;
+ /*should be last*/
+ struct gpmi_perchip_data nand[0];
+
+};
+
+extern struct lba_data *g_data;
+
+void stmp37cc_dma_print_chain(struct stmp37xx_circ_dma_chain *chain);
+
+int lba_blk_init(struct lba_data *data);
+int lba_blk_remove(struct lba_data *data);
+int lba_blk_suspend(struct platform_device *pdev, struct lba_data *data);
+int lba_blk_resume(struct platform_device *pdev, struct lba_data *data);
+
+
+int lba_core_init(struct lba_data *data);
+int lba_core_remove(struct lba_data *data);
+int lba_core_suspend(struct platform_device *pdev, struct lba_data *data);
+int lba_core_resume(struct platform_device *pdev, struct lba_data *data);
+int lba_core_lock_mode(struct lba_data *data, int mode);
+int lba_core_unlock_mode(struct lba_data *data);
+
+int lba_write_sectors(void *priv, unsigned int sector, unsigned int count,
+ void *buffer, dma_addr_t handle);
+int lba_read_sectors(void *priv, unsigned int sector, unsigned int count,
+ void *buffer, dma_addr_t handle);
+void lba_protocol1_set(void *priv, uint8_t param);
+uint8_t lba_protocol1_get(void *priv);
+uint8_t lba_get_status1(void *priv);
+uint8_t lba_get_status2(void *priv);
+
+uint8_t lba_get_id1(void *priv, uint8_t *ret_buffer);
+uint8_t lba_get_id2(void *priv, uint8_t *);
+
+
+int queue_cmd(void *priv,
+ uint8_t *cmd_buf, dma_addr_t cmd_handle, int cmd_len,
+ dma_addr_t data, int data_len,
+ struct lba_cmd *cmd_flags);
+
+int queue_run(void *priv);
+
+dma_addr_t queue_get_cmd_handle(void *priv);
+
+uint8_t *queue_get_cmd_ptr(void *priv);
+
+dma_addr_t queue_get_data_handle(void *priv);
+
+uint8_t *queue_get_data_ptr(void *priv);
+
+void queue_plug(struct lba_data *data);
+void queue_release(struct lba_data *data);
+
+
+#endif
+
diff --git a/drivers/mtd/nand/mxc_nd.c b/drivers/mtd/nand/mxc_nd.c
new file mode 100644
index 000000000000..fc95af3bd442
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd.c
@@ -0,0 +1,1413 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/mach/flash.h>
+
+#include "mxc_nd.h"
+
+#define DVR_VER "2.1"
+
+struct mxc_mtd_s {
+ struct mtd_info mtd;
+ struct nand_chip nand;
+ struct mtd_partition *parts;
+ struct device *dev;
+};
+
+static struct mxc_mtd_s *mxc_nand_data;
+
+/*
+ * Define delays in microsec for NAND device operations
+ */
+#define TROP_US_DELAY 2000
+/*
+ * Macros to get half word and bit positions of ECC
+ */
+#define COLPOS(x) ((x) >> 4)
+#define BITPOS(x) ((x) & 0xf)
+
+/* Define single bit Error positions in Main & Spare area */
+#define MAIN_SINGLEBIT_ERROR 0x4
+#define SPARE_SINGLEBIT_ERROR 0x1
+
+struct nand_info {
+ bool bSpareOnly;
+ bool bStatusRequest;
+ u16 colAddr;
+};
+
+static struct nand_info g_nandfc_info;
+
+#ifdef CONFIG_MTD_NAND_MXC_SWECC
+static int hardware_ecc = 0;
+#else
+static int hardware_ecc = 1;
+#endif
+
+#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+static int Ecc_disabled;
+#endif
+
+static int is2k_Pagesize = 0;
+
+static struct clk *nfc_clk;
+
+/*
+ * OOB placement block for use with hardware ecc generation
+ */
+static struct nand_ecclayout nand_hw_eccoob_8 = {
+ .eccbytes = 5,
+ .eccpos = {6, 7, 8, 9, 10},
+ .oobfree = {{0, 5}, {11, 5}}
+};
+
+static struct nand_ecclayout nand_hw_eccoob_16 = {
+ .eccbytes = 5,
+ .eccpos = {6, 7, 8, 9, 10},
+ .oobfree = {{0, 6}, {12, 4}}
+};
+
+static struct nand_ecclayout nand_hw_eccoob_2k = {
+ .eccbytes = 20,
+ .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
+ 38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
+ .oobfree = {
+ {.offset = 0,
+ .length = 5},
+
+ {.offset = 11,
+ .length = 10},
+
+ {.offset = 27,
+ .length = 10},
+
+ {.offset = 43,
+ .length = 10},
+
+ {.offset = 59,
+ .length = 5}
+ }
+};
+
+/*!
+ * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors
+ */
+
+/*!
+ * @file mxc_nd.c
+ *
+ * @brief This file contains the hardware specific layer for NAND Flash on
+ * MXC processor
+ *
+ * @ingroup NAND_MTD
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
+#endif
+
+static wait_queue_head_t irq_waitq;
+
+static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
+{
+ NFC_CONFIG1 |= NFC_INT_MSK; /* Disable interrupt */
+ wake_up(&irq_waitq);
+
+ return IRQ_RETVAL(1);
+}
+
+/*!
+ * This function polls the NANDFC to wait for the basic operation to complete by
+ * checking the INT bit of config2 register.
+ *
+ * @param maxRetries number of retry attempts (separated by 1 us)
+ * @param param parameter for debug
+ * @param useirq True if IRQ should be used rather than polling
+ */
+static void wait_op_done(int maxRetries, u16 param, bool useirq)
+{
+ if (useirq) {
+ if ((NFC_CONFIG2 & NFC_INT) == 0) {
+ NFC_CONFIG1 &= ~NFC_INT_MSK; /* Enable interrupt */
+ wait_event(irq_waitq, NFC_CONFIG2 & NFC_INT);
+ }
+ NFC_CONFIG2 &= ~NFC_INT;
+ } else {
+ while (maxRetries-- > 0) {
+ if (NFC_CONFIG2 & NFC_INT) {
+ NFC_CONFIG2 &= ~NFC_INT;
+ break;
+ }
+ udelay(1);
+ }
+ if (maxRetries <= 0)
+ DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n",
+ __FUNCTION__, param);
+ }
+}
+
+/*!
+ * This function issues the specified command to the NAND device and
+ * waits for completion.
+ *
+ * @param cmd command for NAND Flash
+ * @param useirq True if IRQ should be used rather than polling
+ */
+static void send_cmd(u16 cmd, bool useirq)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd, useirq);
+
+ NFC_FLASH_CMD = (u16) cmd;
+ NFC_CONFIG2 = NFC_CMD;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, cmd, useirq);
+}
+
+/*!
+ * This function sends an address (or partial address) to the
+ * NAND device. The address is used to select the source/destination for
+ * a NAND command.
+ *
+ * @param addr address to be written to NFC.
+ * @param islast True if this is the last address cycle for command
+ */
+static void send_addr(u16 addr, bool islast)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr, islast);
+
+ NFC_FLASH_ADDR = addr;
+ NFC_CONFIG2 = NFC_ADDR;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, addr, islast);
+}
+
+/*!
+ * This function requests the NANDFC to initate the transfer
+ * of data currently in the NANDFC RAM buffer to the NAND device.
+ *
+ * @param buf_id Specify Internal RAM Buffer number (0-3)
+ * @param bSpareOnly set true if only the spare area is transferred
+ */
+static void send_prog_page(u8 buf_id, bool bSpareOnly)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", bSpareOnly);
+
+ /* NANDFC buffer 0 is used for page read/write */
+
+ NFC_BUF_ADDR = buf_id;
+
+ /* Configure spare or page+spare access */
+ if (!is2k_Pagesize) {
+ if (bSpareOnly) {
+ NFC_CONFIG1 |= NFC_SP_EN;
+ } else {
+ NFC_CONFIG1 &= ~(NFC_SP_EN);
+ }
+ }
+ NFC_CONFIG2 = NFC_INPUT;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, bSpareOnly, true);
+}
+
+/*!
+ * This function will correct the single bit ECC error
+ *
+ * @param buf_id Specify Internal RAM Buffer number (0-3)
+ * @param eccpos Ecc byte and bit position
+ * @param bSpareOnly set to true if only spare area needs correction
+ */
+
+static void mxc_nd_correct_error(u8 buf_id, u16 eccpos, bool bSpareOnly)
+{
+ u16 col;
+ u8 pos;
+ volatile u16 *buf;
+
+ /* Get col & bit position of error
+ these macros works for both 8 & 16 bits */
+ col = COLPOS(eccpos); /* Get half-word position */
+ pos = BITPOS(eccpos); /* Get bit position */
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nd_correct_error (col=%d pos=%d)\n", col, pos);
+
+ /* Set the pointer for main / spare area */
+ if (!bSpareOnly) {
+ buf = (volatile u16 *)(MAIN_AREA0 + col + (256 * buf_id));
+ } else {
+ buf = (volatile u16 *)(SPARE_AREA0 + col + (8 * buf_id));
+ }
+
+ /* Fix the data */
+ *buf ^= (1 << pos);
+}
+
+/*!
+ * This function will maintains state of single bit Error
+ * in Main & spare area
+ *
+ * @param buf_id Specify Internal RAM Buffer number (0-3)
+ * @param spare set to true if only spare area needs correction
+ */
+static void mxc_nd_correct_ecc(u8 buf_id, bool spare)
+{
+#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+ static int lastErrMain = 0, lastErrSpare = 0; /* To maintain single bit
+ error in previous page */
+#endif
+ u16 value, ecc_status;
+ /* Read the ECC result */
+ ecc_status = NFC_ECC_STATUS_RESULT;
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nd_correct_ecc (Ecc status=%x)\n", ecc_status);
+
+#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+ /* Check for Error in Mainarea */
+ if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) {
+ /* Check for error in previous page */
+ if (lastErrMain && !spare) {
+ value = NFC_RSLTMAIN_AREA;
+ /* Correct single bit error in Mainarea
+ NFC will not correct the error in
+ current page */
+ mxc_nd_correct_error(buf_id, value, false);
+ } else {
+ /* Set if single bit error in current page */
+ lastErrMain = 1;
+ }
+ } else {
+ /* Reset if no single bit error in current page */
+ lastErrMain = 0;
+ }
+
+ /* Check for Error in Sparearea */
+ if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) {
+ /* Check for error in previous page */
+ if (lastErrSpare) {
+ value = NFC_RSLTSPARE_AREA;
+ /* Correct single bit error in Mainarea
+ NFC will not correct the error in
+ current page */
+ mxc_nd_correct_error(buf_id, value, true);
+ } else {
+ /* Set if single bit error in current page */
+ lastErrSpare = 1;
+ }
+ } else {
+ /* Reset if no single bit error in current page */
+ lastErrSpare = 0;
+ }
+#else
+ if (((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR)
+ || ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR)) {
+ if (Ecc_disabled) {
+ if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) {
+ value = NFC_RSLTMAIN_AREA;
+ /* Correct single bit error in Mainarea
+ NFC will not correct the error in
+ current page */
+ mxc_nd_correct_error(buf_id, value, false);
+ }
+ if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) {
+ value = NFC_RSLTSPARE_AREA;
+ /* Correct single bit error in Mainarea
+ NFC will not correct the error in
+ current page */
+ mxc_nd_correct_error(buf_id, value, true);
+ }
+
+ } else {
+ /* Disable ECC */
+ NFC_CONFIG1 &= ~(NFC_ECC_EN);
+ Ecc_disabled = 1;
+ }
+ } else if (ecc_status == 0) {
+ if (Ecc_disabled) {
+ /* Enable ECC */
+ NFC_CONFIG1 |= NFC_ECC_EN;
+ Ecc_disabled = 0;
+ }
+ } else {
+ /* 2-bit Error Do nothing */
+ }
+#endif /* CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 */
+
+}
+
+/*!
+ * This function requests the NANDFC to initated the transfer
+ * of data from the NAND device into in the NANDFC ram buffer.
+ *
+ * @param buf_id Specify Internal RAM Buffer number (0-3)
+ * @param bSpareOnly set true if only the spare area is transferred
+ */
+static void send_read_page(u8 buf_id, bool bSpareOnly)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", bSpareOnly);
+
+ /* NANDFC buffer 0 is used for page read/write */
+ NFC_BUF_ADDR = buf_id;
+
+ /* Configure spare or page+spare access */
+ if (!is2k_Pagesize) {
+ if (bSpareOnly) {
+ NFC_CONFIG1 |= NFC_SP_EN;
+ } else {
+ NFC_CONFIG1 &= ~(NFC_SP_EN);
+ }
+ }
+
+ NFC_CONFIG2 = NFC_OUTPUT;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, bSpareOnly, true);
+
+ /* If there are single bit errors in
+ two consecutive page reads then
+ the error is not corrected by the
+ NFC for the second page.
+ Correct single bit error in driver */
+
+ mxc_nd_correct_ecc(buf_id, bSpareOnly);
+}
+
+/*!
+ * This function requests the NANDFC to perform a read of the
+ * NAND device ID.
+ */
+static void send_read_id(void)
+{
+ struct nand_chip *this = &mxc_nand_data->nand;
+
+ /* NANDFC buffer 0 is used for device ID output */
+ NFC_BUF_ADDR = 0x0;
+
+ /* Read ID into main buffer */
+ NFC_CONFIG1 &= (~(NFC_SP_EN));
+ NFC_CONFIG2 = NFC_ID;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, 0, true);
+
+ if (this->options & NAND_BUSWIDTH_16) {
+ volatile u16 *mainBuf = MAIN_AREA0;
+
+ /*
+ * Pack the every-other-byte result for 16-bit ID reads
+ * into every-byte as the generic code expects and various
+ * chips implement.
+ */
+
+ mainBuf[0] = (mainBuf[0] & 0xff) | ((mainBuf[1] & 0xff) << 8);
+ mainBuf[1] = (mainBuf[2] & 0xff) | ((mainBuf[3] & 0xff) << 8);
+ mainBuf[2] = (mainBuf[4] & 0xff) | ((mainBuf[5] & 0xff) << 8);
+ }
+}
+
+/*!
+ * This function requests the NANDFC to perform a read of the
+ * NAND device status and returns the current status.
+ *
+ * @return device status
+ */
+static u16 get_dev_status(void)
+{
+ volatile u16 *mainBuf = MAIN_AREA1;
+ u32 store;
+ u16 ret;
+ /* Issue status request to NAND device */
+
+ /* store the main area1 first word, later do recovery */
+ store = *((u32 *) mainBuf);
+ /*
+ * NANDFC buffer 1 is used for device status to prevent
+ * corruption of read/write buffer on status requests.
+ */
+ NFC_BUF_ADDR = 1;
+
+ /* Read status into main buffer */
+ NFC_CONFIG1 &= (~(NFC_SP_EN));
+ NFC_CONFIG2 = NFC_STATUS;
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, 0, true);
+
+ /* Status is placed in first word of main buffer */
+ /* get status, then recovery area 1 data */
+ ret = mainBuf[0];
+ *((u32 *) mainBuf) = store;
+
+ return ret;
+}
+
+/*!
+ * This functions is used by upper layer to checks if device is ready
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return 0 if device is busy else 1
+ */
+static int mxc_nand_dev_ready(struct mtd_info *mtd)
+{
+ /*
+ * NFC handles R/B internally.Therefore,this function
+ * always returns status as ready.
+ */
+ return 1;
+}
+
+static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ /*
+ * If HW ECC is enabled, we turn it on during init. There is
+ * no need to enable again here.
+ */
+}
+
+static int mxc_nand_correct_data(struct mtd_info *mtd, u_char * dat,
+ u_char * read_ecc, u_char * calc_ecc)
+{
+ /*
+ * 1-Bit errors are automatically corrected in HW. No need for
+ * additional correction. 2-Bit errors cannot be corrected by
+ * HW ECC, so we need to return failure
+ */
+ u16 ecc_status = NFC_ECC_STATUS_RESULT;
+
+ if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
+ DEBUG(MTD_DEBUG_LEVEL0,
+ "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat,
+ u_char * ecc_code)
+{
+ /*
+ * Just return success. HW ECC does not read/write the NFC spare
+ * buffer. Only the FLASH spare area contains the calcuated ECC.
+ */
+ return 0;
+}
+
+/*!
+ * This function reads byte from the NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return data read from the NAND Flash
+ */
+static u_char mxc_nand_read_byte(struct mtd_info *mtd)
+{
+ u_char retVal = 0;
+ u16 col, rdWord;
+ volatile u16 *mainBuf = MAIN_AREA0;
+ volatile u16 *spareBuf = SPARE_AREA0;
+
+ /* Check for status request */
+ if (g_nandfc_info.bStatusRequest) {
+ return (get_dev_status() & 0xFF);
+ }
+
+ /* Get column for 16-bit access */
+ col = g_nandfc_info.colAddr >> 1;
+
+ /* If we are accessing the spare region */
+ if (g_nandfc_info.bSpareOnly) {
+ rdWord = spareBuf[col];
+ } else {
+ rdWord = mainBuf[col];
+ }
+
+ /* Pick upper/lower byte of word from RAM buffer */
+ if (g_nandfc_info.colAddr & 0x1) {
+ retVal = (rdWord >> 8) & 0xFF;
+ } else {
+ retVal = rdWord & 0xFF;
+ }
+
+ /* Update saved column address */
+ g_nandfc_info.colAddr++;
+
+ return retVal;
+}
+
+/*!
+ * This function reads word from the NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return data read from the NAND Flash
+ */
+static u16 mxc_nand_read_word(struct mtd_info *mtd)
+{
+ u16 col;
+ u16 rdWord, retVal;
+ volatile u16 *p;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nand_read_word(col = %d)\n", g_nandfc_info.colAddr);
+
+ col = g_nandfc_info.colAddr;
+ /* Adjust saved column address */
+ if (col < mtd->writesize && g_nandfc_info.bSpareOnly)
+ col += mtd->writesize;
+
+ if (col < mtd->writesize)
+ p = (MAIN_AREA0) + (col >> 1);
+ else
+ p = (SPARE_AREA0) + ((col - mtd->writesize) >> 1);
+
+ if (col & 1) {
+ rdWord = *p;
+ retVal = (rdWord >> 8) & 0xff;
+ rdWord = *(p + 1);
+ retVal |= (rdWord << 8) & 0xff00;
+
+ } else {
+ retVal = *p;
+
+ }
+
+ /* Update saved column address */
+ g_nandfc_info.colAddr = col + 2;
+
+ return retVal;
+}
+
+/*!
+ * This function writes data of length \b len to buffer \b buf. The data to be
+ * written on NAND Flash is first copied to RAMbuffer. After the Data Input
+ * Operation by the NFC, the data is written to NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be written to NAND Flash
+ * @param len number of bytes to be written
+ */
+static void mxc_nand_write_buf(struct mtd_info *mtd,
+ const u_char * buf, int len)
+{
+ int n;
+ int col;
+ int i = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nand_write_buf(col = %d, len = %d)\n", g_nandfc_info.colAddr,
+ len);
+
+ col = g_nandfc_info.colAddr;
+
+ /* Adjust saved column address */
+ if (col < mtd->writesize && g_nandfc_info.bSpareOnly)
+ col += mtd->writesize;
+
+ n = mtd->writesize + mtd->oobsize - col;
+ n = min(len, n);
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "%s:%d: col = %d, n = %d\n", __FUNCTION__, __LINE__, col, n);
+
+ while (n) {
+ volatile u32 *p;
+ if (col < mtd->writesize)
+ p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3));
+ else
+ p = (volatile u32 *)((ulong) (SPARE_AREA0) -
+ mtd->writesize + (col & ~3));
+
+ DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __FUNCTION__,
+ __LINE__, p);
+
+ if (((col | (int)&buf[i]) & 3) || n < 16) {
+ u32 data = 0;
+
+ if (col & 3 || n < 4)
+ data = *p;
+
+ switch (col & 3) {
+ case 0:
+ if (n) {
+ data = (data & 0xffffff00) |
+ (buf[i++] << 0);
+ n--;
+ col++;
+ }
+ case 1:
+ if (n) {
+ data = (data & 0xffff00ff) |
+ (buf[i++] << 8);
+ n--;
+ col++;
+ }
+ case 2:
+ if (n) {
+ data = (data & 0xff00ffff) |
+ (buf[i++] << 16);
+ n--;
+ col++;
+ }
+ case 3:
+ if (n) {
+ data = (data & 0x00ffffff) |
+ (buf[i++] << 24);
+ n--;
+ col++;
+ }
+ }
+
+ *p = data;
+ } else {
+ int m = mtd->writesize - col;
+
+ if (col >= mtd->writesize)
+ m += mtd->oobsize;
+
+ m = min(n, m) & ~3;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "%s:%d: n = %d, m = %d, i = %d, col = %d\n",
+ __FUNCTION__, __LINE__, n, m, i, col);
+
+ memcpy((void *)(p), &buf[i], m);
+ col += m;
+ i += m;
+ n -= m;
+ }
+ }
+ /* Update saved column address */
+ g_nandfc_info.colAddr = col;
+
+}
+
+/*!
+ * This function id is used to read the data buffer from the NAND Flash. To
+ * read the data from NAND Flash first the data output cycle is initiated by
+ * the NFC, which copies the data to RAMbuffer. This data of length \b len is
+ * then copied to buffer \b buf.
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be read from NAND Flash
+ * @param len number of bytes to be read
+ */
+static void mxc_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len)
+{
+
+ int n;
+ int col;
+ int i = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nand_read_buf(col = %d, len = %d)\n", g_nandfc_info.colAddr,
+ len);
+
+ col = g_nandfc_info.colAddr;
+ /* Adjust saved column address */
+ if (col < mtd->writesize && g_nandfc_info.bSpareOnly)
+ col += mtd->writesize;
+
+ n = mtd->writesize + mtd->oobsize - col;
+ n = min(len, n);
+
+ while (n) {
+ volatile u32 *p;
+
+ if (col < mtd->writesize)
+ p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3));
+ else
+ p = (volatile u32 *)((ulong) (SPARE_AREA0) -
+ mtd->writesize + (col & ~3));
+
+ if (((col | (int)&buf[i]) & 3) || n < 16) {
+ u32 data;
+
+ data = *p;
+ switch (col & 3) {
+ case 0:
+ if (n) {
+ buf[i++] = (u8) (data);
+ n--;
+ col++;
+ }
+ case 1:
+ if (n) {
+ buf[i++] = (u8) (data >> 8);
+ n--;
+ col++;
+ }
+ case 2:
+ if (n) {
+ buf[i++] = (u8) (data >> 16);
+ n--;
+ col++;
+ }
+ case 3:
+ if (n) {
+ buf[i++] = (u8) (data >> 24);
+ n--;
+ col++;
+ }
+ }
+ } else {
+ int m = mtd->writesize - col;
+
+ if (col >= mtd->writesize)
+ m += mtd->oobsize;
+
+ m = min(n, m) & ~3;
+ memcpy(&buf[i], (void *)(p), m);
+ col += m;
+ i += m;
+ n -= m;
+ }
+ }
+ /* Update saved column address */
+ g_nandfc_info.colAddr = col;
+
+}
+
+/*!
+ * This function is used by the upper layer to verify the data in NAND Flash
+ * with the data in the \b buf.
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be verified
+ * @param len length of the data to be verified
+ *
+ * @return -EFAULT if error else 0
+ *
+ */
+static int
+mxc_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
+{
+ return -EFAULT;
+}
+
+/*!
+ * This function is used by upper layer for select and deselect of the NAND
+ * chip
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param chip val indicating select or deselect
+ */
+static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE
+ if (chip > 0) {
+ DEBUG(MTD_DEBUG_LEVEL0,
+ "ERROR: Illegal chip select (chip = %d)\n", chip);
+ return;
+ }
+
+ if (chip == -1) {
+ NFC_CONFIG1 &= (~(NFC_CE));
+ return;
+ }
+
+ NFC_CONFIG1 |= NFC_CE;
+#endif
+
+ switch (chip) {
+ case -1:
+ /* Disable the NFC clock */
+ clk_disable(nfc_clk);
+ break;
+ case 0:
+ /* Enable the NFC clock */
+ clk_enable(nfc_clk);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*!
+ * This function is used by the upper layer to write command to NAND Flash for
+ * different operations to be carried out on NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param command command for NAND Flash
+ * @param column column offset for the page read
+ * @param page_addr page to be read from NAND Flash
+ */
+static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ bool useirq = true;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
+ command, column, page_addr);
+
+ /*
+ * Reset command state information
+ */
+ g_nandfc_info.bStatusRequest = false;
+
+ /* Reset column address to 0 */
+ g_nandfc_info.colAddr = 0;
+
+ /*
+ * Command pre-processing step
+ */
+ switch (command) {
+
+ case NAND_CMD_STATUS:
+ g_nandfc_info.bStatusRequest = true;
+ break;
+
+ case NAND_CMD_READ0:
+ g_nandfc_info.colAddr = column;
+ g_nandfc_info.bSpareOnly = false;
+ useirq = false;
+ break;
+
+ case NAND_CMD_READOOB:
+ g_nandfc_info.colAddr = column;
+ g_nandfc_info.bSpareOnly = true;
+ useirq = false;
+ if (is2k_Pagesize)
+ command = NAND_CMD_READ0; /* only READ0 is valid */
+ break;
+
+ case NAND_CMD_SEQIN:
+ if (column >= mtd->writesize) {
+ if (is2k_Pagesize) {
+ /**
+ * FIXME: before send SEQIN command for write OOB,
+ * We must read one page out.
+ * For K9F1GXX has no READ1 command to set current HW
+ * pointer to spare area, we must write the whole page including OOB together.
+ */
+ /* call itself to read a page */
+ mxc_nand_command(mtd, NAND_CMD_READ0, 0,
+ page_addr);
+ }
+ g_nandfc_info.colAddr = column - mtd->writesize;
+ g_nandfc_info.bSpareOnly = true;
+ /* Set program pointer to spare region */
+ if (!is2k_Pagesize)
+ send_cmd(NAND_CMD_READOOB, false);
+ } else {
+ g_nandfc_info.bSpareOnly = false;
+ g_nandfc_info.colAddr = column;
+ /* Set program pointer to page start */
+ if (!is2k_Pagesize)
+ send_cmd(NAND_CMD_READ0, false);
+ }
+ useirq = false;
+ break;
+
+ case NAND_CMD_PAGEPROG:
+#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+ if (Ecc_disabled) {
+ /* Enable Ecc for page writes */
+ NFC_CONFIG1 |= NFC_ECC_EN;
+ }
+#endif
+
+ send_prog_page(0, g_nandfc_info.bSpareOnly);
+
+ if (is2k_Pagesize) {
+ /* data in 4 areas datas */
+ send_prog_page(1, g_nandfc_info.bSpareOnly);
+ send_prog_page(2, g_nandfc_info.bSpareOnly);
+ send_prog_page(3, g_nandfc_info.bSpareOnly);
+ }
+
+ break;
+
+ case NAND_CMD_ERASE1:
+ useirq = false;
+ break;
+ }
+
+ /*
+ * Write out the command to the device.
+ */
+ send_cmd(command, useirq);
+
+ /*
+ * Write out column address, if necessary
+ */
+ if (column != -1) {
+ /*
+ * MXC NANDFC can only perform full page+spare or
+ * spare-only read/write. When the upper layers
+ * layers perform a read/write buf operation,
+ * we will used the saved column adress to index into
+ * the full page.
+ */
+ send_addr(0, page_addr == -1);
+ if (is2k_Pagesize)
+ /* another col addr cycle for 2k page */
+ send_addr(0, false);
+ }
+
+ /*
+ * Write out page address, if necessary
+ */
+ if (page_addr != -1) {
+ send_addr((page_addr & 0xff), false); /* paddr_0 - p_addr_7 */
+
+ if (is2k_Pagesize) {
+ /* One more address cycle for higher density devices */
+ if (mtd->size >= 0x10000000) {
+ /* paddr_8 - paddr_15 */
+ send_addr((page_addr >> 8) & 0xff, false);
+ send_addr((page_addr >> 16) & 0xff, true);
+ } else
+ /* paddr_8 - paddr_15 */
+ send_addr((page_addr >> 8) & 0xff, true);
+ } else {
+ /* One more address cycle for higher density devices */
+ if (mtd->size >= 0x4000000) {
+ /* paddr_8 - paddr_15 */
+ send_addr((page_addr >> 8) & 0xff, false);
+ send_addr((page_addr >> 16) & 0xff, true);
+ } else
+ /* paddr_8 - paddr_15 */
+ send_addr((page_addr >> 8) & 0xff, true);
+ }
+ }
+
+ /*
+ * Command post-processing step
+ */
+ switch (command) {
+
+ case NAND_CMD_RESET:
+ break;
+
+ case NAND_CMD_READOOB:
+ case NAND_CMD_READ0:
+ if (is2k_Pagesize) {
+ /* send read confirm command */
+ send_cmd(NAND_CMD_READSTART, true);
+ /* read for each AREA */
+ send_read_page(0, g_nandfc_info.bSpareOnly);
+ send_read_page(1, g_nandfc_info.bSpareOnly);
+ send_read_page(2, g_nandfc_info.bSpareOnly);
+ send_read_page(3, g_nandfc_info.bSpareOnly);
+ } else {
+ send_read_page(0, g_nandfc_info.bSpareOnly);
+ }
+ break;
+
+ case NAND_CMD_READID:
+ send_read_id();
+ break;
+
+ case NAND_CMD_PAGEPROG:
+#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+ if (Ecc_disabled) {
+ /* Disble Ecc after page writes */
+ NFC_CONFIG1 &= ~(NFC_ECC_EN);
+ }
+#endif
+ break;
+
+ case NAND_CMD_STATUS:
+ break;
+
+ case NAND_CMD_ERASE2:
+ break;
+ }
+}
+
+/* Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks. */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr smallpage_memorybased = {
+ .options = NAND_BBT_SCAN2NDPAGE,
+ .offs = 5,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_memorybased = {
+ .options = 0,
+ .offs = 0,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+
+/* Generic flash bbt decriptors
+*/
+static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = mirror_pattern
+};
+
+static int mxc_nand_scan_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+
+ /* Config before scanning */
+ /* Do not rely on NFMS_BIT, set/clear NFMS bit based on mtd->writesize */
+ if (mtd->writesize == 2048) {
+ NFMS |= (1 << NFMS_BIT);
+ is2k_Pagesize = 1;
+ } else {
+ if ((NFMS >> NFMS_BIT) & 0x1) { /* This case strangly happened on MXC91321 P1.2.2 */
+ printk(KERN_INFO
+ "Oops... NFMS Bit set for 512B Page, resetting it. [RCSR: 0x%08x]\n",
+ NFMS);
+ NFMS &= ~(1 << NFMS_BIT);
+ }
+ is2k_Pagesize = 0;
+ }
+
+ if (is2k_Pagesize)
+ this->ecc.layout = &nand_hw_eccoob_2k;
+
+ /* jffs2 not write oob */
+ mtd->flags &= ~MTD_OOB_WRITEABLE;
+
+ /* use flash based bbt */
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+
+ /* update flash based bbt */
+ this->options |= NAND_USE_FLASH_BBT;
+
+ if (!this->badblock_pattern) {
+ if (mtd->writesize == 2048)
+ this->badblock_pattern = &smallpage_memorybased;
+ else
+ this->badblock_pattern = (mtd->writesize > 512) ?
+ &largepage_memorybased : &smallpage_memorybased;
+ }
+ /* Build bad block table */
+ return nand_scan_bbt(mtd, this->badblock_pattern);
+}
+
+#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE
+static void mxc_low_erase(struct mtd_info *mtd)
+{
+
+ struct nand_chip *this = mtd->priv;
+ unsigned int page_addr, addr;
+ u_char status;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : mxc_low_erase:Erasing NAND\n");
+ for (addr = 0; addr < this->chipsize; addr += mtd->erasesize) {
+ page_addr = addr / mtd->writesize;
+ mxc_nand_command(mtd, NAND_CMD_ERASE1, -1, page_addr);
+ mxc_nand_command(mtd, NAND_CMD_ERASE2, -1, -1);
+ mxc_nand_command(mtd, NAND_CMD_STATUS, -1, -1);
+ status = mxc_nand_read_byte(mtd);
+ if (status & NAND_STATUS_FAIL) {
+ printk(KERN_ERR
+ "ERASE FAILED(block = %d,status = 0x%x)\n",
+ addr / mtd->erasesize, status);
+ }
+ }
+
+}
+#endif
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and
+ * remove functions
+ *
+ * @return The function always returns 0.
+ */
+static int __init mxcnd_probe(struct platform_device *pdev)
+{
+ struct nand_chip *this;
+ struct mtd_info *mtd;
+ struct flash_platform_data *flash = pdev->dev.platform_data;
+ int nr_parts = 0;
+
+ int err = 0;
+ /* Allocate memory for MTD device structure and private data */
+ mxc_nand_data = kzalloc(sizeof(struct mxc_mtd_s), GFP_KERNEL);
+ if (!mxc_nand_data) {
+ printk(KERN_ERR "%s: failed to allocate mtd_info\n",
+ __FUNCTION__);
+ err = -ENOMEM;
+ goto out;
+ }
+ memset((char *)&g_nandfc_info, 0, sizeof(g_nandfc_info));
+
+ mxc_nand_data->dev = &pdev->dev;
+ /* structures must be linked */
+ this = &mxc_nand_data->nand;
+ mtd = &mxc_nand_data->mtd;
+ mtd->priv = this;
+ mtd->owner = THIS_MODULE;
+
+ /* 50 us command delay time */
+ this->chip_delay = 5;
+
+ this->priv = mxc_nand_data;
+ this->dev_ready = mxc_nand_dev_ready;
+ this->cmdfunc = mxc_nand_command;
+ this->select_chip = mxc_nand_select_chip;
+ this->read_byte = mxc_nand_read_byte;
+ this->read_word = mxc_nand_read_word;
+ this->write_buf = mxc_nand_write_buf;
+ this->read_buf = mxc_nand_read_buf;
+ this->verify_buf = mxc_nand_verify_buf;
+ this->scan_bbt = mxc_nand_scan_bbt;
+
+ nfc_clk = clk_get(&pdev->dev, "nfc_clk");
+ clk_enable(nfc_clk);
+
+ NFC_CONFIG1 |= NFC_INT_MSK;
+ init_waitqueue_head(&irq_waitq);
+ err = request_irq(MXC_INT_NANDFC, mxc_nfc_irq, 0, "mxc_nd", NULL);
+ if (err) {
+ goto out_1;
+ }
+
+ if (hardware_ecc) {
+ this->ecc.calculate = mxc_nand_calculate_ecc;
+ this->ecc.hwctl = mxc_nand_enable_hwecc;
+ this->ecc.correct = mxc_nand_correct_data;
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.size = 512;
+ this->ecc.bytes = 3;
+ this->ecc.layout = &nand_hw_eccoob_8;
+ NFC_CONFIG1 |= NFC_ECC_EN;
+ } else {
+ this->ecc.mode = NAND_ECC_SOFT;
+ }
+
+ /* Reset NAND */
+ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* preset operation */
+ /* Unlock the internal RAM Buffer */
+ NFC_CONFIG = 0x2;
+
+ /* Blocks to be unlocked */
+ NFC_UNLOCKSTART_BLKADDR = 0x0;
+ NFC_UNLOCKEND_BLKADDR = 0x4000;
+
+ /* Unlock Block Command for given address range */
+ NFC_WRPROT = 0x4;
+
+ /* NAND bus width determines access funtions used by upper layer */
+ if (flash->width == 2) {
+ this->options |= NAND_BUSWIDTH_16;
+ this->ecc.layout = &nand_hw_eccoob_16;
+ } else {
+ this->options |= 0;
+ }
+
+ is2k_Pagesize = 0;
+
+ /* Scan to find existence of the device */
+ if (nand_scan(mtd, 1)) {
+ DEBUG(MTD_DEBUG_LEVEL0,
+ "MXC_ND: Unable to find any NAND device.\n");
+ err = -ENXIO;
+ goto out_1;
+ }
+
+ /* Register the partitions */
+#ifdef CONFIG_MTD_PARTITIONS
+ nr_parts =
+ parse_mtd_partitions(mtd, part_probes, &mxc_nand_data->parts, 0);
+ if (nr_parts > 0)
+ add_mtd_partitions(mtd, mxc_nand_data->parts, nr_parts);
+ else if (flash->parts)
+ add_mtd_partitions(mtd, flash->parts, flash->nr_parts);
+ else
+#endif
+ {
+ pr_info("Registering %s as whole device\n", mtd->name);
+ add_mtd_device(mtd);
+ }
+#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE
+ /* Erase all the blocks of a NAND */
+ mxc_low_erase(mtd);
+#endif
+
+ platform_set_drvdata(pdev, mtd);
+ return 0;
+
+ out_1:
+ kfree(mxc_nand_data);
+ out:
+ return err;
+
+}
+
+ /*!
+ * Dissociates the driver from the device.
+ *
+ * @param pdev the device structure used to give information on which
+ *
+ * @return The function always returns 0.
+ */
+
+static int __exit mxcnd_remove(struct platform_device *pdev)
+{
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+
+ clk_put(nfc_clk);
+ platform_set_drvdata(pdev, NULL);
+
+ if (mxc_nand_data) {
+ nand_release(mtd);
+ free_irq(MXC_INT_NANDFC, NULL);
+ kfree(mxc_nand_data);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function is called to put the NAND in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device information structure
+ *
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+
+static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mtd_info *info = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n");
+ if (info)
+ ret = info->suspend(info);
+
+ /* Disable the NFC clock */
+ clk_disable(nfc_clk);
+
+ return ret;
+}
+
+/*!
+ * This function is called to bring the NAND back from a low power state. Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device information structure
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxcnd_resume(struct platform_device *pdev)
+{
+ struct mtd_info *info = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n");
+ /* Enable the NFC clock */
+ clk_enable(nfc_clk);
+
+ if (info) {
+ info->resume(info);
+ }
+
+ return ret;
+}
+
+#else
+#define mxcnd_suspend NULL
+#define mxcnd_resume NULL
+#endif /* CONFIG_PM */
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcnd_driver = {
+ .driver = {
+ .name = "mxc_nand_flash",
+ },
+ .probe = mxcnd_probe,
+ .remove = __exit_p(mxcnd_remove),
+ .suspend = mxcnd_suspend,
+ .resume = mxcnd_resume,
+};
+
+/*!
+ * Main initialization routine
+ * @return 0 if successful; non-zero otherwise
+ */
+static int __init mxc_nd_init(void)
+{
+ /* Register the device driver structure. */
+ pr_info("MXC MTD nand Driver %s\n", DVR_VER);
+ if (platform_driver_register(&mxcnd_driver) != 0) {
+ printk(KERN_ERR "Driver register failed for mxcnd_driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/*!
+ * Clean up routine
+ */
+static void __exit mxc_nd_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&mxcnd_driver);
+}
+
+module_init(mxc_nd_init);
+module_exit(mxc_nd_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC NAND MTD driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/mxc_nd.h b/drivers/mtd/nand/mxc_nd.h
new file mode 100644
index 000000000000..e38a9a637c1d
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_nd.h
+ *
+ * @brief This file contains the NAND Flash Controller register information.
+ *
+ *
+ * @ingroup NAND_MTD
+ */
+
+#ifndef __MXC_ND_H__
+#define __MXC_ND_H__
+
+#include <mach/hardware.h>
+
+/*
+ * Addresses for NFC registers
+ */
+#define NFC_BUF_SIZE (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE00)))
+#define NFC_BUF_ADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE04)))
+#define NFC_FLASH_ADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE06)))
+#define NFC_FLASH_CMD (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE08)))
+#define NFC_CONFIG (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0A)))
+#define NFC_ECC_STATUS_RESULT (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0C)))
+#define NFC_RSLTMAIN_AREA (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE0E)))
+#define NFC_RSLTSPARE_AREA (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE10)))
+#define NFC_WRPROT (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE12)))
+#define NFC_UNLOCKSTART_BLKADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE14)))
+#define NFC_UNLOCKEND_BLKADDR (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE16)))
+#define NFC_NF_WRPRST (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE18)))
+#define NFC_CONFIG1 (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE1A)))
+#define NFC_CONFIG2 (*((volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0xE1C)))
+
+/*!
+ * Addresses for NFC RAM BUFFER Main area 0
+ */
+#define MAIN_AREA0 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x000)
+#define MAIN_AREA1 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x200)
+#define MAIN_AREA2 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x400)
+#define MAIN_AREA3 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x600)
+
+/*!
+ * Addresses for NFC SPARE BUFFER Spare area 0
+ */
+#define SPARE_AREA0 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x800)
+#define SPARE_AREA1 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x810)
+#define SPARE_AREA2 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x820)
+#define SPARE_AREA3 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR + 0x830)
+
+/*!
+ * Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register for Command
+ * operation
+ */
+#define NFC_CMD 0x1
+
+/*!
+ * Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register for Address
+ * operation
+ */
+#define NFC_ADDR 0x2
+
+/*!
+ * Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register for Input
+ * operation
+ */
+#define NFC_INPUT 0x4
+
+/*!
+ * Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register for Data Output
+ * operation
+ */
+#define NFC_OUTPUT 0x8
+
+/*!
+ * Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register for Read ID
+ * operation
+ */
+#define NFC_ID 0x10
+
+/*!
+ * Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register for Read Status
+ * operation
+ */
+#define NFC_STATUS 0x20
+
+/*!
+ * Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read Status
+ * operation
+ */
+#define NFC_INT 0x8000
+
+#define NFC_SP_EN (1 << 2)
+#define NFC_ECC_EN (1 << 3)
+#define NFC_INT_MSK (1 << 4)
+#define NFC_BIG (1 << 5)
+#define NFC_RST (1 << 6)
+#define NFC_CE (1 << 7)
+#define NFC_ONE_CYCLE (1 << 8)
+
+#endif /* MXCND_H */
diff --git a/drivers/mtd/nand/mxc_nd2.c b/drivers/mtd/nand/mxc_nd2.c
new file mode 100644
index 000000000000..d8589026d191
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd2.c
@@ -0,0 +1,1458 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mtd/partitions.h>
+#include <asm/mach/flash.h>
+#include <asm/io.h>
+#include "mxc_nd2.h"
+
+#define DVR_VER "2.5"
+
+/* Global address Variables */
+static u32 nfc_axi_base, nfc_ip_base;
+
+struct mxc_mtd_s {
+ struct mtd_info mtd;
+ struct nand_chip nand;
+ struct mtd_partition *parts;
+ struct device *dev;
+};
+
+static struct mxc_mtd_s *mxc_nand_data;
+
+/*
+ * Define delays in microsec for NAND device operations
+ */
+#define TROP_US_DELAY 2000
+
+struct nand_info {
+ bool bStatusRequest;
+ u16 colAddr;
+};
+
+static struct nand_info g_nandfc_info;
+
+#ifdef CONFIG_MTD_NAND_MXC_SWECC
+static int hardware_ecc = 0;
+#else
+static int hardware_ecc = 1;
+#endif
+
+static u8 num_of_interleave = 1;
+
+static u8 *data_buf;
+static u8 *oob_buf;
+
+static int g_page_mask;
+
+static struct clk *nfc_clk;
+
+/*
+ * OOB placement block for use with hardware ecc generation
+ */
+static struct nand_ecclayout nand_hw_eccoob_512 = {
+ .eccbytes = 9,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 4,
+ .oobfree = {{0, 4}}
+};
+
+static struct nand_ecclayout nand_hw_eccoob_2k = {
+ .eccbytes = 9,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 4,
+ .oobfree = {{2, 4}}
+};
+
+static struct nand_ecclayout nand_hw_eccoob_4k = {
+ .eccbytes = 9,
+ .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15},
+ .oobavail = 4,
+ .oobfree = {{2, 4}}
+};
+
+/*!
+ * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors
+ */
+
+/*!
+ * @file mxc_nd2.c
+ *
+ * @brief This file contains the hardware specific layer for NAND Flash on
+ * MXC processor
+ *
+ * @ingroup NAND_MTD
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
+#endif
+
+static wait_queue_head_t irq_waitq;
+
+static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
+{
+ /* Disable Interuupt */
+ raw_write(raw_read(REG_NFC_INTRRUPT) | NFC_INT_MSK, REG_NFC_INTRRUPT);
+ wake_up(&irq_waitq);
+
+ return IRQ_HANDLED;
+}
+
+static void nfc_memcpy(void *dest, void *src, int len)
+{
+ u8 *d = dest;
+ u8 *s = src;
+
+ while (len > 0) {
+ if (len >= 4) {
+ *(u32 *)d = *(u32 *)s;
+ d += 4;
+ s += 4;
+ len -= 4;
+ } else {
+ *(u16 *)d = *(u16 *)s;
+ len -= 2;
+ break;
+ }
+ }
+
+ if (len)
+ BUG();
+}
+
+/*
+ * Functions to transfer data to/from spare erea.
+ */
+static void
+copy_spare(struct mtd_info *mtd, void *pbuf, void *pspare, int len, bool bfrom)
+{
+ u16 i, j;
+ u16 m = mtd->oobsize;
+ u16 n = mtd->writesize >> 9;
+ u8 *d = (u8 *) pbuf;
+ u8 *s = (u8 *) pspare;
+ u16 t = SPARE_LEN;
+
+ m /= num_of_interleave;
+ n /= num_of_interleave;
+
+ j = (m / n >> 1) << 1;
+
+ if (bfrom) {
+ for (i = 0; i < n - 1; i++)
+ nfc_memcpy(&d[i * j], &s[i * t], j);
+
+ /* the last section */
+ nfc_memcpy(&d[i * j], &s[i * t], len - i * j);
+ } else {
+ for (i = 0; i < n - 1; i++)
+ nfc_memcpy(&s[i * t], &d[i * j], j);
+
+ /* the last section */
+ nfc_memcpy(&s[i * t], &d[i * j], len - i * j);
+ }
+}
+
+/*!
+ * This function polls the NFC to wait for the basic operation to complete by
+ * checking the INT bit of config2 register.
+ *
+ * @param maxRetries number of retry attempts (separated by 1 us)
+ * @param useirq True if IRQ should be used rather than polling
+ */
+static void wait_op_done(int maxRetries, bool useirq)
+{
+ if (useirq) {
+ if ((raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT) == 0) {
+ /* Enable Interuupt */
+ raw_write(raw_read(REG_NFC_INTRRUPT) & ~NFC_INT_MSK,
+ REG_NFC_INTRRUPT);
+ wait_event(irq_waitq,
+ (raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT));
+ }
+ WRITE_NFC_IP_REG((raw_read(REG_NFC_OPS_STAT) &
+ ~NFC_OPS_STAT), REG_NFC_OPS_STAT);
+ } else {
+ while (1) {
+ maxRetries--;
+ if (raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT) {
+ WRITE_NFC_IP_REG((raw_read(REG_NFC_OPS_STAT) &
+ ~NFC_OPS_STAT),
+ REG_NFC_OPS_STAT);
+ break;
+ }
+ udelay(1);
+ if (maxRetries <= 0) {
+ DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n",
+ __func__, __LINE__);
+ }
+ }
+ }
+}
+
+static inline void send_atomic_cmd(u16 cmd, bool useirq)
+{
+ /* fill command */
+ raw_write(cmd, REG_NFC_FLASH_CMD);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* send out command */
+ raw_write(NFC_CMD, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, useirq);
+}
+
+static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr);
+static int mxc_check_ecc_status(struct mtd_info *mtd);
+
+#ifdef NFC_AUTO_MODE_ENABLE
+/*!
+ * This function handle the interleave related work
+ * @param mtd mtd info
+ * @param cmd command
+ */
+static void auto_cmd_interleave(struct mtd_info *mtd, u16 cmd)
+{
+ u32 i, page_addr, ncs;
+ u32 j = num_of_interleave;
+ struct nand_chip *this = mtd->priv;
+ u32 addr_low = raw_read(NFC_FLASH_ADDR0);
+ u32 addr_high = raw_read(NFC_FLASH_ADDR8);
+ u8 *dbuf = data_buf;
+ u8 *obuf = oob_buf;
+ u32 dlen = mtd->writesize / j;
+ u32 olen = mtd->oobsize / j;
+
+ /* adjust the addr value
+ * since ADD_OP mode is 01
+ */
+ if (cmd == NAND_CMD_ERASE2)
+ page_addr = addr_low;
+ else
+ page_addr = addr_low >> 16 | addr_high << 16;
+
+ ncs = page_addr >> (this->chip_shift - this->page_shift);
+
+ if (j > 1) {
+ page_addr *= j;
+ } else {
+ page_addr *= this->numchips;
+ page_addr += ncs;
+ }
+
+ switch (cmd) {
+ case NAND_CMD_PAGEPROG:
+ for (i = 0; i < j; i++) {
+ /* reset addr cycle */
+ mxc_do_addr_cycle(mtd, 0, page_addr++);
+
+ /* data transfer */
+ memcpy(MAIN_AREA0, dbuf, dlen);
+ copy_spare(mtd, obuf, SPARE_AREA0, olen, false);
+
+ /* update the value */
+ dbuf += dlen;
+ obuf += olen;
+
+ NFC_SET_RBA(0);
+ ACK_OPS;
+ raw_write(NFC_AUTO_PROG, REG_NFC_OPS);
+
+ /* wait auto_prog_done bit set */
+ while (!(raw_read(REG_NFC_OPS_STAT) & NFC_OP_DONE)) ;
+ }
+
+ wait_op_done(TROP_US_DELAY, false);
+ while (!(raw_read(REG_NFC_OPS_STAT) & NFC_RB)) ;
+
+ break;
+ case NAND_CMD_READSTART:
+ for (i = 0; i < j; i++) {
+ /* reset addr cycle */
+ mxc_do_addr_cycle(mtd, 0, page_addr++);
+
+ NFC_SET_RBA(0);
+ ACK_OPS;
+ raw_write(NFC_AUTO_READ, REG_NFC_OPS);
+ wait_op_done(TROP_US_DELAY, false);
+
+ /* check ecc error */
+ mxc_check_ecc_status(mtd);
+
+ /* data transfer */
+ memcpy(dbuf, MAIN_AREA0, dlen);
+ copy_spare(mtd, obuf, SPARE_AREA0, olen, true);
+
+ /* update the value */
+ dbuf += dlen;
+ obuf += olen;
+ }
+ break;
+ case NAND_CMD_ERASE2:
+ for (i = 0; i < j; i++) {
+ mxc_do_addr_cycle(mtd, -1, page_addr++);
+ ACK_OPS;
+ raw_write(NFC_AUTO_ERASE, REG_NFC_OPS);
+ wait_op_done(TROP_US_DELAY, true);
+ }
+ break;
+ case NAND_CMD_RESET:
+ for (i = 0; i < j; i++) {
+ if (j > 1)
+ NFC_SET_NFC_ACTIVE_CS(i);
+ send_atomic_cmd(cmd, false);
+ }
+ break;
+ default:
+ break;
+ }
+}
+#endif
+
+static void send_addr(u16 addr, bool useirq);
+
+/*!
+ * This function issues the specified command to the NAND device and
+ * waits for completion.
+ *
+ * @param cmd command for NAND Flash
+ * @param useirq True if IRQ should be used rather than polling
+ */
+static void send_cmd(struct mtd_info *mtd, u16 cmd, bool useirq)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd, useirq);
+
+#ifdef NFC_AUTO_MODE_ENABLE
+ switch (cmd) {
+ case NAND_CMD_READ0:
+ case NAND_CMD_READOOB:
+ raw_write(NAND_CMD_READ0, REG_NFC_FLASH_CMD);
+ break;
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_ERASE1:
+ raw_write(cmd, REG_NFC_FLASH_CMD);
+ break;
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_READSTART:
+ raw_write(raw_read(REG_NFC_FLASH_CMD) | cmd << NFC_CMD_1_SHIFT,
+ REG_NFC_FLASH_CMD);
+ auto_cmd_interleave(mtd, cmd);
+ break;
+ case NAND_CMD_READID:
+ send_atomic_cmd(cmd, useirq);
+ send_addr(0, false);
+ break;
+ case NAND_CMD_RESET:
+ auto_cmd_interleave(mtd, cmd);
+ break;
+ case NAND_CMD_STATUS:
+ send_atomic_cmd(cmd, useirq);
+ break;
+ default:
+ break;
+ }
+#else
+ send_atomic_cmd(cmd, useirq);
+#endif
+}
+
+/*!
+ * This function sends an address (or partial address) to the
+ * NAND device. The address is used to select the source/destination for
+ * a NAND command.
+ *
+ * @param addr address to be written to NFC.
+ * @param useirq True if IRQ should be used rather than polling
+ */
+static void send_addr(u16 addr, bool useirq)
+{
+ DEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr, useirq);
+
+ /* fill address */
+ raw_write((addr << NFC_FLASH_ADDR_SHIFT), REG_NFC_FLASH_ADDR);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* send out address */
+ raw_write(NFC_ADDR, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, useirq);
+}
+
+/*!
+ * This function requests the NFC to initate the transfer
+ * of data currently in the NFC RAM buffer to the NAND device.
+ *
+ * @param buf_id Specify Internal RAM Buffer number
+ */
+static void send_prog_page(u8 buf_id)
+{
+#ifndef NFC_AUTO_MODE_ENABLE
+ DEBUG(MTD_DEBUG_LEVEL3, "%s\n", __FUNCTION__);
+
+ /* set ram buffer id */
+ NFC_SET_RBA(buf_id);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* transfer data from NFC ram to nand */
+ raw_write(NFC_INPUT, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, false);
+#endif
+}
+
+/*!
+ * This function requests the NFC to initated the transfer
+ * of data from the NAND device into in the NFC ram buffer.
+ *
+ * @param buf_id Specify Internal RAM Buffer number
+ */
+static void send_read_page(u8 buf_id)
+{
+#ifndef NFC_AUTO_MODE_ENABLE
+ DEBUG(MTD_DEBUG_LEVEL3, "%s(%d)\n", __FUNCTION__, buf_id);
+
+ /* set ram buffer id */
+ NFC_SET_RBA(buf_id);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* transfer data from nand to NFC ram */
+ raw_write(NFC_OUTPUT, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, false);
+#endif
+}
+
+/*!
+ * This function requests the NFC to perform a read of the
+ * NAND device ID.
+ */
+static void send_read_id(void)
+{
+ /* Set RBA bits for BUFFER0 */
+ NFC_SET_RBA(0);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* Read ID into main buffer */
+ raw_write(NFC_ID, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, false);
+
+}
+
+#ifdef NFC_AUTO_MODE_ENABLE
+static inline void read_dev_status(u16 *status)
+{
+ u32 mask = 0xFF << 16;
+
+ /* clear status */
+ ACK_OPS;
+
+
+ /* use atomic mode to read status instead
+ of using auto mode,auto-mode has issues
+ and the status is not correct.
+ */
+ raw_write(NFC_STATUS, REG_NFC_OPS);
+
+ wait_op_done(TROP_US_DELAY, false);
+
+ *status = (raw_read(NFC_CONFIG1) & mask) >> 16;
+
+}
+#endif
+
+/*!
+ * This function requests the NFC to perform a read of the
+ * NAND device status and returns the current status.
+ *
+ * @return device status
+ */
+static u16 get_dev_status(void)
+{
+#ifdef NFC_AUTO_MODE_ENABLE
+ int i;
+ u16 status = 0;
+ for (i = 0; i < num_of_interleave; i++) {
+
+ /* set ative cs */
+ NFC_SET_NFC_ACTIVE_CS(i);
+
+ /* FIXME, NFC Auto erase may have
+ * problem, have to pollingit until
+ * the nand get idle, otherwise
+ * it may get error
+ */
+ read_dev_status(&status);
+ if (status & NAND_STATUS_FAIL)
+ break;
+ }
+
+ return status;
+#else
+ volatile u16 *mainBuf = MAIN_AREA1;
+ u8 val = 1;
+ u16 ret;
+
+ /* Set ram buffer id */
+ NFC_SET_RBA(val);
+
+ /* clear status */
+ ACK_OPS;
+
+ /* Read status into main buffer */
+ raw_write(NFC_STATUS, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, false);
+
+ /* Status is placed in first word of main buffer */
+ /* get status, then recovery area 1 data */
+ ret = *mainBuf;
+
+ return ret;
+#endif
+}
+
+static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ raw_write((raw_read(REG_NFC_ECC_EN) | NFC_ECC_EN), REG_NFC_ECC_EN);
+ return;
+}
+
+/*
+ * Function to record the ECC corrected/uncorrected errors resulted
+ * after a page read. This NFC detects and corrects upto to 4 symbols
+ * of 9-bits each.
+ */
+static int mxc_check_ecc_status(struct mtd_info *mtd)
+{
+ u32 ecc_stat, err;
+ int no_subpages = 1;
+ int ret = 0;
+ u8 ecc_bit_mask, err_limit;
+
+ ecc_bit_mask = (IS_4BIT_ECC ? 0x7 : 0xf);
+ err_limit = (IS_4BIT_ECC ? 0x4 : 0x8);
+
+ no_subpages = mtd->writesize >> 9;
+
+ no_subpages /= num_of_interleave;
+
+ ecc_stat = GET_NFC_ECC_STATUS();
+ do {
+ err = ecc_stat & ecc_bit_mask;
+ if (err > err_limit) {
+ mtd->ecc_stats.failed++;
+ printk(KERN_WARNING "UnCorrectable RS-ECC Error\n");
+ return -1;
+ } else {
+ ret += err;
+ }
+ ecc_stat >>= 4;
+ } while (--no_subpages);
+
+ mtd->ecc_stats.corrected += ret;
+ pr_debug("%d Symbol Correctable RS-ECC Error\n", ret);
+
+ return ret;
+}
+
+/*
+ * Function to correct the detected errors. This NFC corrects all the errors
+ * detected. So this function just return 0.
+ */
+static int mxc_nand_correct_data(struct mtd_info *mtd, u_char * dat,
+ u_char * read_ecc, u_char * calc_ecc)
+{
+ return 0;
+}
+
+/*
+ * Function to calculate the ECC for the data to be stored in the Nand device.
+ * This NFC has a hardware RS(511,503) ECC engine together with the RS ECC
+ * CONTROL blocks are responsible for detection and correction of up to
+ * 8 symbols of 9 bits each in 528 byte page.
+ * So this function is just return 0.
+ */
+
+static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat,
+ u_char * ecc_code)
+{
+ return 0;
+}
+
+/*!
+ * This function id is used to read the data buffer from the NAND Flash. To
+ * read the data from NAND Flash first the data output cycle is initiated by
+ * the NFC, which copies the data to RAMbuffer. This data of length \b len is
+ * then copied to buffer \b buf.
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be read from NAND Flash
+ * @param len number of bytes to be read
+ */
+static void mxc_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len)
+{
+ u16 col = g_nandfc_info.colAddr;
+
+ if (mtd->writesize) {
+
+ int j = mtd->writesize - col;
+ int n = mtd->oobsize + j;
+
+ n = min(n, len);
+
+ if (j > 0) {
+ if (n > j) {
+ memcpy(buf, &data_buf[col], j);
+ memcpy(buf + j, &oob_buf[0], n - j);
+ } else {
+ memcpy(buf, &data_buf[col], n);
+ }
+ } else {
+ col -= mtd->writesize;
+ memcpy(buf, &oob_buf[col], len);
+ }
+
+ /* update */
+ g_nandfc_info.colAddr += n;
+
+ } else {
+ /* At flash identify phase,
+ * mtd->writesize has not been
+ * set correctly, it should
+ * be zero.And len will less 2
+ */
+ memcpy(buf, &data_buf[col], len);
+
+ /* update */
+ g_nandfc_info.colAddr += len;
+ }
+
+}
+
+/*!
+ * This function reads byte from the NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return data read from the NAND Flash
+ */
+static uint8_t mxc_nand_read_byte(struct mtd_info *mtd)
+{
+ uint8_t ret;
+
+ /* Check for status request */
+ if (g_nandfc_info.bStatusRequest) {
+ return (get_dev_status() & 0xFF);
+ }
+
+ mxc_nand_read_buf(mtd, &ret, 1);
+
+ return ret;
+}
+
+/*!
+ * This function reads word from the NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return data read from the NAND Flash
+ */
+static u16 mxc_nand_read_word(struct mtd_info *mtd)
+{
+ u16 ret;
+
+ mxc_nand_read_buf(mtd, (uint8_t *) &ret, sizeof(u16));
+
+ return ret;
+}
+
+/*!
+ * This function reads byte from the NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ *
+ * @return data read from the NAND Flash
+ */
+static u_char mxc_nand_read_byte16(struct mtd_info *mtd)
+{
+ /* Check for status request */
+ if (g_nandfc_info.bStatusRequest) {
+ return (get_dev_status() & 0xFF);
+ }
+
+ return mxc_nand_read_word(mtd) & 0xFF;
+}
+
+/*!
+ * This function writes data of length \b len from buffer \b buf to the NAND
+ * internal RAM buffer's MAIN area 0.
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be written to NAND Flash
+ * @param len number of bytes to be written
+ */
+static void mxc_nand_write_buf(struct mtd_info *mtd,
+ const u_char * buf, int len)
+{
+ u16 col = g_nandfc_info.colAddr;
+ int j = mtd->writesize - col;
+ int n = mtd->oobsize + j;
+
+ n = min(n, len);
+
+ if (j > 0) {
+ if (n > j) {
+ memcpy(&data_buf[col], buf, j);
+ memcpy(&oob_buf[0], buf + j, n - j);
+ } else {
+ memcpy(&data_buf[col], buf, n);
+ }
+ } else {
+ col -= mtd->writesize;
+ memcpy(&oob_buf[col], buf, len);
+ }
+
+ /* update */
+ g_nandfc_info.colAddr += n;
+}
+
+/*!
+ * This function is used by the upper layer to verify the data in NAND Flash
+ * with the data in the \b buf.
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param buf data to be verified
+ * @param len length of the data to be verified
+ *
+ * @return -EFAULT if error else 0
+ *
+ */
+static int mxc_nand_verify_buf(struct mtd_info *mtd, const u_char * buf,
+ int len)
+{
+ u_char *s = data_buf;
+
+ const u_char *p = buf;
+
+ for (; len > 0; len--) {
+ if (*p++ != *s++)
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is used by upper layer for select and deselect of the NAND
+ * chip
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param chip val indicating select or deselect
+ */
+static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+
+ switch (chip) {
+ case -1:
+ /* Disable the NFC clock */
+ clk_disable(nfc_clk);
+ break;
+ case 0 ... 7:
+ /* Enable the NFC clock */
+ clk_enable(nfc_clk);
+
+ NFC_SET_NFC_ACTIVE_CS(chip);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Function to perform the address cycles.
+ */
+static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
+{
+#ifdef NFC_AUTO_MODE_ENABLE
+
+ if (page_addr != -1 && column != -1) {
+ u32 mask = 0xFFFF;
+ /* the column address */
+ raw_write(column & mask, NFC_FLASH_ADDR0);
+ raw_write((raw_read(NFC_FLASH_ADDR0) |
+ ((page_addr & mask) << 16)), NFC_FLASH_ADDR0);
+ /* the row address */
+ raw_write(((raw_read(NFC_FLASH_ADDR8) & (mask << 16)) |
+ ((page_addr & (mask << 16)) >> 16)),
+ NFC_FLASH_ADDR8);
+ } else if (page_addr != -1) {
+ raw_write(page_addr, NFC_FLASH_ADDR0);
+ raw_write(0, NFC_FLASH_ADDR8);
+ }
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "AutoMode:the ADDR REGS value is (0x%x, 0x%x)\n",
+ raw_read(NFC_FLASH_ADDR0), raw_read(NFC_FLASH_ADDR8));
+#else
+
+ u32 page_mask = g_page_mask;
+
+ if (column != -1) {
+ send_addr(column & 0xFF, false);
+ if (IS_2K_PAGE_NAND) {
+ /* another col addr cycle for 2k page */
+ send_addr((column >> 8) & 0xF, false);
+ } else if (IS_4K_PAGE_NAND) {
+ /* another col addr cycle for 4k page */
+ send_addr((column >> 8) & 0x1F, false);
+ }
+ }
+ if (page_addr != -1) {
+ do {
+ send_addr((page_addr & 0xff), false);
+ page_mask >>= 8;
+ page_addr >>= 8;
+ } while (page_mask != 0);
+ }
+#endif
+}
+
+/*!
+ * This function is used by the upper layer to write command to NAND Flash for
+ * different operations to be carried out on NAND Flash
+ *
+ * @param mtd MTD structure for the NAND Flash
+ * @param command command for NAND Flash
+ * @param column column offset for the page read
+ * @param page_addr page to be read from NAND Flash
+ */
+static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ bool useirq = false;
+
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
+ command, column, page_addr);
+ /*
+ * Reset command state information
+ */
+ g_nandfc_info.bStatusRequest = false;
+
+ /*
+ * Command pre-processing step
+ */
+ switch (command) {
+ case NAND_CMD_STATUS:
+ g_nandfc_info.colAddr = 0;
+ g_nandfc_info.bStatusRequest = true;
+ break;
+
+ case NAND_CMD_READ0:
+ g_nandfc_info.colAddr = column;
+ break;
+
+ case NAND_CMD_READOOB:
+ g_nandfc_info.colAddr = column;
+ command = NAND_CMD_READ0;
+ break;
+
+ case NAND_CMD_SEQIN:
+ if (column != 0) {
+
+ /* FIXME: before send SEQIN command for
+ * partial write,We need read one page out.
+ * FSL NFC does not support partial write
+ * It alway send out 512+ecc+512+ecc ...
+ * for large page nand flash. But for small
+ * page nand flash, it did support SPARE
+ * ONLY operation. But to make driver
+ * simple. We take the same as large page,read
+ * whole page out and update. As for MLC nand
+ * NOP(num of operation) = 1. Partial written
+ * on one programed page is not allowed! We
+ * can't limit it on the driver, it need the
+ * upper layer applicaiton take care it
+ */
+
+ mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr);
+ }
+
+ g_nandfc_info.colAddr = column;
+ column = 0;
+
+ break;
+
+ case NAND_CMD_PAGEPROG:
+#ifndef NFC_AUTO_MODE_ENABLE
+ /* FIXME:the NFC interal buffer
+ * access has some limitation, it
+ * does not allow byte access. To
+ * make the code simple and ease use
+ * not every time check the address
+ * alignment.Use the temp buffer
+ * to accomadate the data.since We
+ * know data_buf will be at leat 4
+ * byte alignment, so we can use
+ * memcpy safely
+ */
+ nfc_memcpy(MAIN_AREA0, data_buf, mtd->writesize);
+ copy_spare(mtd, oob_buf, SPARE_AREA0, mtd->oobsize, false);
+#endif
+
+ if (IS_LARGE_PAGE_NAND)
+ PROG_PAGE();
+ else
+ send_prog_page(0);
+
+ useirq = true;
+
+ break;
+
+ case NAND_CMD_ERASE1:
+ break;
+ case NAND_CMD_ERASE2:
+ useirq = true;
+
+ break;
+ }
+
+ /*
+ * Write out the command to the device.
+ */
+ send_cmd(mtd, command, useirq);
+
+ mxc_do_addr_cycle(mtd, column, page_addr);
+
+ /*
+ * Command post-processing step
+ */
+ switch (command) {
+
+ case NAND_CMD_READOOB:
+ case NAND_CMD_READ0:
+ if (IS_LARGE_PAGE_NAND) {
+ /* send read confirm command */
+ send_cmd(mtd, NAND_CMD_READSTART, false);
+ /* read for each AREA */
+ READ_PAGE();
+ } else {
+ send_read_page(0);
+ }
+
+#ifndef NFC_AUTO_MODE_ENABLE
+ /* FIXME, the NFC interal buffer
+ * access has some limitation, it
+ * does not allow byte access. To
+ * make the code simple and ease use
+ * not every time check the address
+ * alignment.Use the temp buffer
+ * to accomadate the data.since We
+ * know data_buf will be at leat 4
+ * byte alignment, so we can use
+ * memcpy safely
+ */
+ nfc_memcpy(data_buf, MAIN_AREA0, mtd->writesize);
+ copy_spare(mtd, oob_buf, SPARE_AREA0, mtd->oobsize, true);
+#endif
+
+ break;
+
+ case NAND_CMD_READID:
+ send_read_id();
+ g_nandfc_info.colAddr = column;
+ nfc_memcpy(data_buf, MAIN_AREA0, 2048);
+
+ break;
+ }
+}
+
+static int mxc_nand_read_oob(struct mtd_info *mtd,
+ struct nand_chip *chip, int page, int sndcmd)
+{
+ if (sndcmd) {
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ sndcmd = 0;
+ }
+
+ memcpy(chip->oob_poi, oob_buf, mtd->oobsize);
+
+ return sndcmd;
+}
+
+static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t * buf)
+{
+
+#ifndef NFC_AUTO_MODE_ENABLE
+ mxc_check_ecc_status(mtd);
+#endif
+
+ memcpy(buf, data_buf, mtd->writesize);
+ memcpy(chip->oob_poi, oob_buf, mtd->oobsize);
+
+ return 0;
+}
+
+static void mxc_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t * buf)
+{
+ memcpy(data_buf, buf, mtd->writesize);
+ memcpy(oob_buf, chip->oob_poi, mtd->oobsize);
+
+}
+
+/* Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks. */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr smallpage_memorybased = {
+ .options = NAND_BBT_SCAN2NDPAGE,
+ .offs = 5,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_memorybased = {
+ .options = 0,
+ .offs = 0,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+
+/* Generic flash bbt decriptors
+*/
+static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION,
+ .offs = 0,
+ .len = 4,
+ .veroffs = 4,
+ .maxblocks = 4,
+ .pattern = mirror_pattern
+};
+
+static int mxc_nand_scan_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+
+ g_page_mask = this->pagemask;
+
+ /* limit to 2G size due to Kernel
+ * larger 4G space support,need fix
+ * it later
+ */
+ if (mtd->size == 0) {
+ mtd->size = 1 << 31;
+ this->numchips = 1;
+ this->chipsize = mtd->size;
+ }
+
+ if (IS_2K_PAGE_NAND) {
+ NFC_SET_NFMS(1 << NFMS_NF_PG_SZ);
+ this->ecc.layout = &nand_hw_eccoob_2k;
+ } else if (IS_4K_PAGE_NAND) {
+ NFC_SET_NFMS(1 << NFMS_NF_PG_SZ);
+ this->ecc.layout = &nand_hw_eccoob_4k;
+ } else {
+ this->ecc.layout = &nand_hw_eccoob_512;
+ }
+
+ /* propagate ecc.layout to mtd_info */
+ mtd->ecclayout = this->ecc.layout;
+
+ /* jffs2 not write oob */
+ mtd->flags &= ~MTD_OOB_WRITEABLE;
+
+ /* use flash based bbt */
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+
+ /* update flash based bbt */
+ this->options |= NAND_USE_FLASH_BBT;
+
+ if (!this->badblock_pattern) {
+ this->badblock_pattern = (mtd->writesize > 512) ?
+ &largepage_memorybased : &smallpage_memorybased;
+ }
+
+ /* Build bad block table */
+ return nand_scan_bbt(mtd, this->badblock_pattern);
+}
+
+static void mxc_nfc_init(void)
+{
+ /* Disable interrupt */
+ raw_write((raw_read(REG_NFC_INTRRUPT) | NFC_INT_MSK), REG_NFC_INTRRUPT);
+
+ /* disable spare enable */
+ raw_write(raw_read(REG_NFC_SP_EN) & ~NFC_SP_EN, REG_NFC_SP_EN);
+
+ /* Unlock the internal RAM Buffer */
+ raw_write(NFC_SET_BLS(NFC_BLS_UNLCOKED), REG_NFC_BLS);
+
+ /* Blocks to be unlocked */
+ UNLOCK_ADDR(0x0, 0xFFFF);
+
+ /* Unlock Block Command for given address range */
+ raw_write(NFC_SET_WPC(NFC_WPC_UNLOCK), REG_NFC_WPC);
+
+ /* Enable symetric mode by default except mx37TO1.0 */
+ if (!(cpu_is_mx37_rev(CHIP_REV_1_0) == 1))
+ raw_write(raw_read(REG_NFC_ONE_CYCLE) |
+ NFC_ONE_CYCLE, REG_NFC_ONE_CYCLE);
+}
+
+static int mxc_alloc_buf(void)
+{
+ int err = 0;
+
+ data_buf = kzalloc(NAND_MAX_PAGESIZE, GFP_KERNEL);
+ if (!data_buf) {
+ printk(KERN_ERR "%s: failed to allocate data_buf\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+ oob_buf = kzalloc(NAND_MAX_OOBSIZE, GFP_KERNEL);
+ if (!oob_buf) {
+ printk(KERN_ERR "%s: failed to allocate oob_buf\n", __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ out:
+ return err;
+}
+
+static void mxc_free_buf(void)
+{
+ kfree(data_buf);
+ kfree(oob_buf);
+}
+
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and
+ * remove functions
+ *
+ * @return The function always returns 0.
+ */
+static int __init mxcnd_probe(struct platform_device *pdev)
+{
+ struct nand_chip *this;
+ struct mtd_info *mtd;
+ struct flash_platform_data *flash = pdev->dev.platform_data;
+ int nr_parts = 0, err = 0;
+
+ nfc_axi_base = IO_ADDRESS(NFC_AXI_BASE_ADDR);
+ nfc_ip_base = IO_ADDRESS(NFC_BASE_ADDR);
+
+ /* init the nfc */
+ mxc_nfc_init();
+
+ /* init data buf */
+ if (mxc_alloc_buf())
+ goto out;
+
+ /* Allocate memory for MTD device structure and private data */
+ mxc_nand_data = kzalloc(sizeof(struct mxc_mtd_s), GFP_KERNEL);
+ if (!mxc_nand_data) {
+ printk(KERN_ERR "%s: failed to allocate mtd_info\n",
+ __FUNCTION__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memset((char *)&g_nandfc_info, 0, sizeof(g_nandfc_info));
+
+ mxc_nand_data->dev = &pdev->dev;
+ /* structures must be linked */
+ this = &mxc_nand_data->nand;
+ mtd = &mxc_nand_data->mtd;
+ mtd->priv = this;
+ mtd->owner = THIS_MODULE;
+
+ this->priv = mxc_nand_data;
+ this->cmdfunc = mxc_nand_command;
+ this->select_chip = mxc_nand_select_chip;
+ this->read_byte = mxc_nand_read_byte;
+ this->read_word = mxc_nand_read_word;
+ this->write_buf = mxc_nand_write_buf;
+ this->read_buf = mxc_nand_read_buf;
+ this->verify_buf = mxc_nand_verify_buf;
+ this->scan_bbt = mxc_nand_scan_bbt;
+
+ /* NAND bus width determines access funtions used by upper layer */
+ if (flash->width == 2) {
+ this->read_byte = mxc_nand_read_byte16;
+ this->options |= NAND_BUSWIDTH_16;
+ NFC_SET_NFMS(1 << NFMS_NF_DWIDTH);
+ } else {
+ NFC_SET_NFMS(0);
+ }
+
+ nfc_clk = clk_get(&pdev->dev, "nfc_clk");
+ clk_enable(nfc_clk);
+
+ init_waitqueue_head(&irq_waitq);
+ err = request_irq(MXC_INT_NANDFC, mxc_nfc_irq, 0, "mxc_nd", NULL);
+ if (err) {
+ goto out_1;
+ }
+
+ if (hardware_ecc) {
+ this->ecc.read_page = mxc_nand_read_page;
+ this->ecc.write_page = mxc_nand_write_page;
+ this->ecc.read_oob = mxc_nand_read_oob;
+ this->ecc.layout = &nand_hw_eccoob_512;
+ this->ecc.calculate = mxc_nand_calculate_ecc;
+ this->ecc.hwctl = mxc_nand_enable_hwecc;
+ this->ecc.correct = mxc_nand_correct_data;
+ this->ecc.mode = NAND_ECC_HW;
+ this->ecc.size = 512;
+ this->ecc.bytes = 9;
+ raw_write((raw_read(REG_NFC_ECC_EN) | NFC_ECC_EN),
+ REG_NFC_ECC_EN);
+ } else {
+ this->ecc.mode = NAND_ECC_SOFT;
+ raw_write((raw_read(REG_NFC_ECC_EN) & ~NFC_ECC_EN),
+ REG_NFC_ECC_EN);
+ }
+
+ /* config the gpio */
+ if (flash->init)
+ flash->init();
+
+ /* Reset NAND */
+ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* Scan to find existence of the device */
+ if (nand_scan(mtd, NFC_GET_MAXCHIP_SP())) {
+ DEBUG(MTD_DEBUG_LEVEL0,
+ "MXC_ND2: Unable to find any NAND device.\n");
+ err = -ENXIO;
+ goto out_1;
+ }
+
+ /* Register the partitions */
+#ifdef CONFIG_MTD_PARTITIONS
+#if defined(CONFIG_MACH_CCWMX51JS) || defined(CONFIG_MACH_CCMX51JS) || \
+ defined(CONFIG_MACH_CCWMX51) || defined(CONFIG_MACH_CCMX51)
+ mtd->name= "onboard_boot";
+#endif
+ nr_parts =
+ parse_mtd_partitions(mtd, part_probes, &mxc_nand_data->parts, 0);
+ if (nr_parts > 0)
+ add_mtd_partitions(mtd, mxc_nand_data->parts, nr_parts);
+ else if (flash->parts)
+ add_mtd_partitions(mtd, flash->parts, flash->nr_parts);
+ else
+#endif
+ {
+ pr_info("Registering %s as whole device\n", mtd->name);
+ add_mtd_device(mtd);
+ }
+
+ platform_set_drvdata(pdev, mtd);
+
+ return 0;
+
+ out_1:
+ kfree(mxc_nand_data);
+ out:
+ return err;
+
+}
+
+ /*!
+ * Dissociates the driver from the device.
+ *
+ * @param pdev the device structure used to give information on which
+ *
+ * @return The function always returns 0.
+ */
+
+static int __exit mxcnd_remove(struct platform_device *pdev)
+{
+ struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct flash_platform_data *flash = pdev->dev.platform_data;
+
+ if (flash->exit)
+ flash->exit();
+
+ mxc_free_buf();
+
+ clk_disable(nfc_clk);
+ clk_put(nfc_clk);
+ platform_set_drvdata(pdev, NULL);
+
+ if (mxc_nand_data) {
+ nand_release(mtd);
+ free_irq(MXC_INT_NANDFC, NULL);
+ kfree(mxc_nand_data);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function is called to put the NAND in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device information structure
+ *
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+
+static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mtd_info *info = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND2 : NAND suspend\n");
+ if (info)
+ ret = info->suspend(info);
+
+ /* Disable the NFC clock */
+ clk_disable(nfc_clk);
+
+ /* Disable the NFC clock */
+ clk_disable(nfc_clk);
+
+ return ret;
+}
+
+/*!
+ * This function is called to bring the NAND back from a low power state. Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device information structure
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxcnd_resume(struct platform_device *pdev)
+{
+ struct mtd_info *info = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND2 : NAND resume\n");
+ /* Enable the NFC clock */
+ clk_enable(nfc_clk);
+
+ if (info) {
+ info->resume(info);
+ }
+
+ return ret;
+}
+
+#else
+#define mxcnd_suspend NULL
+#define mxcnd_resume NULL
+#endif /* CONFIG_PM */
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcnd_driver = {
+ .driver = {
+ .name = "mxc_nandv2_flash",
+ },
+ .probe = mxcnd_probe,
+ .remove = __exit_p(mxcnd_remove),
+ .suspend = mxcnd_suspend,
+ .resume = mxcnd_resume,
+};
+
+/*!
+ * Main initialization routine
+ * @return 0 if successful; non-zero otherwise
+ */
+static int __init mxc_nd_init(void)
+{
+ /* Register the device driver structure. */
+ pr_info("MXC MTD nand Driver %s\n", DVR_VER);
+ if (platform_driver_register(&mxcnd_driver) != 0) {
+ printk(KERN_ERR "Driver register failed for mxcnd_driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/*!
+ * Clean up routine
+ */
+static void __exit mxc_nd_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&mxcnd_driver);
+}
+
+module_init(mxc_nd_init);
+module_exit(mxc_nd_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC NAND MTD driver Version 2-5");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/mxc_nd2.h b/drivers/mtd/nand/mxc_nd2.h
new file mode 100644
index 000000000000..b89b9318f372
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd2.h
@@ -0,0 +1,688 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_nd2.h
+ *
+ * @brief This file contains the NAND Flash Controller register information.
+ *
+ *
+ * @ingroup NAND_MTD
+ */
+
+#ifndef __MXC_ND2_H__
+#define __MXC_ND2_H__
+
+#include <mach/hardware.h>
+
+#define IS_2K_PAGE_NAND ((mtd->writesize / num_of_interleave) \
+ == NAND_PAGESIZE_2KB)
+#define IS_4K_PAGE_NAND ((mtd->writesize / num_of_interleave) \
+ == NAND_PAGESIZE_4KB)
+#define IS_LARGE_PAGE_NAND ((mtd->writesize / num_of_interleave) > 512)
+
+#define GET_NAND_OOB_SIZE (mtd->oobsize / num_of_interleave)
+
+#define NAND_PAGESIZE_2KB 2048
+#define NAND_PAGESIZE_4KB 4096
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3
+/*
+ * For V3 NFC registers Definition
+ */
+/* AXI Bus Mapped */
+#define NFC_AXI_BASE_ADDR NFC_BASE_ADDR_AXI
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) /* mx37 */
+#define MXC_INT_NANDFC MXC_INT_EMI
+#define NFC_FLASH_ADDR_CMD (nfc_axi_base + 0x1E00)
+#define NFC_CONFIG1 (nfc_axi_base + 0x1E04)
+#define NFC_ECC_STATUS_RESULT (nfc_axi_base + 0x1E08)
+#define LAUNCH_NFC (nfc_axi_base + 0x1E0c)
+#define NFC_WRPROT (nfc_ip_base + 0x00)
+#define NFC_WRPROT_UNLOCK_BLK_ADD0 (nfc_ip_base + 0x04)
+#define NFC_CONFIG2 (nfc_ip_base + 0x14)
+#define NFC_IPC (nfc_ip_base + 0x18)
+#elif defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) /* mx51 */
+#define MXC_INT_NANDFC MXC_INT_NFC
+#define NFC_AUTO_MODE_ENABLE
+#define NFC_FLASH_CMD (nfc_axi_base + 0x1E00)
+#define NFC_FLASH_ADDR0 (nfc_axi_base + 0x1E04)
+#define NFC_FLASH_ADDR8 (nfc_axi_base + 0x1E24)
+#define NFC_CONFIG1 (nfc_axi_base + 0x1E34)
+#define NFC_ECC_STATUS_RESULT (nfc_axi_base + 0x1E38)
+#define NFC_ECC_STATUS_SUM (nfc_axi_base + 0x1E3C)
+#define LAUNCH_NFC (nfc_axi_base + 0x1E40)
+#define NFC_WRPROT (nfc_ip_base + 0x00)
+#define NFC_WRPROT_UNLOCK_BLK_ADD0 (nfc_ip_base + 0x04)
+#define NFC_CONFIG2 (nfc_ip_base + 0x24)
+#define NFC_CONFIG3 (nfc_ip_base + 0x28)
+#define NFC_IPC (nfc_ip_base + 0x2C)
+#define NFC_DELAY_LINE (nfc_ip_base + 0x34)
+#else /* skye */
+#define NFC_FLASH_ADDR_CMD (nfc_axi_base + 0xE00)
+#define NFC_CONFIG1 (nfc_axi_base + 0xE04)
+#define NFC_ECC_STATUS_RESULT (nfc_axi_base + 0xE08)
+#define LAUNCH_NFC (nfc_axi_base + 0xE0C)
+#define NFC_WRPROT (nfc_ip_base + 0x00)
+#define NFC_WRPROT_UNLOCK_BLK_ADD0 (nfc_ip_base + 0x04)
+#define NFC_CONFIG2 (nfc_ip_base + 0x14)
+#define NFC_IPC (nfc_ip_base + 0x18)
+#endif
+/*!
+ * Addresses for NFC RAM BUFFER Main area 0
+ */
+#define MAIN_AREA0 ((u16 *)(nfc_axi_base + 0x000))
+#define MAIN_AREA1 ((u16 *)(nfc_axi_base + 0x200))
+
+/*!
+ * Addresses for NFC SPARE BUFFER Spare area 0
+ */
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \
+ defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2)
+#define SPARE_AREA0 ((u16 *)(nfc_axi_base + 0x1000))
+#define SPARE_LEN 64
+#define SPARE_COUNT 8
+#define SPARE_SIZE (SPARE_LEN * SPARE_COUNT)
+#else
+#define SPARE_AREA0 ((u16 *)(nfc_axi_base + 0x800))
+#define SPARE_LEN 16
+#define SPARE_COUNT 4
+#define SPARE_SIZE (SPARE_LEN * SPARE_COUNT)
+#endif
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \
+ defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2)
+#define NFC_SPAS_WIDTH 8
+#define NFC_SPAS_SHIFT 16
+
+#define IS_4BIT_ECC \
+( \
+ cpu_is_mx51_rev(CHIP_REV_2_0) > 0 ? \
+ !((raw_read(NFC_CONFIG2) & NFC_ECC_MODE_4) >> 6) : \
+ ((raw_read(NFC_CONFIG2) & NFC_ECC_MODE_4) >> 6) \
+)
+
+#define NFC_SET_SPAS(v) \
+ raw_write((((raw_read(NFC_CONFIG2) & \
+ NFC_FIELD_RESET(NFC_SPAS_WIDTH, NFC_SPAS_SHIFT)) | ((v) << 16))), \
+ NFC_CONFIG2)
+
+#define NFC_SET_ECC_MODE(v) \
+do { \
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) > 0) { \
+ if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ NFC_ECC_MODE_MASK) | \
+ NFC_ECC_MODE_4), NFC_CONFIG2); \
+ else \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ NFC_ECC_MODE_MASK) & \
+ NFC_ECC_MODE_8), NFC_CONFIG2); \
+ } else { \
+ if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ NFC_ECC_MODE_MASK) & \
+ NFC_ECC_MODE_8), NFC_CONFIG2); \
+ else \
+ raw_write(((raw_read(NFC_CONFIG2) & \
+ NFC_ECC_MODE_MASK) | \
+ NFC_ECC_MODE_4), NFC_CONFIG2); \
+ } \
+} while (0)
+
+#define WRITE_NFC_IP_REG(val,reg) \
+ do { \
+ raw_write(NFC_IPC_CREQ, NFC_IPC); \
+ while (!((raw_read(NFC_IPC) & NFC_IPC_ACK)>>1));\
+ raw_write(val, reg); \
+ raw_write(0, NFC_IPC); \
+ } while(0)
+
+#else
+#define IS_4BIT_ECC 1
+#define NFC_SET_SPAS(v)
+#define NFC_SET_ECC_MODE(v)
+#define NFC_SET_NFMS(v) (NFMS |= (v))
+
+#define WRITE_NFC_IP_REG(val,reg) \
+ raw_write((raw_read(REG_NFC_OPS_STAT) & ~NFC_OPS_STAT), \
+ REG_NFC_OPS_STAT)
+#endif
+
+#define GET_NFC_ECC_STATUS() raw_read(REG_NFC_ECC_STATUS_RESULT);
+
+/*!
+ * Set 1 to specific operation bit, rest to 0 in LAUNCH_NFC Register for
+ * Specific operation
+ */
+#define NFC_CMD 0x1
+#define NFC_ADDR 0x2
+#define NFC_INPUT 0x4
+#define NFC_OUTPUT 0x8
+#define NFC_ID 0x10
+#define NFC_STATUS 0x20
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 /* mx51 */
+#define NFC_AUTO_PROG 0x40
+#define NFC_AUTO_READ 0x80
+#define NFC_AUTO_ERASE 0x200
+#define NFC_COPY_BACK_0 0x400
+#define NFC_COPY_BACK_1 0x800
+#define NFC_AUTO_STATE 0x1000
+#endif
+
+/* Bit Definitions for NFC_IPC*/
+#define NFC_OPS_STAT (1 << 31)
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 /* mx51 */
+#define NFC_OP_DONE (1 << 30)
+#define NFC_RB (1 << 28)
+#define NFC_PS_WIDTH 2
+#define NFC_PS_SHIFT 0
+#define NFC_PS_512 0
+#define NFC_PS_2K 1
+#define NFC_PS_4K 2
+#else
+#define NFC_RB (1 << 29)
+#endif
+
+#define NFC_ONE_CYCLE (1 << 2)
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2 /* mx51 */
+#define NFC_INT_MSK (1 << 15)
+#define NFC_AUTO_PROG_DONE_MSK (1 << 14)
+#define NFC_NUM_ADDR_PHASE1_WIDTH 2
+#define NFC_NUM_ADDR_PHASE1_SHIFT 12
+
+#define NFC_NUM_ADDR_PHASE0_WIDTH 1
+#define NFC_NUM_ADDR_PHASE0_SHIFT 5
+
+#define NFC_ONE_LESS_PHASE1 0
+#define NFC_TWO_LESS_PHASE1 1
+
+#define NFC_FLASH_ADDR_SHIFT 0
+#else
+#define NFC_INT_MSK (1 << 4)
+#define NFC_BIG (1 << 5)
+#define NFC_FLASH_ADDR_SHIFT 16
+#endif
+
+#define NFC_UNLOCK_END_ADDR_SHIFT 16
+
+/* Bit definition for NFC_CONFIGRATION_1 */
+#define NFC_SP_EN (1 << 0)
+#define NFC_CE (1 << 1)
+#define NFC_RST (1 << 2)
+#define NFC_ECC_EN (1 << 3)
+
+#define NFC_FIELD_RESET(width, shift) ~(((1 << (width)) - 1) << (shift))
+
+#define NFC_RBA_SHIFT 4
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \
+ defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) /* mx51 */
+#define NFC_RBA_WIDTH 3
+#else
+#define NFC_RBA_WIDTH 2
+#endif
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2) /* mx51 */
+#define NFC_ITERATION_SHIFT 8
+#define NFC_ITERATION_WIDTH 4
+#define NFC_ACTIVE_CS_SHIFT 12
+#define NFC_ACTIVE_CS_WIDTH 3
+/* bit definition for CONFIGRATION3 */
+#define NFC_NO_SDMA (1 << 20)
+#define NFC_FMP_SHIFT 16
+#define NFC_FMP_WIDTH 4
+#define NFC_RBB_MODE (1 << 15)
+#define NFC_NUM_OF_DEVICES_SHIFT 12
+#define NFC_NUM_OF_DEVICES_WIDTH 4
+#define NFC_DMA_MODE_SHIFT 11
+#define NFC_DMA_MODE_WIDTH 1
+#define NFC_SBB_SHIFT 8
+#define NFC_SBB_WIDTH 3
+#define NFC_BIG (1 << 7)
+#define NFC_SB2R_SHIFT 4
+#define NFC_SB2R_WIDTH 3
+#define NFC_FW_SHIFT 3
+#define NFC_FW_WIDTH 1
+#define NFC_TOO (1 << 2)
+#define NFC_ADD_OP_SHIFT 0
+#define NFC_ADD_OP_WIDTH 2
+#define NFC_FW_8 1
+#define NFC_FW_16 0
+#define NFC_ST_CMD_SHITF 24
+#define NFC_ST_CMD_WIDTH 8
+#endif
+
+#define NFC_PPB_32 (0 << 7)
+#define NFC_PPB_64 (1 << 7)
+#define NFC_PPB_128 (2 << 7)
+#define NFC_PPB_256 (3 << 7)
+#define NFC_PPB_RESET ~(3 << 7)
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2)
+#define NFC_BLS_LOCKED (0 << 6)
+#define NFC_BLS_LOCKED_DEFAULT (1 << 6)
+#define NFC_BLS_UNLCOKED (2 << 6)
+#define NFC_BLS_RESET (~(3 << 6))
+#else
+#define NFC_BLS_LOCKED (0 << 16)
+#define NFC_BLS_LOCKED_DEFAULT (1 << 16)
+#define NFC_BLS_UNLCOKED (2 << 16)
+#define NFC_BLS_RESET (~(3 << 16))
+#endif
+
+#define NFC_WPC_LOCK_TIGHT 1
+#define NFC_WPC_LOCK (1 << 1)
+#define NFC_WPC_UNLOCK (1 << 2)
+#define NFC_WPC_RESET ~(7)
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_1) || \
+ defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2)
+#define NFC_ECC_MODE_4 (1 << 6)
+#define NFC_ECC_MODE_8 ~(1 << 6)
+#define NFC_ECC_MODE_MASK ~(1 << 6)
+#define NFC_SPAS_16 8
+#define NFC_SPAS_64 32
+#define NFC_SPAS_128 64
+#define NFC_SPAS_112 56
+#define NFC_SPAS_218 109
+#define NFC_IPC_CREQ (1 << 0)
+#define NFC_IPC_ACK (1 << 1)
+#endif
+
+#define REG_NFC_OPS_STAT NFC_IPC
+#define REG_NFC_INTRRUPT NFC_CONFIG2
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2
+#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR0
+#define REG_NFC_FLASH_CMD NFC_FLASH_CMD
+#else
+#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR_CMD
+#define REG_NFC_FLASH_CMD NFC_FLASH_ADDR_CMD
+#endif
+#define REG_NFC_OPS LAUNCH_NFC
+#define REG_NFC_SET_RBA NFC_CONFIG1
+#define REG_NFC_RB NFC_IPC
+#define REG_NFC_ECC_EN NFC_CONFIG2
+#define REG_NFC_ECC_STATUS_RESULT NFC_ECC_STATUS_RESULT
+#define REG_NFC_CE NFC_CONFIG1
+#define REG_NFC_RST NFC_CONFIG1
+#define REG_NFC_PPB NFC_CONFIG2
+#define REG_NFC_SP_EN NFC_CONFIG1
+#define REG_NFC_BLS NFC_WRPROT
+#define REG_UNLOCK_BLK_ADD0 NFC_WRPROT_UNLOCK_BLK_ADD0
+#define REG_UNLOCK_BLK_ADD1 NFC_WRPROT_UNLOCK_BLK_ADD1
+#define REG_UNLOCK_BLK_ADD2 NFC_WRPROT_UNLOCK_BLK_ADD2
+#define REG_UNLOCK_BLK_ADD3 NFC_WRPROT_UNLOCK_BLK_ADD3
+#define REG_NFC_WPC NFC_WRPROT
+#define REG_NFC_ONE_CYCLE NFC_CONFIG2
+
+/* NFC V3 Specific MACRO functions definitions */
+#define raw_write(v,a) __raw_writel(v,a)
+#define raw_read(a) __raw_readl(a)
+
+/* Explcit ack ops status (if any), before issue of any command */
+#define ACK_OPS \
+ raw_write((raw_read(REG_NFC_OPS_STAT) & ~NFC_OPS_STAT), \
+ REG_NFC_OPS_STAT);
+
+/* Set RBA buffer id*/
+#define NFC_SET_RBA(val) \
+ raw_write((raw_read(REG_NFC_SET_RBA) & \
+ (NFC_FIELD_RESET(NFC_RBA_WIDTH, NFC_RBA_SHIFT))) | \
+ ((val) << NFC_RBA_SHIFT), REG_NFC_SET_RBA);
+
+#define NFC_SET_PS(val) \
+ raw_write((raw_read(NFC_CONFIG2) & \
+ (NFC_FIELD_RESET(NFC_PS_WIDTH, NFC_PS_SHIFT))) | \
+ ((val) << NFC_PS_SHIFT), NFC_CONFIG2);
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_2
+#define UNLOCK_ADDR(start_addr,end_addr) \
+{ \
+ int i = 0; \
+ for (; i < NAND_MAX_CHIPS; i++) \
+ raw_write(start_addr | \
+ (end_addr << NFC_UNLOCK_END_ADDR_SHIFT), \
+ REG_UNLOCK_BLK_ADD0 + (i << 2)); \
+}
+#define NFC_SET_NFC_ACTIVE_CS(val) \
+ raw_write((raw_read(NFC_CONFIG1) & \
+ (NFC_FIELD_RESET(NFC_ACTIVE_CS_WIDTH, NFC_ACTIVE_CS_SHIFT))) | \
+ ((val) << NFC_ACTIVE_CS_SHIFT), NFC_CONFIG1);
+
+#define NFC_GET_MAXCHIP_SP() 8
+
+#else
+#define UNLOCK_ADDR(start_addr,end_addr) \
+ raw_write(start_addr | \
+ (end_addr << NFC_UNLOCK_END_ADDR_SHIFT), REG_UNLOCK_BLK_ADD0);
+
+#define NFC_SET_NFC_ACTIVE_CS(val)
+#define NFC_GET_MAXCHIP_SP() 1
+#endif
+
+#define NFC_SET_BLS(val) ((raw_read(REG_NFC_BLS) & NFC_BLS_RESET) | val )
+#define NFC_SET_WPC(val) ((raw_read(REG_NFC_WPC) & NFC_WPC_RESET) | val )
+#define CHECK_NFC_RB raw_read(REG_NFC_RB) & NFC_RB
+
+#if defined(CONFIG_ARCH_MXC_HAS_NFC_V3_2)
+#define NFC_SET_NFC_NUM_ADDR_PHASE1(val) \
+ raw_write((raw_read(NFC_CONFIG2) & \
+ (NFC_FIELD_RESET(NFC_NUM_ADDR_PHASE1_WIDTH, \
+ NFC_NUM_ADDR_PHASE1_SHIFT))) | \
+ ((val) << NFC_NUM_ADDR_PHASE1_SHIFT), NFC_CONFIG2);
+
+#define NFC_SET_NFC_NUM_ADDR_PHASE0(val) \
+ raw_write((raw_read(NFC_CONFIG2) & \
+ (NFC_FIELD_RESET(NFC_NUM_ADDR_PHASE0_WIDTH, \
+ NFC_NUM_ADDR_PHASE0_SHIFT))) | \
+ ((val) << NFC_NUM_ADDR_PHASE0_SHIFT), NFC_CONFIG2);
+
+#define NFC_SET_NFC_ITERATION(val) \
+ raw_write((raw_read(NFC_CONFIG1) & \
+ (NFC_FIELD_RESET(NFC_ITERATION_WIDTH, NFC_ITERATION_SHIFT))) | \
+ ((val) << NFC_ITERATION_SHIFT), NFC_CONFIG1);
+
+#define NFC_SET_FW(val) \
+ raw_write((raw_read(NFC_CONFIG3) & \
+ (NFC_FIELD_RESET(NFC_FW_WIDTH, NFC_FW_SHIFT))) | \
+ ((val) << NFC_FW_SHIFT), NFC_CONFIG3);
+
+#define NFC_SET_NUM_OF_DEVICE(val) \
+ raw_write((raw_read(NFC_CONFIG3) & \
+ (NFC_FIELD_RESET(NFC_NUM_OF_DEVICES_WIDTH, \
+ NFC_NUM_OF_DEVICES_SHIFT))) | \
+ ((val) << NFC_NUM_OF_DEVICES_SHIFT), NFC_CONFIG3);
+
+#define NFC_SET_ADD_OP_MODE(val) \
+ raw_write((raw_read(NFC_CONFIG3) & \
+ (NFC_FIELD_RESET(NFC_ADD_OP_WIDTH, NFC_ADD_OP_SHIFT))) | \
+ ((val) << NFC_ADD_OP_SHIFT), NFC_CONFIG3);
+
+#define NFC_SET_ADD_CS_MODE(val) \
+{ \
+ NFC_SET_ADD_OP_MODE(val); \
+ NFC_SET_NUM_OF_DEVICE(this->numchips - 1); \
+}
+
+#define NFC_SET_ST_CMD(val) \
+ raw_write((raw_read(NFC_CONFIG2) & \
+ (NFC_FIELD_RESET(NFC_ST_CMD_WIDTH, \
+ NFC_ST_CMD_SHITF))) | \
+ ((val) << NFC_ST_CMD_SHITF), NFC_CONFIG2);
+
+#define NFMS_NF_DWIDTH 0
+#define NFMS_NF_PG_SZ 1
+#define NFC_CMD_1_SHIFT 8
+
+#define NUM_OF_ADDR_CYCLE (fls(g_page_mask) >> 3)
+#define SET_NFC_DELAY_LINE(val) raw_write((val), NFC_DELAY_LINE)
+
+/*should set the fw,ps,spas,ppb*/
+#define NFC_SET_NFMS(v) \
+do { \
+ if (!(v)) \
+ NFC_SET_FW(NFC_FW_8); \
+ if (((v) & (1 << NFMS_NF_DWIDTH))) \
+ NFC_SET_FW(NFC_FW_16); \
+ if (((v) & (1 << NFMS_NF_PG_SZ))) { \
+ if (IS_2K_PAGE_NAND) { \
+ NFC_SET_PS(NFC_PS_2K); \
+ NFC_SET_NFC_NUM_ADDR_PHASE1(NUM_OF_ADDR_CYCLE); \
+ NFC_SET_NFC_NUM_ADDR_PHASE0(NFC_TWO_LESS_PHASE1); \
+ } else if (IS_4K_PAGE_NAND) { \
+ NFC_SET_PS(NFC_PS_4K); \
+ NFC_SET_NFC_NUM_ADDR_PHASE1(NUM_OF_ADDR_CYCLE); \
+ NFC_SET_NFC_NUM_ADDR_PHASE0(NFC_TWO_LESS_PHASE1); \
+ } else { \
+ NFC_SET_PS(NFC_PS_512); \
+ NFC_SET_NFC_NUM_ADDR_PHASE1(NUM_OF_ADDR_CYCLE - 1); \
+ NFC_SET_NFC_NUM_ADDR_PHASE0(NFC_ONE_LESS_PHASE1); \
+ } \
+ NFC_SET_ADD_CS_MODE(1); \
+ NFC_SET_SPAS(GET_NAND_OOB_SIZE >> 1); \
+ NFC_SET_ECC_MODE(GET_NAND_OOB_SIZE >> 1); \
+ NFC_SET_ST_CMD(0x70); \
+ raw_write(raw_read(NFC_CONFIG3) | NFC_NO_SDMA, NFC_CONFIG3); \
+ raw_write(raw_read(NFC_CONFIG3) | NFC_RBB_MODE, NFC_CONFIG3); \
+ SET_NFC_DELAY_LINE(0); \
+ } \
+} while (0)
+#endif
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3_1
+#define NFC_SET_NFMS(v)
+#endif
+
+#define READ_PAGE() send_read_page(0)
+#define PROG_PAGE() send_prog_page(0)
+
+#elif CONFIG_ARCH_MXC_HAS_NFC_V2
+
+/*
+ * For V1/V2 NFC registers Definition
+ */
+
+#define NFC_AXI_BASE_ADDR 0x00
+/*
+ * Addresses for NFC registers
+ */
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define NFC_REG_BASE (nfc_ip_base + 0x1000)
+#else
+#define NFC_REG_BASE nfc_ip_base
+#endif
+#define NFC_BUF_SIZE (NFC_REG_BASE + 0xE00)
+#define NFC_BUF_ADDR (NFC_REG_BASE + 0xE04)
+#define NFC_FLASH_ADDR (NFC_REG_BASE + 0xE06)
+#define NFC_FLASH_CMD (NFC_REG_BASE + 0xE08)
+#define NFC_CONFIG (NFC_REG_BASE + 0xE0A)
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define NFC_ECC_STATUS_RESULT (NFC_REG_BASE + 0xE0C)
+#define NFC_ECC_STATUS_RESULT_1 (NFC_REG_BASE + 0xE0C)
+#define NFC_ECC_STATUS_RESULT_2 (NFC_REG_BASE + 0xE0E)
+#define NFC_SPAS (NFC_REG_BASE + 0xE10)
+#else
+#define NFC_ECC_STATUS_RESULT (NFC_REG_BASE + 0xE0C)
+#define NFC_RSLTMAIN_AREA (NFC_REG_BASE + 0xE0E)
+#define NFC_RSLTSPARE_AREA (NFC_REG_BASE + 0xE10)
+#endif
+#define NFC_WRPROT (NFC_REG_BASE + 0xE12)
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define NFC_UNLOCKSTART_BLKADDR (NFC_REG_BASE + 0xE20)
+#define NFC_UNLOCKEND_BLKADDR (NFC_REG_BASE + 0xE22)
+#define NFC_UNLOCKSTART_BLKADDR1 (NFC_REG_BASE + 0xE24)
+#define NFC_UNLOCKEND_BLKADDR1 (NFC_REG_BASE + 0xE26)
+#define NFC_UNLOCKSTART_BLKADDR2 (NFC_REG_BASE + 0xE28)
+#define NFC_UNLOCKEND_BLKADDR2 (NFC_REG_BASE + 0xE2A)
+#define NFC_UNLOCKSTART_BLKADDR3 (NFC_REG_BASE + 0xE2C)
+#define NFC_UNLOCKEND_BLKADDR3 (NFC_REG_BASE + 0xE2E)
+#else
+#define NFC_UNLOCKSTART_BLKADDR (NFC_REG_BASE + 0xE14)
+#define NFC_UNLOCKEND_BLKADDR (NFC_REG_BASE + 0xE16)
+#endif
+#define NFC_NF_WRPRST (NFC_REG_BASE + 0xE18)
+#define NFC_CONFIG1 (NFC_REG_BASE + 0xE1A)
+#define NFC_CONFIG2 (NFC_REG_BASE + 0xE1C)
+
+/*!
+ * Addresses for NFC RAM BUFFER Main area 0
+ */
+#define MAIN_AREA0 (u16 *)(nfc_ip_base + 0x000)
+#define MAIN_AREA1 (u16 *)(nfc_ip_base + 0x200)
+
+/*!
+ * Addresses for NFC SPARE BUFFER Spare area 0
+ */
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define SPARE_AREA0 (u16 *)(nfc_ip_base + 0x1000)
+#define SPARE_LEN 64
+#define SPARE_COUNT 8
+#else
+#define SPARE_AREA0 (u16 *)(nfc_ip_base + 0x800)
+#define SPARE_LEN 16
+#define SPARE_COUNT 4
+#endif
+#define SPARE_SIZE (SPARE_LEN * SPARE_COUNT)
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define REG_NFC_ECC_MODE NFC_CONFIG1
+#define SPAS_SHIFT (0)
+#define REG_NFC_SPAS NFC_SPAS
+#define SPAS_MASK (0xFF00)
+#define IS_4BIT_ECC \
+ ((raw_read(REG_NFC_ECC_MODE) & NFC_ECC_MODE_4) >> 0)
+
+#define NFC_SET_SPAS(v) \
+ raw_write(((raw_read(REG_NFC_SPAS) & SPAS_MASK) | ((v<<SPAS_SHIFT))), \
+ REG_NFC_SPAS)
+
+#define NFC_SET_ECC_MODE(v) \
+do { \
+ if ((v) == NFC_SPAS_218 || (v) == NFC_SPAS_112) { \
+ raw_write((raw_read(REG_NFC_ECC_MODE) & NFC_ECC_MODE_8), \
+ REG_NFC_ECC_MODE); \
+ } else { \
+ raw_write((raw_read(REG_NFC_ECC_MODE) | NFC_ECC_MODE_4), \
+ REG_NFC_ECC_MODE); \
+ } \
+} while (0)
+
+#define GET_ECC_STATUS() __raw_readl(REG_NFC_ECC_STATUS_RESULT);
+#define NFC_SET_NFMS(v) \
+do { \
+ (NFMS |= (v)); \
+ if (((v) & (1 << NFMS_NF_PG_SZ))) { \
+ NFC_SET_SPAS(GET_NAND_OOB_SIZE >> 1); \
+ NFC_SET_ECC_MODE(GET_NAND_OOB_SIZE >> 1); \
+ } \
+} while (0)
+#else
+#define IS_4BIT_ECC (1)
+#define NFC_SET_SPAS(v)
+#define NFC_SET_ECC_MODE(v)
+#define GET_ECC_STATUS() raw_read(REG_NFC_ECC_STATUS_RESULT);
+#define NFC_SET_NFMS(v) (NFMS |= (v))
+#endif
+
+#define WRITE_NFC_IP_REG(val,reg) \
+ raw_write((raw_read(REG_NFC_OPS_STAT) & ~NFC_OPS_STAT), \
+ REG_NFC_OPS_STAT)
+
+#define GET_NFC_ECC_STATUS() raw_read(REG_NFC_ECC_STATUS_RESULT);
+
+/*!
+ * Set INT to 0, Set 1 to specific operation bit, rest to 0 in LAUNCH_NFC Register for
+ * Specific operation
+ */
+#define NFC_CMD 0x1
+#define NFC_ADDR 0x2
+#define NFC_INPUT 0x4
+#define NFC_OUTPUT 0x8
+#define NFC_ID 0x10
+#define NFC_STATUS 0x20
+
+/* Bit Definitions */
+#define NFC_OPS_STAT (1 << 15)
+#define NFC_SP_EN (1 << 2)
+#define NFC_ECC_EN (1 << 3)
+#define NFC_INT_MSK (1 << 4)
+#define NFC_BIG (1 << 5)
+#define NFC_RST (1 << 6)
+#define NFC_CE (1 << 7)
+#define NFC_ONE_CYCLE (1 << 8)
+#define NFC_BLS_LOCKED 0
+#define NFC_BLS_LOCKED_DEFAULT 1
+#define NFC_BLS_UNLCOKED 2
+#define NFC_WPC_LOCK_TIGHT 1
+#define NFC_WPC_LOCK (1 << 1)
+#define NFC_WPC_UNLOCK (1 << 2)
+#define NFC_FLASH_ADDR_SHIFT 0
+#define NFC_UNLOCK_END_ADDR_SHIFT 0
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define NFC_ECC_MODE_4 (1<<0)
+#define NFC_ECC_MODE_8 ~(1<<0)
+#define NFC_SPAS_16 8
+#define NFC_SPAS_64 32
+#define NFC_SPAS_112 56
+#define NFC_SPAS_128 64
+#define NFC_SPAS_218 109
+#endif
+/* NFC Register Mapping */
+#define REG_NFC_OPS_STAT NFC_CONFIG2
+#define REG_NFC_INTRRUPT NFC_CONFIG1
+#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR
+#define REG_NFC_FLASH_CMD NFC_FLASH_CMD
+#define REG_NFC_OPS NFC_CONFIG2
+#define REG_NFC_SET_RBA NFC_BUF_ADDR
+#define REG_NFC_ECC_EN NFC_CONFIG1
+#define REG_NFC_ECC_STATUS_RESULT NFC_ECC_STATUS_RESULT
+#define REG_NFC_CE NFC_CONFIG1
+#define REG_NFC_SP_EN NFC_CONFIG1
+#define REG_NFC_BLS NFC_CONFIG
+#define REG_NFC_WPC NFC_WRPROT
+#define REG_START_BLKADDR NFC_UNLOCKSTART_BLKADDR
+#define REG_END_BLKADDR NFC_UNLOCKEND_BLKADDR
+#define REG_NFC_RST NFC_CONFIG1
+#define REG_NFC_ONE_CYCLE NFC_CONFIG1
+
+/* NFC V1/V2 Specific MACRO functions definitions */
+
+#define raw_write(v,a) __raw_writew(v,a)
+#define raw_read(a) __raw_readw(a)
+
+#define NFC_SET_BLS(val) val
+
+#define UNLOCK_ADDR(start_addr,end_addr) \
+{ \
+ raw_write(start_addr,REG_START_BLKADDR); \
+ raw_write(end_addr,REG_END_BLKADDR); \
+}
+
+#define NFC_SET_NFC_ACTIVE_CS(val)
+#define NFC_GET_MAXCHIP_SP() 1
+#define NFC_SET_WPC(val) val
+
+/* NULL Definitions */
+#define ACK_OPS
+#define NFC_SET_RBA(val) raw_write(val, REG_NFC_SET_RBA);
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V2_1
+#define READ_PAGE() send_read_page(0)
+#define PROG_PAGE() send_prog_page(0)
+#else
+#define READ_PAGE() \
+do { \
+ send_read_page(0); \
+ send_read_page(1); \
+ send_read_page(2); \
+ send_read_page(3); \
+} while (0)
+
+#define PROG_PAGE() \
+do { \
+ send_prog_page(0); \
+ send_prog_page(1); \
+ send_prog_page(2); \
+ send_prog_page(3); \
+} while (0)
+#endif
+#define CHECK_NFC_RB 1
+
+#endif
+
+#endif /* __MXC_ND2_H__ */
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 0a9c9cd33f96..338d7b61426a 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1033,7 +1033,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
*
* Internal function. Called with chip held.
*/
-static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int chipnr, page, realpage, col, bytes, aligned;
@@ -1161,6 +1161,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
}
+EXPORT_SYMBOL(nand_do_read_ops);
/**
* nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
@@ -1703,7 +1704,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
*
* NAND write with ECC
*/
-static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
int chipnr, realpage, page, blockmask, column;
@@ -1795,6 +1796,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
ops->oobretlen = ops->ooblen;
return ret;
}
+EXPORT_SYMBOL(nand_do_write_ops);
/**
* nand_write - [MTD Interface] NAND write with ECC
@@ -2181,6 +2183,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
/* Return more or less happy */
return ret;
}
+EXPORT_SYMBOL_GPL(nand_erase_nand);
/**
* nand_sync - [MTD Interface] sync
@@ -2378,7 +2381,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
mtd->writesize = 1024 << (extid & 0x3);
extid >>= 2;
/* Calc oobsize */
- mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
+ mtd->oobsize = (*maf_id == 0x2c && dev_id == 0xd5) ?
+ 218 : (8 << (extid & 0x01)) * (mtd->writesize >> 9);
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03);
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 69ee2c90eb0b..74f49ed0afa8 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -109,6 +109,9 @@ struct nand_flash_dev nand_flash_ids[] = {
{"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16},
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
+ /* 32 Gigabit ,only use 2G due to the linux mtd limitation*/
+ {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 2048, 0, LP_OPTIONS},
+
/*
* Renesas AND 1 Gigabit. Those chips do not support extended id and
* have a strange page/block layout ! The chosen minimum erasesize is
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 556139ed1fdf..e910b5f0d780 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -1,10 +1,26 @@
/* linux/drivers/mtd/nand/s3c2410.c
*
- * Copyright © 2004-2008 Simtec Electronics
- * http://armlinux.simtec.co.uk/
+ * Copyright (c) 2004,2005 Simtec Electronics
+ * http://www.simtec.co.uk/products/SWLINUX/
* Ben Dooks <ben@simtec.co.uk>
*
- * Samsung S3C2410/S3C2440/S3C2412 NAND driver
+ * Samsung S3C2410/S3C240 NAND driver
+ *
+ * Changelog:
+ * 21-Sep-2004 BJD Initial version
+ * 23-Sep-2004 BJD Multiple device support
+ * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode
+ * 12-Oct-2004 BJD Fixed errors in use of platform data
+ * 18-Feb-2005 BJD Fix sparse errors
+ * 14-Mar-2005 BJD Applied tglx's code reduction patch
+ * 02-May-2005 BJD Fixed s3c2440 support
+ * 02-May-2005 BJD Reduced hwcontrol decode
+ * 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug
+ * 08-Jul-2005 BJD Fix OOPS when no platform data supplied
+ * 20-Oct-2005 BJD Fix timing calculation bug
+ * 14-Jan-2006 BJD Allow clock to be stopped when idle
+ *
+ * $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 bjd Exp $
*
* 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
@@ -19,11 +35,7 @@
* 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
-*/
-
-#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
-#define DEBUG
-#endif
+ */
#include <linux/module.h>
#include <linux/types.h>
@@ -36,7 +48,6 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
-#include <linux/cpufreq.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
@@ -48,6 +59,14 @@
#include <asm/plat-s3c/regs-nand.h>
#include <asm/plat-s3c/nand.h>
+/* Name of the kernel boot parameter with the partitions table */
+#define PARTITON_PARAMETER_NAME "onboard_boot"
+
+#if defined(CONFIG_MTD_NAND_S3C2410_DEBUG) && !defined(DEBUG)
+#define DEBUG
+#endif
+
+
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
static int hardware_ecc = 1;
#else
@@ -60,14 +79,34 @@ static int clock_stop = 1;
static const int clock_stop = 0;
#endif
-
-/* new oob placement block for use with hardware ecc generation
+/*
+ * These are the values that we are using in the U-Boot too.
+ * Luis Galdos
*/
-
static struct nand_ecclayout nand_hw_eccoob = {
- .eccbytes = 3,
- .eccpos = {0, 1, 2},
- .oobfree = {{8, 8}}
+ .eccbytes = 4,
+ .eccpos = { 40, 41, 42, 43 },
+ .oobfree = {
+ {
+ .offset = 2,
+ .length = 38
+ }
+ }
+};
+
+/*
+ * Nand flash oob definition for SLC 512b page size by jsgood
+ * IMPORTANT: These values are coming from the U-Boot
+ */
+static struct nand_ecclayout nand_hw_eccoob_16 = {
+ .eccbytes = 6,
+ .eccpos = { 0, 1, 2, 3, 6, 7 },
+ .oobfree = {
+ {
+ .offset = 8,
+ .length = 8
+ }
+ }
};
/* controller and mtd information */
@@ -105,13 +144,8 @@ struct s3c2410_nand_info {
int sel_bit;
int mtd_count;
unsigned long save_sel;
- unsigned long clk_rate;
enum s3c_cpu_type cpu_type;
-
-#ifdef CONFIG_CPU_FREQ
- struct notifier_block freq_transition;
-#endif
};
/* conversion functions */
@@ -169,18 +203,17 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
/* controller setup */
-static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
+static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
+ struct platform_device *pdev)
{
- struct s3c2410_platform_nand *plat = info->platform;
+ struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
+ unsigned long clkrate = clk_get_rate(info->clk);
int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
int tacls, twrph0, twrph1;
- unsigned long clkrate = clk_get_rate(info->clk);
- unsigned long set, cfg, mask;
- unsigned long flags;
+ unsigned long cfg = 0;
/* calculate the timing information for the controller */
- info->clk_rate = clkrate;
clkrate /= 1000; /* turn clock into kHz for ease of use */
if (plat != NULL) {
@@ -200,71 +233,39 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info)
}
dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
- tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
-
- switch (info->cpu_type) {
- case TYPE_S3C2410:
- mask = (S3C2410_NFCONF_TACLS(3) |
- S3C2410_NFCONF_TWRPH0(7) |
- S3C2410_NFCONF_TWRPH1(7));
- set = S3C2410_NFCONF_EN;
- set |= S3C2410_NFCONF_TACLS(tacls - 1);
- set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
- set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
- break;
-
- case TYPE_S3C2440:
- case TYPE_S3C2412:
- mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) |
- S3C2410_NFCONF_TWRPH0(7) |
- S3C2410_NFCONF_TWRPH1(7));
-
- set = S3C2440_NFCONF_TACLS(tacls - 1);
- set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
- set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
- break;
-
- default:
- /* keep compiler happy */
- mask = 0;
- set = 0;
- BUG();
- }
-
- dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
-
- local_irq_save(flags);
-
+ tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
+
+
+ /*
+ * First get the current value of the configuration register, otherwise
+ * some chip-information that is coming from the bootloader will be
+ * gone (e.g. the page size, which is required for the HWECC)
+ * Luis Galdos
+ */
cfg = readl(info->regs + S3C2410_NFCONF);
- cfg &= ~mask;
- cfg |= set;
- writel(cfg, info->regs + S3C2410_NFCONF);
-
- local_irq_restore(flags);
-
- return 0;
-}
-
-static int s3c2410_nand_inithw(struct s3c2410_nand_info *info)
-{
- int ret;
-
- ret = s3c2410_nand_setrate(info);
- if (ret < 0)
- return ret;
-
+
switch (info->cpu_type) {
case TYPE_S3C2410:
- default:
+ cfg |= S3C2410_NFCONF_EN;
+ cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
+ cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
+ cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
break;
case TYPE_S3C2440:
case TYPE_S3C2412:
+ cfg |= S3C2440_NFCONF_TACLS(tacls - 1);
+ cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
+ cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
+
/* enable the controller and de-assert nFCE */
writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
}
+ dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
+
+ writel(cfg, info->regs + S3C2410_NFCONF);
return 0;
}
@@ -310,7 +311,7 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
/* s3c2410_nand_hwcontrol
*
* Issue command and address cycles to the chip
-*/
+ */
static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
@@ -345,7 +346,7 @@ static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,
/* s3c2410_nand_devready()
*
* returns 0 if the nand is busy, 1 if it is ready
-*/
+ */
static int s3c2410_nand_devready(struct mtd_info *mtd)
{
@@ -371,81 +372,80 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned int diff0, diff1, diff2;
- unsigned int bit, byte;
-
- pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc);
-
- diff0 = read_ecc[0] ^ calc_ecc[0];
- diff1 = read_ecc[1] ^ calc_ecc[1];
- diff2 = read_ecc[2] ^ calc_ecc[2];
-
- pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n",
- __func__,
- read_ecc[0], read_ecc[1], read_ecc[2],
- calc_ecc[0], calc_ecc[1], calc_ecc[2],
- diff0, diff1, diff2);
-
- if (diff0 == 0 && diff1 == 0 && diff2 == 0)
- return 0; /* ECC is ok */
-
+ unsigned long nfmeccdata0, nfmeccdata1;
+ unsigned long nfestat0;
+ unsigned char err_type;
+ int retval;
+ unsigned int offset;
+ unsigned char wrong_value, corr_value;
+ struct s3c2410_nand_mtd *nmtd = s3c2410_nand_mtd_toours(mtd);
+ struct nand_chip *chip = &nmtd->chip;
+
+ pr_debug("%s: read %02x%02x%02x%02x calc %02x%02x%02x%02x\n", __func__,
+ read_ecc[0], read_ecc[1], read_ecc[2], read_ecc[3],
+ calc_ecc[0], calc_ecc[1], calc_ecc[2], calc_ecc[3]);
+
/* sometimes people do not think about using the ECC, so check
* to see if we have an 0xff,0xff,0xff read ECC and then ignore
* the error, on the assumption that this is an un-eccd page.
*/
if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff
- && info->platform->ignore_unset_ecc)
+ && read_ecc[3] == 0xff && info->platform->ignore_unset_ecc) {
+ pr_debug("Ignoring the HWECC calculation?\n");
return 0;
-
- /* Can we correct this ECC (ie, one row and column change).
- * Note, this is similar to the 256 error code on smartmedia */
-
- if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 &&
- ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 &&
- ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {
- /* calculate the bit position of the error */
-
- bit = ((diff2 >> 3) & 1) |
- ((diff2 >> 4) & 2) |
- ((diff2 >> 5) & 4);
-
- /* calculate the byte position of the error */
-
- byte = ((diff2 << 7) & 0x100) |
- ((diff1 << 0) & 0x80) |
- ((diff1 << 1) & 0x40) |
- ((diff1 << 2) & 0x20) |
- ((diff1 << 3) & 0x10) |
- ((diff0 >> 4) & 0x08) |
- ((diff0 >> 3) & 0x04) |
- ((diff0 >> 2) & 0x02) |
- ((diff0 >> 1) & 0x01);
-
- dev_dbg(info->device, "correcting error bit %d, byte %d\n",
- bit, byte);
-
- dat[byte] ^= (1 << bit);
- return 1;
}
- /* if there is only one bit difference in the ECC, then
- * one of only a row or column parity has changed, which
- * means the error is most probably in the ECC itself */
-
- diff0 |= (diff1 << 8);
- diff0 |= (diff2 << 16);
-
- if ((diff0 & ~(1<<fls(diff0))) == 0)
- return 1;
-
- return -1;
+ /*
+ * If the chip has a small page then set the fourth byte to 0xff, so that
+ * we can use the NAND-controller to check if the ECC is correct or not.
+ * Please note that this "workaround" is coming from the U-Boot, which has
+ * an ECC-layout with only three ECC-bytes (but why?).
+ * (Luis Galdos)
+ */
+ if (chip->page_shift <= 10) {
+ pr_debug("Setting the fourth byte to 0xff\n");
+ read_ecc[3] = 0xff;
+ }
+
+ /*
+ * The below code is coming from the driver 's3c_nand.c' (by jsgood)
+ * Luis Galdos
+ */
+ nfmeccdata0 = (read_ecc[1] << 16) | read_ecc[0];
+ nfmeccdata1 = (read_ecc[3] << 16) | read_ecc[2];
+ writel(nfmeccdata0, info->regs + S3C2440_NFECCD0);
+ writel(nfmeccdata1, info->regs + S3C2440_NFECCD1);
+ nfestat0 = readl(info->regs + S3C2412_NFMECC_ERR0);
+ err_type = nfestat0 & 0x3;
+
+ switch (err_type) {
+ case 0:
+ retval = 0;
+ break;
+ case 1:
+ offset = (nfestat0 >> 7) & 0x7ff;
+ wrong_value = dat[offset];
+ corr_value = wrong_value ^ (1 << ((nfestat0 >> 4) & 0x7));
+ printk(KERN_DEBUG "[NAND BitErr]: 1 bit error detected at page byte %u. Correcting from 0x%02x to 0x%02x\n",
+ offset, wrong_value, corr_value);
+ dat[offset] = corr_value;
+ retval = 1;
+ break;
+ default:
+ printk(KERN_ERR "[NAND BitErr]: More than 1 bit error detected. Uncorrectable error.\n");
+ retval = -1;
+ break;
+ }
+
+ return retval;
}
+
/* ECC functions
*
* These allow the s3c2410 and s3c2440 to use the controller's ECC
* generator block to ECC the data as it passes through]
-*/
+ */
static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
@@ -462,8 +462,21 @@ static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode)
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
unsigned long ctrl;
+ /*
+ * According to the manual: unlock the main area and set the data transfer
+ * direction (read or write) too
+ * Luis Galdos
+ */
ctrl = readl(info->regs + S3C2440_NFCONT);
- writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT);
+ ctrl |= S3C2412_NFCONT_INIT_MAIN_ECC;
+ ctrl &= ~S3C2412_NFCONT_MAIN_ECC_LOCK;
+
+ if (mode == NAND_ECC_WRITE)
+ ctrl |= S3C2412_NFCONT_ECC4_DIRWR;
+ else
+ ctrl &= ~S3C2412_NFCONT_ECC4_DIRWR;
+
+ writel(ctrl, info->regs + S3C2440_NFCONT);
}
static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
@@ -492,13 +505,25 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
- unsigned long ecc = readl(info->regs + S3C2412_NFMECC0);
+ unsigned long ecc, nfcont;
+
+ /*
+ * According to the manual: lock the main area before reading from the ECC
+ * status register.
+ * Luis Galdos
+ */
+ nfcont = readl(info->regs + S3C2440_NFCONT);
+ nfcont |= S3C2412_NFCONT_MAIN_ECC_LOCK;
+ writel(nfcont, info->regs + S3C2440_NFCONT);
- ecc_code[0] = ecc;
- ecc_code[1] = ecc >> 8;
- ecc_code[2] = ecc >> 16;
+ ecc = readl(info->regs + S3C2412_NFMECC0);
+ ecc_code[0] = ecc & 0xff;
+ ecc_code[1] = (ecc >> 8) & 0xff;
+ ecc_code[2] = (ecc >> 16) & 0xff;
+ ecc_code[3] = (ecc >> 24) & 0xff;
- pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
+ pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x,%02x\n",
+ ecc_code[0], ecc_code[1], ecc_code[2], ecc_code[3]);
return 0;
}
@@ -519,7 +544,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
/* over-ride the standard functions for a little more speed. We can
* use read/write block to move the data buffers to/from the controller
-*/
+ */
static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
@@ -545,52 +570,6 @@ static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int
writesl(info->regs + S3C2440_NFDATA, buf, len / 4);
}
-/* cpufreq driver support */
-
-#ifdef CONFIG_CPU_FREQ
-
-static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb,
- unsigned long val, void *data)
-{
- struct s3c2410_nand_info *info;
- unsigned long newclk;
-
- info = container_of(nb, struct s3c2410_nand_info, freq_transition);
- newclk = clk_get_rate(info->clk);
-
- if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) ||
- (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) {
- s3c2410_nand_setrate(info);
- }
-
- return 0;
-}
-
-static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
-{
- info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition;
-
- return cpufreq_register_notifier(&info->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-
-static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
-{
- cpufreq_unregister_notifier(&info->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-
-#else
-static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info)
-{
- return 0;
-}
-
-static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info)
-{
-}
-#endif
-
/* device management functions */
static int s3c2410_nand_remove(struct platform_device *pdev)
@@ -602,10 +581,9 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
if (info == NULL)
return 0;
- s3c2410_nand_cpufreq_deregister(info);
-
- /* Release all our mtds and their partitions, then go through
- * freeing the resources used
+ /* first thing we need to do is release all our mtds
+ * and their partitions, then go through freeing the
+ * resources used
*/
if (info->mtds != NULL) {
@@ -670,7 +648,7 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
/* s3c2410_nand_init_chip
*
* init a single instance of an chip
-*/
+ */
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *nmtd,
@@ -752,11 +730,14 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
chip->ecc.mode = NAND_ECC_SOFT;
}
+ /* @FIXME: Here seems to be something wrong. (Luis Galdos) */
+#if 0
if (set->ecc_layout != NULL)
chip->ecc.layout = set->ecc_layout;
if (set->disable_ecc)
chip->ecc.mode = NAND_ECC_NONE;
+#endif
}
/* s3c2410_nand_update_chip
@@ -770,20 +751,26 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
{
struct nand_chip *chip = &nmtd->chip;
- dev_dbg(info->device, "chip %p => page shift %d\n",
- chip, chip->page_shift);
+ printk("%s: chip %p: %d\n", __func__, chip, chip->page_shift);
if (hardware_ecc) {
/* change the behaviour depending on wether we are using
* the large or small page nand device */
if (chip->page_shift > 10) {
- chip->ecc.size = 256;
- chip->ecc.bytes = 3;
+ chip->ecc.size = 2048;
+ chip->ecc.bytes = 4;
+ chip->ecc.layout = &nand_hw_eccoob;
} else {
+ /*
+ * IMPORTANT: For using only three ECC-bytes is required to set
+ * the fourth byte to '0xff', otherwise we can't use the internal
+ * NAND-controller for checking if the ECC is correct or not.
+ * (Luis Galdos)
+ */
chip->ecc.size = 512;
chip->ecc.bytes = 3;
- chip->ecc.layout = &nand_hw_eccoob;
+ chip->ecc.layout = &nand_hw_eccoob_16;
}
}
}
@@ -794,7 +781,7 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
* one our driver can handled. This code checks to see if
* it can allocate all necessary resources then calls the
* nand layer to look for devices
-*/
+ */
static int s3c24xx_nand_probe(struct platform_device *pdev,
enum s3c_cpu_type cpu_type)
@@ -809,6 +796,18 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
int nr_sets;
int setno;
+ /*
+ * Required variables for accessing to the partitions from the command line
+ * Luis Galdos
+ */
+#if defined(CONFIG_MTD_CMDLINE_PARTS)
+ struct mtd_partition *mtd_parts;
+ int nr_parts;
+ const char *name_back;
+ struct s3c2410_nand_set nand_sets[1];
+ const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
pr_debug("s3c2410_nand_probe(%p)\n", pdev);
info = kmalloc(sizeof(*info), GFP_KERNEL);
@@ -864,7 +863,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
/* initialise the hardware */
- err = s3c2410_nand_inithw(info);
+ err = s3c2410_nand_inithw(info, pdev);
if (err != 0)
goto exit_error;
@@ -891,12 +890,33 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
for (setno = 0; setno < nr_sets; setno++, nmtd++) {
pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
-
s3c2410_nand_init_chip(info, nmtd, sets);
-
nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
(sets) ? sets->nr_chips : 1);
+ /*
+ * This is the code required for accessing to the partitions table
+ * passed by the bootloader
+ * (Luis Galdos)
+ */
+#if defined(CONFIG_MTD_CMDLINE_PARTS)
+ /*
+ * Need to reset the name of the MTD-device then the name of the
+ * partition passed by the U-Boot differs from the assigned name.
+ * U-Boot : onboard_boot
+ * Device : NAND 128MiB 3,3V 8-bit
+ */
+ name_back = nmtd->mtd.name;
+ nmtd->mtd.name= PARTITON_PARAMETER_NAME;
+ nr_parts = parse_mtd_partitions(&nmtd->mtd, part_probes, &mtd_parts, 0);
+ nmtd->mtd.name = name_back;
+ nand_sets[0].name = "NAND-Parts";
+ nand_sets[0].nr_chips = 1;
+ nand_sets[0].partitions = mtd_parts;
+ nand_sets[0].nr_partitions = nr_parts;
+ sets = nand_sets;
+#endif /* defined(CONFIG_MTD_CMDLINE_PARTS) */
+
if (nmtd->scan_res == 0) {
s3c2410_nand_update_chip(info, nmtd);
nand_scan_tail(&nmtd->mtd);
@@ -907,12 +927,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev,
sets++;
}
- err = s3c2410_nand_cpufreq_register(info);
- if (err < 0) {
- dev_err(&pdev->dev, "failed to init cpufreq support\n");
- goto exit_error;
- }
-
if (allow_clk_stop(info)) {
dev_info(&pdev->dev, "clock idle support enabled\n");
clk_disable(info->clk);
@@ -960,7 +974,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
if (info) {
clk_enable(info->clk);
- s3c2410_nand_inithw(info);
+ s3c2410_nand_inithw(info, dev);
/* Restore the state of the nFCE line. */
@@ -1035,9 +1049,16 @@ static int __init s3c2410_nand_init(void)
{
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
- platform_driver_register(&s3c2412_nand_driver);
- platform_driver_register(&s3c2440_nand_driver);
- return platform_driver_register(&s3c2410_nand_driver);
+ if (platform_driver_register(&s3c2440_nand_driver))
+ printk(KERN_ERR "Registering S3C2440 NAND driver\n");
+
+ if (platform_driver_register(&s3c2412_nand_driver))
+ printk(KERN_ERR "Registering S3C2412 NAND driver\n");
+
+ if (platform_driver_register(&s3c2410_nand_driver))
+ printk(KERN_ERR "Registering S3C2410 NAND driver\n");
+
+ return 0;
}
static void __exit s3c2410_nand_exit(void)
diff --git a/drivers/mxc/Kconfig b/drivers/mxc/Kconfig
new file mode 100644
index 000000000000..72225bbe91de
--- /dev/null
+++ b/drivers/mxc/Kconfig
@@ -0,0 +1,39 @@
+# drivers/video/mxc/Kconfig
+
+if ARCH_MXC
+
+menu "MXC support drivers"
+
+config MXC_IPU
+ bool "Image Processing Unit Driver"
+ depends on !ARCH_MX21
+ depends on !ARCH_MX27
+ depends on !ARCH_MX25
+ select MXC_IPU_V1 if !ARCH_MX37 && !ARCH_MX51
+ select MXC_IPU_V3 if ARCH_MX37 || ARCH_MX51
+ select MXC_IPU_V3D if ARCH_MX37
+ select MXC_IPU_V3EX if ARCH_MX51
+ help
+ If you plan to use the Image Processing unit, say
+ Y here. IPU is needed by Framebuffer and V4L2 drivers.
+
+source "drivers/mxc/ipu/Kconfig"
+source "drivers/mxc/ipu3/Kconfig"
+
+source "drivers/mxc/ssi/Kconfig"
+source "drivers/mxc/dam/Kconfig"
+source "drivers/mxc/pmic/Kconfig"
+source "drivers/mxc/mcu_pmic/Kconfig"
+source "drivers/mxc/security/Kconfig"
+source "drivers/mxc/hmp4e/Kconfig"
+source "drivers/mxc/hw_event/Kconfig"
+source "drivers/mxc/vpu/Kconfig"
+source "drivers/mxc/asrc/Kconfig"
+source "drivers/mxc/bt/Kconfig"
+source "drivers/mxc/gps_ioctrl/Kconfig"
+source "drivers/mxc/mlb/Kconfig"
+source "drivers/mxc/adc/Kconfig"
+
+endmenu
+
+endif
diff --git a/drivers/mxc/Makefile b/drivers/mxc/Makefile
new file mode 100644
index 000000000000..6416bc429888
--- /dev/null
+++ b/drivers/mxc/Makefile
@@ -0,0 +1,17 @@
+obj-$(CONFIG_MXC_IPU_V1) += ipu/
+obj-$(CONFIG_MXC_IPU_V3) += ipu3/
+obj-$(CONFIG_MXC_SSI) += ssi/
+obj-$(CONFIG_MXC_DAM) += dam/
+
+obj-$(CONFIG_MXC_PMIC_MC9SDZ60) += mcu_pmic/
+obj-$(CONFIG_MXC_PMIC) += pmic/
+
+obj-$(CONFIG_MXC_HMP4E) += hmp4e/
+obj-y += security/
+obj-$(CONFIG_MXC_VPU) += vpu/
+obj-$(CONFIG_MXC_HWEVENT) += hw_event/
+obj-$(CONFIG_MXC_ASRC) += asrc/
+obj-$(CONFIG_MXC_BLUETOOTH) += bt/
+obj-$(CONFIG_GPS_IOCTRL) += gps_ioctrl/
+obj-$(CONFIG_MXC_MLB) += mlb/
+obj-$(CONFIG_IMX_ADC) += adc/
diff --git a/drivers/mxc/adc/Kconfig b/drivers/mxc/adc/Kconfig
new file mode 100644
index 000000000000..91ad23bc7cbe
--- /dev/null
+++ b/drivers/mxc/adc/Kconfig
@@ -0,0 +1,14 @@
+#
+# i.MX ADC devices
+#
+
+menu "i.MX ADC support"
+
+config IMX_ADC
+ tristate "i.MX ADC"
+ depends on ARCH_MXC
+ default n
+ help
+ This selects the Freescale i.MX on-chip ADC driver.
+
+endmenu
diff --git a/drivers/mxc/adc/Makefile b/drivers/mxc/adc/Makefile
new file mode 100644
index 000000000000..e21e48ee1185
--- /dev/null
+++ b/drivers/mxc/adc/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for i.MX adc devices.
+#
+obj-$(CONFIG_IMX_ADC) += imx_adc.o
diff --git a/drivers/mxc/adc/imx_adc.c b/drivers/mxc/adc/imx_adc.c
new file mode 100644
index 000000000000..4f885a32a14b
--- /dev/null
+++ b/drivers/mxc/adc/imx_adc.c
@@ -0,0 +1,1047 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file adc/imx_adc.c
+ * @brief This is the main file of i.MX ADC driver.
+ *
+ * @ingroup IMX_ADC
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/imx_adc.h>
+#include "imx_adc_reg.h"
+
+static int imx_adc_major;
+
+/*!
+ * Number of users waiting in suspendq
+ */
+static int swait;
+
+/*!
+ * To indicate whether any of the adc devices are suspending
+ */
+static int suspend_flag;
+
+/*!
+ * The suspendq is used by blocking application calls
+ */
+static wait_queue_head_t suspendq;
+static wait_queue_head_t tsq;
+
+static bool imx_adc_ready;
+static bool ts_data_ready;
+static int tsi_data = TSI_DATA;
+static unsigned short ts_data_buf[16];
+
+static struct class *imx_adc_class;
+static struct imx_adc_data *adc_data;
+
+static DECLARE_MUTEX(general_convert_mutex);
+static DECLARE_MUTEX(ts_convert_mutex);
+
+unsigned long tsc_base;
+
+int is_imx_adc_ready(void)
+{
+ return imx_adc_ready;
+}
+EXPORT_SYMBOL(is_imx_adc_ready);
+
+void tsc_clk_enable(void)
+{
+ unsigned long reg;
+
+ clk_enable(adc_data->adc_clk);
+
+ reg = __raw_readl(tsc_base + TGCR);
+ reg |= TGCR_IPG_CLK_EN;
+ __raw_writel(reg, tsc_base + TGCR);
+}
+
+void tsc_clk_disable(void)
+{
+ unsigned long reg;
+
+ clk_disable(adc_data->adc_clk);
+
+ reg = __raw_readl(tsc_base + TGCR);
+ reg &= ~TGCR_IPG_CLK_EN;
+ __raw_writel(reg, tsc_base + TGCR);
+}
+
+void tsc_self_reset(void)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(tsc_base + TGCR);
+ reg |= TGCR_TSC_RST;
+ __raw_writel(reg, tsc_base + TGCR);
+
+ while (__raw_readl(tsc_base + TGCR) & TGCR_TSC_RST)
+ continue;
+}
+
+/* Internal reference */
+void tsc_intref_enable(void)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(tsc_base + TGCR);
+ reg |= TGCR_INTREFEN;
+ __raw_writel(reg, tsc_base + TGCR);
+}
+
+/* initialize touchscreen */
+void imx_tsc_init(void)
+{
+ unsigned long reg;
+ int lastitemid;
+ int dbtime;
+
+ /* Level sense */
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg |= CQCR_PD_CFG;
+ reg |= (0xf << CQCR_FIFOWATERMARK_SHIFT); /* watermark */
+ __raw_writel(reg, tsc_base + TCQCR);
+
+ /* Configure 4-wire */
+ reg = TSC_4WIRE_PRECHARGE;
+ reg |= CC_IGS;
+ __raw_writel(reg, tsc_base + TCC0);
+
+ reg = TSC_4WIRE_TOUCH_DETECT;
+ reg |= 3 << CC_NOS_SHIFT; /* 4 samples */
+ reg |= 32 << CC_SETTLING_TIME_SHIFT; /* it's important! */
+ __raw_writel(reg, tsc_base + TCC1);
+
+ reg = TSC_4WIRE_X_MEASUMENT;
+ reg |= 3 << CC_NOS_SHIFT; /* 4 samples */
+ reg |= 16 << CC_SETTLING_TIME_SHIFT; /* settling time */
+ __raw_writel(reg, tsc_base + TCC2);
+
+ reg = TSC_4WIRE_Y_MEASUMENT;
+ reg |= 3 << CC_NOS_SHIFT; /* 4 samples */
+ reg |= 16 << CC_SETTLING_TIME_SHIFT; /* settling time */
+ __raw_writel(reg, tsc_base + TCC3);
+
+ reg = (TCQ_ITEM_TCC0 << TCQ_ITEM7_SHIFT) |
+ (TCQ_ITEM_TCC0 << TCQ_ITEM6_SHIFT) |
+ (TCQ_ITEM_TCC1 << TCQ_ITEM5_SHIFT) |
+ (TCQ_ITEM_TCC0 << TCQ_ITEM4_SHIFT) |
+ (TCQ_ITEM_TCC3 << TCQ_ITEM3_SHIFT) |
+ (TCQ_ITEM_TCC2 << TCQ_ITEM2_SHIFT) |
+ (TCQ_ITEM_TCC1 << TCQ_ITEM1_SHIFT) |
+ (TCQ_ITEM_TCC0 << TCQ_ITEM0_SHIFT);
+ __raw_writel(reg, tsc_base + TCQ_ITEM_7_0);
+
+ lastitemid = 5;
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg = (reg & ~CQCR_LAST_ITEM_ID_MASK) |
+ (lastitemid << CQCR_LAST_ITEM_ID_SHIFT);
+ __raw_writel(reg, tsc_base + TCQCR);
+
+ /* pen down enable */
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg &= ~CQCR_PD_MSK;
+ __raw_writel(reg, tsc_base + TCQCR);
+ reg = __raw_readl(tsc_base + TCQMR);
+ reg &= ~TCQMR_PD_IRQ_MSK;
+ __raw_writel(reg, tsc_base + TCQMR);
+
+ /* Debounce time = dbtime*8 adc clock cycles */
+ reg = __raw_readl(tsc_base + TGCR);
+ dbtime = 3;
+ reg &= ~TGCR_PDBTIME_MASK;
+ reg |= dbtime << TGCR_PDBTIME_SHIFT;
+ reg |= TGCR_SLPC;
+ __raw_writel(reg, tsc_base + TGCR);
+
+}
+
+static irqreturn_t imx_adc_interrupt(int irq, void *dev_id)
+{
+ int reg;
+
+ /* disable pen down detect */
+ reg = __raw_readl(tsc_base + TGCR);
+ reg &= ~TGCR_PD_EN;
+ __raw_writel(reg, tsc_base + TGCR);
+
+ ts_data_ready = 1;
+ wake_up_interruptible(&tsq);
+ return IRQ_HANDLED;
+}
+
+enum IMX_ADC_STATUS imx_adc_read_general(unsigned short *result)
+{
+ unsigned long reg;
+ unsigned int data_num = 0;
+
+ reg = __raw_readl(tsc_base + GCQCR);
+ reg |= CQCR_FQS;
+ __raw_writel(reg, tsc_base + GCQCR);
+
+ while (!(__raw_readl(tsc_base + GCQSR) & CQSR_EOQ))
+ continue;
+ reg = __raw_readl(tsc_base + GCQCR);
+ reg &= ~CQCR_FQS;
+ __raw_writel(reg, tsc_base + GCQCR);
+ reg = __raw_readl(tsc_base + GCQSR);
+ reg |= CQSR_EOQ;
+ __raw_writel(reg, tsc_base + GCQSR);
+
+ while (!(__raw_readl(tsc_base + GCQSR) & CQSR_EMPT)) {
+ result[data_num] = __raw_readl(tsc_base + GCQFIFO) >>
+ GCQFIFO_ADCOUT_SHIFT;
+ data_num++;
+ }
+ return IMX_ADC_SUCCESS;
+}
+
+/*!
+ * This function will get raw (X,Y) value by converting the voltage
+ * @param touch_sample Pointer to touch sample
+ *
+ * return This funciton returns 0 if successful.
+ *
+ *
+ */
+enum IMX_ADC_STATUS imx_adc_read_ts(struct t_touch_screen *touch_sample,
+ int wait_tsi)
+{
+ int reg;
+ int data_num = 0;
+ int detect_sample1, detect_sample2;
+
+ memset(ts_data_buf, 0, sizeof ts_data_buf);
+ touch_sample->valid_flag = 1;
+
+ if (wait_tsi) {
+ /* Config idle for 4-wire */
+ reg = TSC_4WIRE_TOUCH_DETECT;
+ __raw_writel(reg, tsc_base + TICR);
+
+ /* Pen interrupt starts new conversion queue */
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg &= ~CQCR_QSM_MASK;
+ reg |= CQCR_QSM_PEN;
+ __raw_writel(reg, tsc_base + TCQCR);
+
+ /* PDEN and PDBEN */
+ reg = __raw_readl(tsc_base + TGCR);
+ reg |= (TGCR_PDB_EN | TGCR_PD_EN);
+ __raw_writel(reg, tsc_base + TGCR);
+
+ wait_event_interruptible(tsq, ts_data_ready);
+ while (!(__raw_readl(tsc_base + TCQSR) & CQSR_EOQ))
+ continue;
+ /* stop the conversion */
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg &= ~CQCR_QSM_MASK;
+ __raw_writel(reg, tsc_base + TCQCR);
+ reg = CQSR_PD | CQSR_EOQ;
+ __raw_writel(reg, tsc_base + TCQSR);
+
+ /* change configuration for FQS mode */
+ tsi_data = TSI_DATA;
+ reg = (0x1 << CC_YPLLSW_SHIFT) | (0x1 << CC_XNURSW_SHIFT) |
+ CC_XPULSW;
+ __raw_writel(reg, tsc_base + TICR);
+ } else {
+ /* FQS semaphore */
+ down(&ts_convert_mutex);
+
+ reg = (0x1 << CC_YPLLSW_SHIFT) | (0x1 << CC_XNURSW_SHIFT) |
+ CC_XPULSW;
+ __raw_writel(reg, tsc_base + TICR);
+
+ /* FQS */
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg &= ~CQCR_QSM_MASK;
+ reg |= CQCR_QSM_FQS;
+ __raw_writel(reg, tsc_base + TCQCR);
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg |= CQCR_FQS;
+ __raw_writel(reg, tsc_base + TCQCR);
+ while (!(__raw_readl(tsc_base + TCQSR) & CQSR_EOQ))
+ continue;
+
+ /* stop FQS */
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg &= ~CQCR_QSM_MASK;
+ __raw_writel(reg, tsc_base + TCQCR);
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg &= ~CQCR_FQS;
+ __raw_writel(reg, tsc_base + TCQCR);
+
+ /* clear status bit */
+ reg = __raw_readl(tsc_base + TCQSR);
+ reg |= CQSR_EOQ;
+ __raw_writel(reg, tsc_base + TCQSR);
+ tsi_data = FQS_DATA;
+ }
+
+ while (!(__raw_readl(tsc_base + TCQSR) & CQSR_EMPT)) {
+ reg = __raw_readl(tsc_base + TCQFIFO);
+ ts_data_buf[data_num] = reg;
+ data_num++;
+ }
+
+ touch_sample->x_position1 = ts_data_buf[4] >> 4;
+ touch_sample->x_position2 = ts_data_buf[5] >> 4;
+ touch_sample->x_position3 = ts_data_buf[6] >> 4;
+ touch_sample->y_position1 = ts_data_buf[9] >> 4;
+ touch_sample->y_position2 = ts_data_buf[10] >> 4;
+ touch_sample->y_position3 = ts_data_buf[11] >> 4;
+
+ detect_sample1 = ts_data_buf[0];
+ detect_sample2 = ts_data_buf[12];
+
+ if ((detect_sample1 > 0x6000) || (detect_sample2 > 0x6000))
+ touch_sample->valid_flag = 0;
+
+ ts_data_ready = 0;
+
+ if (!(touch_sample->x_position1 ||
+ touch_sample->x_position2 || touch_sample->x_position3))
+ touch_sample->contact_resistance = 0;
+ else
+ touch_sample->contact_resistance = 1;
+
+ if (tsi_data == FQS_DATA)
+ up(&ts_convert_mutex);
+ return IMX_ADC_SUCCESS;
+}
+
+/*!
+ * This function performs filtering and rejection of excessive noise prone
+ * sampl.
+ *
+ * @param ts_curr Touch screen value
+ *
+ * @return This function returns 0 on success, -1 otherwise.
+ */
+static int imx_adc_filter(struct t_touch_screen *ts_curr)
+{
+
+ unsigned int ydiff1, ydiff2, ydiff3, xdiff1, xdiff2, xdiff3;
+ unsigned int sample_sumx, sample_sumy;
+ static unsigned int prev_x[FILTLEN], prev_y[FILTLEN];
+ int index = 0;
+ unsigned int y_curr, x_curr;
+ static int filt_count;
+ /* Added a variable filt_type to decide filtering at run-time */
+ unsigned int filt_type = 0;
+
+ /* ignore the data converted when pen down and up */
+ if ((ts_curr->contact_resistance == 0) || tsi_data == TSI_DATA) {
+ ts_curr->x_position = 0;
+ ts_curr->y_position = 0;
+ filt_count = 0;
+ return 0;
+ }
+ /* ignore the data valid */
+ if (ts_curr->valid_flag == 0)
+ return -1;
+
+ ydiff1 = abs(ts_curr->y_position1 - ts_curr->y_position2);
+ ydiff2 = abs(ts_curr->y_position2 - ts_curr->y_position3);
+ ydiff3 = abs(ts_curr->y_position1 - ts_curr->y_position3);
+ if ((ydiff1 > DELTA_Y_MAX) ||
+ (ydiff2 > DELTA_Y_MAX) || (ydiff3 > DELTA_Y_MAX)) {
+ pr_debug("imx_adc_filter: Ret pos 1\n");
+ return -1;
+ }
+
+ xdiff1 = abs(ts_curr->x_position1 - ts_curr->x_position2);
+ xdiff2 = abs(ts_curr->x_position2 - ts_curr->x_position3);
+ xdiff3 = abs(ts_curr->x_position1 - ts_curr->x_position3);
+
+ if ((xdiff1 > DELTA_X_MAX) ||
+ (xdiff2 > DELTA_X_MAX) || (xdiff3 > DELTA_X_MAX)) {
+ pr_debug("imx_adc_filter: Ret pos 2\n");
+ return -1;
+ }
+ /* Compute two closer values among the three available Y readouts */
+
+ if (ydiff1 < ydiff2) {
+ if (ydiff1 < ydiff3) {
+ /* Sample 0 & 1 closest together */
+ sample_sumy = ts_curr->y_position1 +
+ ts_curr->y_position2;
+ } else {
+ /* Sample 0 & 2 closest together */
+ sample_sumy = ts_curr->y_position1 +
+ ts_curr->y_position3;
+ }
+ } else {
+ if (ydiff2 < ydiff3) {
+ /* Sample 1 & 2 closest together */
+ sample_sumy = ts_curr->y_position2 +
+ ts_curr->y_position3;
+ } else {
+ /* Sample 0 & 2 closest together */
+ sample_sumy = ts_curr->y_position1 +
+ ts_curr->y_position3;
+ }
+ }
+
+ /*
+ * Compute two closer values among the three available X
+ * readouts
+ */
+ if (xdiff1 < xdiff2) {
+ if (xdiff1 < xdiff3) {
+ /* Sample 0 & 1 closest together */
+ sample_sumx = ts_curr->x_position1 +
+ ts_curr->x_position2;
+ } else {
+ /* Sample 0 & 2 closest together */
+ sample_sumx = ts_curr->x_position1 +
+ ts_curr->x_position3;
+ }
+ } else {
+ if (xdiff2 < xdiff3) {
+ /* Sample 1 & 2 closest together */
+ sample_sumx = ts_curr->x_position2 +
+ ts_curr->x_position3;
+ } else {
+ /* Sample 0 & 2 closest together */
+ sample_sumx = ts_curr->x_position1 +
+ ts_curr->x_position3;
+ }
+ }
+
+ /*
+ * Wait FILTER_MIN_DELAY number of samples to restart
+ * filtering
+ */
+ if (filt_count < FILTER_MIN_DELAY) {
+ /*
+ * Current output is the average of the two closer
+ * values and no filtering is used
+ */
+ y_curr = (sample_sumy / 2);
+ x_curr = (sample_sumx / 2);
+ ts_curr->y_position = y_curr;
+ ts_curr->x_position = x_curr;
+ filt_count++;
+
+ } else {
+ if (abs(sample_sumx - (prev_x[0] + prev_x[1])) >
+ (DELTA_X_MAX * 16)) {
+ pr_debug("imx_adc_filter: : Ret pos 3\n");
+ return -1;
+ }
+ if (abs(sample_sumy - (prev_y[0] + prev_y[1])) >
+ (DELTA_Y_MAX * 16)) {
+ pr_debug("imx_adc_filter: : Ret pos 4\n");
+ return -1;
+ }
+ sample_sumy /= 2;
+ sample_sumx /= 2;
+ /* Use hard filtering if the sample difference < 10 */
+ if ((abs(sample_sumy - prev_y[0]) > 10) ||
+ (abs(sample_sumx - prev_x[0]) > 10))
+ filt_type = 1;
+
+ /*
+ * Current outputs are the average of three previous
+ * values and the present readout
+ */
+ y_curr = sample_sumy;
+ for (index = 0; index < FILTLEN; index++) {
+ if (filt_type == 0)
+ y_curr = y_curr + (prev_y[index]);
+ else
+ y_curr = y_curr + (prev_y[index] / 3);
+ }
+ if (filt_type == 0)
+ y_curr = y_curr >> 2;
+ else
+ y_curr = y_curr >> 1;
+ ts_curr->y_position = y_curr;
+
+ x_curr = sample_sumx;
+ for (index = 0; index < FILTLEN; index++) {
+ if (filt_type == 0)
+ x_curr = x_curr + (prev_x[index]);
+ else
+ x_curr = x_curr + (prev_x[index] / 3);
+ }
+ if (filt_type == 0)
+ x_curr = x_curr >> 2;
+ else
+ x_curr = x_curr >> 1;
+ ts_curr->x_position = x_curr;
+
+ }
+
+ /* Update previous X and Y values */
+ for (index = (FILTLEN - 1); index > 0; index--) {
+ prev_x[index] = prev_x[index - 1];
+ prev_y[index] = prev_y[index - 1];
+ }
+
+ /*
+ * Current output will be the most recent past for the
+ * next sample
+ */
+ prev_y[0] = y_curr;
+ prev_x[0] = x_curr;
+
+ return 0;
+
+}
+
+/*!
+ * This function retrieves the current touch screen (X,Y) coordinates.
+ *
+ * @param touch_sample Pointer to touch sample.
+ *
+ * @return This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_get_touch_sample(struct t_touch_screen
+ *touch_sample, int wait_tsi)
+{
+ if (imx_adc_read_ts(touch_sample, wait_tsi))
+ return IMX_ADC_ERROR;
+ if (!imx_adc_filter(touch_sample))
+ return IMX_ADC_SUCCESS;
+ else
+ return IMX_ADC_ERROR;
+}
+EXPORT_SYMBOL(imx_adc_get_touch_sample);
+
+/*!
+ * This is the suspend of power management for the i.MX ADC API.
+ * It supports SAVE and POWER_DOWN state.
+ *
+ * @param pdev the device
+ * @param state the state
+ *
+ * @return This function returns 0 if successful.
+ */
+static int imx_adc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (device_may_wakeup(&pdev->dev)) {
+ enable_irq_wake(adc_data->irq);
+ } else {
+ suspend_flag = 1;
+ tsc_clk_disable();
+ }
+ return 0;
+};
+
+/*!
+ * This is the resume of power management for the i.MX adc API.
+ * It supports RESTORE state.
+ *
+ * @param pdev the device
+ *
+ * @return This function returns 0 if successful.
+ */
+static int imx_adc_resume(struct platform_device *pdev)
+{
+ if (device_may_wakeup(&pdev->dev)) {
+ disable_irq_wake(adc_data->irq);
+ } else {
+ suspend_flag = 0;
+ tsc_clk_enable();
+ while (swait > 0) {
+ swait--;
+ wake_up_interruptible(&suspendq);
+ }
+ }
+ return 0;
+}
+
+/*!
+ * This function implements the open method on an i.MX ADC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int imx_adc_open(struct inode *inode, struct file *file)
+{
+ while (suspend_flag) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, !suspend_flag))
+ return -ERESTARTSYS;
+ }
+ pr_debug("imx_adc : imx_adc_open()\n");
+ return 0;
+}
+
+/*!
+ * This function implements the release method on an i.MX ADC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int imx_adc_free(struct inode *inode, struct file *file)
+{
+ pr_debug("imx_adc : imx_adc_free()\n");
+ return 0;
+}
+
+/*!
+ * This function initializes all ADC registers with default values. This
+ * function also registers the interrupt events.
+ *
+ * @return This function returns IMX_ADC_SUCCESS if successful.
+ */
+int imx_adc_init(void)
+{
+ int reg;
+
+ pr_debug("imx_adc_init()\n");
+
+ if (suspend_flag)
+ return -EBUSY;
+
+ tsc_clk_enable();
+
+ /* Reset */
+ tsc_self_reset();
+
+ /* Internal reference */
+ tsc_intref_enable();
+
+ /* Set power mode */
+ reg = __raw_readl(tsc_base + TGCR) & ~TGCR_POWER_MASK;
+ reg |= TGCR_POWER_SAVE;
+ __raw_writel(reg, tsc_base + TGCR);
+
+ imx_tsc_init();
+
+ return IMX_ADC_SUCCESS;
+}
+EXPORT_SYMBOL(imx_adc_init);
+
+/*!
+ * This function disables the ADC, de-registers the interrupt events.
+ *
+ * @return This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_deinit(void)
+{
+ pr_debug("imx_adc_deinit()\n");
+
+ return IMX_ADC_SUCCESS;
+}
+EXPORT_SYMBOL(imx_adc_deinit);
+
+/*!
+ * This function triggers a conversion and returns one sampling result of one
+ * channel.
+ *
+ * @param channel The channel to be sampled
+ * @param result The pointer to the conversion result. The memory
+ * should be allocated by the caller of this function.
+ *
+ * @return This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_convert(enum t_channel channel,
+ unsigned short *result)
+{
+ int reg;
+ int lastitemid;
+ struct t_touch_screen touch_sample;
+
+ switch (channel) {
+
+ case TS_X_POS:
+ imx_adc_get_touch_sample(&touch_sample, 0);
+ result[0] = touch_sample.x_position;
+
+ /* if no pen down ,recover the register configuration */
+ if (touch_sample.contact_resistance == 0) {
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg &= ~CQCR_QSM_MASK;
+ reg |= CQCR_QSM_PEN;
+ __raw_writel(reg, tsc_base + TCQCR);
+ reg = __raw_readl(tsc_base + TGCR);
+ reg |= TGCR_PDB_EN | TGCR_PD_EN;
+ __raw_writel(reg, tsc_base + TGCR);
+ }
+ break;
+
+ case TS_Y_POS:
+ imx_adc_get_touch_sample(&touch_sample, 0);
+ result[1] = touch_sample.y_position;
+
+ /* if no pen down ,recover the register configuration */
+ if (touch_sample.contact_resistance == 0) {
+ reg = __raw_readl(tsc_base + TCQCR);
+ reg &= ~CQCR_QSM_MASK;
+ reg |= CQCR_QSM_PEN;
+ __raw_writel(reg, tsc_base + TCQCR);
+
+ reg = __raw_readl(tsc_base + TGCR);
+ reg |= TGCR_PDB_EN | TGCR_PD_EN;
+ __raw_writel(reg, tsc_base + TGCR);
+ }
+ break;
+
+ case GER_PURPOSE_ADC0:
+ down(&general_convert_mutex);
+
+ lastitemid = 0;
+ reg = (0xf << CQCR_FIFOWATERMARK_SHIFT) |
+ (lastitemid << CQCR_LAST_ITEM_ID_SHIFT) | CQCR_QSM_FQS;
+ __raw_writel(reg, tsc_base + GCQCR);
+
+ reg = TSC_GENERAL_ADC_GCC0;
+ reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+ __raw_writel(reg, tsc_base + GCC0);
+
+ imx_adc_read_general(result);
+ up(&general_convert_mutex);
+ break;
+
+ case GER_PURPOSE_ADC1:
+ down(&general_convert_mutex);
+
+ lastitemid = 0;
+ reg = (0xf << CQCR_FIFOWATERMARK_SHIFT) |
+ (lastitemid << CQCR_LAST_ITEM_ID_SHIFT) | CQCR_QSM_FQS;
+ __raw_writel(reg, tsc_base + GCQCR);
+
+ reg = TSC_GENERAL_ADC_GCC1;
+ reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+ __raw_writel(reg, tsc_base + GCC0);
+
+ imx_adc_read_general(result);
+ up(&general_convert_mutex);
+ break;
+
+ case GER_PURPOSE_ADC2:
+ down(&general_convert_mutex);
+
+ lastitemid = 0;
+ reg = (0xf << CQCR_FIFOWATERMARK_SHIFT) |
+ (lastitemid << CQCR_LAST_ITEM_ID_SHIFT) | CQCR_QSM_FQS;
+ __raw_writel(reg, tsc_base + GCQCR);
+
+ reg = TSC_GENERAL_ADC_GCC2;
+ reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+ __raw_writel(reg, tsc_base + GCC0);
+
+ imx_adc_read_general(result);
+ up(&general_convert_mutex);
+ break;
+
+ case GER_PURPOSE_MULTICHNNEL:
+ down(&general_convert_mutex);
+
+ reg = TSC_GENERAL_ADC_GCC0;
+ reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+ __raw_writel(reg, tsc_base + GCC0);
+
+ reg = TSC_GENERAL_ADC_GCC1;
+ reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+ __raw_writel(reg, tsc_base + GCC1);
+
+ reg = TSC_GENERAL_ADC_GCC2;
+ reg |= (3 << CC_NOS_SHIFT) | (16 << CC_SETTLING_TIME_SHIFT);
+ __raw_writel(reg, tsc_base + GCC2);
+
+ reg = (GCQ_ITEM_GCC2 << GCQ_ITEM2_SHIFT) |
+ (GCQ_ITEM_GCC1 << GCQ_ITEM1_SHIFT) |
+ (GCQ_ITEM_GCC0 << GCQ_ITEM0_SHIFT);
+ __raw_writel(reg, tsc_base + GCQ_ITEM_7_0);
+
+ lastitemid = 2;
+ reg = (0xf << CQCR_FIFOWATERMARK_SHIFT) |
+ (lastitemid << CQCR_LAST_ITEM_ID_SHIFT) | CQCR_QSM_FQS;
+ __raw_writel(reg, tsc_base + GCQCR);
+
+ imx_adc_read_general(result);
+ up(&general_convert_mutex);
+ break;
+ default:
+ pr_debug("%s: bad channel number\n", __func__);
+ return IMX_ADC_ERROR;
+ }
+
+ return IMX_ADC_SUCCESS;
+}
+EXPORT_SYMBOL(imx_adc_convert);
+
+/*!
+ * This function triggers a conversion and returns sampling results of each
+ * specified channel.
+ *
+ * @param channels This input parameter is bitmap to specify channels
+ * to be sampled.
+ * @param result The pointer to array to store sampling results.
+ * The memory should be allocated by the caller of this
+ * function.
+ *
+ * @return This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_convert_multichnnel(enum t_channel channels,
+ unsigned short *result)
+{
+ imx_adc_convert(GER_PURPOSE_MULTICHNNEL, result);
+ return IMX_ADC_SUCCESS;
+}
+EXPORT_SYMBOL(imx_adc_convert_multichnnel);
+
+/*!
+ * This function implements IOCTL controls on an i.MX ADC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @param cmd the command
+ * @param arg the parameter
+ * @return This function returns 0 if successful.
+ */
+static int imx_adc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct t_adc_convert_param *convert_param;
+
+ if ((_IOC_TYPE(cmd) != 'p') && (_IOC_TYPE(cmd) != 'D'))
+ return -ENOTTY;
+
+ while (suspend_flag) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, !suspend_flag))
+ return -ERESTARTSYS;
+ }
+
+ switch (cmd) {
+ case IMX_ADC_INIT:
+ pr_debug("init adc\n");
+ CHECK_ERROR(imx_adc_init());
+ break;
+
+ case IMX_ADC_DEINIT:
+ pr_debug("deinit adc\n");
+ CHECK_ERROR(imx_adc_deinit());
+ break;
+
+ case IMX_ADC_CONVERT:
+ convert_param = kmalloc(sizeof(*convert_param), GFP_KERNEL);
+ if (convert_param == NULL)
+ return -ENOMEM;
+ if (copy_from_user(convert_param,
+ (struct t_adc_convert_param *)arg,
+ sizeof(*convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+ CHECK_ERROR_KFREE(imx_adc_convert(convert_param->channel,
+ convert_param->result),
+ (kfree(convert_param)));
+
+ if (copy_to_user((struct t_adc_convert_param *)arg,
+ convert_param, sizeof(*convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+ kfree(convert_param);
+ break;
+
+ case IMX_ADC_CONVERT_MULTICHANNEL:
+ convert_param = kmalloc(sizeof(*convert_param), GFP_KERNEL);
+ if (convert_param == NULL)
+ return -ENOMEM;
+ if (copy_from_user(convert_param,
+ (struct t_adc_convert_param *)arg,
+ sizeof(*convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+ CHECK_ERROR_KFREE(imx_adc_convert_multichnnel
+ (convert_param->channel,
+ convert_param->result),
+ (kfree(convert_param)));
+
+ if (copy_to_user((struct t_adc_convert_param *)arg,
+ convert_param, sizeof(*convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+ kfree(convert_param);
+ break;
+
+ default:
+ pr_debug("imx_adc_ioctl: unsupported ioctl command 0x%x\n",
+ cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct file_operations imx_adc_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = imx_adc_ioctl,
+ .open = imx_adc_open,
+ .release = imx_adc_free,
+};
+
+static int imx_adc_module_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int retval;
+ struct device *temp_class;
+ struct resource *res;
+ void __iomem *base;
+
+ /* ioremap the base address */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No TSC base address provided\n");
+ goto err_out0;
+ }
+ base = ioremap(res->start, res->end - res->start);
+ if (base == NULL) {
+ dev_err(&pdev->dev, "failed to rebase TSC base address\n");
+ goto err_out0;
+ }
+ tsc_base = (unsigned long)base;
+
+ /* create the chrdev */
+ imx_adc_major = register_chrdev(0, "imx_adc", &imx_adc_fops);
+
+ if (imx_adc_major < 0) {
+ dev_err(&pdev->dev, "Unable to get a major for imx_adc\n");
+ return imx_adc_major;
+ }
+ init_waitqueue_head(&suspendq);
+ init_waitqueue_head(&tsq);
+
+ imx_adc_class = class_create(THIS_MODULE, "imx_adc");
+ if (IS_ERR(imx_adc_class)) {
+ dev_err(&pdev->dev, "Error creating imx_adc class.\n");
+ ret = PTR_ERR(imx_adc_class);
+ goto err_out1;
+ }
+
+ temp_class = device_create(imx_adc_class, NULL,
+ MKDEV(imx_adc_major, 0), NULL, "imx_adc");
+ if (IS_ERR(temp_class)) {
+ dev_err(&pdev->dev, "Error creating imx_adc class device.\n");
+ ret = PTR_ERR(temp_class);
+ goto err_out2;
+ }
+
+ adc_data = kmalloc(sizeof(struct imx_adc_data), GFP_KERNEL);
+ if (adc_data == NULL)
+ return -ENOMEM;
+ adc_data->irq = platform_get_irq(pdev, 0);
+ retval = request_irq(adc_data->irq, imx_adc_interrupt,
+ 0, MOD_NAME, MOD_NAME);
+ if (retval) {
+ dev_dbg(&pdev->dev, "ADC: request_irq(%d) returned error %d\n",
+ MXC_INT_TSC, retval);
+ return retval;
+ }
+ adc_data->adc_clk = clk_get(&pdev->dev, "tchscrn_clk");
+
+ ret = imx_adc_init();
+
+ if (ret != IMX_ADC_SUCCESS) {
+ dev_err(&pdev->dev, "Error in imx_adc_init.\n");
+ goto err_out4;
+ }
+ imx_adc_ready = 1;
+
+ /* By default, devices should wakeup if they can */
+ /* So TouchScreen is set as "should wakeup" as it can */
+ device_init_wakeup(&pdev->dev, 1);
+
+ pr_info("i.MX ADC at 0x%x irq %d\n", (unsigned int)res->start,
+ adc_data->irq);
+ return ret;
+
+err_out4:
+ device_destroy(imx_adc_class, MKDEV(imx_adc_major, 0));
+err_out2:
+ class_destroy(imx_adc_class);
+err_out1:
+ unregister_chrdev(imx_adc_major, "imx_adc");
+err_out0:
+ return ret;
+}
+
+static int imx_adc_module_remove(struct platform_device *pdev)
+{
+ imx_adc_ready = 0;
+ imx_adc_deinit();
+ device_destroy(imx_adc_class, MKDEV(imx_adc_major, 0));
+ class_destroy(imx_adc_class);
+ unregister_chrdev(imx_adc_major, "imx_adc");
+ free_irq(adc_data->irq, MOD_NAME);
+ kfree(adc_data);
+ pr_debug("i.MX ADC successfully removed\n");
+ return 0;
+}
+
+static struct platform_driver imx_adc_driver = {
+ .driver = {
+ .name = "imx_adc",
+ },
+ .suspend = imx_adc_suspend,
+ .resume = imx_adc_resume,
+ .probe = imx_adc_module_probe,
+ .remove = imx_adc_module_remove,
+};
+
+/*
+ * Initialization and Exit
+ */
+static int __init imx_adc_module_init(void)
+{
+ pr_debug("i.MX ADC driver loading...\n");
+ return platform_driver_register(&imx_adc_driver);
+}
+
+static void __exit imx_adc_module_exit(void)
+{
+ platform_driver_unregister(&imx_adc_driver);
+ pr_debug("i.MX ADC driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+module_init(imx_adc_module_init);
+module_exit(imx_adc_module_exit);
+
+MODULE_DESCRIPTION("i.MX ADC device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/adc/imx_adc_reg.h b/drivers/mxc/adc/imx_adc_reg.h
new file mode 100644
index 000000000000..8ae776038fbc
--- /dev/null
+++ b/drivers/mxc/adc/imx_adc_reg.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __IMX_ADC_H__
+#define __IMX_ADC_H__
+
+/* TSC General Config Register */
+#define TGCR 0x000
+#define TGCR_IPG_CLK_EN (1 << 0)
+#define TGCR_TSC_RST (1 << 1)
+#define TGCR_FUNC_RST (1 << 2)
+#define TGCR_SLPC (1 << 4)
+#define TGCR_STLC (1 << 5)
+#define TGCR_HSYNC_EN (1 << 6)
+#define TGCR_HSYNC_POL (1 << 7)
+#define TGCR_POWERMODE_SHIFT 8
+#define TGCR_POWER_OFF (0x0 << TGCR_POWERMODE_SHIFT)
+#define TGCR_POWER_SAVE (0x1 << TGCR_POWERMODE_SHIFT)
+#define TGCR_POWER_ON (0x3 << TGCR_POWERMODE_SHIFT)
+#define TGCR_POWER_MASK (0x3 << TGCR_POWERMODE_SHIFT)
+#define TGCR_INTREFEN (1 << 10)
+#define TGCR_ADCCLKCFG_SHIFT 16
+#define TGCR_PD_EN (1 << 23)
+#define TGCR_PDB_EN (1 << 24)
+#define TGCR_PDBTIME_SHIFT 25
+#define TGCR_PDBTIME128 (0x3f << TGCR_PDBTIME_SHIFT)
+#define TGCR_PDBTIME_MASK (0x7f << TGCR_PDBTIME_SHIFT)
+
+/* TSC General Status Register */
+#define TGSR 0x004
+#define TCQ_INT (1 << 0)
+#define GCQ_INT (1 << 1)
+#define SLP_INT (1 << 2)
+#define TCQ_DMA (1 << 16)
+#define GCQ_DMA (1 << 17)
+
+/* TSC IDLE Config Register */
+#define TICR 0x008
+
+/* TouchScreen Convert Queue FIFO Register */
+#define TCQFIFO 0x400
+/* TouchScreen Convert Queue Control Register */
+#define TCQCR 0x404
+#define CQCR_QSM_SHIFT 0
+#define CQCR_QSM_STOP (0x0 << CQCR_QSM_SHIFT)
+#define CQCR_QSM_PEN (0x1 << CQCR_QSM_SHIFT)
+#define CQCR_QSM_FQS (0x2 << CQCR_QSM_SHIFT)
+#define CQCR_QSM_FQS_PEN (0x3 << CQCR_QSM_SHIFT)
+#define CQCR_QSM_MASK (0x3 << CQCR_QSM_SHIFT)
+#define CQCR_FQS (1 << 2)
+#define CQCR_RPT (1 << 3)
+#define CQCR_LAST_ITEM_ID_SHIFT 4
+#define CQCR_LAST_ITEM_ID_MASK (0xf << CQCR_LAST_ITEM_ID_SHIFT)
+#define CQCR_FIFOWATERMARK_SHIFT 8
+#define CQCR_FIFOWATERMARK_MASK (0xf << CQCR_FIFOWATERMARK_SHIFT)
+#define CQCR_REPEATWAIT_SHIFT 12
+#define CQCR_REPEATWAIT_MASK (0xf << CQCR_REPEATWAIT_SHIFT)
+#define CQCR_QRST (1 << 16)
+#define CQCR_FRST (1 << 17)
+#define CQCR_PD_MSK (1 << 18)
+#define CQCR_PD_CFG (1 << 19)
+
+/* TouchScreen Convert Queue Status Register */
+#define TCQSR 0x408
+#define CQSR_PD (1 << 0)
+#define CQSR_EOQ (1 << 1)
+#define CQSR_FOR (1 << 4)
+#define CQSR_FUR (1 << 5)
+#define CQSR_FER (1 << 6)
+#define CQSR_EMPT (1 << 13)
+#define CQSR_FULL (1 << 14)
+#define CQSR_FDRY (1 << 15)
+
+/* TouchScreen Convert Queue Mask Register */
+#define TCQMR 0x40c
+#define TCQMR_PD_IRQ_MSK (1 << 0)
+#define TCQMR_EOQ_IRQ_MSK (1 << 1)
+#define TCQMR_FOR_IRQ_MSK (1 << 4)
+#define TCQMR_FUR_IRQ_MSK (1 << 5)
+#define TCQMR_FER_IRQ_MSK (1 << 6)
+#define TCQMR_PD_DMA_MSK (1 << 16)
+#define TCQMR_EOQ_DMA_MSK (1 << 17)
+#define TCQMR_FOR_DMA_MSK (1 << 20)
+#define TCQMR_FUR_DMA_MSK (1 << 21)
+#define TCQMR_FER_DMA_MSK (1 << 22)
+#define TCQMR_FDRY_DMA_MSK (1 << 31)
+
+/* TouchScreen Convert Queue ITEM 7~0 */
+#define TCQ_ITEM_7_0 0x420
+
+/* TouchScreen Convert Queue ITEM 15~8 */
+#define TCQ_ITEM_15_8 0x424
+
+#define TCQ_ITEM7_SHIFT 28
+#define TCQ_ITEM6_SHIFT 24
+#define TCQ_ITEM5_SHIFT 20
+#define TCQ_ITEM4_SHIFT 16
+#define TCQ_ITEM3_SHIFT 12
+#define TCQ_ITEM2_SHIFT 8
+#define TCQ_ITEM1_SHIFT 4
+#define TCQ_ITEM0_SHIFT 0
+
+#define TCQ_ITEM_TCC0 0x0
+#define TCQ_ITEM_TCC1 0x1
+#define TCQ_ITEM_TCC2 0x2
+#define TCQ_ITEM_TCC3 0x3
+#define TCQ_ITEM_TCC4 0x4
+#define TCQ_ITEM_TCC5 0x5
+#define TCQ_ITEM_TCC6 0x6
+#define TCQ_ITEM_TCC7 0x7
+#define TCQ_ITEM_GCC7 0x8
+#define TCQ_ITEM_GCC6 0x9
+#define TCQ_ITEM_GCC5 0xa
+#define TCQ_ITEM_GCC4 0xb
+#define TCQ_ITEM_GCC3 0xc
+#define TCQ_ITEM_GCC2 0xd
+#define TCQ_ITEM_GCC1 0xe
+#define TCQ_ITEM_GCC0 0xf
+
+/* TouchScreen Convert Config 0-7 */
+#define TCC0 0x440
+#define TCC1 0x444
+#define TCC2 0x448
+#define TCC3 0x44c
+#define TCC4 0x450
+#define TCC5 0x454
+#define TCC6 0x458
+#define TCC7 0x45c
+#define CC_PEN_IACK (1 << 1)
+#define CC_SEL_REFN_SHIFT 2
+#define CC_SEL_REFN_YNLR (0x1 << CC_SEL_REFN_SHIFT)
+#define CC_SEL_REFN_AGND (0x2 << CC_SEL_REFN_SHIFT)
+#define CC_SEL_REFN_MASK (0x3 << CC_SEL_REFN_SHIFT)
+#define CC_SELIN_SHIFT 4
+#define CC_SELIN_XPUL (0x0 << CC_SELIN_SHIFT)
+#define CC_SELIN_YPLL (0x1 << CC_SELIN_SHIFT)
+#define CC_SELIN_XNUR (0x2 << CC_SELIN_SHIFT)
+#define CC_SELIN_YNLR (0x3 << CC_SELIN_SHIFT)
+#define CC_SELIN_WIPER (0x4 << CC_SELIN_SHIFT)
+#define CC_SELIN_INAUX0 (0x5 << CC_SELIN_SHIFT)
+#define CC_SELIN_INAUX1 (0x6 << CC_SELIN_SHIFT)
+#define CC_SELIN_INAUX2 (0x7 << CC_SELIN_SHIFT)
+#define CC_SELIN_MASK (0x7 << CC_SELIN_SHIFT)
+#define CC_SELREFP_SHIFT 7
+#define CC_SELREFP_YPLL (0x0 << CC_SELREFP_SHIFT)
+#define CC_SELREFP_XPUL (0x1 << CC_SELREFP_SHIFT)
+#define CC_SELREFP_EXT (0x2 << CC_SELREFP_SHIFT)
+#define CC_SELREFP_INT (0x3 << CC_SELREFP_SHIFT)
+#define CC_SELREFP_MASK (0x3 << CC_SELREFP_SHIFT)
+#define CC_XPULSW (1 << 9)
+#define CC_XNURSW_SHIFT 10
+#define CC_XNURSW_HIGH (0x0 << CC_XNURSW_SHIFT)
+#define CC_XNURSW_OFF (0x1 << CC_XNURSW_SHIFT)
+#define CC_XNURSW_LOW (0x3 << CC_XNURSW_SHIFT)
+#define CC_XNURSW_MASK (0x3 << CC_XNURSW_SHIFT)
+#define CC_YPLLSW_SHIFT 12
+#define CC_YPLLSW_MASK (0x3 << CC_YPLLSW_SHIFT)
+#define CC_YNLRSW (1 << 14)
+#define CC_WIPERSW (1 << 15)
+#define CC_NOS_SHIFT 16
+#define CC_YPLLSW_HIGH (0x0 << CC_NOS_SHIFT)
+#define CC_YPLLSW_OFF (0x1 << CC_NOS_SHIFT)
+#define CC_YPLLSW_LOW (0x3 << CC_NOS_SHIFT
+#define CC_NOS_MASK (0xf << CC_NOS_SHIFT)
+#define CC_IGS (1 << 20)
+#define CC_SETTLING_TIME_SHIFT 24
+#define CC_SETTLING_TIME_MASK (0xff << CC_SETTLING_TIME_SHIFT)
+
+#define TSC_4WIRE_PRECHARGE 0x158c
+#define TSC_4WIRE_TOUCH_DETECT 0x578e
+
+#define TSC_4WIRE_X_MEASUMENT 0x1c90
+#define TSC_4WIRE_Y_MEASUMENT 0x4604
+
+#define TSC_GENERAL_ADC_GCC0 0x17dc
+#define TSC_GENERAL_ADC_GCC1 0x17ec
+#define TSC_GENERAL_ADC_GCC2 0x17fc
+
+/* GeneralADC Convert Queue FIFO Register */
+#define GCQFIFO 0x800
+#define GCQFIFO_ADCOUT_SHIFT 4
+#define GCQFIFO_ADCOUT_MASK (0xfff << GCQFIFO_ADCOUT_SHIFT)
+/* GeneralADC Convert Queue Control Register */
+#define GCQCR 0x804
+/* GeneralADC Convert Queue Status Register */
+#define GCQSR 0x808
+/* GeneralADC Convert Queue Mask Register */
+#define GCQMR 0x80c
+
+/* GeneralADC Convert Queue ITEM 7~0 */
+#define GCQ_ITEM_7_0 0x820
+/* GeneralADC Convert Queue ITEM 15~8 */
+#define GCQ_ITEM_15_8 0x824
+
+#define GCQ_ITEM7_SHIFT 28
+#define GCQ_ITEM6_SHIFT 24
+#define GCQ_ITEM5_SHIFT 20
+#define GCQ_ITEM4_SHIFT 16
+#define GCQ_ITEM3_SHIFT 12
+#define GCQ_ITEM2_SHIFT 8
+#define GCQ_ITEM1_SHIFT 4
+#define GCQ_ITEM0_SHIFT 0
+
+#define GCQ_ITEM_GCC0 0x0
+#define GCQ_ITEM_GCC1 0x1
+#define GCQ_ITEM_GCC2 0x2
+#define GCQ_ITEM_GCC3 0x3
+
+/* GeneralADC Convert Config 0-7 */
+#define GCC0 0x840
+#define GCC1 0x844
+#define GCC2 0x848
+#define GCC3 0x84c
+#define GCC4 0x850
+#define GCC5 0x854
+#define GCC6 0x858
+#define GCC7 0x85c
+
+/* TSC Test Register R/W */
+#define TTR 0xc00
+/* TSC Monitor Register 1, 2 */
+#define MNT1 0xc04
+#define MNT2 0xc04
+
+#define DETECT_ITEM_ID_1 1
+#define DETECT_ITEM_ID_2 5
+#define TS_X_ITEM_ID 2
+#define TS_Y_ITEM_ID 3
+#define TSI_DATA 1
+#define FQS_DATA 0
+
+#endif /* __IMX_ADC_H__ */
diff --git a/drivers/mxc/asrc/Kconfig b/drivers/mxc/asrc/Kconfig
new file mode 100644
index 000000000000..a4c66b19f067
--- /dev/null
+++ b/drivers/mxc/asrc/Kconfig
@@ -0,0 +1,13 @@
+#
+# ASRC configuration
+#
+
+menu "MXC Asynchronous Sample Rate Converter support"
+
+config MXC_ASRC
+ tristate "ASRC support"
+ depends on ARCH_MX35
+ ---help---
+ Say Y to get the ASRC service.
+
+endmenu
diff --git a/drivers/mxc/asrc/Makefile b/drivers/mxc/asrc/Makefile
new file mode 100644
index 000000000000..0d2487d389c7
--- /dev/null
+++ b/drivers/mxc/asrc/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel Asynchronous Sample Rate Converter driver
+#
+
+ifeq ($(CONFIG_ARCH_MX35),y)
+ obj-$(CONFIG_MXC_ASRC) += mxc_asrc.o
+endif
diff --git a/drivers/mxc/asrc/mxc_asrc.c b/drivers/mxc/asrc/mxc_asrc.c
new file mode 100644
index 000000000000..653dc77540ea
--- /dev/null
+++ b/drivers/mxc/asrc/mxc_asrc.c
@@ -0,0 +1,1687 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_asrc.c
+ *
+ * @brief MXC Asynchronous Sample Rate Converter
+ *
+ * @ingroup SOUND
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/dma-mapping.h>
+#include <linux/mxc_asrc.h>
+#include <asm/irq.h>
+#include <asm/memory.h>
+#include <mach/dma.h>
+
+static int asrc_major;
+static struct class *asrc_class;
+#define ASRC_PROC_PATH "driver/asrc"
+
+#define ASRC_RATIO_DECIMAL_DEPTH 26
+
+DEFINE_SPINLOCK(data_lock);
+DEFINE_SPINLOCK(input_int_lock);
+DEFINE_SPINLOCK(output_int_lock);
+
+#define AICPA 0 /* Input Clock Divider A Offset */
+#define AICDA 3 /* Input Clock Prescaler A Offset */
+#define AICPB 6 /* Input Clock Divider B Offset */
+#define AICDB 9 /* Input Clock Prescaler B Offset */
+#define AOCPA 12 /* Output Clock Divider A Offset */
+#define AOCDA 15 /* Output Clock Prescaler A Offset */
+#define AOCPB 18 /* Output Clock Divider B Offset */
+#define AOCDB 21 /* Output Clock Prescaler B Offset */
+#define AICPC 0 /* Input Clock Divider C Offset */
+#define AICDC 3 /* Input Clock Prescaler C Offset */
+#define AOCDC 6 /* Output Clock Prescaler C Offset */
+#define AOCPC 9 /* Output Clock Divider C Offset */
+
+char *asrc_pair_id[] = {
+ [0] = "ASRC RX PAIR A",
+ [1] = "ASRC TX PAIR A",
+ [2] = "ASRC RX PAIR B",
+ [3] = "ASRC TX PAIR B",
+ [4] = "ASRC RX PAIR C",
+ [5] = "ASRC TX PAIR C",
+};
+
+enum asrc_status {
+ ASRC_ASRSTR_AIDEA = 0x01,
+ ASRC_ASRSTR_AIDEB = 0x02,
+ ASRC_ASRSTR_AIDEC = 0x04,
+ ASRC_ASRSTR_AODFA = 0x08,
+ ASRC_ASRSTR_AODFB = 0x10,
+ ASRC_ASRSTR_AODFC = 0x20,
+ ASRC_ASRSTR_AOLE = 0x40,
+ ASRC_ASRSTR_FPWT = 0x80,
+ ASRC_ASRSTR_AIDUA = 0x100,
+ ASRC_ASRSTR_AIDUB = 0x200,
+ ASRC_ASRSTR_AIDUC = 0x400,
+ ASRC_ASRSTR_AODOA = 0x800,
+ ASRC_ASRSTR_AODOB = 0x1000,
+ ASRC_ASRSTR_AODOC = 0x2000,
+ ASRC_ASRSTR_AIOLA = 0x4000,
+ ASRC_ASRSTR_AIOLB = 0x8000,
+ ASRC_ASRSTR_AIOLC = 0x10000,
+ ASRC_ASRSTR_AOOLA = 0x20000,
+ ASRC_ASRSTR_AOOLB = 0x40000,
+ ASRC_ASRSTR_AOOLC = 0x80000,
+ ASRC_ASRSTR_ATQOL = 0x100000,
+ ASRC_ASRSTR_DSLCNT = 0x200000,
+};
+
+/* Sample rates are aligned with that defined in pcm.h file */
+static const unsigned char asrc_process_table[][8][2] = {
+ /* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
+/*5512Hz*/
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*8kHz*/
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*11025Hz*/
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*16kHz*/
+ {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*22050Hz*/
+ {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},},
+/*32kHz*/
+ {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},},
+/*44.1kHz*/
+ {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},
+/*48kHz*/
+ {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},},
+/*64kHz*/
+ {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},},
+/*88.2kHz*/
+ {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},
+/*96kHz*/
+ {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},},
+/*176kHz*/
+ {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},
+/*192kHz*/
+ {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},},
+};
+
+static const unsigned char asrc_divider_table[] = {
+/*5500Hz 8kHz 11025Hz 16kHz 22050kHz 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176400Hz 192kHz*/
+ 0x07, 0x15, 0x06, 0x14, 0x05, 0x13, 0x04, 0x04, 0x12, 0x03, 0x03, 0x02,
+ 0x02,
+};
+
+static struct asrc_data *g_asrc_data;
+static struct proc_dir_entry *proc_asrc;
+static unsigned long asrc_vrt_base_addr;
+static struct mxc_asrc_platform_data *mxc_asrc_data;
+
+static int asrc_set_clock_ratio(enum asrc_pair_index index,
+ int input_sample_rate, int output_sample_rate)
+{
+ int i;
+ int integ = 0;
+ unsigned long reg_val = 0;
+
+ if (output_sample_rate == 0)
+ return -1;
+ while (input_sample_rate >= output_sample_rate) {
+ input_sample_rate -= output_sample_rate;
+ integ++;
+ }
+ reg_val |= (integ << 26);
+
+ for (i = 1; i <= ASRC_RATIO_DECIMAL_DEPTH; i++) {
+ if ((input_sample_rate * 2) >= output_sample_rate) {
+ reg_val |= (1 << (ASRC_RATIO_DECIMAL_DEPTH - i));
+ input_sample_rate =
+ input_sample_rate * 2 - output_sample_rate;
+ } else
+ input_sample_rate = input_sample_rate << 1;
+
+ if (input_sample_rate == 0)
+ break;
+ }
+
+ __raw_writel(reg_val,
+ (asrc_vrt_base_addr + ASRC_ASRIDRLA_REG + (index << 3)));
+ __raw_writel((reg_val >> 24),
+ (asrc_vrt_base_addr + ASRC_ASRIDRHA_REG + (index << 3)));
+ return 0;
+}
+
+static int asrc_set_process_configuration(enum asrc_pair_index index,
+ int input_sample_rate,
+ int output_sample_rate)
+{
+ int i = 0, j = 0;
+ unsigned long reg;
+ switch (input_sample_rate) {
+ case 5512:
+ i = 0;
+ case 8000:
+ i = 1;
+ break;
+ case 11025:
+ i = 2;
+ break;
+ case 16000:
+ i = 3;
+ break;
+ case 22050:
+ i = 4;
+ break;
+ case 32000:
+ i = 5;
+ break;
+ case 44100:
+ i = 6;
+ break;
+ case 48000:
+ i = 7;
+ break;
+ case 64000:
+ i = 8;
+ break;
+ case 88200:
+ i = 9;
+ break;
+ case 96000:
+ i = 10;
+ break;
+ case 176400:
+ i = 11;
+ break;
+ case 192000:
+ i = 12;
+ break;
+ default:
+ return -1;
+ }
+
+ switch (output_sample_rate) {
+ case 32000:
+ j = 0;
+ break;
+ case 44100:
+ j = 1;
+ break;
+ case 48000:
+ j = 2;
+ break;
+ case 64000:
+ j = 3;
+ break;
+ case 88200:
+ j = 4;
+ break;
+ case 96000:
+ j = 5;
+ break;
+ case 176400:
+ j = 6;
+ break;
+ case 192000:
+ j = 7;
+ break;
+ default:
+ return -1;
+ }
+
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCFG_REG);
+ reg &= ~(0x0f << (6 + (index << 2)));
+ reg |=
+ ((asrc_process_table[i][j][0] << (6 + (index << 2))) |
+ (asrc_process_table[i][j][1] << (8 + (index << 2))));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCFG_REG);
+
+ return 0;
+}
+
+static int asrc_get_asrck_clock_divider(int sample_rate)
+{
+ int i = 0;
+ switch (sample_rate) {
+ case 5500:
+ i = 0;
+ break;
+ case 8000:
+ i = 1;
+ break;
+ case 11025:
+ i = 2;
+ break;
+ case 16000:
+ i = 3;
+ break;
+ case 22050:
+ i = 4;
+ break;
+ case 32000:
+ i = 5;
+ break;
+ case 44100:
+ i = 6;
+ break;
+ case 48000:
+ i = 7;
+ break;
+ case 64000:
+ i = 8;
+ break;
+ case 88200:
+ i = 9;
+ break;
+ case 96000:
+ i = 10;
+ break;
+ case 176400:
+ i = 11;
+ break;
+ case 192000:
+ i = 12;
+ break;
+ default:
+ return -1;
+ }
+
+ return asrc_divider_table[i];
+}
+
+int asrc_req_pair(int chn_num, enum asrc_pair_index *index)
+{
+ int err = 0;
+ unsigned long lock_flags;
+ spin_lock_irqsave(&data_lock, lock_flags);
+
+ if (chn_num > 2) {
+ if (g_asrc_data->asrc_pair[ASRC_PAIR_C].active
+ || (chn_num > g_asrc_data->asrc_pair[ASRC_PAIR_C].chn_max))
+ err = -EBUSY;
+ else {
+ *index = ASRC_PAIR_C;
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].chn_num = chn_num;
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].active = 1;
+ }
+ } else {
+ if (g_asrc_data->asrc_pair[ASRC_PAIR_A].active ||
+ (g_asrc_data->asrc_pair[ASRC_PAIR_A].chn_max == 0)) {
+ if (g_asrc_data->asrc_pair[ASRC_PAIR_B].
+ active
+ || (g_asrc_data->asrc_pair[ASRC_PAIR_B].
+ chn_max == 0))
+ err = -EBUSY;
+ else {
+ *index = ASRC_PAIR_B;
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].chn_num = 2;
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].active = 1;
+ }
+ } else {
+ *index = ASRC_PAIR_A;
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].chn_num = 2;
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].active = 1;
+ }
+ }
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+ return err;
+}
+
+EXPORT_SYMBOL(asrc_req_pair);
+
+void asrc_release_pair(enum asrc_pair_index index)
+{
+ unsigned long reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&data_lock, lock_flags);
+ g_asrc_data->asrc_pair[index].active = 0;
+ g_asrc_data->asrc_pair[index].overload_error = 0;
+ /********Disable PAIR*************/
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ reg &= ~(1 << (index + 1));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+}
+
+EXPORT_SYMBOL(asrc_release_pair);
+
+int asrc_config_pair(struct asrc_config *config)
+{
+ int err = 0;
+ int reg, tmp, channel_num;
+ unsigned long lock_flags;
+ /* Set the channel number */
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+ spin_lock_irqsave(&data_lock, lock_flags);
+ g_asrc_data->asrc_pair[config->pair].chn_num = config->channel_num;
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+ reg &=
+ ~((0xFFFFFFFF >> (32 - mxc_asrc_data->channel_bits)) <<
+ (mxc_asrc_data->channel_bits * config->pair));
+ if (mxc_asrc_data->channel_bits > 3)
+ channel_num = config->channel_num;
+ else
+ channel_num = (config->channel_num + 1) / 2;
+ tmp = channel_num << (mxc_asrc_data->channel_bits * config->pair);
+ reg |= tmp;
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+
+ /* Set the clock source */
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCSR_REG);
+ tmp = ~(0x0f << (config->pair << 2));
+ reg &= tmp;
+ tmp = ~(0x0f << (12 + (config->pair << 2)));
+ reg &= tmp;
+ reg |=
+ ((config->inclk << (config->pair << 2)) | (config->
+ outclk << (12 +
+ (config->
+ pair <<
+ 2))));
+
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCSR_REG);
+
+ /* default setting */
+ /* automatic selection for processing mode */
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ reg |= (1 << (20 + config->pair));
+ reg &= ~(1 << (14 + (config->pair << 1)));
+
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRRA_REG);
+ reg &= 0xffbfffff;
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRRA_REG);
+
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ reg = reg & (~(1 << 23));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+ /* Default Clock Divider Setting */
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+ if (config->pair == ASRC_PAIR_A) {
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+ reg &= 0xfc0fc0;
+ /* Input Part */
+ if ((config->inclk & 0x0f) == INCLK_SPDIF_RX
+ || (config->inclk & 0x0f) == INCLK_SPDIF_TX) {
+ reg |= 7 << AICPA;
+ } else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ input_sample_rate);
+ reg |= tmp << AICPA;
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AICPA;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AICPA;
+ else
+ err = -EFAULT;
+ }
+ /* Output Part */
+ if ((config->outclk & 0x0f) == OUTCLK_SPDIF_RX
+ || (config->outclk & 0x0f) == OUTCLK_SPDIF_TX) {
+ reg |= 7 << AOCPA;
+ } else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ output_sample_rate);
+ reg |= tmp << AOCPA;
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AOCPA;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AOCPA;
+ else
+ err = -EFAULT;
+ }
+
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+
+ } else if (config->pair == ASRC_PAIR_B) {
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+ reg &= 0x03f03f;
+ /* Input Part */
+ if ((config->inclk & 0x0f) == INCLK_SPDIF_RX
+ || (config->inclk & 0x0f) == INCLK_SPDIF_TX) {
+ reg |= 7 << AICPB;
+ } else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ input_sample_rate);
+ reg |= tmp << AICPB;
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AICPB;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AICPB;
+ else
+ err = -EFAULT;
+ }
+ /* Output Part */
+ if ((config->outclk & 0x0f) == INCLK_SPDIF_RX
+ || (config->outclk & 0x0f) == INCLK_SPDIF_TX) {
+ reg |= 7 << AOCPB;
+ } else if ((config->outclk & 0x0f) == INCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ output_sample_rate);
+ reg |= tmp << AOCPB;
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AOCPB;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AOCPB;
+ else
+ err = -EFAULT;
+ }
+
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCDR1_REG);
+
+ } else {
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCDR2_REG);
+ reg &= 0;
+ /* Input Part */
+ if ((config->inclk & 0x0f) == INCLK_SPDIF_RX
+ || (config->inclk & 0x0f) == INCLK_SPDIF_TX) {
+ reg |= 7 << AICPC;
+ } else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ input_sample_rate);
+ reg |= tmp << AICPC;
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AICPC;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AICPC;
+ else
+ err = -EFAULT;
+ }
+ /* Output Part */
+ if ((config->outclk & 0x0f) == INCLK_SPDIF_RX
+ || (config->outclk & 0x0f) == INCLK_SPDIF_TX) {
+ reg |= 7 << AOCPC;
+ } else if ((config->outclk & 0x0f) == INCLK_ASRCK1_CLK) {
+ tmp =
+ asrc_get_asrck_clock_divider(config->
+ output_sample_rate);
+ reg |= tmp << AOCPC;
+ } else {
+ if (config->word_width == 16 || config->word_width == 8)
+ reg |= 5 << AOCPC;
+ else if (config->word_width == 32
+ || config->word_width == 24)
+ reg |= 6 << AOCPC;
+ else
+ err = -EFAULT;
+ }
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCDR2_REG);
+
+ }
+
+ /* check whether ideal ratio is a must */
+ if ((config->inclk & 0x0f) == INCLK_NONE) {
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ reg &= ~(1 << (20 + config->pair));
+ reg |= (0x03 << (13 + (config->pair << 1)));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ err = asrc_set_clock_ratio(config->pair,
+ config->input_sample_rate,
+ config->output_sample_rate);
+ if (err < 0)
+ return err;
+
+ err = asrc_set_process_configuration(config->pair,
+ config->input_sample_rate,
+ config->
+ output_sample_rate);
+ if (err < 0)
+ return err;
+ } else if ((config->inclk & 0x0f) == INCLK_ASRCK1_CLK) {
+ if (config->input_sample_rate == 44100
+ || config->input_sample_rate == 88200) {
+ pr_info
+ ("ASRC core clock cann't support sample rate %d\n",
+ config->input_sample_rate);
+ err = -EFAULT;
+ }
+ } else if ((config->outclk & 0x0f) == OUTCLK_ASRCK1_CLK) {
+ if (config->output_sample_rate == 44100
+ || config->output_sample_rate == 88200) {
+ pr_info
+ ("ASRC core clock cann't support sample rate %d\n",
+ config->input_sample_rate);
+ err = -EFAULT;
+ }
+ }
+
+ return err;
+}
+
+EXPORT_SYMBOL(asrc_config_pair);
+
+void asrc_start_conv(enum asrc_pair_index index)
+{
+ int reg, reg_1;
+ unsigned long lock_flags;
+ int i;
+
+ spin_lock_irqsave(&data_lock, lock_flags);
+
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ if ((reg & 0x0E) == 0)
+ clk_enable(mxc_asrc_data->asrc_audio_clk);
+ reg |= (1 << (1 + index));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCFG_REG);
+ while (!(reg & (1 << (index + 21))))
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCFG_REG);
+ reg_1 = __raw_readl(asrc_vrt_base_addr + ASRC_ASRSTR_REG);
+
+ reg = 0;
+ for (i = 0; i < 20; i++) {
+ __raw_writel(reg,
+ asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+ (index << 3));
+ __raw_writel(reg,
+ asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+ (index << 3));
+ __raw_writel(reg,
+ asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+ (index << 3));
+ __raw_writel(reg,
+ asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+ (index << 3));
+ __raw_writel(reg,
+ asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+ (index << 3));
+ __raw_writel(reg,
+ asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+ (index << 3));
+ __raw_writel(reg,
+ asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+ (index << 3));
+ __raw_writel(reg,
+ asrc_vrt_base_addr + ASRC_ASRDIA_REG +
+ (index << 3));
+ }
+
+ __raw_writel(0x40, asrc_vrt_base_addr + ASRC_ASRIER_REG);
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+ return;
+}
+
+EXPORT_SYMBOL(asrc_start_conv);
+
+void asrc_stop_conv(enum asrc_pair_index index)
+{
+ int reg;
+ unsigned long lock_flags;
+ spin_lock_irqsave(&data_lock, lock_flags);
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ reg &= ~(1 << (1 + index));
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+ if ((reg & 0x0E) == 0)
+ clk_disable(mxc_asrc_data->asrc_audio_clk);
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+ return;
+}
+
+EXPORT_SYMBOL(asrc_stop_conv);
+
+/*!
+ * @brief asrc interrupt handler
+ */
+static irqreturn_t asrc_isr(int irq, void *dev_id)
+{
+ unsigned long status;
+ int reg = 0x40;
+
+ status = __raw_readl(asrc_vrt_base_addr + ASRC_ASRSTR_REG);
+ if (g_asrc_data->asrc_pair[ASRC_PAIR_A].active == 1) {
+ if (status & ASRC_ASRSTR_ATQOL)
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+ ASRC_TASK_Q_OVERLOAD;
+ if (status & ASRC_ASRSTR_AOOLA)
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+ ASRC_OUTPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AIOLA)
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+ ASRC_INPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AODOA)
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+ ASRC_OUTPUT_BUFFER_OVERFLOW;
+ if (status & ASRC_ASRSTR_AIDUA)
+ g_asrc_data->asrc_pair[ASRC_PAIR_A].overload_error |=
+ ASRC_INPUT_BUFFER_UNDERRUN;
+ } else if (g_asrc_data->asrc_pair[ASRC_PAIR_B].active == 1) {
+ if (status & ASRC_ASRSTR_ATQOL)
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+ ASRC_TASK_Q_OVERLOAD;
+ if (status & ASRC_ASRSTR_AOOLB)
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+ ASRC_OUTPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AIOLB)
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+ ASRC_INPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AODOB)
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+ ASRC_OUTPUT_BUFFER_OVERFLOW;
+ if (status & ASRC_ASRSTR_AIDUB)
+ g_asrc_data->asrc_pair[ASRC_PAIR_B].overload_error |=
+ ASRC_INPUT_BUFFER_UNDERRUN;
+ } else if (g_asrc_data->asrc_pair[ASRC_PAIR_C].active == 1) {
+ if (status & ASRC_ASRSTR_ATQOL)
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+ ASRC_TASK_Q_OVERLOAD;
+ if (status & ASRC_ASRSTR_AOOLC)
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+ ASRC_OUTPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AIOLC)
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+ ASRC_INPUT_TASK_OVERLOAD;
+ if (status & ASRC_ASRSTR_AODOC)
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+ ASRC_OUTPUT_BUFFER_OVERFLOW;
+ if (status & ASRC_ASRSTR_AIDUC)
+ g_asrc_data->asrc_pair[ASRC_PAIR_C].overload_error |=
+ ASRC_INPUT_BUFFER_UNDERRUN;
+ }
+
+ /* try to clean the overload error */
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRSTR_REG);
+
+ return IRQ_HANDLED;
+}
+
+void asrc_get_status(struct asrc_status_flags *flags)
+{
+ unsigned long lock_flags;
+ enum asrc_pair_index index;
+
+ spin_lock_irqsave(&data_lock, lock_flags);
+ index = flags->index;
+ flags->overload_error = g_asrc_data->asrc_pair[index].overload_error;
+
+ spin_unlock_irqrestore(&data_lock, lock_flags);
+ return;
+}
+
+EXPORT_SYMBOL(asrc_get_status);
+
+static int mxc_init_asrc(void)
+{
+ /* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
+ __raw_writel(0x0001, asrc_vrt_base_addr + ASRC_ASRCTR_REG);
+
+ /* Enable overflow interrupt */
+ __raw_writel(0x00, asrc_vrt_base_addr + ASRC_ASRIER_REG);
+
+ /* Default 6: 2: 2 channel assignment */
+ __raw_writel((0x06 << mxc_asrc_data->channel_bits *
+ 2) | (0x02 << mxc_asrc_data->channel_bits) | 0x02,
+ asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+
+ /* Parameter Registers recommended settings */
+ __raw_writel(0x7fffff, asrc_vrt_base_addr + ASRC_ASRPM1_REG);
+ __raw_writel(0x255555, asrc_vrt_base_addr + ASRC_ASRPM2_REG);
+ __raw_writel(0xff7280, asrc_vrt_base_addr + ASRC_ASRPM3_REG);
+ __raw_writel(0xff7280, asrc_vrt_base_addr + ASRC_ASRPM4_REG);
+ __raw_writel(0xff7280, asrc_vrt_base_addr + ASRC_ASRPM5_REG);
+
+ __raw_writel(0x001f00, asrc_vrt_base_addr + ASRC_ASRTFR1);
+
+ /* Set the processing clock for 76KHz, 133M */
+ __raw_writel(0x06D6, asrc_vrt_base_addr + ASRC_ASR76K_REG);
+
+ /* Set the processing clock for 56KHz, 133M */
+ __raw_writel(0x0947, asrc_vrt_base_addr + ASRC_ASR56K_REG);
+
+ if (request_irq(MXC_INT_ASRC, asrc_isr, 0, "asrc", NULL))
+ return -1;
+
+ return 0;
+}
+
+static int asrc_get_output_buffer_size(int input_buffer_size,
+ int input_sample_rate,
+ int output_sample_rate)
+{
+ int i = 0;
+ int outbuffer_size = 0;
+ int outsample = output_sample_rate;
+ while (outsample >= input_sample_rate) {
+ ++i;
+ outsample -= input_sample_rate;
+ }
+ outbuffer_size = i * input_buffer_size;
+ i = 1;
+ while (((input_buffer_size >> i) > 2) && (outsample != 0)) {
+ if (((outsample << 1) - input_sample_rate) >= 0) {
+ outsample = (outsample << 1) - input_sample_rate;
+ outbuffer_size += (input_buffer_size >> i);
+ } else {
+ outsample = outsample << 1;
+ }
+ i++;
+ }
+ outbuffer_size = (outbuffer_size >> 3) << 3;
+ return outbuffer_size;
+}
+
+static void asrc_input_dma_callback(void *data, int error, unsigned int count)
+{
+ struct asrc_pair_params *params;
+ struct dma_block *block;
+ mxc_dma_requestbuf_t dma_request;
+ unsigned long lock_flags;
+
+ params = data;
+
+ spin_lock_irqsave(&input_int_lock, lock_flags);
+ params->input_queue_empty--;
+ if (!list_empty(&params->input_queue)) {
+ block =
+ list_entry(params->input_queue.next,
+ struct dma_block, queue);
+ dma_request.src_addr = (dma_addr_t) block->dma_paddr;
+ dma_request.dst_addr =
+ (ASRC_BASE_ADDR + ASRC_ASRDIA_REG + (params->index << 3));
+ dma_request.num_of_bytes = block->length;
+ mxc_dma_config(params->input_dma_channel, &dma_request,
+ 1, MXC_DMA_MODE_WRITE);
+ list_del(params->input_queue.next);
+ list_add_tail(&block->queue, &params->input_done_queue);
+ params->input_queue_empty++;
+ }
+ params->input_counter++;
+ wake_up_interruptible(&params->input_wait_queue);
+ spin_unlock_irqrestore(&input_int_lock, lock_flags);
+ return;
+}
+
+static void asrc_output_dma_callback(void *data, int error, unsigned int count)
+{
+ struct asrc_pair_params *params;
+ struct dma_block *block;
+ mxc_dma_requestbuf_t dma_request;
+ unsigned long lock_flags;
+
+ params = data;
+
+ spin_lock_irqsave(&output_int_lock, lock_flags);
+ params->output_queue_empty--;
+
+ if (!list_empty(&params->output_queue)) {
+ block =
+ list_entry(params->output_queue.next,
+ struct dma_block, queue);
+ dma_request.src_addr =
+ (ASRC_BASE_ADDR + ASRC_ASRDOA_REG + (params->index << 3));
+ dma_request.dst_addr = (dma_addr_t) block->dma_paddr;
+ dma_request.num_of_bytes = block->length;
+ mxc_dma_config(params->output_dma_channel, &dma_request,
+ 1, MXC_DMA_MODE_READ);
+ list_del(params->output_queue.next);
+ list_add_tail(&block->queue, &params->output_done_queue);
+ params->output_queue_empty++;
+ }
+ params->output_counter++;
+ wake_up_interruptible(&params->output_wait_queue);
+ spin_unlock_irqrestore(&output_int_lock, lock_flags);
+ return;
+}
+
+static void mxc_free_dma_buf(struct asrc_pair_params *params)
+{
+ int i;
+ for (i = 0; i < ASRC_DMA_BUFFER_NUM; i++) {
+ if (params->input_dma[i].dma_vaddr != NULL) {
+ dma_free_coherent(0,
+ params->input_buffer_size,
+ params->input_dma[i].
+ dma_vaddr,
+ params->input_dma[i].dma_paddr);
+ params->input_dma[i].dma_vaddr = NULL;
+ }
+ if (params->output_dma[i].dma_vaddr != NULL) {
+ dma_free_coherent(0,
+ params->output_buffer_size,
+ params->output_dma[i].
+ dma_vaddr,
+ params->output_dma[i].dma_paddr);
+ params->output_dma[i].dma_vaddr = NULL;
+ }
+ }
+
+ return;
+}
+
+static int mxc_allocate_dma_buf(struct asrc_pair_params *params)
+{
+ int i;
+ for (i = 0; i < ASRC_DMA_BUFFER_NUM; i++) {
+ params->input_dma[i].dma_vaddr =
+ dma_alloc_coherent(0, params->input_buffer_size,
+ &params->input_dma[i].dma_paddr,
+ GFP_DMA | GFP_KERNEL);
+ if (params->input_dma[i].dma_vaddr == NULL) {
+ mxc_free_dma_buf(params);
+ pr_info("can't allocate buff\n");
+ return -ENOBUFS;
+ }
+ }
+ for (i = 0; i < ASRC_DMA_BUFFER_NUM; i++) {
+ params->output_dma[i].dma_vaddr =
+ dma_alloc_coherent(0,
+ params->output_buffer_size,
+ &params->output_dma[i].dma_paddr,
+ GFP_DMA | GFP_KERNEL);
+ if (params->output_dma[i].dma_vaddr == NULL) {
+ mxc_free_dma_buf(params);
+ return -ENOBUFS;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * asrc interface - ioctl function
+ *
+ * @param inode struct inode *
+ *
+ * @param file struct file *
+ *
+ * @param cmd unsigned int
+ *
+ * @param arg unsigned long
+ *
+ * @return 0 success, ENODEV for invalid device instance,
+ * -1 for other errors.
+ */
+static int asrc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ struct asrc_pair_params *params;
+ params = file->private_data;
+
+ if (down_interruptible(&params->busy_lock))
+ return -EBUSY;
+ switch (cmd) {
+ case ASRC_REQ_PAIR:
+ {
+ struct asrc_req req;
+ if (copy_from_user(&req, (void __user *)arg,
+ sizeof(struct asrc_req))) {
+ err = -EFAULT;
+ break;
+ }
+ err = asrc_req_pair(req.chn_num, &req.index);
+ if (err < 0)
+ break;
+ params->pair_hold = 1;
+ params->index = req.index;
+ if (copy_to_user
+ ((void __user *)arg, &req, sizeof(struct asrc_req)))
+ err = -EFAULT;
+
+ break;
+ }
+ case ASRC_CONFIG_PAIR:
+ {
+ struct asrc_config config;
+ mxc_dma_device_t rx_id, tx_id;
+ char *rx_name, *tx_name;
+ int channel = -1;
+ if (copy_from_user
+ (&config, (void __user *)arg,
+ sizeof(struct asrc_config))) {
+ err = -EFAULT;
+ break;
+ }
+ err = asrc_config_pair(&config);
+ if (err < 0)
+ break;
+ params->output_buffer_size =
+ asrc_get_output_buffer_size(config.
+ dma_buffer_size,
+ config.
+ input_sample_rate,
+ config.
+ output_sample_rate);
+ params->input_buffer_size = config.dma_buffer_size;
+ if (config.buffer_num > ASRC_DMA_BUFFER_NUM)
+ params->buffer_num = ASRC_DMA_BUFFER_NUM;
+ else
+ params->buffer_num = config.buffer_num;
+ err = mxc_allocate_dma_buf(params);
+ if (err < 0)
+ break;
+
+ /* TBD - need to update when new SDMA interface ready */
+ if (config.pair == ASRC_PAIR_A) {
+ rx_id = MXC_DMA_ASRC_A_RX;
+ tx_id = MXC_DMA_ASRC_A_TX;
+ rx_name = asrc_pair_id[0];
+ tx_name = asrc_pair_id[1];
+ } else if (config.pair == ASRC_PAIR_B) {
+ rx_id = MXC_DMA_ASRC_B_RX;
+ tx_id = MXC_DMA_ASRC_B_TX;
+ rx_name = asrc_pair_id[2];
+ tx_name = asrc_pair_id[3];
+ } else {
+ rx_id = MXC_DMA_ASRC_C_RX;
+ tx_id = MXC_DMA_ASRC_C_TX;
+ rx_name = asrc_pair_id[4];
+ tx_name = asrc_pair_id[5];
+ }
+ channel = mxc_dma_request(rx_id, rx_name);
+ params->input_dma_channel = channel;
+ err = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+ asrc_input_dma_callback,
+ (void *)params);
+ channel = mxc_dma_request(tx_id, tx_name);
+ params->output_dma_channel = channel;
+ err = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+ asrc_output_dma_callback,
+ (void *)params);
+ /* TBD - need to update when new SDMA interface ready */
+ params->input_queue_empty = 0;
+ params->output_queue_empty = 0;
+ INIT_LIST_HEAD(&params->input_queue);
+ INIT_LIST_HEAD(&params->input_done_queue);
+ INIT_LIST_HEAD(&params->output_queue);
+ INIT_LIST_HEAD(&params->output_done_queue);
+ init_waitqueue_head(&params->input_wait_queue);
+ init_waitqueue_head(&params->output_wait_queue);
+
+ if (copy_to_user
+ ((void __user *)arg, &config,
+ sizeof(struct asrc_config)))
+ err = -EFAULT;
+ break;
+ }
+ case ASRC_QUERYBUF:
+ {
+ struct asrc_querybuf buffer;
+ if (copy_from_user
+ (&buffer, (void __user *)arg,
+ sizeof(struct asrc_querybuf))) {
+ err = -EFAULT;
+ break;
+ }
+ buffer.input_offset =
+ (unsigned long)params->input_dma[buffer.
+ buffer_index].
+ dma_paddr;
+ buffer.input_length = params->input_buffer_size;
+ buffer.output_offset =
+ (unsigned long)params->output_dma[buffer.
+ buffer_index].
+ dma_paddr;
+ buffer.output_length = params->output_buffer_size;
+ if (copy_to_user
+ ((void __user *)arg, &buffer,
+ sizeof(struct asrc_querybuf)))
+ err = -EFAULT;
+ break;
+ }
+ case ASRC_RELEASE_PAIR:
+ {
+ enum asrc_pair_index index;
+ if (copy_from_user
+ (&index, (void __user *)arg,
+ sizeof(enum asrc_pair_index))) {
+ err = -EFAULT;
+ break;
+ }
+
+ mxc_dma_free(params->input_dma_channel);
+ mxc_dma_free(params->output_dma_channel);
+ mxc_free_dma_buf(params);
+ asrc_release_pair(index);
+ params->pair_hold = 0;
+ break;
+ }
+ case ASRC_Q_INBUF:
+ {
+ struct asrc_buffer buf;
+ struct dma_block *block;
+ mxc_dma_requestbuf_t dma_request;
+ unsigned long lock_flags;
+ if (copy_from_user
+ (&buf, (void __user *)arg,
+ sizeof(struct asrc_buffer))) {
+ err = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&input_int_lock, lock_flags);
+ params->input_dma[buf.index].index = buf.index;
+ params->input_dma[buf.index].length = buf.length;
+ list_add_tail(&params->input_dma[buf.index].
+ queue, &params->input_queue);
+ if (params->asrc_active == 0
+ || params->input_queue_empty == 0) {
+ block =
+ list_entry(params->input_queue.next,
+ struct dma_block, queue);
+ dma_request.src_addr =
+ (dma_addr_t) block->dma_paddr;
+ dma_request.dst_addr =
+ (ASRC_BASE_ADDR + ASRC_ASRDIA_REG +
+ (params->index << 3));
+ dma_request.num_of_bytes = block->length;
+ mxc_dma_config(params->
+ input_dma_channel,
+ &dma_request, 1,
+ MXC_DMA_MODE_WRITE);
+ params->input_queue_empty++;
+ list_del(params->input_queue.next);
+ list_add_tail(&block->queue,
+ &params->input_done_queue);
+ }
+
+ spin_unlock_irqrestore(&input_int_lock, lock_flags);
+ break;
+ }
+ case ASRC_DQ_INBUF:{
+ struct asrc_buffer buf;
+ struct dma_block *block;
+ unsigned long lock_flags;
+ if (copy_from_user
+ (&buf, (void __user *)arg,
+ sizeof(struct asrc_buffer))) {
+ err = -EFAULT;
+ break;
+ }
+ /* if ASRC is inactive, nonsense to DQ buffer */
+ if (params->asrc_active == 0) {
+ err = -EFAULT;
+ buf.buf_valid = ASRC_BUF_NA;
+ if (copy_to_user
+ ((void __user *)arg, &buf,
+ sizeof(struct asrc_buffer)))
+ err = -EFAULT;
+ break;
+ }
+
+ if (!wait_event_interruptible_timeout
+ (params->input_wait_queue,
+ params->input_counter != 0, 10 * HZ)) {
+ pr_info
+ ("ASRC_DQ_INBUF timeout counter %x\n",
+ params->input_counter);
+ err = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ pr_info("ASRC_DQ_INBUF interrupt received\n");
+ err = -ERESTARTSYS;
+ break;
+ }
+ spin_lock_irqsave(&input_int_lock, lock_flags);
+ params->input_counter--;
+ block =
+ list_entry(params->input_done_queue.next,
+ struct dma_block, queue);
+ list_del(params->input_done_queue.next);
+ spin_unlock_irqrestore(&input_int_lock, lock_flags);
+ buf.index = block->index;
+ buf.length = block->length;
+ buf.buf_valid = ASRC_BUF_AV;
+ if (copy_to_user
+ ((void __user *)arg, &buf,
+ sizeof(struct asrc_buffer)))
+ err = -EFAULT;
+
+ break;
+ }
+ case ASRC_Q_OUTBUF:{
+ struct asrc_buffer buf;
+ struct dma_block *block;
+ mxc_dma_requestbuf_t dma_request;
+ unsigned long lock_flags;
+ if (copy_from_user
+ (&buf, (void __user *)arg,
+ sizeof(struct asrc_buffer))) {
+ err = -EFAULT;
+ break;
+ }
+ spin_lock_irqsave(&output_int_lock, lock_flags);
+ params->output_dma[buf.index].index = buf.index;
+ params->output_dma[buf.index].length = buf.length;
+ list_add_tail(&params->output_dma[buf.index].
+ queue, &params->output_queue);
+ if (params->asrc_active == 0
+ || params->output_queue_empty == 0) {
+ block =
+ list_entry(params->output_queue.
+ next, struct dma_block, queue);
+ dma_request.src_addr =
+ (ASRC_BASE_ADDR + ASRC_ASRDOA_REG +
+ (params->index << 3));
+ dma_request.dst_addr =
+ (dma_addr_t) block->dma_paddr;
+ dma_request.num_of_bytes = block->length;
+ mxc_dma_config(params->
+ output_dma_channel,
+ &dma_request, 1,
+ MXC_DMA_MODE_READ);
+ list_del(params->output_queue.next);
+ list_add_tail(&block->queue,
+ &params->output_done_queue);
+ params->output_queue_empty++;
+ }
+
+ spin_unlock_irqrestore(&output_int_lock, lock_flags);
+ break;
+ }
+ case ASRC_DQ_OUTBUF:{
+ struct asrc_buffer buf;
+ struct dma_block *block;
+ unsigned long lock_flags;
+ if (copy_from_user
+ (&buf, (void __user *)arg,
+ sizeof(struct asrc_buffer))) {
+ err = -EFAULT;
+ break;
+ }
+ /* if ASRC is inactive, nonsense to DQ buffer */
+ if (params->asrc_active == 0) {
+ buf.buf_valid = ASRC_BUF_NA;
+ err = -EFAULT;
+ if (copy_to_user
+ ((void __user *)arg, &buf,
+ sizeof(struct asrc_buffer)))
+ err = -EFAULT;
+ break;
+ }
+
+ if (!wait_event_interruptible_timeout
+ (params->output_wait_queue,
+ params->output_counter != 0, 10 * HZ)) {
+ pr_info
+ ("ASRC_DQ_OUTBUF timeout counter %x\n",
+ params->output_counter);
+ err = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ pr_info("ASRC_DQ_INBUF interrupt received\n");
+ err = -ERESTARTSYS;
+ break;
+ }
+ spin_lock_irqsave(&output_int_lock, lock_flags);
+ params->output_counter--;
+ block =
+ list_entry(params->output_done_queue.next,
+ struct dma_block, queue);
+ list_del(params->output_done_queue.next);
+ spin_unlock_irqrestore(&output_int_lock, lock_flags);
+ buf.index = block->index;
+ buf.length = block->length;
+ buf.buf_valid = ASRC_BUF_AV;
+ if (copy_to_user
+ ((void __user *)arg, &buf,
+ sizeof(struct asrc_buffer)))
+ err = -EFAULT;
+
+ break;
+ }
+ case ASRC_START_CONV:{
+ enum asrc_pair_index index;
+ unsigned long lock_flags;
+ if (copy_from_user
+ (&index, (void __user *)arg,
+ sizeof(enum asrc_pair_index))) {
+ err = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&input_int_lock, lock_flags);
+ if (params->input_queue_empty == 0) {
+ err = -EFAULT;
+ pr_info
+ ("ASRC_START_CONV - no block available\n");
+ break;
+ }
+ spin_unlock_irqrestore(&input_int_lock, lock_flags);
+ params->asrc_active = 1;
+
+ asrc_start_conv(index);
+ mxc_dma_enable(params->input_dma_channel);
+ mxc_dma_enable(params->output_dma_channel);
+ break;
+ }
+ case ASRC_STOP_CONV:{
+ enum asrc_pair_index index;
+ if (copy_from_user
+ (&index, (void __user *)arg,
+ sizeof(enum asrc_pair_index))) {
+ err = -EFAULT;
+ break;
+ }
+ mxc_dma_disable(params->input_dma_channel);
+ mxc_dma_disable(params->output_dma_channel);
+ asrc_stop_conv(index);
+ params->asrc_active = 0;
+ break;
+ }
+ case ASRC_STATUS:{
+ struct asrc_status_flags flags;
+ if (copy_from_user
+ (&flags, (void __user *)arg,
+ sizeof(struct asrc_status_flags))) {
+ err = -EFAULT;
+ break;
+ }
+ asrc_get_status(&flags);
+ if (copy_to_user
+ ((void __user *)arg, &flags,
+ sizeof(struct asrc_status_flags)))
+ err = -EFAULT;
+ break;
+ }
+ case ASRC_FLUSH:{
+ /* flush input dma buffer */
+ unsigned long lock_flags;
+ mxc_dma_device_t rx_id, tx_id;
+ char *rx_name, *tx_name;
+ int channel = -1;
+ spin_lock_irqsave(&input_int_lock, lock_flags);
+ while (!list_empty(&params->input_queue))
+ list_del(params->input_queue.next);
+ while (!list_empty(&params->input_done_queue))
+ list_del(params->input_done_queue.next);
+ params->input_counter = 0;
+ params->input_queue_empty = 0;
+ spin_unlock_irqrestore(&input_int_lock, lock_flags);
+
+ /* flush output dma buffer */
+ spin_lock_irqsave(&output_int_lock, lock_flags);
+ while (!list_empty(&params->output_queue))
+ list_del(params->output_queue.next);
+ while (!list_empty(&params->output_done_queue))
+ list_del(params->output_done_queue.next);
+ params->output_counter = 0;
+ params->output_queue_empty = 0;
+ spin_unlock_irqrestore(&output_int_lock, lock_flags);
+
+ /* release DMA and request again */
+ mxc_dma_free(params->input_dma_channel);
+ mxc_dma_free(params->output_dma_channel);
+ if (params->index == ASRC_PAIR_A) {
+ rx_id = MXC_DMA_ASRC_A_RX;
+ tx_id = MXC_DMA_ASRC_A_TX;
+ rx_name = asrc_pair_id[0];
+ tx_name = asrc_pair_id[1];
+ } else if (params->index == ASRC_PAIR_B) {
+ rx_id = MXC_DMA_ASRC_B_RX;
+ tx_id = MXC_DMA_ASRC_B_TX;
+ rx_name = asrc_pair_id[2];
+ tx_name = asrc_pair_id[3];
+ } else {
+ rx_id = MXC_DMA_ASRC_C_RX;
+ tx_id = MXC_DMA_ASRC_C_TX;
+ rx_name = asrc_pair_id[4];
+ tx_name = asrc_pair_id[5];
+ }
+ channel = mxc_dma_request(rx_id, rx_name);
+ params->input_dma_channel = channel;
+ err = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+ asrc_input_dma_callback,
+ (void *)params);
+ channel = mxc_dma_request(tx_id, tx_name);
+ params->output_dma_channel = channel;
+ err = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+ asrc_output_dma_callback,
+ (void *)params);
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ up(&params->busy_lock);
+ return err;
+}
+
+/*!
+ * asrc interface - open function
+ *
+ * @param inode structure inode *
+ *
+ * @param file structure file *
+ *
+ * @return status 0 success, ENODEV invalid device instance,
+ * ENOBUFS failed to allocate buffer, ERESTARTSYS interrupted by user
+ */
+static int mxc_asrc_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+ struct asrc_pair_params *pair_params;
+ if (signal_pending(current))
+ return -EINTR;
+ pair_params = kzalloc(sizeof(struct asrc_pair_params), GFP_KERNEL);
+ if (pair_params == NULL) {
+ pr_debug("Failed to allocate pair_params\n");
+ err = -ENOBUFS;
+ }
+
+ init_MUTEX(&pair_params->busy_lock);
+ file->private_data = pair_params;
+ return err;
+}
+
+/*!
+ * asrc interface - close function
+ *
+ * @param inode struct inode *
+ * @param file structure file *
+ *
+ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_asrc_close(struct inode *inode, struct file *file)
+{
+ struct asrc_pair_params *pair_params;
+ pair_params = file->private_data;
+ if (pair_params->asrc_active == 1) {
+ mxc_dma_disable(pair_params->input_dma_channel);
+ mxc_dma_disable(pair_params->output_dma_channel);
+ asrc_stop_conv(pair_params->index);
+ wake_up_interruptible(&pair_params->input_wait_queue);
+ wake_up_interruptible(&pair_params->output_wait_queue);
+ }
+ if (pair_params->pair_hold == 1) {
+ mxc_dma_free(pair_params->input_dma_channel);
+ mxc_dma_free(pair_params->output_dma_channel);
+ mxc_free_dma_buf(pair_params);
+ asrc_release_pair(pair_params->index);
+ }
+ kfree(pair_params);
+ file->private_data = NULL;
+ return 0;
+}
+
+/*!
+ * asrc interface - mmap function
+ *
+ * @param file structure file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return status 0 Success, EINTR busy lock error, ENOBUFS remap_page error
+ */
+static int mxc_asrc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long size;
+ int res = 0;
+ size = vma->vm_end - vma->vm_start;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot))
+ return -ENOBUFS;
+
+ vma->vm_flags &= ~VM_IO;
+ return res;
+}
+
+static struct file_operations asrc_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = asrc_ioctl,
+ .mmap = mxc_asrc_mmap,
+ .open = mxc_asrc_open,
+ .release = mxc_asrc_close,
+};
+
+static int asrc_read_proc_attr(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ unsigned long reg;
+ int len = 0;
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+
+ len += sprintf(page, "ANCA: %d\n",
+ (int)(reg &
+ (0xFFFFFFFF >>
+ (32 - mxc_asrc_data->channel_bits))));
+ len +=
+ sprintf(page + len, "ANCB: %d\n",
+ (int)((reg >> mxc_asrc_data->
+ channel_bits) & (0xFFFFFFFF >> (32 -
+ mxc_asrc_data->
+ channel_bits))));
+ len +=
+ sprintf(page + len, "ANCC: %d\n",
+ (int)((reg >> (mxc_asrc_data->channel_bits * 2)) &
+ (0xFFFFFFFF >> (32 - mxc_asrc_data->channel_bits))));
+
+ if (off > len)
+ return 0;
+
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+
+ return min(count, len - (int)off);
+}
+
+static int asrc_write_proc_attr(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ char buf[50];
+ unsigned long reg;
+ int na, nb, nc;
+ int total;
+ if (count > 48)
+ return -EINVAL;
+ if (copy_from_user(buf, buffer, count)) {
+ pr_debug("Attr proc write, Failed to copy buffer from user\n");
+ return -EFAULT;
+ }
+
+ reg = __raw_readl(asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+ sscanf(buf, "ANCA: %d\nANCB: %d\nANCC: %d", &na, &nb, &nc);
+ if (mxc_asrc_data->channel_bits > 3)
+ total = 10;
+ else
+ total = 5;
+ if ((na + nb + nc) != total) {
+ pr_info("Wrong ASRCNR settings\n");
+ return -EFAULT;
+ }
+ reg = na | (nb << mxc_asrc_data->
+ channel_bits) | (nc << (mxc_asrc_data->channel_bits * 2));
+
+ __raw_writel(reg, asrc_vrt_base_addr + ASRC_ASRCNCR_REG);
+
+ return count;
+}
+
+static void asrc_proc_create(void)
+{
+ struct proc_dir_entry *proc_attr;
+ proc_asrc = proc_mkdir(ASRC_PROC_PATH, NULL);
+ if (proc_asrc) {
+ proc_attr = create_proc_entry("ChSettings",
+ S_IFREG | S_IRUGO |
+ S_IWUSR, proc_asrc);
+ if (proc_attr) {
+ proc_attr->read_proc = asrc_read_proc_attr;
+ proc_attr->write_proc = asrc_write_proc_attr;
+ proc_attr->size = 48;
+ proc_attr->uid = proc_attr->gid = 0;
+ proc_attr->owner = THIS_MODULE;
+ } else {
+ remove_proc_entry(ASRC_PROC_PATH, NULL);
+ pr_info("Failed to create proc attribute entry \n");
+ }
+ } else {
+ pr_info("ASRC: Failed to create proc entry %s\n",
+ ASRC_PROC_PATH);
+ }
+}
+
+/*!
+ * Entry point for the asrc device
+ *
+ * @param pdev Pionter to the registered platform device
+ * @return Error code indicating success or failure
+ */
+static int mxc_asrc_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct resource *res;
+ struct device *temp_class;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
+
+ g_asrc_data = kzalloc(sizeof(struct asrc_data), GFP_KERNEL);
+
+ if (g_asrc_data == NULL) {
+ pr_info("Failed to allocate g_asrc_data\n");
+ return -ENOMEM;
+ }
+
+ g_asrc_data->asrc_pair[0].chn_max = 2;
+ g_asrc_data->asrc_pair[1].chn_max = 2;
+ g_asrc_data->asrc_pair[2].chn_max = 6;
+ g_asrc_data->asrc_pair[0].overload_error = 0;
+ g_asrc_data->asrc_pair[1].overload_error = 0;
+ g_asrc_data->asrc_pair[2].overload_error = 0;
+
+ asrc_major = register_chrdev(asrc_major, "mxc_asrc", &asrc_fops);
+ if (asrc_major < 0) {
+ pr_info("Unable to register asrc device\n");
+ err = -EBUSY;
+ goto error;
+ }
+
+ asrc_class = class_create(THIS_MODULE, "mxc_asrc");
+ if (IS_ERR(asrc_class)) {
+ err = PTR_ERR(asrc_class);
+ goto err_out_chrdev;
+ }
+
+ temp_class = device_create(asrc_class, NULL, MKDEV(asrc_major, 0),
+ NULL, "mxc_asrc");
+ if (IS_ERR(temp_class)) {
+ err = PTR_ERR(temp_class);
+ goto err_out_class;
+ }
+
+ asrc_vrt_base_addr =
+ (unsigned long)ioremap(res->start, res->end - res->start + 1);
+
+ mxc_asrc_data =
+ (struct mxc_asrc_platform_data *)pdev->dev.platform_data;
+ clk_enable(mxc_asrc_data->asrc_core_clk);
+
+ asrc_proc_create();
+ err = mxc_init_asrc();
+ if (err < 0)
+ goto err_out_class;
+
+ goto out;
+
+ err_out_class:
+ clk_disable(mxc_asrc_data->asrc_core_clk);
+ device_destroy(asrc_class, MKDEV(asrc_major, 0));
+ class_destroy(asrc_class);
+ err_out_chrdev:
+ unregister_chrdev(asrc_major, "mxc_asrc");
+ error:
+ kfree(g_asrc_data);
+ out:
+ pr_info("mxc_asrc registered\n");
+ return err;
+}
+
+/*!
+ * Exit asrc device
+ *
+ * @param pdev Pionter to the registered platform device
+ * @return Error code indicating success or failure
+ */
+static int mxc_asrc_remove(struct platform_device *pdev)
+{
+ free_irq(MXC_INT_ASRC, NULL);
+ kfree(g_asrc_data);
+ clk_disable(mxc_asrc_data->asrc_core_clk);
+ mxc_asrc_data = NULL;
+ iounmap((unsigned long __iomem *)asrc_vrt_base_addr);
+ remove_proc_entry("ChSettings", proc_asrc);
+ remove_proc_entry(ASRC_PROC_PATH, NULL);
+ device_destroy(asrc_class, MKDEV(asrc_major, 0));
+ class_destroy(asrc_class);
+ unregister_chrdev(asrc_major, "mxc_asrc");
+ return 0;
+}
+
+/*! mxc asrc driver definition
+ *
+ */
+static struct platform_driver mxc_asrc_driver = {
+ .driver = {
+ .name = "mxc_asrc",
+ },
+ .probe = mxc_asrc_probe,
+ .remove = mxc_asrc_remove,
+};
+
+/*!
+ * Register asrc driver
+ *
+ */
+static __init int asrc_init(void)
+{
+ int ret;
+ ret = platform_driver_register(&mxc_asrc_driver);
+ return ret;
+}
+
+/*!
+ * Exit and free the asrc data
+ *
+ */ static void __exit asrc_exit(void)
+{
+ platform_driver_unregister(&mxc_asrc_driver);
+ return;
+}
+
+module_init(asrc_init);
+module_exit(asrc_exit);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Asynchronous Sample Rate Converter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/bt/Kconfig b/drivers/mxc/bt/Kconfig
new file mode 100644
index 000000000000..9dbfbe57a14f
--- /dev/null
+++ b/drivers/mxc/bt/Kconfig
@@ -0,0 +1,13 @@
+#
+# Bluetooth configuration
+#
+
+menu "MXC Bluetooth support"
+
+config MXC_BLUETOOTH
+ tristate "MXC Bluetooth support"
+ depends on MACH_MX31_3DS || MACH_MX35_3DS || MACH_MX37_3DS || MACH_MX51_3DS
+ ---help---
+ Say Y to get the third party Bluetooth service.
+
+endmenu
diff --git a/drivers/mxc/bt/Makefile b/drivers/mxc/bt/Makefile
new file mode 100644
index 000000000000..91bc4cff380e
--- /dev/null
+++ b/drivers/mxc/bt/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the kernel Bluetooth power-on/reset
+#
+obj-$(CONFIG_MXC_BLUETOOTH) += mxc_bt.o
diff --git a/drivers/mxc/bt/mxc_bt.c b/drivers/mxc/bt/mxc_bt.c
new file mode 100644
index 000000000000..6da165ad09dc
--- /dev/null
+++ b/drivers/mxc/bt/mxc_bt.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_bt.c
+ *
+ * @brief MXC Thirty party Bluetooth
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/regulator/consumer.h>
+
+static struct regulator *bt_vdd;
+static struct regulator *bt_vdd_parent;
+static struct regulator *bt_vusb;
+static struct regulator *bt_vusb_parent;
+
+/*!
+ * This function poweron the bluetooth hardware module
+ *
+ * @param pdev Pointer to the platform device
+ * @return 0 on success, -1 otherwise.
+ */
+static int mxc_bt_probe(struct platform_device *pdev)
+{
+ struct mxc_bt_platform_data *platform_data;
+ platform_data = (struct mxc_bt_platform_data *)pdev->dev.platform_data;
+ if (platform_data->bt_vdd) {
+ bt_vdd = regulator_get(&pdev->dev, platform_data->bt_vdd);
+ regulator_enable(bt_vdd);
+ }
+ if (platform_data->bt_vdd_parent) {
+ bt_vdd_parent =
+ regulator_get(&pdev->dev, platform_data->bt_vdd_parent);
+ regulator_enable(bt_vdd_parent);
+ }
+ if (platform_data->bt_vusb) {
+ bt_vusb = regulator_get(&pdev->dev, platform_data->bt_vusb);
+ regulator_enable(bt_vusb);
+ }
+ if (platform_data->bt_vusb_parent) {
+ bt_vusb_parent =
+ regulator_get(&pdev->dev, platform_data->bt_vusb_parent);
+ regulator_enable(bt_vusb_parent);
+ }
+
+ if (platform_data->bt_reset != NULL)
+ platform_data->bt_reset();
+ return 0;
+
+}
+
+/*!
+ * This function poweroff the bluetooth hardware module
+ *
+ * @param pdev Pointer to the platform device
+ * @return 0 on success, -1 otherwise.
+ */
+static int mxc_bt_remove(struct platform_device *pdev)
+{
+ struct mxc_bt_platform_data *platform_data;
+ platform_data = (struct mxc_bt_platform_data *)pdev->dev.platform_data;
+ if (bt_vdd) {
+ regulator_disable(bt_vdd);
+ regulator_put(bt_vdd);
+ }
+ if (bt_vdd_parent) {
+ regulator_disable(bt_vdd_parent);
+ regulator_put(bt_vdd_parent);
+ }
+ if (bt_vusb) {
+ regulator_disable(bt_vusb);
+ regulator_put(bt_vusb);
+ }
+ if (bt_vusb_parent) {
+ regulator_disable(bt_vusb_parent);
+ regulator_put(bt_vusb_parent);
+ }
+ return 0;
+
+}
+
+static struct platform_driver bluetooth_driver = {
+ .driver = {
+ .name = "mxc_bt",
+ },
+ .probe = mxc_bt_probe,
+ .remove = mxc_bt_remove,
+};
+
+/*!
+ * Register bluetooth driver module
+ *
+ */
+static __init int bluetooth_init(void)
+{
+ return platform_driver_register(&bluetooth_driver);
+}
+
+/*!
+ * Exit and free the bluetooth module
+ *
+ */
+static void __exit bluetooth_exit(void)
+{
+ platform_driver_unregister(&bluetooth_driver);
+}
+
+module_init(bluetooth_init);
+module_exit(bluetooth_exit);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Thirty party Bluetooth");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/dam/Kconfig b/drivers/mxc/dam/Kconfig
new file mode 100644
index 000000000000..7b4bee92f648
--- /dev/null
+++ b/drivers/mxc/dam/Kconfig
@@ -0,0 +1,13 @@
+#
+# DAM API configuration
+#
+
+menu "MXC Digital Audio Multiplexer support"
+
+config MXC_DAM
+ tristate "DAM support"
+ depends on ARCH_MXC
+ ---help---
+ Say Y to get the Digital Audio Multiplexer services API available on MXC platform.
+
+endmenu
diff --git a/drivers/mxc/dam/Makefile b/drivers/mxc/dam/Makefile
new file mode 100644
index 000000000000..b5afdc143dfa
--- /dev/null
+++ b/drivers/mxc/dam/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the kernel Digital Audio MUX (DAM) device driver.
+#
+
+ifeq ($(CONFIG_ARCH_MX27),y)
+ obj-$(CONFIG_MXC_DAM) += dam_v1.o
+else
+ obj-$(CONFIG_MXC_DAM) += dam.o
+endif
diff --git a/drivers/mxc/dam/dam.c b/drivers/mxc/dam/dam.c
new file mode 100644
index 000000000000..532b02f65bb2
--- /dev/null
+++ b/drivers/mxc/dam/dam.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file dam.c
+ * @brief This is the brief documentation for this dam.c file.
+ *
+ * This file contains the implementation of the DAM driver main services
+ *
+ * @ingroup DAM
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include "dam.h"
+
+/*!
+ * This include to define bool type, false and true definitions.
+ */
+#include <mach/hardware.h>
+
+#define ModifyRegister32(a,b,c) (c = ( ( (c)&(~(a)) ) | (b) ))
+
+#define DAM_VIRT_BASE_ADDR IO_ADDRESS(AUDMUX_BASE_ADDR)
+
+#ifndef _reg_DAM_PTCR1
+#define _reg_DAM_PTCR1 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x00)))
+#endif
+
+#ifndef _reg_DAM_PDCR1
+#define _reg_DAM_PDCR1 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x04)))
+#endif
+
+#ifndef _reg_DAM_PTCR2
+#define _reg_DAM_PTCR2 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x08)))
+#endif
+
+#ifndef _reg_DAM_PDCR2
+#define _reg_DAM_PDCR2 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x0C)))
+#endif
+
+#ifndef _reg_DAM_PTCR3
+#define _reg_DAM_PTCR3 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x10)))
+#endif
+
+#ifndef _reg_DAM_PDCR3
+#define _reg_DAM_PDCR3 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x14)))
+#endif
+
+#ifndef _reg_DAM_PTCR4
+#define _reg_DAM_PTCR4 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x18)))
+#endif
+
+#ifndef _reg_DAM_PDCR4
+#define _reg_DAM_PDCR4 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x1C)))
+#endif
+
+#ifndef _reg_DAM_PTCR5
+#define _reg_DAM_PTCR5 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x20)))
+#endif
+
+#ifndef _reg_DAM_PDCR5
+#define _reg_DAM_PDCR5 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x24)))
+#endif
+
+#ifndef _reg_DAM_PTCR6
+#define _reg_DAM_PTCR6 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x28)))
+#endif
+
+#ifndef _reg_DAM_PDCR6
+#define _reg_DAM_PDCR6 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x2C)))
+#endif
+
+#ifndef _reg_DAM_PTCR7
+#define _reg_DAM_PTCR7 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x30)))
+#endif
+
+#ifndef _reg_DAM_PDCR7
+#define _reg_DAM_PDCR7 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x34)))
+#endif
+
+#ifndef _reg_DAM_CNMCR
+#define _reg_DAM_CNMCR (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x38)))
+#endif
+
+#ifndef _reg_DAM_PTCR
+#define _reg_DAM_PTCR(a) (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + a*8)))
+#endif
+
+#ifndef _reg_DAM_PDCR
+#define _reg_DAM_PDCR(a) (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 4 + a*8)))
+#endif
+
+/*!
+ * PTCR Registers bit shift definitions
+ */
+#define dam_synchronous_mode_shift 11
+#define dam_receive_clock_select_shift 12
+#define dam_receive_clock_direction_shift 16
+#define dam_receive_frame_sync_select_shift 17
+#define dam_receive_frame_sync_direction_shift 21
+#define dam_transmit_clock_select_shift 22
+#define dam_transmit_clock_direction_shift 26
+#define dam_transmit_frame_sync_select_shift 27
+#define dam_transmit_frame_sync_direction_shift 31
+#define dam_selection_mask 0xF
+
+/*!
+ * HPDCR Register bit shift definitions
+ */
+#define dam_internal_network_mode_shift 0
+#define dam_mode_shift 8
+#define dam_transmit_receive_switch_shift 12
+#define dam_receive_data_select_shift 13
+
+/*!
+ * HPDCR Register bit masq definitions
+ */
+#define dam_mode_masq 0x03
+#define dam_internal_network_mode_mask 0xFF
+
+/*!
+ * CNMCR Register bit shift definitions
+ */
+#define dam_ce_bus_port_cntlow_shift 0
+#define dam_ce_bus_port_cnthigh_shift 8
+#define dam_ce_bus_port_clkpol_shift 16
+#define dam_ce_bus_port_fspol_shift 17
+#define dam_ce_bus_port_enable_shift 18
+
+#define DAM_NAME "dam"
+
+EXPORT_SYMBOL(dam_select_mode);
+EXPORT_SYMBOL(dam_select_RxClk_direction);
+EXPORT_SYMBOL(dam_select_RxClk_source);
+EXPORT_SYMBOL(dam_select_RxD_source);
+EXPORT_SYMBOL(dam_select_RxFS_direction);
+EXPORT_SYMBOL(dam_select_RxFS_source);
+EXPORT_SYMBOL(dam_select_TxClk_direction);
+EXPORT_SYMBOL(dam_select_TxClk_source);
+EXPORT_SYMBOL(dam_select_TxFS_direction);
+EXPORT_SYMBOL(dam_select_TxFS_source);
+EXPORT_SYMBOL(dam_set_internal_network_mode_mask);
+EXPORT_SYMBOL(dam_set_synchronous);
+EXPORT_SYMBOL(dam_switch_Tx_Rx);
+EXPORT_SYMBOL(dam_reset_register);
+
+/*!
+ * This function selects the operation mode of the port.
+ *
+ * @param port the DAM port to configure
+ * @param the_mode the operation mode of the port
+ *
+ * @return This function returns the result of the operation
+ * (0 if successful, -1 otherwise).
+ */
+int dam_select_mode(dam_port port, dam_mode the_mode)
+{
+ int result;
+ result = 0;
+
+ ModifyRegister32(dam_mode_masq << dam_mode_shift,
+ the_mode << dam_mode_shift, _reg_DAM_PDCR(port));
+
+ return result;
+}
+
+/*!
+ * This function controls Receive clock signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Rx clock signal direction
+ */
+void dam_select_RxClk_direction(dam_port port, signal_direction direction)
+{
+ ModifyRegister32(1 << dam_receive_clock_direction_shift,
+ direction << dam_receive_clock_direction_shift,
+ _reg_DAM_PTCR(port));
+}
+
+/*!
+ * This function controls Receive clock signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxClk the signal comes from RxClk or TxClk of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_RxClk_source(dam_port p_config,
+ bool from_RxClk, dam_port p_source)
+{
+ ModifyRegister32(dam_selection_mask << dam_receive_clock_select_shift,
+ ((from_RxClk << 3) | p_source) <<
+ dam_receive_clock_select_shift,
+ _reg_DAM_PTCR(p_config));
+}
+
+/*!
+ * This function selects the source port for the RxD data.
+ *
+ * @param p_config the DAM port to configure
+ * @param p_source the source port
+ */
+void dam_select_RxD_source(dam_port p_config, dam_port p_source)
+{
+ ModifyRegister32(dam_selection_mask << dam_receive_data_select_shift,
+ p_source << dam_receive_data_select_shift,
+ _reg_DAM_PDCR(p_config));
+}
+
+/*!
+ * This function controls Receive Frame Sync signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Rx Frame Sync signal direction
+ */
+void dam_select_RxFS_direction(dam_port port, signal_direction direction)
+{
+ ModifyRegister32(1 << dam_receive_frame_sync_direction_shift,
+ direction << dam_receive_frame_sync_direction_shift,
+ _reg_DAM_PTCR(port));
+}
+
+/*!
+ * This function controls Receive Frame Sync signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxFS the signal comes from RxFS or TxFS of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_RxFS_source(dam_port p_config,
+ bool from_RxFS, dam_port p_source)
+{
+ ModifyRegister32(dam_selection_mask <<
+ dam_receive_frame_sync_select_shift,
+ ((from_RxFS << 3) | p_source) <<
+ dam_receive_frame_sync_select_shift,
+ _reg_DAM_PTCR(p_config));
+}
+
+/*!
+ * This function controls Transmit clock signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Tx clock signal direction
+ */
+void dam_select_TxClk_direction(dam_port port, signal_direction direction)
+{
+ ModifyRegister32(1 << dam_transmit_clock_direction_shift,
+ direction << dam_transmit_clock_direction_shift,
+ _reg_DAM_PTCR(port));
+}
+
+/*!
+ * This function controls Transmit clock signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxClk the signal comes from RxClk or TxClk of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_TxClk_source(dam_port p_config,
+ bool from_RxClk, dam_port p_source)
+{
+ ModifyRegister32(dam_selection_mask << dam_transmit_clock_select_shift,
+ ((from_RxClk << 3) | p_source) <<
+ dam_transmit_clock_select_shift,
+ _reg_DAM_PTCR(p_config));
+}
+
+/*!
+ * This function controls Transmit Frame Sync signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Tx Frame Sync signal direction
+ */
+void dam_select_TxFS_direction(dam_port port, signal_direction direction)
+{
+ ModifyRegister32(1 << dam_transmit_frame_sync_direction_shift,
+ direction << dam_transmit_frame_sync_direction_shift,
+ _reg_DAM_PTCR(port));
+}
+
+/*!
+ * This function controls Transmit Frame Sync signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxFS the signal comes from RxFS or TxFS of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_TxFS_source(dam_port p_config,
+ bool from_RxFS, dam_port p_source)
+{
+ ModifyRegister32(dam_selection_mask <<
+ dam_transmit_frame_sync_select_shift,
+ ((from_RxFS << 3) | p_source) <<
+ dam_transmit_frame_sync_select_shift,
+ _reg_DAM_PTCR(p_config));
+}
+
+/*!
+ * This function sets a bit mask that selects the port from which of the RxD
+ * signals are to be ANDed together for internal network mode.
+ * Bit 6 represents RxD from Port7 and bit0 represents RxD from Port1.
+ * 1 excludes RxDn from ANDing. 0 includes RxDn for ANDing.
+ *
+ * @param port the DAM port to configure
+ * @param bit_mask the bit mask
+ *
+ * @return This function returns the result of the operation
+ * (0 if successful, -1 otherwise).
+ */
+int dam_set_internal_network_mode_mask(dam_port port, unsigned char bit_mask)
+{
+ int result;
+ result = 0;
+
+ ModifyRegister32(dam_internal_network_mode_mask <<
+ dam_internal_network_mode_shift,
+ bit_mask << dam_internal_network_mode_shift,
+ _reg_DAM_PDCR(port));
+
+ return result;
+}
+
+/*!
+ * This function controls whether or not the port is in synchronous mode.
+ * When the synchronous mode is selected, the receive and the transmit sections
+ * use common clock and frame sync signals.
+ * When the synchronous mode is not selected, separate clock and frame sync
+ * signals are used for the transmit and the receive sections.
+ * The defaut value is the synchronous mode selected.
+ *
+ * @param port the DAM port to configure
+ * @param synchronous the state to assign
+ */
+void dam_set_synchronous(dam_port port, bool synchronous)
+{
+ ModifyRegister32(1 << dam_synchronous_mode_shift,
+ synchronous << dam_synchronous_mode_shift,
+ _reg_DAM_PTCR(port));
+}
+
+/*!
+ * This function swaps the transmit and receive signals from (Da-TxD, Db-RxD)
+ * to (Da-RxD, Db-TxD).
+ * This default signal configuration is Da-TxD, Db-RxD.
+ *
+ * @param port the DAM port to configure
+ * @param value the switch state
+ */
+void dam_switch_Tx_Rx(dam_port port, bool value)
+{
+ ModifyRegister32(1 << dam_transmit_receive_switch_shift,
+ value << dam_transmit_receive_switch_shift,
+ _reg_DAM_PDCR(port));
+}
+
+/*!
+ * This function resets the two registers of the selected port.
+ *
+ * @param port the DAM port to reset
+ */
+void dam_reset_register(dam_port port)
+{
+ ModifyRegister32(0xFFFFFFFF, 0x00000000, _reg_DAM_PTCR(port));
+ ModifyRegister32(0xFFFFFFFF, 0x00000000, _reg_DAM_PDCR(port));
+}
+
+/*!
+ * This function implements the init function of the DAM device.
+ * This function is called when the module is loaded.
+ *
+ * @return This function returns 0.
+ */
+static int __init dam_init(void)
+{
+ return 0;
+}
+
+/*!
+ * This function implements the exit function of the SPI device.
+ * This function is called when the module is unloaded.
+ *
+ */
+static void __exit dam_exit(void)
+{
+}
+
+module_init(dam_init);
+module_exit(dam_exit);
+
+MODULE_DESCRIPTION("DAM char device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/dam/dam.h b/drivers/mxc/dam/dam.h
new file mode 100644
index 000000000000..cb9ead53f689
--- /dev/null
+++ b/drivers/mxc/dam/dam.h
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @defgroup DAM Digital Audio Multiplexer (AUDMUX) Driver
+ */
+
+ /*!
+ * @file dam.h
+ * @brief This is the brief documentation for this dam.h file.
+ *
+ * This header file contains DAM driver functions prototypes.
+ *
+ * @ingroup DAM
+ */
+
+#ifndef __MXC_DAM_H__
+#define __MXC_DAM_H__
+
+/*!
+ * This enumeration describes the Digital Audio Multiplexer mode.
+ */
+typedef enum {
+
+ /*!
+ * Normal mode
+ */
+ normal_mode = 0,
+
+ /*!
+ * Internal network mode
+ */
+ internal_network_mode = 1,
+
+ /*!
+ * CE bus network mode
+ */
+ CE_bus_network_mode = 2
+} dam_mode;
+
+/*!
+ * This enumeration describes the port.
+ */
+typedef enum {
+
+ /*!
+ * The port 1
+ */
+ port_1 = 0,
+
+ /*!
+ * The port 2
+ */
+ port_2 = 1,
+
+ /*!
+ * The port 3
+ */
+ port_3 = 2,
+
+ /*!
+ * The port 4
+ */
+ port_4 = 3,
+
+ /*!
+ * The port 5
+ */
+ port_5 = 4,
+
+ /*!
+ * The port 6
+ */
+ port_6 = 5,
+
+ /*!
+ * The port 7
+ */
+ port_7 = 6
+} dam_port;
+
+/*!
+ * This enumeration describes the signal direction.
+ */
+typedef enum {
+
+ /*!
+ * Signal In
+ */
+ signal_in = 0,
+
+ /*!
+ * Signal Out
+ */
+ signal_out = 1
+} signal_direction;
+
+/*!
+ * Test purpose definition
+ */
+#define TEST_DAM 1
+
+#ifdef TEST_DAM
+
+#define DAM_IOCTL 0x55
+#define DAM_CONFIG_SSI1_MC13783 _IOWR(DAM_IOCTL, 1, int)
+#define DAM_CONFIG_SSI2_MC13783 _IOWR(DAM_IOCTL, 2, int)
+#define DAM_CONFIG_SSI_NETWORK_MODE_MC13783 _IOWR(DAM_IOCTL, 3, int)
+#endif
+
+/*!
+ * This function selects the operation mode of the port.
+ *
+ * @param port the DAM port to configure
+ * @param the_mode the operation mode of the port
+ * @return This function returns the result of the operation
+ * (0 if successful, -1 otherwise).
+ */
+int dam_select_mode(dam_port port, dam_mode the_mode);
+
+/*!
+ * This function controls Receive clock signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Rx clock signal direction
+ */
+void dam_select_RxClk_direction(dam_port port, signal_direction direction);
+
+/*!
+ * This function controls Receive clock signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxClk the signal comes from RxClk or TxClk of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_RxClk_source(dam_port p_config, bool from_RxClk,
+ dam_port p_source);
+
+/*!
+ * This function selects the source port for the RxD data.
+ *
+ * @param p_config the DAM port to configure
+ * @param p_source the source port
+ */
+void dam_select_RxD_source(dam_port p_config, dam_port p_source);
+
+/*!
+ * This function controls Receive Frame Sync signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Rx Frame Sync signal direction
+ */
+void dam_select_RxFS_direction(dam_port port, signal_direction direction);
+
+/*!
+ * This function controls Receive Frame Sync signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxFS the signal comes from RxFS or TxFS of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_RxFS_source(dam_port p_config, bool from_RxFS,
+ dam_port p_source);
+
+/*!
+ * This function controls Transmit clock signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Tx clock signal direction
+ */
+void dam_select_TxClk_direction(dam_port port, signal_direction direction);
+
+/*!
+ * This function controls Transmit clock signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxClk the signal comes from RxClk or TxClk of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_TxClk_source(dam_port p_config, bool from_RxClk,
+ dam_port p_source);
+
+/*!
+ * This function controls Transmit Frame Sync signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Tx Frame Sync signal direction
+ */
+void dam_select_TxFS_direction(dam_port port, signal_direction direction);
+
+/*!
+ * This function controls Transmit Frame Sync signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxFS the signal comes from RxFS or TxFS of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_TxFS_source(dam_port p_config, bool from_RxFS,
+ dam_port p_source);
+
+/*!
+ * This function sets a bit mask that selects the port from which of
+ * the RxD signals are to be ANDed together for internal network mode.
+ * Bit 6 represents RxD from Port7 and bit0 represents RxD from Port1.
+ * 1 excludes RxDn from ANDing. 0 includes RxDn for ANDing.
+ *
+ * @param port the DAM port to configure
+ * @param bit_mask the bit mask
+ * @return This function returns the result of the operation
+ * (0 if successful, -1 otherwise).
+ */
+int dam_set_internal_network_mode_mask(dam_port port, unsigned char bit_mask);
+
+/*!
+ * This function controls whether or not the port is in synchronous mode.
+ * When the synchronous mode is selected, the receive and the transmit sections
+ * use common clock and frame sync signals.
+ * When the synchronous mode is not selected, separate clock and frame sync
+ * signals are used for the transmit and the receive sections.
+ * The defaut value is the synchronous mode selected.
+ *
+ * @param port the DAM port to configure
+ * @param synchronous the state to assign
+ */
+void dam_set_synchronous(dam_port port, bool synchronous);
+
+/*!
+ * This function swaps the transmit and receive signals from (Da-TxD, Db-RxD) to
+ * (Da-RxD, Db-TxD).
+ * This default signal configuration is Da-TxD, Db-RxD.
+ *
+ * @param port the DAM port to configure
+ * @param value the switch state
+ */
+void dam_switch_Tx_Rx(dam_port port, bool value);
+
+/*!
+ * This function resets the two registers of the selected port.
+ *
+ * @param port the DAM port to reset
+ */
+void dam_reset_register(dam_port port);
+
+#endif
diff --git a/drivers/mxc/dam/dam_v1.c b/drivers/mxc/dam/dam_v1.c
new file mode 100644
index 000000000000..0fc88bb98a38
--- /dev/null
+++ b/drivers/mxc/dam/dam_v1.c
@@ -0,0 +1,617 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file dam_v1.c
+ * @brief This is the brief documentation for this dam_v1.c file.
+ *
+ * This file contains the implementation of the DAM driver main services
+ *
+ * @ingroup DAM
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <asm/uaccess.h>
+#include "dam.h"
+
+/*!
+ * This include to define bool type, false and true definitions.
+ */
+#include <mach/hardware.h>
+
+#define DAM_VIRT_BASE_ADDR IO_ADDRESS(AUDMUX_BASE_ADDR)
+
+#define ModifyRegister32(a,b,c) do{\
+ __raw_writel( ((__raw_readl(c)) & (~(a))) | (b),(c) );\
+}while(0)
+
+#ifndef _reg_DAM_HPCR1
+#define _reg_DAM_HPCR1 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x00)))
+#endif
+
+#ifndef _reg_DAM_HPCR2
+#define _reg_DAM_HPCR2 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x04)))
+#endif
+
+#ifndef _reg_DAM_HPCR3
+#define _reg_DAM_HPCR3 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x08)))
+#endif
+
+#ifndef _reg_DAM_PPCR1
+#define _reg_DAM_PPCR1 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x10)))
+#endif
+
+#ifndef _reg_DAM_PPCR2
+#define _reg_DAM_PPCR2 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x14)))
+#endif
+
+#ifndef _reg_DAM_PPCR3
+#define _reg_DAM_PPCR3 (*((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x1c)))
+#endif
+
+#ifndef _reg_DAM_HPCR
+#define _reg_DAM_HPCR(a) ((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + (a)*4))
+#endif
+
+#ifndef _reg_DAM_PPCR
+#define _reg_DAM_PPCR(a) ((volatile unsigned long *) \
+ (DAM_VIRT_BASE_ADDR + 0x0c + (0x04 << (a-3)) ))
+#endif
+
+/*!
+ * HPCR/PPCR Registers bit shift definitions
+ */
+#define dam_transmit_frame_sync_direction_shift 31
+#define dam_transmit_clock_direction_shift 30
+#define dam_transmit_frame_sync_select_shift 26
+#define dam_transmit_clock_select_shift 26
+#define dam_receive_frame_sync_direction_shift 25
+#define dam_receive_clock_direction_shift 24
+#define dam_receive_clock_select_shift 20
+#define dam_receive_frame_sync_select_shift 20
+
+#define dam_receive_data_select_shift 13
+#define dam_synchronous_mode_shift 12
+
+#define dam_transmit_receive_switch_shift 10
+
+#define dam_mode_shift 8
+#define dam_internal_network_mode_shift 0
+
+/*!
+ * HPCR/PPCR Register bit masq definitions
+ */
+//#define dam_selection_mask 0xF
+#define dam_fs_selection_mask 0xF
+#define dam_clk_selection_mask 0xF
+#define dam_dat_selection_mask 0x7
+//#define dam_mode_masq 0x03
+#define dam_internal_network_mode_mask 0xFF
+
+/*!
+ * HPCR/PPCR Register reset value definitions
+ */
+#define dam_hpcr_default_value 0x00001000
+#define dam_ppcr_default_value 0x00001000
+
+#define DAM_NAME "dam"
+static struct class *mxc_dam_class;
+
+EXPORT_SYMBOL(dam_select_mode);
+EXPORT_SYMBOL(dam_select_RxClk_direction);
+EXPORT_SYMBOL(dam_select_RxClk_source);
+EXPORT_SYMBOL(dam_select_RxD_source);
+EXPORT_SYMBOL(dam_select_RxFS_direction);
+EXPORT_SYMBOL(dam_select_RxFS_source);
+EXPORT_SYMBOL(dam_select_TxClk_direction);
+EXPORT_SYMBOL(dam_select_TxClk_source);
+EXPORT_SYMBOL(dam_select_TxFS_direction);
+EXPORT_SYMBOL(dam_select_TxFS_source);
+EXPORT_SYMBOL(dam_set_internal_network_mode_mask);
+EXPORT_SYMBOL(dam_set_synchronous);
+EXPORT_SYMBOL(dam_switch_Tx_Rx);
+EXPORT_SYMBOL(dam_reset_register);
+
+/*!
+ * DAM major
+ */
+#ifdef TEST_DAM
+static int major_dam;
+
+typedef struct _mxc_cfg {
+ int reg;
+ int val;
+} mxc_cfg;
+
+#endif
+
+/*!
+ * This function selects the operation mode of the port.
+ *
+ * @param port the DAM port to configure
+ * @param the_mode the operation mode of the port
+ *
+ * @return This function returns the result of the operation
+ * (0 if successful, -1 otherwise).
+ */
+int dam_select_mode(dam_port port, dam_mode the_mode)
+{
+ int result;
+ result = 0;
+
+ if (port >= 3)
+ the_mode = normal_mode;
+ ModifyRegister32(1 << dam_mode_shift,
+ the_mode << dam_mode_shift, _reg_DAM_HPCR(port));
+
+ return result;
+}
+
+/*!
+ * This function controls Receive clock signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Rx clock signal direction
+ */
+void dam_select_RxClk_direction(dam_port port, signal_direction direction)
+{
+ if (port < 3) {
+ ModifyRegister32(1 << dam_receive_clock_direction_shift,
+ direction << dam_receive_clock_direction_shift,
+ _reg_DAM_HPCR(port));
+ } else {
+ ModifyRegister32(1 << dam_receive_clock_direction_shift,
+ direction << dam_receive_clock_direction_shift,
+ _reg_DAM_PPCR(port));
+ }
+ return;
+}
+
+/*!
+ * This function controls Receive clock signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxClk the signal comes from RxClk or TxClk of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_RxClk_source(dam_port p_config,
+ bool from_RxClk, dam_port p_source)
+{
+ if (p_config < 3) {
+ ModifyRegister32(dam_clk_selection_mask <<
+ dam_receive_clock_select_shift,
+ ((from_RxClk << 3) | p_source) <<
+ dam_receive_clock_select_shift,
+ _reg_DAM_HPCR(p_config));
+ } else {
+ ModifyRegister32(dam_clk_selection_mask <<
+ dam_receive_clock_select_shift,
+ ((from_RxClk << 3) | p_source) <<
+ dam_receive_clock_select_shift,
+ _reg_DAM_PPCR(p_config));
+ }
+ return;
+}
+
+/*!
+ * This function selects the source port for the RxD data.
+ *
+ * @param p_config the DAM port to configure
+ * @param p_source the source port
+ */
+void dam_select_RxD_source(dam_port p_config, dam_port p_source)
+{
+ if (p_config < 3) {
+ ModifyRegister32(dam_dat_selection_mask <<
+ dam_receive_data_select_shift,
+ p_source << dam_receive_data_select_shift,
+ _reg_DAM_HPCR(p_config));
+ } else {
+ ModifyRegister32(dam_dat_selection_mask <<
+ dam_receive_data_select_shift,
+ p_source << dam_receive_data_select_shift,
+ _reg_DAM_PPCR(p_config));
+ }
+ return;
+}
+
+/*!
+ * This function controls Receive Frame Sync signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Rx Frame Sync signal direction
+ */
+void dam_select_RxFS_direction(dam_port port, signal_direction direction)
+{
+ if (port < 3) {
+ ModifyRegister32(1 << dam_receive_frame_sync_direction_shift,
+ direction <<
+ dam_receive_frame_sync_direction_shift,
+ _reg_DAM_HPCR(port));
+ } else {
+ ModifyRegister32(1 << dam_receive_frame_sync_direction_shift,
+ direction <<
+ dam_receive_frame_sync_direction_shift,
+ _reg_DAM_PPCR(port));
+ }
+ return;
+}
+
+/*!
+ * This function controls Receive Frame Sync signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxFS the signal comes from RxFS or TxFS of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_RxFS_source(dam_port p_config,
+ bool from_RxFS, dam_port p_source)
+{
+ if (p_config < 3) {
+ ModifyRegister32(dam_fs_selection_mask <<
+ dam_receive_frame_sync_select_shift,
+ ((from_RxFS << 3) | p_source) <<
+ dam_receive_frame_sync_select_shift,
+ _reg_DAM_HPCR(p_config));
+ } else {
+ ModifyRegister32(dam_fs_selection_mask <<
+ dam_receive_frame_sync_select_shift,
+ ((from_RxFS << 3) | p_source) <<
+ dam_receive_frame_sync_select_shift,
+ _reg_DAM_PPCR(p_config));
+ }
+ return;
+}
+
+/*!
+ * This function controls Transmit clock signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Tx clock signal direction
+ */
+void dam_select_TxClk_direction(dam_port port, signal_direction direction)
+{
+ if (port < 3) {
+ ModifyRegister32(1 << dam_transmit_clock_direction_shift,
+ direction <<
+ dam_transmit_clock_direction_shift,
+ _reg_DAM_HPCR(port));
+ } else {
+ ModifyRegister32(1 << dam_transmit_clock_direction_shift,
+ direction <<
+ dam_transmit_clock_direction_shift,
+ _reg_DAM_PPCR(port));
+ }
+ return;
+}
+
+/*!
+ * This function controls Transmit clock signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxClk the signal comes from RxClk or TxClk of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_TxClk_source(dam_port p_config,
+ bool from_RxClk, dam_port p_source)
+{
+ if (p_config < 3) {
+ ModifyRegister32(dam_clk_selection_mask <<
+ dam_transmit_clock_select_shift,
+ ((from_RxClk << 3) | p_source) <<
+ dam_transmit_clock_select_shift,
+ _reg_DAM_HPCR(p_config));
+ } else {
+ ModifyRegister32(dam_clk_selection_mask <<
+ dam_transmit_clock_select_shift,
+ ((from_RxClk << 3) | p_source) <<
+ dam_transmit_clock_select_shift,
+ _reg_DAM_PPCR(p_config));
+ }
+ return;
+}
+
+/*!
+ * This function controls Transmit Frame Sync signal direction for the port.
+ *
+ * @param port the DAM port to configure
+ * @param direction the Tx Frame Sync signal direction
+ */
+void dam_select_TxFS_direction(dam_port port, signal_direction direction)
+{
+ if (port < 3) {
+ ModifyRegister32(1 << dam_transmit_frame_sync_direction_shift,
+ direction <<
+ dam_transmit_frame_sync_direction_shift,
+ _reg_DAM_HPCR(port));
+ } else {
+ ModifyRegister32(1 << dam_transmit_frame_sync_direction_shift,
+ direction <<
+ dam_transmit_frame_sync_direction_shift,
+ _reg_DAM_HPCR(port));
+ }
+ return;
+}
+
+/*!
+ * This function controls Transmit Frame Sync signal source for the port.
+ *
+ * @param p_config the DAM port to configure
+ * @param from_RxFS the signal comes from RxFS or TxFS of
+ * the source port
+ * @param p_source the source port
+ */
+void dam_select_TxFS_source(dam_port p_config,
+ bool from_RxFS, dam_port p_source)
+{
+ if (p_config < 3) {
+ ModifyRegister32(dam_fs_selection_mask <<
+ dam_transmit_frame_sync_select_shift,
+ ((from_RxFS << 3) | p_source) <<
+ dam_transmit_frame_sync_select_shift,
+ _reg_DAM_HPCR(p_config));
+ } else {
+ ModifyRegister32(dam_fs_selection_mask <<
+ dam_transmit_frame_sync_select_shift,
+ ((from_RxFS << 3) | p_source) <<
+ dam_transmit_frame_sync_select_shift,
+ _reg_DAM_PPCR(p_config));
+ }
+ return;
+}
+
+/*!
+ * This function sets a bit mask that selects the port from which of the RxD
+ * signals are to be ANDed together for internal network mode.
+ * Bit 6 represents RxD from Port7 and bit0 represents RxD from Port1.
+ * 1 excludes RxDn from ANDing. 0 includes RxDn for ANDing.
+ *
+ * @param port the DAM port to configure
+ * @param bit_mask the bit mask
+ *
+ * @return This function returns the result of the operation
+ * (0 if successful, -1 otherwise).
+ */
+int dam_set_internal_network_mode_mask(dam_port port, unsigned char bit_mask)
+{
+ int result;
+ result = 0;
+
+ ModifyRegister32(dam_internal_network_mode_mask <<
+ dam_internal_network_mode_shift,
+ bit_mask << dam_internal_network_mode_shift,
+ _reg_DAM_HPCR(port));
+ return result;
+}
+
+/*!
+ * This function controls whether or not the port is in synchronous mode.
+ * When the synchronous mode is selected, the receive and the transmit sections
+ * use common clock and frame sync signals.
+ * When the synchronous mode is not selected, separate clock and frame sync
+ * signals are used for the transmit and the receive sections.
+ * The defaut value is the synchronous mode selected.
+ *
+ * @param port the DAM port to configure
+ * @param synchronous the state to assign
+ */
+void dam_set_synchronous(dam_port port, bool synchronous)
+{
+ if (port < 3) {
+ ModifyRegister32(1 << dam_synchronous_mode_shift,
+ synchronous << dam_synchronous_mode_shift,
+ _reg_DAM_HPCR(port));
+ } else {
+ ModifyRegister32(1 << dam_synchronous_mode_shift,
+ synchronous << dam_synchronous_mode_shift,
+ _reg_DAM_PPCR(port));
+ }
+ return;
+}
+
+/*!
+ * This function swaps the transmit and receive signals from (Da-TxD, Db-RxD)
+ * to (Da-RxD, Db-TxD).
+ * This default signal configuration is Da-TxD, Db-RxD.
+ *
+ * @param port the DAM port to configure
+ * @param value the switch state
+ */
+void dam_switch_Tx_Rx(dam_port port, bool value)
+{
+ if (port < 3) {
+ ModifyRegister32(1 << dam_transmit_receive_switch_shift,
+ value << dam_transmit_receive_switch_shift,
+ _reg_DAM_HPCR(port));
+ } else {
+ ModifyRegister32(1 << dam_transmit_receive_switch_shift,
+ value << dam_transmit_receive_switch_shift,
+ _reg_DAM_PPCR(port));
+ }
+ return;
+}
+
+/*!
+ * This function resets the two registers of the selected port.
+ *
+ * @param port the DAM port to reset
+ */
+void dam_reset_register(dam_port port)
+{
+ if (port < 3) {
+ ModifyRegister32(0xFFFFFFFF, dam_hpcr_default_value,
+ _reg_DAM_HPCR(port));
+ } else {
+ ModifyRegister32(0xFFFFFFFF, dam_ppcr_default_value,
+ _reg_DAM_PPCR(port));
+ }
+ return;
+}
+
+#ifdef TEST_DAM
+
+/*!
+ * This function implements IOCTL controls on a DAM device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @param cmd the command
+ * @param arg the parameter :\n
+ * DAM_CONFIG_SSI1:\n
+ * data from port 1 to port 4, clock and FS from port 1 (SSI1)\n
+ * DAM_CONFIG_SSI2:\n
+ * data from port 2 to port 5, clock and FS from port 2 (SSI2)\n
+ * DAM_CONFIG_SSI_NETWORK_MODE:\n
+ * network mode for mix digital with data from port 1 to port4,\n
+ * data from port 2 to port 4, clock and FS from port 1 (SSI1)
+ *
+ * @return This function returns 0 if successful.
+ */
+static int dam_ioctl(struct inode *inode,
+ struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+
+/*!
+ * This function implements the open method on a DAM device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ *
+ * @return This function returns 0.
+ */
+static int dam_open(struct inode *inode, struct file *file)
+{
+ /* DBG_PRINTK("ssi : dam_open()\n"); */
+ return 0;
+}
+
+/*!
+ * This function implements the release method on a DAM device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ *
+ * @return This function returns 0.
+ */
+static int dam_free(struct inode *inode, struct file *file)
+{
+ /* DBG_PRINTK("ssi : dam_free()\n"); */
+ return 0;
+}
+
+/*!
+ * This structure defines file operations for a DAM device.
+ */
+static struct file_operations dam_fops = {
+
+ /*!
+ * the owner
+ */
+ .owner = THIS_MODULE,
+
+ /*!
+ * the ioctl operation
+ */
+ .ioctl = dam_ioctl,
+
+ /*!
+ * the open operation
+ */
+ .open = dam_open,
+
+ /*!
+ * the release operation
+ */
+ .release = dam_free,
+};
+
+#endif
+
+/*!
+ * This function implements the init function of the DAM device.
+ * This function is called when the module is loaded.
+ *
+ * @return This function returns 0.
+ */
+static int __init dam_init(void)
+{
+#ifdef TEST_DAM
+ struct device *temp_class;
+ printk(KERN_DEBUG "dam : dam_init(void) \n");
+
+ major_dam = register_chrdev(0, DAM_NAME, &dam_fops);
+ if (major_dam < 0) {
+ printk(KERN_WARNING "Unable to get a major for dam");
+ return major_dam;
+ }
+
+ mxc_dam_class = class_create(THIS_MODULE, DAM_NAME);
+ if (IS_ERR(mxc_dam_class)) {
+ goto err_out;
+ }
+
+ temp_class = device_create(mxc_dam_class, NULL,
+ MKDEV(major_dam, 0), NULL, DAM_NAME);
+ if (IS_ERR(temp_class)) {
+ goto err_out;
+ }
+#endif
+ return 0;
+
+ err_out:
+ printk(KERN_ERR "Error creating dam class device.\n");
+ device_destroy(mxc_dam_class, MKDEV(major_dam, 0));
+ class_destroy(mxc_dam_class);
+ unregister_chrdev(major_dam, DAM_NAME);
+ return -1;
+}
+
+/*!
+ * This function implements the exit function of the SPI device.
+ * This function is called when the module is unloaded.
+ *
+ */
+static void __exit dam_exit(void)
+{
+#ifdef TEST_DAM
+ device_destroy(mxc_dam_class, MKDEV(major_dam, 0));
+ class_destroy(mxc_dam_class);
+ unregister_chrdev(major_dam, DAM_NAME);
+ printk(KERN_DEBUG "dam : successfully unloaded\n");
+#endif
+}
+
+module_init(dam_init);
+module_exit(dam_exit);
+
+MODULE_DESCRIPTION("DAM char device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/gps_ioctrl/Kconfig b/drivers/mxc/gps_ioctrl/Kconfig
new file mode 100644
index 000000000000..0a85d1636dd7
--- /dev/null
+++ b/drivers/mxc/gps_ioctrl/Kconfig
@@ -0,0 +1,13 @@
+#
+# BROADCOM GPS configuration
+#
+
+menu "Broadcom GPS ioctrl support"
+
+config GPS_IOCTRL
+ tristate "GPS ioctrl support"
+ depends on MACH_MX31_3DS || MACH_MX35_3DS || MACH_MX37_3DS || MACH_MX51_3DS
+ ---help---
+ Say Y to enable Broadcom GPS ioctrl on MXC platform.
+
+endmenu
diff --git a/drivers/mxc/gps_ioctrl/Makefile b/drivers/mxc/gps_ioctrl/Makefile
new file mode 100644
index 000000000000..42a48fe3bd7b
--- /dev/null
+++ b/drivers/mxc/gps_ioctrl/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the GPIO device driver module.
+#
+obj-$(CONFIG_GPS_IOCTRL) += gps_gpiodrv.o
+gps_gpiodrv-objs := agpsgpiodev.o
diff --git a/drivers/mxc/gps_ioctrl/agpsgpiodev.c b/drivers/mxc/gps_ioctrl/agpsgpiodev.c
new file mode 100644
index 000000000000..cf2c2b4056bf
--- /dev/null
+++ b/drivers/mxc/gps_ioctrl/agpsgpiodev.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file agpsgpiodev.c
+ *
+ * @brief Main file for GPIO kernel module. Contains driver entry/exit
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h> /* Async notification */
+#include <asm/uaccess.h> /* for get_user, put_user, access_ok */
+#include <linux/sched.h> /* jiffies */
+#include <linux/poll.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include "agpsgpiodev.h"
+
+extern void gpio_gps_active(void);
+extern void gpio_gps_inactive(void);
+extern int gpio_gps_access(int para);
+
+struct mxc_gps_platform_data *mxc_gps_ioctrl_data;
+static int Device_Open; /* Only allow a single user of this device */
+static struct cdev mxc_gps_cdev;
+static dev_t agps_gpio_dev;
+static struct class *gps_class;
+static struct device *gps_class_dev;
+
+/* Write GPIO from user space */
+static int ioctl_writegpio(int arg)
+{
+
+ /* Bit 0 of arg identifies the GPIO pin to write:
+ 0 = GPS_RESET_GPIO, 1 = GPS_POWER_GPIO.
+ Bit 1 of arg identifies the value to write (0 or 1). */
+
+ /* Bit 2 should be 0 to show this access is write */
+ return gpio_gps_access(arg & (~0x4));
+}
+
+/* Read GPIO from user space */
+static int ioctl_readgpio(int arg)
+{
+ /* Bit 0 of arg identifies the GPIO pin to read:
+ 0 = GPS_RESET_GPIO. 1 = GPS_POWER_GPIO
+ Bit 2 should be 1 to show this access is read */
+ return gpio_gps_access(arg | 0x4);
+}
+
+static int device_open(struct inode *inode, struct file *fp)
+{
+ /* We don't want to talk to two processes at the same time. */
+ if (Device_Open) {
+ printk(KERN_DEBUG "device_open() - Returning EBUSY. \
+ Device already open... \n");
+ return -EBUSY;
+ }
+ Device_Open++; /* BUGBUG : Not protected! */
+ try_module_get(THIS_MODULE);
+
+ return 0;
+}
+
+static int device_release(struct inode *inode, struct file *fp)
+{
+ /* We're now ready for our next caller */
+ Device_Open--;
+ module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static int device_ioctl(struct inode *inode, struct file *fp,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+
+ /* Extract the type and number bitfields, and don't decode wrong cmds.
+ Return ENOTTY (inappropriate ioctl) before access_ok() */
+ if (_IOC_TYPE(cmd) != MAJOR_NUM) {
+ printk(KERN_ERR
+ "device_ioctl() - Error! IOC_TYPE = %d. Expected %d\n",
+ _IOC_TYPE(cmd), MAJOR_NUM);
+ return -ENOTTY;
+ }
+ if (_IOC_NR(cmd) > IOCTL_MAXNUMBER) {
+ printk(KERN_ERR
+ "device_ioctl() - Error!"
+ "IOC_NR = %d greater than max supported(%d)\n",
+ _IOC_NR(cmd), IOCTL_MAXNUMBER);
+ return -ENOTTY;
+ }
+
+ /* The direction is a bitmask, and VERIFY_WRITE catches R/W transfers.
+ `Type' is user-oriented, while access_ok is kernel-oriented, so the
+ concept of "read" and "write" is reversed. I think this is primarily
+ for good coding practice. You can easily do any kind of R/W access
+ without these checks and IOCTL code can be implemented "randomly"! */
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ err =
+ !access_ok(VERIFY_WRITE, (void __user *)arg,
+ _IOC_SIZE(cmd));
+
+ else if (_IOC_DIR(cmd) & _IOC_WRITE)
+ err =
+ !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
+ if (err) {
+ printk(KERN_ERR
+ "device_ioctl() - Error! User arg not valid"
+ "for selected access (R/W/RW). Cmd %d\n",
+ _IOC_TYPE(cmd));
+ return -EFAULT;
+ }
+
+ /* Note: Read and writing data to user buffer can be done using regular
+ pointer stuff but we may also use get_user() or put_user() */
+
+ /* Cmd and arg has been verified... */
+ switch (cmd) {
+ case IOCTL_WRITEGPIO:
+ return ioctl_writegpio((int)arg);
+ case IOCTL_READGPIO:
+ return ioctl_readgpio((int)arg);
+ default:
+ printk(KERN_ERR "device_ioctl() - Invalid IOCTL (0x%x)\n", cmd);
+ return EINVAL;
+ }
+ return 0;
+}
+
+struct file_operations Fops = {
+ .ioctl = device_ioctl,
+ .open = device_open,
+ .release = device_release,
+};
+
+/* Initialize the module - Register the character device */
+int init_chrdev(struct device *dev)
+{
+ int ret, gps_major;
+
+ ret = alloc_chrdev_region(&agps_gpio_dev, 1, 1, "agps_gpio");
+ gps_major = MAJOR(agps_gpio_dev);
+ if (ret < 0) {
+ dev_err(dev, "can't get major %d\n", gps_major);
+ goto err3;
+ }
+
+ cdev_init(&mxc_gps_cdev, &Fops);
+ mxc_gps_cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&mxc_gps_cdev, agps_gpio_dev, 1);
+ if (ret) {
+ dev_err(dev, "can't add cdev\n");
+ goto err2;
+ }
+
+ /* create class and device for udev information */
+ gps_class = class_create(THIS_MODULE, "gps");
+ if (IS_ERR(gps_class)) {
+ dev_err(dev, "failed to create gps class\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ gps_class_dev = device_create(gps_class, NULL, MKDEV(gps_major, 1), NULL,
+ AGPSGPIO_DEVICE_FILE_NAME);
+ if (IS_ERR(gps_class_dev)) {
+ dev_err(dev, "failed to create gps gpio class device\n");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ return 0;
+err0:
+ class_destroy(gps_class);
+err1:
+ cdev_del(&mxc_gps_cdev);
+err2:
+ unregister_chrdev_region(agps_gpio_dev, 1);
+err3:
+ return ret;
+}
+
+/* Cleanup - unregister the appropriate file from /proc. */
+void cleanup_chrdev(void)
+{
+ /* destroy gps device class */
+ device_destroy(gps_class, MKDEV(MAJOR(agps_gpio_dev), 1));
+ class_destroy(gps_class);
+
+ /* Unregister the device */
+ cdev_del(&mxc_gps_cdev);
+ unregister_chrdev_region(agps_gpio_dev, 1);
+}
+
+/*!
+ * This function initializes the driver in terms of memory of the soundcard
+ * and some basic HW clock settings.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int __init gps_ioctrl_probe(struct platform_device *pdev)
+{
+ struct regulator *gps_regu;
+
+ mxc_gps_ioctrl_data =
+ (struct mxc_gps_platform_data *)pdev->dev.platform_data;
+
+ /* open GPS GPO3 1v8 for GL gps support */
+ if (mxc_gps_ioctrl_data->core_reg != NULL) {
+ mxc_gps_ioctrl_data->gps_regu_core =
+ regulator_get(&(pdev->dev), mxc_gps_ioctrl_data->core_reg);
+ gps_regu = mxc_gps_ioctrl_data->gps_regu_core;
+ if (!IS_ERR_VALUE((u32)gps_regu)) {
+ regulator_set_voltage(gps_regu, 1800000, 1800000);
+ regulator_enable(gps_regu);
+ } else {
+ return -1;
+ }
+ }
+ /* open GPS GPO1 2v8 for GL gps support */
+ if (mxc_gps_ioctrl_data->analog_reg != NULL) {
+ mxc_gps_ioctrl_data->gps_regu_analog =
+ regulator_get(&(pdev->dev),
+ mxc_gps_ioctrl_data->analog_reg);
+ gps_regu = mxc_gps_ioctrl_data->gps_regu_analog;
+ if (!IS_ERR_VALUE((u32)gps_regu)) {
+ regulator_set_voltage(gps_regu, 2800000, 2800000);
+ regulator_enable(gps_regu);
+ } else {
+ return -1;
+ }
+ }
+ gpio_gps_active();
+
+ /* Register character device */
+ init_chrdev(&(pdev->dev));
+ return 0;
+}
+
+static int gps_ioctrl_remove(struct platform_device *pdev)
+{
+ struct regulator *gps_regu;
+
+ mxc_gps_ioctrl_data =
+ (struct mxc_gps_platform_data *)pdev->dev.platform_data;
+
+ /* Character device cleanup.. */
+ cleanup_chrdev();
+ gpio_gps_inactive();
+
+ /* close GPS GPO3 1v8 for GL gps */
+ gps_regu = mxc_gps_ioctrl_data->gps_regu_core;
+ if (mxc_gps_ioctrl_data->core_reg != NULL) {
+ regulator_disable(gps_regu);
+ regulator_put(gps_regu);
+ }
+ /* close GPS GPO1 2v8 for GL gps */
+ gps_regu = mxc_gps_ioctrl_data->gps_regu_analog;
+ if (mxc_gps_ioctrl_data->analog_reg != NULL) {
+ regulator_disable(gps_regu);
+ regulator_put(gps_regu);
+ }
+
+ return 0;
+}
+
+static int gps_ioctrl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ /* PowerEn toggle off */
+ ioctl_writegpio(0x1);
+ return 0;
+}
+
+static int gps_ioctrl_resume(struct platform_device *pdev)
+{
+ /* PowerEn pull up */
+ ioctl_writegpio(0x3);
+ return 0;
+}
+
+static struct platform_driver gps_ioctrl_driver = {
+ .probe = gps_ioctrl_probe,
+ .remove = gps_ioctrl_remove,
+ .suspend = gps_ioctrl_suspend,
+ .resume = gps_ioctrl_resume,
+ .driver = {
+ .name = "gps_ioctrl",
+ },
+};
+
+/*!
+ * Entry point for GPS ioctrl module.
+ *
+ */
+static int __init gps_ioctrl_init(void)
+{
+ return platform_driver_register(&gps_ioctrl_driver);
+}
+
+/*!
+ * unloading module.
+ *
+ */
+static void __exit gps_ioctrl_exit(void)
+{
+ platform_driver_unregister(&gps_ioctrl_driver);
+}
+
+module_init(gps_ioctrl_init);
+module_exit(gps_ioctrl_exit);
+MODULE_DESCRIPTION("GPIO DEVICE DRIVER");
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/gps_ioctrl/agpsgpiodev.h b/drivers/mxc/gps_ioctrl/agpsgpiodev.h
new file mode 100644
index 000000000000..815890578f4a
--- /dev/null
+++ b/drivers/mxc/gps_ioctrl/agpsgpiodev.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file agpsgpiodev.h
+ *
+ * @brief head file of Simple character device interface for AGPS kernel module.
+ *
+ * @ingroup
+ */
+
+#ifndef AGPSGPIODEV_H
+#define AGPSGPIODEV_H
+
+#include <linux/ioctl.h>
+
+#define USE_BLOCKING /* Test driver with blocking calls */
+#undef USE_FASYNC /* Test driver with async notification */
+
+/* The major device number. We can't rely on dynamic registration any more
+ because ioctls need to know it */
+#define MAJOR_NUM 100
+
+#define IOCTL_WRITEGPIO _IOWR(MAJOR_NUM, 1, char *)
+#define IOCTL_READGPIO _IOR(MAJOR_NUM, 2, char *)
+#define IOCTL_MAXNUMBER 2
+
+/* The name of the device file */
+#define AGPSGPIO_DEVICE_FILE_NAME "agpsgpio"
+
+/* Exported prototypes */
+int init_chrdev(struct device *dev);
+void cleanup_chrdev(void);
+void wakeup(void);
+
+#endif
diff --git a/drivers/mxc/hmp4e/Kconfig b/drivers/mxc/hmp4e/Kconfig
new file mode 100644
index 000000000000..fdd7dbc041ba
--- /dev/null
+++ b/drivers/mxc/hmp4e/Kconfig
@@ -0,0 +1,24 @@
+#
+# MPEG4 Encoder kernel module configuration
+#
+
+menu "MXC MPEG4 Encoder Kernel module support"
+
+config MXC_HMP4E
+ tristate "MPEG4 Encoder support"
+ depends on ARCH_MXC
+ depends on !ARCH_MX27
+ default y
+ ---help---
+ Say Y to get the MPEG4 Encoder kernel module available on
+ MXC platform.
+
+config MXC_HMP4E_DEBUG
+ bool "MXC MPEG4 Debug messages"
+ depends on MXC_HMP4E != n
+ default n
+ ---help---
+ Say Y here if you need the Encoder driver to print debug messages.
+ This is an option for developers, most people should say N here.
+
+endmenu
diff --git a/drivers/mxc/hmp4e/Makefile b/drivers/mxc/hmp4e/Makefile
new file mode 100644
index 000000000000..0efe11fbdbc8
--- /dev/null
+++ b/drivers/mxc/hmp4e/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the MPEG4 Encoder kernel module.
+
+obj-$(CONFIG_MXC_HMP4E) += mxc_hmp4e.o
+
+ifeq ($(CONFIG_MXC_HMP4E_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/mxc/hmp4e/mxc_hmp4e.c b/drivers/mxc/hmp4e/mxc_hmp4e.c
new file mode 100644
index 000000000000..f795378cdae0
--- /dev/null
+++ b/drivers/mxc/hmp4e/mxc_hmp4e.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * Encoder device driver (kernel module)
+ *
+ * Copyright (C) 2005 Hantro Products Oy.
+ *
+ * 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/module.h>
+#include <linux/init.h> /* __init,__exit directives */
+#include <linux/mm.h> /* remap_page_range / remap_pfn_range */
+#include <linux/fs.h> /* for struct file_operations */
+#include <linux/errno.h> /* standard error codes */
+#include <linux/platform_device.h> /* for device registeration for PM */
+#include <linux/delay.h> /* for msleep_interruptible */
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h> /* for dma_alloc_consistent */
+#include <linux/clk.h>
+#include <asm/uaccess.h> /* for ioctl __get_user, __put_user */
+#include "mxc_hmp4e.h" /* MPEG4 encoder specific */
+
+/* here's all the must remember stuff */
+typedef struct {
+ ulong buffaddr;
+ u32 buffsize;
+ ulong iobaseaddr;
+ u32 iosize;
+ volatile u32 *hwregs;
+ u32 irq;
+ u16 hwid_offset;
+ u16 intr_offset;
+ u16 busy_offset;
+ u16 type; /* Encoder type, CIF = 0, VGA = 1 */
+ u16 clk_gate;
+ u16 busy_val;
+ struct fasync_struct *async_queue;
+#ifdef CONFIG_PM
+ s32 suspend_state;
+ wait_queue_head_t power_queue;
+#endif
+} hmp4e_t;
+
+/* and this is our MAJOR; use 0 for dynamic allocation (recommended)*/
+static s32 hmp4e_major;
+
+static u32 hmp4e_phys;
+static struct class *hmp4e_class;
+static hmp4e_t hmp4e_data;
+
+/*! MPEG4 enc clock handle. */
+static struct clk *hmp4e_clk;
+
+/*
+ * avoid "enable_irq(x) unbalanced from ..."
+ * error messages from the kernel, since {ena,dis}able_irq()
+ * calls are stacked in kernel.
+ */
+static bool irq_enable = false;
+
+ulong base_port = MPEG4_ENC_BASE_ADDR;
+u32 irq = MXC_INT_MPEG4_ENC;
+
+module_param(base_port, long, 000);
+module_param(irq, int, 000);
+
+/*!
+ * These variables store the register values when HMP4E is in suspend mode.
+ */
+#ifdef CONFIG_PM
+u32 io_regs[64];
+#endif
+
+static s32 hmp4e_map_buffer(struct file *filp, struct vm_area_struct *vma);
+static s32 hmp4e_map_hwregs(struct file *filp, struct vm_area_struct *vma);
+static void hmp4e_reset(hmp4e_t * dev);
+irqreturn_t hmp4e_isr(s32 irq, void *dev_id);
+
+/*!
+ * This funtion is called to write h/w register.
+ *
+ * @param val value to be written into the register
+ * @param offset register offset
+ *
+ */
+static inline void hmp4e_write(u32 val, u32 offset)
+{
+ hmp4e_t *dev = &hmp4e_data;
+ __raw_writel(val, (dev->hwregs + offset));
+}
+
+/*!
+ * This funtion is called to read h/w register.
+ *
+ * @param offset register offset
+ *
+ * @return This function returns the value read from the register.
+ *
+ */
+static inline u32 hmp4e_read(u32 offset)
+{
+ hmp4e_t *dev = &hmp4e_data;
+ u32 val;
+
+ val = __raw_readl(dev->hwregs + offset);
+
+ return val;
+}
+
+/*!
+ * The device's mmap method. The VFS has kindly prepared the process's
+ * vm_area_struct for us, so we examine this to see what was requested.
+ *
+ * @param filp pointer to struct file
+ * @param vma pointer to struct vma_area_struct
+ *
+ * @return This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ s32 result;
+ ulong offset = vma->vm_pgoff << PAGE_SHIFT;
+
+ pr_debug("hmp4e_mmap: size = %lu off = 0x%08lx\n",
+ (unsigned long)(vma->vm_end - vma->vm_start), offset);
+
+ if (offset == 0) {
+ result = hmp4e_map_buffer(filp, vma);
+ } else if (offset == hmp4e_data.iobaseaddr) {
+ result = hmp4e_map_hwregs(filp, vma);
+ } else {
+ pr_debug("hmp4e: mmap invalid value\n");
+ result = -EINVAL;
+ }
+
+ return result;
+}
+
+/*!
+ * This funtion is called to handle ioctls.
+ *
+ * @param inode pointer to struct inode
+ * @param filp pointer to struct file
+ * @param cmd ioctl command
+ * @param arg user data
+ *
+ * @return This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_ioctl(struct inode *inode, struct file *filp,
+ u32 cmd, ulong arg)
+{
+ s32 err = 0, retval = 0;
+ ulong offset = 0;
+ hmp4e_t *dev = &hmp4e_data;
+ write_t bwrite;
+
+#ifdef CONFIG_PM
+ wait_event_interruptible(hmp4e_data.power_queue,
+ hmp4e_data.suspend_state == 0);
+#endif
+
+ /*
+ * extract the type and number bitfields, and don't decode
+ * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
+ */
+ if (_IOC_TYPE(cmd) != HMP4E_IOC_MAGIC) {
+ pr_debug("hmp4e: ioctl invalid magic\n");
+ return -ENOTTY;
+ }
+
+ if (_IOC_NR(cmd) > HMP4E_IOC_MAXNR) {
+ pr_debug("hmp4e: ioctl exceeds max ioctl\n");
+ return -ENOTTY;
+ }
+
+ /*
+ * the direction is a bitmask, and VERIFY_WRITE catches R/W
+ * transfers. `Type' is user-oriented, while
+ * access_ok is kernel-oriented, so the concept of "read" and
+ * "write" is reversed
+ */
+ if (_IOC_DIR(cmd) & _IOC_READ) {
+ err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
+ } else if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
+ }
+
+ if (err) {
+ pr_debug("hmp4e: ioctl invalid direction\n");
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case HMP4E_IOCHARDRESET:
+ break;
+
+ case HMP4E_IOCGBUFBUSADDRESS:
+ retval = __put_user((ulong) hmp4e_phys, (u32 *) arg);
+ break;
+
+ case HMP4E_IOCGBUFSIZE:
+ retval = __put_user(hmp4e_data.buffsize, (u32 *) arg);
+ break;
+
+ case HMP4E_IOCSREGWRITE:
+ if (dev->type != 1) { /* This ioctl only for VGA */
+ pr_debug("hmp4e: HMP4E_IOCSREGWRITE invalid\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ retval = __copy_from_user(&bwrite, (u32 *) arg,
+ sizeof(write_t));
+
+ if (bwrite.offset <= hmp4e_data.iosize - 4) {
+ hmp4e_write(bwrite.data, (bwrite.offset / 4));
+ } else {
+ pr_debug("hmp4e: HMP4E_IOCSREGWRITE failed\n");
+ retval = -EFAULT;
+ }
+ break;
+
+ case HMP4E_IOCXREGREAD:
+ if (dev->type != 1) { /* This ioctl only for VGA */
+ pr_debug("hmp4e: HMP4E_IOCSREGWRITE invalid\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ retval = __get_user(offset, (ulong *) arg);
+ if (offset <= hmp4e_data.iosize - 4) {
+ __put_user(hmp4e_read((offset / 4)), (ulong *) arg);
+ } else {
+ pr_debug("hmp4e: HMP4E_IOCXREGREAD failed\n");
+ retval = -EFAULT;
+ }
+ break;
+
+ case HMP4E_IOCGHWOFFSET:
+ __put_user(hmp4e_data.iobaseaddr, (ulong *) arg);
+ break;
+
+ case HMP4E_IOCGHWIOSIZE:
+ __put_user(hmp4e_data.iosize, (u32 *) arg);
+ break;
+
+ case HMP4E_IOC_CLI:
+ if (irq_enable == true) {
+ disable_irq(hmp4e_data.irq);
+ irq_enable = false;
+ }
+ break;
+
+ case HMP4E_IOC_STI:
+ if (irq_enable == false) {
+ enable_irq(hmp4e_data.irq);
+ irq_enable = true;
+ }
+ break;
+
+ default:
+ pr_debug("unknown case %x\n", cmd);
+ }
+
+ return retval;
+}
+
+/*!
+ * This funtion is called when the device is opened.
+ *
+ * @param inode pointer to struct inode
+ * @param filp pointer to struct file
+ *
+ * @return This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_open(struct inode *inode, struct file *filp)
+{
+ hmp4e_t *dev = &hmp4e_data;
+
+ filp->private_data = (void *)dev;
+
+ if (request_irq(dev->irq, hmp4e_isr, 0, "mxc_hmp4e", dev) != 0) {
+ pr_debug("hmp4e: request irq failed\n");
+ return -EBUSY;
+ }
+
+ if (irq_enable == false) {
+ irq_enable = true;
+ }
+ clk_enable(hmp4e_clk);
+ return 0;
+}
+
+static s32 hmp4e_fasync(s32 fd, struct file *filp, s32 mode)
+{
+ hmp4e_t *dev = (hmp4e_t *) filp->private_data;
+ return fasync_helper(fd, filp, mode, &dev->async_queue);
+}
+
+/*!
+ * This funtion is called when the device is closed.
+ *
+ * @param inode pointer to struct inode
+ * @param filp pointer to struct file
+ *
+ * @return This function returns 0.
+ *
+ */
+static s32 hmp4e_release(struct inode *inode, struct file *filp)
+{
+ hmp4e_t *dev = (hmp4e_t *) filp->private_data;
+
+ /* this is necessary if user process exited asynchronously */
+ if (irq_enable == true) {
+ disable_irq(dev->irq);
+ irq_enable = false;
+ }
+
+ /* reset hardware */
+ hmp4e_reset(&hmp4e_data);
+
+ /* free the encoder IRQ */
+ free_irq(dev->irq, (void *)dev);
+
+ /* remove this filp from the asynchronusly notified filp's */
+ hmp4e_fasync(-1, filp, 0);
+ clk_disable(hmp4e_clk);
+ return 0;
+}
+
+/* VFS methods */
+static struct file_operations hmp4e_fops = {
+ .owner = THIS_MODULE,
+ .open = hmp4e_open,
+ .release = hmp4e_release,
+ .ioctl = hmp4e_ioctl,
+ .mmap = hmp4e_mmap,
+ .fasync = hmp4e_fasync,
+};
+
+/*!
+ * This funtion allocates physical contigous memory.
+ *
+ * @param size size of memory to be allocated
+ *
+ * @return This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_alloc(u32 size)
+{
+ hmp4e_data.buffsize = PAGE_ALIGN(size);
+ hmp4e_data.buffaddr =
+ (ulong) dma_alloc_coherent(NULL, hmp4e_data.buffsize,
+ (dma_addr_t *) & hmp4e_phys,
+ GFP_DMA | GFP_KERNEL);
+
+ if (hmp4e_data.buffaddr == 0) {
+ printk(KERN_ERR "hmp4e: couldn't allocate data buffer\n");
+ return -ENOMEM;
+ }
+
+ memset((s8 *) hmp4e_data.buffaddr, 0, hmp4e_data.buffsize);
+ return 0;
+}
+
+/*!
+ * This funtion frees the DMAed memory.
+ */
+static void hmp4e_free(void)
+{
+ if (hmp4e_data.buffaddr != 0) {
+ dma_free_coherent(NULL, hmp4e_data.buffsize,
+ (void *)hmp4e_data.buffaddr, hmp4e_phys);
+ hmp4e_data.buffaddr = 0;
+ }
+}
+
+/*!
+ * This funtion maps the shared buffer in memory.
+ *
+ * @param filp pointer to struct file
+ * @param vma pointer to struct vm_area_struct
+ *
+ * @return This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_map_buffer(struct file *filp, struct vm_area_struct *vma)
+{
+ ulong phys;
+ ulong start = (u32) vma->vm_start;
+ ulong size = (u32) (vma->vm_end - vma->vm_start);
+
+ /* if userspace tries to mmap beyond end of our buffer, fail */
+ if (size > hmp4e_data.buffsize) {
+ pr_debug("hmp4e: hmp4e_map_buffer, invalid size\n");
+ return -EINVAL;
+ }
+
+ vma->vm_flags |= VM_RESERVED | VM_IO;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ phys = hmp4e_phys;
+
+ if (remap_pfn_range(vma, start, phys >> PAGE_SHIFT, size,
+ vma->vm_page_prot)) {
+ pr_debug("hmp4e: failed mmapping shared buffer\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/*!
+ * This funtion maps the h/w register space in memory.
+ *
+ * @param filp pointer to struct file
+ * @param vma pointer to struct vm_area_struct
+ *
+ * @return This function returns 0 if successful or -ve value on error.
+ *
+ */
+static s32 hmp4e_map_hwregs(struct file *filp, struct vm_area_struct *vma)
+{
+ ulong phys;
+ ulong start = (unsigned long)vma->vm_start;
+ ulong size = (unsigned long)(vma->vm_end - vma->vm_start);
+
+ /* if userspace tries to mmap beyond end of our buffer, fail */
+ if (size > PAGE_SIZE) {
+ pr_debug("hmp4e: hmp4e_map_hwregs, invalid size\n");
+ return -EINVAL;
+ }
+
+ vma->vm_flags |= VM_RESERVED | VM_IO;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* Remember this won't work for vmalloc()d memory ! */
+ phys = hmp4e_data.iobaseaddr;
+
+ if (remap_pfn_range(vma, start, phys >> PAGE_SHIFT, hmp4e_data.iosize,
+ vma->vm_page_prot)) {
+ pr_debug("hmp4e: failed mmapping HW registers\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is the interrupt service routine.
+ *
+ * @param irq the irq number
+ * @param dev_id driver data when ISR was regiatered
+ *
+ * @return The return value is IRQ_HANDLED.
+ *
+ */
+irqreturn_t hmp4e_isr(s32 irq, void *dev_id)
+{
+ hmp4e_t *dev = (hmp4e_t *) dev_id;
+ u32 offset = dev->intr_offset;
+
+ u32 irq_status = hmp4e_read(offset);
+
+ /* clear enc IRQ */
+ hmp4e_write(irq_status & (~0x01), offset);
+
+ if (dev->async_queue)
+ kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function is called to reset the encoder.
+ *
+ * @param dev pointer to struct hmp4e_data
+ *
+ */
+static void hmp4e_reset(hmp4e_t * dev)
+{
+ s32 i;
+
+ /* enable HCLK for register reset */
+ hmp4e_write(dev->clk_gate, 0);
+
+ /* Reset registers, except ECR0 (0x00) and ID (read-only) */
+ for (i = 1; i < (dev->iosize / 4); i += 1) {
+ if (i == dev->hwid_offset) /* ID is read only */
+ continue;
+
+ /* Only for CIF, not used */
+ if ((dev->type == 0) && (i == 14))
+ continue;
+
+ hmp4e_write(0, i);
+ }
+
+ /* disable HCLK */
+ hmp4e_write(0, 0);
+ return;
+}
+
+/*!
+ * This function is called during the driver binding process. This function
+ * does the hardware initialization.
+ *
+ * @param dev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions
+ *
+ * @return The function returns 0 if successful.
+ */
+static s32 hmp4e_probe(struct platform_device *pdev)
+{
+ s32 result;
+ u32 hwid;
+ struct device *temp_class;
+
+ hmp4e_data.iobaseaddr = base_port;
+ hmp4e_data.irq = irq;
+ hmp4e_data.buffaddr = 0;
+
+ /* map hw i/o registers into kernel space */
+ hmp4e_data.hwregs = (volatile void *)IO_ADDRESS(hmp4e_data.iobaseaddr);
+
+ hmp4e_clk = clk_get(&pdev->dev, "mpeg4_clk");
+ if (IS_ERR(hmp4e_clk)) {
+ printk(KERN_INFO "hmp4e: Unable to get clock\n");
+ return -EIO;
+ }
+
+ clk_enable(hmp4e_clk);
+
+ /* check hw id for encoder signature */
+ hwid = hmp4e_read(7);
+ if ((hwid & 0xffff) == 0x1882) { /* CIF first */
+ hmp4e_data.type = 0;
+ hmp4e_data.iosize = (16 * 4);
+ hmp4e_data.hwid_offset = 7;
+ hmp4e_data.intr_offset = 5;
+ hmp4e_data.clk_gate = (1 << 1);
+ hmp4e_data.buffsize = 512000;
+ hmp4e_data.busy_offset = 0;
+ hmp4e_data.busy_val = 1;
+ } else {
+ hwid = hmp4e_read((0x88 / 4));
+ if ((hwid & 0xffff0000) == 0x52510000) { /* VGA */
+ hmp4e_data.type = 1;
+ hmp4e_data.iosize = (35 * 4);
+ hmp4e_data.hwid_offset = (0x88 / 4);
+ hmp4e_data.intr_offset = (0x10 / 4);
+ hmp4e_data.clk_gate = (1 << 12);
+ hmp4e_data.buffsize = 1048576;
+ hmp4e_data.busy_offset = (0x10 / 4);
+ hmp4e_data.busy_val = (1 << 1);
+ } else {
+ printk(KERN_INFO "hmp4e: HW ID not found\n");
+ goto error1;
+ }
+ }
+
+ /* Reset hardware */
+ hmp4e_reset(&hmp4e_data);
+
+ /* allocate memory shared with ewl */
+ result = hmp4e_alloc(hmp4e_data.buffsize);
+ if (result < 0)
+ goto error1;
+
+ result = register_chrdev(hmp4e_major, "hmp4e", &hmp4e_fops);
+ if (result <= 0) {
+ pr_debug("hmp4e: unable to get major %d\n", hmp4e_major);
+ goto error2;
+ }
+
+ hmp4e_major = result;
+
+ hmp4e_class = class_create(THIS_MODULE, "hmp4e");
+ if (IS_ERR(hmp4e_class)) {
+ pr_debug("Error creating hmp4e class.\n");
+ goto error3;
+ }
+
+ temp_class = device_create(hmp4e_class, NULL, MKDEV(hmp4e_major, 0), NULL,
+ "hmp4e");
+ if (IS_ERR(temp_class)) {
+ pr_debug("Error creating hmp4e class device.\n");
+ goto error4;
+ }
+
+ platform_set_drvdata(pdev, &hmp4e_data);
+
+#ifdef CONFIG_PM
+ hmp4e_data.async_queue = NULL;
+ hmp4e_data.suspend_state = 0;
+ init_waitqueue_head(&hmp4e_data.power_queue);
+#endif
+
+ printk(KERN_INFO "hmp4e: %s encoder initialized\n",
+ hmp4e_data.type ? "VGA" : "CIF");
+ clk_disable(hmp4e_clk);
+ return 0;
+
+ error4:
+ class_destroy(hmp4e_class);
+ error3:
+ unregister_chrdev(hmp4e_major, "hmp4e");
+ error2:
+ hmp4e_free();
+ error1:
+ clk_disable(hmp4e_clk);
+ clk_put(hmp4e_clk);
+ printk(KERN_INFO "hmp4e: module not inserted\n");
+ return -EIO;
+}
+
+/*!
+ * Dissociates the driver.
+ *
+ * @param dev the device structure
+ *
+ * @return The function always returns 0.
+ */
+static s32 hmp4e_remove(struct platform_device *pdev)
+{
+ device_destroy(hmp4e_class, MKDEV(hmp4e_major, 0));
+ class_destroy(hmp4e_class);
+ unregister_chrdev(hmp4e_major, "hmp4e");
+ hmp4e_free();
+ clk_disable(hmp4e_clk);
+ clk_put(hmp4e_clk);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This is the suspend of power management for the Hantro MPEG4 module
+ *
+ * @param dev the device
+ * @param state the state
+ *
+ * @return This function always returns 0.
+ */
+static s32 hmp4e_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ s32 i;
+ hmp4e_t *pdata = &hmp4e_data;
+
+ /*
+ * how many times msleep_interruptible will be called before
+ * giving up
+ */
+ s32 timeout = 10;
+
+ pr_debug("hmp4e: Suspend\n");
+ hmp4e_data.suspend_state = 1;
+
+ /* check if encoder is currently running */
+ while ((hmp4e_read(pdata->busy_offset) & (pdata->busy_val)) &&
+ --timeout) {
+ pr_debug("hmp4e: encoder is running, going to sleep\n");
+ msleep_interruptible((unsigned int)30);
+ }
+
+ if (!timeout) {
+ pr_debug("hmp4e: timeout suspending, resetting encoder\n");
+ hmp4e_write(hmp4e_read(pdata->busy_offset) &
+ (~pdata->busy_val), pdata->busy_offset);
+ }
+
+ /* first read register 0 */
+ io_regs[0] = hmp4e_read(0);
+
+ /* then override HCLK to make sure other registers can be read */
+ hmp4e_write(pdata->clk_gate, 0);
+
+ /* read other registers */
+ for (i = 1; i < (pdata->iosize / 4); i += 1) {
+
+ /* Only for CIF, not used */
+ if ((pdata->type == 0) && (i == 14))
+ continue;
+
+ io_regs[i] = hmp4e_read(i);
+ }
+
+ /* restore value of register 0 */
+ hmp4e_write(io_regs[0], 0);
+
+ /* stop HCLK */
+ hmp4e_write(0, 0);
+ clk_disable(hmp4e_clk);
+ return 0;
+};
+
+/*!
+ * This is the resume of power management for the Hantro MPEG4 module
+ * It suports RESTORE state.
+ *
+ * @param pdev the platform device
+ *
+ * @return This function always returns 0
+ */
+static s32 hmp4e_resume(struct platform_device *pdev)
+{
+ s32 i;
+ u32 status;
+ hmp4e_t *pdata = &hmp4e_data;
+
+ pr_debug("hmp4e: Resume\n");
+ clk_enable(hmp4e_clk);
+
+ /* override HCLK to make sure registers can be written */
+ hmp4e_write(pdata->clk_gate, 0x00);
+
+ for (i = 1; i < (pdata->iosize / 4); i += 1) {
+ if (i == pdata->hwid_offset) /* Read only */
+ continue;
+
+ /* Only for CIF, not used */
+ if ((pdata->type == 0) && (i == 14))
+ continue;
+
+ hmp4e_write(io_regs[i], i);
+ }
+
+ /* write register 0 last */
+ hmp4e_write(io_regs[0], 0x00);
+
+ /* Clear the suspend flag */
+ hmp4e_data.suspend_state = 0;
+
+ /* Unblock the wait queue */
+ wake_up_interruptible(&hmp4e_data.power_queue);
+
+ /* Continue operations */
+ status = hmp4e_read(pdata->intr_offset);
+ if (status & 0x1) {
+ hmp4e_write(status & (~0x01), pdata->intr_offset);
+ if (hmp4e_data.async_queue)
+ kill_fasync(&hmp4e_data.async_queue, SIGIO, POLL_IN);
+ }
+
+ return 0;
+};
+
+#endif
+
+static struct platform_driver hmp4e_driver = {
+ .driver = {
+ .name = "mxc_hmp4e",
+ },
+ .probe = hmp4e_probe,
+ .remove = hmp4e_remove,
+#ifdef CONFIG_PM
+ .suspend = hmp4e_suspend,
+ .resume = hmp4e_resume,
+#endif
+};
+
+static s32 __init hmp4e_init(void)
+{
+ printk(KERN_INFO "hmp4e: init\n");
+ platform_driver_register(&hmp4e_driver);
+ return 0;
+}
+
+static void __exit hmp4e_cleanup(void)
+{
+ platform_driver_unregister(&hmp4e_driver);
+ printk(KERN_INFO "hmp4e: module removed\n");
+}
+
+module_init(hmp4e_init);
+module_exit(hmp4e_cleanup);
+
+/* module description */
+MODULE_AUTHOR("Hantro Products Oy");
+MODULE_DESCRIPTION("Device driver for Hantro's hardware based MPEG4 encoder");
+MODULE_SUPPORTED_DEVICE("5251/4251 MPEG4 Encoder");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/hmp4e/mxc_hmp4e.h b/drivers/mxc/hmp4e/mxc_hmp4e.h
new file mode 100644
index 000000000000..f58831716346
--- /dev/null
+++ b/drivers/mxc/hmp4e/mxc_hmp4e.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * Encoder device driver (kernel module headers)
+ *
+ * Copyright (C) 2005 Hantro Products Oy.
+ *
+ * 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 _HMP4ENC_H_
+#define _HMP4ENC_H_
+#include <linux/ioctl.h> /* needed for the _IOW etc stuff used later */
+
+/* this is for writing data through ioctl to registers*/
+typedef struct {
+ unsigned long data;
+ unsigned long offset;
+} write_t;
+
+/*
+ * Ioctl definitions
+ */
+
+/* Use 'k' as magic number */
+#define HMP4E_IOC_MAGIC 'k'
+/*
+ * S means "Set" through a ptr,
+ * T means "Tell" directly with the argument value
+ * G means "Get": reply by setting through a pointer
+ * Q means "Query": response is on the return value
+ * X means "eXchange": G and S atomically
+ * H means "sHift": T and Q atomically
+ */
+#define HMP4E_IOCGBUFBUSADDRESS _IOR(HMP4E_IOC_MAGIC, 1, unsigned long *)
+#define HMP4E_IOCGBUFSIZE _IOR(HMP4E_IOC_MAGIC, 2, unsigned int *)
+#define HMP4E_IOCGHWOFFSET _IOR(HMP4E_IOC_MAGIC, 3, unsigned long *)
+#define HMP4E_IOCGHWIOSIZE _IOR(HMP4E_IOC_MAGIC, 4, unsigned int *)
+#define HMP4E_IOC_CLI _IO(HMP4E_IOC_MAGIC, 5)
+#define HMP4E_IOC_STI _IO(HMP4E_IOC_MAGIC, 6)
+#define HMP4E_IOCHARDRESET _IO(HMP4E_IOC_MAGIC, 7)
+#define HMP4E_IOCSREGWRITE _IOW(HMP4E_IOC_MAGIC, 8, write_t)
+#define HMP4E_IOCXREGREAD _IOWR(HMP4E_IOC_MAGIC, 9, unsigned long)
+
+#define HMP4E_IOC_MAXNR 9
+
+#endif /* !_HMP4ENC_H_ */
diff --git a/drivers/mxc/hw_event/Kconfig b/drivers/mxc/hw_event/Kconfig
new file mode 100644
index 000000000000..bcf479689501
--- /dev/null
+++ b/drivers/mxc/hw_event/Kconfig
@@ -0,0 +1,11 @@
+menu "MXC HARDWARE EVENT"
+
+config MXC_HWEVENT
+ bool "MXC Hardware Event Handler"
+ default y
+ depends on ARCH_MXC
+ help
+ If you plan to use the Hardware Event Handler in the MXC, say
+ Y here. If unsure, select Y.
+
+endmenu
diff --git a/drivers/mxc/hw_event/Makefile b/drivers/mxc/hw_event/Makefile
new file mode 100644
index 000000000000..a53fe2b45e04
--- /dev/null
+++ b/drivers/mxc/hw_event/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MXC_HWEVENT) += mxc_hw_event.o
diff --git a/drivers/mxc/hw_event/mxc_hw_event.c b/drivers/mxc/hw_event/mxc_hw_event.c
new file mode 100644
index 000000000000..5451c1c68859
--- /dev/null
+++ b/drivers/mxc/hw_event/mxc_hw_event.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * mxc_hw_event.c
+ * Collect the hardware events, send to user by netlink
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/signal.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+
+#include <mach/hw_events.h>
+
+#define EVENT_POOL_SIZE 10
+
+struct hw_event_elem {
+ struct mxc_hw_event event;
+ struct list_head list;
+};
+
+static struct sock *nl_event_sock; /* netlink socket */
+static struct list_head event_head;
+static struct list_head free_head;
+static struct hw_event_elem events_pool[EVENT_POOL_SIZE]; /* event pool */
+static DEFINE_SPINLOCK(list_lock);
+static DECLARE_WAIT_QUEUE_HEAD(event_wq);
+static unsigned int seq; /* send seq */
+static int initialized;
+static struct task_struct *hwevent_kthread;
+
+/*!
+ * main HW event handler thread
+ */
+static int hw_event_thread(void *data)
+{
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh = NULL;
+ unsigned int size;
+ struct hw_event_elem *event, *n;
+ LIST_HEAD(tmp_head);
+ DEFINE_WAIT(wait);
+
+ while (1) {
+
+ prepare_to_wait(&event_wq, &wait, TASK_INTERRUPTIBLE);
+ /* wait for event coming */
+ if (!freezing(current) && !kthread_should_stop() &&
+ list_empty(&event_head))
+ schedule();
+ finish_wait(&event_wq, &wait);
+
+ try_to_freeze();
+
+ if (kthread_should_stop())
+ break;
+
+ /* fetch event from list */
+ spin_lock_irq(&list_lock);
+ tmp_head = event_head;
+ tmp_head.prev->next = &tmp_head;
+ tmp_head.next->prev = &tmp_head;
+ /* clear the event list head */
+ INIT_LIST_HEAD(&event_head);
+ spin_unlock_irq(&list_lock);
+
+ list_for_each_entry_safe(event, n, &tmp_head, list) {
+
+ size = NLMSG_SPACE(sizeof(struct mxc_hw_event));
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb) {
+ /* if failed alloc skb, we drop this event */
+ printk(KERN_WARNING
+ "mxc_hw_event: skb_alloc() failed\n");
+ goto alloc_failure;
+ }
+
+ /* put the netlink header struct to skb */
+ nlh =
+ NLMSG_PUT(skb, 0, seq++, NLMSG_DONE,
+ size - sizeof(*nlh));
+
+ /* fill the netlink data */
+ memcpy((struct mxc_hw_event *)NLMSG_DATA(nlh),
+ &event->event, sizeof(struct mxc_hw_event));
+
+ /* free the event node, set to unused */
+ spin_lock_irq(&list_lock);
+ list_move(&event->list, &free_head);
+ spin_unlock_irq(&list_lock);
+
+ /* send to all process that create this socket */
+ NETLINK_CB(skb).pid = 0; /* sender pid */
+ NETLINK_CB(skb).dst_group = HW_EVENT_GROUP;
+ /* broadcast the event */
+ netlink_broadcast(nl_event_sock, skb, 0, HW_EVENT_GROUP,
+ GFP_KERNEL);
+
+ continue;
+ nlmsg_failure:
+ printk(KERN_WARNING
+ "mxc_hw_event: No tailroom for NLMSG in skb\n");
+ alloc_failure:
+ /* free the event node, set to unused */
+ spin_lock_irq(&list_lock);
+ list_del(&event->list);
+ list_add_tail(&event->list, &free_head);
+ spin_unlock_irq(&list_lock);
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ *
+ * @priority the event priority, REALTIME, EMERENCY, NORMAL
+ * @new_event event id to be send
+ */
+int hw_event_send(int priority, struct mxc_hw_event *new_event)
+{
+ unsigned int size;
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh = NULL;
+ struct mxc_hw_event *event;
+ struct hw_event_elem *event_elem;
+ int ret;
+ unsigned long flag;
+ struct list_head *list_node;
+
+ if (!initialized) {
+ pr_info("HW Event module has not been initialized\n");
+ return -1;
+ }
+
+ if (priority == HWE_HIGH_PRIORITY) {
+ /**
+ * the most high priority event,
+ * we send it immediatly.
+ */
+
+ size = NLMSG_SPACE(sizeof(struct mxc_hw_event));
+
+ /* alloc skb */
+ if (in_interrupt()) {
+ skb = alloc_skb(size, GFP_ATOMIC);
+ } else {
+ skb = alloc_skb(size, GFP_KERNEL);
+ }
+ if (!skb) {
+ /* if failed alloc skb, we drop this event */
+ printk(KERN_WARNING
+ "hw_event send: skb_alloc() failed\n");
+ goto send_later;
+ }
+
+ /* put the netlink header struct to skb */
+ nlh = NLMSG_PUT(skb, 0, seq++, NLMSG_DONE, size - sizeof(*nlh));
+
+ /* fill the netlink data */
+ event = (struct mxc_hw_event *)NLMSG_DATA(nlh);
+ memcpy(event, new_event, sizeof(struct mxc_hw_event));
+
+ /* send to all process that create this socket */
+ NETLINK_CB(skb).pid = 0; /* sender pid */
+ NETLINK_CB(skb).dst_group = HW_EVENT_GROUP;
+ /* broadcast the event */
+ ret = netlink_broadcast(nl_event_sock, skb, 0, HW_EVENT_GROUP,
+ in_interrupt()? GFP_ATOMIC :
+ GFP_KERNEL);
+ if (ret) {
+
+ nlmsg_failure:
+ /* send failed */
+ kfree_skb(skb);
+ goto send_later;
+ }
+
+ return 0;
+ }
+
+ send_later:
+ spin_lock_irqsave(&list_lock, flag);
+ if (list_empty(&free_head)) {
+ spin_unlock_irqrestore(&list_lock, flag);
+ /* no more free event node */
+ printk(KERN_WARNING "mxc_event send: no more free node\n");
+ return -1;
+ }
+
+ /* get a free node from free list, and added to event list */
+ list_node = free_head.next;
+ /* fill event */
+ event_elem = list_entry(list_node, struct hw_event_elem, list);
+ event_elem->event = *new_event;
+ list_move(list_node, &event_head);
+ spin_unlock_irqrestore(&list_lock, flag);
+
+ wake_up(&event_wq);
+
+ return 0;
+}
+
+static int __init mxc_hw_event_init(void)
+{
+ int i;
+
+ /* initial the list head for event and free */
+ INIT_LIST_HEAD(&free_head);
+ INIT_LIST_HEAD(&event_head);
+
+ /* initial the free list */
+ for (i = 0; i < EVENT_POOL_SIZE; i++)
+ list_add_tail(&events_pool[i].list, &free_head);
+
+ /* create netlink kernel sock */
+ nl_event_sock =
+ netlink_kernel_create(&init_net, NETLINK_USERSOCK, 0, NULL, NULL,
+ THIS_MODULE);
+ if (!nl_event_sock) {
+ printk(KERN_WARNING
+ "mxc_hw_event: Fail to create netlink socket.\n");
+ return 1;
+ }
+
+ hwevent_kthread = kthread_run(hw_event_thread, NULL, "hwevent");
+ if (IS_ERR(hwevent_kthread)) {
+ printk(KERN_WARNING
+ "mxc_hw_event: Fail to create hwevent thread.\n");
+ return 1;
+ }
+
+ initialized = 1;
+
+ return 0;
+}
+
+static void __exit mxc_hw_event_exit(void)
+{
+ kthread_stop(hwevent_kthread);
+ /* wait for thread completion */
+ sock_release(nl_event_sock->sk_socket);
+}
+
+module_init(mxc_hw_event_init);
+module_exit(mxc_hw_event_exit);
+
+EXPORT_SYMBOL(hw_event_send);
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/ipu/Kconfig b/drivers/mxc/ipu/Kconfig
new file mode 100644
index 000000000000..a57f8ce74758
--- /dev/null
+++ b/drivers/mxc/ipu/Kconfig
@@ -0,0 +1,5 @@
+config MXC_IPU_V1
+ bool
+
+source "drivers/mxc/ipu/pf/Kconfig"
+
diff --git a/drivers/mxc/ipu/Makefile b/drivers/mxc/ipu/Makefile
new file mode 100644
index 000000000000..4e9f19f9afa5
--- /dev/null
+++ b/drivers/mxc/ipu/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MXC_IPU_V1) = mxc_ipu.o
+
+mxc_ipu-objs := ipu_common.o ipu_sdc.o ipu_adc.o ipu_ic.o ipu_csi.o ipu_device.o ipu_calc_stripes_sizes.o
+
+obj-$(CONFIG_MXC_IPU_PF) += pf/
diff --git a/drivers/mxc/ipu/ipu_adc.c b/drivers/mxc/ipu/ipu_adc.c
new file mode 100644
index 000000000000..a66087d71e9e
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_adc.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * @file ipu_adc.c
+ *
+ * @brief IPU ADC functions
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+/*#define ADC_CHAN1_SA_MASK 0xFF800000 */
+
+static void _ipu_set_cmd_data_mappings(display_port_t disp,
+ uint32_t pixel_fmt, int ifc_width);
+
+int32_t _ipu_adc_init_channel(ipu_channel_t chan, display_port_t disp,
+ mcu_mode_t cmd, int16_t x_pos, int16_t y_pos)
+{
+ uint32_t reg;
+ uint32_t start_addr, stride;
+ unsigned long lock_flags;
+ uint32_t size;
+
+ size = 0;
+
+ switch (disp) {
+ case DISP0:
+ reg = __raw_readl(ADC_DISP0_CONF);
+ stride = reg & ADC_DISP_CONF_SL_MASK;
+ break;
+ case DISP1:
+ reg = __raw_readl(ADC_DISP1_CONF);
+ stride = reg & ADC_DISP_CONF_SL_MASK;
+ break;
+ case DISP2:
+ reg = __raw_readl(ADC_DISP2_CONF);
+ stride = reg & ADC_DISP_CONF_SL_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (stride == 0)
+ return -EINVAL;
+
+ stride++;
+ start_addr = (y_pos * stride) + x_pos;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+ reg = __raw_readl(ADC_CONF);
+
+ switch (chan) {
+ case ADC_SYS1:
+ reg &= ~0x00FF4000;
+ reg |=
+ ((uint32_t) size << 21 | (uint32_t) disp << 19 | (uint32_t)
+ cmd << 16);
+
+ __raw_writel(start_addr, ADC_SYSCHA1_SA);
+ break;
+
+ case ADC_SYS2:
+ reg &= ~0xFF008000;
+ reg |=
+ ((uint32_t) size << 29 | (uint32_t) disp << 27 | (uint32_t)
+ cmd << 24);
+
+ __raw_writel(start_addr, ADC_SYSCHA2_SA);
+ break;
+
+ case CSI_PRP_VF_ADC:
+ case MEM_PRP_VF_ADC:
+ reg &= ~0x000000F9;
+ reg |=
+ ((uint32_t) size << 5 | (uint32_t) disp << 3 |
+ ADC_CONF_PRP_EN);
+
+ __raw_writel(start_addr, ADC_PRPCHAN_SA);
+ break;
+
+ case MEM_PP_ADC:
+ reg &= ~0x00003F02;
+ reg |=
+ ((uint32_t) size << 10 | (uint32_t) disp << 8 |
+ ADC_CONF_PP_EN);
+
+ __raw_writel(start_addr, ADC_PPCHAN_SA);
+ break;
+ default:
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return -1;
+ break;
+ }
+ __raw_writel(reg, ADC_CONF);
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return 0;
+}
+
+int32_t _ipu_adc_uninit_channel(ipu_channel_t chan)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+ reg = __raw_readl(ADC_CONF);
+
+ switch (chan) {
+ case ADC_SYS1:
+ reg &= ~0x00FF4000;
+ break;
+ case ADC_SYS2:
+ reg &= ~0xFF008000;
+ break;
+ case CSI_PRP_VF_ADC:
+ case MEM_PRP_VF_ADC:
+ reg &= ~0x000000F9;
+ break;
+ case MEM_PP_ADC:
+ reg &= ~0x00003F02;
+ break;
+ default:
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return -1;
+ break;
+ }
+ __raw_writel(reg, ADC_CONF);
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return 0;
+}
+
+int32_t ipu_adc_write_template(display_port_t disp, uint32_t * pCmd, bool write)
+{
+ uint32_t ima_addr = 0;
+ uint32_t row_nu;
+ int i;
+
+ /* Set IPU_IMA_ADDR (IPU Internal Memory Access Address) */
+ /* MEM_NU = 0x0001 (CPM) */
+ /* ROW_NU = 2*N ( N is channel number) */
+ /* WORD_NU = 0 */
+ if (write) {
+ row_nu = (uint32_t) disp *2 * ATM_ADDR_RANGE;
+ } else {
+ row_nu = ((uint32_t) disp * 2 + 1) * ATM_ADDR_RANGE;
+ }
+
+ /* form template addr for IPU_IMA_ADDR */
+ ima_addr = (0x3 << 16 /*Template memory */ | row_nu << 3);
+
+ __raw_writel(ima_addr, IPU_IMA_ADDR);
+
+ /* write template data for IPU_IMA_DATA */
+ for (i = 0; i < TEMPLATE_BUF_SIZE; i++)
+ /* only DATA field are needed */
+ __raw_writel(pCmd[i], IPU_IMA_DATA);
+
+ return 0;
+}
+
+int32_t
+ipu_adc_write_cmd(display_port_t disp, cmddata_t type,
+ uint32_t cmd, const uint32_t * params, uint16_t numParams)
+{
+ uint16_t i;
+ int disable_di = 0;
+ u32 reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+ reg = __raw_readl(IPU_CONF);
+ if ((reg & IPU_CONF_DI_EN) == 0) {
+ disable_di = 1;
+ reg |= IPU_CONF_DI_EN;
+ __raw_writel(reg, IPU_CONF);
+ }
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ __raw_writel((uint32_t) ((type ? 0x0 : 0x1) | disp << 1 | 0x10),
+ DI_DISP_LLA_CONF);
+ __raw_writel(cmd, DI_DISP_LLA_DATA);
+ udelay(3);
+
+ __raw_writel((uint32_t) (0x10 | disp << 1 | 0x11), DI_DISP_LLA_CONF);
+ for (i = 0; i < numParams; i++) {
+ __raw_writel(params[i], DI_DISP_LLA_DATA);
+ udelay(3);
+ }
+
+ if (disable_di) {
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+ reg = __raw_readl(IPU_CONF);
+ reg &= ~IPU_CONF_DI_EN;
+ __raw_writel(reg, IPU_CONF);
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ }
+
+ return 0;
+}
+
+int32_t ipu_adc_set_update_mode(ipu_channel_t channel,
+ ipu_adc_update_mode_t mode,
+ uint32_t refresh_rate, unsigned long addr,
+ uint32_t * size)
+{
+ int32_t err = 0;
+ uint32_t ref_per, reg, src = 0;
+ unsigned long lock_flags;
+ uint32_t ipu_freq;
+
+ ipu_freq = clk_get_rate(g_ipu_clk);
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(IPU_FS_DISP_FLOW);
+ reg &= ~FS_AUTO_REF_PER_MASK;
+ switch (mode) {
+ case IPU_ADC_REFRESH_NONE:
+ src = 0;
+ break;
+ case IPU_ADC_AUTO_REFRESH:
+ if (refresh_rate == 0) {
+ err = -EINVAL;
+ goto err0;
+ }
+ ref_per = ipu_freq / ((1UL << 17) * refresh_rate);
+ ref_per--;
+ reg |= ref_per << FS_AUTO_REF_PER_OFFSET;
+
+ src = FS_SRC_AUTOREF;
+ break;
+ case IPU_ADC_AUTO_REFRESH_SNOOP:
+ if (refresh_rate == 0) {
+ err = -EINVAL;
+ goto err0;
+ }
+ ref_per = ipu_freq / ((1UL << 17) * refresh_rate);
+ ref_per--;
+ reg |= ref_per << FS_AUTO_REF_PER_OFFSET;
+
+ src = FS_SRC_AUTOREF_SNOOP;
+ break;
+ case IPU_ADC_SNOOPING:
+ src = FS_SRC_SNOOP;
+ break;
+ }
+
+ switch (channel) {
+ case ADC_SYS1:
+ reg &= ~FS_ADC1_SRC_SEL_MASK;
+ reg |= src << FS_ADC1_SRC_SEL_OFFSET;
+ break;
+ case ADC_SYS2:
+ reg &= ~FS_ADC2_SRC_SEL_MASK;
+ reg |= src << FS_ADC2_SRC_SEL_OFFSET;
+ break;
+ default:
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return -EINVAL;
+ }
+ __raw_writel(reg, IPU_FS_DISP_FLOW);
+
+ /* Setup bus snooping */
+ if ((mode == IPU_ADC_AUTO_REFRESH_SNOOP) || (mode == IPU_ADC_SNOOPING)) {
+ err = mxc_snoop_set_config(0, addr, *size);
+ if (err > 0) {
+ *size = err;
+ err = 0;
+ }
+ } else {
+ mxc_snoop_set_config(0, 0, 0);
+ }
+
+ err0:
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return err;
+}
+
+int32_t ipu_adc_get_snooping_status(uint32_t * statl, uint32_t * stath)
+{
+ return mxc_snoop_get_status(0, statl, stath);
+}
+
+int32_t ipu_adc_init_panel(display_port_t disp,
+ uint16_t width, uint16_t height,
+ uint32_t pixel_fmt,
+ uint32_t stride,
+ ipu_adc_sig_cfg_t sig,
+ display_addressing_t addr,
+ uint32_t vsync_width, vsync_t mode)
+{
+ uint32_t temp;
+ unsigned long lock_flags;
+ uint32_t ser_conf;
+ uint32_t disp_conf;
+ uint32_t adc_disp_conf;
+ uint32_t adc_disp_vsync;
+ uint32_t old_pol;
+
+ if ((disp != DISP1) && (disp != DISP2) &&
+ (sig.ifc_mode >= IPU_ADC_IFC_MODE_3WIRE_SERIAL)) {
+ return -EINVAL;
+ }
+/* adc_disp_conf = ((uint32_t)((((size == 3)||(size == 2))?1:0)<<14) | */
+/* (uint32_t)addr<<12 | (stride-1)); */
+ adc_disp_conf = (uint32_t) addr << 12 | (stride - 1);
+
+ _ipu_set_cmd_data_mappings(disp, pixel_fmt, sig.ifc_width);
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+ disp_conf = __raw_readl(DI_DISP_IF_CONF);
+ old_pol = __raw_readl(DI_DISP_SIG_POL);
+ adc_disp_vsync = __raw_readl(ADC_DISP_VSYNC);
+
+ switch (disp) {
+ case DISP0:
+ __raw_writel(adc_disp_conf, ADC_DISP0_CONF);
+ __raw_writel((((height - 1) << 16) | (width - 1)),
+ ADC_DISP0_SS);
+
+ adc_disp_vsync &= ~(ADC_DISP_VSYNC_D0_MODE_MASK |
+ ADC_DISP_VSYNC_D0_WIDTH_MASK);
+ adc_disp_vsync |= (vsync_width << 16) | (uint32_t) mode;
+
+ old_pol &= ~0x2000003FL;
+ old_pol |= sig.data_pol | sig.cs_pol << 1 |
+ sig.addr_pol << 2 | sig.read_pol << 3 |
+ sig.write_pol << 4 | sig.Vsync_pol << 5 |
+ sig.burst_pol << 29;
+ __raw_writel(old_pol, DI_DISP_SIG_POL);
+
+ disp_conf &= ~0x0000001FL;
+ disp_conf |= (sig.burst_mode << 3) | (sig.ifc_mode << 1) |
+ DI_CONF_DISP0_EN;
+ __raw_writel(disp_conf, DI_DISP_IF_CONF);
+ break;
+ case DISP1:
+ __raw_writel(adc_disp_conf, ADC_DISP1_CONF);
+ __raw_writel((((height - 1) << 16) | (width - 1)),
+ ADC_DISP12_SS);
+
+ adc_disp_vsync &= ~(ADC_DISP_VSYNC_D12_MODE_MASK |
+ ADC_DISP_VSYNC_D12_WIDTH_MASK);
+ adc_disp_vsync |= (vsync_width << 16) | (uint32_t) mode;
+
+ old_pol &= ~0x4000FF00L;
+ old_pol |= (sig.Vsync_pol << 6 | sig.data_pol << 8 |
+ sig.cs_pol << 9 | sig.addr_pol << 10 |
+ sig.read_pol << 11 | sig.write_pol << 12 |
+ sig.clk_pol << 14 | sig.burst_pol << 30);
+ __raw_writel(old_pol, DI_DISP_SIG_POL);
+
+ disp_conf &= ~0x00003F00L;
+ if (sig.ifc_mode >= IPU_ADC_IFC_MODE_3WIRE_SERIAL) {
+ ser_conf = (sig.ifc_width - 1) <<
+ DI_SER_DISPx_CONF_SER_BIT_NUM_OFFSET;
+ if (sig.ser_preamble_len) {
+ ser_conf |= DI_SER_DISPx_CONF_PREAMBLE_EN;
+ ser_conf |= sig.ser_preamble <<
+ DI_SER_DISPx_CONF_PREAMBLE_OFFSET;
+ ser_conf |= (sig.ser_preamble_len - 1) <<
+ DI_SER_DISPx_CONF_PREAMBLE_LEN_OFFSET;
+ }
+
+ ser_conf |=
+ sig.ser_rw_mode << DI_SER_DISPx_CONF_RW_CFG_OFFSET;
+
+ if (sig.burst_mode == IPU_ADC_BURST_SERIAL)
+ ser_conf |= DI_SER_DISPx_CONF_BURST_MODE_EN;
+ __raw_writel(ser_conf, DI_SER_DISP1_CONF);
+ } else { /* parallel interface */
+ disp_conf |= (uint32_t) (sig.burst_mode << 12);
+ }
+ disp_conf |= (sig.ifc_mode << 9) | DI_CONF_DISP1_EN;
+ __raw_writel(disp_conf, DI_DISP_IF_CONF);
+ break;
+ case DISP2:
+ __raw_writel(adc_disp_conf, ADC_DISP2_CONF);
+ __raw_writel((((height - 1) << 16) | (width - 1)),
+ ADC_DISP12_SS);
+
+ adc_disp_vsync &= ~(ADC_DISP_VSYNC_D12_MODE_MASK |
+ ADC_DISP_VSYNC_D12_WIDTH_MASK);
+ adc_disp_vsync |= (vsync_width << 16) | (uint32_t) mode;
+
+ old_pol &= ~0x80FF0000L;
+ temp = (uint32_t) (sig.data_pol << 16 | sig.cs_pol << 17 |
+ sig.addr_pol << 18 | sig.read_pol << 19 |
+ sig.write_pol << 20 | sig.Vsync_pol << 6 |
+ sig.burst_pol << 31 | sig.clk_pol << 22);
+ __raw_writel(temp | old_pol, DI_DISP_SIG_POL);
+
+ disp_conf &= ~0x003F0000L;
+ if (sig.ifc_mode >= IPU_ADC_IFC_MODE_3WIRE_SERIAL) {
+ ser_conf = (sig.ifc_width - 1) <<
+ DI_SER_DISPx_CONF_SER_BIT_NUM_OFFSET;
+ if (sig.ser_preamble_len) {
+ ser_conf |= DI_SER_DISPx_CONF_PREAMBLE_EN;
+ ser_conf |= sig.ser_preamble <<
+ DI_SER_DISPx_CONF_PREAMBLE_OFFSET;
+ ser_conf |= (sig.ser_preamble_len - 1) <<
+ DI_SER_DISPx_CONF_PREAMBLE_LEN_OFFSET;
+
+ }
+
+ ser_conf |=
+ sig.ser_rw_mode << DI_SER_DISPx_CONF_RW_CFG_OFFSET;
+
+ if (sig.burst_mode == IPU_ADC_BURST_SERIAL)
+ ser_conf |= DI_SER_DISPx_CONF_BURST_MODE_EN;
+ __raw_writel(ser_conf, DI_SER_DISP2_CONF);
+ } else { /* parallel interface */
+ disp_conf |= (uint32_t) (sig.burst_mode << 20);
+ }
+ disp_conf |= (sig.ifc_mode << 17) | DI_CONF_DISP2_EN;
+ __raw_writel(disp_conf, DI_DISP_IF_CONF);
+ break;
+ default:
+ break;
+ }
+
+ __raw_writel(adc_disp_vsync, ADC_DISP_VSYNC);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+
+int32_t ipu_adc_init_ifc_timing(display_port_t disp, bool read,
+ uint32_t cycle_time,
+ uint32_t up_time,
+ uint32_t down_time,
+ uint32_t read_latch_time, uint32_t pixel_clk)
+{
+ uint32_t reg;
+ uint32_t time_conf3 = 0;
+ uint32_t clk_per;
+ uint32_t up_per;
+ uint32_t down_per;
+ uint32_t read_per;
+ uint32_t pixclk_per = 0;
+ uint32_t ipu_freq;
+
+ ipu_freq = clk_get_rate(g_ipu_clk);
+
+ clk_per = (cycle_time * (ipu_freq / 1000L) * 16L) / 1000000L;
+ up_per = (up_time * (ipu_freq / 1000L) * 4L) / 1000000L;
+ down_per = (down_time * (ipu_freq / 1000L) * 4L) / 1000000L;
+
+ reg = (clk_per << DISPx_IF_CLK_PER_OFFSET) |
+ (up_per << DISPx_IF_CLK_UP_OFFSET) |
+ (down_per << DISPx_IF_CLK_DOWN_OFFSET);
+
+ if (read) {
+ read_per =
+ (read_latch_time * (ipu_freq / 1000L) * 4L) / 1000000L;
+ if (pixel_clk)
+ pixclk_per = (ipu_freq * 16L) / pixel_clk;
+ time_conf3 = (read_per << DISPx_IF_CLK_READ_EN_OFFSET) |
+ (pixclk_per << DISPx_PIX_CLK_PER_OFFSET);
+ }
+
+ dev_dbg(g_ipu_dev, "DI_DISPx_TIME_CONF_1/2 = 0x%08X\n", reg);
+ dev_dbg(g_ipu_dev, "DI_DISPx_TIME_CONF_3 = 0x%08X\n", time_conf3);
+
+ switch (disp) {
+ case DISP0:
+ if (read) {
+ __raw_writel(reg, DI_DISP0_TIME_CONF_2);
+ __raw_writel(time_conf3, DI_DISP0_TIME_CONF_3);
+ } else {
+ __raw_writel(reg, DI_DISP0_TIME_CONF_1);
+ }
+ break;
+ case DISP1:
+ if (read) {
+ __raw_writel(reg, DI_DISP1_TIME_CONF_2);
+ __raw_writel(time_conf3, DI_DISP1_TIME_CONF_3);
+ } else {
+ __raw_writel(reg, DI_DISP1_TIME_CONF_1);
+ }
+ break;
+ case DISP2:
+ if (read) {
+ __raw_writel(reg, DI_DISP2_TIME_CONF_2);
+ __raw_writel(time_conf3, DI_DISP2_TIME_CONF_3);
+ } else {
+ __raw_writel(reg, DI_DISP2_TIME_CONF_1);
+ }
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+}
+
+struct ipu_adc_di_map {
+ uint32_t map_byte1;
+ uint32_t map_byte2;
+ uint32_t map_byte3;
+ uint32_t cycle_cnt;
+};
+
+static const struct ipu_adc_di_map di_mappings[] = {
+ [0] = {
+ /* RGB888, 8-bit bus */
+ .map_byte1 = 0x1600AAAA,
+ .map_byte2 = 0x00E05555,
+ .map_byte2 = 0x00070000,
+ .cycle_cnt = 3,
+ },
+ [1] = {
+ /* RGB666, 8-bit bus */
+ .map_byte1 = 0x1C00AAAF,
+ .map_byte2 = 0x00E0555F,
+ .map_byte3 = 0x0007000F,
+ .cycle_cnt = 3,
+ },
+ [2] = {
+ /* RGB565, 8-bit bus */
+ .map_byte1 = 0x008055BF,
+ .map_byte2 = 0x0142015F,
+ .map_byte3 = 0x0007003F,
+ .cycle_cnt = 2,
+ },
+ [3] = {
+ /* RGB888, 24-bit bus */
+ .map_byte1 = 0x0007000F,
+ .map_byte2 = 0x000F000F,
+ .map_byte3 = 0x0017000F,
+ .cycle_cnt = 1,
+ },
+ [4] = {
+ /* RGB666, 18-bit bus */
+ .map_byte1 = 0x0005000F,
+ .map_byte2 = 0x000B000F,
+ .map_byte3 = 0x0011000F,
+ .cycle_cnt = 1,
+ },
+ [5] = {
+ /* RGB565, 16-bit bus */
+ .map_byte1 = 0x0004003F,
+ .map_byte2 = 0x000A000F,
+ .map_byte3 = 0x000F003F,
+ .cycle_cnt = 1,
+ },
+};
+
+/* Private methods */
+static void _ipu_set_cmd_data_mappings(display_port_t disp,
+ uint32_t pixel_fmt, int ifc_width)
+{
+ uint32_t reg;
+ u32 map = 0;
+
+ if (ifc_width == 8) {
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_BGR24:
+ map = 0;
+ break;
+ case IPU_PIX_FMT_RGB666:
+ map = 1;
+ break;
+ case IPU_PIX_FMT_RGB565:
+ map = 2;
+ break;
+ default:
+ break;
+ }
+ } else if (ifc_width >= 16) {
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_BGR24:
+ map = 3;
+ break;
+ case IPU_PIX_FMT_RGB666:
+ map = 4;
+ break;
+ case IPU_PIX_FMT_RGB565:
+ map = 5;
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (disp) {
+ case DISP0:
+ if (ifc_width == 8) {
+ __raw_writel(0x00070000, DI_DISP0_CB0_MAP);
+ __raw_writel(0x0000FFFF, DI_DISP0_CB1_MAP);
+ __raw_writel(0x0000FFFF, DI_DISP0_CB2_MAP);
+ } else {
+ __raw_writel(0x00070000, DI_DISP0_CB0_MAP);
+ __raw_writel(0x000F0000, DI_DISP0_CB1_MAP);
+ __raw_writel(0x0000FFFF, DI_DISP0_CB2_MAP);
+ }
+ __raw_writel(di_mappings[map].map_byte1, DI_DISP0_DB0_MAP);
+ __raw_writel(di_mappings[map].map_byte2, DI_DISP0_DB1_MAP);
+ __raw_writel(di_mappings[map].map_byte3, DI_DISP0_DB2_MAP);
+ reg = __raw_readl(DI_DISP_ACC_CC);
+ reg &= ~DISP0_IF_CLK_CNT_D_MASK;
+ reg |= (di_mappings[map].cycle_cnt - 1) <<
+ DISP0_IF_CLK_CNT_D_OFFSET;
+ __raw_writel(reg, DI_DISP_ACC_CC);
+ break;
+ case DISP1:
+ if (ifc_width == 8) {
+ __raw_writel(0x00070000, DI_DISP1_CB0_MAP);
+ __raw_writel(0x0000FFFF, DI_DISP1_CB1_MAP);
+ __raw_writel(0x0000FFFF, DI_DISP1_CB2_MAP);
+ } else {
+ __raw_writel(0x00070000, DI_DISP1_CB0_MAP);
+ __raw_writel(0x000F0000, DI_DISP1_CB1_MAP);
+ __raw_writel(0x0000FFFF, DI_DISP1_CB2_MAP);
+ }
+ __raw_writel(di_mappings[map].map_byte1, DI_DISP1_DB0_MAP);
+ __raw_writel(di_mappings[map].map_byte2, DI_DISP1_DB1_MAP);
+ __raw_writel(di_mappings[map].map_byte3, DI_DISP1_DB2_MAP);
+ reg = __raw_readl(DI_DISP_ACC_CC);
+ reg &= ~DISP1_IF_CLK_CNT_D_MASK;
+ reg |= (di_mappings[map].cycle_cnt - 1) <<
+ DISP1_IF_CLK_CNT_D_OFFSET;
+ __raw_writel(reg, DI_DISP_ACC_CC);
+ break;
+ case DISP2:
+ if (ifc_width == 8) {
+ __raw_writel(0x00070000, DI_DISP2_CB0_MAP);
+ __raw_writel(0x0000FFFF, DI_DISP2_CB1_MAP);
+ __raw_writel(0x0000FFFF, DI_DISP2_CB2_MAP);
+ } else {
+ __raw_writel(0x00070000, DI_DISP2_CB0_MAP);
+ __raw_writel(0x000F0000, DI_DISP2_CB1_MAP);
+ __raw_writel(0x0000FFFF, DI_DISP2_CB2_MAP);
+ }
+ __raw_writel(di_mappings[map].map_byte1, DI_DISP2_DB0_MAP);
+ __raw_writel(di_mappings[map].map_byte2, DI_DISP2_DB1_MAP);
+ __raw_writel(di_mappings[map].map_byte3, DI_DISP2_DB2_MAP);
+ reg = __raw_readl(DI_DISP_ACC_CC);
+ reg &= ~DISP2_IF_CLK_CNT_D_MASK;
+ reg |= (di_mappings[map].cycle_cnt - 1) <<
+ DISP2_IF_CLK_CNT_D_OFFSET;
+ __raw_writel(reg, DI_DISP_ACC_CC);
+ break;
+ default:
+ break;
+ }
+}
+
+void ipu_disp_direct_write(ipu_channel_t channel, u32 value, u32 offset)
+{
+ /*TODO*/
+}
+
+int ipu_init_async_panel(int disp, int type, uint32_t cycle_time,
+ uint32_t pixel_fmt, ipu_adc_sig_cfg_t sig)
+{
+ /*TODO:uniform interface for ipu async panel init*/
+}
+
+EXPORT_SYMBOL(ipu_adc_write_template);
+EXPORT_SYMBOL(ipu_adc_write_cmd);
+EXPORT_SYMBOL(ipu_adc_set_update_mode);
+EXPORT_SYMBOL(ipu_adc_init_panel);
+EXPORT_SYMBOL(ipu_adc_init_ifc_timing);
diff --git a/drivers/mxc/ipu/ipu_calc_stripes_sizes.c b/drivers/mxc/ipu/ipu_calc_stripes_sizes.c
new file mode 100644
index 000000000000..2992cce7023d
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_calc_stripes_sizes.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * @file ipu_calc_stripes_sizes.c
+ *
+ * @brief IPU IC functions
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/ipu.h>
+#include <asm/div64.h>
+
+#define BPP_32 0
+#define BPP_16 3
+#define BPP_8 5
+#define BPP_24 1
+#define BPP_12 4
+#define BPP_18 2
+
+static u64 _do_div(u64 a, u32 b)
+{
+ u64 div;
+ div = a;
+ do_div(div, b);
+ return div;
+}
+
+static u32 truncate(u32 up, /* 0: down; else: up */
+ u64 a, /* must be non-negative */
+ u32 b)
+{
+ u32 d;
+ u64 div;
+ div = _do_div(a, b);
+ d = b * (div >> 32);
+ if (up && (a > (((u64)d) << 32)))
+ return d+b;
+ else
+ return d;
+}
+
+static unsigned int f_calc(unsigned int pfs, unsigned int bpp, unsigned int *write)
+{/* return input_f */
+ unsigned int f_calculated = 0;
+ switch (pfs) {
+ case IPU_PIX_FMT_YVU422P:
+ case IPU_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ f_calculated = 16;
+ break;
+
+ case IPU_PIX_FMT_NV12:
+ f_calculated = 8;
+ break;
+
+ default:
+ f_calculated = 0;
+ break;
+
+ }
+ if (!f_calculated) {
+ switch (bpp) {
+ case BPP_32:
+ f_calculated = 2;
+ break;
+
+ case BPP_16:
+ f_calculated = 4;
+ break;
+
+ case BPP_8:
+ case BPP_24:
+ f_calculated = 8;
+ break;
+
+ case BPP_12:
+ f_calculated = 16;
+ break;
+
+ case BPP_18:
+ f_calculated = 32;
+ break;
+
+ default:
+ f_calculated = 0;
+ break;
+ }
+ }
+ return f_calculated;
+}
+
+
+static unsigned int m_calc(unsigned int pfs)
+{
+ unsigned int m_calculated = 0;
+ switch (pfs) {
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YVU422P:
+ case IPU_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YVU420P:
+ case IPU_PIX_FMT_NV12:
+ m_calculated = 8;
+ break;
+
+ case IPU_PIX_FMT_YUYV:
+ case IPU_PIX_FMT_UYVY:
+ m_calculated = 2;
+ break;
+
+ default:
+ m_calculated = 1;
+ break;
+
+ }
+ return m_calculated;
+}
+
+
+/* Stripe parameters calculator */
+/**************************************************************************
+Notes:
+MSW = the maximal width allowed for a stripe
+ i.MX31: 720, i.MX35: 800, i.MX37/51/53: 1024
+cirr = the maximal inverse resizing ratio for which overlap in the input
+ is requested; typically cirr~2
+equal_stripes:
+ 0: each stripe is allowed to have independent parameters
+ for maximal image quality
+ 1: the stripes are requested to have identical parameters
+ (except the base address), for maximal performance
+If performance is the top priority (above image quality)
+ Avoid overlap, by setting CIRR = 0
+ This will also force effectively identical_stripes = 1
+ Choose IF & OF that corresponds to the same IOX/SX for both stripes
+ Choose IFW & OFW such that
+ IFW/IM, IFW/IF, OFW/OM, OFW/OF are even integers
+ The function returns an error status:
+ 0: no error
+ 1: invalid input parameters -> aborted without result
+ Valid parameters should satisfy the following conditions
+ IFW <= OFW, otherwise downsizing is required
+ - which is not supported yet
+ 4 <= IFW,OFW, so some interpolation may be needed even without overlap
+ IM, OM, IF, OF should not vanish
+ 2*IF <= IFW
+ so the frame can be split to two equal stripes, even without overlap
+ 2*(OF+IF/irr_opt) <= OFW
+ so a valid positive INW exists even for equal stripes
+ OF <= MSW, otherwise, the left stripe cannot be sufficiently large
+ MSW < OFW, so splitting to stripes is required
+ OFW <= 2*MSW, so two stripes are sufficient
+ (this also implies that 2<=MSW)
+ 2: OF is not a multiple of OM - not fully-supported yet
+ Output is produced but OW is not guaranited to be a multiple of OM
+ 4: OFW reduced to be a multiple of OM
+ 8: CIRR > 1: truncated to 1
+ Overlap is not supported (and not needed) y for upsizing)
+**************************************************************************/
+int ipu_calc_stripes_sizes(const unsigned int input_frame_width,
+ /* input frame width;>1 */
+ unsigned int output_frame_width, /* output frame width; >1 */
+ const unsigned int maximal_stripe_width,
+ /* the maximal width allowed for a stripe */
+ const unsigned long long cirr, /* see above */
+ const unsigned int equal_stripes, /* see above */
+ u32 input_pixelformat,/* pixel format after of read channel*/
+ u32 output_pixelformat,/* pixel format after of write channel*/
+ struct stripe_param *left,
+ struct stripe_param *right)
+{
+ const unsigned int irr_frac_bits = 13;
+ const unsigned long irr_steps = 1 << irr_frac_bits;
+ const u64 dirr = ((u64)1) << (32 - 2);
+ /* The maximum relative difference allowed between the irrs */
+ const u64 cr = ((u64)4) << 32;
+ /* The importance ratio between the two terms in the cost function below */
+
+ unsigned int status;
+ unsigned int temp;
+ unsigned int onw_min;
+ unsigned int inw, onw, inw_best;
+ /* number of pixels in the left stripe NOT hidden by the right stripe */
+ u64 irr_opt; /* the optimal inverse resizing ratio */
+ u64 rr_opt; /* the optimal resizing ratio = 1/irr_opt*/
+ u64 dinw; /* the misalignment between the stripes */
+ /* (measured in units of input columns) */
+ u64 difwl, difwr;
+ /* The number of input columns not reflected in the output */
+ /* the resizing ratio used for the right stripe is */
+ /* left->irr and right->irr respectively */
+ u64 cost, cost_min;
+ u64 div; /* result of division */
+
+ unsigned int input_m, input_f, output_m, output_f; /* parameters for upsizing by stripes */
+ u32 in_pfs, out_pfs;
+
+ status = 0;
+
+ /* M, F calculations */
+ /* read back pfs from params */
+
+ input_f = f_calc(input_pixelformat, 0, NULL);
+ /*in_m = m_calc(in_pfs);*/
+ input_m = 16;
+ /* BPP should be used in the out_F calc */
+ /* Temporarily not used */
+ /* out_F = F_calc(idmac->pfs, idmac->bpp, NULL); */
+
+ output_f = 16;
+ output_m = m_calc(output_pixelformat);
+
+
+ if ((output_frame_width < input_frame_width) || (input_frame_width < 4)
+ || (output_frame_width < 4))
+ return 1;
+
+ irr_opt = _do_div((((u64)(input_frame_width - 1)) << 32),
+ (output_frame_width - 1));
+ rr_opt = _do_div((((u64)(output_frame_width - 1)) << 32),
+ (input_frame_width - 1));
+
+ if ((input_m == 0) || (output_m == 0) || (input_f == 0) || (output_f == 0)
+ || (input_frame_width < (2 * input_f))
+ || ((((u64)output_frame_width) << 32) <
+ (2 * ((((u64)output_f) << 32) + (input_f * rr_opt))))
+ || (maximal_stripe_width < output_f)
+ || (output_frame_width <= maximal_stripe_width)
+ || ((2 * maximal_stripe_width) < output_frame_width))
+ return 1;
+
+ if (output_f % output_m)
+ status += 2;
+
+ temp = truncate(0, (((u64)output_frame_width) << 32), output_m);
+ if (temp < output_frame_width) {
+ output_frame_width = temp;
+ status += 4;
+ }
+
+ if (equal_stripes) {
+ if ((irr_opt > cirr) /* overlap in the input is not requested */
+ && ((input_frame_width % (input_m << 1)) == 0)
+ && ((input_frame_width % (input_f << 1)) == 0)
+ && ((output_frame_width % (output_m << 1)) == 0)
+ && ((output_frame_width % (output_f << 1)) == 0)) {
+ /* without overlap */
+ left->input_width = right->input_width = right->input_column =
+ input_frame_width >> 1;
+ left->output_width = right->output_width = right->output_column =
+ output_frame_width >> 1;
+ left->input_column = right->input_column = 0;
+ div = _do_div(((((u64)irr_steps) << 32) *
+ (right->input_width - 1)), (right->output_width - 1));
+ left->irr = right->irr = truncate(0, div, 1);
+ } else { /* with overlap */
+ onw = truncate(0, (((u64)output_frame_width) << 32) >> 1,
+ output_f);
+ inw = truncate(0, onw * irr_opt, input_f);
+ /* this is the maximal inw which allows the same resizing ratio */
+ /* in both stripes */
+ onw = truncate(1, (inw * rr_opt), output_f);
+ div = _do_div((((u64)(irr_steps * inw)) <<
+ 32), onw);
+ left->irr = right->irr = truncate(0, div, 1);
+ left->output_width = right->output_width =
+ output_frame_width - onw;
+ /* These are valid assignments for output_width, */
+ /* assuming output_f is a multiple of output_m */
+ div = (((u64)(left->output_width-1) * (left->irr)) << 32);
+ div = (((u64)1) << 32) + _do_div(div, irr_steps);
+
+ left->input_width = right->input_width = truncate(1, div, input_m);
+
+ div = _do_div((((u64)((right->output_width - 1) * right->irr)) <<
+ 32), irr_steps);
+ difwr = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
+ div = _do_div((difwr + (((u64)input_f) << 32)), 2);
+ left->input_column = truncate(0, div, input_f);
+
+
+ /* This splits the truncated input columns evenly */
+ /* between the left and right margins */
+ right->input_column = left->input_column + inw;
+ left->output_column = 0;
+ right->output_column = onw;
+ }
+ } else { /* independent stripes */
+ onw_min = output_frame_width - maximal_stripe_width;
+ /* onw is a multiple of output_f, in the range */
+ /* [max(output_f,output_frame_width-maximal_stripe_width),*/
+ /*min(output_frame_width-2,maximal_stripe_width)] */
+ /* definitely beyond the cost of any valid setting */
+ cost_min = (((u64)input_frame_width) << 32) + cr;
+ onw = truncate(0, ((u64)maximal_stripe_width), output_f);
+ if (output_frame_width - onw == 1)
+ onw -= output_f; /* => onw and output_frame_width-1-onw are positive */
+ inw = truncate(0, onw * irr_opt, input_f);
+ /* this is the maximal inw which allows the same resizing ratio */
+ /* in both stripes */
+ onw = truncate(1, inw * rr_opt, output_f);
+ do {
+ div = _do_div((((u64)(irr_steps * inw)) << 32), onw);
+ left->irr = truncate(0, div, 1);
+ div = _do_div((((u64)(onw * left->irr)) << 32),
+ irr_steps);
+ dinw = (((u64)inw) << 32) - div;
+
+ div = _do_div((((u64)((output_frame_width - 1 - onw) * left->irr)) <<
+ 32), irr_steps);
+
+ difwl = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
+
+ cost = difwl + (((u64)(cr * dinw)) >> 32);
+
+ if (cost < cost_min) {
+ inw_best = inw;
+ cost_min = cost;
+ }
+
+ inw -= input_f;
+ onw = truncate(1, inw * rr_opt, output_f);
+ /* This is the minimal onw which allows the same resizing ratio */
+ /* in both stripes */
+ } while (onw >= onw_min);
+
+ inw = inw_best;
+ onw = truncate(1, inw * rr_opt, output_f);
+ div = _do_div((((u64)(irr_steps * inw)) << 32), onw);
+ left->irr = truncate(0, div, 1);
+
+ left->output_width = onw;
+ right->output_width = output_frame_width - onw;
+ /* These are valid assignments for output_width, */
+ /* assuming output_f is a multiple of output_m */
+ left->input_width = truncate(1, ((u64)(inw + 1)) << 32, input_m);
+ right->input_width = truncate(1, ((u64)(input_frame_width - inw)) <<
+ 32, input_m);
+
+ div = _do_div((((u64)(irr_steps * (input_frame_width - 1 - inw))) <<
+ 32), (right->output_width - 1));
+ right->irr = truncate(0, div, 1);
+ temp = truncate(0, ((u64)left->irr) * ((((u64)1) << 32) + dirr), 1);
+ if (temp < right->irr)
+ right->irr = temp;
+ div = _do_div(((u64)((right->output_width - 1) * right->irr) <<
+ 32), irr_steps);
+ difwr = (u64)(input_frame_width - 1 - inw) - div;
+
+
+ div = _do_div((difwr + (((u64)input_f) << 32)), 2);
+ left->input_column = truncate(0, div, input_f);
+
+ /* This splits the truncated input columns evenly */
+ /* between the left and right margins */
+ right->input_column = left->input_column + inw;
+ left->output_column = 0;
+ right->output_column = onw;
+ }
+ return status;
+}
+EXPORT_SYMBOL(ipu_calc_stripes_sizes);
diff --git a/drivers/mxc/ipu/ipu_common.c b/drivers/mxc/ipu/ipu_common.c
new file mode 100644
index 000000000000..bcfea6c85938
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_common.c
@@ -0,0 +1,1902 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_common.c
+ *
+ * @brief This file contains the IPU driver common API functions.
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+/*
+ * This type definition is used to define a node in the GPIO interrupt queue for
+ * registered interrupts for GPIO pins. Each node contains the GPIO signal number
+ * associated with the ISR and the actual ISR function pointer.
+ */
+struct ipu_irq_node {
+ irqreturn_t(*handler) (int, void *); /*!< the ISR */
+ const char *name; /*!< device associated with the interrupt */
+ void *dev_id; /*!< some unique information for the ISR */
+ __u32 flags; /*!< not used */
+};
+
+/* Globals */
+struct clk *g_ipu_clk;
+struct clk *g_ipu_csi_clk;
+static struct clk *dfm_clk;
+int g_ipu_irq[2];
+int g_ipu_hw_rev;
+bool g_sec_chan_en[21];
+uint32_t g_channel_init_mask;
+DEFINE_SPINLOCK(ipu_lock);
+struct device *g_ipu_dev;
+
+static struct ipu_irq_node ipu_irq_list[IPU_IRQ_COUNT];
+static const char driver_name[] = "mxc_ipu";
+
+static uint32_t g_ipu_config = 0;
+static uint32_t g_channel_init_mask_backup = 0;
+static bool g_csi_used = false;
+
+/* Static functions */
+static irqreturn_t ipu_irq_handler(int irq, void *desc);
+static void _ipu_pf_init(ipu_channel_params_t * params);
+static void _ipu_pf_uninit(void);
+
+inline static uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type)
+{
+ return ((type == IPU_INPUT_BUFFER) ? ((uint32_t) ch & 0xFF) :
+ ((type == IPU_OUTPUT_BUFFER) ? (((uint32_t) ch >> 8) & 0xFF)
+ : (((uint32_t) ch >> 16) & 0xFF)));
+};
+
+inline static uint32_t DMAParamAddr(uint32_t dma_ch)
+{
+ return (0x10000 | (dma_ch << 4));
+};
+
+/*!
+ * This function is called by the driver framework to initialize the IPU
+ * hardware.
+ *
+ * @param dev The device structure for the IPU passed in by the framework.
+ *
+ * @return This function returns 0 on success or negative error code on error
+ */
+static
+int ipu_probe(struct platform_device *pdev)
+{
+// struct platform_device *pdev = to_platform_device(dev);
+ struct mxc_ipu_config *ipu_conf = pdev->dev.platform_data;
+
+ spin_lock_init(&ipu_lock);
+
+ g_ipu_dev = &pdev->dev;
+ g_ipu_hw_rev = ipu_conf->rev;
+
+ /* Register IPU interrupts */
+ g_ipu_irq[0] = platform_get_irq(pdev, 0);
+ if (g_ipu_irq[0] < 0)
+ return -EINVAL;
+
+ if (request_irq(g_ipu_irq[0], ipu_irq_handler, 0, driver_name, 0) != 0) {
+ dev_err(g_ipu_dev, "request SYNC interrupt failed\n");
+ return -EBUSY;
+ }
+ /* Some platforms have 2 IPU interrupts */
+ g_ipu_irq[1] = platform_get_irq(pdev, 1);
+ if (g_ipu_irq[1] >= 0) {
+ if (request_irq
+ (g_ipu_irq[1], ipu_irq_handler, 0, driver_name, 0) != 0) {
+ dev_err(g_ipu_dev, "request ERR interrupt failed\n");
+ return -EBUSY;
+ }
+ }
+
+ /* Enable IPU and CSI clocks */
+ /* Get IPU clock freq */
+ g_ipu_clk = clk_get(&pdev->dev, "ipu_clk");
+ dev_dbg(g_ipu_dev, "ipu_clk = %lu\n", clk_get_rate(g_ipu_clk));
+
+ g_ipu_csi_clk = clk_get(&pdev->dev, "csi_clk");
+
+ dfm_clk = clk_get(NULL, "dfm_clk");
+
+ clk_enable(g_ipu_clk);
+
+ /* resetting the CONF register of the IPU */
+ __raw_writel(0x00000000, IPU_CONF);
+
+ __raw_writel(0x00100010L, DI_HSP_CLK_PER);
+
+ /* Set SDC refresh channels as high priority */
+ __raw_writel(0x0000C000L, IDMAC_CHA_PRI);
+
+ /* Set to max back to back burst requests */
+ __raw_writel(0x00000000L, IDMAC_CONF);
+
+ register_ipu_device();
+
+ return 0;
+}
+
+/*!
+ * This function is called to initialize a logical IPU channel.
+ *
+ * @param channel Input parameter for the logical channel ID to initalize.
+ *
+ * @param params Input parameter containing union of channel initialization
+ * parameters.
+ *
+ * @return This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t * params)
+{
+ uint32_t ipu_conf;
+ uint32_t reg;
+ unsigned long lock_flags;
+
+ dev_dbg(g_ipu_dev, "init channel = %d\n", IPU_CHAN_ID(channel));
+
+ if ((channel != MEM_SDC_BG) && (channel != MEM_SDC_FG) &&
+ (channel != MEM_ROT_ENC_MEM) && (channel != MEM_ROT_VF_MEM) &&
+ (channel != MEM_ROT_PP_MEM) && (channel != CSI_MEM)
+ && (params == NULL)) {
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ ipu_conf = __raw_readl(IPU_CONF);
+ if (ipu_conf == 0) {
+ clk_enable(g_ipu_clk);
+ }
+
+ if (g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) {
+ dev_err(g_ipu_dev, "Warning: channel already initialized %d\n",
+ IPU_CHAN_ID(channel));
+ }
+
+ switch (channel) {
+ case CSI_PRP_VF_MEM:
+ reg = __raw_readl(IPU_FS_PROC_FLOW);
+ __raw_writel(reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW);
+
+ if (params->mem_prp_vf_mem.graphics_combine_en)
+ g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+
+ _ipu_ic_init_prpvf(params, true);
+ break;
+ case CSI_PRP_VF_ADC:
+ reg = __raw_readl(IPU_FS_PROC_FLOW);
+ __raw_writel(reg | (FS_DEST_ADC << FS_PRPVF_DEST_SEL_OFFSET),
+ IPU_FS_PROC_FLOW);
+
+ _ipu_adc_init_channel(CSI_PRP_VF_ADC,
+ params->csi_prp_vf_adc.disp,
+ WriteTemplateNonSeq,
+ params->csi_prp_vf_adc.out_left,
+ params->csi_prp_vf_adc.out_top);
+
+ _ipu_ic_init_prpvf(params, true);
+ break;
+ case MEM_PRP_VF_MEM:
+ reg = __raw_readl(IPU_FS_PROC_FLOW);
+ __raw_writel(reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW);
+
+ if (params->mem_prp_vf_mem.graphics_combine_en)
+ g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+
+ _ipu_ic_init_prpvf(params, false);
+ break;
+ case MEM_ROT_VF_MEM:
+ _ipu_ic_init_rotate_vf(params);
+ break;
+ case CSI_PRP_ENC_MEM:
+ reg = __raw_readl(IPU_FS_PROC_FLOW);
+ __raw_writel(reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW);
+ _ipu_ic_init_prpenc(params, true);
+ break;
+ case MEM_PRP_ENC_MEM:
+ reg = __raw_readl(IPU_FS_PROC_FLOW);
+ __raw_writel(reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW);
+ _ipu_ic_init_prpenc(params, false);
+ break;
+ case MEM_ROT_ENC_MEM:
+ _ipu_ic_init_rotate_enc(params);
+ break;
+ case MEM_PP_ADC:
+ reg = __raw_readl(IPU_FS_PROC_FLOW);
+ __raw_writel(reg | (FS_DEST_ADC << FS_PP_DEST_SEL_OFFSET),
+ IPU_FS_PROC_FLOW);
+
+ _ipu_adc_init_channel(MEM_PP_ADC, params->mem_pp_adc.disp,
+ WriteTemplateNonSeq,
+ params->mem_pp_adc.out_left,
+ params->mem_pp_adc.out_top);
+
+ if (params->mem_pp_adc.graphics_combine_en)
+ g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+
+ _ipu_ic_init_pp(params);
+ break;
+ case MEM_PP_MEM:
+ if (params->mem_pp_mem.graphics_combine_en)
+ g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+
+ _ipu_ic_init_pp(params);
+ break;
+ case MEM_ROT_PP_MEM:
+ _ipu_ic_init_rotate_pp(params);
+ break;
+ case CSI_MEM:
+ _ipu_ic_init_csi(params);
+ break;
+
+ case MEM_PF_Y_MEM:
+ case MEM_PF_U_MEM:
+ case MEM_PF_V_MEM:
+ /* Enable PF block */
+ _ipu_pf_init(params);
+ break;
+
+ case MEM_SDC_BG:
+ break;
+ case MEM_SDC_FG:
+ break;
+ case ADC_SYS1:
+ _ipu_adc_init_channel(ADC_SYS1, params->adc_sys1.disp,
+ params->adc_sys1.ch_mode,
+ params->adc_sys1.out_left,
+ params->adc_sys1.out_top);
+ break;
+ case ADC_SYS2:
+ _ipu_adc_init_channel(ADC_SYS2, params->adc_sys2.disp,
+ params->adc_sys2.ch_mode,
+ params->adc_sys2.out_left,
+ params->adc_sys2.out_top);
+ break;
+ default:
+ dev_err(g_ipu_dev, "Missing channel initialization\n");
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return -EINVAL;
+ }
+
+ /* Enable IPU sub module */
+ g_channel_init_mask |= 1L << IPU_CHAN_ID(channel);
+
+ if (g_channel_init_mask & 0x00000066L) { /*CSI */
+ ipu_conf |= IPU_CONF_CSI_EN;
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ g_csi_used = true;
+ }
+ }
+ if (g_channel_init_mask & 0x00001FFFL) { /*IC */
+ ipu_conf |= IPU_CONF_IC_EN;
+ }
+ if (g_channel_init_mask & 0x00000A10L) { /*ROT */
+ ipu_conf |= IPU_CONF_ROT_EN;
+ }
+ if (g_channel_init_mask & 0x0001C000L) { /*SDC */
+ ipu_conf |= IPU_CONF_SDC_EN | IPU_CONF_DI_EN;
+ }
+ if (g_channel_init_mask & 0x00061140L) { /*ADC */
+ ipu_conf |= IPU_CONF_ADC_EN | IPU_CONF_DI_EN;
+ }
+ if (g_channel_init_mask & 0x00380000L) { /*PF */
+ ipu_conf |= IPU_CONF_PF_EN;
+ }
+ __raw_writel(ipu_conf, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+
+/*!
+ * This function is called to uninitialize a logical IPU channel.
+ *
+ * @param channel Input parameter for the logical channel ID to uninitalize.
+ */
+void ipu_uninit_channel(ipu_channel_t channel)
+{
+ unsigned long lock_flags;
+ uint32_t reg;
+ uint32_t dma, mask = 0;
+ uint32_t ipu_conf;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
+ dev_err(g_ipu_dev, "Channel already uninitialized %d\n",
+ IPU_CHAN_ID(channel));
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return;
+ }
+
+ /* Make sure channel is disabled */
+ /* Get input and output dma channels */
+ dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+ if (dma != IDMA_CHAN_INVALID)
+ mask |= 1UL << dma;
+ dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
+ if (dma != IDMA_CHAN_INVALID)
+ mask |= 1UL << dma;
+ /* Get secondary input dma channel */
+ if (g_sec_chan_en[IPU_CHAN_ID(channel)]) {
+ dma = channel_2_dma(channel, IPU_SEC_INPUT_BUFFER);
+ if (dma != IDMA_CHAN_INVALID) {
+ mask |= 1UL << dma;
+ }
+ }
+ if (mask & __raw_readl(IDMAC_CHA_EN)) {
+ dev_err(g_ipu_dev,
+ "Channel %d is not disabled, disable first\n",
+ IPU_CHAN_ID(channel));
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return;
+ }
+
+ /* Reset the double buffer */
+ reg = __raw_readl(IPU_CHA_DB_MODE_SEL);
+ __raw_writel(reg & ~mask, IPU_CHA_DB_MODE_SEL);
+
+ g_sec_chan_en[IPU_CHAN_ID(channel)] = false;
+
+ switch (channel) {
+ case CSI_MEM:
+ _ipu_ic_uninit_csi();
+ break;
+ case CSI_PRP_VF_ADC:
+ reg = __raw_readl(IPU_FS_PROC_FLOW);
+ __raw_writel(reg & ~FS_PRPVF_DEST_SEL_MASK, IPU_FS_PROC_FLOW);
+
+ _ipu_adc_uninit_channel(CSI_PRP_VF_ADC);
+
+ /* Fall thru */
+ case CSI_PRP_VF_MEM:
+ case MEM_PRP_VF_MEM:
+ _ipu_ic_uninit_prpvf();
+ break;
+ case MEM_PRP_VF_ADC:
+ break;
+ case MEM_ROT_VF_MEM:
+ _ipu_ic_uninit_rotate_vf();
+ break;
+ case CSI_PRP_ENC_MEM:
+ case MEM_PRP_ENC_MEM:
+ _ipu_ic_uninit_prpenc();
+ break;
+ case MEM_ROT_ENC_MEM:
+ _ipu_ic_uninit_rotate_enc();
+ break;
+ case MEM_PP_ADC:
+ reg = __raw_readl(IPU_FS_PROC_FLOW);
+ __raw_writel(reg & ~FS_PP_DEST_SEL_MASK, IPU_FS_PROC_FLOW);
+
+ _ipu_adc_uninit_channel(MEM_PP_ADC);
+
+ /* Fall thru */
+ case MEM_PP_MEM:
+ _ipu_ic_uninit_pp();
+ break;
+ case MEM_ROT_PP_MEM:
+ _ipu_ic_uninit_rotate_pp();
+ break;
+
+ case MEM_PF_Y_MEM:
+ _ipu_pf_uninit();
+ break;
+ case MEM_PF_U_MEM:
+ case MEM_PF_V_MEM:
+ break;
+
+ case MEM_SDC_BG:
+ break;
+ case MEM_SDC_FG:
+ break;
+ case ADC_SYS1:
+ _ipu_adc_uninit_channel(ADC_SYS1);
+ break;
+ case ADC_SYS2:
+ _ipu_adc_uninit_channel(ADC_SYS2);
+ break;
+ case MEM_SDC_MASK:
+ case CHAN_NONE:
+ break;
+ }
+
+ g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel));
+
+ ipu_conf = __raw_readl(IPU_CONF);
+ if ((g_channel_init_mask & 0x00000066L) == 0) { /*CSI */
+ ipu_conf &= ~IPU_CONF_CSI_EN;
+ }
+ if ((g_channel_init_mask & 0x00001FFFL) == 0) { /*IC */
+ ipu_conf &= ~IPU_CONF_IC_EN;
+ }
+ if ((g_channel_init_mask & 0x00000A10L) == 0) { /*ROT */
+ ipu_conf &= ~IPU_CONF_ROT_EN;
+ }
+ if ((g_channel_init_mask & 0x0001C000L) == 0) { /*SDC */
+ ipu_conf &= ~IPU_CONF_SDC_EN;
+ }
+ if ((g_channel_init_mask & 0x00061140L) == 0) { /*ADC */
+ ipu_conf &= ~IPU_CONF_ADC_EN;
+ }
+ if ((g_channel_init_mask & 0x0007D140L) == 0) { /*DI */
+ ipu_conf &= ~IPU_CONF_DI_EN;
+ }
+ if ((g_channel_init_mask & 0x00380000L) == 0) { /*PF */
+ ipu_conf &= ~IPU_CONF_PF_EN;
+ }
+ __raw_writel(ipu_conf, IPU_CONF);
+ if (ipu_conf == 0) {
+ clk_disable(g_ipu_clk);
+ }
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * This function is called to initialize a buffer for logical IPU channel.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param type Input parameter which buffer to initialize.
+ *
+ * @param pixel_fmt Input parameter for pixel format of buffer. Pixel
+ * format is a FOURCC ASCII code.
+ *
+ * @param width Input parameter for width of buffer in pixels.
+ *
+ * @param height Input parameter for height of buffer in pixels.
+ *
+ * @param stride Input parameter for stride length of buffer
+ * in pixels.
+ *
+ * @param rot_mode Input parameter for rotation setting of buffer.
+ * A rotation setting other than \b IPU_ROTATE_VERT_FLIP
+ * should only be used for input buffers of rotation
+ * channels.
+ *
+ * @param phyaddr_0 Input parameter buffer 0 physical address.
+ *
+ * @param phyaddr_1 Input parameter buffer 1 physical address.
+ * Setting this to a value other than NULL enables
+ * double buffering mode.
+ *
+ * @param u private u offset for additional cropping,
+ * zero if not used.
+ *
+ * @param v private v offset for additional cropping,
+ * zero if not used.
+ *
+ * @return This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t pixel_fmt,
+ uint16_t width, uint16_t height,
+ uint32_t stride,
+ ipu_rotate_mode_t rot_mode,
+ dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+ uint32_t u, uint32_t v)
+{
+ uint32_t params[10];
+ unsigned long lock_flags;
+ uint32_t reg;
+ uint32_t dma_chan;
+
+ dma_chan = channel_2_dma(channel, type);
+
+ if (stride < width * bytes_per_pixel(pixel_fmt))
+ stride = width * bytes_per_pixel(pixel_fmt);
+
+ if (dma_chan == IDMA_CHAN_INVALID)
+ return -EINVAL;
+
+ if (stride % 4) {
+ dev_err(g_ipu_dev,
+ "Stride must be 32-bit aligned, stride = %d\n", stride);
+ return -EINVAL;
+ }
+ /* IC channels' width must be multiple of 8 pixels */
+ if ((dma_chan <= 13) && (width % 8)) {
+ dev_err(g_ipu_dev, "width must be 8 pixel multiple\n");
+ return -EINVAL;
+ }
+ /* Build parameter memory data for DMA channel */
+ _ipu_ch_param_set_size(params, pixel_fmt, width, height, stride, u, v);
+ _ipu_ch_param_set_buffer(params, phyaddr_0, phyaddr_1);
+ _ipu_ch_param_set_rotation(params, rot_mode);
+ /* Some channels (rotation) have restriction on burst length */
+ if ((dma_chan == 10) || (dma_chan == 11) || (dma_chan == 13)) {
+ _ipu_ch_param_set_burst_size(params, 8);
+ } else if (dma_chan == 24) { /* PF QP channel */
+ _ipu_ch_param_set_burst_size(params, 4);
+ } else if (dma_chan == 25) { /* PF H264 BS channel */
+ _ipu_ch_param_set_burst_size(params, 16);
+ } else if (((dma_chan == 14) || (dma_chan == 15)) &&
+ pixel_fmt == IPU_PIX_FMT_RGB565) {
+ _ipu_ch_param_set_burst_size(params, 16);
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ _ipu_write_param_mem(DMAParamAddr(dma_chan), params, 10);
+
+ reg = __raw_readl(IPU_CHA_DB_MODE_SEL);
+ if (phyaddr_1) {
+ reg |= 1UL << dma_chan;
+ } else {
+ reg &= ~(1UL << dma_chan);
+ }
+ __raw_writel(reg, IPU_CHA_DB_MODE_SEL);
+
+ /* Reset to buffer 0 */
+ __raw_writel(1UL << dma_chan, IPU_CHA_CUR_BUF);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+
+/*!
+ * This function is called to update the physical address of a buffer for
+ * a logical IPU channel.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param type Input parameter which buffer to initialize.
+ *
+ * @param bufNum Input parameter for which buffer number to update.
+ * 0 or 1 are the only valid values.
+ *
+ * @param phyaddr Input parameter buffer physical address.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail. This function will fail if the buffer is set to ready.
+ */
+int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t bufNum, dma_addr_t phyaddr)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+ uint32_t dma_chan = channel_2_dma(channel, type);
+
+ if (dma_chan == IDMA_CHAN_INVALID)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if (bufNum == 0) {
+ reg = __raw_readl(IPU_CHA_BUF0_RDY);
+ if (reg & (1UL << dma_chan)) {
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return -EACCES;
+ }
+ __raw_writel(DMAParamAddr(dma_chan) + 0x0008UL, IPU_IMA_ADDR);
+ __raw_writel(phyaddr, IPU_IMA_DATA);
+ } else {
+ reg = __raw_readl(IPU_CHA_BUF1_RDY);
+ if (reg & (1UL << dma_chan)) {
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return -EACCES;
+ }
+ __raw_writel(DMAParamAddr(dma_chan) + 0x0009UL, IPU_IMA_ADDR);
+ __raw_writel(phyaddr, IPU_IMA_DATA);
+ }
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ dev_dbg(g_ipu_dev, "IPU: update IDMA ch %d buf %d = 0x%08X\n",
+ dma_chan, bufNum, phyaddr);
+ return 0;
+}
+
+/*!
+ * This function is called to initialize a buffer for logical IPU channel.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param type Input parameter which buffer to initialize.
+ *
+ * @param pixel_fmt Input parameter for pixel format of buffer.
+ * Pixel format is a FOURCC ASCII code.
+ *
+ * @param width Input parameter for width of buffer in pixels.
+ *
+ * @param height Input parameter for height of buffer in pixels.
+ *
+ * @param stride Input parameter for stride length of buffer
+ * in pixels.
+ *
+ * @param u predefined private u offset for additional cropping,
+ * zero if not used.
+ *
+ * @param v predefined private v offset for additional cropping,
+ * zero if not used.
+ *
+ * @param vertical_offset vertical offset for Y coordinate
+ * in the existed frame
+ *
+ *
+ * @param horizontal_offset horizontal offset for X coordinate
+ * in the existed frame
+ *
+ *
+ * @return Returns 0 on success or negative error code on fail
+ * This function will fail if any buffer is set to ready.
+ */
+
+int32_t ipu_update_channel_offset(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t pixel_fmt,
+ uint16_t width, uint16_t height,
+ uint32_t stride,
+ uint32_t u, uint32_t v,
+ uint32_t vertical_offset, uint32_t horizontal_offset)
+{
+ uint32_t reg;
+ int ret = 0;
+ unsigned long lock_flags;
+ uint32_t dma_chan = channel_2_dma(channel, type);
+
+ if (dma_chan == IDMA_CHAN_INVALID)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ ret = -EACCES;
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return ret;
+}
+EXPORT_SYMBOL(ipu_update_channel_offset);
+
+/*!
+ * This function is called to set a channel's buffer as ready.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param type Input parameter which buffer to initialize.
+ *
+ * @param bufNum Input parameter for which buffer number set to
+ * ready state.
+ *
+ * @return This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_select_buffer(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t bufNum)
+{
+ uint32_t dma_chan = channel_2_dma(channel, type);
+
+ if (dma_chan == IDMA_CHAN_INVALID)
+ return -EINVAL;
+
+ if (bufNum == 0) {
+ /*Mark buffer 0 as ready. */
+ __raw_writel(1UL << dma_chan, IPU_CHA_BUF0_RDY);
+ } else {
+ /*Mark buffer 1 as ready. */
+ __raw_writel(1UL << dma_chan, IPU_CHA_BUF1_RDY);
+ }
+ return 0;
+}
+
+/*!
+ * This function links 2 channels together for automatic frame
+ * synchronization. The output of the source channel is linked to the input of
+ * the destination channel.
+ *
+ * @param src_ch Input parameter for the logical channel ID of
+ * the source channel.
+ *
+ * @param dest_ch Input parameter for the logical channel ID of
+ * the destination channel.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int32_t ipu_link_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch)
+{
+ unsigned long lock_flags;
+ uint32_t out_dma;
+ uint32_t in_dma;
+ bool isProc;
+ uint32_t value;
+ uint32_t mask;
+ uint32_t offset;
+ uint32_t fs_proc_flow;
+ uint32_t fs_disp_flow;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ fs_proc_flow = __raw_readl(IPU_FS_PROC_FLOW);
+ fs_disp_flow = __raw_readl(IPU_FS_DISP_FLOW);
+
+ out_dma = (1UL << channel_2_dma(src_ch, IPU_OUTPUT_BUFFER));
+ in_dma = (1UL << channel_2_dma(dest_ch, IPU_INPUT_BUFFER));
+
+ /* PROCESS THE OUTPUT DMA CH */
+ switch (out_dma) {
+ /*VF-> */
+ case IDMA_IC_1:
+ pr_debug("Link VF->");
+ isProc = true;
+ mask = FS_PRPVF_DEST_SEL_MASK;
+ offset = FS_PRPVF_DEST_SEL_OFFSET;
+ value = (in_dma == IDMA_IC_11) ? FS_DEST_ROT : /*->VF_ROT */
+ (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 : /* ->ADC1 */
+ (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 : /* ->ADC2 */
+ (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG : /*->SDC_BG */
+ (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG : /*->SDC_FG */
+ (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 : /*->ADC1 */
+ /* ->ADCDirect */
+ 0;
+ break;
+
+ /*VF_ROT-> */
+ case IDMA_IC_9:
+ pr_debug("Link VF_ROT->");
+ isProc = true;
+ mask = FS_PRPVF_ROT_DEST_SEL_MASK;
+ offset = FS_PRPVF_ROT_DEST_SEL_OFFSET;
+ value = (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 : /*->ADC1 */
+ (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 : /* ->ADC2 */
+ (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG : /*->SDC_BG */
+ (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG : /*->SDC_FG */
+ 0;
+ break;
+
+ /*ENC-> */
+ case IDMA_IC_0:
+ pr_debug("Link ENC->");
+ isProc = true;
+ mask = 0;
+ offset = 0;
+ value = (in_dma == IDMA_IC_10) ? FS_PRPENC_DEST_SEL : /*->ENC_ROT */
+ 0;
+ break;
+
+ /*PP-> */
+ case IDMA_IC_2:
+ pr_debug("Link PP->");
+ isProc = true;
+ mask = FS_PP_DEST_SEL_MASK;
+ offset = FS_PP_DEST_SEL_OFFSET;
+ value = (in_dma == IDMA_IC_13) ? FS_DEST_ROT : /*->PP_ROT */
+ (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 : /* ->ADC1 */
+ (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 : /* ->ADC2 */
+ (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG : /*->SDC_BG */
+ (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG : /*->SDC_FG */
+ /* ->ADCDirect */
+ 0;
+ break;
+
+ /*PP_ROT-> */
+ case IDMA_IC_12:
+ pr_debug("Link PP_ROT->");
+ isProc = true;
+ mask = FS_PP_ROT_DEST_SEL_MASK;
+ offset = FS_PP_ROT_DEST_SEL_OFFSET;
+ value = (in_dma == IDMA_IC_5) ? FS_DEST_ROT : /*->PP */
+ (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 : /* ->ADC1 */
+ (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 : /* ->ADC2 */
+ (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG : /*->SDC_BG */
+ (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG : /*->SDC_FG */
+ 0;
+ break;
+
+ /*PF-> */
+ case IDMA_PF_Y_OUT:
+ case IDMA_PF_U_OUT:
+ case IDMA_PF_V_OUT:
+ pr_debug("Link PF->");
+ isProc = true;
+ mask = FS_PF_DEST_SEL_MASK;
+ offset = FS_PF_DEST_SEL_OFFSET;
+ value = (in_dma == IDMA_IC_5) ? FS_PF_DEST_PP :
+ (in_dma == IDMA_IC_13) ? FS_PF_DEST_ROT : 0;
+ break;
+
+ /* Invalid Chainings: ENC_ROT-> */
+ default:
+ pr_debug("Link Invalid->");
+ value = 0;
+ break;
+
+ }
+
+ if (value) {
+ if (isProc) {
+ fs_proc_flow &= ~mask;
+ fs_proc_flow |= (value << offset);
+ } else {
+ fs_disp_flow &= ~mask;
+ fs_disp_flow |= (value << offset);
+ }
+ } else {
+ dev_err(g_ipu_dev, "Invalid channel chaining %d -> %d\n",
+ out_dma, in_dma);
+ return -EINVAL;
+ }
+
+ /* PROCESS THE INPUT DMA CH */
+ switch (in_dma) {
+ /* ->VF_ROT */
+ case IDMA_IC_11:
+ pr_debug("VF_ROT\n");
+ isProc = true;
+ mask = 0;
+ offset = 0;
+ value = (out_dma == IDMA_IC_1) ? FS_PRPVF_ROT_SRC_SEL : /*VF-> */
+ 0;
+ break;
+
+ /* ->ENC_ROT */
+ case IDMA_IC_10:
+ pr_debug("ENC_ROT\n");
+ isProc = true;
+ mask = 0;
+ offset = 0;
+ value = (out_dma == IDMA_IC_0) ? FS_PRPENC_ROT_SRC_SEL : /*ENC-> */
+ 0;
+ break;
+
+ /* ->PP */
+ case IDMA_IC_5:
+ pr_debug("PP\n");
+ isProc = true;
+ mask = FS_PP_SRC_SEL_MASK;
+ offset = FS_PP_SRC_SEL_OFFSET;
+ value = (out_dma == IDMA_PF_Y_OUT) ? FS_PP_SRC_PF : /*PF-> */
+ (out_dma == IDMA_PF_U_OUT) ? FS_PP_SRC_PF : /*PF-> */
+ (out_dma == IDMA_PF_V_OUT) ? FS_PP_SRC_PF : /*PF-> */
+ (out_dma == IDMA_IC_12) ? FS_PP_SRC_ROT : /*PP_ROT-> */
+ 0;
+ break;
+
+ /* ->PP_ROT */
+ case IDMA_IC_13:
+ pr_debug("PP_ROT\n");
+ isProc = true;
+ mask = FS_PP_ROT_SRC_SEL_MASK;
+ offset = FS_PP_ROT_SRC_SEL_OFFSET;
+ value = (out_dma == IDMA_PF_Y_OUT) ? FS_PP_SRC_PF : /*PF-> */
+ (out_dma == IDMA_PF_U_OUT) ? FS_PP_SRC_PF : /*PF-> */
+ (out_dma == IDMA_PF_V_OUT) ? FS_PP_SRC_PF : /*PF-> */
+ (out_dma == IDMA_IC_2) ? FS_ROT_SRC_PP : /*PP-> */
+ 0;
+ break;
+
+ /* ->SDC_BG */
+ case IDMA_SDC_BG:
+ pr_debug("SDC_BG\n");
+ isProc = false;
+ mask = FS_SDC_BG_SRC_SEL_MASK;
+ offset = FS_SDC_BG_SRC_SEL_OFFSET;
+ value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF : /*VF_ROT-> */
+ (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP : /*PP_ROT-> */
+ (out_dma == IDMA_IC_1) ? FS_SRC_VF : /*VF-> */
+ (out_dma == IDMA_IC_2) ? FS_SRC_PP : /*PP-> */
+ 0;
+ break;
+
+ /* ->SDC_FG */
+ case IDMA_SDC_FG:
+ pr_debug("SDC_FG\n");
+ isProc = false;
+ mask = FS_SDC_FG_SRC_SEL_MASK;
+ offset = FS_SDC_FG_SRC_SEL_OFFSET;
+ value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF : /*VF_ROT-> */
+ (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP : /*PP_ROT-> */
+ (out_dma == IDMA_IC_1) ? FS_SRC_VF : /*VF-> */
+ (out_dma == IDMA_IC_2) ? FS_SRC_PP : /*PP-> */
+ 0;
+ break;
+
+ /* ->ADC1 */
+ case IDMA_ADC_SYS1_WR:
+ pr_debug("ADC_SYS1\n");
+ isProc = false;
+ mask = FS_ADC1_SRC_SEL_MASK;
+ offset = FS_ADC1_SRC_SEL_OFFSET;
+ value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF : /*VF_ROT-> */
+ (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP : /*PP_ROT-> */
+ (out_dma == IDMA_IC_1) ? FS_SRC_VF : /*VF-> */
+ (out_dma == IDMA_IC_2) ? FS_SRC_PP : /*PP-> */
+ 0;
+ break;
+
+ /* ->ADC2 */
+ case IDMA_ADC_SYS2_WR:
+ pr_debug("ADC_SYS2\n");
+ isProc = false;
+ mask = FS_ADC2_SRC_SEL_MASK;
+ offset = FS_ADC2_SRC_SEL_OFFSET;
+ value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF : /*VF_ROT-> */
+ (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP : /*PP_ROT-> */
+ (out_dma == IDMA_IC_1) ? FS_SRC_VF : /*VF-> */
+ (out_dma == IDMA_IC_2) ? FS_SRC_PP : /*PP-> */
+ 0;
+ break;
+
+ /*Invalid chains: */
+ /* ->ENC, ->VF, ->PF, ->VF_COMBINE, ->PP_COMBINE */
+ default:
+ pr_debug("Invalid\n");
+ value = 0;
+ break;
+
+ }
+
+ if (value) {
+ if (isProc) {
+ fs_proc_flow &= ~mask;
+ fs_proc_flow |= (value << offset);
+ } else {
+ fs_disp_flow &= ~mask;
+ fs_disp_flow |= (value << offset);
+ }
+ } else {
+ dev_err(g_ipu_dev, "Invalid channel chaining %d -> %d\n",
+ out_dma, in_dma);
+ return -EINVAL;
+ }
+
+ __raw_writel(fs_proc_flow, IPU_FS_PROC_FLOW);
+ __raw_writel(fs_disp_flow, IPU_FS_DISP_FLOW);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return 0;
+}
+
+/*!
+ * This function unlinks 2 channels and disables automatic frame
+ * synchronization.
+ *
+ * @param src_ch Input parameter for the logical channel ID of
+ * the source channel.
+ *
+ * @param dest_ch Input parameter for the logical channel ID of
+ * the destination channel.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int32_t ipu_unlink_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch)
+{
+ unsigned long lock_flags;
+ uint32_t out_dma;
+ uint32_t in_dma;
+ uint32_t fs_proc_flow;
+ uint32_t fs_disp_flow;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ fs_proc_flow = __raw_readl(IPU_FS_PROC_FLOW);
+ fs_disp_flow = __raw_readl(IPU_FS_DISP_FLOW);
+
+ out_dma = (1UL << channel_2_dma(src_ch, IPU_OUTPUT_BUFFER));
+ in_dma = (1UL << channel_2_dma(dest_ch, IPU_INPUT_BUFFER));
+
+ /*clear the src_ch's output destination */
+ switch (out_dma) {
+ /*VF-> */
+ case IDMA_IC_1:
+ pr_debug("Unlink VF->");
+ fs_proc_flow &= ~FS_PRPVF_DEST_SEL_MASK;
+ break;
+
+ /*VF_ROT-> */
+ case IDMA_IC_9:
+ pr_debug("Unlink VF_Rot->");
+ fs_proc_flow &= ~FS_PRPVF_ROT_DEST_SEL_MASK;
+ break;
+
+ /*ENC-> */
+ case IDMA_IC_0:
+ pr_debug("Unlink ENC->");
+ fs_proc_flow &= ~FS_PRPENC_DEST_SEL;
+ break;
+
+ /*PP-> */
+ case IDMA_IC_2:
+ pr_debug("Unlink PP->");
+ fs_proc_flow &= ~FS_PP_DEST_SEL_MASK;
+ break;
+
+ /*PP_ROT-> */
+ case IDMA_IC_12:
+ pr_debug("Unlink PP_ROT->");
+ fs_proc_flow &= ~FS_PP_ROT_DEST_SEL_MASK;
+ break;
+
+ /*PF-> */
+ case IDMA_PF_Y_OUT:
+ case IDMA_PF_U_OUT:
+ case IDMA_PF_V_OUT:
+ pr_debug("Unlink PF->");
+ fs_proc_flow &= ~FS_PF_DEST_SEL_MASK;
+ break;
+
+ default: /*ENC_ROT-> */
+ pr_debug("Unlink Invalid->");
+ break;
+ }
+
+ /*clear the dest_ch's input source */
+ switch (in_dma) {
+ /*->VF_ROT*/
+ case IDMA_IC_11:
+ pr_debug("VF_ROT\n");
+ fs_proc_flow &= ~FS_PRPVF_ROT_SRC_SEL;
+ break;
+
+ /*->Enc_ROT*/
+ case IDMA_IC_10:
+ pr_debug("ENC_ROT\n");
+ fs_proc_flow &= ~FS_PRPENC_ROT_SRC_SEL;
+ break;
+
+ /*->PP*/
+ case IDMA_IC_5:
+ pr_debug("PP\n");
+ fs_proc_flow &= ~FS_PP_SRC_SEL_MASK;
+ break;
+
+ /*->PP_ROT*/
+ case IDMA_IC_13:
+ pr_debug("PP_ROT\n");
+ fs_proc_flow &= ~FS_PP_ROT_SRC_SEL_MASK;
+ break;
+
+ /*->SDC_FG*/
+ case IDMA_SDC_FG:
+ pr_debug("SDC_FG\n");
+ fs_disp_flow &= ~FS_SDC_FG_SRC_SEL_MASK;
+ break;
+
+ /*->SDC_BG*/
+ case IDMA_SDC_BG:
+ pr_debug("SDC_BG\n");
+ fs_disp_flow &= ~FS_SDC_BG_SRC_SEL_MASK;
+ break;
+
+ /*->ADC1*/
+ case IDMA_ADC_SYS1_WR:
+ pr_debug("ADC_SYS1\n");
+ fs_disp_flow &= ~FS_ADC1_SRC_SEL_MASK;
+ break;
+
+ /*->ADC2*/
+ case IDMA_ADC_SYS2_WR:
+ pr_debug("ADC_SYS2\n");
+ fs_disp_flow &= ~FS_ADC2_SRC_SEL_MASK;
+ break;
+
+ default: /*->VF, ->ENC, ->VF_COMBINE, ->PP_COMBINE, ->PF*/
+ pr_debug("Invalid\n");
+ break;
+ }
+
+ __raw_writel(fs_proc_flow, IPU_FS_PROC_FLOW);
+ __raw_writel(fs_disp_flow, IPU_FS_DISP_FLOW);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return 0;
+}
+
+/*!
+ * This function check whether a logical channel was enabled.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @return This function returns 1 while request channel is enabled or
+ * 0 for not enabled.
+ */
+int32_t ipu_is_channel_busy(ipu_channel_t channel)
+{
+ uint32_t reg;
+ uint32_t in_dma;
+ uint32_t out_dma;
+
+ out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+ in_dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
+
+ reg = __raw_readl(IDMAC_CHA_EN);
+
+ if (reg & ((1UL << out_dma) | (1UL << in_dma)))
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(ipu_is_channel_busy);
+
+/*!
+ * This function enables a logical channel.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int32_t ipu_enable_channel(ipu_channel_t channel)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+ uint32_t in_dma;
+ uint32_t sec_dma;
+ uint32_t out_dma;
+ uint32_t chan_mask = 0;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(IDMAC_CHA_EN);
+
+ /* Get input and output dma channels */
+ out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+ if (out_dma != IDMA_CHAN_INVALID)
+ reg |= 1UL << out_dma;
+ in_dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
+ if (in_dma != IDMA_CHAN_INVALID)
+ reg |= 1UL << in_dma;
+
+ /* Get secondary input dma channel */
+ if (g_sec_chan_en[IPU_CHAN_ID(channel)]) {
+ sec_dma = channel_2_dma(channel, IPU_SEC_INPUT_BUFFER);
+ if (sec_dma != IDMA_CHAN_INVALID) {
+ reg |= 1UL << sec_dma;
+ }
+ }
+
+ __raw_writel(reg | chan_mask, IDMAC_CHA_EN);
+
+ if (IPU_CHAN_ID(channel) <= IPU_CHAN_ID(MEM_PP_ADC)) {
+ _ipu_ic_enable_task(channel);
+ } else if (channel == MEM_SDC_BG) {
+ dev_dbg(g_ipu_dev, "Initializing SDC BG\n");
+ _ipu_sdc_bg_init(NULL);
+ } else if (channel == MEM_SDC_FG) {
+ dev_dbg(g_ipu_dev, "Initializing SDC FG\n");
+ _ipu_sdc_fg_init(NULL);
+ }
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return 0;
+}
+
+/*!
+ * This function disables a logical channel.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param wait_for_stop Flag to set whether to wait for channel end
+ * of frame or return immediately.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int32_t ipu_disable_channel(ipu_channel_t channel, bool wait_for_stop)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+ uint32_t sec_dma;
+ uint32_t in_dma;
+ uint32_t out_dma;
+ uint32_t chan_mask = 0;
+ uint32_t timeout;
+ uint32_t eof_intr;
+ uint32_t enabled;
+
+ /* Get input and output dma channels */
+ out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+ if (out_dma != IDMA_CHAN_INVALID)
+ chan_mask = 1UL << out_dma;
+ in_dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
+ if (in_dma != IDMA_CHAN_INVALID)
+ chan_mask |= 1UL << in_dma;
+ sec_dma = channel_2_dma(channel, IPU_SEC_INPUT_BUFFER);
+ if (sec_dma != IDMA_CHAN_INVALID)
+ chan_mask |= 1UL << sec_dma;
+
+ if (wait_for_stop && channel != MEM_SDC_FG && channel != MEM_SDC_BG) {
+ timeout = 40;
+ while ((__raw_readl(IDMAC_CHA_BUSY) & chan_mask) ||
+ (_ipu_channel_status(channel) == TASK_STAT_ACTIVE)) {
+ timeout--;
+ msleep(10);
+ if (timeout == 0) {
+ printk
+ (KERN_INFO
+ "MXC IPU: Warning - timeout waiting for channel to stop,\n"
+ "\tbuf0_rdy = 0x%08X, buf1_rdy = 0x%08X\n"
+ "\tbusy = 0x%08X, tstat = 0x%08X\n\tmask = 0x%08X\n",
+ __raw_readl(IPU_CHA_BUF0_RDY),
+ __raw_readl(IPU_CHA_BUF1_RDY),
+ __raw_readl(IDMAC_CHA_BUSY),
+ __raw_readl(IPU_TASKS_STAT), chan_mask);
+ break;
+ }
+ }
+ dev_dbg(g_ipu_dev, "timeout = %d * 10ms\n", 40 - timeout);
+ }
+ /* SDC BG and FG must be disabled before DMA is disabled */
+ if ((channel == MEM_SDC_BG) || (channel == MEM_SDC_FG)) {
+
+ if (channel == MEM_SDC_BG)
+ eof_intr = IPU_IRQ_SDC_BG_EOF;
+ else
+ eof_intr = IPU_IRQ_SDC_FG_EOF;
+
+ /* Wait for any buffer flips to finsh */
+ timeout = 4;
+ while (timeout &&
+ ((__raw_readl(IPU_CHA_BUF0_RDY) & chan_mask) ||
+ (__raw_readl(IPU_CHA_BUF1_RDY) & chan_mask))) {
+ msleep(10);
+ timeout--;
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+ ipu_clear_irq(eof_intr);
+ if (channel == MEM_SDC_BG)
+ enabled = _ipu_sdc_bg_uninit();
+ else
+ enabled = _ipu_sdc_fg_uninit();
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ if (enabled && wait_for_stop) {
+ timeout = 5;
+ } else {
+ timeout = 0;
+ }
+ while (timeout && !ipu_get_irq_status(eof_intr)) {
+ msleep(5);
+ timeout--;
+ }
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ /* Disable IC task */
+ if (IPU_CHAN_ID(channel) <= IPU_CHAN_ID(MEM_PP_ADC)) {
+ _ipu_ic_disable_task(channel);
+ }
+
+ /* Disable DMA channel(s) */
+ reg = __raw_readl(IDMAC_CHA_EN);
+ __raw_writel(reg & ~chan_mask, IDMAC_CHA_EN);
+
+ /* Clear DMA related interrupts */
+ __raw_writel(chan_mask, IPU_INT_STAT_1);
+ __raw_writel(chan_mask, IPU_INT_STAT_2);
+ __raw_writel(chan_mask, IPU_INT_STAT_4);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+
+static
+irqreturn_t ipu_irq_handler(int irq, void *desc)
+{
+ uint32_t line_base = 0;
+ uint32_t line;
+ irqreturn_t result = IRQ_NONE;
+ uint32_t int_stat;
+
+ if (g_ipu_irq[1]) {
+ disable_irq(g_ipu_irq[0]);
+ disable_irq(g_ipu_irq[1]);
+ }
+
+ int_stat = __raw_readl(IPU_INT_STAT_1);
+ int_stat &= __raw_readl(IPU_INT_CTRL_1);
+ __raw_writel(int_stat, IPU_INT_STAT_1);
+ while ((line = ffs(int_stat)) != 0) {
+ int_stat &= ~(1UL << (line - 1));
+ line += line_base - 1;
+ result |=
+ ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
+ }
+
+ line_base = 32;
+ int_stat = __raw_readl(IPU_INT_STAT_2);
+ int_stat &= __raw_readl(IPU_INT_CTRL_2);
+ __raw_writel(int_stat, IPU_INT_STAT_2);
+ while ((line = ffs(int_stat)) != 0) {
+ int_stat &= ~(1UL << (line - 1));
+ line += line_base - 1;
+ result |=
+ ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
+ }
+
+ line_base = 64;
+ int_stat = __raw_readl(IPU_INT_STAT_3);
+ int_stat &= __raw_readl(IPU_INT_CTRL_3);
+ __raw_writel(int_stat, IPU_INT_STAT_3);
+ while ((line = ffs(int_stat)) != 0) {
+ int_stat &= ~(1UL << (line - 1));
+ line += line_base - 1;
+ result |=
+ ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
+ }
+
+ line_base = 96;
+ int_stat = __raw_readl(IPU_INT_STAT_4);
+ int_stat &= __raw_readl(IPU_INT_CTRL_4);
+ __raw_writel(int_stat, IPU_INT_STAT_4);
+ while ((line = ffs(int_stat)) != 0) {
+ int_stat &= ~(1UL << (line - 1));
+ line += line_base - 1;
+ result |=
+ ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
+ }
+
+ line_base = 128;
+ int_stat = __raw_readl(IPU_INT_STAT_5);
+ int_stat &= __raw_readl(IPU_INT_CTRL_5);
+ __raw_writel(int_stat, IPU_INT_STAT_5);
+ while ((line = ffs(int_stat)) != 0) {
+ int_stat &= ~(1UL << (line - 1));
+ line += line_base - 1;
+ result |=
+ ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
+ }
+
+ if (g_ipu_irq[1]) {
+ enable_irq(g_ipu_irq[0]);
+ enable_irq(g_ipu_irq[1]);
+ }
+ return result;
+}
+
+/*!
+ * This function enables the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to enable interrupt for.
+ *
+ */
+void ipu_enable_irq(uint32_t irq)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(IPUIRQ_2_CTRLREG(irq));
+ reg |= IPUIRQ_2_MASK(irq);
+ __raw_writel(reg, IPUIRQ_2_CTRLREG(irq));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * This function disables the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to disable interrupt for.
+ *
+ */
+void ipu_disable_irq(uint32_t irq)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(IPUIRQ_2_CTRLREG(irq));
+ reg &= ~IPUIRQ_2_MASK(irq);
+ __raw_writel(reg, IPUIRQ_2_CTRLREG(irq));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * This function clears the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to clear interrupt for.
+ *
+ */
+void ipu_clear_irq(uint32_t irq)
+{
+ __raw_writel(IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq));
+}
+
+/*!
+ * This function returns the current interrupt status for the specified interrupt
+ * line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to get status for.
+ *
+ * @return Returns true if the interrupt is pending/asserted or false if
+ * the interrupt is not pending.
+ */
+bool ipu_get_irq_status(uint32_t irq)
+{
+ uint32_t reg = __raw_readl(IPUIRQ_2_STATREG(irq));
+
+ if (reg & IPUIRQ_2_MASK(irq))
+ return true;
+ else
+ return false;
+}
+
+/*!
+ * This function registers an interrupt handler function for the specified
+ * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to get status for.
+ *
+ * @param handler Input parameter for address of the handler
+ * function.
+ *
+ * @param irq_flags Flags for interrupt mode. Currently not used.
+ *
+ * @param devname Input parameter for string name of driver
+ * registering the handler.
+ *
+ * @param dev_id Input parameter for pointer of data to be passed
+ * to the handler.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int ipu_request_irq(uint32_t irq,
+ irqreturn_t(*handler) (int, void *),
+ uint32_t irq_flags, const char *devname, void *dev_id)
+{
+ unsigned long lock_flags;
+
+ BUG_ON(irq >= IPU_IRQ_COUNT);
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if (ipu_irq_list[irq].handler != NULL) {
+ dev_err(g_ipu_dev,
+ "ipu_request_irq - handler already installed on irq %d\n",
+ irq);
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return -EINVAL;
+ }
+
+ ipu_irq_list[irq].handler = handler;
+ ipu_irq_list[irq].flags = irq_flags;
+ ipu_irq_list[irq].dev_id = dev_id;
+ ipu_irq_list[irq].name = devname;
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ ipu_enable_irq(irq); /* enable the interrupt */
+
+ return 0;
+}
+
+/*!
+ * This function unregisters an interrupt handler for the specified interrupt
+ * line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to get status for.
+ *
+ * @param dev_id Input parameter for pointer of data to be passed
+ * to the handler. This must match value passed to
+ * ipu_request_irq().
+ *
+ */
+void ipu_free_irq(uint32_t irq, void *dev_id)
+{
+ ipu_disable_irq(irq); /* disable the interrupt */
+
+ if (ipu_irq_list[irq].dev_id == dev_id) {
+ ipu_irq_list[irq].handler = NULL;
+ }
+}
+
+/*!
+ * This function sets the post-filter pause row for h.264 mode.
+ *
+ * @param pause_row The last row to process before pausing.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ *
+ */
+int32_t ipu_pf_set_pause_row(uint32_t pause_row)
+{
+ int32_t retval = 0;
+ uint32_t timeout = 5;
+ unsigned long lock_flags;
+ uint32_t reg;
+
+ reg = __raw_readl(IPU_TASKS_STAT);
+ while ((reg & TSTAT_PF_MASK) && ((reg & TSTAT_PF_H264_PAUSE) == 0)) {
+ timeout--;
+ msleep(5);
+ if (timeout == 0) {
+ dev_err(g_ipu_dev, "PF Timeout - tstat = 0x%08X\n",
+ __raw_readl(IPU_TASKS_STAT));
+ retval = -ETIMEDOUT;
+ goto err0;
+ }
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(PF_CONF);
+
+ /* Set the pause row */
+ if (pause_row) {
+ reg &= ~PF_CONF_PAUSE_ROW_MASK;
+ reg |= PF_CONF_PAUSE_EN | pause_row << PF_CONF_PAUSE_ROW_SHIFT;
+ } else {
+ reg &= ~(PF_CONF_PAUSE_EN | PF_CONF_PAUSE_ROW_MASK);
+ }
+ __raw_writel(reg, PF_CONF);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ err0:
+ return retval;
+}
+
+/* Private functions */
+void _ipu_write_param_mem(uint32_t addr, uint32_t * data, uint32_t numWords)
+{
+ for (; numWords > 0; numWords--) {
+ dev_dbg(g_ipu_dev,
+ "write param mem - addr = 0x%08X, data = 0x%08X\n",
+ addr, *data);
+ __raw_writel(addr, IPU_IMA_ADDR);
+ __raw_writel(*data++, IPU_IMA_DATA);
+ addr++;
+ if ((addr & 0x7) == 5) {
+ addr &= ~0x7; /* set to word 0 */
+ addr += 8; /* increment to next row */
+ }
+ }
+}
+
+static void _ipu_pf_init(ipu_channel_params_t * params)
+{
+ uint32_t reg;
+
+ /*Setup the type of filtering required */
+ switch (params->mem_pf_mem.operation) {
+ case PF_MPEG4_DEBLOCK:
+ case PF_MPEG4_DERING:
+ case PF_MPEG4_DEBLOCK_DERING:
+ g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = true;
+ g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = false;
+ break;
+ case PF_H264_DEBLOCK:
+ g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = true;
+ g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = true;
+ break;
+ default:
+ g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = false;
+ g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = false;
+ return;
+ break;
+ }
+ reg = params->mem_pf_mem.operation;
+ __raw_writel(reg, PF_CONF);
+}
+
+static void _ipu_pf_uninit(void)
+{
+ __raw_writel(0x0L, PF_CONF);
+ g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = false;
+ g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = false;
+}
+
+uint32_t _ipu_channel_status(ipu_channel_t channel)
+{
+ uint32_t stat = 0;
+ uint32_t task_stat_reg = __raw_readl(IPU_TASKS_STAT);
+
+ switch (channel) {
+ case CSI_MEM:
+ stat =
+ (task_stat_reg & TSTAT_CSI2MEM_MASK) >>
+ TSTAT_CSI2MEM_OFFSET;
+ break;
+ case CSI_PRP_VF_ADC:
+ case CSI_PRP_VF_MEM:
+ case MEM_PRP_VF_ADC:
+ case MEM_PRP_VF_MEM:
+ stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET;
+ break;
+ case MEM_ROT_VF_MEM:
+ stat =
+ (task_stat_reg & TSTAT_VF_ROT_MASK) >> TSTAT_VF_ROT_OFFSET;
+ break;
+ case CSI_PRP_ENC_MEM:
+ case MEM_PRP_ENC_MEM:
+ stat = (task_stat_reg & TSTAT_ENC_MASK) >> TSTAT_ENC_OFFSET;
+ break;
+ case MEM_ROT_ENC_MEM:
+ stat =
+ (task_stat_reg & TSTAT_ENC_ROT_MASK) >>
+ TSTAT_ENC_ROT_OFFSET;
+ break;
+ case MEM_PP_ADC:
+ case MEM_PP_MEM:
+ stat = (task_stat_reg & TSTAT_PP_MASK) >> TSTAT_PP_OFFSET;
+ break;
+ case MEM_ROT_PP_MEM:
+ stat =
+ (task_stat_reg & TSTAT_PP_ROT_MASK) >> TSTAT_PP_ROT_OFFSET;
+ break;
+
+ case MEM_PF_Y_MEM:
+ case MEM_PF_U_MEM:
+ case MEM_PF_V_MEM:
+ stat = (task_stat_reg & TSTAT_PF_MASK) >> TSTAT_PF_OFFSET;
+ break;
+ case MEM_SDC_BG:
+ break;
+ case MEM_SDC_FG:
+ break;
+ case ADC_SYS1:
+ stat =
+ (task_stat_reg & TSTAT_ADCSYS1_MASK) >>
+ TSTAT_ADCSYS1_OFFSET;
+ break;
+ case ADC_SYS2:
+ stat =
+ (task_stat_reg & TSTAT_ADCSYS2_MASK) >>
+ TSTAT_ADCSYS2_OFFSET;
+ break;
+ case MEM_SDC_MASK:
+ default:
+ stat = TASK_STAT_IDLE;
+ break;
+ }
+ return stat;
+}
+
+uint32_t bytes_per_pixel(uint32_t fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_GENERIC: /*generic data */
+ case IPU_PIX_FMT_RGB332:
+ case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YUV422P:
+ return 1;
+ break;
+ case IPU_PIX_FMT_RGB565:
+ case IPU_PIX_FMT_YUYV:
+ case IPU_PIX_FMT_UYVY:
+ return 2;
+ break;
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ return 3;
+ break;
+ case IPU_PIX_FMT_GENERIC_32: /*generic data */
+ case IPU_PIX_FMT_BGR32:
+ case IPU_PIX_FMT_RGB32:
+ case IPU_PIX_FMT_ABGR32:
+ return 4;
+ break;
+ default:
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+void ipu_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3])
+{
+ /* TODO */
+}
+EXPORT_SYMBOL(ipu_set_csc_coefficients);
+
+ipu_color_space_t format_to_colorspace(uint32_t fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_RGB565:
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ case IPU_PIX_FMT_BGR32:
+ case IPU_PIX_FMT_RGB32:
+ return RGB;
+ break;
+
+ default:
+ return YCbCr;
+ break;
+ }
+ return RGB;
+
+}
+
+static u32 saved_disp3_time_conf;
+static bool in_lpdr_mode;
+static struct clk *default_ipu_parent_clk;
+
+int ipu_lowpwr_display_enable(void)
+{
+ unsigned long rate, div;
+ struct clk *parent_clk = g_ipu_clk;
+
+ if (in_lpdr_mode || IS_ERR(dfm_clk)) {
+ return -EINVAL;
+ }
+
+ if (g_channel_init_mask != (1L << IPU_CHAN_ID(MEM_SDC_BG))) {
+ dev_err(g_ipu_dev, "LPDR mode requires only SDC BG active.\n");
+ return -EINVAL;
+ }
+
+ default_ipu_parent_clk = clk_get_parent(g_ipu_clk);
+ in_lpdr_mode = true;
+
+ /* Calculate current pixel clock rate */
+ rate = clk_get_rate(g_ipu_clk) * 16;
+ saved_disp3_time_conf = __raw_readl(DI_DISP3_TIME_CONF);
+ div = saved_disp3_time_conf & 0xFFF;
+ rate /= div;
+ rate *= 4; /* min hsp clk is 4x pixel clk */
+
+ /* Initialize DFM clock */
+ rate = clk_round_rate(dfm_clk, rate);
+ clk_set_rate(dfm_clk, rate);
+ clk_enable(dfm_clk);
+
+ /* Wait for next VSYNC */
+ __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC),
+ IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC));
+ while ((__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC)) &
+ IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC)) == 0)
+ msleep_interruptible(1);
+
+ /* Set display clock divider to divide by 4 */
+ __raw_writel(((0x8) << 22) | 0x40, DI_DISP3_TIME_CONF);
+
+ clk_set_parent(parent_clk, dfm_clk);
+
+ return 0;
+}
+
+int ipu_lowpwr_display_disable(void)
+{
+ struct clk *parent_clk = g_ipu_clk;
+
+ if (!in_lpdr_mode || IS_ERR(dfm_clk)) {
+ return -EINVAL;
+ }
+
+ if (g_channel_init_mask != (1L << IPU_CHAN_ID(MEM_SDC_BG))) {
+ dev_err(g_ipu_dev, "LPDR mode requires only SDC BG active.\n");
+ return -EINVAL;
+ }
+
+ in_lpdr_mode = false;
+
+ /* Wait for next VSYNC */
+ __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC),
+ IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC));
+ while ((__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC)) &
+ IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC)) == 0)
+ msleep_interruptible(1);
+
+ __raw_writel(saved_disp3_time_conf, DI_DISP3_TIME_CONF);
+ clk_set_parent(parent_clk, default_ipu_parent_clk);
+ clk_disable(dfm_clk);
+
+ return 0;
+}
+
+static int ipu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ /* work-around for i.Mx31 SR mode after camera related test */
+ if (g_csi_used) {
+ g_ipu_config = __raw_readl(IPU_CONF);
+ clk_enable(g_ipu_csi_clk);
+ __raw_writel(0x51, IPU_CONF);
+ g_channel_init_mask_backup = g_channel_init_mask;
+ g_channel_init_mask |= 2;
+ }
+ } else if (cpu_is_mx35()) {
+ g_ipu_config = __raw_readl(IPU_CONF);
+ /* Disable the clock of display otherwise the backlight cannot
+ * be close after camera/tvin related test */
+ __raw_writel(g_ipu_config & 0xfbf, IPU_CONF);
+ }
+
+ return 0;
+}
+
+static int ipu_resume(struct platform_device *pdev)
+{
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ /* work-around for i.Mx31 SR mode after camera related test */
+ if (g_csi_used) {
+ __raw_writel(g_ipu_config, IPU_CONF);
+ clk_disable(g_ipu_csi_clk);
+ g_channel_init_mask = g_channel_init_mask_backup;
+ }
+ } else if (cpu_is_mx35()) {
+ __raw_writel(g_ipu_config, IPU_CONF);
+ }
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcipu_driver = {
+ .driver = {
+ .name = "mxc_ipu",
+ },
+ .probe = ipu_probe,
+ .suspend = ipu_suspend,
+ .resume = ipu_resume,
+};
+
+int32_t __init ipu_gen_init(void)
+{
+ int32_t ret;
+
+ ret = platform_driver_register(&mxcipu_driver);
+ return 0;
+}
+
+subsys_initcall(ipu_gen_init);
+
+static void __exit ipu_gen_uninit(void)
+{
+ if (g_ipu_irq[0])
+ free_irq(g_ipu_irq[0], 0);
+ if (g_ipu_irq[1])
+ free_irq(g_ipu_irq[1], 0);
+
+ platform_driver_unregister(&mxcipu_driver);
+}
+
+module_exit(ipu_gen_uninit);
+
+EXPORT_SYMBOL(ipu_init_channel);
+EXPORT_SYMBOL(ipu_uninit_channel);
+EXPORT_SYMBOL(ipu_init_channel_buffer);
+EXPORT_SYMBOL(ipu_unlink_channels);
+EXPORT_SYMBOL(ipu_update_channel_buffer);
+EXPORT_SYMBOL(ipu_select_buffer);
+EXPORT_SYMBOL(ipu_link_channels);
+EXPORT_SYMBOL(ipu_enable_channel);
+EXPORT_SYMBOL(ipu_disable_channel);
+EXPORT_SYMBOL(ipu_enable_irq);
+EXPORT_SYMBOL(ipu_disable_irq);
+EXPORT_SYMBOL(ipu_clear_irq);
+EXPORT_SYMBOL(ipu_get_irq_status);
+EXPORT_SYMBOL(ipu_request_irq);
+EXPORT_SYMBOL(ipu_free_irq);
+EXPORT_SYMBOL(ipu_pf_set_pause_row);
+EXPORT_SYMBOL(bytes_per_pixel);
diff --git a/drivers/mxc/ipu/ipu_csi.c b/drivers/mxc/ipu/ipu_csi.c
new file mode 100644
index 000000000000..10708cf3ba54
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_csi.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_csi.c
+ *
+ * @brief IPU CMOS Sensor interface functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+
+static bool gipu_csi_get_mclk_flag = false;
+static int csi_mclk_flag = 0;
+
+extern void gpio_sensor_suspend(bool flag);
+
+/*!
+ * ipu_csi_init_interface
+ * Sets initial values for the CSI registers.
+ * The width and height of the sensor and the actual frame size will be
+ * set to the same values.
+ * @param width Sensor width
+ * @param height Sensor height
+ * @param pixel_fmt pixel format
+ * @param sig ipu_csi_signal_cfg_t structure
+ *
+ * @return 0 for success, -EINVAL for error
+ */
+int32_t
+ipu_csi_init_interface(uint16_t width, uint16_t height, uint32_t pixel_fmt,
+ ipu_csi_signal_cfg_t sig)
+{
+ uint32_t data = 0;
+
+ /* Set SENS_DATA_FORMAT bits (8 and 9)
+ RGB or YUV444 is 0 which is current value in data so not set explicitly
+ This is also the default value if attempts are made to set it to
+ something invalid. */
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_UYVY:
+ data = CSI_SENS_CONF_DATA_FMT_YUV422;
+ break;
+ case IPU_PIX_FMT_RGB24:
+ case IPU_PIX_FMT_BGR24:
+ data = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
+ break;
+ case IPU_PIX_FMT_GENERIC:
+ data = CSI_SENS_CONF_DATA_FMT_BAYER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set the CSI_SENS_CONF register remaining fields */
+ data |= sig.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
+ sig.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
+ sig.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
+ sig.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
+ sig.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
+ sig.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
+ sig.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
+ sig.sens_clksrc << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
+
+ __raw_writel(data, CSI_SENS_CONF);
+
+ /* Setup frame size */
+ __raw_writel(width | height << 16, CSI_SENS_FRM_SIZE);
+
+ __raw_writel(width << 16, CSI_FLASH_STROBE_1);
+ __raw_writel(height << 16 | 0x22, CSI_FLASH_STROBE_2);
+
+ /* Set CCIR registers */
+ if ((sig.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) ||
+ (sig.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED)) {
+ __raw_writel(0x40030, CSI_CCIR_CODE_1);
+ __raw_writel(0xFF0000, CSI_CCIR_CODE_3);
+ }
+
+ dev_dbg(g_ipu_dev, "CSI_SENS_CONF = 0x%08X\n",
+ __raw_readl(CSI_SENS_CONF));
+ dev_dbg(g_ipu_dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
+ __raw_readl(CSI_ACT_FRM_SIZE));
+
+ return 0;
+}
+
+/*!
+ * ipu_csi_flash_strobe
+ *
+ * @param flag true to turn on flash strobe
+ *
+ * @return 0 for success
+ */
+void ipu_csi_flash_strobe(bool flag)
+{
+ if (flag == true) {
+ __raw_writel(__raw_readl(CSI_FLASH_STROBE_2) | 0x1,
+ CSI_FLASH_STROBE_2);
+ }
+}
+
+/*!
+ * ipu_csi_enable_mclk
+ *
+ * @param src enum define which source to control the clk
+ * CSI_MCLK_VF CSI_MCLK_ENC CSI_MCLK_RAW CSI_MCLK_I2C
+ * @param flag true to enable mclk, false to disable mclk
+ * @param wait true to wait 100ms make clock stable, false not wait
+ *
+ * @return 0 for success
+ */
+int32_t ipu_csi_enable_mclk(int src, bool flag, bool wait)
+{
+ if (flag == true) {
+ csi_mclk_flag |= src;
+ } else {
+ csi_mclk_flag &= ~src;
+ }
+
+ if (gipu_csi_get_mclk_flag == flag)
+ return 0;
+
+ if (flag == true) {
+ clk_enable(g_ipu_csi_clk);
+ if (wait == true)
+ msleep(10);
+ /*printk("enable csi clock from source %d\n", src); */
+ gipu_csi_get_mclk_flag = true;
+ } else if (csi_mclk_flag == 0) {
+ clk_disable(g_ipu_csi_clk);
+ /*printk("disable csi clock from source %d\n", src); */
+ gipu_csi_get_mclk_flag = flag;
+ }
+
+ return 0;
+}
+
+/*!
+ * ipu_csi_read_mclk_flag
+ *
+ * @return csi_mclk_flag
+ */
+int ipu_csi_read_mclk_flag(void)
+{
+ return csi_mclk_flag;
+}
+
+/*!
+ * ipu_csi_get_window_size
+ *
+ * @param width pointer to window width
+ * @param height pointer to window height
+ * @param dummy dummy for IPUv1 to keep the same interface with IPUv3
+ *
+ */
+void ipu_csi_get_window_size(uint32_t *width, uint32_t *height,
+ uint32_t dummy)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(CSI_ACT_FRM_SIZE);
+ *width = (reg & 0xFFFF) + 1;
+ *height = (reg >> 16 & 0xFFFF) + 1;
+}
+
+/*!
+ * ipu_csi_set_window_size
+ *
+ * @param width window width
+ * @param height window height
+ * @param dummy dummy for IPUv1 to keep the same interface with IPUv3
+ *
+ */
+void ipu_csi_set_window_size(uint32_t width, uint32_t height, uint32_t dummy)
+{
+ __raw_writel((width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE);
+}
+
+/*!
+ * ipu_csi_set_window_pos
+ *
+ * @param left uint32 window x start
+ * @param top uint32 window y start
+ * @param dummy dummy for IPUv1 to keep the same interface with IPUv3
+ *
+ */
+void ipu_csi_set_window_pos(uint32_t left, uint32_t top, uint32_t dummy)
+{
+ uint32_t temp = __raw_readl(CSI_OUT_FRM_CTRL);
+ temp &= 0xffff0000;
+ temp = top | (left << 8) | temp;
+ __raw_writel(temp, CSI_OUT_FRM_CTRL);
+}
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(ipu_csi_set_window_pos);
+EXPORT_SYMBOL(ipu_csi_set_window_size);
+EXPORT_SYMBOL(ipu_csi_get_window_size);
+EXPORT_SYMBOL(ipu_csi_read_mclk_flag);
+EXPORT_SYMBOL(ipu_csi_enable_mclk);
+EXPORT_SYMBOL(ipu_csi_flash_strobe);
+EXPORT_SYMBOL(ipu_csi_init_interface);
diff --git a/drivers/mxc/ipu/ipu_device.c b/drivers/mxc/ipu/ipu_device.c
new file mode 100644
index 000000000000..5fd1c51ec9b1
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_device.c
@@ -0,0 +1,696 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_device.c
+ *
+ * @brief This file contains the IPU driver device interface and fops functions.
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+/* Strucutures and variables for exporting MXC IPU as device*/
+
+#define MAX_Q_SIZE 10
+
+static int mxc_ipu_major;
+static struct class *mxc_ipu_class;
+
+DEFINE_SPINLOCK(queue_lock);
+static DECLARE_MUTEX(user_mutex);
+
+static wait_queue_head_t waitq;
+static int pending_events = 0;
+int read_ptr = 0;
+int write_ptr = 0;
+
+ipu_event_info events[MAX_Q_SIZE];
+
+int register_ipu_device(void);
+
+/* Static functions */
+
+int get_events(ipu_event_info *p)
+{
+ unsigned long flags;
+ int ret = 0, i, cnt, found = 0;
+ spin_lock_irqsave(&queue_lock, flags);
+ if (pending_events != 0) {
+ if (write_ptr > read_ptr)
+ cnt = write_ptr - read_ptr;
+ else
+ cnt = MAX_Q_SIZE - read_ptr + write_ptr;
+ for (i = 0; i < cnt; i++) {
+ if (p->irq == events[read_ptr].irq) {
+ *p = events[read_ptr];
+ events[read_ptr].irq = 0;
+ read_ptr++;
+ if (read_ptr >= MAX_Q_SIZE)
+ read_ptr = 0;
+ found = 1;
+ break;
+ }
+
+ if (events[read_ptr].irq) {
+ events[write_ptr] = events[read_ptr];
+ events[read_ptr].irq = 0;
+ write_ptr++;
+ if (write_ptr >= MAX_Q_SIZE)
+ write_ptr = 0;
+ } else
+ pending_events--;
+
+ read_ptr++;
+ if (read_ptr >= MAX_Q_SIZE)
+ read_ptr = 0;
+ }
+ if (found)
+ pending_events--;
+ else
+ ret = -1;
+ } else {
+ ret = -1;
+ }
+
+ spin_unlock_irqrestore(&queue_lock, flags);
+ return ret;
+}
+
+static irqreturn_t mxc_ipu_generic_handler(int irq, void *dev_id)
+{
+ ipu_event_info e;
+
+ e.irq = irq;
+ e.dev = dev_id;
+ events[write_ptr] = e;
+ write_ptr++;
+ if (write_ptr >= MAX_Q_SIZE)
+ write_ptr = 0;
+ pending_events++;
+ /* Wakeup any blocking user context */
+ wake_up_interruptible(&waitq);
+ return IRQ_HANDLED;
+}
+
+static int mxc_ipu_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ return ret;
+}
+static int mxc_ipu_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+
+ switch (cmd) {
+
+ case IPU_INIT_CHANNEL:
+ {
+ ipu_channel_parm parm;
+ if (copy_from_user
+ (&parm, (ipu_channel_parm *) arg,
+ sizeof(ipu_channel_parm))) {
+ return -EFAULT;
+ }
+ if (!parm.flag) {
+ ret =
+ ipu_init_channel(parm.channel,
+ &parm.params);
+ } else {
+ ret = ipu_init_channel(parm.channel, NULL);
+ }
+ }
+ break;
+
+ case IPU_UNINIT_CHANNEL:
+ {
+ ipu_channel_t ch;
+ int __user *argp = (void __user *)arg;
+ if (get_user(ch, argp))
+ return -EFAULT;
+ ipu_uninit_channel(ch);
+ }
+ break;
+
+ case IPU_INIT_CHANNEL_BUFFER:
+ {
+ ipu_channel_buf_parm parm;
+ if (copy_from_user
+ (&parm, (ipu_channel_buf_parm *) arg,
+ sizeof(ipu_channel_buf_parm))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_init_channel_buffer(parm.channel, parm.type,
+ parm.pixel_fmt,
+ parm.width, parm.height,
+ parm.stride,
+ parm.rot_mode,
+ parm.phyaddr_0,
+ parm.phyaddr_1,
+ parm.u_offset,
+ parm.v_offset);
+
+ }
+ break;
+
+ case IPU_UPDATE_CHANNEL_BUFFER:
+ {
+ ipu_channel_buf_parm parm;
+ if (copy_from_user
+ (&parm, (ipu_channel_buf_parm *) arg,
+ sizeof(ipu_channel_buf_parm))) {
+ return -EFAULT;
+ }
+ if ((parm.phyaddr_0 != (dma_addr_t) NULL)
+ && (parm.phyaddr_1 == (dma_addr_t) NULL)) {
+ ret =
+ ipu_update_channel_buffer(parm.channel,
+ parm.type,
+ parm.bufNum,
+ parm.phyaddr_0);
+ } else if ((parm.phyaddr_0 == (dma_addr_t) NULL)
+ && (parm.phyaddr_1 != (dma_addr_t) NULL)) {
+ ret =
+ ipu_update_channel_buffer(parm.channel,
+ parm.type,
+ parm.bufNum,
+ parm.phyaddr_1);
+ } else {
+ ret = -1;
+ }
+
+ }
+ break;
+ case IPU_SELECT_CHANNEL_BUFFER:
+ {
+ ipu_channel_buf_parm parm;
+ if (copy_from_user
+ (&parm, (ipu_channel_buf_parm *) arg,
+ sizeof(ipu_channel_buf_parm))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_select_buffer(parm.channel, parm.type,
+ parm.bufNum);
+
+ }
+ break;
+ case IPU_LINK_CHANNELS:
+ {
+ ipu_channel_link link;
+ if (copy_from_user
+ (&link, (ipu_channel_link *) arg,
+ sizeof(ipu_channel_link))) {
+ return -EFAULT;
+ }
+ ret = ipu_link_channels(link.src_ch, link.dest_ch);
+
+ }
+ break;
+ case IPU_UNLINK_CHANNELS:
+ {
+ ipu_channel_link link;
+ if (copy_from_user
+ (&link, (ipu_channel_link *) arg,
+ sizeof(ipu_channel_link))) {
+ return -EFAULT;
+ }
+ ret = ipu_unlink_channels(link.src_ch, link.dest_ch);
+
+ }
+ break;
+ case IPU_ENABLE_CHANNEL:
+ {
+ ipu_channel_t ch;
+ int __user *argp = (void __user *)arg;
+ if (get_user(ch, argp))
+ return -EFAULT;
+ ipu_enable_channel(ch);
+ }
+ break;
+ case IPU_DISABLE_CHANNEL:
+ {
+ ipu_channel_info info;
+ if (copy_from_user
+ (&info, (ipu_channel_info *) arg,
+ sizeof(ipu_channel_info))) {
+ return -EFAULT;
+ }
+ ret = ipu_disable_channel(info.channel, info.stop);
+ }
+ break;
+ case IPU_ENABLE_IRQ:
+ {
+ uint32_t irq;
+ int __user *argp = (void __user *)arg;
+ if (get_user(irq, argp))
+ return -EFAULT;
+ ipu_enable_irq(irq);
+ }
+ break;
+ case IPU_DISABLE_IRQ:
+ {
+ uint32_t irq;
+ int __user *argp = (void __user *)arg;
+ if (get_user(irq, argp))
+ return -EFAULT;
+ ipu_disable_irq(irq);
+ }
+ break;
+ case IPU_CLEAR_IRQ:
+ {
+ uint32_t irq;
+ int __user *argp = (void __user *)arg;
+ if (get_user(irq, argp))
+ return -EFAULT;
+ ipu_clear_irq(irq);
+ }
+ break;
+ case IPU_FREE_IRQ:
+ {
+ ipu_irq_info info;
+ if (copy_from_user
+ (&info, (ipu_irq_info *) arg,
+ sizeof(ipu_irq_info))) {
+ return -EFAULT;
+ }
+ ipu_free_irq(info.irq, info.dev_id);
+ }
+ break;
+ case IPU_REQUEST_IRQ_STATUS:
+ {
+ uint32_t irq;
+ int __user *argp = (void __user *)arg;
+ if (get_user(irq, argp))
+ return -EFAULT;
+ ret = ipu_get_irq_status(irq);
+ }
+ break;
+ case IPU_SDC_INIT_PANEL:
+ {
+ ipu_sdc_panel_info sinfo;
+ if (copy_from_user
+ (&sinfo, (ipu_sdc_panel_info *) arg,
+ sizeof(ipu_sdc_panel_info))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_sdc_init_panel(sinfo.panel, sinfo.pixel_clk,
+ sinfo.width, sinfo.height,
+ sinfo.pixel_fmt,
+ sinfo.hStartWidth,
+ sinfo.hSyncWidth,
+ sinfo.hEndWidth,
+ sinfo.vStartWidth,
+ sinfo.vSyncWidth,
+ sinfo.vEndWidth, sinfo.signal);
+ }
+ break;
+ case IPU_SDC_SET_WIN_POS:
+ {
+ ipu_sdc_window_pos pos;
+ if (copy_from_user
+ (&pos, (ipu_sdc_window_pos *) arg,
+ sizeof(ipu_sdc_window_pos))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_disp_set_window_pos(pos.channel, pos.x_pos,
+ pos.y_pos);
+
+ }
+ break;
+ case IPU_SDC_SET_GLOBAL_ALPHA:
+ {
+ ipu_sdc_global_alpha g;
+ if (copy_from_user
+ (&g, (ipu_sdc_global_alpha *) arg,
+ sizeof(ipu_sdc_global_alpha))) {
+ return -EFAULT;
+ }
+ ret = ipu_sdc_set_global_alpha(g.enable, g.alpha);
+ }
+ break;
+ case IPU_SDC_SET_COLOR_KEY:
+ {
+ ipu_sdc_color_key c;
+ if (copy_from_user
+ (&c, (ipu_sdc_color_key *) arg,
+ sizeof(ipu_sdc_color_key))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_sdc_set_color_key(c.channel, c.enable,
+ c.colorKey);
+ }
+ break;
+ case IPU_SDC_SET_BRIGHTNESS:
+ {
+ uint8_t b;
+ int __user *argp = (void __user *)arg;
+ if (get_user(b, argp))
+ return -EFAULT;
+ ret = ipu_sdc_set_brightness(b);
+
+ }
+ break;
+ case IPU_REGISTER_GENERIC_ISR:
+ {
+ ipu_event_info info;
+ if (copy_from_user
+ (&info, (ipu_event_info *) arg,
+ sizeof(ipu_event_info))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_request_irq(info.irq, mxc_ipu_generic_handler,
+ 0, "video_sink", info.dev);
+ }
+ break;
+ case IPU_GET_EVENT:
+ /* User will have to allocate event_type structure and pass the pointer in arg */
+ {
+ ipu_event_info info;
+ int r = -1;
+
+ if (copy_from_user
+ (&info, (ipu_event_info *) arg,
+ sizeof(ipu_event_info)))
+ return -EFAULT;
+
+ r = get_events(&info);
+ if (r == -1) {
+ wait_event_interruptible_timeout(waitq,
+ (pending_events != 0), 2 * HZ);
+ r = get_events(&info);
+ }
+ ret = -1;
+ if (r == 0) {
+ if (!copy_to_user((ipu_event_info *) arg,
+ &info, sizeof(ipu_event_info)))
+ ret = 0;
+ }
+ }
+ break;
+ case IPU_ADC_WRITE_TEMPLATE:
+ {
+ ipu_adc_template temp;
+ if (copy_from_user
+ (&temp, (ipu_adc_template *) arg, sizeof(temp))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_adc_write_template(temp.disp, temp.pCmd,
+ temp.write);
+ }
+ break;
+ case IPU_ADC_UPDATE:
+ {
+ ipu_adc_update update;
+ if (copy_from_user
+ (&update, (ipu_adc_update *) arg, sizeof(update))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_adc_set_update_mode(update.channel, update.mode,
+ update.refresh_rate,
+ update.addr, update.size);
+ }
+ break;
+ case IPU_ADC_SNOOP:
+ {
+ ipu_adc_snoop snoop;
+ if (copy_from_user
+ (&snoop, (ipu_adc_snoop *) arg, sizeof(snoop))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_adc_get_snooping_status(snoop.statl,
+ snoop.stath);
+ }
+ break;
+ case IPU_ADC_CMD:
+ {
+ ipu_adc_cmd cmd;
+ if (copy_from_user
+ (&cmd, (ipu_adc_cmd *) arg, sizeof(cmd))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_adc_write_cmd(cmd.disp, cmd.type, cmd.cmd,
+ cmd.params, cmd.numParams);
+ }
+ break;
+ case IPU_ADC_INIT_PANEL:
+ {
+ ipu_adc_panel panel;
+ if (copy_from_user
+ (&panel, (ipu_adc_panel *) arg, sizeof(panel))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_adc_init_panel(panel.disp, panel.width,
+ panel.height, panel.pixel_fmt,
+ panel.stride, panel.signal,
+ panel.addr, panel.vsync_width,
+ panel.mode);
+ }
+ break;
+ case IPU_ADC_IFC_TIMING:
+ {
+ ipu_adc_ifc_timing t;
+ if (copy_from_user
+ (&t, (ipu_adc_ifc_timing *) arg, sizeof(t))) {
+ return -EFAULT;
+ }
+ ret =
+ ipu_adc_init_ifc_timing(t.disp, t.read,
+ t.cycle_time, t.up_time,
+ t.down_time,
+ t.read_latch_time,
+ t.pixel_clk);
+ }
+ break;
+ case IPU_CSI_INIT_INTERFACE:
+ {
+ ipu_csi_interface c;
+ if (copy_from_user
+ (&c, (ipu_csi_interface *) arg, sizeof(c)))
+ return -EFAULT;
+ ret =
+ ipu_csi_init_interface(c.width, c.height,
+ c.pixel_fmt, c.signal);
+ }
+ break;
+ case IPU_CSI_ENABLE_MCLK:
+ {
+ ipu_csi_mclk m;
+ if (copy_from_user(&m, (ipu_csi_mclk *) arg, sizeof(m)))
+ return -EFAULT;
+ ret = ipu_csi_enable_mclk(m.src, m.flag, m.wait);
+ }
+ break;
+ case IPU_CSI_READ_MCLK_FLAG:
+ {
+ ret = ipu_csi_read_mclk_flag();
+ }
+ break;
+ case IPU_CSI_FLASH_STROBE:
+ {
+ bool strobe;
+ int __user *argp = (void __user *)arg;
+ if (get_user(strobe, argp))
+ return -EFAULT;
+ ipu_csi_flash_strobe(strobe);
+ }
+ break;
+ case IPU_CSI_GET_WIN_SIZE:
+ {
+ ipu_csi_window_size w;
+ int dummy = 0;
+ ipu_csi_get_window_size(&w.width, &w.height, dummy);
+ if (copy_to_user
+ ((ipu_csi_window_size *) arg, &w, sizeof(w)))
+ return -EFAULT;
+ }
+ break;
+ case IPU_CSI_SET_WIN_SIZE:
+ {
+ ipu_csi_window_size w;
+ int dummy = 0;
+ if (copy_from_user
+ (&w, (ipu_csi_window_size *) arg, sizeof(w)))
+ return -EFAULT;
+ ipu_csi_set_window_size(w.width, w.height, dummy);
+ }
+ break;
+ case IPU_CSI_SET_WINDOW:
+ {
+ ipu_csi_window p;
+ int dummy = 0;
+ if (copy_from_user
+ (&p, (ipu_csi_window *) arg, sizeof(p)))
+ return -EFAULT;
+ ipu_csi_set_window_pos(p.left, p.top, dummy);
+ }
+ break;
+ case IPU_PF_SET_PAUSE_ROW:
+ {
+ uint32_t p;
+ int __user *argp = (void __user *)arg;
+ if (get_user(p, argp))
+ return -EFAULT;
+ ret = ipu_pf_set_pause_row(p);
+ }
+ break;
+ case IPU_ALOC_MEM:
+ {
+ ipu_mem_info info;
+ if (copy_from_user
+ (&info, (ipu_mem_info *) arg,
+ sizeof(ipu_mem_info)))
+ return -EFAULT;
+
+ info.vaddr = dma_alloc_coherent(0,
+ PAGE_ALIGN(info.size),
+ &info.paddr,
+ GFP_DMA | GFP_KERNEL);
+ if (info.vaddr == 0) {
+ printk(KERN_ERR "dma alloc failed!\n");
+ return -ENOBUFS;
+ }
+ if (copy_to_user((ipu_mem_info *) arg, &info,
+ sizeof(ipu_mem_info)) > 0)
+ return -EFAULT;
+ }
+ break;
+ case IPU_FREE_MEM:
+ {
+ ipu_mem_info info;
+ if (copy_from_user
+ (&info, (ipu_mem_info *) arg,
+ sizeof(ipu_mem_info)))
+ return -EFAULT;
+
+ if (info.vaddr != 0)
+ dma_free_coherent(0, PAGE_ALIGN(info.size),
+ info.vaddr, info.paddr);
+ else
+ return -EFAULT;
+ }
+ break;
+ case IPU_IS_CHAN_BUSY:
+ {
+ ipu_channel_t chan;
+ if (copy_from_user
+ (&chan, (ipu_channel_t *)arg,
+ sizeof(ipu_channel_t)))
+ return -EFAULT;
+
+ if (ipu_is_channel_busy(chan))
+ ret = 1;
+ else
+ ret = 0;
+ }
+ break;
+ default:
+ break;
+
+ }
+ return ret;
+}
+
+static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ vma->vm_page_prot = pgprot_writethru(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot)) {
+ printk(KERN_ERR
+ "mmap failed!\n");
+ return -ENOBUFS;
+ }
+ return 0;
+}
+
+static int mxc_ipu_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static struct file_operations mxc_ipu_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_ipu_open,
+ .mmap = mxc_ipu_mmap,
+ .release = mxc_ipu_release,
+ .ioctl = mxc_ipu_ioctl
+};
+
+int register_ipu_device()
+{
+ int ret = 0;
+ struct device *temp;
+ mxc_ipu_major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops);
+ if (mxc_ipu_major < 0) {
+ printk(KERN_ERR
+ "Unable to register Mxc Ipu as a char device\n");
+ return mxc_ipu_major;
+ }
+
+ mxc_ipu_class = class_create(THIS_MODULE, "mxc_ipu");
+ if (IS_ERR(mxc_ipu_class)) {
+ printk(KERN_ERR "Unable to create class for Mxc Ipu\n");
+ ret = PTR_ERR(mxc_ipu_class);
+ goto err1;
+ }
+
+ temp = device_create(mxc_ipu_class, NULL, MKDEV(mxc_ipu_major, 0), NULL,
+ "mxc_ipu");
+
+ if (IS_ERR(temp)) {
+ printk(KERN_ERR "Unable to create class device for Mxc Ipu\n");
+ ret = PTR_ERR(temp);
+ goto err2;
+ }
+ spin_lock_init(&queue_lock);
+ init_waitqueue_head(&waitq);
+ return ret;
+
+ err2:
+ class_destroy(mxc_ipu_class);
+ err1:
+ unregister_chrdev(mxc_ipu_major, "mxc_ipu");
+ return ret;
+
+}
diff --git a/drivers/mxc/ipu/ipu_ic.c b/drivers/mxc/ipu/ipu_ic.c
new file mode 100644
index 000000000000..cdf823a2760b
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_ic.c
@@ -0,0 +1,592 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * @file ipu_ic.c
+ *
+ * @brief IPU IC functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+enum {
+ IC_TASK_VIEWFINDER,
+ IC_TASK_ENCODER,
+ IC_TASK_POST_PROCESSOR
+};
+
+extern int g_ipu_hw_rev;
+static void _init_csc(uint8_t ic_task, ipu_color_space_t in_format,
+ ipu_color_space_t out_format);
+static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize,
+ uint32_t * resizeCoeff,
+ uint32_t * downsizeCoeff);
+
+void _ipu_ic_enable_task(ipu_channel_t channel)
+{
+ uint32_t ic_conf;
+
+ ic_conf = __raw_readl(IC_CONF);
+ switch (channel) {
+ case CSI_PRP_VF_ADC:
+ case MEM_PRP_VF_ADC:
+ case CSI_PRP_VF_MEM:
+ case MEM_PRP_VF_MEM:
+ ic_conf |= IC_CONF_PRPVF_EN;
+ break;
+ case MEM_ROT_VF_MEM:
+ ic_conf |= IC_CONF_PRPVF_ROT_EN;
+ break;
+ case CSI_PRP_ENC_MEM:
+ case MEM_PRP_ENC_MEM:
+ ic_conf |= IC_CONF_PRPENC_EN;
+ break;
+ case MEM_ROT_ENC_MEM:
+ ic_conf |= IC_CONF_PRPENC_ROT_EN;
+ break;
+ case MEM_PP_ADC:
+ case MEM_PP_MEM:
+ ic_conf |= IC_CONF_PP_EN;
+ break;
+ case MEM_ROT_PP_MEM:
+ ic_conf |= IC_CONF_PP_ROT_EN;
+ break;
+ case CSI_MEM:
+ // ???
+ ic_conf |= IC_CONF_RWS_EN | IC_CONF_PRPENC_EN;
+ break;
+ default:
+ break;
+ }
+ __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_disable_task(ipu_channel_t channel)
+{
+ uint32_t ic_conf;
+
+ ic_conf = __raw_readl(IC_CONF);
+ switch (channel) {
+ case CSI_PRP_VF_ADC:
+ case MEM_PRP_VF_ADC:
+ case CSI_PRP_VF_MEM:
+ case MEM_PRP_VF_MEM:
+ ic_conf &= ~IC_CONF_PRPVF_EN;
+ break;
+ case MEM_ROT_VF_MEM:
+ ic_conf &= ~IC_CONF_PRPVF_ROT_EN;
+ break;
+ case CSI_PRP_ENC_MEM:
+ case MEM_PRP_ENC_MEM:
+ ic_conf &= ~IC_CONF_PRPENC_EN;
+ break;
+ case MEM_ROT_ENC_MEM:
+ ic_conf &= ~IC_CONF_PRPENC_ROT_EN;
+ break;
+ case MEM_PP_ADC:
+ case MEM_PP_MEM:
+ ic_conf &= ~IC_CONF_PP_EN;
+ break;
+ case MEM_ROT_PP_MEM:
+ ic_conf &= ~IC_CONF_PP_ROT_EN;
+ break;
+ case CSI_MEM:
+ // ???
+ ic_conf &= ~(IC_CONF_RWS_EN | IC_CONF_PRPENC_EN);
+ break;
+ default:
+ break;
+ }
+ __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_init_prpvf(ipu_channel_params_t * params, bool src_is_csi)
+{
+ uint32_t reg, ic_conf;
+ uint32_t downsizeCoeff, resizeCoeff;
+ ipu_color_space_t in_fmt, out_fmt;
+
+ /* Setup vertical resizing */
+ _calc_resize_coeffs(params->mem_prp_vf_mem.in_height,
+ params->mem_prp_vf_mem.out_height,
+ &resizeCoeff, &downsizeCoeff);
+ reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+ /* Setup horizontal resizing */
+ _calc_resize_coeffs(params->mem_prp_vf_mem.in_width,
+ params->mem_prp_vf_mem.out_width,
+ &resizeCoeff, &downsizeCoeff);
+ reg |= (downsizeCoeff << 14) | resizeCoeff;
+
+ __raw_writel(reg, IC_PRP_VF_RSC);
+
+ ic_conf = __raw_readl(IC_CONF);
+
+ /* Setup color space conversion */
+ in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_pixel_fmt);
+ out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
+ if (in_fmt == RGB) {
+ if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+ _init_csc(IC_TASK_VIEWFINDER, RGB, out_fmt);
+ ic_conf |= IC_CONF_PRPVF_CSC1; /* Enable RGB->YCBCR CSC */
+ }
+ }
+ if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+ if (out_fmt == RGB) {
+ _init_csc(IC_TASK_VIEWFINDER, YCbCr, RGB);
+ ic_conf |= IC_CONF_PRPVF_CSC1; /* Enable YCBCR->RGB CSC */
+ } else {
+ /* TODO: Support YUV<->YCbCr conversion? */
+ }
+ }
+
+ if (params->mem_prp_vf_mem.graphics_combine_en) {
+ ic_conf |= IC_CONF_PRPVF_CMB;
+
+ /* need transparent CSC1 conversion */
+ _init_csc(IC_TASK_POST_PROCESSOR, RGB, RGB);
+ ic_conf |= IC_CONF_PRPVF_CSC1; /* Enable RGB->RGB CSC */
+
+ if (params->mem_prp_vf_mem.global_alpha_en) {
+ ic_conf |= IC_CONF_IC_GLB_LOC_A;
+ } else {
+ ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+ }
+
+ if (params->mem_prp_vf_mem.key_color_en) {
+ ic_conf |= IC_CONF_KEY_COLOR_EN;
+ } else {
+ ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+ }
+ } else {
+ ic_conf &= ~IC_CONF_PP_CMB;
+ }
+
+#ifndef CONFIG_VIRTIO_SUPPORT /* Setting RWS_EN doesn't work in Virtio */
+ if (src_is_csi) {
+ ic_conf &= ~IC_CONF_RWS_EN;
+ } else {
+ ic_conf |= IC_CONF_RWS_EN;
+ }
+#endif
+ __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_prpvf(void)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(IC_CONF);
+ reg &= ~(IC_CONF_PRPVF_EN | IC_CONF_PRPVF_CMB |
+ IC_CONF_PRPVF_CSC2 | IC_CONF_PRPVF_CSC1);
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_vf(ipu_channel_params_t * params)
+{
+}
+
+void _ipu_ic_uninit_rotate_vf(void)
+{
+ uint32_t reg;
+ reg = __raw_readl(IC_CONF);
+ reg &= ~IC_CONF_PRPVF_ROT_EN;
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_csi(ipu_channel_params_t * params)
+{
+ uint32_t reg;
+ reg = __raw_readl(IC_CONF);
+ reg &= ~IC_CONF_CSI_MEM_WR_EN;
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_uninit_csi(void)
+{
+ uint32_t reg;
+ reg = __raw_readl(IC_CONF);
+ reg &= ~(IC_CONF_RWS_EN | IC_CONF_PRPENC_EN);
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_prpenc(ipu_channel_params_t * params, bool src_is_csi)
+{
+ uint32_t reg, ic_conf;
+ uint32_t downsizeCoeff, resizeCoeff;
+ ipu_color_space_t in_fmt, out_fmt;
+
+ /* Setup vertical resizing */
+ _calc_resize_coeffs(params->mem_prp_enc_mem.in_height,
+ params->mem_prp_enc_mem.out_height,
+ &resizeCoeff, &downsizeCoeff);
+ reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+ /* Setup horizontal resizing */
+ _calc_resize_coeffs(params->mem_prp_enc_mem.in_width,
+ params->mem_prp_enc_mem.out_width,
+ &resizeCoeff, &downsizeCoeff);
+ reg |= (downsizeCoeff << 14) | resizeCoeff;
+
+ __raw_writel(reg, IC_PRP_ENC_RSC);
+
+ ic_conf = __raw_readl(IC_CONF);
+
+ /* Setup color space conversion */
+ in_fmt = format_to_colorspace(params->mem_prp_enc_mem.in_pixel_fmt);
+ out_fmt = format_to_colorspace(params->mem_prp_enc_mem.out_pixel_fmt);
+ if (in_fmt == RGB) {
+ if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+ /* TODO: ERROR! */
+ }
+ }
+ if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+ if (out_fmt == RGB) {
+ _init_csc(IC_TASK_ENCODER, YCbCr, RGB);
+ ic_conf |= IC_CONF_PRPENC_CSC1; /* Enable YCBCR->RGB CSC */
+ } else {
+ /* TODO: Support YUV<->YCbCr conversion? */
+ }
+ }
+
+ if (src_is_csi) {
+ ic_conf &= ~IC_CONF_RWS_EN;
+ } else {
+ ic_conf |= IC_CONF_RWS_EN;
+ }
+
+ __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_prpenc(void)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(IC_CONF);
+ reg &= ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1);
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_enc(ipu_channel_params_t * params)
+{
+}
+
+void _ipu_ic_uninit_rotate_enc(void)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(IC_CONF);
+ reg &= ~(IC_CONF_PRPENC_ROT_EN);
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_pp(ipu_channel_params_t * params)
+{
+ uint32_t reg, ic_conf;
+ uint32_t downsizeCoeff, resizeCoeff;
+ ipu_color_space_t in_fmt, out_fmt;
+
+ /* Setup vertical resizing */
+ _calc_resize_coeffs(params->mem_pp_mem.in_height,
+ params->mem_pp_mem.out_height,
+ &resizeCoeff, &downsizeCoeff);
+ reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+ /* Setup horizontal resizing */
+ _calc_resize_coeffs(params->mem_pp_mem.in_width,
+ params->mem_pp_mem.out_width,
+ &resizeCoeff, &downsizeCoeff);
+ reg |= (downsizeCoeff << 14) | resizeCoeff;
+
+ __raw_writel(reg, IC_PP_RSC);
+
+ ic_conf = __raw_readl(IC_CONF);
+
+ /* Setup color space conversion */
+ in_fmt = format_to_colorspace(params->mem_pp_mem.in_pixel_fmt);
+ out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
+ if (in_fmt == RGB) {
+ if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+ _init_csc(IC_TASK_POST_PROCESSOR, RGB, out_fmt);
+ ic_conf |= IC_CONF_PP_CSC2; /* Enable RGB->YCBCR CSC */
+ }
+ }
+ if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+ if (out_fmt == RGB) {
+ _init_csc(IC_TASK_POST_PROCESSOR, YCbCr, RGB);
+ ic_conf |= IC_CONF_PP_CSC1; /* Enable YCBCR->RGB CSC */
+ } else {
+ /* TODO: Support YUV<->YCbCr conversion? */
+ }
+ }
+
+ if (params->mem_pp_mem.graphics_combine_en) {
+ ic_conf |= IC_CONF_PP_CMB;
+
+ /* need transparent CSC1 conversion */
+ _init_csc(IC_TASK_POST_PROCESSOR, RGB, RGB);
+ ic_conf |= IC_CONF_PP_CSC1; /* Enable RGB->RGB CSC */
+
+ if (params->mem_pp_mem.global_alpha_en) {
+ ic_conf |= IC_CONF_IC_GLB_LOC_A;
+ } else {
+ ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+ }
+
+ if (params->mem_pp_mem.key_color_en) {
+ ic_conf |= IC_CONF_KEY_COLOR_EN;
+ } else {
+ ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+ }
+ } else {
+ ic_conf &= ~IC_CONF_PP_CMB;
+ }
+
+ __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_pp(void)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(IC_CONF);
+ reg &= ~(IC_CONF_PP_EN | IC_CONF_PP_CSC1 | IC_CONF_PP_CSC2 |
+ IC_CONF_PP_CMB);
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_pp(ipu_channel_params_t * params)
+{
+}
+
+void _ipu_ic_uninit_rotate_pp(void)
+{
+ uint32_t reg;
+ reg = __raw_readl(IC_CONF);
+ reg &= ~IC_CONF_PP_ROT_EN;
+ __raw_writel(reg, IC_CONF);
+}
+
+static void _init_csc(uint8_t ic_task, ipu_color_space_t in_format,
+ ipu_color_space_t out_format)
+{
+/* Y = R * .299 + G * .587 + B * .114;
+ U = R * -.169 + G * -.332 + B * .500 + 128.;
+ V = R * .500 + G * -.419 + B * -.0813 + 128.;*/
+ static const uint32_t rgb2ycbcr_coeff[4][3] = {
+ {0x004D, 0x0096, 0x001D},
+ {0x01D5, 0x01AB, 0x0080},
+ {0x0080, 0x0195, 0x01EB},
+ {0x0000, 0x0200, 0x0200}, /* A0, A1, A2 */
+ };
+
+ /* transparent RGB->RGB matrix for combining
+ */
+ static const uint32_t rgb2rgb_coeff[4][3] = {
+ {0x0080, 0x0000, 0x0000},
+ {0x0000, 0x0080, 0x0000},
+ {0x0000, 0x0000, 0x0080},
+ {0x0000, 0x0000, 0x0000}, /* A0, A1, A2 */
+ };
+
+/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
+ G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
+ B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
+ static const uint32_t ycbcr2rgb_coeff[4][3] = {
+ {149, 0, 204},
+ {149, 462, 408},
+ {149, 255, 0},
+ {8192 - 446, 266, 8192 - 554}, /* A0, A1, A2 */
+ };
+
+ uint32_t param[2];
+ uint32_t address = 0;
+
+ if (g_ipu_hw_rev > 1) {
+ if (ic_task == IC_TASK_VIEWFINDER) {
+ address = 0x645 << 3;
+ } else if (ic_task == IC_TASK_ENCODER) {
+ address = 0x321 << 3;
+ } else if (ic_task == IC_TASK_POST_PROCESSOR) {
+ address = 0x96C << 3;
+ } else {
+ BUG();
+ }
+ } else {
+ if (ic_task == IC_TASK_VIEWFINDER) {
+ address = 0x5a5 << 3;
+ } else if (ic_task == IC_TASK_ENCODER) {
+ address = 0x2d1 << 3;
+ } else if (ic_task == IC_TASK_POST_PROCESSOR) {
+ address = 0x87c << 3;
+ } else {
+ BUG();
+ }
+ }
+
+ if ((in_format == YCbCr) && (out_format == RGB)) {
+ /* Init CSC1 (YCbCr->RGB) */
+ param[0] =
+ (ycbcr2rgb_coeff[3][0] << 27) | (ycbcr2rgb_coeff[0][0] <<
+ 18) |
+ (ycbcr2rgb_coeff[1][1] << 9) | ycbcr2rgb_coeff[2][2];
+ /* scale = 2, sat = 0 */
+ param[1] = (ycbcr2rgb_coeff[3][0] >> 5) | (2L << (40 - 32));
+ _ipu_write_param_mem(address, param, 2);
+ dev_dbg(g_ipu_dev,
+ "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+ address, param[0], param[1]);
+
+ param[0] =
+ (ycbcr2rgb_coeff[3][1] << 27) | (ycbcr2rgb_coeff[0][1] <<
+ 18) |
+ (ycbcr2rgb_coeff[1][0] << 9) | ycbcr2rgb_coeff[2][0];
+ param[1] = (ycbcr2rgb_coeff[3][1] >> 5);
+ address += 1L << 3;
+ _ipu_write_param_mem(address, param, 2);
+ dev_dbg(g_ipu_dev,
+ "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+ address, param[0], param[1]);
+
+ param[0] =
+ (ycbcr2rgb_coeff[3][2] << 27) | (ycbcr2rgb_coeff[0][2] <<
+ 18) |
+ (ycbcr2rgb_coeff[1][2] << 9) | ycbcr2rgb_coeff[2][1];
+ param[1] = (ycbcr2rgb_coeff[3][2] >> 5);
+ address += 1L << 3;
+ _ipu_write_param_mem(address, param, 2);
+ dev_dbg(g_ipu_dev,
+ "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+ address, param[0], param[1]);
+ } else if ((in_format == RGB) && (out_format == YCbCr)) {
+ /* Init CSC1 (RGB->YCbCr) */
+ param[0] =
+ (rgb2ycbcr_coeff[3][0] << 27) | (rgb2ycbcr_coeff[0][0] <<
+ 18) |
+ (rgb2ycbcr_coeff[1][1] << 9) | rgb2ycbcr_coeff[2][2];
+ /* scale = 1, sat = 0 */
+ param[1] = (rgb2ycbcr_coeff[3][0] >> 5) | (1UL << 8);
+ _ipu_write_param_mem(address, param, 2);
+ dev_dbg(g_ipu_dev,
+ "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+ address, param[0], param[1]);
+
+ param[0] =
+ (rgb2ycbcr_coeff[3][1] << 27) | (rgb2ycbcr_coeff[0][1] <<
+ 18) |
+ (rgb2ycbcr_coeff[1][0] << 9) | rgb2ycbcr_coeff[2][0];
+ param[1] = (rgb2ycbcr_coeff[3][1] >> 5);
+ address += 1L << 3;
+ _ipu_write_param_mem(address, param, 2);
+ dev_dbg(g_ipu_dev,
+ "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+ address, param[0], param[1]);
+
+ param[0] =
+ (rgb2ycbcr_coeff[3][2] << 27) | (rgb2ycbcr_coeff[0][2] <<
+ 18) |
+ (rgb2ycbcr_coeff[1][2] << 9) | rgb2ycbcr_coeff[2][1];
+ param[1] = (rgb2ycbcr_coeff[3][2] >> 5);
+ address += 1L << 3;
+ _ipu_write_param_mem(address, param, 2);
+ dev_dbg(g_ipu_dev,
+ "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+ address, param[0], param[1]);
+ } else if ((in_format == RGB) && (out_format == RGB)) {
+ /* Init CSC1 */
+ param[0] =
+ (rgb2rgb_coeff[3][0] << 27) | (rgb2rgb_coeff[0][0] << 18) |
+ (rgb2rgb_coeff[1][1] << 9) | rgb2rgb_coeff[2][2];
+ /* scale = 2, sat = 0 */
+ param[1] = (rgb2rgb_coeff[3][0] >> 5) | (2UL << 8);
+
+ _ipu_write_param_mem(address, param, 2);
+
+ dev_dbg(g_ipu_dev,
+ "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+ address, param[0], param[1]);
+
+ param[0] =
+ (rgb2rgb_coeff[3][1] << 27) | (rgb2rgb_coeff[0][1] << 18) |
+ (rgb2rgb_coeff[1][0] << 9) | rgb2rgb_coeff[2][0];
+ param[1] = (rgb2rgb_coeff[3][1] >> 5);
+
+ address += 1L << 3;
+ _ipu_write_param_mem(address, param, 2);
+
+ dev_dbg(g_ipu_dev,
+ "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+ address, param[0], param[1]);
+
+ param[0] =
+ (rgb2rgb_coeff[3][2] << 27) | (rgb2rgb_coeff[0][2] << 18) |
+ (rgb2rgb_coeff[1][2] << 9) | rgb2rgb_coeff[2][1];
+ param[1] = (rgb2rgb_coeff[3][2] >> 5);
+
+ address += 1L << 3;
+ _ipu_write_param_mem(address, param, 2);
+
+ dev_dbg(g_ipu_dev,
+ "addr 0x%04X: word0 = 0x%08X, word1 = 0x%08X\n",
+ address, param[0], param[1]);
+ } else {
+ dev_err(g_ipu_dev, "Unsupported color space conversion\n");
+ }
+}
+
+static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize,
+ uint32_t * resizeCoeff,
+ uint32_t * downsizeCoeff)
+{
+ uint32_t tempSize;
+ uint32_t tempDownsize;
+
+ /* Cannot downsize more than 8:1 */
+ if ((outSize << 3) < inSize) {
+ return false;
+ }
+ /* compute downsizing coefficient */
+ tempDownsize = 0;
+ tempSize = inSize;
+ while ((tempSize >= outSize * 2) && (tempDownsize < 2)) {
+ tempSize >>= 1;
+ tempDownsize++;
+ }
+ *downsizeCoeff = tempDownsize;
+
+ /* compute resizing coefficient using the following equation:
+ resizeCoeff = M*(SI -1)/(SO - 1)
+ where M = 2^13, SI - input size, SO - output size */
+ *resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
+ if (*resizeCoeff >= 16384L) {
+ dev_err(g_ipu_dev, "Warning! Overflow on resize coeff.\n");
+ *resizeCoeff = 0x3FFF;
+ }
+
+ dev_dbg(g_ipu_dev, "resizing from %u -> %u pixels, "
+ "downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize,
+ *downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0,
+ ((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff);
+
+ return true;
+}
diff --git a/drivers/mxc/ipu/ipu_param_mem.h b/drivers/mxc/ipu/ipu_param_mem.h
new file mode 100644
index 000000000000..07bd03a81e3f
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_param_mem.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __INCLUDE_IPU_PARAM_MEM_H__
+#define __INCLUDE_IPU_PARAM_MEM_H__
+
+#include <linux/types.h>
+
+static __inline void _ipu_ch_param_set_size(uint32_t * params,
+ uint32_t pixel_fmt, uint16_t width,
+ uint16_t height, uint16_t stride,
+ uint32_t u, uint32_t v)
+{
+ uint32_t u_offset = 0;
+ uint32_t v_offset = 0;
+ memset(params, 0, 40);
+
+ params[3] =
+ (uint32_t) ((width - 1) << 12) | ((uint32_t) (height - 1) << 24);
+ params[4] = (uint32_t) (height - 1) >> 8;
+ params[7] = (uint32_t) (stride - 1) << 3;
+
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_GENERIC:
+ /*Represents 8-bit Generic data */
+ params[7] |= 3 | (7UL << (81 - 64)) | (31L << (89 - 64)); /* BPP & PFS */
+ params[8] = 2; /* SAT = use 32-bit access */
+ break;
+ case IPU_PIX_FMT_GENERIC_32:
+ /*Represents 32-bit Generic data */
+ params[7] |= (7UL << (81 - 64)) | (7L << (89 - 64)); /* BPP & PFS */
+ params[8] = 2; /* SAT = use 32-bit access */
+ break;
+ case IPU_PIX_FMT_RGB565:
+ params[7] |= 2L | (4UL << (81 - 64)) | (7L << (89 - 64)); /* BPP & PFS */
+ params[8] = 2 | /* SAT = 32-bit access */
+ (0UL << (99 - 96)) | /* Red bit offset */
+ (5UL << (104 - 96)) | /* Green bit offset */
+ (11UL << (109 - 96)) | /* Blue bit offset */
+ (16UL << (114 - 96)) | /* Alpha bit offset */
+ (4UL << (119 - 96)) | /* Red bit width - 1 */
+ (5UL << (122 - 96)) | /* Green bit width - 1 */
+ (4UL << (125 - 96)); /* Blue bit width - 1 */
+ break;
+ case IPU_PIX_FMT_BGR24: /* 24 BPP & RGB PFS */
+ params[7] |= 1 | (4UL << (81 - 64)) | (7L << (89 - 64));
+ params[8] = 2 | /* SAT = 32-bit access */
+ (8UL << (104 - 96)) | /* Green bit offset */
+ (16UL << (109 - 96)) | /* Blue bit offset */
+ (24UL << (114 - 96)) | /* Alpha bit offset */
+ (7UL << (119 - 96)) | /* Red bit width - 1 */
+ (7UL << (122 - 96)) | /* Green bit width - 1 */
+ (uint32_t) (7UL << (125 - 96)); /* Blue bit width - 1 */
+ break;
+ case IPU_PIX_FMT_RGB24: /* 24 BPP & RGB PFS */
+ params[7] |= 1 | (4UL << (81 - 64)) | (7L << (89 - 64));
+ params[8] = 2 | /* SAT = 32-bit access */
+ (16UL << (99 - 96)) | /* Red bit offset */
+ (8UL << (104 - 96)) | /* Green bit offset */
+ (0UL << (109 - 96)) | /* Blue bit offset */
+ (24UL << (114 - 96)) | /* Alpha bit offset */
+ (7UL << (119 - 96)) | /* Red bit width - 1 */
+ (7UL << (122 - 96)) | /* Green bit width - 1 */
+ (uint32_t) (7UL << (125 - 96)); /* Blue bit width - 1 */
+ break;
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_BGR32:
+ /* BPP & pixel fmt */
+ params[7] |= 0 | (4UL << (81 - 64)) | (7 << (89 - 64));
+ params[8] = 2 | /* SAT = 32-bit access */
+ (8UL << (99 - 96)) | /* Red bit offset */
+ (16UL << (104 - 96)) | /* Green bit offset */
+ (24UL << (109 - 96)) | /* Blue bit offset */
+ (0UL << (114 - 96)) | /* Alpha bit offset */
+ (7UL << (119 - 96)) | /* Red bit width - 1 */
+ (7UL << (122 - 96)) | /* Green bit width - 1 */
+ (uint32_t) (7UL << (125 - 96)); /* Blue bit width - 1 */
+ params[9] = 7; /* Alpha bit width - 1 */
+ break;
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_RGB32:
+ /* BPP & pixel fmt */
+ params[7] |= 0 | (4UL << (81 - 64)) | (7 << (89 - 64));
+ params[8] = 2 | /* SAT = 32-bit access */
+ (24UL << (99 - 96)) | /* Red bit offset */
+ (16UL << (104 - 96)) | /* Green bit offset */
+ (8UL << (109 - 96)) | /* Blue bit offset */
+ (0UL << (114 - 96)) | /* Alpha bit offset */
+ (7UL << (119 - 96)) | /* Red bit width - 1 */
+ (7UL << (122 - 96)) | /* Green bit width - 1 */
+ (uint32_t) (7UL << (125 - 96)); /* Blue bit width - 1 */
+ params[9] = 7; /* Alpha bit width - 1 */
+ break;
+ case IPU_PIX_FMT_ABGR32:
+ /* BPP & pixel fmt */
+ params[7] |= 0 | (4UL << (81 - 64)) | (7 << (89 - 64));
+ params[8] = 2 | /* SAT = 32-bit access */
+ (0UL << (99 - 96)) | /* Alpha bit offset */
+ (8UL << (104 - 96)) | /* Blue bit offset */
+ (16UL << (109 - 96)) | /* Green bit offset */
+ (24UL << (114 - 96)) | /* Red bit offset */
+ (7UL << (119 - 96)) | /* Alpha bit width - 1 */
+ (7UL << (122 - 96)) | /* Blue bit width - 1 */
+ (uint32_t) (7UL << (125 - 96)); /* Green bit width - 1 */
+ params[9] = 7; /* Red bit width - 1 */
+ break;
+ case IPU_PIX_FMT_UYVY:
+ /* BPP & pixel format */
+ params[7] |= 2 | (6UL << 17) | (7 << (89 - 64));
+ params[8] = 2; /* SAT = 32-bit access */
+ break;
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ /* BPP & pixel format */
+ params[7] |= 3 | (3UL << 17) | (7 << (89 - 64));
+ params[8] = 2; /* SAT = 32-bit access */
+ u_offset = (u == 0) ? stride * height : u;
+ v_offset = (v == 0) ? u_offset + u_offset / 4 : v;
+ break;
+ case IPU_PIX_FMT_YVU422P:
+ /* BPP & pixel format */
+ params[7] |= 3 | (2UL << 17) | (7 << (89 - 64));
+ params[8] = 2; /* SAT = 32-bit access */
+ v_offset = (v == 0) ? stride * height : v;
+ u_offset = (u == 0) ? v_offset + v_offset / 2 : u;
+ break;
+ case IPU_PIX_FMT_YUV422P:
+ /* BPP & pixel format */
+ params[7] |= 3 | (2UL << 17) | (7 << (89 - 64));
+ params[8] = 2; /* SAT = 32-bit access */
+ u_offset = (u == 0) ? stride * height : u;
+ v_offset = (v == 0) ? u_offset + u_offset / 2 : v;
+ break;
+ default:
+ dev_err(g_ipu_dev, "mxc ipu: unimplemented pixel format\n");
+ break;
+ }
+
+ params[1] = (1UL << (46 - 32)) | (u_offset << (53 - 32));
+ params[2] = u_offset >> (64 - 53);
+ params[2] |= v_offset << (79 - 64);
+ params[3] |= v_offset >> (96 - 79);
+}
+
+static __inline void _ipu_ch_param_set_burst_size(uint32_t * params,
+ uint16_t burst_pixels)
+{
+ params[7] &= ~(0x3FL << (89 - 64));
+ params[7] |= (uint32_t) (burst_pixels - 1) << (89 - 64);
+};
+
+static __inline void _ipu_ch_param_set_buffer(uint32_t * params,
+ dma_addr_t buf0, dma_addr_t buf1)
+{
+ params[5] = buf0;
+ params[6] = buf1;
+};
+
+static __inline void _ipu_ch_param_set_rotation(uint32_t * params,
+ ipu_rotate_mode_t rot)
+{
+ params[7] |= (uint32_t) rot << (84 - 64);
+};
+
+void _ipu_write_param_mem(uint32_t addr, uint32_t * data, uint32_t numWords);
+
+#endif
diff --git a/drivers/mxc/ipu/ipu_prv.h b/drivers/mxc/ipu/ipu_prv.h
new file mode 100644
index 000000000000..b76a2f2357fd
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_prv.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __INCLUDE_IPU_PRV_H__
+#define __INCLUDE_IPU_PRV_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <mach/hardware.h>
+
+/* Globals */
+extern struct device *g_ipu_dev;
+extern spinlock_t ipu_lock;
+extern struct clk *g_ipu_clk;
+extern struct clk *g_ipu_csi_clk;
+
+int register_ipu_device(void);
+ipu_color_space_t format_to_colorspace(uint32_t fmt);
+
+uint32_t _ipu_channel_status(ipu_channel_t channel);
+
+void _ipu_sdc_fg_init(ipu_channel_params_t * params);
+uint32_t _ipu_sdc_fg_uninit(void);
+void _ipu_sdc_bg_init(ipu_channel_params_t * params);
+uint32_t _ipu_sdc_bg_uninit(void);
+
+void _ipu_ic_enable_task(ipu_channel_t channel);
+void _ipu_ic_disable_task(ipu_channel_t channel);
+void _ipu_ic_init_prpvf(ipu_channel_params_t * params, bool src_is_csi);
+void _ipu_ic_uninit_prpvf(void);
+void _ipu_ic_init_rotate_vf(ipu_channel_params_t * params);
+void _ipu_ic_uninit_rotate_vf(void);
+void _ipu_ic_init_csi(ipu_channel_params_t * params);
+void _ipu_ic_uninit_csi(void);
+void _ipu_ic_init_prpenc(ipu_channel_params_t * params, bool src_is_csi);
+void _ipu_ic_uninit_prpenc(void);
+void _ipu_ic_init_rotate_enc(ipu_channel_params_t * params);
+void _ipu_ic_uninit_rotate_enc(void);
+void _ipu_ic_init_pp(ipu_channel_params_t * params);
+void _ipu_ic_uninit_pp(void);
+void _ipu_ic_init_rotate_pp(ipu_channel_params_t * params);
+void _ipu_ic_uninit_rotate_pp(void);
+
+int32_t _ipu_adc_init_channel(ipu_channel_t chan, display_port_t disp,
+ mcu_mode_t cmd, int16_t x_pos, int16_t y_pos);
+int32_t _ipu_adc_uninit_channel(ipu_channel_t chan);
+
+#endif /* __INCLUDE_IPU_PRV_H__ */
diff --git a/drivers/mxc/ipu/ipu_regs.h b/drivers/mxc/ipu/ipu_regs.h
new file mode 100644
index 000000000000..d3ac76ea7834
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_regs.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * @file ipu_regs.h
+ *
+ * @brief IPU Register definitions
+ *
+ * @ingroup IPU
+ */
+#ifndef __IPU_REGS_INCLUDED__
+#define __IPU_REGS_INCLUDED__
+
+#define IPU_REG_BASE IO_ADDRESS(IPU_CTRL_BASE_ADDR)
+
+/* Register addresses */
+/* IPU Common registers */
+#define IPU_CONF (IPU_REG_BASE + 0x0000)
+#define IPU_CHA_BUF0_RDY (IPU_REG_BASE + 0x0004)
+#define IPU_CHA_BUF1_RDY (IPU_REG_BASE + 0x0008)
+#define IPU_CHA_DB_MODE_SEL (IPU_REG_BASE + 0x000C)
+#define IPU_CHA_CUR_BUF (IPU_REG_BASE + 0x0010)
+#define IPU_FS_PROC_FLOW (IPU_REG_BASE + 0x0014)
+#define IPU_FS_DISP_FLOW (IPU_REG_BASE + 0x0018)
+#define IPU_TASKS_STAT (IPU_REG_BASE + 0x001C)
+#define IPU_IMA_ADDR (IPU_REG_BASE + 0x0020)
+#define IPU_IMA_DATA (IPU_REG_BASE + 0x0024)
+#define IPU_INT_CTRL_1 (IPU_REG_BASE + 0x0028)
+#define IPU_INT_CTRL_2 (IPU_REG_BASE + 0x002C)
+#define IPU_INT_CTRL_3 (IPU_REG_BASE + 0x0030)
+#define IPU_INT_CTRL_4 (IPU_REG_BASE + 0x0034)
+#define IPU_INT_CTRL_5 (IPU_REG_BASE + 0x0038)
+#define IPU_INT_STAT_1 (IPU_REG_BASE + 0x003C)
+#define IPU_INT_STAT_2 (IPU_REG_BASE + 0x0040)
+#define IPU_INT_STAT_3 (IPU_REG_BASE + 0x0044)
+#define IPU_INT_STAT_4 (IPU_REG_BASE + 0x0048)
+#define IPU_INT_STAT_5 (IPU_REG_BASE + 0x004C)
+#define IPU_BRK_CTRL_1 (IPU_REG_BASE + 0x0050)
+#define IPU_BRK_CTRL_2 (IPU_REG_BASE + 0x0054)
+#define IPU_BRK_STAT (IPU_REG_BASE + 0x0058)
+#define IPU_DIAGB_CTRL (IPU_REG_BASE + 0x005C)
+/* CMOS Sensor Interface Registers */
+#define CSI_SENS_CONF (IPU_REG_BASE + 0x0060)
+#define CSI_SENS_FRM_SIZE (IPU_REG_BASE + 0x0064)
+#define CSI_ACT_FRM_SIZE (IPU_REG_BASE + 0x0068)
+#define CSI_OUT_FRM_CTRL (IPU_REG_BASE + 0x006C)
+#define CSI_TST_CTRL (IPU_REG_BASE + 0x0070)
+#define CSI_CCIR_CODE_1 (IPU_REG_BASE + 0x0074)
+#define CSI_CCIR_CODE_2 (IPU_REG_BASE + 0x0078)
+#define CSI_CCIR_CODE_3 (IPU_REG_BASE + 0x007C)
+#define CSI_FLASH_STROBE_1 (IPU_REG_BASE + 0x0080)
+#define CSI_FLASH_STROBE_2 (IPU_REG_BASE + 0x0084)
+/* Image Converter Registers */
+#define IC_CONF (IPU_REG_BASE + 0x0088)
+#define IC_PRP_ENC_RSC (IPU_REG_BASE + 0x008C)
+#define IC_PRP_VF_RSC (IPU_REG_BASE + 0x0090)
+#define IC_PP_RSC (IPU_REG_BASE + 0x0094)
+#define IC_CMBP_1 (IPU_REG_BASE + 0x0098)
+#define IC_CMBP_2 (IPU_REG_BASE + 0x009C)
+#define PF_CONF (IPU_REG_BASE + 0x00A0)
+#define IDMAC_CONF (IPU_REG_BASE + 0x00A4)
+#define IDMAC_CHA_EN (IPU_REG_BASE + 0x00A8)
+#define IDMAC_CHA_PRI (IPU_REG_BASE + 0x00AC)
+#define IDMAC_CHA_BUSY (IPU_REG_BASE + 0x00B0)
+/* SDC Registers */
+#define SDC_COM_CONF (IPU_REG_BASE + 0x00B4)
+#define SDC_GW_CTRL (IPU_REG_BASE + 0x00B8)
+#define SDC_FG_POS (IPU_REG_BASE + 0x00BC)
+#define SDC_BG_POS (IPU_REG_BASE + 0x00C0)
+#define SDC_CUR_POS (IPU_REG_BASE + 0x00C4)
+#define SDC_PWM_CTRL (IPU_REG_BASE + 0x00C8)
+#define SDC_CUR_MAP (IPU_REG_BASE + 0x00CC)
+#define SDC_HOR_CONF (IPU_REG_BASE + 0x00D0)
+#define SDC_VER_CONF (IPU_REG_BASE + 0x00D4)
+#define SDC_SHARP_CONF_1 (IPU_REG_BASE + 0x00D8)
+#define SDC_SHARP_CONF_2 (IPU_REG_BASE + 0x00DC)
+/* ADC Registers */
+#define ADC_CONF (IPU_REG_BASE + 0x00E0)
+#define ADC_SYSCHA1_SA (IPU_REG_BASE + 0x00E4)
+#define ADC_SYSCHA2_SA (IPU_REG_BASE + 0x00E8)
+#define ADC_PRPCHAN_SA (IPU_REG_BASE + 0x00EC)
+#define ADC_PPCHAN_SA (IPU_REG_BASE + 0x00F0)
+#define ADC_DISP0_CONF (IPU_REG_BASE + 0x00F4)
+#define ADC_DISP0_RD_AP (IPU_REG_BASE + 0x00F8)
+#define ADC_DISP0_RDM (IPU_REG_BASE + 0x00FC)
+#define ADC_DISP0_SS (IPU_REG_BASE + 0x0100)
+#define ADC_DISP1_CONF (IPU_REG_BASE + 0x0104)
+#define ADC_DISP1_RD_AP (IPU_REG_BASE + 0x0108)
+#define ADC_DISP1_RDM (IPU_REG_BASE + 0x010C)
+#define ADC_DISP12_SS (IPU_REG_BASE + 0x0110)
+#define ADC_DISP2_CONF (IPU_REG_BASE + 0x0114)
+#define ADC_DISP2_RD_AP (IPU_REG_BASE + 0x0118)
+#define ADC_DISP2_RDM (IPU_REG_BASE + 0x011C)
+#define ADC_DISP_VSYNC (IPU_REG_BASE + 0x0120)
+/* Display Interface re(sters */
+#define DI_DISP_IF_CONF (IPU_REG_BASE + 0x0124)
+#define DI_DISP_SIG_POL (IPU_REG_BASE + 0x0128)
+#define DI_SER_DISP1_CONF (IPU_REG_BASE + 0x012C)
+#define DI_SER_DISP2_CONF (IPU_REG_BASE + 0x0130)
+#define DI_HSP_CLK_PER (IPU_REG_BASE + 0x0134)
+#define DI_DISP0_TIME_CONF_1 (IPU_REG_BASE + 0x0138)
+#define DI_DISP0_TIME_CONF_2 (IPU_REG_BASE + 0x013C)
+#define DI_DISP0_TIME_CONF_3 (IPU_REG_BASE + 0x0140)
+#define DI_DISP1_TIME_CONF_1 (IPU_REG_BASE + 0x0144)
+#define DI_DISP1_TIME_CONF_2 (IPU_REG_BASE + 0x0148)
+#define DI_DISP1_TIME_CONF_3 (IPU_REG_BASE + 0x014C)
+#define DI_DISP2_TIME_CONF_1 (IPU_REG_BASE + 0x0150)
+#define DI_DISP2_TIME_CONF_2 (IPU_REG_BASE + 0x0154)
+#define DI_DISP2_TIME_CONF_3 (IPU_REG_BASE + 0x0158)
+#define DI_DISP3_TIME_CONF (IPU_REG_BASE + 0x015C)
+#define DI_DISP0_DB0_MAP (IPU_REG_BASE + 0x0160)
+#define DI_DISP0_DB1_MAP (IPU_REG_BASE + 0x0164)
+#define DI_DISP0_DB2_MAP (IPU_REG_BASE + 0x0168)
+#define DI_DISP0_CB0_MAP (IPU_REG_BASE + 0x016C)
+#define DI_DISP0_CB1_MAP (IPU_REG_BASE + 0x0170)
+#define DI_DISP0_CB2_MAP (IPU_REG_BASE + 0x0174)
+#define DI_DISP1_DB0_MAP (IPU_REG_BASE + 0x0178)
+#define DI_DISP1_DB1_MAP (IPU_REG_BASE + 0x017C)
+#define DI_DISP1_DB2_MAP (IPU_REG_BASE + 0x0180)
+#define DI_DISP1_CB0_MAP (IPU_REG_BASE + 0x0184)
+#define DI_DISP1_CB1_MAP (IPU_REG_BASE + 0x0188)
+#define DI_DISP1_CB2_MAP (IPU_REG_BASE + 0x018C)
+#define DI_DISP2_DB0_MAP (IPU_REG_BASE + 0x0190)
+#define DI_DISP2_DB1_MAP (IPU_REG_BASE + 0x0194)
+#define DI_DISP2_DB2_MAP (IPU_REG_BASE + 0x0198)
+#define DI_DISP2_CB0_MAP (IPU_REG_BASE + 0x019C)
+#define DI_DISP2_CB1_MAP (IPU_REG_BASE + 0x01A0)
+#define DI_DISP2_CB2_MAP (IPU_REG_BASE + 0x01A4)
+#define DI_DISP3_B0_MAP (IPU_REG_BASE + 0x01A8)
+#define DI_DISP3_B1_MAP (IPU_REG_BASE + 0x01AC)
+#define DI_DISP3_B2_MAP (IPU_REG_BASE + 0x01B0)
+#define DI_DISP_ACC_CC (IPU_REG_BASE + 0x01B4)
+#define DI_DISP_LLA_CONF (IPU_REG_BASE + 0x01B8)
+#define DI_DISP_LLA_DATA (IPU_REG_BASE + 0x01BC)
+
+#define IPUIRQ_2_STATREG(int) (IPU_INT_STAT_1 + 4*(int>>5))
+#define IPUIRQ_2_CTRLREG(int) (IPU_INT_CTRL_1 + 4*(int>>5))
+#define IPUIRQ_2_MASK(int) (1UL << (int & 0x1F))
+
+enum {
+ IPU_CONF_CSI_EN = 0x00000001,
+ IPU_CONF_IC_EN = 0x00000002,
+ IPU_CONF_ROT_EN = 0x00000004,
+ IPU_CONF_PF_EN = 0x00000008,
+ IPU_CONF_SDC_EN = 0x00000010,
+ IPU_CONF_ADC_EN = 0x00000020,
+ IPU_CONF_DI_EN = 0x00000040,
+ IPU_CONF_DU_EN = 0x00000080,
+ IPU_CONF_PXL_ENDIAN = 0x00000100,
+
+ FS_PRPVF_ROT_SRC_SEL = 0x00000040,
+ FS_PRPENC_ROT_SRC_SEL = 0x00000020,
+ FS_PRPENC_DEST_SEL = 0x00000010,
+ FS_PP_SRC_SEL_MASK = 0x00000300,
+ FS_PP_SRC_SEL_OFFSET = 8,
+ FS_PP_ROT_SRC_SEL_MASK = 0x00000C00,
+ FS_PP_ROT_SRC_SEL_OFFSET = 10,
+ FS_PF_DEST_SEL_MASK = 0x00003000,
+ FS_PF_DEST_SEL_OFFSET = 12,
+ FS_PRPVF_DEST_SEL_MASK = 0x00070000,
+ FS_PRPVF_DEST_SEL_OFFSET = 16,
+ FS_PRPVF_ROT_DEST_SEL_MASK = 0x00700000,
+ FS_PRPVF_ROT_DEST_SEL_OFFSET = 20,
+ FS_PP_DEST_SEL_MASK = 0x07000000,
+ FS_PP_DEST_SEL_OFFSET = 24,
+ FS_PP_ROT_DEST_SEL_MASK = 0x70000000,
+ FS_PP_ROT_DEST_SEL_OFFSET = 28,
+ FS_VF_IN_VALID = 0x00000002,
+ FS_ENC_IN_VALID = 0x00000001,
+
+ FS_SDC_BG_SRC_SEL_MASK = 0x00000007,
+ FS_SDC_BG_SRC_SEL_OFFSET = 0,
+ FS_SDC_FG_SRC_SEL_MASK = 0x00000070,
+ FS_SDC_FG_SRC_SEL_OFFSET = 4,
+ FS_ADC1_SRC_SEL_MASK = 0x00000700,
+ FS_ADC1_SRC_SEL_OFFSET = 8,
+ FS_ADC2_SRC_SEL_MASK = 0x00007000,
+ FS_ADC2_SRC_SEL_OFFSET = 12,
+ FS_AUTO_REF_PER_MASK = 0x03FF0000,
+ FS_AUTO_REF_PER_OFFSET = 16,
+
+ FS_DEST_ARM = 0,
+ FS_DEST_ROT = 1,
+ FS_DEST_PP = 1,
+ FS_DEST_ADC1 = 2,
+ FS_DEST_ADC2 = 3,
+ FS_DEST_SDC_BG = 4,
+ FS_DEST_SDC_FG = 5,
+ FS_DEST_ADC = 6,
+
+ FS_SRC_ARM = 0,
+ FS_PP_SRC_PF = 1,
+ FS_PP_SRC_ROT = 2,
+
+ FS_ROT_SRC_PP = 1,
+ FS_ROT_SRC_PF = 2,
+
+ FS_PF_DEST_PP = 1,
+ FS_PF_DEST_ROT = 2,
+
+ FS_SRC_ROT_VF = 1,
+ FS_SRC_ROT_PP = 2,
+ FS_SRC_VF = 3,
+ FS_SRC_PP = 4,
+ FS_SRC_SNOOP = 5,
+ FS_SRC_AUTOREF = 6,
+ FS_SRC_AUTOREF_SNOOP = 7,
+
+ TSTAT_PF_H264_PAUSE = 0x00000001,
+ TSTAT_CSI2MEM_MASK = 0x0000000C,
+ TSTAT_CSI2MEM_OFFSET = 2,
+ TSTAT_VF_MASK = 0x00000600,
+ TSTAT_VF_OFFSET = 9,
+ TSTAT_VF_ROT_MASK = 0x000C0000,
+ TSTAT_VF_ROT_OFFSET = 18,
+ TSTAT_ENC_MASK = 0x00000180,
+ TSTAT_ENC_OFFSET = 7,
+ TSTAT_ENC_ROT_MASK = 0x00030000,
+ TSTAT_ENC_ROT_OFFSET = 16,
+ TSTAT_PP_MASK = 0x00001800,
+ TSTAT_PP_OFFSET = 11,
+ TSTAT_PP_ROT_MASK = 0x00300000,
+ TSTAT_PP_ROT_OFFSET = 20,
+ TSTAT_PF_MASK = 0x00C00000,
+ TSTAT_PF_OFFSET = 22,
+ TSTAT_ADCSYS1_MASK = 0x03000000,
+ TSTAT_ADCSYS1_OFFSET = 24,
+ TSTAT_ADCSYS2_MASK = 0x0C000000,
+ TSTAT_ADCSYS2_OFFSET = 26,
+
+ TASK_STAT_IDLE = 0,
+ TASK_STAT_ACTIVE = 1,
+ TASK_STAT_WAIT4READY = 2,
+
+ /* Register bits */
+ SDC_COM_TFT_COLOR = 0x00000001UL,
+ SDC_COM_FG_EN = 0x00000010UL,
+ SDC_COM_GWSEL = 0x00000020UL,
+ SDC_COM_GLB_A = 0x00000040UL,
+ SDC_COM_KEY_COLOR_G = 0x00000080UL,
+ SDC_COM_BG_EN = 0x00000200UL,
+ SDC_COM_SHARP = 0x00001000UL,
+
+ SDC_V_SYNC_WIDTH_L = 0x00000001UL,
+
+ ADC_CONF_PRP_EN = 0x00000001L,
+ ADC_CONF_PP_EN = 0x00000002L,
+ ADC_CONF_MCU_EN = 0x00000004L,
+
+ ADC_DISP_CONF_SL_MASK = 0x00000FFFL,
+ ADC_DISP_CONF_TYPE_MASK = 0x00003000L,
+ ADC_DISP_CONF_TYPE_XY = 0x00002000L,
+
+ ADC_DISP_VSYNC_D0_MODE_MASK = 0x00000003L,
+ ADC_DISP_VSYNC_D0_WIDTH_MASK = 0x003F0000L,
+ ADC_DISP_VSYNC_D12_MODE_MASK = 0x0000000CL,
+ ADC_DISP_VSYNC_D12_WIDTH_MASK = 0x3F000000L,
+
+ /* Image Converter Register bits */
+ IC_CONF_PRPENC_EN = 0x00000001,
+ IC_CONF_PRPENC_CSC1 = 0x00000002,
+ IC_CONF_PRPENC_ROT_EN = 0x00000004,
+ IC_CONF_PRPVF_EN = 0x00000100,
+ IC_CONF_PRPVF_CSC1 = 0x00000200,
+ IC_CONF_PRPVF_CSC2 = 0x00000400,
+ IC_CONF_PRPVF_CMB = 0x00000800,
+ IC_CONF_PRPVF_ROT_EN = 0x00001000,
+ IC_CONF_PP_EN = 0x00010000,
+ IC_CONF_PP_CSC1 = 0x00020000,
+ IC_CONF_PP_CSC2 = 0x00040000,
+ IC_CONF_PP_CMB = 0x00080000,
+ IC_CONF_PP_ROT_EN = 0x00100000,
+ IC_CONF_IC_GLB_LOC_A = 0x10000000,
+ IC_CONF_KEY_COLOR_EN = 0x20000000,
+ IC_CONF_RWS_EN = 0x40000000,
+ IC_CONF_CSI_MEM_WR_EN = 0x80000000,
+
+ IDMA_CHAN_INVALID = 0x000000FF,
+ IDMA_IC_0 = 0x00000001,
+ IDMA_IC_1 = 0x00000002,
+ IDMA_IC_2 = 0x00000004,
+ IDMA_IC_3 = 0x00000008,
+ IDMA_IC_4 = 0x00000010,
+ IDMA_IC_5 = 0x00000020,
+ IDMA_IC_6 = 0x00000040,
+ IDMA_IC_7 = 0x00000080,
+ IDMA_IC_8 = 0x00000100,
+ IDMA_IC_9 = 0x00000200,
+ IDMA_IC_10 = 0x00000400,
+ IDMA_IC_11 = 0x00000800,
+ IDMA_IC_12 = 0x00001000,
+ IDMA_IC_13 = 0x00002000,
+ IDMA_SDC_BG = 0x00004000,
+ IDMA_SDC_FG = 0x00008000,
+ IDMA_SDC_MASK = 0x00010000,
+ IDMA_SDC_PARTIAL = 0x00020000,
+ IDMA_ADC_SYS1_WR = 0x00040000,
+ IDMA_ADC_SYS2_WR = 0x00080000,
+ IDMA_ADC_SYS1_CMD = 0x00100000,
+ IDMA_ADC_SYS2_CMD = 0x00200000,
+ IDMA_ADC_SYS1_RD = 0x00400000,
+ IDMA_ADC_SYS2_RD = 0x00800000,
+ IDMA_PF_QP = 0x01000000,
+ IDMA_PF_BSP = 0x02000000,
+ IDMA_PF_Y_IN = 0x04000000,
+ IDMA_PF_U_IN = 0x08000000,
+ IDMA_PF_V_IN = 0x10000000,
+ IDMA_PF_Y_OUT = 0x20000000,
+ IDMA_PF_U_OUT = 0x40000000,
+ IDMA_PF_V_OUT = 0x80000000,
+
+ CSI_SENS_CONF_DATA_FMT_SHIFT = 8,
+ CSI_SENS_CONF_DATA_FMT_RGB_YUV444 = 0x00000000L,
+ CSI_SENS_CONF_DATA_FMT_YUV422 = 0x00000200L,
+ CSI_SENS_CONF_DATA_FMT_BAYER = 0x00000300L,
+
+ CSI_SENS_CONF_VSYNC_POL_SHIFT = 0,
+ CSI_SENS_CONF_HSYNC_POL_SHIFT = 1,
+ CSI_SENS_CONF_DATA_POL_SHIFT = 2,
+ CSI_SENS_CONF_PIX_CLK_POL_SHIFT = 3,
+ CSI_SENS_CONF_SENS_PRTCL_SHIFT = 4,
+ CSI_SENS_CONF_SENS_CLKSRC_SHIFT = 7,
+ CSI_SENS_CONF_DATA_WIDTH_SHIFT = 10,
+ CSI_SENS_CONF_EXT_VSYNC_SHIFT = 15,
+ CSI_SENS_CONF_DIVRATIO_SHIFT = 16,
+
+ PF_CONF_TYPE_MASK = 0x00000007,
+ PF_CONF_TYPE_SHIFT = 0,
+ PF_CONF_PAUSE_EN = 0x00000010,
+ PF_CONF_RESET = 0x00008000,
+ PF_CONF_PAUSE_ROW_MASK = 0x00FF0000,
+ PF_CONF_PAUSE_ROW_SHIFT = 16,
+
+ /* DI_DISP_SIG_POL bits */
+ DI_D3_VSYNC_POL_SHIFT = 28,
+ DI_D3_HSYNC_POL_SHIFT = 27,
+ DI_D3_DRDY_SHARP_POL_SHIFT = 26,
+ DI_D3_CLK_POL_SHIFT = 25,
+ DI_D3_DATA_POL_SHIFT = 24,
+
+ /* DI_DISP_IF_CONF bits */
+ DI_D3_CLK_IDLE_SHIFT = 26,
+ DI_D3_CLK_SEL_SHIFT = 25,
+ DI_D3_DATAMSK_SHIFT = 24,
+
+ DISPx_IF_CLK_DOWN_OFFSET = 22,
+ DISPx_IF_CLK_UP_OFFSET = 12,
+ DISPx_IF_CLK_PER_OFFSET = 0,
+ DISPx_IF_CLK_READ_EN_OFFSET = 16,
+ DISPx_PIX_CLK_PER_OFFSET = 0,
+
+ DI_CONF_DISP0_EN = 0x00000001L,
+ DI_CONF_DISP0_IF_MODE_OFFSET = 1,
+ DI_CONF_DISP0_BURST_MODE_OFFSET = 3,
+ DI_CONF_DISP1_EN = 0x00000100L,
+ DI_CONF_DISP1_IF_MODE_OFFSET = 9,
+ DI_CONF_DISP1_BURST_MODE_OFFSET = 12,
+ DI_CONF_DISP2_EN = 0x00010000L,
+ DI_CONF_DISP2_IF_MODE_OFFSET = 17,
+ DI_CONF_DISP2_BURST_MODE_OFFSET = 20,
+
+ DI_SER_DISPx_CONF_SER_BIT_NUM_OFFSET = 16,
+ DI_SER_DISPx_CONF_PREAMBLE_OFFSET = 8,
+ DI_SER_DISPx_CONF_PREAMBLE_LEN_OFFSET = 4,
+ DI_SER_DISPx_CONF_RW_CFG_OFFSET = 1,
+ DI_SER_DISPx_CONF_BURST_MODE_EN = 0x01000000L,
+ DI_SER_DISPx_CONF_PREAMBLE_EN = 0x00000001L,
+
+ /* DI_DISP_ACC_CC */
+ DISP0_IF_CLK_CNT_D_MASK = 0x00000003L,
+ DISP0_IF_CLK_CNT_D_OFFSET = 0,
+ DISP0_IF_CLK_CNT_C_MASK = 0x0000000CL,
+ DISP0_IF_CLK_CNT_C_OFFSET = 2,
+ DISP1_IF_CLK_CNT_D_MASK = 0x00000030L,
+ DISP1_IF_CLK_CNT_D_OFFSET = 4,
+ DISP1_IF_CLK_CNT_C_MASK = 0x000000C0L,
+ DISP1_IF_CLK_CNT_C_OFFSET = 6,
+ DISP2_IF_CLK_CNT_D_MASK = 0x00000300L,
+ DISP2_IF_CLK_CNT_D_OFFSET = 8,
+ DISP2_IF_CLK_CNT_C_MASK = 0x00000C00L,
+ DISP2_IF_CLK_CNT_C_OFFSET = 10,
+ DISP3_IF_CLK_CNT_MASK = 0x00003000L,
+ DISP3_IF_CLK_CNT_OFFSET = 12,
+};
+
+#endif
diff --git a/drivers/mxc/ipu/ipu_sdc.c b/drivers/mxc/ipu/ipu_sdc.c
new file mode 100644
index 000000000000..a36eb6dc6a4e
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_sdc.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_sdc.c
+ *
+ * @brief IPU SDC submodule API functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+static uint32_t g_h_start_width;
+static uint32_t g_v_start_width;
+
+static const uint32_t di_mappings[] = {
+ 0x1600AAAA, 0x00E05555, 0x00070000, 3, /* RGB888 */
+ 0x0005000F, 0x000B000F, 0x0011000F, 1, /* RGB666 */
+ 0x0011000F, 0x000B000F, 0x0005000F, 1, /* BGR666 */
+ 0x0004003F, 0x000A000F, 0x000F003F, 1 /* RGB565 */
+};
+
+/*!
+ * This function is called to initialize a synchronous LCD panel.
+ *
+ * @param panel The type of panel.
+ *
+ * @param pixel_clk Desired pixel clock frequency in Hz.
+ *
+ * @param pixel_fmt Input parameter for pixel format of buffer. Pixel
+ * format is a FOURCC ASCII code.
+ *
+ * @param width The width of panel in pixels.
+ *
+ * @param height The height of panel in pixels.
+ *
+ * @param hStartWidth The number of pixel clocks between the HSYNC
+ * signal pulse and the start of valid data.
+ *
+ * @param hSyncWidth The width of the HSYNC signal in units of pixel
+ * clocks.
+ *
+ * @param hEndWidth The number of pixel clocks between the end of
+ * valid data and the HSYNC signal for next line.
+ *
+ * @param vStartWidth The number of lines between the VSYNC
+ * signal pulse and the start of valid data.
+ *
+ * @param vSyncWidth The width of the VSYNC signal in units of lines
+ *
+ * @param vEndWidth The number of lines between the end of valid
+ * data and the VSYNC signal for next frame.
+ *
+ * @param sig Bitfield of signal polarities for LCD interface.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int32_t ipu_sdc_init_panel(ipu_panel_t panel,
+ uint32_t pixel_clk,
+ uint16_t width, uint16_t height,
+ uint32_t pixel_fmt,
+ uint16_t h_start_width, uint16_t h_sync_width,
+ uint16_t h_end_width, uint16_t v_start_width,
+ uint16_t v_sync_width, uint16_t v_end_width,
+ ipu_di_signal_cfg_t sig)
+{
+ unsigned long lock_flags;
+ uint32_t reg;
+ uint32_t old_conf;
+ uint32_t div;
+
+ dev_dbg(g_ipu_dev, "panel size = %d x %d\n", width, height);
+
+ if ((v_sync_width == 0) || (h_sync_width == 0))
+ return EINVAL;
+
+ /* Init panel size and blanking periods */
+ reg =
+ ((uint32_t) (h_sync_width - 1) << 26) |
+ ((uint32_t) (width + h_sync_width + h_start_width + h_end_width - 1)
+ << 16);
+ __raw_writel(reg, SDC_HOR_CONF);
+
+ reg = ((uint32_t) (v_sync_width - 1) << 26) | SDC_V_SYNC_WIDTH_L |
+ ((uint32_t)
+ (height + v_sync_width + v_start_width + v_end_width - 1) << 16);
+ __raw_writel(reg, SDC_VER_CONF);
+
+ g_h_start_width = h_start_width + h_sync_width;
+ g_v_start_width = v_start_width + v_sync_width;
+
+ switch (panel) {
+ case IPU_PANEL_SHARP_TFT:
+ __raw_writel(0x00FD0102L, SDC_SHARP_CONF_1);
+ __raw_writel(0x00F500F4L, SDC_SHARP_CONF_2);
+ __raw_writel(SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF);
+ break;
+ case IPU_PANEL_TFT:
+ __raw_writel(SDC_COM_TFT_COLOR, SDC_COM_CONF);
+ break;
+ default:
+ return EINVAL;
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ /* Init clocking */
+
+ /* Calculate divider */
+ /* fractional part is 4 bits so simply multiple by 2^4 to get fractional part */
+ dev_dbg(g_ipu_dev, "pixel clk = %d\n", pixel_clk);
+ div = (clk_get_rate(g_ipu_clk) * 16) / pixel_clk;
+ if (div < 0x40) { /* Divider less than 4 */
+ dev_dbg(g_ipu_dev,
+ "InitPanel() - Pixel clock divider less than 1\n");
+ div = 0x40;
+ }
+ /* DISP3_IF_CLK_DOWN_WR is half the divider value and 2 less fraction bits */
+ /* Subtract 1 extra from DISP3_IF_CLK_DOWN_WR based on timing debug */
+ /* DISP3_IF_CLK_UP_WR is 0 */
+ __raw_writel((((div / 8) - 1) << 22) | div, DI_DISP3_TIME_CONF);
+
+ /* DI settings */
+ old_conf = __raw_readl(DI_DISP_IF_CONF) & 0x78FFFFFF;
+ old_conf |= sig.datamask_en << DI_D3_DATAMSK_SHIFT |
+ sig.clksel_en << DI_D3_CLK_SEL_SHIFT |
+ sig.clkidle_en << DI_D3_CLK_IDLE_SHIFT;
+ __raw_writel(old_conf, DI_DISP_IF_CONF);
+
+ old_conf = __raw_readl(DI_DISP_SIG_POL) & 0xE0FFFFFF;
+ old_conf |= sig.data_pol << DI_D3_DATA_POL_SHIFT |
+ sig.clk_pol << DI_D3_CLK_POL_SHIFT |
+ sig.enable_pol << DI_D3_DRDY_SHARP_POL_SHIFT |
+ sig.Hsync_pol << DI_D3_HSYNC_POL_SHIFT |
+ sig.Vsync_pol << DI_D3_VSYNC_POL_SHIFT;
+ __raw_writel(old_conf, DI_DISP_SIG_POL);
+
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_RGB24:
+ __raw_writel(di_mappings[0], DI_DISP3_B0_MAP);
+ __raw_writel(di_mappings[1], DI_DISP3_B1_MAP);
+ __raw_writel(di_mappings[2], DI_DISP3_B2_MAP);
+ __raw_writel(__raw_readl(DI_DISP_ACC_CC) |
+ ((di_mappings[3] - 1) << 12), DI_DISP_ACC_CC);
+ break;
+ case IPU_PIX_FMT_RGB666:
+ __raw_writel(di_mappings[4], DI_DISP3_B0_MAP);
+ __raw_writel(di_mappings[5], DI_DISP3_B1_MAP);
+ __raw_writel(di_mappings[6], DI_DISP3_B2_MAP);
+ __raw_writel(__raw_readl(DI_DISP_ACC_CC) |
+ ((di_mappings[7] - 1) << 12), DI_DISP_ACC_CC);
+ break;
+ case IPU_PIX_FMT_BGR666:
+ __raw_writel(di_mappings[8], DI_DISP3_B0_MAP);
+ __raw_writel(di_mappings[9], DI_DISP3_B1_MAP);
+ __raw_writel(di_mappings[10], DI_DISP3_B2_MAP);
+ __raw_writel(__raw_readl(DI_DISP_ACC_CC) |
+ ((di_mappings[11] - 1) << 12), DI_DISP_ACC_CC);
+ break;
+ default:
+ __raw_writel(di_mappings[12], DI_DISP3_B0_MAP);
+ __raw_writel(di_mappings[13], DI_DISP3_B1_MAP);
+ __raw_writel(di_mappings[14], DI_DISP3_B2_MAP);
+ __raw_writel(__raw_readl(DI_DISP_ACC_CC) |
+ ((di_mappings[15] - 1) << 12), DI_DISP_ACC_CC);
+ break;
+ }
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ dev_dbg(g_ipu_dev, "DI_DISP_IF_CONF = 0x%08X\n",
+ __raw_readl(DI_DISP_IF_CONF));
+ dev_dbg(g_ipu_dev, "DI_DISP_SIG_POL = 0x%08X\n",
+ __raw_readl(DI_DISP_SIG_POL));
+ dev_dbg(g_ipu_dev, "DI_DISP3_TIME_CONF = 0x%08X\n",
+ __raw_readl(DI_DISP3_TIME_CONF));
+
+ return 0;
+}
+
+/*!
+ * This function sets the foreground and background plane global alpha blending
+ * modes.
+ *
+ * @param enable Boolean to enable or disable global alpha
+ * blending. If disabled, per pixel blending is used.
+ *
+ * @param alpha Global alpha value.
+ *
+ * @return This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_sdc_set_global_alpha(bool enable, uint8_t alpha)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if (enable) {
+ reg = __raw_readl(SDC_GW_CTRL) & 0x00FFFFFFL;
+ __raw_writel(reg | ((uint32_t) alpha << 24), SDC_GW_CTRL);
+
+ reg = __raw_readl(SDC_COM_CONF);
+ __raw_writel(reg | SDC_COM_GLB_A, SDC_COM_CONF);
+ } else {
+ reg = __raw_readl(SDC_COM_CONF);
+ __raw_writel(reg & ~SDC_COM_GLB_A, SDC_COM_CONF);
+ }
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+
+/*!
+ * This function sets the transparent color key for SDC graphic plane.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param enable Boolean to enable or disable color key
+ *
+ * @param colorKey 24-bit RGB color to use as transparent color key.
+ *
+ * @return This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_sdc_set_color_key(ipu_channel_t channel, bool enable,
+ uint32_t color_key)
+{
+ uint32_t reg, sdc_conf;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ sdc_conf = __raw_readl(SDC_COM_CONF);
+ if (channel == MEM_SDC_BG) {
+ sdc_conf &= ~SDC_COM_GWSEL;
+ } else {
+ sdc_conf |= SDC_COM_GWSEL;
+ }
+
+ if (enable) {
+ reg = __raw_readl(SDC_GW_CTRL) & 0xFF000000L;
+ __raw_writel(reg | (color_key & 0x00FFFFFFL), SDC_GW_CTRL);
+
+ sdc_conf |= SDC_COM_KEY_COLOR_G;
+ } else {
+ sdc_conf &= ~SDC_COM_KEY_COLOR_G;
+ }
+ __raw_writel(sdc_conf, SDC_COM_CONF);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+
+int32_t ipu_sdc_set_brightness(uint8_t value)
+{
+ __raw_writel(0x03000000UL | value << 16, SDC_PWM_CTRL);
+ return 0;
+}
+
+/*!
+ * This function sets the window position of the foreground or background plane.
+ * modes.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param x_pos The X coordinate position to place window at.
+ * The position is relative to the top left corner.
+ *
+ * @param y_pos The Y coordinate position to place window at.
+ * The position is relative to the top left corner.
+ *
+ * @return This function returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_window_pos(ipu_channel_t channel, int16_t x_pos,
+ int16_t y_pos)
+{
+ x_pos += g_h_start_width;
+ y_pos += g_v_start_width;
+
+ if (channel == MEM_SDC_BG) {
+ __raw_writel((x_pos << 16) | y_pos, SDC_BG_POS);
+ } else if (channel == MEM_SDC_FG) {
+ __raw_writel((x_pos << 16) | y_pos, SDC_FG_POS);
+ } else {
+ return EINVAL;
+ }
+ return 0;
+}
+
+void _ipu_sdc_fg_init(ipu_channel_params_t * params)
+{
+ uint32_t reg;
+ (void)params;
+
+ /* Enable FG channel */
+ reg = __raw_readl(SDC_COM_CONF);
+ __raw_writel(reg | SDC_COM_FG_EN | SDC_COM_BG_EN, SDC_COM_CONF);
+}
+
+uint32_t _ipu_sdc_fg_uninit(void)
+{
+ uint32_t reg;
+
+ /* Disable FG channel */
+ reg = __raw_readl(SDC_COM_CONF);
+ __raw_writel(reg & ~SDC_COM_FG_EN, SDC_COM_CONF);
+
+ return (reg & SDC_COM_FG_EN);
+}
+
+void _ipu_sdc_bg_init(ipu_channel_params_t * params)
+{
+ uint32_t reg;
+ (void)params;
+
+ /* Enable FG channel */
+ reg = __raw_readl(SDC_COM_CONF);
+ __raw_writel(reg | SDC_COM_BG_EN, SDC_COM_CONF);
+}
+
+uint32_t _ipu_sdc_bg_uninit(void)
+{
+ uint32_t reg;
+
+ /* Disable BG channel */
+ reg = __raw_readl(SDC_COM_CONF);
+ __raw_writel(reg & ~SDC_COM_BG_EN, SDC_COM_CONF);
+
+ return (reg & SDC_COM_BG_EN);
+}
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(ipu_sdc_init_panel);
+EXPORT_SYMBOL(ipu_sdc_set_global_alpha);
+EXPORT_SYMBOL(ipu_sdc_set_color_key);
+EXPORT_SYMBOL(ipu_sdc_set_brightness);
+EXPORT_SYMBOL(ipu_disp_set_window_pos);
diff --git a/drivers/mxc/ipu/pf/Kconfig b/drivers/mxc/ipu/pf/Kconfig
new file mode 100644
index 000000000000..fa5a777cf727
--- /dev/null
+++ b/drivers/mxc/ipu/pf/Kconfig
@@ -0,0 +1,7 @@
+config MXC_IPU_PF
+ tristate "MXC MPEG4/H.264 Post Filter Driver"
+ depends on MXC_IPU_V1
+ default y
+ help
+ Driver for MPEG4 dering and deblock and H.264 deblock
+ using MXC IPU h/w
diff --git a/drivers/mxc/ipu/pf/Makefile b/drivers/mxc/ipu/pf/Makefile
new file mode 100644
index 000000000000..641adf4be4bd
--- /dev/null
+++ b/drivers/mxc/ipu/pf/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MXC_IPU_PF) += mxc_pf.o
diff --git a/drivers/mxc/ipu/pf/mxc_pf.c b/drivers/mxc/ipu/pf/mxc_pf.c
new file mode 100644
index 000000000000..744152415e3a
--- /dev/null
+++ b/drivers/mxc/ipu/pf/mxc_pf.c
@@ -0,0 +1,993 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_pf.c
+ *
+ * @brief MXC IPU MPEG4/H.264 Post-filtering driver
+ *
+ * User-level API for IPU Hardware MPEG4/H.264 Post-filtering.
+ *
+ * @ingroup MXC_PF
+ */
+
+#include <linux/pagemap.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/ipu.h>
+#include <linux/mxc_pf.h>
+
+struct mxc_pf_data {
+ pf_operation_t mode;
+ u32 pf_enabled;
+ u32 width;
+ u32 height;
+ u32 stride;
+ uint32_t qp_size;
+ dma_addr_t qp_paddr;
+ void *qp_vaddr;
+ pf_buf buf[PF_MAX_BUFFER_CNT];
+ void *buf_vaddr[PF_MAX_BUFFER_CNT];
+ wait_queue_head_t pf_wait;
+ volatile int done_mask;
+ volatile int wait_mask;
+ volatile int busy_flag;
+ struct semaphore busy_lock;
+};
+
+static struct mxc_pf_data pf_data;
+static u8 open_count = 0;
+static struct class *mxc_pf_class;
+
+/*
+ * Function definitions
+ */
+
+static irqreturn_t mxc_pf_irq_handler(int irq, void *dev_id)
+{
+ struct mxc_pf_data *pf = dev_id;
+
+ if (irq == IPU_IRQ_PF_Y_OUT_EOF) {
+ pf->done_mask |= PF_WAIT_Y;
+ } else if (irq == IPU_IRQ_PF_U_OUT_EOF) {
+ pf->done_mask |= PF_WAIT_U;
+ } else if (irq == IPU_IRQ_PF_V_OUT_EOF) {
+ pf->done_mask |= PF_WAIT_V;
+ } else {
+ return IRQ_NONE;
+ }
+
+ if (pf->wait_mask && ((pf->done_mask & pf->wait_mask) == pf->wait_mask)) {
+ wake_up_interruptible(&pf->pf_wait);
+ }
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function handles PF_IOCTL_INIT calls. It initializes the PF channels,
+ * interrupt handlers, and channel buffers.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * error.
+ */
+static int mxc_pf_init(pf_init_params * pf_init)
+{
+ int err;
+ ipu_channel_params_t params;
+ u32 w;
+ u32 stride;
+ u32 h;
+ u32 qp_size = 0;
+ u32 qp_stride;
+
+ if ((pf_init->pf_mode > 4) || (pf_init->width > 1024) ||
+ (pf_init->height > 1024) || (pf_init->stride < pf_init->width)) {
+ return -EINVAL;
+ }
+
+ pf_data.mode = pf_init->pf_mode;
+ w = pf_data.width = pf_init->width;
+ h = pf_data.height = pf_init->height;
+ stride = pf_data.stride = pf_init->stride;
+ pf_data.qp_size = pf_init->qp_size;
+
+ memset(&params, 0, sizeof(params));
+ params.mem_pf_mem.operation = pf_data.mode;
+ err = ipu_init_channel(MEM_PF_Y_MEM, &params);
+ if (err < 0) {
+ printk(KERN_ERR "mxc_pf: error initializing channel\n");
+ goto err0;
+ }
+
+ err = ipu_init_channel_buffer(MEM_PF_Y_MEM, IPU_INPUT_BUFFER,
+ IPU_PIX_FMT_GENERIC, w, h, stride,
+ IPU_ROTATE_NONE, 0, 0, 0, 0);
+ if (err < 0) {
+ printk(KERN_ERR "mxc_pf: error initializing Y input buffer\n");
+ goto err0;
+ }
+
+ err = ipu_init_channel_buffer(MEM_PF_Y_MEM, IPU_OUTPUT_BUFFER,
+ IPU_PIX_FMT_GENERIC, w, h, stride,
+ IPU_ROTATE_NONE, 0, 0, 0, 0);
+ if (err < 0) {
+ printk(KERN_ERR "mxc_pf: error initializing Y output buffer\n");
+ goto err0;
+ }
+
+ w = w / 2;
+ h = h / 2;
+ stride = stride / 2;
+
+ if (pf_data.mode != PF_MPEG4_DERING) {
+ err = ipu_init_channel_buffer(MEM_PF_U_MEM, IPU_INPUT_BUFFER,
+ IPU_PIX_FMT_GENERIC, w, h, stride,
+ IPU_ROTATE_NONE, 0, 0, 0, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error initializing U input buffer\n");
+ goto err0;
+ }
+
+ err = ipu_init_channel_buffer(MEM_PF_U_MEM, IPU_OUTPUT_BUFFER,
+ IPU_PIX_FMT_GENERIC, w, h, stride,
+ IPU_ROTATE_NONE, 0, 0, 0, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error initializing U output buffer\n");
+ goto err0;
+ }
+
+ err = ipu_init_channel_buffer(MEM_PF_V_MEM, IPU_INPUT_BUFFER,
+ IPU_PIX_FMT_GENERIC, w, h, stride,
+ IPU_ROTATE_NONE, 0, 0, 0, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error initializing V input buffer\n");
+ goto err0;
+ }
+
+ err = ipu_init_channel_buffer(MEM_PF_V_MEM, IPU_OUTPUT_BUFFER,
+ IPU_PIX_FMT_GENERIC, w, h, stride,
+ IPU_ROTATE_NONE, 0, 0, 0, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error initializing V output buffer\n");
+ goto err0;
+ }
+ }
+
+ /*Setup Channel QF and BSC Params */
+ if (pf_data.mode == PF_H264_DEBLOCK) {
+ w = ((pf_data.width + 15) / 16);
+ h = (pf_data.height + 15) / 16;
+ qp_stride = w;
+ qp_size = 4 * qp_stride * h;
+ pr_debug("H264 QP width = %d, height = %d\n", w, h);
+ err = ipu_init_channel_buffer(MEM_PF_Y_MEM,
+ IPU_SEC_INPUT_BUFFER,
+ IPU_PIX_FMT_GENERIC_32, w, h,
+ qp_stride, IPU_ROTATE_NONE, 0, 0,
+ 0, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error initializing H264 QP buffer\n");
+ goto err0;
+ }
+/* w = (pf_data.width + 3) / 4; */
+ w *= 4;
+ h *= 4;
+ qp_stride = w;
+ err = ipu_init_channel_buffer(MEM_PF_U_MEM,
+ IPU_SEC_INPUT_BUFFER,
+ IPU_PIX_FMT_GENERIC, w, h,
+ qp_stride, IPU_ROTATE_NONE, 0, 0,
+ 0, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error initializing H264 BSB buffer\n");
+ goto err0;
+ }
+ qp_size += qp_stride * h;
+ } else { /* MPEG4 mode */
+
+ w = (pf_data.width + 15) / 16;
+ h = (pf_data.height + 15) / 16;
+ qp_stride = (w + 3) & ~0x3UL;
+ pr_debug("MPEG4 QP width = %d, height = %d, stride = %d\n",
+ w, h, qp_stride);
+ err = ipu_init_channel_buffer(MEM_PF_Y_MEM,
+ IPU_SEC_INPUT_BUFFER,
+ IPU_PIX_FMT_GENERIC, w, h,
+ qp_stride, IPU_ROTATE_NONE, 0, 0,
+ 0, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error initializing MPEG4 QP buffer\n");
+ goto err0;
+ }
+ qp_size = qp_stride * h;
+ }
+
+ /* Support 2 QP buffers */
+ qp_size *= 2;
+
+ if (pf_data.qp_size > qp_size)
+ qp_size = pf_data.qp_size;
+ else
+ pf_data.qp_size = qp_size;
+
+ pf_data.qp_vaddr = dma_alloc_coherent(NULL, pf_data.qp_size,
+ &pf_data.qp_paddr,
+ GFP_KERNEL | GFP_DMA);
+ if (!pf_data.qp_vaddr)
+ return -ENOMEM;
+
+ pf_init->qp_paddr = pf_data.qp_paddr;
+ pf_init->qp_size = pf_data.qp_size;
+
+ return 0;
+
+ err0:
+ return err;
+}
+
+/*!
+ * This function handles PF_IOCTL_UNINIT calls. It uninitializes the PF
+ * channels and interrupt handlers.
+ *
+ * @return This function returns 0 on success or negative error code
+ * on error.
+ */
+static int mxc_pf_uninit(void)
+{
+ pf_data.pf_enabled = 0;
+ ipu_disable_irq(IPU_IRQ_PF_Y_OUT_EOF);
+ ipu_disable_irq(IPU_IRQ_PF_U_OUT_EOF);
+ ipu_disable_irq(IPU_IRQ_PF_V_OUT_EOF);
+
+ ipu_disable_channel(MEM_PF_Y_MEM, true);
+ ipu_disable_channel(MEM_PF_U_MEM, true);
+ ipu_disable_channel(MEM_PF_V_MEM, true);
+ ipu_uninit_channel(MEM_PF_Y_MEM);
+ ipu_uninit_channel(MEM_PF_U_MEM);
+ ipu_uninit_channel(MEM_PF_V_MEM);
+
+ if (pf_data.qp_vaddr) {
+ dma_free_coherent(NULL, pf_data.qp_size, pf_data.qp_vaddr,
+ pf_data.qp_paddr);
+ pf_data.qp_vaddr = NULL;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function handles PF_IOCTL_REQBUFS calls. It initializes the PF channels
+ * and channel buffers.
+ *
+ * @param reqbufs Input/Output Structure containing buffer mode,
+ * type, offset, and size. The offset and size of
+ * the buffer are returned for PF_MEMORY_MMAP mode.
+ *
+ * @return This function returns 0 on success or negative error code
+ * on error.
+ */
+static int mxc_pf_reqbufs(pf_reqbufs_params * reqbufs)
+{
+ int err;
+ uint32_t buf_size;
+ int i;
+ int alloc_cnt = 0;
+ pf_buf *buf = pf_data.buf;
+ if (reqbufs->count > PF_MAX_BUFFER_CNT) {
+ reqbufs->count = PF_MAX_BUFFER_CNT;
+ }
+ /* Deallocate mmapped buffers */
+ if (reqbufs->count == 0) {
+ for (i = 0; i < PF_MAX_BUFFER_CNT; i++) {
+ if (buf[i].index != -1) {
+ dma_free_coherent(NULL, buf[i].size,
+ pf_data.buf_vaddr[i],
+ buf[i].offset);
+ pf_data.buf_vaddr[i] = NULL;
+ buf[i].index = -1;
+ buf[i].size = 0;
+ }
+ }
+ return 0;
+ }
+
+ buf_size = (pf_data.stride * pf_data.height * 3) / 2;
+ if (reqbufs->req_size > buf_size) {
+ buf_size = reqbufs->req_size;
+ pr_debug("using requested buffer size of %d\n", buf_size);
+ } else {
+ reqbufs->req_size = buf_size;
+ pr_debug("using default buffer size of %d\n", buf_size);
+ }
+
+ for (i = 0; alloc_cnt < reqbufs->count; i++) {
+ buf[i].index = i;
+ buf[i].size = buf_size;
+ pf_data.buf_vaddr[i] = dma_alloc_coherent(NULL, buf[i].size,
+ &buf[i].offset,
+ GFP_KERNEL | GFP_DMA);
+ if (!pf_data.buf_vaddr[i] || !buf[i].offset) {
+ printk(KERN_ERR
+ "mxc_pf: unable to allocate IPU buffers.\n");
+ err = -ENOMEM;
+ goto err0;
+ }
+ pr_debug("Allocated buffer %d at paddr 0x%08X, vaddr %p\n",
+ i, buf[i].offset, pf_data.buf_vaddr[i]);
+
+ alloc_cnt++;
+ }
+
+ return 0;
+ err0:
+ for (i = 0; i < alloc_cnt; i++) {
+ dma_free_coherent(NULL, buf[i].size, pf_data.buf_vaddr[i],
+ buf[i].offset);
+ pf_data.buf_vaddr[i] = NULL;
+ buf[i].index = -1;
+ buf[i].size = 0;
+ }
+ return err;
+}
+
+/*!
+ * This function handles PF_IOCTL_START calls. It sets the PF channel buffers
+ * addresses and starts the channels
+ *
+ * @return This function returns 0 on success or negative error code on
+ * error.
+ */
+static int mxc_pf_start(pf_buf * in, pf_buf * out, int qp_buf)
+{
+ int err;
+ dma_addr_t y_in_paddr;
+ dma_addr_t u_in_paddr;
+ dma_addr_t v_in_paddr;
+ dma_addr_t p1_in_paddr;
+ dma_addr_t p2_in_paddr;
+ dma_addr_t y_out_paddr;
+ dma_addr_t u_out_paddr;
+ dma_addr_t v_out_paddr;
+
+ /* H.264 requires output buffer equal to input */
+ if (pf_data.mode == PF_H264_DEBLOCK)
+ out = in;
+
+ y_in_paddr = in->offset + in->y_offset;
+ if (in->u_offset)
+ u_in_paddr = in->offset + in->u_offset;
+ else
+ u_in_paddr = y_in_paddr + (pf_data.stride * pf_data.height);
+ if (in->v_offset)
+ v_in_paddr = in->offset + in->v_offset;
+ else
+ v_in_paddr = u_in_paddr + (pf_data.stride * pf_data.height) / 4;
+ p1_in_paddr = pf_data.qp_paddr;
+ if (qp_buf)
+ p1_in_paddr += pf_data.qp_size / 2;
+
+ if (pf_data.mode == PF_H264_DEBLOCK) {
+ p2_in_paddr = p1_in_paddr +
+ ((pf_data.width + 15) / 16) *
+ ((pf_data.height + 15) / 16) * 4;
+ } else {
+ p2_in_paddr = 0;
+ }
+
+ pr_debug("y_in_paddr = 0x%08X\nu_in_paddr = 0x%08X\n"
+ "v_in_paddr = 0x%08X\n"
+ "qp_paddr = 0x%08X\nbsb_paddr = 0x%08X\n",
+ y_in_paddr, u_in_paddr, v_in_paddr, p1_in_paddr, p2_in_paddr);
+
+ y_out_paddr = out->offset + out->y_offset;
+ if (out->u_offset)
+ u_out_paddr = out->offset + out->u_offset;
+ else
+ u_out_paddr = y_out_paddr + (pf_data.stride * pf_data.height);
+ if (out->v_offset)
+ v_out_paddr = out->offset + out->v_offset;
+ else
+ v_out_paddr =
+ u_out_paddr + (pf_data.stride * pf_data.height) / 4;
+
+ pr_debug("y_out_paddr = 0x%08X\nu_out_paddr = 0x%08X\n"
+ "v_out_paddr = 0x%08X\n",
+ y_out_paddr, u_out_paddr, v_out_paddr);
+
+ pf_data.done_mask = 0;
+
+ ipu_enable_irq(IPU_IRQ_PF_Y_OUT_EOF);
+ if (pf_data.mode != PF_MPEG4_DERING) {
+ ipu_enable_irq(IPU_IRQ_PF_U_OUT_EOF);
+ ipu_enable_irq(IPU_IRQ_PF_V_OUT_EOF);
+ }
+
+ err = ipu_update_channel_buffer(MEM_PF_Y_MEM, IPU_INPUT_BUFFER, 0,
+ y_in_paddr);
+ if (err < 0) {
+ printk(KERN_ERR "mxc_pf: error setting Y input buffer\n");
+ goto err0;
+ }
+
+ err = ipu_update_channel_buffer(MEM_PF_Y_MEM, IPU_OUTPUT_BUFFER, 0,
+ y_out_paddr);
+ if (err < 0) {
+ printk(KERN_ERR "mxc_pf: error setting Y output buffer\n");
+ goto err0;
+ }
+
+ if (pf_data.mode != PF_MPEG4_DERING) {
+ err =
+ ipu_update_channel_buffer(MEM_PF_U_MEM, IPU_INPUT_BUFFER, 0,
+ u_in_paddr);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error setting U input buffer\n");
+ goto err0;
+ }
+
+ err =
+ ipu_update_channel_buffer(MEM_PF_U_MEM, IPU_OUTPUT_BUFFER,
+ 0, u_out_paddr);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error setting U output buffer\n");
+ goto err0;
+ }
+
+ err =
+ ipu_update_channel_buffer(MEM_PF_V_MEM, IPU_INPUT_BUFFER, 0,
+ v_in_paddr);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error setting V input buffer\n");
+ goto err0;
+ }
+
+ err =
+ ipu_update_channel_buffer(MEM_PF_V_MEM, IPU_OUTPUT_BUFFER,
+ 0, v_out_paddr);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error setting V output buffer\n");
+ goto err0;
+ }
+ }
+
+ err = ipu_update_channel_buffer(MEM_PF_Y_MEM, IPU_SEC_INPUT_BUFFER, 0,
+ p1_in_paddr);
+ if (err < 0) {
+ printk(KERN_ERR "mxc_pf: error setting QP buffer\n");
+ goto err0;
+ }
+
+ if (pf_data.mode == PF_H264_DEBLOCK) {
+
+ err = ipu_update_channel_buffer(MEM_PF_U_MEM,
+ IPU_SEC_INPUT_BUFFER, 0,
+ p2_in_paddr);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mxc_pf: error setting H264 BSB buffer\n");
+ goto err0;
+ }
+ ipu_select_buffer(MEM_PF_U_MEM, IPU_SEC_INPUT_BUFFER, 0);
+ }
+
+ ipu_select_buffer(MEM_PF_Y_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_PF_Y_MEM, IPU_SEC_INPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_PF_Y_MEM, IPU_INPUT_BUFFER, 0);
+ if (pf_data.mode != PF_MPEG4_DERING) {
+ ipu_select_buffer(MEM_PF_U_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_PF_V_MEM, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_PF_U_MEM, IPU_INPUT_BUFFER, 0);
+ ipu_select_buffer(MEM_PF_V_MEM, IPU_INPUT_BUFFER, 0);
+ }
+
+ if (!pf_data.pf_enabled) {
+ pf_data.pf_enabled = 1;
+ if (pf_data.mode != PF_MPEG4_DERING) {
+ ipu_enable_channel(MEM_PF_V_MEM);
+ ipu_enable_channel(MEM_PF_U_MEM);
+ }
+ ipu_enable_channel(MEM_PF_Y_MEM);
+ }
+
+ return 0;
+ err0:
+ return err;
+}
+
+/*!
+ * Post Filter driver open function. This function implements the Linux
+ * file_operations.open() API function.
+ *
+ * @param inode struct inode *
+ *
+ * @param filp struct file *
+ *
+ * @return This function returns 0 on success or negative error code on
+ * error.
+ */
+static int mxc_pf_open(struct inode *inode, struct file *filp)
+{
+ int i;
+
+ if (open_count) {
+ return -EBUSY;
+ }
+
+ open_count++;
+
+ memset(&pf_data, 0, sizeof(pf_data));
+ for (i = 0; i < PF_MAX_BUFFER_CNT; i++) {
+ pf_data.buf[i].index = -1;
+ }
+ init_waitqueue_head(&pf_data.pf_wait);
+ init_MUTEX(&pf_data.busy_lock);
+
+ pf_data.busy_flag = 1;
+
+ ipu_request_irq(IPU_IRQ_PF_Y_OUT_EOF, mxc_pf_irq_handler,
+ 0, "mxc_ipu_pf", &pf_data);
+
+ ipu_request_irq(IPU_IRQ_PF_U_OUT_EOF, mxc_pf_irq_handler,
+ 0, "mxc_ipu_pf", &pf_data);
+
+ ipu_request_irq(IPU_IRQ_PF_V_OUT_EOF, mxc_pf_irq_handler,
+ 0, "mxc_ipu_pf", &pf_data);
+
+ ipu_disable_irq(IPU_IRQ_PF_Y_OUT_EOF);
+ ipu_disable_irq(IPU_IRQ_PF_U_OUT_EOF);
+ ipu_disable_irq(IPU_IRQ_PF_V_OUT_EOF);
+
+ return 0;
+}
+
+/*!
+ * Post Filter driver release function. This function implements the Linux
+ * file_operations.release() API function.
+ *
+ * @param inode struct inode *
+ *
+ * @param filp struct file *
+ *
+ * @return This function returns 0 on success or negative error code on
+ * error.
+ */
+static int mxc_pf_release(struct inode *inode, struct file *filp)
+{
+ pf_reqbufs_params req_buf;
+
+ if (open_count) {
+ mxc_pf_uninit();
+
+ /* Free any allocated buffers */
+ req_buf.count = 0;
+ mxc_pf_reqbufs(&req_buf);
+
+ ipu_free_irq(IPU_IRQ_PF_V_OUT_EOF, &pf_data);
+ ipu_free_irq(IPU_IRQ_PF_U_OUT_EOF, &pf_data);
+ ipu_free_irq(IPU_IRQ_PF_Y_OUT_EOF, &pf_data);
+ open_count--;
+ }
+ return 0;
+}
+
+/*!
+ * Post Filter driver ioctl function. This function implements the Linux
+ * file_operations.ioctl() API function.
+ *
+ * @param inode struct inode *
+ *
+ * @param filp struct file *
+ *
+ * @param cmd IOCTL command to handle
+ *
+ * @param arg Pointer to arguments for IOCTL
+ *
+ * @return This function returns 0 on success or negative error code on
+ * error.
+ */
+static int mxc_pf_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+
+ switch (cmd) {
+ case PF_IOCTL_INIT:
+ {
+ pf_init_params pf_init;
+
+ pr_debug("PF_IOCTL_INIT\n");
+ if (copy_from_user(&pf_init, (void *)arg,
+ _IOC_SIZE(cmd))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ retval = mxc_pf_init(&pf_init);
+ if (retval < 0)
+ break;
+ pf_init.qp_paddr = pf_data.qp_paddr;
+ pf_init.qp_size = pf_data.qp_size;
+
+ /* Return size of memory allocated */
+ if (copy_to_user((void *)arg, &pf_init, _IOC_SIZE(cmd))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ pf_data.busy_flag = 0;
+ break;
+ }
+ case PF_IOCTL_UNINIT:
+ pr_debug("PF_IOCTL_UNINIT\n");
+ retval = mxc_pf_uninit();
+ break;
+ case PF_IOCTL_REQBUFS:
+ {
+ pf_reqbufs_params reqbufs;
+ pr_debug("PF_IOCTL_REQBUFS\n");
+
+ if (copy_from_user
+ (&reqbufs, (void *)arg, _IOC_SIZE(cmd))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ retval = mxc_pf_reqbufs(&reqbufs);
+
+ /* Return size of memory allocated */
+ if (copy_to_user((void *)arg, &reqbufs, _IOC_SIZE(cmd))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ break;
+ }
+ case PF_IOCTL_QUERYBUF:
+ {
+ pf_buf buf;
+ pr_debug("PF_IOCTL_QUERYBUF\n");
+
+ if (copy_from_user(&buf, (void *)arg, _IOC_SIZE(cmd))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if ((buf.index < 0) ||
+ (buf.index >= PF_MAX_BUFFER_CNT) ||
+ (pf_data.buf[buf.index].index != buf.index)) {
+ retval = -EINVAL;
+ break;
+ }
+ /* Return size of memory allocated */
+ if (copy_to_user((void *)arg, &pf_data.buf[buf.index],
+ _IOC_SIZE(cmd))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ break;
+ }
+ case PF_IOCTL_START:
+ {
+ int index;
+ pf_start_params start_params;
+ pr_debug("PF_IOCTL_START\n");
+
+ if (pf_data.busy_flag) {
+ retval = -EBUSY;
+ break;
+ }
+
+ if (copy_from_user(&start_params, (void *)arg,
+ _IOC_SIZE(cmd))) {
+ retval = -EFAULT;
+ break;
+ }
+ if (start_params.h264_pause_row >=
+ ((pf_data.height + 15) / 16)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ pf_data.busy_flag = 1;
+
+ index = start_params.in.index;
+ if ((index >= 0) && (index < PF_MAX_BUFFER_CNT)) {
+ if (pf_data.buf[index].offset !=
+ start_params.in.offset) {
+ retval = -EINVAL;
+ break;
+ }
+ }
+
+ index = start_params.out.index;
+ if ((index >= 0) && (index < PF_MAX_BUFFER_CNT)) {
+ if (pf_data.buf[index].offset !=
+ start_params.out.offset) {
+ retval = -EINVAL;
+ break;
+ }
+ }
+
+ ipu_pf_set_pause_row(start_params.h264_pause_row);
+
+ /*Update y, u, v buffers in DMA Channels */
+ if ((retval =
+ mxc_pf_start(&start_params.in, &start_params.out,
+ start_params.qp_buf))
+ < 0) {
+ break;
+ }
+
+ pr_debug("PF_IOCTL_START - processing started\n");
+
+ if (!start_params.wait) {
+ break;
+ }
+
+ pr_debug("PF_IOCTL_START - waiting for completion\n");
+
+ pf_data.wait_mask = PF_WAIT_ALL;
+ /* Fall thru to wait */
+ }
+ case PF_IOCTL_WAIT:
+ {
+ if (!pf_data.wait_mask)
+ pf_data.wait_mask = (u32) arg;
+
+ if (pf_data.mode == PF_MPEG4_DERING)
+ pf_data.wait_mask &= PF_WAIT_Y;
+
+ if (!pf_data.wait_mask) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (!wait_event_interruptible_timeout(pf_data.pf_wait,
+ ((pf_data.
+ done_mask &
+ pf_data.
+ wait_mask) ==
+ pf_data.
+ wait_mask),
+ 1 * HZ)) {
+ pr_debug
+ ("PF_IOCTL_WAIT: timeout, done_mask = %d\n",
+ pf_data.done_mask);
+ retval = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ pr_debug("PF_IOCTL_WAIT: interrupt received\n");
+ retval = -ERESTARTSYS;
+ break;
+ }
+ pf_data.busy_flag = 0;
+ pf_data.wait_mask = 0;
+
+ pr_debug("PF_IOCTL_WAIT - finished\n");
+ break;
+ }
+ case PF_IOCTL_RESUME:
+ {
+ int pause_row;
+ pr_debug("PF_IOCTL_RESUME\n");
+
+ if (pf_data.busy_flag == 0) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (copy_from_user(&pause_row, (void *)arg,
+ _IOC_SIZE(cmd))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (pause_row >= ((pf_data.height + 15) / 16)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ ipu_pf_set_pause_row(pause_row);
+ break;
+ }
+
+ default:
+ printk(KERN_ERR "ipu_pf_ioctl not supported ioctl\n");
+ retval = -1;
+ }
+
+ if (retval < 0)
+ pr_debug("return = %d\n", retval);
+ return retval;
+}
+
+/*!
+ * Post Filter driver mmap function. This function implements the Linux
+ * file_operations.mmap() API function for mapping driver buffers to user space.
+ *
+ * @param file struct file *
+ *
+ * @param vma structure vm_area_struct *
+ *
+ * @return 0 Success, EINTR busy lock error,
+ * ENOBUFS remap_page error.
+ */
+static int mxc_pf_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long size = vma->vm_end - vma->vm_start;
+ int res = 0;
+
+ pr_debug("pgoff=0x%lx, start=0x%lx, end=0x%lx\n",
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&pf_data.busy_lock))
+ return -EINTR;
+
+ /* make buffers write-thru cacheable */
+ vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) &
+ ~L_PTE_BUFFERABLE);
+
+ if (remap_pfn_range(vma, vma->vm_start,
+ vma->vm_pgoff, size, vma->vm_page_prot)) {
+ printk(KERN_ERR "mxc_pf: remap_pfn_range failed\n");
+ res = -ENOBUFS;
+ goto mmap_exit;
+ }
+
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+
+ mmap_exit:
+ up(&pf_data.busy_lock);
+ return res;
+}
+
+/*!
+ * Post Filter driver fsync function. This function implements the Linux
+ * file_operations.fsync() API function.
+ *
+ * The user must call fsync() before reading an output buffer. This
+ * call flushes the L1 and L2 caches
+ *
+ * @param filp structure file *
+ *
+ * @param dentry struct dentry *
+ *
+ * @param datasync unused
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+int mxc_pf_fsync(struct file *filp, struct dentry *dentry, int datasync)
+{
+ flush_cache_all();
+ outer_flush_all();
+ return 0;
+}
+
+/*!
+ * Post Filter driver poll function. This function implements the Linux
+ * file_operations.poll() API function.
+ *
+ * @param file structure file *
+ *
+ * @param wait structure poll_table *
+ *
+ * @return status POLLIN | POLLRDNORM
+ */
+static unsigned int mxc_pf_poll(struct file *file, poll_table * wait)
+{
+ wait_queue_head_t *queue = NULL;
+ int res = POLLIN | POLLRDNORM;
+
+ if (down_interruptible(&pf_data.busy_lock))
+ return -EINTR;
+
+ queue = &pf_data.pf_wait;
+ poll_wait(file, queue, wait);
+
+ up(&pf_data.busy_lock);
+
+ return res;
+}
+
+/*!
+ * File operation structure functions pointers.
+ */
+static struct file_operations mxc_pf_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_pf_open,
+ .release = mxc_pf_release,
+ .ioctl = mxc_pf_ioctl,
+ .poll = mxc_pf_poll,
+ .mmap = mxc_pf_mmap,
+ .fsync = mxc_pf_fsync,
+};
+
+static int mxc_pf_major = 0;
+
+/*!
+ * Post Filter driver module initialization function.
+ */
+int mxc_pf_dev_init(void)
+{
+ int ret = 0;
+ struct device *temp_class;
+
+ mxc_pf_major = register_chrdev(0, "mxc_ipu_pf", &mxc_pf_fops);
+
+ if (mxc_pf_major < 0) {
+ printk(KERN_INFO "Unable to get a major for mxc_ipu_pf");
+ return mxc_pf_major;
+ }
+
+ mxc_pf_class = class_create(THIS_MODULE, "mxc_ipu_pf");
+ if (IS_ERR(mxc_pf_class)) {
+ printk(KERN_ERR "Error creating mxc_ipu_pf class.\n");
+ ret = PTR_ERR(mxc_pf_class);
+ goto err_out1;
+ }
+
+ temp_class = device_create(mxc_pf_class, NULL, MKDEV(mxc_pf_major, 0), NULL,
+ "mxc_ipu_pf");
+ if (IS_ERR(temp_class)) {
+ printk(KERN_ERR "Error creating mxc_ipu_pf class device.\n");
+ ret = PTR_ERR(temp_class);
+ goto err_out2;
+ }
+
+ printk(KERN_INFO "IPU Post-filter loading\n");
+
+ return 0;
+
+ err_out2:
+ class_destroy(mxc_pf_class);
+ err_out1:
+ unregister_chrdev(mxc_pf_major, "mxc_ipu_pf");
+ return ret;
+}
+
+/*!
+ * Post Filter driver module exit function.
+ */
+static void mxc_pf_exit(void)
+{
+ if (mxc_pf_major > 0) {
+ device_destroy(mxc_pf_class, MKDEV(mxc_pf_major, 0));
+ class_destroy(mxc_pf_class);
+ unregister_chrdev(mxc_pf_major, "mxc_ipu_pf");
+ }
+}
+
+module_init(mxc_pf_dev_init);
+module_exit(mxc_pf_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC MPEG4/H.264 Postfilter Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/ipu3/Kconfig b/drivers/mxc/ipu3/Kconfig
new file mode 100644
index 000000000000..0ae0ffa9e19d
--- /dev/null
+++ b/drivers/mxc/ipu3/Kconfig
@@ -0,0 +1,5 @@
+config MXC_IPU_V3
+ bool
+
+config MXC_IPU_V3D
+ bool
diff --git a/drivers/mxc/ipu3/Makefile b/drivers/mxc/ipu3/Makefile
new file mode 100644
index 000000000000..aa3e7b1bb501
--- /dev/null
+++ b/drivers/mxc/ipu3/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_MXC_IPU_V3) = mxc_ipu.o
+
+mxc_ipu-objs := ipu_common.o ipu_ic.o ipu_disp.o ipu_capture.o ipu_device.o ipu_calc_stripes_sizes.o
+
diff --git a/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c b/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c
new file mode 100644
index 000000000000..2992cce7023d
--- /dev/null
+++ b/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * @file ipu_calc_stripes_sizes.c
+ *
+ * @brief IPU IC functions
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/ipu.h>
+#include <asm/div64.h>
+
+#define BPP_32 0
+#define BPP_16 3
+#define BPP_8 5
+#define BPP_24 1
+#define BPP_12 4
+#define BPP_18 2
+
+static u64 _do_div(u64 a, u32 b)
+{
+ u64 div;
+ div = a;
+ do_div(div, b);
+ return div;
+}
+
+static u32 truncate(u32 up, /* 0: down; else: up */
+ u64 a, /* must be non-negative */
+ u32 b)
+{
+ u32 d;
+ u64 div;
+ div = _do_div(a, b);
+ d = b * (div >> 32);
+ if (up && (a > (((u64)d) << 32)))
+ return d+b;
+ else
+ return d;
+}
+
+static unsigned int f_calc(unsigned int pfs, unsigned int bpp, unsigned int *write)
+{/* return input_f */
+ unsigned int f_calculated = 0;
+ switch (pfs) {
+ case IPU_PIX_FMT_YVU422P:
+ case IPU_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ f_calculated = 16;
+ break;
+
+ case IPU_PIX_FMT_NV12:
+ f_calculated = 8;
+ break;
+
+ default:
+ f_calculated = 0;
+ break;
+
+ }
+ if (!f_calculated) {
+ switch (bpp) {
+ case BPP_32:
+ f_calculated = 2;
+ break;
+
+ case BPP_16:
+ f_calculated = 4;
+ break;
+
+ case BPP_8:
+ case BPP_24:
+ f_calculated = 8;
+ break;
+
+ case BPP_12:
+ f_calculated = 16;
+ break;
+
+ case BPP_18:
+ f_calculated = 32;
+ break;
+
+ default:
+ f_calculated = 0;
+ break;
+ }
+ }
+ return f_calculated;
+}
+
+
+static unsigned int m_calc(unsigned int pfs)
+{
+ unsigned int m_calculated = 0;
+ switch (pfs) {
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YVU422P:
+ case IPU_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YVU420P:
+ case IPU_PIX_FMT_NV12:
+ m_calculated = 8;
+ break;
+
+ case IPU_PIX_FMT_YUYV:
+ case IPU_PIX_FMT_UYVY:
+ m_calculated = 2;
+ break;
+
+ default:
+ m_calculated = 1;
+ break;
+
+ }
+ return m_calculated;
+}
+
+
+/* Stripe parameters calculator */
+/**************************************************************************
+Notes:
+MSW = the maximal width allowed for a stripe
+ i.MX31: 720, i.MX35: 800, i.MX37/51/53: 1024
+cirr = the maximal inverse resizing ratio for which overlap in the input
+ is requested; typically cirr~2
+equal_stripes:
+ 0: each stripe is allowed to have independent parameters
+ for maximal image quality
+ 1: the stripes are requested to have identical parameters
+ (except the base address), for maximal performance
+If performance is the top priority (above image quality)
+ Avoid overlap, by setting CIRR = 0
+ This will also force effectively identical_stripes = 1
+ Choose IF & OF that corresponds to the same IOX/SX for both stripes
+ Choose IFW & OFW such that
+ IFW/IM, IFW/IF, OFW/OM, OFW/OF are even integers
+ The function returns an error status:
+ 0: no error
+ 1: invalid input parameters -> aborted without result
+ Valid parameters should satisfy the following conditions
+ IFW <= OFW, otherwise downsizing is required
+ - which is not supported yet
+ 4 <= IFW,OFW, so some interpolation may be needed even without overlap
+ IM, OM, IF, OF should not vanish
+ 2*IF <= IFW
+ so the frame can be split to two equal stripes, even without overlap
+ 2*(OF+IF/irr_opt) <= OFW
+ so a valid positive INW exists even for equal stripes
+ OF <= MSW, otherwise, the left stripe cannot be sufficiently large
+ MSW < OFW, so splitting to stripes is required
+ OFW <= 2*MSW, so two stripes are sufficient
+ (this also implies that 2<=MSW)
+ 2: OF is not a multiple of OM - not fully-supported yet
+ Output is produced but OW is not guaranited to be a multiple of OM
+ 4: OFW reduced to be a multiple of OM
+ 8: CIRR > 1: truncated to 1
+ Overlap is not supported (and not needed) y for upsizing)
+**************************************************************************/
+int ipu_calc_stripes_sizes(const unsigned int input_frame_width,
+ /* input frame width;>1 */
+ unsigned int output_frame_width, /* output frame width; >1 */
+ const unsigned int maximal_stripe_width,
+ /* the maximal width allowed for a stripe */
+ const unsigned long long cirr, /* see above */
+ const unsigned int equal_stripes, /* see above */
+ u32 input_pixelformat,/* pixel format after of read channel*/
+ u32 output_pixelformat,/* pixel format after of write channel*/
+ struct stripe_param *left,
+ struct stripe_param *right)
+{
+ const unsigned int irr_frac_bits = 13;
+ const unsigned long irr_steps = 1 << irr_frac_bits;
+ const u64 dirr = ((u64)1) << (32 - 2);
+ /* The maximum relative difference allowed between the irrs */
+ const u64 cr = ((u64)4) << 32;
+ /* The importance ratio between the two terms in the cost function below */
+
+ unsigned int status;
+ unsigned int temp;
+ unsigned int onw_min;
+ unsigned int inw, onw, inw_best;
+ /* number of pixels in the left stripe NOT hidden by the right stripe */
+ u64 irr_opt; /* the optimal inverse resizing ratio */
+ u64 rr_opt; /* the optimal resizing ratio = 1/irr_opt*/
+ u64 dinw; /* the misalignment between the stripes */
+ /* (measured in units of input columns) */
+ u64 difwl, difwr;
+ /* The number of input columns not reflected in the output */
+ /* the resizing ratio used for the right stripe is */
+ /* left->irr and right->irr respectively */
+ u64 cost, cost_min;
+ u64 div; /* result of division */
+
+ unsigned int input_m, input_f, output_m, output_f; /* parameters for upsizing by stripes */
+ u32 in_pfs, out_pfs;
+
+ status = 0;
+
+ /* M, F calculations */
+ /* read back pfs from params */
+
+ input_f = f_calc(input_pixelformat, 0, NULL);
+ /*in_m = m_calc(in_pfs);*/
+ input_m = 16;
+ /* BPP should be used in the out_F calc */
+ /* Temporarily not used */
+ /* out_F = F_calc(idmac->pfs, idmac->bpp, NULL); */
+
+ output_f = 16;
+ output_m = m_calc(output_pixelformat);
+
+
+ if ((output_frame_width < input_frame_width) || (input_frame_width < 4)
+ || (output_frame_width < 4))
+ return 1;
+
+ irr_opt = _do_div((((u64)(input_frame_width - 1)) << 32),
+ (output_frame_width - 1));
+ rr_opt = _do_div((((u64)(output_frame_width - 1)) << 32),
+ (input_frame_width - 1));
+
+ if ((input_m == 0) || (output_m == 0) || (input_f == 0) || (output_f == 0)
+ || (input_frame_width < (2 * input_f))
+ || ((((u64)output_frame_width) << 32) <
+ (2 * ((((u64)output_f) << 32) + (input_f * rr_opt))))
+ || (maximal_stripe_width < output_f)
+ || (output_frame_width <= maximal_stripe_width)
+ || ((2 * maximal_stripe_width) < output_frame_width))
+ return 1;
+
+ if (output_f % output_m)
+ status += 2;
+
+ temp = truncate(0, (((u64)output_frame_width) << 32), output_m);
+ if (temp < output_frame_width) {
+ output_frame_width = temp;
+ status += 4;
+ }
+
+ if (equal_stripes) {
+ if ((irr_opt > cirr) /* overlap in the input is not requested */
+ && ((input_frame_width % (input_m << 1)) == 0)
+ && ((input_frame_width % (input_f << 1)) == 0)
+ && ((output_frame_width % (output_m << 1)) == 0)
+ && ((output_frame_width % (output_f << 1)) == 0)) {
+ /* without overlap */
+ left->input_width = right->input_width = right->input_column =
+ input_frame_width >> 1;
+ left->output_width = right->output_width = right->output_column =
+ output_frame_width >> 1;
+ left->input_column = right->input_column = 0;
+ div = _do_div(((((u64)irr_steps) << 32) *
+ (right->input_width - 1)), (right->output_width - 1));
+ left->irr = right->irr = truncate(0, div, 1);
+ } else { /* with overlap */
+ onw = truncate(0, (((u64)output_frame_width) << 32) >> 1,
+ output_f);
+ inw = truncate(0, onw * irr_opt, input_f);
+ /* this is the maximal inw which allows the same resizing ratio */
+ /* in both stripes */
+ onw = truncate(1, (inw * rr_opt), output_f);
+ div = _do_div((((u64)(irr_steps * inw)) <<
+ 32), onw);
+ left->irr = right->irr = truncate(0, div, 1);
+ left->output_width = right->output_width =
+ output_frame_width - onw;
+ /* These are valid assignments for output_width, */
+ /* assuming output_f is a multiple of output_m */
+ div = (((u64)(left->output_width-1) * (left->irr)) << 32);
+ div = (((u64)1) << 32) + _do_div(div, irr_steps);
+
+ left->input_width = right->input_width = truncate(1, div, input_m);
+
+ div = _do_div((((u64)((right->output_width - 1) * right->irr)) <<
+ 32), irr_steps);
+ difwr = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
+ div = _do_div((difwr + (((u64)input_f) << 32)), 2);
+ left->input_column = truncate(0, div, input_f);
+
+
+ /* This splits the truncated input columns evenly */
+ /* between the left and right margins */
+ right->input_column = left->input_column + inw;
+ left->output_column = 0;
+ right->output_column = onw;
+ }
+ } else { /* independent stripes */
+ onw_min = output_frame_width - maximal_stripe_width;
+ /* onw is a multiple of output_f, in the range */
+ /* [max(output_f,output_frame_width-maximal_stripe_width),*/
+ /*min(output_frame_width-2,maximal_stripe_width)] */
+ /* definitely beyond the cost of any valid setting */
+ cost_min = (((u64)input_frame_width) << 32) + cr;
+ onw = truncate(0, ((u64)maximal_stripe_width), output_f);
+ if (output_frame_width - onw == 1)
+ onw -= output_f; /* => onw and output_frame_width-1-onw are positive */
+ inw = truncate(0, onw * irr_opt, input_f);
+ /* this is the maximal inw which allows the same resizing ratio */
+ /* in both stripes */
+ onw = truncate(1, inw * rr_opt, output_f);
+ do {
+ div = _do_div((((u64)(irr_steps * inw)) << 32), onw);
+ left->irr = truncate(0, div, 1);
+ div = _do_div((((u64)(onw * left->irr)) << 32),
+ irr_steps);
+ dinw = (((u64)inw) << 32) - div;
+
+ div = _do_div((((u64)((output_frame_width - 1 - onw) * left->irr)) <<
+ 32), irr_steps);
+
+ difwl = (((u64)(input_frame_width - 1 - inw)) << 32) - div;
+
+ cost = difwl + (((u64)(cr * dinw)) >> 32);
+
+ if (cost < cost_min) {
+ inw_best = inw;
+ cost_min = cost;
+ }
+
+ inw -= input_f;
+ onw = truncate(1, inw * rr_opt, output_f);
+ /* This is the minimal onw which allows the same resizing ratio */
+ /* in both stripes */
+ } while (onw >= onw_min);
+
+ inw = inw_best;
+ onw = truncate(1, inw * rr_opt, output_f);
+ div = _do_div((((u64)(irr_steps * inw)) << 32), onw);
+ left->irr = truncate(0, div, 1);
+
+ left->output_width = onw;
+ right->output_width = output_frame_width - onw;
+ /* These are valid assignments for output_width, */
+ /* assuming output_f is a multiple of output_m */
+ left->input_width = truncate(1, ((u64)(inw + 1)) << 32, input_m);
+ right->input_width = truncate(1, ((u64)(input_frame_width - inw)) <<
+ 32, input_m);
+
+ div = _do_div((((u64)(irr_steps * (input_frame_width - 1 - inw))) <<
+ 32), (right->output_width - 1));
+ right->irr = truncate(0, div, 1);
+ temp = truncate(0, ((u64)left->irr) * ((((u64)1) << 32) + dirr), 1);
+ if (temp < right->irr)
+ right->irr = temp;
+ div = _do_div(((u64)((right->output_width - 1) * right->irr) <<
+ 32), irr_steps);
+ difwr = (u64)(input_frame_width - 1 - inw) - div;
+
+
+ div = _do_div((difwr + (((u64)input_f) << 32)), 2);
+ left->input_column = truncate(0, div, input_f);
+
+ /* This splits the truncated input columns evenly */
+ /* between the left and right margins */
+ right->input_column = left->input_column + inw;
+ left->output_column = 0;
+ right->output_column = onw;
+ }
+ return status;
+}
+EXPORT_SYMBOL(ipu_calc_stripes_sizes);
diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c
new file mode 100755
index 000000000000..256fb9aa17e5
--- /dev/null
+++ b/drivers/mxc/ipu3/ipu_capture.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_capture.c
+ *
+ * @brief IPU capture dase functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/ipu.h>
+#include <linux/clk.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+
+/*!
+ * ipu_csi_init_interface
+ * Sets initial values for the CSI registers.
+ * The width and height of the sensor and the actual frame size will be
+ * set to the same values.
+ * @param width Sensor width
+ * @param height Sensor height
+ * @param pixel_fmt pixel format
+ * @param cfg_param ipu_csi_signal_cfg_t structure
+ * @param csi csi 0 or csi 1
+ *
+ * @return 0 for success, -EINVAL for error
+ */
+int32_t
+ipu_csi_init_interface(uint16_t width, uint16_t height, uint32_t pixel_fmt,
+ ipu_csi_signal_cfg_t cfg_param)
+{
+ uint32_t data = 0;
+ uint32_t csi = cfg_param.csi;
+ unsigned long lock_flags;
+
+ /* Set SENS_DATA_FORMAT bits (8, 9 and 10)
+ RGB or YUV444 is 0 which is current value in data so not set
+ explicitly
+ This is also the default value if attempts are made to set it to
+ something invalid. */
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_YUYV:
+ cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+ break;
+ case IPU_PIX_FMT_UYVY:
+ cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
+ break;
+ case IPU_PIX_FMT_RGB24:
+ case IPU_PIX_FMT_BGR24:
+ cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
+ break;
+ case IPU_PIX_FMT_GENERIC:
+ cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
+ break;
+ case IPU_PIX_FMT_RGB565:
+ cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
+ break;
+ case IPU_PIX_FMT_RGB555:
+ cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set the CSI_SENS_CONF register remaining fields */
+ data |= cfg_param.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
+ cfg_param.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
+ cfg_param.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
+ cfg_param.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
+ cfg_param.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
+ cfg_param.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
+ cfg_param.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
+ cfg_param.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
+ cfg_param.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
+ cfg_param.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
+ cfg_param.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ __raw_writel(data, CSI_SENS_CONF(csi));
+
+ /* Setup sensor frame size */
+ __raw_writel((width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE(csi));
+
+ /* Set CCIR registers */
+ if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) ||
+ (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED)) {
+ _ipu_csi_ccir_err_detection_enable(csi);
+ __raw_writel(0x40030, CSI_CCIR_CODE_1(csi));
+ __raw_writel(0xFF0000, CSI_CCIR_CODE_3(csi));
+ } else if ((cfg_param.clk_mode ==
+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR) ||
+ (cfg_param.clk_mode ==
+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR) ||
+ (cfg_param.clk_mode ==
+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) ||
+ (cfg_param.clk_mode ==
+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) {
+ _ipu_csi_ccir_err_detection_enable(csi);
+ __raw_writel(0x40030, CSI_CCIR_CODE_1(csi));
+ __raw_writel(0xFF0000, CSI_CCIR_CODE_3(csi));
+ } else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) ||
+ (cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) {
+ _ipu_csi_ccir_err_detection_disable(csi);
+ }
+
+ dev_dbg(g_ipu_dev, "CSI_SENS_CONF = 0x%08X\n",
+ __raw_readl(CSI_SENS_CONF(csi)));
+ dev_dbg(g_ipu_dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
+ __raw_readl(CSI_ACT_FRM_SIZE(csi)));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_csi_init_interface);
+
+/*! _ipu_csi_mclk_set
+ *
+ * @param pixel_clk desired pixel clock frequency in Hz
+ * @param csi csi 0 or csi 1
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_mclk_set(uint32_t pixel_clk, uint32_t csi)
+{
+ uint32_t temp;
+ uint32_t div_ratio;
+
+ div_ratio = (clk_get_rate(g_ipu_clk) / pixel_clk) - 1;
+
+ if (div_ratio > 0xFF || div_ratio < 0) {
+ dev_dbg(g_ipu_dev, "The value of pixel_clk extends normal range\n");
+ return -EINVAL;
+ }
+
+ temp = __raw_readl(CSI_SENS_CONF(csi));
+ temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
+ __raw_writel(temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
+ CSI_SENS_CONF(csi));
+
+ return 0;
+}
+
+/*!
+ * ipu_csi_enable_mclk
+ *
+ * @param csi csi 0 or csi 1
+ * @param flag true to enable mclk, false to disable mclk
+ * @param wait true to wait 100ms make clock stable, false not wait
+ *
+ * @return Returns 0 on success
+ */
+int ipu_csi_enable_mclk(int csi, bool flag, bool wait)
+{
+ if (flag) {
+ clk_enable(g_csi_clk[csi]);
+ if (wait == true)
+ msleep(10);
+ } else
+ clk_disable(g_csi_clk[csi]);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_csi_enable_mclk);
+
+/*!
+ * ipu_csi_get_window_size
+ *
+ * @param width pointer to window width
+ * @param height pointer to window height
+ * @param csi csi 0 or csi 1
+ */
+void ipu_csi_get_window_size(uint32_t *width, uint32_t *height, uint32_t csi)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(CSI_ACT_FRM_SIZE(csi));
+ *width = (reg & 0xFFFF) + 1;
+ *height = (reg >> 16 & 0xFFFF) + 1;
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_csi_get_window_size);
+
+/*!
+ * ipu_csi_set_window_size
+ *
+ * @param width window width
+ * @param height window height
+ * @param csi csi 0 or csi 1
+ */
+void ipu_csi_set_window_size(uint32_t width, uint32_t height, uint32_t csi)
+{
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ __raw_writel((width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE(csi));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_csi_set_window_size);
+
+/*!
+ * ipu_csi_set_window_pos
+ *
+ * @param left uint32 window x start
+ * @param top uint32 window y start
+ * @param csi csi 0 or csi 1
+ */
+void ipu_csi_set_window_pos(uint32_t left, uint32_t top, uint32_t csi)
+{
+ uint32_t temp;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(CSI_OUT_FRM_CTRL(csi));
+ temp &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
+ temp |= ((top << CSI_VSC_SHIFT) | (left << CSI_HSC_SHIFT));
+ __raw_writel(temp, CSI_OUT_FRM_CTRL(csi));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_csi_set_window_pos);
+
+/*!
+ * _ipu_csi_horizontal_downsize_enable
+ * Enable horizontal downsizing(decimation) by 2.
+ *
+ * @param csi csi 0 or csi 1
+ */
+void _ipu_csi_horizontal_downsize_enable(uint32_t csi)
+{
+ uint32_t temp;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(CSI_OUT_FRM_CTRL(csi));
+ temp |= CSI_HORI_DOWNSIZE_EN;
+ __raw_writel(temp, CSI_OUT_FRM_CTRL(csi));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * _ipu_csi_horizontal_downsize_disable
+ * Disable horizontal downsizing(decimation) by 2.
+ *
+ * @param csi csi 0 or csi 1
+ */
+void _ipu_csi_horizontal_downsize_disable(uint32_t csi)
+{
+ uint32_t temp;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(CSI_OUT_FRM_CTRL(csi));
+ temp &= ~CSI_HORI_DOWNSIZE_EN;
+ __raw_writel(temp, CSI_OUT_FRM_CTRL(csi));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * _ipu_csi_vertical_downsize_enable
+ * Enable vertical downsizing(decimation) by 2.
+ *
+ * @param csi csi 0 or csi 1
+ */
+void _ipu_csi_vertical_downsize_enable(uint32_t csi)
+{
+ uint32_t temp;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(CSI_OUT_FRM_CTRL(csi));
+ temp |= CSI_VERT_DOWNSIZE_EN;
+ __raw_writel(temp, CSI_OUT_FRM_CTRL(csi));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * _ipu_csi_vertical_downsize_disable
+ * Disable vertical downsizing(decimation) by 2.
+ *
+ * @param csi csi 0 or csi 1
+ */
+void _ipu_csi_vertical_downsize_disable(uint32_t csi)
+{
+ uint32_t temp;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(CSI_OUT_FRM_CTRL(csi));
+ temp &= ~CSI_VERT_DOWNSIZE_EN;
+ __raw_writel(temp, CSI_OUT_FRM_CTRL(csi));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * ipu_csi_set_test_generator
+ *
+ * @param active 1 for active and 0 for inactive
+ * @param r_value red value for the generated pattern of even pixel
+ * @param g_value green value for the generated pattern of even
+ * pixel
+ * @param b_value blue value for the generated pattern of even pixel
+ * @param pixel_clk desired pixel clock frequency in Hz
+ * @param csi csi 0 or csi 1
+ */
+void ipu_csi_set_test_generator(bool active, uint32_t r_value,
+ uint32_t g_value, uint32_t b_value, uint32_t pix_clk, uint32_t csi)
+{
+ uint32_t temp;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(CSI_TST_CTRL(csi));
+
+ if (active == false) {
+ temp &= ~CSI_TEST_GEN_MODE_EN;
+ __raw_writel(temp, CSI_TST_CTRL(csi));
+ } else {
+ /* Set sensb_mclk div_ratio*/
+ _ipu_csi_mclk_set(pix_clk, csi);
+
+ temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
+ CSI_TEST_GEN_B_MASK);
+ temp |= CSI_TEST_GEN_MODE_EN;
+ temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
+ (g_value << CSI_TEST_GEN_G_SHIFT) |
+ (b_value << CSI_TEST_GEN_B_SHIFT);
+ __raw_writel(temp, CSI_TST_CTRL(csi));
+ }
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+EXPORT_SYMBOL(ipu_csi_set_test_generator);
+
+/*!
+ * _ipu_csi_ccir_err_detection_en
+ * Enable error detection and correction for
+ * CCIR interlaced mode with protection bit.
+ *
+ * @param csi csi 0 or csi 1
+ */
+void _ipu_csi_ccir_err_detection_enable(uint32_t csi)
+{
+ uint32_t temp;
+
+ temp = __raw_readl(CSI_CCIR_CODE_1(csi));
+ temp |= CSI_CCIR_ERR_DET_EN;
+ __raw_writel(temp, CSI_CCIR_CODE_1(csi));
+}
+
+/*!
+ * _ipu_csi_ccir_err_detection_disable
+ * Disable error detection and correction for
+ * CCIR interlaced mode with protection bit.
+ *
+ * @param csi csi 0 or csi 1
+ */
+void _ipu_csi_ccir_err_detection_disable(uint32_t csi)
+{
+ uint32_t temp;
+
+ temp = __raw_readl(CSI_CCIR_CODE_1(csi));
+ temp &= ~CSI_CCIR_ERR_DET_EN;
+ __raw_writel(temp, CSI_CCIR_CODE_1(csi));
+}
+
+/*!
+ * _ipu_csi_set_mipi_di
+ *
+ * @param num MIPI data identifier 0-3 handled by CSI
+ * @param di_val data identifier value
+ * @param csi csi 0 or csi 1
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_set_mipi_di(uint32_t num, uint32_t di_val, uint32_t csi)
+{
+ uint32_t temp;
+ int retval = 0;
+ unsigned long lock_flags;
+
+ if (di_val > 0xFFL) {
+ retval = -EINVAL;
+ goto err;
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(CSI_MIPI_DI(csi));
+
+ switch (num) {
+ case IPU_CSI_MIPI_DI0:
+ temp &= ~CSI_MIPI_DI0_MASK;
+ temp |= (di_val << CSI_MIPI_DI0_SHIFT);
+ __raw_writel(temp, CSI_MIPI_DI(csi));
+ break;
+ case IPU_CSI_MIPI_DI1:
+ temp &= ~CSI_MIPI_DI1_MASK;
+ temp |= (di_val << CSI_MIPI_DI1_SHIFT);
+ __raw_writel(temp, CSI_MIPI_DI(csi));
+ break;
+ case IPU_CSI_MIPI_DI2:
+ temp &= ~CSI_MIPI_DI2_MASK;
+ temp |= (di_val << CSI_MIPI_DI2_SHIFT);
+ __raw_writel(temp, CSI_MIPI_DI(csi));
+ break;
+ case IPU_CSI_MIPI_DI3:
+ temp &= ~CSI_MIPI_DI3_MASK;
+ temp |= (di_val << CSI_MIPI_DI3_SHIFT);
+ __raw_writel(temp, CSI_MIPI_DI(csi));
+ break;
+ default:
+ retval = -EINVAL;
+ goto err;
+ }
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+err:
+ return retval;
+}
+
+/*!
+ * _ipu_csi_set_skip_isp
+ *
+ * @param skip select frames to be skipped and set the
+ * correspond bits to 1
+ * @param max_ratio number of frames in a skipping set and the
+ * maximum value of max_ratio is 5
+ * @param csi csi 0 or csi 1
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_set_skip_isp(uint32_t skip, uint32_t max_ratio, uint32_t csi)
+{
+ uint32_t temp;
+ int retval = 0;
+ unsigned long lock_flags;
+
+ if (max_ratio > 5) {
+ retval = -EINVAL;
+ goto err;
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(CSI_SKIP(csi));
+ temp &= ~(CSI_MAX_RATIO_SKIP_ISP_MASK | CSI_SKIP_ISP_MASK);
+ temp |= (max_ratio << CSI_MAX_RATIO_SKIP_ISP_SHIFT) |
+ (skip << CSI_SKIP_ISP_SHIFT);
+ __raw_writel(temp, CSI_SKIP(csi));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+err:
+ return retval;
+}
+
+/*!
+ * _ipu_csi_set_skip_smfc
+ *
+ * @param skip select frames to be skipped and set the
+ * correspond bits to 1
+ * @param max_ratio number of frames in a skipping set and the
+ * maximum value of max_ratio is 5
+ * @param id csi to smfc skipping id
+ * @param csi csi 0 or csi 1
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_set_skip_smfc(uint32_t skip, uint32_t max_ratio,
+ uint32_t id, uint32_t csi)
+{
+ uint32_t temp;
+ int retval = 0;
+ unsigned long lock_flags;
+
+ if (max_ratio > 5 || id > 3) {
+ retval = -EINVAL;
+ goto err;
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(CSI_SKIP(csi));
+ temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
+ CSI_SKIP_SMFC_MASK);
+ temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
+ (id << CSI_ID_2_SKIP_SHIFT) |
+ (skip << CSI_SKIP_SMFC_SHIFT);
+ __raw_writel(temp, CSI_SKIP(csi));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+err:
+ return retval;
+}
+
+/*!
+ * _ipu_smfc_init
+ * Map CSI frames to IDMAC channels.
+ *
+ * @param channel IDMAC channel 0-3
+ * @param mipi_id mipi id number 0-3
+ * @param csi csi0 or csi1
+ */
+void _ipu_smfc_init(ipu_channel_t channel, uint32_t mipi_id, uint32_t csi)
+{
+ uint32_t temp;
+
+ temp = __raw_readl(SMFC_MAP);
+
+ switch (channel) {
+ case CSI_MEM0:
+ temp &= ~SMFC_MAP_CH0_MASK;
+ temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH0_SHIFT;
+ break;
+ case CSI_MEM1:
+ temp &= ~SMFC_MAP_CH1_MASK;
+ temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH1_SHIFT;
+ break;
+ case CSI_MEM2:
+ temp &= ~SMFC_MAP_CH2_MASK;
+ temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH2_SHIFT;
+ break;
+ case CSI_MEM3:
+ temp &= ~SMFC_MAP_CH3_MASK;
+ temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH3_SHIFT;
+ break;
+ default:
+ return;
+ }
+
+ __raw_writel(temp, SMFC_MAP);
+}
+
+/*!
+ * _ipu_smfc_set_wmc
+ * Caution: The number of required channels, the enabled channels
+ * and the FIFO size per channel are configured restrictedly.
+ *
+ * @param channel IDMAC channel 0-3
+ * @param set set 1 or clear 0
+ * @param level water mark level when FIFO is on the
+ * relative size
+ */
+void _ipu_smfc_set_wmc(ipu_channel_t channel, bool set, uint32_t level)
+{
+ uint32_t temp;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(SMFC_WMC);
+
+ switch (channel) {
+ case CSI_MEM0:
+ if (set == true) {
+ temp &= ~SMFC_WM0_SET_MASK;
+ temp |= level << SMFC_WM0_SET_SHIFT;
+ } else {
+ temp &= ~SMFC_WM0_CLR_MASK;
+ temp |= level << SMFC_WM0_CLR_SHIFT;
+ }
+ break;
+ case CSI_MEM1:
+ if (set == true) {
+ temp &= ~SMFC_WM1_SET_MASK;
+ temp |= level << SMFC_WM1_SET_SHIFT;
+ } else {
+ temp &= ~SMFC_WM1_CLR_MASK;
+ temp |= level << SMFC_WM1_CLR_SHIFT;
+ }
+ break;
+ case CSI_MEM2:
+ if (set == true) {
+ temp &= ~SMFC_WM2_SET_MASK;
+ temp |= level << SMFC_WM2_SET_SHIFT;
+ } else {
+ temp &= ~SMFC_WM2_CLR_MASK;
+ temp |= level << SMFC_WM2_CLR_SHIFT;
+ }
+ break;
+ case CSI_MEM3:
+ if (set == true) {
+ temp &= ~SMFC_WM3_SET_MASK;
+ temp |= level << SMFC_WM3_SET_SHIFT;
+ } else {
+ temp &= ~SMFC_WM3_CLR_MASK;
+ temp |= level << SMFC_WM3_CLR_SHIFT;
+ }
+ break;
+ default:
+ return;
+ }
+
+ __raw_writel(temp, SMFC_WMC);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * _ipu_smfc_set_burst_size
+ *
+ * @param channel IDMAC channel 0-3
+ * @param bs burst size of IDMAC channel,
+ * the value programmed here shoud be BURST_SIZE-1
+ */
+void _ipu_smfc_set_burst_size(ipu_channel_t channel, uint32_t bs)
+{
+ uint32_t temp;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ temp = __raw_readl(SMFC_BS);
+
+ switch (channel) {
+ case CSI_MEM0:
+ temp &= ~SMFC_BS0_MASK;
+ temp |= bs << SMFC_BS0_SHIFT;
+ break;
+ case CSI_MEM1:
+ temp &= ~SMFC_BS1_MASK;
+ temp |= bs << SMFC_BS1_SHIFT;
+ break;
+ case CSI_MEM2:
+ temp &= ~SMFC_BS2_MASK;
+ temp |= bs << SMFC_BS2_SHIFT;
+ break;
+ case CSI_MEM3:
+ temp &= ~SMFC_BS3_MASK;
+ temp |= bs << SMFC_BS3_SHIFT;
+ break;
+ default:
+ return;
+ }
+
+ __raw_writel(temp, SMFC_BS);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+}
+
+/*!
+ * _ipu_csi_init
+ *
+ * @param channel IDMAC channel
+ * @param csi csi 0 or csi 1
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int _ipu_csi_init(ipu_channel_t channel, uint32_t csi)
+{
+ uint32_t csi_sens_conf, csi_dest;
+ int retval = 0;
+
+ switch (channel) {
+ case CSI_MEM0:
+ case CSI_MEM1:
+ case CSI_MEM2:
+ case CSI_MEM3:
+ csi_dest = CSI_DATA_DEST_IDMAC;
+ break;
+ case CSI_PRP_ENC_MEM:
+ case CSI_PRP_VF_MEM:
+ csi_dest = CSI_DATA_DEST_IC;
+ break;
+ default:
+ retval = -EINVAL;
+ goto err;
+ }
+
+ csi_sens_conf = __raw_readl(CSI_SENS_CONF(csi));
+ csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
+ __raw_writel(csi_sens_conf | (csi_dest <<
+ CSI_SENS_CONF_DATA_DEST_SHIFT), CSI_SENS_CONF(csi));
+err:
+ return retval;
+}
diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c
new file mode 100644
index 000000000000..4c46db8b0abc
--- /dev/null
+++ b/drivers/mxc/ipu3/ipu_common.c
@@ -0,0 +1,2318 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_common.c
+ *
+ * @brief This file contains the IPU driver common API functions.
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <linux/clk.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+struct ipu_irq_node {
+ irqreturn_t(*handler) (int, void *); /*!< the ISR */
+ const char *name; /*!< device associated with the interrupt */
+ void *dev_id; /*!< some unique information for the ISR */
+ __u32 flags; /*!< not used */
+};
+
+/* Globals */
+struct clk *g_ipu_clk;
+bool g_ipu_clk_enabled;
+struct clk *g_di_clk[2];
+struct clk *g_csi_clk[2];
+unsigned char g_dc_di_assignment[10];
+ipu_channel_t g_ipu_csi_channel[2];
+int g_ipu_irq[2];
+int g_ipu_hw_rev;
+bool g_sec_chan_en[22];
+bool g_thrd_chan_en[21];
+uint32_t g_channel_init_mask;
+uint32_t g_channel_enable_mask;
+DEFINE_SPINLOCK(ipu_lock);
+struct device *g_ipu_dev;
+
+static struct ipu_irq_node ipu_irq_list[IPU_IRQ_COUNT];
+static const char driver_name[] = "mxc_ipu";
+
+static int ipu_dc_use_count;
+static int ipu_dp_use_count;
+static int ipu_dmfc_use_count;
+static int ipu_smfc_use_count;
+static int ipu_ic_use_count;
+static int ipu_rot_use_count;
+static int ipu_vdi_use_count;
+static int ipu_di_use_count[2];
+static int ipu_csi_use_count[2];
+/* Set to the follow using IC direct channel, default non */
+static ipu_channel_t using_ic_dirct_ch;
+
+/* for power gating */
+static uint32_t ipu_conf_reg;
+static uint32_t ic_conf_reg;
+static uint32_t ipu_cha_db_mode_reg[4];
+static uint32_t ipu_cha_cur_buf_reg[4];
+static uint32_t idma_enable_reg[2];
+static uint32_t buf_ready_reg[8];
+
+u32 *ipu_cm_reg;
+u32 *ipu_idmac_reg;
+u32 *ipu_dp_reg;
+u32 *ipu_ic_reg;
+u32 *ipu_dc_reg;
+u32 *ipu_dc_tmpl_reg;
+u32 *ipu_dmfc_reg;
+u32 *ipu_di_reg[2];
+u32 *ipu_smfc_reg;
+u32 *ipu_csi_reg[2];
+u32 *ipu_cpmem_base;
+u32 *ipu_tpmem_base;
+u32 *ipu_disp_base[2];
+u32 *ipu_vdi_reg;
+
+/* Static functions */
+static irqreturn_t ipu_irq_handler(int irq, void *desc);
+
+static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type)
+{
+ return ((uint32_t) ch >> (6 * type)) & 0x3F;
+};
+
+static inline int _ipu_is_ic_chan(uint32_t dma_chan)
+{
+ return ((dma_chan >= 11) && (dma_chan <= 22) && (dma_chan != 17) && (dma_chan != 18));
+}
+
+static inline int _ipu_is_ic_graphic_chan(uint32_t dma_chan)
+{
+ return (dma_chan == 14 || dma_chan == 15);
+}
+
+/* Either DP BG or DP FG can be graphic window */
+static inline int _ipu_is_dp_graphic_chan(uint32_t dma_chan)
+{
+ return (dma_chan == 23 || dma_chan == 27);
+}
+
+static inline int _ipu_is_irt_chan(uint32_t dma_chan)
+{
+ return ((dma_chan >= 45) && (dma_chan <= 50));
+}
+
+static inline int _ipu_is_dmfc_chan(uint32_t dma_chan)
+{
+ return ((dma_chan >= 23) && (dma_chan <= 29));
+}
+
+static inline int _ipu_is_smfc_chan(uint32_t dma_chan)
+{
+ return ((dma_chan >= 0) && (dma_chan <= 3));
+}
+
+#define idma_is_valid(ch) (ch != NO_DMA)
+#define idma_mask(ch) (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0)
+#define idma_is_set(reg, dma) (__raw_readl(reg(dma)) & idma_mask(dma))
+
+/*!
+ * This function is called by the driver framework to initialize the IPU
+ * hardware.
+ *
+ * @param dev The device structure for the IPU passed in by the
+ * driver framework.
+ *
+ * @return Returns 0 on success or negative error code on error
+ */
+static int ipu_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct mxc_ipu_config *plat_data = pdev->dev.platform_data;
+ unsigned long ipu_base;
+
+ spin_lock_init(&ipu_lock);
+
+ g_ipu_hw_rev = plat_data->rev;
+
+ g_ipu_dev = &pdev->dev;
+
+ /* Register IPU interrupts */
+ g_ipu_irq[0] = platform_get_irq(pdev, 0);
+ if (g_ipu_irq[0] < 0)
+ return -EINVAL;
+
+ if (request_irq(g_ipu_irq[0], ipu_irq_handler, 0, pdev->name, 0) != 0) {
+ dev_err(g_ipu_dev, "request SYNC interrupt failed\n");
+ return -EBUSY;
+ }
+ /* Some platforms have 2 IPU interrupts */
+ g_ipu_irq[1] = platform_get_irq(pdev, 1);
+ if (g_ipu_irq[1] >= 0) {
+ if (request_irq
+ (g_ipu_irq[1], ipu_irq_handler, 0, pdev->name, 0) != 0) {
+ dev_err(g_ipu_dev, "request ERR interrupt failed\n");
+ return -EBUSY;
+ }
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res))
+ return -ENODEV;
+
+ ipu_base = res->start;
+ ipu_cm_reg = ioremap(ipu_base + IPU_CM_REG_BASE, PAGE_SIZE);
+ ipu_ic_reg = ioremap(ipu_base + IPU_IC_REG_BASE, PAGE_SIZE);
+ ipu_idmac_reg = ioremap(ipu_base + IPU_IDMAC_REG_BASE, PAGE_SIZE);
+ /* DP Registers are accessed thru the SRM */
+ ipu_dp_reg = ioremap(ipu_base + IPU_SRM_REG_BASE, PAGE_SIZE);
+ ipu_dc_reg = ioremap(ipu_base + IPU_DC_REG_BASE, PAGE_SIZE);
+ ipu_dmfc_reg = ioremap(ipu_base + IPU_DMFC_REG_BASE, PAGE_SIZE);
+ ipu_di_reg[0] = ioremap(ipu_base + IPU_DI0_REG_BASE, PAGE_SIZE);
+ ipu_di_reg[1] = ioremap(ipu_base + IPU_DI1_REG_BASE, PAGE_SIZE);
+ ipu_smfc_reg = ioremap(ipu_base + IPU_SMFC_REG_BASE, PAGE_SIZE);
+ ipu_csi_reg[0] = ioremap(ipu_base + IPU_CSI0_REG_BASE, PAGE_SIZE);
+ ipu_csi_reg[1] = ioremap(ipu_base + IPU_CSI1_REG_BASE, PAGE_SIZE);
+ ipu_cpmem_base = ioremap(ipu_base + IPU_CPMEM_REG_BASE, PAGE_SIZE);
+ ipu_tpmem_base = ioremap(ipu_base + IPU_TPM_REG_BASE, SZ_64K);
+ ipu_dc_tmpl_reg = ioremap(ipu_base + IPU_DC_TMPL_REG_BASE, SZ_128K);
+ ipu_disp_base[1] = ioremap(ipu_base + IPU_DISP1_BASE, SZ_4K);
+ ipu_vdi_reg = ioremap(ipu_base + IPU_VDI_REG_BASE, PAGE_SIZE);
+
+ dev_dbg(g_ipu_dev, "IPU VDI Regs = %p\n", ipu_vdi_reg);
+ dev_dbg(g_ipu_dev, "IPU CM Regs = %p\n", ipu_cm_reg);
+ dev_dbg(g_ipu_dev, "IPU IC Regs = %p\n", ipu_ic_reg);
+ dev_dbg(g_ipu_dev, "IPU IDMAC Regs = %p\n", ipu_idmac_reg);
+ dev_dbg(g_ipu_dev, "IPU DP Regs = %p\n", ipu_dp_reg);
+ dev_dbg(g_ipu_dev, "IPU DC Regs = %p\n", ipu_dc_reg);
+ dev_dbg(g_ipu_dev, "IPU DMFC Regs = %p\n", ipu_dmfc_reg);
+ dev_dbg(g_ipu_dev, "IPU DI0 Regs = %p\n", ipu_di_reg[0]);
+ dev_dbg(g_ipu_dev, "IPU DI1 Regs = %p\n", ipu_di_reg[1]);
+ dev_dbg(g_ipu_dev, "IPU SMFC Regs = %p\n", ipu_smfc_reg);
+ dev_dbg(g_ipu_dev, "IPU CSI0 Regs = %p\n", ipu_csi_reg[0]);
+ dev_dbg(g_ipu_dev, "IPU CSI1 Regs = %p\n", ipu_csi_reg[1]);
+ dev_dbg(g_ipu_dev, "IPU CPMem = %p\n", ipu_cpmem_base);
+ dev_dbg(g_ipu_dev, "IPU TPMem = %p\n", ipu_tpmem_base);
+ dev_dbg(g_ipu_dev, "IPU DC Template Mem = %p\n", ipu_dc_tmpl_reg);
+ dev_dbg(g_ipu_dev, "IPU Display Region 1 Mem = %p\n", ipu_disp_base[1]);
+
+ /* Enable IPU and CSI clocks */
+ /* Get IPU clock freq */
+ g_ipu_clk = clk_get(&pdev->dev, "ipu_clk");
+ dev_dbg(g_ipu_dev, "ipu_clk = %lu\n", clk_get_rate(g_ipu_clk));
+
+ clk_enable(g_ipu_clk);
+
+ g_di_clk[0] = plat_data->di_clk[0];
+ g_di_clk[1] = plat_data->di_clk[1];
+
+ g_csi_clk[0] = clk_get(&pdev->dev, "csi_mclk1");
+ g_csi_clk[1] = clk_get(&pdev->dev, "csi_mclk2");
+
+ __raw_writel(0x807FFFFF, IPU_MEM_RST);
+ while (__raw_readl(IPU_MEM_RST) & 0x80000000) ;
+
+ _ipu_init_dc_mappings();
+
+ /* Enable error interrupts by default */
+ __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(5));
+ __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(6));
+ __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(9));
+ __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(10));
+
+ /* DMFC Init */
+ _ipu_dmfc_init();
+
+ /* Set sync refresh channels as high priority */
+ __raw_writel(0x18800000L, IDMAC_CHA_PRI(0));
+
+ /* Set MCU_T to divide MCU access window into 2 */
+ __raw_writel(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN);
+
+ clk_disable(g_ipu_clk);
+
+ register_ipu_device();
+
+ return 0;
+}
+
+int ipu_remove(struct platform_device *pdev)
+{
+ if (g_ipu_irq[0])
+ free_irq(g_ipu_irq[0], 0);
+ if (g_ipu_irq[1])
+ free_irq(g_ipu_irq[1], 0);
+
+ clk_put(g_ipu_clk);
+
+ iounmap(ipu_cm_reg);
+ iounmap(ipu_ic_reg);
+ iounmap(ipu_idmac_reg);
+ iounmap(ipu_dc_reg);
+ iounmap(ipu_dp_reg);
+ iounmap(ipu_dmfc_reg);
+ iounmap(ipu_di_reg[0]);
+ iounmap(ipu_di_reg[1]);
+ iounmap(ipu_smfc_reg);
+ iounmap(ipu_csi_reg[0]);
+ iounmap(ipu_csi_reg[1]);
+ iounmap(ipu_cpmem_base);
+ iounmap(ipu_tpmem_base);
+ iounmap(ipu_dc_tmpl_reg);
+ iounmap(ipu_disp_base[1]);
+ iounmap(ipu_vdi_reg);
+
+ return 0;
+}
+
+void ipu_dump_registers(void)
+{
+ printk(KERN_DEBUG "IPU_CONF = \t0x%08X\n", __raw_readl(IPU_CONF));
+ printk(KERN_DEBUG "IDMAC_CONF = \t0x%08X\n", __raw_readl(IDMAC_CONF));
+ printk(KERN_DEBUG "IDMAC_CHA_EN1 = \t0x%08X\n",
+ __raw_readl(IDMAC_CHA_EN(0)));
+ printk(KERN_DEBUG "IDMAC_CHA_EN2 = \t0x%08X\n",
+ __raw_readl(IDMAC_CHA_EN(32)));
+ printk(KERN_DEBUG "IDMAC_CHA_PRI1 = \t0x%08X\n",
+ __raw_readl(IDMAC_CHA_PRI(0)));
+ printk(KERN_DEBUG "IDMAC_CHA_PRI2 = \t0x%08X\n",
+ __raw_readl(IDMAC_CHA_PRI(32)));
+ printk(KERN_DEBUG "IDMAC_BAND_EN1 = \t0x%08X\n",
+ __raw_readl(IDMAC_BAND_EN(0)));
+ printk(KERN_DEBUG "IDMAC_BAND_EN2 = \t0x%08X\n",
+ __raw_readl(IDMAC_BAND_EN(32)));
+ printk(KERN_DEBUG "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
+ __raw_readl(IPU_CHA_DB_MODE_SEL(0)));
+ printk(KERN_DEBUG "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
+ __raw_readl(IPU_CHA_DB_MODE_SEL(32)));
+ printk(KERN_DEBUG "DMFC_WR_CHAN = \t0x%08X\n",
+ __raw_readl(DMFC_WR_CHAN));
+ printk(KERN_DEBUG "DMFC_WR_CHAN_DEF = \t0x%08X\n",
+ __raw_readl(DMFC_WR_CHAN_DEF));
+ printk(KERN_DEBUG "DMFC_DP_CHAN = \t0x%08X\n",
+ __raw_readl(DMFC_DP_CHAN));
+ printk(KERN_DEBUG "DMFC_DP_CHAN_DEF = \t0x%08X\n",
+ __raw_readl(DMFC_DP_CHAN_DEF));
+ printk(KERN_DEBUG "DMFC_IC_CTRL = \t0x%08X\n",
+ __raw_readl(DMFC_IC_CTRL));
+ printk(KERN_DEBUG "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
+ __raw_readl(IPU_FS_PROC_FLOW1));
+ printk(KERN_DEBUG "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
+ __raw_readl(IPU_FS_PROC_FLOW2));
+ printk(KERN_DEBUG "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
+ __raw_readl(IPU_FS_PROC_FLOW3));
+ printk(KERN_DEBUG "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
+ __raw_readl(IPU_FS_DISP_FLOW1));
+}
+
+/*!
+ * This function is called to initialize a logical IPU channel.
+ *
+ * @param channel Input parameter for the logical channel ID to init.
+ *
+ * @param params Input parameter containing union of channel
+ * initialization parameters.
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params)
+{
+ int ret = 0;
+ uint32_t ipu_conf;
+ uint32_t reg;
+ unsigned long lock_flags;
+
+ dev_dbg(g_ipu_dev, "init channel = %d\n", IPU_CHAN_ID(channel));
+
+ /* re-enable error interrupts every time a channel is initialized */
+ __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(5));
+ __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(6));
+ __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(9));
+ __raw_writel(0xFFFFFFFF, IPU_INT_CTRL(10));
+
+ if (g_ipu_clk_enabled == false) {
+ g_ipu_clk_enabled = true;
+ clk_enable(g_ipu_clk);
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if (g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) {
+ dev_err(g_ipu_dev, "Warning: channel already initialized %d\n",
+ IPU_CHAN_ID(channel));
+ }
+
+ ipu_conf = __raw_readl(IPU_CONF);
+
+ switch (channel) {
+ case CSI_MEM0:
+ case CSI_MEM1:
+ case CSI_MEM2:
+ case CSI_MEM3:
+ if (params->csi_mem.csi > 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ipu_smfc_use_count++;
+ ipu_csi_use_count[params->csi_mem.csi]++;
+ g_ipu_csi_channel[params->csi_mem.csi] = channel;
+
+ /*SMFC setting*/
+ if (params->csi_mem.mipi_en) {
+ ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+ params->csi_mem.csi));
+ _ipu_smfc_init(channel, params->csi_mem.mipi_id,
+ params->csi_mem.csi);
+ } else {
+ ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+ params->csi_mem.csi));
+ _ipu_smfc_init(channel, 0, params->csi_mem.csi);
+ }
+
+ /*CSI data (include compander) dest*/
+ _ipu_csi_init(channel, params->csi_mem.csi);
+ break;
+ case CSI_PRP_ENC_MEM:
+ if (params->csi_prp_enc_mem.csi > 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+ if (using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) {
+ ret = -EINVAL;
+ goto err;
+ }
+ using_ic_dirct_ch = CSI_PRP_ENC_MEM;
+
+ ipu_ic_use_count++;
+ ipu_csi_use_count[params->csi_prp_enc_mem.csi]++;
+ g_ipu_csi_channel[params->csi_prp_enc_mem.csi] = channel;
+
+ /*Without SMFC, CSI only support parallel data source*/
+ ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+ params->csi_prp_enc_mem.csi));
+
+ /*CSI0/1 feed into IC*/
+ ipu_conf &= ~IPU_CONF_IC_INPUT;
+ if (params->csi_prp_enc_mem.csi)
+ ipu_conf |= IPU_CONF_CSI_SEL;
+ else
+ ipu_conf &= ~IPU_CONF_CSI_SEL;
+
+ /*PRP skip buffer in memory, only valid when RWS_EN is true*/
+ reg = __raw_readl(IPU_FS_PROC_FLOW1);
+ __raw_writel(reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
+
+ /*CSI data (include compander) dest*/
+ _ipu_csi_init(channel, params->csi_prp_enc_mem.csi);
+ _ipu_ic_init_prpenc(params, true);
+ break;
+ case CSI_PRP_VF_MEM:
+ if (params->csi_prp_vf_mem.csi > 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+ if (using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) {
+ ret = -EINVAL;
+ goto err;
+ }
+ using_ic_dirct_ch = CSI_PRP_VF_MEM;
+
+ ipu_ic_use_count++;
+ ipu_csi_use_count[params->csi_prp_vf_mem.csi]++;
+ g_ipu_csi_channel[params->csi_prp_vf_mem.csi] = channel;
+
+ /*Without SMFC, CSI only support parallel data source*/
+ ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET +
+ params->csi_prp_vf_mem.csi));
+
+ /*CSI0/1 feed into IC*/
+ ipu_conf &= ~IPU_CONF_IC_INPUT;
+ if (params->csi_prp_vf_mem.csi)
+ ipu_conf |= IPU_CONF_CSI_SEL;
+ else
+ ipu_conf &= ~IPU_CONF_CSI_SEL;
+
+ /*PRP skip buffer in memory, only valid when RWS_EN is true*/
+ reg = __raw_readl(IPU_FS_PROC_FLOW1);
+ __raw_writel(reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+
+ /*CSI data (include compander) dest*/
+ _ipu_csi_init(channel, params->csi_prp_vf_mem.csi);
+ _ipu_ic_init_prpvf(params, true);
+ break;
+ case MEM_PRP_VF_MEM:
+ ipu_ic_use_count++;
+ reg = __raw_readl(IPU_FS_PROC_FLOW1);
+ __raw_writel(reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+
+ if (params->mem_prp_vf_mem.graphics_combine_en)
+ g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+ if (params->mem_prp_vf_mem.alpha_chan_en)
+ g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+
+ _ipu_ic_init_prpvf(params, false);
+ break;
+ case MEM_VDI_PRP_VF_MEM:
+ if ((using_ic_dirct_ch == CSI_PRP_VF_MEM) ||
+ (using_ic_dirct_ch == CSI_PRP_ENC_MEM)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ using_ic_dirct_ch = MEM_VDI_PRP_VF_MEM;
+ ipu_ic_use_count++;
+ ipu_vdi_use_count++;
+ reg = __raw_readl(IPU_FS_PROC_FLOW1);
+ reg &= ~FS_VDI_SRC_SEL_MASK;
+ __raw_writel(reg , IPU_FS_PROC_FLOW1);
+
+ if (params->mem_prp_vf_mem.graphics_combine_en)
+ g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+ _ipu_ic_init_prpvf(params, false);
+ _ipu_vdi_init(channel, params);
+ break;
+ case MEM_VDI_PRP_VF_MEM_P:
+ _ipu_vdi_init(channel, params);
+ break;
+ case MEM_VDI_PRP_VF_MEM_N:
+ _ipu_vdi_init(channel, params);
+ break;
+ case MEM_ROT_VF_MEM:
+ ipu_ic_use_count++;
+ ipu_rot_use_count++;
+ _ipu_ic_init_rotate_vf(params);
+ break;
+ case MEM_PRP_ENC_MEM:
+ ipu_ic_use_count++;
+ reg = __raw_readl(IPU_FS_PROC_FLOW1);
+ __raw_writel(reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
+ _ipu_ic_init_prpenc(params, false);
+ break;
+ case MEM_ROT_ENC_MEM:
+ ipu_ic_use_count++;
+ ipu_rot_use_count++;
+ _ipu_ic_init_rotate_enc(params);
+ break;
+ case MEM_PP_MEM:
+ if (params->mem_pp_mem.graphics_combine_en)
+ g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
+ if (params->mem_pp_mem.alpha_chan_en)
+ g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+ _ipu_ic_init_pp(params);
+ ipu_ic_use_count++;
+ break;
+ case MEM_ROT_PP_MEM:
+ _ipu_ic_init_rotate_pp(params);
+ ipu_ic_use_count++;
+ ipu_rot_use_count++;
+ break;
+ case MEM_DC_SYNC:
+ if (params->mem_dc_sync.di > 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ g_dc_di_assignment[1] = params->mem_dc_sync.di;
+ _ipu_dc_init(1, params->mem_dc_sync.di,
+ params->mem_dc_sync.interlaced);
+ ipu_di_use_count[params->mem_dc_sync.di]++;
+ ipu_dc_use_count++;
+ ipu_dmfc_use_count++;
+ break;
+ case MEM_BG_SYNC:
+ if (params->mem_dp_bg_sync.di > 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (params->mem_dp_bg_sync.alpha_chan_en)
+ g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+
+ g_dc_di_assignment[5] = params->mem_dp_bg_sync.di;
+ _ipu_dp_init(channel, params->mem_dp_bg_sync.in_pixel_fmt,
+ params->mem_dp_bg_sync.out_pixel_fmt);
+ _ipu_dc_init(5, params->mem_dp_bg_sync.di,
+ params->mem_dp_bg_sync.interlaced);
+ ipu_di_use_count[params->mem_dp_bg_sync.di]++;
+ ipu_dc_use_count++;
+ ipu_dp_use_count++;
+ ipu_dmfc_use_count++;
+ break;
+ case MEM_FG_SYNC:
+ _ipu_dp_init(channel, params->mem_dp_fg_sync.in_pixel_fmt,
+ params->mem_dp_fg_sync.out_pixel_fmt);
+
+ if (params->mem_dp_fg_sync.alpha_chan_en)
+ g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
+
+ ipu_dc_use_count++;
+ ipu_dp_use_count++;
+ ipu_dmfc_use_count++;
+ break;
+ case DIRECT_ASYNC0:
+ if (params->direct_async.di > 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ g_dc_di_assignment[8] = params->direct_async.di;
+ _ipu_dc_init(8, params->direct_async.di, false);
+ ipu_di_use_count[params->direct_async.di]++;
+ ipu_dc_use_count++;
+ break;
+ case DIRECT_ASYNC1:
+ if (params->direct_async.di > 1) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ g_dc_di_assignment[9] = params->direct_async.di;
+ _ipu_dc_init(9, params->direct_async.di, false);
+ ipu_di_use_count[params->direct_async.di]++;
+ ipu_dc_use_count++;
+ break;
+ default:
+ dev_err(g_ipu_dev, "Missing channel initialization\n");
+ break;
+ }
+
+ /* Enable IPU sub module */
+ g_channel_init_mask |= 1L << IPU_CHAN_ID(channel);
+ if (ipu_ic_use_count == 1)
+ ipu_conf |= IPU_CONF_IC_EN;
+ if (ipu_vdi_use_count == 1) {
+ ipu_conf |= IPU_CONF_VDI_EN;
+ ipu_conf |= IPU_CONF_IC_INPUT;
+ }
+ if (ipu_rot_use_count == 1)
+ ipu_conf |= IPU_CONF_ROT_EN;
+ if (ipu_dc_use_count == 1)
+ ipu_conf |= IPU_CONF_DC_EN;
+ if (ipu_dp_use_count == 1)
+ ipu_conf |= IPU_CONF_DP_EN;
+ if (ipu_dmfc_use_count == 1)
+ ipu_conf |= IPU_CONF_DMFC_EN;
+ if (ipu_di_use_count[0] == 1) {
+ ipu_conf |= IPU_CONF_DI0_EN;
+ clk_enable(g_di_clk[0]);
+ }
+ if (ipu_di_use_count[1] == 1) {
+ ipu_conf |= IPU_CONF_DI1_EN;
+ clk_enable(g_di_clk[1]);
+ }
+ if (ipu_smfc_use_count == 1)
+ ipu_conf |= IPU_CONF_SMFC_EN;
+ if (ipu_csi_use_count[0] == 1)
+ ipu_conf |= IPU_CONF_CSI0_EN;
+ if (ipu_csi_use_count[1] == 1)
+ ipu_conf |= IPU_CONF_CSI1_EN;
+
+ __raw_writel(ipu_conf, IPU_CONF);
+
+err:
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return ret;
+}
+EXPORT_SYMBOL(ipu_init_channel);
+
+/*!
+ * This function is called to uninitialize a logical IPU channel.
+ *
+ * @param channel Input parameter for the logical channel ID to uninit.
+ */
+void ipu_uninit_channel(ipu_channel_t channel)
+{
+ unsigned long lock_flags;
+ uint32_t reg;
+ uint32_t in_dma, out_dma = 0;
+ uint32_t ipu_conf;
+
+ if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
+ dev_err(g_ipu_dev, "Channel already uninitialized %d\n",
+ IPU_CHAN_ID(channel));
+ return;
+ }
+
+ /* Make sure channel is disabled */
+ /* Get input and output dma channels */
+ in_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+ out_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+
+ if (idma_is_set(IDMAC_CHA_EN, in_dma) ||
+ idma_is_set(IDMAC_CHA_EN, out_dma)) {
+ dev_err(g_ipu_dev,
+ "Channel %d is not disabled, disable first\n",
+ IPU_CHAN_ID(channel));
+ return;
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ /* Reset the double buffer */
+ reg = __raw_readl(IPU_CHA_DB_MODE_SEL(in_dma));
+ __raw_writel(reg & ~idma_mask(in_dma), IPU_CHA_DB_MODE_SEL(in_dma));
+ reg = __raw_readl(IPU_CHA_DB_MODE_SEL(out_dma));
+ __raw_writel(reg & ~idma_mask(out_dma), IPU_CHA_DB_MODE_SEL(out_dma));
+
+ g_sec_chan_en[IPU_CHAN_ID(channel)] = false;
+ g_thrd_chan_en[IPU_CHAN_ID(channel)] = false;
+
+ switch (channel) {
+ case CSI_MEM0:
+ case CSI_MEM1:
+ case CSI_MEM2:
+ case CSI_MEM3:
+ ipu_smfc_use_count--;
+ if (g_ipu_csi_channel[0] == channel) {
+ g_ipu_csi_channel[0] = CHAN_NONE;
+ ipu_csi_use_count[0]--;
+ } else if (g_ipu_csi_channel[1] == channel) {
+ g_ipu_csi_channel[1] = CHAN_NONE;
+ ipu_csi_use_count[1]--;
+ }
+ break;
+ case CSI_PRP_ENC_MEM:
+ ipu_ic_use_count--;
+ if (using_ic_dirct_ch == CSI_PRP_ENC_MEM)
+ using_ic_dirct_ch = 0;
+ _ipu_ic_uninit_prpenc();
+ if (g_ipu_csi_channel[0] == channel) {
+ g_ipu_csi_channel[0] = CHAN_NONE;
+ ipu_csi_use_count[0]--;
+ } else if (g_ipu_csi_channel[1] == channel) {
+ g_ipu_csi_channel[1] = CHAN_NONE;
+ ipu_csi_use_count[1]--;
+ }
+ break;
+ case CSI_PRP_VF_MEM:
+ ipu_ic_use_count--;
+ if (using_ic_dirct_ch == CSI_PRP_VF_MEM)
+ using_ic_dirct_ch = 0;
+ _ipu_ic_uninit_prpvf();
+ if (g_ipu_csi_channel[0] == channel) {
+ g_ipu_csi_channel[0] = CHAN_NONE;
+ ipu_csi_use_count[0]--;
+ } else if (g_ipu_csi_channel[1] == channel) {
+ g_ipu_csi_channel[1] = CHAN_NONE;
+ ipu_csi_use_count[1]--;
+ }
+ break;
+ case MEM_PRP_VF_MEM:
+ ipu_ic_use_count--;
+ _ipu_ic_uninit_prpvf();
+ reg = __raw_readl(IPU_FS_PROC_FLOW1);
+ __raw_writel(reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+ break;
+ case MEM_VDI_PRP_VF_MEM:
+ ipu_ic_use_count--;
+ ipu_vdi_use_count--;
+ if (using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM)
+ using_ic_dirct_ch = 0;
+ _ipu_ic_uninit_prpvf();
+ _ipu_vdi_uninit();
+ reg = __raw_readl(IPU_FS_PROC_FLOW1);
+ __raw_writel(reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
+ break;
+ case MEM_ROT_VF_MEM:
+ ipu_rot_use_count--;
+ ipu_ic_use_count--;
+ _ipu_ic_uninit_rotate_vf();
+ break;
+ case MEM_PRP_ENC_MEM:
+ ipu_ic_use_count--;
+ _ipu_ic_uninit_prpenc();
+ reg = __raw_readl(IPU_FS_PROC_FLOW1);
+ __raw_writel(reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
+ break;
+ case MEM_ROT_ENC_MEM:
+ ipu_rot_use_count--;
+ ipu_ic_use_count--;
+ _ipu_ic_uninit_rotate_enc();
+ break;
+ case MEM_PP_MEM:
+ ipu_ic_use_count--;
+ _ipu_ic_uninit_pp();
+ break;
+ case MEM_ROT_PP_MEM:
+ ipu_rot_use_count--;
+ ipu_ic_use_count--;
+ _ipu_ic_uninit_rotate_pp();
+ break;
+ case MEM_DC_SYNC:
+ _ipu_dc_uninit(1);
+ ipu_di_use_count[g_dc_di_assignment[1]]--;
+ ipu_dc_use_count--;
+ ipu_dmfc_use_count--;
+ break;
+ case MEM_BG_SYNC:
+ _ipu_dp_uninit(channel);
+ _ipu_dc_uninit(5);
+ ipu_di_use_count[g_dc_di_assignment[5]]--;
+ ipu_dc_use_count--;
+ ipu_dp_use_count--;
+ ipu_dmfc_use_count--;
+ break;
+ case MEM_FG_SYNC:
+ _ipu_dp_uninit(channel);
+ ipu_dc_use_count--;
+ ipu_dp_use_count--;
+ ipu_dmfc_use_count--;
+ break;
+ case DIRECT_ASYNC0:
+ _ipu_dc_uninit(8);
+ ipu_di_use_count[g_dc_di_assignment[8]]--;
+ ipu_dc_use_count--;
+ break;
+ case DIRECT_ASYNC1:
+ _ipu_dc_uninit(9);
+ ipu_di_use_count[g_dc_di_assignment[9]]--;
+ ipu_dc_use_count--;
+ break;
+ default:
+ break;
+ }
+
+ g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel));
+
+ ipu_conf = __raw_readl(IPU_CONF);
+
+ if (ipu_ic_use_count == 0)
+ ipu_conf &= ~IPU_CONF_IC_EN;
+ if (ipu_vdi_use_count == 0) {
+ ipu_conf &= ~IPU_CONF_VDI_EN;
+ ipu_conf &= ~IPU_CONF_IC_INPUT;
+ }
+ if (ipu_rot_use_count == 0)
+ ipu_conf &= ~IPU_CONF_ROT_EN;
+ if (ipu_dc_use_count == 0)
+ ipu_conf &= ~IPU_CONF_DC_EN;
+ if (ipu_dp_use_count == 0)
+ ipu_conf &= ~IPU_CONF_DP_EN;
+ if (ipu_dmfc_use_count == 0)
+ ipu_conf &= ~IPU_CONF_DMFC_EN;
+ if (ipu_di_use_count[0] == 0) {
+ ipu_conf &= ~IPU_CONF_DI0_EN;
+ clk_disable(g_di_clk[0]);
+ }
+ if (ipu_di_use_count[1] == 0) {
+ ipu_conf &= ~IPU_CONF_DI1_EN;
+ clk_disable(g_di_clk[1]);
+ }
+ if (ipu_smfc_use_count == 0)
+ ipu_conf &= ~IPU_CONF_SMFC_EN;
+ if (ipu_csi_use_count[0] == 0)
+ ipu_conf &= ~IPU_CONF_CSI0_EN;
+ if (ipu_csi_use_count[1] == 0)
+ ipu_conf &= ~IPU_CONF_CSI1_EN;
+
+ __raw_writel(ipu_conf, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ if (ipu_conf == 0) {
+ clk_disable(g_ipu_clk);
+ g_ipu_clk_enabled = false;
+ }
+
+ WARN_ON(ipu_ic_use_count < 0);
+ WARN_ON(ipu_vdi_use_count < 0);
+ WARN_ON(ipu_rot_use_count < 0);
+ WARN_ON(ipu_dc_use_count < 0);
+ WARN_ON(ipu_dp_use_count < 0);
+ WARN_ON(ipu_dmfc_use_count < 0);
+ WARN_ON(ipu_smfc_use_count < 0);
+}
+EXPORT_SYMBOL(ipu_uninit_channel);
+
+/*!
+ * This function is called to initialize a buffer for logical IPU channel.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param type Input parameter which buffer to initialize.
+ *
+ * @param pixel_fmt Input parameter for pixel format of buffer.
+ * Pixel format is a FOURCC ASCII code.
+ *
+ * @param width Input parameter for width of buffer in pixels.
+ *
+ * @param height Input parameter for height of buffer in pixels.
+ *
+ * @param stride Input parameter for stride length of buffer
+ * in pixels.
+ *
+ * @param rot_mode Input parameter for rotation setting of buffer.
+ * A rotation setting other than
+ * IPU_ROTATE_VERT_FLIP
+ * should only be used for input buffers of
+ * rotation channels.
+ *
+ * @param phyaddr_0 Input parameter buffer 0 physical address.
+ *
+ * @param phyaddr_1 Input parameter buffer 1 physical address.
+ * Setting this to a value other than NULL enables
+ * double buffering mode.
+ *
+ * @param u private u offset for additional cropping,
+ * zero if not used.
+ *
+ * @param v private v offset for additional cropping,
+ * zero if not used.
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t pixel_fmt,
+ uint16_t width, uint16_t height,
+ uint32_t stride,
+ ipu_rotate_mode_t rot_mode,
+ dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+ uint32_t u, uint32_t v)
+{
+ unsigned long lock_flags;
+ uint32_t reg;
+ uint32_t dma_chan;
+ uint32_t burst_size;
+
+ dma_chan = channel_2_dma(channel, type);
+ if (!idma_is_valid(dma_chan))
+ return -EINVAL;
+
+ if (stride < width * bytes_per_pixel(pixel_fmt))
+ stride = width * bytes_per_pixel(pixel_fmt);
+
+ if (stride % 4) {
+ dev_err(g_ipu_dev,
+ "Stride not 32-bit aligned, stride = %d\n", stride);
+ return -EINVAL;
+ }
+ /* IC & IRT channels' width must be multiple of 8 pixels */
+ if ((_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan))
+ && (width % 8)) {
+ dev_err(g_ipu_dev, "Width must be 8 pixel multiple\n");
+ return -EINVAL;
+ }
+
+ /* Build parameter memory data for DMA channel */
+ _ipu_ch_param_init(dma_chan, pixel_fmt, width, height, stride, u, v, 0,
+ phyaddr_0, phyaddr_1);
+
+ /* Set correlative channel parameter of local alpha channel */
+ if ((_ipu_is_ic_graphic_chan(dma_chan) ||
+ _ipu_is_dp_graphic_chan(dma_chan)) &&
+ (g_thrd_chan_en[IPU_CHAN_ID(channel)] == true)) {
+ _ipu_ch_param_set_alpha_use_separate_channel(dma_chan, true);
+ _ipu_ch_param_set_alpha_buffer_memory(dma_chan);
+ _ipu_ch_param_set_alpha_condition_read(dma_chan);
+ /* fix alpha width as 8 and burst size as 16*/
+ _ipu_ch_params_set_alpha_width(dma_chan, 8);
+ _ipu_ch_param_set_burst_size(dma_chan, 16);
+ } else if (_ipu_is_ic_graphic_chan(dma_chan) &&
+ ipu_pixel_format_has_alpha(pixel_fmt))
+ _ipu_ch_param_set_alpha_use_separate_channel(dma_chan, false);
+
+ if (rot_mode)
+ _ipu_ch_param_set_rotation(dma_chan, rot_mode);
+
+ /* IC and ROT channels have restriction of 8 or 16 pix burst length */
+ if (_ipu_is_ic_chan(dma_chan)) {
+ if ((width % 16) == 0)
+ _ipu_ch_param_set_burst_size(dma_chan, 16);
+ else
+ _ipu_ch_param_set_burst_size(dma_chan, 8);
+ } else if (_ipu_is_irt_chan(dma_chan)) {
+ _ipu_ch_param_set_burst_size(dma_chan, 8);
+ _ipu_ch_param_set_block_mode(dma_chan);
+ } else if (_ipu_is_dmfc_chan(dma_chan))
+ _ipu_dmfc_set_wait4eot(dma_chan, width);
+
+ if (_ipu_chan_is_interlaced(channel)) {
+ _ipu_ch_param_set_interlaced_scan(dma_chan);
+ }
+
+ if (_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan)) {
+ burst_size = _ipu_ch_param_get_burst_size(dma_chan);
+ _ipu_ic_idma_init(dma_chan, width, height, burst_size,
+ rot_mode);
+ } else if (_ipu_is_smfc_chan(dma_chan)) {
+ burst_size = _ipu_ch_param_get_burst_size(dma_chan);
+ if ((pixel_fmt == IPU_PIX_FMT_GENERIC) &&
+ ((_ipu_ch_param_get_bpp(dma_chan) == 5) ||
+ (_ipu_ch_param_get_bpp(dma_chan) == 3)))
+ burst_size = burst_size >> 4;
+ else
+ burst_size = burst_size >> 2;
+ _ipu_smfc_set_burst_size(channel, burst_size-1);
+ }
+
+ if (idma_is_set(IDMAC_CHA_PRI, dma_chan))
+ _ipu_ch_param_set_high_priority(dma_chan);
+
+ _ipu_ch_param_dump(dma_chan);
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(IPU_CHA_DB_MODE_SEL(dma_chan));
+ if (phyaddr_1)
+ reg |= idma_mask(dma_chan);
+ else
+ reg &= ~idma_mask(dma_chan);
+ __raw_writel(reg, IPU_CHA_DB_MODE_SEL(dma_chan));
+
+ /* Reset to buffer 0 */
+ __raw_writel(idma_mask(dma_chan), IPU_CHA_CUR_BUF(dma_chan));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_init_channel_buffer);
+
+/*!
+ * This function is called to update the physical address of a buffer for
+ * a logical IPU channel.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param type Input parameter which buffer to initialize.
+ *
+ * @param bufNum Input parameter for buffer number to update.
+ * 0 or 1 are the only valid values.
+ *
+ * @param phyaddr Input parameter buffer physical address.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail. This function will fail if the buffer is set to ready.
+ */
+int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t bufNum, dma_addr_t phyaddr)
+{
+ uint32_t reg;
+ int ret = 0;
+ unsigned long lock_flags;
+ uint32_t dma_chan = channel_2_dma(channel, type);
+ if (dma_chan == IDMA_CHAN_INVALID)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if (bufNum == 0)
+ reg = __raw_readl(IPU_CHA_BUF0_RDY(dma_chan));
+ else
+ reg = __raw_readl(IPU_CHA_BUF1_RDY(dma_chan));
+
+ if ((reg & idma_mask(dma_chan)) == 0)
+ _ipu_ch_param_set_buffer(dma_chan, bufNum, phyaddr);
+ else
+ ret = -EACCES;
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return ret;
+}
+EXPORT_SYMBOL(ipu_update_channel_buffer);
+
+
+/*!
+ * This function is called to initialize a buffer for logical IPU channel.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param type Input parameter which buffer to initialize.
+ *
+ * @param pixel_fmt Input parameter for pixel format of buffer.
+ * Pixel format is a FOURCC ASCII code.
+ *
+ * @param width Input parameter for width of buffer in pixels.
+ *
+ * @param height Input parameter for height of buffer in pixels.
+ *
+ * @param stride Input parameter for stride length of buffer
+ * in pixels.
+ *
+ * @param u predefined private u offset for additional cropping,
+ * zero if not used.
+ *
+ * @param v predefined private v offset for additional cropping,
+ * zero if not used.
+ *
+ * @param vertical_offset vertical offset for Y coordinate
+ * in the existed frame
+ *
+ *
+ * @param horizontal_offset horizontal offset for X coordinate
+ * in the existed frame
+ *
+ *
+ * @return Returns 0 on success or negative error code on fail
+ * This function will fail if any buffer is set to ready.
+ */
+
+int32_t ipu_update_channel_offset(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t pixel_fmt,
+ uint16_t width, uint16_t height,
+ uint32_t stride,
+ uint32_t u, uint32_t v,
+ uint32_t vertical_offset, uint32_t horizontal_offset)
+{
+ uint32_t reg;
+ int ret = 0;
+ unsigned long lock_flags;
+ uint32_t dma_chan = channel_2_dma(channel, type);
+
+ if (dma_chan == IDMA_CHAN_INVALID)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if ((__raw_readl(IPU_CHA_BUF0_RDY(dma_chan)) & idma_mask(dma_chan)) ||
+ (__raw_readl(IPU_CHA_BUF0_RDY(dma_chan)) & idma_mask(dma_chan)))
+ ret = -EACCES;
+ else
+ _ipu_ch_offset_update(dma_chan,
+ pixel_fmt,
+ width,
+ height,
+ stride,
+ u,
+ v,
+ 0,
+ vertical_offset,
+ horizontal_offset);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return ret;
+}
+EXPORT_SYMBOL(ipu_update_channel_offset);
+
+
+/*!
+ * This function is called to set a channel's buffer as ready.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param type Input parameter which buffer to initialize.
+ *
+ * @param bufNum Input parameter for which buffer number set to
+ * ready state.
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_select_buffer(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t bufNum)
+{
+ uint32_t dma_chan = channel_2_dma(channel, type);
+
+ if (dma_chan == IDMA_CHAN_INVALID)
+ return -EINVAL;
+
+ if (bufNum == 0) {
+ /*Mark buffer 0 as ready. */
+ __raw_writel(idma_mask(dma_chan), IPU_CHA_BUF0_RDY(dma_chan));
+ } else {
+ /*Mark buffer 1 as ready. */
+ __raw_writel(idma_mask(dma_chan), IPU_CHA_BUF1_RDY(dma_chan));
+ }
+ if (channel == MEM_VDI_PRP_VF_MEM)
+ _ipu_vdi_toggle_top_field_man();
+ return 0;
+}
+EXPORT_SYMBOL(ipu_select_buffer);
+
+/*!
+ * This function is called to set a channel's buffer as ready.
+ *
+ * @param bufNum Input parameter for which buffer number set to
+ * ready state.
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_select_multi_vdi_buffer(uint32_t bufNum)
+{
+
+ uint32_t dma_chan = channel_2_dma(MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER);
+ uint32_t mask_bit =
+ idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_P, IPU_INPUT_BUFFER))|
+ idma_mask(dma_chan)|
+ idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_N, IPU_INPUT_BUFFER));
+
+ if (bufNum == 0) {
+ /*Mark buffer 0 as ready. */
+ __raw_writel(mask_bit, IPU_CHA_BUF0_RDY(dma_chan));
+ } else {
+ /*Mark buffer 1 as ready. */
+ __raw_writel(mask_bit, IPU_CHA_BUF1_RDY(dma_chan));
+ }
+ _ipu_vdi_toggle_top_field_man();
+ return 0;
+}
+EXPORT_SYMBOL(ipu_select_multi_vdi_buffer);
+
+#define NA -1
+static int proc_dest_sel[] =
+ { 0, 1, 1, 3, 5, 5, 4, 7, 8, 9, 10, 11, 12, 14, 15, 16,
+ 0, 1, 1, 5, 5, 5, 5, 5, 7, 8, 9, 10, 11, 12, 14, 31 };
+static int proc_src_sel[] = { 0, 6, 7, 6, 7, 8, 5, NA, NA, NA,
+ NA, NA, NA, NA, NA, 1, 2, 3, 4, 7, 8, NA, NA, NA };
+static int disp_src_sel[] = { 0, 6, 7, 8, 3, 4, 5, NA, NA, NA,
+ NA, NA, NA, NA, NA, 1, NA, 2, NA, 3, 4, 4, 4, 4 };
+
+
+/*!
+ * This function links 2 channels together for automatic frame
+ * synchronization. The output of the source channel is linked to the input of
+ * the destination channel.
+ *
+ * @param src_ch Input parameter for the logical channel ID of
+ * the source channel.
+ *
+ * @param dest_ch Input parameter for the logical channel ID of
+ * the destination channel.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int32_t ipu_link_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch)
+{
+ int retval = 0;
+ unsigned long lock_flags;
+ uint32_t fs_proc_flow1;
+ uint32_t fs_proc_flow2;
+ uint32_t fs_proc_flow3;
+ uint32_t fs_disp_flow1;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ fs_proc_flow1 = __raw_readl(IPU_FS_PROC_FLOW1);
+ fs_proc_flow2 = __raw_readl(IPU_FS_PROC_FLOW2);
+ fs_proc_flow3 = __raw_readl(IPU_FS_PROC_FLOW3);
+ fs_disp_flow1 = __raw_readl(IPU_FS_DISP_FLOW1);
+
+ switch (src_ch) {
+ case CSI_MEM0:
+ fs_proc_flow3 &= ~FS_SMFC0_DEST_SEL_MASK;
+ fs_proc_flow3 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_SMFC0_DEST_SEL_OFFSET;
+ break;
+ case CSI_MEM1:
+ fs_proc_flow3 &= ~FS_SMFC1_DEST_SEL_MASK;
+ fs_proc_flow3 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_SMFC1_DEST_SEL_OFFSET;
+ break;
+ case CSI_MEM2:
+ fs_proc_flow3 &= ~FS_SMFC2_DEST_SEL_MASK;
+ fs_proc_flow3 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_SMFC2_DEST_SEL_OFFSET;
+ break;
+ case CSI_MEM3:
+ fs_proc_flow3 &= ~FS_SMFC3_DEST_SEL_MASK;
+ fs_proc_flow3 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_SMFC3_DEST_SEL_OFFSET;
+ break;
+ case CSI_PRP_ENC_MEM:
+ fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+ fs_proc_flow2 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_PRPENC_DEST_SEL_OFFSET;
+ break;
+ case CSI_PRP_VF_MEM:
+ fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+ fs_proc_flow2 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_PRPVF_DEST_SEL_OFFSET;
+ break;
+ case MEM_PP_MEM:
+ fs_proc_flow2 &= ~FS_PP_DEST_SEL_MASK;
+ fs_proc_flow2 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_PP_DEST_SEL_OFFSET;
+ break;
+ case MEM_ROT_PP_MEM:
+ fs_proc_flow2 &= ~FS_PP_ROT_DEST_SEL_MASK;
+ fs_proc_flow2 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_PP_ROT_DEST_SEL_OFFSET;
+ break;
+ case MEM_PRP_ENC_MEM:
+ fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+ fs_proc_flow2 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_PRPENC_DEST_SEL_OFFSET;
+ break;
+ case MEM_ROT_ENC_MEM:
+ fs_proc_flow2 &= ~FS_PRPENC_ROT_DEST_SEL_MASK;
+ fs_proc_flow2 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_PRPENC_ROT_DEST_SEL_OFFSET;
+ break;
+ case MEM_PRP_VF_MEM:
+ fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+ fs_proc_flow2 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_PRPVF_DEST_SEL_OFFSET;
+ break;
+ case MEM_VDI_PRP_VF_MEM:
+ fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+ fs_proc_flow2 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_PRPVF_DEST_SEL_OFFSET;
+ break;
+ case MEM_ROT_VF_MEM:
+ fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK;
+ fs_proc_flow2 |=
+ proc_dest_sel[IPU_CHAN_ID(dest_ch)] <<
+ FS_PRPVF_ROT_DEST_SEL_OFFSET;
+ break;
+ default:
+ retval = -EINVAL;
+ goto err;
+ }
+
+ switch (dest_ch) {
+ case MEM_PP_MEM:
+ fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK;
+ fs_proc_flow1 |=
+ proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PP_SRC_SEL_OFFSET;
+ break;
+ case MEM_ROT_PP_MEM:
+ fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK;
+ fs_proc_flow1 |=
+ proc_src_sel[IPU_CHAN_ID(src_ch)] <<
+ FS_PP_ROT_SRC_SEL_OFFSET;
+ break;
+ case MEM_PRP_ENC_MEM:
+ fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+ fs_proc_flow1 |=
+ proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET;
+ break;
+ case MEM_ROT_ENC_MEM:
+ fs_proc_flow1 &= ~FS_PRPENC_ROT_SRC_SEL_MASK;
+ fs_proc_flow1 |=
+ proc_src_sel[IPU_CHAN_ID(src_ch)] <<
+ FS_PRPENC_ROT_SRC_SEL_OFFSET;
+ break;
+ case MEM_PRP_VF_MEM:
+ fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+ fs_proc_flow1 |=
+ proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET;
+ break;
+ case MEM_VDI_PRP_VF_MEM:
+ fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+ fs_proc_flow1 |=
+ proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET;
+ break;
+ case MEM_ROT_VF_MEM:
+ fs_proc_flow1 &= ~FS_PRPVF_ROT_SRC_SEL_MASK;
+ fs_proc_flow1 |=
+ proc_src_sel[IPU_CHAN_ID(src_ch)] <<
+ FS_PRPVF_ROT_SRC_SEL_OFFSET;
+ break;
+ case MEM_DC_SYNC:
+ fs_disp_flow1 &= ~FS_DC1_SRC_SEL_MASK;
+ fs_disp_flow1 |=
+ disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DC1_SRC_SEL_OFFSET;
+ break;
+ case MEM_BG_SYNC:
+ fs_disp_flow1 &= ~FS_DP_SYNC0_SRC_SEL_MASK;
+ fs_disp_flow1 |=
+ disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+ FS_DP_SYNC0_SRC_SEL_OFFSET;
+ break;
+ case MEM_FG_SYNC:
+ fs_disp_flow1 &= ~FS_DP_SYNC1_SRC_SEL_MASK;
+ fs_disp_flow1 |=
+ disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+ FS_DP_SYNC1_SRC_SEL_OFFSET;
+ break;
+ case MEM_DC_ASYNC:
+ fs_disp_flow1 &= ~FS_DC2_SRC_SEL_MASK;
+ fs_disp_flow1 |=
+ disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DC2_SRC_SEL_OFFSET;
+ break;
+ case MEM_BG_ASYNC0:
+ fs_disp_flow1 &= ~FS_DP_ASYNC0_SRC_SEL_MASK;
+ fs_disp_flow1 |=
+ disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+ FS_DP_ASYNC0_SRC_SEL_OFFSET;
+ break;
+ case MEM_FG_ASYNC0:
+ fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK;
+ fs_disp_flow1 |=
+ disp_src_sel[IPU_CHAN_ID(src_ch)] <<
+ FS_DP_ASYNC1_SRC_SEL_OFFSET;
+ break;
+ default:
+ retval = -EINVAL;
+ goto err;
+ }
+
+ __raw_writel(fs_proc_flow1, IPU_FS_PROC_FLOW1);
+ __raw_writel(fs_proc_flow2, IPU_FS_PROC_FLOW2);
+ __raw_writel(fs_proc_flow3, IPU_FS_PROC_FLOW3);
+ __raw_writel(fs_disp_flow1, IPU_FS_DISP_FLOW1);
+
+err:
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return retval;
+}
+EXPORT_SYMBOL(ipu_link_channels);
+
+/*!
+ * This function unlinks 2 channels and disables automatic frame
+ * synchronization.
+ *
+ * @param src_ch Input parameter for the logical channel ID of
+ * the source channel.
+ *
+ * @param dest_ch Input parameter for the logical channel ID of
+ * the destination channel.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int32_t ipu_unlink_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch)
+{
+ int retval = 0;
+ unsigned long lock_flags;
+ uint32_t fs_proc_flow1;
+ uint32_t fs_proc_flow2;
+ uint32_t fs_proc_flow3;
+ uint32_t fs_disp_flow1;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ fs_proc_flow1 = __raw_readl(IPU_FS_PROC_FLOW1);
+ fs_proc_flow2 = __raw_readl(IPU_FS_PROC_FLOW2);
+ fs_proc_flow3 = __raw_readl(IPU_FS_PROC_FLOW3);
+ fs_disp_flow1 = __raw_readl(IPU_FS_DISP_FLOW1);
+
+ switch (src_ch) {
+ case CSI_MEM0:
+ fs_proc_flow3 &= ~FS_SMFC0_DEST_SEL_MASK;
+ break;
+ case CSI_MEM1:
+ fs_proc_flow3 &= ~FS_SMFC1_DEST_SEL_MASK;
+ break;
+ case CSI_MEM2:
+ fs_proc_flow3 &= ~FS_SMFC2_DEST_SEL_MASK;
+ break;
+ case CSI_MEM3:
+ fs_proc_flow3 &= ~FS_SMFC3_DEST_SEL_MASK;
+ break;
+ case CSI_PRP_ENC_MEM:
+ fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+ break;
+ case CSI_PRP_VF_MEM:
+ fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+ break;
+ case MEM_PP_MEM:
+ fs_proc_flow2 &= ~FS_PP_DEST_SEL_MASK;
+ break;
+ case MEM_ROT_PP_MEM:
+ fs_proc_flow2 &= ~FS_PP_ROT_DEST_SEL_MASK;
+ break;
+ case MEM_PRP_ENC_MEM:
+ fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK;
+ break;
+ case MEM_ROT_ENC_MEM:
+ fs_proc_flow2 &= ~FS_PRPENC_ROT_DEST_SEL_MASK;
+ break;
+ case MEM_PRP_VF_MEM:
+ fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+ break;
+ case MEM_VDI_PRP_VF_MEM:
+ fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK;
+ break;
+ case MEM_ROT_VF_MEM:
+ fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK;
+ break;
+ default:
+ retval = -EINVAL;
+ goto err;
+ }
+
+ switch (dest_ch) {
+ case MEM_PP_MEM:
+ fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK;
+ break;
+ case MEM_ROT_PP_MEM:
+ fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK;
+ break;
+ case MEM_PRP_ENC_MEM:
+ fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+ break;
+ case MEM_ROT_ENC_MEM:
+ fs_proc_flow1 &= ~FS_PRPENC_ROT_SRC_SEL_MASK;
+ break;
+ case MEM_PRP_VF_MEM:
+ fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+ break;
+ case MEM_VDI_PRP_VF_MEM:
+ fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK;
+ break;
+ case MEM_ROT_VF_MEM:
+ fs_proc_flow1 &= ~FS_PRPVF_ROT_SRC_SEL_MASK;
+ break;
+ case MEM_DC_SYNC:
+ fs_disp_flow1 &= ~FS_DC1_SRC_SEL_MASK;
+ break;
+ case MEM_BG_SYNC:
+ fs_disp_flow1 &= ~FS_DP_SYNC0_SRC_SEL_MASK;
+ break;
+ case MEM_FG_SYNC:
+ fs_disp_flow1 &= ~FS_DP_SYNC1_SRC_SEL_MASK;
+ break;
+ case MEM_DC_ASYNC:
+ fs_disp_flow1 &= ~FS_DC2_SRC_SEL_MASK;
+ break;
+ case MEM_BG_ASYNC0:
+ fs_disp_flow1 &= ~FS_DP_ASYNC0_SRC_SEL_MASK;
+ break;
+ case MEM_FG_ASYNC0:
+ fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK;
+ break;
+ default:
+ retval = -EINVAL;
+ goto err;
+ }
+
+ __raw_writel(fs_proc_flow1, IPU_FS_PROC_FLOW1);
+ __raw_writel(fs_proc_flow2, IPU_FS_PROC_FLOW2);
+ __raw_writel(fs_proc_flow3, IPU_FS_PROC_FLOW3);
+ __raw_writel(fs_disp_flow1, IPU_FS_DISP_FLOW1);
+
+err:
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return retval;
+}
+EXPORT_SYMBOL(ipu_unlink_channels);
+
+/*!
+ * This function check whether a logical channel was enabled.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @return This function returns 1 while request channel is enabled or
+ * 0 for not enabled.
+ */
+int32_t ipu_is_channel_busy(ipu_channel_t channel)
+{
+ uint32_t reg;
+ uint32_t in_dma;
+ uint32_t out_dma;
+
+ out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+ in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+
+ reg = __raw_readl(IDMAC_CHA_EN(in_dma));
+ if (reg & idma_mask(in_dma))
+ return 1;
+ reg = __raw_readl(IDMAC_CHA_EN(out_dma));
+ if (reg & idma_mask(out_dma))
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(ipu_is_channel_busy);
+
+/*!
+ * This function enables a logical channel.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int32_t ipu_enable_channel(ipu_channel_t channel)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+ uint32_t in_dma;
+ uint32_t out_dma;
+ uint32_t sec_dma;
+ uint32_t thrd_dma;
+
+ if (g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) {
+ dev_err(g_ipu_dev, "Warning: channel already enabled %d\n",
+ IPU_CHAN_ID(channel));
+ }
+
+ /* Get input and output dma channels */
+ out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+ in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if (idma_is_valid(in_dma)) {
+ reg = __raw_readl(IDMAC_CHA_EN(in_dma));
+ __raw_writel(reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
+ }
+ if (idma_is_valid(out_dma)) {
+ reg = __raw_readl(IDMAC_CHA_EN(out_dma));
+ __raw_writel(reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
+ }
+
+ if ((g_sec_chan_en[IPU_CHAN_ID(channel)]) &&
+ ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM) ||
+ (channel == MEM_VDI_PRP_VF_MEM))) {
+ sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+ reg = __raw_readl(IDMAC_CHA_EN(sec_dma));
+ __raw_writel(reg | idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
+ }
+ if ((g_thrd_chan_en[IPU_CHAN_ID(channel)]) &&
+ ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM))) {
+ thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
+ reg = __raw_readl(IDMAC_CHA_EN(thrd_dma));
+ __raw_writel(reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
+
+ sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+ reg = __raw_readl(IDMAC_SEP_ALPHA);
+ __raw_writel(reg | idma_mask(sec_dma), IDMAC_SEP_ALPHA);
+ } else if ((g_thrd_chan_en[IPU_CHAN_ID(channel)]) &&
+ ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))) {
+ thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
+ reg = __raw_readl(IDMAC_CHA_EN(thrd_dma));
+ __raw_writel(reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
+ reg = __raw_readl(IDMAC_SEP_ALPHA);
+ __raw_writel(reg | idma_mask(in_dma), IDMAC_SEP_ALPHA);
+ }
+
+ if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) ||
+ (channel == MEM_FG_SYNC))
+ _ipu_dp_dc_enable(channel);
+
+ if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) ||
+ _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma))
+ _ipu_ic_enable_task(channel);
+
+ g_channel_enable_mask |= 1L << IPU_CHAN_ID(channel);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_enable_channel);
+
+static irqreturn_t disable_chan_irq_handler(int irq, void *dev_id)
+{
+ struct completion *comp = dev_id;
+
+ complete(comp);
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function disables a logical channel.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param wait_for_stop Flag to set whether to wait for channel end
+ * of frame or return immediately.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int32_t ipu_disable_channel(ipu_channel_t channel, bool wait_for_stop)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+ uint32_t in_dma;
+ uint32_t out_dma;
+ uint32_t sec_dma = NO_DMA;
+ uint32_t thrd_dma = NO_DMA;
+
+ if ((g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
+ dev_err(g_ipu_dev, "Channel already disabled %d\n",
+ IPU_CHAN_ID(channel));
+ return 0;
+ }
+
+ /* Get input and output dma channels */
+ out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
+ in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER);
+
+ if ((idma_is_valid(in_dma) &&
+ !idma_is_set(IDMAC_CHA_EN, in_dma))
+ && (idma_is_valid(out_dma) &&
+ !idma_is_set(IDMAC_CHA_EN, out_dma)))
+ return -EINVAL;
+
+ if (g_sec_chan_en[IPU_CHAN_ID(channel)])
+ sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+ if (g_thrd_chan_en[IPU_CHAN_ID(channel)]) {
+ sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
+ thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER);
+ }
+
+ if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) ||
+ (channel == MEM_DC_SYNC)) {
+ _ipu_dp_dc_disable(channel, false);
+ } else if (wait_for_stop) {
+ while (idma_is_set(IDMAC_CHA_BUSY, in_dma) ||
+ idma_is_set(IDMAC_CHA_BUSY, out_dma) ||
+ (g_sec_chan_en[IPU_CHAN_ID(channel)] &&
+ idma_is_set(IDMAC_CHA_BUSY, sec_dma)) ||
+ (g_thrd_chan_en[IPU_CHAN_ID(channel)] &&
+ idma_is_set(IDMAC_CHA_BUSY, thrd_dma)) ||
+ (_ipu_channel_status(channel) == TASK_STAT_ACTIVE)) {
+ uint32_t ret, irq = out_dma;
+ DECLARE_COMPLETION_ONSTACK(disable_comp);
+
+ if (idma_is_set(IDMAC_CHA_BUSY, out_dma))
+ irq = out_dma;
+ if (g_sec_chan_en[IPU_CHAN_ID(channel)] &&
+ idma_is_set(IDMAC_CHA_BUSY, sec_dma))
+ irq = sec_dma;
+ if (g_thrd_chan_en[IPU_CHAN_ID(channel)] &&
+ idma_is_set(IDMAC_CHA_BUSY, thrd_dma))
+ irq = thrd_dma;
+ if (idma_is_set(IDMAC_CHA_BUSY, in_dma))
+ irq = in_dma;
+
+ ret = ipu_request_irq(irq, disable_chan_irq_handler, 0, NULL, &disable_comp);
+ if (ret < 0) {
+ dev_err(g_ipu_dev, "irq %d in use\n", irq);
+ } else {
+ ret = wait_for_completion_timeout(&disable_comp, msecs_to_jiffies(50));
+ ipu_free_irq(irq, &disable_comp);
+ if (ret == msecs_to_jiffies(50))
+ ipu_dump_registers();
+ }
+ }
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ /* Disable IC task */
+ if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) ||
+ _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma))
+ _ipu_ic_disable_task(channel);
+
+ /* Disable DMA channel(s) */
+ if (idma_is_valid(in_dma)) {
+ reg = __raw_readl(IDMAC_CHA_EN(in_dma));
+ __raw_writel(reg & ~idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
+ __raw_writel(idma_mask(in_dma), IPU_CHA_CUR_BUF(in_dma));
+ }
+ if (idma_is_valid(out_dma)) {
+ reg = __raw_readl(IDMAC_CHA_EN(out_dma));
+ __raw_writel(reg & ~idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
+ __raw_writel(idma_mask(out_dma), IPU_CHA_CUR_BUF(out_dma));
+ }
+ if (g_sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) {
+ reg = __raw_readl(IDMAC_CHA_EN(sec_dma));
+ __raw_writel(reg & ~idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
+ __raw_writel(idma_mask(sec_dma), IPU_CHA_CUR_BUF(sec_dma));
+ }
+ if (g_thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) {
+ reg = __raw_readl(IDMAC_CHA_EN(thrd_dma));
+ __raw_writel(reg & ~idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
+ if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) {
+ reg = __raw_readl(IDMAC_SEP_ALPHA);
+ __raw_writel(reg & ~idma_mask(in_dma), IDMAC_SEP_ALPHA);
+ } else {
+ reg = __raw_readl(IDMAC_SEP_ALPHA);
+ __raw_writel(reg & ~idma_mask(sec_dma), IDMAC_SEP_ALPHA);
+ }
+ __raw_writel(idma_mask(thrd_dma), IPU_CHA_CUR_BUF(thrd_dma));
+ }
+
+ /* Set channel buffers NOT to be ready */
+ __raw_writel(0xF0000000, IPU_GPR); /* write one to clear */
+ if (idma_is_valid(in_dma)) {
+ if (idma_is_set(IPU_CHA_BUF0_RDY, in_dma)) {
+ __raw_writel(idma_mask(in_dma),
+ IPU_CHA_BUF0_RDY(in_dma));
+ }
+ if (idma_is_set(IPU_CHA_BUF1_RDY, in_dma)) {
+ __raw_writel(idma_mask(in_dma),
+ IPU_CHA_BUF1_RDY(in_dma));
+ }
+ }
+ if (idma_is_valid(out_dma)) {
+ if (idma_is_set(IPU_CHA_BUF0_RDY, out_dma)) {
+ __raw_writel(idma_mask(out_dma),
+ IPU_CHA_BUF0_RDY(out_dma));
+ }
+ if (idma_is_set(IPU_CHA_BUF1_RDY, out_dma)) {
+ __raw_writel(idma_mask(out_dma),
+ IPU_CHA_BUF1_RDY(out_dma));
+ }
+ }
+ if (g_sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) {
+ if (idma_is_set(IPU_CHA_BUF0_RDY, sec_dma)) {
+ __raw_writel(idma_mask(sec_dma),
+ IPU_CHA_BUF0_RDY(sec_dma));
+ }
+ if (idma_is_set(IPU_CHA_BUF1_RDY, sec_dma)) {
+ __raw_writel(idma_mask(sec_dma),
+ IPU_CHA_BUF1_RDY(sec_dma));
+ }
+ }
+ if (g_thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) {
+ if (idma_is_set(IPU_CHA_BUF0_RDY, thrd_dma)) {
+ __raw_writel(idma_mask(thrd_dma),
+ IPU_CHA_BUF0_RDY(thrd_dma));
+ }
+ if (idma_is_set(IPU_CHA_BUF1_RDY, thrd_dma)) {
+ __raw_writel(idma_mask(thrd_dma),
+ IPU_CHA_BUF1_RDY(thrd_dma));
+ }
+ }
+ __raw_writel(0x0, IPU_GPR); /* write one to set */
+
+ g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_disable_channel);
+
+static irqreturn_t ipu_irq_handler(int irq, void *desc)
+{
+ int i;
+ uint32_t line;
+ irqreturn_t result = IRQ_NONE;
+ uint32_t int_stat;
+ const int err_reg[] = { 5, 6, 9, 10, 0 };
+ const int int_reg[] = { 1, 2, 3, 4, 11, 12, 13, 14, 15, 0 };
+
+ if (g_ipu_irq[1]) {
+ disable_irq(g_ipu_irq[0]);
+ disable_irq(g_ipu_irq[1]);
+ }
+
+ for (i = 0;; i++) {
+ if (err_reg[i] == 0)
+ break;
+ int_stat = __raw_readl(IPU_INT_STAT(err_reg[i]));
+ int_stat &= __raw_readl(IPU_INT_CTRL(err_reg[i]));
+ if (int_stat) {
+ __raw_writel(int_stat, IPU_INT_STAT(err_reg[i]));
+ dev_err(g_ipu_dev,
+ "IPU Error - IPU_INT_STAT_%d = 0x%08X\n",
+ err_reg[i], int_stat);
+ /* Disable interrupts so we only get error once */
+ int_stat =
+ __raw_readl(IPU_INT_CTRL(err_reg[i])) & ~int_stat;
+ __raw_writel(int_stat, IPU_INT_CTRL(err_reg[i]));
+ }
+ }
+
+ for (i = 0;; i++) {
+ if (int_reg[i] == 0)
+ break;
+ int_stat = __raw_readl(IPU_INT_STAT(int_reg[i]));
+ int_stat &= __raw_readl(IPU_INT_CTRL(int_reg[i]));
+ __raw_writel(int_stat, IPU_INT_STAT(int_reg[i]));
+ while ((line = ffs(int_stat)) != 0) {
+ line--;
+ int_stat &= ~(1UL << line);
+ line += (int_reg[i] - 1) * 32;
+ result |=
+ ipu_irq_list[line].handler(line,
+ ipu_irq_list[line].
+ dev_id);
+ }
+ }
+
+ if (g_ipu_irq[1]) {
+ enable_irq(g_ipu_irq[0]);
+ enable_irq(g_ipu_irq[1]);
+ }
+ return result;
+}
+
+/*!
+ * This function enables the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to enable interrupt for.
+ *
+ */
+void ipu_enable_irq(uint32_t irq)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+
+ if (!g_ipu_clk_enabled)
+ clk_enable(g_ipu_clk);
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(IPUIRQ_2_CTRLREG(irq));
+ reg |= IPUIRQ_2_MASK(irq);
+ __raw_writel(reg, IPUIRQ_2_CTRLREG(irq));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ if (!g_ipu_clk_enabled)
+ clk_disable(g_ipu_clk);
+}
+EXPORT_SYMBOL(ipu_enable_irq);
+
+/*!
+ * This function disables the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to disable interrupt for.
+ *
+ */
+void ipu_disable_irq(uint32_t irq)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+
+ if (!g_ipu_clk_enabled)
+ clk_enable(g_ipu_clk);
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(IPUIRQ_2_CTRLREG(irq));
+ reg &= ~IPUIRQ_2_MASK(irq);
+ __raw_writel(reg, IPUIRQ_2_CTRLREG(irq));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ if (!g_ipu_clk_enabled)
+ clk_disable(g_ipu_clk);
+}
+EXPORT_SYMBOL(ipu_disable_irq);
+
+/*!
+ * This function clears the interrupt for the specified interrupt line.
+ * The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to clear interrupt for.
+ *
+ */
+void ipu_clear_irq(uint32_t irq)
+{
+ if (!g_ipu_clk_enabled)
+ clk_enable(g_ipu_clk);
+
+ __raw_writel(IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq));
+
+ if (!g_ipu_clk_enabled)
+ clk_disable(g_ipu_clk);
+}
+EXPORT_SYMBOL(ipu_clear_irq);
+
+/*!
+ * This function returns the current interrupt status for the specified
+ * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to get status for.
+ *
+ * @return Returns true if the interrupt is pending/asserted or false if
+ * the interrupt is not pending.
+ */
+bool ipu_get_irq_status(uint32_t irq)
+{
+ uint32_t reg;
+
+ if (!g_ipu_clk_enabled)
+ clk_enable(g_ipu_clk);
+
+ reg = __raw_readl(IPUIRQ_2_STATREG(irq));
+
+ if (!g_ipu_clk_enabled)
+ clk_disable(g_ipu_clk);
+
+ if (reg & IPUIRQ_2_MASK(irq))
+ return true;
+ else
+ return false;
+}
+EXPORT_SYMBOL(ipu_get_irq_status);
+
+/*!
+ * This function registers an interrupt handler function for the specified
+ * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to get status for.
+ *
+ * @param handler Input parameter for address of the handler
+ * function.
+ *
+ * @param irq_flags Flags for interrupt mode. Currently not used.
+ *
+ * @param devname Input parameter for string name of driver
+ * registering the handler.
+ *
+ * @param dev_id Input parameter for pointer of data to be
+ * passed to the handler.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int ipu_request_irq(uint32_t irq,
+ irqreturn_t(*handler) (int, void *),
+ uint32_t irq_flags, const char *devname, void *dev_id)
+{
+ unsigned long lock_flags;
+
+ BUG_ON(irq >= IPU_IRQ_COUNT);
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if (ipu_irq_list[irq].handler != NULL) {
+ dev_err(g_ipu_dev,
+ "handler already installed on irq %d\n", irq);
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return -EINVAL;
+ }
+
+ ipu_irq_list[irq].handler = handler;
+ ipu_irq_list[irq].flags = irq_flags;
+ ipu_irq_list[irq].dev_id = dev_id;
+ ipu_irq_list[irq].name = devname;
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ ipu_enable_irq(irq); /* enable the interrupt */
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_request_irq);
+
+/*!
+ * This function unregisters an interrupt handler for the specified interrupt
+ * line. The interrupt lines are defined in \b ipu_irq_line enum.
+ *
+ * @param irq Interrupt line to get status for.
+ *
+ * @param dev_id Input parameter for pointer of data to be passed
+ * to the handler. This must match value passed to
+ * ipu_request_irq().
+ *
+ */
+void ipu_free_irq(uint32_t irq, void *dev_id)
+{
+ ipu_disable_irq(irq); /* disable the interrupt */
+
+ if (ipu_irq_list[irq].dev_id == dev_id)
+ ipu_irq_list[irq].handler = NULL;
+}
+EXPORT_SYMBOL(ipu_free_irq);
+
+uint32_t _ipu_channel_status(ipu_channel_t channel)
+{
+ uint32_t stat = 0;
+ uint32_t task_stat_reg = __raw_readl(IPU_PROC_TASK_STAT);
+
+ switch (channel) {
+ case MEM_PRP_VF_MEM:
+ stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET;
+ break;
+ case MEM_VDI_PRP_VF_MEM:
+ stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET;
+ break;
+ case MEM_ROT_VF_MEM:
+ stat =
+ (task_stat_reg & TSTAT_VF_ROT_MASK) >> TSTAT_VF_ROT_OFFSET;
+ break;
+ case MEM_PRP_ENC_MEM:
+ stat = (task_stat_reg & TSTAT_ENC_MASK) >> TSTAT_ENC_OFFSET;
+ break;
+ case MEM_ROT_ENC_MEM:
+ stat =
+ (task_stat_reg & TSTAT_ENC_ROT_MASK) >>
+ TSTAT_ENC_ROT_OFFSET;
+ break;
+ case MEM_PP_MEM:
+ stat = (task_stat_reg & TSTAT_PP_MASK) >> TSTAT_PP_OFFSET;
+ break;
+ case MEM_ROT_PP_MEM:
+ stat =
+ (task_stat_reg & TSTAT_PP_ROT_MASK) >> TSTAT_PP_ROT_OFFSET;
+ break;
+
+ default:
+ stat = TASK_STAT_IDLE;
+ break;
+ }
+ return stat;
+}
+
+int32_t ipu_swap_channel(ipu_channel_t from_ch, ipu_channel_t to_ch)
+{
+ uint32_t reg;
+ unsigned long lock_flags;
+
+ int from_dma = channel_2_dma(from_ch, IPU_INPUT_BUFFER);
+ int to_dma = channel_2_dma(to_ch, IPU_INPUT_BUFFER);
+
+ /* enable target channel */
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(IDMAC_CHA_EN(to_dma));
+ __raw_writel(reg | idma_mask(to_dma), IDMAC_CHA_EN(to_dma));
+
+ g_channel_enable_mask |= 1L << IPU_CHAN_ID(to_ch);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ /* switch dp dc */
+ _ipu_dp_dc_disable(from_ch, true);
+
+ /* disable source channel */
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(IDMAC_CHA_EN(from_dma));
+ __raw_writel(reg & ~idma_mask(from_dma), IDMAC_CHA_EN(from_dma));
+ __raw_writel(idma_mask(from_dma), IPU_CHA_CUR_BUF(from_dma));
+
+ g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(from_ch));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_swap_channel);
+
+uint32_t bytes_per_pixel(uint32_t fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_GENERIC: /*generic data */
+ case IPU_PIX_FMT_RGB332:
+ case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YUV422P:
+ return 1;
+ break;
+ case IPU_PIX_FMT_RGB565:
+ case IPU_PIX_FMT_YUYV:
+ case IPU_PIX_FMT_UYVY:
+ return 2;
+ break;
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ return 3;
+ break;
+ case IPU_PIX_FMT_GENERIC_32: /*generic data */
+ case IPU_PIX_FMT_BGR32:
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_RGB32:
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_ABGR32:
+ return 4;
+ break;
+ default:
+ return 1;
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(bytes_per_pixel);
+
+ipu_color_space_t format_to_colorspace(uint32_t fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_RGB666:
+ case IPU_PIX_FMT_RGB565:
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ case IPU_PIX_FMT_BGR32:
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_RGB32:
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_ABGR32:
+ case IPU_PIX_FMT_LVDS666:
+ case IPU_PIX_FMT_LVDS888:
+ return RGB;
+ break;
+
+ default:
+ return YCbCr;
+ break;
+ }
+ return RGB;
+}
+
+bool ipu_pixel_format_has_alpha(uint32_t fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_ABGR32:
+ return true;
+ break;
+ default:
+ return false;
+ break;
+ }
+ return false;
+}
+
+void ipu_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3])
+{
+ _ipu_dp_set_csc_coefficients(channel, param);
+}
+EXPORT_SYMBOL(ipu_set_csc_coefficients);
+
+static int ipu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (g_ipu_clk_enabled) {
+ /* save and disable enabled channels*/
+ idma_enable_reg[0] = __raw_readl(IDMAC_CHA_EN(0));
+ idma_enable_reg[1] = __raw_readl(IDMAC_CHA_EN(32));
+ while ((__raw_readl(IDMAC_CHA_BUSY(0)) & idma_enable_reg[0])
+ || (__raw_readl(IDMAC_CHA_BUSY(32)) &
+ idma_enable_reg[1])) {
+ /* disable channel not busy already */
+ uint32_t chan_should_disable, timeout = 1000, time = 0;
+
+ chan_should_disable =
+ __raw_readl(IDMAC_CHA_BUSY(0))
+ ^ idma_enable_reg[0];
+ __raw_writel((~chan_should_disable) &
+ idma_enable_reg[0], IDMAC_CHA_EN(0));
+ chan_should_disable =
+ __raw_readl(IDMAC_CHA_BUSY(1))
+ ^ idma_enable_reg[1];
+ __raw_writel((~chan_should_disable) &
+ idma_enable_reg[1], IDMAC_CHA_EN(32));
+ msleep(2);
+ time += 2;
+ if (time >= timeout)
+ return -1;
+ }
+ __raw_writel(0, IDMAC_CHA_EN(0));
+ __raw_writel(0, IDMAC_CHA_EN(32));
+
+ /* save double buffer select regs */
+ ipu_cha_db_mode_reg[0] = __raw_readl(IPU_CHA_DB_MODE_SEL(0));
+ ipu_cha_db_mode_reg[1] = __raw_readl(IPU_CHA_DB_MODE_SEL(32));
+ ipu_cha_db_mode_reg[2] =
+ __raw_readl(IPU_ALT_CHA_DB_MODE_SEL(0));
+ ipu_cha_db_mode_reg[3] =
+ __raw_readl(IPU_ALT_CHA_DB_MODE_SEL(32));
+
+ /* save current buffer regs */
+ ipu_cha_cur_buf_reg[0] = __raw_readl(IPU_CHA_CUR_BUF(0));
+ ipu_cha_cur_buf_reg[1] = __raw_readl(IPU_CHA_CUR_BUF(32));
+ ipu_cha_cur_buf_reg[2] = __raw_readl(IPU_ALT_CUR_BUF0);
+ ipu_cha_cur_buf_reg[3] = __raw_readl(IPU_ALT_CUR_BUF1);
+
+ /* save sub-modules status and disable all */
+ ic_conf_reg = __raw_readl(IC_CONF);
+ __raw_writel(0, IC_CONF);
+ ipu_conf_reg = __raw_readl(IPU_CONF);
+ __raw_writel(0, IPU_CONF);
+
+ /* save buf ready regs */
+ buf_ready_reg[0] = __raw_readl(IPU_CHA_BUF0_RDY(0));
+ buf_ready_reg[1] = __raw_readl(IPU_CHA_BUF0_RDY(32));
+ buf_ready_reg[2] = __raw_readl(IPU_CHA_BUF1_RDY(0));
+ buf_ready_reg[3] = __raw_readl(IPU_CHA_BUF1_RDY(32));
+ buf_ready_reg[4] = __raw_readl(IPU_ALT_CHA_BUF0_RDY(0));
+ buf_ready_reg[5] = __raw_readl(IPU_ALT_CHA_BUF0_RDY(32));
+ buf_ready_reg[6] = __raw_readl(IPU_ALT_CHA_BUF1_RDY(0));
+ buf_ready_reg[7] = __raw_readl(IPU_ALT_CHA_BUF1_RDY(32));
+ }
+
+ mxc_pg_enable(pdev);
+
+ return 0;
+}
+
+static int ipu_resume(struct platform_device *pdev)
+{
+ mxc_pg_disable(pdev);
+
+ if (g_ipu_clk_enabled) {
+
+ /* restore buf ready regs */
+ __raw_writel(buf_ready_reg[0], IPU_CHA_BUF0_RDY(0));
+ __raw_writel(buf_ready_reg[1], IPU_CHA_BUF0_RDY(32));
+ __raw_writel(buf_ready_reg[2], IPU_CHA_BUF1_RDY(0));
+ __raw_writel(buf_ready_reg[3], IPU_CHA_BUF1_RDY(32));
+ __raw_writel(buf_ready_reg[4], IPU_ALT_CHA_BUF0_RDY(0));
+ __raw_writel(buf_ready_reg[5], IPU_ALT_CHA_BUF0_RDY(32));
+ __raw_writel(buf_ready_reg[6], IPU_ALT_CHA_BUF1_RDY(0));
+ __raw_writel(buf_ready_reg[7], IPU_ALT_CHA_BUF1_RDY(32));
+
+ /* re-enable sub-modules*/
+ __raw_writel(ipu_conf_reg, IPU_CONF);
+ __raw_writel(ic_conf_reg, IC_CONF);
+
+ /* restore double buffer select regs */
+ __raw_writel(ipu_cha_db_mode_reg[0], IPU_CHA_DB_MODE_SEL(0));
+ __raw_writel(ipu_cha_db_mode_reg[1], IPU_CHA_DB_MODE_SEL(32));
+ __raw_writel(ipu_cha_db_mode_reg[2],
+ IPU_ALT_CHA_DB_MODE_SEL(0));
+ __raw_writel(ipu_cha_db_mode_reg[3],
+ IPU_ALT_CHA_DB_MODE_SEL(32));
+
+ /* restore current buffer select regs */
+ __raw_writel(~(ipu_cha_cur_buf_reg[0]), IPU_CHA_CUR_BUF(0));
+ __raw_writel(~(ipu_cha_cur_buf_reg[1]), IPU_CHA_CUR_BUF(32));
+ __raw_writel(~(ipu_cha_cur_buf_reg[2]), IPU_ALT_CUR_BUF0);
+ __raw_writel(~(ipu_cha_cur_buf_reg[3]), IPU_ALT_CUR_BUF1);
+
+ /* restart idma channel*/
+ __raw_writel(idma_enable_reg[0], IDMAC_CHA_EN(0));
+ __raw_writel(idma_enable_reg[1], IDMAC_CHA_EN(32));
+ } else {
+ clk_enable(g_ipu_clk);
+ _ipu_dmfc_init();
+ _ipu_init_dc_mappings();
+
+ /* Set sync refresh channels as high priority */
+ __raw_writel(0x18800000L, IDMAC_CHA_PRI(0));
+ clk_disable(g_ipu_clk);
+ }
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcipu_driver = {
+ .driver = {
+ .name = "mxc_ipu",
+ },
+ .probe = ipu_probe,
+ .remove = ipu_remove,
+ .suspend_late = ipu_suspend,
+ .resume_early = ipu_resume,
+};
+
+int32_t __init ipu_gen_init(void)
+{
+ int32_t ret;
+
+ ret = platform_driver_register(&mxcipu_driver);
+ return 0;
+}
+
+subsys_initcall(ipu_gen_init);
+
+static void __exit ipu_gen_uninit(void)
+{
+ platform_driver_unregister(&mxcipu_driver);
+}
+
+module_exit(ipu_gen_uninit);
diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c
new file mode 100644
index 000000000000..4c98d4c7edd3
--- /dev/null
+++ b/drivers/mxc/ipu3/ipu_device.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_device.c
+ *
+ * @brief This file contains the IPUv3 driver device interface and fops functions.
+ *
+ * @ingroup IPU
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <asm/cacheflush.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+/* Strucutures and variables for exporting MXC IPU as device*/
+
+static int mxc_ipu_major;
+static struct class *mxc_ipu_class;
+
+DEFINE_SPINLOCK(event_lock);
+
+struct ipu_dev_irq_info {
+ wait_queue_head_t waitq;
+ int irq_pending;
+} irq_info[480];
+
+int register_ipu_device(void);
+
+/* Static functions */
+
+int get_events(ipu_event_info *p)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&event_lock, flags);
+ if (irq_info[p->irq].irq_pending > 0)
+ irq_info[p->irq].irq_pending--;
+ else
+ ret = -1;
+ spin_unlock_irqrestore(&event_lock, flags);
+
+ return ret;
+}
+
+static irqreturn_t mxc_ipu_generic_handler(int irq, void *dev_id)
+{
+ irq_info[irq].irq_pending++;
+
+ /* Wakeup any blocking user context */
+ wake_up_interruptible(&(irq_info[irq].waitq));
+ return IRQ_HANDLED;
+}
+
+static int mxc_ipu_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ return ret;
+}
+static int mxc_ipu_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case IPU_INIT_CHANNEL:
+ {
+ ipu_channel_parm parm;
+
+ if (copy_from_user
+ (&parm, (ipu_channel_parm *) arg,
+ sizeof(ipu_channel_parm)))
+ return -EFAULT;
+
+ if (!parm.flag) {
+ ret =
+ ipu_init_channel(parm.channel,
+ &parm.params);
+ } else {
+ ret = ipu_init_channel(parm.channel, NULL);
+ }
+ }
+ break;
+ case IPU_UNINIT_CHANNEL:
+ {
+ ipu_channel_t ch;
+ int __user *argp = (void __user *)arg;
+ if (get_user(ch, argp))
+ return -EFAULT;
+ ipu_uninit_channel(ch);
+ }
+ break;
+ case IPU_INIT_CHANNEL_BUFFER:
+ {
+ ipu_channel_buf_parm parm;
+ if (copy_from_user
+ (&parm, (ipu_channel_buf_parm *) arg,
+ sizeof(ipu_channel_buf_parm)))
+ return -EFAULT;
+
+ ret =
+ ipu_init_channel_buffer(
+ parm.channel, parm.type,
+ parm.pixel_fmt,
+ parm.width, parm.height,
+ parm.stride,
+ parm.rot_mode,
+ parm.phyaddr_0,
+ parm.phyaddr_1,
+ parm.u_offset,
+ parm.v_offset);
+
+ }
+ break;
+ case IPU_UPDATE_CHANNEL_BUFFER:
+ {
+ ipu_channel_buf_parm parm;
+ if (copy_from_user
+ (&parm, (ipu_channel_buf_parm *) arg,
+ sizeof(ipu_channel_buf_parm)))
+ return -EFAULT;
+
+ if ((parm.phyaddr_0 != (dma_addr_t) NULL)
+ && (parm.phyaddr_1 == (dma_addr_t) NULL)) {
+ ret =
+ ipu_update_channel_buffer(
+ parm.channel,
+ parm.type,
+ parm.bufNum,
+ parm.phyaddr_0);
+ } else if ((parm.phyaddr_0 == (dma_addr_t) NULL)
+ && (parm.phyaddr_1 != (dma_addr_t) NULL)) {
+ ret =
+ ipu_update_channel_buffer(
+ parm.channel,
+ parm.type,
+ parm.bufNum,
+ parm.phyaddr_1);
+ } else {
+ ret = -1;
+ }
+
+ }
+ break;
+ case IPU_SELECT_CHANNEL_BUFFER:
+ {
+ ipu_channel_buf_parm parm;
+ if (copy_from_user
+ (&parm, (ipu_channel_buf_parm *) arg,
+ sizeof(ipu_channel_buf_parm)))
+ return -EFAULT;
+
+ ret =
+ ipu_select_buffer(parm.channel,
+ parm.type, parm.bufNum);
+
+ }
+ break;
+ case IPU_LINK_CHANNELS:
+ {
+ ipu_channel_link link;
+ if (copy_from_user
+ (&link, (ipu_channel_link *) arg,
+ sizeof(ipu_channel_link)))
+ return -EFAULT;
+
+ ret = ipu_link_channels(link.src_ch,
+ link.dest_ch);
+
+ }
+ break;
+ case IPU_UNLINK_CHANNELS:
+ {
+ ipu_channel_link link;
+ if (copy_from_user
+ (&link, (ipu_channel_link *) arg,
+ sizeof(ipu_channel_link)))
+ return -EFAULT;
+
+ ret = ipu_unlink_channels(link.src_ch,
+ link.dest_ch);
+
+ }
+ break;
+ case IPU_ENABLE_CHANNEL:
+ {
+ ipu_channel_t ch;
+ int __user *argp = (void __user *)arg;
+ if (get_user(ch, argp))
+ return -EFAULT;
+ ipu_enable_channel(ch);
+ }
+ break;
+ case IPU_DISABLE_CHANNEL:
+ {
+ ipu_channel_info info;
+ if (copy_from_user
+ (&info, (ipu_channel_info *) arg,
+ sizeof(ipu_channel_info)))
+ return -EFAULT;
+
+ ret = ipu_disable_channel(info.channel,
+ info.stop);
+ }
+ break;
+ case IPU_ENABLE_IRQ:
+ {
+ uint32_t irq;
+ int __user *argp = (void __user *)arg;
+ if (get_user(irq, argp))
+ return -EFAULT;
+ ipu_enable_irq(irq);
+ }
+ break;
+ case IPU_DISABLE_IRQ:
+ {
+ uint32_t irq;
+ int __user *argp = (void __user *)arg;
+ if (get_user(irq, argp))
+ return -EFAULT;
+ ipu_disable_irq(irq);
+ }
+ break;
+ case IPU_CLEAR_IRQ:
+ {
+ uint32_t irq;
+ int __user *argp = (void __user *)arg;
+ if (get_user(irq, argp))
+ return -EFAULT;
+ ipu_clear_irq(irq);
+ }
+ break;
+ case IPU_FREE_IRQ:
+ {
+ ipu_irq_info info;
+
+ if (copy_from_user
+ (&info, (ipu_irq_info *) arg,
+ sizeof(ipu_irq_info)))
+ return -EFAULT;
+
+ ipu_free_irq(info.irq, info.dev_id);
+ irq_info[info.irq].irq_pending = 0;
+ }
+ break;
+ case IPU_REQUEST_IRQ_STATUS:
+ {
+ uint32_t irq;
+ int __user *argp = (void __user *)arg;
+ if (get_user(irq, argp))
+ return -EFAULT;
+ ret = ipu_get_irq_status(irq);
+ }
+ break;
+ case IPU_REGISTER_GENERIC_ISR:
+ {
+ ipu_event_info info;
+ if (copy_from_user
+ (&info, (ipu_event_info *) arg,
+ sizeof(ipu_event_info)))
+ return -EFAULT;
+
+ ret =
+ ipu_request_irq(info.irq,
+ mxc_ipu_generic_handler,
+ 0, "video_sink", info.dev);
+ if (ret == 0)
+ init_waitqueue_head(&(irq_info[info.irq].waitq));
+ }
+ break;
+ case IPU_GET_EVENT:
+ /* User will have to allocate event_type
+ structure and pass the pointer in arg */
+ {
+ ipu_event_info info;
+ int r = -1;
+
+ if (copy_from_user
+ (&info, (ipu_event_info *) arg,
+ sizeof(ipu_event_info)))
+ return -EFAULT;
+
+ r = get_events(&info);
+ if (r == -1) {
+ if ((file->f_flags & O_NONBLOCK) &&
+ (irq_info[info.irq].irq_pending == 0))
+ return -EAGAIN;
+ wait_event_interruptible_timeout(irq_info[info.irq].waitq,
+ (irq_info[info.irq].irq_pending != 0), 2 * HZ);
+ r = get_events(&info);
+ }
+ ret = -1;
+ if (r == 0) {
+ if (!copy_to_user((ipu_event_info *) arg,
+ &info, sizeof(ipu_event_info)))
+ ret = 0;
+ }
+ }
+ break;
+ case IPU_ALOC_MEM:
+ {
+ ipu_mem_info info;
+ if (copy_from_user
+ (&info, (ipu_mem_info *) arg,
+ sizeof(ipu_mem_info)))
+ return -EFAULT;
+
+ info.vaddr = dma_alloc_coherent(0,
+ PAGE_ALIGN(info.size),
+ &info.paddr,
+ GFP_DMA | GFP_KERNEL);
+ if (info.vaddr == 0) {
+ printk(KERN_ERR "dma alloc failed!\n");
+ return -ENOBUFS;
+ }
+ if (copy_to_user((ipu_mem_info *) arg, &info,
+ sizeof(ipu_mem_info)) > 0)
+ return -EFAULT;
+ }
+ break;
+ case IPU_FREE_MEM:
+ {
+ ipu_mem_info info;
+ if (copy_from_user
+ (&info, (ipu_mem_info *) arg,
+ sizeof(ipu_mem_info)))
+ return -EFAULT;
+
+ if (info.vaddr)
+ dma_free_coherent(0, PAGE_ALIGN(info.size),
+ info.vaddr, info.paddr);
+ else
+ return -EFAULT;
+ }
+ break;
+ case IPU_IS_CHAN_BUSY:
+ {
+ ipu_channel_t chan;
+ if (copy_from_user
+ (&chan, (ipu_channel_t *)arg,
+ sizeof(ipu_channel_t)))
+ return -EFAULT;
+
+ if (ipu_is_channel_busy(chan))
+ ret = 1;
+ else
+ ret = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma)
+{
+// vma->vm_page_prot = pgprot_writethru(vma->vm_page_prot);
+
+ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot)) {
+ printk(KERN_ERR
+ "mmap failed!\n");
+ return -ENOBUFS;
+ }
+ return 0;
+}
+
+static int mxc_ipu_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static struct file_operations mxc_ipu_fops = {
+ .owner = THIS_MODULE,
+ .open = mxc_ipu_open,
+ .mmap = mxc_ipu_mmap,
+ .release = mxc_ipu_release,
+ .ioctl = mxc_ipu_ioctl
+};
+
+int register_ipu_device()
+{
+ int ret = 0;
+ struct device *temp;
+ mxc_ipu_major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops);
+ if (mxc_ipu_major < 0) {
+ printk(KERN_ERR
+ "Unable to register Mxc Ipu as a char device\n");
+ return mxc_ipu_major;
+ }
+
+ mxc_ipu_class = class_create(THIS_MODULE, "mxc_ipu");
+ if (IS_ERR(mxc_ipu_class)) {
+ printk(KERN_ERR "Unable to create class for Mxc Ipu\n");
+ ret = PTR_ERR(mxc_ipu_class);
+ goto err1;
+ }
+
+ temp = device_create(mxc_ipu_class, NULL, MKDEV(mxc_ipu_major, 0),
+ NULL, "mxc_ipu");
+
+ if (IS_ERR(temp)) {
+ printk(KERN_ERR "Unable to create class device for Mxc Ipu\n");
+ ret = PTR_ERR(temp);
+ goto err2;
+ }
+ spin_lock_init(&event_lock);
+
+ return ret;
+
+err2:
+ class_destroy(mxc_ipu_class);
+err1:
+ unregister_chrdev(mxc_ipu_major, "mxc_ipu");
+ return ret;
+
+}
diff --git a/drivers/mxc/ipu3/ipu_disp.c b/drivers/mxc/ipu3/ipu_disp.c
new file mode 100644
index 000000000000..79b0c2123c22
--- /dev/null
+++ b/drivers/mxc/ipu3/ipu_disp.c
@@ -0,0 +1,1515 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ipu_disp.c
+ *
+ * @brief IPU display submodule API functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <asm/atomic.h>
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+enum csc_type_t {
+ RGB2YUV = 0,
+ YUV2RGB,
+ RGB2RGB,
+ YUV2YUV,
+ CSC_NONE,
+ CSC_NUM
+};
+
+struct dp_csc_param_t {
+ int mode;
+ void *coeff;
+};
+
+#define SYNC_WAVE 0
+#define ASYNC_SER_WAVE 6
+
+/* DC display ID assignments */
+#define DC_DISP_ID_SYNC(di) (di)
+#define DC_DISP_ID_SERIAL 2
+#define DC_DISP_ID_ASYNC 3
+
+
+/* all value below is determined by fix reg setting in _ipu_dmfc_init*/
+#define DMFC_FIFO_SIZE_28 (128*4)
+#define DMFC_FIFO_SIZE_29 (64*4)
+#define DMFC_FIFO_SIZE_24 (64*4)
+#define DMFC_FIFO_SIZE_27 (128*4)
+#define DMFC_FIFO_SIZE_23 (128*4)
+
+void _ipu_dmfc_init(void)
+{
+ /* disable DMFC-IC channel*/
+ __raw_writel(0x2, DMFC_IC_CTRL);
+ /* 1 - segment 0 and 1; 2, 1C and 2C unused */
+ __raw_writel(0x00000088, DMFC_WR_CHAN);
+ __raw_writel(0x202020F6, DMFC_WR_CHAN_DEF);
+ /* 5B - segment 2 and 3; 5F - segment 4 and 5; */
+ /* 6B - segment 6; 6F - segment 7 */
+ __raw_writel(0x1F1E9694, DMFC_DP_CHAN);
+ /* Enable chan 5 watermark set at 5 bursts and clear at 7 bursts */
+ __raw_writel(0x2020F6F6, DMFC_DP_CHAN_DEF);
+}
+
+void _ipu_dmfc_set_wait4eot(int dma_chan, int width)
+{
+ u32 dmfc_gen1 = __raw_readl(DMFC_GENERAL1);
+
+ if (dma_chan == 23) { /*5B*/
+ if (DMFC_FIFO_SIZE_23/width > 3)
+ dmfc_gen1 |= 1UL << 20;
+ else
+ dmfc_gen1 &= ~(1UL << 20);
+ } else if (dma_chan == 24) { /*6B*/
+ if (DMFC_FIFO_SIZE_24/width > 1)
+ dmfc_gen1 |= 1UL << 22;
+ else
+ dmfc_gen1 &= ~(1UL << 22);
+ } else if (dma_chan == 27) { /*5F*/
+ if (DMFC_FIFO_SIZE_27/width > 2)
+ dmfc_gen1 |= 1UL << 21;
+ else
+ dmfc_gen1 &= ~(1UL << 21);
+ } else if (dma_chan == 28) { /*1*/
+ if (DMFC_FIFO_SIZE_28/width > 2)
+ dmfc_gen1 |= 1UL << 16;
+ else
+ dmfc_gen1 &= ~(1UL << 16);
+ } else if (dma_chan == 29) { /*6F*/
+ if (DMFC_FIFO_SIZE_29/width > 1)
+ dmfc_gen1 |= 1UL << 23;
+ else
+ dmfc_gen1 &= ~(1UL << 23);
+ }
+
+ __raw_writel(dmfc_gen1, DMFC_GENERAL1);
+}
+
+static void _ipu_di_data_wave_config(int di,
+ int wave_gen,
+ int access_size, int component_size)
+{
+ u32 reg;
+ reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) |
+ (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET);
+ __raw_writel(reg, DI_DW_GEN(di, wave_gen));
+}
+
+static void _ipu_di_data_pin_config(int di, int wave_gen, int di_pin, int set,
+ int up, int down)
+{
+ u32 reg;
+
+ reg = __raw_readl(DI_DW_GEN(di, wave_gen));
+ reg &= ~(0x3 << (di_pin * 2));
+ reg |= set << (di_pin * 2);
+ __raw_writel(reg, DI_DW_GEN(di, wave_gen));
+
+ __raw_writel((down << 16) | up, DI_DW_SET(di, wave_gen, set));
+}
+
+static void _ipu_di_sync_config(int di, int wave_gen,
+ int run_count, int run_src,
+ int offset_count, int offset_src,
+ int repeat_count, int cnt_clr_src,
+ int cnt_polarity_gen_en,
+ int cnt_polarity_clr_src,
+ int cnt_polarity_trigger_src,
+ int cnt_up, int cnt_down)
+{
+ u32 reg;
+
+ if ((run_count >= 0x1000) || (offset_count >= 0x1000) || (repeat_count >= 0x1000) ||
+ (cnt_up >= 0x400) || (cnt_down >= 0x400)) {
+ dev_err(g_ipu_dev, "DI%d counters out of range.\n", di);
+ return;
+ }
+
+ reg = (run_count << 19) | (++run_src << 16) |
+ (offset_count << 3) | ++offset_src;
+ __raw_writel(reg, DI_SW_GEN0(di, wave_gen));
+ reg = (cnt_polarity_gen_en << 29) | (++cnt_clr_src << 25) |
+ (++cnt_polarity_trigger_src << 12) | (++cnt_polarity_clr_src << 9);
+ reg |= (cnt_down << 16) | cnt_up;
+ if (repeat_count == 0) {
+ /* Enable auto reload */
+ reg |= 0x10000000;
+ }
+ __raw_writel(reg, DI_SW_GEN1(di, wave_gen));
+ reg = __raw_readl(DI_STP_REP(di, wave_gen));
+ reg &= ~(0xFFFF << (16 * ((wave_gen - 1) & 0x1)));
+ reg |= repeat_count << (16 * ((wave_gen - 1) & 0x1));
+ __raw_writel(reg, DI_STP_REP(di, wave_gen));
+}
+
+static void _ipu_dc_map_config(int map, int byte_num, int offset, int mask)
+{
+ int ptr = map * 3 + byte_num;
+ u32 reg;
+
+ reg = __raw_readl(DC_MAP_CONF_VAL(ptr));
+ reg &= ~(0xFFFF << (16 * (ptr & 0x1)));
+ reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
+ __raw_writel(reg, DC_MAP_CONF_VAL(ptr));
+
+ reg = __raw_readl(DC_MAP_CONF_PTR(map));
+ reg &= ~(0x1F << ((16 * (map & 0x1)) + (5 * byte_num)));
+ reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
+ __raw_writel(reg, DC_MAP_CONF_PTR(map));
+}
+
+static void _ipu_dc_map_clear(int map)
+{
+ u32 reg = __raw_readl(DC_MAP_CONF_PTR(map));
+ __raw_writel(reg & ~(0xFFFF << (16 * (map & 0x1))),
+ DC_MAP_CONF_PTR(map));
+}
+
+static void _ipu_dc_write_tmpl(int word, u32 opcode, u32 operand, int map,
+ int wave, int glue, int sync)
+{
+ u32 reg;
+ int stop = 1;
+
+ reg = sync;
+ reg |= (glue << 4);
+ reg |= (++wave << 11);
+ reg |= (++map << 15);
+ reg |= (operand << 20) & 0xFFF00000;
+ __raw_writel(reg, ipu_dc_tmpl_reg + word * 2);
+
+ reg = (operand >> 12);
+ reg |= opcode << 4;
+ reg |= (stop << 9);
+ __raw_writel(reg, ipu_dc_tmpl_reg + word * 2 + 1);
+}
+
+static void _ipu_dc_link_event(int chan, int event, int addr, int priority)
+{
+ u32 reg;
+
+ reg = __raw_readl(DC_RL_CH(chan, event));
+ reg &= ~(0xFFFF << (16 * (event & 0x1)));
+ reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
+ __raw_writel(reg, DC_RL_CH(chan, event));
+}
+
+/* Y = R * .299 + G * .587 + B * .114;
+ U = R * -.169 + G * -.332 + B * .500 + 128.;
+ V = R * .500 + G * -.419 + B * -.0813 + 128.;*/
+static const int rgb2ycbcr_coeff[5][3] = {
+ {153, 301, 58},
+ {-87, -170, 0x0100},
+ {0x100, -215, -42},
+ {0x0000, 0x0200, 0x0200}, /* B0, B1, B2 */
+ {0x2, 0x2, 0x2}, /* S0, S1, S2 */
+};
+
+/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
+ G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
+ B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
+static const int ycbcr2rgb_coeff[5][3] = {
+ {0x095, 0x000, 0x0CC},
+ {0x095, 0x3CE, 0x398},
+ {0x095, 0x0FF, 0x000},
+ {0x3E42, 0x010A, 0x3DD6}, /*B0,B1,B2 */
+ {0x1, 0x1, 0x1}, /*S0,S1,S2 */
+};
+
+#define mask_a(a) ((u32)(a) & 0x3FF)
+#define mask_b(b) ((u32)(b) & 0x3FFF)
+
+/* Pls keep S0, S1 and S2 as 0x2 by using this convertion */
+static int _rgb_to_yuv(int n, int red, int green, int blue)
+{
+ int c;
+ c = red * rgb2ycbcr_coeff[n][0];
+ c += green * rgb2ycbcr_coeff[n][1];
+ c += blue * rgb2ycbcr_coeff[n][2];
+ c /= 16;
+ c += rgb2ycbcr_coeff[3][n] * 4;
+ c += 8;
+ c /= 16;
+ if (c < 0)
+ c = 0;
+ if (c > 255)
+ c = 255;
+ return c;
+}
+
+/*
+ * Row is for BG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE
+ * Column is for FG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE
+ */
+static struct dp_csc_param_t dp_csc_array[CSC_NUM][CSC_NUM] = {
+{{DP_COM_CONF_CSC_DEF_BOTH, &rgb2ycbcr_coeff}, {0, 0}, {0, 0}, {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff}, {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff} },
+{{0, 0}, {DP_COM_CONF_CSC_DEF_BOTH, &ycbcr2rgb_coeff}, {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff}, {0, 0}, {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff} },
+{{0, 0}, {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, {0, 0}, {0, 0}, {0, 0} },
+{{DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, {0, 0}, {0, 0}, {0, 0}, {0, 0} },
+{{DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, {0, 0}, {0, 0}, {0, 0} }
+};
+
+static enum csc_type_t fg_csc_type = CSC_NONE, bg_csc_type = CSC_NONE;
+static int color_key_4rgb = 1;
+
+void __ipu_dp_csc_setup(int dp, struct dp_csc_param_t dp_csc_param,
+ bool srm_mode_update)
+{
+ u32 reg;
+ const int (*coeff)[5][3];
+
+ if (dp_csc_param.mode >= 0) {
+ reg = __raw_readl(DP_COM_CONF(dp));
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+ reg |= dp_csc_param.mode;
+ __raw_writel(reg, DP_COM_CONF(dp));
+ }
+
+ coeff = dp_csc_param.coeff;
+
+ if (coeff) {
+ __raw_writel(mask_a((*coeff)[0][0]) |
+ (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0(dp));
+ __raw_writel(mask_a((*coeff)[0][2]) |
+ (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1(dp));
+ __raw_writel(mask_a((*coeff)[1][1]) |
+ (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2(dp));
+ __raw_writel(mask_a((*coeff)[2][0]) |
+ (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3(dp));
+ __raw_writel(mask_a((*coeff)[2][2]) |
+ (mask_b((*coeff)[3][0]) << 16) |
+ ((*coeff)[4][0] << 30), DP_CSC_0(dp));
+ __raw_writel(mask_b((*coeff)[3][1]) | ((*coeff)[4][1] << 14) |
+ (mask_b((*coeff)[3][2]) << 16) |
+ ((*coeff)[4][2] << 30), DP_CSC_1(dp));
+ }
+
+ if (srm_mode_update) {
+ reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+ __raw_writel(reg, IPU_SRM_PRI2);
+ }
+}
+
+int _ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt,
+ uint32_t out_pixel_fmt)
+{
+ int in_fmt, out_fmt;
+ int dp;
+ int partial = false;
+ uint32_t reg;
+
+ if (channel == MEM_FG_SYNC) {
+ dp = DP_SYNC;
+ partial = true;
+ } else if (channel == MEM_BG_SYNC) {
+ dp = DP_SYNC;
+ partial = false;
+ } else if (channel == MEM_BG_ASYNC0) {
+ dp = DP_ASYNC0;
+ partial = false;
+ } else {
+ return -EINVAL;
+ }
+
+ in_fmt = format_to_colorspace(in_pixel_fmt);
+ out_fmt = format_to_colorspace(out_pixel_fmt);
+
+ if (partial) {
+ if (in_fmt == RGB) {
+ if (out_fmt == RGB)
+ fg_csc_type = RGB2RGB;
+ else
+ fg_csc_type = RGB2YUV;
+ } else {
+ if (out_fmt == RGB)
+ fg_csc_type = YUV2RGB;
+ else
+ fg_csc_type = YUV2YUV;
+ }
+ } else {
+ if (in_fmt == RGB) {
+ if (out_fmt == RGB)
+ bg_csc_type = RGB2RGB;
+ else
+ bg_csc_type = RGB2YUV;
+ } else {
+ if (out_fmt == RGB)
+ bg_csc_type = YUV2RGB;
+ else
+ bg_csc_type = YUV2YUV;
+ }
+ }
+
+ /* Transform color key from rgb to yuv if CSC is enabled */
+ reg = __raw_readl(DP_COM_CONF(dp));
+ if (color_key_4rgb && (reg & DP_COM_CONF_GWCKE) &&
+ (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB)))) {
+ int red, green, blue;
+ int y, u, v;
+ uint32_t color_key = __raw_readl(DP_GRAPH_WIND_CTRL(dp)) & 0xFFFFFFL;
+
+ dev_dbg(g_ipu_dev, "_ipu_dp_init color key 0x%x need change to yuv fmt!\n", color_key);
+
+ red = (color_key >> 16) & 0xFF;
+ green = (color_key >> 8) & 0xFF;
+ blue = color_key & 0xFF;
+
+ y = _rgb_to_yuv(0, red, green, blue);
+ u = _rgb_to_yuv(1, red, green, blue);
+ v = _rgb_to_yuv(2, red, green, blue);
+ color_key = (y << 16) | (u << 8) | v;
+
+ reg = __raw_readl(DP_GRAPH_WIND_CTRL(dp)) & 0xFF000000L;
+ __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(dp));
+ color_key_4rgb = 0;
+
+ dev_dbg(g_ipu_dev, "_ipu_dp_init color key change to yuv fmt 0x%x!\n", color_key);
+ }
+
+ __ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], true);
+
+ return 0;
+}
+
+void _ipu_dp_uninit(ipu_channel_t channel)
+{
+ int dp;
+ int partial = false;
+
+ if (channel == MEM_FG_SYNC) {
+ dp = DP_SYNC;
+ partial = true;
+ } else if (channel == MEM_BG_SYNC) {
+ dp = DP_SYNC;
+ partial = false;
+ } else if (channel == MEM_BG_ASYNC0) {
+ dp = DP_ASYNC0;
+ partial = false;
+ } else {
+ return;
+ }
+
+ if (partial)
+ fg_csc_type = CSC_NONE;
+ else
+ bg_csc_type = CSC_NONE;
+
+ __ipu_dp_csc_setup(dp, dp_csc_array[bg_csc_type][fg_csc_type], false);
+}
+
+void _ipu_dc_init(int dc_chan, int di, bool interlaced)
+{
+ u32 reg = 0;
+
+ if ((dc_chan == 1) || (dc_chan == 5)) {
+ if (interlaced) {
+ _ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 3);
+ _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1);
+ } else {
+ if (di) {
+ _ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3);
+ _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 4, 1);
+ } else {
+ _ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3);
+ _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 7, 1);
+ }
+ }
+ _ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0);
+
+ reg = 0x2;
+ reg |= DC_DISP_ID_SYNC(di) << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET;
+ reg |= di << 2;
+ if (interlaced)
+ reg |= DC_WR_CH_CONF_FIELD_MODE;
+ } else if ((dc_chan == 8) || (dc_chan == 9)) {
+ /* async channels */
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0x64, 1);
+
+ reg = 0x3;
+ reg |= DC_DISP_ID_SERIAL << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET;
+ }
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+
+ __raw_writel(0x00000000, DC_WR_CH_ADDR(dc_chan));
+
+ __raw_writel(0x00000084, DC_GEN);
+}
+
+void _ipu_dc_uninit(int dc_chan)
+{
+ if ((dc_chan == 1) || (dc_chan == 5)) {
+ _ipu_dc_link_event(dc_chan, DC_EVT_NL, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_EOF, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_EOFIELD, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR, 0, 0);
+ } else if ((dc_chan == 8) || (dc_chan == 9)) {
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_0, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_W_1, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_0, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_W_1, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_0, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_W_1, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_0, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_ADDR_R_1, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_0, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_CHAN_R_1, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_0, 0, 0);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA_R_1, 0, 0);
+ }
+}
+
+int _ipu_chan_is_interlaced(ipu_channel_t channel)
+{
+ if (channel == MEM_DC_SYNC)
+ return !!(__raw_readl(DC_WR_CH_CONF_1) &
+ DC_WR_CH_CONF_FIELD_MODE);
+ else if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))
+ return !!(__raw_readl(DC_WR_CH_CONF_5) &
+ DC_WR_CH_CONF_FIELD_MODE);
+ return 0;
+}
+
+void _ipu_dp_dc_enable(ipu_channel_t channel)
+{
+ int di;
+ uint32_t reg;
+ uint32_t dc_chan;
+ int irq = 0;
+
+ if (channel == MEM_FG_SYNC)
+ irq = IPU_IRQ_DP_SF_END;
+ else if (channel == MEM_DC_SYNC)
+ dc_chan = 1;
+ else if (channel == MEM_BG_SYNC)
+ dc_chan = 5;
+ else
+ return;
+
+ if (channel == MEM_FG_SYNC) {
+ /* Enable FG channel */
+ reg = __raw_readl(DP_COM_CONF(DP_SYNC));
+ __raw_writel(reg | DP_COM_CONF_FG_EN, DP_COM_CONF(DP_SYNC));
+
+ reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+ __raw_writel(reg, IPU_SRM_PRI2);
+ return;
+ }
+
+ di = g_dc_di_assignment[dc_chan];
+
+ /* Make sure other DC sync channel is not assigned same DI */
+ reg = __raw_readl(DC_WR_CH_CONF(6 - dc_chan));
+ if ((di << 2) == (reg & DC_WR_CH_CONF_PROG_DI_ID)) {
+ reg &= ~DC_WR_CH_CONF_PROG_DI_ID;
+ reg |= di ? 0 : DC_WR_CH_CONF_PROG_DI_ID;
+ __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan));
+ }
+
+ reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
+ reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET;
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+
+ reg = __raw_readl(IPU_DISP_GEN);
+ if (di)
+ reg |= DI1_COUNTER_RELEASE;
+ else
+ reg |= DI0_COUNTER_RELEASE;
+ __raw_writel(reg, IPU_DISP_GEN);
+}
+
+static bool dc_swap;
+
+static irqreturn_t dc_irq_handler(int irq, void *dev_id)
+{
+ struct completion *comp = dev_id;
+
+ complete(comp);
+ return IRQ_HANDLED;
+}
+
+void _ipu_dp_dc_disable(ipu_channel_t channel, bool swap)
+{
+ int ret;
+ unsigned long lock_flags;
+ uint32_t reg;
+ uint32_t csc;
+ uint32_t dc_chan;
+ int irq = 0;
+ int timeout = 50;
+ DECLARE_COMPLETION_ONSTACK(dc_comp);
+
+ dc_swap = swap;
+
+ if (channel == MEM_DC_SYNC) {
+ dc_chan = 1;
+ irq = IPU_IRQ_DC_FC_1;
+ } else if (channel == MEM_BG_SYNC) {
+ dc_chan = 5;
+ irq = IPU_IRQ_DP_SF_END;
+ } else if (channel == MEM_FG_SYNC) {
+ /* Disable FG channel */
+ dc_chan = 5;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ reg = __raw_readl(DP_COM_CONF(DP_SYNC));
+ csc = reg & DP_COM_CONF_CSC_DEF_MASK;
+ if (csc == DP_COM_CONF_CSC_DEF_FG)
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+
+ reg &= ~DP_COM_CONF_FG_EN;
+ __raw_writel(reg, DP_COM_CONF(DP_SYNC));
+
+ reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+ __raw_writel(reg, IPU_SRM_PRI2);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_DP_SF_END),
+ IPUIRQ_2_STATREG(IPU_IRQ_DP_SF_END));
+ while ((__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_DP_SF_END)) &
+ IPUIRQ_2_MASK(IPU_IRQ_DP_SF_END)) == 0) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+
+ timeout = 50;
+
+ /*
+ * Wait for DC triple buffer to empty,
+ * this check is useful for tv overlay.
+ */
+ if (g_dc_di_assignment[dc_chan] == 0)
+ while ((__raw_readl(DC_STAT) & 0x00000002)
+ != 0x00000002) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+ else if (g_dc_di_assignment[dc_chan] == 1)
+ while ((__raw_readl(DC_STAT) & 0x00000020)
+ != 0x00000020) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+ return;
+ } else {
+ return;
+ }
+
+ if (!dc_swap)
+ __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_VSYNC_PRE_0
+ + g_dc_di_assignment[dc_chan]),
+ IPUIRQ_2_STATREG(IPU_IRQ_VSYNC_PRE_0
+ + g_dc_di_assignment[dc_chan]));
+ ipu_clear_irq(irq);
+ ret = ipu_request_irq(irq, dc_irq_handler, 0, NULL, &dc_comp);
+ if (ret < 0) {
+ dev_err(g_ipu_dev, "DC irq %d in use\n", irq);
+ return;
+ }
+ ret = wait_for_completion_timeout(&dc_comp, msecs_to_jiffies(50));
+
+ dev_dbg(g_ipu_dev, "DC stop timeout - %d * 10ms\n", 5 - ret);
+ ipu_free_irq(irq, &dc_comp);
+
+ if (dc_swap) {
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+ /* Swap DC channel 1 and 5 settings, and disable old dc chan */
+ reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
+ __raw_writel(reg, DC_WR_CH_CONF(6 - dc_chan));
+ reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+ reg ^= DC_WR_CH_CONF_PROG_DI_ID;
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ } else {
+ timeout = 50;
+
+ /* Wait for DC triple buffer to empty */
+ if (g_dc_di_assignment[dc_chan] == 0)
+ while ((__raw_readl(DC_STAT) & 0x00000002)
+ != 0x00000002) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+ else if (g_dc_di_assignment[dc_chan] == 1)
+ while ((__raw_readl(DC_STAT) & 0x00000020)
+ != 0x00000020) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+ reg = __raw_readl(DC_WR_CH_CONF(dc_chan));
+ reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+ __raw_writel(reg, DC_WR_CH_CONF(dc_chan));
+
+ reg = __raw_readl(IPU_DISP_GEN);
+ if (g_dc_di_assignment[dc_chan])
+ reg &= ~DI1_COUNTER_RELEASE;
+ else
+ reg &= ~DI0_COUNTER_RELEASE;
+ __raw_writel(reg, IPU_DISP_GEN);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ if (__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_VSYNC_PRE_0
+ + g_dc_di_assignment[dc_chan])) &
+ IPUIRQ_2_MASK(IPU_IRQ_VSYNC_PRE_0
+ + g_dc_di_assignment[dc_chan]))
+ dev_dbg(g_ipu_dev,
+ "VSyncPre occurred before DI%d disable\n",
+ g_dc_di_assignment[dc_chan]);
+ }
+}
+
+void _ipu_init_dc_mappings(void)
+{
+ /* IPU_PIX_FMT_RGB24 */
+ _ipu_dc_map_clear(0);
+ _ipu_dc_map_config(0, 0, 7, 0xFF);
+ _ipu_dc_map_config(0, 1, 15, 0xFF);
+ _ipu_dc_map_config(0, 2, 23, 0xFF);
+
+ /* IPU_PIX_FMT_RGB666 */
+ _ipu_dc_map_clear(1);
+ _ipu_dc_map_config(1, 0, 5, 0xFC);
+ _ipu_dc_map_config(1, 1, 11, 0xFC);
+ _ipu_dc_map_config(1, 2, 17, 0xFC);
+
+ /* IPU_PIX_FMT_YUV444 */
+ _ipu_dc_map_clear(2);
+ _ipu_dc_map_config(2, 0, 15, 0xFF);
+ _ipu_dc_map_config(2, 1, 23, 0xFF);
+ _ipu_dc_map_config(2, 2, 7, 0xFF);
+
+ /* IPU_PIX_FMT_RGB565 */
+ _ipu_dc_map_clear(3);
+ _ipu_dc_map_config(3, 0, 4, 0xF8);
+ _ipu_dc_map_config(3, 1, 10, 0xFC);
+ _ipu_dc_map_config(3, 2, 15, 0xF8);
+
+ /* IPU_PIX_FMT_LVDS666 */
+ _ipu_dc_map_clear(4);
+ _ipu_dc_map_config(4, 0, 5, 0xFC);
+ _ipu_dc_map_config(4, 1, 13, 0xFC);
+ _ipu_dc_map_config(4, 2, 21, 0xFC);
+}
+
+int _ipu_pixfmt_to_map(uint32_t fmt)
+{
+ switch (fmt) {
+ case IPU_PIX_FMT_GENERIC:
+ case IPU_PIX_FMT_RGB24:
+ return 0;
+ case IPU_PIX_FMT_RGB666:
+ return 1;
+ case IPU_PIX_FMT_YUV444:
+ return 2;
+ case IPU_PIX_FMT_RGB565:
+ return 3;
+ case IPU_PIX_FMT_LVDS666:
+ return 4;
+ }
+
+ return -1;
+}
+
+/*!
+ * This function sets the colorspace for of dp.
+ * modes.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param param If it's not NULL, update the csc table
+ * with this parameter.
+ *
+ * @return N/A
+ */
+void _ipu_dp_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3])
+{
+ int dp;
+ struct dp_csc_param_t dp_csc_param;
+
+ if (channel == MEM_FG_SYNC)
+ dp = DP_SYNC;
+ else if (channel == MEM_BG_SYNC)
+ dp = DP_SYNC;
+ else if (channel == MEM_BG_ASYNC0)
+ dp = DP_ASYNC0;
+ else
+ return;
+
+ dp_csc_param.mode = -1;
+ dp_csc_param.coeff = param;
+ __ipu_dp_csc_setup(dp, dp_csc_param, true);
+}
+
+/*!
+ * This function is called to initialize a synchronous LCD panel.
+ *
+ * @param disp The DI the panel is attached to.
+ *
+ * @param pixel_clk Desired pixel clock frequency in Hz.
+ *
+ * @param pixel_fmt Input parameter for pixel format of buffer.
+ * Pixel format is a FOURCC ASCII code.
+ *
+ * @param width The width of panel in pixels.
+ *
+ * @param height The height of panel in pixels.
+ *
+ * @param hStartWidth The number of pixel clocks between the HSYNC
+ * signal pulse and the start of valid data.
+ *
+ * @param hSyncWidth The width of the HSYNC signal in units of pixel
+ * clocks.
+ *
+ * @param hEndWidth The number of pixel clocks between the end of
+ * valid data and the HSYNC signal for next line.
+ *
+ * @param vStartWidth The number of lines between the VSYNC
+ * signal pulse and the start of valid data.
+ *
+ * @param vSyncWidth The width of the VSYNC signal in units of lines
+ *
+ * @param vEndWidth The number of lines between the end of valid
+ * data and the VSYNC signal for next frame.
+ *
+ * @param sig Bitfield of signal polarities for LCD interface.
+ *
+ * @return This function returns 0 on success or negative error code on
+ * fail.
+ */
+int32_t ipu_init_sync_panel(int disp, uint32_t pixel_clk,
+ uint16_t width, uint16_t height,
+ uint32_t pixel_fmt,
+ uint16_t h_start_width, uint16_t h_sync_width,
+ uint16_t h_end_width, uint16_t v_start_width,
+ uint16_t v_sync_width, uint16_t v_end_width,
+ uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig)
+{
+ unsigned long lock_flags;
+ uint32_t field0_offset = 0;
+ uint32_t field1_offset;
+ uint32_t reg;
+ uint32_t disp_gen, di_gen, vsync_cnt;
+ uint32_t div;
+ uint32_t h_total, v_total;
+ int map;
+ struct clk *di_clk;
+
+ dev_dbg(g_ipu_dev, "panel size = %d x %d\n", width, height);
+
+ if ((v_sync_width == 0) || (h_sync_width == 0))
+ return EINVAL;
+
+ h_total = width + h_sync_width + h_start_width + h_end_width;
+ v_total = height + v_sync_width + v_start_width + v_end_width;
+
+ /* Init clocking */
+ dev_dbg(g_ipu_dev, "pixel clk = %d\n", pixel_clk);
+ if (sig.ext_clk)
+ di_clk = g_di_clk[disp];
+ else
+ di_clk = g_ipu_clk;
+
+ /*
+ * Calculate divider
+ * Fractional part is 4 bits,
+ * so simply multiply by 2^4 to get fractional part.
+ */
+ div = (clk_get_rate(di_clk) * 16) / pixel_clk;
+ if (div < 0x10) /* Min DI disp clock divider is 1 */
+ div = 0x10;
+ /*
+ * DI disp clock offset is zero,
+ * and fractional part is rounded off to 0.5.
+ */
+ div &= 0xFF8;
+
+ reg = __raw_readl(DI_GENERAL(disp));
+ if (sig.ext_clk)
+ __raw_writel(reg | DI_GEN_DI_CLK_EXT, DI_GENERAL(disp));
+ else
+ __raw_writel(reg & ~DI_GEN_DI_CLK_EXT, DI_GENERAL(disp));
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ disp_gen = __raw_readl(IPU_DISP_GEN);
+ disp_gen &= disp ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE;
+ __raw_writel(disp_gen, IPU_DISP_GEN);
+
+ __raw_writel(div, DI_BS_CLKGEN0(disp));
+
+ /* Setup pixel clock timing */
+ /* FIXME: needs to be more flexible */
+ /* Down time is half of period */
+ __raw_writel((div / 16) << 16, DI_BS_CLKGEN1(disp));
+
+ _ipu_di_data_wave_config(disp, SYNC_WAVE, div / 16 - 1, div / 16 - 1);
+ _ipu_di_data_pin_config(disp, SYNC_WAVE, DI_PIN15, 3, 0, div / 16 * 2);
+
+ div = div / 16; /* Now divider is integer portion */
+
+ map = _ipu_pixfmt_to_map(pixel_fmt);
+ if (map < 0) {
+ dev_dbg(g_ipu_dev, "IPU_DISP: No MAP\n");
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return -EINVAL;
+ }
+
+ di_gen = 0;
+ if (sig.ext_clk)
+ di_gen |= DI_GEN_DI_CLK_EXT;
+
+ if (sig.interlaced) {
+ if (cpu_is_mx51_rev(CHIP_REV_2_0)) {
+ /* Setup internal HSYNC waveform */
+ _ipu_di_sync_config(
+ disp, /* display */
+ 1, /* counter */
+ h_total/2 - 1, /* run count */
+ DI_SYNC_CLK, /* run_resolution */
+ 0, /* offset */
+ DI_SYNC_NONE, /* offset resolution */
+ 0, /* repeat count */
+ DI_SYNC_NONE, /* CNT_CLR_SEL */
+ 0, /* CNT_POLARITY_GEN_EN */
+ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */
+ DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */
+ 0, /* COUNT UP */
+ 0 /* COUNT DOWN */
+ );
+
+ /* Field 1 VSYNC waveform */
+ _ipu_di_sync_config(
+ disp, /* display */
+ 2, /* counter */
+ h_total - 1, /* run count */
+ DI_SYNC_CLK, /* run_resolution */
+ 0, /* offset */
+ DI_SYNC_NONE, /* offset resolution */
+ 0, /* repeat count */
+ DI_SYNC_NONE, /* CNT_CLR_SEL */
+ 0, /* CNT_POLARITY_GEN_EN */
+ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */
+ DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */
+ 0, /* COUNT UP */
+ 4 /* COUNT DOWN */
+ );
+
+ /* Setup internal HSYNC waveform */
+ _ipu_di_sync_config(
+ disp, /* display */
+ 3, /* counter */
+ v_total*2 - 1, /* run count */
+ DI_SYNC_INT_HSYNC, /* run_resolution */
+ 1, /* offset */
+ DI_SYNC_INT_HSYNC, /* offset resolution */
+ 0, /* repeat count */
+ DI_SYNC_NONE, /* CNT_CLR_SEL */
+ 0, /* CNT_POLARITY_GEN_EN */
+ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */
+ DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */
+ 0, /* COUNT UP */
+ 4 /* COUNT DOWN */
+ );
+
+ /* Active Field ? */
+ _ipu_di_sync_config(
+ disp, /* display */
+ 4, /* counter */
+ v_total/2 - 1, /* run count */
+ DI_SYNC_HSYNC, /* run_resolution */
+ v_start_width, /* offset */
+ DI_SYNC_HSYNC, /* offset resolution */
+ 2, /* repeat count */
+ DI_SYNC_VSYNC, /* CNT_CLR_SEL */
+ 0, /* CNT_POLARITY_GEN_EN */
+ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */
+ DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */
+ 0, /* COUNT UP */
+ 0 /* COUNT DOWN */
+ );
+
+ /* Active Line */
+ _ipu_di_sync_config(
+ disp, /* display */
+ 5, /* counter */
+ 0, /* run count */
+ DI_SYNC_HSYNC, /* run_resolution */
+ 0, /* offset */
+ DI_SYNC_NONE, /* offset resolution */
+ height/2, /* repeat count */
+ 4, /* CNT_CLR_SEL */
+ 0, /* CNT_POLARITY_GEN_EN */
+ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */
+ DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */
+ 0, /* COUNT UP */
+ 0 /* COUNT DOWN */
+ );
+
+ /* Field 0 VSYNC waveform */
+ _ipu_di_sync_config(
+ disp, /* display */
+ 6, /* counter */
+ v_total - 1, /* run count */
+ DI_SYNC_HSYNC, /* run_resolution */
+ 0, /* offset */
+ DI_SYNC_NONE, /* offset resolution */
+ 0, /* repeat count */
+ DI_SYNC_NONE, /* CNT_CLR_SEL */
+ 0, /* CNT_POLARITY_GEN_EN */
+ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */
+ DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */
+ 0, /* COUNT UP */
+ 0 /* COUNT DOWN */
+ );
+
+ /* DC VSYNC waveform */
+ vsync_cnt = 7;
+ _ipu_di_sync_config(
+ disp, /* display */
+ 7, /* counter */
+ v_total/2 - 1, /* run count */
+ DI_SYNC_HSYNC, /* run_resolution */
+ 9, /* offset */
+ DI_SYNC_HSYNC, /* offset resolution */
+ 2, /* repeat count */
+ DI_SYNC_VSYNC, /* CNT_CLR_SEL */
+ 0, /* CNT_POLARITY_GEN_EN */
+ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */
+ DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */
+ 0, /* COUNT UP */
+ 0 /* COUNT DOWN */
+ );
+
+ /* active pixel waveform */
+ _ipu_di_sync_config(
+ disp, /* display */
+ 8, /* counter */
+ 0, /* run count */
+ DI_SYNC_CLK, /* run_resolution */
+ h_start_width, /* offset */
+ DI_SYNC_CLK, /* offset resolution */
+ width, /* repeat count */
+ 5, /* CNT_CLR_SEL */
+ 0, /* CNT_POLARITY_GEN_EN */
+ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */
+ DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */
+ 0, /* COUNT UP */
+ 0 /* COUNT DOWN */
+ );
+
+ /* ??? */
+ _ipu_di_sync_config(
+ disp, /* display */
+ 9, /* counter */
+ v_total - 1, /* run count */
+ DI_SYNC_INT_HSYNC, /* run_resolution */
+ v_total/2, /* offset */
+ DI_SYNC_INT_HSYNC, /* offset resolution */
+ 0, /* repeat count */
+ DI_SYNC_HSYNC, /* CNT_CLR_SEL */
+ 0, /* CNT_POLARITY_GEN_EN */
+ DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */
+ DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */
+ 0, /* COUNT UP */
+ 4 /* COUNT DOWN */
+ );
+
+ /* set gentime select and tag sel */
+ reg = __raw_readl(DI_SW_GEN1(disp, 9));
+ reg &= 0x1FFFFFFF;
+ reg |= (3-1)<<29 | 0x00008000;
+ __raw_writel(reg, DI_SW_GEN1(disp, 9));
+
+ __raw_writel(v_total / 2 - 1, DI_SCR_CONF(disp));
+
+ /* set y_sel = 1 */
+ di_gen |= 0x10000000;
+ di_gen |= DI_GEN_POLARITY_5;
+ di_gen |= DI_GEN_POLARITY_8;
+ } else {
+ /* Setup internal HSYNC waveform */
+ _ipu_di_sync_config(disp, 1, h_total - 1, DI_SYNC_CLK,
+ 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0, DI_SYNC_NONE,
+ DI_SYNC_NONE, 0, 0);
+
+ field1_offset = v_sync_width + v_start_width + height / 2 +
+ v_end_width;
+ if (sig.odd_field_first) {
+ field0_offset = field1_offset - 1;
+ field1_offset = 0;
+ }
+ v_total += v_start_width + v_end_width;
+
+ /* Field 1 VSYNC waveform */
+ _ipu_di_sync_config(disp, 2, v_total - 1, 1,
+ field0_offset,
+ field0_offset ? 1 : DI_SYNC_NONE,
+ 0, DI_SYNC_NONE, 0,
+ DI_SYNC_NONE, DI_SYNC_NONE, 0, 4);
+
+ /* Setup internal HSYNC waveform */
+ _ipu_di_sync_config(disp, 3, h_total - 1, DI_SYNC_CLK,
+ 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0,
+ DI_SYNC_NONE, DI_SYNC_NONE, 0, 4);
+
+ /* Active Field ? */
+ _ipu_di_sync_config(disp, 4,
+ field0_offset ?
+ field0_offset : field1_offset - 2,
+ 1, v_start_width + v_sync_width, 1, 2, 2,
+ 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0);
+
+ /* Active Line */
+ _ipu_di_sync_config(disp, 5, 0, 1,
+ 0, DI_SYNC_NONE,
+ height / 2, 4, 0, DI_SYNC_NONE,
+ DI_SYNC_NONE, 0, 0);
+
+ /* Field 0 VSYNC waveform */
+ _ipu_di_sync_config(disp, 6, v_total - 1, 1,
+ 0, DI_SYNC_NONE,
+ 0, DI_SYNC_NONE, 0, DI_SYNC_NONE,
+ DI_SYNC_NONE, 0, 0);
+
+ /* DC VSYNC waveform */
+ vsync_cnt = 7;
+ _ipu_di_sync_config(disp, 7, 0, 1,
+ field1_offset,
+ field1_offset ? 1 : DI_SYNC_NONE,
+ 1, 2, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0);
+
+ /* active pixel waveform */
+ _ipu_di_sync_config(disp, 8, 0, DI_SYNC_CLK,
+ h_sync_width + h_start_width, DI_SYNC_CLK,
+ width, 5, 0, DI_SYNC_NONE, DI_SYNC_NONE,
+ 0, 0);
+
+ /* ??? */
+ _ipu_di_sync_config(disp, 9, v_total - 1, 2,
+ 0, DI_SYNC_NONE,
+ 0, DI_SYNC_NONE, 6, DI_SYNC_NONE,
+ DI_SYNC_NONE, 0, 0);
+
+ reg = __raw_readl(DI_SW_GEN1(disp, 9));
+ reg |= 0x8000;
+ __raw_writel(reg, DI_SW_GEN1(disp, 9));
+
+ __raw_writel(v_sync_width + v_start_width +
+ v_end_width + height / 2 - 1, DI_SCR_CONF(disp));
+ }
+
+ /* Init template microcode */
+ _ipu_dc_write_tmpl(0, WROD(0), 0, map, SYNC_WAVE, 0, 8);
+
+ if (sig.Hsync_pol)
+ di_gen |= DI_GEN_POLARITY_3;
+ if (sig.Vsync_pol)
+ di_gen |= DI_GEN_POLARITY_2;
+ } else {
+ /* Setup internal HSYNC waveform */
+ _ipu_di_sync_config(disp, 1, h_total - 1, DI_SYNC_CLK,
+ 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0, DI_SYNC_NONE,
+ DI_SYNC_NONE, 0, 0);
+
+ /* Setup external (delayed) HSYNC waveform */
+ _ipu_di_sync_config(disp, DI_SYNC_HSYNC, h_total - 1,
+ DI_SYNC_CLK, div * v_to_h_sync, DI_SYNC_CLK,
+ 0, DI_SYNC_NONE, 1, DI_SYNC_NONE,
+ DI_SYNC_CLK, 0, h_sync_width * 2);
+ /* Setup VSYNC waveform */
+ vsync_cnt = DI_SYNC_VSYNC;
+ _ipu_di_sync_config(disp, DI_SYNC_VSYNC, v_total - 1,
+ DI_SYNC_INT_HSYNC, 0, DI_SYNC_NONE, 0,
+ DI_SYNC_NONE, 1, DI_SYNC_NONE,
+ DI_SYNC_INT_HSYNC, 0, v_sync_width * 2);
+ __raw_writel(v_total - 1, DI_SCR_CONF(disp));
+
+ /* Setup active data waveform to sync with DC */
+ _ipu_di_sync_config(disp, 4, 0, DI_SYNC_HSYNC,
+ v_sync_width + v_start_width, DI_SYNC_HSYNC, height,
+ DI_SYNC_VSYNC, 0, DI_SYNC_NONE,
+ DI_SYNC_NONE, 0, 0);
+ _ipu_di_sync_config(disp, 5, 0, DI_SYNC_CLK,
+ h_sync_width + h_start_width, DI_SYNC_CLK,
+ width, 4, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0,
+ 0);
+
+ /* reset all unused counters */
+ __raw_writel(0, DI_SW_GEN0(disp, 6));
+ __raw_writel(0, DI_SW_GEN1(disp, 6));
+ __raw_writel(0, DI_SW_GEN0(disp, 7));
+ __raw_writel(0, DI_SW_GEN1(disp, 7));
+ __raw_writel(0, DI_SW_GEN0(disp, 8));
+ __raw_writel(0, DI_SW_GEN1(disp, 8));
+ __raw_writel(0, DI_SW_GEN0(disp, 9));
+ __raw_writel(0, DI_SW_GEN1(disp, 9));
+
+ reg = __raw_readl(DI_STP_REP(disp, 6));
+ reg &= 0x0000FFFF;
+ __raw_writel(reg, DI_STP_REP(disp, 6));
+ __raw_writel(0, DI_STP_REP(disp, 7));
+ __raw_writel(0, DI_STP_REP(disp, 9));
+
+ /* Init template microcode */
+ if (disp) {
+ _ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ _ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ _ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ } else {
+ _ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ _ipu_dc_write_tmpl(6, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ _ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ }
+
+ if (sig.Hsync_pol)
+ di_gen |= DI_GEN_POLARITY_2;
+ if (sig.Vsync_pol)
+ di_gen |= DI_GEN_POLARITY_3;
+ }
+
+ __raw_writel(di_gen, DI_GENERAL(disp));
+ __raw_writel((--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,
+ DI_SYNC_AS_GEN(disp));
+
+ reg = __raw_readl(DI_POL(disp));
+ reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15);
+ if (sig.enable_pol)
+ reg |= DI_POL_DRDY_POLARITY_15;
+ if (sig.data_pol)
+ reg |= DI_POL_DRDY_DATA_POLARITY;
+ __raw_writel(reg, DI_POL(disp));
+
+ __raw_writel(width, DC_DISP_CONF2(DC_DISP_ID_SYNC(disp)));
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_init_sync_panel);
+
+
+int ipu_init_async_panel(int disp, int type, uint32_t cycle_time,
+ uint32_t pixel_fmt, ipu_adc_sig_cfg_t sig)
+{
+ unsigned long lock_flags;
+ int map;
+ u32 ser_conf = 0;
+ u32 div;
+ u32 di_clk = clk_get_rate(g_ipu_clk);
+
+ /* round up cycle_time, then calcalate the divider using scaled math */
+ cycle_time += (1000000000UL / di_clk) - 1;
+ div = (cycle_time * (di_clk / 256UL)) / (1000000000UL / 256UL);
+
+ map = _ipu_pixfmt_to_map(pixel_fmt);
+ if (map < 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if (type == IPU_PANEL_SERIAL) {
+ __raw_writel((div << 24) | ((sig.ifc_width - 1) << 4),
+ DI_DW_GEN(disp, ASYNC_SER_WAVE));
+
+ _ipu_di_data_pin_config(disp, ASYNC_SER_WAVE, DI_PIN_CS,
+ 0, 0, (div * 2) + 1);
+ _ipu_di_data_pin_config(disp, ASYNC_SER_WAVE, DI_PIN_SER_CLK,
+ 1, div, div * 2);
+ _ipu_di_data_pin_config(disp, ASYNC_SER_WAVE, DI_PIN_SER_RS,
+ 2, 0, 0);
+
+ _ipu_dc_write_tmpl(0x64, WROD(0), 0, map, ASYNC_SER_WAVE, 0, 0);
+
+ /* Configure DC for serial panel */
+ __raw_writel(0x14, DC_DISP_CONF1(DC_DISP_ID_SERIAL));
+
+ if (sig.clk_pol)
+ ser_conf |= DI_SER_CONF_SERIAL_CLK_POL;
+ if (sig.data_pol)
+ ser_conf |= DI_SER_CONF_SERIAL_DATA_POL;
+ if (sig.rs_pol)
+ ser_conf |= DI_SER_CONF_SERIAL_RS_POL;
+ if (sig.cs_pol)
+ ser_conf |= DI_SER_CONF_SERIAL_CS_POL;
+ __raw_writel(ser_conf, DI_SER_CONF(disp));
+ }
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ return 0;
+}
+EXPORT_SYMBOL(ipu_init_async_panel);
+
+/*!
+ * This function sets the foreground and background plane global alpha blending
+ * modes. This function also sets the DP graphic plane according to the
+ * parameter of IPUv3 DP channel.
+ *
+ * @param channel IPUv3 DP channel
+ *
+ * @param enable Boolean to enable or disable global alpha
+ * blending. If disabled, local blending is used.
+ *
+ * @param alpha Global alpha value.
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, bool enable,
+ uint8_t alpha)
+{
+ uint32_t reg;
+ uint32_t flow;
+ unsigned long lock_flags;
+ bool bg_chan;
+
+ if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC)
+ flow = DP_SYNC;
+ else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0)
+ flow = DP_ASYNC0;
+ else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1)
+ flow = DP_ASYNC1;
+ else
+ return -EINVAL;
+
+ if (channel == MEM_BG_SYNC || channel == MEM_BG_ASYNC0 ||
+ channel == MEM_BG_ASYNC1)
+ bg_chan = true;
+ else
+ bg_chan = false;
+
+ if (!g_ipu_clk_enabled)
+ clk_enable(g_ipu_clk);
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ if (bg_chan) {
+ reg = __raw_readl(DP_COM_CONF(flow));
+ __raw_writel(reg & ~DP_COM_CONF_GWSEL, DP_COM_CONF(flow));
+ } else {
+ reg = __raw_readl(DP_COM_CONF(flow));
+ __raw_writel(reg | DP_COM_CONF_GWSEL, DP_COM_CONF(flow));
+ }
+
+ if (enable) {
+ reg = __raw_readl(DP_GRAPH_WIND_CTRL(flow)) & 0x00FFFFFFL;
+ __raw_writel(reg | ((uint32_t) alpha << 24),
+ DP_GRAPH_WIND_CTRL(flow));
+
+ reg = __raw_readl(DP_COM_CONF(flow));
+ __raw_writel(reg | DP_COM_CONF_GWAM, DP_COM_CONF(flow));
+ } else {
+ reg = __raw_readl(DP_COM_CONF(flow));
+ __raw_writel(reg & ~DP_COM_CONF_GWAM, DP_COM_CONF(flow));
+ }
+
+ reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+ __raw_writel(reg, IPU_SRM_PRI2);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ if (!g_ipu_clk_enabled)
+ clk_disable(g_ipu_clk);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_disp_set_global_alpha);
+
+/*!
+ * This function sets the transparent color key for SDC graphic plane.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param enable Boolean to enable or disable color key
+ *
+ * @param colorKey 24-bit RGB color for transparent color key.
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_color_key(ipu_channel_t channel, bool enable,
+ uint32_t color_key)
+{
+ uint32_t reg, flow;
+ int y, u, v;
+ int red, green, blue;
+ unsigned long lock_flags;
+
+ if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC)
+ flow = DP_SYNC;
+ else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0)
+ flow = DP_ASYNC0;
+ else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1)
+ flow = DP_ASYNC1;
+ else
+ return -EINVAL;
+
+ if (!g_ipu_clk_enabled)
+ clk_enable(g_ipu_clk);
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ color_key_4rgb = 1;
+ /* Transform color key from rgb to yuv if CSC is enabled */
+ if (((fg_csc_type == RGB2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == RGB2YUV)) ||
+ ((fg_csc_type == YUV2YUV) && (bg_csc_type == YUV2YUV)) ||
+ ((fg_csc_type == YUV2RGB) && (bg_csc_type == YUV2RGB))) {
+
+ dev_dbg(g_ipu_dev, "color key 0x%x need change to yuv fmt\n", color_key);
+
+ red = (color_key >> 16) & 0xFF;
+ green = (color_key >> 8) & 0xFF;
+ blue = color_key & 0xFF;
+
+ y = _rgb_to_yuv(0, red, green, blue);
+ u = _rgb_to_yuv(1, red, green, blue);
+ v = _rgb_to_yuv(2, red, green, blue);
+ color_key = (y << 16) | (u << 8) | v;
+
+ color_key_4rgb = 0;
+
+ dev_dbg(g_ipu_dev, "color key change to yuv fmt 0x%x\n", color_key);
+ }
+
+ if (enable) {
+ reg = __raw_readl(DP_GRAPH_WIND_CTRL(flow)) & 0xFF000000L;
+ __raw_writel(reg | color_key, DP_GRAPH_WIND_CTRL(flow));
+
+ reg = __raw_readl(DP_COM_CONF(flow));
+ __raw_writel(reg | DP_COM_CONF_GWCKE, DP_COM_CONF(flow));
+ } else {
+ reg = __raw_readl(DP_COM_CONF(flow));
+ __raw_writel(reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF(flow));
+ }
+
+ reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+ __raw_writel(reg, IPU_SRM_PRI2);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ if (!g_ipu_clk_enabled)
+ clk_disable(g_ipu_clk);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_disp_set_color_key);
+
+/*!
+ * This function sets the window position of the foreground or background plane.
+ * modes.
+ *
+ * @param channel Input parameter for the logical channel ID.
+ *
+ * @param x_pos The X coordinate position to place window at.
+ * The position is relative to the top left corner.
+ *
+ * @param y_pos The Y coordinate position to place window at.
+ * The position is relative to the top left corner.
+ *
+ * @return Returns 0 on success or negative error code on fail
+ */
+int32_t ipu_disp_set_window_pos(ipu_channel_t channel, int16_t x_pos,
+ int16_t y_pos)
+{
+ u32 reg;
+ unsigned long lock_flags;
+ uint32_t flow = 0;
+
+ if (channel == MEM_FG_SYNC)
+ flow = DP_SYNC;
+ else if (channel == MEM_FG_ASYNC0)
+ flow = DP_ASYNC0;
+ else if (channel == MEM_FG_ASYNC1)
+ flow = DP_ASYNC1;
+ else
+ return -EINVAL;
+
+ if (!g_ipu_clk_enabled)
+ clk_enable(g_ipu_clk);
+
+ spin_lock_irqsave(&ipu_lock, lock_flags);
+
+ __raw_writel((x_pos << 16) | y_pos, DP_FG_POS(flow));
+
+ reg = __raw_readl(IPU_SRM_PRI2) | 0x8;
+ __raw_writel(reg, IPU_SRM_PRI2);
+
+ spin_unlock_irqrestore(&ipu_lock, lock_flags);
+ if (!g_ipu_clk_enabled)
+ clk_disable(g_ipu_clk);
+
+ return 0;
+}
+EXPORT_SYMBOL(ipu_disp_set_window_pos);
+
+void ipu_disp_direct_write(ipu_channel_t channel, u32 value, u32 offset)
+{
+ if (channel == DIRECT_ASYNC0)
+ __raw_writel(value, ipu_disp_base[0] + offset);
+ else if (channel == DIRECT_ASYNC1)
+ __raw_writel(value, ipu_disp_base[1] + offset);
+}
+EXPORT_SYMBOL(ipu_disp_direct_write);
+
+void ipu_reset_disp_panel(void)
+{
+ uint32_t tmp;
+
+ tmp = __raw_readl(DI_GENERAL(1));
+ __raw_writel(tmp | 0x08, DI_GENERAL(1));
+ msleep(10); /* tRES >= 100us */
+ tmp = __raw_readl(DI_GENERAL(1));
+ __raw_writel(tmp & ~0x08, DI_GENERAL(1));
+ msleep(60);
+
+ return;
+}
+EXPORT_SYMBOL(ipu_reset_disp_panel);
diff --git a/drivers/mxc/ipu3/ipu_ic.c b/drivers/mxc/ipu3/ipu_ic.c
new file mode 100644
index 000000000000..1a4bda3f071a
--- /dev/null
+++ b/drivers/mxc/ipu3/ipu_ic.c
@@ -0,0 +1,826 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * @file ipu_ic.c
+ *
+ * @brief IPU IC functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+
+#include "ipu_prv.h"
+#include "ipu_regs.h"
+#include "ipu_param_mem.h"
+
+enum {
+ IC_TASK_VIEWFINDER,
+ IC_TASK_ENCODER,
+ IC_TASK_POST_PROCESSOR
+};
+
+static void _init_csc(uint8_t ic_task, ipu_color_space_t in_format,
+ ipu_color_space_t out_format, int csc_index);
+static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize,
+ uint32_t *resizeCoeff,
+ uint32_t *downsizeCoeff);
+
+void _ipu_vdi_set_top_field_man(bool top_field_0)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(VDI_C);
+ if (top_field_0)
+ reg &= ~VDI_C_TOP_FIELD_MAN_1;
+ else
+ reg |= VDI_C_TOP_FIELD_MAN_1;
+ __raw_writel(reg, VDI_C);
+}
+
+void _ipu_vdi_set_motion(ipu_motion_sel motion_sel)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(VDI_C);
+ reg &= ~(VDI_C_MOT_SEL_FULL | VDI_C_MOT_SEL_MED | VDI_C_MOT_SEL_LOW);
+ if (motion_sel == HIGH_MOTION)
+ reg |= VDI_C_MOT_SEL_FULL;
+ else if (motion_sel == MED_MOTION)
+ reg |= VDI_C_MOT_SEL_MED;
+ else
+ reg |= VDI_C_MOT_SEL_LOW;
+
+ __raw_writel(reg, VDI_C);
+}
+
+void ic_dump_register(void)
+{
+ printk(KERN_DEBUG "IC_CONF = \t0x%08X\n", __raw_readl(IC_CONF));
+ printk(KERN_DEBUG "IC_PRP_ENC_RSC = \t0x%08X\n",
+ __raw_readl(IC_PRP_ENC_RSC));
+ printk(KERN_DEBUG "IC_PRP_VF_RSC = \t0x%08X\n",
+ __raw_readl(IC_PRP_VF_RSC));
+ printk(KERN_DEBUG "IC_PP_RSC = \t0x%08X\n", __raw_readl(IC_PP_RSC));
+ printk(KERN_DEBUG "IC_IDMAC_1 = \t0x%08X\n", __raw_readl(IC_IDMAC_1));
+ printk(KERN_DEBUG "IC_IDMAC_2 = \t0x%08X\n", __raw_readl(IC_IDMAC_2));
+ printk(KERN_DEBUG "IC_IDMAC_3 = \t0x%08X\n", __raw_readl(IC_IDMAC_3));
+}
+
+void _ipu_ic_enable_task(ipu_channel_t channel)
+{
+ uint32_t ic_conf;
+
+ ic_conf = __raw_readl(IC_CONF);
+ switch (channel) {
+ case CSI_PRP_VF_MEM:
+ case MEM_PRP_VF_MEM:
+ ic_conf |= IC_CONF_PRPVF_EN;
+ break;
+ case MEM_VDI_PRP_VF_MEM:
+ ic_conf |= IC_CONF_PRPVF_EN;
+ break;
+ case MEM_ROT_VF_MEM:
+ ic_conf |= IC_CONF_PRPVF_ROT_EN;
+ break;
+ case CSI_PRP_ENC_MEM:
+ case MEM_PRP_ENC_MEM:
+ ic_conf |= IC_CONF_PRPENC_EN;
+ break;
+ case MEM_ROT_ENC_MEM:
+ ic_conf |= IC_CONF_PRPENC_ROT_EN;
+ break;
+ case MEM_PP_MEM:
+ ic_conf |= IC_CONF_PP_EN;
+ break;
+ case MEM_ROT_PP_MEM:
+ ic_conf |= IC_CONF_PP_ROT_EN;
+ break;
+ default:
+ break;
+ }
+ __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_disable_task(ipu_channel_t channel)
+{
+ uint32_t ic_conf;
+
+ ic_conf = __raw_readl(IC_CONF);
+ switch (channel) {
+ case CSI_PRP_VF_MEM:
+ case MEM_PRP_VF_MEM:
+ ic_conf &= ~IC_CONF_PRPVF_EN;
+ break;
+ case MEM_VDI_PRP_VF_MEM:
+ ic_conf &= ~IC_CONF_PRPVF_EN;
+ break;
+ case MEM_ROT_VF_MEM:
+ ic_conf &= ~IC_CONF_PRPVF_ROT_EN;
+ break;
+ case CSI_PRP_ENC_MEM:
+ case MEM_PRP_ENC_MEM:
+ ic_conf &= ~IC_CONF_PRPENC_EN;
+ break;
+ case MEM_ROT_ENC_MEM:
+ ic_conf &= ~IC_CONF_PRPENC_ROT_EN;
+ break;
+ case MEM_PP_MEM:
+ ic_conf &= ~IC_CONF_PP_EN;
+ break;
+ case MEM_ROT_PP_MEM:
+ ic_conf &= ~IC_CONF_PP_ROT_EN;
+ break;
+ default:
+ break;
+ }
+ __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_vdi_init(ipu_channel_t channel, ipu_channel_params_t *params)
+{
+ uint32_t reg;
+ uint32_t pixel_fmt;
+ bool top_field_0;
+
+ reg = ((params->mem_prp_vf_mem.in_height-1) << 16) |
+ (params->mem_prp_vf_mem.in_width-1);
+ __raw_writel(reg, VDI_FSIZE);
+
+ /* Full motion, only vertical filter is used
+ Burst size is 4 accesses */
+ pixel_fmt =
+ (params->mem_prp_vf_mem.in_pixel_fmt ==
+ V4L2_PIX_FMT_YUV422P) ? VDI_C_CH_422 : VDI_C_CH_420;
+
+ reg = __raw_readl(VDI_C);
+ reg |= pixel_fmt;
+ switch (channel) {
+ case MEM_VDI_PRP_VF_MEM:
+ reg |= VDI_C_BURST_SIZE2_4;
+ break;
+ case MEM_VDI_PRP_VF_MEM_P:
+ reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_SET_1 | VDI_C_VWM1_CLR_2;
+ break;
+ case MEM_VDI_PRP_VF_MEM_N:
+ reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_SET_1 | VDI_C_VWM3_CLR_2;
+ break;
+ default:
+ break;
+ }
+ __raw_writel(reg, VDI_C);
+
+ /* MED_MOTION and LOW_MOTION algorithm that are using 3 fields
+ * should start presenting using the 2nd field.
+ */
+ if (((params->mem_prp_vf_mem.field_fmt == V4L2_FIELD_INTERLACED_TB) &&
+ (params->mem_prp_vf_mem.motion_sel != HIGH_MOTION)) ||
+ ((params->mem_prp_vf_mem.field_fmt == V4L2_FIELD_INTERLACED_BT) &&
+ (params->mem_prp_vf_mem.motion_sel == HIGH_MOTION)))
+ top_field_0 = false;
+ else
+ top_field_0 = true;
+
+ /* Buffer selection toggle the value therefore init val is inverted. */
+ _ipu_vdi_set_top_field_man(!top_field_0);
+
+ _ipu_vdi_set_motion(params->mem_prp_vf_mem.motion_sel);
+
+ reg = __raw_readl(IC_CONF);
+ reg &= ~IC_CONF_RWS_EN;
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_vdi_uninit(void)
+{
+ __raw_writel(0, VDI_FSIZE);
+ __raw_writel(0, VDI_C);
+}
+
+void _ipu_ic_init_prpvf(ipu_channel_params_t *params, bool src_is_csi)
+{
+ uint32_t reg, ic_conf;
+ uint32_t downsizeCoeff, resizeCoeff;
+ ipu_color_space_t in_fmt, out_fmt;
+
+ /* Setup vertical resizing */
+ _calc_resize_coeffs(params->mem_prp_vf_mem.in_height,
+ params->mem_prp_vf_mem.out_height,
+ &resizeCoeff, &downsizeCoeff);
+ reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+ /* Setup horizontal resizing */
+ _calc_resize_coeffs(params->mem_prp_vf_mem.in_width,
+ params->mem_prp_vf_mem.out_width,
+ &resizeCoeff, &downsizeCoeff);
+ reg |= (downsizeCoeff << 14) | resizeCoeff;
+
+ __raw_writel(reg, IC_PRP_VF_RSC);
+
+ ic_conf = __raw_readl(IC_CONF);
+
+ /* Setup color space conversion */
+ in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_pixel_fmt);
+ out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
+ if (in_fmt == RGB) {
+ if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+ /* Enable RGB->YCBCR CSC1 */
+ _init_csc(IC_TASK_VIEWFINDER, RGB, out_fmt, 1);
+ ic_conf |= IC_CONF_PRPVF_CSC1;
+ }
+ }
+ if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+ if (out_fmt == RGB) {
+ /* Enable YCBCR->RGB CSC1 */
+ _init_csc(IC_TASK_VIEWFINDER, YCbCr, RGB, 1);
+ ic_conf |= IC_CONF_PRPVF_CSC1;
+ } else {
+ /* TODO: Support YUV<->YCbCr conversion? */
+ }
+ }
+
+ if (params->mem_prp_vf_mem.graphics_combine_en) {
+ ic_conf |= IC_CONF_PRPVF_CMB;
+
+ if (!(ic_conf & IC_CONF_PRPVF_CSC1)) {
+ /* need transparent CSC1 conversion */
+ _init_csc(IC_TASK_VIEWFINDER, RGB, RGB, 1);
+ ic_conf |= IC_CONF_PRPVF_CSC1; /* Enable RGB->RGB CSC */
+ }
+ in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_g_pixel_fmt);
+ out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
+ if (in_fmt == RGB) {
+ if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+ /* Enable RGB->YCBCR CSC2 */
+ _init_csc(IC_TASK_VIEWFINDER, RGB, out_fmt, 2);
+ ic_conf |= IC_CONF_PRPVF_CSC2;
+ }
+ }
+ if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+ if (out_fmt == RGB) {
+ /* Enable YCBCR->RGB CSC2 */
+ _init_csc(IC_TASK_VIEWFINDER, YCbCr, RGB, 2);
+ ic_conf |= IC_CONF_PRPVF_CSC2;
+ } else {
+ /* TODO: Support YUV<->YCbCr conversion? */
+ }
+ }
+
+ if (params->mem_prp_vf_mem.global_alpha_en) {
+ ic_conf |= IC_CONF_IC_GLB_LOC_A;
+ reg = __raw_readl(IC_CMBP_1);
+ reg &= ~(0xff);
+ reg |= params->mem_prp_vf_mem.alpha;
+ __raw_writel(reg, IC_CMBP_1);
+ } else
+ ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+
+ if (params->mem_prp_vf_mem.key_color_en) {
+ ic_conf |= IC_CONF_KEY_COLOR_EN;
+ __raw_writel(params->mem_prp_vf_mem.key_color,
+ IC_CMBP_2);
+ } else
+ ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+ } else {
+ ic_conf &= ~IC_CONF_PRPVF_CMB;
+ }
+
+ if (src_is_csi)
+ ic_conf &= ~IC_CONF_RWS_EN;
+ else
+ ic_conf |= IC_CONF_RWS_EN;
+
+ __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_prpvf(void)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(IC_CONF);
+ reg &= ~(IC_CONF_PRPVF_EN | IC_CONF_PRPVF_CMB |
+ IC_CONF_PRPVF_CSC2 | IC_CONF_PRPVF_CSC1);
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_vf(ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_vf(void)
+{
+ uint32_t reg;
+ reg = __raw_readl(IC_CONF);
+ reg &= ~IC_CONF_PRPVF_ROT_EN;
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_prpenc(ipu_channel_params_t *params, bool src_is_csi)
+{
+ uint32_t reg, ic_conf;
+ uint32_t downsizeCoeff, resizeCoeff;
+ ipu_color_space_t in_fmt, out_fmt;
+
+ /* Setup vertical resizing */
+ _calc_resize_coeffs(params->mem_prp_enc_mem.in_height,
+ params->mem_prp_enc_mem.out_height,
+ &resizeCoeff, &downsizeCoeff);
+ reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+ /* Setup horizontal resizing */
+ _calc_resize_coeffs(params->mem_prp_enc_mem.in_width,
+ params->mem_prp_enc_mem.out_width,
+ &resizeCoeff, &downsizeCoeff);
+ reg |= (downsizeCoeff << 14) | resizeCoeff;
+
+ __raw_writel(reg, IC_PRP_ENC_RSC);
+
+ ic_conf = __raw_readl(IC_CONF);
+
+ /* Setup color space conversion */
+ in_fmt = format_to_colorspace(params->mem_prp_enc_mem.in_pixel_fmt);
+ out_fmt = format_to_colorspace(params->mem_prp_enc_mem.out_pixel_fmt);
+ if (in_fmt == RGB) {
+ if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+ /* Enable RGB->YCBCR CSC1 */
+ _init_csc(IC_TASK_ENCODER, RGB, out_fmt, 1);
+ ic_conf |= IC_CONF_PRPENC_CSC1;
+ }
+ }
+ if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+ if (out_fmt == RGB) {
+ /* Enable YCBCR->RGB CSC1 */
+ _init_csc(IC_TASK_ENCODER, YCbCr, RGB, 1);
+ ic_conf |= IC_CONF_PRPENC_CSC1;
+ } else {
+ /* TODO: Support YUV<->YCbCr conversion? */
+ }
+ }
+
+ if (src_is_csi)
+ ic_conf &= ~IC_CONF_RWS_EN;
+ else
+ ic_conf |= IC_CONF_RWS_EN;
+
+ __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_prpenc(void)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(IC_CONF);
+ reg &= ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1);
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_enc(ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_enc(void)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(IC_CONF);
+ reg &= ~(IC_CONF_PRPENC_ROT_EN);
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_pp(ipu_channel_params_t *params)
+{
+ uint32_t reg, ic_conf;
+ uint32_t downsizeCoeff, resizeCoeff;
+ ipu_color_space_t in_fmt, out_fmt;
+
+ /* Setup vertical resizing */
+ _calc_resize_coeffs(params->mem_pp_mem.in_height,
+ params->mem_pp_mem.out_height,
+ &resizeCoeff, &downsizeCoeff);
+ reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
+
+ /* Setup horizontal resizing */
+ /* Upadeted for IC split case */
+ if (!(params->mem_pp_mem.out_resize_ratio)) {
+ _calc_resize_coeffs(params->mem_pp_mem.in_width,
+ params->mem_pp_mem.out_width,
+ &resizeCoeff, &downsizeCoeff);
+ reg |= (downsizeCoeff << 14) | resizeCoeff;
+ } else
+ reg |= params->mem_pp_mem.out_resize_ratio;
+
+ __raw_writel(reg, IC_PP_RSC);
+
+ ic_conf = __raw_readl(IC_CONF);
+
+ /* Setup color space conversion */
+ in_fmt = format_to_colorspace(params->mem_pp_mem.in_pixel_fmt);
+ out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
+ if (in_fmt == RGB) {
+ if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+ /* Enable RGB->YCBCR CSC1 */
+ _init_csc(IC_TASK_POST_PROCESSOR, RGB, out_fmt, 1);
+ ic_conf |= IC_CONF_PP_CSC1;
+ }
+ }
+ if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+ if (out_fmt == RGB) {
+ /* Enable YCBCR->RGB CSC1 */
+ _init_csc(IC_TASK_POST_PROCESSOR, YCbCr, RGB, 1);
+ ic_conf |= IC_CONF_PP_CSC1;
+ } else {
+ /* TODO: Support YUV<->YCbCr conversion? */
+ }
+ }
+
+ if (params->mem_pp_mem.graphics_combine_en) {
+ ic_conf |= IC_CONF_PP_CMB;
+
+ if (!(ic_conf & IC_CONF_PP_CSC1)) {
+ /* need transparent CSC1 conversion */
+ _init_csc(IC_TASK_POST_PROCESSOR, RGB, RGB, 1);
+ ic_conf |= IC_CONF_PP_CSC1; /* Enable RGB->RGB CSC */
+ }
+
+ in_fmt = format_to_colorspace(params->mem_pp_mem.in_g_pixel_fmt);
+ out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
+ if (in_fmt == RGB) {
+ if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
+ /* Enable RGB->YCBCR CSC2 */
+ _init_csc(IC_TASK_POST_PROCESSOR, RGB, out_fmt, 2);
+ ic_conf |= IC_CONF_PP_CSC2;
+ }
+ }
+ if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
+ if (out_fmt == RGB) {
+ /* Enable YCBCR->RGB CSC2 */
+ _init_csc(IC_TASK_POST_PROCESSOR, YCbCr, RGB, 2);
+ ic_conf |= IC_CONF_PP_CSC2;
+ } else {
+ /* TODO: Support YUV<->YCbCr conversion? */
+ }
+ }
+
+ if (params->mem_pp_mem.global_alpha_en) {
+ ic_conf |= IC_CONF_IC_GLB_LOC_A;
+ reg = __raw_readl(IC_CMBP_1);
+ reg &= ~(0xff00);
+ reg |= (params->mem_pp_mem.alpha << 8);
+ __raw_writel(reg, IC_CMBP_1);
+ } else
+ ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
+
+ if (params->mem_pp_mem.key_color_en) {
+ ic_conf |= IC_CONF_KEY_COLOR_EN;
+ __raw_writel(params->mem_pp_mem.key_color,
+ IC_CMBP_2);
+ } else
+ ic_conf &= ~IC_CONF_KEY_COLOR_EN;
+ } else {
+ ic_conf &= ~IC_CONF_PP_CMB;
+ }
+
+ __raw_writel(ic_conf, IC_CONF);
+}
+
+void _ipu_ic_uninit_pp(void)
+{
+ uint32_t reg;
+
+ reg = __raw_readl(IC_CONF);
+ reg &= ~(IC_CONF_PP_EN | IC_CONF_PP_CSC1 | IC_CONF_PP_CSC2 |
+ IC_CONF_PP_CMB);
+ __raw_writel(reg, IC_CONF);
+}
+
+void _ipu_ic_init_rotate_pp(ipu_channel_params_t *params)
+{
+}
+
+void _ipu_ic_uninit_rotate_pp(void)
+{
+ uint32_t reg;
+ reg = __raw_readl(IC_CONF);
+ reg &= ~IC_CONF_PP_ROT_EN;
+ __raw_writel(reg, IC_CONF);
+}
+
+int _ipu_ic_idma_init(int dma_chan, uint16_t width, uint16_t height,
+ int burst_size, ipu_rotate_mode_t rot)
+{
+ u32 ic_idmac_1, ic_idmac_2, ic_idmac_3;
+ u32 temp_rot = bitrev8(rot) >> 5;
+ bool need_hor_flip = false;
+
+ if ((burst_size != 8) && (burst_size != 16)) {
+ dev_dbg(g_ipu_dev, "Illegal burst length for IC\n");
+ return -EINVAL;
+ }
+
+ width--;
+ height--;
+
+ if (temp_rot & 0x2) /* Need horizontal flip */
+ need_hor_flip = true;
+
+ ic_idmac_1 = __raw_readl(IC_IDMAC_1);
+ ic_idmac_2 = __raw_readl(IC_IDMAC_2);
+ ic_idmac_3 = __raw_readl(IC_IDMAC_3);
+ if (dma_chan == 22) { /* PP output - CB2 */
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16;
+
+ if (need_hor_flip)
+ ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS;
+
+ ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK;
+ ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET;
+
+ ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK;
+ ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET;
+
+ } else if (dma_chan == 11) { /* PP Input - CB5 */
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16;
+ } else if (dma_chan == 47) { /* PP Rot input */
+ ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK;
+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET;
+ }
+
+ if (dma_chan == 12) { /* PRP Input - CB6 */
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16;
+ }
+
+ if (dma_chan == 20) { /* PRP ENC output - CB0 */
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16;
+
+ if (need_hor_flip)
+ ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS;
+
+ ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK;
+ ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET;
+
+ ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK;
+ ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET;
+
+ } else if (dma_chan == 45) { /* PRP ENC Rot input */
+ ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK;
+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET;
+ }
+
+ if (dma_chan == 21) { /* PRP VF output - CB1 */
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16;
+
+ if (need_hor_flip)
+ ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS;
+
+ ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK;
+ ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET;
+
+ ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK;
+ ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET;
+
+ } else if (dma_chan == 46) { /* PRP VF Rot input */
+ ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK;
+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET;
+ }
+
+ if (dma_chan == 14) { /* PRP VF graphics combining input - CB3 */
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16;
+ } else if (dma_chan == 15) { /* PP graphics combining input - CB4 */
+ if (burst_size == 16)
+ ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16;
+ else
+ ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16;
+ }
+
+ __raw_writel(ic_idmac_1, IC_IDMAC_1);
+ __raw_writel(ic_idmac_2, IC_IDMAC_2);
+ __raw_writel(ic_idmac_3, IC_IDMAC_3);
+
+ return 0;
+}
+
+static void _init_csc(uint8_t ic_task, ipu_color_space_t in_format,
+ ipu_color_space_t out_format, int csc_index)
+{
+
+/* Y = R * .299 + G * .587 + B * .114;
+ U = R * -.169 + G * -.332 + B * .500 + 128.;
+ V = R * .500 + G * -.419 + B * -.0813 + 128.;*/
+ static const uint32_t rgb2ycbcr_coeff[4][3] = {
+ {0x004D, 0x0096, 0x001D},
+ {0x01D5, 0x01AB, 0x0080},
+ {0x0080, 0x0195, 0x01EB},
+ {0x0000, 0x0200, 0x0200}, /* A0, A1, A2 */
+ };
+
+ /* transparent RGB->RGB matrix for combining
+ */
+ static const uint32_t rgb2rgb_coeff[4][3] = {
+ {0x0080, 0x0000, 0x0000},
+ {0x0000, 0x0080, 0x0000},
+ {0x0000, 0x0000, 0x0080},
+ {0x0000, 0x0000, 0x0000}, /* A0, A1, A2 */
+ };
+
+/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
+ G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
+ B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
+ static const uint32_t ycbcr2rgb_coeff[4][3] = {
+ {149, 0, 204},
+ {149, 462, 408},
+ {149, 255, 0},
+ {8192 - 446, 266, 8192 - 554}, /* A0, A1, A2 */
+ };
+
+ uint32_t param;
+ uint32_t *base = NULL;
+
+ if (ic_task == IC_TASK_ENCODER) {
+ base = ipu_tpmem_base + 0x2008 / 4;
+ } else if (ic_task == IC_TASK_VIEWFINDER) {
+ if (csc_index == 1)
+ base = ipu_tpmem_base + 0x4028 / 4;
+ else
+ base = ipu_tpmem_base + 0x4040 / 4;
+ } else if (ic_task == IC_TASK_POST_PROCESSOR) {
+ if (csc_index == 1)
+ base = ipu_tpmem_base + 0x6060 / 4;
+ else
+ base = ipu_tpmem_base + 0x6078 / 4;
+ } else {
+ BUG();
+ }
+
+ if ((in_format == YCbCr) && (out_format == RGB)) {
+ /* Init CSC (YCbCr->RGB) */
+ param = (ycbcr2rgb_coeff[3][0] << 27) |
+ (ycbcr2rgb_coeff[0][0] << 18) |
+ (ycbcr2rgb_coeff[1][1] << 9) | ycbcr2rgb_coeff[2][2];
+ __raw_writel(param, base++);
+ /* scale = 2, sat = 0 */
+ param = (ycbcr2rgb_coeff[3][0] >> 5) | (2L << (40 - 32));
+ __raw_writel(param, base++);
+
+ param = (ycbcr2rgb_coeff[3][1] << 27) |
+ (ycbcr2rgb_coeff[0][1] << 18) |
+ (ycbcr2rgb_coeff[1][0] << 9) | ycbcr2rgb_coeff[2][0];
+ __raw_writel(param, base++);
+ param = (ycbcr2rgb_coeff[3][1] >> 5);
+ __raw_writel(param, base++);
+
+ param = (ycbcr2rgb_coeff[3][2] << 27) |
+ (ycbcr2rgb_coeff[0][2] << 18) |
+ (ycbcr2rgb_coeff[1][2] << 9) | ycbcr2rgb_coeff[2][1];
+ __raw_writel(param, base++);
+ param = (ycbcr2rgb_coeff[3][2] >> 5);
+ __raw_writel(param, base++);
+ } else if ((in_format == RGB) && (out_format == YCbCr)) {
+ /* Init CSC (RGB->YCbCr) */
+ param = (rgb2ycbcr_coeff[3][0] << 27) |
+ (rgb2ycbcr_coeff[0][0] << 18) |
+ (rgb2ycbcr_coeff[1][1] << 9) | rgb2ycbcr_coeff[2][2];
+ __raw_writel(param, base++);
+ /* scale = 1, sat = 0 */
+ param = (rgb2ycbcr_coeff[3][0] >> 5) | (1UL << 8);
+ __raw_writel(param, base++);
+
+ param = (rgb2ycbcr_coeff[3][1] << 27) |
+ (rgb2ycbcr_coeff[0][1] << 18) |
+ (rgb2ycbcr_coeff[1][0] << 9) | rgb2ycbcr_coeff[2][0];
+ __raw_writel(param, base++);
+ param = (rgb2ycbcr_coeff[3][1] >> 5);
+ __raw_writel(param, base++);
+
+ param = (rgb2ycbcr_coeff[3][2] << 27) |
+ (rgb2ycbcr_coeff[0][2] << 18) |
+ (rgb2ycbcr_coeff[1][2] << 9) | rgb2ycbcr_coeff[2][1];
+ __raw_writel(param, base++);
+ param = (rgb2ycbcr_coeff[3][2] >> 5);
+ __raw_writel(param, base++);
+ } else if ((in_format == RGB) && (out_format == RGB)) {
+ /* Init CSC */
+ param =
+ (rgb2rgb_coeff[3][0] << 27) | (rgb2rgb_coeff[0][0] << 18) |
+ (rgb2rgb_coeff[1][1] << 9) | rgb2rgb_coeff[2][2];
+ __raw_writel(param, base++);
+ /* scale = 2, sat = 0 */
+ param = (rgb2rgb_coeff[3][0] >> 5) | (2UL << 8);
+ __raw_writel(param, base++);
+
+ param =
+ (rgb2rgb_coeff[3][1] << 27) | (rgb2rgb_coeff[0][1] << 18) |
+ (rgb2rgb_coeff[1][0] << 9) | rgb2rgb_coeff[2][0];
+ __raw_writel(param, base++);
+ param = (rgb2rgb_coeff[3][1] >> 5);
+ __raw_writel(param, base++);
+
+ param =
+ (rgb2rgb_coeff[3][2] << 27) | (rgb2rgb_coeff[0][2] << 18) |
+ (rgb2rgb_coeff[1][2] << 9) | rgb2rgb_coeff[2][1];
+ __raw_writel(param, base++);
+ param = (rgb2rgb_coeff[3][2] >> 5);
+ __raw_writel(param, base++);
+ } else {
+ dev_err(g_ipu_dev, "Unsupported color space conversion\n");
+ }
+}
+
+static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize,
+ uint32_t *resizeCoeff,
+ uint32_t *downsizeCoeff)
+{
+ uint32_t tempSize;
+ uint32_t tempDownsize;
+
+ /* Input size cannot be more than 4096 */
+ /* Output size cannot be more than 1024 */
+ if ((inSize > 4096) || (outSize > 1024))
+ return false;
+
+ /* Cannot downsize more than 8:1 */
+ if ((outSize << 3) < inSize)
+ return false;
+
+ /* Compute downsizing coefficient */
+ /* Output of downsizing unit cannot be more than 1024 */
+ tempDownsize = 0;
+ tempSize = inSize;
+ while (((tempSize > 1024) || (tempSize >= outSize * 2)) &&
+ (tempDownsize < 2)) {
+ tempSize >>= 1;
+ tempDownsize++;
+ }
+ *downsizeCoeff = tempDownsize;
+
+ /* compute resizing coefficient using the following equation:
+ resizeCoeff = M*(SI -1)/(SO - 1)
+ where M = 2^13, SI - input size, SO - output size */
+ *resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
+ if (*resizeCoeff >= 16384L) {
+ dev_err(g_ipu_dev, "Warning! Overflow on resize coeff.\n");
+ *resizeCoeff = 0x3FFF;
+ }
+
+ dev_dbg(g_ipu_dev, "resizing from %u -> %u pixels, "
+ "downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize,
+ *downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0,
+ ((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff);
+
+ return true;
+}
+
+void _ipu_vdi_toggle_top_field_man()
+{
+ uint32_t reg;
+ uint32_t mask_reg;
+
+ reg = __raw_readl(VDI_C);
+ mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
+ if (mask_reg == VDI_C_TOP_FIELD_MAN_1)
+ reg &= ~VDI_C_TOP_FIELD_MAN_1;
+ else
+ reg |= VDI_C_TOP_FIELD_MAN_1;
+
+ __raw_writel(reg, VDI_C);
+}
+
diff --git a/drivers/mxc/ipu3/ipu_param_mem.h b/drivers/mxc/ipu3/ipu_param_mem.h
new file mode 100644
index 000000000000..dd30cb38c207
--- /dev/null
+++ b/drivers/mxc/ipu3/ipu_param_mem.h
@@ -0,0 +1,494 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __INCLUDE_IPU_PARAM_MEM_H__
+#define __INCLUDE_IPU_PARAM_MEM_H__
+
+#include <linux/types.h>
+#include <linux/bitrev.h>
+
+extern u32 *ipu_cpmem_base;
+
+struct ipu_ch_param_word {
+ uint32_t data[5];
+ uint32_t res[3];
+};
+
+struct ipu_ch_param {
+ struct ipu_ch_param_word word[2];
+};
+
+#define ipu_ch_param_addr(ch) (((struct ipu_ch_param *)ipu_cpmem_base) + (ch))
+
+#define _param_word(base, w) \
+ (((struct ipu_ch_param *)(base))->word[(w)].data)
+
+#define ipu_ch_param_set_field(base, w, bit, size, v) { \
+ int i = (bit) / 32; \
+ int off = (bit) % 32; \
+ _param_word(base, w)[i] |= (v) << off; \
+ if (((bit)+(size)-1)/32 > i) { \
+ _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \
+ } \
+}
+
+#define ipu_ch_param_mod_field(base, w, bit, size, v) { \
+ int i = (bit) / 32; \
+ int off = (bit) % 32; \
+ u32 mask = (1UL << size) - 1; \
+ u32 temp = _param_word(base, w)[i]; \
+ temp &= ~(mask << off); \
+ _param_word(base, w)[i] = temp | (v) << off; \
+ if (((bit)+(size)-1)/32 > i) { \
+ temp = _param_word(base, w)[i + 1]; \
+ temp &= ~(mask >> (32 - off)); \
+ _param_word(base, w)[i + 1] = \
+ temp | ((v) >> (off ? (32 - off) : 0)); \
+ } \
+}
+
+#define ipu_ch_param_read_field(base, w, bit, size) ({ \
+ u32 temp2; \
+ int i = (bit) / 32; \
+ int off = (bit) % 32; \
+ u32 mask = (1UL << size) - 1; \
+ u32 temp1 = _param_word(base, w)[i]; \
+ temp1 = mask & (temp1 >> off); \
+ if (((bit)+(size)-1)/32 > i) { \
+ temp2 = _param_word(base, w)[i + 1]; \
+ temp2 &= mask >> (off ? (32 - off) : 0); \
+ temp1 |= temp2 << (off ? (32 - off) : 0); \
+ } \
+ temp1; \
+})
+
+static inline void _ipu_ch_params_set_packing(struct ipu_ch_param *p,
+ int red_width, int red_offset,
+ int green_width, int green_offset,
+ int blue_width, int blue_offset,
+ int alpha_width, int alpha_offset)
+{
+ /* Setup red width and offset */
+ ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1);
+ ipu_ch_param_set_field(p, 1, 128, 5, red_offset);
+ /* Setup green width and offset */
+ ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1);
+ ipu_ch_param_set_field(p, 1, 133, 5, green_offset);
+ /* Setup blue width and offset */
+ ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1);
+ ipu_ch_param_set_field(p, 1, 138, 5, blue_offset);
+ /* Setup alpha width and offset */
+ ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1);
+ ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset);
+}
+
+static inline void _ipu_ch_param_dump(int ch)
+{
+ struct ipu_ch_param *p = ipu_ch_param_addr(ch);
+ pr_debug("ch %d word 0 - %08X %08X %08X %08X %08X\n", ch,
+ p->word[0].data[0], p->word[0].data[1], p->word[0].data[2],
+ p->word[0].data[3], p->word[0].data[4]);
+ pr_debug("ch %d word 1 - %08X %08X %08X %08X %08X\n", ch,
+ p->word[1].data[0], p->word[1].data[1], p->word[1].data[2],
+ p->word[1].data[3], p->word[1].data[4]);
+ pr_debug("PFS 0x%x, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 85, 4));
+ pr_debug("BPP 0x%x, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 107, 3));
+ pr_debug("NPB 0x%x\n",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 78, 7));
+
+ pr_debug("FW %d, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 125, 13));
+ pr_debug("FH %d, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 138, 12));
+ pr_debug("Stride %d\n",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 102, 14));
+
+ pr_debug("Width0 %d+1, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 116, 3));
+ pr_debug("Width1 %d+1, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 119, 3));
+ pr_debug("Width2 %d+1, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 122, 3));
+ pr_debug("Width3 %d+1, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 125, 3));
+ pr_debug("Offset0 %d, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 128, 5));
+ pr_debug("Offset1 %d, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 133, 5));
+ pr_debug("Offset2 %d, ",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 138, 5));
+ pr_debug("Offset3 %d\n",
+ ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 143, 5));
+}
+
+static inline void _ipu_ch_param_init(int ch,
+ uint32_t pixel_fmt, uint32_t width,
+ uint32_t height, uint32_t stride,
+ uint32_t u, uint32_t v,
+ uint32_t uv_stride, dma_addr_t addr0,
+ dma_addr_t addr1)
+{
+ uint32_t u_offset = 0;
+ uint32_t v_offset = 0;
+ struct ipu_ch_param params;
+
+ memset(&params, 0, sizeof(params));
+
+ ipu_ch_param_set_field(&params, 0, 125, 13, width - 1);
+
+ if ((ch == 8) || (ch == 9) || (ch == 10)) {
+ ipu_ch_param_set_field(&params, 0, 138, 12, (height / 2) - 1);
+ ipu_ch_param_set_field(&params, 1, 102, 14, (stride * 2) - 1);
+ } else {
+ ipu_ch_param_set_field(&params, 0, 138, 12, height - 1);
+ ipu_ch_param_set_field(&params, 1, 102, 14, stride - 1);
+ }
+
+ ipu_ch_param_set_field(&params, 1, 0, 29, addr0 >> 3);
+ ipu_ch_param_set_field(&params, 1, 29, 29, addr1 >> 3);
+
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_GENERIC:
+ /*Represents 8-bit Generic data */
+ ipu_ch_param_set_field(&params, 0, 107, 3, 5); /* bits/pixel */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 6); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 63); /* burst size */
+
+ break;
+ case IPU_PIX_FMT_GENERIC_32:
+ /*Represents 32-bit Generic data */
+ break;
+ case IPU_PIX_FMT_RGB565:
+ ipu_ch_param_set_field(&params, 0, 107, 3, 3); /* bits/pixel */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
+
+ _ipu_ch_params_set_packing(&params, 5, 0, 6, 5, 5, 11, 8, 16);
+ break;
+ case IPU_PIX_FMT_BGR24:
+ ipu_ch_param_set_field(&params, 0, 107, 3, 1); /* bits/pixel */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 19); /* burst size */
+
+ _ipu_ch_params_set_packing(&params, 8, 0, 8, 8, 8, 16, 8, 24);
+ break;
+ case IPU_PIX_FMT_RGB24:
+ case IPU_PIX_FMT_YUV444:
+ ipu_ch_param_set_field(&params, 0, 107, 3, 1); /* bits/pixel */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 19); /* burst size */
+
+ _ipu_ch_params_set_packing(&params, 8, 16, 8, 8, 8, 0, 8, 24);
+ break;
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_BGR32:
+ ipu_ch_param_set_field(&params, 0, 107, 3, 0); /* bits/pixel */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
+
+ _ipu_ch_params_set_packing(&params, 8, 8, 8, 16, 8, 24, 8, 0);
+ break;
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_RGB32:
+ ipu_ch_param_set_field(&params, 0, 107, 3, 0); /* bits/pixel */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
+
+ _ipu_ch_params_set_packing(&params, 8, 24, 8, 16, 8, 8, 8, 0);
+ break;
+ case IPU_PIX_FMT_ABGR32:
+ ipu_ch_param_set_field(&params, 0, 107, 3, 0); /* bits/pixel */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 7); /* pix format */
+
+ _ipu_ch_params_set_packing(&params, 8, 0, 8, 8, 8, 16, 8, 24);
+ break;
+ case IPU_PIX_FMT_UYVY:
+ ipu_ch_param_set_field(&params, 0, 107, 3, 3); /* bits/pixel */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 0xA); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
+ break;
+ case IPU_PIX_FMT_YUYV:
+ ipu_ch_param_set_field(&params, 0, 107, 3, 3); /* bits/pixel */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 0x8); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
+ break;
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ ipu_ch_param_set_field(&params, 1, 85, 4, 2); /* pix format */
+
+ if (uv_stride < stride / 2)
+ uv_stride = stride / 2;
+
+ u_offset = stride * height;
+ v_offset = u_offset + (uv_stride * height / 2);
+ if ((ch == 8) || (ch == 9) || (ch == 10)) {
+ ipu_ch_param_set_field(&params, 1, 78, 7, 15); /* burst size */
+ uv_stride = uv_stride*2;
+ } else {
+ ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
+ }
+ break;
+ case IPU_PIX_FMT_YVU422P:
+ /* BPP & pixel format */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 1); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
+
+ if (uv_stride < stride / 2)
+ uv_stride = stride / 2;
+
+ v_offset = (v == 0) ? stride * height : v;
+ u_offset = (u == 0) ? v_offset + v_offset / 2 : u;
+ break;
+ case IPU_PIX_FMT_YUV422P:
+ /* BPP & pixel format */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 1); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
+
+ if (uv_stride < stride / 2)
+ uv_stride = stride / 2;
+
+ u_offset = (u == 0) ? stride * height : u;
+ v_offset = (v == 0) ? u_offset + u_offset / 2 : v;
+ break;
+ case IPU_PIX_FMT_NV12:
+ /* BPP & pixel format */
+ ipu_ch_param_set_field(&params, 1, 85, 4, 4); /* pix format */
+ ipu_ch_param_set_field(&params, 1, 78, 7, 31); /* burst size */
+ uv_stride = stride;
+ u_offset = (u == 0) ? stride * height : u;
+ break;
+ default:
+ dev_err(g_ipu_dev, "mxc ipu: unimplemented pixel format\n");
+ break;
+ }
+ /*set burst size to 16*/
+
+
+ if (uv_stride)
+ ipu_ch_param_set_field(&params, 1, 128, 14, uv_stride - 1);
+
+ if (u > u_offset)
+ u_offset = u;
+
+ if (v > v_offset)
+ v_offset = v;
+
+ ipu_ch_param_set_field(&params, 0, 46, 22, u_offset / 8);
+ ipu_ch_param_set_field(&params, 0, 68, 22, v_offset / 8);
+
+ pr_debug("initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ch));
+ memcpy(ipu_ch_param_addr(ch), &params, sizeof(params));
+};
+
+static inline void _ipu_ch_param_set_burst_size(uint32_t ch,
+ uint16_t burst_pixels)
+{
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 78, 7,
+ burst_pixels - 1);
+};
+
+static inline int _ipu_ch_param_get_burst_size(uint32_t ch)
+{
+ return ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 78, 7) + 1;
+};
+
+static inline int _ipu_ch_param_get_bpp(uint32_t ch)
+{
+ return ipu_ch_param_read_field(ipu_ch_param_addr(ch), 0, 107, 3);
+};
+
+static inline void _ipu_ch_param_set_buffer(uint32_t ch, int bufNum,
+ dma_addr_t phyaddr)
+{
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 29 * bufNum, 29,
+ phyaddr / 8);
+};
+
+static inline void _ipu_ch_param_set_rotation(uint32_t ch,
+ ipu_rotate_mode_t rot)
+{
+ u32 temp_rot = bitrev8(rot) >> 5;
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 0, 119, 3, temp_rot);
+};
+
+static inline void _ipu_ch_param_set_block_mode(uint32_t ch)
+{
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 0, 117, 2, 1);
+};
+
+static inline void _ipu_ch_param_set_alpha_use_separate_channel(uint32_t ch,
+ bool option)
+{
+ if (option) {
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 89, 1, 1);
+ } else {
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 89, 1, 0);
+ }
+};
+
+static inline void _ipu_ch_param_set_alpha_condition_read(uint32_t ch)
+{
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 149, 1, 1);
+};
+
+static inline void _ipu_ch_param_set_alpha_buffer_memory(uint32_t ch)
+{
+ int alp_mem_idx;
+
+ switch (ch) {
+ case 14: /* PRP graphic */
+ alp_mem_idx = 0;
+ break;
+ case 15: /* PP graphic */
+ alp_mem_idx = 1;
+ break;
+ case 23: /* DP BG SYNC graphic */
+ alp_mem_idx = 4;
+ break;
+ case 27: /* DP FG SYNC graphic */
+ alp_mem_idx = 2;
+ break;
+ default:
+ dev_err(g_ipu_dev, "unsupported correlative channel of local "
+ "alpha channel\n");
+ return;
+ }
+
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 90, 3, alp_mem_idx);
+};
+
+static inline void _ipu_ch_param_set_interlaced_scan(uint32_t ch)
+{
+ u32 stride;
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), 0, 113, 1, 1);
+ stride = ipu_ch_param_read_field(ipu_ch_param_addr(ch), 1, 102, 14) + 1;
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 58, 20, stride / 8);
+ stride *= 2;
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 102, 14, stride - 1);
+};
+
+static inline void _ipu_ch_param_set_high_priority(uint32_t ch)
+{
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 1, 93, 2, 1);
+};
+
+/* IDMAC U/V offset changing support */
+/* U and V input is not affected, */
+/* the update is done by new calculation according to */
+/* vertical_offset and horizontal_offset */
+static inline void _ipu_ch_offset_update(int ch,
+ uint32_t pixel_fmt,
+ uint32_t width,
+ uint32_t height,
+ uint32_t stride,
+ uint32_t u,
+ uint32_t v,
+ uint32_t uv_stride,
+ uint32_t vertical_offset,
+ uint32_t horizontal_offset)
+{
+ uint32_t u_offset = 0;
+ uint32_t v_offset = 0;
+ uint32_t u_fix = 0;
+ uint32_t v_fix = 0;
+
+ switch (pixel_fmt) {
+ case IPU_PIX_FMT_GENERIC:
+ case IPU_PIX_FMT_GENERIC_32:
+ case IPU_PIX_FMT_RGB565:
+ case IPU_PIX_FMT_BGR24:
+ case IPU_PIX_FMT_RGB24:
+ case IPU_PIX_FMT_YUV444:
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_BGR32:
+ case IPU_PIX_FMT_RGBA32:
+ case IPU_PIX_FMT_RGB32:
+ case IPU_PIX_FMT_ABGR32:
+ case IPU_PIX_FMT_UYVY:
+ case IPU_PIX_FMT_YUYV:
+ break;
+
+ case IPU_PIX_FMT_YUV420P2:
+ case IPU_PIX_FMT_YUV420P:
+ if (uv_stride < stride / 2)
+ uv_stride = stride / 2;
+
+ u_fix = u + (uv_stride * vertical_offset / 2) + horizontal_offset / 4;
+ v_fix = v + (uv_stride * vertical_offset / 2) + horizontal_offset / 4;
+ u_offset = (u == 0) ? stride * (height - vertical_offset - 1) +
+ (stride - horizontal_offset) +
+ (uv_stride * vertical_offset / 2) +
+ horizontal_offset / 2 : u_fix;
+ v_offset = (v == 0) ? u_offset + (uv_stride * height / 2) : v_fix;
+
+ break;
+ case IPU_PIX_FMT_YVU422P:
+ if (uv_stride < stride / 2)
+ uv_stride = stride / 2;
+
+ u_fix = u + (uv_stride * vertical_offset) + horizontal_offset / 2;
+ v_fix = v + (uv_stride * vertical_offset) + horizontal_offset / 2;
+
+ v_offset = (v == 0) ? stride * (height - vertical_offset - 1) +
+ (stride - horizontal_offset) +
+ (uv_stride * vertical_offset) +
+ horizontal_offset / 2 : v_fix;
+ u_offset = (u == 0) ? v_offset + uv_stride * height : u_fix;
+ break;
+ case IPU_PIX_FMT_YUV422P:
+ if (uv_stride < stride / 2)
+ uv_stride = stride / 2;
+
+ u_fix = u + (uv_stride * vertical_offset) + horizontal_offset / 2;
+ v_fix = v + (uv_stride * vertical_offset) + horizontal_offset / 2;
+
+ u_offset = (u == 0) ? stride * (height - vertical_offset - 1) +
+ (stride - horizontal_offset) +
+ (uv_stride * vertical_offset) +
+ horizontal_offset / 2 : u_fix;
+ v_offset = (v == 0) ? u_offset + uv_stride * height : v_fix;
+ break;
+
+ case IPU_PIX_FMT_NV12:
+ uv_stride = stride;
+ u_fix = u + (uv_stride * vertical_offset) + horizontal_offset;
+ u_offset = (u == 0) ? stride * (height - vertical_offset - 1) +
+ (stride - horizontal_offset) +
+ (uv_stride * vertical_offset) +
+ horizontal_offset : u_fix;
+
+ break;
+ default:
+ dev_err(g_ipu_dev, "mxc ipu: unimplemented pixel format\n");
+ break;
+ }
+
+
+
+ if (u_fix > u_offset)
+ u_offset = u_fix;
+
+ if (v_fix > v_offset)
+ v_offset = v_fix;
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 0, 46, 22, u_offset / 8);
+ ipu_ch_param_mod_field(ipu_ch_param_addr(ch), 0, 68, 22, v_offset / 8);
+
+};
+
+static inline void _ipu_ch_params_set_alpha_width(uint32_t ch, int alpha_width)
+{
+ ipu_ch_param_set_field(ipu_ch_param_addr(ch), 1, 125, 3, alpha_width - 1);
+};
+
+#endif
diff --git a/drivers/mxc/ipu3/ipu_prv.h b/drivers/mxc/ipu3/ipu_prv.h
new file mode 100644
index 000000000000..c9884068283f
--- /dev/null
+++ b/drivers/mxc/ipu3/ipu_prv.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __INCLUDE_IPU_PRV_H__
+#define __INCLUDE_IPU_PRV_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <mach/hardware.h>
+
+/* Globals */
+extern struct device *g_ipu_dev;
+extern spinlock_t ipu_lock;
+extern bool g_ipu_clk_enabled;
+extern struct clk *g_ipu_clk;
+extern struct clk *g_di_clk[2];
+extern struct clk *g_csi_clk[2];
+extern unsigned char g_dc_di_assignment[];
+extern int g_ipu_hw_rev;
+
+#define IDMA_CHAN_INVALID 0xFF
+
+struct ipu_channel {
+ u8 video_in_dma;
+ u8 alpha_in_dma;
+ u8 graph_in_dma;
+ u8 out_dma;
+};
+
+int register_ipu_device(void);
+ipu_color_space_t format_to_colorspace(uint32_t fmt);
+bool ipu_pixel_format_has_alpha(uint32_t fmt);
+
+void ipu_dump_registers(void);
+
+uint32_t _ipu_channel_status(ipu_channel_t channel);
+
+void _ipu_init_dc_mappings(void);
+int _ipu_dp_init(ipu_channel_t channel, uint32_t in_pixel_fmt,
+ uint32_t out_pixel_fmt);
+void _ipu_dp_uninit(ipu_channel_t channel);
+void _ipu_dc_init(int dc_chan, int di, bool interlaced);
+void _ipu_dc_uninit(int dc_chan);
+void _ipu_dp_dc_enable(ipu_channel_t channel);
+void _ipu_dp_dc_disable(ipu_channel_t channel, bool swap);
+void _ipu_dmfc_init(void);
+void _ipu_dmfc_set_wait4eot(int dma_chan, int width);
+int _ipu_chan_is_interlaced(ipu_channel_t channel);
+
+void _ipu_ic_enable_task(ipu_channel_t channel);
+void _ipu_ic_disable_task(ipu_channel_t channel);
+void _ipu_ic_init_prpvf(ipu_channel_params_t *params, bool src_is_csi);
+void _ipu_vdi_init(ipu_channel_t channel, ipu_channel_params_t *params);
+void _ipu_vdi_uninit(void);
+void _ipu_ic_uninit_prpvf(void);
+void _ipu_ic_init_rotate_vf(ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_vf(void);
+void _ipu_ic_init_csi(ipu_channel_params_t *params);
+void _ipu_ic_uninit_csi(void);
+void _ipu_ic_init_prpenc(ipu_channel_params_t *params, bool src_is_csi);
+void _ipu_ic_uninit_prpenc(void);
+void _ipu_ic_init_rotate_enc(ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_enc(void);
+void _ipu_ic_init_pp(ipu_channel_params_t *params);
+void _ipu_ic_uninit_pp(void);
+void _ipu_ic_init_rotate_pp(ipu_channel_params_t *params);
+void _ipu_ic_uninit_rotate_pp(void);
+int _ipu_ic_idma_init(int dma_chan, uint16_t width, uint16_t height,
+ int burst_size, ipu_rotate_mode_t rot);
+void _ipu_vdi_toggle_top_field_man(void);
+int _ipu_csi_init(ipu_channel_t channel, uint32_t csi);
+void ipu_csi_set_test_generator(bool active, uint32_t r_value,
+ uint32_t g_value, uint32_t b_value,
+ uint32_t pix_clk, uint32_t csi);
+void _ipu_csi_ccir_err_detection_enable(uint32_t csi);
+void _ipu_csi_ccir_err_detection_disable(uint32_t csi);
+void _ipu_smfc_init(ipu_channel_t channel, uint32_t mipi_id, uint32_t csi);
+void _ipu_smfc_set_burst_size(ipu_channel_t channel, uint32_t bs);
+void _ipu_dp_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3]);
+
+#endif /* __INCLUDE_IPU_PRV_H__ */
diff --git a/drivers/mxc/ipu3/ipu_regs.h b/drivers/mxc/ipu3/ipu_regs.h
new file mode 100644
index 000000000000..f4b266aef794
--- /dev/null
+++ b/drivers/mxc/ipu3/ipu_regs.h
@@ -0,0 +1,651 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * @file ipu_regs.h
+ *
+ * @brief IPU Register definitions
+ *
+ * @ingroup IPU
+ */
+#ifndef __IPU_REGS_INCLUDED__
+#define __IPU_REGS_INCLUDED__
+
+#define IPU_DISP0_BASE 0x00000000
+#define IPU_MCU_T_DEFAULT 8
+#define IPU_DISP1_BASE (IPU_MCU_T_DEFAULT << 25)
+#define IPU_CM_REG_BASE 0x1E000000
+#define IPU_IDMAC_REG_BASE 0x1E008000
+#define IPU_ISP_REG_BASE 0x1E010000
+#define IPU_DP_REG_BASE 0x1E018000
+#define IPU_IC_REG_BASE 0x1E020000
+#define IPU_IRT_REG_BASE 0x1E028000
+#define IPU_CSI0_REG_BASE 0x1E030000
+#define IPU_CSI1_REG_BASE 0x1E038000
+#define IPU_DI0_REG_BASE 0x1E040000
+#define IPU_DI1_REG_BASE 0x1E048000
+#define IPU_SMFC_REG_BASE 0x1E050000
+#define IPU_DC_REG_BASE 0x1E058000
+#define IPU_DMFC_REG_BASE 0x1E060000
+#define IPU_CPMEM_REG_BASE 0x1F000000
+#define IPU_LUT_REG_BASE 0x1F020000
+#define IPU_SRM_REG_BASE 0x1F040000
+#define IPU_TPM_REG_BASE 0x1F060000
+#define IPU_DC_TMPL_REG_BASE 0x1F080000
+#define IPU_ISP_TBPR_REG_BASE 0x1F0C0000
+#define IPU_VDI_REG_BASE 0x1E068000
+
+
+extern u32 *ipu_cm_reg;
+extern u32 *ipu_idmac_reg;
+extern u32 *ipu_dp_reg;
+extern u32 *ipu_ic_reg;
+extern u32 *ipu_dc_reg;
+extern u32 *ipu_dc_tmpl_reg;
+extern u32 *ipu_dmfc_reg;
+extern u32 *ipu_di_reg[];
+extern u32 *ipu_smfc_reg;
+extern u32 *ipu_csi_reg[];
+extern u32 *ipu_tpmem_base;
+extern u32 *ipu_disp_base[];
+extern u32 *ipu_vdi_reg;
+
+/* Register addresses */
+/* IPU Common registers */
+#define IPU_CONF (ipu_cm_reg)
+
+#define IPU_SRM_PRI1 (ipu_cm_reg + 0x00A0/4)
+#define IPU_SRM_PRI2 (ipu_cm_reg + 0x00A4/4)
+#define IPU_FS_PROC_FLOW1 (ipu_cm_reg + 0x00A8/4)
+#define IPU_FS_PROC_FLOW2 (ipu_cm_reg + 0x00AC/4)
+#define IPU_FS_PROC_FLOW3 (ipu_cm_reg + 0x00B0/4)
+#define IPU_FS_DISP_FLOW1 (ipu_cm_reg + 0x00B4/4)
+#define IPU_FS_DISP_FLOW2 (ipu_cm_reg + 0x00B8/4)
+#define IPU_SKIP (ipu_cm_reg + 0x00BC/4)
+#define IPU_DISP_ALT_CONF (ipu_cm_reg + 0x00C0/4)
+#define IPU_DISP_GEN (ipu_cm_reg + 0x00C4/4)
+#define IPU_DISP_ALT1 (ipu_cm_reg + 0x00C8/4)
+#define IPU_DISP_ALT2 (ipu_cm_reg + 0x00CC/4)
+#define IPU_DISP_ALT3 (ipu_cm_reg + 0x00D0/4)
+#define IPU_DISP_ALT4 (ipu_cm_reg + 0x00D4/4)
+#define IPU_SNOOP (ipu_cm_reg + 0x00D8/4)
+#define IPU_MEM_RST (ipu_cm_reg + 0x00DC/4)
+#define IPU_PM (ipu_cm_reg + 0x00E0/4)
+#define IPU_GPR (ipu_cm_reg + 0x00E4/4)
+#define IPU_CHA_DB_MODE_SEL(ch) (ipu_cm_reg + 0x0150/4 + (ch / 32))
+#define IPU_ALT_CHA_DB_MODE_SEL(ch) (ipu_cm_reg + 0x0168/4 + (ch / 32))
+#define IPU_CHA_CUR_BUF(ch) ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x023C/4 + (ch / 32)) : \
+ (ipu_cm_reg + 0x0124/4 + (ch / 32)); })
+#define IPU_ALT_CUR_BUF0 ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x0244/4) : \
+ (ipu_cm_reg + 0x012C/4); })
+#define IPU_ALT_CUR_BUF1 ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x0248/4) : \
+ (ipu_cm_reg + 0x0130/4); })
+#define IPU_SRM_STAT ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x024C/4) : \
+ (ipu_cm_reg + 0x0134/4); })
+#define IPU_PROC_TASK_STAT ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x0250/4) : \
+ (ipu_cm_reg + 0x0138/4); })
+#define IPU_DISP_TASK_STAT ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x0254/4) : \
+ (ipu_cm_reg + 0x013C/4); })
+#define IPU_CHA_BUF0_RDY(ch) ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x0268/4 + (ch / 32)) : \
+ (ipu_cm_reg + 0x0140/4 + (ch / 32)); })
+#define IPU_CHA_BUF1_RDY(ch) ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x0270/4 + (ch / 32)) : \
+ (ipu_cm_reg + 0x0148/4 + (ch / 32)); })
+#define IPU_ALT_CHA_BUF0_RDY(ch) ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x0278/4 + (ch / 32)) : \
+ (ipu_cm_reg + 0x0158/4 + (ch / 32)); })
+#define IPU_ALT_CHA_BUF1_RDY(ch) ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x0280/4 + (ch / 32)) : \
+ (ipu_cm_reg + 0x0160/4 + (ch / 32)); })
+
+#define IPU_INT_CTRL(n) (ipu_cm_reg + 0x003C/4 + ((n) - 1))
+#define IPU_INT_CTRL_IRQ(irq) IPU_INT_CTRL(((irq) / 32))
+#define IPU_INT_STAT_IRQ(irq) IPU_INT_STAT(((irq) / 32))
+#define IPU_INT_STAT(n) ({g_ipu_hw_rev == 2 ? \
+ (ipu_cm_reg + 0x0200/4 + ((n) - 1)) : \
+ (ipu_cm_reg + 0x00E8/4 + ((n) - 1)); })
+
+#define IPUIRQ_2_STATREG(irq) (IPU_INT_STAT(1) + ((irq) / 32))
+#define IPUIRQ_2_CTRLREG(irq) (IPU_INT_CTRL(1) + ((irq) / 32))
+#define IPUIRQ_2_MASK(irq) (1UL << ((irq) & 0x1F))
+
+#define VDI_FSIZE (ipu_vdi_reg)
+#define VDI_C (ipu_vdi_reg + 0x0004/4)
+
+/* CMOS Sensor Interface Registers */
+#define CSI_SENS_CONF(csi) (ipu_csi_reg[csi])
+#define CSI_SENS_FRM_SIZE(csi) (ipu_csi_reg[csi] + 0x0004/4)
+#define CSI_ACT_FRM_SIZE(csi) (ipu_csi_reg[csi] + 0x0008/4)
+#define CSI_OUT_FRM_CTRL(csi) (ipu_csi_reg[csi] + 0x000C/4)
+#define CSI_TST_CTRL(csi) (ipu_csi_reg[csi] + 0x0010/4)
+#define CSI_CCIR_CODE_1(csi) (ipu_csi_reg[csi] + 0x0014/4)
+#define CSI_CCIR_CODE_2(csi) (ipu_csi_reg[csi] + 0x0018/4)
+#define CSI_CCIR_CODE_3(csi) (ipu_csi_reg[csi] + 0x001C/4)
+#define CSI_MIPI_DI(csi) (ipu_csi_reg[csi] + 0x0020/4)
+#define CSI_SKIP(csi) (ipu_csi_reg[csi] + 0x0024/4)
+#define CSI_CPD_CTRL(csi) (ipu_csi_reg[csi] + 0x0028/4)
+#define CSI_CPD_RC(csi, n) (ipu_csi_reg[csi] + 0x002C/4 + n)
+#define CSI_CPD_RS(csi, n) (ipu_csi_reg[csi] + 0x004C/4 + n)
+#define CSI_CPD_GRC(csi, n) (ipu_csi_reg[csi] + 0x005C/4 + n)
+#define CSI_CPD_GRS(csi, n) (ipu_csi_reg[csi] + 0x007C/4 + n)
+#define CSI_CPD_GBC(csi, n) (ipu_csi_reg[csi] + 0x008C/4 + n)
+#define CSI_CPD_GBS(csi, n) (ipu_csi_reg[csi] + 0x00AC/4 + n)
+#define CSI_CPD_BC(csi, n) (ipu_csi_reg[csi] + 0x00BC/4 + n)
+#define CSI_CPD_BS(csi, n) (ipu_csi_reg[csi] + 0x00DC/4 + n)
+#define CSI_CPD_OFFSET1(csi) (ipu_csi_reg[csi] + 0x00EC/4)
+#define CSI_CPD_OFFSET2(csi) (ipu_csi_reg[csi] + 0x00F0/4)
+
+/*SMFC Registers */
+#define SMFC_MAP (ipu_smfc_reg)
+#define SMFC_WMC (ipu_smfc_reg + 0x0004/4)
+#define SMFC_BS (ipu_smfc_reg + 0x0008/4)
+
+/* Image Converter Registers */
+#define IC_CONF (ipu_ic_reg)
+#define IC_PRP_ENC_RSC (ipu_ic_reg + 0x0004/4)
+#define IC_PRP_VF_RSC (ipu_ic_reg + 0x0008/4)
+#define IC_PP_RSC (ipu_ic_reg + 0x000C/4)
+#define IC_CMBP_1 (ipu_ic_reg + 0x0010/4)
+#define IC_CMBP_2 (ipu_ic_reg + 0x0014/4)
+#define IC_IDMAC_1 (ipu_ic_reg + 0x0018/4)
+#define IC_IDMAC_2 (ipu_ic_reg + 0x001C/4)
+#define IC_IDMAC_3 (ipu_ic_reg + 0x0020/4)
+#define IC_IDMAC_4 (ipu_ic_reg + 0x0024/4)
+
+#define IDMAC_CONF (ipu_idmac_reg + 0x0000)
+#define IDMAC_CHA_EN(ch) (ipu_idmac_reg + 0x0004/4 + (ch/32))
+#define IDMAC_SEP_ALPHA (ipu_idmac_reg + 0x000C/4)
+#define IDMAC_ALT_SEP_ALPHA (ipu_idmac_reg + 0x0010/4)
+#define IDMAC_CHA_PRI(ch) (ipu_idmac_reg + 0x0014/4 + (ch/32))
+#define IDMAC_WM_EN(ch) (ipu_idmac_reg + 0x001C/4 + (ch/32))
+#define IDMAC_CH_LOCK_EN_1 ({g_ipu_hw_rev == 2 ? \
+ (ipu_idmac_reg + 0x0024/4) : 0; })
+#define IDMAC_CH_LOCK_EN_2 ({g_ipu_hw_rev == 2 ? \
+ (ipu_idmac_reg + 0x0028/4) : \
+ (ipu_idmac_reg + 0x0024/4); })
+#define IDMAC_SUB_ADDR_0 ({g_ipu_hw_rev == 2 ? \
+ (ipu_idmac_reg + 0x002C/4) : \
+ (ipu_idmac_reg + 0x0028/4); })
+#define IDMAC_SUB_ADDR_1 ({g_ipu_hw_rev == 2 ? \
+ (ipu_idmac_reg + 0x0030/4) : \
+ (ipu_idmac_reg + 0x002C/4); })
+#define IDMAC_SUB_ADDR_2 ({g_ipu_hw_rev == 2 ? \
+ (ipu_idmac_reg + 0x0034/4) : \
+ (ipu_idmac_reg + 0x0030/4); })
+#define IDMAC_BAND_EN(ch) ({g_ipu_hw_rev == 2 ? \
+ (ipu_idmac_reg + 0x0040/4 + (ch/32)) : \
+ (ipu_idmac_reg + 0x0034/4 + (ch/32)); })
+#define IDMAC_CHA_BUSY(ch) ({g_ipu_hw_rev == 2 ? \
+ (ipu_idmac_reg + 0x0100/4 + (ch/32)) : \
+ (ipu_idmac_reg + 0x0040/4 + (ch/32)); })
+
+#define DI_GENERAL(di) (ipu_di_reg[di])
+#define DI_BS_CLKGEN0(di) (ipu_di_reg[di] + 0x0004/4)
+#define DI_BS_CLKGEN1(di) (ipu_di_reg[di] + 0x0008/4)
+
+#define DI_SW_GEN0(di, gen) (ipu_di_reg[di] + 0x000C/4 + (gen - 1))
+#define DI_SW_GEN1(di, gen) (ipu_di_reg[di] + 0x0030/4 + (gen - 1))
+#define DI_STP_REP(di, gen) (ipu_di_reg[di] + 0x0148/4 + (gen - 1)/2)
+#define DI_SYNC_AS_GEN(di) (ipu_di_reg[di] + 0x0054/4)
+#define DI_DW_GEN(di, gen) (ipu_di_reg[di] + 0x0058/4 + gen)
+#define DI_DW_SET(di, gen, set) (ipu_di_reg[di] + 0x0088/4 + gen + 0xC*set)
+#define DI_SER_CONF(di) (ipu_di_reg[di] + 0x015C/4)
+#define DI_SSC(di) (ipu_di_reg[di] + 0x0160/4)
+#define DI_POL(di) (ipu_di_reg[di] + 0x0164/4)
+#define DI_AW0(di) (ipu_di_reg[di] + 0x0168/4)
+#define DI_AW1(di) (ipu_di_reg[di] + 0x016C/4)
+#define DI_SCR_CONF(di) (ipu_di_reg[di] + 0x0170/4)
+#define DI_STAT(di) (ipu_di_reg[di] + 0x0174/4)
+
+#define DMFC_RD_CHAN (ipu_dmfc_reg)
+#define DMFC_WR_CHAN (ipu_dmfc_reg + 0x0004/4)
+#define DMFC_WR_CHAN_DEF (ipu_dmfc_reg + 0x0008/4)
+#define DMFC_DP_CHAN (ipu_dmfc_reg + 0x000C/4)
+#define DMFC_DP_CHAN_DEF (ipu_dmfc_reg + 0x0010/4)
+#define DMFC_GENERAL1 (ipu_dmfc_reg + 0x0014/4)
+#define DMFC_GENERAL2 (ipu_dmfc_reg + 0x0018/4)
+#define DMFC_IC_CTRL (ipu_dmfc_reg + 0x001C/4)
+#define DMFC_STAT (ipu_dmfc_reg + 0x0020/4)
+
+#define DC_MAP_CONF_PTR(n) (ipu_dc_reg + 0x0108/4 + n/2)
+#define DC_MAP_CONF_VAL(n) (ipu_dc_reg + 0x0144/4 + n/2)
+
+#define _RL_CH_2_OFFSET(ch) ((ch == 0) ? 8 : ( \
+ (ch == 1) ? 0x24 : ( \
+ (ch == 2) ? 0x40 : ( \
+ (ch == 5) ? 0x64 : ( \
+ (ch == 6) ? 0x80 : ( \
+ (ch == 8) ? 0x9C : ( \
+ (ch == 9) ? 0xBC : (-1))))))))
+#define DC_RL_CH(ch, evt) (ipu_dc_reg + _RL_CH_2_OFFSET(ch)/4 + evt/2)
+
+#define DC_EVT_NF 0
+#define DC_EVT_NL 1
+#define DC_EVT_EOF 2
+#define DC_EVT_NFIELD 3
+#define DC_EVT_EOL 4
+#define DC_EVT_EOFIELD 5
+#define DC_EVT_NEW_ADDR 6
+#define DC_EVT_NEW_CHAN 7
+#define DC_EVT_NEW_DATA 8
+
+#define DC_EVT_NEW_ADDR_W_0 0
+#define DC_EVT_NEW_ADDR_W_1 1
+#define DC_EVT_NEW_CHAN_W_0 2
+#define DC_EVT_NEW_CHAN_W_1 3
+#define DC_EVT_NEW_DATA_W_0 4
+#define DC_EVT_NEW_DATA_W_1 5
+#define DC_EVT_NEW_ADDR_R_0 6
+#define DC_EVT_NEW_ADDR_R_1 7
+#define DC_EVT_NEW_CHAN_R_0 8
+#define DC_EVT_NEW_CHAN_R_1 9
+#define DC_EVT_NEW_DATA_R_0 10
+#define DC_EVT_NEW_DATA_R_1 11
+
+#define dc_ch_offset(ch) \
+({ \
+ const u8 _offset[] = { \
+ 0, 0x1C, 0x38, 0x54, 0x58, 0x5C, 0x78, 0, 0x94, 0xB4}; \
+ _offset[ch]; \
+})
+#define DC_WR_CH_CONF(ch) (ipu_dc_reg + dc_ch_offset(ch)/4)
+#define DC_WR_CH_ADDR(ch) (ipu_dc_reg + dc_ch_offset(ch)/4 + 4/4)
+
+#define DC_WR_CH_CONF_1 (ipu_dc_reg + 0x001C/4)
+#define DC_WR_CH_ADDR_1 (ipu_dc_reg + 0x0020/4)
+#define DC_WR_CH_CONF_5 (ipu_dc_reg + 0x005C/4)
+#define DC_WR_CH_ADDR_5 (ipu_dc_reg + 0x0060/4)
+#define DC_GEN (ipu_dc_reg + 0x00D4/4)
+#define DC_DISP_CONF1(disp) (ipu_dc_reg + 0x00D8/4 + disp)
+#define DC_DISP_CONF2(disp) (ipu_dc_reg + 0x00E8/4 + disp)
+#define DC_STAT (ipu_dc_reg + 0x01C8/4)
+#define DC_UGDE_0(evt) (ipu_dc_reg + 0x0174/4 + evt*4)
+#define DC_UGDE_1(evt) (ipu_dc_reg + 0x0178/4 + evt*4)
+#define DC_UGDE_2(evt) (ipu_dc_reg + 0x017C/4 + evt*4)
+#define DC_UGDE_3(evt) (ipu_dc_reg + 0x0180/4 + evt*4)
+
+#define DP_SYNC 0
+#define DP_ASYNC0 0x60
+#define DP_ASYNC1 0xBC
+#define DP_COM_CONF(flow) (ipu_dp_reg + flow/4)
+#define DP_GRAPH_WIND_CTRL(flow) (ipu_dp_reg + 0x0004/4 + flow/4)
+#define DP_FG_POS(flow) (ipu_dp_reg + 0x0008/4 + flow/4)
+#define DP_CSC_A_0(flow) (ipu_dp_reg + 0x0044/4 + flow/4)
+#define DP_CSC_A_1(flow) (ipu_dp_reg + 0x0048/4 + flow/4)
+#define DP_CSC_A_2(flow) (ipu_dp_reg + 0x004C/4 + flow/4)
+#define DP_CSC_A_3(flow) (ipu_dp_reg + 0x0050/4 + flow/4)
+#define DP_CSC_0(flow) (ipu_dp_reg + 0x0054/4 + flow/4)
+#define DP_CSC_1(flow) (ipu_dp_reg + 0x0058/4 + flow/4)
+
+enum {
+ IPU_CONF_CSI0_EN = 0x00000001,
+ IPU_CONF_CSI1_EN = 0x00000002,
+ IPU_CONF_IC_EN = 0x00000004,
+ IPU_CONF_ROT_EN = 0x00000008,
+ IPU_CONF_ISP_EN = 0x00000010,
+ IPU_CONF_DP_EN = 0x00000020,
+ IPU_CONF_DI0_EN = 0x00000040,
+ IPU_CONF_DI1_EN = 0x00000080,
+ IPU_CONF_DMFC_EN = 0x00000400,
+ IPU_CONF_SMFC_EN = 0x00000100,
+ IPU_CONF_DC_EN = 0x00000200,
+ IPU_CONF_VDI_EN = 0x00001000,
+ IPU_CONF_IDMAC_DIS = 0x00400000,
+ IPU_CONF_IC_DMFC_SEL = 0x02000000,
+ IPU_CONF_IC_DMFC_SYNC = 0x04000000,
+ IPU_CONF_VDI_DMFC_SYNC = 0x08000000,
+ IPU_CONF_CSI0_DATA_SOURCE = 0x10000000,
+ IPU_CONF_CSI0_DATA_SOURCE_OFFSET = 28,
+ IPU_CONF_CSI1_DATA_SOURCE = 0x20000000,
+ IPU_CONF_IC_INPUT = 0x40000000,
+ IPU_CONF_CSI_SEL = 0x80000000,
+
+ DI0_COUNTER_RELEASE = 0x01000000,
+ DI1_COUNTER_RELEASE = 0x02000000,
+
+ FS_PRPVF_ROT_SRC_SEL_MASK = 0x00000F00,
+ FS_PRPVF_ROT_SRC_SEL_OFFSET = 8,
+ FS_PRPENC_ROT_SRC_SEL_MASK = 0x0000000F,
+ FS_PRPENC_ROT_SRC_SEL_OFFSET = 0,
+ FS_PP_ROT_SRC_SEL_MASK = 0x000F0000,
+ FS_PP_ROT_SRC_SEL_OFFSET = 16,
+ FS_PP_SRC_SEL_MASK = 0x0000F000,
+ FS_PP_SRC_SEL_OFFSET = 12,
+ FS_PRP_SRC_SEL_MASK = 0x0F000000,
+ FS_PRP_SRC_SEL_OFFSET = 24,
+ FS_VF_IN_VALID = 0x80000000,
+ FS_ENC_IN_VALID = 0x40000000,
+ FS_VDI_SRC_SEL_MASK = 0x30000000,
+ FS_VDI_SRC_SEL_OFFSET = 28,
+
+
+ FS_PRPENC_DEST_SEL_MASK = 0x0000000F,
+ FS_PRPENC_DEST_SEL_OFFSET = 0,
+ FS_PRPVF_DEST_SEL_MASK = 0x000000F0,
+ FS_PRPVF_DEST_SEL_OFFSET = 4,
+ FS_PRPVF_ROT_DEST_SEL_MASK = 0x00000F00,
+ FS_PRPVF_ROT_DEST_SEL_OFFSET = 8,
+ FS_PP_DEST_SEL_MASK = 0x0000F000,
+ FS_PP_DEST_SEL_OFFSET = 12,
+ FS_PP_ROT_DEST_SEL_MASK = 0x000F0000,
+ FS_PP_ROT_DEST_SEL_OFFSET = 16,
+ FS_PRPENC_ROT_DEST_SEL_MASK = 0x00F00000,
+ FS_PRPENC_ROT_DEST_SEL_OFFSET = 20,
+
+ FS_SMFC0_DEST_SEL_MASK = 0x0000000F,
+ FS_SMFC0_DEST_SEL_OFFSET = 0,
+ FS_SMFC1_DEST_SEL_MASK = 0x00000070,
+ FS_SMFC1_DEST_SEL_OFFSET = 4,
+ FS_SMFC2_DEST_SEL_MASK = 0x00000780,
+ FS_SMFC2_DEST_SEL_OFFSET = 7,
+ FS_SMFC3_DEST_SEL_MASK = 0x00003800,
+ FS_SMFC3_DEST_SEL_OFFSET = 11,
+
+ FS_DC1_SRC_SEL_MASK = 0x00F00000,
+ FS_DC1_SRC_SEL_OFFSET = 20,
+ FS_DC2_SRC_SEL_MASK = 0x000F0000,
+ FS_DC2_SRC_SEL_OFFSET = 16,
+ FS_DP_SYNC0_SRC_SEL_MASK = 0x0000000F,
+ FS_DP_SYNC0_SRC_SEL_OFFSET = 0,
+ FS_DP_SYNC1_SRC_SEL_MASK = 0x000000F0,
+ FS_DP_SYNC1_SRC_SEL_OFFSET = 4,
+ FS_DP_ASYNC0_SRC_SEL_MASK = 0x00000F00,
+ FS_DP_ASYNC0_SRC_SEL_OFFSET = 8,
+ FS_DP_ASYNC1_SRC_SEL_MASK = 0x0000F000,
+ FS_DP_ASYNC1_SRC_SEL_OFFSET = 12,
+
+ FS_AUTO_REF_PER_MASK = 0,
+ FS_AUTO_REF_PER_OFFSET = 16,
+
+ TSTAT_VF_MASK = 0x0000000C,
+ TSTAT_VF_OFFSET = 2,
+ TSTAT_VF_ROT_MASK = 0x00000300,
+ TSTAT_VF_ROT_OFFSET = 8,
+ TSTAT_ENC_MASK = 0x00000003,
+ TSTAT_ENC_OFFSET = 0,
+ TSTAT_ENC_ROT_MASK = 0x000000C0,
+ TSTAT_ENC_ROT_OFFSET = 6,
+ TSTAT_PP_MASK = 0x00000030,
+ TSTAT_PP_OFFSET = 4,
+ TSTAT_PP_ROT_MASK = 0x00000C00,
+ TSTAT_PP_ROT_OFFSET = 10,
+
+ TASK_STAT_IDLE = 0,
+ TASK_STAT_ACTIVE = 1,
+ TASK_STAT_WAIT4READY = 2,
+
+ /* Image Converter Register bits */
+ IC_CONF_PRPENC_EN = 0x00000001,
+ IC_CONF_PRPENC_CSC1 = 0x00000002,
+ IC_CONF_PRPENC_ROT_EN = 0x00000004,
+ IC_CONF_PRPVF_EN = 0x00000100,
+ IC_CONF_PRPVF_CSC1 = 0x00000200,
+ IC_CONF_PRPVF_CSC2 = 0x00000400,
+ IC_CONF_PRPVF_CMB = 0x00000800,
+ IC_CONF_PRPVF_ROT_EN = 0x00001000,
+ IC_CONF_PP_EN = 0x00010000,
+ IC_CONF_PP_CSC1 = 0x00020000,
+ IC_CONF_PP_CSC2 = 0x00040000,
+ IC_CONF_PP_CMB = 0x00080000,
+ IC_CONF_PP_ROT_EN = 0x00100000,
+ IC_CONF_IC_GLB_LOC_A = 0x10000000,
+ IC_CONF_KEY_COLOR_EN = 0x20000000,
+ IC_CONF_RWS_EN = 0x40000000,
+ IC_CONF_CSI_MEM_WR_EN = 0x80000000,
+
+ IC_IDMAC_1_CB0_BURST_16 = 0x00000001,
+ IC_IDMAC_1_CB1_BURST_16 = 0x00000002,
+ IC_IDMAC_1_CB2_BURST_16 = 0x00000004,
+ IC_IDMAC_1_CB3_BURST_16 = 0x00000008,
+ IC_IDMAC_1_CB4_BURST_16 = 0x00000010,
+ IC_IDMAC_1_CB5_BURST_16 = 0x00000020,
+ IC_IDMAC_1_CB6_BURST_16 = 0x00000040,
+ IC_IDMAC_1_CB7_BURST_16 = 0x00000080,
+ IC_IDMAC_1_PRPENC_ROT_MASK = 0x00003800,
+ IC_IDMAC_1_PRPENC_ROT_OFFSET = 11,
+ IC_IDMAC_1_PRPVF_ROT_MASK = 0x0001C000,
+ IC_IDMAC_1_PRPVF_ROT_OFFSET = 14,
+ IC_IDMAC_1_PP_ROT_MASK = 0x000E0000,
+ IC_IDMAC_1_PP_ROT_OFFSET = 17,
+ IC_IDMAC_1_PP_FLIP_RS = 0x00400000,
+ IC_IDMAC_1_PRPVF_FLIP_RS = 0x00200000,
+ IC_IDMAC_1_PRPENC_FLIP_RS = 0x00100000,
+
+ IC_IDMAC_2_PRPENC_HEIGHT_MASK = 0x000003FF,
+ IC_IDMAC_2_PRPENC_HEIGHT_OFFSET = 0,
+ IC_IDMAC_2_PRPVF_HEIGHT_MASK = 0x000FFC00,
+ IC_IDMAC_2_PRPVF_HEIGHT_OFFSET = 10,
+ IC_IDMAC_2_PP_HEIGHT_MASK = 0x3FF00000,
+ IC_IDMAC_2_PP_HEIGHT_OFFSET = 20,
+
+ IC_IDMAC_3_PRPENC_WIDTH_MASK = 0x000003FF,
+ IC_IDMAC_3_PRPENC_WIDTH_OFFSET = 0,
+ IC_IDMAC_3_PRPVF_WIDTH_MASK = 0x000FFC00,
+ IC_IDMAC_3_PRPVF_WIDTH_OFFSET = 10,
+ IC_IDMAC_3_PP_WIDTH_MASK = 0x3FF00000,
+ IC_IDMAC_3_PP_WIDTH_OFFSET = 20,
+
+ CSI_SENS_CONF_DATA_FMT_SHIFT = 8,
+ CSI_SENS_CONF_DATA_FMT_MASK = 0x00000700,
+ CSI_SENS_CONF_DATA_FMT_RGB_YUV444 = 0L,
+ CSI_SENS_CONF_DATA_FMT_YUV422_YUYV = 1L,
+ CSI_SENS_CONF_DATA_FMT_YUV422_UYVY = 2L,
+ CSI_SENS_CONF_DATA_FMT_BAYER = 3L,
+ CSI_SENS_CONF_DATA_FMT_RGB565 = 4L,
+ CSI_SENS_CONF_DATA_FMT_RGB555 = 5L,
+ CSI_SENS_CONF_DATA_FMT_RGB444 = 6L,
+ CSI_SENS_CONF_DATA_FMT_JPEG = 7L,
+
+ CSI_SENS_CONF_VSYNC_POL_SHIFT = 0,
+ CSI_SENS_CONF_HSYNC_POL_SHIFT = 1,
+ CSI_SENS_CONF_DATA_POL_SHIFT = 2,
+ CSI_SENS_CONF_PIX_CLK_POL_SHIFT = 3,
+ CSI_SENS_CONF_SENS_PRTCL_SHIFT = 4,
+ CSI_SENS_CONF_PACK_TIGHT_SHIFT = 7,
+ CSI_SENS_CONF_DATA_WIDTH_SHIFT = 11,
+ CSI_SENS_CONF_EXT_VSYNC_SHIFT = 15,
+ CSI_SENS_CONF_DIVRATIO_SHIFT = 16,
+
+ CSI_SENS_CONF_DIVRATIO_MASK = 0x00FF0000L,
+ CSI_SENS_CONF_DATA_DEST_SHIFT = 24,
+ CSI_SENS_CONF_DATA_DEST_MASK = 0x07000000L,
+ CSI_SENS_CONF_JPEG8_EN_SHIFT = 27,
+ CSI_SENS_CONF_JPEG_EN_SHIFT = 28,
+ CSI_SENS_CONF_FORCE_EOF_SHIFT = 29,
+ CSI_SENS_CONF_DATA_EN_POL_SHIFT = 31,
+
+ CSI_DATA_DEST_ISP = 1L,
+ CSI_DATA_DEST_IC = 2L,
+ CSI_DATA_DEST_IDMAC = 4L,
+
+ CSI_CCIR_ERR_DET_EN = 0x01000000L,
+ CSI_HORI_DOWNSIZE_EN = 0x80000000L,
+ CSI_VERT_DOWNSIZE_EN = 0x40000000L,
+ CSI_TEST_GEN_MODE_EN = 0x01000000L,
+
+ CSI_HSC_MASK = 0x1FFF0000,
+ CSI_HSC_SHIFT = 16,
+ CSI_VSC_MASK = 0x00000FFF,
+ CSI_VSC_SHIFT = 0,
+
+ CSI_TEST_GEN_R_MASK = 0x000000FFL,
+ CSI_TEST_GEN_R_SHIFT = 0,
+ CSI_TEST_GEN_G_MASK = 0x0000FF00L,
+ CSI_TEST_GEN_G_SHIFT = 8,
+ CSI_TEST_GEN_B_MASK = 0x00FF0000L,
+ CSI_TEST_GEN_B_SHIFT = 16,
+
+ CSI_MIPI_DI0_MASK = 0x000000FFL,
+ CSI_MIPI_DI0_SHIFT = 0,
+ CSI_MIPI_DI1_MASK = 0x0000FF00L,
+ CSI_MIPI_DI1_SHIFT = 8,
+ CSI_MIPI_DI2_MASK = 0x00FF0000L,
+ CSI_MIPI_DI2_SHIFT = 16,
+ CSI_MIPI_DI3_MASK = 0xFF000000L,
+ CSI_MIPI_DI3_SHIFT = 24,
+
+ CSI_MAX_RATIO_SKIP_ISP_MASK = 0x00070000L,
+ CSI_MAX_RATIO_SKIP_ISP_SHIFT = 16,
+ CSI_SKIP_ISP_MASK = 0x00F80000L,
+ CSI_SKIP_ISP_SHIFT = 19,
+ CSI_MAX_RATIO_SKIP_SMFC_MASK = 0x00000007L,
+ CSI_MAX_RATIO_SKIP_SMFC_SHIFT = 0,
+ CSI_SKIP_SMFC_MASK = 0x000000F8L,
+ CSI_SKIP_SMFC_SHIFT = 3,
+ CSI_ID_2_SKIP_MASK = 0x00000300L,
+ CSI_ID_2_SKIP_SHIFT = 8,
+
+ CSI_COLOR_FIRST_ROW_MASK = 0x00000002L,
+ CSI_COLOR_FIRST_COMP_MASK = 0x00000001L,
+
+ SMFC_MAP_CH0_MASK = 0x00000007L,
+ SMFC_MAP_CH0_SHIFT = 0,
+ SMFC_MAP_CH1_MASK = 0x00000038L,
+ SMFC_MAP_CH1_SHIFT = 3,
+ SMFC_MAP_CH2_MASK = 0x000001C0L,
+ SMFC_MAP_CH2_SHIFT = 6,
+ SMFC_MAP_CH3_MASK = 0x00000E00L,
+ SMFC_MAP_CH3_SHIFT = 9,
+
+ SMFC_WM0_SET_MASK = 0x00000007L,
+ SMFC_WM0_SET_SHIFT = 0,
+ SMFC_WM1_SET_MASK = 0x000001C0L,
+ SMFC_WM1_SET_SHIFT = 6,
+ SMFC_WM2_SET_MASK = 0x00070000L,
+ SMFC_WM2_SET_SHIFT = 16,
+ SMFC_WM3_SET_MASK = 0x01C00000L,
+ SMFC_WM3_SET_SHIFT = 22,
+
+ SMFC_WM0_CLR_MASK = 0x00000038L,
+ SMFC_WM0_CLR_SHIFT = 3,
+ SMFC_WM1_CLR_MASK = 0x00000E00L,
+ SMFC_WM1_CLR_SHIFT = 9,
+ SMFC_WM2_CLR_MASK = 0x00380000L,
+ SMFC_WM2_CLR_SHIFT = 19,
+ SMFC_WM3_CLR_MASK = 0x0E000000L,
+ SMFC_WM3_CLR_SHIFT = 25,
+
+ SMFC_BS0_MASK = 0x0000000FL,
+ SMFC_BS0_SHIFT = 0,
+ SMFC_BS1_MASK = 0x000000F0L,
+ SMFC_BS1_SHIFT = 4,
+ SMFC_BS2_MASK = 0x00000F00L,
+ SMFC_BS2_SHIFT = 8,
+ SMFC_BS3_MASK = 0x0000F000L,
+ SMFC_BS3_SHIFT = 12,
+
+ PF_CONF_TYPE_MASK = 0x00000007,
+ PF_CONF_TYPE_SHIFT = 0,
+ PF_CONF_PAUSE_EN = 0x00000010,
+ PF_CONF_RESET = 0x00008000,
+ PF_CONF_PAUSE_ROW_MASK = 0x00FF0000,
+ PF_CONF_PAUSE_ROW_SHIFT = 16,
+
+ DI_DW_GEN_ACCESS_SIZE_OFFSET = 24,
+ DI_DW_GEN_COMPONENT_SIZE_OFFSET = 16,
+
+ DI_GEN_DI_CLK_EXT = 0x100000,
+ DI_GEN_POLARITY_1 = 0x00000001,
+ DI_GEN_POLARITY_2 = 0x00000002,
+ DI_GEN_POLARITY_3 = 0x00000004,
+ DI_GEN_POLARITY_4 = 0x00000008,
+ DI_GEN_POLARITY_5 = 0x00000010,
+ DI_GEN_POLARITY_6 = 0x00000020,
+ DI_GEN_POLARITY_7 = 0x00000040,
+ DI_GEN_POLARITY_8 = 0x00000080,
+
+ DI_POL_DRDY_DATA_POLARITY = 0x00000080,
+ DI_POL_DRDY_POLARITY_15 = 0x00000010,
+
+ DI_VSYNC_SEL_OFFSET = 13,
+
+ DC_WR_CH_CONF_FIELD_MODE = 0x00000200,
+ DC_WR_CH_CONF_PROG_TYPE_OFFSET = 5,
+ DC_WR_CH_CONF_PROG_TYPE_MASK = 0x000000E0,
+ DC_WR_CH_CONF_PROG_DI_ID = 0x00000004,
+ DC_WR_CH_CONF_PROG_DISP_ID_OFFSET = 3,
+ DC_WR_CH_CONF_PROG_DISP_ID_MASK = 0x00000018,
+
+ DC_UGDE_0_ODD_EN = 0x02000000,
+ DC_UGDE_0_ID_CODED_MASK = 0x00000007,
+ DC_UGDE_0_ID_CODED_OFFSET = 0,
+ DC_UGDE_0_EV_PRIORITY_MASK = 0x00000078,
+ DC_UGDE_0_EV_PRIORITY_OFFSET = 3,
+
+ DP_COM_CONF_FG_EN = 0x00000001,
+ DP_COM_CONF_GWSEL = 0x00000002,
+ DP_COM_CONF_GWAM = 0x00000004,
+ DP_COM_CONF_GWCKE = 0x00000008,
+ DP_COM_CONF_CSC_DEF_MASK = 0x00000300,
+ DP_COM_CONF_CSC_DEF_OFFSET = 8,
+ DP_COM_CONF_CSC_DEF_FG = 0x00000300,
+ DP_COM_CONF_CSC_DEF_BG = 0x00000200,
+ DP_COM_CONF_CSC_DEF_BOTH = 0x00000100,
+
+ DI_SER_CONF_LLA_SER_ACCESS = 0x00000020,
+ DI_SER_CONF_SERIAL_CLK_POL = 0x00000010,
+ DI_SER_CONF_SERIAL_DATA_POL = 0x00000008,
+ DI_SER_CONF_SERIAL_RS_POL = 0x00000004,
+ DI_SER_CONF_SERIAL_CS_POL = 0x00000002,
+ DI_SER_CONF_WAIT4SERIAL = 0x00000001,
+
+ VDI_C_CH_420 = 0x00000000,
+ VDI_C_CH_422 = 0x00000002,
+ VDI_C_MOT_SEL_FULL = 0x00000008,
+ VDI_C_MOT_SEL_LOW = 0x00000004,
+ VDI_C_MOT_SEL_MED = 0x00000000,
+ VDI_C_BURST_SIZE1_4 = 0x00000030,
+ VDI_C_BURST_SIZE2_4 = 0x00000300,
+ VDI_C_BURST_SIZE3_4 = 0x00003000,
+ VDI_C_VWM1_SET_1 = 0x00000000,
+ VDI_C_VWM1_CLR_2 = 0x00080000,
+ VDI_C_VWM3_SET_1 = 0x00000000,
+ VDI_C_VWM3_CLR_2 = 0x02000000,
+ VDI_C_TOP_FIELD_MAN_1 = 0x40000000,
+ VDI_C_TOP_FIELD_AUTO_1 = 0x80000000,
+};
+
+enum di_pins {
+ DI_PIN11 = 0,
+ DI_PIN12 = 1,
+ DI_PIN13 = 2,
+ DI_PIN14 = 3,
+ DI_PIN15 = 4,
+ DI_PIN16 = 5,
+ DI_PIN17 = 6,
+ DI_PIN_CS = 7,
+
+ DI_PIN_SER_CLK = 0,
+ DI_PIN_SER_RS = 1,
+};
+
+enum di_sync_wave {
+ DI_SYNC_NONE = -1,
+ DI_SYNC_CLK = 0,
+ DI_SYNC_INT_HSYNC = 1,
+ DI_SYNC_HSYNC = 2,
+ DI_SYNC_VSYNC = 3,
+ DI_SYNC_DE = 5,
+};
+
+/* DC template opcodes */
+#define WROD(lf) (0x18 | (lf << 1))
+
+#endif
diff --git a/drivers/mxc/mcu_pmic/Kconfig b/drivers/mxc/mcu_pmic/Kconfig
new file mode 100644
index 000000000000..43addf06f82e
--- /dev/null
+++ b/drivers/mxc/mcu_pmic/Kconfig
@@ -0,0 +1,19 @@
+#
+# PMIC Modules configuration
+#
+
+config MXC_PMIC_MC9S08DZ60
+ tristate "MC9S08DZ60 PMIC"
+ depends on ARCH_MXC && I2C
+ ---help---
+ This is the MXC MC9S08DZ60(MCU) PMIC support.
+
+config MXC_MC9SDZ60_RTC
+ tristate "MC9SDZ60 Real Time Clock (RTC) support"
+ depends on MXC_PMIC_MC9SDZ60
+ ---help---
+ This is the MC9SDZ60 RTC module driver. This module provides kernel API
+ for RTC part of MC9SDZ60.
+ If you want MC9SDZ60 RTC support, you should say Y here
+
+
diff --git a/drivers/mxc/mcu_pmic/Makefile b/drivers/mxc/mcu_pmic/Makefile
new file mode 100644
index 000000000000..dee58bfc1051
--- /dev/null
+++ b/drivers/mxc/mcu_pmic/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the mc9sdz60 pmic drivers.
+#
+
+obj-$(CONFIG_MXC_PMIC_MC9SDZ60) += pmic_mc9sdz60_mod.o
+pmic_mc9sdz60_mod-objs := mcu_pmic_core.o max8660.o mc9s08dz60.o mcu_pmic_gpio.o
+
+
diff --git a/drivers/mxc/mcu_pmic/max8660.c b/drivers/mxc/mcu_pmic/max8660.c
new file mode 100644
index 000000000000..f48899f212f9
--- /dev/null
+++ b/drivers/mxc/mcu_pmic/max8660.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file max8660.c
+ * @brief Driver for max8660
+ *
+ * @ingroup pmic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include <asm/uaccess.h>
+#include "mcu_pmic_core.h"
+#include "max8660.h"
+
+/* I2C bus id and device address of mcu */
+#define I2C1_BUS 0
+#define MAX8660_I2C_ADDR 0x68
+
+static struct i2c_client *max8660_i2c_client;
+
+ /* reg names for max8660
+ REG_MAX8660_OUTPUT_ENABLE_1,
+ REG_MAX8660_OUTPUT_ENABLE_2,
+ REG_MAX8660_VOLT__CHANGE_1,
+ REG_MAX8660_V3_TARGET_VOLT_1,
+ REG_MAX8660_V3_TARGET_VOLT_2,
+ REG_MAX8660_V4_TARGET_VOLT_1,
+ REG_MAX8660_V4_TARGET_VOLT_2,
+ REG_MAX8660_V5_TARGET_VOLT_1,
+ REG_MAX8660_V5_TARGET_VOLT_2,
+ REG_MAX8660_V6V7_TARGET_VOLT,
+ REG_MAX8660_FORCE_PWM
+ */
+
+ /* save down the reg values for the device is write only */
+static u8 max8660_reg_value_table[] =
+ { 0x0, 0x0, 0x0, 0x17, 0x17, 0x1F, 0x1F, 0x04, 0x04, 0x0, 0x0
+};
+static int max8660_dev_present;
+
+int is_max8660_present(void)
+{
+ return max8660_dev_present;
+}
+
+int max8660_get_buffered_reg_val(int reg_name, u8 *value)
+{
+ if (!max8660_dev_present)
+ return -1;
+ /* outof range */
+ if (reg_name < REG_MAX8660_OUTPUT_ENABLE_1
+ || reg_name > REG_MAX8660_FORCE_PWM)
+ return -1;
+ *value =
+ max8660_reg_value_table[reg_name - REG_MAX8660_OUTPUT_ENABLE_1];
+ return 0;
+}
+int max8660_save_buffered_reg_val(int reg_name, u8 value)
+{
+
+ /* outof range */
+ if (reg_name < REG_MAX8660_OUTPUT_ENABLE_1
+ || reg_name > REG_MAX8660_FORCE_PWM)
+ return -1;
+ max8660_reg_value_table[reg_name - REG_MAX8660_OUTPUT_ENABLE_1] = value;
+ return 0;
+}
+
+int max8660_write_reg(u8 reg, u8 value)
+{
+ if (max8660_dev_present && (i2c_smbus_write_byte_data(
+ max8660_i2c_client, reg, value) >= 0))
+ return 0;
+ return -1;
+}
+
+/*!
+ * max8660 I2C attach function
+ *
+ * @param adapter struct i2c_client *
+ * @return 0 for max8660 successfully detected
+ */
+static int max8660_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int retval;
+ max8660_i2c_client = client;
+ retval = i2c_smbus_write_byte_data(max8660_i2c_client,
+ MAX8660_OUTPUT_ENABLE_1, 0);
+ if (retval == 0) {
+ max8660_dev_present = 1;
+ pr_info("max8660 probed !\n");
+ } else {
+ max8660_dev_present = 0;
+ pr_info("max8660 not detected!\n");
+ }
+ return retval;
+}
+
+/*!
+ * max8660 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return 0
+ */
+static int max8660_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id max8660_id[] = {
+ { "max8660", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, max8660_id);
+
+static struct i2c_driver max8660_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "max8660",},
+ .probe = max8660_probe,
+ .remove = max8660_remove,
+ .id_table = max8660_id,
+};
+
+/* called by pmic core when init*/
+int max8660_init(void)
+{
+ int err;
+ err = i2c_add_driver(&max8660_i2c_driver);
+ return err;
+}
+void max8660_exit(void)
+{
+ i2c_del_driver(&max8660_i2c_driver);
+}
diff --git a/drivers/mxc/mcu_pmic/max8660.h b/drivers/mxc/mcu_pmic/max8660.h
new file mode 100644
index 000000000000..567784611d80
--- /dev/null
+++ b/drivers/mxc/mcu_pmic/max8660.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file max8660.h
+ * @brief Driver for max8660
+ *
+ * @ingroup pmic
+ */
+#ifndef _MAX8660_H_
+#define _MAX8660_H_
+
+#ifdef __KERNEL__
+
+#define MAX8660_OUTPUT_ENABLE_1 0x10
+#define MAX8660_OUTPUT_ENABLE_2 0x12
+#define MAX8660_VOLT_CHANGE_CONTROL 0x20
+#define MAX8660_V3_TARGET_VOLT_1 0x23
+#define MAX8660_V3_TARGET_VOLT_2 0x24
+#define MAX8660_V4_TARGET_VOLT_1 0x29
+#define MAX8660_V4_TARGET_VOLT_2 0x2A
+#define MAX8660_V5_TARGET_VOLT_1 0x32
+#define MAX8660_V5_TARGET_VOLT_2 0x33
+#define MAX8660_V6V7_TARGET_VOLT 0x39
+#define MAX8660_FORCE_PWM 0x80
+
+int is_max8660_present(void);
+int max8660_write_reg(u8 reg, u8 value);
+int max8660_save_buffered_reg_val(int reg_name, u8 value);
+int max8660_get_buffered_reg_val(int reg_name, u8 *value);
+int max8660_init(void);
+void max8660_exit(void);
+
+extern int reg_max8660_probe(void);
+extern int reg_max8660_remove(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* _MAX8660_H_ */
diff --git a/drivers/mxc/mcu_pmic/mc9s08dz60.c b/drivers/mxc/mcu_pmic/mc9s08dz60.c
new file mode 100644
index 000000000000..1e5da6319b12
--- /dev/null
+++ b/drivers/mxc/mcu_pmic/mc9s08dz60.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @file mc9s08dz60.c
+ * @brief Driver for MC9sdz60
+ *
+ * @ingroup pmic
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/mfd/mc9s08dz60/core.h>
+
+#include <mach/clock.h>
+#include <linux/uaccess.h>
+#include "mc9s08dz60.h"
+
+/* I2C bus id and device address of mcu */
+#define I2C1_BUS 0
+#define MC9S08DZ60_I2C_ADDR 0xD2 /* 7bits I2C address */
+static struct i2c_client *mc9s08dz60_i2c_client;
+
+int mc9s08dz60_read_reg(u8 reg, u8 *value)
+{
+ *value = (u8) i2c_smbus_read_byte_data(mc9s08dz60_i2c_client, reg);
+ return 0;
+}
+
+int mc9s08dz60_write_reg(u8 reg, u8 value)
+{
+ if (i2c_smbus_write_byte_data(mc9s08dz60_i2c_client, reg, value) < 0)
+ return -1;
+ return 0;
+}
+
+static ssize_t mc9s08dz60_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int i;
+ u8 value;
+ int offset = 7;
+
+ for (i = 0; i < 7; i++) {
+ mc9s08dz60_read_reg(i, &value);
+ pr_info("reg%02x: %02x\t", i, value);
+ mc9s08dz60_read_reg(i + offset, &value);
+ pr_info("reg%02x: %02x\t", i + offset, value);
+ mc9s08dz60_read_reg(i + offset * 2, &value);
+ pr_info("reg%02x: %02x\t", i + offset * 2, value);
+ mc9s08dz60_read_reg(i + offset * 3, &value);
+ pr_info("reg%02x: %02x\t", i + offset * 3, value);
+ mc9s08dz60_read_reg(i + offset * 4, &value);
+ pr_info("reg%02x: %02x\t", i + offset * 4, value);
+ mc9s08dz60_read_reg(i + offset * 5, &value);
+ pr_info("reg%02x: %02x\n", i + offset * 5, value);
+ }
+
+ return 0;
+}
+
+static ssize_t mc9s08dz60_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int ret;
+ unsigned long reg, new_value;
+ u8 value;
+ char *p;
+
+ strict_strtoul(buf, 16, &reg);
+
+ p = NULL;
+ p = memchr(buf, ' ', count);
+
+ if (p == NULL) {
+ mc9s08dz60_read_reg(reg, &value);
+ pr_info("reg%02lu: %06x\n", reg, value);
+ return count;
+ }
+
+ p += 1;
+
+ strict_strtoul(p, 16, &new_value);
+ value = new_value;
+
+ ret = mc9s08dz60_write_reg((u8)reg, value);
+ if (ret == 0)
+ pr_info("write reg%02lx: %06x\n", reg, value);
+ else
+ pr_info("register update failed\n");
+
+ return count;
+}
+
+static struct device_attribute mc9s08dz60_dev_attr = {
+ .attr = {
+ .name = "mc9s08dz60_ctl",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .show = mc9s08dz60_show,
+ .store = mc9s08dz60_store,
+};
+
+
+/*!
+ * mc9s08dz60 I2C attach function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return 0
+ */
+static int mc9s08dz60_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct mc9s08dz60 *mc9s08dz60 = NULL;
+ struct mc9s08dz60_platform_data *plat_data = client->dev.platform_data;
+ pr_info("mc9s08dz60 probing .... \n");
+
+ mc9s08dz60 = kzalloc(sizeof(struct mc9s08dz60), GFP_KERNEL);
+ if (mc9s08dz60 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, mc9s08dz60);
+ mc9s08dz60->dev = &client->dev;
+ mc9s08dz60->i2c_client = client;
+
+ if (plat_data && plat_data->init) {
+ ret = plat_data->init(mc9s08dz60);
+ if (ret != 0)
+ return -1;
+ }
+
+ ret = device_create_file(&client->dev, &mc9s08dz60_dev_attr);
+ if (ret)
+ dev_err(&client->dev, "create device file failed!\n");
+
+
+ mc9s08dz60_i2c_client = client;
+
+ return 0;
+}
+
+/*!
+ * mc9s08dz60 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return 0
+ */
+static int mc9s08dz60_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id mc9s08dz60_id[] = {
+ { "mc9s08dz60", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mc9s08dz60_id);
+
+static struct i2c_driver mc9s08dz60_i2c_driver = {
+ .driver = {.owner = THIS_MODULE,
+ .name = "mc9s08dz60",
+ },
+ .probe = mc9s08dz60_probe,
+ .remove = mc9s08dz60_remove,
+ .id_table = mc9s08dz60_id,
+};
+
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+int mc9s08dz60_init(void)
+{
+ int err;
+ err = i2c_add_driver(&mc9s08dz60_i2c_driver);
+ return err;
+}
+void mc9s08dz60_exit(void)
+{
+ i2c_del_driver(&mc9s08dz60_i2c_driver);
+}
diff --git a/drivers/mxc/mcu_pmic/mc9s08dz60.h b/drivers/mxc/mcu_pmic/mc9s08dz60.h
new file mode 100644
index 000000000000..b47b20c79828
--- /dev/null
+++ b/drivers/mxc/mcu_pmic/mc9s08dz60.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+/*
+ * 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
+ */
+
+/*!
+ * @file mc9s08dz60.h
+ * @brief Driver for mc9s08dz60
+ *
+ * @ingroup pmic
+ */
+#ifndef _MC9SDZ60_H_
+#define _MC9SDZ60_H_
+
+#define MCU_VERSION 0x00
+/*#define Reserved 0x01*/
+#define MCU_SECS 0x02
+#define MCU_MINS 0x03
+#define MCU_HRS 0x04
+#define MCU_DAY 0x05
+#define MCU_DATE 0x06
+#define MCU_MONTH 0x07
+#define MCU_YEAR 0x08
+
+#define MCU_ALARM_SECS 0x09
+#define MCU_ALARM_MINS 0x0A
+#define MCU_ALARM_HRS 0x0B
+/* #define Reserved 0x0C*/
+/* #define Reserved 0x0D*/
+#define MCU_TS_CONTROL 0x0E
+#define MCU_X_LOW 0x0F
+#define MCU_Y_LOW 0x10
+#define MCU_XY_HIGH 0x11
+#define MCU_X_LEFT_LOW 0x12
+#define MCU_X_LEFT_HIGH 0x13
+#define MCU_X_RIGHT 0x14
+#define MCU_Y_TOP_LOW 0x15
+#define MCU_Y_TOP_HIGH 0x16
+#define MCU_Y_BOTTOM 0x17
+/* #define Reserved 0x18*/
+/* #define Reserved 0x19*/
+#define MCU_RESET_1 0x1A
+#define MCU_RESET_2 0x1B
+#define MCU_POWER_CTL 0x1C
+#define MCU_DELAY_CONFIG 0x1D
+/* #define Reserved 0x1E */
+/* #define Reserved 0x1F */
+#define MCU_GPIO_1 0x20
+#define MCU_GPIO_2 0x21
+#define MCU_KPD_1 0x22
+#define MCU_KPD_2 0x23
+#define MCU_KPD_CONTROL 0x24
+#define MCU_INT_ENABLE_1 0x25
+#define MCU_INT_ENABLE_2 0x26
+#define MCU_INT_FLAG_1 0x27
+#define MCU_INT_FLAG_2 0x28
+#define MCU_DES_FLAG 0x29
+int mc9s08dz60_read_reg(u8 reg, u8 *value);
+int mc9s08dz60_write_reg(u8 reg, u8 value);
+int mc9s08dz60_init(void);
+void mc9s08dz60_exit(void);
+
+extern int reg_mc9s08dz60_probe(void);
+extern int reg_mc9s08dz60_remove(void);
+
+#endif /* _MC9SDZ60_H_ */
+
diff --git a/drivers/mxc/mcu_pmic/mcu_pmic_core.c b/drivers/mxc/mcu_pmic/mcu_pmic_core.c
new file mode 100644
index 000000000000..55b2f5fe6e3e
--- /dev/null
+++ b/drivers/mxc/mcu_pmic/mcu_pmic_core.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc9s08dz60/mcu_pmic_core.c
+ * @brief This is the main file of mc9s08dz60 Power Control driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include <asm/ioctl.h>
+#include <asm/uaccess.h>
+#include <mach/gpio.h>
+
+#include "mcu_pmic_core.h"
+#include "mc9s08dz60.h"
+#include "max8660.h"
+
+/* bitfield macros for mcu pmic*/
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+
+/* map reg names (enum pmic_reg in pmic_external.h) to real addr*/
+const static u8 mcu_pmic_reg_addr_table[] = {
+ MCU_VERSION,
+ MCU_SECS,
+ MCU_MINS,
+ MCU_HRS,
+ MCU_DAY,
+ MCU_DATE,
+ MCU_MONTH,
+ MCU_YEAR,
+ MCU_ALARM_SECS,
+ MCU_ALARM_MINS,
+ MCU_ALARM_HRS,
+ MCU_TS_CONTROL,
+ MCU_X_LOW,
+ MCU_Y_LOW,
+ MCU_XY_HIGH,
+ MCU_X_LEFT_LOW,
+ MCU_X_LEFT_HIGH,
+ MCU_X_RIGHT,
+ MCU_Y_TOP_LOW,
+ MCU_Y_TOP_HIGH,
+ MCU_Y_BOTTOM,
+ MCU_RESET_1,
+ MCU_RESET_2,
+ MCU_POWER_CTL,
+ MCU_DELAY_CONFIG,
+ MCU_GPIO_1,
+ MCU_GPIO_2,
+ MCU_KPD_1,
+ MCU_KPD_2,
+ MCU_KPD_CONTROL,
+ MCU_INT_ENABLE_1,
+ MCU_INT_ENABLE_2,
+ MCU_INT_FLAG_1,
+ MCU_INT_FLAG_2,
+ MCU_DES_FLAG,
+ MAX8660_OUTPUT_ENABLE_1,
+ MAX8660_OUTPUT_ENABLE_2,
+ MAX8660_VOLT_CHANGE_CONTROL,
+ MAX8660_V3_TARGET_VOLT_1,
+ MAX8660_V3_TARGET_VOLT_2,
+ MAX8660_V4_TARGET_VOLT_1,
+ MAX8660_V4_TARGET_VOLT_2,
+ MAX8660_V5_TARGET_VOLT_1,
+ MAX8660_V5_TARGET_VOLT_2,
+ MAX8660_V6V7_TARGET_VOLT,
+ MAX8660_FORCE_PWM
+};
+
+static int mcu_pmic_read(int reg_num, unsigned int *reg_val)
+{
+ int ret;
+ u8 value = 0;
+ /* mcu ops */
+ if (reg_num >= REG_MCU_VERSION && reg_num <= REG_MCU_DES_FLAG)
+ ret = mc9s08dz60_read_reg(mcu_pmic_reg_addr_table[reg_num],
+ &value);
+ else if (reg_num >= REG_MAX8660_OUTPUT_ENABLE_1
+ && reg_num <= REG_MAX8660_FORCE_PWM)
+ ret = max8660_get_buffered_reg_val(reg_num, &value);
+ else
+ return -1;
+
+ if (ret < 0)
+ return -1;
+ *reg_val = value;
+
+ return 0;
+}
+
+static int mcu_pmic_write(int reg_num, const unsigned int reg_val)
+{
+ int ret;
+ u8 value = reg_val;
+ /* mcu ops */
+ if (reg_num >= REG_MCU_VERSION && reg_num <= REG_MCU_DES_FLAG) {
+
+ ret =
+ mc9s08dz60_write_reg(
+ mcu_pmic_reg_addr_table[reg_num], value);
+ if (ret < 0)
+ return -1;
+ } else if (reg_num >= REG_MAX8660_OUTPUT_ENABLE_1
+ && reg_num <= REG_MAX8660_FORCE_PWM) {
+ ret =
+ max8660_write_reg(mcu_pmic_reg_addr_table[reg_num], value);
+
+ if (ret < 0)
+ return -1;
+
+ ret = max8660_save_buffered_reg_val(reg_num, value);
+ } else
+ return -1;
+
+ return 0;
+}
+
+int mcu_pmic_read_reg(int reg, unsigned int *reg_value,
+ unsigned int reg_mask)
+{
+ int ret = 0;
+ unsigned int temp = 0;
+
+ ret = mcu_pmic_read(reg, &temp);
+ if (ret != 0)
+ return -1;
+ *reg_value = (temp & reg_mask);
+
+ pr_debug("Read REG[ %d ] = 0x%x\n", reg, *reg_value);
+
+ return ret;
+}
+
+
+int mcu_pmic_write_reg(int reg, unsigned int reg_value,
+ unsigned int reg_mask)
+{
+ int ret = 0;
+ unsigned int temp = 0;
+
+ ret = mcu_pmic_read(reg, &temp);
+ if (ret != 0)
+ return -1;
+ temp = (temp & (~reg_mask)) | reg_value;
+
+ ret = mcu_pmic_write(reg, temp);
+ if (ret != 0)
+ return -1;
+
+ pr_debug("Write REG[ %d ] = 0x%x\n", reg, reg_value);
+
+ return ret;
+}
+
+/*!
+ * make max8660 - mc9s08dz60 enter low-power mode
+ */
+static void pmic_power_off(void)
+{
+ mcu_pmic_write_reg(REG_MCU_POWER_CTL, 0x10, 0x10);
+}
+
+static int __init mcu_pmic_init(void)
+{
+ int err;
+
+ /* init chips */
+ err = max8660_init();
+ if (err)
+ goto fail1;
+
+ err = mc9s08dz60_init();
+ if (err)
+ goto fail1;
+
+ if (is_max8660_present()) {
+ pr_info("max8660 is present \n");
+ pm_power_off = pmic_power_off;
+ } else
+ pr_debug("max8660 is not present\n");
+ pr_info("mcu_pmic_init completed!\n");
+ return 0;
+
+fail1:
+ pr_err("mcu_pmic_init failed!\n");
+ return err;
+}
+
+static void __exit mcu_pmic_exit(void)
+{
+ reg_max8660_remove();
+ mc9s08dz60_exit();
+ max8660_exit();
+}
+
+subsys_initcall_sync(mcu_pmic_init);
+module_exit(mcu_pmic_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("mcu pmic driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/mcu_pmic/mcu_pmic_core.h b/drivers/mxc/mcu_pmic/mcu_pmic_core.h
new file mode 100644
index 000000000000..9ab07356f8a8
--- /dev/null
+++ b/drivers/mxc/mcu_pmic/mcu_pmic_core.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mcu_pmic_core.h
+ * @brief Driver for max8660
+ *
+ * @ingroup pmic
+ */
+#ifndef _MCU_PMIC_CORE_H_
+#define _MCU_PMIC_CORE_H_
+
+#include <linux/mfd/mc9s08dz60/pmic.h>
+
+#define MAX8660_REG_START (REG_MCU_DES_FLAG + 1)
+enum {
+
+ /* reg names for max8660 */
+ REG_MAX8660_OUTPUT_ENABLE_1 = MAX8660_REG_START,
+ REG_MAX8660_OUTPUT_ENABLE_2,
+ REG_MAX8660_VOLT_CHANGE_CONTROL_1,
+ REG_MAX8660_V3_TARGET_VOLT_1,
+ REG_MAX8660_V3_TARGET_VOLT_2,
+ REG_MAX8660_V4_TARGET_VOLT_1,
+ REG_MAX8660_V4_TARGET_VOLT_2,
+ REG_MAX8660_V5_TARGET_VOLT_1,
+ REG_MAX8660_V5_TARGET_VOLT_2,
+ REG_MAX8660_V6V7_TARGET_VOLT,
+ REG_MAX8660_FORCE_PWM
+};
+
+
+#endif /* _MCU_PMIC_CORE_H_ */
diff --git a/drivers/mxc/mcu_pmic/mcu_pmic_gpio.c b/drivers/mxc/mcu_pmic/mcu_pmic_gpio.c
new file mode 100644
index 000000000000..bf3a0f97513a
--- /dev/null
+++ b/drivers/mxc/mcu_pmic/mcu_pmic_gpio.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc9s08dz60/mcu_pmic_gpio.c
+ * @brief This is the main file of mc9s08dz60 Power Control driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include <linux/pmic_status.h>
+#include <linux/ioctl.h>
+
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+int pmic_gpio_set_bit_val(int reg, unsigned int bit,
+ unsigned int val)
+{
+ int reg_name;
+ u8 reg_mask = 0;
+
+ if (bit > 7)
+ return -1;
+
+ switch (reg) {
+ case MCU_GPIO_REG_RESET_1:
+ reg_name = REG_MCU_RESET_1;
+ break;
+ case MCU_GPIO_REG_RESET_2:
+ reg_name = REG_MCU_RESET_2;
+ break;
+ case MCU_GPIO_REG_POWER_CONTROL:
+ reg_name = REG_MCU_POWER_CTL;
+ break;
+ case MCU_GPIO_REG_GPIO_CONTROL_1:
+ reg_name = REG_MCU_GPIO_1;
+ break;
+ case MCU_GPIO_REG_GPIO_CONTROL_2:
+ reg_name = REG_MCU_GPIO_2;
+ break;
+ default:
+ return -1;
+ }
+
+ SET_BIT_IN_BYTE(reg_mask, bit);
+ if (0 == val)
+ CHECK_ERROR(mcu_pmic_write_reg(reg_name, 0, reg_mask));
+ else
+ CHECK_ERROR(mcu_pmic_write_reg(reg_name, reg_mask, reg_mask));
+
+ return 0;
+}
+EXPORT_SYMBOL(pmic_gpio_set_bit_val);
+
+int pmic_gpio_get_bit_val(int reg, unsigned int bit,
+ unsigned int *val)
+{
+ int reg_name;
+ unsigned int reg_read_val;
+ u8 reg_mask = 0;
+
+ if (bit > 7)
+ return -1;
+
+ switch (reg) {
+ case MCU_GPIO_REG_RESET_1:
+ reg_name = REG_MCU_RESET_1;
+ break;
+ case MCU_GPIO_REG_RESET_2:
+ reg_name = REG_MCU_RESET_2;
+ break;
+ case MCU_GPIO_REG_POWER_CONTROL:
+ reg_name = REG_MCU_POWER_CTL;
+ break;
+ case MCU_GPIO_REG_GPIO_CONTROL_1:
+ reg_name = REG_MCU_GPIO_1;
+ break;
+ case MCU_GPIO_REG_GPIO_CONTROL_2:
+ reg_name = REG_MCU_GPIO_2;
+ break;
+ default:
+ return -1;
+ }
+
+ SET_BIT_IN_BYTE(reg_mask, bit);
+ CHECK_ERROR(mcu_pmic_read_reg(reg_name, &reg_read_val, reg_mask));
+ if (0 == reg_read_val)
+ *val = 0;
+ else
+ *val = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL(pmic_gpio_get_bit_val);
+
+int pmic_gpio_get_designation_bit_val(unsigned int bit,
+ unsigned int *val)
+{
+ unsigned int reg_read_val;
+ u8 reg_mask = 0;
+
+ if (bit > 7)
+ return -1;
+
+ SET_BIT_IN_BYTE(reg_mask, bit);
+ CHECK_ERROR(
+ mcu_pmic_read_reg(REG_MCU_DES_FLAG, &reg_read_val, reg_mask));
+ if (0 == reg_read_val)
+ *val = 0;
+ else
+ *val = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL(pmic_gpio_get_designation_bit_val);
+
diff --git a/drivers/mxc/mlb/Kconfig b/drivers/mxc/mlb/Kconfig
new file mode 100644
index 000000000000..294c9776fb4d
--- /dev/null
+++ b/drivers/mxc/mlb/Kconfig
@@ -0,0 +1,13 @@
+#
+# MLB configuration
+#
+
+menu "MXC Media Local Bus Driver"
+
+config MXC_MLB
+ tristate "MLB support"
+ depends on ARCH_MX35
+ ---help---
+ Say Y to get the MLB support.
+
+endmenu
diff --git a/drivers/mxc/mlb/Makefile b/drivers/mxc/mlb/Makefile
new file mode 100644
index 000000000000..60662eb1c031
--- /dev/null
+++ b/drivers/mxc/mlb/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the kernel MLB driver
+#
+
+obj-$(CONFIG_MXC_MLB) += mxc_mlb.o
diff --git a/drivers/mxc/mlb/mxc_mlb.c b/drivers/mxc/mlb/mxc_mlb.c
new file mode 100644
index 000000000000..b170c925dbd1
--- /dev/null
+++ b/drivers/mxc/mlb/mxc_mlb.c
@@ -0,0 +1,1049 @@
+/*
+ * linux/drivers/mxc/mlb/mxc_mlb.c
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/cdev.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mxc_mlb.h>
+
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/*!
+ * MLB module memory map registers define
+ */
+#define MLB_REG_DCCR 0x0
+#define MLB_REG_SSCR 0x4
+#define MLB_REG_SDCR 0x8
+#define MLB_REG_SMCR 0xC
+#define MLB_REG_VCCR 0x1C
+#define MLB_REG_SBCR 0x20
+#define MLB_REG_ABCR 0x24
+#define MLB_REG_CBCR 0x28
+#define MLB_REG_IBCR 0x2C
+#define MLB_REG_CICR 0x30
+#define MLB_REG_CECRn 0x40
+#define MLB_REG_CSCRn 0x44
+#define MLB_REG_CCBCRn 0x48
+#define MLB_REG_CNBCRn 0x4C
+#define MLB_REG_LCBCRn 0x280
+
+#define MLB_DCCR_FS_OFFSET 28
+#define MLB_DCCR_EN (1 << 31)
+#define MLB_DCCR_LBM_OFFSET 30
+#define MLB_DCCR_RESET (1 << 23)
+#define MLB_CECR_CE (1 << 31)
+#define MLB_CECR_TR (1 << 30)
+#define MLB_CECR_CT_OFFSET 28
+#define MLB_CECR_MBS (1 << 19)
+#define MLB_CSCR_CBPE (1 << 0)
+#define MLB_CSCR_CBDB (1 << 1)
+#define MLB_CSCR_CBD (1 << 2)
+#define MLB_CSCR_CBS (1 << 3)
+#define MLB_CSCR_BE (1 << 4)
+#define MLB_CSCR_ABE (1 << 5)
+#define MLB_CSCR_LFS (1 << 6)
+#define MLB_CSCR_PBPE (1 << 8)
+#define MLB_CSCR_PBDB (1 << 9)
+#define MLB_CSCR_PBD (1 << 10)
+#define MLB_CSCR_PBS (1 << 11)
+#define MLB_CSCR_RDY (1 << 16)
+#define MLB_CSCR_BM (1 << 31)
+#define MLB_CSCR_BF (1 << 30)
+#define MLB_SSCR_SDML (1 << 5)
+
+#define MLB_CONTROL_TX_CHANN (0 << 4)
+#define MLB_CONTROL_RX_CHANN (1 << 4)
+#define MLB_ASYNC_TX_CHANN (2 << 4)
+#define MLB_ASYNC_RX_CHANN (3 << 4)
+
+#define MLB_MINOR_DEVICES 2
+#define MLB_CONTROL_DEV_NAME "ctrl"
+#define MLB_ASYNC_DEV_NAME "async"
+
+#define TX_CHANNEL 0
+#define RX_CHANNEL 1
+#define TX_CHANNEL_BUF_SIZE 1024
+#define RX_CHANNEL_BUF_SIZE 2*1024
+/* max package data size */
+#define ASYNC_PACKET_SIZE 1024
+#define CTRL_PACKET_SIZE 64
+#define RX_RING_NODES 10
+
+#define _get_txchan(dev) mlb_devinfo[dev].channels[TX_CHANNEL]
+#define _get_rxchan(dev) mlb_devinfo[dev].channels[RX_CHANNEL]
+
+enum {
+ MLB_CTYPE_SYNC,
+ MLB_CTYPE_ISOC,
+ MLB_CTYPE_ASYNC,
+ MLB_CTYPE_CTRL,
+};
+
+/*!
+ * Rx ring buffer
+ */
+struct mlb_rx_ringnode {
+ int size;
+ char *data;
+};
+
+struct mlb_channel_info {
+
+ /* channel offset in memmap */
+ const unsigned int reg_offset;
+ /* channel address */
+ int address;
+ /*!
+ * channel buffer start address
+ * for Rx, buf_head pointer to a loop ring buffer
+ */
+ unsigned long buf_head;
+ /* physical buffer head address */
+ unsigned long phy_head;
+ /* channel buffer size */
+ unsigned int buf_size;
+ /* channel buffer current ptr */
+ unsigned long buf_ptr;
+ /* buffer spin lock */
+ rwlock_t buf_lock;
+};
+
+struct mlb_dev_info {
+
+ /* device node name */
+ const char dev_name[20];
+ /* channel type */
+ const unsigned int channel_type;
+ /* channel info for tx/rx */
+ struct mlb_channel_info channels[2];
+ /* rx ring buffer */
+ struct mlb_rx_ringnode rx_bufs[RX_RING_NODES];
+ /* rx ring buffer read/write ptr */
+ unsigned int rdpos, wtpos;
+ /* exception event */
+ unsigned long ex_event;
+ /* channel started up or not */
+ atomic_t on;
+ /* device open count */
+ atomic_t opencnt;
+ /* wait queue head for channel */
+ wait_queue_head_t rd_wq;
+ wait_queue_head_t wt_wq;
+ /* spinlock for event access */
+ spinlock_t event_lock;
+};
+
+static struct mlb_dev_info mlb_devinfo[MLB_MINOR_DEVICES] = {
+ {
+ .dev_name = MLB_CONTROL_DEV_NAME,
+ .channel_type = MLB_CTYPE_CTRL,
+ .channels = {
+ [0] = {
+ .reg_offset = MLB_CONTROL_TX_CHANN,
+ .buf_size = TX_CHANNEL_BUF_SIZE,
+ .buf_lock =
+ __RW_LOCK_UNLOCKED(mlb_devinfo[0].channels[0].
+ buf_lock),
+ },
+ [1] = {
+ .reg_offset = MLB_CONTROL_RX_CHANN,
+ .buf_size = RX_CHANNEL_BUF_SIZE,
+ .buf_lock =
+ __RW_LOCK_UNLOCKED(mlb_devinfo[0].channels[1].
+ buf_lock),
+ },
+ },
+ .on = ATOMIC_INIT(0),
+ .opencnt = ATOMIC_INIT(0),
+ .rd_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[0].rd_wq),
+ .wt_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[0].wt_wq),
+ .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[0].event_lock),
+ },
+ {
+ .dev_name = MLB_ASYNC_DEV_NAME,
+ .channel_type = MLB_CTYPE_ASYNC,
+ .channels = {
+ [0] = {
+ .reg_offset = MLB_ASYNC_TX_CHANN,
+ .buf_size = TX_CHANNEL_BUF_SIZE,
+ .buf_lock =
+ __RW_LOCK_UNLOCKED(mlb_devinfo[1].channels[0].
+ buf_lock),
+ },
+ [1] = {
+ .reg_offset = MLB_ASYNC_RX_CHANN,
+ .buf_size = RX_CHANNEL_BUF_SIZE,
+ .buf_lock =
+ __RW_LOCK_UNLOCKED(mlb_devinfo[1].channels[1].
+ buf_lock),
+ },
+ },
+ .on = ATOMIC_INIT(0),
+ .opencnt = ATOMIC_INIT(0),
+ .rd_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[1].rd_wq),
+ .wt_wq = __WAIT_QUEUE_HEAD_INITIALIZER(mlb_devinfo[1].wt_wq),
+ .event_lock = __SPIN_LOCK_UNLOCKED(mlb_devinfo[1].event_lock),
+ },
+};
+
+static struct regulator *reg_nvcc; /* NVCC_MLB regulator */
+static struct clk *mlb_clk;
+static struct cdev mxc_mlb_dev; /* chareset device */
+static dev_t dev;
+static struct class *mlb_class; /* device class */
+static struct device *class_dev;
+static unsigned long mlb_base; /* mlb module base address */
+static unsigned int irq;
+
+/*!
+ * Initial the MLB module device
+ */
+static void mlb_dev_init(void)
+{
+ unsigned long dccr_val;
+ unsigned long phyaddr;
+
+ /* reset the MLB module */
+ __raw_writel(MLB_DCCR_RESET, mlb_base + MLB_REG_DCCR);
+ while (__raw_readl(mlb_base + MLB_REG_DCCR)
+ & MLB_DCCR_RESET) ;
+
+ /*!
+ * Enable MLB device, disable loopback mode,
+ * set default fps to 512, set mlb device address to 0
+ */
+ dccr_val = MLB_DCCR_EN;
+ __raw_writel(dccr_val, mlb_base + MLB_REG_DCCR);
+
+ /* disable all the system interrupt */
+ __raw_writel(0x5F, mlb_base + MLB_REG_SMCR);
+
+ /* write async, control tx/rx base address */
+ phyaddr = _get_txchan(0).phy_head >> 16;
+ __raw_writel(phyaddr << 16 | phyaddr, mlb_base + MLB_REG_CBCR);
+ phyaddr = _get_txchan(1).phy_head >> 16;
+ __raw_writel(phyaddr << 16 | phyaddr, mlb_base + MLB_REG_ABCR);
+
+}
+
+static void mlb_dev_exit(void)
+{
+ __raw_writel(0, mlb_base + MLB_REG_DCCR);
+}
+
+/*!
+ * MLB receive start function
+ *
+ * load phy_head to next buf register to start next rx
+ * here use single-packet buffer, set start=end
+ */
+static void mlb_start_rx(int cdev_id)
+{
+ struct mlb_channel_info *chinfo = &_get_rxchan(cdev_id);
+ unsigned long next;
+
+ next = chinfo->phy_head & 0xFFFC;
+ /* load next buf */
+ __raw_writel((next << 16) | next, mlb_base +
+ MLB_REG_CNBCRn + chinfo->reg_offset);
+ /* set ready bit to start next rx */
+ __raw_writel(MLB_CSCR_RDY, mlb_base + MLB_REG_CSCRn
+ + chinfo->reg_offset);
+}
+
+/*!
+ * MLB transmit start function
+ * make sure aquiring the rw buf_lock, when calling this
+ */
+static void mlb_start_tx(int cdev_id)
+{
+ struct mlb_channel_info *chinfo = &_get_txchan(cdev_id);
+ unsigned long begin, end;
+
+ begin = chinfo->phy_head;
+ end = (chinfo->phy_head + chinfo->buf_ptr - chinfo->buf_head) & 0xFFFC;
+ /* load next buf */
+ __raw_writel((begin << 16) | end, mlb_base +
+ MLB_REG_CNBCRn + chinfo->reg_offset);
+ /* set ready bit to start next tx */
+ __raw_writel(MLB_CSCR_RDY, mlb_base + MLB_REG_CSCRn
+ + chinfo->reg_offset);
+}
+
+/*!
+ * Enable the MLB channel
+ */
+static void mlb_channel_enable(int chan_dev_id, int on)
+{
+ unsigned long tx_regval = 0, rx_regval = 0;
+ /*!
+ * setup the direction, enable, channel type,
+ * mode select, channel address and mask buf start
+ */
+ if (on) {
+ unsigned int ctype = mlb_devinfo[chan_dev_id].channel_type;
+ tx_regval = MLB_CECR_CE | MLB_CECR_TR | MLB_CECR_MBS |
+ (ctype << MLB_CECR_CT_OFFSET) |
+ _get_txchan(chan_dev_id).address;
+ rx_regval = MLB_CECR_CE | MLB_CECR_MBS |
+ (ctype << MLB_CECR_CT_OFFSET) |
+ _get_rxchan(chan_dev_id).address;
+
+ atomic_set(&mlb_devinfo[chan_dev_id].on, 1);
+ } else {
+ atomic_set(&mlb_devinfo[chan_dev_id].on, 0);
+ }
+
+ /* update the rx/tx channel entry config */
+ __raw_writel(tx_regval, mlb_base + MLB_REG_CECRn +
+ _get_txchan(chan_dev_id).reg_offset);
+ __raw_writel(rx_regval, mlb_base + MLB_REG_CECRn +
+ _get_rxchan(chan_dev_id).reg_offset);
+
+ if (on)
+ mlb_start_rx(chan_dev_id);
+}
+
+/*!
+ * MLB interrupt handler
+ */
+void mlb_tx_isr(int minor, unsigned int cis)
+{
+ struct mlb_channel_info *chinfo = &_get_txchan(minor);
+
+ if (cis & MLB_CSCR_CBD) {
+ /* buffer done, reset the buf_ptr */
+ write_lock(&chinfo->buf_lock);
+ chinfo->buf_ptr = chinfo->buf_head;
+ write_unlock(&chinfo->buf_lock);
+ /* wake up the writer */
+ wake_up_interruptible(&mlb_devinfo[minor].wt_wq);
+ }
+}
+
+void mlb_rx_isr(int minor, unsigned int cis)
+{
+ struct mlb_channel_info *chinfo = &_get_rxchan(minor);
+ unsigned long end;
+ unsigned int len;
+
+ if (cis & MLB_CSCR_CBD) {
+
+ int wpos, rpos;
+
+ rpos = mlb_devinfo[minor].rdpos;
+ wpos = mlb_devinfo[minor].wtpos;
+
+ /* buffer done, get current buffer ptr */
+ end =
+ __raw_readl(mlb_base + MLB_REG_CCBCRn + chinfo->reg_offset);
+ end >>= 16; /* end here is phy */
+ len = end - (chinfo->phy_head & 0xFFFC);
+
+ /*!
+ * copy packet from IRAM buf to ring buf.
+ * if the wpos++ == rpos, drop this packet
+ */
+ if (((wpos + 1) % RX_RING_NODES) != rpos) {
+
+#ifdef DEBUG
+ if (mlb_devinfo[minor].channel_type == MLB_CTYPE_CTRL) {
+ if (len > CTRL_PACKET_SIZE)
+ pr_debug
+ ("mxc_mlb: ctrl packet"
+ "overflow\n");
+ } else {
+ if (len > ASYNC_PACKET_SIZE)
+ pr_debug
+ ("mxc_mlb: async packet"
+ "overflow\n");
+ }
+#endif
+ memcpy(mlb_devinfo[minor].rx_bufs[wpos].data,
+ (const void *)chinfo->buf_head, len);
+ mlb_devinfo[minor].rx_bufs[wpos].size = len;
+
+ /* update the ring wpos */
+ mlb_devinfo[minor].wtpos = (wpos + 1) % RX_RING_NODES;
+
+ /* wake up the reader */
+ wake_up_interruptible(&mlb_devinfo[minor].rd_wq);
+
+ pr_debug("recv package, len:%d, rdpos: %d, wtpos: %d\n",
+ len, rpos, mlb_devinfo[minor].wtpos);
+ } else {
+ pr_debug
+ ("drop package, due to no space, (%d,%d)\n",
+ rpos, mlb_devinfo[minor].wtpos);
+ }
+
+ /* start next rx */
+ mlb_start_rx(minor);
+ }
+}
+
+static irqreturn_t mlb_isr(int irq, void *dev_id)
+{
+ unsigned long int_status, sscr, tx_cis, rx_cis;
+ struct mlb_dev_info *pdev;
+ int minor;
+
+ sscr = __raw_readl(mlb_base + MLB_REG_SSCR);
+ pr_debug("mxc_mlb: system interrupt:%lx\n", sscr);
+ __raw_writel(0x7F, mlb_base + MLB_REG_SSCR);
+
+ int_status = __raw_readl(mlb_base + MLB_REG_CICR) & 0xFFFF;
+ pr_debug("mxc_mlb: channel interrupt ids: %lx\n", int_status);
+
+ for (minor = 0; minor < MLB_MINOR_DEVICES; minor++) {
+
+ pdev = &mlb_devinfo[minor];
+ tx_cis = rx_cis = 0;
+
+ /* get tx channel interrupt status */
+ if (int_status & (1 << (_get_txchan(minor).reg_offset >> 4)))
+ tx_cis = __raw_readl(mlb_base + MLB_REG_CSCRn
+ + _get_txchan(minor).reg_offset);
+ /* get rx channel interrupt status */
+ if (int_status & (1 << (_get_rxchan(minor).reg_offset >> 4)))
+ rx_cis = __raw_readl(mlb_base + MLB_REG_CSCRn
+ + _get_rxchan(minor).reg_offset);
+
+ if (!tx_cis && !rx_cis)
+ continue;
+
+ pr_debug("tx/rx int status: 0x%08lx/0x%08lx\n", tx_cis, rx_cis);
+ /* fill exception event */
+ spin_lock(&pdev->event_lock);
+ pdev->ex_event |= tx_cis & 0x303;
+ pdev->ex_event |= (rx_cis & 0x303) << 16;
+ spin_unlock(&pdev->event_lock);
+
+ /* clear the interrupt status */
+ __raw_writel(tx_cis & 0xFFFF, mlb_base + MLB_REG_CSCRn
+ + _get_txchan(minor).reg_offset);
+ __raw_writel(rx_cis & 0xFFFF, mlb_base + MLB_REG_CSCRn
+ + _get_rxchan(minor).reg_offset);
+
+ /* handel tx channel */
+ if (tx_cis)
+ mlb_tx_isr(minor, tx_cis);
+ /* handle rx channel */
+ if (rx_cis)
+ mlb_rx_isr(minor, rx_cis);
+
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mxc_mlb_open(struct inode *inode, struct file *filp)
+{
+ int minor;
+
+ minor = MINOR(inode->i_rdev);
+
+ if (minor < 0 || minor >= MLB_MINOR_DEVICES)
+ return -ENODEV;
+
+ /* open for each channel device */
+ if (atomic_cmpxchg(&mlb_devinfo[minor].opencnt, 0, 1) != 0)
+ return -EBUSY;
+
+ /* reset the buffer read/write ptr */
+ _get_txchan(minor).buf_ptr = _get_txchan(minor).buf_head;
+ _get_rxchan(minor).buf_ptr = _get_rxchan(minor).buf_head;
+ mlb_devinfo[minor].rdpos = mlb_devinfo[minor].wtpos = 0;
+ mlb_devinfo[minor].ex_event = 0;
+
+ return 0;
+}
+
+static int mxc_mlb_release(struct inode *inode, struct file *filp)
+{
+ int minor;
+
+ minor = MINOR(inode->i_rdev);
+
+ /* clear channel settings and info */
+ mlb_channel_enable(minor, 0);
+
+ /* decrease the open count */
+ atomic_set(&mlb_devinfo[minor].opencnt, 0);
+
+ return 0;
+}
+
+static int mxc_mlb_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ unsigned long flags, event;
+ int minor;
+
+ minor = MINOR(inode->i_rdev);
+
+ switch (cmd) {
+
+ case MLB_CHAN_SETADDR:
+ {
+ unsigned int caddr;
+ /* get channel address from user space */
+ if (copy_from_user(&caddr, argp, sizeof(caddr))) {
+ pr_err("mxc_mlb: copy from user failed\n");
+ return -EFAULT;
+ }
+ _get_txchan(minor).address = (caddr >> 16) & 0xFFFF;
+ _get_rxchan(minor).address = caddr & 0xFFFF;
+ break;
+ }
+
+ case MLB_CHAN_STARTUP:
+ if (atomic_read(&mlb_devinfo[minor].on)) {
+ pr_debug("mxc_mlb: channel areadly startup\n");
+ break;
+ }
+ mlb_channel_enable(minor, 1);
+ break;
+ case MLB_CHAN_SHUTDOWN:
+ if (atomic_read(&mlb_devinfo[minor].on) == 0) {
+ pr_debug("mxc_mlb: channel areadly shutdown\n");
+ break;
+ }
+ mlb_channel_enable(minor, 0);
+ break;
+ case MLB_CHAN_GETEVENT:
+ /* get and clear the ex_event */
+ spin_lock_irqsave(&mlb_devinfo[minor].event_lock, flags);
+ event = mlb_devinfo[minor].ex_event;
+ mlb_devinfo[minor].ex_event = 0;
+ spin_unlock_irqrestore(&mlb_devinfo[minor].event_lock, flags);
+
+ if (event) {
+ if (copy_to_user(argp, &event, sizeof(event))) {
+ pr_err("mxc_mlb: copy to user failed\n");
+ return -EFAULT;
+ }
+ } else {
+ pr_debug("mxc_mlb: no exception event now\n");
+ return -EAGAIN;
+ }
+ break;
+ case MLB_SET_FPS:
+ {
+ unsigned int fps;
+ unsigned long dccr_val;
+
+ /* get fps from user space */
+ if (copy_from_user(&fps, argp, sizeof(fps))) {
+ pr_err("mxc_mlb: copy from user failed\n");
+ return -EFAULT;
+ }
+
+ /* check fps value */
+ if (fps != 256 && fps != 512 && fps != 1024) {
+ pr_debug("mxc_mlb: invalid fps argument\n");
+ return -EINVAL;
+ }
+
+ dccr_val = __raw_readl(mlb_base + MLB_REG_DCCR);
+ dccr_val &= ~(0x3 << MLB_DCCR_FS_OFFSET);
+ dccr_val |= (fps >> 9) << MLB_DCCR_FS_OFFSET;
+ __raw_writel(dccr_val, mlb_base + MLB_REG_DCCR);
+ break;
+ }
+
+ case MLB_GET_VER:
+ {
+ unsigned long version;
+
+ /* get MLB device module version */
+ version = __raw_readl(mlb_base + MLB_REG_VCCR);
+
+ if (copy_to_user(argp, &version, sizeof(version))) {
+ pr_err("mxc_mlb: copy to user failed\n");
+ return -EFAULT;
+ }
+ break;
+ }
+
+ case MLB_SET_DEVADDR:
+ {
+ unsigned long dccr_val;
+ unsigned char devaddr;
+
+ /* get MLB device address from user space */
+ if (copy_from_user
+ (&devaddr, argp, sizeof(unsigned char))) {
+ pr_err("mxc_mlb: copy from user failed\n");
+ return -EFAULT;
+ }
+
+ dccr_val = __raw_readl(mlb_base + MLB_REG_DCCR);
+ dccr_val &= ~0xFF;
+ dccr_val |= devaddr;
+ __raw_writel(dccr_val, mlb_base + MLB_REG_DCCR);
+
+ break;
+ }
+ default:
+ pr_info("mxc_mlb: Invalid ioctl command\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*!
+ * MLB read routine
+ *
+ * Read the current received data from queued buffer,
+ * and free this buffer for hw to fill ingress data.
+ */
+static ssize_t mxc_mlb_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int minor, ret;
+ int size, rdpos;
+ struct mlb_rx_ringnode *rxbuf;
+
+ minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+
+ rdpos = mlb_devinfo[minor].rdpos;
+ rxbuf = mlb_devinfo[minor].rx_bufs;
+
+ /* check the current rx buffer is available or not */
+ if (rdpos == mlb_devinfo[minor].wtpos) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ /* if !O_NONBLOCK, we wait for recv packet */
+ ret = wait_event_interruptible(mlb_devinfo[minor].rd_wq,
+ (mlb_devinfo[minor].wtpos !=
+ rdpos));
+ if (ret < 0)
+ return ret;
+ }
+
+ size = rxbuf[rdpos].size;
+ if (size > count) {
+ /* the user buffer is too small */
+ pr_warning
+ ("mxc_mlb: received data size is bigger than count\n");
+ return -EINVAL;
+ }
+
+ /* copy rx buffer data to user buffer */
+ if (copy_to_user(buf, rxbuf[rdpos].data, size)) {
+ pr_err("mxc_mlb: copy from user failed\n");
+ return -EFAULT;
+ }
+
+ /* update the read ptr */
+ mlb_devinfo[minor].rdpos = (rdpos + 1) % RX_RING_NODES;
+
+ *f_pos = 0;
+
+ return size;
+}
+
+/*!
+ * MLB write routine
+ *
+ * Copy the user data to tx channel buffer,
+ * and prepare the channel current/next buffer ptr.
+ */
+static ssize_t mxc_mlb_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int minor;
+ unsigned long flags;
+ DEFINE_WAIT(__wait);
+ int ret;
+
+ minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+
+ if (count > _get_txchan(minor).buf_size) {
+ /* too many data to write */
+ pr_warning("mxc_mlb: overflow write data\n");
+ return -EFBIG;
+ }
+
+ *f_pos = 0;
+
+ /* check the current tx buffer is used or not */
+ write_lock_irqsave(&_get_txchan(minor).buf_lock, flags);
+ if (_get_txchan(minor).buf_ptr != _get_txchan(minor).buf_head) {
+ write_unlock_irqrestore(&_get_txchan(minor).buf_lock, flags);
+
+ /* there's already some datas being transmit now */
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ /* if !O_NONBLOCK, we wait for transmit finish */
+ for (;;) {
+ prepare_to_wait(&mlb_devinfo[minor].wt_wq,
+ &__wait, TASK_INTERRUPTIBLE);
+
+ write_lock_irqsave(&_get_txchan(minor).buf_lock, flags);
+ if (_get_txchan(minor).buf_ptr ==
+ _get_txchan(minor).buf_head)
+ break;
+
+ write_unlock_irqrestore(&_get_txchan(minor).buf_lock,
+ flags);
+ if (!signal_pending(current)) {
+ schedule();
+ continue;
+ }
+ return -ERESTARTSYS;
+ }
+ finish_wait(&mlb_devinfo[minor].wt_wq, &__wait);
+ }
+
+ /* copy user buffer to tx buffer */
+ if (copy_from_user((void *)_get_txchan(minor).buf_ptr, buf, count)) {
+ pr_err("mxc_mlb: copy from user failed\n");
+ ret = -EFAULT;
+ goto out;
+ }
+ _get_txchan(minor).buf_ptr += count;
+
+ /* set current/next buffer start/end */
+ mlb_start_tx(minor);
+
+ ret = count;
+
+out:
+ write_unlock_irqrestore(&_get_txchan(minor).buf_lock, flags);
+ return ret;
+}
+
+static unsigned int mxc_mlb_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ int minor;
+ unsigned int ret = 0;
+ unsigned long flags;
+
+ minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+
+ poll_wait(filp, &mlb_devinfo[minor].rd_wq, wait);
+ poll_wait(filp, &mlb_devinfo[minor].wt_wq, wait);
+
+ /* check the tx buffer is avaiable or not */
+ read_lock_irqsave(&_get_txchan(minor).buf_lock, flags);
+ if (_get_txchan(minor).buf_ptr == _get_txchan(minor).buf_head)
+ ret |= POLLOUT | POLLWRNORM;
+ read_unlock_irqrestore(&_get_txchan(minor).buf_lock, flags);
+
+ /* check the rx buffer filled or not */
+ if (mlb_devinfo[minor].rdpos != mlb_devinfo[minor].wtpos)
+ ret |= POLLIN | POLLRDNORM;
+
+ /* check the exception event */
+ if (mlb_devinfo[minor].ex_event)
+ ret |= POLLIN | POLLRDNORM;
+
+ return ret;
+}
+
+/*!
+ * char dev file operations structure
+ */
+static struct file_operations mxc_mlb_fops = {
+
+ .owner = THIS_MODULE,
+ .open = mxc_mlb_open,
+ .release = mxc_mlb_release,
+ .ioctl = mxc_mlb_ioctl,
+ .poll = mxc_mlb_poll,
+ .read = mxc_mlb_read,
+ .write = mxc_mlb_write,
+};
+
+/*!
+ * This function is called whenever the MLB device is detected.
+ */
+static int __devinit mxc_mlb_probe(struct platform_device *pdev)
+{
+ int ret, mlb_major, i, j;
+ struct mxc_mlb_platform_data *plat_data;
+ struct resource *res;
+ void __iomem *base;
+ unsigned long bufaddr, phyaddr;
+
+ /* malloc the Rx ring buffer firstly */
+ for (i = 0; i < MLB_MINOR_DEVICES; i++) {
+ char *buf;
+ int bufsize;
+
+ if (mlb_devinfo[i].channel_type == MLB_CTYPE_ASYNC)
+ bufsize = ASYNC_PACKET_SIZE;
+ else
+ bufsize = CTRL_PACKET_SIZE;
+
+ buf = kmalloc(bufsize * RX_RING_NODES, GFP_KERNEL);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "can not alloc rx buffers\n");
+ goto err4;
+ }
+ for (j = 0; j < RX_RING_NODES; j++) {
+ mlb_devinfo[i].rx_bufs[j].data = buf;
+ buf += bufsize;
+ }
+ }
+
+ /**
+ * Register MLB lld as two character devices
+ * One for Packet date channel, the other for control data channel
+ */
+ ret = alloc_chrdev_region(&dev, 0, MLB_MINOR_DEVICES, "mxc_mlb");
+ mlb_major = MAJOR(dev);
+
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't get major %d\n", mlb_major);
+ goto err3;
+ }
+
+ cdev_init(&mxc_mlb_dev, &mxc_mlb_fops);
+ mxc_mlb_dev.owner = THIS_MODULE;
+
+ ret = cdev_add(&mxc_mlb_dev, dev, MLB_MINOR_DEVICES);
+ if (ret) {
+ dev_err(&pdev->dev, "can't add cdev\n");
+ goto err2;
+ }
+
+ /* create class and device for udev information */
+ mlb_class = class_create(THIS_MODULE, "mlb");
+ if (IS_ERR(mlb_class)) {
+ dev_err(&pdev->dev, "failed to create mlb class\n");
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ for (i = 0; i < MLB_MINOR_DEVICES; i++) {
+
+ class_dev = device_create(mlb_class, NULL, MKDEV(mlb_major, i),
+ NULL, mlb_devinfo[i].dev_name);
+ if (IS_ERR(class_dev)) {
+ dev_err(&pdev->dev, "failed to create mlb %s"
+ " class device\n", mlb_devinfo[i].dev_name);
+ ret = -ENOMEM;
+ goto err1;
+ }
+ }
+
+ /* get irq line */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No mlb irq line provided\n");
+ goto err1;
+ }
+
+ irq = res->start;
+ /* request irq */
+ if (request_irq(irq, mlb_isr, 0, "mlb", NULL)) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ ret = -EBUSY;
+ goto err1;
+ }
+
+ /* ioremap from phy mlb to kernel space */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "No mlb base address provided\n");
+ goto err0;
+ }
+
+ base = ioremap(res->start, res->end - res->start);
+ dev_dbg(&pdev->dev, "mapped mlb base address: 0x%08x\n",
+ (unsigned int)base);
+
+ if (base == NULL) {
+ dev_err(&pdev->dev, "failed to do ioremap with mlb base\n");
+ goto err0;
+ }
+ mlb_base = (unsigned long)base;
+
+ /*!
+ * get rx/tx buffer address from platform data
+ * make sure the buf_address is 4bytes aligned
+ *
+ * ------------------- <-- plat_data->buf_address
+ * | minor 0 tx buf |
+ * -----------------
+ * | minor 0 rx buf |
+ * -----------------
+ * | .... |
+ * -----------------
+ * | minor n tx buf |
+ * -----------------
+ * | minor n rx buf |
+ * -------------------
+ */
+
+ plat_data = (struct mxc_mlb_platform_data *)pdev->dev.platform_data;
+
+ bufaddr = plat_data->buf_address & ~0x3;
+ phyaddr = plat_data->phy_address & ~0x3;
+
+ for (i = 0; i < MLB_MINOR_DEVICES; i++) {
+ /* set the virtual and physical buf head address */
+ _get_txchan(i).buf_head = bufaddr;
+ _get_txchan(i).phy_head = phyaddr;
+
+ bufaddr += TX_CHANNEL_BUF_SIZE;
+ phyaddr += TX_CHANNEL_BUF_SIZE;
+
+ _get_rxchan(i).buf_head = bufaddr;
+ _get_rxchan(i).phy_head = phyaddr;
+
+ bufaddr += RX_CHANNEL_BUF_SIZE;
+ phyaddr += RX_CHANNEL_BUF_SIZE;
+
+ dev_dbg(&pdev->dev, "phy_head: tx(%lx), rx(%lx)\n",
+ _get_txchan(i).phy_head, _get_rxchan(i).phy_head);
+ dev_dbg(&pdev->dev, "buf_head: tx(%lx), rx(%lx)\n",
+ _get_txchan(i).buf_head, _get_rxchan(i).buf_head);
+ }
+
+ /* enable GPIO */
+ gpio_mlb_active();
+
+ /* power on MLB */
+ reg_nvcc = regulator_get(&pdev->dev, plat_data->reg_nvcc);
+ /* set MAX LDO6 for NVCC to 2.5V */
+ regulator_set_voltage(reg_nvcc, 2500000, 2500000);
+ regulator_enable(reg_nvcc);
+
+ /* enable clock */
+ mlb_clk = clk_get(&pdev->dev, plat_data->mlb_clk);
+ clk_enable(mlb_clk);
+
+ /* initial MLB module */
+ mlb_dev_init();
+
+ return 0;
+
+err0:
+ free_irq(irq, NULL);
+err1:
+ for (--i; i >= 0; i--)
+ device_destroy(mlb_class, MKDEV(mlb_major, i));
+
+ class_destroy(mlb_class);
+err2:
+ cdev_del(&mxc_mlb_dev);
+err3:
+ unregister_chrdev_region(dev, MLB_MINOR_DEVICES);
+err4:
+ for (i = 0; i < MLB_MINOR_DEVICES; i++)
+ kfree(mlb_devinfo[i].rx_bufs[0].data);
+
+ return ret;
+}
+
+static int __devexit mxc_mlb_remove(struct platform_device *pdev)
+{
+ int i;
+
+ mlb_dev_exit();
+
+ /* disable mlb clock */
+ clk_disable(mlb_clk);
+ clk_put(mlb_clk);
+
+ /* disable mlb power */
+ regulator_disable(reg_nvcc);
+ regulator_put(reg_nvcc);
+
+ /* inactive GPIO */
+ gpio_mlb_inactive();
+
+ /* iounmap */
+ iounmap((void *)mlb_base);
+
+ free_irq(irq, NULL);
+
+ /* destroy mlb device class */
+ for (i = MLB_MINOR_DEVICES - 1; i >= 0; i--)
+ device_destroy(mlb_class, MKDEV(MAJOR(dev), i));
+ class_destroy(mlb_class);
+
+ /* Unregister the two MLB devices */
+ cdev_del(&mxc_mlb_dev);
+ unregister_chrdev_region(dev, MLB_MINOR_DEVICES);
+
+ for (i = 0; i < MLB_MINOR_DEVICES; i++)
+ kfree(mlb_devinfo[i].rx_bufs[0].data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mxc_mlb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+static int mxc_mlb_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define mxc_mlb_suspend NULL
+#define mxc_mlb_resume NULL
+#endif
+
+/*!
+ * platform driver structure for MLB
+ */
+static struct platform_driver mxc_mlb_driver = {
+ .driver = {
+ .name = "mxc_mlb"},
+ .probe = mxc_mlb_probe,
+ .remove = __devexit_p(mxc_mlb_remove),
+ .suspend = mxc_mlb_suspend,
+ .resume = mxc_mlb_resume,
+};
+
+static int __init mxc_mlb_init(void)
+{
+ return platform_driver_register(&mxc_mlb_driver);
+}
+
+static void __exit mxc_mlb_exit(void)
+{
+ platform_driver_unregister(&mxc_mlb_driver);
+}
+
+module_init(mxc_mlb_init);
+module_exit(mxc_mlb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MLB low level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/Kconfig b/drivers/mxc/pmic/Kconfig
new file mode 100644
index 000000000000..d2273996cb2c
--- /dev/null
+++ b/drivers/mxc/pmic/Kconfig
@@ -0,0 +1,64 @@
+#
+# PMIC device driver configuration
+#
+
+menu "MXC PMIC support"
+
+config MXC_PMIC
+ boolean
+
+config MXC_PMIC_MC13783
+ tristate "MC13783 PMIC"
+ depends on ARCH_MXC && SPI
+ select MXC_PMIC
+ ---help---
+ This is the MXC MC13783(PMIC) support. It include
+ ADC, Audio, Battery, Connectivity, Light, Power and RTC.
+
+config MXC_PMIC_MC13892
+ tristate "MC13892 PMIC"
+ depends on ARCH_MXC && (I2C || SPI)
+ select MXC_PMIC
+ ---help---
+ This is the MXC MC13892(PMIC) support. It include
+ ADC, Battery, Connectivity, Light, Power and RTC.
+
+config MXC_PMIC_I2C
+ bool "Support PMIC I2C Interface"
+ depends on MXC_PMIC_MC13892 && I2C
+
+config MXC_PMIC_SPI
+ bool "Support PMIC SPI Interface"
+ depends on (MXC_PMIC_MC13892 || MXC_PMIC_MC13783) && SPI
+
+config MXC_PMIC_MC34704
+ tristate "MC34704 PMIC"
+ depends on ARCH_MXC && I2C
+ select MXC_PMIC
+ ---help---
+ This is the MXC MC34704 PMIC support.
+
+config MXC_PMIC_MC9SDZ60
+ tristate "MC9sDZ60 PMIC"
+ depends on ARCH_MXC && I2C
+ select MXC_PMIC
+ ---help---
+ This is the MXC MC9sDZ60(MCU) PMIC support.
+
+config MXC_PMIC_CHARDEV
+ tristate "MXC PMIC device interface"
+ depends on MXC_PMIC
+ help
+ Say Y here to use "pmic" device files, found in the /dev directory
+ on the system. They make it possible to have user-space programs
+ use or controll PMIC. Mainly its useful for notifying PMIC events
+ to user-space programs.
+
+comment "MXC PMIC Client Drivers"
+ depends on MXC_PMIC
+
+source "drivers/mxc/pmic/mc13783/Kconfig"
+
+source "drivers/mxc/pmic/mc13892/Kconfig"
+
+endmenu
diff --git a/drivers/mxc/pmic/Makefile b/drivers/mxc/pmic/Makefile
new file mode 100644
index 000000000000..ef58a5889587
--- /dev/null
+++ b/drivers/mxc/pmic/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the MXC PMIC drivers.
+#
+
+obj-y += core/
+obj-$(CONFIG_MXC_PMIC_MC13783) += mc13783/
+obj-$(CONFIG_MXC_PMIC_MC13892) += mc13892/
+
diff --git a/drivers/mxc/pmic/core/Makefile b/drivers/mxc/pmic/core/Makefile
new file mode 100644
index 000000000000..e746702bc569
--- /dev/null
+++ b/drivers/mxc/pmic/core/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for the PMIC core drivers.
+#
+obj-$(CONFIG_MXC_PMIC_MC13783) += pmic_mc13783_mod.o
+pmic_mc13783_mod-objs := pmic_external.o pmic_event.o pmic_common.o pmic_core_spi.o mc13783.o
+
+obj-$(CONFIG_MXC_PMIC_MC13892) += pmic_mc13892_mod.o
+pmic_mc13892_mod-objs := pmic_external.o pmic_event.o pmic_common.o mc13892.o
+
+ifneq ($(CONFIG_MXC_PMIC_SPI),)
+pmic_mc13892_mod-objs += pmic_core_spi.o
+endif
+
+ifneq ($(CONFIG_MXC_PMIC_I2C),)
+pmic_mc13892_mod-objs += pmic_core_i2c.o
+endif
+
+obj-$(CONFIG_MXC_PMIC_MC34704) += pmic_mc34704_mod.o
+pmic_mc34704_mod-objs := pmic_external.o pmic_event.o mc34704.o
+
+obj-$(CONFIG_MXC_PMIC_CHARDEV) += pmic-dev.o
+
diff --git a/drivers/mxc/pmic/core/mc13783.c b/drivers/mxc/pmic/core/mc13783.c
new file mode 100644
index 000000000000..179cad815ebf
--- /dev/null
+++ b/drivers/mxc/pmic/core/mc13783.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file pmic/core/mc13783.c
+ * @brief This file contains MC13783 specific PMIC code. This implementaion
+ * may differ for each PMIC chip.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/mc13783/core.h>
+
+#include <asm/uaccess.h>
+
+#include "pmic.h"
+
+/*
+ * Defines
+ */
+#define EVENT_MASK_0 0x697fdf
+#define EVENT_MASK_1 0x3efffb
+#define MXC_PMIC_FRAME_MASK 0x00FFFFFF
+#define MXC_PMIC_MAX_REG_NUM 0x3F
+#define MXC_PMIC_REG_NUM_SHIFT 0x19
+#define MXC_PMIC_WRITE_BIT_SHIFT 31
+
+static unsigned int events_enabled0 = 0;
+static unsigned int events_enabled1 = 0;
+struct mxc_pmic pmic_drv_data;
+
+/*!
+ * This function is called to read a register on PMIC.
+ *
+ * @param reg_num number of the pmic register to be read
+ * @param reg_val return value of register
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+int pmic_read(unsigned int reg_num, unsigned int *reg_val)
+{
+ unsigned int frame = 0;
+ int ret = 0;
+
+ if (reg_num > MXC_PMIC_MAX_REG_NUM)
+ return PMIC_ERROR;
+
+ frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT;
+
+ ret = spi_rw(pmic_drv_data.spi, (u8 *) & frame, 1);
+
+ *reg_val = frame & MXC_PMIC_FRAME_MASK;
+
+ return ret;
+}
+
+/*!
+ * This function is called to write a value to the register on PMIC.
+ *
+ * @param reg_num number of the pmic register to be written
+ * @param reg_val value to be written
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+int pmic_write(int reg_num, const unsigned int reg_val)
+{
+ unsigned int frame = 0;
+ int ret = 0;
+
+ if (reg_num > MXC_PMIC_MAX_REG_NUM)
+ return PMIC_ERROR;
+
+ frame |= (1 << MXC_PMIC_WRITE_BIT_SHIFT);
+
+ frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT;
+
+ frame |= reg_val & MXC_PMIC_FRAME_MASK;
+
+ ret = spi_rw(pmic_drv_data.spi, (u8 *) & frame, 1);
+
+ return ret;
+}
+
+void *pmic_alloc_data(struct device *dev)
+{
+ struct mc13783 *mc13783;
+
+ mc13783 = kzalloc(sizeof(struct mc13783), GFP_KERNEL);
+ if (mc13783 == NULL)
+ return NULL;
+
+ mc13783->dev = dev;
+
+ return (void *)mc13783;
+}
+
+/*!
+ * This function initializes the SPI device parameters for this PMIC.
+ *
+ * @param spi the SPI slave device(PMIC)
+ *
+ * @return None
+ */
+int pmic_spi_setup(struct spi_device *spi)
+{
+ /* Setup the SPI slave i.e.PMIC */
+ pmic_drv_data.spi = spi;
+
+ spi->mode = SPI_MODE_2 | SPI_CS_HIGH;
+ spi->bits_per_word = 32;
+
+ return spi_setup(spi);
+}
+
+/*!
+ * This function initializes the PMIC registers.
+ *
+ * @return None
+ */
+int pmic_init_registers(void)
+{
+ CHECK_ERROR(pmic_write(REG_INTERRUPT_MASK_0, MXC_PMIC_FRAME_MASK));
+ CHECK_ERROR(pmic_write(REG_INTERRUPT_MASK_1, MXC_PMIC_FRAME_MASK));
+ CHECK_ERROR(pmic_write(REG_INTERRUPT_STATUS_0, MXC_PMIC_FRAME_MASK));
+ CHECK_ERROR(pmic_write(REG_INTERRUPT_STATUS_1, MXC_PMIC_FRAME_MASK));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function returns the PMIC version in system.
+ *
+ * @param ver pointer to the pmic_version_t structure
+ *
+ * @return This function returns PMIC version.
+ */
+void pmic_get_revision(pmic_version_t * ver)
+{
+ int rev_id = 0;
+ int rev1 = 0;
+ int rev2 = 0;
+ int finid = 0;
+ int icid = 0;
+
+ ver->id = PMIC_MC13783;
+ pmic_read(REG_REVISION, &rev_id);
+
+ rev1 = (rev_id & 0x018) >> 3;
+ rev2 = (rev_id & 0x007);
+ icid = (rev_id & 0x01C0) >> 6;
+ finid = (rev_id & 0x01E00) >> 9;
+
+ /* Ver 0.2 is actually 3.2a. Report as 3.2 */
+ if ((rev1 == 0) && (rev2 == 2)) {
+ rev1 = 3;
+ }
+
+ if (rev1 == 0 || icid != 2) {
+ ver->revision = -1;
+ printk(KERN_NOTICE
+ "mc13783: Not detected.\tAccess failed\t!!!\n");
+ } else {
+ ver->revision = ((rev1 * 10) + rev2);
+ printk(KERN_INFO "mc13783 Rev %d.%d FinVer %x detected\n", rev1,
+ rev2, finid);
+ }
+
+ return;
+
+}
+
+/*!
+ * This function reads the interrupt status registers of PMIC
+ * and determine the current active events.
+ *
+ * @param active_events array pointer to be used to return active
+ * event numbers.
+ *
+ * @return This function returns PMIC version.
+ */
+unsigned int pmic_get_active_events(unsigned int *active_events)
+{
+ unsigned int count = 0;
+ unsigned int status0, status1;
+ int bit_set;
+
+ pmic_read(REG_INTERRUPT_STATUS_0, &status0);
+ pmic_read(REG_INTERRUPT_STATUS_1, &status1);
+ pmic_write(REG_INTERRUPT_STATUS_0, status0);
+ pmic_write(REG_INTERRUPT_STATUS_1, status1);
+ status0 &= events_enabled0;
+ status1 &= events_enabled1;
+
+ while (status0) {
+ bit_set = ffs(status0) - 1;
+ *(active_events + count) = bit_set;
+ count++;
+ status0 ^= (1 << bit_set);
+ }
+ while (status1) {
+ bit_set = ffs(status1) - 1;
+ *(active_events + count) = bit_set + 24;
+ count++;
+ status1 ^= (1 << bit_set);
+ }
+
+ return count;
+}
+
+/*!
+ * This function unsets a bit in mask register of pmic to unmask an event IT.
+ *
+ * @param event the event to be unmasked
+ *
+ * @return This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+int pmic_event_unmask(type_event event)
+{
+ unsigned int event_mask = 0;
+ unsigned int mask_reg = 0;
+ unsigned int event_bit = 0;
+ int ret;
+
+ if (event < EVENT_E1HZI) {
+ mask_reg = REG_INTERRUPT_MASK_0;
+ event_mask = EVENT_MASK_0;
+ event_bit = (1 << event);
+ events_enabled0 |= event_bit;
+ } else {
+ event -= 24;
+ mask_reg = REG_INTERRUPT_MASK_1;
+ event_mask = EVENT_MASK_1;
+ event_bit = (1 << event);
+ events_enabled1 |= event_bit;
+ }
+
+ if ((event_bit & event_mask) == 0) {
+ pr_debug("Error: unmasking a reserved/unused event\n");
+ return PMIC_ERROR;
+ }
+
+ ret = pmic_write_reg(mask_reg, 0, event_bit);
+
+ pr_debug("Enable Event : %d\n", event);
+
+ return ret;
+}
+
+/*!
+ * This function sets a bit in mask register of pmic to disable an event IT.
+ *
+ * @param event the event to be masked
+ *
+ * @return This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+int pmic_event_mask(type_event event)
+{
+ unsigned int event_mask = 0;
+ unsigned int mask_reg = 0;
+ unsigned int event_bit = 0;
+ int ret;
+
+ if (event < EVENT_E1HZI) {
+ mask_reg = REG_INTERRUPT_MASK_0;
+ event_mask = EVENT_MASK_0;
+ event_bit = (1 << event);
+ events_enabled0 &= ~event_bit;
+ } else {
+ event -= 24;
+ mask_reg = REG_INTERRUPT_MASK_1;
+ event_mask = EVENT_MASK_1;
+ event_bit = (1 << event);
+ events_enabled1 &= ~event_bit;
+ }
+
+ if ((event_bit & event_mask) == 0) {
+ pr_debug("Error: masking a reserved/unused event\n");
+ return PMIC_ERROR;
+ }
+
+ ret = pmic_write_reg(mask_reg, event_bit, event_bit);
+
+ pr_debug("Disable Event : %d\n", event);
+
+ return ret;
+}
+
+/*!
+ * This function is called to read all sensor bits of PMIC.
+ *
+ * @param sensor Sensor to be checked.
+ *
+ * @return This function returns true if the sensor bit is high;
+ * or returns false if the sensor bit is low.
+ */
+bool pmic_check_sensor(t_sensor sensor)
+{
+ unsigned int reg_val = 0;
+
+ CHECK_ERROR(pmic_read_reg
+ (REG_INTERRUPT_SENSE_0, &reg_val, PMIC_ALL_BITS));
+
+ if ((1 << sensor) & reg_val)
+ return true;
+ else
+ return false;
+}
+
+/*!
+ * This function checks one sensor of PMIC.
+ *
+ * @param sensor_bits structure of all sensor bits.
+ *
+ * @return This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+
+PMIC_STATUS pmic_get_sensors(t_sensor_bits * sensor_bits)
+{
+ int sense_0 = 0;
+ int sense_1 = 0;
+
+ memset(sensor_bits, 0, sizeof(t_sensor_bits));
+
+ pmic_read_reg(REG_INTERRUPT_SENSE_0, &sense_0, 0xffffff);
+ pmic_read_reg(REG_INTERRUPT_SENSE_1, &sense_1, 0xffffff);
+
+ sensor_bits->sense_chgdets = (sense_0 & (1 << 6)) ? true : false;
+ sensor_bits->sense_chgovs = (sense_0 & (1 << 7)) ? true : false;
+ sensor_bits->sense_chgrevs = (sense_0 & (1 << 8)) ? true : false;
+ sensor_bits->sense_chgshorts = (sense_0 & (1 << 9)) ? true : false;
+ sensor_bits->sense_cccvs = (sense_0 & (1 << 10)) ? true : false;
+ sensor_bits->sense_chgcurrs = (sense_0 & (1 << 11)) ? true : false;
+ sensor_bits->sense_bpons = (sense_0 & (1 << 12)) ? true : false;
+ sensor_bits->sense_lobatls = (sense_0 & (1 << 13)) ? true : false;
+ sensor_bits->sense_lobaths = (sense_0 & (1 << 14)) ? true : false;
+ sensor_bits->sense_usb4v4s = (sense_0 & (1 << 16)) ? true : false;
+ sensor_bits->sense_usb2v0s = (sense_0 & (1 << 17)) ? true : false;
+ sensor_bits->sense_usb0v8s = (sense_0 & (1 << 18)) ? true : false;
+ sensor_bits->sense_id_floats = (sense_0 & (1 << 19)) ? true : false;
+ sensor_bits->sense_id_gnds = (sense_0 & (1 << 20)) ? true : false;
+ sensor_bits->sense_se1s = (sense_0 & (1 << 21)) ? true : false;
+ sensor_bits->sense_ckdets = (sense_0 & (1 << 22)) ? true : false;
+
+ sensor_bits->sense_onofd1s = (sense_1 & (1 << 3)) ? true : false;
+ sensor_bits->sense_onofd2s = (sense_1 & (1 << 4)) ? true : false;
+ sensor_bits->sense_onofd3s = (sense_1 & (1 << 5)) ? true : false;
+ sensor_bits->sense_pwrrdys = (sense_1 & (1 << 11)) ? true : false;
+ sensor_bits->sense_thwarnhs = (sense_1 & (1 << 12)) ? true : false;
+ sensor_bits->sense_thwarnls = (sense_1 & (1 << 13)) ? true : false;
+ sensor_bits->sense_clks = (sense_1 & (1 << 14)) ? true : false;
+ sensor_bits->sense_mc2bs = (sense_1 & (1 << 17)) ? true : false;
+ sensor_bits->sense_hsdets = (sense_1 & (1 << 18)) ? true : false;
+ sensor_bits->sense_hsls = (sense_1 & (1 << 19)) ? true : false;
+ sensor_bits->sense_alspths = (sense_1 & (1 << 20)) ? true : false;
+ sensor_bits->sense_ahsshorts = (sense_1 & (1 << 21)) ? true : false;
+ return PMIC_SUCCESS;
+}
+
+EXPORT_SYMBOL(pmic_check_sensor);
+EXPORT_SYMBOL(pmic_get_sensors);
diff --git a/drivers/mxc/pmic/core/mc13892.c b/drivers/mxc/pmic/core/mc13892.c
new file mode 100644
index 000000000000..34ceec59221e
--- /dev/null
+++ b/drivers/mxc/pmic/core/mc13892.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file pmic/core/mc13892.c
+ * @brief This file contains MC13892 specific PMIC code. This implementaion
+ * may differ for each PMIC chip.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+#include <linux/mfd/mc13892/core.h>
+
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+
+#include "pmic.h"
+
+/*
+ * Defines
+ */
+#define MC13892_I2C_RETRY_TIMES 10
+#define MXC_PMIC_FRAME_MASK 0x00FFFFFF
+#define MXC_PMIC_MAX_REG_NUM 0x3F
+#define MXC_PMIC_REG_NUM_SHIFT 0x19
+#define MXC_PMIC_WRITE_BIT_SHIFT 31
+
+static unsigned int events_enabled0;
+static unsigned int events_enabled1;
+static struct mxc_pmic pmic_drv_data;
+#ifndef CONFIG_MXC_PMIC_I2C
+struct i2c_client *mc13892_client;
+#endif
+
+int pmic_i2c_24bit_read(struct i2c_client *client, unsigned int reg_num,
+ unsigned int *value)
+{
+ unsigned char buf[3];
+ int ret;
+ int i;
+
+ memset(buf, 0, 3);
+ for (i = 0; i < MC13892_I2C_RETRY_TIMES; i++) {
+ ret = i2c_smbus_read_i2c_block_data(client, reg_num, 3, buf);
+ if (ret == 3)
+ break;
+ msleep(1);
+ }
+
+ if (ret == 3) {
+ *value = buf[0] << 16 | buf[1] << 8 | buf[2];
+ return ret;
+ } else {
+ pr_debug("24bit read error, ret = %d\n", ret);
+ return -1; /* return -1 on failure */
+ }
+}
+
+int pmic_i2c_24bit_write(struct i2c_client *client,
+ unsigned int reg_num, unsigned int reg_val)
+{
+ char buf[3];
+ int ret;
+ int i;
+
+ buf[0] = (reg_val >> 16) & 0xff;
+ buf[1] = (reg_val >> 8) & 0xff;
+ buf[2] = (reg_val) & 0xff;
+
+ for (i = 0; i < MC13892_I2C_RETRY_TIMES; i++) {
+ ret = i2c_smbus_write_i2c_block_data(client, reg_num, 3, buf);
+ if (ret == 0)
+ break;
+ msleep(1);
+ }
+
+ return ret;
+}
+
+int pmic_read(int reg_num, unsigned int *reg_val)
+{
+ unsigned int frame = 0;
+ int ret = 0;
+
+ if (pmic_drv_data.spi != NULL) {
+ if (reg_num > MXC_PMIC_MAX_REG_NUM)
+ return PMIC_ERROR;
+
+ frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT;
+
+ ret = spi_rw(pmic_drv_data.spi, (u8 *) &frame, 1);
+
+ *reg_val = frame & MXC_PMIC_FRAME_MASK;
+ } else {
+ if (mc13892_client == NULL)
+ return PMIC_ERROR;
+
+ if (pmic_i2c_24bit_read(mc13892_client, reg_num, reg_val) == -1)
+ return PMIC_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+int pmic_write(int reg_num, const unsigned int reg_val)
+{
+ unsigned int frame = 0;
+ int ret = 0;
+
+ if (pmic_drv_data.spi != NULL) {
+ if (reg_num > MXC_PMIC_MAX_REG_NUM)
+ return PMIC_ERROR;
+
+ frame |= (1 << MXC_PMIC_WRITE_BIT_SHIFT);
+
+ frame |= reg_num << MXC_PMIC_REG_NUM_SHIFT;
+
+ frame |= reg_val & MXC_PMIC_FRAME_MASK;
+
+ ret = spi_rw(pmic_drv_data.spi, (u8 *) &frame, 1);
+
+ return ret;
+ } else {
+ if (mc13892_client == NULL)
+ return PMIC_ERROR;
+
+ return pmic_i2c_24bit_write(mc13892_client, reg_num, reg_val);
+ }
+}
+
+void *pmic_alloc_data(struct device *dev)
+{
+ struct mc13892 *mc13892;
+
+ mc13892 = kzalloc(sizeof(struct mc13892), GFP_KERNEL);
+ if (mc13892 == NULL)
+ return NULL;
+
+ mc13892->dev = dev;
+
+ return (void *)mc13892;
+}
+
+/*!
+ * This function initializes the SPI device parameters for this PMIC.
+ *
+ * @param spi the SPI slave device(PMIC)
+ *
+ * @return None
+ */
+int pmic_spi_setup(struct spi_device *spi)
+{
+ /* Setup the SPI slave i.e.PMIC */
+ pmic_drv_data.spi = spi;
+
+ spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
+ spi->bits_per_word = 32;
+
+ return spi_setup(spi);
+}
+
+int pmic_init_registers(void)
+{
+ CHECK_ERROR(pmic_write(REG_INT_MASK0, 0xFFFFFF));
+ CHECK_ERROR(pmic_write(REG_INT_MASK0, 0xFFFFFF));
+ CHECK_ERROR(pmic_write(REG_INT_STATUS0, 0xFFFFFF));
+ CHECK_ERROR(pmic_write(REG_INT_STATUS1, 0xFFFFFF));
+ /* disable auto charge */
+ if (machine_is_mx51_3ds())
+ CHECK_ERROR(pmic_write(REG_CHARGE, 0xB40003));
+
+ pm_power_off = mc13892_power_off;
+
+ return PMIC_SUCCESS;
+}
+
+unsigned int pmic_get_active_events(unsigned int *active_events)
+{
+ unsigned int count = 0;
+ unsigned int status0, status1;
+ int bit_set;
+
+ pmic_read(REG_INT_STATUS0, &status0);
+ pmic_read(REG_INT_STATUS1, &status1);
+ pmic_write(REG_INT_STATUS0, status0);
+ pmic_write(REG_INT_STATUS1, status1);
+ status0 &= events_enabled0;
+ status1 &= events_enabled1;
+
+ while (status0) {
+ bit_set = ffs(status0) - 1;
+ *(active_events + count) = bit_set;
+ count++;
+ status0 ^= (1 << bit_set);
+ }
+ while (status1) {
+ bit_set = ffs(status1) - 1;
+ *(active_events + count) = bit_set + 24;
+ count++;
+ status1 ^= (1 << bit_set);
+ }
+
+ return count;
+}
+
+#define EVENT_MASK_0 0x387fff
+#define EVENT_MASK_1 0x1177eb
+
+int pmic_event_unmask(type_event event)
+{
+ unsigned int event_mask = 0;
+ unsigned int mask_reg = 0;
+ unsigned int event_bit = 0;
+ int ret;
+
+ if (event < EVENT_1HZI) {
+ mask_reg = REG_INT_MASK0;
+ event_mask = EVENT_MASK_0;
+ event_bit = (1 << event);
+ events_enabled0 |= event_bit;
+ } else {
+ event -= 24;
+ mask_reg = REG_INT_MASK1;
+ event_mask = EVENT_MASK_1;
+ event_bit = (1 << event);
+ events_enabled1 |= event_bit;
+ }
+
+ if ((event_bit & event_mask) == 0) {
+ pr_debug("Error: unmasking a reserved/unused event\n");
+ return PMIC_ERROR;
+ }
+
+ ret = pmic_write_reg(mask_reg, 0, event_bit);
+
+ pr_debug("Enable Event : %d\n", event);
+
+ return ret;
+}
+
+int pmic_event_mask(type_event event)
+{
+ unsigned int event_mask = 0;
+ unsigned int mask_reg = 0;
+ unsigned int event_bit = 0;
+ int ret;
+
+ if (event < EVENT_1HZI) {
+ mask_reg = REG_INT_MASK0;
+ event_mask = EVENT_MASK_0;
+ event_bit = (1 << event);
+ events_enabled0 &= ~event_bit;
+ } else {
+ event -= 24;
+ mask_reg = REG_INT_MASK1;
+ event_mask = EVENT_MASK_1;
+ event_bit = (1 << event);
+ events_enabled1 &= ~event_bit;
+ }
+
+ if ((event_bit & event_mask) == 0) {
+ pr_debug("Error: masking a reserved/unused event\n");
+ return PMIC_ERROR;
+ }
+
+ ret = pmic_write_reg(mask_reg, event_bit, event_bit);
+
+ pr_debug("Disable Event : %d\n", event);
+
+ return ret;
+}
+
+/*!
+ * This function returns the PMIC version in system.
+ *
+ * @param ver pointer to the pmic_version_t structure
+ *
+ * @return This function returns PMIC version.
+ */
+void pmic_get_revision(pmic_version_t *ver)
+{
+ int rev_id = 0;
+ int rev1 = 0;
+ int rev2 = 0;
+ int finid = 0;
+ int icid = 0;
+
+ ver->id = PMIC_MC13892;
+ pmic_read(REG_IDENTIFICATION, &rev_id);
+
+ rev1 = (rev_id & 0x018) >> 3;
+ rev2 = (rev_id & 0x007);
+ icid = (rev_id & 0x01C0) >> 6;
+ finid = (rev_id & 0x01E00) >> 9;
+
+ ver->revision = ((rev1 * 10) + rev2);
+ printk(KERN_INFO "mc13892 Rev %d.%d FinVer %x detected\n", rev1,
+ rev2, finid);
+}
+
+void mc13892_power_off(void)
+{
+ unsigned int value;
+
+ pmic_read_reg(REG_POWER_CTL0, &value, 0xffffff);
+
+ value |= 0x000008;
+
+ pmic_write_reg(REG_POWER_CTL0, value, 0xffffff);
+}
diff --git a/drivers/mxc/pmic/core/mc34704.c b/drivers/mxc/pmic/core/mc34704.c
new file mode 100644
index 000000000000..f0ec05afe0ab
--- /dev/null
+++ b/drivers/mxc/pmic/core/mc34704.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file pmic/core/mc34704.c
+ * @brief This file contains MC34704 specific PMIC code.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/mc34704/core.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+
+#include "pmic.h"
+
+/*
+ * Globals
+ */
+static pmic_version_t mxc_pmic_version = {
+ .id = PMIC_MC34704,
+ .revision = 0,
+};
+static unsigned int events_enabled;
+unsigned int active_events[MAX_ACTIVE_EVENTS];
+struct i2c_client *mc34704_client;
+static void pmic_trigger_poll(void);
+
+#define MAX_MC34704_REG 0x59
+static unsigned int mc34704_reg_readonly[MAX_MC34704_REG / 32 + 1] = {
+ (1 << 0x03) || (1 << 0x05) || (1 << 0x07) || (1 << 0x09) ||
+ (1 << 0x0B) || (1 << 0x0E) || (1 << 0x11) || (1 << 0x14) ||
+ (1 << 0x17) || (1 << 0x18),
+ 0,
+};
+static unsigned int mc34704_reg_written[MAX_MC34704_REG / 32 + 1];
+static unsigned char mc34704_shadow_regs[MAX_MC34704_REG - 1];
+#define IS_READONLY(r) ((1 << ((r) % 32)) & mc34704_reg_readonly[(r) / 32])
+#define WAS_WRITTEN(r) ((1 << ((r) % 32)) & mc34704_reg_written[(r) / 32])
+#define MARK_WRITTEN(r) do { \
+ mc34704_reg_written[(r) / 32] |= (1 << ((r) % 32)); \
+} while (0)
+
+int pmic_read(int reg_nr, unsigned int *reg_val)
+{
+ int c;
+
+ /*
+ * Use the shadow register if we've written to it
+ */
+ if (WAS_WRITTEN(reg_nr)) {
+ *reg_val = mc34704_shadow_regs[reg_nr];
+ return PMIC_SUCCESS;
+ }
+
+ /*
+ * Otherwise, actually read the real register.
+ * Write-only registers will read as zero.
+ */
+ c = i2c_smbus_read_byte_data(mc34704_client, reg_nr);
+ if (c == -1) {
+ pr_debug("mc34704: error reading register 0x%02x\n", reg_nr);
+ return PMIC_ERROR;
+ } else {
+ *reg_val = c;
+ return PMIC_SUCCESS;
+ }
+}
+
+int pmic_write(int reg_nr, const unsigned int reg_val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(mc34704_client, reg_nr, reg_val);
+ if (ret == -1) {
+ return PMIC_ERROR;
+ } else {
+ /*
+ * Update our software copy of the register since you
+ * can't read what you wrote.
+ */
+ if (!IS_READONLY(reg_nr)) {
+ mc34704_shadow_regs[reg_nr] = reg_val;
+ MARK_WRITTEN(reg_nr);
+ }
+ return PMIC_SUCCESS;
+ }
+}
+
+unsigned int pmic_get_active_events(unsigned int *active_events)
+{
+ unsigned int count = 0;
+ unsigned int faults;
+ int bit_set;
+
+ /* Check for any relevant PMIC faults */
+ pmic_read(REG_MC34704_FAULTS, &faults);
+ faults &= events_enabled;
+
+ /*
+ * Mask all active events, because there is no way to acknowledge
+ * or dismiss them in the PMIC -- they're sticky.
+ */
+ events_enabled &= ~faults;
+
+ /* Account for all unmasked faults */
+ while (faults) {
+ bit_set = ffs(faults) - 1;
+ *(active_events + count) = bit_set;
+ count++;
+ faults ^= (1 << bit_set);
+ }
+ return count;
+}
+
+int pmic_event_unmask(type_event event)
+{
+ unsigned int event_bit = 0;
+ unsigned int prior_events = events_enabled;
+
+ event_bit = (1 << event);
+ events_enabled |= event_bit;
+
+ pr_debug("Enable Event : %d\n", event);
+
+ /* start the polling task as needed */
+ if (events_enabled && prior_events == 0)
+ pmic_trigger_poll();
+
+ return 0;
+}
+
+int pmic_event_mask(type_event event)
+{
+ unsigned int event_bit = 0;
+
+ event_bit = (1 << event);
+ events_enabled &= ~event_bit;
+
+ pr_debug("Disable Event : %d\n", event);
+
+ return 0;
+}
+
+/*!
+ * PMIC event polling task. This task is called periodically to poll
+ * for possible MC34704 events (No interrupt supplied by the hardware).
+ */
+static void pmic_event_task(struct work_struct *work);
+DECLARE_DELAYED_WORK(pmic_ws, pmic_event_task);
+
+static void pmic_trigger_poll(void)
+{
+ schedule_delayed_work(&pmic_ws, HZ / 10);
+}
+
+static void pmic_event_task(struct work_struct *work)
+{
+ unsigned int count = 0;
+ int i;
+
+ count = pmic_get_active_events(active_events);
+ pr_debug("active events number %d\n", count);
+
+ /* call handlers for all active events */
+ for (i = 0; i < count; i++)
+ pmic_event_callback(active_events[i]);
+
+ /* re-trigger this task, but only if somebody is watching */
+ if (events_enabled)
+ pmic_trigger_poll();
+
+ return;
+}
+
+pmic_version_t pmic_get_version(void)
+{
+ return mxc_pmic_version;
+}
+EXPORT_SYMBOL(pmic_get_version);
+
+int __devinit pmic_init_registers(void)
+{
+ /*
+ * Set some registers to what they should be,
+ * if for no other reason than to initialize our
+ * software register copies.
+ */
+ CHECK_ERROR(pmic_write(REG_MC34704_GENERAL2, 0x09));
+ CHECK_ERROR(pmic_write(REG_MC34704_VGSET1, 0));
+ CHECK_ERROR(pmic_write(REG_MC34704_REG2SET1, 0));
+ CHECK_ERROR(pmic_write(REG_MC34704_REG3SET1, 0));
+ CHECK_ERROR(pmic_write(REG_MC34704_REG4SET1, 0));
+ CHECK_ERROR(pmic_write(REG_MC34704_REG5SET1, 0));
+
+ return PMIC_SUCCESS;
+}
+
+static int __devinit is_chip_onboard(struct i2c_client *client)
+{
+ int val;
+
+ /*
+ * This PMIC has no version or ID register, so just see
+ * if it ACK's and returns 0 on some write-only register as
+ * evidence of its presence.
+ */
+ val = i2c_smbus_read_byte_data(client, REG_MC34704_GENERAL2);
+ if (val != 0)
+ return -1;
+
+ return 0;
+}
+
+static int __devinit pmic_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct mc34704 *mc34704;
+ struct mc34704_platform_data *plat_data = client->dev.platform_data;
+
+ if (!plat_data || !plat_data->init)
+ return -ENODEV;
+
+ ret = is_chip_onboard(client);
+
+ if (ret == -1)
+ return -ENODEV;
+
+ mc34704 = kzalloc(sizeof(struct mc34704), GFP_KERNEL);
+ if (mc34704 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, mc34704);
+ mc34704->dev = &client->dev;
+ mc34704->i2c_client = client;
+
+ mc34704_client = client;
+
+ /* Initialize the PMIC event handling */
+ pmic_event_list_init();
+
+ /* Initialize PMI registers */
+ if (pmic_init_registers() != PMIC_SUCCESS)
+ return PMIC_ERROR;
+
+ ret = plat_data->init(mc34704);
+ if (ret != 0)
+ return PMIC_ERROR;
+
+ dev_info(&client->dev, "Loaded\n");
+
+ return PMIC_SUCCESS;
+}
+
+static int pmic_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static int pmic_suspend(struct i2c_client *client, pm_message_t state)
+{
+ return 0;
+}
+
+static int pmic_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id mc34704_id[] = {
+ {"mc34704", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc34704_id);
+
+static struct i2c_driver pmic_driver = {
+ .driver = {
+ .name = "mc34704",
+ .bus = NULL,
+ },
+ .probe = pmic_probe,
+ .remove = pmic_remove,
+ .suspend = pmic_suspend,
+ .resume = pmic_resume,
+ .id_table = mc34704_id,
+};
+
+static int __init pmic_init(void)
+{
+ return i2c_add_driver(&pmic_driver);
+}
+
+static void __exit pmic_exit(void)
+{
+ i2c_del_driver(&pmic_driver);
+}
+
+/*
+ * Module entry points
+ */
+subsys_initcall_sync(pmic_init);
+module_exit(pmic_exit);
+
+MODULE_DESCRIPTION("MC34704 PMIC driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/core/pmic-dev.c b/drivers/mxc/pmic/core/pmic-dev.c
new file mode 100644
index 000000000000..fee100bfacec
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic-dev.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All rights reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file pmic-dev.c
+ * @brief This provides /dev interface to the user program. They make it
+ * possible to have user-space programs use or control PMIC. Mainly its
+ * useful for notifying PMIC events to user-space programs.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kdev_t.h>
+#include <linux/circ_buf.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pmic_external.h>
+
+#include <asm/uaccess.h>
+
+#define PMIC_NAME "pmic"
+#define CIRC_BUF_MAX 16
+#define CIRC_ADD(elem,cir_buf,size) \
+ down(&event_mutex); \
+ if(CIRC_SPACE(cir_buf.head, cir_buf.tail, size)){ \
+ cir_buf.buf[cir_buf.head] = (char)elem; \
+ cir_buf.head = (cir_buf.head + 1) & (size - 1); \
+ } else { \
+ pr_info("Failed to notify event to the user\n");\
+ } \
+ up(&event_mutex);
+
+#define CIRC_REMOVE(elem,cir_buf,size) \
+ down(&event_mutex); \
+ if(CIRC_CNT(cir_buf.head, cir_buf.tail, size)){ \
+ elem = (int)cir_buf.buf[cir_buf.tail]; \
+ cir_buf.tail = (cir_buf.tail + 1) & (size - 1); \
+ } else { \
+ elem = -1; \
+ pr_info("No valid notified event\n"); \
+ } \
+ up(&event_mutex);
+
+static int pmic_major;
+static struct class *pmic_class;
+static struct fasync_struct *pmic_dev_queue;
+
+static DECLARE_MUTEX(event_mutex);
+static struct circ_buf pmic_events;
+
+static void callbackfn(void *event)
+{
+ printk(KERN_INFO "\n\n DETECTED PMIC EVENT : %d\n\n",
+ (unsigned int)event);
+}
+
+static void user_notify_callback(void *event)
+{
+ CIRC_ADD((int)event, pmic_events, CIRC_BUF_MAX);
+ kill_fasync(&pmic_dev_queue, SIGIO, POLL_IN);
+}
+
+/*!
+ * This function implements IOCTL controls on a PMIC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @param cmd the command
+ * @param arg the parameter
+ * @return This function returns 0 if successful.
+ */
+static int pmic_dev_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ register_info reg_info;
+ pmic_event_callback_t event_sub;
+ type_event event;
+ int ret = 0;
+
+ if (_IOC_TYPE(cmd) != 'P')
+ return -ENOTTY;
+
+ switch (cmd) {
+ case PMIC_READ_REG:
+ if (copy_from_user(&reg_info, (register_info *) arg,
+ sizeof(register_info))) {
+ return -EFAULT;
+ }
+ ret =
+ pmic_read_reg(reg_info.reg, &(reg_info.reg_value),
+ 0x00ffffff);
+ pr_debug("read reg %d %x\n", reg_info.reg, reg_info.reg_value);
+ if (copy_to_user((register_info *) arg, &reg_info,
+ sizeof(register_info))) {
+ return -EFAULT;
+ }
+ break;
+
+ case PMIC_WRITE_REG:
+ if (copy_from_user(&reg_info, (register_info *) arg,
+ sizeof(register_info))) {
+ return -EFAULT;
+ }
+ ret =
+ pmic_write_reg(reg_info.reg, reg_info.reg_value,
+ 0x00ffffff);
+ pr_debug("write reg %d %x\n", reg_info.reg, reg_info.reg_value);
+ if (copy_to_user((register_info *) arg, &reg_info,
+ sizeof(register_info))) {
+ return -EFAULT;
+ }
+ break;
+
+ case PMIC_SUBSCRIBE:
+ if (get_user(event, (int __user *)arg)) {
+ return -EFAULT;
+ }
+ event_sub.func = callbackfn;
+ event_sub.param = (void *)event;
+ ret = pmic_event_subscribe(event, event_sub);
+ pr_debug("subscribe done\n");
+ break;
+
+ case PMIC_UNSUBSCRIBE:
+ if (get_user(event, (int __user *)arg)) {
+ return -EFAULT;
+ }
+ event_sub.func = callbackfn;
+ event_sub.param = (void *)event;
+ ret = pmic_event_unsubscribe(event, event_sub);
+ pr_debug("unsubscribe done\n");
+ break;
+
+ case PMIC_NOTIFY_USER:
+ if (get_user(event, (int __user *)arg)) {
+ return -EFAULT;
+ }
+ event_sub.func = user_notify_callback;
+ event_sub.param = (void *)event;
+ ret = pmic_event_subscribe(event, event_sub);
+ break;
+
+ case PMIC_GET_NOTIFY:
+ CIRC_REMOVE(event, pmic_events, CIRC_BUF_MAX);
+ if (put_user(event, (int __user *)arg)) {
+ return -EFAULT;
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%d unsupported ioctl command\n", (int)cmd);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/*!
+ * This function implements the open method on a PMIC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_dev_open(struct inode *inode, struct file *file)
+{
+ pr_debug("open\n");
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function implements the release method on a PMIC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ *
+ * @return This function returns 0.
+ */
+static int pmic_dev_free(struct inode *inode, struct file *file)
+{
+ pr_debug("free\n");
+ return PMIC_SUCCESS;
+}
+
+static int pmic_dev_fasync(int fd, struct file *filp, int mode)
+{
+ return fasync_helper(fd, filp, mode, &pmic_dev_queue);
+}
+
+/*!
+ * This structure defines file operations for a PMIC device.
+ */
+static struct file_operations pmic_fops = {
+ /*!
+ * the owner
+ */
+ .owner = THIS_MODULE,
+ /*!
+ * the ioctl operation
+ */
+ .ioctl = pmic_dev_ioctl,
+ /*!
+ * the open operation
+ */
+ .open = pmic_dev_open,
+ /*!
+ * the release operation
+ */
+ .release = pmic_dev_free,
+ /*!
+ * the release operation
+ */
+ .fasync = pmic_dev_fasync,
+};
+
+/*!
+ * This function implements the init function of the PMIC char device.
+ * This function is called when the module is loaded. It registers
+ * the character device for PMIC to be used by user-space programs.
+ *
+ * @return This function returns 0.
+ */
+static int __init pmic_dev_init(void)
+{
+ int ret = 0;
+ struct device *pmic_device;
+ pmic_version_t pmic_ver;
+
+ pmic_ver = pmic_get_version();
+ if (pmic_ver.revision < 0) {
+ printk(KERN_ERR "No PMIC device found\n");
+ return -ENODEV;
+ }
+
+ pmic_major = register_chrdev(0, PMIC_NAME, &pmic_fops);
+ if (pmic_major < 0) {
+ printk(KERN_ERR "unable to get a major for pmic\n");
+ return pmic_major;
+ }
+
+ pmic_class = class_create(THIS_MODULE, PMIC_NAME);
+ if (IS_ERR(pmic_class)) {
+ printk(KERN_ERR "Error creating pmic class.\n");
+ ret = PMIC_ERROR;
+ goto err;
+ }
+
+ pmic_device = device_create(pmic_class, NULL, MKDEV(pmic_major, 0), NULL,
+ PMIC_NAME);
+ if (IS_ERR(pmic_device)) {
+ printk(KERN_ERR "Error creating pmic class device.\n");
+ ret = PMIC_ERROR;
+ goto err1;
+ }
+
+ pmic_events.buf = kmalloc(CIRC_BUF_MAX * sizeof(char), GFP_KERNEL);
+ if (NULL == pmic_events.buf) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+ pmic_events.head = pmic_events.tail = 0;
+
+ printk(KERN_INFO "PMIC Character device: successfully loaded\n");
+ return ret;
+ err2:
+ device_destroy(pmic_class, MKDEV(pmic_major, 0));
+ err1:
+ class_destroy(pmic_class);
+ err:
+ unregister_chrdev(pmic_major, PMIC_NAME);
+ return ret;
+
+}
+
+/*!
+ * This function implements the exit function of the PMIC character device.
+ * This function is called when the module is unloaded. It unregisters
+ * the PMIC character device.
+ *
+ */
+static void __exit pmic_dev_exit(void)
+{
+ device_destroy(pmic_class, MKDEV(pmic_major, 0));
+ class_destroy(pmic_class);
+
+ unregister_chrdev(pmic_major, PMIC_NAME);
+
+ printk(KERN_INFO "PMIC Character device: successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+module_init(pmic_dev_init);
+module_exit(pmic_dev_exit);
+
+MODULE_DESCRIPTION("PMIC Protocol /dev entries driver");
+MODULE_AUTHOR("FreeScale");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/core/pmic.h b/drivers/mxc/pmic/core/pmic.h
new file mode 100644
index 000000000000..b1382a34e850
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __PMIC_H__
+#define __PMIC_H__
+
+ /*!
+ * @file pmic.h
+ * @brief This file contains prototypes of all the functions to be
+ * defined for each PMIC chip. The implementation of these may differ
+ * from PMIC chip to PMIC chip.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+#include <linux/spi/spi.h>
+
+#define MAX_ACTIVE_EVENTS 10
+
+/*!
+ * This structure is a way for the PMIC core driver to define their own
+ * \b spi_device structure. This structure includes the core \b spi_device
+ * structure that is provided by Linux SPI Framework/driver as an
+ * element and may contain other elements that are required by core driver.
+ */
+struct mxc_pmic {
+ /*!
+ * Master side proxy for an SPI slave device(PMIC)
+ */
+ struct spi_device *spi;
+};
+
+/*!
+ * This function is called to transfer data to PMIC on SPI.
+ *
+ * @param spi the SPI slave device(PMIC)
+ * @param buf the pointer to the data buffer
+ * @param len the length of the data to be transferred
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+static inline int spi_rw(struct spi_device *spi, u8 * buf, size_t len)
+{
+ struct spi_transfer t = {
+ .tx_buf = (const void *)buf,
+ .rx_buf = buf,
+ .len = len,
+ .cs_change = 0,
+ .delay_usecs = 0,
+ };
+ struct spi_message m;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ if (spi_sync(spi, &m) != 0 || m.status != 0)
+ return PMIC_ERROR;
+ return (len - m.actual_length);
+}
+
+/*!
+ * This function returns the PMIC version in system.
+ *
+ * @param ver pointer to the pmic_version_t structure
+ *
+ * @return This function returns PMIC version.
+ */
+void pmic_get_revision(pmic_version_t * ver);
+
+/*!
+ * This function initializes the SPI device parameters for this PMIC.
+ *
+ * @param spi the SPI slave device(PMIC)
+ *
+ * @return None
+ */
+int pmic_spi_setup(struct spi_device *spi);
+
+/*!
+ * This function initializes the PMIC registers.
+ *
+ * @return None
+ */
+int pmic_init_registers(void);
+
+/*!
+ * This function reads the interrupt status registers of PMIC
+ * and determine the current active events.
+ *
+ * @param active_events array pointer to be used to return active
+ * event numbers.
+ *
+ * @return This function returns PMIC version.
+ */
+unsigned int pmic_get_active_events(unsigned int *active_events);
+
+/*!
+ * This function sets a bit in mask register of pmic to disable an event IT.
+ *
+ * @param event the event to be masked
+ *
+ * @return This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+int pmic_event_mask(type_event event);
+
+/*!
+ * This function unsets a bit in mask register of pmic to unmask an event IT.
+ *
+ * @param event the event to be unmasked
+ *
+ * @return This function returns PMIC_SUCCESS on SUCCESS, error on FAILURE.
+ */
+int pmic_event_unmask(type_event event);
+
+#ifdef CONFIG_MXC_PMIC_FIXARB
+extern PMIC_STATUS pmic_fix_arbitration(struct spi_device *spi);
+#else
+static inline PMIC_STATUS pmic_fix_arbitration(struct spi_device *spi)
+{
+ return PMIC_SUCCESS;
+}
+#endif
+
+void *pmic_alloc_data(struct device *dev);
+
+#endif /* __PMIC_H__ */
diff --git a/drivers/mxc/pmic/core/pmic_common.c b/drivers/mxc/pmic/core/pmic_common.c
new file mode 100644
index 000000000000..55f34b29cd93
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic_common.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file pmic_common.c
+ * @brief This is the common file for the PMIC Core/Protocol driver.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+
+#include <asm/uaccess.h>
+
+#include "pmic.h"
+
+/*
+ * Global variables
+ */
+pmic_version_t mxc_pmic_version;
+unsigned int active_events[MAX_ACTIVE_EVENTS];
+struct workqueue_struct *pmic_event_wq;
+
+void pmic_bh_handler(struct work_struct *work);
+/*!
+ * Bottom half handler of PMIC event handling.
+ */
+DECLARE_WORK(pmic_ws, pmic_bh_handler);
+
+/*!
+ * This function is the bottom half handler of the PMIC interrupt.
+ * It checks for active events and launches callback for the
+ * active events.
+ */
+void pmic_bh_handler(struct work_struct *work)
+{
+ unsigned int loop;
+ unsigned int count = 0;
+
+ count = pmic_get_active_events(active_events);
+ pr_debug("active events number %d\n", count);
+
+ for (loop = 0; loop < count; loop++)
+ pmic_event_callback(active_events[loop]);
+
+ return;
+}
+
+/*!
+ * This function is called when pmic interrupt occurs on the processor.
+ * It is the interrupt handler for the pmic module.
+ *
+ * @param irq the irq number
+ * @param dev_id the pointer on the device
+ *
+ * @return The function returns IRQ_HANDLED when handled.
+ */
+irqreturn_t pmic_irq_handler(int irq, void *dev_id)
+{
+ /* prepare a task */
+ queue_work(pmic_event_wq, &pmic_ws);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function is used to determine the PMIC type and its revision.
+ *
+ * @return Returns the PMIC type and its revision.
+ */
+
+pmic_version_t pmic_get_version(void)
+{
+ return mxc_pmic_version;
+}
+EXPORT_SYMBOL(pmic_get_version);
diff --git a/drivers/mxc/pmic/core/pmic_core_i2c.c b/drivers/mxc/pmic/core/pmic_core_i2c.c
new file mode 100644
index 000000000000..5f393aa93484
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic_core_i2c.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file pmic_core_i2c.c
+ * @brief This is the main file for the PMIC Core/Protocol driver. i2c
+ * should be providing the interface between the PMIC and the MCU.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/mfd/mc13892/core.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+
+#include <asm/uaccess.h>
+
+#include "pmic.h"
+
+#define MC13892_GENERATION_ID_LSH 6
+#define MC13892_IC_ID_LSH 13
+
+#define MC13892_GENERATION_ID_WID 3
+#define MC13892_IC_ID_WID 6
+
+#define MC13892_GEN_ID_VALUE 0x7
+#define MC13892_IC_ID_VALUE 1
+
+/*
+ * Global variables
+ */
+struct i2c_client *mc13892_client;
+
+extern struct workqueue_struct *pmic_event_wq;
+extern pmic_version_t mxc_pmic_version;
+extern irqreturn_t pmic_irq_handler(int irq, void *dev_id);
+
+/*
+ * Platform device structure for PMIC client drivers
+ */
+static struct platform_device adc_ldm = {
+ .name = "pmic_adc",
+ .id = 1,
+};
+static struct platform_device battery_ldm = {
+ .name = "pmic_battery",
+ .id = 1,
+};
+static struct platform_device power_ldm = {
+ .name = "pmic_power",
+ .id = 1,
+};
+static struct platform_device rtc_ldm = {
+ .name = "pmic_rtc",
+ .id = 1,
+};
+static struct platform_device light_ldm = {
+ .name = "pmic_light",
+ .id = 1,
+};
+static struct platform_device rleds_ldm = {
+ .name = "pmic_leds",
+ .id = 'r',
+};
+static struct platform_device gleds_ldm = {
+ .name = "pmic_leds",
+ .id = 'g',
+};
+static struct platform_device bleds_ldm = {
+ .name = "pmic_leds",
+ .id = 'b',
+};
+
+static void pmic_pdev_register(struct device *dev)
+{
+ platform_device_register(&adc_ldm);
+ platform_device_register(&battery_ldm);
+ platform_device_register(&rtc_ldm);
+ platform_device_register(&power_ldm);
+ platform_device_register(&light_ldm);
+ platform_device_register(&rleds_ldm);
+ platform_device_register(&gleds_ldm);
+ platform_device_register(&bleds_ldm);
+}
+
+/*!
+ * This function unregisters platform device structures for
+ * PMIC client drivers.
+ */
+static void pmic_pdev_unregister(void)
+{
+ platform_device_unregister(&adc_ldm);
+ platform_device_unregister(&battery_ldm);
+ platform_device_unregister(&rtc_ldm);
+ platform_device_unregister(&power_ldm);
+ platform_device_unregister(&light_ldm);
+}
+
+static int __devinit is_chip_onboard(struct i2c_client *client)
+{
+ unsigned int ret = 0;
+
+ /*bind the right device to the driver */
+ if (pmic_i2c_24bit_read(client, REG_IDENTIFICATION, &ret) == -1)
+ return -1;
+
+ if (MC13892_GEN_ID_VALUE != BITFEXT(ret, MC13892_GENERATION_ID)) {
+ /*compare the address value */
+ dev_err(&client->dev,
+ "read generation ID 0x%x is not equal to 0x%x!\n",
+ BITFEXT(ret, MC13892_GENERATION_ID),
+ MC13892_GEN_ID_VALUE);
+ return -1;
+ }
+
+ return 0;
+}
+
+static ssize_t mc13892_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, value;
+ int offset = (REG_TEST4 + 1) / 4;
+
+ for (i = 0; i < offset; i++) {
+ pmic_read(i, &value);
+ pr_info("reg%02d: %06x\t\t", i, value);
+ pmic_read(i + offset, &value);
+ pr_info("reg%02d: %06x\t\t", i + offset, value);
+ pmic_read(i + offset * 2, &value);
+ pr_info("reg%02d: %06x\t\t", i + offset * 2, value);
+ pmic_read(i + offset * 3, &value);
+ pr_info("reg%02d: %06x\n", i + offset * 3, value);
+ }
+
+ return 0;
+}
+
+static ssize_t mc13892_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int reg, value, ret;
+ char *p;
+
+ reg = simple_strtoul(buf, NULL, 10);
+
+ p = NULL;
+ p = memchr(buf, ' ', count);
+
+ if (p == NULL) {
+ pmic_read(reg, &value);
+ pr_debug("reg%02d: %06x\n", reg, value);
+ return count;
+ }
+
+ p += 1;
+
+ value = simple_strtoul(p, NULL, 16);
+
+ ret = pmic_write(reg, value);
+ if (ret == 0)
+ pr_debug("write reg%02d: %06x\n", reg, value);
+ else
+ pr_debug("register update failed\n");
+
+ return count;
+}
+
+static struct device_attribute mc13892_dev_attr = {
+ .attr = {
+ .name = "mc13892_ctl",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .show = mc13892_show,
+ .store = mc13892_store,
+};
+
+static int __devinit pmic_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ int pmic_irq;
+ struct mc13892 *mc13892;
+ struct mc13892_platform_data *plat_data = client->dev.platform_data;
+
+ ret = is_chip_onboard(client);
+ if (ret == -1)
+ return -ENODEV;
+
+ mc13892 = kzalloc(sizeof(struct mc13892), GFP_KERNEL);
+ if (mc13892 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, mc13892);
+ mc13892->dev = &client->dev;
+ mc13892->i2c_client = client;
+
+ /* so far, we got matched chip on board */
+
+ mc13892_client = client;
+
+ /* Initialize the PMIC event handling */
+ pmic_event_list_init();
+
+ /* Initialize GPIO for PMIC Interrupt */
+ gpio_pmic_active();
+
+ /* Get the PMIC Version */
+ pmic_get_revision(&mxc_pmic_version);
+ if (mxc_pmic_version.revision < 0) {
+ dev_err((struct device *)client,
+ "PMIC not detected!!! Access Failed\n");
+ return -ENODEV;
+ } else {
+ dev_dbg((struct device *)client,
+ "Detected pmic core IC version number is %d\n",
+ mxc_pmic_version.revision);
+ }
+
+ /* Initialize the PMIC parameters */
+ ret = pmic_init_registers();
+ if (ret != PMIC_SUCCESS)
+ return PMIC_ERROR;
+
+ pmic_event_wq = create_workqueue("mc13892");
+ if (!pmic_event_wq) {
+ pr_err("mc13892 pmic driver init: fail to create work queue");
+ return -EFAULT;
+ }
+
+ /* Set and install PMIC IRQ handler */
+ pmic_irq = (int)(client->irq);
+ if (pmic_irq == 0)
+ return PMIC_ERROR;
+
+ set_irq_type(IOMUX_TO_IRQ(pmic_irq), IRQF_TRIGGER_RISING);
+ ret =
+ request_irq(IOMUX_TO_IRQ(pmic_irq), pmic_irq_handler, 0, "PMIC_IRQ",
+ 0);
+
+ if (ret) {
+ dev_err(&client->dev, "request irq %d error!\n", pmic_irq);
+ return ret;
+ }
+ enable_irq_wake(IOMUX_TO_IRQ(pmic_irq));
+
+ if (plat_data && plat_data->init) {
+ ret = plat_data->init(mc13892);
+ if (ret != 0)
+ return PMIC_ERROR;
+ }
+
+ ret = device_create_file(&client->dev, &mc13892_dev_attr);
+ if (ret)
+ dev_err(&client->dev, "create device file failed!\n");
+
+ pmic_pdev_register(&client->dev);
+
+ dev_info(&client->dev, "Loaded\n");
+
+ return PMIC_SUCCESS;
+}
+
+static int pmic_remove(struct i2c_client *client)
+{
+ int pmic_irq = (int)(client->irq);
+
+ if (pmic_event_wq)
+ destroy_workqueue(pmic_event_wq);
+
+ free_irq(pmic_irq, 0);
+ pmic_pdev_unregister();
+ return 0;
+}
+
+static int pmic_suspend(struct i2c_client *client, pm_message_t state)
+{
+ return 0;
+}
+
+static int pmic_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id mc13892_id[] = {
+ {"mc13892", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mc13892_id);
+
+static struct i2c_driver pmic_driver = {
+ .driver = {
+ .name = "mc13892",
+ .bus = NULL,
+ },
+ .probe = pmic_probe,
+ .remove = pmic_remove,
+ .suspend = pmic_suspend,
+ .resume = pmic_resume,
+ .id_table = mc13892_id,
+};
+
+static int __init pmic_init(void)
+{
+ return i2c_add_driver(&pmic_driver);
+}
+
+static void __exit pmic_exit(void)
+{
+ i2c_del_driver(&pmic_driver);
+}
+
+/*
+ * Module entry points
+ */
+subsys_initcall_sync(pmic_init);
+module_exit(pmic_exit);
+
+MODULE_DESCRIPTION("Core/Protocol driver for PMIC");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/core/pmic_core_spi.c b/drivers/mxc/pmic/core/pmic_core_spi.c
new file mode 100644
index 000000000000..bada77ff34d6
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic_core_spi.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file pmic_core_spi.c
+ * @brief This is the main file for the PMIC Core/Protocol driver. SPI
+ * should be providing the interface between the PMIC and the MCU.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+
+#include <asm/uaccess.h>
+
+#include "pmic.h"
+
+/*
+ * Static functions
+ */
+static void pmic_pdev_register(void);
+static void pmic_pdev_unregister(void);
+
+/*
+ * Platform device structure for PMIC client drivers
+ */
+static struct platform_device adc_ldm = {
+ .name = "pmic_adc",
+ .id = 1,
+};
+static struct platform_device battery_ldm = {
+ .name = "pmic_battery",
+ .id = 1,
+};
+static struct platform_device power_ldm = {
+ .name = "pmic_power",
+ .id = 1,
+};
+static struct platform_device rtc_ldm = {
+ .name = "pmic_rtc",
+ .id = 1,
+};
+static struct platform_device light_ldm = {
+ .name = "pmic_light",
+ .id = 1,
+};
+static struct platform_device rleds_ldm = {
+ .name = "pmic_leds",
+ .id = 'r',
+};
+static struct platform_device gleds_ldm = {
+ .name = "pmic_leds",
+ .id = 'g',
+};
+static struct platform_device bleds_ldm = {
+ .name = "pmic_leds",
+ .id = 'b',
+};
+
+/*
+ * External functions
+ */
+extern void pmic_event_list_init(void);
+extern void pmic_event_callback(type_event event);
+extern void gpio_pmic_active(void);
+extern irqreturn_t pmic_irq_handler(int irq, void *dev_id);
+extern pmic_version_t mxc_pmic_version;
+extern struct workqueue_struct *pmic_event_wq;
+
+/*!
+ * This function registers platform device structures for
+ * PMIC client drivers.
+ */
+static void pmic_pdev_register(void)
+{
+ platform_device_register(&adc_ldm);
+ platform_device_register(&battery_ldm);
+ platform_device_register(&rtc_ldm);
+ platform_device_register(&power_ldm);
+ platform_device_register(&light_ldm);
+ platform_device_register(&rleds_ldm);
+ platform_device_register(&gleds_ldm);
+ platform_device_register(&bleds_ldm);
+}
+
+/*!
+ * This function unregisters platform device structures for
+ * PMIC client drivers.
+ */
+static void pmic_pdev_unregister(void)
+{
+ platform_device_unregister(&adc_ldm);
+ platform_device_unregister(&battery_ldm);
+ platform_device_unregister(&rtc_ldm);
+ platform_device_unregister(&power_ldm);
+ platform_device_unregister(&light_ldm);
+}
+
+/*!
+ * This function puts the SPI slave device in low-power mode/state.
+ *
+ * @param spi the SPI slave device
+ * @param message the power state to enter
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int pmic_suspend(struct spi_device *spi, pm_message_t message)
+{
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function brings the SPI slave device back from low-power mode/state.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int pmic_resume(struct spi_device *spi)
+{
+ return PMIC_SUCCESS;
+}
+
+static struct spi_driver pmic_driver;
+
+/*!
+ * This function is called whenever the SPI slave device is detected.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devinit pmic_probe(struct spi_device *spi)
+{
+ int ret = 0;
+ struct pmic_platform_data *plat_data = spi->dev.platform_data;
+
+ if (!strcmp(spi->dev.bus_id, PMIC_ARBITRATION)) {
+ if (PMIC_SUCCESS != pmic_fix_arbitration(spi)) {
+ dev_err((struct device *)spi,
+ "Unable to fix arbitration!! Access Failed\n");
+ return -EACCES;
+ }
+ return PMIC_SUCCESS;
+ }
+
+ /* Initialize the PMIC parameters */
+ ret = pmic_spi_setup(spi);
+ if (ret != PMIC_SUCCESS) {
+ return PMIC_ERROR;
+ }
+
+ /* Initialize the PMIC event handling */
+ pmic_event_list_init();
+
+ /* Initialize GPIO for PMIC Interrupt */
+ gpio_pmic_active();
+
+ /* Get the PMIC Version */
+ pmic_get_revision(&mxc_pmic_version);
+ if (mxc_pmic_version.revision < 0) {
+ dev_err((struct device *)spi,
+ "PMIC not detected!!! Access Failed\n");
+ return -ENODEV;
+ } else {
+ dev_dbg((struct device *)spi,
+ "Detected pmic core IC version number is %d\n",
+ mxc_pmic_version.revision);
+ }
+
+ spi_set_drvdata(spi, pmic_alloc_data(&(spi->dev)));
+
+ /* Initialize the PMIC parameters */
+ ret = pmic_init_registers();
+ if (ret != PMIC_SUCCESS) {
+ kfree(spi_get_drvdata(spi));
+ spi_set_drvdata(spi, NULL);
+ return PMIC_ERROR;
+ }
+
+ pmic_event_wq = create_workqueue("pmic_spi");
+ if (!pmic_event_wq) {
+ pr_err("pmic driver init: fail to create work queue");
+ kfree(spi_get_drvdata(spi));
+ spi_set_drvdata(spi, NULL);
+ return -EFAULT;
+ }
+
+ /* Set and install PMIC IRQ handler */
+ set_irq_type(spi->irq, IRQF_TRIGGER_RISING);
+ ret = request_irq(spi->irq, pmic_irq_handler, 0, "PMIC_IRQ", 0);
+ if (ret) {
+ kfree(spi_get_drvdata(spi));
+ spi_set_drvdata(spi, NULL);
+ dev_err((struct device *)spi, "gpio1: irq%d error.", spi->irq);
+ return ret;
+ }
+
+ enable_irq_wake(spi->irq);
+
+ if (plat_data && plat_data->init) {
+ ret = plat_data->init(spi_get_drvdata(spi));
+ if (ret != 0) {
+ kfree(spi_get_drvdata(spi));
+ spi_set_drvdata(spi, NULL);
+ return PMIC_ERROR;
+ }
+ }
+
+ power_ldm.dev.platform_data = spi->dev.platform_data;
+
+ pmic_pdev_register();
+
+ printk(KERN_INFO "Device %s probed\n", spi->dev.bus_id);
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is called whenever the SPI slave device is removed.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devexit pmic_remove(struct spi_device *spi)
+{
+ if (pmic_event_wq)
+ destroy_workqueue(pmic_event_wq);
+
+ free_irq(spi->irq, 0);
+
+ pmic_pdev_unregister();
+
+ printk(KERN_INFO "Device %s removed\n", spi->dev.bus_id);
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct spi_driver pmic_driver = {
+ .driver = {
+ .name = "pmic_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = pmic_probe,
+ .remove = __devexit_p(pmic_remove),
+ .suspend = pmic_suspend,
+ .resume = pmic_resume,
+};
+
+/*
+ * Initialization and Exit
+ */
+
+/*!
+ * This function implements the init function of the PMIC device.
+ * This function is called when the module is loaded. It registers
+ * the PMIC Protocol driver.
+ *
+ * @return This function returns 0.
+ */
+static int __init pmic_init(void)
+{
+ return spi_register_driver(&pmic_driver);
+}
+
+/*!
+ * This function implements the exit function of the PMIC device.
+ * This function is called when the module is unloaded. It unregisters
+ * the PMIC Protocol driver.
+ *
+ */
+static void __exit pmic_exit(void)
+{
+ pr_debug("Unregistering the PMIC Protocol Driver\n");
+ spi_unregister_driver(&pmic_driver);
+}
+
+/*
+ * Module entry points
+ */
+subsys_initcall_sync(pmic_init);
+module_exit(pmic_exit);
+
+MODULE_DESCRIPTION("Core/Protocol driver for PMIC");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/core/pmic_event.c b/drivers/mxc/pmic/core/pmic_event.c
new file mode 100644
index 000000000000..2d1bebfef448
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic_event.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file pmic_event.c
+ * @brief This file manage all event of PMIC component.
+ *
+ * It contains event subscription, unsubscription and callback
+ * launch methods implemeted.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+#include "pmic.h"
+
+/*!
+ * This structure is used to keep a list of subscribed
+ * callbacks for an event.
+ */
+typedef struct {
+ /*!
+ * Keeps a list of subscribed clients to an event.
+ */
+ struct list_head list;
+
+ /*!
+ * Callback function with parameter, called when event occurs
+ */
+ pmic_event_callback_t callback;
+} pmic_event_callback_list_t;
+
+/* Create a mutex to be used to prevent concurrent access to the event list */
+static DECLARE_MUTEX(event_mutex);
+
+/* This is a pointer to the event handler array. It defines the currently
+ * active set of events and user-defined callback functions.
+ */
+static struct list_head pmic_events[PMIC_MAX_EVENTS];
+
+/*!
+ * This function initializes event list for PMIC event handling.
+ *
+ */
+void pmic_event_list_init(void)
+{
+ int i;
+
+ for (i = 0; i < PMIC_MAX_EVENTS; i++) {
+ INIT_LIST_HEAD(&pmic_events[i]);
+ }
+
+ sema_init(&event_mutex, 1);
+ return;
+}
+
+/*!
+ * This function is used to subscribe on an event.
+ *
+ * @param event the event number to be subscribed
+ * @param callback the callback funtion to be subscribed
+ *
+ * @return This function returns 0 on SUCCESS, error on FAILURE.
+ */
+PMIC_STATUS pmic_event_subscribe(type_event event,
+ pmic_event_callback_t callback)
+{
+ pmic_event_callback_list_t *new = NULL;
+
+ pr_debug("Event:%d Subscribe\n", event);
+
+ /* Check whether the event & callback are valid? */
+ if (event >= PMIC_MAX_EVENTS) {
+ pr_debug("Invalid Event:%d\n", event);
+ return -EINVAL;
+ }
+ if (NULL == callback.func) {
+ pr_debug("Null or Invalid Callback\n");
+ return -EINVAL;
+ }
+
+ /* Create a new linked list entry */
+ new = kmalloc(sizeof(pmic_event_callback_list_t), GFP_KERNEL);
+ if (NULL == new) {
+ return -ENOMEM;
+ }
+ /* Initialize the list node fields */
+ new->callback.func = callback.func;
+ new->callback.param = callback.param;
+ INIT_LIST_HEAD(&new->list);
+
+ /* Obtain the lock to access the list */
+ if (down_interruptible(&event_mutex)) {
+ kfree(new);
+ return PMIC_SYSTEM_ERROR_EINTR;
+ }
+
+ /* Unmask the requested event */
+ if (list_empty(&pmic_events[event])) {
+ if (pmic_event_unmask(event) != PMIC_SUCCESS) {
+ kfree(new);
+ up(&event_mutex);
+ return PMIC_ERROR;
+ }
+ }
+
+ /* Add this entry to the event list */
+ list_add_tail(&new->list, &pmic_events[event]);
+
+ /* Release the lock */
+ up(&event_mutex);
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to unsubscribe on an event.
+ *
+ * @param event the event number to be unsubscribed
+ * @param callback the callback funtion to be unsubscribed
+ *
+ * @return This function returns 0 on SUCCESS, error on FAILURE.
+ */
+PMIC_STATUS pmic_event_unsubscribe(type_event event,
+ pmic_event_callback_t callback)
+{
+ struct list_head *p;
+ struct list_head *n;
+ pmic_event_callback_list_t *temp = NULL;
+ int ret = PMIC_EVENT_NOT_SUBSCRIBED;
+
+ pr_debug("Event:%d Unsubscribe\n", event);
+
+ /* Check whether the event & callback are valid? */
+ if (event >= PMIC_MAX_EVENTS) {
+ pr_debug("Invalid Event:%d\n", event);
+ return -EINVAL;
+ }
+
+ if (NULL == callback.func) {
+ pr_debug("Null or Invalid Callback\n");
+ return -EINVAL;
+ }
+
+ /* Obtain the lock to access the list */
+ if (down_interruptible(&event_mutex)) {
+ return PMIC_SYSTEM_ERROR_EINTR;
+ }
+
+ /* Find the entry in the list */
+ list_for_each_safe(p, n, &pmic_events[event]) {
+ temp = list_entry(p, pmic_event_callback_list_t, list);
+ if (temp->callback.func == callback.func
+ && temp->callback.param == callback.param) {
+ /* Remove the entry from the list */
+ list_del(p);
+ kfree(temp);
+ ret = PMIC_SUCCESS;
+ break;
+ }
+ }
+
+ /* Unmask the requested event */
+ if (list_empty(&pmic_events[event])) {
+ if (pmic_event_mask(event) != PMIC_SUCCESS) {
+ ret = PMIC_UNSUBSCRIBE_ERROR;
+ }
+ }
+
+ /* Release the lock */
+ up(&event_mutex);
+
+ return ret;
+}
+
+/*!
+ * This function calls all callback of a specific event.
+ *
+ * @param event the active event number
+ *
+ * @return None
+ */
+void pmic_event_callback(type_event event)
+{
+ struct list_head *p;
+ pmic_event_callback_list_t *temp = NULL;
+
+ /* Obtain the lock to access the list */
+ if (down_interruptible(&event_mutex)) {
+ return;
+ }
+
+ if (list_empty(&pmic_events[event])) {
+ pr_debug("PMIC Event:%d detected. No callback subscribed\n",
+ event);
+ up(&event_mutex);
+ return;
+ }
+
+ list_for_each(p, &pmic_events[event]) {
+ temp = list_entry(p, pmic_event_callback_list_t, list);
+ temp->callback.func(temp->callback.param);
+ }
+
+ /* Release the lock */
+ up(&event_mutex);
+
+ return;
+
+}
+
+EXPORT_SYMBOL(pmic_event_subscribe);
+EXPORT_SYMBOL(pmic_event_unsubscribe);
diff --git a/drivers/mxc/pmic/core/pmic_external.c b/drivers/mxc/pmic/core/pmic_external.c
new file mode 100644
index 000000000000..bea23af3207d
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic_external.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file pmic_external.c
+ * @brief This file contains all external functions of PMIC drivers.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+
+#include <linux/pmic_external.h>
+#include <linux/pmic_status.h>
+
+/*
+ * External Functions
+ */
+extern int pmic_read(int reg_num, unsigned int *reg_val);
+extern int pmic_write(int reg_num, const unsigned int reg_val);
+
+/*!
+ * This function is called by PMIC clients to read a register on PMIC.
+ *
+ * @param reg number of register
+ * @param reg_value return value of register
+ * @param reg_mask Bitmap mask indicating which bits to modify
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_read_reg(int reg, unsigned int *reg_value,
+ unsigned int reg_mask)
+{
+ int ret = 0;
+ unsigned int temp = 0;
+
+ ret = pmic_read(reg, &temp);
+ if (ret != PMIC_SUCCESS) {
+ return PMIC_ERROR;
+ }
+ *reg_value = (temp & reg_mask);
+
+ pr_debug("Read REG[ %d ] = 0x%x\n", reg, *reg_value);
+
+ return ret;
+}
+
+/*!
+ * This function is called by PMIC clients to write a register on PMIC.
+ *
+ * @param reg number of register
+ * @param reg_value New value of register
+ * @param reg_mask Bitmap mask indicating which bits to modify
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_write_reg(int reg, unsigned int reg_value,
+ unsigned int reg_mask)
+{
+ int ret = 0;
+ unsigned int temp = 0;
+
+ ret = pmic_read(reg, &temp);
+ if (ret != PMIC_SUCCESS) {
+ return PMIC_ERROR;
+ }
+ temp = (temp & (~reg_mask)) | reg_value;
+#ifdef CONFIG_MXC_PMIC_MC13783
+ if (reg == REG_POWER_MISCELLANEOUS)
+ temp &= 0xFFFE7FFF;
+#endif
+ ret = pmic_write(reg, temp);
+ if (ret != PMIC_SUCCESS) {
+ return PMIC_ERROR;
+ }
+
+ pr_debug("Write REG[ %d ] = 0x%x\n", reg, reg_value);
+
+ return ret;
+}
+
+EXPORT_SYMBOL(pmic_read_reg);
+EXPORT_SYMBOL(pmic_write_reg);
diff --git a/drivers/mxc/pmic/mc13783/Kconfig b/drivers/mxc/pmic/mc13783/Kconfig
new file mode 100644
index 000000000000..02496c624e2e
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/Kconfig
@@ -0,0 +1,55 @@
+#
+# PMIC Modules configuration
+#
+
+config MXC_MC13783_ADC
+ tristate "MC13783 ADC support"
+ depends on MXC_PMIC_MC13783
+ ---help---
+ This is the MC13783 ADC module driver. This module provides kernel API
+ for the ADC system of MC13783.
+ It controls also the touch screen interface.
+ If you want MC13783 ADC support, you should say Y here
+
+config MXC_MC13783_AUDIO
+ tristate "MC13783 Audio support"
+ depends on MXC_PMIC_MC13783
+ ---help---
+ This is the MC13783 audio module driver. This module provides kernel API
+ for audio part of MC13783.
+ If you want MC13783 audio support, you should say Y here
+config MXC_MC13783_RTC
+ tristate "MC13783 Real Time Clock (RTC) support"
+ depends on MXC_PMIC_MC13783
+ ---help---
+ This is the MC13783 RTC module driver. This module provides kernel API
+ for RTC part of MC13783.
+ If you want MC13783 RTC support, you should say Y here
+config MXC_MC13783_LIGHT
+ tristate "MC13783 Light and Backlight support"
+ depends on MXC_PMIC_MC13783
+ ---help---
+ This is the MC13783 Light module driver. This module provides kernel API
+ for led and backlight control part of MC13783.
+ If you want MC13783 Light support, you should say Y here
+config MXC_MC13783_BATTERY
+ tristate "MC13783 Battery API support"
+ depends on MXC_PMIC_MC13783
+ ---help---
+ This is the MC13783 battery module driver. This module provides kernel API
+ for battery control part of MC13783.
+ If you want MC13783 battery support, you should say Y here
+config MXC_MC13783_CONNECTIVITY
+ tristate "MC13783 Connectivity API support"
+ depends on MXC_PMIC_MC13783
+ ---help---
+ This is the MC13783 connectivity module driver. This module provides kernel API
+ for USB/RS232 connectivity control part of MC13783.
+ If you want MC13783 connectivity support, you should say Y here
+config MXC_MC13783_POWER
+ tristate "MC13783 Power API support"
+ depends on MXC_PMIC_MC13783
+ ---help---
+ This is the MC13783 power and supplies module driver. This module provides kernel API
+ for power and regulator control part of MC13783.
+ If you want MC13783 power support, you should say Y here
diff --git a/drivers/mxc/pmic/mc13783/Makefile b/drivers/mxc/pmic/mc13783/Makefile
new file mode 100644
index 000000000000..7bbba23f5ab9
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the mc13783 pmic drivers.
+#
+
+obj-$(CONFIG_MXC_MC13783_ADC) += pmic_adc-mod.o
+obj-$(CONFIG_MXC_MC13783_AUDIO) += pmic_audio-mod.o
+obj-$(CONFIG_MXC_MC13783_RTC) += pmic_rtc-mod.o
+obj-$(CONFIG_MXC_MC13783_LIGHT) += pmic_light-mod.o
+obj-$(CONFIG_MXC_MC13783_BATTERY) += pmic_battery-mod.o
+obj-$(CONFIG_MXC_MC13783_CONNECTIVITY) += pmic_convity-mod.o
+obj-$(CONFIG_MXC_MC13783_POWER) += pmic_power-mod.o
+pmic_adc-mod-objs := pmic_adc.o
+pmic_audio-mod-objs := pmic_audio.o
+pmic_rtc-mod-objs := pmic_rtc.o
+pmic_light-mod-objs := pmic_light.o
+pmic_battery-mod-objs := pmic_battery.o
+pmic_convity-mod-objs := pmic_convity.o
+pmic_power-mod-objs := pmic_power.o
diff --git a/drivers/mxc/pmic/mc13783/pmic_adc.c b/drivers/mxc/pmic/mc13783/pmic_adc.c
new file mode 100644
index 000000000000..1554faffd288
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_adc.c
@@ -0,0 +1,1542 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_adc.c
+ * @brief This is the main file of PMIC(mc13783) ADC driver.
+ *
+ * @ingroup PMIC_ADC
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/pmic_adc.h>
+#include <linux/pmic_status.h>
+
+#include "../core/pmic.h"
+#include "pmic_adc_defs.h"
+
+#define NB_ADC_REG 5
+
+static int pmic_adc_major;
+
+/* internal function */
+static void callback_tsi(void *);
+static void callback_adcdone(void *);
+static void callback_adcbisdone(void *);
+static void callback_adc_comp_high(void *);
+
+/*!
+ * Number of users waiting in suspendq
+ */
+static int swait = 0;
+
+/*!
+ * To indicate whether any of the adc devices are suspending
+ */
+static int suspend_flag = 0;
+
+/*!
+ * The suspendq is used by blocking application calls
+ */
+static wait_queue_head_t suspendq;
+
+static struct class *pmic_adc_class;
+
+/*
+ * ADC mc13783 API
+ */
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_adc_init);
+EXPORT_SYMBOL(pmic_adc_deinit);
+EXPORT_SYMBOL(pmic_adc_convert);
+EXPORT_SYMBOL(pmic_adc_convert_8x);
+EXPORT_SYMBOL(pmic_adc_convert_multichnnel);
+EXPORT_SYMBOL(pmic_adc_set_touch_mode);
+EXPORT_SYMBOL(pmic_adc_get_touch_mode);
+EXPORT_SYMBOL(pmic_adc_get_touch_sample);
+EXPORT_SYMBOL(pmic_adc_get_battery_current);
+EXPORT_SYMBOL(pmic_adc_active_comparator);
+EXPORT_SYMBOL(pmic_adc_deactive_comparator);
+
+static DECLARE_COMPLETION(adcdone_it);
+static DECLARE_COMPLETION(adcbisdone_it);
+static DECLARE_COMPLETION(adc_tsi);
+static pmic_event_callback_t tsi_event;
+static pmic_event_callback_t event_adc;
+static pmic_event_callback_t event_adc_bis;
+static pmic_event_callback_t adc_comp_h;
+static bool data_ready_adc_1;
+static bool data_ready_adc_2;
+static bool adc_ts;
+static bool wait_ts;
+static bool monitor_en;
+static bool monitor_adc;
+static t_check_mode wcomp_mode;
+static DECLARE_MUTEX(convert_mutex);
+
+void (*monitoring_cb) (void); /*call back to be called when event is detected. */
+
+static DECLARE_WAIT_QUEUE_HEAD(queue_adc_busy);
+static t_adc_state adc_dev[2];
+
+static unsigned channel_num[] = {
+ 0,
+ 1,
+ 3,
+ 4,
+ 2,
+ 12,
+ 13,
+ 14,
+ 15,
+ -1,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 7,
+ 6,
+ -1,
+ -1,
+ -1,
+ -1,
+ 5,
+ 7
+};
+
+static bool pmic_adc_ready;
+
+int is_pmic_adc_ready()
+{
+ return pmic_adc_ready;
+}
+EXPORT_SYMBOL(is_pmic_adc_ready);
+
+
+/*!
+ * This is the suspend of power management for the mc13783 ADC API.
+ * It supports SAVE and POWER_DOWN state.
+ *
+ * @param pdev the device
+ * @param state the state
+ *
+ * @return This function returns 0 if successful.
+ */
+static int pmic_adc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ unsigned int reg_value = 0;
+ suspend_flag = 1;
+ CHECK_ERROR(pmic_write_reg(REG_ADC_0, DEF_ADC_0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_1, reg_value, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_2, reg_value, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_3, DEF_ADC_3, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_4, reg_value, PMIC_ALL_BITS));
+
+ return 0;
+};
+
+/*!
+ * This is the resume of power management for the mc13783 adc API.
+ * It supports RESTORE state.
+ *
+ * @param pdev the device
+ *
+ * @return This function returns 0 if successful.
+ */
+static int pmic_adc_resume(struct platform_device *pdev)
+{
+ /* nothing for mc13783 adc */
+ unsigned int adc_0_reg, adc_1_reg;
+ suspend_flag = 0;
+
+ /* let interrupt of TSI again */
+ adc_0_reg = ADC_WAIT_TSI_0;
+ CHECK_ERROR(pmic_write_reg(REG_ADC_0, adc_0_reg, PMIC_ALL_BITS));
+ adc_1_reg = ADC_WAIT_TSI_1 | (ADC_BIS * adc_ts);
+ CHECK_ERROR(pmic_write_reg(REG_ADC_1, adc_1_reg, PMIC_ALL_BITS));
+
+ while (swait > 0) {
+ swait--;
+ wake_up_interruptible(&suspendq);
+ }
+
+ return 0;
+};
+
+/*
+ * Call back functions
+ */
+
+/*!
+ * This is the callback function called on TSI mc13783 event, used in synchronous call.
+ */
+static void callback_tsi(void *unused)
+{
+ pr_debug("*** TSI IT mc13783 PMIC_ADC_GET_TOUCH_SAMPLE ***\n");
+ if (wait_ts) {
+ complete(&adc_tsi);
+ pmic_event_mask(EVENT_TSI);
+ }
+}
+
+/*!
+ * This is the callback function called on ADCDone mc13783 event.
+ */
+static void callback_adcdone(void *unused)
+{
+ if (data_ready_adc_1) {
+ complete(&adcdone_it);
+ }
+}
+
+/*!
+ * This is the callback function called on ADCDone mc13783 event.
+ */
+static void callback_adcbisdone(void *unused)
+{
+ pr_debug("* adcdone bis it callback *\n");
+ if (data_ready_adc_2) {
+ complete(&adcbisdone_it);
+ }
+}
+
+/*!
+ * This is the callback function called on mc13783 event.
+ */
+static void callback_adc_comp_high(void *unused)
+{
+ pr_debug("* adc comp it high *\n");
+ if (wcomp_mode == CHECK_HIGH || wcomp_mode == CHECK_LOW_OR_HIGH) {
+ /* launch callback */
+ if (monitoring_cb != NULL) {
+ monitoring_cb();
+ }
+ }
+}
+
+/*!
+ * This function performs filtering and rejection of excessive noise prone
+ * samples.
+ *
+ * @param ts_curr Touch screen value
+ *
+ * @return This function returns 0 on success, -1 otherwise.
+ */
+static int pmic_adc_filter(t_touch_screen * ts_curr)
+{
+ unsigned int ydiff1, ydiff2, ydiff3, xdiff1, xdiff2, xdiff3;
+ unsigned int sample_sumx, sample_sumy;
+ static unsigned int prev_x[FILTLEN], prev_y[FILTLEN];
+ int index = 0;
+ unsigned int y_curr, x_curr;
+ static int filt_count = 0;
+ /* Added a variable filt_type to decide filtering at run-time */
+ unsigned int filt_type = 0;
+
+ if (ts_curr->contact_resistance == 0) {
+ ts_curr->x_position = 0;
+ ts_curr->y_position = 0;
+ filt_count = 0;
+ return 0;
+ }
+
+ ydiff1 = abs(ts_curr->y_position1 - ts_curr->y_position2);
+ ydiff2 = abs(ts_curr->y_position2 - ts_curr->y_position3);
+ ydiff3 = abs(ts_curr->y_position1 - ts_curr->y_position3);
+ if ((ydiff1 > DELTA_Y_MAX) ||
+ (ydiff2 > DELTA_Y_MAX) || (ydiff3 > DELTA_Y_MAX)) {
+ pr_debug("pmic_adc_filter: Ret pos 1\n");
+ return -1;
+ }
+
+ xdiff1 = abs(ts_curr->x_position1 - ts_curr->x_position2);
+ xdiff2 = abs(ts_curr->x_position2 - ts_curr->x_position3);
+ xdiff3 = abs(ts_curr->x_position1 - ts_curr->x_position3);
+
+ if ((xdiff1 > DELTA_X_MAX) ||
+ (xdiff2 > DELTA_X_MAX) || (xdiff3 > DELTA_X_MAX)) {
+ pr_debug("mc13783_adc_filter: Ret pos 2\n");
+ return -1;
+ }
+ /* Compute two closer values among the three available Y readouts */
+
+ if (ydiff1 < ydiff2) {
+ if (ydiff1 < ydiff3) {
+ // Sample 0 & 1 closest together
+ sample_sumy = ts_curr->y_position1 +
+ ts_curr->y_position2;
+ } else {
+ // Sample 0 & 2 closest together
+ sample_sumy = ts_curr->y_position1 +
+ ts_curr->y_position3;
+ }
+ } else {
+ if (ydiff2 < ydiff3) {
+ // Sample 1 & 2 closest together
+ sample_sumy = ts_curr->y_position2 +
+ ts_curr->y_position3;
+ } else {
+ // Sample 0 & 2 closest together
+ sample_sumy = ts_curr->y_position1 +
+ ts_curr->y_position3;
+ }
+ }
+
+ /*
+ * Compute two closer values among the three available X
+ * readouts
+ */
+ if (xdiff1 < xdiff2) {
+ if (xdiff1 < xdiff3) {
+ // Sample 0 & 1 closest together
+ sample_sumx = ts_curr->x_position1 +
+ ts_curr->x_position2;
+ } else {
+ // Sample 0 & 2 closest together
+ sample_sumx = ts_curr->x_position1 +
+ ts_curr->x_position3;
+ }
+ } else {
+ if (xdiff2 < xdiff3) {
+ // Sample 1 & 2 closest together
+ sample_sumx = ts_curr->x_position2 +
+ ts_curr->x_position3;
+ } else {
+ // Sample 0 & 2 closest together
+ sample_sumx = ts_curr->x_position1 +
+ ts_curr->x_position3;
+ }
+ }
+ /*
+ * Wait FILTER_MIN_DELAY number of samples to restart
+ * filtering
+ */
+ if (filt_count < FILTER_MIN_DELAY) {
+ /*
+ * Current output is the average of the two closer
+ * values and no filtering is used
+ */
+ y_curr = (sample_sumy / 2);
+ x_curr = (sample_sumx / 2);
+ ts_curr->y_position = y_curr;
+ ts_curr->x_position = x_curr;
+ filt_count++;
+ } else {
+ if (abs(sample_sumx - (prev_x[0] + prev_x[1])) >
+ (DELTA_X_MAX * 16)) {
+ pr_debug("pmic_adc_filter: : Ret pos 3\n");
+ return -1;
+ }
+ if (abs(sample_sumy - (prev_y[0] + prev_y[1])) >
+ (DELTA_Y_MAX * 16)) {
+ return -1;
+ }
+ sample_sumy /= 2;
+ sample_sumx /= 2;
+ /* Use hard filtering if the sample difference < 10 */
+ if ((abs(sample_sumy - prev_y[0]) > 10) ||
+ (abs(sample_sumx - prev_x[0]) > 10)) {
+ filt_type = 1;
+ }
+
+ /*
+ * Current outputs are the average of three previous
+ * values and the present readout
+ */
+ y_curr = sample_sumy;
+ for (index = 0; index < FILTLEN; index++) {
+ if (filt_type == 0) {
+ y_curr = y_curr + (prev_y[index]);
+ } else {
+ y_curr = y_curr + (prev_y[index] / 3);
+ }
+ }
+ if (filt_type == 0) {
+ y_curr = y_curr >> 2;
+ } else {
+ y_curr = y_curr >> 1;
+ }
+ ts_curr->y_position = y_curr;
+
+ x_curr = sample_sumx;
+ for (index = 0; index < FILTLEN; index++) {
+ if (filt_type == 0) {
+ x_curr = x_curr + (prev_x[index]);
+ } else {
+ x_curr = x_curr + (prev_x[index] / 3);
+ }
+ }
+ if (filt_type == 0) {
+ x_curr = x_curr >> 2;
+ } else {
+ x_curr = x_curr >> 1;
+ }
+ ts_curr->x_position = x_curr;
+
+ }
+
+ /* Update previous X and Y values */
+ for (index = (FILTLEN - 1); index > 0; index--) {
+ prev_x[index] = prev_x[index - 1];
+ prev_y[index] = prev_y[index - 1];
+ }
+
+ /*
+ * Current output will be the most recent past for the
+ * next sample
+ */
+ prev_y[0] = y_curr;
+ prev_x[0] = x_curr;
+
+ return 0;
+}
+
+/*!
+ * This function implements the open method on a MC13783 ADC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_adc_open(struct inode *inode, struct file *file)
+{
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ pr_debug("mc13783_adc : mc13783_adc_open()\n");
+ return 0;
+}
+
+/*!
+ * This function implements the release method on a MC13783 ADC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_adc_free(struct inode *inode, struct file *file)
+{
+ pr_debug("mc13783_adc : mc13783_adc_free()\n");
+ return 0;
+}
+
+/*!
+ * This function initializes all ADC registers with default values. This
+ * function also registers the interrupt events.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+int pmic_adc_init(void)
+{
+ unsigned int reg_value = 0, i = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ for (i = 0; i < ADC_NB_AVAILABLE; i++) {
+ adc_dev[i] = ADC_FREE;
+ }
+ CHECK_ERROR(pmic_write_reg(REG_ADC_0, DEF_ADC_0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_1, reg_value, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_2, reg_value, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_3, DEF_ADC_3, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_4, reg_value, PMIC_ALL_BITS));
+ reg_value = 0x001000;
+ CHECK_ERROR(pmic_write_reg(REG_ARBITRATION_PERIPHERAL_AUDIO, reg_value,
+ 0xFFFFFF));
+
+ data_ready_adc_1 = false;
+ data_ready_adc_2 = false;
+ adc_ts = false;
+ wait_ts = false;
+ monitor_en = false;
+ monitor_adc = false;
+ wcomp_mode = CHECK_LOW;
+ monitoring_cb = NULL;
+ /* sub to ADCDone IT */
+ event_adc.param = NULL;
+ event_adc.func = callback_adcdone;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_ADCDONEI, event_adc));
+
+ /* sub to ADCDoneBis IT */
+ event_adc_bis.param = NULL;
+ event_adc_bis.func = callback_adcbisdone;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_ADCBISDONEI, event_adc_bis));
+
+ /* sub to Touch Screen IT */
+ tsi_event.param = NULL;
+ tsi_event.func = callback_tsi;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_TSI, tsi_event));
+
+ /* ADC reading above high limit */
+ adc_comp_h.param = NULL;
+ adc_comp_h.func = callback_adc_comp_high;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_WHIGHI, adc_comp_h));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables the ADC, de-registers the interrupt events.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_deinit(void)
+{
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_ADCDONEI, event_adc));
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_ADCBISDONEI, event_adc_bis));
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_TSI, tsi_event));
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_WHIGHI, adc_comp_h));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initializes adc_param structure.
+ *
+ * @param adc_param Structure to be initialized.
+ *
+ * @return This function returns 0 if successful.
+ */
+int mc13783_adc_init_param(t_adc_param * adc_param)
+{
+ int i = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ adc_param->delay = 0;
+ adc_param->conv_delay = false;
+ adc_param->single_channel = false;
+ adc_param->group = false;
+ adc_param->channel_0 = BATTERY_VOLTAGE;
+ adc_param->channel_1 = BATTERY_VOLTAGE;
+ adc_param->read_mode = 0;
+ adc_param->wait_tsi = 0;
+ adc_param->chrgraw_devide_5 = true;
+ adc_param->read_ts = false;
+ adc_param->ts_value.x_position = 0;
+ adc_param->ts_value.y_position = 0;
+ adc_param->ts_value.contact_resistance = 0;
+ for (i = 0; i <= MAX_CHANNEL; i++) {
+ adc_param->value[i] = 0;
+ }
+ return 0;
+}
+
+/*!
+ * This function starts the convert.
+ *
+ * @param adc_param contains all adc configuration and return value.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS mc13783_adc_convert(t_adc_param * adc_param)
+{
+ bool use_bis = false;
+ unsigned int adc_0_reg = 0, adc_1_reg = 0, reg_1 = 0, result_reg =
+ 0, i = 0;
+ unsigned int result = 0, temp = 0;
+ pmic_version_t mc13783_ver;
+ pr_debug("mc13783 ADC - mc13783_adc_convert ....\n");
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ if (adc_param->wait_tsi) {
+ /* we need to set ADCEN 1 for TSI interrupt on mc13783 1.x */
+ /* configure adc to wait tsi interrupt */
+ INIT_COMPLETION(adc_tsi);
+ pr_debug("mc13783 ADC - pmic_write_reg ....\n");
+ /*for ts don't use bis */
+ adc_0_reg = 0x001c00 | (ADC_BIS * 0);
+ pmic_event_unmask(EVENT_TSI);
+ CHECK_ERROR(pmic_write_reg
+ (REG_ADC_0, adc_0_reg, PMIC_ALL_BITS));
+ /*for ts don't use bis */
+ adc_1_reg = 0x200001 | (ADC_BIS * 0);
+ CHECK_ERROR(pmic_write_reg
+ (REG_ADC_1, adc_1_reg, PMIC_ALL_BITS));
+ pr_debug("wait tsi ....\n");
+ wait_ts = true;
+ wait_for_completion_interruptible(&adc_tsi);
+ wait_ts = false;
+ }
+ if (adc_param->read_ts == false)
+ down(&convert_mutex);
+ use_bis = mc13783_adc_request(adc_param->read_ts);
+ if (use_bis < 0) {
+ pr_debug("process has received a signal and got interrupted\n");
+ return -EINTR;
+ }
+
+ /* CONFIGURE ADC REG 0 */
+ adc_0_reg = 0;
+ adc_1_reg = 0;
+ if (adc_param->read_ts == false) {
+ adc_0_reg = adc_param->read_mode & 0x00003F;
+ /* add auto inc */
+ adc_0_reg |= ADC_INC;
+ if (use_bis) {
+ /* add adc bis */
+ adc_0_reg |= ADC_BIS;
+ }
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ if (adc_param->chrgraw_devide_5) {
+ adc_0_reg |= ADC_CHRGRAW_D5;
+ }
+ }
+ if (adc_param->single_channel) {
+ adc_1_reg |= ADC_SGL_CH;
+ }
+
+ if (adc_param->conv_delay) {
+ adc_1_reg |= ADC_ATO;
+ }
+
+ if (adc_param->group) {
+ adc_1_reg |= ADC_ADSEL;
+ }
+
+ if (adc_param->single_channel) {
+ adc_1_reg |= ADC_SGL_CH;
+ }
+
+ adc_1_reg |= (adc_param->channel_0 << ADC_CH_0_POS) &
+ ADC_CH_0_MASK;
+ adc_1_reg |= (adc_param->channel_1 << ADC_CH_1_POS) &
+ ADC_CH_1_MASK;
+ } else {
+ adc_0_reg = 0x003c00 | (ADC_BIS * use_bis) | ADC_INC;
+ }
+ pr_debug("Write Reg %i = %x\n", REG_ADC_0, adc_0_reg);
+ /*Change has been made here */
+ CHECK_ERROR(pmic_write_reg(REG_ADC_0, adc_0_reg,
+ ADC_INC | ADC_BIS | ADC_CHRGRAW_D5 |
+ 0xfff00ff));
+ /* CONFIGURE ADC REG 1 */
+ if (adc_param->read_ts == false) {
+ adc_1_reg |= ADC_NO_ADTRIG;
+ adc_1_reg |= ADC_EN;
+ adc_1_reg |= (adc_param->delay << ADC_DELAY_POS) &
+ ADC_DELAY_MASK;
+ if (use_bis) {
+ adc_1_reg |= ADC_BIS;
+ }
+ } else {
+ /* configure and start convert to read x and y position */
+ /* configure to read 2 value in channel selection 1 & 2 */
+ adc_1_reg = 0x100409 | (ADC_BIS * use_bis) | ADC_NO_ADTRIG;
+ }
+ reg_1 = adc_1_reg;
+ if (use_bis == 0) {
+ data_ready_adc_1 = false;
+ adc_1_reg |= ASC_ADC;
+ data_ready_adc_1 = true;
+ pr_debug("Write Reg %i = %x\n", REG_ADC_1, adc_1_reg);
+ INIT_COMPLETION(adcdone_it);
+ CHECK_ERROR(pmic_write_reg(REG_ADC_1, adc_1_reg,
+ ADC_SGL_CH | ADC_ATO | ADC_ADSEL
+ | ADC_CH_0_MASK | ADC_CH_1_MASK |
+ ADC_NO_ADTRIG | ADC_EN |
+ ADC_DELAY_MASK | ASC_ADC | ADC_BIS));
+ pr_debug("wait adc done \n");
+ wait_for_completion_interruptible(&adcdone_it);
+ data_ready_adc_1 = false;
+ } else {
+ data_ready_adc_2 = false;
+ adc_1_reg |= ASC_ADC;
+ data_ready_adc_2 = true;
+ INIT_COMPLETION(adcbisdone_it);
+ CHECK_ERROR(pmic_write_reg(REG_ADC_1, adc_1_reg, 0xFFFFFF));
+ temp = 0x800000;
+ CHECK_ERROR(pmic_write_reg(REG_ADC_3, temp, 0xFFFFFF));
+ temp = 0x001000;
+ pmic_write_reg(REG_ARBITRATION_PERIPHERAL_AUDIO, temp,
+ 0xFFFFFF);
+ pr_debug("wait adc done bis\n");
+ wait_for_completion_interruptible(&adcbisdone_it);
+ data_ready_adc_2 = false;
+ }
+ /* read result and store in adc_param */
+ result = 0;
+ if (use_bis == 0) {
+ result_reg = REG_ADC_2;
+ } else {
+ result_reg = REG_ADC_4;
+ }
+ CHECK_ERROR(pmic_write_reg(REG_ADC_1, 4 << ADC_CH_1_POS,
+ ADC_CH_0_MASK | ADC_CH_1_MASK));
+
+ for (i = 0; i <= 3; i++) {
+ CHECK_ERROR(pmic_read_reg(result_reg, &result, PMIC_ALL_BITS));
+ pr_debug("result %i = %x\n", result_reg, result);
+ adc_param->value[i] = ((result & ADD1_RESULT_MASK) >> 2);
+ adc_param->value[i + 4] = ((result & ADD2_RESULT_MASK) >> 14);
+ }
+ if (adc_param->read_ts) {
+ adc_param->ts_value.x_position = adc_param->value[2];
+ adc_param->ts_value.x_position1 = adc_param->value[0];
+ adc_param->ts_value.x_position2 = adc_param->value[1];
+ adc_param->ts_value.x_position3 = adc_param->value[2];
+ adc_param->ts_value.y_position1 = adc_param->value[3];
+ adc_param->ts_value.y_position2 = adc_param->value[4];
+ adc_param->ts_value.y_position3 = adc_param->value[5];
+ adc_param->ts_value.y_position = adc_param->value[5];
+ adc_param->ts_value.contact_resistance = adc_param->value[6];
+
+ }
+
+ /*if (adc_param->read_ts) {
+ adc_param->ts_value.x_position = adc_param->value[2];
+ adc_param->ts_value.y_position = adc_param->value[5];
+ adc_param->ts_value.contact_resistance = adc_param->value[6];
+ } */
+ mc13783_adc_release(use_bis);
+ if (adc_param->read_ts == false)
+ up(&convert_mutex);
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function select the required read_mode for a specific channel.
+ *
+ * @param channel The channel to be sampled
+ *
+ * @return This function returns the requires read_mode
+ */
+t_reading_mode mc13783_set_read_mode(t_channel channel)
+{
+ t_reading_mode read_mode = 0;
+
+ switch (channel) {
+ case LICELL:
+ read_mode = M_LITHIUM_CELL;
+ break;
+ case CHARGE_CURRENT:
+ read_mode = M_CHARGE_CURRENT;
+ break;
+ case BATTERY_CURRENT:
+ read_mode = M_BATTERY_CURRENT;
+ break;
+ case THERMISTOR:
+ read_mode = M_THERMISTOR;
+ break;
+ case DIE_TEMP:
+ read_mode = M_DIE_TEMPERATURE;
+ break;
+ case USB_ID:
+ read_mode = M_UID;
+ break;
+ default:
+ read_mode = 0;
+ }
+
+ return read_mode;
+}
+
+/*!
+ * This function triggers a conversion and returns one sampling result of one
+ * channel.
+ *
+ * @param channel The channel to be sampled
+ * @param result The pointer to the conversion result. The memory
+ * should be allocated by the caller of this function.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_convert(t_channel channel, unsigned short *result)
+{
+ t_adc_param adc_param;
+ PMIC_STATUS ret;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ channel = channel_num[channel];
+ if (channel == -1) {
+ pr_debug("Wrong channel ID\n");
+ return PMIC_PARAMETER_ERROR;
+ }
+ mc13783_adc_init_param(&adc_param);
+ pr_debug("pmic_adc_convert\n");
+ adc_param.read_ts = false;
+ adc_param.read_mode = mc13783_set_read_mode(channel);
+
+ adc_param.single_channel = true;
+ /* Find the group */
+ if ((channel >= 0) && (channel <= 7)) {
+ adc_param.channel_0 = channel;
+ adc_param.group = false;
+ } else if ((channel >= 8) && (channel <= 15)) {
+ adc_param.channel_0 = channel & 0x07;
+ adc_param.group = true;
+ } else {
+ return PMIC_PARAMETER_ERROR;
+ }
+ ret = mc13783_adc_convert(&adc_param);
+ *result = adc_param.value[0];
+ return ret;
+}
+
+/*!
+ * This function triggers a conversion and returns eight sampling results of
+ * one channel.
+ *
+ * @param channel The channel to be sampled
+ * @param result The pointer to array to store eight sampling results.
+ * The memory should be allocated by the caller of this
+ * function.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_convert_8x(t_channel channel, unsigned short *result)
+{
+ t_adc_param adc_param;
+ int i;
+ PMIC_STATUS ret;
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ channel = channel_num[channel];
+
+ if (channel == -1) {
+ pr_debug("Wrong channel ID\n");
+ return PMIC_PARAMETER_ERROR;
+ }
+ mc13783_adc_init_param(&adc_param);
+ pr_debug("pmic_adc_convert_8x\n");
+ adc_param.read_ts = false;
+ adc_param.single_channel = true;
+ adc_param.read_mode = mc13783_set_read_mode(channel);
+ if ((channel >= 0) && (channel <= 7)) {
+ adc_param.channel_0 = channel;
+ adc_param.channel_1 = channel;
+ adc_param.group = false;
+ } else if ((channel >= 8) && (channel <= 15)) {
+ adc_param.channel_0 = channel & 0x07;
+ adc_param.channel_1 = channel & 0x07;
+ adc_param.group = true;
+ } else {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ ret = mc13783_adc_convert(&adc_param);
+ for (i = 0; i <= 7; i++) {
+ result[i] = adc_param.value[i];
+ }
+ return ret;
+}
+
+/*!
+ * This function triggers a conversion and returns sampling results of each
+ * specified channel.
+ *
+ * @param channels This input parameter is bitmap to specify channels
+ * to be sampled.
+ * @param result The pointer to array to store sampling results.
+ * The memory should be allocated by the caller of this
+ * function.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_convert_multichnnel(t_channel channels,
+ unsigned short *result)
+{
+ t_adc_param adc_param;
+ int i;
+ PMIC_STATUS ret;
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ mc13783_adc_init_param(&adc_param);
+ pr_debug("pmic_adc_convert_multichnnel\n");
+
+ channels = channel_num[channels];
+
+ if (channels == -1) {
+ pr_debug("Wrong channel ID\n");
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ adc_param.read_ts = false;
+ adc_param.single_channel = false;
+ if ((channels >= 0) && (channels <= 7)) {
+ adc_param.channel_0 = channels;
+ adc_param.channel_1 = ((channels + 4) % 4) + 4;
+ adc_param.group = false;
+ } else if ((channels >= 8) && (channels <= 15)) {
+ channels = channels & 0x07;
+ adc_param.channel_0 = channels;
+ adc_param.channel_1 = ((channels + 4) % 4) + 4;
+ adc_param.group = true;
+ } else {
+ return PMIC_PARAMETER_ERROR;
+ }
+ adc_param.read_mode = 0x00003f;
+ adc_param.read_ts = false;
+ ret = mc13783_adc_convert(&adc_param);
+
+ for (i = 0; i <= 7; i++) {
+ result[i] = adc_param.value[i];
+ }
+ return ret;
+}
+
+/*!
+ * This function sets touch screen operation mode.
+ *
+ * @param touch_mode Touch screen operation mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_set_touch_mode(t_touch_mode touch_mode)
+{
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ CHECK_ERROR(pmic_write_reg(REG_ADC_0,
+ BITFVAL(MC13783_ADC0_TS_M, touch_mode),
+ BITFMASK(MC13783_ADC0_TS_M)));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrieves the current touch screen operation mode.
+ *
+ * @param touch_mode Pointer to the retrieved touch screen operation
+ * mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_get_touch_mode(t_touch_mode * touch_mode)
+{
+ unsigned int value;
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ CHECK_ERROR(pmic_read_reg(REG_ADC_0, &value, PMIC_ALL_BITS));
+
+ *touch_mode = BITFEXT(value, MC13783_ADC0_TS_M);
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrieves the current touch screen (X,Y) coordinates.
+ *
+ * @param touch_sample Pointer to touch sample.
+ * @param wait indicates whether this call must block or not.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_get_touch_sample(t_touch_screen * touch_sample, int wait)
+{
+ if (mc13783_adc_read_ts(touch_sample, wait) != 0)
+ return PMIC_ERROR;
+ if (0 == pmic_adc_filter(touch_sample))
+ return PMIC_SUCCESS;
+ else
+ return PMIC_ERROR;
+}
+
+/*!
+ * This function read the touch screen value.
+ *
+ * @param ts_value return value of touch screen
+ * @param wait_tsi if true, this function is synchronous (wait in TSI event).
+ *
+ * @return This function returns 0.
+ */
+PMIC_STATUS mc13783_adc_read_ts(t_touch_screen * ts_value, int wait_tsi)
+{
+ t_adc_param param;
+ pr_debug("mc13783_adc : mc13783_adc_read_ts\n");
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ if (wait_ts) {
+ pr_debug("mc13783_adc : error TS busy \n");
+ return PMIC_ERROR;
+ }
+ mc13783_adc_init_param(&param);
+ param.wait_tsi = wait_tsi;
+ param.read_ts = true;
+ if (mc13783_adc_convert(&param) != 0)
+ return PMIC_ERROR;
+ /* check if x-y is ok */
+ if ((param.ts_value.x_position1 < TS_X_MAX) &&
+ (param.ts_value.x_position1 >= TS_X_MIN) &&
+ (param.ts_value.y_position1 < TS_Y_MAX) &&
+ (param.ts_value.y_position1 >= TS_Y_MIN) &&
+ (param.ts_value.x_position2 < TS_X_MAX) &&
+ (param.ts_value.x_position2 >= TS_X_MIN) &&
+ (param.ts_value.y_position2 < TS_Y_MAX) &&
+ (param.ts_value.y_position2 >= TS_Y_MIN) &&
+ (param.ts_value.x_position3 < TS_X_MAX) &&
+ (param.ts_value.x_position3 >= TS_X_MIN) &&
+ (param.ts_value.y_position3 < TS_Y_MAX) &&
+ (param.ts_value.y_position3 >= TS_Y_MIN)) {
+ ts_value->x_position = param.ts_value.x_position;
+ ts_value->x_position1 = param.ts_value.x_position1;
+ ts_value->x_position2 = param.ts_value.x_position2;
+ ts_value->x_position3 = param.ts_value.x_position3;
+ ts_value->y_position = param.ts_value.y_position;
+ ts_value->y_position1 = param.ts_value.y_position1;
+ ts_value->y_position2 = param.ts_value.y_position2;
+ ts_value->y_position3 = param.ts_value.y_position3;
+ ts_value->contact_resistance =
+ param.ts_value.contact_resistance + 1;
+
+ } else {
+ ts_value->x_position = 0;
+ ts_value->y_position = 0;
+ ts_value->contact_resistance = 0;
+
+ }
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function starts a Battery Current mode conversion.
+ *
+ * @param mode Conversion mode.
+ * @param result Battery Current measurement result.
+ * if \a mode = ADC_8CHAN_1X, the result is \n
+ * result[0] = (BATTP - BATT_I) \n
+ * if \a mode = ADC_1CHAN_8X, the result is \n
+ * result[0] = BATTP \n
+ * result[1] = BATT_I \n
+ * result[2] = BATTP \n
+ * result[3] = BATT_I \n
+ * result[4] = BATTP \n
+ * result[5] = BATT_I \n
+ * result[6] = BATTP \n
+ * result[7] = BATT_I
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_get_battery_current(t_conversion_mode mode,
+ unsigned short *result)
+{
+ PMIC_STATUS ret;
+ t_channel channel;
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ channel = BATTERY_CURRENT;
+ if (mode == ADC_8CHAN_1X) {
+ ret = pmic_adc_convert(channel, result);
+ } else {
+ ret = pmic_adc_convert_8x(channel, result);
+ }
+ return ret;
+}
+
+/*!
+ * This function request a ADC.
+ *
+ * @return This function returns index of ADC to be used (0 or 1) if successful.
+ return -1 if error.
+ */
+int mc13783_adc_request(bool read_ts)
+{
+ int adc_index = -1;
+ if (read_ts != 0) {
+ /*for ts we use bis=0 */
+ if (adc_dev[0] == ADC_USED)
+ return -1;
+ /*no wait here */
+ adc_dev[0] = ADC_USED;
+ adc_index = 0;
+ } else {
+ /*for other adc use bis = 1 */
+ if (adc_dev[1] == ADC_USED) {
+ return -1;
+ /*no wait here */
+ }
+ adc_dev[1] = ADC_USED;
+ adc_index = 1;
+ }
+ pr_debug("mc13783_adc : request ADC %d\n", adc_index);
+ return adc_index;
+}
+
+/*!
+ * This function release an ADC.
+ *
+ * @param adc_index index of ADC to be released.
+ *
+ * @return This function returns 0 if successful.
+ */
+int mc13783_adc_release(int adc_index)
+{
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+
+ pr_debug("mc13783_adc : release ADC %d\n", adc_index);
+ if ((adc_dev[adc_index] == ADC_MONITORING) ||
+ (adc_dev[adc_index] == ADC_USED)) {
+ adc_dev[adc_index] = ADC_FREE;
+ wake_up(&queue_adc_busy);
+ return 0;
+ }
+ return -1;
+}
+
+/*!
+ * This function initializes monitoring structure.
+ *
+ * @param monitor Structure to be initialized.
+ *
+ * @return This function returns 0 if successful.
+ */
+int mc13783_adc_init_monitor_param(t_monitoring_param * monitor)
+{
+ pr_debug("mc13783_adc : init monitor\n");
+ monitor->delay = 0;
+ monitor->conv_delay = false;
+ monitor->channel = BATTERY_VOLTAGE;
+ monitor->read_mode = 0;
+ monitor->comp_low = 0;
+ monitor->comp_high = 0;
+ monitor->group = 0;
+ monitor->check_mode = CHECK_LOW_OR_HIGH;
+ monitor->callback = NULL;
+ return 0;
+}
+
+/*!
+ * This function actives the comparator. When comparator is active and ADC
+ * is enabled, the 8th converted value will be digitally compared against the
+ * window defined by WLOW and WHIGH registers.
+ *
+ * @param low Comparison window low threshold (WLOW).
+ * @param high Comparison window high threshold (WHIGH).
+ * @param channel The channel to be sampled
+ * @param callback Callback function to be called when the converted
+ * value is beyond the comparison window. The callback
+ * function will pass a parameter of type
+ * \b t_comp_expection to indicate the reason of
+ * comparator exception.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_active_comparator(unsigned char low,
+ unsigned char high,
+ t_channel channel,
+ t_comparator_cb callback)
+{
+ bool use_bis = false;
+ unsigned int adc_0_reg = 0, adc_1_reg = 0, adc_3_reg = 0;
+ t_monitoring_param monitoring;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ if (monitor_en) {
+ pr_debug("mc13783_adc : monitoring already configured\n");
+ return PMIC_ERROR;
+ }
+ monitor_en = true;
+ mc13783_adc_init_monitor_param(&monitoring);
+ monitoring.comp_low = low;
+ monitoring.comp_high = high;
+ monitoring.channel = channel;
+ monitoring.callback = (void *)callback;
+
+ use_bis = mc13783_adc_request(false);
+ if (use_bis < 0) {
+ pr_debug("mc13783_adc : request error\n");
+ return PMIC_ERROR;
+ }
+ monitor_adc = use_bis;
+
+ adc_0_reg = 0;
+
+ /* TO DO ADOUT CONFIGURE */
+ adc_0_reg = monitoring.read_mode & ADC_MODE_MASK;
+ if (use_bis) {
+ /* add adc bis */
+ adc_0_reg |= ADC_BIS;
+ }
+ adc_0_reg |= ADC_WCOMP;
+
+ /* CONFIGURE ADC REG 1 */
+ adc_1_reg = 0;
+ adc_1_reg |= ADC_EN;
+ if (monitoring.conv_delay) {
+ adc_1_reg |= ADC_ATO;
+ }
+ if (monitoring.group) {
+ adc_1_reg |= ADC_ADSEL;
+ }
+ adc_1_reg |= (monitoring.channel << ADC_CH_0_POS) & ADC_CH_0_MASK;
+ adc_1_reg |= (monitoring.delay << ADC_DELAY_POS) & ADC_DELAY_MASK;
+ if (use_bis) {
+ adc_1_reg |= ADC_BIS;
+ }
+
+ adc_3_reg |= (monitoring.comp_high << ADC_WCOMP_H_POS) &
+ ADC_WCOMP_H_MASK;
+ adc_3_reg |= (monitoring.comp_low << ADC_WCOMP_L_POS) &
+ ADC_WCOMP_L_MASK;
+ if (use_bis) {
+ adc_3_reg |= ADC_BIS;
+ }
+
+ wcomp_mode = monitoring.check_mode;
+ /* call back to be called when event is detected. */
+ monitoring_cb = monitoring.callback;
+
+ CHECK_ERROR(pmic_write_reg(REG_ADC_0, adc_0_reg, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_1, adc_1_reg, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_3, adc_3_reg, PMIC_ALL_BITS));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function deactivates the comparator.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_deactive_comparator(void)
+{
+ unsigned int reg_value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ if (!monitor_en) {
+ pr_debug("mc13783_adc : adc monitoring free\n");
+ return PMIC_ERROR;
+ }
+
+ if (monitor_en) {
+ reg_value = ADC_BIS;
+ }
+
+ /* clear all reg value */
+ CHECK_ERROR(pmic_write_reg(REG_ADC_0, reg_value, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_1, reg_value, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC_3, reg_value, PMIC_ALL_BITS));
+
+ reg_value = 0;
+
+ if (monitor_adc) {
+ CHECK_ERROR(pmic_write_reg
+ (REG_ADC_4, reg_value, PMIC_ALL_BITS));
+ } else {
+ CHECK_ERROR(pmic_write_reg
+ (REG_ADC_2, reg_value, PMIC_ALL_BITS));
+ }
+
+ mc13783_adc_release(monitor_adc);
+ monitor_en = false;
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function implements IOCTL controls on a MC13783 ADC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @param cmd the command
+ * @param arg the parameter
+ * @return This function returns 0 if successful.
+ */
+static int pmic_adc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ t_adc_convert_param *convert_param;
+ t_touch_mode touch_mode;
+ t_touch_screen touch_sample;
+ unsigned short b_current;
+ t_adc_comp_param *comp_param;
+ if ((_IOC_TYPE(cmd) != 'p') && (_IOC_TYPE(cmd) != 'D'))
+ return -ENOTTY;
+
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+
+ switch (cmd) {
+ case PMIC_ADC_INIT:
+ pr_debug("init adc\n");
+ CHECK_ERROR(pmic_adc_init());
+ break;
+
+ case PMIC_ADC_DEINIT:
+ pr_debug("deinit adc\n");
+ CHECK_ERROR(pmic_adc_deinit());
+ break;
+
+ case PMIC_ADC_CONVERT:
+ if ((convert_param = kmalloc(sizeof(t_adc_convert_param),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(convert_param, (t_adc_convert_param *) arg,
+ sizeof(t_adc_convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+ CHECK_ERROR_KFREE(pmic_adc_convert(convert_param->channel,
+ convert_param->result),
+ (kfree(convert_param)));
+
+ if (copy_to_user((t_adc_convert_param *) arg, convert_param,
+ sizeof(t_adc_convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+ kfree(convert_param);
+ break;
+
+ case PMIC_ADC_CONVERT_8X:
+ if ((convert_param = kmalloc(sizeof(t_adc_convert_param),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(convert_param, (t_adc_convert_param *) arg,
+ sizeof(t_adc_convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+ CHECK_ERROR_KFREE(pmic_adc_convert_8x(convert_param->channel,
+ convert_param->result),
+ (kfree(convert_param)));
+
+ if (copy_to_user((t_adc_convert_param *) arg, convert_param,
+ sizeof(t_adc_convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+ kfree(convert_param);
+ break;
+
+ case PMIC_ADC_CONVERT_MULTICHANNEL:
+ if ((convert_param = kmalloc(sizeof(t_adc_convert_param),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(convert_param, (t_adc_convert_param *) arg,
+ sizeof(t_adc_convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+
+ CHECK_ERROR_KFREE(pmic_adc_convert_multichnnel
+ (convert_param->channel,
+ convert_param->result),
+ (kfree(convert_param)));
+
+ if (copy_to_user((t_adc_convert_param *) arg, convert_param,
+ sizeof(t_adc_convert_param))) {
+ kfree(convert_param);
+ return -EFAULT;
+ }
+ kfree(convert_param);
+ break;
+
+ case PMIC_ADC_SET_TOUCH_MODE:
+ CHECK_ERROR(pmic_adc_set_touch_mode((t_touch_mode) arg));
+ break;
+
+ case PMIC_ADC_GET_TOUCH_MODE:
+ CHECK_ERROR(pmic_adc_get_touch_mode(&touch_mode));
+ if (copy_to_user((t_touch_mode *) arg, &touch_mode,
+ sizeof(t_touch_mode))) {
+ return -EFAULT;
+ }
+ break;
+
+ case PMIC_ADC_GET_TOUCH_SAMPLE:
+ pr_debug("pmic_adc_ioctl: " "PMIC_ADC_GET_TOUCH_SAMPLE\n");
+ CHECK_ERROR(pmic_adc_get_touch_sample(&touch_sample, 1));
+ if (copy_to_user((t_touch_screen *) arg, &touch_sample,
+ sizeof(t_touch_screen))) {
+ return -EFAULT;
+ }
+ break;
+
+ case PMIC_ADC_GET_BATTERY_CURRENT:
+ CHECK_ERROR(pmic_adc_get_battery_current(ADC_8CHAN_1X,
+ &b_current));
+ if (copy_to_user((unsigned short *)arg, &b_current,
+ sizeof(unsigned short))) {
+
+ return -EFAULT;
+ }
+ break;
+
+ case PMIC_ADC_ACTIVATE_COMPARATOR:
+ if ((comp_param = kmalloc(sizeof(t_adc_comp_param), GFP_KERNEL))
+ == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(comp_param, (t_adc_comp_param *) arg,
+ sizeof(t_adc_comp_param))) {
+ kfree(comp_param);
+ return -EFAULT;
+ }
+ CHECK_ERROR_KFREE(pmic_adc_active_comparator(comp_param->wlow,
+ comp_param->whigh,
+ comp_param->
+ channel,
+ comp_param->
+ callback),
+ (kfree(comp_param)));
+ break;
+
+ case PMIC_ADC_DEACTIVE_COMPARATOR:
+ CHECK_ERROR(pmic_adc_deactive_comparator());
+ break;
+
+ default:
+ pr_debug("pmic_adc_ioctl: unsupported ioctl command 0x%x\n",
+ cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct file_operations mc13783_adc_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = pmic_adc_ioctl,
+ .open = pmic_adc_open,
+ .release = pmic_adc_free,
+};
+
+static int pmic_adc_module_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device *temp_class;
+
+ pmic_adc_major = register_chrdev(0, "pmic_adc", &mc13783_adc_fops);
+
+ if (pmic_adc_major < 0) {
+ pr_debug(KERN_ERR "Unable to get a major for pmic_adc\n");
+ return pmic_adc_major;
+ }
+ init_waitqueue_head(&suspendq);
+
+ pmic_adc_class = class_create(THIS_MODULE, "pmic_adc");
+ if (IS_ERR(pmic_adc_class)) {
+ pr_debug(KERN_ERR "Error creating pmic_adc class.\n");
+ ret = PTR_ERR(pmic_adc_class);
+ goto err_out1;
+ }
+
+ temp_class = device_create(pmic_adc_class, NULL,
+ MKDEV(pmic_adc_major, 0), NULL, "pmic_adc");
+ if (IS_ERR(temp_class)) {
+ pr_debug(KERN_ERR "Error creating pmic_adc class device.\n");
+ ret = PTR_ERR(temp_class);
+ goto err_out2;
+ }
+
+ ret = pmic_adc_init();
+ if (ret != PMIC_SUCCESS) {
+ pr_debug(KERN_ERR "Error in pmic_adc_init.\n");
+ goto err_out4;
+ }
+
+ pmic_adc_ready = 1;
+ pr_debug(KERN_INFO "PMIC ADC successfully probed\n");
+ return ret;
+
+ err_out4:
+ device_destroy(pmic_adc_class, MKDEV(pmic_adc_major, 0));
+ err_out2:
+ class_destroy(pmic_adc_class);
+ err_out1:
+ unregister_chrdev(pmic_adc_major, "pmic_adc");
+ return ret;
+}
+
+static int pmic_adc_module_remove(struct platform_device *pdev)
+{
+ pmic_adc_ready = 0;
+ pmic_adc_deinit();
+ device_destroy(pmic_adc_class, MKDEV(pmic_adc_major, 0));
+ class_destroy(pmic_adc_class);
+ unregister_chrdev(pmic_adc_major, "pmic_adc");
+ pr_debug(KERN_INFO "PMIC ADC successfully removed\n");
+ return 0;
+}
+
+static struct platform_driver pmic_adc_driver_ldm = {
+ .driver = {
+ .name = "pmic_adc",
+ },
+ .suspend = pmic_adc_suspend,
+ .resume = pmic_adc_resume,
+ .probe = pmic_adc_module_probe,
+ .remove = pmic_adc_module_remove,
+};
+
+/*
+ * Initialization and Exit
+ */
+static int __init pmic_adc_module_init(void)
+{
+ pr_debug("PMIC ADC driver loading...\n");
+ return platform_driver_register(&pmic_adc_driver_ldm);
+}
+
+static void __exit pmic_adc_module_exit(void)
+{
+ platform_driver_unregister(&pmic_adc_driver_ldm);
+ pr_debug("PMIC ADC driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+module_init(pmic_adc_module_init);
+module_exit(pmic_adc_module_exit);
+
+MODULE_DESCRIPTION("PMIC ADC device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_adc_defs.h b/drivers/mxc/pmic/mc13783/pmic_adc_defs.h
new file mode 100644
index 000000000000..db1b08232c77
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_adc_defs.h
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_adc_defs.h
+ * @brief This header contains all defines for PMIC(mc13783) ADC driver.
+ *
+ * @ingroup PMIC_ADC
+ */
+
+#ifndef __MC13783_ADC__DEFS_H__
+#define __MC13783_ADC__DEFS_H__
+
+#define MC13783_ADC_DEVICE "/dev/mc13783_adc"
+
+#define DEF_ADC_0 0x008000
+#define DEF_ADC_3 0x000080
+
+#define ADC_NB_AVAILABLE 2
+
+#define MAX_CHANNEL 7
+
+/*
+ * Maximun allowed variation in the three X/Y co-ordinates acquired from
+ * touch-screen
+ */
+#define DELTA_Y_MAX 50
+#define DELTA_X_MAX 50
+
+/* Upon clearing the filter, this is the delay in restarting the filter */
+#define FILTER_MIN_DELAY 4
+
+/* Length of X and Y Touch screen filters */
+#define FILTLEN 3
+
+#define TS_X_MAX 1000
+#define TS_Y_MAX 1000
+
+#define TS_X_MIN 80
+#define TS_Y_MIN 80
+
+#define MC13783_ADC0_TS_M_LSH 14
+#define MC13783_ADC0_TS_M_WID 3
+/*
+ * ADC 0
+ */
+#define ADC_WAIT_TSI_0 0x001C00
+
+/*
+ * ADC 1
+ */
+
+#define ADC_EN 0x000001
+#define ADC_SGL_CH 0x000002
+#define ADC_ADSEL 0x000008
+#define ADC_CH_0_POS 5
+#define ADC_CH_0_MASK 0x0000E0
+#define ADC_CH_1_POS 8
+#define ADC_CH_1_MASK 0x000700
+#define ADC_DELAY_POS 11
+#define ADC_DELAY_MASK 0x07F800
+#define ADC_ATO 0x080000
+#define ASC_ADC 0x100000
+#define ADC_WAIT_TSI_1 0x300001
+#define ADC_CHRGRAW_D5 0x008000
+
+/*
+ * ADC 2 - 4
+ */
+#define ADD1_RESULT_MASK 0x00000FFC
+#define ADD2_RESULT_MASK 0x00FFC000
+#define ADC_TS_MASK 0x00FFCFFC
+
+/*
+ * ADC 3
+ */
+#define ADC_INC 0x030000
+#define ADC_BIS 0x800000
+
+/*
+ * ADC 3
+ */
+#define ADC_NO_ADTRIG 0x200000
+#define ADC_WCOMP 0x040000
+#define ADC_WCOMP_H_POS 0
+#define ADC_WCOMP_L_POS 9
+#define ADC_WCOMP_H_MASK 0x00003F
+#define ADC_WCOMP_L_MASK 0x007E00
+
+#define ADC_MODE_MASK 0x00003F
+
+/*
+ * Interrupt Status 0
+ */
+#define ADC_INT_BISDONEI 0x02
+
+/*!
+ * Define state mode of ADC.
+ */
+typedef enum adc_state {
+ /*!
+ * Free.
+ */
+ ADC_FREE,
+ /*!
+ * Used.
+ */
+ ADC_USED,
+ /*!
+ * Monitoring
+ */
+ ADC_MONITORING,
+} t_adc_state;
+
+/*!
+ * This enumeration, is used to configure the mode of ADC.
+ */
+typedef enum reading_mode {
+ /*!
+ * Enables lithium cell reading
+ */
+ M_LITHIUM_CELL = 0x000001,
+ /*!
+ * Enables charge current reading
+ */
+ M_CHARGE_CURRENT = 0x000002,
+ /*!
+ * Enables battery current reading
+ */
+ M_BATTERY_CURRENT = 0x000004,
+ /*!
+ * Enables thermistor reading
+ */
+ M_THERMISTOR = 0x000008,
+ /*!
+ * Enables die temperature reading
+ */
+ M_DIE_TEMPERATURE = 0x000010,
+ /*!
+ * Enables UID reading
+ */
+ M_UID = 0x000020,
+} t_reading_mode;
+
+/*!
+ * This enumeration, is used to configure the monitoring mode.
+ */
+typedef enum check_mode {
+ /*!
+ * Comparator low level
+ */
+ CHECK_LOW,
+ /*!
+ * Comparator high level
+ */
+ CHECK_HIGH,
+ /*!
+ * Comparator low or high level
+ */
+ CHECK_LOW_OR_HIGH,
+} t_check_mode;
+
+/*!
+ * This structure is used to configure and report adc value.
+ */
+typedef struct {
+ /*!
+ * Delay before first conversion
+ */
+ unsigned int delay;
+ /*!
+ * sets the ATX bit for delay on all conversion
+ */
+ bool conv_delay;
+ /*!
+ * Sets the single channel mode
+ */
+ bool single_channel;
+ /*!
+ * Selects the set of inputs
+ */
+ bool group;
+ /*!
+ * Channel selection 1
+ */
+ t_channel channel_0;
+ /*!
+ * Channel selection 2
+ */
+ t_channel channel_1;
+ /*!
+ * Used to configure ADC mode with t_reading_mode
+ */
+ t_reading_mode read_mode;
+ /*!
+ * Sets the Touch screen mode
+ */
+ bool read_ts;
+ /*!
+ * Wait TSI event before touch screen reading
+ */
+ bool wait_tsi;
+ /*!
+ * Sets CHRGRAW scaling to divide by 5
+ * Only supported on 2.0 and higher
+ */
+ bool chrgraw_devide_5;
+ /*!
+ * Return ADC values
+ */
+ unsigned int value[8];
+ /*!
+ * Return touch screen values
+ */
+ t_touch_screen ts_value;
+} t_adc_param;
+
+/*!
+ * This structure is used to configure the monitoring mode of ADC.
+ */
+typedef struct {
+ /*!
+ * Delay before first conversion
+ */
+ unsigned int delay;
+ /*!
+ * sets the ATX bit for delay on all conversion
+ */
+ bool conv_delay;
+ /*!
+ * Channel selection 1
+ */
+ t_channel channel;
+ /*!
+ * Selects the set of inputs
+ */
+ bool group;
+ /*!
+ * Used to configure ADC mode with t_reading_mode
+ */
+ unsigned int read_mode;
+ /*!
+ * Comparator low level in WCOMP mode
+ */
+ unsigned int comp_low;
+ /*!
+ * Comparator high level in WCOMP mode
+ */
+ unsigned int comp_high;
+ /*!
+ * Sets type of monitoring (low, high or both)
+ */
+ t_check_mode check_mode;
+ /*!
+ * Callback to be launched when event is detected
+ */
+ void (*callback) (void);
+} t_monitoring_param;
+
+/*!
+ * This function performs filtering and rejection of excessive noise prone
+ * samples.
+ *
+ * @param ts_curr Touch screen value
+ *
+ * @return This function returns 0 on success, -1 otherwise.
+ */
+static int pmic_adc_filter(t_touch_screen * ts_curr);
+
+/*!
+ * This function request a ADC.
+ *
+ * @return This function returns index of ADC to be used (0 or 1) if successful.
+ return -1 if error.
+ */
+int mc13783_adc_request(bool read_ts);
+
+/*!
+ * This function is used to update buffer of touch screen value in read mode.
+ */
+void update_buffer(void);
+
+/*!
+ * This function release an ADC.
+ *
+ * @param adc_index index of ADC to be released.
+ *
+ * @return This function returns 0 if successful.
+ */
+int mc13783_adc_release(int adc_index);
+
+/*!
+ * This function select the required read_mode for a specific channel.
+ *
+ * @param channel The channel to be sampled
+ *
+ * @return This function returns the requires read_mode
+ */
+t_reading_mode mc13783_set_read_mode(t_channel channel);
+
+/*!
+ * This function read the touch screen value.
+ *
+ * @param touch_sample return value of touch screen
+ * @param wait_tsi if true, this function is synchronous (wait in TSI event).
+ *
+ * @return This function returns 0.
+ */
+PMIC_STATUS mc13783_adc_read_ts(t_touch_screen * touch_sample, int wait_tsi);
+
+#endif /* __MC13783_ADC__DEFS_H__ */
diff --git a/drivers/mxc/pmic/mc13783/pmic_audio.c b/drivers/mxc/pmic/mc13783/pmic_audio.c
new file mode 100644
index 000000000000..6aab4b83f6ea
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_audio.c
@@ -0,0 +1,5876 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_audio.c
+ * @brief Implementation of the PMIC(mc13783) Audio driver APIs.
+ *
+ * The PMIC Audio driver and this API were developed to support the
+ * audio playback, recording, and mixing capabilities of the power
+ * management ICs that are available from Freescale Semiconductor, Inc.
+ *
+ * The following operating modes are supported:
+ *
+ * @verbatim
+ Operating Mode mc13783
+ ---------------------------- -------
+ Stereo DAC Playback Yes
+ Stereo DAC Input Mixing Yes
+ Voice CODEC Playback Yes
+ Voice CODEC Input Mixing Yes
+ Voice CODEC Mono Recording Yes
+ Voice CODEC Stereo Recording Yes
+ Microphone Bias Control Yes
+ Output Amplifier Control Yes
+ Output Mixing Control Yes
+ Input Amplifier Control Yes
+ Master/Slave Mode Select Yes
+ Anti Pop Bias Circuit Control Yes
+ @endverbatim
+ *
+ * Note that the Voice CODEC may also be referred to as the Telephone
+ * CODEC in the PMIC DTS documentation. Also note that, while the power
+ * management ICs do provide similar audio capabilities, each PMIC may
+ * support additional configuration settings and features. Therefore, it
+ * is highly recommended that the appropriate power management IC DTS
+ * documents be used in conjunction with this API interface.
+ *
+ * @ingroup PMIC_AUDIO
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h> /* For tasklet interface. */
+#include <linux/platform_device.h> /* For kernel module interface. */
+#include <linux/init.h>
+#include <linux/spinlock.h> /* For spinlock interface. */
+#include <linux/pmic_adc.h> /* For PMIC ADC driver interface. */
+#include <linux/pmic_status.h>
+#include <mach/pmic_audio.h> /* For PMIC Audio driver interface. */
+
+/*
+ * mc13783 PMIC Audio API
+ */
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(MIN_STDAC_SAMPLING_RATE_HZ);
+EXPORT_SYMBOL(MAX_STDAC_SAMPLING_RATE_HZ);
+EXPORT_SYMBOL(pmic_audio_open);
+EXPORT_SYMBOL(pmic_audio_close);
+EXPORT_SYMBOL(pmic_audio_set_protocol);
+EXPORT_SYMBOL(pmic_audio_get_protocol);
+EXPORT_SYMBOL(pmic_audio_enable);
+EXPORT_SYMBOL(pmic_audio_disable);
+EXPORT_SYMBOL(pmic_audio_reset);
+EXPORT_SYMBOL(pmic_audio_reset_all);
+EXPORT_SYMBOL(pmic_audio_set_callback);
+EXPORT_SYMBOL(pmic_audio_clear_callback);
+EXPORT_SYMBOL(pmic_audio_get_callback);
+EXPORT_SYMBOL(pmic_audio_antipop_enable);
+EXPORT_SYMBOL(pmic_audio_antipop_disable);
+EXPORT_SYMBOL(pmic_audio_digital_filter_reset);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_clock);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_clock);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_rxtx_timeslot);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_rxtx_timeslot);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_secondary_txslot);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_secondary_txslot);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_config);
+EXPORT_SYMBOL(pmic_audio_vcodec_clear_config);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_config);
+EXPORT_SYMBOL(pmic_audio_vcodec_enable_bypass);
+EXPORT_SYMBOL(pmic_audio_vcodec_disable_bypass);
+EXPORT_SYMBOL(pmic_audio_stdac_set_clock);
+EXPORT_SYMBOL(pmic_audio_stdac_get_clock);
+EXPORT_SYMBOL(pmic_audio_stdac_set_rxtx_timeslot);
+EXPORT_SYMBOL(pmic_audio_stdac_get_rxtx_timeslot);
+EXPORT_SYMBOL(pmic_audio_stdac_set_config);
+EXPORT_SYMBOL(pmic_audio_stdac_clear_config);
+EXPORT_SYMBOL(pmic_audio_stdac_get_config);
+EXPORT_SYMBOL(pmic_audio_input_set_config);
+EXPORT_SYMBOL(pmic_audio_input_clear_config);
+EXPORT_SYMBOL(pmic_audio_input_get_config);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_mic);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_mic);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_mic_on_off);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_mic_on_off);
+EXPORT_SYMBOL(pmic_audio_vcodec_set_record_gain);
+EXPORT_SYMBOL(pmic_audio_vcodec_get_record_gain);
+EXPORT_SYMBOL(pmic_audio_vcodec_enable_micbias);
+EXPORT_SYMBOL(pmic_audio_vcodec_disable_micbias);
+EXPORT_SYMBOL(pmic_audio_vcodec_enable_mixer);
+EXPORT_SYMBOL(pmic_audio_vcodec_disable_mixer);
+EXPORT_SYMBOL(pmic_audio_stdac_enable_mixer);
+EXPORT_SYMBOL(pmic_audio_stdac_disable_mixer);
+EXPORT_SYMBOL(pmic_audio_output_set_port);
+EXPORT_SYMBOL(pmic_audio_output_get_port);
+EXPORT_SYMBOL(pmic_audio_output_clear_port);
+EXPORT_SYMBOL(pmic_audio_output_set_stereo_in_gain);
+EXPORT_SYMBOL(pmic_audio_output_get_stereo_in_gain);
+EXPORT_SYMBOL(pmic_audio_output_set_pgaGain);
+EXPORT_SYMBOL(pmic_audio_output_get_pgaGain);
+EXPORT_SYMBOL(pmic_audio_output_enable_mixer);
+EXPORT_SYMBOL(pmic_audio_output_disable_mixer);
+EXPORT_SYMBOL(pmic_audio_output_set_balance);
+EXPORT_SYMBOL(pmic_audio_output_get_balance);
+EXPORT_SYMBOL(pmic_audio_output_enable_mono_adder);
+EXPORT_SYMBOL(pmic_audio_output_disable_mono_adder);
+EXPORT_SYMBOL(pmic_audio_output_set_mono_adder_gain);
+EXPORT_SYMBOL(pmic_audio_output_get_mono_adder_gain);
+EXPORT_SYMBOL(pmic_audio_output_set_config);
+EXPORT_SYMBOL(pmic_audio_output_clear_config);
+EXPORT_SYMBOL(pmic_audio_output_get_config);
+EXPORT_SYMBOL(pmic_audio_output_enable_phantom_ground);
+EXPORT_SYMBOL(pmic_audio_output_disable_phantom_ground);
+EXPORT_SYMBOL(pmic_audio_set_autodetect);
+#ifdef DEBUG_AUDIO
+EXPORT_SYMBOL(pmic_audio_dump_registers);
+#endif /* DEBUG_AUDIO */
+/*!
+ * Define the minimum sampling rate (in Hz) that is supported by the
+ * Stereo DAC.
+ */
+const unsigned MIN_STDAC_SAMPLING_RATE_HZ = 8000;
+
+/*!
+ * Define the maximum sampling rate (in Hz) that is supported by the
+ * Stereo DAC.
+ */
+const unsigned MAX_STDAC_SAMPLING_RATE_HZ = 96000;
+
+/*! @def SET_BITS
+ * Set a register field to a given value.
+ */
+#define SET_BITS(reg, field, value) (((value) << reg.field.offset) & \
+ reg.field.mask)
+/*! @def GET_BITS
+ * Get the current value of a given register field.
+ */
+#define GET_BITS(reg, field, value) (((value) & reg.field.mask) >> \
+ reg.field.offset)
+
+/*!
+ * @brief Define the possible states for a device handle.
+ *
+ * This enumeration is used to track the current state of each device handle.
+ */
+typedef enum {
+ HANDLE_FREE, /*!< Handle is available for use. */
+ HANDLE_IN_USE /*!< Handle is currently in use. */
+} HANDLE_STATE;
+
+/*!
+ * @brief Identifies the hardware interrupt source.
+ *
+ * This enumeration identifies which of the possible hardware interrupt
+ * sources actually caused the current interrupt handler to be called.
+ */
+typedef enum {
+ CORE_EVENT_MC2BI, /*!< Microphone Bias 2 detect. */
+ CORE_EVENT_HSDETI, /*!< Detect Headset attach */
+ CORE_EVENT_HSLI, /*!< Detect Stereo Headset */
+ CORE_EVENT_ALSPTHI, /*!< Detect Thermal shutdown of ALSP */
+ CORE_EVENT_AHSSHORTI /*!< Detect Short circuit on AHS outputs */
+} PMIC_CORE_EVENT;
+
+/*!
+ * @brief This structure is used to track the state of a microphone input.
+ */
+typedef struct {
+ PMIC_AUDIO_INPUT_PORT mic; /*!< Microphone input port. */
+ PMIC_AUDIO_INPUT_MIC_STATE micOnOff; /*!< Microphone On/Off state. */
+ PMIC_AUDIO_MIC_AMP_MODE ampMode; /*!< Input amplifier mode. */
+ PMIC_AUDIO_MIC_GAIN gain; /*!< Input amplifier gain level. */
+} PMIC_MICROPHONE_STATE;
+
+/*!
+ * @brief Tracks whether a headset is currently attached or not.
+ */
+typedef enum {
+ NO_HEADSET, /*!< No headset currently attached. */
+ HEADSET_ON /*!< Headset has been attached. */
+} HEADSET_STATUS;
+
+/*!
+ * @brief mc13783 only enum that indicates the path to output taken
+ * by the voice codec output
+ */
+typedef enum {
+ VCODEC_DIRECT_OUT, /*!< Vcodec signal out direct */
+ VCODEC_MIXER_OUT /*!< Output via the mixer */
+} PMIC_AUDIO_VCODEC_OUTPUT_PATH;
+
+/*!
+ * @brief This structure is used to define a specific hardware register field.
+ *
+ * All hardware register fields are defined using an offset to the LSB
+ * and a mask. The offset is used to right shift a register value before
+ * applying the mask to actually obtain the value of the field.
+ */
+typedef struct {
+ const unsigned char offset; /*!< Offset of LSB of register field. */
+ const unsigned int mask; /*!< Mask value used to isolate register field. */
+} REGFIELD;
+
+/*!
+ * @brief This structure lists all fields of the AUD_CODEC hardware register.
+ */
+typedef struct {
+ REGFIELD CDCSSISEL; /*!< codec SSI bus select */
+ REGFIELD CDCCLKSEL; /*!< Codec clock input select */
+ REGFIELD CDCSM; /*!< Codec slave / master select */
+ REGFIELD CDCBCLINV; /*!< Codec bitclock inversion */
+ REGFIELD CDCFSINV; /*!< Codec framesync inversion */
+ REGFIELD CDCFS; /*!< Bus protocol selection - 2 bits */
+ REGFIELD CDCCLK; /*!< Codec clock setting - 3 bits */
+ REGFIELD CDCFS8K16K; /*!< Codec framesync select */
+ REGFIELD CDCEN; /*!< Codec enable */
+ REGFIELD CDCCLKEN; /*!< Codec clocking enable */
+ REGFIELD CDCTS; /*!< Codec SSI tristate */
+ REGFIELD CDCDITH; /*!< Codec dithering */
+ REGFIELD CDCRESET; /*!< Codec filter reset */
+ REGFIELD CDCBYP; /*!< Codec bypass */
+ REGFIELD CDCALM; /*!< Codec analog loopback */
+ REGFIELD CDCDLM; /*!< Codec digital loopback */
+ REGFIELD AUDIHPF; /*!< Transmit high pass filter enable */
+ REGFIELD AUDOHPF; /*!< Receive high pass filter enable */
+} REGISTER_AUD_CODEC;
+
+/*!
+ * @brief This variable is used to access the AUD_CODEC hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * AUD_CODEC hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_AUD_CODEC regAUD_CODEC = {
+ {0, 0x000001}, /* CDCSSISEL */
+ {1, 0x000002}, /* CDCCLKSEL */
+ {2, 0x000004}, /* CDCSM */
+ {3, 0x000008}, /* CDCBCLINV */
+ {4, 0x000010}, /* CDCFSINV */
+ {5, 0x000060}, /* CDCFS */
+ {7, 0x000380}, /* CDCCLK */
+ {10, 0x000400}, /* CDCFS8K16K */
+ {11, 0x000800}, /* CDCEN */
+ {12, 0x001000}, /* CDCCLKEN */
+ {13, 0x002000}, /* CDCTS */
+ {14, 0x004000}, /* CDCDITH */
+ {15, 0x008000}, /* CDCRESET */
+ {16, 0x010000}, /* CDCBYP */
+ {17, 0x020000}, /* CDCALM */
+ {18, 0x040000}, /* CDCDLM */
+ {19, 0x080000}, /* AUDIHPF */
+ {20, 0x100000} /* AUDOHPF */
+ /* Unused */
+ /* Unused */
+ /* Unused */
+
+};
+
+/*!
+ * @brief This structure lists all fields of the ST_DAC hardware register.
+ */
+ /* VVV */
+typedef struct {
+ REGFIELD STDCSSISEL; /*!< Stereo DAC SSI bus select */
+ REGFIELD STDCCLKSEL; /*!< Stereo DAC clock input select */
+ REGFIELD STDCSM; /*!< Stereo DAC slave / master select */
+ REGFIELD STDCBCLINV; /*!< Stereo DAC bitclock inversion */
+ REGFIELD STDCFSINV; /*!< Stereo DAC framesync inversion */
+ REGFIELD STDCFS; /*!< Bus protocol selection - 2 bits */
+ REGFIELD STDCCLK; /*!< Stereo DAC clock setting - 3 bits */
+ REGFIELD STDCFSDLYB; /*!< Stereo DAC framesync delay bar */
+ REGFIELD STDCEN; /*!< Stereo DAC enable */
+ REGFIELD STDCCLKEN; /*!< Stereo DAC clocking enable */
+ REGFIELD STDCRESET; /*!< Stereo DAC filter reset */
+ REGFIELD SPDIF; /*!< Stereo DAC SSI SPDIF mode. Mode no longer available. */
+ REGFIELD SR; /*!< Stereo DAC sample rate - 4 bits */
+} REGISTER_ST_DAC;
+
+/*!
+ * @brief This variable is used to access the ST_DAC hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * ST_DAC hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_ST_DAC regST_DAC = {
+ {0, 0x000001}, /* STDCSSISEL */
+ {1, 0x000002}, /* STDCCLKSEL */
+ {2, 0x000004}, /* STDCSM */
+ {3, 0x000008}, /* STDCBCLINV */
+ {4, 0x000010}, /* STDCFSINV */
+ {5, 0x000060}, /* STDCFS */
+ {7, 0x000380}, /* STDCCLK */
+ {10, 0x000400}, /* STDCFSDLYB */
+ {11, 0x000800}, /* STDCEN */
+ {12, 0x001000}, /* STDCCLKEN */
+ {15, 0x008000}, /* STDCRESET */
+ {16, 0x010000}, /* SPDIF */
+ {17, 0x1E0000} /* SR */
+};
+
+/*!
+ * @brief This structure lists all of the fields in the SSI_NETWORK hardware register.
+ */
+typedef struct {
+ REGFIELD CDCTXRXSLOT; /*!< Codec timeslot assignment - 2 bits */
+ REGFIELD CDCTXSECSLOT; /*!< Codec secondary transmit timeslot - 2 bits */
+ REGFIELD CDCRXSECSLOT; /*!< Codec secondary receive timeslot - 2 bits */
+ REGFIELD CDCRXSECGAIN; /*!< Codec secondary receive channel gain setting - 2 bits */
+ REGFIELD CDCSUMGAIN; /*!< Codec summed receive signal gain setting */
+ REGFIELD CDCFSDLY; /*!< Codec framesync delay */
+ REGFIELD STDCSLOTS; /*!< Stereo DAC number of timeslots select - 2 bits */
+ REGFIELD STDCRXSLOT; /*!< Stereo DAC timeslot assignment - 2 bits */
+ REGFIELD STDCRXSECSLOT; /*!< Stereo DAC secondary receive timeslot - 2 bits */
+ REGFIELD STDCRXSECGAIN; /*!< Stereo DAC secondary receive channel gain setting - 2 bits */
+ REGFIELD STDCSUMGAIN; /*!< Stereo DAC summed receive signal gain setting */
+} REGISTER_SSI_NETWORK;
+
+/*!
+ * @brief This variable is used to access the SSI_NETWORK hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * SSI_NETWORK hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_SSI_NETWORK regSSI_NETWORK = {
+ {2, 0x00000c}, /* CDCTXRXSLOT */
+ {4, 0x000030}, /* CDCTXSECSLOT */
+ {6, 0x0000c0}, /* CDCRXSECSLOT */
+ {8, 0x000300}, /* CDCRXSECGAIN */
+ {10, 0x000400}, /* CDCSUMGAIN */
+ {11, 0x000800}, /* CDCFSDLY */
+ {12, 0x003000}, /* STDCSLOTS */
+ {14, 0x00c000}, /* STDCRXSLOT */
+ {16, 0x030000}, /* STDCRXSECSLOT */
+ {18, 0x0c0000}, /* STDCRXSECGAIN */
+ {20, 0x100000} /* STDCSUMGAIN */
+};
+
+/*!
+ * @brief This structure lists all fields of the AUDIO_TX hardware register.
+ *
+ *
+ */
+typedef struct {
+ REGFIELD MC1BEN; /*!< Microphone bias 1 enable */
+ REGFIELD MC2BEN; /*!< Microphone bias 2 enable */
+ REGFIELD MC2BDETDBNC; /*!< Microphone bias detect debounce setting */
+ REGFIELD MC2BDETEN; /*!< Microphone bias 2 detect enable */
+ REGFIELD AMC1REN; /*!< Amplifier Amc1R enable */
+ REGFIELD AMC1RITOV; /*!< Amplifier Amc1R current to voltage mode enable */
+ REGFIELD AMC1LEN; /*!< Amplifier Amc1L enable */
+ REGFIELD AMC1LITOV; /*!< Amplifier Amc1L current to voltage mode enable */
+ REGFIELD AMC2EN; /*!< Amplifier Amc2 enable */
+ REGFIELD AMC2ITOV; /*!< Amplifier Amc2 current to voltage mode enable */
+ REGFIELD ATXINEN; /*!< Amplifier Atxin enable */
+ REGFIELD ATXOUTEN; /*!< Reserved for output TXOUT enable, currently not used */
+ REGFIELD RXINREC; /*!< RXINR/RXINL to voice CODEC ADC routing enable */
+ REGFIELD PGATXR; /*!< Transmit gain setting right - 5 bits */
+ REGFIELD PGATXL; /*!< Transmit gain setting left - 5 bits */
+} REGISTER_AUDIO_TX;
+
+/*!
+ * @brief This variable is used to access the AUDIO_TX hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * AUDIO_TX hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_AUDIO_TX regAUDIO_TX = {
+ {0, 0x000001}, /* MC1BEN */
+ {1, 0x000002}, /* MC2BEN */
+ {2, 0x000004}, /* MC2BDETDBNC */
+ {3, 0x000008}, /* MC2BDETEN */
+ {5, 0x000020}, /* AMC1REN */
+ {6, 0x000040}, /* AMC1RITOV */
+ {7, 0x000080}, /* AMC1LEN */
+ {8, 0x000100}, /* AMC1LITOV */
+ {9, 0x000200}, /* AMC2EN */
+ {10, 0x000400}, /* AMC2ITOV */
+ {11, 0x000800}, /* ATXINEN */
+ {12, 0x001000}, /* ATXOUTEN */
+ {13, 0x002000}, /* RXINREC */
+ {14, 0x07c000}, /* PGATXR */
+ {19, 0xf80000} /* PGATXL */
+};
+
+/*!
+ * @brief This structure lists all fields of the AUDIO_RX_0 hardware register.
+ */
+typedef struct {
+ REGFIELD VAUDIOON; /*!< Forces VAUDIO in active on mode */
+ REGFIELD BIASEN; /*!< Audio bias enable */
+ REGFIELD BIASSPEED; /*!< Turn on ramp speed of the audio bias */
+ REGFIELD ASPEN; /*!< Amplifier Asp enable */
+ REGFIELD ASPSEL; /*!< Asp input selector */
+ REGFIELD ALSPEN; /*!< Amplifier Alsp enable */
+ REGFIELD ALSPREF; /*!< Bias Alsp at common audio reference */
+ REGFIELD ALSPSEL; /*!< Alsp input selector */
+ REGFIELD LSPLEN; /*!< Output LSPL enable */
+ REGFIELD AHSREN; /*!< Amplifier AhsR enable */
+ REGFIELD AHSLEN; /*!< Amplifier AhsL enable */
+ REGFIELD AHSSEL; /*!< Ahsr and Ahsl input selector */
+ REGFIELD HSPGDIS; /*!< Phantom ground disable */
+ REGFIELD HSDETEN; /*!< Headset detect enable */
+ REGFIELD HSDETAUTOB; /*!< Amplifier state determined by headset detect */
+ REGFIELD ARXOUTREN; /*!< Output RXOUTR enable */
+ REGFIELD ARXOUTLEN; /*!< Output RXOUTL enable */
+ REGFIELD ARXOUTSEL; /*!< Arxout input selector */
+ REGFIELD CDCOUTEN; /*!< Output CDCOUT enable */
+ REGFIELD HSLDETEN; /*!< Headset left channel detect enable */
+ REGFIELD ADDCDC; /*!< Adder channel codec selection */
+ REGFIELD ADDSTDC; /*!< Adder channel stereo DAC selection */
+ REGFIELD ADDRXIN; /*!< Adder channel line in selection */
+} REGISTER_AUDIO_RX_0;
+
+/*!
+ * @brief This variable is used to access the AUDIO_RX_0 hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * AUDIO_RX_0 hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_AUDIO_RX_0 regAUDIO_RX_0 = {
+ {0, 0x000001}, /* VAUDIOON */
+ {1, 0x000002}, /* BIASEN */
+ {2, 0x000004}, /* BIASSPEED */
+ {3, 0x000008}, /* ASPEN */
+ {4, 0x000010}, /* ASPSEL */
+ {5, 0x000020}, /* ALSPEN */
+ {6, 0x000040}, /* ALSPREF */
+ {7, 0x000080}, /* ALSPSEL */
+ {8, 0x000100}, /* LSPLEN */
+ {9, 0x000200}, /* AHSREN */
+ {10, 0x000400}, /* AHSLEN */
+ {11, 0x000800}, /* AHSSEL */
+ {12, 0x001000}, /* HSPGDIS */
+ {13, 0x002000}, /* HSDETEN */
+ {14, 0x004000}, /* HSDETAUTOB */
+ {15, 0x008000}, /* ARXOUTREN */
+ {16, 0x010000}, /* ARXOUTLEN */
+ {17, 0x020000}, /* ARXOUTSEL */
+ {18, 0x040000}, /* CDCOUTEN */
+ {19, 0x080000}, /* HSLDETEN */
+ {21, 0x200000}, /* ADDCDC */
+ {22, 0x400000}, /* ADDSTDC */
+ {23, 0x800000} /* ADDRXIN */
+};
+
+/*!
+ * @brief This structure lists all fields of the AUDIO_RX_1 hardware register.
+ */
+typedef struct {
+ REGFIELD PGARXEN; /*!< Codec receive PGA enable */
+ REGFIELD PGARX; /*!< Codec receive gain setting - 4 bits */
+ REGFIELD PGASTEN; /*!< Stereo DAC PGA enable */
+ REGFIELD PGAST; /*!< Stereo DAC gain setting - 4 bits */
+ REGFIELD ARXINEN; /*!< Amplifier Arx enable */
+ REGFIELD ARXIN; /*!< Amplifier Arx additional gain setting */
+ REGFIELD PGARXIN; /*!< PGArxin gain setting - 4 bits */
+ REGFIELD MONO; /*!< Mono adder setting - 2 bits */
+ REGFIELD BAL; /*!< Balance control - 3 bits */
+ REGFIELD BALLR; /*!< Left / right balance */
+} REGISTER_AUDIO_RX_1;
+
+/*!
+ * @brief This variable is used to access the AUDIO_RX_1 hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * AUDIO_RX_1 hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const REGISTER_AUDIO_RX_1 regAUDIO_RX_1 = {
+ {0, 0x000001}, /* PGARXEN */
+ {1, 0x00001e}, /* PGARX */
+ {5, 0x000020}, /* PGASTEN */
+ {6, 0x0003c0}, /* PGAST */
+ {10, 0x000400}, /* ARXINEN */
+ {11, 0x000800}, /* ARXIN */
+ {12, 0x00f000}, /* PGARXIN */
+ {16, 0x030000}, /* MONO */
+ {18, 0x1c0000}, /* BAL */
+ {21, 0x200000} /* BALLR */
+};
+
+/*! Define a mask to access the entire hardware register. */
+static const unsigned int REG_FULLMASK = 0xffffff;
+
+/*! Reset value for the AUD_CODEC register. */
+static const unsigned int RESET_AUD_CODEC = 0x180027;
+
+/*! Reset value for the ST_DAC register.
+ *
+ * Note that we avoid resetting any of the arbitration bits.
+ */
+static const unsigned int RESET_ST_DAC = 0x0E0004;
+
+/*! Reset value for the SSI_NETWORK register. */
+static const unsigned int RESET_SSI_NETWORK = 0x013060;
+
+/*! Reset value for the AUDIO_TX register.
+ *
+ * Note that we avoid resetting any of the arbitration bits.
+ */
+static const unsigned int RESET_AUDIO_TX = 0x420000;
+
+/*! Reset value for the AUDIO_RX_0 register. */
+static const unsigned int RESET_AUDIO_RX_0 = 0x001000;
+
+/*! Reset value for the AUDIO_RX_1 register. */
+static const unsigned int RESET_AUDIO_RX_1 = 0x00D35A;
+
+/*! Reset mask for the SSI network Vcodec part. first 12 bits
+ * 0 - 11 */
+static const unsigned int REG_SSI_VCODEC_MASK = 0x000fff;
+
+/*! Reset mask for the SSI network STDAC part. last 12 bits
+ * 12 - 24 */
+static const unsigned int REG_SSI_STDAC_MASK = 0xfff000;
+
+/*! Constant NULL value for initializing/reseting the audio handles. */
+static const PMIC_AUDIO_HANDLE AUDIO_HANDLE_NULL = (PMIC_AUDIO_HANDLE) NULL;
+
+/*!
+ * @brief This structure maintains the current state of the Stereo DAC.
+ */
+typedef struct {
+ PMIC_AUDIO_HANDLE handle; /*!< Handle used to access
+ the Stereo DAC. */
+ HANDLE_STATE handleState; /*!< Current handle state. */
+ PMIC_AUDIO_DATA_BUS busID; /*!< Data bus used to access
+ the Stereo DAC. */
+ bool protocol_set;
+ PMIC_AUDIO_BUS_PROTOCOL protocol; /*!< Data bus protocol. */
+ PMIC_AUDIO_BUS_MODE masterSlave; /*!< Master/Slave mode
+ select. */
+ PMIC_AUDIO_NUMSLOTS numSlots; /*!< Number of timeslots
+ used. */
+ PMIC_AUDIO_CALLBACK callback; /*!< Event notification
+ callback function
+ pointer. */
+ PMIC_AUDIO_EVENTS eventMask; /*!< Event notification mask. */
+ PMIC_AUDIO_CLOCK_IN_SOURCE clockIn; /*!< Stereo DAC clock input
+ source select. */
+ PMIC_AUDIO_STDAC_SAMPLING_RATE samplingRate; /*!< Stereo DAC sampling rate
+ select. */
+ PMIC_AUDIO_STDAC_CLOCK_IN_FREQ clockFreq; /*!< Stereo DAC clock input
+ frequency. */
+ PMIC_AUDIO_CLOCK_INVERT invert; /*!< Stereo DAC clock signal
+ invert select. */
+ PMIC_AUDIO_STDAC_TIMESLOTS timeslot; /*!< Stereo DAC data
+ timeslots select. */
+ PMIC_AUDIO_STDAC_CONFIG config; /*!< Stereo DAC configuration
+ options. */
+} PMIC_AUDIO_STDAC_STATE;
+
+/*!
+ * @brief This variable maintains the current state of the Stereo DAC.
+ *
+ * This variable tracks the current state of the Stereo DAC audio hardware
+ * along with any information that is required by the device driver to
+ * manage the hardware (e.g., callback functions and event notification
+ * masks).
+ *
+ * The initial values represent the reset/power on state of the Stereo DAC.
+ */
+static PMIC_AUDIO_STDAC_STATE stDAC = {
+ (PMIC_AUDIO_HANDLE) NULL, /* handle */
+ HANDLE_FREE, /* handleState */
+ AUDIO_DATA_BUS_1, /* busID */
+ false,
+ NORMAL_MSB_JUSTIFIED_MODE, /* protocol */
+ BUS_MASTER_MODE, /* masterSlave */
+ USE_2_TIMESLOTS, /* numSlots */
+ (PMIC_AUDIO_CALLBACK) NULL, /* callback */
+ (PMIC_AUDIO_EVENTS) NULL, /* eventMask */
+ CLOCK_IN_CLIA, /* clockIn */
+ STDAC_RATE_44_1_KHZ, /* samplingRate */
+ STDAC_CLI_13MHZ, /* clockFreq */
+ NO_INVERT, /* invert */
+ USE_TS0_TS1, /* timeslot */
+ (PMIC_AUDIO_STDAC_CONFIG) 0 /* config */
+};
+
+/*!
+ * @brief This structure maintains the current state of the Voice CODEC.
+ */
+typedef struct {
+ PMIC_AUDIO_HANDLE handle; /*!< Handle used to access
+ the Voice CODEC. */
+ HANDLE_STATE handleState; /*!< Current handle state. */
+ PMIC_AUDIO_DATA_BUS busID; /*!< Data bus used to access
+ the Voice CODEC. */
+ bool protocol_set;
+ PMIC_AUDIO_BUS_PROTOCOL protocol; /*!< Data bus protocol. */
+ PMIC_AUDIO_BUS_MODE masterSlave; /*!< Master/Slave mode
+ select. */
+ PMIC_AUDIO_NUMSLOTS numSlots; /*!< Number of timeslots
+ used. */
+ PMIC_AUDIO_CALLBACK callback; /*!< Event notification
+ callback function
+ pointer. */
+ PMIC_AUDIO_EVENTS eventMask; /*!< Event notification
+ mask. */
+ PMIC_AUDIO_CLOCK_IN_SOURCE clockIn; /*!< Voice CODEC clock input
+ source select. */
+ PMIC_AUDIO_VCODEC_SAMPLING_RATE samplingRate; /*!< Voice CODEC sampling
+ rate select. */
+ PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ clockFreq; /*!< Voice CODEC clock input
+ frequency. */
+ PMIC_AUDIO_CLOCK_INVERT invert; /*!< Voice CODEC clock
+ signal invert select. */
+ PMIC_AUDIO_VCODEC_TIMESLOT timeslot; /*!< Voice CODEC data
+ timeslot select. */
+ PMIC_AUDIO_VCODEC_TIMESLOT secondaryTXtimeslot;
+
+ PMIC_AUDIO_VCODEC_CONFIG config; /*!< Voice CODEC
+ configuration
+ options. */
+ PMIC_MICROPHONE_STATE leftChannelMic; /*!< Left channel
+ microphone
+ configuration. */
+ PMIC_MICROPHONE_STATE rightChannelMic; /*!< Right channel
+ microphone
+ configuration. */
+} PMIC_AUDIO_VCODEC_STATE;
+
+/*!
+ * @brief This variable maintains the current state of the Voice CODEC.
+ *
+ * This variable tracks the current state of the Voice CODEC audio hardware
+ * along with any information that is required by the device driver to
+ * manage the hardware (e.g., callback functions and event notification
+ * masks).
+ *
+ * The initial values represent the reset/power on state of the Voice CODEC.
+ */
+static PMIC_AUDIO_VCODEC_STATE vCodec = {
+ (PMIC_AUDIO_HANDLE) NULL, /* handle */
+ HANDLE_FREE, /* handleState */
+ AUDIO_DATA_BUS_2, /* busID */
+ false,
+ NETWORK_MODE, /* protocol */
+ BUS_SLAVE_MODE, /* masterSlave */
+ USE_4_TIMESLOTS, /* numSlots */
+ (PMIC_AUDIO_CALLBACK) NULL, /* callback */
+ (PMIC_AUDIO_EVENTS) NULL, /* eventMask */
+ CLOCK_IN_CLIB, /* clockIn */
+ VCODEC_RATE_8_KHZ, /* samplingRate */
+ VCODEC_CLI_13MHZ, /* clockFreq */
+ NO_INVERT, /* invert */
+ USE_TS0, /* timeslot pri */
+ USE_TS2, /* timeslot sec TX */
+ INPUT_HIGHPASS_FILTER | OUTPUT_HIGHPASS_FILTER, /* config */
+ /* leftChannelMic */
+ {NO_MIC, /* mic */
+ MICROPHONE_OFF, /* micOnOff */
+ AMP_OFF, /* ampMode */
+ MIC_GAIN_0DB /* gain */
+ },
+ /* rightChannelMic */
+ {NO_MIC, /* mic */
+ MICROPHONE_OFF, /* micOnOff */
+ AMP_OFF, /* ampMode */
+ MIC_GAIN_0DB /* gain */
+ }
+};
+
+/*!
+ * @brief This maintains the current state of the External Stereo Input.
+ */
+typedef struct {
+ PMIC_AUDIO_HANDLE handle; /*!< Handle used to access the
+ External Stereo Inputs. */
+ HANDLE_STATE handleState; /*!< Current handle state. */
+ PMIC_AUDIO_CALLBACK callback; /*!< Event notification callback
+ function pointer. */
+ PMIC_AUDIO_EVENTS eventMask; /*!< Event notification mask. */
+ PMIC_AUDIO_STEREO_IN_GAIN inputGain; /*!< External Stereo Input
+ amplifier gain level. */
+} PMIC_AUDIO_EXT_STEREO_IN_STATE;
+
+/*!
+ * @brief This maintains the current state of the External Stereo Input.
+ *
+ * This variable tracks the current state of the External Stereo Input audio
+ * hardware along with any information that is required by the device driver
+ * to manage the hardware (e.g., callback functions and event notification
+ * masks).
+ *
+ * The initial values represent the reset/power on state of the External
+ * Stereo Input.
+ */
+static PMIC_AUDIO_EXT_STEREO_IN_STATE extStereoIn = {
+ (PMIC_AUDIO_HANDLE) NULL, /* handle */
+ HANDLE_FREE, /* handleState */
+ (PMIC_AUDIO_CALLBACK) NULL, /* callback */
+ (PMIC_AUDIO_EVENTS) NULL, /* eventMask */
+ STEREO_IN_GAIN_0DB /* inputGain */
+};
+
+/*!
+ * @brief This maintains the current state of the callback & Eventmask.
+ */
+typedef struct {
+ PMIC_AUDIO_CALLBACK callback; /*!< Event notification callback
+ function pointer. */
+ PMIC_AUDIO_EVENTS eventMask; /*!< Event notification mask. */
+} PMIC_AUDIO_EVENT_STATE;
+
+static PMIC_AUDIO_EVENT_STATE event_state = {
+ (PMIC_AUDIO_CALLBACK) NULL, /*Callback */
+ (PMIC_AUDIO_EVENTS) NULL, /* EventMask */
+
+};
+
+/*!
+ * @brief This maintains the current state of the Audio Output Section.
+ */
+typedef struct {
+ PMIC_AUDIO_OUTPUT_PORT outputPort; /*!< Current audio
+ output port. */
+ PMIC_AUDIO_OUTPUT_PGA_GAIN vCodecoutputPGAGain; /*!< Output PGA gain
+ level codec */
+ PMIC_AUDIO_OUTPUT_PGA_GAIN stDacoutputPGAGain; /*!< Output PGA gain
+ level stDAC */
+ PMIC_AUDIO_OUTPUT_PGA_GAIN extStereooutputPGAGain; /*!< Output PGA gain
+ level stereo ext */
+ PMIC_AUDIO_OUTPUT_BALANCE_GAIN balanceLeftGain; /*!< Left channel
+ balance gain
+ level. */
+ PMIC_AUDIO_OUTPUT_BALANCE_GAIN balanceRightGain; /*!< Right channel
+ balance gain
+ level. */
+ PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN monoAdderGain; /*!< Mono adder gain
+ level. */
+ PMIC_AUDIO_OUTPUT_CONFIG config; /*!< Audio output
+ section config
+ options. */
+ PMIC_AUDIO_VCODEC_OUTPUT_PATH vCodecOut;
+
+} PMIC_AUDIO_AUDIO_OUTPUT_STATE;
+
+/*!
+ * @brief This variable maintains the current state of the Audio Output Section.
+ *
+ * This variable tracks the current state of the Audio Output Section.
+ *
+ * The initial values represent the reset/power on state of the Audio
+ * Output Section.
+ */
+static PMIC_AUDIO_AUDIO_OUTPUT_STATE audioOutput = {
+ (PMIC_AUDIO_OUTPUT_PORT) NULL, /* outputPort */
+ OUTPGA_GAIN_0DB, /* outputPGAGain */
+ OUTPGA_GAIN_0DB, /* outputPGAGain */
+ OUTPGA_GAIN_0DB, /* outputPGAGain */
+ BAL_GAIN_0DB, /* balanceLeftGain */
+ BAL_GAIN_0DB, /* balanceRightGain */
+ MONOADD_GAIN_0DB, /* monoAdderGain */
+ (PMIC_AUDIO_OUTPUT_CONFIG) 0, /* config */
+ VCODEC_DIRECT_OUT
+};
+
+/*! The current headset status. */
+static HEADSET_STATUS headsetState = NO_HEADSET;
+
+/* Removed PTT variable */
+/*! Define a 1 ms wait interval that is needed to ensure that certain
+ * hardware operations are successfully completed.
+ */
+static const unsigned long delay_1ms = (HZ / 1000);
+
+/*!
+ * @brief This spinlock is used to provide mutual exclusion.
+ *
+ * Create a spinlock that can be used to provide mutually exclusive
+ * read/write access to the globally accessible data structures
+ * that were defined above. Mutually exclusive access is required to
+ * ensure that the audio data structures are consistent at all times
+ * when possibly accessed by multiple threads of execution (for example,
+ * while simultaneously handling a user request and an interrupt event).
+ *
+ * We need to use a spinlock whenever we do need to provide mutual
+ * exclusion while possibly executing in a hardware interrupt context.
+ * Spinlocks should be held for the minimum time that is necessary
+ * because hardware interrupts are disabled while a spinlock is held.
+ *
+ */
+
+static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+/*!
+ * @brief This mutex is used to provide mutual exclusion.
+ *
+ * Create a mutex that can be used to provide mutually exclusive
+ * read/write access to the globally accessible data structures
+ * that were defined above. Mutually exclusive access is required to
+ * ensure that the audio data structures are consistent at all times
+ * when possibly accessed by multiple threads of execution.
+ *
+ * Note that we use a mutex instead of the spinlock whenever disabling
+ * interrupts while in the critical section is not required. This helps
+ * to minimize kernel interrupt handling latency.
+ */
+static DECLARE_MUTEX(mutex);
+
+/*!
+ * @brief Global variable to track currently active interrupt events.
+ *
+ * This global variable is used to keep track of all of the currently
+ * active interrupt events for the audio driver. Note that access to this
+ * variable may occur while within an interrupt context and, therefore,
+ * must be guarded by using a spinlock.
+ */
+/* static PMIC_CORE_EVENT eventID = 0; */
+
+/* Prototypes for all static audio driver functions. */
+/*
+static PMIC_STATUS pmic_audio_mic_boost_enable(void);
+static PMIC_STATUS pmic_audio_mic_boost_disable(void);*/
+static PMIC_STATUS pmic_audio_close_handle(const PMIC_AUDIO_HANDLE handle);
+static PMIC_STATUS pmic_audio_reset_device(const PMIC_AUDIO_HANDLE handle);
+
+static PMIC_STATUS pmic_audio_deregister(void *callback,
+ PMIC_AUDIO_EVENTS * const eventMask);
+
+/*************************************************************************
+ * Audio device access APIs.
+ *************************************************************************
+ */
+
+/*!
+ * @name General Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Audio
+ * hardware.
+ */
+/*@{*/
+
+PMIC_STATUS pmic_audio_set_autodetect(int val)
+{
+ PMIC_STATUS status;
+ unsigned int reg_mask = 0, reg_write = 0;
+ reg_mask = SET_BITS(regAUDIO_RX_0, VAUDIOON, 1);
+ status = pmic_write_reg(REG_AUDIO_RX_0, reg_mask, reg_mask);
+ if (status != PMIC_SUCCESS)
+ return status;
+ reg_mask = 0;
+ if (val == 1) {
+ reg_write = SET_BITS(regAUDIO_RX_0, HSDETEN, 1) |
+ SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+ } else {
+ reg_write = 0;
+ }
+ reg_mask =
+ SET_BITS(regAUDIO_RX_0, HSDETEN, 1) | SET_BITS(regAUDIO_RX_0,
+ HSDETAUTOB, 1);
+ status = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
+
+ return status;
+}
+
+/*!
+ * @brief Request exclusive access to the PMIC Audio hardware.
+ *
+ * Attempt to open and gain exclusive access to a key PMIC audio hardware
+ * component (e.g., the Stereo DAC or the Voice CODEC). Depending upon the
+ * type of audio operation that is desired and the nature of the audio data
+ * stream, the Stereo DAC and/or the Voice CODEC will be a required hardware
+ * component and needs to be acquired by calling this function.
+ *
+ * If the open request is successful, then a numeric handle is returned
+ * and this handle must be used in all subsequent function calls to complete
+ * the configuration of either the Stereo DAC or the Voice CODEC and along
+ * with any other associated audio hardware components that will be needed.
+ *
+ * The same handle must also be used in the close call when use of the PMIC
+ * audio hardware is no longer required.
+ *
+ * The open request will fail if the requested audio hardware component has
+ * already been acquired by a previous open call but not yet closed.
+ *
+ * @param handle Device handle to be used for subsequent PMIC
+ * audio API calls.
+ * @param device The required PMIC audio hardware component.
+ *
+ * @retval PMIC_SUCCESS If the open request was successful
+ * @retval PMIC_PARAMETER_ERROR If the handle argument is NULL.
+ * @retval PMIC_ERROR If the audio hardware component is
+ * unavailable.
+ */
+PMIC_STATUS pmic_audio_open(PMIC_AUDIO_HANDLE * const handle,
+ const PMIC_AUDIO_SOURCE device)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ if (handle == (PMIC_AUDIO_HANDLE *) NULL) {
+ /* Do not dereference a NULL pointer. */
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ /* We only need to acquire a mutex here because the interrupt handler
+ * never modifies the device handle or device handle state. Therefore,
+ * we don't need to worry about conflicts with the interrupt handler
+ * or the need to execute in an interrupt context.
+ *
+ * But we do need a critical section here to avoid problems in case
+ * multiple calls to pmic_audio_open() are made since we can only allow
+ * one of them to succeed.
+ */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ /* Check the current device handle state and acquire the handle if
+ * it is available.
+ */
+
+ if ((device == STEREO_DAC) && (stDAC.handleState == HANDLE_FREE)) {
+ stDAC.handle = (PMIC_AUDIO_HANDLE) (&stDAC);
+ stDAC.handleState = HANDLE_IN_USE;
+ *handle = stDAC.handle;
+ rc = PMIC_SUCCESS;
+ } else if ((device == VOICE_CODEC)
+ && (vCodec.handleState == HANDLE_FREE)) {
+ vCodec.handle = (PMIC_AUDIO_HANDLE) (&vCodec);
+ vCodec.handleState = HANDLE_IN_USE;
+ *handle = vCodec.handle;
+ rc = PMIC_SUCCESS;
+ } else if ((device == EXTERNAL_STEREO_IN) &&
+ (extStereoIn.handleState == HANDLE_FREE)) {
+ extStereoIn.handle = (PMIC_AUDIO_HANDLE) (&extStereoIn);
+ extStereoIn.handleState = HANDLE_IN_USE;
+ *handle = extStereoIn.handle;
+ rc = PMIC_SUCCESS;
+ } else {
+ *handle = AUDIO_HANDLE_NULL;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Terminate further access to the PMIC audio hardware.
+ *
+ * Terminate further access to the PMIC audio hardware that was previously
+ * acquired by calling pmic_audio_open(). This now allows another thread to
+ * successfully call pmic_audio_open() to gain access.
+ *
+ * Note that we will shutdown/reset the Voice CODEC or Stereo DAC as well as
+ * any associated audio input/output components that are no longer required.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the close request was successful.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_close(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* We need a critical section here to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ /* We can now call pmic_audio_close_handle() to actually do the work. */
+ rc = pmic_audio_close_handle(handle);
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Configure the data bus protocol to be used.
+ *
+ * Provide the parameters needed to properly configure the audio data bus
+ * protocol so that data can be read/written to either the Stereo DAC or
+ * the Voice CODEC.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param busID Select data bus to be used.
+ * @param protocol Select the data bus protocol.
+ * @param masterSlave Select the data bus timing mode.
+ * @param numSlots Define the number of timeslots (only if in
+ * master mode).
+ *
+ * @retval PMIC_SUCCESS If the protocol was successful configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the protocol parameters
+ * are invalid.
+ */
+PMIC_STATUS pmic_audio_set_protocol(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_DATA_BUS busID,
+ const PMIC_AUDIO_BUS_PROTOCOL protocol,
+ const PMIC_AUDIO_BUS_MODE masterSlave,
+ const PMIC_AUDIO_NUMSLOTS numSlots)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ const unsigned int ST_DAC_MASK = SET_BITS(regST_DAC, STDCSSISEL, 1) |
+ SET_BITS(regST_DAC, STDCFS, 3) | SET_BITS(regST_DAC, STDCSM, 1);
+
+ unsigned int reg_mask;
+ /*unsigned int VCODEC_MASK = SET_BITS(regAUD_CODEC, CDCSSISEL, 1) |
+ SET_BITS(regAUD_CODEC, CDCFS, 3) | SET_BITS(regAUD_CODEC, CDCSM, 1); */
+
+ unsigned int SSI_NW_MASK = SET_BITS(regSSI_NETWORK, STDCSLOTS, 1);
+ unsigned int reg_value = 0;
+ unsigned int ssi_nw_value = 0;
+
+ /* Enter a critical section so that we can ensure only one
+ * state change request is completed at a time.
+ */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if (handle == (PMIC_AUDIO_HANDLE) NULL) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else {
+ if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) {
+ if ((stDAC.handleState == HANDLE_IN_USE) &&
+ (stDAC.busID == busID) && (stDAC.protocol_set)) {
+ pr_debug("The requested bus already in USE\n");
+ rc = PMIC_PARAMETER_ERROR;
+ } else if ((masterSlave == BUS_MASTER_MODE)
+ && (numSlots != USE_4_TIMESLOTS)) {
+ pr_debug
+ ("mc13783 supports only 4 slots in Master mode\n");
+ rc = PMIC_NOT_SUPPORTED;
+ } else if ((masterSlave == BUS_SLAVE_MODE)
+ && (numSlots != USE_4_TIMESLOTS)) {
+ pr_debug
+ ("Driver currently supports only 4 slots in Slave mode\n");
+ rc = PMIC_NOT_SUPPORTED;
+ } else if (!((protocol == NETWORK_MODE) ||
+ (protocol == I2S_MODE))) {
+ pr_debug
+ ("mc13783 Voice codec works only in Network and I2S modes\n");
+ rc = PMIC_NOT_SUPPORTED;
+ } else {
+ pr_debug
+ ("Proceeding to configure Voice Codec\n");
+ if (busID == AUDIO_DATA_BUS_1) {
+ reg_value =
+ SET_BITS(regAUD_CODEC, CDCSSISEL,
+ 0);
+ } else {
+ reg_value =
+ SET_BITS(regAUD_CODEC, CDCSSISEL,
+ 1);
+ }
+ reg_mask = SET_BITS(regAUD_CODEC, CDCSSISEL, 1);
+ if (PMIC_SUCCESS !=
+ pmic_write_reg(REG_AUDIO_CODEC,
+ reg_value, reg_mask))
+ return PMIC_ERROR;
+
+ if (masterSlave == BUS_MASTER_MODE) {
+ reg_value =
+ SET_BITS(regAUD_CODEC, CDCSM, 0);
+ } else {
+ reg_value =
+ SET_BITS(regAUD_CODEC, CDCSM, 1);
+ }
+ reg_mask = SET_BITS(regAUD_CODEC, CDCSM, 1);
+ if (PMIC_SUCCESS !=
+ pmic_write_reg(REG_AUDIO_CODEC,
+ reg_value, reg_mask))
+ return PMIC_ERROR;
+
+ if (protocol == NETWORK_MODE) {
+ reg_value =
+ SET_BITS(regAUD_CODEC, CDCFS, 1);
+ } else { /* protocol == I2S, other options have been already eliminated */
+ reg_value =
+ SET_BITS(regAUD_CODEC, CDCFS, 2);
+ }
+ reg_mask = SET_BITS(regAUD_CODEC, CDCFS, 3);
+ if (PMIC_SUCCESS !=
+ pmic_write_reg(REG_AUDIO_CODEC,
+ reg_value, reg_mask))
+ return PMIC_ERROR;
+
+ ssi_nw_value =
+ SET_BITS(regSSI_NETWORK, CDCFSDLY, 1);
+ /*if (pmic_write_reg
+ (REG_AUDIO_CODEC, reg_value,
+ VCODEC_MASK) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else { */
+ vCodec.busID = busID;
+ vCodec.protocol = protocol;
+ vCodec.masterSlave = masterSlave;
+ vCodec.numSlots = numSlots;
+ vCodec.protocol_set = true;
+ //pmic_write_reg(REG_AUDIO_SSI_NETWORK, ssi_nw_value, ssi_nw_value);
+
+ pr_debug
+ ("mc13783 Voice codec successfully configured\n");
+ rc = PMIC_SUCCESS;
+ //}
+
+ }
+
+ } else if ((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) {
+ if ((vCodec.handleState == HANDLE_IN_USE) &&
+ (vCodec.busID == busID) && (vCodec.protocol_set)) {
+ pr_debug("The requested bus already in USE\n");
+ rc = PMIC_PARAMETER_ERROR;
+ } else if (((protocol == NORMAL_MSB_JUSTIFIED_MODE) ||
+ (protocol == I2S_MODE))
+ && (numSlots != USE_2_TIMESLOTS)) {
+ pr_debug
+ ("STDAC uses only 2 slots in Normal and I2S modes\n");
+ rc = PMIC_PARAMETER_ERROR;
+ } else if ((protocol == NETWORK_MODE) &&
+ !((numSlots == USE_2_TIMESLOTS) ||
+ (numSlots == USE_4_TIMESLOTS) ||
+ (numSlots == USE_8_TIMESLOTS))) {
+ pr_debug
+ ("STDAC uses only 2,4 or 8 slots in Network mode\n");
+ rc = PMIC_PARAMETER_ERROR;
+ } else if (protocol == SPD_IF_MODE) {
+ pr_debug
+ ("STDAC driver currently does not support SPD IF mode\n");
+ rc = PMIC_NOT_SUPPORTED;
+ } else {
+ pr_debug
+ ("Proceeding to configure Stereo DAC\n");
+ if (busID == AUDIO_DATA_BUS_1) {
+ reg_value =
+ SET_BITS(regST_DAC, STDCSSISEL, 0);
+ } else {
+ reg_value =
+ SET_BITS(regST_DAC, STDCSSISEL, 1);
+ }
+ if (masterSlave == BUS_MASTER_MODE) {
+ reg_value |=
+ SET_BITS(regST_DAC, STDCSM, 0);
+ } else {
+ reg_value |=
+ SET_BITS(regST_DAC, STDCSM, 1);
+ }
+ if (protocol == NETWORK_MODE) {
+ reg_value |=
+ SET_BITS(regST_DAC, STDCFS, 1);
+ } else if (protocol ==
+ NORMAL_MSB_JUSTIFIED_MODE) {
+ reg_value |=
+ SET_BITS(regST_DAC, STDCFS, 0);
+ } else { /* I2S mode as the other option has already been eliminated */
+ reg_value |=
+ SET_BITS(regST_DAC, STDCFS, 2);
+ }
+
+ if (pmic_write_reg
+ (REG_AUDIO_STEREO_DAC,
+ reg_value, ST_DAC_MASK) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ if (numSlots == USE_2_TIMESLOTS) {
+ reg_value =
+ SET_BITS(regSSI_NETWORK,
+ STDCSLOTS, 3);
+ } else if (numSlots == USE_4_TIMESLOTS) {
+ reg_value =
+ SET_BITS(regSSI_NETWORK,
+ STDCSLOTS, 2);
+ } else { /* Use 8 timeslots - L , R and 6 other */
+ reg_value =
+ SET_BITS(regSSI_NETWORK,
+ STDCSLOTS, 1);
+ }
+ if (pmic_write_reg
+ (REG_AUDIO_SSI_NETWORK,
+ reg_value,
+ SSI_NW_MASK) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ stDAC.busID = busID;
+ stDAC.protocol = protocol;
+ stDAC.protocol_set = true;
+ stDAC.masterSlave = masterSlave;
+ stDAC.numSlots = numSlots;
+ pr_debug
+ ("mc13783 Stereo DAC successfully configured\n");
+ rc = PMIC_SUCCESS;
+ }
+ }
+
+ }
+ } else {
+ rc = PMIC_PARAMETER_ERROR;
+ /* Handle can only be Voice Codec or Stereo DAC */
+ pr_debug("Handles only STDAC and VCODEC\n");
+ }
+
+ }
+ /* Exit critical section. */
+ up(&mutex);
+ return rc;
+}
+
+/*!
+ * @brief Retrieve the current data bus protocol configuration.
+ *
+ * Retrieve the parameters that define the current audio data bus protocol.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param busID The data bus being used.
+ * @param protocol The data bus protocol being used.
+ * @param masterSlave The data bus timing mode being used.
+ * @param numSlots The number of timeslots being used (if in
+ * master mode).
+ *
+ * @retval PMIC_SUCCESS If the protocol was successful retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_get_protocol(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_DATA_BUS * const busID,
+ PMIC_AUDIO_BUS_PROTOCOL * const protocol,
+ PMIC_AUDIO_BUS_MODE * const masterSlave,
+ PMIC_AUDIO_NUMSLOTS * const numSlots)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ if ((busID != (PMIC_AUDIO_DATA_BUS *) NULL) &&
+ (protocol != (PMIC_AUDIO_BUS_PROTOCOL *) NULL) &&
+ (masterSlave != (PMIC_AUDIO_BUS_MODE *) NULL) &&
+ (numSlots != (PMIC_AUDIO_NUMSLOTS *) NULL)) {
+ /* Enter a critical section so that we return a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) {
+ *busID = stDAC.busID;
+ *protocol = stDAC.protocol;
+ *masterSlave = stDAC.masterSlave;
+ *numSlots = stDAC.numSlots;
+ rc = PMIC_SUCCESS;
+ } else if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) {
+ *busID = vCodec.busID;
+ *protocol = vCodec.protocol;
+ *masterSlave = vCodec.masterSlave;
+ *numSlots = vCodec.numSlots;
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit critical section. */
+ up(&mutex);
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief Enable the Stereo DAC or the Voice CODEC.
+ *
+ * Explicitly enable the Stereo DAC or the Voice CODEC to begin audio
+ * playback or recording as required. This should only be done after
+ * successfully configuring all of the associated audio components (e.g.,
+ * microphones, amplifiers, etc.).
+ *
+ * Note that the timed delays used in this function are necessary to
+ * ensure reliable operation of the Voice CODEC and Stereo DAC. The
+ * Stereo DAC seems to be particularly sensitive and it has been observed
+ * to fail to generate the required master mode clock signals if it is
+ * not allowed enough time to initialize properly.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the device was successful enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_ERROR If the device could not be enabled.
+ */
+PMIC_STATUS pmic_audio_enable(const PMIC_AUDIO_HANDLE handle)
+{
+ const unsigned int AUDIO_BIAS_ENABLE = SET_BITS(regAUDIO_RX_0,
+ VAUDIOON, 1);
+ const unsigned int STDAC_ENABLE = SET_BITS(regST_DAC, STDCEN, 1);
+ const unsigned int VCODEC_ENABLE = SET_BITS(regAUD_CODEC, CDCEN, 1);
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+ pmic_write_reg(REG_AUDIO_RX_0, AUDIO_BIAS_ENABLE,
+ AUDIO_BIAS_ENABLE);
+ reg_mask =
+ SET_BITS(regAUDIO_RX_0, HSDETEN,
+ 1) | SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+ reg_write =
+ SET_BITS(regAUDIO_RX_0, HSDETEN,
+ 1) | SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+ rc = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
+ if (rc == PMIC_SUCCESS)
+ pr_debug("pmic_audio_enable\n");
+ /* We can enable the Stereo DAC. */
+ rc = pmic_write_reg(REG_AUDIO_STEREO_DAC,
+ STDAC_ENABLE, STDAC_ENABLE);
+ /*pmic_read_reg(REG_AUDIO_STEREO_DAC, &reg_value); */
+ if (rc != PMIC_SUCCESS) {
+ pr_debug("Failed to enable the Stereo DAC\n");
+ rc = PMIC_ERROR;
+ }
+ } else if ((handle == vCodec.handle)
+ && (vCodec.handleState == HANDLE_IN_USE)) {
+ /* Must first set the audio bias bit to power up the audio circuits. */
+ pmic_write_reg(REG_AUDIO_RX_0, AUDIO_BIAS_ENABLE,
+ AUDIO_BIAS_ENABLE);
+ reg_mask = SET_BITS(regAUDIO_RX_0, HSDETEN, 1) |
+ SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+ reg_write = SET_BITS(regAUDIO_RX_0, HSDETEN, 1) |
+ SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+ rc = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
+
+ /* Then we can enable the Voice CODEC. */
+ rc = pmic_write_reg(REG_AUDIO_CODEC, VCODEC_ENABLE,
+ VCODEC_ENABLE);
+
+ /* pmic_read_reg(REG_AUDIO_CODEC, &reg_value); */
+ if (rc != PMIC_SUCCESS) {
+ pr_debug("Failed to enable the Voice codec\n");
+ rc = PMIC_ERROR;
+ }
+ }
+ /* Exit critical section. */
+ up(&mutex);
+ return rc;
+}
+
+/*!
+ * @brief Disable the Stereo DAC or the Voice CODEC.
+ *
+ * Explicitly disable the Stereo DAC or the Voice CODEC to end audio
+ * playback or recording as required.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the device was successful disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_ERROR If the device could not be disabled.
+ */
+PMIC_STATUS pmic_audio_disable(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ const unsigned int STDAC_DISABLE = SET_BITS(regST_DAC, STDCEN, 1);
+ const unsigned int VCODEC_DISABLE = SET_BITS(regAUD_CODEC, CDCEN, 1);
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+ if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+ rc = pmic_write_reg(REG_AUDIO_STEREO_DAC, 0, STDAC_DISABLE);
+ } else if ((handle == vCodec.handle)
+ && (vCodec.handleState == HANDLE_IN_USE)) {
+ rc = pmic_write_reg(REG_AUDIO_CODEC, 0, VCODEC_DISABLE);
+ }
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Disabled successfully\n");
+ }
+ /* Exit critical section. */
+ up(&mutex);
+ return rc;
+}
+
+/*!
+ * @brief Reset the selected audio hardware control registers to their
+ * power on state.
+ *
+ * This resets all of the audio hardware control registers currently
+ * associated with the device handle back to their power on states. For
+ * example, if the handle is associated with the Stereo DAC and a
+ * specific output port and output amplifiers, then this function will
+ * reset all of those components to their initial power on state.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the reset operation was successful.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_ERROR If the reset was unsuccessful.
+ */
+PMIC_STATUS pmic_audio_reset(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ rc = pmic_audio_reset_device(handle);
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Reset all audio hardware control registers to their power on state.
+ *
+ * This resets all of the audio hardware control registers back to their
+ * power on states. Use this function with care since it also invalidates
+ * (i.e., automatically closes) all currently opened device handles.
+ *
+ * @retval PMIC_SUCCESS If the reset operation was successful.
+ * @retval PMIC_ERROR If the reset was unsuccessful.
+ */
+PMIC_STATUS pmic_audio_reset_all(void)
+{
+ PMIC_STATUS rc = PMIC_SUCCESS;
+ unsigned int audio_ssi_reset = 0;
+ unsigned int audio_rx1_reset = 0;
+ /* We need a critical section here to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ /* First close all opened device handles, also deregisters callbacks. */
+ pmic_audio_close_handle(stDAC.handle);
+ pmic_audio_close_handle(vCodec.handle);
+ pmic_audio_close_handle(extStereoIn.handle);
+
+ if (pmic_write_reg(REG_AUDIO_RX_1, RESET_AUDIO_RX_1,
+ PMIC_ALL_BITS) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ audio_rx1_reset = 1;
+ }
+ if (pmic_write_reg(REG_AUDIO_SSI_NETWORK, RESET_SSI_NETWORK,
+ PMIC_ALL_BITS) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ audio_ssi_reset = 1;
+ }
+ if (pmic_write_reg
+ (REG_AUDIO_STEREO_DAC, RESET_ST_DAC,
+ PMIC_ALL_BITS) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ /* Also reset the driver state information to match. Note that we
+ * keep the device handle and event callback settings unchanged
+ * since these don't affect the actual hardware and we rely on
+ * the user to explicitly close the handle or deregister callbacks
+ */
+ if (audio_ssi_reset) {
+ /* better to check if SSI is also reset as some fields are represennted in SSI reg */
+ stDAC.busID = AUDIO_DATA_BUS_1;
+ stDAC.protocol = NORMAL_MSB_JUSTIFIED_MODE;
+ stDAC.masterSlave = BUS_MASTER_MODE;
+ stDAC.protocol_set = false;
+ stDAC.numSlots = USE_2_TIMESLOTS;
+ stDAC.clockIn = CLOCK_IN_CLIA;
+ stDAC.samplingRate = STDAC_RATE_44_1_KHZ;
+ stDAC.clockFreq = STDAC_CLI_13MHZ;
+ stDAC.invert = NO_INVERT;
+ stDAC.timeslot = USE_TS0_TS1;
+ stDAC.config = (PMIC_AUDIO_STDAC_CONFIG) 0;
+ }
+ }
+
+ if (pmic_write_reg(REG_AUDIO_CODEC, RESET_AUD_CODEC,
+ PMIC_ALL_BITS) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ /* Also reset the driver state information to match. Note that we
+ * keep the device handle and event callback settings unchanged
+ * since these don't affect the actual hardware and we rely on
+ * the user to explicitly close the handle or deregister callbacks
+ */
+ if (audio_ssi_reset) {
+ vCodec.busID = AUDIO_DATA_BUS_2;
+ vCodec.protocol = NETWORK_MODE;
+ vCodec.masterSlave = BUS_SLAVE_MODE;
+ vCodec.protocol_set = false;
+ vCodec.numSlots = USE_4_TIMESLOTS;
+ vCodec.clockIn = CLOCK_IN_CLIB;
+ vCodec.samplingRate = VCODEC_RATE_8_KHZ;
+ vCodec.clockFreq = VCODEC_CLI_13MHZ;
+ vCodec.invert = NO_INVERT;
+ vCodec.timeslot = USE_TS0;
+ vCodec.config =
+ INPUT_HIGHPASS_FILTER | OUTPUT_HIGHPASS_FILTER;
+ }
+ }
+
+ if (pmic_write_reg(REG_AUDIO_RX_0, RESET_AUDIO_RX_0,
+ PMIC_ALL_BITS) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ /* Also reset the driver state information to match. */
+ audioOutput.outputPort = (PMIC_AUDIO_OUTPUT_PORT) NULL;
+ audioOutput.vCodecoutputPGAGain = OUTPGA_GAIN_0DB;
+ audioOutput.stDacoutputPGAGain = OUTPGA_GAIN_0DB;
+ audioOutput.extStereooutputPGAGain = OUTPGA_GAIN_0DB;
+ audioOutput.balanceLeftGain = BAL_GAIN_0DB;
+ audioOutput.balanceRightGain = BAL_GAIN_0DB;
+ audioOutput.monoAdderGain = MONOADD_GAIN_0DB;
+ audioOutput.config = (PMIC_AUDIO_OUTPUT_CONFIG) 0;
+ audioOutput.vCodecOut = VCODEC_DIRECT_OUT;
+ }
+
+ if (pmic_write_reg(REG_AUDIO_TX, RESET_AUDIO_TX,
+ PMIC_ALL_BITS) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ /* Also reset the driver state information to match. Note that we
+ * reset the vCodec fields since all of the input/recording
+ * devices are only connected to the Voice CODEC and are managed
+ * as part of the Voice CODEC state.
+ */
+ if (audio_rx1_reset) {
+ vCodec.leftChannelMic.mic = NO_MIC;
+ vCodec.leftChannelMic.micOnOff = MICROPHONE_OFF;
+ vCodec.leftChannelMic.ampMode = CURRENT_TO_VOLTAGE;
+ vCodec.leftChannelMic.gain = MIC_GAIN_0DB;
+ vCodec.rightChannelMic.mic = NO_MIC;
+ vCodec.rightChannelMic.micOnOff = MICROPHONE_OFF;
+ vCodec.rightChannelMic.ampMode = AMP_OFF;
+ vCodec.rightChannelMic.gain = MIC_GAIN_0DB;
+ }
+ }
+ /* Finally, also reset any global state variables. */
+ headsetState = NO_HEADSET;
+ /* Exit the critical section. */
+ up(&mutex);
+ return rc;
+}
+
+/*!
+ * @brief Set the Audio callback function.
+ *
+ * Register a callback function that will be used to signal PMIC audio
+ * events. For example, the OSS audio driver should register a callback
+ * function in order to be notified of headset connect/disconnect events.
+ *
+ * @param func A pointer to the callback function.
+ * @param eventMask A mask selecting events to be notified.
+ * @param hs_state To know the headset state.
+ *
+ *
+ *
+ * @retval PMIC_SUCCESS If the callback was successfully
+ * registered.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the eventMask is invalid.
+ */
+PMIC_STATUS pmic_audio_set_callback(void *func,
+ const PMIC_AUDIO_EVENTS eventMask,
+ PMIC_HS_STATE * hs_state)
+{
+ unsigned long flags;
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ pmic_event_callback_t eventNotify;
+
+ /* We need to start a critical section here to ensure a consistent state
+ * in case simultaneous calls to pmic_audio_set_callback() are made. In
+ * that case, we must serialize the calls to ensure that the "callback"
+ * and "eventMask" state variables are always consistent.
+ *
+ * Note that we don't actually need to acquire the spinlock until later
+ * when we are finally ready to update the "callback" and "eventMask"
+ * state variables which are shared with the interrupt handler.
+ */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ rc = PMIC_ERROR;
+ /* Register for PMIC events from the core protocol driver. */
+ if (eventMask & MICROPHONE_DETECTED) {
+ /* We need to register for the A1 amplifier interrupt. */
+ eventNotify.func = func;
+ eventNotify.param = (void *)(CORE_EVENT_MC2BI);
+ rc = pmic_event_subscribe(EVENT_MC2BI, eventNotify);
+
+ if (rc != PMIC_SUCCESS) {
+ pr_debug
+ ("%s: pmic_event_subscribe() for EVENT_HSDETI "
+ "failed\n", __FILE__);
+ goto End;
+ }
+ }
+
+ if (eventMask & HEADSET_DETECTED) {
+ /* We need to register for the A1 amplifier interrupt. */
+ eventNotify.func = func;
+ eventNotify.param = (void *)(CORE_EVENT_HSDETI);
+ rc = pmic_event_subscribe(EVENT_HSDETI, eventNotify);
+
+ if (rc != PMIC_SUCCESS) {
+ pr_debug
+ ("%s: pmic_event_subscribe() for EVENT_HSDETI "
+ "failed\n", __FILE__);
+ goto Cleanup_HDT;
+ }
+
+ }
+ if (eventMask & HEADSET_STEREO) {
+ /* We need to register for the A1 amplifier interrupt. */
+ eventNotify.func = func;
+ eventNotify.param = (void *)(CORE_EVENT_HSLI);
+ rc = pmic_event_subscribe(EVENT_HSLI, eventNotify);
+
+ if (rc != PMIC_SUCCESS) {
+ pr_debug
+ ("%s: pmic_event_subscribe() for EVENT_HSLI "
+ "failed\n", __FILE__);
+ goto Cleanup_HST;
+ }
+ }
+ if (eventMask & HEADSET_THERMAL_SHUTDOWN) {
+ /* We need to register for the A1 amplifier interrupt. */
+ eventNotify.func = func;
+ eventNotify.param = (void *)(CORE_EVENT_ALSPTHI);
+ rc = pmic_event_subscribe(EVENT_ALSPTHI, eventNotify);
+
+ if (rc != PMIC_SUCCESS) {
+ pr_debug
+ ("%s: pmic_event_subscribe() for EVENT_ALSPTHI "
+ "failed\n", __FILE__);
+ goto Cleanup_TSD;
+ }
+ pr_debug("Registered for EVENT_ALSPTHI\n");
+ }
+ if (eventMask & HEADSET_SHORT_CIRCUIT) {
+ /* We need to register for the A1 amplifier interrupt. */
+ eventNotify.func = func;
+ eventNotify.param = (void *)(CORE_EVENT_AHSSHORTI);
+ rc = pmic_event_subscribe(EVENT_AHSSHORTI, eventNotify);
+
+ if (rc != PMIC_SUCCESS) {
+ pr_debug
+ ("%s: pmic_event_subscribe() for EVENT_AHSSHORTI "
+ "failed\n", __FILE__);
+ goto Cleanup_HShort;
+ }
+ pr_debug("Registered for EVENT_AHSSHORTI\n");
+ }
+
+ /* We also need the spinlock here to avoid possible problems
+ * with the interrupt handler when we update the
+ * "callback" and "eventMask" state variables.
+ */
+ spin_lock_irqsave(&lock, flags);
+
+ /* Successfully registered for all events. */
+ event_state.callback = func;
+ event_state.eventMask = eventMask;
+
+ /* The spinlock is no longer needed now that we've finished
+ * updating the "callback" and "eventMask" state variables.
+ */
+ spin_unlock_irqrestore(&lock, flags);
+
+ goto End;
+
+ /* This section unregisters any already registered events if we should
+ * encounter an error partway through the registration process. Note
+ * that we don't check the return status here since it is already set
+ * to PMIC_ERROR before we get here.
+ */
+ Cleanup_HShort:
+
+ if (eventMask & HEADSET_SHORT_CIRCUIT) {
+ eventNotify.func = func;
+ eventNotify.param = (void *)(CORE_EVENT_AHSSHORTI);
+ pmic_event_unsubscribe(EVENT_AHSSHORTI, eventNotify);
+ }
+
+ Cleanup_TSD:
+
+ if (eventMask & HEADSET_THERMAL_SHUTDOWN) {
+ eventNotify.func = func;
+ eventNotify.param = (void *)(CORE_EVENT_ALSPTHI);
+ pmic_event_unsubscribe(EVENT_ALSPTHI, eventNotify);
+ }
+
+ Cleanup_HST:
+
+ if (eventMask & HEADSET_STEREO) {
+ eventNotify.func = func;
+ eventNotify.param = (void *)(CORE_EVENT_HSLI);
+ pmic_event_unsubscribe(EVENT_HSLI, eventNotify);
+ }
+
+ Cleanup_HDT:
+
+ if (eventMask & HEADSET_DETECTED) {
+ eventNotify.func = func;
+ eventNotify.param = (void *)(CORE_EVENT_HSDETI);
+ pmic_event_unsubscribe(EVENT_HSDETI, eventNotify);
+ }
+
+ End:
+ /* Exit the critical section. */
+ up(&mutex);
+ return rc;
+}
+
+/*!
+ * @brief Deregisters the existing audio callback function.
+ *
+ * Deregister the callback function that was previously registered by calling
+ * pmic_audio_set_callback().
+ *
+ *
+ * @retval PMIC_SUCCESS If the callback was successfully
+ * deregistered.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_clear_callback(void)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* We need a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if (event_state.callback != (PMIC_AUDIO_CALLBACK) NULL) {
+ rc = pmic_audio_deregister(&(event_state.callback),
+ &(event_state.eventMask));
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+ return rc;
+}
+
+/*!
+ * @brief Get the current audio callback function settings.
+ *
+ * Get the current callback function and event mask.
+ *
+ * @param func The current callback function.
+ * @param eventMask The current event selection mask.
+ *
+ * @retval PMIC_SUCCESS If the callback information was
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+PMIC_STATUS pmic_audio_get_callback(PMIC_AUDIO_CALLBACK * const func,
+ PMIC_AUDIO_EVENTS * const eventMask)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* We only need to acquire the mutex here because we will not be updating
+ * anything that may affect the interrupt handler. We just need to ensure
+ * that the callback fields are not changed while we are in the critical
+ * section by calling either pmic_audio_set_callback() or
+ * pmic_audio_clear_callback().
+ */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((func != (PMIC_AUDIO_CALLBACK *) NULL) &&
+ (eventMask != (PMIC_AUDIO_EVENTS *) NULL)) {
+
+ *func = event_state.callback;
+ *eventMask = event_state.eventMask;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+ return rc;
+}
+
+/*!
+ * @brief Enable the anti-pop circuitry to avoid extra noise when inserting
+ * or removing a external device (e.g., a headset).
+ *
+ * Enable the use of the built-in anti-pop circuitry to prevent noise from
+ * being generated when an external audio device is inserted or removed
+ * from an audio plug. A slow ramp speed may be needed to avoid extra noise.
+ *
+ * @param rampSpeed The desired anti-pop circuitry ramp speed.
+ *
+ * @retval PMIC_SUCCESS If the anti-pop circuitry was successfully
+ * enabled.
+ * @retval PMIC_ERROR If the anti-pop circuitry could not be
+ * enabled.
+ */
+PMIC_STATUS pmic_audio_antipop_enable(const PMIC_AUDIO_ANTI_POP_RAMP_SPEED
+ rampSpeed)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+ unsigned int reg_value = 0;
+ const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, BIASEN, 1) |
+ SET_BITS(regAUDIO_RX_0, BIASSPEED, 1);
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ /*
+ * Antipop is enabled by enabling the BIAS (BIASEN) and setting the
+ * BIASSPEED .
+ * BIASEN is just to make sure that BIAS is enabled
+ */
+ reg_value = SET_BITS(regAUDIO_RX_0, BIASEN, 1)
+ | SET_BITS(regAUDIO_RX_0, BIASSPEED, 0) | SET_BITS(regAUDIO_RX_0,
+ HSLDETEN, 1);
+ rc = pmic_write_reg(REG_AUDIO_RX_0, reg_value, reg_mask);
+ return rc;
+}
+
+/*!
+ * @brief Disable the anti-pop circuitry.
+ *
+ * Disable the use of the built-in anti-pop circuitry to prevent noise from
+ * being generated when an external audio device is inserted or removed
+ * from an audio plug.
+ *
+ * @retval PMIC_SUCCESS If the anti-pop circuitry was successfully
+ * disabled.
+ * @retval PMIC_ERROR If the anti-pop circuitry could not be
+ * disabled.
+ */
+PMIC_STATUS pmic_audio_antipop_disable(void)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+ const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, BIASSPEED, 1) |
+ SET_BITS(regAUDIO_RX_0, BIASEN, 1);
+ const unsigned int reg_write = SET_BITS(regAUDIO_RX_0, BIASSPEED, 1) |
+ SET_BITS(regAUDIO_RX_0, BIASEN, 0);
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ /*
+ * Antipop is disabled by setting BIASSPEED = 0. BIASEN bit remains set
+ * as only antipop needs to be disabled
+ */
+ rc = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
+
+ return rc;
+}
+
+/*!
+ * @brief Performs a reset of the Voice CODEC/Stereo DAC digital filter.
+ *
+ * The digital filter should be reset whenever the clock or sampling rate
+ * configuration has been changed.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the digital filter was successfully
+ * reset.
+ * @retval PMIC_ERROR If the digital filter could not be reset.
+ */
+PMIC_STATUS pmic_audio_digital_filter_reset(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_mask = 0;
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+ reg_mask = SET_BITS(regST_DAC, STDCRESET, 1);
+ if (pmic_write_reg(REG_AUDIO_STEREO_DAC, reg_mask,
+ reg_mask) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ pr_debug("STDAC filter reset\n");
+ }
+
+ } else if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) {
+ reg_mask = SET_BITS(regAUD_CODEC, CDCRESET, 1);
+ if (pmic_write_reg(REG_AUDIO_CODEC, reg_mask,
+ reg_mask) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ pr_debug("CODEC filter reset\n");
+ }
+ }
+ return rc;
+}
+
+/*!
+ * @brief Get the most recent PTT button voltage reading.
+ *
+ * This feature is not supported by mc13783
+ * @param level PTT button level.
+ *
+ * @retval PMIC_SUCCESS If the most recent PTT button voltage was
+ * returned.
+ * @retval PMIC_PARAMETER_ERROR If a NULL pointer argument was given.
+ */
+PMIC_STATUS pmic_audio_get_ptt_button_level(unsigned int *const level)
+{
+ PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+ return rc;
+}
+
+#ifdef DEBUG_AUDIO
+
+/*!
+ * @brief Provide a hexadecimal dump of all PMIC audio registers (DEBUG only)
+ *
+ * This function is intended strictly for debugging purposes only and will
+ * print the current values of the following PMIC registers:
+ *
+ * - AUD_CODEC
+ * - ST_DAC
+ * - AUDIO_RX_0
+ * - AUDIO_RX_1
+ * - AUDIO_TX
+ * - AUDIO_SSI_NW
+ *
+ * The register fields will not be decoded.
+ *
+ * Note that we don't dump any of the arbitration bits because we cannot
+ * access the true arbitration bit settings when reading the registers
+ * from the secondary SPI bus.
+ *
+ * Also note that we must not call this function with interrupts disabled,
+ * for example, while holding a spinlock, because calls to pmic_read_reg()
+ * eventually end up in the SPI driver which will want to perform a
+ * schedule() operation. If schedule() is called with interrupts disabled,
+ * then you will see messages like the following:
+ *
+ * BUG: scheduling while atomic: ...
+ *
+ */
+void pmic_audio_dump_registers(void)
+{
+ unsigned int reg_value = 0;
+
+ /* Dump the AUD_CODEC (Voice CODEC) register. */
+ if (pmic_read_reg(REG_AUDIO_CODEC, &reg_value, REG_FULLMASK)
+ == PMIC_SUCCESS) {
+ pr_debug("Audio Codec = 0x%x\n", reg_value);
+ } else {
+ pr_debug("Failed to read audio codec\n");
+ }
+
+ /* Dump the ST DAC (Stereo DAC) register. */
+ if (pmic_read_reg
+ (REG_AUDIO_STEREO_DAC, &reg_value, REG_FULLMASK) == PMIC_SUCCESS) {
+ pr_debug("Stereo DAC = 0x%x\n", reg_value);
+ } else {
+ pr_debug("Failed to read Stereo DAC\n");
+ }
+
+ /* Dump the SSI NW register. */
+ if (pmic_read_reg
+ (REG_AUDIO_SSI_NETWORK, &reg_value, REG_FULLMASK) == PMIC_SUCCESS) {
+ pr_debug("SSI Network = 0x%x\n", reg_value);
+ } else {
+ pr_debug("Failed to read SSI network\n");
+ }
+
+ /* Dump the Audio RX 0 register. */
+ if (pmic_read_reg(REG_AUDIO_RX_0, &reg_value, REG_FULLMASK)
+ == PMIC_SUCCESS) {
+ pr_debug("Audio RX 0 = 0x%x\n", reg_value);
+ } else {
+ pr_debug("Failed to read audio RX 0\n");
+ }
+
+ /* Dump the Audio RX 1 register. */
+ if (pmic_read_reg(REG_AUDIO_RX_1, &reg_value, REG_FULLMASK)
+ == PMIC_SUCCESS) {
+ pr_debug("Audio RX 1 = 0x%x\n", reg_value);
+ } else {
+ pr_debug("Failed to read audio RX 1\n");
+ }
+ /* Dump the Audio TX register. */
+ if (pmic_read_reg(REG_AUDIO_TX, &reg_value, REG_FULLMASK) ==
+ PMIC_SUCCESS) {
+ pr_debug("Audio Tx = 0x%x\n", reg_value);
+ } else {
+ pr_debug("Failed to read audio TX\n");
+ }
+
+}
+
+#endif /* DEBUG_AUDIO */
+
+/*@}*/
+
+/*************************************************************************
+ * General Voice CODEC configuration.
+ *************************************************************************
+ */
+
+/*!
+ * @name General Voice CODEC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Voice
+ * CODEC hardware.
+ */
+/*@{*/
+
+/*!
+ * @brief Set the Voice CODEC clock source and operating characteristics.
+ *
+ * Define the Voice CODEC clock source and operating characteristics. This
+ * must be done before the Voice CODEC is enabled.
+ *
+ *
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param clockIn Select the clock signal source.
+ * @param clockFreq Select the clock signal frequency.
+ * @param samplingRate Select the audio data sampling rate.
+ * @param invert Enable inversion of the frame sync and/or
+ * bit clock inputs.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC clock settings were
+ * successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or clock configuration was
+ * invalid.
+ * @retval PMIC_ERROR If the Voice CODEC clock configuration
+ * could not be set.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_clock(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_CLOCK_IN_SOURCE
+ clockIn,
+ const PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ
+ clockFreq,
+ const PMIC_AUDIO_VCODEC_SAMPLING_RATE
+ samplingRate,
+ const PMIC_AUDIO_CLOCK_INVERT invert)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_value = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ /* Validate all of the calling parameters. */
+ if (handle == (PMIC_AUDIO_HANDLE) NULL) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) {
+ if ((clockIn != CLOCK_IN_CLIA) && (clockIn != CLOCK_IN_CLIB)) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else if (!((clockFreq >= VCODEC_CLI_13MHZ)
+ && (clockFreq <= VCODEC_CLI_33_6MHZ))) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else if ((samplingRate != VCODEC_RATE_8_KHZ)
+ && (samplingRate != VCODEC_RATE_16_KHZ)) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else if (!((invert >= NO_INVERT)
+ && (invert <= INVERT_FRAMESYNC))) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else {
+ /*reg_mask = SET_BITS(regAUD_CODEC, CDCCLK, 7) |
+ SET_BITS(regAUD_CODEC, CDCCLKSEL, 1) |
+ SET_BITS(regAUD_CODEC, CDCFS8K16K, 1) |
+ SET_BITS(regAUD_CODEC, CDCBCLINV, 1) |
+ SET_BITS(regAUD_CODEC, CDCFSINV, 1); */
+ if (clockIn == CLOCK_IN_CLIA) {
+ reg_value =
+ SET_BITS(regAUD_CODEC, CDCCLKSEL, 0);
+ } else {
+ reg_value =
+ SET_BITS(regAUD_CODEC, CDCCLKSEL, 1);
+ }
+ reg_mask = SET_BITS(regAUD_CODEC, CDCCLKSEL, 1);
+ if (PMIC_SUCCESS !=
+ pmic_write_reg(REG_AUDIO_CODEC,
+ reg_value, reg_mask))
+ return PMIC_ERROR;
+
+ reg_value = 0;
+ if (clockFreq == VCODEC_CLI_13MHZ) {
+ reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 0);
+ } else if (clockFreq == VCODEC_CLI_15_36MHZ) {
+ reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 1);
+ } else if (clockFreq == VCODEC_CLI_16_8MHZ) {
+ reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 2);
+ } else if (clockFreq == VCODEC_CLI_26MHZ) {
+ reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 4);
+ } else {
+ reg_value |= SET_BITS(regAUD_CODEC, CDCCLK, 7);
+ }
+ reg_mask = SET_BITS(regAUD_CODEC, CDCCLK, 7);
+ if (PMIC_SUCCESS !=
+ pmic_write_reg(REG_AUDIO_CODEC,
+ reg_value, reg_mask))
+ return PMIC_ERROR;
+
+ reg_value = 0;
+ reg_mask = 0;
+
+ if (samplingRate == VCODEC_RATE_8_KHZ) {
+ reg_value |=
+ SET_BITS(regAUD_CODEC, CDCFS8K16K, 0);
+ } else {
+ reg_value |=
+ SET_BITS(regAUD_CODEC, CDCFS8K16K, 1);
+ }
+ reg_mask = SET_BITS(regAUD_CODEC, CDCFS8K16K, 1);
+ if (PMIC_SUCCESS !=
+ pmic_write_reg(REG_AUDIO_CODEC,
+ reg_value, reg_mask))
+ return PMIC_ERROR;
+ reg_value = 0;
+ reg_mask =
+ SET_BITS(regAUD_CODEC, CDCBCLINV,
+ 1) | SET_BITS(regAUD_CODEC, CDCFSINV, 1);
+
+ if (invert & INVERT_BITCLOCK) {
+ reg_value |=
+ SET_BITS(regAUD_CODEC, CDCBCLINV, 1);
+ }
+ if (invert & INVERT_FRAMESYNC) {
+ reg_value |=
+ SET_BITS(regAUD_CODEC, CDCFSINV, 1);
+ }
+ if (invert & NO_INVERT) {
+ reg_value |=
+ SET_BITS(regAUD_CODEC, CDCBCLINV, 0);
+ reg_value |=
+ SET_BITS(regAUD_CODEC, CDCFSINV, 0);
+ }
+ if (pmic_write_reg
+ (REG_AUDIO_CODEC, reg_value,
+ reg_mask) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ pr_debug("CODEC clock set\n");
+ vCodec.clockIn = clockIn;
+ vCodec.clockFreq = clockFreq;
+ vCodec.samplingRate = samplingRate;
+ vCodec.invert = invert;
+ }
+
+ }
+
+ } else {
+ rc = PMIC_PARAMETER_ERROR;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the Voice CODEC clock source and operating characteristics.
+ *
+ * Get the current Voice CODEC clock source and operating characteristics.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param clockIn The clock signal source.
+ * @param clockFreq The clock signal frequency.
+ * @param samplingRate The audio data sampling rate.
+ * @param invert Inversion of the frame sync and/or
+ * bit clock inputs is enabled/disabled.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC clock settings were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle invalid.
+ * @retval PMIC_ERROR If the Voice CODEC clock configuration
+ * could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_clock(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_CLOCK_IN_SOURCE *
+ const clockIn,
+ PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ *
+ const clockFreq,
+ PMIC_AUDIO_VCODEC_SAMPLING_RATE *
+ const samplingRate,
+ PMIC_AUDIO_CLOCK_INVERT * const invert)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure that we return a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE) &&
+ (clockIn != (PMIC_AUDIO_CLOCK_IN_SOURCE *) NULL) &&
+ (clockFreq != (PMIC_AUDIO_VCODEC_CLOCK_IN_FREQ *) NULL) &&
+ (samplingRate != (PMIC_AUDIO_VCODEC_SAMPLING_RATE *) NULL) &&
+ (invert != (PMIC_AUDIO_CLOCK_INVERT *) NULL)) {
+ *clockIn = vCodec.clockIn;
+ *clockFreq = vCodec.clockFreq;
+ *samplingRate = vCodec.samplingRate;
+ *invert = vCodec.invert;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Set the Voice CODEC primary audio channel timeslot.
+ *
+ * Set the Voice CODEC primary audio channel timeslot. This function must be
+ * used if the default timeslot for the primary audio channel is to be changed.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param timeslot Select the primary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC primary audio channel
+ * timeslot was successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
+ * was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC primary audio channel
+ * timeslot could not be set.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_VCODEC_TIMESLOT
+ timeslot)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ const unsigned int reg_mask = SET_BITS(regSSI_NETWORK, CDCTXRXSLOT, 3);
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE) &&
+ ((timeslot == USE_TS0) || (timeslot == USE_TS1) ||
+ (timeslot == USE_TS2) || (timeslot == USE_TS3))) {
+ reg_write = SET_BITS(regSSI_NETWORK, CDCTXRXSLOT, timeslot);
+
+ rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK, reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ vCodec.timeslot = timeslot;
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the current Voice CODEC primary audio channel timeslot.
+ *
+ * Get the current Voice CODEC primary audio channel timeslot.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param timeslot The primary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC primary audio channel
+ * timeslot was successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC primary audio channel
+ * timeslot could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_VCODEC_TIMESLOT *
+ const timeslot)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE) &&
+ (timeslot != (PMIC_AUDIO_VCODEC_TIMESLOT *) NULL)) {
+ *timeslot = vCodec.timeslot;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Set the Voice CODEC secondary recording audio channel timeslot.
+ *
+ * Set the Voice CODEC secondary audio channel timeslot. This function must be
+ * used if the default timeslot for the secondary audio channel is to be
+ * changed. The secondary audio channel timeslot is used to transmit the audio
+ * data that was recorded by the Voice CODEC from the secondary audio input
+ * channel.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param timeslot Select the secondary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC secondary audio channel
+ * timeslot was successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
+ * was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC secondary audio channel
+ * timeslot could not be set.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_secondary_txslot(const PMIC_AUDIO_HANDLE
+ handle,
+ const
+ PMIC_AUDIO_VCODEC_TIMESLOT
+ timeslot)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_mask = SET_BITS(regSSI_NETWORK, CDCTXSECSLOT, 3);
+ unsigned int reg_write = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ /* How to handle primary slot and secondary slot being the same */
+ if ((timeslot >= USE_TS0) && (timeslot <= USE_TS3)
+ && (timeslot != vCodec.timeslot)) {
+ reg_write =
+ SET_BITS(regSSI_NETWORK, CDCTXSECSLOT, timeslot);
+
+ rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ vCodec.secondaryTXtimeslot = timeslot;
+ }
+ }
+
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the Voice CODEC secondary recording audio channel timeslot.
+ *
+ * Get the Voice CODEC secondary audio channel timeslot.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param timeslot The secondary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC secondary audio channel
+ * timeslot was successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC secondary audio channel
+ * timeslot could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_secondary_txslot(const PMIC_AUDIO_HANDLE
+ handle,
+ PMIC_AUDIO_VCODEC_TIMESLOT *
+ const timeslot)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE) &&
+ (timeslot != (PMIC_AUDIO_VCODEC_TIMESLOT *) NULL)) {
+ rc = PMIC_SUCCESS;
+ *timeslot = vCodec.secondaryTXtimeslot;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+ return rc;
+}
+
+/*!
+ * @brief Set/Enable the Voice CODEC options.
+ *
+ * Set or enable various Voice CODEC options. The available options include
+ * the use of dithering, highpass digital filters, and loopback modes.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The Voice CODEC options to enable.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC options were
+ * successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or Voice CODEC options
+ * were invalid.
+ * @retval PMIC_ERROR If the Voice CODEC options could not be
+ * successfully set/enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_VCODEC_CONFIG config)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ if (config & DITHERING) {
+ reg_write = SET_BITS(regAUD_CODEC, CDCDITH, 0);
+ reg_mask = SET_BITS(regAUD_CODEC, CDCDITH, 1);
+ }
+
+ if (config & INPUT_HIGHPASS_FILTER) {
+ reg_write |= SET_BITS(regAUD_CODEC, AUDIHPF, 1);
+ reg_mask |= SET_BITS(regAUD_CODEC, AUDIHPF, 1);
+ }
+
+ if (config & OUTPUT_HIGHPASS_FILTER) {
+ reg_write |= SET_BITS(regAUD_CODEC, AUDOHPF, 1);
+ reg_mask |= SET_BITS(regAUD_CODEC, AUDOHPF, 1);
+ }
+
+ if (config & DIGITAL_LOOPBACK) {
+ reg_write |= SET_BITS(regAUD_CODEC, CDCDLM, 1);
+ reg_mask |= SET_BITS(regAUD_CODEC, CDCDLM, 1);
+ }
+
+ if (config & ANALOG_LOOPBACK) {
+ reg_write |= SET_BITS(regAUD_CODEC, CDCALM, 1);
+ reg_mask |= SET_BITS(regAUD_CODEC, CDCALM, 1);
+ }
+
+ if (config & VCODEC_MASTER_CLOCK_OUTPUTS) {
+ reg_write |= SET_BITS(regAUD_CODEC, CDCCLKEN, 1) |
+ SET_BITS(regAUD_CODEC, CDCTS, 0);
+ reg_mask |= SET_BITS(regAUD_CODEC, CDCCLKEN, 1) |
+ SET_BITS(regAUD_CODEC, CDCTS, 1);
+
+ }
+
+ if (config & TRISTATE_TS) {
+ reg_write |= SET_BITS(regAUD_CODEC, CDCTS, 1);
+ reg_mask |= SET_BITS(regAUD_CODEC, CDCTS, 1);
+ }
+
+ if (reg_mask == 0) {
+ /* We should not reach this point without having to configure
+ * anything so we flag it as an error.
+ */
+ rc = PMIC_ERROR;
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_CODEC,
+ reg_write, reg_mask);
+ }
+
+ if (rc == PMIC_SUCCESS) {
+ vCodec.config |= config;
+ }
+ }
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Clear/Disable the Voice CODEC options.
+ *
+ * Clear or disable various Voice CODEC options.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The Voice CODEC options to be cleared/disabled.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC options were
+ * successfully cleared/disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the Voice CODEC options
+ * were invalid.
+ * @retval PMIC_ERROR If the Voice CODEC options could not be
+ * cleared/disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_clear_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_VCODEC_CONFIG
+ config)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ if (config & DITHERING) {
+ reg_mask = SET_BITS(regAUD_CODEC, CDCDITH, 1);
+ reg_write = SET_BITS(regAUD_CODEC, CDCDITH, 1);
+ }
+
+ if (config & INPUT_HIGHPASS_FILTER) {
+ reg_mask |= SET_BITS(regAUD_CODEC, AUDIHPF, 1);
+ }
+
+ if (config & OUTPUT_HIGHPASS_FILTER) {
+ reg_mask |= SET_BITS(regAUD_CODEC, AUDOHPF, 1);
+ }
+
+ if (config & DIGITAL_LOOPBACK) {
+ reg_mask |= SET_BITS(regAUD_CODEC, CDCDLM, 1);
+ }
+
+ if (config & ANALOG_LOOPBACK) {
+ reg_mask |= SET_BITS(regAUD_CODEC, CDCALM, 1);
+ }
+
+ if (config & VCODEC_MASTER_CLOCK_OUTPUTS) {
+ reg_mask |= SET_BITS(regAUD_CODEC, CDCCLKEN, 1);
+ }
+
+ if (config & TRISTATE_TS) {
+ reg_mask |= SET_BITS(regAUD_CODEC, CDCTS, 1);
+ }
+
+ if (reg_mask == 0) {
+ /* We should not reach this point without having to configure
+ * anything so we flag it as an error.
+ */
+ rc = PMIC_ERROR;
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_CODEC,
+ reg_write, reg_mask);
+ }
+
+ if (rc == PMIC_SUCCESS) {
+ vCodec.config |= config;
+ }
+
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the current Voice CODEC options.
+ *
+ * Get the current Voice CODEC options.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The current set of Voice CODEC options.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC options were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC options could not be
+ * retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_config(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_VCODEC_CONFIG *
+ const config)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE) &&
+ (config != (PMIC_AUDIO_VCODEC_CONFIG *) NULL)) {
+ *config = vCodec.config;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Enable the Voice CODEC bypass audio pathway.
+ *
+ * Enables the Voice CODEC bypass pathway for audio data. This allows direct
+ * output of the voltages on the TX data bus line to the output amplifiers
+ * (bypassing the digital-to-analog converters within the Voice CODEC).
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC bypass was successfully
+ * enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC bypass could not be
+ * enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_enable_bypass(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ const unsigned int reg_write = SET_BITS(regAUD_CODEC, CDCBYP, 1);
+ const unsigned int reg_mask = SET_BITS(regAUD_CODEC, CDCBYP, 1);
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ rc = pmic_write_reg(REG_AUDIO_CODEC, reg_write, reg_mask);
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief Disable the Voice CODEC bypass audio pathway.
+ *
+ * Disables the Voice CODEC bypass pathway for audio data. This means that
+ * the TX data bus line will deliver digital data to the digital-to-analog
+ * converters within the Voice CODEC.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC bypass was successfully
+ * disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC bypass could not be
+ * disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_disable_bypass(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ const unsigned int reg_write = 0;
+ const unsigned int reg_mask = SET_BITS(regAUD_CODEC, CDCBYP, 1);
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ rc = pmic_write_reg(REG_AUDIO_CODEC, reg_write, reg_mask);
+ }
+
+ return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * General Stereo DAC configuration.
+ *************************************************************************
+ */
+
+/*!
+ * @name General Stereo DAC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Stereo
+ * DAC hardware.
+ */
+/*@{*/
+
+/*!
+ * @brief Set the Stereo DAC clock source and operating characteristics.
+ *
+ * Define the Stereo DAC clock source and operating characteristics. This
+ * must be done before the Stereo DAC is enabled.
+ *
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param clockIn Select the clock signal source.
+ * @param clockFreq Select the clock signal frequency.
+ * @param samplingRate Select the audio data sampling rate.
+ * @param invert Enable inversion of the frame sync and/or
+ * bit clock inputs.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC clock settings were
+ * successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or clock configuration was
+ * invalid.
+ * @retval PMIC_ERROR If the Stereo DAC clock configuration
+ * could not be set.
+ */
+PMIC_STATUS pmic_audio_stdac_set_clock(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_CLOCK_IN_SOURCE clockIn,
+ const PMIC_AUDIO_STDAC_CLOCK_IN_FREQ
+ clockFreq,
+ const PMIC_AUDIO_STDAC_SAMPLING_RATE
+ samplingRate,
+ const PMIC_AUDIO_CLOCK_INVERT invert)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_value = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+ /* Validate all of the calling parameters. */
+ if (handle == (PMIC_AUDIO_HANDLE) NULL) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else if ((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) {
+ if ((clockIn != CLOCK_IN_CLIA) && (clockIn != CLOCK_IN_CLIB)) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else if ((stDAC.masterSlave == BUS_MASTER_MODE)
+ && !((clockFreq >= STDAC_CLI_3_36864MHZ)
+ && (clockFreq <= STDAC_CLI_33_6MHZ))) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else if ((stDAC.masterSlave == BUS_SLAVE_MODE)
+ && !((clockFreq >= STDAC_MCLK_PLL_DISABLED)
+ && (clockFreq <= STDAC_BCLK_IN_PLL))) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else if (!((samplingRate >= STDAC_RATE_8_KHZ)
+ && (samplingRate <= STDAC_RATE_96_KHZ))) {
+ rc = PMIC_PARAMETER_ERROR;
+ }
+ /*
+ else if(!((invert >= NO_INVERT) && (invert <= INVERT_FRAMESYNC)))
+ {
+ rc = PMIC_PARAMETER_ERROR;
+ } */
+ else {
+ reg_mask = SET_BITS(regST_DAC, STDCCLK, 7) |
+ SET_BITS(regST_DAC, STDCCLKSEL, 1) |
+ SET_BITS(regST_DAC, SR, 15) |
+ SET_BITS(regST_DAC, STDCBCLINV, 1) |
+ SET_BITS(regST_DAC, STDCFSINV, 1);
+ if (clockIn == CLOCK_IN_CLIA) {
+ reg_value = SET_BITS(regST_DAC, STDCCLKSEL, 0);
+ } else {
+ reg_value = SET_BITS(regST_DAC, STDCCLKSEL, 1);
+ }
+ /* How to take care of sample rates in SLAVE mode */
+ if ((clockFreq == STDAC_CLI_3_36864MHZ)
+ || ((clockFreq == STDAC_FSYNC_IN_PLL))) {
+ reg_value |= SET_BITS(regST_DAC, STDCCLK, 6);
+ } else if ((clockFreq == STDAC_CLI_12MHZ)
+ || (clockFreq == STDAC_MCLK_PLL_DISABLED)) {
+ reg_value |= SET_BITS(regST_DAC, STDCCLK, 5);
+ } else if (clockFreq == STDAC_CLI_13MHZ) {
+ reg_value |= SET_BITS(regST_DAC, STDCCLK, 0);
+ } else if (clockFreq == STDAC_CLI_15_36MHZ) {
+ reg_value |= SET_BITS(regST_DAC, STDCCLK, 1);
+ } else if (clockFreq == STDAC_CLI_16_8MHZ) {
+ reg_value |= SET_BITS(regST_DAC, STDCCLK, 2);
+ } else if (clockFreq == STDAC_CLI_26MHZ) {
+ reg_value |= SET_BITS(regST_DAC, STDCCLK, 4);
+ } else if ((clockFreq == STDAC_CLI_33_6MHZ)
+ || (clockFreq == STDAC_BCLK_IN_PLL)) {
+ reg_value |= SET_BITS(regST_DAC, STDCCLK, 7);
+ }
+
+ reg_value |= SET_BITS(regST_DAC, SR, samplingRate);
+
+ if (invert & INVERT_BITCLOCK) {
+ reg_value |= SET_BITS(regST_DAC, STDCBCLINV, 1);
+ }
+ if (invert & INVERT_FRAMESYNC) {
+ reg_value |= SET_BITS(regST_DAC, STDCFSINV, 1);
+ }
+ if (invert & NO_INVERT) {
+ reg_value |= SET_BITS(regST_DAC, STDCBCLINV, 0);
+ reg_value |= SET_BITS(regST_DAC, STDCFSINV, 0);
+ }
+ if (pmic_write_reg
+ (REG_AUDIO_STEREO_DAC, reg_value,
+ reg_mask) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ pr_debug("STDAC clock set\n");
+ rc = PMIC_SUCCESS;
+ stDAC.clockIn = clockIn;
+ stDAC.clockFreq = clockFreq;
+ stDAC.samplingRate = samplingRate;
+ stDAC.invert = invert;
+ }
+
+ }
+
+ } else {
+ rc = PMIC_PARAMETER_ERROR;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the Stereo DAC clock source and operating characteristics.
+ *
+ * Get the current Stereo DAC clock source and operating characteristics.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param clockIn The clock signal source.
+ * @param clockFreq The clock signal frequency.
+ * @param samplingRate The audio data sampling rate.
+ * @param invert Inversion of the frame sync and/or
+ * bit clock inputs is enabled/disabled.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC clock settings were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle invalid.
+ * @retval PMIC_ERROR If the Stereo DAC clock configuration
+ * could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_stdac_get_clock(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_CLOCK_IN_SOURCE *
+ const clockIn,
+ PMIC_AUDIO_STDAC_SAMPLING_RATE *
+ const samplingRate,
+ PMIC_AUDIO_STDAC_CLOCK_IN_FREQ *
+ const clockFreq,
+ PMIC_AUDIO_CLOCK_INVERT * const invert)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE) &&
+ (clockIn != (PMIC_AUDIO_CLOCK_IN_SOURCE *) NULL) &&
+ (samplingRate != (PMIC_AUDIO_STDAC_SAMPLING_RATE *) NULL) &&
+ (clockFreq != (PMIC_AUDIO_STDAC_CLOCK_IN_FREQ *) NULL) &&
+ (invert != (PMIC_AUDIO_CLOCK_INVERT *) NULL)) {
+ *clockIn = stDAC.clockIn;
+ *samplingRate = stDAC.samplingRate;
+ *clockFreq = stDAC.clockFreq;
+ *invert = stDAC.invert;
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Set the Stereo DAC primary audio channel timeslot.
+ *
+ * Set the Stereo DAC primary audio channel timeslot. This function must be
+ * used if the default timeslot for the primary audio channel is to be changed.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param timeslot Select the primary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC primary audio channel
+ * timeslot was successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or audio channel timeslot
+ * was invalid.
+ * @retval PMIC_ERROR If the Stereo DAC primary audio channel
+ * timeslot could not be set.
+ */
+PMIC_STATUS pmic_audio_stdac_set_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_STDAC_TIMESLOTS
+ timeslot)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_mask = SET_BITS(regSSI_NETWORK, STDCRXSLOT, 3);
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+ if ((timeslot == USE_TS0_TS1) || (timeslot == USE_TS2_TS3)
+ || (timeslot == USE_TS4_TS5) || (timeslot == USE_TS6_TS7)) {
+ if (pmic_write_reg
+ (REG_AUDIO_SSI_NETWORK, timeslot,
+ reg_mask) != PMIC_SUCCESS) {
+ rc = PMIC_ERROR;
+ } else {
+ pr_debug("STDAC primary timeslot set\n");
+ stDAC.timeslot = timeslot;
+ rc = PMIC_SUCCESS;
+ }
+
+ } else {
+ rc = PMIC_PARAMETER_ERROR;
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the current Stereo DAC primary audio channel timeslot.
+ *
+ * Get the current Stereo DAC primary audio channel timeslot.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param timeslot The primary audio channel timeslot.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC primary audio channel
+ * timeslot was successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Stereo DAC primary audio channel
+ * timeslot could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_stdac_get_rxtx_timeslot(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_STDAC_TIMESLOTS *
+ const timeslot)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE) &&
+ (timeslot != (PMIC_AUDIO_STDAC_TIMESLOTS *) NULL)) {
+ *timeslot = stDAC.timeslot;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Set/Enable the Stereo DAC options.
+ *
+ * Set or enable various Stereo DAC options. The available options include
+ * resetting the digital filter and enabling the bus master clock outputs.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The Stereo DAC options to enable.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC options were
+ * successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or Stereo DAC options
+ * were invalid.
+ * @retval PMIC_ERROR If the Stereo DAC options could not be
+ * successfully set/enabled.
+ */
+PMIC_STATUS pmic_audio_stdac_set_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_STDAC_CONFIG config)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+ if (config & STDAC_MASTER_CLOCK_OUTPUTS) {
+ reg_write |= SET_BITS(regST_DAC, STDCCLKEN, 1);
+ reg_mask |= SET_BITS(regST_DAC, STDCCLKEN, 1);
+ }
+
+ rc = pmic_write_reg(REG_AUDIO_STEREO_DAC, reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ stDAC.config |= config;
+ pr_debug("STDAC config set\n");
+
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Clear/Disable the Stereo DAC options.
+ *
+ * Clear or disable various Stereo DAC options.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The Stereo DAC options to be cleared/disabled.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC options were
+ * successfully cleared/disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the Stereo DAC options
+ * were invalid.
+ * @retval PMIC_ERROR If the Stereo DAC options could not be
+ * cleared/disabled.
+ */
+PMIC_STATUS pmic_audio_stdac_clear_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_STDAC_CONFIG config)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+
+ if (config & STDAC_MASTER_CLOCK_OUTPUTS) {
+ reg_mask |= SET_BITS(regST_DAC, STDCCLKEN, 1);
+ }
+
+ if (reg_mask != 0) {
+ rc = pmic_write_reg(REG_AUDIO_STEREO_DAC,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ stDAC.config &= ~config;
+ }
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the current Stereo DAC options.
+ *
+ * Get the current Stereo DAC options.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The current set of Stereo DAC options.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC options were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Stereo DAC options could not be
+ * retrieved.
+ */
+PMIC_STATUS pmic_audio_stdac_get_config(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_STDAC_CONFIG * const config)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE) &&
+ (config != (PMIC_AUDIO_STDAC_CONFIG *) NULL)) {
+ *config = stDAC.config;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * Audio input section configuration.
+ *************************************************************************
+ */
+
+/*!
+ * @name Audio Input Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC audio
+ * input hardware.
+ */
+/*@{*/
+
+/*!
+ * @brief Set/Enable the audio input section options.
+ *
+ * Set or enable various audio input section options. The only available
+ * option right now is to enable the automatic disabling of the microphone
+ * input amplifiers when a microphone/headset is inserted or removed.
+ * NOT SUPPORTED BY MC13783
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The audio input section options to enable.
+ *
+ * @retval PMIC_SUCCESS If the audio input section options were
+ * successfully configured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or audio input section
+ * options were invalid.
+ * @retval PMIC_ERROR If the audio input section options could
+ * not be successfully set/enabled.
+ */
+PMIC_STATUS pmic_audio_input_set_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_INPUT_CONFIG config)
+{
+ PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+ return rc;
+}
+
+/*!
+ * @brief Clear/Disable the audio input section options.
+ *
+ * Clear or disable various audio input section options.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The audio input section options to be
+ * cleared/disabled.
+ * NOT SUPPORTED BY MC13783
+ *
+ * @retval PMIC_SUCCESS If the audio input section options were
+ * successfully cleared/disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or the audio input section
+ * options were invalid.
+ * @retval PMIC_ERROR If the audio input section options could
+ * not be cleared/disabled.
+ */
+PMIC_STATUS pmic_audio_input_clear_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_INPUT_CONFIG config)
+{
+ PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+ return rc;
+
+}
+
+/*!
+ * @brief Get the current audio input section options.
+ *
+ * Get the current audio input section options.
+ *
+ * @param[in] handle Device handle from pmic_audio_open() call.
+ * @param[out] config The current set of audio input section options.
+ * NOT SUPPORTED BY MC13783
+ *
+ * @retval PMIC_SUCCESS If the audio input section options were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the audio input section options could
+ * not be retrieved.
+ */
+PMIC_STATUS pmic_audio_input_get_config(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_INPUT_CONFIG * const config)
+{
+ PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+ return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * Audio recording using the Voice CODEC.
+ *************************************************************************
+ */
+
+/*!
+ * @name Audio Recording Using the Voice CODEC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Voice CODEC
+ * to perform audio recording.
+ */
+/*@{*/
+
+/*!
+ * @brief Select the microphone inputs to be used for Voice CODEC recording.
+ *
+ * Select left (mc13783-only) and right microphone inputs for Voice CODEC
+ * recording. It is possible to disable or not use a particular microphone
+ * input channel by specifying NO_MIC as a parameter.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param leftChannel Select the left microphone input channel.
+ * @param rightChannel Select the right microphone input channel.
+ *
+ * @retval PMIC_SUCCESS If the microphone input channels were
+ * successfully enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or microphone input ports
+ * were invalid.
+ * @retval PMIC_ERROR If the microphone input channels could
+ * not be successfully enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_mic(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_INPUT_PORT leftChannel,
+ const PMIC_AUDIO_INPUT_PORT rightChannel)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ if (!((leftChannel == NO_MIC) || (leftChannel == MIC1_LEFT))) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else if (!((rightChannel == NO_MIC)
+ || (rightChannel == MIC1_RIGHT_MIC_MONO)
+ || (rightChannel == TXIN_EXT)
+ || (rightChannel == MIC2_AUX))) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else {
+ if (leftChannel == NO_MIC) {
+ } else { /* Left channel MIC enable */
+ reg_mask = SET_BITS(regAUDIO_TX, AMC1LEN, 1) |
+ SET_BITS(regAUDIO_TX, RXINREC, 1);
+ reg_write = SET_BITS(regAUDIO_TX, AMC1LEN, 1) |
+ SET_BITS(regAUDIO_TX, RXINREC, 0);
+ }
+ /*For right channel enable one and clear the other two as well as RXINREC */
+ if (rightChannel == NO_MIC) {
+ } else if (rightChannel == MIC1_RIGHT_MIC_MONO) {
+ reg_mask |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
+ SET_BITS(regAUDIO_TX, RXINREC, 1) |
+ SET_BITS(regAUDIO_TX, AMC2EN, 1) |
+ SET_BITS(regAUDIO_TX, ATXINEN, 1);
+ reg_write |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
+ SET_BITS(regAUDIO_TX, RXINREC, 0) |
+ SET_BITS(regAUDIO_TX, AMC2EN, 0) |
+ SET_BITS(regAUDIO_TX, ATXINEN, 0);
+ } else if (rightChannel == MIC2_AUX) {
+ reg_mask |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
+ SET_BITS(regAUDIO_TX, RXINREC, 1) |
+ SET_BITS(regAUDIO_TX, AMC2EN, 1) |
+ SET_BITS(regAUDIO_TX, ATXINEN, 1);
+ reg_write |= SET_BITS(regAUDIO_TX, AMC1REN, 0) |
+ SET_BITS(regAUDIO_TX, RXINREC, 0) |
+ SET_BITS(regAUDIO_TX, AMC2EN, 1) |
+ SET_BITS(regAUDIO_TX, ATXINEN, 0);
+ } else { /* TX line in */
+ reg_mask |= SET_BITS(regAUDIO_TX, AMC1REN, 1) |
+ SET_BITS(regAUDIO_TX, RXINREC, 1) |
+ SET_BITS(regAUDIO_TX, AMC2EN, 1) |
+ SET_BITS(regAUDIO_TX, ATXINEN, 1);
+ reg_write |= SET_BITS(regAUDIO_TX, AMC1REN, 0) |
+ SET_BITS(regAUDIO_TX, RXINREC, 0) |
+ SET_BITS(regAUDIO_TX, AMC2EN, 0) |
+ SET_BITS(regAUDIO_TX, ATXINEN, 1);
+ }
+
+ if (reg_mask == 0) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_TX,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug
+ ("MIC inputs configured successfully\n");
+ vCodec.leftChannelMic.mic = leftChannel;
+ vCodec.rightChannelMic.mic =
+ rightChannel;
+
+ }
+ }
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the current microphone inputs being used for Voice CODEC
+ * recording.
+ *
+ * Get the left (mc13783-only) and right microphone inputs currently being
+ * used for Voice CODEC recording.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param leftChannel The left microphone input channel.
+ * @param rightChannel The right microphone input channel.
+ *
+ * @retval PMIC_SUCCESS If the microphone input channels were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the microphone input channels could
+ * not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_mic(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_INPUT_PORT * const leftChannel,
+ PMIC_AUDIO_INPUT_PORT *
+ const rightChannel)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE) &&
+ (leftChannel != (PMIC_AUDIO_INPUT_PORT *) NULL) &&
+ (rightChannel != (PMIC_AUDIO_INPUT_PORT *) NULL)) {
+ *leftChannel = vCodec.leftChannelMic.mic;
+ *rightChannel = vCodec.rightChannelMic.mic;
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+ return rc;
+}
+
+/*!
+ * @brief Enable/disable the microphone input.
+ *
+ * This function enables/disables the current microphone input channel. The
+ * input amplifier is automatically turned off when the microphone input is
+ * disabled.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param leftChannel The left microphone input channel state.
+ * @param rightChannel the right microphone input channel state.
+ *
+ * @retval PMIC_SUCCESS If the microphone input channels were
+ * successfully reconfigured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or microphone input states
+ * were invalid.
+ * @retval PMIC_ERROR If the microphone input channels could
+ * not be reconfigured.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_mic_on_off(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_INPUT_MIC_STATE
+ leftChannel,
+ const PMIC_AUDIO_INPUT_MIC_STATE
+ rightChannel)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+ unsigned int curr_left = 0;
+ unsigned int curr_right = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ curr_left = vCodec.leftChannelMic.mic;
+ curr_right = vCodec.rightChannelMic.mic;
+ if ((curr_left == NO_MIC) && (curr_right == NO_MIC)) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else {
+ if (curr_left == MIC1_LEFT) {
+ if ((leftChannel == MICROPHONE_ON) &&
+ (vCodec.leftChannelMic.micOnOff ==
+ MICROPHONE_OFF)) {
+ /* Enable the microphone */
+ reg_mask |=
+ SET_BITS(regAUDIO_TX, AMC1LEN,
+ 1) | SET_BITS(regAUDIO_TX,
+ RXINREC, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC1LEN,
+ 1) | SET_BITS(regAUDIO_TX,
+ RXINREC, 0);
+
+ } else if ((leftChannel == MICROPHONE_OFF) &&
+ (vCodec.leftChannelMic.micOnOff ==
+ MICROPHONE_ON)) {
+ /* Disable the microphone */
+ reg_mask |=
+ SET_BITS(regAUDIO_TX, AMC1LEN,
+ 1) | SET_BITS(regAUDIO_TX,
+ RXINREC, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC1LEN,
+ 0) | SET_BITS(regAUDIO_TX,
+ RXINREC, 0);
+
+ } else {
+ /* Both are in same state . Nothing to be done */
+ }
+
+ }
+ if (curr_right == MIC1_RIGHT_MIC_MONO) {
+ if ((rightChannel == MICROPHONE_ON) &&
+ (vCodec.leftChannelMic.micOnOff ==
+ MICROPHONE_OFF)) {
+ /* Enable the microphone */
+ reg_mask |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 1) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 1) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 1) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 1) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 0) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 0) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 0);
+ } else if ((rightChannel == MICROPHONE_OFF)
+ && (vCodec.leftChannelMic.micOnOff ==
+ MICROPHONE_ON)) {
+ /* Disable the microphone */
+ reg_mask |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 1) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 1) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 1) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 0) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 0) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 0) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 0);
+ } else {
+ /* Both are in same state . Nothing to be done */
+ }
+ } else if (curr_right == MIC2_AUX) {
+ if ((rightChannel == MICROPHONE_ON)
+ && (vCodec.leftChannelMic.micOnOff ==
+ MICROPHONE_OFF)) {
+ /* Enable the microphone */
+ reg_mask |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 1) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 1) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 1) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 0) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 0) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 1) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 0);
+ } else if ((rightChannel == MICROPHONE_OFF)
+ && (vCodec.leftChannelMic.micOnOff ==
+ MICROPHONE_ON)) {
+ /* Disable the microphone */
+ reg_mask |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 1) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 1) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 1) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 0) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 0) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 0) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 0);
+ } else {
+ /* Both are in same state . Nothing to be done */
+ }
+ } else if (curr_right == TXIN_EXT) {
+ if ((rightChannel == MICROPHONE_ON)
+ && (vCodec.leftChannelMic.micOnOff ==
+ MICROPHONE_OFF)) {
+ /* Enable the microphone */
+ reg_mask |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 1) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 1) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 1) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 0) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 0) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 0) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 1);
+ } else if ((rightChannel == MICROPHONE_OFF)
+ && (vCodec.leftChannelMic.micOnOff ==
+ MICROPHONE_ON)) {
+ /* Disable the microphone */
+ reg_mask |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 1) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 1) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 1) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC1REN,
+ 0) | SET_BITS(regAUDIO_TX,
+ RXINREC,
+ 0) |
+ SET_BITS(regAUDIO_TX, AMC2EN,
+ 0) | SET_BITS(regAUDIO_TX,
+ ATXINEN, 0);
+ } else {
+ /* Both are in same state . Nothing to be done */
+ }
+ }
+ if (reg_mask == 0) {
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_TX,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug
+ ("MIC states configured successfully\n");
+ vCodec.leftChannelMic.micOnOff =
+ leftChannel;
+ vCodec.rightChannelMic.micOnOff =
+ rightChannel;
+ }
+ }
+ }
+
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Return the current state of the microphone inputs.
+ *
+ * This function returns the current state (on/off) of the microphone
+ * input channels.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param leftChannel The current left microphone input channel
+ * state.
+ * @param rightChannel the current right microphone input channel
+ * state.
+ *
+ * @retval PMIC_SUCCESS If the microphone input channel states
+ * were successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the microphone input channel states
+ * could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_mic_on_off(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_INPUT_MIC_STATE *
+ const leftChannel,
+ PMIC_AUDIO_INPUT_MIC_STATE *
+ const rightChannel)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE) &&
+ (leftChannel != (PMIC_AUDIO_INPUT_MIC_STATE *) NULL) &&
+ (rightChannel != (PMIC_AUDIO_INPUT_MIC_STATE *) NULL)) {
+ *leftChannel = vCodec.leftChannelMic.micOnOff;
+ *rightChannel = vCodec.rightChannelMic.micOnOff;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Set the microphone input amplifier mode and gain level.
+ *
+ * This function sets the current microphone input amplifier operating mode
+ * and gain level.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param leftChannelMode The left microphone input amplifier mode.
+ * @param leftChannelGain The left microphone input amplifier gain level.
+ * @param rightChannelMode The right microphone input amplifier mode.
+ * @param rightChannelGain The right microphone input amplifier gain
+ * level.
+ *
+ * @retval PMIC_SUCCESS If the microphone input amplifiers were
+ * successfully reconfigured.
+ * @retval PMIC_PARAMETER_ERROR If the handle or microphone input amplifier
+ * modes or gain levels were invalid.
+ * @retval PMIC_ERROR If the microphone input amplifiers could
+ * not be reconfigured.
+ */
+PMIC_STATUS pmic_audio_vcodec_set_record_gain(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_MIC_AMP_MODE
+ leftChannelMode,
+ const PMIC_AUDIO_MIC_GAIN
+ leftChannelGain,
+ const PMIC_AUDIO_MIC_AMP_MODE
+ rightChannelMode,
+ const PMIC_AUDIO_MIC_GAIN
+ rightChannelGain)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ if (!(((leftChannelGain >= MIC_GAIN_MINUS_8DB)
+ && (leftChannelGain <= MIC_GAIN_PLUS_23DB))
+ && ((rightChannelGain >= MIC_GAIN_MINUS_8DB)
+ && (rightChannelGain <= MIC_GAIN_PLUS_23DB)))) {
+ rc = PMIC_PARAMETER_ERROR;
+ pr_debug("VCODEC set record gain - wrong gain value\n");
+ } else if (((leftChannelMode != AMP_OFF)
+ && (leftChannelMode != VOLTAGE_TO_VOLTAGE)
+ && (leftChannelMode != CURRENT_TO_VOLTAGE))
+ || ((rightChannelMode != VOLTAGE_TO_VOLTAGE)
+ && (rightChannelMode != CURRENT_TO_VOLTAGE)
+ && (rightChannelMode != AMP_OFF))) {
+ rc = PMIC_PARAMETER_ERROR;
+ pr_debug("VCODEC set record gain - wrong amp mode\n");
+ } else {
+ if (vCodec.leftChannelMic.mic == MIC1_LEFT) {
+ reg_mask = SET_BITS(regAUDIO_TX, AMC1LITOV, 1) |
+ SET_BITS(regAUDIO_TX, PGATXL, 31);
+ if (leftChannelMode == VOLTAGE_TO_VOLTAGE) {
+ reg_write =
+ SET_BITS(regAUDIO_TX, AMC1LITOV, 0);
+ } else {
+ reg_write =
+ SET_BITS(regAUDIO_TX, AMC1LITOV, 1);
+ }
+ reg_write |=
+ SET_BITS(regAUDIO_TX, PGATXL,
+ leftChannelGain);
+ }
+ if (vCodec.rightChannelMic.mic == MIC1_RIGHT_MIC_MONO) {
+ reg_mask |=
+ SET_BITS(regAUDIO_TX, AMC1RITOV,
+ 1) | SET_BITS(regAUDIO_TX, PGATXR,
+ 31);
+ if (rightChannelMode == VOLTAGE_TO_VOLTAGE) {
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC1RITOV, 0);
+ } else {
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC1RITOV, 1);
+ }
+ reg_write |=
+ SET_BITS(regAUDIO_TX, PGATXR,
+ rightChannelGain);
+ } else if (vCodec.rightChannelMic.mic == MIC2_AUX) {
+ reg_mask |= SET_BITS(regAUDIO_TX, AMC2ITOV, 1);
+ reg_mask |= SET_BITS(regAUDIO_TX, PGATXR, 31);
+ if (rightChannelMode == VOLTAGE_TO_VOLTAGE) {
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC2ITOV, 0);
+ } else {
+ reg_write |=
+ SET_BITS(regAUDIO_TX, AMC2ITOV, 1);
+ }
+ reg_write |=
+ SET_BITS(regAUDIO_TX, PGATXR,
+ rightChannelGain);
+ } else if (vCodec.rightChannelMic.mic == TXIN_EXT) {
+ reg_mask |= SET_BITS(regAUDIO_TX, PGATXR, 31);
+ /* No current to voltage option for TX IN amplifier */
+ reg_write |=
+ SET_BITS(regAUDIO_TX, PGATXR,
+ rightChannelGain);
+ }
+
+ if (reg_mask == 0) {
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_TX,
+ reg_write, reg_mask);
+ reg_write =
+ SET_BITS(regAUDIO_TX, PGATXL,
+ leftChannelGain);
+ reg_mask = SET_BITS(regAUDIO_TX, PGATXL, 31);
+ rc = pmic_write_reg(REG_AUDIO_TX,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("MIC amp mode and gain set\n");
+ vCodec.leftChannelMic.ampMode =
+ leftChannelMode;
+ vCodec.leftChannelMic.gain =
+ leftChannelGain;
+ vCodec.rightChannelMic.ampMode =
+ rightChannelMode;
+ vCodec.rightChannelMic.gain =
+ rightChannelGain;
+
+ }
+ }
+ }
+ }
+
+ /* Exit critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the current microphone input amplifier mode and gain level.
+ *
+ * This function gets the current microphone input amplifier operating mode
+ * and gain level.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param leftChannelMode The left microphone input amplifier mode.
+ * @param leftChannelGain The left microphone input amplifier gain level.
+ * @param rightChannelMode The right microphone input amplifier mode.
+ * @param rightChannelGain The right microphone input amplifier gain
+ * level.
+ *
+ * @retval PMIC_SUCCESS If the microphone input amplifier modes
+ * and gain levels were successfully
+ * retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the microphone input amplifier modes
+ * and gain levels could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_vcodec_get_record_gain(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_MIC_AMP_MODE *
+ const leftChannelMode,
+ PMIC_AUDIO_MIC_GAIN *
+ const leftChannelGain,
+ PMIC_AUDIO_MIC_AMP_MODE *
+ const rightChannelMode,
+ PMIC_AUDIO_MIC_GAIN *
+ const rightChannelGain)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE) &&
+ (leftChannelMode != (PMIC_AUDIO_MIC_AMP_MODE *) NULL) &&
+ (leftChannelGain != (PMIC_AUDIO_MIC_GAIN *) NULL) &&
+ (rightChannelMode != (PMIC_AUDIO_MIC_AMP_MODE *) NULL) &&
+ (rightChannelGain != (PMIC_AUDIO_MIC_GAIN *) NULL)) {
+ *leftChannelMode = vCodec.leftChannelMic.ampMode;
+ *leftChannelGain = vCodec.leftChannelMic.gain;
+ *rightChannelMode = vCodec.rightChannelMic.ampMode;
+ *rightChannelGain = vCodec.rightChannelMic.gain;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Enable a microphone bias circuit.
+ *
+ * This function enables one of the available microphone bias circuits.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param biasCircuit The microphone bias circuit to be enabled.
+ *
+ * @retval PMIC_SUCCESS If the microphone bias circuit was
+ * successfully enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or selected microphone bias
+ * circuit was invalid.
+ * @retval PMIC_ERROR If the microphone bias circuit could not
+ * be enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_enable_micbias(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_MIC_BIAS
+ biasCircuit)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ if (biasCircuit & MIC_BIAS1) {
+ reg_write = SET_BITS(regAUDIO_TX, MC1BEN, 1);
+ reg_mask = SET_BITS(regAUDIO_TX, MC1BEN, 1);
+ }
+ if (biasCircuit & MIC_BIAS2) {
+ reg_write |= SET_BITS(regAUDIO_TX, MC2BEN, 1);
+ reg_mask |= SET_BITS(regAUDIO_TX, MC2BEN, 1);
+ }
+ if (reg_mask != 0) {
+ rc = pmic_write_reg(REG_AUDIO_TX, reg_write, reg_mask);
+ }
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief Disable a microphone bias circuit.
+ *
+ * This function disables one of the available microphone bias circuits.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param biasCircuit The microphone bias circuit to be disabled.
+ *
+ * @retval PMIC_SUCCESS If the microphone bias circuit was
+ * successfully disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or selected microphone bias
+ * circuit was invalid.
+ * @retval PMIC_ERROR If the microphone bias circuit could not
+ * be disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_disable_micbias(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_MIC_BIAS
+ biasCircuit)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ if (biasCircuit & MIC_BIAS1) {
+ reg_mask = SET_BITS(regAUDIO_TX, MC1BEN, 1);
+ }
+
+ if (biasCircuit & MIC_BIAS2) {
+ reg_mask |= SET_BITS(regAUDIO_TX, MC2BEN, 1);
+ }
+
+ if (reg_mask != 0) {
+ rc = pmic_write_reg(REG_AUDIO_TX, reg_write, reg_mask);
+ }
+ }
+
+ return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * Audio Playback Using the Voice CODEC.
+ *************************************************************************
+ */
+
+/*!
+ * @name Audio Playback Using the Voice CODEC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Voice CODEC
+ * to perform audio playback.
+ */
+/*@{*/
+
+/*!
+ * @brief Configure and enable the Voice CODEC mixer.
+ *
+ * This function configures and enables the Voice CODEC mixer.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param rxSecondaryTimeslot The timeslot used for the secondary audio
+ * channel.
+ * @param gainIn The secondary audio channel gain level.
+ * @param gainOut The mixer output gain level.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC mixer was successfully
+ * configured and enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or mixer configuration
+ * was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC mixer could not be
+ * reconfigured or enabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_enable_mixer(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_VCODEC_TIMESLOT
+ rxSecondaryTimeslot,
+ const PMIC_AUDIO_VCODEC_MIX_IN_GAIN
+ gainIn,
+ const PMIC_AUDIO_VCODEC_MIX_OUT_GAIN
+ gainOut)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ if (!((rxSecondaryTimeslot >= USE_TS0)
+ && (rxSecondaryTimeslot <= USE_TS3))) {
+ pr_debug
+ ("VCODEC enable mixer - wrong sec rx timeslot\n");
+ } else if (!((gainIn >= VCODEC_NO_MIX)
+ && (gainIn <= VCODEC_MIX_IN_MINUS_12DB))) {
+ pr_debug("VCODEC enable mixer - wrong mix in gain\n");
+
+ } else if (!((gainOut >= VCODEC_MIX_OUT_0DB)
+ && (gainOut <= VCODEC_MIX_OUT_MINUS_6DB))) {
+ pr_debug("VCODEC enable mixer - wrong mix out gain\n");
+ } else {
+
+ reg_mask = SET_BITS(regSSI_NETWORK, CDCRXSECSLOT, 3) |
+ SET_BITS(regSSI_NETWORK, CDCRXSECGAIN, 3) |
+ SET_BITS(regSSI_NETWORK, CDCSUMGAIN, 1);
+ reg_write =
+ SET_BITS(regSSI_NETWORK, CDCRXSECSLOT,
+ rxSecondaryTimeslot) |
+ SET_BITS(regSSI_NETWORK, CDCRXSECGAIN,
+ gainIn) | SET_BITS(regSSI_NETWORK,
+ CDCSUMGAIN, gainOut);
+ rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+ reg_write, reg_mask);
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Vcodec mixer enabled\n");
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief Disable the Voice CODEC mixer.
+ *
+ * This function disables the Voice CODEC mixer.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the Voice CODEC mixer was successfully
+ * disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Voice CODEC mixer could not be
+ * disabled.
+ */
+PMIC_STATUS pmic_audio_vcodec_disable_mixer(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_mask;
+
+ if ((handle == vCodec.handle) && (vCodec.handleState == HANDLE_IN_USE)) {
+ reg_mask = SET_BITS(regSSI_NETWORK, CDCRXSECGAIN, 1);
+ rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+ VCODEC_NO_MIX, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Vcodec mixer disabled\n");
+ }
+
+ }
+
+ return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * Audio Playback Using the Stereo DAC.
+ *************************************************************************
+ */
+
+/*!
+ * @name Audio Playback Using the Stereo DAC Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC Stereo DAC
+ * to perform audio playback.
+ */
+/*@{*/
+
+/*!
+ * @brief Configure and enable the Stereo DAC mixer.
+ *
+ * This function configures and enables the Stereo DAC mixer.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param rxSecondaryTimeslot The timeslot used for the secondary audio
+ * channel.
+ * @param gainIn The secondary audio channel gain level.
+ * @param gainOut The mixer output gain level.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC mixer was successfully
+ * configured and enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or mixer configuration
+ * was invalid.
+ * @retval PMIC_ERROR If the Stereo DAC mixer could not be
+ * reconfigured or enabled.
+ */
+PMIC_STATUS pmic_audio_stdac_enable_mixer(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_STDAC_TIMESLOTS
+ rxSecondaryTimeslot,
+ const PMIC_AUDIO_STDAC_MIX_IN_GAIN
+ gainIn,
+ const PMIC_AUDIO_STDAC_MIX_OUT_GAIN
+ gainOut)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+ if (!((rxSecondaryTimeslot >= USE_TS0_TS1)
+ && (rxSecondaryTimeslot <= USE_TS6_TS7))) {
+ rc = PMIC_PARAMETER_ERROR;
+ pr_debug("STDAC enable mixer - wrong sec timeslot\n");
+ } else if (!((gainIn >= STDAC_NO_MIX)
+ && (gainIn <= STDAC_MIX_IN_MINUS_12DB))) {
+ rc = PMIC_PARAMETER_ERROR;
+ pr_debug("STDAC enable mixer - wrong mix in gain\n");
+ } else if (!((gainOut >= STDAC_MIX_OUT_0DB)
+ && (gainOut <= STDAC_MIX_OUT_MINUS_6DB))) {
+ rc = PMIC_PARAMETER_ERROR;
+ pr_debug("STDAC enable mixer - wrong mix out gain\n");
+ } else {
+
+ reg_mask = SET_BITS(regSSI_NETWORK, STDCRXSECSLOT, 3) |
+ SET_BITS(regSSI_NETWORK, STDCRXSECGAIN, 3) |
+ SET_BITS(regSSI_NETWORK, STDCSUMGAIN, 1);
+ reg_write =
+ SET_BITS(regSSI_NETWORK, STDCRXSECSLOT,
+ rxSecondaryTimeslot) |
+ SET_BITS(regSSI_NETWORK, STDCRXSECGAIN,
+ gainIn) | SET_BITS(regSSI_NETWORK,
+ STDCSUMGAIN, gainOut);
+ rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+ reg_write, reg_mask);
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("STDAC mixer enabled\n");
+ }
+ }
+
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief Disable the Stereo DAC mixer.
+ *
+ * This function disables the Stereo DAC mixer.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the Stereo DAC mixer was successfully
+ * disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the Stereo DAC mixer could not be
+ * disabled.
+ */
+PMIC_STATUS pmic_audio_stdac_disable_mixer(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ const unsigned int reg_write = 0;
+ const unsigned int reg_mask =
+ SET_BITS(regSSI_NETWORK, STDCRXSECGAIN, 1);
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+ rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK, reg_write, reg_mask);
+ }
+
+ return rc;
+}
+
+/*@}*/
+
+/*************************************************************************
+ * Audio Output Control
+ *************************************************************************
+ */
+
+/*!
+ * @name Audio Output Section Setup and Configuration APIs
+ * Functions for general setup and configuration of the PMIC audio output
+ * section to support playback.
+ */
+/*@{*/
+
+/*!
+ * @brief Select the audio output ports.
+ *
+ * This function selects the audio output ports to be used. This also enables
+ * the appropriate output amplifiers.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param port The audio output ports to be used.
+ *
+ * @retval PMIC_SUCCESS If the audio output ports were successfully
+ * acquired.
+ * @retval PMIC_PARAMETER_ERROR If the handle or output ports were
+ * invalid.
+ * @retval PMIC_ERROR If the audio output ports could not be
+ * acquired.
+ */
+PMIC_STATUS pmic_audio_output_set_port(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_PORT port)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((port == MONO_ALERT) || (port == MONO_EXTOUT)) {
+ rc = PMIC_NOT_SUPPORTED;
+ } else {
+ if (((handle == stDAC.handle)
+ && (stDAC.handleState == HANDLE_IN_USE))
+ || ((handle == extStereoIn.handle)
+ && (extStereoIn.handleState == HANDLE_IN_USE))
+ || ((handle == vCodec.handle)
+ && (vCodec.handleState == HANDLE_IN_USE)
+ && (audioOutput.vCodecOut == VCODEC_MIXER_OUT))) {
+ /* Stereo signal and MIXER source needs to be routed to the port
+ / Avoid Codec direct out */
+
+ if (port & MONO_SPEAKER) {
+ reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
+ SET_BITS(regAUDIO_RX_0, ASPSEL, 1);
+ reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
+ SET_BITS(regAUDIO_RX_0, ASPSEL, 1);
+ }
+ if (port & MONO_LOUDSPEAKER) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
+ SET_BITS(regAUDIO_RX_0, ALSPREF, 1) |
+ SET_BITS(regAUDIO_RX_0, ALSPSEL, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ALSPEN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ ALSPREF,
+ 1) |
+ SET_BITS(regAUDIO_RX_0, ALSPSEL, 1);
+ }
+ if (port & STEREO_HEADSET_LEFT) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1) |
+ SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, AHSLEN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ AHSSEL, 1);
+ }
+ if (port & STEREO_HEADSET_RIGHT) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1) |
+ SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, AHSREN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ AHSSEL, 1);
+ }
+ if (port & STEREO_OUT_LEFT) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ ARXOUTSEL, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ ARXOUTSEL, 1);
+ }
+ if (port & STEREO_OUT_RIGHT) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTREN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ ARXOUTSEL, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTREN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ ARXOUTSEL, 1);
+ }
+ if (port & STEREO_LEFT_LOW_POWER) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, LSPLEN, 1);
+
+ reg_write |= SET_BITS(regAUDIO_RX_0, LSPLEN, 1);
+ }
+ } else if ((handle == vCodec.handle)
+ && (vCodec.handleState == HANDLE_IN_USE)
+ && (audioOutput.vCodecOut = VCODEC_DIRECT_OUT)) {
+ if (port & MONO_SPEAKER) {
+ reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
+ SET_BITS(regAUDIO_RX_0, ASPSEL, 1);
+ reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 1) |
+ SET_BITS(regAUDIO_RX_0, ASPSEL, 0);
+ }
+ if (port & MONO_LOUDSPEAKER) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
+ SET_BITS(regAUDIO_RX_0, ALSPREF, 1) |
+ SET_BITS(regAUDIO_RX_0, ALSPSEL, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ALSPEN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ ALSPREF,
+ 1) |
+ SET_BITS(regAUDIO_RX_0, ALSPSEL, 0);
+ }
+
+ if (port & STEREO_HEADSET_LEFT) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1) |
+ SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, AHSLEN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ AHSSEL, 0);
+ }
+ if (port & STEREO_HEADSET_RIGHT) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1) |
+ SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, AHSREN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ AHSSEL, 0);
+ }
+ if (port & STEREO_OUT_LEFT) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ ARXOUTSEL, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTLEN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ ARXOUTSEL, 0);
+ }
+ if (port & STEREO_OUT_RIGHT) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTREN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ ARXOUTSEL, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTREN,
+ 1) | SET_BITS(regAUDIO_RX_0,
+ ARXOUTSEL, 0);
+ }
+ if (port & MONO_CDCOUT) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, CDCOUTEN, 1);
+
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, CDCOUTEN, 1);
+ }
+ }
+
+ if (reg_mask == 0) {
+
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_RX_0,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("output ports enabled\n");
+ audioOutput.outputPort = port;
+
+ }
+ }
+ }
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Deselect/disable the audio output ports.
+ *
+ * This function disables the audio output ports that were previously enabled
+ * by calling pmic_audio_output_set_port().
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param port The audio output ports to be disabled.
+ *
+ * @retval PMIC_SUCCESS If the audio output ports were successfully
+ * disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or output ports were
+ * invalid.
+ * @retval PMIC_ERROR If the audio output ports could not be
+ * disabled.
+ */
+PMIC_STATUS pmic_audio_output_clear_port(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_PORT port)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((port == MONO_ALERT) || (port == MONO_EXTOUT)) {
+ rc = PMIC_NOT_SUPPORTED;
+ } else {
+ if (((handle == stDAC.handle)
+ && (stDAC.handleState == HANDLE_IN_USE))
+ || ((handle == extStereoIn.handle)
+ && (extStereoIn.handleState == HANDLE_IN_USE))
+ || ((handle == vCodec.handle)
+ && (vCodec.handleState == HANDLE_IN_USE)
+ && (audioOutput.vCodecOut = VCODEC_MIXER_OUT))) {
+ /* Stereo signal and MIXER source needs to be routed to the port /
+ Avoid Codec direct out */
+ if (port & MONO_SPEAKER) {
+ reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 0);
+ }
+ if (port & MONO_LOUDSPEAKER) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
+ SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
+
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ALSPEN,
+ 0) | SET_BITS(regAUDIO_RX_0,
+ ALSPREF, 0);
+
+ }
+ if (port & STEREO_HEADSET_LEFT) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, AHSLEN, 0);
+ }
+ if (port & STEREO_HEADSET_RIGHT) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, AHSREN, 0);
+ }
+ if (port & STEREO_OUT_LEFT) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 0);
+ }
+ if (port & STEREO_OUT_RIGHT) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTREN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTREN, 0);
+ }
+ if (port & STEREO_LEFT_LOW_POWER) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, LSPLEN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, LSPLEN, 0);
+ }
+ } else if ((handle == vCodec.handle)
+ && (vCodec.handleState == HANDLE_IN_USE)
+ && (audioOutput.vCodecOut = VCODEC_DIRECT_OUT)) {
+ if (port & MONO_SPEAKER) {
+ reg_mask = SET_BITS(regAUDIO_RX_0, ASPEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_0, ASPEN, 0);
+ }
+ if (port & MONO_LOUDSPEAKER) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, ALSPEN, 1) |
+ SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ALSPEN,
+ 0) | SET_BITS(regAUDIO_RX_0,
+ ALSPREF, 0);
+ }
+ if (port & STEREO_HEADSET_LEFT) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, AHSLEN, 0);
+ }
+ if (port & STEREO_HEADSET_RIGHT) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, AHSREN, 0);
+ }
+ if (port & STEREO_OUT_LEFT) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTLEN, 0);
+ }
+ if (port & STEREO_OUT_RIGHT) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTREN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, ARXOUTREN, 0);
+ }
+ if (port & MONO_CDCOUT) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, CDCOUTEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, CDCOUTEN, 0);
+ }
+ }
+#ifdef CONFIG_HEADSET_DETECT_ENABLE
+
+ if (port & STEREO_HEADSET_LEFT) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, AHSLEN, 0);
+ }
+ if (port & STEREO_HEADSET_RIGHT) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, AHSREN, 0);
+ }
+#endif
+
+ if (reg_mask == 0) {
+
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_RX_0,
+ reg_write, reg_mask);
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("output ports disabled\n");
+ audioOutput.outputPort &= ~port;
+ }
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the current audio output ports.
+ *
+ * This function retrieves the audio output ports that are currently being
+ * used.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param port The audio output ports currently being used.
+ *
+ * @retval PMIC_SUCCESS If the audio output ports were successfully
+ * retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the audio output ports could not be
+ * retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_port(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_OUTPUT_PORT * const port)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) ||
+ ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) ||
+ ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE))) &&
+ (port != (PMIC_AUDIO_OUTPUT_PORT *) NULL)) {
+ *port = audioOutput.outputPort;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Set the gain level for the external stereo inputs.
+ *
+ * This function sets the gain levels for the external stereo inputs.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param gain The external stereo input gain level.
+ *
+ * @retval PMIC_SUCCESS If the gain level was successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
+ * @retval PMIC_ERROR If the gain level could not be set.
+ */
+PMIC_STATUS pmic_audio_output_set_stereo_in_gain(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_STEREO_IN_GAIN
+ gain)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1) |
+ SET_BITS(regAUDIO_RX_1, ARXIN, 1);
+ unsigned int reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ /* The ARX amplifier for stereo is also enabled over here */
+
+ if ((gain == STEREO_IN_GAIN_0DB) || (gain == STEREO_IN_GAIN_PLUS_18DB)) {
+ if ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE)) {
+
+ if (gain == STEREO_IN_GAIN_0DB) {
+ reg_write |= SET_BITS(regAUDIO_RX_1, ARXIN, 1);
+ } else {
+ reg_write |= SET_BITS(regAUDIO_RX_1, ARXIN, 0);
+ }
+
+ rc = pmic_write_reg(REG_AUDIO_RX_1,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Ext stereo gain set\n");
+ extStereoIn.inputGain = gain;
+
+ }
+
+ } else {
+ rc = PMIC_PARAMETER_ERROR;
+ }
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief Get the current gain level for the external stereo inputs.
+ *
+ * This function retrieves the current gain levels for the external stereo
+ * inputs.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param gain The current external stereo input gain
+ * level.
+ *
+ * @retval PMIC_SUCCESS If the gain level was successfully
+ * retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the gain level could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_stereo_in_gain(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_STEREO_IN_GAIN *
+ const gain)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE) &&
+ (gain != (PMIC_AUDIO_STEREO_IN_GAIN *) NULL)) {
+ *gain = extStereoIn.inputGain;
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Set the output PGA gain level.
+ *
+ * This function sets the audio output PGA gain level.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param gain The output PGA gain level.
+ *
+ * @retval PMIC_SUCCESS If the gain level was successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
+ * @retval PMIC_ERROR If the gain level could not be set.
+ */
+PMIC_STATUS pmic_audio_output_set_pgaGain(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_PGA_GAIN gain)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = 0;
+ unsigned int reg_gain;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if (!((gain >= OUTPGA_GAIN_MINUS_33DB)
+ && (gain <= OUTPGA_GAIN_PLUS_6DB))) {
+ rc = PMIC_NOT_SUPPORTED;
+ pr_debug("output set PGA gain - wrong gain value\n");
+ } else {
+ reg_gain = gain + 2;
+ if ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE)) {
+ reg_mask = SET_BITS(regAUDIO_RX_1, ARXIN, 15) |
+ SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_1, ARXIN, reg_gain) |
+ SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+ } else if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) {
+ reg_mask = SET_BITS(regAUDIO_RX_1, PGARX, 15);
+ reg_write = SET_BITS(regAUDIO_RX_1, PGARX, reg_gain);
+ } else if ((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) {
+ reg_mask = SET_BITS(regAUDIO_RX_1, PGAST, 15);
+ reg_write = SET_BITS(regAUDIO_RX_1, PGAST, reg_gain);
+ }
+
+ if (reg_mask == 0) {
+
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_RX_1,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Output PGA gains set\n");
+
+ if (handle == stDAC.handle) {
+ audioOutput.stDacoutputPGAGain = gain;
+ } else if (handle == vCodec.handle) {
+ audioOutput.vCodecoutputPGAGain = gain;
+ } else {
+ audioOutput.extStereooutputPGAGain =
+ gain;
+ }
+ } else {
+ pr_debug
+ ("Error writing PGA gains to register\n");
+ }
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the output PGA gain level.
+ *
+ * This function retrieves the current audio output PGA gain level.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param gain The current output PGA gain level.
+ *
+ * @retval PMIC_SUCCESS If the gain level was successfully
+ * retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the gain level could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_pgaGain(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_OUTPUT_PGA_GAIN *
+ const gain)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if (gain != (PMIC_AUDIO_OUTPUT_PGA_GAIN *) NULL) {
+ if ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE)) {
+ *gain = audioOutput.extStereooutputPGAGain;
+ rc = PMIC_SUCCESS;
+ } else if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) {
+ *gain = audioOutput.vCodecoutputPGAGain;
+ rc = PMIC_SUCCESS;
+ } else if ((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) {
+ *gain = audioOutput.stDacoutputPGAGain;
+ rc = PMIC_SUCCESS;
+ } else {
+ rc = PMIC_PARAMETER_ERROR;
+ }
+ } else {
+ rc = PMIC_PARAMETER_ERROR;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Enable the output mixer.
+ *
+ * This function enables the output mixer for the audio stream that
+ * corresponds to the current handle (i.e., the Voice CODEC, Stereo DAC, or
+ * the external stereo inputs).
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the mixer was successfully enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the mixer could not be enabled.
+ */
+PMIC_STATUS pmic_audio_output_enable_mixer(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_mask = 0;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask_mix = 0;
+ unsigned int reg_write_mix = 0;
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if (((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE))) {
+ reg_mask = SET_BITS(regAUDIO_RX_1, PGASTEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_1, PGASTEN, 1);
+ reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 1);
+ reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 1);
+ } else if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) {
+ reg_mask = SET_BITS(regAUDIO_RX_1, PGARXEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_1, PGARXEN, 1);
+ audioOutput.vCodecOut = VCODEC_MIXER_OUT;
+
+ reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 1);
+ reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 1);
+ } else if ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE)) {
+ reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+ reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+ reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+ }
+
+ if (reg_mask == 0) {
+
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+
+ rc = pmic_write_reg(REG_AUDIO_RX_0,
+ reg_write_mix, reg_mask_mix);
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Output PGA mixers enabled\n");
+ rc = PMIC_SUCCESS;
+ }
+
+ } else {
+ pr_debug("Error writing mixer enable to register\n");
+ }
+
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief Disable the output mixer.
+ *
+ * This function disables the output mixer for the audio stream that
+ * corresponds to the current handle (i.e., the Voice CODEC, Stereo DAC, or
+ * the external stereo inputs).
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the mixer was successfully disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the mixer could not be disabled.
+ */
+PMIC_STATUS pmic_audio_output_disable_mixer(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_mask = 0;
+ unsigned int reg_write = 0;
+
+ unsigned int reg_mask_mix = 0;
+ unsigned int reg_write_mix = 0;
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+ if (((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE))) {
+ /*reg_mask = SET_BITS(regAUDIO_RX_1, PGASTEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_1, PGASTEN, 0); */
+
+ reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 1);
+ reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDSTDC, 0);
+ } else if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) {
+ reg_mask = SET_BITS(regAUDIO_RX_1, PGARXEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_1, PGARXEN, 0);
+ audioOutput.vCodecOut = VCODEC_DIRECT_OUT;
+
+ reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 1);
+ reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDCDC, 0);
+ } else if ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE)) {
+ /*reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 0); */
+
+ reg_mask_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+ reg_write_mix = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+ }
+
+ if (reg_mask == 0) {
+
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+
+ rc = pmic_write_reg(REG_AUDIO_RX_0,
+ reg_write_mix, reg_mask_mix);
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Output PGA mixers disabled\n");
+ }
+ }
+ }
+ return rc;
+}
+
+/*!
+ * @brief Configure and enable the output balance amplifiers.
+ *
+ * This function configures and enables the output balance amplifiers.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param leftGain The desired left channel gain level.
+ * @param rightGain The desired right channel gain level.
+ *
+ * @retval PMIC_SUCCESS If the output balance amplifiers were
+ * successfully configured and enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or gain levels were invalid.
+ * @retval PMIC_ERROR If the output balance amplifiers could not
+ * be reconfigured or enabled.
+ */
+PMIC_STATUS pmic_audio_output_set_balance(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_BALANCE_GAIN
+ leftGain,
+ const PMIC_AUDIO_OUTPUT_BALANCE_GAIN
+ rightGain)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_mask = 0;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask_ch = 0;
+ unsigned int reg_write_ch = 0;
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if (!((leftGain >= BAL_GAIN_MINUS_21DB) && (leftGain <= BAL_GAIN_0DB))) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else if (!((rightGain >= BAL_GAIN_MINUS_21DB)
+ && (rightGain <= BAL_GAIN_0DB))) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else {
+ if (((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) ||
+ ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) ||
+ ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE))) {
+ /* In mc13783 only one channel can be attenuated wrt the other.
+ * It is not possible to specify attenuation for both
+ * This function will return an error if both channels
+ * are required to be attenuated
+ * The BALLR bit is set/reset depending on whether leftGain
+ * or rightGain is specified*/
+ if ((rightGain == BAL_GAIN_0DB)
+ && (leftGain == BAL_GAIN_0DB)) {
+ /* Nothing to be done */
+ } else if ((rightGain != BAL_GAIN_0DB)
+ && (leftGain == BAL_GAIN_0DB)) {
+ /* Attenuate right channel */
+ reg_mask = SET_BITS(regAUDIO_RX_1, BAL, 7);
+ reg_mask_ch = SET_BITS(regAUDIO_RX_1, BALLR, 1);
+ reg_write =
+ SET_BITS(regAUDIO_RX_1, BAL,
+ (BAL_GAIN_0DB - rightGain));
+ /* The enum and the register values are reversed in order .. */
+ reg_write_ch =
+ SET_BITS(regAUDIO_RX_1, BALLR, 0);
+ /* BALLR = 0 selects right channel for atten */
+ } else if ((rightGain == BAL_GAIN_0DB)
+ && (leftGain != BAL_GAIN_0DB)) {
+ /* Attenuate left channel */
+
+ reg_mask = SET_BITS(regAUDIO_RX_1, BAL, 7);
+ reg_mask_ch = SET_BITS(regAUDIO_RX_1, BALLR, 1);
+ reg_write =
+ SET_BITS(regAUDIO_RX_1, BAL,
+ (BAL_GAIN_0DB - leftGain));
+ reg_write_ch =
+ SET_BITS(regAUDIO_RX_1, BALLR, 1);
+ /* BALLR = 1 selects left channel for atten */
+ } else {
+ rc = PMIC_PARAMETER_ERROR;
+ }
+
+ if ((reg_mask == 0) || (reg_mask_ch == 0)) {
+
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_RX_1,
+ reg_write_ch, reg_mask_ch);
+
+ if (rc == PMIC_SUCCESS) {
+ rc = pmic_write_reg(REG_AUDIO_RX_1,
+ reg_write,
+ reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug
+ ("Output balance attenuation set\n");
+ audioOutput.balanceLeftGain =
+ leftGain;
+ audioOutput.balanceRightGain =
+ rightGain;
+ }
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+/*!
+ * @brief Get the current output balance amplifier gain levels.
+ *
+ * This function retrieves the current output balance amplifier gain levels.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param leftGain The current left channel gain level.
+ * @param rightGain The current right channel gain level.
+ *
+ * @retval PMIC_SUCCESS If the output balance amplifier gain levels
+ * were successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the output balance amplifier gain levels
+ * could be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_balance(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_OUTPUT_BALANCE_GAIN *
+ const leftGain,
+ PMIC_AUDIO_OUTPUT_BALANCE_GAIN *
+ const rightGain)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) ||
+ ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) ||
+ ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE))) &&
+ ((leftGain != (PMIC_AUDIO_OUTPUT_BALANCE_GAIN *) NULL) &&
+ (rightGain != (PMIC_AUDIO_OUTPUT_BALANCE_GAIN *) NULL))) {
+ *leftGain = audioOutput.balanceLeftGain;
+ *rightGain = audioOutput.balanceRightGain;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Configure and enable the output mono adder.
+ *
+ * This function configures and enables the output mono adder.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param mode The desired mono adder operating mode.
+ *
+ * @retval PMIC_SUCCESS If the mono adder was successfully
+ * configured and enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle or mono adder mode was
+ * invalid.
+ * @retval PMIC_ERROR If the mono adder could not be reconfigured
+ * or enabled.
+ */
+PMIC_STATUS pmic_audio_output_enable_mono_adder(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_MONO_ADDER_MODE
+ mode)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_write = 0;
+ unsigned int reg_mask = SET_BITS(regAUDIO_RX_1, MONO, 3);
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if ((mode >= MONO_ADDER_OFF) && (mode <= STEREO_OPPOSITE_PHASE)) {
+ if (((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) ||
+ ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) ||
+ ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE))) {
+ if (mode == MONO_ADDER_OFF) {
+ reg_write = SET_BITS(regAUDIO_RX_1, MONO, 0);
+ } else if (mode == MONO_ADD_LEFT_RIGHT) {
+ reg_write = SET_BITS(regAUDIO_RX_1, MONO, 2);
+ } else if (mode == MONO_ADD_OPPOSITE_PHASE) {
+ reg_write = SET_BITS(regAUDIO_RX_1, MONO, 3);
+ } else { /* stereo opposite */
+
+ reg_write = SET_BITS(regAUDIO_RX_1, MONO, 1);
+ }
+
+ rc = pmic_write_reg(REG_AUDIO_RX_1,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Output mono adder mode set\n");
+
+ }
+
+ } else {
+ rc = PMIC_PARAMETER_ERROR;
+ }
+ } else {
+ rc = PMIC_PARAMETER_ERROR;
+ }
+ return rc;
+}
+
+/*!
+ * @brief Disable the output mono adder.
+ *
+ * This function disables the output mono adder.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the mono adder was successfully
+ * disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the mono adder could not be disabled.
+ */
+PMIC_STATUS pmic_audio_output_disable_mono_adder(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ const unsigned int reg_write = 0;
+ const unsigned int reg_mask = SET_BITS(regAUDIO_RX_1, MONO, 3);
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ if (((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) ||
+ ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) ||
+ ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE))) {
+ rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief Configure the mono adder output gain level.
+ *
+ * This function configures the mono adder output amplifier gain level.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param gain The desired output gain level.
+ *
+ * @retval PMIC_SUCCESS If the mono adder output amplifier gain
+ * level was successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle or gain level was invalid.
+ * @retval PMIC_ERROR If the mono adder output amplifier gain
+ * level could not be reconfigured.
+ */
+PMIC_STATUS pmic_audio_output_set_mono_adder_gain(const PMIC_AUDIO_HANDLE
+ handle,
+ const
+ PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN
+ gain)
+{
+ PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+ return rc;
+}
+
+/*!
+ * @brief Get the current mono adder output gain level.
+ *
+ * This function retrieves the current mono adder output amplifier gain level.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param gain The current output gain level.
+ *
+ * @retval PMIC_SUCCESS If the mono adder output amplifier gain
+ * level was successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the mono adder output amplifier gain
+ * level could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_mono_adder_gain(const PMIC_AUDIO_HANDLE
+ handle,
+ PMIC_AUDIO_MONO_ADDER_OUTPUT_GAIN
+ * const gain)
+{
+ PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+ return rc;
+}
+
+/*!
+ * @brief Set various audio output section options.
+ *
+ * This function sets one or more audio output section configuration
+ * options. The currently supported options include whether to disable
+ * the non-inverting mono speaker output, enabling the loudspeaker common
+ * bias circuit, enabling detection of headset insertion/removal, and
+ * whether to automatically disable the headset amplifiers when a headset
+ * insertion/removal has been detected.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The desired audio output section
+ * configuration options to be set.
+ *
+ * @retval PMIC_SUCCESS If the desired configuration options were
+ * all successfully set.
+ * @retval PMIC_PARAMETER_ERROR If the handle or configuration options
+ * were invalid.
+ * @retval PMIC_ERROR If the desired configuration options
+ * could not be set.
+ */
+PMIC_STATUS pmic_audio_output_set_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_CONFIG config)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_mask = 0;
+ unsigned int reg_write = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if (((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) ||
+ ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) ||
+ ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE))) {
+ if (config & MONO_SPEAKER_INVERT_OUT_ONLY) {
+ /* If this is one of the parameters */
+ rc = PMIC_NOT_SUPPORTED;
+ } else {
+ if (config & MONO_LOUDSPEAKER_COMMON_BIAS) {
+ reg_mask = SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
+ reg_write = SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
+ }
+ if (config & HEADSET_DETECT_ENABLE) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, HSDETEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, HSDETEN, 1);
+ }
+ if (config & STEREO_HEADSET_AMP_AUTO_DISABLE) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+ }
+
+ if (reg_mask == 0) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_RX_0,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Output config set\n");
+ audioOutput.config |= config;
+
+ }
+ }
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Clear various audio output section options.
+ *
+ * This function clears one or more audio output section configuration
+ * options.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The desired audio output section
+ * configuration options to be cleared.
+ *
+ * @retval PMIC_SUCCESS If the desired configuration options were
+ * all successfully cleared.
+ * @retval PMIC_PARAMETER_ERROR If the handle or configuration options
+ * were invalid.
+ * @retval PMIC_ERROR If the desired configuration options
+ * could not be cleared.
+ */
+PMIC_STATUS pmic_audio_output_clear_config(const PMIC_AUDIO_HANDLE handle,
+ const PMIC_AUDIO_OUTPUT_CONFIG
+ config)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ /*unsigned int reg_write_RX = 0;
+ unsigned int reg_mask_RX = 0;
+ unsigned int reg_write_TX = 0;
+ unsigned int reg_mask_TX = 0; */
+ unsigned int reg_mask = 0;
+ unsigned int reg_write = 0;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if (((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) ||
+ ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) ||
+ ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE))) {
+ if (config & MONO_SPEAKER_INVERT_OUT_ONLY) {
+ /* If this is one of the parameters */
+ rc = PMIC_NOT_SUPPORTED;
+ } else {
+ if (config & MONO_LOUDSPEAKER_COMMON_BIAS) {
+ reg_mask = SET_BITS(regAUDIO_RX_0, ALSPREF, 1);
+ reg_write = SET_BITS(regAUDIO_RX_0, ALSPREF, 0);
+ }
+
+ if (config & HEADSET_DETECT_ENABLE) {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, HSDETEN, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, HSDETEN, 0);
+ }
+
+ if (config & STEREO_HEADSET_AMP_AUTO_DISABLE) {
+ reg_mask |=
+ SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 1);
+ reg_write |=
+ SET_BITS(regAUDIO_RX_0, HSDETAUTOB, 0);
+ }
+
+ if (reg_mask == 0) {
+ rc = PMIC_PARAMETER_ERROR;
+ } else {
+ rc = pmic_write_reg(REG_AUDIO_RX_0,
+ reg_write, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Output config cleared\n");
+ audioOutput.config &= ~config;
+
+ }
+ }
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Get the current audio output section options.
+ *
+ * This function retrieves the current audio output section configuration
+ * option settings.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ * @param config The current audio output section
+ * configuration option settings.
+ *
+ * @retval PMIC_SUCCESS If the current configuration options were
+ * successfully retrieved.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the current configuration options
+ * could not be retrieved.
+ */
+PMIC_STATUS pmic_audio_output_get_config(const PMIC_AUDIO_HANDLE handle,
+ PMIC_AUDIO_OUTPUT_CONFIG *
+ const config)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Use a critical section to ensure a consistent hardware state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((((handle == stDAC.handle) &&
+ (stDAC.handleState == HANDLE_IN_USE)) ||
+ ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) ||
+ ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE))) &&
+ (config != (PMIC_AUDIO_OUTPUT_CONFIG *) NULL)) {
+ *config = audioOutput.config;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * @brief Enable the phantom ground circuit that is used to help identify
+ * the type of headset that has been inserted.
+ *
+ * This function enables the phantom ground circuit that is used to help
+ * identify the type of headset (e.g., stereo or mono) that has been inserted.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the phantom ground circuit was
+ * successfully enabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the phantom ground circuit could not
+ * be enabled.
+ */
+PMIC_STATUS pmic_audio_output_enable_phantom_ground()
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, HSPGDIS, 1);
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ rc = pmic_write_reg(REG_AUDIO_RX_0, 0, reg_mask);
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Phantom ground enabled\n");
+
+ }
+ return rc;
+}
+
+/*!
+ * @brief Disable the phantom ground circuit that is used to help identify
+ * the type of headset that has been inserted.
+ *
+ * This function disables the phantom ground circuit that is used to help
+ * identify the type of headset (e.g., stereo or mono) that has been inserted.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the phantom ground circuit was
+ * successfully disabled.
+ * @retval PMIC_PARAMETER_ERROR If the handle was invalid.
+ * @retval PMIC_ERROR If the phantom ground circuit could not
+ * be disabled.
+ */
+PMIC_STATUS pmic_audio_output_disable_phantom_ground()
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ const unsigned int reg_mask = SET_BITS(regAUDIO_RX_0, HSPGDIS, 1);
+
+ /* No critical section required here since we are not updating any
+ * global data.
+ */
+
+ rc = pmic_write_reg(REG_AUDIO_RX_0, 1, reg_mask);
+ if (rc == PMIC_SUCCESS) {
+ pr_debug("Phantom ground disabled\n");
+
+ }
+ return rc;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * Static functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name Audio Driver Internal Support Functions
+ * These non-exported internal functions are used to support the functionality
+ * of the exported audio APIs.
+ */
+/*@{*/
+
+/*!
+ * @brief Enables the 5.6V boost for the microphone bias 2 circuit.
+ *
+ * This function enables the switching regulator SW3 and configures it to
+ * provide the 5.6V boost that is required for driving the microphone bias 2
+ * circuit when using a 5-pole jack configuration (which is the case for the
+ * Sphinx board).
+ *
+ * @retval PMIC_SUCCESS The 5.6V boost was successfully enabled.
+ * @retval PMIC_ERROR Failed to enable the 5.6V boost.
+ */
+/*
+static PMIC_STATUS pmic_audio_mic_boost_enable(void)
+{
+ PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+
+ return rc;
+}
+*/
+/*!
+ * @brief Disables the 5.6V boost for the microphone bias 2 circuit.
+ *
+ * This function disables the switching regulator SW3 to turn off the 5.6V
+ * boost for the microphone bias 2 circuit.
+ *
+ * @retval PMIC_SUCCESS The 5.6V boost was successfully disabled.
+ * @retval PMIC_ERROR Failed to disable the 5.6V boost.
+ */
+/*
+static PMIC_STATUS pmic_audio_mic_boost_disable(void)
+{
+ PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+
+ return rc;
+}
+*/
+
+/*!
+ * @brief Free a device handle previously acquired by calling pmic_audio_open().
+ *
+ * Terminate further access to the PMIC audio hardware that was previously
+ * acquired by calling pmic_audio_open(). This now allows another thread to
+ * successfully call pmic_audio_open() to gain access.
+ *
+ * Note that we will shutdown/reset the Voice CODEC or Stereo DAC as well as
+ * any associated audio input/output components that are no longer required.
+ *
+ * Also note that this function should only be called with the mutex already
+ * acquired.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the close request was successful.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ */
+static PMIC_STATUS pmic_audio_close_handle(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+
+ /* Match up the handle to the audio device and then close it. */
+ if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+ /* Also shutdown the Stereo DAC hardware. The simplest way to
+ * do this is to simply call pmic_audio_reset_device() which will
+ * restore the ST_DAC register to it's initial power-on state.
+ *
+ * This will also shutdown the audio output section if no one
+ * else is still using it.
+ */
+ rc = pmic_audio_reset_device(stDAC.handle);
+
+ if (rc == PMIC_SUCCESS) {
+ stDAC.handle = AUDIO_HANDLE_NULL;
+ stDAC.handleState = HANDLE_FREE;
+ }
+ } else if ((handle == vCodec.handle) &&
+ (vCodec.handleState == HANDLE_IN_USE)) {
+ /* Also shutdown the Voice CODEC and audio input hardware. The
+ * simplest way to do this is to simply call pmic_audio_reset_device()
+ * which will restore the AUD_CODEC register to it's initial
+ * power-on state.
+ *
+ * This will also shutdown the audio output section if no one
+ * else is still using it.
+ */
+ rc = pmic_audio_reset_device(vCodec.handle);
+ if (rc == PMIC_SUCCESS) {
+ vCodec.handle = AUDIO_HANDLE_NULL;
+ vCodec.handleState = HANDLE_FREE;
+ }
+ } else if ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE)) {
+
+ /* Call pmic_audio_reset_device() here to shutdown the audio output
+ * section if no one else is still using it.
+ */
+ rc = pmic_audio_reset_device(extStereoIn.handle);
+
+ if (rc == PMIC_SUCCESS) {
+ extStereoIn.handle = AUDIO_HANDLE_NULL;
+ extStereoIn.handleState = HANDLE_FREE;
+ }
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief Reset the selected audio hardware control registers to their
+ * power on state.
+ *
+ * This resets all of the audio hardware control registers currently
+ * associated with the device handle back to their power on states. For
+ * example, if the handle is associated with the Stereo DAC and a
+ * specific output port and output amplifiers, then this function will
+ * reset all of those components to their initial power on state.
+ *
+ * This function can only be called if the mutex has already been acquired.
+ *
+ * @param handle Device handle from pmic_audio_open() call.
+ *
+ * @retval PMIC_SUCCESS If the reset operation was successful.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_ERROR If the reset was unsuccessful.
+ */
+static PMIC_STATUS pmic_audio_reset_device(const PMIC_AUDIO_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ unsigned int reg_mask = 0;
+
+ if ((handle == stDAC.handle) && (stDAC.handleState == HANDLE_IN_USE)) {
+ /* Also shutdown the audio output section if nobody else is using it.
+ if ((vCodec.handleState == HANDLE_FREE) &&
+ (extStereoIn.handleState == HANDLE_FREE))
+ {
+ pmic_write_reg(REG_RX_AUD_AMPS, RESET_RX_AUD_AMPS,
+ REG_FULLMASK);
+ } */
+
+ rc = pmic_write_reg(REG_AUDIO_STEREO_DAC,
+ RESET_ST_DAC, REG_FULLMASK);
+
+ if (rc == PMIC_SUCCESS) {
+ rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+ RESET_SSI_NETWORK,
+ REG_SSI_STDAC_MASK);
+ if (rc == PMIC_SUCCESS) {
+ /* Also reset the driver state information to match. Note that we
+ * keep the device handle and event callback settings unchanged
+ * since these don't affect the actual hardware and we rely on
+ * the user to explicitly close the handle or deregister callbacks
+ */
+ stDAC.busID = AUDIO_DATA_BUS_1;
+ stDAC.protocol = NORMAL_MSB_JUSTIFIED_MODE;
+ stDAC.protocol_set = false;
+ stDAC.masterSlave = BUS_MASTER_MODE;
+ stDAC.numSlots = USE_2_TIMESLOTS;
+ stDAC.clockIn = CLOCK_IN_CLIA;
+ stDAC.samplingRate = STDAC_RATE_44_1_KHZ;
+ stDAC.clockFreq = STDAC_CLI_13MHZ;
+ stDAC.invert = NO_INVERT;
+ stDAC.timeslot = USE_TS0_TS1;
+ stDAC.config = (PMIC_AUDIO_STDAC_CONFIG) 0;
+
+ }
+ }
+ } else if ((handle == vCodec.handle)
+ && (vCodec.handleState == HANDLE_IN_USE)) {
+ /* Disable the audio input section when disabling the Voice CODEC. */
+ pmic_write_reg(REG_AUDIO_TX, RESET_AUDIO_TX, REG_FULLMASK);
+
+ rc = pmic_write_reg(REG_AUDIO_CODEC,
+ RESET_AUD_CODEC, REG_FULLMASK);
+
+ if (rc == PMIC_SUCCESS) {
+ rc = pmic_write_reg(REG_AUDIO_SSI_NETWORK,
+ RESET_SSI_NETWORK,
+ REG_SSI_VCODEC_MASK);
+ if (rc == PMIC_SUCCESS) {
+
+ /* Also reset the driver state information to match. Note that we
+ * keep the device handle and event callback settings unchanged
+ * since these don't affect the actual hardware and we rely on
+ * the user to explicitly close the handle or deregister callbacks
+ */
+ vCodec.busID = AUDIO_DATA_BUS_2;
+ vCodec.protocol = NETWORK_MODE;
+ vCodec.protocol_set = false;
+ vCodec.masterSlave = BUS_SLAVE_MODE;
+ vCodec.numSlots = USE_4_TIMESLOTS;
+ vCodec.clockIn = CLOCK_IN_CLIB;
+ vCodec.samplingRate = VCODEC_RATE_8_KHZ;
+ vCodec.clockFreq = VCODEC_CLI_13MHZ;
+ vCodec.invert = NO_INVERT;
+ vCodec.timeslot = USE_TS0;
+ vCodec.config =
+ INPUT_HIGHPASS_FILTER |
+ OUTPUT_HIGHPASS_FILTER;
+
+ }
+ }
+
+ } else if ((handle == extStereoIn.handle) &&
+ (extStereoIn.handleState == HANDLE_IN_USE)) {
+ /* Disable the Ext stereo Amplifier and disable it as analog mixer input */
+ reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+ pmic_write_reg(REG_AUDIO_RX_1, 0, reg_mask);
+
+ reg_mask = SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+ pmic_write_reg(REG_AUDIO_RX_0, 0, reg_mask);
+
+ /* We don't need to reset any other registers for this case. */
+ rc = PMIC_SUCCESS;
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief Deregister the callback function and event mask currently associated
+ * with an audio device handle.
+ *
+ * This function deregisters any existing callback function and event mask for
+ * the given audio device handle. This is done by either calling the
+ * pmic_audio_clear_callback() API or by closing the device handle.
+ *
+ * Note that this function should only be called with the mutex already
+ * acquired. We will also acquire the spinlock here to prevent possible
+ * race conditions with the interrupt handler.
+ *
+ * @param[in] callback The current event callback function pointer.
+ * @param[in] eventMask The current audio event mask.
+ *
+ * @retval PMIC_SUCCESS If the callback function and event mask
+ * were both successfully deregistered.
+ * @retval PMIC_ERROR If either the callback function or the
+ * event mask was not successfully
+ * deregistered.
+ */
+
+static PMIC_STATUS pmic_audio_deregister(void *callback,
+ PMIC_AUDIO_EVENTS * const eventMask)
+{
+ unsigned long flags;
+ pmic_event_callback_t eventNotify;
+ PMIC_STATUS rc = PMIC_SUCCESS;
+
+ /* Deregister each of the PMIC events that we had previously
+ * registered for by calling pmic_event_subscribe().
+ */
+ if (*eventMask & (HEADSET_DETECTED)) {
+ /* We need to deregister for the A1 amplifier interrupt. */
+ eventNotify.func = callback;
+ eventNotify.param = (void *)(CORE_EVENT_HSDETI);
+ if (pmic_event_unsubscribe(EVENT_HSDETI, eventNotify) ==
+ PMIC_SUCCESS) {
+ *eventMask &= ~(HEADSET_DETECTED);
+ pr_debug("Deregistered for EVENT_HSDETI\n");
+ } else {
+ rc = PMIC_ERROR;
+ }
+ }
+
+ if (*eventMask & (HEADSET_STEREO)) {
+ /* We need to deregister for the A1 amplifier interrupt. */
+ eventNotify.func = callback;
+ eventNotify.param = (void *)(CORE_EVENT_HSLI);
+ if (pmic_event_unsubscribe(EVENT_HSLI, eventNotify) ==
+ PMIC_SUCCESS) {
+ *eventMask &= ~(HEADSET_STEREO);
+ pr_debug("Deregistered for EVENT_HSLI\n");
+ } else {
+ rc = PMIC_ERROR;
+ }
+ }
+ if (*eventMask & (HEADSET_THERMAL_SHUTDOWN)) {
+ /* We need to deregister for the A1 amplifier interrupt. */
+ eventNotify.func = callback;
+ eventNotify.param = (void *)(CORE_EVENT_ALSPTHI);
+ if (pmic_event_unsubscribe(EVENT_ALSPTHI, eventNotify) ==
+ PMIC_SUCCESS) {
+ *eventMask &= ~(HEADSET_THERMAL_SHUTDOWN);
+ pr_debug("Deregistered for EVENT_ALSPTHI\n");
+ } else {
+ rc = PMIC_ERROR;
+ }
+ }
+ if (*eventMask & (HEADSET_SHORT_CIRCUIT)) {
+ /* We need to deregister for the A1 amplifier interrupt. */
+ eventNotify.func = callback;
+ eventNotify.param = (void *)(CORE_EVENT_AHSSHORTI);
+ if (pmic_event_unsubscribe(EVENT_AHSSHORTI, eventNotify) ==
+ PMIC_SUCCESS) {
+ *eventMask &= ~(HEADSET_SHORT_CIRCUIT);
+ pr_debug("Deregistered for EVENT_AHSSHORTI\n");
+ } else {
+ rc = PMIC_ERROR;
+ }
+ }
+
+ if (rc == PMIC_SUCCESS) {
+ /* We need to grab the spinlock here to create a critical section to
+ * avoid any possible race conditions with the interrupt handler
+ */
+ spin_lock_irqsave(&lock, flags);
+
+ /* Restore the initial reset values for the callback function
+ * and event mask parameters. This should be NULL and zero,
+ * respectively.
+ */
+ callback = NULL;
+ *eventMask = 0;
+
+ /* Exit the critical section. */
+ spin_unlock_irqrestore(&lock, flags);
+ }
+
+ return rc;
+}
+
+/*!
+ * @brief enable/disable fm output.
+ *
+ * @param[in] enable true to enable false to disable
+ */
+PMIC_STATUS pmic_audio_fm_output_enable(bool enable)
+{
+ unsigned int reg_mask = 0;
+ unsigned int reg_write = 0;
+ PMIC_STATUS rc = PMIC_PARAMETER_ERROR;
+ if (enable) {
+ pmic_audio_antipop_enable(ANTI_POP_RAMP_FAST);
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, AHSLEN, 1);
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, AHSREN, 1);
+
+ reg_mask |= SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, AHSSEL, 1);
+
+ reg_mask |= SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+
+ reg_mask |= SET_BITS(regAUDIO_RX_0, HSPGDIS, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, HSPGDIS, 0);
+ } else {
+ reg_mask |= SET_BITS(regAUDIO_RX_0, ADDRXIN, 1);
+ reg_write |= SET_BITS(regAUDIO_RX_0, ADDRXIN, 0);
+ }
+ rc = pmic_write_reg(REG_AUDIO_RX_0, reg_write, reg_mask);
+ if (rc != PMIC_SUCCESS)
+ return rc;
+ if (enable) {
+ reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+ } else {
+ reg_mask = SET_BITS(regAUDIO_RX_1, ARXINEN, 1);
+ reg_write = SET_BITS(regAUDIO_RX_1, ARXINEN, 0);
+ }
+ rc = pmic_write_reg(REG_AUDIO_RX_1, reg_write, reg_mask);
+ return rc;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * Module initialization and termination functions.
+ *
+ * Note that if this code is compiled into the kernel, then the
+ * module_init() function will be called within the device_initcall()
+ * group.
+ **************************************************************************
+ */
+
+/*!
+ * @name Audio Driver Loading/Unloading Functions
+ * These non-exported internal functions are used to support the audio
+ * device driver initialization and de-initialization operations.
+ */
+/*@{*/
+
+/*!
+ * @brief This is the audio device driver initialization function.
+ *
+ * This function is called by the kernel when this device driver is first
+ * loaded.
+ */
+static int __init mc13783_pmic_audio_init(void)
+{
+ printk(KERN_INFO "PMIC Audio driver loading...\n");
+
+ return 0;
+}
+
+/*!
+ * @brief This is the audio device driver de-initialization function.
+ *
+ * This function is called by the kernel when this device driver is about
+ * to be unloaded.
+ */
+static void __exit mc13783_pmic_audio_exit(void)
+{
+ printk(KERN_INFO "PMIC Audio driver unloading...\n");
+
+ /* Close all device handles that are still open. This will also
+ * deregister any callbacks that may still be active.
+ */
+ if (stDAC.handleState == HANDLE_IN_USE) {
+ pmic_audio_close(stDAC.handle);
+ }
+ if (vCodec.handleState == HANDLE_IN_USE) {
+ pmic_audio_close(vCodec.handle);
+ }
+ if (extStereoIn.handleState == HANDLE_IN_USE) {
+ pmic_audio_close(extStereoIn.handle);
+ }
+
+ /* Explicitly reset all of the audio registers so that there is no
+ * possibility of leaving the audio hardware in a state
+ * where it can cause problems if there is no device driver loaded.
+ */
+ pmic_write_reg(REG_AUDIO_STEREO_DAC, RESET_ST_DAC, REG_FULLMASK);
+ pmic_write_reg(REG_AUDIO_CODEC, RESET_AUD_CODEC, REG_FULLMASK);
+ pmic_write_reg(REG_AUDIO_TX, RESET_AUDIO_TX, REG_FULLMASK);
+ pmic_write_reg(REG_AUDIO_SSI_NETWORK, RESET_SSI_NETWORK, REG_FULLMASK);
+ pmic_write_reg(REG_AUDIO_RX_0, RESET_AUDIO_RX_0, REG_FULLMASK);
+ pmic_write_reg(REG_AUDIO_RX_1, RESET_AUDIO_RX_1, REG_FULLMASK);
+}
+
+/*@}*/
+
+/*
+ * Module entry points and description information.
+ */
+
+module_init(mc13783_pmic_audio_init);
+module_exit(mc13783_pmic_audio_exit);
+
+MODULE_DESCRIPTION("PMIC - mc13783 ADC driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_battery.c b/drivers/mxc/pmic/mc13783/pmic_battery.c
new file mode 100644
index 000000000000..775ebe77a5c0
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_battery.c
@@ -0,0 +1,1221 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_battery.c
+ * @brief This is the main file of PMIC(mc13783) Battery driver.
+ *
+ * @ingroup PMIC_BATTERY
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+
+#include <linux/pmic_battery.h>
+#include <linux/pmic_adc.h>
+#include <linux/pmic_status.h>
+
+#include "pmic_battery_defs.h"
+
+#include <mach/pmic_power.h>
+#ifdef CONFIG_MXC_HWEVENT
+#include <mach/hw_events.h>
+#endif
+
+static int pmic_battery_major;
+
+/*!
+ * Number of users waiting in suspendq
+ */
+static int swait = 0;
+
+/*!
+ * To indicate whether any of the battery devices are suspending
+ */
+static int suspend_flag = 0;
+
+/*!
+ * The suspendq is used to block application calls
+ */
+static wait_queue_head_t suspendq;
+
+static struct class *pmic_battery_class;
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_batt_enable_charger);
+EXPORT_SYMBOL(pmic_batt_disable_charger);
+EXPORT_SYMBOL(pmic_batt_set_charger);
+EXPORT_SYMBOL(pmic_batt_get_charger_setting);
+EXPORT_SYMBOL(pmic_batt_get_charge_current);
+EXPORT_SYMBOL(pmic_batt_enable_eol);
+EXPORT_SYMBOL(pmic_batt_bp_enable_eol);
+EXPORT_SYMBOL(pmic_batt_disable_eol);
+EXPORT_SYMBOL(pmic_batt_set_out_control);
+EXPORT_SYMBOL(pmic_batt_set_threshold);
+EXPORT_SYMBOL(pmic_batt_led_control);
+EXPORT_SYMBOL(pmic_batt_set_reverse_supply);
+EXPORT_SYMBOL(pmic_batt_set_unregulated);
+EXPORT_SYMBOL(pmic_batt_set_5k_pull);
+EXPORT_SYMBOL(pmic_batt_event_subscribe);
+EXPORT_SYMBOL(pmic_batt_event_unsubscribe);
+
+static DECLARE_MUTEX(count_mutex); /* open count mutex */
+static int open_count; /* open count for device file */
+
+/*!
+ * Callback function for events, we want on MGN board
+ */
+static void callback_chg_detect(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+ t_sensor_bits sensor;
+ struct mxc_hw_event event = { HWE_BAT_CHARGER_PLUG, 0 };
+
+ pr_debug("In callback_chg_detect\n");
+
+ /* get sensor values */
+ pmic_get_sensors(&sensor);
+
+ pr_debug("Callback, charger detect:%d\n", sensor.sense_chgdets);
+
+ if (sensor.sense_chgdets)
+ event.args = 1;
+ else
+ event.args = 0;
+ /* send hardware event */
+ hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+static void callback_low_battery(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+ struct mxc_hw_event event = { HWE_BAT_BATTERY_LOW, 0 };
+
+ pr_debug("In callback_low_battery\n");
+ /* send hardware event */
+ hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+static void callback_power_fail(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+ struct mxc_hw_event event = { HWE_BAT_POWER_FAILED, 0 };
+
+ pr_debug("In callback_power_fail\n");
+ /* send hardware event */
+ hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+static void callback_chg_overvoltage(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+ struct mxc_hw_event event = { HWE_BAT_CHARGER_OVERVOLTAGE, 0 };
+
+ pr_debug("In callback_chg_overvoltage\n");
+ /* send hardware event */
+ hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+static void callback_chg_full(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+ t_sensor_bits sensor;
+ struct mxc_hw_event event = { HWE_BAT_CHARGER_FULL, 0 };
+
+ pr_debug("In callback_chg_full\n");
+
+ /* disable charge function */
+ pmic_batt_disable_charger(BATT_MAIN_CHGR);
+
+ /* get charger sensor */
+ pmic_get_sensors(&sensor);
+
+ /* if did not detect the charger */
+ if (sensor.sense_chgdets)
+ return;
+ /* send hardware event */
+ hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+/*!
+ * This is the suspend of power management for the pmic battery API.
+ * It suports SAVE and POWER_DOWN state.
+ *
+ * @param pdev the device
+ * @param state the state
+ *
+ * @return This function returns 0 if successful.
+ */
+static int pmic_battery_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ unsigned int reg_value = 0;
+
+ suspend_flag = 1;
+ CHECK_ERROR(pmic_write_reg(REG_CHARGER, reg_value, PMIC_ALL_BITS));
+
+ return 0;
+};
+
+/*!
+ * This is the resume of power management for the pmic battery API.
+ * It suports RESTORE state.
+ *
+ * @param pdev the device
+ *
+ * @return This function returns 0 if successful.
+ */
+static int pmic_battery_resume(struct platform_device *pdev)
+{
+ suspend_flag = 0;
+ while (swait > 0) {
+ swait--;
+ wake_up_interruptible(&suspendq);
+ }
+
+ return 0;
+};
+
+/*!
+ * This function is used to start charging a battery. For different charger,
+ * different voltage and current range are supported. \n
+ *
+ *
+ * @param chgr Charger as defined in \b t_batt_charger.
+ * @param c_voltage Charging voltage.
+ * @param c_current Charging current.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_enable_charger(t_batt_charger chgr,
+ unsigned char c_voltage,
+ unsigned char c_current)
+{
+ unsigned int val, mask, reg;
+
+ val = 0;
+ mask = 0;
+ reg = 0;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ switch (chgr) {
+ case BATT_MAIN_CHGR:
+ val = BITFVAL(MC13783_BATT_DAC_DAC, c_current) |
+ BITFVAL(MC13783_BATT_DAC_V_DAC, c_voltage);
+ mask = BITFMASK(MC13783_BATT_DAC_DAC) |
+ BITFMASK(MC13783_BATT_DAC_V_DAC);
+ reg = REG_CHARGER;
+ break;
+
+ case BATT_CELL_CHGR:
+ val = BITFVAL(MC13783_BATT_DAC_V_COIN, c_voltage) |
+ BITFVAL(MC13783_BATT_DAC_COIN_CH_EN,
+ MC13783_BATT_DAC_COIN_CH_EN_ENABLED);
+ mask = BITFMASK(MC13783_BATT_DAC_V_COIN) |
+ BITFMASK(MC13783_BATT_DAC_COIN_CH_EN);
+ reg = REG_POWER_CONTROL_0;
+ break;
+
+ case BATT_TRCKLE_CHGR:
+ val = BITFVAL(MC13783_BATT_DAC_TRCKLE, c_current);
+ mask = BITFMASK(MC13783_BATT_DAC_TRCKLE);
+ reg = REG_CHARGER;
+ break;
+
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, val, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function turns off a charger.
+ *
+ * @param chgr Charger as defined in \b t_batt_charger.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_disable_charger(t_batt_charger chgr)
+{
+ unsigned int val, mask, reg;
+
+ val = 0;
+ mask = 0;
+ reg = 0;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+ switch (chgr) {
+ case BATT_MAIN_CHGR:
+ val = BITFVAL(MC13783_BATT_DAC_DAC, 0) |
+ BITFVAL(MC13783_BATT_DAC_V_DAC, 0);
+ mask = BITFMASK(MC13783_BATT_DAC_DAC) |
+ BITFMASK(MC13783_BATT_DAC_V_DAC);
+ reg = REG_CHARGER;
+ break;
+
+ case BATT_CELL_CHGR:
+ val = BITFVAL(MC13783_BATT_DAC_COIN_CH_EN,
+ MC13783_BATT_DAC_COIN_CH_EN_DISABLED);
+ mask = BITFMASK(MC13783_BATT_DAC_COIN_CH_EN);
+ reg = REG_POWER_CONTROL_0;
+ break;
+
+ case BATT_TRCKLE_CHGR:
+ val = BITFVAL(MC13783_BATT_DAC_TRCKLE, 0);
+ mask = BITFMASK(MC13783_BATT_DAC_TRCKLE);
+ reg = REG_CHARGER;
+ break;
+
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, val, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to change the charger setting.
+ *
+ * @param chgr Charger as defined in \b t_batt_charger.
+ * @param c_voltage Charging voltage.
+ * @param c_current Charging current.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_charger(t_batt_charger chgr,
+ unsigned char c_voltage,
+ unsigned char c_current)
+{
+ unsigned int val, mask, reg;
+
+ val = 0;
+ mask = 0;
+ reg = 0;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ switch (chgr) {
+ case BATT_MAIN_CHGR:
+ val = BITFVAL(MC13783_BATT_DAC_DAC, c_current) |
+ BITFVAL(MC13783_BATT_DAC_V_DAC, c_voltage);
+ mask = BITFMASK(MC13783_BATT_DAC_DAC) |
+ BITFMASK(MC13783_BATT_DAC_V_DAC);
+ reg = REG_CHARGER;
+ break;
+
+ case BATT_CELL_CHGR:
+ val = BITFVAL(MC13783_BATT_DAC_V_COIN, c_voltage);
+ mask = BITFMASK(MC13783_BATT_DAC_V_COIN);
+ reg = REG_POWER_CONTROL_0;
+ break;
+
+ case BATT_TRCKLE_CHGR:
+ val = BITFVAL(MC13783_BATT_DAC_TRCKLE, c_current);
+ mask = BITFMASK(MC13783_BATT_DAC_TRCKLE);
+ reg = REG_CHARGER;
+ break;
+
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, val, mask));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to retrive the charger setting.
+ *
+ * @param chgr Charger as defined in \b t_batt_charger.
+ * @param c_voltage Output parameter for charging voltage setting.
+ * @param c_current Output parameter for charging current setting.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_charger_setting(t_batt_charger chgr,
+ unsigned char *c_voltage,
+ unsigned char *c_current)
+{
+ unsigned int val, reg;
+
+ reg = 0;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ switch (chgr) {
+ case BATT_MAIN_CHGR:
+ case BATT_TRCKLE_CHGR:
+ reg = REG_CHARGER;
+ break;
+ case BATT_CELL_CHGR:
+ reg = REG_POWER_CONTROL_0;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &val, PMIC_ALL_BITS));
+
+ switch (chgr) {
+ case BATT_MAIN_CHGR:
+ *c_voltage = BITFEXT(val, MC13783_BATT_DAC_V_DAC);;
+ *c_current = BITFEXT(val, MC13783_BATT_DAC_DAC);
+ break;
+
+ case BATT_CELL_CHGR:
+ *c_voltage = BITFEXT(val, MC13783_BATT_DAC_V_COIN);
+ *c_current = 0;
+ break;
+
+ case BATT_TRCKLE_CHGR:
+ *c_voltage = 0;
+ *c_current = BITFEXT(val, MC13783_BATT_DAC_TRCKLE);
+ break;
+
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is retrives the main battery voltage.
+ *
+ * @param b_voltage Output parameter for voltage setting.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_batt_voltage(unsigned short *b_voltage)
+{
+ t_channel channel;
+ unsigned short result[8];
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+ channel = BATTERY_VOLTAGE;
+ CHECK_ERROR(pmic_adc_convert(channel, result));
+ *b_voltage = result[0];
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is retrives the main battery current.
+ *
+ * @param b_current Output parameter for current setting.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_batt_current(unsigned short *b_current)
+{
+ t_channel channel;
+ unsigned short result[8];
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ channel = BATTERY_CURRENT;
+ CHECK_ERROR(pmic_adc_convert(channel, result));
+ *b_current = result[0];
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is retrives the main battery temperature.
+ *
+ * @param b_temper Output parameter for temperature setting.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_batt_temperature(unsigned short *b_temper)
+{
+ t_channel channel;
+ unsigned short result[8];
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ channel = GEN_PURPOSE_AD5;
+ CHECK_ERROR(pmic_adc_convert(channel, result));
+ *b_temper = result[0];
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is retrives the main battery charging voltage.
+ *
+ * @param c_voltage Output parameter for charging voltage setting.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_charge_voltage(unsigned short *c_voltage)
+{
+ t_channel channel;
+ unsigned short result[8];
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ channel = CHARGE_VOLTAGE;
+ CHECK_ERROR(pmic_adc_convert(channel, result));
+ *c_voltage = result[0];
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is retrives the main battery charging current.
+ *
+ * @param c_current Output parameter for charging current setting.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_charge_current(unsigned short *c_current)
+{
+ t_channel channel;
+ unsigned short result[8];
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ channel = CHARGE_CURRENT;
+ CHECK_ERROR(pmic_adc_convert(channel, result));
+ *c_current = result[0];
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables End-of-Life comparator. Not supported on
+ * mc13783. Use pmic_batt_bp_enable_eol function.
+ *
+ * @param threshold End-of-Life threshold.
+ *
+ * @return This function returns PMIC_UNSUPPORTED
+ */
+PMIC_STATUS pmic_batt_enable_eol(unsigned char threshold)
+{
+ return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function enables End-of-Life comparator.
+ *
+ * @param typical Falling Edge Threshold threshold.
+ * @verbatim
+ BPDET UVDET LOBATL
+ ____ _____ ___________
+ 0 2.6 UVDET + 0.2
+ 1 2.6 UVDET + 0.3
+ 2 2.6 UVDET + 0.4
+ 3 2.6 UVDET + 0.5
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_bp_enable_eol(t_bp_threshold typical)
+{
+ unsigned int val, mask;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ val = BITFVAL(MC13783_BATT_DAC_EOL_CMP_EN,
+ MC13783_BATT_DAC_EOL_CMP_EN_ENABLE) |
+ BITFVAL(MC13783_BATT_DAC_EOL_SEL, typical);
+ mask = BITFMASK(MC13783_BATT_DAC_EOL_CMP_EN) |
+ BITFMASK(MC13783_BATT_DAC_EOL_SEL);
+
+ CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0, val, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables End-of-Life comparator.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_disable_eol(void)
+{
+ unsigned int val, mask;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ val = BITFVAL(MC13783_BATT_DAC_EOL_CMP_EN,
+ MC13783_BATT_DAC_EOL_CMP_EN_DISABLE);
+ mask = BITFMASK(MC13783_BATT_DAC_EOL_CMP_EN);
+
+ CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0, val, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the output controls.
+ * It sets the FETOVRD and FETCTRL bits of mc13783
+ *
+ * @param control type of control.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_set_out_control(t_control control)
+{
+ unsigned int val, mask;
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ switch (control) {
+ case CONTROL_HARDWARE:
+ val = BITFVAL(MC13783_BATT_DAC_FETOVRD_EN, 0) |
+ BITFVAL(MC13783_BATT_DAC_FETCTRL_EN, 0);
+ mask = BITFMASK(MC13783_BATT_DAC_FETOVRD_EN) |
+ BITFMASK(MC13783_BATT_DAC_FETCTRL_EN);
+ break;
+ case CONTROL_BPFET_LOW:
+ val = BITFVAL(MC13783_BATT_DAC_FETOVRD_EN, 1) |
+ BITFVAL(MC13783_BATT_DAC_FETCTRL_EN, 0);
+ mask = BITFMASK(MC13783_BATT_DAC_FETOVRD_EN) |
+ BITFMASK(MC13783_BATT_DAC_FETCTRL_EN);
+ break;
+ case CONTROL_BPFET_HIGH:
+ val = BITFVAL(MC13783_BATT_DAC_FETOVRD_EN, 1) |
+ BITFVAL(MC13783_BATT_DAC_FETCTRL_EN, 1);
+ mask = BITFMASK(MC13783_BATT_DAC_FETOVRD_EN) |
+ BITFMASK(MC13783_BATT_DAC_FETCTRL_EN);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets over voltage threshold.
+ *
+ * @param threshold value of over voltage threshold.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_set_threshold(int threshold)
+{
+ unsigned int val, mask;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ if (threshold > BAT_THRESHOLD_MAX)
+ return PMIC_PARAMETER_ERROR;
+
+ val = BITFVAL(MC13783_BATT_DAC_OVCTRL, threshold);
+ mask = BITFMASK(MC13783_BATT_DAC_OVCTRL);
+ CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function controls charge LED.
+ *
+ * @param on If on is ture, LED will be turned on,
+ * or otherwise, LED will be turned off.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_led_control(bool on)
+{
+ unsigned val, mask;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ val = BITFVAL(MC13783_BATT_DAC_LED_EN, on);
+ mask = BITFMASK(MC13783_BATT_DAC_LED_EN);
+
+ CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets reverse supply mode.
+ *
+ * @param enable If enable is ture, reverse supply mode is enable,
+ * or otherwise, reverse supply mode is disabled.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_reverse_supply(bool enable)
+{
+ unsigned val, mask;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ val = BITFVAL(MC13783_BATT_DAC_REVERSE_SUPPLY, enable);
+ mask = BITFMASK(MC13783_BATT_DAC_REVERSE_SUPPLY);
+
+ CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets unregulatored charging mode on main battery.
+ *
+ * @param enable If enable is ture, unregulated charging mode is
+ * enable, or otherwise, disabled.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_unregulated(bool enable)
+{
+ unsigned val, mask;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ val = BITFVAL(MC13783_BATT_DAC_UNREGULATED, enable);
+ mask = BITFMASK(MC13783_BATT_DAC_UNREGULATED);
+
+ CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a 5K pull down at CHRGRAW.
+ * To be used in the dual path charging configuration.
+ *
+ * @param enable If enable is true, 5k pull down is
+ * enable, or otherwise, disabled.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_5k_pull(bool enable)
+{
+ unsigned val, mask;
+
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ val = BITFVAL(MC13783_BATT_DAC_5K, enable);
+ mask = BITFMASK(MC13783_BATT_DAC_5K);
+
+ CHECK_ERROR(pmic_write_reg(REG_CHARGER, val, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to un/subscribe on battery event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ * @param sub define if Un/subscribe event.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS mc13783_battery_event(t_batt_event event, void *callback, bool sub)
+{
+ pmic_event_callback_t bat_callback;
+ type_event bat_event;
+
+ bat_callback.func = callback;
+ bat_callback.param = NULL;
+ switch (event) {
+ case BAT_IT_CHG_DET:
+ bat_event = EVENT_CHGDETI;
+ break;
+ case BAT_IT_CHG_OVERVOLT:
+ bat_event = EVENT_CHGOVI;
+ break;
+ case BAT_IT_CHG_REVERSE:
+ bat_event = EVENT_CHGREVI;
+ break;
+ case BAT_IT_CHG_SHORT_CIRCUIT:
+ bat_event = EVENT_CHGSHORTI;
+ break;
+ case BAT_IT_CCCV:
+ bat_event = EVENT_CCCVI;
+ break;
+ case BAT_IT_BELOW_THRESHOLD:
+ bat_event = EVENT_CHRGCURRI;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ if (sub == true) {
+ CHECK_ERROR(pmic_event_subscribe(bat_event, bat_callback));
+ } else {
+ CHECK_ERROR(pmic_event_unsubscribe(bat_event, bat_callback));
+ }
+ return 0;
+}
+
+/*!
+ * This function is used to subscribe on battery event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_event_subscribe(t_batt_event event, void *callback)
+{
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ return mc13783_battery_event(event, callback, true);
+}
+
+/*!
+ * This function is used to un subscribe on battery event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_event_unsubscribe(t_batt_event event, void *callback)
+{
+ if (suspend_flag == 1)
+ return PMIC_ERROR;
+
+ return mc13783_battery_event(event, callback, false);
+}
+
+/*!
+ * This function implements IOCTL controls on a PMIC Battery device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @param cmd the command
+ * @param arg the parameter
+ * @return This function returns 0 if successful.
+ */
+static int pmic_battery_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ t_charger_setting *chgr_setting = NULL;
+ unsigned short c_current;
+ unsigned int bc_info;
+ t_eol_setting *eol_setting;
+
+ if (_IOC_TYPE(cmd) != 'p')
+ return -ENOTTY;
+
+ switch (cmd) {
+ case PMIC_BATT_CHARGER_CONTROL:
+ if ((chgr_setting = kmalloc(sizeof(t_charger_setting),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(chgr_setting, (t_charger_setting *) arg,
+ sizeof(t_charger_setting))) {
+ kfree(chgr_setting);
+ return -EFAULT;
+ }
+
+ if (chgr_setting->on != false) {
+ CHECK_ERROR_KFREE(pmic_batt_enable_charger
+ (chgr_setting->chgr,
+ chgr_setting->c_voltage,
+ chgr_setting->c_current),
+ (kfree(chgr_setting)));
+ } else {
+ CHECK_ERROR(pmic_batt_disable_charger
+ (chgr_setting->chgr));
+ }
+
+ kfree(chgr_setting);
+ break;
+
+ case PMIC_BATT_SET_CHARGER:
+ if ((chgr_setting = kmalloc(sizeof(t_charger_setting),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(chgr_setting, (t_charger_setting *) arg,
+ sizeof(t_charger_setting))) {
+ kfree(chgr_setting);
+ return -EFAULT;
+ }
+
+ CHECK_ERROR_KFREE(pmic_batt_set_charger(chgr_setting->chgr,
+ chgr_setting->c_voltage,
+ chgr_setting->
+ c_current),
+ (kfree(chgr_setting)));
+
+ kfree(chgr_setting);
+ break;
+
+ case PMIC_BATT_GET_CHARGER:
+ if ((chgr_setting = kmalloc(sizeof(t_charger_setting),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(chgr_setting, (t_charger_setting *) arg,
+ sizeof(t_charger_setting))) {
+ kfree(chgr_setting);
+ return -EFAULT;
+ }
+
+ CHECK_ERROR_KFREE(pmic_batt_get_charger_setting
+ (chgr_setting->chgr, &chgr_setting->c_voltage,
+ &chgr_setting->c_current),
+ (kfree(chgr_setting)));
+ if (copy_to_user
+ ((t_charger_setting *) arg, chgr_setting,
+ sizeof(t_charger_setting))) {
+ return -EFAULT;
+ }
+
+ kfree(chgr_setting);
+ break;
+
+ case PMIC_BATT_GET_CHARGER_SENSOR:
+ {
+ t_sensor_bits sensor;
+ pmic_get_sensors(&sensor);
+ if (copy_to_user
+ ((unsigned int *)arg, &sensor.sense_chgdets,
+ sizeof(unsigned int)))
+ return -EFAULT;
+
+ break;
+ }
+ case PMIC_BATT_GET_BATTERY_VOLTAGE:
+ CHECK_ERROR(pmic_batt_get_batt_voltage(&c_current));
+ bc_info = (unsigned int)c_current * 2300 / 1023 + 2400;
+ if (copy_to_user((unsigned int *)arg, &bc_info,
+ sizeof(unsigned int)))
+ return -EFAULT;
+
+ break;
+
+ case PMIC_BATT_GET_BATTERY_CURRENT:
+ CHECK_ERROR(pmic_batt_get_batt_current(&c_current));
+ bc_info = (unsigned int)c_current * 5750 / 1023;
+ if (copy_to_user((unsigned int *)arg, &bc_info,
+ sizeof(unsigned int)))
+ return -EFAULT;
+ break;
+
+ case PMIC_BATT_GET_BATTERY_TEMPERATURE:
+ CHECK_ERROR(pmic_batt_get_batt_temperature(&c_current));
+ bc_info = (unsigned int)c_current;
+ if (copy_to_user((unsigned int *)arg, &bc_info,
+ sizeof(unsigned int)))
+ return -EFAULT;
+
+ break;
+
+ case PMIC_BATT_GET_CHARGER_VOLTAGE:
+ CHECK_ERROR(pmic_batt_get_charge_voltage(&c_current));
+ bc_info = (unsigned int)c_current * 23000 / 1023;
+ if (copy_to_user((unsigned int *)arg, &bc_info,
+ sizeof(unsigned int)))
+ return -EFAULT;
+
+ break;
+
+ case PMIC_BATT_GET_CHARGER_CURRENT:
+ CHECK_ERROR(pmic_batt_get_charge_current(&c_current));
+ bc_info = (unsigned int)c_current * 5750 / 1023;
+ if (copy_to_user((unsigned int *)arg, &bc_info,
+ sizeof(unsigned int)))
+ return -EFAULT;
+
+ break;
+
+ case PMIC_BATT_EOL_CONTROL:
+ if ((eol_setting = kmalloc(sizeof(t_eol_setting), GFP_KERNEL))
+ == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(eol_setting, (t_eol_setting *) arg,
+ sizeof(t_eol_setting))) {
+ kfree(eol_setting);
+ return -EFAULT;
+ }
+
+ if (eol_setting->enable != false) {
+ CHECK_ERROR_KFREE(pmic_batt_bp_enable_eol
+ (eol_setting->typical),
+ (kfree(chgr_setting)));
+ } else {
+ CHECK_ERROR_KFREE(pmic_batt_disable_eol(),
+ (kfree(chgr_setting)));
+ }
+
+ kfree(eol_setting);
+ break;
+
+ case PMIC_BATT_SET_OUT_CONTROL:
+ CHECK_ERROR(pmic_batt_set_out_control((t_control) arg));
+ break;
+
+ case PMIC_BATT_SET_THRESHOLD:
+ CHECK_ERROR(pmic_batt_set_threshold((int)arg));
+ break;
+
+ case PMIC_BATT_LED_CONTROL:
+ CHECK_ERROR(pmic_batt_led_control((bool) arg));
+ break;
+
+ case PMIC_BATT_REV_SUPP_CONTROL:
+ CHECK_ERROR(pmic_batt_set_reverse_supply((bool) arg));
+ break;
+
+ case PMIC_BATT_UNREG_CONTROL:
+ CHECK_ERROR(pmic_batt_set_unregulated((bool) arg));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * This function implements the open method on a Pmic battery device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_battery_open(struct inode *inode, struct file *file)
+{
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+
+ /* check open count, if open firstly, register callbacks */
+ down(&count_mutex);
+ if (open_count++ > 0) {
+ up(&count_mutex);
+ return 0;
+ }
+
+ pr_debug("Subscribe the callbacks\n");
+ /* register battery event callback */
+ if (pmic_batt_event_subscribe(BAT_IT_CHG_DET, callback_chg_detect)) {
+ pr_debug("Failed to subscribe the charger detect callback\n");
+ goto event_err1;
+ }
+ if (pmic_power_event_sub(PWR_IT_LOBATLI, callback_power_fail)) {
+ pr_debug("Failed to subscribe the power failed callback\n");
+ goto event_err2;
+ }
+ if (pmic_power_event_sub(PWR_IT_LOBATHI, callback_low_battery)) {
+ pr_debug("Failed to subscribe the low battery callback\n");
+ goto event_err3;
+ }
+ if (pmic_batt_event_subscribe
+ (BAT_IT_CHG_OVERVOLT, callback_chg_overvoltage)) {
+ pr_debug("Failed to subscribe the low battery callback\n");
+ goto event_err4;
+ }
+ if (pmic_batt_event_subscribe
+ (BAT_IT_BELOW_THRESHOLD, callback_chg_full)) {
+ pr_debug("Failed to subscribe the charge full callback\n");
+ goto event_err5;
+ }
+
+ up(&count_mutex);
+
+ return 0;
+
+ /* un-subscribe the event callbacks */
+event_err5:
+ pmic_batt_event_unsubscribe(BAT_IT_CHG_OVERVOLT,
+ callback_chg_overvoltage);
+event_err4:
+ pmic_power_event_unsub(PWR_IT_LOBATHI, callback_low_battery);
+event_err3:
+ pmic_power_event_unsub(PWR_IT_LOBATLI, callback_power_fail);
+event_err2:
+ pmic_batt_event_unsubscribe(BAT_IT_CHG_DET, callback_chg_detect);
+event_err1:
+
+ open_count--;
+ up(&count_mutex);
+
+ return -EFAULT;
+
+}
+
+/*!
+ * This function implements the release method on a Pmic battery device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_battery_release(struct inode *inode, struct file *file)
+{
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+
+ /* check open count, if open firstly, register callbacks */
+ down(&count_mutex);
+ if (--open_count == 0) {
+ /* unregister these event callback */
+ pr_debug("Unsubscribe the callbacks\n");
+ pmic_batt_event_unsubscribe(BAT_IT_BELOW_THRESHOLD,
+ callback_chg_full);
+ pmic_batt_event_unsubscribe(BAT_IT_CHG_OVERVOLT,
+ callback_chg_overvoltage);
+ pmic_power_event_unsub(PWR_IT_LOBATHI, callback_low_battery);
+ pmic_power_event_unsub(PWR_IT_LOBATLI, callback_power_fail);
+ pmic_batt_event_unsubscribe(BAT_IT_CHG_DET,
+ callback_chg_detect);
+ }
+ up(&count_mutex);
+
+ return 0;
+}
+
+static struct file_operations pmic_battery_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = pmic_battery_ioctl,
+ .open = pmic_battery_open,
+ .release = pmic_battery_release,
+};
+
+static int pmic_battery_remove(struct platform_device *pdev)
+{
+ device_destroy(pmic_battery_class, MKDEV(pmic_battery_major, 0));
+ class_destroy(pmic_battery_class);
+ unregister_chrdev(pmic_battery_major, PMIC_BATTERY_STRING);
+ return 0;
+}
+
+static int pmic_battery_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device *temp_class;
+
+ pmic_battery_major = register_chrdev(0, PMIC_BATTERY_STRING,
+ &pmic_battery_fops);
+
+ if (pmic_battery_major < 0) {
+ printk(KERN_ERR "Unable to get a major for pmic_battery\n");
+ return pmic_battery_major;
+ }
+ init_waitqueue_head(&suspendq);
+
+ pmic_battery_class = class_create(THIS_MODULE, PMIC_BATTERY_STRING);
+ if (IS_ERR(pmic_battery_class)) {
+ printk(KERN_ERR "Error creating PMIC battery class.\n");
+ ret = PTR_ERR(pmic_battery_class);
+ goto err_out1;
+ }
+
+ temp_class = device_create(pmic_battery_class, NULL,
+ MKDEV(pmic_battery_major, 0), NULL,
+ PMIC_BATTERY_STRING);
+ if (IS_ERR(temp_class)) {
+ printk(KERN_ERR "Error creating PMIC battery class device.\n");
+ ret = PTR_ERR(temp_class);
+ goto err_out2;
+ }
+
+ pmic_batt_led_control(true);
+ pmic_batt_set_5k_pull(true);
+
+ printk(KERN_INFO "PMIC Battery successfully probed\n");
+
+ return ret;
+
+ err_out2:
+ class_destroy(pmic_battery_class);
+ err_out1:
+ unregister_chrdev(pmic_battery_major, PMIC_BATTERY_STRING);
+ return ret;
+}
+
+static struct platform_driver pmic_battery_driver_ldm = {
+ .driver = {
+ .name = "pmic_battery",
+ .bus = &platform_bus_type,
+ },
+ .suspend = pmic_battery_suspend,
+ .resume = pmic_battery_resume,
+ .probe = pmic_battery_probe,
+ .remove = pmic_battery_remove,
+};
+
+/*
+ * Init and Exit
+ */
+
+static int __init pmic_battery_init(void)
+{
+ pr_debug("PMIC Battery driver loading...\n");
+ return platform_driver_register(&pmic_battery_driver_ldm);
+}
+
+static void __exit pmic_battery_exit(void)
+{
+ platform_driver_unregister(&pmic_battery_driver_ldm);
+ pr_debug("PMIC Battery driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+module_init(pmic_battery_init);
+module_exit(pmic_battery_exit);
+
+MODULE_DESCRIPTION("pmic_battery driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_battery_defs.h b/drivers/mxc/pmic/mc13783/pmic_battery_defs.h
new file mode 100644
index 000000000000..9f66a185cd2f
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_battery_defs.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_battery_defs.h
+ * @brief This is the internal header for PMIC(mc13783) Battery driver.
+ *
+ * @ingroup PMIC_BATTERY
+ */
+
+#ifndef __PMIC_BATTERY_DEFS_H__
+#define __PMIC_BATTERY_DEFS_H__
+
+#define PMIC_BATTERY_STRING "pmic_battery"
+
+/* REG_CHARGE */
+#define MC13783_BATT_DAC_V_DAC_LSH 0
+#define MC13783_BATT_DAC_V_DAC_WID 3
+#define MC13783_BATT_DAC_DAC_LSH 3
+#define MC13783_BATT_DAC_DAC_WID 4
+#define MC13783_BATT_DAC_TRCKLE_LSH 7
+#define MC13783_BATT_DAC_TRCKLE_WID 3
+#define MC13783_BATT_DAC_FETOVRD_EN_LSH 10
+#define MC13783_BATT_DAC_FETOVRD_EN_WID 1
+#define MC13783_BATT_DAC_FETCTRL_EN_LSH 11
+#define MC13783_BATT_DAC_FETCTRL_EN_WID 1
+#define MC13783_BATT_DAC_REVERSE_SUPPLY_LSH 13
+#define MC13783_BATT_DAC_REVERSE_SUPPLY_WID 1
+#define MC13783_BATT_DAC_OVCTRL_LSH 15
+#define MC13783_BATT_DAC_OVCTRL_WID 2
+#define MC13783_BATT_DAC_UNREGULATED_LSH 17
+#define MC13783_BATT_DAC_UNREGULATED_WID 1
+#define MC13783_BATT_DAC_LED_EN_LSH 18
+#define MC13783_BATT_DAC_LED_EN_WID 1
+#define MC13783_BATT_DAC_5K_LSH 19
+#define MC13783_BATT_DAC_5K_WID 1
+
+#define BITS_OUT_VOLTAGE 0
+#define LONG_OUT_VOLTAGE 3
+#define BITS_CURRENT_MAIN 3
+#define LONG_CURRENT_MAIN 4
+#define BITS_CURRENT_TRICKLE 7
+#define LONG_CURRENT_TRICKLE 3
+#define BIT_FETOVRD 10
+#define BIT_FETCTRL 11
+#define BIT_RVRSMODE 13
+#define BITS_OVERVOLTAGE 15
+#define LONG_OVERVOLTAGE 2
+#define BIT_UNREGULATED 17
+#define BIT_CHRG_LED 18
+#define BIT_CHRGRAWPDEN 19
+
+/* REG_POWXER_CONTROL_0 */
+#define MC13783_BATT_DAC_V_COIN_LSH 20
+#define MC13783_BATT_DAC_V_COIN_WID 3
+#define MC13783_BATT_DAC_COIN_CH_EN_LSH 23
+#define MC13783_BATT_DAC_COIN_CH_EN_WID 1
+#define MC13783_BATT_DAC_COIN_CH_EN_ENABLED 1
+#define MC13783_BATT_DAC_COIN_CH_EN_DISABLED 0
+#define MC13783_BATT_DAC_EOL_CMP_EN_LSH 18
+#define MC13783_BATT_DAC_EOL_CMP_EN_WID 1
+#define MC13783_BATT_DAC_EOL_CMP_EN_ENABLE 1
+#define MC13783_BATT_DAC_EOL_CMP_EN_DISABLE 0
+#define MC13783_BATT_DAC_EOL_SEL_LSH 16
+#define MC13783_BATT_DAC_EOL_SEL_WID 2
+
+#define DEF_VALUE 0
+
+#define BAT_THRESHOLD_MAX 3
+
+#endif /* __PMIC_BATTERY_DEFS_H__ */
diff --git a/drivers/mxc/pmic/mc13783/pmic_convity.c b/drivers/mxc/pmic/mc13783/pmic_convity.c
new file mode 100644
index 000000000000..5d634ebebacf
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_convity.c
@@ -0,0 +1,2482 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_convity.c
+ * @brief Implementation of the PMIC Connectivity driver APIs.
+ *
+ * The PMIC connectivity device driver and this API were developed to support
+ * the external connectivity capabilities of several power management ICs that
+ * are available from Freescale Semiconductor, Inc.
+ *
+ * The following operating modes, in terms of external connectivity, are
+ * supported:
+ *
+ * @verbatim
+ Operating Mode mc13783
+ --------------- -------
+ USB (incl. OTG) Yes
+ RS-232 Yes
+ CEA-936 Yes
+
+ @endverbatim
+ *
+ * @ingroup PMIC_CONNECTIVITY
+ */
+
+#include <linux/interrupt.h> /* For tasklet interface. */
+#include <linux/platform_device.h> /* For kernel module interface. */
+#include <linux/spinlock.h> /* For spinlock interface. */
+#include <linux/pmic_adc.h> /* For PMIC ADC driver interface. */
+#include <linux/pmic_status.h>
+#include <mach/pmic_convity.h> /* For PMIC Connectivity driver interface. */
+
+/*
+ * mc13783 Connectivity API
+ */
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_convity_open);
+EXPORT_SYMBOL(pmic_convity_close);
+EXPORT_SYMBOL(pmic_convity_set_mode);
+EXPORT_SYMBOL(pmic_convity_get_mode);
+EXPORT_SYMBOL(pmic_convity_reset);
+EXPORT_SYMBOL(pmic_convity_set_callback);
+EXPORT_SYMBOL(pmic_convity_clear_callback);
+EXPORT_SYMBOL(pmic_convity_get_callback);
+EXPORT_SYMBOL(pmic_convity_usb_set_speed);
+EXPORT_SYMBOL(pmic_convity_usb_get_speed);
+EXPORT_SYMBOL(pmic_convity_usb_set_power_source);
+EXPORT_SYMBOL(pmic_convity_usb_get_power_source);
+EXPORT_SYMBOL(pmic_convity_usb_set_xcvr);
+EXPORT_SYMBOL(pmic_convity_usb_get_xcvr);
+EXPORT_SYMBOL(pmic_convity_usb_otg_set_dlp_duration);
+EXPORT_SYMBOL(pmic_convity_usb_otg_get_dlp_duration);
+EXPORT_SYMBOL(pmic_convity_usb_otg_set_config);
+EXPORT_SYMBOL(pmic_convity_usb_otg_clear_config);
+EXPORT_SYMBOL(pmic_convity_usb_otg_get_config);
+EXPORT_SYMBOL(pmic_convity_set_output);
+EXPORT_SYMBOL(pmic_convity_rs232_set_config);
+EXPORT_SYMBOL(pmic_convity_rs232_get_config);
+EXPORT_SYMBOL(pmic_convity_cea936_exit_signal);
+
+/*! @def SET_BITS
+ * Set a register field to a given value.
+ */
+
+#define SET_BITS(reg, field, value) (((value) << reg.field.offset) & \
+ reg.field.mask)
+
+/*! @def GET_BITS
+ * Get the current value of a given register field.
+ */
+#define GET_BITS(reg, value) (((value) & reg.mask) >> \
+ reg.offset)
+
+/*!
+ * @brief Define the possible states for a device handle.
+ *
+ * This enumeration is used to track the current state of each device handle.
+ */
+typedef enum {
+ HANDLE_FREE, /*!< Handle is available for use. */
+ HANDLE_IN_USE /*!< Handle is currently in use. */
+} HANDLE_STATE;
+
+/*
+ * This structure is used to define a specific hardware register field.
+ *
+ * All hardware register fields are defined using an offset to the LSB
+ * and a mask. The offset is used to right shift a register value before
+ * applying the mask to actually obtain the value of the field.
+ */
+typedef struct {
+ const unsigned char offset; /* Offset of LSB of register field. */
+ const unsigned int mask; /* Mask value used to isolate register field. */
+} REGFIELD;
+
+/*!
+ * @brief This structure is used to identify the fields in the USBCNTRL_REG_0 hardware register.
+ *
+ * This structure lists all of the fields within the USBCNTRL_REG_0 hardware
+ * register.
+ */
+typedef struct {
+ REGFIELD FSENB; /*!< USB Full Speed Enable */
+ REGFIELD USB_SUSPEND; /*!< USB Suspend Mode Enable */
+ REGFIELD USB_PU; /*!< USB Pullup Enable */
+ REGFIELD UDP_PD; /*!< USB Data Plus Pulldown Enable */
+ REGFIELD UDM_PD; /*!< USB 150K UDP Pullup Enable */
+ REGFIELD DP150K_PU; /*!< USB Pullup/Pulldown Override Enable */
+ REGFIELD VBUSPDENB; /*!< USB VBUS Pulldown NMOS Switch Enable */
+ REGFIELD CURRENT_LIMIT; /*!< USB Regulator Current Limit Setting-3 bits */
+ REGFIELD DLP_SRP; /*!< USB Data Line Pulsing Timer Enable */
+ REGFIELD SE0_CONN; /*!< USB Pullup Connect When SE0 Detected */
+ REGFIELD USBXCVREN; /*!< USB Transceiver Enabled When INTERFACE_MODE[2:0]=000 and RESETB=high */
+ REGFIELD PULLOVR; /*!< 1K5 Pullup and UDP/UDM Pulldown Disable When UTXENB=Low */
+ REGFIELD INTERFACE_MODE; /*!< Connectivity Interface Mode Select-3 Bits */
+ REGFIELD DATSE0; /*!< USB Single or Differential Mode Select */
+ REGFIELD BIDIR; /*!< USB Unidirectional/Bidirectional Transmission */
+ REGFIELD USBCNTRL; /*!< USB Mode of Operation controlled By USBEN/SPI Pin */
+ REGFIELD IDPD; /*!< USB UID Pulldown Enable */
+ REGFIELD IDPULSE; /*!< USB Pulse to Gnd on UID Line Generated */
+ REGFIELD IDPUCNTRL; /*!< USB UID Pin pulled high By 5ua Curr Source */
+ REGFIELD DMPULSE; /*!< USB Positive pulse on the UDM Line Generated */
+} USBCNTRL_REG_0;
+
+/*!
+ * @brief This variable is used to access the USBCNTRL_REG_0 hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * USBCNTRL_REG_0 hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const USBCNTRL_REG_0 regUSB0 = {
+ {0, 0x000001}, /*!< FSENB */
+ {1, 0x000002}, /*!< USB_SUSPEND */
+ {2, 0x000004}, /*!< USB_PU */
+ {3, 0x000008}, /*!< UDP_PD */
+ {4, 0x000010}, /*!< UDM_PD */
+ {5, 0x000020}, /*!< DP150K_PU */
+ {6, 0x000040}, /*!< VBUSPDENB */
+ {7, 0x000380}, /*!< CURRENT_LIMIT */
+ {10, 0x000400}, /*!< DLP_SRP */
+ {11, 0x000800}, /*!< SE0_CONN */
+ {12, 0x001000}, /*!< USBXCVREN */
+ {13, 0x002000}, /*!< PULLOVR */
+ {14, 0x01c000}, /*!< INTERFACE_MODE */
+ {17, 0x020000}, /*!< DATSE0 */
+ {18, 0x040000}, /*!< BIDIR */
+ {19, 0x080000}, /*!< USBCNTRL */
+ {20, 0x100000}, /*!< IDPD */
+ {21, 0x200000}, /*!< IDPULSE */
+ {22, 0x400000}, /*!< IDPUCNTRL */
+ {23, 0x800000} /*!< DMPULSE */
+
+};
+
+/*!
+ * @brief This structure is used to identify the fields in the USBCNTRL_REG_1 hardware register.
+ *
+ * This structure lists all of the fields within the USBCNTRL_REG_1 hardware
+ * register.
+ */
+typedef struct {
+ REGFIELD VUSBIN; /*!< Controls The Input Source For VUSB */
+ REGFIELD VUSB; /*!< VUSB Output Voltage Select-High=3.3V Low=2.775V */
+ REGFIELD VUSBEN; /*!< VUSB Output Enable- */
+ REGFIELD VBUSEN; /*!< VBUS Output Enable- */
+ REGFIELD RSPOL; /*!< Low=RS232 TX on UDM, RX on UDP
+ High= RS232 TX on UDP, RX on UDM */
+ REGFIELD RSTRI; /*!< TX Forced To Tristate in RS232 Mode Only */
+ REGFIELD ID100kPU; /*!< 100k UID Pullup Enabled */
+} USBCNTRL_REG_1;
+
+/*!
+ * @brief This variable is used to access the USBCNTRL_REG_1 hardware register.
+ *
+ * This variable defines how to access all of the fields within the
+ * USBCNTRL_REG_1 hardware register. The initial values consist of the offset
+ * and mask values needed to access each of the register fields.
+ */
+static const USBCNTRL_REG_1 regUSB1 = {
+ {0, 0x000003}, /*!< VUSBIN-2 Bits */
+ {2, 0x000004}, /*!< VUSB */
+ {3, 0x000008}, /*!< VUSBEN */
+ /*{4, 0x000010} *//*!< Reserved */
+ {5, 0x000020}, /*!< VBUSEN */
+ {6, 0x000040}, /*!< RSPOL */
+ {7, 0x000080}, /*!< RSTRI */
+ {8, 0x000100} /*!< ID100kPU */
+ /*!< 9-23 Unused */
+};
+
+/*! Define a mask to access the entire hardware register. */
+static const unsigned int REG_FULLMASK = 0xffffff;
+
+/*! Define the mc13783 USBCNTRL_REG_0 register power on reset state. */
+static const unsigned int RESET_USBCNTRL_REG_0 = 0x080060;
+
+/*! Define the mc13783 USBCNTRL_REG_1 register power on reset state. */
+static const unsigned int RESET_USBCNTRL_REG_1 = 0x000006;
+
+static pmic_event_callback_t eventNotify;
+
+/*!
+ * @brief This structure is used to maintain the current device driver state.
+ *
+ * This structure maintains the current state of the connectivity driver. This
+ * includes both the PMIC hardware state as well as the device handle and
+ * callback states.
+ */
+
+typedef struct {
+ PMIC_CONVITY_HANDLE handle; /*!< Device handle. */
+ HANDLE_STATE handle_state; /*!< Device handle
+ state. */
+ PMIC_CONVITY_MODE mode; /*!< Device mode. */
+ PMIC_CONVITY_CALLBACK callback; /*!< Event callback function pointer. */
+ PMIC_CONVITY_EVENTS eventMask; /*!< Event mask. */
+ PMIC_CONVITY_USB_SPEED usbSpeed; /*!< USB connection
+ speed. */
+ PMIC_CONVITY_USB_MODE usbMode; /*!< USB connection
+ mode. */
+ PMIC_CONVITY_USB_POWER_IN usbPowerIn; /*!< USB transceiver
+ power source. */
+ PMIC_CONVITY_USB_POWER_OUT usbPowerOut; /*!< USB transceiver
+ power output
+ level. */
+ PMIC_CONVITY_USB_TRANSCEIVER_MODE usbXcvrMode; /*!< USB transceiver
+ mode. */
+ unsigned int usbDlpDuration; /*!< USB Data Line
+ Pulsing duration. */
+ PMIC_CONVITY_USB_OTG_CONFIG usbOtgCfg; /*!< USB OTG
+ configuration
+ options. */
+ PMIC_CONVITY_RS232_INTERNAL rs232CfgInternal; /*!< RS-232 internal
+ connections. */
+ PMIC_CONVITY_RS232_EXTERNAL rs232CfgExternal; /*!< RS-232 external
+ connections. */
+} pmic_convity_state_struct;
+
+/*!
+ * @brief This structure is used to maintain the current device driver state.
+ *
+ * This structure maintains the current state of the driver in USB mode. This
+ * includes both the PMIC hardware state as well as the device handle and
+ * callback states.
+ */
+
+typedef struct {
+ PMIC_CONVITY_HANDLE handle; /*!< Device handle. */
+ HANDLE_STATE handle_state; /*!< Device handle
+ state. */
+ PMIC_CONVITY_MODE mode; /*!< Device mode. */
+ PMIC_CONVITY_CALLBACK callback; /*!< Event callback function pointer. */
+ PMIC_CONVITY_EVENTS eventMask; /*!< Event mask. */
+ PMIC_CONVITY_USB_SPEED usbSpeed; /*!< USB connection
+ speed. */
+ PMIC_CONVITY_USB_MODE usbMode; /*!< USB connection
+ mode. */
+ PMIC_CONVITY_USB_POWER_IN usbPowerIn; /*!< USB transceiver
+ power source. */
+ PMIC_CONVITY_USB_POWER_OUT usbPowerOut; /*!< USB transceiver
+ power output
+ level. */
+ PMIC_CONVITY_USB_TRANSCEIVER_MODE usbXcvrMode; /*!< USB transceiver
+ mode. */
+ unsigned int usbDlpDuration; /*!< USB Data Line
+ Pulsing duration. */
+ PMIC_CONVITY_USB_OTG_CONFIG usbOtgCfg; /*!< USB OTG
+ configuration
+ options. */
+} pmic_convity_usb_state;
+
+/*!
+ * @brief This structure is used to maintain the current device driver state.
+ *
+ * This structure maintains the current state of the driver in RS_232 mode. This
+ * includes both the PMIC hardware state as well as the device handle and
+ * callback states.
+ */
+
+typedef struct {
+ PMIC_CONVITY_HANDLE handle; /*!< Device handle. */
+ HANDLE_STATE handle_state; /*!< Device handle
+ state. */
+ PMIC_CONVITY_MODE mode; /*!< Device mode. */
+ PMIC_CONVITY_CALLBACK callback; /*!< Event callback function pointer. */
+ PMIC_CONVITY_EVENTS eventMask; /*!< Event mask. */
+ PMIC_CONVITY_RS232_INTERNAL rs232CfgInternal; /*!< RS-232 internal
+ connections. */
+ PMIC_CONVITY_RS232_EXTERNAL rs232CfgExternal; /*!< RS-232 external
+ connections. */
+} pmic_convity_rs232_state;
+
+/*!
+ * @brief This structure is used to maintain the current device driver state.
+ *
+ * This structure maintains the current state of the driver in cea-936 mode. This
+ * includes both the PMIC hardware state as well as the device handle and
+ * callback states.
+ */
+
+typedef struct {
+ PMIC_CONVITY_HANDLE handle; /*!< Device handle. */
+ HANDLE_STATE handle_state; /*!< Device handle
+ state. */
+ PMIC_CONVITY_MODE mode; /*!< Device mode. */
+ PMIC_CONVITY_CALLBACK callback; /*!< Event callback function pointer. */
+ PMIC_CONVITY_EVENTS eventMask; /*!< Event mask. */
+
+} pmic_convity_cea936_state;
+
+/*!
+ * @brief Identifies the hardware interrupt source.
+ *
+ * This enumeration identifies which of the possible hardware interrupt
+ * sources actually caused the current interrupt handler to be called.
+ */
+typedef enum {
+ CORE_EVENT_4V4 = 1, /*!< Detected USB 4.4 V event. */
+ CORE_EVENT_2V0 = 2, /*!< Detected USB 2.0 V event. */
+ CORE_EVENT_0V8 = 4, /*!< Detected USB 0.8 V event. */
+ CORE_EVENT_ABDET = 8 /*!< Detected USB mini A-B connector event. */
+} PMIC_CORE_EVENT;
+
+/*!
+ * @brief This structure defines the reset/power on state for the Connectivity driver.
+ */
+static const pmic_convity_state_struct reset = {
+ 0,
+ HANDLE_FREE,
+ USB,
+ NULL,
+ 0,
+ USB_FULL_SPEED,
+ USB_PERIPHERAL,
+ USB_POWER_INTERNAL,
+ USB_POWER_3V3,
+ USB_TRANSCEIVER_OFF,
+ 0,
+ USB_PULL_OVERRIDE | USB_VBUS_CURRENT_LIMIT_HIGH,
+ RS232_TX_USE0VM_RX_UDATVP,
+ RS232_TX_UDM_RX_UDP
+};
+
+/*!
+ * @brief This structure maintains the current state of the Connectivity driver.
+ *
+ * The initial values must be identical to the reset state defined by the
+ * #reset variable.
+ */
+static pmic_convity_usb_state usb = {
+ 0,
+ HANDLE_FREE,
+ USB,
+ NULL,
+ 0,
+ USB_FULL_SPEED,
+ USB_PERIPHERAL,
+ USB_POWER_INTERNAL,
+ USB_POWER_3V3,
+ USB_TRANSCEIVER_OFF,
+ 0,
+ USB_PULL_OVERRIDE | USB_VBUS_CURRENT_LIMIT_HIGH,
+};
+
+/*!
+ * @brief This structure maintains the current state of the Connectivity driver.
+ *
+ * The initial values must be identical to the reset state defined by the
+ * #reset variable.
+ */
+static pmic_convity_rs232_state rs_232 = {
+ 0,
+ HANDLE_FREE,
+ RS232_1,
+ NULL,
+ 0,
+ RS232_TX_USE0VM_RX_UDATVP,
+ RS232_TX_UDM_RX_UDP
+};
+
+/*!
+ * @brief This structure maintains the current state of the Connectivity driver.
+ *
+ * The initial values must be identical to the reset state defined by the
+ * #reset variable.
+ */
+static pmic_convity_cea936_state cea_936 = {
+ 0,
+ HANDLE_FREE,
+ CEA936_MONO,
+ NULL,
+ 0,
+};
+
+/*!
+ * @brief This spinlock is used to provide mutual exclusion.
+ *
+ * Create a spinlock that can be used to provide mutually exclusive
+ * read/write access to the globally accessible "convity" data structure
+ * that was defined above. Mutually exclusive access is required to
+ * ensure that the convity data structure is consistent at all times
+ * when possibly accessed by multiple threads of execution (for example,
+ * while simultaneously handling a user request and an interrupt event).
+ *
+ * We need to use a spinlock sometimes because we need to provide mutual
+ * exclusion while handling a hardware interrupt.
+ */
+static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+
+/*!
+ * @brief This mutex is used to provide mutual exclusion.
+ *
+ * Create a mutex that can be used to provide mutually exclusive
+ * read/write access to the globally accessible data structures
+ * that were defined above. Mutually exclusive access is required to
+ * ensure that the Connectivity data structures are consistent at all
+ * times when possibly accessed by multiple threads of execution.
+ *
+ * Note that we use a mutex instead of the spinlock whenever disabling
+ * interrupts while in the critical section is not required. This helps
+ * to minimize kernel interrupt handling latency.
+ */
+static DECLARE_MUTEX(mutex);
+
+/* Prototype for the connectivity driver tasklet function. */
+static void pmic_convity_tasklet(struct work_struct *work);
+
+/*!
+ * @brief Tasklet handler for the connectivity driver.
+ *
+ * Declare a tasklet that will do most of the processing for all of the
+ * connectivity-related interrupt events (USB4.4VI, USB2.0VI, USB0.8VI,
+ * and AB_DETI). Note that we cannot do all of the required processing
+ * within the interrupt handler itself because we may need to call the
+ * ADC driver to measure voltages as well as calling any user-registered
+ * callback functions.
+ */
+DECLARE_WORK(convityTasklet, pmic_convity_tasklet);
+
+/*!
+ * @brief Global variable to track currently active interrupt events.
+ *
+ * This global variable is used to keep track of all of the currently
+ * active interrupt events for the connectivity driver. Note that access
+ * to this variable may occur while within an interrupt context and,
+ * therefore, must be guarded by using a spinlock.
+ */
+static PMIC_CORE_EVENT eventID = 0;
+
+/* Prototypes for all static connectivity driver functions. */
+static PMIC_STATUS pmic_convity_set_mode_internal(const PMIC_CONVITY_MODE mode);
+static PMIC_STATUS pmic_convity_deregister_all(void);
+static void pmic_convity_event_handler(void *param);
+
+/**************************************************************************
+ * General setup and configuration functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name General Setup and Configuration Connectivity APIs
+ * Functions for setting up and configuring the connectivity hardware.
+ */
+/*@{*/
+
+/*!
+ * Attempt to open and gain exclusive access to the PMIC connectivity
+ * hardware. An initial operating mode must also be specified.
+ *
+ * If the open request is successful, then a numeric handle is returned
+ * and this handle must be used in all subsequent function calls. The
+ * same handle must also be used in the pmic_convity_close() call when use
+ * of the PMIC connectivity hardware is no longer required.
+ *
+ * @param handle device handle from open() call
+ * @param mode initial connectivity operating mode
+ *
+ * @return PMIC_SUCCESS if the open request was successful
+ */
+PMIC_STATUS pmic_convity_open(PMIC_CONVITY_HANDLE * const handle,
+ const PMIC_CONVITY_MODE mode)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ if (handle == (PMIC_CONVITY_HANDLE *) NULL) {
+ /* Do not dereference a NULL pointer. */
+ return PMIC_ERROR;
+ }
+
+ /* We only need to acquire a mutex here because the interrupt handler
+ * never modifies the device handle or device handle state. Therefore,
+ * we don't need to worry about conflicts with the interrupt handler
+ * or the need to execute in an interrupt context.
+ *
+ * But we do need a critical section here to avoid problems in case
+ * multiple calls to pmic_convity_open() are made since we can only
+ * allow one of them to succeed.
+ */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ /* Check the current device handle state and acquire the handle if
+ * it is available.
+ */
+ if ((usb.handle_state != HANDLE_FREE)
+ && (rs_232.handle_state != HANDLE_FREE)
+ && (cea_936.handle_state != HANDLE_FREE)) {
+
+ /* Cannot open the PMIC connectivity hardware at this time or an invalid
+ * mode was requested.
+ */
+ *handle = reset.handle;
+ } else {
+
+ if (mode == USB) {
+ usb.handle = (PMIC_CONVITY_HANDLE) (&usb);
+ usb.handle_state = HANDLE_IN_USE;
+ } else if ((mode == RS232_1) || (mode == RS232_2)) {
+ rs_232.handle = (PMIC_CONVITY_HANDLE) (&rs_232);
+ rs_232.handle_state = HANDLE_IN_USE;
+ } else if ((mode == CEA936_STEREO) || (mode == CEA936_MONO)
+ || (mode == CEA936_TEST_LEFT)
+ || (mode == CEA936_TEST_RIGHT)) {
+ cea_936.handle = (PMIC_CONVITY_HANDLE) (&cea_936);
+ cea_936.handle_state = HANDLE_IN_USE;
+
+ }
+ /* Let's begin by acquiring the connectivity device handle. */
+ /* Then we can try to set the desired operating mode. */
+ rc = pmic_convity_set_mode_internal(mode);
+
+ if (rc == PMIC_SUCCESS) {
+ /* Successfully set the desired operating mode, now return the
+ * handle to the caller.
+ */
+ if (mode == USB) {
+ *handle = usb.handle;
+ } else if ((mode == RS232_1) || (mode == RS232_2)) {
+ *handle = rs_232.handle;
+ } else if ((mode == CEA936_STEREO)
+ || (mode == CEA936_MONO)
+ || (mode == CEA936_TEST_LEFT)
+ || (mode == CEA936_TEST_RIGHT)) {
+ *handle = cea_936.handle;
+ }
+ } else {
+ /* Failed to set the desired mode, return the handle to an unused
+ * state.
+ */
+ if (mode == USB) {
+ usb.handle = reset.handle;
+ usb.handle_state = reset.handle_state;
+ } else if ((mode == RS232_1) || (mode == RS232_2)) {
+ rs_232.handle = reset.handle;
+ rs_232.handle_state = reset.handle_state;
+ } else if ((mode == CEA936_STEREO)
+ || (mode == CEA936_MONO)
+ || (mode == CEA936_TEST_LEFT)
+ || (mode == CEA936_TEST_RIGHT)) {
+ cea_936.handle = reset.handle;
+ cea_936.handle_state = reset.handle_state;
+ }
+
+ *handle = reset.handle;
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Terminate further access to the PMIC connectivity hardware. Also allows
+ * another process to call pmic_convity_open() to gain access.
+ *
+ * @param handle device handle from open() call
+ *
+ * @return PMIC_SUCCESS if the close request was successful
+ */
+PMIC_STATUS pmic_convity_close(const PMIC_CONVITY_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Begin a critical section here to avoid the possibility of race
+ * conditions if multiple threads happen to call this function and
+ * pmic_convity_open() at the same time.
+ */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ /* Confirm that the device handle matches the one assigned in the
+ * pmic_convity_open() call and then close the connection.
+ */
+ if (((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+ && (rs_232.handle_state ==
+ HANDLE_IN_USE))
+ || ((handle == cea_936.handle)
+ && (cea_936.handle_state == HANDLE_IN_USE))) {
+ rc = PMIC_SUCCESS;
+
+ /* Deregister for all existing callbacks if necessary and make sure
+ * that the event handling settings are consistent following the
+ * close operation.
+ */
+ if ((usb.callback != reset.callback)
+ || (rs_232.callback != reset.callback)
+ || (cea_936.callback != reset.callback)) {
+ /* Deregister the existing callback function and all registered
+ * events before we completely close the handle.
+ */
+ rc = pmic_convity_deregister_all();
+ if (rc == PMIC_SUCCESS) {
+
+ } else if (usb.eventMask != reset.eventMask) {
+ /* Having a non-zero eventMask without a callback function being
+ * defined should never occur but let's just make sure here that
+ * we keep things consistent.
+ */
+ usb.eventMask = reset.eventMask;
+ /* Mark the connectivity device handle as being closed. */
+ usb.handle = reset.handle;
+ usb.handle_state = reset.handle_state;
+
+ } else if (rs_232.eventMask != reset.eventMask) {
+
+ rs_232.eventMask = reset.eventMask;
+ /* Mark the connectivity device handle as being closed. */
+ rs_232.handle = reset.handle;
+ rs_232.handle_state = reset.handle_state;
+
+ } else if (cea_936.eventMask != reset.eventMask) {
+ cea_936.eventMask = reset.eventMask;
+ /* Mark the connectivity device handle as being closed. */
+ cea_936.handle = reset.handle;
+ cea_936.handle_state = reset.handle_state;
+
+ }
+
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Change the current operating mode of the PMIC connectivity hardware.
+ * The available connectivity operating modes is hardware dependent and
+ * consists of one or more of the following: USB (including USB On-the-Go),
+ * RS-232, and CEA-936. Requesting an operating mode that is not supported
+ * by the PMIC hardware will return PMIC_NOT_SUPPORTED.
+ *
+ * @param handle device handle from
+ open() call
+ * @param mode desired operating mode
+ *
+ * @return PMIC_SUCCESS if the requested mode was successfully set
+ */
+PMIC_STATUS pmic_convity_set_mode(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_MODE mode)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if (((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+ && (rs_232.handle_state ==
+ HANDLE_IN_USE))
+ || ((handle == cea_936.handle)
+ && (cea_936.handle_state == HANDLE_IN_USE))) {
+ rc = pmic_convity_set_mode_internal(mode);
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Get the current operating mode for the PMIC connectivity hardware.
+ *
+ * @param handle device handle from open() call
+ * @param mode the current PMIC connectivity operating mode
+ *
+ * @return PMIC_SUCCESS if the requested mode was successfully set
+ */
+PMIC_STATUS pmic_convity_get_mode(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_MODE * const mode)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+ && (rs_232.
+ handle_state ==
+ HANDLE_IN_USE))
+ || ((handle == cea_936.handle)
+ && (cea_936.handle_state == HANDLE_IN_USE)))
+ && (mode != (PMIC_CONVITY_MODE *) NULL)) {
+
+ *mode = usb.mode;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Restore all registers to the initial power-on/reset state.
+ *
+ * @param handle device handle from open() call
+ *
+ * @return PMIC_SUCCESS if the reset was successful
+ */
+PMIC_STATUS pmic_convity_reset(const PMIC_CONVITY_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+ if (((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+ && (rs_232.handle_state ==
+ HANDLE_IN_USE))
+ || ((handle == cea_936.handle)
+ && (cea_936.handle_state == HANDLE_IN_USE))) {
+
+ /* Reset the PMIC Connectivity register to it's power on state. */
+ rc = pmic_write_reg(REG_USB, RESET_USBCNTRL_REG_0,
+ REG_FULLMASK);
+
+ rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+ RESET_USBCNTRL_REG_1, REG_FULLMASK);
+
+ if (rc == PMIC_SUCCESS) {
+ /* Also reset the device driver state data structure. */
+
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Register a callback function that will be used to signal PMIC connectivity
+ * events. For example, the USB subsystem should register a callback function
+ * in order to be notified of device connect/disconnect events. Note, however,
+ * that non-USB events may also be signalled depending upon the PMIC hardware
+ * capabilities. Therefore, the callback function must be able to properly
+ * handle all of the possible events if support for non-USB peripherals is
+ * also to be included.
+ *
+ * @param handle device handle from open() call
+ * @param func a pointer to the callback function
+ * @param eventMask a mask selecting events to be notified
+ *
+ * @return PMIC_SUCCESS if the callback was successful registered
+ */
+PMIC_STATUS pmic_convity_set_callback(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_CALLBACK func,
+ const PMIC_CONVITY_EVENTS eventMask)
+{
+ unsigned long flags;
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* We need to start a critical section here to ensure a consistent state
+ * in case simultaneous calls to pmic_convity_set_callback() are made. In
+ * that case, we must serialize the calls to ensure that the "callback"
+ * and "eventMask" state variables are always consistent.
+ *
+ * Note that we don't actually need to acquire the spinlock until later
+ * when we are finally ready to update the "callback" and "eventMask"
+ * state variables which are shared with the interrupt handler.
+ */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+
+ /* Return an error if either the callback function or event mask
+ * is not properly defined.
+ *
+ * It is also considered an error if a callback function has already
+ * been defined. If you wish to register for a new set of events,
+ * then you must first call pmic_convity_clear_callback() to
+ * deregister the existing callback function and list of events
+ * before trying to register a new callback function.
+ */
+ if ((func == NULL) || (eventMask == 0)
+ || (usb.callback != NULL)) {
+ rc = PMIC_ERROR;
+
+ /* Register for PMIC events from the core protocol driver. */
+ } else {
+
+ if ((eventMask & USB_DETECT_4V4_RISE) ||
+ (eventMask & USB_DETECT_4V4_FALL)) {
+ /* We need to register for the 4.4V interrupt. */
+ //EVENT_USBI or EVENT_USB_44VI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_4V4);
+ rc = pmic_event_subscribe(EVENT_USBI,
+ eventNotify);
+
+ if (rc != PMIC_SUCCESS) {
+ return rc;
+ }
+ }
+
+ if ((eventMask & USB_DETECT_2V0_RISE) ||
+ (eventMask & USB_DETECT_2V0_FALL)) {
+ /* We need to register for the 2.0V interrupt. */
+ //EVENT_USB_20VI or EVENT_USBI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_2V0);
+ rc = pmic_event_subscribe(EVENT_USBI,
+ eventNotify);
+
+ if (rc != PMIC_SUCCESS) {
+ goto Cleanup_4V4;
+ }
+ }
+
+ if ((eventMask & USB_DETECT_0V8_RISE) ||
+ (eventMask & USB_DETECT_0V8_FALL)) {
+ /* We need to register for the 0.8V interrupt. */
+ //EVENT_USB_08VI or EVENT_USBI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_0V8);
+ rc = pmic_event_subscribe(EVENT_USBI,
+ eventNotify);
+
+ if (rc != PMIC_SUCCESS) {
+ goto Cleanup_2V0;
+ }
+ }
+
+ if ((eventMask & USB_DETECT_MINI_A) ||
+ (eventMask & USB_DETECT_MINI_B)
+ || (eventMask & USB_DETECT_NON_USB_ACCESSORY)
+ || (eventMask & USB_DETECT_FACTORY_MODE)) {
+ /* We need to register for the AB_DET interrupt. */
+ //EVENT_AB_DETI or EVENT_IDI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_ABDET);
+ rc = pmic_event_subscribe(EVENT_IDI,
+ eventNotify);
+
+ if (rc != PMIC_SUCCESS) {
+ goto Cleanup_0V8;
+ }
+ }
+
+ /* Use a critical section to maintain a consistent state. */
+ spin_lock_irqsave(&lock, flags);
+
+ /* Successfully registered for all events. */
+ usb.callback = func;
+ usb.eventMask = eventMask;
+ spin_unlock_irqrestore(&lock, flags);
+
+ goto End;
+
+ /* This section unregisters any already registered events if we should
+ * encounter an error partway through the registration process. Note
+ * that we don't check the return status here since it is already set
+ * to PMIC_ERROR before we get here.
+ */
+ Cleanup_0V8:
+
+ if ((eventMask & USB_DETECT_0V8_RISE) ||
+ (eventMask & USB_DETECT_0V8_FALL)) {
+ //EVENT_USB_08VI or EVENT_USBI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_0V8);
+ pmic_event_unsubscribe(EVENT_USBI, eventNotify);
+ goto End;
+ }
+
+ Cleanup_2V0:
+
+ if ((eventMask & USB_DETECT_2V0_RISE) ||
+ (eventMask & USB_DETECT_2V0_FALL)) {
+ //EVENT_USB_20VI or EVENT_USBI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_2V0);
+ pmic_event_unsubscribe(EVENT_USBI, eventNotify);
+ goto End;
+ }
+
+ Cleanup_4V4:
+
+ if ((eventMask & USB_DETECT_4V4_RISE) ||
+ (eventMask & USB_DETECT_4V4_FALL)) {
+ //EVENT_USB_44VI or EVENT_USBI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_4V4);
+ pmic_event_unsubscribe(EVENT_USBI, eventNotify);
+ }
+ }
+ /* Exit the critical section. */
+
+ }
+ End:up(&mutex);
+ return rc;
+
+}
+
+/*!
+ * Clears the current callback function. If this function returns successfully
+ * then all future Connectivity events will only be handled by the default
+ * handler within the Connectivity driver.
+ *
+ * @param handle device handle from open() call
+ *
+ * @return PMIC_SUCCESS if the callback was successful cleared
+ */
+PMIC_STATUS pmic_convity_clear_callback(const PMIC_CONVITY_HANDLE handle)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+ if (((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+ && (rs_232.handle_state ==
+ HANDLE_IN_USE))
+ || ((handle == cea_936.handle)
+ && (cea_936.handle_state == HANDLE_IN_USE))) {
+
+ rc = pmic_convity_deregister_all();
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Get the current callback function and event mask.
+ *
+ * @param handle device handle from open() call
+ * @param func the current callback function
+ * @param eventMask the current event selection mask
+ *
+ * @return PMIC_SUCCESS if the callback information was successful
+ * retrieved
+ */
+PMIC_STATUS pmic_convity_get_callback(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_CALLBACK * const func,
+ PMIC_CONVITY_EVENTS * const eventMask)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+ if ((((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE)) || ((handle == rs_232.handle)
+ && (rs_232.
+ handle_state ==
+ HANDLE_IN_USE))
+ || ((handle == cea_936.handle)
+ && (cea_936.handle_state == HANDLE_IN_USE)))
+ && (func != (PMIC_CONVITY_CALLBACK *) NULL)
+ && (eventMask != (PMIC_CONVITY_EVENTS *) NULL)) {
+ *func = usb.callback;
+ *eventMask = usb.eventMask;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+
+ up(&mutex);
+
+ return rc;
+}
+
+/*@*/
+
+/**************************************************************************
+ * USB-specific configuration and setup functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name USB and USB-OTG Connectivity APIs
+ * Functions for controlling USB and USB-OTG connectivity.
+ */
+/*@{*/
+
+/*!
+ * Set the USB transceiver speed.
+ *
+ * @param handle device handle from open() call
+ * @param speed the desired USB transceiver speed
+ *
+ * @return PMIC_SUCCESS if the transceiver speed was successfully set
+ */
+PMIC_STATUS pmic_convity_usb_set_speed(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_SPEED speed)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+ unsigned int reg_value = 0;
+ unsigned int reg_mask = SET_BITS(regUSB0, FSENB, 1);
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if (handle == (rs_232.handle || cea_936.handle)) {
+ return PMIC_PARAMETER_ERROR;
+ } else {
+ if ((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE)) {
+ /* Validate the function parameters and if they are valid, then
+ * configure the pull-up and pull-down resistors as required for
+ * the desired operating mode.
+ */
+ if ((speed == USB_HIGH_SPEED)) {
+ /*
+ * The USB transceiver also does not support the high speed mode
+ * (which is also optional under the USB OTG specification).
+ */
+ rc = PMIC_NOT_SUPPORTED;
+ } else if ((speed != USB_LOW_SPEED)
+ && (speed != USB_FULL_SPEED)) {
+ /* Final validity check on the speed parameter. */
+ rc = PMIC_ERROR;;
+ } else {
+ /* First configure the D+ and D- pull-up/pull-down resistors as
+ * per the USB OTG specification.
+ */
+ if (speed == USB_FULL_SPEED) {
+ /* Activate pull-up on D+ and pull-down on D-. */
+ reg_value =
+ SET_BITS(regUSB0, UDM_PD, 1);
+ } else if (speed == USB_LOW_SPEED) {
+ /* Activate pull-up on D+ and pull-down on D-. */
+ reg_value = SET_BITS(regUSB0, FSENB, 1);
+ }
+
+ /* Now set the desired USB transceiver speed. Note that
+ * USB_FULL_SPEED simply requires FSENB=0 (which it
+ * already is).
+ */
+
+ rc = pmic_write_reg(REG_USB, reg_value,
+ reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ usb.usbSpeed = speed;
+ }
+ }
+ }
+ }
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Get the USB transceiver speed.
+ *
+ * @param handle device handle from open() call
+ * @param speed the current USB transceiver speed
+ * @param mode the current USB transceiver mode
+ *
+ * @return PMIC_SUCCESS if the transceiver speed was successfully
+ * obtained
+ */
+PMIC_STATUS pmic_convity_usb_get_speed(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_USB_SPEED * const speed,
+ PMIC_CONVITY_USB_MODE * const mode)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE) &&
+ (speed != (PMIC_CONVITY_USB_SPEED *) NULL) &&
+ (mode != (PMIC_CONVITY_USB_MODE *) NULL)) {
+ *speed = usb.usbSpeed;
+ *mode = usb.usbMode;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * This function enables/disables VUSB and VBUS output.
+ * This API configures the VUSBEN and VBUSEN bits of USB register
+ *
+ * @param handle device handle from open() call
+ * @param out_type true, for VBUS
+ * false, for VUSB
+ * @param out if true, output is enabled
+ * if false, output is disabled
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_convity_set_output(const PMIC_CONVITY_HANDLE handle,
+ bool out_type, bool out)
+{
+
+ PMIC_STATUS rc = PMIC_ERROR;
+ unsigned int reg_value = 0;
+
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+
+ if ((out_type == 0) && (out == 1)) {
+
+ reg_value = SET_BITS(regUSB1, VUSBEN, 1);
+ reg_mask = SET_BITS(regUSB1, VUSBEN, 1);
+
+ rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+ reg_value, reg_mask);
+ } else if (out_type == 0 && out == 0) {
+ reg_mask = SET_BITS(regUSB1, VBUSEN, 1);
+
+ rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+ reg_value, reg_mask);
+ } else if (out_type == 1 && out == 1) {
+
+ reg_value = SET_BITS(regUSB1, VBUSEN, 1);
+ reg_mask = SET_BITS(regUSB1, VBUSEN, 1);
+
+ rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+ reg_value, reg_mask);
+ }
+
+ else if (out_type == 1 && out == 0) {
+
+ reg_mask = SET_BITS(regUSB1, VBUSEN, 1);
+ rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+ reg_value, reg_mask);
+ }
+
+ /*else {
+
+ rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+ reg_value, reg_mask);
+ } */
+ }
+
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Set the USB transceiver's power supply configuration.
+ *
+ * @param handle device handle from open() call
+ * @param pwrin USB transceiver regulator input power source
+ * @param pwrout USB transceiver regulator output power level
+ *
+ * @return PMIC_SUCCESS if the USB transceiver's power supply
+ * configuration was successfully set
+ */
+PMIC_STATUS pmic_convity_usb_set_power_source(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_POWER_IN
+ pwrin,
+ const PMIC_CONVITY_USB_POWER_OUT
+ pwrout)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+ unsigned int reg_value = 0;
+ unsigned int reg_mask = 0;
+ /* SET_BITS(regUSB1, VUSBEN, 1) | SET_BITS(regUSB1, VBUSEN,
+ 1) | SET_BITS(regUSB1,
+ VUSBIN,
+ 2) | */
+ // SET_BITS(regUSB1, VUSB, 1);
+/* SET_BITS(regUSB1, VUSBIN, 1) | SET_BITS(regUSB1, VUSBEN,
+ 1) | SET_BITS(regUSB1,
+ VBUSEN, 1);*/
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+
+ if (pwrin == USB_POWER_INTERNAL_BOOST) {
+ reg_value |= SET_BITS(regUSB1, VUSBIN, 0);
+ reg_mask = SET_BITS(regUSB1, VUSBIN, 1);
+ } else if (pwrin == USB_POWER_VBUS) {
+ reg_value |= SET_BITS(regUSB1, VUSBIN, 1);
+ reg_mask = SET_BITS(regUSB1, VUSBIN, 1);
+ }
+
+ else if (pwrin == USB_POWER_INTERNAL) {
+ reg_value |= SET_BITS(regUSB1, VUSBIN, 2);
+ reg_mask = SET_BITS(regUSB1, VUSBIN, 1);
+ }
+
+ if (pwrout == USB_POWER_3V3) {
+ reg_value |= SET_BITS(regUSB1, VUSB, 1);
+ reg_mask |= SET_BITS(regUSB1, VUSB, 1);
+ }
+
+ else if (pwrout == USB_POWER_2V775) {
+ reg_value |= SET_BITS(regUSB1, VUSB, 0);
+ reg_mask |= SET_BITS(regUSB1, VUSB, 1);
+ }
+ rc = pmic_write_reg(REG_CHARGE_USB_SPARE, reg_value, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ usb.usbPowerIn = pwrin;
+ usb.usbPowerOut = pwrout;
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Get the USB transceiver's current power supply configuration.
+ *
+ * @param handle device handle from open() call
+ * @param pwrin USB transceiver regulator input power source
+ * @param pwrout USB transceiver regulator output power level
+ *
+ * @return PMIC_SUCCESS if the USB transceiver's power supply
+ * configuration was successfully retrieved
+ */
+PMIC_STATUS pmic_convity_usb_get_power_source(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_USB_POWER_IN *
+ const pwrin,
+ PMIC_CONVITY_USB_POWER_OUT *
+ const pwrout)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE) &&
+ (pwrin != (PMIC_CONVITY_USB_POWER_IN *) NULL) &&
+ (pwrout != (PMIC_CONVITY_USB_POWER_OUT *) NULL)) {
+ *pwrin = usb.usbPowerIn;
+ *pwrout = usb.usbPowerOut;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Set the USB transceiver's operating mode.
+ *
+ * @param handle device handle from open() call
+ * @param mode desired operating mode
+ *
+ * @return PMIC_SUCCESS if the USB transceiver's operating mode
+ * was successfully configured
+ */
+PMIC_STATUS pmic_convity_usb_set_xcvr(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_TRANSCEIVER_MODE
+ mode)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+ unsigned int reg_value = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+
+ if (mode == USB_TRANSCEIVER_OFF) {
+ reg_value = SET_BITS(regUSB0, USBXCVREN, 0);
+ reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
+
+ rc = pmic_write_reg(REG_USB, reg_value, reg_mask);
+
+ }
+
+ if (mode == USB_SINGLE_ENDED_UNIDIR) {
+ reg_value |=
+ SET_BITS(regUSB0, DATSE0, 1) | SET_BITS(regUSB0,
+ BIDIR, 0);
+ reg_mask |=
+ SET_BITS(regUSB0, USB_SUSPEND,
+ 1) | SET_BITS(regUSB0, DATSE0,
+ 1) | SET_BITS(regUSB0, BIDIR,
+ 1);
+ } else if (mode == USB_SINGLE_ENDED_BIDIR) {
+ reg_value |=
+ SET_BITS(regUSB0, DATSE0, 1) | SET_BITS(regUSB0,
+ BIDIR, 1);
+ reg_mask |=
+ SET_BITS(regUSB0, USB_SUSPEND,
+ 1) | SET_BITS(regUSB0, DATSE0,
+ 1) | SET_BITS(regUSB0, BIDIR,
+ 1);
+ } else if (mode == USB_DIFFERENTIAL_UNIDIR) {
+ reg_value |=
+ SET_BITS(regUSB0, DATSE0, 0) | SET_BITS(regUSB0,
+ BIDIR, 0);
+ reg_mask |=
+ SET_BITS(regUSB0, USB_SUSPEND,
+ 1) | SET_BITS(regUSB0, DATSE0,
+ 1) | SET_BITS(regUSB0, BIDIR,
+ 1);
+ } else if (mode == USB_DIFFERENTIAL_BIDIR) {
+ reg_value |=
+ SET_BITS(regUSB0, DATSE0, 0) | SET_BITS(regUSB0,
+ BIDIR, 1);
+ reg_mask |=
+ SET_BITS(regUSB0, USB_SUSPEND,
+ 1) | SET_BITS(regUSB0, DATSE0,
+ 1) | SET_BITS(regUSB0, BIDIR,
+ 1);
+ }
+
+ if (mode == USB_SUSPEND_ON) {
+ reg_value |= SET_BITS(regUSB0, USB_SUSPEND, 1);
+ reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
+ } else if (mode == USB_SUSPEND_OFF) {
+ reg_value |= SET_BITS(regUSB0, USB_SUSPEND, 0);
+ reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
+ }
+
+ if (mode == USB_OTG_SRP_DLP_START) {
+ reg_value |= SET_BITS(regUSB0, USB_PU, 0);
+ reg_mask |= SET_BITS(regUSB0, USB_SUSPEND, 1);
+ } else if (mode == USB_OTG_SRP_DLP_STOP) {
+ reg_value &= SET_BITS(regUSB0, USB_PU, 1);
+ reg_mask |= SET_BITS(regUSB0, USB_PU, 1);
+ }
+
+ rc = pmic_write_reg(REG_USB, reg_value, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ usb.usbXcvrMode = mode;
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Get the USB transceiver's current operating mode.
+ *
+ * @param handle device handle from open() call
+ * @param mode current operating mode
+ *
+ * @return PMIC_SUCCESS if the USB transceiver's operating mode
+ * was successfully retrieved
+ */
+PMIC_STATUS pmic_convity_usb_get_xcvr(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_USB_TRANSCEIVER_MODE *
+ const mode)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE) &&
+ (mode != (PMIC_CONVITY_USB_TRANSCEIVER_MODE *) NULL)) {
+ *mode = usb.usbXcvrMode;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Set the Data Line Pulse duration (in milliseconds) for the USB OTG
+ * Session Request Protocol.
+ *
+ * For mc13783, this feature is not supported.So return PMIC_NOT_SUPPORTED
+ *
+ * @param handle device handle from open() call
+ * @param duration the data line pulse duration (ms)
+ *
+ * @return PMIC_SUCCESS if the pulse duration was successfully set
+ */
+PMIC_STATUS pmic_convity_usb_otg_set_dlp_duration(const PMIC_CONVITY_HANDLE
+ handle,
+ const unsigned int duration)
+{
+ PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+
+ /* The setting of the dlp duration is not supported by the mc13783 PMIC hardware. */
+
+ /* No critical section is required. */
+
+ if ((handle != usb.handle)
+ || (usb.handle_state != HANDLE_IN_USE)) {
+ /* Must return error indication for invalid handle parameter to be
+ * consistent with other APIs.
+ */
+ rc = PMIC_ERROR;
+ }
+
+ return rc;
+}
+
+/*!
+ * Get the current Data Line Pulse duration (in milliseconds) for the USB
+ * OTG Session Request Protocol.
+ *
+ * @param handle device handle from open() call
+ * @param duration the data line pulse duration (ms)
+ *
+ * @return PMIC_SUCCESS if the pulse duration was successfully obtained
+ */
+PMIC_STATUS pmic_convity_usb_otg_get_dlp_duration(const PMIC_CONVITY_HANDLE
+ handle,
+ unsigned int *const duration)
+{
+ PMIC_STATUS rc = PMIC_NOT_SUPPORTED;
+
+ /* The setting of dlp duration is not supported by the mc13783 PMIC hardware. */
+
+ /* No critical section is required. */
+
+ if ((handle != usb.handle)
+ || (usb.handle_state != HANDLE_IN_USE)) {
+ /* Must return error indication for invalid handle parameter to be
+ * consistent with other APIs.
+ */
+ rc = PMIC_ERROR;
+ }
+
+ return rc;
+}
+
+/*!
+ * Set the USB On-The-Go (OTG) configuration.
+ *
+ * @param handle device handle from open() call
+ * @param cfg desired USB OTG configuration
+ *
+ * @return PMIC_SUCCESS if the OTG configuration was successfully set
+ */
+PMIC_STATUS pmic_convity_usb_otg_set_config(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_OTG_CONFIG
+ cfg)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+ unsigned int reg_value = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+ if (cfg & USB_OTG_SE0CONN) {
+ reg_value = SET_BITS(regUSB0, SE0_CONN, 1);
+ reg_mask = SET_BITS(regUSB0, SE0_CONN, 1);
+ }
+ if (cfg & USBXCVREN) {
+ reg_value |= SET_BITS(regUSB0, USBXCVREN, 1);
+ reg_mask |= SET_BITS(regUSB0, USBXCVREN, 1);
+ }
+
+ if (cfg & USB_OTG_DLP_SRP) {
+ reg_value |= SET_BITS(regUSB0, DLP_SRP, 1);
+ reg_mask |= SET_BITS(regUSB0, DLP_SRP, 1);
+ }
+
+ if (cfg & USB_PULL_OVERRIDE) {
+ reg_value |= SET_BITS(regUSB0, PULLOVR, 1);
+ reg_mask |= SET_BITS(regUSB0, PULLOVR, 1);
+ }
+
+ if (cfg & USB_PU) {
+ reg_value |= SET_BITS(regUSB0, USB_PU, 1);
+ reg_mask |= SET_BITS(regUSB0, USB_PU, 1);
+ }
+
+ if (cfg & USB_UDM_PD) {
+ reg_value |= SET_BITS(regUSB0, UDM_PD, 1);
+ reg_mask |= SET_BITS(regUSB0, UDM_PD, 1);
+ }
+
+ if (cfg & USB_UDP_PD) {
+ reg_value |= SET_BITS(regUSB0, UDP_PD, 1);
+ reg_mask |= SET_BITS(regUSB0, UDP_PD, 1);
+ }
+
+ if (cfg & USB_DP150K_PU) {
+ reg_value |= SET_BITS(regUSB0, DP150K_PU, 1);
+ reg_mask |= SET_BITS(regUSB0, DP150K_PU, 1);
+ }
+
+ if (cfg & USB_USBCNTRL) {
+ reg_value |= SET_BITS(regUSB0, USBCNTRL, 1);
+ reg_mask |= SET_BITS(regUSB0, USBCNTRL, 1);
+ }
+
+ if (cfg & USB_VBUS_CURRENT_LIMIT_HIGH) {
+ reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 0);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) {
+ reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 1);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) {
+ reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 2);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) {
+ reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 3);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) {
+ reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 4);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) {
+ reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 5);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS) {
+ reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 6);
+ }
+ if (cfg & USB_VBUS_CURRENT_LIMIT_LOW) {
+ reg_value |= SET_BITS(regUSB0, CURRENT_LIMIT, 7);
+ reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 7);
+ }
+
+ if (cfg & USB_VBUS_PULLDOWN) {
+ reg_value |= SET_BITS(regUSB0, VBUSPDENB, 1);
+ reg_mask |= SET_BITS(regUSB0, VBUSPDENB, 1);
+ }
+
+ rc = pmic_write_reg(REG_USB, reg_value, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ if ((cfg & USB_VBUS_CURRENT_LIMIT_HIGH) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS)) {
+ /* Make sure that the VBUS current limit state is
+ * correctly set to either USB_VBUS_CURRENT_LIMIT_HIGH
+ * or USB_VBUS_CURRENT_LIMIT_LOW but never both at the
+ * same time.
+ *
+ * We guarantee this by first clearing both of the
+ * status bits and then resetting the correct one.
+ */
+ usb.usbOtgCfg &=
+ ~(USB_VBUS_CURRENT_LIMIT_HIGH |
+ USB_VBUS_CURRENT_LIMIT_LOW |
+ USB_VBUS_CURRENT_LIMIT_LOW_10MS |
+ USB_VBUS_CURRENT_LIMIT_LOW_20MS |
+ USB_VBUS_CURRENT_LIMIT_LOW_30MS |
+ USB_VBUS_CURRENT_LIMIT_LOW_40MS |
+ USB_VBUS_CURRENT_LIMIT_LOW_50MS |
+ USB_VBUS_CURRENT_LIMIT_LOW_60MS);
+ }
+
+ usb.usbOtgCfg |= cfg;
+ }
+ }
+ //}
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Clears the USB On-The-Go (OTG) configuration. Multiple configuration settings
+ * may be OR'd together in a single call. However, selecting conflicting
+ * settings (e.g., multiple VBUS current limits) will result in undefined
+ * behavior.
+ *
+ * @param handle Device handle from open() call.
+ * @param cfg USB OTG configuration settings to be cleared.
+ *
+ * @retval PMIC_SUCCESS If the OTG configuration was successfully
+ * cleared.
+ * @retval PMIC_PARAMETER_ERROR If the handle is invalid.
+ * @retval PMIC_NOT_SUPPORTED If the desired USB OTG configuration is
+ * not supported by the PMIC hardware.
+ */
+PMIC_STATUS pmic_convity_usb_otg_clear_config(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_USB_OTG_CONFIG
+ cfg)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+ unsigned int reg_value = 0;
+ unsigned int reg_mask = 0;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == usb.handle) && (usb.handle_state == HANDLE_IN_USE)) {
+ /* if ((cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) ||
+ (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS))
+ {
+ rc = PMIC_NOT_SUPPORTED;
+ } */
+ //else
+
+ if (cfg & USB_OTG_SE0CONN) {
+ reg_mask = SET_BITS(regUSB0, SE0_CONN, 1);
+ }
+
+ if (cfg & USB_OTG_DLP_SRP) {
+ reg_mask |= SET_BITS(regUSB0, DLP_SRP, 1);
+ }
+
+ if (cfg & USB_DP150K_PU) {
+ reg_mask |= SET_BITS(regUSB0, DP150K_PU, 1);
+ }
+
+ if (cfg & USB_PULL_OVERRIDE) {
+ reg_mask |= SET_BITS(regUSB0, PULLOVR, 1);
+ }
+
+ if (cfg & USB_PU) {
+
+ reg_mask |= SET_BITS(regUSB0, USB_PU, 1);
+ }
+
+ if (cfg & USB_UDM_PD) {
+
+ reg_mask |= SET_BITS(regUSB0, UDM_PD, 1);
+ }
+
+ if (cfg & USB_UDP_PD) {
+
+ reg_mask |= SET_BITS(regUSB0, UDP_PD, 1);
+ }
+
+ if (cfg & USB_USBCNTRL) {
+ reg_mask |= SET_BITS(regUSB0, USBCNTRL, 1);
+ }
+
+ if (cfg & USB_VBUS_CURRENT_LIMIT_HIGH) {
+ reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 0);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_10MS) {
+ reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 1);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_20MS) {
+ reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 2);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_30MS) {
+ reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 3);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_40MS) {
+ reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 4);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_50MS) {
+ reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 5);
+ } else if (cfg & USB_VBUS_CURRENT_LIMIT_LOW_60MS) {
+ reg_mask |= SET_BITS(regUSB0, CURRENT_LIMIT, 6);
+ }
+
+ if (cfg & USB_VBUS_PULLDOWN) {
+ // reg_value |= SET_BITS(regUSB0, VBUSPDENB, 1);
+ reg_mask |= SET_BITS(regUSB0, VBUSPDENB, 1);
+ }
+
+ rc = pmic_write_reg(REG_USB, reg_value, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ usb.usbOtgCfg &= ~cfg;
+ }
+ }
+ //}
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Get the current USB On-The-Go (OTG) configuration.
+ *
+ * @param handle device handle from open() call
+ * @param cfg the current USB OTG configuration
+ *
+ * @return PMIC_SUCCESS if the OTG configuration was successfully
+ * retrieved
+ */
+PMIC_STATUS pmic_convity_usb_otg_get_config(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_USB_OTG_CONFIG *
+ const cfg)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == usb.handle) &&
+ (usb.handle_state == HANDLE_IN_USE) &&
+ (cfg != (PMIC_CONVITY_USB_OTG_CONFIG *) NULL)) {
+ *cfg = usb.usbOtgCfg;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * RS-232-specific configuration and setup functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name RS-232 Serial Connectivity APIs
+ * Functions for controlling RS-232 serial connectivity.
+ */
+/*@{*/
+
+/*!
+ * Set the connectivity interface to the selected RS-232 operating
+ * configuration. Note that the RS-232 operating mode will be automatically
+ * overridden if the USB_EN is asserted at any time (e.g., when a USB device
+ * is attached). However, we will also automatically return to the RS-232
+ * mode once the USB device is detached.
+ *
+ * @param handle device handle from open() call
+ * @param cfgInternal RS-232 transceiver internal connections
+ * @param cfgExternal RS-232 transceiver external connections
+ *
+ * @return PMIC_SUCCESS if the requested mode was set
+ */
+PMIC_STATUS pmic_convity_rs232_set_config(const PMIC_CONVITY_HANDLE handle,
+ const PMIC_CONVITY_RS232_INTERNAL
+ cfgInternal,
+ const PMIC_CONVITY_RS232_EXTERNAL
+ cfgExternal)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+ unsigned int reg_value0 = 0, reg_value1 = 0;
+ unsigned int reg_mask = SET_BITS(regUSB1, RSPOL, 1);
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == rs_232.handle) && (rs_232.handle_state == HANDLE_IN_USE)) {
+ rc = PMIC_SUCCESS;
+
+ /* Validate the calling parameters. */
+ /*if ((cfgInternal != RS232_TX_USE0VM_RX_UDATVP) &&
+ (cfgInternal != RS232_TX_RX_INTERNAL_DEFAULT) && (cfgInternal != RS232_TX_UDATVP_RX_URXVM))
+ {
+
+ rc = PMIC_NOT_SUPPORTED;
+ } */
+ if (cfgInternal == RS232_TX_USE0VM_RX_UDATVP) {
+
+ reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 1);
+
+ } else if (cfgInternal == RS232_TX_RX_INTERNAL_DEFAULT) {
+
+ reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 1);
+ reg_mask |= SET_BITS(regUSB1, RSPOL, 1);
+
+ } else if (cfgInternal == RS232_TX_UDATVP_RX_URXVM) {
+
+ reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 2);
+ reg_value1 |= SET_BITS(regUSB1, RSPOL, 1);
+
+ } else if ((cfgExternal == RS232_TX_UDM_RX_UDP) ||
+ (cfgExternal == RS232_TX_RX_EXTERNAL_DEFAULT)) {
+ /* Configure for TX on D+ and RX on D-. */
+ reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 1);
+ reg_value1 |= SET_BITS(regUSB1, RSPOL, 0);
+ } else if (cfgExternal != RS232_TX_UDM_RX_UDP) {
+ /* Any other RS-232 configuration is an error. */
+ rc = PMIC_ERROR;
+ }
+
+ if (rc == PMIC_SUCCESS) {
+ /* Configure for TX on D- and RX on D+. */
+ rc = pmic_write_reg(REG_USB, reg_value0, reg_mask);
+
+ rc = pmic_write_reg(REG_CHARGE_USB_SPARE,
+ reg_value1, reg_mask);
+
+ if (rc == PMIC_SUCCESS) {
+ rs_232.rs232CfgInternal = cfgInternal;
+ rs_232.rs232CfgExternal = cfgExternal;
+ }
+ }
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*!
+ * Get the connectivity interface's current RS-232 operating configuration.
+ *
+ * @param handle device handle from open() call
+ * @param cfgInternal RS-232 transceiver internal connections
+ * @param cfgExternal RS-232 transceiver external connections
+ *
+ * @return PMIC_SUCCESS if the requested mode was retrieved
+ */
+PMIC_STATUS pmic_convity_rs232_get_config(const PMIC_CONVITY_HANDLE handle,
+ PMIC_CONVITY_RS232_INTERNAL *
+ const cfgInternal,
+ PMIC_CONVITY_RS232_EXTERNAL *
+ const cfgExternal)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle == rs_232.handle) &&
+ (rs_232.handle_state == HANDLE_IN_USE) &&
+ (cfgInternal != (PMIC_CONVITY_RS232_INTERNAL *) NULL) &&
+ (cfgExternal != (PMIC_CONVITY_RS232_EXTERNAL *) NULL)) {
+ *cfgInternal = rs_232.rs232CfgInternal;
+ *cfgExternal = rs_232.rs232CfgExternal;
+
+ rc = PMIC_SUCCESS;
+ }
+
+ /* Exit the critical section. */
+ up(&mutex);
+
+ return rc;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * CEA-936-specific configuration and setup functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name CEA-936 Connectivity APIs
+ * Functions for controlling CEA-936 connectivity.
+ */
+/*@{*/
+
+/*!
+ * Signal the attached device to exit the current CEA-936 operating mode.
+ * Returns an error if the current operating mode is not CEA-936.
+ *
+ * @param handle device handle from open() call
+ * @param signal type of exit signal to be sent
+ *
+ * @return PMIC_SUCCESS if exit signal was sent
+ */
+PMIC_STATUS pmic_convity_cea936_exit_signal(const PMIC_CONVITY_HANDLE handle,
+ const
+ PMIC_CONVITY_CEA936_EXIT_SIGNAL
+ signal)
+{
+ PMIC_STATUS rc = PMIC_ERROR;
+ unsigned int reg_value = 0;
+ unsigned int reg_mask =
+ SET_BITS(regUSB0, IDPD, 1) | SET_BITS(regUSB0, IDPULSE, 1);
+
+ /* Use a critical section to maintain a consistent state. */
+ if (down_interruptible(&mutex))
+ return PMIC_SYSTEM_ERROR_EINTR;
+
+ if ((handle != cea_936.handle)
+ || (cea_936.handle_state != HANDLE_IN_USE)) {
+ /* Must return error indication for invalid handle parameter to be
+ * consistent with other APIs.
+ */
+ rc = PMIC_ERROR;
+ } else if (signal == CEA936_UID_PULLDOWN_6MS) {
+ reg_value =
+ SET_BITS(regUSB0, IDPULSE, 0) | SET_BITS(regUSB0, IDPD, 0);
+ } else if (signal == CEA936_UID_PULLDOWN_6MS) {
+ reg_value = SET_BITS(regUSB0, IDPULSE, 1);
+ } else if (signal == CEA936_UID_PULLDOWN) {
+ reg_value = SET_BITS(regUSB0, IDPD, 1);
+ } else if (signal == CEA936_UDMPULSE) {
+ reg_value = SET_BITS(regUSB0, DMPULSE, 1);
+ }
+
+ rc = pmic_write_reg(REG_USB, reg_value, reg_mask);
+
+ up(&mutex);
+
+ return rc;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * Static functions.
+ **************************************************************************
+ */
+
+/*!
+ * @name Connectivity Driver Internal Support Functions
+ * These non-exported internal functions are used to support the functionality
+ * of the exported connectivity APIs.
+ */
+/*@{*/
+
+/*!
+ * This internal helper function sets the desired operating mode (either USB
+ * OTG or RS-232). It must be called with the mutex already acquired.
+ *
+ * @param mode the desired operating mode (USB or RS232)
+ *
+ * @return PMIC_SUCCESS if the desired operating mode was set
+ * @return PMIC_NOT_SUPPORTED if the desired operating mode is invalid
+ */
+static PMIC_STATUS pmic_convity_set_mode_internal(const PMIC_CONVITY_MODE mode)
+{
+ unsigned int reg_value0 = 0, reg_value1 = 0;
+ unsigned int reg_mask0 = 0, reg_mask1 = 0;
+
+ //unsigned int reg_mask1 = SET_BITS(regUSB1, VBUSEN, 1);
+
+ PMIC_STATUS rc = PMIC_SUCCESS;
+
+ switch (mode) {
+ case USB:
+ /* For the USB mode, we start by tri-stating the USB bus (by
+ * setting VBUSEN = 0) until a device is connected (i.e.,
+ * until we receive a 4.4V rising edge event). All pull-up
+ * and pull-down resistors are also disabled until a USB
+ * device is actually connected and we have determined which
+ * device is the host and the desired USB bus speed.
+ *
+ * Also tri-state the RS-232 buffers (by setting RSTRI = 1).
+ * This prevents the hardware from automatically returning to
+ * the RS-232 mode when the USB device is detached.
+ */
+
+ reg_value0 = SET_BITS(regUSB0, INTERFACE_MODE, 0);
+ reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);
+
+ /*reg_value1 = SET_BITS(regUSB1, RSTRI, 1); */
+
+ rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+ /* if (rc == PMIC_SUCCESS) {
+ CHECK_ERROR(pmic_write_reg
+ (REG_CHARGE_USB_SPARE,
+ reg_value1, reg_mask1));
+ } */
+
+ break;
+
+ case RS232_1:
+ /* For the RS-232 mode, we tri-state the USB bus (by setting
+ * VBUSEN = 0) and enable the RS-232 transceiver (by setting
+ * RS232ENB = 0).
+ *
+ * Note that even in the RS-232 mode, if a USB device is
+ * plugged in, we will receive a 4.4V rising edge event which
+ * will automatically disable the RS-232 transceiver and
+ * tri-state the RS-232 buffers. This allows us to temporarily
+ * switch over to USB mode while the USB device is attached.
+ * The RS-232 transceiver and buffers will be automatically
+ * re-enabled when the USB device is detached.
+ */
+
+ /* Explicitly disconnect all of the USB pull-down resistors
+ * and the VUSB power regulator here just to be safe.
+ *
+ * But we do connect the internal pull-up resistor on USB_D+
+ * to avoid having an extra load on the USB_D+ line when in
+ * RS-232 mode.
+ */
+
+ reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 1) |
+ SET_BITS(regUSB0, VBUSPDENB, 1) |
+ SET_BITS(regUSB0, USB_PU, 1);
+ reg_mask0 =
+ SET_BITS(regUSB0, INTERFACE_MODE, 7) | SET_BITS(regUSB0,
+ VBUSPDENB,
+ 1) |
+ SET_BITS(regUSB0, USB_PU, 1);
+
+ reg_value1 = SET_BITS(regUSB1, RSPOL, 0);
+
+ rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+
+ if (rc == PMIC_SUCCESS) {
+ CHECK_ERROR(pmic_write_reg
+ (REG_CHARGE_USB_SPARE,
+ reg_value1, reg_mask1));
+ }
+ break;
+
+ case RS232_2:
+ reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 2) |
+ SET_BITS(regUSB0, VBUSPDENB, 1) |
+ SET_BITS(regUSB0, USB_PU, 1);
+ reg_mask0 =
+ SET_BITS(regUSB0, INTERFACE_MODE, 7) | SET_BITS(regUSB0,
+ VBUSPDENB,
+ 1) |
+ SET_BITS(regUSB0, USB_PU, 1);
+
+ reg_value1 = SET_BITS(regUSB1, RSPOL, 1);
+
+ rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+
+ if (rc == PMIC_SUCCESS) {
+ CHECK_ERROR(pmic_write_reg
+ (REG_CHARGE_USB_SPARE,
+ reg_value1, reg_mask1));
+ }
+ break;
+
+ case CEA936_MONO:
+ reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 4);
+
+ rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+ break;
+
+ case CEA936_STEREO:
+ reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 5);
+ reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);
+
+ rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+ break;
+
+ case CEA936_TEST_RIGHT:
+ reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 6);
+ reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);
+
+ rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+ break;
+
+ case CEA936_TEST_LEFT:
+ reg_value0 |= SET_BITS(regUSB0, INTERFACE_MODE, 7);
+ reg_mask0 = SET_BITS(regUSB0, INTERFACE_MODE, 7);
+
+ rc = pmic_write_reg(REG_USB, reg_value0, reg_mask0);
+ break;
+
+ default:
+ rc = PMIC_NOT_SUPPORTED;
+ }
+
+ if (rc == PMIC_SUCCESS) {
+ if (mode == USB) {
+ usb.mode = mode;
+ } else if ((mode == RS232_1) || (mode == RS232_1)) {
+ rs_232.mode = mode;
+ } else if ((mode == CEA936_MONO) || (mode == CEA936_STEREO) ||
+ (mode == CEA936_TEST_RIGHT)
+ || (mode == CEA936_TEST_LEFT)) {
+ cea_936.mode = mode;
+ }
+ }
+
+ return rc;
+}
+
+/*!
+ * This internal helper function deregisters all of the currently registered
+ * callback events. It must be called with the mutual exclusion spinlock
+ * already acquired.
+ *
+ * We've defined the event and callback deregistration code here as a separate
+ * function because it can be called by either the pmic_convity_close() or the
+ * pmic_convity_clear_callback() APIs. We also wanted to avoid any possible
+ * issues with having the same thread calling spin_lock_irq() twice.
+ *
+ * Note that the mutex must have already been acquired. We will also acquire
+ * the spinlock here to avoid any possible race conditions with the interrupt
+ * handler.
+ *
+ * @return PMIC_SUCCESS if all of the callback events were cleared
+ */
+static PMIC_STATUS pmic_convity_deregister_all(void)
+{
+ unsigned long flags;
+ PMIC_STATUS rc = PMIC_SUCCESS;
+
+ /* Deregister each of the PMIC events that we had previously
+ * registered for by using pmic_event_subscribe().
+ */
+
+ if ((usb.eventMask & USB_DETECT_MINI_A) ||
+ (usb.eventMask & USB_DETECT_MINI_B) ||
+ (usb.eventMask & USB_DETECT_NON_USB_ACCESSORY) ||
+ (usb.eventMask & USB_DETECT_FACTORY_MODE)) {
+ //EVENT_AB_DETI or EVENT_IDI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_ABDET);
+
+ if (pmic_event_unsubscribe(EVENT_IDI, eventNotify) ==
+ PMIC_SUCCESS) {
+ /* Also acquire the spinlock here to avoid any possible race
+ * conditions with the interrupt handler.
+ */
+
+ spin_lock_irqsave(&lock, flags);
+
+ usb.eventMask &= ~(USB_DETECT_MINI_A |
+ USB_DETECT_MINI_B |
+ USB_DETECT_NON_USB_ACCESSORY |
+ USB_DETECT_FACTORY_MODE);
+
+ spin_unlock_irqrestore(&lock, flags);
+ } else {
+ pr_debug
+ ("%s: pmic_event_unsubscribe() for EVENT_AB_DETI failed\n",
+ __FILE__);
+ rc = PMIC_ERROR;
+ }
+ }
+
+ else if ((usb.eventMask & USB_DETECT_0V8_RISE) ||
+ (usb.eventMask & USB_DETECT_0V8_FALL)) {
+ //EVENT_USB_08VI or EVENT_USBI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_0V8);
+ if (pmic_event_unsubscribe(EVENT_USBI, eventNotify) ==
+ PMIC_SUCCESS) {
+ /* Also acquire the spinlock here to avoid any possible race
+ * conditions with the interrupt handler.
+ */
+ spin_lock_irqsave(&lock, flags);
+
+ usb.eventMask &= ~(USB_DETECT_0V8_RISE |
+ USB_DETECT_0V8_FALL);
+
+ spin_unlock_irqrestore(&lock, flags);
+ } else {
+ pr_debug
+ ("%s: pmic_event_unsubscribe() for EVENT_USB_08VI failed\n",
+ __FILE__);
+ rc = PMIC_ERROR;
+ }
+
+ }
+
+ else if ((usb.eventMask & USB_DETECT_2V0_RISE) ||
+ (usb.eventMask & USB_DETECT_2V0_FALL)) {
+ //EVENT_USB_20VI or EVENT_USBI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_2V0);
+ if (pmic_event_unsubscribe(EVENT_USBI, eventNotify) ==
+ PMIC_SUCCESS) {
+ /* Also acquire the spinlock here to avoid any possible race
+ * conditions with the interrupt handler.
+ */
+ spin_lock_irqsave(&lock, flags);
+
+ usb.eventMask &= ~(USB_DETECT_2V0_RISE |
+ USB_DETECT_2V0_FALL);
+
+ spin_unlock_irqrestore(&lock, flags);
+ } else {
+ pr_debug
+ ("%s: pmic_event_unsubscribe() for EVENT_USB_20VI failed\n",
+ __FILE__);
+ rc = PMIC_ERROR;
+ }
+ }
+
+ else if ((usb.eventMask & USB_DETECT_4V4_RISE) ||
+ (usb.eventMask & USB_DETECT_4V4_FALL)) {
+
+ //EVENT_USB_44VI or EVENT_USBI
+ eventNotify.func = pmic_convity_event_handler;
+ eventNotify.param = (void *)(CORE_EVENT_4V4);
+
+ if (pmic_event_unsubscribe(EVENT_USBI, eventNotify) ==
+ PMIC_SUCCESS) {
+
+ /* Also acquire the spinlock here to avoid any possible race
+ * conditions with the interrupt handler.
+ */
+ spin_lock_irqsave(&lock, flags);
+
+ usb.eventMask &= ~(USB_DETECT_4V4_RISE |
+ USB_DETECT_4V4_FALL);
+
+ spin_unlock_irqrestore(&lock, flags);
+ } else {
+ pr_debug
+ ("%s: pmic_event_unsubscribe() for EVENT_USB_44VI failed\n",
+ __FILE__);
+ rc = PMIC_ERROR;
+ }
+ }
+
+ if (rc == PMIC_SUCCESS) {
+ /* Also acquire the spinlock here to avoid any possible race
+ * conditions with the interrupt handler.
+ */
+ spin_lock_irqsave(&lock, flags);
+
+ /* Restore the initial reset values for the callback function
+ * and event mask parameters. This should be NULL and zero,
+ * respectively.
+ *
+ * Note that we wait until the end here to fully reset everything
+ * just in case some of the pmic_event_unsubscribe() calls above
+ * failed for some reason (which normally shouldn't happen).
+ */
+ usb.callback = reset.callback;
+ usb.eventMask = reset.eventMask;
+
+ spin_unlock_irqrestore(&lock, flags);
+ }
+ return rc;
+}
+
+/*!
+ * This is the default event handler for all connectivity-related events
+ * and hardware interrupts.
+ *
+ * @param param event ID
+ */
+static void pmic_convity_event_handler(void *param)
+{
+ unsigned long flags;
+
+ /* Update the global list of active interrupt events. */
+ spin_lock_irqsave(&lock, flags);
+ eventID |= (PMIC_CORE_EVENT) (param);
+ spin_unlock_irqrestore(&lock, flags);
+
+ /* Schedule the tasklet to be run as soon as it is convenient to do so. */
+ schedule_work(&convityTasklet);
+}
+
+/*!
+ * @brief This is the connectivity driver tasklet that handles interrupt events.
+ *
+ * This function is scheduled by the connectivity driver interrupt handler
+ * pmic_convity_event_handler() to complete the processing of all of the
+ * connectivity-related interrupt events.
+ *
+ * Since this tasklet runs with interrupts enabled, we can safely call
+ * the ADC driver, if necessary, to properly detect the type of USB connection
+ * that is being made and to call any user-registered callback functions.
+ *
+ * @param arg The parameter that was provided above in
+ * the DECLARE_TASKLET() macro (unused).
+ */
+static void pmic_convity_tasklet(struct work_struct *work)
+{
+
+ PMIC_CONVITY_EVENTS activeEvents = 0;
+ unsigned long flags = 0;
+
+ /* Check the interrupt sense bits to determine exactly what
+ * event just occurred.
+ */
+ if (eventID & CORE_EVENT_4V4) {
+ spin_lock_irqsave(&lock, flags);
+ eventID &= ~CORE_EVENT_4V4;
+ spin_unlock_irqrestore(&lock, flags);
+
+ activeEvents |= pmic_check_sensor(SENSE_USB4V4S) ?
+ USB_DETECT_4V4_RISE : USB_DETECT_4V4_FALL;
+
+ if (activeEvents & ~usb.eventMask) {
+ /* The default handler for 4.4 V rising/falling edge detection
+ * is to simply ignore the event.
+ */
+ ;
+ }
+ }
+ if (eventID & CORE_EVENT_2V0) {
+ spin_lock_irqsave(&lock, flags);
+ eventID &= ~CORE_EVENT_2V0;
+ spin_unlock_irqrestore(&lock, flags);
+
+ activeEvents |= pmic_check_sensor(SENSE_USB2V0S) ?
+ USB_DETECT_2V0_RISE : USB_DETECT_2V0_FALL;
+ if (activeEvents & ~usb.eventMask) {
+ /* The default handler for 2.0 V rising/falling edge detection
+ * is to simply ignore the event.
+ */
+ ;
+ }
+ }
+ if (eventID & CORE_EVENT_0V8) {
+ spin_lock_irqsave(&lock, flags);
+ eventID &= ~CORE_EVENT_0V8;
+ spin_unlock_irqrestore(&lock, flags);
+
+ activeEvents |= pmic_check_sensor(SENSE_USB0V8S) ?
+ USB_DETECT_0V8_RISE : USB_DETECT_0V8_FALL;
+
+ if (activeEvents & ~usb.eventMask) {
+ /* The default handler for 0.8 V rising/falling edge detection
+ * is to simply ignore the event.
+ */
+ ;
+ }
+ }
+ if (eventID & CORE_EVENT_ABDET) {
+ spin_lock_irqsave(&lock, flags);
+ eventID &= ~CORE_EVENT_ABDET;
+ spin_unlock_irqrestore(&lock, flags);
+
+ activeEvents |= pmic_check_sensor(SENSE_ID_GNDS) ?
+ USB_DETECT_MINI_A : 0;
+
+ activeEvents |= pmic_check_sensor(SENSE_ID_FLOATS) ?
+ USB_DETECT_MINI_B : 0;
+ }
+
+ /* Begin a critical section here so that we don't register/deregister
+ * for events or open/close the connectivity driver while the existing
+ * event handler (if it is currently defined) is in the middle of handling
+ * the current event.
+ */
+ spin_lock_irqsave(&lock, flags);
+
+ /* Finally, call the user-defined callback function if required. */
+ if ((usb.handle_state == HANDLE_IN_USE) &&
+ (usb.callback != NULL) && (activeEvents & usb.eventMask)) {
+ (*usb.callback) (activeEvents);
+ }
+
+ spin_unlock_irqrestore(&lock, flags);
+}
+
+/*@}*/
+
+/**************************************************************************
+ * Module initialization and termination functions.
+ *
+ * Note that if this code is compiled into the kernel, then the
+ * module_init() function will be called within the device_initcall()
+ * group.
+ **************************************************************************
+ */
+
+/*!
+ * @name Connectivity Driver Loading/Unloading Functions
+ * These non-exported internal functions are used to support the connectivity
+ * device driver initialization and de-initialization operations.
+ */
+/*@{*/
+
+/*!
+ * @brief This is the connectivity device driver initialization function.
+ *
+ * This function is called by the kernel when this device driver is first
+ * loaded.
+ */
+static int __init mc13783_pmic_convity_init(void)
+{
+ printk(KERN_INFO "PMIC Connectivity driver loading..\n");
+
+ return 0;
+}
+
+/*!
+ * @brief This is the Connectivity device driver de-initialization function.
+ *
+ * This function is called by the kernel when this device driver is about
+ * to be unloaded.
+ */
+static void __exit mc13783_pmic_convity_exit(void)
+{
+ printk(KERN_INFO "PMIC Connectivity driver unloading\n");
+
+ /* Close the device handle if it is still open. This will also
+ * deregister any callbacks that may still be active.
+ */
+ if (usb.handle_state == HANDLE_IN_USE) {
+ pmic_convity_close(usb.handle);
+ } else if (usb.handle_state == HANDLE_IN_USE) {
+ pmic_convity_close(rs_232.handle);
+ } else if (usb.handle_state == HANDLE_IN_USE) {
+ pmic_convity_close(cea_936.handle);
+ }
+
+ /* Reset the PMIC Connectivity register to it's power on state.
+ * We should do this when unloading the module so that we don't
+ * leave the hardware in a state which could cause problems when
+ * no device driver is loaded.
+ */
+ pmic_write_reg(REG_USB, RESET_USBCNTRL_REG_0, REG_FULLMASK);
+ pmic_write_reg(REG_CHARGE_USB_SPARE, RESET_USBCNTRL_REG_1,
+ REG_FULLMASK);
+ /* Note that there is no need to reset the "convity" device driver
+ * state structure to the reset state since we are in the final
+ * stage of unloading the device driver. The device driver state
+ * structure will be automatically and properly reinitialized if
+ * this device driver is reloaded.
+ */
+}
+
+/*@}*/
+
+/*
+ * Module entry points and description information.
+ */
+
+module_init(mc13783_pmic_convity_init);
+module_exit(mc13783_pmic_convity_exit);
+
+MODULE_DESCRIPTION("mc13783 Connectivity device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_light.c b/drivers/mxc/pmic/mc13783/pmic_light.c
new file mode 100644
index 000000000000..f6f5121d28c2
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_light.c
@@ -0,0 +1,2768 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_light.c
+ * @brief This is the main file of PMIC(mc13783) Light and Backlight driver.
+ *
+ * @ingroup PMIC_LIGHT
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/pmic_light.h>
+#include <linux/pmic_status.h>
+#include "pmic_light_defs.h"
+
+#define NB_LIGHT_REG 6
+
+static int pmic_light_major;
+
+/*!
+ * Number of users waiting in suspendq
+ */
+static int swait = 0;
+
+/*!
+ * To indicate whether any of the light devices are suspending
+ */
+static int suspend_flag = 0;
+
+/*!
+ * The suspendq is used to block application calls
+ */
+static wait_queue_head_t suspendq;
+
+static struct class *pmic_light_class;
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_bklit_tcled_master_enable);
+EXPORT_SYMBOL(pmic_bklit_tcled_master_disable);
+EXPORT_SYMBOL(pmic_bklit_master_enable);
+EXPORT_SYMBOL(pmic_bklit_master_disable);
+EXPORT_SYMBOL(pmic_bklit_set_current);
+EXPORT_SYMBOL(pmic_bklit_get_current);
+EXPORT_SYMBOL(pmic_bklit_set_dutycycle);
+EXPORT_SYMBOL(pmic_bklit_get_dutycycle);
+EXPORT_SYMBOL(pmic_bklit_set_cycle_time);
+EXPORT_SYMBOL(pmic_bklit_get_cycle_time);
+EXPORT_SYMBOL(pmic_bklit_set_mode);
+EXPORT_SYMBOL(pmic_bklit_get_mode);
+EXPORT_SYMBOL(pmic_bklit_rampup);
+EXPORT_SYMBOL(pmic_bklit_off_rampup);
+EXPORT_SYMBOL(pmic_bklit_rampdown);
+EXPORT_SYMBOL(pmic_bklit_off_rampdown);
+EXPORT_SYMBOL(pmic_bklit_enable_edge_slow);
+EXPORT_SYMBOL(pmic_bklit_disable_edge_slow);
+EXPORT_SYMBOL(pmic_bklit_get_edge_slow);
+EXPORT_SYMBOL(pmic_bklit_set_strobemode);
+EXPORT_SYMBOL(pmic_tcled_enable);
+EXPORT_SYMBOL(pmic_tcled_disable);
+EXPORT_SYMBOL(pmic_tcled_get_mode);
+EXPORT_SYMBOL(pmic_tcled_ind_set_current);
+EXPORT_SYMBOL(pmic_tcled_ind_get_current);
+EXPORT_SYMBOL(pmic_tcled_ind_set_blink_pattern);
+EXPORT_SYMBOL(pmic_tcled_ind_get_blink_pattern);
+EXPORT_SYMBOL(pmic_tcled_fun_set_current);
+EXPORT_SYMBOL(pmic_tcled_fun_get_current);
+EXPORT_SYMBOL(pmic_tcled_fun_set_cycletime);
+EXPORT_SYMBOL(pmic_tcled_fun_get_cycletime);
+EXPORT_SYMBOL(pmic_tcled_fun_set_dutycycle);
+EXPORT_SYMBOL(pmic_tcled_fun_get_dutycycle);
+EXPORT_SYMBOL(pmic_tcled_fun_blendedramps);
+EXPORT_SYMBOL(pmic_tcled_fun_sawramps);
+EXPORT_SYMBOL(pmic_tcled_fun_blendedbowtie);
+EXPORT_SYMBOL(pmic_tcled_fun_chasinglightspattern);
+EXPORT_SYMBOL(pmic_tcled_fun_strobe);
+EXPORT_SYMBOL(pmic_tcled_fun_rampup);
+EXPORT_SYMBOL(pmic_tcled_get_fun_rampup);
+EXPORT_SYMBOL(pmic_tcled_fun_rampdown);
+EXPORT_SYMBOL(pmic_tcled_get_fun_rampdown);
+EXPORT_SYMBOL(pmic_tcled_fun_triode_on);
+EXPORT_SYMBOL(pmic_tcled_fun_triode_off);
+EXPORT_SYMBOL(pmic_tcled_enable_edge_slow);
+EXPORT_SYMBOL(pmic_tcled_disable_edge_slow);
+EXPORT_SYMBOL(pmic_tcled_enable_half_current);
+EXPORT_SYMBOL(pmic_tcled_disable_half_current);
+EXPORT_SYMBOL(pmic_tcled_enable_audio_modulation);
+EXPORT_SYMBOL(pmic_tcled_disable_audio_modulation);
+EXPORT_SYMBOL(pmic_bklit_set_boost_mode);
+EXPORT_SYMBOL(pmic_bklit_get_boost_mode);
+EXPORT_SYMBOL(pmic_bklit_config_boost_mode);
+EXPORT_SYMBOL(pmic_bklit_gets_boost_mode);
+
+/*!
+ * This is the suspend of power management for the pmic light API.
+ * It suports SAVE and POWER_DOWN state.
+ *
+ * @param pdev the device
+ * @param state the state
+ *
+ * @return This function returns 0 if successful.
+ */
+static int pmic_light_suspend(struct platform_device *dev, pm_message_t state)
+{
+ suspend_flag = 1;
+ /* switch off all leds and backlights */
+ CHECK_ERROR(pmic_light_init_reg());
+
+ return 0;
+};
+
+/*!
+ * This is the resume of power management for the pmic light API.
+ * It suports RESTORE state.
+ *
+ * @param dev the device
+ *
+ * @return This function returns 0 if successful.
+ */
+static int pmic_light_resume(struct platform_device *pdev)
+{
+ suspend_flag = 0;
+ while (swait > 0) {
+ swait--;
+ wake_up_interruptible(&suspendq);
+ }
+
+ return 0;
+};
+
+/*!
+ * This function enables backlight & tcled.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_tcled_master_enable(void)
+{
+ unsigned int reg_value = 0;
+ unsigned int mask = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ reg_value = BITFVAL(BIT_LEDEN, 1);
+ mask = BITFMASK(BIT_LEDEN);
+ CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables backlight & tcled.
+ *
+ * @return This function returns PMIC_SUCCESS if successful
+ */
+PMIC_STATUS pmic_bklit_tcled_master_disable(void)
+{
+ unsigned int reg_value = 0;
+ unsigned int mask = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ reg_value = BITFVAL(BIT_LEDEN, 0);
+ mask = BITFMASK(BIT_LEDEN);
+ CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables backlight. Not supported on mc13783
+ * Use pmic_bklit_tcled_master_enable.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED
+ */
+PMIC_STATUS pmic_bklit_master_enable(void)
+{
+ return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function disables backlight. Not supported on mc13783
+ * Use pmic_bklit_tcled_master_enable.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED
+ */
+PMIC_STATUS pmic_bklit_master_disable(void)
+{
+ return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function sets backlight current level.
+ *
+ * @param channel Backlight channel
+ * @param level Backlight current level, as the following table.
+ * @verbatim
+ level main & aux keyboard
+ ------ ----------- --------
+ 0 0 mA 0 mA
+ 1 3 mA 12 mA
+ 2 6 mA 24 mA
+ 3 9 mA 36 mA
+ 4 12 mA 48 mA
+ 5 15 mA 60 mA
+ 6 18 mA 72 mA
+ 7 21 mA 84 mA
+ @endverbatim
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_current(t_bklit_channel channel, unsigned char level)
+{
+ unsigned int mask;
+ unsigned int value;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ value = BITFVAL(BIT_CL_MAIN, level);
+ mask = BITFMASK(BIT_CL_MAIN);
+ break;
+ case BACKLIGHT_LED2:
+ value = BITFVAL(BIT_CL_AUX, level);
+ mask = BITFMASK(BIT_CL_AUX);
+ break;
+ case BACKLIGHT_LED3:
+ value = BITFVAL(BIT_CL_KEY, level);
+ mask = BITFMASK(BIT_CL_KEY);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ CHECK_ERROR(pmic_write_reg(LREG_2, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives backlight current level.
+ * The channels are not individually adjustable, hence
+ * the channel parameter is ignored.
+ *
+ * @param channel Backlight channel (Ignored because the
+ * channels are not individually adjustable)
+ * @param level Pointer to store backlight current level result.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_current(t_bklit_channel channel,
+ unsigned char *level)
+{
+ unsigned int reg_value = 0;
+ unsigned int mask = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ mask = BITFMASK(BIT_CL_MAIN);
+ break;
+ case BACKLIGHT_LED2:
+ mask = BITFMASK(BIT_CL_AUX);
+ break;
+ case BACKLIGHT_LED3:
+ mask = BITFMASK(BIT_CL_KEY);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(LREG_2, &reg_value, mask));
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ *level = BITFEXT(reg_value, BIT_CL_MAIN);
+ break;
+ case BACKLIGHT_LED2:
+ *level = BITFEXT(reg_value, BIT_CL_AUX);
+ break;
+ case BACKLIGHT_LED3:
+ *level = BITFEXT(reg_value, BIT_CL_KEY);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a backlight channel duty cycle.
+ * LED perceived brightness for each zone may be individually set by setting
+ * duty cycle. The default setting is for 0% duty cycle; this keeps all zone
+ * drivers turned off even after the master enable command. Each LED current
+ * sink can be turned on and adjusted for brightness with an independent 4 bit
+ * word for a duty cycle ranging from 0% to 100% in approximately 6.7% steps.
+ *
+ * @param channel Backlight channel.
+ * @param dc Backlight duty cycle, as the following table.
+ * @verbatim
+ dc Duty Cycle (% On-time over Cycle Time)
+ ------ ---------------------------------------
+ 0 0%
+ 1 6.7%
+ 2 13.3%
+ 3 20%
+ 4 26.7%
+ 5 33.3%
+ 6 40%
+ 7 46.7%
+ 8 53.3%
+ 9 60%
+ 10 66.7%
+ 11 73.3%
+ 12 80%
+ 13 86.7%
+ 14 93.3%
+ 15 100%
+ @endverbatim
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_dutycycle(t_bklit_channel channel, unsigned char dc)
+{
+ unsigned int reg_value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ if (dc > 15) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(LREG_2, &reg_value, PMIC_ALL_BITS));
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ reg_value = reg_value & (~MASK_DUTY_CYCLE);
+ reg_value = reg_value | (dc << BIT_DUTY_CYCLE);
+ break;
+ case BACKLIGHT_LED2:
+ reg_value = reg_value & (~(MASK_DUTY_CYCLE << INDEX_AUX));
+ reg_value = reg_value | (dc << (BIT_DUTY_CYCLE + INDEX_AUX));
+ break;
+ case BACKLIGHT_LED3:
+ reg_value = reg_value & (~(MASK_DUTY_CYCLE << INDEX_KYD));
+ reg_value = reg_value | (dc << (BIT_DUTY_CYCLE + INDEX_KYD));
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ CHECK_ERROR(pmic_write_reg(LREG_2, reg_value, PMIC_ALL_BITS));
+ return PMIC_SUCCESS;
+
+}
+
+/*!
+ * This function retrives a backlight channel duty cycle.
+ *
+ * @param channel Backlight channel.
+ * @param dc Pointer to backlight duty cycle.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_dutycycle(t_bklit_channel channel, unsigned char *dc)
+{
+ unsigned int reg_value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ CHECK_ERROR(pmic_read_reg(LREG_2, &reg_value, PMIC_ALL_BITS));
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ *dc = (int)((reg_value & (MASK_DUTY_CYCLE))
+ >> BIT_DUTY_CYCLE);
+
+ break;
+ case BACKLIGHT_LED2:
+ *dc = (int)((reg_value & (MASK_DUTY_CYCLE << INDEX_AUX))
+ >> (BIT_DUTY_CYCLE + INDEX_AUX));
+ break;
+ case BACKLIGHT_LED3:
+ *dc = (int)((reg_value & (MASK_DUTY_CYCLE <<
+ INDEX_KYD)) >> (BIT_DUTY_CYCLE +
+ INDEX_KYD));
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a backlight channel cycle time.
+ * Cycle Time is defined as the period of a complete cycle of
+ * Time_on + Time_off. The default Cycle Time is set to 0.01 seconds such that
+ * the 100 Hz on-off cycling is averaged out by the eye to eliminate
+ * flickering. Additionally, the Cycle Time can be programmed to intentionally
+ * extend the period of on-off cycles for a visual pulsating or blinking effect.
+ *
+ * @param period Backlight cycle time, as the following table.
+ * @verbatim
+ period Cycle Time
+ -------- ------------
+ 0 0.01 seconds
+ 1 0.1 seconds
+ 2 0.5 seconds
+ 3 2 seconds
+ @endverbatim
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_cycle_time(unsigned char period)
+{
+ unsigned int mask;
+ unsigned int value;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ if (period > 3) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ mask = BITFMASK(BIT_PERIOD);
+ value = BITFVAL(BIT_PERIOD, period);
+ CHECK_ERROR(pmic_write_reg(LREG_2, value, mask));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives a backlight channel cycle time setting.
+ *
+ * @param period Pointer to save backlight cycle time setting result.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_cycle_time(unsigned char *period)
+{
+ unsigned int mask;
+ unsigned int value;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ mask = BITFMASK(BIT_PERIOD);
+ CHECK_ERROR(pmic_read_reg(LREG_2, &value, mask));
+ *period = BITFEXT(value, BIT_PERIOD);
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets backlight operation mode. There are two modes of
+ * operations: current control and triode mode.
+ * The Duty Cycle/Cycle Time control is retained in Triode Mode. Audio
+ * coupling is not available in Triode Mode.
+ *
+ * @param channel Backlight channel.
+ * @param mode Backlight operation mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_mode(t_bklit_channel channel, t_bklit_mode mode)
+{
+ unsigned int reg_value = 0;
+ unsigned int clear_val = 0;
+ unsigned int triode_val = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ CHECK_ERROR(pmic_read_reg(LREG_0, &reg_value, PMIC_ALL_BITS));
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ clear_val = ~(MASK_TRIODE_MAIN_BL);
+ triode_val = MASK_TRIODE_MAIN_BL;
+ break;
+ case BACKLIGHT_LED2:
+ clear_val = ~(MASK_TRIODE_MAIN_BL << INDEX_AUXILIARY);
+ triode_val = (MASK_TRIODE_MAIN_BL << INDEX_AUXILIARY);
+ break;
+ case BACKLIGHT_LED3:
+ clear_val = ~(MASK_TRIODE_MAIN_BL << INDEX_KEYPAD);
+ triode_val = (MASK_TRIODE_MAIN_BL << INDEX_KEYPAD);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ reg_value = (reg_value & clear_val);
+
+ if (mode == BACKLIGHT_TRIODE_MODE) {
+ reg_value = (reg_value | triode_val);
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, PMIC_ALL_BITS));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets backlight operation mode. There are two modes of
+ * operations: current control and triode mode.
+ * The Duty Cycle/Cycle Time control is retained in Triode Mode. Audio
+ * coupling is not available in Triode Mode.
+ *
+ * @param channel Backlight channel.
+ * @param mode Backlight operation mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_mode(t_bklit_channel channel, t_bklit_mode * mode)
+{
+ unsigned int reg_value = 0;
+ unsigned int mask = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ mask = BITFMASK(BIT_TRIODE_MAIN_BL);
+ break;
+ case BACKLIGHT_LED2:
+ mask = BITFMASK(BIT_TRIODE_AUX_BL);
+ break;
+ case BACKLIGHT_LED3:
+ mask = BITFMASK(BIT_TRIODE_KEY_BL);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(LREG_0, &reg_value, mask));
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ *mode = BITFEXT(reg_value, BIT_TRIODE_MAIN_BL);
+ break;
+ case BACKLIGHT_LED2:
+ *mode = BITFEXT(reg_value, BIT_TRIODE_AUX_BL);
+ break;
+ case BACKLIGHT_LED3:
+ *mode = BITFEXT(reg_value, BIT_TRIODE_KEY_BL);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function starts backlight brightness ramp up function; ramp time is
+ * fixed at 0.5 seconds.
+ *
+ * @param channel Backlight channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_rampup(t_bklit_channel channel)
+{
+ unsigned int reg_value = 0;
+ unsigned int mask = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ mask = BITFMASK(BIT_UP_MAIN_BL);
+ reg_value = BITFVAL(BIT_UP_MAIN_BL, 1);
+ break;
+ case BACKLIGHT_LED2:
+ mask = BITFMASK(BIT_UP_AUX_BL);
+ reg_value = BITFVAL(BIT_UP_AUX_BL, 1);
+ break;
+ case BACKLIGHT_LED3:
+ mask = BITFMASK(BIT_UP_KEY_BL);
+ reg_value = BITFVAL(BIT_UP_KEY_BL, 1);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function stops backlight brightness ramp up function;
+ *
+ * @param channel Backlight channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_off_rampup(t_bklit_channel channel)
+{
+ unsigned int reg_value = 0;
+ unsigned int mask = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ mask = BITFMASK(BIT_UP_MAIN_BL);
+ break;
+ case BACKLIGHT_LED2:
+ mask = BITFMASK(BIT_UP_AUX_BL);
+ break;
+ case BACKLIGHT_LED3:
+ mask = BITFMASK(BIT_UP_KEY_BL);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function starts backlight brightness ramp down function; ramp time is
+ * fixed at 0.5 seconds.
+ *
+ * @param channel Backlight channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_rampdown(t_bklit_channel channel)
+{
+ unsigned int reg_value = 0;
+ unsigned int mask = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ mask = BITFMASK(BIT_DOWN_MAIN_BL);
+ reg_value = BITFVAL(BIT_DOWN_MAIN_BL, 1);
+ break;
+ case BACKLIGHT_LED2:
+ mask = BITFMASK(BIT_DOWN_AUX_BL);
+ reg_value = BITFVAL(BIT_DOWN_AUX_BL, 1);
+ break;
+ case BACKLIGHT_LED3:
+ mask = BITFMASK(BIT_DOWN_KEY_BL);
+ reg_value = BITFVAL(BIT_DOWN_KEY_BL, 1);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function stops backlight brightness ramp down function.
+ *
+ * @param channel Backlight channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_off_rampdown(t_bklit_channel channel)
+{
+ unsigned int reg_value = 0;
+ unsigned int mask = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (channel) {
+ case BACKLIGHT_LED1:
+ mask = BITFMASK(BIT_DOWN_MAIN_BL);
+ break;
+ case BACKLIGHT_LED2:
+ mask = BITFMASK(BIT_DOWN_AUX_BL);
+ break;
+ case BACKLIGHT_LED3:
+ mask = BITFMASK(BIT_DOWN_KEY_BL);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_0, reg_value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables backlight analog edge slowing mode. Analog Edge
+ * Slowing slows down the transient edges to reduce the chance of coupling LED
+ * modulation activity into other circuits. Rise and fall times will be targeted
+ * for approximately 50usec.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_enable_edge_slow(void)
+{
+ unsigned int mask;
+ unsigned int value;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ mask = BITFMASK(BIT_SLEWLIMBL);
+ value = BITFVAL(BIT_SLEWLIMBL, 1);
+ CHECK_ERROR(pmic_write_reg(LREG_2, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables backlight analog edge slowing mode. The backlight
+ * drivers will default to an <93>Instant On<94> mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_disable_edge_slow(void)
+{
+ unsigned int mask;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ mask = BITFMASK(BIT_SLEWLIMBL);
+ CHECK_ERROR(pmic_write_reg(LREG_2, 0, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets backlight analog edge slowing mode. DThe backlight
+ *
+ * @param edge Edge slowing mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_edge_slow(bool * edge)
+{
+ unsigned int mask;
+ unsigned int value;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ mask = BITFMASK(BIT_SLEWLIMBL);
+ CHECK_ERROR(pmic_read_reg(LREG_2, &value, mask));
+ *edge = (bool) BITFEXT(value, BIT_SLEWLIMBL);
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets backlight Strobe Light Pulsing mode.
+ *
+ * @param channel Backlight channel.
+ * @param mode Strobe Light Pulsing mode.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_bklit_set_strobemode(t_bklit_channel channel,
+ t_bklit_strobe_mode mode)
+{
+ return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function enables tri-color LED.
+ *
+ * @param mode Tri-color LED operation mode.
+ * @param bank Selected tri-color bank
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_enable(t_tcled_mode mode, t_funlight_bank bank)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (mode) {
+ case TCLED_FUN_MODE:
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ mask = MASK_BK1_FL;
+ value = MASK_BK1_FL;
+ break;
+ case TCLED_FUN_BANK2:
+ mask = MASK_BK2_FL;
+ value = MASK_BK2_FL;
+ break;
+ case TCLED_FUN_BANK3:
+ mask = MASK_BK3_FL;
+ value = MASK_BK3_FL;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ break;
+ case TCLED_IND_MODE:
+ mask = MASK_BK1_FL | MASK_BK2_FL | MASK_BK3_FL;
+ break;
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables tri-color LED.
+ *
+ * @param bank Selected tri-color bank
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ *
+ */
+PMIC_STATUS pmic_tcled_disable(t_funlight_bank bank)
+{
+ unsigned int mask = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ mask = MASK_BK1_FL;
+ break;
+ case TCLED_FUN_BANK2:
+ mask = MASK_BK2_FL;
+ break;
+ case TCLED_FUN_BANK3:
+ mask = MASK_BK3_FL;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_0, 0, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives tri-color LED operation mode.
+ *
+ * @param mode Pointer to Tri-color LED operation mode.
+ * @param bank Selected tri-color bank
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_get_mode(t_tcled_mode * mode, t_funlight_bank bank)
+{
+ unsigned int val;
+ unsigned int mask;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ mask = MASK_BK1_FL;
+ break;
+ case TCLED_FUN_BANK2:
+ mask = MASK_BK2_FL;
+ break;
+ case TCLED_FUN_BANK3:
+ mask = MASK_BK3_FL;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(LREG_0, &val, mask));
+
+ if (val) {
+ *mode = TCLED_FUN_MODE;
+ } else {
+ *mode = TCLED_IND_MODE;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a tri-color LED channel current level in indicator mode.
+ *
+ * @param channel Tri-color LED channel.
+ * @param level Current level.
+ * @param bank Selected tri-color bank
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_set_current(t_ind_channel channel,
+ t_tcled_cur_level level,
+ t_funlight_bank bank)
+{
+ unsigned int reg_conf = 0;
+ unsigned int mask;
+ unsigned int value;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ if (level > TCLED_CUR_LEVEL_4) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ reg_conf = LREG_3;
+ break;
+ case TCLED_FUN_BANK2:
+ reg_conf = LREG_4;
+ break;
+ case TCLED_FUN_BANK3:
+ reg_conf = LREG_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_IND_RED:
+ value = BITFVAL(BITS_CL_RED, level);
+ mask = BITFMASK(BITS_CL_RED);
+ break;
+ case TCLED_IND_GREEN:
+ value = BITFVAL(BITS_CL_GREEN, level);
+ mask = BITFMASK(BITS_CL_GREEN);
+ break;
+ case TCLED_IND_BLUE:
+ value = BITFVAL(BITS_CL_BLUE, level);
+ mask = BITFMASK(BITS_CL_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg_conf, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives a tri-color LED channel current level
+ * in indicator mode.
+ *
+ * @param channel Tri-color LED channel.
+ * @param level Pointer to current level.
+ * @param bank Selected tri-color bank
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_get_current(t_ind_channel channel,
+ t_tcled_cur_level * level,
+ t_funlight_bank bank)
+{
+ unsigned int reg_conf = 0;
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ reg_conf = LREG_3;
+ break;
+ case TCLED_FUN_BANK2:
+ reg_conf = LREG_4;
+ break;
+ case TCLED_FUN_BANK3:
+ reg_conf = LREG_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_IND_RED:
+ mask = BITFMASK(BITS_CL_RED);
+ break;
+ case TCLED_IND_GREEN:
+ mask = BITFMASK(BITS_CL_GREEN);
+ break;
+ case TCLED_IND_BLUE:
+ mask = BITFMASK(BITS_CL_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg_conf, &value, mask));
+
+ switch (channel) {
+ case TCLED_IND_RED:
+ *level = BITFEXT(value, BITS_CL_RED);
+ break;
+ case TCLED_IND_GREEN:
+ *level = BITFEXT(value, BITS_CL_GREEN);
+ break;
+ case TCLED_IND_BLUE:
+ *level = BITFEXT(value, BITS_CL_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a tri-color LED channel blinking pattern in indication
+ * mode.
+ *
+ * @param channel Tri-color LED channel.
+ * @param pattern Blinking pattern.
+ * @param skip If true, skip a cycle after each cycle.
+ * @param bank Selected tri-color bank
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_set_blink_pattern(t_ind_channel channel,
+ t_tcled_ind_blink_pattern pattern,
+ bool skip, t_funlight_bank bank)
+{
+ unsigned int reg_conf = 0;
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ if (skip == true) {
+ return PMIC_NOT_SUPPORTED;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ reg_conf = LREG_3;
+ break;
+ case TCLED_FUN_BANK2:
+ reg_conf = LREG_4;
+ break;
+ case TCLED_FUN_BANK3:
+ reg_conf = LREG_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_IND_RED:
+ value = BITFVAL(BITS_DC_RED, pattern);
+ mask = BITFMASK(BITS_DC_RED);
+ break;
+ case TCLED_IND_GREEN:
+ value = BITFVAL(BITS_DC_GREEN, pattern);
+ mask = BITFMASK(BITS_DC_GREEN);
+ break;
+ case TCLED_IND_BLUE:
+ value = BITFVAL(BITS_DC_BLUE, pattern);
+ mask = BITFMASK(BITS_DC_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg_conf, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives a tri-color LED channel blinking pattern in
+ * indication mode.
+ *
+ * @param channel Tri-color LED channel.
+ * @param pattern Pointer to Blinking pattern.
+ * @param skip Pointer to a boolean varible indicating if skip
+ * @param bank Selected tri-color bank
+ * a cycle after each cycle.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_get_blink_pattern(t_ind_channel channel,
+ t_tcled_ind_blink_pattern *
+ pattern, bool * skip,
+ t_funlight_bank bank)
+{
+ unsigned int reg_conf = 0;
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ reg_conf = LREG_3;
+ break;
+ case TCLED_FUN_BANK2:
+ reg_conf = LREG_4;
+ break;
+ case TCLED_FUN_BANK3:
+ reg_conf = LREG_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_IND_RED:
+ mask = BITFMASK(BITS_DC_RED);
+ break;
+ case TCLED_IND_GREEN:
+ mask = BITFMASK(BITS_DC_GREEN);
+ break;
+ case TCLED_IND_BLUE:
+ mask = BITFMASK(BITS_DC_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg_conf, &value, mask));
+
+ switch (channel) {
+ case TCLED_IND_RED:
+ *pattern = BITFEXT(value, BITS_DC_RED);
+ break;
+ case TCLED_IND_GREEN:
+ *pattern = BITFEXT(value, BITS_DC_GREEN);
+ break;
+ case TCLED_IND_BLUE:
+ *pattern = BITFEXT(value, BITS_DC_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a tri-color LED channel current level in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param level Current level.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_set_current(t_funlight_bank bank,
+ t_funlight_channel channel,
+ t_tcled_cur_level level)
+{
+ unsigned int reg_conf = 0;
+ unsigned int mask;
+ unsigned int value;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ if (level > TCLED_CUR_LEVEL_4) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ reg_conf = LREG_3;
+ break;
+ case TCLED_FUN_BANK2:
+ reg_conf = LREG_4;
+ break;
+ case TCLED_FUN_BANK3:
+ reg_conf = LREG_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_FUN_CHANNEL1:
+ value = BITFVAL(BITS_CL_RED, level);
+ mask = BITFMASK(BITS_CL_RED);
+ break;
+ case TCLED_FUN_CHANNEL2:
+ value = BITFVAL(BITS_CL_GREEN, level);
+ mask = BITFMASK(BITS_CL_GREEN);
+ break;
+ case TCLED_FUN_CHANNEL3:
+ value = BITFVAL(BITS_CL_BLUE, level);
+ mask = BITFMASK(BITS_CL_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg_conf, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives a tri-color LED channel current level
+ * in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param level Pointer to current level.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_get_current(t_funlight_bank bank,
+ t_funlight_channel channel,
+ t_tcled_cur_level * level)
+{
+ unsigned int reg_conf = 0;
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ reg_conf = LREG_3;
+ break;
+ case TCLED_FUN_BANK2:
+ reg_conf = LREG_4;
+ break;
+ case TCLED_FUN_BANK3:
+ reg_conf = LREG_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_FUN_CHANNEL1:
+ mask = BITFMASK(BITS_CL_RED);
+ break;
+ case TCLED_FUN_CHANNEL2:
+ mask = BITFMASK(BITS_CL_GREEN);
+ break;
+ case TCLED_FUN_CHANNEL3:
+ mask = BITFMASK(BITS_CL_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg_conf, &value, mask));
+
+ switch (channel) {
+ case TCLED_FUN_CHANNEL1:
+ *level = BITFEXT(value, BITS_CL_RED);
+ break;
+ case TCLED_FUN_CHANNEL2:
+ *level = BITFEXT(value, BITS_CL_GREEN);
+ break;
+ case TCLED_FUN_CHANNEL3:
+ *level = BITFEXT(value, BITS_CL_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets tri-color LED cycle time.
+ *
+ * @param bank Tri-color LED bank
+ * @param ct Cycle time.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_set_cycletime(t_funlight_bank bank,
+ t_tcled_fun_cycle_time ct)
+{
+ unsigned int reg_conf = 0;
+ unsigned int mask;
+ unsigned int value;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ if (ct > TC_CYCLE_TIME_4) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ reg_conf = LREG_3;
+ break;
+ case TCLED_FUN_BANK2:
+ reg_conf = LREG_4;
+ break;
+ case TCLED_FUN_BANK3:
+ reg_conf = LREG_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ value = BITFVAL(BIT_PERIOD, ct);
+ mask = BITFMASK(BIT_PERIOD);
+
+ CHECK_ERROR(pmic_write_reg(reg_conf, value, mask));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives tri-color LED cycle time in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param ct Pointer to cycle time.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_get_cycletime(t_funlight_bank bank,
+ t_tcled_fun_cycle_time * ct)
+{
+ unsigned int reg_conf = 0;
+ unsigned int mask;
+ unsigned int value;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ if (*ct > TC_CYCLE_TIME_4) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ reg_conf = LREG_3;
+ break;
+ case TCLED_FUN_BANK2:
+ reg_conf = LREG_4;
+ break;
+ case TCLED_FUN_BANK3:
+ reg_conf = LREG_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ mask = BITFMASK(BIT_PERIOD);
+ CHECK_ERROR(pmic_read_reg(reg_conf, &value, mask));
+
+ *ct = BITFVAL(BIT_PERIOD, value);
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets a tri-color LED channel duty cycle in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param dc Duty cycle.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_set_dutycycle(t_funlight_bank bank,
+ t_funlight_channel channel,
+ unsigned char dc)
+{
+ unsigned int reg_conf = 0;
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ reg_conf = LREG_3;
+ break;
+ case TCLED_FUN_BANK2:
+ reg_conf = LREG_4;
+ break;
+ case TCLED_FUN_BANK3:
+ reg_conf = LREG_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_FUN_CHANNEL1:
+ value = BITFVAL(BITS_DC_RED, dc);
+ mask = BITFMASK(BITS_DC_RED);
+ break;
+ case TCLED_FUN_CHANNEL2:
+ value = BITFVAL(BITS_DC_GREEN, dc);
+ mask = BITFMASK(BITS_DC_GREEN);
+ break;
+ case TCLED_FUN_CHANNEL3:
+ value = BITFVAL(BITS_DC_BLUE, dc);
+ mask = BITFMASK(BITS_DC_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg_conf, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives a tri-color LED channel duty cycle in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param dc Pointer to duty cycle.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_get_dutycycle(t_funlight_bank bank,
+ t_funlight_channel channel,
+ unsigned char *dc)
+{
+ unsigned int reg_conf = 0;
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ reg_conf = LREG_3;
+ break;
+ case TCLED_FUN_BANK2:
+ reg_conf = LREG_4;
+ break;
+ case TCLED_FUN_BANK3:
+ reg_conf = LREG_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_FUN_CHANNEL1:
+ mask = BITFMASK(BITS_DC_RED);
+ break;
+ case TCLED_FUN_CHANNEL2:
+ mask = BITFMASK(BITS_DC_GREEN);
+ break;
+ case TCLED_FUN_CHANNEL3:
+ mask = BITFMASK(BITS_DC_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg_conf, &value, mask));
+
+ switch (channel) {
+ case TCLED_FUN_CHANNEL1:
+ *dc = BITFEXT(value, BITS_DC_RED);
+ break;
+ case TCLED_FUN_CHANNEL2:
+ *dc = BITFEXT(value, BITS_DC_GREEN);
+ break;
+ case TCLED_FUN_CHANNEL3:
+ *dc = BITFEXT(value, BITS_DC_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Blended Ramp fun light pattern.
+ *
+ * @param bank Tri-color LED bank
+ * @param speed Speed of pattern.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_blendedramps(t_funlight_bank bank,
+ t_tcled_fun_speed speed)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (speed) {
+ case TC_OFF:
+ value = BITFVAL(BITS_FUN_LIGHT, FUN_LIGHTS_OFF);
+ break;
+ case TC_SLOW:
+ value = BITFVAL(BITS_FUN_LIGHT, BLENDED_RAMPS_SLOW);
+ break;
+ case TC_FAST:
+ value = BITFVAL(BITS_FUN_LIGHT, BLENDED_RAMPS_FAST);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ mask = BITFMASK(BITS_FUN_LIGHT);
+ CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Saw Ramp fun light pattern.
+ *
+ * @param bank Tri-color LED bank
+ * @param speed Speed of pattern.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_sawramps(t_funlight_bank bank,
+ t_tcled_fun_speed speed)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (speed) {
+ case TC_OFF:
+ value = BITFVAL(BITS_FUN_LIGHT, FUN_LIGHTS_OFF);
+ break;
+ case TC_SLOW:
+ value = BITFVAL(BITS_FUN_LIGHT, SAW_RAMPS_SLOW);
+ break;
+ case TC_FAST:
+ value = BITFVAL(BITS_FUN_LIGHT, SAW_RAMPS_FAST);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ mask = BITFMASK(BITS_FUN_LIGHT);
+ CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Blended Bowtie fun light pattern.
+ *
+ * @param bank Tri-color LED bank
+ * @param speed Speed of pattern.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_blendedbowtie(t_funlight_bank bank,
+ t_tcled_fun_speed speed)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (speed) {
+ case TC_OFF:
+ value = BITFVAL(BITS_FUN_LIGHT, FUN_LIGHTS_OFF);
+ break;
+ case TC_SLOW:
+ value = BITFVAL(BITS_FUN_LIGHT, BLENDED_INVERSE_RAMPS_SLOW);
+ break;
+ case TC_FAST:
+ value = BITFVAL(BITS_FUN_LIGHT, BLENDED_INVERSE_RAMPS_FAST);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ mask = BITFMASK(BITS_FUN_LIGHT);
+ CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Chasing Lights fun light pattern.
+ *
+ * @param bank Tri-color LED bank
+ * @param pattern Chasing light pattern mode.
+ * @param speed Speed of pattern.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_chasinglightspattern(t_funlight_bank bank,
+ t_chaselight_pattern pattern,
+ t_tcled_fun_speed speed)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ if (pattern > BGR) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (speed) {
+ case TC_OFF:
+ value = BITFVAL(BITS_FUN_LIGHT, FUN_LIGHTS_OFF);
+ break;
+ case TC_SLOW:
+ if (pattern == PMIC_RGB) {
+ value =
+ BITFVAL(BITS_FUN_LIGHT, CHASING_LIGHTS_RGB_SLOW);
+ } else {
+ value =
+ BITFVAL(BITS_FUN_LIGHT, CHASING_LIGHTS_BGR_SLOW);
+ }
+ break;
+ case TC_FAST:
+ if (pattern == PMIC_RGB) {
+ value =
+ BITFVAL(BITS_FUN_LIGHT, CHASING_LIGHTS_RGB_FAST);
+ } else {
+ value =
+ BITFVAL(BITS_FUN_LIGHT, CHASING_LIGHTS_BGR_FAST);
+ }
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ mask = BITFMASK(BITS_FUN_LIGHT);
+ CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Strobe Mode fun light pattern.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param speed Speed of pattern.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_strobe(t_funlight_bank bank,
+ t_funlight_channel channel,
+ t_tcled_fun_strobe_speed speed)
+{
+ /* not supported on mc13783 */
+
+ return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function initiates Tri-color LED brightness Ramp Up function; Ramp time
+ * is fixed at 1 second.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param rampup Ramp-up configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_rampup(t_funlight_bank bank,
+ t_funlight_channel channel, bool rampup)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ mask = LEDR1RAMPUP;
+ value = LEDR1RAMPUP;
+ break;
+ case TCLED_FUN_BANK2:
+ mask = LEDR2RAMPUP;
+ value = LEDR2RAMPUP;
+ break;
+ case TCLED_FUN_BANK3:
+ mask = LEDR3RAMPUP;
+ value = LEDR3RAMPUP;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_FUN_CHANNEL1:
+ mask = mask;
+ value = value;
+ break;
+ case TCLED_FUN_CHANNEL2:
+ mask = mask * 2;
+ value = value * 2;
+ break;
+ case TCLED_FUN_CHANNEL3:
+ mask = mask * 4;
+ value = value * 4;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ if (!rampup) {
+ value = 0;
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets Tri-color LED brightness Ramp Up function; Ramp time
+ * is fixed at 1 second.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param rampup Ramp-up configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_get_fun_rampup(t_funlight_bank bank,
+ t_funlight_channel channel, bool * rampup)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ mask = LEDR1RAMPUP;
+ break;
+ case TCLED_FUN_BANK2:
+ mask = LEDR2RAMPUP;
+ break;
+ case TCLED_FUN_BANK3:
+ mask = LEDR3RAMPUP;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_FUN_CHANNEL1:
+ mask = mask;
+ break;
+ case TCLED_FUN_CHANNEL2:
+ mask = mask * 2;
+ break;
+ case TCLED_FUN_CHANNEL3:
+ mask = mask * 4;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(LREG_1, &value, mask));
+ if (value) {
+ *rampup = true;
+ } else {
+ *rampup = false;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Tri-color LED brightness Ramp Down function; Ramp
+ * time is fixed at 1 second.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param rampdown Ramp-down configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_rampdown(t_funlight_bank bank,
+ t_funlight_channel channel, bool rampdown)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ mask = LEDR1RAMPDOWN;
+ value = LEDR1RAMPDOWN;
+ break;
+ case TCLED_FUN_BANK2:
+ mask = LEDR2RAMPDOWN;
+ value = LEDR2RAMPDOWN;
+ break;
+ case TCLED_FUN_BANK3:
+ mask = LEDR3RAMPDOWN;
+ value = LEDR3RAMPDOWN;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_FUN_CHANNEL1:
+ mask = mask;
+ value = value;
+ break;
+ case TCLED_FUN_CHANNEL2:
+ mask = mask * 2;
+ value = value * 2;
+ break;
+ case TCLED_FUN_CHANNEL3:
+ mask = mask * 4;
+ value = value * 4;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ if (!rampdown) {
+ value = 0;
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function initiates Tri-color LED brightness Ramp Down function; Ramp
+ * time is fixed at 1 second.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param rampdown Ramp-down configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_get_fun_rampdown(t_funlight_bank bank,
+ t_funlight_channel channel,
+ bool * rampdown)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ mask = LEDR1RAMPDOWN;
+ break;
+ case TCLED_FUN_BANK2:
+ mask = LEDR2RAMPDOWN;
+ break;
+ case TCLED_FUN_BANK3:
+ mask = LEDR3RAMPDOWN;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (channel) {
+ case TCLED_FUN_CHANNEL1:
+ mask = mask;
+ break;
+ case TCLED_FUN_CHANNEL2:
+ mask = mask * 2;
+ break;
+ case TCLED_FUN_CHANNEL3:
+ mask = mask * 4;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(LREG_1, &value, mask));
+ if (value) {
+ *rampdown = true;
+ } else {
+ *rampdown = false;
+ }
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables a Tri-color channel triode mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_triode_on(t_funlight_bank bank,
+ t_funlight_channel channel)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ mask = MASK_BK1_FL;
+ value = ENABLE_BK1_FL;
+ break;
+ case TCLED_FUN_BANK2:
+ mask = MASK_BK2_FL;
+ value = ENABLE_BK2_FL;
+ break;
+ case TCLED_FUN_BANK3:
+ mask = MASK_BK3_FL;
+ value = ENABLE_BK2_FL;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables a Tri-color LED channel triode mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_triode_off(t_funlight_bank bank,
+ t_funlight_channel channel)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ switch (bank) {
+ case TCLED_FUN_BANK1:
+ mask = MASK_BK1_FL;
+ break;
+ case TCLED_FUN_BANK2:
+ mask = MASK_BK2_FL;
+ break;
+ case TCLED_FUN_BANK3:
+ mask = MASK_BK3_FL;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables Tri-color LED edge slowing.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_enable_edge_slow(void)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ value = BITFVAL(BIT_SLEWLIMTC, 1);
+ mask = BITFMASK(BIT_SLEWLIMTC);
+
+ CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables Tri-color LED edge slowing.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_disable_edge_slow(void)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ value = BITFVAL(BIT_SLEWLIMTC, 0);
+ mask = BITFMASK(BIT_SLEWLIMTC);
+
+ CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables Tri-color LED half current mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_enable_half_current(void)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ value = BITFVAL(BIT_TC1HALF, 1);
+ mask = BITFMASK(BIT_TC1HALF);
+
+ CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function disables Tri-color LED half current mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_disable_half_current(void)
+{
+ unsigned int mask = 0;
+ unsigned int value = 0;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ value = BITFVAL(BIT_TC1HALF, 0);
+ mask = BITFMASK(BIT_TC1HALF);
+
+ CHECK_ERROR(pmic_write_reg(LREG_1, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables backlight or Tri-color LED audio modulation.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_tcled_enable_audio_modulation(t_led_channel channel,
+ t_aud_path path,
+ t_aud_gain gain, bool lpf_bypass)
+{
+ return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function disables backlight or Tri-color LED audio modulation.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_tcled_disable_audio_modulation(void)
+{
+ return PMIC_NOT_SUPPORTED;
+}
+
+/*!
+ * This function enables the boost mode.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param en_dis Enable or disable the boost mode
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_set_boost_mode(bool en_dis)
+{
+
+ pmic_version_t mc13783_ver;
+ unsigned int mask;
+ unsigned int value;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ value = BITFVAL(BIT_BOOSTEN, en_dis);
+ mask = BITFMASK(BIT_BOOSTEN);
+ CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function gets the boost mode.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param en_dis Enable or disable the boost mode
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_get_boost_mode(bool * en_dis)
+{
+ pmic_version_t mc13783_ver;
+ unsigned int mask;
+ unsigned int value;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ mask = BITFMASK(BIT_BOOSTEN);
+ CHECK_ERROR(pmic_read_reg(LREG_0, &value, mask));
+ *en_dis = BITFEXT(value, BIT_BOOSTEN);
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function sets boost mode configuration
+ * Only on mc13783 2.0 or higher
+ *
+ * @param abms Define adaptive boost mode selection
+ * @param abr Define adaptive boost reference
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_config_boost_mode(unsigned int abms, unsigned int abr)
+{
+ unsigned int conf_boost = 0;
+ unsigned int mask;
+ unsigned int value;
+ pmic_version_t mc13783_ver;
+
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ if (abms > MAX_BOOST_ABMS) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ if (abr > MAX_BOOST_ABR) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ conf_boost = abms | (abr << 3);
+
+ value = BITFVAL(BITS_BOOST, conf_boost);
+ mask = BITFMASK(BITS_BOOST);
+ CHECK_ERROR(pmic_write_reg(LREG_0, value, mask));
+
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function gets boost mode configuration
+ * Only on mc13783 2.0 or higher
+ *
+ * @param abms Define adaptive boost mode selection
+ * @param abr Define adaptive boost reference
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_gets_boost_mode(unsigned int *abms, unsigned int *abr)
+{
+ unsigned int mask;
+ unsigned int value;
+ pmic_version_t mc13783_ver;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+
+ mask = BITFMASK(BITS_BOOST_ABMS);
+ CHECK_ERROR(pmic_read_reg(LREG_0, &value, mask));
+ *abms = BITFEXT(value, BITS_BOOST_ABMS);
+
+ mask = BITFMASK(BITS_BOOST_ABR);
+ CHECK_ERROR(pmic_read_reg(LREG_0, &value, mask));
+ *abr = BITFEXT(value, BITS_BOOST_ABR);
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function implements IOCTL controls on a PMIC Light device.
+ *
+
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @param cmd the command
+ * @param arg the parameter
+ * @return This function returns 0 if successful.
+ */
+static int pmic_light_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ t_bklit_setting_param *bklit_setting = NULL;
+ t_tcled_enable_param *tcled_setting;
+ t_fun_param *fun_param;
+ t_tcled_ind_param *tcled_ind;
+
+ if (_IOC_TYPE(cmd) != 'p')
+ return -ENOTTY;
+
+ switch (cmd) {
+ case PMIC_BKLIT_TCLED_ENABLE:
+ pmic_bklit_tcled_master_enable();
+ break;
+
+ case PMIC_BKLIT_TCLED_DISABLE:
+ pmic_bklit_tcled_master_disable();
+ break;
+
+ case PMIC_BKLIT_ENABLE:
+ pmic_bklit_master_enable();
+ break;
+
+ case PMIC_BKLIT_DISABLE:
+ pmic_bklit_master_disable();
+ break;
+
+ case PMIC_SET_BKLIT:
+ if ((bklit_setting = kmalloc(sizeof(t_bklit_setting_param),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(bklit_setting, (t_bklit_setting_param *) arg,
+ sizeof(t_bklit_setting_param))) {
+ kfree(bklit_setting);
+ return -EFAULT;
+ }
+
+ CHECK_ERROR_KFREE(pmic_bklit_set_mode(bklit_setting->channel,
+ bklit_setting->mode),
+ (kfree(bklit_setting)));
+
+ CHECK_ERROR_KFREE(pmic_bklit_set_current(bklit_setting->channel,
+ bklit_setting->
+ current_level),
+ (kfree(bklit_setting)));
+ CHECK_ERROR_KFREE(pmic_bklit_set_dutycycle
+ (bklit_setting->channel,
+ bklit_setting->duty_cycle),
+ (kfree(bklit_setting)));
+ CHECK_ERROR_KFREE(pmic_bklit_set_cycle_time
+ (bklit_setting->cycle_time),
+ (kfree(bklit_setting)));
+ CHECK_ERROR_KFREE(pmic_bklit_set_boost_mode
+ (bklit_setting->en_dis),
+ (kfree(bklit_setting)));
+ CHECK_ERROR_KFREE(pmic_bklit_config_boost_mode
+ (bklit_setting->abms, bklit_setting->abr),
+ (kfree(bklit_setting)));
+ if (bklit_setting->edge_slow != false) {
+ CHECK_ERROR_KFREE(pmic_bklit_enable_edge_slow(),
+ (kfree(bklit_setting)));
+ } else {
+ CHECK_ERROR_KFREE(pmic_bklit_disable_edge_slow(),
+ (kfree(bklit_setting)));
+ }
+
+ kfree(bklit_setting);
+ break;
+
+ case PMIC_GET_BKLIT:
+ if ((bklit_setting = kmalloc(sizeof(t_bklit_setting_param),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(bklit_setting, (t_bklit_setting_param *) arg,
+ sizeof(t_bklit_setting_param))) {
+ kfree(bklit_setting);
+ return -EFAULT;
+ }
+
+ CHECK_ERROR_KFREE(pmic_bklit_get_current(bklit_setting->channel,
+ &bklit_setting->
+ current_level),
+ (kfree(bklit_setting)));
+ CHECK_ERROR_KFREE(pmic_bklit_get_cycle_time
+ (&bklit_setting->cycle_time),
+ (kfree(bklit_setting)));
+ CHECK_ERROR_KFREE(pmic_bklit_get_dutycycle
+ (bklit_setting->channel,
+ &bklit_setting->duty_cycle),
+ (kfree(bklit_setting)));
+ bklit_setting->strobe = BACKLIGHT_STROBE_NONE;
+ CHECK_ERROR_KFREE(pmic_bklit_get_mode(bklit_setting->channel,
+ &bklit_setting->mode),
+ (kfree(bklit_setting)));
+ CHECK_ERROR_KFREE(pmic_bklit_get_edge_slow
+ (&bklit_setting->edge_slow),
+ (kfree(bklit_setting)));
+ CHECK_ERROR_KFREE(pmic_bklit_get_boost_mode
+ (&bklit_setting->en_dis),
+ (kfree(bklit_setting)));
+ CHECK_ERROR_KFREE(pmic_bklit_gets_boost_mode
+ (&bklit_setting->abms, &bklit_setting->abr),
+ (kfree(bklit_setting)));
+
+ if (copy_to_user((t_bklit_setting_param *) arg, bklit_setting,
+ sizeof(t_bklit_setting_param))) {
+ kfree(bklit_setting);
+ return -EFAULT;
+ }
+ kfree(bklit_setting);
+ break;
+
+ case PMIC_RAMPUP_BKLIT:
+ CHECK_ERROR(pmic_bklit_rampup((t_bklit_channel) arg));
+ break;
+
+ case PMIC_RAMPDOWN_BKLIT:
+ CHECK_ERROR(pmic_bklit_rampdown((t_bklit_channel) arg));
+ break;
+
+ case PMIC_OFF_RAMPUP_BKLIT:
+ CHECK_ERROR(pmic_bklit_off_rampup((t_bklit_channel) arg));
+ break;
+
+ case PMIC_OFF_RAMPDOWN_BKLIT:
+ CHECK_ERROR(pmic_bklit_off_rampdown((t_bklit_channel) arg));
+ break;
+
+ case PMIC_TCLED_ENABLE:
+ if ((tcled_setting = kmalloc(sizeof(t_tcled_enable_param),
+ GFP_KERNEL))
+ == NULL) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(tcled_setting, (t_tcled_enable_param *) arg,
+ sizeof(t_tcled_enable_param))) {
+ kfree(tcled_setting);
+ return -EFAULT;
+ }
+ CHECK_ERROR_KFREE(pmic_tcled_enable(tcled_setting->mode,
+ tcled_setting->bank),
+ (kfree(bklit_setting)));
+ break;
+
+ case PMIC_TCLED_DISABLE:
+ CHECK_ERROR(pmic_tcled_disable((t_funlight_bank) arg));
+ break;
+
+ case PMIC_TCLED_PATTERN:
+ if ((fun_param = kmalloc(sizeof(t_fun_param),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(fun_param,
+ (t_fun_param *) arg, sizeof(t_fun_param))) {
+ kfree(fun_param);
+ return -EFAULT;
+ }
+
+ switch (fun_param->pattern) {
+ case BLENDED_RAMPS_SLOW:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_blendedramps
+ (fun_param->bank, TC_SLOW),
+ (kfree(fun_param)));
+ break;
+
+ case BLENDED_RAMPS_FAST:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_blendedramps
+ (fun_param->bank, TC_FAST),
+ (kfree(fun_param)));
+ break;
+
+ case SAW_RAMPS_SLOW:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_sawramps
+ (fun_param->bank, TC_SLOW),
+ (kfree(fun_param)));
+ break;
+
+ case SAW_RAMPS_FAST:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_sawramps
+ (fun_param->bank, TC_FAST),
+ (kfree(fun_param)));
+ break;
+
+ case BLENDED_BOWTIE_SLOW:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_blendedbowtie
+ (fun_param->bank, TC_SLOW),
+ (kfree(fun_param)));
+ break;
+
+ case BLENDED_BOWTIE_FAST:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_blendedbowtie
+ (fun_param->bank, TC_FAST),
+ (kfree(fun_param)));
+ break;
+
+ case STROBE_SLOW:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_strobe
+ (fun_param->bank, fun_param->channel,
+ TC_STROBE_SLOW), (kfree(fun_param)));
+ break;
+
+ case STROBE_FAST:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_strobe
+ (fun_param->bank,
+ fun_param->channel, TC_STROBE_SLOW),
+ (kfree(fun_param)));
+ break;
+
+ case CHASING_LIGHT_RGB_SLOW:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_chasinglightspattern
+ (fun_param->bank, PMIC_RGB, TC_SLOW),
+ (kfree(fun_param)));
+ break;
+
+ case CHASING_LIGHT_RGB_FAST:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_chasinglightspattern
+ (fun_param->bank, PMIC_RGB, TC_FAST),
+ (kfree(fun_param)));
+ break;
+
+ case CHASING_LIGHT_BGR_SLOW:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_chasinglightspattern
+ (fun_param->bank, BGR, TC_SLOW),
+ (kfree(fun_param)));
+ break;
+
+ case CHASING_LIGHT_BGR_FAST:
+ CHECK_ERROR_KFREE(pmic_tcled_fun_chasinglightspattern
+ (fun_param->bank, BGR, TC_FAST),
+ (kfree(fun_param)));
+ break;
+ }
+
+ kfree(fun_param);
+ break;
+
+ case PMIC_SET_TCLED:
+ if ((tcled_ind = kmalloc(sizeof(t_tcled_ind_param), GFP_KERNEL))
+ == NULL) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(tcled_ind, (t_tcled_ind_param *) arg,
+ sizeof(t_tcled_ind_param))) {
+ kfree(tcled_ind);
+ return -EFAULT;
+ }
+ CHECK_ERROR_KFREE(pmic_tcled_ind_set_current(tcled_ind->channel,
+ tcled_ind->level,
+ tcled_ind->bank),
+ (kfree(tcled_ind)));
+ CHECK_ERROR_KFREE(pmic_tcled_ind_set_blink_pattern
+ (tcled_ind->channel, tcled_ind->pattern,
+ tcled_ind->skip, tcled_ind->bank),
+ (kfree(tcled_ind)));
+ CHECK_ERROR_KFREE(pmic_tcled_fun_rampup
+ (tcled_ind->bank, tcled_ind->channel,
+ tcled_ind->rampup), (kfree(tcled_ind)));
+ CHECK_ERROR_KFREE(pmic_tcled_fun_rampdown
+ (tcled_ind->bank, tcled_ind->channel,
+ tcled_ind->rampdown), (kfree(tcled_ind)));
+ if (tcled_ind->half_current) {
+ CHECK_ERROR_KFREE(pmic_tcled_enable_half_current(),
+ (kfree(tcled_ind)));
+ } else {
+ CHECK_ERROR_KFREE(pmic_tcled_disable_half_current(),
+ (kfree(tcled_ind)));
+ }
+
+ kfree(tcled_ind);
+ break;
+
+ case PMIC_GET_TCLED:
+ if ((tcled_ind = kmalloc(sizeof(t_tcled_ind_param), GFP_KERNEL))
+ == NULL) {
+ return -ENOMEM;
+ }
+ if (copy_from_user(tcled_ind, (t_tcled_ind_param *) arg,
+ sizeof(t_tcled_ind_param))) {
+ kfree(tcled_ind);
+ return -EFAULT;
+ }
+ CHECK_ERROR_KFREE(pmic_tcled_ind_get_current(tcled_ind->channel,
+ &tcled_ind->level,
+ tcled_ind->bank),
+ (kfree(tcled_ind)));
+ CHECK_ERROR_KFREE(pmic_tcled_ind_get_blink_pattern
+ (tcled_ind->channel, &tcled_ind->pattern,
+ &tcled_ind->skip, tcled_ind->bank),
+ (kfree(tcled_ind)));
+ CHECK_ERROR_KFREE(pmic_tcled_get_fun_rampup
+ (tcled_ind->bank, tcled_ind->channel,
+ &tcled_ind->rampup), (kfree(tcled_ind)));
+ CHECK_ERROR_KFREE(pmic_tcled_get_fun_rampdown
+ (tcled_ind->bank, tcled_ind->channel,
+ &tcled_ind->rampdown), (kfree(tcled_ind)));
+ if (copy_to_user
+ ((t_tcled_ind_param *) arg, tcled_ind,
+ sizeof(t_tcled_ind_param))) {
+ return -EFAULT;
+ }
+ kfree(tcled_ind);
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * This function initialize Light registers of mc13783 with 0.
+ *
+ * @return This function returns 0 if successful.
+ */
+int pmic_light_init_reg(void)
+{
+ CHECK_ERROR(pmic_write_reg(LREG_0, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(LREG_1, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(LREG_2, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(LREG_3, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(LREG_4, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(LREG_5, 0, PMIC_ALL_BITS));
+ return 0;
+}
+
+/*!
+ * This function implements the open method on a mc13783 light device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_light_open(struct inode *inode, struct file *file)
+{
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * This function implements the release method on a mc13783 light device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_light_release(struct inode *inode, struct file *file)
+{
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ return 0;
+}
+
+static struct file_operations pmic_light_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = pmic_light_ioctl,
+ .open = pmic_light_open,
+ .release = pmic_light_release,
+};
+
+static int pmic_light_remove(struct platform_device *pdev)
+{
+ device_destroy(pmic_light_class, MKDEV(pmic_light_major, 0));
+ class_destroy(pmic_light_class);
+ unregister_chrdev(pmic_light_major, "pmic_light");
+ return 0;
+}
+
+static int pmic_light_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device *temp_class;
+
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ pmic_light_major = register_chrdev(0, "pmic_light", &pmic_light_fops);
+
+ if (pmic_light_major < 0) {
+ printk(KERN_ERR "Unable to get a major for pmic_light\n");
+ return pmic_light_major;
+ }
+ init_waitqueue_head(&suspendq);
+
+ pmic_light_class = class_create(THIS_MODULE, "pmic_light");
+ if (IS_ERR(pmic_light_class)) {
+ printk(KERN_ERR "Error creating pmic_light class.\n");
+ ret = PTR_ERR(pmic_light_class);
+ goto err_out1;
+ }
+
+ temp_class = device_create(pmic_light_class, NULL,
+ MKDEV(pmic_light_major, 0), NULL,
+ "pmic_light");
+ if (IS_ERR(temp_class)) {
+ printk(KERN_ERR "Error creating pmic_light class device.\n");
+ ret = PTR_ERR(temp_class);
+ goto err_out2;
+ }
+
+ ret = pmic_light_init_reg();
+ if (ret != PMIC_SUCCESS) {
+ goto err_out3;
+ }
+
+ printk(KERN_INFO "PMIC Light successfully loaded\n");
+ return ret;
+
+ err_out3:
+ device_destroy(pmic_light_class, MKDEV(pmic_light_major, 0));
+ err_out2:
+ class_destroy(pmic_light_class);
+ err_out1:
+ unregister_chrdev(pmic_light_major, "pmic_light");
+ return ret;
+}
+
+static struct platform_driver pmic_light_driver_ldm = {
+ .driver = {
+ .name = "pmic_light",
+ },
+ .suspend = pmic_light_suspend,
+ .resume = pmic_light_resume,
+ .probe = pmic_light_probe,
+ .remove = pmic_light_remove,
+};
+
+/*
+ * Initialization and Exit
+ */
+
+static int __init pmic_light_init(void)
+{
+ pr_debug("PMIC Light driver loading...\n");
+ return platform_driver_register(&pmic_light_driver_ldm);
+}
+static void __exit pmic_light_exit(void)
+{
+ platform_driver_unregister(&pmic_light_driver_ldm);
+ pr_debug("PMIC Light driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall(pmic_light_init);
+module_exit(pmic_light_exit);
+
+MODULE_DESCRIPTION("PMIC_LIGHT");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_light_defs.h b/drivers/mxc/pmic/mc13783/pmic_light_defs.h
new file mode 100644
index 000000000000..80082363d9ed
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_light_defs.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_light_defs.h
+ * @brief This is the internal header PMIC(mc13783) Light and Backlight driver.
+ *
+ * @ingroup PMIC_LIGHT
+ */
+
+#ifndef __MC13783_LIGHT_DEFS_H__
+#define __MC13783_LIGHT_DEFS_H__
+
+#define LREG_0 REG_LED_CONTROL_0
+#define LREG_1 REG_LED_CONTROL_1
+#define LREG_2 REG_LED_CONTROL_2
+#define LREG_3 REG_LED_CONTROL_3
+#define LREG_4 REG_LED_CONTROL_4
+#define LREG_5 REG_LED_CONTROL_5
+
+/* REG_LED_CONTROL_0 */
+
+#define BIT_LEDEN_LSH 0
+#define BIT_LEDEN_WID 1
+#define MASK_TRIODE_MAIN_BL 0x080
+#define INDEX_AUXILIARY 1
+#define INDEX_KEYPAD 2
+#define BITS_FUN_LIGHT_LSH 17
+#define BITS_FUN_LIGHT_WID 4
+#define MASK_FUN_LIGHT 0x1E0000
+#define MASK_BK1_FL 0x200000
+#define ENABLE_BK1_FL 0x200000
+#define MASK_BK2_FL 0x400000
+#define ENABLE_BK2_FL 0x400000
+#define MASK_BK3_FL 0x800000
+#define ENABLE_BK3_FL 0x800000
+#define BIT_UP_MAIN_BL_LSH 1
+#define BIT_UP_MAIN_BL_WID 1
+#define BIT_UP_AUX_BL_LSH 2
+#define BIT_UP_AUX_BL_WID 1
+#define BIT_UP_KEY_BL_LSH 3
+#define BIT_UP_KEY_BL_WID 1
+#define BIT_DOWN_MAIN_BL_LSH 4
+#define BIT_DOWN_MAIN_BL_WID 1
+#define BIT_DOWN_AUX_BL_LSH 5
+#define BIT_DOWN_AUX_BL_WID 1
+#define BIT_DOWN_KEY_BL_LSH 6
+#define BIT_DOWN_KEY_BL_WID 1
+#define BIT_TRIODE_MAIN_BL_LSH 7
+#define BIT_TRIODE_MAIN_BL_WID 1
+#define BIT_TRIODE_AUX_BL_LSH 8
+#define BIT_TRIODE_AUX_BL_WID 1
+#define BIT_TRIODE_KEY_BL_LSH 9
+#define BIT_TRIODE_KEY_BL_WID 1
+
+#define BIT_BOOSTEN_LSH 10
+#define BIT_BOOSTEN_WID 1
+#define BITS_BOOST_LSH 11
+#define BITS_BOOST_WID 5
+#define BITS_BOOST_ABMS_LSH 11
+#define BITS_BOOST_ABMS_WID 3
+#define BITS_BOOST_ABR_LSH 14
+#define BITS_BOOST_ABR_WID 2
+
+#define MAX_BOOST_ABMS 7
+#define MAX_BOOST_ABR 3
+
+/* REG_LED_CONTROL_1 */
+
+#define BIT_SLEWLIMTC_LSH 23
+#define BIT_SLEWLIMTC_WID 1
+#define BIT_TC1HALF_LSH 18
+#define BIT_TC1HALF_WID 1
+#define LEDR1RAMPUP 0x000001
+#define LEDR2RAMPUP 0x000040
+#define LEDR3RAMPUP 0x001000
+#define LEDR1RAMPDOWN 0x000008
+#define LEDR2RAMPDOWN 0x000200
+#define LEDR3RAMPDOWN 0x008000
+
+/* REG_LED_CONTROL_2 */
+
+#define BIT_SLEWLIMBL_LSH 23
+#define BIT_SLEWLIMBL_WID 1
+#define BIT_DUTY_CYCLE 9
+#define MASK_DUTY_CYCLE 0x001E00
+#define INDEX_AUX 4
+#define INDEX_KYD 8
+#define BIT_CL_MAIN_LSH 0
+#define BIT_CL_MAIN_WID 3
+#define BIT_CL_AUX_LSH 3
+#define BIT_CL_AUX_WID 3
+#define BIT_CL_KEY_LSH 6
+#define BIT_CL_KEY_WID 3
+
+/* REG_LED_CONTROL_3 4 5 */
+#define BITS_CL_RED_LSH 0
+#define BITS_CL_RED_WID 2
+#define BITS_CL_GREEN_LSH 2
+#define BITS_CL_GREEN_WID 2
+#define BITS_CL_BLUE_LSH 4
+#define BITS_CL_BLUE_WID 2
+#define BITS_DC_RED_LSH 6
+#define BITS_DC_RED_WID 5
+#define BITS_DC_GREEN_LSH 11
+#define BITS_DC_GREEN_WID 5
+#define BITS_DC_BLUE_LSH 16
+#define BITS_DC_BLUE_WID 5
+#define BIT_PERIOD_LSH 21
+#define BIT_PERIOD_WID 2
+
+#define DUTY_CYCLE_MAX 31
+
+/* Fun light pattern */
+#define BLENDED_RAMPS_SLOW 0
+#define BLENDED_RAMPS_FAST 1
+#define SAW_RAMPS_SLOW 2
+#define SAW_RAMPS_FAST 3
+#define BLENDED_INVERSE_RAMPS_SLOW 4
+#define BLENDED_INVERSE_RAMPS_FAST 5
+#define CHASING_LIGHTS_RGB_SLOW 6
+#define CHASING_LIGHTS_RGB_FAST 7
+#define CHASING_LIGHTS_BGR_SLOW 8
+#define CHASING_LIGHTS_BGR_FAST 9
+#define FUN_LIGHTS_OFF 15
+
+/*!
+ * This function initialize Light registers of mc13783 with 0.
+ *
+ * @return This function returns 0 if successful.
+ */
+int pmic_light_init_reg(void);
+
+#endif /* __MC13783_LIGHT_DEFS_H__ */
diff --git a/drivers/mxc/pmic/mc13783/pmic_power.c b/drivers/mxc/pmic/mc13783/pmic_power.c
new file mode 100644
index 000000000000..8f877b887e16
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_power.c
@@ -0,0 +1,3146 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_power.c
+ * @brief This is the main file of PMIC(mc13783) Power driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/platform_device.h>
+#include <linux/ioctl.h>
+#include <linux/pmic_status.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <mach/pmic_power.h>
+
+#include "pmic_power_defs.h"
+
+#ifdef CONFIG_MXC_HWEVENT
+#include <mach/hw_events.h>
+#endif
+
+#include <asm/mach-types.h>
+
+#define MC13783_REGCTRL_GPOx_MASK 0x18000
+
+static bool VBKUP1_EN = false;
+static bool VBKUP2_EN = false;
+
+/*
+ * Power Pmic API
+ */
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_power_off);
+EXPORT_SYMBOL(pmic_power_set_pc_config);
+EXPORT_SYMBOL(pmic_power_get_pc_config);
+EXPORT_SYMBOL(pmic_power_regulator_on);
+EXPORT_SYMBOL(pmic_power_regulator_off);
+EXPORT_SYMBOL(pmic_power_regulator_set_voltage);
+EXPORT_SYMBOL(pmic_power_regulator_get_voltage);
+EXPORT_SYMBOL(pmic_power_regulator_set_config);
+EXPORT_SYMBOL(pmic_power_regulator_get_config);
+EXPORT_SYMBOL(pmic_power_vbkup2_auto_en);
+EXPORT_SYMBOL(pmic_power_get_vbkup2_auto_state);
+EXPORT_SYMBOL(pmic_power_bat_det_en);
+EXPORT_SYMBOL(pmic_power_get_bat_det_state);
+EXPORT_SYMBOL(pmic_power_vib_pin_en);
+EXPORT_SYMBOL(pmic_power_gets_vib_pin_state);
+EXPORT_SYMBOL(pmic_power_get_power_mode_sense);
+EXPORT_SYMBOL(pmic_power_set_regen_assig);
+EXPORT_SYMBOL(pmic_power_get_regen_assig);
+EXPORT_SYMBOL(pmic_power_set_regen_inv);
+EXPORT_SYMBOL(pmic_power_get_regen_inv);
+EXPORT_SYMBOL(pmic_power_esim_v_en);
+EXPORT_SYMBOL(pmic_power_gets_esim_v_state);
+EXPORT_SYMBOL(pmic_power_set_auto_reset_en);
+EXPORT_SYMBOL(pmic_power_get_auto_reset_en);
+EXPORT_SYMBOL(pmic_power_set_conf_button);
+EXPORT_SYMBOL(pmic_power_get_conf_button);
+EXPORT_SYMBOL(pmic_power_event_sub);
+EXPORT_SYMBOL(pmic_power_event_unsub);
+
+/*!
+ * This function is called to put the power in a low power state.
+ * Switching off the platform cannot be decided by
+ * the power module. It has to be handled by the
+ * client application.
+ *
+ * @param pdev the device structure used to give information on which power
+ * device (0 through 3 channels) to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int pmic_power_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+};
+
+/*!
+ * This function is called to resume the power from a low power state.
+ *
+ * @param pdev the device structure used to give information on which power
+ * device (0 through 3 channels) to suspend
+ *
+ * @return The function always returns 0.
+ */
+static int pmic_power_resume(struct platform_device *pdev)
+{
+ return 0;
+};
+
+/*!
+ * This function sets user power off in power control register and thus powers
+ * off the phone.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+void pmic_power_off(void)
+{
+ unsigned int mask, value;
+
+ mask = BITFMASK(MC13783_PWRCTRL_USER_OFF_SPI);
+ value = BITFVAL(MC13783_PWRCTRL_USER_OFF_SPI,
+ MC13783_PWRCTRL_USER_OFF_SPI_ENABLE);
+
+ pmic_write_reg(REG_POWER_CONTROL_0, value, mask);
+}
+
+/*!
+ * This function sets the power control configuration.
+ *
+ * @param pc_config power control configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_set_pc_config(t_pc_config * pc_config)
+{
+ unsigned int pwrctrl_val_reg0 = 0;
+ unsigned int pwrctrl_val_reg1 = 0;
+ unsigned int pwrctrl_mask_reg0 = 0;
+ unsigned int pwrctrl_mask_reg1 = 0;
+
+ if (pc_config == NULL) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ if (pc_config->pc_enable != false) {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_PCEN,
+ MC13783_PWRCTRL_PCEN_ENABLE);
+ pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_PCT,
+ pc_config->pc_timer);
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_PCEN,
+ MC13783_PWRCTRL_PCEN_DISABLE);
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_PCEN);
+ pwrctrl_mask_reg1 |= BITFMASK(MC13783_PWRCTRL_PCT);
+
+ if (pc_config->pc_count_enable != false) {
+
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_PC_COUNT_EN,
+ MC13783_PWRCTRL_PC_COUNT_EN_ENABLE);
+ pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_PC_COUNT,
+ pc_config->pc_count);
+ pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_PC_MAX_CNT,
+ pc_config->pc_max_count);
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_PC_COUNT_EN,
+ MC13783_PWRCTRL_PC_COUNT_EN_DISABLE);
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_PC_COUNT_EN);
+ pwrctrl_mask_reg1 |= BITFMASK(MC13783_PWRCTRL_PC_MAX_CNT) |
+ BITFMASK(MC13783_PWRCTRL_PC_COUNT);
+
+ if (pc_config->warm_enable != false) {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_WARM_EN,
+ MC13783_PWRCTRL_WARM_EN_ENABLE);
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_WARM_EN,
+ MC13783_PWRCTRL_WARM_EN_DISABLE);
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_WARM_EN);
+
+ if (pc_config->user_off_pc != false) {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_USER_OFF_PC,
+ MC13783_PWRCTRL_USER_OFF_PC_ENABLE);
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_WARM_EN,
+ MC13783_PWRCTRL_USER_OFF_PC_DISABLE);
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_USER_OFF_PC);
+
+ if (pc_config->clk_32k_user_off != false) {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_32OUT_USER_OFF,
+ MC13783_PWRCTRL_32OUT_USER_OFF_ENABLE);
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_32OUT_USER_OFF,
+ MC13783_PWRCTRL_32OUT_USER_OFF_DISABLE);
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_32OUT_USER_OFF);
+
+ if (pc_config->clk_32k_enable != false) {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_32OUT_EN,
+ MC13783_PWRCTRL_32OUT_EN_ENABLE);
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_32OUT_EN,
+ MC13783_PWRCTRL_32OUT_EN_DISABLE);
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_32OUT_EN);
+
+ if (pc_config->en_vbkup1 != false) {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP1_EN,
+ MC13783_PWRCTRL_VBKUP_ENABLE);
+ VBKUP1_EN = true;
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP1_EN,
+ MC13783_PWRCTRL_VBKUP_DISABLE);
+ VBKUP1_EN = false;
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_VBKUP1_EN);
+
+ if (pc_config->en_vbkup2 != false) {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP2_EN,
+ MC13783_PWRCTRL_VBKUP_ENABLE);
+ VBKUP2_EN = true;
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP2_EN,
+ MC13783_PWRCTRL_VBKUP_DISABLE);
+ VBKUP2_EN = false;
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_VBKUP2_EN);
+
+ if (pc_config->auto_en_vbkup1 != false) {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP1_AUTO_EN,
+ MC13783_PWRCTRL_VBKUP_ENABLE);
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP1_AUTO_EN,
+ MC13783_PWRCTRL_VBKUP_DISABLE);
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_VBKUP1_AUTO_EN);
+
+ if (pc_config->auto_en_vbkup2 != false) {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP2_AUTO_EN,
+ MC13783_PWRCTRL_VBKUP_ENABLE);
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP2_AUTO_EN,
+ MC13783_PWRCTRL_VBKUP_DISABLE);
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_VBKUP2_AUTO_EN);
+
+ if (VBKUP1_EN != false) {
+ if (pc_config->vhold_voltage > 3
+ || pc_config->vhold_voltage < 0) {
+ return PMIC_PARAMETER_ERROR;
+ } else {
+
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP1,
+ pc_config->vhold_voltage);
+ }
+ }
+ if (VBKUP2_EN != false) {
+ if (pc_config->vhold_voltage > 3
+ || pc_config->vhold_voltage < 0) {
+ return PMIC_PARAMETER_ERROR;
+ } else {
+ pwrctrl_val_reg0 |= BITFVAL(MC13783_PWRCTRL_VBKUP2,
+ pc_config->vhold_voltage2);
+ }
+ }
+ pwrctrl_mask_reg0 |= BITFMASK(MC13783_PWRCTRL_VBKUP1) |
+ BITFMASK(MC13783_PWRCTRL_VBKUP2);
+
+ if (pc_config->mem_allon != false) {
+ pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_MEM_ALLON,
+ MC13783_PWRCTRL_MEM_ALLON_ENABLE);
+ pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_MEM_TMR,
+ pc_config->mem_timer);
+ } else {
+ pwrctrl_val_reg1 |= BITFVAL(MC13783_PWRCTRL_MEM_ALLON,
+ MC13783_PWRCTRL_MEM_ALLON_DISABLE);
+ }
+ pwrctrl_mask_reg1 |= BITFMASK(MC13783_PWRCTRL_MEM_ALLON) |
+ BITFMASK(MC13783_PWRCTRL_MEM_TMR);
+
+ CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0,
+ pwrctrl_val_reg0, pwrctrl_mask_reg0));
+ CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_1,
+ pwrctrl_val_reg1, pwrctrl_mask_reg1));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives the power control configuration.
+ *
+ * @param pc_config pointer to power control configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_pc_config(t_pc_config * pc_config)
+{
+ unsigned int pwrctrl_val_reg0 = 0;
+ unsigned int pwrctrl_val_reg1 = 0;
+
+ if (pc_config == NULL) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_0,
+ &pwrctrl_val_reg0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_1,
+ &pwrctrl_val_reg1, PMIC_ALL_BITS));
+
+ if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_PCEN)
+ == MC13783_PWRCTRL_PCEN_ENABLE) {
+ pc_config->pc_enable = true;
+ pc_config->pc_timer = BITFEXT(pwrctrl_val_reg1,
+ MC13783_PWRCTRL_PCT);
+
+ } else {
+ pc_config->pc_enable = false;
+ pc_config->pc_timer = 0;
+ }
+
+ if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_PC_COUNT_EN)
+ == MC13783_PWRCTRL_PCEN_ENABLE) {
+ pc_config->pc_count_enable = true;
+ pc_config->pc_count = BITFEXT(pwrctrl_val_reg1,
+ MC13783_PWRCTRL_PC_COUNT);
+ pc_config->pc_max_count = BITFEXT(pwrctrl_val_reg1,
+ MC13783_PWRCTRL_PC_MAX_CNT);
+ } else {
+ pc_config->pc_count_enable = false;
+ pc_config->pc_count = 0;
+ pc_config->pc_max_count = 0;
+ }
+
+ if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_WARM_EN)
+ == MC13783_PWRCTRL_WARM_EN_ENABLE) {
+ pc_config->warm_enable = true;
+ } else {
+ pc_config->warm_enable = false;
+ }
+
+ if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_USER_OFF_PC)
+ == MC13783_PWRCTRL_USER_OFF_PC_ENABLE) {
+ pc_config->user_off_pc = true;
+ } else {
+ pc_config->user_off_pc = false;
+ }
+
+ if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_32OUT_USER_OFF)
+ == MC13783_PWRCTRL_32OUT_USER_OFF_ENABLE) {
+ pc_config->clk_32k_user_off = true;
+ } else {
+ pc_config->clk_32k_user_off = false;
+ }
+
+ if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_32OUT_EN)
+ == MC13783_PWRCTRL_32OUT_EN_ENABLE) {
+ pc_config->clk_32k_enable = true;
+ } else {
+ pc_config->clk_32k_enable = false;
+ }
+
+ if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_VBKUP1_AUTO_EN)
+ == MC13783_PWRCTRL_VBKUP_ENABLE) {
+ pc_config->auto_en_vbkup1 = true;
+ } else {
+ pc_config->auto_en_vbkup1 = false;
+ }
+
+ if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_VBKUP2_AUTO_EN)
+ == MC13783_PWRCTRL_VBKUP_ENABLE) {
+ pc_config->auto_en_vbkup2 = true;
+ } else {
+ pc_config->auto_en_vbkup2 = false;
+ }
+
+ if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_VBKUP1_EN)
+ == MC13783_PWRCTRL_VBKUP_ENABLE) {
+ pc_config->en_vbkup1 = true;
+ pc_config->vhold_voltage = BITFEXT(pwrctrl_val_reg0,
+ MC13783_PWRCTRL_VBKUP1);
+ } else {
+ pc_config->en_vbkup1 = false;
+ pc_config->vhold_voltage = 0;
+ }
+
+ if (BITFEXT(pwrctrl_val_reg0, MC13783_PWRCTRL_VBKUP2_EN)
+ == MC13783_PWRCTRL_VBKUP_ENABLE) {
+ pc_config->en_vbkup2 = true;
+ pc_config->vhold_voltage2 = BITFEXT(pwrctrl_val_reg0,
+ MC13783_PWRCTRL_VBKUP2);
+ } else {
+ pc_config->en_vbkup2 = false;
+ pc_config->vhold_voltage2 = 0;
+ }
+
+ if (BITFEXT(pwrctrl_val_reg1, MC13783_PWRCTRL_MEM_ALLON) ==
+ MC13783_PWRCTRL_MEM_ALLON_ENABLE) {
+ pc_config->mem_allon = true;
+ pc_config->mem_timer = BITFEXT(pwrctrl_val_reg1,
+ MC13783_PWRCTRL_MEM_TMR);
+ } else {
+ pc_config->mem_allon = false;
+ pc_config->mem_timer = 0;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function turns on a regulator.
+ *
+ * @param regulator The regulator to be truned on.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_on(t_pmic_regulator regulator)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_PLL:
+ reg_val = BITFVAL(MC13783_SWCTRL_PLL_EN,
+ MC13783_SWCTRL_PLL_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_SWCTRL_PLL_EN);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW3:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW3_EN,
+ MC13783_SWCTRL_SW3_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW3_EN);
+ reg = REG_SWITCHERS_5;
+ break;
+ case REGU_VAUDIO:
+ reg_val = BITFVAL(MC13783_REGCTRL_VAUDIO_EN,
+ MC13783_REGCTRL_VAUDIO_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VAUDIO_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VIOHI:
+ reg_val = BITFVAL(MC13783_REGCTRL_VIOHI_EN,
+ MC13783_REGCTRL_VIOHI_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VIOHI_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VIOLO:
+ reg_val = BITFVAL(MC13783_REGCTRL_VIOLO_EN,
+ MC13783_REGCTRL_VIOLO_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VIOLO_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VDIG:
+ reg_val = BITFVAL(MC13783_REGCTRL_VDIG_EN,
+ MC13783_REGCTRL_VDIG_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VDIG_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VGEN:
+ reg_val = BITFVAL(MC13783_REGCTRL_VGEN_EN,
+ MC13783_REGCTRL_VGEN_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VGEN_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFDIG:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFDIG_EN,
+ MC13783_REGCTRL_VRFDIG_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFDIG_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFREF:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFREF_EN,
+ MC13783_REGCTRL_VRFREF_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFREF_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFCP:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFCP_EN,
+ MC13783_REGCTRL_VRFCP_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFCP_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VSIM:
+ reg_val = BITFVAL(MC13783_REGCTRL_VSIM_EN,
+ MC13783_REGCTRL_VSIM_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VSIM_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VESIM:
+ reg_val = BITFVAL(MC13783_REGCTRL_VESIM_EN,
+ MC13783_REGCTRL_VESIM_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VESIM_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VCAM:
+ reg_val = BITFVAL(MC13783_REGCTRL_VCAM_EN,
+ MC13783_REGCTRL_VCAM_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VCAM_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRFBG:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFBG_EN,
+ MC13783_REGCTRL_VRFBG_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFBG_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VVIB:
+ reg_val = BITFVAL(MC13783_REGCTRL_VVIB_EN,
+ MC13783_REGCTRL_VVIB_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VVIB_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRF1:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRF1_EN,
+ MC13783_REGCTRL_VRF1_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRF1_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRF2:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRF2_EN,
+ MC13783_REGCTRL_VRF2_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRF2_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VMMC1:
+ reg_val = BITFVAL(MC13783_REGCTRL_VMMC1_EN,
+ MC13783_REGCTRL_VMMC1_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VMMC1_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VMMC2:
+ reg_val = BITFVAL(MC13783_REGCTRL_VMMC2_EN,
+ MC13783_REGCTRL_VMMC2_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VMMC2_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_GPO1:
+ reg_val = BITFVAL(MC13783_REGCTRL_GPO1_EN,
+ MC13783_REGCTRL_GPO1_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_GPO1_EN);
+ reg = REG_POWER_MISCELLANEOUS;
+ break;
+ case REGU_GPO2:
+ reg_val = BITFVAL(MC13783_REGCTRL_GPO2_EN,
+ MC13783_REGCTRL_GPO2_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_GPO2_EN);
+ reg = REG_POWER_MISCELLANEOUS;
+ break;
+ case REGU_GPO3:
+ reg_val = BITFVAL(MC13783_REGCTRL_GPO3_EN,
+ MC13783_REGCTRL_GPO3_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_GPO3_EN);
+ reg = REG_POWER_MISCELLANEOUS;
+ break;
+ case REGU_GPO4:
+ reg_val = BITFVAL(MC13783_REGCTRL_GPO4_EN,
+ MC13783_REGCTRL_GPO4_EN_ENABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_GPO4_EN);
+ reg = REG_POWER_MISCELLANEOUS;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function turns off a regulator.
+ *
+ * @param regulator The regulator to be truned off.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_off(t_pmic_regulator regulator)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_PLL:
+ reg_val = BITFVAL(MC13783_SWCTRL_PLL_EN,
+ MC13783_SWCTRL_PLL_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_SWCTRL_PLL_EN);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW3:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW3_EN,
+ MC13783_SWCTRL_SW3_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW3_EN);
+ reg = REG_SWITCHERS_5;
+ break;
+ case REGU_VAUDIO:
+ reg_val = BITFVAL(MC13783_REGCTRL_VAUDIO_EN,
+ MC13783_REGCTRL_VAUDIO_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VAUDIO_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VIOHI:
+ reg_val = BITFVAL(MC13783_REGCTRL_VIOHI_EN,
+ MC13783_REGCTRL_VIOHI_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VIOHI_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VIOLO:
+ reg_val = BITFVAL(MC13783_REGCTRL_VIOLO_EN,
+ MC13783_REGCTRL_VIOLO_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VIOLO_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VDIG:
+ reg_val = BITFVAL(MC13783_REGCTRL_VDIG_EN,
+ MC13783_REGCTRL_VDIG_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VDIG_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VGEN:
+ reg_val = BITFVAL(MC13783_REGCTRL_VGEN_EN,
+ MC13783_REGCTRL_VGEN_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VGEN_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFDIG:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFDIG_EN,
+ MC13783_REGCTRL_VRFDIG_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFDIG_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFREF:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFREF_EN,
+ MC13783_REGCTRL_VRFREF_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFREF_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFCP:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFCP_EN,
+ MC13783_REGCTRL_VRFCP_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFCP_EN);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VSIM:
+ reg_val = BITFVAL(MC13783_REGCTRL_VSIM_EN,
+ MC13783_REGCTRL_VSIM_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VSIM_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VESIM:
+ reg_val = BITFVAL(MC13783_REGCTRL_VESIM_EN,
+ MC13783_REGCTRL_VESIM_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VESIM_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VCAM:
+ reg_val = BITFVAL(MC13783_REGCTRL_VCAM_EN,
+ MC13783_REGCTRL_VCAM_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VCAM_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRFBG:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFBG_EN,
+ MC13783_REGCTRL_VRFBG_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFBG_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VVIB:
+ reg_val = BITFVAL(MC13783_REGCTRL_VVIB_EN,
+ MC13783_REGCTRL_VVIB_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VVIB_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRF1:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRF1_EN,
+ MC13783_REGCTRL_VRF1_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRF1_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRF2:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRF2_EN,
+ MC13783_REGCTRL_VRF2_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRF2_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VMMC1:
+ reg_val = BITFVAL(MC13783_REGCTRL_VMMC1_EN,
+ MC13783_REGCTRL_VMMC1_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VMMC1_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VMMC2:
+ reg_val = BITFVAL(MC13783_REGCTRL_VMMC2_EN,
+ MC13783_REGCTRL_VMMC2_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VMMC2_EN);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_GPO1:
+ reg_val = BITFVAL(MC13783_REGCTRL_GPO1_EN,
+ MC13783_REGCTRL_GPO1_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_GPO1_EN);
+ reg = REG_POWER_MISCELLANEOUS;
+ break;
+ case REGU_GPO2:
+ reg_val = BITFVAL(MC13783_REGCTRL_GPO2_EN,
+ MC13783_REGCTRL_GPO2_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_GPO2_EN);
+ reg = REG_POWER_MISCELLANEOUS;
+ break;
+ case REGU_GPO3:
+ reg_val = BITFVAL(MC13783_REGCTRL_GPO3_EN,
+ MC13783_REGCTRL_GPO3_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_GPO3_EN);
+ reg = REG_POWER_MISCELLANEOUS;
+ break;
+ case REGU_GPO4:
+ reg_val = BITFVAL(MC13783_REGCTRL_GPO4_EN,
+ MC13783_REGCTRL_GPO4_EN_DISABLE);
+ reg_mask = BITFMASK(MC13783_REGCTRL_GPO4_EN);
+ reg = REG_POWER_MISCELLANEOUS;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the regulator output voltage.
+ *
+ * @param regulator The regulator to be configured.
+ * @param voltage The regulator output voltage.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_set_voltage(t_pmic_regulator regulator,
+ t_regulator_voltage voltage)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_SW1A:
+ if ((voltage.sw1a < SW1A_0_9V) || (voltage.sw1a > SW1A_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW1A, voltage.sw1a);
+ reg_mask = BITFMASK(MC13783_SWSET_SW1A);
+ reg = REG_SWITCHERS_0;
+ break;
+ case SW_SW1B:
+ if ((voltage.sw1b < SW1B_0_9V) || (voltage.sw1b > SW1B_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW1B, voltage.sw1b);
+ reg_mask = BITFMASK(MC13783_SWSET_SW1B);
+ reg = REG_SWITCHERS_1;
+ break;
+ case SW_SW2A:
+ if ((voltage.sw2a < SW2A_0_9V) || (voltage.sw2a > SW2A_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW2A, voltage.sw1a);
+ reg_mask = BITFMASK(MC13783_SWSET_SW2A);
+ reg = REG_SWITCHERS_2;
+ break;
+ case SW_SW2B:
+ if ((voltage.sw2b < SW2B_0_9V) || (voltage.sw2b > SW2B_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW2B, voltage.sw2b);
+ reg_mask = BITFMASK(MC13783_SWSET_SW1A);
+ reg = REG_SWITCHERS_3;
+ break;
+ case SW_SW3:
+ if (voltage.sw3 != SW3_5V) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW3, voltage.sw3);
+ reg_mask = BITFMASK(MC13783_SWSET_SW3);
+ reg = REG_SWITCHERS_5;
+ break;
+ case REGU_VIOLO:
+ if ((voltage.violo < VIOLO_1_2V) ||
+ (voltage.violo > VIOLO_1_8V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VIOLO, voltage.violo);
+ reg_mask = BITFMASK(MC13783_REGSET_VIOLO);
+ reg = REG_REGULATOR_SETTING_0;
+ break;
+ case REGU_VDIG:
+ if ((voltage.vdig < VDIG_1_2V) || (voltage.vdig > VDIG_1_8V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VDIG, voltage.vdig);
+ reg_mask = BITFMASK(MC13783_REGSET_VDIG);
+ reg = REG_REGULATOR_SETTING_0;
+ break;
+ case REGU_VGEN:
+ if ((voltage.vgen < VGEN_1_2V) || (voltage.vgen > VGEN_2_4V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VGEN, voltage.vgen);
+ reg_mask = BITFMASK(MC13783_REGSET_VGEN);
+ reg = REG_REGULATOR_SETTING_0;
+ break;
+ case REGU_VRFDIG:
+ if ((voltage.vrfdig < VRFDIG_1_2V) ||
+ (voltage.vrfdig > VRFDIG_1_875V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VRFDIG, voltage.vrfdig);
+ reg_mask = BITFMASK(MC13783_REGSET_VRFDIG);
+ reg = REG_REGULATOR_SETTING_0;
+ break;
+ case REGU_VRFREF:
+ if ((voltage.vrfref < VRFREF_2_475V) ||
+ (voltage.vrfref > VRFREF_2_775V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VRFREF, voltage.vrfref);
+ reg_mask = BITFMASK(MC13783_REGSET_VRFREF);
+ reg = REG_REGULATOR_SETTING_0;
+ break;
+ case REGU_VRFCP:
+ if ((voltage.vrfcp < VRFCP_2_7V) ||
+ (voltage.vrfcp > VRFCP_2_775V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VRFCP, voltage.vrfcp);
+ reg_mask = BITFMASK(MC13783_REGSET_VRFCP);
+ reg = REG_REGULATOR_SETTING_0;
+ break;
+ case REGU_VSIM:
+ if ((voltage.vsim < VSIM_1_8V) || (voltage.vsim > VSIM_2_9V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VSIM, voltage.vsim);
+ reg_mask = BITFMASK(MC13783_REGSET_VSIM);
+ reg = REG_REGULATOR_SETTING_0;
+ break;
+ case REGU_VESIM:
+ if ((voltage.vesim < VESIM_1_8V) ||
+ (voltage.vesim > VESIM_2_9V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VESIM, voltage.vesim);
+ reg_mask = BITFMASK(MC13783_REGSET_VESIM);
+ reg = REG_REGULATOR_SETTING_0;
+ break;
+ case REGU_VCAM:
+ if ((voltage.vcam < VCAM_1_5V) || (voltage.vcam > VCAM_3V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VCAM, voltage.vcam);
+ reg_mask = BITFMASK(MC13783_REGSET_VCAM);
+ reg = REG_REGULATOR_SETTING_0;
+ break;
+ case REGU_VVIB:
+ if ((voltage.vvib < VVIB_1_3V) || (voltage.vvib > VVIB_3V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VVIB, voltage.vvib);
+ reg_mask = BITFMASK(MC13783_REGSET_VVIB);
+ reg = REG_REGULATOR_SETTING_1;
+ break;
+ case REGU_VRF1:
+ if ((voltage.vrf1 < VRF1_1_5V) || (voltage.vrf1 > VRF1_2_775V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VRF1, voltage.vrf1);
+ reg_mask = BITFMASK(MC13783_REGSET_VRF1);
+ reg = REG_REGULATOR_SETTING_1;
+ break;
+ case REGU_VRF2:
+ if ((voltage.vrf2 < VRF2_1_5V) || (voltage.vrf2 > VRF2_2_775V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VRF2, voltage.vrf2);
+ reg_mask = BITFMASK(MC13783_REGSET_VRF2);
+ reg = REG_REGULATOR_SETTING_1;
+ break;
+ case REGU_VMMC1:
+ if ((voltage.vmmc1 < VMMC1_1_6V) || (voltage.vmmc1 > VMMC1_3V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VMMC1, voltage.vmmc1);
+ reg_mask = BITFMASK(MC13783_REGSET_VMMC1);
+ reg = REG_REGULATOR_SETTING_1;
+ break;
+ case REGU_VMMC2:
+ if ((voltage.vmmc2 < VMMC2_1_6V) || (voltage.vmmc2 > VMMC2_3V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGSET_VMMC2, voltage.vmmc2);
+ reg_mask = BITFMASK(MC13783_REGSET_VMMC2);
+ reg = REG_REGULATOR_SETTING_1;
+ break;
+ case REGU_VAUDIO:
+ case REGU_VIOHI:
+ case REGU_VRFBG:
+ case REGU_GPO1:
+ case REGU_GPO2:
+ case REGU_GPO3:
+ case REGU_GPO4:
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives the regulator output voltage.
+ *
+ * @param regulator The regulator to be truned off.
+ * @param voltage Pointer to regulator output voltage.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_get_voltage(t_pmic_regulator regulator,
+ t_regulator_voltage * voltage)
+{
+ unsigned int reg_val = 0;
+
+ if (regulator == SW_SW1A) {
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_0,
+ &reg_val, PMIC_ALL_BITS));
+ } else if (regulator == SW_SW1B) {
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_1,
+ &reg_val, PMIC_ALL_BITS));
+ } else if (regulator == SW_SW2A) {
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_2,
+ &reg_val, PMIC_ALL_BITS));
+ } else if (regulator == SW_SW2B) {
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_3,
+ &reg_val, PMIC_ALL_BITS));
+ } else if (regulator == SW_SW3) {
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_5,
+ &reg_val, PMIC_ALL_BITS));
+ } else if ((regulator == REGU_VIOLO) || (regulator == REGU_VDIG) ||
+ (regulator == REGU_VGEN) ||
+ (regulator == REGU_VRFDIG) ||
+ (regulator == REGU_VRFREF) ||
+ (regulator == REGU_VRFCP) ||
+ (regulator == REGU_VSIM) ||
+ (regulator == REGU_VESIM) || (regulator == REGU_VCAM)) {
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+ &reg_val, PMIC_ALL_BITS));
+ } else if ((regulator == REGU_VVIB) || (regulator == REGU_VRF1) ||
+ (regulator == REGU_VRF2) ||
+ (regulator == REGU_VMMC1) || (regulator == REGU_VMMC2)) {
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_1,
+ &reg_val, PMIC_ALL_BITS));
+ }
+
+ switch (regulator) {
+ case SW_SW1A:
+ voltage->sw1a = BITFEXT(reg_val, MC13783_SWSET_SW1A);
+ break;
+ case SW_SW1B:
+ voltage->sw1b = BITFEXT(reg_val, MC13783_SWSET_SW1B);
+ break;
+ case SW_SW2A:
+ voltage->sw2a = BITFEXT(reg_val, MC13783_SWSET_SW2A);
+ break;
+ case SW_SW2B:
+ voltage->sw2b = BITFEXT(reg_val, MC13783_SWSET_SW2B);
+ break;
+ case SW_SW3:
+ voltage->sw3 = BITFEXT(reg_val, MC13783_SWSET_SW3);
+ break;
+ case REGU_VIOLO:
+ voltage->violo = BITFEXT(reg_val, MC13783_REGSET_VIOLO);
+ break;
+ case REGU_VDIG:
+ voltage->vdig = BITFEXT(reg_val, MC13783_REGSET_VDIG);
+ break;
+ case REGU_VGEN:
+ voltage->vgen = BITFEXT(reg_val, MC13783_REGSET_VGEN);
+ break;
+ case REGU_VRFDIG:
+ voltage->vrfdig = BITFEXT(reg_val, MC13783_REGSET_VRFDIG);
+ break;
+ case REGU_VRFREF:
+ voltage->vrfref = BITFEXT(reg_val, MC13783_REGSET_VRFREF);
+ break;
+ case REGU_VRFCP:
+ voltage->vrfcp = BITFEXT(reg_val, MC13783_REGSET_VRFCP);
+ break;
+ case REGU_VSIM:
+ voltage->vsim = BITFEXT(reg_val, MC13783_REGSET_VSIM);
+ break;
+ case REGU_VESIM:
+ voltage->vesim = BITFEXT(reg_val, MC13783_REGSET_VESIM);
+ break;
+ case REGU_VCAM:
+ voltage->vcam = BITFEXT(reg_val, MC13783_REGSET_VCAM);
+ break;
+ case REGU_VVIB:
+ voltage->vvib = BITFEXT(reg_val, MC13783_REGSET_VVIB);
+ break;
+ case REGU_VRF1:
+ voltage->vrf1 = BITFEXT(reg_val, MC13783_REGSET_VRF1);
+ break;
+ case REGU_VRF2:
+ voltage->vrf2 = BITFEXT(reg_val, MC13783_REGSET_VRF2);
+ break;
+ case REGU_VMMC1:
+ voltage->vmmc1 = BITFEXT(reg_val, MC13783_REGSET_VMMC1);
+ break;
+ case REGU_VMMC2:
+ voltage->vmmc2 = BITFEXT(reg_val, MC13783_REGSET_VMMC2);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the DVS voltage
+ *
+ * @param regulator The regulator to be configured.
+ * @param dvs The switch Dynamic Voltage Scaling
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_dvs(t_pmic_regulator regulator,
+ t_regulator_voltage dvs)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_SW1A:
+ if ((dvs.sw1a < SW1A_0_9V) || (dvs.sw1a > SW1A_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW1A_DVS, dvs.sw1a);
+ reg_mask = BITFMASK(MC13783_SWSET_SW1A_DVS);
+ reg = REG_SWITCHERS_0;
+ break;
+ case SW_SW1B:
+ if ((dvs.sw1b < SW1B_0_9V) || (dvs.sw1b > SW1B_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW1B_DVS, dvs.sw1b);
+ reg_mask = BITFMASK(MC13783_SWSET_SW1B_DVS);
+ reg = REG_SWITCHERS_1;
+ break;
+ case SW_SW2A:
+ if ((dvs.sw2a < SW2A_0_9V) || (dvs.sw2a > SW2A_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW2A_DVS, dvs.sw2a);
+ reg_mask = BITFMASK(MC13783_SWSET_SW2A_DVS);
+ reg = REG_SWITCHERS_2;
+ break;
+ case SW_SW2B:
+ if ((dvs.sw2b < SW2B_0_9V) || (dvs.sw2b > SW2B_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW2B_DVS, dvs.sw2b);
+ reg_mask = BITFMASK(MC13783_SWSET_SW2B_DVS);
+ reg = REG_SWITCHERS_3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the DVS voltage
+ *
+ * @param regulator The regulator to be handled.
+ * @param dvs The switch Dynamic Voltage Scaling
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_dvs(t_pmic_regulator regulator,
+ t_regulator_voltage * dvs)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_SW1A:
+ reg_mask = BITFMASK(MC13783_SWSET_SW1A_DVS);
+ reg = REG_SWITCHERS_0;
+ break;
+ case SW_SW1B:
+ reg_mask = BITFMASK(MC13783_SWSET_SW1B_DVS);
+ reg = REG_SWITCHERS_1;
+ break;
+ case SW_SW2A:
+ reg_mask = BITFMASK(MC13783_SWSET_SW2A_DVS);
+ reg = REG_SWITCHERS_2;
+ break;
+ case SW_SW2B:
+ reg_mask = BITFMASK(MC13783_SWSET_SW2B_DVS);
+ reg = REG_SWITCHERS_3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+ switch (regulator) {
+ case SW_SW1A:
+ *dvs = (t_regulator_voltage) BITFEXT(reg_val,
+ MC13783_SWSET_SW1A_DVS);
+ break;
+ case SW_SW1B:
+ *dvs = (t_regulator_voltage) BITFEXT(reg_val,
+ MC13783_SWSET_SW1B_DVS);
+ break;
+ case SW_SW2A:
+ *dvs = (t_regulator_voltage) BITFEXT(reg_val,
+ MC13783_SWSET_SW2A_DVS);
+ break;
+ case SW_SW2B:
+ *dvs = (t_regulator_voltage) BITFEXT(reg_val,
+ MC13783_SWSET_SW2B_DVS);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the standiby voltage
+ *
+ * @param regulator The regulator to be configured.
+ * @param stby The switch standby voltage
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_stby(t_pmic_regulator regulator,
+ t_regulator_voltage stby)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_SW1A:
+ if ((stby.sw1a < SW1A_0_9V) || (stby.sw1a > SW1A_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW1A_STDBY, stby.sw1a);
+ reg_mask = BITFMASK(MC13783_SWSET_SW1A_STDBY);
+ reg = REG_SWITCHERS_0;
+ break;
+ case SW_SW1B:
+ if ((stby.sw1b < SW1B_0_9V) || (stby.sw1b > SW1B_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW1B_STDBY, stby.sw1b);
+ reg_mask = BITFMASK(MC13783_SWSET_SW1B_STDBY);
+ reg = REG_SWITCHERS_1;
+ break;
+ case SW_SW2A:
+ if ((stby.sw2a < SW2A_0_9V) || (stby.sw2a > SW2A_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW2A_STDBY, stby.sw2a);
+ reg_mask = BITFMASK(MC13783_SWSET_SW2A_STDBY);
+ reg = REG_SWITCHERS_2;
+ break;
+ case SW_SW2B:
+ if ((stby.sw2b < SW2B_0_9V) || (stby.sw2b > SW2B_2_2V)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWSET_SW2B_STDBY, stby.sw2b);
+ reg_mask = BITFMASK(MC13783_SWSET_SW2B_STDBY);
+ reg = REG_SWITCHERS_3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the standiby voltage
+ *
+ * @param regulator The regulator to be handled.
+ * @param stby The switch standby voltage
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_stby(t_pmic_regulator regulator,
+ t_regulator_voltage * stby)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_SW1A:
+ reg_mask = BITFMASK(MC13783_SWSET_SW1A_STDBY);
+ reg = REG_SWITCHERS_0;
+ break;
+ case SW_SW1B:
+ reg_mask = BITFMASK(MC13783_SWSET_SW1B_STDBY);
+ reg = REG_SWITCHERS_1;
+ break;
+ case SW_SW2A:
+ reg_mask = BITFMASK(MC13783_SWSET_SW2A_STDBY);
+ reg = REG_SWITCHERS_2;
+ break;
+ case SW_SW2B:
+ reg_mask = BITFMASK(MC13783_SWSET_SW2B_STDBY);
+ reg = REG_SWITCHERS_3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+ switch (regulator) {
+ case SW_SW1A:
+ *stby = (t_regulator_voltage) BITFEXT(reg_val,
+ MC13783_SWSET_SW1A_STDBY);
+ break;
+ case SW_SW1B:
+ *stby = (t_regulator_voltage) BITFEXT(reg_val,
+ MC13783_SWSET_SW1B_STDBY);
+ break;
+ case SW_SW2A:
+ *stby = (t_regulator_voltage) BITFEXT(reg_val,
+ MC13783_SWSET_SW2A_STDBY);
+ break;
+ case SW_SW2B:
+ *stby = (t_regulator_voltage) BITFEXT(reg_val,
+ MC13783_SWSET_SW2B_STDBY);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the switchers mode.
+ *
+ * @param regulator The regulator to be configured.
+ * @param mode The switcher mode
+ * @param stby Switch between main and standby.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_mode(t_pmic_regulator regulator,
+ t_regulator_sw_mode mode, bool stby)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+ unsigned int l_mode;
+
+ if (mode == SYNC_RECT) {
+ l_mode = MC13783_SWCTRL_SW_MODE_SYNC_RECT_EN;
+ } else if (mode == NO_PULSE_SKIP) {
+ l_mode = MC13783_SWCTRL_SW_MODE_PULSE_NO_SKIP_EN;
+ } else if (mode == PULSE_SKIP) {
+ l_mode = MC13783_SWCTRL_SW_MODE_PULSE_SKIP_EN;
+ } else if (mode == LOW_POWER) {
+ l_mode = MC13783_SWCTRL_SW_MODE_LOW_POWER_EN;
+ } else {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (regulator) {
+ case SW_SW1A:
+ if (stby) {
+ reg_val =
+ BITFVAL(MC13783_SWCTRL_SW1A_STBY_MODE, l_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_STBY_MODE);
+ } else {
+ reg_val = BITFVAL(MC13783_SWCTRL_SW1A_MODE, l_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_MODE);
+ }
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW1B:
+ if (stby) {
+ reg_val =
+ BITFVAL(MC13783_SWCTRL_SW1B_STBY_MODE, l_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_STBY_MODE);
+ } else {
+ reg_val = BITFVAL(MC13783_SWCTRL_SW1B_MODE, l_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_MODE);
+ }
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW2A:
+ if (stby) {
+ reg_val =
+ BITFVAL(MC13783_SWCTRL_SW2A_STBY_MODE, l_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_STBY_MODE);
+ } else {
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2A_MODE, l_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_MODE);
+ }
+ reg = REG_SWITCHERS_5;
+ break;
+ case SW_SW2B:
+ if (stby) {
+ reg_val =
+ BITFVAL(MC13783_SWCTRL_SW2B_STBY_MODE, l_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_STBY_MODE);
+ } else {
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2B_MODE, l_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_MODE);
+ }
+ reg = REG_SWITCHERS_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the switchers mode.
+ *
+ * @param regulator The regulator to be handled.
+ * @param mode The switcher mode.
+ * @param stby Switch between main and standby.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_mode(t_pmic_regulator regulator,
+ t_regulator_sw_mode * mode, bool stby)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg = 0;
+ unsigned int l_mode = 0;
+
+ switch (regulator) {
+ case SW_SW1A:
+ if (stby) {
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_STBY_MODE);
+ } else {
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_MODE);
+ }
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW1B:
+ if (stby) {
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_STBY_MODE);
+ } else {
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_MODE);
+ }
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW2A:
+ if (stby) {
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_STBY_MODE);
+ } else {
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_MODE);
+ }
+ reg = REG_SWITCHERS_5;
+ break;
+ case SW_SW2B:
+ if (stby) {
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_STBY_MODE);
+ } else {
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_MODE);
+ }
+ reg = REG_SWITCHERS_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+ switch (regulator) {
+ case SW_SW1A:
+ if (stby) {
+ l_mode =
+ BITFEXT(reg_val, MC13783_SWCTRL_SW1A_STBY_MODE);
+ } else {
+ l_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW1A_MODE);
+ }
+ break;
+ case SW_SW1B:
+ if (stby) {
+ l_mode =
+ BITFEXT(reg_val, MC13783_SWCTRL_SW1B_STBY_MODE);
+ } else {
+ l_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW1B_MODE);
+ }
+ break;
+ case SW_SW2A:
+ if (stby) {
+ l_mode =
+ BITFEXT(reg_val, MC13783_SWCTRL_SW2A_STBY_MODE);
+ } else {
+ l_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW2A_MODE);
+ }
+ break;
+ case SW_SW2B:
+ if (stby) {
+ l_mode =
+ BITFEXT(reg_val, MC13783_SWCTRL_SW2B_STBY_MODE);
+ } else {
+ l_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW2B_MODE);
+ }
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ if (l_mode == MC13783_SWCTRL_SW_MODE_SYNC_RECT_EN) {
+ *mode = SYNC_RECT;
+ } else if (l_mode == MC13783_SWCTRL_SW_MODE_PULSE_NO_SKIP_EN) {
+ *mode = NO_PULSE_SKIP;
+ } else if (l_mode == MC13783_SWCTRL_SW_MODE_PULSE_SKIP_EN) {
+ *mode = PULSE_SKIP;
+ } else if (l_mode == MC13783_SWCTRL_SW_MODE_LOW_POWER_EN) {
+ *mode = LOW_POWER;
+ } else {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the switch dvs speed
+ *
+ * @param regulator The regulator to be configured.
+ * @param speed The dvs speed.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_dvs_speed(t_pmic_regulator regulator,
+ t_switcher_dvs_speed speed)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+ if (speed > 3 || speed < 0) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (regulator) {
+ case SW_SW1A:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW1A_DVS_SPEED, speed);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_DVS_SPEED);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW1B:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2B_DVS_SPEED, speed);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_DVS_SPEED);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW2A:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2A_DVS_SPEED, speed);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_DVS_SPEED);
+ reg = REG_SWITCHERS_5;
+ break;
+ case SW_SW2B:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2B_DVS_SPEED, speed);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_DVS_SPEED);
+ reg = REG_SWITCHERS_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the switch dvs speed
+ *
+ * @param regulator The regulator to be handled.
+ * @param speed The dvs speed.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_dvs_speed(t_pmic_regulator regulator,
+ t_switcher_dvs_speed * speed)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_SW1A:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_DVS_SPEED);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW1B:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_DVS_SPEED);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW2A:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_DVS_SPEED);
+ reg = REG_SWITCHERS_5;
+ break;
+ case SW_SW2B:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_DVS_SPEED);
+ reg = REG_SWITCHERS_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+ switch (regulator) {
+ case SW_SW1A:
+ *speed = BITFEXT(reg_val, MC13783_SWCTRL_SW1A_DVS_SPEED);
+ break;
+ case SW_SW1B:
+ *speed = BITFEXT(reg_val, MC13783_SWCTRL_SW1B_DVS_SPEED);
+ break;
+ case SW_SW2A:
+ *speed = BITFEXT(reg_val, MC13783_SWCTRL_SW2A_DVS_SPEED);
+ break;
+ case SW_SW2B:
+ *speed = BITFEXT(reg_val, MC13783_SWCTRL_SW2B_DVS_SPEED);
+ break;
+ default:
+ break;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the switch panic mode
+ *
+ * @param regulator The regulator to be configured.
+ * @param panic_mode Enable or disable panic mode
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_panic_mode(t_pmic_regulator regulator,
+ bool panic_mode)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_SW1A:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW1A_PANIC_MODE, panic_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_PANIC_MODE);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW1B:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2B_PANIC_MODE, panic_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_PANIC_MODE);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW2A:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2A_PANIC_MODE, panic_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_PANIC_MODE);
+ reg = REG_SWITCHERS_5;
+ break;
+ case SW_SW2B:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2B_PANIC_MODE, panic_mode);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_PANIC_MODE);
+ reg = REG_SWITCHERS_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the switch panic mode
+ *
+ * @param regulator The regulator to be handled
+ * @param panic_mode Enable or disable panic mode
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_panic_mode(t_pmic_regulator regulator,
+ bool * panic_mode)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_SW1A:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_PANIC_MODE);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW1B:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_PANIC_MODE);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW2A:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_PANIC_MODE);
+ reg = REG_SWITCHERS_5;
+ break;
+ case SW_SW2B:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_PANIC_MODE);
+ reg = REG_SWITCHERS_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+ switch (regulator) {
+ case SW_SW1A:
+ *panic_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW1A_PANIC_MODE);
+ break;
+ case SW_SW1B:
+ *panic_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW1B_PANIC_MODE);
+ break;
+ case SW_SW2A:
+ *panic_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW2A_PANIC_MODE);
+ break;
+ case SW_SW2B:
+ *panic_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW2B_PANIC_MODE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the switch softstart mode
+ *
+ * @param regulator The regulator to be configured.
+ * @param softstart Enable or disable softstart.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_softstart(t_pmic_regulator regulator,
+ bool softstart)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_SW1A:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW1A_SOFTSTART, softstart);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_SOFTSTART);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW1B:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2B_SOFTSTART, softstart);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_SOFTSTART);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW2A:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2A_SOFTSTART, softstart);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_SOFTSTART);
+ reg = REG_SWITCHERS_5;
+ break;
+ case SW_SW2B:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW2B_SOFTSTART, softstart);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_SOFTSTART);
+ reg = REG_SWITCHERS_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the switch softstart mode
+ *
+ * @param regulator The regulator to be handled
+ * @param softstart Enable or disable softstart.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_softstart(t_pmic_regulator regulator,
+ bool * softstart)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+
+ switch (regulator) {
+ case SW_SW1A:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1A_SOFTSTART);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW1B:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW1B_SOFTSTART);
+ reg = REG_SWITCHERS_4;
+ break;
+ case SW_SW2A:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2A_SOFTSTART);
+ reg = REG_SWITCHERS_5;
+ break;
+ case SW_SW2B:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW2B_SOFTSTART);
+ reg = REG_SWITCHERS_5;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+ switch (regulator) {
+ case SW_SW1A:
+ *softstart = BITFEXT(reg_val, MC13783_SWCTRL_SW1A_SOFTSTART);
+ break;
+ case SW_SW1B:
+ *softstart = BITFEXT(reg_val, MC13783_SWCTRL_SW2B_SOFTSTART);
+ break;
+ case SW_SW2A:
+ *softstart = BITFEXT(reg_val, MC13783_SWCTRL_SW2A_SOFTSTART);
+ break;
+ case SW_SW2B:
+ *softstart = BITFEXT(reg_val, MC13783_SWCTRL_SW2B_SOFTSTART);
+ break;
+ default:
+ break;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the PLL multiplication factor
+ *
+ * @param regulator The regulator to be configured.
+ * @param factor The multiplication factor.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_set_factor(t_pmic_regulator regulator,
+ t_switcher_factor factor)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+
+ if (regulator != SW_PLL) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ if (factor < FACTOR_28 || factor > FACTOR_35) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_SWCTRL_PLL_FACTOR, factor);
+ reg_mask = BITFMASK(MC13783_SWCTRL_PLL_FACTOR);
+
+ CHECK_ERROR(pmic_write_reg(REG_SWITCHERS_4, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the PLL multiplication factor
+ *
+ * @param regulator The regulator to be handled
+ * @param factor The multiplication factor.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_switcher_get_factor(t_pmic_regulator regulator,
+ t_switcher_factor * factor)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+
+ if (regulator != SW_PLL) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_mask = BITFMASK(MC13783_SWCTRL_PLL_FACTOR);
+
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_4, &reg_val, reg_mask));
+
+ *factor = BITFEXT(reg_val, MC13783_SWCTRL_PLL_FACTOR);
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables or disables low power mode.
+ *
+ * @param regulator The regulator to be configured.
+ * @param lp_mode Select nominal or low power mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_set_lp_mode(t_pmic_regulator regulator,
+ t_regulator_lp_mode lp_mode)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+ unsigned int l_mode, l_stby;
+
+ if (lp_mode == LOW_POWER_DISABLED) {
+ l_mode = MC13783_REGTRL_LP_MODE_DISABLE;
+ l_stby = MC13783_REGTRL_STBY_MODE_DISABLE;
+ } else if (lp_mode == LOW_POWER_CTRL_BY_PIN) {
+ l_mode = MC13783_REGTRL_LP_MODE_DISABLE;
+ l_stby = MC13783_REGTRL_STBY_MODE_ENABLE;
+ } else if (lp_mode == LOW_POWER_EN) {
+ l_mode = MC13783_REGTRL_LP_MODE_ENABLE;
+ l_stby = MC13783_REGTRL_STBY_MODE_DISABLE;
+ } else if (lp_mode == LOW_POWER_AND_LOW_POWER_CTRL_BY_PIN) {
+ l_mode = MC13783_REGTRL_LP_MODE_ENABLE;
+ l_stby = MC13783_REGTRL_STBY_MODE_ENABLE;
+ } else {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (regulator) {
+ case SW_SW3:
+ reg_val = BITFVAL(MC13783_SWCTRL_SW3_MODE, l_mode) |
+ BITFVAL(MC13783_SWCTRL_SW3_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW3_MODE) |
+ BITFMASK(MC13783_SWCTRL_SW3_STBY);
+ reg = REG_SWITCHERS_5;
+ break;
+ case REGU_VAUDIO:
+ reg_val = BITFVAL(MC13783_REGCTRL_VAUDIO_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VAUDIO_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VAUDIO_MODE) |
+ BITFMASK(MC13783_REGCTRL_VAUDIO_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VIOHI:
+ reg_val = BITFVAL(MC13783_REGCTRL_VIOHI_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VIOHI_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VIOHI_MODE) |
+ BITFMASK(MC13783_REGCTRL_VIOHI_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VIOLO:
+ reg_val = BITFVAL(MC13783_REGCTRL_VIOLO_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VIOLO_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VIOLO_MODE) |
+ BITFMASK(MC13783_REGCTRL_VIOLO_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VDIG:
+ reg_val = BITFVAL(MC13783_REGCTRL_VDIG_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VDIG_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VDIG_MODE) |
+ BITFMASK(MC13783_REGCTRL_VDIG_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VGEN:
+ reg_val = BITFVAL(MC13783_REGCTRL_VGEN_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VGEN_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VGEN_MODE) |
+ BITFMASK(MC13783_REGCTRL_VGEN_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFDIG:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFDIG_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VRFDIG_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFDIG_MODE) |
+ BITFMASK(MC13783_REGCTRL_VRFDIG_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFREF:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFREF_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VRFREF_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFREF_MODE) |
+ BITFMASK(MC13783_REGCTRL_VRFREF_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFCP:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFCP_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VRFCP_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFCP_MODE) |
+ BITFMASK(MC13783_REGCTRL_VRFCP_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VSIM:
+ reg_val = BITFVAL(MC13783_REGCTRL_VSIM_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VSIM_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VSIM_MODE) |
+ BITFMASK(MC13783_REGCTRL_VSIM_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VESIM:
+ reg_val = BITFVAL(MC13783_REGCTRL_VESIM_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VESIM_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VESIM_MODE) |
+ BITFMASK(MC13783_REGCTRL_VESIM_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VCAM:
+ reg_val = BITFVAL(MC13783_REGCTRL_VCAM_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VCAM_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VCAM_MODE) |
+ BITFMASK(MC13783_REGCTRL_VCAM_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRFBG:
+ if ((lp_mode == LOW_POWER) ||
+ (lp_mode == LOW_POWER_AND_LOW_POWER_CTRL_BY_PIN)) {
+ return PMIC_PARAMETER_ERROR;
+ }
+ reg_val = BITFVAL(MC13783_REGCTRL_VRFBG_STBY, l_mode);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFBG_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRF1:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRF1_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VRF1_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRF1_MODE) |
+ BITFMASK(MC13783_REGCTRL_VRF1_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRF2:
+ reg_val = BITFVAL(MC13783_REGCTRL_VRF2_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VRF2_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRF2_MODE) |
+ BITFMASK(MC13783_REGCTRL_VRF2_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VMMC1:
+ reg_val = BITFVAL(MC13783_REGCTRL_VMMC1_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VMMC1_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VMMC1_MODE) |
+ BITFMASK(MC13783_REGCTRL_VMMC1_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VMMC2:
+ reg_val = BITFVAL(MC13783_REGCTRL_VMMC2_MODE, l_mode) |
+ BITFVAL(MC13783_REGCTRL_VMMC2_STBY, l_stby);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VMMC2_MODE) |
+ BITFMASK(MC13783_REGCTRL_VMMC2_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(reg, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets low power mode.
+ *
+ * @param regulator The regulator to be handled
+ * @param lp_mode Select nominal or low power mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_get_lp_mode(t_pmic_regulator regulator,
+ t_regulator_lp_mode * lp_mode)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int reg;
+ unsigned int l_mode, l_stby;
+
+ switch (regulator) {
+ case SW_SW3:
+ reg_mask = BITFMASK(MC13783_SWCTRL_SW3_MODE) |
+ BITFMASK(MC13783_SWCTRL_SW3_STBY);
+ reg = REG_SWITCHERS_5;
+ break;
+ case REGU_VAUDIO:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VAUDIO_MODE) |
+ BITFMASK(MC13783_REGCTRL_VAUDIO_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VIOHI:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VIOHI_MODE) |
+ BITFMASK(MC13783_REGCTRL_VIOHI_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VIOLO:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VIOLO_MODE) |
+ BITFMASK(MC13783_REGCTRL_VIOLO_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VDIG:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VDIG_MODE) |
+ BITFMASK(MC13783_REGCTRL_VDIG_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VGEN:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VGEN_MODE) |
+ BITFMASK(MC13783_REGCTRL_VGEN_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFDIG:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFDIG_MODE) |
+ BITFMASK(MC13783_REGCTRL_VRFDIG_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFREF:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFREF_MODE) |
+ BITFMASK(MC13783_REGCTRL_VRFREF_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VRFCP:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFCP_MODE) |
+ BITFMASK(MC13783_REGCTRL_VRFCP_STBY);
+ reg = REG_REGULATOR_MODE_0;
+ break;
+ case REGU_VSIM:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VSIM_MODE) |
+ BITFMASK(MC13783_REGCTRL_VSIM_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VESIM:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VESIM_MODE) |
+ BITFMASK(MC13783_REGCTRL_VESIM_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VCAM:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VCAM_MODE) |
+ BITFMASK(MC13783_REGCTRL_VCAM_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRFBG:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRFBG_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRF1:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRF1_MODE) |
+ BITFMASK(MC13783_REGCTRL_VRF1_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VRF2:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VRF2_MODE) |
+ BITFMASK(MC13783_REGCTRL_VRF2_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VMMC1:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VMMC1_MODE) |
+ BITFMASK(MC13783_REGCTRL_VMMC1_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ case REGU_VMMC2:
+ reg_mask = BITFMASK(MC13783_REGCTRL_VMMC2_MODE) |
+ BITFMASK(MC13783_REGCTRL_VMMC2_STBY);
+ reg = REG_REGULATOR_MODE_1;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &reg_val, reg_mask));
+
+ switch (regulator) {
+ case SW_SW3:
+ l_mode = BITFEXT(reg_val, MC13783_SWCTRL_SW3_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_SWCTRL_SW3_STBY);
+ break;
+ case REGU_VAUDIO:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VAUDIO_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VAUDIO_STBY);
+ break;
+ case REGU_VIOHI:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VIOHI_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VIOHI_STBY);
+ break;
+ case REGU_VIOLO:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VIOLO_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VIOLO_STBY);
+ break;
+ case REGU_VDIG:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VDIG_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VDIG_STBY);
+ break;
+ case REGU_VGEN:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VGEN_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VGEN_STBY);
+ break;
+ case REGU_VRFDIG:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VRFDIG_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRFDIG_STBY);
+ break;
+ case REGU_VRFREF:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VRFREF_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRFREF_STBY);
+ break;
+ case REGU_VRFCP:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VRFCP_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRFCP_STBY);
+ break;
+ case REGU_VSIM:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VSIM_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VSIM_STBY);
+ break;
+ case REGU_VESIM:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VESIM_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VESIM_STBY);
+ break;
+ case REGU_VCAM:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VCAM_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VCAM_STBY);
+ break;
+ case REGU_VRFBG:
+ l_mode = MC13783_REGTRL_LP_MODE_DISABLE;
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRFBG_STBY);
+ break;
+ case REGU_VRF1:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VRF1_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRF1_STBY);
+ break;
+ case REGU_VRF2:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VRF2_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VRF2_STBY);
+ break;
+ case REGU_VMMC1:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VMMC1_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VMMC1_STBY);
+ break;
+ case REGU_VMMC2:
+ l_mode = BITFEXT(reg_val, MC13783_REGCTRL_VMMC2_MODE);
+ l_stby = BITFEXT(reg_val, MC13783_REGCTRL_VMMC2_STBY);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ if ((l_mode == MC13783_REGTRL_LP_MODE_DISABLE) &&
+ (l_stby == MC13783_REGTRL_STBY_MODE_DISABLE)) {
+ *lp_mode = LOW_POWER_DISABLED;
+ } else if ((l_mode == MC13783_REGTRL_LP_MODE_DISABLE) &&
+ (l_stby == MC13783_REGTRL_STBY_MODE_ENABLE)) {
+ *lp_mode = LOW_POWER_CTRL_BY_PIN;
+ } else if ((l_mode == MC13783_REGTRL_LP_MODE_ENABLE) &&
+ (l_stby == MC13783_REGTRL_STBY_MODE_DISABLE)) {
+ *lp_mode = LOW_POWER_EN;
+ } else {
+ *lp_mode = LOW_POWER_AND_LOW_POWER_CTRL_BY_PIN;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the regulator configuration.
+ *
+ * @param regulator The regulator to be configured.
+ * @param config The regulator output configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_set_config(t_pmic_regulator regulator,
+ t_regulator_config * config)
+{
+ if (config == NULL) {
+ return PMIC_ERROR;
+ }
+
+ switch (regulator) {
+ case SW_SW1A:
+ case SW_SW1B:
+ case SW_SW2A:
+ case SW_SW2B:
+ CHECK_ERROR(pmic_power_regulator_set_voltage
+ (regulator, config->voltage));
+ CHECK_ERROR(pmic_power_switcher_set_dvs
+ (regulator, config->voltage_lvs));
+ CHECK_ERROR(pmic_power_switcher_set_stby
+ (regulator, config->voltage_stby));
+ CHECK_ERROR(pmic_power_switcher_set_mode
+ (regulator, config->mode, false));
+ CHECK_ERROR(pmic_power_switcher_set_mode
+ (regulator, config->stby_mode, true));
+ CHECK_ERROR(pmic_power_switcher_set_dvs_speed
+ (regulator, config->dvs_speed));
+ CHECK_ERROR(pmic_power_switcher_set_panic_mode
+ (regulator, config->panic_mode));
+ CHECK_ERROR(pmic_power_switcher_set_softstart
+ (regulator, config->softstart));
+ break;
+ case SW_PLL:
+ CHECK_ERROR(pmic_power_switcher_set_factor
+ (regulator, config->factor));
+ break;
+ case SW_SW3:
+ case REGU_VIOLO:
+ case REGU_VDIG:
+ case REGU_VGEN:
+ case REGU_VRFDIG:
+ case REGU_VRFREF:
+ case REGU_VRFCP:
+ case REGU_VSIM:
+ case REGU_VESIM:
+ case REGU_VCAM:
+ case REGU_VRF1:
+ case REGU_VRF2:
+ case REGU_VMMC1:
+ case REGU_VMMC2:
+ CHECK_ERROR(pmic_power_regulator_set_voltage
+ (regulator, config->voltage));
+ CHECK_ERROR(pmic_power_regulator_set_lp_mode
+ (regulator, config->lp_mode));
+ break;
+ case REGU_VVIB:
+ CHECK_ERROR(pmic_power_regulator_set_voltage
+ (regulator, config->voltage));
+ break;
+ case REGU_VAUDIO:
+ case REGU_VIOHI:
+ case REGU_VRFBG:
+ CHECK_ERROR(pmic_power_regulator_set_lp_mode
+ (regulator, config->lp_mode));
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function retrives the regulator output configuration.
+ *
+ * @param regulator The regulator to be truned off.
+ * @param config Pointer to regulator configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_regulator_get_config(t_pmic_regulator regulator,
+ t_regulator_config * config)
+{
+ if (config == NULL) {
+ return PMIC_ERROR;
+ }
+
+ switch (regulator) {
+ case SW_SW1A:
+ case SW_SW1B:
+ case SW_SW2A:
+ case SW_SW2B:
+ CHECK_ERROR(pmic_power_regulator_get_voltage
+ (regulator, &config->voltage));
+ CHECK_ERROR(pmic_power_switcher_get_dvs
+ (regulator, &config->voltage_lvs));
+ CHECK_ERROR(pmic_power_switcher_get_stby
+ (regulator, &config->voltage_stby));
+ CHECK_ERROR(pmic_power_switcher_get_mode
+ (regulator, &config->mode, false));
+ CHECK_ERROR(pmic_power_switcher_get_mode
+ (regulator, &config->stby_mode, true));
+ CHECK_ERROR(pmic_power_switcher_get_dvs_speed
+ (regulator, &config->dvs_speed));
+ CHECK_ERROR(pmic_power_switcher_get_panic_mode
+ (regulator, &config->panic_mode));
+ CHECK_ERROR(pmic_power_switcher_get_softstart
+ (regulator, &config->softstart));
+ break;
+ case SW_PLL:
+ CHECK_ERROR(pmic_power_switcher_get_factor
+ (regulator, &config->factor));
+ break;
+ case SW_SW3:
+ case REGU_VIOLO:
+ case REGU_VDIG:
+ case REGU_VGEN:
+ case REGU_VRFDIG:
+ case REGU_VRFREF:
+ case REGU_VRFCP:
+ case REGU_VSIM:
+ case REGU_VESIM:
+ case REGU_VCAM:
+ case REGU_VRF1:
+ case REGU_VRF2:
+ case REGU_VMMC1:
+ case REGU_VMMC2:
+ CHECK_ERROR(pmic_power_regulator_get_voltage
+ (regulator, &config->voltage));
+ CHECK_ERROR(pmic_power_regulator_get_lp_mode
+ (regulator, &config->lp_mode));
+ break;
+ case REGU_VVIB:
+ CHECK_ERROR(pmic_power_regulator_get_voltage
+ (regulator, &config->voltage));
+ break;
+ case REGU_VAUDIO:
+ case REGU_VIOHI:
+ case REGU_VRFBG:
+ CHECK_ERROR(pmic_power_regulator_get_lp_mode
+ (regulator, &config->lp_mode));
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables automatically VBKUP2 in the memory hold modes.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param en if true, enable VBKUP2AUTOMH
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_vbkup2_auto_en(bool en)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ pmic_version_t mc13783_ver;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ reg_val = BITFVAL(MC13783_REGCTRL_VBKUP2AUTOMH, en);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VBKUP2AUTOMH);
+
+ CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0,
+ reg_val, reg_mask));
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function gets state of automatically VBKUP2.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param en if true, VBKUP2AUTOMH is enabled
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_vbkup2_auto_state(bool * en)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ pmic_version_t mc13783_ver;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ reg_mask = BITFMASK(MC13783_REGCTRL_VBKUP2AUTOMH);
+ CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_0,
+ &reg_val, reg_mask));
+ *en = BITFEXT(reg_val, MC13783_REGCTRL_VBKUP2AUTOMH);
+
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function enables battery detect function.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param en if true, enable BATTDETEN
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_bat_det_en(bool en)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ pmic_version_t mc13783_ver;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ reg_val = BITFVAL(MC13783_REGCTRL_BATTDETEN, en);
+ reg_mask = BITFMASK(MC13783_REGCTRL_BATTDETEN);
+
+ CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0,
+ reg_val, reg_mask));
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function gets state of battery detect function.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param en if true, BATTDETEN is enabled
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_bat_det_state(bool * en)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ pmic_version_t mc13783_ver;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ reg_mask = BITFMASK(MC13783_REGCTRL_BATTDETEN);
+
+ CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_0,
+ &reg_val, reg_mask));
+ *en = BITFEXT(reg_val, MC13783_REGCTRL_BATTDETEN);
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function enables control of VVIB by VIBEN pin.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param en if true, enable VIBPINCTRL
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_vib_pin_en(bool en)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ pmic_version_t mc13783_ver;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ reg_val = BITFVAL(MC13783_REGCTRL_VIBPINCTRL, en);
+ reg_mask = BITFMASK(MC13783_REGCTRL_VIBPINCTRL);
+
+ CHECK_ERROR(pmic_write_reg(REG_POWER_MISCELLANEOUS,
+ reg_val, reg_mask));
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function gets state of control of VVIB by VIBEN pin.
+ * Only on mc13783 2.0 or higher
+ * @param en if true, VIBPINCTRL is enabled
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_gets_vib_pin_state(bool * en)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ pmic_version_t mc13783_ver;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ reg_mask = BITFMASK(MC13783_REGCTRL_VIBPINCTRL);
+ CHECK_ERROR(pmic_read_reg(REG_POWER_MISCELLANEOUS,
+ &reg_val, reg_mask));
+ *en = BITFEXT(reg_val, MC13783_REGCTRL_VIBPINCTRL);
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function returns power up sense value
+ *
+ * @param p_up_sense value of power up sense
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_power_get_power_mode_sense(struct t_p_up_sense * p_up_sense)
+{
+ unsigned int reg_value = 0;
+ CHECK_ERROR(pmic_read_reg(REG_POWER_UP_MODE_SENSE,
+ &reg_value, PMIC_ALL_BITS));
+ p_up_sense->state_ictest = (STATE_ICTEST_MASK & reg_value);
+ p_up_sense->state_clksel = ((STATE_CLKSEL_MASK & reg_value)
+ >> STATE_CLKSEL_BIT);
+ p_up_sense->state_pums1 = ((STATE_PUMS1_MASK & reg_value)
+ >> STATE_PUMS1_BITS);
+ p_up_sense->state_pums2 = ((STATE_PUMS2_MASK & reg_value)
+ >> STATE_PUMS2_BITS);
+ p_up_sense->state_pums3 = ((STATE_PUMS3_MASK & reg_value)
+ >> STATE_PUMS3_BITS);
+ p_up_sense->state_chrgmode0 = ((STATE_CHRGM1_MASK & reg_value)
+ >> STATE_CHRGM1_BITS);
+ p_up_sense->state_chrgmode1 = ((STATE_CHRGM2_MASK & reg_value)
+ >> STATE_CHRGM2_BITS);
+ p_up_sense->state_umod = ((STATE_UMOD_MASK & reg_value)
+ >> STATE_UMOD_BITS);
+ p_up_sense->state_usben = ((STATE_USBEN_MASK & reg_value)
+ >> STATE_USBEN_BIT);
+ p_up_sense->state_sw_1a1b_joined = ((STATE_SW1A_J_B_MASK & reg_value)
+ >> STATE_SW1A_J_B_BIT);
+ p_up_sense->state_sw_2a2b_joined = ((STATE_SW2A_J_B_MASK & reg_value)
+ >> STATE_SW2A_J_B_BIT);
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function configures the Regen assignment for all regulator
+ *
+ * @param regulator type of regulator
+ * @param en_dis if true, the regulator is enabled by regen.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_regen_assig(t_pmic_regulator regulator, bool en_dis)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+
+ switch (regulator) {
+ case REGU_VAUDIO:
+ reg_val = BITFVAL(MC13783_REGGEN_VAUDIO, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VAUDIO);
+ break;
+ case REGU_VIOHI:
+ reg_val = BITFVAL(MC13783_REGGEN_VIOHI, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VIOHI);
+ break;
+ case REGU_VIOLO:
+ reg_val = BITFVAL(MC13783_REGGEN_VIOLO, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VIOLO);
+ break;
+ case REGU_VDIG:
+ reg_val = BITFVAL(MC13783_REGGEN_VDIG, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VDIG);
+ break;
+ case REGU_VGEN:
+ reg_val = BITFVAL(MC13783_REGGEN_VGEN, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VGEN);
+ break;
+ case REGU_VRFDIG:
+ reg_val = BITFVAL(MC13783_REGGEN_VRFDIG, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VRFDIG);
+ break;
+ case REGU_VRFREF:
+ reg_val = BITFVAL(MC13783_REGGEN_VRFREF, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VRFREF);
+ break;
+ case REGU_VRFCP:
+ reg_val = BITFVAL(MC13783_REGGEN_VRFCP, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VRFCP);
+ break;
+ case REGU_VCAM:
+ reg_val = BITFVAL(MC13783_REGGEN_VCAM, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VCAM);
+ break;
+ case REGU_VRFBG:
+ reg_val = BITFVAL(MC13783_REGGEN_VRFBG, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VRFBG);
+ break;
+ case REGU_VRF1:
+ reg_val = BITFVAL(MC13783_REGGEN_VRF1, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VRF1);
+ break;
+ case REGU_VRF2:
+ reg_val = BITFVAL(MC13783_REGGEN_VRF2, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VRF2);
+ break;
+ case REGU_VMMC1:
+ reg_val = BITFVAL(MC13783_REGGEN_VMMC1, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VMMC1);
+ break;
+ case REGU_VMMC2:
+ reg_val = BITFVAL(MC13783_REGGEN_VMMC2, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_VMMC2);
+ break;
+ case REGU_GPO1:
+ reg_val = BITFVAL(MC13783_REGGEN_GPO1, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_GPO1);
+ break;
+ case REGU_GPO2:
+ reg_val = BITFVAL(MC13783_REGGEN_GPO2, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_GPO2);
+ break;
+ case REGU_GPO3:
+ reg_val = BITFVAL(MC13783_REGGEN_GPO3, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_GPO3);
+ break;
+ case REGU_GPO4:
+ reg_val = BITFVAL(MC13783_REGGEN_GPO4, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_GPO4);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(REG_REGEN_ASSIGNMENT, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the Regen assignment for all regulator
+ *
+ * @param regulator type of regulator
+ * @param en_dis return value, if true :
+ * the regulator is enabled by regen.
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_regen_assig(t_pmic_regulator regulator,
+ bool * en_dis)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+
+ switch (regulator) {
+ case REGU_VAUDIO:
+ reg_mask = BITFMASK(MC13783_REGGEN_VAUDIO);
+ break;
+ case REGU_VIOHI:
+ reg_mask = BITFMASK(MC13783_REGGEN_VIOHI);
+ break;
+ case REGU_VIOLO:
+ reg_mask = BITFMASK(MC13783_REGGEN_VIOLO);
+ break;
+ case REGU_VDIG:
+ reg_mask = BITFMASK(MC13783_REGGEN_VDIG);
+ break;
+ case REGU_VGEN:
+ reg_mask = BITFMASK(MC13783_REGGEN_VGEN);
+ break;
+ case REGU_VRFDIG:
+ reg_mask = BITFMASK(MC13783_REGGEN_VRFDIG);
+ break;
+ case REGU_VRFREF:
+ reg_mask = BITFMASK(MC13783_REGGEN_VRFREF);
+ break;
+ case REGU_VRFCP:
+ reg_mask = BITFMASK(MC13783_REGGEN_VRFCP);
+ break;
+ case REGU_VCAM:
+ reg_mask = BITFMASK(MC13783_REGGEN_VCAM);
+ break;
+ case REGU_VRFBG:
+ reg_mask = BITFMASK(MC13783_REGGEN_VRFBG);
+ break;
+ case REGU_VRF1:
+ reg_mask = BITFMASK(MC13783_REGGEN_VRF1);
+ break;
+ case REGU_VRF2:
+ reg_mask = BITFMASK(MC13783_REGGEN_VRF2);
+ break;
+ case REGU_VMMC1:
+ reg_mask = BITFMASK(MC13783_REGGEN_VMMC1);
+ break;
+ case REGU_VMMC2:
+ reg_mask = BITFMASK(MC13783_REGGEN_VMMC2);
+ break;
+ case REGU_GPO1:
+ reg_mask = BITFMASK(MC13783_REGGEN_GPO1);
+ break;
+ case REGU_GPO2:
+ reg_mask = BITFMASK(MC13783_REGGEN_GPO2);
+ break;
+ case REGU_GPO3:
+ reg_mask = BITFMASK(MC13783_REGGEN_GPO3);
+ break;
+ case REGU_GPO4:
+ reg_mask = BITFMASK(MC13783_REGGEN_GPO4);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(REG_REGEN_ASSIGNMENT, &reg_val, reg_mask));
+
+ switch (regulator) {
+ case REGU_VAUDIO:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VAUDIO);
+ break;
+ case REGU_VIOHI:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VIOHI);
+ break;
+ case REGU_VIOLO:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VIOLO);
+ break;
+ case REGU_VDIG:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VDIG);
+ break;
+ case REGU_VGEN:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VGEN);
+ break;
+ case REGU_VRFDIG:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRFDIG);
+ break;
+ case REGU_VRFREF:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRFREF);
+ break;
+ case REGU_VRFCP:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRFCP);
+ break;
+ case REGU_VCAM:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VCAM);
+ break;
+ case REGU_VRFBG:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRFBG);
+ break;
+ case REGU_VRF1:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRF1);
+ break;
+ case REGU_VRF2:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VRF2);
+ break;
+ case REGU_VMMC1:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VMMC1);
+ break;
+ case REGU_VMMC2:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_VMMC2);
+ break;
+ case REGU_GPO1:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_GPO1);
+ break;
+ case REGU_GPO2:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_GPO2);
+ break;
+ case REGU_GPO3:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_GPO3);
+ break;
+ case REGU_GPO4:
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_GPO4);
+ break;
+ default:
+ break;
+ }
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function sets the Regen polarity.
+ *
+ * @param en_dis If true regen is inverted.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_regen_inv(bool en_dis)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+
+ reg_val = BITFVAL(MC13783_REGGEN_INV, en_dis);
+ reg_mask = BITFMASK(MC13783_REGGEN_INV);
+
+ CHECK_ERROR(pmic_write_reg(REG_REGEN_ASSIGNMENT, reg_val, reg_mask));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets the Regen polarity.
+ *
+ * @param en_dis If true regen is inverted.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_regen_inv(bool * en_dis)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+
+ reg_mask = BITFMASK(MC13783_REGGEN_INV);
+ CHECK_ERROR(pmic_read_reg(REG_REGEN_ASSIGNMENT, &reg_val, reg_mask));
+ *en_dis = BITFEXT(reg_val, MC13783_REGGEN_INV);
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function enables esim control voltage.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param vesim if true, enable VESIMESIMEN
+ * @param vmmc1 if true, enable VMMC1ESIMEN
+ * @param vmmc2 if true, enable VMMC2ESIMEN
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_esim_v_en(bool vesim, bool vmmc1, bool vmmc2)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ pmic_version_t mc13783_ver;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ reg_val = BITFVAL(MC13783_REGGEN_VESIMESIM, vesim) |
+ BITFVAL(MC13783_REGGEN_VMMC1ESIM, vesim) |
+ BITFVAL(MC13783_REGGEN_VMMC2ESIM, vesim);
+ reg_mask = BITFMASK(MC13783_REGGEN_VESIMESIM) |
+ BITFMASK(MC13783_REGGEN_VMMC1ESIM) |
+ BITFMASK(MC13783_REGGEN_VMMC2ESIM);
+ CHECK_ERROR(pmic_write_reg(REG_REGEN_ASSIGNMENT,
+ reg_val, reg_mask));
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function gets esim control voltage values.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param vesim if true, enable VESIMESIMEN
+ * @param vmmc1 if true, enable VMMC1ESIMEN
+ * @param vmmc2 if true, enable VMMC2ESIMEN
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_gets_esim_v_state(bool * vesim, bool * vmmc1,
+ bool * vmmc2)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ pmic_version_t mc13783_ver;
+ mc13783_ver = pmic_get_version();
+ if (mc13783_ver.revision >= 20) {
+ reg_mask = BITFMASK(MC13783_REGGEN_VESIMESIM) |
+ BITFMASK(MC13783_REGGEN_VMMC1ESIM) |
+ BITFMASK(MC13783_REGGEN_VMMC2ESIM);
+ CHECK_ERROR(pmic_read_reg(REG_REGEN_ASSIGNMENT,
+ &reg_val, reg_mask));
+ *vesim = BITFEXT(reg_val, MC13783_REGGEN_VESIMESIM);
+ *vmmc1 = BITFEXT(reg_val, MC13783_REGGEN_VMMC1ESIM);
+ *vmmc2 = BITFEXT(reg_val, MC13783_REGGEN_VMMC2ESIM);
+ return PMIC_SUCCESS;
+ } else {
+ return PMIC_NOT_SUPPORTED;
+ }
+}
+
+/*!
+ * This function enables auto reset after a system reset.
+ *
+ * @param en if true, the auto reset is enabled
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_auto_reset_en(bool en)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+
+ reg_val = BITFVAL(MC13783_AUTO_RESTART, en);
+ reg_mask = BITFMASK(MC13783_AUTO_RESTART);
+
+ CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_2, reg_val, reg_mask));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets auto reset configuration.
+ *
+ * @param en if true, the auto reset is enabled
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_auto_reset_en(bool * en)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+
+ reg_mask = BITFMASK(MC13783_AUTO_RESTART);
+ CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_2, &reg_val, reg_mask));
+ *en = BITFEXT(reg_val, MC13783_AUTO_RESTART);
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function configures a system reset on a button.
+ *
+ * @param bt type of button.
+ * @param sys_rst if true, enable the system reset on this button
+ * @param deb_time sets the debounce time on this button pin
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_set_conf_button(t_button bt, bool sys_rst, int deb_time)
+{
+ int max_val = 0;
+ unsigned int reg_val = 0, reg_mask = 0;
+
+ max_val = (1 << MC13783_DEB_BT_ON1B_WID) - 1;
+ if (deb_time > max_val) {
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ switch (bt) {
+ case BT_ON1B:
+ reg_val = BITFVAL(MC13783_EN_BT_ON1B, sys_rst) |
+ BITFVAL(MC13783_DEB_BT_ON1B, deb_time);
+ reg_mask = BITFMASK(MC13783_EN_BT_ON1B) |
+ BITFMASK(MC13783_DEB_BT_ON1B);
+ break;
+ case BT_ON2B:
+ reg_val = BITFVAL(MC13783_EN_BT_ON2B, sys_rst) |
+ BITFVAL(MC13783_DEB_BT_ON2B, deb_time);
+ reg_mask = BITFMASK(MC13783_EN_BT_ON2B) |
+ BITFMASK(MC13783_DEB_BT_ON2B);
+ break;
+ case BT_ON3B:
+ reg_val = BITFVAL(MC13783_EN_BT_ON3B, sys_rst) |
+ BITFVAL(MC13783_DEB_BT_ON3B, deb_time);
+ reg_mask = BITFMASK(MC13783_EN_BT_ON3B) |
+ BITFMASK(MC13783_DEB_BT_ON3B);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_2, reg_val, reg_mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function gets configuration of a button.
+ *
+ * @param bt type of button.
+ * @param sys_rst if true, the system reset is enabled on this button
+ * @param deb_time gets the debounce time on this button pin
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_get_conf_button(t_button bt,
+ bool * sys_rst, int *deb_time)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+
+ switch (bt) {
+ case BT_ON1B:
+ reg_mask = BITFMASK(MC13783_EN_BT_ON1B) |
+ BITFMASK(MC13783_DEB_BT_ON1B);
+ break;
+ case BT_ON2B:
+ reg_mask = BITFMASK(MC13783_EN_BT_ON2B) |
+ BITFMASK(MC13783_DEB_BT_ON2B);
+ break;
+ case BT_ON3B:
+ reg_mask = BITFMASK(MC13783_EN_BT_ON3B) |
+ BITFMASK(MC13783_DEB_BT_ON3B);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(REG_POWER_CONTROL_2, &reg_val, reg_mask));
+
+ switch (bt) {
+ case BT_ON1B:
+ *sys_rst = BITFEXT(reg_val, MC13783_EN_BT_ON1B);
+ *deb_time = BITFEXT(reg_val, MC13783_DEB_BT_ON1B);
+ break;
+ case BT_ON2B:
+ *sys_rst = BITFEXT(reg_val, MC13783_EN_BT_ON2B);
+ *deb_time = BITFEXT(reg_val, MC13783_DEB_BT_ON2B);
+ break;
+ case BT_ON3B:
+ *sys_rst = BITFEXT(reg_val, MC13783_EN_BT_ON3B);
+ *deb_time = BITFEXT(reg_val, MC13783_DEB_BT_ON3B);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to un/subscribe on power event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ * @param sub define if Un/subscribe event.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_event(t_pwr_int event, void *callback, bool sub)
+{
+ pmic_event_callback_t power_callback;
+ type_event power_event;
+
+ power_callback.func = callback;
+ power_callback.param = NULL;
+ switch (event) {
+ case PWR_IT_BPONI:
+ power_event = EVENT_BPONI;
+ break;
+ case PWR_IT_LOBATLI:
+ power_event = EVENT_LOBATLI;
+ break;
+ case PWR_IT_LOBATHI:
+ power_event = EVENT_LOBATHI;
+ break;
+ case PWR_IT_ONOFD1I:
+ power_event = EVENT_ONOFD1I;
+ break;
+ case PWR_IT_ONOFD2I:
+ power_event = EVENT_ONOFD2I;
+ break;
+ case PWR_IT_ONOFD3I:
+ power_event = EVENT_ONOFD3I;
+ break;
+ case PWR_IT_SYSRSTI:
+ power_event = EVENT_SYSRSTI;
+ break;
+ case PWR_IT_PWRRDYI:
+ power_event = EVENT_PWRRDYI;
+ break;
+ case PWR_IT_PCI:
+ power_event = EVENT_PCI;
+ break;
+ case PWR_IT_WARMI:
+ power_event = EVENT_WARMI;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ if (sub == true) {
+ CHECK_ERROR(pmic_event_subscribe(power_event, power_callback));
+ } else {
+ CHECK_ERROR(pmic_event_unsubscribe
+ (power_event, power_callback));
+ }
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to subscribe on power event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_event_sub(t_pwr_int event, void *callback)
+{
+ return pmic_power_event(event, callback, true);
+}
+
+/*!
+ * This function is used to un subscribe on power event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_power_event_unsub(t_pwr_int event, void *callback)
+{
+ return pmic_power_event(event, callback, false);
+}
+
+void pmic_power_key_callback(void)
+{
+#ifdef CONFIG_MXC_HWEVENT
+ /*read the power key is pressed or up */
+ t_sensor_bits sense;
+ struct mxc_hw_event event = { HWE_POWER_KEY, 0 };
+
+ pmic_get_sensors(&sense);
+ if (sense.sense_onofd1s) {
+ pr_debug("PMIC Power key up\n");
+ event.args = PWRK_UNPRESS;
+ } else {
+ pr_debug("PMIC Power key pressed\n");
+ event.args = PWRK_PRESS;
+ }
+ /* send hw event */
+ hw_event_send(HWE_DEF_PRIORITY, &event);
+#endif
+}
+
+static irqreturn_t power_key_int(int irq, void *dev_id)
+{
+ pr_info(KERN_INFO "on-off key pressed\n");
+
+ return 0;
+}
+
+extern void gpio_power_key_active(void);
+
+/*
+ * Init and Exit
+ */
+
+static int pmic_power_probe(struct platform_device *pdev)
+{
+ int irq, ret;
+ struct pmic_platform_data *ppd;
+
+ /* configure on/off button */
+ gpio_power_key_active();
+
+ ppd = pdev->dev.platform_data;
+ if (ppd)
+ irq = ppd->power_key_irq;
+ else
+ goto done;
+
+ if (irq == 0) {
+ pr_info(KERN_INFO "PMIC Power has no platform data\n");
+ goto done;
+ }
+ set_irq_type(irq, IRQF_TRIGGER_RISING);
+
+ ret = request_irq(irq, power_key_int, 0, "power_key", 0);
+ if (ret)
+ pr_info(KERN_ERR "register on-off key interrupt failed\n");
+
+ set_irq_wake(irq, 1);
+
+ done:
+ pr_info(KERN_INFO "PMIC Power successfully probed\n");
+ return 0;
+}
+
+static struct platform_driver pmic_power_driver_ldm = {
+ .driver = {
+ .name = "pmic_power",
+ },
+ .suspend = pmic_power_suspend,
+ .resume = pmic_power_resume,
+ .probe = pmic_power_probe,
+ .remove = NULL,
+};
+
+static int __init pmic_power_init(void)
+{
+ pr_debug("PMIC Power driver loading..\n");
+ pmic_power_event_sub(PWR_IT_ONOFD1I, pmic_power_key_callback);
+ /* set power off hook to mc13783 power off */
+ pm_power_off = pmic_power_off;
+ return platform_driver_register(&pmic_power_driver_ldm);
+}
+static void __exit pmic_power_exit(void)
+{
+ pmic_power_event_unsub(PWR_IT_ONOFD1I, pmic_power_key_callback);
+ platform_driver_unregister(&pmic_power_driver_ldm);
+ pr_debug("PMIC Power driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall_sync(pmic_power_init);
+module_exit(pmic_power_exit);
+
+MODULE_DESCRIPTION("pmic_power driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_power_defs.h b/drivers/mxc/pmic/mc13783/pmic_power_defs.h
new file mode 100644
index 000000000000..38e554146a70
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_power_defs.h
@@ -0,0 +1,509 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_power_defs.h
+ * @brief This is the internal header define of PMIC(mc13783) Power driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+
+#ifndef __MC13783_POWER_DEFS_H__
+#define __MC13783_POWER_DEFS_H__
+
+/*
+ * Power Up Mode Sense bits
+ */
+
+#define STATE_ICTEST_MASK 0x000001
+
+#define STATE_CLKSEL_BIT 1
+#define STATE_CLKSEL_MASK 0x000002
+
+#define STATE_PUMS1_BITS 2
+#define STATE_PUMS1_MASK 0x00000C
+
+#define STATE_PUMS2_BITS 4
+#define STATE_PUMS2_MASK 0x000030
+
+#define STATE_PUMS3_BITS 6
+#define STATE_PUMS3_MASK 0x0000C0
+
+#define STATE_CHRGM1_BITS 8
+#define STATE_CHRGM1_MASK 0x000300
+
+#define STATE_CHRGM2_BITS 10
+#define STATE_CHRGM2_MASK 0x000C00
+
+#define STATE_UMOD_BITS 12
+#define STATE_UMOD_MASK 0x003000
+
+#define STATE_USBEN_BIT 14
+#define STATE_USBEN_MASK 0x004000
+
+#define STATE_SW1A_J_B_BIT 15
+#define STATE_SW1A_J_B_MASK 0x008000
+
+#define STATE_SW2A_J_B_BIT 16
+#define STATE_SW2A_J_B_MASK 0x010000
+
+#define PC_COUNT_MAX 3
+#define PC_COUNT_MIN 0
+/*
+ * Reg Regen
+ */
+#define MC13783_REGGEN_VAUDIO_LSH 0
+#define MC13783_REGGEN_VAUDIO_WID 1
+#define MC13783_REGGEN_VIOHI_LSH 1
+#define MC13783_REGGEN_VIOHI_WID 1
+#define MC13783_REGGEN_VIOLO_LSH 2
+#define MC13783_REGGEN_VIOLO_WID 1
+#define MC13783_REGGEN_VDIG_LSH 3
+#define MC13783_REGGEN_VDIG_WID 1
+#define MC13783_REGGEN_VGEN_LSH 4
+#define MC13783_REGGEN_VGEN_WID 1
+#define MC13783_REGGEN_VRFDIG_LSH 5
+#define MC13783_REGGEN_VRFDIG_WID 1
+#define MC13783_REGGEN_VRFREF_LSH 6
+#define MC13783_REGGEN_VRFREF_WID 1
+#define MC13783_REGGEN_VRFCP_LSH 7
+#define MC13783_REGGEN_VRFCP_WID 1
+#define MC13783_REGGEN_VCAM_LSH 8
+#define MC13783_REGGEN_VCAM_WID 1
+#define MC13783_REGGEN_VRFBG_LSH 9
+#define MC13783_REGGEN_VRFBG_WID 1
+#define MC13783_REGGEN_VRF1_LSH 10
+#define MC13783_REGGEN_VRF1_WID 1
+#define MC13783_REGGEN_VRF2_LSH 11
+#define MC13783_REGGEN_VRF2_WID 1
+#define MC13783_REGGEN_VMMC1_LSH 12
+#define MC13783_REGGEN_VMMC1_WID 1
+#define MC13783_REGGEN_VMMC2_LSH 13
+#define MC13783_REGGEN_VMMC2_WID 1
+#define MC13783_REGGEN_GPO1_LSH 16
+#define MC13783_REGGEN_GPO1_WID 1
+#define MC13783_REGGEN_GPO2_LSH 17
+#define MC13783_REGGEN_GPO2_WID 1
+#define MC13783_REGGEN_GPO3_LSH 18
+#define MC13783_REGGEN_GPO3_WID 1
+#define MC13783_REGGEN_GPO4_LSH 19
+#define MC13783_REGGEN_GPO4_WID 1
+#define MC13783_REGGEN_INV_LSH 20
+#define MC13783_REGGEN_INV_WID 1
+#define MC13783_REGGEN_VESIMESIM_LSH 21
+#define MC13783_REGGEN_VESIMESIM_WID 1
+#define MC13783_REGGEN_VMMC1ESIM_LSH 22
+#define MC13783_REGGEN_VMMC1ESIM_WID 1
+#define MC13783_REGGEN_VMMC2ESIM_LSH 23
+#define MC13783_REGGEN_VMMC2ESIM_WID 1
+
+/*
+ * Reg Power Control 0
+ */
+#define MC13783_PWRCTRL_PCEN_LSH 0
+#define MC13783_PWRCTRL_PCEN_WID 1
+#define MC13783_PWRCTRL_PCEN_ENABLE 1
+#define MC13783_PWRCTRL_PCEN_DISABLE 0
+#define MC13783_PWRCTRL_PC_COUNT_EN_LSH 1
+#define MC13783_PWRCTRL_PC_COUNT_EN_WID 1
+#define MC13783_PWRCTRL_PC_COUNT_EN_ENABLE 1
+#define MC13783_PWRCTRL_PC_COUNT_EN_DISABLE 0
+#define MC13783_PWRCTRL_WARM_EN_LSH 2
+#define MC13783_PWRCTRL_WARM_EN_WID 1
+#define MC13783_PWRCTRL_WARM_EN_ENABLE 1
+#define MC13783_PWRCTRL_WARM_EN_DISABLE 0
+#define MC13783_PWRCTRL_USER_OFF_SPI_LSH 3
+#define MC13783_PWRCTRL_USER_OFF_SPI_WID 1
+#define MC13783_PWRCTRL_USER_OFF_SPI_ENABLE 1
+#define MC13783_PWRCTRL_USER_OFF_PC_LSH 4
+#define MC13783_PWRCTRL_USER_OFF_PC_WID 1
+#define MC13783_PWRCTRL_USER_OFF_PC_ENABLE 1
+#define MC13783_PWRCTRL_USER_OFF_PC_DISABLE 0
+#define MC13783_PWRCTRL_32OUT_USER_OFF_LSH 5
+#define MC13783_PWRCTRL_32OUT_USER_OFF_WID 1
+#define MC13783_PWRCTRL_32OUT_USER_OFF_ENABLE 1
+#define MC13783_PWRCTRL_32OUT_USER_OFF_DISABLE 0
+#define MC13783_PWRCTRL_32OUT_EN_LSH 6
+#define MC13783_PWRCTRL_32OUT_EN_WID 1
+#define MC13783_PWRCTRL_32OUT_EN_ENABLE 1
+#define MC13783_PWRCTRL_32OUT_EN_DISABLE 0
+#define MC13783_REGCTRL_VBKUP2AUTOMH_LSH 7
+#define MC13783_REGCTRL_VBKUP2AUTOMH_WID 1
+#define MC13783_PWRCTRL_VBKUP1_EN_LSH 8
+#define MC13783_PWRCTRL_VBKUP1_EN_WID 1
+#define MC13783_PWRCTRL_VBKUP_ENABLE 1
+#define MC13783_PWRCTRL_VBKUP_DISABLE 0
+#define MC13783_PWRCTRL_VBKUP1_AUTO_EN_LSH 9
+#define MC13783_PWRCTRL_VBKUP1_AUTO_EN_WID 1
+#define MC13783_PWRCTRL_VBKUP1_LSH 10
+#define MC13783_PWRCTRL_VBKUP1_WID 2
+#define MC13783_PWRCTRL_VBKUP2_EN_LSH 12
+#define MC13783_PWRCTRL_VBKUP2_EN_WID 1
+#define MC13783_PWRCTRL_VBKUP2_AUTO_EN_LSH 13
+#define MC13783_PWRCTRL_VBKUP2_AUTO_EN_WID 1
+#define MC13783_PWRCTRL_VBKUP2_LSH 14
+#define MC13783_PWRCTRL_VBKUP2_WID 2
+#define MC13783_REGCTRL_BATTDETEN_LSH 19
+#define MC13783_REGCTRL_BATTDETEN_WID 1
+
+/*
+ * Reg Power Control 1
+ */
+#define MC13783_PWRCTRL_PCT_LSH 0
+#define MC13783_PWRCTRL_PCT_WID 8
+#define MC13783_PWRCTRL_PC_COUNT_LSH 8
+#define MC13783_PWRCTRL_PC_COUNT_WID 4
+#define MC13783_PWRCTRL_PC_MAX_CNT_LSH 12
+#define MC13783_PWRCTRL_PC_MAX_CNT_WID 4
+#define MC13783_PWRCTRL_MEM_TMR_LSH 16
+#define MC13783_PWRCTRL_MEM_TMR_WID 4
+#define MC13783_PWRCTRL_MEM_ALLON_LSH 20
+#define MC13783_PWRCTRL_MEM_ALLON_WID 1
+#define MC13783_PWRCTRL_MEM_ALLON_ENABLE 1
+#define MC13783_PWRCTRL_MEM_ALLON_DISABLE 0
+
+/*
+ * Reg Power Control 2
+ */
+#define MC13783_AUTO_RESTART_LSH 0
+#define MC13783_AUTO_RESTART_WID 1
+#define MC13783_EN_BT_ON1B_LSH 1
+#define MC13783_EN_BT_ON1B_WID 1
+#define MC13783_EN_BT_ON2B_LSH 2
+#define MC13783_EN_BT_ON2B_WID 1
+#define MC13783_EN_BT_ON3B_LSH 3
+#define MC13783_EN_BT_ON3B_WID 1
+#define MC13783_DEB_BT_ON1B_LSH 4
+#define MC13783_DEB_BT_ON1B_WID 2
+#define MC13783_DEB_BT_ON2B_LSH 6
+#define MC13783_DEB_BT_ON2B_WID 2
+#define MC13783_DEB_BT_ON3B_LSH 8
+#define MC13783_DEB_BT_ON3B_WID 2
+
+/*
+ * Reg Regulator Mode 0
+ */
+#define MC13783_REGCTRL_VAUDIO_EN_LSH 0
+#define MC13783_REGCTRL_VAUDIO_EN_WID 1
+#define MC13783_REGCTRL_VAUDIO_EN_ENABLE 1
+#define MC13783_REGCTRL_VAUDIO_EN_DISABLE 0
+#define MC13783_REGCTRL_VAUDIO_STBY_LSH 1
+#define MC13783_REGCTRL_VAUDIO_STBY_WID 1
+#define MC13783_REGCTRL_VAUDIO_MODE_LSH 2
+#define MC13783_REGCTRL_VAUDIO_MODE_WID 1
+#define MC13783_REGCTRL_VIOHI_EN_LSH 3
+#define MC13783_REGCTRL_VIOHI_EN_WID 1
+#define MC13783_REGCTRL_VIOHI_EN_ENABLE 1
+#define MC13783_REGCTRL_VIOHI_EN_DISABLE 0
+#define MC13783_REGCTRL_VIOHI_STBY_LSH 4
+#define MC13783_REGCTRL_VIOHI_STBY_WID 1
+#define MC13783_REGCTRL_VIOHI_MODE_LSH 5
+#define MC13783_REGCTRL_VIOHI_MODE_WID 1
+#define MC13783_REGCTRL_VIOLO_EN_LSH 6
+#define MC13783_REGCTRL_VIOLO_EN_WID 1
+#define MC13783_REGCTRL_VIOLO_EN_ENABLE 1
+#define MC13783_REGCTRL_VIOLO_EN_DISABLE 0
+#define MC13783_REGCTRL_VIOLO_STBY_LSH 7
+#define MC13783_REGCTRL_VIOLO_STBY_WID 1
+#define MC13783_REGCTRL_VIOLO_MODE_LSH 8
+#define MC13783_REGCTRL_VIOLO_MODE_WID 1
+#define MC13783_REGCTRL_VDIG_EN_LSH 9
+#define MC13783_REGCTRL_VDIG_EN_WID 1
+#define MC13783_REGCTRL_VDIG_EN_ENABLE 1
+#define MC13783_REGCTRL_VDIG_EN_DISABLE 0
+#define MC13783_REGCTRL_VDIG_STBY_LSH 10
+#define MC13783_REGCTRL_VDIG_STBY_WID 1
+#define MC13783_REGCTRL_VDIG_MODE_LSH 11
+#define MC13783_REGCTRL_VDIG_MODE_WID 1
+#define MC13783_REGCTRL_VGEN_EN_LSH 12
+#define MC13783_REGCTRL_VGEN_EN_WID 1
+#define MC13783_REGCTRL_VGEN_EN_ENABLE 1
+#define MC13783_REGCTRL_VGEN_EN_DISABLE 0
+#define MC13783_REGCTRL_VGEN_STBY_LSH 13
+#define MC13783_REGCTRL_VGEN_STBY_WID 1
+#define MC13783_REGCTRL_VGEN_MODE_LSH 14
+#define MC13783_REGCTRL_VGEN_MODE_WID 1
+#define MC13783_REGCTRL_VRFDIG_EN_LSH 15
+#define MC13783_REGCTRL_VRFDIG_EN_WID 1
+#define MC13783_REGCTRL_VRFDIG_EN_ENABLE 1
+#define MC13783_REGCTRL_VRFDIG_EN_DISABLE 0
+#define MC13783_REGCTRL_VRFDIG_STBY_LSH 16
+#define MC13783_REGCTRL_VRFDIG_STBY_WID 1
+#define MC13783_REGCTRL_VRFDIG_MODE_LSH 17
+#define MC13783_REGCTRL_VRFDIG_MODE_WID 1
+#define MC13783_REGCTRL_VRFREF_EN_LSH 18
+#define MC13783_REGCTRL_VRFREF_EN_WID 1
+#define MC13783_REGCTRL_VRFREF_EN_ENABLE 1
+#define MC13783_REGCTRL_VRFREF_EN_DISABLE 0
+#define MC13783_REGCTRL_VRFREF_STBY_LSH 19
+#define MC13783_REGCTRL_VRFREF_STBY_WID 1
+#define MC13783_REGCTRL_VRFREF_MODE_LSH 20
+#define MC13783_REGCTRL_VRFREF_MODE_WID 1
+#define MC13783_REGCTRL_VRFCP_EN_LSH 21
+#define MC13783_REGCTRL_VRFCP_EN_WID 1
+#define MC13783_REGCTRL_VRFCP_EN_ENABLE 1
+#define MC13783_REGCTRL_VRFCP_EN_DISABLE 0
+#define MC13783_REGCTRL_VRFCP_STBY_LSH 22
+#define MC13783_REGCTRL_VRFCP_STBY_WID 1
+#define MC13783_REGCTRL_VRFCP_MODE_LSH 23
+#define MC13783_REGCTRL_VRFCP_MODE_WID 1
+
+/*
+ * Reg Regulator Mode 1
+ */
+#define MC13783_REGCTRL_VSIM_EN_LSH 0
+#define MC13783_REGCTRL_VSIM_EN_WID 1
+#define MC13783_REGCTRL_VSIM_EN_ENABLE 1
+#define MC13783_REGCTRL_VSIM_EN_DISABLE 0
+#define MC13783_REGCTRL_VSIM_STBY_LSH 1
+#define MC13783_REGCTRL_VSIM_STBY_WID 1
+#define MC13783_REGCTRL_VSIM_MODE_LSH 2
+#define MC13783_REGCTRL_VSIM_MODE_WID 1
+#define MC13783_REGCTRL_VESIM_EN_LSH 3
+#define MC13783_REGCTRL_VESIM_EN_WID 1
+#define MC13783_REGCTRL_VESIM_EN_ENABLE 1
+#define MC13783_REGCTRL_VESIM_EN_DISABLE 0
+#define MC13783_REGCTRL_VESIM_STBY_LSH 4
+#define MC13783_REGCTRL_VESIM_STBY_WID 1
+#define MC13783_REGCTRL_VESIM_MODE_LSH 5
+#define MC13783_REGCTRL_VESIM_MODE_WID 1
+#define MC13783_REGCTRL_VCAM_EN_LSH 6
+#define MC13783_REGCTRL_VCAM_EN_WID 1
+#define MC13783_REGCTRL_VCAM_EN_ENABLE 1
+#define MC13783_REGCTRL_VCAM_EN_DISABLE 0
+#define MC13783_REGCTRL_VCAM_STBY_LSH 7
+#define MC13783_REGCTRL_VCAM_STBY_WID 1
+#define MC13783_REGCTRL_VCAM_MODE_LSH 8
+#define MC13783_REGCTRL_VCAM_MODE_WID 1
+#define MC13783_REGCTRL_VRFBG_EN_LSH 9
+#define MC13783_REGCTRL_VRFBG_EN_WID 1
+#define MC13783_REGCTRL_VRFBG_EN_ENABLE 1
+#define MC13783_REGCTRL_VRFBG_EN_DISABLE 0
+#define MC13783_REGCTRL_VRFBG_STBY_LSH 10
+#define MC13783_REGCTRL_VRFBG_STBY_WID 1
+#define MC13783_REGCTRL_VVIB_EN_LSH 11
+#define MC13783_REGCTRL_VVIB_EN_WID 1
+#define MC13783_REGCTRL_VVIB_EN_ENABLE 1
+#define MC13783_REGCTRL_VVIB_EN_DISABLE 0
+#define MC13783_REGCTRL_VRF1_EN_LSH 12
+#define MC13783_REGCTRL_VRF1_EN_WID 1
+#define MC13783_REGCTRL_VRF1_EN_ENABLE 1
+#define MC13783_REGCTRL_VRF1_EN_DISABLE 0
+#define MC13783_REGCTRL_VRF1_STBY_LSH 13
+#define MC13783_REGCTRL_VRF1_STBY_WID 1
+#define MC13783_REGCTRL_VRF1_MODE_LSH 14
+#define MC13783_REGCTRL_VRF1_MODE_WID 1
+#define MC13783_REGCTRL_VRF2_EN_LSH 15
+#define MC13783_REGCTRL_VRF2_EN_WID 1
+#define MC13783_REGCTRL_VRF2_EN_ENABLE 1
+#define MC13783_REGCTRL_VRF2_EN_DISABLE 0
+#define MC13783_REGCTRL_VRF2_STBY_LSH 16
+#define MC13783_REGCTRL_VRF2_STBY_WID 1
+#define MC13783_REGCTRL_VRF2_MODE_LSH 17
+#define MC13783_REGCTRL_VRF2_MODE_WID 1
+#define MC13783_REGCTRL_VMMC1_EN_LSH 18
+#define MC13783_REGCTRL_VMMC1_EN_WID 1
+#define MC13783_REGCTRL_VMMC1_EN_ENABLE 1
+#define MC13783_REGCTRL_VMMC1_EN_DISABLE 0
+#define MC13783_REGCTRL_VMMC1_STBY_LSH 19
+#define MC13783_REGCTRL_VMMC1_STBY_WID 1
+#define MC13783_REGCTRL_VMMC1_MODE_LSH 20
+#define MC13783_REGCTRL_VMMC1_MODE_WID 1
+#define MC13783_REGCTRL_VMMC2_EN_LSH 21
+#define MC13783_REGCTRL_VMMC2_EN_WID 1
+#define MC13783_REGCTRL_VMMC2_EN_ENABLE 1
+#define MC13783_REGCTRL_VMMC2_EN_DISABLE 0
+#define MC13783_REGCTRL_VMMC2_STBY_LSH 22
+#define MC13783_REGCTRL_VMMC2_STBY_WID 1
+#define MC13783_REGCTRL_VMMC2_MODE_LSH 23
+#define MC13783_REGCTRL_VMMC2_MODE_WID 1
+
+/*
+ * Reg Regulator Misc.
+ */
+#define MC13783_REGCTRL_GPO1_EN_LSH 6
+#define MC13783_REGCTRL_GPO1_EN_WID 1
+#define MC13783_REGCTRL_GPO1_EN_ENABLE 1
+#define MC13783_REGCTRL_GPO1_EN_DISABLE 0
+#define MC13783_REGCTRL_GPO2_EN_LSH 8
+#define MC13783_REGCTRL_GPO2_EN_WID 1
+#define MC13783_REGCTRL_GPO2_EN_ENABLE 1
+#define MC13783_REGCTRL_GPO2_EN_DISABLE 0
+#define MC13783_REGCTRL_GPO3_EN_LSH 10
+#define MC13783_REGCTRL_GPO3_EN_WID 1
+#define MC13783_REGCTRL_GPO3_EN_ENABLE 1
+#define MC13783_REGCTRL_GPO3_EN_DISABLE 0
+#define MC13783_REGCTRL_GPO4_EN_LSH 12
+#define MC13783_REGCTRL_GPO4_EN_WID 1
+#define MC13783_REGCTRL_GPO4_EN_ENABLE 1
+#define MC13783_REGCTRL_GPO4_EN_DISABLE 0
+#define MC13783_REGCTRL_VIBPINCTRL_LSH 14
+#define MC13783_REGCTRL_VIBPINCTRL_WID 1
+
+/*
+ * Reg Regulator Setting 0
+ */
+#define MC13783_REGSET_VIOLO_LSH 2
+#define MC13783_REGSET_VIOLO_WID 2
+#define MC13783_REGSET_VDIG_LSH 4
+#define MC13783_REGSET_VDIG_WID 2
+#define MC13783_REGSET_VGEN_LSH 6
+#define MC13783_REGSET_VGEN_WID 3
+#define MC13783_REGSET_VRFDIG_LSH 9
+#define MC13783_REGSET_VRFDIG_WID 2
+#define MC13783_REGSET_VRFREF_LSH 11
+#define MC13783_REGSET_VRFREF_WID 2
+#define MC13783_REGSET_VRFCP_LSH 13
+#define MC13783_REGSET_VRFCP_WID 1
+#define MC13783_REGSET_VSIM_LSH 14
+#define MC13783_REGSET_VSIM_WID 1
+#define MC13783_REGSET_VESIM_LSH 15
+#define MC13783_REGSET_VESIM_WID 1
+#define MC13783_REGSET_VCAM_LSH 16
+#define MC13783_REGSET_VCAM_WID 3
+
+/*
+ * Reg Regulator Setting 1
+ */
+#define MC13783_REGSET_VVIB_LSH 0
+#define MC13783_REGSET_VVIB_WID 2
+#define MC13783_REGSET_VRF1_LSH 2
+#define MC13783_REGSET_VRF1_WID 2
+#define MC13783_REGSET_VRF2_LSH 4
+#define MC13783_REGSET_VRF2_WID 2
+#define MC13783_REGSET_VMMC1_LSH 6
+#define MC13783_REGSET_VMMC1_WID 3
+#define MC13783_REGSET_VMMC2_LSH 9
+#define MC13783_REGSET_VMMC2_WID 3
+
+/*
+ * Reg Switcher 0
+ */
+#define MC13783_SWSET_SW1A_LSH 0
+#define MC13783_SWSET_SW1A_WID 6
+#define MC13783_SWSET_SW1A_DVS_LSH 6
+#define MC13783_SWSET_SW1A_DVS_WID 6
+#define MC13783_SWSET_SW1A_STDBY_LSH 12
+#define MC13783_SWSET_SW1A_STDBY_WID 6
+
+/*
+ * Reg Switcher 1
+ */
+#define MC13783_SWSET_SW1B_LSH 0
+#define MC13783_SWSET_SW1B_WID 6
+#define MC13783_SWSET_SW1B_DVS_LSH 6
+#define MC13783_SWSET_SW1B_DVS_WID 6
+#define MC13783_SWSET_SW1B_STDBY_LSH 12
+#define MC13783_SWSET_SW1B_STDBY_WID 6
+
+/*
+ * Reg Switcher 2
+ */
+#define MC13783_SWSET_SW2A_LSH 0
+#define MC13783_SWSET_SW2A_WID 6
+#define MC13783_SWSET_SW2A_DVS_LSH 6
+#define MC13783_SWSET_SW2A_DVS_WID 6
+#define MC13783_SWSET_SW2A_STDBY_LSH 12
+#define MC13783_SWSET_SW2A_STDBY_WID 6
+
+/*
+ * Reg Switcher 3
+ */
+#define MC13783_SWSET_SW2B_LSH 0
+#define MC13783_SWSET_SW2B_WID 6
+#define MC13783_SWSET_SW2B_DVS_LSH 6
+#define MC13783_SWSET_SW2B_DVS_WID 6
+#define MC13783_SWSET_SW2B_STDBY_LSH 12
+#define MC13783_SWSET_SW2B_STDBY_WID 6
+
+/*
+ * Reg Switcher 4
+ */
+#define MC13783_SWCTRL_SW1A_MODE_LSH 0
+#define MC13783_SWCTRL_SW1A_MODE_WID 2
+#define MC13783_SWCTRL_SW1A_STBY_MODE_LSH 2
+#define MC13783_SWCTRL_SW1A_STBY_MODE_WID 2
+#define MC13783_SWCTRL_SW1A_DVS_SPEED_LSH 6
+#define MC13783_SWCTRL_SW1A_DVS_SPEED_WID 2
+#define MC13783_SWCTRL_SW1A_PANIC_MODE_LSH 8
+#define MC13783_SWCTRL_SW1A_PANIC_MODE_WID 1
+#define MC13783_SWCTRL_SW1A_SOFTSTART_LSH 9
+#define MC13783_SWCTRL_SW1A_SOFTSTART_WID 1
+#define MC13783_SWCTRL_SW1B_MODE_LSH 10
+#define MC13783_SWCTRL_SW1B_MODE_WID 2
+#define MC13783_SWCTRL_SW1B_STBY_MODE_LSH 12
+#define MC13783_SWCTRL_SW1B_STBY_MODE_WID 2
+#define MC13783_SWCTRL_SW1B_DVS_SPEED_LSH 14
+#define MC13783_SWCTRL_SW1B_DVS_SPEED_WID 2
+#define MC13783_SWCTRL_SW1B_PANIC_MODE_LSH 16
+#define MC13783_SWCTRL_SW1B_PANIC_MODE_WID 1
+#define MC13783_SWCTRL_SW1B_SOFTSTART_LSH 17
+#define MC13783_SWCTRL_SW1B_SOFTSTART_WID 1
+#define MC13783_SWCTRL_PLL_EN_LSH 18
+#define MC13783_SWCTRL_PLL_EN_WID 1
+#define MC13783_SWCTRL_PLL_EN_ENABLE 1
+#define MC13783_SWCTRL_PLL_EN_DISABLE 0
+#define MC13783_SWCTRL_PLL_FACTOR_LSH 19
+#define MC13783_SWCTRL_PLL_FACTOR_WID 3
+
+/*
+ * Reg Switcher 5
+ */
+#define MC13783_SWCTRL_SW2A_MODE_LSH 0
+#define MC13783_SWCTRL_SW2A_MODE_WID 2
+#define MC13783_SWCTRL_SW2A_STBY_MODE_LSH 2
+#define MC13783_SWCTRL_SW2A_STBY_MODE_WID 2
+#define MC13783_SWCTRL_SW2A_DVS_SPEED_LSH 6
+#define MC13783_SWCTRL_SW2A_DVS_SPEED_WID 2
+#define MC13783_SWCTRL_SW2A_PANIC_MODE_LSH 8
+#define MC13783_SWCTRL_SW2A_PANIC_MODE_WID 1
+#define MC13783_SWCTRL_SW2A_SOFTSTART_LSH 9
+#define MC13783_SWCTRL_SW2A_SOFTSTART_WID 1
+#define MC13783_SWCTRL_SW2B_MODE_LSH 10
+#define MC13783_SWCTRL_SW2B_MODE_WID 2
+#define MC13783_SWCTRL_SW2B_STBY_MODE_LSH 12
+#define MC13783_SWCTRL_SW2B_STBY_MODE_WID 2
+#define MC13783_SWCTRL_SW2B_DVS_SPEED_LSH 14
+#define MC13783_SWCTRL_SW2B_DVS_SPEED_WID 2
+#define MC13783_SWCTRL_SW2B_PANIC_MODE_LSH 16
+#define MC13783_SWCTRL_SW2B_PANIC_MODE_WID 1
+#define MC13783_SWCTRL_SW2B_SOFTSTART_LSH 17
+#define MC13783_SWCTRL_SW2B_SOFTSTART_WID 1
+#define MC13783_SWSET_SW3_LSH 18
+#define MC13783_SWSET_SW3_WID 2
+#define MC13783_SWCTRL_SW3_EN_LSH 20
+#define MC13783_SWCTRL_SW3_EN_WID 2
+#define MC13783_SWCTRL_SW3_EN_ENABLE 1
+#define MC13783_SWCTRL_SW3_EN_DISABLE 0
+#define MC13783_SWCTRL_SW3_STBY_LSH 21
+#define MC13783_SWCTRL_SW3_STBY_WID 1
+#define MC13783_SWCTRL_SW3_MODE_LSH 22
+#define MC13783_SWCTRL_SW3_MODE_WID 1
+
+/*
+ * Switcher configuration
+ */
+#define MC13783_SWCTRL_SW_MODE_SYNC_RECT_EN 0
+#define MC13783_SWCTRL_SW_MODE_PULSE_NO_SKIP_EN 1
+#define MC13783_SWCTRL_SW_MODE_PULSE_SKIP_EN 2
+#define MC13783_SWCTRL_SW_MODE_LOW_POWER_EN 3
+#define MC13783_REGTRL_LP_MODE_ENABLE 1
+#define MC13783_REGTRL_LP_MODE_DISABLE 0
+#define MC13783_REGTRL_STBY_MODE_ENABLE 1
+#define MC13783_REGTRL_STBY_MODE_DISABLE 0
+
+#endif /* __MC13783_POWER_DEFS_H__ */
diff --git a/drivers/mxc/pmic/mc13783/pmic_rtc.c b/drivers/mxc/pmic/mc13783/pmic_rtc.c
new file mode 100644
index 000000000000..50f086f87022
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_rtc.c
@@ -0,0 +1,552 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13783/pmic_rtc.c
+ * @brief This is the main file of PMIC(mc13783) RTC driver.
+ *
+ * @ingroup PMIC_RTC
+ */
+
+/*
+ * Includes
+ */
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+#include <linux/pmic_rtc.h>
+#include <linux/pmic_status.h>
+
+#include "pmic_rtc_defs.h"
+
+#define PMIC_LOAD_ERROR_MSG \
+"PMIC card was not correctly detected. Stop loading PMIC RTC driver\n"
+
+/*
+ * Global variables
+ */
+static int pmic_rtc_major;
+static void callback_alarm_asynchronous(void *);
+static void callback_alarm_synchronous(void *);
+static unsigned int pmic_rtc_poll(struct file *file, poll_table * wait);
+static DECLARE_WAIT_QUEUE_HEAD(queue_alarm);
+static DECLARE_WAIT_QUEUE_HEAD(pmic_rtc_wait);
+static pmic_event_callback_t alarm_callback;
+static pmic_event_callback_t rtc_callback;
+static int pmic_rtc_detected = 0;
+static bool pmic_rtc_done = 0;
+static struct class *pmic_rtc_class;
+
+static DECLARE_MUTEX(mutex);
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_rtc_set_time);
+EXPORT_SYMBOL(pmic_rtc_get_time);
+EXPORT_SYMBOL(pmic_rtc_set_time_alarm);
+EXPORT_SYMBOL(pmic_rtc_get_time_alarm);
+EXPORT_SYMBOL(pmic_rtc_wait_alarm);
+EXPORT_SYMBOL(pmic_rtc_event_sub);
+EXPORT_SYMBOL(pmic_rtc_event_unsub);
+EXPORT_SYMBOL(pmic_rtc_loaded);
+
+/*
+ * Real Time Clock Pmic API
+ */
+
+/*!
+ * This is the callback function called on TSI Pmic event, used in asynchronous
+ * call.
+ */
+static void callback_alarm_asynchronous(void *unused)
+{
+ pmic_rtc_done = true;
+}
+
+/*!
+ * This is the callback function is used in test code for (un)sub.
+ */
+static void callback_test_sub(void)
+{
+ printk(KERN_INFO "*****************************************\n");
+ printk(KERN_INFO "***** PMIC RTC 'Alarm IT CallBack' ******\n");
+ printk(KERN_INFO "*****************************************\n");
+}
+
+/*!
+ * This is the callback function called on TSI Pmic event, used in synchronous
+ * call.
+ */
+static void callback_alarm_synchronous(void *unused)
+{
+ printk(KERN_INFO "*** Alarm IT Pmic ***\n");
+ wake_up(&queue_alarm);
+}
+
+/*!
+ * This function wait the Alarm event
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_wait_alarm(void)
+{
+ DEFINE_WAIT(wait);
+ alarm_callback.func = callback_alarm_synchronous;
+ alarm_callback.param = NULL;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_TODAI, alarm_callback));
+ prepare_to_wait(&queue_alarm, &wait, TASK_UNINTERRUPTIBLE);
+ schedule();
+ finish_wait(&queue_alarm, &wait);
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_TODAI, alarm_callback));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function set the real time clock of PMIC
+ *
+ * @param pmic_time value of date and time
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_set_time(struct timeval * pmic_time)
+{
+ unsigned int tod_reg_val = 0;
+ unsigned int day_reg_val = 0;
+ unsigned int mask, value;
+
+ tod_reg_val = pmic_time->tv_sec % 86400;
+ day_reg_val = pmic_time->tv_sec / 86400;
+
+ mask = BITFMASK(MC13783_RTCTIME_TIME);
+ value = BITFVAL(MC13783_RTCTIME_TIME, tod_reg_val);
+ CHECK_ERROR(pmic_write_reg(REG_RTC_TIME, value, mask));
+
+ mask = BITFMASK(MC13783_RTCDAY_DAY);
+ value = BITFVAL(MC13783_RTCDAY_DAY, day_reg_val);
+ CHECK_ERROR(pmic_write_reg(REG_RTC_DAY, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function get the real time clock of PMIC
+ *
+ * @param pmic_time return value of date and time
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_get_time(struct timeval * pmic_time)
+{
+ unsigned int tod_reg_val = 0;
+ unsigned int day_reg_val = 0;
+ unsigned int mask, value;
+
+ mask = BITFMASK(MC13783_RTCTIME_TIME);
+ CHECK_ERROR(pmic_read_reg(REG_RTC_TIME, &value, mask));
+ tod_reg_val = BITFEXT(value, MC13783_RTCTIME_TIME);
+
+ mask = BITFMASK(MC13783_RTCDAY_DAY);
+ CHECK_ERROR(pmic_read_reg(REG_RTC_DAY, &value, mask));
+ day_reg_val = BITFEXT(value, MC13783_RTCDAY_DAY);
+
+ pmic_time->tv_sec = (unsigned long)((unsigned long)(tod_reg_val &
+ 0x0001FFFF) +
+ (unsigned long)(day_reg_val *
+ 86400));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function set the real time clock alarm of PMIC
+ *
+ * @param pmic_time value of date and time
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_set_time_alarm(struct timeval * pmic_time)
+{
+ unsigned int tod_reg_val = 0;
+ unsigned int day_reg_val = 0;
+ unsigned int mask, value;
+ int ret;
+
+ if ((ret = down_interruptible(&mutex)) < 0)
+ return ret;
+
+ tod_reg_val = pmic_time->tv_sec % 86400;
+ day_reg_val = pmic_time->tv_sec / 86400;
+
+ mask = BITFMASK(MC13783_RTCALARM_TIME);
+ value = BITFVAL(MC13783_RTCALARM_TIME, tod_reg_val);
+ CHECK_ERROR(pmic_write_reg(REG_RTC_ALARM, value, mask));
+
+ mask = BITFMASK(MC13783_RTCALARM_DAY);
+ value = BITFVAL(MC13783_RTCALARM_DAY, day_reg_val);
+ CHECK_ERROR(pmic_write_reg(REG_RTC_DAY_ALARM, value, mask));
+ up(&mutex);
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function get the real time clock alarm of PMIC
+ *
+ * @param pmic_time return value of date and time
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_get_time_alarm(struct timeval * pmic_time)
+{
+ unsigned int tod_reg_val = 0;
+ unsigned int day_reg_val = 0;
+ unsigned int mask, value;
+
+ mask = BITFMASK(MC13783_RTCALARM_TIME);
+ CHECK_ERROR(pmic_read_reg(REG_RTC_ALARM, &value, mask));
+ tod_reg_val = BITFEXT(value, MC13783_RTCALARM_TIME);
+
+ mask = BITFMASK(MC13783_RTCALARM_DAY);
+ CHECK_ERROR(pmic_read_reg(REG_RTC_DAY_ALARM, &value, mask));
+ day_reg_val = BITFEXT(value, MC13783_RTCALARM_DAY);
+
+ pmic_time->tv_sec = (unsigned long)((unsigned long)(tod_reg_val &
+ 0x0001FFFF) +
+ (unsigned long)(day_reg_val *
+ 86400));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to un/subscribe on RTC event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ * @param sub define if Un/subscribe event.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_event(t_rtc_int event, void *callback, bool sub)
+{
+ type_event rtc_event;
+ if (callback == NULL) {
+ return PMIC_ERROR;
+ } else {
+ rtc_callback.func = callback;
+ rtc_callback.param = NULL;
+ }
+ switch (event) {
+ case RTC_IT_ALARM:
+ rtc_event = EVENT_TODAI;
+ break;
+ case RTC_IT_1HZ:
+ rtc_event = EVENT_E1HZI;
+ break;
+ case RTC_IT_RST:
+ rtc_event = EVENT_RTCRSTI;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ if (sub == true) {
+ CHECK_ERROR(pmic_event_subscribe(rtc_event, rtc_callback));
+ } else {
+ CHECK_ERROR(pmic_event_unsubscribe(rtc_event, rtc_callback));
+ }
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to subscribe on RTC event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_event_sub(t_rtc_int event, void *callback)
+{
+ CHECK_ERROR(pmic_rtc_event(event, callback, true));
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * This function is used to un subscribe on RTC event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_rtc_event_unsub(t_rtc_int event, void *callback)
+{
+ CHECK_ERROR(pmic_rtc_event(event, callback, false));
+ return PMIC_SUCCESS;
+}
+
+/* Called without the kernel lock - fine */
+static unsigned int pmic_rtc_poll(struct file *file, poll_table * wait)
+{
+ /*poll_wait(file, &pmic_rtc_wait, wait); */
+
+ if (pmic_rtc_done)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+/*!
+ * This function implements IOCTL controls on a PMIC RTC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @param cmd the command
+ * @param arg the parameter
+ * @return This function returns 0 if successful.
+ */
+static int pmic_rtc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct timeval *pmic_time = NULL;
+
+ if (_IOC_TYPE(cmd) != 'p')
+ return -ENOTTY;
+
+ if (arg) {
+ if ((pmic_time = kmalloc(sizeof(struct timeval),
+ GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+ /* if (copy_from_user(pmic_time, (struct timeval *)arg,
+ sizeof(struct timeval))) {
+ return -EFAULT;
+ } */
+ }
+
+ switch (cmd) {
+ case PMIC_RTC_SET_TIME:
+ if (copy_from_user(pmic_time, (struct timeval *)arg,
+ sizeof(struct timeval))) {
+ return -EFAULT;
+ }
+ pr_debug("SET RTC\n");
+ CHECK_ERROR(pmic_rtc_set_time(pmic_time));
+ break;
+ case PMIC_RTC_GET_TIME:
+ if (copy_to_user((struct timeval *)arg, pmic_time,
+ sizeof(struct timeval))) {
+ return -EFAULT;
+ }
+ pr_debug("GET RTC\n");
+ CHECK_ERROR(pmic_rtc_get_time(pmic_time));
+ break;
+ case PMIC_RTC_SET_ALARM:
+ if (copy_from_user(pmic_time, (struct timeval *)arg,
+ sizeof(struct timeval))) {
+ return -EFAULT;
+ }
+ pr_debug("SET RTC ALARM\n");
+ CHECK_ERROR(pmic_rtc_set_time_alarm(pmic_time));
+ break;
+ case PMIC_RTC_GET_ALARM:
+ if (copy_to_user((struct timeval *)arg, pmic_time,
+ sizeof(struct timeval))) {
+ return -EFAULT;
+ }
+ pr_debug("GET RTC ALARM\n");
+ CHECK_ERROR(pmic_rtc_get_time_alarm(pmic_time));
+ break;
+ case PMIC_RTC_WAIT_ALARM:
+ printk(KERN_INFO "WAIT ALARM...\n");
+ CHECK_ERROR(pmic_rtc_event_sub(RTC_IT_ALARM,
+ callback_test_sub));
+ CHECK_ERROR(pmic_rtc_wait_alarm());
+ printk(KERN_INFO "ALARM DONE\n");
+ CHECK_ERROR(pmic_rtc_event_unsub(RTC_IT_ALARM,
+ callback_test_sub));
+ break;
+ case PMIC_RTC_ALARM_REGISTER:
+ printk(KERN_INFO "PMIC RTC ALARM REGISTER\n");
+ alarm_callback.func = callback_alarm_asynchronous;
+ alarm_callback.param = NULL;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_TODAI, alarm_callback));
+ break;
+ case PMIC_RTC_ALARM_UNREGISTER:
+ printk(KERN_INFO "PMIC RTC ALARM UNREGISTER\n");
+ alarm_callback.func = callback_alarm_asynchronous;
+ alarm_callback.param = NULL;
+ CHECK_ERROR(pmic_event_unsubscribe
+ (EVENT_TODAI, alarm_callback));
+ pmic_rtc_done = false;
+ break;
+ default:
+ pr_debug("%d unsupported ioctl command\n", (int)cmd);
+ return -EINVAL;
+ }
+
+ if (arg) {
+ if (copy_to_user((struct timeval *)arg, pmic_time,
+ sizeof(struct timeval))) {
+ return -EFAULT;
+ }
+ kfree(pmic_time);
+ }
+
+ return 0;
+}
+
+/*!
+ * This function implements the open method on a PMIC RTC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_rtc_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/*!
+ * This function implements the release method on a PMIC RTC device.
+ *
+ * @param inode pointer on the node
+ * @param file pointer on the file
+ * @return This function returns 0.
+ */
+static int pmic_rtc_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/*!
+ * This function is called to put the RTC in a low power state.
+ * There is no need for power handlers for the RTC device.
+ * The RTC cannot be suspended.
+ *
+ * @param pdev the device structure used to give information on which RTC
+ * device (0 through 3 channels) to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int pmic_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+/*!
+ * This function is called to resume the RTC from a low power state.
+ *
+ * @param pdev the device structure used to give information on which RTC
+ * device (0 through 3 channels) to suspend
+ *
+ * @return The function always returns 0.
+ */
+static int pmic_rtc_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+
+static struct file_operations pmic_rtc_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = pmic_rtc_ioctl,
+ .poll = pmic_rtc_poll,
+ .open = pmic_rtc_open,
+ .release = pmic_rtc_release,
+};
+
+int pmic_rtc_loaded(void)
+{
+ return pmic_rtc_detected;
+}
+
+static int pmic_rtc_remove(struct platform_device *pdev)
+{
+ device_destroy(pmic_rtc_class, MKDEV(pmic_rtc_major, 0));
+ class_destroy(pmic_rtc_class);
+ unregister_chrdev(pmic_rtc_major, "pmic_rtc");
+ return 0;
+}
+
+static int pmic_rtc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct device *temp_class;
+
+ pmic_rtc_major = register_chrdev(0, "pmic_rtc", &pmic_rtc_fops);
+ if (pmic_rtc_major < 0) {
+ printk(KERN_ERR "Unable to get a major for pmic_rtc\n");
+ return pmic_rtc_major;
+ }
+
+ pmic_rtc_class = class_create(THIS_MODULE, "pmic_rtc");
+ if (IS_ERR(pmic_rtc_class)) {
+ printk(KERN_ERR "Error creating pmic rtc class.\n");
+ ret = PTR_ERR(pmic_rtc_class);
+ goto err_out1;
+ }
+
+ temp_class = device_create(pmic_rtc_class, NULL,
+ MKDEV(pmic_rtc_major, 0), NULL,
+ "pmic_rtc");
+ if (IS_ERR(temp_class)) {
+ printk(KERN_ERR "Error creating pmic rtc class device.\n");
+ ret = PTR_ERR(temp_class);
+ goto err_out2;
+ }
+
+ pmic_rtc_detected = 1;
+ printk(KERN_INFO "PMIC RTC successfully probed\n");
+ return ret;
+
+ err_out2:
+ class_destroy(pmic_rtc_class);
+ err_out1:
+ unregister_chrdev(pmic_rtc_major, "pmic_rtc");
+ return ret;
+}
+
+static struct platform_driver pmic_rtc_driver_ldm = {
+ .driver = {
+ .name = "pmic_rtc",
+ .owner = THIS_MODULE,
+ },
+ .suspend = pmic_rtc_suspend,
+ .resume = pmic_rtc_resume,
+ .probe = pmic_rtc_probe,
+ .remove = pmic_rtc_remove,
+};
+
+static int __init pmic_rtc_init(void)
+{
+ pr_debug("PMIC RTC driver loading...\n");
+ return platform_driver_register(&pmic_rtc_driver_ldm);
+}
+static void __exit pmic_rtc_exit(void)
+{
+ platform_driver_unregister(&pmic_rtc_driver_ldm);
+ pr_debug("PMIC RTC driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall(pmic_rtc_init);
+module_exit(pmic_rtc_exit);
+
+MODULE_DESCRIPTION("Pmic_rtc driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13783/pmic_rtc_defs.h b/drivers/mxc/pmic/mc13783/pmic_rtc_defs.h
new file mode 100644
index 000000000000..16e968dd9977
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_rtc_defs.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MC13783_RTC_DEFS_H__
+#define __MC13783_RTC_DEFS_H__
+
+/*!
+ * @file mc13783/pmic_rtc_defs.h
+ * @brief This is the internal header of PMIC(mc13783) RTC driver.
+ *
+ * @ingroup PMIC_RTC
+ */
+
+/*
+ * RTC Time
+ */
+#define MC13783_RTCTIME_TIME_LSH 0
+#define MC13783_RTCTIME_TIME_WID 17
+
+/*
+ * RTC Alarm
+ */
+#define MC13783_RTCALARM_TIME_LSH 0
+#define MC13783_RTCALARM_TIME_WID 17
+
+/*
+ * RTC Day
+ */
+#define MC13783_RTCDAY_DAY_LSH 0
+#define MC13783_RTCDAY_DAY_WID 15
+
+/*
+ * RTC Day alarm
+ */
+#define MC13783_RTCALARM_DAY_LSH 0
+#define MC13783_RTCALARM_DAY_WID 15
+
+#endif /* __MC13783_RTC_DEFS_H__ */
diff --git a/drivers/mxc/pmic/mc13892/Kconfig b/drivers/mxc/pmic/mc13892/Kconfig
new file mode 100644
index 000000000000..930e06ab2282
--- /dev/null
+++ b/drivers/mxc/pmic/mc13892/Kconfig
@@ -0,0 +1,48 @@
+#
+# PMIC Modules configuration
+#
+
+config MXC_MC13892_ADC
+ tristate "MC13892 ADC support"
+ depends on MXC_PMIC_MC13892
+ ---help---
+ This is the MC13892 ADC module driver. This module provides kernel API
+ for the ADC system of MC13892.
+ It controls also the touch screen interface.
+ If you want MC13892 ADC support, you should say Y here
+
+config MXC_MC13892_RTC
+ tristate "MC13892 Real Time Clock (RTC) support"
+ depends on MXC_PMIC_MC13892
+ ---help---
+ This is the MC13892 RTC module driver. This module provides kernel API
+ for RTC part of MC13892.
+ If you want MC13892 RTC support, you should say Y here
+config MXC_MC13892_LIGHT
+ tristate "MC13892 Light and Backlight support"
+ depends on MXC_PMIC_MC13892
+ ---help---
+ This is the MC13892 Light module driver. This module provides kernel API
+ for led and backlight control part of MC13892.
+ If you want MC13892 Light support, you should say Y here
+config MXC_MC13892_BATTERY
+ tristate "MC13892 Battery API support"
+ depends on MXC_PMIC_MC13892
+ ---help---
+ This is the MC13892 battery module driver. This module provides kernel API
+ for battery control part of MC13892.
+ If you want MC13892 battery support, you should say Y here
+config MXC_MC13892_CONNECTIVITY
+ tristate "MC13892 Connectivity API support"
+ depends on MXC_PMIC_MC13892
+ ---help---
+ This is the MC13892 connectivity module driver. This module provides kernel API
+ for USB/RS232 connectivity control part of MC13892.
+ If you want MC13892 connectivity support, you should say Y here
+config MXC_MC13892_POWER
+ tristate "MC13892 Power API support"
+ depends on MXC_PMIC_MC13892
+ ---help---
+ This is the MC13892 power and supplies module driver. This module provides kernel API
+ for power and regulator control part of MC13892.
+ If you want MC13892 power support, you should say Y here
diff --git a/drivers/mxc/pmic/mc13892/Makefile b/drivers/mxc/pmic/mc13892/Makefile
new file mode 100644
index 000000000000..0ed2b7eb4c11
--- /dev/null
+++ b/drivers/mxc/pmic/mc13892/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the mc13783 pmic drivers.
+#
+
+obj-$(CONFIG_MXC_MC13892_ADC) += pmic_adc.o
+#obj-$(CONFIG_MXC_MC13892_RTC) += pmic_rtc.o
+obj-$(CONFIG_MXC_MC13892_LIGHT) += pmic_light.o
+obj-$(CONFIG_MXC_MC13892_BATTERY) += pmic_battery.o
+#obj-$(CONFIG_MXC_MC13892_CONNECTIVITY) += pmic_convity.o
+#obj-$(CONFIG_MXC_MC13892_POWER) += pmic_power.o
diff --git a/drivers/mxc/pmic/mc13892/pmic_adc.c b/drivers/mxc/pmic/mc13892/pmic_adc.c
new file mode 100644
index 000000000000..cec4d6045974
--- /dev/null
+++ b/drivers/mxc/pmic/mc13892/pmic_adc.c
@@ -0,0 +1,984 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/device.h>
+
+#include <linux/pmic_adc.h>
+#include <linux/pmic_status.h>
+
+#include "../core/pmic.h"
+
+#define DEF_ADC_0 0x008000
+#define DEF_ADC_3 0x0001c0
+
+#define ADC_NB_AVAILABLE 2
+
+#define MAX_CHANNEL 7
+
+#define MC13892_ADC0_TS_M_LSH 14
+#define MC13892_ADC0_TS_M_WID 3
+
+/*
+ * Maximun allowed variation in the three X/Y co-ordinates acquired from
+ * touch-screen
+ */
+#define DELTA_Y_MAX 50
+#define DELTA_X_MAX 50
+
+/*
+ * ADC 0
+ */
+#define ADC_WAIT_TSI_0 0x001400
+
+#define ADC_INC 0x030000
+#define ADC_BIS 0x800000
+#define ADC_CHRGRAW_D5 0x008000
+
+/*
+ * ADC 1
+ */
+
+#define ADC_EN 0x000001
+#define ADC_SGL_CH 0x000002
+#define ADC_ADSEL 0x000008
+#define ADC_CH_0_POS 5
+#define ADC_CH_0_MASK 0x0000E0
+#define ADC_CH_1_POS 8
+#define ADC_CH_1_MASK 0x000700
+#define ADC_DELAY_POS 11
+#define ADC_DELAY_MASK 0x07F800
+#define ADC_ATO 0x080000
+#define ASC_ADC 0x100000
+#define ADC_WAIT_TSI_1 0x200001
+#define ADC_NO_ADTRIG 0x200000
+
+/*
+ * ADC 2 - 4
+ */
+#define ADD1_RESULT_MASK 0x00000FFC
+#define ADD2_RESULT_MASK 0x00FFC000
+#define ADC_TS_MASK 0x00FFCFFC
+
+#define ADC_WCOMP 0x040000
+#define ADC_WCOMP_H_POS 0
+#define ADC_WCOMP_L_POS 9
+#define ADC_WCOMP_H_MASK 0x00003F
+#define ADC_WCOMP_L_MASK 0x007E00
+
+#define ADC_MODE_MASK 0x00003F
+
+#define ADC_INT_BISDONEI 0x02
+#define ADC_TSMODE_MASK 0x007000
+
+typedef enum adc_state {
+ ADC_FREE,
+ ADC_USED,
+ ADC_MONITORING,
+} t_adc_state;
+
+typedef enum reading_mode {
+ /*!
+ * Enables lithium cell reading
+ */
+ M_LITHIUM_CELL = 0x000001,
+ /*!
+ * Enables charge current reading
+ */
+ M_CHARGE_CURRENT = 0x000002,
+ /*!
+ * Enables battery current reading
+ */
+ M_BATTERY_CURRENT = 0x000004,
+} t_reading_mode;
+
+typedef struct {
+ /*!
+ * Delay before first conversion
+ */
+ unsigned int delay;
+ /*!
+ * sets the ATX bit for delay on all conversion
+ */
+ bool conv_delay;
+ /*!
+ * Sets the single channel mode
+ */
+ bool single_channel;
+ /*!
+ * Channel selection 1
+ */
+ t_channel channel_0;
+ /*!
+ * Channel selection 2
+ */
+ t_channel channel_1;
+ /*!
+ * Used to configure ADC mode with t_reading_mode
+ */
+ t_reading_mode read_mode;
+ /*!
+ * Sets the Touch screen mode
+ */
+ bool read_ts;
+ /*!
+ * Wait TSI event before touch screen reading
+ */
+ bool wait_tsi;
+ /*!
+ * Sets CHRGRAW scaling to divide by 5
+ * Only supported on 2.0 and higher
+ */
+ bool chrgraw_devide_5;
+ /*!
+ * Return ADC values
+ */
+ unsigned int value[8];
+ /*!
+ * Return touch screen values
+ */
+ t_touch_screen ts_value;
+} t_adc_param;
+
+static int pmic_adc_filter(t_touch_screen *ts_curr);
+int mc13892_adc_request(bool read_ts);
+int mc13892_adc_release(int adc_index);
+t_reading_mode mc13892_set_read_mode(t_channel channel);
+PMIC_STATUS mc13892_adc_read_ts(t_touch_screen *touch_sample, int wait_tsi);
+
+/* internal function */
+static void callback_tsi(void *);
+static void callback_adcdone(void *);
+static void callback_adcbisdone(void *);
+
+static int swait;
+
+static int suspend_flag;
+
+static wait_queue_head_t suspendq;
+
+/* EXPORTED FUNCTIONS */
+EXPORT_SYMBOL(pmic_adc_init);
+EXPORT_SYMBOL(pmic_adc_deinit);
+EXPORT_SYMBOL(pmic_adc_convert);
+EXPORT_SYMBOL(pmic_adc_convert_8x);
+EXPORT_SYMBOL(pmic_adc_set_touch_mode);
+EXPORT_SYMBOL(pmic_adc_get_touch_mode);
+EXPORT_SYMBOL(pmic_adc_get_touch_sample);
+
+static DECLARE_COMPLETION(adcdone_it);
+static DECLARE_COMPLETION(adcbisdone_it);
+static DECLARE_COMPLETION(adc_tsi);
+static pmic_event_callback_t tsi_event;
+static pmic_event_callback_t event_adc;
+static pmic_event_callback_t event_adc_bis;
+static bool data_ready_adc_1;
+static bool data_ready_adc_2;
+static bool adc_ts;
+static bool wait_ts;
+static bool monitor_en;
+static bool monitor_adc;
+static DECLARE_MUTEX(convert_mutex);
+
+static DECLARE_WAIT_QUEUE_HEAD(queue_adc_busy);
+static t_adc_state adc_dev[2];
+
+static unsigned channel_num[] = {
+ 0,
+ 1,
+ 3,
+ 4,
+ 2,
+ 0,
+ 1,
+ 3,
+ 4,
+ -1,
+ 5,
+ 6,
+ 7,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1
+};
+
+static bool pmic_adc_ready;
+
+int is_pmic_adc_ready()
+{
+ return pmic_adc_ready;
+}
+EXPORT_SYMBOL(is_pmic_adc_ready);
+
+
+static int pmic_adc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ suspend_flag = 1;
+ CHECK_ERROR(pmic_write_reg(REG_ADC0, DEF_ADC_0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC2, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC3, DEF_ADC_3, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC4, 0, PMIC_ALL_BITS));
+
+ return 0;
+};
+
+static int pmic_adc_resume(struct platform_device *pdev)
+{
+ /* nothing for mc13892 adc */
+ unsigned int adc_0_reg, adc_1_reg, reg_mask;
+ suspend_flag = 0;
+
+ /* let interrupt of TSI again */
+ adc_0_reg = ADC_WAIT_TSI_0;
+ reg_mask = ADC_WAIT_TSI_0;
+ CHECK_ERROR(pmic_write_reg(REG_ADC0, adc_0_reg, reg_mask));
+ adc_1_reg = ADC_WAIT_TSI_1 | (ADC_BIS * adc_ts);
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg, PMIC_ALL_BITS));
+
+ while (swait > 0) {
+ swait--;
+ wake_up_interruptible(&suspendq);
+ }
+
+ return 0;
+};
+
+static void callback_tsi(void *unused)
+{
+ pr_debug("*** TSI IT mc13892 PMIC_ADC_GET_TOUCH_SAMPLE ***\n");
+ if (wait_ts) {
+ complete(&adc_tsi);
+ pmic_event_mask(EVENT_TSI);
+ }
+}
+
+static void callback_adcdone(void *unused)
+{
+ if (data_ready_adc_1)
+ complete(&adcdone_it);
+}
+
+static void callback_adcbisdone(void *unused)
+{
+ pr_debug("* adcdone bis it callback *\n");
+ if (data_ready_adc_2)
+ complete(&adcbisdone_it);
+}
+
+static int pmic_adc_filter(t_touch_screen *ts_curr)
+{
+ unsigned int ydiff, xdiff;
+ unsigned int sample_sumx, sample_sumy;
+
+ if (ts_curr->contact_resistance == 0) {
+ ts_curr->x_position = 0;
+ ts_curr->y_position = 0;
+ return 0;
+ }
+
+ ydiff = abs(ts_curr->y_position1 - ts_curr->y_position2);
+ if (ydiff > DELTA_Y_MAX) {
+ pr_debug("pmic_adc_filter: Ret pos y\n");
+ return -1;
+ }
+
+ xdiff = abs(ts_curr->x_position1 - ts_curr->x_position2);
+ if (xdiff > DELTA_X_MAX) {
+ pr_debug("mc13892_adc_filter: Ret pos x\n");
+ return -1;
+ }
+
+ sample_sumx = ts_curr->x_position1 + ts_curr->x_position2;
+ sample_sumy = ts_curr->y_position1 + ts_curr->y_position2;
+
+ ts_curr->y_position = sample_sumy / 2;
+ ts_curr->x_position = sample_sumx / 2;
+
+ return 0;
+}
+
+int pmic_adc_init(void)
+{
+ unsigned int reg_value = 0, i = 0;
+
+ if (suspend_flag == 1)
+ return -EBUSY;
+
+ for (i = 0; i < ADC_NB_AVAILABLE; i++)
+ adc_dev[i] = ADC_FREE;
+
+ CHECK_ERROR(pmic_write_reg(REG_ADC0, DEF_ADC_0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC2, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC3, DEF_ADC_3, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_ADC4, 0, PMIC_ALL_BITS));
+ reg_value = 0x001000;
+
+ data_ready_adc_1 = false;
+ data_ready_adc_2 = false;
+ adc_ts = false;
+ wait_ts = false;
+ monitor_en = false;
+ monitor_adc = false;
+
+ /* sub to ADCDone IT */
+ event_adc.param = NULL;
+ event_adc.func = callback_adcdone;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_ADCDONEI, event_adc));
+
+ /* sub to ADCDoneBis IT */
+ event_adc_bis.param = NULL;
+ event_adc_bis.func = callback_adcbisdone;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_ADCBISDONEI, event_adc_bis));
+
+ /* sub to Touch Screen IT */
+ tsi_event.param = NULL;
+ tsi_event.func = callback_tsi;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_TSI, tsi_event));
+
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS pmic_adc_deinit(void)
+{
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_ADCDONEI, event_adc));
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_ADCBISDONEI, event_adc_bis));
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_TSI, tsi_event));
+
+ return PMIC_SUCCESS;
+}
+
+int mc13892_adc_init_param(t_adc_param * adc_param)
+{
+ int i = 0;
+
+ if (suspend_flag == 1)
+ return -EBUSY;
+
+ adc_param->delay = 0;
+ adc_param->conv_delay = false;
+ adc_param->single_channel = false;
+ adc_param->channel_0 = BATTERY_VOLTAGE;
+ adc_param->channel_1 = BATTERY_VOLTAGE;
+ adc_param->read_mode = 0;
+ adc_param->wait_tsi = 0;
+ adc_param->chrgraw_devide_5 = true;
+ adc_param->read_ts = false;
+ adc_param->ts_value.x_position = 0;
+ adc_param->ts_value.y_position = 0;
+ adc_param->ts_value.contact_resistance = 0;
+ for (i = 0; i <= MAX_CHANNEL; i++)
+ adc_param->value[i] = 0;
+
+ return 0;
+}
+
+PMIC_STATUS mc13892_adc_convert(t_adc_param * adc_param)
+{
+ bool use_bis = false;
+ unsigned int adc_0_reg = 0, adc_1_reg = 0, reg_1 = 0, result_reg =
+ 0, i = 0;
+ unsigned int result = 0, temp = 0;
+ pmic_version_t mc13892_ver;
+ pr_debug("mc13892 ADC - mc13892_adc_convert ....\n");
+ if (suspend_flag == 1)
+ return -EBUSY;
+
+ if (adc_param->wait_tsi) {
+ /* configure adc to wait tsi interrupt */
+ INIT_COMPLETION(adc_tsi);
+
+ /*for ts don't use bis */
+ /*put ts in interrupt mode */
+ /* still kep reference? */
+ adc_0_reg = 0x001400 | (ADC_BIS * 0);
+ pmic_event_unmask(EVENT_TSI);
+ CHECK_ERROR(pmic_write_reg(REG_ADC0, adc_0_reg, PMIC_ALL_BITS));
+ /*for ts don't use bis */
+ adc_1_reg = 0x200001 | (ADC_BIS * 0);
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg, PMIC_ALL_BITS));
+ pr_debug("wait tsi ....\n");
+ wait_ts = true;
+ wait_for_completion_interruptible(&adc_tsi);
+ wait_ts = false;
+ }
+ down(&convert_mutex);
+ use_bis = mc13892_adc_request(adc_param->read_ts);
+ if (use_bis < 0) {
+ pr_debug("process has received a signal and got interrupted\n");
+ return -EINTR;
+ }
+
+ /* CONFIGURE ADC REG 0 */
+ adc_0_reg = 0;
+ adc_1_reg = 0;
+ if (adc_param->read_ts == false) {
+ adc_0_reg = adc_param->read_mode & 0x00003F;
+ /* add auto inc */
+ adc_0_reg |= ADC_INC;
+ if (use_bis) {
+ /* add adc bis */
+ adc_0_reg |= ADC_BIS;
+ }
+ mc13892_ver = pmic_get_version();
+ if (mc13892_ver.revision >= 20)
+ if (adc_param->chrgraw_devide_5)
+ adc_0_reg |= ADC_CHRGRAW_D5;
+
+ if (adc_param->single_channel)
+ adc_1_reg |= ADC_SGL_CH;
+
+ if (adc_param->conv_delay)
+ adc_1_reg |= ADC_ATO;
+
+ if (adc_param->single_channel)
+ adc_1_reg |= ADC_SGL_CH;
+
+ adc_1_reg |= (adc_param->channel_0 << ADC_CH_0_POS) &
+ ADC_CH_0_MASK;
+ adc_1_reg |= (adc_param->channel_1 << ADC_CH_1_POS) &
+ ADC_CH_1_MASK;
+ } else {
+ adc_0_reg = 0x002400 | (ADC_BIS * use_bis) | ADC_INC;
+ }
+ pr_debug("Write Reg %i = %x\n", REG_ADC0, adc_0_reg);
+ /*Change has been made here */
+ CHECK_ERROR(pmic_write_reg(REG_ADC0, adc_0_reg,
+ ADC_INC | ADC_BIS | ADC_CHRGRAW_D5 |
+ 0xfff00ff));
+ /* CONFIGURE ADC REG 1 */
+ if (adc_param->read_ts == false) {
+ adc_1_reg |= ADC_NO_ADTRIG;
+ adc_1_reg |= ADC_EN;
+ adc_1_reg |= (adc_param->delay << ADC_DELAY_POS) &
+ ADC_DELAY_MASK;
+ if (use_bis)
+ adc_1_reg |= ADC_BIS;
+ } else {
+ /* configure and start convert to read x and y position */
+ /* configure to read 2 value in channel selection 1 & 2 */
+ adc_1_reg = 0x100409 | (ADC_BIS * use_bis) | ADC_NO_ADTRIG;
+ /* set ATOx = 5, it could be better for ts ADC */
+ adc_1_reg |= 0x002800;
+ }
+ reg_1 = adc_1_reg;
+ if (use_bis == 0) {
+ data_ready_adc_1 = false;
+ adc_1_reg |= ASC_ADC;
+ data_ready_adc_1 = true;
+ pr_debug("Write Reg %i = %x\n", REG_ADC1, adc_1_reg);
+ INIT_COMPLETION(adcdone_it);
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg,
+ ADC_SGL_CH | ADC_ATO | ADC_ADSEL
+ | ADC_CH_0_MASK | ADC_CH_1_MASK |
+ ADC_NO_ADTRIG | ADC_EN |
+ ADC_DELAY_MASK | ASC_ADC | ADC_BIS));
+ pr_debug("wait adc done \n");
+ wait_for_completion_interruptible(&adcdone_it);
+ data_ready_adc_1 = false;
+ } else {
+ data_ready_adc_2 = false;
+ adc_1_reg |= ASC_ADC;
+ data_ready_adc_2 = true;
+ INIT_COMPLETION(adcbisdone_it);
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, adc_1_reg, 0xFFFFFF));
+ temp = 0x800000;
+ CHECK_ERROR(pmic_write_reg(REG_ADC3, temp, 0xFFFFFF));
+ pr_debug("wait adc done bis\n");
+ wait_for_completion_interruptible(&adcbisdone_it);
+ data_ready_adc_2 = false;
+ }
+ /* read result and store in adc_param */
+ result = 0;
+ if (use_bis == 0)
+ result_reg = REG_ADC2;
+ else
+ result_reg = REG_ADC4;
+
+ CHECK_ERROR(pmic_write_reg(REG_ADC1, 4 << ADC_CH_1_POS,
+ ADC_CH_0_MASK | ADC_CH_1_MASK));
+
+ for (i = 0; i <= 3; i++) {
+ CHECK_ERROR(pmic_read_reg(result_reg, &result, PMIC_ALL_BITS));
+ adc_param->value[i] = ((result & ADD1_RESULT_MASK) >> 2);
+ adc_param->value[i + 4] = ((result & ADD2_RESULT_MASK) >> 14);
+ pr_debug("value[%d] = %d, value[%d] = %d\n",
+ i, adc_param->value[i],
+ i + 4, adc_param->value[i + 4]);
+ }
+ if (adc_param->read_ts) {
+ adc_param->ts_value.x_position = adc_param->value[0];
+ adc_param->ts_value.x_position1 = adc_param->value[0];
+ adc_param->ts_value.x_position2 = adc_param->value[1];
+ adc_param->ts_value.y_position = adc_param->value[3];
+ adc_param->ts_value.y_position1 = adc_param->value[3];
+ adc_param->ts_value.y_position2 = adc_param->value[4];
+ adc_param->ts_value.contact_resistance = adc_param->value[6];
+ CHECK_ERROR(pmic_write_reg(REG_ADC0, 0x0,
+ ADC_TSMODE_MASK));
+ }
+
+ /*if (adc_param->read_ts) {
+ adc_param->ts_value.x_position = adc_param->value[2];
+ adc_param->ts_value.y_position = adc_param->value[5];
+ adc_param->ts_value.contact_resistance = adc_param->value[6];
+ } */
+ mc13892_adc_release(use_bis);
+ up(&convert_mutex);
+
+ return PMIC_SUCCESS;
+}
+
+t_reading_mode mc13892_set_read_mode(t_channel channel)
+{
+ t_reading_mode read_mode = 0;
+
+ switch (channel) {
+ case CHARGE_CURRENT:
+ read_mode = M_CHARGE_CURRENT;
+ break;
+ case BATTERY_CURRENT:
+ read_mode = M_BATTERY_CURRENT;
+ break;
+ default:
+ read_mode = 0;
+ }
+
+ return read_mode;
+}
+
+PMIC_STATUS pmic_adc_convert(t_channel channel, unsigned short *result)
+{
+ t_adc_param adc_param;
+ PMIC_STATUS ret;
+
+ if (suspend_flag == 1)
+ return -EBUSY;
+
+ channel = channel_num[channel];
+ if (channel == -1) {
+ pr_debug("Wrong channel ID\n");
+ return PMIC_PARAMETER_ERROR;
+ }
+ mc13892_adc_init_param(&adc_param);
+ pr_debug("pmic_adc_convert\n");
+ adc_param.read_ts = false;
+ adc_param.single_channel = true;
+ adc_param.read_mode = mc13892_set_read_mode(channel);
+
+ /* Find the group */
+ if (channel <= 7)
+ adc_param.channel_0 = channel;
+ else
+ return PMIC_PARAMETER_ERROR;
+
+ ret = mc13892_adc_convert(&adc_param);
+ *result = adc_param.value[0];
+ return ret;
+}
+
+PMIC_STATUS pmic_adc_convert_8x(t_channel channel, unsigned short *result)
+{
+ t_adc_param adc_param;
+ int i;
+ PMIC_STATUS ret;
+ if (suspend_flag == 1)
+ return -EBUSY;
+
+ channel = channel_num[channel];
+
+ if (channel == -1) {
+ pr_debug("Wrong channel ID\n");
+ return PMIC_PARAMETER_ERROR;
+ }
+ mc13892_adc_init_param(&adc_param);
+ pr_debug("pmic_adc_convert_8x\n");
+ adc_param.read_ts = false;
+ adc_param.single_channel = true;
+ adc_param.read_mode = mc13892_set_read_mode(channel);
+
+ if (channel <= 7) {
+ adc_param.channel_0 = channel;
+ adc_param.channel_1 = channel;
+ } else
+ return PMIC_PARAMETER_ERROR;
+
+ ret = mc13892_adc_convert(&adc_param);
+ for (i = 0; i <= 7; i++)
+ result[i] = adc_param.value[i];
+
+ return ret;
+}
+
+PMIC_STATUS pmic_adc_set_touch_mode(t_touch_mode touch_mode)
+{
+ if (suspend_flag == 1)
+ return -EBUSY;
+
+ CHECK_ERROR(pmic_write_reg(REG_ADC0,
+ BITFVAL(MC13892_ADC0_TS_M, touch_mode),
+ BITFMASK(MC13892_ADC0_TS_M)));
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS pmic_adc_get_touch_mode(t_touch_mode * touch_mode)
+{
+ unsigned int value;
+ if (suspend_flag == 1)
+ return -EBUSY;
+
+ CHECK_ERROR(pmic_read_reg(REG_ADC0, &value, PMIC_ALL_BITS));
+
+ *touch_mode = BITFEXT(value, MC13892_ADC0_TS_M);
+
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS pmic_adc_get_touch_sample(t_touch_screen *touch_sample, int wait)
+{
+ if (mc13892_adc_read_ts(touch_sample, wait) != 0)
+ return PMIC_ERROR;
+ if (0 == pmic_adc_filter(touch_sample))
+ return PMIC_SUCCESS;
+ else
+ return PMIC_ERROR;
+}
+
+PMIC_STATUS mc13892_adc_read_ts(t_touch_screen *ts_value, int wait_tsi)
+{
+ t_adc_param param;
+ pr_debug("mc13892_adc : mc13892_adc_read_ts\n");
+ if (suspend_flag == 1)
+ return -EBUSY;
+
+ if (wait_ts) {
+ pr_debug("mc13892_adc : error TS busy \n");
+ return PMIC_ERROR;
+ }
+ mc13892_adc_init_param(&param);
+ param.wait_tsi = wait_tsi;
+ param.read_ts = true;
+ if (mc13892_adc_convert(&param) != 0)
+ return PMIC_ERROR;
+ /* check if x-y is ok */
+ if (param.ts_value.contact_resistance < 1000) {
+ ts_value->x_position = param.ts_value.x_position;
+ ts_value->x_position1 = param.ts_value.x_position1;
+ ts_value->x_position2 = param.ts_value.x_position2;
+
+ ts_value->y_position = param.ts_value.y_position;
+ ts_value->y_position1 = param.ts_value.y_position1;
+ ts_value->y_position2 = param.ts_value.y_position2;
+
+ ts_value->contact_resistance =
+ param.ts_value.contact_resistance + 1;
+
+ } else {
+ ts_value->x_position = 0;
+ ts_value->y_position = 0;
+ ts_value->contact_resistance = 0;
+
+ }
+ return PMIC_SUCCESS;
+}
+
+int mc13892_adc_request(bool read_ts)
+{
+ int adc_index = -1;
+ if (read_ts != 0) {
+ /*for ts we use bis=0 */
+ if (adc_dev[0] == ADC_USED)
+ return -1;
+ /*no wait here */
+ adc_dev[0] = ADC_USED;
+ adc_index = 0;
+ } else {
+ /*for other adc use bis = 1 */
+ if (adc_dev[1] == ADC_USED) {
+ return -1;
+ /*no wait here */
+ }
+ adc_dev[1] = ADC_USED;
+ adc_index = 1;
+ }
+ pr_debug("mc13892_adc : request ADC %d\n", adc_index);
+ return adc_index;
+}
+
+int mc13892_adc_release(int adc_index)
+{
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0)))
+ return -ERESTARTSYS;
+ }
+
+ pr_debug("mc13892_adc : release ADC %d\n", adc_index);
+ if ((adc_dev[adc_index] == ADC_MONITORING) ||
+ (adc_dev[adc_index] == ADC_USED)) {
+ adc_dev[adc_index] = ADC_FREE;
+ wake_up(&queue_adc_busy);
+ return 0;
+ }
+ return -1;
+}
+
+#ifdef DEBUG
+static t_adc_param adc_param_db;
+
+static ssize_t adc_info(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int *value = adc_param_db.value;
+
+ pr_debug("adc_info\n");
+
+ pr_debug("ch0\t\t%d\n", adc_param_db.channel_0);
+ pr_debug("ch1\t\t%d\n", adc_param_db.channel_1);
+ pr_debug("d5\t\t%d\n", adc_param_db.chrgraw_devide_5);
+ pr_debug("conv delay\t%d\n", adc_param_db.conv_delay);
+ pr_debug("delay\t\t%d\n", adc_param_db.delay);
+ pr_debug("read mode\t%d\n", adc_param_db.read_mode);
+ pr_debug("read ts\t\t%d\n", adc_param_db.read_ts);
+ pr_debug("single ch\t%d\n", adc_param_db.single_channel);
+ pr_debug("wait ts int\t%d\n", adc_param_db.wait_tsi);
+ pr_debug("value0-3:\t%d\t%d\t%d\t%d\n", value[0], value[1],
+ value[2], value[3]);
+ pr_debug("value4-7:\t%d\t%d\t%d\t%d\n", value[4], value[5],
+ value[6], value[7]);
+
+ return 0;
+}
+
+enum {
+ ADC_SET_CH0 = 0,
+ ADC_SET_CH1,
+ ADC_SET_DV5,
+ ADC_SET_CON_DELAY,
+ ADC_SET_DELAY,
+ ADC_SET_RM,
+ ADC_SET_RT,
+ ADC_SET_S_CH,
+ ADC_SET_WAIT_TS,
+ ADC_INIT_P,
+ ADC_START,
+ ADC_TS,
+ ADC_TS_READ,
+ ADC_TS_CAL,
+ ADC_CMD_MAX
+};
+
+static const char *const adc_cmd[ADC_CMD_MAX] = {
+ [ADC_SET_CH0] = "ch0",
+ [ADC_SET_CH1] = "ch1",
+ [ADC_SET_DV5] = "dv5",
+ [ADC_SET_CON_DELAY] = "cd",
+ [ADC_SET_DELAY] = "dl",
+ [ADC_SET_RM] = "rm",
+ [ADC_SET_RT] = "rt",
+ [ADC_SET_S_CH] = "sch",
+ [ADC_SET_WAIT_TS] = "wt",
+ [ADC_INIT_P] = "init",
+ [ADC_START] = "start",
+ [ADC_TS] = "touch",
+ [ADC_TS_READ] = "touchr",
+ [ADC_TS_CAL] = "cal"
+};
+
+static int cmd(unsigned int index, int value)
+{
+ t_touch_screen ts;
+
+ switch (index) {
+ case ADC_SET_CH0:
+ adc_param_db.channel_0 = value;
+ break;
+ case ADC_SET_CH1:
+ adc_param_db.channel_1 = value;
+ break;
+ case ADC_SET_DV5:
+ adc_param_db.chrgraw_devide_5 = value;
+ break;
+ case ADC_SET_CON_DELAY:
+ adc_param_db.conv_delay = value;
+ break;
+ case ADC_SET_RM:
+ adc_param_db.read_mode = value;
+ break;
+ case ADC_SET_RT:
+ adc_param_db.read_ts = value;
+ break;
+ case ADC_SET_S_CH:
+ adc_param_db.single_channel = value;
+ break;
+ case ADC_SET_WAIT_TS:
+ adc_param_db.wait_tsi = value;
+ break;
+ case ADC_INIT_P:
+ mc13892_adc_init_param(&adc_param_db);
+ break;
+ case ADC_START:
+ mc13892_adc_convert(&adc_param_db);
+ break;
+ case ADC_TS:
+ pmic_adc_get_touch_sample(&ts, 1);
+ pr_debug("x = %d\n", ts.x_position);
+ pr_debug("y = %d\n", ts.y_position);
+ pr_debug("p = %d\n", ts.contact_resistance);
+ break;
+ case ADC_TS_READ:
+ pmic_adc_get_touch_sample(&ts, 0);
+ pr_debug("x = %d\n", ts.x_position);
+ pr_debug("y = %d\n", ts.y_position);
+ pr_debug("p = %d\n", ts.contact_resistance);
+ break;
+ case ADC_TS_CAL:
+ break;
+ default:
+ pr_debug("error command\n");
+ break;
+ }
+ return 0;
+}
+
+static ssize_t adc_ctl(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int state = 0;
+ const char *const *s;
+ char *p, *q;
+ int error;
+ int len, value = 0;
+
+ pr_debug("adc_ctl\n");
+
+ q = NULL;
+ q = memchr(buf, ' ', count);
+
+ if (q != NULL) {
+ len = q - buf;
+ q += 1;
+ value = simple_strtoul(q, NULL, 10);
+ } else {
+ p = memchr(buf, '\n', count);
+ len = p ? p - buf : count;
+ }
+
+ for (s = &adc_cmd[state]; state < ADC_CMD_MAX; s++, state++) {
+ if (*s && !strncmp(buf, *s, len))
+ break;
+ }
+ if (state < ADC_CMD_MAX && *s)
+ error = cmd(state, value);
+ else
+ error = -EINVAL;
+
+ return count;
+}
+
+#else
+static ssize_t adc_info(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return 0;
+}
+
+static ssize_t adc_ctl(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return count;
+}
+
+#endif
+
+static DEVICE_ATTR(adc, 0644, adc_info, adc_ctl);
+
+static int pmic_adc_module_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ pr_debug("PMIC ADC start probe\n");
+ ret = device_create_file(&(pdev->dev), &dev_attr_adc);
+ if (ret) {
+ pr_debug("Can't create device file!\n");
+ return -ENODEV;
+ }
+
+ init_waitqueue_head(&suspendq);
+
+ ret = pmic_adc_init();
+ if (ret != PMIC_SUCCESS) {
+ pr_debug("Error in pmic_adc_init.\n");
+ goto rm_dev_file;
+ }
+
+ pmic_adc_ready = 1;
+ pr_debug("PMIC ADC successfully probed\n");
+ return 0;
+
+ rm_dev_file:
+ device_remove_file(&(pdev->dev), &dev_attr_adc);
+ return ret;
+}
+
+static int pmic_adc_module_remove(struct platform_device *pdev)
+{
+ pmic_adc_deinit();
+ pmic_adc_ready = 0;
+ pr_debug("PMIC ADC successfully removed\n");
+ return 0;
+}
+
+static struct platform_driver pmic_adc_driver_ldm = {
+ .driver = {
+ .name = "pmic_adc",
+ },
+ .suspend = pmic_adc_suspend,
+ .resume = pmic_adc_resume,
+ .probe = pmic_adc_module_probe,
+ .remove = pmic_adc_module_remove,
+};
+
+static int __init pmic_adc_module_init(void)
+{
+ pr_debug("PMIC ADC driver loading...\n");
+ return platform_driver_register(&pmic_adc_driver_ldm);
+}
+
+static void __exit pmic_adc_module_exit(void)
+{
+ platform_driver_unregister(&pmic_adc_driver_ldm);
+ pr_debug("PMIC ADC driver successfully unloaded\n");
+}
+
+module_init(pmic_adc_module_init);
+module_exit(pmic_adc_module_exit);
+
+MODULE_DESCRIPTION("PMIC ADC device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13892/pmic_battery.c b/drivers/mxc/pmic/mc13892/pmic_battery.c
new file mode 100644
index 000000000000..f2337cda51db
--- /dev/null
+++ b/drivers/mxc/pmic/mc13892/pmic_battery.c
@@ -0,0 +1,634 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/delay.h>
+#include <asm/mach-types.h>
+#include <linux/pmic_battery.h>
+#include <linux/pmic_adc.h>
+#include <linux/pmic_status.h>
+
+#define BIT_CHG_VOL_LSH 0
+#define BIT_CHG_VOL_WID 3
+
+#define BIT_CHG_CURR_LSH 3
+#define BIT_CHG_CURR_WID 4
+
+#define BIT_CHG_PLIM_LSH 15
+#define BIT_CHG_PLIM_WID 2
+
+#define BIT_CHG_DETS_LSH 6
+#define BIT_CHG_DETS_WID 1
+#define BIT_CHG_CURRS_LSH 11
+#define BIT_CHG_CURRS_WID 1
+
+#define TRICKLE_CHG_EN_LSH 7
+#define LOW_POWER_BOOT_ACK_LSH 8
+#define BAT_TH_CHECK_DIS_LSH 9
+#define BATTFET_CTL_EN_LSH 10
+#define BATTFET_CTL_LSH 11
+#define REV_MOD_EN_LSH 13
+#define PLIM_DIS_LSH 17
+#define CHG_LED_EN_LSH 18
+#define RESTART_CHG_STAT_LSH 20
+#define AUTO_CHG_DIS_LSH 21
+#define CYCLING_DIS_LSH 22
+#define VI_PROGRAM_EN_LSH 23
+
+#define TRICKLE_CHG_EN_WID 1
+#define LOW_POWER_BOOT_ACK_WID 1
+#define BAT_TH_CHECK_DIS_WID 1
+#define BATTFET_CTL_EN_WID 1
+#define BATTFET_CTL_WID 1
+#define REV_MOD_EN_WID 1
+#define PLIM_DIS_WID 1
+#define CHG_LED_EN_WID 1
+#define RESTART_CHG_STAT_WID 1
+#define AUTO_CHG_DIS_WID 1
+#define CYCLING_DIS_WID 1
+#define VI_PROGRAM_EN_WID 1
+
+#define ACC_STARTCC_LSH 0
+#define ACC_STARTCC_WID 1
+#define ACC_RSTCC_LSH 1
+#define ACC_RSTCC_WID 1
+#define ACC_CCFAULT_LSH 7
+#define ACC_CCFAULT_WID 7
+#define ACC_CCOUT_LSH 8
+#define ACC_CCOUT_WID 16
+#define ACC1_ONEC_LSH 0
+#define ACC1_ONEC_WID 15
+
+#define ACC_CALIBRATION 0x17
+#define ACC_START_COUNTER 0x07
+#define ACC_STOP_COUNTER 0x2
+#define ACC_CONTROL_BIT_MASK 0x1f
+#define ACC_ONEC_VALUE 2621
+#define ACC_COULOMB_PER_LSB 1
+#define ACC_CALIBRATION_DURATION_MSECS 20
+
+#define BAT_VOLTAGE_UNIT_UV 4692
+#define BAT_CURRENT_UNIT_UA 5870
+#define CHG_VOLTAGE_UINT_UV 23474
+#define CHG_MIN_CURRENT_UA 3500
+
+#define COULOMB_TO_UAH(c) (10000 * c / 36)
+
+enum chg_setting {
+ TRICKLE_CHG_EN,
+ LOW_POWER_BOOT_ACK,
+ BAT_TH_CHECK_DIS,
+ BATTFET_CTL_EN,
+ BATTFET_CTL,
+ REV_MOD_EN,
+ PLIM_DIS,
+ CHG_LED_EN,
+ RESTART_CHG_STAT,
+ AUTO_CHG_DIS,
+ CYCLING_DIS,
+ VI_PROGRAM_EN
+};
+
+static int pmic_set_chg_current(unsigned short curr)
+{
+ unsigned int mask;
+ unsigned int value;
+
+ value = BITFVAL(BIT_CHG_CURR, curr);
+ mask = BITFMASK(BIT_CHG_CURR);
+ CHECK_ERROR(pmic_write_reg(REG_CHARGE, value, mask));
+
+ return 0;
+}
+
+static int pmic_set_chg_misc(enum chg_setting type, unsigned short flag)
+{
+
+ unsigned int reg_value = 0;
+ unsigned int mask = 0;
+
+ switch (type) {
+ case TRICKLE_CHG_EN:
+ reg_value = BITFVAL(TRICKLE_CHG_EN, flag);
+ mask = BITFMASK(TRICKLE_CHG_EN);
+ break;
+ case LOW_POWER_BOOT_ACK:
+ reg_value = BITFVAL(LOW_POWER_BOOT_ACK, flag);
+ mask = BITFMASK(LOW_POWER_BOOT_ACK);
+ break;
+ case BAT_TH_CHECK_DIS:
+ reg_value = BITFVAL(BAT_TH_CHECK_DIS, flag);
+ mask = BITFMASK(BAT_TH_CHECK_DIS);
+ break;
+ case BATTFET_CTL_EN:
+ reg_value = BITFVAL(BATTFET_CTL_EN, flag);
+ mask = BITFMASK(BATTFET_CTL_EN);
+ break;
+ case BATTFET_CTL:
+ reg_value = BITFVAL(BATTFET_CTL, flag);
+ mask = BITFMASK(BATTFET_CTL);
+ break;
+ case REV_MOD_EN:
+ reg_value = BITFVAL(REV_MOD_EN, flag);
+ mask = BITFMASK(REV_MOD_EN);
+ break;
+ case PLIM_DIS:
+ reg_value = BITFVAL(PLIM_DIS, flag);
+ mask = BITFMASK(PLIM_DIS);
+ break;
+ case CHG_LED_EN:
+ reg_value = BITFVAL(CHG_LED_EN, flag);
+ mask = BITFMASK(CHG_LED_EN);
+ break;
+ case RESTART_CHG_STAT:
+ reg_value = BITFVAL(RESTART_CHG_STAT, flag);
+ mask = BITFMASK(RESTART_CHG_STAT);
+ break;
+ case AUTO_CHG_DIS:
+ reg_value = BITFVAL(AUTO_CHG_DIS, flag);
+ mask = BITFMASK(AUTO_CHG_DIS);
+ break;
+ case CYCLING_DIS:
+ reg_value = BITFVAL(CYCLING_DIS, flag);
+ mask = BITFMASK(CYCLING_DIS);
+ break;
+ case VI_PROGRAM_EN:
+ reg_value = BITFVAL(VI_PROGRAM_EN, flag);
+ mask = BITFMASK(VI_PROGRAM_EN);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_write_reg(REG_CHARGE, reg_value, mask));
+
+ return 0;
+}
+
+static int pmic_get_batt_voltage(unsigned short *voltage)
+{
+ t_channel channel;
+ unsigned short result[8];
+
+ channel = BATTERY_VOLTAGE;
+ CHECK_ERROR(pmic_adc_convert(channel, result));
+ *voltage = result[0];
+
+ return 0;
+}
+
+static int pmic_get_batt_current(unsigned short *curr)
+{
+ t_channel channel;
+ unsigned short result[8];
+
+ channel = BATTERY_CURRENT;
+ CHECK_ERROR(pmic_adc_convert(channel, result));
+ *curr = result[0];
+
+ return 0;
+}
+
+static int coulomb_counter_calibration;
+static unsigned int coulomb_counter_start_time_msecs;
+
+static int pmic_start_coulomb_counter(void)
+{
+ /* set scaler */
+ CHECK_ERROR(pmic_write_reg(REG_ACC1,
+ ACC_COULOMB_PER_LSB * ACC_ONEC_VALUE, BITFMASK(ACC1_ONEC)));
+
+ CHECK_ERROR(pmic_write_reg(
+ REG_ACC0, ACC_START_COUNTER, ACC_CONTROL_BIT_MASK));
+ coulomb_counter_start_time_msecs = jiffies_to_msecs(jiffies);
+ pr_debug("coulomb counter start time %u\n",
+ coulomb_counter_start_time_msecs);
+ return 0;
+}
+
+static int pmic_stop_coulomb_counter(void)
+{
+ CHECK_ERROR(pmic_write_reg(
+ REG_ACC0, ACC_STOP_COUNTER, ACC_CONTROL_BIT_MASK));
+ return 0;
+}
+
+static int pmic_calibrate_coulomb_counter(void)
+{
+ int ret;
+ unsigned int value;
+
+ /* set scaler */
+ CHECK_ERROR(pmic_write_reg(REG_ACC1,
+ 0x1, BITFMASK(ACC1_ONEC)));
+
+ CHECK_ERROR(pmic_write_reg(
+ REG_ACC0, ACC_CALIBRATION, ACC_CONTROL_BIT_MASK));
+ msleep(ACC_CALIBRATION_DURATION_MSECS);
+
+ ret = pmic_read_reg(REG_ACC0, &value, BITFMASK(ACC_CCOUT));
+ if (ret != 0)
+ return -1;
+ value = BITFEXT(value, ACC_CCOUT);
+ pr_debug("calibrate value = %x\n", value);
+ coulomb_counter_calibration = (int)((s16)((u16) value));
+ pr_debug("coulomb_counter_calibration = %d\n",
+ coulomb_counter_calibration);
+
+ return 0;
+
+}
+
+static int pmic_get_charger_coulomb(int *coulomb)
+{
+ int ret;
+ unsigned int value;
+ int calibration;
+ unsigned int time_diff_msec;
+
+ ret = pmic_read_reg(REG_ACC0, &value, BITFMASK(ACC_CCOUT));
+ if (ret != 0)
+ return -1;
+ value = BITFEXT(value, ACC_CCOUT);
+ pr_debug("counter value = %x\n", value);
+ *coulomb = ((s16)((u16)value)) * ACC_COULOMB_PER_LSB;
+
+ if (abs(*coulomb) >= ACC_COULOMB_PER_LSB) {
+ /* calibrate */
+ time_diff_msec = jiffies_to_msecs(jiffies);
+ time_diff_msec =
+ (time_diff_msec > coulomb_counter_start_time_msecs) ?
+ (time_diff_msec - coulomb_counter_start_time_msecs) :
+ (0xffffffff - coulomb_counter_start_time_msecs
+ + time_diff_msec);
+ calibration = coulomb_counter_calibration * (int)time_diff_msec
+ / (ACC_ONEC_VALUE * ACC_CALIBRATION_DURATION_MSECS);
+ *coulomb -= calibration;
+ }
+
+ return 0;
+}
+
+static int pmic_restart_charging(void)
+{
+ pmic_set_chg_misc(BAT_TH_CHECK_DIS, 1);
+ pmic_set_chg_misc(AUTO_CHG_DIS, 0);
+ pmic_set_chg_misc(VI_PROGRAM_EN, 1);
+ pmic_set_chg_current(0x8);
+ pmic_set_chg_misc(RESTART_CHG_STAT, 1);
+ return 0;
+}
+
+struct mc13892_dev_info {
+ struct device *dev;
+
+ unsigned short voltage_raw;
+ int voltage_uV;
+ unsigned short current_raw;
+ int current_uA;
+ int battery_status;
+ int full_counter;
+ int charger_online;
+ int charger_voltage_uV;
+ int accum_current_uAh;
+
+ struct power_supply bat;
+ struct power_supply charger;
+
+ struct workqueue_struct *monitor_wqueue;
+ struct delayed_work monitor_work;
+};
+
+#define mc13892_SENSER 25
+#define to_mc13892_dev_info(x) container_of((x), struct mc13892_dev_info, \
+ bat);
+
+static enum power_supply_property mc13892_battery_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_STATUS,
+};
+
+static enum power_supply_property mc13892_charger_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int mc13892_charger_update_status(struct mc13892_dev_info *di)
+{
+ int ret;
+ unsigned int value;
+ int online;
+
+ ret = pmic_read_reg(REG_INT_SENSE0, &value, BITFMASK(BIT_CHG_DETS));
+
+ if (ret == 0) {
+ online = BITFEXT(value, BIT_CHG_DETS);
+ if (online != di->charger_online) {
+ di->charger_online = online;
+ dev_info(di->charger.dev, "charger status: %s\n",
+ online ? "online" : "offline");
+ power_supply_changed(&di->charger);
+
+ cancel_delayed_work(&di->monitor_work);
+ queue_delayed_work(di->monitor_wqueue,
+ &di->monitor_work, HZ / 10);
+ if (online) {
+ pmic_start_coulomb_counter();
+ pmic_restart_charging();
+ } else
+ pmic_stop_coulomb_counter();
+ }
+ }
+
+ return ret;
+}
+
+static int mc13892_charger_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct mc13892_dev_info *di =
+ container_of((psy), struct mc13892_dev_info, charger);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = di->charger_online;
+ return 0;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int mc13892_battery_read_status(struct mc13892_dev_info *di)
+{
+ int retval;
+ int coulomb;
+ retval = pmic_get_batt_voltage(&(di->voltage_raw));
+ if (retval == 0)
+ di->voltage_uV = di->voltage_raw * BAT_VOLTAGE_UNIT_UV;
+
+ retval = pmic_get_batt_current(&(di->current_raw));
+ if (retval == 0) {
+ if (di->current_raw & 0x200)
+ di->current_uA =
+ (0x1FF - (di->current_raw & 0x1FF)) *
+ BAT_CURRENT_UNIT_UA * (-1);
+ else
+ di->current_uA =
+ (di->current_raw & 0x1FF) * BAT_CURRENT_UNIT_UA;
+ }
+ retval = pmic_get_charger_coulomb(&coulomb);
+ if (retval == 0)
+ di->accum_current_uAh = COULOMB_TO_UAH(coulomb);
+
+ return retval;
+}
+
+static void mc13892_battery_update_status(struct mc13892_dev_info *di)
+{
+ unsigned int value;
+ int retval;
+ int old_battery_status = di->battery_status;
+
+ if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN)
+ di->full_counter = 0;
+
+ if (di->charger_online) {
+ retval = pmic_read_reg(REG_INT_SENSE0,
+ &value, BITFMASK(BIT_CHG_CURRS));
+
+ if (retval == 0) {
+ value = BITFEXT(value, BIT_CHG_CURRS);
+ if (value)
+ di->battery_status =
+ POWER_SUPPLY_STATUS_CHARGING;
+ else
+ di->battery_status =
+ POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
+
+ if (di->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING)
+ di->full_counter++;
+ else
+ di->full_counter = 0;
+ } else {
+ di->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ di->full_counter = 0;
+ }
+
+ dev_dbg(di->bat.dev, "bat status: %d\n",
+ di->battery_status);
+
+ if (di->battery_status != old_battery_status)
+ power_supply_changed(&di->bat);
+}
+
+static void mc13892_battery_work(struct work_struct *work)
+{
+ struct mc13892_dev_info *di = container_of(work,
+ struct mc13892_dev_info,
+ monitor_work.work);
+ const int interval = HZ * 60;
+
+ dev_dbg(di->dev, "%s\n", __func__);
+
+ mc13892_battery_update_status(di);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
+}
+
+static void charger_online_event_callback(void *para)
+{
+ struct mc13892_dev_info *di = (struct mc13892_dev_info *) para;
+ pr_info("\n\n DETECTED charger plug/unplug event\n");
+ mc13892_charger_update_status(di);
+}
+
+
+static int mc13892_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct mc13892_dev_info *di = to_mc13892_dev_info(psy);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN) {
+ mc13892_charger_update_status(di);
+ mc13892_battery_update_status(di);
+ }
+ val->intval = di->battery_status;
+ return 0;
+ default:
+ break;
+ }
+
+ mc13892_battery_read_status(di);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = di->voltage_uV;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = di->current_uA;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = di->accum_current_uAh;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = 3800000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = 3300000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pmic_battery_remove(struct platform_device *pdev)
+{
+ pmic_event_callback_t bat_event_callback;
+ struct mc13892_dev_info *di = platform_get_drvdata(pdev);
+
+ bat_event_callback.func = charger_online_event_callback;
+ bat_event_callback.param = (void *) di;
+ pmic_event_unsubscribe(EVENT_CHGDETI, bat_event_callback);
+
+ cancel_rearming_delayed_workqueue(di->monitor_wqueue,
+ &di->monitor_work);
+ destroy_workqueue(di->monitor_wqueue);
+ power_supply_unregister(&di->bat);
+ power_supply_unregister(&di->charger);
+
+ kfree(di);
+
+ return 0;
+}
+
+static int pmic_battery_probe(struct platform_device *pdev)
+{
+ int retval = 0;
+ struct mc13892_dev_info *di;
+ pmic_event_callback_t bat_event_callback;
+ pmic_version_t pmic_version;
+
+ /* Only apply battery driver for MC13892 V2.0 due to ENGR108085 */
+ pmic_version = pmic_get_version();
+ if (pmic_version.revision < 20) {
+ pr_debug("Battery driver is only applied for MC13892 V2.0\n");
+ return -1;
+ }
+ if (machine_is_mx51_babbage()) {
+ pr_debug("mc13892 charger is not used for this platform\n");
+ return -1;
+ }
+
+ di = kzalloc(sizeof(*di), GFP_KERNEL);
+ if (!di) {
+ retval = -ENOMEM;
+ goto di_alloc_failed;
+ }
+
+ platform_set_drvdata(pdev, di);
+
+ di->dev = &pdev->dev;
+ di->bat.name = "mc13892_bat";
+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->bat.properties = mc13892_battery_props;
+ di->bat.num_properties = ARRAY_SIZE(mc13892_battery_props);
+ di->bat.get_property = mc13892_battery_get_property;
+ di->bat.use_for_apm = 1;
+
+ di->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+ retval = power_supply_register(&pdev->dev, &di->bat);
+ if (retval) {
+ dev_err(di->dev, "failed to register battery\n");
+ goto batt_failed;
+ }
+ di->charger.name = "mc13892_charger";
+ di->charger.type = POWER_SUPPLY_TYPE_MAINS;
+ di->charger.properties = mc13892_charger_props;
+ di->charger.num_properties = ARRAY_SIZE(mc13892_charger_props);
+ di->charger.get_property = mc13892_charger_get_property;
+ retval = power_supply_register(&pdev->dev, &di->charger);
+ if (retval) {
+ dev_err(di->dev, "failed to register charger\n");
+ goto charger_failed;
+ }
+ INIT_DELAYED_WORK(&di->monitor_work, mc13892_battery_work);
+ di->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id);
+ if (!di->monitor_wqueue) {
+ retval = -ESRCH;
+ goto workqueue_failed;
+ }
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 10);
+
+ bat_event_callback.func = charger_online_event_callback;
+ bat_event_callback.param = (void *) di;
+ pmic_event_subscribe(EVENT_CHGDETI, bat_event_callback);
+
+ pmic_stop_coulomb_counter();
+ pmic_calibrate_coulomb_counter();
+ goto success;
+
+workqueue_failed:
+ power_supply_unregister(&di->charger);
+charger_failed:
+ power_supply_unregister(&di->bat);
+batt_failed:
+ kfree(di);
+di_alloc_failed:
+success:
+ dev_dbg(di->dev, "%s battery probed!\n", __func__);
+ return retval;
+
+
+ return 0;
+}
+
+static struct platform_driver pmic_battery_driver_ldm = {
+ .driver = {
+ .name = "pmic_battery",
+ .bus = &platform_bus_type,
+ },
+ .probe = pmic_battery_probe,
+ .remove = pmic_battery_remove,
+};
+
+static int __init pmic_battery_init(void)
+{
+ pr_debug("PMIC Battery driver loading...\n");
+ return platform_driver_register(&pmic_battery_driver_ldm);
+}
+
+static void __exit pmic_battery_exit(void)
+{
+ platform_driver_unregister(&pmic_battery_driver_ldm);
+ pr_debug("PMIC Battery driver successfully unloaded\n");
+}
+
+module_init(pmic_battery_init);
+module_exit(pmic_battery_exit);
+
+MODULE_DESCRIPTION("pmic_battery driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pmic/mc13892/pmic_light.c b/drivers/mxc/pmic/mc13892/pmic_light.c
new file mode 100644
index 000000000000..ae02430a1981
--- /dev/null
+++ b/drivers/mxc/pmic/mc13892/pmic_light.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mc13892/pmic_light.c
+ * @brief This is the main file of PMIC(mc13783) Light and Backlight driver.
+ *
+ * @ingroup PMIC_LIGHT
+ */
+
+/*
+ * Includes
+ */
+#define DEBUG
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/pmic_light.h>
+#include <linux/pmic_status.h>
+
+#define BIT_CL_MAIN_LSH 9
+#define BIT_CL_AUX_LSH 21
+#define BIT_CL_KEY_LSH 9
+#define BIT_CL_RED_LSH 9
+#define BIT_CL_GREEN_LSH 21
+#define BIT_CL_BLUE_LSH 9
+
+#define BIT_CL_MAIN_WID 3
+#define BIT_CL_AUX_WID 3
+#define BIT_CL_KEY_WID 3
+#define BIT_CL_RED_WID 3
+#define BIT_CL_GREEN_WID 3
+#define BIT_CL_BLUE_WID 3
+
+#define BIT_DC_MAIN_LSH 3
+#define BIT_DC_AUX_LSH 15
+#define BIT_DC_KEY_LSH 3
+#define BIT_DC_RED_LSH 3
+#define BIT_DC_GREEN_LSH 15
+#define BIT_DC_BLUE_LSH 3
+
+#define BIT_DC_MAIN_WID 6
+#define BIT_DC_AUX_WID 6
+#define BIT_DC_KEY_WID 6
+#define BIT_DC_RED_WID 6
+#define BIT_DC_GREEN_WID 6
+#define BIT_DC_BLUE_WID 6
+
+#define BIT_RP_MAIN_LSH 2
+#define BIT_RP_AUX_LSH 14
+#define BIT_RP_KEY_LSH 2
+#define BIT_RP_RED_LSH 2
+#define BIT_RP_GREEN_LSH 14
+#define BIT_RP_BLUE_LSH 2
+
+#define BIT_RP_MAIN_WID 1
+#define BIT_RP_AUX_WID 1
+#define BIT_RP_KEY_WID 1
+#define BIT_RP_RED_WID 1
+#define BIT_RP_GREEN_WID 1
+#define BIT_RP_BLUE_WID 1
+
+#define BIT_HC_MAIN_LSH 1
+#define BIT_HC_AUX_LSH 13
+#define BIT_HC_KEY_LSH 1
+
+#define BIT_HC_MAIN_WID 1
+#define BIT_HC_AUX_WID 1
+#define BIT_HC_KEY_WID 1
+
+#define BIT_BP_RED_LSH 0
+#define BIT_BP_GREEN_LSH 12
+#define BIT_BP_BLUE_LSH 0
+
+#define BIT_BP_RED_WID 2
+#define BIT_BP_GREEN_WID 2
+#define BIT_BP_BLUE_WID 2
+
+int pmic_light_init_reg(void)
+{
+ CHECK_ERROR(pmic_write_reg(REG_LED_CTL0, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_LED_CTL1, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_LED_CTL2, 0, PMIC_ALL_BITS));
+ CHECK_ERROR(pmic_write_reg(REG_LED_CTL3, 0, PMIC_ALL_BITS));
+
+ return 0;
+}
+
+static int pmic_light_suspend(struct platform_device *dev, pm_message_t state)
+{
+ return 0;
+};
+
+static int pmic_light_resume(struct platform_device *pdev)
+{
+ return 0;
+};
+
+PMIC_STATUS mc13892_bklit_set_hi_current(enum lit_channel channel, int mode)
+{
+ unsigned int mask;
+ unsigned int value;
+ int reg;
+
+ switch (channel) {
+ case LIT_MAIN:
+ value = BITFVAL(BIT_HC_MAIN, mode);
+ mask = BITFMASK(BIT_HC_MAIN);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_AUX:
+ value = BITFVAL(BIT_HC_AUX, mode);
+ mask = BITFMASK(BIT_HC_AUX);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_KEY:
+ value = BITFVAL(BIT_HC_KEY, mode);
+ mask = BITFMASK(BIT_HC_KEY);
+ reg = REG_LED_CTL1;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ CHECK_ERROR(pmic_write_reg(reg, value, mask));
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_get_hi_current(enum lit_channel channel, int *mode)
+{
+ unsigned int mask;
+ int reg;
+
+ switch (channel) {
+ case LIT_MAIN:
+ mask = BITFMASK(BIT_HC_MAIN);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_AUX:
+ mask = BITFMASK(BIT_HC_AUX);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_KEY:
+ mask = BITFMASK(BIT_HC_KEY);
+ reg = REG_LED_CTL1;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, mode, mask));
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_set_current(enum lit_channel channel,
+ unsigned char level)
+{
+ unsigned int mask;
+ unsigned int value;
+ int reg;
+
+ if (level > LIT_CURR_HI_42)
+ return PMIC_PARAMETER_ERROR;
+ else if (level >= LIT_CURR_HI_0) {
+ CHECK_ERROR(mc13892_bklit_set_hi_current(channel, 1));
+ level -= LIT_CURR_HI_0;
+ }
+
+ switch (channel) {
+ case LIT_MAIN:
+ value = BITFVAL(BIT_CL_MAIN, level);
+ mask = BITFMASK(BIT_CL_MAIN);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_AUX:
+ value = BITFVAL(BIT_CL_AUX, level);
+ mask = BITFMASK(BIT_CL_AUX);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_KEY:
+ value = BITFVAL(BIT_CL_KEY, level);
+ mask = BITFMASK(BIT_CL_KEY);
+ reg = REG_LED_CTL1;
+ break;
+ case LIT_RED:
+ value = BITFVAL(BIT_CL_RED, level);
+ mask = BITFMASK(BIT_CL_RED);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_GREEN:
+ value = BITFVAL(BIT_CL_GREEN, level);
+ mask = BITFMASK(BIT_CL_GREEN);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_BLUE:
+ value = BITFVAL(BIT_CL_BLUE, level);
+ mask = BITFMASK(BIT_CL_BLUE);
+ reg = REG_LED_CTL3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ CHECK_ERROR(pmic_write_reg(reg, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_get_current(enum lit_channel channel,
+ unsigned char *level)
+{
+ unsigned int reg_value = 0;
+ unsigned int mask = 0;
+ int reg, mode;
+
+ CHECK_ERROR(mc13892_bklit_get_hi_current(channel, &mode));
+
+ switch (channel) {
+ case LIT_MAIN:
+ mask = BITFMASK(BIT_CL_MAIN);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_AUX:
+ mask = BITFMASK(BIT_CL_AUX);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_KEY:
+ mask = BITFMASK(BIT_CL_KEY);
+ reg = REG_LED_CTL1;
+ break;
+ case LIT_RED:
+ mask = BITFMASK(BIT_CL_RED);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_GREEN:
+ mask = BITFMASK(BIT_CL_GREEN);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_BLUE:
+ mask = BITFMASK(BIT_CL_BLUE);
+ reg = REG_LED_CTL3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &reg_value, mask));
+
+ switch (channel) {
+ case LIT_MAIN:
+ *level = BITFEXT(reg_value, BIT_CL_MAIN);
+ break;
+ case LIT_AUX:
+ *level = BITFEXT(reg_value, BIT_CL_AUX);
+ break;
+ case LIT_KEY:
+ *level = BITFEXT(reg_value, BIT_CL_KEY);
+ break;
+ case LIT_RED:
+ *level = BITFEXT(reg_value, BIT_CL_RED);
+ break;
+ case LIT_GREEN:
+ *level = BITFEXT(reg_value, BIT_CL_GREEN);
+ break;
+ case LIT_BLUE:
+ *level = BITFEXT(reg_value, BIT_CL_BLUE);
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ if (mode == 1)
+ *level += LIT_CURR_HI_0;
+
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_set_dutycycle(enum lit_channel channel,
+ unsigned char dc)
+{
+ unsigned int mask;
+ unsigned int value;
+ int reg;
+
+ switch (channel) {
+ case LIT_MAIN:
+ value = BITFVAL(BIT_DC_MAIN, dc);
+ mask = BITFMASK(BIT_DC_MAIN);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_AUX:
+ value = BITFVAL(BIT_DC_AUX, dc);
+ mask = BITFMASK(BIT_DC_AUX);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_KEY:
+ value = BITFVAL(BIT_DC_KEY, dc);
+ mask = BITFMASK(BIT_DC_KEY);
+ reg = REG_LED_CTL1;
+ break;
+ case LIT_RED:
+ value = BITFVAL(BIT_DC_RED, dc);
+ mask = BITFMASK(BIT_DC_RED);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_GREEN:
+ value = BITFVAL(BIT_DC_GREEN, dc);
+ mask = BITFMASK(BIT_DC_GREEN);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_BLUE:
+ value = BITFVAL(BIT_DC_BLUE, dc);
+ mask = BITFMASK(BIT_DC_BLUE);
+ reg = REG_LED_CTL3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ CHECK_ERROR(pmic_write_reg(reg, value, mask));
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_get_dutycycle(enum lit_channel channel,
+ unsigned char *dc)
+{
+ unsigned int mask;
+ int reg;
+ unsigned int reg_value = 0;
+
+ switch (channel) {
+ case LIT_MAIN:
+ mask = BITFMASK(BIT_DC_MAIN);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_AUX:
+ mask = BITFMASK(BIT_DC_AUX);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_KEY:
+ mask = BITFMASK(BIT_DC_KEY);
+ reg = REG_LED_CTL1;
+ break;
+ case LIT_RED:
+ mask = BITFMASK(BIT_DC_RED);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_GREEN:
+ mask = BITFMASK(BIT_DC_GREEN);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_BLUE:
+ mask = BITFMASK(BIT_DC_BLUE);
+ reg = REG_LED_CTL3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &reg_value, mask));
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_set_ramp(enum lit_channel channel, int flag)
+{
+ unsigned int mask;
+ unsigned int value;
+ int reg;
+
+ switch (channel) {
+ case LIT_MAIN:
+ value = BITFVAL(BIT_RP_MAIN, flag);
+ mask = BITFMASK(BIT_RP_MAIN);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_AUX:
+ value = BITFVAL(BIT_RP_AUX, flag);
+ mask = BITFMASK(BIT_RP_AUX);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_KEY:
+ value = BITFVAL(BIT_RP_KEY, flag);
+ mask = BITFMASK(BIT_RP_KEY);
+ reg = REG_LED_CTL1;
+ break;
+ case LIT_RED:
+ value = BITFVAL(BIT_RP_RED, flag);
+ mask = BITFMASK(BIT_RP_RED);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_GREEN:
+ value = BITFVAL(BIT_RP_GREEN, flag);
+ mask = BITFMASK(BIT_RP_GREEN);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_BLUE:
+ value = BITFVAL(BIT_RP_BLUE, flag);
+ mask = BITFMASK(BIT_RP_BLUE);
+ reg = REG_LED_CTL3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ CHECK_ERROR(pmic_write_reg(reg, value, mask));
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_get_ramp(enum lit_channel channel, int *flag)
+{
+ unsigned int mask;
+ int reg;
+
+ switch (channel) {
+ case LIT_MAIN:
+ mask = BITFMASK(BIT_RP_MAIN);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_AUX:
+ mask = BITFMASK(BIT_RP_AUX);
+ reg = REG_LED_CTL0;
+ break;
+ case LIT_KEY:
+ mask = BITFMASK(BIT_RP_KEY);
+ reg = REG_LED_CTL1;
+ break;
+ case LIT_RED:
+ mask = BITFMASK(BIT_RP_RED);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_GREEN:
+ mask = BITFMASK(BIT_RP_GREEN);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_BLUE:
+ mask = BITFMASK(BIT_RP_BLUE);
+ reg = REG_LED_CTL3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, flag, mask));
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_set_blink_p(enum lit_channel channel, int period)
+{
+ unsigned int mask;
+ unsigned int value;
+ int reg;
+
+ switch (channel) {
+ case LIT_RED:
+ value = BITFVAL(BIT_BP_RED, period);
+ mask = BITFMASK(BIT_BP_RED);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_GREEN:
+ value = BITFVAL(BIT_BP_GREEN, period);
+ mask = BITFMASK(BIT_BP_GREEN);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_BLUE:
+ value = BITFVAL(BIT_BP_BLUE, period);
+ mask = BITFMASK(BIT_BP_BLUE);
+ reg = REG_LED_CTL3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+ CHECK_ERROR(pmic_write_reg(reg, value, mask));
+ return PMIC_SUCCESS;
+}
+
+PMIC_STATUS mc13892_bklit_get_blink_p(enum lit_channel channel, int *period)
+{
+ unsigned int mask;
+ int reg;
+
+ switch (channel) {
+ case LIT_RED:
+ mask = BITFMASK(BIT_BP_RED);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_GREEN:
+ mask = BITFMASK(BIT_BP_GREEN);
+ reg = REG_LED_CTL2;
+ break;
+ case LIT_BLUE:
+ mask = BITFMASK(BIT_BP_BLUE);
+ reg = REG_LED_CTL3;
+ break;
+ default:
+ return PMIC_PARAMETER_ERROR;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, period, mask));
+ return PMIC_SUCCESS;
+}
+
+EXPORT_SYMBOL(mc13892_bklit_set_current);
+EXPORT_SYMBOL(mc13892_bklit_get_current);
+EXPORT_SYMBOL(mc13892_bklit_set_dutycycle);
+EXPORT_SYMBOL(mc13892_bklit_get_dutycycle);
+EXPORT_SYMBOL(mc13892_bklit_set_ramp);
+EXPORT_SYMBOL(mc13892_bklit_get_ramp);
+EXPORT_SYMBOL(mc13892_bklit_set_blink_p);
+EXPORT_SYMBOL(mc13892_bklit_get_blink_p);
+
+static int pmic_light_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#ifdef DEBUG
+static ssize_t lit_info(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return 0;
+}
+
+enum {
+ SET_CURR = 0,
+ SET_DC,
+ SET_RAMP,
+ SET_BP,
+ SET_CH,
+ LIT_CMD_MAX
+};
+
+static const char *const lit_cmd[LIT_CMD_MAX] = {
+ [SET_CURR] = "cur",
+ [SET_DC] = "dc",
+ [SET_RAMP] = "ra",
+ [SET_BP] = "bp",
+ [SET_CH] = "ch"
+};
+
+static int cmd(unsigned int index, int value)
+{
+ static int ch = LIT_MAIN;
+ int ret = 0;
+
+ switch (index) {
+ case SET_CH:
+ ch = value;
+ break;
+ case SET_CURR:
+ pr_debug("set %d cur %d\n", ch, value);
+ ret = mc13892_bklit_set_current(ch, value);
+ break;
+ case SET_DC:
+ pr_debug("set %d dc %d\n", ch, value);
+ ret = mc13892_bklit_set_dutycycle(ch, value);
+ break;
+ case SET_RAMP:
+ pr_debug("set %d ramp %d\n", ch, value);
+ ret = mc13892_bklit_set_ramp(ch, value);
+ break;
+ case SET_BP:
+ pr_debug("set %d bp %d\n", ch, value);
+ ret = mc13892_bklit_set_blink_p(ch, value);
+ break;
+ default:
+ pr_debug("error command\n");
+ break;
+ }
+
+ if (ret == PMIC_SUCCESS)
+ pr_debug("command exec successfully!\n");
+
+ return 0;
+}
+
+static ssize_t lit_ctl(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int state = 0;
+ const char *const *s;
+ char *p, *q;
+ int error;
+ int len, value = 0;
+
+ pr_debug("lit_ctl\n");
+
+ q = NULL;
+ q = memchr(buf, ' ', count);
+
+ if (q != NULL) {
+ len = q - buf;
+ q += 1;
+ value = simple_strtoul(q, NULL, 10);
+ } else {
+ p = memchr(buf, '\n', count);
+ len = p ? p - buf : count;
+ }
+
+ for (s = &lit_cmd[state]; state < LIT_CMD_MAX; s++, state++) {
+ if (*s && !strncmp(buf, *s, len))
+ break;
+ }
+ if (state < LIT_CMD_MAX && *s)
+ error = cmd(state, value);
+ else
+ error = -EINVAL;
+
+ return count;
+}
+
+#else
+static ssize_t lit_info(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return 0;
+}
+
+static ssize_t lit_ctl(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return count;
+}
+
+#endif
+
+static DEVICE_ATTR(lit, 0644, lit_info, lit_ctl);
+
+static int pmic_light_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ pr_debug("PMIC ADC start probe\n");
+ ret = device_create_file(&(pdev->dev), &dev_attr_lit);
+ if (ret) {
+ pr_debug("Can't create device file!\n");
+ return -ENODEV;
+ }
+
+ pmic_light_init_reg();
+
+ pr_debug("PMIC Light successfully loaded\n");
+ return 0;
+}
+
+static struct platform_driver pmic_light_driver_ldm = {
+ .driver = {
+ .name = "pmic_light",
+ },
+ .suspend = pmic_light_suspend,
+ .resume = pmic_light_resume,
+ .probe = pmic_light_probe,
+ .remove = pmic_light_remove,
+};
+
+/*
+ * Initialization and Exit
+ */
+
+static int __init pmic_light_init(void)
+{
+ pr_debug("PMIC Light driver loading...\n");
+ return platform_driver_register(&pmic_light_driver_ldm);
+}
+static void __exit pmic_light_exit(void)
+{
+ platform_driver_unregister(&pmic_light_driver_ldm);
+ pr_debug("PMIC Light driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall(pmic_light_init);
+module_exit(pmic_light_exit);
+
+MODULE_DESCRIPTION("PMIC_LIGHT");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/security/Kconfig b/drivers/mxc/security/Kconfig
new file mode 100644
index 000000000000..0e5e3fe2ed7a
--- /dev/null
+++ b/drivers/mxc/security/Kconfig
@@ -0,0 +1,64 @@
+menu "MXC Security Drivers"
+
+config MXC_SECURITY_SCC
+ tristate "MXC SCC Driver"
+ default n
+ ---help---
+ This module contains the core API's for accessing the SCC module.
+ If you are unsure about this, say N here.
+
+config MXC_SECURITY_SCC2
+ tristate "MXC SCC2 Driver"
+ depends on ARCH_MX37 || ARCH_MX51
+ default n
+ ---help---
+ This module contains the core API's for accessing the SCC2 module.
+ If you are unsure about this, say N here.
+
+config SCC_DEBUG
+ bool "MXC SCC Module debugging"
+ depends on MXC_SECURITY_SCC || MXC_SECURITY_SCC2
+ ---help---
+ This is an option for use by developers; most people should
+ say N here. This enables SCC module debugging.
+
+config MXC_SECURITY_RNG
+ tristate "MXC RNG Driver"
+ depends on ARCH_MXC
+ depends on !ARCH_MXC91321
+ depends on !ARCH_MX27
+ default n
+ select MXC_SECURITY_CORE
+ ---help---
+ This module contains the core API's for accessing the RNG module.
+ If you are unsure about this, say N here.
+
+config MXC_RNG_TEST_DRIVER
+ bool "MXC RNG debug register"
+ depends on MXC_SECURITY_RNG
+ default n
+ ---help---
+ This option enables the RNG kcore driver to provide peek-poke facility
+ into the RNG device registers. Enable this, only for development and
+ testing purposes.
+config MXC_RNG_DEBUG
+ bool "MXC RNG Module Dubugging"
+ depends on MXC_SECURITY_RNG
+ default n
+ ---help---
+ This is an option for use by developers; most people should
+ say N here. This enables RNG module debugging.
+
+config MXC_DRYICE
+ tristate "MXC DryIce Driver"
+ depends on ARCH_MX25
+ default n
+ ---help---
+ This module contains the core API's for accessing the DryIce module.
+ If you are unsure about this, say N here.
+
+if (ARCH_MX37 || ARCH_MX51 || ARCH_MX27)
+source "drivers/mxc/security/sahara2/Kconfig"
+endif
+
+endmenu
diff --git a/drivers/mxc/security/Makefile b/drivers/mxc/security/Makefile
new file mode 100644
index 000000000000..f643a09a423d
--- /dev/null
+++ b/drivers/mxc/security/Makefile
@@ -0,0 +1,11 @@
+# Makefile for the Linux MXC Security API
+ifeq ($( SCC_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+EXTRA_CFLAGS += -DMXC -DLINUX_KERNEL
+
+obj-$(CONFIG_MXC_SECURITY_SCC2) += scc2_driver.o
+obj-$(CONFIG_MXC_SECURITY_SCC) += mxc_scc.o
+obj-$(CONFIG_MXC_SECURITY_RNG) += rng/
+obj-$(CONFIG_MXC_SAHARA) += sahara2/
+obj-$(CONFIG_MXC_DRYICE) += dryice.o
diff --git a/drivers/mxc/security/dryice-regs.h b/drivers/mxc/security/dryice-regs.h
new file mode 100644
index 000000000000..b8d3858bdb7d
--- /dev/null
+++ b/drivers/mxc/security/dryice-regs.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __DRYICE_REGS_H__
+#define __DRYICE_REGS_H__
+
+/***********************************************************************
+ * DryIce Register Definitions
+ ***********************************************************************/
+
+/* DryIce Time Counter MSB Reg */
+#define DTCMR 0x00
+
+/* DryIce Time Counter LSB Reg */
+#define DTCLR 0x04
+
+/* DryIce Clock Alarm MSB Reg */
+#define DCAMR 0x08
+
+/* DryIce Clock Alarm LSB Reg */
+#define DCALR 0x0c
+
+/* DryIce Control Reg */
+#define DCR 0x10
+#define DCR_TDCHL (1 << 30) /* Tamper Detect Config Hard Lock */
+#define DCR_TDCSL (1 << 29) /* Tamper Detect COnfig Soft Lock */
+#define DCR_KSHL (1 << 28) /* Key Select Hard Lock */
+#define DCR_KSSL (1 << 27) /* Key Select Soft Lock */
+#define DCR_RKHL (1 << 26) /* Random Key Hard Lock */
+#define DCR_RKSL (1 << 25) /* Random Key Soft Lock */
+#define DCR_PKRHL (1 << 24) /* Programmed Key Read Hard Lock */
+#define DCR_PKRSL (1 << 23) /* Programmed Key Read Soft Lock */
+#define DCR_PKWHL (1 << 22) /* Programmed Key Write Hard Lock */
+#define DCR_PKWSL (1 << 21) /* Programmed Key Write Soft Lock */
+#define DCR_MCHL (1 << 20) /* Monotonic Counter Hard Lock */
+#define DCR_MCSL (1 << 19) /* Monotonic Counter Soft Lock */
+#define DCR_TCHL (1 << 18) /* Time Counter Hard Lock */
+#define DCR_TCSL (1 << 17) /* Time Counter Soft Lock */
+#define DCR_FSHL (1 << 16) /* Failure State Hard Lock */
+#define DCR_NSA (1 << 15) /* Non-Secure Access */
+#define DCR_OSCB (1 << 14) /* Oscillator Bypass */
+#define DCR_APE (1 << 4) /* Alarm Pin Enable */
+#define DCR_TCE (1 << 3) /* Time Counter Enable */
+#define DCR_MCE (1 << 2) /* Monotonic Counter Enable */
+#define DCR_SWR (1 << 0) /* Software Reset (w/o) */
+
+/* DryIce Status Reg */
+#define DSR 0x14
+#define DSR_WTD (1 << 23) /* Wire-mesh Tampering Detected */
+#define DSR_ETBD (1 << 22) /* External Tampering B Detected */
+#define DSR_ETAD (1 << 21) /* External Tampering A Detected */
+#define DSR_EBD (1 << 20) /* External Boot Detected */
+#define DSR_SAD (1 << 19) /* Security Alarm Detected */
+#define DSR_TTD (1 << 18) /* Temperature Tampering Detected */
+#define DSR_CTD (1 << 17) /* Clock Tampering Detected */
+#define DSR_VTD (1 << 16) /* Voltage Tampering Detected */
+#define DSR_KBF (1 << 11) /* Key Busy Flag */
+#define DSR_WBF (1 << 10) /* Write Busy Flag */
+#define DSR_WNF (1 << 9) /* Write Next Flag */
+#define DSR_WCF (1 << 8) /* Write Complete Flag */
+#define DSR_WEF (1 << 7) /* Write Error Flag */
+#define DSR_RKE (1 << 6) /* Random Key Error */
+#define DSR_RKV (1 << 5) /* Random Key Valid */
+#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
+#define DSR_MCO (1 << 3) /* Monotonic Counter Overflow */
+#define DSR_TCO (1 << 2) /* Time Counter Overflow */
+#define DSR_NVF (1 << 1) /* Non-Valid Flag */
+#define DSR_SVF (1 << 0) /* Security Violation Flag */
+
+#define DSR_TAMPER_BITS (DSR_WTD | DSR_ETBD | DSR_ETAD | DSR_EBD | DSR_SAD | \
+ DSR_TTD | DSR_CTD | DSR_VTD | DSR_MCO | DSR_TCO)
+
+/* ensure that external tamper defs match register bits */
+#if DSR_WTD != DI_TAMPER_EVENT_WTD
+#error "Mismatch between DSR_WTD and DI_TAMPER_EVENT_WTD"
+#endif
+#if DSR_ETBD != DI_TAMPER_EVENT_ETBD
+#error "Mismatch between DSR_ETBD and DI_TAMPER_EVENT_ETBD"
+#endif
+#if DSR_ETAD != DI_TAMPER_EVENT_ETAD
+#error "Mismatch between DSR_ETAD and DI_TAMPER_EVENT_ETAD"
+#endif
+#if DSR_EBD != DI_TAMPER_EVENT_EBD
+#error "Mismatch between DSR_EBD and DI_TAMPER_EVENT_EBD"
+#endif
+#if DSR_SAD != DI_TAMPER_EVENT_SAD
+#error "Mismatch between DSR_SAD and DI_TAMPER_EVENT_SAD"
+#endif
+#if DSR_TTD != DI_TAMPER_EVENT_TTD
+#error "Mismatch between DSR_TTD and DI_TAMPER_EVENT_TTD"
+#endif
+#if DSR_CTD != DI_TAMPER_EVENT_CTD
+#error "Mismatch between DSR_CTD and DI_TAMPER_EVENT_CTD"
+#endif
+#if DSR_VTD != DI_TAMPER_EVENT_VTD
+#error "Mismatch between DSR_VTD and DI_TAMPER_EVENT_VTD"
+#endif
+#if DSR_MCO != DI_TAMPER_EVENT_MCO
+#error "Mismatch between DSR_MCO and DI_TAMPER_EVENT_MCO"
+#endif
+#if DSR_TCO != DI_TAMPER_EVENT_TCO
+#error "Mismatch between DSR_TCO and DI_TAMPER_EVENT_TCO"
+#endif
+
+/* DryIce Interrupt Enable Reg */
+#define DIER 0x18
+#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
+#define DIER_RKIE (1 << 5) /* Random Key Interrupt Enable */
+#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
+#define DIER_MOIE (1 << 3) /* Monotonic Overflow Interrupt En */
+#define DIER_TOIE (1 << 2) /* Time Overflow Interrupt Enable */
+#define DIER_SVIE (1 << 0) /* Security Violation Interrupt En */
+
+/* DryIce Monotonic Counter Reg */
+#define DMCR 0x1c
+
+/* DryIce Key Select Reg */
+#define DKSR 0x20
+#define DKSR_IIM_KEY 0x0
+#define DKSR_PROG_KEY 0x4
+#define DKSR_RAND_KEY 0x5
+#define DKSR_PROG_XOR_IIM_KEY 0x6
+#define DKSR_RAND_XOR_IIM_KEY 0x7
+
+/* DryIce Key Control Reg */
+#define DKCR 0x24
+#define DKCR_LRK (1 << 0) /* Load Random Key */
+
+/* DryIce Tamper Configuration Reg */
+#define DTCR 0x28
+#define DTCR_ETGFB_SHIFT 27 /* Ext Tamper Glitch Filter B */
+#define DTCR_ETGFB_MASK 0xf8000000
+#define DTCR_ETGFA_SHIFT 22 /* Ext Tamper Glitch Filter A */
+#define DTCR_ETGFA_MASK 0x07c00000
+#define DTCR_WTGF_SHIFT 17 /* Wire-mesh Tamper Glitch Filter */
+#define DTCR_WTGF_MASK 0x003e0000
+#define DTCR_WGFE (1 << 16) /* Wire-mesh Glitch Filter Enable */
+#define DTCR_SAOE (1 << 15) /* Security Alarm Output Enable */
+#define DTCR_MOE (1 << 9) /* Monotonic Overflow Enable */
+#define DTCR_TOE (1 << 8) /* Time Overflow Enable */
+#define DTCR_WTE (1 << 7) /* Wire-mesh Tampering Enable */
+#define DTCR_ETBE (1 << 6) /* External Tampering B Enable */
+#define DTCR_ETAE (1 << 5) /* External Tampering A Enable */
+#define DTCR_EBE (1 << 4) /* External Boot Enable */
+#define DTCR_SAIE (1 << 3) /* Security Alarm Input Enable */
+#define DTCR_TTE (1 << 2) /* Temperature Tamper Enable */
+#define DTCR_CTE (1 << 1) /* Clock Tamper Enable */
+#define DTCR_VTE (1 << 0) /* Voltage Tamper Enable */
+
+/* DryIce Analog Configuration Reg */
+#define DACR 0x2c
+#define DACR_VRC_SHIFT 6 /* Voltage Reference Configuration */
+#define DACR_VRC_MASK 0x000001c0
+#define DACR_HTDC_SHIFT 3 /* High Temperature Detect Configuration */
+#define DACR_HTDC_MASK 0x00000038
+#define DACR_LTDC_SHIFT 0 /* Low Temperature Detect Configuration */
+#define DACR_LTDC_MASK 0x00000007
+
+/* DryIce General Purpose Reg */
+#define DGPR 0x3c
+
+/* DryIce Programmed Key0-7 Regs */
+#define DPKR0 0x40
+#define DPKR1 0x44
+#define DPKR2 0x48
+#define DPKR3 0x4c
+#define DPKR4 0x50
+#define DPKR5 0x54
+#define DPKR6 0x58
+#define DPKR7 0x5c
+
+/* DryIce Random Key0-7 Regs */
+#define DRKR0 0x60
+#define DRKR1 0x64
+#define DRKR2 0x68
+#define DRKR3 0x6c
+#define DRKR4 0x70
+#define DRKR5 0x74
+#define DRKR6 0x78
+#define DRKR7 0x7c
+
+#define DI_ADDRESS_RANGE (DRKR7 + 4)
+
+/*
+ * this doesn't really belong here but the
+ * portability layer doesn't include it
+ */
+#ifdef LINUX_KERNEL
+#define EXTERN_SYMBOL(symbol) EXPORT_SYMBOL(symbol)
+#else
+#define EXTERN_SYMBOL(symbol) do {} while (0)
+#endif
+
+#endif /* __DRYICE_REGS_H__ */
diff --git a/drivers/mxc/security/dryice.c b/drivers/mxc/security/dryice.c
new file mode 100644
index 000000000000..f7cefb6ae599
--- /dev/null
+++ b/drivers/mxc/security/dryice.c
@@ -0,0 +1,1123 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+#undef DI_DEBUG /* enable debug messages */
+#undef DI_DEBUG_REGIO /* show register read/write */
+#undef DI_TESTING /* include test code */
+
+#ifdef DI_DEBUG
+#define di_debug(fmt, arg...) os_printk(KERN_INFO fmt, ##arg)
+#else
+#define di_debug(fmt, arg...) do {} while (0)
+#endif
+
+#define di_info(fmt, arg...) os_printk(KERN_INFO fmt, ##arg)
+#define di_warn(fmt, arg...) os_printk(KERN_WARNING fmt, ##arg)
+
+#include "sahara2/include/portable_os.h"
+#include "dryice.h"
+#include "dryice-regs.h"
+
+/* mask of the lock-related function flags */
+#define DI_FUNC_LOCK_FLAGS (DI_FUNC_FLAG_READ_LOCK | \
+ DI_FUNC_FLAG_WRITE_LOCK | \
+ DI_FUNC_FLAG_HARD_LOCK)
+
+/*
+ * dryice hardware states
+ */
+enum di_states {
+ DI_STATE_VALID = 0,
+ DI_STATE_NON_VALID,
+ DI_STATE_FAILURE,
+};
+
+/*
+ * todo list actions
+ */
+enum todo_actions {
+ TODO_ACT_WRITE_VAL,
+ TODO_ACT_WRITE_PTR,
+ TODO_ACT_WRITE_PTR32,
+ TODO_ACT_ASSIGN,
+ TODO_ACT_WAIT_RKG,
+};
+
+/*
+ * todo list status
+ */
+enum todo_status {
+ TODO_ST_LOADING,
+ TODO_ST_READY,
+ TODO_ST_PEND_WCF,
+ TODO_ST_PEND_RKG,
+ TODO_ST_DONE,
+};
+
+OS_DEV_INIT_DCL(dryice_init)
+OS_DEV_SHUTDOWN_DCL(dryice_exit)
+OS_DEV_ISR_DCL(dryice_norm_irq)
+OS_WAIT_OBJECT(done_queue);
+OS_WAIT_OBJECT(exit_queue);
+
+struct dryice_data {
+ int busy; /* enforce exclusive access */
+ os_lock_t busy_lock;
+ int exit_flag; /* don't start new operations */
+
+ uint32_t baseaddr; /* physical base address */
+ void *ioaddr; /* virtual base address */
+
+ /* interrupt handling */
+ struct irq_struct {
+ os_interrupt_id_t irq;
+ int set;
+ } irq_norm, irq_sec;
+
+ struct clk *clk; /* clock control */
+
+ int key_programmed; /* key has been programmed */
+ int key_selected; /* key has been selected */
+
+ /* callback function and cookie */
+ void (*cb_func)(di_return_t rc, unsigned long cookie);
+ unsigned long cb_cookie;
+} *di = NULL;
+
+#define TODO_LIST_LEN 12
+static struct {
+ struct td {
+ enum todo_actions action;
+ uint32_t src;
+ uint32_t dst;
+ int num;
+ } list[TODO_LIST_LEN];
+ int cur; /* current todo pointer */
+ int num; /* number of todo's on the list */
+ int async; /* non-zero if list is async */
+ int status; /* current status of the list */
+ di_return_t rc; /* return code generated by the list */
+} todo;
+
+/*
+ * dryice register read/write functions
+ */
+#ifdef DI_DEBUG_REGIO
+static uint32_t di_read(int reg)
+{
+ uint32_t val = os_read32(di->ioaddr + (reg));
+ di_info("di_read(0x%02x) = 0x%08x\n", reg, val);
+
+ return val;
+}
+
+static void di_write(uint32_t val, int reg)
+{
+ di_info("dryice_write_reg(0x%08x, 0x%02x)\n", val, reg);
+ os_write32(di->ioaddr + (reg), val);
+}
+#else
+#define di_read(reg) os_read32(di->ioaddr + (reg))
+#define di_write(val, reg) os_write32(di->ioaddr + (reg), val);
+#endif
+
+/*
+ * set the dryice busy flag atomically, allowing
+ * for case where the driver is trying to exit.
+ */
+static int di_busy_set(void)
+{
+ os_lock_context_t context;
+ int rc = 0;
+
+ os_lock_save_context(di->busy_lock, context);
+ if (di->exit_flag || di->busy)
+ rc = 1;
+ else
+ di->busy = 1;
+ os_unlock_restore_context(di->busy_lock, context);
+
+ return rc;
+}
+
+/*
+ * clear the dryice busy flag
+ */
+static inline void di_busy_clear(void)
+{
+ /* don't acquire the lock because the race is benign */
+ di->busy = 0;
+
+ if (di->exit_flag)
+ os_wake_sleepers(exit_queue);
+}
+
+/*
+ * return the current state of dryice
+ * (valid, non-valid, or failure)
+ */
+static enum di_states di_state(void)
+{
+ enum di_states state = DI_STATE_VALID;
+ uint32_t dsr = di_read(DSR);
+
+ if (dsr & DSR_NVF)
+ state = DI_STATE_NON_VALID;
+ else if (dsr & DSR_SVF)
+ state = DI_STATE_FAILURE;
+
+ return state;
+}
+
+#define DI_WRITE_LOOP_CNT 0x1000
+/*
+ * the write-error flag is something that shouldn't get set
+ * during normal operation. if it's set something is terribly
+ * wrong. the best we can do is try to clear the bit and hope
+ * that dryice will recover. this situation is similar to an
+ * unexpected bus fault in terms of severity.
+ */
+static void try_to_clear_wef(void)
+{
+ int cnt;
+
+ while (1) {
+ di_write(DSR_WEF, DSR);
+ for (cnt = 0; cnt < DI_WRITE_LOOP_CNT; cnt++) {
+ if ((di_read(DSR) & DSR_WEF) == 0)
+ break;
+ }
+ di_warn("WARNING: DryIce cannot clear DSR_WEF "
+ "(Write Error Flag)!\n");
+ }
+}
+
+/*
+ * write a dryice register and loop, waiting for it
+ * to complete. use only during driver initialization.
+ * returns 0 on success or 1 on write failure.
+ */
+static int di_write_loop(uint32_t val, int reg)
+{
+ int rc = 0;
+ int cnt;
+
+ di_debug("FUNC: %s\n", __func__);
+ di_write(val, reg);
+
+ for (cnt = 0; cnt < DI_WRITE_LOOP_CNT; cnt++) {
+ uint32_t dsr = di_read(DSR);
+ if (dsr & DSR_WEF) {
+ try_to_clear_wef();
+ rc = 1;
+ }
+ if (dsr & DSR_WCF)
+ break;
+ }
+ di_debug("wait_write_loop looped %d times\n", cnt);
+ if (cnt == DI_WRITE_LOOP_CNT)
+ rc = 1;
+
+ if (rc)
+ di_warn("DryIce wait_write_done: WRITE ERROR!\n");
+ return rc;
+}
+
+/*
+ * initialize the todo list. must be called
+ * before adding items to the list.
+ */
+static void todo_init(int async_flag)
+{
+ di_debug("FUNC: %s\n", __func__);
+ todo.cur = 0;
+ todo.num = 0;
+ todo.async = async_flag;
+ todo.rc = 0;
+ todo.status = TODO_ST_LOADING;
+}
+
+/*
+ * perform the current action on the todo list
+ */
+#define TC todo.list[todo.cur]
+void todo_cur(void)
+{
+ di_debug("FUNC: %s[%d]\n", __func__, todo.cur);
+ switch (TC.action) {
+ case TODO_ACT_WRITE_VAL:
+ di_debug(" TODO_ACT_WRITE_VAL\n");
+ /* enable the write-completion interrupt */
+ todo.status = TODO_ST_PEND_WCF;
+ di_write(di_read(DIER) | DIER_WCIE, DIER);
+
+ di_write(TC.src, TC.dst);
+ break;
+
+ case TODO_ACT_WRITE_PTR32:
+ di_debug(" TODO_ACT_WRITE_PTR32\n");
+ /* enable the write-completion interrupt */
+ todo.status = TODO_ST_PEND_WCF;
+ di_write(di_read(DIER) | DIER_WCIE, DIER);
+
+ di_write(*(uint32_t *)TC.src, TC.dst);
+ break;
+
+ case TODO_ACT_WRITE_PTR:
+ {
+ uint8_t *p = (uint8_t *)TC.src;
+ uint32_t val = 0;
+ int num = TC.num;
+
+ di_debug(" TODO_ACT_WRITE_PTR\n");
+ while (num--)
+ val = (val << 8) | *p++;
+
+ /* enable the write-completion interrupt */
+ todo.status = TODO_ST_PEND_WCF;
+ di_write(di_read(DIER) | DIER_WCIE, DIER);
+
+ di_write(val, TC.dst);
+ }
+ break;
+
+ case TODO_ACT_ASSIGN:
+ di_debug(" TODO_ACT_ASSIGN\n");
+ switch (TC.num) {
+ case 1:
+ *(uint8_t *)TC.dst = TC.src;
+ break;
+ case 2:
+ *(uint16_t *)TC.dst = TC.src;
+ break;
+ case 4:
+ *(uint32_t *)TC.dst = TC.src;
+ break;
+ default:
+ di_warn("Unexpected size in TODO_ACT_ASSIGN\n");
+ break;
+ }
+ break;
+
+ case TODO_ACT_WAIT_RKG:
+ di_debug(" TODO_ACT_WAIT_RKG\n");
+ /* enable the random-key interrupt */
+ todo.status = TODO_ST_PEND_RKG;
+ di_write(di_read(DIER) | DIER_RKIE, DIER);
+ break;
+
+ default:
+ di_debug(" TODO_ACT_NOOP\n");
+ break;
+ }
+}
+
+/*
+ * called when done with the todo list.
+ * if async, it does the callback.
+ * if blocking, it wakes up the caller.
+ */
+static void todo_done(di_return_t rc)
+{
+ todo.rc = rc;
+ todo.status = TODO_ST_DONE;
+ if (todo.async) {
+ di_busy_clear();
+ if (di->cb_func)
+ di->cb_func(rc, di->cb_cookie);
+ } else
+ os_wake_sleepers(done_queue);
+}
+
+/*
+ * performs the actions sequentially from the todo list
+ * until it encounters an item that isn't ready.
+ */
+static void todo_run(void)
+{
+ di_debug("FUNC: %s\n", __func__);
+ while (todo.status == TODO_ST_READY) {
+ if (todo.cur == todo.num) {
+ todo_done(0);
+ break;
+ }
+ todo_cur();
+ if (todo.status != TODO_ST_READY)
+ break;
+ todo.cur++;
+ }
+}
+
+/*
+ * kick off the todo list by making it ready
+ */
+static void todo_start(void)
+{
+ di_debug("FUNC: %s\n", __func__);
+ todo.status = TODO_ST_READY;
+ todo_run();
+}
+
+/*
+ * blocking callers sleep here until the todo list is done
+ */
+static int todo_wait_done(void)
+{
+ di_debug("FUNC: %s\n", __func__);
+ os_sleep(done_queue, todo.status == TODO_ST_DONE, 0);
+
+ return todo.rc;
+}
+
+/*
+ * add a dryice register write to the todo list.
+ * the value to be written is supplied.
+ */
+#define todo_write_val(val, reg) \
+ todo_add(TODO_ACT_WRITE_VAL, val, reg, 0)
+
+/*
+ * add a dryice register write to the todo list.
+ * "size" bytes pointed to by addr will be written.
+ */
+#define todo_write_ptr(addr, reg, size) \
+ todo_add(TODO_ACT_WRITE_PTR, (uint32_t)addr, reg, size)
+
+/*
+ * add a dryice register write to the todo list.
+ * the word pointed to by addr will be written.
+ */
+#define todo_write_ptr32(addr, reg) \
+ todo_add(TODO_ACT_WRITE_PTR32, (uint32_t)addr, reg, 0)
+
+/*
+ * add a dryice memory write to the todo list.
+ * object can only have a size of 1, 2, or 4 bytes.
+ */
+#define todo_assign(var, val) \
+ todo_add(TODO_ACT_ASSIGN, val, (uint32_t)&(var), sizeof(var))
+
+#define todo_wait_rkg() \
+ todo_add(TODO_ACT_WAIT_RKG, 0, 0, 0)
+
+static void todo_add(int action, uint32_t src, uint32_t dst, int num)
+{
+ struct td *p = &todo.list[todo.num];
+
+ di_debug("FUNC: %s\n", __func__);
+ if (todo.num == TODO_LIST_LEN) {
+ di_warn("WARNING: DryIce todo-list overflow!\n");
+ return;
+ }
+ p->action = action;
+ p->src = src;
+ p->dst = dst;
+ p->num = num;
+ todo.num++;
+}
+
+#if defined(DI_DEBUG) || defined(DI_TESTING)
+/*
+ * print out the contents of the dryice status register
+ * with all the bits decoded
+ */
+static void show_dsr(const char *heading)
+{
+ uint32_t dsr = di_read(DSR);
+
+ di_info("%s\n", heading);
+ if (dsr & DSR_TAMPER_BITS) {
+ if (dsr & DSR_WTD)
+ di_info("Wire-mesh Tampering Detected\n");
+ if (dsr & DSR_ETBD)
+ di_info("External Tampering B Detected\n");
+ if (dsr & DSR_ETAD)
+ di_info("External Tampering A Detected\n");
+ if (dsr & DSR_EBD)
+ di_info("External Boot Detected\n");
+ if (dsr & DSR_SAD)
+ di_info("Security Alarm Detected\n");
+ if (dsr & DSR_TTD)
+ di_info("Temperature Tampering Detected\n");
+ if (dsr & DSR_CTD)
+ di_info("Clock Tampering Detected\n");
+ if (dsr & DSR_VTD)
+ di_info("Voltage Tampering Detected\n");
+ if (dsr & DSR_MCO)
+ di_info("Monotonic Counter Overflow\n");
+ if (dsr & DSR_TCO)
+ di_info("Time Counter Overflow\n");
+ } else
+ di_info("No Tamper Events Detected\n");
+
+ di_info("%d Key Busy Flag\n", !!(dsr & DSR_KBF));
+ di_info("%d Write Busy Flag\n", !!(dsr & DSR_WBF));
+ di_info("%d Write Next Flag\n", !!(dsr & DSR_WNF));
+ di_info("%d Write Complete Flag\n", !!(dsr & DSR_WCF));
+ di_info("%d Write Error Flag\n", !!(dsr & DSR_WEF));
+ di_info("%d Random Key Error\n", !!(dsr & DSR_RKE));
+ di_info("%d Random Key Valid\n", !!(dsr & DSR_RKV));
+ di_info("%d Clock Alarm Flag\n", !!(dsr & DSR_CAF));
+ di_info("%d Non-Valid Flag\n", !!(dsr & DSR_NVF));
+ di_info("%d Security Violation Flag\n", !!(dsr & DSR_SVF));
+}
+
+/*
+ * print out a key in hex
+ */
+static void print_key(const char *tag, uint8_t *key, int bits)
+{
+ int bytes = (bits + 7) / 8;
+
+ di_info("%s", tag);
+ while (bytes--)
+ os_printk("%02x", *key++);
+ os_printk("\n");
+}
+#endif /* defined(DI_DEBUG) || defined(DI_TESTING) */
+
+/*
+ * dryice normal interrupt service routine
+ */
+OS_DEV_ISR(dryice_norm_irq)
+{
+ /* save dryice status register */
+ uint32_t dsr = di_read(DSR);
+
+ if (dsr & DSR_WCF) {
+ /* disable the write-completion interrupt */
+ di_write(di_read(DIER) & ~DIER_WCIE, DIER);
+
+ if (todo.status == TODO_ST_PEND_WCF) {
+ if (dsr & DSR_WEF) {
+ try_to_clear_wef();
+ todo_done(DI_ERR_WRITE);
+ } else {
+ todo.cur++;
+ todo.status = TODO_ST_READY;
+ todo_run();
+ }
+ }
+ } else if (dsr & (DSR_RKV | DSR_RKE)) {
+ /* disable the random-key-gen interrupt */
+ di_write(di_read(DIER) & ~DIER_RKIE, DIER);
+
+ if (todo.status == TODO_ST_PEND_RKG) {
+ if (dsr & DSR_RKE)
+ todo_done(DI_ERR_FAIL);
+ else {
+ todo.cur++;
+ todo.status = TODO_ST_READY;
+ todo_run();
+ }
+ }
+ }
+
+ os_dev_isr_return(1);
+}
+
+/* write loop with error handling -- for init only */
+#define di_write_loop_goto(val, reg, rc, label) \
+ do {if (di_write_loop(val, reg)) \
+ {rc = OS_ERROR_FAIL_S; goto label; } } while (0)
+
+/*
+ * dryice driver initialization
+ */
+OS_DEV_INIT(dryice_init)
+{
+ di_return_t rc = 0;
+
+ di_info("MXC DryIce driver\n");
+
+ /* allocate memory */
+ di = os_alloc_memory(sizeof(*di), GFP_KERNEL);
+ if (di == NULL) {
+ rc = OS_ERROR_NO_MEMORY_S;
+ goto err_alloc;
+ }
+ memset(di, 0, sizeof(*di));
+ di->baseaddr = DRYICE_BASE_ADDR;
+ di->irq_norm.irq = MXC_INT_DRYICE_NORM;
+ di->irq_sec.irq = MXC_INT_DRYICE_SEC;
+
+ /* map i/o registers */
+ di->ioaddr = os_map_device(di->baseaddr, DI_ADDRESS_RANGE);
+ if (di->ioaddr == NULL) {
+ rc = OS_ERROR_FAIL_S;
+ goto err_iomap;
+ }
+
+ /* allocate locks */
+ di->busy_lock = os_lock_alloc_init();
+ if (di->busy_lock == NULL) {
+ rc = OS_ERROR_NO_MEMORY_S;
+ goto err_locks;
+ }
+
+ /* enable clocks (is there a portable way to do this?) */
+ di->clk = clk_get(NULL, "dryice_clk");
+ clk_enable(di->clk);
+
+ /* register for interrupts */
+ /* os_register_interrupt() dosen't support an option to make the
+ interrupt as shared. Replaced it with request_irq().*/
+ rc = request_irq(di->irq_norm.irq, dryice_norm_irq, IRQF_SHARED,
+ "dry_ice", di);
+ if (rc)
+ goto err_irqs;
+ else
+ di->irq_norm.set = 1;
+
+ /*
+ * DRYICE HARDWARE INIT
+ */
+
+#ifdef DI_DEBUG
+ show_dsr("DSR Pre-Initialization State");
+#endif
+
+ if (di_state() == DI_STATE_NON_VALID) {
+ uint32_t dsr = di_read(DSR);
+
+ di_debug("initializing from non-valid state\n");
+
+ /* clear security violation flag */
+ if (dsr & DSR_SVF)
+ di_write_loop_goto(DSR_SVF, DSR, rc, err_write);
+
+ /* clear tamper detect flags */
+ if (dsr & DSR_TAMPER_BITS)
+ di_write_loop_goto(DSR_TAMPER_BITS, DSR, rc, err_write);
+
+ /* initialize timers */
+ di_write_loop_goto(0, DTCLR, rc, err_write);
+ di_write_loop_goto(0, DTCMR, rc, err_write);
+ di_write_loop_goto(0, DMCR, rc, err_write);
+
+ /* clear non-valid flag */
+ di_write_loop_goto(DSR_NVF, DSR, rc, err_write);
+ }
+
+ /* set tamper events we are interested in watching */
+ di_write_loop_goto(DTCR_WTE | DTCR_ETBE | DTCR_ETAE, DTCR, rc,
+ err_write);
+#ifdef DI_DEBUG
+ show_dsr("DSR Post-Initialization State");
+#endif
+ os_dev_init_return(OS_ERROR_OK_S);
+
+err_write:
+ /* unregister interrupts */
+ if (di->irq_norm.set)
+ os_deregister_interrupt(di->irq_norm.irq);
+ if (di->irq_sec.set)
+ os_deregister_interrupt(di->irq_sec.irq);
+
+ /* turn off clocks (is there a portable way to do this?) */
+ clk_disable(di->clk);
+ clk_put(di->clk);
+
+err_irqs:
+ /* unallocate locks */
+ os_lock_deallocate(di->busy_lock);
+
+err_locks:
+ /* unmap i/o registers */
+ os_unmap_device(di->ioaddr, DI_ADDRESS_RANGE);
+
+err_iomap:
+ /* free the dryice struct */
+ os_free_memory(di);
+
+err_alloc:
+ os_dev_init_return(rc);
+}
+
+/*
+ * dryice driver exit routine
+ */
+OS_DEV_SHUTDOWN(dryice_exit)
+{
+ /* don't allow new operations */
+ di->exit_flag = 1;
+
+ /* wait for the current operation to complete */
+ os_sleep(exit_queue, di->busy == 0, 0);
+
+ /* unregister interrupts */
+ if (di->irq_norm.set)
+ os_deregister_interrupt(di->irq_norm.irq);
+ if (di->irq_sec.set)
+ os_deregister_interrupt(di->irq_sec.irq);
+
+ /* turn off clocks (is there a portable way to do this?) */
+ clk_disable(di->clk);
+ clk_put(di->clk);
+
+ /* unallocate locks */
+ os_lock_deallocate(di->busy_lock);
+
+ /* unmap i/o registers */
+ os_unmap_device(di->ioaddr, DI_ADDRESS_RANGE);
+
+ /* free the dryice struct */
+ os_free_memory(di);
+
+ os_dev_shutdown_return(OS_ERROR_OK_S);
+}
+
+di_return_t dryice_set_programmed_key(const void *key_data, int key_bits,
+ int flags)
+{
+ uint32_t dcr;
+ int key_bytes, reg;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (key_data == NULL) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ if (key_bits < 0 || key_bits > MAX_KEY_LEN || key_bits % 8) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ if (flags & DI_FUNC_FLAG_WORD_KEY) {
+ if (key_bits % 32 || (uint32_t)key_data & 0x3) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ }
+ if (di->key_programmed) {
+ rc = DI_ERR_INUSE;
+ goto err;
+ }
+ if (di_state() == DI_STATE_FAILURE) {
+ rc = DI_ERR_STATE;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_PKWHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_PKWSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ key_bytes = key_bits / 8;
+
+ todo_init((flags & DI_FUNC_FLAG_ASYNC) != 0);
+
+ /* accomodate busses that can only do 32-bit transfers */
+ if (flags & DI_FUNC_FLAG_WORD_KEY) {
+ uint32_t *keyp = (void *)key_data;
+
+ for (reg = 0; reg < MAX_KEY_WORDS; reg++) {
+ if (reg < MAX_KEY_WORDS - key_bytes / 4)
+ todo_write_val(0, DPKR7 - reg * 4);
+ else {
+ todo_write_ptr32(keyp, DPKR7 - reg * 4);
+ keyp++;
+ }
+ }
+ } else {
+ uint8_t *keyp = (void *)key_data;
+
+ for (reg = 0; reg < MAX_KEY_WORDS; reg++) {
+ int size = key_bytes - (MAX_KEY_WORDS - reg - 1) * 4;
+ if (size <= 0)
+ todo_write_val(0, DPKR7 - reg * 4);
+ else {
+ if (size > 4)
+ size = 4;
+ todo_write_ptr(keyp, DPKR7 - reg * 4, size);
+ keyp += size;
+ }
+ }
+ }
+ todo_assign(di->key_programmed, 1);
+
+ if (flags & DI_FUNC_LOCK_FLAGS) {
+ dcr = di_read(DCR);
+ if (flags & DI_FUNC_FLAG_READ_LOCK) {
+ if (flags & DI_FUNC_FLAG_HARD_LOCK)
+ dcr |= DCR_PKRHL;
+ else
+ dcr |= DCR_PKRSL;
+ }
+ if (flags & DI_FUNC_FLAG_WRITE_LOCK) {
+ if (flags & DI_FUNC_FLAG_HARD_LOCK)
+ dcr |= DCR_PKWHL;
+ else
+ dcr |= DCR_PKWSL;
+ }
+ todo_write_val(dcr, DCR);
+ }
+ todo_start();
+
+ if (flags & DI_FUNC_FLAG_ASYNC)
+ return 0;
+
+ rc = todo_wait_done();
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_set_programmed_key);
+
+di_return_t dryice_get_programmed_key(uint8_t *key_data, int key_bits)
+{
+ int reg, byte, key_bytes;
+ uint32_t dcr, dpkr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (key_data == NULL) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ if (key_bits < 0 || key_bits > MAX_KEY_LEN || key_bits % 8) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ #if 0
+ if (!di->key_programmed) {
+ rc = DI_ERR_UNSET;
+ goto err;
+ }
+ #endif
+ if (di_state() == DI_STATE_FAILURE) {
+ rc = DI_ERR_STATE;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_PKRHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_PKRSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ key_bytes = key_bits / 8;
+
+ /* read key */
+ for (reg = 0; reg < MAX_KEY_WORDS; reg++) {
+ if (reg < (MAX_KEY_BYTES - key_bytes) / 4)
+ continue;
+ dpkr = di_read(DPKR7 - reg * 4);
+
+ for (byte = 0; byte < 4; byte++) {
+ if (reg * 4 + byte >= MAX_KEY_BYTES - key_bytes) {
+ int shift = 24 - byte * 8;
+ *key_data++ = (dpkr >> shift) & 0xff;
+ }
+ }
+ dpkr = 0; /* cleared for security */
+ }
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_get_programmed_key);
+
+di_return_t dryice_release_programmed_key(void)
+{
+ uint32_t dcr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (!di->key_programmed) {
+ rc = DI_ERR_UNSET;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_PKWHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_PKWSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ di->key_programmed = 0;
+
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_release_programmed_key);
+
+di_return_t dryice_set_random_key(int flags)
+{
+ uint32_t dcr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (di_state() == DI_STATE_FAILURE) {
+ rc = DI_ERR_STATE;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_RKHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_RKSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ todo_init((flags & DI_FUNC_FLAG_ASYNC) != 0);
+
+ /* clear Random Key Error bit, if set */
+ if (di_read(DSR) & DSR_RKE)
+ todo_write_val(DSR_RKE, DCR);
+
+ /* load random key */
+ todo_write_val(DKCR_LRK, DKCR);
+
+ /* wait for RKV (valid) or RKE (error) */
+ todo_wait_rkg();
+
+ if (flags & DI_FUNC_LOCK_FLAGS) {
+ dcr = di_read(DCR);
+ if (flags & DI_FUNC_FLAG_WRITE_LOCK) {
+ if (flags & DI_FUNC_FLAG_HARD_LOCK)
+ dcr |= DCR_RKHL;
+ else
+ dcr |= DCR_RKSL;
+ }
+ todo_write_val(dcr, DCR);
+ }
+ todo_start();
+
+ if (flags & DI_FUNC_FLAG_ASYNC)
+ return 0;
+
+ rc = todo_wait_done();
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_set_random_key);
+
+di_return_t dryice_select_key(di_key_t key, int flags)
+{
+ uint32_t dcr, dksr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ switch (key) {
+ case DI_KEY_FK:
+ dksr = DKSR_IIM_KEY;
+ break;
+ case DI_KEY_PK:
+ dksr = DKSR_PROG_KEY;
+ break;
+ case DI_KEY_RK:
+ dksr = DKSR_RAND_KEY;
+ break;
+ case DI_KEY_FPK:
+ dksr = DKSR_PROG_XOR_IIM_KEY;
+ break;
+ case DI_KEY_FRK:
+ dksr = DKSR_RAND_XOR_IIM_KEY;
+ break;
+ default:
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ if (di->key_selected) {
+ rc = DI_ERR_INUSE;
+ goto err;
+ }
+ if (di_state() != DI_STATE_VALID) {
+ rc = DI_ERR_STATE;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_KSHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_KSSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ todo_init((flags & DI_FUNC_FLAG_ASYNC) != 0);
+
+ /* select key */
+ todo_write_val(dksr, DKSR);
+
+ todo_assign(di->key_selected, 1);
+
+ if (flags & DI_FUNC_LOCK_FLAGS) {
+ dcr = di_read(DCR);
+ if (flags & DI_FUNC_FLAG_WRITE_LOCK) {
+ if (flags & DI_FUNC_FLAG_HARD_LOCK)
+ dcr |= DCR_KSHL;
+ else
+ dcr |= DCR_KSSL;
+ }
+ todo_write_val(dcr, DCR);
+ }
+ todo_start();
+
+ if (flags & DI_FUNC_FLAG_ASYNC)
+ return 0;
+
+ rc = todo_wait_done();
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_select_key);
+
+di_return_t dryice_check_key(di_key_t *key)
+{
+ uint32_t dksr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (key == NULL) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+
+ dksr = di_read(DKSR);
+
+ if (di_state() != DI_STATE_VALID) {
+ dksr = DKSR_IIM_KEY;
+ rc = DI_ERR_STATE;
+ } else if (dksr == DI_KEY_RK || dksr == DI_KEY_FRK) {
+ if (!(di_read(DSR) & DSR_RKV)) {
+ dksr = DKSR_IIM_KEY;
+ rc = DI_ERR_UNSET;
+ }
+ }
+ switch (dksr) {
+ case DKSR_IIM_KEY:
+ *key = DI_KEY_FK;
+ break;
+ case DKSR_PROG_KEY:
+ *key = DI_KEY_PK;
+ break;
+ case DKSR_RAND_KEY:
+ *key = DI_KEY_RK;
+ break;
+ case DKSR_PROG_XOR_IIM_KEY:
+ *key = DI_KEY_FPK;
+ break;
+ case DKSR_RAND_XOR_IIM_KEY:
+ *key = DI_KEY_FRK;
+ break;
+ }
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_check_key);
+
+di_return_t dryice_release_key_selection(void)
+{
+ uint32_t dcr;
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (!di->key_selected) {
+ rc = DI_ERR_UNSET;
+ goto err;
+ }
+ dcr = di_read(DCR);
+ if (dcr & DCR_KSHL) {
+ rc = DI_ERR_HLOCK;
+ goto err;
+ }
+ if (dcr & DCR_KSSL) {
+ rc = DI_ERR_SLOCK;
+ goto err;
+ }
+ di->key_selected = 0;
+
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_release_key_selection);
+
+di_return_t dryice_get_tamper_event(uint32_t *events, uint32_t *timestamp,
+ int flags)
+{
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ if (di_state() == DI_STATE_VALID) {
+ rc = DI_ERR_STATE;
+ goto err;
+ }
+ if (events == NULL) {
+ rc = DI_ERR_INVAL;
+ goto err;
+ }
+ *events = di_read(DSR) & DSR_TAMPER_BITS;
+ if (timestamp) {
+ if (di_state() == DI_STATE_NON_VALID)
+ *timestamp = di_read(DTCMR);
+ else
+ *timestamp = 0;
+ }
+err:
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_get_tamper_event);
+
+di_return_t dryice_register_callback(void (*func)(di_return_t,
+ unsigned long cookie),
+ unsigned long cookie)
+{
+ di_return_t rc = 0;
+
+ if (di_busy_set())
+ return DI_ERR_BUSY;
+
+ di->cb_func = func;
+ di->cb_cookie = cookie;
+
+ di_busy_clear();
+ return rc;
+}
+EXTERN_SYMBOL(dryice_register_callback);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("DryIce");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/security/dryice.h b/drivers/mxc/security/dryice.h
new file mode 100644
index 000000000000..8334b5098d31
--- /dev/null
+++ b/drivers/mxc/security/dryice.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __DRYICE_H__
+#define __DRYICE_H__
+
+
+/*!
+ * @file dryice.h
+ * @brief Definition of DryIce API.
+ */
+
+/*! @page dryice_api DryIce API
+ *
+ * Definition of the DryIce API.
+ *
+ * The DryIce API implements a software interface to the DryIce hardware
+ * block. Methods are provided to store, retrieve, generate, and manage
+ * cryptographic keys and to monitor security tamper events.
+ *
+ * See @ref dryice_api for the DryIce API.
+ */
+
+/*!
+ * This defines the SCC key length (in bits)
+ */
+#define SCC_KEY_LEN 168
+
+/*!
+ * This defines the maximum key length (in bits)
+ */
+#define MAX_KEY_LEN 256
+#define MAX_KEY_BYTES ((MAX_KEY_LEN) / 8)
+#define MAX_KEY_WORDS ((MAX_KEY_LEN) / 32)
+
+/*!
+ * @name DryIce Function Flags
+ */
+/*@{*/
+#define DI_FUNC_FLAG_ASYNC 0x01 /*!< do not block */
+#define DI_FUNC_FLAG_READ_LOCK 0x02 /*!< set read lock for this resource */
+#define DI_FUNC_FLAG_WRITE_LOCK 0x04 /*!< set write lock for resource */
+#define DI_FUNC_FLAG_HARD_LOCK 0x08 /*!< locks will be hard (default soft) */
+#define DI_FUNC_FLAG_WORD_KEY 0x10 /*!< key provided as 32-bit words */
+/*@}*/
+
+/*!
+ * @name DryIce Tamper Events
+ */
+/*@{*/
+#define DI_TAMPER_EVENT_WTD (1 << 23) /*!< wire-mesh tampering det */
+#define DI_TAMPER_EVENT_ETBD (1 << 22) /*!< ext tampering det: input B */
+#define DI_TAMPER_EVENT_ETAD (1 << 21) /*!< ext tampering det: input A */
+#define DI_TAMPER_EVENT_EBD (1 << 20) /*!< external boot detected */
+#define DI_TAMPER_EVENT_SAD (1 << 19) /*!< security alarm detected */
+#define DI_TAMPER_EVENT_TTD (1 << 18) /*!< temperature tampering det */
+#define DI_TAMPER_EVENT_CTD (1 << 17) /*!< clock tampering det */
+#define DI_TAMPER_EVENT_VTD (1 << 16) /*!< voltage tampering det */
+#define DI_TAMPER_EVENT_MCO (1 << 3) /*!< monotonic counter overflow */
+#define DI_TAMPER_EVENT_TCO (1 << 2) /*!< time counter overflow */
+/*@}*/
+
+/*!
+ * DryIce Key Sources
+ */
+typedef enum di_key {
+ DI_KEY_FK, /*!< the fused (IIM) key */
+ DI_KEY_PK, /*!< the programmed key */
+ DI_KEY_RK, /*!< the random key */
+ DI_KEY_FPK, /*!< the programmed key XORed with the fused key */
+ DI_KEY_FRK, /*!< the random key XORed with the fused key */
+} di_key_t;
+
+/*!
+ * DryIce Error Codes
+ */
+typedef enum dryice_return {
+ DI_SUCCESS = 0, /*!< operation was successful */
+ DI_ERR_BUSY, /*!< device or resource busy */
+ DI_ERR_STATE, /*!< dryice is in incompatible state */
+ DI_ERR_INUSE, /*!< resource is already in use */
+ DI_ERR_UNSET, /*!< resource has not been initialized */
+ DI_ERR_WRITE, /*!< error occurred during register write */
+ DI_ERR_INVAL, /*!< invalid argument */
+ DI_ERR_FAIL, /*!< operation failed */
+ DI_ERR_HLOCK, /*!< resource is hard locked */
+ DI_ERR_SLOCK, /*!< resource is soft locked */
+ DI_ERR_NOMEM, /*!< out of memory */
+} di_return_t;
+
+/*!
+ * These functions define the DryIce API.
+ */
+
+/*!
+ * Write a given key to the Programmed Key registers in DryIce, and
+ * optionally lock the Programmed Key against either reading or further
+ * writing. The value is held until a call to the release_programmed_key
+ * interface is made, or until the appropriate HW reset if the write-lock
+ * flags are used. Unused key bits will be zeroed.
+ *
+ * @param[in] key_data A pointer to the key data to be programmed, with
+ * the most significant byte or word first. This
+ * will be interpreted as a byte pointer unless the
+ * WORD_KEY flag is set, in which case it will be
+ * treated as a word pointer and the key data will be
+ * read a word at a time, starting with the MSW.
+ * When called asynchronously, the data pointed to by
+ * key_data must persist until the operation completes.
+ *
+ * @param[in] key_bits The number of bits in the key to be stored.
+ * This must be a multiple of 8 and within the
+ * range of 0 and MAX_KEY_LEN.
+ *
+ * @param[in] flags This is a bit-wise OR of the flags to be passed
+ * to the function. Flags can include:
+ * ASYNC, READ_LOCK, WRITE_LOCK, HARD_LOCK, and
+ * WORD_KEY.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, INVAL
+ * on invalid arguments, INUSE if key has already been
+ * programmed, STATE if DryIce is in the wrong state,
+ * HLOCK or SLOCK if the key registers are locked for
+ * writing, and WRITE if a write error occurs
+ * (See #di_return_t).
+ */
+extern di_return_t dryice_set_programmed_key(const void *key_data, int key_bits,
+ int flags);
+
+/*!
+ * Read the Programmed Key registers and write the contents into a buffer.
+ *
+ * @param[out] key_data A byte pointer to where the key data will be written,
+ * with the most significant byte being written first.
+ *
+ * @param[in] key_bits The number of bits of the key to be retrieved.
+ * This must be a multiple of 8 and within the
+ * range of 0 and MAX_KEY_LEN.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, INVAL
+ * on invalid arguments, UNSET if key has not been
+ * programmed, STATE if DryIce is in the wrong state,
+ * and HLOCK or SLOCK if the key registers are locked for
+ * reading (See #di_return_t).
+ */
+extern di_return_t dryice_get_programmed_key(uint8_t *key_data, int key_bits);
+
+/*!
+ * Allow the set_programmed_key interface to be used to write a new
+ * Programmed Key to DryIce. Note that this interface does not overwrite
+ * the value in the Programmed Key registers.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy,
+ * UNSET if the key has not been previously set, and
+ * HLOCK or SLOCK if the key registers are locked for
+ * writing (See #di_return_t).
+ */
+extern di_return_t dryice_release_programmed_key(void);
+
+/*!
+ * Generate and load a new Random Key in DryIce, and optionally lock the
+ * Random Key against further change.
+ *
+ * @param[in] flags This is a bit-wise OR of the flags to be passed
+ * to the function. Flags can include:
+ * ASYNC, READ_LOCK, WRITE_LOCK, and HARD_LOCK.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, STATE
+ * if DryIce is in the wrong state, FAIL if the key gen
+ * failed, HLOCK or SLOCK if the key registers are
+ * locked, and WRITE if a write error occurs
+ * (See #di_return_t).
+ */
+extern di_return_t dryice_set_random_key(int flags);
+
+/*!
+ * Set the key selection in DryIce to determine the key used by an
+ * encryption module such as SCC. The selection is held until a call to the
+ * Release Selected Key interface is made, or until the appropriate HW
+ * reset if the LOCK flags are used.
+ *
+ * @param[in] key The source of the key to be used by the SCC
+ * (See #di_key_t).
+ *
+ * @param[in] flags This is a bit-wise OR of the flags to be passed
+ * to the function. Flags can include:
+ * ASYNC, WRITE_LOCK, and HARD_LOCK.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, INVAL
+ * on invalid arguments, INUSE if a selection has already
+ * been made, STATE if DryIce is in the wrong state,
+ * HLOCK or SLOCK if the selection register is locked,
+ * and WRITE if a write error occurs
+ */
+extern di_return_t dryice_select_key(di_key_t key, int flags);
+
+/*!
+ * Check which key will be used in the SCC. This is needed because in some
+ * DryIce states, the Key Select Register is overridden by a default value
+ * (the Fused/IIM key).
+ *
+ * @param[out] key The source of the key that is currently selected for
+ * use by the SCC. This may be different from the key
+ * specified by the dryice_select_key function
+ * (See #di_key_t). This value is set even if an error
+ * code (except for BUSY) is returned.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, STATE if
+ * DryIce is in the wrong state, INVAL on invalid
+ * arguments, or UNSET if no key has been selected
+ * (See #di_return_t).
+ */
+extern di_return_t dryice_check_key(di_key_t *key);
+
+/*!
+ * Allow the dryice_select_key interface to be used to set a new key selection
+ * in DryIce. Note that this interface does not overwrite the value in DryIce.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, UNSET
+ * if the no selection has been made previously, and
+ * HLOCK or SLOCK if the selection register is locked
+ * (See #di_return_t).
+ */
+extern di_return_t dryice_release_key_selection(void);
+
+/*!
+ * Returns tamper-detection status bits. Also an optional timestamp when
+ * DryIce is in the Non-valid state. If DryIce is not in Failure or Non-valid
+ * state, this interface returns a failure code.
+ *
+ * @param[out] events This is a bit-wise OR of the following events:
+ * WTD (Wire Mesh), ETBD (External Tamper B),
+ * ETAD (External Tamper A), EBD (External Boot),
+ * SAD (Security Alarm), TTD (Temperature Tamper),
+ * CTD (Clock Tamper), VTD (Voltage Tamper),
+ * MCO (Monolithic Counter Overflow), and
+ * TCO (Time Counter Overflow).
+ *
+ * @param[out] timestamp This is the value of the time counter in seconds
+ * when the tamper occurred. A timestamp will not be
+ * returned if a NULL pointer is specified. If DryIce
+ * is not in the Non-valid state the time cannot be
+ * read, so a timestamp of 0 will be returned.
+ *
+ * @param[in] flags This is a bit-wise OR of the flags to be passed
+ * to the function. Flags is ignored currently by
+ * this function.
+ *
+ * @return Returns SUCCESS (0), BUSY if DryIce is busy, and
+ * INVAL on invalid arguments (See #di_return_t).
+ */
+extern di_return_t
+dryice_get_tamper_event(uint32_t *events, uint32_t *timestamp, int flags);
+
+/*!
+ * Provide a callback function to be called upon the completion of DryIce calls
+ * that are executed asynchronously.
+ *
+ * @param[in] func This is a pointer to a function of type:
+ * void callback(di_return_t rc, unsigned long cookie)
+ * The return code of the async function is passed
+ * back in "rc" along with the cookie provided when
+ * registering the callback.
+ *
+ * @param[in] cookie This is an "opaque" cookie of type unsigned long that
+ * is returned on subsequent callbacks. It may be of any
+ * value.
+ *
+ * @return Returns SUCCESS (0), or BUSY if DryIce is busy
+ * (See #di_return_t).
+ */
+extern di_return_t dryice_register_callback(void (*func)(di_return_t rc,
+ unsigned long cookie),
+ unsigned long cookie);
+
+#endif /* __DRYICE_H__ */
diff --git a/drivers/mxc/security/mxc_scc.c b/drivers/mxc/security/mxc_scc.c
new file mode 100644
index 000000000000..82f971d3454b
--- /dev/null
+++ b/drivers/mxc/security/mxc_scc.c
@@ -0,0 +1,2386 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_scc.c
+ *
+ * This is the driver code for the Security Controller (SCC). It has no device
+ * driver interface, so no user programs may access it. Its interaction with
+ * the Linux kernel is from calls to #scc_init() when the driver is loaded, and
+ * #scc_cleanup() should the driver be unloaded. The driver uses locking and
+ * (task-sleep/task-wakeup) functions of the kernel. It also registers itself
+ * to handle the interrupt line(s) from the SCC.
+ *
+ * Other drivers in the kernel may use the remaining API functions to get at
+ * the services of the SCC. The main service provided is the Secure Memory,
+ * which allows encoding and decoding of secrets with a per-chip secret key.
+ *
+ * The SCC is single-threaded, and so is this module. When the scc_crypt()
+ * routine is called, it will lock out other accesses to the function. If
+ * another task is already in the module, the subsequent caller will spin on a
+ * lock waiting for the other access to finish.
+ *
+ * Note that long crypto operations could cause a task to spin for a while,
+ * preventing other kernel work (other than interrupt processing) to get done.
+ *
+ * The external (kernel module) interface is through the following functions:
+ * @li scc_get_configuration()
+ * @li scc_crypt()
+ * @li scc_zeroize_memories()
+ * @li scc_monitor_security_failure()
+ * @li scc_stop_monitoring_security_failure()
+ * @li scc_set_sw_alarm()
+ * @li scc_read_register()
+ * @li scc_write_register()
+ *
+ * All other functions are internal to the driver.
+ *
+ * @ingroup MXCSCC
+*/
+#include "sahara2/include/fsl_platform.h"
+#include "sahara2/include/portable_os.h"
+#include "mxc_scc_internals.h"
+
+#include <linux/delay.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+
+#include <linux/device.h>
+#include <mach/clock.h>
+#include <linux/device.h>
+
+#else
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#endif
+
+/*!
+ * This is the set of errors which signal that access to the SCM RAM has
+ * failed or will fail.
+ */
+#define SCM_ACCESS_ERRORS \
+ (SCM_ERR_USER_ACCESS | SCM_ERR_ILLEGAL_ADDRESS | \
+ SCM_ERR_ILLEGAL_MASTER | SCM_ERR_CACHEABLE_ACCESS | \
+ SCM_ERR_UNALIGNED_ACCESS | SCM_ERR_BYTE_ACCESS | \
+ SCM_ERR_INTERNAL_ERROR | SCM_ERR_SMN_BLOCKING_ACCESS | \
+ SCM_ERR_CIPHERING | SCM_ERR_ZEROIZING | SCM_ERR_BUSY)
+/******************************************************************************
+ *
+ * Global / Static Variables
+ *
+ *****************************************************************************/
+
+/*!
+ * This is type void* so that a) it cannot directly be dereferenced,
+ * and b) pointer arithmetic on it will function in a 'normal way' for
+ * the offsets in scc_defines.h
+ *
+ * scc_base is the location in the iomap where the SCC's registers
+ * (and memory) start.
+ *
+ * The referenced data is declared volatile so that the compiler will
+ * not make any assumptions about the value of registers in the SCC,
+ * and thus will always reload the register into CPU memory before
+ * using it (i.e. wherever it is referenced in the driver).
+ *
+ * This value should only be referenced by the #SCC_READ_REGISTER and
+ * #SCC_WRITE_REGISTER macros and their ilk. All dereferences must be
+ * 32 bits wide.
+ */
+static volatile void *scc_base;
+
+/*! Array to hold function pointers registered by
+ #scc_monitor_security_failure() and processed by
+ #scc_perform_callbacks() */
+static void (*scc_callbacks[SCC_CALLBACK_SIZE]) (void);
+
+/*! Structure returned by #scc_get_configuration() */
+static scc_config_t scc_configuration = {
+ .driver_major_version = SCC_DRIVER_MAJOR_VERSION_1,
+ .driver_minor_version = SCC_DRIVER_MINOR_VERSION_8,
+ .scm_version = -1,
+ .smn_version = -1,
+ .block_size_bytes = -1,
+ .black_ram_size_blocks = -1,
+ .red_ram_size_blocks = -1
+};
+
+/*! Key Control Information. Integrity is controlled by use of
+ #scc_crypto_lock. */
+static struct scc_key_slot scc_key_info[SCC_KEY_SLOTS];
+
+/*! Internal flag to know whether SCC is in Failed state (and thus many
+ * registers are unavailable). Once it goes failed, it never leaves it. */
+static volatile enum scc_status scc_availability = SCC_STATUS_INITIAL;
+
+/*! Flag to say whether interrupt handler has been registered for
+ * SMN interrupt */
+static int smn_irq_set = 0;
+
+/*! Flag to say whether interrupt handler has been registered for
+ * SCM interrupt */
+static int scm_irq_set = 0;
+
+/*! This lock protects the #scc_callbacks list as well as the @c
+ * callbacks_performed flag in #scc_perform_callbacks. Since the data this
+ * protects may be read or written from either interrupt or base level, all
+ * operations should use the irqsave/irqrestore or similar to make sure that
+ * interrupts are inhibited when locking from base level.
+ */
+static spinlock_t scc_callbacks_lock = SPIN_LOCK_UNLOCKED;
+
+/*!
+ * Ownership of this lock prevents conflicts on the crypto operation in the SCC
+ * and the integrity of the #scc_key_info.
+ */
+static spinlock_t scc_crypto_lock = SPIN_LOCK_UNLOCKED;
+
+/*! Calculated once for quick reference to size of the unreserved space in one
+ * RAM in SCM.
+ */
+static uint32_t scc_memory_size_bytes;
+
+/*! Calculated once for quick reference to size of SCM address space */
+static uint32_t scm_highest_memory_address;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18))
+#ifndef SCC_CLOCK_NOT_GATED
+/*! Pointer to SCC's clock information. Initialized during scc_init(). */
+static struct clk *scc_clk = NULL;
+#endif
+#endif
+
+/*! The lookup table for an 8-bit value. Calculated once
+ * by #scc_init_ccitt_crc().
+ */
+static uint16_t scc_crc_lookup_table[256];
+
+/*! Fixed padding for appending to plaintext to fill out a block */
+static uint8_t scc_block_padding[8] =
+ { SCC_DRIVER_PAD_CHAR, 0, 0, 0, 0, 0, 0, 0 };
+
+/******************************************************************************
+ *
+ * Function Implementations - Externally Accessible
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn scc_init() */
+/*****************************************************************************/
+/*!
+ * Initialize the driver at boot time or module load time.
+ *
+ * Register with the kernel as the interrupt handler for the SCC interrupt
+ * line(s).
+ *
+ * Map the SCC's register space into the driver's memory space.
+ *
+ * Query the SCC for its configuration and status. Save the configuration in
+ * #scc_configuration and save the status in #scc_availability. Called by the
+ * kernel.
+ *
+ * Do any locking/wait queue initialization which may be necessary.
+ *
+ * The availability fuse may be checked, depending on platform.
+ */
+static int scc_init(void)
+{
+ uint32_t smn_status;
+ int i;
+ int return_value = -EIO; /* assume error */
+ if (scc_availability == SCC_STATUS_INITIAL) {
+
+ /* Set this until we get an initial reading */
+ scc_availability = SCC_STATUS_CHECKING;
+
+ /* Initialize the constant for the CRC function */
+ scc_init_ccitt_crc();
+
+ /* initialize the callback table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ scc_callbacks[i] = 0;
+ }
+
+ /* Initialize key slots */
+ for (i = 0; i < SCC_KEY_SLOTS; i++) {
+ scc_key_info[i].offset = i * SCC_KEY_SLOT_SIZE;
+ scc_key_info[i].status = 0; /* unassigned */
+ }
+
+ /* Enable the SCC clock on platforms where it is gated */
+#ifndef SCC_CLOCK_NOT_GATED
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+ mxc_clks_enable(SCC_CLK);
+#else
+
+ scc_clk = clk_get(NULL, "scc_clk");
+ if (scc_clk != ERR_PTR(ENOENT)) {
+ clk_enable(scc_clk);
+ }
+#endif /* LINUX_VERSION_CODE */
+
+#endif /* SCC_CLOCK_NOT_GATED */
+ /* See whether there is an SCC available */
+ if (0 && !SCC_ENABLED()) {
+ os_printk(KERN_ERR
+ "SCC: Fuse for SCC is set to disabled. Exiting.\n");
+ } else {
+ /* Map the SCC (SCM and SMN) memory on the internal bus into
+ kernel address space */
+ scc_base = (void *)IO_ADDRESS(SCC_BASE);
+
+ /* If that worked, we can try to use the SCC */
+ if (scc_base == NULL) {
+ os_printk(KERN_ERR
+ "SCC: Register mapping failed. Exiting.\n");
+ } else {
+ /* Get SCM into 'clean' condition w/interrupts cleared &
+ disabled */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ |
+ SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+
+ /* Clear error status register (any write will do it) */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, 0);
+
+ /*
+ * There is an SCC. Determine its current state. Side effect
+ * is to populate scc_config and scc_availability
+ */
+ smn_status = scc_grab_config_values();
+
+ /* Try to set up interrupt handler(s) */
+ if (scc_availability == SCC_STATUS_OK) {
+ if (setup_interrupt_handling() != 0) {
+ unsigned condition;
+ /*!
+ * The error could be only that the SCM interrupt was
+ * not set up. This interrupt is always masked, so
+ * that is not an issue.
+ *
+ * The SMN's interrupt may be shared on that line, it
+ * may be separate, or it may not be wired. Do what
+ * is necessary to check its status.
+ *
+ * Although the driver is coded for possibility of not
+ * having SMN interrupt, the fact that there is one
+ * means it should be available and used.
+ */
+#ifdef USE_SMN_INTERRUPT
+ condition = !smn_irq_set; /* Separate. Check SMN binding */
+#elif !defined(NO_SMN_INTERRUPT)
+ condition = !scm_irq_set; /* Shared. Check SCM binding */
+#else
+ condition = FALSE; /* SMN not wired at all. Ignore. */
+#endif
+ /* setup was not able to set up SMN interrupt */
+ scc_availability =
+ SCC_STATUS_UNIMPLEMENTED;
+ } /* interrupt handling returned non-zero */
+ } /* availability is OK */
+ if (scc_availability == SCC_STATUS_OK) {
+ /* Get SMN into 'clean' condition w/interrupts cleared &
+ enabled */
+ SCC_WRITE_REGISTER(SMN_COMMAND,
+ SMN_COMMAND_CLEAR_INTERRUPT
+ |
+ SMN_COMMAND_ENABLE_INTERRUPT);
+ }
+ /* availability is still OK */
+ } /* if scc_base != NULL */
+
+ } /* if SCC_ENABLED() */
+
+ /*
+ * If status is SCC_STATUS_UNIMPLEMENTED or is still
+ * SCC_STATUS_CHECKING, could be leaving here with the driver partially
+ * initialized. In either case, cleanup (which will mark the SCC as
+ * UNIMPLEMENTED).
+ */
+ if (scc_availability == SCC_STATUS_CHECKING ||
+ scc_availability == SCC_STATUS_UNIMPLEMENTED) {
+ scc_cleanup();
+ } else {
+ return_value = 0; /* All is well */
+ }
+ }
+ /* ! STATUS_INITIAL */
+ pr_debug("SCC: Driver Status is %s\n",
+ (scc_availability == SCC_STATUS_INITIAL) ? "INITIAL" :
+ (scc_availability == SCC_STATUS_CHECKING) ? "CHECKING" :
+ (scc_availability ==
+ SCC_STATUS_UNIMPLEMENTED) ? "UNIMPLEMENTED"
+ : (scc_availability ==
+ SCC_STATUS_OK) ? "OK" : (scc_availability ==
+ SCC_STATUS_FAILED) ? "FAILED" :
+ "UNKNOWN");
+
+ return return_value;
+} /* scc_init */
+
+/*****************************************************************************/
+/* fn scc_cleanup() */
+/*****************************************************************************/
+/*!
+ * Perform cleanup before driver/module is unloaded by setting the machine
+ * state close to what it was when the driver was loaded. This function is
+ * called when the kernel is shutting down or when this driver is being
+ * unloaded.
+ *
+ * A driver like this should probably never be unloaded, especially if there
+ * are other module relying upon the callback feature for monitoring the SCC
+ * status.
+ *
+ * In any case, cleanup the callback table (by clearing out all of the
+ * pointers). Deregister the interrupt handler(s). Unmap SCC registers.
+ *
+ */
+static void scc_cleanup(void)
+{
+ int i;
+
+ /* Mark the driver / SCC as unusable. */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED;
+
+ /* Clear out callback table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ scc_callbacks[i] = 0;
+ }
+
+ /* If SCC has been mapped in, clean it up and unmap it */
+ if (scc_base) {
+ /* For the SCM, disable interrupts, zeroize RAMs. Interrupt
+ * status will appear because zeroize will complete. */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_MASK_INTERRUPTS |
+ SCM_INTERRUPT_CTRL_ZEROIZE_MEMORY);
+
+ /* For the SMN, clear and disable interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND, SMN_COMMAND_CLEAR_INTERRUPT);
+
+ /* remove virtual mapping */
+ iounmap((void *)scc_base);
+ }
+
+ /* Now that interrupts cannot occur, disassociate driver from the interrupt
+ * lines.
+ */
+
+ /* Deregister SCM interrupt handler */
+ if (scm_irq_set) {
+ os_deregister_interrupt(INT_SCC_SCM);
+ }
+
+ /* Deregister SMN interrupt handler */
+ if (smn_irq_set) {
+#ifdef USE_SMN_INTERRUPT
+ os_deregister_interrupt(INT_SCC_SMN);
+#endif
+ }
+ pr_debug("SCC driver cleaned up.\n");
+
+} /* scc_cleanup */
+
+/*****************************************************************************/
+/* fn scc_get_configuration() */
+/*****************************************************************************/
+scc_config_t *scc_get_configuration(void)
+{
+ /*
+ * If some other driver calls scc before the kernel does, make sure that
+ * this driver's initialization is performed.
+ */
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /*!
+ * If there is no SCC, yet the driver exists, the value -1 will be in
+ * the #scc_config_t fields for other than the driver versions.
+ */
+ return &scc_configuration;
+} /* scc_get_configuration */
+
+/*****************************************************************************/
+/* fn scc_zeroize_memories() */
+/*****************************************************************************/
+scc_return_t scc_zeroize_memories(void)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+ uint32_t status;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ if (scc_availability == SCC_STATUS_OK) {
+ unsigned long irq_flags; /* for IRQ save/restore */
+
+ /* Lock access to crypto memory of the SCC */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ /* Start the Zeroize by setting a bit in the SCM_INTERRUPT_CTRL
+ * register */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_MASK_INTERRUPTS
+ | SCM_INTERRUPT_CTRL_ZEROIZE_MEMORY);
+
+ scc_wait_completion();
+
+ /* Get any error info */
+ status = SCC_READ_REGISTER(SCM_ERROR_STATUS);
+
+ /* unlock the SCC */
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ if (!(status & SCM_ERR_ZEROIZE_FAILED)) {
+ return_status = SCC_RET_OK;
+ } else {
+ pr_debug
+ ("SCC: Zeroize failed. SCM Error Status is 0x%08x\n",
+ status);
+ }
+
+ /* Clear out any status. */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ | SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+
+ /* and any error status */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, 0);
+ }
+
+ return return_status;
+} /* scc_zeroize_memories */
+
+/*****************************************************************************/
+/* fn scc_crypt() */
+/*****************************************************************************/
+scc_return_t
+scc_crypt(unsigned long count_in_bytes, const uint8_t * data_in,
+ const uint8_t * init_vector,
+ scc_enc_dec_t direction, scc_crypto_mode_t crypto_mode,
+ scc_verify_t check_mode, uint8_t * data_out,
+ unsigned long *count_out_bytes)
+
+{
+ scc_return_t return_code = SCC_RET_FAIL;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ (void)scc_update_state(); /* in case no interrupt line from SMN */
+
+ /* make initial error checks */
+ if (scc_availability != SCC_STATUS_OK
+ || count_in_bytes == 0
+ || data_in == 0
+ || data_out == 0
+ || (crypto_mode != SCC_CBC_MODE && crypto_mode != SCC_ECB_MODE)
+ || (crypto_mode == SCC_CBC_MODE && init_vector == NULL)
+ || (direction != SCC_ENCRYPT && direction != SCC_DECRYPT)
+ || (check_mode == SCC_VERIFY_MODE_NONE &&
+ count_in_bytes % SCC_BLOCK_SIZE_BYTES() != 0)
+ || (direction == SCC_DECRYPT &&
+ count_in_bytes % SCC_BLOCK_SIZE_BYTES() != 0)
+ || (check_mode != SCC_VERIFY_MODE_NONE &&
+ check_mode != SCC_VERIFY_MODE_CCITT_CRC)) {
+ pr_debug
+ ("SCC: scc_crypt() count_in_bytes_ok = %d; data_in_ok = %d;"
+ " data_out_ok = %d; iv_ok = %d\n", !(count_in_bytes == 0),
+ !(data_in == 0), !(data_out == 0),
+ !(crypto_mode == SCC_CBC_MODE && init_vector == NULL));
+ pr_debug("SCC: scc_crypt() mode_ok=%d; direction_ok=%d;"
+ " size_ok=%d, check_mode_ok=%d\n",
+ !(crypto_mode != SCC_CBC_MODE
+ && crypto_mode != SCC_ECB_MODE),
+ !(direction != SCC_ENCRYPT
+ && direction != SCC_DECRYPT),
+ !((check_mode == SCC_VERIFY_MODE_NONE
+ && count_in_bytes % SCC_BLOCK_SIZE_BYTES() != 0)
+ || (direction == SCC_DECRYPT
+ && count_in_bytes % SCC_BLOCK_SIZE_BYTES() !=
+ 0)), !(check_mode != SCC_VERIFY_MODE_NONE
+ && check_mode !=
+ SCC_VERIFY_MODE_CCITT_CRC));
+ pr_debug("SCC: scc_crypt() detected bad argument\n");
+ } else {
+ /* Start settings for write to SCM_CONTROL register */
+ uint32_t scc_control = SCM_CONTROL_START_CIPHER;
+ unsigned long irq_flags; /* for IRQ save/restore */
+
+ /* Lock access to crypto memory of the SCC */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ /* Special needs for CBC Mode */
+ if (crypto_mode == SCC_CBC_MODE) {
+ scc_control |= SCM_CBC_MODE; /* change default of ECB */
+ /* Put in Initial Context. Vector registers are contiguous */
+ copy_to_scc(init_vector, SCM_INIT_VECTOR_0,
+ SCC_BLOCK_SIZE_BYTES(), NULL);
+ }
+
+ /* Fill the RED_START register */
+ SCC_WRITE_REGISTER(SCM_RED_START,
+ SCM_NON_RESERVED_OFFSET /
+ SCC_BLOCK_SIZE_BYTES());
+
+ /* Fill the BLACK_START register */
+ SCC_WRITE_REGISTER(SCM_BLACK_START,
+ SCM_NON_RESERVED_OFFSET /
+ SCC_BLOCK_SIZE_BYTES());
+
+ if (direction == SCC_ENCRYPT) {
+ /* Check for sufficient space in data_out */
+ if (check_mode == SCC_VERIFY_MODE_NONE) {
+ if (*count_out_bytes < count_in_bytes) {
+ return_code =
+ SCC_RET_INSUFFICIENT_SPACE;
+ }
+ } else { /* SCC_VERIFY_MODE_CCITT_CRC */
+ /* Calculate extra bytes needed for crc (2) and block
+ padding */
+ int padding_needed =
+ CRC_SIZE_BYTES + SCC_BLOCK_SIZE_BYTES() -
+ ((count_in_bytes + CRC_SIZE_BYTES)
+ % SCC_BLOCK_SIZE_BYTES());
+
+ /* Verify space is available */
+ if (*count_out_bytes <
+ count_in_bytes + padding_needed) {
+ return_code =
+ SCC_RET_INSUFFICIENT_SPACE;
+ }
+ }
+ /* If did not detect space error, do the encryption */
+ if (return_code != SCC_RET_INSUFFICIENT_SPACE) {
+ return_code =
+ scc_encrypt(count_in_bytes, data_in,
+ scc_control, data_out,
+ check_mode ==
+ SCC_VERIFY_MODE_CCITT_CRC,
+ count_out_bytes);
+ }
+
+ }
+ /* direction == SCC_ENCRYPT */
+ else { /* SCC_DECRYPT */
+ /* Check for sufficient space in data_out */
+ if (check_mode == SCC_VERIFY_MODE_NONE) {
+ if (*count_out_bytes < count_in_bytes) {
+ return_code =
+ SCC_RET_INSUFFICIENT_SPACE;
+ }
+ } else { /* SCC_VERIFY_MODE_CCITT_CRC */
+ /* Do initial check. Assume last block (of padding) and CRC
+ * will get stripped. After decipher is done and padding is
+ * removed, will know exact value.
+ */
+ int possible_size =
+ (int)count_in_bytes - CRC_SIZE_BYTES -
+ SCC_BLOCK_SIZE_BYTES();
+ if ((int)*count_out_bytes < possible_size) {
+ pr_debug
+ ("SCC: insufficient decrypt space %ld/%d.\n",
+ *count_out_bytes, possible_size);
+ return_code =
+ SCC_RET_INSUFFICIENT_SPACE;
+ }
+ }
+
+ /* If did not detect space error, do the decryption */
+ if (return_code != SCC_RET_INSUFFICIENT_SPACE) {
+ return_code =
+ scc_decrypt(count_in_bytes, data_in,
+ scc_control, data_out,
+ check_mode ==
+ SCC_VERIFY_MODE_CCITT_CRC,
+ count_out_bytes);
+ }
+
+ } /* SCC_DECRYPT */
+
+ /* unlock the SCC */
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ } /* else no initial error */
+
+ return return_code;
+} /* scc_crypt */
+
+/*****************************************************************************/
+/* fn scc_set_sw_alarm() */
+/*****************************************************************************/
+void scc_set_sw_alarm(void)
+{
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Update scc_availability based on current SMN status. This might
+ * perform callbacks.
+ */
+ (void)scc_update_state();
+
+ /* if everything is OK, make it fail */
+ if (scc_availability == SCC_STATUS_OK) {
+
+ /* sound the alarm (and disable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND, SMN_COMMAND_SET_SOFTWARE_ALARM);
+
+ scc_availability = SCC_STATUS_FAILED; /* Remember what we've done */
+
+ /* In case SMN interrupt is not available, tell the world */
+ scc_perform_callbacks();
+ }
+
+ return;
+} /* scc_set_sw_alarm */
+
+/*****************************************************************************/
+/* fn scc_monitor_security_failure() */
+/*****************************************************************************/
+scc_return_t scc_monitor_security_failure(void callback_func(void))
+{
+ int i;
+ unsigned long irq_flags; /* for IRQ save/restore */
+ scc_return_t return_status = SCC_RET_TOO_MANY_FUNCTIONS;
+ int function_stored = FALSE;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Acquire lock of callbacks table. Could be spin_lock_irq() if this
+ * routine were just called from base (not interrupt) level
+ */
+ spin_lock_irqsave(&scc_callbacks_lock, irq_flags);
+
+ /* Search through table looking for empty slot */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ if (scc_callbacks[i] == callback_func) {
+ if (function_stored) {
+ /* Saved duplicate earlier. Clear this later one. */
+ scc_callbacks[i] = NULL;
+ }
+ /* Exactly one copy is now stored */
+ return_status = SCC_RET_OK;
+ break;
+ } else if (scc_callbacks[i] == NULL && !function_stored) {
+ /* Found open slot. Save it and remember */
+ scc_callbacks[i] = callback_func;
+ return_status = SCC_RET_OK;
+ function_stored = TRUE;
+ }
+ }
+
+ /* Free the lock */
+ spin_unlock_irqrestore(&scc_callbacks_lock, irq_flags);
+
+ return return_status;
+} /* scc_monitor_security_failure */
+
+/*****************************************************************************/
+/* fn scc_stop_monitoring_security_failure() */
+/*****************************************************************************/
+void scc_stop_monitoring_security_failure(void callback_func(void))
+{
+ unsigned long irq_flags; /* for IRQ save/restore */
+ int i;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Acquire lock of callbacks table. Could be spin_lock_irq() if this
+ * routine were just called from base (not interrupt) level
+ */
+ spin_lock_irqsave(&scc_callbacks_lock, irq_flags);
+
+ /* Search every entry of the table for this function */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ if (scc_callbacks[i] == callback_func) {
+ scc_callbacks[i] = NULL; /* found instance - clear it out */
+ break;
+ }
+ }
+
+ /* Free the lock */
+ spin_unlock_irqrestore(&scc_callbacks_lock, irq_flags);
+
+ return;
+} /* scc_stop_monitoring_security_failure */
+
+/*****************************************************************************/
+/* fn scc_read_register() */
+/*****************************************************************************/
+scc_return_t scc_read_register(int register_offset, uint32_t * value)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+ uint32_t smn_status;
+ uint32_t scm_status;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* First layer of protection -- completely unaccessible SCC */
+ if (scc_availability != SCC_STATUS_UNIMPLEMENTED) {
+
+ /* Second layer -- that offset is valid */
+ if (register_offset != SMN_BITBANK_DECREMENT && /* write only! */
+ check_register_offset(register_offset) == SCC_RET_OK) {
+
+ /* Get current status / update local state */
+ smn_status = scc_update_state();
+ scm_status = SCC_READ_REGISTER(SCM_STATUS);
+
+ /*
+ * Third layer - verify that the register being requested is
+ * available in the current state of the SCC.
+ */
+ if ((return_status =
+ check_register_accessible(register_offset,
+ smn_status,
+ scm_status)) ==
+ SCC_RET_OK) {
+ *value = SCC_READ_REGISTER(register_offset);
+ }
+ }
+ }
+
+ return return_status;
+} /* scc_read_register */
+
+/*****************************************************************************/
+/* fn scc_write_register() */
+/*****************************************************************************/
+scc_return_t scc_write_register(int register_offset, uint32_t value)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+ uint32_t smn_status;
+ uint32_t scm_status;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* First layer of protection -- completely unaccessible SCC */
+ if (scc_availability != SCC_STATUS_UNIMPLEMENTED) {
+
+ /* Second layer -- that offset is valid */
+ if (!(register_offset == SCM_STATUS || /* These registers are */
+ register_offset == SCM_CONFIGURATION || /* Read Only */
+ register_offset == SMN_BIT_COUNT ||
+ register_offset == SMN_TIMER) &&
+ check_register_offset(register_offset) == SCC_RET_OK) {
+
+ /* Get current status / update local state */
+ smn_status = scc_update_state();
+ scm_status = SCC_READ_REGISTER(SCM_STATUS);
+
+ /*
+ * Third layer - verify that the register being requested is
+ * available in the current state of the SCC.
+ */
+ if (check_register_accessible
+ (register_offset, smn_status, scm_status) == 0) {
+ SCC_WRITE_REGISTER(register_offset, value);
+ return_status = SCC_RET_OK;
+ }
+ }
+ }
+
+ return return_status;
+} /* scc_write_register() */
+
+/******************************************************************************
+ *
+ * Function Implementations - Internal
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn scc_irq() */
+/*****************************************************************************/
+/*!
+ * This is the interrupt handler for the SCC.
+ *
+ * This function checks the SMN Status register to see whether it
+ * generated the interrupt, then it checks the SCM Status register to
+ * see whether it needs attention.
+ *
+ * If an SMN Interrupt is active, then the SCC state set to failure, and
+ * #scc_perform_callbacks() is invoked to notify any interested parties.
+ *
+ * The SCM Interrupt should be masked, as this driver uses polling to determine
+ * when the SCM has completed a crypto or zeroing operation. Therefore, if the
+ * interrupt is active, the driver will just clear the interrupt and (re)mask.
+ *
+ */
+OS_DEV_ISR(scc_irq)
+{
+ uint32_t smn_status;
+ uint32_t scm_status;
+ int handled = 0; /* assume interrupt isn't from SMN */
+#if defined(USE_SMN_INTERRUPT)
+ int smn_irq = INT_SCC_SMN; /* SMN interrupt is on a line by itself */
+#elif defined (NO_SMN_INTERRUPT)
+ int smn_irq = -1; /* not wired to CPU at all */
+#else
+ int smn_irq = INT_SCC_SCM; /* SMN interrupt shares a line with SCM */
+#endif
+
+ /* Update current state... This will perform callbacks... */
+ smn_status = scc_update_state();
+
+ /* SMN is on its own interrupt line. Verify the IRQ was triggered
+ * before clearing the interrupt and marking it handled. */
+ if ((os_dev_get_irq() == smn_irq) &&
+ (smn_status & SMN_STATUS_SMN_STATUS_IRQ)) {
+ SCC_WRITE_REGISTER(SMN_COMMAND, SMN_COMMAND_CLEAR_INTERRUPT);
+ handled++; /* tell kernel that interrupt was handled */
+ }
+
+ /* Check on the health of the SCM */
+ scm_status = SCC_READ_REGISTER(SCM_STATUS);
+
+ /* The driver masks interrupts, so this should never happen. */
+ if (os_dev_get_irq() == INT_SCC_SCM) {
+ /* but if it does, try to prevent it in the future */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ | SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+ handled++;
+ }
+
+ /* Any non-zero value of handled lets kernel know we got something */
+ return IRQ_RETVAL(handled);
+}
+
+/*****************************************************************************/
+/* fn scc_perform_callbacks() */
+/*****************************************************************************/
+/*! Perform callbacks registered by #scc_monitor_security_failure().
+ *
+ * Make sure callbacks only happen once... Since there may be some reason why
+ * the interrupt isn't generated, this routine could be called from base(task)
+ * level.
+ *
+ * One at a time, go through #scc_callbacks[] and call any non-null pointers.
+ */
+static void scc_perform_callbacks(void)
+{
+ static int callbacks_performed = 0;
+ unsigned long irq_flags; /* for IRQ save/restore */
+ int i;
+
+ /* Acquire lock of callbacks table and callbacks_performed flag */
+ spin_lock_irqsave(&scc_callbacks_lock, irq_flags);
+
+ if (!callbacks_performed) {
+ callbacks_performed = 1;
+
+ /* Loop over all of the entries in the table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ /* If not null, ... */
+ if (scc_callbacks[i]) {
+ scc_callbacks[i] (); /* invoke the callback routine */
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_callbacks_lock, irq_flags);
+
+ return;
+}
+
+/*****************************************************************************/
+/* fn copy_to_scc() */
+/*****************************************************************************/
+/*!
+ * Move data from possibly unaligned source and realign for SCC, possibly
+ * while calculating CRC.
+ *
+ * Multiple calls can be made to this routine (without intervening calls to
+ * #copy_from_scc(), as long as the sum total of bytes copied is a multiple of
+ * four (SCC native word size).
+ *
+ * @param[in] from Location in memory
+ * @param[out] to Location in SCC
+ * @param[in] count_bytes Number of bytes to copy
+ * @param[in,out] crc Pointer to CRC. Initial value must be
+ * #CRC_CCITT_START if this is the start of
+ * message. Output is the resulting (maybe
+ * partial) CRC. If NULL, no crc is calculated.
+ *
+ * @return Zero - success. Non-zero - SCM status bits defining failure.
+ */
+static uint32_t
+copy_to_scc(const uint8_t * from, uint32_t to, unsigned long count_bytes,
+ uint16_t * crc)
+{
+ int i;
+ uint32_t scm_word;
+ uint16_t current_crc = 0; /* local copy for fast access */
+ uint32_t status;
+
+ pr_debug("SCC: copying %ld bytes to 0x%0x.\n", count_bytes, to);
+
+ status = SCC_READ_REGISTER(SCM_ERROR_STATUS) & SCM_ACCESS_ERRORS;
+ if (status != 0) {
+ pr_debug
+ ("SCC copy_to_scc(): Error status detected (before copy):"
+ " %08x\n", status);
+ /* clear out errors left behind by somebody else */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, status);
+ }
+
+ if (crc) {
+ current_crc = *crc;
+ }
+
+ /* Initialize value being built for SCM. If we are starting 'clean',
+ * set it to zero. Otherwise pick up partial value which had been saved
+ * earlier. */
+ if (SCC_BYTE_OFFSET(to) == 0) {
+ scm_word = 0;
+ } else {
+ scm_word = SCC_READ_REGISTER(SCC_WORD_PTR(to)); /* recover */
+ }
+
+ /* Now build up SCM words and write them out when each is full */
+ for (i = 0; i < count_bytes; i++) {
+ uint8_t byte = *from++; /* value from plaintext */
+
+#if defined(__BIG_ENDIAN) || defined(FSL_HAVE_DRYICE)
+ scm_word = (scm_word << 8) | byte; /* add byte to SCM word */
+#else
+ scm_word = (byte << 24) | (scm_word >> 8);
+#endif
+ /* now calculate CCITT CRC */
+ if (crc) {
+ CALC_CRC(byte, current_crc);
+ }
+
+ to++; /* bump location in SCM */
+
+ /* check for full word */
+ if (SCC_BYTE_OFFSET(to) == 0) {
+ SCC_WRITE_REGISTER((uint32_t) (to - 4), scm_word); /* write it out */
+ }
+ }
+
+ /* If at partial word after previous loop, save it in SCM memory for
+ next time. */
+ if (SCC_BYTE_OFFSET(to) != 0) {
+ SCC_WRITE_REGISTER(SCC_WORD_PTR(to), scm_word); /* save */
+ }
+
+ /* Copy CRC back */
+ if (crc) {
+ *crc = current_crc;
+ }
+
+ status = SCC_READ_REGISTER(SCM_ERROR_STATUS) & SCM_ACCESS_ERRORS;
+ if (status != 0) {
+ pr_debug("SCC copy_to_scc(): Error status detected: %08x\n",
+ status);
+ /* Clear any/all bits. */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, status);
+ }
+ return status;
+}
+
+/*****************************************************************************/
+/* fn copy_from_scc() */
+/*****************************************************************************/
+/*!
+ * Move data from aligned 32-bit source and place in (possibly unaligned)
+ * target, and maybe calculate CRC at the same time.
+ *
+ * Multiple calls can be made to this routine (without intervening calls to
+ * #copy_to_scc(), as long as the sum total of bytes copied is be a multiple
+ * of four.
+ *
+ * @param[in] from Location in SCC
+ * @param[out] to Location in memory
+ * @param[in] count_bytes Number of bytes to copy
+ * @param[in,out] crc Pointer to CRC. Initial value must be
+ * #CRC_CCITT_START if this is the start of
+ * message. Output is the resulting (maybe
+ * partial) CRC. If NULL, crc is not calculated.
+ *
+ * @return Zero - success. Non-zero - SCM status bits defining failure.
+ */
+static uint32_t
+copy_from_scc(const uint32_t from, uint8_t * to, unsigned long count_bytes,
+ uint16_t * crc)
+{
+ uint32_t running_from = from;
+ uint32_t scm_word;
+ uint16_t current_crc = 0; /* local copy for fast access */
+ uint32_t status;
+ pr_debug("SCC: copying %ld bytes from 0x%x.\n", count_bytes, from);
+ status = SCC_READ_REGISTER(SCM_ERROR_STATUS) & SCM_ACCESS_ERRORS;
+ if (status != 0) {
+ pr_debug
+ ("SCC copy_from_scc(): Error status detected (before copy):"
+ " %08x\n", status);
+ /* clear out errors left behind by somebody else */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, status);
+ }
+
+ if (crc) {
+ current_crc = *crc;
+ }
+
+ /* Read word which is sitting in SCM memory. Ignore byte offset */
+ scm_word = SCC_READ_REGISTER(SCC_WORD_PTR(running_from));
+
+ /* If necessary, move the 'first' byte into place */
+ if (SCC_BYTE_OFFSET(running_from) != 0) {
+#if defined(__BIG_ENDIAN) || defined(FSL_HAVE_DRYICE)
+ scm_word <<= 8 * SCC_BYTE_OFFSET(running_from);
+#else
+ scm_word >>= 8 * SCC_BYTE_OFFSET(running_from);
+#endif
+ }
+
+ /* Now build up SCM words and write them out when each is full */
+ while (count_bytes--) {
+ uint8_t byte; /* value from plaintext */
+
+#if defined(__BIG_ENDIAN) || defined(FSL_HAVE_DRYICE)
+ byte = (scm_word & 0xff000000) >> 24; /* pull byte out of SCM word */
+ scm_word <<= 8; /* shift over to remove the just-pulled byte */
+#else
+ byte = (scm_word & 0xff);
+ scm_word >>= 8; /* shift over to remove the just-pulled byte */
+#endif
+ *to++ = byte; /* send byte to memory */
+
+ /* now calculate CRC */
+ if (crc) {
+ CALC_CRC(byte, current_crc);
+ }
+
+ running_from++;
+ /* check for empty word */
+ if (count_bytes && SCC_BYTE_OFFSET(running_from) == 0) {
+ /* read one in */
+ scm_word = SCC_READ_REGISTER((uint32_t) running_from);
+ }
+ }
+
+ if (crc) {
+ *crc = current_crc;
+ }
+
+ status = SCC_READ_REGISTER(SCM_ERROR_STATUS) & SCM_ACCESS_ERRORS;
+ if (status != 0) {
+ pr_debug("SCC copy_from_scc(): Error status detected: %08x\n",
+ status);
+ /* Clear any/all bits. */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, status);
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn scc_strip_padding() */
+/*****************************************************************************/
+/*!
+ * Remove padding from plaintext. Search backwards for #SCC_DRIVER_PAD_CHAR,
+ * verifying that each byte passed over is zero (0). Maximum number of bytes
+ * to examine is 8.
+ *
+ * @param[in] from Pointer to byte after end of message
+ * @param[out] count_bytes_stripped Number of padding bytes removed by this
+ * function.
+ *
+ * @return #SCC_RET_OK if all goes, well, #SCC_RET_FAIL if padding was
+ * not present.
+*/
+static scc_return_t
+scc_strip_padding(uint8_t * from, unsigned *count_bytes_stripped)
+{
+ int i = SCC_BLOCK_SIZE_BYTES();
+ scc_return_t return_code = SCC_RET_VERIFICATION_FAILED;
+
+ /*
+ * Search backwards looking for the magic marker. If it isn't found,
+ * make sure that a 0 byte is there in its place. Stop after the maximum
+ * amount of padding (8 bytes) has been searched);
+ */
+ while (i-- > 0) {
+ if (*--from == SCC_DRIVER_PAD_CHAR) {
+ *count_bytes_stripped = SCC_BLOCK_SIZE_BYTES() - i;
+ return_code = SCC_RET_OK;
+ break;
+ } else if (*from != 0) { /* if not marker, check for 0 */
+ pr_debug("SCC: Found non-zero interim pad: 0x%x\n",
+ *from);
+ break;
+ }
+ }
+
+ return return_code;
+}
+
+/*****************************************************************************/
+/* fn scc_update_state() */
+/*****************************************************************************/
+/*!
+ * Make certain SCC is still running.
+ *
+ * Side effect is to update #scc_availability and, if the state goes to failed,
+ * run #scc_perform_callbacks().
+ *
+ * (If #SCC_BRINGUP is defined, bring SCC to secure state if it is found to be
+ * in health check state)
+ *
+ * @return Current value of #SMN_STATUS register.
+ */
+static uint32_t scc_update_state(void)
+{
+ uint32_t smn_status_register = SMN_STATE_FAIL;
+ int smn_state;
+
+ /* if FAIL or UNIMPLEMENTED, don't bother */
+ if (scc_availability == SCC_STATUS_CHECKING ||
+ scc_availability == SCC_STATUS_OK) {
+
+ smn_status_register = SCC_READ_REGISTER(SMN_STATUS);
+ smn_state = smn_status_register & SMN_STATUS_STATE_MASK;
+
+#ifdef SCC_BRINGUP
+ /* If in Health Check while booting, try to 'bringup' to Secure mode */
+ if (scc_availability == SCC_STATUS_CHECKING &&
+ smn_state == SMN_STATE_HEALTH_CHECK) {
+ /* Code up a simple algorithm for the ASC */
+ SCC_WRITE_REGISTER(SMN_SEQUENCE_START, 0xaaaa);
+ SCC_WRITE_REGISTER(SMN_SEQUENCE_END, 0x5555);
+ SCC_WRITE_REGISTER(SMN_SEQUENCE_CHECK, 0x5555);
+ /* State should be SECURE now */
+ smn_status_register = SCC_READ_REGISTER(SMN_STATUS);
+ smn_state = smn_status_register & SMN_STATUS_STATE_MASK;
+ }
+#endif
+
+ /*
+ * State should be SECURE or NON_SECURE for operation of the part. If
+ * FAIL, mark failed (i.e. limited access to registers). Any other
+ * state, mark unimplemented, as the SCC is unuseable.
+ */
+ if (smn_state == SMN_STATE_SECURE
+ || smn_state == SMN_STATE_NON_SECURE) {
+ /* Healthy */
+ scc_availability = SCC_STATUS_OK;
+ } else if (smn_state == SMN_STATE_FAIL) {
+ scc_availability = SCC_STATUS_FAILED; /* uh oh - unhealthy */
+ scc_perform_callbacks();
+ os_printk(KERN_ERR "SCC: SCC went into FAILED mode\n");
+ } else {
+ /* START, ZEROIZE RAM, HEALTH CHECK, or unknown */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED; /* unuseable */
+ os_printk(KERN_ERR "SCC: SCC declared UNIMPLEMENTED\n");
+ }
+ }
+ /* if availability is initial or ok */
+ return smn_status_register;
+}
+
+/*****************************************************************************/
+/* fn scc_init_ccitt_crc() */
+/*****************************************************************************/
+/*!
+ * Populate the partial CRC lookup table.
+ *
+ * @return none
+ *
+ */
+static void scc_init_ccitt_crc(void)
+{
+ int dividend; /* index for lookup table */
+ uint16_t remainder; /* partial value for a given dividend */
+ int bit; /* index into bits of a byte */
+
+ /*
+ * Compute the remainder of each possible dividend.
+ */
+ for (dividend = 0; dividend < 256; ++dividend) {
+ /*
+ * Start with the dividend followed by zeros.
+ */
+ remainder = dividend << (8);
+
+ /*
+ * Perform modulo-2 division, a bit at a time.
+ */
+ for (bit = 8; bit > 0; --bit) {
+ /*
+ * Try to divide the current data bit.
+ */
+ if (remainder & 0x8000) {
+ remainder = (remainder << 1) ^ CRC_POLYNOMIAL;
+ } else {
+ remainder = (remainder << 1);
+ }
+ }
+
+ /*
+ * Store the result into the table.
+ */
+ scc_crc_lookup_table[dividend] = remainder;
+ }
+
+} /* scc_init_ccitt_crc() */
+
+/*****************************************************************************/
+/* fn grab_config_values() */
+/*****************************************************************************/
+/*!
+ * grab_config_values() will read the SCM Configuration and SMN Status
+ * registers and store away version and size information for later use.
+ *
+ * @return The current value of the SMN Status register.
+ */
+static uint32_t scc_grab_config_values(void)
+{
+ uint32_t config_register;
+ uint32_t smn_status_register = SMN_STATE_FAIL;
+
+ if (scc_availability != SCC_STATUS_UNIMPLEMENTED) {
+ /* access the SCC - these are 'safe' registers */
+ config_register = SCC_READ_REGISTER(SCM_CONFIGURATION);
+ pr_debug("SCC Driver: SCM config is 0x%08x\n", config_register);
+
+ /* Get SMN status and update scc_availability */
+ smn_status_register = scc_update_state();
+ pr_debug("SCC Driver: SMN status is 0x%08x\n",
+ smn_status_register);
+
+ /* save sizes and versions information for later use */
+ scc_configuration.block_size_bytes = (config_register &
+ SCM_CFG_BLOCK_SIZE_MASK)
+ >> SCM_CFG_BLOCK_SIZE_SHIFT;
+
+ scc_configuration.red_ram_size_blocks = (config_register &
+ SCM_CFG_RED_SIZE_MASK)
+ >> SCM_CFG_RED_SIZE_SHIFT;
+
+ scc_configuration.black_ram_size_blocks = (config_register &
+ SCM_CFG_BLACK_SIZE_MASK)
+ >> SCM_CFG_BLACK_SIZE_SHIFT;
+
+ scc_configuration.scm_version = (config_register
+ & SCM_CFG_VERSION_ID_MASK)
+ >> SCM_CFG_VERSION_ID_SHIFT;
+
+ scc_configuration.smn_version = (smn_status_register &
+ SMN_STATUS_VERSION_ID_MASK)
+ >> SMN_STATUS_VERSION_ID_SHIFT;
+
+ if (scc_configuration.scm_version != SCM_VERSION_1) {
+ scc_availability = SCC_STATUS_UNIMPLEMENTED; /* Unknown version */
+ }
+
+ scc_memory_size_bytes = (SCC_BLOCK_SIZE_BYTES() *
+ scc_configuration.
+ black_ram_size_blocks)
+ - SCM_NON_RESERVED_OFFSET;
+
+ /* This last is for driver consumption only */
+ scm_highest_memory_address = SCM_BLACK_MEMORY +
+ (SCC_BLOCK_SIZE_BYTES() *
+ scc_configuration.black_ram_size_blocks);
+ }
+
+ return smn_status_register;
+} /* grab_config_values */
+
+/*****************************************************************************/
+/* fn setup_interrupt_handling() */
+/*****************************************************************************/
+/*!
+ * Register the SCM and SMN interrupt handlers.
+ *
+ * Called from #scc_init()
+ *
+ * @return 0 on success
+ */
+static int setup_interrupt_handling(void)
+{
+ int smn_error_code = -1;
+ int scm_error_code = -1;
+
+ /* Disnable SCM interrupts */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ | SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+
+#ifdef USE_SMN_INTERRUPT
+ /* Install interrupt service routine for SMN. */
+ smn_error_code = os_register_interrupt(SCC_DRIVER_NAME,
+ INT_SCC_SMN, scc_irq);
+ if (smn_error_code != 0) {
+ os_printk
+ ("SCC Driver: Error installing SMN Interrupt Handler: %d\n",
+ smn_error_code);
+ } else {
+ smn_irq_set = 1; /* remember this for cleanup */
+ /* Enable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND,
+ SMN_COMMAND_CLEAR_INTERRUPT |
+ SMN_COMMAND_ENABLE_INTERRUPT);
+ }
+#else
+ smn_error_code = 0; /* no problems... will handle later */
+#endif
+
+ /*
+ * Install interrupt service routine for SCM (or both together).
+ */
+ scm_error_code = os_register_interrupt(SCC_DRIVER_NAME,
+ INT_SCC_SCM, scc_irq);
+ if (scm_error_code != 0) {
+#ifndef MXC
+ os_printk
+ ("SCC Driver: Error installing SCM Interrupt Handler: %d\n",
+ scm_error_code);
+#else
+ os_printk
+ ("SCC Driver: Error installing SCC Interrupt Handler: %d\n",
+ scm_error_code);
+#endif
+ } else {
+ scm_irq_set = 1; /* remember this for cleanup */
+#if defined(USE_SMN_INTERRUPT) && !defined(NO_SMN_INTERRUPT)
+ /* Enable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND,
+ SMN_COMMAND_CLEAR_INTERRUPT |
+ SMN_COMMAND_ENABLE_INTERRUPT);
+#endif
+ }
+
+ /* Return an error if one was encountered */
+ return scm_error_code ? scm_error_code : smn_error_code;
+} /* setup_interrupt_handling */
+
+/*****************************************************************************/
+/* fn scc_do_crypto() */
+/*****************************************************************************/
+/*! Have the SCM perform the crypto function.
+ *
+ * Set up length register, and the store @c scm_control into control register
+ * to kick off the operation. Wait for completion, gather status, clear
+ * interrupt / status.
+ *
+ * @param byte_count number of bytes to perform in this operation
+ * @param scm_control Bit values to be set in @c SCM_CONTROL register
+ *
+ * @return 0 on success, value of #SCM_ERROR_STATUS on failure
+ */
+static uint32_t scc_do_crypto(int byte_count, uint32_t scm_control)
+{
+ int block_count = byte_count / SCC_BLOCK_SIZE_BYTES();
+ uint32_t crypto_status;
+
+ /* clear any outstanding flags */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ | SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+
+ /* In length register, 0 means 1, etc. */
+ SCC_WRITE_REGISTER(SCM_LENGTH, block_count - 1);
+
+ /* set modes and kick off the operation */
+ SCC_WRITE_REGISTER(SCM_CONTROL, scm_control);
+
+ scc_wait_completion();
+
+ /* Mask for done and error bits */
+ crypto_status = SCC_READ_REGISTER(SCM_STATUS)
+ & (SCM_STATUS_CIPHERING_DONE
+ | SCM_STATUS_LENGTH_ERROR | SCM_STATUS_INTERNAL_ERROR);
+
+ /* Only done bit should be on */
+ if (crypto_status != SCM_STATUS_CIPHERING_DONE) {
+ /* Replace with error status instead */
+ crypto_status = SCC_READ_REGISTER(SCM_ERROR_STATUS);
+ pr_debug("SCM Failure: 0x%x\n", crypto_status);
+ if (crypto_status == 0) {
+ /* That came up 0. Turn on arbitrary bit to signal error. */
+ crypto_status = SCM_ERR_INTERNAL_ERROR;
+ }
+ } else {
+ crypto_status = 0;
+ }
+
+ pr_debug("SCC: Done waiting.\n");
+
+ /* Clear out any status. */
+ SCC_WRITE_REGISTER(SCM_INTERRUPT_CTRL,
+ SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT
+ | SCM_INTERRUPT_CTRL_MASK_INTERRUPTS);
+
+ /* And clear any error status */
+ SCC_WRITE_REGISTER(SCM_ERROR_STATUS, 0);
+
+ return crypto_status;
+}
+
+/*****************************************************************************/
+/* fn scc_encrypt() */
+/*****************************************************************************/
+/*!
+ * Perform an encryption on the input. If @c verify_crc is true, a CRC must be
+ * calculated on the plaintext, and appended, with padding, before computing
+ * the ciphertext.
+ *
+ * @param[in] count_in_bytes Count of bytes of plaintext
+ * @param[in] data_in Pointer to the plaintext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing ciphertext
+ * @param[in] add_crc Flag for computing CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+ */
+static scc_return_t
+scc_encrypt(uint32_t count_in_bytes, const uint8_t * data_in,
+ uint32_t scm_control,
+ uint8_t * data_out, int add_crc, unsigned long *count_out_bytes)
+
+{
+ scc_return_t return_code = SCC_RET_FAIL; /* initialised for failure */
+ uint32_t input_bytes_left = count_in_bytes; /* local copy */
+ uint32_t output_bytes_copied = 0; /* running total */
+ uint32_t bytes_to_process; /* multi-purpose byte counter */
+ uint16_t crc = CRC_CCITT_START; /* running CRC value */
+ crc_t *crc_ptr = NULL; /* Reset if CRC required */
+ /* byte address into SCM RAM */
+ uint32_t scm_location = SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET;
+ /* free RED RAM */
+ uint32_t scm_bytes_remaining = scc_memory_size_bytes;
+ /* CRC+padding holder */
+ uint8_t padding_buffer[PADDING_BUFFER_MAX_BYTES];
+ unsigned padding_byte_count = 0; /* Reset if padding required */
+ uint32_t scm_error_status = 0; /* No known SCM error initially */
+ uint32_t i; /* Counter for clear data loop */
+ uint32_t dirty_bytes; /* Number of bytes of memory used
+ temporarily during encryption,
+ which need to be wiped after
+ completion of the operation. */
+
+ /* Set location of CRC and prepare padding bytes if required */
+ if (add_crc != 0) {
+ crc_ptr = &crc;
+ padding_byte_count = SCC_BLOCK_SIZE_BYTES()
+ - (count_in_bytes +
+ CRC_SIZE_BYTES) % SCC_BLOCK_SIZE_BYTES();
+ memcpy(padding_buffer + CRC_SIZE_BYTES, scc_block_padding,
+ padding_byte_count);
+ }
+
+ /* Process remaining input or padding data */
+ while (input_bytes_left > 0) {
+
+ /* Determine how much work to do this pass */
+ bytes_to_process = (input_bytes_left > scm_bytes_remaining) ?
+ scm_bytes_remaining : input_bytes_left;
+
+ /* Copy plaintext into SCM RAM, calculating CRC if required */
+ copy_to_scc(data_in, scm_location, bytes_to_process, crc_ptr);
+
+ /* Adjust pointers & counters */
+ input_bytes_left -= bytes_to_process;
+ data_in += bytes_to_process;
+ scm_location += bytes_to_process;
+ scm_bytes_remaining -= bytes_to_process;
+
+ /* Add CRC and padding after the last byte is copied if required */
+ if ((input_bytes_left == 0) && (crc_ptr != NULL)) {
+
+ /* Copy CRC into padding buffer MSB first */
+ padding_buffer[0] = (crc >> 8) & 0xFF;
+ padding_buffer[1] = crc & 0xFF;
+
+ /* Reset pointers and counter */
+ data_in = padding_buffer;
+ input_bytes_left = CRC_SIZE_BYTES + padding_byte_count;
+ crc_ptr = NULL; /* CRC no longer required */
+
+ /* Go round loop again to copy CRC and padding to SCM */
+ continue;
+ }
+
+ /* if no input and crc_ptr */
+ /* Now have block-sized plaintext in SCM to encrypt */
+ /* Encrypt plaintext; exit loop on error */
+ bytes_to_process = scm_location -
+ (SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET);
+
+ if (output_bytes_copied + bytes_to_process > *count_out_bytes) {
+ return_code = SCC_RET_INSUFFICIENT_SPACE;
+ scm_error_status = -1; /* error signal */
+ pr_debug
+ ("SCC: too many ciphertext bytes for space available\n");
+ break;
+ }
+ pr_debug("SCC: Starting encryption. %x for %d bytes (%p/%p)\n",
+ scm_control, bytes_to_process,
+ (void *)SCC_READ_REGISTER(SCM_RED_START),
+ (void *)SCC_READ_REGISTER(SCM_BLACK_START));
+ scm_error_status = scc_do_crypto(bytes_to_process, scm_control);
+ if (scm_error_status != 0) {
+ break;
+ }
+
+ /* Copy out ciphertext */
+ copy_from_scc(SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET,
+ data_out, bytes_to_process, NULL);
+
+ /* Adjust pointers and counters for next loop */
+ output_bytes_copied += bytes_to_process;
+ data_out += bytes_to_process;
+ scm_location = SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET;
+ scm_bytes_remaining = scc_memory_size_bytes;
+
+ } /* input_bytes_left > 0 */
+ /* Clear all red and black memory used during ephemeral encryption */
+ dirty_bytes = (count_in_bytes > scc_memory_size_bytes) ?
+ scc_memory_size_bytes : count_in_bytes;
+
+ for (i = 0; i < dirty_bytes; i += 4) {
+ SCC_WRITE_REGISTER(SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET + i,
+ 0);
+ SCC_WRITE_REGISTER(SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET +
+ i, 0);
+ }
+
+ /* If no SCM error, set OK status and save ouput byte count */
+ if (scm_error_status == 0) {
+ return_code = SCC_RET_OK;
+ *count_out_bytes = output_bytes_copied;
+ }
+
+ return return_code;
+} /* scc_encrypt */
+
+/*****************************************************************************/
+/* fn scc_decrypt() */
+/*****************************************************************************/
+/*!
+ * Perform a decryption on the input. If @c verify_crc is true, the last block
+ * (maybe the two last blocks) is special - it should contain a CRC and
+ * padding. These must be stripped and verified.
+ *
+ * @param[in] count_in_bytes Count of bytes of ciphertext
+ * @param[in] data_in Pointer to the ciphertext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing plaintext
+ * @param[in] verify_crc Flag for running CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+
+ */
+static scc_return_t
+scc_decrypt(uint32_t count_in_bytes, const uint8_t * data_in,
+ uint32_t scm_control,
+ uint8_t * data_out, int verify_crc, unsigned long *count_out_bytes)
+{
+ scc_return_t return_code = SCC_RET_FAIL;
+ uint32_t bytes_left = count_in_bytes; /* local copy */
+ uint32_t bytes_copied = 0; /* running total of bytes going to user */
+ uint32_t bytes_to_copy = 0; /* Number in this encryption 'chunk' */
+ uint16_t crc = CRC_CCITT_START; /* running CRC value */
+ /* next target for ctext */
+ uint32_t scm_location = SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET;
+ unsigned padding_byte_count; /* number of bytes of padding stripped */
+ uint8_t last_two_blocks[2 * SCC_BLOCK_SIZE_BYTES()]; /* temp */
+ uint32_t scm_error_status = 0; /* register value */
+ uint32_t i; /* Counter for clear data loop */
+ uint32_t dirty_bytes; /* Number of bytes of memory used
+ temporarily during decryption,
+ which need to be wiped after
+ completion of the operation. */
+
+ scm_control |= SCM_DECRYPT_MODE;
+
+ if (verify_crc) {
+ /* Save last two blocks (if there are at least two) of ciphertext for
+ special treatment. */
+ bytes_left -= SCC_BLOCK_SIZE_BYTES();
+ if (bytes_left >= SCC_BLOCK_SIZE_BYTES()) {
+ bytes_left -= SCC_BLOCK_SIZE_BYTES();
+ }
+ }
+
+ /* Copy ciphertext into SCM BLACK memory */
+ while (bytes_left && scm_error_status == 0) {
+
+ /* Determine how much work to do this pass */
+ if (bytes_left > (scc_memory_size_bytes)) {
+ bytes_to_copy = scc_memory_size_bytes;
+ } else {
+ bytes_to_copy = bytes_left;
+ }
+
+ if (bytes_copied + bytes_to_copy > *count_out_bytes) {
+ scm_error_status = -1;
+ break;
+ }
+ copy_to_scc(data_in, scm_location, bytes_to_copy, NULL);
+ data_in += bytes_to_copy; /* move pointer */
+
+ pr_debug("SCC: Starting decryption of %d bytes.\n",
+ bytes_to_copy);
+
+ /* Do the work, wait for completion */
+ scm_error_status = scc_do_crypto(bytes_to_copy, scm_control);
+
+ copy_from_scc(SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET,
+ data_out, bytes_to_copy, &crc);
+ bytes_copied += bytes_to_copy;
+ data_out += bytes_to_copy;
+ scm_location = SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET;
+
+ /* Do housekeeping */
+ bytes_left -= bytes_to_copy;
+
+ } /* while bytes_left */
+
+ /* At this point, either the process is finished, or this is verify mode */
+
+ if (scm_error_status == 0) {
+ if (!verify_crc) {
+ *count_out_bytes = bytes_copied;
+ return_code = SCC_RET_OK;
+ } else {
+ /* Verify mode. There are one or two blocks of unprocessed
+ * ciphertext sitting at data_in. They need to be moved to the
+ * SCM, decrypted, searched to remove padding, then the plaintext
+ * copied back to the user (while calculating CRC, of course).
+ */
+
+ /* Calculate ciphertext still left */
+ bytes_to_copy = count_in_bytes - bytes_copied;
+
+ copy_to_scc(data_in, scm_location, bytes_to_copy, NULL);
+ data_in += bytes_to_copy; /* move pointer */
+
+ pr_debug("SCC: Finishing decryption (%d bytes).\n",
+ bytes_to_copy);
+
+ /* Do the work, wait for completion */
+ scm_error_status =
+ scc_do_crypto(bytes_to_copy, scm_control);
+
+ if (scm_error_status == 0) {
+ /* Copy decrypted data back from SCM RED memory */
+ copy_from_scc(SCM_RED_MEMORY +
+ SCM_NON_RESERVED_OFFSET,
+ last_two_blocks, bytes_to_copy,
+ NULL);
+
+ /* (Plaintext) + crc + padding should be in temp buffer */
+ if (scc_strip_padding
+ (last_two_blocks + bytes_to_copy,
+ &padding_byte_count) == SCC_RET_OK) {
+ bytes_to_copy -=
+ padding_byte_count + CRC_SIZE_BYTES;
+
+ /* verify enough space in user buffer */
+ if (bytes_copied + bytes_to_copy <=
+ *count_out_bytes) {
+ int i = 0;
+
+ /* Move out last plaintext and calc CRC */
+ while (i < bytes_to_copy) {
+ CALC_CRC(last_two_blocks
+ [i], crc);
+ *data_out++ =
+ last_two_blocks
+ [i++];
+ bytes_copied++;
+ }
+
+ /* Verify the CRC by running over itself */
+ CALC_CRC(last_two_blocks
+ [bytes_to_copy], crc);
+ CALC_CRC(last_two_blocks
+ [bytes_to_copy + 1],
+ crc);
+ if (crc == 0) {
+ /* Just fine ! */
+ *count_out_bytes =
+ bytes_copied;
+ return_code =
+ SCC_RET_OK;
+ } else {
+ return_code =
+ SCC_RET_VERIFICATION_FAILED;
+ pr_debug
+ ("SCC: CRC values are %04x, %02x%02x\n",
+ crc,
+ last_two_blocks
+ [bytes_to_copy],
+ last_two_blocks
+ [bytes_to_copy +
+ 1]);
+ }
+ } /* if space available */
+ } /* if scc_strip_padding... */
+ else {
+ /* bad padding means bad verification */
+ return_code =
+ SCC_RET_VERIFICATION_FAILED;
+ }
+ }
+ /* scm_error_status == 0 */
+ } /* verify_crc */
+ }
+
+ /* scm_error_status == 0 */
+ /* Clear all red and black memory used during ephemeral decryption */
+ dirty_bytes = (count_in_bytes > scc_memory_size_bytes) ?
+ scc_memory_size_bytes : count_in_bytes;
+
+ for (i = 0; i < dirty_bytes; i += 4) {
+ SCC_WRITE_REGISTER(SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET + i,
+ 0);
+ SCC_WRITE_REGISTER(SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET +
+ i, 0);
+ }
+ return return_code;
+} /* scc_decrypt */
+
+/*****************************************************************************/
+/* fn scc_alloc_slot() */
+/*****************************************************************************/
+/*!
+ * Allocate a key slot to fit the requested size.
+ *
+ * @param value_size_bytes Size of the key or other secure data
+ * @param owner_id Value to tie owner to slot
+ * @param[out] slot Handle to access or deallocate slot
+ *
+ * @return SCC_RET_OK on success, SCC_RET_INSUFFICIENT_SPACE if not slots of
+ * requested size are available.
+ */
+scc_return_t
+scc_alloc_slot(uint32_t value_size_bytes, uint64_t owner_id, uint32_t * slot)
+{
+ scc_return_t status = SCC_RET_FAIL;
+ unsigned long irq_flags;
+
+ if (scc_availability != SCC_STATUS_OK) {
+ goto out;
+ }
+ /* ACQUIRE LOCK to prevent others from using SCC crypto */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ pr_debug("SCC: Allocating %d-byte slot for 0x%Lx\n",
+ value_size_bytes, owner_id);
+
+ if ((value_size_bytes != 0) && (value_size_bytes <= SCC_MAX_KEY_SIZE)) {
+ int i;
+
+ for (i = 0; i < SCC_KEY_SLOTS; i++) {
+ if (scc_key_info[i].status == 0) {
+ scc_key_info[i].owner_id = owner_id;
+ scc_key_info[i].length = value_size_bytes;
+ scc_key_info[i].status = 1; /* assigned! */
+ *slot = i;
+ status = SCC_RET_OK;
+ break; /* exit 'for' loop */
+ }
+ }
+
+ if (status != SCC_RET_OK) {
+ status = SCC_RET_INSUFFICIENT_SPACE;
+ } else {
+ pr_debug("SCC: Allocated slot %d (0x%Lx)\n", i,
+ owner_id);
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ out:
+ return status;
+}
+
+/*****************************************************************************/
+/* fn verify_slot_access() */
+/*****************************************************************************/
+inline static scc_return_t
+verify_slot_access(uint64_t owner_id, uint32_t slot, uint32_t access_len)
+{
+ scc_return_t status = SCC_RET_FAIL;
+ if (scc_availability != SCC_STATUS_OK) {
+ goto out;
+ }
+
+ if ((slot < SCC_KEY_SLOTS) && scc_key_info[slot].status
+ && (scc_key_info[slot].owner_id == owner_id)
+ && (access_len <= SCC_KEY_SLOT_SIZE)) {
+ status = SCC_RET_OK;
+ pr_debug("SCC: Verify on slot %d succeeded\n", slot);
+ } else {
+ if (slot >= SCC_KEY_SLOTS) {
+ pr_debug("SCC: Verify on bad slot (%d) failed\n", slot);
+ } else if (scc_key_info[slot].status) {
+ pr_debug("SCC: Verify on slot %d failed (%Lx) \n", slot,
+ owner_id);
+ } else {
+ pr_debug
+ ("SCC: Verify on slot %d failed: not allocated\n",
+ slot);
+ }
+ }
+
+ out:
+ return status;
+}
+
+scc_return_t
+scc_verify_slot_access(uint64_t owner_id, uint32_t slot, uint32_t access_len)
+{
+ return verify_slot_access(owner_id, slot, access_len);
+}
+
+/*****************************************************************************/
+/* fn scc_dealloc_slot() */
+/*****************************************************************************/
+scc_return_t scc_dealloc_slot(uint64_t owner_id, uint32_t slot)
+{
+ scc_return_t status;
+ unsigned long irq_flags;
+ int i;
+
+ /* ACQUIRE LOCK to prevent others from using SCC crypto */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ status = verify_slot_access(owner_id, slot, 0);
+
+ if (status == SCC_RET_OK) {
+ scc_key_info[slot].owner_id = 0;
+ scc_key_info[slot].status = 0; /* unassign */
+
+ /* clear old info */
+ for (i = 0; i < SCC_KEY_SLOT_SIZE; i += 4) {
+ SCC_WRITE_REGISTER(SCM_RED_MEMORY +
+ scc_key_info[slot].offset + i, 0);
+ SCC_WRITE_REGISTER(SCM_BLACK_MEMORY +
+ scc_key_info[slot].offset + i, 0);
+ }
+ pr_debug("SCC: Deallocated slot %d\n", slot);
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn scc_load_slot() */
+/*****************************************************************************/
+/*!
+ * Load a value into a slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param key_data Data to load into the slot
+ * @param key_length Length, in bytes, of @c key_data to copy to SCC.
+ *
+ * @return SCC_RET_OK on success. SCC_RET_FAIL will be returned if slot
+ * specified cannot be accessed for any reason, or SCC_RET_INSUFFICIENT_SPACE
+ * if @c key_length exceeds the size of the slot.
+ */
+scc_return_t
+scc_load_slot(uint64_t owner_id, uint32_t slot, const uint8_t * key_data,
+ uint32_t key_length)
+{
+ scc_return_t status;
+ unsigned long irq_flags;
+
+ /* ACQUIRE LOCK to prevent others from using SCC crypto */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ status = verify_slot_access(owner_id, slot, key_length);
+ if ((status == SCC_RET_OK) && (key_data != NULL)) {
+ status = SCC_RET_FAIL; /* reset expectations */
+
+ if (key_length > SCC_KEY_SLOT_SIZE) {
+ pr_debug
+ ("SCC: scc_load_slot() rejecting key of %d bytes.\n",
+ key_length);
+ status = SCC_RET_INSUFFICIENT_SPACE;
+ } else {
+ if (copy_to_scc(key_data,
+ SCM_RED_MEMORY +
+ scc_key_info[slot].offset, key_length,
+ NULL)) {
+ pr_debug("SCC: RED copy_to_scc() failed for"
+ " scc_load_slot()\n");
+ } else {
+ if ((key_length % 4) != 0) {
+ uint32_t zeros = 0;
+
+ /* zero-pad to get remainder bytes in correct place */
+ copy_to_scc((uint8_t *) & zeros,
+ SCM_RED_MEMORY
+ +
+ scc_key_info[slot].offset +
+ key_length,
+ 4 - (key_length % 4), NULL);
+ }
+ status = SCC_RET_OK;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+} /* scc_load_slot */
+
+scc_return_t
+scc_read_slot(uint64_t owner_id, uint32_t slot, uint32_t key_length,
+ uint8_t * key_data)
+{
+ scc_return_t status;
+ unsigned long irq_flags;
+
+ /* ACQUIRE LOCK to prevent others from using SCC crypto */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ status = verify_slot_access(owner_id, slot, key_length);
+ if ((status == SCC_RET_OK) && (key_data != NULL)) {
+ status = SCC_RET_FAIL; /* reset expectations */
+
+ if (key_length > SCC_KEY_SLOT_SIZE) {
+ pr_debug
+ ("SCC: scc_read_slot() rejecting key of %d bytes.\n",
+ key_length);
+ status = SCC_RET_INSUFFICIENT_SPACE;
+ } else {
+ if (copy_from_scc
+ (SCM_RED_MEMORY + scc_key_info[slot].offset,
+ key_data, key_length, NULL)) {
+ pr_debug("SCC: RED copy_from_scc() failed for"
+ " scc_read_slot()\n");
+ } else {
+ status = SCC_RET_OK;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+} /* scc_read_slot */
+
+/*****************************************************************************/
+/* fn scc_encrypt_slot() */
+/*****************************************************************************/
+/*!
+ * Encrypt the key data stored in a slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param length Length, in bytes, of @c black_data
+ * @param black_data Location to store result of encrypting RED data in slot
+ *
+ * @return SCC_RET_OK on success, SCC_RET_FAIL if slot specified cannot be
+ * accessed for any reason.
+ */
+scc_return_t scc_encrypt_slot(uint64_t owner_id, uint32_t slot,
+ uint32_t length, uint8_t * black_data)
+{
+ unsigned long irq_flags;
+ scc_return_t status;
+ uint32_t crypto_status;
+ uint32_t slot_offset =
+ scc_key_info[slot].offset / SCC_BLOCK_SIZE_BYTES();
+
+ /* ACQUIRE LOCK to prevent others from using crypto or releasing slot */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ status = verify_slot_access(owner_id, slot, length);
+ if (status == SCC_RET_OK) {
+ SCC_WRITE_REGISTER(SCM_BLACK_START, slot_offset);
+ SCC_WRITE_REGISTER(SCM_RED_START, slot_offset);
+
+ /* Use OwnerID as CBC IV to tie Owner to data */
+ SCC_WRITE_REGISTER(SCM_INIT_VECTOR_0, *(uint32_t *) & owner_id);
+ SCC_WRITE_REGISTER(SCM_INIT_VECTOR_1,
+ *(((uint32_t *) & owner_id) + 1));
+
+ /* Set modes and kick off the encryption */
+ crypto_status = scc_do_crypto(length,
+ SCM_CONTROL_START_CIPHER |
+ SCM_CBC_MODE);
+
+ if (crypto_status != 0) {
+ pr_debug("SCM encrypt red crypto failure: 0x%x\n",
+ crypto_status);
+ } else {
+
+ /* Give blob back to caller */
+ if (!copy_from_scc
+ (SCM_BLACK_MEMORY + scc_key_info[slot].offset,
+ black_data, length, NULL)) {
+ status = SCC_RET_OK;
+ pr_debug("SCC: Encrypted slot %d\n", slot);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn scc_decrypt_slot() */
+/*****************************************************************************/
+/*!
+ * Decrypt some black data and leave result in the slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param length Length, in bytes, of @c black_data
+ * @param black_data Location of data to dencrypt and store in slot
+ *
+ * @return SCC_RET_OK on success, SCC_RET_FAIL if slot specified cannot be
+ * accessed for any reason.
+ */
+scc_return_t scc_decrypt_slot(uint64_t owner_id, uint32_t slot,
+ uint32_t length, const uint8_t * black_data)
+{
+ unsigned long irq_flags;
+ scc_return_t status;
+ uint32_t crypto_status;
+ uint32_t slot_offset =
+ scc_key_info[slot].offset / SCC_BLOCK_SIZE_BYTES();
+
+ /* ACQUIRE LOCK to prevent others from using crypto or releasing slot */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+
+ status = verify_slot_access(owner_id, slot, length);
+ if (status == SCC_RET_OK) {
+ status = SCC_RET_FAIL; /* reset expectations */
+
+ /* Place black key in to BLACK RAM and set up the SCC */
+ copy_to_scc(black_data,
+ SCM_BLACK_MEMORY + scc_key_info[slot].offset,
+ length, NULL);
+
+ SCC_WRITE_REGISTER(SCM_BLACK_START, slot_offset);
+ SCC_WRITE_REGISTER(SCM_RED_START, slot_offset);
+
+ /* Use OwnerID as CBC IV to tie Owner to data */
+ SCC_WRITE_REGISTER(SCM_INIT_VECTOR_0, *(uint32_t *) & owner_id);
+ SCC_WRITE_REGISTER(SCM_INIT_VECTOR_1,
+ *(((uint32_t *) & owner_id) + 1));
+
+ /* Set modes and kick off the decryption */
+ crypto_status = scc_do_crypto(length,
+ SCM_CONTROL_START_CIPHER
+ | SCM_CBC_MODE |
+ SCM_DECRYPT_MODE);
+
+ if (crypto_status != 0) {
+ pr_debug("SCM decrypt black crypto failure: 0x%x\n",
+ crypto_status);
+ } else {
+ status = SCC_RET_OK;
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn scc_get_slot_info() */
+/*****************************************************************************/
+/*!
+ * Determine address and value length for a give slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param address Location to store kernel address of slot data
+ * @param value_size_bytes Location to store allocated length of data in slot.
+ * May be NULL if value is not needed by caller.
+ * @param slot_size_bytes Location to store max length data in slot
+ * May be NULL if value is not needed by caller.
+ *
+ * @return SCC_RET_OK or error indication
+ */
+scc_return_t
+scc_get_slot_info(uint64_t owner_id, uint32_t slot, uint32_t * address,
+ uint32_t * value_size_bytes, uint32_t * slot_size_bytes)
+{
+ scc_return_t status = verify_slot_access(owner_id, slot, 0);
+
+ if (status == SCC_RET_OK) {
+ *address =
+ SCC_BASE + SCM_RED_MEMORY + scc_key_info[slot].offset;
+ if (value_size_bytes != NULL) {
+ *value_size_bytes = scc_key_info[slot].length;
+ }
+ if (slot_size_bytes != NULL) {
+ *slot_size_bytes = SCC_KEY_SLOT_SIZE;
+ }
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn scc_wait_completion() */
+/*****************************************************************************/
+/*!
+ * Poll looking for end-of-cipher indication. Only used
+ * if @c SCC_SCM_SLEEP is not defined.
+ *
+ * @internal
+ *
+ * On a Tahiti, crypto under 230 or so bytes is done after the first loop, all
+ * the way up to five sets of spins for 1024 bytes. (8- and 16-byte functions
+ * are done when we first look. Zeroizing takes one pass around.
+ */
+static void scc_wait_completion(void)
+{
+ int i = 0;
+
+ /* check for completion by polling */
+ while (!is_cipher_done() && (i++ < SCC_CIPHER_MAX_POLL_COUNT)) {
+ udelay(10);
+ }
+ pr_debug("SCC: Polled DONE %d times\n", i);
+} /* scc_wait_completion() */
+
+/*****************************************************************************/
+/* fn is_cipher_done() */
+/*****************************************************************************/
+/*!
+ * This function returns non-zero if SCM Status register indicates
+ * that a cipher has terminated or some other interrupt-generating
+ * condition has occurred.
+ */
+static int is_cipher_done(void)
+{
+ register uint32_t scm_status;
+ register int cipher_done;
+
+ scm_status = SCC_READ_REGISTER(SCM_STATUS);
+
+ /*
+ * Done when 'SCM is currently performing a function' bits are zero
+ */
+ cipher_done = !(scm_status & (SCM_STATUS_ZEROIZING |
+ SCM_STATUS_CIPHERING));
+
+ return cipher_done;
+} /* is_cipher_done() */
+
+/*****************************************************************************/
+/* fn offset_within_smn() */
+/*****************************************************************************/
+/*!
+ * Check that the offset is with the bounds of the SMN register set.
+ *
+ * @param[in] register_offset register offset of SMN.
+ *
+ * @return 1 if true, 0 if false (not within SMN)
+ */
+static inline int offset_within_smn(uint32_t register_offset)
+{
+ return register_offset >= SMN_STATUS && register_offset <= SMN_TIMER;
+}
+
+/*****************************************************************************/
+/* fn offset_within_scm() */
+/*****************************************************************************/
+/*!
+ * Check that the offset is with the bounds of the SCM register set.
+ *
+ * @param[in] register_offset Register offset of SCM
+ *
+ * @return 1 if true, 0 if false (not within SCM)
+ */
+static inline int offset_within_scm(uint32_t register_offset)
+{
+ return (register_offset >= SCM_RED_START)
+ && (register_offset < scm_highest_memory_address);
+ /* Although this would cause trouble for zeroize testing, this change would
+ * close a security whole which currently allows any kernel program to access
+ * any location in RED RAM. Perhaps enforce in non-SCC_DEBUG compiles?
+ && (register_offset <= SCM_INIT_VECTOR_1); */
+}
+
+/*****************************************************************************/
+/* fn check_register_accessible() */
+/*****************************************************************************/
+/*!
+ * Given the current SCM and SMN status, verify that access to the requested
+ * register should be OK.
+ *
+ * @param[in] register_offset register offset within SCC
+ * @param[in] smn_status recent value from #SMN_STATUS
+ * @param[in] scm_status recent value from #SCM_STATUS
+ *
+ * @return #SCC_RET_OK if ok, #SCC_RET_FAIL if not
+ */
+static scc_return_t
+check_register_accessible(uint32_t register_offset, uint32_t smn_status,
+ uint32_t scm_status)
+{
+ int error_code = SCC_RET_FAIL;
+
+ /* Verify that the register offset passed in is not among the verboten set
+ * if the SMN is in Fail mode.
+ */
+ if (offset_within_smn(register_offset)) {
+ if ((smn_status & SMN_STATUS_STATE_MASK) == SMN_STATE_FAIL) {
+ if (!((register_offset == SMN_STATUS) ||
+ (register_offset == SMN_COMMAND) ||
+ (register_offset == SMN_DEBUG_DETECT_STAT))) {
+ pr_debug
+ ("SCC Driver: Note: Security State is in FAIL state.\n");
+ } /* register not a safe one */
+ else {
+ /* SMN is in FAIL, but register is a safe one */
+ error_code = SCC_RET_OK;
+ }
+ } /* State is FAIL */
+ else {
+ /* State is not fail. All registers accessible. */
+ error_code = SCC_RET_OK;
+ }
+ }
+ /* offset within SMN */
+ /* Not SCM register. Check for SCM busy. */
+ else if (offset_within_scm(register_offset)) {
+ /* This is the 'cannot access' condition in the SCM */
+ if ((scm_status & SCM_STATUS_BUSY)
+ /* these are always available - rest fail on busy */
+ && !((register_offset == SCM_STATUS) ||
+ (register_offset == SCM_ERROR_STATUS) ||
+ (register_offset == SCM_INTERRUPT_CTRL) ||
+ (register_offset == SCM_CONFIGURATION))) {
+ pr_debug
+ ("SCC Driver: Note: Secure Memory is in BUSY state.\n");
+ } /* status is busy & register inaccessible */
+ else {
+ error_code = SCC_RET_OK;
+ }
+ }
+ /* offset within SCM */
+ return error_code;
+
+} /* check_register_accessible() */
+
+/*****************************************************************************/
+/* fn check_register_offset() */
+/*****************************************************************************/
+/*!
+ * Check that the offset is with the bounds of the SCC register set.
+ *
+ * @param[in] register_offset register offset of SMN.
+ *
+ * #SCC_RET_OK if ok, #SCC_RET_FAIL if not
+ */
+static scc_return_t check_register_offset(uint32_t register_offset)
+{
+ int return_value = SCC_RET_FAIL;
+
+ /* Is it valid word offset ? */
+ if (SCC_BYTE_OFFSET(register_offset) == 0) {
+ /* Yes. Is register within SCM? */
+ if (offset_within_scm(register_offset)) {
+ return_value = SCC_RET_OK; /* yes, all ok */
+ }
+ /* Not in SCM. Now look within the SMN */
+ else if (offset_within_smn(register_offset)) {
+ return_value = SCC_RET_OK; /* yes, all ok */
+ }
+ }
+
+ return return_value;
+}
+
+#ifdef SCC_REGISTER_DEBUG
+
+/*****************************************************************************/
+/* fn dbg_scc_read_register() */
+/*****************************************************************************/
+/*!
+ * Noisily read a 32-bit value to an SCC register.
+ * @param offset The address of the register to read.
+ *
+ * @return The register value
+ * */
+static uint32_t dbg_scc_read_register(uint32_t offset)
+{
+ uint32_t value;
+
+ value = readl(scc_base + offset);
+
+#ifndef SCC_RAM_DEBUG /* print no RAM references */
+ if ((offset < SCM_RED_MEMORY) || (offset >= scm_highest_memory_address)) {
+#endif
+ pr_debug("SCC RD: 0x%4x : 0x%08x\n", offset, value);
+#ifndef SCC_RAM_DEBUG
+ }
+#endif
+
+ return value;
+}
+
+/*****************************************************************************/
+/* fn dbg_scc_write_register() */
+/*****************************************************************************/
+/*
+ * Noisily read a 32-bit value to an SCC register.
+ * @param offset The address of the register to written.
+ *
+ * @param value The new register value
+ */
+static void dbg_scc_write_register(uint32_t offset, uint32_t value)
+{
+
+#ifndef SCC_RAM_DEBUG /* print no RAM references */
+ if ((offset < SCM_RED_MEMORY) || (offset >= scm_highest_memory_address)) {
+#endif
+ pr_debug("SCC WR: 0x%4x : 0x%08x\n", offset, value);
+#ifndef SCC_RAM_DEBUG
+ }
+#endif
+
+ (void)writel(value, scc_base + offset);
+}
+
+#endif /* SCC_REGISTER_DEBUG */
diff --git a/drivers/mxc/security/mxc_scc_internals.h b/drivers/mxc/security/mxc_scc_internals.h
new file mode 100644
index 000000000000..f86fb9104b54
--- /dev/null
+++ b/drivers/mxc/security/mxc_scc_internals.h
@@ -0,0 +1,499 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MXC_SCC_INTERNALS_H__
+#define __MXC_SCC_INTERNALS_H__
+
+/*!
+ * @file mxc_scc_internals.h
+ *
+ * @brief This is intended to be the file which contains most or all of the code or
+ * changes need to port the driver. It also includes other definitions needed
+ * by the driver.
+ *
+ * This header file should only ever be included by scc_driver.c
+ *
+ * Compile-time flags minimally needed:
+ *
+ * @li Some sort of platform flag.
+ * @li Some start-of-SCC consideration, such as SCC_BASE_ADDR
+ *
+ * Some changes which could be made when porting this driver:
+ * #SCC_SPIN_COUNT
+ *
+ * @ingroup MXCSCC
+ */
+#if 0
+#include <linux/version.h> /* Current version Linux kernel */
+#include <linux/module.h> /* Basic support for loadable modules,
+ printk */
+#include <linux/init.h> /* module_init, module_exit */
+#include <linux/kernel.h> /* General kernel system calls */
+#include <linux/sched.h> /* for interrupt.h */
+#include <linux/spinlock.h>
+#include <linux/interrupt.h> /* IRQ / interrupt definitions */
+#include <linux/io.h> /* ioremap() */
+#endif
+#include <linux/mxc_scc_driver.h>
+
+/* Get handle on certain per-platform symbols */
+#ifdef TAHITI
+#include <asm/arch/mx2.h>
+
+/*
+ * Mark the SCC as always there... as Tahiti is not officially supported by
+ * driver. Porting opportunity.
+ */
+#define SCC_ENABLED() (1)
+
+#elif defined(MXC)
+
+#include <mach/iim.h>
+#include <mach/mxc_scc.h>
+
+#ifdef SCC_FUSE
+
+/*
+ * This macro is used to determine whether the SCC is enabled/available
+ * on the platform. This macro may need to be ported.
+ */
+#define SCC_ENABLED() ((SCC_FUSE & MXC_IIMHWV1_SCC_DISABLE) == 0)
+
+#else
+
+#warning SCC_FUSE not defined, assuming the SCC is enabled.
+#define SCC_ENABLED() (1)
+
+#endif
+
+#else /* neither TAHITI nor MXC */
+
+#error Do not understand target architecture
+
+#endif /* TAHITI */
+
+/* Temporarily define compile-time flags to make Doxygen happy. */
+#ifdef DOXYGEN_HACK
+/*! @addtogroup scccompileflags */
+/*! @{ */
+
+/*! @def NO_SMN_INTERRUPT
+ * The SMN interrupt is not wired to the CPU at all.
+ */
+#define NO_SMN_INTERRUPT
+
+/*!
+ * Register an interrupt handler for the SMN as well as
+ * the SCM. In some implementations, the SMN is not connected at all (see
+ * #NO_SMN_INTERRUPT), and in others, it is on the same interrupt line as the
+ * SCM. When defining this flag, the SMN interrupt should be on a separate
+ * line from the SCM interrupt.
+ */
+
+#define USE_SMN_INTERRUPT
+
+/*!
+ * Turn on generation of run-time operational, debug, and error messages
+ */
+#define SCC_DEBUG
+
+/*!
+ * Turn on generation of run-time logging of access to the SCM and SMN
+ * registers.
+ */
+#define SCC_REGISTER_DEBUG
+
+/*!
+ * Turn on generation of run-time logging of access to the SCM Red and
+ * Black memories. Will only work if #SCC_REGISTER_DEBUG is also defined.
+ */
+#define SCC_RAM_DEBUG
+
+/*!
+ * If the driver finds the SCC in HEALTH_CHECK state, go ahead and
+ * run a quick ASC to bring it to SECURE state.
+ */
+#define SCC_BRINGUP
+
+/*!
+ * Expected to come from platform header files or compile command line.
+ * This symbol must be the address of the SCC
+ */
+#define SCC_BASE
+
+/*!
+ * This must be the interrupt line number of the SCM interrupt.
+ */
+#define INT_SCM
+
+/*!
+ * if #USE_SMN_INTERRUPT is defined, this must be the interrupt line number of
+ * the SMN interrupt.
+ */
+#define INT_SMN
+
+/*!
+ * Define the number of Stored Keys which the SCC driver will make available.
+ * Value shall be from 0 to 20. Default is zero (0).
+ */
+#define SCC_KEY_SLOTS
+
+/*!
+ * Make sure that this flag is defined if compiling for a Little-Endian
+ * platform. Linux Kernel builds provide this flag.
+ */
+#define __LITTLE_ENDIAN
+
+/*!
+ * Make sure that this flag is defined if compiling for a Big-Endian platform.
+ * Linux Kernel builds provide this flag.
+ */
+#define __BIG_ENDIAN
+
+/*!
+ * Read a 32-bit register value from a 'peripheral'. Standard Linux/Unix
+ * macro.
+ *
+ * @param offset Bus address of register to be read
+ *
+ * @return The value of the register
+ */
+#define readl(offset)
+
+/*!
+ * Write a 32-bit value to a register in a 'peripheral'. Standard Linux/Unix
+ * macro.
+ *
+ * @param value The 32-bit value to store
+ * @param offset Bus address of register to be written
+ *
+ * return (none)
+ */
+#define writel(value,offset)
+
+ /*! @} *//* end group scccompileflags */
+
+#endif /* DOXYGEN_HACK */
+
+/*!
+ * Define the number of Stored Keys which the SCC driver will make available.
+ * Value shall be from 0 to 20. Default is zero (0).
+ */
+#define SCC_KEY_SLOTS 20
+
+#ifndef SCC_KEY_SLOTS
+#define SCC_KEY_SLOTS 0
+
+#else
+
+#if (SCC_KEY_SLOTS < 0) || (SCC_KEY_SLOTS > 20)
+#error Bad value for SCC_KEY_SLOTS
+#endif
+
+/*!
+ * Maximum length of key/secret value which can be stored in SCC.
+ */
+#define SCC_MAX_KEY_SIZE 32
+
+/*!
+ * This is the size, in bytes, of each key slot, and therefore the maximum size
+ * of the wrapped key.
+ */
+#define SCC_KEY_SLOT_SIZE 32
+
+/*!
+ * This is the offset into each RAM of the base of the area which is
+ * not used for Stored Keys.
+ */
+#define SCM_NON_RESERVED_OFFSET (SCC_KEY_SLOTS * SCC_KEY_SLOT_SIZE)
+
+#endif
+
+/* These come for free with Linux, but may need to be set in a port. */
+#ifndef __BIG_ENDIAN
+#ifndef __LITTLE_ENDIAN
+#error One of __LITTLE_ENDIAN or __BIG_ENDIAN must be #defined
+#endif
+#else
+#ifdef __LITTLE_ENDIAN
+#error Exactly one of __LITTLE_ENDIAN or __BIG_ENDIAN must be #defined
+#endif
+#endif
+
+#ifndef SCC_CALLBACK_SIZE
+/*! The number of function pointers which can be stored in #scc_callbacks.
+ * Defaults to 4, can be overridden with compile-line argument.
+ */
+#define SCC_CALLBACK_SIZE 4
+#endif
+
+/*! Initial CRC value for CCITT-CRC calculation. */
+#define CRC_CCITT_START 0xFFFF
+
+#ifdef TAHITI
+
+/*!
+ * The SCC_BASE has to be SMN_BASE_ADDR on TAHITI, as the banks of
+ * registers are swapped in place.
+ */
+#define SCC_BASE SMN_BASE_ADDR
+
+/*! The interrupt number for the SCC (SCM only!) on Tahiti */
+#define INT_SCC_SCM 62
+
+/*! Tahiti does not have the SMN interrupt wired to the CPU. */
+#define NO_SMN_INTERRUPT
+
+#endif /* TAHITI */
+
+/*! Number of times to spin between polling of SCC while waiting for cipher
+ * or zeroizing function to complete. See also #SCC_CIPHER_MAX_POLL_COUNT. */
+#define SCC_SPIN_COUNT 1000
+
+/*! Number of times to polling SCC while waiting for cipher
+ * or zeroizing function to complete. See also #SCC_SPIN_COUNT. */
+#define SCC_CIPHER_MAX_POLL_COUNT 100
+
+/*!
+ * @def SCC_READ_REGISTER
+ * Read a 32-bit value from an SCC register. Macro which depends upon
+ * #scc_base. Linux readl()/writel() macros operate on 32-bit quantities, as
+ * do SCC register reads/writes.
+ *
+ * @param offset Register offset within SCC.
+ *
+ * @return The value from the SCC's register.
+ */
+#ifndef SCC_REGISTER_DEBUG
+#define SCC_READ_REGISTER(offset) __raw_readl(scc_base+(offset))
+#else
+#define SCC_READ_REGISTER(offset) dbg_scc_read_register(offset)
+#endif
+
+/*!
+ * Write a 32-bit value to an SCC register. Macro depends upon #scc_base.
+ * Linux readl()/writel() macros operate on 32-bit quantities, as do SCC
+ * register reads/writes.
+ *
+ * @param offset Register offset within SCC.
+ * @param value 32-bit value to store into the register
+ *
+ * @return (void)
+ */
+#ifndef SCC_REGISTER_DEBUG
+#define SCC_WRITE_REGISTER(offset,value) (void)__raw_writel(value, scc_base+(offset))
+#else
+#define SCC_WRITE_REGISTER(offset,value) dbg_scc_write_register(offset, value)
+#endif
+
+/*!
+ * Calculates the byte offset into a word
+ * @param bp The byte (char*) pointer
+ * @return The offset (0, 1, 2, or 3)
+ */
+#define SCC_BYTE_OFFSET(bp) ((uint32_t)(bp) % sizeof(uint32_t))
+
+/*!
+ * Converts (by rounding down) a byte pointer into a word pointer
+ * @param bp The byte (char*) pointer
+ * @return The word (uint32_t) as though it were an aligned (uint32_t*)
+ */
+#define SCC_WORD_PTR(bp) (((uint32_t)(bp)) & ~(sizeof(uint32_t)-1))
+
+/*!
+ * Determine number of bytes in an SCC block
+ *
+ * @return Bytes / block
+ */
+#define SCC_BLOCK_SIZE_BYTES() scc_configuration.block_size_bytes
+
+/*!
+ * Maximum number of additional bytes which may be added in CRC+padding mode.
+ */
+#define PADDING_BUFFER_MAX_BYTES (CRC_SIZE_BYTES + sizeof(scc_block_padding))
+
+/*!
+ * Shorthand (clearer, anyway) for number of bytes in a CRC.
+ */
+#define CRC_SIZE_BYTES (sizeof(crc_t))
+
+/*!
+ * The polynomial used in CCITT-CRC calculation
+ */
+#define CRC_POLYNOMIAL 0x1021
+
+/*!
+ * Calculate CRC on one byte of data
+ *
+ * @param[in,out] running_crc A value of type crc_t where CRC is kept. This
+ * must be an rvalue and an lvalue.
+ * @param[in] byte_value The byte (uint8_t, char) to be put in the CRC
+ *
+ * @return none
+ */
+#define CALC_CRC(byte_value,running_crc) { \
+ uint8_t data; \
+ data = (0xff&(byte_value)) ^ (running_crc >> 8); \
+ running_crc = scc_crc_lookup_table[data] ^ (running_crc << 8); \
+}
+
+/*! Value of 'beginning of padding' marker in driver-provided padding */
+#define SCC_DRIVER_PAD_CHAR 0x80
+
+/*! Name of the driver. Used (on Linux, anyway) when registering interrupts */
+#define SCC_DRIVER_NAME "scc"
+
+/* Port -- these symbols are defined in Linux 2.6 and later. They are defined
+ * here for backwards compatibility because this started life as a 2.4
+ * driver, and as a guide to portation to other platforms.
+ */
+
+#if !defined(LINUX_VERSION_CODE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+
+#define irqreturn_t void /* Return type of an interrupt handler */
+
+#define IRQ_HANDLED /* Would be '1' for handled -- as in return IRQ_HANDLED; */
+
+#define IRQ_NONE /* would be '0' for not handled -- as in return IRQ_NONE; */
+
+#define IRQ_RETVAL(x) /* Return x==0 (not handled) or non-zero (handled) */
+
+#endif /* LINUX earlier than 2.5 */
+
+/* These are nice to have around */
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+/*! Provide a typedef for the CRC which can be used in encrypt/decrypt */
+typedef uint16_t crc_t;
+
+/*! Gives high-level view of state of the SCC */
+enum scc_status {
+ SCC_STATUS_INITIAL, /*!< State of driver before ever checking */
+ SCC_STATUS_CHECKING, /*!< Transient state while driver loading */
+ SCC_STATUS_UNIMPLEMENTED, /*!< SCC is non-existent or unuseable */
+ SCC_STATUS_OK, /*!< SCC is in Secure or Default state */
+ SCC_STATUS_FAILED /*!< In Failed state */
+};
+
+/*!
+ * Information about a key slot.
+ */
+struct scc_key_slot {
+ uint64_t owner_id; /*!< Access control value. */
+ uint32_t length; /*!< Length of value in slot. */
+ uint32_t offset; /*!< Offset of value from start of each RAM. */
+ uint32_t status; /*!< 0 = unassigned, 1 = assigned. */
+};
+
+/* Forward-declare a number routines which are not part of user api */
+static int scc_init(void);
+static void scc_cleanup(void);
+
+/* Forward defines of internal functions */
+OS_DEV_ISR(scc_irq);
+/*! Perform callbacks registered by #scc_monitor_security_failure().
+ *
+ * Make sure callbacks only happen once... Since there may be some reason why
+ * the interrupt isn't generated, this routine could be called from base(task)
+ * level.
+ *
+ * One at a time, go through #scc_callbacks[] and call any non-null pointers.
+ */
+static void scc_perform_callbacks(void);
+static uint32_t copy_to_scc(const uint8_t * from, uint32_t to,
+ unsigned long count_bytes, uint16_t * crc);
+static uint32_t copy_from_scc(const uint32_t from, uint8_t * to,
+ unsigned long count_bytes, uint16_t * crc);
+static scc_return_t scc_strip_padding(uint8_t * from,
+ unsigned *count_bytes_stripped);
+static uint32_t scc_update_state(void);
+static void scc_init_ccitt_crc(void);
+static uint32_t scc_grab_config_values(void);
+static int setup_interrupt_handling(void);
+/*!
+ * Perform an encryption on the input. If @c verify_crc is true, a CRC must be
+ * calculated on the plaintext, and appended, with padding, before computing
+ * the ciphertext.
+ *
+ * @param[in] count_in_bytes Count of bytes of plaintext
+ * @param[in] data_in Pointer to the plaintext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing ciphertext
+ * @param[in] add_crc Flag for computing CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+ */
+static scc_return_t scc_encrypt(uint32_t count_in_bytes,
+ const uint8_t * data_in,
+ uint32_t scm_control, uint8_t * data_out,
+ int add_crc, unsigned long *count_out_bytes);
+
+/*!
+ * Perform a decryption on the input. If @c verify_crc is true, the last block
+ * (maybe the two last blocks) is special - it should contain a CRC and
+ * padding. These must be stripped and verified.
+ *
+ * @param[in] count_in_bytes Count of bytes of ciphertext
+ * @param[in] data_in Pointer to the ciphertext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing plaintext
+ * @param[in] verify_crc Flag for running CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+
+ */
+static scc_return_t scc_decrypt(uint32_t count_in_bytes,
+ const uint8_t * data_in,
+ uint32_t scm_control, uint8_t * data_out,
+ int verify_crc, unsigned long *count_out_bytes);
+
+static void scc_wait_completion(void);
+static int is_cipher_done(void);
+static scc_return_t check_register_accessible(uint32_t offset,
+ uint32_t smn_status,
+ uint32_t scm_status);
+static scc_return_t check_register_offset(uint32_t offset);
+
+#ifdef SCC_REGISTER_DEBUG
+static uint32_t dbg_scc_read_register(uint32_t offset);
+static void dbg_scc_write_register(uint32_t offset, uint32_t value);
+#endif
+
+/* For Linux kernel, export the API functions to other kernel modules */
+EXPORT_SYMBOL(scc_get_configuration);
+EXPORT_SYMBOL(scc_zeroize_memories);
+EXPORT_SYMBOL(scc_crypt);
+EXPORT_SYMBOL(scc_set_sw_alarm);
+EXPORT_SYMBOL(scc_monitor_security_failure);
+EXPORT_SYMBOL(scc_stop_monitoring_security_failure);
+EXPORT_SYMBOL(scc_read_register);
+EXPORT_SYMBOL(scc_write_register);
+EXPORT_SYMBOL(scc_alloc_slot);
+EXPORT_SYMBOL(scc_dealloc_slot);
+EXPORT_SYMBOL(scc_load_slot);
+EXPORT_SYMBOL(scc_encrypt_slot);
+EXPORT_SYMBOL(scc_decrypt_slot);
+EXPORT_SYMBOL(scc_get_slot_info);
+
+/* Tell Linux where to invoke driver at boot/module load time */
+module_init(scc_init);
+/* Tell Linux where to invoke driver on module unload */
+module_exit(scc_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Device Driver for SCC (SMN/SCM)");
+
+#endif /* __MXC_SCC_INTERNALS_H__ */
diff --git a/drivers/mxc/security/rng/Makefile b/drivers/mxc/security/rng/Makefile
new file mode 100644
index 000000000000..aaf6ace8aa5a
--- /dev/null
+++ b/drivers/mxc/security/rng/Makefile
@@ -0,0 +1,35 @@
+# Makefile for the Linux RNG Driver
+#
+# This makefile works within a kernel driver tree
+
+ # Makefile for rng_driver
+
+
+# Possible configurable paramters
+CFG_RNG += -DRNGA_MAX_REQUEST_SIZE=32
+
+#DBG_RNGA = -DRNGA_DEBUG
+#DBG_RNGA += -DRNGA_REGISTER_DEBUG
+#DBG_RNGA += -DRNGA_ENTROPY_DEBUG
+
+EXTRA_CFLAGS = -DLINUX_KERNEL $(CFG_RNG) $(DBG_RNG)
+
+
+ifeq ($(CONFIG_MXC_RNG_TEST_DRIVER),y)
+EXTRA_CFLAGS += -DRNG_REGISTER_PEEK_POKE
+endif
+ifeq ($(CONFIG_RNG_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
+
+EXTRA_CFLAGS += -Idrivers/mxc/security/rng/include -Idrivers/mxc/security/sahara2/include
+
+obj-$(CONFIG_MXC_SECURITY_RNG) += shw.o
+#shw-objs := shw_driver.o shw_memory_mapper.o ../sahara2/fsl_shw_keystore.o
+shw-objs := shw_driver.o shw_memory_mapper.o ../sahara2/fsl_shw_keystore.o \
+ fsl_shw_sym.o fsl_shw_wrap.o shw_dryice.o des_key.o \
+ shw_hash.o shw_hmac.o
+
+obj-$(CONFIG_MXC_SECURITY_RNG) += rng.o
+rng-objs := rng_driver.o
diff --git a/drivers/mxc/security/rng/des_key.c b/drivers/mxc/security/rng/des_key.c
new file mode 100644
index 000000000000..741a800130b6
--- /dev/null
+++ b/drivers/mxc/security/rng/des_key.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+/*!
+ * @file des_key.c
+ *
+ * This file implements the function #fsl_shw_permute1_bytes().
+ *
+ * The code was lifted from crypto++ v5.5.2, which is public domain code. The
+ * code to handle words instead of bytes was extensively modified from the byte
+ * version and then converted to handle one to three keys at once.
+ *
+ */
+
+#include "shw_driver.h"
+#ifdef DIAG_SECURITY_FUNC
+#include "apihelp.h"
+#endif
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <linux/byteorder/little_endian.h> /* or whichever is proper for target arch */
+#endif
+
+#ifdef DEBUG
+#undef DEBUG /* TEMPORARY */
+#endif
+
+#if defined(DEBUG) || defined(SELF_TEST)
+static void DUMP_BYTES(const char *label, const uint8_t * data, int len)
+{
+ int i;
+
+ printf("%s: ", label);
+ for (i = 0; i < len; i++) {
+ printf("%02X", data[i]);
+ if ((i % 8 == 0) && (i != 0)) {
+ printf("_"); /* key separator */
+ }
+ }
+ printf("\n");
+}
+
+static void DUMP_WORDS(const char *label, const uint32_t * data, int len)
+{
+ int i, j;
+
+ printf("%s: ", label);
+ /* Dump the words in reverse order, so that they are intelligible */
+ for (i = len - 1; i >= 0; i--) {
+ for (j = 3; j >= 0; j--) {
+ uint32_t word = data[i];
+ printf("%02X", (word >> ((j * 8)) & 0xff));
+ if ((i != 0) && ((((i) * 4 + 5 + j) % 7) == 5))
+ printf("_"); /* key separator */
+ }
+ printf("|"); /* word separator */
+ }
+ printf("\n");
+}
+#else
+#define DUMP_BYTES(label, data,len)
+#define DUMP_WORDS(label, data,len)
+#endif
+
+/*!
+ * permuted choice table (key)
+ *
+ * Note that this table has had one subtracted from each element so that the
+ * code doesn't have to do it.
+ */
+static const uint8_t pc1[] = {
+ 56, 48, 40, 32, 24, 16, 8,
+ 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26,
+ 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14,
+ 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28,
+ 20, 12, 4, 27, 19, 11, 3,
+};
+
+/*! bit 0 is left-most in byte */
+static const int bytebit[] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+/*!
+ * Convert a 3-key 3DES key into the first-permutation 168-bit version.
+ *
+ * This is the format of the input key:
+ *
+ * @verbatim
+ BIT: |191 128|127 64|63 0|
+ BYTE: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
+ KEY: | 0 | 1 | 2 |
+ @endverbatim
+ *
+ * This is the format of the output key:
+ *
+ * @verbatim
+ BIT: |167 112|111 56|55 0|
+ BYTE: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
+ KEY: | 1 | 2 | 3 |
+ @endverbatim
+ *
+ * @param[in] key bytes of 3DES key
+ * @param[out] permuted_key 21 bytes of permuted key
+ * @param[in] key_count How many DES keys (2 or 3)
+ */
+void fsl_shw_permute1_bytes(const uint8_t * key, uint8_t * permuted_key,
+ int key_count)
+{
+ int i;
+ int j;
+ int l;
+ int m;
+
+ DUMP_BYTES("Input key", key, 8 * key_count);
+
+ /* For each individual sub-key */
+ for (i = 0; i < 3; i++) {
+ DUMP_BYTES("(key)", key, 8);
+ memset(permuted_key, 0, 7);
+ /* For each bit of key */
+ for (j = 0; j < 56; j++) { /* convert pc1 to bits of key */
+ l = pc1[j]; /* integer bit location */
+ m = l & 07; /* find bit */
+ permuted_key[j >> 3] |= (((key[l >> 3] & /* find which key byte l is in */
+ bytebit[m]) /* and which bit of that byte */
+ ? 0x80 : 0) >> (j % 8)); /* and store 1-bit result */
+ }
+ switch (i) {
+ case 0:
+ if (key_count != 1)
+ key += 8; /* move on to second key */
+ break;
+ case 1:
+ if (key_count == 2)
+ key -= 8; /* go back to first key */
+ else if (key_count == 3)
+ key += 8; /* move on to third key */
+ break;
+ default:
+ break;
+ }
+ permuted_key += 7;
+ }
+ DUMP_BYTES("Output key (bytes)", permuted_key - 21, 21);
+}
+
+#ifdef SELF_TEST
+const uint8_t key1_in[] = {
+ /* FE01FE01FE01FE01_01FE01FE01FE01FE_FEFE0101FEFE0101 */
+ 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01,
+ 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE,
+ 0xFE, 0xFE, 0x01, 0x01, 0xFE, 0xFE, 0x01, 0x01
+};
+
+const uint32_t key1_word_in[] = {
+ 0xFE01FE01, 0xFE01FE01,
+ 0x01FE01FE, 0x01FE01FE,
+ 0xFEFE0101, 0xFEFE0101
+};
+
+uint8_t exp_key1_out[] = {
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33
+};
+
+uint32_t exp_word_key1_out[] = {
+ 0x33333333, 0xAA333333, 0xAAAAAAAA, 0x5555AAAA,
+ 0x55555555, 0x00000055,
+};
+
+const uint8_t key2_in[] = {
+ 0xEF, 0x10, 0xBB, 0xA4, 0x23, 0x49, 0x42, 0x58,
+ 0x01, 0x28, 0x01, 0x4A, 0x10, 0xE4, 0x03, 0x59,
+ 0xFE, 0x84, 0x30, 0x29, 0x8E, 0xF1, 0x10, 0x5A
+};
+
+const uint32_t key2_word_in[] = {
+ 0xEF10BBA4, 0x23494258,
+ 0x0128014A, 0x10E40359,
+ 0xFE843029, 0x8EF1105A
+};
+
+uint8_t exp_key2_out[] = {
+ 0x0D, 0xE1, 0x1D, 0x85, 0x50, 0x9A, 0x56, 0x20,
+ 0xA8, 0x22, 0x94, 0x82, 0x08, 0xA0, 0x33, 0xA1,
+ 0x2D, 0xE9, 0x11, 0x39, 0x95
+};
+
+uint32_t exp_word_key2_out[] = {
+ 0xE9113995, 0xA033A12D, 0x22948208, 0x9A5620A8,
+ 0xE11D8550, 0x0000000D
+};
+
+const uint8_t key3_in[] = {
+ 0x3F, 0xE9, 0x49, 0x4B, 0x67, 0x57, 0x07, 0x3C,
+ 0x89, 0x77, 0x73, 0x0C, 0xA0, 0x05, 0x41, 0x69,
+ 0xB3, 0x7C, 0x98, 0xD8, 0xC9, 0x35, 0x57, 0x19
+};
+
+const uint32_t key3_word_in[] = {
+ 0xEF10BBA4, 0x23494258,
+ 0x0128014A, 0x10E40359,
+ 0xFE843029, 0x8EF1105A
+};
+
+uint8_t exp_key3_out[] = {
+ 0x02, 0x3E, 0x93, 0xA7, 0x9F, 0x18, 0xF1, 0x11,
+ 0xC6, 0x96, 0x00, 0x62, 0xA8, 0x96, 0x02, 0x3E,
+ 0x93, 0xA7, 0x9F, 0x18, 0xF1
+};
+
+uint32_t exp_word_key3_out[] = {
+ 0xE9113995, 0xA033A12D, 0x22948208, 0x9A5620A8,
+ 0xE11D8550, 0x0000000D
+};
+
+const uint8_t key4_in[] = {
+ 0x3F, 0xE9, 0x49, 0x4B, 0x67, 0x57, 0x07, 0x3C,
+ 0x89, 0x77, 0x73, 0x0C, 0xA0, 0x05, 0x41, 0x69,
+};
+
+const uint32_t key4_word_in[] = {
+ 0xEF10BBA4, 0x23494258,
+ 0x0128014A, 0x10E40359,
+ 0xFE843029, 0x8EF1105A
+};
+
+const uint8_t key5_in[] = {
+ 0x3F, 0xE9, 0x49, 0x4B, 0x67, 0x57, 0x07, 0x3C,
+ 0x89, 0x77, 0x73, 0x0C, 0xA0, 0x05, 0x41, 0x69,
+ 0x3F, 0xE9, 0x49, 0x4B, 0x67, 0x57, 0x07, 0x3C,
+};
+
+uint8_t exp_key4_out[] = {
+ 0x0D, 0xE1, 0x1D, 0x85, 0x50, 0x9A, 0x56, 0x20,
+ 0xA8, 0x22, 0x94, 0x82, 0x08, 0xA0, 0x33, 0xA1,
+ 0x2D, 0xE9, 0x11, 0x39, 0x95
+};
+
+uint32_t exp_word_key4_out[] = {
+ 0xE9113995, 0xA033A12D, 0x22948208, 0x9A5620A8,
+ 0xE11D8550, 0x0000000D
+};
+
+const uint8_t key6_in[] = {
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
+};
+
+uint8_t exp_key6_out[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+uint32_t exp_word_key6_out[] = {
+ 0x00000000, 0x0000000, 0x0000000, 0x00000000,
+ 0x00000000, 0x0000000
+};
+
+const uint8_t key7_in[] = {
+ /* 01FE01FE01FE01FE_FE01FE01FE01FE01_0101FEFE0101FEFE */
+ /* 0101FEFE0101FEFE_FE01FE01FE01FE01_01FE01FE01FE01FE */
+ 0x01, 0x01, 0xFE, 0xFE, 0x01, 0x01, 0xFE, 0xFE,
+ 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01,
+ 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE,
+};
+
+uint8_t exp_key7_out[] = {
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x55,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+uint32_t exp_word_key7_out[] = {
+ 0xcccccccc, 0x55cccccc, 0x55555555, 0xaaaa5555,
+ 0xaaaaaaaa, 0x000000aa
+};
+
+int run_test(const uint8_t * key_in,
+ const int key_count,
+ const uint32_t * key_word_in,
+ const uint8_t * exp_bytes_key_out,
+ const uint32_t * exp_word_key_out)
+{
+ uint8_t key_out[22];
+ uint32_t word_key_out[6];
+ int failed = 0;
+
+ memset(key_out, 0x42, 22);
+ fsl_shw_permute1_bytes(key_in, key_out, key_count);
+ if (memcmp(key_out, exp_bytes_key_out, 21) != 0) {
+ printf("bytes_to_bytes: ERROR: \n");
+ DUMP_BYTES("key_in", key_in, 8 * key_count);
+ DUMP_BYTES("key_out", key_out, 21);
+ DUMP_BYTES("exp_out", exp_bytes_key_out, 21);
+ failed |= 1;
+ } else if (key_out[21] != 0x42) {
+ printf("bytes_to_bytes: ERROR: Buffer overflow 0x%02x\n",
+ (int)key_out[21]);
+ } else {
+ printf("bytes_to_bytes: OK\n");
+ }
+#if 0
+ memset(word_key_out, 0x42, 21);
+ fsl_shw_permute1_bytes_to_words(key_in, word_key_out, key_count);
+ if (memcmp(word_key_out, exp_word_key_out, 21) != 0) {
+ printf("bytes_to_words: ERROR: \n");
+ DUMP_BYTES("key_in", key_in, 8 * key_count);
+ DUMP_WORDS("key_out", word_key_out, 6);
+ DUMP_WORDS("exp_out", exp_word_key_out, 6);
+ failed |= 1;
+ } else {
+ printf("bytes_to_words: OK\n");
+ }
+
+ if (key_word_in != NULL) {
+ memset(word_key_out, 0x42, 21);
+ fsl_shw_permute1_words_to_words(key_word_in, word_key_out);
+ if (memcmp(word_key_out, exp_word_key_out, 21) != 0) {
+ printf("words_to_words: ERROR: \n");
+ DUMP_BYTES("key_in", key_in, 24);
+ DUMP_WORDS("key_out", word_key_out, 6);
+ DUMP_WORDS("exp_out", exp_word_key_out, 6);
+ failed |= 1;
+ } else {
+ printf("words_to_words: OK\n");
+ }
+ }
+#endif
+
+ return failed;
+} /* end fn run_test */
+
+int main()
+{
+ int failed = 0;
+
+ printf("key1\n");
+ failed |=
+ run_test(key1_in, 3, key1_word_in, exp_key1_out, exp_word_key1_out);
+ printf("\nkey2\n");
+ failed |=
+ run_test(key2_in, 3, key2_word_in, exp_key2_out, exp_word_key2_out);
+ printf("\nkey3\n");
+ failed |= run_test(key3_in, 3, NULL, exp_key3_out, exp_word_key3_out);
+ printf("\nkey4\n");
+ failed |= run_test(key4_in, 2, NULL, exp_key4_out, exp_word_key4_out);
+ printf("\nkey5\n");
+ failed |= run_test(key5_in, 3, NULL, exp_key4_out, exp_word_key4_out);
+ printf("\nkey6 - 3\n");
+ failed |= run_test(key6_in, 3, NULL, exp_key6_out, exp_word_key6_out);
+ printf("\nkey6 - 2\n");
+ failed |= run_test(key6_in, 2, NULL, exp_key6_out, exp_word_key6_out);
+ printf("\nkey6 - 1\n");
+ failed |= run_test(key6_in, 1, NULL, exp_key6_out, exp_word_key6_out);
+ printf("\nkey7\n");
+ failed |= run_test(key7_in, 3, NULL, exp_key7_out, exp_word_key7_out);
+ printf("\n");
+
+ if (failed != 0) {
+ printf("TEST FAILED\n");
+ }
+ return failed;
+}
+
+#endif /* SELF_TEST */
diff --git a/drivers/mxc/security/rng/fsl_shw_hash.c b/drivers/mxc/security/rng/fsl_shw_hash.c
new file mode 100644
index 000000000000..60572862a0fa
--- /dev/null
+++ b/drivers/mxc/security/rng/fsl_shw_hash.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+/*!
+ * @file fsl_shw_hash.c
+ *
+ * This file implements Cryptographic Hashing functions of the FSL SHW API
+ * for Sahara. This does not include HMAC.
+ */
+
+#include "shw_driver.h"
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-005 */
+/*!
+ * Hash a stream of data with a cryptographic hash algorithm.
+ *
+ * The flags in the @a hash_ctx control the operation of this function.
+ *
+ * Hashing functions work on 64 octets of message at a time. Therefore, when
+ * any partial hashing of a long message is performed, the message @a length of
+ * each segment must be a multiple of 64. When ready to
+ * #FSL_HASH_FLAGS_FINALIZE the hash, the @a length may be any value.
+ *
+ * With the #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_FINALIZE flags on, a
+ * one-shot complete hash, including padding, will be performed. The @a length
+ * may be any value.
+ *
+ * The first octets of a data stream can be hashed by setting the
+ * #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_SAVE flags. The @a length must be
+ * a multiple of 64.
+ *
+ * The flag #FSL_HASH_FLAGS_LOAD is used to load a context previously saved by
+ * #FSL_HASH_FLAGS_SAVE. The two in combination will allow a (multiple-of-64
+ * octets) 'middle sequence' of the data stream to be hashed with the
+ * beginning. The @a length must again be a multiple of 64.
+ *
+ * Since the flag #FSL_HASH_FLAGS_LOAD is used to load a context previously
+ * saved by #FSL_HASH_FLAGS_SAVE, the #FSL_HASH_FLAGS_LOAD and
+ * #FSL_HASH_FLAGS_FINALIZE flags, used together, can be used to finish the
+ * stream. The @a length may be any value.
+ *
+ * If the user program wants to do the padding for the hash, it can leave off
+ * the #FSL_HASH_FLAGS_FINALIZE flag. The @a length must then be a multiple of
+ * 64 octets.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] hash_ctx Hashing algorithm and state of the cipher.
+ * @param msg Pointer to the data to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result If not null, pointer to where to store the hash
+ * digest.
+ * @param result_len Number of octets to store in @a result.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
+ fsl_shw_hco_t * hash_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)hash_ctx;
+ (void)msg;
+ (void)length;
+ (void)result;
+ (void)result_len;
+
+ return ret;
+}
diff --git a/drivers/mxc/security/rng/fsl_shw_hmac.c b/drivers/mxc/security/rng/fsl_shw_hmac.c
new file mode 100644
index 000000000000..4d2a104afcd8
--- /dev/null
+++ b/drivers/mxc/security/rng/fsl_shw_hmac.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+/*!
+ * @file fsl_shw_hmac.c
+ *
+ * This file implements Hashed Message Authentication Code functions of the FSL
+ * SHW API.
+ */
+
+#include "shw_driver.h"
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-001 */
+/*!
+ * Get the precompute information
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param hmac_ctx
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hmac_precompute(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx)
+{
+ fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)hmac_ctx;
+
+ return status;
+}
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-002 */
+/*!
+ * Get the hmac
+ *
+ *
+ * @param user_ctx Info for acquiring memory
+ * @param key_info
+ * @param hmac_ctx
+ * @param msg
+ * @param length
+ * @param result
+ * @param result_len
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hmac(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)hmac_ctx;
+ (void)msg;
+ (void)length;
+ (void)result;
+ (void)result_len;
+
+ return status;
+}
diff --git a/drivers/mxc/security/rng/fsl_shw_rand.c b/drivers/mxc/security/rng/fsl_shw_rand.c
new file mode 100644
index 000000000000..aa4d426c70fe
--- /dev/null
+++ b/drivers/mxc/security/rng/fsl_shw_rand.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+/*!
+ * @file fsl_shw_rand.c
+ *
+ * This file implements Random Number Generation functions of the FSL SHW API
+ * in USER MODE for talking to a standalone RNGA/RNGC device driver.
+ *
+ * It contains the fsl_shw_get_random() and fsl_shw_add_entropy() functions.
+ *
+ * These routines will build a request block and pass it to the SHW driver.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#ifdef FSL_DEBUG
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#endif /* FSL_DEBUG */
+
+#include "shw_driver.h"
+
+extern fsl_shw_return_t validate_uco(fsl_shw_uco_t * uco);
+
+#if defined(FSL_HAVE_RNGA) || defined(FSL_HAVE_RNGB) || defined(FSL_HAVE_RNGC)
+
+/* REQ-S2LRD-PINTFC-API-BASIC-RNG-002 */
+fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* perform a sanity check / update uco */
+ ret = validate_uco(user_ctx);
+ if (ret == FSL_RETURN_OK_S) {
+ struct get_random_req *req = malloc(sizeof(*req));
+
+ if (req == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+
+ init_req(&req->hdr, user_ctx);
+ req->size = length;
+ req->random = data;
+
+ ret =
+ send_req(SHW_USER_REQ_GET_RANDOM, &req->hdr,
+ user_ctx);
+ }
+ }
+
+ return ret;
+}
+
+fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* perform a sanity check on the uco */
+ ret = validate_uco(user_ctx);
+ if (ret == FSL_RETURN_OK_S) {
+ struct add_entropy_req *req = malloc(sizeof(*req));
+
+ if (req == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ init_req(&req->hdr, user_ctx);
+ req->size = length;
+ req->entropy = data;
+
+ ret =
+ send_req(SHW_USER_REQ_ADD_ENTROPY, &req->hdr,
+ user_ctx);
+ }
+ }
+
+ return ret;
+}
+
+#else /* no H/W RNG block */
+
+fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+
+ (void)user_ctx;
+ (void)length;
+ (void)data;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+
+ (void)user_ctx;
+ (void)length;
+ (void)data;
+
+ return FSL_RETURN_ERROR_S;
+}
+#endif
diff --git a/drivers/mxc/security/rng/fsl_shw_sym.c b/drivers/mxc/security/rng/fsl_shw_sym.c
new file mode 100644
index 000000000000..e844e3ae7420
--- /dev/null
+++ b/drivers/mxc/security/rng/fsl_shw_sym.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+/*!
+ * @file fsl_shw_sym.c
+ *
+ * This file implements the Symmetric Cipher functions of the FSL SHW API. Its
+ * features are limited to what can be done with the combination of SCC and
+ * DryIce.
+ */
+#include "fsl_platform.h"
+#include "shw_driver.h"
+
+#if defined(__KERNEL__) && defined(FSL_HAVE_DRYICE)
+
+#include "../dryice.h"
+#include <linux/mxc_scc_driver.h>
+#ifdef DIAG_SECURITY_FUNC
+#include "apihelp.h"
+#endif
+
+#include <diagnostic.h>
+
+#define SYM_DECRYPT 0
+#define SYM_ENCRYPT 1
+
+extern fsl_shw_return_t shw_convert_pf_key(fsl_shw_pf_key_t shw_pf_key,
+ di_key_t * di_keyp);
+
+/*! 'Initial' IV for presence of FSL_SYM_CTX_LOAD flag */
+static uint8_t zeros[8] = {
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*!
+ * Common function for encryption and decryption
+ *
+ * This is for a device with DryIce.
+ *
+ * A key must either refer to a 'pure' HW key, or, if PRG or PRG_IIM,
+ * established, then that key will be programmed. Then, the HW_key in the
+ * object will be selected. After this setup, the ciphering will be performed
+ * by calling the SCC driver..
+ *
+ * The function 'releases' the reservations before it completes.
+ */
+fsl_shw_return_t do_symmetric(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ int encrypt,
+ uint32_t length,
+ const uint8_t * in, uint8_t * out)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+ int key_selected = 0;
+ uint8_t *iv = NULL;
+ unsigned long count_out = length;
+ di_key_t di_key = DI_KEY_PK; /* default for user key */
+ di_key_t di_key_orig; /* currently selected key */
+ di_key_t selected_key = -1;
+ di_return_t di_code;
+ scc_return_t scc_code;
+
+ /* For now, only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* No software keys allowed */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ }
+
+ /* The only algorithm the SCC supports */
+ if (key_info->algorithm != FSL_KEY_ALG_TDES) {
+ ret = FSL_RETURN_BAD_ALGORITHM_S;
+ goto out;
+ }
+
+ /* Validate key length */
+ if ((key_info->key_length != 16)
+ && (key_info->key_length != 21)
+ && (key_info->key_length != 24)) {
+ ret = FSL_RETURN_BAD_KEY_LENGTH_S;
+ goto out;
+ }
+
+ /* Validate data is multiple of DES/TDES block */
+ if ((length & 7) != 0) {
+ ret = FSL_RETURN_BAD_DATA_LENGTH_S;
+ goto out;
+ }
+
+ /* Do some setup according to where the key lives */
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+ if ((key_info->pf_key != FSL_SHW_PF_KEY_PRG)
+ && (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG)) {
+ ret = FSL_RETURN_ERROR_S;
+ }
+ } else if (key_info->flags & FSL_SKO_KEY_PRESENT) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ } else if (key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY) {
+ /*
+ * No key present or established, just refer to HW
+ * as programmed.
+ */
+ } else {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* Now make proper selection */
+ ret = shw_convert_pf_key(key_info->pf_key, &di_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Determine the current DI key selection */
+ di_code = dryice_check_key(&di_key_orig);
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not save current DI key state: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ /* If the requested DI key is already selected, don't re-select it. */
+ if (di_key != di_key_orig) {
+ di_code = dryice_select_key(di_key, 0);
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Error from select_key: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+ goto out;
+ }
+ }
+ key_selected = 1;
+
+ /* Verify that we are using the key we want */
+ di_code = dryice_check_key(&selected_key);
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Error from check_key: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+ goto out;
+ }
+
+ if (di_key != selected_key) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Wrong key in use: %d instead of %d\n\n",
+ selected_key, di_key);
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ if (sym_ctx->mode == FSL_SYM_MODE_CBC) {
+ if ((sym_ctx->flags & FSL_SYM_CTX_LOAD)
+ && !(sym_ctx->flags & FSL_SYM_CTX_INIT)) {
+ iv = sym_ctx->context;
+ } else if ((sym_ctx->flags & FSL_SYM_CTX_INIT)
+ && !(sym_ctx->flags & FSL_SYM_CTX_LOAD)) {
+ iv = zeros;
+ } else {
+ /* Exactly one must be set! */
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+ }
+
+ /* Now run the data through the SCC */
+ scc_code = scc_crypt(length, in, iv,
+ encrypt ? SCC_ENCRYPT : SCC_DECRYPT,
+ (sym_ctx->mode == FSL_SYM_MODE_ECB)
+ ? SCC_ECB_MODE : SCC_CBC_MODE,
+ SCC_VERIFY_MODE_NONE, out, &count_out);
+ if (scc_code != SCC_RET_OK) {
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("scc_code from scc_crypt() is %d\n", scc_code);
+#endif
+ goto out;
+ }
+
+ if ((sym_ctx->mode == FSL_SYM_MODE_CBC)
+ && (sym_ctx->flags & FSL_SYM_CTX_SAVE)) {
+ /* Save the context for the caller */
+ if (encrypt) {
+ /* Last ciphertext block ... */
+ memcpy(sym_ctx->context, out + length - 8, 8);
+ } else {
+ /* Last ciphertext block ... */
+ memcpy(sym_ctx->context, in + length - 8, 8);
+ }
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ if (key_selected) {
+ (void)dryice_release_key_selection();
+ }
+
+ return ret;
+}
+
+EXPORT_SYMBOL(fsl_shw_symmetric_encrypt);
+/*!
+ * Compute symmetric encryption
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param sym_ctx
+ * @param length
+ * @param pt
+ * @param ct
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt, uint8_t * ct)
+{
+ fsl_shw_return_t ret;
+
+ ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_ENCRYPT,
+ length, pt, ct);
+
+ return ret;
+}
+
+EXPORT_SYMBOL(fsl_shw_symmetric_decrypt);
+/*!
+ * Compute symmetric decryption
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param sym_ctx
+ * @param length
+ * @param pt
+ * @param ct
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct, uint8_t * pt)
+{
+ fsl_shw_return_t ret;
+
+ ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_DECRYPT,
+ length, ct, pt);
+
+ return ret;
+}
+
+#else /* __KERNEL__ && DRYICE */
+
+fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt, uint8_t * ct)
+{
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)sym_ctx;
+ (void)length;
+ (void)pt;
+ (void)ct;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct, uint8_t * pt)
+{
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)sym_ctx;
+ (void)length;
+ (void)ct;
+ (void)pt;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#endif /* __KERNEL__ and DRYICE */
diff --git a/drivers/mxc/security/rng/fsl_shw_wrap.c b/drivers/mxc/security/rng/fsl_shw_wrap.c
new file mode 100644
index 000000000000..90e241e5af86
--- /dev/null
+++ b/drivers/mxc/security/rng/fsl_shw_wrap.c
@@ -0,0 +1,1301 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_shw_wrap.c
+ *
+ * This file implements Key-Wrap (Black Key) and Key Establishment functions of
+ * the FSL SHW API for the SHW (non-SAHARA) driver.
+ *
+ * This is the Black Key information:
+ *
+ * <ul>
+ * <li> Ownerid is an 8-byte, user-supplied, value to keep KEY
+ * confidential.</li>
+ * <li> KEY is a 1-32 byte value which starts in SCC RED RAM before
+ * wrapping, and ends up there on unwrap. Length is limited because of
+ * size of SCC1 RAM.</li>
+ * <li> KEY' is the encrypted KEY</li>
+ * <li> LEN is a 1-byte (for now) byte-length of KEY</li>
+ * <li> ALG is a 1-byte value for the algorithm which which the key is
+ * associated. Values are defined by the FSL SHW API</li>
+ * <li> FLAGS is a 1-byte value contain information like "this key is for
+ * software" (TBD)</li>
+ * <li> Ownerid, LEN, and ALG come from the user's "key_info" object, as does
+ * the slot number where KEY already is/will be.</li>
+ * <li> T is a Nonce</li>
+ * <li> T' is the encrypted T</li>
+ * <li> KEK is a Key-Encryption Key for the user's Key</li>
+ * <li> ICV is the "Integrity Check Value" for the wrapped key</li>
+ * <li> Black Key is the string of bytes returned as the wrapped key</li>
+ * <li> Wrap Key is the user's choice for encrypting the nonce. One of
+ * the Fused Key, the Random Key, or the XOR of the two.
+ * </ul>
+<table border="0">
+<tr><TD align="right">BLACK_KEY <TD width="3">=<TD>ICV | T' | LEN | ALG |
+ FLAGS | KEY'</td></tr>
+<tr><td>&nbsp;</td></tr>
+
+<tr><th>To Wrap</th></tr>
+<tr><TD align="right">T</td> <TD width="3">=</td> <TD>RND()<sub>16</sub>
+ </td></tr>
+<tr><TD align="right">KEK</td><TD width="3">=</td><TD>HASH<sub>sha256</sub>(T |
+ Ownerid)<sub>16</sub></td></tr>
+<tr><TD align="right">KEY'<TD width="3">=</td><TD>
+ TDES<sub>cbc-enc</sub>(Key=KEK, Data=KEY, IV=Ownerid)</td></tr>
+<tr><TD align="right">ICV</td><TD width="3">=</td><td>HMAC<sub>sha256</sub>
+ (Key=T, Data=Ownerid | LEN | ALG | FLAGS | KEY')<sub>16</sub></td></tr>
+<tr><TD align="right">T'</td><TD width="3">=</td><TD>TDES<sub>ecb-enc</sub>
+ (Key=Wrap_Key, IV=Ownerid, Data=T)</td></tr>
+
+<tr><td>&nbsp;</td></tr>
+
+<tr><th>To Unwrap</th></tr>
+<tr><TD align="right">T</td><TD width="3">=</td><TD>TDES<sub>ecb-dec</sub>
+ (Key=Wrap_Key, IV=Ownerid, Data=T')</td></tr>
+<tr><TD align="right">ICV</td><TD width="3">=</td><td>HMAC<sub>sha256</sub>
+ (Key=T, Data=Ownerid | LEN | ALG | FLAGS | KEY')<sub>16</sub></td></tr>
+<tr><TD align="right">KEK</td><TD width="3">=</td><td>HASH<sub>sha256</sub>
+ (T | Ownerid)<sub>16</sub></td></tr>
+<tr><TD align="right">KEY<TD width="3">=</td><TD>TDES<sub>cbc-dec</sub>
+ (Key=KEK, Data=KEY', IV=Ownerid)</td></tr>
+</table>
+
+ * This code supports two types of keys: Software Keys and keys destined for
+ * (or residing in) the DryIce Programmed Key Register.
+ *
+ * Software Keys go to / from the keystore.
+ *
+ * PK keys go to / from the DryIce Programmed Key Register.
+ *
+ * This code only works on a platform with DryIce. "software" keys go into
+ * the keystore. "Program" keys go to the DryIce Programmed Key Register.
+ * As far as this code is concerned, the size of that register is 21 bytes,
+ * the size of a 3DES key with parity stripped.
+ *
+ * The maximum key size supported for wrapped/unwrapped keys depends upon
+ * LENGTH_LENGTH. Currently, it is one byte, so the maximum key size is
+ * 255 bytes. However, key objects cannot currently hold a key of this
+ * length, so a smaller key size is the max.
+ */
+
+#include "fsl_platform.h"
+
+/* This code only works in kernel mode */
+
+#include "shw_driver.h"
+#ifdef DIAG_SECURITY_FUNC
+#include "apihelp.h"
+#endif
+
+#if defined(__KERNEL__) && defined(FSL_HAVE_DRYICE)
+
+#include "../dryice.h"
+#include <linux/mxc_scc_driver.h>
+
+#include "portable_os.h"
+#include "fsl_shw_keystore.h"
+
+#include <diagnostic.h>
+
+#include "shw_hmac.h"
+#include "shw_hash.h"
+
+#define ICV_LENGTH 16
+#define T_LENGTH 16
+#define KEK_LENGTH 21
+#define LENGTH_LENGTH 1
+#define ALGORITHM_LENGTH 1
+#define FLAGS_LENGTH 1
+
+/* ICV | T' | LEN | ALG | FLAGS | KEY' */
+#define ICV_OFFSET 0
+#define T_PRIME_OFFSET (ICV_OFFSET + ICV_LENGTH)
+#define LENGTH_OFFSET (T_PRIME_OFFSET + T_LENGTH)
+#define ALGORITHM_OFFSET (LENGTH_OFFSET + LENGTH_LENGTH)
+#define FLAGS_OFFSET (ALGORITHM_OFFSET + ALGORITHM_LENGTH)
+#define KEY_PRIME_OFFSET (FLAGS_OFFSET + FLAGS_LENGTH)
+
+#define FLAGS_SW_KEY 0x01
+
+#define LENGTH_PATCH 8
+#define LENGTH_PATCH_MASK (LENGTH_PATCH - 1)
+
+/*! rounded up from 168 bits to the next word size */
+#define HW_KEY_LEN_WORDS_BITS 192
+
+/*!
+ * Round a key length up to the TDES block size
+ *
+ * @param len Length of key, in bytes
+ *
+ * @return Length rounded up, if necessary
+ */
+#define ROUND_LENGTH(len) \
+({ \
+ uint32_t orig_len = len; \
+ uint32_t new_len; \
+ \
+ if ((orig_len & LENGTH_PATCH_MASK) != 0) { \
+ new_len = (orig_len + LENGTH_PATCH \
+ - (orig_len & LENGTH_PATCH_MASK)); \
+ } \
+ else { \
+ new_len = orig_len; \
+ } \
+ \
+ new_len; \
+})
+
+/* This is the system keystore object */
+extern fsl_shw_kso_t system_keystore;
+
+#ifdef DIAG_SECURITY_FUNC
+static void dump(const char *name, const uint8_t * data, unsigned int len)
+{
+ os_printk("%s: ", name);
+ while (len > 0) {
+ os_printk("%02x ", (unsigned)*data++);
+ len--;
+ }
+ os_printk("\n");
+}
+#endif
+
+/*
+ * For testing of the algorithm implementation,, the DO_REPEATABLE_WRAP flag
+ * causes the T_block to go into the T field during a wrap operation. This
+ * will make the black key value repeatable (for a given SCC secret key, or
+ * always if the default key is in use).
+ *
+ * Normally, a random sequence is used.
+ */
+#ifdef DO_REPEATABLE_WRAP
+/*!
+ * Block of zeroes which is maximum Symmetric block size, used for
+ * initializing context register, etc.
+ */
+static uint8_t T_block[16] = {
+ 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42,
+ 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42
+};
+#endif
+
+EXPORT_SYMBOL(fsl_shw_establish_key);
+EXPORT_SYMBOL(fsl_shw_read_key);
+EXPORT_SYMBOL(fsl_shw_extract_key);
+EXPORT_SYMBOL(fsl_shw_release_key);
+
+extern fsl_shw_return_t alloc_slot(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info);
+
+extern fsl_shw_return_t load_slot(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ const uint8_t * key);
+
+extern fsl_shw_return_t dealloc_slot(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info);
+
+/*!
+ * Initalialize SKO and SCCO used for T <==> T' cipher operation
+ *
+ * @param wrap_key Which wrapping key user wants
+ * @param key_info Key object for selecting wrap key
+ * @param wrap_ctx Sym Context object for doing the cipher op
+ */
+static inline void init_wrap_key(fsl_shw_pf_key_t wrap_key,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * wrap_ctx)
+{
+ fsl_shw_sko_init_pf_key(key_info, FSL_KEY_ALG_TDES, wrap_key);
+ fsl_shw_scco_init(wrap_ctx, FSL_KEY_ALG_TDES, FSL_SYM_MODE_ECB);
+}
+
+/*!
+ * Insert descriptors to calculate ICV = HMAC(key=T, data=LEN|ALG|KEY')
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param T Location of nonce (length is T_LENGTH bytes)
+ * @param userid Location of userid/ownerid
+ * @param userid_len Length, in bytes of @c userid
+ * @param black_key Beginning of Black Key region
+ * @param key_length Number of bytes of key' there are in @c black_key
+ * @param[out] hmac Location to store ICV. Will be tagged "USES" so
+ * sf routines will not try to free it.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t calc_icv(const uint8_t * T,
+ const uint8_t * userid,
+ unsigned int userid_len,
+ const uint8_t * black_key,
+ uint32_t key_length, uint8_t * hmac)
+{
+ fsl_shw_return_t code;
+ shw_hmac_state_t hmac_state;
+
+ /* Load up T as key for the HMAC */
+ code = shw_hmac_init(&hmac_state, T, T_LENGTH);
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Previous step loaded key; Now set up to hash the data */
+
+ /* Input - start with ownerid */
+ code = shw_hmac_update(&hmac_state, userid, userid_len);
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Still input - Append black-key fields len, alg, key' */
+ code = shw_hmac_update(&hmac_state,
+ (void *)black_key + LENGTH_OFFSET,
+ (LENGTH_LENGTH
+ + ALGORITHM_LENGTH
+ + FLAGS_LENGTH + key_length));
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Output - computed ICV/HMAC */
+ code = shw_hmac_final(&hmac_state, hmac, ICV_LENGTH);
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ out:
+
+ return code;
+} /* calc_icv */
+
+/*!
+ * Compute and return the KEK (Key Encryption Key) from the inputs
+ *
+ * @param userid The user's 'secret' for the key
+ * @param userid_len Length, in bytes of @c userid
+ * @param T The nonce
+ * @param[out] kek Location to store the computed KEK. It will
+ * be 21 bytes long.
+ *
+ * @return the usual error code
+ */
+static fsl_shw_return_t calc_kek(const uint8_t * userid,
+ unsigned int userid_len,
+ const uint8_t * T, uint8_t * kek)
+{
+ fsl_shw_return_t code = FSL_RETURN_INTERNAL_ERROR_S;
+ shw_hash_state_t hash_state;
+
+ code = shw_hash_init(&hash_state, FSL_HASH_ALG_SHA256);
+ if (code != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Hash init failed: %s\n", fsl_error_string(code));
+#endif
+ goto out;
+ }
+
+ code = shw_hash_update(&hash_state, T, T_LENGTH);
+ if (code != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Hash for T failed: %s\n",
+ fsl_error_string(code));
+#endif
+ goto out;
+ }
+
+ code = shw_hash_update(&hash_state, userid, userid_len);
+ if (code != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Hash for userid failed: %s\n",
+ fsl_error_string(code));
+#endif
+ goto out;
+ }
+
+ code = shw_hash_final(&hash_state, kek, KEK_LENGTH);
+ if (code != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not extract kek: %s\n",
+ fsl_error_string(code));
+#endif
+ goto out;
+ }
+
+#if KEK_LENGTH != 21
+ {
+ uint8_t permuted_kek[21];
+
+ fsl_shw_permute1_bytes(kek, permuted_kek, KEK_LENGTH / 8);
+ memcpy(kek, permuted_kek, 21);
+ memset(permuted_kek, 0, sizeof(permuted_kek));
+ }
+#endif
+
+#ifdef DIAG_SECURITY_FUNC
+ dump("kek", kek, 21);
+#endif
+
+ out:
+
+ return code;
+} /* end fn calc_kek */
+
+/*!
+ * Validate user's wrap key selection
+ *
+ * @param wrap_key The user's desired wrapping key
+ */
+static fsl_shw_return_t check_wrap_key(fsl_shw_pf_key_t wrap_key)
+{
+ /* unable to use desired key */
+ fsl_shw_return_t ret = FSL_RETURN_NO_RESOURCE_S;
+
+ if ((wrap_key != FSL_SHW_PF_KEY_IIM) &&
+ (wrap_key != FSL_SHW_PF_KEY_RND) &&
+ (wrap_key != FSL_SHW_PF_KEY_IIM_RND)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Invalid wrap_key in key wrap/unwrap attempt");
+#endif
+ goto out;
+ }
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ return ret;
+} /* end fn check_wrap_key */
+
+/*!
+ * Perform unwrapping of a black key into a RED slot
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be unwrapped... key length, slot info, etc.
+ * @param black_key Encrypted key
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t unwrap(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ const uint8_t * black_key)
+{
+ fsl_shw_return_t ret;
+ uint8_t hmac[ICV_LENGTH];
+ uint8_t T[T_LENGTH];
+ uint8_t kek[KEK_LENGTH + 20];
+ int key_length = black_key[LENGTH_OFFSET];
+ int rounded_key_length = ROUND_LENGTH(key_length);
+ uint8_t key[rounded_key_length];
+ fsl_shw_sko_t t_key_info;
+ fsl_shw_scco_t t_key_ctx;
+ fsl_shw_sko_t kek_key_info;
+ fsl_shw_scco_t kek_ctx;
+ int unwrapping_sw_key = key_info->flags & FSL_SKO_KEY_SW_KEY;
+ int pk_needs_restoration = 0; /* bool */
+ unsigned original_key_length = key_info->key_length;
+ int pk_was_held = 0;
+ uint8_t current_pk[21];
+ di_return_t di_code;
+
+ ret = check_wrap_key(user_ctx->wrap_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ if (black_key == NULL) {
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ dump("black", black_key, KEY_PRIME_OFFSET + key_length);
+#endif
+ /* Validate SW flags to prevent misuse */
+ if ((key_info->flags & FSL_SKO_KEY_SW_KEY)
+ && !(black_key[FLAGS_OFFSET] & FLAGS_SW_KEY)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* Compute T = 3des-dec-ecb(wrap_key, T') */
+ init_wrap_key(user_ctx->wrap_key, &t_key_info, &t_key_ctx);
+ ret = fsl_shw_symmetric_decrypt(user_ctx, &t_key_info, &t_key_ctx,
+ T_LENGTH,
+ black_key + T_PRIME_OFFSET, T);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Recovery of nonce (T) failed");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+
+ /* Compute ICV = HMAC(T, ownerid | len | alg | flags | key' */
+ ret = calc_icv(T, (uint8_t *) & key_info->userid,
+ sizeof(key_info->userid),
+ black_key, original_key_length, hmac);
+
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Calculation of ICV failed");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Validating MAC of wrapped key");
+#endif
+
+ /* Check computed ICV against value in Black Key */
+ if (memcmp(black_key + ICV_OFFSET, hmac, ICV_LENGTH) != 0) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Computed ICV fails validation\n");
+#endif
+ ret = FSL_RETURN_AUTH_FAILED_S;
+ goto out;
+ }
+
+ /* Compute KEK = SHA256(T | ownerid). */
+ ret = calc_kek((uint8_t *) & key_info->userid, sizeof(key_info->userid),
+ T, kek);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ if (unwrapping_sw_key) {
+ di_code = dryice_get_programmed_key(current_pk, 8 * 21);
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not save current PK: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ }
+
+ /*
+ * "Establish" the KEK in the PK. If the PK was held and unwrapping a
+ * software key, then release it and try again, but remember that we need
+ * to leave it 'held' if we are unwrapping a software key.
+ *
+ * If the PK is held while we are unwrapping a key for the PK, then
+ * the user didn't call release, so gets an error.
+ */
+ di_code = dryice_set_programmed_key(kek, 8 * 21, 0);
+ if ((di_code == DI_ERR_INUSE) && unwrapping_sw_key) {
+ /* Temporarily reprogram the PK out from under the user */
+ pk_was_held = 1;
+ dryice_release_programmed_key();
+ di_code = dryice_set_programmed_key(kek, 8 * 21, 0);
+ }
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not program KEK: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ if (unwrapping_sw_key) {
+ pk_needs_restoration = 1;
+ }
+ dryice_release_programmed_key(); /* Because of previous 'set' */
+
+ /* Compute KEY = TDES-decrypt(KEK, KEY') */
+ fsl_shw_sko_init_pf_key(&kek_key_info, FSL_KEY_ALG_TDES,
+ FSL_SHW_PF_KEY_PRG);
+ fsl_shw_sko_set_key_length(&kek_key_info, KEK_LENGTH);
+
+ fsl_shw_scco_init(&kek_ctx, FSL_KEY_ALG_TDES, FSL_SYM_MODE_CBC);
+ fsl_shw_scco_set_flags(&kek_ctx, FSL_SYM_CTX_LOAD);
+ fsl_shw_scco_set_context(&kek_ctx, (uint8_t *) & key_info->userid);
+#ifdef DIAG_SECURITY_FUNC
+ dump("KEY'", black_key + KEY_PRIME_OFFSET, rounded_key_length);
+#endif
+ ret = fsl_shw_symmetric_decrypt(user_ctx, &kek_key_info, &kek_ctx,
+ rounded_key_length,
+ black_key + KEY_PRIME_OFFSET, key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ dump("KEY", key, original_key_length);
+#endif
+ /* Now either put key into PK or into a slot */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ ret = load_slot(user_ctx, key_info, key);
+ } else {
+ /*
+ * Since we have just unwrapped a program key, it had
+ * to have been wrapped as a program key, so it must
+ * be 168 bytes long and permuted ...
+ */
+ ret = dryice_set_programmed_key(key, 8 * key_length, 0);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ }
+
+ out:
+ key_info->key_length = original_key_length;
+
+ if (pk_needs_restoration) {
+ di_code = dryice_set_programmed_key(current_pk, 8 * 21, 0);
+ }
+
+ if (!pk_was_held) {
+ dryice_release_programmed_key();
+ }
+
+ /* Erase tracks of confidential data */
+ memset(T, 0, T_LENGTH);
+ memset(key, 0, rounded_key_length);
+ memset(current_pk, 0, sizeof(current_pk));
+ memset(&t_key_info, 0, sizeof(t_key_info));
+ memset(&t_key_ctx, 0, sizeof(t_key_ctx));
+ memset(&kek_key_info, 0, sizeof(kek_key_info));
+ memset(&kek_ctx, 0, sizeof(kek_ctx));
+ memset(kek, 0, KEK_LENGTH);
+
+ return ret;
+} /* unwrap */
+
+/*!
+ * Perform wrapping of a black key from a RED slot (or the PK register)
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be wrapped... key length, slot info, etc.
+ * @param black_key Place to store encrypted key
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t wrap(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, uint8_t * black_key)
+{
+ fsl_shw_return_t ret = FSL_RETURN_OK_S;
+ fsl_shw_sko_t t_key_info; /* for holding T */
+ fsl_shw_scco_t t_key_ctx;
+ fsl_shw_sko_t kek_key_info;
+ fsl_shw_scco_t kek_ctx;
+ unsigned original_key_length = key_info->key_length;
+ unsigned rounded_key_length;
+ uint8_t T[T_LENGTH];
+ uint8_t kek[KEK_LENGTH + 20];
+ uint8_t *red_key = 0;
+ int red_key_malloced = 0; /* bool */
+ int pk_was_held = 0; /* bool */
+ uint8_t saved_pk[21];
+ uint8_t pk_needs_restoration; /* bool */
+ di_return_t di_code;
+
+ ret = check_wrap_key(user_ctx->wrap_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ if (black_key == NULL) {
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ if (key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY) {
+ if ((key_info->pf_key != FSL_SHW_PF_KEY_PRG)
+ && (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG)) {
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ } else {
+ if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)) {
+ ret = FSL_RETURN_BAD_FLAG_S; /* not established! */
+ goto out;
+ }
+ }
+
+ black_key[ALGORITHM_OFFSET] = key_info->algorithm;
+
+#ifndef DO_REPEATABLE_WRAP
+ /* Compute T = RND() */
+ ret = fsl_shw_get_random(user_ctx, T_LENGTH, T);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#else
+ memcpy(T, T_block, T_LENGTH);
+#endif
+
+ /* Compute KEK = SHA256(T | ownerid). */
+ ret = calc_kek((uint8_t *) & key_info->userid, sizeof(key_info->userid),
+ T, kek);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Calculation of KEK failed\n");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+
+ rounded_key_length = ROUND_LENGTH(original_key_length);
+
+ di_code = dryice_get_programmed_key(saved_pk, 8 * 21);
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not save current PK: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ /*
+ * Load KEK into DI PKR. Note that we are NOT permuting it before loading,
+ * so we are using it as though it is a 168-bit key ready for the SCC.
+ */
+ di_code = dryice_set_programmed_key(kek, 8 * 21, 0);
+ if (di_code == DI_ERR_INUSE) {
+ /* Temporarily reprogram the PK out from under the user */
+ pk_was_held = 1;
+ dryice_release_programmed_key();
+ di_code = dryice_set_programmed_key(kek, 8 * 21, 0);
+ }
+ if (di_code != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("Could not program KEK: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ pk_needs_restoration = 1;
+ dryice_release_programmed_key();
+
+ /* Find red key */
+ if (key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY) {
+ black_key[LENGTH_OFFSET] = 21;
+ rounded_key_length = 24;
+
+ red_key = saved_pk;
+ } else {
+ black_key[LENGTH_OFFSET] = key_info->key_length;
+
+ red_key = os_alloc_memory(key_info->key_length, 0);
+ if (red_key == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ goto out;
+ }
+ red_key_malloced = 1;
+
+ ret = fsl_shw_read_key(user_ctx, key_info, red_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ }
+
+#ifdef DIAG_SECURITY_FUNC
+ dump("KEY", red_key, black_key[LENGTH_OFFSET]);
+#endif
+ /* Compute KEY' = TDES-encrypt(KEK, KEY) */
+ fsl_shw_sko_init_pf_key(&kek_key_info, FSL_KEY_ALG_TDES,
+ FSL_SHW_PF_KEY_PRG);
+ fsl_shw_sko_set_key_length(&kek_key_info, KEK_LENGTH);
+
+ fsl_shw_scco_init(&kek_ctx, FSL_KEY_ALG_TDES, FSL_SYM_MODE_CBC);
+ fsl_shw_scco_set_flags(&kek_ctx, FSL_SYM_CTX_LOAD);
+ fsl_shw_scco_set_context(&kek_ctx, (uint8_t *) & key_info->userid);
+ ret = fsl_shw_symmetric_encrypt(user_ctx, &kek_key_info, &kek_ctx,
+ rounded_key_length,
+ red_key, black_key + KEY_PRIME_OFFSET);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Encryption of KEY failed\n");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+
+ /* Set up flags info */
+ black_key[FLAGS_OFFSET] = 0;
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ black_key[FLAGS_OFFSET] |= FLAGS_SW_KEY;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ dump("KEY'", black_key + KEY_PRIME_OFFSET, rounded_key_length);
+#endif
+ /* Compute and store ICV into Black Key */
+ ret = calc_icv(T,
+ (uint8_t *) & key_info->userid,
+ sizeof(key_info->userid),
+ black_key, original_key_length, black_key + ICV_OFFSET);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Calculation of ICV failed\n");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+
+ /* Compute T' = 3des-enc-ecb(wrap_key, T); Result goes to Black Key */
+ init_wrap_key(user_ctx->wrap_key, &t_key_info, &t_key_ctx);
+ ret = fsl_shw_symmetric_encrypt(user_ctx, &t_key_info, &t_key_ctx,
+ T_LENGTH,
+ T, black_key + T_PRIME_OFFSET);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Encryption of nonce failed");
+#endif
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ dump("black", black_key, KEY_PRIME_OFFSET + black_key[LENGTH_OFFSET]);
+#endif
+
+ out:
+ if (pk_needs_restoration) {
+ dryice_set_programmed_key(saved_pk, 8 * 21, 0);
+ }
+
+ if (!pk_was_held) {
+ dryice_release_programmed_key();
+ }
+
+ if (red_key_malloced) {
+ memset(red_key, 0, key_info->key_length);
+ os_free_memory(red_key);
+ }
+
+ key_info->key_length = original_key_length;
+
+ /* Erase tracks of confidential data */
+ memset(T, 0, T_LENGTH);
+ memset(&t_key_info, 0, sizeof(t_key_info));
+ memset(&t_key_ctx, 0, sizeof(t_key_ctx));
+ memset(&kek_key_info, 0, sizeof(kek_key_info));
+ memset(&kek_ctx, 0, sizeof(kek_ctx));
+ memset(kek, 0, sizeof(kek));
+ memset(saved_pk, 0, sizeof(saved_pk));
+
+ return ret;
+} /* wrap */
+
+static fsl_shw_return_t create(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+ unsigned key_length = key_info->key_length;
+ di_return_t di_code;
+
+ if (!(key_info->flags & FSL_SKO_KEY_SW_KEY)) {
+ /* Must be creating key for PK */
+ if ((key_info->algorithm != FSL_KEY_ALG_TDES) ||
+ ((key_info->key_length != 16)
+ && (key_info->key_length != 21) /* permuted 168-bit key */
+ &&(key_info->key_length != 24))) {
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ key_length = 21; /* 168-bit PK */
+ }
+
+ /* operational block */
+ {
+ uint8_t key_value[key_length];
+
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creating random key\n");
+#endif
+ ret = fsl_shw_get_random(user_ctx, key_length, key_value);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("get_random for CREATE KEY failed\n");
+#endif
+ goto out;
+ }
+
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ ret = load_slot(user_ctx, key_info, key_value);
+ } else {
+ di_code =
+ dryice_set_programmed_key(key_value, 8 * key_length,
+ 0);
+ if (di_code != 0) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("di set_pk failed: %s\n",
+ di_error_string(di_code));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ ret = FSL_RETURN_OK_S;
+ }
+ memset(key_value, 0, key_length);
+ } /* end operational block */
+
+#ifdef DIAG_SECURITY_FUNC
+ if (ret != FSL_RETURN_OK_S) {
+ LOG_DIAG("Loading random key failed");
+ }
+#endif
+
+ out:
+
+ return ret;
+} /* end fn create */
+
+static fsl_shw_return_t accept(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, const uint8_t * key)
+{
+ uint8_t permuted_key[21];
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ if (key == NULL) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("ACCEPT: Red Key is NULL");
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ dump("red", key, key_info->key_length);
+#endif
+ /* Only SW keys go into the keystore */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+
+ /* Copy in safe number of bytes of Red key */
+ ret = load_slot(user_ctx, key_info, key);
+ } else { /* not SW key */
+ di_return_t di_ret;
+
+ /* Only 3DES PGM key types can be established */
+ if (((key_info->pf_key != FSL_SHW_PF_KEY_PRG)
+ && (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG))
+ || (key_info->algorithm != FSL_KEY_ALG_TDES)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS
+ ("ACCEPT: Failed trying to establish non-PRG"
+ " or invalid 3DES Key: iim%d, iim_prg%d, alg%d\n",
+ (key_info->pf_key != FSL_SHW_PF_KEY_PRG),
+ (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG),
+ (key_info->algorithm != FSL_KEY_ALG_TDES));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ if ((key_info->key_length != 16)
+ && (key_info->key_length != 21)
+ && (key_info->key_length != 24)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("ACCEPT: Failed trying to establish"
+ " invalid 3DES Key: len=%d (%d)\n",
+ key_info->key_length,
+ ((key_info->key_length != 16)
+ && (key_info->key_length != 21)
+ && (key_info->key_length != 24)));
+#endif
+ ret = FSL_RETURN_BAD_KEY_LENGTH_S;
+ goto out;
+ }
+
+ /* Convert key into 168-bit value and put it into PK */
+ if (key_info->key_length != 21) {
+ fsl_shw_permute1_bytes(key, permuted_key,
+ key_info->key_length / 8);
+ di_ret =
+ dryice_set_programmed_key(permuted_key, 168, 0);
+ } else {
+ /* Already permuted ! */
+ di_ret = dryice_set_programmed_key(key, 168, 0);
+ }
+ if (di_ret != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS
+ ("ACCEPT: DryIce error setting Program Key: %s",
+ di_error_string(di_ret));
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ memset(permuted_key, 0, 21);
+
+ return ret;
+} /* end fn accept */
+
+/*!
+ * Place a key into a protected location for use only by cryptographic
+ * algorithms.
+ *
+ * This only needs to be used to a) unwrap a key, or b) set up a key which
+ * could be wrapped by calling #fsl_shw_extract_key() at some later time).
+ *
+ * The protected key will not be available for use until this operation
+ * successfully completes.
+ *
+ * @bug This whole discussion needs review.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be established. In the create case, the key
+ * length must be set.
+ * @param establish_type How @a key will be interpreted to establish a
+ * key for use.
+ * @param key If @a establish_type is #FSL_KEY_WRAP_UNWRAP,
+ * this is the location of a wrapped key. If
+ * @a establish_type is #FSL_KEY_WRAP_CREATE, this
+ * parameter can be @a NULL. If @a establish_type
+ * is #FSL_KEY_WRAP_ACCEPT, this is the location
+ * of a plaintext key.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+ unsigned original_key_length = key_info->key_length;
+ unsigned rounded_key_length;
+ unsigned slot_allocated = 0;
+
+ /* For now, only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("%s: Non-blocking call not supported\n",
+ __FUNCTION__);
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /*
+ HW keys are always 'established', but otherwise do not allow user
+ * to establish over the top of an established key.
+ */
+ if ((key_info->flags & FSL_SKO_KEY_ESTABLISHED)
+ && !(key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("%s: Key already established\n", __FUNCTION__);
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* @bug VALIDATE KEY flags here -- SW or PRG/IIM_PRG */
+
+ /* Write operations into SCC memory require word-multiple number of
+ * bytes. For ACCEPT and CREATE functions, the key length may need
+ * to be rounded up. Calculate. */
+ if (LENGTH_PATCH && (original_key_length & LENGTH_PATCH_MASK) != 0) {
+ rounded_key_length = original_key_length + LENGTH_PATCH
+ - (original_key_length & LENGTH_PATCH_MASK);
+ } else {
+ rounded_key_length = original_key_length;
+ }
+
+ /* SW keys need a place to live */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ ret = alloc_slot(user_ctx, key_info);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Slot allocation failed\n");
+#endif
+ goto out;
+ }
+ slot_allocated = 1;
+ }
+
+ switch (establish_type) {
+ case FSL_KEY_WRAP_CREATE:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creating random key\n");
+#endif
+ ret = create(user_ctx, key_info);
+ break;
+
+ case FSL_KEY_WRAP_ACCEPT:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Accepting plaintext key\n");
+#endif
+ ret = accept(user_ctx, key_info, key);
+ break;
+
+ case FSL_KEY_WRAP_UNWRAP:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Unwrapping wrapped key\n");
+#endif
+ ret = unwrap(user_ctx, key_info, key);
+ break;
+
+ default:
+ ret = FSL_RETURN_BAD_FLAG_S;
+ break;
+ } /* switch */
+
+ out:
+ if (ret != FSL_RETURN_OK_S) {
+ if (slot_allocated) {
+ (void)dealloc_slot(user_ctx, key_info);
+ }
+ key_info->flags &= ~FSL_SKO_KEY_ESTABLISHED;
+ } else {
+ key_info->flags |= FSL_SKO_KEY_ESTABLISHED;
+ }
+
+ return ret;
+} /* end fn fsl_shw_establish_key */
+
+/*!
+ * Wrap a key and retrieve the wrapped value.
+ *
+ * A wrapped key is a key that has been cryptographically obscured. It is
+ * only able to be used with #fsl_shw_establish_key().
+ *
+ * This function will also release a software key (see #fsl_shw_release_key())
+ * so it must be re-established before reuse. This is not true of PGM keys.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ * @param[out] covered_key The location to store the 48-octet wrapped key.
+ * (This size is based upon the maximum key size
+ * of 32 octets).
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* For now, only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Wrapping a key\n");
+#endif
+
+ if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("%s: Key not established\n", __FUNCTION__);
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+ /* Verify that a SW key info really belongs to a SW key */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ /* ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;*/
+ }
+
+ ret = wrap(user_ctx, key_info, covered_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ /* Need to deallocate on successful extraction */
+ (void)dealloc_slot(user_ctx, key_info);
+ /* Mark key not available in the flags */
+ key_info->flags &=
+ ~(FSL_SKO_KEY_ESTABLISHED | FSL_SKO_KEY_PRESENT);
+ memset(key_info->key, 0, sizeof(key_info->key));
+ }
+
+ out:
+ return ret;
+} /* end fn fsl_shw_extract_key */
+
+/*!
+ * De-establish a key so that it can no longer be accessed.
+ *
+ * The key will need to be re-established before it can again be used.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* For now, only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Not in blocking mode\n");
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Releasing a key\n");
+#endif
+
+ if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Key not established\n");
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ (void)dealloc_slot(user_ctx, key_info);
+ /* Turn off 'established' flag */
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("dealloc_slot() called\n");
+#endif
+ key_info->flags &= ~FSL_SKO_KEY_ESTABLISHED;
+ ret = FSL_RETURN_OK_S;
+ goto out;
+ }
+
+ if ((key_info->pf_key == FSL_SHW_PF_KEY_PRG)
+ || (key_info->pf_key == FSL_SHW_PF_KEY_IIM_PRG)) {
+ di_return_t di_ret;
+
+ di_ret = dryice_release_programmed_key();
+ if (di_ret != DI_SUCCESS) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS
+ ("dryice_release_programmed_key() failed: %d\n",
+ di_ret);
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ } else {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Neither SW nor HW key\n");
+#endif
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ return ret;
+} /* end fn fsl_shw_release_key */
+
+fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, uint8_t * key)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+
+ /* Only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+ printk("Reading a key\n");
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Reading a key");
+#endif
+ if (key_info->flags & FSL_SKO_KEY_PRESENT) {
+ memcpy(key_info->key, key, key_info->key_length);
+ ret = FSL_RETURN_OK_S;
+ } else if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+ printk("key established\n");
+ if (key_info->keystore == NULL) {
+ printk("keystore is null\n");
+ /* First verify that the key access is valid */
+ ret =
+ system_keystore.slot_verify_access(system_keystore.
+ user_data,
+ key_info->userid,
+ key_info->
+ handle);
+
+ printk("key in system keystore\n");
+
+ /* Key is in system keystore */
+ ret = keystore_slot_read(&system_keystore,
+ key_info->userid,
+ key_info->handle,
+ key_info->key_length, key);
+ } else {
+ printk("key goes in user keystore.\n");
+ /* Key goes in user keystore */
+ ret = keystore_slot_read(key_info->keystore,
+ key_info->userid,
+ key_info->handle,
+ key_info->key_length, key);
+ }
+ }
+
+ out:
+ return ret;
+} /* end fn fsl_shw_read_key */
+
+#else /* __KERNEL__ && DRYICE */
+
+/* User mode -- these functions are unsupported */
+
+fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key)
+{
+ (void)user_ctx;
+ (void)key_info;
+ (void)establish_type;
+ (void)key;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key)
+{
+ (void)user_ctx;
+ (void)key_info;
+ (void)covered_key;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ (void)user_ctx;
+ (void)key_info;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, uint8_t * key)
+{
+ (void)user_ctx;
+ (void)key_info;
+ (void)key;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+#endif /* __KERNEL__ && DRYICE */
diff --git a/drivers/mxc/security/rng/include/rng_driver.h b/drivers/mxc/security/rng/include/rng_driver.h
new file mode 100644
index 000000000000..7d6d24dde915
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_driver.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef RNG_DRIVER_H
+#define RNG_DRIVER_H
+
+#include "shw_driver.h"
+
+/* This is a Linux flag meaning 'compiling kernel code'... */
+#ifndef __KERNEL__
+#include <inttypes.h>
+#include <stdlib.h>
+#include <memory.h>
+#else
+#include "../../sahara2/include/portable_os.h"
+#endif
+
+#include "../../sahara2/include/fsl_platform.h"
+
+/*! @file rng_driver.h
+ *
+ * @brief Header file to use the RNG driver.
+ *
+ * @ingroup RNG
+ */
+
+#if defined(FSL_HAVE_RNGA)
+
+#include "rng_rnga.h"
+
+#elif defined(FSL_HAVE_RNGB) || defined(FSL_HAVE_RNGC)
+
+#include "rng_rngc.h"
+
+#else /* neither RNGA, RNGB, nor RNGC */
+
+#error NO_RNG_TYPE_IDENTIFIED
+
+#endif
+
+/*****************************************************************************
+ * Enumerations
+ *****************************************************************************/
+
+/*! Values from Version ID register */
+enum rng_type {
+ /*! Type RNGA. */
+ RNG_TYPE_RNGA = 0,
+ /*! Type RNGB. */
+ RNG_TYPE_RNGB = 1,
+ /*! Type RNGC */
+ RNG_TYPE_RNGC = 2
+};
+
+/*!
+ * Return values (error codes) for kernel register interface functions
+ */
+typedef enum rng_return {
+ RNG_RET_OK = 0, /*!< Function succeeded */
+ RNG_RET_FAIL /*!< Non-specific failure */
+} rng_return_t;
+
+/*****************************************************************************
+ * Data Structures
+ *****************************************************************************/
+/*!
+ * An entry in the RNG Work Queue. Based upon standard SHW queue entry.
+ *
+ * This entry also gets saved (for non-blocking requests) in the user's result
+ * pool. When the user picks up the request, the final processing (copy from
+ * data_local to data_user) will get made if status was good.
+ */
+typedef struct rng_work_entry {
+ struct shw_queue_entry_t hdr; /*!< Standards SHW queue info. */
+ uint32_t length; /*!< Number of bytes still needed to satisfy request. */
+ uint32_t *data_local; /*!< Where data from RNG FIFO gets placed. */
+ uint8_t *data_user; /*!< Ultimate target of data. */
+ unsigned completed; /*!< Non-zero if job is done. */
+} rng_work_entry_t;
+
+/*****************************************************************************
+ * Function Prototypes
+ *****************************************************************************/
+
+#ifdef RNG_REGISTER_PEEK_POKE
+/*!
+ * Read value from an RNG register.
+ * The offset will be checked for validity as well as whether it is
+ * accessible at the time of the call.
+ *
+ * This routine cannot be used to read the RNG's Output FIFO if the RNG is in
+ * High Assurance mode.
+ *
+ * @param[in] register_offset The (byte) offset within the RNG block
+ * of the register to be queried. See
+ * RNG(A, C) registers for meanings.
+ * @param[out] value Pointer to where value from the register
+ * should be placed.
+ *
+ * @return See #rng_return_t.
+ */
+/* REQ-FSLSHW-PINTFC-API-LLF-001 */
+extern rng_return_t rng_read_register(uint32_t register_offset,
+ uint32_t * value);
+
+/*!
+ * Write a new value into an RNG register.
+ *
+ * The offset will be checked for validity as well as whether it is
+ * accessible at the time of the call.
+ *
+ * @param[in] register_offset The (byte) offset within the RNG block
+ * of the register to be modified. See
+ * RNG(A, C) registers for meanings.
+ * @param[in] value The value to store into the register.
+ *
+ * @return See #rng_return_t.
+ */
+/* REQ-FSLSHW-PINTFC-API-LLF-002 */
+extern rng_return_t rng_write_register(uint32_t register_offset,
+ uint32_t value);
+#endif /* RNG_REGISTER_PEEK_POKE */
+
+#endif /* RNG_DRIVER_H */
diff --git a/drivers/mxc/security/rng/include/rng_internals.h b/drivers/mxc/security/rng/include/rng_internals.h
new file mode 100644
index 000000000000..62d195bf4df7
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_internals.h
@@ -0,0 +1,680 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 RNG_INTERNALS_H
+#define RNG_INTERNALS_H
+
+/*! @file rng_internals.h
+ *
+ * This file contains definitions which are internal to the RNG driver.
+ *
+ * This header file should only ever be needed by rng_driver.c
+ *
+ * Compile-time flags minimally needed:
+ *
+ * @li Some sort of platform flag. (FSL_HAVE_RNGA or FSL_HAVE_RNGC)
+ *
+ * @ingroup RNG
+ */
+
+#include "portable_os.h"
+#include "shw_driver.h"
+#include "rng_driver.h"
+
+/*! @defgroup rngcompileflags RNG Compile Flags
+ *
+ * These are flags which are used to configure the RNG driver at compilation
+ * time.
+ *
+ * Most of them default to good values for normal operation, but some
+ * (#INT_RNG and #RNG_BASE_ADDR) need to be provided.
+ *
+ * The terms 'defined' and 'undefined' refer to whether a @c \#define (or -D on
+ * a compile command) has defined a given preprocessor symbol. If a given
+ * symbol is defined, then @c \#ifdef \<symbol\> will succeed. Some symbols
+ * described below default to not having a definition, i.e. they are undefined.
+ *
+ */
+
+/*! @addtogroup rngcompileflags */
+/*! @{ */
+
+/*!
+ * This is the maximum number of times the driver will loop waiting for the
+ * RNG hardware to say that it has generated random data. It prevents the
+ * driver from stalling forever should there be a hardware problem.
+ *
+ * Default value is 100. It should be revisited as CPU clocks speed up.
+ */
+#ifndef RNG_MAX_TRIES
+#define RNG_MAX_TRIES 100
+#endif
+
+/* Temporarily define compile-time flags to make Doxygen happy and allow them
+ to get into the documentation. */
+#ifdef DOXYGEN_HACK
+
+/*!
+ * This symbol is the base address of the RNG in the CPU memory map. It may
+ * come from some included header file, or it may come from the compile command
+ * line. This symbol has no default, and the driver will not compile without
+ * it.
+ */
+#define RNG_BASE_ADDR
+#undef RNG_BASE_ADDR
+
+/*!
+ * This symbol is the Interrupt Number of the RNG in the CPU. It may come
+ * from some included header file, or it may come from the compile command
+ * line. This symbol has no default, and the driver will not compile without
+ * it.
+ */
+#define INT_RNG
+#undef INT_RNG
+
+/*!
+ * Defining this symbol will allow other kernel programs to call the
+ * #rng_read_register() and #rng_write_register() functions. If this symbol is
+ * not defined, those functions will not be present in the driver.
+ */
+#define RNG_REGISTER_PEEK_POKE
+#undef RNG_REGISTER_PEEK_POKE
+
+/*!
+ * Turn on compilation of run-time operational, debug, and error messages.
+ *
+ * This flag is undefined by default.
+ */
+/* REQ-FSLSHW-DEBUG-001 */
+
+/*!
+ * Turn on compilation of run-time logging of access to the RNG registers,
+ * except for the RNG's Output FIFO register. See #RNG_ENTROPY_DEBUG.
+ *
+ * This flag is undefined by default
+ */
+#define RNG_REGISTER_DEBUG
+#undef RNG_REGISTER_DEBUG
+
+/*!
+ * Turn on compilation of run-time logging of reading of the RNG's Output FIFO
+ * register. This flag does nothing if #RNG_REGISTER_DEBUG is not defined.
+ *
+ * This flag is undefined by default
+ */
+#define RNG_ENTROPY_DEBUG
+#undef RNG_ENTROPY_DEBUG
+
+/*!
+ * If this flag is defined, the driver will not attempt to put the RNG into
+ * High Assurance mode.
+
+ * If it is undefined, the driver will attempt to put the RNG into High
+ * Assurance mode. If RNG fails to go into High Assurance mode, the driver
+ * will fail to initialize.
+
+ * In either case, if the RNG is already in this mode, the driver will operate
+ * normally.
+ *
+ * This flag is undefined by default.
+ */
+#define RNG_NO_FORCE_HIGH_ASSURANCE
+#undef RNG_NO_FORCE_HIGH_ASSURANCE
+
+/*!
+ * If this flag is defined, the driver will put the RNG into low power mode
+ * every opportunity.
+ *
+ * This flag is undefined by default.
+ */
+#define RNG_USE_LOW_POWER_MODE
+#undef RNG_USE_LOW_POWER_MODE
+
+/*! @} */
+#endif /* end DOXYGEN_HACK */
+
+/*!
+ * If this flag is defined, the driver will not attempt to put the RNG into
+ * High Assurance mode.
+
+ * If it is undefined, the driver will attempt to put the RNG into High
+ * Assurance mode. If RNG fails to go into High Assurance mode, the driver
+ * will fail to initialize.
+
+ * In either case, if the RNG is already in this mode, the driver will operate
+ * normally.
+ *
+ */
+#define RNG_NO_FORCE_HIGH_ASSURANCE
+
+/*!
+ * Read a 32-bit value from an RNG register. This macro depends upon
+ * #rng_base. The os_read32() macro operates on 32-bit quantities, as do
+ * all RNG register reads.
+ *
+ * @param offset Register byte offset within RNG.
+ *
+ * @return The value from the RNG's register.
+ */
+#ifndef RNG_REGISTER_DEBUG
+#define RNG_READ_REGISTER(offset) os_read32(rng_base+(offset))
+#else
+#define RNG_READ_REGISTER(offset) dbg_rng_read_register(offset)
+#endif
+
+/*!
+ * Write a 32-bit value to an RNG register. This macro depends upon
+ * #rng_base. The os_write32() macro operates on 32-bit quantities, as do
+ * all RNG register writes.
+ *
+ * @param offset Register byte offset within RNG.
+ * @param value 32-bit value to store into the register
+ *
+ * @return (void)
+ */
+#ifndef RNG_REGISTER_DEBUG
+#define RNG_WRITE_REGISTER(offset,value) \
+ (void)os_write32(rng_base+(offset), value)
+#else
+#define RNG_WRITE_REGISTER(offset,value) dbg_rng_write_register(offset,value)
+#endif
+
+#ifndef RNG_DRIVER_NAME
+/*! @addtogroup rngcompileflags */
+/*! @{ */
+/*! Name the driver will use to register itself to the kernel as the driver. */
+#define RNG_DRIVER_NAME "rng"
+/*! @} */
+#endif
+
+/*!
+ * Calculate number of words needed to hold the given number of bytes.
+ *
+ * @param byte_count Number of bytes
+ *
+ * @return Number of words
+ */
+#define BYTES_TO_WORDS(byte_count) \
+ (((byte_count)+sizeof(uint32_t)-1)/sizeof(uint32_t))
+
+/*! Gives high-level view of state of the RNG */
+typedef enum rng_status {
+ RNG_STATUS_INITIAL, /*!< Driver status before ever starting. */
+ RNG_STATUS_CHECKING, /*!< During driver initialization. */
+ RNG_STATUS_UNIMPLEMENTED, /*!< Hardware is non-existent / unreachable. */
+ RNG_STATUS_OK, /*!< Hardware is In Secure or Default state. */
+ RNG_STATUS_FAILED /*!< Hardware is In Failed state / other fatal
+ problem. Driver is still able to read/write
+ some registers, but cannot get Random
+ data. */
+} rng_status_t;
+
+static shw_queue_t rng_work_queue;
+
+/*****************************************************************************
+ *
+ * Function Declarations
+ *
+ *****************************************************************************/
+
+/* kernel interface functions */
+OS_DEV_INIT_DCL(rng_init);
+OS_DEV_TASK_DCL(rng_entropy_task);
+OS_DEV_SHUTDOWN_DCL(rng_shutdown);
+OS_DEV_ISR_DCL(rng_irq);
+
+#define RNG_ADD_QUEUE_ENTRY(pool, entry) \
+ SHW_ADD_QUEUE_ENTRY(pool, (shw_queue_entry_t*)entry)
+
+#define RNG_REMOVE_QUEUE_ENTRY(pool, entry) \
+ SHW_REMOVE_QUEUE_ENTRY(pool, (shw_queue_entry_t*)entry)
+#define RNG_GET_WORK_ENTRY() \
+ (rng_work_entry_t*)SHW_POP_FIRST_ENTRY(&rng_work_queue)
+
+/*!
+ * Add an work item to a work list. Item will be marked incomplete.
+ *
+ * @param work Work entry to place at tail of list.
+ *
+ * @return none
+ */
+inline static void RNG_ADD_WORK_ENTRY(rng_work_entry_t * work)
+{
+ work->completed = FALSE;
+
+ SHW_ADD_QUEUE_ENTRY(&rng_work_queue, (shw_queue_entry_t *) work);
+
+ os_dev_schedule_task(rng_entropy_task);
+}
+
+/*!
+ * For #rng_check_register_accessible(), check read permission on given
+ * register.
+ */
+#define RNG_CHECK_READ 0
+
+/*!
+ * For #rng_check_register_accessible(), check write permission on given
+ * register.
+ */
+#define RNG_CHECK_WRITE 1
+
+/* Define different helper symbols based on RNG type */
+#ifdef FSL_HAVE_RNGA
+
+/******************************************************************************
+ *
+ * RNGA support
+ *
+ *****************************************************************************/
+
+/*! Interrupt number for driver. */
+#if defined(MXC_INT_RNG)
+/* Most modern definition */
+#define INT_RNG MXC_INT_RNG
+#elif defined(MXC_INT_RNGA)
+#define INT_RNG MXC_INT_RNGA
+#else
+#define INT_RNG INT_RNGA
+#endif
+
+/*! Base (bus?) address of RNG component. */
+#define RNG_BASE_ADDR RNGA_BASE_ADDR
+
+/*! Read and return the status register. */
+#define RNG_GET_STATUS() \
+ RNG_READ_REGISTER(RNGA_STATUS)
+/*! Configure RNG for Auto seeding */
+#define RNG_AUTO_SEED()
+/* Put RNG for Seed Generation */
+#define RNG_SEED_GEN()
+/*!
+ * Return RNG Type value. Should be RNG_TYPE_RNGA, RNG_TYPE_RNGB,
+ * or RNG_TYPE_RNGC.
+ */
+#define RNG_GET_RNG_TYPE() \
+ ((RNG_READ_REGISTER(RNGA_CONTROL) & RNGA_CONTROL_RNG_TYPE_MASK) \
+ >> RNGA_CONTROL_RNG_TYPE_SHIFT)
+
+/*!
+ * Verify Type value of RNG.
+ *
+ * Returns true of OK, false if not.
+ */
+#define RNG_VERIFY_TYPE(type) \
+ ((type) == RNG_TYPE_RNGA)
+
+/*! Returns non-zero if RNG device is reporting an error. */
+#define RNG_HAS_ERROR() \
+ (RNG_READ_REGISTER(RNGA_STATUS) & RNGA_STATUS_ERROR_INTERRUPT)
+/*! Returns non-zero if Bad Key is selected */
+#define RNG_HAS_BAD_KEY() 0
+/*! Return non-zero if Self Test Done */
+#define RNG_SELF_TEST_DONE() 0
+/*! Returns non-zero if RNG ring oscillators have failed. */
+#define RNG_OSCILLATOR_FAILED() \
+ (RNG_READ_REGISTER(RNGA_STATUS) & RNGA_STATUS_OSCILLATOR_DEAD)
+
+/*! Returns maximum number of 32-bit words in the RNG's output fifo. */
+#define RNG_GET_FIFO_SIZE() \
+ ((RNG_READ_REGISTER(RNGA_STATUS) & RNGA_STATUS_OUTPUT_FIFO_SIZE_MASK) \
+ >> RNGA_STATUS_OUTPUT_FIFO_SIZE_SHIFT)
+
+/*! Returns number of 32-bit words currently in the RNG's output fifo. */
+#define RNG_GET_WORDS_IN_FIFO() \
+ ((RNG_READ_REGISTER(RNGA_STATUS) & RNGA_STATUS_OUTPUT_FIFO_LEVEL_MASK) \
+ >> RNGA_STATUS_OUTPUT_FIFO_LEVEL_SHIFT)
+/* Configuring RNG for Self Test */
+#define RNG_SELF_TEST()
+/*! Get a random value from the RNG's output FIFO. */
+#define RNG_READ_FIFO() \
+ RNG_READ_REGISTER(RNGA_OUTPUT_FIFO)
+
+/*! Put entropy into the RNG's algorithm.
+ * @param value 32-bit value to add to RNG's entropy.
+ **/
+#define RNG_ADD_ENTROPY(value) \
+ RNG_WRITE_REGISTER(RNGA_ENTROPY, (value))
+/*! Return non-zero in case of Error during Self Test */
+#define RNG_CHECK_SELF_ERR() 0
+/*! Return non-zero in case of Error during Seed Generation */
+#define RNG_CHECK_SEED_ERR() 0
+/*! Get the RNG started at generating output. */
+#define RNG_GO() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control | RNGA_CONTROL_GO); \
+}
+/*! To clear all Error Bits in Error Status Register */
+#define RNG_CLEAR_ERR()
+/*! Put RNG into High Assurance mode */
+#define RNG_SET_HIGH_ASSURANCE() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control | RNGA_CONTROL_HIGH_ASSURANCE); \
+}
+
+/*! Return non-zero if the RNG is in High Assurance mode. */
+#define RNG_GET_HIGH_ASSURANCE() \
+ (RNG_READ_REGISTER(RNGA_CONTROL) & RNGA_CONTROL_HIGH_ASSURANCE)
+
+/*! Clear all status, error and otherwise. */
+#define RNG_CLEAR_ALL_STATUS() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control | RNGA_CONTROL_CLEAR_INTERRUPT); \
+}
+/* Return non-zero if RESEED Required */
+#define RNG_RESEED() 1
+
+/*! Return non-zero if Seeding is done */
+#define RNG_SEED_DONE() 1
+
+/*! Return non-zero if everything seems OK with the RNG. */
+#define RNG_WORKING() \
+ ((RNG_READ_REGISTER(RNGA_STATUS) \
+ & (RNGA_STATUS_SLEEP | RNGA_STATUS_SECURITY_VIOLATION \
+ | RNGA_STATUS_ERROR_INTERRUPT | RNGA_STATUS_FIFO_UNDERFLOW \
+ | RNGA_STATUS_LAST_READ_STATUS )) == 0)
+
+/*! Put the RNG into sleep (low-power) mode. */
+#define RNG_SLEEP() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control | RNGA_CONTROL_SLEEP); \
+}
+
+/*! Wake the RNG from sleep (low-power) mode. */
+#define RNG_WAKE() \
+{ \
+ uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control & ~RNGA_CONTROL_SLEEP); \
+}
+
+/*! Mask interrupts so that the driver/OS will not see them. */
+#define RNG_MASK_ALL_INTERRUPTS() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control | RNGA_CONTROL_MASK_INTERRUPTS); \
+}
+
+/*! Unmask interrupts so that the driver/OS will see them. */
+#define RNG_UNMASK_ALL_INTERRUPTS() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGA_CONTROL); \
+ RNG_WRITE_REGISTER(RNGA_CONTROL, control & ~RNGA_CONTROL_MASK_INTERRUPTS);\
+}
+
+/*!
+ * @def RNG_PUT_RNG_TO_SLEEP()
+ *
+ * If compiled with #RNG_USE_LOW_POWER_MODE, this routine will put the RNG
+ * to sleep (low power mode).
+ *
+ * @return none
+ */
+/*!
+ * @def RNG_WAKE_RNG_FROM_SLEEP()
+ *
+ * If compiled with #RNG_USE_LOW_POWER_MODE, this routine will wake the RNG
+ * from sleep (low power mode).
+ *
+ * @return none
+ */
+#ifdef RNG_USE_LOW_POWER_MODE
+
+#define RNG_PUT_RNG_TO_SLEEP() \
+ RNG_SLEEP()
+
+#define RNG_WAKE_FROM_SLEEP() \
+ RNG_WAKE() 1
+
+#else /* not low power mode */
+
+#define RNG_PUT_RNG_TO_SLEEP()
+
+#define RNG_WAKE_FROM_SLEEP()
+
+#endif /* Use low-power mode */
+
+#else /* FSL_HAVE_RNGB or FSL_HAVE_RNGC */
+
+/******************************************************************************
+ *
+ * RNGB and RNGC support
+ *
+ *****************************************************************************/
+/*
+ * The operational interfaces for RNGB and RNGC are almost identical, so
+ * the defines for RNGC work fine for both. There are minor differences
+ * which will be treated within this conditional block.
+ */
+
+/*! Interrupt number for driver. */
+#if defined(MXC_INT_RNG)
+/* Most modern definition */
+#define INT_RNG MXC_INT_RNG
+#elif defined(MXC_INT_RNGC)
+#define INT_RNG MXC_INT_RNGC
+#elif defined(MXC_INT_RNGB)
+#define INT_RNG MXC_INT_RNGB
+#elif defined(INT_RNGC)
+#define INT_RNG INT_RNGC
+#else
+#error NO_INTERRUPT_DEFINED
+#endif
+
+/*! Base address of RNG component. */
+#ifdef FSL_HAVE_RNGB
+#define RNG_BASE_ADDR RNGB_BASE_ADDR
+#else
+#define RNG_BASE_ADDR RNGC_BASE_ADDR
+#endif
+
+/*! Read and return the status register. */
+#define RNG_GET_STATUS() \
+ RNG_READ_REGISTER(RNGC_ERROR)
+
+/*!
+ * Return RNG Type value. Should be RNG_TYPE_RNGA or RNG_TYPE_RNGC.
+ */
+#define RNG_GET_RNG_TYPE() \
+ ((RNG_READ_REGISTER(RNGC_VERSION_ID) & RNGC_VERID_RNG_TYPE_MASK) \
+ >> RNGC_VERID_RNG_TYPE_SHIFT)
+
+/*!
+ * Verify Type value of RNG.
+ *
+ * Returns true of OK, false if not.
+ */
+#ifdef FSL_HAVE_RNGB
+#define RNG_VERIFY_TYPE(type) \
+ ((type) == RNG_TYPE_RNGB)
+#else /* RNGC */
+#define RNG_VERIFY_TYPE(type) \
+ ((type) == RNG_TYPE_RNGC)
+#endif
+
+/*! Returns non-zero if RNG device is reporting an error. */
+#define RNG_HAS_ERROR() \
+ (RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_ERROR)
+/*! Returns non-zero if Bad Key is selected */
+#define RNG_HAS_BAD_KEY() \
+ (RNG_READ_REGISTER(RNGC_ERROR) & RNGC_ERROR_STATUS_BAD_KEY)
+/*! Returns non-zero if RNG ring oscillators have failed. */
+#define RNG_OSCILLATOR_FAILED() \
+ (RNG_READ_REGISTER(RNGC_ERROR) & RNGC_ERROR_STATUS_OSC_ERR)
+
+/*! Returns maximum number of 32-bit words in the RNG's output fifo. */
+#define RNG_GET_FIFO_SIZE() \
+ ((RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_FIFO_SIZE_MASK) \
+ >> RNGC_STATUS_FIFO_SIZE_SHIFT)
+
+/*! Returns number of 32-bit words currently in the RNG's output fifo. */
+#define RNG_GET_WORDS_IN_FIFO() \
+ ((RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_FIFO_LEVEL_MASK) \
+ >> RNGC_STATUS_FIFO_LEVEL_SHIFT)
+
+/*! Get a random value from the RNG's output FIFO. */
+#define RNG_READ_FIFO() \
+ RNG_READ_REGISTER(RNGC_FIFO)
+
+/*! Put entropy into the RNG's algorithm.
+ * @param value 32-bit value to add to RNG's entropy.
+ **/
+#ifdef FSL_HAVE_RNGB
+#define RNG_ADD_ENTROPY(value) \
+ RNG_WRITE_REGISTER(RNGB_ENTROPY, value)
+#else /* RNGC does not have Entropy register */
+#define RNG_ADD_ENTROPY(value)
+#endif
+/*! Wake the RNG from sleep (low-power) mode. */
+#define RNG_WAKE() 1
+/*! Get the RNG started at generating output. */
+#define RNG_GO()
+/*! Put RNG into High Assurance mode. */
+#define RNG_SET_HIGH_ASSURANCE()
+/*! Returns non-zero in case of Error during Self Test */
+#define RNG_CHECK_SELF_ERR() \
+ (RNG_READ_REGISTER(RNGC_ERROR) & RNGC_ERROR_STATUS_ST_ERR)
+/*! Return non-zero in case of Error during Seed Generation */
+#define RNG_CHECK_SEED_ERR() \
+ (RNG_READ_REGISTER(RNGC_ERROR) & RNGC_ERROR_STATUS_STAT_ERR)
+
+/*! Configure RNG for Self Test */
+#define RNG_SELF_TEST() \
+{ \
+ register uint32_t command = RNG_READ_REGISTER(RNGC_COMMAND); \
+ RNG_WRITE_REGISTER(RNGC_COMMAND, command \
+ | RNGC_COMMAND_SELF_TEST); \
+}
+/*! Clearing the Error bits in Error Status Register */
+#define RNG_CLEAR_ERR() \
+{ \
+ register uint32_t command = RNG_READ_REGISTER(RNGC_COMMAND); \
+ RNG_WRITE_REGISTER(RNGC_COMMAND, command \
+ | RNGC_COMMAND_CLEAR_ERROR); \
+}
+
+/*! Return non-zero if Self Test Done */
+#define RNG_SELF_TEST_DONE() \
+ (RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_ST_DONE)
+/* Put RNG for SEED Generation */
+#define RNG_SEED_GEN() \
+{ \
+ register uint32_t command = RNG_READ_REGISTER(RNGC_COMMAND); \
+ RNG_WRITE_REGISTER(RNGC_COMMAND, command \
+ | RNGC_COMMAND_SEED); \
+}
+/* Return non-zero if RESEED Required */
+#define RNG_RESEED() \
+ (RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_RESEED)
+
+/*! Return non-zero if the RNG is in High Assurance mode. */
+#define RNG_GET_HIGH_ASSURANCE() (RNG_READ_REGISTER(RNGC_STATUS) & \
+ RNGC_STATUS_SEC_STATE)
+
+/*! Clear all status, error and otherwise. */
+#define RNG_CLEAR_ALL_STATUS() \
+ RNG_WRITE_REGISTER(RNGC_COMMAND, \
+ RNGC_COMMAND_CLEAR_INTERRUPT \
+ | RNGC_COMMAND_CLEAR_ERROR)
+
+/*! Return non-zero if everything seems OK with the RNG. */
+#define RNG_WORKING() \
+ ((RNG_READ_REGISTER(RNGC_ERROR) \
+ & (RNGC_ERROR_STATUS_STAT_ERR | RNGC_ERROR_STATUS_RAND_ERR \
+ | RNGC_ERROR_STATUS_FIFO_ERR | RNGC_ERROR_STATUS_ST_ERR | \
+ RNGC_ERROR_STATUS_OSC_ERR | RNGC_ERROR_STATUS_LFSR_ERR )) == 0)
+/*! Return Non zero if SEEDING is DONE */
+#define RNG_SEED_DONE() \
+ ((RNG_READ_REGISTER(RNGC_STATUS) & RNGC_STATUS_SEED_DONE) != 0)
+
+/*! Put the RNG into sleep (low-power) mode. */
+#define RNG_SLEEP()
+
+/*! Wake the RNG from sleep (low-power) mode. */
+
+/*! Mask interrupts so that the driver/OS will not see them. */
+#define RNG_MASK_ALL_INTERRUPTS() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGC_CONTROL); \
+ RNG_WRITE_REGISTER(RNGC_CONTROL, control \
+ | RNGC_CONTROL_MASK_DONE \
+ | RNGC_CONTROL_MASK_ERROR); \
+}
+/*! Configuring RNGC for self Test. */
+
+#define RNG_AUTO_SEED() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGC_CONTROL); \
+ RNG_WRITE_REGISTER(RNGC_CONTROL, control \
+ | RNGC_CONTROL_AUTO_SEED); \
+}
+
+/*! Unmask interrupts so that the driver/OS will see them. */
+#define RNG_UNMASK_ALL_INTERRUPTS() \
+{ \
+ register uint32_t control = RNG_READ_REGISTER(RNGC_CONTROL); \
+ RNG_WRITE_REGISTER(RNGC_CONTROL, \
+ control & ~(RNGC_CONTROL_MASK_DONE|RNGC_CONTROL_MASK_ERROR)); \
+}
+
+/*! Put RNG to sleep if appropriate. */
+#define RNG_PUT_RNG_TO_SLEEP()
+
+/*! Wake RNG from sleep if necessary. */
+#define RNG_WAKE_FROM_SLEEP()
+
+#endif /* RNG TYPE */
+
+/* internal functions */
+static os_error_code rng_map_RNG_memory(void);
+static os_error_code rng_setup_interrupt_handling(void);
+#ifdef RNG_REGISTER_PEEK_POKE
+inline static int rng_check_register_offset(uint32_t offset);
+inline static int rng_check_register_accessible(uint32_t offset,
+ int access_write);
+#endif /* DEBUG_RNG_REGISTERS */
+static fsl_shw_return_t rng_drain_fifo(uint32_t * random_p, int count_words);
+static os_error_code rng_grab_config_values(void);
+static void rng_cleanup(void);
+
+#ifdef FSL_HAVE_RNGA
+static void rng_sec_failure(void);
+#endif
+
+#ifdef RNG_REGISTER_DEBUG
+static uint32_t dbg_rng_read_register(uint32_t offset);
+static void dbg_rng_write_register(uint32_t offset, uint32_t value);
+#endif
+
+#if defined(LINUX_VERSION_CODE)
+
+EXPORT_SYMBOL(fsl_shw_add_entropy);
+EXPORT_SYMBOL(fsl_shw_get_random);
+
+#ifdef RNG_REGISTER_PEEK_POKE
+/* For Linux kernel, export the API functions to other kernel modules */
+EXPORT_SYMBOL(rng_read_register);
+EXPORT_SYMBOL(rng_write_register);
+#endif /* DEBUG_RNG_REGISTERS */
+
+
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("Device Driver for RNG");
+
+#endif /* LINUX_VERSION_CODE */
+
+#endif /* RNG_INTERNALS_H */
diff --git a/drivers/mxc/security/rng/include/rng_rnga.h b/drivers/mxc/security/rng/include/rng_rnga.h
new file mode 100644
index 000000000000..971064d51522
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_rnga.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 RNG_RNGA_H
+#define RNG_RNGA_H
+
+/*! @defgroup rngaregs RNGA Registers
+ * @ingroup RNG
+ * These are the definitions for the RNG registers and their offsets
+ * within the RNG. They are used in the @c register_offset parameter of
+ * #rng_read_register() and #rng_write_register().
+ */
+/*! @addtogroup rngaregs */
+/*! @{ */
+
+/*! Control Register. See @ref rngacontrolreg. */
+#define RNGA_CONTROL 0x00
+/*! Status Register. See @ref rngastatusreg. */
+#define RNGA_STATUS 0x04
+/*! Register for adding to the Entropy of the RNG */
+#define RNGA_ENTROPY 0x08
+/*! Register containing latest 32 bits of random value */
+#define RNGA_OUTPUT_FIFO 0x0c
+/*! Mode Register. Non-secure mode access only. See @ref rngmodereg. */
+#define RNGA_MODE 0x10
+/*! Verification Control Register. Non-secure mode access only. See
+ * @ref rngvfctlreg. */
+#define RNGA_VERIFICATION_CONTROL 0x14
+/*! Oscillator Control Counter Register. Non-secure mode access only.
+ * See @ref rngosccntctlreg. */
+#define RNGA_OSCILLATOR_CONTROL_COUNTER 0x18
+/*! Oscillator 1 Counter Register. Non-secure mode access only. See
+ * @ref rngosccntreg. */
+#define RNGA_OSCILLATOR1_COUNTER 0x1c
+/*! Oscillator 2 Counter Register. Non-secure mode access only. See
+ * @ref rngosccntreg. */
+#define RNGA_OSCILLATOR2_COUNTER 0x20
+/*! Oscillator Counter Status Register. Non-secure mode access only. See
+ * @ref rngosccntstatreg. */
+#define RNGA_OSCILLATOR_COUNTER_STATUS 0x24
+/*! @} */
+
+/*! Total address space of the RNGA, in bytes */
+#define RNG_ADDRESS_RANGE 0x28
+
+/*! @defgroup rngacontrolreg RNGA Control Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngacontrolreg */
+/*! @{ */
+/*! These bits are unimplemented or reserved */
+#define RNGA_CONTROL_ZEROS_MASK 0x0fffffe0
+/*! 'RNG type' - should be 0 for RNGA */
+#define RNGA_CONTROL_RNG_TYPE_MASK 0xf0000000
+/*! Number of bits to shift the type to get it to LSB */
+#define RNGA_CONTROL_RNG_TYPE_SHIFT 28
+/*! Put RNG to sleep */
+#define RNGA_CONTROL_SLEEP 0x00000010
+/*! Clear interrupt & status */
+#define RNGA_CONTROL_CLEAR_INTERRUPT 0x00000008
+/*! Mask interrupt generation */
+#define RNGA_CONTROL_MASK_INTERRUPTS 0x00000004
+/*! Enter into Secure Mode. Notify SCC of security violation should FIFO
+ * underflow occur. */
+#define RNGA_CONTROL_HIGH_ASSURANCE 0x00000002
+/*! Load data into FIFO */
+#define RNGA_CONTROL_GO 0x00000001
+/*! @} */
+
+/*! @defgroup rngastatusreg RNGA Status Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngastatusreg */
+/*! @{ */
+/*! RNG Oscillator not working */
+#define RNGA_STATUS_OSCILLATOR_DEAD 0x80000000
+/*! These bits are undefined or reserved */
+#define RNGA_STATUS_ZEROS1_MASK 0x7f000000
+/*! How big FIFO is, in bytes */
+#define RNGA_STATUS_OUTPUT_FIFO_SIZE_MASK 0x00ff0000
+/*! How many bits right to shift fifo size to make it LSB */
+#define RNGA_STATUS_OUTPUT_FIFO_SIZE_SHIFT 16
+/*! How many bytes are currently in the FIFO */
+#define RNGA_STATUS_OUTPUT_FIFO_LEVEL_MASK 0x0000ff00
+/*! How many bits right to shift fifo level to make it LSB */
+#define RNGA_STATUS_OUTPUT_FIFO_LEVEL_SHIFT 8
+/*! These bits are undefined or reserved. */
+#define RNGA_STATUS_ZEROS2_MASK 0x000000e0
+/*! RNG is sleeping. */
+#define RNGA_STATUS_SLEEP 0x00000010
+/*! Error detected. */
+#define RNGA_STATUS_ERROR_INTERRUPT 0x00000008
+/*! FIFO was empty on some read since last status read. */
+#define RNGA_STATUS_FIFO_UNDERFLOW 0x00000004
+/*! FIFO was empty on most recent read. */
+#define RNGA_STATUS_LAST_READ_STATUS 0x00000002
+/*! Security violation occurred. Will only happen in High Assurance mode. */
+#define RNGA_STATUS_SECURITY_VIOLATION 0x00000001
+/*! @} */
+
+/*! @defgroup rngmodereg RNG Mode Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngmodereg */
+/*! @{ */
+/*! These bits are undefined or reserved */
+#define RNGA_MODE_ZEROS_MASK 0xfffffffc
+/*! RNG is in / put RNG in Oscillator Frequency Test Mode. */
+#define RNGA_MODE_OSCILLATOR_FREQ_TEST 0x00000002
+/*! Put RNG in verification mode / RNG is in verification mode. */
+#define RNGA_MODE_VERIFICATION 0x00000001
+/*! @} */
+
+/*! @defgroup rngvfctlreg RNG Verification Control Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngvfctlreg */
+/*! @{ */
+/*! These bits are undefined or reserved. */
+#define RNGA_VFCTL_ZEROS_MASK 0xfffffff8
+/*! Reset the shift registers. */
+#define RNGA_VFCTL_RESET_SHIFT_REGISTERS 0x00000004
+/*! Drive shift registers from system clock. */
+#define RNGA_VFCTL_FORCE_SYSTEM_CLOCK 0x00000002
+/*! Turn off shift register clocks. */
+#define RNGA_VFCTL_SHIFT_CLOCK_OFF 0x00000001
+/*! @} */
+
+/*!
+ * @defgroup rngosccntctlreg RNG Oscillator Counter Control Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngosccntctlreg */
+/*! @{ */
+/*! These bits are undefined or reserved. */
+#define RNGA_OSCCR_ZEROS_MASK 0xfffc0000
+/*! Bits containing clock cycle counter */
+#define RNGA_OSCCR_CLOCK_CYCLES_MASK 0x0003ffff
+/*! Bits to shift right RNG_OSCCR_CLOCK_CYCLES_MASK */
+#define RNGA_OSCCR_CLOCK_CYCLES_SHIFT 0
+/*! @} */
+
+/*!
+ * @defgroup rngosccntreg RNG Oscillator (1 and 2) Counter Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngosccntreg */
+/*! @{ */
+/*! These bits are undefined or reserved. */
+#define RNGA_COUNTER_ZEROS_MASK 0xfff00000
+/*! Bits containing number of clock pulses received from the oscillator. */
+#define RNGA_COUNTER_PULSES_MASK 0x000fffff
+/*! Bits right to shift RNG_COUNTER_PULSES_MASK to make it LSB. */
+#define RNGA_COUNTER_PULSES_SHIFT 0
+/*! @} */
+
+/*!
+ * @defgroup rngosccntstatreg RNG Oscillator Counter Status Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngosccntstatreg */
+/*! @{ */
+/*! These bits are undefined or reserved. */
+#define RNGA_COUNTER_STATUS_ZEROS_MASK 0xfffffffc
+/*! Oscillator 2 has toggled 0x400 times */
+#define RNGA_COUNTER_STATUS_OSCILLATOR2 0x00000002
+/*! Oscillator 1 has toggled 0x400 times */
+#define RNGA_COUNTER_STATUS_OSCILLATOR1 0x00000001
+/*! @} */
+
+#endif /* RNG_RNGA_H */
diff --git a/drivers/mxc/security/rng/include/rng_rngc.h b/drivers/mxc/security/rng/include/rng_rngc.h
new file mode 100644
index 000000000000..336b5fd5f20d
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_rngc.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @file rng_rngc.h
+ *
+ * Definition of the registers for the RNGB and RNGC. The names start with
+ * RNGC where they are in common or relate only to the RNGC; the RNGB-only
+ * definitions begin with RNGB.
+ *
+ */
+
+#ifndef RNG_RNGC_H
+#define RNG_RNGC_H
+
+#define RNGC_VERSION_MAJOR3 3
+
+/*! @defgroup rngcregs RNGB/RNGC Registers
+ * These are the definitions for the RNG registers and their offsets
+ * within the RNG. They are used in the @c register_offset parameter of
+ * #rng_read_register() and #rng_write_register().
+ *
+ * @ingroup RNG
+ */
+/*! @addtogroup rngcregs */
+/*! @{ */
+
+/*! RNGC Version ID Register R/W */
+#define RNGC_VERSION_ID 0x0000
+/*! RNGC Command Register R/W */
+#define RNGC_COMMAND 0x0004
+/*! RNGC Control Register R/W */
+#define RNGC_CONTROL 0x0008
+/*! RNGC Status Register R */
+#define RNGC_STATUS 0x000C
+/*! RNGC Error Status Register R */
+#define RNGC_ERROR 0x0010
+/*! RNGC FIFO Register W */
+#define RNGC_FIFO 0x0014
+/*! Undefined */
+#define RNGC_UNDEF_18 0x0018
+/*! RNGB Entropy Register W */
+#define RNGB_ENTROPY 0x0018
+/*! Undefined */
+#define RNGC_UNDEF_1C 0x001C
+/*! RNGC Verification Control Register1 R/W */
+#define RNGC_VERIFICATION_CONTROL 0x0020
+/*! Undefined */
+#define RNGC_UNDEF_24 0x0024
+/*! RNGB XKEY Data Register R */
+#define RNGB_XKEY 0x0024
+/*! RNGC Oscillator Counter Control Register1 R/W */
+#define RNGC_OSC_COUNTER_CONTROL 0x0028
+/*! RNGC Oscillator Counter Register1 R */
+#define RNGC_OSC_COUNTER 0x002C
+/*! RNGC Oscillator Counter Status Register1 R */
+#define RNGC_OSC_COUNTER_STATUS 0x0030
+/*! @} */
+
+/*! @defgroup rngcveridreg RNGB/RNGC Version ID Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngcveridreg */
+/*! @{ */
+/*! These bits are unimplemented or reserved */
+#define RNGC_VERID_ZEROS_MASK 0x0f000000
+/*! Mask for RNG TYPE */
+#define RNGC_VERID_RNG_TYPE_MASK 0xf0000000
+/*! Shift to make RNG TYPE be LSB */
+#define RNGC_VERID_RNG_TYPE_SHIFT 28
+/*! Mask for RNG Chip Version */
+#define RNGC_VERID_CHIP_VERSION_MASK 0x00ff0000
+/*! Shift to make RNG Chip version be LSB */
+#define RNGC_VERID_CHIP_VERSION_SHIFT 16
+/*! Mask for RNG Major Version */
+#define RNGC_VERID_VERSION_MAJOR_MASK 0x0000ff00
+/*! Shift to make RNG Major version be LSB */
+#define RNGC_VERID_VERSION_MAJOR_SHIFT 8
+/*! Mask for RNG Minor Version */
+#define RNGC_VERID_VERSION_MINOR_MASK 0x000000ff
+/*! Shift to make RNG Minor version be LSB */
+#define RNGC_VERID_VERSION_MINOR_SHIFT 0
+/*! @} */
+
+/*! @defgroup rngccommandreg RNGB/RNGC Command Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngccommandreg */
+/*! @{ */
+/*! These bits are unimplemented or reserved. */
+#define RNGC_COMMAND_ZEROS_MASK 0xffffff8c
+/*! Perform a software reset of the RNGC. */
+#define RNGC_COMMAND_SOFTWARE_RESET 0x00000040
+/*! Clear error from Error Status register (and interrupt). */
+#define RNGC_COMMAND_CLEAR_ERROR 0x00000020
+/*! Clear interrupt & status. */
+#define RNGC_COMMAND_CLEAR_INTERRUPT 0x00000010
+/*! Start RNGC seed generation. */
+#define RNGC_COMMAND_SEED 0x00000002
+/*! Perform a self test of (and reset) the RNGC. */
+#define RNGC_COMMAND_SELF_TEST 0x00000001
+/*! @} */
+
+/*! @defgroup rngccontrolreg RNGB/RNGC Control Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngccontrolreg */
+/*! @{ */
+/*! These bits are unimplemented or reserved */
+#define RNGC_CONTROL_ZEROS_MASK 0xfffffc8c
+/*! Allow access to verification registers. */
+#define RNGC_CONTROL_CTL_ACC 0x00000200
+/*! Put RNGC into deterministic verifcation mode. */
+#define RNGC_CONTROL_VERIF_MODE 0x00000100
+/*! Prevent RNGC from generating interrupts caused by errors. */
+#define RNGC_CONTROL_MASK_ERROR 0x00000040
+
+/*!
+ * Prevent RNGB/RNGC from generating interrupts after Seed Done or Self Test
+ * Mode completion.
+ */
+#define RNGC_CONTROL_MASK_DONE 0x00000020
+/*! Allow RNGC to generate a new seed whenever it is needed. */
+#define RNGC_CONTROL_AUTO_SEED 0x00000010
+/*! Set FIFO Underflow Response.*/
+#define RNGC_CONTROL_FIFO_UFLOW_MASK 0x00000003
+/*! Shift value to make FIFO Underflow Response be LSB. */
+#define RNGC_CONTROL_FIFO_UFLOW_SHIFT 0
+
+/*! @} */
+
+/*! @{ */
+/*! FIFO Underflow should cause ... */
+#define RNGC_CONTROL_FIFO_UFLOW_ZEROS_ERROR 0
+/*! FIFO Underflow should cause ... */
+#define RNGC_CONTROL_FIFO_UFLOW_ZEROS_ERROR2 1
+/*! FIFO Underflow should cause ... */
+#define RNGC_CONTROL_FIFO_UFLOW_BUS_XFR 2
+/*! FIFO Underflow should cause ... */
+#define RNGC_CONTROL_FIFO_UFLOW_ZEROS_INTR 3
+/*! @} */
+
+/*! @defgroup rngcstatusreg RNGB/RNGC Status Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngcstatusreg */
+/*! @{ */
+/*! Unused or MBZ. */
+#define RNGC_STATUS_ZEROS_MASK 0x003e0080
+/*!
+ * Statistical tests pass-fail. Individual bits on indicate failure of a
+ * particular test.
+ */
+#define RNGC_STATUS_STAT_TEST_PF_MASK 0xff000000
+/*! Mask to get Statistical PF to be LSB. */
+#define RNGC_STATUS_STAT_TEST_PF_SHIFT 24
+/*!
+ * Self tests pass-fail. Individual bits on indicate failure of a
+ * particular test.
+ */
+#define RNGC_STATUS_ST_PF_MASK 0x00c00000
+/*! Shift value to get Self Test PF field to be LSB. */
+#define RNGC_STATUS_ST_PF_SHIFT 22
+/* TRNG Self test pass-fail */
+#define RNGC_STATUS_ST_PF_TRNG 0x00800000
+/* PRNG Self test pass-fail */
+#define RNGC_STATUS_ST_PF_PRNG 0x00400000
+/*! Error detected in RNGC. See Error Status register. */
+#define RNGC_STATUS_ERROR 0x00010000
+/*! Size of the internal FIFO in 32-bit words. */
+#define RNGC_STATUS_FIFO_SIZE_MASK 0x0000f000
+/*! Shift value to get FIFO Size to be LSB. */
+#define RNGC_STATUS_FIFO_SIZE_SHIFT 12
+/*! The level (available data) of the internal FIFO in 32-bit words. */
+#define RNGC_STATUS_FIFO_LEVEL_MASK 0x00000f00
+/*! Shift value to get FIFO Level to be LSB. */
+#define RNGC_STATUS_FIFO_LEVEL_SHIFT 8
+/*! A new seed is ready for use. */
+#define RNGC_STATUS_NEXT_SEED_DONE 0x00000040
+/*! The first seed has been generated. */
+#define RNGC_STATUS_SEED_DONE 0x00000020
+/*! Self Test has been completed. */
+#define RNGC_STATUS_ST_DONE 0x00000010
+/*! Reseed is necessary. */
+#define RNGC_STATUS_RESEED 0x00000008
+/*! RNGC is sleeping. */
+#define RNGC_STATUS_SLEEP 0x00000004
+/*! RNGC is currently generating numbers, seeding, generating next seed, or
+ performing a self test. */
+#define RNGC_STATUS_BUSY 0x00000002
+/*! RNGC is in secure state. */
+#define RNGC_STATUS_SEC_STATE 0x00000001
+
+/*! @} */
+
+/*! @defgroup rngcerrstatusreg RNGB/RNGC Error Status Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngcerrstatusreg */
+/*! @{ */
+/*! Unused or MBZ. */
+#define RNGC_ERROR_STATUS_ZEROS_MASK 0xffffff80
+/*! Bad Key Error Status */
+#define RNGC_ERROR_STATUS_BAD_KEY 0x00000040
+/*! Random Compare Error. Previous number matched the current number. */
+#define RNGC_ERROR_STATUS_RAND_ERR 0x00000020
+/*! FIFO Underflow. FIFO was read while empty. */
+#define RNGC_ERROR_STATUS_FIFO_ERR 0x00000010
+/*! Statistic Error Statistic Test failed for the last seed. */
+#define RNGC_ERROR_STATUS_STAT_ERR 0x00000008
+/*! Self-test error. Some self test has failed. */
+#define RNGC_ERROR_STATUS_ST_ERR 0x00000004
+/*!
+ * Oscillator Error. The oscillator may be broken. Clear by hard or soft
+ * reset.
+ */
+#define RNGC_ERROR_STATUS_OSC_ERR 0x00000002
+/*! LFSR Error. Clear by hard or soft reset. */
+#define RNGC_ERROR_STATUS_LFSR_ERR 0x00000001
+
+/*! @} */
+
+/*! Total address space of the RNGB/RNGC registers, in bytes */
+#define RNG_ADDRESS_RANGE 0x34
+
+#endif /* RNG_RNGC_H */
diff --git a/drivers/mxc/security/rng/include/shw_driver.h b/drivers/mxc/security/rng/include/shw_driver.h
new file mode 100644
index 000000000000..5c2280cee2b2
--- /dev/null
+++ b/drivers/mxc/security/rng/include/shw_driver.h
@@ -0,0 +1,2971 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef SHW_DRIVER_H
+#define SHW_DRIVER_H
+
+/* This is a Linux flag meaning 'compiling kernel code'... */
+#ifndef __KERNEL__
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <memory.h>
+#include <stdio.h>
+#else
+#include "../../sahara2/include/portable_os.h"
+#endif /* __KERNEL__ */
+
+#include "../../sahara2/include/fsl_platform.h"
+
+/*! @file shw_driver.h
+ *
+ * @brief Header file to use the SHW driver.
+ *
+ * The SHW driver is used in two modes: By a user, from the FSL SHW API in user
+ * space, which goes through /dev/fsl_shw to make open(), ioctl(), and close()
+ * calls; and by other kernel modules/drivers, which use the FSL SHW API, parts
+ * of which are supported directly by the SHW driver.
+ *
+ * Testing is performed by using the apitest and kernel api test routines
+ * developed for the Sahara2 driver.
+ */
+/*#define DIAG_SECURITY_FUNC*/
+/*! Perform a security function. */
+#define SHW_IOCTL_REQUEST 21
+
+/* This definition may need a new name, and needs to go somewhere which
+ * can determine platform, kernel vs. user, os, etc.
+ */
+#define copy_bytes(out, in, len) memcpy(out, in, len)
+
+
+/*!
+ * This is part of the IOCTL request type passed between kernel and user space.
+ * It is added to #SHW_IOCTL_REQUEST to generate the actual value.
+ */
+typedef enum shw_user_request_t {
+ SHW_USER_REQ_REGISTER_USER, /*!< Initialize user-kernel discussion. */
+ SHW_USER_REQ_DEREGISTER_USER, /*!< Terminate user-kernel discussion. */
+ SHW_USER_REQ_GET_RESULTS, /*!< Get information on outstanding
+ results. */
+ SHW_USER_REQ_GET_CAPABILITIES, /*!< Get information on hardware support. */
+ SHW_USER_REQ_GET_RANDOM, /*!< Get random data from RNG. */
+ SHW_USER_REQ_ADD_ENTROPY, /*!< Add entropy to hardware RNG. */
+ SHW_USER_REQ_DROP_PERMS, /*!< Diminish the permissions of a block of
+ secure memory */
+ SHW_USER_REQ_SSTATUS, /*!< Check the status of a block of secure
+ memory */
+ SHW_USER_REQ_SFREE, /*!< Free a block of secure memory */
+ SHW_USER_REQ_SCC_ENCRYPT, /*!< Encrypt a region of user-owned secure
+ memory */
+ SHW_USER_REQ_SCC_DECRYPT, /*!< Decrypt a region of user-owned secure
+ memory */
+} shw_user_request_t;
+
+
+/*!
+ * @typedef scc_partition_status_t
+ */
+/** Partition status information. */
+typedef enum fsl_shw_partition_status_t {
+ FSL_PART_S_UNUSABLE, /*!< Partition not implemented */
+ FSL_PART_S_UNAVAILABLE, /*!< Partition owned by other host */
+ FSL_PART_S_AVAILABLE, /*!< Partition available */
+ FSL_PART_S_ALLOCATED, /*!< Partition owned by host but not engaged
+ */
+ FSL_PART_S_ENGAGED, /*!< Partition owned by host and engaged */
+} fsl_shw_partition_status_t;
+
+
+/*
+ * Structure passed during user ioctl() calls to manage secure partitions.
+ */
+typedef struct scc_partition_info_t {
+ uint32_t user_base; /*!< Userspace pointer to base of partition */
+ uint32_t permissions; /*!< Permissions to give the partition (only
+ used in call to _DROP_PERMS) */
+ fsl_shw_partition_status_t status; /*!< Status of the partition */
+} scc_partition_info_t;
+
+
+/******************************************************************************
+ * Enumerations
+ *****************************************************************************/
+/*!
+ * Flags for the state of the User Context Object (#fsl_shw_uco_t).
+ */
+typedef enum fsl_shw_user_ctx_flags_t
+{
+ /*!
+ * API will block the caller until operation completes. The result will be
+ * available in the return code. If this is not set, user will have to get
+ * results using #fsl_shw_get_results().
+ */
+ FSL_UCO_BLOCKING_MODE = 0x01,
+ /*!
+ * User wants callback (at the function specified with
+ * #fsl_shw_uco_set_callback()) when the operation completes. This flag is
+ * valid only if #FSL_UCO_BLOCKING_MODE is not set.
+ */
+ FSL_UCO_CALLBACK_MODE = 0x02,
+ /*! Do not free descriptor chain after driver (adaptor) finishes */
+ FSL_UCO_SAVE_DESC_CHAIN = 0x04,
+ /*!
+ * User has made at least one request with callbacks requested, so API is
+ * ready to handle others.
+ */
+ FSL_UCO_CALLBACK_SETUP_COMPLETE = 0x08,
+ /*!
+ * (virtual) pointer to descriptor chain is completely linked with physical
+ * (DMA) addresses, ready for the hardware. This flag should not be used
+ * by FSL SHW API programs.
+ */
+ FSL_UCO_CHAIN_PREPHYSICALIZED = 0x10,
+ /*!
+ * The user has changed the context but the changes have not been copied to
+ * the kernel driver.
+ */
+ FSL_UCO_CONTEXT_CHANGED = 0x20,
+ /*! Internal Use. This context belongs to a user-mode API user. */
+ FSL_UCO_USERMODE_USER = 0x40,
+} fsl_shw_user_ctx_flags_t;
+
+
+/*!
+ * Return code for FSL_SHW library.
+ *
+ * These codes may be returned from a function call. In non-blocking mode,
+ * they will appear as the status in a Result Object.
+ */
+/* REQ-FSLSHW-ERR-001 */
+typedef enum fsl_shw_return_t
+{
+ /*!
+ * No error. As a function return code in Non-blocking mode, this may
+ * simply mean that the operation was accepted for eventual execution.
+ */
+ FSL_RETURN_OK_S = 0,
+ /*! Failure for non-specific reason. */
+ FSL_RETURN_ERROR_S,
+ /*!
+ * Operation failed because some resource was not able to be allocated.
+ */
+ FSL_RETURN_NO_RESOURCE_S,
+ /*! Crypto algorithm unrecognized or improper. */
+ FSL_RETURN_BAD_ALGORITHM_S,
+ /*! Crypto mode unrecognized or improper. */
+ FSL_RETURN_BAD_MODE_S,
+ /*! Flag setting unrecognized or inconsistent. */
+ FSL_RETURN_BAD_FLAG_S,
+ /*! Improper or unsupported key length for algorithm. */
+ FSL_RETURN_BAD_KEY_LENGTH_S,
+ /*! Improper parity in a (DES, TDES) key. */
+ FSL_RETURN_BAD_KEY_PARITY_S,
+ /*!
+ * Improper or unsupported data length for algorithm or internal buffer.
+ */
+ FSL_RETURN_BAD_DATA_LENGTH_S,
+ /*! Authentication / Integrity Check code check failed. */
+ FSL_RETURN_AUTH_FAILED_S,
+ /*! A memory error occurred. */
+ FSL_RETURN_MEMORY_ERROR_S,
+ /*! An error internal to the hardware occurred. */
+ FSL_RETURN_INTERNAL_ERROR_S,
+ /*! ECC detected Point at Infinity */
+ FSL_RETURN_POINT_AT_INFINITY_S,
+ /*! ECC detected No Point at Infinity */
+ FSL_RETURN_POINT_NOT_AT_INFINITY_S,
+ /*! GCD is One */
+ FSL_RETURN_GCD_IS_ONE_S,
+ /*! GCD is not One */
+ FSL_RETURN_GCD_IS_NOT_ONE_S,
+ /*! Candidate is Prime */
+ FSL_RETURN_PRIME_S,
+ /*! Candidate is not Prime */
+ FSL_RETURN_NOT_PRIME_S,
+ /*! N register loaded improperly with even value */
+ FSL_RETURN_EVEN_MODULUS_ERROR_S,
+ /*! Divisor is zero. */
+ FSL_RETURN_DIVIDE_BY_ZERO_ERROR_S,
+ /*! Bad Exponent or Scalar value for Point Multiply */
+ FSL_RETURN_BAD_EXPONENT_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_OSCILLATOR_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_STATISTICS_ERROR_S,
+} fsl_shw_return_t;
+
+
+/*!
+ * Algorithm Identifier.
+ *
+ * Selection of algorithm will determine how large the block size of the
+ * algorithm is. Context size is the same length unless otherwise specified.
+ * Selection of algorithm also affects the allowable key length.
+ */
+typedef enum fsl_shw_key_alg_t
+{
+ /*!
+ * Key will be used to perform an HMAC. Key size is 1 to 64 octets. Block
+ * size is 64 octets.
+ */
+ FSL_KEY_ALG_HMAC,
+ /*!
+ * Advanced Encryption Standard (Rijndael). Block size is 16 octets. Key
+ * size is 16 octets. (The single choice of key size is a Sahara platform
+ * limitation.)
+ */
+ FSL_KEY_ALG_AES,
+ /*!
+ * Data Encryption Standard. Block size is 8 octets. Key size is 8
+ * octets.
+ */
+ FSL_KEY_ALG_DES,
+ /*!
+ * 2- or 3-key Triple DES. Block size is 8 octets. Key size is 16 octets
+ * for 2-key Triple DES, and 24 octets for 3-key.
+ */
+ FSL_KEY_ALG_TDES,
+ /*!
+ * ARC4. No block size. Context size is 259 octets. Allowed key size is
+ * 1-16 octets. (The choices for key size are a Sahara platform
+ * limitation.)
+ */
+ FSL_KEY_ALG_ARC4,
+} fsl_shw_key_alg_t;
+
+
+/*!
+ * Mode selector for Symmetric Ciphers.
+ *
+ * The selection of mode determines how a cryptographic algorithm will be
+ * used to process the plaintext or ciphertext.
+ *
+ * For all modes which are run block-by-block (that is, all but
+ * #FSL_SYM_MODE_STREAM), any partial operations must be performed on a text
+ * length which is multiple of the block size. Except for #FSL_SYM_MODE_CTR,
+ * these block-by-block algorithms must also be passed a total number of octets
+ * which is a multiple of the block size.
+ *
+ * In modes which require that the total number of octets of data be a multiple
+ * of the block size (#FSL_SYM_MODE_ECB and #FSL_SYM_MODE_CBC), and the user
+ * has a total number of octets which are not a multiple of the block size, the
+ * user must perform any necessary padding to get to the correct data length.
+ */
+typedef enum fsl_shw_sym_mode_t
+{
+ /*!
+ * Stream. There is no associated block size. Any request to process data
+ * may be of any length. This mode is only for ARC4 operations, and is
+ * also the only mode used for ARC4.
+ */
+ FSL_SYM_MODE_STREAM,
+
+ /*!
+ * Electronic Codebook. Each block of data is encrypted/decrypted. The
+ * length of the data stream must be a multiple of the block size. This
+ * mode may be used for DES, 3DES, and AES. The block size is determined
+ * by the algorithm.
+ */
+ FSL_SYM_MODE_ECB,
+ /*!
+ * Cipher-Block Chaining. Each block of data is encrypted/decrypted and
+ * then "chained" with the previous block by an XOR function. Requires
+ * context to start the XOR (previous block). This mode may be used for
+ * DES, 3DES, and AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CBC,
+ /*!
+ * Counter. The counter is encrypted, then XORed with a block of data.
+ * The counter is then incremented (using modulus arithmetic) for the next
+ * block. The final operation may be non-multiple of block size. This mode
+ * may be used for AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CTR,
+} fsl_shw_sym_mode_t;
+
+
+/*!
+ * Algorithm selector for Cryptographic Hash functions.
+ *
+ * Selection of algorithm determines how large the context and digest will be.
+ * Context is the same size as the digest (resulting hash), unless otherwise
+ * specified.
+ */
+typedef enum fsl_shw_hash_alg_t
+{
+ FSL_HASH_ALG_MD5, /*!< MD5 algorithm. Digest is 16 octets. */
+ FSL_HASH_ALG_SHA1, /*!< SHA-1 (aka SHA or SHA-160) algorithm.
+ Digest is 20 octets. */
+ FSL_HASH_ALG_SHA224, /*!< SHA-224 algorithm. Digest is 28 octets,
+ though context is 32 octets. */
+ FSL_HASH_ALG_SHA256 /*!< SHA-256 algorithm. Digest is 32
+ octets. */
+} fsl_shw_hash_alg_t;
+
+
+/*!
+ * The type of Authentication-Cipher function which will be performed.
+ */
+typedef enum fsl_shw_acc_mode_t
+{
+ /*!
+ * CBC-MAC for Counter. Requires context and modulus. Final operation may
+ * be non-multiple of block size. This mode may be used for AES.
+ */
+ FSL_ACC_MODE_CCM,
+ /*!
+ * SSL mode. Not supported. Combines HMAC and encrypt (or decrypt).
+ * Needs one key object for encryption, another for the HMAC. The usual
+ * hashing and symmetric encryption algorithms are supported.
+ */
+ FSL_ACC_MODE_SSL
+} fsl_shw_acc_mode_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-HCO-001 */
+/*!
+ * Flags which control a Hash operation.
+ */
+typedef enum fsl_shw_hash_ctx_flags_t
+{
+ FSL_HASH_FLAGS_INIT = 0x01, /*!< Context is empty. Hash is started
+ from scratch, with a message-processed
+ count of zero. */
+ FSL_HASH_FLAGS_SAVE = 0x02, /*!< Retrieve context from hardware after
+ hashing. If used with the
+ #FSL_HASH_FLAGS_FINALIZE flag, the final
+ digest value will be saved in the
+ object. */
+ FSL_HASH_FLAGS_LOAD = 0x04, /*!< Place context into hardware before
+ hashing. */
+ FSL_HASH_FLAGS_FINALIZE = 0x08, /*!< PAD message and perform final digest
+ operation. If user message is
+ pre-padded, this flag should not be
+ used. */
+} fsl_shw_hash_ctx_flags_t;
+
+
+/*!
+ * Flags which control an HMAC operation.
+ *
+ * These may be combined by ORing them together. See #fsl_shw_hmco_set_flags()
+ * and #fsl_shw_hmco_clear_flags().
+ */
+typedef enum fsl_shw_hmac_ctx_flags_t
+{
+ FSL_HMAC_FLAGS_INIT = 1, /**< Message context is empty. HMAC is
+ started from scratch (with key) or from
+ precompute of inner hash, depending on
+ whether
+ #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT is
+ set. */
+ FSL_HMAC_FLAGS_SAVE = 2, /**< Retrieve ongoing context from hardware
+ after hashing. If used with the
+ #FSL_HMAC_FLAGS_FINALIZE flag, the final
+ digest value (HMAC) will be saved in the
+ object. */
+ FSL_HMAC_FLAGS_LOAD = 4, /**< Place ongoing context into hardware
+ before hashing. */
+ FSL_HMAC_FLAGS_FINALIZE = 8, /**< PAD message and perform final HMAC
+ operations of inner and outer hashes. */
+ FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT = 16 /**< This means that the context
+ contains precomputed inner and outer
+ hash values. */
+} fsl_shw_hmac_ctx_flags_t;
+
+
+/**
+ * Flags to control use of the #fsl_shw_scco_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_scco_set_flags() and #fsl_shw_scco_clear_flags()
+ */
+typedef enum fsl_shw_sym_ctx_flags_t
+{
+ /**
+ * Context is empty. In ARC4, this means that the S-Box needs to be
+ * generated from the key. In #FSL_SYM_MODE_CBC mode, this allows an IV of
+ * zero to be specified. In #FSL_SYM_MODE_CTR mode, it means that an
+ * initial CTR value of zero is desired.
+ */
+ FSL_SYM_CTX_INIT = 1,
+ /**
+ * Load context from object into hardware before running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_LOAD = 2,
+ /**
+ * Save context from hardware into object after running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_SAVE = 4,
+ /**
+ * Context (SBox) is to be unwrapped and wrapped on each use.
+ * This flag is unsupported.
+ * */
+ FSL_SYM_CTX_PROTECT = 8,
+} fsl_shw_sym_ctx_flags_t;
+
+
+/**
+ * Flags which describe the state of the #fsl_shw_sko_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_sko_set_flags() and #fsl_shw_sko_clear_flags()
+ */
+typedef enum fsl_shw_key_flags_t
+{
+ FSL_SKO_KEY_IGNORE_PARITY = 1, /*!< If algorithm is DES or 3DES, do not
+ validate the key parity bits. */
+ FSL_SKO_KEY_PRESENT = 2, /*!< Clear key is present in the object. */
+ FSL_SKO_KEY_ESTABLISHED = 4, /*!< Key has been established for use. This
+ feature is not available for all
+ platforms, nor for all algorithms and
+ modes.*/
+ FSL_SKO_USE_SECRET_KEY = 8, /*!< Use device-unique key. Not always
+ available. */
+ FSL_SKO_KEY_SW_KEY = 16, /*!< Clear key can be provided to the user */
+ FSL_SKO_KEY_SELECT_PF_KEY = 32, /*!< Internal flag to show that this key
+ references one of the hardware keys, and
+ its value is in pf_key. */
+} fsl_shw_key_flags_t;
+
+
+/**
+ * Type of value which is associated with an established key.
+ */
+typedef uint64_t key_userid_t;
+
+
+/**
+ * Flags which describe the state of the #fsl_shw_acco_t.
+ *
+ * The @a FSL_ACCO_CTX_INIT and @a FSL_ACCO_CTX_FINALIZE flags, when used
+ * together, provide for a one-shot operation.
+ */
+typedef enum fsl_shw_auth_ctx_flags_t
+{
+ FSL_ACCO_CTX_INIT = 1, /**< Initialize Context(s) */
+ FSL_ACCO_CTX_LOAD = 2, /**< Load intermediate context(s).
+ This flag is unsupported. */
+ FSL_ACCO_CTX_SAVE = 4, /**< Save intermediate context(s).
+ This flag is unsupported. */
+ FSL_ACCO_CTX_FINALIZE = 8, /**< Create MAC during this operation. */
+ FSL_ACCO_NIST_CCM = 0x10, /**< Formatting of CCM input data is
+ performed by calls to
+ #fsl_shw_ccm_nist_format_ctr_and_iv() and
+ #fsl_shw_ccm_nist_update_ctr_and_iv(). */
+} fsl_shw_auth_ctx_flags_t;
+
+
+/**
+ * The operation which controls the behavior of #fsl_shw_establish_key().
+ *
+ * These values are passed to #fsl_shw_establish_key().
+ */
+typedef enum fsl_shw_key_wrap_t
+{
+ FSL_KEY_WRAP_CREATE, /**< Generate a key from random values. */
+ FSL_KEY_WRAP_ACCEPT, /**< Use the provided clear key. */
+ FSL_KEY_WRAP_UNWRAP /**< Unwrap a previously wrapped key. */
+} fsl_shw_key_wrap_t;
+
+
+/**
+ * Modulus Selector for CTR modes.
+ *
+ * The incrementing of the Counter value may be modified by a modulus. If no
+ * modulus is needed or desired for AES, use #FSL_CTR_MOD_128.
+ */
+typedef enum fsl_shw_ctr_mod_t
+{
+ FSL_CTR_MOD_8, /**< Run counter with modulus of 2^8. */
+ FSL_CTR_MOD_16, /**< Run counter with modulus of 2^16. */
+ FSL_CTR_MOD_24, /**< Run counter with modulus of 2^24. */
+ FSL_CTR_MOD_32, /**< Run counter with modulus of 2^32. */
+ FSL_CTR_MOD_40, /**< Run counter with modulus of 2^40. */
+ FSL_CTR_MOD_48, /**< Run counter with modulus of 2^48. */
+ FSL_CTR_MOD_56, /**< Run counter with modulus of 2^56. */
+ FSL_CTR_MOD_64, /**< Run counter with modulus of 2^64. */
+ FSL_CTR_MOD_72, /**< Run counter with modulus of 2^72. */
+ FSL_CTR_MOD_80, /**< Run counter with modulus of 2^80. */
+ FSL_CTR_MOD_88, /**< Run counter with modulus of 2^88. */
+ FSL_CTR_MOD_96, /**< Run counter with modulus of 2^96. */
+ FSL_CTR_MOD_104, /**< Run counter with modulus of 2^104. */
+ FSL_CTR_MOD_112, /**< Run counter with modulus of 2^112. */
+ FSL_CTR_MOD_120, /**< Run counter with modulus of 2^120. */
+ FSL_CTR_MOD_128 /**< Run counter with modulus of 2^128. */
+} fsl_shw_ctr_mod_t;
+
+
+/**
+ * A work type associated with a work/result queue request.
+ */
+typedef enum shw_work_type_t
+{
+ SHW_WORK_GET_RANDOM = 1, /**< fsl_shw_get_random() request. */
+ SHW_WORK_ADD_RANDOM, /**< fsl_shw_add_entropy() request. */
+} shw_work_type_t;
+
+
+/**
+ * Permissions flags for Secure Partitions
+ */
+typedef enum fsl_shw_permission_t
+{
+/** SCM Access Permission: Do not zeroize/deallocate partition on SMN Fail state */
+ FSL_PERM_NO_ZEROIZE = 0x80000000,
+/** SCM Access Permission: Enforce trusted key read in */
+ FSL_PERM_TRUSTED_KEY_READ = 0x40000000,
+/** SCM Access Permission: Ignore Supervisor/User mode in permission determination */
+ FSL_PERM_HD_S = 0x00000800,
+/** SCM Access Permission: Allow Read Access to Host Domain */
+ FSL_PERM_HD_R = 0x00000400,
+/** SCM Access Permission: Allow Write Access to Host Domain */
+ FSL_PERM_HD_W = 0x00000200,
+/** SCM Access Permission: Allow Execute Access to Host Domain */
+ FSL_PERM_HD_X = 0x00000100,
+/** SCM Access Permission: Allow Read Access to Trusted Host Domain */
+ FSL_PERM_TH_R = 0x00000040,
+/** SCM Access Permission: Allow Write Access to Trusted Host Domain */
+ FSL_PERM_TH_W = 0x00000020,
+/** SCM Access Permission: Allow Read Access to Other/World Domain */
+ FSL_PERM_OT_R = 0x00000004,
+/** SCM Access Permission: Allow Write Access to Other/World Domain */
+ FSL_PERM_OT_W = 0x00000002,
+/** SCM Access Permission: Allow Execute Access to Other/World Domain */
+ FSL_PERM_OT_X = 0x00000001,
+} fsl_shw_permission_t;
+
+/*!
+ * Select the cypher mode to use for partition cover/uncover operations.
+ *
+ * They currently map directly to the values used in the SCC2 driver, but this
+ * is not guarinteed behavior.
+ */
+typedef enum fsl_shw_cypher_mode_t
+{
+ FSL_SHW_CYPHER_MODE_ECB = 1, /*!< ECB mode */
+ FSL_SHW_CYPHER_MODE_CBC = 2, /*!< CBC mode */
+} fsl_shw_cypher_mode_t;
+
+/*!
+ * Which platform key should be presented for cryptographic use.
+ */
+typedef enum fsl_shw_pf_key_t {
+ FSL_SHW_PF_KEY_IIM, /*!< Present fused IIM key */
+ FSL_SHW_PF_KEY_PRG, /*!< Present Program key */
+ FSL_SHW_PF_KEY_IIM_PRG, /*!< Present IIM ^ Program key */
+ FSL_SHW_PF_KEY_IIM_RND, /*!< Present Random key */
+ FSL_SHW_PF_KEY_RND, /*!< Present IIM ^ Random key */
+} fsl_shw_pf_key_t;
+
+/*!
+ * The various security tamper events
+ */
+typedef enum fsl_shw_tamper_t {
+ FSL_SHW_TAMPER_NONE, /*!< No error detected */
+ FSL_SHW_TAMPER_WTD, /*!< wire-mesh tampering det */
+ FSL_SHW_TAMPER_ETBD, /*!< ext tampering det: input B */
+ FSL_SHW_TAMPER_ETAD, /*!< ext tampering det: input A */
+ FSL_SHW_TAMPER_EBD, /*!< external boot detected */
+ FSL_SHW_TAMPER_SAD, /*!< security alarm detected */
+ FSL_SHW_TAMPER_TTD, /*!< temperature tampering det */
+ FSL_SHW_TAMPER_CTD, /*!< clock tampering det */
+ FSL_SHW_TAMPER_VTD, /*!< voltage tampering det */
+ FSL_SHW_TAMPER_MCO, /*!< monotonic counter overflow */
+ FSL_SHW_TAMPER_TCO, /*!< time counter overflow */
+} fsl_shw_tamper_t;
+
+/*
+ * Structure passed during user ioctl() calls to manage data stored in secure
+ * partitions.
+ */
+
+typedef struct scc_region_t {
+ uint32_t partition_base; /*!< Base address of partition */
+ uint32_t offset; /*!< Byte offset into partition */
+ uint32_t length; /*!< Number of bytes in request */
+ uint8_t *black_data; /*!< Address of cipher text */
+ uint64_t owner_id; /*!< user's secret */
+ fsl_shw_cypher_mode_t cypher_mode; /*!< ECB or CBC */
+ uint32_t IV[4]; /*!< IV for CBC mode */
+} scc_region_t;
+
+/******************************************************************************
+ * Data Structures
+ *****************************************************************************/
+
+/**
+ * Initialization Object
+ */
+typedef struct fsl_sho_ibo
+{
+} fsl_sho_ibo_t;
+
+
+/**
+ * Common Entry structure for work queues, results queues.
+ */
+typedef struct shw_queue_entry_t {
+ struct shw_queue_entry_t* next; /**< Next entry in queue. */
+ struct fsl_shw_uco_t* user_ctx; /**< Associated user context. */
+ uint32_t flags; /**< User context flags at time of request. */
+ void (*callback)(struct fsl_shw_uco_t* uco); /**< Any callback request. */
+ uint32_t user_ref; /**< User's reference for this request. */
+ fsl_shw_return_t code; /**< FSL SHW result of this operation. */
+ uint32_t detail1; /**< Any extra error info. */
+ uint32_t detail2; /**< More any extra error info. */
+ void* user_mode_req; /**< Pointer into user space. */
+ uint32_t (*postprocess)(struct shw_queue_entry_t* q); /**< (internal)
+ function to call
+ when this operation
+ completes.
+ */
+} shw_queue_entry_t;
+
+
+/**
+ * A queue. Fields must be initialized to NULL before use.
+ */
+typedef struct shw_queue_t
+{
+ struct shw_queue_entry_t* head; /**< First entry in queue. */
+ struct shw_queue_entry_t* tail; /**< Last entry. */
+} shw_queue_t;
+
+
+/**
+ * Secure Partition information
+ */
+typedef struct fsl_shw_spo_t
+{
+ uint32_t user_base;
+ void* kernel_base;
+ struct fsl_shw_spo_t* next;
+} fsl_shw_spo_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-UCO-001 */
+/**
+ * User Context Object
+ */
+typedef struct fsl_shw_uco_t
+{
+ int openfd; /**< user-mode file descriptor */
+ uint32_t user_ref; /**< User's reference */
+ void (*callback)(struct fsl_shw_uco_t* uco); /**< User's callback fn */
+ uint32_t flags; /**< from fsl_shw_user_ctx_flags_t */
+ unsigned pool_size; /**< maximum size of user result pool */
+#ifdef __KERNEL__
+ shw_queue_t result_pool; /**< where non-blocking results go */
+ os_process_handle_t process; /**< remember for signalling User mode */
+ fsl_shw_spo_t* partition; /**< chain of secure partitions owned by
+ the user */
+#endif
+ struct fsl_shw_uco_t* next; /**< To allow user-mode chaining of contexts,
+ for signalling and in kernel, to link user
+ contexts. */
+ fsl_shw_pf_key_t wrap_key; /*!< What key for ciphering T */
+} fsl_shw_uco_t;
+
+
+/* REQ-FSLSHW-PINTFC-API-GEN-006 ?? */
+/**
+ * Result object
+ */
+typedef struct fsl_shw_result_t
+{
+ uint32_t user_ref; /**< User's reference at time of request. */
+ fsl_shw_return_t code; /**< Return code from request. */
+ uint32_t detail1; /**< Extra error info. Unused in SHW driver. */
+ uint32_t detail2; /**< Extra error info. Unused in SHW driver. */
+ void* user_req; /**< Pointer to original user request. */
+} fsl_shw_result_t;
+
+
+/**
+ * Keystore Object
+ */
+typedef struct fsl_shw_kso_t
+{
+#ifdef __KERNEL__
+ os_lock_t lock; /**< Pointer to lock that controls access to
+ the keystore. */
+#endif
+ void* user_data; /**< Pointer to user structure that handles
+ the internals of the keystore. */
+ fsl_shw_return_t (*data_init) (fsl_shw_uco_t* user_ctx,
+ void** user_data);
+ void (*data_cleanup) (fsl_shw_uco_t* user_ctx,
+ void** user_data);
+ fsl_shw_return_t (*slot_verify_access)(void* user_data, uint64_t owner_id,
+ uint32_t slot);
+ fsl_shw_return_t (*slot_alloc) (void* user_data, uint32_t size_bytes,
+ uint64_t owner_id, uint32_t* slot);
+ fsl_shw_return_t (*slot_dealloc) (void* user_data,
+ uint64_t owner_id, uint32_t slot);
+ void* (*slot_get_address) (void* user_data, uint32_t slot);
+ uint32_t (*slot_get_base) (void* user_data, uint32_t slot);
+ uint32_t (*slot_get_offset) (void* user_data, uint32_t slot);
+ uint32_t (*slot_get_slot_size) (void* user_data, uint32_t slot);
+} fsl_shw_kso_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-SKO-001 */
+/**
+ * Secret Key Context Object
+ */
+typedef struct fsl_shw_sko_t
+{
+ uint32_t flags; /**< Flags from #fsl_shw_sym_ctx_flags_t. */
+ fsl_shw_key_alg_t algorithm; /**< Algorithm for this key. */
+ key_userid_t userid; /**< User's identifying value for Black key. */
+ uint32_t handle; /**< Reference in SCC driver for Red key. */
+ uint16_t key_length; /**< Length of stored key, in bytes. */
+ uint8_t key[64]; /**< Bytes of stored key. */
+ struct fsl_shw_kso_t* keystore; /**< If present, key is in keystore */
+ fsl_shw_pf_key_t pf_key; /*!< What key to select for use when this key
+ is doing ciphering. If FSL_SHW_PF_KEY_PRG
+ or FSL_SHW_PF_KEY_PRG_IIM is the value, then
+ a 'present' or 'established' key will be
+ programed into the PK. */
+} fsl_shw_sko_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-CO-001 */
+/**
+ * Platform Capability Object
+ *
+ * Pointer to this structure is returned by fsl_shw_get_capabilities() and
+ * queried with the various fsl_shw_pco_() functions.
+ */
+typedef struct fsl_shw_pco_t
+{
+ int api_major; /**< Major version number for API. */
+ int api_minor; /**< Minor version number for API. */
+ int driver_major; /**< Major version of some driver. */
+ int driver_minor; /**< Minor version of some driver. */
+ unsigned sym_algorithm_count; /**< Number of sym_algorithms. */
+ fsl_shw_key_alg_t* sym_algorithms; /**< Pointer to array. */
+ unsigned sym_mode_count; /**< Number of sym_modes. */
+ fsl_shw_sym_mode_t* sym_modes; /**< Pointer to array. */
+ unsigned hash_algorithm_count; /**< Number of hash_algorithms. */
+ fsl_shw_hash_alg_t* hash_algorithms; /**< Pointer to array */
+ uint8_t sym_support[5][4]; /**< indexed by key alg then mode */
+
+ int scc_driver_major;
+ int scc_driver_minor;
+ int scm_version; /**< Version from SCM Configuration register */
+ int smn_version; /**< Version from SMN Status register */
+ int block_size_bytes; /**< Number of bytes per block of RAM; also
+ block size of the crypto algorithm. */
+ union {
+ struct scc_info {
+ int black_ram_size_blocks; /**< Number of blocks of Black RAM */
+ int red_ram_size_blocks; /**< Number of blocks of Red RAM */
+ } scc_info;
+ struct scc2_info {
+ int partition_size_bytes; /**< Number of bytes in each partition */
+ int partition_count; /**< Number of partitions on this platform */
+ } scc2_info;
+ } u;
+} fsl_shw_pco_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-HCO-001 */
+/**
+ * Hash Context Object
+ */
+typedef struct fsl_shw_hco_t /* fsl_shw_hash_context_object */
+{
+ fsl_shw_hash_alg_t algorithm; /**< Algorithm for this context. */
+ uint32_t flags; /**< Flags from
+ #fsl_shw_hash_ctx_flags_t. */
+ uint8_t digest_length; /**< hash result length in bytes */
+ uint8_t context_length; /**< Context length in bytes */
+ uint8_t context_register_length; /**< in bytes */
+ uint32_t context[9]; /**< largest digest + msg size */
+} fsl_shw_hco_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-HCO-001 */
+/**
+ * HMAC Context Object
+ */
+typedef struct fsl_shw_hmco_t /* fsl_shw_hmac_context_object */
+{
+ fsl_shw_hash_alg_t algorithm; /**< Hash algorithm for the HMAC. */
+ uint32_t flags; /**< Flags from
+ #fsl_shw_hmac_ctx_flags_t. */
+ uint8_t digest_length; /**< in bytes */
+ uint8_t context_length; /**< in bytes */
+ uint8_t context_register_length; /**< in bytes */
+ uint32_t ongoing_context[9]; /**< largest digest + msg
+ size */
+ uint32_t inner_precompute[9]; /**< largest digest + msg
+ size */
+ uint32_t outer_precompute[9]; /**< largest digest + msg
+ size */
+} fsl_shw_hmco_t;
+
+
+/* REQ-FSLSHW-PINTFC-COA-SCCO-001 */
+/**
+ * Symmetric Crypto Context Object Context Object
+ */
+typedef struct fsl_shw_scco_t
+{
+ uint32_t flags; /**< Flags from #fsl_shw_sym_ctx_flags_t. */
+ unsigned block_size_bytes; /**< Both block and ctx size */
+ fsl_shw_sym_mode_t mode; /**< Symmetric mode for this context. */
+ /* Could put modulus plus 16-octet context in union with arc4
+ sbox+ptrs... */
+ fsl_shw_ctr_mod_t modulus_exp; /**< Exponent value for CTR modulus */
+ uint8_t context[8]; /**< Stored context. Large enough
+ for 3DES. */
+} fsl_shw_scco_t;
+
+
+/**
+ * Authenticate-Cipher Context Object
+
+ * An object for controlling the function of, and holding information about,
+ * data for the authenticate-cipher functions, #fsl_shw_gen_encrypt() and
+ * #fsl_shw_auth_decrypt().
+ */
+typedef struct fsl_shw_acco_t
+{
+ uint32_t flags; /**< See #fsl_shw_auth_ctx_flags_t for
+ meanings */
+ fsl_shw_acc_mode_t mode; /**< CCM only */
+ uint8_t mac_length; /**< User's value for length */
+ unsigned q_length; /**< NIST parameter - */
+ fsl_shw_scco_t cipher_ctx_info; /**< For running
+ encrypt/decrypt. */
+ union {
+ fsl_shw_scco_t CCM_ctx_info; /**< For running the CBC in
+ AES-CCM. */
+ fsl_shw_hco_t hash_ctx_info; /**< For running the hash */
+ } auth_info; /**< "auth" info struct */
+ uint8_t unencrypted_mac[16]; /**< max block size... */
+} fsl_shw_acco_t;
+
+
+/**
+ * Common header in request structures between User-mode API and SHW driver.
+ */
+struct shw_req_header {
+ uint32_t flags; /**< Flags - from user-mode context. */
+ uint32_t user_ref; /**< Reference - from user-mode context. */
+ fsl_shw_return_t code; /**< Result code for operation. */
+};
+
+/**
+ * Used by user-mode API to retrieve completed non-blocking results in
+ * SHW_USER_REQ_GET_RESULTS ioctl().
+ */
+struct results_req {
+ struct shw_req_header hdr; /**< Boilerplate. */
+ unsigned requested; /**< number of results requested, */
+ unsigned actual; /**< number of results obtained. */
+ fsl_shw_result_t *results; /**< pointer to memory to hold results. */
+};
+
+
+/**
+ * Used by user-mode API to retrieve hardware capabilities in
+ * SHW_USER_REQ_GET_CAPABILITIES ioctl().
+ */
+struct capabilities_req {
+ struct shw_req_header hdr; /**< Boilerplate. */
+ unsigned size; /**< Size, in bytes, capabilities. */
+ fsl_shw_pco_t* capabilities; /**< Place to copy out the info. */
+};
+
+
+/**
+ * Used by user-mode API to get a random number
+ */
+struct get_random_req {
+ struct shw_req_header hdr; /**< Boilerplate. */
+ unsigned size; /**< Size, in bytes, of random. */
+ uint8_t* random; /**< Place to copy out the random number. */
+};
+
+
+/**
+ * Used by API to add entropy to a random number generator
+ */
+struct add_entropy_req {
+ struct shw_req_header hdr; /**< Boilerplate. */
+ unsigned size; /**< Size, in bytes, of entropy. */
+ uint8_t* entropy; /**< Location of the entropy to be added. */
+};
+
+
+/******************************************************************************
+ * External variables
+ *****************************************************************************/
+#ifdef __KERNEL__
+extern os_lock_t shw_queue_lock;
+
+extern fsl_shw_uco_t* user_list;
+#endif
+
+
+/******************************************************************************
+ * Access Macros for Objects
+ *****************************************************************************/
+/**
+ * Get FSL SHW API version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the API is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the API is to be stored.
+ */
+#define fsl_shw_pco_get_version(pcobject, pcmajor, pcminor) \
+do { \
+ *(pcmajor) = (pcobject)->api_major; \
+ *(pcminor) = (pcobject)->api_minor; \
+} while (0)
+
+
+/**
+ * Get underlying driver version.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the driver is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the driver is to be stored.
+ */
+#define fsl_shw_pco_get_driver_version(pcobject, pcmajor, pcminor) \
+do { \
+ *(pcmajor) = (pcobject)->driver_major; \
+ *(pcminor) = (pcobject)->driver_minor; \
+} while (0)
+
+
+/**
+ * Get list of symmetric algorithms supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcalgorithms A pointer to where to store the location of
+ * the list of algorithms.
+ * @param[out] pcacount A pointer to where to store the number of
+ * algorithms in the list at @a algorithms.
+ */
+#define fsl_shw_pco_get_sym_algorithms(pcobject, pcalgorithms, pcacount) \
+do { \
+ *(pcalgorithms) = (pcobject)->sym_algorithms; \
+ *(pcacount) = (pcobject)->sym_algorithm_count; \
+} while (0)
+
+
+/**
+ * Get list of symmetric modes supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] gsmodes A pointer to where to store the location of
+ * the list of modes.
+ * @param[out] gsacount A pointer to where to store the number of
+ * algorithms in the list at @a modes.
+ */
+#define fsl_shw_pco_get_sym_modes(pcobject, gsmodes, gsacount) \
+do { \
+ *(gsmodes) = (pcobject)->sym_modes; \
+ *(gsacount) = (pcobject)->sym_mode_count; \
+} while (0)
+
+
+/**
+ * Get list of hash algorithms supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] gsalgorithms A pointer which will be set to the list of
+ * algorithms.
+ * @param[out] gsacount The number of algorithms in the list at @a
+ * algorithms.
+ */
+#define fsl_shw_pco_get_hash_algorithms(pcobject, gsalgorithms, gsacount) \
+do { \
+ *(gsalgorithms) = (pcobject)->hash_algorithms; \
+ *(gsacount) = (pcobject)->hash_algorithm_count; \
+} while (0)
+
+
+/**
+ * Determine whether the combination of a given symmetric algorithm and a given
+ * mode is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param pcalg A Symmetric Cipher algorithm.
+ * @param pcmode A Symmetric Cipher mode.
+ *
+ * @return 0 if combination is not supported, non-zero if supported.
+ */
+#if defined(FSL_HAVE_DRYICE) && defined(__KERNEL__)
+#define fsl_shw_pco_check_sym_supported(pcobject, pcalg, pcmode) \
+ ((pcobject)->sym_support[pcalg][pcmode])
+#else
+#define fsl_shw_pco_check_sym_supported(pcobject, pcalg, pcmode) \
+ 0
+#endif
+
+/**
+ * Determine whether a given Encryption-Authentication mode is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param pcmode The Authentication mode.
+ *
+ * @return 0 if mode is not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_auth_supported(pcobject, pcmode) \
+ 0
+
+
+/**
+ * Determine whether Black Keys (key establishment / wrapping) is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ *
+ * @return 0 if wrapping is not supported, non-zero if supported.
+ */
+#if defined(FSL_HAVE_DRYICE) && defined(__KERNEL__)
+#define fsl_shw_pco_check_black_key_supported(pcobject) \
+ 1
+#else
+#define fsl_shw_pco_check_black_key_supported(pcobject) \
+ 0
+
+#endif
+
+/*!
+ * Determine whether Programmed Key features are available
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ *
+ * @return 1 if Programmed Key features are available, otherwise zero.
+ */
+#if defined(FSL_HAVE_DRYICE) && defined(__KERNEL__)
+#define fsl_shw_pco_check_pk_supported(pcobject) \
+ 1
+#else
+#define fsl_shw_pco_check_pk_supported(pcobject) \
+ 0
+#endif
+
+/*!
+ * Determine whether Software Key features are available
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 1 if Software key features are available, otherwise zero.
+ */
+#if defined(FSL_HAVE_DRYICE) && defined(__KERNEL__)
+#define fsl_shw_pco_check_sw_keys_supported(pcobject) \
+ 1
+#else
+#define fsl_shw_pco_check_sw_keys_supported(pcobject) \
+ 0
+#endif
+
+/*!
+ * Get FSL SHW SCC driver version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the SCC driver is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the SCC driver is to be stored.
+ */
+#define fsl_shw_pco_get_scc_driver_version(pcobject, pcmajor, pcminor) \
+{ \
+ *(pcmajor) = (pcobject)->scc_driver_major; \
+ *(pcminor) = (pcobject)->scc_driver_minor; \
+}
+
+
+/**
+ * Get SCM hardware version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @return The SCM hardware version
+ */
+#define fsl_shw_pco_get_scm_version(pcobject) \
+ ((pcobject)->scm_version)
+
+
+/**
+ * Get SMN hardware version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @return The SMN hardware version
+ */
+#define fsl_shw_pco_get_smn_version(pcobject) \
+ ((pcobject)->smn_version)
+
+
+/**
+ * Get the size of an SCM block, in bytes
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @return The size of an SCM block, in bytes.
+ */
+#define fsl_shw_pco_get_scm_block_size(pcobject) \
+ ((pcobject)->block_size_bytes)
+
+
+/**
+ * Get size of Black and Red RAM memory
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] black_size A pointer to where the size of the Black RAM, in
+ * blocks, is to be placed.
+ * @param[out] red_size A pointer to where the size of the Red RAM, in
+ * blocks, is to be placed.
+ */
+#define fsl_shw_pco_get_smn_size(pcobject, black_size, red_size) \
+{ \
+ if ((pcobject)->scm_version == 1) { \
+ *(black_size) = (pcobject)->u.scc_info.black_ram_size_blocks; \
+ *(red_size) = (pcobject)->u.scc_info.red_ram_size_blocks; \
+ } else { \
+ *(black_size) = 0; \
+ *(red_size) = 0; \
+ } \
+}
+
+
+/**
+ * Determine whether Secure Partitions are supported
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ *
+ * @return 0 if secure partitions are not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_spo_supported(pcobject) \
+ ((pcobject)->scm_version == 2)
+
+
+/**
+ * Get the size of a Secure Partitions
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ *
+ * @return Partition size, in bytes. 0 if Secure Partitions not supported.
+ */
+#define fsl_shw_pco_get_spo_size_bytes(pcobject) \
+ (((pcobject)->scm_version == 2) ? \
+ ((pcobject)->u.scc2_info.partition_size_bytes) : 0 ) \
+
+
+/**
+ * Get the number of Secure Partitions on this platform
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ *
+ * @return Number of partitions. 0 if Secure Paritions not supported. Note
+ * that this returns the total number of partitions, not all may be
+ * available to the user.
+ */
+#define fsl_shw_pco_get_spo_count(pcobject) \
+ (((pcobject)->scm_version == 2) ? \
+ ((pcobject)->u.scc2_info.partition_count) : 0 ) \
+
+
+/*!
+ * Initialize a Secret Key Object.
+ *
+ * This function must be called before performing any other operation with
+ * the Object.
+ *
+ * @param skobject The Secret Key Object to be initialized.
+ * @param skalgorithm DES, AES, etc.
+ *
+ */
+#define fsl_shw_sko_init(skobject,skalgorithm) \
+{ \
+ fsl_shw_sko_t* skop = skobject; \
+ \
+ skop->algorithm = skalgorithm; \
+ skop->flags = 0; \
+ skop->keystore = NULL; \
+ skop->pf_key = FSL_SHW_PF_KEY_PRG; \
+}
+
+/*!
+ * Initialize a Secret Key Object to use a Platform Key register.
+ *
+ * This function must be called before performing any other operation with
+ * the Object.
+ *
+ * @param skobject The Secret Key Object to be initialized.
+ * @param skalgorithm DES, AES, etc.
+ * @param skhwkey one of the fsl_shw_pf_key_t values.
+ *
+ */
+#define fsl_shw_sko_init_pf_key(skobject,skalgorithm,skhwkey) \
+{ \
+ fsl_shw_sko_t* skop = skobject; \
+ fsl_shw_key_alg_t alg = skalgorithm; \
+ fsl_shw_pf_key_t key = skhwkey; \
+ \
+ skop->algorithm = alg; \
+ if (alg == FSL_KEY_ALG_TDES) { \
+ skop->key_length = 21; \
+ } \
+ skop->keystore = NULL; \
+ skop->flags = FSL_SKO_KEY_SELECT_PF_KEY; \
+ skop->pf_key = key; \
+ if ((key == FSL_SHW_PF_KEY_IIM) || (key == FSL_SHW_PF_KEY_PRG) \
+ || (key == FSL_SHW_PF_KEY_IIM_PRG) \
+ || (key == FSL_SHW_PF_KEY_IIM_RND) \
+ || (key == FSL_SHW_PF_KEY_RND)) { \
+ skop->flags |= FSL_SKO_KEY_ESTABLISHED; \
+ } \
+}
+
+/*!
+ * Store a cleartext key in the key object.
+ *
+ * This has the side effect of setting the #FSL_SKO_KEY_PRESENT flag and
+ * resetting the #FSL_SKO_KEY_ESTABLISHED flag.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skkey A pointer to the beginning of the key.
+ * @param skkeylen The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+#define fsl_shw_sko_set_key(skobject, skkey, skkeylen) \
+{ \
+ (skobject)->key_length = skkeylen; \
+ copy_bytes((skobject)->key, skkey, skkeylen); \
+ (skobject)->flags |= FSL_SKO_KEY_PRESENT; \
+ (skobject)->flags &= ~FSL_SKO_KEY_ESTABLISHED; \
+}
+
+/**
+ * Set a size for the key.
+ *
+ * This function would normally be used when the user wants the key to be
+ * generated from a random source.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skkeylen The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+#define fsl_shw_sko_set_key_length(skobject, skkeylen) \
+ (skobject)->key_length = skkeylen;
+
+
+/**
+ * Set the User ID associated with the key.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skuserid The User ID to identify authorized users of the key.
+ */
+#define fsl_shw_sko_set_user_id(skobject, skuserid) \
+ (skobject)->userid = (skuserid)
+
+/**
+ * Establish a user Keystore to hold the key.
+ */
+#define fsl_shw_sko_set_keystore(skobject, user_keystore) \
+ (skobject)->keystore = (user_keystore)
+
+
+
+/**
+ * Set the establish key handle into a key object.
+ *
+ * The @a userid field will be used to validate the access to the unwrapped
+ * key. This feature is not available for all platforms, nor for all
+ * algorithms and modes.
+ *
+ * The #FSL_SKO_KEY_ESTABLISHED will be set (and the #FSL_SKO_KEY_PRESENT flag
+ * will be cleared).
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skuserid The User ID to verify this user is an authorized user of
+ * the key.
+ * @param skhandle A @a handle from #fsl_shw_sko_get_established_info.
+ */
+#define fsl_shw_sko_set_established_info(skobject, skuserid, skhandle) \
+{ \
+ (skobject)->userid = (skuserid); \
+ (skobject)->handle = (skhandle); \
+ (skobject)->flags |= FSL_SKO_KEY_ESTABLISHED; \
+ (skobject)->flags &= \
+ ~(FSL_SKO_KEY_PRESENT); \
+}
+
+
+/**
+ * Retrieve the established-key handle from a key object.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skhandle The location to store the @a handle of the unwrapped
+ * key.
+ */
+#define fsl_shw_sko_get_established_info(skobject, skhandle) \
+ *(skhandle) = (skobject)->handle
+
+
+/**
+ * Extract the algorithm from a key object.
+ *
+ * @param skobject The Key Object to be queried.
+ * @param[out] skalgorithm A pointer to the location to store the algorithm.
+ */
+#define fsl_shw_sko_get_algorithm(skobject, skalgorithm) \
+ *(skalgorithm) = (skobject)->algorithm
+
+
+/**
+ * Retrieve the cleartext key from a key object that is stored in a user
+ * keystore.
+ *
+ * @param skobject The Key Object to be queried.
+ * @param[out] skkey A pointer to the location to store the key. NULL
+ * if the key is not stored in a user keystore.
+ */
+#define fsl_shw_sko_get_key(skobject, skkey) \
+{ \
+ fsl_shw_kso_t* keystore = (skobject)->keystore; \
+ if (keystore != NULL) { \
+ *(skkey) = keystore->slot_get_address(keystore->user_data, \
+ (skobject)->handle); \
+ } else { \
+ *(skkey) = NULL; \
+ } \
+}
+
+
+/*!
+ * Determine the size of a wrapped key based upon the cleartext key's length.
+ *
+ * This function can be used to calculate the number of octets that
+ * #fsl_shw_extract_key() will write into the location at @a covered_key.
+ *
+ * If zero is returned at @a length, this means that the key length in
+ * @a key_info is not supported.
+ *
+ * @param wkeyinfo Information about a key to be wrapped.
+ * @param wkeylen Location to store the length of a wrapped
+ * version of the key in @a key_info.
+ */
+#define fsl_shw_sko_calculate_wrapped_size(wkeyinfo, wkeylen) \
+{ \
+ register fsl_shw_sko_t* kp = wkeyinfo; \
+ register uint32_t kl = kp->key_length; \
+ int key_blocks; \
+ int base_size = 35; /* ICV + T' + ALG + LEN + FLAGS */ \
+ \
+ if (kp->flags & FSL_SKO_KEY_SELECT_PF_KEY) { \
+ kl = 21; /* 168-bit 3DES key */ \
+ } \
+ key_blocks = (kl + 7) / 8; \
+ /* Round length up to 3DES block size for CBC mode */ \
+ *(wkeylen) = base_size + 8 * key_blocks; \
+}
+
+/*!
+ * Set some flags in the key object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skflags (One or more) ORed members of #fsl_shw_key_flags_t which
+ * are to be set.
+ */
+#define fsl_shw_sko_set_flags(skobject, skflags) \
+ (skobject)->flags |= (skflags)
+
+
+/**
+ * Clear some flags in the key object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skflags (One or more) ORed members of #fsl_shw_key_flags_t
+ * which are to be reset.
+ */
+#define fsl_shw_sko_clear_flags(skobject, skflags) \
+ (skobject)->flags &= ~(skflags)
+
+/**
+ * Initialize a User Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the User Context Object to initial values, and set the size
+ * of the results pool. The mode will be set to a default of
+ * #FSL_UCO_BLOCKING_MODE.
+ *
+ * When using non-blocking operations, this sets the maximum number of
+ * operations which can be outstanding. This number includes the counts of
+ * operations waiting to start, operation(s) being performed, and results which
+ * have not been retrieved.
+ *
+ * Changes to this value are ignored once user registration has completed. It
+ * should be set to 1 if only blocking operations will ever be performed.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param usize The maximum number of operations which can be
+ * outstanding.
+ */
+#ifdef __KERNEL__
+
+#define fsl_shw_uco_init(ucontext, usize) \
+do { \
+ fsl_shw_uco_t* uco = ucontext; \
+ \
+ (uco)->pool_size = usize; \
+ (uco)->flags = FSL_UCO_BLOCKING_MODE | FSL_UCO_CONTEXT_CHANGED; \
+ (uco)->openfd = -1; \
+ (uco)->callback = NULL; \
+ (uco)->partition = NULL; \
+ (uco)->wrap_key = FSL_SHW_PF_KEY_IIM; \
+} while (0)
+
+#else /* __KERNEL__ */
+
+#define fsl_shw_uco_init(ucontext, usize) \
+do { \
+ fsl_shw_uco_t* uco = ucontext; \
+ \
+ (uco)->pool_size = usize; \
+ (uco)->flags = FSL_UCO_BLOCKING_MODE | FSL_UCO_CONTEXT_CHANGED; \
+ (uco)->openfd = -1; \
+ (uco)->callback = NULL; \
+ (uco)->wrap_key = FSL_SHW_PF_KEY_IIM; \
+} while (0)
+
+#endif /* __KERNEL__ */
+
+
+/**
+ * Set the User Reference for the User Context.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uref A value which will be passed back with a result.
+ */
+#define fsl_shw_uco_set_reference(ucontext, uref) \
+do { \
+ fsl_shw_uco_t* uco = ucontext; \
+ \
+ (uco)->user_ref = uref; \
+ (uco)->flags |= FSL_UCO_CONTEXT_CHANGED; \
+} while (0)
+
+
+/**
+ * Set the User Reference for the User Context.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param ucallback The function the API will invoke when an operation
+ * completes.
+ */
+#define fsl_shw_uco_set_callback(ucontext, ucallback) \
+do { \
+ fsl_shw_uco_t* uco = ucontext; \
+ \
+ (uco)->callback = ucallback; \
+ (uco)->flags |= FSL_UCO_CONTEXT_CHANGED; \
+} while (0)
+
+/**
+ * Set flags in the User Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uflags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+#define fsl_shw_uco_set_flags(ucontext, uflags) \
+ (ucontext)->flags |= (uflags) | FSL_UCO_CONTEXT_CHANGED
+
+
+/**
+ * Clear flags in the User Context.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uflags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+#define fsl_shw_uco_clear_flags(ucontext, uflags) \
+do { \
+ fsl_shw_uco_t* uco = ucontext; \
+ \
+ (uco)->flags &= ~(uflags); \
+ (uco)->flags |= FSL_UCO_CONTEXT_CHANGED; \
+} while (0)
+
+
+/**
+ * Retrieve the reference value from a Result Object.
+ *
+ * @param robject The result object to query.
+ *
+ * @return The reference associated with the request.
+ */
+#define fsl_shw_ro_get_reference(robject) \
+ (robject)->user_ref
+
+
+/**
+ * Retrieve the status code from a Result Object.
+ *
+ * @param robject The result object to query.
+ *
+ * @return The status of the request.
+ */
+#define fsl_shw_ro_get_status(robject) \
+ (robject)->code
+
+
+
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HASH-004 */
+/**
+ * Initialize a Hash Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the hash
+ * context object.
+ *
+ * @param hcobject The hash context to operate upon.
+ * @param hcalgorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+#define fsl_shw_hco_init(hcobject, hcalgorithm) \
+do { \
+ (hcobject)->algorithm = hcalgorithm; \
+ (hcobject)->flags = 0; \
+ switch (hcalgorithm) { \
+ case FSL_HASH_ALG_MD5: \
+ (hcobject)->digest_length = 16; \
+ (hcobject)->context_length = 16; \
+ (hcobject)->context_register_length = 24; \
+ break; \
+ case FSL_HASH_ALG_SHA1: \
+ (hcobject)->digest_length = 20; \
+ (hcobject)->context_length = 20; \
+ (hcobject)->context_register_length = 24; \
+ break; \
+ case FSL_HASH_ALG_SHA224: \
+ (hcobject)->digest_length = 28; \
+ (hcobject)->context_length = 32; \
+ (hcobject)->context_register_length = 36; \
+ break; \
+ case FSL_HASH_ALG_SHA256: \
+ (hcobject)->digest_length = 32; \
+ (hcobject)->context_length = 32; \
+ (hcobject)->context_register_length = 36; \
+ break; \
+ default: \
+ /* error ! */ \
+ (hcobject)->digest_length = 1; \
+ (hcobject)->context_length = 1; \
+ (hcobject)->context_register_length = 1; \
+ break; \
+ } \
+} while (0)
+
+
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HASH-001 */
+/**
+ * Get the current hash value and message length from the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hcobject The hash context to query.
+ * @param[out] hccontext Pointer to the location of @a length octets where to
+ * store a copy of the current value of the digest.
+ * @param hcclength Number of octets of hash value to copy.
+ * @param[out] hcmsglen Pointer to the location to store the number of octets
+ * already hashed.
+ */
+#define fsl_shw_hco_get_digest(hcobject, hccontext, hcclength, hcmsglen) \
+do { \
+ memcpy(hccontext, (hcobject)->context, hcclength); \
+ if ((hcobject)->algorithm == FSL_HASH_ALG_SHA224 \
+ || (hcobject)->algorithm == FSL_HASH_ALG_SHA256) { \
+ *(hcmsglen) = (hcobject)->context[8]; \
+ } else { \
+ *(hcmsglen) = (hcobject)->context[5]; \
+ } \
+} while (0)
+
+
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HASH-002 */
+/**
+ * Get the hash algorithm from the hash context object.
+ *
+ * @param hcobject The hash context to query.
+ * @param[out] hcalgorithm Pointer to where the algorithm is to be stored.
+ */
+#define fsl_shw_hco_get_info(hcobject, hcalgorithm) \
+do { \
+ *(hcalgorithm) = (hcobject)->algorithm; \
+} while (0)
+
+
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HASH-003 */
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HASH-004 */
+/**
+ * Set the current hash value and message length in the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hcobject The hash context to operate upon.
+ * @param hccontext Pointer to buffer of appropriate length to copy into
+ * the hash context object.
+ * @param hcmsglen The number of octets of the message which have
+ * already been hashed.
+ *
+ */
+#define fsl_shw_hco_set_digest(hcobject, hccontext, hcmsglen) \
+do { \
+ memcpy((hcobject)->context, hccontext, (hcobject)->context_length); \
+ if (((hcobject)->algorithm == FSL_HASH_ALG_SHA224) \
+ || ((hcobject)->algorithm == FSL_HASH_ALG_SHA256)) { \
+ (hcobject)->context[8] = hcmsglen; \
+ } else { \
+ (hcobject)->context[5] = hcmsglen; \
+ } \
+} while (0)
+
+
+/**
+ * Set flags in a Hash Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The hash context to be operated on.
+ * @param hcflags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+#define fsl_shw_hco_set_flags(hcobject, hcflags) \
+ (hcobject)->flags |= (hcflags)
+
+
+/**
+ * Clear flags in a Hash Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The hash context to be operated on.
+ * @param hcflags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+#define fsl_shw_hco_clear_flags(hcobject, hcflags) \
+ (hcobject)->flags &= ~(hcflags)
+
+
+/**
+ * Initialize an HMAC Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the HMAC
+ * context object.
+ *
+ * @param hcobject The HMAC context to operate upon.
+ * @param hcalgorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+#define fsl_shw_hmco_init(hcobject, hcalgorithm) \
+ fsl_shw_hco_init(hcobject, hcalgorithm)
+
+
+/**
+ * Set flags in an HMAC Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The HMAC context to be operated on.
+ * @param hcflags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+#define fsl_shw_hmco_set_flags(hcobject, hcflags) \
+ (hcobject)->flags |= (hcflags)
+
+
+/**
+ * Clear flags in an HMAC Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The HMAC context to be operated on.
+ * @param hcflags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+#define fsl_shw_hmco_clear_flags(hcobject, hcflags) \
+ (hcobject)->flags &= ~(hcflags)
+
+
+/**
+ * Initialize a Symmetric Cipher Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. This will set the @a mode and @a algorithm and initialize the
+ * Object.
+ *
+ * @param scobject The context object to operate on.
+ * @param scalg The cipher algorithm this context will be used with.
+ * @param scmode #FSL_SYM_MODE_CBC, #FSL_SYM_MODE_ECB, etc.
+ *
+ */
+#define fsl_shw_scco_init(scobject, scalg, scmode) \
+do { \
+ register uint32_t bsb; /* block-size bytes */ \
+ \
+ switch (scalg) { \
+ case FSL_KEY_ALG_AES: \
+ bsb = 16; \
+ break; \
+ case FSL_KEY_ALG_DES: \
+ /* fall through */ \
+ case FSL_KEY_ALG_TDES: \
+ bsb = 8; \
+ break; \
+ case FSL_KEY_ALG_ARC4: \
+ bsb = 259; \
+ break; \
+ case FSL_KEY_ALG_HMAC: \
+ bsb = 1; /* meaningless */ \
+ break; \
+ default: \
+ bsb = 00; \
+ } \
+ (scobject)->block_size_bytes = bsb; \
+ (scobject)->mode = scmode; \
+ (scobject)->flags = 0; \
+} while (0)
+
+
+/**
+ * Set the flags for a Symmetric Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param scobject The context object to operate on.
+ * @param scflags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_scco_set_flags(scobject, scflags) \
+ (scobject)->flags |= (scflags)
+
+
+/**
+ * Clear some flags in a Symmetric Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param scobject The context object to operate on.
+ * @param scflags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_scco_clear_flags(scobject, scflags) \
+ (scobject)->flags &= ~(scflags)
+
+
+/**
+ * Set the Context (IV) for a Symmetric Cipher Context.
+ *
+ * This is to set the context/IV for #FSL_SYM_MODE_CBC mode, or to set the
+ * context (the S-Box and pointers) for ARC4. The full context size will
+ * be copied.
+ *
+ * @param scobject The context object to operate on.
+ * @param sccontext A pointer to the buffer which contains the context.
+ *
+ */
+#define fsl_shw_scco_set_context(scobject, sccontext) \
+ memcpy((scobject)->context, sccontext, \
+ (scobject)->block_size_bytes)
+
+
+/**
+ * Get the Context for a Symmetric Cipher Context.
+ *
+ * This is to retrieve the context/IV for #FSL_SYM_MODE_CBC mode, or to
+ * retrieve context (the S-Box and pointers) for ARC4. The full context
+ * will be copied.
+ *
+ * @param scobject The context object to operate on.
+ * @param[out] sccontext Pointer to location where context will be stored.
+ */
+#define fsl_shw_scco_get_context(scobject, sccontext) \
+ memcpy(sccontext, (scobject)->context, (scobject)->block_size_bytes)
+
+
+/**
+ * Set the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will set the Counter Value for CTR mode.
+ *
+ * @param scobject The context object to operate on.
+ * @param sccounter The starting counter value. The number of octets.
+ * copied will be the block size for the algorithm.
+ * @param scmodulus The modulus for controlling the incrementing of the
+ * counter.
+ *
+ */
+#define fsl_shw_scco_set_counter_info(scobject, sccounter, scmodulus) \
+do { \
+ if ((sccounter) != NULL) { \
+ memcpy((scobject)->context, sccounter, \
+ (scobject)->block_size_bytes); \
+ } \
+ (scobject)->modulus_exp = scmodulus; \
+} while (0)
+
+
+/**
+ * Get the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will retrieve the Counter Value is for CTR mode.
+ *
+ * @param scobject The context object to query.
+ * @param[out] sccounter Pointer to location to store the current counter
+ * value. The number of octets copied will be the
+ * block size for the algorithm.
+ * @param[out] scmodulus Pointer to location to store the modulus.
+ *
+ */
+#define fsl_shw_scco_get_counter_info(scobject, sccounter, scmodulus) \
+do { \
+ if ((sccounter) != NULL) { \
+ memcpy(sccounter, (scobject)->context, \
+ (scobject)->block_size_bytes); \
+ } \
+ if ((scmodulus) != NULL) { \
+ *(scmodulus) = (scobject)->modulus_exp; \
+ } \
+} while (0)
+
+
+/**
+ * Initialize a Authentication-Cipher Context.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acmode The mode for this object (only #FSL_ACC_MODE_CCM
+ * supported).
+ */
+#define fsl_shw_acco_init(acobject, acmode) \
+do { \
+ (acobject)->flags = 0; \
+ (acobject)->mode = (acmode); \
+} while (0)
+
+
+/**
+ * Set the flags for a Authentication-Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acflags The flags to set (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_acco_set_flags(acobject, acflags) \
+ (acobject)->flags |= (acflags)
+
+
+/**
+ * Clear some flags in a Authentication-Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acflags The flags to reset (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_acco_clear_flags(acobject, acflags) \
+ (acobject)->flags &= ~(acflags)
+
+
+/**
+ * Set up the Authentication-Cipher Object for CCM mode.
+ *
+ * This will set the @a auth_object for CCM mode and save the @a ctr,
+ * and @a mac_length. This function can be called instead of
+ * #fsl_shw_acco_init().
+ *
+ * The paramater @a ctr is Counter Block 0, (counter value 0), which is for the
+ * MAC.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acalg Cipher algorithm. Only AES is supported.
+ * @param accounter The initial counter value.
+ * @param acmaclen The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ */
+/* Do we need to stash the +1 value of the CTR somewhere? */
+#define fsl_shw_acco_set_ccm(acobject, acalg, accounter, acmaclen) \
+ do { \
+ (acobject)->flags = 0; \
+ (acobject)->mode = FSL_ACC_MODE_CCM; \
+ (acobject)->auth_info.CCM_ctx_info.block_size_bytes = 16; \
+ (acobject)->cipher_ctx_info.block_size_bytes = 16; \
+ (acobject)->mac_length = acmaclen; \
+ fsl_shw_scco_set_counter_info(&(acobject)->cipher_ctx_info, accounter, \
+ FSL_CTR_MOD_128); \
+} while (0)
+
+
+/**
+ * Format the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will also set the IV and CTR values per Appendix A of NIST
+ * Special Publication 800-38C (May 2004). It will also perform the
+ * #fsl_shw_acco_set_ccm() operation with information derived from this set of
+ * parameters.
+ *
+ * Note this function assumes the algorithm is AES. It initializes the
+ * @a auth_object by setting the mode to #FSL_ACC_MODE_CCM and setting the
+ * flags to be #FSL_ACCO_NIST_CCM.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param act The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ * @param acad Number of octets of Associated Data (may be zero).
+ * @param acq A value for the size of the length of @a q field. Valid
+ * values are 1-8.
+ * @param acN The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param acQ The value of Q (size of the payload in octets).
+ *
+ */
+#define fsl_shw_ccm_nist_format_ctr_and_iv(acobject, act, acad, acq, acN, acQ)\
+ do { \
+ uint64_t Q = acQ; \
+ uint8_t bflag = ((acad)?0x40:0) | ((((act)-2)/2)<<3) | ((acq)-1); \
+ unsigned i; \
+ uint8_t* qptr = (acobject)->auth_info.CCM_ctx_info.context + 15; \
+ (acobject)->auth_info.CCM_ctx_info.block_size_bytes = 16; \
+ (acobject)->cipher_ctx_info.block_size_bytes = 16; \
+ (acobject)->mode = FSL_ACC_MODE_CCM; \
+ (acobject)->flags = FSL_ACCO_NIST_CCM; \
+ \
+ /* Store away the MAC length (after calculating actual value */ \
+ (acobject)->mac_length = (act); \
+ /* Set Flag field in Block 0 */ \
+ *((acobject)->auth_info.CCM_ctx_info.context) = bflag; \
+ /* Set Nonce field in Block 0 */ \
+ memcpy((acobject)->auth_info.CCM_ctx_info.context+1, acN, \
+ 15-(acq)); \
+ /* Set Flag field in ctr */ \
+ *((acobject)->cipher_ctx_info.context) = (acq)-1; \
+ /* Update the Q (payload length) field of Block0 */ \
+ (acobject)->q_length = acq; \
+ for (i = 0; i < (acq); i++) { \
+ *qptr-- = Q & 0xFF; \
+ Q >>= 8; \
+ } \
+ /* Set the Nonce field of the ctr */ \
+ memcpy((acobject)->cipher_ctx_info.context+1, acN, 15-(acq)); \
+ /* Clear the block counter field of the ctr */ \
+ memset((acobject)->cipher_ctx_info.context+16-(acq), 0, (acq)+1); \
+ } while (0)
+
+
+/**
+ * Update the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will set the IV and CTR values per Appendix A of NIST Special
+ * Publication 800-38C (May 2004).
+ *
+ * Note this function assumes that #fsl_shw_ccm_nist_format_ctr_and_iv() has
+ * previously been called on the @a auth_object.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acN The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param acQ The value of Q (size of the payload in octets).
+ *
+ */
+/* Do we need to stash the +1 value of the CTR somewhere? */
+#define fsl_shw_ccm_nist_update_ctr_and_iv(acobject, acN, acQ) \
+ do { \
+ uint64_t Q = acQ; \
+ unsigned i; \
+ uint8_t* qptr = (acobject)->auth_info.CCM_ctx_info.context + 15; \
+ \
+ /* Update the Nonce field field of Block0 */ \
+ memcpy((acobject)->auth_info.CCM_ctx_info.context+1, acN, \
+ 15 - (acobject)->q_length); \
+ /* Update the Q (payload length) field of Block0 */ \
+ for (i = 0; i < (acobject)->q_length; i++) { \
+ *qptr-- = Q & 0xFF; \
+ Q >>= 8; \
+ } \
+ /* Update the Nonce field of the ctr */ \
+ memcpy((acobject)->cipher_ctx_info.context+1, acN, \
+ 15 - (acobject)->q_length); \
+ } while (0)
+
+
+/******************************************************************************
+ * Library functions
+ *****************************************************************************/
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-GEN-003 */
+/**
+ * Determine the hardware security capabilities of this platform.
+ *
+ * Though a user context object is passed into this function, it will always
+ * act in a non-blocking manner.
+ *
+ * @param user_ctx The user context which will be used for the query.
+ *
+ * @return A pointer to the capabilities object.
+ */
+extern fsl_shw_pco_t* fsl_shw_get_capabilities(fsl_shw_uco_t* user_ctx);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-GEN-004 */
+/**
+ * Create an association between the the user and the provider of the API.
+ *
+ * @param user_ctx The user context which will be used for this association.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_register_user(fsl_shw_uco_t* user_ctx);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-GEN-005 */
+/**
+ * Destroy the association between the the user and the provider of the API.
+ *
+ * @param user_ctx The user context which is no longer needed.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_deregister_user(fsl_shw_uco_t* user_ctx);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-GEN-006 */
+/**
+ * Retrieve results from earlier operations.
+ *
+ * @param user_ctx The user's context.
+ * @param result_size The number of array elements of @a results.
+ * @param[in,out] results Pointer to first of the (array of) locations to
+ * store results.
+ * @param[out] result_count Pointer to store the number of results which
+ * were returned.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t* user_ctx,
+ unsigned result_size,
+ fsl_shw_result_t results[],
+ unsigned* result_count);
+
+/**
+ * Place a key into a protected location for use only by cryptographic
+ * algorithms.
+ *
+ * This only needs to be used to a) unwrap a key, or b) set up a key which
+ * could be wrapped with a later call to #fsl_shw_extract_key(). Normal
+ * cleartext keys can simply be placed into #fsl_shw_sko_t key objects with
+ * #fsl_shw_sko_set_key() and used directly.
+ *
+ * The maximum key size supported for wrapped/unwrapped keys is 32 octets.
+ * (This is the maximum reasonable key length on Sahara - 32 octets for an HMAC
+ * key based on SHA-256.) The key size is determined by the @a key_info. The
+ * expected length of @a key can be determined by
+ * #fsl_shw_sko_calculate_wrapped_size()
+ *
+ * The protected key will not be available for use until this operation
+ * successfully completes.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be established. In the create case, the key
+ * length must be set.
+ * @param establish_type How @a key will be interpreted to establish a
+ * key for use.
+ * @param key If @a establish_type is #FSL_KEY_WRAP_UNWRAP,
+ * this is the location of a wrapped key. If
+ * @a establish_type is #FSL_KEY_WRAP_CREATE, this
+ * parameter can be @a NULL. If @a establish_type
+ * is #FSL_KEY_WRAP_ACCEPT, this is the location
+ * of a plaintext key.
+ */
+extern fsl_shw_return_t fsl_shw_establish_key(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t* key);
+
+
+/**
+ * Wrap a key and retrieve the wrapped value.
+ *
+ * A wrapped key is a key that has been cryptographically obscured. It is
+ * only able to be used with #fsl_shw_establish_key().
+ *
+ * This function will also release the key (see #fsl_shw_release_key()) so
+ * that it must be re-established before reuse.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ * @param[out] covered_key The location to store the wrapped key.
+ * (This size is based upon the maximum key size
+ * of 32 octets).
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ uint8_t* covered_key);
+
+/*!
+ * Read the key value from a key object.
+ *
+ * Only a key marked as a software key (#FSL_SKO_KEY_SW_KEY) can be read with
+ * this call. It has no effect on the status of the key store.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The referenced key.
+ * @param[out] key The location to store the key value.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * key);
+
+/**
+ * De-establish a key so that it can no longer be accessed.
+ *
+ * The key will need to be re-established before it can again be used.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_release_key(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info);
+
+
+/*
+ * In userspace, partition assignments will be tracked using the user context.
+ * In kernel mode, partition assignments are based on address only.
+ */
+
+/**
+ * Allocate a block of secure memory
+ *
+ * @param user_ctx User context
+ * @param size Memory size (octets). Note: currently only
+ * supports only single-partition sized blocks.
+ * @param UMID User Mode ID to use when registering the
+ * partition.
+ * @param permissions Permissions to initialize the partition with.
+ * Can be made by ORing flags from the
+ * #fsl_shw_permission_t.
+ *
+ * @return Address of the allocated memory. NULL if the
+ * call was not successful.
+ */
+extern void *fsl_shw_smalloc(fsl_shw_uco_t* user_ctx,
+ uint32_t size,
+ const uint8_t* UMID,
+ uint32_t permissions);
+
+
+/**
+ * Free a block of secure memory that was allocated with #fsl_shw_smalloc
+ *
+ * @param user_ctx User context
+ * @param address Address of the block of secure memory to be
+ * released.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_sfree(
+ fsl_shw_uco_t* user_ctx,
+ void* address);
+
+
+/**
+ * Check the status of a block of a secure memory that was allocated with
+ * #fsl_shw_smalloc
+ *
+ * @param user_ctx User context
+ * @param address Address of the block of secure memory to be
+ * released.
+ * @param status Status of the partition, of type
+ * #fsl_partition_status_t
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_sstatus(fsl_shw_uco_t* user_ctx,
+ void* address,
+ fsl_shw_partition_status_t* status);
+
+
+/**
+ * Diminish the permissions of a block of secure memory. Note that permissions
+ * can only be revoked.
+ *
+ * @param user_ctx User context
+ * @param address Base address of the secure memory to work with
+ * @param permissions Permissions to initialize the partition with.
+ * Can be made by ORing flags from the
+ * #fsl_shw_permission_t.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_diminish_perms(
+ fsl_shw_uco_t* user_ctx,
+ void* address,
+ uint32_t permissions);
+
+extern fsl_shw_return_t do_scc_engage_partition(
+ fsl_shw_uco_t* user_ctx,
+ void* address,
+ const uint8_t* UMID,
+ uint32_t permissions);
+
+extern fsl_shw_return_t do_system_keystore_slot_alloc(
+ fsl_shw_uco_t* user_ctx,
+ uint32_t key_lenth,
+ uint64_t ownerid,
+ uint32_t *slot);
+
+extern fsl_shw_return_t do_system_keystore_slot_dealloc(
+ fsl_shw_uco_t* user_ctx,
+ uint64_t ownerid,
+ uint32_t slot);
+
+extern fsl_shw_return_t do_system_keystore_slot_load(
+ fsl_shw_uco_t* user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ const uint8_t *key,
+ uint32_t key_length);
+
+extern fsl_shw_return_t do_system_keystore_slot_encrypt(
+ fsl_shw_uco_t* user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ uint8_t* black_data);
+
+extern fsl_shw_return_t do_system_keystore_slot_decrypt(
+ fsl_shw_uco_t* user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t* black_data);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-COA-SKO */
+/* REQ-FSL-SHW-PINTFC-COA-SCCO */
+/* REQ-FSLSHW-PINTFC-API-BASIC-SYM-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+/**
+ * Encrypt a stream of data with a symmetric-key algorithm.
+ *
+ * In ARC4, and also in #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_CTR modes, the
+ * flags of the @a sym_ctx object will control part of the operation of this
+ * function. The #FSL_SYM_CTX_INIT flag means that there is no context info in
+ * the object. The #FSL_SYM_CTX_LOAD means to use information in the
+ * @a sym_ctx at the start of the operation, and the #FSL_SYM_CTX_SAVE flag
+ * means to update the object's context information after the operation has
+ * been performed.
+ *
+ * All of the data for an operation can be run through at once using the
+ * #FSL_SYM_CTX_INIT or #FSL_SYM_CTX_LOAD flags, as appropriate, and then using
+ * a @a length for the whole of the data.
+ *
+ * If a #FSL_SYM_CTX_SAVE flag were added, an additional call to the function
+ * would "pick up" where the previous call left off, allowing the user to
+ * perform the larger function in smaller steps.
+ *
+ * In #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_ECB modes, the @a length must always
+ * be a multiple of the block size for the algorithm being used. For proper
+ * operation in #FSL_SYM_MODE_CTR mode, the @a length must be a multiple of the
+ * block size until the last operation on the total octet stream.
+ *
+ * Some users of ARC4 may want to compute the context (S-Box and pointers) from
+ * the key before any data is available. This may be done by running this
+ * function with a @a length of zero, with the init & save flags flags on in
+ * the @a sym_ctx. Subsequent operations would then run as normal with the
+ * load and save flags. Note that they key object is still required.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info Key and algorithm being used for this operation.
+ * @param[in,out] sym_ctx Info on cipher mode, state of the cipher.
+ * @param length Length, in octets, of the pt (and ct).
+ * @param pt pointer to plaintext to be encrypted.
+ * @param[out] ct pointer to where to store the resulting ciphertext.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ *
+ */
+extern fsl_shw_return_t fsl_shw_symmetric_encrypt(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ fsl_shw_scco_t* sym_ctx,
+ uint32_t length,
+ const uint8_t* pt,
+ uint8_t* ct);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-COA-SKO */
+/* REQ-FSL-SHW-PINTFC-COA-SCCO */
+/* PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+/**
+ * Decrypt a stream of data with a symmetric-key algorithm.
+ *
+ * In ARC4, and also in #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_CTR modes, the
+ * flags of the @a sym_ctx object will control part of the operation of this
+ * function. The #FSL_SYM_CTX_INIT flag means that there is no context info in
+ * the object. The #FSL_SYM_CTX_LOAD means to use information in the
+ * @a sym_ctx at the start of the operation, and the #FSL_SYM_CTX_SAVE flag
+ * means to update the object's context information after the operation has
+ * been performed.
+ *
+ * All of the data for an operation can be run through at once using the
+ * #FSL_SYM_CTX_INIT or #FSL_SYM_CTX_LOAD flags, as appropriate, and then using
+ * a @a length for the whole of the data.
+ *
+ * If a #FSL_SYM_CTX_SAVE flag were added, an additional call to the function
+ * would "pick up" where the previous call left off, allowing the user to
+ * perform the larger function in smaller steps.
+ *
+ * In #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_ECB modes, the @a length must always
+ * be a multiple of the block size for the algorithm being used. For proper
+ * operation in #FSL_SYM_MODE_CTR mode, the @a length must be a multiple of the
+ * block size until the last operation on the total octet stream.
+ *
+ * Some users of ARC4 may want to compute the context (S-Box and pointers) from
+ * the key before any data is available. This may be done by running this
+ * function with a @a length of zero, with the #FSL_SYM_CTX_INIT &
+ * #FSL_SYM_CTX_SAVE flags on in the @a sym_ctx. Subsequent operations would
+ * then run as normal with the load & save flags. Note that they key object is
+ * still required.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The key and algorithm being used in this operation.
+ * @param[in,out] sym_ctx Info on cipher mode, state of the cipher.
+ * @param length Length, in octets, of the ct (and pt).
+ * @param ct pointer to ciphertext to be decrypted.
+ * @param[out] pt pointer to where to store the resulting plaintext.
+ *
+ * @return A return code of type #fsl_shw_return_t
+ *
+ */
+extern fsl_shw_return_t fsl_shw_symmetric_decrypt(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ fsl_shw_scco_t* sym_ctx,
+ uint32_t length,
+ const uint8_t* ct,
+ uint8_t* pt);
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-COA-HCO */
+/* REQ-FSLSHW-PINTFC-API-BASIC-HASH-005 */
+/**
+ * Hash a stream of data with a cryptographic hash algorithm.
+ *
+ * The flags in the @a hash_ctx control the operation of this function.
+ *
+ * Hashing functions work on 64 octets of message at a time. Therefore, when
+ * any partial hashing of a long message is performed, the message @a length of
+ * each segment must be a multiple of 64. When ready to
+ * #FSL_HASH_FLAGS_FINALIZE the hash, the @a length may be any value.
+ *
+ * With the #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_FINALIZE flags on, a
+ * one-shot complete hash, including padding, will be performed. The @a length
+ * may be any value.
+ *
+ * The first octets of a data stream can be hashed by setting the
+ * #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_SAVE flags. The @a length must be
+ * a multiple of 64.
+ *
+ * The flag #FSL_HASH_FLAGS_LOAD is used to load a context previously saved by
+ * #FSL_HASH_FLAGS_SAVE. The two in combination will allow a (multiple-of-64
+ * octets) 'middle sequence' of the data stream to be hashed with the
+ * beginning. The @a length must again be a multiple of 64.
+ *
+ * Since the flag #FSL_HASH_FLAGS_LOAD is used to load a context previously
+ * saved by #FSL_HASH_FLAGS_SAVE, the #FSL_HASH_FLAGS_LOAD and
+ * #FSL_HASH_FLAGS_FINALIZE flags, used together, can be used to finish the
+ * stream. The @a length may be any value.
+ *
+ * If the user program wants to do the padding for the hash, it can leave off
+ * the #FSL_HASH_FLAGS_FINALIZE flag. The @a length must then be a multiple of
+ * 64 octets.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] hash_ctx Hashing algorithm and state of the cipher.
+ * @param msg Pointer to the data to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result If not null, pointer to where to store the hash
+ * digest.
+ * @param result_len Number of octets to store in @a result.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hash(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_hco_t* hash_ctx,
+ const uint8_t* msg,
+ uint32_t length,
+ uint8_t* result,
+ uint32_t result_len);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-API-BASIC-HMAC-001 */
+/**
+ * Precompute the Key hashes for an HMAC operation.
+ *
+ * This function may be used to calculate the inner and outer precomputes,
+ * which are the hash contexts resulting from hashing the XORed key for the
+ * 'inner hash' and the 'outer hash', respectively, of the HMAC function.
+ *
+ * After execution of this function, the @a hmac_ctx will contain the
+ * precomputed inner and outer contexts, so that they may be used by
+ * #fsl_shw_hmac(). The flags of @a hmac_ctx will be updated with
+ * #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT to mark their presence. In addtion, the
+ * #FSL_HMAC_FLAGS_INIT flag will be set.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The key being used in this operation. Key must be
+ * 1 to 64 octets long.
+ * @param[in,out] hmac_ctx The context which controls, by its flags and
+ * algorithm, the operation of this function.
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hmac_precompute(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ fsl_shw_hmco_t* hmac_ctx);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-BASIC-HMAC-002 */
+/**
+ * Continue, finalize, or one-shot an HMAC operation.
+ *
+ * There are a number of ways to use this function. The flags in the
+ * @a hmac_ctx object will determine what operations occur.
+ *
+ * If #FSL_HMAC_FLAGS_INIT is set, then the hash will be started either from
+ * the @a key_info, or from the precomputed inner hash value in the
+ * @a hmac_ctx, depending on the value of #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT.
+ *
+ * If, instead, #FSL_HMAC_FLAGS_LOAD is set, then the hash will be continued
+ * from the ongoing inner hash computation in the @a hmac_ctx.
+ *
+ * If #FSL_HMAC_FLAGS_FINALIZE are set, then the @a msg will be padded, hashed,
+ * the outer hash will be performed, and the @a result will be generated.
+ *
+ * If the #FSL_HMAC_FLAGS_SAVE flag is set, then the (ongoing or final) digest
+ * value will be stored in the ongoing inner hash computation field of the @a
+ * hmac_ctx.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info If #FSL_HMAC_FLAGS_INIT is set in the @a hmac_ctx,
+ * this is the key being used in this operation, and the
+ * IPAD. If #FSL_HMAC_FLAGS_INIT is set in the @a
+ * hmac_ctx and @a key_info is NULL, then
+ * #fsl_shw_hmac_precompute() has been used to populate
+ * the @a inner_precompute and @a outer_precompute
+ * contexts. If #FSL_HMAC_FLAGS_INIT is not set, this
+ * parameter is ignored.
+
+ * @param[in,out] hmac_ctx The context which controls, by its flags and
+ * algorithm, the operation of this function.
+ * @param msg Pointer to the message to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result Pointer, of @a result_len octets, to where to
+ * store the HMAC.
+ * @param result_len Length of @a result buffer.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hmac(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_sko_t* key_info,
+ fsl_shw_hmco_t* hmac_ctx,
+ const uint8_t* msg,
+ uint32_t length,
+ uint8_t* result,
+ uint32_t result_len);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-BASIC-RNG-002 */
+/**
+ * Get random data.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length The number of octets of @a data being requested.
+ * @param[out] data A pointer to a location of @a length octets to where
+ * random data will be returned.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_get_random(
+ fsl_shw_uco_t* user_ctx,
+ uint32_t length,
+ uint8_t* data);
+
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSLSHW-PINTFC-API-BASIC-RNG-003 */
+/**
+ * Add entropy to random number generator.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length Number of bytes at @a data.
+ * @param data Entropy to add to random number generator.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_add_entropy(
+ fsl_shw_uco_t* user_ctx,
+ uint32_t length,
+ uint8_t* data);
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-COA-SKO */
+/**
+ * Perform Generation-Encryption by doing a Cipher and a Hash.
+ *
+ * Generate the authentication value @a auth_value as well as encrypt the @a
+ * payload into @a ct (the ciphertext). This is a one-shot function, so all of
+ * the @a auth_data and the total message @a payload must passed in one call.
+ * This also means that the flags in the @a auth_ctx must be #FSL_ACCO_CTX_INIT
+ * and #FSL_ACCO_CTX_FINALIZE.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param auth_ctx Controlling object for Authenticate-decrypt.
+ * @param cipher_key_info The key being used for the cipher part of this
+ * operation. In CCM mode, this key is used for
+ * both parts.
+ * @param auth_key_info The key being used for the authentication part
+ * of this operation. In CCM mode, this key is
+ * ignored and may be NULL.
+ * @param auth_data_length Length, in octets, of @a auth_data.
+ * @param auth_data Data to be authenticated but not encrypted.
+ * @param payload_length Length, in octets, of @a payload.
+ * @param payload Pointer to the plaintext to be encrypted.
+ * @param[out] ct Pointer to the where the encrypted @a payload
+ * will be stored. Must be @a payload_length
+ * octets long.
+ * @param[out] auth_value Pointer to where the generated authentication
+ * field will be stored. Must be as many octets as
+ * indicated by MAC length in the @a function_ctx.
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_gen_encrypt(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_acco_t* auth_ctx,
+ fsl_shw_sko_t* cipher_key_info,
+ fsl_shw_sko_t* auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t* auth_data,
+ uint32_t payload_length,
+ const uint8_t* payload,
+ uint8_t* ct,
+ uint8_t* auth_value);
+
+/* REQ-FSL-SHW-PINTFC-COA-UCO */
+/* REQ-FSL-SHW-PINTFC-COA-SKO */
+/**
+ * Perform Authentication-Decryption in Cipher + Hash.
+ *
+ * This function will perform a one-shot decryption of a data stream as well as
+ * authenticate the authentication value. This is a one-shot function, so all
+ * of the @a auth_data and the total message @a payload must passed in one
+ * call. This also means that the flags in the @a auth_ctx must be
+ * #FSL_ACCO_CTX_INIT and #FSL_ACCO_CTX_FINALIZE.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param auth_ctx Controlling object for Authenticate-decrypt.
+ * @param cipher_key_info The key being used for the cipher part of this
+ * operation. In CCM mode, this key is used for
+ * both parts.
+ * @param auth_key_info The key being used for the authentication part
+ * of this operation. In CCM mode, this key is
+ * ignored and may be NULL.
+ * @param auth_data_length Length, in octets, of @a auth_data.
+ * @param auth_data Data to be authenticated but not decrypted.
+ * @param payload_length Length, in octets, of @a ct and @a pt.
+ * @param ct Pointer to the encrypted input stream.
+ * @param auth_value The (encrypted) authentication value which will
+ * be authenticated. This is the same data as the
+ * (output) @a auth_value argument to
+ * #fsl_shw_gen_encrypt().
+ * @param[out] payload Pointer to where the plaintext resulting from
+ * the decryption will be stored.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_auth_decrypt(
+ fsl_shw_uco_t* user_ctx,
+ fsl_shw_acco_t* auth_ctx,
+ fsl_shw_sko_t* cipher_key_info,
+ fsl_shw_sko_t* auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t* auth_data,
+ uint32_t payload_length,
+ const uint8_t* ct,
+ const uint8_t* auth_value,
+ uint8_t* payload);
+
+/*!
+ * Cause the hardware to create a new random key for secure memory use.
+ *
+ * Have the hardware use the secure hardware random number generator to load a
+ * new secret key into the hardware random key register. It will not be made
+ * active without a call to #fsl_shw_select_pf_key().
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+#ifdef __KERNEL__
+
+extern fsl_shw_return_t fsl_shw_gen_random_pf_key(fsl_shw_uco_t * user_ctx);
+
+#else
+
+#define fsl_shw_gen_random_pf_key(user_ctx) FSL_RETURN_NO_RESOURCE_S
+
+#endif /* __KERNEL__ */
+
+/*!
+ * Retrieve the detected tamper event.
+ *
+ * Note that if more than one event was detected, this routine will only ever
+ * return one of them.
+ *
+ * @param[in] user_ctx A user context from #fsl_shw_register_user().
+ * @param[out] tamperp Location to store the tamper information.
+ * @param[out] timestampp Locate to store timestamp from hardwhare when
+ * an event was detected.
+ *
+ *
+ * @return A return code of type #fsl_shw_return_t (for instance, if the platform
+ * is not in a fail state.
+ */
+#ifdef __KERNEL__
+
+extern fsl_shw_return_t fsl_shw_read_tamper_event(fsl_shw_uco_t * user_ctx,
+ fsl_shw_tamper_t * tamperp,
+ uint64_t * timestampp);
+#else
+
+#define fsl_shw_read_tamper_event(user_ctx,tamperp,timestampp) \
+ FSL_RETURN_NO_RESOURCE_S
+
+#endif /* __KERNEL__ */
+
+/*****************************************************************************
+ *
+ * Functions internal to SHW driver.
+ *
+*****************************************************************************/
+
+fsl_shw_return_t
+do_scc_encrypt_region(fsl_shw_uco_t* user_ctx,
+ void* partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t* black_data,
+ uint32_t* IV, fsl_shw_cypher_mode_t cypher_mode);
+
+fsl_shw_return_t
+do_scc_decrypt_region(fsl_shw_uco_t* user_ctx,
+ void* partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, const uint8_t* black_data,
+ uint32_t* IV, fsl_shw_cypher_mode_t cypher_mode);
+
+
+/*****************************************************************************
+ *
+ * Functions available to other SHW-family drivers.
+ *
+*****************************************************************************/
+
+#ifdef __KERNEL__
+/**
+ * Add an entry to a work/result queue.
+ *
+ * @param pool Pointer to list structure
+ * @param entry Entry to place at tail of list
+ *
+ * @return void
+ */
+inline static void SHW_ADD_QUEUE_ENTRY(shw_queue_t* pool,
+ shw_queue_entry_t* entry)
+{
+ os_lock_context_t lock_context;
+
+ entry->next = NULL;
+ os_lock_save_context(shw_queue_lock, lock_context);
+
+ if (pool->tail != NULL) {
+ pool->tail->next = entry;
+ } else {
+ /* Queue was empty, so this is also the head. */
+ pool->head = entry;
+ }
+ pool->tail = entry;
+
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+
+ return;
+
+
+}
+
+
+/**
+ * Get first entry on the queue and remove it from the queue.
+ *
+ * @return Pointer to first entry, or NULL if none.
+ */
+inline static shw_queue_entry_t* SHW_POP_FIRST_ENTRY(shw_queue_t* queue)
+{
+ shw_queue_entry_t* entry;
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(shw_queue_lock, lock_context);
+
+ entry = queue->head;
+
+ if (entry != NULL) {
+ queue->head = entry->next;
+ entry->next = NULL;
+ /* If this was only entry, clear the tail. */
+ if (queue->tail == entry) {
+ queue->tail = NULL;
+ }
+ }
+
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+
+ return entry;
+}
+
+
+
+/**
+ * Remove an entry from the list.
+ *
+ * If the entry not on the queue, no error will be returned.
+ *
+ * @param pool Pointer to work queue
+ * @param entry Entry to remove from queue
+ *
+ * @return void
+ *
+ */
+inline static void SHW_QUEUE_REMOVE_ENTRY(shw_queue_t* pool,
+ shw_queue_entry_t* entry)
+{
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(shw_queue_lock, lock_context);
+
+ /* Check for quick case.*/
+ if (pool->head == entry) {
+ pool->head = entry->next;
+ entry->next = NULL;
+ if (pool->tail == entry) {
+ pool->tail = NULL;
+ }
+ } else {
+ register shw_queue_entry_t* prev = pool->head;
+
+ /* We know it is not the head, so start looking at entry after head. */
+ while (prev->next) {
+ if (prev->next != entry) {
+ prev = prev->next; /* Try another */
+ continue;
+ } else {
+ /* Unlink from chain. */
+ prev->next = entry->next;
+ entry->next = NULL;
+ /* If last in chain, update tail. */
+ if (pool->tail == entry) {
+ pool->tail = prev;
+ }
+ break;
+ }
+ } /* while */
+ }
+
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+
+ return;
+}
+#endif /* __KERNEL__ */
+
+
+/*****************************************************************************
+ *
+ * Functions available to User-Mode API functions
+ *
+ ****************************************************************************/
+#ifndef __KERNEL__
+
+
+ /**
+ * Sanity checks the user context object fields to ensure that they make some
+ * sense before passing the uco as a parameter.
+ *
+ * @brief Verify the user context object
+ *
+ * @param uco user context object
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t validate_uco(fsl_shw_uco_t *uco);
+
+
+/**
+ * Initialize a request block to go to the driver.
+ *
+ * @param hdr Pointer to request block header
+ * @param user_ctx Pointer to user's context
+ *
+ * @return void
+ */
+inline static void init_req(struct shw_req_header* hdr,
+ fsl_shw_uco_t* user_ctx)
+{
+ hdr->flags = user_ctx->flags;
+ hdr->user_ref = user_ctx->user_ref;
+ hdr->code = FSL_RETURN_ERROR_S;
+
+ return;
+}
+
+
+/**
+ * Send a request block off to the driver.
+ *
+ * If this is a non-blocking request, then req will be freed.
+ *
+ * @param type The type of request being sent
+ * @param req Pointer to the request block
+ * @param ctx Pointer to user's context
+ *
+ * @return code from driver if ioctl() succeeded, otherwise
+ * FSL_RETURN_INTERNAL_ERROR_S.
+ */
+inline static fsl_shw_return_t send_req(shw_user_request_t type,
+ struct shw_req_header* req,
+ fsl_shw_uco_t* ctx)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ unsigned blocking = ctx->flags & FSL_UCO_BLOCKING_MODE;
+ int code;
+
+ code = ioctl(ctx->openfd, SHW_IOCTL_REQUEST + type, req);
+
+ if (code == 0) {
+ if (blocking) {
+ ret = req->code;
+ } else {
+ ret = FSL_RETURN_OK_S;
+ }
+ } else {
+#ifdef FSL_DEBUG
+ fprintf(stderr, "SHW: send_req failed with (%d), %s\n", errno,
+ strerror(errno));
+#endif
+ }
+
+ if (blocking) {
+ free(req);
+ }
+
+ return ret;
+}
+
+
+#endif /* no __KERNEL__ */
+
+#if defined(FSL_HAVE_DRYICE)
+/* Some kernel functions */
+void fsl_shw_permute1_bytes(const uint8_t * key, uint8_t * permuted_key,
+ int key_count);
+void fsl_shw_permute1_bytes_to_words(const uint8_t * key,
+ uint32_t * permuted_key, int key_count);
+
+#define PFKEY_TO_STR(key_in) \
+({ \
+ di_key_t key = key_in; \
+ \
+ ((key == DI_KEY_FK) ? "IIM" : \
+ ((key == DI_KEY_PK) ? "PRG" : \
+ ((key == DI_KEY_RK) ? "RND" : \
+ ((key == DI_KEY_FPK) ? "IIM_PRG" : \
+ ((key == DI_KEY_FRK) ? "IIM_RND" : "unk"))))); \
+})
+
+#ifdef DIAG_SECURITY_FUNC
+extern const char *di_error_string(int code);
+#endif
+
+#endif /* HAVE DRYICE */
+
+#endif /* SHW_DRIVER_H */
diff --git a/drivers/mxc/security/rng/include/shw_hash.h b/drivers/mxc/security/rng/include/shw_hash.h
new file mode 100644
index 000000000000..5cd229003637
--- /dev/null
+++ b/drivers/mxc/security/rng/include/shw_hash.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+/*!
+ * @file shw_hash.h
+ *
+ * This file contains definitions for use of the (internal) SHW hash
+ * software computation. It defines the usual three steps:
+ *
+ * - #shw_hash_init()
+ * - #shw_hash_update()
+ * - #shw_hash_final()
+ *
+ * The only other item of note to callers is #SHW_HASH_LEN, which is the number
+ * of bytes calculated for the hash.
+ */
+
+#ifndef SHW_HASH_H
+#define SHW_HASH_H
+
+/*! Define which gives the number of bytes available in an hash result */
+#define SHW_HASH_LEN 32
+
+/* Define which matches block length in bytes of the underlying hash */
+#define SHW_HASH_BLOCK_LEN 64
+
+/* "Internal" define which matches SHA-256 state size (32-bit words) */
+#define SHW_HASH_STATE_WORDS 8
+
+/* "Internal" define which matches word length in blocks of the underlying
+ hash. */
+#define SHW_HASH_BLOCK_WORD_SIZE 16
+
+#define SHW_HASH_STATE_SIZE 32
+
+/*!
+ * State for a SHA-1/SHA-2 Hash
+ *
+ * (Note to maintainers: state needs to be updated to uint64_t to handle
+ * SHA-384/SHA-512)... And bit_count to uint128_t (heh).
+ */
+typedef struct shw_hash_state {
+ unsigned int partial_count_bytes; /*!< Number of bytes of message sitting
+ * in @c partial_block */
+ uint8_t partial_block[SHW_HASH_BLOCK_LEN]; /*!< Data waiting to be processed as a block */
+ uint32_t state[SHW_HASH_STATE_WORDS]; /*!< Current hash state variables */
+ uint64_t bit_count; /*!< Number of bits sent through the update function */
+} shw_hash_state_t;
+
+/*!
+ * Initialize the hash state structure
+ *
+ * @param state Address of hash state structure.
+ * @param algorithm Which hash algorithm to use (must be FSL_HASH_ALG_SHA256)
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hash_init(shw_hash_state_t * state,
+ fsl_shw_hash_alg_t algorithm);
+
+/*!
+ * Put data into the hash calculation
+ *
+ * @param state Address of hash state structure.
+ * @param msg Address of the message data for the hash.
+ * @param msg_len Number of bytes of @c msg.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hash_update(shw_hash_state_t * state,
+ const uint8_t * msg, unsigned int msg_len);
+
+/*!
+ * Calculate the final hash value
+ *
+ * @param state Address of hash state structure.
+ * @param hash Address of location to store the hash.
+ * @param hash_len Number of bytes of @c hash to be stored.
+ *
+ * @return FSL_RETURN_OK_S if all went well, FSL_RETURN_BAD_DATA_LENGTH_S if
+ * hash_len is too long, otherwise an error code.
+ */
+fsl_shw_return_t shw_hash_final(shw_hash_state_t * state,
+ uint8_t * hash, unsigned int hash_len);
+
+#endif /* SHW_HASH_H */
diff --git a/drivers/mxc/security/rng/include/shw_hmac.h b/drivers/mxc/security/rng/include/shw_hmac.h
new file mode 100644
index 000000000000..de44941f2ef2
--- /dev/null
+++ b/drivers/mxc/security/rng/include/shw_hmac.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+/*!
+ * @file shw_hmac.h
+ *
+ * This file contains definitions for use of the (internal) SHW HMAC
+ * software computation. It defines the usual three steps:
+ *
+ * - #shw_hmac_init()
+ * - #shw_hmac_update()
+ * - #shw_hmac_final()
+ *
+ * The only other item of note to callers is #SHW_HASH_LEN, which is the number
+ * of bytes calculated for the HMAC.
+ */
+
+#ifndef SHW_HMAC_H
+#define SHW_HMAC_H
+
+#include "shw_hash.h"
+
+/*!
+ * State for an HMAC
+ *
+ * Note to callers: This structure contains key material and should be kept in
+ * a secure location, such as internal RAM.
+ */
+typedef struct shw_hmac_state {
+ shw_hash_state_t inner_hash; /*!< Current state of inner hash */
+ shw_hash_state_t outer_hash; /*!< Current state of outer hash */
+} shw_hmac_state_t;
+
+/*!
+ * Initialize the HMAC state structure with the HMAC key
+ *
+ * @param state Address of HMAC state structure.
+ * @param key Address of the key to be used for the HMAC.
+ * @param key_len Number of bytes of @c key. This must not be greater than
+ * the block size of the underlying hash (#SHW_HASH_BLOCK_LEN).
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_init(shw_hmac_state_t * state,
+ const uint8_t * key, unsigned int key_len);
+
+/*!
+ * Put data into the HMAC calculation
+ *
+ * @param state Address of HMAC state structure.
+ * @param msg Address of the message data for the HMAC.
+ * @param msg_len Number of bytes of @c msg.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_update(shw_hmac_state_t * state,
+ const uint8_t * msg, unsigned int msg_len);
+
+/*!
+ * Calculate the final HMAC
+ *
+ * @param state Address of HMAC state structure.
+ * @param hmac Address of location to store the HMAC.
+ * @param hmac_len Number of bytes of @c mac to be stored. Probably best if
+ * this value is no greater than #SHW_HASH_LEN.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_final(shw_hmac_state_t * state,
+ uint8_t * hmac, unsigned int hmac_len);
+
+#endif /* SHW_HMAC_H */
diff --git a/drivers/mxc/security/rng/include/shw_internals.h b/drivers/mxc/security/rng/include/shw_internals.h
new file mode 100644
index 000000000000..7e45af868053
--- /dev/null
+++ b/drivers/mxc/security/rng/include/shw_internals.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 SHW_INTERNALS_H
+#define SHW_INTERNALS_H
+
+/*! @file shw_internals.h
+ *
+ * This file contains definitions which are internal to the SHW driver.
+ *
+ * This header file should only ever be included by shw_driver.c
+ *
+ * Compile-time flags minimally needed:
+ *
+ * @li Some sort of platform flag.
+ *
+ */
+
+#include "portable_os.h"
+#include "shw_driver.h"
+
+/*! @defgroup shwcompileflags SHW Compile Flags
+ *
+ * These are flags which are used to configure the SHW driver at compilation
+ * time.
+ *
+ * The terms 'defined' and 'undefined' refer to whether a @c \#define (or -D on
+ * a compile command) has defined a given preprocessor symbol. If a given
+ * symbol is defined, then @c \#ifdef \<symbol\> will succeed. Some symbols
+ * described below default to not having a definition, i.e. they are undefined.
+ *
+ */
+
+/*! @addtogroup shwcompileflags */
+/*! @{ */
+#ifndef SHW_MAJOR_NODE
+/*!
+ * This should be configured in a Makefile/compile command line. It is the
+ * value the driver will use to register itself as a device driver for a
+ * /dev/node file. Zero means allow (Linux) to assign a value. Any positive
+ * number will be attempted as the registration value, to allow for
+ * coordination with the creation/existence of a /dev/fsl_shw (for instance)
+ * file in the filesystem.
+ */
+#define SHW_MAJOR_NODE 0
+#endif
+
+/* Temporarily define compile-time flags to make Doxygen happy and allow them
+ to get into the documentation. */
+#ifdef DOXYGEN_HACK
+
+/*!
+ * Turn on compilation of run-time operational, debug, and error messages.
+ *
+ * This flag is undefined by default.
+ */
+/* REQ-FSLSHW-DEBUG-001 */
+#define SHW_DEBUG
+#undef SHW_DEBUG
+
+/*! @} */
+#endif /* end DOXYGEN_HACK */
+
+#ifndef SHW_DRIVER_NAME
+/*! @addtogroup shwcompileflags */
+/*! @{ */
+/*! Name the driver will use to register itself to the kernel as the driver for
+ * the #shw_major_node and interrupt handling. */
+#define SHW_DRIVER_NAME "fsl_shw"
+/*! @} */
+#endif
+/*#define SHW_DEBUG*/
+
+/*!
+ * Add a user context onto the list of registered users.
+ *
+ * Place it at the head of the #user_list queue.
+ *
+ * @param ctx A pointer to a user context
+ *
+ * @return void
+ */
+inline static void SHW_ADD_USER(fsl_shw_uco_t * ctx)
+{
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(shw_queue_lock, lock_context);
+ ctx->next = user_list;
+ user_list = ctx;
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+
+}
+
+/*!
+ * Remove a user context from the list of registered users.
+ *
+ * @param ctx A pointer to a user context
+ *
+ * @return void
+ *
+ */
+inline static void SHW_REMOVE_USER(fsl_shw_uco_t * ctx)
+{
+ fsl_shw_uco_t *prev_ctx = user_list;
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(shw_queue_lock, lock_context);
+
+ if (prev_ctx == ctx) {
+ /* Found at head, so just set new head */
+ user_list = ctx->next;
+ } else {
+ for (; (prev_ctx != NULL); prev_ctx = prev_ctx->next) {
+ if (prev_ctx->next == ctx) {
+ prev_ctx->next = ctx->next;
+ break;
+ }
+ }
+ }
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+}
+
+static void shw_user_callback(fsl_shw_uco_t * uco);
+
+/* internal functions */
+static os_error_code shw_setup_user_driver_interaction(void);
+static void shw_cleanup(void);
+
+static os_error_code init_uco(fsl_shw_uco_t * user_ctx, void *user_mode_uco);
+static os_error_code get_capabilities(fsl_shw_uco_t * user_ctx,
+ void *user_mode_pco_request);
+static os_error_code get_results(fsl_shw_uco_t * user_ctx,
+ void *user_mode_result_req);
+static os_error_code get_random(fsl_shw_uco_t * user_ctx,
+ void *user_mode_get_random_req);
+static os_error_code add_entropy(fsl_shw_uco_t * user_ctx,
+ void *user_mode_add_entropy_req);
+
+void* wire_user_memory(void* address, uint32_t length, void** page_ctx);
+void unwire_user_memory(void** page_ctx);
+os_error_code map_user_memory(struct vm_area_struct* vma,
+ uint32_t physical_addr, uint32_t size);
+os_error_code unmap_user_memory(uint32_t user_addr, uint32_t size);
+
+#if defined(LINUX_VERSION_CODE)
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("Device Driver for FSL SHW API");
+
+#endif /* LINUX_VERSION_CODE */
+
+#endif /* SHW_INTERNALS_H */
diff --git a/drivers/mxc/security/rng/rng_driver.c b/drivers/mxc/security/rng/rng_driver.c
new file mode 100644
index 000000000000..bad901926945
--- /dev/null
+++ b/drivers/mxc/security/rng/rng_driver.c
@@ -0,0 +1,1150 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*! @file rng_driver.c
+ *
+ * This is the driver code for the hardware Random Number Generator (RNG).
+ *
+ * It provides the following functions to callers:
+ * fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t* user_ctx,
+ * uint32_t length,
+ * uint8_t* data);
+ *
+ * fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t* user_ctx,
+ * uint32_t length,
+ * uint8_t* data);
+ *
+ * The life of the driver starts at boot (or module load) time, with a call by
+ * the kernel to #rng_init(). As part of initialization, a background task
+ * running #rng_entropy_task() will be created.
+ *
+ * The life of the driver ends when the kernel is shutting down (or the driver
+ * is being unloaded). At this time, #rng_shutdown() is called. No function
+ * will ever be called after that point. In the case that the driver is
+ * reloaded, a new copy of the driver, with fresh global values, etc., is
+ * loaded, and there will be a new call to #rng_init().
+ *
+ * A call to fsl_shw_get_random() gets converted into a work entry which is
+ * queued and handed off to a background task for fulfilment. This provides
+ * for a single thread of control for reading the RNG's FIFO register, which
+ * might otherwise underflow if not carefully managed.
+ *
+ * A call to fsl_shw_add_entropy() will cause the additional entropy to
+ * be passed directly into the hardware.
+ *
+ * In a debug configuration, it provides the following kernel functions:
+ * rng_return_t rng_read_register(uint32_t byte_offset, uint32_t* valuep);
+ * rng_return_t rng_write_register(uint32_t byte_offset, uint32_t value);
+ * @ingroup RNG
+ */
+
+#include "portable_os.h"
+#include "fsl_shw.h"
+#include "rng_internals.h"
+
+#ifdef FSL_HAVE_SCC2
+#include <linux/mxc_scc2_driver.h>
+#else
+#include <linux/mxc_scc_driver.h>
+#endif
+
+#if defined(RNG_DEBUG) || defined(RNG_ENTROPY_DEBUG) || \
+ defined(RNG_REGISTER_DEBUG)
+
+#include <diagnostic.h>
+
+#else
+
+#define LOG_KDIAG_ARGS(fmt, ...)
+#define LOG_KDIAG(diag)
+
+#endif
+
+/* These are often handy */
+#ifndef FALSE
+/*! Non-true value for arguments, return values. */
+#define FALSE 0
+#endif
+#ifndef TRUE
+/*! True value for arguments, return values. */
+#define TRUE 1
+#endif
+
+/******************************************************************************
+ *
+ * Global / Static Variables
+ *
+ *****************************************************************************/
+
+/*!
+ * This is type void* so that a) it cannot directly be dereferenced, and b)
+ * pointer arithmetic on it will function for the byte offsets in rng_rnga.h
+ * and rng_rngc.h
+ *
+ * rng_base is the location in the iomap where the RNG's registers
+ * (and memory) start.
+ *
+ * The referenced data is declared volatile so that the compiler will
+ * not make any assumptions about the value of registers in the RNG,
+ * and thus will always reload the register into CPU memory before
+ * using it (i.e. wherever it is referenced in the driver).
+ *
+ * This value should only be referenced by the #RNG_READ_REGISTER and
+ * #RNG_WRITE_REGISTER macros and their ilk. All dereferences must be
+ * 32 bits wide.
+ */
+static volatile void *rng_base;
+
+/*!
+ * Flag to say whether interrupt handler has been registered for RNG
+ * interrupt */
+static int rng_irq_set = FALSE;
+
+/*!
+ * Size of the RNG's OUTPUT_FIFO, in words. Retrieved with
+ * #RNG_GET_FIFO_SIZE() during driver initialization.
+ */
+static int rng_output_fifo_size;
+
+/*! Major number for device driver. */
+static int rng_major;
+
+/*! Registration handle for registering driver with OS. */
+os_driver_reg_t rng_reg_handle;
+
+/*!
+ * Internal flag to know whether RNG is in Failed state (and thus many
+ * registers are unavailable). If the value ever goes to #RNG_STATUS_FAILED,
+ * it will never change.
+ */
+static volatile rng_status_t rng_availability = RNG_STATUS_INITIAL;
+
+/*!
+ * Global lock for the RNG driver. Mainly used for entries on the RNG work
+ * queue.
+ */
+static os_lock_t rng_queue_lock = NULL;
+
+/*!
+ * Queue for the RNG task to process.
+ */
+static shw_queue_t rng_work_queue;
+
+/*!
+ * Flag to say whether task initialization succeeded.
+ */
+static unsigned task_started = FALSE;
+/*!
+ * Waiting queue for RNG SELF TESTING
+ */
+static DECLARE_COMPLETION(rng_self_testing);
+static DECLARE_COMPLETION(rng_seed_done);
+/*!
+ * Object for blocking-mode callers of RNG driver to sleep.
+ */
+OS_WAIT_OBJECT(rng_wait_queue);
+
+/******************************************************************************
+ *
+ * Function Implementations - Externally Accessible
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn rng_init() */
+/*****************************************************************************/
+/*!
+ * Initialize the driver.
+ *
+ * Set up the driver to have access to RNG device registers and verify that
+ * it appears to be a proper working device.
+ *
+ * Set up interrupt handling. Assure RNG is ready to go and (possibly) set it
+ * into High Assurance mode. Create a background task to run
+ * #rng_entropy_task(). Set up up a callback with the SCC driver should the
+ * security alarm go off. Tell the kernel that the driver is here.
+ *
+ * This routine is called during kernel init or module load (insmod).
+ *
+ * The function will fail in one of two ways: Returning OK to the caller so
+ * that kernel startup / driver initialization completes, or returning an
+ * error. In the success case, the function could set the rng_avaailability to
+ * RNG_STATUS_FAILED so that only minimal support (e.g. register peek / poke)
+ * is available in the driver.
+ *
+ * @return a call to os_dev_init_return()
+ */
+OS_DEV_INIT(rng_init)
+{
+ struct clk *clk;
+ os_error_code return_code = OS_ERROR_FAIL_S;
+ rng_availability = RNG_STATUS_CHECKING;
+
+#if !defined(FSL_HAVE_RNGA)
+ INIT_COMPLETION(rng_self_testing);
+ INIT_COMPLETION(rng_seed_done);
+#endif
+ rng_work_queue.head = NULL;
+ rng_work_queue.tail = NULL;
+
+ clk = clk_get(NULL, "rng_clk");
+
+ // Check that the clock was found
+ if (IS_ERR(clk)) {
+ LOG_KDIAG("RNG: Failed to find rng_clock.");
+ return_code = OS_ERROR_FAIL_S;
+ goto check_err;
+ }
+
+ clk_enable(clk);
+
+ os_printk(KERN_INFO "RNG Driver: Loading\n");
+
+ return_code = rng_map_RNG_memory();
+ if (return_code != OS_ERROR_OK_S) {
+ rng_availability = RNG_STATUS_UNIMPLEMENTED;
+ LOG_KDIAG_ARGS("RNG: Driver failed to map RNG registers. %d",
+ return_code);
+ goto check_err;
+ }
+ LOG_KDIAG_ARGS("RNG Driver: rng_base is 0x%08x", (uint32_t) rng_base);
+ /*Check SCC keys are fused */
+ if (RNG_HAS_ERROR()) {
+ if (RNG_HAS_BAD_KEY()) {
+#ifdef RNG_DEBUG
+#if !defined(FSL_HAVE_RNGA)
+ LOG_KDIAG("ERROR: BAD KEYS SELECTED");
+ {
+ uint32_t rngc_status =
+ RNG_READ_REGISTER(RNGC_STATUS);
+ uint32_t rngc_error =
+ RNG_READ_REGISTER(RNGC_ERROR);
+ LOG_KDIAG_ARGS
+ ("status register: %08x, error status: %08x",
+ rngc_status, rngc_error);
+ }
+#endif
+#endif
+ rng_availability = RNG_STATUS_FAILED;
+ return_code = OS_ERROR_FAIL_S;
+ goto check_err;
+ }
+ }
+
+ /* Check RNG configuration and status */
+ return_code = rng_grab_config_values();
+ if (return_code != OS_ERROR_OK_S) {
+ rng_availability = RNG_STATUS_UNIMPLEMENTED;
+ goto check_err;
+ }
+
+ /* Masking All Interrupts */
+ /* They are unmasked later in rng_setup_interrupt_handling() */
+ RNG_MASK_ALL_INTERRUPTS();
+
+ RNG_WAKE();
+
+ /* Determine status of RNG */
+ if (RNG_OSCILLATOR_FAILED()) {
+ LOG_KDIAG("RNG Driver: RNG Oscillator is dead");
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ }
+
+ /* Oscillator not dead. Setup interrupt code and start the RNG. */
+ if ((return_code = rng_setup_interrupt_handling()) == OS_ERROR_OK_S) {
+#if defined(FSL_HAVE_RNGA)
+ scc_return_t scc_code;
+#endif
+
+ RNG_GO();
+
+ /* Self Testing For RNG */
+ do {
+ RNG_CLEAR_ERR();
+
+ /* wait for Clearing Erring finished */
+ msleep(1);
+
+ RNG_UNMASK_ALL_INTERRUPTS();
+ RNG_SELF_TEST();
+#if !defined(FSL_HAVE_RNGA)
+ wait_for_completion(&rng_self_testing);
+#endif
+ } while (RNG_CHECK_SELF_ERR());
+
+ RNG_CLEAR_ALL_STATUS();
+ /* checking for RNG SEED done */
+ do {
+ RNG_CLEAR_ERR();
+ RNG_SEED_GEN();
+#if !defined(FSL_HAVE_RNGA)
+ wait_for_completion(&rng_seed_done);
+#endif
+ } while (RNG_CHECK_SEED_ERR());
+#ifndef RNG_NO_FORCE_HIGH_ASSURANCE
+ RNG_SET_HIGH_ASSURANCE();
+#endif
+ if (RNG_GET_HIGH_ASSURANCE()) {
+ LOG_KDIAG("RNG Driver: RNG is in High Assurance mode");
+ } else {
+#ifndef RNG_NO_FORCE_HIGH_ASSURANCE
+ LOG_KDIAG
+ ("RNG Driver: RNG could not be put in High Assurance mode");
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+#endif /* RNG_NO_FORCE_HIGH_ASSURANCE */
+ }
+
+ /* Check that RNG is OK */
+ if (!RNG_WORKING()) {
+ LOG_KDIAG_ARGS
+ ("RNG determined to be inoperable. Status %08x",
+ RNG_GET_STATUS());
+ /* Couldn't wake it up or other problem */
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ }
+
+ rng_queue_lock = os_lock_alloc_init();
+ if (rng_queue_lock == NULL) {
+ LOG_KDIAG("RNG: lock initialization failed");
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ }
+
+ return_code = os_create_task(rng_entropy_task);
+ if (return_code != OS_ERROR_OK_S) {
+ LOG_KDIAG("RNG: task initialization failed");
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ } else {
+ task_started = TRUE;
+ }
+#ifdef FSL_HAVE_RNGA
+ scc_code = scc_monitor_security_failure(rng_sec_failure);
+ if (scc_code != SCC_RET_OK) {
+ LOG_KDIAG_ARGS("Failed to register SCC callback: %d",
+ scc_code);
+#ifndef RNG_NO_FORCE_HIGH_ASSURANCE
+ return_code = OS_ERROR_FAIL_S;
+ goto check_err;
+#endif
+ }
+#endif /* FSL_HAVE_RNGA */
+ return_code = os_driver_init_registration(rng_reg_handle);
+ if (return_code != OS_ERROR_OK_S) {
+ goto check_err;
+ }
+ /* add power suspend here */
+ /* add power resume here */
+ return_code =
+ os_driver_complete_registration(rng_reg_handle,
+ rng_major, RNG_DRIVER_NAME);
+ }
+ /* RNG is working */
+
+ check_err:
+
+ /* If FIFO underflow or other error occurred during drain, this will fail,
+ * as system will have been put into fail mode by SCC. */
+ if ((return_code == OS_ERROR_OK_S)
+ && (rng_availability == RNG_STATUS_CHECKING)) {
+ RNG_PUT_RNG_TO_SLEEP();
+ rng_availability = RNG_STATUS_OK; /* RNG & driver are ready */
+ } else if (return_code != OS_ERROR_OK_S) {
+ os_printk(KERN_ALERT "Driver initialization failed. %d",
+ return_code);
+ rng_cleanup();
+ }
+
+ os_dev_init_return(return_code);
+
+} /* rng_init */
+
+/*****************************************************************************/
+/* fn rng_shutdown() */
+/*****************************************************************************/
+/*!
+ * Prepare driver for exit.
+ *
+ * This is called during @c rmmod when the driver is unloading.
+ * Try to undo whatever was done during #rng_init(), to make the machine be
+ * in the same state, if possible.
+ *
+ * Calls rng_cleanup() to do all work, and then unmap device's register space.
+ */
+OS_DEV_SHUTDOWN(rng_shutdown)
+{
+ LOG_KDIAG("shutdown called");
+
+ rng_cleanup();
+
+ os_driver_remove_registration(rng_reg_handle);
+ if (rng_base != NULL) {
+ /* release the virtual memory map to the RNG */
+ os_unmap_device((void *)rng_base, RNG_ADDRESS_RANGE);
+ rng_base = NULL;
+ }
+
+ os_dev_shutdown_return(OS_ERROR_OK_S);
+} /* rng_shutdown */
+
+/*****************************************************************************/
+/* fn rng_cleanup() */
+/*****************************************************************************/
+/*!
+ * Undo everything done by rng_init() and place driver in fail mode.
+ *
+ * Deregister from SCC, stop tasklet, shutdown the RNG. Leave the register
+ * map in place in case other drivers call rng_read/write_register()
+ *
+ * @return void
+ */
+static void rng_cleanup(void)
+{
+ struct clk *clk;
+
+#ifdef FSL_HAVE_RNGA
+ scc_stop_monitoring_security_failure(rng_sec_failure);
+#endif
+
+ clk = clk_get(NULL, "rng_clk");
+ clk_disable(clk);
+ if (task_started) {
+ os_dev_stop_task(rng_entropy_task);
+ }
+
+ if (rng_base != NULL) {
+ /* mask off RNG interrupts */
+ RNG_MASK_ALL_INTERRUPTS();
+ RNG_SLEEP();
+
+ if (rng_irq_set) {
+ /* unmap the interrupts from the IRQ lines */
+ os_deregister_interrupt(INT_RNG);
+ rng_irq_set = FALSE;
+ }
+ LOG_KDIAG("Leaving rng driver status as failed");
+ rng_availability = RNG_STATUS_FAILED;
+ } else {
+ LOG_KDIAG("Leaving rng driver status as unimplemented");
+ rng_availability = RNG_STATUS_UNIMPLEMENTED;
+ }
+ LOG_KDIAG("Cleaned up");
+} /* rng_cleanup */
+
+/*!
+ * Post-process routine for fsl_shw_get_random().
+ *
+ * This function will copy the random data generated by the background task
+ * into the user's buffer and then free the local buffer.
+ *
+ * @param gen_entry The work request.
+ *
+ * @return 0 = meaning work completed, pass back result.
+ */
+static uint32_t finish_random(shw_queue_entry_t * gen_entry)
+{
+ rng_work_entry_t *work = (rng_work_entry_t *) gen_entry;
+
+ if (work->hdr.flags & FSL_UCO_USERMODE_USER) {
+ os_copy_to_user(work->data_user, work->data_local,
+ work->length);
+ } else {
+ memcpy(work->data_user, work->data_local, work->length);
+ }
+
+ os_free_memory(work->data_local);
+ work->data_local = NULL;
+
+ return 0; /* means completed. */
+}
+
+/* REQ-FSLSHW-PINTFC-API-BASIC-RNG-002 */
+/*****************************************************************************/
+/* fn fsl_shw_get_random() */
+/*****************************************************************************/
+/*!
+ * Get random data.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length The number of octets of @a data being requested.
+ * @param data A pointer to a location of @a length octets to where
+ * random data will be returned.
+ *
+ * @return FSL_RETURN_NO_RESOURCE_S A return code of type #fsl_shw_return_t.
+ * FSL_RETURN_OK_S
+ */
+fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx, uint32_t length,
+ uint8_t * data)
+{
+ fsl_shw_return_t return_code = FSL_RETURN_NO_RESOURCE_S;
+ /* Boost up length to cover any 'missing' bytes at end of a word */
+ uint32_t *buf = os_alloc_memory(length + 3, 0);
+ volatile rng_work_entry_t *work = os_alloc_memory(sizeof(*work), 0);
+
+ if ((rng_availability != RNG_STATUS_OK) || (buf == NULL)
+ || (work == NULL)) {
+ if (rng_availability != RNG_STATUS_OK) {
+ LOG_KDIAG_ARGS("rng not available: %d\n",
+ rng_availability);
+ } else {
+ LOG_KDIAG_ARGS
+ ("Resource allocation failure: %d or %d bytes",
+ length, sizeof(*work));
+ }
+ /* Cannot perform function. Clean up and clear out. */
+ if (buf != NULL) {
+ os_free_memory(buf);
+ }
+ if (work != NULL) {
+ os_free_memory((void *)work);
+ }
+ } else {
+ unsigned blocking = user_ctx->flags & FSL_UCO_BLOCKING_MODE;
+
+ work->hdr.user_ctx = user_ctx;
+ work->hdr.flags = user_ctx->flags;
+ work->hdr.callback = user_ctx->callback;
+ work->hdr.user_ref = user_ctx->user_ref;
+ work->hdr.postprocess = finish_random;
+ work->length = length;
+ work->data_local = buf;
+ work->data_user = data;
+
+ RNG_ADD_WORK_ENTRY((rng_work_entry_t *) work);
+
+ if (blocking) {
+ os_sleep(rng_wait_queue, work->completed != FALSE,
+ FALSE);
+ finish_random((shw_queue_entry_t *) work);
+ return_code = work->hdr.code;
+ os_free_memory((void *)work);
+ } else {
+ return_code = FSL_RETURN_OK_S;
+ }
+ }
+
+ return return_code;
+} /* fsl_shw_get_entropy */
+
+/*****************************************************************************/
+/* fn fsl_shw_add_entropy() */
+/*****************************************************************************/
+/*!
+ * Add entropy to random number generator.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length Number of bytes at @a data.
+ * @param data Entropy to add to random number generator.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx, uint32_t length,
+ uint8_t * data)
+{
+ fsl_shw_return_t return_code = FSL_RETURN_NO_RESOURCE_S;
+#if defined(FSL_HAVE_RNGC)
+ /* No Entropy Register in RNGC */
+ return_code = FSL_RETURN_OK_S;
+#else
+ uint32_t *local_data = NULL;
+ if (rng_availability == RNG_STATUS_OK) {
+ /* make 32-bit aligned place to hold data */
+ local_data = os_alloc_memory(length + 3, 0);
+ if (local_data == NULL) {
+ return_code = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ memcpy(local_data, data, length);
+
+ /* Copy one word at a time to hardware */
+ while (TRUE) {
+ register uint32_t *ptr = local_data;
+
+ RNG_ADD_ENTROPY(*ptr++);
+ if (length <= 4) {
+ break;
+ }
+ length -= 4;
+ }
+ return_code = FSL_RETURN_OK_S;
+ os_free_memory(local_data);
+ } /* else local_data not NULL */
+
+ }
+#endif
+ /* rng_availability is OK */
+ return return_code;
+} /* fsl_shw_add_entropy */
+
+#ifdef RNG_REGISTER_PEEK_POKE
+/*****************************************************************************/
+/* fn rng_read_register() */
+/*****************************************************************************/
+/*
+ * Debug routines to allow reading of RNG registers.
+ *
+ * This routine is only for accesses by other than this driver.
+ *
+ * @param register_offset The byte offset of the register to be read.
+ * @param value Pointer to store the value of the register.
+ *
+ * @return RNG_RET_OK or an error return.
+ */
+rng_return_t rng_read_register(uint32_t register_offset, uint32_t * value)
+{
+ rng_return_t return_code = RNG_RET_FAIL;
+
+ if ((rng_availability == RNG_STATUS_OK)
+ || (rng_availability == RNG_STATUS_FAILED)) {
+ if ((rng_check_register_offset(register_offset)
+ && rng_check_register_accessible(register_offset,
+ RNG_CHECK_READ))) {
+ /* The guards let the check through */
+ *value = RNG_READ_REGISTER(register_offset);
+ return_code = RNG_RET_OK;
+ }
+ }
+
+ return return_code;
+} /* rng_read_register */
+
+/*****************************************************************************/
+/* fn rng_write_register() */
+/*****************************************************************************/
+/*
+ * Debug routines to allow writing of RNG registers.
+ *
+ * This routine is only for accesses by other than this driver.
+ *
+ * @param register_offset The byte offset of the register to be written.
+ * @param value Value to store in the register.
+ *
+ * @return RNG_RET_OK or an error return.
+ */
+rng_return_t rng_write_register(uint32_t register_offset, uint32_t value)
+{
+ rng_return_t return_code = RNG_RET_FAIL;
+
+ if ((rng_availability == RNG_STATUS_OK)
+ || (rng_availability == RNG_STATUS_FAILED)) {
+ if ((rng_check_register_offset(register_offset)
+ && rng_check_register_accessible(register_offset,
+ RNG_CHECK_WRITE))) {
+ RNG_WRITE_REGISTER(register_offset, value);
+ return_code = RNG_RET_OK;
+ }
+ }
+
+ return return_code;
+} /* rng_write_register */
+#endif /* RNG_REGISTER_PEEK_POKE */
+
+/******************************************************************************
+ *
+ * Function Implementations - Internal
+ *
+ *****************************************************************************/
+
+#ifdef RNG_REGISTER_PEEK_POKE
+/*****************************************************************************/
+/* fn check_register_offset() */
+/*****************************************************************************/
+/*!
+ * Verify that the @c offset is appropriate for the RNG's register set.
+ *
+ * @param[in] offset The (byte) offset within the RNG block
+ * of the register to be accessed. See
+ * RNG(A, C) register definitions for meanings.
+ *
+ * This routine is only for checking accesses by other than this driver.
+ *
+ * @return 0 if register offset out of bounds, 1 if ok to use
+ */
+inline int rng_check_register_offset(uint32_t offset)
+{
+ int return_code = FALSE; /* invalid */
+
+ /* Make sure offset isn't too high and also that it is aligned to
+ * aa 32-bit offset (multiple of four).
+ */
+ if ((offset < RNG_ADDRESS_RANGE) && (offset % sizeof(uint32_t) == 0)) {
+ return_code = TRUE; /* OK */
+ } else {
+ pr_debug("RNG: Denied access to offset %8x\n", offset);
+ }
+
+ return return_code;
+
+} /* rng_check_register */
+
+/*****************************************************************************/
+/* fn check_register_accessible() */
+/*****************************************************************************/
+/*!
+ * Make sure that register access is legal.
+ *
+ * Verify that, if in secure mode, only safe registers are used.
+ * For any register access, make sure that read-only registers are not written
+ * and that write-only registers are not read. This check also disallows any
+ * access to the RNG's Output FIFO, to prevent other drivers from draining the
+ * FIFO and causing an underflow condition.
+ *
+ * This routine is only for checking accesses by other than this driver.
+ *
+ * @param offset The (byte) offset within the RNG block
+ * of the register to be accessed. See
+ * @ref rngregs for meanings.
+ * @param access_write 0 for read, anything else for write
+ *
+ * @return 0 if invalid, 1 if OK.
+ */
+static int rng_check_register_accessible(uint32_t offset, int access_write)
+{
+ int return_code = FALSE; /* invalid */
+ uint32_t secure = RNG_GET_HIGH_ASSURANCE();
+
+ /* First check for RNG in Secure Mode -- most registers inaccessible.
+ * Also disallowing access to RNG_OUTPUT_FIFO except by the driver.
+ */
+ if (!
+#ifdef FSL_HAVE_RNGA
+ (secure &&
+ ((offset == RNGA_OUTPUT_FIFO) ||
+ (offset == RNGA_MODE) ||
+ (offset == RNGA_VERIFICATION_CONTROL) ||
+ (offset == RNGA_OSCILLATOR_CONTROL_COUNTER) ||
+ (offset == RNGA_OSCILLATOR1_COUNTER) ||
+ (offset == RNGA_OSCILLATOR2_COUNTER) ||
+ (offset == RNGA_OSCILLATOR_COUNTER_STATUS)))
+#else /* RNGB or RNGC */
+ (secure &&
+ ((offset == RNGC_FIFO) ||
+ (offset == RNGC_VERIFICATION_CONTROL) ||
+ (offset == RNGC_OSC_COUNTER_CONTROL) ||
+ (offset == RNGC_OSC_COUNTER) ||
+ (offset == RNGC_OSC_COUNTER_STATUS)))
+#endif
+ ) {
+
+ /* Passed that test. Either not in high assurance, and/or are
+ checking register that is always available. Now check
+ R/W permissions. */
+ if (access_write == RNG_CHECK_READ) { /* read request */
+ /* Only the entropy register is write-only */
+#ifdef FSL_HAVE_RNGC
+ /* No registers are write-only */
+ return_code = TRUE;
+#else /* else RNGA or RNGB */
+#ifdef FSL_HAVE_RNGA
+ if (1) {
+#else
+ if (!(offset == RNGB_ENTROPY)) {
+#endif
+ return_code = TRUE; /* Let all others be read */
+ } else {
+ pr_debug
+ ("RNG: Offset %04x denied read access\n",
+ offset);
+ }
+#endif /* RNGA or RNGB */
+ } /* read */
+ else { /* access_write means write */
+ /* Check against list of non-writable registers */
+ if (!
+#ifdef FSL_HAVE_RNGA
+ ((offset == RNGA_STATUS) ||
+ (offset == RNGA_OUTPUT_FIFO) ||
+ (offset == RNGA_OSCILLATOR1_COUNTER) ||
+ (offset == RNGA_OSCILLATOR2_COUNTER) ||
+ (offset == RNGA_OSCILLATOR_COUNTER_STATUS))
+#else /* FSL_HAVE_RNGB or FSL_HAVE_RNGC */
+ ((offset == RNGC_STATUS) ||
+ (offset == RNGC_FIFO) ||
+ (offset == RNGC_OSC_COUNTER) ||
+ (offset == RNGC_OSC_COUNTER_STATUS))
+#endif
+ ) {
+ return_code = TRUE; /* can be written */
+ } else {
+ LOG_KDIAG_ARGS
+ ("Offset %04x denied write access", offset);
+ }
+ } /* write */
+ } /* not high assurance and inaccessible register... */
+ else {
+ LOG_KDIAG_ARGS("Offset %04x denied high-assurance access",
+ offset);
+ }
+
+ return return_code;
+} /* rng_check_register_accessible */
+#endif /* RNG_REGISTER_PEEK_POKE */
+
+/*****************************************************************************/
+/* fn rng_irq() */
+/*****************************************************************************/
+/*!
+ * This is the interrupt handler for the RNG. It is only ever invoked if the
+ * RNG detects a FIFO Underflow error.
+ *
+ * If the error is a Security Violation, this routine will
+ * set the #rng_availability to #RNG_STATUS_FAILED, as the entropy pool may
+ * have been corrupted. The RNG will also be placed into low power mode. The
+ * SCC will have noticed the problem as well.
+ *
+ * The other possibility, if the RNG is not in High Assurance mode, would be
+ * simply a FIFO Underflow. No special action, other than to
+ * clear the interrupt, is taken.
+ */
+OS_DEV_ISR(rng_irq)
+{
+ int handled = FALSE; /* assume interrupt isn't from RNG */
+
+ LOG_KDIAG("rng irq!");
+
+ if (RNG_SEED_DONE()) {
+ complete(&rng_seed_done);
+ RNG_CLEAR_ALL_STATUS();
+ handled = TRUE;
+ }
+
+ if (RNG_SELF_TEST_DONE()) {
+ complete(&rng_self_testing);
+ RNG_CLEAR_ALL_STATUS();
+ handled = TRUE;
+ }
+ /* Look to see whether RNG needs attention */
+ if (RNG_HAS_ERROR()) {
+ if (RNG_GET_HIGH_ASSURANCE()) {
+ RNG_SLEEP();
+ rng_availability = RNG_STATUS_FAILED;
+ RNG_MASK_ALL_INTERRUPTS();
+ }
+ handled = TRUE;
+ /* Clear the interrupt */
+ RNG_CLEAR_ALL_STATUS();
+
+ }
+ os_dev_isr_return(handled);
+} /* rng_irq */
+
+/*****************************************************************************/
+/* fn map_RNG_memory() */
+/*****************************************************************************/
+/*!
+ * Place the RNG's memory into kernel virtual space.
+ *
+ * @return OS_ERROR_OK_S on success, os_error_code on failure
+ */
+static os_error_code rng_map_RNG_memory(void)
+{
+ os_error_code error_code = OS_ERROR_FAIL_S;
+
+ rng_base = os_map_device(RNG_BASE_ADDR, RNG_ADDRESS_RANGE);
+ if (rng_base == NULL) {
+ /* failure ! */
+ LOG_KDIAG("RNG Driver: ioremap failed.");
+ } else {
+ error_code = OS_ERROR_OK_S;
+ }
+
+ return error_code;
+} /* rng_map_RNG_memory */
+
+/*****************************************************************************/
+/* fn rng_setup_interrupt_handling() */
+/*****************************************************************************/
+/*!
+ * Register #rng_irq() as the interrupt handler for #INT_RNG.
+ *
+ * @return OS_ERROR_OK_S on success, os_error_code on failure
+ */
+static os_error_code rng_setup_interrupt_handling(void)
+{
+ os_error_code error_code;
+
+ /*
+ * Install interrupt service routine for the RNG. Ignore the
+ * assigned IRQ number.
+ */
+ error_code = os_register_interrupt(RNG_DRIVER_NAME, INT_RNG,
+ OS_DEV_ISR_REF(rng_irq));
+ if (error_code != OS_ERROR_OK_S) {
+ LOG_KDIAG("RNG Driver: Error installing Interrupt Handler");
+ } else {
+ rng_irq_set = TRUE;
+ RNG_UNMASK_ALL_INTERRUPTS();
+ }
+
+ return error_code;
+} /* rng_setup_interrupt_handling */
+
+/*****************************************************************************/
+/* fn rng_grab_config_values() */
+/*****************************************************************************/
+/*!
+ * Read configuration information from the RNG.
+ *
+ * Sets #rng_output_fifo_size.
+ *
+ * @return A error code indicating whether the part is the expected one.
+ */
+static os_error_code rng_grab_config_values(void)
+{
+ enum rng_type type;
+ os_error_code ret = OS_ERROR_FAIL_S;
+
+ /* Go for type, versions... */
+ type = RNG_GET_RNG_TYPE();
+
+ /* Make sure type is the one this code has been compiled for. */
+ if (RNG_VERIFY_TYPE(type)) {
+ rng_output_fifo_size = RNG_GET_FIFO_SIZE();
+ if (rng_output_fifo_size != 0) {
+ ret = OS_ERROR_OK_S;
+ }
+ }
+ if (ret != OS_ERROR_OK_S) {
+ LOG_KDIAG_ARGS
+ ("Unknown or unexpected RNG type %d (FIFO size %d)."
+ " Failing driver initialization", type,
+ rng_output_fifo_size);
+ }
+
+ return ret;
+}
+
+ /* rng_grab_config_values */
+
+/*****************************************************************************/
+/* fn rng_drain_fifo() */
+/*****************************************************************************/
+/*!
+ * This function copies words from the RNG FIFO into the caller's buffer.
+ *
+ *
+ * @param random_p Location to copy random data
+ * @param count_words Number of words to copy
+ *
+ * @return An error code.
+ */
+static fsl_shw_return_t rng_drain_fifo(uint32_t * random_p, int count_words)
+{
+
+ int words_in_rng; /* Number of words available now in RNG */
+ fsl_shw_return_t code = FSL_RETURN_ERROR_S;
+ int sequential_count = 0; /* times through big while w/empty FIFO */
+ int fifo_empty_count = 0; /* number of times FIFO was empty */
+ int max_sequential = 0; /* max times 0 seen in a row */
+#if !defined(FSL_HAVE_RNGA)
+ int count_for_reseed = 0;
+ INIT_COMPLETION(rng_seed_done);
+#endif
+#if !defined(FSL_HAVE_RNGA)
+ if (RNG_RESEED()) {
+ do {
+ LOG_KDIAG("Reseeding RNG");
+
+ RNG_CLEAR_ERR();
+ RNG_SEED_GEN();
+ wait_for_completion(&rng_seed_done);
+ if (count_for_reseed == 3) {
+ os_printk(KERN_ALERT
+ "Device was not able to enter RESEED Mode\n");
+ code = FSL_RETURN_INTERNAL_ERROR_S;
+ }
+ count_for_reseed++;
+ } while (RNG_CHECK_SEED_ERR());
+ }
+#endif
+ /* Copy all of them in. Stop if pool fills. */
+ while ((rng_availability == RNG_STATUS_OK) && (count_words > 0)) {
+ /* Ask RNG how many words currently in FIFO */
+ words_in_rng = RNG_GET_WORDS_IN_FIFO();
+ if (words_in_rng == 0) {
+ ++sequential_count;
+ fifo_empty_count++;
+ if (sequential_count > max_sequential) {
+ max_sequential = sequential_count;
+ }
+ if (sequential_count >= RNG_MAX_TRIES) {
+ LOG_KDIAG_ARGS("FIFO staying empty (%d)",
+ words_in_rng);
+ code = FSL_RETURN_NO_RESOURCE_S;
+ break;
+ }
+ } else {
+ /* Found at least one word */
+ sequential_count = 0;
+ /* Now adjust: words_in_rng = MAX(count_words, words_in_rng) */
+ words_in_rng = (count_words < words_in_rng)
+ ? count_words : words_in_rng;
+ } /* else found words */
+
+#ifdef RNG_FORCE_FIFO_UNDERFLOW
+ /*
+ * For unit test, force occasional extraction of more words than
+ * available. This should cause FIFO Underflow, and IRQ invocation.
+ */
+ words_in_rng = count_words;
+#endif
+
+ /* Copy out all available & neeeded data */
+ while (words_in_rng-- > 0) {
+ *random_p++ = RNG_READ_FIFO();
+ count_words--;
+ }
+ } /* while words still needed */
+
+ if (count_words == 0) {
+ code = FSL_RETURN_OK_S;
+ }
+ if (fifo_empty_count != 0) {
+ LOG_KDIAG_ARGS("FIFO empty %d times, max loop count %d",
+ fifo_empty_count, max_sequential);
+ }
+
+ return code;
+} /* rng_drain_fifo */
+
+/*****************************************************************************/
+/* fn rng_entropy_task() */
+/*****************************************************************************/
+/*!
+ * This is the background task of the driver. It is scheduled by
+ * RNG_ADD_WORK_ENTRY().
+ *
+ * This will process each entry on the #rng_work_queue. Blocking requests will
+ * cause sleepers to be awoken. Non-blocking requests will be placed on the
+ * results queue, and if appropriate, the callback function will be invoked.
+ */
+OS_DEV_TASK(rng_entropy_task)
+{
+ rng_work_entry_t *work;
+
+ os_dev_task_begin();
+
+#ifdef RNG_ENTROPY_DEBUG
+ LOG_KDIAG("entropy task starting");
+#endif
+
+ while ((work = RNG_GET_WORK_ENTRY()) != NULL) {
+#ifdef RNG_ENTROPY_DEBUG
+ LOG_KDIAG_ARGS("found %d bytes of work at %p (%p)",
+ work->length, work, work->data_local);
+#endif
+ work->hdr.code = rng_drain_fifo(work->data_local,
+ BYTES_TO_WORDS(work->length));
+ work->completed = TRUE;
+
+ if (work->hdr.flags & FSL_UCO_BLOCKING_MODE) {
+#ifdef RNG_ENTROPY_DEBUG
+ LOG_KDIAG("Waking queued processes");
+#endif
+ os_wake_sleepers(rng_wait_queue);
+ } else {
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(rng_queue_lock, lock_context);
+ RNG_ADD_QUEUE_ENTRY(&work->hdr.user_ctx->result_pool,
+ work);
+ os_unlock_restore_context(rng_queue_lock, lock_context);
+
+ if (work->hdr.flags & FSL_UCO_CALLBACK_MODE) {
+ if (work->hdr.callback != NULL) {
+ work->hdr.callback(work->hdr.user_ctx);
+ } else {
+#ifdef RNG_ENTROPY_DEBUG
+ LOG_KDIAG_ARGS
+ ("Callback ptr for %p is NULL",
+ work);
+#endif
+ }
+ }
+ }
+ } /* while */
+
+#ifdef RNG_ENTROPY_DEBUG
+ LOG_KDIAG("entropy task ending");
+#endif
+
+ os_dev_task_return(OS_ERROR_OK_S);
+} /* rng_entropy_task */
+
+#ifdef FSL_HAVE_RNGA
+/*****************************************************************************/
+/* fn rng_sec_failure() */
+/*****************************************************************************/
+/*!
+ * Function to handle "Security Alarm" indication from SCC.
+ *
+ * This function is registered with the Security Monitor ans the callback
+ * function for the RNG driver. Upon alarm, it will shut down the driver so
+ * that no more random data can be retrieved.
+ *
+ * @return void
+ */
+static void rng_sec_failure(void)
+{
+ os_printk(KERN_ALERT "RNG Driver: Security Failure Alarm received.\n");
+
+ rng_cleanup();
+
+ return;
+}
+#endif
+
+#ifdef RNG_REGISTER_DEBUG
+/*****************************************************************************/
+/* fn dbg_rng_read_register() */
+/*****************************************************************************/
+/*!
+ * Noisily read a 32-bit value to an RNG register.
+ * @param offset The address of the register to read.
+ *
+ * @return The register value
+ * */
+static uint32_t dbg_rng_read_register(uint32_t offset)
+{
+ uint32_t value;
+
+ value = os_read32(rng_base + offset);
+#ifndef RNG_ENTROPY_DEBUG
+ if (offset != RNG_OUTPUT_FIFO) {
+#endif
+ pr_debug("RNG RD: 0x%4x : 0x%08x\n", offset, value);
+#ifndef RNG_ENTROPY_DEBUG
+ }
+#endif
+ return value;
+}
+
+/*****************************************************************************/
+/* fn dbg_rng_write_register() */
+/*****************************************************************************/
+/*!
+ * Noisily write a 32-bit value to an RNG register.
+ * @param offset The address of the register to written.
+ *
+ * @param value The new register value
+ */
+static void dbg_rng_write_register(uint32_t offset, uint32_t value)
+{
+ LOG_KDIAG_ARGS("WR: 0x%4x : 0x%08x", offset, value);
+ os_write32(value, rng_base + offset);
+ return;
+}
+
+#endif /* RNG_REGISTER_DEBUG */
diff --git a/drivers/mxc/security/rng/shw_driver.c b/drivers/mxc/security/rng/shw_driver.c
new file mode 100644
index 000000000000..81212e55919e
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_driver.c
@@ -0,0 +1,2335 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*! @file shw_driver.c
+ *
+ * This is the user-mode driver code for the FSL Security Hardware (SHW) API.
+ * as well as the 'common' FSL SHW API code for kernel API users.
+ *
+ * Its interaction with the Linux kernel is from calls to shw_init() when the
+ * driver is loaded, and shw_shutdown() should the driver be unloaded.
+ *
+ * The User API (driver interface) is handled by the following functions:
+ * @li shw_open() - handles open() system call on FSL SHW device
+ * @li shw_release() - handles close() system call on FSL SHW device
+ * @li shw_ioctl() - handles ioctl() system call on FSL SHW device
+ *
+ * The driver also provides the following functions for kernel users of the FSL
+ * SHW API:
+ * @li fsl_shw_register_user()
+ * @li fsl_shw_deregister_user()
+ * @li fsl_shw_get_capabilities()
+ * @li fsl_shw_get_results()
+ *
+ * All other functions are internal to the driver.
+ *
+ * The life of the driver starts at boot (or module load) time, with a call by
+ * the kernel to shw_init().
+ *
+ * The life of the driver ends when the kernel is shutting down (or the driver
+ * is being unloaded). At this time, shw_shutdown() is called. No function
+ * will ever be called after that point.
+ *
+ * In the case that the driver is reloaded, a new copy of the driver, with
+ * fresh global values, etc., is loaded, and there will be a new call to
+ * shw_init().
+ *
+ * In user mode, the user's fsl_shw_register_user() call causes an open() event
+ * on the driver followed by a ioctl() with the registration information. Any
+ * subsequent API calls by the user are handled through the ioctl() function
+ * and shuffled off to the appropriate routine (or driver) for service. The
+ * fsl_shw_deregister_user() call by the user results in a close() function
+ * call on the driver.
+ *
+ * In kernel mode, the driver provides the functions fsl_shw_register_user(),
+ * fsl_shw_deregister_user(), fsl_shw_get_capabilities(), and
+ * fsl_shw_get_results(). Other parts of the API are provided by other
+ * drivers, if available, to support the cryptographic functions.
+ */
+
+#include "portable_os.h"
+#include "fsl_shw.h"
+#include "fsl_shw_keystore.h"
+
+#include "shw_internals.h"
+
+#ifdef FSL_HAVE_SCC2
+#include <linux/mxc_scc2_driver.h>
+#else
+#include <linux/mxc_scc_driver.h>
+#endif
+
+#ifdef SHW_DEBUG
+#include <diagnostic.h>
+#endif
+
+/******************************************************************************
+ *
+ * Function Declarations
+ *
+ *****************************************************************************/
+
+/* kernel interface functions */
+OS_DEV_INIT_DCL(shw_init);
+OS_DEV_SHUTDOWN_DCL(shw_shutdown);
+OS_DEV_IOCTL_DCL(shw_ioctl);
+OS_DEV_MMAP_DCL(shw_mmap);
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_smalloc);
+EXPORT_SYMBOL(fsl_shw_sfree);
+EXPORT_SYMBOL(fsl_shw_sstatus);
+EXPORT_SYMBOL(fsl_shw_diminish_perms);
+EXPORT_SYMBOL(do_scc_encrypt_region);
+EXPORT_SYMBOL(do_scc_decrypt_region);
+
+EXPORT_SYMBOL(do_system_keystore_slot_alloc);
+EXPORT_SYMBOL(do_system_keystore_slot_dealloc);
+EXPORT_SYMBOL(do_system_keystore_slot_load);
+EXPORT_SYMBOL(do_system_keystore_slot_encrypt);
+EXPORT_SYMBOL(do_system_keystore_slot_decrypt);
+#endif
+
+static os_error_code
+shw_handle_scc_sfree(fsl_shw_uco_t * user_ctx, uint32_t info);
+
+static os_error_code
+shw_handle_scc_sstatus(fsl_shw_uco_t * user_ctx, uint32_t info);
+
+static os_error_code
+shw_handle_scc_drop_perms(fsl_shw_uco_t * user_ctx, uint32_t info);
+
+static os_error_code
+shw_handle_scc_encrypt(fsl_shw_uco_t * user_ctx, uint32_t info);
+
+static os_error_code
+shw_handle_scc_decrypt(fsl_shw_uco_t * user_ctx, uint32_t info);
+
+#ifdef FSL_HAVE_SCC2
+static fsl_shw_return_t register_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base,
+ void *kernel_base);
+static fsl_shw_return_t deregister_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base);
+void *lookup_user_partition(fsl_shw_uco_t * user_ctx, uint32_t user_base);
+
+#endif /* FSL_HAVE_SCC2 */
+
+/******************************************************************************
+ *
+ * Global / Static Variables
+ *
+ *****************************************************************************/
+
+/*!
+ * Major node (user/device interaction value) of this driver.
+ */
+static int shw_major_node = SHW_MAJOR_NODE;
+
+/*!
+ * Flag to know whether the driver has been associated with its user device
+ * node (e.g. /dev/shw).
+ */
+static int shw_device_registered = 0;
+
+/*!
+ * OS-dependent handle used for registering user interface of a driver.
+ */
+static os_driver_reg_t reg_handle;
+
+/*!
+ * Linked List of registered users of the API
+ */
+fsl_shw_uco_t *user_list;
+
+/*!
+ * This is the lock for all user request pools. H/W component drivers may also
+ * use it for their own work queues.
+ */
+os_lock_t shw_queue_lock = NULL;
+
+/* This is the system keystore object */
+fsl_shw_kso_t system_keystore;
+
+#ifndef FSL_HAVE_SAHARA
+/*! Empty list of supported symmetric algorithms. */
+static fsl_shw_key_alg_t pf_syms[] = {
+};
+
+/*! Empty list of supported symmetric modes. */
+static fsl_shw_sym_mode_t pf_modes[] = {
+};
+
+/*! Empty list of supported hash algorithms. */
+static fsl_shw_hash_alg_t pf_hashes[] = {
+};
+#endif /* no Sahara */
+
+/*! This matches SHW capabilities... */
+static fsl_shw_pco_t cap = {
+ 1, 3, /* api version number - major & minor */
+ 2, 3, /* driver version number - major & minor */
+ sizeof(pf_syms) / sizeof(fsl_shw_key_alg_t), /* key alg count */
+ pf_syms, /* key alg list ptr */
+ sizeof(pf_modes) / sizeof(fsl_shw_sym_mode_t), /* sym mode count */
+ pf_modes, /* modes list ptr */
+ sizeof(pf_hashes) / sizeof(fsl_shw_hash_alg_t), /* hash alg count */
+ pf_hashes, /* hash list ptr */
+ /*
+ * The following table must be set to handle all values of key algorithm
+ * and sym mode, and be in the correct order..
+ */
+ { /* Stream, ECB, CBC, CTR */
+ {0, 0, 0, 0}
+ , /* HMAC */
+ {0, 0, 0, 0}
+ , /* AES */
+ {0, 0, 0, 0}
+ , /* DES */
+#ifdef FSL_HAVE_DRYICE
+ {0, 1, 1, 0}
+ , /* 3DES - ECB and CBC */
+#else
+ {0, 0, 0, 0}
+ , /* 3DES */
+#endif
+ {0, 0, 0, 0} /* ARC4 */
+ }
+ ,
+ 0, 0, /* SCC driver version */
+ 0, 0, 0, /* SCC version/capabilities */
+ {{0, 0}
+ }
+ , /* (filled in during OS_INIT) */
+};
+
+/* These are often handy */
+#ifndef FALSE
+/*! Not true. Guaranteed to be zero. */
+#define FALSE 0
+#endif
+#ifndef TRUE
+/*! True. Guaranteed to be non-zero. */
+#define TRUE 1
+#endif
+
+/******************************************************************************
+ *
+ * Function Implementations - Externally Accessible
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn shw_init() */
+/*****************************************************************************/
+/*!
+ * Initialize the driver.
+ *
+ * This routine is called during kernel init or module load (insmod).
+ *
+ * @return OS_ERROR_OK_S on success, errno on failure
+ */
+OS_DEV_INIT(shw_init)
+{
+ os_error_code error_code = OS_ERROR_NO_MEMORY_S; /* assume failure */
+ scc_config_t *shw_capabilities;
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW Driver: Loading");
+#endif
+
+ user_list = NULL;
+ shw_queue_lock = os_lock_alloc_init();
+
+ if (shw_queue_lock != NULL) {
+ error_code = shw_setup_user_driver_interaction();
+ if (error_code != OS_ERROR_OK_S) {
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("SHW Driver: Failed to setup user i/f: %d",
+ error_code);
+#endif
+ }
+ }
+
+ /* queue_lock not NULL */
+ /* Fill in the SCC portion of the capabilities object */
+ shw_capabilities = scc_get_configuration();
+ cap.scc_driver_major = shw_capabilities->driver_major_version;
+ cap.scc_driver_minor = shw_capabilities->driver_minor_version;
+ cap.scm_version = shw_capabilities->scm_version;
+ cap.smn_version = shw_capabilities->smn_version;
+ cap.block_size_bytes = shw_capabilities->block_size_bytes;
+
+#ifdef FSL_HAVE_SCC
+ cap.u.scc_info.black_ram_size_blocks =
+ shw_capabilities->black_ram_size_blocks;
+ cap.u.scc_info.red_ram_size_blocks =
+ shw_capabilities->red_ram_size_blocks;
+#elif defined(FSL_HAVE_SCC2)
+ cap.u.scc2_info.partition_size_bytes =
+ shw_capabilities->partition_size_bytes;
+ cap.u.scc2_info.partition_count = shw_capabilities->partition_count;
+#endif
+
+#if defined(FSL_HAVE_SCC2) || defined(FSL_HAVE_DRYICE)
+ if (error_code == OS_ERROR_OK_S) {
+ /* set up the system keystore, using the default keystore handler */
+ fsl_shw_init_keystore_default(&system_keystore);
+
+ if (fsl_shw_establish_keystore(NULL, &system_keystore)
+ == FSL_RETURN_OK_S) {
+ error_code = OS_ERROR_OK_S;
+ } else {
+ error_code = OS_ERROR_FAIL_S;
+ }
+
+ if (error_code != OS_ERROR_OK_S) {
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("Registering the system keystore failed with error"
+ " code: %d\n", error_code);
+#endif
+ }
+ }
+#endif /* FSL_HAVE_SCC2 */
+
+ if (error_code != OS_ERROR_OK_S) {
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: Driver initialization failed. %d",
+ error_code);
+#endif
+ shw_cleanup();
+ } else {
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: Driver initialization complete.");
+#endif
+ }
+
+ os_dev_init_return(error_code);
+} /* shw_init */
+
+/*****************************************************************************/
+/* fn shw_shutdown() */
+/*****************************************************************************/
+/*!
+ * Prepare driver for exit.
+ *
+ * This is called during @c rmmod when the driver is unloading or when the
+ * kernel is shutting down.
+ *
+ * Calls shw_cleanup() to do all work to undo anything that happened during
+ * initialization or while driver was running.
+ */
+OS_DEV_SHUTDOWN(shw_shutdown)
+{
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: shutdown called");
+#endif
+ shw_cleanup();
+
+ os_dev_shutdown_return(OS_ERROR_OK_S);
+} /* shw_shutdown */
+
+/*****************************************************************************/
+/* fn shw_cleanup() */
+/*****************************************************************************/
+/*!
+ * Prepare driver for shutdown.
+ *
+ * Remove the driver registration.
+ *
+ */
+static void shw_cleanup(void)
+{
+ if (shw_device_registered) {
+
+ /* Turn off the all association with OS */
+ os_driver_remove_registration(reg_handle);
+ shw_device_registered = 0;
+ }
+
+ if (shw_queue_lock != NULL) {
+ os_lock_deallocate(shw_queue_lock);
+ }
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW Driver: Cleaned up");
+#endif
+} /* shw_cleanup */
+
+/*****************************************************************************/
+/* fn shw_open() */
+/*****************************************************************************/
+/*!
+ * Handle @c open() call from user.
+ *
+ * @return OS_ERROR_OK_S on success (always!)
+ */
+OS_DEV_OPEN(shw_open)
+{
+ os_error_code status = OS_ERROR_OK_S;
+
+ os_dev_set_user_private(NULL); /* Make sure */
+
+ os_dev_open_return(status);
+} /* shw_open */
+
+/*****************************************************************************/
+/* fn shw_ioctl() */
+/*****************************************************************************/
+/*!
+ * Process an ioctl() request from user-mode API.
+ *
+ * This code determines which of the API requests the user has made and then
+ * sends the request off to the appropriate function.
+ *
+ * @return ioctl_return()
+ */
+OS_DEV_IOCTL(shw_ioctl)
+{
+ os_error_code code = OS_ERROR_FAIL_S;
+
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: IOCTL %d received", os_dev_get_ioctl_op());
+#endif
+ switch (os_dev_get_ioctl_op()) {
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_REGISTER_USER:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: register_user ioctl received");
+#endif
+ {
+ fsl_shw_uco_t *user_ctx =
+ os_alloc_memory(sizeof(*user_ctx), 0);
+
+ if (user_ctx == NULL) {
+ code = OS_ERROR_NO_MEMORY_S;
+ } else {
+ code =
+ init_uco(user_ctx,
+ (fsl_shw_uco_t *)
+ os_dev_get_ioctl_arg());
+ if (code == OS_ERROR_OK_S) {
+ os_dev_set_user_private(user_ctx);
+ } else {
+ os_free_memory(user_ctx);
+ }
+ }
+ }
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_DEREGISTER_USER:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: deregister_user ioctl received");
+#endif
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+ SHW_REMOVE_USER(user_ctx);
+ }
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_GET_RESULTS:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: get_results ioctl received");
+#endif
+ code = get_results(user_ctx,
+ (struct results_req *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_GET_CAPABILITIES:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: get_capabilities ioctl received");
+#endif
+ code = get_capabilities(user_ctx,
+ (fsl_shw_pco_t *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_GET_RANDOM:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: get_random ioctl received");
+#endif
+ code = get_random(user_ctx,
+ (struct get_random_req *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_ADD_ENTROPY:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: add_entropy ioctl received");
+#endif
+ code = add_entropy(user_ctx,
+ (struct add_entropy_req *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_DROP_PERMS:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: drop permissions ioctl received");
+#endif
+ code =
+ shw_handle_scc_drop_perms(user_ctx, os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_SSTATUS:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: sstatus ioctl received");
+#endif
+ code = shw_handle_scc_sstatus(user_ctx, os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_SFREE:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: sfree ioctl received");
+#endif
+ code = shw_handle_scc_sfree(user_ctx, os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_SCC_ENCRYPT:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: scc encrypt ioctl received");
+#endif
+ code = shw_handle_scc_encrypt(user_ctx, os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_SCC_DECRYPT:
+#ifdef SHW_DEBUG
+ LOG_KDIAG("SHW: scc decrypt ioctl received");
+#endif
+ code = shw_handle_scc_decrypt(user_ctx, os_dev_get_ioctl_arg());
+ break;
+
+ default:
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: Unexpected ioctl %d",
+ os_dev_get_ioctl_op());
+#endif
+ break;
+ }
+
+ os_dev_ioctl_return(code);
+}
+
+#ifdef FSL_HAVE_SCC2
+
+/*****************************************************************************/
+/* fn get_user_smid() */
+/*****************************************************************************/
+uint32_t get_user_smid(void *proc)
+{
+ /*
+ * A real implementation would have some way to handle signed applications
+ * which wouild be assigned distinct SMIDs. For the reference
+ * implementation, we show where this would be determined (here), but
+ * always provide a fixed answer, thus not separating users at all.
+ */
+
+ return 0x42eaae42;
+}
+
+/* user_base: userspace base address of the partition
+ * kernel_base: kernel mode base address of the partition
+ */
+static fsl_shw_return_t register_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base,
+ void *kernel_base)
+{
+ fsl_shw_spo_t *partition_info;
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ if (user_ctx == NULL) {
+ goto out;
+ }
+
+ partition_info = os_alloc_memory(sizeof(fsl_shw_spo_t), GFP_KERNEL);
+
+ if (partition_info == NULL) {
+ goto out;
+ }
+
+ /* stuff the partition info, then put it at the front of the chain */
+ partition_info->user_base = user_base;
+ partition_info->kernel_base = kernel_base;
+ partition_info->next = user_ctx->partition;
+
+ user_ctx->partition = (struct fsl_shw_spo_t *)partition_info;
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("partition with user_base=%p, kernel_base=%p registered.",
+ (void *)user_base, kernel_base);
+#endif
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+
+ return ret;
+}
+
+/* if the partition is in the users list, remove it */
+static fsl_shw_return_t deregister_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base)
+{
+ fsl_shw_spo_t *curr = (fsl_shw_spo_t *) user_ctx->partition;
+ fsl_shw_spo_t *last = (fsl_shw_spo_t *) user_ctx->partition;
+
+ while (curr != NULL) {
+ if (curr->user_base == user_base) {
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("deregister_user_partition: partition with "
+ "user_base=%p, kernel_base=%p deregistered.\n",
+ (void *)curr->user_base, curr->kernel_base);
+#endif
+
+ if (last == curr) {
+ user_ctx->partition = curr->next;
+ os_free_memory(curr);
+ return FSL_RETURN_OK_S;
+ } else {
+ last->next = curr->next;
+ os_free_memory(curr);
+ return FSL_RETURN_OK_S;
+ }
+ }
+ last = curr;
+ curr = (fsl_shw_spo_t *) curr->next;
+ }
+
+ return FSL_RETURN_ERROR_S;
+}
+
+/* Find the kernel-mode address of the partition.
+ * This can then be passed to the SCC functions.
+ */
+void *lookup_user_partition(fsl_shw_uco_t * user_ctx, uint32_t user_base)
+{
+ /* search through the partition chain to find one that matches the user base
+ * address.
+ */
+ fsl_shw_spo_t *curr = (fsl_shw_spo_t *) user_ctx->partition;
+
+ while (curr != NULL) {
+ if (curr->user_base == user_base) {
+ return curr->kernel_base;
+ }
+ curr = (fsl_shw_spo_t *) curr->next;
+ }
+ return NULL;
+}
+
+#endif /* FSL_HAVE_SCC2 */
+
+/*!
+*******************************************************************************
+* This function implements the smalloc() function for userspace programs, by
+* making a call to the SCC2 mmap() function that acquires a region of secure
+* memory on behalf of the user, and then maps it into the users memory space.
+* Currently, the only memory size supported is that of a single SCC2 partition.
+* Requests for other sized memory regions will fail.
+*/
+OS_DEV_MMAP(shw_mmap)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+
+#ifdef FSL_HAVE_SCC2
+ {
+ scc_return_t scc_ret;
+ fsl_shw_return_t fsl_ret;
+ uint32_t partition_registered = FALSE;
+
+ uint32_t user_base;
+ void *partition_base;
+ uint32_t smid;
+ scc_config_t *scc_configuration;
+
+ int part_no = -1;
+ uint32_t part_phys;
+
+ fsl_shw_uco_t *user_ctx =
+ (fsl_shw_uco_t *) os_dev_get_user_private();
+
+ /* Make sure that the user context is valid */
+ if (user_ctx == NULL) {
+ user_ctx =
+ os_alloc_memory(sizeof(*user_ctx), GFP_KERNEL);
+
+ if (user_ctx == NULL) {
+ status = OS_ERROR_NO_MEMORY_S;
+ goto out;
+ }
+ fsl_shw_register_user(user_ctx);
+ os_dev_set_user_private(user_ctx);
+ }
+
+ /* Determine the size of a secure partition */
+ scc_configuration = scc_get_configuration();
+
+ /* Check that the memory size requested is equal to the partition
+ * size, and that the requested destination is on a page boundary.
+ */
+ if (((os_mmap_user_base() % PAGE_SIZE) != 0) ||
+ (os_mmap_memory_size() !=
+ scc_configuration->partition_size_bytes)) {
+ status = OS_ERROR_BAD_ARG_S;
+ goto out;
+ }
+
+ /* Retrieve the SMID associated with the user */
+ smid = get_user_smid(user_ctx->process);
+
+ /* Attempt to allocate a secure partition */
+ scc_ret =
+ scc_allocate_partition(smid, &part_no, &partition_base,
+ &part_phys);
+ if (scc_ret != SCC_RET_OK) {
+ pr_debug
+ ("SCC mmap() request failed to allocate partition;"
+ " error %d\n", status);
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ pr_debug("scc_mmap() acquired partition %d at %08x\n",
+ part_no, part_phys);
+
+ /* Record partition info in the user context */
+ user_base = os_mmap_user_base();
+ fsl_ret =
+ register_user_partition(user_ctx, user_base,
+ partition_base);
+
+ if (fsl_ret != FSL_RETURN_OK_S) {
+ pr_debug
+ ("SCC mmap() request failed to register partition with user"
+ " context, error: %d\n", fsl_ret);
+ status = OS_ERROR_FAIL_S;
+ }
+
+ partition_registered = TRUE;
+
+ status = map_user_memory(os_mmap_memory_ctx(), part_phys,
+ os_mmap_memory_size());
+
+#ifdef SHW_DEBUG
+ if (status == OS_ERROR_OK_S) {
+ LOG_KDIAG_ARGS
+ ("Partition allocated: user_base=%p, partition_base=%p.",
+ (void *)user_base, partition_base);
+ }
+#endif
+
+ out:
+ /* If there is an error it has to be handled here */
+ if (status != OS_ERROR_OK_S) {
+ /* if the partition was registered with the user, unregister it. */
+ if (partition_registered == TRUE) {
+ deregister_user_partition(user_ctx, user_base);
+ }
+
+ /* if the partition was allocated, deallocate it */
+ if (partition_base != NULL) {
+ scc_release_partition(partition_base);
+ }
+ }
+ }
+#endif /* FSL_HAVE_SCC2 */
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn shw_release() */
+/*****************************************************************************/
+/*!
+ * Handle @c close() call from user.
+ * This is a Linux device driver interface routine.
+ *
+ * @return OS_ERROR_OK_S on success (always!)
+ */
+OS_DEV_CLOSE(shw_release)
+{
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+ os_error_code code = OS_ERROR_OK_S;
+
+ if (user_ctx != NULL) {
+
+ fsl_shw_deregister_user(user_ctx);
+ os_free_memory(user_ctx);
+ os_dev_set_user_private(NULL);
+
+ }
+
+ os_dev_close_return(code);
+} /* shw_release */
+
+/*****************************************************************************/
+/* fn shw_user_callback() */
+/*****************************************************************************/
+/*!
+ * FSL SHW User callback function.
+ *
+ * This function is set in the kernel version of the user context as the
+ * callback function when the user mode user wants a callback. Its job is to
+ * inform the user process that results (may) be available. It does this by
+ * sending a SIGUSR2 signal which is then caught by the user-mode FSL SHW
+ * library.
+ *
+ * @param user_ctx Kernel version of uco associated with the request.
+ *
+ * @return void
+ */
+static void shw_user_callback(fsl_shw_uco_t * user_ctx)
+{
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: Signalling callback user process for context %p\n",
+ user_ctx);
+#endif
+ os_send_signal(user_ctx->process, SIGUSR2);
+}
+
+/*****************************************************************************/
+/* fn setup_user_driver_interaction() */
+/*****************************************************************************/
+/*!
+ * Register the driver with the kernel as the driver for shw_major_node. Note
+ * that this value may be zero, in which case the major number will be assigned
+ * by the OS. shw_major_node is never modified.
+ *
+ * The open(), ioctl(), and close() handles for the driver ned to be registered
+ * with the kernel. Upon success, shw_device_registered will be true;
+ *
+ * @return OS_ERROR_OK_S on success, or an os err code
+ */
+static os_error_code shw_setup_user_driver_interaction(void)
+{
+ os_error_code error_code;
+
+ os_driver_init_registration(reg_handle);
+ os_driver_add_registration(reg_handle, OS_FN_OPEN,
+ OS_DEV_OPEN_REF(shw_open));
+ os_driver_add_registration(reg_handle, OS_FN_IOCTL,
+ OS_DEV_IOCTL_REF(shw_ioctl));
+ os_driver_add_registration(reg_handle, OS_FN_CLOSE,
+ OS_DEV_CLOSE_REF(shw_release));
+ os_driver_add_registration(reg_handle, OS_FN_MMAP,
+ OS_DEV_MMAP_REF(shw_mmap));
+ error_code = os_driver_complete_registration(reg_handle, shw_major_node,
+ SHW_DRIVER_NAME);
+
+ if (error_code != OS_ERROR_OK_S) {
+ /* failure ! */
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW Driver: register device driver failed: %d",
+ error_code);
+#endif
+ } else { /* success */
+ shw_device_registered = TRUE;
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW Driver: Major node is %d\n",
+ os_driver_get_major(reg_handle));
+#endif
+ }
+
+ return error_code;
+} /* shw_setup_user_driver_interaction */
+
+/******************************************************************/
+/* User Mode Support */
+/******************************************************************/
+
+/*!
+ * Initialze kernel User Context Object from User-space version.
+ *
+ * Copy user UCO into kernel UCO, set flags and fields for operation
+ * within kernel space. Add user to driver's list of users.
+ *
+ * @param user_ctx Pointer to kernel space UCO
+ * @param user_mode_uco User pointer to user space version
+ *
+ * @return os_error_code
+ */
+static os_error_code init_uco(fsl_shw_uco_t * user_ctx, void *user_mode_uco)
+{
+ os_error_code code;
+
+ code = os_copy_from_user(user_ctx, user_mode_uco, sizeof(*user_ctx));
+ if (code == OS_ERROR_OK_S) {
+ user_ctx->flags |= FSL_UCO_USERMODE_USER;
+ user_ctx->result_pool.head = NULL;
+ user_ctx->result_pool.tail = NULL;
+ user_ctx->process = os_get_process_handle();
+ user_ctx->callback = shw_user_callback;
+ SHW_ADD_USER(user_ctx);
+ }
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: init uco returning %d (flags %x)",
+ code, user_ctx->flags);
+#endif
+
+ return code;
+}
+
+/*!
+ * Copy array from kernel to user space.
+ *
+ * This routine will check bounds before trying to copy, and return failure
+ * on bounds violation or error during the copy.
+ *
+ * @param userloc Location in userloc to place data. If NULL, the function
+ * will do nothing (except return NULL).
+ * @param userend Address beyond allowed copy region at @c userloc.
+ * @param data_start Location of data to be copied
+ * @param element_size sizeof() an element
+ * @param element_count Number of elements of size element_size to copy.
+ * @return New value of userloc, or NULL if there was an error.
+ */
+inline static void *copy_array(void *userloc, void *userend, void *data_start,
+ unsigned element_size, unsigned element_count)
+{
+ unsigned byte_count = element_size * element_count;
+
+ if ((userloc == NULL) || (userend == NULL)
+ || ((userloc + byte_count) >= userend) ||
+ (copy_to_user(userloc, data_start, byte_count) != OS_ERROR_OK_S)) {
+ userloc = NULL;
+ } else {
+ userloc += byte_count;
+ }
+
+ return userloc;
+}
+
+/*!
+ * Send an FSL SHW API return code up into the user-space request structure.
+ *
+ * @param user_header User address of request block / request header
+ * @param result_code The FSL SHW API code to be placed at header.code
+ *
+ * @return an os_error_code
+ *
+ * NOTE: This function assumes that the shw_req_header is at the beginning of
+ * each request structure.
+ */
+inline static os_error_code copy_fsl_code(void *user_header,
+ fsl_shw_return_t result_code)
+{
+ return os_copy_to_user(user_header +
+ offsetof(struct shw_req_header, code),
+ &result_code, sizeof(result_code));
+}
+
+static os_error_code shw_handle_scc_drop_perms(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ scc_return_t scc_ret;
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+
+ status =
+ os_copy_from_user(&partition_info, (void *)info,
+ sizeof(partition_info));
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base = lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef SHW_DEBUG
+ LOG_KDIAG("_scc_drop_perms(): failed to find partition\n");
+#endif
+ goto out;
+ }
+
+ /* call scc driver to perform the drop */
+ scc_ret = scc_diminish_permissions(kernel_base,
+ partition_info.permissions);
+ if (scc_ret == SCC_RET_OK) {
+ status = OS_ERROR_OK_S;
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code shw_handle_scc_sstatus(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+
+ status = os_copy_from_user(&partition_info,
+ (void *)info, sizeof(partition_info));
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base = lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef SHW_DEBUG
+ LOG_KDIAG("Failed to find partition\n");
+#endif
+ goto out;
+ }
+
+ /* Call the SCC driver to ask about the partition status */
+ partition_info.status = scc_partition_status(kernel_base);
+
+ /* and copy the structure out */
+ status = os_copy_to_user((void *)info,
+ &partition_info, sizeof(partition_info));
+
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code shw_handle_scc_sfree(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+ int ret;
+
+ status = os_copy_from_user(&partition_info,
+ (void *)info,
+ sizeof(partition_info));
+
+ /* check that the copy was successful */
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base =
+ lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef SHW_DEBUG
+ LOG_KDIAG("failed to find partition\n");
+#endif /*SHW_DEBUG */
+ goto out;
+ }
+
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ ret = unmap_user_memory(partition_info.user_base, 8192);
+
+ /* If the memory was successfully released */
+ if (ret == OS_ERROR_OK_S) {
+
+ /* release the partition */
+ scc_release_partition(kernel_base);
+
+ /* and remove it from the users context */
+ deregister_user_partition(user_ctx,
+ partition_info.user_base);
+
+ status = OS_ERROR_OK_S;
+
+ } else {
+#ifdef SHW_DEBUG
+ LOG_KDIAG("do_munmap not successful!");
+#endif
+ }
+
+ }
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code shw_handle_scc_encrypt(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_FAIL_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ fsl_shw_return_t retval;
+ scc_region_t region_info;
+ void *page_ctx = NULL;
+ void *black_addr = NULL;
+ void *partition_base = NULL;
+ scc_config_t *scc_configuration;
+
+ status =
+ os_copy_from_user(&region_info, (void *)info,
+ sizeof(region_info));
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ partition_base = lookup_user_partition(user_ctx,
+ region_info.
+ partition_base);
+
+ if (partition_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef SHW_DEBUG
+ LOG_KDIAG("failed to find secure partition\n");
+#endif
+ goto out;
+ }
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (region_info.offset + region_info.length >
+ scc_configuration->partition_size_bytes) {
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ /* wire down black_data */
+ black_addr = wire_user_memory(region_info.black_data,
+ region_info.length, &page_ctx);
+
+ if (black_addr == NULL) {
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ retval =
+ do_scc_encrypt_region(NULL, partition_base,
+ region_info.offset,
+ region_info.length, black_addr,
+ region_info.IV,
+ region_info.cypher_mode);
+
+ if (retval == FSL_RETURN_OK_S) {
+ status = OS_ERROR_OK_S;
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+
+ /* release black data */
+ unwire_user_memory(&page_ctx);
+ }
+ out:
+
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code shw_handle_scc_decrypt(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_FAIL_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ fsl_shw_return_t retval;
+ scc_region_t region_info;
+ void *page_ctx = NULL;
+ void *black_addr;
+ void *partition_base;
+ scc_config_t *scc_configuration;
+
+ status =
+ os_copy_from_user(&region_info, (void *)info,
+ sizeof(region_info));
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("partition_base: %p, offset: %i, length: %i, black data: %p",
+ (void *)region_info.partition_base, region_info.offset,
+ region_info.length, (void *)region_info.black_data);
+#endif
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ partition_base = lookup_user_partition(user_ctx,
+ region_info.
+ partition_base);
+
+ if (partition_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef SHW_DEBUG
+ LOG_KDIAG("failed to find partition\n");
+#endif
+ goto out;
+ }
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (region_info.offset + region_info.length >
+ scc_configuration->partition_size_bytes) {
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ /* wire down black_data */
+ black_addr = wire_user_memory(region_info.black_data,
+ region_info.length, &page_ctx);
+
+ if (black_addr == NULL) {
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ retval =
+ do_scc_decrypt_region(NULL, partition_base,
+ region_info.offset,
+ region_info.length, black_addr,
+ region_info.IV,
+ region_info.cypher_mode);
+
+ if (retval == FSL_RETURN_OK_S) {
+ status = OS_ERROR_OK_S;
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+
+ /* release black data */
+ unwire_user_memory(&page_ctx);
+ }
+ out:
+
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+fsl_shw_return_t do_system_keystore_slot_alloc(fsl_shw_uco_t * user_ctx,
+ uint32_t key_length,
+ uint64_t ownerid,
+ uint32_t * slot)
+{
+ (void)user_ctx;
+ return keystore_slot_alloc(&system_keystore, key_length, ownerid, slot);
+}
+
+fsl_shw_return_t do_system_keystore_slot_dealloc(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot)
+{
+ (void)user_ctx;
+ return keystore_slot_dealloc(&system_keystore, ownerid, slot);
+}
+
+fsl_shw_return_t do_system_keystore_slot_load(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ const uint8_t * key,
+ uint32_t key_length)
+{
+ (void)user_ctx;
+ return keystore_slot_load(&system_keystore, ownerid, slot,
+ (void *)key, key_length);
+}
+
+fsl_shw_return_t do_system_keystore_slot_encrypt(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ uint8_t * black_data)
+{
+ (void)user_ctx;
+ return keystore_slot_encrypt(NULL, &system_keystore, ownerid,
+ slot, key_length, black_data);
+}
+
+fsl_shw_return_t do_system_keystore_slot_decrypt(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t * black_data)
+{
+ (void)user_ctx;
+ return keystore_slot_decrypt(NULL, &system_keystore, ownerid,
+ slot, key_length, black_data);
+}
+
+fsl_shw_return_t do_system_keystore_slot_read(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ uint8_t * key_data)
+{
+ (void)user_ctx;
+
+ return keystore_slot_read(&system_keystore, ownerid,
+ slot, key_length, key_data);
+}
+
+/*!
+ * Handle user-mode Get Capabilities request
+ *
+ * Right now, this function can only have a failure if the user has failed to
+ * provide a pointer to a location in user space with enough room to hold the
+ * fsl_shw_pco_t structure and any associated data. It will treat this failure
+ * as an ioctl failure and return an ioctl error code, instead of treating it
+ * as an API failure.
+ *
+ * @param user_ctx The kernel version of user's context
+ * @param user_mode_pco_request Pointer to user-space request
+ *
+ * @return an os_error_code
+ */
+static os_error_code get_capabilities(fsl_shw_uco_t * user_ctx,
+ void *user_mode_pco_request)
+{
+ os_error_code code;
+ struct capabilities_req req;
+ fsl_shw_pco_t local_cap;
+
+ memcpy(&local_cap, &cap, sizeof(cap));
+ /* Initialize pointers to out-of-struct arrays */
+ local_cap.sym_algorithms = NULL;
+ local_cap.sym_modes = NULL;
+ local_cap.sym_modes = NULL;
+
+ code = os_copy_from_user(&req, user_mode_pco_request, sizeof(req));
+ if (code == OS_ERROR_OK_S) {
+ void *endcap;
+ void *user_bounds;
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHE: Received get_cap request: 0x%p/%u/0x%x",
+ req.capabilities, req.size,
+ sizeof(fsl_shw_pco_t));
+#endif
+ endcap = req.capabilities + 1; /* point to end of structure */
+ user_bounds = (void *)req.capabilities + req.size; /* end of area */
+
+ /* First verify that request is big enough for the main structure */
+ if (endcap >= user_bounds) {
+ endcap = NULL; /* No! */
+ }
+
+ /* Copy any Symmetric Algorithm suppport */
+ if (cap.sym_algorithm_count != 0) {
+ local_cap.sym_algorithms = endcap;
+ endcap =
+ copy_array(endcap, user_bounds, cap.sym_algorithms,
+ sizeof(fsl_shw_key_alg_t),
+ cap.sym_algorithm_count);
+ }
+
+ /* Copy any Symmetric Modes suppport */
+ if (cap.sym_mode_count != 0) {
+ local_cap.sym_modes = endcap;
+ endcap = copy_array(endcap, user_bounds, cap.sym_modes,
+ sizeof(fsl_shw_sym_mode_t),
+ cap.sym_mode_count);
+ }
+
+ /* Copy any Hash Algorithm suppport */
+ if (cap.hash_algorithm_count != 0) {
+ local_cap.hash_algorithms = endcap;
+ endcap =
+ copy_array(endcap, user_bounds, cap.hash_algorithms,
+ sizeof(fsl_shw_hash_alg_t),
+ cap.hash_algorithm_count);
+ }
+
+ /* Now copy up the (possibly modified) main structure */
+ if (endcap != NULL) {
+ code =
+ os_copy_to_user(req.capabilities, &local_cap,
+ sizeof(cap));
+ }
+
+ if (endcap == NULL) {
+ code = OS_ERROR_BAD_ADDRESS_S;
+ }
+
+ /* And return the FSL SHW code in the request structure. */
+ if (code == OS_ERROR_OK_S) {
+ code =
+ copy_fsl_code(user_mode_pco_request,
+ FSL_RETURN_OK_S);
+ }
+ }
+
+ /* code may already be set to an error. This is another error case. */
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: get capabilities returning %d", code);
+#endif
+
+ return code;
+}
+
+/*!
+ * Handle user-mode Get Results request
+ *
+ * Get arguments from user space into kernel space, then call
+ * fsl_shw_get_results, and then copy its return code and any results from
+ * kernel space back to user space.
+ *
+ * @param user_ctx The kernel version of user's context
+ * @param user_mode_results_req Pointer to user-space request
+ *
+ * @return an os_error_code
+ */
+static os_error_code get_results(fsl_shw_uco_t * user_ctx,
+ void *user_mode_results_req)
+{
+ os_error_code code;
+ struct results_req req;
+ fsl_shw_result_t *results = NULL;
+ int loop;
+
+ code = os_copy_from_user(&req, user_mode_results_req, sizeof(req));
+ loop = 0;
+
+ if (code == OS_ERROR_OK_S) {
+ results = os_alloc_memory(req.requested * sizeof(*results), 0);
+ if (results == NULL) {
+ code = OS_ERROR_NO_MEMORY_S;
+ }
+ }
+
+ if (code == OS_ERROR_OK_S) {
+ fsl_shw_return_t err =
+ fsl_shw_get_results(user_ctx, req.requested,
+ results, &req.actual);
+
+ /* Send API return code up to user. */
+ code = copy_fsl_code(user_mode_results_req, err);
+
+ if ((code == OS_ERROR_OK_S) && (err == FSL_RETURN_OK_S)) {
+ /* Now copy up the result count */
+ code = os_copy_to_user(user_mode_results_req
+ + offsetof(struct results_req,
+ actual), &req.actual,
+ sizeof(req.actual));
+ if ((code == OS_ERROR_OK_S) && (req.actual != 0)) {
+ /* now copy up the results... */
+ code = os_copy_to_user(req.results, results,
+ req.actual *
+ sizeof(*results));
+ }
+ }
+ }
+
+ if (results != NULL) {
+ os_free_memory(results);
+ }
+
+ return code;
+}
+
+/*!
+ * Process header of user-mode request.
+ *
+ * Mark header as User Mode request. Update UCO's flags and reference fields
+ * with current versions from the header.
+ *
+ * @param user_ctx Pointer to kernel version of UCO.
+ * @param hdr Pointer to common part of user request.
+ *
+ * @return void
+ */
+inline static void process_hdr(fsl_shw_uco_t * user_ctx,
+ struct shw_req_header *hdr)
+{
+ hdr->flags |= FSL_UCO_USERMODE_USER;
+ user_ctx->flags = hdr->flags;
+ user_ctx->user_ref = hdr->user_ref;
+
+ return;
+}
+
+/*!
+ * Handle user-mode Get Random request
+ *
+ * @param user_ctx The kernel version of user's context
+ * @param user_mode_get_random_req Pointer to user-space request
+ *
+ * @return an os_error_code
+ */
+static os_error_code get_random(fsl_shw_uco_t * user_ctx,
+ void *user_mode_get_random_req)
+{
+ os_error_code code;
+ struct get_random_req req;
+
+ code = os_copy_from_user(&req, user_mode_get_random_req, sizeof(req));
+ if (code == OS_ERROR_OK_S) {
+ process_hdr(user_ctx, &req.hdr);
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("SHW: get_random() for %d bytes in %sblocking mode",
+ req.size,
+ (req.hdr.flags & FSL_UCO_BLOCKING_MODE) ? "" : "non-");
+#endif
+ req.hdr.code =
+ fsl_shw_get_random(user_ctx, req.size, req.random);
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("SHW: get_random() returning %d", req.hdr.code);
+#endif
+
+ /* Copy FSL function status back to user */
+ code = copy_fsl_code(user_mode_get_random_req, req.hdr.code);
+ }
+
+ return code;
+}
+
+/*!
+ * Handle user-mode Add Entropy request
+ *
+ * @param user_ctx Pointer to the kernel version of user's context
+ * @param user_mode_add_entropy_req Address of user-space request
+ *
+ * @return an os_error_code
+ */
+static os_error_code add_entropy(fsl_shw_uco_t * user_ctx,
+ void *user_mode_add_entropy_req)
+{
+ os_error_code code;
+ struct add_entropy_req req;
+ uint8_t *local_buffer = NULL;
+
+ code = os_copy_from_user(&req, user_mode_add_entropy_req, sizeof(req));
+ if (code == OS_ERROR_OK_S) {
+ local_buffer = os_alloc_memory(req.size, 0); /* for random */
+ if (local_buffer != NULL) {
+ code =
+ os_copy_from_user(local_buffer, req.entropy,
+ req.size);
+ }
+ if (code == OS_ERROR_OK_S) {
+ req.hdr.code = fsl_shw_add_entropy(user_ctx, req.size,
+ local_buffer);
+
+ code =
+ copy_fsl_code(user_mode_add_entropy_req,
+ req.hdr.code);
+ }
+ }
+
+ if (local_buffer != NULL) {
+ os_free_memory(local_buffer);
+ }
+
+ return code;
+}
+
+/******************************************************************/
+/* End User Mode Support */
+/******************************************************************/
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_register_user);
+#endif
+/* REQ-S2LRD-PINTFC-API-GEN-004 */
+/*
+ * Handle user registration.
+ *
+ * @param user_ctx The user context for the registration.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_register_user(fsl_shw_uco_t * user_ctx)
+{
+ fsl_shw_return_t code = FSL_RETURN_INTERNAL_ERROR_S;
+
+ if ((user_ctx->flags & FSL_UCO_BLOCKING_MODE) &&
+ (user_ctx->flags & FSL_UCO_CALLBACK_MODE)) {
+ code = FSL_RETURN_BAD_FLAG_S;
+ goto error_exit;
+ } else if (user_ctx->pool_size == 0) {
+ code = FSL_RETURN_NO_RESOURCE_S;
+ goto error_exit;
+ } else {
+ user_ctx->result_pool.head = NULL;
+ user_ctx->result_pool.tail = NULL;
+ SHW_ADD_USER(user_ctx);
+ code = FSL_RETURN_OK_S;
+ }
+
+ error_exit:
+ return code;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_deregister_user);
+#endif
+/* REQ-S2LRD-PINTFC-API-GEN-005 */
+/*!
+ * Destroy the association between the the user and the provider of the API.
+ *
+ * @param user_ctx The user context which is no longer needed.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_deregister_user(fsl_shw_uco_t * user_ctx)
+{
+ shw_queue_entry_t *finished_request;
+ fsl_shw_return_t ret = FSL_RETURN_OK_S;
+
+ /* Clean up what we find in result pool. */
+ do {
+ os_lock_context_t lock_context;
+ os_lock_save_context(shw_queue_lock, lock_context);
+ finished_request = user_ctx->result_pool.head;
+
+ if (finished_request != NULL) {
+ SHW_QUEUE_REMOVE_ENTRY(&user_ctx->result_pool,
+ finished_request);
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+ os_free_memory(finished_request);
+ } else {
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+ }
+ } while (finished_request != NULL);
+
+#ifdef FSL_HAVE_SCC2
+ {
+ fsl_shw_spo_t *partition;
+ struct mm_struct *mm = current->mm;
+
+ while ((user_ctx->partition != NULL)
+ && (ret == FSL_RETURN_OK_S)) {
+
+ partition = user_ctx->partition;
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS
+ ("Found an abandoned secure partition at %p, releasing",
+ partition);
+#endif
+
+ /* It appears that current->mm is not valid if this is called from a
+ * close routine (perhaps only if the program raised an exception that
+ * caused it to close?) If that is the case, then still free the
+ * partition, but do not remove it from the memory space (dangerous?)
+ */
+
+ if (mm == NULL) {
+#ifdef SHW_DEBUG
+ LOG_KDIAG
+ ("Warning: no mm structure found, not unmapping "
+ "partition from user memory\n");
+#endif
+ } else {
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ /* Note that this assumes a single memory partition */
+ unmap_user_memory(partition->user_base, 8192);
+ }
+
+ /* If the memory was successfully released */
+ if (ret == OS_ERROR_OK_S) {
+ /* release the partition */
+ scc_release_partition(partition->kernel_base);
+
+ /* and remove it from the users context */
+ deregister_user_partition(user_ctx,
+ partition->user_base);
+
+ ret = FSL_RETURN_OK_S;
+ } else {
+ ret = FSL_RETURN_ERROR_S;
+
+ goto out;
+ }
+ }
+ }
+ out:
+#endif /* FSL_HAVE_SCC2 */
+
+ SHW_REMOVE_USER(user_ctx);
+
+ return ret;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_get_results);
+#endif
+/* REQ-S2LRD-PINTFC-API-GEN-006 */
+fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t * user_ctx,
+ unsigned result_size,
+ fsl_shw_result_t results[],
+ unsigned *result_count)
+{
+ shw_queue_entry_t *finished_request;
+ unsigned loop = 0;
+
+ do {
+ os_lock_context_t lock_context;
+
+ /* Protect state of user's result pool until we have retrieved and
+ * remove the first entry, or determined that the pool is empty. */
+ os_lock_save_context(shw_queue_lock, lock_context);
+ finished_request = user_ctx->result_pool.head;
+
+ if (finished_request != NULL) {
+ uint32_t code = 0;
+
+ SHW_QUEUE_REMOVE_ENTRY(&user_ctx->result_pool,
+ finished_request);
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+
+ results[loop].user_ref = finished_request->user_ref;
+ results[loop].code = finished_request->code;
+ results[loop].detail1 = 0;
+ results[loop].detail2 = 0;
+ results[loop].user_req =
+ finished_request->user_mode_req;
+ if (finished_request->postprocess != NULL) {
+ code =
+ finished_request->
+ postprocess(finished_request);
+ }
+
+ results[loop].code = finished_request->code;
+ os_free_memory(finished_request);
+ if (code == 0) {
+ loop++;
+ }
+ } else { /* finished_request is NULL */
+ /* pool is empty */
+ os_unlock_restore_context(shw_queue_lock, lock_context);
+ }
+
+ } while ((loop < result_size) && (finished_request != NULL));
+
+ *result_count = loop;
+
+ return FSL_RETURN_OK_S;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_get_capabilities);
+#endif
+fsl_shw_pco_t *fsl_shw_get_capabilities(fsl_shw_uco_t * user_ctx)
+{
+
+ /* Unused */
+ (void)user_ctx;
+
+ return &cap;
+}
+
+#if !(defined(FSL_HAVE_SAHARA) || defined(FSL_HAVE_RNGA) \
+ || defined(FSL_HAVE_RNGB) || defined(FSL_HAVE_RNGC))
+
+#if defined(LINUX_VERSION_CODE)
+EXPORT_SYMBOL(fsl_shw_get_random);
+#endif
+fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)length;
+ (void)data;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#if defined(LINUX_VERSION_CODE)
+EXPORT_SYMBOL(fsl_shw_add_entropy);
+#endif
+fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)length;
+ (void)data;
+
+ return FSL_RETURN_ERROR_S;
+}
+#endif
+
+#if !defined(FSL_HAVE_DRYICE) && !defined(FSL_HAVE_SAHARA2)
+#if 0
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_symmetric_decrypt);
+#endif
+fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct, uint8_t * pt)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)sym_ctx;
+ (void)length;
+ (void)ct;
+ (void)pt;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_symmetric_encrypt);
+#endif
+fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt, uint8_t * ct)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)sym_ctx;
+ (void)length;
+ (void)pt;
+ (void)ct;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+/* DryIce support provided in separate file */
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_establish_key);
+#endif
+fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)establish_type;
+ (void)key;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_extract_key);
+#endif
+fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)covered_key;
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_release_key);
+#endif
+fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+
+ return FSL_RETURN_ERROR_S;
+}
+#endif
+#endif /* SAHARA or DRYICE */
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_hash);
+#endif
+#if !defined(FSL_HAVE_SAHARA)
+fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
+ fsl_shw_hco_t * hash_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)hash_ctx;
+ (void)msg;
+ (void)length;
+ (void)result;
+ (void)result_len;
+
+ return ret;
+}
+#endif
+
+#ifndef FSL_HAVE_SAHARA
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_hmac_precompute);
+#endif
+
+fsl_shw_return_t fsl_shw_hmac_precompute(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx)
+{
+ fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)hmac_ctx;
+
+ return status;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_hmac);
+#endif
+
+fsl_shw_return_t fsl_shw_hmac(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)key_info;
+ (void)hmac_ctx;
+ (void)msg;
+ (void)length;
+ (void)result;
+ (void)result_len;
+
+ return status;
+}
+#endif
+
+/*!
+ * Call the proper function to encrypt a region of encrypted secure memory
+ *
+ * @brief
+ *
+ * @param user_ctx User context of the partition owner (NULL in kernel)
+ * @param partition_base Base address (physical) of the partition
+ * @param offset_bytes Offset from base address of the data to be encrypted
+ * @param byte_count Length of the message (bytes)
+ * @param black_data Pointer to where the encrypted data is stored
+ * @param IV IV to use for encryption
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #fsl_shw_cypher_mode_t
+ *
+ * @return status
+ */
+fsl_shw_return_t
+do_scc_encrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode)
+{
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+#ifdef FSL_HAVE_SCC2
+
+ scc_return_t scc_ret;
+
+#ifdef SHW_DEBUG
+ uint32_t *owner_32 = (uint32_t *) & (owner_id);
+
+ LOG_KDIAG_ARGS
+ ("partition base: %p, offset: %i, count: %i, black data: %p\n",
+ partition_base, offset_bytes, byte_count, (void *)black_data);
+
+ LOG_KDIAG_ARGS("Owner ID: %08x%08x\n", owner_32[1], owner_32[0]);
+#endif /* SHW_DEBUG */
+ (void)user_ctx;
+
+ os_cache_flush_range(black_data, byte_count);
+
+ scc_ret =
+ scc_encrypt_region((uint32_t) partition_base, offset_bytes,
+ byte_count, __virt_to_phys(black_data), IV,
+ cypher_mode);
+
+ if (scc_ret == SCC_RET_OK) {
+ retval = FSL_RETURN_OK_S;
+ } else {
+ retval = FSL_RETURN_ERROR_S;
+ }
+
+ /* The SCC2 DMA engine should have written to the black ram, so we need to
+ * invalidate that region of memory. Note that the red ram is not an
+ * because it is mapped with the cache disabled.
+ */
+ os_cache_inv_range(black_data, byte_count);
+
+#endif /* FSL_HAVE_SCC2 */
+ return retval;
+}
+
+/*!
+ * Call the proper function to decrypt a region of encrypted secure memory
+ *
+ * @brief
+ *
+ * @param user_ctx User context of the partition owner (NULL in kernel)
+ * @param partition_base Base address (physical) of the partition
+ * @param offset_bytes Offset from base address that the decrypted data
+ * shall be placed
+ * @param byte_count Length of the message (bytes)
+ * @param black_data Pointer to where the encrypted data is stored
+ * @param IV IV to use for decryption
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #fsl_shw_cypher_mode_t
+ *
+ * @return status
+ */
+fsl_shw_return_t
+do_scc_decrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, const uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode)
+{
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+
+#ifdef FSL_HAVE_SCC2
+
+ scc_return_t scc_ret;
+
+#ifdef SHW_DEBUG
+ uint32_t *owner_32 = (uint32_t *) & (owner_id);
+
+ LOG_KDIAG_ARGS
+ ("partition base: %p, offset: %i, count: %i, black data: %p\n",
+ partition_base, offset_bytes, byte_count, (void *)black_data);
+
+ LOG_KDIAG_ARGS("Owner ID: %08x%08x\n", owner_32[1], owner_32[0]);
+#endif /* SHW_DEBUG */
+
+ (void)user_ctx;
+
+ /* The SCC2 DMA engine will be reading from the black ram, so we need to
+ * make sure that the data is pushed out of the cache. Note that the red
+ * ram is not an issue because it is mapped with the cache disabled.
+ */
+ os_cache_flush_range(black_data, byte_count);
+
+ scc_ret =
+ scc_decrypt_region((uint32_t) partition_base, offset_bytes,
+ byte_count,
+ (uint8_t *) __virt_to_phys(black_data), IV,
+ cypher_mode);
+
+ if (scc_ret == SCC_RET_OK) {
+ retval = FSL_RETURN_OK_S;
+ } else {
+ retval = FSL_RETURN_ERROR_S;
+ }
+
+#endif /* FSL_HAVE_SCC2 */
+
+ return retval;
+}
+
+void *fsl_shw_smalloc(fsl_shw_uco_t * user_ctx,
+ uint32_t size, const uint8_t * UMID, uint32_t permissions)
+{
+#ifdef FSL_HAVE_SCC2
+ int part_no;
+ void *part_base;
+ uint32_t part_phys;
+ scc_config_t *scc_configuration;
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (size != scc_configuration->partition_size_bytes) {
+ return NULL;
+ }
+
+ /* attempt to grab a partition. */
+ if (scc_allocate_partition(0, &part_no, &part_base, &part_phys)
+ != SCC_RET_OK) {
+ return NULL;
+ }
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("Partition_base: %p, partition_base_phys: %p\n",
+ part_base, (void *)part_phys);
+#endif
+
+ if (scc_engage_partition(part_base, UMID, permissions)
+ != SCC_RET_OK) {
+ /* Engagement failed, so the partition needs to be de-allocated */
+
+#ifdef SHW_DEBUG
+ LOG_KDIAG_ARGS("Failed to engage partition %p, de-allocating",
+ part_base);
+#endif
+ scc_release_partition(part_base);
+
+ return NULL;
+ }
+
+ return part_base;
+
+#else /* FSL_HAVE_SCC2 */
+
+ (void)user_ctx;
+ (void)size;
+ (void)UMID;
+ (void)permissions;
+ return NULL;
+
+#endif /* FSL_HAVE_SCC2 */
+}
+
+/* Release a block of secure memory */
+fsl_shw_return_t fsl_shw_sfree(fsl_shw_uco_t * user_ctx, void *address)
+{
+ (void)user_ctx;
+
+#ifdef FSL_HAVE_SCC2
+ if (scc_release_partition(address) == SCC_RET_OK) {
+ return FSL_RETURN_OK_S;
+ }
+#endif
+
+ return FSL_RETURN_ERROR_S;
+}
+
+/* Check the status of a block of secure memory */
+fsl_shw_return_t fsl_shw_sstatus(fsl_shw_uco_t * user_ctx,
+ void *address,
+ fsl_shw_partition_status_t * part_status)
+{
+ (void)user_ctx;
+
+#ifdef FSL_HAVE_SCC2
+ *part_status = scc_partition_status(address);
+
+ return FSL_RETURN_OK_S;
+#endif
+
+ return FSL_RETURN_ERROR_S;
+}
+
+/* Diminish permissions on some secure memory */
+fsl_shw_return_t fsl_shw_diminish_perms(fsl_shw_uco_t * user_ctx,
+ void *address, uint32_t permissions)
+{
+
+ (void)user_ctx; /* unused parameter warning */
+
+#ifdef FSL_HAVE_SCC2
+ if (scc_diminish_permissions(address, permissions) == SCC_RET_OK) {
+ return FSL_RETURN_OK_S;
+ }
+#endif
+ return FSL_RETURN_ERROR_S;
+}
+
+#ifndef FSL_HAVE_SAHARA
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_gen_encrypt);
+#endif
+
+fsl_shw_return_t fsl_shw_gen_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * payload,
+ uint8_t * ct, uint8_t * auth_value)
+{
+ volatile fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)auth_ctx;
+ (void)cipher_key_info;
+ (void)auth_key_info; /* save compilation warning */
+ (void)auth_data_length;
+ (void)auth_data;
+ (void)payload_length;
+ (void)payload;
+ (void)ct;
+ (void)auth_value;
+
+ return status;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_auth_decrypt);
+#endif
+/*!
+ * @brief Authenticate and decrypt a (CCM) stream.
+ *
+ * @param user_ctx The user's context
+ * @param auth_ctx Info on this Auth operation
+ * @param cipher_key_info Key to encrypt payload
+ * @param auth_key_info (unused - same key in CCM)
+ * @param auth_data_length Length in bytes of @a auth_data
+ * @param auth_data Any auth-only data
+ * @param payload_length Length in bytes of @a payload
+ * @param ct The encrypted data
+ * @param auth_value The authentication code to validate
+ * @param[out] payload The location to store decrypted data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_auth_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * ct,
+ const uint8_t * auth_value,
+ uint8_t * payload)
+{
+ volatile fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ /* Unused */
+ (void)user_ctx;
+ (void)auth_ctx;
+ (void)cipher_key_info;
+ (void)auth_key_info; /* save compilation warning */
+ (void)auth_data_length;
+ (void)auth_data;
+ (void)payload_length;
+ (void)ct;
+ (void)auth_value;
+ (void)payload;
+
+ return status;
+}
+
+#endif /* no SAHARA */
+
+#ifndef FSL_HAVE_DRYICE
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_gen_random_pf_key);
+#endif
+/*!
+ * Cause the hardware to create a new random key for secure memory use.
+ *
+ * Have the hardware use the secure hardware random number generator to load a
+ * new secret key into the hardware random key register.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_gen_random_pf_key(fsl_shw_uco_t * user_ctx)
+{
+ volatile fsl_shw_return_t status = FSL_RETURN_ERROR_S;
+
+ return status;
+}
+
+#endif /* not have DRYICE */
+
+fsl_shw_return_t alloc_slot(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_alloc(user_ctx,
+ key_info->key_length,
+ key_info->userid,
+ &(key_info->handle));
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("key length: %i, handle: %i",
+ key_info->key_length, key_info->handle);
+#endif
+
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_alloc(key_info->keystore,
+ key_info->key_length,
+ key_info->userid,
+ &(key_info->handle));
+ }
+
+ return ret;
+} /* end fn alloc_slot */
+
+fsl_shw_return_t load_slot(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, const uint8_t * key)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_load(user_ctx,
+ key_info->userid,
+ key_info->handle, key,
+ key_info->key_length);
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_load(key_info->keystore,
+ key_info->userid,
+ key_info->handle, key,
+ key_info->key_length);
+ }
+
+ return ret;
+} /* end fn load_slot */
+
+fsl_shw_return_t dealloc_slot(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid,
+ key_info->handle);
+ } else {
+ /* Key goes in user keystore */
+ keystore_slot_dealloc(key_info->keystore,
+ key_info->userid, key_info->handle);
+ }
+
+ key_info->flags &= ~(FSL_SKO_KEY_ESTABLISHED | FSL_SKO_KEY_PRESENT);
+
+ return ret;
+} /* end fn slot_dealloc */
diff --git a/drivers/mxc/security/rng/shw_dryice.c b/drivers/mxc/security/rng/shw_dryice.c
new file mode 100644
index 000000000000..1fbd4bfef986
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_dryice.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include "shw_driver.h"
+#include "../dryice.h"
+
+#include <diagnostic.h>
+
+#ifdef FSL_HAVE_DRYICE
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_gen_random_pf_key);
+#endif
+/*!
+ * Cause the hardware to create a new random key for secure memory use.
+ *
+ * Have the hardware use the secure hardware random number generator to load a
+ * new secret key into the hardware random key register.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_gen_random_pf_key(fsl_shw_uco_t * user_ctx)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+ di_return_t di_ret;
+
+ /* For now, only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ di_ret = dryice_set_random_key(0);
+ if (di_ret != DI_SUCCESS) {
+ printk("dryice_set_random_key returned %d\n", di_ret);
+ goto out;
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ return ret;
+}
+
+#ifdef LINUX_VERSION_CODE
+EXPORT_SYMBOL(fsl_shw_read_tamper_event);
+#endif
+fsl_shw_return_t fsl_shw_read_tamper_event(fsl_shw_uco_t * user_ctx,
+ fsl_shw_tamper_t * tamperp,
+ uint64_t * timestampp)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+ di_return_t di_ret;
+ uint32_t di_events = 0;
+ uint32_t di_time_stamp;
+
+ /* Only blocking mode calls are supported */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ di_ret = dryice_get_tamper_event(&di_events, &di_time_stamp, 0);
+ if ((di_ret != DI_SUCCESS) && (di_ret != DI_ERR_STATE)) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("dryice_get_tamper_event returned %s\n",
+ di_error_string(di_ret));
+#endif
+ goto out;
+ }
+
+ /* Pass time back to caller */
+ *timestampp = (uint64_t) di_time_stamp;
+
+ if (di_events & DI_TAMPER_EVENT_WTD) {
+ *tamperp = FSL_SHW_TAMPER_WTD;
+ } else if (di_events & DI_TAMPER_EVENT_ETBD) {
+ *tamperp = FSL_SHW_TAMPER_ETBD;
+ } else if (di_events & DI_TAMPER_EVENT_ETAD) {
+ *tamperp = FSL_SHW_TAMPER_ETAD;
+ } else if (di_events & DI_TAMPER_EVENT_EBD) {
+ *tamperp = FSL_SHW_TAMPER_EBD;
+ } else if (di_events & DI_TAMPER_EVENT_SAD) {
+ *tamperp = FSL_SHW_TAMPER_SAD;
+ } else if (di_events & DI_TAMPER_EVENT_TTD) {
+ *tamperp = FSL_SHW_TAMPER_TTD;
+ } else if (di_events & DI_TAMPER_EVENT_CTD) {
+ *tamperp = FSL_SHW_TAMPER_CTD;
+ } else if (di_events & DI_TAMPER_EVENT_VTD) {
+ *tamperp = FSL_SHW_TAMPER_VTD;
+ } else if (di_events & DI_TAMPER_EVENT_MCO) {
+ *tamperp = FSL_SHW_TAMPER_MCO;
+ } else if (di_events & DI_TAMPER_EVENT_TCO) {
+ *tamperp = FSL_SHW_TAMPER_TCO;
+ } else if (di_events != 0) {
+ /* Apparentliy a tamper type not known to this driver was detected */
+ goto out;
+ } else {
+ *tamperp = FSL_SHW_TAMPER_NONE;
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ return ret;
+} /* end fn fsl_shw_read_tamper_event */
+#endif
+/*!
+ * Convert an SHW HW key reference into a DI driver key reference
+ *
+ * @param shw_pf_key An SHW HW key value
+ * @param di_keyp Location to store the equivalent DI driver key
+ *
+ * @return FSL_RETURN_OK_S, or error if key is unknown or cannot translate.
+ */
+fsl_shw_return_t shw_convert_pf_key(fsl_shw_pf_key_t shw_pf_key,
+ di_key_t * di_keyp)
+{
+ fsl_shw_return_t ret = FSL_RETURN_BAD_FLAG_S;
+
+ switch (shw_pf_key) {
+ case FSL_SHW_PF_KEY_IIM:
+ *di_keyp = DI_KEY_FK;
+ break;
+ case FSL_SHW_PF_KEY_RND:
+ *di_keyp = DI_KEY_RK;
+ break;
+ case FSL_SHW_PF_KEY_IIM_RND:
+ *di_keyp = DI_KEY_FRK;
+ break;
+ case FSL_SHW_PF_KEY_PRG:
+ *di_keyp = DI_KEY_PK;
+ break;
+ case FSL_SHW_PF_KEY_IIM_PRG:
+ *di_keyp = DI_KEY_FPK;
+ break;
+ default:
+ goto out;
+ }
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+ return ret;
+}
+
+#ifdef DIAG_SECURITY_FUNC
+const char *di_error_string(int code)
+{
+ char *str = "unknown";
+
+ switch (code) {
+ case DI_SUCCESS:
+ str = "operation was successful";
+ break;
+ case DI_ERR_BUSY:
+ str = "device or resource busy";
+ break;
+ case DI_ERR_STATE:
+ str = "dryice is in incompatible state";
+ break;
+ case DI_ERR_INUSE:
+ str = "resource is already in use";
+ break;
+ case DI_ERR_UNSET:
+ str = "resource has not been initialized";
+ break;
+ case DI_ERR_WRITE:
+ str = "error occurred during register write";
+ break;
+ case DI_ERR_INVAL:
+ str = "invalid argument";
+ break;
+ case DI_ERR_FAIL:
+ str = "operation failed";
+ break;
+ case DI_ERR_HLOCK:
+ str = "resource is hard locked";
+ break;
+ case DI_ERR_SLOCK:
+ str = "resource is soft locked";
+ break;
+ case DI_ERR_NOMEM:
+ str = "out of memory";
+ break;
+ default:
+ break;
+ }
+
+ return str;
+}
+#endif /* HAVE DRYICE */
diff --git a/drivers/mxc/security/rng/shw_hash.c b/drivers/mxc/security/rng/shw_hash.c
new file mode 100644
index 000000000000..ad9e7f319625
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_hash.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file shw_hash.c
+ *
+ * This file contains implementations for use of the (internal) SHW hash
+ * software computation. It defines the usual three steps:
+ *
+ * - #shw_hash_init()
+ * - #shw_hash_update()
+ * - #shw_hash_final()
+ *
+ * In support of the above functions, it also contains these functions:
+ * - #sha256_init()
+ * - #sha256_process_block()
+ *
+ *
+ * These functions depend upon the Linux Endian functions __be32_to_cpu(),
+ * __cpu_to_be32() to convert a 4-byte big-endian array to an integer and
+ * vice-versa. For those without Linux, it should be pretty obvious what they
+ * do.
+ *
+ * The #shw_hash_update() and #shw_hash_final() functions are generic enough to
+ * support SHA-1/SHA-224/SHA-256, as needed. Some extra tweaking would be
+ * necessary to get them to support SHA-384/SHA-512.
+ *
+ */
+
+#include "shw_driver.h"
+#include "shw_hash.h"
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <linux/byteorder/little_endian.h> /* or whichever is proper for target arch */
+#define printk printf
+#endif
+
+/*!
+ * Rotate a value right by a number of bits.
+ *
+ * @param x Word of data which needs rotating
+ * @param y Number of bits to rotate
+ *
+ * @return The new value
+ */
+inline uint32_t rotr32fixed(uint32_t x, unsigned int y)
+{
+ return (uint32_t) ((x >> y) | (x << (32 - y)));
+}
+
+#define blk0(i) (W[i] = data[i])
+// Referencing parameters so many times is really poor practice. Do not imitate these macros
+#define blk2(i) (W[i & 15] += s1(W[(i - 2) & 15]) + W[(i - 7) & 15] + s0(W[(i - 15) & 15]))
+
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) ((x & y) | (z & (x | y)))
+
+#define a(i) T[(0 - i) & 7]
+#define b(i) T[(1 - i) & 7]
+#define c(i) T[(2 - i) & 7]
+#define d(i) T[(3 - i) & 7]
+#define e(i) T[(4 - i) & 7]
+#define f(i) T[(5 - i) & 7]
+#define g(i) T[(6 - i) & 7]
+#define h(i) T[(7 - i) & 7]
+
+// This is a bad way to write a multi-statement macro... and referencing 'i' so many
+// times is really poor practice. Do not imitate.
+#define R(i) h(i) += S1( e(i)) + Ch(e(i), f(i), g(i)) + K[i + j] +(j ? blk2(i) : blk0(i));\
+ d(i) += h(i);h(i) += S0(a(i)) + Maj(a(i), b(i), c(i))
+
+// for SHA256
+#define S0(x) (rotr32fixed(x, 2) ^ rotr32fixed(x, 13) ^ rotr32fixed(x, 22))
+#define S1(x) (rotr32fixed(x, 6) ^ rotr32fixed(x, 11) ^ rotr32fixed(x, 25))
+#define s0(x) (rotr32fixed(x, 7) ^ rotr32fixed(x, 18) ^ (x >> 3))
+#define s1(x) (rotr32fixed(x, 17) ^ rotr32fixed(x, 19) ^ (x >> 10))
+
+/*!
+ * Initialize the Hash State
+ *
+ * Constructs the SHA256 hash engine.
+ * Specification:
+ * State Size = 32 bytes
+ * Block Size = 64 bytes
+ * Digest Size = 32 bytes
+ *
+ * @param state Address of hash state structure
+ *
+ */
+void sha256_init(shw_hash_state_t * state)
+{
+ state->bit_count = 0;
+ state->partial_count_bytes = 0;
+
+ state->state[0] = 0x6a09e667;
+ state->state[1] = 0xbb67ae85;
+ state->state[2] = 0x3c6ef372;
+ state->state[3] = 0xa54ff53a;
+ state->state[4] = 0x510e527f;
+ state->state[5] = 0x9b05688c;
+ state->state[6] = 0x1f83d9ab;
+ state->state[7] = 0x5be0cd19;
+}
+
+const uint32_t K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+/*!
+ * Hash a block of data into the SHA-256 hash state.
+ *
+ * This function hash the block of data in the @c partial_block
+ * element of the state structure into the state variables of the
+ * state structure.
+ *
+ * @param state Address of hash state structure
+ *
+ */
+static void sha256_process_block(shw_hash_state_t * state)
+{
+ uint32_t W[16];
+ uint32_t T[8];
+ uint32_t stack_buffer[SHW_HASH_BLOCK_WORD_SIZE];
+ uint32_t *data = &stack_buffer[0];
+ uint8_t *input = state->partial_block;
+ unsigned int i;
+ unsigned int j;
+
+ /* Copy byte-oriented input block into word-oriented registers */
+ for (i = 0; i < SHW_HASH_BLOCK_LEN / sizeof(uint32_t);
+ i++, input += sizeof(uint32_t)) {
+ stack_buffer[i] = __be32_to_cpu(*(uint32_t *) input);
+ }
+
+ /* Copy context->state[] to working vars */
+ memcpy(T, state->state, sizeof(T));
+
+ /* 64 operations, partially loop unrolled */
+ for (j = 0; j < SHW_HASH_BLOCK_LEN; j += 16) {
+ R(0);
+ R(1);
+ R(2);
+ R(3);
+ R(4);
+ R(5);
+ R(6);
+ R(7);
+ R(8);
+ R(9);
+ R(10);
+ R(11);
+ R(12);
+ R(13);
+ R(14);
+ R(15);
+ }
+ /* Add the working vars back into context.state[] */
+ state->state[0] += a(0);
+ state->state[1] += b(0);
+ state->state[2] += c(0);
+ state->state[3] += d(0);
+ state->state[4] += e(0);
+ state->state[5] += f(0);
+ state->state[6] += g(0);
+ state->state[7] += h(0);
+
+ /* Wipe variables */
+ memset(W, 0, sizeof(W));
+ memset(T, 0, sizeof(T));
+}
+
+/*!
+ * Initialize the hash state structure
+ *
+ * @param state Address of hash state structure.
+ * @param alg Which hash algorithm to use (must be FSL_HASH_ALG_SHA1)
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hash_init(shw_hash_state_t * state, fsl_shw_hash_alg_t alg)
+{
+ if (alg != FSL_HASH_ALG_SHA256) {
+ return FSL_RETURN_BAD_ALGORITHM_S;
+ }
+
+ sha256_init(state);
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * Add input bytes to the hash
+ *
+ * The bytes are added to the partial_block element of the hash state, and as
+ * the partial block is filled, it is processed by sha1_process_block(). This
+ * function also updates the bit_count element of the hash state.
+ *
+ * @param state Address of hash state structure
+ * @param input Address of bytes to add to the hash
+ * @param input_len Numbef of bytes at @c input
+ *
+ */
+fsl_shw_return_t shw_hash_update(shw_hash_state_t * state,
+ const uint8_t * input, unsigned int input_len)
+{
+ unsigned int bytes_needed; /* Needed to fill a block */
+ unsigned int bytes_to_copy; /* to copy into the block */
+
+ /* Account for new data */
+ state->bit_count += 8 * input_len;
+
+ /*
+ * Process input bytes into the ongoing block; process the block when it
+ * gets full.
+ */
+ while (input_len > 0) {
+ bytes_needed = SHW_HASH_BLOCK_LEN - state->partial_count_bytes;
+ bytes_to_copy = ((input_len < bytes_needed) ?
+ input_len : bytes_needed);
+
+ /* Add in the bytes and do the accounting */
+ memcpy(state->partial_block + state->partial_count_bytes,
+ input, bytes_to_copy);
+ input += bytes_to_copy;
+ input_len -= bytes_to_copy;
+ state->partial_count_bytes += bytes_to_copy;
+
+ /* Run a full block through the transform */
+ if (state->partial_count_bytes == SHW_HASH_BLOCK_LEN) {
+ sha256_process_block(state);
+ state->partial_count_bytes = 0;
+ }
+ }
+
+ return FSL_RETURN_OK_S;
+} /* end fn shw_hash_update */
+
+/*!
+ * Finalize the hash
+ *
+ * Performs the finalize operation on the previous input data & returns the
+ * resulting digest. The finalize operation performs the appropriate padding
+ * up to the block size.
+ *
+ * @param state Address of hash state structure
+ * @param result Location to store the hash result
+ * @param result_len Number of bytes of @c result to be stored.
+ *
+ * @return FSL_RETURN_OK_S if all went well, FSL_RETURN_BAD_DATA_LENGTH_S if
+ * hash_len is too long, otherwise an error code.
+ */
+fsl_shw_return_t shw_hash_final(shw_hash_state_t * state, uint8_t * result,
+ unsigned int result_len)
+{
+ static const uint8_t pad[SHW_HASH_BLOCK_LEN * 2] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ uint8_t data[sizeof(state->bit_count)];
+ uint32_t pad_length;
+ uint64_t bit_count = state->bit_count;
+ uint8_t hash[SHW_HASH_LEN];
+ int i;
+
+ if (result_len > SHW_HASH_LEN) {
+ return FSL_RETURN_BAD_DATA_LENGTH_S;
+ }
+
+ /* Save the length before padding. */
+ for (i = sizeof(state->bit_count) - 1; i >= 0; i--) {
+ data[i] = bit_count & 0xFF;
+ bit_count >>= 8;
+ }
+ pad_length = ((state->partial_count_bytes < 56) ?
+ (56 - state->partial_count_bytes) :
+ (120 - state->partial_count_bytes));
+
+ /* Pad to 56 bytes mod 64 (BLOCK_SIZE). */
+ shw_hash_update(state, pad, pad_length);
+
+ /*
+ * Append the length. This should trigger transform of the final block.
+ */
+ shw_hash_update(state, data, sizeof(state->bit_count));
+
+ /* Copy the result into a byte array */
+ for (i = 0; i < SHW_HASH_STATE_WORDS; i++) {
+ *(uint32_t *) (hash + 4 * i) = __cpu_to_be32(state->state[i]);
+ }
+
+ /* And copy the result out to caller */
+ memcpy(result, hash, result_len);
+
+ return FSL_RETURN_OK_S;
+} /* end fn shw_hash_final */
diff --git a/drivers/mxc/security/rng/shw_hmac.c b/drivers/mxc/security/rng/shw_hmac.c
new file mode 100644
index 000000000000..0e8728299860
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_hmac.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file shw_hmac.c
+ *
+ * This file contains implementations for use of the (internal) SHW HMAC
+ * software computation. It defines the usual three steps:
+ *
+ * - #shw_hmac_init()
+ * - #shw_hmac_update()
+ * - #shw_hmac_final()
+ *
+ *
+ */
+
+#include "shw_driver.h"
+#include "shw_hmac.h"
+
+#ifndef __KERNEL__
+#include <asm/types.h>
+#include <linux/byteorder/little_endian.h> /* or whichever is proper for target arch */
+#define printk printf
+#endif
+
+/*! XOR value for HMAC inner key */
+#define INNER_HASH_CONSTANT 0x36
+
+/*! XOR value for HMAC outer key */
+#define OUTER_HASH_CONSTANT 0x5C
+
+/*!
+ * Initialize the HMAC state structure with the HMAC key
+ *
+ * @param state Address of HMAC state structure
+ * @param key Address of the key to be used for the HMAC.
+ * @param key_len Number of bytes of @c key.
+ *
+ * Convert the key into its equivalent inner and outer hash state objects.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_init(shw_hmac_state_t * state,
+ const uint8_t * key, unsigned int key_len)
+{
+ fsl_shw_return_t code = FSL_RETURN_ERROR_S;
+ uint8_t first_block[SHW_HASH_BLOCK_LEN];
+ unsigned int i;
+
+ /* Don't bother handling the pre-hash. */
+ if (key_len > SHW_HASH_BLOCK_LEN) {
+ code = FSL_RETURN_BAD_KEY_LENGTH_S;
+ goto out;
+ }
+
+ /* Prepare inner hash */
+ for (i = 0; i < SHW_HASH_BLOCK_LEN; i++) {
+ if (i < key_len) {
+ first_block[i] = key[i] ^ INNER_HASH_CONSTANT;
+ } else {
+ first_block[i] = INNER_HASH_CONSTANT;
+ }
+ }
+ code = shw_hash_init(&state->inner_hash, FSL_HASH_ALG_SHA256);
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ shw_hash_update(&state->inner_hash, first_block, SHW_HASH_BLOCK_LEN);
+
+ /* Prepare outer hash */
+ for (i = 0; i < SHW_HASH_BLOCK_LEN; i++) {
+ if (i < key_len) {
+ first_block[i] = key[i] ^ OUTER_HASH_CONSTANT;
+ } else {
+ first_block[i] = OUTER_HASH_CONSTANT;
+ }
+ }
+ code = shw_hash_init(&state->outer_hash, FSL_HASH_ALG_SHA256);
+ if (code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ shw_hash_update(&state->outer_hash, first_block, SHW_HASH_BLOCK_LEN);
+
+ /* Wipe evidence of key */
+ memset(first_block, 0, SHW_HASH_BLOCK_LEN);
+
+ out:
+ return code;
+}
+
+/*!
+ * Put data into the HMAC calculation
+ *
+ * Send the msg data inner inner hash's update function.
+ *
+ * @param state Address of HMAC state structure.
+ * @param msg Address of the message data for the HMAC.
+ * @param msg_len Number of bytes of @c msg.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_update(shw_hmac_state_t * state,
+ const uint8_t * msg, unsigned int msg_len)
+{
+ shw_hash_update(&state->inner_hash, msg, msg_len);
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * Calculate the final HMAC
+ *
+ * @param state Address of HMAC state structure.
+ * @param hmac Address of location to store the HMAC.
+ * @param hmac_len Number of bytes of @c mac to be stored. Probably best if
+ * this value is no greater than #SHW_HASH_LEN.
+ *
+ * This function finalizes the internal hash, and uses that result as
+ * data for the outer hash. As many bytes of that result are passed
+ * to the user as desired.
+ *
+ * @return FSL_RETURN_OK_S if all went well, otherwise an error code.
+ */
+fsl_shw_return_t shw_hmac_final(shw_hmac_state_t * state,
+ uint8_t * hmac, unsigned int hmac_len)
+{
+ uint8_t hash_result[SHW_HASH_LEN];
+
+ shw_hash_final(&state->inner_hash, hash_result, sizeof(hash_result));
+ shw_hash_update(&state->outer_hash, hash_result, SHW_HASH_LEN);
+
+ shw_hash_final(&state->outer_hash, hmac, hmac_len);
+
+ return FSL_RETURN_OK_S;
+}
diff --git a/drivers/mxc/security/rng/shw_memory_mapper.c b/drivers/mxc/security/rng/shw_memory_mapper.c
new file mode 100644
index 000000000000..71f1d301f02a
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_memory_mapper.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2005-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+
+
+/**
+ * Memory management functions, from Sahara Crypto API
+ *
+ * This is a subset of the memory management functions from the Sahara Crypto
+ * API, and is intended to support user secure partitions.
+ */
+
+#include "portable_os.h"
+#include "fsl_shw.h"
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+
+#ifdef SHW_DEBUG
+#include <diagnostic.h>
+#endif
+
+/* Page context structure. Used by wire_user_memory and unwire_user_memory */
+typedef struct page_ctx_t {
+ uint32_t count;
+ struct page **local_pages;
+} page_ctx_t;
+
+/**
+*******************************************************************************
+* Map and wire down a region of user memory.
+*
+*
+* @param address Userspace address of the memory to wire
+* @param length Length of the memory region to wire
+* @param page_ctx Page context, to be passed to unwire_user_memory
+*
+* @return (if successful) Kernel virtual address of the wired pages
+*/
+void* wire_user_memory(void* address, uint32_t length, void **page_ctx)
+{
+ void* kernel_black_addr = NULL;
+ int result = -1;
+ int page_index = 0;
+ page_ctx_t *page_context;
+ int nr_pages = 0;
+ unsigned long start_page;
+ fsl_shw_return_t status;
+
+ /* Determine the number of pages being used for this link */
+ nr_pages = (((unsigned long)(address) & ~PAGE_MASK)
+ + length + ~PAGE_MASK) >> PAGE_SHIFT;
+
+ start_page = (unsigned long)(address) & PAGE_MASK;
+
+ /* Allocate some memory to keep track of the wired user pages, so that
+ * they can be deallocated later. The block of memory will contain both
+ * the structure and the array of pages.
+ */
+ page_context = kmalloc(sizeof(page_ctx_t)
+ + nr_pages * sizeof(struct page *), GFP_KERNEL);
+
+ if (page_context == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S; /* no memory! */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("kmalloc() failed.");
+#endif
+ return NULL;
+ }
+
+ /* Set the page pointer to point to the allocated region of memory */
+ page_context->local_pages = (void*)page_context + sizeof(page_ctx_t);
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS("page_context at: %p, local_pages at: %p",
+ (void *)page_context,
+ (void *)(page_context->local_pages));
+#endif
+
+ /* Wire down the pages from user space */
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current, current->mm,
+ start_page, nr_pages,
+ WRITE, 0 /* noforce */,
+ (page_context->local_pages), NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (result < nr_pages) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("get_user_pages() failed.");
+#endif
+ if (result > 0) {
+ for (page_index = 0; page_index < result; page_index++)
+ page_cache_release((page_context->local_pages[page_index]));
+
+ kfree(page_context);
+ }
+ return NULL;
+ }
+
+ kernel_black_addr = page_address(page_context->local_pages[0]) +
+ ((unsigned long)address & ~PAGE_MASK);
+
+ page_context->count = nr_pages;
+ *page_ctx = page_context;
+
+ return kernel_black_addr;
+}
+
+
+/**
+*******************************************************************************
+* Release and unmap a region of user memory.
+*
+* @param page_ctx Page context from wire_user_memory
+*/
+void unwire_user_memory(void** page_ctx)
+{
+ int page_index = 0;
+ struct page_ctx_t *page_context = *page_ctx;
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS("page_context at: %p, first page at:%p, count: %i",
+ (void *)page_context,
+ (void *)(page_context->local_pages),
+ page_context->count);
+#endif
+
+ if ((page_context != NULL) && (page_context->local_pages != NULL)) {
+ for (page_index = 0; page_index < page_context->count; page_index++)
+ page_cache_release(page_context->local_pages[page_index]);
+
+ kfree(page_context);
+ *page_ctx = NULL;
+ }
+}
+
+
+/**
+*******************************************************************************
+* Map some physical memory into a users memory space
+*
+* @param vma Memory structure to map to
+* @param physical_addr Physical address of the memory to be mapped in
+* @param size Size of the memory to map (bytes)
+*
+* @return
+*/
+os_error_code
+map_user_memory(struct vm_area_struct *vma, uint32_t physical_addr, uint32_t size)
+{
+ os_error_code retval;
+
+ /* Map the acquired partition into the user's memory space */
+ vma->vm_end = vma->vm_start + size;
+
+ /* set cache policy to uncached so that each write of the UMID and
+ * permissions get directly to the SCC2 in order to engage it
+ * properly. Once the permissions have been written, it may be
+ * useful to provide a service for the user to request a different
+ * cache policy
+ */
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* Make sure that the user cannot fork() a child which will inherit
+ * this mapping, as it creates a security hole. Likewise, do not
+ * allow the user to 'expand' his mapping beyond this partition.
+ */
+ vma->vm_flags |= VM_IO | VM_RESERVED | VM_DONTCOPY | VM_DONTEXPAND;
+
+ retval = remap_pfn_range(vma,
+ vma->vm_start,
+ __phys_to_pfn(physical_addr),
+ size,
+ vma->vm_page_prot);
+
+ return retval;
+}
+
+
+/**
+*******************************************************************************
+* Remove some memory from a user's memory space
+*
+* @param user_addr Userspace address of the memory to be unmapped
+* @param size Size of the memory to map (bytes)
+*
+* @return
+*/
+os_error_code
+unmap_user_memory(uint32_t user_addr, uint32_t size)
+{
+ os_error_code retval;
+ struct mm_struct *mm = current->mm;
+
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ down_write(&mm->mmap_sem);
+ retval = do_munmap(mm, (unsigned long)user_addr, size);
+ up_write(&mm->mmap_sem);
+
+ return retval;
+}
diff --git a/drivers/mxc/security/sahara2/Kconfig b/drivers/mxc/security/sahara2/Kconfig
new file mode 100644
index 000000000000..ab4e6fdc2cfc
--- /dev/null
+++ b/drivers/mxc/security/sahara2/Kconfig
@@ -0,0 +1,35 @@
+menu "SAHARA2 Security Hardware Support"
+
+config MXC_SAHARA
+ tristate "Security Hardware Support (FSL SHW)"
+ ---help---
+ Provides driver and kernel mode API for using cryptographic
+ accelerators.
+
+config MXC_SAHARA_USER_MODE
+ tristate "User Mode API for FSL SHW"
+ depends on MXC_SAHARA
+ ---help---
+ Provides kernel driver for User Mode API.
+
+config MXC_SAHARA_POLL_MODE
+ bool "Force driver to POLL for hardware completion."
+ depends on MXC_SAHARA
+ default n
+ ---help---
+ When this flag is yes, the driver will not use interrupts to
+ determine when the hardware has completed a task, but instead
+ will hold onto the CPU and continually poll the hardware until
+ it completes.
+
+config MXC_SAHARA_POLL_MODE_TIMEOUT
+ hex "Poll loop timeout"
+ depends on MXC_SAHARA_POLL_MODE
+ default "0xFFFFFFFF"
+ help
+ To avoid infinite polling, a timeout is provided. Should the
+ timeout be reached, a fault is reported, indicating there must
+ be something wrong with SAHARA, and SAHARA is reset. The loop
+ will exit after the given number of iterations.
+
+endmenu
diff --git a/drivers/mxc/security/sahara2/Makefile b/drivers/mxc/security/sahara2/Makefile
new file mode 100644
index 000000000000..b515709be29b
--- /dev/null
+++ b/drivers/mxc/security/sahara2/Makefile
@@ -0,0 +1,47 @@
+# Makefile for the Linux Sahara2 driver
+#
+# This makefile works within a kernel driver tree
+
+# Need to augment this to support optionally building user-mode support
+API_SOURCES = fsl_shw_sym.c fsl_shw_user.c fsl_shw_hash.c fsl_shw_auth.c \
+ fsl_shw_hmac.c fsl_shw_rand.c sf_util.c km_adaptor.c fsl_shw_keystore.c \
+ fsl_shw_wrap.c \
+
+
+SOURCES = sah_driver_interface.c sah_hardware_interface.c \
+ sah_interrupt_handler.c sah_queue.c sah_queue_manager.c \
+ sah_status_manager.c sah_memory_mapper.c
+
+
+# Turn on for mostly full debugging
+# DIAGS = -DDIAG_DRV_STATUS -DDIAG_DRV_QUEUE -DDIAG_DRV_INTERRUPT -DDIAG_DRV_IF
+# DIAGS += -DDIAG_DURING_INTERRUPT
+
+# Turn on for lint-type checking
+#EXTRA_CFLAGS = -Wall -W -Wstrict-prototypes -Wmissing-prototypes
+EXTRA_CFLAGS += -DLINUX_KERNEL $(DIAGS)
+
+
+ifeq ($(CONFIG_MXC_SAHARA_POLL_MODE),y)
+EXTRA_CFLAGS += -DSAHARA_POLL_MODE
+EXTRA_CFLAGS += -DSAHARA_POLL_MODE_TIMEOUT=$(CONFIG_SAHARA_POLL_MODE_TIMEOUT)
+endif
+
+ifeq ($(CONFIG_MXC_SAHARA_USER_MODE),y)
+EXTRA_CFLAGS += -DSAHARA_USER_MODE
+SOURCES +=
+endif
+
+ifeq ($(CONFIG_PM),y)
+EXTRA_CFLAGS += -DSAHARA_POWER_MANAGMENT
+endif
+
+EXTRA_CFLAGS += -Idrivers/mxc/security/sahara2/include
+
+# handle buggy BSP -- uncomment if these are undefined during build
+#EXTRA_CFLAGS += -DSAHARA_BASE_ADDR=HAC_BASE_ADDR -DINT_SAHARA=INT_HAC_RTIC
+
+
+obj-$(CONFIG_MXC_SAHARA) += sahara.o
+
+sahara-objs := $(SOURCES:.c=.o) $(API_SOURCES:.c=.o)
diff --git a/drivers/mxc/security/sahara2/fsl_shw_auth.c b/drivers/mxc/security/sahara2/fsl_shw_auth.c
new file mode 100644
index 000000000000..d3100f01380a
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_auth.c
@@ -0,0 +1,706 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @file fsl_shw_auth.c
+ *
+ * This file contains the routines which do the combined encrypt+authentication
+ * functions. For now, only AES-CCM is supported.
+ */
+
+#include "sahara.h"
+#include "adaptor.h"
+#include "sf_util.h"
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_gen_encrypt);
+EXPORT_SYMBOL(fsl_shw_auth_decrypt);
+#endif
+
+
+/*! Size of buffer to repetively sink useless CBC output */
+#define CBC_BUF_LEN 4096
+
+/*!
+ * Compute the size, in bytes, of the encoded auth length
+ *
+ * @param l The actual associated data length
+ *
+ * @return The encoded length
+ */
+#define COMPUTE_NIST_AUTH_LEN_SIZE(l) \
+({ \
+ unsigned val; \
+ uint32_t len = l; \
+ if (len == 0) { \
+ val = 0; \
+ } else if (len < 65280) { \
+ val = 2; \
+ } else { /* cannot handle >= 2^32 */ \
+ val = 6; \
+ } \
+ val; \
+})
+
+/*!
+ * Store the encoded Auth Length into the Auth Data
+ *
+ * @param l The actual Auth Length
+ * @param p Location to store encoding (must be uint8_t*)
+ *
+ * @return void
+ */
+#define STORE_NIST_AUTH_LEN(l, p) \
+{ \
+ register uint32_t L = l; \
+ if ((uint32_t)(l) < 65280) { \
+ (p)[1] = L & 0xff; \
+ L >>= 8; \
+ (p)[0] = L & 0xff; \
+ } else { /* cannot handle >= 2^32 */ \
+ int i; \
+ for (i = 5; i > 1; i--) { \
+ (p)[i] = L & 0xff; \
+ L >>= 8; \
+ } \
+ (p)[1] = 0xfe; /* Markers */ \
+ (p)[0] = 0xff; \
+ } \
+}
+
+#if defined (FSL_HAVE_SAHARA2) || defined (USE_S2_CCM_DECRYPT_CHAIN) \
+ || defined (USE_S2_CCM_ENCRYPT_CHAIN)
+/*! Buffer to repetively sink useless CBC output */
+static uint8_t cbc_buffer[CBC_BUF_LEN];
+#endif
+
+/*!
+ * Place to store useless output (while bumping CTR0 to CTR1, for instance.
+ * Must be maximum Symmetric block size
+ */
+static uint8_t garbage_output[16];
+
+/*!
+ * Block of zeroes which is maximum Symmetric block size, used for
+ * initializing context register, etc.
+ */
+static uint8_t block_zeros[16] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*!
+ * Append a descriptor chain which will compute CBC over the
+ * formatted associated data blocks.
+ *
+ * @param[in,out] link1 Where to append the new link
+ * @param[in,out] data_len Location of current/updated auth-only data length
+ * @param user_ctx Info for acquiring memory
+ * @param auth_ctx Location of block0 value
+ * @param auth_data Unformatted associated data
+ * @param auth_data_length Length in octets of @a auth_data
+ * @param[in,out] temp_buf Location of in-process data.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t process_assoc_from_nist_params(sah_Link ** link1,
+ uint32_t *
+ data_len,
+ fsl_shw_uco_t *
+ user_ctx,
+ fsl_shw_acco_t *
+ auth_ctx,
+ const uint8_t *
+ auth_data,
+ uint32_t
+ auth_data_length,
+ uint8_t **
+ temp_buf)
+{
+ fsl_shw_return_t status;
+ uint32_t auth_size_length =
+ COMPUTE_NIST_AUTH_LEN_SIZE(auth_data_length);
+ uint32_t auth_pad_length =
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes -
+ (auth_data_length +
+ auth_size_length) %
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes;
+
+ if (auth_pad_length ==
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes) {
+ auth_pad_length = 0;
+ }
+
+ /* Put in Block0 */
+ status = sah_Create_Link(user_ctx->mem_util, link1,
+ auth_ctx->auth_info.CCM_ctx_info.context,
+ auth_ctx->auth_info.CCM_ctx_info.
+ block_size_bytes, SAH_USES_LINK_DATA);
+
+ if (auth_data_length != 0) {
+ if (status == FSL_RETURN_OK_S) {
+ /* Add on length preamble to auth data */
+ STORE_NIST_AUTH_LEN(auth_data_length, *temp_buf);
+ status = sah_Append_Link(user_ctx->mem_util, *link1,
+ *temp_buf, auth_size_length,
+ SAH_OWNS_LINK_DATA);
+ *temp_buf += auth_size_length; /* 2, 6, or 10 bytes */
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ /* Add in auth data */
+ status = sah_Append_Link(user_ctx->mem_util, *link1,
+ (uint8_t *) auth_data,
+ auth_data_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if ((status == FSL_RETURN_OK_S) && (auth_pad_length > 0)) {
+ status = sah_Append_Link(user_ctx->mem_util, *link1,
+ block_zeros, auth_pad_length,
+ SAH_USES_LINK_DATA);
+ }
+ }
+ /* ... if auth_data_length != 0 */
+ *data_len = auth_ctx->auth_info.CCM_ctx_info.block_size_bytes +
+ auth_data_length + auth_size_length + auth_pad_length;
+
+ return status;
+} /* end fn process_assoc_from_nist_params */
+
+/*!
+ * Add a Descriptor which will process with CBC the NIST preamble data
+ *
+ * @param desc_chain Current chain
+ * @param user_ctx User's context
+ * @param auth_ctx Inf
+ * @pararm encrypt 0 => decrypt, non-zero => encrypt
+ * @param auth_data Additional auth data for this call
+ * @param auth_data_length Length in bytes of @a auth_data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t add_assoc_preamble(sah_Head_Desc ** desc_chain,
+ fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ int encrypt,
+ const uint8_t * auth_data,
+ uint32_t auth_data_length)
+{
+ uint8_t *temp_buf;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ uint32_t cbc_data_length = 0;
+ /* Assume AES */
+ uint32_t header = SAH_HDR_SKHA_ENC_DEC;
+ uint32_t temp_buf_flag;
+ unsigned chain_s2 = 1;
+
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_DECRYPT_CHAIN)
+ if (!encrypt) {
+ chain_s2 = 0;
+ }
+#endif
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_ENCRYPT_CHAIN)
+ if (encrypt) {
+ chain_s2 = 0;
+ }
+#endif
+ /* Grab a block big enough for multiple uses so that only one allocate
+ * request needs to be made.
+ */
+ temp_buf =
+ user_ctx->mem_util->mu_malloc(user_ctx->mem_util->mu_ref,
+ 3 *
+ auth_ctx->auth_info.CCM_ctx_info.
+ block_size_bytes);
+
+ if (temp_buf == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S;
+ goto out;
+ }
+
+ if (auth_ctx->flags & FSL_ACCO_NIST_CCM) {
+ status = process_assoc_from_nist_params(&link1,
+ &cbc_data_length,
+ user_ctx,
+ auth_ctx,
+ auth_data,
+ auth_data_length,
+ &temp_buf);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* temp_buf has been referenced (and incremented). Only 'own' it
+ * once, at its first value. Since the nist routine called above
+ * bumps it...
+ */
+ temp_buf_flag = SAH_USES_LINK_DATA;
+ } else { /* if NIST */
+ status = sah_Create_Link(user_ctx->mem_util, &link1,
+ (uint8_t *) auth_data,
+ auth_data_length, SAH_USES_LINK_DATA);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* for next/first use of temp_buf */
+ temp_buf_flag = SAH_OWNS_LINK_DATA;
+ cbc_data_length = auth_data_length;
+ } /* else not NIST */
+
+#if defined (FSL_HAVE_SAHARA2) || defined (USE_S2_CCM_ENCRYPT_CHAIN) \
+ || defined (USE_S2_CCM_DECRYPT_CHAIN)
+
+ if (!chain_s2) {
+ header = SAH_HDR_SKHA_CBC_ICV
+ ^ sah_insert_skha_mode_cbc ^ sah_insert_skha_aux0
+ ^ sah_insert_skha_encrypt;
+ } else {
+ /*
+ * Auth data links have been created. Now create link for the
+ * useless output of the CBC calculation.
+ */
+ status = sah_Create_Link(user_ctx->mem_util, &link2,
+ temp_buf,
+ auth_ctx->auth_info.CCM_ctx_info.
+ block_size_bytes,
+ temp_buf_flag | SAH_OUTPUT_LINK);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ temp_buf += auth_ctx->auth_info.CCM_ctx_info.block_size_bytes;
+
+ cbc_data_length -=
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes;
+ if (cbc_data_length != 0) {
+ while ((status == FSL_RETURN_OK_S)
+ && (cbc_data_length != 0)) {
+ uint32_t linklen =
+ (cbc_data_length >
+ CBC_BUF_LEN) ? CBC_BUF_LEN :
+ cbc_data_length;
+
+ status =
+ sah_Append_Link(user_ctx->mem_util, link2,
+ cbc_buffer, linklen,
+ SAH_USES_LINK_DATA |
+ SAH_OUTPUT_LINK);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ cbc_data_length -= linklen;
+ }
+ }
+ }
+#else
+ header = SAH_HDR_SKHA_CBC_ICV
+ ^ sah_insert_skha_mode_cbc ^ sah_insert_skha_aux0
+ ^ sah_insert_skha_encrypt;
+#endif
+ /* Crank through auth data */
+ status = sah_Append_Desc(user_ctx->mem_util, desc_chain,
+ header, link1, link2);
+
+ out:
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(user_ctx->mem_util, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(user_ctx->mem_util, link2);
+ }
+ }
+
+ (void)encrypt;
+ return status;
+} /* add_assoc_preamble() */
+
+#if SUPPORT_SSL
+/*!
+ * Generate an SSL value
+ *
+ * @param user_ctx Info for acquiring memory
+ * @param auth_ctx Info for CTR0, size of MAC
+ * @param cipher_key_info
+ * @param auth_key_info
+ * @param auth_data_length
+ * @param auth_data
+ * @param payload_length
+ * @param payload
+ * @param ct
+ * @param auth_value
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t do_ssl_gen(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * payload,
+ uint8_t * ct, uint8_t * auth_value)
+{
+ SAH_SF_DCLS;
+ uint8_t *ptr1 = NULL;
+
+ /* Assume one-shot init-finalize... no precomputes */
+ header = SAH_HDR_MDHA_SET_MODE_MD_KEY ^
+ sah_insert_mdha_algorithm[auth_ctx->auth_info.hash_ctx_info.
+ algorithm] ^ sah_insert_mdha_init ^
+ sah_insert_mdha_ssl ^ sah_insert_mdha_pdata ^
+ sah_insert_mdha_mac_full;
+
+ /* set up hmac */
+ DESC_IN_KEY(header, 0, NULL, auth_key_info);
+
+ /* This is wrong -- need to find 16 extra bytes of data from
+ * somewhere */
+ DESC_IN_OUT(SAH_HDR_MDHA_HASH, payload_length, payload, 1, auth_value);
+
+ /* set up encrypt */
+ header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_mode[auth_ctx->cipher_ctx_info.mode]
+ ^ sah_insert_skha_encrypt
+ ^ sah_insert_skha_algorithm[cipher_key_info->algorithm];
+
+ /* Honor 'no key parity checking' for DES and TDES */
+ if ((cipher_key_info->flags & FSL_SKO_KEY_IGNORE_PARITY) &&
+ ((cipher_key_info->algorithm == FSL_KEY_ALG_DES) ||
+ (cipher_key_info->algorithm == FSL_KEY_ALG_TDES))) {
+ header ^= sah_insert_skha_no_key_parity;
+ }
+
+ if (auth_ctx->cipher_ctx_info.mode == FSL_SYM_MODE_CTR) {
+ header ^=
+ sah_insert_skha_modulus[auth_ctx->cipher_ctx_info.
+ modulus_exp];
+ }
+
+ if ((auth_ctx->cipher_ctx_info.mode == FSL_SYM_MODE_ECB)
+ || (auth_ctx->cipher_ctx_info.flags & FSL_SYM_CTX_INIT)) {
+ ptr1 = block_zeros;
+ } else {
+ ptr1 = auth_ctx->cipher_ctx_info.context;
+ }
+
+ DESC_IN_KEY(header, auth_ctx->cipher_ctx_info.block_size_bytes, ptr1,
+ cipher_key_info);
+
+ /* This is wrong -- need to find 16 extra bytes of data from
+ * somewhere...
+ */
+ if (payload_length != 0) {
+ DESC_IN_OUT(SAH_HDR_SKHA_ENC_DEC,
+ payload_length, payload, payload_length, ct);
+ }
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ /* Eliminate compiler warnings until full implementation... */
+ (void)auth_data;
+ (void)auth_data_length;
+
+ return ret;
+} /* do_ssl_gen() */
+#endif
+
+/*!
+ * @brief Generate a (CCM) auth code and encrypt the payload.
+ *
+ * This is a very complicated function. Seven (or eight) descriptors are
+ * required to perform a CCM calculation.
+ *
+ * First: Load CTR0 and key.
+ *
+ * Second: Run an octet of data through to bump to CTR1. (This could be
+ * done in software, but software will have to bump and later decrement -
+ * or copy and bump.
+ *
+ * Third: (in Virtio) Load a descriptor with data of zeros for CBC IV.
+ *
+ * Fourth: Run any (optional) "additional data" through the CBC-mode
+ * portion of the algorithm.
+ *
+ * Fifth: Run the payload through in CCM mode.
+ *
+ * Sixth: Extract the unencrypted MAC.
+ *
+ * Seventh: Load CTR0.
+ *
+ * Eighth: Encrypt the MAC.
+ *
+ * @param user_ctx The user's context
+ * @param auth_ctx Info on this Auth operation
+ * @param cipher_key_info Key to encrypt payload
+ * @param auth_key_info (unused - same key in CCM)
+ * @param auth_data_length Length in bytes of @a auth_data
+ * @param auth_data Any auth-only data
+ * @param payload_length Length in bytes of @a payload
+ * @param payload The data to encrypt
+ * @param[out] ct The location to store encrypted data
+ * @param[out] auth_value The location to store authentication code
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_gen_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * payload,
+ uint8_t * ct, uint8_t * auth_value)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ if (auth_ctx->mode == FSL_ACC_MODE_SSL) {
+#if SUPPORT_SSL
+ ret = do_ssl_gen(user_ctx, auth_ctx, cipher_key_info,
+ auth_key_info, auth_data_length, auth_data,
+ payload_length, payload, ct, auth_value);
+#else
+ ret = FSL_RETURN_BAD_MODE_S;
+#endif
+ goto out;
+ }
+
+ if (auth_ctx->mode != FSL_ACC_MODE_CCM) {
+ ret = FSL_RETURN_BAD_MODE_S;
+ goto out;
+ }
+
+ /* Only support INIT and FINALIZE flags right now. */
+ if ((auth_ctx->flags & (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_LOAD |
+ FSL_ACCO_CTX_SAVE | FSL_ACCO_CTX_FINALIZE))
+ != (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_FINALIZE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* Load CTR0 and Key */
+ header = (SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_mode_ctr
+ ^ sah_insert_skha_modulus_128 ^ sah_insert_skha_encrypt);
+ DESC_IN_KEY(header,
+ auth_ctx->cipher_ctx_info.block_size_bytes,
+ auth_ctx->cipher_ctx_info.context, cipher_key_info);
+
+ /* Encrypt dummy data to bump to CTR1 */
+ header = SAH_HDR_SKHA_ENC_DEC;
+ DESC_IN_OUT(header, auth_ctx->mac_length, garbage_output,
+ auth_ctx->mac_length, garbage_output);
+
+#if defined(FSL_HAVE_SAHARA2) || defined(USE_S2_CCM_ENCRYPT_CHAIN)
+#ifndef NO_ZERO_IV_LOAD
+ header = (SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_encrypt ^ sah_insert_skha_mode_cbc);
+ DESC_IN_IN(header,
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes,
+ block_zeros, 0, NULL);
+#endif
+#endif
+
+ ret = add_assoc_preamble(&desc_chain, user_ctx,
+ auth_ctx, 1, auth_data, auth_data_length);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Process the payload */
+ header = (SAH_HDR_SKHA_SET_MODE_ENC_DEC
+ ^ sah_insert_skha_mode_ccm
+ ^ sah_insert_skha_modulus_128 ^ sah_insert_skha_encrypt);
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_ENCRYPT_CHAIN)
+ header ^= sah_insert_skha_aux0;
+#endif
+ if (payload_length != 0) {
+ DESC_IN_OUT(header, payload_length, payload, payload_length,
+ ct);
+ } else {
+ DESC_IN_OUT(header, 0, NULL, 0, NULL);
+ } /* if payload_length */
+
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_ENCRYPT_CHAIN)
+ /* Pull out the CBC-MAC value. */
+ DESC_OUT_OUT(SAH_HDR_SKHA_READ_CONTEXT_IV, 0, NULL,
+ auth_ctx->mac_length, auth_value);
+#else
+ /* Pull out the unencrypted CBC-MAC value. */
+ DESC_OUT_OUT(SAH_HDR_SKHA_READ_CONTEXT_IV,
+ 0, NULL, auth_ctx->mac_length, auth_ctx->unencrypted_mac);
+
+ /* Now load CTR0 in, and encrypt the MAC */
+ header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_encrypt
+ ^ sah_insert_skha_mode_ctr ^ sah_insert_skha_modulus_128;
+ DESC_IN_IN(header,
+ auth_ctx->cipher_ctx_info.block_size_bytes,
+ auth_ctx->cipher_ctx_info.context, 0, NULL);
+
+ header = SAH_HDR_SKHA_ENC_DEC; /* Desc. #4 SKHA Enc/Dec */
+ DESC_IN_OUT(header,
+ auth_ctx->mac_length, auth_ctx->unencrypted_mac,
+ auth_ctx->mac_length, auth_value);
+#endif
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ (void)auth_key_info;
+ return ret;
+} /* fsl_shw_gen_encrypt() */
+
+/*!
+ * @brief Authenticate and decrypt a (CCM) stream.
+ *
+ * @param user_ctx The user's context
+ * @param auth_ctx Info on this Auth operation
+ * @param cipher_key_info Key to encrypt payload
+ * @param auth_key_info (unused - same key in CCM)
+ * @param auth_data_length Length in bytes of @a auth_data
+ * @param auth_data Any auth-only data
+ * @param payload_length Length in bytes of @a payload
+ * @param ct The encrypted data
+ * @param auth_value The authentication code to validate
+ * @param[out] payload The location to store decrypted data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_auth_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * ct,
+ const uint8_t * auth_value,
+ uint8_t * payload)
+{
+ SAH_SF_DCLS;
+#if defined(FSL_HAVE_SAHARA2) || defined(USE_S2_CCM_DECRYPT_CHAIN)
+ uint8_t *calced_auth = NULL;
+ unsigned blocking = user_ctx->flags & FSL_UCO_BLOCKING_MODE;
+#endif
+
+ SAH_SF_USER_CHECK();
+
+ /* Only support CCM */
+ if (auth_ctx->mode != FSL_ACC_MODE_CCM) {
+ ret = FSL_RETURN_BAD_MODE_S;
+ goto out;
+ }
+ /* Only support INIT and FINALIZE flags right now. */
+ if ((auth_ctx->flags & (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_LOAD |
+ FSL_ACCO_CTX_SAVE | FSL_ACCO_CTX_FINALIZE))
+ != (FSL_ACCO_CTX_INIT | FSL_ACCO_CTX_FINALIZE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* Load CTR0 and Key */
+ header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_mode_ctr ^ sah_insert_skha_modulus_128;
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_DECRYPT_CHAIN)
+ header ^= sah_insert_skha_aux0;
+#endif
+ DESC_IN_KEY(header,
+ auth_ctx->cipher_ctx_info.block_size_bytes,
+ auth_ctx->cipher_ctx_info.context, cipher_key_info);
+
+ /* Decrypt the MAC which the user passed in */
+ header = SAH_HDR_SKHA_ENC_DEC;
+ DESC_IN_OUT(header,
+ auth_ctx->mac_length, auth_value,
+ auth_ctx->mac_length, auth_ctx->unencrypted_mac);
+
+#if defined(FSL_HAVE_SAHARA2) || defined(USE_S2_CCM_DECRYPT_CHAIN)
+#ifndef NO_ZERO_IV_LOAD
+ header = (SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_encrypt ^ sah_insert_skha_mode_cbc);
+ DESC_IN_IN(header,
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes,
+ block_zeros, 0, NULL);
+#endif
+#endif
+
+ ret = add_assoc_preamble(&desc_chain, user_ctx,
+ auth_ctx, 0, auth_data, auth_data_length);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Process the payload */
+ header = (SAH_HDR_SKHA_SET_MODE_ENC_DEC
+ ^ sah_insert_skha_mode_ccm ^ sah_insert_skha_modulus_128);
+#if defined (FSL_HAVE_SAHARA4) && !defined (USE_S2_CCM_DECRYPT_CHAIN)
+ header ^= sah_insert_skha_aux0;
+#endif
+ if (payload_length != 0) {
+ DESC_IN_OUT(header, payload_length, ct, payload_length,
+ payload);
+ } else {
+ DESC_IN_OUT(header, 0, NULL, 0, NULL);
+ }
+
+#if defined (FSL_HAVE_SAHARA2) || defined (USE_S2_CCM_DECRYPT_CHAIN)
+ /* Now pull CBC context (unencrypted MAC) out for comparison. */
+ /* Need to allocate a place for it, to handle non-blocking mode
+ * when this stack frame will disappear!
+ */
+ calced_auth = DESC_TEMP_ALLOC(auth_ctx->mac_length);
+ header = SAH_HDR_SKHA_READ_CONTEXT_IV;
+ DESC_OUT_OUT(header, 0, NULL, auth_ctx->mac_length, calced_auth);
+ if (!blocking) {
+ /* get_results will need this for comparison */
+ desc_chain->out1_ptr = calced_auth;
+ desc_chain->out2_ptr = auth_ctx->unencrypted_mac;
+ desc_chain->out_len = auth_ctx->mac_length;
+ }
+#endif
+
+ SAH_SF_EXECUTE();
+
+#if defined (FSL_HAVE_SAHARA2) || defined (USE_S2_CCM_DECRYPT_CHAIN)
+ if (blocking && (ret == FSL_RETURN_OK_S)) {
+ unsigned i;
+ /* Validate the auth code */
+ for (i = 0; i < auth_ctx->mac_length; i++) {
+ if (calced_auth[i] != auth_ctx->unencrypted_mac[i]) {
+ ret = FSL_RETURN_AUTH_FAILED_S;
+ break;
+ }
+ }
+ }
+#endif
+
+ out:
+ SAH_SF_DESC_CLEAN();
+#if defined (FSL_HAVE_SAHARA2) || defined (USE_S2_CCM_DECRYPT_CHAIN)
+ DESC_TEMP_FREE(calced_auth);
+#endif
+
+ (void)auth_key_info;
+ return ret;
+} /* fsl_shw_gen_decrypt() */
diff --git a/drivers/mxc/security/sahara2/fsl_shw_hash.c b/drivers/mxc/security/sahara2/fsl_shw_hash.c
new file mode 100644
index 000000000000..1551173c1c62
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_hash.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_shw_hash.c
+ *
+ * This file implements Cryptographic Hashing functions of the FSL SHW API
+ * for Sahara. This does not include HMAC.
+ */
+
+#include "sahara.h"
+#include "sf_util.h"
+
+#ifdef LINUX_KERNEL
+EXPORT_SYMBOL(fsl_shw_hash);
+#endif
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-005 */
+/*!
+ * Hash a stream of data with a cryptographic hash algorithm.
+ *
+ * The flags in the @a hash_ctx control the operation of this function.
+ *
+ * Hashing functions work on 64 octets of message at a time. Therefore, when
+ * any partial hashing of a long message is performed, the message @a length of
+ * each segment must be a multiple of 64. When ready to
+ * #FSL_HASH_FLAGS_FINALIZE the hash, the @a length may be any value.
+ *
+ * With the #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_FINALIZE flags on, a
+ * one-shot complete hash, including padding, will be performed. The @a length
+ * may be any value.
+ *
+ * The first octets of a data stream can be hashed by setting the
+ * #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_SAVE flags. The @a length must be
+ * a multiple of 64.
+ *
+ * The flag #FSL_HASH_FLAGS_LOAD is used to load a context previously saved by
+ * #FSL_HASH_FLAGS_SAVE. The two in combination will allow a (multiple-of-64
+ * octets) 'middle sequence' of the data stream to be hashed with the
+ * beginning. The @a length must again be a multiple of 64.
+ *
+ * Since the flag #FSL_HASH_FLAGS_LOAD is used to load a context previously
+ * saved by #FSL_HASH_FLAGS_SAVE, the #FSL_HASH_FLAGS_LOAD and
+ * #FSL_HASH_FLAGS_FINALIZE flags, used together, can be used to finish the
+ * stream. The @a length may be any value.
+ *
+ * If the user program wants to do the padding for the hash, it can leave off
+ * the #FSL_HASH_FLAGS_FINALIZE flag. The @a length must then be a multiple of
+ * 64 octets.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] hash_ctx Hashing algorithm and state of the cipher.
+ * @param msg Pointer to the data to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result If not null, pointer to where to store the hash
+ * digest.
+ * @param result_len Number of octets to store in @a result.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
+ fsl_shw_hco_t * hash_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ SAH_SF_DCLS;
+ unsigned ctx_flags = (hash_ctx->flags & (FSL_HASH_FLAGS_INIT
+ | FSL_HASH_FLAGS_LOAD
+ | FSL_HASH_FLAGS_SAVE
+ | FSL_HASH_FLAGS_FINALIZE));
+
+ SAH_SF_USER_CHECK();
+
+ /* Reset expectations if user gets overly zealous. */
+ if (result_len > hash_ctx->digest_length) {
+ result_len = hash_ctx->digest_length;
+ }
+
+ /* Validate hash ctx flags.
+ * Need INIT or LOAD but not both.
+ * Need SAVE or digest ptr (both is ok).
+ */
+ if (((ctx_flags & (FSL_HASH_FLAGS_INIT | FSL_HASH_FLAGS_LOAD))
+ == (FSL_HASH_FLAGS_INIT | FSL_HASH_FLAGS_LOAD))
+ || ((ctx_flags & (FSL_HASH_FLAGS_INIT | FSL_HASH_FLAGS_LOAD)) == 0)
+ || (!(ctx_flags & FSL_HASH_FLAGS_SAVE) && (result == NULL))) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ if (ctx_flags & FSL_HASH_FLAGS_INIT) {
+ sah_Oct_Str out_ptr;
+ unsigned out_len;
+
+ /* Create desc to perform the initial hashing operation */
+ /* Desc. #8 w/INIT and algorithm */
+ header = SAH_HDR_MDHA_SET_MODE_HASH
+ ^ sah_insert_mdha_init
+ ^ sah_insert_mdha_algorithm[hash_ctx->algorithm];
+
+ /* If user wants one-shot, set padding operation. */
+ if (ctx_flags & FSL_HASH_FLAGS_FINALIZE) {
+ header ^= sah_insert_mdha_pdata;
+ }
+
+ /* Determine where Digest will go - hash_ctx or result */
+ if (ctx_flags & FSL_HASH_FLAGS_SAVE) {
+ out_ptr = (sah_Oct_Str) hash_ctx->context;
+ out_len = hash_ctx->context_register_length;
+ } else {
+ out_ptr = result;
+ out_len = (result_len > hash_ctx->digest_length)
+ ? hash_ctx->digest_length : result_len;
+ }
+
+ DESC_IN_OUT(header, length, (sah_Oct_Str) msg, out_len,
+ out_ptr);
+ } else { /* not doing hash INIT */
+ void *out_ptr;
+ unsigned out_len;
+
+ /*
+ * Build two descriptors -- one to load in context/set mode, the
+ * other to compute & retrieve hash/context value.
+ *
+ * First up - Desc. #6 to load context.
+ */
+ /* Desc. #8 w/algorithm */
+ header = SAH_HDR_MDHA_SET_MODE_MD_KEY
+ ^ sah_insert_mdha_algorithm[hash_ctx->algorithm];
+
+ if (ctx_flags & FSL_HASH_FLAGS_FINALIZE) {
+ header ^= sah_insert_mdha_pdata;
+ }
+
+ /* Message Digest (in) */
+ DESC_IN_IN(header,
+ hash_ctx->context_register_length,
+ (sah_Oct_Str) hash_ctx->context, 0, NULL);
+
+ if (ctx_flags & FSL_HASH_FLAGS_SAVE) {
+ out_ptr = hash_ctx->context;
+ out_len = hash_ctx->context_register_length;
+ } else {
+ out_ptr = result;
+ out_len = result_len;
+ }
+
+ /* Second -- run data through and retrieve ctx regs */
+ /* Desc. #10 - no mode register with this. */
+ header = SAH_HDR_MDHA_HASH;
+ DESC_IN_OUT(header, length, (sah_Oct_Str) msg, out_len,
+ out_ptr);
+ } /* else not INIT */
+
+ /* Now that execution is rejoined, we can append another descriptor
+ to extract the digest/context a second time, into the result. */
+ if ((ctx_flags & FSL_HASH_FLAGS_SAVE)
+ && (result != NULL) && (result_len != 0)) {
+
+ header = SAH_HDR_MDHA_STORE_DIGEST;
+
+ /* Message Digest (out) */
+ DESC_IN_OUT(header, 0, NULL,
+ (result_len > hash_ctx->digest_length)
+ ? hash_ctx->digest_length : result_len, result);
+ }
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+} /* fsl_shw_hash() */
diff --git a/drivers/mxc/security/sahara2/fsl_shw_hmac.c b/drivers/mxc/security/sahara2/fsl_shw_hmac.c
new file mode 100644
index 000000000000..4184d9b280dd
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_hmac.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_shw_hmac.c
+ *
+ * This file implements Hashed Message Authentication Code functions of the FSL
+ * SHW API for Sahara.
+ */
+
+#include "sahara.h"
+#include "sf_util.h"
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_hmac_precompute);
+EXPORT_SYMBOL(fsl_shw_hmac);
+#endif
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-001 */
+/*!
+ * Get the precompute information
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param hmac_ctx
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hmac_precompute(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ if ((key_info->algorithm != FSL_KEY_ALG_HMAC) ||
+ (key_info->key_length > 64)) {
+ return FSL_RETURN_BAD_ALGORITHM_S;
+ } else if (key_info->key_length == 0) {
+ return FSL_RETURN_BAD_KEY_LENGTH_S;
+ }
+
+ /* Set up to start the Inner Calculation */
+ /* Desc. #8 w/IPAD, & INIT */
+ header = SAH_HDR_MDHA_SET_MODE_HASH
+ ^ sah_insert_mdha_ipad
+ ^ sah_insert_mdha_init
+ ^ sah_insert_mdha_algorithm[hmac_ctx->algorithm];
+
+ DESC_KEY_OUT(header, key_info,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->inner_precompute);
+
+ /* Set up for starting Outer calculation */
+ /* exchange IPAD bit for OPAD bit */
+ header ^= (sah_insert_mdha_ipad ^ sah_insert_mdha_opad);
+
+ /* Theoretically, we can leave this link out and use the key which is
+ * already in the register... however, if we do, the resulting opad
+ * hash does not have the correct value when using the model. */
+ DESC_KEY_OUT(header, key_info,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->outer_precompute);
+
+ SAH_SF_EXECUTE();
+ if (ret == FSL_RETURN_OK_S) {
+ /* flag that precomputes have been entered in this hco
+ * assume it'll first be used for initilizing */
+ hmac_ctx->flags |= (FSL_HMAC_FLAGS_INIT |
+ FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT);
+ }
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-002 */
+/*!
+ * Get the hmac
+ *
+ *
+ * @param user_ctx Info for acquiring memory
+ * @param key_info
+ * @param hmac_ctx
+ * @param msg
+ * @param length
+ * @param result
+ * @param result_len
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_hmac(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ /* check flag consistency */
+ /* Note that Final, Init, and Save are an illegal combination when a key
+ * is being used. Because of the logic flow of this routine, that is
+ * taken care of without being explict */
+ if (
+ /* nothing to work on */
+ (((hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) == 0) &&
+ ((hmac_ctx->flags & FSL_HMAC_FLAGS_LOAD) == 0)) ||
+ /* can't do both */
+ ((hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) &&
+ (hmac_ctx->flags & FSL_HMAC_FLAGS_LOAD)) ||
+ /* must be some output */
+ (((hmac_ctx->flags & FSL_HMAC_FLAGS_SAVE) == 0) &&
+ ((hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) == 0))) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* build descriptor #6 */
+
+ /* start building descriptor header */
+ header = SAH_HDR_MDHA_SET_MODE_MD_KEY ^
+ sah_insert_mdha_algorithm[hmac_ctx->algorithm] ^
+ sah_insert_mdha_init;
+
+ /* if this is to finalize the digest, mark to pad last block */
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) {
+ header ^= sah_insert_mdha_pdata;
+ }
+
+ /* Check if this is a one shot */
+ if ((hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) &&
+ (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE)) {
+
+ header ^= sah_insert_mdha_hmac;
+
+ /* See if this uses Precomputes */
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT) {
+ DESC_IN_IN(header,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->inner_precompute,
+ hmac_ctx->context_length,
+ (uint8_t *) hmac_ctx->outer_precompute);
+ } else { /* Precomputes not requested, try using Key */
+ if (key_info->key != NULL) {
+ /* first, validate the key fields and related flag */
+ if ((key_info->key_length == 0)
+ || (key_info->key_length > 64)) {
+ ret = FSL_RETURN_BAD_KEY_LENGTH_S;
+ goto out;
+ } else {
+ if (key_info->algorithm !=
+ FSL_KEY_ALG_HMAC) {
+ ret =
+ FSL_RETURN_BAD_ALGORITHM_S;
+ goto out;
+ }
+ }
+
+ /* finish building descriptor header (Key specific) */
+ header ^= sah_insert_mdha_mac_full;
+ DESC_IN_KEY(header, 0, NULL, key_info);
+ } else { /* not using Key or Precomputes, so die */
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+ }
+ } else { /* it's not a one shot, must be multi-step */
+ /* this the last chunk? */
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) {
+ header ^= sah_insert_mdha_hmac;
+ DESC_IN_IN(header,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->ongoing_context,
+ hmac_ctx->context_length,
+ (uint8_t *) hmac_ctx->outer_precompute);
+ } else { /* not last chunk */
+ uint8_t *ptr1;
+
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) {
+ /* must be using precomputes, cannot 'chunk' message
+ * starting with a key */
+ if (hmac_ctx->
+ flags & FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT)
+ {
+ ptr1 =
+ (uint8_t *) hmac_ctx->
+ inner_precompute;
+ } else {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ goto out;
+ }
+ } else {
+ ptr1 = (uint8_t *) hmac_ctx->ongoing_context;
+ }
+
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ DESC_IN_IN(header,
+ hmac_ctx->context_register_length, ptr1,
+ 0, NULL);
+ }
+ } /* multi-step */
+
+ /* build descriptor #10 & maybe 11 */
+ header = SAH_HDR_MDHA_HASH;
+
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) {
+ /* check that the results parameters seem reasonable */
+ if ((result_len != 0) && (result != NULL)) {
+ if (result_len > hmac_ctx->context_register_length) {
+ result_len = hmac_ctx->context_register_length;
+ }
+
+ /* message in / digest out (descriptor #10) */
+ DESC_IN_OUT(header, length, msg, result_len, result);
+
+ /* see if descriptor #11 needs to be built */
+ if (hmac_ctx->flags & FSL_HMAC_FLAGS_SAVE) {
+ header = SAH_HDR_MDHA_STORE_DIGEST;
+ /* nothing in / context out */
+ DESC_IN_IN(header, 0, NULL,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->
+ ongoing_context);
+ }
+ } else {
+ /* something wrong with result or its length */
+ ret = FSL_RETURN_BAD_DATA_LENGTH_S;
+ }
+ } else { /* finalize not set, so store in ongoing context field */
+ if ((length % 64) == 0) { /* this will change for 384/512 support */
+ /* message in / context out */
+ DESC_IN_OUT(header, length, msg,
+ hmac_ctx->context_register_length,
+ (uint8_t *) hmac_ctx->ongoing_context);
+ } else {
+ /* not final data, and not multiple of block length */
+ ret = FSL_RETURN_BAD_DATA_LENGTH_S;
+ }
+ }
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+} /* fsl_shw_hmac() */
diff --git a/drivers/mxc/security/sahara2/fsl_shw_keystore.c b/drivers/mxc/security/sahara2/fsl_shw_keystore.c
new file mode 100644
index 000000000000..3a8a22a81305
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_keystore.c
@@ -0,0 +1,837 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/**
+ * @file fsl_shw_keystore.c
+ *
+ * File which implements a default keystore policy, for use as the system
+ * keystore.
+ */
+#include "fsl_platform.h"
+#include "fsl_shw.h"
+#include "fsl_shw_keystore.h"
+
+#if defined(DIAG_DRV_IF)
+#include <diagnostic.h>
+#endif
+
+#if !defined(FSL_HAVE_SCC2) && defined(__KERNEL__)
+#include <linux/mxc_scc_driver.h>
+#endif
+
+/* Define a semaphore to protect the keystore data */
+#ifdef __KERNEL__
+#define LOCK_INCLUDES os_lock_context_t context
+#define ACQUIRE_LOCK os_lock_save_context(keystore->lock, context)
+#define RELEASE_LOCK os_unlock_restore_context(keystore->lock, context);
+#else
+#define LOCK_INCLUDES
+#define ACQUIRE_LOCK
+#define RELEASE_LOCK
+#endif /* __KERNEL__ */
+
+/*!
+ * Calculates the byte offset into a word
+ * @param bp The byte (char*) pointer
+ * @return The offset (0, 1, 2, or 3)
+ */
+#define SCC_BYTE_OFFSET(bp) ((uint32_t)(bp) % sizeof(uint32_t))
+
+/*!
+ * Converts (by rounding down) a byte pointer into a word pointer
+ * @param bp The byte (char*) pointer
+ * @return The word (uint32_t) as though it were an aligned (uint32_t*)
+ */
+#define SCC_WORD_PTR(bp) (((uint32_t)(bp)) & ~(sizeof(uint32_t)-1))
+
+/* Depending on the architecture, these functions should be defined
+ * differently. On Platforms with SCC2, the functions use the secure
+ * partition interface and should be available in both user and kernel space.
+ * On platforms with SCC, they use the SCC keystore interface. This is only
+ * available in kernel mode, so they should be stubbed out in user mode.
+ */
+#if defined(FSL_HAVE_SCC2) || (defined(FSL_HAVE_SCC) && defined(__KERNEL__))
+EXPORT_SYMBOL(fsl_shw_init_keystore);
+void fsl_shw_init_keystore(
+ fsl_shw_kso_t *keystore,
+ fsl_shw_return_t(*data_init) (fsl_shw_uco_t *user_ctx,
+ void **user_data),
+ void (*data_cleanup) (fsl_shw_uco_t *user_ctx,
+ void **user_data),
+ fsl_shw_return_t(*slot_alloc) (void *user_data,
+ uint32_t size,
+ uint64_t owner_id,
+ uint32_t *slot),
+ fsl_shw_return_t(*slot_dealloc) (void *user_data,
+ uint64_t
+ owner_id,
+ uint32_t slot),
+ fsl_shw_return_t(*slot_verify_access) (void
+ *user_data,
+ uint64_t
+ owner_id,
+ uint32_t
+ slot),
+ void *(*slot_get_address) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_base) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_offset) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_slot_size) (void *user_data,
+ uint32_t handle))
+{
+ keystore->data_init = data_init;
+ keystore->data_cleanup = data_cleanup;
+ keystore->slot_alloc = slot_alloc;
+ keystore->slot_dealloc = slot_dealloc;
+ keystore->slot_verify_access = slot_verify_access;
+ keystore->slot_get_address = slot_get_address;
+ keystore->slot_get_base = slot_get_base;
+ keystore->slot_get_offset = slot_get_offset;
+ keystore->slot_get_slot_size = slot_get_slot_size;
+}
+
+EXPORT_SYMBOL(fsl_shw_init_keystore_default);
+void fsl_shw_init_keystore_default(fsl_shw_kso_t *keystore)
+{
+ keystore->data_init = shw_kso_init_data;
+ keystore->data_cleanup = shw_kso_cleanup_data;
+ keystore->slot_alloc = shw_slot_alloc;
+ keystore->slot_dealloc = shw_slot_dealloc;
+ keystore->slot_verify_access = shw_slot_verify_access;
+ keystore->slot_get_address = shw_slot_get_address;
+ keystore->slot_get_base = shw_slot_get_base;
+ keystore->slot_get_offset = shw_slot_get_offset;
+ keystore->slot_get_slot_size = shw_slot_get_slot_size;
+}
+
+/*!
+ * Do any keystore specific initializations
+ */
+EXPORT_SYMBOL(fsl_shw_establish_keystore);
+fsl_shw_return_t fsl_shw_establish_keystore(fsl_shw_uco_t *user_ctx,
+ fsl_shw_kso_t *keystore)
+{
+ if (keystore->data_init == NULL) {
+ return FSL_RETURN_ERROR_S;
+ }
+
+ /* Call the data_init function for any user setup */
+ return keystore->data_init(user_ctx, &(keystore->user_data));
+}
+
+EXPORT_SYMBOL(fsl_shw_release_keystore);
+void fsl_shw_release_keystore(fsl_shw_uco_t *user_ctx,
+ fsl_shw_kso_t *keystore)
+{
+
+ /* Call the data_cleanup function for any keystore cleanup.
+ * NOTE: The keystore doesn't have any way of telling which keys are using
+ * it, so it is up to the user program to manage their key objects
+ * correctly.
+ */
+ if ((keystore != NULL) && (keystore->data_cleanup != NULL)) {
+ keystore->data_cleanup(user_ctx, &(keystore->user_data));
+ }
+ return;
+}
+
+fsl_shw_return_t keystore_slot_alloc(fsl_shw_kso_t *keystore, uint32_t size,
+ uint64_t owner_id, uint32_t *slot)
+{
+ LOCK_INCLUDES;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+
+#ifdef DIAG_DRV_IF
+ LOG_DIAG("In keystore_slot_alloc.");
+
+#endif
+ ACQUIRE_LOCK;
+ if ((keystore->slot_alloc == NULL) || (keystore->user_data == NULL)) {
+ goto out;
+ }
+
+#ifdef DIAG_DRV_IF
+ LOG_DIAG_ARGS("key length: %i, handle: %i\n", size, *slot);
+
+#endif
+retval = keystore->slot_alloc(keystore->user_data, size, owner_id, slot);
+out:RELEASE_LOCK;
+ return retval;
+}
+
+fsl_shw_return_t keystore_slot_dealloc(fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot)
+{
+ LOCK_INCLUDES;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ ACQUIRE_LOCK;
+ if ((keystore->slot_alloc == NULL) || (keystore->user_data == NULL)) {
+ goto out;
+ }
+ retval =
+ keystore->slot_dealloc(keystore->user_data, owner_id, slot);
+out:RELEASE_LOCK;
+ return retval;
+}
+
+fsl_shw_return_t
+keystore_slot_load(fsl_shw_kso_t * keystore, uint64_t owner_id, uint32_t slot,
+ const uint8_t * key_data, uint32_t key_length)
+{
+
+#ifdef FSL_HAVE_SCC2
+ LOCK_INCLUDES;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ uint32_t slot_size;
+ uint32_t i;
+ uint8_t * slot_location;
+ ACQUIRE_LOCK;
+ if ((keystore->slot_verify_access == NULL) ||
+ (keystore->user_data == NULL))
+ goto out;
+ if (keystore->
+ slot_verify_access(keystore->user_data, owner_id,
+ slot) !=FSL_RETURN_OK_S) {
+ retval = FSL_RETURN_AUTH_FAILED_S;
+ goto out;
+ }
+ slot_size = keystore->slot_get_slot_size(keystore->user_data, slot);
+ if (key_length > slot_size) {
+ retval = FSL_RETURN_BAD_DATA_LENGTH_S;
+ goto out;
+ }
+ slot_location = keystore->slot_get_address(keystore->user_data, slot);
+ for (i = 0; i < key_length; i++) {
+ slot_location[i] = key_data[i];
+ }
+ retval = FSL_RETURN_OK_S;
+out:RELEASE_LOCK;
+ return retval;
+
+#else /* FSL_HAVE_SCC2 */
+ fsl_shw_return_t retval;
+ scc_return_t scc_ret;
+ scc_ret =
+ scc_load_slot(owner_id, slot, (uint8_t *) key_data, key_length);
+ switch (scc_ret) {
+ case SCC_RET_OK:
+ retval = FSL_RETURN_OK_S;
+ break;
+ case SCC_RET_VERIFICATION_FAILED:
+ retval = FSL_RETURN_AUTH_FAILED_S;
+ break;
+ case SCC_RET_INSUFFICIENT_SPACE:
+ retval = FSL_RETURN_BAD_DATA_LENGTH_S;
+ break;
+ default:
+ retval = FSL_RETURN_ERROR_S;
+ }
+ return retval;
+
+#endif /* FSL_HAVE_SCC2 */
+}
+
+fsl_shw_return_t
+keystore_slot_read(fsl_shw_kso_t * keystore, uint64_t owner_id, uint32_t slot,
+ uint32_t key_length, uint8_t * key_data)
+{
+#ifdef FSL_HAVE_SCC2
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ uint8_t *slot_addr;
+ uint32_t slot_size;
+
+ slot_addr = keystore->slot_get_address(keystore->user_data, slot);
+ slot_size = keystore->slot_get_slot_size(keystore->user_data, slot);
+
+ if (key_length > slot_size) {
+ retval = FSL_RETURN_BAD_KEY_LENGTH_S;
+ goto out;
+ }
+
+ memcpy(key_data, slot_addr, key_length);
+ retval = FSL_RETURN_OK_S;
+
+ out:
+ return retval;
+
+#else /* Have SCC2 */
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ scc_return_t scc_ret;
+ printk("keystore SCC \n");
+
+ scc_ret =
+ scc_read_slot(owner_id, slot, key_length, (uint8_t *) key_data);
+ printk("keystore SCC Ret value: %d \n", scc_ret);
+ switch (scc_ret) {
+ case SCC_RET_OK:
+ retval = FSL_RETURN_OK_S;
+ break;
+ case SCC_RET_VERIFICATION_FAILED:
+ retval = FSL_RETURN_AUTH_FAILED_S;
+ break;
+ case SCC_RET_INSUFFICIENT_SPACE:
+ retval = FSL_RETURN_BAD_DATA_LENGTH_S;
+ break;
+ default:
+ retval = FSL_RETURN_ERROR_S;
+ }
+
+ return retval;
+
+#endif /* FSL_HAVE_SCC2 */
+}/* end fn keystore_slot_read */
+
+fsl_shw_return_t
+keystore_slot_encrypt(fsl_shw_uco_t *user_ctx, fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot, uint32_t length,
+ uint8_t *destination)
+{
+
+#ifdef FSL_HAVE_SCC2
+ LOCK_INCLUDES;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ uint32_t slot_length;
+ uint32_t IV[4];
+ uint32_t * iv_ptr = (uint32_t *) & (owner_id);
+
+ /* Build the IV */
+ IV[0] = iv_ptr[0];
+ IV[1] = iv_ptr[1];
+ IV[2] = 0;
+ IV[3] = 0;
+ ACQUIRE_LOCK;
+
+ /* Ensure that the data will fit in the key slot */
+ slot_length =
+ keystore->slot_get_slot_size(keystore->user_data, slot);
+ if (length > slot_length) {
+ goto out;
+ }
+
+ /* Call scc encrypt function to encrypt the data. */
+ retval = do_scc_encrypt_region(user_ctx,
+ (void *)keystore->
+ slot_get_base(keystore->user_data,
+ slot),
+ keystore->slot_get_offset(keystore->
+ user_data,
+ slot),
+ length, destination, IV,
+ FSL_SHW_CYPHER_MODE_CBC);
+ goto out;
+out:RELEASE_LOCK;
+ return retval;
+
+#else
+ scc_return_t retval;
+ retval = scc_encrypt_slot(owner_id, slot, length, destination);
+ if (retval == SCC_RET_OK)
+ return FSL_RETURN_OK_S;
+ return FSL_RETURN_ERROR_S;
+
+#endif /* FSL_HAVE_SCC2 */
+}
+
+fsl_shw_return_t
+keystore_slot_decrypt(fsl_shw_uco_t *user_ctx, fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot, uint32_t length,
+ const uint8_t *source)
+{
+
+#ifdef FSL_HAVE_SCC2
+ LOCK_INCLUDES;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+ uint32_t slot_length;
+ uint32_t IV[4];
+ uint32_t *iv_ptr = (uint32_t *) & (owner_id);
+
+ /* Build the IV */
+ IV[0] = iv_ptr[0];
+ IV[1] = iv_ptr[1];
+ IV[2] = 0;
+ IV[3] = 0;
+ ACQUIRE_LOCK;
+
+ /* Call scc decrypt function to decrypt the data. */
+
+ /* Ensure that the data will fit in the key slot */
+ slot_length =
+ keystore->slot_get_slot_size(keystore->user_data, slot);
+ if (length > slot_length)
+ goto out;
+
+ /* Call scc decrypt function to encrypt the data. */
+ retval = do_scc_decrypt_region(user_ctx,
+ (void *)keystore->
+ slot_get_base(keystore->user_data,
+ slot),
+ keystore->slot_get_offset(keystore->
+ user_data,
+ slot),
+ length, source, IV,
+ FSL_SHW_CYPHER_MODE_CBC);
+ goto out;
+out:RELEASE_LOCK;
+ return retval;
+
+#else
+ scc_return_t retval;
+ retval = scc_decrypt_slot(owner_id, slot, length, source);
+ if (retval == SCC_RET_OK)
+ return FSL_RETURN_OK_S;
+ return FSL_RETURN_ERROR_S;
+
+#endif /* FSL_HAVE_SCC2 */
+}
+
+#else /* SCC in userspace */
+void fsl_shw_init_keystore(
+ fsl_shw_kso_t *keystore,
+ fsl_shw_return_t(*data_init) (fsl_shw_uco_t *user_ctx,
+ void **user_data),
+ void (*data_cleanup) (fsl_shw_uco_t *user_ctx,
+ void **user_data),
+ fsl_shw_return_t(*slot_alloc) (void *user_data,
+ uint32_t size,
+ uint64_t owner_id,
+ uint32_t *slot),
+ fsl_shw_return_t(*slot_dealloc) (void *user_data,
+ uint64_t
+ owner_id,
+ uint32_t slot),
+ fsl_shw_return_t(*slot_verify_access) (void
+ *user_data,
+ uint64_t
+ owner_id,
+ uint32_t
+ slot),
+ void *(*slot_get_address) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_base) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_offset) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_slot_size) (void *user_data,
+ uint32_t handle))
+{
+ (void)keystore;
+ (void)data_init;
+ (void)data_cleanup;
+ (void)slot_alloc;
+ (void)slot_dealloc;
+ (void)slot_verify_access;
+ (void)slot_get_address;
+ (void)slot_get_base;
+ (void)slot_get_offset;
+ (void)slot_get_slot_size;
+}
+
+void fsl_shw_init_keystore_default(fsl_shw_kso_t * keystore)
+{
+ (void)keystore;
+}
+fsl_shw_return_t fsl_shw_establish_keystore(fsl_shw_uco_t *user_ctx,
+ fsl_shw_kso_t *keystore)
+{
+ (void)user_ctx;
+ (void)keystore;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+void fsl_shw_release_keystore(fsl_shw_uco_t *user_ctx,
+ fsl_shw_kso_t *keystore)
+{
+ (void)user_ctx;
+ (void)keystore;
+ return;
+}
+
+fsl_shw_return_t keystore_slot_alloc(fsl_shw_kso_t *keystore, uint32_t size,
+ uint64_t owner_id, uint32_t *slot)
+{
+ (void)keystore;
+ (void)size;
+ (void)owner_id;
+ (void)slot;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t keystore_slot_dealloc(fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot)
+{
+ (void)keystore;
+ (void)owner_id;
+ (void)slot;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t
+keystore_slot_load(fsl_shw_kso_t *keystore, uint64_t owner_id, uint32_t slot,
+ const uint8_t *key_data, uint32_t key_length)
+{
+ (void)keystore;
+ (void)owner_id;
+ (void)slot;
+ (void)key_data;
+ (void)key_length;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t
+keystore_slot_read(fsl_shw_kso_t * keystore, uint64_t owner_id, uint32_t slot,
+ uint32_t key_length, uint8_t * key_data)
+{
+ (void)keystore;
+ (void)owner_id;
+ (void)slot;
+ (void)key_length;
+ (void)key_data;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t
+keystore_slot_decrypt(fsl_shw_uco_t *user_ctx, fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot, uint32_t length,
+ const uint8_t *source)
+{
+ (void)user_ctx;
+ (void)keystore;
+ (void)owner_id;
+ (void)slot;
+ (void)length;
+ (void)source;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t
+keystore_slot_encrypt(fsl_shw_uco_t *user_ctx, fsl_shw_kso_t *keystore,
+ uint64_t owner_id, uint32_t slot, uint32_t length,
+ uint8_t *destination)
+{
+ (void)user_ctx;
+ (void)keystore;
+ (void)owner_id;
+ (void)slot;
+ (void)length;
+ (void)destination;
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+
+#endif /* FSL_HAVE_SCC2 */
+
+/***** Default keystore implementation **************************************/
+
+#ifdef FSL_HAVE_SCC2
+ fsl_shw_return_t shw_kso_init_data(fsl_shw_uco_t *user_ctx,
+ void **user_data)
+{
+ int retval = FSL_RETURN_ERROR_S;
+ keystore_data_t *keystore_data = NULL;
+ fsl_shw_pco_t *capabilities = fsl_shw_get_capabilities(user_ctx);
+ uint32_t partition_size;
+ uint32_t slot_count;
+ uint32_t keystore_data_size;
+ uint8_t UMID[16] = {
+ 0x42, 0, 0, 0, 0x43, 0, 0, 0, 0x19, 0, 0, 0, 0x59, 0, 0, 0};
+ uint32_t permissions =
+ FSL_PERM_TH_R | FSL_PERM_TH_W | FSL_PERM_HD_R | FSL_PERM_HD_W |
+ FSL_PERM_HD_X;
+
+ /* Look up the size of a partition to see how big to make the keystore */
+ partition_size = fsl_shw_pco_get_spo_size_bytes(capabilities);
+
+ /* Calculate the required size of the keystore data structure, based on the
+ * number of keys that can fit in the partition.
+ */
+ slot_count = partition_size / KEYSTORE_SLOT_SIZE;
+ keystore_data_size =
+ sizeof(keystore_data_t) +
+ slot_count * sizeof(keystore_data_slot_info_t);
+
+#ifdef __KERNEL__
+ keystore_data = os_alloc_memory(keystore_data_size, GFP_KERNEL);
+
+#else
+ keystore_data = malloc(keystore_data_size);
+
+#endif
+ if (keystore_data == NULL) {
+ retval = FSL_RETURN_NO_RESOURCE_S;
+ goto out;
+ }
+
+ /* Clear the memory (effectively clear all key assignments) */
+ memset(keystore_data, 0, keystore_data_size);
+
+ /* Place the slot information structure directly after the keystore data
+ * structure.
+ */
+ keystore_data->slot =
+ (keystore_data_slot_info_t *) (keystore_data + 1);
+ keystore_data->slot_count = slot_count;
+
+ /* Retrieve a secure partition to put the keystore in. */
+ keystore_data->base_address =
+ fsl_shw_smalloc(user_ctx, partition_size, UMID, permissions);
+ if (keystore_data->base_address == NULL) {
+ retval = FSL_RETURN_NO_RESOURCE_S;
+ goto out;
+ }
+ *user_data = keystore_data;
+ retval = FSL_RETURN_OK_S;
+out:if (retval != FSL_RETURN_OK_S) {
+ if (keystore_data != NULL) {
+ if (keystore_data->base_address != NULL)
+ fsl_shw_sfree(NULL,
+ keystore_data->base_address);
+
+#ifdef __KERNEL__
+ os_free_memory(keystore_data);
+
+#else
+ free(keystore_data);
+
+#endif
+ }
+ }
+ return retval;
+}
+void shw_kso_cleanup_data(fsl_shw_uco_t *user_ctx, void **user_data)
+{
+ if (user_data != NULL) {
+ keystore_data_t * keystore_data =
+ (keystore_data_t *) (*user_data);
+ fsl_shw_sfree(user_ctx, keystore_data->base_address);
+
+#ifdef __KERNEL__
+ os_free_memory(*user_data);
+
+#else
+ free(*user_data);
+
+#endif
+ }
+ return;
+}
+
+fsl_shw_return_t shw_slot_verify_access(void *user_data, uint64_t owner_id,
+ uint32_t slot)
+{
+ keystore_data_t * data = user_data;
+ if (data->slot[slot].owner == owner_id) {
+ return FSL_RETURN_OK_S;
+ } else {
+
+#ifdef DIAG_DRV_IF
+ LOG_DIAG_ARGS("Access to slot %i fails.\n", slot);
+
+#endif
+ return FSL_RETURN_AUTH_FAILED_S;
+ }
+}
+
+fsl_shw_return_t shw_slot_alloc(void *user_data, uint32_t size,
+ uint64_t owner_id, uint32_t *slot)
+{
+ keystore_data_t *data = user_data;
+ uint32_t i;
+ if (size > KEYSTORE_SLOT_SIZE)
+ return FSL_RETURN_BAD_KEY_LENGTH_S;
+ for (i = 0; i < data->slot_count; i++) {
+ if (data->slot[i].allocated == 0) {
+ data->slot[i].allocated = 1;
+ data->slot[i].owner = owner_id;
+ (*slot) = i;
+
+#ifdef DIAG_DRV_IF
+ LOG_DIAG_ARGS("Keystore: allocated slot %i. Slot "
+ "address: %p\n",
+ (*slot),
+ data->base_address +
+ (*slot) * KEYSTORE_SLOT_SIZE);
+
+#endif
+ return FSL_RETURN_OK_S;
+ }
+ }
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t shw_slot_dealloc(void *user_data, uint64_t owner_id,
+ uint32_t slot)
+{
+ keystore_data_t * data = user_data;
+ (void)owner_id;
+ (void)slot;
+ if (slot >= data->slot_count)
+ return FSL_RETURN_ERROR_S;
+ if (data->slot[slot].allocated == 1) {
+ /* Forcibly remove the data from the keystore */
+ memset(shw_slot_get_address(user_data, slot), 0,
+ KEYSTORE_SLOT_SIZE);
+ data->slot[slot].allocated = 0;
+ return FSL_RETURN_OK_S;
+ }
+ return FSL_RETURN_ERROR_S;
+}
+
+void *shw_slot_get_address(void *user_data, uint32_t slot)
+{
+ keystore_data_t * data = user_data;
+ if (slot >= data->slot_count)
+ return NULL;
+ return data->base_address + slot * KEYSTORE_SLOT_SIZE;
+}
+
+uint32_t shw_slot_get_base(void *user_data, uint32_t slot)
+{
+ keystore_data_t * data = user_data;
+
+ /* There could potentially be more than one secure partition object
+ * associated with this keystore. For now, there is just one.
+ */
+ (void)slot;
+ return (uint32_t) (data->base_address);
+}
+
+uint32_t shw_slot_get_offset(void *user_data, uint32_t slot)
+{
+ keystore_data_t *data = user_data;
+ if (slot >= data->slot_count)
+ return FSL_RETURN_ERROR_S;
+ return (slot * KEYSTORE_SLOT_SIZE);
+}
+
+uint32_t shw_slot_get_slot_size(void *user_data, uint32_t slot)
+{
+ (void)user_data;
+ (void)slot;
+
+ /* All slots are the same size in the default implementation */
+ return KEYSTORE_SLOT_SIZE;
+}
+
+#else /* FSL_HAVE_SCC2 */
+
+#ifdef __KERNEL__
+ fsl_shw_return_t shw_kso_init_data(fsl_shw_uco_t *user_ctx,
+ void **user_data)
+{
+
+ /* The SCC does its own initialization. All that needs to be done here is
+ * make sure an SCC exists.
+ */
+ *user_data = (void *)0xFEEDFEED;
+ return FSL_RETURN_OK_S;
+}
+void shw_kso_cleanup_data(fsl_shw_uco_t *user_ctx, void **user_data)
+{
+
+ /* The SCC does its own cleanup. */
+ *user_data = NULL;
+ return;
+}
+
+fsl_shw_return_t shw_slot_verify_access(void *user_data, uint64_t owner_id,
+ uint32_t slot)
+{
+
+ /* Zero is used for the size because the newer interface does bounds
+ * checking later.
+ */
+ scc_return_t retval;
+ retval = scc_verify_slot_access(owner_id, slot, 0);
+ if (retval == SCC_RET_OK) {
+ return FSL_RETURN_OK_S;
+ }
+ return FSL_RETURN_AUTH_FAILED_S;
+}
+
+fsl_shw_return_t shw_slot_alloc(void *user_data, uint32_t size,
+ uint64_t owner_id, uint32_t *slot)
+{
+ scc_return_t retval;
+
+#ifdef DIAG_DRV_IF
+ LOG_DIAG_ARGS("key length: %i, handle: %i\n", size, *slot);
+
+#endif
+ retval = scc_alloc_slot(size, owner_id, slot);
+ if (retval == SCC_RET_OK)
+ return FSL_RETURN_OK_S;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t shw_slot_dealloc(void *user_data, uint64_t owner_id,
+ uint32_t slot)
+{
+ scc_return_t retval;
+ retval = scc_dealloc_slot(owner_id, slot);
+ if (retval == SCC_RET_OK)
+ return FSL_RETURN_OK_S;
+
+ return FSL_RETURN_ERROR_S;
+}
+void *shw_slot_get_address(void *user_data, uint32_t slot)
+{
+ uint64_t owner_id = *((uint64_t *) user_data);
+ uint32_t address;
+ uint32_t value_size_bytes;
+ uint32_t slot_size_bytes;
+ scc_return_t scc_ret;
+ scc_ret =
+ scc_get_slot_info(owner_id, slot, &address, &value_size_bytes,
+ &slot_size_bytes);
+ if (scc_ret == SCC_RET_OK) {
+ return (void *)address;
+ }
+ return NULL;
+}
+
+uint32_t shw_slot_get_base(void *user_data, uint32_t slot)
+{
+ return 0;
+}
+
+uint32_t shw_slot_get_offset(void *user_data, uint32_t slot)
+{
+ return 0;
+}
+
+
+/* Return the size of the key slot, in octets */
+uint32_t shw_slot_get_slot_size(void *user_data, uint32_t slot)
+{
+ uint64_t owner_id = *((uint64_t *) user_data);
+ uint32_t address;
+ uint32_t value_size_bytes;
+ uint32_t slot_size_bytes;
+ scc_return_t scc_ret;
+ scc_ret =
+ scc_get_slot_info(owner_id, slot, &address, &value_size_bytes,
+ &slot_size_bytes);
+ if (scc_ret == SCC_RET_OK)
+ return slot_size_bytes;
+ return 0;
+}
+
+
+#endif /* __KERNEL__ */
+
+#endif /* FSL_HAVE_SCC2 */
+
+/*****************************************************************************/
diff --git a/drivers/mxc/security/sahara2/fsl_shw_rand.c b/drivers/mxc/security/sahara2/fsl_shw_rand.c
new file mode 100644
index 000000000000..566c9e5c1e0f
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_rand.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_shw_rand.c
+ *
+ * This file implements Random Number Generation functions of the FSL SHW API
+ * for Sahara.
+ */
+
+#include "sahara.h"
+#include "sf_util.h"
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_get_random);
+#endif
+
+/* REQ-S2LRD-PINTFC-API-BASIC-RNG-002 */
+/*!
+ * Get a random number
+ *
+ *
+ * @param user_ctx
+ * @param length
+ * @param data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+ SAH_SF_DCLS;
+
+ /* perform a sanity check on the uco */
+ ret = sah_validate_uco(user_ctx);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ header = SAH_HDR_RNG_GENERATE; /* Desc. #18 */
+ DESC_OUT_OUT(header, length, data, 0, NULL);
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_add_entropy);
+#endif
+
+/*!
+ * Add entropy to a random number generator
+
+ * @param user_ctx
+ * @param length
+ * @param data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data)
+{
+ SAH_SF_DCLS;
+
+ /* perform a sanity check on the uco */
+ ret = sah_validate_uco(user_ctx);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ header = SAH_HDR_RNG_GENERATE; /* Desc. #18 */
+
+ /* create descriptor #18. Generate random data */
+ DESC_IN_IN(header, 0, NULL, length, data)
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
diff --git a/drivers/mxc/security/sahara2/fsl_shw_sym.c b/drivers/mxc/security/sahara2/fsl_shw_sym.c
new file mode 100644
index 000000000000..454905bbcf68
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_sym.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_shw_sym.c
+ *
+ * This file implements Symmetric Cipher functions of the FSL SHW API for
+ * Sahara. This does not include CCM.
+ */
+
+#include "sahara.h"
+#include "fsl_platform.h"
+
+#include "sf_util.h"
+#include "adaptor.h"
+
+#ifdef LINUX_KERNEL
+EXPORT_SYMBOL(fsl_shw_symmetric_encrypt);
+EXPORT_SYMBOL(fsl_shw_symmetric_decrypt);
+#endif
+
+#if defined(NEED_CTR_WORKAROUND)
+/* CTR mode needs block-multiple data in/out */
+#define LENGTH_PATCH (sym_ctx->block_size_bytes)
+#define LENGTH_PATCH_MASK (sym_ctx->block_size_bytes-1)
+#else
+#define LENGTH_PATCH 0
+#define LENGTH_PATCH_MASK 0 /* du not use! */
+#endif
+
+/*!
+ * Block of zeroes which is maximum Symmetric block size, used for
+ * initializing context register, etc.
+ */
+static uint32_t block_zeros[4] = {
+ 0, 0, 0, 0
+};
+
+typedef enum cipher_direction {
+ SYM_DECRYPT,
+ SYM_ENCRYPT
+} cipher_direction_t;
+
+/*!
+ * Create and run the chain for a symmetric-key operation.
+ *
+ * @param user_ctx Who the user is
+ * @param key_info What key is to be used
+ * @param sym_ctx Info details about algorithm
+ * @param encrypt 0 = decrypt, non-zero = encrypt
+ * @param length Number of octets at @a in and @a out
+ * @param in Pointer to input data
+ * @param out Location to store output data
+ *
+ * @return The status of handing chain to driver,
+ * or an earlier argument/flag or allocation
+ * error.
+ */
+static fsl_shw_return_t do_symmetric(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ cipher_direction_t encrypt,
+ uint32_t length,
+ const uint8_t * in, uint8_t * out)
+{
+ SAH_SF_DCLS;
+ uint8_t *sink = NULL;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+ sah_Oct_Str ptr1;
+ uint32_t size1 = sym_ctx->block_size_bytes;
+
+ SAH_SF_USER_CHECK();
+
+ /* Two different sets of chains, depending on algorithm */
+ if (key_info->algorithm == FSL_KEY_ALG_ARC4) {
+ if (sym_ctx->flags & FSL_SYM_CTX_INIT) {
+ /* Desc. #35 w/ARC4 - start from key */
+ header = SAH_HDR_ARC4_SET_MODE_KEY
+ ^ sah_insert_skha_algorithm_arc4;
+
+ DESC_IN_KEY(header, 0, NULL, key_info);
+ } else { /* load SBox */
+ /* Desc. #33 w/ARC4 and NO PERMUTE */
+ header = SAH_HDR_ARC4_SET_MODE_SBOX
+ ^ sah_insert_skha_no_permute
+ ^ sah_insert_skha_algorithm_arc4;
+ DESC_IN_IN(header, 256, sym_ctx->context,
+ 3, sym_ctx->context + 256);
+ } /* load SBox */
+
+ /* Add in-out data descriptor to process the data */
+ if (length != 0) {
+ DESC_IN_OUT(SAH_HDR_SKHA_ENC_DEC, length, in, length,
+ out);
+ }
+
+ /* Operation is done ... save what came out? */
+ if (sym_ctx->flags & FSL_SYM_CTX_SAVE) {
+ /* Desc. #34 - Read SBox, pointers */
+ header = SAH_HDR_ARC4_READ_SBOX;
+ DESC_OUT_OUT(header, 256, sym_ctx->context,
+ 3, sym_ctx->context + 256);
+ }
+ } else { /* not ARC4 */
+ /* Doing 1- or 2- descriptor chain. */
+ /* Desc. #1 and algorithm and mode */
+ header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_mode[sym_ctx->mode]
+ ^ sah_insert_skha_algorithm[key_info->algorithm];
+
+ /* Honor 'no key parity checking' for DES and TDES */
+ if ((key_info->flags & FSL_SKO_KEY_IGNORE_PARITY) &&
+ ((key_info->algorithm == FSL_KEY_ALG_DES) ||
+ (key_info->algorithm == FSL_KEY_ALG_TDES))) {
+ header ^= sah_insert_skha_no_key_parity;
+ }
+
+ /* Header by default is decrypting, so... */
+ if (encrypt == SYM_ENCRYPT) {
+ header ^= sah_insert_skha_encrypt;
+ }
+
+ if (sym_ctx->mode == FSL_SYM_MODE_CTR) {
+ header ^= sah_insert_skha_modulus[sym_ctx->modulus_exp];
+ }
+
+ if (sym_ctx->mode == FSL_SYM_MODE_ECB) {
+ ptr1 = NULL;
+ size1 = 0;
+ } else if (sym_ctx->flags & FSL_SYM_CTX_INIT) {
+ ptr1 = (uint8_t *) block_zeros;
+ } else {
+ ptr1 = sym_ctx->context;
+ }
+
+ DESC_IN_KEY(header, sym_ctx->block_size_bytes, ptr1, key_info);
+
+ /* Add in-out data descriptor */
+ if (length != 0) {
+ header = SAH_HDR_SKHA_ENC_DEC;
+ if (LENGTH_PATCH && (sym_ctx->mode == FSL_SYM_MODE_CTR)
+ && ((length & LENGTH_PATCH_MASK) != 0)) {
+ sink = DESC_TEMP_ALLOC(LENGTH_PATCH);
+ ret =
+ sah_Create_Link(user_ctx->mem_util, &link1,
+ (uint8_t *) in, length,
+ SAH_USES_LINK_DATA);
+ ret =
+ sah_Append_Link(user_ctx->mem_util, link1,
+ (uint8_t *) sink,
+ LENGTH_PATCH -
+ (length &
+ LENGTH_PATCH_MASK),
+ SAH_USES_LINK_DATA);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ ret =
+ sah_Create_Link(user_ctx->mem_util, &link2,
+ out, length,
+ SAH_USES_LINK_DATA |
+ SAH_OUTPUT_LINK);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ ret = sah_Append_Link(user_ctx->mem_util, link2,
+ sink,
+ LENGTH_PATCH -
+ (length &
+ LENGTH_PATCH_MASK),
+ SAH_USES_LINK_DATA);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ ret =
+ sah_Append_Desc(user_ctx->mem_util,
+ &desc_chain, header, link1,
+ link2);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ link1 = link2 = NULL;
+ } else {
+ DESC_IN_OUT(header, length, in, length, out);
+ }
+ }
+
+ /* Unload any desired context */
+ if (sym_ctx->flags & FSL_SYM_CTX_SAVE) {
+ DESC_OUT_OUT(SAH_HDR_SKHA_READ_CONTEXT_IV, 0, NULL,
+ sym_ctx->block_size_bytes,
+ sym_ctx->context);
+ }
+
+ } /* not ARC4 */
+
+ SAH_SF_EXECUTE();
+
+ out:
+ SAH_SF_DESC_CLEAN();
+ DESC_TEMP_FREE(sink);
+ if (LENGTH_PATCH) {
+ sah_Destroy_Link(user_ctx->mem_util, link1);
+ sah_Destroy_Link(user_ctx->mem_util, link2);
+ }
+
+ return ret;
+}
+
+/* REQ-S2LRD-PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+
+/*!
+ * Compute symmetric encryption
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param sym_ctx
+ * @param length
+ * @param pt
+ * @param ct
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt, uint8_t * ct)
+{
+ fsl_shw_return_t ret;
+
+ ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_ENCRYPT,
+ length, pt, ct);
+
+ return ret;
+}
+
+/* PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+
+/*!
+ * Compute symmetric decryption
+ *
+ *
+ * @param user_ctx
+ * @param key_info
+ * @param sym_ctx
+ * @param length
+ * @param pt
+ * @param ct
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct, uint8_t * pt)
+{
+ fsl_shw_return_t ret;
+
+ ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_DECRYPT,
+ length, ct, pt);
+
+ return ret;
+}
diff --git a/drivers/mxc/security/sahara2/fsl_shw_user.c b/drivers/mxc/security/sahara2/fsl_shw_user.c
new file mode 100644
index 000000000000..49ec97e7662a
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_user.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_shw_user.c
+ *
+ * This file implements user and platform capabilities functions of the FSL SHW
+ * API for Sahara
+ */
+#include "sahara.h"
+#include <adaptor.h>
+#include <sf_util.h>
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_get_capabilities);
+EXPORT_SYMBOL(fsl_shw_register_user);
+EXPORT_SYMBOL(fsl_shw_deregister_user);
+EXPORT_SYMBOL(fsl_shw_get_results);
+#endif /* __KERNEL__ */
+
+struct cap_t {
+ unsigned populated;
+ union {
+ uint32_t buffer[sizeof(fsl_shw_pco_t)];
+ fsl_shw_pco_t pco;
+ };
+};
+
+static struct cap_t cap = {
+ 0,
+ {}
+};
+
+/* REQ-S2LRD-PINTFC-API-GEN-003 */
+/*!
+ * Determine the hardware security capabilities of this platform.
+ *
+ * Though a user context object is passed into this function, it will always
+ * act in a non-blocking manner.
+ *
+ * @param user_ctx The user context which will be used for the query.
+ *
+ * @return A pointer to the capabilities object.
+ */
+fsl_shw_pco_t *fsl_shw_get_capabilities(fsl_shw_uco_t * user_ctx)
+{
+ fsl_shw_pco_t *retval = NULL;
+
+ if (cap.populated) {
+ retval = &cap.pco;
+ } else {
+ if (get_capabilities(user_ctx, &cap.pco) == FSL_RETURN_OK_S) {
+ cap.populated = 1;
+ retval = &cap.pco;
+ }
+ }
+ return retval;
+}
+
+/* REQ-S2LRD-PINTFC-API-GEN-004 */
+
+/*!
+ * Create an association between the the user and the provider of the API.
+ *
+ * @param user_ctx The user context which will be used for this association.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_register_user(fsl_shw_uco_t * user_ctx)
+{
+ return sah_register(user_ctx);
+}
+
+/* REQ-S2LRD-PINTFC-API-GEN-005 */
+
+/*!
+ * Destroy the association between the the user and the provider of the API.
+ *
+ * @param user_ctx The user context which is no longer needed.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_deregister_user(fsl_shw_uco_t * user_ctx)
+{
+ return sah_deregister(user_ctx);
+}
+
+/* REQ-S2LRD-PINTFC-API-GEN-006 */
+
+/*!
+ * Retrieve results from earlier operations.
+ *
+ * @param user_ctx The user's context.
+ * @param result_size The number of array elements of @a results.
+ * @param[in,out] results Pointer to first of the (array of) locations to
+ * store results.
+ * @param[out] result_count Pointer to store the number of results which
+ * were returned.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t * user_ctx,
+ unsigned result_size,
+ fsl_shw_result_t results[],
+ unsigned *result_count)
+{
+ fsl_shw_return_t status;
+
+ /* perform a sanity check on the uco */
+ status = sah_validate_uco(user_ctx);
+
+ /* if uco appears ok, build structure and pass to get results */
+ if (status == FSL_RETURN_OK_S) {
+ sah_results arg;
+
+ /* if requested is zero, it's done before it started */
+ if (result_size > 0) {
+ arg.requested = result_size;
+ arg.actual = result_count;
+ arg.results = results;
+ /* get the results */
+ status = sah_get_results(&arg, user_ctx);
+ }
+ }
+
+ return status;
+}
diff --git a/drivers/mxc/security/sahara2/fsl_shw_wrap.c b/drivers/mxc/security/sahara2/fsl_shw_wrap.c
new file mode 100644
index 000000000000..08fd7fa8dfee
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_wrap.c
@@ -0,0 +1,967 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_shw_wrap.c
+ *
+ * This file implements Key-Wrap (Black Key) functions of the FSL SHW API for
+ * Sahara.
+ *
+ * - Ownerid is an 8-byte, user-supplied, value to keep KEY confidential.
+ * - KEY is a 1-32 byte value which starts in SCC RED RAM before
+ * wrapping, and ends up there on unwrap. Length is limited because of
+ * size of SCC1 RAM.
+ * - KEY' is the encrypted KEY
+ * - LEN is a 1-byte (for now) byte-length of KEY
+ * - ALG is a 1-byte value for the algorithm which which the key is
+ * associated. Values are defined by the FSL SHW API
+ * - Ownerid, LEN, and ALG come from the user's "key_info" object, as does the
+ * slot number where KEY already is/will be.
+ * - T is a Nonce
+ * - T' is the encrypted T
+ * - KEK is a Key-Encryption Key for the user's Key
+ * - ICV is the "Integrity Check Value" for the wrapped key
+ * - Black Key is the string of bytes returned as the wrapped key
+<table>
+<tr><TD align="right">BLACK_KEY <TD width="3">=<TD>ICV | T' | LEN | ALG |
+ KEY'</td></tr>
+<tr><td>&nbsp;</td></tr>
+
+<tr><th>To Wrap</th></tr>
+<tr><TD align="right">T</td> <TD width="3">=</td> <TD>RND()<sub>16</sub>
+ </td></tr>
+<tr><TD align="right">KEK</td><TD width="3">=</td><TD>HASH<sub>sha256</sub>(T |
+ Ownerid)<sub>16</sub></td></tr>
+<tr><TD align="right">KEY'<TD width="3">=</td><TD>
+ AES<sub>ctr-enc</sub>(Key=KEK, CTR=0, Data=KEY)</td></tr>
+<tr><TD align="right">ICV</td><TD width="3">=</td><td>HMAC<sub>sha256</sub>
+ (Key=T, Data=Ownerid | LEN | ALG | KEY')<sub>16</sub></td></tr>
+<tr><TD align="right">T'</td><TD width="3">=</td><TD>TDES<sub>cbc-enc</sub>
+ (Key=SLID, IV=Ownerid, Data=T)</td></tr>
+
+<tr><td>&nbsp;</td></tr>
+
+<tr><th>To Unwrap</th></tr>
+<tr><TD align="right">T</td><TD width="3">=</td><TD>TDES<sub>ecb-dec</sub>
+ (Key=SLID, IV=Ownerid, Data=T')</sub></td></tr>
+<tr><TD align="right">ICV</td><TD width="3">=</td><td>HMAC<sub>sha256</sub>
+ (Key=T, Data=Ownerid | LEN | ALG | KEY')<sub>16</sub></td></tr>
+<tr><TD align="right">KEK</td><TD width="3">=</td><td>HASH<sub>sha256</sub>
+ (T | Ownerid)<sub>16</sub></td></tr>
+<tr><TD align="right">KEY<TD width="3">=</td><TD>AES<sub>ctr-dec</sub>
+ (Key=KEK, CTR=0, Data=KEY')</td></tr>
+</table>
+
+ */
+
+#include "sahara.h"
+#include "fsl_platform.h"
+#include "fsl_shw_keystore.h"
+
+#include "sf_util.h"
+#include "adaptor.h"
+
+#if defined(DIAG_SECURITY_FUNC)
+#include <diagnostic.h>
+#endif
+
+#if defined(NEED_CTR_WORKAROUND)
+/* CTR mode needs block-multiple data in/out */
+#define LENGTH_PATCH 16
+#define LENGTH_PATCH_MASK 0xF
+#else
+#define LENGTH_PATCH 4
+#define LENGTH_PATCH_MASK 3
+#endif
+
+#if LENGTH_PATCH
+#define ROUND_LENGTH(len) \
+({ \
+ uint32_t orig_len = len; \
+ uint32_t new_len; \
+ \
+ if ((orig_len & LENGTH_PATCH_MASK) != 0) { \
+ new_len = (orig_len + LENGTH_PATCH \
+ - (orig_len & LENGTH_PATCH_MASK)); \
+ } \
+ else { \
+ new_len = orig_len; \
+ } \
+ new_len; \
+})
+#else
+#define ROUND_LENGTH(len) (len)
+#endif
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(fsl_shw_establish_key);
+EXPORT_SYMBOL(fsl_shw_extract_key);
+EXPORT_SYMBOL(fsl_shw_release_key);
+EXPORT_SYMBOL(fsl_shw_read_key);
+#endif
+
+#define ICV_LENGTH 16
+#define T_LENGTH 16
+#define KEK_LENGTH 16
+#define LENGTH_LENGTH 1
+#define ALGORITHM_LENGTH 1
+#define FLAGS_LENGTH 1
+
+/* ICV | T' | LEN | ALG | KEY' */
+#define ICV_OFFSET 0
+#define T_PRIME_OFFSET (ICV_OFFSET + ICV_LENGTH)
+#define LENGTH_OFFSET (T_PRIME_OFFSET + T_LENGTH)
+#define ALGORITHM_OFFSET (LENGTH_OFFSET + LENGTH_LENGTH)
+#define FLAGS_OFFSET (ALGORITHM_OFFSET + ALGORITHM_LENGTH)
+#define KEY_PRIME_OFFSET (FLAGS_OFFSET + FLAGS_LENGTH)
+#define FLAGS_SW_KEY 0x01
+
+/*
+ * For testing of the algorithm implementation,, the DO_REPEATABLE_WRAP flag
+ * causes the T_block to go into the T field during a wrap operation. This
+ * will make the black key value repeatable (for a given SCC secret key, or
+ * always if the default key is in use).
+ *
+ * Normally, a random sequence is used.
+ */
+#ifdef DO_REPEATABLE_WRAP
+/*!
+ * Block of zeroes which is maximum Symmetric block size, used for
+ * initializing context register, etc.
+ */
+static uint8_t T_block_[16] = {
+ 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42,
+ 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42
+};
+#endif
+
+/*!
+ * Insert descriptors to calculate ICV = HMAC(key=T, data=LEN|ALG|KEY')
+ *
+ * @param user_ctx User's context for this operation
+ * @param desc_chain Descriptor chain to append to
+ * @param t_key_info T's key object
+ * @param black_key Beginning of Black Key region
+ * @param key_length Number of bytes of key' there are in @c black_key
+ * @param[out] hmac Location to store ICV. Will be tagged "USES" so
+ * sf routines will not try to free it.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t create_icv_calc(fsl_shw_uco_t * user_ctx,
+ sah_Head_Desc ** desc_chain,
+ fsl_shw_sko_t * t_key_info,
+ const uint8_t * black_key,
+ uint32_t key_length,
+ uint8_t * hmac)
+{
+ fsl_shw_return_t sah_code;
+ uint32_t header;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ /* Load up T as key for the HMAC */
+ header = (SAH_HDR_MDHA_SET_MODE_MD_KEY /* #6 */
+ ^ sah_insert_mdha_algorithm_sha256
+ ^ sah_insert_mdha_init ^ sah_insert_mdha_hmac ^
+ sah_insert_mdha_pdata ^ sah_insert_mdha_mac_full);
+ sah_code = sah_add_in_key_desc(header, NULL, 0, t_key_info, /* Reference T in RED */
+ user_ctx->mem_util, desc_chain);
+ if (sah_code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Previous step loaded key; Now set up to hash the data */
+ header = SAH_HDR_MDHA_HASH; /* #10 */
+
+ /* Input - start with ownerid */
+ sah_code = sah_Create_Link(user_ctx->mem_util, &link1,
+ (void *)&t_key_info->userid,
+ sizeof(t_key_info->userid),
+ SAH_USES_LINK_DATA);
+ if (sah_code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Still input - Append black-key fields len, alg, key' */
+ sah_code = sah_Append_Link(user_ctx->mem_util, link1,
+ (void *)black_key + LENGTH_OFFSET,
+ (LENGTH_LENGTH
+ + ALGORITHM_LENGTH
+ + key_length), SAH_USES_LINK_DATA);
+
+ if (sah_code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* Output - computed ICV/HMAC */
+ sah_code = sah_Create_Link(user_ctx->mem_util, &link2,
+ hmac, ICV_LENGTH,
+ SAH_USES_LINK_DATA | SAH_OUTPUT_LINK);
+ if (sah_code != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ sah_code = sah_Append_Desc(user_ctx->mem_util, desc_chain,
+ header, link1, link2);
+
+ out:
+ if (sah_code != FSL_RETURN_OK_S) {
+ (void)sah_Destroy_Link(user_ctx->mem_util, link1);
+ (void)sah_Destroy_Link(user_ctx->mem_util, link2);
+ }
+
+ return sah_code;
+} /* create_icv_calc */
+
+/*!
+ * Perform unwrapping of a black key into a RED slot
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be unwrapped... key length, slot info, etc.
+ * @param black_key Encrypted key
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t unwrap(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ const uint8_t * black_key)
+{
+ SAH_SF_DCLS;
+ uint8_t *hmac = NULL;
+ fsl_shw_sko_t t_key_info;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+ unsigned i;
+ unsigned rounded_key_length;
+ unsigned original_key_length = key_info->key_length;
+
+ hmac = DESC_TEMP_ALLOC(ICV_LENGTH);
+
+ /* Set up key_info for "T" - use same slot as eventual key */
+ fsl_shw_sko_init(&t_key_info, FSL_KEY_ALG_AES);
+ t_key_info.userid = key_info->userid;
+ t_key_info.handle = key_info->handle;
+ t_key_info.flags = key_info->flags;
+ t_key_info.key_length = T_LENGTH;
+ t_key_info.keystore = key_info->keystore;
+
+ /* Validate SW flags to prevent misuse */
+ if ((key_info->flags & FSL_SKO_KEY_SW_KEY)
+ && !(black_key[FLAGS_OFFSET] & FLAGS_SW_KEY)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ /* Compute T = SLID_decrypt(T'); leave in RED slot */
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_decrypt(user_ctx,
+ key_info->userid,
+ t_key_info.handle,
+ T_LENGTH,
+ black_key + T_PRIME_OFFSET);
+
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_decrypt(user_ctx,
+ key_info->keystore,
+ key_info->userid,
+ t_key_info.handle,
+ T_LENGTH,
+ black_key + T_PRIME_OFFSET);
+ }
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Compute ICV = HMAC(T, ownerid | len | alg | key' */
+ ret = create_icv_calc(user_ctx, &desc_chain, &t_key_info,
+ black_key, original_key_length, hmac);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creation of sah_Key_Link failed due to bad key"
+ " flag!\n");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Validating MAC of wrapped key");
+#endif
+ SAH_SF_EXECUTE();
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ SAH_SF_DESC_CLEAN();
+
+ /* Check computed ICV against value in Black Key */
+ for (i = 0; i < ICV_LENGTH; i++) {
+ if (black_key[ICV_OFFSET + i] != hmac[i]) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS("computed ICV fails at offset %i\n", i);
+
+ {
+ char buff[300];
+ int a;
+ for (a = 0; a < ICV_LENGTH; a++)
+ sprintf(&(buff[a * 2]), "%02x",
+ black_key[ICV_OFFSET + a]);
+ buff[a * 2 + 1] = 0;
+ LOG_DIAG_ARGS("black key: %s", buff);
+
+ for (a = 0; a < ICV_LENGTH; a++)
+ sprintf(&(buff[a * 2]), "%02x",
+ hmac[a]);
+ buff[a * 2 + 1] = 0;
+ LOG_DIAG_ARGS("hmac: %s", buff);
+ }
+#endif
+ ret = FSL_RETURN_AUTH_FAILED_S;
+ goto out;
+ }
+ }
+
+ /* This is no longer needed. */
+ DESC_TEMP_FREE(hmac);
+
+ /* Compute KEK = SHA256(T | ownerid). Rewrite slot with value */
+ header = (SAH_HDR_MDHA_SET_MODE_HASH /* #8 */
+ ^ sah_insert_mdha_init
+ ^ sah_insert_mdha_algorithm_sha256 ^ sah_insert_mdha_pdata);
+
+ /* Input - Start with T */
+ ret = sah_Create_Key_Link(user_ctx->mem_util, &link1, &t_key_info);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Still input - append ownerid */
+ ret = sah_Append_Link(user_ctx->mem_util, link1,
+ (void *)&key_info->userid,
+ sizeof(key_info->userid), SAH_USES_LINK_DATA);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Output - KEK goes into RED slot */
+ ret = sah_Create_Key_Link(user_ctx->mem_util, &link2, &t_key_info);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Put the Hash calculation into the chain. */
+ ret = sah_Append_Desc(user_ctx->mem_util, &desc_chain,
+ header, link1, link2);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Compute KEY = AES-decrypt(KEK, KEY') */
+ header = (SAH_HDR_SKHA_SET_MODE_IV_KEY /* #1 */
+ ^ sah_insert_skha_mode_ctr
+ ^ sah_insert_skha_algorithm_aes
+ ^ sah_insert_skha_modulus_128);
+ /* Load KEK in as the key to use */
+ DESC_IN_KEY(header, 0, NULL, &t_key_info);
+
+ rounded_key_length = ROUND_LENGTH(original_key_length);
+ key_info->key_length = rounded_key_length;
+
+ /* Now set up for computation. Result in RED */
+ header = SAH_HDR_SKHA_ENC_DEC; /* #4 */
+ DESC_IN_KEY(header, rounded_key_length, black_key + KEY_PRIME_OFFSET,
+ key_info);
+
+ /* Perform the operation */
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Decrypting key with KEK");
+#endif
+ SAH_SF_EXECUTE();
+
+ out:
+ key_info->key_length = original_key_length;
+ SAH_SF_DESC_CLEAN();
+
+ DESC_TEMP_FREE(hmac);
+
+ /* Erase tracks */
+ t_key_info.userid = 0xdeadbeef;
+ t_key_info.handle = 0xdeadbeef;
+
+ return ret;
+} /* unwrap */
+
+/*!
+ * Perform wrapping of a black key from a RED slot
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be wrapped... key length, slot info, etc.
+ * @param black_key Place to store encrypted key
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t wrap(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, uint8_t * black_key)
+{
+ SAH_SF_DCLS;
+ unsigned slots_allocated = 0; /* boolean */
+ fsl_shw_sko_t T_key_info; /* for holding T */
+ fsl_shw_sko_t KEK_key_info; /* for holding KEK */
+ unsigned original_key_length = key_info->key_length;
+ unsigned rounded_key_length;
+ sah_Link *link1;
+ sah_Link *link2;
+
+ black_key[LENGTH_OFFSET] = key_info->key_length;
+ black_key[ALGORITHM_OFFSET] = key_info->algorithm;
+
+ memcpy(&T_key_info, key_info, sizeof(T_key_info));
+ fsl_shw_sko_set_key_length(&T_key_info, T_LENGTH);
+ T_key_info.algorithm = FSL_KEY_ALG_HMAC;
+
+ memcpy(&KEK_key_info, &T_key_info, sizeof(KEK_key_info));
+ KEK_key_info.algorithm = FSL_KEY_ALG_AES;
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_alloc(user_ctx,
+ T_LENGTH, key_info->userid,
+ &T_key_info.handle);
+
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_alloc(key_info->keystore,
+ T_LENGTH,
+ key_info->userid, &T_key_info.handle);
+ }
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_alloc(user_ctx,
+ KEK_LENGTH, key_info->userid,
+ &KEK_key_info.handle);
+
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_alloc(key_info->keystore,
+ KEK_LENGTH, key_info->userid,
+ &KEK_key_info.handle);
+ }
+
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("do_scc_slot_alloc() failed");
+#endif
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ (void)do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid, T_key_info.handle);
+
+ } else {
+ /* Key goes in user keystore */
+ (void)keystore_slot_dealloc(key_info->keystore,
+ key_info->userid, T_key_info.handle);
+ }
+ } else {
+ slots_allocated = 1;
+ }
+
+ /* Set up to compute everything except T' ... */
+#ifndef DO_REPEATABLE_WRAP
+ /* Compute T = RND() */
+ header = SAH_HDR_RNG_GENERATE; /* Desc. #18 */
+ DESC_KEY_OUT(header, &T_key_info, 0, NULL);
+#else
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_load(user_ctx,
+ T_key_info.userid,
+ T_key_info.handle, T_block,
+ T_key_info.key_length);
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_load(key_info->keystore,
+ T_key_info.userid,
+ T_key_info.handle,
+ T_block, T_key_info.key_length);
+ }
+
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#endif
+
+ /* Compute KEK = SHA256(T | Ownerid) */
+ header = (SAH_HDR_MDHA_SET_MODE_HASH /* #8 */
+ ^ sah_insert_mdha_init
+ ^ sah_insert_mdha_algorithm[FSL_HASH_ALG_SHA256]
+ ^ sah_insert_mdha_pdata);
+ /* Input - Start with T */
+ ret = sah_Create_Key_Link(user_ctx->mem_util, &link1, &T_key_info);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* Still input - append ownerid */
+ ret = sah_Append_Link(user_ctx->mem_util, link1,
+ (void *)&key_info->userid,
+ sizeof(key_info->userid), SAH_USES_LINK_DATA);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* Output - KEK goes into RED slot */
+ ret = sah_Create_Key_Link(user_ctx->mem_util, &link2, &KEK_key_info);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ /* Put the Hash calculation into the chain. */
+ ret = sah_Append_Desc(user_ctx->mem_util, &desc_chain,
+ header, link1, link2);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#if defined(NEED_CTR_WORKAROUND)
+ rounded_key_length = ROUND_LENGTH(original_key_length);
+ key_info->key_length = rounded_key_length;
+#else
+ rounded_key_length = original_key_length;
+#endif
+ /* Compute KEY' = AES-encrypt(KEK, KEY) */
+ header = (SAH_HDR_SKHA_SET_MODE_IV_KEY /* #1 */
+ ^ sah_insert_skha_mode[FSL_SYM_MODE_CTR]
+ ^ sah_insert_skha_algorithm[FSL_KEY_ALG_AES]
+ ^ sah_insert_skha_modulus[FSL_CTR_MOD_128]);
+ /* Set up KEK as key to use */
+ DESC_IN_KEY(header, 0, NULL, &KEK_key_info);
+ header = SAH_HDR_SKHA_ENC_DEC;
+ DESC_KEY_OUT(header, key_info,
+ key_info->key_length, black_key + KEY_PRIME_OFFSET);
+
+ /* Set up flags info */
+ black_key[FLAGS_OFFSET] = 0;
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ black_key[FLAGS_OFFSET] |= FLAGS_SW_KEY;
+ }
+
+ /* Compute and store ICV into Black Key */
+ ret = create_icv_calc(user_ctx, &desc_chain, &T_key_info,
+ black_key, original_key_length,
+ black_key + ICV_OFFSET);
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creation of sah_Key_Link failed due to bad key"
+ " flag!\n");
+#endif /*DIAG_SECURITY_FUNC */
+ goto out;
+ }
+
+ /* Now get Sahara to do the work. */
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Encrypting key with KEK");
+#endif
+ SAH_SF_EXECUTE();
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("sah_Descriptor_Chain_Execute() failed");
+#endif
+ goto out;
+ }
+
+ /* Compute T' = SLID_encrypt(T); Result goes to Black Key */
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_encrypt(user_ctx,
+ T_key_info.userid, T_key_info.handle,
+ T_LENGTH, black_key + T_PRIME_OFFSET);
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_encrypt(user_ctx,
+ key_info->keystore,
+ T_key_info.userid,
+ T_key_info.handle,
+ T_LENGTH,
+ black_key + T_PRIME_OFFSET);
+ }
+
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("do_scc_slot_encrypt() failed");
+#endif
+ goto out;
+ }
+
+ out:
+ key_info->key_length = original_key_length;
+
+ SAH_SF_DESC_CLEAN();
+ if (slots_allocated) {
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ (void)do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid,
+ T_key_info.
+ handle);
+ (void)do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid,
+ KEK_key_info.
+ handle);
+ } else {
+ /* Key goes in user keystore */
+ (void)keystore_slot_dealloc(key_info->keystore,
+ key_info->userid,
+ T_key_info.handle);
+ (void)keystore_slot_dealloc(key_info->keystore,
+ key_info->userid,
+ KEK_key_info.handle);
+ }
+ }
+
+ return ret;
+} /* wrap */
+
+/*!
+ * Place a key into a protected location for use only by cryptographic
+ * algorithms.
+ *
+ * This only needs to be used to a) unwrap a key, or b) set up a key which
+ * could be wrapped with a later call to #fsl_shw_extract_key(). Normal
+ * cleartext keys can simply be placed into #fsl_shw_sko_t key objects with
+ * #fsl_shw_sko_set_key() and used directly.
+ *
+ * The maximum key size supported for wrapped/unwrapped keys is 32 octets.
+ * (This is the maximum reasonable key length on Sahara - 32 octets for an HMAC
+ * key based on SHA-256.) The key size is determined by the @a key_info. The
+ * expected length of @a key can be determined by
+ * #fsl_shw_sko_calculate_wrapped_size()
+ *
+ * The protected key will not be available for use until this operation
+ * successfully completes.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be established. In the create case, the key
+ * length must be set.
+ * @param establish_type How @a key will be interpreted to establish a
+ * key for use.
+ * @param key If @a establish_type is #FSL_KEY_WRAP_UNWRAP,
+ * this is the location of a wrapped key. If
+ * @a establish_type is #FSL_KEY_WRAP_CREATE, this
+ * parameter can be @a NULL. If @a establish_type
+ * is #FSL_KEY_WRAP_ACCEPT, this is the location
+ * of a plaintext key.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key)
+{
+ SAH_SF_DCLS;
+ unsigned original_key_length = key_info->key_length;
+ unsigned rounded_key_length;
+ unsigned slot_allocated = 0;
+ uint32_t old_flags;
+
+ header = SAH_HDR_RNG_GENERATE; /* Desc. #18 for rand */
+
+ /* TODO: THIS STILL NEEDS TO BE REFACTORED */
+
+ /* Write operations into SCC memory require word-multiple number of
+ * bytes. For ACCEPT and CREATE functions, the key length may need
+ * to be rounded up. Calculate. */
+ if (LENGTH_PATCH && (original_key_length & LENGTH_PATCH_MASK) != 0) {
+ rounded_key_length = original_key_length + LENGTH_PATCH
+ - (original_key_length & LENGTH_PATCH_MASK);
+ } else {
+ rounded_key_length = original_key_length;
+ }
+
+ SAH_SF_USER_CHECK();
+
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+#ifdef DIAG_SECURITY_FUNC
+ ret = FSL_RETURN_BAD_FLAG_S;
+ LOG_DIAG("Key already established\n");
+#endif
+ }
+
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_alloc(user_ctx,
+ key_info->key_length,
+ key_info->userid,
+ &(key_info->handle));
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG_ARGS
+ ("key length: %i, handle: %i, rounded key length: %i",
+ key_info->key_length, key_info->handle,
+ rounded_key_length);
+#endif
+
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_alloc(key_info->keystore,
+ key_info->key_length,
+ key_info->userid,
+ &(key_info->handle));
+ }
+ if (ret != FSL_RETURN_OK_S) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Slot allocation failed\n");
+#endif
+ goto out;
+ }
+ slot_allocated = 1;
+
+ key_info->flags |= FSL_SKO_KEY_ESTABLISHED;
+ switch (establish_type) {
+ case FSL_KEY_WRAP_CREATE:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creating random key\n");
+#endif
+ /* Use safe version of key length */
+ key_info->key_length = rounded_key_length;
+ /* Generate descriptor to put random value into */
+ DESC_KEY_OUT(header, key_info, 0, NULL);
+ /* Restore actual, desired key length */
+ key_info->key_length = original_key_length;
+
+ old_flags = user_ctx->flags;
+ /* Now put random value into key */
+ SAH_SF_EXECUTE();
+ /* Restore user's old flag value */
+ user_ctx->flags = old_flags;
+#ifdef DIAG_SECURITY_FUNC
+ if (ret == FSL_RETURN_OK_S) {
+ LOG_DIAG("ret is ok");
+ } else {
+ LOG_DIAG("ret is not ok");
+ }
+#endif
+ break;
+
+ case FSL_KEY_WRAP_ACCEPT:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Accepting plaintext key\n");
+#endif
+ if (key == NULL) {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("ACCEPT: Red Key is NULL");
+#endif
+ ret = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+ /* Copy in safe number of bytes of Red key */
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_load(user_ctx,
+ key_info->userid,
+ key_info->handle, key,
+ rounded_key_length);
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_load(key_info->keystore,
+ key_info->userid,
+ key_info->handle, key,
+ key_info->key_length);
+ }
+ break;
+
+ case FSL_KEY_WRAP_UNWRAP:
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Unwrapping wrapped key\n");
+#endif
+ /* For now, disallow non-blocking calls. */
+ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ } else if (key == NULL) {
+ ret = FSL_RETURN_ERROR_S;
+ } else {
+ ret = unwrap(user_ctx, key_info, key);
+ }
+ break;
+
+ default:
+ ret = FSL_RETURN_BAD_FLAG_S;
+ break;
+ } /* switch */
+
+ out:
+ if (slot_allocated && (ret != FSL_RETURN_OK_S)) {
+ fsl_shw_return_t scc_err;
+
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ scc_err = do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid,
+ key_info->handle);
+ } else {
+ /* Key goes in user keystore */
+ scc_err = keystore_slot_dealloc(key_info->keystore,
+ key_info->userid, key_info->handle);
+ }
+
+ key_info->flags &= ~FSL_SKO_KEY_ESTABLISHED;
+ }
+
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+} /* fsl_shw_establish_key() */
+
+/*!
+ * Wrap a key and retrieve the wrapped value.
+ *
+ * A wrapped key is a key that has been cryptographically obscured. It is
+ * only able to be used with #fsl_shw_establish_key().
+ *
+ * This function will also release the key (see #fsl_shw_release_key()) so
+ * that it must be re-established before reuse.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ * @param[out] covered_key The location to store the 48-octet wrapped key.
+ * (This size is based upon the maximum key size
+ * of 32 octets).
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ /* For now, only blocking mode calls are supported */
+ if (user_ctx->flags & FSL_UCO_BLOCKING_MODE) {
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+ ret = wrap(user_ctx, key_info, covered_key);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Verify that a SW key info really belongs to a SW key */
+ if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
+ /* ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;*/
+ }
+
+ /* Need to deallocate on successful extraction */
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ ret = do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid, key_info->handle);
+ } else {
+ /* Key goes in user keystore */
+ ret = keystore_slot_dealloc(key_info->keystore,
+ key_info->userid, key_info->handle);
+ }
+ /* Mark key not available in the flags */
+ key_info->flags &=
+ ~(FSL_SKO_KEY_ESTABLISHED | FSL_SKO_KEY_PRESENT);
+ }
+ }
+
+out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
+
+/*!
+ * De-establish a key so that it can no longer be accessed.
+ *
+ * The key will need to be re-established before it can again be used.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+ if (key_info->keystore == NULL) {
+ /* Key goes in system keystore */
+ do_system_keystore_slot_dealloc(user_ctx,
+ key_info->userid,
+ key_info->handle);
+ } else {
+ /* Key goes in user keystore */
+ keystore_slot_dealloc(key_info->keystore,
+ key_info->userid,
+ key_info->handle);
+ }
+ key_info->flags &= ~(FSL_SKO_KEY_ESTABLISHED |
+ FSL_SKO_KEY_PRESENT);
+ }
+
+out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
+
+fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info, uint8_t * key)
+{
+ SAH_SF_DCLS;
+
+ SAH_SF_USER_CHECK();
+
+ if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)
+ || !(key_info->flags & FSL_SKO_KEY_SW_KEY)) {
+ ret = FSL_RETURN_BAD_FLAG_S;
+ goto out;
+ }
+
+ if (key_info->keystore == NULL) {
+ /* Key lives in system keystore */
+ ret = do_system_keystore_slot_read(user_ctx,
+ key_info->userid,
+ key_info->handle,
+ key_info->key_length, key);
+ } else {
+ /* Key lives in user keystore */
+ ret = keystore_slot_read(key_info->keystore,
+ key_info->userid,
+ key_info->handle,
+ key_info->key_length, key);
+ }
+
+ out:
+ SAH_SF_DESC_CLEAN();
+
+ return ret;
+}
diff --git a/drivers/mxc/security/sahara2/include/adaptor.h b/drivers/mxc/security/sahara2/include/adaptor.h
new file mode 100644
index 000000000000..d2cb35b21e4c
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/adaptor.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+* @file adaptor.h
+*
+* @brief The Adaptor component provides an interface to the device
+* driver.
+*
+* Intended to be used by the FSL SHW API, this can also be called directly
+*/
+
+#ifndef ADAPTOR_H
+#define ADAPTOR_H
+
+#include <sahara.h>
+
+/*!
+ * Structure passed during user ioctl() call to submit request.
+ */
+typedef struct sah_dar {
+ sah_Desc *desc_addr; /*!< head of descriptor chain */
+ uint32_t uco_flags; /*!< copy of fsl_shw_uco flags field */
+ uint32_t uco_user_ref; /*!< copy of fsl_shw_uco user_ref */
+ uint32_t result; /*!< result of descriptor chain request */
+ struct sah_dar *next; /*!< for driver use */
+} sah_dar_t;
+
+/*!
+ * Structure passed during user ioctl() call to Register a user
+ */
+typedef struct sah_register {
+ uint32_t pool_size; /*!< max number of outstanding requests possible */
+ uint32_t result; /*!< result of registration request */
+} sah_register_t;
+
+/*!
+ * Structure passed during ioctl() call to request SCC operation
+ */
+typedef struct scc_data {
+ uint32_t length; /*!< length of data */
+ uint8_t *in; /*!< input data */
+ uint8_t *out; /*!< output data */
+ unsigned direction; /*!< encrypt or decrypt */
+ fsl_shw_sym_mode_t crypto_mode; /*!< CBC or EBC */
+ uint8_t *init_vector; /*!< initialization vector or NULL */
+} scc_data_t;
+
+/*!
+ * Structure passed during user ioctl() calls to manage stored keys and
+ * stored-key slots.
+ */
+typedef struct scc_slot_t {
+ uint64_t ownerid; /*!< Owner's id to check/set permissions */
+ uint32_t key_length; /*!< Length of key */
+ uint32_t slot; /*!< Slot to operation on, or returned slot
+ number. */
+ uint8_t *key; /*!< User-memory pointer to key value */
+ fsl_shw_return_t code; /*!< API return code from operation */
+} scc_slot_t;
+
+/*
+ * Structure passed during user ioctl() calls to manage data stored in secure
+ * partitions.
+ */
+typedef struct scc_region_t {
+ uint32_t partition_base; /*!< User virtual address of the
+ partition base. */
+ uint32_t offset; /*!< Offset from the start of the
+ partition where the cleartext data
+ is located. */
+ uint32_t length; /*!< Length of the region to be
+ operated on */
+ uint8_t *black_data; /*!< User virtual address of any black
+ (encrypted) data. */
+ fsl_shw_cypher_mode_t cypher_mode; /*!< Cypher mode to use in an encryt/
+ decrypt operation. */
+ uint32_t IV[4]; /*!< Intialization vector to use in an
+ encrypt/decrypt operation. */
+ fsl_shw_return_t code; /*!< API return code from operation */
+} scc_region_t;
+
+/*
+ * Structure passed during user ioctl() calls to manage secure partitions.
+ */
+typedef struct scc_partition_info_t {
+ uint32_t user_base; /**< Userspace pointer to base of partition */
+ uint32_t permissions; /**< Permissions to give the partition (only
+ used in call to _DROP_PERMS) */
+ fsl_shw_partition_status_t status; /*!< Status of the partition */
+} scc_partition_info_t;
+
+fsl_shw_return_t adaptor_Exec_Descriptor_Chain(sah_Head_Desc * dar,
+ fsl_shw_uco_t * uco);
+fsl_shw_return_t sah_get_results(sah_results * arg, fsl_shw_uco_t * uco);
+fsl_shw_return_t sah_register(fsl_shw_uco_t * user_ctx);
+fsl_shw_return_t sah_deregister(fsl_shw_uco_t * user_ctx);
+fsl_shw_return_t get_capabilities(fsl_shw_uco_t * user_ctx,
+ fsl_shw_pco_t *capabilities);
+
+#endif /* ADAPTOR_H */
+
+/* End of adaptor.h */
diff --git a/drivers/mxc/security/sahara2/include/diagnostic.h b/drivers/mxc/security/sahara2/include/diagnostic.h
new file mode 100644
index 000000000000..57f84d4cbb05
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/diagnostic.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+* @file diagnostic.h
+*
+* @brief Macros for outputting kernel and user space diagnostics.
+*/
+
+#ifndef DIAGNOSTIC_H
+#define DIAGNOSTIC_H
+
+#ifndef __KERNEL__ /* linux flag */
+#include <stdio.h>
+#endif
+#include "fsl_platform.h"
+
+#if defined(FSL_HAVE_SAHARA2) || defined(FSL_HAVE_SAHARA4)
+#define DEV_NAME "sahara"
+#elif defined(FSL_HAVE_RNGA) || defined(FSL_HAVE_RNGB) || \
+ defined(FSL_HAVE_RNGC)
+#define DEV_NAME "shw"
+#endif
+
+/*!
+********************************************************************
+* @brief This macro logs diagnostic messages to stderr.
+*
+* @param diag String that must be logged, char *.
+*
+* @return void
+*
+*/
+//#if defined DIAG_SECURITY_FUNC || defined DIAG_ADAPTOR
+#define LOG_DIAG(diag) \
+({ \
+ const char* fname = strrchr(__FILE__, '/'); \
+ \
+ sah_Log_Diag(fname ? fname+1 : __FILE__, __LINE__, diag); \
+})
+
+#ifdef __KERNEL__
+
+#define LOG_DIAG_ARGS(fmt, ...) \
+({ \
+ const char* fname = strrchr(__FILE__, '/'); \
+ os_printk(KERN_ALERT "%s:%i: " fmt "\n", \
+ fname ? fname+1 : __FILE__, \
+ __LINE__, \
+ __VA_ARGS__); \
+})
+
+#else
+
+#define LOG_DIAG_ARGS(fmt, ...) \
+({ \
+ const char* fname = strrchr(__FILE__, '/'); \
+ printf("%s:%i: " fmt "\n", \
+ fname ? fname+1 : __FILE__, \
+ __LINE__, \
+ __VA_ARGS__); \
+})
+
+#ifndef __KERNEL__
+void sah_Log_Diag(char *source_name, int source_line, char *diag);
+#endif
+#endif /* if define DIAG_SECURITY_FUNC ... */
+
+#ifdef __KERNEL__
+/*!
+********************************************************************
+* @brief This macro logs kernel diagnostic messages to the kernel
+* log.
+*
+* @param diag String that must be logged, char *.
+*
+* @return As for printf()
+*/
+#if 0
+#if defined(DIAG_DRV_IF) || defined(DIAG_DRV_QUEUE) || \
+ defined(DIAG_DRV_STATUS) || defined(DIAG_DRV_INTERRUPT) || \
+ defined(DIAG_MEM) || defined(DIAG_SECURITY_FUNC) || defined(DIAG_ADAPTOR)
+#endif
+#endif
+
+#define LOG_KDIAG_ARGS(fmt, ...) \
+({ \
+ os_printk (KERN_ALERT "%s (%s:%i): " fmt "\n", \
+ DEV_NAME, strrchr(__FILE__, '/')+1, __LINE__, __VA_ARGS__); \
+})
+
+#define LOG_KDIAG(diag) \
+ os_printk (KERN_ALERT "%s (%s:%i): %s\n", \
+ DEV_NAME, strrchr(__FILE__, '/')+1, __LINE__, diag);
+
+#define sah_Log_Diag(n, l, d) \
+ os_printk(KERN_ALERT "%s:%i: %s\n", n, l, d)
+
+#else /* not KERNEL */
+
+#define sah_Log_Diag(n, l, d) \
+ printf("%s:%i: %s\n", n, l, d)
+
+#endif /* __KERNEL__ */
+
+#endif /* DIAGNOSTIC_H */
diff --git a/drivers/mxc/security/sahara2/include/fsl_platform.h b/drivers/mxc/security/sahara2/include/fsl_platform.h
new file mode 100644
index 000000000000..e7a6fd1718b2
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/fsl_platform.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file fsl_platform.h
+ *
+ * Header file to isolate code which might be platform-dependent
+ */
+
+#ifndef FSL_PLATFORM_H
+#define FSL_PLATFORM_H
+
+#ifdef __KERNEL__
+#include "portable_os.h"
+#endif
+
+#if defined(FSL_PLATFORM_OTHER)
+
+/* Have Makefile or other method of setting FSL_HAVE_* flags */
+
+#elif defined(CONFIG_ARCH_MX3) /* i.MX31 */
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RTIC
+#define FSL_HAVE_RNGA
+
+#elif defined(CONFIG_ARCH_MX21)
+
+#define FSL_HAVE_HAC
+#define FSL_HAVE_RNGA
+#define FSL_HAVE_SCC
+
+#elif defined(CONFIG_ARCH_MX25)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGB
+#define FSL_HAVE_RTIC3
+#define FSL_HAVE_DRYICE
+
+#elif defined(CONFIG_ARCH_MX27)
+
+#define FSL_HAVE_SAHARA2
+#define SUBMIT_MULTIPLE_DARS
+#define FSL_HAVE_RTIC
+#define FSL_HAVE_SCC
+#define ALLOW_LLO_DESCRIPTORS
+
+#elif defined(CONFIG_ARCH_MX35)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGC
+#define FSL_HAVE_RTIC
+
+#elif defined(CONFIG_ARCH_MX37)
+
+#define FSL_HAVE_SCC2
+#define FSL_HAVE_RNGC
+#define FSL_HAVE_RTIC2
+#define FSL_HAVE_SRTC
+
+#elif defined(CONFIG_ARCH_MX51)
+
+#define FSL_HAVE_SCC2
+#define FSL_HAVE_SAHARA4
+#define FSL_HAVE_RTIC3
+#define FSL_HAVE_SRTC
+#define NO_RESEED_WORKAROUND
+#define NEED_CTR_WORKAROUND
+#define USE_S2_CCM_ENCRYPT_CHAIN
+#define USE_S2_CCM_DECRYPT_CHAIN
+#define ALLOW_LLO_DESCRIPTORS
+
+#elif defined(CONFIG_ARCH_MXC91131)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGC
+#define FSL_HAVE_HAC
+
+#elif defined(CONFIG_ARCH_MXC91221)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGC
+#define FSL_HAVE_RTIC2
+
+#elif defined(CONFIG_ARCH_MXC91231)
+
+#define FSL_HAVE_SAHARA2
+#define FSL_HAVE_RTIC
+#define FSL_HAVE_SCC
+#define NO_OUTPUT_1K_CROSSING
+
+#elif defined(CONFIG_ARCH_MXC91311)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGC
+
+#elif defined(CONFIG_ARCH_MXC91314)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_SAHAR4
+#define FSL_HAVE_RTIC3
+#define NO_RESEED_WORKAROUND
+#define NEED_CTR_WORKAROUND
+#define USE_S2_CCM_ENCRYPT_CHAIN
+#define USE_S2_CCM_DECRYPT_CHAIN
+#define ALLOW_LLO_DESCRIPTORS
+
+#elif defined(CONFIG_ARCH_MXC91321)
+
+#define FSL_HAVE_SAHARA2
+#define FSL_HAVE_RTIC
+#define FSL_HAVE_SCC
+#define SCC_CLOCK_NOT_GATED
+#define NO_OUTPUT_1K_CROSSING
+
+#elif defined(CONFIG_ARCH_MXC92323)
+
+#define FSL_HAVE_SCC2
+#define FSL_HAVE_SAHARA4
+#define FSL_HAVE_PKHA
+#define FSL_HAVE_RTIC2
+#define NO_1K_CROSSING
+#define NO_RESEED_WORKAROUND
+#define NEED_CTR_WORKAROUND
+#define USE_S2_CCM_ENCRYPT_CHAIN
+#define USE_S2_CCM_DECRYPT_CHAIN
+#define ALLOW_LLO_DESCRIPTORS
+
+
+#elif defined(CONFIG_ARCH_MXC91331)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGA
+#define FSL_HAVE_HAC
+#define FSL_HAVE_RTIC
+
+#elif defined(CONFIG_8548)
+
+#define FSL_HAVE_SEC2x
+
+#elif defined(CONFIG_MPC8374)
+
+#define FSL_HAVE_SEC3x
+
+#else
+
+#error UNKNOWN_PLATFORM
+
+#endif /* platform checks */
+
+#endif /* FSL_PLATFORM_H */
diff --git a/drivers/mxc/security/sahara2/include/fsl_shw.h b/drivers/mxc/security/sahara2/include/fsl_shw.h
new file mode 100644
index 000000000000..1a2a7639b92a
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/fsl_shw.h
@@ -0,0 +1,2515 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * NOTE TO MAINTAINERS: Although this header file is *the* header file to be
+ * #include'd by FSL SHW programs, it does not itself make any definitions for
+ * the API. Instead, it uses the fsl_platform.h file and / or compiler
+ * environment variables to determine which actual driver header file to
+ * include. This allows different implementations to contain different
+ * implementations of the various objects, macros, etc., or even to change
+ * which functions are macros and which are not.
+ */
+
+/*!
+ * @file fsl_shw.h
+ *
+ * @brief Definition of the Freescale Security Hardware API.
+ *
+ * See @ref index for an overview of the API.
+ */
+
+/*!
+ * @if USE_MAINPAGE
+ * @mainpage Common API for Freescale Security Hardware (FSL SHW API)
+ * @endif
+ *
+ * @section intro_sec Introduction
+ *
+ * This is the interface definition for the Freescale Security Hardware API
+ * (FSL SHW API) for User Mode and Kernel Mode to access Freescale Security
+ * Hardware components for cryptographic acceleration. The API is intended to
+ * provide cross-platform access to security hardware components of Freescale.
+ *
+ * This documentation has not been approved, and should not be taken to
+ * mean anything definite about future direction.
+ *
+ * Some example code is provided to give some idea of usage of this API.
+ *
+ * Note: This first version has been defined around the capabilities of the
+ * Sahara2 cryptographic accelerator, and may be expanded in the future to
+ * provide support for other platforms. The Platform Capabilities Object is
+ * intended as a way to allow programs to adapt to different platforms.
+ *
+ * The i.MX25 is an example of a platform without a SAHARA but yet has
+ * capabilities supported by this API. These include #fsl_shw_get_random() and
+ * #fsl_shw_add_entropy(), and the use of Triple-DES (TDEA) cipher algorithm
+ * (with no checking of key parity supported) in ECB and CBC modes with @ref
+ * sym_sec. See also the @ref di_sec for information on key handling, and @ref
+ * td_sec for detection of Tamper Events. Only the random functions are
+ * available from user space on this platform.
+ *
+ * @section usr_ctx The User Context
+ *
+ * The User Context Object (#fsl_shw_uco_t) controls the interaction between
+ * the user program and the API. It is initialized as part of user
+ * registration (#fsl_shw_register_user()), and is part of every interaction
+ * thereafter.
+ *
+ * @section pf_sec Platform Capabilities
+ *
+ * Since this API is not tied to one specific type of hardware or even one
+ * given version of a given type of hardware, the platform capabilities object
+ * could be used by a portable program to make choices about using software
+ * instead of hardware for certain operations.
+ *
+ * See the #fsl_shw_pco_t, returned by #fsl_shw_get_capabilities().
+ *
+ * @ref pcoops are provided to query its contents.
+ *
+ *
+ * @section sym_sec Symmetric-Key Encryption and Decryption
+ *
+ * Symmetric-Key encryption support is provided for the block cipher algorithms
+ * AES, DES, and Triple DES. Modes supported are #FSL_SYM_MODE_ECB,
+ * #FSL_SYM_MODE_CBC, and #FSL_SYM_MODE_CTR, though not necessarily all modes
+ * for all algorithms. There is also support for the stream cipher algorithm
+ * commonly known as ARC4.
+ *
+ * Encryption and decryption are performed by using the functions
+ * #fsl_shw_symmetric_encrypt() and #fsl_shw_symmetric_decrypt(), respectively.
+ * There are two objects which provide information about the operation of these
+ * functions. They are the #fsl_shw_sko_t, to provide key and algorithm
+ * information; and the #fsl_shw_scco_t, to provide (and store) initial context
+ * or counter value information.
+ *
+ * CCM is not supported by these functions. For information CCM support, see
+ * @ref cmb_sec.
+ *
+ *
+ * @section hash_sec Cryptographic Hashing
+ *
+ * Hashing is performed by fsl_shw_hash(). Control of the function is through
+ * flags in the #fsl_shw_hco_t. The algorithms which are
+ * supported are listed in #fsl_shw_hash_alg_t.
+ *
+ * The hashing function works on octet streams. If a user application needs to
+ * hash a bitstream, it will need to do its own padding of the last block.
+ *
+ *
+ * @section hmac_sec Hashed Message Authentication Codes
+ *
+ * An HMAC is a method of combining a hash and a key so that a message cannot
+ * be faked by a third party.
+ *
+ * The #fsl_shw_hmac() can be used by itself for one-shot or multi-step
+ * operations, or in combination with #fsl_shw_hmac_precompute() to provide the
+ * ability to compute and save the beginning hashes from a key one time, and
+ * then use #fsl_shw_hmac() to calculate an HMAC on each message as it is
+ * processed.
+ *
+ * The maximum key length which is directly supported by this API is 64 octets.
+ * If a longer key size is needed for HMAC, the user will have to hash the key
+ * and present the digest value as the key to be used by the HMAC functions.
+ *
+ *
+ * @section rnd_sec Random Numbers
+ *
+ * Support is available for acquiring random values from a
+ * cryptographically-strong random number generator. See
+ * #fsl_shw_get_random(). The function #fsl_shw_add_entropy() may be used to
+ * add entropy to the random number generator.
+ *
+ *
+ * @section cmb_sec Combined Cipher and Authentication
+ *
+ * Some schemes require that messages be encrypted and that they also have an
+ * authentication code associated with the message. The function
+ * #fsl_shw_gen_encrypt() will generate the authentication code and encrypt the
+ * message.
+ *
+ * Upon receipt of such a message, the message must be decrypted and the
+ * authentication code validated. The function
+ * #fsl_shw_auth_decrypt() will perform these steps.
+ *
+ * Only AES-CCM is supported.
+ *
+ *
+ * @section wrap_sec Wrapped Keys
+ *
+ * On platforms with a Secure Memory, the function #fsl_shw_establish_key() can
+ * be used to place a key into the System Keystore. This key then can be used
+ * directly by the cryptographic hardware. It later then be wrapped
+ * (cryptographically obscured) by #fsl_shw_extract_key() and stored for later
+ * use. If a software key (#FSL_SKO_KEY_SW_KEY) was established, then its
+ * value can be retrieved with a call to #fsl_shw_read_key().
+ *
+ * The wrapping and unwrapping functions provide security against unauthorized
+ * use and detection of tampering.
+ *
+ * The functions can also be used with a User Keystore.
+ *
+ * @section smalloc_sec Secure Memory Allocation
+ *
+ * On platforms with multiple partitions of Secure Memory, the function
+ * #fsl_shw_smalloc() can be used to acquire a partition for private use. The
+ * function #fsl_shw_diminish_perms() can then be used to revoke specific
+ * permissions on the partition, and #fsl_shw_sfree() can be used to release the
+ * partition.
+ *
+ * @section keystore_sec User Keystore
+ *
+ * User Keystore functionality is defined in fsl_shw_keystore.h. See @ref
+ * user_keystore for details. This is not supported on platforms without SCC2.
+ *
+ * @section di_sec Hardware key-select extensions - DryIce
+ *
+ * Some platforms have a component called DryIce which allows the software to
+ * control which key will be used by the secure memory encryption hardware.
+ * The choices are the secret per-chip Fused (IIM) Key, an unknown, hardware-
+ * generated Random Key, a software-written Programmed Key, or the IIM Key in
+ * combination with one of the others. #fsl_shw_pco_check_pk_supported() can
+ * be used to determine whether this feature is available on the platform.
+ * The rest of this section will explain the symmetric ciphering and key
+ * operations which are available on such a platform.
+ *
+ * The function #fsl_shw_sko_init_pf_key() will set up a Secret Key Object to
+ * refer to one of the system's platform keys. All keys which reference a
+ * platform key must use this initialization function, including a user-
+ * provided key value. Keys which are intended for software encryption must
+ * use #fsl_shw_sko_init().
+ *
+ * To change the setting of the Programmed Key of the DryIce module,
+ * #fsl_shw_establish_key() must be called with a platform key object of type
+ * #FSL_SHW_PF_KEY_PRG or #FSL_SHW_PF_KEY_IIM_PRG. The key will be go
+ * into the PK register of DryIce and not to the keystore. Any symmetric
+ * operation which references either #FSL_SHW_PF_KEY_PRG or
+ * #FSL_SHW_PF_KEY_IIM_PRG will use the current PK value (possibly modified by
+ * the secret fused IIM key). Before the Flatform Key can be changed, a call to
+ * #fsl_shw_release_key() or #fsl_shw_extract_key() must be made. Neither
+ * function will change the value in the PK registers, and further ciphering
+ * can take place.
+ *
+ * When #fsl_shw_establish_key() is called to change the PK value, a plaintext
+ * key can be passed in with the #FSL_KEY_WRAP_ACCEPT argument or a previously
+ * wrapped key can be passed in with the #FSL_KEY_WRAP_UNWRAP argument. If
+ * #FSL_KEY_WRAP_CREATE is passed in, then a random value will be loaded into
+ * the PK register. The PK value can be wrapped by a call to
+ * #fsl_shw_extract_key() for later use with the #FSL_KEY_WRAP_UNWRAP argument.
+ *
+ * As an alternative to using only the fused key for @ref wrap_sec,
+ * #fsl_shw_uco_set_wrap_key() can be used to select either the random key or
+ * the random key with the fused key as the key which will be used to protect
+ * the one-time value used to wrap the key. This allows for these
+ * wrapped keys to be dependent upon and therefore unrecoverable after a tamper
+ * event causes the erasure of the DryIce Random Key register.
+ *
+ * The software can request that the hardware generate a (new) Random Key for
+ * DryIce by calling #fsl_shw_gen_random_pf_key().
+ *
+ *
+ * @section td_sec Device Tamper-Detection
+ *
+ * Some platforms have a component which can detect certain types of tampering
+ * with the hardware. #fsl_shw_read_tamper_event() API will allow the
+ * retrieval of the type of event which caused a tamper-detection failure.
+ *
+ */
+
+/*! @defgroup glossary Glossary
+ *
+ * @li @b AES - Advanced Encryption Standard - An NIST-created block cipher
+ * originally knowns as Rijndael.
+ * @li @b ARC4 - ARCFOUR - An S-Box-based OFB mode stream cipher.
+ * @li @b CBC - Cipher-Block Chaining - Each encrypted block is XORed with the
+ * result of the previous block's encryption.
+ * @li @b CCM - A way of combining CBC and CTR to perform cipher and
+ * authentication.
+ * @li @b ciphertext - @a plaintext which has been encrypted in some fashion.
+ * @li @b context - Information on the state of a cryptographic operation,
+ * excluding any key. This could include IV, Counter Value, or SBox.
+ * @li @b CTR - A mode where a counter value is encrypted and then XORed with
+ * the data. After each block, the counter value is incremented.
+ * @li @b DES - Data Encryption Standard - An 8-octet-block cipher.
+ * @li @b ECB - Electronic Codebook - A straight encryption/decryption of the
+ * data.
+ * @li @b hash - A cryptographically strong one-way function performed on data.
+ * @li @b HMAC - Hashed Message Authentication Code - A key-dependent one-way
+ * hash result, used to verify authenticity of a message. The equation
+ * for an HMAC is hash((K + A) || hash((K + B) || msg)), where K is the
+ * key, A is the constant for the outer hash, B is the constant for the
+ * inner hash, and hash is the hashing function (MD5, SHA256, etc).
+ * @li @b IPAD - In an HMAC operation, the context generated by XORing the key
+ * with a constant and then hashing that value as the first block of the
+ * inner hash.
+ * @li @b IV - An "Initial Vector" or @a context for modes like CBC.
+ * @li @b MAC - A Message Authentication Code. HMAC, hashing, and CCM all
+ * produce a MAC.
+ * @li @b mode - A way of using a cryptographic algorithm. See ECB, CBC, etc.
+ * @li @b MD5 - Message Digest 5 - A one-way hash function.
+ * @li @b plaintext - Data which has not been encrypted, or has been decrypted
+ * from @a ciphertext.
+ * @li @b OPAD - In an HMAC operation, the context generated by XORing the key
+ * with a constant and then hashing that value as the first block of the
+ * outer hash.
+ * @li @b SHA - Secure Hash Algorithm - A one-way hash function.
+ * @li @b TDES - AKA @b 3DES - Triple Data Encryption Standard - A method of
+ * using two or three keys and DES to perform three operations (encrypt
+ * decrypt encrypt) to create a new algorithm.
+ * @li @b XOR - Exclusive-OR. A Boolean arithmetic function.
+ * @li @b Wrapped value - A (key) which has been encrypted into an opaque datum
+ * which cannot be unwrapped (decrypted) for use except by an authorized
+ * user. Once created, the key is never visible, but may be used for
+ * other cryptographic operations.
+ */
+
+#ifndef FSL_SHW_H
+#define FSL_SHW_H
+
+/* Set FSL_HAVE_* flags */
+
+#include "fsl_platform.h"
+
+#ifndef API_DOC
+
+#if defined(FSL_HAVE_SAHARA2) || defined(FSL_HAVE_SAHARA4)
+
+#include "sahara.h"
+
+#else
+
+#if defined(FSL_HAVE_RNGA) || defined(FSL_HAVE_RNGB) || defined(FSL_HAVE_RNGC)
+
+#include "rng_driver.h"
+
+#else
+
+#error FSL_SHW_API_platform_not_recognized
+
+#endif
+
+#endif /* HAVE SAHARA */
+
+#else /* API_DOC */
+
+#include <inttypes.h> /* for uint32_t, etc. */
+#include <stdio.h> /* Mainly for definition of NULL !! */
+
+/* These groups will appear in the order in which they are defined. */
+
+/*!
+ * @defgroup strgrp Objects
+ *
+ * These objects are used to pass information into and out of the API. Through
+ * flags and other settings, they control the behavior of the @ref opfuns.
+ *
+ * They are manipulated and queried by use of the various access functions.
+ * There are different sets defined for each object. See @ref objman.
+ */
+
+/*!
+ * @defgroup consgrp Enumerations and other Constants
+ *
+ * This collection of symbols comprise the values which can be passed into
+ * various functions to control how the API will work.
+ */
+
+/*! @defgroup opfuns Operational Functions
+ *
+ * These functions request that the underlying hardware perform cryptographic
+ * operations. They are the heart of the API.
+ */
+
+/****** Organization the Object Operations under one group ! **********/
+/*! @defgroup objman Object-Manipulation Operations
+ *
+ */
+/*! @addtogroup objman
+ @{ */
+/*!
+ * @defgroup pcoops Platform Context Object Operations
+ *
+ * The Platform Context object is "read-only", so only query operations are
+ * provided for it. It is returned by the #fsl_shw_get_capabilities()
+ * function.
+ */
+
+/*! @defgroup ucoops User Context Operations
+ *
+ * These operations should be the only access to the #fsl_shw_uco_t
+ * type/struct, as the internal members of the object are subject to change.
+ * The #fsl_shw_uco_init() function must be called before any other use of the
+ * object.
+ */
+
+/*!
+ * @defgroup rops Result Object Operations
+ *
+ * As the Result Object contains the result of one of the @ref opfuns. The
+ * manipulations provided are query-only. No initialization is needed for this
+ * object.
+ */
+
+/*!
+ * @defgroup skoops Secret Key Object Operations
+ *
+ * These operations should be the only access to the #fsl_shw_sko_t
+ * type/struct, as the internal members of that object are subject to change.
+ */
+
+/*!
+ * @defgroup ksoops Keystore Object Operations
+ *
+ * These operations should be the only access to the #fsl_shw_kso_t
+ * type/struct, as the internal members of that object are subject to change.
+ */
+
+/*!
+ * @defgroup hcops Hash Context Object Operations
+ *
+ * These operations should be the only access to the #fsl_shw_hco_t
+ * type/struct, as the internal members of that object are subject to change.
+ */
+
+/*!
+ * @defgroup hmcops HMAC Context Object Operations
+ *
+ * These operations should be the only access to the #fsl_shw_hmco_t
+ * type/struct, as the internal members of that object are subject to change.
+ */
+
+/*!
+ * @defgroup sccops Symmetric Cipher Context Operations
+ *
+ * These operations should be the only access to the #fsl_shw_scco_t
+ * type/struct, as the internal members of that object are subject to change
+ */
+
+/*! @defgroup accoops Authentication-Cipher Context Object Operations
+ *
+ * These functions operate on a #fsl_shw_acco_t. Their purpose is to set
+ * flags, fields, etc., in order to control the operation of
+ * #fsl_shw_gen_encrypt() and #fsl_shw_auth_decrypt().
+ */
+
+ /* @} *//************ END GROUPING of Object Manipulations *****************/
+
+/*! @defgroup miscfuns Miscellaneous Functions
+ *
+ * These functions are neither @ref opfuns nor @ref objman. Their behavior
+ * does not depend upon the flags in the #fsl_shw_uco_t, yet they may involve
+ * more interaction with the library and the kernel than simply querying an
+ * object.
+ */
+
+/******************************************************************************
+ * Enumerations
+ *****************************************************************************/
+/*! @addtogroup consgrp
+ @{ */
+
+/*!
+ * Flags for the state of the User Context Object (#fsl_shw_uco_t).
+ *
+ * These flags describe how the @ref opfuns will operate.
+ */
+typedef enum fsl_shw_user_ctx_flags_t {
+ /*!
+ * API will block the caller until operation completes. The result will be
+ * available in the return code. If this is not set, user will have to get
+ * results using #fsl_shw_get_results().
+ */
+ FSL_UCO_BLOCKING_MODE,
+ /*!
+ * User wants callback (at the function specified with
+ * #fsl_shw_uco_set_callback()) when the operation completes. This flag is
+ * valid only if #FSL_UCO_BLOCKING_MODE is not set.
+ */
+ FSL_UCO_CALLBACK_MODE,
+ /*! Do not free descriptor chain after driver (adaptor) finishes */
+ FSL_UCO_SAVE_DESC_CHAIN,
+ /*!
+ * User has made at least one request with callbacks requested, so API is
+ * ready to handle others.
+ */
+ FSL_UCO_CALLBACK_SETUP_COMPLETE,
+ /*!
+ * (virtual) pointer to descriptor chain is completely linked with physical
+ * (DMA) addresses, ready for the hardware. This flag should not be used
+ * by FSL SHW API programs.
+ */
+ FSL_UCO_CHAIN_PREPHYSICALIZED,
+ /*!
+ * The user has changed the context but the changes have not been copied to
+ * the kernel driver.
+ */
+ FSL_UCO_CONTEXT_CHANGED,
+ /*! Internal Use. This context belongs to a user-mode API user. */
+ FSL_UCO_USERMODE_USER,
+} fsl_shw_user_ctx_flags_t;
+
+/*!
+ * Return code for FSL_SHW library.
+ *
+ * These codes may be returned from a function call. In non-blocking mode,
+ * they will appear as the status in a Result Object.
+ */
+typedef enum fsl_shw_return_t {
+ /*!
+ * No error. As a function return code in Non-blocking mode, this may
+ * simply mean that the operation was accepted for eventual execution.
+ */
+ FSL_RETURN_OK_S = 0,
+ /*! Failure for non-specific reason. */
+ FSL_RETURN_ERROR_S,
+ /*!
+ * Operation failed because some resource was not able to be allocated.
+ */
+ FSL_RETURN_NO_RESOURCE_S,
+ /*! Crypto algorithm unrecognized or improper. */
+ FSL_RETURN_BAD_ALGORITHM_S,
+ /*! Crypto mode unrecognized or improper. */
+ FSL_RETURN_BAD_MODE_S,
+ /*! Flag setting unrecognized or inconsistent. */
+ FSL_RETURN_BAD_FLAG_S,
+ /*! Improper or unsupported key length for algorithm. */
+ FSL_RETURN_BAD_KEY_LENGTH_S,
+ /*! Improper parity in a (DES, TDES) key. */
+ FSL_RETURN_BAD_KEY_PARITY_S,
+ /*!
+ * Improper or unsupported data length for algorithm or internal buffer.
+ */
+ FSL_RETURN_BAD_DATA_LENGTH_S,
+ /*! Authentication / Integrity Check code check failed. */
+ FSL_RETURN_AUTH_FAILED_S,
+ /*! A memory error occurred. */
+ FSL_RETURN_MEMORY_ERROR_S,
+ /*! An error internal to the hardware occurred. */
+ FSL_RETURN_INTERNAL_ERROR_S,
+ /*! ECC detected Point at Infinity */
+ FSL_RETURN_POINT_AT_INFINITY_S,
+ /*! ECC detected No Point at Infinity */
+ FSL_RETURN_POINT_NOT_AT_INFINITY_S,
+ /*! GCD is One */
+ FSL_RETURN_GCD_IS_ONE_S,
+ /*! GCD is not One */
+ FSL_RETURN_GCD_IS_NOT_ONE_S,
+ /*! Candidate is Prime */
+ FSL_RETURN_PRIME_S,
+ /*! Candidate is not Prime */
+ FSL_RETURN_NOT_PRIME_S,
+ /*! N register loaded improperly with even value */
+ FSL_RETURN_EVEN_MODULUS_ERROR_S,
+ /*! Divisor is zero. */
+ FSL_RETURN_DIVIDE_BY_ZERO_ERROR_S,
+ /*! Bad Exponent or Scalar value for Point Multiply */
+ FSL_RETURN_BAD_EXPONENT_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_OSCILLATOR_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_STATISTICS_ERROR_S,
+} fsl_shw_return_t;
+
+/*!
+ * Algorithm Identifier.
+ *
+ * Selection of algorithm will determine how large the block size of the
+ * algorithm is. Context size is the same length unless otherwise specified.
+ * Selection of algorithm also affects the allowable key length.
+ */
+typedef enum fsl_shw_key_alg_t {
+ FSL_KEY_ALG_HMAC, /*!< Key will be used to perform an HMAC. Key
+ size is 1 to 64 octets. Block size is 64
+ octets. */
+ FSL_KEY_ALG_AES, /*!< Advanced Encryption Standard (Rijndael).
+ Block size is 16 octets. Key size is 16
+ octets. (The single choice of key size is a
+ Sahara platform limitation.) */
+ FSL_KEY_ALG_DES, /*!< Data Encryption Standard. Block size is
+ 8 octets. Key size is 8 octets. */
+ FSL_KEY_ALG_TDES, /*!< 2- or 3-key Triple DES. Block size is 8
+ octets. Key size is 16 octets for 2-key
+ Triple DES, and 24 octets for 3-key. */
+ FSL_KEY_ALG_ARC4 /*!< ARC4. No block size. Context size is 259
+ octets. Allowed key size is 1-16 octets.
+ (The choices for key size are a Sahara
+ platform limitation.) */
+} fsl_shw_key_alg_t;
+
+/*!
+ * Mode selector for Symmetric Ciphers.
+ *
+ * The selection of mode determines how a cryptographic algorithm will be
+ * used to process the plaintext or ciphertext.
+ *
+ * For all modes which are run block-by-block (that is, all but
+ * #FSL_SYM_MODE_STREAM), any partial operations must be performed on a text
+ * length which is multiple of the block size. Except for #FSL_SYM_MODE_CTR,
+ * these block-by-block algorithms must also be passed a total number of octets
+ * which is a multiple of the block size.
+ *
+ * In modes which require that the total number of octets of data be a multiple
+ * of the block size (#FSL_SYM_MODE_ECB and #FSL_SYM_MODE_CBC), and the user
+ * has a total number of octets which are not a multiple of the block size, the
+ * user must perform any necessary padding to get to the correct data length.
+ */
+typedef enum fsl_shw_sym_mode_t {
+ /*!
+ * Stream. There is no associated block size. Any request to process data
+ * may be of any length. This mode is only for ARC4 operations, and is
+ * also the only mode used for ARC4.
+ */
+ FSL_SYM_MODE_STREAM,
+
+ /*!
+ * Electronic Codebook. Each block of data is encrypted/decrypted. The
+ * length of the data stream must be a multiple of the block size. This
+ * mode may be used for DES, 3DES, and AES. The block size is determined
+ * by the algorithm.
+ */
+ FSL_SYM_MODE_ECB,
+ /*!
+ * Cipher-Block Chaining. Each block of data is encrypted/decrypted and
+ * then "chained" with the previous block by an XOR function. Requires
+ * context to start the XOR (previous block). This mode may be used for
+ * DES, 3DES, and AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CBC,
+ /*!
+ * Counter. The counter is encrypted, then XORed with a block of data.
+ * The counter is then incremented (using modulus arithmetic) for the next
+ * block. The final operation may be non-multiple of block size. This mode
+ * may be used for AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CTR,
+} fsl_shw_sym_mode_t;
+
+/*!
+ * Algorithm selector for Cryptographic Hash functions.
+ *
+ * Selection of algorithm determines how large the context and digest will be.
+ * Context is the same size as the digest (resulting hash), unless otherwise
+ * specified.
+ */
+typedef enum fsl_shw_hash_alg_t {
+ FSL_HASH_ALG_MD5, /*!< MD5 algorithm. Digest is 16 octets. */
+ FSL_HASH_ALG_SHA1, /*!< SHA-1 (aka SHA or SHA-160) algorithm.
+ Digest is 20 octets. */
+ FSL_HASH_ALG_SHA224, /*!< SHA-224 algorithm. Digest is 28 octets,
+ though context is 32 octets. */
+ FSL_HASH_ALG_SHA256 /*!< SHA-256 algorithm. Digest is 32
+ octets. */
+} fsl_shw_hash_alg_t;
+
+/*!
+ * The type of Authentication-Cipher function which will be performed.
+ */
+typedef enum fsl_shw_acc_mode_t {
+ /*!
+ * CBC-MAC for Counter. Requires context and modulus. Final operation may
+ * be non-multiple of block size. This mode may be used for AES.
+ */
+ FSL_ACC_MODE_CCM,
+ /*!
+ * SSL mode. Not supported. Combines HMAC and encrypt (or decrypt).
+ * Needs one key object for encryption, another for the HMAC. The usual
+ * hashing and symmetric encryption algorithms are supported.
+ */
+ FSL_ACC_MODE_SSL,
+} fsl_shw_acc_mode_t;
+
+/*!
+ * The operation which controls the behavior of #fsl_shw_establish_key().
+ *
+ * These values are passed to #fsl_shw_establish_key().
+ */
+typedef enum fsl_shw_key_wrap_t {
+ FSL_KEY_WRAP_CREATE, /*!< Generate a key from random values. */
+ FSL_KEY_WRAP_ACCEPT, /*!< Use the provided clear key. */
+ FSL_KEY_WRAP_UNWRAP /*!< Unwrap a previously wrapped key. */
+} fsl_shw_key_wrap_t;
+
+/* REQ-S2LRD-PINTFC-COA-HCO-001 */
+/*!
+ * Flags which control a Hash operation.
+ *
+ * These may be combined by ORing them together. See #fsl_shw_hco_set_flags()
+ * and #fsl_shw_hco_clear_flags().
+ */
+typedef enum fsl_shw_hash_ctx_flags_t {
+ FSL_HASH_FLAGS_INIT = 1, /*!< Context is empty. Hash is started
+ from scratch, with a message-processed
+ count of zero. */
+ FSL_HASH_FLAGS_SAVE = 2, /*!< Retrieve context from hardware after
+ hashing. If used with the
+ #FSL_HASH_FLAGS_FINALIZE flag, the final
+ digest value will be saved in the
+ object. */
+ FSL_HASH_FLAGS_LOAD = 4, /*!< Place context into hardware before
+ hashing. */
+ FSL_HASH_FLAGS_FINALIZE = 8, /*!< PAD message and perform final digest
+ operation. If user message is
+ pre-padded, this flag should not be
+ used. */
+} fsl_shw_hash_ctx_flags_t;
+
+/*!
+ * Flags which control an HMAC operation.
+ *
+ * These may be combined by ORing them together. See #fsl_shw_hmco_set_flags()
+ * and #fsl_shw_hmco_clear_flags().
+ */
+typedef enum fsl_shw_hmac_ctx_flags_t {
+ FSL_HMAC_FLAGS_INIT = 1, /*!< Message context is empty. HMAC is
+ started from scratch (with key) or from
+ precompute of inner hash, depending on
+ whether
+ #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT is
+ set. */
+ FSL_HMAC_FLAGS_SAVE = 2, /*!< Retrieve ongoing context from hardware
+ after hashing. If used with the
+ #FSL_HMAC_FLAGS_FINALIZE flag, the final
+ digest value (HMAC) will be saved in the
+ object. */
+ FSL_HMAC_FLAGS_LOAD = 4, /*!< Place ongoing context into hardware
+ before hashing. */
+ FSL_HMAC_FLAGS_FINALIZE = 8, /*!< PAD message and perform final HMAC
+ operations of inner and outer hashes. */
+ FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT = 16 /*!< This means that the context
+ contains precomputed inner and outer
+ hash values. */
+} fsl_shw_hmac_ctx_flags_t;
+
+/*!
+ * Flags to control use of the #fsl_shw_scco_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_scco_set_flags() and #fsl_shw_scco_clear_flags()
+ */
+typedef enum fsl_shw_sym_ctx_flags_t {
+ /*!
+ * Context is empty. In ARC4, this means that the S-Box needs to be
+ * generated from the key. In #FSL_SYM_MODE_CBC mode, this allows an IV of
+ * zero to be specified. In #FSL_SYM_MODE_CTR mode, it means that an
+ * initial CTR value of zero is desired.
+ */
+ FSL_SYM_CTX_INIT = 1,
+ /*!
+ * Load context from object into hardware before running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_LOAD = 2,
+ /*!
+ * Save context from hardware into object after running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_SAVE = 4,
+ /*!
+ * Context (SBox) is to be unwrapped and wrapped on each use.
+ * This flag is unsupported.
+ * */
+ FSL_SYM_CTX_PROTECT = 8,
+} fsl_shw_sym_ctx_flags_t;
+
+/*!
+ * Flags which describe the state of the #fsl_shw_sko_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_sko_set_flags() and #fsl_shw_sko_clear_flags()
+ */
+typedef enum fsl_shw_key_flags_t {
+ FSL_SKO_KEY_IGNORE_PARITY = 1, /*!< If algorithm is DES or 3DES, do not
+ validate the key parity bits. */
+ FSL_SKO_KEY_PRESENT = 2, /*!< Clear key is present in the object. */
+ FSL_SKO_KEY_ESTABLISHED = 4, /*!< Key has been established for use. This
+ feature is not available for all
+ platforms, nor for all algorithms and
+ modes. */
+ FSL_SKO_KEY_SW_KEY = 8, /*!< This key is for software use, and can
+ be copied out of a keystore by its owner.
+ The default is that they key is available
+ only for hardware (or security driver)
+ use. */
+} fsl_shw_key_flags_t;
+
+/*!
+ * Type of value which is associated with an established key.
+ */
+typedef uint64_t key_userid_t;
+
+/*!
+ * Flags which describe the state of the #fsl_shw_acco_t.
+ *
+ * The @a FSL_ACCO_CTX_INIT and @a FSL_ACCO_CTX_FINALIZE flags, when used
+ * together, provide for a one-shot operation.
+ */
+typedef enum fsl_shw_auth_ctx_flags_t {
+ FSL_ACCO_CTX_INIT = 1, /*!< Initialize Context(s) */
+ FSL_ACCO_CTX_LOAD = 2, /*!< Load intermediate context(s).
+ This flag is unsupported. */
+ FSL_ACCO_CTX_SAVE = 4, /*!< Save intermediate context(s).
+ This flag is unsupported. */
+ FSL_ACCO_CTX_FINALIZE = 8, /*!< Create MAC during this operation. */
+ FSL_ACCO_NIST_CCM = 16, /*!< Formatting of CCM input data is
+ performed by calls to
+ #fsl_shw_ccm_nist_format_ctr_and_iv() and
+ #fsl_shw_ccm_nist_update_ctr_and_iv(). */
+} fsl_shw_auth_ctx_flags_t;
+
+/*!
+ * Modulus Selector for CTR modes.
+ *
+ * The incrementing of the Counter value may be modified by a modulus. If no
+ * modulus is needed or desired for AES, use #FSL_CTR_MOD_128.
+ */
+typedef enum fsl_shw_ctr_mod_t {
+ FSL_CTR_MOD_8, /*!< Run counter with modulus of 2^8. */
+ FSL_CTR_MOD_16, /*!< Run counter with modulus of 2^16. */
+ FSL_CTR_MOD_24, /*!< Run counter with modulus of 2^24. */
+ FSL_CTR_MOD_32, /*!< Run counter with modulus of 2^32. */
+ FSL_CTR_MOD_40, /*!< Run counter with modulus of 2^40. */
+ FSL_CTR_MOD_48, /*!< Run counter with modulus of 2^48. */
+ FSL_CTR_MOD_56, /*!< Run counter with modulus of 2^56. */
+ FSL_CTR_MOD_64, /*!< Run counter with modulus of 2^64. */
+ FSL_CTR_MOD_72, /*!< Run counter with modulus of 2^72. */
+ FSL_CTR_MOD_80, /*!< Run counter with modulus of 2^80. */
+ FSL_CTR_MOD_88, /*!< Run counter with modulus of 2^88. */
+ FSL_CTR_MOD_96, /*!< Run counter with modulus of 2^96. */
+ FSL_CTR_MOD_104, /*!< Run counter with modulus of 2^104. */
+ FSL_CTR_MOD_112, /*!< Run counter with modulus of 2^112. */
+ FSL_CTR_MOD_120, /*!< Run counter with modulus of 2^120. */
+ FSL_CTR_MOD_128 /*!< Run counter with modulus of 2^128. */
+} fsl_shw_ctr_mod_t;
+
+/*!
+ * Permissions flags for Secure Partitions
+ *
+ * They currently map directly to the SCC2 hardware values, but this is not
+ * guarinteed behavior.
+ */
+typedef enum fsl_shw_permission_t {
+/*! SCM Access Permission: Do not zeroize/deallocate partition on SMN Fail state */
+ FSL_PERM_NO_ZEROIZE,
+/*! SCM Access Permission: Enforce trusted key read in */
+ FSL_PERM_TRUSTED_KEY_READ,
+/*! SCM Access Permission: Ignore Supervisor/User mode in permission determination */
+ FSL_PERM_HD_S,
+/*! SCM Access Permission: Allow Read Access to Host Domain */
+ FSL_PERM_HD_R,
+/*! SCM Access Permission: Allow Write Access to Host Domain */
+ FSL_PERM_HD_W,
+/*! SCM Access Permission: Allow Execute Access to Host Domain */
+ FSL_PERM_HD_X,
+/*! SCM Access Permission: Allow Read Access to Trusted Host Domain */
+ FSL_PERM_TH_R,
+/*! SCM Access Permission: Allow Write Access to Trusted Host Domain */
+ FSL_PERM_TH_W,
+/*! SCM Access Permission: Allow Read Access to Other/World Domain */
+ FSL_PERM_OT_R,
+/*! SCM Access Permission: Allow Write Access to Other/World Domain */
+ FSL_PERM_OT_W,
+/*! SCM Access Permission: Allow Execute Access to Other/World Domain */
+ FSL_PERM_OT_X,
+} fsl_shw_permission_t;
+
+/*!
+ * Select the cypher mode to use for partition cover/uncover operations.
+ *
+ * They currently map directly to the values used in the SCC2 driver, but this
+ * is not guarinteed behavior.
+ */
+typedef enum fsl_shw_cypher_mode_t {
+ FSL_SHW_CYPHER_MODE_ECB, /*!< ECB mode */
+ FSL_SHW_CYPHER_MODE_CBC, /*!< CBC mode */
+} fsl_shw_cypher_mode_t;
+
+/*!
+ * Which platform key should be presented for cryptographic use.
+ */
+typedef enum fsl_shw_pf_key_t {
+ FSL_SHW_PF_KEY_IIM, /*!< Present fused IIM key */
+ FSL_SHW_PF_KEY_PRG, /*!< Present Program key */
+ FSL_SHW_PF_KEY_IIM_PRG, /*!< Present IIM ^ Program key */
+ FSL_SHW_PF_KEY_IIM_RND, /*!< Present Random key */
+ FSL_SHW_PF_KEY_RND, /*!< Present IIM ^ Random key */
+} fsl_shw_pf_key_t;
+
+/*!
+ * The various security tamper events
+ */
+typedef enum fsl_shw_tamper_t {
+ FSL_SHW_TAMPER_NONE, /*!< No error detected */
+ FSL_SHW_TAMPER_WTD, /*!< wire-mesh tampering det */
+ FSL_SHW_TAMPER_ETBD, /*!< ext tampering det: input B */
+ FSL_SHW_TAMPER_ETAD, /*!< ext tampering det: input A */
+ FSL_SHW_TAMPER_EBD, /*!< external boot detected */
+ FSL_SHW_TAMPER_SAD, /*!< security alarm detected */
+ FSL_SHW_TAMPER_TTD, /*!< temperature tampering det */
+ FSL_SHW_TAMPER_CTD, /*!< clock tampering det */
+ FSL_SHW_TAMPER_VTD, /*!< voltage tampering det */
+ FSL_SHW_TAMPER_MCO, /*!< monotonic counter overflow */
+ FSL_SHW_TAMPER_TCO, /*!< time counter overflow */
+} fsl_shw_tamper_t;
+
+/*! @} *//* consgrp */
+
+/******************************************************************************
+ * Data Structures
+ *****************************************************************************/
+/*! @addtogroup strgrp
+ @{ */
+
+/* REQ-S2LRD-PINTFC-COA-IBO-001 */
+/*!
+ * Application Initialization Object
+ *
+ * This object, the operations on it, and its interaction with the driver are
+ * TBD.
+ */
+typedef struct fsl_sho_ibo_t {
+} fsl_sho_ibo_t;
+
+/* REQ-S2LRD-PINTFC-COA-UCO-001 */
+/*!
+ * User Context Object
+ *
+ * This object must be initialized by a call to #fsl_shw_uco_init(). It must
+ * then be passed to #fsl_shw_register_user() before it can be used in any
+ * calls besides those in @ref ucoops.
+ *
+ * It contains the user's configuration for the API, for instance whether an
+ * operation should block, or instead should call back the user upon completion
+ * of the operation.
+ *
+ * See @ref ucoops for further information.
+ */
+typedef struct fsl_shw_uco_t { /* fsl_shw_user_context_object */
+} fsl_shw_uco_t;
+
+/* REQ-S2LRD-PINTFC-API-GEN-006 ?? */
+/*!
+ * Result Object
+ *
+ * This object will contain success and failure information about a specific
+ * cryptographic request which has been made.
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref rops.
+ */
+typedef struct fsl_shw_result_t { /* fsl_shw_result */
+} fsl_shw_result_t;
+
+/*!
+ * Keystore Object
+ *
+ * This object holds the context of a user keystore, including the functions
+ * that define the interface and pointers to where the key data is stored. The
+ * user must supply a set of functions to handle keystore management, including
+ * slot allocation, deallocation, etc. A default keystore manager is provided
+ * as part of the API.
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref ksoops.
+ */
+typedef struct fsl_shw_kso_t { /* fsl_shw_keystore_object */
+} fsl_shw_kso_t;
+
+/* REQ-S2LRD-PINTFC-COA-SKO-001 */
+/*!
+ * Secret Key Object
+ *
+ * This object contains a key for a cryptographic operation, and information
+ * about its current state, its intended usage, etc. It may instead contain
+ * information about a protected key, or an indication to use a platform-
+ * specific secret key.
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref skoops.
+ */
+typedef struct fsl_shw_sko_t { /* fsl_shw_secret_key_object */
+} fsl_shw_sko_t;
+
+/* REQ-S2LRD-PINTFC-COA-CO-001 */
+/*!
+ * Platform Capabilities Object
+ *
+ * This object will contain information about the cryptographic features of the
+ * platform which the program is running on.
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions.
+ *
+ * See @ref pcoops.
+ */
+typedef struct fsl_shw_pco_t { /* fsl_shw_platform_capabilities_object */
+} fsl_shw_pco_t;
+
+/* REQ-S2LRD-PINTFC-COA-HCO-001 */
+/*!
+ * Hash Context Object
+ *
+ * This object contains information to control hashing functions.
+
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref hcops.
+ */
+typedef struct fsl_shw_hco_t { /* fsl_shw_hash_context_object */
+} fsl_shw_hco_t;
+
+/*!
+ * HMAC Context Object
+ *
+ * This object contains information to control HMAC functions.
+
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref hmcops.
+ */
+typedef struct fsl_shw_hmco_t { /* fsl_shw_hmac_context_object */
+} fsl_shw_hmco_t;
+
+/* REQ-S2LRD-PINTFC-COA-SCCO-001 */
+/*!
+ * Symmetric Cipher Context Object
+ *
+ * This object contains information to control Symmetric Ciphering encrypt and
+ * decrypt functions in #FSL_SYM_MODE_STREAM (ARC4), #FSL_SYM_MODE_ECB,
+ * #FSL_SYM_MODE_CBC, and #FSL_SYM_MODE_CTR modes and the
+ * #fsl_shw_symmetric_encrypt() and #fsl_shw_symmetric_decrypt() functions.
+ * CCM mode is controlled with the #fsl_shw_acco_t object.
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref sccops.
+ */
+typedef struct fsl_shw_scco_t { /* fsl_shw_symmetric_cipher_context_object */
+} fsl_shw_scco_t;
+
+/*!
+ * Authenticate-Cipher Context Object
+
+ * An object for controlling the function of, and holding information about,
+ * data for the authenticate-cipher functions, #fsl_shw_gen_encrypt() and
+ * #fsl_shw_auth_decrypt().
+ *
+ * No direct access to its members should be made by programs. Instead, the
+ * object should be manipulated using the provided functions. See @ref
+ * accoops.
+ */
+typedef struct fsl_shw_acco_t { /* fsl_shw_authenticate_cipher_context_object */
+} fsl_shw_acco_t;
+ /*! @} *//* strgrp */
+
+/******************************************************************************
+ * Access Macros for Objects
+ *****************************************************************************/
+/*! @addtogroup pcoops
+ @{ */
+
+/*!
+ * Get FSL SHW API version
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] major A pointer to where the major version
+ * of the API is to be stored.
+ * @param[out] minor A pointer to where the minor version
+ * of the API is to be stored.
+ */
+void fsl_shw_pco_get_version(const fsl_shw_pco_t * pc_info,
+ uint32_t * major, uint32_t * minor);
+
+/*!
+ * Get underlying driver version.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] major A pointer to where the major version
+ * of the driver is to be stored.
+ * @param[out] minor A pointer to where the minor version
+ * of the driver is to be stored.
+ */
+void fsl_shw_pco_get_driver_version(const fsl_shw_pco_t * pc_info,
+ uint32_t * major, uint32_t * minor);
+
+/*!
+ * Get list of symmetric algorithms supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] algorithms A pointer to where to store the location of
+ * the list of algorithms.
+ * @param[out] algorithm_count A pointer to where to store the number of
+ * algorithms in the list at @a algorithms.
+ */
+void fsl_shw_pco_get_sym_algorithms(const fsl_shw_pco_t * pc_info,
+ fsl_shw_key_alg_t * algorithms[],
+ uint8_t * algorithm_count);
+
+/*!
+ * Get list of symmetric modes supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] modes A pointer to where to store the location of
+ * the list of modes.
+ * @param[out] mode_count A pointer to where to store the number of
+ * algorithms in the list at @a modes.
+ */
+void fsl_shw_pco_get_sym_modes(const fsl_shw_pco_t * pc_info,
+ fsl_shw_sym_mode_t * modes[],
+ uint8_t * mode_count);
+
+/*!
+ * Get list of hash algorithms supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] algorithms A pointer which will be set to the list of
+ * algorithms.
+ * @param[out] algorithm_count The number of algorithms in the list at @a
+ * algorithms.
+ */
+void fsl_shw_pco_get_hash_algorithms(const fsl_shw_pco_t * pc_info,
+ fsl_shw_hash_alg_t * algorithms[],
+ uint8_t * algorithm_count);
+
+/*!
+ * Determine whether the combination of a given symmetric algorithm and a given
+ * mode is supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param algorithm A Symmetric Cipher algorithm.
+ * @param mode A Symmetric Cipher mode.
+ *
+ * @return 0 if combination is not supported, non-zero if supported.
+ */
+int fsl_shw_pco_check_sym_supported(const fsl_shw_pco_t * pc_info,
+ fsl_shw_key_alg_t algorithm,
+ fsl_shw_sym_mode_t mode);
+
+/*!
+ * Determine whether a given Encryption-Authentication mode is supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param mode The Authentication mode.
+ *
+ * @return 0 if mode is not supported, non-zero if supported.
+ */
+int fsl_shw_pco_check_auth_supported(const fsl_shw_pco_t * pc_info,
+ fsl_shw_acc_mode_t mode);
+
+/*!
+ * Determine whether Black Keys (key establishment / wrapping) is supported.
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 0 if wrapping is not supported, non-zero if supported.
+ */
+int fsl_shw_pco_check_black_key_supported(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get FSL SHW SCC driver version
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] major A pointer to where the major version
+ * of the SCC driver is to be stored.
+ * @param[out] minor A pointer to where the minor version
+ * of the SCC driver is to be stored.
+ */
+void fsl_shw_pco_get_scc_driver_version(const fsl_shw_pco_t * pc_info,
+ uint32_t * major, uint32_t * minor);
+
+/*!
+ * Get SCM hardware version
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @return The SCM hardware version
+ */
+uint32_t fsl_shw_pco_get_scm_version(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get SMN hardware version
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @return The SMN hardware version
+ */
+uint32_t fsl_shw_pco_get_smn_version(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get the size of an SCM block, in bytes
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @return The size of an SCM block, in bytes.
+ */
+uint32_t fsl_shw_pco_get_scm_block_size(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get size of Black and Red RAM memory
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ * @param[out] black_size A pointer to where the size of the Black RAM, in
+ * blocks, is to be placed.
+ * @param[out] red_size A pointer to where the size of the Red RAM, in
+ * blocks, is to be placed.
+ */
+void fsl_shw_pco_get_smn_size(const fsl_shw_pco_t * pc_info,
+ uint32_t * black_size, uint32_t * red_size);
+
+/*!
+ * Determine whether Secure Partitions are supported
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 0 if secure partitions are not supported, non-zero if supported.
+ */
+int fsl_shw_pco_check_spo_supported(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get the size of a Secure Partitions
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return Partition size, in bytes. 0 if Secure Partitions not supported.
+ */
+uint32_t fsl_shw_pco_get_spo_size_bytes(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Get the number of Secure Partitions on this platform
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return Number of partitions. 0 if Secure Partitions not supported. Note
+ * that this returns the total number of partitions, though
+ * not all may be available to the user.
+ */
+uint32_t fsl_shw_pco_get_spo_count(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Determine whether Platform Key features are available
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 1 if Programmed Key features are available, otherwise zero.
+ */
+int fsl_shw_pco_check_pk_supported(const fsl_shw_pco_t * pc_info);
+
+/*!
+ * Determine whether Software Key features are available
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 1 if Software key features are available, otherwise zero.
+ */
+int fsl_shw_pco_check_sw_keys_supported(const fsl_shw_pco_t * pc_info);
+
+/*! @} *//* pcoops */
+
+/*! @addtogroup ucoops
+ @{ */
+
+/*!
+ * Initialize a User Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the User Context Object to initial values, and set the size
+ * of the results pool. The mode will be set to a default of
+ * #FSL_UCO_BLOCKING_MODE.
+ *
+ * When using non-blocking operations, this sets the maximum number of
+ * operations which can be outstanding. This number includes the counts of
+ * operations waiting to start, operation(s) being performed, and results which
+ * have not been retrieved.
+ *
+ * Changes to this value are ignored once user registration has completed. It
+ * should be set to 1 if only blocking operations will ever be performed.
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param pool_size The maximum number of operations which can be
+ * outstanding.
+ */
+void fsl_shw_uco_init(fsl_shw_uco_t * user_ctx, uint16_t pool_size);
+
+/*!
+ * Set the User Reference for the User Context.
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param reference A value which will be passed back with a result.
+ */
+void fsl_shw_uco_set_reference(fsl_shw_uco_t * user_ctx, uint32_t reference);
+
+/*!
+ * Set the callback routine for the User Context.
+ *
+ * Note that the callback routine may be called when no results are available,
+ * and possibly even when no requests are outstanding.
+ *
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param callback_fn The function the API will invoke when an operation
+ * completes.
+ */
+void fsl_shw_uco_set_callback(fsl_shw_uco_t * user_ctx,
+ void (*callback_fn) (fsl_shw_uco_t * uco));
+
+/*!
+ * Set flags in the User Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param flags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+void fsl_shw_uco_set_flags(fsl_shw_uco_t * user_ctx, uint32_t flags);
+
+/*!
+ * Clear flags in the User Context.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param flags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+void fsl_shw_uco_clear_flags(fsl_shw_uco_t * user_ctx, uint32_t flags);
+
+/*!
+ * Select a key for the key-wrap key for key wrapping/unwrapping
+ *
+ * Without a call to this function, default is FSL_SHW_PF_KEY_IIM. The wrap
+ * key is used to encrypt and decrypt the per-key random secret which is used
+ * to calculate the key which will encrypt/decrypt the user's key.
+ *
+ * @param user_ctx The User Context object to operate on.
+ * @param pf_key Which key to use. Valid choices are
+ * #FSL_SHW_PF_KEY_IIM, #FSL_SHW_PF_KEY_RND, and
+ * #FSL_SHW_PF_KEY_IIM_RND.
+ */
+void fsl_shw_uco_set_wrap_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_pf_key_t pf_key);
+
+ /*! @} *//* ucoops */
+
+/*! @addtogroup rops
+ @{ */
+
+/*!
+ * Retrieve the status code from a Result Object.
+ *
+ * @param result The result object to query.
+ *
+ * @return The status of the request.
+ */
+fsl_shw_return_t fsl_shw_ro_get_status(fsl_shw_result_t * result);
+
+/*!
+ * Retrieve the reference value from a Result Object.
+ *
+ * @param result The result object to query.
+ *
+ * @return The reference associated with the request.
+ */
+uint32_t fsl_shw_ro_get_reference(fsl_shw_result_t * result);
+
+ /* @} *//* rops */
+
+/*! @addtogroup skoops
+ @{ */
+
+/*!
+ * Initialize a Secret Key Object.
+ *
+ * This function or #fsl_shw_sko_init_pf_key() must be called before performing
+ * any other operation with the Object.
+ *
+ * @param key_info The Secret Key Object to be initialized.
+ * @param algorithm DES, AES, etc.
+ *
+ */
+void fsl_shw_sko_init(fsl_shw_sko_t * key_info, fsl_shw_key_alg_t algorithm);
+
+/*!
+ * Initialize a Secret Key Object to use a Platform Key register.
+ *
+ * This function or #fsl_shw_sko_init() must be called before performing any
+ * other operation with the Object. #fsl_shw_sko_set_key() does not work on
+ * a key object initialized in this way.
+ *
+ * If this function is used to initialize the key object, but no key is
+ * established with the key object, then the object will refer strictly to the
+ * key value specified by the @c pf_key selection.
+ *
+ * If the pf key is #FSL_SHW_PF_KEY_PRG or #FSL_SHW_PF_KEY_IIM_PRG, then the
+ * key object may be used with #fsl_shw_establish_key() to change the Program
+ * Key value. When the pf key is neither #FSL_SHW_PF_KEY_PRG nor
+ * #FSL_SHW_PF_KEY_IIM_PRG, it is an error to call #fsl_shw_establish_key().
+ *
+ * @param key_info The Secret Key Object to be initialized.
+ * @param algorithm DES, AES, etc.
+ * @param pf_key Which platform key is referenced.
+ */
+void fsl_shw_sko_init_pf_key(fsl_shw_sko_t * key_info,
+ fsl_shw_key_alg_t algorithm,
+ fsl_shw_pf_key_t pf_key);
+
+/*!
+ * Store a cleartext key in the key object.
+ *
+ * This has the side effect of setting the #FSL_SKO_KEY_PRESENT flag. It should
+ * not be used if there is a key established with the key object. If there is,
+ * a call to #fsl_shw_release_key() should be made first.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param key A pointer to the beginning of the key.
+ * @param key_length The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+void fsl_shw_sko_set_key(fsl_shw_sko_t * key_object,
+ const uint8_t * key, uint16_t key_length);
+
+/*!
+ * Set a size for the key.
+ *
+ * This function would normally be used when the user wants the key to be
+ * generated from a random source.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param key_length The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+void fsl_shw_sko_set_key_length(fsl_shw_sko_t * key_object,
+ uint16_t key_length);
+
+/*!
+ * Set the User ID associated with the key.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param userid The User ID to identify authorized users of the key.
+ */
+void fsl_shw_sko_set_user_id(fsl_shw_sko_t * key_object, key_userid_t userid);
+
+/*!
+ * Set the keystore that the key will be stored in.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param keystore The keystore to place the key in. This is a variable of
+ * type #fsl_shw_kso_t.
+ */
+void fsl_shw_sko_set_keystore(fsl_shw_sko_t * key_object,
+ fsl_shw_kso_t * keystore);
+
+/*!
+ * Set the establish key handle into a key object.
+ *
+ * The @a userid field will be used to validate the access to the unwrapped
+ * key. This feature is not available for all platforms, nor for all
+ * algorithms and modes.
+ *
+ * The #FSL_SKO_KEY_ESTABLISHED will be set (and the #FSL_SKO_KEY_PRESENT
+ * flag will be cleared).
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param userid The User ID to verify this user is an authorized user of
+ * the key.
+ * @param handle A @a handle from #fsl_shw_sko_get_established_info.
+ */
+void fsl_shw_sko_set_established_info(fsl_shw_sko_t * key_object,
+ key_userid_t userid, uint32_t handle);
+
+/*!
+ * Extract the algorithm from a key object.
+ *
+ * @param key_info The Key Object to be queried.
+ * @param[out] algorithm A pointer to the location to store the algorithm.
+ */
+void fsl_shw_sko_get_algorithm(const fsl_shw_sko_t * key_info,
+ fsl_shw_key_alg_t * algorithm);
+
+/*!
+ * Retrieve the cleartext key from a key object that is stored in a user
+ * keystore.
+ *
+ * @param skobject The Key Object to be queried.
+ * @param[out] skkey A pointer to the location to store the key. NULL
+ * if the key is not stored in a user keystore.
+ */
+void fsl_shw_sko_get_key(const fsl_shw_sko_t * skobject, void *skkey);
+
+/*!
+ * Retrieve the established-key handle from a key object.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param handle The location to store the @a handle of the unwrapped
+ * key.
+ */
+void fsl_shw_sko_get_established_info(fsl_shw_sko_t * key_object,
+ uint32_t * handle);
+
+/*!
+ * Determine the size of a wrapped key based upon the cleartext key's length.
+ *
+ * This function can be used to calculate the number of octets that
+ * #fsl_shw_extract_key() will write into the location at @a covered_key.
+ *
+ * If zero is returned at @a length, this means that the key length in
+ * @a key_info is not supported.
+ *
+ * @param key_info Information about a key to be wrapped.
+ * @param length Location to store the length of a wrapped
+ * version of the key in @a key_info.
+ */
+void fsl_shw_sko_calculate_wrapped_size(const fsl_shw_sko_t * key_info,
+ uint32_t * length);
+
+/*!
+ * Set some flags in the key object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param flags (One or more) ORed members of #fsl_shw_key_flags_t which
+ * are to be set.
+ */
+void fsl_shw_sko_set_flags(fsl_shw_sko_t * key_object, uint32_t flags);
+
+/*!
+ * Clear some flags in the key object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param key_object A variable of type #fsl_shw_sko_t.
+ * @param flags (One or more) ORed members of #fsl_shw_key_flags_t which
+ * are to be reset.
+ */
+void fsl_shw_sko_clear_flags(fsl_shw_sko_t * key_object, uint32_t flags);
+
+ /*! @} *//* end skoops */
+
+/*****************************************************************************/
+
+/*! @addtogroup hcops
+ @{ */
+
+/*****************************************************************************/
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-004 - partially */
+/*!
+ * Initialize a Hash Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the hash
+ * context object.
+ *
+ * @param hash_ctx The hash context to operate upon.
+ * @param algorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+void fsl_shw_hco_init(fsl_shw_hco_t * hash_ctx, fsl_shw_hash_alg_t algorithm);
+
+/*****************************************************************************/
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-001 */
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-002 */
+/*!
+ * Get the current hash value and message length from the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hash_ctx The hash context to query.
+ * @param[out] digest Pointer to the location of @a length octets where to
+ * store a copy of the current value of the digest.
+ * @param length Number of octets of hash value to copy.
+ * @param[out] msg_length Pointer to the location to store the number of octets
+ * already hashed.
+ */
+void fsl_shw_hco_get_digest(const fsl_shw_hco_t * hash_ctx, uint8_t * digest,
+ uint8_t length, uint32_t * msg_length);
+
+/*****************************************************************************/
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-002 - partially */
+/*!
+ * Get the hash algorithm from the hash context object.
+ *
+ * @param hash_ctx The hash context to query.
+ * @param[out] algorithm Pointer to where the algorithm is to be stored.
+ */
+void fsl_shw_hco_get_info(const fsl_shw_hco_t * hash_ctx,
+ fsl_shw_hash_alg_t * algorithm);
+
+/*****************************************************************************/
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-003 */
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-004 */
+/*!
+ * Set the current hash value and message length in the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hash_ctx The hash context to operate upon.
+ * @param context Pointer to buffer of appropriate length to copy into
+ * the hash context object.
+ * @param msg_length The number of octets of the message which have
+ * already been hashed.
+ *
+ */
+void fsl_shw_hco_set_digest(fsl_shw_hco_t * hash_ctx, const uint8_t * context,
+ uint32_t msg_length);
+
+/*!
+ * Set flags in a Hash Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hash_ctx The hash context to be operated on.
+ * @param flags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+void fsl_shw_hco_set_flags(fsl_shw_hco_t * hash_ctx, uint32_t flags);
+
+/*!
+ * Clear flags in a Hash Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hash_ctx The hash context to be operated on.
+ * @param flags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+void fsl_shw_hco_clear_flags(fsl_shw_hco_t * hash_ctx, uint32_t flags);
+
+ /*! @} *//* end hcops */
+
+/*****************************************************************************/
+
+/*! @addtogroup hmcops
+ @{ */
+
+/*!
+ * Initialize an HMAC Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the HMAC
+ * context object.
+ *
+ * @param hmac_ctx The HMAC context to operate upon.
+ * @param algorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+void fsl_shw_hmco_init(fsl_shw_hmco_t * hmac_ctx, fsl_shw_hash_alg_t algorithm);
+
+/*!
+ * Set flags in an HMAC Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hmac_ctx The HMAC context to be operated on.
+ * @param flags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+void fsl_shw_hmco_set_flags(fsl_shw_hmco_t * hmac_ctx, uint32_t flags);
+
+/*!
+ * Clear flags in an HMAC Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hmac_ctx The HMAC context to be operated on.
+ * @param flags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+void fsl_shw_hmco_clear_flags(fsl_shw_hmco_t * hmac_ctx, uint32_t flags);
+
+/*! @} */
+
+/*****************************************************************************/
+
+/*! @addtogroup sccops
+ @{ */
+
+/*!
+ * Initialize a Symmetric Cipher Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. This will set the @a mode and @a algorithm and initialize the
+ * Object.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param algorithm The cipher algorithm this context will be used with.
+ * @param mode #FSL_SYM_MODE_CBC, #FSL_SYM_MODE_ECB, etc.
+ *
+ */
+void fsl_shw_scco_init(fsl_shw_scco_t * sym_ctx,
+ fsl_shw_key_alg_t algorithm, fsl_shw_sym_mode_t mode);
+
+/*!
+ * Set the flags for a Symmetric Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param flags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+void fsl_shw_scco_set_flags(fsl_shw_scco_t * sym_ctx, uint32_t flags);
+
+/*!
+ * Clear some flags in a Symmetric Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param flags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+void fsl_shw_scco_clear_flags(fsl_shw_scco_t * sym_ctx, uint32_t flags);
+
+/*!
+ * Set the Context (IV) for a Symmetric Cipher Context.
+ *
+ * This is to set the context/IV for #FSL_SYM_MODE_CBC mode, or to set the
+ * context (the S-Box and pointers) for ARC4. The full context size will
+ * be copied.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param context A pointer to the buffer which contains the context.
+ *
+ */
+void fsl_shw_scco_set_context(fsl_shw_scco_t * sym_ctx, uint8_t * context);
+
+/*!
+ * Get the Context for a Symmetric Cipher Context.
+ *
+ * This is to retrieve the context/IV for #FSL_SYM_MODE_CBC mode, or to
+ * retrieve context (the S-Box and pointers) for ARC4. The full context
+ * will be copied.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param[out] context Pointer to location where context will be stored.
+ */
+void fsl_shw_scco_get_context(const fsl_shw_scco_t * sym_ctx,
+ uint8_t * context);
+
+/*!
+ * Set the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will set the Counter Value for CTR mode.
+ *
+ * @param sym_ctx The context object to operate on.
+ * @param counter The starting counter value. The number of octets.
+ * copied will be the block size for the algorithm.
+ * @param modulus The modulus for controlling the incrementing of the counter.
+ *
+ */
+void fsl_shw_scco_set_counter_info(fsl_shw_scco_t * sym_ctx,
+ const uint8_t * counter,
+ fsl_shw_ctr_mod_t modulus);
+
+/*!
+ * Get the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will retrieve the Counter Value is for CTR mode.
+ *
+ * @param sym_ctx The context object to query.
+ * @param[out] counter Pointer to location to store the current counter
+ * value. The number of octets copied will be the
+ * block size for the algorithm.
+ * @param[out] modulus Pointer to location to store the modulus.
+ *
+ */
+void fsl_shw_scco_get_counter_info(const fsl_shw_scco_t * sym_ctx,
+ uint8_t * counter,
+ fsl_shw_ctr_mod_t * modulus);
+
+ /*! @} *//* end sccops */
+
+/*****************************************************************************/
+
+/*! @addtogroup accoops
+ @{ */
+
+/*!
+ * Initialize a Authentication-Cipher Context.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param mode The mode for this object (only #FSL_ACC_MODE_CCM
+ * supported).
+ */
+void fsl_shw_acco_init(fsl_shw_acco_t * auth_object, fsl_shw_acc_mode_t mode);
+
+/*!
+ * Set the flags for a Authentication-Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param flags The flags to set (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+void fsl_shw_acco_set_flags(fsl_shw_acco_t * auth_object, uint32_t flags);
+
+/*!
+ * Clear some flags in a Authentication-Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param flags The flags to reset (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+void fsl_shw_acco_clear_flags(fsl_shw_acco_t * auth_object, uint32_t flags);
+
+/*!
+ * Set up the Authentication-Cipher Object for CCM mode.
+ *
+ * This will set the @a auth_object for CCM mode and save the @a ctr,
+ * and @a mac_length. This function can be called instead of
+ * #fsl_shw_acco_init().
+ *
+ * The parameter @a ctr is Counter Block 0, (counter value 0), which is for the
+ * MAC.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param algorithm Cipher algorithm. Only AES is supported.
+ * @param ctr The initial counter value.
+ * @param mac_length The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ */
+void fsl_shw_acco_set_ccm(fsl_shw_acco_t * auth_object,
+ fsl_shw_key_alg_t algorithm,
+ const uint8_t * ctr, uint8_t mac_length);
+
+/*!
+ * Format the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will also set the IV and CTR values per Appendix A of NIST
+ * Special Publication 800-38C (May 2004). It will also perform the
+ * #fsl_shw_acco_set_ccm() operation with information derived from this set of
+ * parameters.
+ *
+ * Note this function assumes the algorithm is AES. It initializes the
+ * @a auth_object by setting the mode to #FSL_ACC_MODE_CCM and setting the
+ * flags to be #FSL_ACCO_NIST_CCM.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param t_length The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ * @param ad_length Number of octets of Associated Data (may be zero).
+ * @param q_length A value for the size of the length of @a q field. Valid
+ * values are 1-8.
+ * @param n The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param q The value of Q (size of the payload in octets).
+ *
+ */
+void fsl_shw_ccm_nist_format_ctr_and_iv(fsl_shw_acco_t * auth_object,
+ uint8_t t_length,
+ uint32_t ad_length,
+ uint8_t q_length,
+ const uint8_t * n, uint32_t q);
+
+/*!
+ * Update the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will set the IV and CTR values per Appendix A of NIST Special
+ * Publication 800-38C (May 2004).
+ *
+ * Note this function assumes that #fsl_shw_ccm_nist_format_ctr_and_iv() has
+ * previously been called on the @a auth_object.
+ *
+ * @param auth_object Pointer to object to operate on.
+ * @param n The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param q The value of Q (size of the payload in octets).
+ *
+ */
+void fsl_shw_ccm_nist_update_ctr_and_iv(fsl_shw_acco_t * auth_object,
+ const uint8_t * n, uint32_t q);
+
+ /* @} *//* accoops */
+
+/******************************************************************************
+ * Library functions
+ *****************************************************************************/
+
+/*! @addtogroup miscfuns
+ @{ */
+
+/* REQ-S2LRD-PINTFC-API-GEN-003 */
+/*!
+ * Determine the hardware security capabilities of this platform.
+ *
+ * Though a user context object is passed into this function, it will always
+ * act in a non-blocking manner.
+ *
+ * @param user_ctx The user context which will be used for the query.
+ *
+ * @return A pointer to the capabilities object.
+ */
+extern fsl_shw_pco_t *fsl_shw_get_capabilities(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-004 */
+/*!
+ * Create an association between the user and the provider of the API.
+ *
+ * @param user_ctx The user context which will be used for this association.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_register_user(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-005 */
+/*!
+ * Destroy the association between the user and the provider of the API.
+ *
+ * @param user_ctx The user context which is no longer needed.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_deregister_user(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-006 */
+/*!
+ * Retrieve results from earlier operations.
+ *
+ * @param user_ctx The user's context.
+ * @param result_size The number of array elements of @a results.
+ * @param[in,out] results Pointer to first of the (array of) locations to
+ * store results.
+ * @param[out] result_count Pointer to store the number of results which
+ * were returned.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t * user_ctx,
+ uint16_t result_size,
+ fsl_shw_result_t results[],
+ uint16_t * result_count);
+
+/*!
+ * Allocate a block of secure memory
+ *
+ * @param user_ctx User context
+ * @param size Memory size (octets). Note: currently only
+ * supports only single-partition sized blocks.
+ * @param UMID User Mode ID to use when registering the
+ * partition.
+ * @param permissions Permissions to initialize the partition with.
+ * Can be made by ORing flags from the
+ * #fsl_shw_permission_t.
+ *
+ * @return Address of the allocated memory. NULL if the
+ * call was not successful.
+ */
+extern void *fsl_shw_smalloc(fsl_shw_uco_t * user_ctx,
+ uint32_t size,
+ const uint8_t * UMID, uint32_t permissions);
+
+/*!
+ * Free a block of secure memory that was allocated with #fsl_shw_smalloc
+ *
+ * @param user_ctx User context
+ * @param address Address of the block of secure memory to be
+ * released.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_sfree(fsl_shw_uco_t * user_ctx, void *address);
+
+/*!
+ * Diminish the permissions of a block of secure memory. Note that permissions
+ * can only be revoked.
+ *
+ * @param user_ctx User context
+ * @param address Base address of the secure memory to work with
+ * @param permissions Permissions to initialize the partition with.
+ * Can be made by ORing flags from the
+ * #fsl_shw_permission_t.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_diminish_perms(fsl_shw_uco_t * user_ctx,
+ void *address,
+ uint32_t permissions);
+
+/*!
+ * @brief Encrypt a region of secure memory using the hardware secret key
+ *
+ * @param user_ctx User context
+ * @param partition_base Base address of the partition
+ * @param offset_bytes Offset of data from the partition base
+ * @param byte_count Length of the data to encrypt
+ * @param black_data Location to store the encrypted data
+ * @param IV IV to use for the encryption routine
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #fsl_shw_cypher_mode_t
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+do_scc_encrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode);
+
+/*!
+ * @brief Decrypt a region of secure memory using the hardware secret key
+ *
+ * @param user_ctx User context
+ * @param partition_base Base address of the partition
+ * @param offset_bytes Offset of data from the partition base
+ * @param byte_count Length of the data to encrypt
+ * @param black_data Location to store the encrypted data
+ * @param IV IV to use for the encryption routine
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #fsl_shw_cypher_mode_t
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+do_scc_decrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, const uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode);
+
+ /*! @} *//* miscfuns */
+
+/*! @addtogroup opfuns
+ @{ */
+
+/* REQ-S2LRD-PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+/*!
+ * Encrypt a stream of data with a symmetric-key algorithm.
+ *
+ * In ARC4, and also in #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_CTR modes, the
+ * flags of the @a sym_ctx object will control part of the operation of this
+ * function. The #FSL_SYM_CTX_INIT flag means that there is no context info in
+ * the object. The #FSL_SYM_CTX_LOAD means to use information in the
+ * @a sym_ctx at the start of the operation, and the #FSL_SYM_CTX_SAVE flag
+ * means to update the object's context information after the operation has
+ * been performed.
+ *
+ * All of the data for an operation can be run through at once using the
+ * #FSL_SYM_CTX_INIT or #FSL_SYM_CTX_LOAD flags, as appropriate, and then using
+ * a @a length for the whole of the data.
+ *
+ * If a #FSL_SYM_CTX_SAVE flag were added, an additional call to the function
+ * would "pick up" where the previous call left off, allowing the user to
+ * perform the larger function in smaller steps.
+ *
+ * In #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_ECB modes, the @a length must always
+ * be a multiple of the block size for the algorithm being used. For proper
+ * operation in #FSL_SYM_MODE_CTR mode, the @a length must be a multiple of the
+ * block size until the last operation on the total octet stream.
+ *
+ * Some users of ARC4 may want to compute the context (S-Box and pointers) from
+ * the key before any data is available. This may be done by running this
+ * function with a @a length of zero, with the init & save flags flags on in
+ * the @a sym_ctx. Subsequent operations would then run as normal with the
+ * load and save flags. Note that they key object is still required.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info Key and algorithm being used for this operation.
+ * @param[in,out] sym_ctx Info on cipher mode, state of the cipher.
+ * @param length Length, in octets, of the pt (and ct).
+ * @param pt pointer to plaintext to be encrypted.
+ * @param[out] ct pointer to where to store the resulting ciphertext.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ *
+ */
+extern fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt,
+ uint8_t * ct);
+
+/* PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+/*!
+ * Decrypt a stream of data with a symmetric-key algorithm.
+ *
+ * In ARC4, and also in #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_CTR modes, the
+ * flags of the @a sym_ctx object will control part of the operation of this
+ * function. The #FSL_SYM_CTX_INIT flag means that there is no context info in
+ * the object. The #FSL_SYM_CTX_LOAD means to use information in the
+ * @a sym_ctx at the start of the operation, and the #FSL_SYM_CTX_SAVE flag
+ * means to update the object's context information after the operation has
+ * been performed.
+ *
+ * All of the data for an operation can be run through at once using the
+ * #FSL_SYM_CTX_INIT or #FSL_SYM_CTX_LOAD flags, as appropriate, and then using
+ * a @a length for the whole of the data.
+ *
+ * If a #FSL_SYM_CTX_SAVE flag were added, an additional call to the function
+ * would "pick up" where the previous call left off, allowing the user to
+ * perform the larger function in smaller steps.
+ *
+ * In #FSL_SYM_MODE_CBC and #FSL_SYM_MODE_ECB modes, the @a length must always
+ * be a multiple of the block size for the algorithm being used. For proper
+ * operation in #FSL_SYM_MODE_CTR mode, the @a length must be a multiple of the
+ * block size until the last operation on the total octet stream.
+ *
+ * Some users of ARC4 may want to compute the context (S-Box and pointers) from
+ * the key before any data is available. This may be done by running this
+ * function with a @a length of zero, with the #FSL_SYM_CTX_INIT &
+ * #FSL_SYM_CTX_SAVE flags on in the @a sym_ctx. Subsequent operations would
+ * then run as normal with the load & save flags. Note that they key object is
+ * still required.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The key and algorithm being used in this operation.
+ * @param[in,out] sym_ctx Info on cipher mode, state of the cipher.
+ * @param length Length, in octets, of the ct (and pt).
+ * @param ct pointer to ciphertext to be decrypted.
+ * @param[out] pt pointer to where to store the resulting plaintext.
+ *
+ * @return A return code of type #fsl_shw_return_t
+ *
+ */
+extern fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct,
+ uint8_t * pt);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-005 */
+/*!
+ * Hash a stream of data with a cryptographic hash algorithm.
+ *
+ * The flags in the @a hash_ctx control the operation of this function.
+ *
+ * Hashing functions work on 64 octets of message at a time. Therefore, when
+ * any partial hashing of a long message is performed, the message @a length of
+ * each segment must be a multiple of 64. When ready to
+ * #FSL_HASH_FLAGS_FINALIZE the hash, the @a length may be any value.
+ *
+ * With the #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_FINALIZE flags on, a
+ * one-shot complete hash, including padding, will be performed. The @a length
+ * may be any value.
+ *
+ * The first octets of a data stream can be hashed by setting the
+ * #FSL_HASH_FLAGS_INIT and #FSL_HASH_FLAGS_SAVE flags. The @a length must be
+ * a multiple of 64.
+ *
+ * The flag #FSL_HASH_FLAGS_LOAD is used to load a context previously saved by
+ * #FSL_HASH_FLAGS_SAVE. The two in combination will allow a (multiple-of-64
+ * octets) 'middle sequence' of the data stream to be hashed with the
+ * beginning. The @a length must again be a multiple of 64.
+ *
+ * Since the flag #FSL_HASH_FLAGS_LOAD is used to load a context previously
+ * saved by #FSL_HASH_FLAGS_SAVE, the #FSL_HASH_FLAGS_LOAD and
+ * #FSL_HASH_FLAGS_FINALIZE flags, used together, can be used to finish the
+ * stream. The @a length may be any value.
+ *
+ * If the user program wants to do the padding for the hash, it can leave off
+ * the #FSL_HASH_FLAGS_FINALIZE flag. The @a length must then be a multiple of
+ * 64 octets.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] hash_ctx Hashing algorithm and state of the cipher.
+ * @param msg Pointer to the data to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result If not null, pointer to where to store the hash
+ * digest.
+ * @param result_len Number of octets to store in @a result.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
+ fsl_shw_hco_t * hash_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-001 */
+/*!
+ * Precompute the Key hashes for an HMAC operation.
+ *
+ * This function may be used to calculate the inner and outer precomputes,
+ * which are the hash contexts resulting from hashing the XORed key for the
+ * 'inner hash' and the 'outer hash', respectively, of the HMAC function.
+ *
+ * After execution of this function, the @a hmac_ctx will contain the
+ * precomputed inner and outer contexts, so that they may be used by
+ * #fsl_shw_hmac(). The flags of @a hmac_ctx will be updated with
+ * #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT to mark their presence. In addition, the
+ * #FSL_HMAC_FLAGS_INIT flag will be set.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The key being used in this operation. Key must be
+ * 1 to 64 octets long.
+ * @param[in,out] hmac_ctx The context which controls, by its flags and
+ * algorithm, the operation of this function.
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hmac_precompute(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-002 */
+/*!
+ * Continue, finalize, or one-shot an HMAC operation.
+ *
+ * There are a number of ways to use this function. The flags in the
+ * @a hmac_ctx object will determine what operations occur.
+ *
+ * If #FSL_HMAC_FLAGS_INIT is set, then the hash will be started either from
+ * the @a key_info, or from the precomputed inner hash value in the
+ * @a hmac_ctx, depending on the value of #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT.
+ *
+ * If, instead, #FSL_HMAC_FLAGS_LOAD is set, then the hash will be continued
+ * from the ongoing inner hash computation in the @a hmac_ctx.
+ *
+ * If #FSL_HMAC_FLAGS_FINALIZE are set, then the @a msg will be padded, hashed,
+ * the outer hash will be performed, and the @a result will be generated.
+ *
+ * If the #FSL_HMAC_FLAGS_SAVE flag is set, then the (ongoing or final) digest
+ * value will be stored in the ongoing inner hash computation field of the @a
+ * hmac_ctx.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info If #FSL_HMAC_FLAGS_INIT is set in the @a hmac_ctx,
+ * this is the key being used in this operation, and the
+ * IPAD. If #FSL_HMAC_FLAGS_INIT is set in the @a
+ * hmac_ctx and @a key_info is NULL, then
+ * #fsl_shw_hmac_precompute() has been used to populate
+ * the @a inner_precompute and @a outer_precompute
+ * contexts. If #FSL_HMAC_FLAGS_INIT is not set, this
+ * parameter is ignored.
+
+ * @param[in,out] hmac_ctx The context which controls, by its flags and
+ * algorithm, the operation of this function.
+ * @param msg Pointer to the message to be hashed.
+ * @param length Length, in octets, of the @a msg.
+ * @param[out] result Pointer, of @a result_len octets, to where to
+ * store the HMAC.
+ * @param result_len Length of @a result buffer.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_hmac(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-RNG-002 */
+/*!
+ * Get random data.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length The number of octets of @a data being requested.
+ * @param[out] data A pointer to a location of @a length octets to where
+ * random data will be returned.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-RNG-002 */
+/*!
+ * Add entropy to random number generator.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param length Number of bytes at @a data.
+ * @param data Entropy to add to random number generator.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data);
+
+/*!
+ * Perform Generation-Encryption by doing a Cipher and a Hash.
+ *
+ * Generate the authentication value @a auth_value as well as encrypt the @a
+ * payload into @a ct (the ciphertext). This is a one-shot function, so all of
+ * the @a auth_data and the total message @a payload must passed in one call.
+ * This also means that the flags in the @a auth_ctx must be #FSL_ACCO_CTX_INIT
+ * and #FSL_ACCO_CTX_FINALIZE.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param auth_ctx Controlling object for Authenticate-decrypt.
+ * @param cipher_key_info The key being used for the cipher part of this
+ * operation. In CCM mode, this key is used for
+ * both parts.
+ * @param auth_key_info The key being used for the authentication part
+ * of this operation. In CCM mode, this key is
+ * ignored and may be NULL.
+ * @param auth_data_length Length, in octets, of @a auth_data.
+ * @param auth_data Data to be authenticated but not encrypted.
+ * @param payload_length Length, in octets, of @a payload.
+ * @param payload Pointer to the plaintext to be encrypted.
+ * @param[out] ct Pointer to the where the encrypted @a payload
+ * will be stored. Must be @a payload_length
+ * octets long.
+ * @param[out] auth_value Pointer to where the generated authentication
+ * field will be stored. Must be as many octets as
+ * indicated by MAC length in the @a function_ctx.
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_gen_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * payload,
+ uint8_t * ct, uint8_t * auth_value);
+
+/*!
+ * Perform Authentication-Decryption in Cipher + Hash.
+ *
+ * This function will perform a one-shot decryption of a data stream as well as
+ * authenticate the authentication value. This is a one-shot function, so all
+ * of the @a auth_data and the total message @a payload must passed in one
+ * call. This also means that the flags in the @a auth_ctx must be
+ * #FSL_ACCO_CTX_INIT and #FSL_ACCO_CTX_FINALIZE.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param auth_ctx Controlling object for Authenticate-decrypt.
+ * @param cipher_key_info The key being used for the cipher part of this
+ * operation. In CCM mode, this key is used for
+ * both parts.
+ * @param auth_key_info The key being used for the authentication part
+ * of this operation. In CCM mode, this key is
+ * ignored and may be NULL.
+ * @param auth_data_length Length, in octets, of @a auth_data.
+ * @param auth_data Data to be authenticated but not decrypted.
+ * @param payload_length Length, in octets, of @a ct and @a pt.
+ * @param ct Pointer to the encrypted input stream.
+ * @param auth_value The (encrypted) authentication value which will
+ * be authenticated. This is the same data as the
+ * (output) @a auth_value argument to
+ * #fsl_shw_gen_encrypt().
+ * @param[out] payload Pointer to where the plaintext resulting from
+ * the decryption will be stored.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_auth_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * ct,
+ const uint8_t * auth_value,
+ uint8_t * payload);
+
+/*!
+ * Establish the key in a protected location, which can be the system keystore,
+ * user keystore, or (on platforms that support it) as a Platform Key.
+ *
+ * By default, keys initialized with #fsl_shw_sko_init() will be placed into
+ * the system keystore. The user can cause the key to be established in a
+ * user keystore by first calling #fsl_shw_sko_set_keystore() on the key.
+ * Normally, keys in the system keystore can only be used for hardware
+ * encrypt or decrypt operations, however if the #FSL_SKO_KEY_SW_KEY flag is
+ * applied using #fsl_shw_sko_set_flags(), the key will be established as a
+ * software key, which can then be read out using #fsl_shw_read_key().
+ *
+ * Keys initialized with #fsl_shw_sko_init_pf_key() are established as a
+ * Platform Key. Their use is covered in @ref di_sec.
+ *
+ * This function only needs to be used when unwrapping a key, setting up a key
+ * which could be wrapped with a later call to #fsl_shw_extract_key(), or
+ * setting up a key as a Platform Key. Normal cleartext keys can simply be
+ * placed into #fsl_shw_sko_t key objects with #fsl_shw_sko_set_key() and used
+ * directly.
+ *
+ * The maximum key size supported for wrapped/unwrapped keys is 32 octets.
+ * (This is the maximum reasonable key length on Sahara - 32 octets for an HMAC
+ * key based on SHA-256.) The key size is determined by the @a key_info. The
+ * expected length of @a key can be determined by
+ * #fsl_shw_sko_calculate_wrapped_size()
+ *
+ * The protected key will not be available for use until this operation
+ * successfully completes.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param[in,out] key_info The information about the key to be which will
+ * be established. In the create case, the key
+ * length must be set.
+ * @param establish_type How @a key will be interpreted to establish a
+ * key for use.
+ * @param key If @a establish_type is #FSL_KEY_WRAP_UNWRAP,
+ * this is the location of a wrapped key. If
+ * @a establish_type is #FSL_KEY_WRAP_CREATE, this
+ * parameter can be @a NULL. If @a establish_type
+ * is #FSL_KEY_WRAP_ACCEPT, this is the location
+ * of a plaintext key.
+ */
+extern fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key);
+
+/*!
+ * Read the key value from a key object.
+ *
+ * Only a key marked as a software key (#FSL_SKO_KEY_SW_KEY) can be read with
+ * this call. It has no effect on the status of the key store.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The referenced key.
+ * @param[out] key The location to store the key value.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * key);
+
+/*!
+ * Wrap a key and retrieve the wrapped value.
+ *
+ * A wrapped key is a key that has been cryptographically obscured. It is
+ * only able to be used with keys that have been established by
+ * #fsl_shw_establish_key().
+ *
+ * For keys established in the system or user keystore, this function will
+ * also release the key (see #fsl_shw_release_key()) so that it must be re-
+ * established before reuse. This function will not release keys that are
+ * established as a Platform Key, so a call to #fsl_shw_release_key() is
+ * necessary to release those keys.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ * @param[out] covered_key The location to store the wrapped key.
+ * (This size is based upon the maximum key size
+ * of 32 octets).
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key);
+
+/*!
+ * De-establish a key so that it can no longer be accessed.
+ *
+ * The key will need to be re-established before it can again be used.
+ *
+ * This feature is not available for all platforms, nor for all algorithms and
+ * modes.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ * @param key_info The information about the key to be deleted.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info);
+
+/*!
+ * Cause the hardware to create a new random key for use by the secure memory
+ * encryption hardware.
+ *
+ * Have the hardware use the secure hardware random number generator to load a
+ * new secret key into the system's Random Key register.
+ *
+ * @param user_ctx A user context from #fsl_shw_register_user().
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_gen_random_pf_key(fsl_shw_uco_t * user_ctx);
+
+/*!
+ * Retrieve the detected tamper event.
+ *
+ * Note that if more than one event was detected, this routine will only ever
+ * return one of them.
+ *
+ * @param[in] user_ctx A user context from #fsl_shw_register_user().
+ * @param[out] tamperp Location to store the tamper information.
+ * @param[out] timestampp Locate to store timestamp from hardwhare when
+ * an event was detected.
+ *
+ *
+ * @return A return code of type #fsl_shw_return_t (for instance, if the platform
+ * is not in a fail state.
+ */
+extern fsl_shw_return_t fsl_shw_read_tamper_event(fsl_shw_uco_t * user_ctx,
+ fsl_shw_tamper_t * tamperp,
+ uint64_t * timestampp);
+
+/*! @} *//* opfuns */
+
+/* Insert example code into the API documentation. */
+
+/*!
+ * @example apitest.c
+ */
+
+/*!
+ * @example sym.c
+ */
+
+/*!
+ * @example rand.c
+ */
+
+/*!
+ * @example hash.c
+ */
+
+/*!
+ * @example hmac1.c
+ */
+
+/*!
+ * @example hmac2.c
+ */
+
+/*!
+ * @example gen_encrypt.c
+ */
+
+/*!
+ * @example auth_decrypt.c
+ */
+
+/*!
+ * @example wrapped_key.c
+ */
+
+/*!
+ * @example smalloc.c
+ */
+
+/*!
+ * @example user_keystore.c
+ */
+
+/*!
+ * @example dryice.c
+ */
+
+#endif /* API_DOC */
+
+#endif /* FSL_SHW_H */
diff --git a/drivers/mxc/security/sahara2/include/fsl_shw_keystore.h b/drivers/mxc/security/sahara2/include/fsl_shw_keystore.h
new file mode 100644
index 000000000000..837d01006086
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/fsl_shw_keystore.h
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 FSL_SHW_KEYSTORE_H
+#define FSL_SHW_KEYSTORE_H
+
+/*!
+ * @file fsl_shw_keystore.h
+ *
+ * @brief Definition of the User Keystore API.
+ *
+ */
+
+/*! \page user_keystore User Keystore API
+ *
+ * Definition of the User Keystore API.
+ *
+ * On platforms with multiple partitions of Secure Memory, the Keystore Object
+ * (#fsl_shw_kso_t) is provided to allow users to manage a private keystore for
+ * use in software cryptographic routines. The user can define a custom set of
+ * methods for managing their keystore, or use a default keystore handler. The
+ * keystore is established by #fsl_shw_establish_keystore(), and released by
+ * #fsl_shw_release_keystore(). The intent of this design is to make the
+ * keystore implementation as flexible as possible.
+ *
+ * See @ref keystore_api for the generic keystore API, and @ref
+ * default_keystore for the default keystore implementation.
+ *
+ */
+
+/*!
+ * @defgroup keystore_api User Keystore API
+ *
+ * Keystore API
+ *
+ * These functions define the generic keystore API, which can be used in
+ * conjunction with a keystore implementation backend to support a user
+ * keystore.
+ */
+
+/*!
+ * @defgroup default_keystore Default Keystore Implementation
+ *
+ * Default Keystore Implementation
+ *
+ * These functions define the default keystore implementation, which is used
+ * for the system keystore and for user keystores initialized by
+ * #fsl_shw_init_keystore_default(). They can be used as-is or as a reference
+ * for creating a custom keystore handler. It uses an entire Secure Memory
+ * partition, divided in to equal slots of length #KEYSTORE_SLOT_SIZE. These
+ * functions are not intended to be used directly- all user interaction with
+ * the keystore should be through the @ref keystore_api and the Wrapped Key
+ * interface.
+ *
+ * The current implementation is designed to work with both SCC and SCC2.
+ * Differences between the two versions are noted below.
+ */
+
+/*! @addtogroup keystore_api
+ @{ */
+
+#ifndef KEYSTORE_SLOT_SIZE
+/*! Size of each key slot, in octets. This sets an upper bound on the size
+ * of a key that can placed in the keystore.
+ */
+#define KEYSTORE_SLOT_SIZE 32
+#endif
+
+/*!
+ * Initialize a Keystore Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It allows the user to associate a custom keystore interface by
+ * specifying the correct set of functions that will be used to perform actions
+ * on the keystore object. To use the default keystore handler, the function
+ * #fsl_shw_init_keystore_default() can be used instead.
+ *
+ * @param keystore The Keystore object to operate on.
+ * @param data_init Keystore initialization function. This function is
+ * responsible for initializing the keystore. A
+ * user-defined object can be assigned to the user_data
+ * pointer, and will be passed to any function acting on
+ * that keystore. It is called during
+ * #fsl_shw_establish_keystore().
+ * @param data_cleanup Keystore cleanup function. This function cleans up
+ * any data structures associated with the keyboard. It
+ * is called by #fsl_shw_release_keystore().
+ * @param slot_alloc Slot allocation function. This function allocates a
+ * key slot, potentially based on size and owner id. It
+ * is called by #fsl_shw_establish_key().
+ * @param slot_dealloc Slot deallocation function.
+ * @param slot_verify_access Function to verify that a given Owner ID
+ * credential matches the given slot.
+ * @param slot_get_address For SCC2: Get the virtual address (kernel or
+ * userspace) of the data stored in the slot.
+ * For SCC: Get the physical address of the data
+ * stored in the slot.
+ * @param slot_get_base For SCC2: Get the (virtual) base address of the
+ * partition that the slot is located on.
+ * For SCC: Not implemented.
+ * @param slot_get_offset For SCC2: Get the offset from the start of the
+ * partition that the slot data is located at (in
+ * octets)
+ * For SCC: Not implemented.
+ * @param slot_get_slot_size Get the size of the key slot, in octets.
+ */
+extern void fsl_shw_init_keystore(fsl_shw_kso_t * keystore,
+ fsl_shw_return_t(*data_init) (fsl_shw_uco_t *
+ user_ctx,
+ void
+ **user_data),
+ void (*data_cleanup) (fsl_shw_uco_t *
+ user_ctx,
+ void **user_data),
+ fsl_shw_return_t(*slot_alloc) (void
+ *user_data,
+ uint32_t size,
+ uint64_t
+ owner_id,
+ uint32_t *
+ slot),
+ fsl_shw_return_t(*slot_dealloc) (void
+ *user_data,
+ uint64_t
+ owner_id,
+ uint32_t
+ slot),
+ fsl_shw_return_t(*slot_verify_access) (void
+ *user_data,
+ uint64_t
+ owner_id,
+ uint32_t
+ slot),
+ void *(*slot_get_address) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_base) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_offset) (void *user_data,
+ uint32_t handle),
+ uint32_t(*slot_get_slot_size) (void
+ *user_data,
+ uint32_t
+ handle));
+
+/*!
+ * Initialize a Keystore Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the user keystore object up to use the default keystore
+ * handler. If a custom keystore handler is desired, the function
+ * #fsl_shw_init_keystore() can be used instead.
+ *
+ * @param keystore The Keystore object to operate on.
+ */
+extern void fsl_shw_init_keystore_default(fsl_shw_kso_t * keystore);
+
+/*!
+ * Establish a Keystore Object.
+ *
+ * This function establishes a keystore object that has been set up by a call
+ * to #fsl_shw_init_keystore(). It is a wrapper for the user-defined
+ * data_init() function, which is specified during keystore initialization.
+ *
+ * @param user_ctx The user context that this keystore should be attached
+ * to
+ * @param keystore The Keystore object to operate on.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t fsl_shw_establish_keystore(fsl_shw_uco_t * user_ctx,
+ fsl_shw_kso_t * keystore);
+
+/*!
+ * Release a Keystore Object.
+ *
+ * This function releases an established keystore object. It is a wrapper for
+ * the user-defined data_cleanup() function, which is specified during keystore
+ * initialization.
+ *
+ * @param user_ctx The user context that this keystore should be attached
+ * to.
+ * @param keystore The Keystore object to operate on.
+ */
+extern void fsl_shw_release_keystore(fsl_shw_uco_t * user_ctx,
+ fsl_shw_kso_t * keystore);
+
+/*!
+ * Allocate a slot in the Keystore.
+ *
+ * This function attempts to allocate a slot to hold a key in the keystore. It
+ * is called by #fsl_shw_establish_key() when establishing a Secure Key Object,
+ * if the key has been flagged to be stored in a user keystore by the
+ * #fsl_shw_sko_set_keystore() function. It is a wrapper for the
+ * implementation-specific function slot_alloc().
+ *
+ * @param keystore The Keystore object to operate on.
+ * @param[in] size Size of the key to be stored (octets).
+ * @param[in] owner_id ID of the key owner.
+ * @param[out] slot If successful, assigned slot ID
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t keystore_slot_alloc(fsl_shw_kso_t * keystore,
+ uint32_t size,
+ uint64_t owner_id, uint32_t * slot);
+
+/*!
+ * Deallocate a slot in the Keystore.
+ *
+ * This function attempts to allocate a slot to hold a key in the keystore.
+ * It is called by #fsl_shw_extract_key() and #fsl_shw_release_key() when the
+ * key that it contains is to be released. It is a wrapper for the
+ * implmentation-specific function slot_dealloc().
+
+ * @param keystore The Keystore object to operate on.
+ * @param[in] owner_id ID of the key owner.
+ * @param[in] slot If successful, assigned slot ID.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t keystore_slot_dealloc(fsl_shw_kso_t * keystore,
+ uint64_t owner_id, uint32_t slot);
+
+/*!
+ * Load cleartext key data into a key slot
+ *
+ * This function loads a key slot with cleartext data.
+ *
+ * @param keystore The Keystore object to operate on.
+ * @param[in] owner_id ID of the key owner.
+ * @param[in] slot If successful, assigned slot ID.
+ * @param[in] key_data Pointer to the location of the cleartext key data.
+ * @param[in] key_length Length of the key data (octets).
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+keystore_slot_load(fsl_shw_kso_t * keystore, uint64_t owner_id, uint32_t slot,
+ const uint8_t * key_data, uint32_t key_length);
+
+/*!
+ * Read cleartext key data from a key slot
+ *
+ * This function returns the key in a key slot.
+ *
+ * @param keystore The Keystore object to operate on.
+ * @param[in] owner_id ID of the key owner.
+ * @param[in] slot ID of slot where key resides.
+ * @param[in] key_length Length of the key data (octets).
+ * @param[out] key_data Pointer to the location of the cleartext key data.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+keystore_slot_read(fsl_shw_kso_t * keystore, uint64_t owner_id, uint32_t slot,
+ uint32_t key_length, uint8_t * key_data);
+
+/*!
+ * Encrypt a keyslot
+ *
+ * This function encrypts a key using the hardware secret key.
+ *
+ * @param user_ctx User context
+ * @param keystore The Keystore object to operate on.
+ * @param[in] owner_id ID of the key owner.
+ * @param[in] slot Slot ID of the key to encrypt.
+ * @param[in] length Length of the key
+ * @param[out] destination Pointer to the location where the encrypted data
+ * is to be stored.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+keystore_slot_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_kso_t * keystore, uint64_t owner_id,
+ uint32_t slot, uint32_t length, uint8_t * destination);
+
+/*!
+ * Decrypt a keyslot
+ *
+ * This function decrypts a key using the hardware secret key.
+ *
+ * @param user_ctx User context
+ * @param keystore The Keystore object to operate on.
+ * @param[in] owner_id ID of the key owner.
+ * @param[in] slot Slot ID of the key to encrypt.
+ * @param[in] length Length of the key
+ * @param[in] source Pointer to the location where the encrypted data
+ * is stored.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+extern fsl_shw_return_t
+keystore_slot_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_kso_t * keystore, uint64_t owner_id,
+ uint32_t slot, uint32_t length, const uint8_t * source);
+
+/* @} */
+
+/*! @addtogroup default_keystore
+ @{ */
+
+/*!
+ * Data structure to hold per-slot information
+ */
+typedef struct keystore_data_slot_info_t {
+ uint8_t allocated; /*!< Track slot assignments */
+ uint64_t owner; /*!< Owner IDs */
+ uint32_t key_length; /*!< Size of the key */
+} keystore_data_slot_info_t;
+
+/*!
+ * Data structure to hold keystore information.
+ */
+typedef struct keystore_data_t {
+ void *base_address; /*!< Base of the Secure Partition */
+ uint32_t slot_count; /*!< Number of slots in the keystore */
+ struct keystore_data_slot_info_t *slot; /*!< Per-slot information */
+} keystore_data_t;
+
+/*!
+ * Default keystore initialization routine.
+ *
+ * This function acquires a Secure Partition Object to store the keystore,
+ * divides it into slots of length #KEYSTORE_SLOT_SIZE, and builds a data
+ * structure to hold key information.
+ *
+ * @param user_ctx User context
+ * @param[out] user_data Pointer to the location where the keystore data
+ * structure is to be stored.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t shw_kso_init_data(fsl_shw_uco_t * user_ctx, void **user_data);
+
+/*!
+ * Default keystore cleanup routine.
+ *
+ * This function releases the Secure Partition Object and the memory holding
+ * the keystore data structure, that obtained by the shw_kso_init_data
+ * function.
+ *
+ * @param user_ctx User context
+ * @param[in,out] user_data Pointer to the location where the keystore data
+ * structure is stored.
+ */
+void shw_kso_cleanup_data(fsl_shw_uco_t * user_ctx, void **user_data);
+
+/*!
+ * Default keystore slot access verification
+ *
+ * This function compares the supplied Owner ID to the registered owner of
+ * the key slot, to see if the supplied ID is correct.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] owner_id Owner ID supplied as a credential.
+ * @param[in] slot Requested slot
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t shw_slot_verify_access(void *user_data, uint64_t owner_id,
+ uint32_t slot);
+
+/*!
+ * Default keystore slot allocation
+ *
+ * This function first checks that the requested size is equal to or less than
+ * the maximum keystore slot size. If so, it searches the keystore for a free
+ * key slot, and if found, marks it as used and returns a slot reference to the
+ * user.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] size Size of the key data that will be stored in this slot
+ * (octets)
+ * @param[in] owner_id Owner ID supplied as a credential.
+ * @param[out] slot Requested slot
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t shw_slot_alloc(void *user_data, uint32_t size,
+ uint64_t owner_id, uint32_t * slot);
+
+/*!
+ * Default keystore slot deallocation
+ *
+ * This function releases the given key slot in the keystore, making it
+ * available to store a new key.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] owner_id Owner ID supplied as a credential.
+ * @param[in] slot Requested slot
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t shw_slot_dealloc(void *user_data,
+ uint64_t owner_id, uint32_t slot);
+
+/*!
+ * Default keystore slot address lookup
+ *
+ * This function calculates the address where the key data is stored.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] slot Requested slot
+ *
+ * @return SCC2: Virtual address (kernel or userspace) of the key data.
+ * SCC: Physical address of the key data.
+ */
+void *shw_slot_get_address(void *user_data, uint32_t slot);
+
+/*!
+ * Default keystore slot base address lookup
+ *
+ * This function calculates the base address of the Secure Partition on which
+ * the key data is located. For the reference design, only one Secure
+ * Partition is used per Keystore, however in general, any number may be used.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] slot Requested slot
+ *
+ * @return SCC2: Secure Partition virtual (kernel or userspace) base address.
+ * SCC: Secure Partition physical base address.
+ */
+uint32_t shw_slot_get_base(void *user_data, uint32_t slot);
+
+/*!
+ * Default keystore slot offset lookup
+ *
+ * This function calculates the offset from the base of the Secure Partition
+ * where the key data is located.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] slot Requested slot
+ *
+ * @return SCC2: Key data offset (octets)
+ * SCC: Not implemented
+ */
+uint32_t shw_slot_get_offset(void *user_data, uint32_t slot);
+
+/*!
+ * Default keystore slot offset lookup
+ *
+ * This function returns the size of the given key slot. In the reference
+ * implementation, all key slots are of the same size, however in general,
+ * the keystore slot sizes can be made variable.
+ *
+ * @param[in] user_data Pointer to the location where the keystore data
+ * structure stored.
+ * @param[in] slot Requested slot
+ *
+ * @return SCC2: Keystore slot size.
+ * SCC: Not implemented
+ */
+uint32_t shw_slot_get_slot_size(void *user_data, uint32_t slot);
+
+/* @} */
+
+#endif /* FSL_SHW_KEYSTORE_H */
diff --git a/drivers/mxc/security/sahara2/include/linux_port.h b/drivers/mxc/security/sahara2/include/linux_port.h
new file mode 100644
index 000000000000..880658f3ad90
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/linux_port.h
@@ -0,0 +1,1804 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file linux_port.h
+ *
+ * OS_PORT ported to Linux (2.6.9+ for now)
+ *
+ */
+
+ /*!
+ * @if USE_MAINPAGE
+ * @mainpage ==Linux version of== Generic OS API for STC Drivers
+ * @endif
+ *
+ * @section intro_sec Introduction
+ *
+ * This API / kernel programming environment blah blah.
+ *
+ * See @ref dkops "Driver-to-Kernel Operations" as a good place to start.
+ */
+
+#ifndef LINUX_PORT_H
+#define LINUX_PORT_H
+
+#define PORTABLE_OS_VERSION 101
+
+/* Linux Kernel Includes */
+#include <linux/version.h> /* Current version Linux kernel */
+
+#if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+#include <linux/modversions.h>
+#endif
+#define MODVERSIONS
+#endif
+/*!
+ * __NO_VERSION__ defined due to Kernel module possibly spanning multiple
+ * files.
+ */
+#define __NO_VERSION__
+
+#include <linux/module.h> /* Basic support for loadable modules,
+ printk */
+#include <linux/init.h> /* module_init, module_exit */
+#include <linux/kernel.h> /* General kernel system calls */
+#include <linux/sched.h> /* for interrupt.h */
+#include <linux/fs.h> /* for inode */
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h> /* kmalloc */
+
+#include <stdarg.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+#include <linux/device.h> /* used in dynamic power management */
+#else
+#include <linux/platform_device.h> /* used in dynamic power management */
+#endif
+
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/clk.h> /* clock en/disable for DPM */
+
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/uaccess.h> /* copy_to_user(), copy_from_user() */
+#include <asm/io.h> /* ioremap() */
+#include <asm/irq.h>
+#include <asm/cacheflush.h>
+
+#ifndef TRUE
+/*! Useful symbol for unsigned values used as flags. */
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+/*! Useful symbol for unsigned values used as flags. */
+#define FALSE 0
+#endif
+
+/* These symbols are defined in Linux 2.6 and later. Include here for minimal
+ * support of 2.4 kernel.
+ **/
+#if !defined(LINUX_VERSION_CODE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+/*!
+ * Symbol defined somewhere in 2.5/2.6. It is the return signature of an ISR.
+ */
+#define irqreturn_t void
+/*! Possible return value of 'modern' ISR routine. */
+#define IRQ_HANDLED
+/*! Method of generating value of 'modern' ISR routine. */
+#define IRQ_RETVAL(x)
+#endif
+
+/*!
+ * Type used for registering and deregistering interrupts.
+ */
+typedef int os_interrupt_id_t;
+
+/*!
+ * Type used as handle for a process
+ *
+ * See #os_get_process_handle() and #os_send_signal().
+ */
+/*
+ * The following should be defined this way, but it gets compiler errors
+ * on the current tool chain.
+ *
+ * typedef task_t *os_process_handle_t;
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+typedef task_t *os_process_handle_t;
+#else
+typedef struct task_struct *os_process_handle_t;
+#endif
+
+/*!
+ * Generic return code for functions which need such a thing.
+ *
+ * No knowledge should be assumed of the value of any of these symbols except
+ * that @c OS_ERROR_OK_S is guaranteed to be zero.
+ */
+typedef enum {
+ OS_ERROR_OK_S = 0, /*!< Success */
+ OS_ERROR_FAIL_S = -EIO, /*!< Generic driver failure */
+ OS_ERROR_NO_MEMORY_S = -ENOMEM, /*!< Failure to acquire/use memory */
+ OS_ERROR_BAD_ADDRESS_S = -EFAULT, /*!< Bad address */
+ OS_ERROR_BAD_ARG_S = -EINVAL, /*!< Bad input argument */
+} os_error_code;
+
+/*!
+ * Handle to a lock.
+ */
+#ifdef CONFIG_PREEMPT_RT
+typedef raw_spinlock_t *os_lock_t;
+#else
+typedef spinlock_t *os_lock_t;
+#endif
+
+/*!
+ * Context while locking.
+ */
+typedef unsigned long os_lock_context_t;
+
+/*!
+ * Declare a wait object for sleeping/waking processes.
+ */
+#define OS_WAIT_OBJECT(name) \
+ DECLARE_WAIT_QUEUE_HEAD(name##_qh)
+
+/*!
+ * Driver registration handle
+ *
+ * Used with #os_driver_init_registration(), #os_driver_add_registration(),
+ * and #os_driver_complete_registration().
+ */
+typedef struct {
+ unsigned reg_complete; /*!< TRUE if next inits succeeded. */
+ dev_t dev; /*!< dev_t for register_chrdev() */
+ struct file_operations fops; /*!< struct for register_chrdev() */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+ struct class_simple *cs; /*!< results of class_simple_create() */
+#else
+ struct class *cs; /*!< results of class_create() */
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+ struct class_device *cd; /*!< Result of class_device_create() */
+#else
+ struct device *cd; /*!< Result of device_create() */
+#endif
+ unsigned power_complete; /*!< TRUE if next inits succeeded */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ struct device_driver dd; /*!< struct for register_driver() */
+#else
+ struct platform_driver dd; /*!< struct for register_driver() */
+#endif
+ struct platform_device pd; /*!< struct for platform_register_device() */
+} os_driver_reg_t;
+
+/*
+ * Function types which can be associated with driver entry points.
+ *
+ * Note that init and shutdown are absent.
+ */
+/*! @{ */
+/*! Keyword for registering open() operation handler. */
+#define OS_FN_OPEN open
+/*! Keyword for registering close() operation handler. */
+#define OS_FN_CLOSE release
+/*! Keyword for registering read() operation handler. */
+#define OS_FN_READ read
+/*! Keyword for registering write() operation handler. */
+#define OS_FN_WRITE write
+/*! Keyword for registering ioctl() operation handler. */
+#define OS_FN_IOCTL ioctl
+/*! Keyword for registering mmap() operation handler. */
+#define OS_FN_MMAP mmap
+/*! @} */
+
+/*!
+ * Function signature for the portable interrupt handler
+ *
+ * While it would be nice to know which interrupt is being serviced, the
+ * Least Common Denominator rule says that no arguments get passed in.
+ *
+ * @return Zero if not handled, non-zero if handled.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+typedef int (*os_interrupt_handler_t) (int, void *, struct pt_regs *);
+#else
+typedef int (*os_interrupt_handler_t) (int, void *);
+#endif
+
+/*!
+ * @defgroup dkops Driver-to-Kernel Operations
+ *
+ * These are the operations which drivers should call to get the OS to perform
+ * services.
+ */
+
+/*! @addtogroup dkops */
+/*! @{ */
+
+/*!
+ * Register an interrupt handler.
+ *
+ * @param driver_name The name of the driver
+ * @param interrupt_id The interrupt line to monitor (type
+ * #os_interrupt_id_t)
+ * @param function The function to be called to handle an interrupt
+ *
+ * @return #os_error_code
+ */
+#define os_register_interrupt(driver_name, interrupt_id, function) \
+ request_irq(interrupt_id, function, 0, driver_name, NULL)
+
+/*!
+ * Deregister an interrupt handler.
+ *
+ * @param interrupt_id The interrupt line to stop monitoring
+ *
+ * @return #os_error_code
+ */
+#define os_deregister_interrupt(interrupt_id) \
+ free_irq(interrupt_id, NULL)
+
+/*!
+ * INTERNAL implementation of os_driver_init_registration()
+ *
+ * @return An os error code.
+ */
+inline static int os_drv_do_init_reg(os_driver_reg_t * handle)
+{
+ memset(handle, 0, sizeof(*handle));
+ handle->fops.owner = THIS_MODULE;
+ handle->power_complete = FALSE;
+ handle->reg_complete = FALSE;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ handle->dd.name = NULL;
+#else
+ handle->dd.driver.name = NULL;
+#endif
+
+ return OS_ERROR_OK_S;
+}
+
+/*!
+ * Initialize driver registration.
+ *
+ * If the driver handles open(), close(), ioctl(), read(), write(), or mmap()
+ * calls, then it needs to register their location with the kernel so that they
+ * get associated with the device.
+ *
+ * @param handle The handle object to be used with this registration. The
+ * object must live (be in memory somewhere) at least until
+ * os_driver_remove_registration() is called.
+ *
+ * @return A handle for further driver registration, or NULL if failed.
+ */
+#define os_driver_init_registration(handle) \
+ os_drv_do_init_reg(&handle)
+
+/*!
+ * Add a function registration to driver registration.
+ *
+ * @param handle A handle initialized by #os_driver_init_registration().
+ * @param name Which function is being supported.
+ * @param function The result of a call to a @c _REF version of one of the
+ * driver function signature macros
+ * @return void
+ */
+#define os_driver_add_registration(handle, name, function) \
+ do {handle.fops.name = (void*)(function); } while (0)
+
+/*!
+ * Record 'power suspend' function for the device.
+ *
+ * @param handle A handle initialized by #os_driver_init_registration().
+ * @param function Name of function to call on power suspend request
+ *
+ * Status: Provisional
+ *
+ * @return void
+ */
+#define os_driver_register_power_suspend(handle, function) \
+ handle.dd.suspend = function
+
+/*!
+ * Record 'power resume' function for the device.
+ *
+ * @param handle A handle initialized by #os_driver_init_registration().
+ * @param function Name of function to call on power resume request
+ *
+ * Status: Provisional
+ *
+ * @return void
+ */
+#define os_driver_register_resume(handle, function) \
+ handle.dd.resume = function
+
+/*!
+ * INTERNAL function of the Linux port of the OS API. Implements the
+ * os_driver_complete_registration() function.
+ *
+ * @param handle The handle used with #os_driver_init_registration().
+ * @param major The major device number to be associated with the driver.
+ * If this value is zero, a major number may be assigned.
+ * See #os_driver_get_major() to determine final value.
+ * #os_driver_remove_registration().
+ * @param driver_name The driver name. Can be used as part of 'device node'
+ * name on platforms which support such a feature.
+ *
+ * @return An error code
+ */
+inline static int os_drv_do_reg(os_driver_reg_t * handle,
+ unsigned major, char *driver_name)
+{
+ os_error_code code = OS_ERROR_NO_MEMORY_S;
+ char *name = kmalloc(strlen(driver_name) + 1, 0);
+
+ if (name != NULL) {
+ memcpy(name, driver_name, strlen(driver_name) + 1);
+ code = OS_ERROR_OK_S; /* OK so far */
+ /* If any chardev/POSIX routines were added, then do chrdev part */
+ if (handle->fops.open || handle->fops.release
+ || handle->fops.read || handle->fops.write
+ || handle->fops.ioctl || handle->fops.mmap) {
+
+ printk("ioctl pointer: %p. mmap pointer: %p\n",
+ handle->fops.ioctl, handle->fops.mmap);
+
+ /* this method is depricated, see:
+ * http://lwn.net/Articles/126808/
+ */
+ code =
+ register_chrdev(major, driver_name, &handle->fops);
+
+ /* instead something like this: */
+#if 0
+ handle->dev = MKDEV(major, 0);
+ code =
+ register_chrdev_region(handle->dev, 1, driver_name);
+ if (code < 0) {
+ code = OS_ERROR_FAIL_S;
+ } else {
+ cdev_init(&handle->cdev, &handle->fops);
+ code = cdev_add(&handle->cdev, major, 1);
+ }
+#endif
+
+ if (code < 0) {
+ code = OS_ERROR_FAIL_S;
+ } else {
+ if (code != 0) {
+ /* Zero was passed in for major; code is actual value */
+ handle->dev = MKDEV(code, 0);
+ } else {
+ handle->dev = MKDEV(major, 0);
+ }
+ code = OS_ERROR_OK_S;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+ handle->cs =
+ class_simple_create(THIS_MODULE,
+ driver_name);
+ if (IS_ERR(handle->cs)) {
+ code = (os_error_code) handle->cs;
+ handle->cs = NULL;
+ } else {
+ handle->cd =
+ class_simple_device_add(handle->cs,
+ handle->dev,
+ NULL,
+ driver_name);
+ if (IS_ERR(handle->cd)) {
+ class_simple_device_remove
+ (handle->dev);
+ unregister_chrdev(MAJOR
+ (handle->dev),
+ driver_name);
+ code =
+ (os_error_code) handle->cs;
+ handle->cs = NULL;
+ } else {
+ handle->reg_complete = TRUE;
+ }
+ }
+#else
+ handle->cs =
+ class_create(THIS_MODULE, driver_name);
+ if (IS_ERR(handle->cs)) {
+ code = (os_error_code) handle->cs;
+ handle->cs = NULL;
+ } else {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+ handle->cd =
+ class_device_create(handle->cs,
+ NULL,
+ handle->dev,
+ NULL,
+ driver_name);
+#else
+ handle->cd =
+ device_create(handle->cs, NULL,
+ handle->dev, NULL,
+ driver_name);
+#endif
+ if (IS_ERR(handle->cd)) {
+ class_destroy(handle->cs);
+ unregister_chrdev(MAJOR
+ (handle->dev),
+ driver_name);
+ code =
+ (os_error_code) handle->cs;
+ handle->cs = NULL;
+ } else {
+ handle->reg_complete = TRUE;
+ }
+ }
+#endif
+ }
+ }
+ /* ... fops routine registered */
+ /* Handle power management fns through separate interface */
+ if ((code == OS_ERROR_OK_S) &&
+ (handle->dd.suspend || handle->dd.resume)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ handle->dd.name = name;
+ handle->dd.bus = &platform_bus_type;
+ code = driver_register(&handle->dd);
+#else
+ handle->dd.driver.name = name;
+ handle->dd.driver.bus = &platform_bus_type;
+ code = driver_register(&handle->dd.driver);
+#endif
+ if (code == OS_ERROR_OK_S) {
+ handle->pd.name = name;
+ handle->pd.id = 0;
+ code = platform_device_register(&handle->pd);
+ if (code != OS_ERROR_OK_S) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ driver_unregister(&handle->dd);
+#else
+ driver_unregister(&handle->dd.driver);
+#endif
+ } else {
+ handle->power_complete = TRUE;
+ }
+ }
+ } /* ... suspend or resume */
+ } /* name != NULL */
+ return code;
+}
+
+/*!
+ * Finalize the driver registration with the kernel.
+ *
+ * Upon return from this call, the driver may begin receiving calls at the
+ * defined entry points.
+ *
+ * @param handle The handle used with #os_driver_init_registration().
+ * @param major The major device number to be associated with the driver.
+ * If this value is zero, a major number may be assigned.
+ * See #os_driver_get_major() to determine final value.
+ * #os_driver_remove_registration().
+ * @param driver_name The driver name. Can be used as part of 'device node'
+ * name on platforms which support such a feature.
+ *
+ * @return An error code
+ */
+#define os_driver_complete_registration(handle, major, driver_name) \
+ os_drv_do_reg(&handle, major, driver_name)
+
+/*!
+ * Get driver Major Number from handle after a successful registration.
+ *
+ * @param handle A handle which has completed registration.
+ *
+ * @return The major number (if any) associated with the handle.
+ */
+#define os_driver_get_major(handle) \
+ (handle.reg_complete ? MAJOR(handle.dev) : -1)
+
+/*!
+ * INTERNAL implemention of os_driver_remove_registration.
+ *
+ * @param handle A handle initialized by #os_driver_init_registration().
+ *
+ * @return An error code.
+ */
+inline static int os_drv_rmv_reg(os_driver_reg_t * handle)
+{
+ if (handle->reg_complete) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+ if (handle->cd != NULL) {
+ class_simple_device_remove(handle->dev);
+ handle->cd = NULL;
+ }
+ if (handle->cs != NULL) {
+ class_simple_destroy(handle->cs);
+ handle->cs = NULL;
+ }
+ unregister_chrdev(MAJOR(handle->dev), handle->dd.name);
+#else
+ if (handle->cd != NULL) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+ class_device_destroy(handle->cs, handle->dev);
+#else
+ device_destroy(handle->cs, handle->dev);
+#endif
+ handle->cd = NULL;
+ }
+ if (handle->cs != NULL) {
+ class_destroy(handle->cs);
+ handle->cs = NULL;
+ }
+ unregister_chrdev(MAJOR(handle->dev), handle->dd.driver.name);
+#endif
+ handle->reg_complete = FALSE;
+ }
+ if (handle->power_complete) {
+ platform_device_unregister(&handle->pd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ driver_unregister(&handle->dd);
+#else
+ driver_unregister(&handle->dd.driver);
+#endif
+ handle->power_complete = FALSE;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ if (handle->dd.name != NULL) {
+ kfree(handle->dd.name);
+ handle->dd.name = NULL;
+ }
+#else
+ if (handle->dd.driver.name != NULL) {
+ kfree(handle->dd.driver.name);
+ handle->dd.driver.name = NULL;
+ }
+#endif
+ return OS_ERROR_OK_S;
+}
+
+/*!
+ * Remove the driver's registration with the kernel.
+ *
+ * Upon return from this call, the driver not receive any more calls at the
+ * defined entry points (other than ISR and shutdown).
+ *
+ * @param handle A handle initialized by #os_driver_init_registration().
+ *
+ * @return An error code.
+ */
+#define os_driver_remove_registration(handle) \
+ os_drv_rmv_reg(&handle)
+
+/*!
+ * Register a driver with the Linux Device Model.
+ *
+ * @param driver_information The device_driver structure information
+ *
+ * @return An error code.
+ *
+ * Status: denigrated in favor of #os_driver_complete_registration()
+ */
+#define os_register_to_driver(driver_information) \
+ driver_register(driver_information)
+
+/*!
+ * Unregister a driver from the Linux Device Model
+ *
+ * this routine unregisters from the Linux Device Model
+ *
+ * @param driver_information The device_driver structure information
+ *
+ * @return An error code.
+ *
+ * Status: Denigrated. See #os_register_to_driver().
+ */
+#define os_unregister_from_driver(driver_information) \
+ driver_unregister(driver_information)
+
+/*!
+ * register a device to a driver
+ *
+ * this routine registers a drivers devices to the Linux Device Model
+ *
+ * @param device_information The platform_device structure information
+ *
+ * @return An error code.
+ *
+ * Status: denigrated in favor of #os_driver_complete_registration()
+ */
+#define os_register_a_device(device_information) \
+ platform_device_register(device_information)
+
+/*!
+ * unregister a device from a driver
+ *
+ * this routine unregisters a drivers devices from the Linux Device Model
+ *
+ * @param device_information The platform_device structure information
+ *
+ * @return An error code.
+ *
+ * Status: Denigrated. See #os_register_a_device().
+ */
+#define os_unregister_a_device(device_information) \
+ platform_device_unregister(device_information)
+
+/*!
+ * Print a message to console / into log file. After the @c msg argument a
+ * number of printf-style arguments may be added. Types should be limited to
+ * printf string, char, octal, decimal, and hexadecimal types. (This excludes
+ * pointers, and floating point).
+ *
+ * @param msg The main text of the message to be logged
+ * @param s The printf-style arguments which go with msg, if any
+ *
+ * @return (void)
+ */
+#define os_printk(...) \
+ (void) printk(__VA_ARGS__)
+
+/*!
+ * Prepare a task to execute the given function. This should only be done once
+ * per function,, during the driver's initialization routine.
+ *
+ * @param task_fn Name of the OS_DEV_TASK() function to be created.
+ *
+ * @return an OS ERROR code.
+ */
+#define os_create_task(function_name) \
+ OS_ERROR_OK_S
+
+/*!
+ * Schedule execution of a task.
+ *
+ * @param function_name The function associated with the task.
+ *
+ * @return (void)
+ */
+#define os_dev_schedule_task(function_name) \
+ tasklet_schedule(&(function_name ## let))
+
+/*!
+ * Make sure that task is no longer running and will no longer run.
+ *
+ * This function will not return until both are true. This is useful when
+ * shutting down a driver.
+ */
+#define os_dev_stop_task(function_name) \
+do { \
+ tasklet_disable(&(function_name ## let)); \
+ tasklet_kill(&(function_name ## let)); \
+} while (0)
+
+/*!
+ * Allocate some kernel memory
+ *
+ * @param amount Number of 8-bit bytes to allocate
+ * @param flags Some indication of purpose of memory (needs definition)
+ *
+ * @return Pointer to allocated memory, or NULL if failed.
+ */
+#define os_alloc_memory(amount, flags) \
+ (void*)kmalloc(amount, flags)
+
+/*!
+ * Free some kernel memory
+ *
+ * @param location The beginning of the region to be freed.
+ *
+ * Do some OSes have separate free() functions which should be
+ * distinguished by passing in @c flags here, too? Don't some also require the
+ * size of the buffer being freed?
+ */
+#define os_free_memory(location) \
+ kfree(location)
+
+/*!
+ * Allocate cache-coherent memory
+ *
+ * @param amount Number of bytes to allocate
+ * @param[out] dma_addrp Location to store physical address of allocated
+ * memory.
+ * @param flags Some indication of purpose of memory (needs
+ * definition).
+ *
+ * @return (virtual space) pointer to allocated memory, or NULL if failed.
+ *
+ */
+#define os_alloc_coherent(amount, dma_addrp, flags) \
+ (void*)dma_alloc_coherent(NULL, amount, dma_addrp, flags)
+
+/*!
+ * Free cache-coherent memory
+ *
+ * @param size Number of bytes which were allocated.
+ * @param virt_addr Virtual(kernel) address of memory.to be freed, as
+ * returned by #os_alloc_coherent().
+ * @param dma_addr Physical address of memory.to be freed, as returned
+ * by #os_alloc_coherent().
+ *
+ * @return void
+ *
+ */
+#define os_free_coherent(size, virt_addr, dma_addr) \
+ dma_free_coherent(NULL, size, virt_addr, dma_addr
+
+/*!
+ * Map an I/O space into kernel memory space
+ *
+ * @param start The starting address of the (physical / io space) region
+ * @param range_bytes The number of bytes to map
+ *
+ * @return A pointer to the mapped area, or NULL on failure
+ */
+#define os_map_device(start, range_bytes) \
+ (void*)ioremap_nocache((start), range_bytes)
+
+/*!
+ * Unmap an I/O space from kernel memory space
+ *
+ * @param start The starting address of the (virtual) region
+ * @param range_bytes The number of bytes to unmap
+ *
+ * @return None
+ */
+#define os_unmap_device(start, range_bytes) \
+ iounmap((void*)(start))
+
+/*!
+ * Copy data from Kernel space to User space
+ *
+ * @param to The target location in user memory
+ * @param from The source location in kernel memory
+ * @param size The number of bytes to be copied
+ *
+ * @return #os_error_code
+ */
+#define os_copy_to_user(to, from, size) \
+ ((copy_to_user(to, from, size) == 0) ? 0 : OS_ERROR_BAD_ADDRESS_S)
+
+/*!
+ * Copy data from User space to Kernel space
+ *
+ * @param to The target location in kernel memory
+ * @param from The source location in user memory
+ * @param size The number of bytes to be copied
+ *
+ * @return #os_error_code
+ */
+#define os_copy_from_user(to, from, size) \
+ ((copy_from_user(to, from, size) == 0) ? 0 : OS_ERROR_BAD_ADDRESS_S)
+
+/*!
+ * Read a 8-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+#define os_read8(register_address) \
+ __raw_readb(register_address)
+
+/*!
+ * Write a 8-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+#define os_write8(register_address, value) \
+ __raw_writeb(value, register_address)
+
+/*!
+ * Read a 16-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+#define os_read16(register_address) \
+ __raw_readw(register_address)
+
+/*!
+ * Write a 16-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+#define os_write16(register_address, value) \
+ __raw_writew(value, (uint32_t*)(register_address))
+
+/*!
+ * Read a 32-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+#define os_read32(register_address) \
+ __raw_readl((uint32_t*)(register_address))
+
+/*!
+ * Write a 32-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+#define os_write32(register_address, value) \
+ __raw_writel(value, register_address)
+
+/*!
+ * Read a 64-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+#define os_read64(register_address) \
+ ERROR_UNIMPLEMENTED
+
+/*!
+ * Write a 64-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+#define os_write64(register_address, value) \
+ ERROR_UNIMPLEMENTED
+
+/*!
+ * Delay some number of microseconds
+ *
+ * Note that this is a busy-loop, not a suspension of the task/process.
+ *
+ * @param msecs The number of microseconds to delay
+ *
+ * @return void
+ */
+#define os_mdelay mdelay
+
+/*!
+ * Calculate virtual address from physical address
+ *
+ * @param pa Physical address
+ *
+ * @return virtual address
+ *
+ * @note this assumes that addresses are 32 bits wide
+ */
+#define os_va __va
+
+/*!
+ * Calculate physical address from virtual address
+ *
+ *
+ * @param va Virtual address
+ *
+ * @return physical address
+ *
+ * @note this assumes that addresses are 32 bits wide
+ */
+#define os_pa __pa
+
+#ifdef CONFIG_PREEMPT_RT
+/*!
+ * Allocate and initialize a lock, returning a lock handle.
+ *
+ * The lock state will be initialized to 'unlocked'.
+ *
+ * @return A lock handle, or NULL if an error occurred.
+ */
+inline static os_lock_t os_lock_alloc_init(void)
+{
+ raw_spinlock_t *lockp;
+ lockp = (raw_spinlock_t *) kmalloc(sizeof(raw_spinlock_t), 0);
+ if (lockp) {
+ _raw_spin_lock_init(lockp);
+ } else {
+ printk("OS: lock init failed\n");
+ }
+
+ return lockp;
+}
+#else
+/*!
+ * Allocate and initialize a lock, returning a lock handle.
+ *
+ * The lock state will be initialized to 'unlocked'.
+ *
+ * @return A lock handle, or NULL if an error occurred.
+ */
+inline static os_lock_t os_lock_alloc_init(void)
+{
+ spinlock_t *lockp;
+ lockp = (spinlock_t *) kmalloc(sizeof(spinlock_t), 0);
+ if (lockp) {
+ spin_lock_init(lockp);
+ } else {
+ printk("OS: lock init failed\n");
+ }
+
+ return lockp;
+}
+#endif /* CONFIG_PREEMPT_RT */
+
+/*!
+ * Acquire a lock.
+ *
+ * This function should only be called from an interrupt service routine.
+ *
+ * @param lock_handle A handle to the lock to acquire.
+ *
+ * @return void
+ */
+#define os_lock(lock_handle) \
+ spin_lock(lock_handle)
+
+/*!
+ * Unlock a lock. Lock must have been acquired by #os_lock().
+ *
+ * @param lock_handle A handle to the lock to unlock.
+ *
+ * @return void
+ */
+#define os_unlock(lock_handle) \
+ spin_unlock(lock_handle)
+
+/*!
+ * Acquire a lock in non-ISR context
+ *
+ * This function will spin until the lock is available.
+ *
+ * @param lock_handle A handle of the lock to acquire.
+ * @param context Place to save the before-lock context
+ *
+ * @return void
+ */
+#define os_lock_save_context(lock_handle, context) \
+ spin_lock_irqsave(lock_handle, context)
+
+/*!
+ * Release a lock in non-ISR context
+ *
+ * @param lock_handle A handle of the lock to release.
+ * @param context Place where before-lock context was saved.
+ *
+ * @return void
+ */
+#define os_unlock_restore_context(lock_handle, context) \
+ spin_unlock_irqrestore(lock_handle, context)
+
+/*!
+ * Deallocate a lock handle.
+ *
+ * @param lock_handle An #os_lock_t that has been allocated.
+ *
+ * @return void
+ */
+#define os_lock_deallocate(lock_handle) \
+ kfree(lock_handle)
+
+/*!
+ * Determine process handle
+ *
+ * The process handle of the current user is returned.
+ *
+ * @return A handle on the current process.
+ */
+#define os_get_process_handle() \
+ current
+
+/*!
+ * Send a signal to a process
+ *
+ * @param proc A handle to the target process.
+ * @param sig The POSIX signal to send to that process.
+ */
+#define os_send_signal(proc, sig) \
+ send_sig(sig, proc, 0);
+
+/*!
+ * Get some random bytes
+ *
+ * @param buf The location to store the random data.
+ * @param count The number of bytes to store.
+ *
+ * @return void
+ */
+#define os_get_random_bytes(buf, count) \
+ get_random_bytes(buf, count)
+
+/*!
+ * Go to sleep on an object.
+ *
+ * @param object The object on which to sleep
+ * @param condition An expression to check for sleep completion. Must be
+ * coded so that it can be referenced more than once inside
+ * macro, i.e., no ++ or other modifying expressions.
+ * @param atomic Non-zero if sleep must not return until condition.
+ *
+ * @return error code -- OK or sleep interrupted??
+ */
+#define os_sleep(object, condition, atomic) \
+({ \
+ DEFINE_WAIT(_waitentry_); \
+ os_error_code code = OS_ERROR_OK_S; \
+ \
+ while (!(condition)) { \
+ prepare_to_wait(&(object##_qh), &_waitentry_, \
+ atomic ? 0 : TASK_INTERRUPTIBLE); \
+ if (!(condition)) { \
+ schedule(); \
+ } \
+ \
+ finish_wait(&(object##_qh), &_waitentry_); \
+ \
+ if (!atomic && signal_pending(current)) { \
+ code = OS_ERROR_FAIL_S; /* NEED SOMETHING BETTER */ \
+ break; \
+ } \
+ }; \
+ \
+ code; \
+})
+
+/*!
+ * Wake up whatever is sleeping on sleep object
+ *
+ * @param object The object on which things might be sleeping
+ *
+ * @return none
+ */
+#define os_wake_sleepers(object) \
+ wake_up_interruptible(&(object##_qh));
+
+ /*! @} *//* dkops */
+
+/******************************************************************************
+ * Function signature-generating macros
+ *****************************************************************************/
+
+/*!
+ * @defgroup drsigs Driver Signatures
+ *
+ * These macros will define the entry point signatures for interrupt handlers;
+ * driver initialization and shutdown; device open/close; etc.
+ *
+ * There are two versions of each macro for a given Driver Entry Point. The
+ * first version is used to define a function and its implementation in the
+ * driver.c file, e.g. #OS_DEV_INIT().
+ *
+ * The second form is used whenever a forward declaration (prototype) is
+ * needed. It has the letters @c _DCL appended to the name of the defintion
+ * function, and takes only the first two arguments (driver_name and
+ * function_name). These are not otherwise mentioned in this documenation.
+ *
+ * There is a third form used when a reference to a function is required, for
+ * instance when passing the routine as a pointer to a function. It has the
+ * letters @c _REF appended to it, and takes only the first two arguments
+ * (driver_name and function_name). These functions are not otherwise
+ * mentioned in this documentation.
+ *
+ * (Note that these two extra forms are required because of the
+ * possibility/likelihood of having a 'wrapper function' which invokes the
+ * generic function with expected arguments. An alternative would be to have a
+ * generic function which isn't able to get at any arguments directly, but
+ * would be equipped with macros which could get at information passed in.
+ *
+ * Example:
+ *
+ * (in a header file)
+ * @code
+ * OS_DEV_INIT_DCL(widget, widget_init);
+ * @endcode
+ *
+ * (in an implementation file)
+ * @code
+ * OS_DEV_INIT(widget, widget_init)
+ * {
+ * os_dev_init_return(TRUE);
+ * }
+ * @endcode
+ *
+ */
+
+/*! @addtogroup drsigs */
+/*! @{ */
+
+/*!
+ * Define a function which will handle device initialization
+ *
+ * This is tne driver initialization routine. This is normally where the
+ * part would be initialized; queues, locks, interrupts handlers defined;
+ * long-term dynamic memory allocated for driver use; etc.
+ *
+ * @param function_name The name of the portable initialization function.
+ *
+ * @return A call to #os_dev_init_return()
+ *
+ */
+#define OS_DEV_INIT(function_name) \
+module_init(function_name); \
+static int __init function_name (void)
+
+/*! Make declaration for driver init function.
+ * @param function_name foo
+ */
+#define OS_DEV_INIT_DCL(function_name) \
+static int __init function_name (void);
+
+/*!
+ * Generate a function reference to the driver's init function.
+ * @param function_name Name of the OS_DEV_INIT() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_INIT_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will handle device shutdown
+ *
+ * This is the inverse of the #OS_DEV_INIT() routine.
+ *
+ * @param function_name The name of the portable driver shutdown routine.
+ *
+ * @return A call to #os_dev_shutdown_return()
+ *
+ */
+#define OS_DEV_SHUTDOWN(function_name) \
+module_exit(function_name); \
+static void function_name(void)
+
+/*!
+ * Generate a function reference to the driver's shutdown function.
+ * @param function_name Name of the OS_DEV_HUSTDOWN() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_SHUTDOWN_DCL(function_name) \
+static void function_name(void);
+
+/*!
+ * Generate a reference to driver's shutdown function
+ * @param function_name Name of the OS_DEV_HUSTDOWN() function.
+*/
+
+#define OS_DEV_SHUTDOWN_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will open the device for a user.
+ *
+ * @param function_name The name of the driver open() function
+ *
+ * @return A call to #os_dev_open_return()
+ */
+#define OS_DEV_OPEN(function_name) \
+static int function_name(struct inode* inode_p_, struct file* file_p_)
+
+/*!
+ * Declare prototype for an open() function.
+ *
+ * @param function_name The name of the OS_DEV_OPEN() function.
+ */
+#define OS_DEV_OPEN_DCL(function_name) \
+OS_DEV_OPEN(function_name);
+
+/*!
+ * Generate a function reference to the driver's open() function.
+ * @param function_name Name of the OS_DEV_OPEN() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_OPEN_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will handle a user's ioctl() request
+ *
+ * @param function_name The name of the driver ioctl() function
+ *
+ * @return A call to #os_dev_ioctl_return()
+ */
+#define OS_DEV_IOCTL(function_name) \
+static int function_name(struct inode* inode_p_, struct file* file_p_, \
+ unsigned int cmd_, unsigned long data_)
+
+/*! Boo. */
+#define OS_DEV_IOCTL_DCL(function_name) \
+OS_DEV_IOCTL(function_name);
+
+/*!
+ * Generate a function reference to the driver's ioctl() function.
+ * @param function_name Name of the OS_DEV_IOCTL() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_IOCTL_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will handle a user's mmap() request
+ *
+ * @param function_name The name of the driver mmap() function
+ *
+ * @return A call to #os_dev_ioctl_return()
+ */
+#define OS_DEV_MMAP(function_name) \
+int function_name(struct file* file_p_, struct vm_area_struct* vma_)
+
+#define OS_DEV_MMAP_DCL(function_name) \
+OS_DEV_MMAP(function_name);
+
+#define OS_DEV_MMAP_REF(function_name) \
+function_name
+
+/* Retrieve the context to the memory structure that is to be MMAPed */
+#define os_mmap_memory_ctx() (vma_)
+
+/* Determine the size of the requested MMAP region*/
+#define os_mmap_memory_size() (vma_->vm_end - vma_->vm_start)
+
+/* Determine the base address of the requested MMAP region*/
+#define os_mmap_user_base() (vma_->vm_start)
+
+/*!
+ * Declare prototype for an read() function.
+ *
+ * @param function_name The name of the driver read function.
+ */
+#define OS_DEV_READ_DCL(function_name) \
+OS_DEV_READ(function_name);
+
+/*!
+ * Generate a function reference to the driver's read() routine
+ * @param function_name Name of the OS_DEV_READ() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_READ_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will handle a user's write() request
+ *
+ * @param function_name The name of the driver write() function
+ *
+ * @return A call to #os_dev_write_return()
+ */
+#define OS_DEV_WRITE(function_name) \
+static ssize_t function_name(struct file* file_p_, char* user_buffer_, \
+ size_t count_bytes_, loff_t* file_position_)
+
+/*!
+ * Declare prototype for an write() function.
+ *
+ * @param function_name The name of the driver write function.
+ */
+#define OS_DEV_WRITE_DCL(function_name) \
+OS_DEV_WRITE(function_name);
+
+/*!
+ * Generate a function reference to the driver's write() routine
+ * @param function_name Name of the OS_DEV_WRITE() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_WRITE_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will close the device - opposite of OS_DEV_OPEN()
+ *
+ * @param function_name The name of the driver close() function
+ *
+ * @return A call to #os_dev_close_return()
+ */
+#define OS_DEV_CLOSE(function_name) \
+static int function_name(struct inode* inode_p_, struct file* file_p_)
+
+/*!
+ * Declare prototype for an close() function
+ *
+ * @param function_name The name of the driver close() function.
+ */
+#define OS_DEV_CLOSE_DCL(function_name) \
+OS_DEV_CLOSE(function_name);
+
+/*!
+ * Generate a function reference to the driver's close function.
+ * @param function_name Name of the OS_DEV_CLOSE() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_CLOSE_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will handle an interrupt
+ *
+ * No arguments are available to the generic function. It must not invoke any
+ * OS functions which are illegal in a ISR. It gets no parameters, and must
+ * have a call to #os_dev_isr_return() instead of any/all return statements.
+ *
+ * Example:
+ * @code
+ * OS_DEV_ISR(widget)
+ * {
+ * os_dev_isr_return(1);
+ * }
+ * @endcode
+ *
+ * @param function_name The name of the driver ISR function
+ *
+ * @return A call to #os_dev_isr_return()
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#define OS_DEV_ISR(function_name) \
+static irqreturn_t function_name(int N1_, void* N2_, struct pt_regs* N3_)
+#else
+#define OS_DEV_ISR(function_name) \
+static irqreturn_t function_name(int N1_, void* N2_)
+#endif
+
+/*!
+ * Declare prototype for an ISR function.
+ *
+ * @param function_name The name of the driver ISR function.
+ */
+#define OS_DEV_ISR_DCL(function_name) \
+OS_DEV_ISR(function_name);
+
+/*!
+ * Generate a function reference to the driver's interrupt service routine
+ * @param function_name Name of the OS_DEV_ISR() function.
+ *
+ * @return A function pointer.
+ */
+#define OS_DEV_ISR_REF(function_name) \
+function_name
+
+/*!
+ * Define a function which will operate as a background task / bottom half.
+ *
+ * Tasklet stuff isn't strictly limited to 'Device drivers', but leave it
+ * this namespace anyway.
+ *
+ * @param function_name The name of this background task function
+ *
+ * @return A call to #os_dev_task_return()
+ */
+#define OS_DEV_TASK(function_name) \
+static void function_name(unsigned long data_)
+
+/*!
+ * Declare prototype for a background task / bottom half function
+ *
+ * @param function_name The name of this background task function
+ */
+#define OS_DEV_TASK_DCL(function_name) \
+OS_DEV_TASK(function_name); \
+DECLARE_TASKLET(function_name ## let, function_name, 0);
+
+/*!
+ * Generate a reference to an #OS_DEV_TASK() function
+ *
+ * @param function_name The name of the task being referenced.
+ */
+#define OS_DEV_TASK_REF(function_name) \
+ (function_name ## let)
+
+ /*! @} *//* drsigs */
+
+/*****************************************************************************
+ * Functions/Macros for returning values from Driver Signature routines
+ *****************************************************************************/
+
+/*!
+ * Return from the #OS_DEV_INIT() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_init_return(code) \
+ return code
+
+/*!
+ * Return from the #OS_DEV_SHUTDOWN() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_shutdown_return(code) \
+ return
+
+/*!
+ * Return from the #OS_DEV_ISR() function
+ *
+ * The function should verify that it really was supposed to be called,
+ * and that its device needed attention, in order to properly set the
+ * return code.
+ *
+ * @param code non-zero if interrupt handled, zero otherwise.
+ *
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+#define os_dev_isr_return(code) \
+do { \
+ /* Unused warnings */ \
+ (void)N1_; \
+ (void)N2_; \
+ (void)N3_; \
+ \
+ return IRQ_RETVAL(code); \
+} while (0)
+#else
+#define os_dev_isr_return(code) \
+do { \
+ /* Unused warnings */ \
+ (void)N1_; \
+ (void)N2_; \
+ \
+ return IRQ_RETVAL(code); \
+} while (0)
+#endif
+
+/*!
+ * Return from the #OS_DEV_OPEN() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_open_return(code) \
+do { \
+ int retcode = code; \
+ \
+ /* get rid of 'unused parameter' warnings */ \
+ (void)inode_p_; \
+ (void)file_p_; \
+ \
+ return retcode; \
+} while (0)
+
+/*!
+ * Return from the #OS_DEV_IOCTL() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_ioctl_return(code) \
+do { \
+ int retcode = code; \
+ \
+ /* get rid of 'unused parameter' warnings */ \
+ (void)inode_p_; \
+ (void)file_p_; \
+ (void)cmd_; \
+ (void)data_; \
+ \
+ return retcode; \
+} while (0)
+
+/*!
+ * Return from the #OS_DEV_READ() function
+ *
+ * @param code Number of bytes read, or an error code to report failure.
+ *
+ */
+#define os_dev_read_return(code) \
+do { \
+ ssize_t retcode = code; \
+ \
+ /* get rid of 'unused parameter' warnings */ \
+ (void)file_p_; \
+ (void)user_buffer_; \
+ (void)count_bytes_; \
+ (void)file_position_; \
+ \
+ return retcode; \
+} while (0)
+
+/*!
+ * Return from the #OS_DEV_WRITE() function
+ *
+ * @param code Number of bytes written, or an error code to report failure.
+ *
+ */
+#define os_dev_write_return(code) \
+do { \
+ ssize_t retcode = code; \
+ \
+ /* get rid of 'unused parameter' warnings */ \
+ (void)file_p_; \
+ (void)user_buffer_; \
+ (void)count_bytes_; \
+ (void)file_position_; \
+ \
+ return retcode; \
+} while (0)
+
+/*!
+ * Return from the #OS_DEV_CLOSE() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_close_return(code) \
+do { \
+ ssize_t retcode = code; \
+ \
+ /* get rid of 'unused parameter' warnings */ \
+ (void)inode_p_; \
+ (void)file_p_; \
+ \
+ return retcode; \
+} while (0)
+
+/*!
+ * Start the #OS_DEV_TASK() function
+ *
+ * In some implementations, this could be turned into a label for
+ * the os_dev_task_return() call.
+ *
+ * @return none
+ */
+#define os_dev_task_begin()
+
+/*!
+ * Return from the #OS_DEV_TASK() function
+ *
+ * In some implementations, this could be turned into a sleep followed
+ * by a jump back to the os_dev_task_begin() call.
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+#define os_dev_task_return(code) \
+do { \
+ /* Unused warnings */ \
+ (void)data_; \
+ \
+ return; \
+} while (0)
+
+/*****************************************************************************
+ * Functions/Macros for accessing arguments from Driver Signature routines
+ *****************************************************************************/
+
+/*! @defgroup drsigargs Functions for Getting Arguments in Signature functions
+ *
+ */
+/* @addtogroup @drsigargs */
+/*! @{ */
+/*!
+ * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
+ * #OS_DEV_WRITE() routines to check whether user is requesting read
+ * (permission)
+ */
+#define os_dev_is_flag_read() \
+ (file_p_->f_mode & FMODE_READ)
+
+/*!
+ * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
+ * #OS_DEV_WRITE() routines to check whether user is requesting write
+ * (permission)
+ */
+#define os_dev_is_flag_write() \
+ (file_p_->f_mode & FMODE_WRITE)
+
+/*!
+ * Used in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
+ * #OS_DEV_WRITE() routines to check whether user is requesting non-blocking
+ * I/O.
+ */
+#define os_dev_is_flag_nonblock() \
+ (file_p_->f_flags & (O_NONBLOCK | O_NDELAY))
+
+/*!
+ * Used in #OS_DEV_OPEN() and #OS_DEV_CLOSE() to determine major device being
+ * accessed.
+ */
+#define os_dev_get_major() \
+ (imajor(inode_p_))
+
+/*!
+ * Used in #OS_DEV_OPEN() and #OS_DEV_CLOSE() to determine minor device being
+ * accessed.
+ */
+#define os_dev_get_minor() \
+ (iminor(inode_p_))
+
+/*!
+ * Used in #OS_DEV_IOCTL() to determine which operation the user wants
+ * performed.
+ *
+ * @return Value of the operation.
+ */
+#define os_dev_get_ioctl_op() \
+ (cmd_)
+
+/*!
+ * Used in #OS_DEV_IOCTL() to return the associated argument for the desired
+ * operation.
+ *
+ * @return A value which can be cast to a struct pointer or used as
+ * int/long.
+ */
+#define os_dev_get_ioctl_arg() \
+ (data_)
+
+/*!
+ * Used in OS_DEV_READ() and OS_DEV_WRITE() routines to access the requested
+ * byte count.
+ *
+ * @return (unsigned) a count of bytes
+ */
+#define os_dev_get_count() \
+ ((unsigned)count_bytes_)
+
+/*!
+ * Used in OS_DEV_READ() and OS_DEV_WRITE() routines to return the pointer
+ * byte count.
+ *
+ * @return char* pointer to user buffer
+ */
+#define os_dev_get_user_buffer() \
+ ((void*)user_buffer_)
+
+/*!
+ * Used in OS_DEV_READ(), OS_DEV_WRITE(), and OS_DEV_IOCTL() routines to
+ * get the POSIX flags field for the associated open file).
+ *
+ * @return The flags associated with the file.
+ */
+#define os_dev_get_file_flags() \
+ (file_p_->f_flags)
+
+/*!
+ * Set the driver's private structure associated with this file/open.
+ *
+ * Generally used during #OS_DEV_OPEN(). See #os_dev_get_user_private().
+ *
+ * @param struct_p The driver data structure to associate with this user.
+ */
+#define os_dev_set_user_private(struct_p) \
+ file_p_->private_data = (void*)(struct_p)
+
+/*!
+ * Get the driver's private structure associated with this file.
+ *
+ * May be used during #OS_DEV_OPEN(), #OS_DEV_READ(), #OS_DEV_WRITE(),
+ * #OS_DEV_IOCTL(), and #OS_DEV_CLOSE(). See #os_dev_set_user_private().
+ *
+ * @return The driver data structure to associate with this user.
+ */
+#define os_dev_get_user_private() \
+ ((void*)file_p_->private_data)
+
+/*!
+ * Get the IRQ associated with this call to the #OS_DEV_ISR() function.
+ *
+ * @return The IRQ (integer) interrupt number.
+ */
+#define os_dev_get_irq() \
+ N1_
+
+ /*! @} *//* drsigargs */
+
+/*!
+ * @defgroup cacheops Cache Operations
+ *
+ * These functions are for synchronizing processor cache with RAM.
+ */
+/*! @addtogroup cacheops */
+/*! @{ */
+
+/*!
+ * Flush and invalidate all cache lines.
+ */
+#if 0
+#define os_flush_cache_all() \
+ flush_cache_all()
+#else
+/* Call ARM fn directly, in case L2cache=on3 not set */
+#define os_flush_cache_all() \
+ v6_flush_kern_cache_all_L2()
+
+/*!
+ * ARM-routine to flush all cache. Defined here, because it exists in no
+ * easy-access header file. ARM-11 with L210 cache only!
+ */
+extern void v6_flush_kern_cache_all_L2(void);
+#endif
+
+/*
+ * These macros are using part of the Linux DMA API. They rely on the
+ * map function to do nothing more than the equivalent clean/inv/flush
+ * operation at the time of the mapping, and do nothing at an unmapping
+ * call, which the Sahara driver code will never invoke.
+ */
+
+/*!
+ * Clean a range of addresses from the cache. That is, write updates back
+ * to (RAM, next layer).
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ *
+ * @return void
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define os_cache_clean_range(start,len) \
+ dma_map_single(NULL, (void*)start, len, DMA_TO_DEVICE)
+#else
+#define os_cache_clean_range(start,len) \
+{ \
+ void *s = (void*)start; \
+ void *e = s + len; \
+ dmac_clean_range(s, e); \
+ outer_clean_range(__pa(s), __pa(e)); \
+}
+#endif
+
+/*!
+ * Invalidate a range of addresses in the cache
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ *
+ * @return void
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define os_cache_inv_range(start,len) \
+ dma_map_single(NULL, (void*)start, len, DMA_FROM_DEVICE)
+#else
+#define os_cache_inv_range(start,len) \
+{ \
+ void *s = (void*)start; \
+ void *e = s + len; \
+ dmac_inv_range(s, e); \
+ outer_inv_range(__pa(s), __pa(e)); \
+}
+#endif
+
+/*!
+ * Flush a range of addresses from the cache. That is, perform clean
+ * and invalidate
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ *
+ * @return void
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
+#define os_cache_flush_range(start,len) \
+ dma_map_single(NULL, (void*)start, len, DMA_BIDIRECTIONAL)
+#else
+#define os_cache_flush_range(start,len) \
+{ \
+ void *s = (void*)start; \
+ void *e = s + len; \
+ dmac_flush_range(s, e); \
+ outer_flush_range(__pa(s), __pa(e)); \
+}
+#endif
+
+ /*! @} *//* cacheops */
+
+#endif /* LINUX_PORT_H */
diff --git a/drivers/mxc/security/sahara2/include/platform_abstractions.h b/drivers/mxc/security/sahara2/include/platform_abstractions.h
new file mode 100644
index 000000000000..c2f9c489eb0b
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/platform_abstractions.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @file platform_abstractions.h
+ */
diff --git a/drivers/mxc/security/sahara2/include/portable_os.h b/drivers/mxc/security/sahara2/include/portable_os.h
new file mode 100644
index 000000000000..e904b3222cbb
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/portable_os.h
@@ -0,0 +1,1453 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 PORTABLE_OS_H
+#define PORTABLE_OS_H
+
+/***************************************************************************/
+
+/*
+ * Add support for your target OS by checking appropriate flags and then
+ * including the appropriate file. Don't forget to document the conditions
+ * in the later documentation section at the beginning of the
+ * DOXYGEN_PORTABLE_OS_DOC.
+ */
+
+#if defined(LINUX_KERNEL)
+
+#include "linux_port.h"
+
+#elif defined(PORTABLE_OS)
+
+#include "check_portability.h"
+
+#else
+
+#error Target OS unsupported or unspecified
+
+#endif
+
+
+/***************************************************************************/
+
+/*!
+ * @file portable_os.h
+ *
+ * This file should be included by portable driver code in order to gain access
+ * to the OS-specific header files. It is the only OS-related header file that
+ * the writer of a portable driver should need.
+ *
+ * This file also contains the documentation for the common API.
+ *
+ * Begin reading the documentation for this file at the @ref index "main page".
+ *
+ */
+
+/*!
+ * @if USE_MAINPAGE
+ * @mainpage Generic OS API for STC Drivers
+ * @endif
+ *
+ * @section intro_sec Introduction
+ *
+ * This defines the API / kernel programming environment for portable drivers.
+ *
+ * This API is broken up into several functional areas. It greatly limits the
+ * choices of a device-driver author, but at the same time should allow for
+ * greater portability of the resulting code.
+ *
+ * Each kernel-to-driver function (initialization function, interrupt service
+ * routine, etc.) has a 'portable signature' which must be used, and a specific
+ * function which must be called to generate the return statement. There is
+ * one exception, a background task or "bottom half" routine, which instead has
+ * a specific structure which must be followed. These signatures and function
+ * definitions are found in @ref drsigs.
+ *
+ * None of these kernel-to-driver functions seem to get any arguments passed to
+ * them. Instead, there are @ref drsigargs which allow one of these functions
+ * to get at fairly generic parts of its calling arguments, if there are any.
+ *
+ * Almost every driver will have some need to call the operating system
+ * @ref dkops is the list of services which are available to the driver.
+ *
+ *
+ * @subsection warn_sec Warning
+ *
+ * The specifics of the types, values of the various enumerations
+ * (unless specifically stated, like = 0), etc., are only here for illustrative
+ * purposes. No attempts should be made to make use of any specific knowledge
+ * gleaned from this documentation. These types are only meant to be passed in
+ * and out of the API, and their contents are to be handled only by the
+ * provided OS-specific functions.
+ *
+ * Also, note that the function may be provided as macros in some
+ * implementations, or vice versa.
+ *
+ *
+ * @section dev_sec Writing a Portable Driver
+ *
+ * First off, writing a portable driver means calling no function in an OS
+ * except what is available through this header file.
+ *
+ * Secondly, all OS-called functions in your driver must be invoked and
+ * referenced through the signature routines.
+ *
+ * Thirdly, there might be some rules which you can get away with ignoring or
+ * violating on one OS, yet will cause your code not to be portable to a
+ * different OS.
+ *
+ *
+ * @section limit_sec Limitations
+ *
+ * This API is not expected to handle every type of driver which may be found
+ * in an operating system. For example, it will not be able to handle the
+ * usual design for something like a UART driver, where there are multiple
+ * logical devices to access, because the design usually calls for some sort of
+ * indication to the #OS_DEV_TASK() function or OS_DEV_ISR() to indicate which
+ * channel is to be serviced by that instance of the task/function. This sort
+ * of argument is missing in this API for functions like os_dev_schedule_task() and
+ * os_register_interrupt().
+ *
+ *
+ * @section port_guide Porting Guidelines
+ *
+ * This section is intended for a developer who needs to port the header file
+ * to an operating system which is not yet supported.
+ *
+ * This interface allows for a lot of flexibility when it comes to porting to
+ * an operating systems device driver interface. There are three main areas to
+ * examine: The use of Driver Routine Signatures, the use of Driver Argument
+ * Access functions, the Calls to Kernel Functions, and Data Types.
+ *
+ *
+ * @subsection port_sig Porting Driver Routine Signatures
+ *
+ * The three macros for each function (e.g. #OS_DEV_INIT(), #OS_DEV_INIT_DCL(),
+ * and #OS_DEV_INIT_REF()) allow the flexibility of having a 'wrapper' function
+ * with the OS-required signature, which would then call the user's function
+ * with some different signature.
+ *
+ * The first form would lay down the wrapper function, followed by the
+ * signature for the user function. The second form would lay down just the
+ * signatures for both functions, and the last function would reference the
+ * wrapper function, since that is the interface function called by the OS.
+ *
+ * Note that the driver author has no visibility at all to the signature of the
+ * routines. The author can access arguments only through a limited set of
+ * functions, and must return via another function.
+ *
+ * The Return Functions allow a lot of flexibility in converting the return
+ * value, or not returning a value at all. These will likely be implemented as
+ * macros.
+ *
+ *
+ * @subsection port_arg Porting Driver Argument Access Functions
+ *
+ * The signatures defined by the guide will usually be replaced with macro
+ * definitions.
+ *
+ *
+ * @subsection port_dki Porting Calls to Kernel Functions
+ *
+ * The signatures defined by the guide may be replaced with macro definitions,
+ * if that makes more sense.
+ *
+ * Implementors are free to ignore arguments which are not applicable to their
+ * OS.
+ *
+ * @subsection port_datatypes Porting Data Types
+ *
+ *
+ */
+
+/***************************************************************************
+ * Compile flags
+ **************************************************************************/
+
+/*
+ * This compile flag should only be turned on when running doxygen to generate
+ * the API documentation.
+ */
+#ifdef DOXYGEN_PORTABLE_OS_DOC
+
+/*!
+ * @todo module_init()/module_cleanup() for Linux need to be added to OS
+ * abstractions. Also need EXPORT_SYMBOL() equivalent??
+ *
+ */
+
+/* Drop OS differentation documentation here */
+
+/*!
+ * \#define this flag to build your driver as a Linux driver
+ */
+#define LINUX
+
+/* end OS differentation documentation */
+
+/*!
+ * Symbol to give version number of the implementation file. Earliest
+ * definition is in version 1.1, with value 101 (to mean version 1.1)
+ */
+#define PORTABLE_OS_VERSION 101
+
+/*
+ * NOTICE: The following definitions (the rest of the file) are not meant ever
+ * to be compiled. Instead, they are the documentation for the portable OS
+ * API, to be used by driver writers.
+ *
+ * Each individual OS port will define each of these types, functions, or
+ * macros as appropriate to the target OS. This is why they are under the
+ * DOXYGEN_PORTABLE_OS_DOC flag.
+ */
+
+/***************************************************************************
+ * Type definitions
+ **************************************************************************/
+
+/*!
+ * Type used for registering and deregistering interrupts.
+ *
+ * This is typically an interrupt channel number.
+ */
+typedef int os_interrupt_id_t;
+
+/*!
+ * Type used as handle for a process
+ *
+ * See #os_get_process_handle() and #os_send_signal().
+ */
+typedef int os_process_handle_t;
+
+/*!
+ * Generic return code for functions which need such a thing.
+ *
+ * No knowledge should be assumed of the value of any of these symbols except
+ * that @c OS_ERROR_OK_S is guaranteed to be zero.
+ *
+ * @todo Any other named values? What about (-EAGAIN? -ERESTARTSYS? Are they
+ * too Linux/Unix-specific read()/write() return values) ?
+ */
+typedef enum {
+ OS_ERROR_OK_S = 0, /*!< Success */
+ OS_ERROR_FAIL_S, /*!< Generic driver failure */
+ OS_ERROR_NO_MEMORY_S, /*!< Failure to acquire/use memory */
+ OS_ERROR_BAD_ADDRESS_S, /*!< Bad address */
+ OS_ERROR_BAD_ARG_S /*!< Bad input argument */
+} os_error_code;
+
+/*!
+ * Handle to a lock.
+ */
+typedef int *os_lock_t;
+
+/*!
+ * Context while locking.
+ */
+typedef int os_lock_context_t;
+
+/*!
+ * An object which can be slept on and later used to wake any/all sleepers.
+ */
+typedef int os_sleep_object_t;
+
+/*!
+ * Driver registration handle
+ */
+typedef void *os_driver_reg_t;
+
+/*!
+ * Function signature for an #OS_DEV_INIT() function.
+ *
+ * @return A call to os_dev_init_return() function.
+ */
+typedef void (*os_init_function_t) (void);
+
+/*!
+ * Function signature for an #OS_DEV_SHUTDOWN() function.
+ *
+ * @return A call to os_dev_shutdown_return() function.
+ */
+typedef void (*os_shutdown_function_t) (void);
+
+/*!
+ * Function signature for a user-driver function.
+ *
+ * @return A call to the appropriate os_dev_xxx_return() function.
+ */
+typedef void (*os_user_function_t) (void);
+
+/*!
+ * Function signature for the portable interrupt handler
+ *
+ * While it would be nice to know which interrupt is being serviced, the
+ * Least Common Denominator rule says that no arguments get passed in.
+ *
+ * @return A call to #os_dev_isr_return()
+ */
+typedef void (*os_interrupt_handler_t) (void);
+
+/*!
+ * Function signature for a task function
+ *
+ * Many task function definitions get some sort of generic argument so that the
+ * same function can run across many (queues, channels, ...) as separate task
+ * instances. This has been left out of this API.
+ *
+ * This function must be structured as documented by #OS_DEV_TASK().
+ *
+ */
+typedef void (*os_task_fn_t) (void);
+
+/*!
+ * Function types which can be associated with driver entry points. These are
+ * used in os_driver_add_registration().
+ *
+ * Note that init and shutdown are absent.
+ */
+typedef enum {
+ OS_FN_OPEN, /*!< open() operation handler. */
+ OS_FN_CLOSE, /*!< close() operation handler. */
+ OS_FN_READ, /*!< read() operation handler. */
+ OS_FN_WRITE, /*!< write() operation handler. */
+ OS_FN_IOCTL, /*!< ioctl() operation handler. */
+ OS_FN_MMAP /*!< mmap() operation handler. */
+} os_driver_fn_t;
+
+/***************************************************************************
+ * Driver-to-Kernel Operations
+ **************************************************************************/
+
+/*!
+ * @defgroup dkops Driver-to-Kernel Operations
+ *
+ * These are the operations which drivers should call to get the OS to perform
+ * services.
+ */
+
+/*! @addtogroup dkops */
+/*! @{ */
+
+/*!
+ * Register an interrupt handler.
+ *
+ * @param driver_name The name of the driver
+ * @param interrupt_id The interrupt line to monitor (type
+ * #os_interrupt_id_t)
+ * @param function The function to be called to handle an interrupt
+ *
+ * @return #os_error_code
+ */
+os_error_code os_register_interrupt(char *driver_name,
+ os_interrupt_id_t interrupt_id,
+ os_interrupt_handler_t function);
+
+/*!
+ * Deregister an interrupt handler.
+ *
+ * @param interrupt_id The interrupt line to stop monitoring
+ *
+ * @return #os_error_code
+ */
+os_error_code os_deregister_interrupt(os_interrupt_id_t interrupt_id);
+
+/*!
+ * Initialize driver registration.
+ *
+ * If the driver handles open(), close(), ioctl(), read(), write(), or mmap()
+ * calls, then it needs to register their location with the kernel so that they
+ * get associated with the device.
+ *
+ * @param handle The handle object to be used with this registration. The
+ * object must live (be in memory somewhere) at least until
+ * os_driver_remove_registration() is called.
+ *
+ * @return An os error code.
+ */
+os_error_code os_driver_init_registration(os_driver_reg_t handle);
+
+/*!
+ * Add a function registration to driver registration.
+ *
+ * @param handle The handle used with #os_driver_init_registration().
+ * @param name Which function is being supported.
+ * @param function The result of a call to a @c _REF version of one of the
+ * driver function signature macros
+ * driver function signature macros
+ * @return void
+ */
+void os_driver_add_registration(os_driver_reg_t handle, os_driver_fn_t name,
+ void *function);
+
+/*!
+ * Finalize the driver registration with the kernel.
+ *
+ * Upon return from this call, the driver may begin receiving calls at the
+ * defined entry points.
+ *
+ * @param handle The handle used with #os_driver_init_registration().
+ * @param major The major device number to be associated with the driver.
+ * If this value is zero, a major number may be assigned.
+ * See #os_driver_get_major() to determine final value.
+ * #os_driver_remove_registration().
+ * @param driver_name The driver name. Can be used as part of 'device node'
+ * name on platforms which support such a feature.
+ *
+ * @return An error code
+ */
+os_error_code os_driver_complete_registration(os_driver_reg_t handle,
+ int major, char *driver_name);
+
+/*!
+ * Get driver Major Number from handle after a successful registration.
+ *
+ * @param handle A handle which has completed registration.
+ *
+ * @return The major number (if any) associated with the handle.
+ */
+uint32_t os_driver_get_major(os_driver_reg_t handle);
+
+/*!
+ * Remove the driver's registration with the kernel.
+ *
+ * Upon return from this call, the driver not receive any more calls at the
+ * defined entry points (other than ISR and shutdown).
+ *
+ * @param major The major device number to be associated with the driver.
+ * @param driver_name The driver name
+ *
+ * @return An error code.
+ */
+os_error_code os_driver_remove_registration(int major, char *driver_name);
+
+/*!
+ * Print a message to console / into log file. After the @c msg argument a
+ * number of printf-style arguments may be added. Types should be limited to
+ * printf string, char, octal, decimal, and hexadecimal types. (This excludes
+ * pointers, and floating point).
+ *
+ * @param msg The message to print to console / system log
+ *
+ * @return (void)
+ */
+void os_printk(char *msg, ...);
+
+/*!
+ * Allocate some kernel memory
+ *
+ * @param amount Number of 8-bit bytes to allocate
+ * @param flags Some indication of purpose of memory (needs definition)
+ *
+ * @return Pointer to allocated memory, or NULL if failed.
+ */
+void *os_alloc_memory(unsigned amount, int flags);
+
+/*!
+ * Free some kernel memory
+ *
+ * @param location The beginning of the region to be freed.
+ *
+ * Do some OSes have separate free() functions which should be
+ * distinguished by passing in @c flags here, too? Don't some also require the
+ * size of the buffer being freed? Perhaps separate routines for each
+ * alloc/free pair (DMAable, etc.)?
+ */
+void os_free_memory(void *location);
+
+/*!
+ * Allocate cache-coherent memory
+ *
+ * @param amount Number of bytes to allocate
+ * @param[out] dma_addrp Location to store physical address of allocated
+ * memory.
+ * @param flags Some indication of purpose of memory (needs
+ * definition).
+ *
+ * @return (virtual space) pointer to allocated memory, or NULL if failed.
+ *
+ */
+void *os_alloc_coherent(unsigned amount, uint32_t * dma_addrp, int flags);
+
+/*!
+ * Free cache-coherent memory
+ *
+ * @param size Number of bytes which were allocated.
+ * @param[out] virt_addr Virtual(kernel) address of memory.to be freed, as
+ * returned by #os_alloc_coherent().
+ * @param[out] dma_addr Physical address of memory.to be freed, as returned
+ * by #os_alloc_coherent().
+ *
+ * @return void
+ *
+ */
+void os_free_coherent(unsigned size, void *virt_addr, uint32_t dma_addr);
+
+/*!
+ * Map an I/O space into kernel memory space
+ *
+ * @param start The starting address of the (physical / io space) region
+ * @param range_bytes The number of bytes to map
+ *
+ * @return A pointer to the mapped area, or NULL on failure
+ */
+void *os_map_device(uint32_t start, unsigned range_bytes);
+
+/*!
+ * Unmap an I/O space from kernel memory space
+ *
+ * @param start The starting address of the (virtual) region
+ * @param range_bytes The number of bytes to unmap
+ *
+ * @return None
+ */
+void os_unmap_device(void *start, unsigned range_bytes);
+
+/*!
+ * Copy data from Kernel space to User space
+ *
+ * @param to The target location in user memory
+ * @param from The source location in kernel memory
+ * @param size The number of bytes to be copied
+ *
+ * @return #os_error_code
+ */
+os_error_code os_copy_to_user(void *to, void *from, unsigned size);
+
+/*!
+ * Copy data from User space to Kernel space
+ *
+ * @param to The target location in kernel memory
+ * @param from The source location in user memory
+ * @param size The number of bytes to be copied
+ *
+ * @return #os_error_code
+ */
+os_error_code os_copy_from_user(void *to, void *from, unsigned size);
+
+/*!
+ * Read an 8-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+uint8_t os_read8(uint8_t * register_address);
+
+/*!
+ * Write an 8-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+void os_write8(uint8_t * register_address, uint8_t value);
+
+/*!
+ * Read a 16-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+uint16_t os_read16(uint16_t * register_address);
+
+/*!
+ * Write a 16-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+void os_write16(uint16_t * register_address, uint16_t value);
+
+/*!
+ * Read a 32-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+uint32_t os_read32(uint32_t * register_address);
+
+/*!
+ * Write a 32-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+void os_write32(uint32_t * register_address, uint32_t value);
+
+/*!
+ * Read a 64-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @return The value in the register
+ */
+uint64_t os_read64(uint64_t * register_address);
+
+/*!
+ * Write a 64-bit device register
+ *
+ * @param register_address The (bus) address of the register to write to
+ * @param value The value to write into the register
+ */
+void os_write64(uint64_t * register_address, uint64_t value);
+
+/*!
+ * Prepare a task to execute the given function. This should only be done once
+ * per task, during the driver's initialization routine.
+ *
+ * @param task_fn Name of the OS_DEV_TASK() function to be created.
+ *
+ * @return an OS ERROR code.
+ */
+os_error os_create_task(os_task_fn_t * task_fn);
+
+/*!
+ * Run the task associated with an #OS_DEV_TASK() function
+ *
+ * The task will begin execution sometime after or during this call.
+ *
+ * @param task_fn Name of the OS_DEV_TASK() function to be scheduled.
+ *
+ * @return void
+ */
+void os_dev_schedule_task(os_task_fn_t * task_fn);
+
+/*!
+ * Make sure that task is no longer running and will no longer run.
+ *
+ * This function will not return until both are true. This is useful when
+ * shutting down a driver.
+ *
+ * @param task_fn Name of the OS_DEV_TASK() funciton to be stopped.
+ *
+ */
+void os_stop_task(os_task_fn_t * task_fn);
+
+/*!
+ * Delay some number of microseconds
+ *
+ * Note that this is a busy-loop, not a suspension of the task/process.
+ *
+ * @param msecs The number of microseconds to delay
+ *
+ * @return void
+ */
+void os_mdelay(unsigned long msecs);
+
+/*!
+ * Calculate virtual address from physical address
+ *
+ * @param pa Physical address
+ *
+ * @return virtual address
+ *
+ * @note this assumes that addresses are 32 bits wide
+ */
+void *os_va(uint32_t pa);
+
+/*!
+ * Calculate physical address from virtual address
+ *
+ *
+ * @param va Virtual address
+ *
+ * @return physical address
+ *
+ * @note this assumes that addresses are 32 bits wide
+ */
+uint32_t os_pa(void *va);
+
+/*!
+ * Allocate and initialize a lock, returning a lock handle.
+ *
+ * The lock state will be initialized to 'unlocked'.
+ *
+ * @return A lock handle, or NULL if an error occurred.
+ */
+os_lock_t os_lock_alloc_init(void);
+
+/*!
+ * Acquire a lock.
+ *
+ * This function should only be called from an interrupt service routine.
+ *
+ * @param lock_handle A handle to the lock to acquire.
+ *
+ * @return void
+ */
+void os_lock(os_lock_t lock_handle);
+
+/*!
+ * Unlock a lock. Lock must have been acquired by #os_lock().
+ *
+ * @param lock_handle A handle to the lock to unlock.
+ *
+ * @return void
+ */
+void os_unlock(os_lock_t lock_handle);
+
+/*!
+ * Acquire a lock in non-ISR context
+ *
+ * This function will spin until the lock is available.
+ *
+ * @param lock_handle A handle of the lock to acquire.
+ * @param context Place to save the before-lock context
+ *
+ * @return void
+ */
+void os_lock_save_context(os_lock_t lock_handle, os_lock_context_t context);
+
+/*!
+ * Release a lock in non-ISR context
+ *
+ * @param lock_handle A handle of the lock to release.
+ * @param context Place where before-lock context was saved.
+ *
+ * @return void
+ */
+void os_unlock_restore_context(os_lock_t lock_handle,
+ os_lock_context_t context);
+
+/*!
+ * Deallocate a lock handle.
+ *
+ * @param lock_handle An #os_lock_t that has been allocated.
+ *
+ * @return void
+ */
+void os_lock_deallocate(os_lock_t lock_handle);
+
+/*!
+ * Determine process handle
+ *
+ * The process handle of the current user is returned.
+ *
+ * @return A handle on the current process.
+ */
+os_process_handle_t os_get_process_handle();
+
+/*!
+ * Send a signal to a process
+ *
+ * @param proc A handle to the target process.
+ * @param sig The POSIX signal to send to that process.
+ */
+void os_send_signal(os_process_handle_t proc, int sig);
+
+/*!
+ * Get some random bytes
+ *
+ * @param buf The location to store the random data.
+ * @param count The number of bytes to store.
+ *
+ * @return void
+ */
+void os_get_random_bytes(void *buf, unsigned count);
+
+/*!
+ * Go to sleep on an object.
+ *
+ * Example: code = os_sleep(my_queue, available_count == 0, 0);
+ *
+ * @param object The object on which to sleep
+ * @param condition An expression to check for sleep completion. Must be
+ * coded so that it can be referenced more than once inside
+ * macro, i.e., no ++ or other modifying expressions.
+ * @param atomic Non-zero if sleep must not return until condition.
+ *
+ * @return error code -- OK or sleep interrupted??
+ */
+os_error_code os_sleep(os_sleep_object_t object, unsigned condition,
+ unsigned atomic);
+
+/*!
+ * Wake up whatever is sleeping on sleep object
+ *
+ * @param object The object on which things might be sleeping
+ *
+ * @return none
+ */
+void os_wake_sleepers(os_sleep_object_t object);
+
+ /*! @} *//* dkops */
+
+/*****************************************************************************
+ * Function-signature-generating macros
+ *****************************************************************************/
+
+/*!
+ * @defgroup drsigs Driver Function Signatures
+ *
+ * These macros will define the entry point signatures for interrupt handlers;
+ * driver initialization and shutdown; device open/close; etc. They are to be
+ * used whenever the Kernel will call into the Driver. They are not
+ * appropriate for driver calls to other routines in the driver.
+ *
+ * There are three versions of each macro for a given Driver Entry Point. The
+ * first version is used to define a function and its implementation in the
+ * driver.c file, e.g. #OS_DEV_INIT().
+ *
+ * The second form is used whenever a forward declaration (prototype) is
+ * needed. It has the letters @c _DCL appended to the name of the definition
+ * function. These are not otherwise mentioned in this documenation.
+ *
+ * There is a third form used when a reference to a function is required, for
+ * instance when passing the routine as a pointer to a function. It has the
+ * letters @c _REF appended to the name of the definition function
+ * (e.g. DEV_IOCTL_REF).
+ *
+ * Note that these two extra forms are required because of the possibility of
+ * having an invisible 'wrapper function' created by the os-specific header
+ * file which would need to be invoked by the operating system, and which in
+ * turn would invoke the generic function.
+ *
+ * Example:
+ *
+ * (in a header file)
+ * @code
+ * OS_DEV_INIT_DCL(widget_init);
+ * OS_DEV_ISR_DCL(widget_isr);
+ * @endcode
+ *
+ * (in an implementation file)
+ * @code
+ * OS_DEV_INIT(widget, widget_init)
+ * {
+ *
+ * os_register_interrupt("widget", WIDGET_IRQ, OS_DEV_ISR_REF(widget_isr));
+ *
+ * os_dev_init_return(OS_RETURN_NO_ERROR_S);
+ * }
+ *
+ * OS_DEV_ISR(widget_isr)
+ * {
+ * os_dev_isr_return(TRUE);
+ * }
+ * @endcode
+ */
+
+/*! @addtogroup drsigs */
+/*! @{ */
+
+/*!
+ * Define a function which will handle device initialization
+ *
+ * This is tne driver initialization routine. This is normally where the
+ * part would be initialized; queues, locks, interrupts handlers defined;
+ * long-term dynamic memory allocated for driver use; etc.
+ *
+ * @param function_name The name of the portable initialization function.
+ *
+ * @return A call to #os_dev_init_return()
+ *
+ */
+#define OS_DEV_INIT(function_name)
+
+/*!
+ * Define a function which will handle device shutdown
+ *
+ * This is the reverse of the #OS_DEV_INIT() routine.
+ *
+ * @param function_name The name of the portable driver shutdown routine.
+ *
+ * @return A call to #os_dev_shutdown_return()
+ */
+#define OS_DEV_SHUTDOWN(function_name)
+
+/*!
+ * Define a function which will open the device for a user.
+ *
+ * @param function_name The name of the driver open() function
+ *
+ * @return A call to #os_dev_open_return()
+ */
+#define OS_DEV_OPEN(function_name)
+
+/*!
+ * Define a function which will handle a user's ioctl() request
+ *
+ * @param function_name The name of the driver ioctl() function
+ *
+ * @return A call to #os_dev_ioctl_return()
+ */
+#define OS_DEV_IOCTL(function_name)
+
+/*!
+ * Define a function which will handle a user's read() request
+ *
+ * @param function_name The name of the driver read() function
+ *
+ * @return A call to #os_dev_read_return()
+ */
+#define OS_DEV_READ(function_name)
+
+/*!
+ * Define a function which will handle a user's write() request
+ *
+ * @param function_name The name of the driver write() function
+ *
+ * @return A call to #os_dev_write_return()
+ */
+#define OS_DEV_WRITE(function_name)
+
+/*!
+ * Define a function which will handle a user's mmap() request
+ *
+ * The mmap() function requests the driver to map some memory into user space.
+ *
+ * @todo Determine what support functions are needed for mmap() handling.
+ *
+ * @param function_name The name of the driver mmap() function
+ *
+ * @return A call to #os_dev_mmap_return()
+ */
+#define OS_DEV_MMAP(function_name)
+
+/*!
+ * Define a function which will close the device - opposite of OS_DEV_OPEN()
+ *
+ * @param function_name The name of the driver close() function
+ *
+ * @return A call to #os_dev_close_return()
+ */
+#define OS_DEV_CLOSE(function_name)
+
+/*!
+ * Define a function which will handle an interrupt
+ *
+ * No arguments are available to the generic function. It must not invoke any
+ * OS functions which are illegal in a ISR. It gets no parameters, and must
+ * have a call to #os_dev_isr_return() instead of any/all return statements.
+ *
+ * Example:
+ * @code
+ * OS_DEV_ISR(widget, widget_isr, WIDGET_IRQ_NUMBER)
+ * {
+ * os_dev_isr_return(1);
+ * }
+ * @endcode
+ *
+ * @param function_name The name of the driver ISR function
+ *
+ * @return A call to #os_dev_isr_return()
+ */
+#define OS_DEV_ISR(function_name)
+
+/*!
+ * Define a function which will operate as a background task / bottom half.
+ *
+ * The function implementation must be structured in the following manner:
+ * @code
+ * OS_DEV_TASK(widget_task)
+ * {
+ * OS_DEV_TASK_SETUP(widget_task);
+ *
+ * while OS_DEV_TASK_CONDITION(widget_task) }
+ *
+ * };
+ * }
+ * @endcode
+ *
+ * @todo In some systems the OS_DEV_TASK_CONDITION() will be an action which
+ * will cause the task to sleep on some event triggered by os_run_task(). In
+ * others, the macro will reference a variable laid down by
+ * OS_DEV_TASK_SETUP() to make sure that the loop is only performed once.
+ *
+ * @param function_name The name of this background task function
+ */
+#define OS_DEV_TASK(function_name)
+
+ /*! @} *//* drsigs */
+
+/*! @defgroup dclsigs Routines to declare Driver Signature routines
+ *
+ * These macros drop prototypes suitable for forward-declaration of
+ * @ref drsigs "function signatures".
+ */
+
+/*! @addtogroup dclsigs */
+/*! @{ */
+
+/*!
+ * Declare prototype for the device initialization function
+ *
+ * @param function_name The name of the portable initialization function.
+ */
+#define OS_DEV_INIT_DCL(function_name)
+
+/*!
+ * Declare prototype for the device shutdown function
+ *
+ * @param function_name The name of the portable driver shutdown routine.
+ *
+ * @return A call to #os_dev_shutdown_return()
+ */
+#define OS_DEV_SHUTDOWN_DCL(function_name)
+
+/*!
+ * Declare prototype for the open() function.
+ *
+ * @param function_name The name of the driver open() function
+ *
+ * @return A call to #os_dev_open_return()
+ */
+#define OS_DEV_OPEN_DCL(function_name)
+
+/*!
+ * Declare prototype for the user's ioctl() request function
+ *
+ * @param function_name The name of the driver ioctl() function
+ *
+ * @return A call to #os_dev_ioctl_return()
+ */
+#define OS_DEV_IOCTL_DCL(function_name)
+
+/*!
+ * Declare prototype for the function a user's read() request
+ *
+ * @param function_name The name of the driver read() function
+ */
+#define OS_DEV_READ_DCL(function_name)
+
+/*!
+ * Declare prototype for the user's write() request function
+ *
+ * @param function_name The name of the driver write() function
+ */
+#define OS_DEV_WRITE_DCL(function_name)
+
+/*!
+ * Declare prototype for the user's mmap() request function
+ *
+ * @param function_name The name of the driver mmap() function
+ */
+#define OS_DEV_MMAP_DCL(function_name)
+
+/*!
+ * Declare prototype for the close function
+ *
+ * @param function_name The name of the driver close() function
+ *
+ * @return A call to #os_dev_close_return()
+ */
+#define OS_DEV_CLOSE_DCL(function_name)
+
+/*!
+ * Declare prototype for the interrupt handling function
+ *
+ * @param function_name The name of the driver ISR function
+ */
+#define OS_DEV_ISR_DCL(function_name)
+
+/*!
+ * Declare prototype for a background task / bottom half function
+ *
+ * @param function_name The name of this background task function
+ */
+#define OS_DEV_TASK_DCL(function_name)
+
+ /*! @} *//* dclsigs */
+
+/*****************************************************************************
+ * Functions for Returning Values from Driver Signature routines
+ *****************************************************************************/
+
+/*!
+ * @defgroup retfns Functions to Return Values from Driver Signature routines
+ */
+
+/*! @addtogroup retfns */
+/*! @{ */
+
+/*!
+ * Return from the #OS_DEV_INIT() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_init_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_SHUTDOWN() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_shutdown_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_ISR() function
+ *
+ * The function should verify that it really was supposed to be called,
+ * and that its device needed attention, in order to properly set the
+ * return code.
+ *
+ * @param code non-zero if interrupt handled, zero otherwise.
+ *
+ */
+void os_dev_isr_return(int code);
+
+/*!
+ * Return from the #OS_DEV_OPEN() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_open_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_IOCTL() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_ioctl_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_READ() function
+ *
+ * @param code Number of bytes read, or an error code to report failure.
+ *
+ */
+void os_dev_read_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_WRITE() function
+ *
+ * @param code Number of bytes written, or an error code to report failure.
+ *
+ */
+void os_dev_write_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_MMAP() function
+ *
+ * @param code Number of bytes written, or an error code to report failure.
+ *
+ */
+void os_dev_mmap_return(os_error_code code);
+
+/*!
+ * Return from the #OS_DEV_CLOSE() function
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_close_return(os_error_code code);
+
+/*!
+ * Start the #OS_DEV_TASK() function
+ *
+ * In some implementations, this could be turned into a label for
+ * the os_dev_task_return() call.
+ *
+ * For a more portable interface, should this take the sleep object as an
+ * argument???
+ *
+ * @return none
+ */
+void os_dev_task_begin(void);
+
+/*!
+ * Return from the #OS_DEV_TASK() function
+ *
+ * In some implementations, this could be turned into a sleep followed
+ * by a jump back to the os_dev_task_begin() call.
+ *
+ * @param code An error code to report success or failure.
+ *
+ */
+void os_dev_task_return(os_error_code code);
+
+ /*! @} *//* retfns */
+
+/*****************************************************************************
+ * Functions/Macros for accessing arguments from Driver Signature routines
+ *****************************************************************************/
+
+/*! @defgroup drsigargs Functions for Getting Arguments in Signature functions
+ *
+ */
+/* @addtogroup @drsigargs */
+/*! @{ */
+
+/*!
+ * Check whether user is requesting read (permission) on the file/device.
+ * Usable in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ()
+ * and #OS_DEV_WRITE() routines.
+ */
+int os_dev_is_flag_read(void);
+
+/*!
+ * Check whether user is requesting write (permission) on the file/device.
+ * Usable in #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ()
+ * and #OS_DEV_WRITE() routines.
+ */
+int os_dev_is_flag_write(void);
+
+/*!
+ * Check whether user is requesting non-blocking I/O. Usable in
+ * #OS_DEV_OPEN(), #OS_DEV_CLOSE(), #OS_DEV_IOCTL(), #OS_DEV_READ() and
+ * #OS_DEV_WRITE() routines.
+ *
+ * @todo Specify required behavior when nonblock is requested and (sufficient?)
+ * data are not available to fulfill the request.
+ *
+ */
+int os_dev_is_flag_nonblock(void);
+
+/*!
+ * Determine which major device is being accessed. Usable in #OS_DEV_OPEN()
+ * and #OS_DEV_CLOSE().
+ */
+int os_dev_get_major(void);
+
+/*!
+ * Determine which minor device is being accessed. Usable in #OS_DEV_OPEN()
+ * and #OS_DEV_CLOSE().
+ */
+int os_dev_get_minor(void);
+
+/*!
+ * Determine which operation the user wants performed. Usable in
+ * #OS_DEV_IOCTL().
+ *
+ * @return Value of the operation.
+ *
+ * @todo Define some generic way to define the individual operations.
+ */
+unsigned os_dev_get_ioctl_op(void);
+
+/*!
+ * Retrieve the associated argument for the desired operation. Usable in
+ * #OS_DEV_IOCTL().
+ *
+ * @return A value which can be cast to a struct pointer or used as
+ * int/long.
+ */
+os_dev_ioctl_arg_t os_dev_get_ioctl_arg(void);
+
+/*!
+ * Determine the requested byte count. This should be the size of buffer at
+ * #os_dev_get_user_buffer(). Usable in OS_DEV_READ() and OS_DEV_WRITE()
+ * routines.
+ *
+ * @return A count of bytes
+ */
+unsigned os_dev_get_count(void);
+
+/*!
+ * Get the pointer to the user's data buffer. Usable in OS_DEV_READ(),
+ * OS_DEV_WRITE(), and OS_DEV_MMAP() routines.
+ *
+ * @return Pointer to user buffer (in user space). See #os_copy_to_user()
+ * and #os_copy_from_user().
+ */
+void *os_dev_get_user_buffer(void);
+
+/*!
+ * Get the POSIX flags field for the associated open file. Usable in
+ * OS_DEV_READ(), OS_DEV_WRITE(), and OS_DEV_IOCTL() routines.
+ *
+ * @return The flags associated with the file.
+ */
+unsigned os_dev_get_file_flags(void);
+
+/*!
+ * Set the driver's private structure associated with this file/open.
+ *
+ * Generally used during #OS_DEV_OPEN(). May also be used during
+ * #OS_DEV_READ(), #OS_DEV_WRITE(), #OS_DEV_IOCTL(), #OS_DEV_MMAP(), and
+ * #OS_DEV_CLOSE(). See also #os_dev_get_user_private().
+ *
+ * @param struct_p The driver data structure to associate with this user.
+ */
+void os_dev_set_user_private(void *struct_p);
+
+/*!
+ * Get the driver's private structure associated with this file.
+ *
+ * May be used during #OS_DEV_OPEN(), #OS_DEV_READ(), #OS_DEV_WRITE(),
+ * #OS_DEV_IOCTL(), #OS_DEV_MMAP(), and #OS_DEV_CLOSE(). See
+ * also #os_dev_set_user_private().
+ *
+ * @return The driver data structure to associate with this user.
+ */
+void *os_dev_get_user_private(void);
+
+/*!
+ * Get the IRQ associated with this call to the #OS_DEV_ISR() function.
+ *
+ * @return The IRQ (integer) interrupt number.
+ */
+int os_dev_get_irq(void);
+
+ /*! @} *//* drsigargs */
+
+/*****************************************************************************
+ * Functions for Generating References to Driver Routines
+ *****************************************************************************/
+
+/*!
+ * @defgroup drref Functions for Generating References to Driver Routines
+ *
+ * These functions will most likely be implemented as macros. They are a
+ * necessary part of the portable API to guarantee portability. The @c symbol
+ * type in here is the same symbol passed to the associated
+ * signature-generating macro.
+ *
+ * These macros must be used whenever referring to a
+ * @ref drsigs "driver signature function", for instance when storing or
+ * passing a pointer to the function.
+ */
+
+/*! @addtogroup drref */
+/*! @{ */
+
+/*!
+ * Generate a reference to an #OS_DEV_INIT() function
+ *
+ * @param function_name The name of the init function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_init_function_t OS_DEV_INIT_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_SHUTDOWN() function
+ *
+ * @param function_name The name of the shutdown function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_shutdown_function_t OS_DEV_SHUTDOWN_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_OPEN() function
+ *
+ * @param function_name The name of the open function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_OPEN_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_CLOSE() function
+ *
+ * @param function_name The name of the close function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_CLOSE_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_READ() function
+ *
+ * @param function_name The name of the read function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_READ_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_WRITE() function
+ *
+ * @param function_name The name of the write function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_WRITE_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_IOCTL() function
+ *
+ * @param function_name The name of the ioctl function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_IOCTL_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_MMAP() function
+ *
+ * @param function_name The name of the mmap function being referenced.
+ *
+ * @return A reference to the function
+ */
+os_user_function_t OS_DEV_MMAP_REF(symbol function_name);
+
+/*!
+ * Generate a reference to an #OS_DEV_ISR() function
+ *
+ * @param function_name The name of the isr being referenced.
+ *
+ * @return a reference to the function
+ */
+os_interrupt_handler_t OS_DEV_ISR_REF(symbol function_name);
+
+ /*! @} *//* drref */
+
+/*!
+ * Flush and invalidate all cache lines.
+ */
+void os_flush_cache_all(void);
+
+/*!
+ * Flush a range of addresses from the cache
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ */
+void os_cache_flush_range(void *start, uint32_t len);
+
+/*!
+ * Invalidate a range of addresses in the cache
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ */
+void os_cache_inv_range(void *start, uint32_t len);
+
+/*!
+ * Clean a range of addresses from the cache
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ */
+void os_cache_clean_range(void *start, uint32_t len);
+
+/*!
+ * @example widget.h
+ */
+
+/*!
+ * @example widget.c
+ */
+
+/*!
+ * @example rng_driver.h
+ */
+
+/*!
+ * @example rng_driver.c
+ */
+
+/*!
+ * @example shw_driver.h
+ */
+
+/*!
+ * @example shw_driver.c
+ */
+
+#endif /* DOXYGEN_PORTABLE_OS_DOC */
+
+#endif /* PORTABLE_OS_H */
diff --git a/drivers/mxc/security/sahara2/include/sah_driver_common.h b/drivers/mxc/security/sahara2/include/sah_driver_common.h
new file mode 100644
index 000000000000..7cfd32a30352
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_driver_common.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/**
+* @file sah_driver_common.h
+*
+* @brief Provides types and defined values for use in the Driver Interface.
+*
+*/
+
+#ifndef SAH_DRIVER_COMMON_H
+#define SAH_DRIVER_COMMON_H
+
+#include "fsl_platform.h"
+#include <sahara.h>
+#include <adaptor.h>
+
+/** This specifies the permissions for the device file. It is equivalent to
+ * chmod 666.
+ */
+#define SAHARA_DEVICE_MODE S_IFCHR | S_IRUGO | S_IWUGO
+
+/**
+* The status of entries in the Queue.
+*
+******************************************************************************/
+typedef enum
+{
+ /** This state indicates that the entry is in the queue and awaits
+ * execution on SAHARA. */
+ SAH_STATE_PENDING,
+ /** This state indicates that the entry has been written to the SAHARA
+ * DAR. */
+ SAH_STATE_ON_SAHARA,
+ /** This state indicates that the entry is off of SAHARA, and is awaiting
+ post-processing. */
+ SAH_STATE_OFF_SAHARA,
+ /** This state indicates that the entry is successfully executed on SAHARA,
+ and it is finished with post-processing. */
+ SAH_STATE_COMPLETE,
+ /** This state indicates that the entry caused an error or fault on SAHARA,
+ * and it is finished with post-processing. */
+ SAH_STATE_FAILED,
+ /** This state indicates that the entry was reset via the Reset IO
+ * Control, and it is finished with post-processing. */
+ SAH_STATE_RESET,
+ /** This state indicates that the entry was signalled from user-space and
+ * either in the DAR, IDAR or has finished executing pending Bottom Half
+ * processing. */
+ SAH_STATE_IGNORE,
+ /** This state indicates that the entry was signalled from user-space and
+ * has been processed by the bottom half. */
+ SAH_STATE_IGNORED
+} sah_Queue_Status;
+
+/* any of these conditions being true indicates the descriptor's processing
+ * is complete */
+#define SAH_DESC_PROCESSED(status) \
+ (((status) == SAH_STATE_COMPLETE) || \
+ ((status) == SAH_STATE_FAILED ) || \
+ ((status) == SAH_STATE_RESET ))
+
+extern os_lock_t desc_queue_lock;
+
+extern uint32_t dar_count;
+extern uint32_t interrupt_count;
+extern uint32_t done1done2_count;
+extern uint32_t done1busy2_count;
+extern uint32_t done1_count;
+
+#ifdef FSL_HAVE_SCC2
+extern void *lookup_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base);
+#endif
+
+int sah_get_results_pointers(fsl_shw_uco_t* user_ctx, uint32_t arg);
+fsl_shw_return_t sah_get_results_from_pool(volatile fsl_shw_uco_t* user_ctx,
+ sah_results *arg);
+fsl_shw_return_t sah_handle_registration(fsl_shw_uco_t *user_cts);
+fsl_shw_return_t sah_handle_deregistration(fsl_shw_uco_t *user_cts);
+
+int sah_Queue_Manager_Count_Entries(int ignore_state, sah_Queue_Status state);
+unsigned long sah_Handle_Poll(sah_Head_Desc *entry);
+
+#ifdef DIAG_DRV_IF
+/******************************************************************************
+* Descriptor and Link dumping functions.
+******************************************************************************/
+void sah_Dump_Chain(const sah_Desc *chain, dma_addr_t addr);
+#endif /* DIAG_DRV_IF */
+
+#endif /* SAH_DRIVER_COMMON_H */
diff --git a/drivers/mxc/security/sahara2/include/sah_hardware_interface.h b/drivers/mxc/security/sahara2/include/sah_hardware_interface.h
new file mode 100644
index 000000000000..0933346fc223
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_hardware_interface.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/**
+* @file sah_hardware_interface.h
+*
+* @brief Provides an interface to the SAHARA hardware registers.
+*
+*/
+
+#ifndef SAH_HARDWARE_INTERFACE_H
+#define SAH_HARDWARE_INTERFACE_H
+
+#include <sah_driver_common.h>
+#include <sah_status_manager.h>
+
+/* These values can be used with sah_HW_Write_Control(). */
+#ifdef SAHARA1
+/** Define platform as Little-Endian */
+#define CTRL_LITTLE_END 0x00000002
+/** Bit to cause endian change in transfers */
+#define CTRL_INT_EN 0x00000004
+/** Set High Assurance mode */
+#define CTRL_HA 0x00000008
+#else
+/** Bit to cause byte swapping in (data?) transfers */
+#define CTRL_BYTE_SWAP 0x00000001
+/** Bit to cause halfword swapping in (data?) transfers */
+#define CTRL_HALFWORD_SWAP 0x00000002
+/** Bit to cause endian change in (data?) transfers */
+#define CTRL_INT_EN 0x00000010
+/** Set High Assurance mode */
+#define CTRL_HA 0x00000020
+/** Disable High Assurance */
+#define CTRL_HA_DISABLE 0x00000040
+/** Reseed the RNG CHA */
+#define CTRL_RNG_RESEED 0x00000080
+#endif
+
+
+/* These values can be used with sah_HW_Write_Command(). */
+/** Reset the Sahara */
+#define CMD_RESET 0x00000001
+/** Set Sahara into Batch mode. */
+#define CMD_BATCH 0x00010000
+/** Clear the Sahara interrupt */
+#define CMD_CLR_INT_BIT 0x00000100
+/** Clear the Sahara error */
+#define CMD_CLR_ERROR_BIT 0x00000200
+
+
+/** error status register contains error */
+#define STATUS_ERROR 0x00000010
+
+/** Op status register contains op status */
+#define OP_STATUS 0x00000020
+
+
+/* High Level functions */
+int sah_HW_Reset(void);
+fsl_shw_return_t sah_HW_Set_HA(void);
+sah_Execute_Status sah_Wait_On_Sahara(void);
+
+/* Low Level functions */
+uint32_t sah_HW_Read_Version(void);
+uint32_t sah_HW_Read_Control(void);
+uint32_t sah_HW_Read_Status(void);
+uint32_t sah_HW_Read_Error_Status(void);
+uint32_t sah_HW_Read_Op_Status(void);
+uint32_t sah_HW_Read_DAR(void);
+uint32_t sah_HW_Read_CDAR(void);
+uint32_t sah_HW_Read_IDAR(void);
+uint32_t sah_HW_Read_Fault_Address(void);
+uint32_t sah_HW_Read_MM_Status(void);
+uint32_t sah_HW_Read_Config(void);
+void sah_HW_Write_Command(uint32_t command);
+void sah_HW_Write_Control(uint32_t control);
+void sah_HW_Write_DAR(uint32_t pointer);
+void sah_HW_Write_Config(uint32_t configuration);
+
+#if defined DIAG_DRV_IF || defined(DO_DBG)
+
+void sah_Dump_Words(const char *prefix, const unsigned *data, dma_addr_t addr,
+ unsigned length);
+#endif
+
+#endif /* SAH_HARDWARE_INTERFACE_H */
+
+/* End of sah_hardware_interface.c */
diff --git a/drivers/mxc/security/sahara2/include/sah_interrupt_handler.h b/drivers/mxc/security/sahara2/include/sah_interrupt_handler.h
new file mode 100644
index 000000000000..8eb8690ff093
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_interrupt_handler.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/**
+* @file sah_interrupt_handler.h
+*
+* @brief Provides a hardware interrupt handling mechanism for device driver.
+*
+*/
+/******************************************************************************
+*
+* CAUTION:
+*
+* MODIFICATION HISTORY:
+*
+* Date Person Change
+* 30/07/03 MW Initial Creation
+*******************************************************************
+*/
+
+#ifndef SAH_INTERRUPT_HANDLER_H
+#define SAH_INTERRUPT_HANDLER_H
+
+#include <sah_driver_common.h>
+
+/******************************************************************************
+* External function declarations
+******************************************************************************/
+int sah_Intr_Init (wait_queue_head_t *wait_queue);
+void sah_Intr_Release (void);
+
+#endif /* SAH_INTERRUPT_HANDLER_H */
diff --git a/drivers/mxc/security/sahara2/include/sah_kernel.h b/drivers/mxc/security/sahara2/include/sah_kernel.h
new file mode 100644
index 000000000000..42013272f610
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_kernel.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*
+* @file sah_kernel.h
+*
+* @brief Provides definitions for items that user-space and kernel-space share.
+*/
+/******************************************************************************
+*
+* This file needs to be PORTED to a non-Linux platform
+*/
+
+#ifndef SAH_KERNEL_H
+#define SAH_KERNEL_H
+
+#if defined(__KERNEL__)
+
+#if defined(CONFIG_ARCH_MXC91321) || defined(CONFIG_ARCH_MXC91231) \
+ || defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MXC92323)
+#include <mach/hardware.h>
+#define SAHA_BASE_ADDR SAHARA_BASE_ADDR
+#define SAHARA_IRQ MXC_INT_SAHARA
+#elif defined(CONFIG_ARCH_MX51)
+#include <mach/hardware.h>
+#define SAHA_BASE_ADDR SAHARA_BASE_ADDR
+#define SAHARA_IRQ MXC_INT_SAHARA_H0
+#else
+#include <mach/mx2.h>
+#endif
+
+#endif /* KERNEL */
+
+/* IO Controls */
+/* The magic number 'k' is reserved for the SPARC architecture. (See <kernel
+ * source root>/Documentation/ioctl-number.txt.
+ *
+ * Note: Numbers 8-13 were used in a previous version of the API and should
+ * be avoided.
+ */
+#define SAH_IOC_MAGIC 'k'
+#define SAHARA_HWRESET _IO(SAH_IOC_MAGIC, 0)
+#define SAHARA_SET_HA _IO(SAH_IOC_MAGIC, 1)
+#define SAHARA_CHK_TEST_MODE _IOR(SAH_IOC_MAGIC,2, int)
+#define SAHARA_DAR _IO(SAH_IOC_MAGIC, 3)
+#define SAHARA_GET_RESULTS _IO(SAH_IOC_MAGIC, 4)
+#define SAHARA_REGISTER _IO(SAH_IOC_MAGIC, 5)
+#define SAHARA_DEREGISTER _IO(SAH_IOC_MAGIC, 6)
+/* 7 */
+/* 8 */
+/* 9 */
+/* 10 */
+/* 11 */
+/* 12 */
+/* 13 */
+
+#define SAHARA_SCC_DROP_PERMS _IOWR(SAH_IOC_MAGIC, 14, scc_partition_info_t)
+#define SAHARA_SCC_SFREE _IOWR(SAH_IOC_MAGIC, 15, scc_partition_info_t)
+
+#define SAHARA_SK_ALLOC _IOWR(SAH_IOC_MAGIC, 16, scc_slot_t)
+#define SAHARA_SK_DEALLOC _IOWR(SAH_IOC_MAGIC, 17, scc_slot_t)
+#define SAHARA_SK_LOAD _IOWR(SAH_IOC_MAGIC, 18, scc_slot_t)
+#define SAHARA_SK_UNLOAD _IOWR(SAH_IOC_MAGIC, 19, scc_slot_t)
+#define SAHARA_SK_SLOT_ENC _IOWR(SAH_IOC_MAGIC, 20, scc_slot_t)
+#define SAHARA_SK_SLOT_DEC _IOWR(SAH_IOC_MAGIC, 21, scc_slot_t)
+
+#define SAHARA_SCC_ENCRYPT _IOWR(SAH_IOC_MAGIC, 22, scc_region_t)
+#define SAHARA_SCC_DECRYPT _IOWR(SAH_IOC_MAGIC, 23, scc_region_t)
+#define SAHARA_GET_CAPS _IOWR(SAH_IOC_MAGIC, 24, fsl_shw_pco_t)
+
+#define SAHARA_SCC_SSTATUS _IOWR(SAH_IOC_MAGIC, 25, scc_partition_info_t)
+
+#define SAHARA_SK_READ _IOWR(SAH_IOC_MAGIC, 29, scc_slot_t)
+
+/*! This is the name that will appear in /proc/interrupts */
+#define SAHARA_NAME "SAHARA"
+
+/*!
+ * SAHARA IRQ number. See page 9-239 of TLICS - Motorola Semiconductors H.K.
+ * TAHITI-Lite IC Specification, Rev 1.1, Feb 2003.
+ *
+ * TAHITI has two blocks of 32 interrupts. The SAHARA IRQ is number 27
+ * in the second block. This means that the SAHARA IRQ is 27 + 32 = 59.
+ */
+#ifndef SAHARA_IRQ
+#define SAHARA_IRQ (27+32)
+#endif
+
+/*!
+ * Device file definition. The #ifndef is here to support the unit test code
+ * by allowing it to specify a different test device.
+ */
+#ifndef SAHARA_DEVICE_SHORT
+#define SAHARA_DEVICE_SHORT "sahara"
+#endif
+
+#ifndef SAHARA_DEVICE
+#define SAHARA_DEVICE "/dev/"SAHARA_DEVICE_SHORT
+#endif
+
+#endif /* SAH_KERNEL_H */
+
+/* End of sah_kernel.h */
diff --git a/drivers/mxc/security/sahara2/include/sah_memory_mapper.h b/drivers/mxc/security/sahara2/include/sah_memory_mapper.h
new file mode 100644
index 000000000000..838ce47cbf85
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_memory_mapper.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/**
+* @file sah_memory_mapper.h
+*
+* @brief Re-creates SAHARA data structures in Kernel memory such that they are
+* suitable for DMA.
+*
+*/
+
+#ifndef SAH_MEMORY_MAPPER_H
+#define SAH_MEMORY_MAPPER_H
+
+#include <sah_driver_common.h>
+#include <sah_queue_manager.h>
+
+
+/******************************************************************************
+* External function declarations
+******************************************************************************/
+sah_Head_Desc *sah_Copy_Descriptors(fsl_shw_uco_t * user_ctx,
+ sah_Head_Desc * desc);
+
+sah_Link *sah_Copy_Links(fsl_shw_uco_t * user_ctx, sah_Link * ptr);
+
+sah_Head_Desc *sah_Physicalise_Descriptors(sah_Head_Desc * desc);
+
+sah_Link *sah_Physicalise_Links (sah_Link *ptr);
+
+sah_Head_Desc *sah_DePhysicalise_Descriptors (sah_Head_Desc *desc);
+
+sah_Link *sah_DePhysicalise_Links (sah_Link *ptr);
+
+sah_Link *sah_Make_Links(fsl_shw_uco_t * user_ctx,
+ sah_Link * ptr, sah_Link ** tail);
+
+
+void sah_Destroy_Descriptors (sah_Head_Desc *desc);
+
+void sah_Destroy_Links (sah_Link *link);
+
+void sah_Free_Chained_Descriptors (sah_Head_Desc *desc);
+
+void sah_Free_Chained_Links (sah_Link *link);
+
+int sah_Init_Mem_Map (void);
+
+void sah_Stop_Mem_Map (void);
+
+int sah_Block_Add_Page (int big);
+
+sah_Desc *sah_Alloc_Descriptor (void);
+sah_Head_Desc *sah_Alloc_Head_Descriptor (void);
+void sah_Free_Descriptor (sah_Desc *desc);
+void sah_Free_Head_Descriptor (sah_Head_Desc *desc);
+sah_Link *sah_Alloc_Link (void);
+void sah_Free_Link (sah_Link *link);
+
+void *wire_user_memory(void *address, uint32_t length, void **page_ctx);
+void unwire_user_memory(void **page_ctx);
+
+os_error_code map_user_memory(struct vm_area_struct *vma,
+ uint32_t physical_addr, uint32_t size);
+os_error_code unmap_user_memory(uint32_t user_addr, uint32_t size);
+
+#endif /* SAH_MEMORY_MAPPER_H */
+
+/* End of sah_memory_mapper.h */
diff --git a/drivers/mxc/security/sahara2/include/sah_queue_manager.h b/drivers/mxc/security/sahara2/include/sah_queue_manager.h
new file mode 100644
index 000000000000..2dde5883e193
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_queue_manager.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/**
+* @file sah_queue_manager.h
+*
+* @brief This file definitions for the Queue Manager.
+
+* The Queue Manager manages additions and removal from the queue and updates
+* the status of queue entries. It also calls sah_HW_* functions to interact
+* with the hardware.
+*
+*/
+
+#ifndef SAH_QUEUE_MANAGER_H
+#define SAH_QUEUE_MANAGER_H
+
+#include <sah_driver_common.h>
+#include <sah_status_manager.h>
+
+
+/*************************
+* Queue Manager Functions
+*************************/
+fsl_shw_return_t sah_Queue_Manager_Init(void);
+void sah_Queue_Manager_Close(void);
+void sah_Queue_Manager_Reset_Entries(void);
+void sah_Queue_Manager_Append_Entry(sah_Head_Desc *entry);
+void sah_Queue_Manager_Remove_Entry(sah_Head_Desc *entry);
+
+
+/*************************
+* Queue Functions
+*************************/
+sah_Queue *sah_Queue_Construct(void);
+void sah_Queue_Destroy(sah_Queue *this);
+void sah_Queue_Append_Entry(sah_Queue *this, sah_Head_Desc *entry);
+void sah_Queue_Remove_Entry(sah_Queue *this);
+void sah_Queue_Remove_Any_Entry(sah_Queue *this, sah_Head_Desc *entry);
+void sah_postprocess_queue(unsigned long reset_flag);
+
+
+/*************************
+* Misc Releated Functions
+*************************/
+
+int sah_blocking_mode(struct sah_Head_Desc *entry);
+fsl_shw_return_t sah_convert_error_status(uint32_t error_status);
+
+
+#endif /* SAH_QUEUE_MANAGER_H */
+
+/* End of sah_queue_manager.h */
diff --git a/drivers/mxc/security/sahara2/include/sah_status_manager.h b/drivers/mxc/security/sahara2/include/sah_status_manager.h
new file mode 100644
index 000000000000..e38731bf4835
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_status_manager.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/**
+* @file sah_status_manager.h
+*
+* @brief SAHARA Status Manager Types and Function Prototypes
+*
+* @author Stuart Holloway (SH)
+*
+*/
+
+#ifndef STATUS_MANAGER_H
+#define STATUS_MANAGER_H
+#include "sah_driver_common.h"
+#include "sahara.h"
+
+
+/******************************************************************************
+* User defined data types
+******************************************************************************/
+/**
+******************************************************************************
+* sah_Execute_Status
+* Types read from SAHARA Status Register, with additional state for Op Status
+******************************************************************************/
+typedef enum sah_Execute_Status
+{
+ /** Sahara is Idle. */
+ SAH_EXEC_IDLE = 0,
+ /** SAHARA is busy performing a resest or processing a decriptor chain. */
+ SAH_EXEC_BUSY = 1,
+ /** An error occurred while SAHARA executed the first descriptor. */
+ SAH_EXEC_ERROR1 = 2,
+ /** SAHARA has failed internally. */
+ SAH_EXEC_FAULT = 3,
+ /** SAHARA has finished processing a descriptor chain and is idle. */
+ SAH_EXEC_DONE1 = 4,
+ /** SAHARA has finished processing a descriptor chain, and is processing a
+ * second chain. */
+ SAH_EXEC_DONE1_BUSY2 = 5,
+ /** SAHARA has finished processing a descriptor chain, but has generated an
+ * error while processing a second descriptor chain. */
+ SAH_EXEC_DONE1_ERROR2 = 6,
+ /** SAHARA has finished two descriptors. */
+ SAH_EXEC_DONE1_DONE2 = 7,
+ /** SAHARA has stopped, and first descriptor has Op Status, not Err */
+ SAH_EXEC_OPSTAT1 = 0x20,
+} sah_Execute_Status;
+
+/**
+ * When this bit is on in a #sah_Execute_Status, it means that DONE1 is true.
+ */
+#define SAH_EXEC_DONE1_BIT 4
+
+/**
+ * Bits which make up the Sahara State
+ */
+#define SAH_EXEC_STATE_MASK 0x00000007
+
+/**
+*******************************************************************************
+* sah_Execute_Error
+* Types read from SAHARA Error Status Register
+******************************************************************************/
+typedef enum sah_Execute_Error
+{
+ /** No Error */
+ SAH_ERR_NONE = 0,
+ /** Header is not valid. */
+ SAH_ERR_HEADER = 1,
+ /** Descriptor length is not correct. */
+ SAH_ERR_DESC_LENGTH = 2,
+ /** Length or pointer field is zero while the other is non-zero. */
+ SAH_ERR_DESC_POINTER = 3,
+ /** Length of the link is not a multiple of 4 and is not the last link */
+ SAH_ERR_LINK_LENGTH = 4,
+ /** The data pointer in a link is zero */
+ SAH_ERR_LINK_POINTER = 5,
+ /** Input Buffer reported an overflow */
+ SAH_ERR_INPUT_BUFFER = 6,
+ /** Output Buffer reported an underflow */
+ SAH_ERR_OUTPUT_BUFFER = 7,
+ /** Incorrect data in output buffer after CHA's has signalled 'done'. */
+ SAH_ERR_OUTPUT_BUFFER_STARVATION = 8,
+ /** Internal Hardware Failure. */
+ SAH_ERR_INTERNAL_STATE = 9,
+ /** Current Descriptor was not legal, but cause is unknown. */
+ SAH_ERR_GENERAL_DESCRIPTOR = 10,
+ /** Reserved pointer fields have been set to 1. */
+ SAH_ERR_RESERVED_FIELDS = 11,
+ /** Descriptor address error */
+ SAH_ERR_DESCRIPTOR_ADDRESS = 12,
+ /** Link address error */
+ SAH_ERR_LINK_ADDRESS = 13,
+ /** Processing error in CHA module */
+ SAH_ERR_CHA = 14,
+ /** Processing error during DMA */
+ SAH_ERR_DMA = 15
+} sah_Execute_Error;
+
+
+/**
+*******************************************************************************
+* sah_CHA_Error_Source
+* Types read from SAHARA Error Status Register for CHA Error Source
+*
+******************************************************************************/
+typedef enum sah_CHA_Error_Source
+{
+ /** No Error indicated in Source CHA Error. */
+ SAH_CHA_NO_ERROR = 0,
+ /** Error in SKHA module. */
+ SAH_CHA_SKHA_ERROR = 1,
+ /** Error in MDHA module. */
+ SAH_CHA_MDHA_ERROR = 2,
+ /** Error in RNG module. */
+ SAH_CHA_RNG_ERROR = 3,
+ /** Error in PKHA module. */
+ SAH_CHA_PKHA_ERROR = 4,
+} sah_CHA_Error_Source;
+
+/**
+******************************************************************************
+* sah_CHA_Error_Status
+* Types read from SAHARA Error Status Register for CHA Error Status
+*
+******************************************************************************/
+typedef enum sah_CHA_Error_Status
+{
+ /** No CHA error detected */
+ SAH_CHA_NO_ERR = 0x000,
+ /** Non-empty input buffer when complete. */
+ SAH_CHA_IP_BUF = 0x001,
+ /** Illegal Address Error. */
+ SAH_CHA_ADD_ERR = 0x002,
+ /** Illegal Mode Error. */
+ SAH_CHA_MODE_ERR = 0x004,
+ /** Illegal Data Size Error. */
+ SAH_CHA_DATA_SIZE_ERR = 0x008,
+ /** Illegal Key Size Error. */
+ SAH_CHA_KEY_SIZE_ERR = 0x010,
+ /** Mode/Context/Key written during processing. */
+ SAH_CHA_PROC_ERR = 0x020,
+ /** Context Read During Processing. */
+ SAH_CHA_CTX_READ_ERR = 0x040,
+ /** Internal Hardware Error. */
+ SAH_CHA_INTERNAL_HW_ERR = 0x080,
+ /** Input Buffer not enabled or underflow. */
+ SAH_CHA_IP_BUFF_ERR = 0x100,
+ /** Output Buffer not enabled or overflow. */
+ SAH_CHA_OP_BUFF_ERR = 0x200,
+ /** DES key parity error (SKHA) */
+ SAH_CHA_DES_KEY_ERR = 0x400,
+ /** Reserved error code. */
+ SAH_CHA_RES = 0x800
+} sah_CHA_Error_Status;
+
+/**
+*****************************************************************************
+* sah_DMA_Error_Status
+* Types read from SAHARA Error Status Register for DMA Error Status
+******************************************************************************/
+typedef enum sah_DMA_Error_Status
+{
+ /** No DMA Error Code. */
+ SAH_DMA_NO_ERR = 0,
+ /** AHB terminated a bus cycle with an error. */
+ SAH_DMA_AHB_ERR = 2,
+ /** Internal IP bus cycle was terminated with an error termination. */
+ SAH_DMA_IP_ERR = 4,
+ /** Parity error detected on DMA command. */
+ SAH_DMA_PARITY_ERR = 6,
+ /** DMA was requested to cross a 256 byte internal address boundary. */
+ SAH_DMA_BOUNDRY_ERR = 8,
+ /** DMA controller is busy */
+ SAH_DMA_BUSY_ERR = 10,
+ /** Memory Bounds Error */
+ SAH_DMA_RESERVED_ERR = 12,
+ /** Internal DMA hardware error detected */
+ SAH_DMA_INT_ERR = 14
+} sah_DMA_Error_Status;
+
+/**
+*****************************************************************************
+* sah_DMA_Error_Size
+* Types read from SAHARA Error Status Register for DMA Error Size
+*
+******************************************************************************/
+typedef enum sah_DMA_Error_Size
+{
+ /** Error during Byte transfer. */
+ SAH_DMA_SIZE_BYTE = 0,
+ /** Error during Half-word transfer. */
+ SAH_DMA_SIZE_HALF_WORD = 1,
+ /** Error during Word transfer. */
+ SAH_DMA_SIZE_WORD = 2,
+ /** Reserved DMA word size. */
+ SAH_DMA_SIZE_RES = 3
+} sah_DMA_Error_Size;
+
+
+
+
+extern bool sah_dpm_flag;
+
+/*************************
+* Status Manager Functions
+*************************/
+
+unsigned long sah_Handle_Interrupt(sah_Execute_Status hw_status);
+sah_Head_Desc *sah_Find_With_State (sah_Queue_Status status);
+int sah_dpm_init(void);
+void sah_dpm_close(void);
+void sah_Queue_Manager_Prime (sah_Head_Desc *entry);
+
+
+#endif /* STATUS_MANAGER_H */
diff --git a/drivers/mxc/security/sahara2/include/sahara.h b/drivers/mxc/security/sahara2/include/sahara.h
new file mode 100644
index 000000000000..da2352780efd
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sahara.h
@@ -0,0 +1,2266 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file sahara.h
+ *
+ * File which implements the FSL_SHW API when used on Sahara
+ */
+/*!
+ * @if USE_MAINPAGE
+ * @mainpage Sahara2 implemtation of FSL Security Hardware API
+ * @endif
+ *
+ */
+
+#define _DIAG_DRV_IF
+#define _DIAG_SECURITY_FUNC
+#define _DIAG_ADAPTOR
+
+#ifndef SAHARA2_API_H
+#define SAHARA2_API_H
+
+#ifdef DIAG_SECURITY_FUNC
+#include <diagnostic.h>
+#endif /* DIAG_SECURITY_FUNC */
+
+/* This is a Linux flag... ? */
+#ifndef __KERNEL__
+#include <inttypes.h>
+#include <stdlib.h>
+#include <memory.h>
+#else
+#include "portable_os.h"
+#endif
+
+/* This definition may need a new name, and needs to go somewhere which
+ * can determine platform, kernel vs. user, os, etc.
+ */
+#define copy_bytes(out, in, len) memcpy(out, in, len)
+
+/* Does this belong here? */
+#ifndef SAHARA_DEVICE
+#define SAHARA_DEVICE "/dev/sahara"
+#endif
+
+/*!
+*******************************************************************************
+* @defgroup lnkflags Link Flags
+*
+* @brief Flags to show information about link data and link segments
+*
+******************************************************************************/
+/*! @addtogroup lnkflags
+ * @{
+ */
+
+/*!
+*******************************************************************************
+* This flag indicates that the data in a link is owned by the security
+* function component and this memory will be freed by the security function
+* component. To be used as part of the flag field of the sah_Link structure.
+******************************************************************************/
+#define SAH_OWNS_LINK_DATA 0x01
+
+/*!
+*******************************************************************************
+* The data in a link is not owned by the security function component and
+* therefore it will not attempt to free this memory. To be used as part of the
+* flag field of the sah_Link structure.
+******************************************************************************/
+#define SAH_USES_LINK_DATA 0x02
+
+/*!
+*******************************************************************************
+* The data in this link will change when the descriptor gets executed.
+******************************************************************************/
+#define SAH_OUTPUT_LINK 0x04
+
+/*!
+*******************************************************************************
+* The ptr and length in this link are really 'established key' info. They
+* are to be converted to ptr/length before putting on request queue.
+******************************************************************************/
+#define SAH_KEY_IS_HIDDEN 0x08
+
+/*!
+*******************************************************************************
+* The link structure has been appended to the previous one by the driver. It
+* needs to be removed before leaving the driver (and returning to API).
+******************************************************************************/
+#define SAH_REWORKED_LINK 0x10
+
+/*!
+*******************************************************************************
+* The length and data fields of this link contain the slot and user id
+* used to access the SCC stored key
+******************************************************************************/
+#define SAH_STORED_KEY_INFO 0x20
+
+/*!
+*******************************************************************************
+* The Data field points to a physical address, and does not need to be
+* processed by the driver. Honored only in Kernel API.
+******************************************************************************/
+#define SAH_PREPHYS_DATA 0x40
+
+/*!
+*******************************************************************************
+* The link was inserted during the Physicalise procedure. It is tagged so
+* it can be removed during DePhysicalise, thereby returning to the caller an
+* intact chain.
+******************************************************************************/
+#define SAH_LINK_INSERTED_LINK 0x80
+
+/*!
+*******************************************************************************
+* The Data field points to the location of the key, which is in a secure
+* partition held by the user. The memory address needs to be converted to
+* kernel space manually, by looking through the partitions that the user holds.
+******************************************************************************/
+#define SAH_IN_USER_KEYSTORE 0x100
+
+/*!
+*******************************************************************************
+* sah_Link_Flags
+*
+* Type to be used for flags associated with a Link in security function.
+* These flags are used internally by the security function component only.
+*
+* Values defined at @ref lnkflags
+*
+* @brief typedef for flags field of sah_Link
+******************************************************************************/
+typedef uint32_t sah_Link_Flags;
+
+/*
+*******************************************************************************
+* Security Parameters Related Structures
+*
+* All of structures associated with API parameters
+*
+******************************************************************************/
+
+/*
+*******************************************************************************
+* Common Types
+*
+* All of structures used across several classes of crytography
+******************************************************************************/
+
+/*!
+*******************************************************************************
+* @brief Indefinite precision integer used for security operations on SAHARA
+* accelerator. The data will always be in little Endian format.
+******************************************************************************/
+typedef uint8_t *sah_Int;
+
+/*!
+*******************************************************************************
+* @brief Byte array used for block cipher and hash digest/MAC operations on
+* SAHARA accelerator. The Endian format will be as specified by the function
+* using the sah_Oct_Str.
+******************************************************************************/
+typedef uint8_t *sah_Oct_Str;
+
+/*!
+ * A queue of descriptor heads -- used to hold requests waiting for user to
+ * pick up the results. */
+typedef struct sah_Queue {
+ int count; /*!< # entries in queue */
+ struct sah_Head_Desc *head; /*!< first entry in queue */
+ struct sah_Head_Desc *tail; /*!< last entry in queue */
+} sah_Queue;
+
+/******************************************************************************
+ * Enumerations
+ *****************************************************************************/
+/*!
+ * Flags for the state of the User Context Object (#fsl_shw_uco_t).
+ */
+typedef enum fsl_shw_user_ctx_flags_t {
+ /*!
+ * API will block the caller until operation completes. The result will be
+ * available in the return code. If this is not set, user will have to get
+ * results using #fsl_shw_get_results().
+ */
+ FSL_UCO_BLOCKING_MODE = 0x01,
+ /*!
+ * User wants callback (at the function specified with
+ * #fsl_shw_uco_set_callback()) when the operation completes. This flag is
+ * valid only if #FSL_UCO_BLOCKING_MODE is not set.
+ */
+ FSL_UCO_CALLBACK_MODE = 0x02,
+ /*! Do not free descriptor chain after driver (adaptor) finishes */
+ FSL_UCO_SAVE_DESC_CHAIN = 0x04,
+ /*!
+ * User has made at least one request with callbacks requested, so API is
+ * ready to handle others.
+ */
+ FSL_UCO_CALLBACK_SETUP_COMPLETE = 0x08,
+ /*!
+ * (virtual) pointer to descriptor chain is completely linked with physical
+ * (DMA) addresses, ready for the hardware. This flag should not be used
+ * by FSL SHW API programs.
+ */
+ FSL_UCO_CHAIN_PREPHYSICALIZED = 0x10,
+ /*!
+ * The user has changed the context but the changes have not been copied to
+ * the kernel driver.
+ */
+ FSL_UCO_CONTEXT_CHANGED = 0x20,
+ /*! Internal Use. This context belongs to a user-mode API user. */
+ FSL_UCO_USERMODE_USER = 0x40,
+} fsl_shw_user_ctx_flags_t;
+
+/*!
+ * Return code for FSL_SHW library.
+ *
+ * These codes may be returned from a function call. In non-blocking mode,
+ * they will appear as the status in a Result Object.
+ */
+typedef enum fsl_shw_return_t {
+ /*!
+ * No error. As a function return code in Non-blocking mode, this may
+ * simply mean that the operation was accepted for eventual execution.
+ */
+ FSL_RETURN_OK_S = 0,
+ /*! Failure for non-specific reason. */
+ FSL_RETURN_ERROR_S,
+ /*!
+ * Operation failed because some resource was not able to be allocated.
+ */
+ FSL_RETURN_NO_RESOURCE_S,
+ /*! Crypto algorithm unrecognized or improper. */
+ FSL_RETURN_BAD_ALGORITHM_S,
+ /*! Crypto mode unrecognized or improper. */
+ FSL_RETURN_BAD_MODE_S,
+ /*! Flag setting unrecognized or inconsistent. */
+ FSL_RETURN_BAD_FLAG_S,
+ /*! Improper or unsupported key length for algorithm. */
+ FSL_RETURN_BAD_KEY_LENGTH_S,
+ /*! Improper parity in a (DES, TDES) key. */
+ FSL_RETURN_BAD_KEY_PARITY_S,
+ /*!
+ * Improper or unsupported data length for algorithm or internal buffer.
+ */
+ FSL_RETURN_BAD_DATA_LENGTH_S,
+ /*! Authentication / Integrity Check code check failed. */
+ FSL_RETURN_AUTH_FAILED_S,
+ /*! A memory error occurred. */
+ FSL_RETURN_MEMORY_ERROR_S,
+ /*! An error internal to the hardware occurred. */
+ FSL_RETURN_INTERNAL_ERROR_S,
+ /*! ECC detected Point at Infinity */
+ FSL_RETURN_POINT_AT_INFINITY_S,
+ /*! ECC detected No Point at Infinity */
+ FSL_RETURN_POINT_NOT_AT_INFINITY_S,
+ /*! GCD is One */
+ FSL_RETURN_GCD_IS_ONE_S,
+ /*! GCD is not One */
+ FSL_RETURN_GCD_IS_NOT_ONE_S,
+ /*! Candidate is Prime */
+ FSL_RETURN_PRIME_S,
+ /*! Candidate is not Prime */
+ FSL_RETURN_NOT_PRIME_S,
+ /*! N register loaded improperly with even value */
+ FSL_RETURN_EVEN_MODULUS_ERROR_S,
+ /*! Divisor is zero. */
+ FSL_RETURN_DIVIDE_BY_ZERO_ERROR_S,
+ /*! Bad Exponent or Scalar value for Point Multiply */
+ FSL_RETURN_BAD_EXPONENT_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_OSCILLATOR_ERROR_S,
+ /*! RNG hardware problem. */
+ FSL_RETURN_STATISTICS_ERROR_S,
+} fsl_shw_return_t;
+
+/*!
+ * Algorithm Identifier.
+ *
+ * Selection of algorithm will determine how large the block size of the
+ * algorithm is. Context size is the same length unless otherwise specified.
+ * Selection of algorithm also affects the allowable key length.
+ */
+typedef enum fsl_shw_key_alg_t {
+ /*!
+ * Key will be used to perform an HMAC. Key size is 1 to 64 octets. Block
+ * size is 64 octets.
+ */
+ FSL_KEY_ALG_HMAC,
+ /*!
+ * Advanced Encryption Standard (Rijndael). Block size is 16 octets. Key
+ * size is 16 octets. (The single choice of key size is a Sahara platform
+ * limitation.)
+ */
+ FSL_KEY_ALG_AES,
+ /*!
+ * Data Encryption Standard. Block size is 8 octets. Key size is 8
+ * octets.
+ */
+ FSL_KEY_ALG_DES,
+ /*!
+ * 2- or 3-key Triple DES. Block size is 8 octets. Key size is 16 octets
+ * for 2-key Triple DES, and 24 octets for 3-key.
+ */
+ FSL_KEY_ALG_TDES,
+ /*!
+ * ARC4. No block size. Context size is 259 octets. Allowed key size is
+ * 1-16 octets. (The choices for key size are a Sahara platform
+ * limitation.)
+ */
+ FSL_KEY_ALG_ARC4,
+ /*!
+ * Private key of a public-private key-pair. Max is 512 bits...
+ */
+ FSL_KEY_PK_PRIVATE,
+} fsl_shw_key_alg_t;
+
+/*!
+ * Mode selector for Symmetric Ciphers.
+ *
+ * The selection of mode determines how a cryptographic algorithm will be
+ * used to process the plaintext or ciphertext.
+ *
+ * For all modes which are run block-by-block (that is, all but
+ * #FSL_SYM_MODE_STREAM), any partial operations must be performed on a text
+ * length which is multiple of the block size. Except for #FSL_SYM_MODE_CTR,
+ * these block-by-block algorithms must also be passed a total number of octets
+ * which is a multiple of the block size.
+ *
+ * In modes which require that the total number of octets of data be a multiple
+ * of the block size (#FSL_SYM_MODE_ECB and #FSL_SYM_MODE_CBC), and the user
+ * has a total number of octets which are not a multiple of the block size, the
+ * user must perform any necessary padding to get to the correct data length.
+ */
+typedef enum fsl_shw_sym_mode_t {
+ /*!
+ * Stream. There is no associated block size. Any request to process data
+ * may be of any length. This mode is only for ARC4 operations, and is
+ * also the only mode used for ARC4.
+ */
+ FSL_SYM_MODE_STREAM,
+
+ /*!
+ * Electronic Codebook. Each block of data is encrypted/decrypted. The
+ * length of the data stream must be a multiple of the block size. This
+ * mode may be used for DES, 3DES, and AES. The block size is determined
+ * by the algorithm.
+ */
+ FSL_SYM_MODE_ECB,
+ /*!
+ * Cipher-Block Chaining. Each block of data is encrypted/decrypted and
+ * then "chained" with the previous block by an XOR function. Requires
+ * context to start the XOR (previous block). This mode may be used for
+ * DES, 3DES, and AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CBC,
+ /*!
+ * Counter. The counter is encrypted, then XORed with a block of data.
+ * The counter is then incremented (using modulus arithmetic) for the next
+ * block. The final operation may be non-multiple of block size. This mode
+ * may be used for AES. The block size is determined by the algorithm.
+ */
+ FSL_SYM_MODE_CTR,
+} fsl_shw_sym_mode_t;
+
+/*!
+ * Algorithm selector for Cryptographic Hash functions.
+ *
+ * Selection of algorithm determines how large the context and digest will be.
+ * Context is the same size as the digest (resulting hash), unless otherwise
+ * specified.
+ */
+typedef enum fsl_shw_hash_alg_t {
+ /*! MD5 algorithm. Digest is 16 octets. */
+ FSL_HASH_ALG_MD5,
+ /*! SHA-1 (aka SHA or SHA-160) algorithm. Digest is 20 octets. */
+ FSL_HASH_ALG_SHA1,
+ /*!
+ * SHA-224 algorithm. Digest is 28 octets, though context is 32 octets.
+ */
+ FSL_HASH_ALG_SHA224,
+ /*! SHA-256 algorithm. Digest is 32 octets. */
+ FSL_HASH_ALG_SHA256
+} fsl_shw_hash_alg_t;
+
+/*!
+ * The type of Authentication-Cipher function which will be performed.
+ */
+typedef enum fsl_shw_acc_mode_t {
+ /*!
+ * CBC-MAC for Counter. Requires context and modulus. Final operation may
+ * be non-multiple of block size. This mode may be used for AES.
+ */
+ FSL_ACC_MODE_CCM,
+ /*!
+ * SSL mode. Not supported. Combines HMAC and encrypt (or decrypt).
+ * Needs one key object for encryption, another for the HMAC. The usual
+ * hashing and symmetric encryption algorithms are supported.
+ */
+ FSL_ACC_MODE_SSL,
+} fsl_shw_acc_mode_t;
+
+/* REQ-S2LRD-PINTFC-COA-HCO-001 */
+/*!
+ * Flags which control a Hash operation.
+ */
+typedef enum fsl_shw_hash_ctx_flags_t {
+ /*!
+ * Context is empty. Hash is started from scratch, with a
+ * message-processed count of zero.
+ */
+ FSL_HASH_FLAGS_INIT = 0x01,
+ /*!
+ * Retrieve context from hardware after hashing. If used with the
+ * #FSL_HASH_FLAGS_FINALIZE flag, the final digest value will be saved in
+ * the object.
+ */
+ FSL_HASH_FLAGS_SAVE = 0x02,
+ /*! Place context into hardware before hashing. */
+ FSL_HASH_FLAGS_LOAD = 0x04,
+ /*!
+ * PAD message and perform final digest operation. If user message is
+ * pre-padded, this flag should not be used.
+ */
+ FSL_HASH_FLAGS_FINALIZE = 0x08,
+} fsl_shw_hash_ctx_flags_t;
+
+/*!
+ * Flags which control an HMAC operation.
+ *
+ * These may be combined by ORing them together. See #fsl_shw_hmco_set_flags()
+ * and #fsl_shw_hmco_clear_flags().
+ */
+typedef enum fsl_shw_hmac_ctx_flags_t {
+ /*!
+ * Message context is empty. HMAC is started from scratch (with key) or
+ * from precompute of inner hash, depending on whether
+ * #FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT is set.
+ */
+ FSL_HMAC_FLAGS_INIT = 1,
+ /*!
+ * Retrieve ongoing context from hardware after hashing. If used with the
+ * #FSL_HMAC_FLAGS_FINALIZE flag, the final digest value (HMAC) will be
+ * saved in the object.
+ */
+ FSL_HMAC_FLAGS_SAVE = 2,
+ /*! Place ongoing context into hardware before hashing. */
+ FSL_HMAC_FLAGS_LOAD = 4,
+ /*!
+ * PAD message and perform final HMAC operations of inner and outer
+ * hashes.
+ */
+ FSL_HMAC_FLAGS_FINALIZE = 8,
+ /*!
+ * This means that the context contains precomputed inner and outer hash
+ * values.
+ */
+ FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT = 16,
+} fsl_shw_hmac_ctx_flags_t;
+
+/*!
+ * Flags to control use of the #fsl_shw_scco_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_scco_set_flags() and #fsl_shw_scco_clear_flags()
+ */
+typedef enum fsl_shw_sym_ctx_flags_t {
+ /*!
+ * Context is empty. In ARC4, this means that the S-Box needs to be
+ * generated from the key. In #FSL_SYM_MODE_CBC mode, this allows an IV of
+ * zero to be specified. In #FSL_SYM_MODE_CTR mode, it means that an
+ * initial CTR value of zero is desired.
+ */
+ FSL_SYM_CTX_INIT = 1,
+ /*!
+ * Load context from object into hardware before running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_LOAD = 2,
+ /*!
+ * Save context from hardware into object after running cipher. In
+ * #FSL_SYM_MODE_CTR mode, this would refer to the Counter Value.
+ */
+ FSL_SYM_CTX_SAVE = 4,
+ /*!
+ * Context (SBox) is to be unwrapped and wrapped on each use.
+ * This flag is unsupported.
+ * */
+ FSL_SYM_CTX_PROTECT = 8,
+} fsl_shw_sym_ctx_flags_t;
+
+/*!
+ * Flags which describe the state of the #fsl_shw_sko_t.
+ *
+ * These may be ORed together to get the desired effect.
+ * See #fsl_shw_sko_set_flags() and #fsl_shw_sko_clear_flags()
+ */
+typedef enum fsl_shw_key_flags_t {
+ /*! If algorithm is DES or 3DES, do not validate the key parity bits. */
+ FSL_SKO_KEY_IGNORE_PARITY = 1,
+ /*! Clear key is present in the object. */
+ FSL_SKO_KEY_PRESENT = 2,
+ /*!
+ * Key has been established for use. This feature is not available for all
+ * platforms, nor for all algorithms and modes.
+ */
+ FSL_SKO_KEY_ESTABLISHED = 4,
+ /*!
+ * Key intended for user (software) use; can be read cleartext from the
+ * keystore.
+ */
+ FSL_SKO_KEY_SW_KEY = 8,
+} fsl_shw_key_flags_t;
+
+/*!
+ * Type of value which is associated with an established key.
+ */
+typedef uint64_t key_userid_t;
+
+/*!
+ * Flags which describe the state of the #fsl_shw_acco_t.
+ *
+ * The @a FSL_ACCO_CTX_INIT and @a FSL_ACCO_CTX_FINALIZE flags, when used
+ * together, provide for a one-shot operation.
+ */
+typedef enum fsl_shw_auth_ctx_flags_t {
+ /*! Initialize Context(s) */
+ FSL_ACCO_CTX_INIT = 1,
+ /*! Load intermediate context(s). This flag is unsupported. */
+ FSL_ACCO_CTX_LOAD = 2,
+ /*! Save intermediate context(s). This flag is unsupported. */
+ FSL_ACCO_CTX_SAVE = 4,
+ /*! Create MAC during this operation. */
+ FSL_ACCO_CTX_FINALIZE = 8,
+ /*!
+ * Formatting of CCM input data is performed by calls to
+ * #fsl_shw_ccm_nist_format_ctr_and_iv() and
+ * #fsl_shw_ccm_nist_update_ctr_and_iv().
+ */
+ FSL_ACCO_NIST_CCM = 0x10,
+} fsl_shw_auth_ctx_flags_t;
+
+/*!
+ * The operation which controls the behavior of #fsl_shw_establish_key().
+ *
+ * These values are passed to #fsl_shw_establish_key().
+ */
+typedef enum fsl_shw_key_wrap_t {
+ /*! Generate a key from random values. */
+ FSL_KEY_WRAP_CREATE,
+ /*! Use the provided clear key. */
+ FSL_KEY_WRAP_ACCEPT,
+ /*! Unwrap a previously wrapped key. */
+ FSL_KEY_WRAP_UNWRAP
+} fsl_shw_key_wrap_t;
+
+/*!
+ * Modulus Selector for CTR modes.
+ *
+ * The incrementing of the Counter value may be modified by a modulus. If no
+ * modulus is needed or desired for AES, use #FSL_CTR_MOD_128.
+ */
+typedef enum fsl_shw_ctr_mod_t {
+ FSL_CTR_MOD_8, /*!< Run counter with modulus of 2^8. */
+ FSL_CTR_MOD_16, /*!< Run counter with modulus of 2^16. */
+ FSL_CTR_MOD_24, /*!< Run counter with modulus of 2^24. */
+ FSL_CTR_MOD_32, /*!< Run counter with modulus of 2^32. */
+ FSL_CTR_MOD_40, /*!< Run counter with modulus of 2^40. */
+ FSL_CTR_MOD_48, /*!< Run counter with modulus of 2^48. */
+ FSL_CTR_MOD_56, /*!< Run counter with modulus of 2^56. */
+ FSL_CTR_MOD_64, /*!< Run counter with modulus of 2^64. */
+ FSL_CTR_MOD_72, /*!< Run counter with modulus of 2^72. */
+ FSL_CTR_MOD_80, /*!< Run counter with modulus of 2^80. */
+ FSL_CTR_MOD_88, /*!< Run counter with modulus of 2^88. */
+ FSL_CTR_MOD_96, /*!< Run counter with modulus of 2^96. */
+ FSL_CTR_MOD_104, /*!< Run counter with modulus of 2^104. */
+ FSL_CTR_MOD_112, /*!< Run counter with modulus of 2^112. */
+ FSL_CTR_MOD_120, /*!< Run counter with modulus of 2^120. */
+ FSL_CTR_MOD_128 /*!< Run counter with modulus of 2^128. */
+} fsl_shw_ctr_mod_t;
+
+/*!
+ * Permissions flags for Secure Partitions
+ */
+typedef enum fsl_shw_permission_t {
+/*! SCM Access Permission: Do not zeroize/deallocate partition on SMN Fail state */
+ FSL_PERM_NO_ZEROIZE = 0x80000000,
+/*! SCM Access Permission: Enforce trusted key read in */
+ FSL_PERM_TRUSTED_KEY_READ = 0x40000000,
+/*! SCM Access Permission: Ignore Supervisor/User mode in permission determination */
+ FSL_PERM_HD_S = 0x00000800,
+/*! SCM Access Permission: Allow Read Access to Host Domain */
+ FSL_PERM_HD_R = 0x00000400,
+/*! SCM Access Permission: Allow Write Access to Host Domain */
+ FSL_PERM_HD_W = 0x00000200,
+/*! SCM Access Permission: Allow Execute Access to Host Domain */
+ FSL_PERM_HD_X = 0x00000100,
+/*! SCM Access Permission: Allow Read Access to Trusted Host Domain */
+ FSL_PERM_TH_R = 0x00000040,
+/*! SCM Access Permission: Allow Write Access to Trusted Host Domain */
+ FSL_PERM_TH_W = 0x00000020,
+/*! SCM Access Permission: Allow Read Access to Other/World Domain */
+ FSL_PERM_OT_R = 0x00000004,
+/*! SCM Access Permission: Allow Write Access to Other/World Domain */
+ FSL_PERM_OT_W = 0x00000002,
+/*! SCM Access Permission: Allow Execute Access to Other/World Domain */
+ FSL_PERM_OT_X = 0x00000001,
+} fsl_shw_permission_t;
+
+typedef enum fsl_shw_cypher_mode_t {
+ FSL_SHW_CYPHER_MODE_ECB = 1, /*!< ECB mode */
+ FSL_SHW_CYPHER_MODE_CBC = 2, /*!< CBC mode */
+} fsl_shw_cypher_mode_t;
+
+typedef enum fsl_shw_pf_key_t {
+ FSL_SHW_PF_KEY_IIM, /*!< Present fused IIM key */
+ FSL_SHW_PF_KEY_PRG, /*!< Present Program key */
+ FSL_SHW_PF_KEY_IIM_PRG, /*!< Present IIM ^ Program key */
+ FSL_SHW_PF_KEY_IIM_RND, /*!< Present Random key */
+ FSL_SHW_PF_KEY_RND, /*!< Present IIM ^ Random key */
+} fsl_shw_pf_key_t;
+
+typedef enum fsl_shw_tamper_t {
+ FSL_SHW_TAMPER_NONE, /*!< No error detected */
+ FSL_SHW_TAMPER_WTD, /*!< wire-mesh tampering det */
+ FSL_SHW_TAMPER_ETBD, /*!< ext tampering det: input B */
+ FSL_SHW_TAMPER_ETAD, /*!< ext tampering det: input A */
+ FSL_SHW_TAMPER_EBD, /*!< external boot detected */
+ FSL_SHW_TAMPER_SAD, /*!< security alarm detected */
+ FSL_SHW_TAMPER_TTD, /*!< temperature tampering det */
+ FSL_SHW_TAMPER_CTD, /*!< clock tampering det */
+ FSL_SHW_TAMPER_VTD, /*!< voltage tampering det */
+ FSL_SHW_TAMPER_MCO, /*!< monotonic counter overflow */
+ FSL_SHW_TAMPER_TCO, /*!< time counter overflow */
+} fsl_shw_tamper_t;
+
+/******************************************************************************
+ * Data Structures
+ *****************************************************************************/
+
+/*!
+ *
+ * @brief Structure type for descriptors
+ *
+ * The first five fields are passed to the hardware.
+ *
+ *****************************************************************************/
+#ifndef USE_NEW_PTRS /* Experimental */
+
+typedef struct sah_Desc {
+ uint32_t header; /*!< descriptor header value */
+ uint32_t len1; /*!< number of data bytes in 'ptr1' buffer */
+ void *ptr1; /*!< pointer to first sah_Link structure */
+ uint32_t len2; /*!< number of data bytes in 'ptr2' buffer */
+ void *ptr2; /*!< pointer to second sah_Link structure */
+ struct sah_Desc *next; /*!< pointer to next descriptor */
+#ifdef __KERNEL__ /* This needs a better test */
+ /* These two must be last. See sah_Copy_Descriptors */
+ struct sah_Desc *virt_addr; /*!< Virtual (kernel) address of this
+ descriptor. */
+ dma_addr_t dma_addr; /*!< Physical (bus) address of this
+ descriptor. */
+ void *original_ptr1; /*!< user's pointer to ptr1 */
+ void *original_ptr2; /*!< user's pointer to ptr2 */
+ struct sah_Desc *original_next; /*!< user's pointer to next */
+#endif
+} sah_Desc;
+
+#else
+
+typedef struct sah_Desc {
+ uint32_t header; /*!< descriptor header value */
+ uint32_t len1; /*!< number of data bytes in 'ptr1' buffer */
+ uint32_t hw_ptr1; /*!< pointer to first sah_Link structure */
+ uint32_t len2; /*!< number of data bytes in 'ptr2' buffer */
+ uint32_t hw_ptr2; /*!< pointer to second sah_Link structure */
+ uint32_t hw_next; /*!< pointer to next descriptor */
+ struct sah_Link *ptr1; /*!< (virtual) pointer to first sah_Link structure */
+ struct sah_Link *ptr2; /*!< (virtual) pointer to first sah_Link structure */
+ struct sah_Desc *next; /*!< (virtual) pointer to next descriptor */
+#ifdef __KERNEL__ /* This needs a better test */
+ /* These two must be last. See sah_Copy_Descriptors */
+ struct sah_Desc *virt_addr; /*!< Virtual (kernel) address of this
+ descriptor. */
+ dma_addr_t dma_addr; /*!< Physical (bus) address of this
+ descriptor. */
+#endif
+} sah_Desc;
+
+#endif
+
+/*!
+*******************************************************************************
+* @brief The first descriptor in a chain
+******************************************************************************/
+typedef struct sah_Head_Desc {
+ sah_Desc desc; /*!< whole struct - must be first */
+ struct fsl_shw_uco_t *user_info; /*!< where result pool lives */
+ uint32_t user_ref; /*!< at time of request */
+ uint32_t uco_flags; /*!< at time of request */
+ uint32_t status; /*!< Status of queue entry */
+ uint32_t error_status; /*!< If error, register from Sahara */
+ uint32_t fault_address; /*!< If error, register from Sahara */
+ uint32_t op_status; /*!< If error, register from Sahara */
+ fsl_shw_return_t result; /*!< Result of running descriptor */
+ struct sah_Head_Desc *next; /*!< Next in queue */
+ struct sah_Head_Desc *prev; /*!< previous in queue */
+ struct sah_Head_Desc *user_desc; /*!< For API async get_results */
+ void *out1_ptr; /*!< For async post-processing */
+ void *out2_ptr; /*!< For async post-processing */
+ uint32_t out_len; /*!< For async post-processing */
+} sah_Head_Desc;
+
+/*!
+ * @brief Structure type for links
+ *
+ * The first three fields are used by hardware.
+ *****************************************************************************/
+#ifndef USE_NEW_PTRS
+
+typedef struct sah_Link {
+ size_t len; /*!< len of 'data' buffer in bytes */
+ uint8_t *data; /*!< buffer to store data */
+ struct sah_Link *next; /*!< pointer to the next sah_Link storing
+ * data */
+ sah_Link_Flags flags; /*!< indicates the component that created the
+ * data buffer. Security Function internal
+ * information */
+ key_userid_t ownerid; /*!< Auth code for established key */
+ uint32_t slot; /*!< Location of the the established key */
+#ifdef __KERNEL__ /* This needs a better test */
+ /* These two elements must be last. See sah_Copy_Links() */
+ struct sah_Link *virt_addr;
+ dma_addr_t dma_addr;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ struct page *vm_info;
+#endif
+ uint8_t *original_data; /*!< user's version of data pointer */
+ struct sah_Link *original_next; /*!< user's version of next pointer */
+#ifdef SAH_COPY_DATA
+ uint8_t *copy_data; /*!< Virtual address of acquired buffer */
+#endif
+#endif /* kernel-only */
+} sah_Link;
+
+#else
+
+typedef struct sah_Link {
+ /*! len of 'data' buffer in bytes */
+ size_t len;
+ /*! buffer to store data */
+ uint32_t hw_data;
+ /*! Physical address */
+ uint32_t hw_next;
+ /*!
+ * indicates the component that created the data buffer. Security Function
+ * internal information
+ */
+ sah_Link_Flags flags;
+ /*! (virtual) pointer to data */
+ uint8_t *data;
+ /*! (virtual) pointer to the next sah_Link storing data */
+ struct sah_Link *next;
+ /*! Auth code for established key */
+ key_userid_t ownerid;
+ /*! Location of the the established key */
+ uint32_t slot;
+#ifdef __KERNEL__ /* This needs a better test */
+ /* These two elements must be last. See sah_Copy_Links() */
+ struct sah_Link *virt_addr;
+ dma_addr_t dma_addr;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ struct page *vm_info;
+#endif
+#endif /* kernel-only */
+} sah_Link;
+
+#endif
+
+/*!
+ * Initialization Object
+ */
+typedef struct fsl_sho_ibo_t {
+} fsl_sho_ibo_t;
+
+/* Imported from Sahara1 driver -- is it needed forever? */
+/*!
+*******************************************************************************
+* FIELDS
+*
+* void * ref - parameter to be passed into the memory function calls
+*
+* void * (*malloc)(void *ref, size_t n) - pointer to user's malloc function
+*
+* void (*free)(void *ref, void *ptr) - pointer to user's free function
+*
+* void * (*memcpy)(void *ref, void *dest, const void *src, size_t n) -
+* pointer to user's memcpy function
+*
+* void * (*memset)(void *ref, void *ptr, int ch, size_t n) - pointer to
+* user's memset function
+*
+* @brief Structure for API memory utilities
+******************************************************************************/
+typedef struct sah_Mem_Util {
+ /*! Who knows. Vestigial. */
+ void *mu_ref;
+ /*! Acquire buffer of size n bytes */
+ void *(*mu_malloc) (void *ref, size_t n);
+ /*! Acquire a sah_Head_Desc */
+ sah_Head_Desc *(*mu_alloc_head_desc) (void *ref);
+ /* Acquire a sah_Desc */
+ sah_Desc *(*mu_alloc_desc) (void *ref);
+ /* Acquire a sah_Link */
+ sah_Link *(*mu_alloc_link) (void *ref);
+ /*! Free buffer at ptr */
+ void (*mu_free) (void *ref, void *ptr);
+ /*! Free sah_Head_Desc at ptr */
+ void (*mu_free_head_desc) (void *ref, sah_Head_Desc * ptr);
+ /*! Free sah_Desc at ptr */
+ void (*mu_free_desc) (void *ref, sah_Desc * ptr);
+ /*! Free sah_Link at ptr */
+ void (*mu_free_link) (void *ref, sah_Link * ptr);
+ /*! Funciton which will copy n bytes from src to dest */
+ void *(*mu_memcpy) (void *ref, void *dest, const void *src, size_t n);
+ /*! Set all n bytes of ptr to ch */
+ void *(*mu_memset) (void *ref, void *ptr, int ch, size_t n);
+} sah_Mem_Util;
+
+/*!
+ * Secure Partition information
+ *
+ * This holds the context to a single secure partition owned by the user. It
+ * is only available in the kernel version of the User Context Object.
+ */
+typedef struct fsl_shw_spo_t {
+ uint32_t user_base; /*!< Base address (user virtual) */
+ void *kernel_base; /*!< Base address (kernel virtual) */
+ struct fsl_shw_spo_t *next; /*!< Pointer to the next partition
+ owned by the user. NULL if this
+ is the last partition. */
+} fsl_shw_spo_t;
+
+/* REQ-S2LRD-PINTFC-COA-UCO-001 */
+/*!
+ * User Context Object
+ */
+typedef struct fsl_shw_uco_t {
+ int sahara_openfd; /*!< this should be kernel-only?? */
+ sah_Mem_Util *mem_util; /*!< Memory utility fns */
+ uint32_t user_ref; /*!< User's reference */
+ void (*callback) (struct fsl_shw_uco_t * uco); /*!< User's callback fn */
+ uint32_t flags; /*!< from fsl_shw_user_ctx_flags_t */
+ unsigned pool_size; /*!< maximum size of user pool */
+#ifdef __KERNEL__
+ sah_Queue result_pool; /*!< where non-blocking results go */
+ os_process_handle_t process; /*!< remember for signalling User mode */
+ fsl_shw_spo_t *partition; /*!< chain of secure partitions owned by
+ the user */
+#else
+ struct fsl_shw_uco_t *next; /*!< To allow user-mode chaining of contexts,
+ for signalling. */
+#endif
+} fsl_shw_uco_t;
+
+/* REQ-S2LRD-PINTFC-API-GEN-006 ?? */
+/*!
+ * Result object
+ */
+typedef struct fsl_shw_result_t {
+ uint32_t user_ref;
+ fsl_shw_return_t code;
+ uint32_t detail1;
+ uint32_t detail2;
+ sah_Head_Desc *user_desc;
+} fsl_shw_result_t;
+
+/*!
+ * Keystore Object
+ */
+typedef struct fsl_shw_kso_t {
+#ifdef __KERNEL__
+ os_lock_t lock; /*!< Pointer to lock that controls access to
+ the keystore. */
+#endif
+ void *user_data; /*!< Pointer to user structure that handles
+ the internals of the keystore. */
+ fsl_shw_return_t(*data_init) (fsl_shw_uco_t * user_ctx,
+ void **user_data);
+ void (*data_cleanup) (fsl_shw_uco_t * user_ctx, void **user_data);
+ fsl_shw_return_t(*slot_verify_access) (void *user_data,
+ uint64_t owner_id,
+ uint32_t slot);
+ fsl_shw_return_t(*slot_alloc) (void *user_data, uint32_t size_bytes,
+ uint64_t owner_id, uint32_t * slot);
+ fsl_shw_return_t(*slot_dealloc) (void *user_data, uint64_t owner_id,
+ uint32_t slot);
+ void *(*slot_get_address) (void *user_data, uint32_t slot);
+ uint32_t(*slot_get_base) (void *user_data, uint32_t slot);
+ uint32_t(*slot_get_offset) (void *user_data, uint32_t slot);
+ uint32_t(*slot_get_slot_size) (void *user_data, uint32_t slot);
+} fsl_shw_kso_t;
+
+/* REQ-S2LRD-PINTFC-COA-SKO-001 */
+/*!
+ * Secret Key Context Object
+ */
+typedef struct fsl_shw_sko_t {
+ uint32_t flags;
+ fsl_shw_key_alg_t algorithm;
+ key_userid_t userid;
+ uint32_t handle;
+ uint16_t key_length;
+ uint8_t key[64];
+ struct fsl_shw_kso_t *keystore; /*!< If present, key is in keystore */
+} fsl_shw_sko_t;
+
+/* REQ-S2LRD-PINTFC-COA-CO-001 */
+/*!
+ * @brief Platform Capability Object
+ */
+typedef struct fsl_shw_pco_t { /* Consider turning these constants into symbols */
+ int api_major;
+ int api_minor;
+ int driver_major;
+ int driver_minor;
+ fsl_shw_key_alg_t sym_algorithms[4];
+ fsl_shw_sym_mode_t sym_modes[4];
+ fsl_shw_hash_alg_t hash_algorithms[4];
+ uint8_t sym_support[5][4]; /* indexed by key alg then mode */
+
+ int scc_driver_major;
+ int scc_driver_minor;
+ int scm_version; /*!< Version from SCM Configuration register */
+ int smn_version; /*!< Version from SMN Status register */
+ int block_size_bytes; /*!< Number of bytes per block of RAM; also
+ block size of the crypto algorithm. */
+ union {
+ struct {
+ int black_ram_size_blocks; /*!< Number of blocks of Black RAM */
+ int red_ram_size_blocks; /*!< Number of blocks of Red RAM */
+ } scc_info;
+ struct {
+ int partition_size_bytes; /*!< Number of bytes in each partition */
+ int partition_count; /*!< Number of partitions on this platform */
+ } scc2_info;
+ };
+} fsl_shw_pco_t;
+
+/* REQ-S2LRD-PINTFC-COA-HCO-001 */
+/*!
+ * Hash Context Object
+ */
+typedef struct fsl_shw_hco_t { /* fsl_shw_hash_context_object */
+ fsl_shw_hash_alg_t algorithm;
+ uint32_t flags;
+ uint8_t digest_length; /* in bytes */
+ uint8_t context_length; /* in bytes */
+ uint8_t context_register_length; /* in bytes */
+ uint32_t context[9]; /* largest digest + msg size */
+} fsl_shw_hco_t;
+
+/*!
+ * HMAC Context Object
+ */
+typedef struct fsl_shw_hmco_t { /* fsl_shw_hmac_context_object */
+ fsl_shw_hash_alg_t algorithm;
+ uint32_t flags;
+ uint8_t digest_length; /*!< in bytes */
+ uint8_t context_length; /*!< in bytes */
+ uint8_t context_register_length; /*!< in bytes */
+ uint32_t ongoing_context[9]; /*!< largest digest + msg
+ size */
+ uint32_t inner_precompute[9]; /*!< largest digest + msg
+ size */
+ uint32_t outer_precompute[9]; /*!< largest digest + msg
+ size */
+} fsl_shw_hmco_t;
+
+/* REQ-S2LRD-PINTFC-COA-SCCO-001 */
+/*!
+ * Symmetric Crypto Context Object Context Object
+ */
+typedef struct fsl_shw_scco_t {
+ uint32_t flags;
+ unsigned block_size_bytes; /* double duty block&ctx size */
+ fsl_shw_sym_mode_t mode;
+ /* Could put modulus plus 16-octet context in union with arc4
+ sbox+ptrs... */
+ fsl_shw_ctr_mod_t modulus_exp;
+ uint8_t context[259];
+} fsl_shw_scco_t;
+
+/*!
+ * Authenticate-Cipher Context Object
+
+ * An object for controlling the function of, and holding information about,
+ * data for the authenticate-cipher functions, #fsl_shw_gen_encrypt() and
+ * #fsl_shw_auth_decrypt().
+ */
+typedef struct fsl_shw_acco_t {
+ uint32_t flags; /*!< See #fsl_shw_auth_ctx_flags_t for
+ meanings */
+ fsl_shw_acc_mode_t mode; /*!< CCM only */
+ uint8_t mac_length; /*!< User's value for length */
+ unsigned q_length; /*!< NIST parameter - */
+ fsl_shw_scco_t cipher_ctx_info; /*!< For running
+ encrypt/decrypt. */
+ union {
+ fsl_shw_scco_t CCM_ctx_info; /*!< For running the CBC in
+ AES-CCM. */
+ fsl_shw_hco_t hash_ctx_info; /*!< For running the hash */
+ } auth_info; /*!< "auth" info struct */
+ uint8_t unencrypted_mac[16]; /*!< max block size... */
+} fsl_shw_acco_t;
+
+/*!
+ * Used by Sahara API to retrieve completed non-blocking results.
+ */
+typedef struct sah_results {
+ unsigned requested; /*!< number of results requested */
+ unsigned *actual; /*!< number of results obtained */
+ fsl_shw_result_t *results; /*!< pointer to memory to hold results */
+} sah_results;
+
+/*!
+ * @typedef scc_partition_status_t
+ */
+/*! Partition status information. */
+typedef enum fsl_shw_partition_status_t {
+ FSL_PART_S_UNUSABLE, /*!< Partition not implemented */
+ FSL_PART_S_UNAVAILABLE, /*!< Partition owned by other host */
+ FSL_PART_S_AVAILABLE, /*!< Partition available */
+ FSL_PART_S_ALLOCATED, /*!< Partition owned by host but not engaged
+ */
+ FSL_PART_S_ENGAGED, /*!< Partition owned by host and engaged */
+} fsl_shw_partition_status_t;
+
+/******************************************************************************
+ * Access Macros for Objects
+ *****************************************************************************/
+/*!
+ * Get FSL SHW API version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the API is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the API is to be stored.
+ */
+#define fsl_shw_pco_get_version(pcobject, pcmajor, pcminor) \
+{ \
+ *(pcmajor) = (pcobject)->api_major; \
+ *(pcminor) = (pcobject)->api_minor; \
+}
+
+/*!
+ * Get underlying driver version.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the driver is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the driver is to be stored.
+ */
+#define fsl_shw_pco_get_driver_version(pcobject, pcmajor, pcminor) \
+{ \
+ *(pcmajor) = (pcobject)->driver_major; \
+ *(pcminor) = (pcobject)->driver_minor; \
+}
+
+/*!
+ * Get list of symmetric algorithms supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] pcalgorithms A pointer to where to store the location of
+ * the list of algorithms.
+ * @param[out] pcacount A pointer to where to store the number of
+ * algorithms in the list at @a algorithms.
+ */
+#define fsl_shw_pco_get_sym_algorithms(pcobject, pcalgorithms, pcacount) \
+{ \
+ *(pcalgorithms) = (pcobject)->sym_algorithms; \
+ *(pcacount) = sizeof((pcobject)->sym_algorithms)/4; \
+}
+
+/*!
+ * Get list of symmetric modes supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] gsmodes A pointer to where to store the location of
+ * the list of modes.
+ * @param[out] gsacount A pointer to where to store the number of
+ * algorithms in the list at @a modes.
+ */
+#define fsl_shw_pco_get_sym_modes(pcobject, gsmodes, gsacount) \
+{ \
+ *(gsmodes) = (pcobject)->sym_modes; \
+ *(gsacount) = sizeof((pcobject)->sym_modes)/4; \
+}
+
+/*!
+ * Get list of hash algorithms supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param[out] gsalgorithms A pointer which will be set to the list of
+ * algorithms.
+ * @param[out] gsacount The number of algorithms in the list at @a
+ * algorithms.
+ */
+#define fsl_shw_pco_get_hash_algorithms(pcobject, gsalgorithms, gsacount) \
+{ \
+ *(gsalgorithms) = (pcobject)->hash_algorithms; \
+ *(gsacount) = sizeof((pcobject)->hash_algorithms)/4; \
+}
+
+/*!
+ * Determine whether the combination of a given symmetric algorithm and a given
+ * mode is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param pcalg A Symmetric Cipher algorithm.
+ * @param pcmode A Symmetric Cipher mode.
+ *
+ * @return 0 if combination is not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_sym_supported(pcobject, pcalg, pcmode) \
+ ((pcobject)->sym_support[pcalg][pcmode])
+
+/*!
+ * Determine whether a given Encryption-Authentication mode is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param pcmode The Authentication mode.
+ *
+ * @return 0 if mode is not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_auth_supported(pcobject, pcmode) \
+ ((pcmode == FSL_ACC_MODE_CCM) ? 1 : 0)
+
+/*!
+ * Determine whether Black Keys (key establishment / wrapping) is supported.
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ *
+ * @return 0 if wrapping is not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_black_key_supported(pcobject) \
+ 1
+
+/*!
+ * Determine whether Programmed Key features are available
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 1 if Programmed Key features are available, otherwise zero.
+ */
+#define fsl_shw_pco_check_pk_supported(pcobject) \
+ 0
+
+/*!
+ * Determine whether Software Key features are available
+ *
+ * @param pc_info The Platform Capabilities Object to query.
+ *
+ * @return 1 if Software key features are available, otherwise zero.
+ */
+#define fsl_shw_pco_check_sw_keys_supported(pcobject) \
+ 0
+
+/*!
+ * Get FSL SHW SCC driver version
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ * @param[out] pcmajor A pointer to where the major version
+ * of the SCC driver is to be stored.
+ * @param[out] pcminor A pointer to where the minor version
+ * of the SCC driver is to be stored.
+ */
+#define fsl_shw_pco_get_scc_driver_version(pcobject, pcmajor, pcminor) \
+{ \
+ *(pcmajor) = (pcobject)->scc_driver_major; \
+ *(pcminor) = (pcobject)->scc_driver_minor; \
+}
+
+/*!
+ * Get SCM hardware version
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ * @return The SCM hardware version
+ */
+#define fsl_shw_pco_get_scm_version(pcobject) \
+ ((pcobject)->scm_version)
+
+/*!
+ * Get SMN hardware version
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ * @return The SMN hardware version
+ */
+#define fsl_shw_pco_get_smn_version(pcobject) \
+ ((pcobject)->smn_version)
+
+/*!
+ * Get the size of an SCM block, in bytes
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ * @return The size of an SCM block, in bytes.
+ */
+#define fsl_shw_pco_get_scm_block_size(pcobject) \
+ ((pcobject)->block_size_bytes)
+
+/*!
+ * Get size of Black and Red RAM memory
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ * @param[out] black_size A pointer to where the size of the Black RAM, in
+ * blocks, is to be placed.
+ * @param[out] red_size A pointer to where the size of the Red RAM, in
+ * blocks, is to be placed.
+ */
+#define fsl_shw_pco_get_smn_size(pcobject, black_size, red_size) \
+{ \
+ if ((pcobject)->scm_version == 1) { \
+ *(black_size) = (pcobject)->scc_info.black_ram_size_blocks; \
+ *(red_size) = (pcobject)->scc_info.red_ram_size_blocks; \
+ } else { \
+ *(black_size) = 0; \
+ *(red_size) = 0; \
+ } \
+}
+
+/*!
+ * Determine whether Secure Partitions are supported
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ *
+ * @return 0 if secure partitions are not supported, non-zero if supported.
+ */
+#define fsl_shw_pco_check_spo_supported(pcobject) \
+ ((pcobject)->scm_version == 2)
+
+/*!
+ * Get the size of a Secure Partitions
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ *
+ * @return Partition size, in bytes. 0 if Secure Partitions not supported.
+ */
+#define fsl_shw_pco_get_spo_size_bytes(pcobject) \
+ (((pcobject)->scm_version == 2) ? \
+ ((pcobject)->scc2_info.partition_size_bytes) : 0 )
+
+/*!
+ * Get the number of Secure Partitions on this platform
+ *
+ * @param pcobject The Platform Capabilities Object to query.
+ *
+ * @return Number of partitions. 0 if Secure Paritions not supported. Note
+ * that this returns the total number of partitions, not all may be
+ * available to the user.
+ */
+#define fsl_shw_pco_get_spo_count(pcobject) \
+ (((pcobject)->scm_version == 2) ? \
+ ((pcobject)->scc2_info.partition_count) : 0 )
+
+/*!
+ * Initialize a User Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the User Context Object to initial values, and set the size
+ * of the results pool. The mode will be set to a default of
+ * #FSL_UCO_BLOCKING_MODE.
+ *
+ * When using non-blocking operations, this sets the maximum number of
+ * operations which can be outstanding. This number includes the counts of
+ * operations waiting to start, operation(s) being performed, and results which
+ * have not been retrieved.
+ *
+ * Changes to this value are ignored once user registration has completed. It
+ * should be set to 1 if only blocking operations will ever be performed.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param usize The maximum number of operations which can be
+ * outstanding.
+ */
+#ifdef __KERNEL__
+#define fsl_shw_uco_init(ucontext, usize) \
+{ \
+ (ucontext)->pool_size = usize; \
+ (ucontext)->flags = FSL_UCO_BLOCKING_MODE; \
+ (ucontext)->sahara_openfd = -1; \
+ (ucontext)->mem_util = NULL; \
+ (ucontext)->partition = NULL; \
+ (ucontext)->callback = NULL; \
+}
+#else
+#define fsl_shw_uco_init(ucontext, usize) \
+{ \
+ (ucontext)->pool_size = usize; \
+ (ucontext)->flags = FSL_UCO_BLOCKING_MODE; \
+ (ucontext)->sahara_openfd = -1; \
+ (ucontext)->mem_util = NULL; \
+ (ucontext)->callback = NULL; \
+}
+#endif
+
+/*!
+ * Set the User Reference for the User Context.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uref A value which will be passed back with a result.
+ */
+#define fsl_shw_uco_set_reference(ucontext, uref) \
+ (ucontext)->user_ref = uref
+
+/*!
+ * Set the User Reference for the User Context.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param ucallback The function the API will invoke when an operation
+ * completes.
+ */
+#define fsl_shw_uco_set_callback(ucontext, ucallback) \
+ (ucontext)->callback = ucallback
+
+/*!
+ * Set flags in the User Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uflags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+#define fsl_shw_uco_set_flags(ucontext, uflags) \
+ (ucontext)->flags |= (uflags)
+
+/*!
+ * Clear flags in the User Context.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param ucontext The User Context object to operate on.
+ * @param uflags ORed values from #fsl_shw_user_ctx_flags_t.
+ */
+#define fsl_shw_uco_clear_flags(ucontext, uflags) \
+ (ucontext)->flags &= ~(uflags)
+
+/*!
+ * Retrieve the reference value from a Result Object.
+ *
+ * @param robject The result object to query.
+ *
+ * @return The reference associated with the request.
+ */
+#define fsl_shw_ro_get_reference(robject) \
+ (robject)->user_ref
+
+/*!
+ * Retrieve the status code from a Result Object.
+ *
+ * @param robject The result object to query.
+ *
+ * @return The status of the request.
+ */
+#define fsl_shw_ro_get_status(robject) \
+ (robject)->code
+
+/*!
+ * Initialize a Secret Key Object.
+ *
+ * This function must be called before performing any other operation with
+ * the Object.
+ *
+ * @param skobject The Secret Key Object to be initialized.
+ * @param skalgorithm DES, AES, etc.
+ *
+ */
+#define fsl_shw_sko_init(skobject,skalgorithm) \
+{ \
+ (skobject)->algorithm = skalgorithm; \
+ (skobject)->flags = 0; \
+ (skobject)->keystore = NULL; \
+}
+
+/*!
+ * Initialize a Secret Key Object to use a Platform Key register.
+ *
+ * This function must be called before performing any other operation with
+ * the Object. INVALID on this platform.
+ *
+ * @param skobject The Secret Key Object to be initialized.
+ * @param skalgorithm DES, AES, etc.
+ * @param skhwkey one of the fsl_shw_pf_key_t values.
+ *
+ */
+#define fsl_shw_sko_init_pf_key(skobject,skalgorithm,skhwkey) \
+{ \
+ (skobject)->algorithm = -1; \
+ (skobject)->flags = -1; \
+ (skobject)->keystore = NULL; \
+}
+
+/*!
+ * Store a cleartext key in the key object.
+ *
+ * This has the side effect of setting the #FSL_SKO_KEY_PRESENT flag and
+ * resetting the #FSL_SKO_KEY_ESTABLISHED flag.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skkey A pointer to the beginning of the key.
+ * @param skkeylen The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+#define fsl_shw_sko_set_key(skobject, skkey, skkeylen) \
+{ \
+ (skobject)->key_length = skkeylen; \
+ copy_bytes((skobject)->key, skkey, skkeylen); \
+ (skobject)->flags |= FSL_SKO_KEY_PRESENT; \
+ (skobject)->flags &= ~FSL_SKO_KEY_ESTABLISHED; \
+}
+
+/*!
+ * Set a size for the key.
+ *
+ * This function would normally be used when the user wants the key to be
+ * generated from a random source.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skkeylen The length, in octets, of the key. The value should be
+ * appropriate to the key size supported by the algorithm.
+ * 64 octets is the absolute maximum value allowed for this
+ * call.
+ */
+#define fsl_shw_sko_set_key_length(skobject, skkeylen) \
+ (skobject)->key_length = skkeylen;
+
+/*!
+ * Set the User ID associated with the key.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skuserid The User ID to identify authorized users of the key.
+ */
+#define fsl_shw_sko_set_user_id(skobject, skuserid) \
+ (skobject)->userid = (skuserid)
+
+/*!
+ * Establish a user Keystore to hold the key.
+ */
+#define fsl_shw_sko_set_keystore(skobject, user_keystore) \
+ (skobject)->keystore = (user_keystore)
+
+/*!
+ * Set the establish key handle into a key object.
+ *
+ * The @a userid field will be used to validate the access to the unwrapped
+ * key. This feature is not available for all platforms, nor for all
+ * algorithms and modes.
+ *
+ * The #FSL_SKO_KEY_ESTABLISHED will be set (and the #FSL_SKO_KEY_PRESENT flag
+ * will be cleared).
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skuserid The User ID to verify this user is an authorized user of
+ * the key.
+ * @param skhandle A @a handle from #fsl_shw_sko_get_established_info.
+ */
+#define fsl_shw_sko_set_established_info(skobject, skuserid, skhandle) \
+{ \
+ (skobject)->userid = (skuserid); \
+ (skobject)->handle = (skhandle); \
+ (skobject)->flags |= FSL_SKO_KEY_ESTABLISHED; \
+ (skobject)->flags &= \
+ ~(FSL_SKO_KEY_PRESENT); \
+}
+
+/*!
+ * Retrieve the established-key handle from a key object.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skhandle The location to store the @a handle of the unwrapped
+ * key.
+ */
+#define fsl_shw_sko_get_established_info(skobject, skhandle) \
+ *(skhandle) = (skobject)->handle
+
+/*!
+ * Extract the algorithm from a key object.
+ *
+ * @param skobject The Key Object to be queried.
+ * @param[out] skalgorithm A pointer to the location to store the algorithm.
+ */
+#define fsl_shw_sko_get_algorithm(skobject, skalgorithm) \
+ *(skalgorithm) = (skobject)->algorithm
+
+/*!
+ * Retrieve the cleartext key from a key object that is stored in a user
+ * keystore.
+ *
+ * @param skobject The Key Object to be queried.
+ * @param[out] skkey A pointer to the location to store the key. NULL
+ * if the key is not stored in a user keystore.
+ */
+#define fsl_shw_sko_get_key(skobject, skkey) \
+{ \
+ fsl_shw_kso_t* keystore = (skobject)->keystore; \
+ if (keystore != NULL) { \
+ *(skkey) = keystore->slot_get_address(keystore->user_data, \
+ (skobject)->handle); \
+ } else { \
+ *(skkey) = NULL; \
+ } \
+}
+
+/*!
+ * Determine the size of a wrapped key based upon the cleartext key's length.
+ *
+ * This function can be used to calculate the number of octets that
+ * #fsl_shw_extract_key() will write into the location at @a covered_key.
+ *
+ * If zero is returned at @a length, this means that the key length in
+ * @a key_info is not supported.
+ *
+ * @param wkeyinfo Information about a key to be wrapped.
+ * @param wkeylen Location to store the length of a wrapped
+ * version of the key in @a key_info.
+ */
+#define fsl_shw_sko_calculate_wrapped_size(wkeyinfo, wkeylen) \
+{ \
+ register fsl_shw_sko_t* kp = wkeyinfo; \
+ register uint32_t kl = kp->key_length; \
+ int key_blocks = (kl + 15) / 16; \
+ int base_size = 35; /* ICV + T' + ALG + LEN + FLAGS */ \
+ \
+ *(wkeylen) = base_size + 16 * key_blocks; \
+}
+
+/*!
+ * Set some flags in the key object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skflags (One or more) ORed members of #fsl_shw_key_flags_t which
+ * are to be set.
+ */
+#define fsl_shw_sko_set_flags(skobject, skflags) \
+ (skobject)->flags |= (skflags)
+
+/*!
+ * Clear some flags in the key object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param skobject A variable of type #fsl_shw_sko_t.
+ * @param skflags (One or more) ORed members of #fsl_shw_key_flags_t
+ * which are to be reset.
+ */
+#define fsl_shw_sko_clear_flags(skobject, skflags) \
+ (skobject)->flags &= ~(skflags)
+
+/*!
+ * Initialize a Hash Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the hash
+ * context object.
+ *
+ * @param hcobject The hash context to operate upon.
+ * @param hcalgorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+#define fsl_shw_hco_init(hcobject, hcalgorithm) \
+{ \
+ (hcobject)->algorithm = hcalgorithm; \
+ (hcobject)->flags = 0; \
+ switch (hcalgorithm) { \
+ case FSL_HASH_ALG_MD5: \
+ (hcobject)->digest_length = 16; \
+ (hcobject)->context_length = 16; \
+ (hcobject)->context_register_length = 24; \
+ break; \
+ case FSL_HASH_ALG_SHA1: \
+ (hcobject)->digest_length = 20; \
+ (hcobject)->context_length = 20; \
+ (hcobject)->context_register_length = 24; \
+ break; \
+ case FSL_HASH_ALG_SHA224: \
+ (hcobject)->digest_length = 28; \
+ (hcobject)->context_length = 32; \
+ (hcobject)->context_register_length = 36; \
+ break; \
+ case FSL_HASH_ALG_SHA256: \
+ (hcobject)->digest_length = 32; \
+ (hcobject)->context_length = 32; \
+ (hcobject)->context_register_length = 36; \
+ break; \
+ default: \
+ /* error ! */ \
+ (hcobject)->digest_length = 1; \
+ (hcobject)->context_length = 1; \
+ (hcobject)->context_register_length = 1; \
+ break; \
+ } \
+}
+
+/*!
+ * Get the current hash value and message length from the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hcobject The hash context to query.
+ * @param[out] hccontext Pointer to the location of @a length octets where to
+ * store a copy of the current value of the digest.
+ * @param hcclength Number of octets of hash value to copy.
+ * @param[out] hcmsglen Pointer to the location to store the number of octets
+ * already hashed.
+ */
+#define fsl_shw_hco_get_digest(hcobject, hccontext, hcclength, hcmsglen) \
+{ \
+ copy_bytes(hccontext, (hcobject)->context, hcclength); \
+ if ((hcobject)->algorithm == FSL_HASH_ALG_SHA224 \
+ || (hcobject)->algorithm == FSL_HASH_ALG_SHA256) { \
+ *(hcmsglen) = (hcobject)->context[8]; \
+ } else { \
+ *(hcmsglen) = (hcobject)->context[5]; \
+ } \
+}
+
+/*!
+ * Get the hash algorithm from the hash context object.
+ *
+ * @param hcobject The hash context to query.
+ * @param[out] hcalgorithm Pointer to where the algorithm is to be stored.
+ */
+#define fsl_shw_hco_get_info(hcobject, hcalgorithm) \
+{ \
+ *(hcalgorithm) = (hcobject)->algorithm; \
+}
+
+/*!
+ * Set the current hash value and message length in the hash context object.
+ *
+ * The algorithm must have already been specified. See #fsl_shw_hco_init().
+ *
+ * @param hcobject The hash context to operate upon.
+ * @param hccontext Pointer to buffer of appropriate length to copy into
+ * the hash context object.
+ * @param hcmsglen The number of octets of the message which have
+ * already been hashed.
+ *
+ */
+#define fsl_shw_hco_set_digest(hcobject, hccontext, hcmsglen) \
+{ \
+ copy_bytes((hcobject)->context, hccontext, (hcobject)->context_length); \
+ if (((hcobject)->algorithm == FSL_HASH_ALG_SHA224) \
+ || ((hcobject)->algorithm == FSL_HASH_ALG_SHA256)) { \
+ (hcobject)->context[8] = hcmsglen; \
+ } else { \
+ (hcobject)->context[5] = hcmsglen; \
+ } \
+}
+
+/*!
+ * Set flags in a Hash Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The hash context to be operated on.
+ * @param hcflags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+#define fsl_shw_hco_set_flags(hcobject, hcflags) \
+ (hcobject)->flags |= (hcflags)
+
+/*!
+ * Clear flags in a Hash Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The hash context to be operated on.
+ * @param hcflags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hash_ctx_flags_t.
+ */
+#define fsl_shw_hco_clear_flags(hcobject, hcflags) \
+ (hcobject)->flags &= ~(hcflags)
+
+/*!
+ * Initialize an HMAC Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. It sets the current message length and hash algorithm in the HMAC
+ * context object.
+ *
+ * @param hcobject The HMAC context to operate upon.
+ * @param hcalgorithm The hash algorithm to be used (#FSL_HASH_ALG_MD5,
+ * #FSL_HASH_ALG_SHA256, etc).
+ *
+ */
+#define fsl_shw_hmco_init(hcobject, hcalgorithm) \
+ fsl_shw_hco_init(hcobject, hcalgorithm)
+
+/*!
+ * Set flags in an HMAC Context Object.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The HMAC context to be operated on.
+ * @param hcflags The flags to be set in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+#define fsl_shw_hmco_set_flags(hcobject, hcflags) \
+ (hcobject)->flags |= (hcflags)
+
+/*!
+ * Clear flags in an HMAC Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param hcobject The HMAC context to be operated on.
+ * @param hcflags The flags to be reset in the context. These can be ORed
+ * members of #fsl_shw_hmac_ctx_flags_t.
+ */
+#define fsl_shw_hmco_clear_flags(hcobject, hcflags) \
+ (hcobject)->flags &= ~(hcflags)
+
+/*!
+ * Initialize a Symmetric Cipher Context Object.
+ *
+ * This function must be called before performing any other operation with the
+ * Object. This will set the @a mode and @a algorithm and initialize the
+ * Object.
+ *
+ * @param scobject The context object to operate on.
+ * @param scalg The cipher algorithm this context will be used with.
+ * @param scmode #FSL_SYM_MODE_CBC, #FSL_SYM_MODE_ECB, etc.
+ *
+ */
+#define fsl_shw_scco_init(scobject, scalg, scmode) \
+{ \
+ register uint32_t bsb; /* block-size bytes */ \
+ \
+ switch (scalg) { \
+ case FSL_KEY_ALG_AES: \
+ bsb = 16; \
+ break; \
+ case FSL_KEY_ALG_DES: \
+ /* fall through */ \
+ case FSL_KEY_ALG_TDES: \
+ bsb = 8; \
+ break; \
+ case FSL_KEY_ALG_ARC4: \
+ bsb = 259; \
+ break; \
+ case FSL_KEY_ALG_HMAC: \
+ bsb = 1; /* meaningless */ \
+ break; \
+ default: \
+ bsb = 00; \
+ } \
+ (scobject)->block_size_bytes = bsb; \
+ (scobject)->mode = scmode; \
+ (scobject)->flags = 0; \
+}
+
+/*!
+ * Set the flags for a Symmetric Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param scobject The context object to operate on.
+ * @param scflags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_scco_set_flags(scobject, scflags) \
+ (scobject)->flags |= (scflags)
+
+/*!
+ * Clear some flags in a Symmetric Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param scobject The context object to operate on.
+ * @param scflags The flags to reset (one or more values from
+ * #fsl_shw_sym_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_scco_clear_flags(scobject, scflags) \
+ (scobject)->flags &= ~(scflags)
+
+/*!
+ * Set the Context (IV) for a Symmetric Cipher Context.
+ *
+ * This is to set the context/IV for #FSL_SYM_MODE_CBC mode, or to set the
+ * context (the S-Box and pointers) for ARC4. The full context size will
+ * be copied.
+ *
+ * @param scobject The context object to operate on.
+ * @param sccontext A pointer to the buffer which contains the context.
+ *
+ */
+#define fsl_shw_scco_set_context(scobject, sccontext) \
+ copy_bytes((scobject)->context, sccontext, \
+ (scobject)->block_size_bytes)
+
+/*!
+ * Get the Context for a Symmetric Cipher Context.
+ *
+ * This is to retrieve the context/IV for #FSL_SYM_MODE_CBC mode, or to
+ * retrieve context (the S-Box and pointers) for ARC4. The full context
+ * will be copied.
+ *
+ * @param scobject The context object to operate on.
+ * @param[out] sccontext Pointer to location where context will be stored.
+ */
+#define fsl_shw_scco_get_context(scobject, sccontext) \
+ copy_bytes(sccontext, (scobject)->context, (scobject)->block_size_bytes)
+
+/*!
+ * Set the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will set the Counter Value for CTR mode.
+ *
+ * @param scobject The context object to operate on.
+ * @param sccounter The starting counter value. The number of octets.
+ * copied will be the block size for the algorithm.
+ * @param scmodulus The modulus for controlling the incrementing of the
+ * counter.
+ *
+ */
+#define fsl_shw_scco_set_counter_info(scobject, sccounter, scmodulus) \
+ { \
+ if ((sccounter) != NULL) { \
+ copy_bytes((scobject)->context, sccounter, \
+ (scobject)->block_size_bytes); \
+ } \
+ (scobject)->modulus_exp = scmodulus; \
+ }
+
+/*!
+ * Get the Counter Value for a Symmetric Cipher Context.
+ *
+ * This will retrieve the Counter Value is for CTR mode.
+ *
+ * @param scobject The context object to query.
+ * @param[out] sccounter Pointer to location to store the current counter
+ * value. The number of octets copied will be the
+ * block size for the algorithm.
+ * @param[out] scmodulus Pointer to location to store the modulus.
+ *
+ */
+#define fsl_shw_scco_get_counter_info(scobject, sccounter, scmodulus) \
+ { \
+ if ((sccounter) != NULL) { \
+ copy_bytes(sccounter, (scobject)->context, \
+ (scobject)->block_size_bytes); \
+ } \
+ if ((scmodulus) != NULL) { \
+ *(scmodulus) = (scobject)->modulus_exp; \
+ } \
+ }
+
+/*!
+ * Initialize a Authentication-Cipher Context.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acmode The mode for this object (only #FSL_ACC_MODE_CCM
+ * supported).
+ */
+#define fsl_shw_acco_init(acobject, acmode) \
+ { \
+ (acobject)->flags = 0; \
+ (acobject)->mode = (acmode); \
+ }
+
+/*!
+ * Set the flags for a Authentication-Cipher Context.
+ *
+ * Turns on the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acflags The flags to set (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_acco_set_flags(acobject, acflags) \
+ (acobject)->flags |= (acflags)
+
+/*!
+ * Clear some flags in a Authentication-Cipher Context Object.
+ *
+ * Turns off the flags specified in @a flags. Other flags are untouched.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acflags The flags to reset (one or more from
+ * #fsl_shw_auth_ctx_flags_t ORed together).
+ *
+ */
+#define fsl_shw_acco_clear_flags(acobject, acflags) \
+ (acobject)->flags &= ~(acflags)
+
+/*!
+ * Set up the Authentication-Cipher Object for CCM mode.
+ *
+ * This will set the @a auth_object for CCM mode and save the @a ctr,
+ * and @a mac_length. This function can be called instead of
+ * #fsl_shw_acco_init().
+ *
+ * The paramater @a ctr is Counter Block 0, (counter value 0), which is for the
+ * MAC.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acalg Cipher algorithm. Only AES is supported.
+ * @param accounter The initial counter value.
+ * @param acmaclen The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ */
+/* Do we need to stash the +1 value of the CTR somewhere? */
+#define fsl_shw_acco_set_ccm(acobject, acalg, accounter, acmaclen) \
+{ \
+ (acobject)->flags = 0; \
+ (acobject)->mode = FSL_ACC_MODE_CCM; \
+ (acobject)->auth_info.CCM_ctx_info.block_size_bytes = 16; \
+ (acobject)->cipher_ctx_info.block_size_bytes = 16; \
+ (acobject)->mac_length = acmaclen; \
+ fsl_shw_scco_set_counter_info(&(acobject)->cipher_ctx_info, accounter, \
+ FSL_CTR_MOD_128); \
+}
+
+/*!
+ * Format the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will also set the IV and CTR values per Appendix A of NIST
+ * Special Publication 800-38C (May 2004). It will also perform the
+ * #fsl_shw_acco_set_ccm() operation with information derived from this set of
+ * parameters.
+ *
+ * Note this function assumes the algorithm is AES. It initializes the
+ * @a auth_object by setting the mode to #FSL_ACC_MODE_CCM and setting the
+ * flags to be #FSL_ACCO_NIST_CCM.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param act The number of octets used for the MAC. Valid values are
+ * 4, 6, 8, 10, 12, 14, and 16.
+ * @param acad Number of octets of Associated Data (may be zero).
+ * @param acq A value for the size of the length of @a q field. Valid
+ * values are 1-8.
+ * @param acN The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param acQ The value of Q (size of the payload in octets).
+ *
+ */
+/* Do we need to stash the +1 value of the CTR somewhere? */
+#define fsl_shw_ccm_nist_format_ctr_and_iv(acobject, act, acad, acq, acN, acQ)\
+ { \
+ uint64_t Q = acQ; \
+ uint8_t bflag = ((acad)?0x40:0) | ((((act)-2)/2)<<3) | ((acq)-1); \
+ unsigned i; \
+ uint8_t* qptr = (acobject)->auth_info.CCM_ctx_info.context + 15; \
+ (acobject)->auth_info.CCM_ctx_info.block_size_bytes = 16; \
+ (acobject)->cipher_ctx_info.block_size_bytes = 16; \
+ (acobject)->mode = FSL_ACC_MODE_CCM; \
+ (acobject)->flags = FSL_ACCO_NIST_CCM; \
+ \
+ /* Store away the MAC length (after calculating actual value */ \
+ (acobject)->mac_length = (act); \
+ /* Set Flag field in Block 0 */ \
+ *((acobject)->auth_info.CCM_ctx_info.context) = bflag; \
+ /* Set Nonce field in Block 0 */ \
+ copy_bytes((acobject)->auth_info.CCM_ctx_info.context+1, acN, \
+ 15-(acq)); \
+ /* Set Flag field in ctr */ \
+ *((acobject)->cipher_ctx_info.context) = (acq)-1; \
+ /* Update the Q (payload length) field of Block0 */ \
+ (acobject)->q_length = acq; \
+ for (i = 0; i < (acq); i++) { \
+ *qptr-- = Q & 0xFF; \
+ Q >>= 8; \
+ } \
+ /* Set the Nonce field of the ctr */ \
+ copy_bytes((acobject)->cipher_ctx_info.context+1, acN, 15-(acq)); \
+ /* Clear the block counter field of the ctr */ \
+ memset((acobject)->cipher_ctx_info.context+16-(acq), 0, (acq)+1); \
+ }
+
+/*!
+ * Update the First Block (IV) & Initial Counter Value per NIST CCM.
+ *
+ * This function will set the IV and CTR values per Appendix A of NIST Special
+ * Publication 800-38C (May 2004).
+ *
+ * Note this function assumes that #fsl_shw_ccm_nist_format_ctr_and_iv() has
+ * previously been called on the @a auth_object.
+ *
+ * @param acobject Pointer to object to operate on.
+ * @param acN The Nonce (packet number or other changing value). Must
+ * be (15 - @a q_length) octets long.
+ * @param acQ The value of Q (size of the payload in octets).
+ *
+ */
+/* Do we need to stash the +1 value of the CTR somewhere? */
+#define fsl_shw_ccm_nist_update_ctr_and_iv(acobject, acN, acQ) \
+ { \
+ uint64_t Q = acQ; \
+ unsigned i; \
+ uint8_t* qptr = (acobject)->auth_info.CCM_ctx_info.context + 15; \
+ \
+ /* Update the Nonce field field of Block0 */ \
+ copy_bytes((acobject)->auth_info.CCM_ctx_info.context+1, acN, \
+ 15 - (acobject)->q_length); \
+ /* Update the Q (payload length) field of Block0 */ \
+ for (i = 0; i < (acobject)->q_length; i++) { \
+ *qptr-- = Q & 0xFF; \
+ Q >>= 8; \
+ } \
+ /* Update the Nonce field of the ctr */ \
+ copy_bytes((acobject)->cipher_ctx_info.context+1, acN, \
+ 15 - (acobject)->q_length); \
+ }
+
+/******************************************************************************
+ * Library functions
+ *****************************************************************************/
+/* REQ-S2LRD-PINTFC-API-GEN-003 */
+extern fsl_shw_pco_t *fsl_shw_get_capabilities(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-004 */
+extern fsl_shw_return_t fsl_shw_register_user(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-005 */
+extern fsl_shw_return_t fsl_shw_deregister_user(fsl_shw_uco_t * user_ctx);
+
+/* REQ-S2LRD-PINTFC-API-GEN-006 */
+extern fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t * user_ctx,
+ unsigned result_size,
+ fsl_shw_result_t results[],
+ unsigned *result_count);
+
+extern fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_key_wrap_t establish_type,
+ const uint8_t * key);
+
+extern fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * covered_key);
+
+extern fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info);
+
+extern void *fsl_shw_smalloc(fsl_shw_uco_t * user_ctx,
+ uint32_t size,
+ const uint8_t * UMID, uint32_t permissions);
+
+extern fsl_shw_return_t fsl_shw_sfree(fsl_shw_uco_t * user_ctx, void *address);
+
+extern fsl_shw_return_t fsl_shw_sstatus(fsl_shw_uco_t * user_ctx,
+ void *address,
+ fsl_shw_partition_status_t * status);
+
+extern fsl_shw_return_t fsl_shw_diminish_perms(fsl_shw_uco_t * user_ctx,
+ void *address,
+ uint32_t permissions);
+
+extern fsl_shw_return_t do_scc_engage_partition(fsl_shw_uco_t * user_ctx,
+ void *address,
+ const uint8_t * UMID,
+ uint32_t permissions);
+
+extern fsl_shw_return_t do_system_keystore_slot_alloc(fsl_shw_uco_t * user_ctx,
+ uint32_t key_lenth,
+ uint64_t ownerid,
+ uint32_t * slot);
+
+extern fsl_shw_return_t do_system_keystore_slot_dealloc(fsl_shw_uco_t *
+ user_ctx,
+ uint64_t ownerid,
+ uint32_t slot);
+
+extern fsl_shw_return_t do_system_keystore_slot_load(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ const uint8_t * key,
+ uint32_t key_length);
+
+extern fsl_shw_return_t do_system_keystore_slot_read(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t * key);
+
+extern fsl_shw_return_t do_system_keystore_slot_encrypt(fsl_shw_uco_t *
+ user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ uint8_t * black_data);
+
+extern fsl_shw_return_t do_system_keystore_slot_decrypt(fsl_shw_uco_t *
+ user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t *
+ black_data);
+
+extern fsl_shw_return_t
+do_scc_encrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode);
+
+extern fsl_shw_return_t
+do_scc_decrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, const uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode);
+
+extern fsl_shw_return_t
+system_keystore_get_slot_info(uint64_t owner_id, uint32_t slot,
+ uint32_t * address, uint32_t * slot_size_bytes);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+extern fsl_shw_return_t fsl_shw_symmetric_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * pt,
+ uint8_t * ct);
+
+/* PINTFC-API-BASIC-SYM-002 */
+/* PINTFC-API-BASIC-SYM-ARC4-001 */
+/* PINTFC-API-BASIC-SYM-ARC4-002 */
+extern fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_scco_t * sym_ctx,
+ uint32_t length,
+ const uint8_t * ct,
+ uint8_t * pt);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HASH-005 */
+extern fsl_shw_return_t fsl_shw_hash(fsl_shw_uco_t * user_ctx,
+ fsl_shw_hco_t * hash_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-001 */
+extern fsl_shw_return_t fsl_shw_hmac_precompute(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-HMAC-002 */
+extern fsl_shw_return_t fsl_shw_hmac(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ fsl_shw_hmco_t * hmac_ctx,
+ const uint8_t * msg,
+ uint32_t length,
+ uint8_t * result, uint32_t result_len);
+
+/* REQ-S2LRD-PINTFC-API-BASIC-RNG-002 */
+extern fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data);
+
+extern fsl_shw_return_t fsl_shw_add_entropy(fsl_shw_uco_t * user_ctx,
+ uint32_t length, uint8_t * data);
+
+extern fsl_shw_return_t fsl_shw_gen_encrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * payload,
+ uint8_t * ct, uint8_t * auth_value);
+
+extern fsl_shw_return_t fsl_shw_auth_decrypt(fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * cipher_key_info,
+ fsl_shw_sko_t * auth_key_info,
+ uint32_t auth_data_length,
+ const uint8_t * auth_data,
+ uint32_t payload_length,
+ const uint8_t * ct,
+ const uint8_t * auth_value,
+ uint8_t * payload);
+
+extern fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx,
+ fsl_shw_sko_t * key_info,
+ uint8_t * key);
+
+static inline fsl_shw_return_t fsl_shw_gen_random_pf_key(fsl_shw_uco_t *
+ user_ctx)
+{
+ (void)user_ctx;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+static inline fsl_shw_return_t fsl_shw_read_tamper_event(fsl_shw_uco_t *
+ user_ctx,
+ fsl_shw_tamper_t *
+ tamperp,
+ uint64_t * timestampp)
+{
+ (void)user_ctx;
+ (void)tamperp;
+ (void)timestampp;
+
+ return FSL_RETURN_NO_RESOURCE_S;
+}
+
+fsl_shw_return_t sah_Append_Desc(const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_head,
+ const uint32_t header,
+ sah_Link * link1, sah_Link * link2);
+
+/* Utility Function leftover from sahara1 API */
+void sah_Descriptor_Chain_Destroy(const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/* Utility Function leftover from sahara1 API */
+fsl_shw_return_t sah_Descriptor_Chain_Execute(sah_Head_Desc * desc_chain,
+ fsl_shw_uco_t * user_ctx);
+
+fsl_shw_return_t sah_Append_Link(const sah_Mem_Util * mu,
+ sah_Link * link,
+ uint8_t * p,
+ const size_t length,
+ const sah_Link_Flags flags);
+
+fsl_shw_return_t sah_Create_Link(const sah_Mem_Util * mu,
+ sah_Link ** link,
+ uint8_t * p,
+ const size_t length,
+ const sah_Link_Flags flags);
+
+fsl_shw_return_t sah_Create_Key_Link(const sah_Mem_Util * mu,
+ sah_Link ** link,
+ fsl_shw_sko_t * key_info);
+
+void sah_Destroy_Link(const sah_Mem_Util * mu, sah_Link * link);
+
+void sah_Postprocess_Results(fsl_shw_uco_t * user_ctx,
+ sah_results * result_info);
+
+#endif /* SAHARA2_API_H */
+
diff --git a/drivers/mxc/security/sahara2/include/sahara2_kernel.h b/drivers/mxc/security/sahara2/include/sahara2_kernel.h
new file mode 100644
index 000000000000..b833f0ab8f56
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sahara2_kernel.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#define DRIVER_NAME sahara2
+
+#define SAHARA_MAJOR_NODE 78
+
+#include "portable_os.h"
+
+#include "platform_abstractions.h"
+
+/* Forward-declare prototypes using signature macros */
+
+OS_DEV_ISR_DCL(sahara2_isr);
+
+OS_DEV_INIT_DCL(sahara2_init);
+
+OS_DEV_SHUTDOWN_DCL(sahara2_shutdown);
+
+OS_DEV_OPEN_DCL(sahara2_open);
+
+OS_DEV_CLOSE_DCL(sahara2_release);
+
+OS_DEV_IOCTL_DCL(sahara2_ioctl);
+
+struct sahara2_kernel_user {
+ void *command_ring[32];
+};
+
+struct sahara2_sym_arg {
+ char *key;
+ unsigned key_len;
+};
+
+/*! These need to be added to Linux / OS abstractions */
+/*
+module_init(OS_DEV_INIT_REF(sahara2_init));
+module_cleanup(OS_DEV_SHUTDOWN_REF(sahara2_shutdown));
+*/
diff --git a/drivers/mxc/security/sahara2/include/sf_util.h b/drivers/mxc/security/sahara2/include/sf_util.h
new file mode 100644
index 000000000000..c0af0c96ff02
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sf_util.h
@@ -0,0 +1,466 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+* @file sf_util.h
+*
+* @brief Header for Sahara Descriptor-chain building Functions
+*/
+#ifndef SF_UTIL_H
+#define SF_UTIL_H
+
+#include <fsl_platform.h>
+#include <sahara.h>
+
+/*! Header value for Sahara Descriptor 1 */
+#define SAH_HDR_SKHA_SET_MODE_IV_KEY 0x10880000
+/*! Header value for Sahara Descriptor 2 */
+#define SAH_HDR_SKHA_SET_MODE_ENC_DEC 0x108D0000
+/*! Header value for Sahara Descriptor 4 */
+#define SAH_HDR_SKHA_ENC_DEC 0x90850000
+/*! Header value for Sahara Descriptor 5 */
+#define SAH_HDR_SKHA_READ_CONTEXT_IV 0x10820000
+/*! Header value for Sahara Descriptor 6 */
+#define SAH_HDR_MDHA_SET_MODE_MD_KEY 0x20880000
+/*! Header value for Sahara Descriptor 8 */
+#define SAH_HDR_MDHA_SET_MODE_HASH 0x208D0000
+/*! Header value for Sahara Descriptor 10 */
+#define SAH_HDR_MDHA_HASH 0xA0850000
+/*! Header value for Sahara Descriptor 11 */
+#define SAH_HDR_MDHA_STORE_DIGEST 0x20820000
+/*! Header value for Sahara Descriptor 18 */
+#define SAH_HDR_RNG_GENERATE 0x308C0000
+/*! Header value for Sahara Descriptor 19 */
+#define SAH_HDR_PKHA_LD_N_E 0xC0800000
+/*! Header value for Sahara Descriptor 20 */
+#define SAH_HDR_PKHA_LD_A_EX_ST_B 0x408D0000
+/*! Header value for Sahara Descriptor 21 */
+#define SAH_HDR_PKHA_LD_N_EX_ST_B 0x408E0000
+/*! Header value for Sahara Descriptor 22 */
+#define SAH_HDR_PKHA_LD_A_B 0xC0830000
+/*! Header value for Sahara Descriptor 23 */
+#define SAH_HDR_PKHA_LD_A0_A1 0x40840000
+/*! Header value for Sahara Descriptor 24 */
+#define SAH_HDR_PKHA_LD_A2_A3 0xC0850000
+/*! Header value for Sahara Descriptor 25 */
+#define SAH_HDR_PKHA_LD_B0_B1 0xC0860000
+/*! Header value for Sahara Descriptor 26 */
+#define SAH_HDR_PKHA_LD_B2_B3 0x40870000
+/*! Header value for Sahara Descriptor 27 */
+#define SAH_HDR_PKHA_ST_A_B 0x40820000
+/*! Header value for Sahara Descriptor 28 */
+#define SAH_HDR_PKHA_ST_A0_A1 0x40880000
+/*! Header value for Sahara Descriptor 29 */
+#define SAH_HDR_PKHA_ST_A2_A3 0xC0890000
+/*! Header value for Sahara Descriptor 30 */
+#define SAH_HDR_PKHA_ST_B0_B1 0xC08A0000
+/*! Header value for Sahara Descriptor 31 */
+#define SAH_HDR_PKHA_ST_B2_B3 0x408B0000
+/*! Header value for Sahara Descriptor 32 */
+#define SAH_HDR_PKHA_EX_ST_B1 0xC08C0000
+/*! Header value for Sahara Descriptor 33 */
+#define SAH_HDR_ARC4_SET_MODE_SBOX 0x90890000
+/*! Header value for Sahara Descriptor 34 */
+#define SAH_HDR_ARC4_READ_SBOX 0x90860000
+/*! Header value for Sahara Descriptor 35 */
+#define SAH_HDR_ARC4_SET_MODE_KEY 0x90830000
+/*! Header value for Sahara Descriptor 36 */
+#define SAH_HDR_PKHA_LD_A3_B0 0x40810000
+/*! Header value for Sahara Descriptor 37 */
+#define SAH_HDR_PKHA_ST_B1_B2 0xC08F0000
+/*! Header value for Sahara Descriptor 38 */
+#define SAH_HDR_SKHA_CBC_ICV 0x10840000
+/*! Header value for Sahara Descriptor 39 */
+#define SAH_HDR_MDHA_ICV_CHECK 0xA08A0000
+
+/*! Header bit indicating "Link-List optimization" */
+#define SAH_HDR_LLO 0x01000000
+
+#define SAH_SF_DCLS \
+ fsl_shw_return_t ret; \
+ unsigned sf_executed = 0; \
+ sah_Head_Desc* desc_chain = NULL; \
+ uint32_t header
+
+#define SAH_SF_USER_CHECK() \
+do { \
+ ret = sah_validate_uco(user_ctx); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+} while (0)
+
+#define SAH_SF_EXECUTE() \
+do { \
+ sf_executed = 1; \
+ ret = sah_Descriptor_Chain_Execute(desc_chain, user_ctx); \
+} while (0)
+
+#define SAH_SF_DESC_CLEAN() \
+do { \
+ if (!sf_executed || (user_ctx->flags & FSL_UCO_BLOCKING_MODE)) { \
+ sah_Descriptor_Chain_Destroy(user_ctx->mem_util, &desc_chain); \
+ } \
+ (void) header; \
+} while (0)
+
+/*! Add Descriptor with two inputs */
+#define DESC_IN_IN(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_two_in_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with two vectors */
+#define DESC_D_D(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_two_d_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with input and a key */
+#define DESC_IN_KEY(hdr, len1, ptr1, key2) \
+{ \
+ ret = sah_add_in_key_desc(hdr, ptr1, len1, key2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with input and an output */
+#define DESC_IN_OUT(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_in_out_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with input and a key output */
+#define DESC_IN_KEYOUT(hdr, len1, ptr1, key2) \
+{ \
+ ret = sah_add_in_keyout_desc(hdr, ptr1, len1, key2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with a key and an output */
+#define DESC_KEY_OUT(hdr, key1, len2, ptr2) \
+{ \
+ ret = sah_add_key_out_desc(hdr, key1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with two outputs */
+#define DESC_OUT_OUT(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_two_out_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+/*! Add Descriptor with output then input pointers */
+#define DESC_OUT_IN(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_out_in_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+
+#ifdef SAH_SF_DEBUG
+/*! Add Descriptor with two outputs */
+#define DBG_DESC(hdr, len1, ptr1, len2, ptr2) \
+{ \
+ ret = sah_add_two_out_desc(hdr, ptr1, len1, ptr2, len2, \
+ user_ctx->mem_util, &desc_chain); \
+ if (ret != FSL_RETURN_OK_S) { \
+ goto out; \
+ } \
+}
+#else
+#define DBG_DESC(hdr, len1, ptr1, len2, ptr2)
+#endif
+
+#ifdef __KERNEL__
+#define DESC_DBG_ON ({console_loglevel = 8;})
+#define DESC_DBG_OFF ({console_loglevel = 7;})
+#else
+#define DESC_DBG_ON system("echo 8 > /proc/sys/kernel/printk")
+#define DESC_DBG_OFF system("echo 7 > /proc/sys/kernel/printk")
+#endif
+
+#define DESC_TEMP_ALLOC(size) \
+({ \
+ uint8_t* ptr; \
+ ptr = user_ctx->mem_util->mu_malloc(user_ctx->mem_util->mu_ref, \
+ size); \
+ if (ptr == NULL) { \
+ ret = FSL_RETURN_NO_RESOURCE_S; \
+ goto out; \
+ } \
+ \
+ ptr; \
+})
+
+#define DESC_TEMP_FREE(ptr) \
+({ \
+ if ((ptr != NULL) && \
+ (!sf_executed || (user_ctx->flags & FSL_UCO_BLOCKING_MODE))) { \
+ user_ctx->mem_util-> \
+ mu_free(user_ctx->mem_util->mu_ref, ptr); \
+ ptr = NULL; \
+ } \
+})
+
+/* Temporary implementation. This needs to be in internal/secure RAM */
+#define DESC_TEMP_SECURE_ALLOC(size) \
+({ \
+ uint8_t* ptr; \
+ ptr = user_ctx->mem_util->mu_malloc(user_ctx->mem_util->mu_ref, \
+ size); \
+ if (ptr == NULL) { \
+ ret = FSL_RETURN_NO_RESOURCE_S; \
+ goto out; \
+ } \
+ \
+ ptr; \
+})
+
+#define DESC_TEMP_SECURE_FREE(ptr, size) \
+({ \
+ if ((ptr != NULL) && \
+ (!sf_executed || (user_ctx->flags & FSL_UCO_BLOCKING_MODE))) { \
+ user_ctx->mem_util->mu_memset(user_ctx->mem_util->mu_ref, \
+ ptr, 0, size); \
+ \
+ user_ctx->mem_util-> \
+ mu_free(user_ctx->mem_util->mu_ref, ptr); \
+ ptr = NULL; \
+ } \
+})
+
+extern const uint32_t sah_insert_mdha_algorithm[];
+
+/*! @defgroup mdhaflags MDHA Mode Register Values
+ *
+ * These are bit fields and combinations of bit fields for setting the Mode
+ * Register portion of a Sahara Descriptor Header field.
+ *
+ * The parity bit has been set to ensure that these values have even parity,
+ * therefore using an Exclusive-OR operation against an existing header will
+ * preserve its parity.
+ *
+ * @addtogroup mdhaflags
+ @{
+ */
+#define sah_insert_mdha_icv_check 0x80001000
+#define sah_insert_mdha_ssl 0x80000400
+#define sah_insert_mdha_mac_full 0x80000200
+#define sah_insert_mdha_opad 0x80000080
+#define sah_insert_mdha_ipad 0x80000040
+#define sah_insert_mdha_init 0x80000020
+#define sah_insert_mdha_hmac 0x80000008
+#define sah_insert_mdha_pdata 0x80000004
+#define sah_insert_mdha_algorithm_sha224 0x00000003
+#define sah_insert_mdha_algorithm_sha256 0x80000002
+#define sah_insert_mdha_algorithm_md5 0x80000001
+#define sah_insert_mdha_algorithm_sha1 0x00000000
+/*! @} */
+
+extern const uint32_t sah_insert_skha_algorithm[];
+extern const uint32_t sah_insert_skha_mode[];
+extern const uint32_t sah_insert_skha_modulus[];
+
+/*! @defgroup skhaflags SKHA Mode Register Values
+ *
+ * These are bit fields and combinations of bit fields for setting the Mode
+ * Register portion of a Sahara Descriptor Header field.
+ *
+ * The parity bit has been set to ensure that these values have even parity,
+ * therefore using an Exclusive-OR operation against an existing header will
+ * preserve its parity.
+ *
+ * @addtogroup skhaflags
+ @{
+ */
+/*! */
+#define sah_insert_skha_modulus_128 0x00001e00
+#define sah_insert_skha_no_key_parity 0x80000100
+#define sah_insert_skha_ctr_last_block 0x80000020
+#define sah_insert_skha_suppress_cbc 0x80000020
+#define sah_insert_skha_no_permute 0x80000020
+#define sah_insert_skha_algorithm_arc4 0x00000003
+#define sah_insert_skha_algorithm_tdes 0x80000002
+#define sah_insert_skha_algorithm_des 0x80000001
+#define sah_insert_skha_algorithm_aes 0x00000000
+#define sah_insert_skha_aux0 0x80000020
+#define sah_insert_skha_mode_ctr 0x00000018
+#define sah_insert_skha_mode_ccm 0x80000010
+#define sah_insert_skha_mode_cbc 0x80000008
+#define sah_insert_skha_mode_ecb 0x00000000
+#define sah_insert_skha_encrypt 0x80000004
+#define sah_insert_skha_decrypt 0x00000000
+/*! @} */
+
+/*! @defgroup rngflags RNG Mode Register Values
+ *
+ */
+/*! */
+#define sah_insert_rng_gen_seed 0x80000001
+
+/*! @} */
+
+/*! @defgroup pkhaflags PKHA Mode Register Values
+ *
+ */
+/*! */
+#define sah_insert_pkha_soft_err_false 0x80000200
+#define sah_insert_pkha_soft_err_true 0x80000100
+
+#define sah_insert_pkha_rtn_clr_mem 0x80000001
+#define sah_insert_pkha_rtn_clr_eram 0x80000002
+#define sah_insert_pkha_rtn_mod_exp 0x00000003
+#define sah_insert_pkha_rtn_mod_r2modn 0x80000004
+#define sah_insert_pkha_rtn_mod_rrmodp 0x00000005
+#define sah_insert_pkha_rtn_ec_fp_aff_ptmul 0x00000006
+#define sah_insert_pkha_rtn_ec_f2m_aff_ptmul 0x80000007
+#define sah_insert_pkha_rtn_ec_fp_proj_ptmul 0x80000008
+#define sah_insert_pkha_rtn_ec_f2m_proj_ptmul 0x00000009
+#define sah_insert_pkha_rtn_ec_fp_add 0x0000000A
+#define sah_insert_pkha_rtn_ec_fp_double 0x8000000B
+#define sah_insert_pkha_rtn_ec_f2m_add 0x0000000C
+#define sah_insert_pkha_rtn_ec_f2m_double 0x8000000D
+#define sah_insert_pkha_rtn_f2m_r2modn 0x8000000E
+#define sah_insert_pkha_rtn_f2m_inv 0x0000000F
+#define sah_insert_pkha_rtn_mod_inv 0x80000010
+#define sah_insert_pkha_rtn_rsa_sstep 0x00000011
+#define sah_insert_pkha_rtn_mod_emodn 0x00000012
+#define sah_insert_pkha_rtn_f2m_emodn 0x80000013
+#define sah_insert_pkha_rtn_ec_fp_ptmul 0x00000014
+#define sah_insert_pkha_rtn_ec_f2m_ptmul 0x80000015
+#define sah_insert_pkha_rtn_f2m_gcd 0x80000016
+#define sah_insert_pkha_rtn_mod_gcd 0x00000017
+#define sah_insert_pkha_rtn_f2m_dbl_aff 0x00000018
+#define sah_insert_pkha_rtn_fp_dbl_aff 0x80000019
+#define sah_insert_pkha_rtn_f2m_add_aff 0x8000001A
+#define sah_insert_pkha_rtn_fp_add_aff 0x0000001B
+#define sah_insert_pkha_rtn_f2m_exp 0x8000001C
+#define sah_insert_pkha_rtn_mod_exp_teq 0x0000001D
+#define sah_insert_pkha_rtn_rsa_sstep_teq 0x0000001E
+#define sah_insert_pkha_rtn_f2m_multn 0x8000001F
+#define sah_insert_pkha_rtn_mod_multn 0x80000020
+#define sah_insert_pkha_rtn_mod_add 0x00000021
+#define sah_insert_pkha_rtn_mod_sub 0x00000022
+#define sah_insert_pkha_rtn_mod_mult1_mont 0x80000023
+#define sah_insert_pkha_rtn_mod_mult2_deconv 0x00000024
+#define sah_insert_pkha_rtn_f2m_add 0x80000025
+#define sah_insert_pkha_rtn_f2m_mult1_mont 0x80000026
+#define sah_insert_pkha_rtn_f2m_mult2_deconv 0x00000027
+#define sah_insert_pkha_rtn_miller_rabin 0x00000028
+#define sah_insert_pkha_rtn_mod_amodn 0x00000029
+#define sah_insert_pkha_rtn_f2m_amodn 0x8000002A
+/*! @} */
+
+/*! Add a descriptor with two input pointers */
+fsl_shw_return_t sah_add_two_in_desc(uint32_t header,
+ const uint8_t * in1,
+ uint32_t in1_length,
+ const uint8_t * in2,
+ uint32_t in2_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with two 'data' pointers */
+fsl_shw_return_t sah_add_two_d_desc(uint32_t header,
+ const uint8_t * in1,
+ uint32_t in1_length,
+ const uint8_t * in2,
+ uint32_t in2_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with an input and key pointer */
+fsl_shw_return_t sah_add_in_key_desc(uint32_t header,
+ const uint8_t * in1,
+ uint32_t in1_length,
+ fsl_shw_sko_t * key_info,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with two key pointers */
+fsl_shw_return_t sah_add_key_key_desc(uint32_t header,
+ fsl_shw_sko_t * key_info1,
+ fsl_shw_sko_t * key_info2,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with two output pointers */
+fsl_shw_return_t sah_add_two_out_desc(uint32_t header,
+ uint8_t * out1,
+ uint32_t out1_length,
+ uint8_t * out2,
+ uint32_t out2_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with an input and output pointer */
+fsl_shw_return_t sah_add_in_out_desc(uint32_t header,
+ const uint8_t * in, uint32_t in_length,
+ uint8_t * out, uint32_t out_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with an input and key output pointer */
+fsl_shw_return_t sah_add_in_keyout_desc(uint32_t header,
+ const uint8_t * in, uint32_t in_length,
+ fsl_shw_sko_t * key_info,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with a key and an output pointer */
+fsl_shw_return_t sah_add_key_out_desc(uint32_t header,
+ const fsl_shw_sko_t * key_info,
+ uint8_t * out, uint32_t out_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Add a descriptor with an output and input pointer */
+fsl_shw_return_t sah_add_out_in_desc(uint32_t header,
+ uint8_t * out, uint32_t out_length,
+ const uint8_t * in, uint32_t in_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain);
+
+/*! Verify that supplied User Context Object is valid */
+fsl_shw_return_t sah_validate_uco(fsl_shw_uco_t * uco);
+
+#endif /* SF_UTIL_H */
+
+/* End of sf_util.h */
diff --git a/drivers/mxc/security/sahara2/km_adaptor.c b/drivers/mxc/security/sahara2/km_adaptor.c
new file mode 100644
index 000000000000..2cec1b8d0f98
--- /dev/null
+++ b/drivers/mxc/security/sahara2/km_adaptor.c
@@ -0,0 +1,849 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+* @file km_adaptor.c
+*
+* @brief The Adaptor component provides an interface to the
+* driver for a kernel user.
+*/
+
+#include <adaptor.h>
+#include <sf_util.h>
+#include <sah_queue_manager.h>
+#include <sah_memory_mapper.h>
+#include <fsl_shw_keystore.h>
+#ifdef FSL_HAVE_SCC
+#include <linux/mxc_scc_driver.h>
+#elif defined (FSL_HAVE_SCC2)
+#include <linux/mxc_scc2_driver.h>
+#endif
+
+
+EXPORT_SYMBOL(adaptor_Exec_Descriptor_Chain);
+EXPORT_SYMBOL(sah_register);
+EXPORT_SYMBOL(sah_deregister);
+EXPORT_SYMBOL(sah_get_results);
+EXPORT_SYMBOL(fsl_shw_smalloc);
+EXPORT_SYMBOL(fsl_shw_sfree);
+EXPORT_SYMBOL(fsl_shw_sstatus);
+EXPORT_SYMBOL(fsl_shw_diminish_perms);
+EXPORT_SYMBOL(do_scc_encrypt_region);
+EXPORT_SYMBOL(do_scc_decrypt_region);
+EXPORT_SYMBOL(do_system_keystore_slot_alloc);
+EXPORT_SYMBOL(do_system_keystore_slot_dealloc);
+EXPORT_SYMBOL(do_system_keystore_slot_load);
+EXPORT_SYMBOL(do_system_keystore_slot_read);
+EXPORT_SYMBOL(do_system_keystore_slot_encrypt);
+EXPORT_SYMBOL(do_system_keystore_slot_decrypt);
+
+
+#if defined(DIAG_DRV_IF) || defined(DIAG_MEM) || defined(DIAG_ADAPTOR)
+#include <diagnostic.h>
+#endif
+
+#if defined(DIAG_DRV_IF) || defined(DIAG_MEM) || defined(DIAG_ADAPTOR)
+#define MAX_DUMP 16
+
+#define DIAG_MSG_SIZE 300
+static char Diag_msg[DIAG_MSG_SIZE];
+#endif
+
+/* This is the wait queue to this mode of driver */
+DECLARE_WAIT_QUEUE_HEAD(Wait_queue_km);
+
+/*! This matches Sahara2 capabilities... */
+fsl_shw_pco_t sahara2_capabilities = {
+ 1, 3, /* api version number - major & minor */
+ 1, 6, /* driver version number - major & minor */
+ {
+ FSL_KEY_ALG_AES,
+ FSL_KEY_ALG_DES,
+ FSL_KEY_ALG_TDES,
+ FSL_KEY_ALG_ARC4},
+ {
+ FSL_SYM_MODE_STREAM,
+ FSL_SYM_MODE_ECB,
+ FSL_SYM_MODE_CBC,
+ FSL_SYM_MODE_CTR},
+ {
+ FSL_HASH_ALG_MD5,
+ FSL_HASH_ALG_SHA1,
+ FSL_HASH_ALG_SHA224,
+ FSL_HASH_ALG_SHA256},
+ /*
+ * The following table must be set to handle all values of key algorithm
+ * and sym mode, and be in the correct order..
+ */
+ { /* Stream, ECB, CBC, CTR */
+ {0, 0, 0, 0}, /* HMAC */
+ {0, 1, 1, 1}, /* AES */
+ {0, 1, 1, 0}, /* DES */
+ {0, 1, 1, 0}, /* 3DES */
+ {1, 0, 0, 0} /* ARC4 */
+ },
+ 0, 0,
+ 0, 0, 0,
+ {{0, 0}}
+};
+
+#ifdef DIAG_ADAPTOR
+void km_Dump_Chain(const sah_Desc * chain);
+
+void km_Dump_Region(const char *prefix, const unsigned char *data,
+ unsigned length);
+
+static void km_Dump_Link(const char *prefix, const sah_Link * link);
+
+void km_Dump_Words(const char *prefix, const unsigned *data, unsigned length);
+#endif
+
+/**** Memory routines ****/
+
+static void *my_malloc(void *ref, size_t n)
+{
+ register void *mem;
+
+#ifndef DIAG_MEM_ERRORS
+ mem = os_alloc_memory(n, GFP_KERNEL);
+
+#else
+ {
+ uint32_t rand;
+ /* are we feeling lucky ? */
+ os_get_random_bytes(&rand, sizeof(rand));
+ if ((rand % DIAG_MEM_CONST) == 0) {
+ mem = 0;
+ } else {
+ mem = os_alloc_memory(n, GFP_ATOMIC);
+ }
+ }
+#endif /* DIAG_MEM_ERRORS */
+
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "API kmalloc: %p for %d\n", mem, n);
+ LOG_KDIAG(Diag_msg);
+#endif
+ ref = 0; /* unused param warning */
+ return mem;
+}
+
+static sah_Head_Desc *my_alloc_head_desc(void *ref)
+{
+ register sah_Head_Desc *ptr;
+
+#ifndef DIAG_MEM_ERRORS
+ ptr = sah_Alloc_Head_Descriptor();
+
+#else
+ {
+ uint32_t rand;
+ /* are we feeling lucky ? */
+ os_get_random_bytes(&rand, sizeof(rand));
+ if ((rand % DIAG_MEM_CONST) == 0) {
+ ptr = 0;
+ } else {
+ ptr = sah_Alloc_Head_Descriptor();
+ }
+ }
+#endif
+ ref = 0;
+ return ptr;
+}
+
+static sah_Desc *my_alloc_desc(void *ref)
+{
+ register sah_Desc *ptr;
+
+#ifndef DIAG_MEM_ERRORS
+ ptr = sah_Alloc_Descriptor();
+
+#else
+ {
+ uint32_t rand;
+ /* are we feeling lucky ? */
+ os_get_random_bytes(&rand, sizeof(rand));
+ if ((rand % DIAG_MEM_CONST) == 0) {
+ ptr = 0;
+ } else {
+ ptr = sah_Alloc_Descriptor();
+ }
+ }
+#endif
+ ref = 0;
+ return ptr;
+}
+
+static sah_Link *my_alloc_link(void *ref)
+{
+ register sah_Link *ptr;
+
+#ifndef DIAG_MEM_ERRORS
+ ptr = sah_Alloc_Link();
+
+#else
+ {
+ uint32_t rand;
+ /* are we feeling lucky ? */
+ os_get_random_bytes(&rand, sizeof(rand));
+ if ((rand % DIAG_MEM_CONST) == 0) {
+ ptr = 0;
+ } else {
+ ptr = sah_Alloc_Link();
+ }
+ }
+#endif
+ ref = 0;
+ return ptr;
+}
+
+static void my_free(void *ref, void *ptr)
+{
+ ref = 0; /* unused param warning */
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "API kfree: %p\n", ptr);
+ LOG_KDIAG(Diag_msg);
+#endif
+ os_free_memory(ptr);
+}
+
+static void my_free_head_desc(void *ref, sah_Head_Desc * ptr)
+{
+ sah_Free_Head_Descriptor(ptr);
+}
+
+static void my_free_desc(void *ref, sah_Desc * ptr)
+{
+ sah_Free_Descriptor(ptr);
+}
+
+static void my_free_link(void *ref, sah_Link * ptr)
+{
+ sah_Free_Link(ptr);
+}
+
+static void *my_memcpy(void *ref, void *dest, const void *src, size_t n)
+{
+ ref = 0; /* unused param warning */
+ return memcpy(dest, src, n);
+}
+
+static void *my_memset(void *ref, void *ptr, int ch, size_t n)
+{
+ ref = 0; /* unused param warning */
+ return memset(ptr, ch, n);
+}
+
+/*! Standard memory manipulation routines for kernel API. */
+static sah_Mem_Util std_kernelmode_mem_util = {
+ .mu_ref = 0,
+ .mu_malloc = my_malloc,
+ .mu_alloc_head_desc = my_alloc_head_desc,
+ .mu_alloc_desc = my_alloc_desc,
+ .mu_alloc_link = my_alloc_link,
+ .mu_free = my_free,
+ .mu_free_head_desc = my_free_head_desc,
+ .mu_free_desc = my_free_desc,
+ .mu_free_link = my_free_link,
+ .mu_memcpy = my_memcpy,
+ .mu_memset = my_memset
+};
+
+fsl_shw_return_t get_capabilities(fsl_shw_uco_t * user_ctx,
+ fsl_shw_pco_t * capabilities)
+{
+ scc_config_t *scc_capabilities;
+
+ /* Fill in the Sahara2 capabilities. */
+ memcpy(capabilities, &sahara2_capabilities, sizeof(fsl_shw_pco_t));
+
+ /* Fill in the SCC portion of the capabilities object */
+ scc_capabilities = scc_get_configuration();
+ capabilities->scc_driver_major = scc_capabilities->driver_major_version;
+ capabilities->scc_driver_minor = scc_capabilities->driver_minor_version;
+ capabilities->scm_version = scc_capabilities->scm_version;
+ capabilities->smn_version = scc_capabilities->smn_version;
+ capabilities->block_size_bytes = scc_capabilities->block_size_bytes;
+
+#ifdef FSL_HAVE_SCC
+ capabilities->scc_info.black_ram_size_blocks =
+ scc_capabilities->black_ram_size_blocks;
+ capabilities->scc_info.red_ram_size_blocks =
+ scc_capabilities->red_ram_size_blocks;
+#elif defined(FSL_HAVE_SCC2)
+ capabilities->scc2_info.partition_size_bytes =
+ scc_capabilities->partition_size_bytes;
+ capabilities->scc2_info.partition_count =
+ scc_capabilities->partition_count;
+#endif
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * Sends a request to register this user
+ *
+ * @brief Sends a request to register this user
+ *
+ * @param[in,out] user_ctx part of the structure contains input parameters and
+ * part is filled in by the driver
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_register(fsl_shw_uco_t * user_ctx)
+{
+ fsl_shw_return_t status;
+
+ /* this field is used in user mode to indicate a file open has occured.
+ * it is used here, in kernel mode, to indicate that the uco is registered
+ */
+ user_ctx->sahara_openfd = 0; /* set to 'registered' */
+ user_ctx->mem_util = &std_kernelmode_mem_util;
+
+ /* check that uco is valid */
+ status = sah_validate_uco(user_ctx);
+
+ /* If life is good, register this user */
+ if (status == FSL_RETURN_OK_S) {
+ status = sah_handle_registration(user_ctx);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ user_ctx->sahara_openfd = -1; /* set to 'not registered' */
+ }
+
+ return status;
+}
+
+/*!
+ * Sends a request to deregister this user
+ *
+ * @brief Sends a request to deregister this user
+ *
+ * @param[in,out] user_ctx Info on user being deregistered.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_deregister(fsl_shw_uco_t * user_ctx)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+
+ if (user_ctx->sahara_openfd == 0) {
+ status = sah_handle_deregistration(user_ctx);
+ user_ctx->sahara_openfd = -1; /* set to 'no registered */
+ }
+
+ return status;
+}
+
+/*!
+ * Sends a request to get results for this user
+ *
+ * @brief Sends a request to get results for this user
+ *
+ * @param[in,out] arg Pointer to structure to collect results
+ * @param uco User's context
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_get_results(sah_results * arg, fsl_shw_uco_t * uco)
+{
+ fsl_shw_return_t code = sah_get_results_from_pool(uco, arg);
+
+ if ((code == FSL_RETURN_OK_S) && (arg->actual != 0)) {
+ sah_Postprocess_Results(uco, arg);
+ }
+
+ return code;
+}
+
+/*!
+ * This function writes the Descriptor Chain to the kernel driver.
+ *
+ * @brief Writes the Descriptor Chain to the kernel driver.
+ *
+ * @param dar A pointer to a Descriptor Chain of type sah_Head_Desc
+ * @param uco The user context object
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t adaptor_Exec_Descriptor_Chain(sah_Head_Desc * dar,
+ fsl_shw_uco_t * uco)
+{
+ sah_Head_Desc *kernel_space_desc = NULL;
+ fsl_shw_return_t code = FSL_RETURN_OK_S;
+ int os_error_code = 0;
+ unsigned blocking_mode = dar->uco_flags & FSL_UCO_BLOCKING_MODE;
+
+#ifdef DIAG_ADAPTOR
+ km_Dump_Chain(&dar->desc);
+#endif
+
+ dar->user_info = uco;
+ dar->user_desc = dar;
+
+ /* This code has been shamelessly copied from sah_driver_interface.c */
+ /* It needs to be moved somewhere common ... */
+ kernel_space_desc = sah_Physicalise_Descriptors(dar);
+
+ if (kernel_space_desc == NULL) {
+ /* We may have failed due to a -EFAULT as well, but we will return
+ * -ENOMEM since either way it is a memory related failure. */
+ code = FSL_RETURN_NO_RESOURCE_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Physicalise_Descriptors() failed\n");
+#endif
+ } else {
+ if (blocking_mode) {
+#ifdef SAHARA_POLL_MODE
+ os_error_code = sah_Handle_Poll(dar);
+#else
+ os_error_code = sah_blocking_mode(dar);
+#endif
+ if (os_error_code != 0) {
+ code = FSL_RETURN_ERROR_S;
+ } else { /* status of actual operation */
+ code = dar->result;
+ }
+ } else {
+#ifdef SAHARA_POLL_MODE
+ sah_Handle_Poll(dar);
+#else
+ /* just put someting in the DAR */
+ sah_Queue_Manager_Append_Entry(dar);
+#endif /* SAHARA_POLL_MODE */
+ }
+ }
+
+ return code;
+}
+
+
+/* System keystore context, defined in sah_driver_interface.c */
+extern fsl_shw_kso_t system_keystore;
+
+fsl_shw_return_t do_system_keystore_slot_alloc(fsl_shw_uco_t * user_ctx,
+ uint32_t key_length,
+ uint64_t ownerid,
+ uint32_t * slot)
+{
+ (void)user_ctx;
+ return keystore_slot_alloc(&system_keystore, key_length, ownerid, slot);
+}
+
+fsl_shw_return_t do_system_keystore_slot_dealloc(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot)
+{
+ (void)user_ctx;
+ return keystore_slot_dealloc(&system_keystore, ownerid, slot);
+}
+
+fsl_shw_return_t do_system_keystore_slot_load(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ const uint8_t * key,
+ uint32_t key_length)
+{
+ (void)user_ctx;
+ return keystore_slot_load(&system_keystore, ownerid, slot,
+ (void *)key, key_length);
+}
+
+fsl_shw_return_t do_system_keystore_slot_read(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t * key)
+{
+ (void)user_ctx;
+ return keystore_slot_read(&system_keystore, ownerid, slot,
+ key_length, (void *)key);
+}
+
+fsl_shw_return_t do_system_keystore_slot_encrypt(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ uint8_t * black_data)
+{
+ (void)user_ctx;
+ return keystore_slot_encrypt(NULL, &system_keystore, ownerid,
+ slot, key_length, black_data);
+}
+
+fsl_shw_return_t do_system_keystore_slot_decrypt(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t * black_data)
+{
+ (void)user_ctx;
+ return keystore_slot_decrypt(NULL, &system_keystore, ownerid,
+ slot, key_length, black_data);
+}
+
+void *fsl_shw_smalloc(fsl_shw_uco_t * user_ctx,
+ uint32_t size, const uint8_t * UMID, uint32_t permissions)
+{
+#ifdef FSL_HAVE_SCC2
+ int part_no;
+ void *part_base;
+ uint32_t part_phys;
+ scc_config_t *scc_configuration;
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (size != scc_configuration->partition_size_bytes) {
+ return NULL;
+ }
+
+ /* Attempt to grab a partition. */
+ if (scc_allocate_partition(0, &part_no, &part_base, &part_phys)
+ != SCC_RET_OK) {
+ return NULL;
+ }
+ printk(KERN_ALERT "In fsh_shw_smalloc (km): partition_base:%p "
+ "partition_base_phys: %p\n", part_base, (void *)part_phys);
+
+ /* these bits should be in a separate function */
+ printk(KERN_ALERT "writing UMID and MAP to secure the partition\n");
+
+ scc_engage_partition(part_base, UMID, permissions);
+
+ (void)user_ctx; /* unused param warning */
+
+ return part_base;
+#else /* FSL_HAVE_SCC2 */
+ (void)user_ctx;
+ (void)size;
+ (void)UMID;
+ (void)permissions;
+ return NULL;
+#endif /* FSL_HAVE_SCC2 */
+
+}
+
+fsl_shw_return_t fsl_shw_sfree(fsl_shw_uco_t * user_ctx, void *address)
+{
+ (void)user_ctx;
+
+#ifdef FSL_HAVE_SCC2
+ if (scc_release_partition(address) == SCC_RET_OK) {
+ return FSL_RETURN_OK_S;
+ }
+#endif
+
+ return FSL_RETURN_ERROR_S;
+}
+
+fsl_shw_return_t fsl_shw_sstatus(fsl_shw_uco_t * user_ctx,
+ void *address,
+ fsl_shw_partition_status_t * status)
+{
+ (void)user_ctx;
+
+#ifdef FSL_HAVE_SCC2
+ *status = scc_partition_status(address);
+ return FSL_RETURN_OK_S;
+#endif
+
+ return FSL_RETURN_ERROR_S;
+}
+
+/* Diminish permissions on some secure memory */
+fsl_shw_return_t fsl_shw_diminish_perms(fsl_shw_uco_t * user_ctx,
+ void *address, uint32_t permissions)
+{
+
+ (void)user_ctx; /* unused parameter warning */
+
+#ifdef FSL_HAVE_SCC2
+ if (scc_diminish_permissions(address, permissions) == SCC_RET_OK) {
+ return FSL_RETURN_OK_S;
+ }
+#endif
+ return FSL_RETURN_ERROR_S;
+}
+
+/*
+ * partition_base - physical address of the partition
+ * offset - offset, in blocks, of the data from the start of the partition
+ * length - length, in bytes, of the data to be encrypted (multiple of 4)
+ * black_data - virtual address that the encrypted data should be stored at
+ * Note that this virtual address must be translatable using the __virt_to_phys
+ * macro; ie, it can't be a specially mapped address. To do encryption with those
+ * addresses, use the scc_encrypt_region function directly. This is to make
+ * this function compatible with the user mode declaration, which does not know
+ * the physical addresses of the data it is using.
+ */
+fsl_shw_return_t
+do_scc_encrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode)
+{
+ scc_return_t scc_ret;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+
+#ifdef FSL_HAVE_SCC2
+
+#ifdef DIAG_ADAPTOR
+ uint32_t *owner_32 = (uint32_t *) & (owner_id);
+
+ LOG_KDIAG_ARGS
+ ("partition base: %p, offset: %i, count: %i, black data: %p\n",
+ partition_base, offset_bytes, byte_count, (void *)black_data);
+#endif
+ (void)user_ctx;
+
+ os_cache_flush_range(black_data, byte_count);
+
+ scc_ret =
+ scc_encrypt_region((uint32_t) partition_base, offset_bytes,
+ byte_count, __virt_to_phys(black_data), IV,
+ cypher_mode);
+
+ if (scc_ret == SCC_RET_OK) {
+ retval = FSL_RETURN_OK_S;
+ } else {
+ retval = FSL_RETURN_ERROR_S;
+ }
+
+ /* The SCC2 DMA engine should have written to the black ram, so we need to
+ * invalidate that region of memory. Note that the red ram is not an
+ * because it is mapped with the cache disabled.
+ */
+ os_cache_inv_range(black_data, byte_count);
+
+#else
+ (void)scc_ret;
+#endif /* FSL_HAVE_SCC2 */
+
+ return retval;
+}
+
+/*!
+ * Call the proper function to decrypt a region of encrypted secure memory
+ *
+ * @brief
+ *
+ * @param user_ctx User context of the partition owner (NULL in kernel)
+ * @param partition_base Base address (physical) of the partition
+ * @param offset_bytes Offset from base address that the decrypted data
+ * shall be placed
+ * @param byte_count Length of the message (bytes)
+ * @param black_data Pointer to where the encrypted data is stored
+ * @param owner_id
+ *
+ * @return status
+ */
+
+fsl_shw_return_t
+do_scc_decrypt_region(fsl_shw_uco_t * user_ctx,
+ void *partition_base, uint32_t offset_bytes,
+ uint32_t byte_count, const uint8_t * black_data,
+ uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode)
+{
+ scc_return_t scc_ret;
+ fsl_shw_return_t retval = FSL_RETURN_ERROR_S;
+
+#ifdef FSL_HAVE_SCC2
+
+#ifdef DIAG_ADAPTOR
+ uint32_t *owner_32 = (uint32_t *) & (owner_id);
+
+ LOG_KDIAG_ARGS
+ ("partition base: %p, offset: %i, count: %i, black data: %p\n",
+ partition_base, offset_bytes, byte_count, (void *)black_data);
+#endif
+
+ (void)user_ctx;
+
+ /* The SCC2 DMA engine will be reading from the black ram, so we need to
+ * make sure that the data is pushed out of the cache. Note that the red
+ * ram is not an issue because it is mapped with the cache disabled.
+ */
+ os_cache_flush_range(black_data, byte_count);
+
+ scc_ret =
+ scc_decrypt_region((uint32_t) partition_base, offset_bytes,
+ byte_count,
+ (uint8_t *) __virt_to_phys(black_data), IV,
+ cypher_mode);
+
+ if (scc_ret == SCC_RET_OK) {
+ retval = FSL_RETURN_OK_S;
+ } else {
+ retval = FSL_RETURN_ERROR_S;
+ }
+
+#else
+ (void)scc_ret;
+#endif /* FSL_HAVE_SCC2 */
+
+ return retval;
+}
+
+#ifdef DIAG_ADAPTOR
+/*!
+ * Dump chain of descriptors to the log.
+ *
+ * @brief Dump descriptor chain
+ *
+ * @param chain Kernel virtual address of start of chain of descriptors
+ *
+ * @return void
+ */
+void km_Dump_Chain(const sah_Desc * chain)
+{
+ while (chain != NULL) {
+ km_Dump_Words("Desc", (unsigned *)chain,
+ 6 /*sizeof(*chain)/sizeof(unsigned) */ );
+ /* place this definition elsewhere */
+ if (chain->ptr1) {
+ if (chain->header & SAH_HDR_LLO) {
+ km_Dump_Region(" Data1", chain->ptr1,
+ chain->len1);
+ } else {
+ km_Dump_Link(" Link1", chain->ptr1);
+ }
+ }
+ if (chain->ptr2) {
+ if (chain->header & SAH_HDR_LLO) {
+ km_Dump_Region(" Data2", chain->ptr2,
+ chain->len2);
+ } else {
+ km_Dump_Link(" Link2", chain->ptr2);
+ }
+ }
+
+ chain = chain->next;
+ }
+}
+
+/*!
+ * Dump chain of links to the log.
+ *
+ * @brief Dump chain of links
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param link Kernel virtual address of start of chain of links
+ *
+ * @return void
+ */
+static void km_Dump_Link(const char *prefix, const sah_Link * link)
+{
+ while (link != NULL) {
+ km_Dump_Words(prefix, (unsigned *)link,
+ 3 /* # words in h/w link */ );
+ if (link->flags & SAH_STORED_KEY_INFO) {
+#ifdef CAN_DUMP_SCC_DATA
+ uint32_t len;
+#endif
+
+#ifdef CAN_DUMP_SCC_DATA
+ {
+ char buf[50];
+
+ scc_get_slot_info(link->ownerid, link->slot, (uint32_t *) & link->data, /* RED key address */
+ &len); /* key length */
+ sprintf(buf, " SCC slot %d: ", link->slot);
+ km_Dump_Words(buf,
+ (void *)IO_ADDRESS((uint32_t)
+ link->data),
+ link->len / 4);
+ }
+#else
+ sprintf(Diag_msg, " SCC slot %d", link->slot);
+ LOG_KDIAG(Diag_msg);
+#endif
+ } else if (link->data != NULL) {
+ km_Dump_Region(" Data", link->data, link->len);
+ }
+
+ link = link->next;
+ }
+}
+
+/*!
+ * Dump given region of data to the log.
+ *
+ * @brief Dump data
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param data Kernel virtual address of start of region to dump
+ * @param length Amount of data to dump
+ *
+ * @return void
+*/
+void km_Dump_Region(const char *prefix, const unsigned char *data,
+ unsigned length)
+{
+ unsigned count;
+ char *output;
+ unsigned data_len;
+
+ sprintf(Diag_msg, "%s (%08X,%u):", prefix, (uint32_t) data, length);
+
+ /* Restrict amount of data to dump */
+ if (length > MAX_DUMP) {
+ data_len = MAX_DUMP;
+ } else {
+ data_len = length;
+ }
+
+ /* We've already printed some text in output buffer, skip over it */
+ output = Diag_msg + strlen(Diag_msg);
+
+ for (count = 0; count < data_len; count++) {
+ if (count % 4 == 0) {
+ *output++ = ' ';
+ }
+ sprintf(output, "%02X", *data++);
+ output += 2;
+ }
+
+ LOG_KDIAG(Diag_msg);
+}
+
+/*!
+ * Dump given wors of data to the log.
+ *
+ * @brief Dump data
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param data Kernel virtual address of start of region to dump
+ * @param word_count Amount of data to dump
+ *
+ * @return void
+*/
+void km_Dump_Words(const char *prefix, const unsigned *data,
+ unsigned word_count)
+{
+ char *output;
+
+ sprintf(Diag_msg, "%s (%08X,%uw): ", prefix, (uint32_t) data,
+ word_count);
+
+ /* We've already printed some text in output buffer, skip over it */
+ output = Diag_msg + strlen(Diag_msg);
+
+ while (word_count--) {
+ sprintf(output, "%08X ", *data++);
+ output += 9;
+ }
+
+ LOG_KDIAG(Diag_msg);
+}
+#endif
diff --git a/drivers/mxc/security/sahara2/sah_driver_interface.c b/drivers/mxc/security/sahara2/sah_driver_interface.c
new file mode 100644
index 000000000000..d34e3ed90af8
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_driver_interface.c
@@ -0,0 +1,2179 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+* @file sah_driver_interface.c
+*
+* @brief Provides a Linux Kernel Module interface to the SAHARA h/w device.
+*
+*/
+
+/* SAHARA Includes */
+#include <sah_driver_common.h>
+#include <sah_kernel.h>
+#include <sah_memory_mapper.h>
+#include <sah_queue_manager.h>
+#include <sah_status_manager.h>
+#include <sah_interrupt_handler.h>
+#include <sah_hardware_interface.h>
+#include <fsl_shw_keystore.h>
+#include <adaptor.h>
+#ifdef FSL_HAVE_SCC
+#include <linux/mxc_scc_driver.h>
+#else
+#include <linux/mxc_scc2_driver.h>
+#endif
+
+#ifdef DIAG_DRV_IF
+#include <diagnostic.h>
+#endif
+
+#if defined(CONFIG_DEVFS_FS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+#include <linux/devfs_fs_kernel.h>
+#else
+#include <linux/proc_fs.h>
+#endif
+
+#include <mach/spba.h>
+
+#ifdef PERF_TEST
+#define interruptible_sleep_on(x) sah_Handle_Interrupt()
+#endif
+
+#define TEST_MODE_OFF 1
+#define TEST_MODE_ON 2
+
+/*! Version register on first deployments */
+#define SAHARA_VERSION2 2
+/*! Version register on MX27 */
+#define SAHARA_VERSION3 3
+/*! Version register on MXC92323 */
+#define SAHARA_VERSION4 4
+
+/******************************************************************************
+* Module function declarations
+******************************************************************************/
+
+OS_DEV_INIT_DCL(sah_init);
+OS_DEV_SHUTDOWN_DCL(sah_cleanup);
+OS_DEV_OPEN_DCL(sah_open);
+OS_DEV_CLOSE_DCL(sah_release);
+OS_DEV_IOCTL_DCL(sah_ioctl);
+OS_DEV_MMAP_DCL(sah_mmap);
+
+static os_error_code sah_handle_get_capabilities(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+
+static void sah_user_callback(fsl_shw_uco_t * user_ctx);
+static os_error_code sah_handle_scc_sfree(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+static os_error_code sah_handle_scc_sstatus(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+static os_error_code sah_handle_scc_drop_perms(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+static os_error_code sah_handle_scc_encrypt(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+static os_error_code sah_handle_scc_decrypt(fsl_shw_uco_t* user_ctx,
+ uint32_t info);
+
+#ifdef FSL_HAVE_SCC2
+static fsl_shw_return_t register_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base,
+ void *kernel_base);
+static fsl_shw_return_t deregister_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base);
+#endif
+
+static os_error_code sah_handle_sk_slot_alloc(uint32_t info);
+static os_error_code sah_handle_sk_slot_dealloc(uint32_t info);
+static os_error_code sah_handle_sk_slot_load(uint32_t info);
+static os_error_code sah_handle_sk_slot_read(uint32_t info);
+static os_error_code sah_handle_sk_slot_decrypt(uint32_t info);
+static os_error_code sah_handle_sk_slot_encrypt(uint32_t info);
+
+/*! Boolean flag for whether interrupt handler needs to be released on exit */
+static unsigned interrupt_registered;
+
+static int handle_sah_ioctl_dar(fsl_shw_uco_t * filp, uint32_t user_space_desc);
+
+#if !defined(CONFIG_DEVFS_FS) || (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int sah_read_procfs(char *buf,
+ char **start,
+ off_t offset, int count, int *eof, void *data);
+
+static int sah_write_procfs(struct file *file, const char __user * buffer,
+ unsigned long count, void *data);
+#endif
+
+#if defined(CONFIG_DEVFS_FS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+
+/* This is a handle to the sahara DEVFS entry. */
+static devfs_handle_t Sahara_devfs_handle;
+
+#else
+
+/* Major number assigned to our device driver */
+static int Major;
+
+/* This is a handle to the sahara PROCFS entry */
+static struct proc_dir_entry *Sahara_procfs_handle;
+
+#endif
+
+uint32_t sah_hw_version;
+
+/* This is the wait queue to this driver. Linux declaration. */
+DECLARE_WAIT_QUEUE_HEAD(Wait_queue);
+
+/* This is a global variable that is used to track how many times the device
+ * has been opened simultaneously. */
+#ifdef DIAG_DRV_IF
+static int Device_in_use = 0;
+#endif
+
+/* This is the system keystore object */
+fsl_shw_kso_t system_keystore;
+
+/*!
+ * OS-dependent handle used for registering user interface of a driver.
+ */
+static os_driver_reg_t reg_handle;
+
+#ifdef DIAG_DRV_IF
+/* This is for sprintf() to use when constructing output. */
+#define DIAG_MSG_SIZE 1024
+static char Diag_msg[DIAG_MSG_SIZE];
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18))
+/** Pointer to Sahara clock information. Initialized during os_dev_init(). */
+static struct clk *sah_clk;
+#endif
+
+/*!
+*******************************************************************************
+* This function gets called when the module is inserted (insmod) into the
+* running kernel.
+*
+* @brief SAHARA device initialisation function.
+*
+* @return 0 on success
+* @return -EBUSY if the device or proc file entry cannot be created.
+* @return OS_ERROR_NO_MEMORY_S if kernel memory could not be allocated.
+* @return OS_ERROR_FAIL_S if initialisation of proc entry failed
+*/
+OS_DEV_INIT(sah_init)
+{
+ /* Status variable */
+ int os_error_code = 0;
+
+ interrupt_registered = 0;
+
+ /* Enable the SAHARA Clocks */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA : Enabling the IPG and AHB clocks\n")
+#endif /*DIAG_DRV_IF */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+ mxc_clks_enable(SAHARA2_CLK);
+#else
+ {
+ sah_clk = clk_get(NULL, "sahara_clk");
+ if (sah_clk != ERR_PTR(ENOENT))
+ clk_enable(sah_clk);
+ }
+#endif
+
+ /* Check for SPBA need */
+#if defined(CONFIG_ARCH_MXC91231) || defined(CONFIG_ARCH_MXC91321)
+ /* This needs to be a PLATFORM abstraction */
+ if (spba_take_ownership(SPBA_SAHARA, SPBA_MASTER_A)) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Sahara driver could not take ownership of Sahara\n");
+#endif
+ os_error_code = OS_ERROR_FAIL_S;
+ }
+#endif /* SPBA */
+
+ if (os_error_code == OS_ERROR_OK_S) {
+ sah_hw_version = sah_HW_Read_Version();
+ os_printk("Sahara HW Version is 0x%08x\n", sah_hw_version);
+
+ /* verify code and hardware are version compatible */
+ if ((sah_hw_version != SAHARA_VERSION2)
+ && (sah_hw_version != SAHARA_VERSION3)) {
+ if (((sah_hw_version >> 8) & 0xff) != SAHARA_VERSION4) {
+ os_printk
+ ("Sahara HW Version was not expected value.\n");
+ os_error_code = OS_ERROR_FAIL_S;
+ }
+ }
+ }
+
+ if (os_error_code == OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Calling sah_Init_Mem_Map to initialise "
+ "memory subsystem.");
+#endif
+ /* Do any memory-routine initialization */
+ os_error_code = sah_Init_Mem_Map();
+ }
+
+ if (os_error_code == OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Calling sah_HW_Reset() to Initialise the Hardware.");
+#endif
+ /* Initialise the hardware */
+ os_error_code = sah_HW_Reset();
+ if (os_error_code != OS_ERROR_OK_S) {
+ os_printk
+ ("sah_HW_Reset() failed to Initialise the Hardware.\n");
+ }
+
+ }
+
+ if (os_error_code == OS_ERROR_OK_S) {
+#if defined(CONFIG_DEVFS_FS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ /* Register the DEVFS entry */
+ Sahara_devfs_handle = devfs_register(NULL,
+ SAHARA_DEVICE_SHORT,
+ DEVFS_FL_AUTO_DEVNUM,
+ 0, 0,
+ SAHARA_DEVICE_MODE,
+ &Fops, NULL);
+ if (Sahara_devfs_handle == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("Registering the DEVFS character device failed.");
+#endif /* DIAG_DRV_IF */
+ os_error_code = -EBUSY;
+ }
+#else /* CONFIG_DEVFS_FS */
+ /* Create the PROCFS entry. This is used to report the assigned device
+ * major number back to user-space. */
+#if 1
+ Sahara_procfs_handle = create_proc_entry(SAHARA_DEVICE_SHORT, 0700, /* default mode */
+ NULL); /* parent dir */
+ if (Sahara_procfs_handle == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Registering the PROCFS interface failed.");
+#endif /* DIAG_DRV_IF */
+ os_error_code = OS_ERROR_FAIL_S;
+ } else {
+ Sahara_procfs_handle->nlink = 1;
+ Sahara_procfs_handle->data = 0;
+ Sahara_procfs_handle->read_proc = sah_read_procfs;
+ Sahara_procfs_handle->write_proc = sah_write_procfs;
+ }
+#endif /* #if 1 */
+ }
+
+ if (os_error_code == OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("Calling sah_Queue_Manager_Init() to Initialise the Queue "
+ "Manager.");
+#endif
+ /* Initialise the Queue Manager */
+ if (sah_Queue_Manager_Init() != FSL_RETURN_OK_S) {
+ os_error_code = -ENOMEM;
+ }
+ }
+#ifndef SAHARA_POLL_MODE
+ if (os_error_code == OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Calling sah_Intr_Init() to Initialise the Interrupt "
+ "Handler.");
+#endif
+ /* Initialise the Interrupt Handler */
+ os_error_code = sah_Intr_Init(&Wait_queue);
+ if (os_error_code == OS_ERROR_OK_S) {
+ interrupt_registered = 1;
+ }
+ }
+#endif /* ifndef SAHARA_POLL_MODE */
+
+#ifdef SAHARA_POWER_MANAGEMENT
+ if (os_error_code == OS_ERROR_OK_S) {
+ /* set up dynamic power management (dmp) */
+ os_error_code = sah_dpm_init();
+ }
+#endif
+
+ if (os_error_code == OS_ERROR_OK_S) {
+ os_driver_init_registration(reg_handle);
+ os_driver_add_registration(reg_handle, OS_FN_OPEN,
+ OS_DEV_OPEN_REF(sah_open));
+ os_driver_add_registration(reg_handle, OS_FN_IOCTL,
+ OS_DEV_IOCTL_REF(sah_ioctl));
+ os_driver_add_registration(reg_handle, OS_FN_CLOSE,
+ OS_DEV_CLOSE_REF(sah_release));
+ os_driver_add_registration(reg_handle, OS_FN_MMAP,
+ OS_DEV_MMAP_REF(sah_mmap));
+
+ os_error_code =
+ os_driver_complete_registration(reg_handle, Major,
+ "sahara");
+
+ if (os_error_code < OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Registering the regular "
+ "character device failed with error code: %d\n",
+ os_error_code);
+ LOG_KDIAG(Diag_msg);
+#endif
+ }
+ }
+#endif /* CONFIG_DEVFS_FS */
+
+ if (os_error_code == OS_ERROR_OK_S) {
+ /* set up the system keystore, using the default keystore handler */
+ fsl_shw_init_keystore_default(&system_keystore);
+
+ if (fsl_shw_establish_keystore(NULL, &system_keystore)
+ == FSL_RETURN_OK_S) {
+ os_error_code = OS_ERROR_OK_S;
+ } else {
+ os_error_code = OS_ERROR_FAIL_S;
+ }
+
+ if (os_error_code != OS_ERROR_OK_S) {
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Registering the system keystore "
+ "failed with error code: %d\n", os_error_code);
+ LOG_KDIAG(Diag_msg);
+#endif
+ }
+ }
+
+ if (os_error_code != OS_ERROR_OK_S) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ cleanup_module();
+#else
+ sah_cleanup();
+#endif
+ }
+#ifdef DIAG_DRV_IF
+ else {
+ LOG_KDIAG_ARGS("Sahara major node is %d\n", Major);
+ }
+#endif
+
+/* Disabling the Clock after the driver has been registered fine.
+ This is done to save power when Sahara is not in use.*/
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA : Disabling the clocks\n")
+#endif /* DIAG_DRV_IF */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SAHARA2_CLK);
+#else
+ {
+ if (sah_clk != ERR_PTR(ENOENT))
+ clk_disable(sah_clk);
+ }
+#endif
+
+ os_dev_init_return(os_error_code);
+}
+
+/*!
+*******************************************************************************
+* This function gets called when the module is removed (rmmod) from the running
+* kernel.
+*
+* @brief SAHARA device clean-up function.
+*
+* @return void
+*/
+OS_DEV_SHUTDOWN(sah_cleanup)
+{
+ int ret_val = 0;
+
+ printk(KERN_ALERT "Sahara going into cleanup\n");
+
+ /* clear out the system keystore */
+ fsl_shw_release_keystore(NULL, &system_keystore);
+
+ /* Unregister the device */
+#if defined(CONFIG_DEVFS_FS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ devfs_unregister(Sahara_devfs_handle);
+#else
+
+ if (Sahara_procfs_handle != NULL) {
+ remove_proc_entry(SAHARA_DEVICE_SHORT, NULL);
+ }
+
+ if (Major >= 0) {
+ ret_val = os_driver_remove_registration(reg_handle);
+ }
+#ifdef DIAG_DRV_IF
+ if (ret_val < 0) {
+ snprintf(Diag_msg, DIAG_MSG_SIZE, "Error while attempting to "
+ "unregister the device: %d\n", ret_val);
+ LOG_KDIAG(Diag_msg);
+ }
+#endif
+
+#endif /* CONFIG_DEVFS_FS */
+ sah_Queue_Manager_Close();
+
+#ifndef SAHARA_POLL_MODE
+ if (interrupt_registered) {
+ sah_Intr_Release();
+ interrupt_registered = 0;
+ }
+#endif
+ sah_Stop_Mem_Map();
+#ifdef SAHARA_POWER_MANAGEMENT
+ sah_dpm_close();
+#endif
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA : Disabling the clocks\n")
+#endif /* DIAG_DRV_IF */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+ mxc_clks_disable(SAHARA2_CLK);
+#else
+ {
+ if (sah_clk != ERR_PTR(ENOENT))
+ clk_disable(sah_clk);
+ clk_put(sah_clk);
+ }
+#endif
+
+ os_dev_shutdown_return(OS_ERROR_OK_S);
+}
+
+/*!
+*******************************************************************************
+* This function simply increments the module usage count.
+*
+* @brief SAHARA device open function.
+*
+* @param inode Part of the kernel prototype.
+* @param file Part of the kernel prototype.
+*
+* @return 0 - Always returns 0 since any number of calls to this function are
+* allowed.
+*
+*/
+OS_DEV_OPEN(sah_open)
+{
+
+#if defined(LINUX_VERSION) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10))
+ MOD_INC_USE_COUNT;
+#endif
+
+#ifdef DIAG_DRV_IF
+ Device_in_use++;
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Incrementing module use count to: %d ", Device_in_use);
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ os_dev_set_user_private(NULL);
+
+ /* Return 0 to indicate success */
+ os_dev_open_return(0);
+}
+
+/*!
+*******************************************************************************
+* This function simply decrements the module usage count.
+*
+* @brief SAHARA device release function.
+*
+* @param inode Part of the kernel prototype.
+* @param file Part of the kernel prototype.
+*
+* @return 0 - Always returns 0 since this function does not fail.
+*/
+OS_DEV_CLOSE(sah_release)
+{
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+#if defined(LINUX_VERSION) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10))
+ MOD_DEC_USE_COUNT;
+#endif
+
+#ifdef DIAG_DRV_IF
+ Device_in_use--;
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Decrementing module use count to: %d ", Device_in_use);
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ if (user_ctx != NULL) {
+ sah_handle_deregistration(user_ctx);
+ os_free_memory(user_ctx);
+ os_dev_set_user_private(NULL);
+ }
+
+ /* Return 0 to indicate success */
+ os_dev_close_return(OS_ERROR_OK_S);
+}
+
+/*!
+*******************************************************************************
+* This function provides the IO Controls for the SAHARA driver. Three IO
+* Controls are supported:
+*
+* SAHARA_HWRESET and
+* SAHARA_SET_HA
+* SAHARA_CHK_TEST_MODE
+*
+* @brief SAHARA device IO Control function.
+*
+* @param inode Part of the kernel prototype.
+* @param filp Part of the kernel prototype.
+* @param cmd Part of the kernel prototype.
+* @param arg Part of the kernel prototype.
+*
+* @return 0 on success
+* @return -EBUSY if the HA bit could not be set due to busy hardware.
+* @return -ENOTTY if an unsupported IOCTL was attempted on the device.
+* @return -EFAULT if put_user() fails
+*/
+OS_DEV_IOCTL(sah_ioctl)
+{
+ int status = 0;
+ int test_mode;
+
+ switch (os_dev_get_ioctl_op()) {
+ case SAHARA_HWRESET:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_HWRESET IOCTL.");
+#endif
+ /* We need to reset the hardware. */
+ sah_HW_Reset();
+
+ /* Mark all the entries in the Queue Manager's queue with state
+ * SAH_STATE_RESET.
+ */
+ sah_Queue_Manager_Reset_Entries();
+
+ /* Wake up all sleeping write() calls. */
+ wake_up_interruptible(&Wait_queue);
+ break;
+#ifdef SAHARA_HA_ENABLED
+ case SAHARA_SET_HA:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SET_HA IOCTL.");
+#endif /* DIAG_DRV_IF */
+ if (sah_HW_Set_HA() == ERR_INTERNAL) {
+ status = -EBUSY;
+ }
+ break;
+#endif /* SAHARA_HA_ENABLED */
+
+ case SAHARA_CHK_TEST_MODE:
+ /* load test_mode */
+ test_mode = TEST_MODE_OFF;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_CHECK_TEST_MODE IOCTL.");
+ test_mode = TEST_MODE_ON;
+#endif /* DIAG_DRV_IF */
+#if defined(KERNEL_TEST) || defined(PERF_TEST)
+ test_mode = TEST_MODE_ON;
+#endif /* KERNEL_TEST || PERF_TEST */
+ /* copy test_mode back to user space. put_user() is Linux fn */
+ /* compiler warning `register': no problem found so ignored */
+ status = put_user(test_mode, (int *)os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_DAR:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_DAR IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ if (user_ctx != NULL) {
+ status =
+ handle_sah_ioctl_dar(user_ctx,
+ os_dev_get_ioctl_arg
+ ());
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+
+ }
+ break;
+
+ case SAHARA_GET_RESULTS:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_GET_RESULTS IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ if (user_ctx != NULL) {
+ status =
+ sah_get_results_pointers(user_ctx,
+ os_dev_get_ioctl_arg
+ ());
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+ }
+ break;
+
+ case SAHARA_REGISTER:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_REGISTER IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ if (user_ctx != NULL) {
+ status = OS_ERROR_FAIL_S; /* already registered */
+ } else {
+ user_ctx =
+ os_alloc_memory(sizeof(fsl_shw_uco_t),
+ GFP_KERNEL);
+ if (user_ctx == NULL) {
+ status = OS_ERROR_NO_MEMORY_S;
+ } else {
+ /* Copy UCO from user, but only as big as the common UCO */
+ if (os_copy_from_user(user_ctx,
+ (void *)
+ os_dev_get_ioctl_arg
+ (),
+ offsetof
+ (fsl_shw_uco_t,
+ result_pool))) {
+ status = OS_ERROR_FAIL_S;
+ } else {
+ os_dev_set_user_private
+ (user_ctx);
+ status =
+ sah_handle_registration
+ (user_ctx);
+ }
+ }
+ }
+ }
+ break;
+
+ /* This ioctl cmd should disappear in favor of a close() routine. */
+ case SAHARA_DEREGISTER:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_DEREGISTER IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ if (user_ctx == NULL) {
+ status = OS_ERROR_FAIL_S;
+ } else {
+ status = sah_handle_deregistration(user_ctx);
+ os_free_memory(user_ctx);
+ os_dev_set_user_private(NULL);
+ }
+ }
+ break;
+ case SAHARA_SCC_DROP_PERMS:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_DROP_PERMS IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ /* drop permissions on the specified partition */
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_scc_drop_perms(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ case SAHARA_SCC_SFREE:
+ /* Unmap the specified partition from the users space, and then
+ * free it for use by someone else.
+ */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_SFREE IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_scc_sfree(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ case SAHARA_SCC_SSTATUS:
+ /* Unmap the specified partition from the users space, and then
+ * free it for use by someone else.
+ */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_SSTATUS IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_scc_sstatus(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ case SAHARA_SCC_ENCRYPT:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_ENCRYPT IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_scc_encrypt(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ case SAHARA_SCC_DECRYPT:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_DECRYPT IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_scc_decrypt(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ case SAHARA_SK_ALLOC:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_ALLOC IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_alloc(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SK_DEALLOC:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_DEALLOC IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_dealloc(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SK_LOAD:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_LOAD IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_load(os_dev_get_ioctl_arg());
+ break;
+ case SAHARA_SK_READ:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_READ IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_read(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SK_SLOT_DEC:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_SLOT_DECRYPT IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_decrypt(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SK_SLOT_ENC:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SK_SLOT_ENCRYPT IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_sk_slot_encrypt(os_dev_get_ioctl_arg());
+ break;
+ case SAHARA_GET_CAPS:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_GET_CAPS IOCTL.");
+#endif /* DIAG_DRV_IF */
+ {
+ fsl_shw_uco_t *user_ctx = os_dev_get_user_private();
+
+ status =
+ sah_handle_get_capabilities(user_ctx,
+ os_dev_get_ioctl_arg());
+ }
+ break;
+
+ default:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Unknown SAHARA IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = OS_ERROR_FAIL_S;
+ }
+
+ os_dev_ioctl_return(status);
+}
+
+/* Fill in the user's capabilities structure */
+static os_error_code sah_handle_get_capabilities(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_FAIL_S;
+ fsl_shw_pco_t capabilities;
+
+ status = os_copy_from_user(&capabilities, (void *)info,
+ sizeof(fsl_shw_pco_t));
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ if (get_capabilities(user_ctx, &capabilities) == FSL_RETURN_OK_S) {
+ status = os_copy_to_user((void *)info, &capabilities,
+ sizeof(fsl_shw_pco_t));
+ }
+
+ out:
+ return status;
+}
+
+#ifdef FSL_HAVE_SCC2
+
+/* Find the kernel-mode address of the partition.
+ * This can then be passed to the SCC functions.
+ */
+void *lookup_user_partition(fsl_shw_uco_t * user_ctx, uint32_t user_base)
+{
+ /* search through the partition chain to find one that matches the user base
+ * address.
+ */
+ fsl_shw_spo_t *curr = (fsl_shw_spo_t *) user_ctx->partition;
+
+ while (curr != NULL) {
+ if (curr->user_base == user_base) {
+ return curr->kernel_base;
+ }
+ curr = (fsl_shw_spo_t *) curr->next;
+ }
+ return NULL;
+}
+
+/* user_base: userspace base address of the partition
+ * kernel_base: kernel mode base address of the partition
+ */
+static fsl_shw_return_t register_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base,
+ void *kernel_base)
+{
+ fsl_shw_spo_t *partition_info;
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ if (user_ctx == NULL) {
+ goto out;
+ }
+
+ partition_info = os_alloc_memory(sizeof(fsl_shw_spo_t), GFP_KERNEL);
+
+ if (partition_info == NULL) {
+ goto out;
+ }
+
+ /* stuff the partition info, then put it at the front of the chain */
+ partition_info->user_base = user_base;
+ partition_info->kernel_base = kernel_base;
+ partition_info->next = user_ctx->partition;
+
+ user_ctx->partition = (struct fsl_shw_spo_t *)partition_info;
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("partition with user_base=%p, kernel_base=%p registered.",
+ (void *)user_base, kernel_base);
+#endif
+
+ ret = FSL_RETURN_OK_S;
+
+ out:
+
+ return ret;
+}
+
+/* if the partition is in the users list, remove it */
+static fsl_shw_return_t deregister_user_partition(fsl_shw_uco_t * user_ctx,
+ uint32_t user_base)
+{
+ fsl_shw_spo_t *curr = (fsl_shw_spo_t *) user_ctx->partition;
+ fsl_shw_spo_t *last = (fsl_shw_spo_t *) user_ctx->partition;
+
+ while (curr != NULL) {
+ if (curr->user_base == user_base) {
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("deregister_user_partition: partition with "
+ "user_base=%p, kernel_base=%p deregistered.\n",
+ (void *)curr->user_base, curr->kernel_base);
+#endif
+
+ if (last == curr) {
+ user_ctx->partition = curr->next;
+ os_free_memory(curr);
+ return FSL_RETURN_OK_S;
+ } else {
+ last->next = curr->next;
+ os_free_memory(curr);
+ return FSL_RETURN_OK_S;
+ }
+ }
+ last = curr;
+ curr = (fsl_shw_spo_t *) curr->next;
+ }
+
+ return FSL_RETURN_ERROR_S;
+}
+
+#endif /* FSL_HAVE_SCC2 */
+
+static os_error_code sah_handle_scc_drop_perms(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ scc_return_t scc_ret;
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+
+ status =
+ os_copy_from_user(&partition_info, (void *)info,
+ sizeof(partition_info));
+
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base = lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("_scc_drop_perms(): failed to find partition\n");
+#endif
+ goto out;
+ }
+
+ /* call scc driver to perform the drop */
+ scc_ret = scc_diminish_permissions(kernel_base,
+ partition_info.permissions);
+ if (scc_ret == SCC_RET_OK) {
+ status = OS_ERROR_OK_S;
+ } else {
+ status = OS_ERROR_FAIL_S;
+ }
+
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code sah_handle_scc_sfree(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+ int ret;
+
+ status =
+ os_copy_from_user(&partition_info, (void *)info,
+ sizeof(partition_info));
+
+ /* check that the copy was successful */
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base =
+ lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("failed to find partition\n");
+#endif /*DIAG_DRV_IF */
+ goto out;
+ }
+
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ ret = unmap_user_memory(partition_info.user_base, 8192);
+
+ /* If the memory was successfully released */
+ if (ret == OS_ERROR_OK_S) {
+
+ /* release the partition */
+ scc_release_partition(kernel_base);
+
+ /* and remove it from the users context */
+ deregister_user_partition(user_ctx,
+ partition_info.user_base);
+
+ status = OS_ERROR_OK_S;
+ }
+ }
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code sah_handle_scc_sstatus(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ scc_partition_info_t partition_info;
+ void *kernel_base;
+
+ status =
+ os_copy_from_user(&partition_info, (void *)info,
+ sizeof(partition_info));
+
+ /* check that the copy was successful */
+ if (status != OS_ERROR_OK_S) {
+ goto out;
+ }
+
+ /* validate that the user owns this partition, and look up its handle */
+ kernel_base =
+ lookup_user_partition(user_ctx, partition_info.user_base);
+
+ if (kernel_base == NULL) {
+ status = OS_ERROR_FAIL_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("failed to find partition\n");
+#endif /*DIAG_DRV_IF */
+ goto out;
+ }
+
+ partition_info.status = scc_partition_status(kernel_base);
+
+ status =
+ os_copy_to_user((void *)info, &partition_info,
+ sizeof(partition_info));
+ }
+ out:
+#endif /* FSL_HAVE_SCC2 */
+ return status;
+}
+
+static os_error_code sah_handle_scc_encrypt(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code os_err = OS_ERROR_FAIL_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ fsl_shw_return_t retval;
+ scc_region_t region_info;
+ void *page_ctx = NULL;
+ void *black_addr = NULL;
+ void *partition_base = NULL;
+ scc_config_t *scc_configuration;
+
+ os_err =
+ os_copy_from_user(&region_info, (void *)info,
+ sizeof(region_info));
+
+ if (os_err != OS_ERROR_OK_S) {
+ goto out;
+ }
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("partition_base: %p, offset: %i, length: %i, black data: %p",
+ (void *)region_info.partition_base, region_info.offset,
+ region_info.length, (void *)region_info.black_data);
+#endif
+
+ /* validate that the user owns this partition, and look up its handle */
+ partition_base = lookup_user_partition(user_ctx,
+ region_info.
+ partition_base);
+
+ if (partition_base == NULL) {
+ retval = FSL_RETURN_ERROR_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("failed to find secure partition\n");
+#endif
+ goto out;
+ }
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (region_info.offset + region_info.length >
+ scc_configuration->partition_size_bytes) {
+ retval = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ /* wire down black data */
+ black_addr = wire_user_memory(region_info.black_data,
+ region_info.length, &page_ctx);
+
+ if (black_addr == NULL) {
+ retval = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ retval =
+ do_scc_encrypt_region(NULL, partition_base,
+ region_info.offset,
+ region_info.length, black_addr,
+ region_info.IV,
+ region_info.cypher_mode);
+
+ /* release black data */
+ unwire_user_memory(&page_ctx);
+
+ out:
+ if (os_err == OS_ERROR_OK_S) {
+ /* Return error code */
+ region_info.code = retval;
+ os_err =
+ os_copy_to_user((void *)info, &region_info,
+ sizeof(region_info));
+ }
+ }
+
+#endif
+ return os_err;
+}
+
+static os_error_code sah_handle_scc_decrypt(fsl_shw_uco_t * user_ctx,
+ uint32_t info)
+{
+ os_error_code os_err = OS_ERROR_FAIL_S;
+#ifdef FSL_HAVE_SCC2
+ {
+ fsl_shw_return_t retval;
+ scc_region_t region_info;
+ void *page_ctx = NULL;
+ void *black_addr;
+ void *partition_base;
+ scc_config_t *scc_configuration;
+
+ os_err =
+ os_copy_from_user(&region_info, (void *)info,
+ sizeof(region_info));
+
+ if (os_err != OS_ERROR_OK_S) {
+ goto out;
+ }
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("partition_base: %p, offset: %i, length: %i, black data: %p",
+ (void *)region_info.partition_base, region_info.offset,
+ region_info.length, (void *)region_info.black_data);
+#endif
+
+ /* validate that the user owns this partition, and look up its handle */
+ partition_base = lookup_user_partition(user_ctx,
+ region_info.
+ partition_base);
+
+ if (partition_base == NULL) {
+ retval = FSL_RETURN_ERROR_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("failed to find partition\n");
+#endif
+ goto out;
+ }
+
+ /* Check that the memory size requested is correct */
+ scc_configuration = scc_get_configuration();
+ if (region_info.offset + region_info.length >
+ scc_configuration->partition_size_bytes) {
+ retval = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ /* wire down black data */
+ black_addr = wire_user_memory(region_info.black_data,
+ region_info.length, &page_ctx);
+
+ if (black_addr == NULL) {
+ retval = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+ retval =
+ do_scc_decrypt_region(NULL, partition_base,
+ region_info.offset,
+ region_info.length, black_addr,
+ region_info.IV,
+ region_info.cypher_mode);
+
+ /* release black data */
+ unwire_user_memory(&page_ctx);
+
+ out:
+ if (os_err == OS_ERROR_OK_S) {
+ /* Return error code */
+ region_info.code = retval;
+ os_err =
+ os_copy_to_user((void *)info, &region_info,
+ sizeof(region_info));
+ }
+ }
+
+#endif /* FSL_HAVE_SCC2 */
+ return os_err;
+}
+
+/*****************************************************************************/
+/* fn get_user_smid() */
+/*****************************************************************************/
+uint32_t get_user_smid(void *proc)
+{
+ /*
+ * A real implementation would have some way to handle signed applications
+ * which wouild be assigned distinct SMIDs. For the reference
+ * implementation, we show where this would be determined (here), but
+ * always provide a fixed answer, thus not separating users at all.
+ */
+
+ return 0x42eaae42;
+}
+
+/*!
+*******************************************************************************
+* This function implements the smalloc() function for userspace programs, by
+* making a call to the SCC2 mmap() function that acquires a region of secure
+* memory on behalf of the user, and then maps it into the users memory space.
+* Currently, the only memory size supported is that of a single SCC2 partition.
+* Requests for other sized memory regions will fail.
+*/
+OS_DEV_MMAP(sah_mmap)
+{
+ os_error_code status = OS_ERROR_NO_MEMORY_S;
+
+#ifdef FSL_HAVE_SCC2
+ {
+ scc_return_t scc_ret;
+ fsl_shw_return_t fsl_ret;
+ uint32_t partition_registered = FALSE;
+
+ uint32_t user_base;
+ void *partition_base;
+ uint32_t smid;
+ scc_config_t *scc_configuration;
+
+ int part_no = -1;
+ uint32_t part_phys;
+
+ fsl_shw_uco_t *user_ctx =
+ (fsl_shw_uco_t *) os_dev_get_user_private();
+
+ /* Make sure that the user context is valid */
+ if (user_ctx == NULL) {
+ user_ctx =
+ os_alloc_memory(sizeof(*user_ctx), GFP_KERNEL);
+
+ if (user_ctx == NULL) {
+ status = OS_ERROR_NO_MEMORY_S;
+ goto out;
+ }
+
+ sah_handle_registration(user_ctx);
+ os_dev_set_user_private(user_ctx);
+ }
+
+ /* Determine the size of a secure partition */
+ scc_configuration = scc_get_configuration();
+
+ /* Check that the memory size requested is equal to the partition
+ * size, and that the requested destination is on a page boundary.
+ */
+ if (((os_mmap_user_base() % PAGE_SIZE) != 0) ||
+ (os_mmap_memory_size() !=
+ scc_configuration->partition_size_bytes)) {
+ status = OS_ERROR_BAD_ARG_S;
+ goto out;
+ }
+
+ /* Retrieve the SMID associated with the user */
+ smid = get_user_smid(user_ctx->process);
+
+ /* Attempt to allocate a secure partition */
+ scc_ret =
+ scc_allocate_partition(smid, &part_no, &partition_base,
+ &part_phys);
+ if (scc_ret != SCC_RET_OK) {
+ pr_debug
+ ("SCC mmap() request failed to allocate partition;"
+ " error %d\n", status);
+ status = OS_ERROR_FAIL_S;
+ goto out;
+ }
+
+ pr_debug("scc_mmap() acquired partition %d at %08x\n",
+ part_no, part_phys);
+
+ /* Record partition info in the user context */
+ user_base = os_mmap_user_base();
+ fsl_ret =
+ register_user_partition(user_ctx, user_base,
+ partition_base);
+
+ if (fsl_ret != FSL_RETURN_OK_S) {
+ pr_debug
+ ("SCC mmap() request failed to register partition with user"
+ " context, error: %d\n", fsl_ret);
+ status = OS_ERROR_FAIL_S;
+ }
+
+ partition_registered = TRUE;
+
+ status = map_user_memory(os_mmap_memory_ctx(), part_phys,
+ os_mmap_memory_size());
+
+#ifdef SHW_DEBUG
+ if (status == OS_ERROR_OK_S) {
+ LOG_KDIAG_ARGS
+ ("Partition allocated: user_base=%p, partition_base=%p.",
+ (void *)user_base, partition_base);
+ }
+#endif
+
+ out:
+ /* If there is an error it has to be handled here */
+ if (status != OS_ERROR_OK_S) {
+ /* if the partition was registered with the user, unregister it. */
+ if (partition_registered == TRUE) {
+ deregister_user_partition(user_ctx, user_base);
+ }
+
+ /* if the partition was allocated, deallocate it */
+ if (partition_base != NULL) {
+ scc_release_partition(partition_base);
+ }
+ }
+ }
+#endif /* FSL_HAVE_SCC2 */
+
+ return status;
+}
+
+/* Find the physical address of a key stored in the system keystore */
+fsl_shw_return_t
+system_keystore_get_slot_info(uint64_t owner_id, uint32_t slot,
+ uint32_t * address, uint32_t * slot_size_bytes)
+{
+ fsl_shw_return_t retval;
+ void *kernel_address;
+
+ /* First verify that the key access is valid */
+ retval = system_keystore.slot_verify_access(system_keystore.user_data,
+ owner_id, slot);
+
+ if (retval != FSL_RETURN_OK_S) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("verification failed");
+#endif
+ return retval;
+ }
+
+ if (address != NULL) {
+#ifdef FSL_HAVE_SCC2
+ kernel_address =
+ system_keystore.slot_get_address(system_keystore.user_data,
+ slot);
+ (*address) = scc_virt_to_phys(kernel_address);
+#else
+ kernel_address =
+ system_keystore.slot_get_address((void *)&owner_id, slot);
+ (*address) = (uint32_t) kernel_address;
+#endif
+ }
+
+ if (slot_size_bytes != NULL) {
+#ifdef FSL_HAVE_SCC2
+ *slot_size_bytes =
+ system_keystore.slot_get_slot_size(system_keystore.
+ user_data, slot);
+#else
+ *slot_size_bytes =
+ system_keystore.slot_get_slot_size((void *)&owner_id, slot);
+#endif
+ }
+
+ return retval;
+}
+
+static os_error_code sah_handle_sk_slot_alloc(uint32_t info)
+{
+ scc_slot_t slot_info;
+ os_error_code os_err;
+ scc_return_t scc_ret;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+ if (os_err == OS_ERROR_OK_S) {
+ scc_ret = keystore_slot_alloc(&system_keystore,
+ slot_info.key_length,
+ slot_info.ownerid,
+ &slot_info.slot);
+ if (scc_ret == SCC_RET_OK) {
+ slot_info.code = FSL_RETURN_OK_S;
+ } else if (scc_ret == SCC_RET_INSUFFICIENT_SPACE) {
+ slot_info.code = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ slot_info.code = FSL_RETURN_ERROR_S;
+ }
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS("key length: %i, handle: %i\n",
+ slot_info.key_length, slot_info.slot);
+#endif
+
+ /* Return error code and slot info */
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+
+ if (os_err != OS_ERROR_OK_S) {
+ (void)keystore_slot_dealloc(&system_keystore,
+ slot_info.ownerid,
+ slot_info.slot);
+ }
+ }
+
+ return os_err;
+}
+
+static os_error_code sah_handle_sk_slot_dealloc(uint32_t info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ scc_slot_t slot_info;
+ os_error_code os_err;
+ scc_return_t scc_ret;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+
+ if (os_err == OS_ERROR_OK_S) {
+ scc_ret = keystore_slot_dealloc(&system_keystore,
+ slot_info.ownerid,
+ slot_info.slot);
+
+ if (scc_ret == SCC_RET_OK) {
+ ret = FSL_RETURN_OK_S;
+ } else {
+ ret = FSL_RETURN_ERROR_S;
+ }
+ slot_info.code = ret;
+
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+ }
+
+ return os_err;
+}
+
+static os_error_code sah_handle_sk_slot_load(uint32_t info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ scc_slot_t slot_info;
+ os_error_code os_err;
+ uint8_t *key = NULL;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+
+ if (os_err == OS_ERROR_OK_S) {
+ /* Allow slop in alloc in case we are rounding up to word multiple */
+ key = os_alloc_memory(slot_info.key_length + 3, GFP_KERNEL);
+ if (key == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ os_err = OS_ERROR_NO_MEMORY_S;
+ } else {
+ os_err = os_copy_from_user(key, slot_info.key,
+ slot_info.key_length);
+ }
+ }
+
+ if (os_err == OS_ERROR_OK_S) {
+ unsigned key_length = slot_info.key_length;
+
+ /* Round up if necessary, as SCC call wants a multiple of 32-bit
+ * values for the full object being loaded. */
+ if ((key_length & 3) != 0) {
+ key_length += 4 - (key_length & 3);
+ }
+ ret = keystore_slot_load(&system_keystore,
+ slot_info.ownerid, slot_info.slot, key,
+ key_length);
+
+ slot_info.code = ret;
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+ }
+
+ if (key != NULL) {
+ memset(key, 0, slot_info.key_length);
+ os_free_memory(key);
+ }
+
+ return os_err;
+}
+
+static os_error_code sah_handle_sk_slot_read(uint32_t info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ scc_slot_t slot_info;
+ os_error_code os_err;
+ uint8_t *key = NULL;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+
+ if (os_err == OS_ERROR_OK_S) {
+
+ /* This operation is not allowed for user keys */
+ slot_info.code = FSL_RETURN_NO_RESOURCE_S;
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+
+ return os_err;
+ }
+
+ if (os_err == OS_ERROR_OK_S) {
+ /* Allow slop in alloc in case we are rounding up to word multiple */
+ key = os_alloc_memory(slot_info.key_length + 3, GFP_KERNEL);
+ if (key == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ os_err = OS_ERROR_NO_MEMORY_S;
+ }
+ }
+
+ if (os_err == OS_ERROR_OK_S) {
+ unsigned key_length = slot_info.key_length;
+
+ /* @bug Do some PERMISSIONS checking - make sure this is SW key */
+
+ /* Round up if necessary, as SCC call wants a multiple of 32-bit
+ * values for the full object being loaded. */
+ if ((key_length & 3) != 0) {
+ key_length += 4 - (key_length & 3);
+ }
+ ret = keystore_slot_read(&system_keystore,
+ slot_info.ownerid, slot_info.slot,
+ key_length, key);
+
+ /* @bug do some error checking */
+
+ /* Send key back to user */
+ os_err = os_copy_to_user(slot_info.key, key,
+ slot_info.key_length);
+
+ slot_info.code = ret;
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+ }
+
+ if (key != NULL) {
+ memset(key, 0, slot_info.key_length);
+ os_free_memory(key);
+ }
+
+ return os_err;
+}
+
+static os_error_code sah_handle_sk_slot_encrypt(uint32_t info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ scc_slot_t slot_info;
+ os_error_code os_err;
+ scc_return_t scc_ret;
+ uint8_t *key = NULL;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+
+ if (os_err == OS_ERROR_OK_S) {
+ key = os_alloc_memory(slot_info.key_length, GFP_KERNEL);
+ if (key == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ }
+ }
+
+ if (key != NULL) {
+
+ scc_ret = keystore_slot_encrypt(NULL, &system_keystore,
+ slot_info.ownerid,
+ slot_info.slot,
+ slot_info.key_length, key);
+
+ if (scc_ret != SCC_RET_OK) {
+ ret = FSL_RETURN_ERROR_S;
+ } else {
+ os_err =
+ os_copy_to_user(slot_info.key, key,
+ slot_info.key_length);
+ if (os_err != OS_ERROR_OK_S) {
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+ } else {
+ ret = FSL_RETURN_OK_S;
+ }
+ }
+
+ slot_info.code = ret;
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+
+ memset(key, 0, slot_info.key_length);
+ os_free_memory(key);
+ }
+
+ return os_err;
+}
+
+static os_error_code sah_handle_sk_slot_decrypt(uint32_t info)
+{
+ fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S;
+ scc_slot_t slot_info; /*!< decrypt request fields */
+ os_error_code os_err;
+ scc_return_t scc_ret;
+ uint8_t *key = NULL;
+
+ os_err = os_copy_from_user(&slot_info, (void *)info, sizeof(slot_info));
+
+ if (os_err == OS_ERROR_OK_S) {
+ key = os_alloc_memory(slot_info.key_length, GFP_KERNEL);
+ if (key == NULL) {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ os_err = OS_ERROR_OK_S;
+ } else {
+ os_err = os_copy_from_user(key, slot_info.key,
+ slot_info.key_length);
+ }
+ }
+
+ if (os_err == OS_ERROR_OK_S) {
+ scc_ret = keystore_slot_decrypt(NULL, &system_keystore,
+ slot_info.ownerid,
+ slot_info.slot,
+ slot_info.key_length, key);
+ if (scc_ret == SCC_RET_OK) {
+ ret = FSL_RETURN_OK_S;
+ } else {
+ ret = FSL_RETURN_ERROR_S;
+ }
+
+ slot_info.code = ret;
+ os_err =
+ os_copy_to_user((void *)info, &slot_info,
+ sizeof(slot_info));
+ }
+
+ if (key != NULL) {
+ memset(key, 0, slot_info.key_length);
+ os_free_memory(key);
+ }
+
+ return os_err;
+}
+
+/*!
+ * Register a user
+ *
+ * @brief Register a user
+ *
+ * @param user_ctx information about this user
+ *
+ * @return status code
+ */
+fsl_shw_return_t sah_handle_registration(fsl_shw_uco_t * user_ctx)
+{
+ /* Initialize the user's result pool (like sah_Queue_Construct() */
+ user_ctx->result_pool.head = NULL;
+ user_ctx->result_pool.tail = NULL;
+ user_ctx->result_pool.count = 0;
+
+ /* initialize the user's partition chain */
+ user_ctx->partition = NULL;
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * Deregister a user
+ *
+ * @brief Deregister a user
+ *
+ * @param user_ctx information about this user
+ *
+ * @return status code
+ */
+fsl_shw_return_t sah_handle_deregistration(fsl_shw_uco_t * user_ctx)
+{
+ /* NOTE:
+ * This will release any secure partitions that are held by the user.
+ * Encryption keys that were placed in the system keystore by the user
+ * should not be removed here, because they might have been shared with
+ * another process. The user must be careful to release any that are no
+ * longer in use.
+ */
+ fsl_shw_return_t ret = FSL_RETURN_OK_S;
+
+#ifdef FSL_HAVE_SCC2
+ fsl_shw_spo_t *partition;
+ struct mm_struct *mm = current->mm;
+
+ while ((user_ctx->partition != NULL) && (ret == FSL_RETURN_OK_S)) {
+
+ partition = user_ctx->partition;
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("Found an abandoned secure partition at %p, releasing",
+ partition);
+#endif
+
+ /* It appears that current->mm is not valid if this is called from a
+ * close routine (perhaps only if the program raised an exception that
+ * caused it to close?) If that is the case, then still free the
+ * partition, but do not remove it from the memory space (dangerous?)
+ */
+
+ if (mm == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("Warning: no mm structure found, not unmapping "
+ "partition from user memory\n");
+#endif
+ } else {
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ /* Note that this assumes a single memory partition */
+ unmap_user_memory(partition->user_base, 8192);
+ }
+
+ /* If the memory was successfully released */
+ if (ret == OS_ERROR_OK_S) {
+ /* release the partition */
+ scc_release_partition(partition->kernel_base);
+
+ /* and remove it from the users context */
+ deregister_user_partition(user_ctx,
+ partition->user_base);
+
+ ret = FSL_RETURN_OK_S;
+ } else {
+ ret = FSL_RETURN_ERROR_S;
+
+ goto out;
+ }
+ }
+ out:
+#endif /* FSL_HAVE_SCC2 */
+
+ return ret;
+}
+
+/*!
+ * Sets up memory to extract results from results pool
+ *
+ * @brief Sets up memory to extract results from results pool
+ *
+ * @param user_ctx information about this user
+ * @param[in,out] arg contains input parameters and fields that the driver
+ * fills in
+ *
+ * @return os error code or 0 on success
+ */
+int sah_get_results_pointers(fsl_shw_uco_t * user_ctx, uint32_t arg)
+{
+ sah_results results_arg; /* kernel mode usable version of 'arg' */
+ fsl_shw_result_t *user_results; /* user mode address of results */
+ unsigned *user_actual; /* user mode address of actual number of results */
+ unsigned actual; /* local memory of actual number of results */
+ int ret_val = OS_ERROR_FAIL_S;
+ sah_Head_Desc *finished_request;
+ unsigned int loop;
+
+ /* copy structure from user to kernel space */
+ if (!os_copy_from_user(&results_arg, (void *)arg, sizeof(sah_results))) {
+ /* save user space pointers */
+ user_actual = results_arg.actual; /* where count goes */
+ user_results = results_arg.results; /* where results goe */
+
+ /* Set pointer for actual value to temporary kernel memory location */
+ results_arg.actual = &actual;
+
+ /* Allocate kernel memory to hold temporary copy of the results */
+ results_arg.results =
+ os_alloc_memory(sizeof(fsl_shw_result_t) *
+ results_arg.requested, GFP_KERNEL);
+
+ /* if memory allocated, continue */
+ if (results_arg.results == NULL) {
+ ret_val = OS_ERROR_NO_MEMORY_S;
+ } else {
+ fsl_shw_return_t get_status;
+
+ /* get the results */
+ get_status =
+ sah_get_results_from_pool(user_ctx, &results_arg);
+
+ /* free the copy of the user space descriptor chain */
+ for (loop = 0; loop < actual; ++loop) {
+ /* get sah_Head_Desc from results and put user address into
+ * the return structure */
+ finished_request =
+ results_arg.results[loop].user_desc;
+ results_arg.results[loop].user_desc =
+ finished_request->user_desc;
+ /* return the descriptor chain memory to the block free pool */
+ sah_Free_Chained_Descriptors(finished_request);
+ }
+
+ /* if no errors, copy results and then the actual number of results
+ * back to user space
+ */
+ if (get_status == FSL_RETURN_OK_S) {
+ if (os_copy_to_user
+ (user_results, results_arg.results,
+ actual * sizeof(fsl_shw_result_t))
+ || os_copy_to_user(user_actual, &actual,
+ sizeof(user_actual))) {
+ ret_val = OS_ERROR_FAIL_S;
+ } else {
+ ret_val = 0; /* no error */
+ }
+ }
+ /* free the allocated memory */
+ os_free_memory(results_arg.results);
+ }
+ }
+
+ return ret_val;
+}
+
+/*!
+ * Extracts results from results pool
+ *
+ * @brief Extract results from results pool
+ *
+ * @param user_ctx information about this user
+ * @param[in,out] arg contains input parameters and fields that the
+ * driver fills in
+ *
+ * @return status code
+ */
+fsl_shw_return_t sah_get_results_from_pool(volatile fsl_shw_uco_t * user_ctx,
+ sah_results * arg)
+{
+ sah_Head_Desc *finished_request;
+ unsigned int loop = 0;
+ os_lock_context_t int_flags;
+
+ /* Get the number of results requested, up to total number of results
+ * available
+ */
+ do {
+ /* Protect state of user's result pool until we have retrieved and
+ * remove the first entry, or determined that the pool is empty. */
+ os_lock_save_context(desc_queue_lock, int_flags);
+ finished_request = user_ctx->result_pool.head;
+
+ if (finished_request != NULL) {
+ sah_Queue_Remove_Entry((sah_Queue *) & user_ctx->
+ result_pool);
+ os_unlock_restore_context(desc_queue_lock, int_flags);
+
+ /* Prepare to free. */
+ (void)sah_DePhysicalise_Descriptors(finished_request);
+
+ arg->results[loop].user_ref =
+ finished_request->user_ref;
+ arg->results[loop].code = finished_request->result;
+ arg->results[loop].detail1 =
+ finished_request->fault_address;
+ arg->results[loop].detail2 = 0;
+ arg->results[loop].user_desc = finished_request;
+
+ loop++;
+ } else { /* finished_request is NULL */
+ /* pool is empty */
+ os_unlock_restore_context(desc_queue_lock, int_flags);
+ }
+
+ } while ((loop < arg->requested) && (finished_request != NULL));
+
+ /* record number of results actually obtained */
+ *arg->actual = loop;
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * Converts descriptor chain to kernel space (from user space) and submits
+ * chain to Sahara for processing
+ *
+ * @brief Submits converted descriptor chain to sahara
+ *
+ * @param user_ctx Pointer to Kernel version of user's ctx
+ * @param user_space_desc user space address of descriptor chain that is
+ * in user space
+ *
+ * @return OS status code
+ */
+static int handle_sah_ioctl_dar(fsl_shw_uco_t * user_ctx,
+ uint32_t user_space_desc)
+{
+ int os_error_code = OS_ERROR_FAIL_S;
+ sah_Head_Desc *desc_chain_head; /* chain in kernel - virtual address */
+
+ /* This will re-create the linked list so that the SAHARA hardware can
+ * DMA on it.
+ */
+ desc_chain_head = sah_Copy_Descriptors(user_ctx,
+ (sah_Head_Desc *)
+ user_space_desc);
+
+ if (desc_chain_head == NULL) {
+ /* We may have failed due to a -EFAULT as well, but we will return
+ * OS_ERROR_NO_MEMORY_S since either way it is a memory related
+ * failure.
+ */
+ os_error_code = OS_ERROR_NO_MEMORY_S;
+ } else {
+ fsl_shw_return_t stat;
+
+ desc_chain_head->user_info = user_ctx;
+ desc_chain_head->user_desc = (sah_Head_Desc *) user_space_desc;
+
+ if (desc_chain_head->uco_flags & FSL_UCO_BLOCKING_MODE) {
+#ifdef SAHARA_POLL_MODE
+ sah_Handle_Poll(desc_chain_head);
+#else
+ sah_blocking_mode(desc_chain_head);
+#endif
+ stat = desc_chain_head->result;
+ /* return the descriptor chain memory to the block free pool */
+ sah_Free_Chained_Descriptors(desc_chain_head);
+ /* Tell user how the call turned out */
+
+ /* Copy 'result' back up to the result member.
+ *
+ * The dereference of the different member will cause correct the
+ * arithmetic to occur on the user-space address because of the
+ * missing dma/bus locations in the user mode version of the
+ * sah_Desc structure. */
+ os_error_code =
+ os_copy_to_user((void *)(user_space_desc
+ + offsetof(sah_Head_Desc,
+ uco_flags)),
+ &stat, sizeof(fsl_shw_return_t));
+
+ } else { /* not blocking mode - queue and forget */
+
+ if (desc_chain_head->uco_flags & FSL_UCO_CALLBACK_MODE) {
+ user_ctx->process = os_get_process_handle();
+ user_ctx->callback = sah_user_callback;
+ }
+#ifdef SAHARA_POLL_MODE
+ /* will put results in result pool */
+ sah_Handle_Poll(desc_chain_head);
+#else
+ /* just put someting in the DAR */
+ sah_Queue_Manager_Append_Entry(desc_chain_head);
+#endif
+ /* assume all went well */
+ os_error_code = OS_ERROR_OK_S;
+ }
+ }
+
+ return os_error_code;
+}
+
+static void sah_user_callback(fsl_shw_uco_t * user_ctx)
+{
+ os_send_signal(user_ctx->process, SIGUSR2);
+}
+
+/*!
+ * This function is called when a thread attempts to read from the /proc/sahara
+ * file. Upon read, statistics and information about the state of the driver
+ * are returned in nthe supplied buffer.
+ *
+ * @brief SAHARA PROCFS read function.
+ *
+ * @param buf Anything written to this buffer will be returned to the
+ * user-space process that is reading from this proc entry.
+ * @param start Part of the kernel prototype.
+ * @param offset Part of the kernel prototype.
+ * @param count The size of the buf argument.
+ * @param eof An integer which is set to one to tell the user-space
+ * process that there is no more data to read.
+ * @param data Part of the kernel prototype.
+ *
+ * @return The number of bytes written to the proc entry.
+ */
+#if !defined(CONFIG_DEVFS_FS) || (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int sah_read_procfs(char *buf,
+ char **start,
+ off_t offset, int count, int *eof, void *data)
+{
+ int output_bytes = 0;
+ int in_queue_count = 0;
+ os_lock_context_t lock_context;
+
+ os_lock_save_context(desc_queue_lock, lock_context);
+ in_queue_count = sah_Queue_Manager_Count_Entries(TRUE, 0);
+ os_unlock_restore_context(desc_queue_lock, lock_context);
+ output_bytes += snprintf(buf, count - output_bytes, "queued: %d\n",
+ in_queue_count);
+ output_bytes += snprintf(buf + output_bytes, count - output_bytes,
+ "Descriptors: %d, "
+ "Interrupts %d (%d Done1Done2, %d Done1Busy2, "
+ " %d Done1)\n",
+ dar_count, interrupt_count, done1done2_count,
+ done1busy2_count, done1_count);
+ output_bytes += snprintf(buf + output_bytes, count - output_bytes,
+ "Control: %08x\n", sah_HW_Read_Control());
+#if !defined(FSL_HAVE_SAHARA4) || defined(SAHARA4_NO_USE_SQUIB)
+ output_bytes += snprintf(buf + output_bytes, count - output_bytes,
+ "IDAR: %08x; CDAR: %08x\n",
+ sah_HW_Read_IDAR(), sah_HW_Read_CDAR());
+#endif
+#ifdef DIAG_DRV_STATUS
+ output_bytes += snprintf(buf + output_bytes, count - output_bytes,
+ "Status: %08x; Error Status: %08x; Op Status: %08x\n",
+ sah_HW_Read_Status(),
+ sah_HW_Read_Error_Status(),
+ sah_HW_Read_Op_Status());
+#endif
+#ifdef FSL_HAVE_SAHARA4
+ output_bytes += snprintf(buf + output_bytes, count - output_bytes,
+ "MMStat: %08x; Config: %08x\n",
+ sah_HW_Read_MM_Status(), sah_HW_Read_Config());
+#endif
+
+ /* Signal the end of the file */
+ *eof = 1;
+
+ /* To get rid of the unused parameter warnings */
+ (void)start;
+ (void)data;
+ (void)offset;
+
+ return output_bytes;
+}
+
+static int sah_write_procfs(struct file *file, const char __user * buffer,
+ unsigned long count, void *data)
+{
+
+ /* Any write to this file will reset all counts. */
+ dar_count = interrupt_count = done1done2_count =
+ done1busy2_count = done1_count = 0;
+
+ (void)file;
+ (void)buffer;
+ (void)data;
+
+ return count;
+}
+
+#endif
+
+#ifndef SAHARA_POLL_MODE
+/*!
+ * Block user call until processing is complete.
+ *
+ * @param entry The user's request.
+ *
+ * @return An OS error code, or 0 if no error
+ */
+int sah_blocking_mode(sah_Head_Desc * entry)
+{
+ int os_error_code = 0;
+ sah_Queue_Status status;
+
+ /* queue entry, put something in the DAR, if nothing is there currently */
+ sah_Queue_Manager_Append_Entry(entry);
+
+ /* get this descriptor chain's current status */
+ status = ((volatile sah_Head_Desc *)entry)->status;
+
+ while (!SAH_DESC_PROCESSED(status)) {
+ extern sah_Queue *main_queue;
+
+ DEFINE_WAIT(sahara_wait); /* create a wait queue entry. Linux */
+
+ /* enter the wait queue entry into the queue */
+ prepare_to_wait(&Wait_queue, &sahara_wait, TASK_INTERRUPTIBLE);
+
+ /* check if this entry has been processed */
+ status = ((volatile sah_Head_Desc *)entry)->status;
+
+ if (!SAH_DESC_PROCESSED(status)) {
+ /* go to sleep - Linux */
+ schedule();
+ }
+
+ /* un-queue the 'prepare to wait' queue? - Linux */
+ finish_wait(&Wait_queue, &sahara_wait);
+
+ /* signal belongs to this thread? */
+ if (signal_pending(current)) { /* Linux */
+ os_lock_context_t lock_flags;
+
+ /* don't allow access during this check and operation */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ status = ((volatile sah_Head_Desc *)entry)->status;
+ if (status == SAH_STATE_PENDING) {
+ sah_Queue_Remove_Any_Entry(main_queue, entry);
+ entry->result = FSL_RETURN_INTERNAL_ERROR_S;
+ ((volatile sah_Head_Desc *)entry)->status =
+ SAH_STATE_FAILED;
+ }
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+ }
+
+ status = ((volatile sah_Head_Desc *)entry)->status;
+ } /* while ... */
+
+ /* Do this so that caller can free */
+ (void)sah_DePhysicalise_Descriptors(entry);
+
+ return os_error_code;
+}
+
+/*!
+ * If interrupt does not return in a reasonable time, time out, trigger
+ * interrupt, and continue with process
+ *
+ * @param data ignored
+ */
+void sahara_timeout_handler(unsigned long data)
+{
+ /* Sahara has not issuing an interrupt, so timed out */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Sahara HW did not respond. Resetting.\n");
+#endif
+ /* assume hardware needs resetting */
+ sah_Handle_Interrupt(SAH_EXEC_FAULT);
+ /* wake up sleeping thread to try again */
+ wake_up_interruptible(&Wait_queue);
+}
+
+#endif /* ifndef SAHARA_POLL_MODE */
+
+/* End of sah_driver_interface.c */
diff --git a/drivers/mxc/security/sahara2/sah_hardware_interface.c b/drivers/mxc/security/sahara2/sah_hardware_interface.c
new file mode 100644
index 000000000000..cf5065379d3d
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_hardware_interface.c
@@ -0,0 +1,854 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file sah_hardware_interface.c
+ *
+ * @brief Provides an interface to the SAHARA hardware registers.
+ *
+ */
+
+/* SAHARA Includes */
+#include <sah_driver_common.h>
+#include <sah_hardware_interface.h>
+#include <sah_memory_mapper.h>
+#include <sah_kernel.h>
+
+#if defined DIAG_DRV_IF || defined(DO_DBG)
+#include <diagnostic.h>
+#ifndef LOG_KDIAG
+#define LOG_KDIAG(x) os_printk("%s\n", x)
+#endif
+
+static void sah_Dump_Link(const char *prefix, const sah_Link * link,
+ dma_addr_t addr);
+
+/* This is for sprintf() to use when constructing output. */
+#define DIAG_MSG_SIZE 1024
+/* was 200 */
+#define MAX_DUMP 200
+static char Diag_msg[DIAG_MSG_SIZE];
+
+#endif /* DIAG_DRV_IF */
+
+/*!
+ * Number of descriptors sent to Sahara. This value should only be updated
+ * with the main queue lock held.
+ */
+uint32_t dar_count;
+
+/*! The "link-list optimize" bit in the Header of a Descriptor */
+#define SAH_HDR_LLO 0x01000000
+
+/* IO_ADDRESS() is Linux macro -- need portable equivalent */
+#define SAHARA_BASE_ADDRESS IO_ADDRESS(SAHA_BASE_ADDR)
+#define SAHARA_VERSION_REGISTER_OFFSET 0x000
+#define SAHARA_DAR_REGISTER_OFFSET 0x004
+#define SAHARA_CONTROL_REGISTER_OFFSET 0x008
+#define SAHARA_COMMAND_REGISTER_OFFSET 0x00C
+#define SAHARA_STATUS_REGISTER_OFFSET 0x010
+#define SAHARA_ESTATUS_REGISTER_OFFSET 0x014
+#define SAHARA_FLT_ADD_REGISTER_OFFSET 0x018
+#define SAHARA_CDAR_REGISTER_OFFSET 0x01C
+#define SAHARA_IDAR_REGISTER_OFFSET 0x020
+#define SAHARA_OSTATUS_REGISTER_OFFSET 0x028
+#define SAHARA_CONFIG_REGISTER_OFFSET 0x02C
+#define SAHARA_MM_STAT_REGISTER_OFFSET 0x030
+
+/*! Register within Sahara which contains hardware version. (1 or 2). */
+#define SAHARA_VERSION_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_VERSION_REGISTER_OFFSET)
+
+/*! Register within Sahara which is used to provide new work to the block. */
+#define SAHARA_DAR_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_DAR_REGISTER_OFFSET)
+
+/*! Register with Sahara which is used for configuration. */
+#define SAHARA_CONTROL_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_CONTROL_REGISTER_OFFSET)
+
+/*! Register with Sahara which is used for changing status. */
+#define SAHARA_COMMAND_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_COMMAND_REGISTER_OFFSET)
+
+/*! Register with Sahara which is contains status and state. */
+#define SAHARA_STATUS_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_STATUS_REGISTER_OFFSET)
+
+/*! Register with Sahara which is contains error status information. */
+#define SAHARA_ESTATUS_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_ESTATUS_REGISTER_OFFSET)
+
+/*! Register with Sahara which is contains faulting address information. */
+#define SAHARA_FLT_ADD_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_FLT_ADD_REGISTER_OFFSET)
+
+/*! Register with Sahara which is contains current descriptor address. */
+#define SAHARA_CDAR_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_CDAR_REGISTER_OFFSET)
+
+/*! Register with Sahara which is contains initial descriptor address (of a
+ chain). */
+#define SAHARA_IDAR_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_IDAR_REGISTER_OFFSET)
+
+/*! Register with Sahara which is contains op status information. */
+#define SAHARA_OSTATUS_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_OSTATUS_REGISTER_OFFSET)
+
+/*! Register with Sahara which is contains configuration information. */
+#define SAHARA_CONFIG_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_CONFIG_REGISTER_OFFSET)
+
+/*! Register with Sahara which is contains configuration information. */
+#define SAHARA_MM_STAT_REGISTER (SAHARA_BASE_ADDRESS + \
+ SAHARA_MM_STAT_REGISTER_OFFSET)
+
+/* Local Functions */
+#if defined DIAG_DRV_IF || defined DO_DBG
+void sah_Dump_Region(const char *prefix, const unsigned char *data,
+ dma_addr_t addr, unsigned length);
+
+#endif /* DIAG_DRV_IF */
+
+/* time out value when polling SAHARA status register for completion */
+static uint32_t sah_poll_timeout = 0xFFFFFFFF;
+
+/*!
+ * Polls Sahara to determine when its current operation is complete
+ *
+ * @return last value found in Sahara's status register
+ */
+sah_Execute_Status sah_Wait_On_Sahara()
+{
+ uint32_t count = 0; /* ensure we don't get stuck in the loop forever */
+ sah_Execute_Status status; /* Sahara's status register */
+ uint32_t stat_reg;
+
+ pr_debug("Entered sah_Wait_On_Sahara\n");
+
+ do {
+ /* get current status register from Sahara */
+ stat_reg = sah_HW_Read_Status();
+ status = stat_reg & SAH_EXEC_STATE_MASK;
+
+ /* timeout if SAHARA takes too long to complete */
+ if (++count == sah_poll_timeout) {
+ status = SAH_EXEC_FAULT;
+ printk("sah_Wait_On_Sahara timed out\n");
+ }
+
+ /* stay in loop as long as Sahara is still busy */
+ } while ((status == SAH_EXEC_BUSY) || (status == SAH_EXEC_DONE1_BUSY2));
+
+ if (status == SAH_EXEC_ERROR1) {
+ if (stat_reg & OP_STATUS) {
+ status = SAH_EXEC_OPSTAT1;
+ }
+ }
+
+ return status;
+} /* sah_Wait_on_Sahara() */
+
+/*!
+ * This function resets the SAHARA hardware. The following operations are
+ * performed:
+ * 1. Resets SAHARA.
+ * 2. Requests BATCH mode.
+ * 3. Enables interrupts.
+ * 4. Requests Little Endian mode.
+ *
+ * @brief SAHARA hardware reset function.
+ *
+ * @return void
+ */
+int sah_HW_Reset(void)
+{
+ sah_Execute_Status sah_state;
+ int status; /* this is the value to return to the calling routine */
+ uint32_t saha_control = 0;
+
+#ifndef USE_3WORD_BURST
+#ifdef FSL_HAVE_SAHARA2
+ saha_control |= (8 << 16); /* Allow 8-word burst */
+#endif
+#else
+/***************** HARDWARE BUG WORK AROUND ******************/
+/* A burst size of > 4 can cause Sahara DMA to issue invalid AHB transactions
+ * when crossing 1KB boundaries. By limiting the 'burst size' to 3, these
+ * invalid transactions will not be generated, but Sahara will still transfer
+ * data more efficiently than if the burst size were set to 1.
+ */
+ saha_control |= (3 << 16); /* Limit DMA burst size. For versions 2/3 */
+#endif /* USE_3WORD_BURST */
+
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Address of SAHARA_BASE_ADDRESS = 0x%08x\n",
+ SAHARA_BASE_ADDRESS);
+ LOG_KDIAG(Diag_msg);
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Sahara Status register before reset: %08x",
+ sah_HW_Read_Status());
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ /* Write the Reset & BATCH mode command to the SAHARA Command register. */
+ sah_HW_Write_Command(CMD_BATCH | CMD_RESET);
+#ifdef SAHARA4_NO_USE_SQUIB
+ {
+ uint32_t cfg = sah_HW_Read_Config();
+ cfg &= ~0x10000;
+ sah_HW_Write_Config(cfg);
+ }
+#endif
+
+ sah_poll_timeout = 0x0FFFFFFF;
+ sah_state = sah_Wait_On_Sahara();
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Sahara Status register after reset: %08x",
+ sah_HW_Read_Status());
+ LOG_KDIAG(Diag_msg);
+#endif
+ /* on reset completion, check that Sahara is in the idle state */
+ status = (sah_state == SAH_EXEC_IDLE) ? 0 : OS_ERROR_FAIL_S;
+
+ /* Set initial value out of reset */
+ sah_HW_Write_Control(saha_control);
+
+#ifndef NO_RESEED_WORKAROUND
+/***************** HARDWARE BUG WORK AROUND ******************/
+/* In order to set the 'auto reseed' bit, must first acquire a random value. */
+ /*
+ * to solve a hardware bug, a random number must be generated before
+ * the 'RNG Auto Reseed' bit can be set. So this generates a random
+ * number that is thrown away.
+ *
+ * Note that the interrupt bit has not been set at this point so
+ * the result can be polled.
+ */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Create and submit Random Number Descriptor");
+#endif
+
+ if (status == OS_ERROR_OK_S) {
+ /* place to put random number */
+ volatile uint32_t *random_data_ptr;
+ sah_Head_Desc *random_desc;
+ dma_addr_t desc_dma;
+ dma_addr_t rand_dma;
+ const int rnd_cnt = 3; /* how many random 32-bit values to get */
+
+ /* Get space for data -- assume at least 32-bit aligned! */
+ random_data_ptr = os_alloc_memory(rnd_cnt * sizeof(uint32_t),
+ GFP_ATOMIC);
+
+ random_desc = sah_Alloc_Head_Descriptor();
+
+ if ((random_data_ptr == NULL) || (random_desc == NULL)) {
+ status = OS_ERROR_FAIL_S;
+ } else {
+ int i;
+
+ /* Clear out values */
+ for (i = 0; i < rnd_cnt; i++) {
+ random_data_ptr[i] = 0;
+ }
+
+ rand_dma = os_pa(random_data_ptr);
+
+ random_desc->desc.header = 0xB18C0000; /* LLO get random number */
+ random_desc->desc.len1 =
+ rnd_cnt * sizeof(*random_data_ptr);
+ random_desc->desc.ptr1 = (void *)rand_dma;
+ random_desc->desc.original_ptr1 =
+ (void *)random_data_ptr;
+
+ random_desc->desc.len2 = 0; /* not used */
+ random_desc->desc.ptr2 = 0; /* not used */
+
+ random_desc->desc.next = 0; /* chain terminates here */
+ random_desc->desc.original_next = 0; /* chain terminates here */
+
+ desc_dma = random_desc->desc.dma_addr;
+
+ /* Force in-cache data out to RAM */
+ os_cache_clean_range(random_data_ptr,
+ rnd_cnt *
+ sizeof(*random_data_ptr));
+
+ /* pass descriptor to Sahara */
+ sah_HW_Write_DAR(desc_dma);
+
+ /*
+ * Wait for RNG to complete (interrupts are disabled at this point
+ * due to sahara being reset previously) then check for error
+ */
+ sah_state = sah_Wait_On_Sahara();
+ /* Force CPU to ignore in-cache and reload from RAM */
+ os_cache_inv_range(random_data_ptr,
+ rnd_cnt * sizeof(*random_data_ptr));
+
+ /* if it didn't move to done state, an error occured */
+ if (
+#ifndef SUBMIT_MULTIPLE_DARS
+ (sah_state != SAH_EXEC_IDLE) &&
+#endif
+ (sah_state != SAH_EXEC_DONE1)
+ ) {
+ status = OS_ERROR_FAIL_S;
+ os_printk
+ ("(sahara) Failure: state is %08x; random_data is"
+ " %08x\n", sah_state, *random_data_ptr);
+ os_printk
+ ("(sahara) CDAR: %08x, IDAR: %08x, FADR: %08x,"
+ " ESTAT: %08x\n", sah_HW_Read_CDAR(),
+ sah_HW_Read_IDAR(),
+ sah_HW_Read_Fault_Address(),
+ sah_HW_Read_Error_Status());
+ } else {
+ int i;
+ int seen_rand = 0;
+
+ for (i = 0; i < rnd_cnt; i++) {
+ if (*random_data_ptr != 0) {
+ seen_rand = 1;
+ break;
+ }
+ }
+ if (!seen_rand) {
+ status = OS_ERROR_FAIL_S;
+ os_printk
+ ("(sahara) Error: Random number is zero!\n");
+ }
+ }
+ }
+
+ if (random_data_ptr) {
+ os_free_memory((void *)random_data_ptr);
+ }
+ if (random_desc) {
+ sah_Free_Head_Descriptor(random_desc);
+ }
+ }
+/***************** END HARDWARE BUG WORK AROUND ******************/
+#endif
+
+ if (status == 0) {
+#ifdef FSL_HAVE_SAHARA2
+ saha_control |= CTRL_RNG_RESEED;
+#endif
+
+#ifndef SAHARA_POLL_MODE
+ saha_control |= CTRL_INT_EN; /* enable interrupts */
+#else
+ sah_poll_timeout = SAHARA_POLL_MODE_TIMEOUT;
+#endif
+
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Setting up Sahara's Control Register: %08x\n",
+ saha_control);
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ /* Rewrite the setup to the SAHARA Control register */
+ sah_HW_Write_Control(saha_control);
+#ifdef DIAG_DRV_IF
+ snprintf(Diag_msg, DIAG_MSG_SIZE,
+ "Sahara Status register after control write: %08x",
+ sah_HW_Read_Status());
+ LOG_KDIAG(Diag_msg);
+#endif
+
+#ifdef FSL_HAVE_SAHARA4
+ {
+ uint32_t cfg = sah_HW_Read_Config();
+ sah_HW_Write_Config(cfg | 0x100); /* Add RNG auto-reseed */
+ }
+#endif
+ } else {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Reset failed\n");
+#endif
+ }
+
+ return status;
+} /* sah_HW_Reset() */
+
+/*!
+ * This function enables High Assurance mode.
+ *
+ * @brief SAHARA hardware enable High Assurance mode.
+ *
+ * @return FSL_RETURN_OK_S - if HA was set successfully
+ * @return FSL_RETURN_INTERNAL_ERROR_S - if HA was not set due to SAHARA
+ * being busy.
+ */
+fsl_shw_return_t sah_HW_Set_HA(void)
+{
+ /* This is the value to write to the register */
+ uint32_t value;
+
+ /* Read from the control register. */
+ value = sah_HW_Read_Control();
+
+ /* Set the HA bit */
+ value |= CTRL_HA;
+
+ /* Write to the control register. */
+ sah_HW_Write_Control(value);
+
+ /* Read from the control register. */
+ value = sah_HW_Read_Control();
+
+ return (value & CTRL_HA) ? FSL_RETURN_OK_S :
+ FSL_RETURN_INTERNAL_ERROR_S;
+}
+
+/*!
+ * This function reads the SAHARA hardware Version Register.
+ *
+ * @brief Read SAHARA hardware Version Register.
+ *
+ * @return uint32_t Register value.
+ */
+uint32_t sah_HW_Read_Version(void)
+{
+ return os_read32(SAHARA_VERSION_REGISTER);
+}
+
+/*!
+ * This function reads the SAHARA hardware Control Register.
+ *
+ * @brief Read SAHARA hardware Control Register.
+ *
+ * @return uint32_t Register value.
+ */
+uint32_t sah_HW_Read_Control(void)
+{
+ return os_read32(SAHARA_CONTROL_REGISTER);
+}
+
+/*!
+ * This function reads the SAHARA hardware Status Register.
+ *
+ * @brief Read SAHARA hardware Status Register.
+ *
+ * @return uint32_t Register value.
+ */
+uint32_t sah_HW_Read_Status(void)
+{
+ return os_read32(SAHARA_STATUS_REGISTER);
+}
+
+/*!
+ * This function reads the SAHARA hardware Error Status Register.
+ *
+ * @brief Read SAHARA hardware Error Status Register.
+ *
+ * @return uint32_t Error Status value.
+ */
+uint32_t sah_HW_Read_Error_Status(void)
+{
+ return os_read32(SAHARA_ESTATUS_REGISTER);
+}
+
+/*!
+ * This function reads the SAHARA hardware Op Status Register.
+ *
+ * @brief Read SAHARA hardware Op Status Register.
+ *
+ * @return uint32_t Op Status value.
+ */
+uint32_t sah_HW_Read_Op_Status(void)
+{
+ return os_read32(SAHARA_OSTATUS_REGISTER);
+}
+
+/*!
+ * This function reads the SAHARA hardware Descriptor Address Register.
+ *
+ * @brief Read SAHARA hardware DAR Register.
+ *
+ * @return uint32_t DAR value.
+ */
+uint32_t sah_HW_Read_DAR(void)
+{
+ return os_read32(SAHARA_DAR_REGISTER);
+}
+
+/*!
+ * This function reads the SAHARA hardware Current Descriptor Address Register.
+ *
+ * @brief Read SAHARA hardware CDAR Register.
+ *
+ * @return uint32_t CDAR value.
+ */
+uint32_t sah_HW_Read_CDAR(void)
+{
+ return os_read32(SAHARA_CDAR_REGISTER);
+}
+
+/*!
+ * This function reads the SAHARA hardware Initial Descriptor Address Register.
+ *
+ * @brief Read SAHARA hardware IDAR Register.
+ *
+ * @return uint32_t IDAR value.
+ */
+uint32_t sah_HW_Read_IDAR(void)
+{
+ return os_read32(SAHARA_IDAR_REGISTER);
+}
+
+/*!
+ * This function reads the SAHARA hardware Fault Address Register.
+ *
+ * @brief Read SAHARA Fault Address Register.
+ *
+ * @return uint32_t Fault Address value.
+ */
+uint32_t sah_HW_Read_Fault_Address(void)
+{
+ return os_read32(SAHARA_FLT_ADD_REGISTER);
+}
+
+/*!
+ * This function reads the SAHARA hardware Multiple Master Status Register.
+ *
+ * @brief Read SAHARA hardware MM Stat Register.
+ *
+ * @return uint32_t MM Stat value.
+ */
+uint32_t sah_HW_Read_MM_Status(void)
+{
+ return os_read32(SAHARA_MM_STAT_REGISTER);
+}
+
+/*!
+ * This function reads the SAHARA hardware Configuration Register.
+ *
+ * @brief Read SAHARA Configuration Register.
+ *
+ * @return uint32_t Configuration value.
+ */
+uint32_t sah_HW_Read_Config(void)
+{
+ return os_read32(SAHARA_CONFIG_REGISTER);
+}
+
+/*!
+ * This function writes a command to the SAHARA hardware Command Register.
+ *
+ * @brief Write to SAHARA hardware Command Register.
+ *
+ * @param command An unsigned 32bit command value.
+ *
+ * @return void
+ */
+void sah_HW_Write_Command(uint32_t command)
+{
+ os_write32(SAHARA_COMMAND_REGISTER, command);
+}
+
+/*!
+ * This function writes a control value to the SAHARA hardware Control
+ * Register.
+ *
+ * @brief Write to SAHARA hardware Control Register.
+ *
+ * @param control An unsigned 32bit control value.
+ *
+ * @return void
+ */
+void sah_HW_Write_Control(uint32_t control)
+{
+ os_write32(SAHARA_CONTROL_REGISTER, control);
+}
+
+/*!
+ * This function writes a configuration value to the SAHARA hardware Configuration
+ * Register.
+ *
+ * @brief Write to SAHARA hardware Configuration Register.
+ *
+ * @param configuration An unsigned 32bit configuration value.
+ *
+ * @return void
+ */
+void sah_HW_Write_Config(uint32_t configuration)
+{
+ os_write32(SAHARA_CONFIG_REGISTER, configuration);
+}
+
+/*!
+ * This function writes a descriptor address to the SAHARA Descriptor Address
+ * Register.
+ *
+ * @brief Write to SAHARA Descriptor Address Register.
+ *
+ * @param pointer An unsigned 32bit descriptor address value.
+ *
+ * @return void
+ */
+void sah_HW_Write_DAR(uint32_t pointer)
+{
+ os_write32(SAHARA_DAR_REGISTER, pointer);
+ dar_count++;
+}
+
+#if defined DIAG_DRV_IF || defined DO_DBG
+
+static char *interpret_header(uint32_t header)
+{
+ unsigned desc_type = ((header >> 24) & 0x70) | ((header >> 16) & 0xF);
+
+ switch (desc_type) {
+ case 0x12:
+ return "5/SKHA_ST_CTX";
+ case 0x13:
+ return "35/SKHA_LD_MODE_KEY";
+ case 0x14:
+ return "38/SKHA_LD_MODE_IN_CPHR_ST_CTX";
+ case 0x15:
+ return "4/SKHA_IN_CPHR_OUT";
+ case 0x16:
+ return "34/SKHA_ST_SBOX";
+ case 0x18:
+ return "1/SKHA_LD_MODE_IV_KEY";
+ case 0x19:
+ return "33/SKHA_ST_SBOX";
+ case 0x1D:
+ return "2/SKHA_LD_MODE_IN_CPHR_OUT";
+ case 0x22:
+ return "11/MDHA_ST_MD";
+ case 0x25:
+ return "10/MDHA_HASH_ST_MD";
+ case 0x28:
+ return "6/MDHA_LD_MODE_MD_KEY";
+ case 0x2A:
+ return "39/MDHA_ICV";
+ case 0x2D:
+ return "8/MDHA_LD_MODE_HASH_ST_MD";
+ case 0x3C:
+ return "18/RNG_GEN";
+ case 0x40:
+ return "19/PKHA_LD_N_E";
+ case 0x41:
+ return "36/PKHA_LD_A3_B0";
+ case 0x42:
+ return "27/PKHA_ST_A_B";
+ case 0x43:
+ return "22/PKHA_LD_A_B";
+ case 0x44:
+ return "23/PKHA_LD_A0_A1";
+ case 0x45:
+ return "24/PKHA_LD_A2_A3";
+ case 0x46:
+ return "25/PKHA_LD_B0_B1";
+ case 0x47:
+ return "26/PKHA_LD_B2_B3";
+ case 0x48:
+ return "28/PKHA_ST_A0_A1";
+ case 0x49:
+ return "29/PKHA_ST_A2_A3";
+ case 0x4A:
+ return "30/PKHA_ST_B0_B1";
+ case 0x4B:
+ return "31/PKHA_ST_B2_B3";
+ case 0x4C:
+ return "32/PKHA_EX_ST_B1";
+ case 0x4D:
+ return "20/PKHA_LD_A_EX_ST_B";
+ case 0x4E:
+ return "21/PKHA_LD_N_EX_ST_B";
+ case 0x4F:
+ return "37/PKHA_ST_B1_B2";
+ default:
+ return "??/UNKNOWN";
+ }
+} /* cvt_desc_name() */
+
+/*!
+ * Dump chain of descriptors to the log.
+ *
+ * @brief Dump descriptor chain
+ *
+ * @param chain Kernel virtual address of start of chain of descriptors
+ *
+ * @return void
+ */
+void sah_Dump_Chain(const sah_Desc * chain, dma_addr_t addr)
+{
+ int desc_no = 1;
+
+ pr_debug("Chain for Sahara\n");
+
+ while (chain != NULL) {
+ char desc_name[50];
+
+ sprintf(desc_name, "Desc %02d (%s)\n" KERN_DEBUG "Desc ",
+ desc_no++, interpret_header(chain->header));
+
+ sah_Dump_Words(desc_name, (unsigned *)chain, addr,
+ 6 /* #words in h/w link */ );
+ if (chain->original_ptr1) {
+ if (chain->header & SAH_HDR_LLO) {
+ sah_Dump_Region(" Data1",
+ (unsigned char *)chain->
+ original_ptr1,
+ (dma_addr_t) chain->ptr1,
+ chain->len1);
+ } else {
+ sah_Dump_Link(" Link1", chain->original_ptr1,
+ (dma_addr_t) chain->ptr1);
+ }
+ }
+ if (chain->ptr2) {
+ if (chain->header & SAH_HDR_LLO) {
+ sah_Dump_Region(" Data2",
+ (unsigned char *)chain->
+ original_ptr2,
+ (dma_addr_t) chain->ptr2,
+ chain->len2);
+ } else {
+ sah_Dump_Link(" Link2", chain->original_ptr2,
+ (dma_addr_t) chain->ptr2);
+ }
+ }
+
+ addr = (dma_addr_t) chain->next;
+ chain = (chain->next) ? (chain->original_next) : NULL;
+ }
+}
+
+/*!
+ * Dump chain of links to the log.
+ *
+ * @brief Dump chain of links
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param link Kernel virtual address of start of chain of links
+ *
+ * @return void
+ */
+static void sah_Dump_Link(const char *prefix, const sah_Link * link,
+ dma_addr_t addr)
+{
+#ifdef DUMP_SCC_DATA
+ extern uint8_t *sahara_partition_base;
+ extern dma_addr_t sahara_partition_phys;
+#endif
+
+ while (link != NULL) {
+ sah_Dump_Words(prefix, (unsigned *)link, addr,
+ 3 /* # words in h/w link */ );
+ if (link->flags & SAH_STORED_KEY_INFO) {
+#ifdef SAH_DUMP_DATA
+#ifdef DUMP_SCC_DATA
+ sah_Dump_Region(" Data",
+ (uint8_t *) link->data -
+ (uint8_t *) sahara_partition_phys +
+ sahara_partition_base,
+ (dma_addr_t) link->data, link->len);
+#else
+ pr_debug(" Key Slot %d\n", link->slot);
+#endif
+#endif
+ } else {
+#ifdef SAH_DUMP_DATA
+ sah_Dump_Region(" Data", link->original_data,
+ (dma_addr_t) link->data, link->len);
+#endif
+ }
+ addr = (dma_addr_t) link->next;
+ link = link->original_next;
+ }
+}
+
+/*!
+ * Dump given region of data to the log.
+ *
+ * @brief Dump data
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param data Kernel virtual address of start of region to dump
+ * @param length Amount of data to dump
+ *
+ * @return void
+ */
+void sah_Dump_Region(const char *prefix, const unsigned char *data,
+ dma_addr_t addr, unsigned length)
+{
+ unsigned count;
+ char *output;
+ unsigned data_len;
+
+ sprintf(Diag_msg, "%s (%08X,%u):", prefix, addr, length);
+
+ /* Restrict amount of data to dump */
+ if (length > MAX_DUMP) {
+ data_len = MAX_DUMP;
+ } else {
+ data_len = length;
+ }
+
+ /* We've already printed some text in output buffer, skip over it */
+ output = Diag_msg + strlen(Diag_msg);
+
+ for (count = 0; count < data_len; count++) {
+ if ((count % 4) == 0) {
+ *output++ = ' ';
+ }
+ sprintf(output, "%02X", *data++);
+ output += 2;
+ }
+
+ pr_debug("%s\n", Diag_msg);
+}
+
+/*!
+ * Dump given word of data to the log.
+ *
+ * @brief Dump data
+ *
+ * @param prefix Text to put in front of dumped data
+ * @param data Kernel virtual address of start of region to dump
+ * @param word_count Amount of data to dump
+ *
+ * @return void
+ */
+void sah_Dump_Words(const char *prefix, const unsigned *data, dma_addr_t addr,
+ unsigned word_count)
+{
+ char *output;
+
+ sprintf(Diag_msg, "%s (%08X,%uw): ", prefix, addr, word_count);
+
+ /* We've already printed some text in output buffer, skip over it */
+ output = Diag_msg + strlen(Diag_msg);
+
+ while (word_count--) {
+ sprintf(output, "%08X ", *data++);
+ output += 9;
+ }
+
+ pr_debug("%s\n", Diag_msg);
+
+}
+
+#endif /* DIAG_DRV_IF */
+
+/* End of sah_hardware_interface.c */
diff --git a/drivers/mxc/security/sahara2/sah_interrupt_handler.c b/drivers/mxc/security/sahara2/sah_interrupt_handler.c
new file mode 100644
index 000000000000..3d70fe8d2c64
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_interrupt_handler.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+* @file sah_interrupt_handler.c
+*
+* @brief Provides a hardware interrupt handling mechanism for device driver.
+*
+* This file needs to be ported for a non-Linux OS.
+*
+* It gets a call at #sah_Intr_Init() during initialization.
+*
+* #sah_Intr_Top_Half() is intended to be the Interrupt Service Routine. It
+* calls a portable function in another file to process the Sahara status.
+*
+* #sah_Intr_Bottom_Half() is a 'background' task scheduled by the top half to
+* take care of the expensive tasks of the interrupt processing.
+*
+* The driver shutdown code calls #sah_Intr_Release().
+*
+*/
+
+#include <portable_os.h>
+
+/* SAHARA Includes */
+#include <sah_kernel.h>
+#include <sah_interrupt_handler.h>
+#include <sah_status_manager.h>
+#include <sah_hardware_interface.h>
+#include <sah_queue_manager.h>
+
+/*Enable this flag for debugging*/
+#if 0
+#define DIAG_DRV_INTERRUPT
+#endif
+
+#ifdef DIAG_DRV_INTERRUPT
+#include <diagnostic.h>
+#endif
+
+/*!
+ * Number of interrupts received. This value should only be updated during
+ * interrupt processing.
+ */
+uint32_t interrupt_count;
+
+#ifndef SAHARA_POLL_MODE
+
+#if !defined(LINUX_VERSION_CODE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#define irqreturn_t void
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#endif
+
+/* Internal Prototypes */
+static irqreturn_t sah_Intr_Top_Half(int irq, void *dev_id);
+
+#ifdef KERNEL_TEST
+extern void (*SAHARA_INT_PTR) (int, void *);
+#endif
+
+unsigned long reset_flag;
+static void sah_Intr_Bottom_Half(unsigned long reset_flag);
+
+/* This is the Bottom Half Task, (reset flag set to false) */
+DECLARE_TASKLET(BH_task, sah_Intr_Bottom_Half, (unsigned long)&reset_flag);
+
+/*! This is set by the Initialisation function */
+wait_queue_head_t *int_queue = NULL;
+
+/*!
+*******************************************************************************
+* This function registers the Top Half of the interrupt handler with the Kernel
+* and the SAHARA IRQ number.
+*
+* @brief SAHARA Interrupt Handler Initialisation
+*
+* @param wait_queue Pointer to the wait queue used by driver interface
+*
+* @return int A return of Zero indicates successful initialisation.
+*/
+/******************************************************************************
+*
+* CAUTION: NONE
+*
+* MODIFICATION HISTORY:
+*
+* Date Person Change
+* 30/07/2003 MW Initial Creation
+******************************************************************************/
+int sah_Intr_Init(wait_queue_head_t * wait_queue)
+{
+
+#ifdef DIAG_DRV_INTERRUPT
+ char err_string[200];
+#endif
+
+ int result;
+
+#ifdef KERNEL_TEST
+ SAHARA_INT_PTR = sah_Intr_Top_Half;
+#endif
+
+ /* Set queue used by the interrupt handler to match the driver interface */
+ int_queue = wait_queue;
+
+ /* Request use of the Interrupt line. */
+ result = request_irq(SAHARA_IRQ,
+ sah_Intr_Top_Half, 0, SAHARA_NAME, NULL);
+
+#ifdef DIAG_DRV_INTERRUPT
+ if (result != 0) {
+ sprintf(err_string, "Cannot use SAHARA interrupt line %d. "
+ "request_irq() return code is %i.", SAHARA_IRQ, result);
+ LOG_KDIAG(err_string);
+ } else {
+ sprintf(err_string,
+ "SAHARA driver registered for interrupt %d. ",
+ SAHARA_IRQ);
+ LOG_KDIAG(err_string);
+ }
+#endif
+
+ return result;
+}
+
+/*!
+*******************************************************************************
+* This function releases the Top Half of the interrupt handler. The driver will
+* not receive any more interrupts after calling this functions.
+*
+* @brief SAHARA Interrupt Handler Release
+*
+* @return void
+*/
+/******************************************************************************
+*
+* CAUTION: NONE
+*
+* MODIFICATION HISTORY:
+*
+* Date Person Change
+* 30/07/2003 MW Initial Creation
+******************************************************************************/
+void sah_Intr_Release(void)
+{
+ /* Release the Interrupt. */
+ free_irq(SAHARA_IRQ, NULL);
+}
+
+/*!
+*******************************************************************************
+* This function is the Top Half of the interrupt handler. It updates the
+* status of any finished descriptor chains and then tries to add any pending
+* requests into the hardware. It then queues the bottom half to complete
+* operations on the finished chains.
+*
+* @brief SAHARA Interrupt Handler Top Half
+*
+* @param irq Part of the kernel prototype.
+* @param dev_id Part of the kernel prototype.
+*
+* @return An IRQ_RETVAL() -- non-zero to that function means 'handled'
+*/
+static irqreturn_t sah_Intr_Top_Half(int irq, void *dev_id)
+{
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+ LOG_KDIAG("Top half of Sahara's interrupt handler called.");
+#endif
+
+ interrupt_count++;
+ reset_flag = sah_Handle_Interrupt(sah_HW_Read_Status());
+
+ /* Schedule the Bottom Half of the Interrupt. */
+ tasklet_schedule(&BH_task);
+
+ /* To get rid of the unused parameter warnings. */
+ irq = 0;
+ dev_id = NULL;
+ return IRQ_RETVAL(1);
+}
+
+/*!
+*******************************************************************************
+* This function is the Bottom Half of the interrupt handler. It calls
+* #sah_postprocess_queue() to complete the processing of the Descriptor Chains
+* which were finished by the hardware.
+*
+* @brief SAHARA Interrupt Handler Bottom Half
+*
+* @param data Part of the kernel prototype.
+*
+* @return void
+*/
+static void sah_Intr_Bottom_Half(unsigned long reset_flag)
+{
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+ LOG_KDIAG("Bottom half of Sahara's interrupt handler called.");
+#endif
+ sah_postprocess_queue(*(unsigned long *)reset_flag);
+
+ return;
+}
+
+/* end of sah_interrupt_handler.c */
+#endif /* ifndef SAHARA_POLL_MODE */
diff --git a/drivers/mxc/security/sahara2/sah_memory_mapper.c b/drivers/mxc/security/sahara2/sah_memory_mapper.c
new file mode 100644
index 000000000000..d239a34368f0
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_memory_mapper.c
@@ -0,0 +1,2349 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+* @file sah_memory_mapper.c
+*
+* @brief Re-creates SAHARA data structures in Kernel memory such that they are
+* suitable for DMA. Provides support for kernel API.
+*
+* This file needs to be ported.
+*
+* The memory mapper gets a call at #sah_Init_Mem_Map() during driver
+* initialization.
+*
+* The routine #sah_Copy_Descriptors() is used to bring descriptor chains from
+* user memory down to kernel memory, relink using physical addresses, and make
+* sure that all user data will be accessible by the Sahara DMA.
+* #sah_Destroy_Descriptors() does the inverse.
+*
+* The #sah_Alloc_Block(), #sah_Free_Block(), and #sah_Block_Add_Page() routines
+* implement a cache of free blocks used when allocating descriptors and links
+* within the kernel.
+*
+* The memory mapper gets a call at #sah_Stop_Mem_Map() during driver shutdown.
+*
+*/
+
+#include <sah_driver_common.h>
+#include <sah_kernel.h>
+#include <sah_queue_manager.h>
+#include <sah_memory_mapper.h>
+#ifdef FSL_HAVE_SCC2
+#include <linux/mxc_scc2_driver.h>
+#else
+#include <linux/mxc_scc_driver.h>
+#endif
+
+#if defined(DIAG_DRV_IF) || defined(DIAG_MEM) || defined(DO_DBG)
+#include <diagnostic.h>
+#include <sah_hardware_interface.h>
+#endif
+
+#include <linux/mm.h> /* get_user_pages() */
+#include <linux/pagemap.h>
+#include <linux/dmapool.h>
+
+#include <linux/slab.h>
+#include <linux/highmem.h>
+
+#if defined(DIAG_MEM) || defined(DIAG_DRV_IF)
+#define DIAG_MSG_SIZE 1024
+static char Diag_msg[DIAG_MSG_SIZE];
+#endif
+
+#ifdef LINUX_VERSION_CODE
+#define FLUSH_SPECIFIC_DATA_ONLY
+#else
+#define SELF_MANAGED_POOL
+#endif
+
+#if defined(LINUX_VERSION_CODE)
+EXPORT_SYMBOL(sah_Alloc_Link);
+EXPORT_SYMBOL(sah_Free_Link);
+EXPORT_SYMBOL(sah_Alloc_Descriptor);
+EXPORT_SYMBOL(sah_Free_Descriptor);
+EXPORT_SYMBOL(sah_Alloc_Head_Descriptor);
+EXPORT_SYMBOL(sah_Free_Head_Descriptor);
+EXPORT_SYMBOL(sah_Physicalise_Descriptors);
+EXPORT_SYMBOL(sah_DePhysicalise_Descriptors);
+#endif
+
+/* Determine if L2 cache support should be built in. */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21))
+#ifdef CONFIG_OUTER_CACHE
+#define HAS_L2_CACHE
+#endif
+#else
+#ifdef CONFIG_CPU_CACHE_L210
+#define HAS_L2_CACHE
+#endif
+#endif
+
+/* Number of bytes the hardware uses out of sah_Link and sah_*Desc structs */
+#define SAH_HW_LINK_LEN 1
+#define SAH_HW_DESC_LEN 24
+
+/* Macros for Descriptors */
+#define SAH_LLO_BIT 0x01000000
+#define sah_Desc_Get_LLO(desc) (desc->header & SAH_LLO_BIT)
+#define sah_Desc_Set_Header(desc, h) (desc->header = (h))
+
+#define sah_Desc_Get_Next(desc) (desc->next)
+#define sah_Desc_Set_Next(desc, n) (desc->next = (n))
+
+#define sah_Desc_Get_Ptr1(desc) (desc->ptr1)
+#define sah_Desc_Get_Ptr2(desc) (desc->ptr2)
+#define sah_Desc_Set_Ptr1(desc,p1) (desc->ptr1 = (p1))
+#define sah_Desc_Set_Ptr2(desc,p2) (desc->ptr2 = (p2))
+
+#define sah_Desc_Get_Len1(desc) (desc->len1)
+#define sah_Desc_Get_Len2(desc) (desc->len2)
+#define sah_Desc_Set_Len1(desc,l1) (desc->len1 = (l1))
+#define sah_Desc_Set_Len2(desc,l2) (desc->len2 = (l2))
+
+/* Macros for Links */
+#define sah_Link_Get_Next(link) (link->next)
+#define sah_Link_Set_Next(link, n) (link->next = (n))
+
+#define sah_Link_Get_Data(link) (link->data)
+#define sah_Link_Set_Data(link,d) (link->data = (d))
+
+#define sah_Link_Get_Len(link) (link->len)
+#define sah_Link_Set_Len(link, l) (link->len = (l))
+
+#define sah_Link_Get_Flags(link) (link->flags)
+
+/* Memory block defines */
+/* Warning! This assumes that kernel version of sah_Link
+ * is larger than kernel version of sah_Desc.
+ */
+#define MEM_BLOCK_SIZE sizeof(sah_Link)
+
+/*! Structure for link/descriptor memory blocks in internal pool */
+typedef struct mem_block {
+ uint8_t data[MEM_BLOCK_SIZE]; /*!< the actual buffer area */
+ struct mem_block *next; /*!< next block when in free chain */
+ dma_addr_t dma_addr; /*!< physical address of @a data */
+} Mem_Block;
+
+#define MEM_BLOCK_ENTRIES (PAGE_SIZE / sizeof(Mem_Block))
+
+#define MEM_BIG_BLOCK_SIZE sizeof(sah_Head_Desc)
+
+/*! Structure for head descriptor memory blocks in internal pool */
+typedef struct mem_big_block {
+ uint8_t data[MEM_BIG_BLOCK_SIZE]; /*!< the actual buffer area */
+ struct mem_big_block *next; /*!< next block when in free chain */
+ uint32_t dma_addr; /*!< physical address of @a data */
+} Mem_Big_Block;
+
+#define MEM_BIG_BLOCK_ENTRIES (PAGE_SIZE / sizeof(Mem_Big_Block))
+
+/* Shared variables */
+
+/*!
+ * Lock to protect the memory chain composed of #block_free_head and
+ * #block_free_tail.
+ */
+static os_lock_t mem_lock;
+
+#ifndef SELF_MANAGED_POOL
+static struct dma_pool *big_dma_pool = NULL;
+static struct dma_pool *small_dma_pool = NULL;
+#endif
+
+#ifdef SELF_MANAGED_POOL
+/*!
+ * Memory block free pool - pointer to first block. Chain is protected by
+ * #mem_lock.
+ */
+static Mem_Block *block_free_head = NULL;
+/*!
+ * Memory block free pool - pointer to last block. Chain is protected by
+ * #mem_lock.
+ */
+static Mem_Block *block_free_tail = NULL;
+/*!
+ * Memory block free pool - pointer to first block. Chain is protected by
+ * #mem_lock.
+ */
+static Mem_Big_Block *big_block_free_head = NULL;
+/*!
+ * Memory block free pool - pointer to last block. Chain is protected by
+ * #mem_lock.
+a */
+static Mem_Big_Block *big_block_free_tail = NULL;
+#endif /* SELF_MANAGED_POOL */
+
+static Mem_Block *sah_Alloc_Block(void);
+static void sah_Free_Block(Mem_Block * block);
+static Mem_Big_Block *sah_Alloc_Big_Block(void);
+static void sah_Free_Big_Block(Mem_Big_Block * block);
+#ifdef SELF_MANAGED_POOL
+static void sah_Append_Block(Mem_Block * block);
+static void sah_Append_Big_Block(Mem_Big_Block * block);
+#endif /* SELF_MANAGED_POOL */
+
+/* Page context structure. Used by wire_user_memory and unwire_user_memory */
+typedef struct page_ctx_t {
+ uint32_t count;
+ struct page **local_pages;
+} page_ctx_t;
+
+/*!
+*******************************************************************************
+* Map and wire down a region of user memory.
+*
+*
+* @param address Userspace address of the memory to wire
+* @param length Length of the memory region to wire
+* @param page_ctx Page context, to be passed to unwire_user_memory
+*
+* @return (if successful) Kernel virtual address of the wired pages
+*/
+void *wire_user_memory(void *address, uint32_t length, void **page_ctx)
+{
+ void *kernel_black_addr = NULL;
+ int result = -1;
+ int page_index = 0;
+ page_ctx_t *page_context;
+ int nr_pages = 0;
+ unsigned long start_page;
+ fsl_shw_return_t status;
+
+ /* Determine the number of pages being used for this link */
+ nr_pages = (((unsigned long)(address) & ~PAGE_MASK)
+ + length + ~PAGE_MASK) >> PAGE_SHIFT;
+
+ start_page = (unsigned long)(address) & PAGE_MASK;
+
+ /* Allocate some memory to keep track of the wired user pages, so that
+ * they can be deallocated later. The block of memory will contain both
+ * the structure and the array of pages.
+ */
+ page_context = kmalloc(sizeof(page_ctx_t)
+ + nr_pages * sizeof(struct page *), GFP_KERNEL);
+
+ if (page_context == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S; /* no memory! */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("kmalloc() failed.");
+#endif
+ return NULL;
+ }
+
+ /* Set the page pointer to point to the allocated region of memory */
+ page_context->local_pages = (void *)page_context + sizeof(page_ctx_t);
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS("page_context at: %p, local_pages at: %p",
+ (void *)page_context,
+ (void *)(page_context->local_pages));
+#endif
+
+ /* Wire down the pages from user space */
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current, current->mm,
+ start_page, nr_pages, WRITE, 0 /* noforce */ ,
+ (page_context->local_pages), NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (result < nr_pages) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("get_user_pages() failed.");
+#endif
+ if (result > 0) {
+ for (page_index = 0; page_index < result; page_index++) {
+ page_cache_release((page_context->
+ local_pages[page_index]));
+ }
+
+ kfree(page_context);
+ }
+ return NULL;
+ }
+
+ kernel_black_addr = page_address(page_context->local_pages[0]) +
+ ((unsigned long)address & ~PAGE_MASK);
+
+ page_context->count = nr_pages;
+ *page_ctx = page_context;
+
+ return kernel_black_addr;
+}
+
+/*!
+*******************************************************************************
+* Release and unmap a region of user memory.
+*
+* @param page_ctx Page context from wire_user_memory
+*/
+void unwire_user_memory(void **page_ctx)
+{
+ int page_index = 0;
+ struct page_ctx_t *page_context = *page_ctx;
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS("page_context at: %p, first page at:%p, count: %i",
+ (void *)page_context,
+ (void *)(page_context->local_pages),
+ page_context->count);
+#endif
+
+ if ((page_context != NULL) && (page_context->local_pages != NULL)) {
+ for (page_index = 0; page_index < page_context->count;
+ page_index++) {
+ page_cache_release(page_context->
+ local_pages[page_index]);
+ }
+
+ kfree(page_context);
+ *page_ctx = NULL;
+ }
+}
+
+/*!
+*******************************************************************************
+* Map some physical memory into a users memory space
+*
+* @param vma Memory structure to map to
+* @param physical_addr Physical address of the memory to be mapped in
+* @param size Size of the memory to map (bytes)
+*
+* @return
+*/
+os_error_code
+map_user_memory(struct vm_area_struct *vma, uint32_t physical_addr,
+ uint32_t size)
+{
+ os_error_code retval;
+
+ /* Map the acquired partition into the user's memory space */
+ vma->vm_end = vma->vm_start + size;
+
+ /* set cache policy to uncached so that each write of the UMID and
+ * permissions get directly to the SCC2 in order to engage it
+ * properly. Once the permissions have been written, it may be
+ * useful to provide a service for the user to request a different
+ * cache policy
+ */
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* Make sure that the user cannot fork() a child which will inherit
+ * this mapping, as it creates a security hole. Likewise, do not
+ * allow the user to 'expand' his mapping beyond this partition.
+ */
+ vma->vm_flags |= VM_IO | VM_RESERVED | VM_DONTCOPY | VM_DONTEXPAND;
+
+ retval = remap_pfn_range(vma,
+ vma->vm_start,
+ __phys_to_pfn(physical_addr),
+ size, vma->vm_page_prot);
+
+ return retval;
+}
+
+/*!
+*******************************************************************************
+* Remove some memory from a user's memory space
+*
+* @param user_addr Userspace address of the memory to be unmapped
+* @param size Size of the memory to map (bytes)
+*
+* @return
+*/
+os_error_code unmap_user_memory(uint32_t user_addr, uint32_t size)
+{
+ os_error_code retval;
+ struct mm_struct *mm = current->mm;
+
+ /* Unmap the memory region (see sys_munmap in mmap.c) */
+ down_write(&mm->mmap_sem);
+ retval = do_munmap(mm, (unsigned long)user_addr, size);
+ up_write(&mm->mmap_sem);
+
+ return retval;
+}
+
+/*!
+*******************************************************************************
+* Free descriptor back to free pool
+*
+* @brief Free descriptor
+*
+* @param desc A descriptor allocated with sah_Alloc_Descriptor().
+*
+* @return none
+*
+*/
+void sah_Free_Descriptor(sah_Desc * desc)
+{
+ memset(desc, 0x45, sizeof(*desc));
+ sah_Free_Block((Mem_Block *) desc);
+}
+
+/*!
+*******************************************************************************
+* Free Head descriptor back to free pool
+*
+* @brief Free Head descriptor
+*
+* @param desc A Head descriptor allocated with sah_Alloc_Head_Descriptor().
+*
+* @return none
+*
+*/
+void sah_Free_Head_Descriptor(sah_Head_Desc * desc)
+{
+ memset(desc, 0x43, sizeof(*desc));
+ sah_Free_Big_Block((Mem_Big_Block *) desc);
+}
+
+/*!
+*******************************************************************************
+* Free link back to free pool
+*
+* @brief Free link
+*
+* @param link A link allocated with sah_Alloc_Link().
+*
+* @return none
+*
+*/
+void sah_Free_Link(sah_Link * link)
+{
+ memset(link, 0x41, sizeof(*link));
+ sah_Free_Block((Mem_Block *) link);
+}
+
+/*!
+*******************************************************************************
+* This function runs through a descriptor chain pointed to by a user-space
+* address. It duplicates each descriptor in Kernel space memory and calls
+* sah_Copy_Links() to handle any links attached to the descriptors. This
+* function cleans-up everything that it created in the case of a failure.
+*
+* @brief Kernel Descriptor Chain Copier
+*
+* @param fsl_shw_uco_t The user context to act under
+* @param user_head_desc A Head Descriptor pointer from user-space.
+*
+* @return sah_Head_Desc * - A virtual address of the first descriptor in the
+* chain.
+* @return NULL - If there was some error.
+*
+*/
+sah_Head_Desc *sah_Copy_Descriptors(fsl_shw_uco_t * user_ctx,
+ sah_Head_Desc * user_head_desc)
+{
+ sah_Desc *curr_desc = NULL;
+ sah_Desc *prev_desc = NULL;
+ sah_Desc *next_desc = NULL;
+ sah_Head_Desc *head_desc = NULL;
+ sah_Desc *user_desc = NULL;
+ unsigned long result;
+
+ /* Internal status variable to be used in this function */
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Head_Desc *ret_val = NULL;
+
+ /* This will be set to True when we have finished processing our
+ * descriptor chain.
+ */
+ int drv_if_done = FALSE;
+ int is_this_the_head = TRUE;
+
+ do {
+ /* Allocate memory for this descriptor */
+ if (is_this_the_head) {
+ head_desc =
+ (sah_Head_Desc *) sah_Alloc_Head_Descriptor();
+
+#ifdef DIAG_MEM
+ sprintf(Diag_msg,
+ "Alloc_Head_Descriptor returned %p\n",
+ head_desc);
+ LOG_KDIAG(Diag_msg);
+#endif
+ if (head_desc == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("sah_Alloc_Head_Descriptor() failed.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ void *virt_addr = head_desc->desc.virt_addr;
+ dma_addr_t dma_addr = head_desc->desc.dma_addr;
+
+ /* Copy the head descriptor from user-space */
+ /* Instead of copying the whole structure,
+ * unneeded bits at the end are left off.
+ * The user space version is missing virt/dma addrs, which
+ * means that the copy will be off for flags... */
+ result = copy_from_user(head_desc,
+ user_head_desc,
+ (sizeof(*head_desc) -
+ sizeof(head_desc->desc.
+ dma_addr) -
+ sizeof(head_desc->desc.
+ virt_addr) -
+ sizeof(head_desc->desc.
+ original_ptr1) -
+/* sizeof(head_desc->desc.original_ptr2) -
+ sizeof(head_desc->status) -
+ sizeof(head_desc->error_status) -
+ sizeof(head_desc->fault_address) -
+ sizeof(head_desc->current_dar) -
+ sizeof(head_desc->result) -
+ sizeof(head_desc->next) -
+ sizeof(head_desc->prev) -
+ sizeof(head_desc->user_desc) -
+*/ sizeof(head_desc->out1_ptr) -
+ sizeof(head_desc->
+ out2_ptr) -
+ sizeof(head_desc->
+ out_len)));
+ /* there really isn't a 'next' descriptor at this point, so
+ * set that pointer to NULL, but remember it for if/when there
+ * is a next */
+ next_desc = head_desc->desc.next;
+ head_desc->desc.next = NULL;
+
+ if (result != 0) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("copy_from_user() failed.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ /* when destroying the descriptor, skip these links.
+ * They've not been copied down, so don't exist */
+ head_desc->desc.ptr1 = NULL;
+ head_desc->desc.ptr2 = NULL;
+
+ } else {
+ /* The kernel DESC has five more words than user DESC, so
+ * the missing values are in the middle of the HEAD DESC,
+ * causing values after the missing ones to be at different
+ * offsets in kernel and user space.
+ *
+ * Patch up the problem by moving field two spots.
+ * This assumes sizeof(pointer) == sizeof(uint32_t).
+ * Note that 'user_info' is not needed, so not copied.
+ */
+ head_desc->user_ref =
+ (uint32_t) head_desc->desc.dma_addr;
+ head_desc->uco_flags =
+ (uint32_t) head_desc->desc.
+ original_ptr1;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS(
+ "User flags: %x; User Reference: %x",
+ head_desc->uco_flags,
+ head_desc->user_ref);
+#endif
+ /* These values were destroyed by the copy. */
+ head_desc->desc.virt_addr = virt_addr;
+ head_desc->desc.dma_addr = dma_addr;
+
+ /* ensure that the save descriptor chain bit is not set.
+ * the copy of the user space descriptor chain should
+ * always be deleted */
+ head_desc->uco_flags &=
+ ~FSL_UCO_SAVE_DESC_CHAIN;
+
+ curr_desc = (sah_Desc *) head_desc;
+ is_this_the_head = FALSE;
+ }
+ }
+ } else { /* not head */
+ curr_desc = sah_Alloc_Descriptor();
+#ifdef DIAG_MEM
+ LOG_KDIAG_ARGS("Alloc_Descriptor returned %p\n",
+ curr_desc);
+#endif
+ if (curr_desc == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Alloc_Descriptor() failed.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ /* need to update the previous descriptors' next field to
+ * pointer to the current descriptor. */
+ prev_desc->original_next = curr_desc;
+ prev_desc->next =
+ (sah_Desc *) curr_desc->dma_addr;
+
+ /* Copy the current descriptor from user-space */
+ /* The virtual address and DMA address part of the sah_Desc
+ * struct are not copied to user space */
+ result = copy_from_user(curr_desc, user_desc, (sizeof(sah_Desc) - sizeof(dma_addr_t) - /* dma_addr */
+ sizeof(uint32_t) - /* virt_addr */
+ sizeof(void *) - /* original_ptr1 */
+ sizeof(void *) - /* original_ptr2 */
+ sizeof(sah_Desc **))); /* original_next */
+ /* there really isn't a 'next' descriptor at this point, so
+ * set that pointer to NULL, but remember it for if/when there
+ * is a next */
+ next_desc = curr_desc->next;
+ curr_desc->next = NULL;
+ curr_desc->original_next = NULL;
+
+ if (result != 0) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("copy_from_user() failed.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ /* when destroying the descriptor chain, skip these links.
+ * They've not been copied down, so don't exist */
+ curr_desc->ptr1 = NULL;
+ curr_desc->ptr2 = NULL;
+ }
+ }
+ } /* end if (is_this_the_head) */
+
+ if (status == FSL_RETURN_OK_S) {
+ if (!(curr_desc->header & SAH_LLO_BIT)) {
+ /* One or both pointer fields being NULL is a valid
+ * configuration. */
+ if (curr_desc->ptr1 == NULL) {
+ curr_desc->original_ptr1 = NULL;
+ } else {
+ /* pointer fields point to sah_Link structures */
+ curr_desc->original_ptr1 =
+ sah_Copy_Links(user_ctx, curr_desc->ptr1);
+ if (curr_desc->original_ptr1 == NULL) {
+ /* This descriptor and any links created successfully
+ * are cleaned-up at the bottom of this function. */
+ drv_if_done = TRUE;
+ status =
+ FSL_RETURN_INTERNAL_ERROR_S;
+ /* mark that link 2 doesn't exist */
+ curr_desc->ptr2 = NULL;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("sah_Copy_Links() failed.");
+#endif
+ } else {
+ curr_desc->ptr1 = (void *)
+ ((sah_Link *) curr_desc->
+ original_ptr1)->dma_addr;
+ }
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ if (curr_desc->ptr2 == NULL) {
+ curr_desc->original_ptr2 = NULL;
+ } else {
+ /* pointer fields point to sah_Link structures */
+ curr_desc->original_ptr2 =
+ sah_Copy_Links(user_ctx, curr_desc->ptr2);
+ if (curr_desc->original_ptr2 ==
+ NULL) {
+ /* This descriptor and any links created
+ * successfully are cleaned-up at the bottom of
+ * this function. */
+ drv_if_done = TRUE;
+ status =
+ FSL_RETURN_INTERNAL_ERROR_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("sah_Copy_Links() failed.");
+#endif
+ } else {
+ curr_desc->ptr2 =
+ (void
+ *)(((sah_Link *)
+ curr_desc->
+ original_ptr2)
+ ->dma_addr);
+ }
+ }
+ }
+ } else {
+ /* Pointer fields point directly to user buffers. We don't
+ * support this mode.
+ */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG
+ ("The LLO bit in the Descriptor Header field was "
+ "set. This an invalid configuration.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ }
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ user_desc = next_desc;
+ prev_desc = curr_desc;
+ if (user_desc == NULL) {
+ /* We have reached the end our our descriptor chain */
+ drv_if_done = TRUE;
+ }
+ }
+
+ } while (drv_if_done == FALSE);
+
+ if (status != FSL_RETURN_OK_S) {
+ /* Clean-up if failed */
+ if (head_desc != NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("Error! Calling destroy descriptors!\n");
+#endif
+ sah_Destroy_Descriptors(head_desc);
+ }
+ ret_val = NULL;
+ } else {
+ /* Flush the caches */
+#ifndef FLUSH_SPECIFIC_DATA_ONLY
+ os_flush_cache_all();
+#endif
+
+ /* Success. Return the DMA'able head descriptor. */
+ ret_val = head_desc;
+
+ }
+
+ return ret_val;
+} /* sah_Copy_Descriptors() */
+
+/*!
+*******************************************************************************
+* This function runs through a sah_Link chain pointed to by a kernel-space
+* address. It computes the physical address for each pointer, and converts
+* the chain to use these physical addresses.
+*
+******
+* This function needs to return some indication that the chain could not be
+* converted. It also needs to back out any conversion already taken place on
+* this chain of links.
+*
+* Then, of course, sah_Physicalise_Descriptors() will need to recognize that
+* an error occured, and then be able to back out any physicalization of the
+* chain which had taken place up to that point!
+******
+*
+* @brief Convert kernel Link chain
+*
+* @param first_link A sah_Link pointer from kernel space; must not be
+* NULL, so error case can be distinguished.
+*
+* @return sah_Link * A dma'able address of the first descriptor in the
+* chain.
+* @return NULL If Link chain could not be physicalised, i.e. ERROR
+*
+*/
+sah_Link *sah_Physicalise_Links(sah_Link * first_link)
+{
+ sah_Link *link = first_link;
+
+ while (link != NULL) {
+#ifdef DO_DBG
+ sah_Dump_Words("Link", (unsigned *)link, link->dma_addr, 3);
+#endif
+ link->vm_info = NULL;
+
+ /* need to retrieve stored key? */
+ if (link->flags & SAH_STORED_KEY_INFO) {
+ uint32_t max_len = 0; /* max slot length */
+ fsl_shw_return_t ret_status;
+
+ /* get length and physical address of stored key */
+ ret_status = system_keystore_get_slot_info(link->ownerid, link->slot, (uint32_t *) & link->data, /* RED key address */
+ &max_len);
+ if ((ret_status != FSL_RETURN_OK_S) || (link->len > max_len)) {
+ /* trying to illegally/incorrectly access a key. Cause the
+ * error status register to show a Link Length Error by
+ * putting a zero in the links length. */
+ link->len = 0; /* Cause error. Somebody is up to no good. */
+ }
+ } else if (link->flags & SAH_IN_USER_KEYSTORE) {
+
+#ifdef FSL_HAVE_SCC2
+ /* The data field points to the virtual address of the key. Convert
+ * this to a physical address by modifying the address based
+ * on where the secure memory was mapped to the kernel. Note: In
+ * kernel mode, no attempt is made to track or control who owns what
+ * memory partition.
+ */
+ link->data = (uint8_t *) scc_virt_to_phys(link->data);
+
+ /* Do bounds checking to ensure that the user is not overstepping
+ * the bounds of their partition. This is a simple implementation
+ * that assumes the user only owns one partition. It only checks
+ * to see if the address of the last byte of data steps over a
+ * page boundary.
+ */
+
+#ifdef DO_DBG
+ LOG_KDIAG_ARGS("start page: %08x, end page: %08x"
+ "first addr: %p, last addr: %p, len; %i",
+ ((uint32_t) (link->data) >> PAGE_SHIFT),
+ (((uint32_t) link->data +
+ link->len) >> PAGE_SHIFT), link->data,
+ link->data + link->len, link->len);
+#endif
+
+ if ((((uint32_t) link->data +
+ link->len) >> PAGE_SHIFT) !=
+ ((uint32_t) link->data >> PAGE_SHIFT)) {
+ link->len = 0; /* Cause error. Somebody is up to no good. */
+ }
+#else /* FSL_HAVE_SCC2 */
+
+ /* User keystores are not valid on non-SCC2 platforms */
+ link->len = 0; /* Cause error. Somebody is up to no good. */
+
+#endif /* FSL_HAVE_SCC2 */
+
+ } else {
+ if (!(link->flags & SAH_PREPHYS_DATA)) {
+ link->original_data = link->data;
+
+ /* All pointers are virtual right now */
+ link->data = (void *)os_pa(link->data);
+#ifdef DO_DBG
+ os_printk("%sput: %p (%d)\n",
+ (link->
+ flags & SAH_OUTPUT_LINK) ? "out" :
+ "in", link->data, link->len);
+#endif
+
+ if (link->flags & SAH_OUTPUT_LINK) {
+ /* clean and invalidate */
+ os_cache_flush_range(link->
+ original_data,
+ link->len);
+ } else {
+ os_cache_clean_range(link->original_data,
+ link->len);
+ }
+ } /* not prephys */
+ } /* else not key reference */
+
+#if defined(NO_OUTPUT_1K_CROSSING) || defined(NO_1K_CROSSING)
+ if (
+#ifdef NO_OUTPUT_1K_CROSSING
+ /* Insert extra link if 1k boundary on output pointer
+ * crossed not at an 8-word boundary */
+ (link->flags & SAH_OUTPUT_LINK) &&
+ (((uint32_t) link->data % 32) != 0) &&
+#endif
+ ((((uint32_t) link->data & 1023) + link->len) >
+ 1024)) {
+ uint32_t full_length = link->len;
+ sah_Link *new_link = sah_Alloc_Link();
+ link->len = 1024 - ((uint32_t) link->data % 1024);
+ new_link->len = full_length - link->len;
+ new_link->data = link->data + link->len;
+ new_link->original_data =
+ link->original_data + link->len;
+ new_link->flags = link->flags & ~(SAH_OWNS_LINK_DATA);
+ new_link->flags |= SAH_LINK_INSERTED_LINK;
+ new_link->next = link->next;
+
+ link->next = (sah_Link *) new_link->dma_addr;
+ link->original_next = new_link;
+ link = new_link;
+ }
+#endif /* ALLOW_OUTPUT_1K_CROSSING */
+
+ link->original_next = link->next;
+ if (link->next != NULL) {
+ link->next = (sah_Link *) link->next->dma_addr;
+ }
+#ifdef DO_DBG
+ sah_Dump_Words("Link", link, link->dma_addr, 3);
+#endif
+
+ link = link->original_next;
+ }
+
+ return (sah_Link *) first_link->dma_addr;
+} /* sah_Physicalise_Links */
+
+/*!
+ * Run through descriptors and links created by KM-API and set the
+ * dma addresses and 'do not free' flags.
+ *
+ * @param first_desc KERNEL VIRTUAL address of first descriptor in chain.
+ *
+ * Warning! This ONLY works without LLO flags in headers!!!
+ *
+ * @return Virtual address of @a first_desc.
+ * @return NULL if Descriptor Chain could not be physicalised
+ */
+sah_Head_Desc *sah_Physicalise_Descriptors(sah_Head_Desc * first_desc)
+{
+ sah_Desc *desc = &first_desc->desc;
+
+ if (!(first_desc->uco_flags & FSL_UCO_CHAIN_PREPHYSICALIZED)) {
+ while (desc != NULL) {
+ sah_Desc *next_desc;
+
+#ifdef DO_DBG
+
+ sah_Dump_Words("Desc", (unsigned *)desc, desc->dma_addr, 6);
+#endif
+
+ desc->original_ptr1 = desc->ptr1;
+ if (desc->ptr1 != NULL) {
+ if ((desc->ptr1 =
+ sah_Physicalise_Links(desc->ptr1)) ==
+ NULL) {
+ /* Clean up ... */
+ sah_DePhysicalise_Descriptors
+ (first_desc);
+ first_desc = NULL;
+ break;
+ }
+ }
+ desc->original_ptr2 = desc->ptr2;
+ if (desc->ptr2 != NULL) {
+ if ((desc->ptr2 =
+ sah_Physicalise_Links(desc->ptr2)) ==
+ NULL) {
+ /* Clean up ... */
+ sah_DePhysicalise_Descriptors
+ (first_desc);
+ first_desc = NULL;
+ break;
+ }
+ }
+
+ desc->original_next = desc->next;
+ next_desc = desc->next; /* save for bottom of while loop */
+ if (desc->next != NULL) {
+ desc->next = (sah_Desc *) desc->next->dma_addr;
+ }
+
+ desc = next_desc;
+ }
+ }
+ /* not prephysicalized */
+#ifdef DO_DBG
+ os_printk("Physicalise finished\n");
+#endif
+
+ return first_desc;
+} /* sah_Physicalise_Descriptors() */
+
+/*!
+*******************************************************************************
+* This function runs through a sah_Link chain pointed to by a physical address.
+* It computes the virtual address for each pointer
+*
+* @brief Convert physical Link chain
+*
+* @param first_link A kernel address of a sah_Link
+*
+* @return sah_Link * A kernal address for the link chain of @c first_link
+* @return NULL If there was some error.
+*
+* @post All links will be chained together by original virtual addresses,
+* data pointers will point to virtual addresses. Appropriate cache
+* lines will be flushed, memory unwired, etc.
+*/
+sah_Link *sah_DePhysicalise_Links(sah_Link * first_link)
+{
+ sah_Link *link = first_link;
+ sah_Link *prev_link = NULL;
+
+ /* Loop on virtual link pointer */
+ while (link != NULL) {
+
+#ifdef DO_DBG
+ sah_Dump_Words("Link", (unsigned *)link, link->dma_addr, 3);
+#endif
+
+ /* if this references stored keys, don't want to dephysicalize them */
+ if (!(link->flags & SAH_STORED_KEY_INFO)
+ && !(link->flags & SAH_PREPHYS_DATA)
+ && !(link->flags & SAH_IN_USER_KEYSTORE)) {
+
+ /* */
+ if (link->flags & SAH_OUTPUT_LINK) {
+ os_cache_inv_range(link->original_data,
+ link->len);
+ }
+
+ /* determine if there is a page in user space associated with this
+ * link */
+ if (link->vm_info != NULL) {
+ /* check that this isn't reserved and contains output */
+ if (!PageReserved(link->vm_info) &&
+ (link->flags & SAH_OUTPUT_LINK)) {
+
+ /* Mark to force page eventually to backing store */
+ SetPageDirty(link->vm_info);
+ }
+
+ /* Untie this page from physical memory */
+ page_cache_release(link->vm_info);
+ } else {
+ /* kernel-mode data */
+#ifdef DO_DBG
+ os_printk("%sput: %p (%d)\n",
+ (link->
+ flags & SAH_OUTPUT_LINK) ? "out" :
+ "in", link->original_data, link->len);
+#endif
+ }
+ link->data = link->original_data;
+ }
+#ifndef ALLOW_OUTPUT_1K_CROSSING
+ if (link->flags & SAH_LINK_INSERTED_LINK) {
+ /* Reconsolidate data by merging this link with previous */
+ prev_link->len += link->len;
+ prev_link->next = link->next;
+ prev_link->original_next = link->original_next;
+ sah_Free_Link(link);
+ link = prev_link;
+
+ }
+#endif
+
+ if (link->next != NULL) {
+ link->next = link->original_next;
+ }
+ prev_link = link;
+ link = link->next;
+ }
+
+ return first_link;
+} /* sah_DePhysicalise_Links() */
+
+/*!
+ * Run through descriptors and links that have been Physicalised
+ * (sah_Physicalise_Descriptors function) and set the dma addresses back
+ * to KM virtual addresses
+ *
+ * @param first_desc Kernel virtual address of first descriptor in chain.
+ *
+ * Warning! This ONLY works without LLO flags in headers!!!
+ */
+sah_Head_Desc *sah_DePhysicalise_Descriptors(sah_Head_Desc * first_desc)
+{
+ sah_Desc *desc = &first_desc->desc;
+
+ if (!(first_desc->uco_flags & FSL_UCO_CHAIN_PREPHYSICALIZED)) {
+ while (desc != NULL) {
+#ifdef DO_DBG
+ sah_Dump_Words("Desc", (unsigned *)desc, desc->dma_addr, 6);
+#endif
+
+ if (desc->ptr1 != NULL) {
+ desc->ptr1 =
+ sah_DePhysicalise_Links(desc->
+ original_ptr1);
+ }
+ if (desc->ptr2 != NULL) {
+ desc->ptr2 =
+ sah_DePhysicalise_Links(desc->
+ original_ptr2);
+ }
+ if (desc->next != NULL) {
+ desc->next = desc->original_next;
+ }
+ desc = desc->next;
+ }
+ }
+ /* not prephysicalized */
+ return first_desc;
+} /* sah_DePhysicalise_Descriptors() */
+
+/*!
+*******************************************************************************
+* This walks through a SAHARA descriptor chain and free()'s everything
+* that is not NULL. Finally it also unmaps all of the physical memory and
+* frees the kiobuf_list Queue.
+*
+* @brief Kernel Descriptor Chain Destructor
+*
+* @param head_desc A Descriptor pointer from kernel-space.
+*
+* @return void
+*
+*/
+void sah_Free_Chained_Descriptors(sah_Head_Desc * head_desc)
+{
+ sah_Desc *desc = NULL;
+ sah_Desc *next_desc = NULL;
+ int this_is_head = 1;
+
+ desc = &head_desc->desc;
+
+ while (desc != NULL) {
+
+ sah_Free_Chained_Links(desc->ptr1);
+ sah_Free_Chained_Links(desc->ptr2);
+
+ /* Get a bus pointer to the next Descriptor */
+ next_desc = desc->next;
+
+ /* Zero the header and Length fields for security reasons. */
+ desc->header = 0;
+ desc->len1 = 0;
+ desc->len2 = 0;
+
+ if (this_is_head) {
+ sah_Free_Head_Descriptor(head_desc);
+ this_is_head = 0;
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Head_Descriptor: %p\n",
+ head_desc);
+ LOG_KDIAG(Diag_msg);
+#endif
+ } else {
+ /* free this descriptor */
+ sah_Free_Descriptor(desc);
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Descriptor: %p\n", desc);
+ LOG_KDIAG(Diag_msg);
+#endif
+ }
+
+ /* Look at the next Descriptor */
+ desc = next_desc;
+ }
+} /* sah_Free_Chained_Descriptors() */
+
+/*!
+*******************************************************************************
+* This walks through a SAHARA link chain and frees everything that is
+* not NULL, excluding user-space buffers.
+*
+* @brief Kernel Link Chain Destructor
+*
+* @param link A Link pointer from kernel-space. This is in bus address
+* space.
+*
+* @return void
+*
+*/
+void sah_Free_Chained_Links(sah_Link * link)
+{
+ sah_Link *next_link = NULL;
+
+ while (link != NULL) {
+ /* Get a bus pointer to the next Link */
+ next_link = link->next;
+
+ /* Zero some fields for security reasons. */
+ link->data = NULL;
+ link->len = 0;
+ link->ownerid = 0;
+
+ /* Free this Link */
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Link: %p(->%p)\n", link, link->next);
+ LOG_KDIAG(Diag_msg);
+#endif
+ sah_Free_Link(link);
+
+ /* Move on to the next Link */
+ link = next_link;
+ }
+}
+
+/*!
+*******************************************************************************
+* This function runs through a link chain pointed to by a user-space
+* address. It makes a temporary kernel-space copy of each link in the
+* chain and calls sah_Make_Links() to create a set of kernel-side links
+* to replace it.
+*
+* @brief Kernel Link Chain Copier
+*
+* @param ptr A link pointer from user-space.
+*
+* @return sah_Link * - The virtual address of the first link in the
+* chain.
+* @return NULL - If there was some error.
+*/
+sah_Link *sah_Copy_Links(fsl_shw_uco_t * user_ctx, sah_Link * ptr)
+{
+ sah_Link *head_link = NULL;
+ sah_Link *new_head_link = NULL;
+ sah_Link *new_tail_link = NULL;
+ sah_Link *prev_tail_link = NULL;
+ sah_Link *user_link = ptr;
+ sah_Link link_copy;
+ int link_data_length = 0;
+
+ /* Internal status variable to be used in this function */
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *ret_val = NULL;
+
+ /* This will be set to True when we have finished processing our
+ * link chain. */
+ int drv_if_done = FALSE;
+ int is_this_the_head = TRUE;
+ int result;
+
+ /* transfer all links, on this link chain, from user space */
+ while (drv_if_done == FALSE) {
+ /* Copy the current link from user-space. The virtual address, DMA
+ * address, and vm_info fields of the sah_Link struct are not part
+ * of the user-space structure. They must be the last elements and
+ * should not be copied. */
+ result = copy_from_user(&link_copy,
+ user_link, (sizeof(sah_Link) -
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ sizeof(struct page *) - /* vm_info */
+#endif
+ sizeof(dma_addr_t) - /* dma_addr */
+ sizeof(uint32_t) - /* virt_addr */
+ sizeof(uint8_t *) - /* original_data */
+ sizeof(sah_Link *))); /* original_next */
+
+ if (result != 0) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("copy_from_user() failed.");
+#endif
+ drv_if_done = TRUE;
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ /* This will create new links which can be used to replace tmp_link
+ * in the chain. This will return a new head and tail link. */
+ link_data_length = link_data_length + link_copy.len;
+ new_head_link =
+ sah_Make_Links(user_ctx, &link_copy, &new_tail_link);
+
+ if (new_head_link == NULL) {
+ /* If we ran out of memory or a user pointer was invalid */
+ drv_if_done = TRUE;
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Make_Links() failed.");
+#endif
+ } else {
+ if (is_this_the_head == TRUE) {
+ /* Keep a reference to the head link */
+ head_link = new_head_link;
+ is_this_the_head = FALSE;
+ } else {
+ /* Need to update the previous links' next field to point
+ * to the current link. */
+ prev_tail_link->next =
+ (void *)new_head_link->dma_addr;
+ prev_tail_link->original_next =
+ new_head_link;
+ }
+ }
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ /* Get to the next link in the chain. */
+ user_link = link_copy.next;
+ prev_tail_link = new_tail_link;
+
+ /* Check if the end of the link chain was reached (TRUE) or if
+ * there is another linked to this one (FALSE) */
+ drv_if_done = (user_link == NULL) ? TRUE : FALSE;
+ }
+ } /* end while */
+
+ if (status != FSL_RETURN_OK_S) {
+ ret_val = NULL;
+ /* There could be clean-up to do here because we may have made some
+ * successful iterations through the while loop and as a result, the
+ * links created by sah_Make_Links() need to be destroyed.
+ */
+ if (head_link != NULL) {
+ /* Failed somewhere in the while loop and need to clean-up. */
+ sah_Destroy_Links(head_link);
+ }
+ } else {
+ /* Success. Return the head link. */
+ ret_val = head_link;
+ }
+
+ return ret_val;
+} /* sah_Copy_Links() */
+
+/*!
+*******************************************************************************
+* This function takes an input link pointed to by a user-space address
+* and returns a chain of links that span the physical pages pointed
+* to by the input link.
+*
+* @brief Kernel Link Chain Constructor
+*
+* @param ptr A link pointer from user-space.
+* @param tail The address of a link pointer. This is used to return
+* the tail link created by this function.
+*
+* @return sah_Link * - A virtual address of the first link in the
+* chain.
+* @return NULL - If there was some error.
+*
+*/
+sah_Link *sah_Make_Links(fsl_shw_uco_t * user_ctx,
+ sah_Link * ptr, sah_Link ** tail)
+{
+ int result = -1;
+ int page_index = 0;
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ int is_this_the_head = TRUE;
+ void *buffer_start = NULL;
+ sah_Link *link = NULL;
+ sah_Link *prev_link = NULL;
+ sah_Link *head_link = NULL;
+ sah_Link *ret_val = NULL;
+ int buffer_length = 0;
+ struct page **local_pages = NULL;
+ int nr_pages = 0;
+ int write = (sah_Link_Get_Flags(ptr) & SAH_OUTPUT_LINK) ? WRITE : READ;
+
+ /* need to retrieve stored key? */
+ if (ptr->flags & SAH_STORED_KEY_INFO) {
+ fsl_shw_return_t ret_status;
+
+ /* allocate space for this link */
+ link = sah_Alloc_Link();
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Alloc_Link returned %p/%p\n", link,
+ (void *)link->dma_addr);
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ if (link == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Alloc_Link() failed!");
+#endif
+ return link;
+ } else {
+ uint32_t max_len = 0; /* max slot length */
+
+ /* get length and physical address of stored key */
+ ret_status = system_keystore_get_slot_info(ptr->ownerid, ptr->slot, (uint32_t *) & link->data, /* RED key address */
+ &max_len);
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG_ARGS
+ ("ret_status==SCC_RET_OK? %s. slot: %i. data: %p"
+ ". len: %i, key length: %i",
+ (ret_status == FSL_RETURN_OK_S ? "yes" : "no"),
+ ptr->slot, link->data, max_len, ptr->len);
+#endif
+
+ if ((ret_status == FSL_RETURN_OK_S) && (ptr->len <= max_len)) {
+ /* finish populating the link */
+ link->len = ptr->len;
+ link->flags = ptr->flags & ~SAH_PREPHYS_DATA;
+ *tail = link;
+ } else {
+#ifdef DIAG_DRV_IF
+ if (ret_status == FSL_RETURN_OK_S) {
+ LOG_KDIAG
+ ("SCC sah_Link key slot reference is too long");
+ } else {
+ LOG_KDIAG
+ ("SCC sah_Link slot slot reference is invalid");
+ }
+#endif
+ sah_Free_Link(link);
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ return NULL;
+ }
+ return link;
+ }
+ } else if (ptr->flags & SAH_IN_USER_KEYSTORE) {
+
+#ifdef FSL_HAVE_SCC2
+
+ void *kernel_base;
+
+ /* allocate space for this link */
+ link = sah_Alloc_Link();
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Alloc_Link returned %p/%p\n", link,
+ (void *)link->dma_addr);
+ LOG_KDIAG(Diag_msg);
+#endif /* DIAG_MEM */
+
+ if (link == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Alloc_Link() failed!");
+#endif
+ return link;
+ } else {
+ /* link->data points to the virtual address of the key data, however
+ * this memory does not need to be locked down.
+ */
+ kernel_base = lookup_user_partition(user_ctx,
+ (uint32_t) ptr->
+ data & PAGE_MASK);
+
+ link->data = (uint8_t *) scc_virt_to_phys(kernel_base +
+ ((unsigned
+ long)ptr->
+ data &
+ ~PAGE_MASK));
+
+ /* Do bounds checking to ensure that the user is not overstepping
+ * the bounds of their partition. This is a simple implementation
+ * that assumes the user only owns one partition. It only checks
+ * to see if the address of the last byte of data steps over a
+ * page boundary.
+ */
+ if ((kernel_base != NULL) &&
+ ((((uint32_t) link->data +
+ link->len) >> PAGE_SHIFT) ==
+ ((uint32_t) link->data >> PAGE_SHIFT))) {
+ /* finish populating the link */
+ link->len = ptr->len;
+ link->flags = ptr->flags & ~SAH_PREPHYS_DATA;
+ *tail = link;
+ } else {
+#ifdef DIAG_DRV_IF
+ if (kernel_base != NULL) {
+ LOG_KDIAG
+ ("SCC sah_Link key slot reference is too long");
+ } else {
+ LOG_KDIAG
+ ("SCC sah_Link slot slot reference is invalid");
+ }
+#endif
+ sah_Free_Link(link);
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ return NULL;
+ }
+ return link;
+ }
+
+#else /* FSL_HAVE_SCC2 */
+
+ return NULL;
+
+#endif /* FSL_HAVE_SCC2 */
+ }
+
+ if (ptr->data == NULL) {
+ /* The user buffer must not be NULL because map_user_kiobuf() cannot
+ * handle NULL pointer input.
+ */
+ status = FSL_RETURN_BAD_DATA_LENGTH_S;
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Link data pointer is NULL.");
+#endif
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ unsigned long start_page = (unsigned long)ptr->data & PAGE_MASK;
+
+ /* determine number of pages being used for this link */
+ nr_pages = (((unsigned long)(ptr->data) & ~PAGE_MASK)
+ + ptr->len + ~PAGE_MASK) >> PAGE_SHIFT;
+
+ /* ptr contains all the 'user space' information, add the pages
+ * to it also just so everything is in one place */
+ local_pages =
+ kmalloc(nr_pages * sizeof(struct page *), GFP_KERNEL);
+
+ if (local_pages == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S; /* no memory! */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("kmalloc() failed.");
+#endif
+ } else {
+ /* get the actual pages being used in 'user space' */
+
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current, current->mm,
+ start_page, nr_pages,
+ write, 0 /* noforce */ ,
+ local_pages, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (result < nr_pages) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("get_user_pages() failed.");
+#endif
+ if (result > 0) {
+ for (page_index = 0;
+ page_index < result;
+ page_index++) {
+ page_cache_release(local_pages
+ [page_index]);
+ }
+ }
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ }
+ }
+ }
+
+ /* Now we can walk through the list of pages in the buffer */
+ if (status == FSL_RETURN_OK_S) {
+
+#if defined(FLUSH_SPECIFIC_DATA_ONLY) && !defined(HAS_L2_CACHE)
+ /*
+ * Now that pages are wired, clear user data from cache lines. When
+ * there is just an L1 cache, clean based on user virtual for ARM.
+ */
+ if (write == WRITE) {
+ os_cache_flush_range(ptr->data, ptr->len);
+ } else {
+ os_cache_clean_range(ptr->data, ptr->len);
+ }
+#endif
+
+ for (page_index = 0; page_index < nr_pages; page_index++) {
+ /* Allocate a new link structure */
+ link = sah_Alloc_Link();
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Alloc_Link returned %p/%p\n", link,
+ (void *)link->dma_addr);
+ LOG_KDIAG(Diag_msg);
+#endif
+ if (link == NULL) {
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("sah_Alloc_Link() failed.");
+#endif
+ status = FSL_RETURN_NO_RESOURCE_S;
+
+ /* need to free the rest of the pages. Destroy_Links will take
+ * care of the ones already assigned to a link */
+ for (; page_index < nr_pages; page_index++) {
+ page_cache_release(local_pages
+ [page_index]);
+ }
+ break; /* exit 'for page_index' loop */
+ }
+
+ if (status == FSL_RETURN_OK_S) {
+ if (is_this_the_head == TRUE) {
+ /* keep a reference to the head link */
+ head_link = link;
+ /* remember that we have seen the head link */
+ is_this_the_head = FALSE;
+ } else {
+ /* If this is not the head link then set the previous
+ * link's next pointer to point to this link */
+ prev_link->original_next = link;
+ prev_link->next =
+ (sah_Link *) link->dma_addr;
+ }
+
+ buffer_start =
+ page_address(local_pages[page_index]);
+
+ if (page_index == 0) {
+ /* If this is the first page, there might be an
+ * offset. We need to increment the address by this offset
+ * so we don't just get the start of the page.
+ */
+ buffer_start +=
+ (unsigned long)
+ sah_Link_Get_Data(ptr)
+ & ~PAGE_MASK;
+ buffer_length = PAGE_SIZE
+ -
+ ((unsigned long)
+ sah_Link_Get_Data(ptr)
+ & ~PAGE_MASK);
+ } else {
+ buffer_length = PAGE_SIZE;
+ }
+
+ if (page_index == nr_pages - 1) {
+ /* if this is the last page, we need to adjust
+ * the buffer_length to account for the last page being
+ * partially used.
+ */
+ buffer_length -=
+ nr_pages * PAGE_SIZE -
+ sah_Link_Get_Len(ptr) -
+ ((unsigned long)
+ sah_Link_Get_Data(ptr) &
+ ~PAGE_MASK);
+ }
+#if defined(FLUSH_SPECIFIC_DATA_ONLY) && defined(HAS_L2_CACHE)
+ /*
+ * When there is an L2 cache, clean based on kernel
+ * virtual..
+ */
+ if (write == WRITE) {
+ os_cache_flush_range(buffer_start,
+ buffer_length);
+ } else {
+ os_cache_clean_range(buffer_start,
+ buffer_length);
+ }
+#endif
+
+ /* Fill in link information */
+ link->len = buffer_length;
+#if !defined(HAS_L2_CACHE)
+ /* use original virtual */
+ link->original_data = ptr->data;
+#else
+ /* use kernel virtual */
+ link->original_data = buffer_start;
+#endif
+ link->data = (void *)os_pa(buffer_start);
+ link->flags = ptr->flags & ~SAH_PREPHYS_DATA;
+ link->vm_info = local_pages[page_index];
+ prev_link = link;
+
+#if defined(NO_OUTPUT_1K_CROSSING) || defined(NO_1K_CROSSING)
+ if (
+#ifdef NO_OUTPUT_1K_CROSSING
+ /* Insert extra link if 1k boundary on output pointer
+ * crossed not at an 8-word boundary */
+ (link->flags & SAH_OUTPUT_LINK) &&
+ (((uint32_t) buffer_start % 32) != 0)
+ &&
+#endif
+ ((((uint32_t) buffer_start & 1023) +
+ buffer_length) > 1024)) {
+
+ /* Shorten current link to 1k boundary */
+ link->len =
+ 1024 -
+ ((uint32_t) buffer_start % 1024);
+
+ /* Get new link to follow it */
+ link = sah_Alloc_Link();
+ prev_link->len =
+ 1024 -
+ ((uint32_t) buffer_start % 1024);
+ prev_link->original_next = link;
+ prev_link->next =
+ (sah_Link *) link->dma_addr;
+ buffer_length -= prev_link->len;
+ buffer_start += prev_link->len;
+
+#if !defined(HAS_L2_CACHE)
+ /* use original virtual */
+ link->original_data = ptr->data;
+#else
+ /* use kernel virtual */
+ link->original_data = buffer_start;
+#endif
+ link->data =
+ (void *)os_pa(buffer_start);
+ link->vm_info = prev_link->vm_info;
+ prev_link->vm_info = NULL; /* delay release */
+ link->flags = ptr->flags;
+ link->len = buffer_length;
+ prev_link = link;
+ } /* while link would cross 1K boundary */
+#endif /* 1K_CROSSING */
+ }
+ } /* for each page */
+ }
+
+ if (local_pages != NULL) {
+ kfree(local_pages);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ /* De-allocated any links created, this routine first looks if
+ * head_link is NULL */
+ sah_Destroy_Links(head_link);
+
+ /* Clean-up of the KIOBUF will occur in the * sah_Copy_Descriptors()
+ * function.
+ * Clean-up of the Queue entry must occur in the function called
+ * sah_Copy_Descriptors().
+ */
+ } else {
+
+ /* Success. Return the head link. */
+ ret_val = head_link;
+ link->original_next = NULL;
+ /* return the tail link as well */
+ *tail = link;
+ }
+
+ return ret_val;
+} /* sah_Make_Links() */
+
+/*!
+*******************************************************************************
+* This walks through a SAHARA descriptor chain and frees everything
+* that is not NULL. Finally it also unmaps all of the physical memory and
+* frees the kiobuf_list Queue.
+*
+* @brief Kernel Descriptor Chain Destructor
+*
+* @param desc A Descriptor pointer from kernel-space. This should be
+* in bus address space.
+*
+* @return void
+*
+*/
+void sah_Destroy_Descriptors(sah_Head_Desc * head_desc)
+{
+ sah_Desc *this_desc = (sah_Desc *) head_desc;
+ sah_Desc *next_desc = NULL;
+ int this_is_head = 1;
+
+ /*
+ * Flush the D-cache. This flush is here because the hardware has finished
+ * processing this descriptor and probably has changed the contents of
+ * some linked user buffers as a result. This flush will enable
+ * user-space applications to see the correct data rather than the
+ * out-of-date cached version.
+ */
+#ifndef FLUSH_SPECIFIC_DATA_ONLY
+ os_flush_cache_all();
+#endif
+
+ head_desc = (sah_Head_Desc *) this_desc->virt_addr;
+
+ while (this_desc != NULL) {
+ if (this_desc->ptr1 != NULL) {
+ sah_Destroy_Links(this_desc->original_ptr1
+ ? this_desc->
+ original_ptr1 : this_desc->ptr1);
+ }
+ if (this_desc->ptr2 != NULL) {
+ sah_Destroy_Links(this_desc->original_ptr2
+ ? this_desc->
+ original_ptr2 : this_desc->ptr2);
+ }
+
+ /* Get a bus pointer to the next Descriptor */
+ next_desc = (this_desc->original_next
+ ? this_desc->original_next : this_desc->next);
+
+ /* Zero the header and Length fields for security reasons. */
+ this_desc->header = 0;
+ this_desc->len1 = 0;
+ this_desc->len2 = 0;
+
+ if (this_is_head) {
+ sah_Free_Head_Descriptor(head_desc);
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Head_Descriptor: %p\n",
+ head_desc);
+ LOG_KDIAG(Diag_msg);
+#endif
+ this_is_head = 0;
+ } else {
+ /* free this descriptor */
+ sah_Free_Descriptor(this_desc);
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Descriptor: %p\n", this_desc);
+ LOG_KDIAG(Diag_msg);
+#endif
+ }
+
+ /* Set up for next round. */
+ this_desc = (sah_Desc *) next_desc;
+ }
+}
+
+/*!
+*******************************************************************************
+* This walks through a SAHARA link chain and frees everything that is
+* not NULL excluding user-space buffers.
+*
+* @brief Kernel Link Chain Destructor
+*
+* @param link A Link pointer from kernel-space.
+*
+* @return void
+*
+*/
+void sah_Destroy_Links(sah_Link * link)
+{
+ sah_Link *this_link = link;
+ sah_Link *next_link = NULL;
+
+ while (this_link != NULL) {
+
+ /* if this link indicates an associated page, process it */
+ if (this_link->vm_info != NULL) {
+ /* since this function is only called from the routine that
+ * creates a kernel copy of the user space descriptor chain,
+ * there are no pages to dirty. All that is needed is to release
+ * the page from cache */
+ page_cache_release(this_link->vm_info);
+ }
+
+ /* Get a bus pointer to the next Link */
+ next_link = (this_link->original_next
+ ? this_link->original_next : this_link->next);
+
+ /* Zero the Pointer and Length fields for security reasons. */
+ this_link->data = NULL;
+ this_link->len = 0;
+
+ /* Free this Link */
+ sah_Free_Link(this_link);
+#ifdef DIAG_MEM
+ sprintf(Diag_msg, "Free_Link: %p\n", this_link);
+ LOG_KDIAG(Diag_msg);
+#endif
+
+ /* Look at the next Link */
+ this_link = next_link;
+ }
+}
+
+/*!
+*******************************************************************************
+* @brief Initialize memory manager/mapper.
+*
+* In 2.4, this function also allocates a kiovec to be used when mapping user
+* data to kernel space
+*
+* @return 0 for success, OS error code on failure
+*
+*/
+int sah_Init_Mem_Map(void)
+{
+ int ret = OS_ERROR_FAIL_S;
+
+ mem_lock = os_lock_alloc_init();
+
+ /*
+ * If one of these fails, change the calculation in the #define earlier in
+ * the file to be the other one.
+ */
+ if (sizeof(sah_Link) > MEM_BLOCK_SIZE) {
+ os_printk("Sahara Driver: sah_Link structure is too large\n");
+ } else if (sizeof(sah_Desc) > MEM_BLOCK_SIZE) {
+ os_printk("Sahara Driver: sah_Desc structure is too large\n");
+ } else {
+ ret = OS_ERROR_OK_S;
+ }
+
+#ifndef SELF_MANAGED_POOL
+
+ big_dma_pool = dma_pool_create("sah_big_blocks", NULL,
+ sizeof(Mem_Big_Block), sizeof(uint32_t),
+ PAGE_SIZE);
+ small_dma_pool = dma_pool_create("sah_small_blocks", NULL,
+ sizeof(Mem_Block), sizeof(uint32_t),
+ PAGE_SIZE);
+#else
+
+#endif
+ return ret;
+}
+
+/*!
+*******************************************************************************
+* @brief Clean up memory manager/mapper.
+*
+* In 2.4, this function also frees the kiovec used when mapping user data to
+* kernel space.
+*
+* @return none
+*
+*/
+void sah_Stop_Mem_Map(void)
+{
+ os_lock_deallocate(mem_lock);
+
+#ifndef SELF_MANAGED_POOL
+ if (big_dma_pool != NULL) {
+ dma_pool_destroy(big_dma_pool);
+ }
+ if (small_dma_pool != NULL) {
+ dma_pool_destroy(small_dma_pool);
+ }
+#endif
+}
+
+/*!
+*******************************************************************************
+* Allocate Head descriptor from free pool.
+*
+* @brief Allocate Head descriptor
+*
+* @return sah_Head_Desc Free descriptor, NULL if no free descriptors available.
+*
+*/
+sah_Head_Desc *sah_Alloc_Head_Descriptor(void)
+{
+ Mem_Big_Block *block;
+ sah_Head_Desc *desc;
+
+ block = sah_Alloc_Big_Block();
+ if (block != NULL) {
+ /* initialize everything */
+ desc = (sah_Head_Desc *) block->data;
+
+ desc->desc.virt_addr = (sah_Desc *) desc;
+ desc->desc.dma_addr = block->dma_addr;
+ desc->desc.original_ptr1 = NULL;
+ desc->desc.original_ptr2 = NULL;
+ desc->desc.original_next = NULL;
+
+ desc->desc.ptr1 = NULL;
+ desc->desc.ptr2 = NULL;
+ desc->desc.next = NULL;
+ } else {
+ desc = NULL;
+ }
+
+ return desc;
+}
+
+/*!
+*******************************************************************************
+* Allocate descriptor from free pool.
+*
+* @brief Allocate descriptor
+*
+* @return sah_Desc Free descriptor, NULL if no free descriptors available.
+*
+*/
+sah_Desc *sah_Alloc_Descriptor(void)
+{
+ Mem_Block *block;
+ sah_Desc *desc;
+
+ block = sah_Alloc_Block();
+ if (block != NULL) {
+ /* initialize everything */
+ desc = (sah_Desc *) block->data;
+
+ desc->virt_addr = desc;
+ desc->dma_addr = block->dma_addr;
+ desc->original_ptr1 = NULL;
+ desc->original_ptr2 = NULL;
+ desc->original_next = NULL;
+
+ desc->ptr1 = NULL;
+ desc->ptr2 = NULL;
+ desc->next = NULL;
+ } else {
+ desc = NULL;
+ }
+
+ return (desc);
+}
+
+/*!
+*******************************************************************************
+* Allocate link from free pool.
+*
+* @brief Allocate link
+*
+* @return sah_Link Free link, NULL if no free links available.
+*
+*/
+sah_Link *sah_Alloc_Link(void)
+{
+ Mem_Block *block;
+ sah_Link *link;
+
+ block = sah_Alloc_Block();
+ if (block != NULL) {
+ /* initialize everything */
+ link = (sah_Link *) block->data;
+
+ link->virt_addr = link;
+ link->original_next = NULL;
+ link->original_data = NULL;
+ /* information found in allocated block */
+ link->dma_addr = block->dma_addr;
+
+ /* Sahara link fields */
+ link->len = 0;
+ link->data = NULL;
+ link->next = NULL;
+
+ /* driver required fields */
+ link->flags = 0;
+ link->vm_info = NULL;
+ } else {
+ link = NULL;
+ }
+
+ return link;
+}
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Add a new page to end of block free pool. This will allocate one page and
+* fill the pool with entries, appending to the end.
+*
+* @brief Add page of blocks to block free pool.
+*
+* @pre This function must be called with the #mem_lock held.
+*
+* @param big 0 - make blocks big enough for sah_Desc
+* non-zero - make blocks big enough for sah_Head_Desc
+*
+* @return int TRUE if blocks added succeesfully, FALSE otherwise
+*
+*/
+int sah_Block_Add_Page(int big)
+{
+ void *page;
+ int success;
+ dma_addr_t dma_addr;
+ unsigned block_index;
+ uint32_t dma_offset;
+ unsigned block_entries =
+ big ? MEM_BIG_BLOCK_ENTRIES : MEM_BLOCK_ENTRIES;
+ unsigned block_size = big ? sizeof(Mem_Big_Block) : sizeof(Mem_Block);
+ void *block;
+
+ /* Allocate page of memory */
+#ifndef USE_COHERENT_MEMORY
+ page = os_alloc_memory(PAGE_SIZE, GFP_ATOMIC | __GFP_DMA);
+ dma_addr = os_pa(page);
+#else
+ page = os_alloc_coherent(PAGE_SIZE, &dma_addr, GFP_ATOMIC);
+#endif
+ if (page != NULL) {
+ /*
+ * Find the difference between the virtual address and the DMA
+ * address of the page. This is used later to determine the DMA
+ * address of each individual block.
+ */
+ dma_offset = page - (void *)dma_addr;
+
+ /* Split page into blocks and add to free pool */
+ block = page;
+ for (block_index = 0; block_index < block_entries;
+ block_index++) {
+ if (big) {
+ register Mem_Big_Block *blockp = block;
+ blockp->dma_addr =
+ (uint32_t) (block - dma_offset);
+ sah_Append_Big_Block(blockp);
+ } else {
+ register Mem_Block *blockp = block;
+ blockp->dma_addr =
+ (uint32_t) (block - dma_offset);
+ /* sah_Append_Block must be protected with spin locks. This is
+ * done in sah_Alloc_Block(), which calls
+ * sah_Block_Add_Page() */
+ sah_Append_Block(blockp);
+ }
+ block += block_size;
+ }
+ success = TRUE;
+#ifdef DIAG_MEM
+ LOG_KDIAG("Succeeded in allocating new page");
+#endif
+ } else {
+ success = FALSE;
+#ifdef DIAG_MEM
+ LOG_KDIAG("Failed in allocating new page");
+#endif
+ }
+
+ return success;
+}
+#endif /* SELF_MANAGED_POOL */
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Allocate block from free pool. A block is large enough to fit either a link
+* or descriptor.
+*
+* @brief Allocate memory block
+*
+* @return Mem_Block Free block, NULL if no free blocks available.
+*
+*/
+static Mem_Big_Block *sah_Alloc_Big_Block(void)
+{
+ Mem_Big_Block *block;
+ os_lock_context_t lock_flags;
+
+ os_lock_save_context(mem_lock, lock_flags);
+
+ /* If the pool is empty, try to allocate more entries */
+ if (big_block_free_head == NULL) {
+ (void)sah_Block_Add_Page(1);
+ }
+
+ /* Check that the pool now has some free entries */
+ if (big_block_free_head != NULL) {
+ /* Return the head of the free pool */
+ block = big_block_free_head;
+
+ big_block_free_head = big_block_free_head->next;
+ if (big_block_free_head == NULL) {
+ /* Allocated last entry in pool */
+ big_block_free_tail = NULL;
+ }
+ } else {
+ block = NULL;
+ }
+ os_unlock_restore_context(mem_lock, lock_flags);
+
+ return block;
+}
+#else
+/*!
+*******************************************************************************
+* Allocate block from free pool. A block is large enough to fit either a link
+* or descriptor.
+*
+* @brief Allocate memory block
+*
+* @return Mem_Block Free block, NULL if no free blocks available.
+*
+*/
+static Mem_Big_Block *sah_Alloc_Big_Block(void)
+{
+ dma_addr_t dma_addr;
+ Mem_Big_Block *block =
+ dma_pool_alloc(big_dma_pool, GFP_ATOMIC, &dma_addr);
+
+ if (block == NULL) {
+ } else {
+ block->dma_addr = dma_addr;
+ }
+
+ return block;
+}
+#endif
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Allocate block from free pool. A block is large enough to fit either a link
+* or descriptor.
+*
+* @brief Allocate memory block
+*
+* @return Mem_Block Free block, NULL if no free blocks available.
+*
+*/
+/******************************************************************************
+*
+* MODIFICATION HISTORY:
+*
+* Date Person Change
+* 31/10/2003 RWK PR52734 - Implement functions to allocate
+* descriptors and links. Replace
+* consistent_alloc() calls. Initial creation.
+*
+******************************************************************************/
+static Mem_Block *sah_Alloc_Block(void)
+{
+ Mem_Block *block;
+ os_lock_context_t lock_flags;
+
+ os_lock_save_context(mem_lock, lock_flags);
+
+ /* If the pool is empty, try to allocate more entries */
+ if (block_free_head == NULL) {
+ (void)sah_Block_Add_Page(0);
+ }
+
+ /* Check that the pool now has some free entries */
+ if (block_free_head != NULL) {
+ /* Return the head of the free pool */
+ block = block_free_head;
+
+ block_free_head = block_free_head->next;
+ if (block_free_head == NULL) {
+ /* Allocated last entry in pool */
+ block_free_tail = NULL;
+ }
+ } else {
+ block = NULL;
+ }
+ os_unlock_restore_context(mem_lock, lock_flags);
+
+ return block;
+}
+#else
+/*!
+*******************************************************************************
+* Allocate block from free pool. A block is large enough to fit either a link
+* or descriptor.
+*
+* @brief Allocate memory block
+*
+* @return Mem_Block Free block, NULL if no free blocks available.
+*
+*/
+/******************************************************************************
+*
+* MODIFICATION HISTORY:
+*
+* Date Person Change
+* 31/10/2003 RWK PR52734 - Implement functions to allocate
+* descriptors and links. Replace
+* consistent_alloc() calls. Initial creation.
+*
+******************************************************************************/
+static Mem_Block *sah_Alloc_Block(void)
+{
+
+ dma_addr_t dma_addr;
+ Mem_Block *block =
+ dma_pool_alloc(small_dma_pool, GFP_ATOMIC, &dma_addr);
+
+ if (block == NULL) {
+ } else {
+ block->dma_addr = dma_addr;
+ }
+
+ return block;
+}
+#endif
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Free memory block back to free pool
+*
+* @brief Free memory block
+*
+* @param block A block allocated with sah_Alloc_Block().
+*
+* @return none
+*
+*/
+static void sah_Free_Block(Mem_Block * block)
+{
+ os_lock_context_t lock_flags;
+
+ os_lock_save_context(mem_lock, lock_flags);
+ sah_Append_Block(block);
+ os_unlock_restore_context(mem_lock, lock_flags);
+}
+#else
+/*!
+*******************************************************************************
+* Free memory block back to free pool
+*
+* @brief Free memory block
+*
+* @param block A block allocated with sah_Alloc_Block().
+*
+* @return none
+*
+*/
+static void sah_Free_Block(Mem_Block * block)
+{
+ dma_pool_free(small_dma_pool, block, block->dma_addr);
+}
+#endif
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Free memory block back to free pool
+*
+* @brief Free memory block
+*
+* @param block A block allocated with sah_Alloc_Block().
+*
+* @return none
+*
+*/
+static void sah_Free_Big_Block(Mem_Big_Block * block)
+{
+ os_lock_context_t lock_flags;
+
+ os_lock_save_context(mem_lock, lock_flags);
+ sah_Append_Big_Block(block);
+ os_unlock_restore_context(mem_lock, lock_flags);
+}
+#else
+/*!
+*******************************************************************************
+* Free memory block back to free pool
+*
+* @brief Free memory block
+*
+* @param block A block allocated with sah_Alloc_Block().
+*
+* @return none
+*
+*/
+static void sah_Free_Big_Block(Mem_Big_Block * block)
+{
+ dma_pool_free(big_dma_pool, block, block->dma_addr);
+}
+#endif
+
+#ifdef SELF_MANAGED_POOL
+/*!
+*******************************************************************************
+* Append memory block to end of free pool.
+*
+* @param block A block entry
+*
+* @return none
+*
+* @pre This function must be called with the #mem_lock held.
+*
+* @brief Append memory block to free pool
+*/
+static void sah_Append_Big_Block(Mem_Big_Block * block)
+{
+
+ /* Initialise block */
+ block->next = NULL;
+
+ /* Remember that block may be the first in the pool */
+ if (big_block_free_tail != NULL) {
+ big_block_free_tail->next = block;
+ } else {
+ /* Pool is empty */
+ big_block_free_head = block;
+ }
+
+ big_block_free_tail = block;
+}
+
+/*!
+*******************************************************************************
+* Append memory block to end of free pool.
+*
+* @brief Append memory block to free pool
+*
+* @param block A block entry
+*
+* @return none
+*
+* @pre #mem_lock must be held
+*
+*/
+static void sah_Append_Block(Mem_Block * block)
+{
+
+ /* Initialise block */
+ block->next = NULL;
+
+ /* Remember that block may be the first in the pool */
+ if (block_free_tail != NULL) {
+ block_free_tail->next = block;
+ } else {
+ /* Pool is empty */
+ block_free_head = block;
+ }
+
+ block_free_tail = block;
+}
+#endif /* SELF_MANAGED_POOL */
+
+/* End of sah_memory_mapper.c */
diff --git a/drivers/mxc/security/sahara2/sah_queue.c b/drivers/mxc/security/sahara2/sah_queue.c
new file mode 100644
index 000000000000..0f3e56e4c254
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_queue.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+* @file sah_queue.c
+*
+* @brief This file provides a FIFO Queue implementation.
+*
+*/
+/******************************************************************************
+*
+* CAUTION:
+*******************************************************************
+*/
+
+/* SAHARA Includes */
+#include <sah_queue_manager.h>
+#ifdef DIAG_DRV_QUEUE
+#include <diagnostic.h>
+#endif
+
+/******************************************************************************
+* Queue Functions
+******************************************************************************/
+
+/*!
+*******************************************************************************
+* This function constructs a new sah_Queue.
+*
+* @brief sah_Queue Constructor
+*
+* @return A pointer to a newly allocated sah_Queue.
+* @return NULL if allocation of memory failed.
+*/
+/******************************************************************************
+*
+* CAUTION: This function may sleep in low-memory situations, as it uses
+* kmalloc ( ..., GFP_KERNEL).
+******************************************************************************/
+sah_Queue *sah_Queue_Construct(void)
+{
+ sah_Queue *q = (sah_Queue *) os_alloc_memory(sizeof(sah_Queue),
+ GFP_KERNEL);
+
+ if (q != NULL) {
+ /* Initialise the queue to an empty state. */
+ q->head = NULL;
+ q->tail = NULL;
+ q->count = 0;
+ }
+#ifdef DIAG_DRV_QUEUE
+ else {
+ LOG_KDIAG("kmalloc() failed.");
+ }
+#endif
+
+ return q;
+}
+
+/*!
+*******************************************************************************
+* This function destroys a sah_Queue.
+*
+* @brief sah_Queue Destructor
+*
+* @param q A pointer to a sah_Queue.
+*
+* @return void
+*/
+/******************************************************************************
+*
+* CAUTION: This function does not free any queue entries.
+*
+******************************************************************************/
+void sah_Queue_Destroy(sah_Queue * q)
+{
+#ifdef DIAG_DRV_QUEUE
+ if (q == NULL) {
+ LOG_KDIAG("Trying to kfree() a NULL pointer.");
+ } else {
+ if (q->count != 0) {
+ LOG_KDIAG
+ ("Trying to destroy a queue that is not empty.");
+ }
+ }
+#endif
+
+ if (q != NULL) {
+ os_free_memory(q);
+ q = NULL;
+ }
+}
+
+/*!
+*******************************************************************************
+* This function appends a sah_Head_Desc to the tail of a sah_Queue.
+*
+* @brief Appends a sah_Head_Desc to a sah_Queue.
+*
+* @param q A pointer to a sah_Queue to append to.
+* @param entry A pointer to a sah_Head_Desc to append.
+*
+* @pre The #desc_queue_lock must be held before calling this function.
+*
+* @return void
+*/
+/******************************************************************************
+*
+* CAUTION: NONE
+******************************************************************************/
+void sah_Queue_Append_Entry(sah_Queue * q, sah_Head_Desc * entry)
+{
+ sah_Head_Desc *tail_entry = NULL;
+
+ if ((q == NULL) || (entry == NULL)) {
+#ifdef DIAG_DRV_QUEUE
+ LOG_KDIAG("Null pointer input.");
+#endif
+ return;
+ }
+
+ if (q->count == 0) {
+ /* The queue is empty */
+ q->head = entry;
+ q->tail = entry;
+ entry->next = NULL;
+ entry->prev = NULL;
+ } else {
+ /* The queue is not empty */
+ tail_entry = q->tail;
+ tail_entry->next = entry;
+ entry->next = NULL;
+ entry->prev = tail_entry;
+ q->tail = entry;
+ }
+ q->count++;
+}
+
+/*!
+*******************************************************************************
+* This function a removes a sah_Head_Desc from the head of a sah_Queue.
+*
+* @brief Removes a sah_Head_Desc from a the head of a sah_Queue.
+*
+* @param q A pointer to a sah_Queue to remove from.
+*
+* @pre The #desc_queue_lock must be held before calling this function.
+*
+* @return void
+*/
+/******************************************************************************
+*
+* CAUTION: This does not kfree() the entry.
+******************************************************************************/
+void sah_Queue_Remove_Entry(sah_Queue * q)
+{
+ sah_Queue_Remove_Any_Entry(q, q->head);
+}
+
+/*!
+*******************************************************************************
+* This function a removes a sah_Head_Desc from anywhere in a sah_Queue.
+*
+* @brief Removes a sah_Head_Desc from anywhere in a sah_Queue.
+*
+* @param qq A pointer to a sah_Queue to remove from.
+* @param entry A pointer to a sah_Head_Desc to remove.
+*
+* @pre The #desc_queue_lock must be held before calling this function.
+*
+* @return void
+*/
+/******************************************************************************
+*
+* CAUTION: This does not kfree() the entry. Does not check to see if the entry
+* actually belongs to the queue.
+******************************************************************************/
+void sah_Queue_Remove_Any_Entry(sah_Queue * q, sah_Head_Desc * entry)
+{
+ sah_Head_Desc *prev_entry = NULL;
+ sah_Head_Desc *next_entry = NULL;
+
+ if ((q == NULL) || (entry == NULL)) {
+#if defined DIAG_DRV_QUEUE && defined DIAG_DURING_INTERRUPT
+ LOG_KDIAG("Null pointer input.");
+#endif
+ return;
+ }
+
+ if (q->count == 1) {
+ /* If q is the only entry in the queue. */
+ q->tail = NULL;
+ q->head = NULL;
+ q->count = 0;
+ } else if (q->count > 1) {
+ /* There are 2 or more entries in the queue. */
+
+#if defined DIAG_DRV_QUEUE && defined DIAG_DURING_INTERRUPT
+ if ((entry->next == NULL) && (entry->prev == NULL)) {
+ LOG_KDIAG
+ ("Queue is not empty yet both next and prev pointers"
+ " are NULL");
+ }
+#endif
+
+ if (entry->next == NULL) {
+ /* If this is the end of the queue */
+ prev_entry = entry->prev;
+ prev_entry->next = NULL;
+ q->tail = prev_entry;
+ } else if (entry->prev == NULL) {
+ /* If this is the head of the queue */
+ next_entry = entry->next;
+ next_entry->prev = NULL;
+ q->head = next_entry;
+ } else {
+ /* If this is somewhere in the middle of the queue */
+ prev_entry = entry->prev;
+ next_entry = entry->next;
+ prev_entry->next = next_entry;
+ next_entry->prev = prev_entry;
+ }
+ q->count--;
+ }
+ /*
+ * Otherwise we are removing an entry from an empty queue.
+ * Don't do anything in the product code
+ */
+#if defined DIAG_DRV_QUEUE && defined DIAG_DURING_INTERRUPT
+ else {
+ LOG_KDIAG("Trying to remove an entry from an empty queue.");
+ }
+#endif
+
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+/* End of sah_queue.c */
diff --git a/drivers/mxc/security/sahara2/sah_queue_manager.c b/drivers/mxc/security/sahara2/sah_queue_manager.c
new file mode 100644
index 000000000000..1602c7043a13
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_queue_manager.c
@@ -0,0 +1,1050 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file sah_queue_manager.c
+ *
+ * @brief This file provides a Queue Manager implementation.
+ *
+ * The Queue Manager manages additions and removal from the queue and updates
+ * the status of queue entries. It also calls sah_HW_* functions to interract
+ * with the hardware.
+*/
+
+#include "portable_os.h"
+
+/* SAHARA Includes */
+#include <sah_driver_common.h>
+#include <sah_queue_manager.h>
+#include <sah_status_manager.h>
+#include <sah_hardware_interface.h>
+#if defined(DIAG_DRV_QUEUE) || defined(DIAG_DRV_STATUS)
+#include <diagnostic.h>
+#endif
+#include <sah_memory_mapper.h>
+
+#ifdef DIAG_DRV_STATUS
+
+#define FSL_INVALID_RETURN 13
+#define MAX_RETURN_STRING_LEN 22
+#endif
+
+/* Defines for parsing value from Error Status register */
+#define SAH_STATUS_MASK 0x07
+#define SAH_ERROR_MASK 0x0F
+#define SAH_CHA_ERR_SOURCE_MASK 0x07
+#define SAH_CHA_ERR_STATUS_MASK 0x0FFF
+#define SAH_DMA_ERR_STATUS_MASK 0x0F
+#define SAH_DMA_ERR_SIZE_MASK 0x03
+#define SAH_DMA_ERR_DIR_MASK 0x01
+
+#define SHA_ERROR_STATUS_CONTINUE 0xFFFFFFFF
+#define SHA_CHA_ERROR_STATUS_DONE 0xFFFFFFFF
+
+/* this maps the error status register's error source 4 bit field to the API
+ * return values. A 0xFFFFFFFF indicates additional fields must be checked to
+ * determine an appropriate return value */
+static sah_Execute_Error sah_Execute_Error_Array[] = {
+ FSL_RETURN_ERROR_S, /* SAH_ERR_NONE */
+ FSL_RETURN_BAD_FLAG_S, /* SAH_ERR_HEADER */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_DESC_LENGTH */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_DESC_POINTER */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_LINK_LENGTH */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_LINK_POINTER */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_INPUT_BUFFER */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_OUTPUT_BUFFER */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_ERR_OUTPUT_BUFFER_STARVATION */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_INTERNAL_STATE */
+ FSL_RETURN_ERROR_S, /* SAH_ERR_GENERAL_DESCRIPTOR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_ERR_RESERVED_FIELDS */
+ FSL_RETURN_MEMORY_ERROR_S, /* SAH_ERR_DESCRIPTOR_ADDRESS */
+ FSL_RETURN_MEMORY_ERROR_S, /* SAH_ERR_LINK_ADDRESS */
+ SHA_ERROR_STATUS_CONTINUE, /* SAH_ERR_CHA */
+ SHA_ERROR_STATUS_CONTINUE /* SAH_ERR_DMA */
+};
+
+static sah_DMA_Error_Status sah_DMA_Error_Status_Array[] = {
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_NO_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_AHB_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_IP_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_PARITY_ERR */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_DMA_BOUNDRY_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_BUSY_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_DMA_RESERVED_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S /* SAH_DMA_INT_ERR */
+};
+
+static sah_CHA_Error_Status sah_CHA_Error_Status_Array[] = {
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_NO_ERR */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_CHA_IP_BUF */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_ADD_ERR */
+ FSL_RETURN_BAD_MODE_S, /* SAH_CHA_MODE_ERR */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /* SAH_CHA_DATA_SIZE_ERR */
+ FSL_RETURN_BAD_KEY_LENGTH_S, /* SAH_CHA_KEY_SIZE_ERR */
+ FSL_RETURN_BAD_MODE_S, /* SAH_CHA_PROC_ERR */
+ FSL_RETURN_ERROR_S, /* SAH_CHA_CTX_READ_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_INTERNAL_HW_ERR */
+ FSL_RETURN_MEMORY_ERROR_S, /* SAH_CHA_IP_BUFF_ERR */
+ FSL_RETURN_MEMORY_ERROR_S, /* SAH_CHA_OP_BUFF_ERR */
+ FSL_RETURN_BAD_KEY_PARITY_S, /* SAH_CHA_DES_KEY_ERR */
+ FSL_RETURN_INTERNAL_ERROR_S, /* SAH_CHA_RES */
+};
+
+#ifdef DIAG_DRV_STATUS
+
+char sah_return_text[FSL_INVALID_RETURN][MAX_RETURN_STRING_LEN] = {
+ "No error", /* FSL_RETURN_OK_S */
+ "Error", /* FSL_RETURN_ERROR_S */
+ "No resource", /* FSL_RETURN_NO_RESOURCE_S */
+ "Bad algorithm", /* FSL_RETURN_BAD_ALGORITHM_S */
+ "Bad mode", /* FSL_RETURN_BAD_MODE_S */
+ "Bad flag", /* FSL_RETURN_BAD_FLAG_S */
+ "Bad key length", /* FSL_RETURN_BAD_KEY_LENGTH_S */
+ "Bad key parity", /* FSL_RETURN_BAD_KEY_PARITY_S */
+ "Bad data length", /* FSL_RETURN_BAD_DATA_LENGTH_S */
+ "Authentication failed", /* FSL_RETURN_AUTH_FAILED_S */
+ "Memory error", /* FSL_RETURN_MEMORY_ERROR_S */
+ "Internal error", /* FSL_RETURN_INTERNAL_ERROR_S */
+ "unknown value", /* default */
+};
+
+#endif /* DIAG_DRV_STATUS */
+
+/*!
+ * This lock must be held while performing any queuing or unqueuing functions,
+ * including reading the first pointer on the queue. It also protects reading
+ * and writing the Sahara DAR register. It must be held during a read-write
+ * operation on the DAR so that the 'test-and-set' is atomic.
+ */
+os_lock_t desc_queue_lock;
+
+/*! This is the main queue for the driver. This is shared between all threads
+ * and is not protected by mutexes since the kernel is non-preemptable. */
+sah_Queue *main_queue = NULL;
+
+/* Internal Prototypes */
+sah_Head_Desc *sah_Find_With_State(sah_Queue_Status state);
+
+#ifdef DIAG_DRV_STATUS
+void sah_Log_Error(uint32_t descriptor, uint32_t error, uint32_t fault_address);
+#endif
+
+extern wait_queue_head_t *int_queue;
+
+/*!
+ * This function initialises the Queue Manager
+ *
+ * @brief Initialise the Queue Manager
+ *
+ * @return FSL_RETURN_OK_S on success; FSL_RETURN_MEMORY_ERROR_S if not
+ */
+fsl_shw_return_t sah_Queue_Manager_Init(void)
+{
+ fsl_shw_return_t ret_val = FSL_RETURN_OK_S;
+
+ desc_queue_lock = os_lock_alloc_init();
+
+ if (main_queue == NULL) {
+ /* Construct the main queue. */
+ main_queue = sah_Queue_Construct();
+
+ if (main_queue == NULL) {
+ ret_val = FSL_RETURN_MEMORY_ERROR_S;
+ }
+ } else {
+#ifdef DIAG_DRV_QUEUE
+ LOG_KDIAG
+ ("Trying to initialise the queue manager more than once.");
+#endif
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This function closes the Queue Manager
+ *
+ * @brief Close the Queue Manager
+ *
+ * @return void
+ */
+void sah_Queue_Manager_Close(void)
+{
+#ifdef DIAG_DRV_QUEUE
+ if (main_queue && main_queue->count != 0) {
+ LOG_KDIAG
+ ("Trying to close the main queue when it is not empty.");
+ }
+#endif
+
+ if (main_queue) {
+ /* There is no error checking here because there is no way to handle
+ it. */
+ sah_Queue_Destroy(main_queue);
+ main_queue = NULL;
+ }
+}
+
+/*!
+ * Count the number of entries on the Queue Manager's queue
+ *
+ * @param ignore_state If non-zero, the @a state parameter is ignored.
+ * If zero, only entries matching @a state are counted.
+ * @param state State of entry to match for counting.
+ *
+ * @return Number of entries which matched criteria
+ */
+int sah_Queue_Manager_Count_Entries(int ignore_state, sah_Queue_Status state)
+{
+ int count = 0;
+ sah_Head_Desc *current_entry;
+
+ /* Start at the head */
+ current_entry = main_queue->head;
+ while (current_entry != NULL) {
+ if (ignore_state || (current_entry->status == state)) {
+ count++;
+ }
+ /* Jump to the next entry. */
+ current_entry = current_entry->next;
+ }
+
+ return count;
+}
+
+/*!
+ * This function removes an entry from the Queue Manager's queue. The entry to
+ * be removed can be anywhere in the queue.
+ *
+ * @brief Remove an entry from the Queue Manager's queue.
+ *
+ * @param entry A pointer to a sah_Head_Desc to remove from the Queue
+ * Manager's queue.
+ *
+ * @pre The #desc_queue_lock must be held before calling this function.
+ *
+ * @return void
+ */
+void sah_Queue_Manager_Remove_Entry(sah_Head_Desc * entry)
+{
+ if (entry == NULL) {
+#ifdef DIAG_DRV_QUEUE
+ LOG_KDIAG("NULL pointer input.");
+#endif
+ } else {
+ sah_Queue_Remove_Any_Entry(main_queue, entry);
+ }
+}
+
+/*!
+ * This function appends an entry to the Queue Managers queue. It primes SAHARA
+ * if this entry is the first PENDING entry in the Queue Manager's Queue.
+ *
+ * @brief Appends an entry to the Queue Manager's queue.
+ *
+ * @param entry A pointer to a sah_Head_Desc to append to the Queue
+ * Manager's queue.
+ *
+ * @pre The #desc_queue_lock may not may be held when calling this function.
+ *
+ * @return void
+ */
+void sah_Queue_Manager_Append_Entry(sah_Head_Desc * entry)
+{
+ sah_Head_Desc *current_entry;
+ os_lock_context_t int_flags;
+
+#ifdef DIAG_DRV_QUEUE
+ if (entry == NULL) {
+ LOG_KDIAG("NULL pointer input.");
+ }
+#endif
+ entry->status = SAH_STATE_PENDING;
+ os_lock_save_context(desc_queue_lock, int_flags);
+ sah_Queue_Append_Entry(main_queue, entry);
+
+ /* Prime SAHARA if the operation that was just appended is the only PENDING
+ * operation in the queue.
+ */
+ current_entry = sah_Find_With_State(SAH_STATE_PENDING);
+ if (current_entry != NULL) {
+ if (current_entry == entry) {
+ sah_Queue_Manager_Prime(entry);
+ }
+ }
+
+ os_unlock_restore_context(desc_queue_lock, int_flags);
+}
+
+/*!
+ * This function marks all entries in the Queue Manager's queue with state
+ * SAH_STATE_RESET.
+ *
+ * @brief Mark all entries with state SAH_STATE_RESET
+ *
+ * @return void
+ *
+ * @note This feature needs re-visiting
+ */
+void sah_Queue_Manager_Reset_Entries(void)
+{
+ sah_Head_Desc *current_entry = NULL;
+
+ /* Start at the head */
+ current_entry = main_queue->head;
+
+ while (current_entry != NULL) {
+ /* Set the state. */
+ current_entry->status = SAH_STATE_RESET;
+ /* Jump to the next entry. */
+ current_entry = current_entry->next;
+ }
+}
+
+/*!
+ * This function primes SAHARA for the first time or after the queue becomes
+ * empty. Queue lock must have been set by the caller of this routine.
+ *
+ * @brief Prime SAHARA.
+ *
+ * @param entry A pointer to a sah_Head_Desc to Prime SAHARA with.
+ *
+ * @return void
+ */
+void sah_Queue_Manager_Prime(sah_Head_Desc * entry)
+{
+#ifdef DIAG_DRV_QUEUE
+ LOG_KDIAG("Priming SAHARA");
+ if (entry == NULL) {
+ LOG_KDIAG("Trying to prime SAHARA with a NULL entry pointer.");
+ }
+#endif
+
+#ifndef SUBMIT_MULTIPLE_DARS
+ /* BUG FIX: state machine can transition from Done1 Busy2 directly
+ * to Idle. To fix that problem, only one DAR is being allowed on
+ * SAHARA at a time */
+ if (sah_Find_With_State(SAH_STATE_ON_SAHARA) != NULL) {
+ return;
+ }
+#endif
+
+#ifdef SAHARA_POWER_MANAGEMENT
+ /* check that dynamic power management is not asserted */
+ if (!sah_dpm_flag) {
+#endif
+
+ /* Enable the SAHARA Clocks */
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA : Enabling the IPG and AHB clocks\n")
+#endif /*DIAG_DRV_IF */
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_enable(SAHARA2_CLK);
+#else
+ {
+ struct clk *clk = clk_get(NULL, "sahara_clk");
+ if (clk != ERR_PTR(ENOENT))
+ clk_enable(clk);
+ clk_put(clk);
+ }
+#endif
+
+ /* Make sure nothing is in the DAR */
+ if (sah_HW_Read_DAR() == 0) {
+#if defined(DIAG_DRV_IF)
+ sah_Dump_Chain(&entry->desc, entry->desc.dma_addr);
+#endif /* DIAG_DRV_IF */
+
+ sah_HW_Write_DAR((entry->desc.dma_addr));
+ entry->status = SAH_STATE_ON_SAHARA;
+ }
+#ifdef DIAG_DRV_QUEUE
+ else {
+ LOG_KDIAG("DAR should be empty when Priming SAHARA");
+ }
+#endif
+#ifdef SAHARA_POWER_MANAGEMENT
+ }
+#endif
+}
+
+#ifndef SAHARA_POLL_MODE
+
+/*!
+ * Reset SAHARA, then load the next descriptor on it, if one exists
+ */
+void sah_reset_sahara_request(void)
+{
+ sah_Head_Desc *desc;
+ os_lock_context_t lock_flags;
+
+#ifdef DIAG_DRV_STATUS
+ LOG_KDIAG("Sahara required reset from tasklet, replace chip");
+#endif
+ sah_HW_Reset();
+
+ /* Now stick in a waiting request */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ if ((desc = sah_Find_With_State(SAH_STATE_PENDING))) {
+ sah_Queue_Manager_Prime(desc);
+ }
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+}
+
+/*!
+ * Post-process a descriptor chain after the hardware has finished with it.
+ *
+ * The status of the descriptor could also be checked. (for FATAL or IGNORED).
+ *
+ * @param desc_head The finished chain
+ * @param error A boolean to mark whether hardware reported error
+ *
+ * @pre The #desc_queue_lock may not be held when calling this function.
+ */
+void sah_process_finished_request(sah_Head_Desc * desc_head, unsigned error)
+{
+ os_lock_context_t lock_flags;
+
+ if (!error) {
+ desc_head->result = FSL_RETURN_OK_S;
+ } else if (desc_head->error_status == -1) {
+ /* Disaster! Sahara has faulted */
+ desc_head->result = FSL_RETURN_ERROR_S;
+ } else {
+ /* translate from SAHARA error status to fsl_shw return values */
+ desc_head->result =
+ sah_convert_error_status(desc_head->error_status);
+#ifdef DIAG_DRV_STATUS
+ sah_Log_Error(desc_head->current_dar, desc_head->error_status,
+ desc_head->fault_address);
+#endif
+ }
+
+ /* Show that the request has been processd */
+ desc_head->status = error ? SAH_STATE_FAILED : SAH_STATE_COMPLETE;
+
+ if (desc_head->uco_flags & FSL_UCO_BLOCKING_MODE) {
+
+ /* Wake up all processes on Sahara queue */
+ wake_up_interruptible(int_queue);
+
+ } else {
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ sah_Queue_Append_Entry(&desc_head->user_info->result_pool,
+ desc_head);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ /* perform callback */
+ if (desc_head->uco_flags & FSL_UCO_CALLBACK_MODE) {
+ desc_head->user_info->callback(desc_head->user_info);
+ }
+ }
+} /* sah_process_finished_request */
+
+/*! Called from bottom half.
+ *
+ * @pre The #desc_queue_lock may not be held when calling this function.
+ */
+void sah_postprocess_queue(unsigned long reset_flag)
+{
+
+ /* if SAHARA needs to be reset, do it here. This starts a descriptor chain
+ * if one is ready also */
+ if (reset_flag) {
+ sah_reset_sahara_request();
+ }
+
+ /* now handle the descriptor chain(s) that has/have completed */
+ do {
+ sah_Head_Desc *first_entry;
+ os_lock_context_t lock_flags;
+
+ os_lock_save_context(desc_queue_lock, lock_flags);
+
+ first_entry = main_queue->head;
+ if ((first_entry != NULL) &&
+ (first_entry->status == SAH_STATE_OFF_SAHARA)) {
+ sah_Queue_Remove_Entry(main_queue);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ sah_process_finished_request(first_entry,
+ (first_entry->
+ error_status != 0));
+ } else {
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+ break;
+ }
+ } while (1);
+
+ return;
+}
+
+#endif /* ifndef SAHARA_POLL_MODE */
+
+/*!
+ * This is a helper function for Queue Manager. This function finds the first
+ * entry in the Queue Manager's queue whose state matches the given input
+ * state. This function starts at the head of the queue and works towards the
+ * tail. If a matching entry was found, the address of the entry is returned.
+ *
+ * @brief Handle the IDLE state.
+ *
+ * @param state A sah_Queue_Status value.
+ *
+ * @pre The #desc_queue_lock must be held before calling this function.
+ *
+ * @return A pointer to a sah_Head_Desc that matches the given state.
+ * @return NULL otherwise.
+ */
+sah_Head_Desc *sah_Find_With_State(sah_Queue_Status state)
+{
+ sah_Head_Desc *current_entry = NULL;
+ sah_Head_Desc *ret_val = NULL;
+ int done_looping = FALSE;
+
+ /* Start at the head */
+ current_entry = main_queue->head;
+
+ while ((current_entry != NULL) && (done_looping == FALSE)) {
+ if (current_entry->status == state) {
+ done_looping = TRUE;
+ ret_val = current_entry;
+ }
+ /* Jump to the next entry. */
+ current_entry = current_entry->next;
+ }
+
+ return ret_val;
+} /* sah_postprocess_queue */
+
+/*!
+ * Process the value from the Sahara error status register and convert it into
+ * an FSL SHW API error code.
+ *
+ * Warning, this routine must only be called if an error exists.
+ *
+ * @param error_status The value from the error status register.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_convert_error_status(uint32_t error_status)
+{
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S; /* catchall */
+ uint8_t error_source;
+ uint8_t DMA_error_status;
+ uint8_t DMA_error_size;
+
+ /* get the error source from the error status register */
+ error_source = error_status & SAH_ERROR_MASK;
+
+ /* array size is maximum allowed by mask, so no boundary checking is
+ * needed here */
+ ret = sah_Execute_Error_Array[error_source];
+
+ /* is this one that needs additional fields checked to determine the
+ * error condition? */
+ if (ret == SHA_ERROR_STATUS_CONTINUE) {
+ /* check the DMA fields */
+ if (error_source == SAH_ERR_DMA) {
+ /* get the DMA transfer error size. If this indicates that no
+ * error was detected, something is seriously wrong */
+ DMA_error_size =
+ (error_status >> 9) & SAH_DMA_ERR_SIZE_MASK;
+ if (DMA_error_size == SAH_DMA_NO_ERR) {
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+ } else {
+ /* get DMA error status */
+ DMA_error_status = (error_status >> 12) &
+ SAH_DMA_ERR_STATUS_MASK;
+
+ /* the DMA error bits cover all the even numbers. By dividing
+ * by 2 it can be used as an index into the error array */
+ ret =
+ sah_DMA_Error_Status_Array[DMA_error_status
+ >> 1];
+ }
+ } else { /* not SAH_ERR_DMA, so must be SAH_ERR_CHA */
+ uint16_t CHA_error_status;
+ uint8_t CHA_error_source;
+
+ /* get CHA Error Source. If this indicates that no error was
+ * detected, something is seriously wrong */
+ CHA_error_source =
+ (error_status >> 28) & SAH_CHA_ERR_SOURCE_MASK;
+ if (CHA_error_source == SAH_CHA_NO_ERROR) {
+ ret = FSL_RETURN_INTERNAL_ERROR_S;
+ } else {
+ uint32_t mask = 1;
+ uint32_t count = 0;
+
+ /* get CHA Error Status */
+ CHA_error_status = (error_status >> 16) &
+ SAH_CHA_ERR_STATUS_MASK;
+
+ /* If more than one bit is set (which shouldn't happen), only
+ * the first will be captured */
+ if (CHA_error_status != 0) {
+ count = 1;
+ while (CHA_error_status != mask) {
+ ++count;
+ mask <<= 1;
+ }
+ }
+
+ ret = sah_CHA_Error_Status_Array[count];
+ }
+ }
+ }
+
+ return ret;
+}
+
+fsl_shw_return_t sah_convert_op_status(uint32_t op_status)
+{
+ unsigned op_source = (op_status >> 28) & 0x7;
+ unsigned op_detail = op_status & 0x3f;
+ fsl_shw_return_t ret = FSL_RETURN_ERROR_S;
+
+ switch (op_source) {
+ case 1: /* SKHA */
+ /* Can't this have "ICV" error from CCM ?? */
+ break;
+ case 2: /* MDHA */
+ if (op_detail == 1) {
+ ret = FSL_RETURN_AUTH_FAILED_S;
+ }
+ break;
+ case 3: /* RNGA */
+ /* Self-test and Compare errors... what to do? */
+ break;
+ case 4: /* PKHA */
+ switch (op_detail) {
+ case 0x01:
+ ret = FSL_RETURN_PRIME_S;
+ break;
+ case 0x02:
+ ret = FSL_RETURN_NOT_PRIME_S;
+ break;
+ case 0x04:
+ ret = FSL_RETURN_POINT_AT_INFINITY_S;
+ break;
+ case 0x08:
+ ret = FSL_RETURN_POINT_NOT_AT_INFINITY_S;
+ break;
+ case 0x10:
+ ret = FSL_RETURN_GCD_IS_ONE_S;
+ break;
+ case 0x20:
+ ret = FSL_RETURN_GCD_IS_NOT_ONE_S;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+#ifdef DIAG_DRV_STATUS
+
+/*!
+ * This function logs the diagnostic information for the given error and
+ * descriptor address. Only used for diagnostic purposes.
+ *
+ * @brief (debug only) Log a description of hardware-detected error.
+ *
+ * @param descriptor The descriptor address that caused the error
+ * @param error The SAHARA error code
+ * @param fault_address Value from the Fault address register
+ *
+ * @return void
+ */
+void sah_Log_Error(uint32_t descriptor, uint32_t error, uint32_t fault_address)
+{
+ char *source_text; /* verbose error source from register */
+ char *address; /* string buffer for descriptor address */
+ char *error_log; /* the complete logging message */
+ char *cha_log = NULL; /* string buffer for descriptor address */
+ char *dma_log = NULL; /* string buffer for descriptor address */
+
+ uint16_t cha_error = 0;
+ uint16_t dma_error = 0;
+
+ uint8_t error_source;
+ sah_Execute_Error return_code;
+
+ /* log error code and descriptor address */
+ error_source = error & SAH_ERROR_MASK;
+ return_code = sah_Execute_Error_Array[error_source];
+
+ source_text = os_alloc_memory(64, GFP_KERNEL);
+
+ switch (error_source) {
+ case SAH_ERR_HEADER:
+ sprintf(source_text, "%s", "Header is not valid");
+ break;
+
+ case SAH_ERR_DESC_LENGTH:
+ sprintf(source_text, "%s",
+ "Descriptor length not equal to sum of link lengths");
+ break;
+
+ case SAH_ERR_DESC_POINTER:
+ sprintf(source_text, "%s", "Length or pointer "
+ "field is zero while the other is non-zero");
+ break;
+
+ case SAH_ERR_LINK_LENGTH:
+ /* note that the Sahara Block Guide 2.7 has an invalid explaination
+ * of this. It only happens when a link length is zero */
+ sprintf(source_text, "%s", "A data length is a link is zero");
+ break;
+
+ case SAH_ERR_LINK_POINTER:
+ sprintf(source_text, "%s",
+ "The data pointer in a link is zero");
+ break;
+
+ case SAH_ERR_INPUT_BUFFER:
+ sprintf(source_text, "%s", "Input Buffer reported an overflow");
+ break;
+
+ case SAH_ERR_OUTPUT_BUFFER:
+ sprintf(source_text, "%s",
+ "Output Buffer reported an underflow");
+ break;
+
+ case SAH_ERR_OUTPUT_BUFFER_STARVATION:
+ sprintf(source_text, "%s", "Incorrect data in output "
+ "buffer after CHA has signalled 'done'");
+ break;
+
+ case SAH_ERR_INTERNAL_STATE:
+ sprintf(source_text, "%s", "Internal Hardware Failure");
+ break;
+
+ case SAH_ERR_GENERAL_DESCRIPTOR:
+ sprintf(source_text, "%s",
+ "Current Descriptor was not legal, but cause is unknown");
+ break;
+
+ case SAH_ERR_RESERVED_FIELDS:
+ sprintf(source_text, "%s",
+ "Reserved pointer field is non-zero");
+ break;
+
+ case SAH_ERR_DESCRIPTOR_ADDRESS:
+ sprintf(source_text, "%s",
+ "Descriptor address not word aligned");
+ break;
+
+ case SAH_ERR_LINK_ADDRESS:
+ sprintf(source_text, "%s", "Link address not word aligned");
+ break;
+
+ case SAH_ERR_CHA:
+ sprintf(source_text, "%s", "CHA Error");
+ {
+ char *cha_module = os_alloc_memory(5, GFP_KERNEL);
+ char *cha_text = os_alloc_memory(45, GFP_KERNEL);
+
+ cha_error = (error >> 28) & SAH_CHA_ERR_SOURCE_MASK;
+
+ switch (cha_error) {
+ case SAH_CHA_SKHA_ERROR:
+ sprintf(cha_module, "%s", "SKHA");
+ break;
+
+ case SAH_CHA_MDHA_ERROR:
+ sprintf(cha_module, "%s", "MDHA");
+ break;
+
+ case SAH_CHA_RNG_ERROR:
+ sprintf(cha_module, "%s", "RNG ");
+ break;
+
+ case SAH_CHA_PKHA_ERROR:
+ sprintf(cha_module, "%s", "PKHA");
+ break;
+
+ case SAH_CHA_NO_ERROR:
+ /* can't happen */
+ /* no break */
+ default:
+ sprintf(cha_module, "%s", "????");
+ break;
+ }
+
+ cha_error = (error >> 16) & SAH_CHA_ERR_STATUS_MASK;
+
+ /* Log CHA Error Status */
+ switch (cha_error) {
+ case SAH_CHA_IP_BUF:
+ sprintf(cha_text, "%s",
+ "Non-empty input buffer when done");
+ break;
+
+ case SAH_CHA_ADD_ERR:
+ sprintf(cha_text, "%s", "Illegal address");
+ break;
+
+ case SAH_CHA_MODE_ERR:
+ sprintf(cha_text, "%s", "Illegal mode");
+ break;
+
+ case SAH_CHA_DATA_SIZE_ERR:
+ sprintf(cha_text, "%s", "Illegal data size");
+ break;
+
+ case SAH_CHA_KEY_SIZE_ERR:
+ sprintf(cha_text, "%s", "Illegal key size");
+ break;
+
+ case SAH_CHA_PROC_ERR:
+ sprintf(cha_text, "%s",
+ "Mode/Context/Key written during processing");
+ break;
+
+ case SAH_CHA_CTX_READ_ERR:
+ sprintf(cha_text, "%s",
+ "Context read during processing");
+ break;
+
+ case SAH_CHA_INTERNAL_HW_ERR:
+ sprintf(cha_text, "%s", "Internal hardware");
+ break;
+
+ case SAH_CHA_IP_BUFF_ERR:
+ sprintf(cha_text, "%s",
+ "Input buffer not enabled or underflow");
+ break;
+
+ case SAH_CHA_OP_BUFF_ERR:
+ sprintf(cha_text, "%s",
+ "Output buffer not enabled or overflow");
+ break;
+
+ case SAH_CHA_DES_KEY_ERR:
+ sprintf(cha_text, "%s", "DES key parity error");
+ break;
+
+ case SAH_CHA_RES:
+ sprintf(cha_text, "%s", "Reserved");
+ break;
+
+ case SAH_CHA_NO_ERR:
+ /* can't happen */
+ /* no break */
+ default:
+ sprintf(cha_text, "%s", "Unknown error");
+ break;
+ }
+
+ cha_log = os_alloc_memory(90, GFP_KERNEL);
+ sprintf(cha_log,
+ " Module %s encountered the error: %s.",
+ cha_module, cha_text);
+
+ os_free_memory(cha_module);
+ os_free_memory(cha_text);
+
+ {
+ uint32_t mask = 1;
+ uint32_t count = 0;
+
+ if (cha_error != 0) {
+ count = 1;
+ while (cha_error != mask) {
+ ++count;
+ mask <<= 1;
+ }
+ }
+
+ return_code = sah_CHA_Error_Status_Array[count];
+ }
+ cha_error = 1;
+ }
+ break;
+
+ case SAH_ERR_DMA:
+ sprintf(source_text, "%s", "DMA Error");
+ {
+ char *dma_direction = os_alloc_memory(6, GFP_KERNEL);
+ char *dma_size = os_alloc_memory(14, GFP_KERNEL);
+ char *dma_text = os_alloc_memory(250, GFP_KERNEL);
+
+ if ((dma_direction == NULL) || (dma_size == NULL) ||
+ (dma_text == NULL)) {
+ LOG_KDIAG
+ ("No memory allocated for DMA debug messages\n");
+ }
+
+ /* log DMA error direction */
+ sprintf(dma_direction, "%s",
+ (((error >> 8) & SAH_DMA_ERR_DIR_MASK) == 1) ?
+ "read" : "write");
+
+ /* log the size of the DMA transfer error */
+ dma_error = (error >> 9) & SAH_DMA_ERR_SIZE_MASK;
+ switch (dma_error) {
+ case SAH_DMA_SIZE_BYTE:
+ sprintf(dma_size, "%s", "byte");
+ break;
+
+ case SAH_DMA_SIZE_HALF_WORD:
+ sprintf(dma_size, "%s", "half-word");
+ break;
+
+ case SAH_DMA_SIZE_WORD:
+ sprintf(dma_size, "%s", "word");
+ break;
+
+ case SAH_DMA_SIZE_RES:
+ sprintf(dma_size, "%s", "reserved size");
+ break;
+
+ default:
+ sprintf(dma_size, "%s", "unknown size");
+ break;
+ }
+
+ /* log DMA error status */
+ dma_error = (error >> 12) & SAH_DMA_ERR_STATUS_MASK;
+ switch (dma_error) {
+ case SAH_DMA_NO_ERR:
+ sprintf(dma_text, "%s", "No DMA Error Code");
+ break;
+
+ case SAH_DMA_AHB_ERR:
+ sprintf(dma_text, "%s",
+ "AHB terminated a bus cycle with an error");
+ break;
+
+ case SAH_DMA_IP_ERR:
+ sprintf(dma_text, "%s",
+ "Internal IP bus cycle was terminated with an "
+ "error termination. This would likely be "
+ "caused by a descriptor length being too "
+ "large, and thus accessing an illegal "
+ "internal address. Verify the length field "
+ "of the current descriptor");
+ break;
+
+ case SAH_DMA_PARITY_ERR:
+ sprintf(dma_text, "%s",
+ "Parity error detected on DMA command from "
+ "Descriptor Decoder. Cause is likely to be "
+ "internal hardware fault");
+ break;
+
+ case SAH_DMA_BOUNDRY_ERR:
+ sprintf(dma_text, "%s",
+ "DMA was requested to cross a 256 byte "
+ "internal address boundary. Cause is likely a "
+ "descriptor length being too large, thus "
+ "accessing two different internal hardware "
+ "blocks");
+ break;
+
+ case SAH_DMA_BUSY_ERR:
+ sprintf(dma_text, "%s",
+ "Descriptor Decoder has made a DMA request "
+ "while the DMA controller is busy. Cause is "
+ "likely due to hardware fault");
+ break;
+
+ case SAH_DMA_RESERVED_ERR:
+ sprintf(dma_text, "%s", "Reserved");
+ break;
+
+ case SAH_DMA_INT_ERR:
+ sprintf(dma_text, "%s",
+ "Internal DMA hardware error detected. The "
+ "DMA controller has detected an internal "
+ "condition which should never occur");
+ break;
+
+ default:
+ sprintf(dma_text, "%s",
+ "Unknown DMA Error Status Code");
+ break;
+ }
+
+ return_code =
+ sah_DMA_Error_Status_Array[dma_error >> 1];
+ dma_error = 1;
+
+ dma_log = os_alloc_memory(320, GFP_KERNEL);
+ sprintf(dma_log,
+ " Occurred during a %s operation of a %s transfer: %s.",
+ dma_direction, dma_size, dma_text);
+
+ os_free_memory(dma_direction);
+ os_free_memory(dma_size);
+ os_free_memory(dma_text);
+ }
+ break;
+
+ case SAH_ERR_NONE:
+ default:
+ sprintf(source_text, "%s", "Unknown Error Code");
+ break;
+ }
+
+ address = os_alloc_memory(35, GFP_KERNEL);
+
+ /* convert error & descriptor address to strings */
+ if (dma_error) {
+ sprintf(address, "Fault address is 0x%08x", fault_address);
+ } else {
+ sprintf(address, "Descriptor bus address is 0x%08x",
+ descriptor);
+ }
+
+ if (return_code > FSL_INVALID_RETURN) {
+ return_code = FSL_INVALID_RETURN;
+ }
+
+ error_log = os_alloc_memory(250, GFP_KERNEL);
+
+ /* construct final log message */
+ sprintf(error_log, "Error source = 0x%08x. Return = %s. %s. %s.",
+ error, sah_return_text[return_code], address, source_text);
+
+ os_free_memory(source_text);
+ os_free_memory(address);
+
+ /* log standard messages */
+ LOG_KDIAG(error_log);
+ os_free_memory(error_log);
+
+ /* add additional information if available */
+ if (cha_error) {
+ LOG_KDIAG(cha_log);
+ os_free_memory(cha_log);
+ }
+
+ if (dma_error) {
+ LOG_KDIAG(dma_log);
+ os_free_memory(dma_log);
+ }
+
+ return;
+} /* sah_Log_Error */
+
+#endif /* DIAG_DRV_STATUS */
+
+/* End of sah_queue_manager.c */
diff --git a/drivers/mxc/security/sahara2/sah_status_manager.c b/drivers/mxc/security/sahara2/sah_status_manager.c
new file mode 100644
index 000000000000..d82facb600a6
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_status_manager.c
@@ -0,0 +1,734 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+* @file sah_status_manager.c
+*
+* @brief Status Manager Function
+*
+* This file contains the function which processes the Sahara status register
+* during an interrupt.
+*
+* This file does not need porting.
+*/
+
+#include "portable_os.h"
+
+#include <sah_status_manager.h>
+#include <sah_hardware_interface.h>
+#include <sah_queue_manager.h>
+#include <sah_memory_mapper.h>
+#include <sah_kernel.h>
+
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+#include <diagnostic.h>
+#endif
+
+/*! Compile-time flag to count various interrupt types. */
+#define DIAG_INT_COUNT
+
+/*!
+ * Number of interrupts processed with Done1Done2 status. Updates to this
+ * value should only be done in interrupt processing.
+ */
+uint32_t done1_count;
+
+/*!
+ * Number of interrupts processed with Done1Busy2 status. Updates to this
+ * value should only be done in interrupt processing.
+ */
+uint32_t done1busy2_count;
+
+/*!
+ * Number of interrupts processed with Done1Done2 status. Updates to this
+ * value should only be done in interrupt processing.
+ */
+uint32_t done1done2_count;
+
+/*!
+ * the dynameic power management flag is false when power management is not
+ * asserted and true when dpm is.
+ */
+#ifdef SAHARA_POWER_MANAGEMENT
+bool sah_dpm_flag = FALSE;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+static int sah_dpm_suspend(struct device *dev, uint32_t state, uint32_t level);
+static int sah_dpm_resume(struct device *dev, uint32_t level);
+#else
+static int sah_dpm_suspend(struct platform_device *dev, pm_message_t state);
+static int sah_dpm_resume(struct platform_device *dev);
+#endif
+#endif
+
+#ifndef SAHARA_POLL_MODE
+/*!
+*******************************************************************************
+* This functionx processes the status register of the Sahara, updates the state
+* of the finished queue entry, and then tries to find more work for Sahara to
+* do.
+*
+* @brief The bulk of the interrupt handling code.
+*
+* @param hw_status The status register of Sahara at time of interrupt.
+* The Clear interrupt bit is already handled by this
+* register read prior to entry into this function.
+* @return void
+*/
+unsigned long sah_Handle_Interrupt(sah_Execute_Status hw_status)
+{
+ unsigned long reset_flag = 0; /* assume no SAHARA reset needed */
+ os_lock_context_t lock_flags;
+ sah_Head_Desc *current_entry;
+
+ /* HW status at time of interrupt */
+ sah_Execute_Status state = hw_status & SAH_EXEC_STATE_MASK;
+
+ do {
+ uint32_t dar;
+
+#ifdef DIAG_INT_COUNT
+ if (state == SAH_EXEC_DONE1) {
+ done1_count++;
+ } else if (state == SAH_EXEC_DONE1_BUSY2) {
+ done1busy2_count++;
+ } else if (state == SAH_EXEC_DONE1_DONE2) {
+ done1done2_count++;
+ }
+#endif
+
+ /* if the first entry on sahara has completed... */
+ if ((state & SAH_EXEC_DONE1_BIT) ||
+ (state == SAH_EXEC_ERROR1)) {
+ /* lock queue while searching */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ current_entry =
+ sah_Find_With_State(SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ /* an active descriptor was not found */
+ if (current_entry == NULL) {
+ /* change state to avoid an infinite loop (possible if
+ * state is SAH_EXEC_DONE1_BUSY2 first time into loop) */
+ hw_status = SAH_EXEC_IDLE;
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+ LOG_KDIAG
+ ("Interrupt received with nothing on queue.");
+#endif
+ } else {
+ /* SAHARA has completed its work on this descriptor chain */
+ current_entry->status = SAH_STATE_OFF_SAHARA;
+
+ if (state == SAH_EXEC_ERROR1) {
+ if (hw_status & STATUS_ERROR) {
+ /* Gather extra diagnostic information */
+ current_entry->fault_address =
+ sah_HW_Read_Fault_Address();
+ /* Read this last - it clears the error */
+ current_entry->error_status =
+ sah_HW_Read_Error_Status();
+ current_entry->op_status = 0;
+#ifdef FSL_HAVE_SAHARA4
+ } else {
+ current_entry->op_status =
+ sah_HW_Read_Op_Status();
+ current_entry->error_status = 0;
+#endif
+ }
+
+ } else {
+ /* indicate that no errors were found with descriptor
+ * chain 1 */
+ current_entry->error_status = 0;
+ current_entry->op_status = 0;
+
+ /* is there a second, successfully, completed descriptor
+ * chain? (done1/error2 processing is handled later) */
+ if (state == SAH_EXEC_DONE1_DONE2) {
+ os_lock_save_context
+ (desc_queue_lock,
+ lock_flags);
+ current_entry =
+ sah_Find_With_State
+ (SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context
+ (desc_queue_lock,
+ lock_flags);
+
+ if (current_entry == NULL) {
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+ LOG_KDIAG
+ ("Done1_Done2 Interrupt received with "
+ "one entry on queue.");
+#endif
+ } else {
+ /* indicate no errors in descriptor chain 2 */
+ current_entry->
+ error_status = 0;
+ current_entry->status =
+ SAH_STATE_OFF_SAHARA;
+ }
+ }
+ }
+ }
+
+#ifdef SAHARA_POWER_MANAGEMENT
+ /* check dynamic power management is not asserted */
+ if (!sah_dpm_flag) {
+#endif
+ do {
+ /* protect DAR and main_queue */
+ os_lock_save_context(desc_queue_lock,
+ lock_flags);
+ dar = sah_HW_Read_DAR();
+ /* check if SAHARA has space for another descriptor. SAHARA
+ * only accepts up to the DAR queue size number of DAR
+ * entries, after that 'dar' will not be zero until the
+ * pending interrupt is serviced */
+ if (dar == 0) {
+ current_entry =
+ sah_Find_With_State
+ (SAH_STATE_PENDING);
+ if (current_entry != NULL) {
+#ifndef SUBMIT_MULTIPLE_DARS
+ /* BUG FIX: state machine can transition from Done1
+ * Busy2 directly to Idle. To fix that problem,
+ * only one DAR is being allowed on SAHARA at a
+ * time. If a high level interrupt has happened,
+ * there could * be an active descriptor chain */
+ if (sah_Find_With_State
+ (SAH_STATE_ON_SAHARA)
+ == NULL) {
+#endif
+#if defined(DIAG_DRV_IF) && defined(DIAG_DURING_INTERRUPT)
+ sah_Dump_Chain
+ (&current_entry->
+ desc,
+ current_entry->
+ desc.
+ dma_addr);
+#endif /* DIAG_DRV_IF */
+ sah_HW_Write_DAR
+ (current_entry->
+ desc.
+ dma_addr);
+ current_entry->
+ status =
+ SAH_STATE_ON_SAHARA;
+#ifndef SUBMIT_MULTIPLE_DARS
+ }
+ current_entry = NULL; /* exit loop */
+#endif
+ }
+ }
+ os_unlock_restore_context
+ (desc_queue_lock, lock_flags);
+ } while ((dar == 0) && (current_entry != NULL));
+#ifdef SAHARA_POWER_MANAGEMENT
+ } /* sah_device_power_manager */
+#endif
+ } else {
+ if (state == SAH_EXEC_FAULT) {
+ sah_Head_Desc *previous_entry; /* point to chain 1 */
+ /* Address of request when fault occured */
+ uint32_t bad_dar = sah_HW_Read_IDAR();
+
+ reset_flag = 1; /* SAHARA needs to be reset */
+
+ /* get first of possible two descriptor chain that was
+ * on SAHARA */
+ os_lock_save_context(desc_queue_lock,
+ lock_flags);
+ previous_entry =
+ sah_Find_With_State(SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context(desc_queue_lock,
+ lock_flags);
+
+ /* if it exists, continue processing the fault */
+ if (previous_entry) {
+ /* assume this chain didn't complete correctly */
+ previous_entry->error_status = -1;
+ previous_entry->status =
+ SAH_STATE_OFF_SAHARA;
+
+ /* get the second descriptor chain */
+ os_lock_save_context(desc_queue_lock,
+ lock_flags);
+ current_entry =
+ sah_Find_With_State
+ (SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context
+ (desc_queue_lock, lock_flags);
+
+ /* if it exists, continue processing both chains */
+ if (current_entry) {
+ /* assume this chain didn't complete correctly */
+ current_entry->error_status =
+ -1;
+ current_entry->status =
+ SAH_STATE_OFF_SAHARA;
+
+ /* now see if either can be identified as the one
+ * in progress when the fault occured */
+ if (current_entry->desc.
+ dma_addr == bad_dar) {
+ /* the second descriptor chain was active when the
+ * fault occured, so the first descriptor chain
+ * was successfull */
+ previous_entry->
+ error_status = 0;
+ } else {
+ if (previous_entry->
+ desc.dma_addr ==
+ bad_dar) {
+ /* if the first chain was in progress when the
+ * fault occured, the second has not yet been
+ * touched, so reset it to PENDING */
+ current_entry->
+ status =
+ SAH_STATE_PENDING;
+ }
+ }
+ }
+ }
+#if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT)
+ } else {
+ /* shouldn't ever get here */
+ if (state == SAH_EXEC_BUSY) {
+ LOG_KDIAG
+ ("Got Sahara interrupt in Busy state");
+ } else {
+ if (state == SAH_EXEC_IDLE) {
+ LOG_KDIAG
+ ("Got Sahara interrupt in Idle state");
+ } else {
+ LOG_KDIAG
+ ("Got Sahara interrupt in unknown state");
+ }
+ }
+#endif
+ }
+ }
+
+ /* haven't handled the done1/error2 (the error 2 part), so setup to
+ * do that now. Otherwise, exit loop */
+ state = (state == SAH_EXEC_DONE1_ERROR2) ?
+ SAH_EXEC_ERROR1 : SAH_EXEC_IDLE;
+
+ /* Keep going while further status is available. */
+ } while (state == SAH_EXEC_ERROR1);
+
+ /* Disabling Sahara Clock only if the hardware is in idle state and
+ the DAR queue is empty.*/
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ current_entry = sah_Find_With_State(SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ if ((current_entry == NULL) && (state == SAH_EXEC_IDLE)) {
+
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA : Disabling the clocks\n")
+#endif /* DIAG_DRV_IF */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SAHARA2_CLK);
+#else
+ {
+ struct clk *clk = clk_get(NULL, "sahara_clk");
+ if (clk != ERR_PTR(ENOENT))
+ clk_disable(clk);
+ clk_put(clk);
+ }
+#endif
+
+ }
+
+ return reset_flag;
+}
+
+#endif /* ifndef SAHARA_POLL_MODE */
+
+#ifdef SAHARA_POLL_MODE
+/*!
+*******************************************************************************
+* Submits descriptor chain to SAHARA, polls on SAHARA for completion, process
+* results, and dephysicalizes chain
+*
+* @brief Handle poll mode.
+*
+* @param entry Virtual address of a physicalized chain
+*
+* @return 0 this function is always successful
+*/
+
+unsigned long sah_Handle_Poll(sah_Head_Desc * entry)
+{
+ sah_Execute_Status hw_status; /* Sahara's status register */
+ os_lock_context_t lock_flags;
+
+ /* lock SARAHA */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+
+#ifdef SAHARA_POWER_MANAGEMENT
+ /* check if the dynamic power management is asserted */
+ if (sah_dpm_flag) {
+ /* return that request failed to be processed */
+ entry->result = FSL_RETURN_ERROR_S;
+ entry->fault_address = 0xBAD;
+ entry->op_status= 0xBAD;
+ entry->error_status = 0xBAD;
+ } else {
+#endif /* SAHARA_POWER_MANAGEMENT */
+
+#if defined(DIAG_DRV_IF)
+ sah_Dump_Chain(&entry->desc, entry->desc.dma_addr);
+#endif /* DIAG_DRV_IF */
+ /* Nothing can be in the dar if we got the lock */
+ sah_HW_Write_DAR((uint32_t) (entry->desc.dma_addr));
+
+ /* Wait for SAHARA to finish with this entry */
+ hw_status = sah_Wait_On_Sahara();
+
+ /* if entry completed successfully, mark it as such */
+ /**** HARDWARE ERROR WORK AROUND (hw_status == SAH_EXEC_IDLE) *****/
+ if (
+#ifndef SUBMIT_MULTIPLE_DARS
+ (hw_status == SAH_EXEC_IDLE) || (hw_status == SAH_EXEC_DONE1_BUSY2) || /* should not happen */
+#endif
+ (hw_status == SAH_EXEC_DONE1)
+ ) {
+ entry->error_status = 0;
+ entry->result = FSL_RETURN_OK_S;
+ } else {
+ /* SAHARA is reporting an error with entry */
+ if (hw_status == SAH_EXEC_ERROR1) {
+ /* Gather extra diagnostic information */
+ entry->fault_address =
+ sah_HW_Read_Fault_Address();
+ /* Read this register last - it clears the error */
+ entry->error_status =
+ sah_HW_Read_Error_Status();
+ entry->op_status = 0;
+ /* translate from SAHARA error status to fsl_shw return values */
+ entry->result =
+ sah_convert_error_status(entry->
+ error_status);
+#ifdef DIAG_DRV_STATUS
+ sah_Log_Error(entry->op_status,
+ entry->error_status,
+ entry->fault_address);
+#endif
+ } else if (hw_status == SAH_EXEC_OPSTAT1) {
+ entry->op_status = sah_HW_Read_Op_Status();
+ entry->error_status = 0;
+ entry->result =
+ sah_convert_op_status(op_status);
+ } else {
+ /* SAHARA entered FAULT state (or something bazaar has
+ * happened) */
+ pr_debug
+ ("Sahara: hw_status = 0x%x; Stat: 0x%08x; IDAR: 0x%08x; "
+ "CDAR: 0x%08x; FltAdr: 0x%08x; Estat: 0x%08x\n",
+ hw_status, sah_HW_Read_Status(),
+ sah_HW_Read_IDAR(), sah_HW_Read_CDAR(),
+ sah_HW_Read_Fault_Address(),
+ sah_HW_Read_Error_Status());
+#ifdef DIAG_DRV_IF
+ {
+ int old_level = console_loglevel;
+ console_loglevel = 8;
+ sah_Dump_Chain(&(entry->desc),
+ entry->desc.dma_addr);
+ console_loglevel = old_level;
+ }
+#endif
+
+ entry->error_status = -1;
+ entry->result = FSL_RETURN_ERROR_S;
+ sah_HW_Reset();
+ }
+ }
+#ifdef SAHARA_POWER_MANAGEMENT
+ }
+#endif
+
+ if (!(entry->uco_flags & FSL_UCO_BLOCKING_MODE)) {
+ /* put it in results pool to allow get_results to work */
+ sah_Queue_Append_Entry(&entry->user_info->result_pool, entry);
+ if (entry->uco_flags & FSL_UCO_CALLBACK_MODE) {
+ /* invoke callback */
+ entry->user_info->callback(entry->user_info);
+ }
+ } else {
+ /* convert the descriptor link back to virtual memory, mark dirty pages
+ * if they are from user mode, and release the page cache for user
+ * pages
+ */
+ entry = sah_DePhysicalise_Descriptors(entry);
+ }
+
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ return 0;
+}
+
+#endif /* SAHARA_POLL_MODE */
+
+/******************************************************************************
+* The following is the implementation of the Dynamic Power Management
+* functionality.
+******************************************************************************/
+#ifdef SAHARA_POWER_MANAGEMENT
+
+static bool sah_dpm_init_flag;
+
+/* dynamic power management information for the sahara driver */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+static struct device_driver sah_dpm_driver = {
+ .name = "sahara_",
+ .bus = &platform_bus_type,
+#else
+static struct platform_driver sah_dpm_driver = {
+ .driver.name = "sahara_",
+ .driver.bus = &platform_bus_type,
+#endif
+ .suspend = sah_dpm_suspend,
+ .resume = sah_dpm_resume
+};
+
+/* dynamic power management information for the sahara HW device */
+static struct platform_device sah_dpm_device = {
+ .name = "sahara_",
+ .id = 1,
+};
+
+/*!
+*******************************************************************************
+* Initilaizes the dynamic power managment functionality
+*
+* @brief Initialization of the Dynamic Power Management functionality
+*
+* @return 0 = success; failed otherwise
+*/
+int sah_dpm_init()
+{
+ int status;
+
+ /* dpm is not asserted */
+ sah_dpm_flag = FALSE;
+
+ /* register the driver to the kernel */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ status = os_register_to_driver(&sah_dpm_driver);
+#else
+ status = os_register_to_driver(&sah_dpm_driver.driver);
+#endif
+
+ if (status == 0) {
+ /* register a single sahara chip */
+ /*status = platform_device_register(&sah_dpm_device); */
+ status = os_register_a_device(&sah_dpm_device);
+
+ /* if something went awry, unregister the driver */
+ if (status != 0) {
+ /*driver_unregister(&sah_dpm_driver); */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ os_unregister_from_driver(&sah_dpm_driver);
+#else
+ os_unregister_from_driver(&sah_dpm_driver.driver);
+#endif
+ sah_dpm_init_flag = FALSE;
+ } else {
+ /* if everything went okay, flag that life is good */
+ sah_dpm_init_flag = TRUE;
+ }
+ }
+
+ /* let the kernel know how it went */
+ return status;
+
+}
+
+/*!
+*******************************************************************************
+* Unregister the dynamic power managment functionality
+*
+* @brief Unregister the Dynamic Power Management functionality
+*
+*/
+void sah_dpm_close()
+{
+ /* if dynamic power management was initilaized, kill it */
+ if (sah_dpm_init_flag == TRUE) {
+ /*driver_unregister(&sah_dpm_driver); */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ os_unregister_from_driver(&sah_dpm_driver);
+#else
+ os_unregister_from_driver(&sah_dpm_driver.driver);
+#endif
+ /*platform_device_register(&sah_dpm_device); */
+ os_unregister_a_device(&sah_dpm_device);
+ }
+}
+
+/*!
+*******************************************************************************
+* Callback routine defined by the Linux Device Model / Dynamic Power management
+* extension. It sets a global flag to disallow the driver to enter queued items
+* into Sahara's DAR.
+*
+* It allows the current active descriptor chains to complete before it returns
+*
+* @brief Suspends the driver
+*
+* @param dev contains device information
+* @param state contains state information
+* @param level level of shutdown
+*
+* @return 0 = success; failed otherwise
+*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+static int sah_dpm_suspend(struct device *dev, uint32_t state, uint32_t level)
+#else
+static int sah_dpm_suspend(struct platform_device *dev, pm_message_t state)
+#endif
+{
+ sah_Head_Desc *entry = NULL;
+ os_lock_context_t lock_flags;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ switch (level) {
+ case SUSPEND_DISABLE:
+ /* Assert dynamic power management. This stops the driver from
+ * entering queued requests to Sahara */
+ sah_dpm_flag = TRUE;
+ break;
+
+ case SUSPEND_SAVE_STATE:
+ break;
+
+ case SUSPEND_POWER_DOWN:
+ /* hopefully between the DISABLE call and this one, the outstanding
+ * work Sahara was doing complete. this checks (and waits) for
+ * those entries that were already active on Sahara to complete */
+ /* lock queue while searching */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ do {
+ entry = sah_Find_With_State(SAH_STATE_ON_SAHARA);
+ } while (entry != NULL);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+
+ /* now we kill the clock so the control circuitry isn't sucking
+ * any power */
+ mxc_clks_disable(SAHARA2_CLK);
+ break;
+ }
+#else
+ /* Assert dynamic power management. This stops the driver from
+ * entering queued requests to Sahara */
+ sah_dpm_flag = TRUE;
+
+ /* Now wait for any outstanding work Sahara was doing to complete.
+ * this checks (and waits) for
+ * those entries that were already active on Sahara to complete */
+ do {
+ /* lock queue while searching */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ entry = sah_Find_With_State(SAH_STATE_ON_SAHARA);
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+ } while (entry != NULL);
+
+ /* now we kill the clock so the control circuitry isn't sucking
+ * any power */
+ {
+ struct clk *clk = clk_get(NULL, "sahara_clk");
+ if (clk != ERR_PTR(ENOENT)) {
+ clk_disable(clk);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+/*!
+*******************************************************************************
+* Callback routine defined by the Linux Device Model / Dynamic Power management
+* extension. It cleears a global flag to allow the driver to enter queued items
+* into Sahara's DAR.
+*
+* It primes the mechanism to start depleting the queue
+*
+* @brief Resumes the driver
+*
+* @param dev contains device information
+* @param level level of resumption
+*
+* @return 0 = success; failed otherwise
+*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+static int sah_dpm_resume(struct device *dev, uint32_t level)
+#else
+static int sah_dpm_resume(struct platform_device *dev)
+#endif
+{
+ sah_Head_Desc *entry = NULL;
+ os_lock_context_t lock_flags;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ switch (level) {
+ case RESUME_POWER_ON:
+ /* enable Sahara's clock */
+ mxc_clks_enable(SAHARA2_CLK);
+ break;
+
+ case RESUME_RESTORE_STATE:
+ break;
+
+ case RESUME_ENABLE:
+ /* Disable dynamic power managment. This allows the driver to put
+ * entries into Sahara's DAR */
+ sah_dpm_flag = FALSE;
+
+ /* find a pending entry to prime the pump */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ entry = sah_Find_With_State(SAH_STATE_PENDING);
+ if (entry != NULL) {
+ sah_Queue_Manager_Prime(entry);
+ }
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+ break;
+ }
+#else
+ {
+ /* enable Sahara's clock */
+ struct clk *clk = clk_get(NULL, "sahara_clk");
+
+ if (clk != ERR_PTR(ENOENT)) {
+ clk_enable(clk);
+ }
+ }
+ sah_dpm_flag = FALSE;
+
+ /* find a pending entry to prime the pump */
+ os_lock_save_context(desc_queue_lock, lock_flags);
+ entry = sah_Find_With_State(SAH_STATE_PENDING);
+ if (entry != NULL) {
+ sah_Queue_Manager_Prime(entry);
+ }
+ os_unlock_restore_context(desc_queue_lock, lock_flags);
+#endif
+ return 0;
+}
+
+#endif /* SAHARA_POWER_MANAGEMENT */
diff --git a/drivers/mxc/security/sahara2/sf_util.c b/drivers/mxc/security/sahara2/sf_util.c
new file mode 100644
index 000000000000..c813b7b22338
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sf_util.c
@@ -0,0 +1,1390 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/**
+* @file sf_util.c
+*
+* @brief Security Functions component API - Utility functions
+*
+* These are the 'Sahara api' functions which are used by the higher-level
+* FSL SHW API to build and then execute descriptor chains.
+*/
+
+
+#include "sf_util.h"
+#include <adaptor.h>
+
+#ifdef DIAG_SECURITY_FUNC
+#include <diagnostic.h>
+#endif /*DIAG_SECURITY_FUNC*/
+
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(sah_Append_Desc);
+EXPORT_SYMBOL(sah_Append_Link);
+EXPORT_SYMBOL(sah_Create_Link);
+EXPORT_SYMBOL(sah_Create_Key_Link);
+EXPORT_SYMBOL(sah_Destroy_Link);
+EXPORT_SYMBOL(sah_Descriptor_Chain_Execute);
+EXPORT_SYMBOL(sah_insert_mdha_algorithm);
+EXPORT_SYMBOL(sah_insert_skha_algorithm);
+EXPORT_SYMBOL(sah_insert_skha_mode);
+EXPORT_SYMBOL(sah_insert_skha_modulus);
+EXPORT_SYMBOL(sah_Descriptor_Chain_Destroy);
+EXPORT_SYMBOL(sah_add_two_in_desc);
+EXPORT_SYMBOL(sah_add_in_key_desc);
+EXPORT_SYMBOL(sah_add_two_out_desc);
+EXPORT_SYMBOL(sah_add_in_out_desc);
+EXPORT_SYMBOL(sah_add_key_out_desc);
+#endif
+
+#ifdef DEBUG_REWORK
+#ifndef __KERNEL__
+#include <stdio.h>
+#define os_printk printf
+#endif
+#endif
+
+/**
+ * Convert fsl_shw_hash_alg_t to mdha mode bits.
+ *
+ * Index must be maintained in order of fsl_shw_hash_alg_t enumeration!!!
+ */
+const uint32_t sah_insert_mdha_algorithm[] =
+{
+ [FSL_HASH_ALG_MD5] = sah_insert_mdha_algorithm_md5,
+ [FSL_HASH_ALG_SHA1] = sah_insert_mdha_algorithm_sha1,
+ [FSL_HASH_ALG_SHA224] = sah_insert_mdha_algorithm_sha224,
+ [FSL_HASH_ALG_SHA256] = sah_insert_mdha_algorithm_sha256,
+};
+
+/**
+ * Header bits for Algorithm field of SKHA header
+ *
+ * Index value must be kept in sync with fsl_shw_key_alg_t
+ */
+const uint32_t sah_insert_skha_algorithm[] =
+{
+ [FSL_KEY_ALG_HMAC] = 0x00000040,
+ [FSL_KEY_ALG_AES] = sah_insert_skha_algorithm_aes,
+ [FSL_KEY_ALG_DES] = sah_insert_skha_algorithm_des,
+ [FSL_KEY_ALG_TDES] = sah_insert_skha_algorithm_tdes,
+ [FSL_KEY_ALG_ARC4] = sah_insert_skha_algorithm_arc4,
+};
+
+
+/**
+ * Header bits for MODE field of SKHA header
+ *
+ * Index value must be kept in sync with fsl_shw_sym_mod_t
+ */
+const uint32_t sah_insert_skha_mode[] =
+{
+ [FSL_SYM_MODE_STREAM] = sah_insert_skha_mode_ecb,
+ [FSL_SYM_MODE_ECB] = sah_insert_skha_mode_ecb,
+ [FSL_SYM_MODE_CBC] = sah_insert_skha_mode_cbc,
+ [FSL_SYM_MODE_CTR] = sah_insert_skha_mode_ctr,
+};
+
+
+/**
+ * Header bits to set CTR modulus size. These have parity
+ * included to allow XOR insertion of values.
+ *
+ * @note Must be kept in sync with fsl_shw_ctr_mod_t
+ */
+const uint32_t sah_insert_skha_modulus[] =
+{
+ [FSL_CTR_MOD_8] = 0x00000000, /**< 2**8 */
+ [FSL_CTR_MOD_16] = 0x80000200, /**< 2**16 */
+ [FSL_CTR_MOD_24] = 0x80000400, /**< 2**24 */
+ [FSL_CTR_MOD_32] = 0x00000600, /**< 2**32 */
+ [FSL_CTR_MOD_40] = 0x80000800, /**< 2**40 */
+ [FSL_CTR_MOD_48] = 0x00000a00, /**< 2**48 */
+ [FSL_CTR_MOD_56] = 0x00000c00, /**< 2**56 */
+ [FSL_CTR_MOD_64] = 0x80000e00, /**< 2**64 */
+ [FSL_CTR_MOD_72] = 0x80001000, /**< 2**72 */
+ [FSL_CTR_MOD_80] = 0x00001200, /**< 2**80 */
+ [FSL_CTR_MOD_88] = 0x00001400, /**< 2**88 */
+ [FSL_CTR_MOD_96] = 0x80001600, /**< 2**96 */
+ [FSL_CTR_MOD_104] = 0x00001800, /**< 2**104 */
+ [FSL_CTR_MOD_112] = 0x80001a00, /**< 2**112 */
+ [FSL_CTR_MOD_120] = 0x80001c00, /**< 2**120 */
+ [FSL_CTR_MOD_128] = 0x00001e00 /**< 2**128 */
+};
+
+
+/******************************************************************************
+* Internal function declarations
+******************************************************************************/
+static fsl_shw_return_t sah_Create_Desc(
+ const sah_Mem_Util *mu,
+ sah_Desc ** desc,
+ int head,
+ uint32_t header,
+ sah_Link * link1,
+ sah_Link * link2);
+
+
+/**
+ * Create a descriptor chain using the the header and links passed in as
+ * parameters. The newly created descriptor will be added to the end of
+ * the descriptor chain passed.
+ *
+ * If @a desc_head points to a NULL value, then a sah_Head_Desc will be created
+ * as the first descriptor. Otherwise a sah_Desc will be created and appended.
+ *
+ * @pre
+ *
+ * - None
+ *
+ * @post
+ *
+ * - A descriptor has been created from the header, link1 and link2.
+ *
+ * - The newly created descriptor has been appended to the end of
+ * desc_head, or its location stored into the location pointed to by
+ * @a desc_head.
+ *
+ * - On allocation failure, @a link1 and @a link2 will be destroyed., and
+ * @a desc_head will be untouched.
+ *
+ * @brief Create and append descriptor chain, inserting header and links
+ * pointing to link1 and link2
+ *
+ * @param mu Memory functions
+ * @param header Value of descriptor header to be added
+ * @param desc_head Pointer to head of descriptor chain to append new desc
+ * @param link1 Pointer to sah_Link 1 (or NULL)
+ * @param link2 Pointer to sah_Link 2 (or NULL)
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_Append_Desc(
+ const sah_Mem_Util *mu,
+ sah_Head_Desc **desc_head,
+ const uint32_t header,
+ sah_Link *link1,
+ sah_Link *link2)
+{
+ fsl_shw_return_t status;
+ sah_Desc *desc;
+ sah_Desc *desc_ptr;
+
+
+ status = sah_Create_Desc(mu, (sah_Desc**)&desc, (*desc_head == NULL),
+ header, link1, link2);
+ /* append newly created descriptor to end of current chain */
+ if (status == FSL_RETURN_OK_S) {
+ if (*desc_head == NULL) {
+ (*desc_head) = (sah_Head_Desc*)desc;
+ (*desc_head)->out1_ptr = NULL;
+ (*desc_head)->out2_ptr = NULL;
+
+ } else {
+ desc_ptr = (sah_Desc*)*desc_head;
+ while (desc_ptr->next != NULL) {
+ desc_ptr = desc_ptr->next;
+ }
+ desc_ptr->next = desc;
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Releases the memory allocated by the Security Function library for
+ * descriptors, links and any internally allocated memory referenced in the
+ * given chain. Note that memory allocated by user applications is not
+ * released.
+ *
+ * @post The @a desc_head pointer will be set to NULL to prevent further use.
+ *
+ * @brief Destroy a descriptor chain and free memory of associated links
+ *
+ * @param mu Memory functions
+ * @param desc_head Pointer to head of descriptor chain to be freed
+ *
+ * @return none
+ */
+void sah_Descriptor_Chain_Destroy (
+ const sah_Mem_Util *mu,
+ sah_Head_Desc **desc_head)
+{
+ sah_Desc *desc_ptr = &(*desc_head)->desc;
+ sah_Head_Desc *desc_head_ptr = (sah_Head_Desc *)desc_ptr;
+
+ while (desc_ptr != NULL) {
+ register sah_Desc *next_desc_ptr;
+
+ if (desc_ptr->ptr1 != NULL) {
+ sah_Destroy_Link(mu, desc_ptr->ptr1);
+ }
+ if (desc_ptr->ptr2 != NULL) {
+ sah_Destroy_Link(mu, desc_ptr->ptr2);
+ }
+
+ next_desc_ptr = desc_ptr->next;
+
+ /* Be sure to free head descriptor as such */
+ if (desc_ptr == (sah_Desc*)desc_head_ptr) {
+ mu->mu_free_head_desc(mu->mu_ref, desc_head_ptr);
+ } else {
+ mu->mu_free_desc(mu->mu_ref, desc_ptr);
+ }
+
+ desc_ptr = next_desc_ptr;
+ }
+
+ *desc_head = NULL;
+}
+
+
+#ifndef NO_INPUT_WORKAROUND
+/**
+ * Reworks the link chain
+ *
+ * @brief Reworks the link chain
+ *
+ * @param mu Memory functions
+ * @param link Pointer to head of link chain to be reworked
+ *
+ * @return none
+ */
+static fsl_shw_return_t sah_rework_link_chain(
+ const sah_Mem_Util *mu,
+ sah_Link* link)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ int found_potential_problem = 0;
+ uint32_t total_data = 0;
+#ifdef DEBUG_REWORK
+ sah_Link* first_link = link;
+#endif
+
+ if ((link->flags & SAH_OUTPUT_LINK)) {
+ return status;
+ }
+
+ while (link != NULL) {
+ total_data += link->len;
+
+ /* Only non-key Input Links are affected by the DMA flush-to-FIFO
+ * problem */
+
+ /* If have seen problem and at end of chain... */
+ if (found_potential_problem && (link->next == NULL) &&
+ (total_data > 16)) {
+ /* insert new 1-byte link */
+ sah_Link* new_tail_link = mu->mu_alloc_link(mu->mu_ref);
+ if (new_tail_link == NULL) {
+ status = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+#ifdef DEBUG_REWORK
+ sah_Link* dump_link = first_link;
+ while (dump_link != NULL) {
+ uint32_t i;
+ unsigned bytes_to_dump = (dump_link->len > 32) ?
+ 32 : dump_link->len;
+ os_printk("(rework)Link %p: %p/%u/%p\n", dump_link,
+ dump_link->data, dump_link->len,
+ dump_link->next);
+ if (!(dump_link->flags & SAH_STORED_KEY_INFO)) {
+ os_printk("(rework)Data %p: ", dump_link->data);
+ for (i = 0; i < bytes_to_dump; i++) {
+ os_printk("%02X ", dump_link->data[i]);
+ }
+ os_printk("\n");
+ }
+ else {
+ os_printk("rework)Data %p: Red key data\n", dump_link);
+ }
+ dump_link = dump_link->next;
+ }
+#endif
+ link->len--;
+ link->next = new_tail_link;
+ new_tail_link->len = 1;
+ new_tail_link->data = link->data+link->len;
+ new_tail_link->flags = link->flags & ~(SAH_OWNS_LINK_DATA);
+ new_tail_link->next = NULL;
+ link = new_tail_link;
+#ifdef DEBUG_REWORK
+ os_printk("(rework)New link chain:\n");
+ dump_link = first_link;
+ while (dump_link != NULL) {
+ uint32_t i;
+ unsigned bytes_to_dump = (dump_link->len > 32) ?
+ 32 : dump_link->len;
+
+ os_printk("Link %p: %p/%u/%p\n", dump_link,
+ dump_link->data, dump_link->len,
+ dump_link->next);
+ if (!(dump_link->flags & SAH_STORED_KEY_INFO)) {
+ os_printk("Data %p: ", dump_link->data);
+ for (i = 0; i < bytes_to_dump; i++) {
+ os_printk("%02X ", dump_link->data[i]);
+ }
+ os_printk("\n");
+ }
+ else {
+ os_printk("Data %p: Red key data\n", dump_link);
+ }
+ dump_link = dump_link->next;
+ }
+#endif
+ }
+ } else if ((link->len % 4) || ((uint32_t)link->data % 4)) {
+ found_potential_problem = 1;
+ }
+
+ link = link->next;
+ }
+
+ return status;
+}
+
+
+/**
+ * Rework links to avoid H/W bug
+ *
+ * @param head Beginning of descriptor chain
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t sah_rework_links(
+ const sah_Mem_Util *mu,
+ sah_Head_Desc *head)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Desc* desc = &head->desc;
+
+ while ((status == FSL_RETURN_OK_S) && (desc != NULL)) {
+ if (desc->header & SAH_HDR_LLO) {
+ status = FSL_RETURN_ERROR_S;
+ break;
+ }
+ if (desc->ptr1 != NULL) {
+ status = sah_rework_link_chain(mu, desc->ptr1);
+ }
+ if ((status == FSL_RETURN_OK_S) && (desc->ptr2 != NULL)) {
+ status = sah_rework_link_chain(mu, desc->ptr2);
+ }
+ desc = desc->next;
+ }
+
+ return status;
+}
+#endif /* NO_INPUT_WORKAROUND */
+
+
+/**
+ * Send a descriptor chain to the SAHARA driver for processing.
+ *
+ * Note that SAHARA will read the input data from and write the output data
+ * directly to the locations indicated during construction of the chain.
+ *
+ * @pre
+ *
+ * - None
+ *
+ * @post
+ *
+ * - @a head will have been executed on SAHARA
+ * - @a head Will be freed unless a SAVE flag is set
+ *
+ * @brief Execute a descriptor chain
+ *
+ * @param head Pointer to head of descriptor chain to be executed
+ * @param user_ctx The user context on which to execute the descriptor chain.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_Descriptor_Chain_Execute(
+ sah_Head_Desc *head,
+ fsl_shw_uco_t *user_ctx)
+{
+ fsl_shw_return_t status;
+
+ /* Check for null pointer or non-multiple-of-four value */
+ if ((head == NULL) || ((uint32_t)head & 0x3)) {
+ status = FSL_RETURN_ERROR_S;
+ goto out;
+ }
+
+#ifndef NO_INPUT_WORKAROUND
+ status = sah_rework_links(user_ctx->mem_util, head);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#endif
+
+ /* complete the information in the descriptor chain head node */
+ head->user_ref = user_ctx->user_ref;
+ head->uco_flags = user_ctx->flags;
+ head->next = NULL; /* driver will use this to link chain heads */
+
+ status = adaptor_Exec_Descriptor_Chain(head, user_ctx);
+
+#ifdef DIAG_SECURITY_FUNC
+ if (status == FSL_RETURN_OK_S)
+ LOG_DIAG("after exec desc chain: status is ok\n");
+ else
+ LOG_DIAG("after exec desc chain: status is not ok\n");
+#endif
+
+ out:
+ return status;
+}
+
+
+/**
+ * Create Link
+ *
+ * @brief Allocate Memory for Link structure and populate using input
+ * parameters
+ *
+ * @post On allocation failure, @a p will be freed if #SAH_OWNS_LINK_DATA is
+ * p set in @a flags.
+
+ * @param mu Memory functions
+ * @param link Pointer to link to be created
+ * @param p Pointer to data to use in link
+ * @param length Length of buffer 'p' in bytes
+ * @param flags Indicates whether memory has been allocated by the calling
+ * function or the security function
+ *
+ * @return FSL_RETURN_OK_S or FSL_RETURN_NO_RESOURCE_S
+ */
+fsl_shw_return_t sah_Create_Link(
+ const sah_Mem_Util *mu,
+ sah_Link **link,
+ uint8_t *p,
+ const size_t length,
+ const sah_Link_Flags flags)
+{
+
+#ifdef DIAG_SECURITY_FUNC
+
+ char diag[50];
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+ fsl_shw_return_t status = FSL_RETURN_NO_RESOURCE_S;
+
+
+ *link = mu->mu_alloc_link(mu->mu_ref);
+
+ /* populate link if memory allocation successful */
+ if (*link != NULL) {
+ (*link)->len = length;
+ (*link)->data = p;
+ (*link)->next = NULL;
+ (*link)->flags = flags;
+ status = FSL_RETURN_OK_S;
+
+#ifdef DIAG_SECURITY_FUNC
+
+ LOG_DIAG("Created Link");
+ LOG_DIAG("------------");
+ sprintf(diag," address = 0x%x", (int) *link);
+ LOG_DIAG(diag);
+ sprintf(diag," link->len = %d",(*link)->len);
+ LOG_DIAG(diag);
+ sprintf(diag," link->data = 0x%x",(int) (*link)->data);
+ LOG_DIAG(diag);
+ sprintf(diag," link->flags = 0x%x",(*link)->flags);
+ LOG_DIAG(diag);
+ LOG_DIAG(" link->next = NULL");
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+
+ } else {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Allocation of memory for sah_Link failed!\n");
+#endif /*DIAG_SECURITY_FUNC*/
+
+ /* Free memory previously allocated by the security function layer for
+ link data. Note that the memory being pointed to will be zeroed before
+ being freed, for security reasons. */
+ if (flags & SAH_OWNS_LINK_DATA) {
+ mu->mu_memset(mu->mu_ref, p, 0x00, length);
+ mu->mu_free(mu->mu_ref, p);
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Create Key Link
+ *
+ * @brief Allocate Memory for Link structure and populate using key info
+ * object
+ *
+ * @param mu Memory functions
+ * @param link Pointer to store address of link to be created
+ * @param key_info Pointer to Key Info object to be referenced
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_Create_Key_Link(
+ const sah_Mem_Util *mu,
+ sah_Link **link,
+ fsl_shw_sko_t *key_info)
+{
+#ifdef DIAG_SECURITY_FUNC_UGLY
+ char diag[50];
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+ fsl_shw_return_t status = FSL_RETURN_NO_RESOURCE_S;
+ sah_Link_Flags flags = 0;
+
+
+ *link = mu->mu_alloc_link(mu->mu_ref);
+
+ /* populate link if memory allocation successful */
+ if (*link != NULL) {
+ (*link)->len = key_info->key_length;
+
+ if (key_info->flags & FSL_SKO_KEY_PRESENT) {
+ (*link)->data = key_info->key;
+ status = FSL_RETURN_OK_S;
+ } else {
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+
+ if (key_info->keystore == NULL) {
+ /* System Keystore */
+ (*link)->slot = key_info->handle;
+ (*link)->ownerid = key_info->userid;
+ (*link)->data = 0;
+ flags |= SAH_STORED_KEY_INFO;
+ status = FSL_RETURN_OK_S;
+ } else {
+#ifdef FSL_HAVE_SCC2
+ /* User Keystore */
+ fsl_shw_kso_t *keystore = key_info->keystore;
+ /* Note: the key data is stored here, but the address has to
+ * be converted to a partition and offset in the kernel.
+ * This will be calculated in kernel space, based on the
+ * list of partitions held by the users context.
+ */
+ (*link)->data =
+ keystore->slot_get_address(keystore->user_data,
+ key_info->handle);
+
+ flags |= SAH_IN_USER_KEYSTORE;
+ status = FSL_RETURN_OK_S;
+#else
+ /* User keystores only supported in SCC2 */
+ status = FSL_RETURN_BAD_FLAG_S;
+#endif /* FSL_HAVE_SCC2 */
+
+ }
+ } else {
+ /* the flag is bad. Should never get here */
+ status = FSL_RETURN_BAD_FLAG_S;
+ }
+ }
+
+ (*link)->next = NULL;
+ (*link)->flags = flags;
+
+#ifdef DIAG_SECURITY_FUNC_UGLY
+ if (status == FSL_RETURN_OK_S) {
+ LOG_DIAG("Created Link");
+ LOG_DIAG("------------");
+ sprintf(diag," address = 0x%x", (int) *link);
+ LOG_DIAG(diag);
+ sprintf(diag," link->len = %d", (*link)->len);
+ LOG_DIAG(diag);
+ if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) {
+ sprintf(diag," link->slot = 0x%x", (*link)->slot);
+ LOG_DIAG(diag);
+ } else {
+ sprintf(diag," link->data = 0x%x", (int)(*link)->data);
+ LOG_DIAG(diag);
+ }
+ sprintf(diag," link->flags = 0x%x", (*link)->flags);
+ LOG_DIAG(diag);
+ LOG_DIAG(" link->next = NULL");
+ }
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+
+ if (status == FSL_RETURN_BAD_FLAG_S) {
+ mu->mu_free_link(mu->mu_ref, *link);
+ *link = NULL;
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creation of sah_Key_Link failed due to bad key flag!\n");
+#endif /*DIAG_SECURITY_FUNC*/
+ }
+
+ } else {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Allocation of memory for sah_Key_Link failed!\n");
+#endif /*DIAG_SECURITY_FUNC*/
+ }
+
+ return status;
+}
+
+
+/**
+ * Append Link
+ *
+ * @brief Allocate Memory for Link structure and append it to the end of
+ * the link chain.
+ *
+ * @post On allocation failure, @a p will be freed if #SAH_OWNS_LINK_DATA is
+ * p set in @a flags.
+ *
+ * @param mu Memory functions
+ * @param link_head Pointer to (head of) existing link chain
+ * @param p Pointer to data to use in link
+ * @param length Length of buffer 'p' in bytes
+ * @param flags Indicates whether memory has been allocated by the calling
+ * function or the security function
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_Append_Link(
+ const sah_Mem_Util *mu,
+ sah_Link *link_head,
+ uint8_t *p,
+ const size_t length,
+ const sah_Link_Flags flags)
+{
+ sah_Link* new_link;
+ fsl_shw_return_t status;
+
+
+ status = sah_Create_Link(mu, &new_link, p, length, flags);
+
+ if (status == FSL_RETURN_OK_S) {
+ /* chase for the tail */
+ while (link_head->next != NULL) {
+ link_head = link_head->next;
+ }
+
+ /* and add new tail */
+ link_head->next = new_link;
+ }
+
+ return status;
+}
+
+
+/**
+ * Create and populate a single descriptor
+ *
+ * The pointer and length fields will be be set based on the chains passed in
+ * as @a link1 and @a link2.
+ *
+ * @param mu Memory utility suite
+ * @param desc Location to store pointer of new descriptor
+ * @param head_desc Non-zero if this will be first in chain; zero otherwise
+ * @param header The Sahara header value to store in the descriptor
+ * @param link1 A value (or NULL) for the first ptr
+ * @param link2 A value (or NULL) for the second ptr
+ *
+ * @post If allocation succeeded, the @a link1 and @link2 will be linked into
+ * the descriptor. If allocation failed, those link structues will be
+ * freed, and the @a desc will be unchanged.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static fsl_shw_return_t sah_Create_Desc(
+ const sah_Mem_Util *mu,
+ sah_Desc ** desc,
+ int head_desc,
+ uint32_t header,
+ sah_Link * link1,
+ sah_Link * link2)
+{
+ fsl_shw_return_t status = FSL_RETURN_NO_RESOURCE_S;
+#ifdef DIAG_SECURITY_FUNC_UGLY
+ char diag[50];
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+
+
+ if (head_desc != 0) {
+ *desc = (sah_Desc *)mu->mu_alloc_head_desc(mu->mu_ref);
+ } else {
+ *desc = mu->mu_alloc_desc(mu->mu_ref);
+ }
+
+ /* populate descriptor if memory allocation successful */
+ if ((*desc) != NULL) {
+ sah_Link* temp_link;
+
+ status = FSL_RETURN_OK_S;
+ (*desc)->header = header;
+
+ temp_link = (*desc)->ptr1 = link1;
+ (*desc)->len1 = 0;
+ while (temp_link != NULL) {
+ (*desc)->len1 += temp_link->len;
+ temp_link = temp_link->next;
+ }
+
+ temp_link = (*desc)->ptr2 = link2;
+ (*desc)->len2 = 0;
+ while (temp_link != NULL) {
+ (*desc)->len2 += temp_link->len;
+ temp_link = temp_link->next;
+ }
+
+ (*desc)->next = NULL;
+
+#ifdef DIAG_SECURITY_FUNC_UGLY
+ LOG_DIAG("Created Desc");
+ LOG_DIAG("------------");
+ sprintf(diag," address = 0x%x",(int) *desc);
+ LOG_DIAG(diag);
+ sprintf(diag," desc->header = 0x%x",(*desc)->header);
+ LOG_DIAG(diag);
+ sprintf(diag," desc->len1 = %d",(*desc)->len1);
+ LOG_DIAG(diag);
+ sprintf(diag," desc->ptr1 = 0x%x",(int) ((*desc)->ptr1));
+ LOG_DIAG(diag);
+ sprintf(diag," desc->len2 = %d",(*desc)->len2);
+ LOG_DIAG(diag);
+ sprintf(diag," desc->ptr2 = 0x%x",(int) ((*desc)->ptr2));
+ LOG_DIAG(diag);
+ sprintf(diag," desc->next = 0x%x",(int) ((*desc)->next));
+ LOG_DIAG(diag);
+#endif /*DIAG_SECURITY_FUNC_UGLY*/
+ } else {
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Allocation of memory for sah_Desc failed!\n");
+#endif /*DIAG_SECURITY_FUNC*/
+
+ /* Destroy the links, otherwise the memory allocated by the Security
+ Function layer for the links (and possibly the data within the links)
+ will be lost */
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Destroy a link (chain) and free memory
+ *
+ * @param mu memory utility suite
+ * @param link The Link to destroy
+ *
+ * @return none
+ */
+void sah_Destroy_Link(
+ const sah_Mem_Util *mu,
+ sah_Link * link)
+{
+
+ while (link != NULL) {
+ sah_Link * next_link = link->next;
+
+ if (link->flags & SAH_OWNS_LINK_DATA) {
+ /* zero data for security purposes */
+ mu->mu_memset(mu->mu_ref, link->data, 0x00, link->len);
+ mu->mu_free(mu->mu_ref, link->data);
+ }
+
+ link->data = NULL;
+ link->next = NULL;
+ mu->mu_free_link(mu->mu_ref, link);
+
+ link = next_link;
+ }
+}
+
+
+/**
+ * Add descriptor where both links are inputs.
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in1 The first input buffer (or NULL)
+ * @param in1_length Size of @a in1
+ * @param[out] in2 The second input buffer (or NULL)
+ * @param in2_length Size of @a in2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_two_in_desc(uint32_t header,
+ const uint8_t* in1,
+ uint32_t in1_length,
+ const uint8_t* in2,
+ uint32_t in2_length,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link* link1 = NULL;
+ sah_Link* link2 = NULL;
+
+ if (in1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) in1, in1_length, SAH_USES_LINK_DATA);
+ }
+
+ if ( (in2 != NULL) && (status == FSL_RETURN_OK_S) ) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) in2, in2_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+}
+
+/*!
+ * Add descriptor where neither link needs sync
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in1 The first input buffer (or NULL)
+ * @param in1_length Size of @a in1
+ * @param[out] in2 The second input buffer (or NULL)
+ * @param in2_length Size of @a in2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_two_d_desc(uint32_t header,
+ const uint8_t * in1,
+ uint32_t in1_length,
+ const uint8_t * in2,
+ uint32_t in2_length,
+ const sah_Mem_Util * mu,
+ sah_Head_Desc ** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ printk("Entering sah_add_two_d_desc \n");
+
+ if (in1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) in1, in1_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if ((in2 != NULL) && (status == FSL_RETURN_OK_S)) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) in2, in2_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+} /* sah_add_two_d_desc() */
+
+/**
+ * Add descriptor where both links are inputs, the second one being a key.
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in1 The first input buffer (or NULL)
+ * @param in1_length Size of @a in1
+ * @param[out] in2 The second input buffer (or NULL)
+ * @param in2_length Size of @a in2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_in_key_desc(uint32_t header,
+ const uint8_t* in1,
+ uint32_t in1_length,
+ fsl_shw_sko_t* key_info,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ if (in1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) in1, in1_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ status = sah_Create_Key_Link(mu, &link2, key_info);
+
+
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+
+out:
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Create two links using keys allocated in the scc
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in1 The first input buffer (or NULL)
+ * @param in1_length Size of @a in1
+ * @param[out] in2 The second input buffer (or NULL)
+ * @param in2_length Size of @a in2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_key_key_desc(uint32_t header,
+ fsl_shw_sko_t *key_info1,
+ fsl_shw_sko_t *key_info2,
+ const sah_Mem_Util *mu,
+ sah_Head_Desc **desc_chain)
+{
+ fsl_shw_return_t status;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+
+ status = sah_Create_Key_Link(mu, &link1, key_info1);
+
+ if (status == FSL_RETURN_OK_S) {
+ status = sah_Create_Key_Link(mu, &link2, key_info2);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+}
+
+
+/**
+ * Add descriptor where first link is input, the second is a changing key
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in1 The first input buffer (or NULL)
+ * @param in1_length Size of @a in1
+ * @param[out] in2 The key for output
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_in_keyout_desc(uint32_t header,
+ const uint8_t* in1,
+ uint32_t in1_length,
+ fsl_shw_sko_t* key_info,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ if (in1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) in1, in1_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ status = sah_Create_Key_Link(mu, &link2, key_info);
+
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+link2->flags |= SAH_OUTPUT_LINK; /* mark key for output */
+status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+
+out:
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ }
+
+ return status;
+}
+
+/**
+ * Add descriptor where both links are outputs.
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param out1 The first output buffer (or NULL)
+ * @param out1_length Size of @a out1
+ * @param[out] out2 The second output buffer (or NULL)
+ * @param out2_length Size of @a out2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_two_out_desc(uint32_t header,
+ uint8_t* out1,
+ uint32_t out1_length,
+ uint8_t* out2,
+ uint32_t out2_length,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+
+ if (out1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) out1, out1_length,
+ SAH_OUTPUT_LINK | SAH_USES_LINK_DATA);
+ }
+
+ if ( (out2 != NULL) && (status == FSL_RETURN_OK_S) ) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) out2, out2_length,
+ SAH_OUTPUT_LINK |
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+}
+
+
+/**
+ * Add descriptor where first link is output, second is output
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param out1 The first output buffer (or NULL)
+ * @param out1_length Size of @a out1
+ * @param[out] in2 The input buffer (or NULL)
+ * @param in2_length Size of @a in2
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_out_in_desc(uint32_t header,
+ uint8_t* out1,
+ uint32_t out1_length,
+ const uint8_t* in2,
+ uint32_t in2_length,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+
+ if (out1 != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) out1, out1_length,
+ SAH_OUTPUT_LINK |
+ SAH_USES_LINK_DATA);
+ }
+
+ if ( (in2 != NULL) && (status == FSL_RETURN_OK_S) ) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) in2, in2_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+}
+
+
+/**
+ * Add descriptor where link1 is input buffer, link2 is output buffer.
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param in The input buffer
+ * @param in_length Size of the input buffer
+ * @param[out] out The output buffer
+ * @param out_length Size of the output buffer
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_in_out_desc(uint32_t header,
+ const uint8_t* in, uint32_t in_length,
+ uint8_t* out, uint32_t out_length,
+ const sah_Mem_Util* mu,
+ sah_Head_Desc** desc_chain)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ if (in != NULL) {
+ status = sah_Create_Link(mu, &link1,
+ (sah_Oct_Str) in, in_length,
+ SAH_USES_LINK_DATA);
+ }
+
+ if ((status == FSL_RETURN_OK_S) && (out != NULL)) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) out, out_length,
+ SAH_OUTPUT_LINK |
+ SAH_USES_LINK_DATA);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+ }
+
+ return status;
+}
+
+
+/**
+ * Add descriptor where link1 is a key, link2 is output buffer.
+ *
+ * @param header The Sahara header value for the descriptor.
+ * @param key_info Key information
+ * @param[out] out The output buffer
+ * @param out_length Size of the output buffer
+ * @param mu Memory functions
+ * @param[in,out] desc_chain Chain to start or append to
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_add_key_out_desc(uint32_t header,
+ const fsl_shw_sko_t *key_info,
+ uint8_t* out, uint32_t out_length,
+ const sah_Mem_Util *mu,
+ sah_Head_Desc **desc_chain)
+{
+ fsl_shw_return_t status;
+ sah_Link *link1 = NULL;
+ sah_Link *link2 = NULL;
+
+ status = sah_Create_Key_Link(mu, &link1, (fsl_shw_sko_t *) key_info);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+
+ if (out != NULL) {
+ status = sah_Create_Link(mu, &link2,
+ (sah_Oct_Str) out, out_length,
+ SAH_OUTPUT_LINK |
+ SAH_USES_LINK_DATA);
+ if (status != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ }
+status = sah_Append_Desc(mu, desc_chain, header, link1, link2);
+
+out:
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Sanity checks the user context object fields to ensure that they make some
+ * sense before passing the uco as a parameter
+ *
+ * @brief Verify the user context object
+ *
+ * @param uco user context object
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t sah_validate_uco(fsl_shw_uco_t *uco)
+{
+ fsl_shw_return_t status = FSL_RETURN_OK_S;
+
+
+ /* check if file is opened */
+ if (uco->sahara_openfd < 0) {
+ status = FSL_RETURN_NO_RESOURCE_S;
+ } else {
+ /* check flag combination: the only invalid setting of the
+ * blocking and callback flags is blocking with callback. So check
+ * for that
+ */
+ if ((uco->flags & (FSL_UCO_BLOCKING_MODE | FSL_UCO_CALLBACK_MODE)) ==
+ (FSL_UCO_BLOCKING_MODE | FSL_UCO_CALLBACK_MODE)) {
+ status = FSL_RETURN_BAD_FLAG_S;
+ } else {
+ /* check that memory utilities have been attached */
+ if (uco->mem_util == NULL) {
+ status = FSL_RETURN_MEMORY_ERROR_S;
+ } else {
+ /* must have pool of at least 1, even for blocking mode */
+ if (uco->pool_size == 0) {
+ status = FSL_RETURN_ERROR_S;
+ } else {
+ /* if callback flag is set, it better have a callback
+ * routine */
+ if (uco->flags & FSL_UCO_CALLBACK_MODE) {
+ if (uco->callback == NULL) {
+ status = FSL_RETURN_INTERNAL_ERROR_S;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return status;
+}
+
+
+/**
+ * Perform any post-processing on non-blocking results.
+ *
+ * For instance, free descriptor chains, compare authentication codes, ...
+ *
+ * @param user_ctx User context object
+ * @param result_info A set of results
+ */
+void sah_Postprocess_Results(fsl_shw_uco_t* user_ctx, sah_results* result_info)
+{
+ unsigned i;
+
+ /* for each result returned */
+ for (i = 0; i < *result_info->actual; i++) {
+ sah_Head_Desc* desc = result_info->results[i].user_desc;
+ uint8_t* out1 = desc->out1_ptr;
+ uint8_t* out2 = desc->out2_ptr;
+ uint32_t len = desc->out_len;
+
+ /*
+ * For now, tne only post-processing besides freeing the
+ * chain is the need to check the auth code for fsl_shw_auth_decrypt().
+ *
+ * If other uses are required in the future, this code will probably
+ * need a flag in the sah_Head_Desc (or a function pointer!) to
+ * determine what needs to be done.
+ */
+ if ((out1 != NULL) && (out2 != NULL)) {
+ unsigned j;
+ for (j = 0; j < len; j++) {
+ if (out1[j] != out2[j]) {
+ /* Problem detected. Change result. */
+ result_info->results[i].code = FSL_RETURN_AUTH_FAILED_S;
+ break;
+ }
+ }
+ /* free allocated 'calced_auth' */
+ user_ctx->mem_util->
+ mu_free(user_ctx->mem_util->mu_ref, out1);
+ }
+
+ /* Free the API-created chain, unless tagged as not-from-API */
+ if (! (desc->uco_flags & FSL_UCO_SAVE_DESC_CHAIN)) {
+ sah_Descriptor_Chain_Destroy(user_ctx->mem_util, &desc);
+ }
+ }
+}
+
+
+/* End of sf_util.c */
diff --git a/drivers/mxc/security/scc2_driver.c b/drivers/mxc/security/scc2_driver.c
new file mode 100644
index 000000000000..6cd837f77806
--- /dev/null
+++ b/drivers/mxc/security/scc2_driver.c
@@ -0,0 +1,2306 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*! @file scc2_driver.c
+ *
+ * This is the driver code for the Security Controller version 2 (SCC2). It's
+ * interaction with the Linux kernel is from calls to #scc_init() when the
+ * driver is loaded, and #scc_cleanup() should the driver be unloaded. The
+ * driver uses locking and (task-sleep/task-wakeup) functions from the kernel.
+ * It also registers itself to handle the interrupt line(s) from the SCC. New
+ * to this version of the driver is an interface providing access to the secure
+ * partitions. This is in turn exposed to the API user through the
+ * fsl_shw_smalloc() series of functions. Other drivers in the kernel may use
+ * the remaining API functions to get at the services of the SCC. The main
+ * service provided is the Secure Memory, which allows encoding and decoding of
+ * secrets with a per-chip secret key.
+ *
+ * The SCC is single-threaded, and so is this module. When the scc_crypt()
+ * routine is called, it will lock out other accesses to the function. If
+ * another task is already in the module, the subsequent caller will spin on a
+ * lock waiting for the other access to finish.
+ *
+ * Note that long crypto operations could cause a task to spin for a while,
+ * preventing other kernel work (other than interrupt processing) to get done.
+ *
+ * The external (kernel module) interface is through the following functions:
+ * @li scc_get_configuration() @li scc_crypt() @li scc_zeroize_memories() @li
+ * scc_monitor_security_failure() @li scc_stop_monitoring_security_failure()
+ * @li scc_set_sw_alarm() @li scc_read_register() @li scc_write_register() @li
+ * scc_allocate_partition() @li scc_initialize_partition @li
+ * scc_release_partition() @li scc_diminish_permissions @li
+ * scc_encrypt_region() @li scc_decrypt_region() @li scc_virt_to_phys
+ *
+ * All other functions are internal to the driver.
+ */
+
+#include "scc2_internals.h"
+#include "sahara2/include/portable_os.h"
+#include <linux/delay.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+
+#include <linux/device.h>
+#include <mach/clock.h>
+#include <linux/device.h>
+
+#else
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#endif
+
+#include <linux/dmapool.h>
+
+/**
+ * This is the set of errors which signal that access to the SCM RAM has
+ * failed or will fail.
+ */
+#define SCM_ACCESS_ERRORS \
+ (SCM_ERRSTAT_ILM | SCM_ERRSTAT_SUP | SCM_ERRSTAT_ERC_MASK)
+
+/******************************************************************************
+ *
+ * Global / Static Variables
+ *
+ *****************************************************************************/
+
+#ifdef SCC_REGISTER_DEBUG
+
+#define REG_PRINT_BUFFER_SIZE 200
+
+static char reg_print_buffer[REG_PRINT_BUFFER_SIZE];
+
+typedef char *(*reg_print_routine_t) (uint32_t value, char *print_buffer,
+ int buf_size);
+
+#endif
+
+/**
+ * This is type void* so that a) it cannot directly be dereferenced,
+ * and b) pointer arithmetic on it will function in a 'normal way' for
+ * the offsets in scc_defines.h
+ *
+ * scc_base is the location in the iomap where the SCC's registers
+ * (and memory) start.
+ *
+ * The referenced data is declared volatile so that the compiler will
+ * not make any assumptions about the value of registers in the SCC,
+ * and thus will always reload the register into CPU memory before
+ * using it (i.e. wherever it is referenced in the driver).
+ *
+ * This value should only be referenced by the #SCC_READ_REGISTER and
+ * #SCC_WRITE_REGISTER macros and their ilk. All dereferences must be
+ * 32 bits wide.
+ */
+static volatile void *scc_base;
+
+/** Array to hold function pointers registered by
+ #scc_monitor_security_failure() and processed by
+ #scc_perform_callbacks() */
+static void (*scc_callbacks[SCC_CALLBACK_SIZE]) (void);
+/*SCC need IRAM's base address but use only the partitions allocated for it.*/
+uint32_t scm_ram_phys_base = IRAM_BASE_ADDR;
+
+void *scm_ram_base = NULL;
+
+/** Calculated once for quick reference to size of the unreserved space in
+ * RAM in SCM.
+ */
+uint32_t scm_memory_size_bytes;
+
+/** Structure returned by #scc_get_configuration() */
+static scc_config_t scc_configuration = {
+ .driver_major_version = SCC_DRIVER_MAJOR_VERSION,
+ .driver_minor_version = SCC_DRIVER_MINOR_VERSION_2,
+ .scm_version = -1,
+ .smn_version = -1,
+ .block_size_bytes = -1,
+ .partition_size_bytes = -1,
+ .partition_count = -1,
+};
+
+/** Internal flag to know whether SCC is in Failed state (and thus many
+ * registers are unavailable). Once it goes failed, it never leaves it. */
+static volatile enum scc_status scc_availability = SCC_STATUS_INITIAL;
+
+/** Flag to say whether interrupt handler has been registered for
+ * SMN interrupt */
+static int smn_irq_set = 0;
+
+/** Flag to say whether interrupt handler has been registered for
+ * SCM interrupt */
+static int scm_irq_set = 0;
+
+/** This lock protects the #scc_callbacks list as well as the @c
+ * callbacks_performed flag in #scc_perform_callbacks. Since the data this
+ * protects may be read or written from either interrupt or base level, all
+ * operations should use the irqsave/irqrestore or similar to make sure that
+ * interrupts are inhibited when locking from base level.
+ */
+static os_lock_t scc_callbacks_lock = NULL;
+
+/**
+ * Ownership of this lock prevents conflicts on the crypto operation in the
+ * SCC.
+ */
+static os_lock_t scc_crypto_lock = NULL;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18))
+/** Pointer to SCC's clock information. Initialized during scc_init(). */
+static struct clk *scc_clk = NULL;
+#endif
+
+/** The lookup table for an 8-bit value. Calculated once
+ * by #scc_init_ccitt_crc().
+ */
+static uint16_t scc_crc_lookup_table[256];
+
+/******************************************************************************
+ *
+ * Function Implementations - Externally Accessible
+ *
+ *****************************************************************************/
+
+/**
+ * Allocate a partition of secure memory
+ *
+ * @param smid_value Value to use for the SMID register. Must be 0 for
+ * kernel mode access.
+ * @param[out] part_no (If successful) Assigned partition number.
+ * @param[out] part_base Kernel virtual address of the partition.
+ * @param[out] part_phys Physical address of the partition.
+ *
+ * @return
+ */
+scc_return_t scc_allocate_partition(uint32_t smid_value,
+ int *part_no,
+ void **part_base, uint32_t *part_phys)
+{
+ uint32_t i;
+ os_lock_context_t irq_flags = 0; /* for IRQ save/restore */
+ int local_part;
+ scc_return_t retval = SCC_RET_FAIL;
+ void *base_addr = NULL;
+ uint32_t reg_value;
+
+ local_part = -1;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+ if (scc_availability == SCC_STATUS_UNIMPLEMENTED) {
+ goto out;
+ }
+
+ /* ACQUIRE LOCK to prevent others from using crypto or acquiring a
+ * partition. Note that crypto operations could take a long time, so the
+ * calling process could potentially spin for some time.
+ */
+ os_lock_save_context(scc_crypto_lock, irq_flags);
+
+ do {
+ /* Find current state of partition ownership */
+ reg_value = SCC_READ_REGISTER(SCM_PART_OWNERS_REG);
+
+ /* Search for a free one */
+ for (i = 0; i < scc_configuration.partition_count; i++) {
+ if (((reg_value >> (SCM_POWN_SHIFT * i))
+ & SCM_POWN_MASK) == SCM_POWN_PART_FREE) {
+ break; /* found a free one */
+ }
+ }
+ if (i == local_part) {
+ /* found this one last time, and failed to allocated it */
+ pr_debug(KERN_ERR "Partition %d cannot be allocated\n",
+ i);
+ goto out;
+ }
+ if (i >= scc_configuration.partition_count) {
+ retval = SCC_RET_INSUFFICIENT_SPACE; /* all used up */
+ goto out;
+ }
+
+ pr_debug
+ ("SCC2: Attempting to allocate partition %i, owners:%08x\n",
+ i, SCC_READ_REGISTER(SCM_PART_OWNERS_REG));
+
+ local_part = i;
+ /* Store SMID to grab a partition */
+ SCC_WRITE_REGISTER(SCM_SMID0_REG +
+ SCM_SMID_WIDTH * (local_part), smid_value);
+ mdelay(2);
+
+ /* Now make sure it is ours... ? */
+ reg_value = SCC_READ_REGISTER(SCM_PART_OWNERS_REG);
+
+ if (((reg_value >> (SCM_POWN_SHIFT * (local_part)))
+ & SCM_POWN_MASK) != SCM_POWN_PART_OWNED) {
+ continue; /* try for another */
+ }
+ base_addr = scm_ram_base +
+ (local_part * scc_configuration.partition_size_bytes);
+ break;
+ } while (1);
+
+out:
+
+ /* Free the lock */
+ os_unlock_restore_context(scc_callbacks_lock, irq_flags);
+
+ /* If the base address was assigned, then a partition was successfully
+ * acquired.
+ */
+ if (base_addr != NULL) {
+ pr_debug("SCC2 Part owners: %08x, engaged: %08x\n",
+ reg_value, SCC_READ_REGISTER(SCM_PART_ENGAGED_REG));
+ pr_debug("SCC2 MAP for part %d: %08x\n",
+ local_part,
+ SCC_READ_REGISTER(SCM_ACC0_REG + 8 * local_part));
+
+ /* Copy the partition information to the data structures passed by the
+ * user.
+ */
+ *part_no = local_part;
+ *part_base = base_addr;
+ *part_phys = (uint32_t) scm_ram_phys_base
+ + (local_part * scc_configuration.partition_size_bytes);
+ retval = SCC_RET_OK;
+
+ pr_debug
+ ("SCC2 partition engaged. Kernel address: %p. Physical "
+ "address: %p, pfn: %08x\n", *part_base, (void *)*part_phys,
+ __phys_to_pfn(*part_phys));
+ }
+
+ return retval;
+} /* allocate_partition() */
+
+/**
+ * Release a partition of secure memory
+ *
+ * @param part_base Kernel virtual address of the partition to be released.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+scc_return_t scc_release_partition(void *part_base)
+{
+ uint32_t partition_no;
+
+ if (part_base == NULL) {
+ return SCC_RET_FAIL;
+ }
+
+ /* Ensure that this is a proper partition location */
+ partition_no = SCM_PART_NUMBER((uint32_t) part_base);
+
+ pr_debug("SCC2: Attempting to release partition %i, owners:%08x\n",
+ partition_no, SCC_READ_REGISTER(SCM_PART_OWNERS_REG));
+
+ /* check that the partition is ours to de-establish */
+ if (!host_owns_partition(partition_no)) {
+ return SCC_RET_FAIL;
+ }
+
+ /* TODO: The state of the zeroize engine (SRS field in the Command Status
+ * Register) should be examined before issuing the zeroize command here.
+ * To make the driver thread-safe, a lock should be taken out before
+ * issuing the check and released after the zeroize command has been
+ * issued.
+ */
+
+ /* Zero the partition to release it */
+ scc_write_register(SCM_ZCMD_REG,
+ (partition_no << SCM_ZCMD_PART_SHIFT) |
+ (ZCMD_DEALLOC_PART << SCM_ZCMD_CCMD_SHIFT));
+ mdelay(2);
+
+ pr_debug("SCC2: done releasing partition %i, owners:%08x\n",
+ partition_no, SCC_READ_REGISTER(SCM_PART_OWNERS_REG));
+
+ /* Check that the de-assignment went correctly */
+ if (host_owns_partition(partition_no)) {
+ return SCC_RET_FAIL;
+ }
+
+ return SCC_RET_OK;
+}
+
+/**
+ * Diminish the permissions on a partition of secure memory
+ *
+ * @param part_base Kernel virtual address of the partition.
+ * @param permissions ORed values of the type SCM_PERM_* which will be used as
+ * initial partition permissions. SHW API users should use
+ * the FSL_PERM_* definitions instead.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+scc_return_t scc_diminish_permissions(void *part_base, uint32_t permissions)
+{
+ uint32_t partition_no;
+ uint32_t permissions_requested;
+ permissions_requested = permissions;
+
+ /* ensure that this is a proper partition location */
+ partition_no = SCM_PART_NUMBER((uint32_t) part_base);
+
+ /* invert the permissions, masking out unused bits */
+ permissions = (~permissions) & SCM_PERM_MASK;
+
+ /* attempt to diminish the permissions */
+ scc_write_register(SCM_ACC0_REG + 8 * partition_no, permissions);
+ mdelay(2);
+
+ /* Reading it back puts it into the original form */
+ permissions = SCC_READ_REGISTER(SCM_ACC0_REG + 8 * partition_no);
+ if (permissions == permissions_requested) {
+ pr_debug("scc_partition_diminish_perms: successful\n");
+ pr_debug("scc_partition_diminish_perms: successful\n");
+ return SCC_RET_OK;
+ }
+ pr_debug("scc_partition_diminish_perms: not successful\n");
+
+ return SCC_RET_FAIL;
+}
+
+extern scc_partition_status_t scc_partition_status(void *part_base)
+{
+ uint32_t part_no;
+ uint32_t part_owner;
+
+ /* Determine the partition number from the address */
+ part_no = SCM_PART_NUMBER((uint32_t) part_base);
+
+ /* Check if the partition is implemented */
+ if (part_no >= scc_configuration.partition_count) {
+ return SCC_PART_S_UNUSABLE;
+ }
+
+ /* Determine the value of the partition owners register */
+ part_owner = (SCC_READ_REGISTER(SCM_PART_OWNERS_REG)
+ >> (part_no * SCM_POWN_SHIFT)) & SCM_POWN_MASK;
+
+ switch (part_owner) {
+ case SCM_POWN_PART_OTHER:
+ return SCC_PART_S_UNAVAILABLE;
+ break;
+ case SCM_POWN_PART_FREE:
+ return SCC_PART_S_AVAILABLE;
+ break;
+ case SCM_POWN_PART_OWNED:
+ /* could be allocated or engaged*/
+ if (partition_engaged(part_no)) {
+ return SCC_PART_S_ENGAGED;
+ } else {
+ return SCC_PART_S_ALLOCATED;
+ }
+ break;
+ case SCM_POWN_PART_UNUSABLE:
+ default:
+ return SCC_PART_S_UNUSABLE;
+ break;
+ }
+}
+
+/**
+ * Calculate the physical address from the kernel virtual address.
+ *
+ * @param address Kernel virtual address of data in an Secure Partition.
+ * @return Physical address of said data.
+ */
+uint32_t scc_virt_to_phys(void *address)
+{
+ return (uint32_t) address - (uint32_t) scm_ram_base
+ + (uint32_t) scm_ram_phys_base;
+}
+
+/**
+ * Engage partition of secure memory
+ *
+ * @param part_base (kernel) Virtual
+ * @param UMID NULL, or 16-byte UMID for partition security
+ * @param permissions ORed values from fsl_shw_permission_t which
+ * will be used as initial partiition permissions.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+
+scc_return_t
+scc_engage_partition(void *part_base,
+ const uint8_t *UMID, uint32_t permissions)
+{
+ uint32_t partition_no;
+ uint8_t *UMID_base = part_base + 0x10;
+ uint32_t *MAP_base = part_base;
+ uint8_t i;
+
+ partition_no = SCM_PART_NUMBER((uint32_t) part_base);
+
+ if (!host_owns_partition(partition_no) ||
+ partition_engaged(partition_no) ||
+ !(SCC_READ_REGISTER(SCM_SMID0_REG + (partition_no * 8)) == 0)) {
+
+ return SCC_RET_FAIL;
+ }
+
+ if (UMID != NULL) {
+ for (i = 0; i < 16; i++) {
+ UMID_base[i] = UMID[i];
+ }
+ }
+
+ MAP_base[0] = permissions;
+
+ udelay(20);
+
+ /* Check that the partition was engaged correctly, and that it has the
+ * proper permissions.
+ */
+
+ if ((!partition_engaged(partition_no)) ||
+ (permissions !=
+ SCC_READ_REGISTER(SCM_ACC0_REG + 8 * partition_no))) {
+ return SCC_RET_FAIL;
+ }
+
+ return SCC_RET_OK;
+}
+
+/*****************************************************************************/
+/* fn scc_init() */
+/*****************************************************************************/
+/**
+ * Initialize the driver at boot time or module load time.
+ *
+ * Register with the kernel as the interrupt handler for the SCC interrupt
+ * line(s).
+ *
+ * Map the SCC's register space into the driver's memory space.
+ *
+ * Query the SCC for its configuration and status. Save the configuration in
+ * #scc_configuration and save the status in #scc_availability. Called by the
+ * kernel.
+ *
+ * Do any locking/wait queue initialization which may be necessary.
+ *
+ * The availability fuse may be checked, depending on platform.
+ */
+static int scc_init(void)
+{
+ uint32_t smn_status;
+ int i;
+ int return_value = -EIO; /* assume error */
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+
+ /* Set this until we get an initial reading */
+ scc_availability = SCC_STATUS_CHECKING;
+
+ /* Initialize the constant for the CRC function */
+ scc_init_ccitt_crc();
+
+ /* initialize the callback table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ scc_callbacks[i] = 0;
+ }
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
+ mxc_clks_enable(SCC_CLK);
+#else
+ scc_clk = clk_get(NULL, "scc_clk");
+ if (scc_clk != ERR_PTR(ENOENT)) {
+ clk_enable(scc_clk);
+ }
+#endif
+
+ /* Set up the hardware access locks */
+ scc_callbacks_lock = os_lock_alloc_init();
+ scc_crypto_lock = os_lock_alloc_init();
+ if (scc_callbacks_lock == NULL || scc_crypto_lock == NULL) {
+ os_printk(KERN_ERR
+ "SCC2: Failed to allocate context locks. Exiting.\n");
+ goto out;
+ }
+
+ /* See whether there is an SCC available */
+ if (0 && !SCC_ENABLED()) {
+ os_printk(KERN_ERR
+ "SCC2: Fuse for SCC is set to disabled. Exiting.\n");
+ goto out;
+ }
+ /* Map the SCC (SCM and SMN) memory on the internal bus into
+ kernel address space */
+ scc_base = (void *)IO_ADDRESS(SCC_BASE);
+ if (scc_base == NULL) {
+ os_printk(KERN_ERR
+ "SCC2: Register mapping failed. Exiting.\n");
+ goto out;
+ }
+
+ /* If that worked, we can try to use the SCC */
+ /* Get SCM into 'clean' condition w/interrupts cleared &
+ disabled */
+ SCC_WRITE_REGISTER(SCM_INT_CTL_REG, 0);
+
+ /* Clear error status register */
+ (void)SCC_READ_REGISTER(SCM_ERR_STATUS_REG);
+
+ /*
+ * There is an SCC. Determine its current state. Side effect
+ * is to populate scc_config and scc_availability
+ */
+ smn_status = scc_grab_config_values();
+
+ /* Try to set up interrupt handler(s) */
+ if (scc_availability != SCC_STATUS_OK) {
+ goto out;
+ }
+
+ if (cpu_is_mx51_rev(CHIP_REV_2_0) < 0)
+ scm_ram_phys_base += 0x8000;
+
+ scm_ram_base = (void *)ioremap_nocache(scm_ram_phys_base,
+ scc_configuration.
+ partition_count *
+ scc_configuration.
+ partition_size_bytes);
+ if (scm_ram_base == NULL) {
+ os_printk(KERN_ERR
+ "SCC2: RAM failed to remap: %p for %d bytes\n",
+ (void *)scm_ram_phys_base,
+ scc_configuration.partition_count *
+ scc_configuration.partition_size_bytes);
+ goto out;
+ }
+ pr_debug("SCC2: RAM at Physical %p / Virtual %p\n",
+ (void *)scm_ram_phys_base, scm_ram_base);
+
+ pr_debug("Secure Partition Table: Found %i partitions\n",
+ scc_configuration.partition_count);
+
+ if (setup_interrupt_handling() != 0) {
+ unsigned err_cond;
+ /**
+ * The error could be only that the SCM interrupt was
+ * not set up. This interrupt is always masked, so
+ * that is not an issue.
+ * The SMN's interrupt may be shared on that line, it
+ * may be separate, or it may not be wired. Do what
+ * is necessary to check its status.
+ * Although the driver is coded for possibility of not
+ * having SMN interrupt, the fact that there is one
+ * means it should be available and used.
+ */
+#ifdef USE_SMN_INTERRUPT
+ err_cond = !smn_irq_set; /* Separate. Check SMN binding */
+#elif !defined(NO_SMN_INTERRUPT)
+ err_cond = !scm_irq_set; /* Shared. Check SCM binding */
+#else
+ err_cond = FALSE; /* SMN not wired at all. Ignore. */
+#endif
+ if (err_cond) {
+ /* setup was not able to set up SMN interrupt */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED;
+ goto out;
+ }
+ }
+
+ /* interrupt handling returned non-zero */
+ /* Get SMN into 'clean' condition w/interrupts cleared &
+ enabled */
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_CLEAR_INTERRUPT
+ | SMN_COMMAND_ENABLE_INTERRUPT);
+
+ out:
+ /*
+ * If status is SCC_STATUS_UNIMPLEMENTED or is still
+ * SCC_STATUS_CHECKING, could be leaving here with the driver partially
+ * initialized. In either case, cleanup (which will mark the SCC as
+ * UNIMPLEMENTED).
+ */
+ if (scc_availability == SCC_STATUS_CHECKING ||
+ scc_availability == SCC_STATUS_UNIMPLEMENTED) {
+ scc_cleanup();
+ } else {
+ return_value = 0; /* All is well */
+ }
+ }
+ /* ! STATUS_INITIAL */
+ os_printk(KERN_ALERT "SCC2: Driver Status is %s\n",
+ (scc_availability == SCC_STATUS_INITIAL) ? "INITIAL" :
+ (scc_availability == SCC_STATUS_CHECKING) ? "CHECKING" :
+ (scc_availability ==
+ SCC_STATUS_UNIMPLEMENTED) ? "UNIMPLEMENTED"
+ : (scc_availability ==
+ SCC_STATUS_OK) ? "OK" : (scc_availability ==
+ SCC_STATUS_FAILED) ? "FAILED" :
+ "UNKNOWN");
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SCC_CLK);
+#else
+ if (scc_clk != ERR_PTR(ENOENT))
+ clk_disable(scc_clk);
+#endif
+
+ return return_value;
+} /* scc_init */
+
+/*****************************************************************************/
+/* fn scc_cleanup() */
+/*****************************************************************************/
+/**
+ * Perform cleanup before driver/module is unloaded by setting the machine
+ * state close to what it was when the driver was loaded. This function is
+ * called when the kernel is shutting down or when this driver is being
+ * unloaded.
+ *
+ * A driver like this should probably never be unloaded, especially if there
+ * are other module relying upon the callback feature for monitoring the SCC
+ * status.
+ *
+ * In any case, cleanup the callback table (by clearing out all of the
+ * pointers). Deregister the interrupt handler(s). Unmap SCC registers.
+ *
+ * Note that this will not release any partitions that have been allocated.
+ *
+ */
+static void scc_cleanup(void)
+{
+ int i;
+
+ /******************************************************/
+
+ /* Mark the driver / SCC as unusable. */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED;
+
+ /* Clear out callback table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ scc_callbacks[i] = 0;
+ }
+
+ /* If SCC has been mapped in, clean it up and unmap it */
+ if (scc_base) {
+ /* For the SCM, disable interrupts. */
+ SCC_WRITE_REGISTER(SCM_INT_CTL_REG, 0);
+
+ /* For the SMN, clear and disable interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_CLEAR_INTERRUPT);
+ }
+
+ /* Now that interrupts cannot occur, disassociate driver from the interrupt
+ * lines.
+ */
+
+ /* Deregister SCM interrupt handler */
+ if (scm_irq_set) {
+ os_deregister_interrupt(INT_SCC_SCM);
+ }
+
+ /* Deregister SMN interrupt handler */
+ if (smn_irq_set) {
+#ifdef USE_SMN_INTERRUPT
+ os_deregister_interrupt(INT_SCC_SMN);
+#endif
+ }
+
+ /* Finally, release the mapped memory */
+ iounmap(scm_ram_base);
+
+ if (scc_callbacks_lock != NULL)
+ os_lock_deallocate(scc_callbacks_lock);
+
+ if (scc_crypto_lock != NULL)
+ os_lock_deallocate(scc_crypto_lock);
+
+ /*Disabling SCC Clock*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SCC_CLK);
+#else
+ if (scc_clk != ERR_PTR(ENOENT))
+ clk_disable(scc_clk);
+ clk_put(scc_clk);
+#endif
+ pr_debug("SCC2 driver cleaned up.\n");
+
+} /* scc_cleanup */
+
+/*****************************************************************************/
+/* fn scc_get_configuration() */
+/*****************************************************************************/
+scc_config_t *scc_get_configuration(void)
+{
+ /*
+ * If some other driver calls scc before the kernel does, make sure that
+ * this driver's initialization is performed.
+ */
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /**
+ * If there is no SCC, yet the driver exists, the value -1 will be in
+ * the #scc_config_t fields for other than the driver versions.
+ */
+ return &scc_configuration;
+} /* scc_get_configuration */
+
+/*****************************************************************************/
+/* fn scc_zeroize_memories() */
+/*****************************************************************************/
+scc_return_t scc_zeroize_memories(void)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+
+ return return_status;
+} /* scc_zeroize_memories */
+
+/*****************************************************************************/
+/* fn scc_set_sw_alarm() */
+/*****************************************************************************/
+void scc_set_sw_alarm(void)
+{
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Update scc_availability based on current SMN status. This might
+ * perform callbacks.
+ */
+ (void)scc_update_state();
+
+ /* if everything is OK, make it fail */
+ if (scc_availability == SCC_STATUS_OK) {
+
+ /* sound the alarm (and disable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_SET_SOFTWARE_ALARM);
+
+ scc_availability = SCC_STATUS_FAILED; /* Remember what we've done */
+
+ /* In case SMN interrupt is not available, tell the world */
+ scc_perform_callbacks();
+ }
+
+ return;
+} /* scc_set_sw_alarm */
+
+/*****************************************************************************/
+/* fn scc_monitor_security_failure() */
+/*****************************************************************************/
+scc_return_t scc_monitor_security_failure(void callback_func(void))
+{
+ int i;
+ os_lock_context_t irq_flags; /* for IRQ save/restore */
+ scc_return_t return_status = SCC_RET_TOO_MANY_FUNCTIONS;
+ int function_stored = FALSE;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Acquire lock of callbacks table. Could be spin_lock_irq() if this
+ * routine were just called from base (not interrupt) level
+ */
+ os_lock_save_context(scc_callbacks_lock, irq_flags);
+
+ /* Search through table looking for empty slot */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ if (scc_callbacks[i] == callback_func) {
+ if (function_stored) {
+ /* Saved duplicate earlier. Clear this later one. */
+ scc_callbacks[i] = NULL;
+ }
+ /* Exactly one copy is now stored */
+ return_status = SCC_RET_OK;
+ break;
+ } else if (scc_callbacks[i] == NULL && !function_stored) {
+ /* Found open slot. Save it and remember */
+ scc_callbacks[i] = callback_func;
+ return_status = SCC_RET_OK;
+ function_stored = TRUE;
+ }
+ }
+
+ /* Free the lock */
+ os_unlock_restore_context(scc_callbacks_lock, irq_flags);
+
+ return return_status;
+} /* scc_monitor_security_failure */
+
+/*****************************************************************************/
+/* fn scc_stop_monitoring_security_failure() */
+/*****************************************************************************/
+void scc_stop_monitoring_security_failure(void callback_func(void))
+{
+ os_lock_context_t irq_flags; /* for IRQ save/restore */
+ int i;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* Acquire lock of callbacks table. Could be spin_lock_irq() if this
+ * routine were just called from base (not interrupt) level
+ */
+ os_lock_save_context(scc_callbacks_lock, irq_flags);
+
+ /* Search every entry of the table for this function */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ if (scc_callbacks[i] == callback_func) {
+ scc_callbacks[i] = NULL; /* found instance - clear it out */
+ break;
+ }
+ }
+
+ /* Free the lock */
+ os_unlock_restore_context(scc_callbacks_lock, irq_flags);
+
+ return;
+} /* scc_stop_monitoring_security_failure */
+
+/*****************************************************************************/
+/* fn scc_read_register() */
+/*****************************************************************************/
+scc_return_t scc_read_register(int register_offset, uint32_t * value)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+ uint32_t smn_status;
+ uint32_t scm_status;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* First layer of protection -- completely unaccessible SCC */
+ if (scc_availability != SCC_STATUS_UNIMPLEMENTED) {
+
+ /* Second layer -- that offset is valid */
+ if (register_offset != SMN_BB_DEC_REG && /* write only! */
+ check_register_offset(register_offset) == SCC_RET_OK) {
+
+ /* Get current status / update local state */
+ smn_status = scc_update_state();
+ scm_status = SCC_READ_REGISTER(SCM_STATUS_REG);
+
+ /*
+ * Third layer - verify that the register being requested is
+ * available in the current state of the SCC.
+ */
+ if ((return_status =
+ check_register_accessible(register_offset,
+ smn_status,
+ scm_status)) ==
+ SCC_RET_OK) {
+ *value = SCC_READ_REGISTER(register_offset);
+ }
+ }
+ }
+
+ return return_status;
+} /* scc_read_register */
+
+/*****************************************************************************/
+/* fn scc_write_register() */
+/*****************************************************************************/
+scc_return_t scc_write_register(int register_offset, uint32_t value)
+{
+ scc_return_t return_status = SCC_RET_FAIL;
+ uint32_t smn_status;
+ uint32_t scm_status;
+
+ if (scc_availability == SCC_STATUS_INITIAL) {
+ scc_init();
+ }
+
+ /* First layer of protection -- completely unaccessible SCC */
+ if (scc_availability != SCC_STATUS_UNIMPLEMENTED) {
+
+ /* Second layer -- that offset is valid */
+ if (!((register_offset == SCM_STATUS_REG) || /* These registers are */
+ (register_offset == SCM_VERSION_REG) || /* Read Only */
+ (register_offset == SMN_BB_CNT_REG) ||
+ (register_offset == SMN_TIMER_REG)) &&
+ check_register_offset(register_offset) == SCC_RET_OK) {
+
+ /* Get current status / update local state */
+ smn_status = scc_update_state();
+ scm_status = SCC_READ_REGISTER(SCM_STATUS_REG);
+
+ /*
+ * Third layer - verify that the register being requested is
+ * available in the current state of the SCC.
+ */
+ if (check_register_accessible
+ (register_offset, smn_status, scm_status) == 0) {
+ SCC_WRITE_REGISTER(register_offset, value);
+ return_status = SCC_RET_OK;
+ }
+ }
+ }
+
+ return return_status;
+} /* scc_write_register() */
+
+/******************************************************************************
+ *
+ * Function Implementations - Internal
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn scc_irq() */
+/*****************************************************************************/
+/**
+ * This is the interrupt handler for the SCC.
+ *
+ * This function checks the SMN Status register to see whether it
+ * generated the interrupt, then it checks the SCM Status register to
+ * see whether it needs attention.
+ *
+ * If an SMN Interrupt is active, then the SCC state set to failure, and
+ * #scc_perform_callbacks() is invoked to notify any interested parties.
+ *
+ * The SCM Interrupt should be masked, as this driver uses polling to determine
+ * when the SCM has completed a crypto or zeroing operation. Therefore, if the
+ * interrupt is active, the driver will just clear the interrupt and (re)mask.
+ */
+OS_DEV_ISR(scc_irq)
+{
+ uint32_t smn_status;
+ uint32_t scm_status;
+ int handled = 0; /* assume interrupt isn't from SMN */
+#if defined(USE_SMN_INTERRUPT)
+ int smn_irq = INT_SCC_SMN; /* SMN interrupt is on a line by itself */
+#elif defined (NO_SMN_INTERRUPT)
+ int smn_irq = -1; /* not wired to CPU at all */
+#else
+ int smn_irq = INT_SCC_SCM; /* SMN interrupt shares a line with SCM */
+#endif
+
+ /* Update current state... This will perform callbacks... */
+ smn_status = scc_update_state();
+
+ /* SMN is on its own interrupt line. Verify the IRQ was triggered
+ * before clearing the interrupt and marking it handled. */
+ if ((os_dev_get_irq() == smn_irq) &&
+ (smn_status & SMN_STATUS_SMN_STATUS_IRQ)) {
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_CLEAR_INTERRUPT);
+ handled++; /* tell kernel that interrupt was handled */
+ }
+
+ /* Check on the health of the SCM */
+ scm_status = SCC_READ_REGISTER(SCM_STATUS_REG);
+
+ /* The driver masks interrupts, so this should never happen. */
+ if (os_dev_get_irq() == INT_SCC_SCM) {
+ /* but if it does, try to prevent it in the future */
+ SCC_WRITE_REGISTER(SCM_INT_CTL_REG, 0);
+ handled++;
+ }
+
+ /* Any non-zero value of handled lets kernel know we got something */
+ os_dev_isr_return(handled);
+}
+
+/*****************************************************************************/
+/* fn scc_perform_callbacks() */
+/*****************************************************************************/
+/** Perform callbacks registered by #scc_monitor_security_failure().
+ *
+ * Make sure callbacks only happen once... Since there may be some reason why
+ * the interrupt isn't generated, this routine could be called from base(task)
+ * level.
+ *
+ * One at a time, go through #scc_callbacks[] and call any non-null pointers.
+ */
+static void scc_perform_callbacks(void)
+{
+ static int callbacks_performed = 0;
+ unsigned long irq_flags; /* for IRQ save/restore */
+ int i;
+
+ /* Acquire lock of callbacks table and callbacks_performed flag */
+ os_lock_save_context(scc_callbacks_lock, irq_flags);
+
+ if (!callbacks_performed) {
+ callbacks_performed = 1;
+
+ /* Loop over all of the entries in the table */
+ for (i = 0; i < SCC_CALLBACK_SIZE; i++) {
+ /* If not null, ... */
+ if (scc_callbacks[i]) {
+ scc_callbacks[i] (); /* invoke the callback routine */
+ }
+ }
+ }
+
+ os_unlock_restore_context(scc_callbacks_lock, irq_flags);
+
+ return;
+}
+
+/*****************************************************************************/
+/* fn scc_update_state() */
+/*****************************************************************************/
+/**
+ * Make certain SCC is still running.
+ *
+ * Side effect is to update #scc_availability and, if the state goes to failed,
+ * run #scc_perform_callbacks().
+ *
+ * (If #SCC_BRINGUP is defined, bring SCC to secure state if it is found to be
+ * in health check state)
+ *
+ * @return Current value of #SMN_STATUS_REG register.
+ */
+static uint32_t scc_update_state(void)
+{
+ uint32_t smn_status_register = SMN_STATE_FAIL;
+ int smn_state;
+
+ /* if FAIL or UNIMPLEMENTED, don't bother */
+ if (scc_availability == SCC_STATUS_CHECKING ||
+ scc_availability == SCC_STATUS_OK) {
+
+ smn_status_register = SCC_READ_REGISTER(SMN_STATUS_REG);
+ smn_state = smn_status_register & SMN_STATUS_STATE_MASK;
+
+#ifdef SCC_BRINGUP
+ /* If in Health Check while booting, try to 'bringup' to Secure mode */
+ if (scc_availability == SCC_STATUS_CHECKING &&
+ smn_state == SMN_STATE_HEALTH_CHECK) {
+ /* Code up a simple algorithm for the ASC */
+ SCC_WRITE_REGISTER(SMN_SEQ_START_REG, 0xaaaa);
+ SCC_WRITE_REGISTER(SMN_SEQ_END_REG, 0x5555);
+ SCC_WRITE_REGISTER(SMN_SEQ_CHECK_REG, 0x5555);
+ /* State should be SECURE now */
+ smn_status_register = SCC_READ_REGISTER(SMN_STATUS);
+ smn_state = smn_status_register & SMN_STATUS_STATE_MASK;
+ }
+#endif
+
+ /*
+ * State should be SECURE or NON_SECURE for operation of the part. If
+ * FAIL, mark failed (i.e. limited access to registers). Any other
+ * state, mark unimplemented, as the SCC is unuseable.
+ */
+ if (smn_state == SMN_STATE_SECURE
+ || smn_state == SMN_STATE_NON_SECURE) {
+ /* Healthy */
+ scc_availability = SCC_STATUS_OK;
+ } else if (smn_state == SMN_STATE_FAIL) {
+ scc_availability = SCC_STATUS_FAILED; /* uh oh - unhealthy */
+ scc_perform_callbacks();
+ os_printk(KERN_ERR "SCC2: SCC went into FAILED mode\n");
+ } else {
+ /* START, ZEROIZE RAM, HEALTH CHECK, or unknown */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED; /* unuseable */
+ os_printk(KERN_ERR
+ "SCC2: SCC declared UNIMPLEMENTED\n");
+ }
+ }
+ /* if availability is initial or ok */
+ return smn_status_register;
+}
+
+/*****************************************************************************/
+/* fn scc_init_ccitt_crc() */
+/*****************************************************************************/
+/**
+ * Populate the partial CRC lookup table.
+ *
+ * @return none
+ *
+ */
+static void scc_init_ccitt_crc(void)
+{
+ int dividend; /* index for lookup table */
+ uint16_t remainder; /* partial value for a given dividend */
+ int bit; /* index into bits of a byte */
+
+ /*
+ * Compute the remainder of each possible dividend.
+ */
+ for (dividend = 0; dividend < 256; ++dividend) {
+ /*
+ * Start with the dividend followed by zeros.
+ */
+ remainder = dividend << (8);
+
+ /*
+ * Perform modulo-2 division, a bit at a time.
+ */
+ for (bit = 8; bit > 0; --bit) {
+ /*
+ * Try to divide the current data bit.
+ */
+ if (remainder & 0x8000) {
+ remainder = (remainder << 1) ^ CRC_POLYNOMIAL;
+ } else {
+ remainder = (remainder << 1);
+ }
+ }
+
+ /*
+ * Store the result into the table.
+ */
+ scc_crc_lookup_table[dividend] = remainder;
+ }
+
+} /* scc_init_ccitt_crc() */
+
+/*****************************************************************************/
+/* fn grab_config_values() */
+/*****************************************************************************/
+/**
+ * grab_config_values() will read the SCM Configuration and SMN Status
+ * registers and store away version and size information for later use.
+ *
+ * @return The current value of the SMN Status register.
+ */
+static uint32_t scc_grab_config_values(void)
+{
+ uint32_t scm_version_register;
+ uint32_t smn_status_register = SMN_STATE_FAIL;
+
+ if (scc_availability != SCC_STATUS_CHECKING) {
+ goto out;
+ }
+ scm_version_register = SCC_READ_REGISTER(SCM_VERSION_REG);
+ pr_debug("SCC2 Driver: SCM version is 0x%08x\n", scm_version_register);
+
+ /* Get SMN status and update scc_availability */
+ smn_status_register = scc_update_state();
+ pr_debug("SCC2 Driver: SMN status is 0x%08x\n", smn_status_register);
+
+ /* save sizes and versions information for later use */
+ scc_configuration.block_size_bytes = 16; /* BPCP ? */
+ scc_configuration.partition_count =
+ 1 + ((scm_version_register & SCM_VER_NP_MASK) >> SCM_VER_NP_SHIFT);
+ scc_configuration.partition_size_bytes =
+ 1 << ((scm_version_register & SCM_VER_BPP_MASK) >>
+ SCM_VER_BPP_SHIFT);
+ scc_configuration.scm_version =
+ (scm_version_register & SCM_VER_MAJ_MASK) >> SCM_VER_MAJ_SHIFT;
+ scc_configuration.smn_version =
+ (smn_status_register & SMN_STATUS_VERSION_ID_MASK)
+ >> SMN_STATUS_VERSION_ID_SHIFT;
+ if (scc_configuration.scm_version != SCM_MAJOR_VERSION_2) {
+ scc_availability = SCC_STATUS_UNIMPLEMENTED; /* Unknown version */
+ }
+
+ out:
+ return smn_status_register;
+} /* grab_config_values */
+
+/*****************************************************************************/
+/* fn setup_interrupt_handling() */
+/*****************************************************************************/
+/**
+ * Register the SCM and SMN interrupt handlers.
+ *
+ * Called from #scc_init()
+ *
+ * @return 0 on success
+ */
+static int setup_interrupt_handling(void)
+{
+ int smn_error_code = -1;
+ int scm_error_code = -1;
+
+ /* Disnable SCM interrupts */
+ SCC_WRITE_REGISTER(SCM_INT_CTL_REG, 0);
+
+#ifdef USE_SMN_INTERRUPT
+ /* Install interrupt service routine for SMN. */
+ smn_error_code = os_register_interrupt(SCC_DRIVER_NAME,
+ INT_SCC_SMN, scc_irq);
+ if (smn_error_code != 0) {
+ os_printk(KERN_ERR
+ "SCC2 Driver: Error installing SMN Interrupt Handler: %d\n",
+ smn_error_code);
+ } else {
+ smn_irq_set = 1; /* remember this for cleanup */
+ /* Enable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_CLEAR_INTERRUPT |
+ SMN_COMMAND_ENABLE_INTERRUPT);
+ }
+#else
+ smn_error_code = 0; /* no problems... will handle later */
+#endif
+
+ /*
+ * Install interrupt service routine for SCM (or both together).
+ */
+ scm_error_code = os_register_interrupt(SCC_DRIVER_NAME,
+ INT_SCC_SCM, scc_irq);
+ if (scm_error_code != 0) {
+#ifndef MXC
+ os_printk(KERN_ERR
+ "SCC2 Driver: Error installing SCM Interrupt Handler: %d\n",
+ scm_error_code);
+#else
+ os_printk(KERN_ERR
+ "SCC2 Driver: Error installing SCC Interrupt Handler: %d\n",
+ scm_error_code);
+#endif
+ } else {
+ scm_irq_set = 1; /* remember this for cleanup */
+#if defined(USE_SMN_INTERRUPT) && !defined(NO_SMN_INTERRUPT)
+ /* Enable SMN interrupts */
+ SCC_WRITE_REGISTER(SMN_COMMAND_REG,
+ SMN_COMMAND_CLEAR_INTERRUPT |
+ SMN_COMMAND_ENABLE_INTERRUPT);
+#endif
+ }
+
+ /* Return an error if one was encountered */
+ return scm_error_code ? scm_error_code : smn_error_code;
+} /* setup_interrupt_handling */
+
+/*****************************************************************************/
+/* fn scc_do_crypto() */
+/*****************************************************************************/
+/** Have the SCM perform the crypto function.
+ *
+ * Set up length register, and the store @c scm_control into control register
+ * to kick off the operation. Wait for completion, gather status, clear
+ * interrupt / status.
+ *
+ * @param byte_count number of bytes to perform in this operation
+ * @param scm_command Bit values to be set in @c SCM_CCMD_REG register
+ *
+ * @return 0 on success, value of #SCM_ERR_STATUS_REG on failure
+ */
+static uint32_t scc_do_crypto(int byte_count, uint32_t scm_command)
+{
+ int block_count = byte_count / SCC_BLOCK_SIZE_BYTES();
+ uint32_t crypto_status;
+ scc_return_t ret;
+
+ /* This seems to be necessary in order to allow subsequent cipher
+ * operations to succeed when a partition is deallocated/reallocated!
+ */
+ (void)SCC_READ_REGISTER(SCM_STATUS_REG);
+
+ /* In length register, 0 means 1, etc. */
+ scm_command |= (block_count - 1) << SCM_CCMD_LENGTH_SHIFT;
+
+ /* set modes and kick off the operation */
+ SCC_WRITE_REGISTER(SCM_CCMD_REG, scm_command);
+
+ ret = scc_wait_completion(&crypto_status);
+
+ /* Only done bit should be on */
+ if (crypto_status & SCM_STATUS_ERR) {
+ /* Replace with error status instead */
+ crypto_status = SCC_READ_REGISTER(SCM_ERR_STATUS_REG);
+ pr_debug("SCM Failure: 0x%x\n", crypto_status);
+ if (crypto_status == 0) {
+ /* That came up 0. Turn on arbitrary bit to signal error. */
+ crypto_status = SCM_ERRSTAT_ILM;
+ }
+ } else {
+ crypto_status = 0;
+ }
+ pr_debug("SCC2: Done waiting.\n");
+
+ return crypto_status;
+}
+
+/**
+ * Encrypt a region of secure memory.
+ *
+ * @param part_base Kernel virtual address of the partition.
+ * @param offset_bytes Offset from the start of the partition to the plaintext
+ * data.
+ * @param byte_count Length of the region (octets).
+ * @param black_data Physical location to store the encrypted data.
+ * @param IV Value to use for the IV.
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #scc_cypher_mode_t
+ *
+ * @return SCC_RET_OK if successful.
+ */
+scc_return_t
+scc_encrypt_region(uint32_t part_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t *black_data,
+ uint32_t *IV, scc_cypher_mode_t cypher_mode)
+{
+ os_lock_context_t irq_flags; /* for IRQ save/restore */
+ scc_return_t status = SCC_RET_OK;
+ uint32_t crypto_status;
+ uint32_t scm_command;
+ int offset_blocks = offset_bytes / SCC_BLOCK_SIZE_BYTES();
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_enable(SCC_CLK);
+#else
+ if (scc_clk != ERR_PTR(ENOENT))
+ clk_enable(scc_clk);
+#endif
+
+ scm_command = ((offset_blocks << SCM_CCMD_OFFSET_SHIFT) |
+ (SCM_PART_NUMBER(part_base) << SCM_CCMD_PART_SHIFT));
+
+ switch (cypher_mode) {
+ case SCC_CYPHER_MODE_CBC:
+ scm_command |= SCM_CCMD_AES_ENC_CBC;
+ break;
+ case SCC_CYPHER_MODE_ECB:
+ scm_command |= SCM_CCMD_AES_ENC_ECB;
+ break;
+ default:
+ status = SCC_RET_FAIL;
+ break;
+ }
+
+ pr_debug("Received encrypt request. SCM_C_BLACK_ST_REG: %p, "
+ "scm_Command: %08x, length: %i (part_base: %08x, "
+ "offset: %i)\n",
+ black_data, scm_command, byte_count, part_base, offset_blocks);
+
+ if (status != SCC_RET_OK)
+ goto out;
+
+ /* ACQUIRE LOCK to prevent others from using crypto or releasing slot */
+ os_lock_save_context(scc_crypto_lock, irq_flags);
+
+ if (status == SCC_RET_OK) {
+ SCC_WRITE_REGISTER(SCM_C_BLACK_ST_REG, (uint32_t) black_data);
+
+ /* Only write the IV if it will actually be used */
+ if (cypher_mode == SCC_CYPHER_MODE_CBC) {
+ /* Write the IV register */
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV0_REG, *(IV));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV1_REG, *(IV + 1));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV2_REG, *(IV + 2));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV3_REG, *(IV + 3));
+ }
+
+ /* Set modes and kick off the encryption */
+ crypto_status = scc_do_crypto(byte_count, scm_command);
+
+ if (crypto_status != 0) {
+ pr_debug("SCM encrypt red crypto failure: 0x%x\n",
+ crypto_status);
+ } else {
+ status = SCC_RET_OK;
+ pr_debug("SCC2: Encrypted %d bytes\n", byte_count);
+ }
+ }
+
+ os_unlock_restore_context(scc_crypto_lock, irq_flags);
+
+out:
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SCC_CLK);
+#else
+ if (scc_clk != ERR_PTR(ENOENT))
+ clk_disable(scc_clk);
+#endif
+
+ return status;
+}
+
+/* Decrypt a region into secure memory
+ *
+ * @param part_base Kernel virtual address of the partition.
+ * @param offset_bytes Offset from the start of the partition to store the
+ * plaintext data.
+ * @param byte_counts Length of the region (octets).
+ * @param black_data Physical location of the encrypted data.
+ * @param IV Value to use for the IV.
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #scc_cypher_mode_t
+ *
+ * @return SCC_RET_OK if successful.
+ */
+scc_return_t
+scc_decrypt_region(uint32_t part_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t *black_data,
+ uint32_t *IV, scc_cypher_mode_t cypher_mode)
+{
+ os_lock_context_t irq_flags; /* for IRQ save/restore */
+ scc_return_t status = SCC_RET_OK;
+ uint32_t crypto_status;
+ uint32_t scm_command;
+ int offset_blocks = offset_bytes / SCC_BLOCK_SIZE_BYTES();
+
+ /*Enabling SCC clock.*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_enable(SCC_CLK);
+#else
+ if (scc_clk != ERR_PTR(ENOENT))
+ clk_enable(scc_clk);
+#endif
+ scm_command = ((offset_blocks << SCM_CCMD_OFFSET_SHIFT) |
+ (SCM_PART_NUMBER(part_base) << SCM_CCMD_PART_SHIFT));
+
+ switch (cypher_mode) {
+ case SCC_CYPHER_MODE_CBC:
+ scm_command |= SCM_CCMD_AES_DEC_CBC;
+ break;
+ case SCC_CYPHER_MODE_ECB:
+ scm_command |= SCM_CCMD_AES_DEC_ECB;
+ break;
+ default:
+ status = SCC_RET_FAIL;
+ break;
+ }
+
+ pr_debug("Received decrypt request. SCM_C_BLACK_ST_REG: %p, "
+ "scm_Command: %08x, length: %i (part_base: %08x, "
+ "offset: %i)\n",
+ black_data, scm_command, byte_count, part_base, offset_blocks);
+
+ if (status != SCC_RET_OK)
+ goto out;
+
+ /* ACQUIRE LOCK to prevent others from using crypto or releasing slot */
+ os_lock_save_context(scc_crypto_lock, irq_flags);
+
+ if (status == SCC_RET_OK) {
+ status = SCC_RET_FAIL; /* reset expectations */
+ SCC_WRITE_REGISTER(SCM_C_BLACK_ST_REG, (uint32_t) black_data);
+
+ /* Write the IV register */
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV0_REG, *(IV));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV1_REG, *(IV + 1));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV2_REG, *(IV + 2));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV3_REG, *(IV + 3));
+
+ /* Set modes and kick off the decryption */
+ crypto_status = scc_do_crypto(byte_count, scm_command);
+
+ if (crypto_status != 0) {
+ pr_debug("SCM decrypt black crypto failure: 0x%x\n",
+ crypto_status);
+ } else {
+ status = SCC_RET_OK;
+ pr_debug("SCC2: Decrypted %d bytes\n", byte_count);
+ }
+ }
+
+ os_unlock_restore_context(scc_crypto_lock, irq_flags);
+out:
+ /*Disabling the Clock when the driver is not in use.*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18))
+ mxc_clks_disable(SCC_CLK);
+#else
+ if (scc_clk != ERR_PTR(ENOENT))
+ clk_disable(scc_clk);
+#endif
+ return status;
+}
+
+/*****************************************************************************/
+/* fn host_owns_partition() */
+/*****************************************************************************/
+/**
+ * Determine if the host owns a given partition.
+ *
+ * @internal
+ *
+ * @param part_no Partition number to query
+ *
+ * @return TRUE if the host owns the partition, FALSE otherwise.
+ */
+
+static uint32_t host_owns_partition(uint32_t part_no)
+{
+ uint32_t value;
+
+ if (part_no < scc_configuration.partition_count) {
+
+ /* Check the partition owners register */
+ value = SCC_READ_REGISTER(SCM_PART_OWNERS_REG);
+ if (((value >> (part_no * SCM_POWN_SHIFT)) & SCM_POWN_MASK)
+ == SCM_POWN_PART_OWNED)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*****************************************************************************/
+/* fn partition_engaged() */
+/*****************************************************************************/
+/**
+ * Determine if the given partition is engaged.
+ *
+ * @internal
+ *
+ * @param part_no Partition number to query
+ *
+ * @return TRUE if the partition is engaged, FALSE otherwise.
+ */
+
+static uint32_t partition_engaged(uint32_t part_no)
+{
+ uint32_t value;
+
+ if (part_no < scc_configuration.partition_count) {
+
+ /* Check the partition engaged register */
+ value = SCC_READ_REGISTER(SCM_PART_ENGAGED_REG);
+ if (((value >> (part_no * SCM_PENG_SHIFT)) & 0x1)
+ == SCM_PENG_ENGAGED)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*****************************************************************************/
+/* fn scc_wait_completion() */
+/*****************************************************************************/
+/**
+ * Poll looking for end-of-cipher indication. Only used
+ * if @c SCC_SCM_SLEEP is not defined.
+ *
+ * @internal
+ *
+ * On a Tahiti, crypto under 230 or so bytes is done after the first loop, all
+ * the way up to five sets of spins for 1024 bytes. (8- and 16-byte functions
+ * are done when we first look. Zeroizing takes one pass around.
+ *
+ * @param scm_status Address of the SCM_STATUS register
+ *
+ * @return A return code of type #scc_return_t
+ */
+static scc_return_t scc_wait_completion(uint32_t * scm_status)
+{
+ scc_return_t ret;
+ int done;
+ int i = 0;
+
+ /* check for completion by polling */
+ do {
+ done = is_cipher_done(scm_status);
+ if (done)
+ break;
+ /* TODO: shorten this delay */
+ udelay(1000);
+ } while (i++ < SCC_CIPHER_MAX_POLL_COUNT);
+
+ pr_debug("SCC2: Polled DONE %d times\n", i);
+ if (!done) {
+ ret = SCC_RET_FAIL;
+ }
+
+ return ret;
+} /* scc_wait_completion() */
+
+/*****************************************************************************/
+/* fn is_cipher_done() */
+/*****************************************************************************/
+/**
+ * This function returns non-zero if SCM Status register indicates
+ * that a cipher has terminated or some other interrupt-generating
+ * condition has occurred.
+ *
+ * @param scm_status Address of the SCM STATUS register
+ *
+ * @return 0 if cipher operations are finished
+ */
+static int is_cipher_done(uint32_t * scm_status)
+{
+ register unsigned status;
+ register int cipher_done;
+
+ *scm_status = SCC_READ_REGISTER(SCM_STATUS_REG);
+ status = (*scm_status & SCM_STATUS_SRS_MASK) >> SCM_STATUS_SRS_SHIFT;
+
+ /*
+ * Done when SCM is not in 'currently performing a function' states.
+ */
+ cipher_done = ((status != SCM_STATUS_SRS_ZBUSY)
+ && (status != SCM_STATUS_SRS_CBUSY)
+ && (status != SCM_STATUS_SRS_ABUSY));
+
+ return cipher_done;
+} /* is_cipher_done() */
+
+/*****************************************************************************/
+/* fn offset_within_smn() */
+/*****************************************************************************/
+/*!
+ * Check that the offset is with the bounds of the SMN register set.
+ *
+ * @param[in] register_offset register offset of SMN.
+ *
+ * @return 1 if true, 0 if false (not within SMN)
+ */
+static inline int offset_within_smn(uint32_t register_offset)
+{
+ return ((register_offset >= SMN_STATUS_REG)
+ && (register_offset <= SMN_HAC_REG));
+}
+
+/*****************************************************************************/
+/* fn offset_within_scm() */
+/*****************************************************************************/
+/*!
+ * Check that the offset is with the bounds of the SCM register set.
+ *
+ * @param[in] register_offset Register offset of SCM
+ *
+ * @return 1 if true, 0 if false (not within SCM)
+ */
+static inline int offset_within_scm(uint32_t register_offset)
+{
+ return 1; /* (register_offset >= SCM_RED_START)
+ && (register_offset < scm_highest_memory_address); */
+/* Although this would cause trouble for zeroize testing, this change would
+ * close a security hole which currently allows any kernel program to access
+ * any location in RED RAM. Perhaps enforce in non-SCC_DEBUG compiles?
+ && (register_offset <= SCM_INIT_VECTOR_1); */
+}
+
+/*****************************************************************************/
+/* fn check_register_accessible() */
+/*****************************************************************************/
+/**
+ * Given the current SCM and SMN status, verify that access to the requested
+ * register should be OK.
+ *
+ * @param[in] register_offset register offset within SCC
+ * @param[in] smn_status recent value from #SMN_STATUS_REG
+ * @param[in] scm_status recent value from #SCM_STATUS_REG
+ *
+ * @return #SCC_RET_OK if ok, #SCC_RET_FAIL if not
+ */
+static scc_return_t
+check_register_accessible(uint32_t register_offset, uint32_t smn_status,
+ uint32_t scm_status)
+{
+ int error_code = SCC_RET_FAIL;
+
+ /* Verify that the register offset passed in is not among the verboten set
+ * if the SMN is in Fail mode.
+ */
+ if (offset_within_smn(register_offset)) {
+ if ((smn_status & SMN_STATUS_STATE_MASK) == SMN_STATE_FAIL) {
+ if (!((register_offset == SMN_STATUS_REG) ||
+ (register_offset == SMN_COMMAND_REG) ||
+ (register_offset == SMN_SEC_VIO_REG))) {
+ pr_debug
+ ("SCC2 Driver: Note: Security State is in FAIL state.\n");
+ } /* register not a safe one */
+ else {
+ /* SMN is in FAIL, but register is a safe one */
+ error_code = SCC_RET_OK;
+ }
+ } /* State is FAIL */
+ else {
+ /* State is not fail. All registers accessible. */
+ error_code = SCC_RET_OK;
+ }
+ }
+ /* offset within SMN */
+ /* Not SCM register. Check for SCM busy. */
+ else if (offset_within_scm(register_offset)) {
+ /* This is the 'cannot access' condition in the SCM */
+ if (0 /* (scm_status & SCM_STATUS_BUSY) */
+ /* these are always available - rest fail on busy */
+ && !((register_offset == SCM_STATUS_REG) ||
+ (register_offset == SCM_ERR_STATUS_REG) ||
+ (register_offset == SCM_INT_CTL_REG) ||
+ (register_offset == SCM_VERSION_REG))) {
+ pr_debug
+ ("SCC2 Driver: Note: Secure Memory is in BUSY state.\n");
+ } /* status is busy & register inaccessible */
+ else {
+ error_code = SCC_RET_OK;
+ }
+ }
+ /* offset within SCM */
+ return error_code;
+
+} /* check_register_accessible() */
+
+/*****************************************************************************/
+/* fn check_register_offset() */
+/*****************************************************************************/
+/**
+ * Check that the offset is with the bounds of the SCC register set.
+ *
+ * @param[in] register_offset register offset of SMN.
+ *
+ * #SCC_RET_OK if ok, #SCC_RET_FAIL if not
+ */
+static scc_return_t check_register_offset(uint32_t register_offset)
+{
+ int return_value = SCC_RET_FAIL;
+
+ /* Is it valid word offset ? */
+ if (SCC_BYTE_OFFSET(register_offset) == 0) {
+ /* Yes. Is register within SCM? */
+ if (offset_within_scm(register_offset)) {
+ return_value = SCC_RET_OK; /* yes, all ok */
+ }
+ /* Not in SCM. Now look within the SMN */
+ else if (offset_within_smn(register_offset)) {
+ return_value = SCC_RET_OK; /* yes, all ok */
+ }
+ }
+
+ return return_value;
+}
+
+#ifdef SCC_REGISTER_DEBUG
+
+/**
+ * Names of the SCC Registers, indexed by register number
+ */
+static char *scc_regnames[] = {
+ "SCM_VERSION_REG",
+ "0x04",
+ "SCM_INT_CTL_REG",
+ "SCM_STATUS_REG",
+ "SCM_ERR_STATUS_REG",
+ "SCM_FAULT_ADR_REG",
+ "SCM_PART_OWNERS_REG",
+ "SCM_PART_ENGAGED_REG",
+ "SCM_UNIQUE_ID0_REG",
+ "SCM_UNIQUE_ID1_REG",
+ "SCM_UNIQUE_ID2_REG",
+ "SCM_UNIQUE_ID3_REG",
+ "0x30",
+ "0x34",
+ "0x38",
+ "0x3C",
+ "0x40",
+ "0x44",
+ "0x48",
+ "0x4C",
+ "SCM_ZCMD_REG",
+ "SCM_CCMD_REG",
+ "SCM_C_BLACK_ST_REG",
+ "SCM_DBG_STATUS_REG",
+ "SCM_AES_CBC_IV0_REG",
+ "SCM_AES_CBC_IV1_REG",
+ "SCM_AES_CBC_IV2_REG",
+ "SCM_AES_CBC_IV3_REG",
+ "0x70",
+ "0x74",
+ "0x78",
+ "0x7C",
+ "SCM_SMID0_REG",
+ "SCM_ACC0_REG",
+ "SCM_SMID1_REG",
+ "SCM_ACC1_REG",
+ "SCM_SMID2_REG",
+ "SCM_ACC2_REG",
+ "SCM_SMID3_REG",
+ "SCM_ACC3_REG",
+ "SCM_SMID4_REG",
+ "SCM_ACC4_REG",
+ "SCM_SMID5_REG",
+ "SCM_ACC5_REG",
+ "SCM_SMID6_REG",
+ "SCM_ACC6_REG",
+ "SCM_SMID7_REG",
+ "SCM_ACC7_REG",
+ "SCM_SMID8_REG",
+ "SCM_ACC8_REG",
+ "SCM_SMID9_REG",
+ "SCM_ACC9_REG",
+ "SCM_SMID10_REG",
+ "SCM_ACC10_REG",
+ "SCM_SMID11_REG",
+ "SCM_ACC11_REG",
+ "SCM_SMID12_REG",
+ "SCM_ACC12_REG",
+ "SCM_SMID13_REG",
+ "SCM_ACC13_REG",
+ "SCM_SMID14_REG",
+ "SCM_ACC14_REG",
+ "SCM_SMID15_REG",
+ "SCM_ACC15_REG",
+ "SMN_STATUS_REG",
+ "SMN_COMMAND_REG",
+ "SMN_SEQ_START_REG",
+ "SMN_SEQ_END_REG",
+ "SMN_SEQ_CHECK_REG",
+ "SMN_BB_CNT_REG",
+ "SMN_BB_INC_REG",
+ "SMN_BB_DEC_REG",
+ "SMN_COMPARE_REG",
+ "SMN_PT_CHK_REG",
+ "SMN_CT_CHK_REG",
+ "SMN_TIMER_IV_REG",
+ "SMN_TIMER_CTL_REG",
+ "SMN_SEC_VIO_REG",
+ "SMN_TIMER_REG",
+ "SMN_HAC_REG"
+};
+
+/**
+ * Names of the Secure RAM States
+ */
+static char *srs_names[] = {
+ "SRS_Reset",
+ "SRS_All_Ready",
+ "SRS_ZeroizeBusy",
+ "SRS_CipherBusy",
+ "SRS_AllBusy",
+ "SRS_ZeroizeDoneCipherReady",
+ "SRS_CipherDoneZeroizeReady",
+ "SRS_ZeroizeDoneCipherBusy",
+ "SRS_CipherDoneZeroizeBusy",
+ "SRS_UNKNOWN_STATE_9",
+ "SRS_TransitionalA",
+ "SRS_TransitionalB",
+ "SRS_TransitionalC",
+ "SRS_TransitionalD",
+ "SRS_AllDone",
+ "SRS_UNKNOWN_STATE_E",
+ "SRS_FAIL"
+};
+
+/**
+ * Create a text interpretation of the SCM Version Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_version_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ snprintf(print_buffer, buf_size,
+ "Bpp: %u, Bpcb: %u, np: %u, maj: %u, min: %u",
+ (value & SCM_VER_BPP_MASK) >> SCM_VER_BPP_SHIFT,
+ ((value & SCM_VER_BPCB_MASK) >> SCM_VER_BPCB_SHIFT) + 1,
+ ((value & SCM_VER_NP_MASK) >> SCM_VER_NP_SHIFT) + 1,
+ (value & SCM_VER_MAJ_MASK) >> SCM_VER_MAJ_SHIFT,
+ (value & SCM_VER_MIN_MASK) >> SCM_VER_MIN_SHIFT);
+
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of the SCM Status Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_status_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+
+ snprintf(print_buffer, buf_size, "%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (value & SCM_STATUS_KST_DEFAULT_KEY) ? "KST_DefaultKey " : "",
+ /* reserved */
+ (value & SCM_STATUS_KST_WRONG_KEY) ? "KST_WrongKey " : "",
+ (value & SCM_STATUS_KST_BAD_KEY) ? "KST_BadKey " : "",
+ (value & SCM_STATUS_ERR) ? "Error " : "",
+ (value & SCM_STATUS_MSS_FAIL) ? "MSS_FailState " : "",
+ (value & SCM_STATUS_MSS_SEC) ? "MSS_SecureState " : "",
+ (value & SCM_STATUS_RSS_FAIL) ? "RSS_FailState " : "",
+ (value & SCM_STATUS_RSS_SEC) ? "RSS_SecureState " : "",
+ (value & SCM_STATUS_RSS_INIT) ? "RSS_Initializing " : "",
+ (value & SCM_STATUS_UNV) ? "UID_Invalid " : "",
+ (value & SCM_STATUS_BIG) ? "BigEndian " : "",
+ (value & SCM_STATUS_USK) ? "SecretKey " : "",
+ srs_names[(value & SCM_STATUS_SRS_MASK) >>
+ SCM_STATUS_SRS_SHIFT]);
+
+ return print_buffer;
+}
+
+/**
+ * Names of the SCM Error Codes
+ */
+static
+char *scm_err_code[] = {
+ "Unknown_0",
+ "UnknownAddress",
+ "UnknownCommand",
+ "ReadPermErr",
+ "WritePermErr",
+ "DMAErr",
+ "EncBlockLenOvfl",
+ "KeyNotEngaged",
+ "ZeroizeCmdQOvfl",
+ "CipherCmdQOvfl",
+ "ProcessIntr",
+ "WrongKey",
+ "DeviceBusy",
+ "DMAUnalignedAddr",
+ "Unknown_E",
+ "Unknown_F",
+};
+
+/**
+ * Names of the SMN States
+ */
+static char *smn_state_name[] = {
+ "Start",
+ "Invalid_01",
+ "Invalid_02",
+ "Invalid_03",
+ "Zeroizing_04",
+ "Zeroizing",
+ "HealthCheck",
+ "HealthCheck_07",
+ "Invalid_08",
+ "Fail",
+ "Secure",
+ "Invalid_0B",
+ "NonSecure",
+ "Invalid_0D",
+ "Invalid_0E",
+ "Invalid_0F",
+ "Invalid_10",
+ "Invalid_11",
+ "Invalid_12",
+ "Invalid_13",
+ "Invalid_14",
+ "Invalid_15",
+ "Invalid_16",
+ "Invalid_17",
+ "Invalid_18",
+ "FailHard",
+ "Invalid_1A",
+ "Invalid_1B",
+ "Invalid_1C",
+ "Invalid_1D",
+ "Invalid_1E",
+ "Invalid_1F"
+};
+
+/**
+ * Create a text interpretation of the SCM Error Status Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_err_status_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ snprintf(print_buffer, buf_size,
+ "MID: 0x%x, %s%s ErrorCode: %s, SMSState: %s, SCMState: %s",
+ (value & SCM_ERRSTAT_MID_MASK) >> SCM_ERRSTAT_MID_SHIFT,
+ (value & SCM_ERRSTAT_ILM) ? "ILM, " : "",
+ (value & SCM_ERRSTAT_SUP) ? "SUP, " : "",
+ scm_err_code[(value & SCM_ERRSTAT_ERC_MASK) >>
+ SCM_ERRSTAT_ERC_SHIFT],
+ smn_state_name[(value & SCM_ERRSTAT_SMS_MASK) >>
+ SCM_ERRSTAT_SMS_SHIFT],
+ srs_names[(value & SCM_ERRSTAT_SRS_MASK) >>
+ SCM_ERRSTAT_SRS_SHIFT]);
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of the SCM Zeroize Command Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_zcmd_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ unsigned cmd = (value & SCM_ZCMD_CCMD_MASK) >> SCM_CCMD_CCMD_SHIFT;
+
+ snprintf(print_buffer, buf_size, "%s %u",
+ (cmd ==
+ ZCMD_DEALLOC_PART) ? "DeallocPartition" :
+ "(unknown function)",
+ (value & SCM_ZCMD_PART_MASK) >> SCM_ZCMD_PART_SHIFT);
+
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of the SCM Cipher Command Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_ccmd_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ unsigned cmd = (value & SCM_CCMD_CCMD_MASK) >> SCM_CCMD_CCMD_SHIFT;
+
+ snprintf(print_buffer, buf_size,
+ "%s %u bytes, %s offset 0x%x, in partition %u",
+ (cmd == SCM_CCMD_AES_DEC_ECB) ? "ECB Decrypt" : (cmd ==
+ SCM_CCMD_AES_ENC_ECB)
+ ? "ECB Encrypt" : (cmd ==
+ SCM_CCMD_AES_DEC_CBC) ? "CBC Decrypt" : (cmd
+ ==
+ SCM_CCMD_AES_ENC_CBC)
+ ? "CBC Encrypt" : "(unknown function)",
+ 16 +
+ 16 * ((value & SCM_CCMD_LENGTH_MASK) >> SCM_CCMD_LENGTH_SHIFT),
+ ((cmd == SCM_CCMD_AES_ENC_CBC)
+ || (cmd == SCM_CCMD_AES_ENC_ECB)) ? "at" : "to",
+ 16 * ((value & SCM_CCMD_OFFSET_MASK) >> SCM_CCMD_OFFSET_SHIFT),
+ (value & SCM_CCMD_PART_MASK) >> SCM_CCMD_PART_SHIFT);
+
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of an SCM Access Permissions Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_acc_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ snprintf(print_buffer, buf_size, "%s%s%s%s%s%s%s%s%s%s",
+ (value & SCM_PERM_NO_ZEROIZE) ? "NO_ZERO " : "",
+ (value & SCM_PERM_HD_SUP_DISABLE) ? "SUP_DIS " : "",
+ (value & SCM_PERM_HD_READ) ? "HD_RD " : "",
+ (value & SCM_PERM_HD_WRITE) ? "HD_WR " : "",
+ (value & SCM_PERM_HD_EXECUTE) ? "HD_EX " : "",
+ (value & SCM_PERM_TH_READ) ? "TH_RD " : "",
+ (value & SCM_PERM_TH_WRITE) ? "TH_WR " : "",
+ (value & SCM_PERM_OT_READ) ? "OT_RD " : "",
+ (value & SCM_PERM_OT_WRITE) ? "OT_WR " : "",
+ (value & SCM_PERM_OT_EXECUTE) ? "OT_EX" : "");
+
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of the SCM Partitions Engaged Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *scm_print_part_eng_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ snprintf(print_buffer, buf_size, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (value & 0x8000) ? "15 " : "",
+ (value & 0x4000) ? "14 " : "",
+ (value & 0x2000) ? "13 " : "",
+ (value & 0x1000) ? "12 " : "",
+ (value & 0x0800) ? "11 " : "",
+ (value & 0x0400) ? "10 " : "",
+ (value & 0x0200) ? "9 " : "",
+ (value & 0x0100) ? "8 " : "",
+ (value & 0x0080) ? "7 " : "",
+ (value & 0x0040) ? "6 " : "",
+ (value & 0x0020) ? "5 " : "",
+ (value & 0x0010) ? "4 " : "",
+ (value & 0x0008) ? "3 " : "",
+ (value & 0x0004) ? "2 " : "",
+ (value & 0x0002) ? "1 " : "", (value & 0x0001) ? "0" : "");
+
+ return print_buffer;
+}
+
+/**
+ * Create a text interpretation of the SMN Status Register
+ *
+ * @param value The value of the register
+ * @param[out] print_buffer Place to store the interpretation
+ * @param buf_size Number of bytes available at print_buffer
+ *
+ * @return The print_buffer
+ */
+static
+char *smn_print_status_reg(uint32_t value, char *print_buffer, int buf_size)
+{
+ snprintf(print_buffer, buf_size,
+ "Version %d %s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (value & SMN_STATUS_VERSION_ID_MASK) >>
+ SMN_STATUS_VERSION_ID_SHIFT,
+ (value & SMN_STATUS_ILLEGAL_MASTER) ? "IllMaster " : "",
+ (value & SMN_STATUS_SCAN_EXIT) ? "ScanExit " : "",
+ (value & SMN_STATUS_PERIP_INIT) ? "PeripInit " : "",
+ (value & SMN_STATUS_SMN_ERROR) ? "SMNError " : "",
+ (value & SMN_STATUS_SOFTWARE_ALARM) ? "SWAlarm " : "",
+ (value & SMN_STATUS_TIMER_ERROR) ? "TimerErr " : "",
+ (value & SMN_STATUS_PC_ERROR) ? "PTCTErr " : "",
+ (value & SMN_STATUS_BITBANK_ERROR) ? "BitbankErr " : "",
+ (value & SMN_STATUS_ASC_ERROR) ? "ASCErr " : "",
+ (value & SMN_STATUS_SECURITY_POLICY_ERROR) ? "SecPlcyErr " :
+ "",
+ (value & SMN_STATUS_SEC_VIO_ACTIVE_ERROR) ? "SecVioAct " : "",
+ (value & SMN_STATUS_INTERNAL_BOOT) ? "IntBoot " : "",
+ smn_state_name[(value & SMN_STATUS_STATE_MASK) >>
+ SMN_STATUS_STATE_SHIFT]);
+
+ return print_buffer;
+}
+
+/**
+ * The array, indexed by register number (byte-offset / 4), of print routines
+ * for the SCC (SCM and SMN) registers.
+ */
+static reg_print_routine_t reg_printers[] = {
+ scm_print_version_reg,
+ NULL, /* 0x04 */
+ NULL, /* SCM_INT_CTL_REG */
+ scm_print_status_reg,
+ scm_print_err_status_reg,
+ NULL, /* SCM_FAULT_ADR_REG */
+ NULL, /* SCM_PART_OWNERS_REG */
+ scm_print_part_eng_reg,
+ NULL, /* SCM_UNIQUE_ID0_REG */
+ NULL, /* SCM_UNIQUE_ID1_REG */
+ NULL, /* SCM_UNIQUE_ID2_REG */
+ NULL, /* SCM_UNIQUE_ID3_REG */
+ NULL, /* 0x30 */
+ NULL, /* 0x34 */
+ NULL, /* 0x38 */
+ NULL, /* 0x3C */
+ NULL, /* 0x40 */
+ NULL, /* 0x44 */
+ NULL, /* 0x48 */
+ NULL, /* 0x4C */
+ scm_print_zcmd_reg,
+ scm_print_ccmd_reg,
+ NULL, /* SCM_C_BLACK_ST_REG */
+ NULL, /* SCM_DBG_STATUS_REG */
+ NULL, /* SCM_AES_CBC_IV0_REG */
+ NULL, /* SCM_AES_CBC_IV1_REG */
+ NULL, /* SCM_AES_CBC_IV2_REG */
+ NULL, /* SCM_AES_CBC_IV3_REG */
+ NULL, /* 0x70 */
+ NULL, /* 0x74 */
+ NULL, /* 0x78 */
+ NULL, /* 0x7C */
+ NULL, /* SCM_SMID0_REG */
+ scm_print_acc_reg, /* ACC0 */
+ NULL, /* SCM_SMID1_REG */
+ scm_print_acc_reg, /* ACC1 */
+ NULL, /* SCM_SMID2_REG */
+ scm_print_acc_reg, /* ACC2 */
+ NULL, /* SCM_SMID3_REG */
+ scm_print_acc_reg, /* ACC3 */
+ NULL, /* SCM_SMID4_REG */
+ scm_print_acc_reg, /* ACC4 */
+ NULL, /* SCM_SMID5_REG */
+ scm_print_acc_reg, /* ACC5 */
+ NULL, /* SCM_SMID6_REG */
+ scm_print_acc_reg, /* ACC6 */
+ NULL, /* SCM_SMID7_REG */
+ scm_print_acc_reg, /* ACC7 */
+ NULL, /* SCM_SMID8_REG */
+ scm_print_acc_reg, /* ACC8 */
+ NULL, /* SCM_SMID9_REG */
+ scm_print_acc_reg, /* ACC9 */
+ NULL, /* SCM_SMID10_REG */
+ scm_print_acc_reg, /* ACC10 */
+ NULL, /* SCM_SMID11_REG */
+ scm_print_acc_reg, /* ACC11 */
+ NULL, /* SCM_SMID12_REG */
+ scm_print_acc_reg, /* ACC12 */
+ NULL, /* SCM_SMID13_REG */
+ scm_print_acc_reg, /* ACC13 */
+ NULL, /* SCM_SMID14_REG */
+ scm_print_acc_reg, /* ACC14 */
+ NULL, /* SCM_SMID15_REG */
+ scm_print_acc_reg, /* ACC15 */
+ smn_print_status_reg,
+ NULL, /* SMN_COMMAND_REG */
+ NULL, /* SMN_SEQ_START_REG */
+ NULL, /* SMN_SEQ_END_REG */
+ NULL, /* SMN_SEQ_CHECK_REG */
+ NULL, /* SMN_BB_CNT_REG */
+ NULL, /* SMN_BB_INC_REG */
+ NULL, /* SMN_BB_DEC_REG */
+ NULL, /* SMN_COMPARE_REG */
+ NULL, /* SMN_PT_CHK_REG */
+ NULL, /* SMN_CT_CHK_REG */
+ NULL, /* SMN_TIMER_IV_REG */
+ NULL, /* SMN_TIMER_CTL_REG */
+ NULL, /* SMN_SEC_VIO_REG */
+ NULL, /* SMN_TIMER_REG */
+ NULL, /* SMN_HAC_REG */
+};
+
+/*****************************************************************************/
+/* fn dbg_scc_read_register() */
+/*****************************************************************************/
+/**
+ * Noisily read a 32-bit value to an SCC register.
+ * @param offset The address of the register to read.
+ *
+ * @return The register value
+ * */
+uint32_t dbg_scc_read_register(uint32_t offset)
+{
+ uint32_t value;
+ char *regname = scc_regnames[offset / 4];
+
+ value = __raw_readl(scc_base + offset);
+ pr_debug("SCC2 RD: 0x%03x : 0x%08x (%s) %s\n", offset, value, regname,
+ reg_printers[offset / 4]
+ ? reg_printers[offset / 4] (value, reg_print_buffer,
+ REG_PRINT_BUFFER_SIZE)
+ : "");
+
+ return value;
+}
+
+/*****************************************************************************/
+/* fn dbg_scc_write_register() */
+/*****************************************************************************/
+/*
+ * Noisily read a 32-bit value to an SCC register.
+ * @param offset The address of the register to written.
+ *
+ * @param value The new register value
+ */
+void dbg_scc_write_register(uint32_t offset, uint32_t value)
+{
+ char *regname = scc_regnames[offset / 4];
+
+ pr_debug("SCC2 WR: 0x%03x : 0x%08x (%s) %s\n", offset, value, regname,
+ reg_printers[offset / 4]
+ ? reg_printers[offset / 4] (value, reg_print_buffer,
+ REG_PRINT_BUFFER_SIZE)
+ : "");
+ (void)__raw_writel(value, scc_base + offset);
+
+}
+
+#endif /* SCC_REGISTER_DEBUG */
diff --git a/drivers/mxc/security/scc2_internals.h b/drivers/mxc/security/scc2_internals.h
new file mode 100644
index 000000000000..d91ac45f62bd
--- /dev/null
+++ b/drivers/mxc/security/scc2_internals.h
@@ -0,0 +1,527 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 SCC_INTERNALS_H
+#define SCC_INTERNALS_H
+
+/** @file scc2_internals.h
+ *
+ * @brief This is intended to be the file which contains most or all of the
+ * code or changes need to port the driver. It also includes other definitions
+ * needed by the driver.
+ *
+ * This header file should only ever be included by scc2_driver.c
+ *
+ * Compile-time flags minimally needed:
+ *
+ * @li Some sort of platform flag. Currently TAHITI and MXC are understood.
+ * @li Some start-of-SCC consideration, such as SCC_BASE_ADDR
+ *
+ * Some changes which could be made when porting this driver:
+ * #SCC_SPIN_COUNT
+ *
+ */
+
+#include <linux/version.h> /* Current version Linux kernel */
+#include <linux/module.h> /* Basic support for loadable modules,
+ printk */
+#include <linux/init.h> /* module_init, module_exit */
+#include <linux/kernel.h> /* General kernel system calls */
+#include <linux/sched.h> /* for interrupt.h */
+#include <linux/spinlock.h>
+
+#include <linux/io.h> /* ioremap() */
+#include <linux/interrupt.h> /* IRQ / interrupt definitions */
+
+
+#include <linux/mxc_scc2_driver.h>
+
+#if defined(MXC)
+
+#include <mach/iim.h>
+#include <mach/mxc_scc.h>
+
+
+/**
+ * This macro is used to determine whether the SCC is enabled/available
+ * on the platform. This macro may need to be ported.
+ */
+#define SCC_FUSE IO_ADDRESS(IIM_BASE_ADDR + MXC_IIMHWV1)
+#define SCC_ENABLED() ((SCC_FUSE & MXC_IIMHWV1_SCC_DISABLE) == 0)
+
+#else /* neither TAHITI nor MXC */
+
+#error Do not understand target architecture
+
+#endif /* TAHITI */
+/**
+ * Define the number of Stored Keys which the SCC driver will make available.
+ * Value shall be from 0 to 20. Default is zero (0).
+ */
+/*#define SCC_KEY_SLOTS 20*/
+
+
+/* Temporarily define compile-time flags to make Doxygen happy. */
+#ifdef DOXYGEN_HACK
+/** @addtogroup scccompileflags */
+/** @{ */
+
+
+/** @def NO_SMN_INTERRUPT
+ * The SMN interrupt is not wired to the CPU at all.
+ */
+#define NO_SMN_INTERRUPT
+
+
+/**
+ * Register an interrupt handler for the SMN as well as
+ * the SCM. In some implementations, the SMN is not connected at all (see
+ * #NO_SMN_INTERRUPT), and in others, it is on the same interrupt line as the
+ * SCM. When defining this flag, the SMN interrupt should be on a separate
+ * line from the SCM interrupt.
+ */
+
+#define USE_SMN_INTERRUPT
+
+
+/**
+ * Turn on generation of run-time operational, debug, and error messages
+ */
+#define SCC_DEBUG
+
+
+/**
+ * Turn on generation of run-time logging of access to the SCM and SMN
+ * registers.
+ */
+#define SCC_REGISTER_DEBUG
+
+
+/**
+ * Turn on generation of run-time logging of access to the SCM Red and
+ * Black memories. Will only work if #SCC_REGISTER_DEBUG is also defined.
+ */
+#define SCC_RAM_DEBUG
+
+
+/**
+ * If the driver finds the SCC in HEALTH_CHECK state, go ahead and
+ * run a quick ASC to bring it to SECURE state.
+ */
+#define SCC_BRINGUP
+
+
+/**
+ * Expected to come from platform header files or compile command line.
+ * This symbol must be the address of the SCC
+ */
+#define SCC_BASE
+
+/**
+ * This must be the interrupt line number of the SCM interrupt.
+ */
+#define INT_SCM
+
+/**
+ * if #USE_SMN_INTERRUPT is defined, this must be the interrupt line number of
+ * the SMN interrupt.
+ */
+#define INT_SMN
+
+/**
+ * Define the number of Stored Keys which the SCC driver will make available.
+ * Value shall be from 0 to 20. Default is zero (0).
+ */
+#define SCC_KEY_SLOTS
+
+/**
+ * Make sure that this flag is defined if compiling for a Little-Endian
+ * platform. Linux Kernel builds provide this flag.
+ */
+#define __LITTLE_ENDIAN
+
+/**
+ * Make sure that this flag is defined if compiling for a Big-Endian platform.
+ * Linux Kernel builds provide this flag.
+ */
+#define __BIG_ENDIAN
+
+/**
+ * Read a 32-bit register value from a 'peripheral'. Standard Linux/Unix
+ * macro.
+ *
+ * @param offset Bus address of register to be read
+ *
+ * @return The value of the register
+ */
+#define readl(offset)
+
+
+/**
+ * Write a 32-bit value to a register in a 'peripheral'. Standard Linux/Unix
+ * macro.
+ *
+ * @param value The 32-bit value to store
+ * @param offset Bus address of register to be written
+ *
+ * return (none)
+ */
+#define writel(value,offset)
+
+
+/** @} */ /* end group scccompileflags */
+
+#endif /* DOXYGEN_HACK */
+
+
+#ifndef SCC_KEY_SLOTS
+#define SCC_KEY_SLOTS 0
+
+#else
+
+#if (SCC_KEY_SLOTS < 0) || (SCC_KEY_SLOTS > 20)
+#error Bad value for SCC_KEY_SLOTS
+#endif
+
+#endif
+
+
+/**
+ * Maximum length of key/secret value which can be stored in SCC.
+ */
+#define SCC_MAX_KEY_SIZE 256
+
+
+/**
+ * This is the size, in bytes, of each key slot, and therefore the maximum size
+ * of the wrapped key.
+ */
+#define SCC_KEY_SLOT_SIZE 32
+
+
+/* These come for free with Linux, but may need to be set in a port. */
+#ifndef __BIG_ENDIAN
+#ifndef __LITTLE_ENDIAN
+#error One of __LITTLE_ENDIAN or __BIG_ENDIAN must be #defined
+#endif
+#else
+#ifdef __LITTLE_ENDIAN
+#error Exactly one of __LITTLE_ENDIAN or __BIG_ENDIAN must be #defined
+#endif
+#endif
+
+
+#ifndef SCC_CALLBACK_SIZE
+/** The number of function pointers which can be stored in #scc_callbacks.
+ * Defaults to 4, can be overridden with compile-line argument.
+ */
+#define SCC_CALLBACK_SIZE 4
+#endif
+
+
+/** Initial CRC value for CCITT-CRC calculation. */
+#define CRC_CCITT_START 0xFFFF
+
+
+#ifdef TAHITI
+
+/**
+ * The SCC_BASE has to be SMN_BASE_ADDR on TAHITI, as the banks of
+ * registers are swapped in place.
+ */
+#define SCC_BASE SMN_BASE_ADDR
+
+
+/** The interrupt number for the SCC (SCM only!) on Tahiti */
+#define INT_SCC_SCM 62
+
+
+/** Tahiti does not have the SMN interrupt wired to the CPU. */
+#define NO_SMN_INTERRUPT
+
+
+#endif /* TAHITI */
+
+
+/** Number of times to spin between polling of SCC while waiting for cipher
+ * or zeroizing function to complete. See also #SCC_CIPHER_MAX_POLL_COUNT. */
+#define SCC_SPIN_COUNT 1000
+
+
+/** Number of times to polling SCC while waiting for cipher
+ * or zeroizing function to complete. See also #SCC_SPIN_COUNT. */
+#define SCC_CIPHER_MAX_POLL_COUNT 100
+
+
+/**
+ * @def SCC_READ_REGISTER
+ * Read a 32-bit value from an SCC register. Macro which depends upon
+ * #scc_base. Linux readl()/writel() macros operate on 32-bit quantities, as
+ * do SCC register reads/writes.
+ *
+ * @param offset Register offset within SCC.
+ *
+ * @return The value from the SCC's register.
+ */
+#ifndef SCC_REGISTER_DEBUG
+#define SCC_READ_REGISTER(offset) __raw_readl(scc_base+(offset))
+#else
+#define SCC_READ_REGISTER(offset) dbg_scc_read_register(offset)
+#endif
+
+
+/**
+ * Write a 32-bit value to an SCC register. Macro depends upon #scc_base.
+ * Linux readl()/writel() macros operate on 32-bit quantities, as do SCC
+ * register reads/writes.
+ *
+ * @param offset Register offset within SCC.
+ * @param value 32-bit value to store into the register
+ *
+ * @return (void)
+ */
+#ifndef SCC_REGISTER_DEBUG
+#define SCC_WRITE_REGISTER(offset,value) \
+ (void)__raw_writel(value, scc_base+(offset))
+#else
+#define SCC_WRITE_REGISTER(offset,value) \
+ dbg_scc_write_register(offset, value)
+#endif
+
+/**
+ * Calculate the physical address of a partition from the partition number.
+ */
+#define SCM_PART_PHYS_ADDRESS(part) \
+ ((uint32_t)scm_ram_phys_base + (part*scc_configuration.partition_size_bytes))
+
+/**
+ * Calculate the kernel virtual address of a partition from the partition number.
+ */
+#define SCM_PART_ADDRESS(part) \
+ (scm_ram_base + (part*scc_configuration.partition_size_bytes))
+
+/**
+ * Calculate the partition number from the kernel virtual address.
+ */
+#define SCM_PART_NUMBER(address) \
+ ((address - (uint32_t)scm_ram_base)/scc_configuration.partition_size_bytes)
+
+/**
+ * Calculates the byte offset into a word
+ * @param bp The byte (char*) pointer
+ * @return The offset (0, 1, 2, or 3)
+ */
+#define SCC_BYTE_OFFSET(bp) ((uint32_t)(bp) % sizeof(uint32_t))
+
+
+/**
+ * Converts (by rounding down) a byte pointer into a word pointer
+ * @param bp The byte (char*) pointer
+ * @return The word (uint32_t) as though it were an aligned (uint32_t*)
+ */
+#define SCC_WORD_PTR(bp) (((uint32_t)(bp)) & ~(sizeof(uint32_t)-1))
+
+
+/**
+ * Determine number of bytes in an SCC block
+ *
+ * @return Bytes / block
+ */
+#define SCC_BLOCK_SIZE_BYTES() scc_configuration.block_size_bytes
+
+
+/**
+ * Maximum number of additional bytes which may be added in CRC+padding mode.
+ */
+#define PADDING_BUFFER_MAX_BYTES (CRC_SIZE_BYTES + sizeof(scc_block_padding))
+
+/**
+ * Shorthand (clearer, anyway) for number of bytes in a CRC.
+ */
+#define CRC_SIZE_BYTES (sizeof(crc_t))
+
+/**
+ * The polynomial used in CCITT-CRC calculation
+ */
+#define CRC_POLYNOMIAL 0x1021
+
+/**
+ * Calculate CRC on one byte of data
+ *
+ * @param[in,out] running_crc A value of type crc_t where CRC is kept. This
+ * must be an rvalue and an lvalue.
+ * @param[in] byte_value The byte (uint8_t, char) to be put in the CRC
+ *
+ * @return none
+ */
+#define CALC_CRC(byte_value,running_crc) { \
+ uint8_t data; \
+ data = (0xff&(byte_value)) ^ (running_crc >> 8); \
+ running_crc = scc_crc_lookup_table[data] ^ (running_crc << 8); \
+}
+
+/** Value of 'beginning of padding' marker in driver-provided padding */
+#define SCC_DRIVER_PAD_CHAR 0x80
+
+
+/** Name of the driver. Used (on Linux, anyway) when registering interrupts */
+#define SCC_DRIVER_NAME "scc"
+
+
+/* Port -- these symbols are defined in Linux 2.6 and later. They are defined
+ * here for backwards compatibility because this started life as a 2.4
+ * driver, and as a guide to portation to other platforms.
+ */
+
+#if !defined(LINUX_VERSION_CODE) || LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+
+#define irqreturn_t void /* Return type of an interrupt handler */
+
+#define IRQ_HANDLED /* Would be '1' for handled -- as in return IRQ_HANDLED; */
+
+#define IRQ_NONE /* would be '0' for not handled -- as in return IRQ_NONE; */
+
+#define IRQ_RETVAL(x) /* Return x==0 (not handled) or non-zero (handled) */
+
+#endif /* LINUX earlier than 2.5 */
+
+
+/* These are nice to have around */
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+
+/** Provide a typedef for the CRC which can be used in encrypt/decrypt */
+typedef uint16_t crc_t;
+
+
+/** Gives high-level view of state of the SCC */
+enum scc_status {
+ SCC_STATUS_INITIAL, /**< State of driver before ever checking */
+ SCC_STATUS_CHECKING, /**< Transient state while driver loading */
+ SCC_STATUS_UNIMPLEMENTED, /**< SCC is non-existent or unuseable */
+ SCC_STATUS_OK, /**< SCC is in Secure or Default state */
+ SCC_STATUS_FAILED /**< In Failed state */
+};
+
+/**
+ * Information about a key slot.
+ */
+struct scc_key_slot
+{
+ uint64_t owner_id; /**< Access control value. */
+ uint32_t length; /**< Length of value in slot. */
+ uint32_t offset; /**< Offset of value from start of RAM. */
+ uint32_t status; /**< 0 = unassigned, 1 = assigned. */
+ uint32_t part_ctl; /**< for the CCMD register */
+};
+
+/* Forward-declare a number routines which are not part of user api */
+static int scc_init(void);
+static void scc_cleanup(void);
+
+/* Forward defines of internal functions */
+OS_DEV_ISR(scc_irq);
+/*static irqreturn_t scc_irq(int irq, void *dev_id);*/
+/** Perform callbacks registered by #scc_monitor_security_failure().
+ *
+ * Make sure callbacks only happen once... Since there may be some reason why
+ * the interrupt isn't generated, this routine could be called from base(task)
+ * level.
+ *
+ * One at a time, go through #scc_callbacks[] and call any non-null pointers.
+ */
+static void scc_perform_callbacks(void);
+/*static uint32_t copy_to_scc(const uint8_t* from, uint32_t to, unsigned long count_bytes, uint16_t* crc);
+static uint32_t copy_from_scc(const uint32_t from, uint8_t* to,unsigned long count_bytes, uint16_t* crc);
+static scc_return_t scc_strip_padding(uint8_t* from,unsigned* count_bytes_stripped);*/
+static uint32_t scc_update_state(void);
+static void scc_init_ccitt_crc(void);
+static uint32_t scc_grab_config_values(void);
+static int setup_interrupt_handling(void);
+/**
+ * Perform an encryption on the input. If @c verify_crc is true, a CRC must be
+ * calculated on the plaintext, and appended, with padding, before computing
+ * the ciphertext.
+ *
+ * @param[in] count_in_bytes Count of bytes of plaintext
+ * @param[in] data_in Pointer to the plaintext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing ciphertext
+ * @param[in] add_crc Flag for computing CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+ */
+/*static scc_return_t scc_encrypt(uint32_t count_in_bytes, uint8_t* data_in, uint32_t scm_control, uint8_t* data_out,int add_crc, unsigned long* count_out_bytes);*/
+/**
+ * Perform a decryption on the input. If @c verify_crc is true, the last block
+ * (maybe the two last blocks) is special - it should contain a CRC and
+ * padding. These must be stripped and verified.
+ *
+ * @param[in] count_in_bytes Count of bytes of ciphertext
+ * @param[in] data_in Pointer to the ciphertext
+ * @param[in] scm_control Bit values for the SCM_CONTROL register
+ * @param[in,out] data_out Pointer for storing plaintext
+ * @param[in] verify_crc Flag for running CRC - 0 no, else yes
+ * @param[in,out] count_out_bytes Number of bytes available at @c data_out
+
+ */
+/*static scc_return_t scc_decrypt(uint32_t count_in_bytes, uint8_t* data_in, uint32_t scm_control, uint8_t* data_out, int verify_crc, unsigned long* count_out_bytes);*/
+static uint32_t host_owns_partition(uint32_t part_no);
+static uint32_t partition_engaged(uint32_t part_no);
+
+static scc_return_t scc_wait_completion(uint32_t* scm_status);
+static int is_cipher_done(uint32_t* scm_status);
+static scc_return_t check_register_accessible (uint32_t offset,
+ uint32_t smn_status,
+ uint32_t scm_status);
+static scc_return_t check_register_offset(uint32_t offset);
+/*uint8_t make_vpu_partition(void);*/
+
+#ifdef SCC_REGISTER_DEBUG
+static uint32_t dbg_scc_read_register(uint32_t offset);
+static void dbg_scc_write_register(uint32_t offset, uint32_t value);
+#endif
+
+
+/* For Linux kernel, export the API functions to other kernel modules */
+EXPORT_SYMBOL(scc_get_configuration);
+EXPORT_SYMBOL(scc_zeroize_memories);
+/*EXPORT_SYMBOL(scc_crypt);*/
+EXPORT_SYMBOL(scc_set_sw_alarm);
+EXPORT_SYMBOL(scc_monitor_security_failure);
+EXPORT_SYMBOL(scc_stop_monitoring_security_failure);
+EXPORT_SYMBOL(scc_read_register);
+EXPORT_SYMBOL(scc_write_register);
+EXPORT_SYMBOL(scc_allocate_partition);
+EXPORT_SYMBOL(scc_engage_partition);
+EXPORT_SYMBOL(scc_release_partition);
+EXPORT_SYMBOL(scc_diminish_permissions);
+EXPORT_SYMBOL(scc_encrypt_region);
+EXPORT_SYMBOL(scc_decrypt_region);
+/*EXPORT_SYMBOL(make_vpu_partition);*/
+/* Tell Linux where to invoke driver at boot/module load time */
+module_init(scc_init);
+/* Tell Linux where to invoke driver on module unload */
+module_exit(scc_cleanup);
+
+
+/* Tell Linux this is not GPL code */
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Device Driver for SCC (SMN/SCM)");
+
+
+#endif /* SCC_INTERNALS_H */
diff --git a/drivers/mxc/ssi/Kconfig b/drivers/mxc/ssi/Kconfig
new file mode 100644
index 000000000000..4cb581c2f945
--- /dev/null
+++ b/drivers/mxc/ssi/Kconfig
@@ -0,0 +1,12 @@
+#
+# SPI device configuration
+#
+
+menu "MXC SSI support"
+
+config MXC_SSI
+ tristate "SSI support"
+ ---help---
+ Say Y to get the SSI services API available on MXC platform.
+
+endmenu
diff --git a/drivers/mxc/ssi/Makefile b/drivers/mxc/ssi/Makefile
new file mode 100644
index 000000000000..f9bb4419fc19
--- /dev/null
+++ b/drivers/mxc/ssi/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel SSI device drivers.
+#
+
+obj-$(CONFIG_MXC_SSI) += ssimod.o
+
+ssimod-objs := ssi.o
diff --git a/drivers/mxc/ssi/registers.h b/drivers/mxc/ssi/registers.h
new file mode 100644
index 000000000000..ba98d96c5274
--- /dev/null
+++ b/drivers/mxc/ssi/registers.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @file ../ssi/registers.h
+ * @brief This header file contains SSI driver low level definition to access module registers.
+ *
+ * @ingroup SSI
+ */
+
+#ifndef __MXC_SSI_REGISTERS_H__
+#define __MXC_SSI_REGISTERS_H__
+
+/*!
+ * This include to define bool type, false and true definitions.
+ */
+#include <mach/hardware.h>
+
+#define SPBA_CPU_SSI 0x07
+
+#define MXC_SSISTX0 0x00
+#define MXC_SSISTX1 0x04
+#define MXC_SSISRX0 0x08
+#define MXC_SSISRX1 0x0C
+#define MXC_SSISCR 0x10
+#define MXC_SSISISR 0x14
+#define MXC_SSISIER 0x18
+#define MXC_SSISTCR 0x1C
+#define MXC_SSISRCR 0x20
+#define MXC_SSISTCCR 0x24
+#define MXC_SSISRCCR 0x28
+#define MXC_SSISFCSR 0x2C
+#define MXC_SSISTR 0x30
+#define MXC_SSISOR 0x34
+#define MXC_SSISACNT 0x38
+#define MXC_SSISACADD 0x3C
+#define MXC_SSISACDAT 0x40
+#define MXC_SSISATAG 0x44
+#define MXC_SSISTMSK 0x48
+#define MXC_SSISRMSK 0x4C
+
+/* MXC 91221 only */
+#define MXC_SSISACCST 0x50
+#define MXC_SSISACCEN 0x54
+#define MXC_SSISACCDIS 0x58
+
+/*! SSI1 registers offset*/
+#define MXC_SSI1STX0 0x00
+#define MXC_SSI1STX1 0x04
+#define MXC_SSI1SRX0 0x08
+#define MXC_SSI1SRX1 0x0C
+#define MXC_SSI1SCR 0x10
+#define MXC_SSI1SISR 0x14
+#define MXC_SSI1SIER 0x18
+#define MXC_SSI1STCR 0x1C
+#define MXC_SSI1SRCR 0x20
+#define MXC_SSI1STCCR 0x24
+#define MXC_SSI1SRCCR 0x28
+#define MXC_SSI1SFCSR 0x2C
+#define MXC_SSI1STR 0x30
+#define MXC_SSI1SOR 0x34
+#define MXC_SSI1SACNT 0x38
+#define MXC_SSI1SACADD 0x3C
+#define MXC_SSI1SACDAT 0x40
+#define MXC_SSI1SATAG 0x44
+#define MXC_SSI1STMSK 0x48
+#define MXC_SSI1SRMSK 0x4C
+
+/* MXC91221 only */
+
+#define MXC_SSISACCST 0x50
+#define MXC_SSISACCEN 0x54
+#define MXC_SSISACCDIS 0x58
+
+/* Not on MXC91221 */
+/*! SSI2 registers offset*/
+#define MXC_SSI2STX0 0x00
+#define MXC_SSI2STX1 0x04
+#define MXC_SSI2SRX0 0x08
+#define MXC_SSI2SRX1 0x0C
+#define MXC_SSI2SCR 0x10
+#define MXC_SSI2SISR 0x14
+#define MXC_SSI2SIER 0x18
+#define MXC_SSI2STCR 0x1C
+#define MXC_SSI2SRCR 0x20
+#define MXC_SSI2STCCR 0x24
+#define MXC_SSI2SRCCR 0x28
+#define MXC_SSI2SFCSR 0x2C
+#define MXC_SSI2STR 0x30
+#define MXC_SSI2SOR 0x34
+#define MXC_SSI2SACNT 0x38
+#define MXC_SSI2SACADD 0x3C
+#define MXC_SSI2SACDAT 0x40
+#define MXC_SSI2SATAG 0x44
+#define MXC_SSI2STMSK 0x48
+#define MXC_SSI2SRMSK 0x4C
+
+/*!
+ * SCR Register bit shift definitions
+ */
+#define SSI_ENABLE_SHIFT 0
+#define SSI_TRANSMIT_ENABLE_SHIFT 1
+#define SSI_RECEIVE_ENABLE_SHIFT 2
+#define SSI_NETWORK_MODE_SHIFT 3
+#define SSI_SYNCHRONOUS_MODE_SHIFT 4
+#define SSI_I2S_MODE_SHIFT 5
+#define SSI_SYSTEM_CLOCK_SHIFT 7
+#define SSI_TWO_CHANNEL_SHIFT 8
+#define SSI_CLOCK_IDLE_SHIFT 9
+
+/* MXC91221 only*/
+#define SSI_TX_FRAME_CLOCK_DISABLE_SHIFT 10
+#define SSI_RX_FRAME_CLOCK_DISABLE_SHIFT 11
+
+/*!
+ * STCR & SRCR Registers bit shift definitions
+ */
+#define SSI_EARLY_FRAME_SYNC_SHIFT 0
+#define SSI_FRAME_SYNC_LENGTH_SHIFT 1
+#define SSI_FRAME_SYNC_INVERT_SHIFT 2
+#define SSI_CLOCK_POLARITY_SHIFT 3
+#define SSI_SHIFT_DIRECTION_SHIFT 4
+#define SSI_CLOCK_DIRECTION_SHIFT 5
+#define SSI_FRAME_DIRECTION_SHIFT 6
+#define SSI_FIFO_ENABLE_0_SHIFT 7
+#define SSI_FIFO_ENABLE_1_SHIFT 8
+#define SSI_BIT_0_SHIFT 9
+
+/* MXC91221 only*/
+#define SSI_TX_FRAME_CLOCK_DISABLE_SHIFT 10
+#define SSI_RX_DATA_EXTENSION_SHIFT 10 /*SRCR only */
+/*!
+ * STCCR & SRCCR Registers bit shift definitions
+ */
+#define SSI_PRESCALER_MODULUS_SHIFT 0
+#define SSI_FRAME_RATE_DIVIDER_SHIFT 8
+#define SSI_WORD_LENGTH_SHIFT 13
+#define SSI_PRESCALER_RANGE_SHIFT 17
+#define SSI_DIVIDE_BY_TWO_SHIFT 18
+#define SSI_FRAME_DIVIDER_MASK 31
+#define SSI_MIN_FRAME_DIVIDER_RATIO 1
+#define SSI_MAX_FRAME_DIVIDER_RATIO 32
+#define SSI_PRESCALER_MODULUS_MASK 255
+#define SSI_MIN_PRESCALER_MODULUS_RATIO 1
+#define SSI_MAX_PRESCALER_MODULUS_RATIO 256
+#define SSI_WORD_LENGTH_MASK 15
+
+#define SSI_IRQ_STATUS_NUMBER 25
+
+/*!
+ * SFCSR Register bit shift definitions
+ */
+#define SSI_RX_FIFO_1_COUNT_SHIFT 28
+#define SSI_TX_FIFO_1_COUNT_SHIFT 24
+#define SSI_RX_FIFO_1_WATERMARK_SHIFT 20
+#define SSI_TX_FIFO_1_WATERMARK_SHIFT 16
+#define SSI_RX_FIFO_0_COUNT_SHIFT 12
+#define SSI_TX_FIFO_0_COUNT_SHIFT 8
+#define SSI_RX_FIFO_0_WATERMARK_SHIFT 4
+#define SSI_TX_FIFO_0_WATERMARK_SHIFT 0
+#define SSI_MIN_FIFO_WATERMARK 0
+#define SSI_MAX_FIFO_WATERMARK 8
+
+/*!
+ * SSI Option Register (SOR) bit shift definitions
+ */
+#define SSI_FRAME_SYN_RESET_SHIFT 0
+#define SSI_WAIT_SHIFT 1
+#define SSI_INIT_SHIFT 3
+#define SSI_TRANSMITTER_CLEAR_SHIFT 4
+#define SSI_RECEIVER_CLEAR_SHIFT 5
+#define SSI_CLOCK_OFF_SHIFT 6
+#define SSI_WAIT_STATE_MASK 0x3
+
+/*!
+ * SSI AC97 Control Register (SACNT) bit shift definitions
+ */
+#define AC97_MODE_ENABLE_SHIFT 0
+#define AC97_VARIABLE_OPERATION_SHIFT 1
+#define AC97_TAG_IN_FIFO_SHIFT 2
+#define AC97_READ_COMMAND_SHIFT 3
+#define AC97_WRITE_COMMAND_SHIFT 4
+#define AC97_FRAME_RATE_DIVIDER_SHIFT 5
+#define AC97_FRAME_RATE_MASK 0x3F
+
+/*!
+ * SSI Test Register (STR) bit shift definitions
+ */
+#define SSI_TEST_MODE_SHIFT 15
+#define SSI_RCK2TCK_SHIFT 14
+#define SSI_RFS2TFS_SHIFT 13
+#define SSI_RXSTATE_SHIFT 8
+#define SSI_TXD2RXD_SHIFT 7
+#define SSI_TCK2RCK_SHIFT 6
+#define SSI_TFS2RFS_SHIFT 5
+#define SSI_TXSTATE_SHIFT 0
+
+#endif /* __MXC_SSI_REGISTERS_H__ */
diff --git a/drivers/mxc/ssi/ssi.c b/drivers/mxc/ssi/ssi.c
new file mode 100644
index 000000000000..c6b8ad5b4758
--- /dev/null
+++ b/drivers/mxc/ssi/ssi.c
@@ -0,0 +1,1239 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ssi.c
+ * @brief This file contains the implementation of the SSI driver main services
+ *
+ *
+ * @ingroup SSI
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <mach/clock.h>
+
+#include "registers.h"
+#include "ssi.h"
+
+static spinlock_t ssi_lock;
+struct mxc_audio_platform_data *ssi_platform_data;
+
+EXPORT_SYMBOL(ssi_ac97_frame_rate_divider);
+EXPORT_SYMBOL(ssi_ac97_get_command_address_register);
+EXPORT_SYMBOL(ssi_ac97_get_command_data_register);
+EXPORT_SYMBOL(ssi_ac97_get_tag_register);
+EXPORT_SYMBOL(ssi_ac97_mode_enable);
+EXPORT_SYMBOL(ssi_ac97_tag_in_fifo);
+EXPORT_SYMBOL(ssi_ac97_read_command);
+EXPORT_SYMBOL(ssi_ac97_set_command_address_register);
+EXPORT_SYMBOL(ssi_ac97_set_command_data_register);
+EXPORT_SYMBOL(ssi_ac97_set_tag_register);
+EXPORT_SYMBOL(ssi_ac97_variable_mode);
+EXPORT_SYMBOL(ssi_ac97_write_command);
+EXPORT_SYMBOL(ssi_clock_idle_state);
+EXPORT_SYMBOL(ssi_clock_off);
+EXPORT_SYMBOL(ssi_enable);
+EXPORT_SYMBOL(ssi_get_data);
+EXPORT_SYMBOL(ssi_get_status);
+EXPORT_SYMBOL(ssi_i2s_mode);
+EXPORT_SYMBOL(ssi_interrupt_disable);
+EXPORT_SYMBOL(ssi_interrupt_enable);
+EXPORT_SYMBOL(ssi_network_mode);
+EXPORT_SYMBOL(ssi_receive_enable);
+EXPORT_SYMBOL(ssi_rx_bit0);
+EXPORT_SYMBOL(ssi_rx_clock_direction);
+EXPORT_SYMBOL(ssi_rx_clock_divide_by_two);
+EXPORT_SYMBOL(ssi_rx_clock_polarity);
+EXPORT_SYMBOL(ssi_rx_clock_prescaler);
+EXPORT_SYMBOL(ssi_rx_early_frame_sync);
+EXPORT_SYMBOL(ssi_rx_fifo_counter);
+EXPORT_SYMBOL(ssi_rx_fifo_enable);
+EXPORT_SYMBOL(ssi_rx_fifo_full_watermark);
+EXPORT_SYMBOL(ssi_rx_flush_fifo);
+EXPORT_SYMBOL(ssi_rx_frame_direction);
+EXPORT_SYMBOL(ssi_rx_frame_rate);
+EXPORT_SYMBOL(ssi_rx_frame_sync_active);
+EXPORT_SYMBOL(ssi_rx_frame_sync_length);
+EXPORT_SYMBOL(ssi_rx_mask_time_slot);
+EXPORT_SYMBOL(ssi_rx_prescaler_modulus);
+EXPORT_SYMBOL(ssi_rx_shift_direction);
+EXPORT_SYMBOL(ssi_rx_word_length);
+EXPORT_SYMBOL(ssi_set_data);
+EXPORT_SYMBOL(ssi_set_wait_states);
+EXPORT_SYMBOL(ssi_synchronous_mode);
+EXPORT_SYMBOL(ssi_system_clock);
+EXPORT_SYMBOL(ssi_transmit_enable);
+EXPORT_SYMBOL(ssi_two_channel_mode);
+EXPORT_SYMBOL(ssi_tx_bit0);
+EXPORT_SYMBOL(ssi_tx_clock_direction);
+EXPORT_SYMBOL(ssi_tx_clock_divide_by_two);
+EXPORT_SYMBOL(ssi_tx_clock_polarity);
+EXPORT_SYMBOL(ssi_tx_clock_prescaler);
+EXPORT_SYMBOL(ssi_tx_early_frame_sync);
+EXPORT_SYMBOL(ssi_tx_fifo_counter);
+EXPORT_SYMBOL(ssi_tx_fifo_empty_watermark);
+EXPORT_SYMBOL(ssi_tx_fifo_enable);
+EXPORT_SYMBOL(ssi_tx_flush_fifo);
+EXPORT_SYMBOL(ssi_tx_frame_direction);
+EXPORT_SYMBOL(ssi_tx_frame_rate);
+EXPORT_SYMBOL(ssi_tx_frame_sync_active);
+EXPORT_SYMBOL(ssi_tx_frame_sync_length);
+EXPORT_SYMBOL(ssi_tx_mask_time_slot);
+EXPORT_SYMBOL(ssi_tx_prescaler_modulus);
+EXPORT_SYMBOL(ssi_tx_shift_direction);
+EXPORT_SYMBOL(ssi_tx_word_length);
+EXPORT_SYMBOL(get_ssi_fifo_addr);
+
+struct resource *res;
+void *base_addr_1;
+void *base_addr_2;
+
+unsigned int get_ssi_fifo_addr(unsigned int ssi, int direction)
+{
+ unsigned int fifo_addr;
+ if (direction == 1) {
+ if (ssi_platform_data->ssi_num == 2) {
+ fifo_addr =
+ (ssi ==
+ SSI1) ? (int)(base_addr_1 +
+ MXC_SSI1STX0) : (int)(base_addr_2 +
+ MXC_SSI2STX0);
+ } else {
+ fifo_addr = (int)(base_addr_1 + MXC_SSI1STX0);
+ }
+ } else {
+ fifo_addr = (int)(base_addr_1 + MXC_SSI1SRX0);
+ }
+ return fifo_addr;
+}
+
+unsigned int get_ssi_base_addr(unsigned int ssi)
+{
+ int base_addr;
+ if (ssi_platform_data->ssi_num == 2) {
+ base_addr =
+ (ssi ==
+ SSI1) ? IO_ADDRESS((int)base_addr_1) : IO_ADDRESS((int)
+ base_addr_2);
+ } else {
+ base_addr = IO_ADDRESS((int)base_addr_1);
+ }
+ return base_addr;
+}
+
+void set_register_bits(unsigned int mask, unsigned int data,
+ unsigned int offset, unsigned int ssi)
+{
+ volatile unsigned long reg = 0;
+ unsigned int base_addr = 0;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&ssi_lock, flags);
+ base_addr = get_ssi_base_addr(ssi);
+ reg = __raw_readl(base_addr + offset);
+ reg = (reg & (~mask)) | data;
+ __raw_writel(reg, base_addr + offset);
+ spin_unlock_irqrestore(&ssi_lock, flags);
+}
+
+unsigned long getreg_value(unsigned int offset, unsigned int ssi)
+{
+ volatile unsigned long reg = 0;
+ unsigned int base_addr = 0;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&ssi_lock, flags);
+ base_addr = get_ssi_base_addr(ssi);
+ reg = __raw_readl(base_addr + offset);
+ spin_unlock_irqrestore(&ssi_lock, flags);
+
+ return reg;
+}
+
+void set_register(unsigned int data, unsigned int offset, unsigned int ssi)
+{
+ unsigned int base_addr = 0;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&ssi_lock, flags);
+ base_addr = get_ssi_base_addr(ssi);
+ __raw_writel(data, base_addr + offset);
+ spin_unlock_irqrestore(&ssi_lock, flags);
+
+}
+
+/*!
+ * This function controls the AC97 frame rate divider.
+ *
+ * @param module the module number
+ * @param frame_rate_divider the AC97 frame rate divider
+ */
+void ssi_ac97_frame_rate_divider(ssi_mod module,
+ unsigned char frame_rate_divider)
+{
+ unsigned int reg = 0;
+
+ reg = getreg_value(MXC_SSISACNT, module);
+ reg |= ((frame_rate_divider & AC97_FRAME_RATE_MASK)
+ << AC97_FRAME_RATE_DIVIDER_SHIFT);
+ set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function gets the AC97 command address register.
+ *
+ * @param module the module number
+ * @return This function returns the command address slot information.
+ */
+unsigned int ssi_ac97_get_command_address_register(ssi_mod module)
+{
+ return getreg_value(MXC_SSISACADD, module);
+}
+
+/*!
+ * This function gets the AC97 command data register.
+ *
+ * @param module the module number
+ * @return This function returns the command data slot information.
+ */
+unsigned int ssi_ac97_get_command_data_register(ssi_mod module)
+{
+ return getreg_value(MXC_SSISACDAT, module);
+}
+
+/*!
+ * This function gets the AC97 tag register.
+ *
+ * @param module the module number
+ * @return This function returns the tag information.
+ */
+unsigned int ssi_ac97_get_tag_register(ssi_mod module)
+{
+ return getreg_value(MXC_SSISATAG, module);
+}
+
+/*!
+ * This function controls the AC97 mode.
+ *
+ * @param module the module number
+ * @param state the AC97 mode state (enabled or disabled)
+ */
+void ssi_ac97_mode_enable(ssi_mod module, bool state)
+{
+ unsigned int reg = 0;
+
+ reg = getreg_value(MXC_SSISACNT, module);
+ if (state == true) {
+ reg |= (1 << AC97_MODE_ENABLE_SHIFT);
+ } else {
+ reg &= ~(1 << AC97_MODE_ENABLE_SHIFT);
+ }
+
+ set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function controls the AC97 tag in FIFO behavior.
+ *
+ * @param module the module number
+ * @param state the tag in fifo behavior (Tag info stored in Rx FIFO 0 if true,
+ * Tag info stored in SATAG register otherwise)
+ */
+void ssi_ac97_tag_in_fifo(ssi_mod module, bool state)
+{
+ unsigned int reg = 0;
+
+ reg = getreg_value(MXC_SSISACNT, module);
+ if (state == true) {
+ reg |= (1 << AC97_TAG_IN_FIFO_SHIFT);
+ } else {
+ reg &= ~(1 << AC97_TAG_IN_FIFO_SHIFT);
+ }
+
+ set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function controls the AC97 read command.
+ *
+ * @param module the module number
+ * @param state the next AC97 command is a read command or not
+ */
+void ssi_ac97_read_command(ssi_mod module, bool state)
+{
+ unsigned int reg = 0;
+
+ reg = getreg_value(MXC_SSISACNT, module);
+ if (state == true) {
+ reg |= (1 << AC97_READ_COMMAND_SHIFT);
+ } else {
+ reg &= ~(1 << AC97_READ_COMMAND_SHIFT);
+ }
+
+ set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function sets the AC97 command address register.
+ *
+ * @param module the module number
+ * @param address the command address slot information
+ */
+void ssi_ac97_set_command_address_register(ssi_mod module, unsigned int address)
+{
+ set_register(address, MXC_SSISACADD, module);
+}
+
+/*!
+ * This function sets the AC97 command data register.
+ *
+ * @param module the module number
+ * @param data the command data slot information
+ */
+void ssi_ac97_set_command_data_register(ssi_mod module, unsigned int data)
+{
+ set_register(data, MXC_SSISACDAT, module);
+}
+
+/*!
+ * This function sets the AC97 tag register.
+ *
+ * @param module the module number
+ * @param tag the tag information
+ */
+void ssi_ac97_set_tag_register(ssi_mod module, unsigned int tag)
+{
+ set_register(tag, MXC_SSISATAG, module);
+}
+
+/*!
+ * This function controls the AC97 variable mode.
+ *
+ * @param module the module number
+ * @param state the AC97 variable mode state (enabled or disabled)
+ */ void ssi_ac97_variable_mode(ssi_mod module, bool state)
+{
+ unsigned int reg = 0;
+
+ reg = getreg_value(MXC_SSISACNT, module);
+ if (state == true) {
+ reg |= (1 << AC97_VARIABLE_OPERATION_SHIFT);
+ } else {
+ reg &= ~(1 << AC97_VARIABLE_OPERATION_SHIFT);
+ }
+
+ set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function controls the AC97 write command.
+ *
+ * @param module the module number
+ * @param state the next AC97 command is a write command or not
+ */
+void ssi_ac97_write_command(ssi_mod module, bool state)
+{
+ unsigned int reg = 0;
+
+ reg = getreg_value(MXC_SSISACNT, module);
+ if (state == true) {
+ reg |= (1 << AC97_WRITE_COMMAND_SHIFT);
+ } else {
+ reg &= ~(1 << AC97_WRITE_COMMAND_SHIFT);
+ }
+
+ set_register(reg, MXC_SSISACNT, module);
+}
+
+/*!
+ * This function controls the idle state of the transmit clock port during SSI internal gated mode.
+ *
+ * @param module the module number
+ * @param state the clock idle state
+ */
+void ssi_clock_idle_state(ssi_mod module, idle_state state)
+{
+ set_register_bits(1 << SSI_CLOCK_IDLE_SHIFT,
+ state << SSI_CLOCK_IDLE_SHIFT, MXC_SSISCR, module);
+}
+
+/*!
+ * This function turns off/on the ccm_ssi_clk to reduce power consumption.
+ *
+ * @param module the module number
+ * @param state the state for ccm_ssi_clk (true: turn off, else:turn on)
+ */
+void ssi_clock_off(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_CLOCK_OFF_SHIFT,
+ state << SSI_CLOCK_OFF_SHIFT, MXC_SSISOR, module);
+}
+
+/*!
+ * This function enables/disables the SSI module.
+ *
+ * @param module the module number
+ * @param state the state for SSI module
+ */
+void ssi_enable(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_ENABLE_SHIFT, state << SSI_ENABLE_SHIFT,
+ MXC_SSISCR, module);
+}
+
+/*!
+ * This function gets the data word in the Receive FIFO of the SSI module.
+ *
+ * @param module the module number
+ * @param fifo the Receive FIFO to read
+ * @return This function returns the read data.
+ */
+unsigned int ssi_get_data(ssi_mod module, fifo_nb fifo)
+{
+ unsigned int result = 0;
+
+ if (ssi_fifo_0 == fifo) {
+ result = getreg_value(MXC_SSISRX0, module);
+ } else {
+ result = getreg_value(MXC_SSISRX1, module);
+ }
+
+ return result;
+}
+
+/*!
+ * This function returns the status of the SSI module (SISR register) as a combination of status.
+ *
+ * @param module the module number
+ * @return This function returns the status of the SSI module
+ */
+ssi_status_enable_mask ssi_get_status(ssi_mod module)
+{
+ unsigned int result;
+
+ result = getreg_value(MXC_SSISISR, module);
+ result &= ((1 << SSI_IRQ_STATUS_NUMBER) - 1);
+
+ return (ssi_status_enable_mask) result;
+}
+
+/*!
+ * This function selects the I2S mode of the SSI module.
+ *
+ * @param module the module number
+ * @param mode the I2S mode
+ */
+void ssi_i2s_mode(ssi_mod module, mode_i2s mode)
+{
+ set_register_bits(3 << SSI_I2S_MODE_SHIFT, mode << SSI_I2S_MODE_SHIFT,
+ MXC_SSISCR, module);
+}
+
+/*!
+ * This function disables the interrupts of the SSI module.
+ *
+ * @param module the module number
+ * @param mask the mask of the interrupts to disable
+ */
+void ssi_interrupt_disable(ssi_mod module, ssi_status_enable_mask mask)
+{
+ set_register_bits(mask, 0, MXC_SSISIER, module);
+}
+
+/*!
+ * This function enables the interrupts of the SSI module.
+ *
+ * @param module the module number
+ * @param mask the mask of the interrupts to enable
+ */
+void ssi_interrupt_enable(ssi_mod module, ssi_status_enable_mask mask)
+{
+ set_register_bits(0, mask, MXC_SSISIER, module);
+}
+
+/*!
+ * This function enables/disables the network mode of the SSI module.
+ *
+ * @param module the module number
+ * @param state the network mode state
+ */
+void ssi_network_mode(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_NETWORK_MODE_SHIFT,
+ state << SSI_NETWORK_MODE_SHIFT, MXC_SSISCR, module);
+}
+
+/*!
+ * This function enables/disables the receive section of the SSI module.
+ *
+ * @param module the module number
+ * @param state the receive section state
+ */
+void ssi_receive_enable(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_RECEIVE_ENABLE_SHIFT,
+ state << SSI_RECEIVE_ENABLE_SHIFT, MXC_SSISCR,
+ module);
+}
+
+/*!
+ * This function configures the SSI module to receive data word at bit position 0 or 23 in the Receive shift register.
+ *
+ * @param module the module number
+ * @param state the state to receive at bit 0
+ */
+void ssi_rx_bit0(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_BIT_0_SHIFT, state << SSI_BIT_0_SHIFT,
+ MXC_SSISRCR, module);
+}
+
+/*!
+ * This function controls the source of the clock signal used to clock the Receive shift register.
+ *
+ * @param module the module number
+ * @param direction the clock signal direction
+ */
+void ssi_rx_clock_direction(ssi_mod module, ssi_tx_rx_direction direction)
+{
+ set_register_bits(1 << SSI_CLOCK_DIRECTION_SHIFT,
+ direction << SSI_CLOCK_DIRECTION_SHIFT, MXC_SSISRCR,
+ module);
+}
+
+/*!
+ * This function configures the divide-by-two divider of the SSI module for the receive section.
+ *
+ * @param module the module number
+ * @param state the divider state
+ */
+void ssi_rx_clock_divide_by_two(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_DIVIDE_BY_TWO_SHIFT,
+ state << SSI_DIVIDE_BY_TWO_SHIFT, MXC_SSISRCCR,
+ module);
+}
+
+/*!
+ * This function controls which bit clock edge is used to clock in data.
+ *
+ * @param module the module number
+ * @param polarity the clock polarity
+ */
+void ssi_rx_clock_polarity(ssi_mod module, ssi_tx_rx_clock_polarity polarity)
+{
+ set_register_bits(1 << SSI_CLOCK_POLARITY_SHIFT,
+ polarity << SSI_CLOCK_POLARITY_SHIFT, MXC_SSISRCR,
+ module);
+}
+
+/*!
+ * This function configures a fixed divide-by-eight clock prescaler divider of the SSI module in series with the variable prescaler for the receive section.
+ *
+ * @param module the module number
+ * @param state the prescaler state
+ */
+void ssi_rx_clock_prescaler(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_PRESCALER_RANGE_SHIFT,
+ state << SSI_PRESCALER_RANGE_SHIFT,
+ MXC_SSISRCCR, module);
+}
+
+/*!
+ * This function controls the early frame sync configuration.
+ *
+ * @param module the module number
+ * @param early the early frame sync configuration
+ */
+void ssi_rx_early_frame_sync(ssi_mod module, ssi_tx_rx_early_frame_sync early)
+{
+ set_register_bits(1 << SSI_EARLY_FRAME_SYNC_SHIFT,
+ early << SSI_EARLY_FRAME_SYNC_SHIFT,
+ MXC_SSISRCR, module);
+}
+
+/*!
+ * This function gets the number of data words in the Receive FIFO.
+ *
+ * @param module the module number
+ * @param fifo the fifo
+ * @return This function returns the number of words in the Rx FIFO.
+ */
+unsigned char ssi_rx_fifo_counter(ssi_mod module, fifo_nb fifo)
+{
+ unsigned char result;
+ result = 0;
+
+ if (ssi_fifo_0 == fifo) {
+ result = getreg_value(MXC_SSISFCSR, module);
+ result &= (0xF << SSI_RX_FIFO_0_COUNT_SHIFT);
+ result = result >> SSI_RX_FIFO_0_COUNT_SHIFT;
+ } else {
+ result = getreg_value(MXC_SSISFCSR, module);
+ result &= (0xF << SSI_RX_FIFO_1_COUNT_SHIFT);
+ result = result >> SSI_RX_FIFO_1_COUNT_SHIFT;
+ }
+
+ return result;
+}
+
+/*!
+ * This function enables the Receive FIFO.
+ *
+ * @param module the module number
+ * @param fifo the fifo to enable
+ * @param enable the state of the fifo, enabled or disabled
+ */
+
+void ssi_rx_fifo_enable(ssi_mod module, fifo_nb fifo, bool enable)
+{
+ volatile unsigned int reg;
+
+ reg = getreg_value(MXC_SSISRCR, module);
+ if (enable == true) {
+ reg |= ((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT);
+ } else {
+ reg &= ~((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT);
+ }
+
+ set_register(reg, MXC_SSISRCR, module);
+}
+
+/*!
+ * This function controls the threshold at which the RFFx flag will be set.
+ *
+ * @param module the module number
+ * @param fifo the fifo to enable
+ * @param watermark the watermark
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_fifo_full_watermark(ssi_mod module,
+ fifo_nb fifo, unsigned char watermark)
+{
+ int result = -1;
+ result = -1;
+
+ if ((watermark > SSI_MIN_FIFO_WATERMARK) &&
+ (watermark <= SSI_MAX_FIFO_WATERMARK)) {
+ if (ssi_fifo_0 == fifo) {
+ set_register_bits(0xf << SSI_RX_FIFO_0_WATERMARK_SHIFT,
+ watermark <<
+ SSI_RX_FIFO_0_WATERMARK_SHIFT,
+ MXC_SSISFCSR, module);
+ } else {
+ set_register_bits(0xf << SSI_RX_FIFO_1_WATERMARK_SHIFT,
+ watermark <<
+ SSI_RX_FIFO_1_WATERMARK_SHIFT,
+ MXC_SSISFCSR, module);
+ }
+
+ result = 0;
+ }
+
+ return result;
+}
+
+/*!
+ * This function flushes the Receive FIFOs.
+ *
+ * @param module the module number
+ */
+void ssi_rx_flush_fifo(ssi_mod module)
+{
+ set_register_bits(0, 1 << SSI_RECEIVER_CLEAR_SHIFT, MXC_SSISOR, module);
+}
+
+/*!
+ * This function controls the direction of the Frame Sync signal for the receive section.
+ *
+ * @param module the module number
+ * @param direction the Frame Sync signal direction
+ */
+void ssi_rx_frame_direction(ssi_mod module, ssi_tx_rx_direction direction)
+{
+ set_register_bits(1 << SSI_FRAME_DIRECTION_SHIFT,
+ direction << SSI_FRAME_DIRECTION_SHIFT,
+ MXC_SSISRCR, module);
+}
+
+/*!
+ * This function configures the Receive frame rate divider for the receive section.
+ *
+ * @param module the module number
+ * @param ratio the divide ratio from 1 to 32
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_frame_rate(ssi_mod module, unsigned char ratio)
+{
+ int result = -1;
+
+ if ((ratio >= SSI_MIN_FRAME_DIVIDER_RATIO) &&
+ (ratio <= SSI_MAX_FRAME_DIVIDER_RATIO)) {
+ set_register_bits(SSI_FRAME_DIVIDER_MASK <<
+ SSI_FRAME_RATE_DIVIDER_SHIFT,
+ (ratio - 1) << SSI_FRAME_RATE_DIVIDER_SHIFT,
+ MXC_SSISRCCR, module);
+ result = 0;
+ }
+
+ return result;
+}
+
+/*!
+ * This function controls the Frame Sync active polarity for the receive section.
+ *
+ * @param module the module number
+ * @param active the Frame Sync active polarity
+ */
+void ssi_rx_frame_sync_active(ssi_mod module,
+ ssi_tx_rx_frame_sync_active active)
+{
+ set_register_bits(1 << SSI_FRAME_SYNC_INVERT_SHIFT,
+ active << SSI_FRAME_SYNC_INVERT_SHIFT,
+ MXC_SSISRCR, module);
+}
+
+/*!
+ * This function controls the Frame Sync length (one word or one bit long) for the receive section.
+ *
+ * @param module the module number
+ * @param length the Frame Sync length
+ */
+void ssi_rx_frame_sync_length(ssi_mod module,
+ ssi_tx_rx_frame_sync_length length)
+{
+ set_register_bits(1 << SSI_FRAME_SYNC_LENGTH_SHIFT,
+ length << SSI_FRAME_SYNC_LENGTH_SHIFT,
+ MXC_SSISRCR, module);
+}
+
+/*!
+ * This function configures the time slot(s) to mask for the receive section.
+ *
+ * @param module the module number
+ * @param mask the mask to indicate the time slot(s) masked
+ */
+void ssi_rx_mask_time_slot(ssi_mod module, unsigned int mask)
+{
+ set_register_bits(0xFFFFFFFF, mask, MXC_SSISRMSK, module);
+}
+
+/*!
+ * This function configures the Prescale divider for the receive section.
+ *
+ * @param module the module number
+ * @param divider the divide ratio from 1 to 256
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_prescaler_modulus(ssi_mod module, unsigned int divider)
+{
+ int result = -1;
+
+ if ((divider >= SSI_MIN_PRESCALER_MODULUS_RATIO) &&
+ (divider <= SSI_MAX_PRESCALER_MODULUS_RATIO)) {
+
+ set_register_bits(SSI_PRESCALER_MODULUS_MASK <<
+ SSI_PRESCALER_MODULUS_SHIFT,
+ (divider - 1) << SSI_PRESCALER_MODULUS_SHIFT,
+ MXC_SSISRCCR, module);
+ result = 0;
+ }
+
+ return result;
+}
+
+/*!
+ * This function controls whether the MSB or LSB will be received first in a sample.
+ *
+ * @param module the module number
+ * @param direction the shift direction
+ */
+void ssi_rx_shift_direction(ssi_mod module, ssi_tx_rx_shift_direction direction)
+{
+ set_register_bits(1 << SSI_SHIFT_DIRECTION_SHIFT,
+ direction << SSI_SHIFT_DIRECTION_SHIFT,
+ MXC_SSISRCR, module);
+}
+
+/*!
+ * This function configures the Receive word length.
+ *
+ * @param module the module number
+ * @param length the word length
+ */
+void ssi_rx_word_length(ssi_mod module, ssi_word_length length)
+{
+ set_register_bits(SSI_WORD_LENGTH_MASK << SSI_WORD_LENGTH_SHIFT,
+ length << SSI_WORD_LENGTH_SHIFT,
+ MXC_SSISRCCR, module);
+}
+
+/*!
+ * This function sets the data word in the Transmit FIFO of the SSI module.
+ *
+ * @param module the module number
+ * @param fifo the FIFO number
+ * @param data the data to load in the FIFO
+ */
+
+void ssi_set_data(ssi_mod module, fifo_nb fifo, unsigned int data)
+{
+ if (ssi_fifo_0 == fifo) {
+ set_register(data, MXC_SSISTX0, module);
+ } else {
+ set_register(data, MXC_SSISTX1, module);
+ }
+}
+
+/*!
+ * This function controls the number of wait states between the core and SSI.
+ *
+ * @param module the module number
+ * @param wait the number of wait state(s)
+ */
+void ssi_set_wait_states(ssi_mod module, ssi_wait_states wait)
+{
+ set_register_bits(SSI_WAIT_STATE_MASK << SSI_WAIT_SHIFT,
+ wait << SSI_WAIT_SHIFT, MXC_SSISOR, module);
+}
+
+/*!
+ * This function enables/disables the synchronous mode of the SSI module.
+ *
+ * @param module the module number
+ * @param state the synchronous mode state
+ */
+void ssi_synchronous_mode(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_SYNCHRONOUS_MODE_SHIFT,
+ state << SSI_SYNCHRONOUS_MODE_SHIFT,
+ MXC_SSISCR, module);
+}
+
+/*!
+ * This function allows the SSI module to output the SYS_CLK at the SRCK port.
+ *
+ * @param module the module number
+ * @param state the system clock state
+ */
+void ssi_system_clock(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_SYSTEM_CLOCK_SHIFT,
+ state << SSI_SYSTEM_CLOCK_SHIFT, MXC_SSISCR, module);
+}
+
+/*!
+ * This function enables/disables the transmit section of the SSI module.
+ *
+ * @param module the module number
+ * @param state the transmit section state
+ */
+void ssi_transmit_enable(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_TRANSMIT_ENABLE_SHIFT,
+ state << SSI_TRANSMIT_ENABLE_SHIFT,
+ MXC_SSISCR, module);
+}
+
+/*!
+ * This function allows the SSI module to operate in the two channel mode.
+ *
+ * @param module the module number
+ * @param state the two channel mode state
+ */
+void ssi_two_channel_mode(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_TWO_CHANNEL_SHIFT,
+ state << SSI_TWO_CHANNEL_SHIFT, MXC_SSISCR, module);
+}
+
+/*!
+ * This function configures the SSI module to transmit data word from bit position 0 or 23 in the Transmit shift register.
+ *
+ * @param module the module number
+ * @param state the transmit from bit 0 state
+ */
+void ssi_tx_bit0(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_BIT_0_SHIFT,
+ state << SSI_BIT_0_SHIFT, MXC_SSISTCR, module);
+}
+
+/*!
+ * This function controls the direction of the clock signal used to clock the Transmit shift register.
+ *
+ * @param module the module number
+ * @param direction the clock signal direction
+ */
+void ssi_tx_clock_direction(ssi_mod module, ssi_tx_rx_direction direction)
+{
+ set_register_bits(1 << SSI_CLOCK_DIRECTION_SHIFT,
+ direction << SSI_CLOCK_DIRECTION_SHIFT,
+ MXC_SSISTCR, module);
+}
+
+/*!
+ * This function configures the divide-by-two divider of the SSI module for the transmit section.
+ *
+ * @param module the module number
+ * @param state the divider state
+ */
+void ssi_tx_clock_divide_by_two(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_DIVIDE_BY_TWO_SHIFT,
+ state << SSI_DIVIDE_BY_TWO_SHIFT,
+ MXC_SSISTCCR, module);
+}
+
+/*!
+ * This function controls which bit clock edge is used to clock out data.
+ *
+ * @param module the module number
+ * @param polarity the clock polarity
+ */
+void ssi_tx_clock_polarity(ssi_mod module, ssi_tx_rx_clock_polarity polarity)
+{
+ set_register_bits(1 << SSI_CLOCK_POLARITY_SHIFT,
+ polarity << SSI_CLOCK_POLARITY_SHIFT,
+ MXC_SSISTCR, module);
+}
+
+/*!
+ * This function configures a fixed divide-by-eight clock prescaler divider of the SSI module in series with the variable prescaler for the transmit section.
+ *
+ * @param module the module number
+ * @param state the prescaler state
+ */
+void ssi_tx_clock_prescaler(ssi_mod module, bool state)
+{
+ set_register_bits(1 << SSI_PRESCALER_RANGE_SHIFT,
+ state << SSI_PRESCALER_RANGE_SHIFT,
+ MXC_SSISTCCR, module);
+}
+
+/*!
+ * This function controls the early frame sync configuration for the transmit section.
+ *
+ * @param module the module number
+ * @param early the early frame sync configuration
+ */
+void ssi_tx_early_frame_sync(ssi_mod module, ssi_tx_rx_early_frame_sync early)
+{
+ set_register_bits(1 << SSI_EARLY_FRAME_SYNC_SHIFT,
+ early << SSI_EARLY_FRAME_SYNC_SHIFT,
+ MXC_SSISTCR, module);
+}
+
+/*!
+ * This function gets the number of data words in the Transmit FIFO.
+ *
+ * @param module the module number
+ * @param fifo the fifo
+ * @return This function returns the number of words in the Tx FIFO.
+ */
+unsigned char ssi_tx_fifo_counter(ssi_mod module, fifo_nb fifo)
+{
+ unsigned char result = 0;
+
+ if (ssi_fifo_0 == fifo) {
+ result = getreg_value(MXC_SSISFCSR, module);
+ result &= (0xF << SSI_TX_FIFO_0_COUNT_SHIFT);
+ result >>= SSI_TX_FIFO_0_COUNT_SHIFT;
+ } else {
+ result = getreg_value(MXC_SSISFCSR, module);
+ result &= (0xF << SSI_TX_FIFO_1_COUNT_SHIFT);
+ result >>= SSI_TX_FIFO_1_COUNT_SHIFT;
+ }
+
+ return result;
+}
+
+/*!
+ * This function controls the threshold at which the TFEx flag will be set.
+ *
+ * @param module the module number
+ * @param fifo the fifo to enable
+ * @param watermark the watermark
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_fifo_empty_watermark(ssi_mod module,
+ fifo_nb fifo, unsigned char watermark)
+{
+ int result = -1;
+
+ if ((watermark > SSI_MIN_FIFO_WATERMARK) &&
+ (watermark <= SSI_MAX_FIFO_WATERMARK)) {
+ if (ssi_fifo_0 == fifo) {
+ set_register_bits(0xf << SSI_TX_FIFO_0_WATERMARK_SHIFT,
+ watermark <<
+ SSI_TX_FIFO_0_WATERMARK_SHIFT,
+ MXC_SSISFCSR, module);
+ } else {
+ set_register_bits(0xf << SSI_TX_FIFO_1_WATERMARK_SHIFT,
+ watermark <<
+ SSI_TX_FIFO_1_WATERMARK_SHIFT,
+ MXC_SSISFCSR, module);
+ }
+
+ result = 0;
+ }
+
+ return result;
+}
+
+/*!
+ * This function enables the Transmit FIFO.
+ *
+ * @param module the module number
+ * @param fifo the fifo to enable
+ * @param enable the state of the fifo, enabled or disabled
+ */
+
+void ssi_tx_fifo_enable(ssi_mod module, fifo_nb fifo, bool enable)
+{
+ unsigned int reg;
+
+ reg = getreg_value(MXC_SSISTCR, module);
+ if (enable == true) {
+ reg |= ((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT);
+ } else {
+ reg &= ~((1 + fifo) << SSI_FIFO_ENABLE_0_SHIFT);
+ }
+
+ set_register(reg, MXC_SSISTCR, module);
+}
+
+/*!
+ * This function flushes the Transmit FIFOs.
+ *
+ * @param module the module number
+ */
+void ssi_tx_flush_fifo(ssi_mod module)
+{
+ set_register_bits(0, 1 << SSI_TRANSMITTER_CLEAR_SHIFT,
+ MXC_SSISOR, module);
+}
+
+/*!
+ * This function controls the direction of the Frame Sync signal for the transmit section.
+ *
+ * @param module the module number
+ * @param direction the Frame Sync signal direction
+ */
+void ssi_tx_frame_direction(ssi_mod module, ssi_tx_rx_direction direction)
+{
+ set_register_bits(1 << SSI_FRAME_DIRECTION_SHIFT,
+ direction << SSI_FRAME_DIRECTION_SHIFT,
+ MXC_SSISTCR, module);
+}
+
+/*!
+ * This function configures the Transmit frame rate divider.
+ *
+ * @param module the module number
+ * @param ratio the divide ratio from 1 to 32
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_frame_rate(ssi_mod module, unsigned char ratio)
+{
+ int result = -1;
+
+ if ((ratio >= SSI_MIN_FRAME_DIVIDER_RATIO) &&
+ (ratio <= SSI_MAX_FRAME_DIVIDER_RATIO)) {
+
+ set_register_bits(SSI_FRAME_DIVIDER_MASK <<
+ SSI_FRAME_RATE_DIVIDER_SHIFT,
+ (ratio - 1) << SSI_FRAME_RATE_DIVIDER_SHIFT,
+ MXC_SSISTCCR, module);
+ result = 0;
+ }
+
+ return result;
+}
+
+/*!
+ * This function controls the Frame Sync active polarity for the transmit section.
+ *
+ * @param module the module number
+ * @param active the Frame Sync active polarity
+ */
+void ssi_tx_frame_sync_active(ssi_mod module,
+ ssi_tx_rx_frame_sync_active active)
+{
+ set_register_bits(1 << SSI_FRAME_SYNC_INVERT_SHIFT,
+ active << SSI_FRAME_SYNC_INVERT_SHIFT,
+ MXC_SSISTCR, module);
+}
+
+/*!
+ * This function controls the Frame Sync length (one word or one bit long) for the transmit section.
+ *
+ * @param module the module number
+ * @param length the Frame Sync length
+ */
+void ssi_tx_frame_sync_length(ssi_mod module,
+ ssi_tx_rx_frame_sync_length length)
+{
+ set_register_bits(1 << SSI_FRAME_SYNC_LENGTH_SHIFT,
+ length << SSI_FRAME_SYNC_LENGTH_SHIFT,
+ MXC_SSISTCR, module);
+}
+
+/*!
+ * This function configures the time slot(s) to mask for the transmit section.
+ *
+ * @param module the module number
+ * @param mask the mask to indicate the time slot(s) masked
+ */
+void ssi_tx_mask_time_slot(ssi_mod module, unsigned int mask)
+{
+ set_register_bits(0xFFFFFFFF, mask, MXC_SSISTMSK, module);
+}
+
+/*!
+ * This function configures the Prescale divider for the transmit section.
+ *
+ * @param module the module number
+ * @param divider the divide ratio from 1 to 256
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_prescaler_modulus(ssi_mod module, unsigned int divider)
+{
+ int result = -1;
+
+ if ((divider >= SSI_MIN_PRESCALER_MODULUS_RATIO) &&
+ (divider <= SSI_MAX_PRESCALER_MODULUS_RATIO)) {
+
+ set_register_bits(SSI_PRESCALER_MODULUS_MASK <<
+ SSI_PRESCALER_MODULUS_SHIFT,
+ (divider - 1) << SSI_PRESCALER_MODULUS_SHIFT,
+ MXC_SSISTCCR, module);
+ result = 0;
+ }
+
+ return result;
+}
+
+/*!
+ * This function controls whether the MSB or LSB will be transmitted first in a sample.
+ *
+ * @param module the module number
+ * @param direction the shift direction
+ */
+void ssi_tx_shift_direction(ssi_mod module, ssi_tx_rx_shift_direction direction)
+{
+ set_register_bits(1 << SSI_SHIFT_DIRECTION_SHIFT,
+ direction << SSI_SHIFT_DIRECTION_SHIFT,
+ MXC_SSISTCR, module);
+}
+
+/*!
+ * This function configures the Transmit word length.
+ *
+ * @param module the module number
+ * @param length the word length
+ */
+void ssi_tx_word_length(ssi_mod module, ssi_word_length length)
+{
+ set_register_bits(SSI_WORD_LENGTH_MASK << SSI_WORD_LENGTH_SHIFT,
+ length << SSI_WORD_LENGTH_SHIFT,
+ MXC_SSISTCCR, module);
+}
+
+/*!
+ * This function initializes the driver in terms of memory of the soundcard
+ * and some basic HW clock settings.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int __init ssi_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ ssi_platform_data =
+ (struct mxc_audio_platform_data *)pdev->dev.platform_data;
+ if (!ssi_platform_data) {
+ dev_err(&pdev->dev, "can't get the platform data for SSI\n");
+ return -EINVAL;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ dev_err(&pdev->dev, "can't get platform resource - SSI\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (pdev->id == 0) {
+ base_addr_1 = (void *)res->start;
+ } else if (pdev->id == 1) {
+ base_addr_2 = (void *)res->start;
+ }
+
+ printk(KERN_INFO "SSI %d module loaded successfully \n", pdev->id + 1);
+
+ return 0;
+ err:
+ return -1;
+
+}
+
+static int ssi_remove(struct platform_device *dev)
+{
+ return 0;
+}
+
+static struct platform_driver mxc_ssi_driver = {
+ .probe = ssi_probe,
+ .remove = ssi_remove,
+ .driver = {
+ .name = "mxc_ssi",
+ },
+};
+
+/*!
+ * This function implements the init function of the SSI device.
+ * This function is called when the module is loaded.
+ *
+ * @return This function returns 0.
+ */
+static int __init ssi_init(void)
+{
+ spin_lock_init(&ssi_lock);
+ return platform_driver_register(&mxc_ssi_driver);
+
+}
+
+/*!
+ * This function implements the exit function of the SPI device.
+ * This function is called when the module is unloaded.
+ *
+ */
+static void __exit ssi_exit(void)
+{
+ platform_driver_unregister(&mxc_ssi_driver);
+ printk(KERN_INFO "SSI module unloaded successfully\n");
+}
+
+module_init(ssi_init);
+module_exit(ssi_exit);
+
+MODULE_DESCRIPTION("SSI char device driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/ssi/ssi.h b/drivers/mxc/ssi/ssi.h
new file mode 100644
index 000000000000..22032b6616fa
--- /dev/null
+++ b/drivers/mxc/ssi/ssi.h
@@ -0,0 +1,574 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @defgroup SSI Synchronous Serial Interface (SSI) Driver
+ */
+
+ /*!
+ * @file ssi.h
+ * @brief This header file contains SSI driver functions prototypes.
+ *
+ * @ingroup SSI
+ */
+
+#ifndef __MXC_SSI_H__
+#define __MXC_SSI_H__
+
+#include "ssi_types.h"
+
+/*!
+ * This function gets the SSI fifo address.
+ *
+ * @param ssi ssi number
+ * @param direction To indicate playback / recording
+ * @return This function returns the SSI fifo address.
+ */
+unsigned int get_ssi_fifo_addr(unsigned int ssi, int direction);
+
+/*!
+ * This function controls the AC97 frame rate divider.
+ *
+ * @param module the module number
+ * @param frame_rate_divider the AC97 frame rate divider
+ */
+void ssi_ac97_frame_rate_divider(ssi_mod module,
+ unsigned char frame_rate_divider);
+
+/*!
+ * This function gets the AC97 command address register.
+ *
+ * @param module the module number
+ * @return This function returns the command address slot information.
+ */
+unsigned int ssi_ac97_get_command_address_register(ssi_mod module);
+
+/*!
+ * This function gets the AC97 command data register.
+ *
+ * @param module the module number
+ * @return This function returns the command data slot information.
+ */
+unsigned int ssi_ac97_get_command_data_register(ssi_mod module);
+
+/*!
+ * This function gets the AC97 tag register.
+ *
+ * @param module the module number
+ * @return This function returns the tag information.
+ */
+unsigned int ssi_ac97_get_tag_register(ssi_mod module);
+
+/*!
+ * This function controls the AC97 mode.
+ *
+ * @param module the module number
+ * @param state the AC97 mode state (enabled or disabled)
+ */
+void ssi_ac97_mode_enable(ssi_mod module, bool state);
+
+/*!
+ * This function controls the AC97 tag in FIFO behavior.
+ *
+ * @param module the module number
+ * @param state the tag in fifo behavior (Tag info stored in Rx FIFO 0 if TRUE,
+ * Tag info stored in SATAG register otherwise)
+ */
+void ssi_ac97_tag_in_fifo(ssi_mod module, bool state);
+
+/*!
+ * This function controls the AC97 read command.
+ *
+ * @param module the module number
+ * @param state the next AC97 command is a read command or not
+ */
+void ssi_ac97_read_command(ssi_mod module, bool state);
+
+/*!
+ * This function sets the AC97 command address register.
+ *
+ * @param module the module number
+ * @param address the command address slot information
+ */
+void ssi_ac97_set_command_address_register(ssi_mod module,
+ unsigned int address);
+
+/*!
+ * This function sets the AC97 command data register.
+ *
+ * @param module the module number
+ * @param data the command data slot information
+ */
+void ssi_ac97_set_command_data_register(ssi_mod module, unsigned int data);
+
+/*!
+ * This function sets the AC97 tag register.
+ *
+ * @param module the module number
+ * @param tag the tag information
+ */
+void ssi_ac97_set_tag_register(ssi_mod module, unsigned int tag);
+
+/*!
+ * This function controls the AC97 variable mode.
+ *
+ * @param module the module number
+ * @param state the AC97 variable mode state (enabled or disabled)
+ */
+void ssi_ac97_variable_mode(ssi_mod module, bool state);
+
+/*!
+ * This function controls the AC97 write command.
+ *
+ * @param module the module number
+ * @param state the next AC97 command is a write command or not
+ */
+void ssi_ac97_write_command(ssi_mod module, bool state);
+
+/*!
+ * This function controls the idle state of the transmit clock port during SSI internal gated mode.
+ *
+ * @param module the module number
+ * @param state the clock idle state
+ */
+void ssi_clock_idle_state(ssi_mod module, idle_state state);
+
+/*!
+ * This function turns off/on the ccm_ssi_clk to reduce power consumption.
+ *
+ * @param module the module number
+ * @param state the state for ccm_ssi_clk (true: turn off, else:turn on)
+ */
+void ssi_clock_off(ssi_mod module, bool state);
+
+/*!
+ * This function enables/disables the SSI module.
+ *
+ * @param module the module number
+ * @param state the state for SSI module
+ */
+void ssi_enable(ssi_mod module, bool state);
+
+/*!
+ * This function gets the data word in the Receive FIFO of the SSI module.
+ *
+ * @param module the module number
+ * @param fifo the Receive FIFO to read
+ * @return This function returns the read data.
+ */
+unsigned int ssi_get_data(ssi_mod module, fifo_nb fifo);
+
+/*!
+ * This function returns the status of the SSI module (SISR register) as a combination of status.
+ *
+ * @param module the module number
+ * @return This function returns the status of the SSI module.
+ */
+ssi_status_enable_mask ssi_get_status(ssi_mod module);
+
+/*!
+ * This function selects the I2S mode of the SSI module.
+ *
+ * @param module the module number
+ * @param mode the I2S mode
+ */
+void ssi_i2s_mode(ssi_mod module, mode_i2s mode);
+
+/*!
+ * This function disables the interrupts of the SSI module.
+ *
+ * @param module the module number
+ * @param mask the mask of the interrupts to disable
+ */
+void ssi_interrupt_disable(ssi_mod module, ssi_status_enable_mask mask);
+
+/*!
+ * This function enables the interrupts of the SSI module.
+ *
+ * @param module the module number
+ * @param mask the mask of the interrupts to enable
+ */
+void ssi_interrupt_enable(ssi_mod module, ssi_status_enable_mask mask);
+
+/*!
+ * This function enables/disables the network mode of the SSI module.
+ *
+ * @param module the module number
+ * @param state the network mode state
+ */
+void ssi_network_mode(ssi_mod module, bool state);
+
+/*!
+ * This function enables/disables the receive section of the SSI module.
+ *
+ * @param module the module number
+ * @param state the receive section state
+ */
+void ssi_receive_enable(ssi_mod module, bool state);
+
+/*!
+ * This function configures the SSI module to receive data word at bit position 0 or 23 in the Receive shift register.
+ *
+ * @param module the module number
+ * @param state the state to receive at bit 0
+ */
+void ssi_rx_bit0(ssi_mod module, bool state);
+
+/*!
+ * This function controls the source of the clock signal used to clock the Receive shift register.
+ *
+ * @param module the module number
+ * @param direction the clock signal direction
+ */
+void ssi_rx_clock_direction(ssi_mod module, ssi_tx_rx_direction direction);
+
+/*!
+ * This function configures the divide-by-two divider of the SSI module for the receive section.
+ *
+ * @param module the module number
+ * @param state the divider state
+ */
+void ssi_rx_clock_divide_by_two(ssi_mod module, bool state);
+
+/*!
+ * This function controls which bit clock edge is used to clock in data.
+ *
+ * @param module the module number
+ * @param polarity the clock polarity
+ */
+void ssi_rx_clock_polarity(ssi_mod module, ssi_tx_rx_clock_polarity polarity);
+
+/*!
+ * This function configures a fixed divide-by-eight clock prescaler divider of the SSI module in series with the variable prescaler for the receive section.
+ *
+ * @param module the module number
+ * @param state the prescaler state
+ */
+void ssi_rx_clock_prescaler(ssi_mod module, bool state);
+
+/*!
+ * This function controls the early frame sync configuration.
+ *
+ * @param module the module number
+ * @param early the early frame sync configuration
+ */
+void ssi_rx_early_frame_sync(ssi_mod module, ssi_tx_rx_early_frame_sync early);
+
+/*!
+ * This function gets the number of data words in the Receive FIFO.
+ *
+ * @param module the module number
+ * @param fifo the fifo
+ * @return This function returns the number of words in the Rx FIFO.
+ */
+unsigned char ssi_rx_fifo_counter(ssi_mod module, fifo_nb fifo);
+
+/*!
+ * This function enables the Receive FIFO.
+ *
+ * @param module the module number
+ * @param fifo the fifo to enable
+ * @param enabled the state of the fifo, enabled or disabled
+ */
+void ssi_rx_fifo_enable(ssi_mod module, fifo_nb fifo, bool enabled);
+
+/*!
+ * This function controls the threshold at which the RFFx flag will be set.
+ *
+ * @param module the module number
+ * @param fifo the fifo to enable
+ * @param watermark the watermark
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_fifo_full_watermark(ssi_mod module,
+ fifo_nb fifo, unsigned char watermark);
+
+/*!
+ * This function flushes the Receive FIFOs.
+ *
+ * @param module the module number
+ */
+void ssi_rx_flush_fifo(ssi_mod module);
+
+/*!
+ * This function controls the direction of the Frame Sync signal for the receive section.
+ *
+ * @param module the module number
+ * @param direction the Frame Sync signal direction
+ */
+void ssi_rx_frame_direction(ssi_mod module, ssi_tx_rx_direction direction);
+
+/*!
+ * This function configures the Receive frame rate divider for the receive section.
+ *
+ * @param module the module number
+ * @param ratio the divide ratio from 1 to 32
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_frame_rate(ssi_mod module, unsigned char ratio);
+
+/*!
+ * This function controls the Frame Sync active polarity for the receive section.
+ *
+ * @param module the module number
+ * @param active the Frame Sync active polarity
+ */
+void ssi_rx_frame_sync_active(ssi_mod module,
+ ssi_tx_rx_frame_sync_active active);
+
+/*!
+ * This function controls the Frame Sync length (one word or one bit long) for the receive section.
+ *
+ * @param module the module number
+ * @param length the Frame Sync length
+ */
+void ssi_rx_frame_sync_length(ssi_mod module,
+ ssi_tx_rx_frame_sync_length length);
+
+/*!
+ * This function configures the time slot(s) to mask for the receive section.
+ *
+ * @param module the module number
+ * @param mask the mask to indicate the time slot(s) masked
+ */
+void ssi_rx_mask_time_slot(ssi_mod module, unsigned int mask);
+
+/*!
+ * This function configures the Prescale divider for the receive section.
+ *
+ * @param module the module number
+ * @param divider the divide ratio from 1 to 256
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_rx_prescaler_modulus(ssi_mod module, unsigned int divider);
+
+/*!
+ * This function controls whether the MSB or LSB will be received first in a sample.
+ *
+ * @param module the module number
+ * @param direction the shift direction
+ */
+void ssi_rx_shift_direction(ssi_mod module,
+ ssi_tx_rx_shift_direction direction);
+
+/*!
+ * This function configures the Receive word length.
+ *
+ * @param module the module number
+ * @param length the word length
+ */
+void ssi_rx_word_length(ssi_mod module, ssi_word_length length);
+
+/*!
+ * This function sets the data word in the Transmit FIFO of the SSI module.
+ *
+ * @param module the module number
+ * @param fifo the FIFO number
+ * @param data the data to load in the FIFO
+ */
+void ssi_set_data(ssi_mod module, fifo_nb fifo, unsigned int data);
+
+/*!
+ * This function controls the number of wait states between the core and SSI.
+ *
+ * @param module the module number
+ * @param wait the number of wait state(s)
+ */
+void ssi_set_wait_states(ssi_mod module, ssi_wait_states wait);
+
+/*!
+ * This function enables/disables the synchronous mode of the SSI module.
+ *
+ * @param module the module number
+ * @param state the synchronous mode state
+ */
+void ssi_synchronous_mode(ssi_mod module, bool state);
+
+/*!
+ * This function allows the SSI module to output the SYS_CLK at the SRCK port.
+ *
+ * @param module the module number
+ * @param state the system clock state
+ */
+void ssi_system_clock(ssi_mod module, bool state);
+
+/*!
+ * This function enables/disables the transmit section of the SSI module.
+ *
+ * @param module the module number
+ * @param state the transmit section state
+ */
+void ssi_transmit_enable(ssi_mod module, bool state);
+
+/*!
+ * This function allows the SSI module to operate in the two channel mode.
+ *
+ * @param module the module number
+ * @param state the two channel mode state
+ */
+void ssi_two_channel_mode(ssi_mod module, bool state);
+
+/*!
+ * This function configures the SSI module to transmit data word from bit position 0 or 23 in the Transmit shift register.
+ *
+ * @param module the module number
+ * @param state the transmit from bit 0 state
+ */
+void ssi_tx_bit0(ssi_mod module, bool state);
+
+/*!
+ * This function controls the direction of the clock signal used to clock the Transmit shift register.
+ *
+ * @param module the module number
+ * @param direction the clock signal direction
+ */
+void ssi_tx_clock_direction(ssi_mod module, ssi_tx_rx_direction direction);
+
+/*!
+ * This function configures the divide-by-two divider of the SSI module for the transmit section.
+ *
+ * @param module the module number
+ * @param state the divider state
+ */
+void ssi_tx_clock_divide_by_two(ssi_mod module, bool state);
+
+/*!
+ * This function controls which bit clock edge is used to clock out data.
+ *
+ * @param module the module number
+ * @param polarity the clock polarity
+ */
+void ssi_tx_clock_polarity(ssi_mod module, ssi_tx_rx_clock_polarity polarity);
+
+/*!
+ * This function configures a fixed divide-by-eight clock prescaler divider of the SSI module in series with the variable prescaler for the transmit section.
+ *
+ * @param module the module number
+ * @param state the prescaler state
+ */
+void ssi_tx_clock_prescaler(ssi_mod module, bool state);
+
+/*!
+ * This function controls the early frame sync configuration for the transmit section.
+ *
+ * @param module the module number
+ * @param early the early frame sync configuration
+ */
+void ssi_tx_early_frame_sync(ssi_mod module, ssi_tx_rx_early_frame_sync early);
+
+/*!
+ * This function gets the number of data words in the Transmit FIFO.
+ *
+ * @param module the module number
+ * @param fifo the fifo
+ * @return This function returns the number of words in the Tx FIFO.
+ */
+unsigned char ssi_tx_fifo_counter(ssi_mod module, fifo_nb fifo);
+
+/*!
+ * This function controls the threshold at which the TFEx flag will be set.
+ *
+ * @param module the module number
+ * @param fifo the fifo to enable
+ * @param watermark the watermark
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_fifo_empty_watermark(ssi_mod module, fifo_nb fifo,
+ unsigned char watermark);
+
+/*!
+ * This function enables the Transmit FIFO.
+ *
+ * @param module the module number
+ * @param fifo the fifo to enable
+ * @param enable the state of the FIFO, enabled or disabled
+ */
+void ssi_tx_fifo_enable(ssi_mod module, fifo_nb fifo, bool enable);
+
+/*!
+ * This function flushes the Transmit FIFOs.
+ *
+ * @param module the module number
+ */
+void ssi_tx_flush_fifo(ssi_mod module);
+
+/*!
+ * This function controls the direction of the Frame Sync signal for the transmit section.
+ *
+ * @param module the module number
+ * @param direction the Frame Sync signal direction
+ */
+void ssi_tx_frame_direction(ssi_mod module, ssi_tx_rx_direction direction);
+
+/*!
+ * This function configures the Transmit frame rate divider.
+ *
+ * @param module the module number
+ * @param ratio the divide ratio from 1 to 32
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_frame_rate(ssi_mod module, unsigned char ratio);
+
+/*!
+ * This function controls the Frame Sync active polarity for the transmit section.
+ *
+ * @param module the module number
+ * @param active the Frame Sync active polarity
+ */
+void ssi_tx_frame_sync_active(ssi_mod module,
+ ssi_tx_rx_frame_sync_active active);
+
+/*!
+ * This function controls the Frame Sync length (one word or one bit long) for the transmit section.
+ *
+ * @param module the module number
+ * @param length the Frame Sync length
+ */
+void ssi_tx_frame_sync_length(ssi_mod module,
+ ssi_tx_rx_frame_sync_length length);
+
+/*!
+ * This function configures the time slot(s) to mask for the transmit section.
+ *
+ * @param module the module number
+ * @param mask the mask to indicate the time slot(s) masked
+ */
+void ssi_tx_mask_time_slot(ssi_mod module, unsigned int mask);
+
+/*!
+ * This function configures the Prescale divider for the transmit section.
+ *
+ * @param module the module number
+ * @param divider the divide ratio from 1 to 256
+ * @return This function returns the result of the operation (0 if successful, -1 otherwise).
+ */
+int ssi_tx_prescaler_modulus(ssi_mod module, unsigned int divider);
+
+/*!
+ * This function controls whether the MSB or LSB will be transmited first in a sample.
+ *
+ * @param module the module number
+ * @param direction the shift direction
+ */
+void ssi_tx_shift_direction(ssi_mod module,
+ ssi_tx_rx_shift_direction direction);
+
+/*!
+ * This function configures the Transmit word length.
+ *
+ * @param module the module number
+ * @param length the word length
+ */
+void ssi_tx_word_length(ssi_mod module, ssi_word_length length);
+
+#endif /* __MXC_SSI_H__ */
diff --git a/drivers/mxc/ssi/ssi_types.h b/drivers/mxc/ssi/ssi_types.h
new file mode 100644
index 000000000000..015e65a6d2d2
--- /dev/null
+++ b/drivers/mxc/ssi/ssi_types.h
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @file ssi_types.h
+ * @brief This header file contains SSI types.
+ *
+ * @ingroup SSI
+ */
+
+#ifndef __MXC_SSI_TYPES_H__
+#define __MXC_SSI_TYPES_H__
+
+/*!
+ * This enumeration describes the FIFO number.
+ */
+typedef enum {
+ /*!
+ * FIFO 0
+ */
+ ssi_fifo_0 = 0,
+ /*!
+ * FIFO 1
+ */
+ ssi_fifo_1 = 1
+} fifo_nb;
+
+/*!
+ * This enumeration describes the clock idle state.
+ */
+typedef enum {
+ /*!
+ * Clock idle state is 1
+ */
+ clock_idle_state_1 = 0,
+ /*!
+ * Clock idle state is 0
+ */
+ clock_idle_state_0 = 1
+} idle_state;
+
+/*!
+ * This enumeration describes I2S mode.
+ */
+typedef enum {
+ /*!
+ * Normal mode
+ */
+ i2s_normal = 0,
+ /*!
+ * Master mode
+ */
+ i2s_master = 1,
+ /*!
+ * Slave mode
+ */
+ i2s_slave = 2
+} mode_i2s;
+
+/*!
+ * This enumeration describes index for both SSI1 and SSI2 modules.
+ */
+typedef enum {
+ /*!
+ * SSI1 index
+ */
+ SSI1 = 0,
+ /*!
+ * SSI2 index not present on MXC 91221 and MXC91311
+ */
+ SSI2 = 1
+} ssi_mod;
+
+/*!
+ * This enumeration describes the status/enable bits for interrupt source of the SSI module.
+ */
+typedef enum {
+ /*!
+ * SSI Transmit FIFO 0 empty bit
+ */
+ ssi_tx_fifo_0_empty = 0x00000001,
+ /*!
+ * SSI Transmit FIFO 1 empty bit
+ */
+ ssi_tx_fifo_1_empty = 0x00000002,
+ /*!
+ * SSI Receive FIFO 0 full bit
+ */
+ ssi_rx_fifo_0_full = 0x00000004,
+ /*!
+ * SSI Receive FIFO 1 full bit
+ */
+ ssi_rx_fifo_1_full = 0x00000008,
+ /*!
+ * SSI Receive Last Time Slot bit
+ */
+ ssi_rls = 0x00000010,
+ /*!
+ * SSI Transmit Last Time Slot bit
+ */
+ ssi_tls = 0x00000020,
+ /*!
+ * SSI Receive Frame Sync bit
+ */
+ ssi_rfs = 0x00000040,
+ /*!
+ * SSI Transmit Frame Sync bit
+ */
+ ssi_tfs = 0x00000080,
+ /*!
+ * SSI Transmitter underrun 0 bit
+ */
+ ssi_transmitter_underrun_0 = 0x00000100,
+ /*!
+ * SSI Transmitter underrun 1 bit
+ */
+ ssi_transmitter_underrun_1 = 0x00000200,
+ /*!
+ * SSI Receiver overrun 0 bit
+ */
+ ssi_receiver_overrun_0 = 0x00000400,
+ /*!
+ * SSI Receiver overrun 1 bit
+ */
+ ssi_receiver_overrun_1 = 0x00000800,
+ /*!
+ * SSI Transmit Data register empty 0 bit
+ */
+ ssi_tx_data_reg_empty_0 = 0x00001000,
+ /*!
+ * SSI Transmit Data register empty 1 bit
+ */
+ ssi_tx_data_reg_empty_1 = 0x00002000,
+
+ /*!
+ * SSI Receive Data Ready 0 bit
+ */
+ ssi_rx_data_ready_0 = 0x00004000,
+ /*!
+ * SSI Receive Data Ready 1 bit
+ */
+ ssi_rx_data_ready_1 = 0x00008000,
+ /*!
+ * SSI Receive tag updated bit
+ */
+ ssi_rx_tag_updated = 0x00010000,
+ /*!
+ * SSI Command data register updated bit
+ */
+ ssi_cmd_data_reg_updated = 0x00020000,
+ /*!
+ * SSI Command address register updated bit
+ */
+ ssi_cmd_address_reg_updated = 0x00040000,
+ /*!
+ * SSI Transmit interrupt enable bit
+ */
+ ssi_tx_interrupt_enable = 0x00080000,
+ /*!
+ * SSI Transmit DMA enable bit
+ */
+ ssi_tx_dma_interrupt_enable = 0x00100000,
+ /*!
+ * SSI Receive interrupt enable bit
+ */
+ ssi_rx_interrupt_enable = 0x00200000,
+ /*!
+ * SSI Receive DMA enable bit
+ */
+ ssi_rx_dma_interrupt_enable = 0x00400000,
+ /*!
+ * SSI Tx frame complete enable bit on MXC91221 & MXC91311 only
+ */
+ ssi_tx_frame_complete = 0x00800000,
+ /*!
+ * SSI Rx frame complete on MXC91221 & MXC91311 only
+ */
+ ssi_rx_frame_complete = 0x001000000
+} ssi_status_enable_mask;
+
+/*!
+ * This enumeration describes the clock edge to clock in or clock out data.
+ */
+typedef enum {
+ /*!
+ * Clock on rising edge
+ */
+ ssi_clock_on_rising_edge = 0,
+ /*!
+ * Clock on falling edge
+ */
+ ssi_clock_on_falling_edge = 1
+} ssi_tx_rx_clock_polarity;
+
+/*!
+ * This enumeration describes the clock direction.
+ */
+typedef enum {
+ /*!
+ * Clock is external
+ */
+ ssi_tx_rx_externally = 0,
+ /*!
+ * Clock is generated internally
+ */
+ ssi_tx_rx_internally = 1
+} ssi_tx_rx_direction;
+
+/*!
+ * This enumeration describes the early frame sync behavior.
+ */
+typedef enum {
+ /*!
+ * Frame Sync starts on the first data bit
+ */
+ ssi_frame_sync_first_bit = 0,
+ /*!
+ * Frame Sync starts one bit before the first data bit
+ */
+ ssi_frame_sync_one_bit_before = 1
+} ssi_tx_rx_early_frame_sync;
+
+/*!
+ * This enumeration describes the Frame Sync active value.
+ */
+typedef enum {
+ /*!
+ * Frame Sync is active when high
+ */
+ ssi_frame_sync_active_high = 0,
+ /*!
+ * Frame Sync is active when low
+ */
+ ssi_frame_sync_active_low = 1
+} ssi_tx_rx_frame_sync_active;
+
+/*!
+ * This enumeration describes the Frame Sync active length.
+ */
+typedef enum {
+ /*!
+ * Frame Sync is active when high
+ */
+ ssi_frame_sync_one_word = 0,
+ /*!
+ * Frame Sync is active when low
+ */
+ ssi_frame_sync_one_bit = 1
+} ssi_tx_rx_frame_sync_length;
+
+/*!
+ * This enumeration describes the Tx/Rx frame shift direction.
+ */
+typedef enum {
+ /*!
+ * MSB first
+ */
+ ssi_msb_first = 0,
+ /*!
+ * LSB first
+ */
+ ssi_lsb_first = 1
+} ssi_tx_rx_shift_direction;
+
+/*!
+ * This enumeration describes the wait state number.
+ */
+typedef enum {
+ /*!
+ * 0 wait state
+ */
+ ssi_waitstates0 = 0x0,
+ /*!
+ * 1 wait state
+ */
+ ssi_waitstates1 = 0x1,
+ /*!
+ * 2 wait states
+ */
+ ssi_waitstates2 = 0x2,
+ /*!
+ * 3 wait states
+ */
+ ssi_waitstates3 = 0x3
+} ssi_wait_states;
+
+/*!
+ * This enumeration describes the word length.
+ */
+typedef enum {
+ /*!
+ * 2 bits long
+ */
+ ssi_2_bits = 0x0,
+ /*!
+ * 4 bits long
+ */
+ ssi_4_bits = 0x1,
+ /*!
+ * 6 bits long
+ */
+ ssi_6_bits = 0x2,
+ /*!
+ * 8 bits long
+ */
+ ssi_8_bits = 0x3,
+ /*!
+ * 10 bits long
+ */
+ ssi_10_bits = 0x4,
+ /*!
+ * 12 bits long
+ */
+ ssi_12_bits = 0x5,
+ /*!
+ * 14 bits long
+ */
+ ssi_14_bits = 0x6,
+ /*!
+ * 16 bits long
+ */
+ ssi_16_bits = 0x7,
+ /*!
+ * 18 bits long
+ */
+ ssi_18_bits = 0x8,
+ /*!
+ * 20 bits long
+ */
+ ssi_20_bits = 0x9,
+ /*!
+ * 22 bits long
+ */
+ ssi_22_bits = 0xA,
+ /*!
+ * 24 bits long
+ */
+ ssi_24_bits = 0xB,
+ /*!
+ * 26 bits long
+ */
+ ssi_26_bits = 0xC,
+ /*!
+ * 28 bits long
+ */
+ ssi_28_bits = 0xD,
+ /*!
+ * 30 bits long
+ */
+ ssi_30_bits = 0xE,
+ /*!
+ * 32 bits long
+ */
+ ssi_32_bits = 0xF
+} ssi_word_length;
+
+#endif /* __MXC_SSI_TYPES_H__ */
diff --git a/drivers/mxc/vpu/Kconfig b/drivers/mxc/vpu/Kconfig
new file mode 100644
index 000000000000..e41974591b86
--- /dev/null
+++ b/drivers/mxc/vpu/Kconfig
@@ -0,0 +1,30 @@
+#
+# Codec configuration
+#
+
+menu "MXC VPU(Video Processing Unit) support"
+
+config MXC_VPU
+ tristate "Support for MXC VPU(Video Processing Unit)"
+ depends on (ARCH_MX3 || ARCH_MX27 || ARCH_MXC92323 || ARCH_MX37 || ARCH_MX51)
+ default y
+ ---help---
+ The VPU codec device provides codec function for H.264/MPEG4/H.263,
+ as well as MPEG2/VC-1/DivX on some platforms.
+
+config MXC_VPU_IRAM
+ tristate "Use IRAM as temporary buffer for VPU to enhance performace"
+ depends on (ARCH_MX37 || ARCH_MX51)
+ default y
+ ---help---
+ The VPU can use internal RAM as temporary buffer to save external
+ memroy bandwith, thus to enhance video performance.
+
+config MXC_VPU_DEBUG
+ bool "MXC VPU debugging"
+ depends on MXC_VPU != n
+ help
+ This is an option for the developers; most people should
+ say N here. This enables MXC VPU driver debugging.
+
+endmenu
diff --git a/drivers/mxc/vpu/Makefile b/drivers/mxc/vpu/Makefile
new file mode 100644
index 000000000000..88e8f2c084a0
--- /dev/null
+++ b/drivers/mxc/vpu/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the VPU drivers.
+#
+
+obj-$(CONFIG_MXC_VPU) += vpu.o
+vpu-objs := mxc_vpu.o mxc_vl2cc.o
+
+ifeq ($(CONFIG_MXC_VPU_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/mxc/vpu/mxc_vl2cc.c b/drivers/mxc/vpu/mxc_vl2cc.c
new file mode 100644
index 000000000000..acc40dd01689
--- /dev/null
+++ b/drivers/mxc/vpu/mxc_vl2cc.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_vl2cc.c
+ *
+ * @brief VL2CC initialization and flush operation implementation
+ *
+ * @ingroup VL2CC
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+
+#define VL2CC_CTRL_OFFSET (0x100)
+#define VL2CC_AUXCTRL_OFFSET (0x104)
+#define VL2CC_INVWAY_OFFSET (0x77C)
+#define VL2CC_CLEANWAY_OFFSET (0x7BC)
+
+/*! VL2CC clock handle. */
+static struct clk *vl2cc_clk;
+static u32 *vl2cc_base;
+
+/*!
+ * Initialization function of VL2CC. Remap the VL2CC base address.
+ *
+ * @return status 0 success.
+ */
+int vl2cc_init(u32 vl2cc_hw_base)
+{
+ vl2cc_base = ioremap(vl2cc_hw_base, SZ_8K - 1);
+ if (vl2cc_base == NULL) {
+ printk(KERN_INFO "vl2cc: Unable to ioremap\n");
+ return -ENOMEM;
+ }
+
+ vl2cc_clk = clk_get(NULL, "vl2cc_clk");
+ if (IS_ERR(vl2cc_clk)) {
+ printk(KERN_INFO "vl2cc: Unable to get clock\n");
+ iounmap(vl2cc_base);
+ return -EIO;
+ }
+
+ printk(KERN_INFO "VL2CC initialized\n");
+ return 0;
+}
+
+/*!
+ * Enable VL2CC hardware
+ */
+void vl2cc_enable(void)
+{
+ volatile u32 reg;
+
+ clk_enable(vl2cc_clk);
+
+ /* Disable VL2CC */
+ reg = __raw_readl(vl2cc_base + VL2CC_CTRL_OFFSET);
+ reg &= 0xFFFFFFFE;
+ __raw_writel(reg, vl2cc_base + VL2CC_CTRL_OFFSET);
+
+ /* Set the latency for data RAM reads, data RAM writes, tag RAM and
+ * dirty RAM to 1 cycle - write 0x0 to AUX CTRL [11:0] and also
+ * configure the number of ways to 8 - write 8 to AUX CTRL [16:13]
+ */
+ reg = __raw_readl(vl2cc_base + VL2CC_AUXCTRL_OFFSET);
+ reg &= 0xFFFE1000; /* Clear [16:13] too */
+ reg |= (0x8 << 13); /* [16:13] = 8; */
+ __raw_writel(reg, vl2cc_base + VL2CC_AUXCTRL_OFFSET);
+
+ /* Invalidate the VL2CC ways - write 0xff to INV BY WAY and poll the
+ * register until its value is 0x0
+ */
+ __raw_writel(0xff, vl2cc_base + VL2CC_INVWAY_OFFSET);
+ while (__raw_readl(vl2cc_base + VL2CC_INVWAY_OFFSET) != 0x0) ;
+
+ /* Enable VL2CC */
+ reg = __raw_readl(vl2cc_base + VL2CC_CTRL_OFFSET);
+ reg |= 0x1;
+ __raw_writel(reg, vl2cc_base + VL2CC_CTRL_OFFSET);
+}
+
+/*!
+ * Flush VL2CC
+ */
+void vl2cc_flush(void)
+{
+ __raw_writel(0xff, vl2cc_base + VL2CC_CLEANWAY_OFFSET);
+ while (__raw_readl(vl2cc_base + VL2CC_CLEANWAY_OFFSET) != 0x0) ;
+}
+
+/*!
+ * Disable VL2CC
+ */
+void vl2cc_disable(void)
+{
+ __raw_writel(0, vl2cc_base + VL2CC_CTRL_OFFSET);
+ clk_disable(vl2cc_clk);
+}
+
+/*!
+ * Cleanup VL2CC
+ */
+void vl2cc_cleanup(void)
+{
+ clk_put(vl2cc_clk);
+ iounmap(vl2cc_base);
+}
diff --git a/drivers/mxc/vpu/mxc_vpu.c b/drivers/mxc/vpu/mxc_vpu.c
new file mode 100644
index 000000000000..ffc31b5ba7fa
--- /dev/null
+++ b/drivers/mxc/vpu/mxc_vpu.c
@@ -0,0 +1,847 @@
+/*
+ * Copyright 2006-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_vpu.c
+ *
+ * @brief VPU system initialization and file operation implementation
+ *
+ * @ingroup VPU
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/autoconf.h>
+#include <linux/ioport.h>
+#include <linux/stat.h>
+#include <linux/platform_device.h>
+#include <linux/kdev_t.h>
+#include <linux/dma-mapping.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+#include <asm/dma-mapping.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+
+#include <mach/mxc_vpu.h>
+
+struct vpu_priv {
+ struct fasync_struct *async_queue;
+};
+
+/* To track the allocated memory buffer */
+typedef struct memalloc_record {
+ struct list_head list;
+ struct vpu_mem_desc mem;
+} memalloc_record;
+
+struct iram_setting {
+ u32 start;
+ u32 end;
+};
+
+static DEFINE_SPINLOCK(vpu_lock);
+static LIST_HEAD(head);
+
+static int vpu_major = 0;
+static int vpu_clk_usercount;
+static struct class *vpu_class;
+static struct vpu_priv vpu_data;
+static u8 open_count = 0;
+static struct clk *vpu_clk;
+static struct vpu_mem_desc bitwork_mem = { 0 };
+static struct vpu_mem_desc pic_para_mem = { 0 };
+static struct vpu_mem_desc user_data_mem = { 0 };
+static struct vpu_mem_desc share_mem = { 0 };
+
+/* IRAM setting */
+static struct iram_setting iram;
+/* store SRC base addr */
+static u32 src_base_addr;
+
+/* implement the blocking ioctl */
+static int codec_done = 0;
+static wait_queue_head_t vpu_queue;
+
+static u32 workctrl_regsave[6];
+static u32 rd_ptr_regsave[4];
+static u32 wr_ptr_regsave[4];
+static u32 dis_flag_regsave[4];
+
+#define READ_REG(x) __raw_readl(IO_ADDRESS(VPU_BASE_ADDR+(x)))
+#define WRITE_REG(val, x) \
+ __raw_writel((val), IO_ADDRESS(VPU_BASE_ADDR+(x)))
+
+#define SAVE_WORK_REGS do { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(workctrl_regsave)/2; i++) \
+ workctrl_regsave[i] = READ_REG(BIT_WORK_CTRL_BUF_REG(i));\
+} while (0)
+#define RESTORE_WORK_REGS do { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(workctrl_regsave)/2; i++) \
+ WRITE_REG(workctrl_regsave[i], BIT_WORK_CTRL_BUF_REG(i));\
+} while (0)
+#define SAVE_CTRL_REGS do { \
+ int i; \
+ for (i = ARRAY_SIZE(workctrl_regsave)/2; \
+ i < ARRAY_SIZE(workctrl_regsave); i++) \
+ workctrl_regsave[i] = READ_REG(BIT_WORK_CTRL_BUF_REG(i));\
+} while (0)
+#define RESTORE_CTRL_REGS do { \
+ int i; \
+ for (i = ARRAY_SIZE(workctrl_regsave)/2; \
+ i < ARRAY_SIZE(workctrl_regsave); i++) \
+ WRITE_REG(workctrl_regsave[i], BIT_WORK_CTRL_BUF_REG(i));\
+} while (0)
+#define SAVE_RDWR_PTR_REGS do { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(rd_ptr_regsave); i++) \
+ rd_ptr_regsave[i] = READ_REG(BIT_RD_PTR_REG(i)); \
+ for (i = 0; i < ARRAY_SIZE(wr_ptr_regsave); i++) \
+ wr_ptr_regsave[i] = READ_REG(BIT_WR_PTR_REG(i)); \
+} while (0)
+#define RESTORE_RDWR_PTR_REGS do { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(rd_ptr_regsave); i++) \
+ WRITE_REG(rd_ptr_regsave[i], BIT_RD_PTR_REG(i)); \
+ for (i = 0; i < ARRAY_SIZE(wr_ptr_regsave); i++) \
+ WRITE_REG(wr_ptr_regsave[i], BIT_WR_PTR_REG(i)); \
+} while (0)
+#define SAVE_DIS_FLAG_REGS do { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(dis_flag_regsave); i++) \
+ dis_flag_regsave[i] = READ_REG(BIT_FRM_DIS_FLG_REG(i)); \
+} while (0)
+#define RESTORE_DIS_FLAG_REGS do { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(dis_flag_regsave); i++) \
+ WRITE_REG(dis_flag_regsave[i], BIT_FRM_DIS_FLG_REG(i)); \
+} while (0)
+
+/*!
+ * Private function to alloc dma buffer
+ * @return status 0 success.
+ */
+static int vpu_alloc_dma_buffer(struct vpu_mem_desc *mem)
+{
+ mem->cpu_addr = (unsigned long)
+ dma_alloc_coherent(NULL, PAGE_ALIGN(mem->size),
+ (dma_addr_t *) (&mem->phy_addr),
+ GFP_DMA | GFP_KERNEL);
+ pr_debug("[ALLOC] mem alloc cpu_addr = 0x%x\n", mem->cpu_addr);
+ if ((void *)(mem->cpu_addr) == NULL) {
+ printk(KERN_ERR "Physical memory allocation error!\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*!
+ * Private function to free dma buffer
+ */
+static void vpu_free_dma_buffer(struct vpu_mem_desc *mem)
+{
+ if (mem->cpu_addr != 0) {
+ dma_free_coherent(0, PAGE_ALIGN(mem->size),
+ (void *)mem->cpu_addr, mem->phy_addr);
+ }
+}
+
+/*!
+ * Private function to free buffers
+ * @return status 0 success.
+ */
+static int vpu_free_buffers(void)
+{
+ struct memalloc_record *rec, *n;
+ struct vpu_mem_desc mem;
+
+ list_for_each_entry_safe(rec, n, &head, list) {
+ mem = rec->mem;
+ if (mem.cpu_addr != 0) {
+ vpu_free_dma_buffer(&mem);
+ pr_debug("[FREE] freed paddr=0x%08X\n", mem.phy_addr);
+ /* delete from list */
+ list_del(&rec->list);
+ kfree(rec);
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief vpu interrupt handler
+ */
+static irqreturn_t vpu_irq_handler(int irq, void *dev_id)
+{
+ struct vpu_priv *dev = dev_id;
+
+ READ_REG(BIT_INT_STATUS);
+ WRITE_REG(0x1, BIT_INT_CLEAR);
+
+ if (dev->async_queue)
+ kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
+
+ /*
+ * Clock is gated on when dec/enc started, gate it off when
+ * interrupt is received.
+ */
+ clk_disable(vpu_clk);
+
+ codec_done = 1;
+ wake_up_interruptible(&vpu_queue);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * @brief open function for vpu file operation
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_open(struct inode *inode, struct file *filp)
+{
+ spin_lock(&vpu_lock);
+ if ((open_count++ == 0) && cpu_is_mx32())
+ vl2cc_enable();
+ filp->private_data = (void *)(&vpu_data);
+ spin_unlock(&vpu_lock);
+ return 0;
+}
+
+/*!
+ * @brief IO ctrl function for vpu file operation
+ * @param cmd IO ctrl command
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_ioctl(struct inode *inode, struct file *filp, u_int cmd,
+ u_long arg)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case VPU_IOC_PHYMEM_ALLOC:
+ {
+ struct memalloc_record *rec;
+
+ rec = kzalloc(sizeof(*rec), GFP_KERNEL);
+ if (!rec)
+ return -ENOMEM;
+
+ ret = copy_from_user(&(rec->mem),
+ (struct vpu_mem_desc *)arg,
+ sizeof(struct vpu_mem_desc));
+ if (ret) {
+ kfree(rec);
+ return -EFAULT;
+ }
+
+ pr_debug("[ALLOC] mem alloc size = 0x%x\n",
+ rec->mem.size);
+
+ ret = vpu_alloc_dma_buffer(&(rec->mem));
+ if (ret == -1) {
+ kfree(rec);
+ printk(KERN_ERR
+ "Physical memory allocation error!\n");
+ break;
+ }
+ ret = copy_to_user((void __user *)arg, &(rec->mem),
+ sizeof(struct vpu_mem_desc));
+ if (ret) {
+ kfree(rec);
+ ret = -EFAULT;
+ break;
+ }
+
+ spin_lock(&vpu_lock);
+ list_add(&rec->list, &head);
+ spin_unlock(&vpu_lock);
+
+ break;
+ }
+ case VPU_IOC_PHYMEM_FREE:
+ {
+ struct memalloc_record *rec, *n;
+ struct vpu_mem_desc vpu_mem;
+
+ ret = copy_from_user(&vpu_mem,
+ (struct vpu_mem_desc *)arg,
+ sizeof(struct vpu_mem_desc));
+ if (ret)
+ return -EACCES;
+
+ pr_debug("[FREE] mem freed cpu_addr = 0x%x\n",
+ vpu_mem.cpu_addr);
+ if ((void *)vpu_mem.cpu_addr != NULL) {
+ vpu_free_dma_buffer(&vpu_mem);
+ }
+
+ spin_lock(&vpu_lock);
+ list_for_each_entry_safe(rec, n, &head, list) {
+ if (rec->mem.cpu_addr == vpu_mem.cpu_addr) {
+ /* delete from list */
+ list_del(&rec->list);
+ kfree(rec);
+ break;
+ }
+ }
+ spin_unlock(&vpu_lock);
+
+ break;
+ }
+ case VPU_IOC_WAIT4INT:
+ {
+ u_long timeout = (u_long) arg;
+ if (!wait_event_interruptible_timeout
+ (vpu_queue, codec_done != 0,
+ msecs_to_jiffies(timeout))) {
+ printk(KERN_WARNING "VPU blocking: timeout.\n");
+ ret = -ETIME;
+ } else if (signal_pending(current)) {
+ printk(KERN_WARNING
+ "VPU interrupt received.\n");
+ ret = -ERESTARTSYS;
+ } else
+ codec_done = 0;
+ break;
+ }
+ case VPU_IOC_VL2CC_FLUSH:
+ if (cpu_is_mx32()) {
+ vl2cc_flush();
+ }
+ break;
+ case VPU_IOC_IRAM_SETTING:
+ {
+ ret = copy_to_user((void __user *)arg, &iram,
+ sizeof(struct iram_setting));
+ if (ret)
+ ret = -EFAULT;
+
+ break;
+ }
+ case VPU_IOC_CLKGATE_SETTING:
+ {
+ u32 clkgate_en;
+
+ if (get_user(clkgate_en, (u32 __user *) arg))
+ return -EFAULT;
+
+ if (clkgate_en) {
+ clk_enable(vpu_clk);
+ } else {
+ clk_disable(vpu_clk);
+ }
+
+ break;
+ }
+ case VPU_IOC_GET_SHARE_MEM:
+ {
+ spin_lock(&vpu_lock);
+ if (share_mem.cpu_addr != 0) {
+ ret = copy_to_user((void __user *)arg,
+ &share_mem,
+ sizeof(struct vpu_mem_desc));
+ spin_unlock(&vpu_lock);
+ break;
+ } else {
+ if (copy_from_user(&share_mem,
+ (struct vpu_mem_desc *)arg,
+ sizeof(struct vpu_mem_desc))) {
+ spin_unlock(&vpu_lock);
+ return -EFAULT;
+ }
+ if (vpu_alloc_dma_buffer(&share_mem) == -1)
+ ret = -EFAULT;
+ else {
+ if (copy_to_user((void __user *)arg,
+ &share_mem,
+ sizeof(struct
+ vpu_mem_desc)))
+ ret = -EFAULT;
+ }
+ }
+ spin_unlock(&vpu_lock);
+ break;
+ }
+ case VPU_IOC_GET_WORK_ADDR:
+ {
+ if (bitwork_mem.cpu_addr != 0) {
+ ret =
+ copy_to_user((void __user *)arg,
+ &bitwork_mem,
+ sizeof(struct vpu_mem_desc));
+ break;
+ } else {
+ if (copy_from_user(&bitwork_mem,
+ (struct vpu_mem_desc *)arg,
+ sizeof(struct vpu_mem_desc)))
+ return -EFAULT;
+
+ if (vpu_alloc_dma_buffer(&bitwork_mem) == -1)
+ ret = -EFAULT;
+ else if (copy_to_user((void __user *)arg,
+ &bitwork_mem,
+ sizeof(struct
+ vpu_mem_desc)))
+ ret = -EFAULT;
+ }
+ break;
+ }
+ case VPU_IOC_GET_PIC_PARA_ADDR:
+ {
+ if (pic_para_mem.cpu_addr != 0) {
+ ret =
+ copy_to_user((void __user *)arg,
+ &pic_para_mem,
+ sizeof(struct vpu_mem_desc));
+ break;
+ } else {
+ if (copy_from_user(&pic_para_mem,
+ (struct vpu_mem_desc *)arg,
+ sizeof(struct vpu_mem_desc)))
+ return -EFAULT;
+
+ if (vpu_alloc_dma_buffer(&pic_para_mem) == -1)
+ ret = -EFAULT;
+ else if (copy_to_user((void __user *)arg,
+ &pic_para_mem,
+ sizeof(struct
+ vpu_mem_desc)))
+ ret = -EFAULT;
+ }
+ break;
+ }
+ case VPU_IOC_GET_USER_DATA_ADDR:
+ {
+ if (user_data_mem.cpu_addr != 0) {
+ ret =
+ copy_to_user((void __user *)arg,
+ &user_data_mem,
+ sizeof(struct vpu_mem_desc));
+ break;
+ } else {
+ if (copy_from_user(&user_data_mem,
+ (struct vpu_mem_desc *)arg,
+ sizeof(struct vpu_mem_desc)))
+ return -EFAULT;
+
+ if (vpu_alloc_dma_buffer(&user_data_mem) == -1)
+ ret = -EFAULT;
+ else if (copy_to_user((void __user *)arg,
+ &user_data_mem,
+ sizeof(struct
+ vpu_mem_desc)))
+ ret = -EFAULT;
+ }
+ break;
+ }
+ case VPU_IOC_SYS_SW_RESET:
+ {
+ if (cpu_is_mx37() || cpu_is_mx51()) {
+ u32 reg;
+
+ reg = __raw_readl(src_base_addr);
+ reg |= 0x02; /* SW_VPU_RST_BIT */
+ __raw_writel(reg, src_base_addr);
+ while (__raw_readl(src_base_addr) & 0x02)
+ ;
+ }
+ break;
+ }
+ case VPU_IOC_REG_DUMP:
+ break;
+ case VPU_IOC_PHYMEM_DUMP:
+ break;
+ default:
+ {
+ printk(KERN_ERR "No such IOCTL, cmd is %d\n", cmd);
+ break;
+ }
+ }
+ return ret;
+}
+
+/*!
+ * @brief Release function for vpu file operation
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_release(struct inode *inode, struct file *filp)
+{
+ spin_lock(&vpu_lock);
+ if (open_count > 0 && !(--open_count)) {
+ vpu_free_buffers();
+
+ if (cpu_is_mx32())
+ vl2cc_disable();
+
+ /* Free shared memory when vpu device is idle */
+ vpu_free_dma_buffer(&share_mem);
+ share_mem.cpu_addr = 0;
+ }
+ spin_unlock(&vpu_lock);
+
+ return 0;
+}
+
+/*!
+ * @brief fasync function for vpu file operation
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_fasync(int fd, struct file *filp, int mode)
+{
+ struct vpu_priv *dev = (struct vpu_priv *)filp->private_data;
+ return fasync_helper(fd, filp, mode, &dev->async_queue);
+}
+
+/*!
+ * @brief memory map function of harware registers for vpu file operation
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_map_hwregs(struct file *fp, struct vm_area_struct *vm)
+{
+ unsigned long pfn;
+
+ vm->vm_flags |= VM_IO | VM_RESERVED;
+ vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot);
+ pfn = VPU_BASE_ADDR >> PAGE_SHIFT;
+ pr_debug("size=0x%x, page no.=0x%x\n",
+ (int)(vm->vm_end - vm->vm_start), (int)pfn);
+ return remap_pfn_range(vm, vm->vm_start, pfn, vm->vm_end - vm->vm_start,
+ vm->vm_page_prot) ? -EAGAIN : 0;
+}
+
+/*!
+ * @brief memory map function of memory for vpu file operation
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_map_mem(struct file *fp, struct vm_area_struct *vm)
+{
+ int request_size;
+ request_size = vm->vm_end - vm->vm_start;
+
+ pr_debug(" start=0x%x, pgoff=0x%x, size=0x%x\n",
+ (unsigned int)(vm->vm_start), (unsigned int)(vm->vm_pgoff),
+ request_size);
+
+ vm->vm_flags |= VM_IO | VM_RESERVED;
+ vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot);
+
+ return remap_pfn_range(vm, vm->vm_start, vm->vm_pgoff,
+ request_size, vm->vm_page_prot) ? -EAGAIN : 0;
+
+}
+
+/*!
+ * @brief memory map interface for vpu file operation
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_mmap(struct file *fp, struct vm_area_struct *vm)
+{
+ if (vm->vm_pgoff)
+ return vpu_map_mem(fp, vm);
+ else
+ return vpu_map_hwregs(fp, vm);
+}
+
+struct file_operations vpu_fops = {
+ .owner = THIS_MODULE,
+ .open = vpu_open,
+ .ioctl = vpu_ioctl,
+ .release = vpu_release,
+ .fasync = vpu_fasync,
+ .mmap = vpu_mmap,
+};
+
+/*!
+ * This function is called by the driver framework to initialize the vpu device.
+ * @param dev The device structure for the vpu passed in by the framework.
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_dev_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct device *temp_class;
+ struct resource *res;
+
+ if (cpu_is_mx32()) {
+ /* Obtain VL2CC base address */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ printk(KERN_ERR "vpu: unable to get VL2CC base\n");
+ return -ENODEV;
+ }
+
+ err = vl2cc_init(res->start);
+ if (err != 0)
+ return err;
+ }
+
+ if (cpu_is_mx37() || cpu_is_mx51()) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ printk(KERN_ERR "vpu: unable to get VPU IRAM base\n");
+ return -ENODEV;
+ }
+ iram.start = res->start;
+ iram.end = res->end;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ printk(KERN_ERR "vpu: unable to get src base addr\n");
+ return -ENODEV;
+ }
+ src_base_addr = res->start;
+ }
+
+ vpu_major = register_chrdev(vpu_major, "mxc_vpu", &vpu_fops);
+ if (vpu_major < 0) {
+ printk(KERN_ERR "vpu: unable to get a major for VPU\n");
+ err = -EBUSY;
+ goto error;
+ }
+
+ vpu_class = class_create(THIS_MODULE, "mxc_vpu");
+ if (IS_ERR(vpu_class)) {
+ err = PTR_ERR(vpu_class);
+ goto err_out_chrdev;
+ }
+
+ temp_class = device_create(vpu_class, NULL, MKDEV(vpu_major, 0),
+ NULL, "mxc_vpu");
+ if (IS_ERR(temp_class)) {
+ err = PTR_ERR(temp_class);
+ goto err_out_class;
+ }
+
+ vpu_clk = clk_get(&pdev->dev, "vpu_clk");
+ if (IS_ERR(vpu_clk)) {
+ err = -ENOENT;
+ goto err_out_class;
+ }
+
+ err = request_irq(MXC_INT_VPU, vpu_irq_handler, 0, "VPU_CODEC_IRQ",
+ (void *)(&vpu_data));
+ if (err)
+ goto err_out_class;
+
+ printk(KERN_INFO "VPU initialized\n");
+ goto out;
+
+ err_out_class:
+ device_destroy(vpu_class, MKDEV(vpu_major, 0));
+ class_destroy(vpu_class);
+ err_out_chrdev:
+ unregister_chrdev(vpu_major, "mxc_vpu");
+ error:
+ if (cpu_is_mx32()) {
+ vl2cc_cleanup();
+ }
+ out:
+ return err;
+}
+
+#ifdef CONFIG_PM
+static int vpu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int i;
+ unsigned long timeout;
+
+ /* Wait for vpu go to idle state, suspect vpu cannot be changed
+ to idle state after about 1 sec */
+ if (open_count > 0) {
+ timeout = jiffies + HZ;
+ clk_enable(vpu_clk);
+ while (READ_REG(BIT_BUSY_FLAG)) {
+ msleep(1);
+ if (time_after(jiffies, timeout))
+ goto out;
+ }
+ clk_disable(vpu_clk);
+ }
+
+ /* Make sure clock is disabled before suspend */
+ vpu_clk_usercount = clk_get_usecount(vpu_clk);
+ for (i = 0; i < vpu_clk_usercount; i++)
+ clk_disable(vpu_clk);
+
+ clk_enable(vpu_clk);
+ if (bitwork_mem.cpu_addr != 0) {
+ SAVE_WORK_REGS;
+ SAVE_CTRL_REGS;
+ SAVE_RDWR_PTR_REGS;
+ SAVE_DIS_FLAG_REGS;
+
+ WRITE_REG(0x1, BIT_BUSY_FLAG);
+ WRITE_REG(VPU_SLEEP_REG_VALUE, BIT_RUN_COMMAND);
+ while (READ_REG(BIT_BUSY_FLAG)) ;
+ }
+
+ clk_disable(vpu_clk);
+
+ if (cpu_is_mx37() || cpu_is_mx51())
+ mxc_pg_enable(pdev);
+
+ return 0;
+
+out:
+ clk_disable(vpu_clk);
+ return -EAGAIN;
+
+}
+
+static int vpu_resume(struct platform_device *pdev)
+{
+ int i;
+
+ if (cpu_is_mx37() || cpu_is_mx51())
+ mxc_pg_disable(pdev);
+
+ clk_enable(vpu_clk);
+
+ if (bitwork_mem.cpu_addr != 0) {
+ u32 *p = (u32 *) bitwork_mem.cpu_addr;
+ u32 data;
+ u16 data_hi;
+ u16 data_lo;
+
+ RESTORE_WORK_REGS;
+
+ WRITE_REG(0x0, BIT_RESET_CTRL);
+ WRITE_REG(0x0, BIT_CODE_RUN);
+
+ /*
+ * Re-load boot code, from the codebuffer in external RAM.
+ * Thankfully, we only need 4096 bytes, same for all platforms.
+ */
+ if (cpu_is_mx51()) {
+ for (i = 0; i < 2048; i += 4) {
+ data = p[(i / 2) + 1];
+ data_hi = (data >> 16) & 0xFFFF;
+ data_lo = data & 0xFFFF;
+ WRITE_REG((i << 16) | data_hi, BIT_CODE_DOWN);
+ WRITE_REG(((i + 1) << 16) | data_lo,
+ BIT_CODE_DOWN);
+
+ data = p[i / 2];
+ data_hi = (data >> 16) & 0xFFFF;
+ data_lo = data & 0xFFFF;
+ WRITE_REG(((i + 2) << 16) | data_hi,
+ BIT_CODE_DOWN);
+ WRITE_REG(((i + 3) << 16) | data_lo,
+ BIT_CODE_DOWN);
+ }
+ } else {
+ for (i = 0; i < 2048; i += 2) {
+ if (cpu_is_mx37())
+ data = swab32(p[i / 2]);
+ else
+ data = p[i / 2];
+ data_hi = (data >> 16) & 0xFFFF;
+ data_lo = data & 0xFFFF;
+
+ WRITE_REG((i << 16) | data_hi, BIT_CODE_DOWN);
+ WRITE_REG(((i + 1) << 16) | data_lo,
+ BIT_CODE_DOWN);
+ }
+ }
+
+ RESTORE_CTRL_REGS;
+
+ WRITE_REG(BITVAL_PIC_RUN, BIT_INT_ENABLE);
+
+ WRITE_REG(0x1, BIT_BUSY_FLAG);
+ WRITE_REG(0x1, BIT_CODE_RUN);
+ while (READ_REG(BIT_BUSY_FLAG)) ;
+
+ RESTORE_RDWR_PTR_REGS;
+ RESTORE_DIS_FLAG_REGS;
+
+ WRITE_REG(0x1, BIT_BUSY_FLAG);
+ WRITE_REG(VPU_WAKE_REG_VALUE, BIT_RUN_COMMAND);
+ while (READ_REG(BIT_BUSY_FLAG)) ;
+ }
+
+ clk_disable(vpu_clk);
+
+ /* Recover vpu clock */
+ for (i = 0; i < vpu_clk_usercount; i++)
+ clk_enable(vpu_clk);
+
+ return 0;
+}
+#else
+#define vpu_suspend NULL
+#define vpu_resume NULL
+#endif /* !CONFIG_PM */
+
+/*! Driver definition
+ *
+ */
+static struct platform_driver mxcvpu_driver = {
+ .driver = {
+ .name = "mxc_vpu",
+ },
+ .probe = vpu_dev_probe,
+ .suspend = vpu_suspend,
+ .resume = vpu_resume,
+};
+
+static int __init vpu_init(void)
+{
+ int ret = platform_driver_register(&mxcvpu_driver);
+
+ init_waitqueue_head(&vpu_queue);
+
+ return ret;
+}
+
+static void __exit vpu_exit(void)
+{
+ free_irq(MXC_INT_VPU, (void *)(&vpu_data));
+ if (vpu_major > 0) {
+ device_destroy(vpu_class, MKDEV(vpu_major, 0));
+ class_destroy(vpu_class);
+ unregister_chrdev(vpu_major, "mxc_vpu");
+ vpu_major = 0;
+ }
+
+ if (cpu_is_mx32()) {
+ vl2cc_cleanup();
+ }
+
+ vpu_free_dma_buffer(&bitwork_mem);
+ vpu_free_dma_buffer(&pic_para_mem);
+ vpu_free_dma_buffer(&user_data_mem);
+
+ clk_put(vpu_clk);
+
+ platform_driver_unregister(&mxcvpu_driver);
+ return;
+}
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Linux VPU driver for Freescale i.MX/MXC");
+MODULE_LICENSE("GPL");
+
+module_init(vpu_init);
+module_exit(vpu_exit);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 231eeaf1d552..601215803b79 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -962,6 +962,17 @@ config ENC28J60_WRITEVERIFY
Enable the verify after the buffer write useful for debugging purpose.
If unsure, say N.
+config NS9XXX_ETH
+ tristate "Digi NS9xxx ethernet support"
+ depends on ARM && ARCH_NS9XXX
+ depends on PHYLIB
+ depends on CRC32
+ help
+ If you have a Digi NS9xxx based system and wish to use its ethernet
+ port.
+ If you choose to build this driver as a module, it will be called
+ ns9xxx-eth.
+
config SMC911X
tristate "SMSC LAN911[5678] support"
select CRC32
@@ -970,14 +981,35 @@ config SMC911X
help
This is a driver for SMSC's LAN911x series of Ethernet chipsets
including the new LAN9115, LAN9116, LAN9117, and LAN9118.
- Say Y if you want it compiled into the kernel,
+ Say Y if you want it compiled into the kernel,
and read the Ethernet-HOWTO, available from
<http://www.linuxdoc.org/docs.html#howto>.
- This driver is also available as a module. The module will be
- called smc911x. If you want to compile it as a module, say M
+ This driver is also available as a module. The module will be
+ called smc911x. If you want to compile it as a module, say M
here and read <file:Documentation/kbuild/modules.txt>
+config SMSC911X
+ tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
+ depends on NET_ETHERNET
+ select CRC32
+ select MII
+ ---help---
+ Say Y here if you want support for SMSC LAN911x and LAN921x families
+ of ethernet controllers.
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module
+ will be called smsc911x.
+
+config SMSC9118
+ tristate "SMSC LAN9218 support"
+ depends on NET_ETHERNET && (MACH_CC9M2443JS || MACH_CCW9M2443JS || MACH_CCWMX51JS || MACH_CCMX51JS)
+ select CRC32
+ select MII
+ ---help---
+ Say Y here if you want support for SMSC LAN921x families
+ of ethernet controllers.
+
config NET_VENDOR_RACAL
bool "Racal-Interlan (Micom) NI cards"
depends on ISA
@@ -1393,7 +1425,7 @@ config FORCEDETH_NAPI
config CS89x0
tristate "CS89x0 support"
depends on NET_ETHERNET && (ISA || EISA || MACH_IXDP2351 \
- || ARCH_IXDP2X01 || ARCH_PNX010X || MACH_MX31ADS)
+ || ARCH_IXDP2X01 || ARCH_PNX010X || ARCH_MXC)
---help---
Support for CS89x0 chipset based Ethernet cards. If you have a
network (Ethernet) card of this type, say Y and read the
@@ -1407,7 +1439,7 @@ config CS89x0
config CS89x0_NONISA_IRQ
def_bool y
depends on CS89x0 != n
- depends on MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X || MACH_MX31ADS
+ depends on MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X || ARCH_MXC
config TC35815
tristate "TOSHIBA TC35815 Ethernet support"
@@ -1433,9 +1465,9 @@ config E100
select MII
---help---
This driver supports Intel(R) PRO/100 family of adapters.
- To verify that your adapter is supported, find the board ID number
- on the adapter. Look for a label that has a barcode and a number
- in the format 123456-001 (six digits hyphen three digits).
+ To verify that your adapter is supported, find the board ID number
+ on the adapter. Look for a label that has a barcode and a number
+ in the format 123456-001 (six digits hyphen three digits).
Use the above information and the Adapter & Driver ID Guide at:
@@ -1447,7 +1479,7 @@ config E100
<http://appsr.intel.com/scripts-df/support_intel.asp>
- More specific information on configuring the driver is in
+ More specific information on configuring the driver is in
<file:Documentation/networking/e100.txt>.
To compile this driver as a module, choose M here. The module
@@ -1810,11 +1842,11 @@ config 68360_ENET
the Motorola 68360 processor.
config FEC
- bool "FEC ethernet controller (of ColdFire CPUs)"
- depends on M523x || M527x || M5272 || M528x || M520x
+ tristate "FEC ethernet controller"
+ depends on M523x || M527x || M5272 || M528x || M520x || ARCH_MX27 || ARCH_MX37 || ARCH_MX35 || ARCH_MX51 || ARCH_MX25
help
Say Y here if you want to use the built-in 10/100 Fast ethernet
- controller on some Motorola ColdFire processors.
+ controller on some Motorola/Freescale processors.
config FEC2
bool "Second FEC ethernet controller (on some ColdFire CPUs)"
@@ -1935,7 +1967,7 @@ config E1000
depends on PCI
---help---
This driver supports Intel(R) PRO/1000 gigabit ethernet family of
- adapters. For more information on how to identify your adapter, go
+ adapters. For more information on how to identify your adapter, go
to the Adapter & Driver ID Guide at:
<http://support.intel.com/support/network/adapter/pro100/21397.htm>
@@ -1945,7 +1977,7 @@ config E1000
<http://support.intel.com>
- More specific information on configuring the driver is in
+ More specific information on configuring the driver is in
<file:Documentation/networking/e1000.txt>.
To compile this driver as a module, choose M here. The module
@@ -2122,7 +2154,7 @@ config SKGE
and related Gigabit Ethernet adapters. It is a new smaller driver
with better performance and more complete ethtool support.
- It does not support the link failover and network management
+ It does not support the link failover and network management
features that "portable" vendor supplied sk98lin driver does.
This driver supports adapters based on the original Yukon chipset:
@@ -2466,7 +2498,7 @@ config IXGB
<http://support.intel.com>
- More specific information on configuring the driver is in
+ More specific information on configuring the driver is in
<file:Documentation/networking/ixgb.txt>.
To compile this driver as a module, choose M here. The module
@@ -2476,8 +2508,8 @@ config S2IO
tristate "S2IO 10Gbe XFrame NIC"
depends on PCI
---help---
- This driver supports the 10Gbe XFrame NIC of S2IO.
- More specific information on configuring the driver is in
+ This driver supports the 10Gbe XFrame NIC of S2IO.
+ More specific information on configuring the driver is in
<file:Documentation/networking/s2io.txt>.
config MYRI10GE
@@ -2897,9 +2929,9 @@ config PPPOE
Support for PPP over Ethernet.
This driver requires the latest version of pppd from the CVS
- repository at cvs.samba.org. Alternatively, see the
+ repository at cvs.samba.org. Alternatively, see the
RoaringPenguin package (<http://www.roaringpenguin.com/pppoe>)
- which contains instruction on how to use this driver (under
+ which contains instruction on how to use this driver (under
the heading "Kernel mode PPPoE").
config PPPOATM
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 017383ad5ec6..c7cf052d08cf 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -220,18 +220,21 @@ obj-$(CONFIG_S2IO) += s2io.o
obj-$(CONFIG_MYRI10GE) += myri10ge/
obj-$(CONFIG_SMC91X) += smc91x.o
obj-$(CONFIG_SMC911X) += smc911x.o
+obj-$(CONFIG_SMSC911X) += smsc911x.o
obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
obj-$(CONFIG_DM9000) += dm9000.o
obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_driver.o
pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o
obj-$(CONFIG_MLX4_CORE) += mlx4/
obj-$(CONFIG_ENC28J60) += enc28j60.o
+obj-$(CONFIG_NS9XXX_ETH) += ns9xxx-eth.o
obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
obj-$(CONFIG_MACB) += macb.o
obj-$(CONFIG_ARM) += arm/
+obj-$(CONFIG_SMSC9118) += smsc9118/
obj-$(CONFIG_DEV_APPLETALK) += appletalk/
obj-$(CONFIG_TR) += tokenring/
obj-$(CONFIG_WAN) += wan/
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 57def0d57371..3ac5a12a0c8b 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -12,6 +12,164 @@ config CAN_VCAN
This driver can also be built as a module. If so, the module
will be called vcan.
+config CAN_SLCAN
+ tristate "Serial / USB serial CAN Adaptors (slcan)"
+ depends on CAN && EXPERIMENTAL
+ default N
+ ---help---
+ CAN driver for several 'low cost' CAN interfaces that are attached
+ via serial lines or via USB-to-serial adapters using the LAWICEL
+ ASCII protocol. The driver implements the tty linediscipline N_SLCAN.
+
+ This driver can also be built as a module. If so, the module
+ will be called slcan.
+
+config CAN_OLD_DRIVERS
+ tristate "Prompt for old CAN drivers (e.g. no sysfs support)"
+ depends on CAN
+ default N
+ ---help---
+ The old drivers do not support sysfs nor proper platform device
+ support. Some of the old drivers might only be configured by
+ module commandline options.
+
+if CAN_OLD_DRIVERS
+source "drivers/net/can/old/Kconfig"
+endif
+
+config CAN_DEV
+ tristate "Prompt for platform CAN drivers with sysfs support"
+ depends on CAN && SYSFS
+ default Y
+ ---help---
+ Enables the common framework for platform CAN drivers with sysfs
+ support. This is the standard library for CAN drivers.
+ If unsure, say Y.
+
+config CAN_CALC_BITTIMING
+ bool "CAN bit-timing calculation"
+ depends on CAN_DEV
+ default Y
+ ---help---
+ If enabled, CAN bit-timing parameters will be calculated for the
+ bit-rate specified via SYSFS file "bitrate" when the device gets
+ started. This works fine for the most common CAN controllers
+ with standard bit-rates but may fail for exotic bit-rates or CAN
+ source clock frequencies. Disabling saves some space, but then the
+ bit-timing parameters must be specified directly using the SYSFS
+ files "tq", "prop_seg", "phase_seg1", "phase_seg2" and "sjw".
+ If unsure, say Y.
+
+config CAN_SJA1000
+ depends on CAN_DEV
+ tristate "Philips SJA1000"
+ ---help---
+ The SJA1000 is one of the top CAN controllers out there. As it
+ has a multiplexed interface it fits directly to 8051
+ microcontrollers or into the PC I/O port space. The SJA1000
+ is a full CAN controller, with shadow registers for RX and TX.
+ It can send and receive any kinds of CAN frames (SFF/EFF/RTR)
+ with a single (simple) filter setup.
+
+ This driver will use the new device interface.
+
+config CAN_SJA1000_PLATFORM
+ depends on CAN_SJA1000
+ tristate "generic Platform Bus based SJA1000 driver"
+ ---help---
+ This driver adds support for the SJA1000 chips connected to
+ the "platform bus" (Linux abstraction for directly to the
+ processor attached devices). Which can be found on various
+ boards from Phytec (http://www.phytec.de) like the PCM027,
+ PCM038.
+
+config CAN_SJA1000_OF_PLATFORM
+ depends on CAN_SJA1000 && PPC_OF
+ tristate "generic OF Platform Bus based SJA1000 driver"
+ ---help---
+ This driver adds support for the SJA1000 chips connected to
+ the OpenFirmware "platform bus" found on embedded systems with
+ OpenFirmware bindings, e.g. if you have a PowerPC based system
+ you should enable this option.
+
+config CAN_EMS_PCI
+ tristate "EMS CPC-PCI and CPC-PCIe Card"
+ depends on PCI && CAN_SJA1000
+ ---help---
+ This driver is for the one or two channel CPC-PCI and CPC-PCIe
+ cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+
+config CAN_EMS_PCMCIA
+ tristate "EMS CPC-CARD Card"
+ depends on PCMCIA && CAN_SJA1000
+ ---help---
+ This driver is for the one or two channel CPC-CARD cards from
+ EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+
+config CAN_IXXAT_PCI
+ tristate "IXXAT PCI Card"
+ depends on PCI && CAN_SJA1000
+ ---help---
+ This driver is for the IXXAT PC-I 04/PCI card (1 or 2 channel)
+ from the IXXAT Automation GmbH (http://www.ixxat.de).
+
+config CAN_PEAK_PCI
+ tristate "PEAK PCAN PCI Card"
+ depends on PCI && CAN_SJA1000
+ ---help---
+ This driver is for the PCAN PCI, the PC-PCI CAN plug-in card (1 or
+ 2 channel) from PEAK Systems (http://www.peak-system.com).
+
+config CAN_PIPCAN
+ depends on CAN_SJA1000
+ tristate "MPL PIPCAN CAN module driver (SJA1000)"
+ ---help---
+ This driver adds support for the PIPCAN module used on some SBC
+ boards from MPL AG (http://www.mpl.ch).
+
+config CAN_KVASER_PCI
+ tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards"
+ depends on PCI && CAN_SJA1000
+ ---help---
+ This driver is for the the PCIcanx and PCIcan cards (1, 2 or
+ 4 channel) from Kvaser (http://www.kvaser.com).
+
+config CAN_SOFTING
+ tristate "Softing Gmbh CAN generic support"
+ depends on CAN_DEV
+ ---help---
+ generic softing CAN cards
+
+config CAN_SOFTING_CS
+ tristate "Softing CAN pcmcia cards"
+ depends on CAN_SOFTING && PCMCIA
+
+config CAN_MSCAN
+ depends on CAN_DEV && (PPC || M68K || M68KNOMMU)
+ tristate "Support for a Freescale MSCAN based chips"
+ ---help---
+ The Motorola Scalable Controller Area Network (MSCAN) definition
+ is based on the MSCAN12 definition which is the specific
+ implementation of the Motorola Scalable CAN concept targeted for
+ the Motorola MC68HC12 Microcontroller Family.
+
+config CAN_MPC52XX
+ tristate "Freescale MPC5200 onboard CAN controller"
+ depends on CAN_MSCAN && (PPC_MPC52xx || PPC_52xx)
+ default LITE5200
+ ---help---
+ If you say yes here you get support for Freescale MPC5200
+ onboard dualCAN controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called mpc52xx_can.
+
+config CAN_MCP251X
+ tristate "Microchip MCP251x SPI CAN controllers"
+ depends on CAN_DEV && SPI
+ ---help---
+ Driver for the Microchip MCP251x SPI CAN controllers.
+
config CAN_DEBUG_DEVICES
bool "CAN devices debugging messages"
depends on CAN
@@ -22,4 +180,13 @@ config CAN_DEBUG_DEVICES
a problem with CAN support and want to see more of what is going
on.
+config CAN_FLEXCAN
+ tristate "Freescale FlexCAN"
+ depends on CAN && (ARCH_MX25 || ARCH_MX35)
+ default m
+ ---help---
+ This select the support of Freescale CAN(FlexCAN).
+ This driver can also be built as a module.
+ If unsure, say N.
+
endmenu
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index c4bead705cd9..60c7ecca200d 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -1,5 +1,49 @@
#
-# Makefile for the Linux Controller Area Network drivers.
#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../..
+
+export CONFIG_CAN_VCAN=m
+export CONFIG_CAN_DEV=m
+#export CONFIG_CAN_SJA1000_OLD=m
+#export CONFIG_CAN_I82527_OLD=m
+export CONFIG_CAN_SJA1000=m
+export CONFIG_CAN_SJA1000_PLATFORM=m
+export CONFIG_CAN_EMS_PCI=m
+export CONFIG_CAN_EMS_PCMCIA=m
+export CONFIG_CAN_PIPCAN=m
+export CONFIG_CAN_SOFTING=m
+export CONFIG_CAN_SOFTING_CS=m
+export CONFIG_CAN_MCP251X=m
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
obj-$(CONFIG_CAN_VCAN) += vcan.o
+obj-$(CONFIG_CAN_SLCAN) += slcan.o
+
+obj-$(CONFIG_CAN_DEV) += can-dev.o
+can-dev-y := dev.o sysfs.o
+
+obj-$(CONFIG_CAN_SJA1000) += sja1000/
+obj-$(CONFIG_CAN_SOFTING) += softing/
+obj-$(CONFIG_CAN_MSCAN) += mscan/
+obj-$(CONFIG_CAN_SJA1000_OLD) += old/sja1000/
+obj-$(CONFIG_CAN_I82527_OLD) += old/i82527/
+obj-$(CONFIG_CAN_MSCAN_OLD) += old/mscan/
+obj-$(CONFIG_CAN_CCAN_OLD) += old/ccan/
+obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
+
+ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+
+endif
+obj-$(CONFIG_CAN_FLEXCAN) += flexcan/
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
new file mode 100644
index 000000000000..faa75d14cf29
--- /dev/null
+++ b/drivers/net/can/dev.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
+ * Copyright (C) 2006 Andrey Volkov, Varma Electronics
+ * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <net/rtnetlink.h>
+
+#include "sysfs.h"
+
+#define MOD_DESC "CAN device driver interface"
+
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+
+#ifdef CONFIG_CAN_CALC_BITTIMING
+#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
+
+/*
+ * Bit-timing calculation derived from:
+ *
+ * Code based on LinCAN sources and H8S2638 project
+ * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
+ * Copyright 2005 Stanislav Marek
+ * email: pisa@cmp.felk.cvut.cz
+ */
+static int can_update_spt(const struct can_bittiming_const *btc,
+ int sampl_pt, int tseg, int *tseg1, int *tseg2)
+{
+ *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000;
+ if (*tseg2 < btc->tseg2_min)
+ *tseg2 = btc->tseg2_min;
+ if (*tseg2 > btc->tseg2_max)
+ *tseg2 = btc->tseg2_max;
+ *tseg1 = tseg - *tseg2;
+ if (*tseg1 > btc->tseg1_max) {
+ *tseg1 = btc->tseg1_max;
+ *tseg2 = tseg - *tseg1;
+ }
+ return 1000 * (tseg + 1 - *tseg2) / (tseg + 1);
+}
+
+static int can_calc_bittiming(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ const struct can_bittiming_const *btc = priv->bittiming_const;
+ long rate, best_rate = 0;
+ long best_error = 1000000000, error = 0;
+ int best_tseg = 0, best_brp = 0, brp = 0;
+ int tsegall, tseg = 0, tseg1 = 0, tseg2 = 0;
+ int spt_error = 1000, spt = 0, sampl_pt;
+ u64 v64;
+
+ if (!priv->bittiming_const)
+ return -ENOTSUPP;
+
+ /* Use CIA recommended sample points */
+ if (bt->sample_point) {
+ sampl_pt = bt->sample_point;
+ } else {
+ if (bt->bitrate > 800000)
+ sampl_pt = 750;
+ else if (bt->bitrate > 500000)
+ sampl_pt = 800;
+ else
+ sampl_pt = 875;
+ }
+
+ /* tseg even = round down, odd = round up */
+ for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
+ tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) {
+ tsegall = 1 + tseg / 2;
+ /* Compute all possible tseg choices (tseg=tseg1+tseg2) */
+ brp = bt->clock / (tsegall * bt->bitrate) + tseg % 2;
+ /* chose brp step which is possible in system */
+ brp = (brp / btc->brp_inc) * btc->brp_inc;
+ if ((brp < btc->brp_min) || (brp > btc->brp_max))
+ continue;
+ rate = bt->clock / (brp * tsegall);
+ error = bt->bitrate - rate;
+ /* tseg brp biterror */
+ if (error < 0)
+ error = -error;
+ if (error > best_error)
+ continue;
+ best_error = error;
+ if (error == 0) {
+ spt = can_update_spt(btc, sampl_pt, tseg / 2,
+ &tseg1, &tseg2);
+ error = sampl_pt - spt;
+ if (error < 0)
+ error = -error;
+ if (error > spt_error)
+ continue;
+ spt_error = error;
+ }
+ best_tseg = tseg / 2;
+ best_brp = brp;
+ best_rate = rate;
+ if (error == 0)
+ break;
+ }
+
+ if (best_error) {
+ /* Error in one-tenth of a percent */
+ error = (best_error * 1000) / bt->bitrate;
+ if (error > CAN_CALC_MAX_ERROR) {
+ dev_err(ND2D(dev), "bitrate error %ld.%ld%% too high\n",
+ error / 10, error % 10);
+ return -EDOM;
+ } else {
+ dev_warn(ND2D(dev), "bitrate error %ld.%ld%%\n",
+ error / 10, error % 10);
+ }
+ }
+
+ spt = can_update_spt(btc, sampl_pt, best_tseg, &tseg1, &tseg2);
+
+ v64 = (u64)best_brp * 1000000000UL;
+ do_div(v64, bt->clock);
+ bt->tq = (u32)v64;
+ bt->prop_seg = tseg1 / 2;
+ bt->phase_seg1 = tseg1 - bt->prop_seg;
+ bt->phase_seg2 = tseg2;
+ bt->sjw = 1;
+ bt->brp = best_brp;
+
+ return 0;
+}
+#else /* !CONFIG_CAN_CALC_BITTIMING */
+static int can_calc_bittiming(struct net_device *dev)
+{
+ dev_err(ND2D(dev), "bit-timing calculation not available\n");
+ return -EINVAL;
+}
+#endif /* CONFIG_CAN_CALC_BITTIMING */
+
+int can_sample_point(struct can_bittiming *bt)
+{
+ return ((bt->prop_seg + bt->phase_seg1 + 1) * 1000) /
+ (bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1);
+}
+
+static int can_fixup_bittiming(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ const struct can_bittiming_const *btc = priv->bittiming_const;
+ int tseg1, alltseg;
+ u32 bitrate;
+ u64 brp64;
+
+ if (!priv->bittiming_const)
+ return -ENOTSUPP;
+
+ tseg1 = bt->prop_seg + bt->phase_seg1;
+ if (bt->sjw > btc->sjw_max ||
+ tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max ||
+ bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max)
+ return -EINVAL;
+
+ brp64 = (u64)bt->clock * (u64)bt->tq;
+ if (btc->brp_inc > 1)
+ do_div(brp64, btc->brp_inc);
+ brp64 += 500000000UL - 1;
+ do_div(brp64, 1000000000UL); /* the practicable BRP */
+ if (btc->brp_inc > 1)
+ brp64 *= btc->brp_inc;
+ bt->brp = (u32)brp64;
+
+ if (bt->brp < btc->brp_min || bt->brp > btc->brp_max)
+ return -EINVAL;
+
+ alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1;
+ bitrate = bt->clock / (bt->brp * alltseg);
+ bt->bitrate = bitrate;
+
+ return 0;
+}
+
+/*
+ * Set CAN bit-timing for the device
+ *
+ * This functions should be called in the open function of the device
+ * driver to determine, check and set appropriate bit-timing parameters.
+ */
+int can_set_bittiming(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* Check if bit-timing parameters have been pre-defined */
+ if (!priv->bittiming.tq && !priv->bittiming.bitrate) {
+ dev_err(ND2D(dev), "bit-timing not yet defined\n");
+ return -EINVAL;
+ }
+
+ /* Check if the CAN device has bit-timing parameters */
+ if (priv->bittiming_const) {
+
+ /* Check if bit-timing parameters have already been set */
+ if (priv->bittiming.tq && priv->bittiming.bitrate)
+ return 0;
+
+ /* Non-expert mode? Check if the bitrate has been pre-defined */
+ if (!priv->bittiming.tq)
+ /* Determine bit-timing parameters */
+ err = can_calc_bittiming(dev);
+ else
+ /* Check bit-timing params and calculate proper brp */
+ err = can_fixup_bittiming(dev);
+ if (err)
+ return err;
+ }
+
+ if (priv->do_set_bittiming) {
+ /* Finally, set the bit-timing registers */
+ err = priv->do_set_bittiming(dev);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(can_set_bittiming);
+
+static void can_setup(struct net_device *dev)
+{
+ dev->type = ARPHRD_CAN;
+ dev->mtu = sizeof(struct can_frame);
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ /* New-style flags. */
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+}
+
+/*
+ * Allocate and setup space for the CAN network device
+ */
+struct net_device *alloc_candev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct can_priv *priv;
+
+ dev = alloc_netdev(sizeof_priv, "can%d", can_setup);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+
+ priv->state = CAN_STATE_STOPPED;
+ spin_lock_init(&priv->irq_lock);
+
+ init_timer(&priv->timer);
+ priv->timer.expires = 0;
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_candev);
+
+/*
+ * Allocate space of the CAN network device
+ */
+void free_candev(struct net_device *dev)
+{
+ free_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(free_candev);
+
+/*
+ * Register the CAN network device
+ */
+int register_candev(struct net_device *dev)
+{
+ int err;
+
+ err = register_netdev(dev);
+ if (err)
+ return err;
+
+ can_create_sysfs(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(register_candev);
+
+/*
+ * Unregister the CAN network device
+ */
+void unregister_candev(struct net_device *dev)
+{
+ can_remove_sysfs(dev);
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_candev);
+
+/*
+ * Local echo of CAN messages
+ *
+ * CAN network devices *should* support a local echo functionality
+ * (see Documentation/networking/can.txt). To test the handling of CAN
+ * interfaces that do not support the local echo both driver types are
+ * implemented. In the case that the driver does not support the echo
+ * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core
+ * to perform the echo as a fallback solution.
+ */
+
+static void can_flush_echo_skb(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ int i;
+
+ for (i = 0; i < CAN_ECHO_SKB_MAX; i++) {
+ if (priv->echo_skb[i]) {
+ kfree_skb(priv->echo_skb[i]);
+ priv->echo_skb[i] = NULL;
+ stats->tx_dropped++;
+ stats->tx_aborted_errors++;
+ }
+ }
+}
+
+/*
+ * Put the skb on the stack to be looped backed locally lateron
+ *
+ * The function is typically called in the start_xmit function
+ * of the device driver.
+ */
+void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set flag whether this packet has to be looped back */
+ if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK) {
+ kfree_skb(skb);
+ return;
+ }
+
+ if (!priv->echo_skb[idx]) {
+ struct sock *srcsk = skb->sk;
+
+ if (atomic_read(&skb->users) != 1) {
+ struct sk_buff *old_skb = skb;
+
+ skb = skb_clone(old_skb, GFP_ATOMIC);
+ kfree_skb(old_skb);
+ if (!skb)
+ return;
+ } else
+ skb_orphan(skb);
+
+ skb->sk = srcsk;
+
+ /* make settings for echo to reduce code in irq context */
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->dev = dev;
+
+ /* save this skb for tx interrupt echo handling */
+ priv->echo_skb[idx] = skb;
+ } else {
+ /* locking problem with netif_stop_queue() ?? */
+ printk(KERN_ERR "%s: %s: BUG! echo_skb is occupied!\n",
+ dev->name, __func__);
+ kfree_skb(skb);
+ }
+}
+EXPORT_SYMBOL_GPL(can_put_echo_skb);
+
+/*
+ * Get the skb from the stack and loop it back locally
+ *
+ * The function is typically called when the TX done interrupt
+ * is handled in the device driver.
+ */
+void can_get_echo_skb(struct net_device *dev, int idx)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if ((dev->flags & IFF_ECHO) && priv->echo_skb[idx]) {
+ netif_rx(priv->echo_skb[idx]);
+ priv->echo_skb[idx] = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(can_get_echo_skb);
+
+/*
+ * CAN device restart for bus-off recovery
+ */
+int can_restart_now(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+ int err;
+
+ if (netif_carrier_ok(dev))
+ netif_carrier_off(dev);
+
+ /* Cancel restart in progress */
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0; /* mark inactive timer */
+ }
+
+ can_flush_echo_skb(dev);
+
+ err = priv->do_set_mode(dev, CAN_MODE_START);
+ if (err)
+ return err;
+
+ netif_carrier_on(dev);
+
+ dev_dbg(ND2D(dev), "restarted\n");
+ priv->can_stats.restarts++;
+
+ /* send restart message upstream */
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return -ENOMEM;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ return 0;
+}
+
+static void can_restart_after(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct can_priv *priv = netdev_priv(dev);
+
+ priv->timer.expires = 0; /* mark inactive timer */
+ can_restart_now(dev);
+}
+
+/*
+ * CAN bus-off
+ *
+ * This functions should be called when the device goes bus-off to
+ * tell the netif layer that no more packets can be sent or received.
+ * If enabled, a timer is started to trigger bus-off recovery.
+ */
+void can_bus_off(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ dev_dbg(ND2D(dev), "bus-off\n");
+
+ netif_carrier_off(dev);
+
+ if (priv->restart_ms > 0 && !priv->timer.expires) {
+
+ priv->timer.function = can_restart_after;
+ priv->timer.data = (unsigned long)dev;
+ priv->timer.expires =
+ jiffies + (priv->restart_ms * HZ) / 1000;
+ add_timer(&priv->timer);
+ }
+}
+EXPORT_SYMBOL_GPL(can_bus_off);
+
+/*
+ * Cleanup function before the device gets closed.
+ *
+ * This functions should be called in the close function of the device
+ * driver.
+ */
+void can_close_cleanup(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0;
+ }
+
+ can_flush_echo_skb(dev);
+}
+EXPORT_SYMBOL_GPL(can_close_cleanup);
+
+static __init int can_dev_init(void)
+{
+ printk(KERN_INFO MOD_DESC "\n");
+
+ return 0;
+}
+module_init(can_dev_init);
+
+static __exit void can_dev_exit(void)
+{
+}
+module_exit(can_dev_exit);
diff --git a/drivers/net/can/flexcan/Makefile b/drivers/net/can/flexcan/Makefile
new file mode 100644
index 000000000000..b2dbb4fb2793
--- /dev/null
+++ b/drivers/net/can/flexcan/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
+
+flexcan-y := dev.o drv.o mbm.o
diff --git a/drivers/net/can/flexcan/dev.c b/drivers/net/can/flexcan/dev.c
new file mode 100644
index 000000000000..f2040c135a89
--- /dev/null
+++ b/drivers/net/can/flexcan/dev.c
@@ -0,0 +1,619 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file dev.c
+ *
+ * @brief Driver for Freescale CAN Controller FlexCAN.
+ *
+ * @ingroup can
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#include <linux/module.h>
+#include "flexcan.h"
+
+enum {
+ FLEXCAN_ATTR_STATE = 0,
+ FLEXCAN_ATTR_BITRATE,
+ FLEXCAN_ATTR_BR_PRESDIV,
+ FLEXCAN_ATTR_BR_RJW,
+ FLEXCAN_ATTR_BR_PROPSEG,
+ FLEXCAN_ATTR_BR_PSEG1,
+ FLEXCAN_ATTR_BR_PSEG2,
+ FLEXCAN_ATTR_BR_CLKSRC,
+ FLEXCAN_ATTR_MAXMB,
+ FLEXCAN_ATTR_XMIT_MAXMB,
+ FLEXCAN_ATTR_FIFO,
+ FLEXCAN_ATTR_WAKEUP,
+ FLEXCAN_ATTR_SRX_DIS,
+ FLEXCAN_ATTR_WAK_SRC,
+ FLEXCAN_ATTR_BCC,
+ FLEXCAN_ATTR_LOCAL_PRIORITY,
+ FLEXCAN_ATTR_ABORT,
+ FLEXCAN_ATTR_LOOPBACK,
+ FLEXCAN_ATTR_SMP,
+ FLEXCAN_ATTR_BOFF_REC,
+ FLEXCAN_ATTR_TSYN,
+ FLEXCAN_ATTR_LISTEN,
+ FLEXCAN_ATTR_EXTEND_MSG,
+ FLEXCAN_ATTR_STANDARD_MSG,
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ FLEXCAN_ATTR_DUMP_REG,
+ FLEXCAN_ATTR_DUMP_XMIT_MB,
+ FLEXCAN_ATTR_DUMP_RX_MB,
+#endif
+ FLEXCAN_ATTR_MAX
+};
+
+static ssize_t flexcan_show_attr(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t flexcan_set_attr(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count);
+
+static struct device_attribute flexcan_dev_attr[FLEXCAN_ATTR_MAX] = {
+ [FLEXCAN_ATTR_STATE] = __ATTR(state, 0444, flexcan_show_attr, NULL),
+ [FLEXCAN_ATTR_BITRATE] =
+ __ATTR(bitrate, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_PRESDIV] =
+ __ATTR(br_presdiv, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_RJW] =
+ __ATTR(br_rjw, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_PROPSEG] =
+ __ATTR(br_propseg, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_PSEG1] =
+ __ATTR(br_pseg1, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_PSEG2] =
+ __ATTR(br_pseg2, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BR_CLKSRC] =
+ __ATTR(br_clksrc, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_MAXMB] =
+ __ATTR(maxmb, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_XMIT_MAXMB] =
+ __ATTR(xmit_maxmb, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_FIFO] =
+ __ATTR(fifo, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_WAKEUP] =
+ __ATTR(wakeup, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_SRX_DIS] =
+ __ATTR(srx_dis, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_WAK_SRC] =
+ __ATTR(wak_src, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BCC] =
+ __ATTR(bcc, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_LOCAL_PRIORITY] =
+ __ATTR(local_priority, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_ABORT] =
+ __ATTR(abort, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_LOOPBACK] =
+ __ATTR(loopback, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_SMP] =
+ __ATTR(smp, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_BOFF_REC] =
+ __ATTR(boff_rec, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_TSYN] =
+ __ATTR(tsyn, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_LISTEN] =
+ __ATTR(listen, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_EXTEND_MSG] =
+ __ATTR(ext_msg, 0644, flexcan_show_attr, flexcan_set_attr),
+ [FLEXCAN_ATTR_STANDARD_MSG] =
+ __ATTR(std_msg, 0644, flexcan_show_attr, flexcan_set_attr),
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ [FLEXCAN_ATTR_DUMP_REG] =
+ __ATTR(dump_reg, 0444, flexcan_show_attr, NULL),
+ [FLEXCAN_ATTR_DUMP_XMIT_MB] =
+ __ATTR(dump_xmit_mb, 0444, flexcan_show_attr, NULL),
+ [FLEXCAN_ATTR_DUMP_RX_MB] =
+ __ATTR(dump_rx_mb, 0444, flexcan_show_attr, NULL),
+#endif
+};
+
+static void flexcan_set_bitrate(struct flexcan_device *flexcan, int bitrate)
+{
+ /* TODO:: implement in future
+ * based on the bitrate to get the timing of
+ * presdiv, pseg1, pseg2, propseg
+ */
+}
+
+static void flexcan_update_bitrate(struct flexcan_device *flexcan)
+{
+ int rate, div;
+
+ if (flexcan->br_clksrc)
+ rate = clk_get_rate(flexcan->clk);
+ else {
+ struct clk *clk;
+ clk = clk_get(NULL, "ckih");
+ if (!clk)
+ return;
+ rate = clk_get_rate(clk);
+ clk_put(clk);
+ }
+ if (!rate)
+ return;
+
+ div = (flexcan->br_presdiv + 1);
+ div *=
+ (flexcan->br_propseg + flexcan->br_pseg1 + flexcan->br_pseg2 + 4);
+ flexcan->bitrate = (rate + div - 1) / div;
+}
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static int flexcan_dump_reg(struct flexcan_device *flexcan, char *buf)
+{
+ int ret = 0;
+ unsigned int reg;
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ ret += sprintf(buf + ret, "MCR::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_CTRL);
+ ret += sprintf(buf + ret, "CTRL::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_RXGMASK);
+ ret += sprintf(buf + ret, "RXGMASK::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_RX14MASK);
+ ret += sprintf(buf + ret, "RX14MASK::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_RX15MASK);
+ ret += sprintf(buf + ret, "RX15MASK::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_ECR);
+ ret += sprintf(buf + ret, "ECR::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_ESR);
+ ret += sprintf(buf + ret, "ESR::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK2);
+ ret += sprintf(buf + ret, "IMASK2::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK1);
+ ret += sprintf(buf + ret, "IMASK1::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG2);
+ ret += sprintf(buf + ret, "IFLAG2::0x%x\n", reg);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG1);
+ ret += sprintf(buf + ret, "IFLAG1::0x%x\n", reg);
+ return ret;
+}
+
+static int flexcan_dump_xmit_mb(struct flexcan_device *flexcan, char *buf)
+{
+ int ret = 0, i;
+ i = flexcan->xmit_maxmb + 1;
+ for (; i <= flexcan->maxmb; i++)
+ ret +=
+ sprintf(buf + ret,
+ "mb[%d]::CS:0x%x ID:0x%x DATA[1~2]:0x%02x,0x%02x\n",
+ i, flexcan->hwmb[i].mb_cs.data,
+ flexcan->hwmb[i].mb_id, flexcan->hwmb[i].mb_data[1],
+ flexcan->hwmb[i].mb_data[2]);
+ return ret;
+}
+
+static int flexcan_dump_rx_mb(struct flexcan_device *flexcan, char *buf)
+{
+ int ret = 0, i;
+ for (i = 0; i <= flexcan->xmit_maxmb; i++)
+ ret +=
+ sprintf(buf + ret,
+ "mb[%d]::CS:0x%x ID:0x%x DATA[1~2]:0x%02x,0x%02x\n",
+ i, flexcan->hwmb[i].mb_cs.data,
+ flexcan->hwmb[i].mb_id, flexcan->hwmb[i].mb_data[1],
+ flexcan->hwmb[i].mb_data[2]);
+ return ret;
+}
+#endif
+
+static ssize_t flexcan_show_state(struct net_device *net, char *buf)
+{
+ int ret, esr;
+ struct flexcan_device *flexcan = netdev_priv(net);
+ ret = sprintf(buf, "%s::", netif_running(net) ? "Start" : "Stop");
+ if (netif_carrier_ok(net)) {
+ esr = __raw_readl(flexcan->io_base + CAN_HW_REG_ESR);
+ switch ((esr & __ESR_FLT_CONF_MASK) >> __ESR_FLT_CONF_OFF) {
+ case 0:
+ ret += sprintf(buf + ret, "normal\n");
+ break;
+ case 1:
+ ret += sprintf(buf + ret, "error passive\n");
+ break;
+ default:
+ ret += sprintf(buf + ret, "bus off\n");
+ }
+ } else
+ ret += sprintf(buf + ret, "bus off\n");
+ return ret;
+}
+
+static ssize_t flexcan_show_attr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int attr_id;
+ struct net_device *net;
+ struct flexcan_device *flexcan;
+
+ net = dev_get_drvdata(dev);
+ BUG_ON(!net);
+ flexcan = netdev_priv(net);
+ BUG_ON(!flexcan);
+
+ attr_id = attr - flexcan_dev_attr;
+ switch (attr_id) {
+ case FLEXCAN_ATTR_STATE:
+ return flexcan_show_state(net, buf);
+ case FLEXCAN_ATTR_BITRATE:
+ return sprintf(buf, "%d\n", flexcan->bitrate);
+ case FLEXCAN_ATTR_BR_PRESDIV:
+ return sprintf(buf, "%d\n", flexcan->br_presdiv + 1);
+ case FLEXCAN_ATTR_BR_RJW:
+ return sprintf(buf, "%d\n", flexcan->br_rjw);
+ case FLEXCAN_ATTR_BR_PROPSEG:
+ return sprintf(buf, "%d\n", flexcan->br_propseg + 1);
+ case FLEXCAN_ATTR_BR_PSEG1:
+ return sprintf(buf, "%d\n", flexcan->br_pseg1 + 1);
+ case FLEXCAN_ATTR_BR_PSEG2:
+ return sprintf(buf, "%d\n", flexcan->br_pseg2 + 1);
+ case FLEXCAN_ATTR_BR_CLKSRC:
+ return sprintf(buf, "%s\n", flexcan->br_clksrc ? "bus" : "osc");
+ case FLEXCAN_ATTR_MAXMB:
+ return sprintf(buf, "%d\n", flexcan->maxmb + 1);
+ case FLEXCAN_ATTR_XMIT_MAXMB:
+ return sprintf(buf, "%d\n", flexcan->xmit_maxmb + 1);
+ case FLEXCAN_ATTR_FIFO:
+ return sprintf(buf, "%d\n", flexcan->fifo);
+ case FLEXCAN_ATTR_WAKEUP:
+ return sprintf(buf, "%d\n", flexcan->wakeup);
+ case FLEXCAN_ATTR_SRX_DIS:
+ return sprintf(buf, "%d\n", flexcan->srx_dis);
+ case FLEXCAN_ATTR_WAK_SRC:
+ return sprintf(buf, "%d\n", flexcan->wak_src);
+ case FLEXCAN_ATTR_BCC:
+ return sprintf(buf, "%d\n", flexcan->bcc);
+ case FLEXCAN_ATTR_LOCAL_PRIORITY:
+ return sprintf(buf, "%d\n", flexcan->lprio);
+ case FLEXCAN_ATTR_ABORT:
+ return sprintf(buf, "%d\n", flexcan->abort);
+ case FLEXCAN_ATTR_LOOPBACK:
+ return sprintf(buf, "%d\n", flexcan->loopback);
+ case FLEXCAN_ATTR_SMP:
+ return sprintf(buf, "%d\n", flexcan->smp);
+ case FLEXCAN_ATTR_BOFF_REC:
+ return sprintf(buf, "%d\n", flexcan->boff_rec);
+ case FLEXCAN_ATTR_TSYN:
+ return sprintf(buf, "%d\n", flexcan->tsyn);
+ case FLEXCAN_ATTR_LISTEN:
+ return sprintf(buf, "%d\n", flexcan->listen);
+ case FLEXCAN_ATTR_EXTEND_MSG:
+ return sprintf(buf, "%d\n", flexcan->ext_msg);
+ case FLEXCAN_ATTR_STANDARD_MSG:
+ return sprintf(buf, "%d\n", flexcan->std_msg);
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ case FLEXCAN_ATTR_DUMP_REG:
+ return flexcan_dump_reg(flexcan, buf);
+ case FLEXCAN_ATTR_DUMP_XMIT_MB:
+ return flexcan_dump_xmit_mb(flexcan, buf);
+ case FLEXCAN_ATTR_DUMP_RX_MB:
+ return flexcan_dump_rx_mb(flexcan, buf);
+#endif
+ default:
+ return sprintf(buf, "%s:%p->%p\n", __func__, flexcan_dev_attr,
+ attr);
+ }
+}
+
+static ssize_t flexcan_set_attr(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int attr_id, tmp;
+ struct net_device *net;
+ struct flexcan_device *flexcan;
+
+ net = dev_get_drvdata(dev);
+ BUG_ON(!net);
+ flexcan = netdev_priv(net);
+ BUG_ON(!flexcan);
+
+ attr_id = attr - flexcan_dev_attr;
+
+ if (mutex_lock_interruptible(&flexcan->mutex))
+ return count;
+
+ if (netif_running(net))
+ goto set_finish;
+
+ if (attr_id == FLEXCAN_ATTR_BR_CLKSRC) {
+ if (!strcasecmp(buf, "bus"))
+ flexcan->br_clksrc = 1;
+ else if (!strcasecmp(buf, "osc"))
+ flexcan->br_clksrc = 0;
+ goto set_finish;
+ }
+
+ tmp = simple_strtoul(buf, NULL, 0);
+ switch (attr_id) {
+ case FLEXCAN_ATTR_BITRATE:
+ flexcan_set_bitrate(flexcan, tmp);
+ break;
+ case FLEXCAN_ATTR_BR_PRESDIV:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PRESDIV)) {
+ flexcan->br_presdiv = tmp - 1;
+ flexcan_update_bitrate(flexcan);
+ }
+ break;
+ case FLEXCAN_ATTR_BR_RJW:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_RJW))
+ flexcan->br_rjw = tmp - 1;
+ break;
+ case FLEXCAN_ATTR_BR_PROPSEG:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PROPSEG)) {
+ flexcan->br_propseg = tmp - 1;
+ flexcan_update_bitrate(flexcan);
+ }
+ break;
+ case FLEXCAN_ATTR_BR_PSEG1:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PSEG1)) {
+ flexcan->br_pseg1 = tmp - 1;
+ flexcan_update_bitrate(flexcan);
+ }
+ break;
+ case FLEXCAN_ATTR_BR_PSEG2:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PSEG2)) {
+ flexcan->br_pseg2 = tmp - 1;
+ flexcan_update_bitrate(flexcan);
+ }
+ break;
+ case FLEXCAN_ATTR_MAXMB:
+ if ((tmp > 0) && (tmp <= FLEXCAN_MAX_MB)) {
+ if (flexcan->maxmb != (tmp - 1)) {
+ flexcan->maxmb = tmp - 1;
+ if (flexcan->xmit_maxmb < flexcan->maxmb)
+ flexcan->xmit_maxmb = flexcan->maxmb;
+ }
+ }
+ break;
+ case FLEXCAN_ATTR_XMIT_MAXMB:
+ if ((tmp > 0) && (tmp <= (flexcan->maxmb + 1))) {
+ if (flexcan->xmit_maxmb != (tmp - 1))
+ flexcan->xmit_maxmb = tmp - 1;
+ }
+ break;
+ case FLEXCAN_ATTR_FIFO:
+ flexcan->fifo = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_WAKEUP:
+ flexcan->wakeup = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_SRX_DIS:
+ flexcan->srx_dis = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_WAK_SRC:
+ flexcan->wak_src = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_BCC:
+ flexcan->bcc = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_LOCAL_PRIORITY:
+ flexcan->lprio = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_ABORT:
+ flexcan->abort = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_LOOPBACK:
+ flexcan->loopback = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_SMP:
+ flexcan->smp = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_BOFF_REC:
+ flexcan->boff_rec = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_TSYN:
+ flexcan->tsyn = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_LISTEN:
+ flexcan->listen = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_EXTEND_MSG:
+ flexcan->ext_msg = tmp ? 1 : 0;
+ break;
+ case FLEXCAN_ATTR_STANDARD_MSG:
+ flexcan->std_msg = tmp ? 1 : 0;
+ break;
+ }
+ set_finish:
+ mutex_unlock(&flexcan->mutex);
+ return count;
+}
+
+static void flexcan_device_default(struct flexcan_device *dev)
+{
+ dev->br_clksrc = 1;
+ dev->br_rjw = 2;
+ dev->br_presdiv = 6;
+ dev->br_propseg = 4;
+ dev->br_pseg1 = 4;
+ dev->br_pseg2 = 7;
+
+ dev->bcc = 1;
+ dev->srx_dis = 1;
+ dev->smp = 1;
+ dev->boff_rec = 1;
+
+ dev->maxmb = FLEXCAN_MAX_MB - 1;
+ dev->xmit_maxmb = (FLEXCAN_MAX_MB >> 1) - 1;
+ dev->xmit_mb = dev->maxmb - dev->xmit_maxmb;
+
+ dev->ext_msg = 1;
+ dev->std_msg = 1;
+}
+
+static int flexcan_device_attach(struct flexcan_device *flexcan)
+{
+ int ret;
+ struct resource *res;
+ struct platform_device *pdev = flexcan->dev;
+ struct flexcan_platform_data *plat_data = (pdev->dev).platform_data;
+
+ res = platform_get_resource(flexcan->dev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ flexcan->io_base = ioremap(res->start, res->end - res->start + 1);
+ if (!flexcan->io_base)
+ return -ENOMEM;
+
+ flexcan->irq = platform_get_irq(flexcan->dev, 0);
+ if (!flexcan->irq) {
+ ret = -ENODEV;
+ goto no_irq_err;
+ }
+
+ ret = -EINVAL;
+ if (plat_data) {
+ if (plat_data->core_reg) {
+ flexcan->core_reg = regulator_get(&pdev->dev,
+ plat_data->core_reg);
+ if (!flexcan->core_reg)
+ goto plat_err;
+ }
+
+ if (plat_data->io_reg) {
+ flexcan->io_reg = regulator_get(&pdev->dev,
+ plat_data->io_reg);
+ if (!flexcan->io_reg)
+ goto plat_err;
+ }
+ }
+ flexcan->clk = clk_get(&(flexcan->dev)->dev, "can_clk");
+ flexcan->hwmb = (struct can_hw_mb *)(flexcan->io_base + CAN_MB_BASE);
+ flexcan->rx_mask = (unsigned int *)(flexcan->io_base + CAN_RXMASK_BASE);
+
+ return 0;
+ plat_err:
+ if (flexcan->core_reg) {
+ regulator_put(flexcan->core_reg);
+ flexcan->core_reg = NULL;
+ }
+ no_irq_err:
+ if (flexcan->io_base)
+ iounmap(flexcan->io_base);
+ return ret;
+}
+
+static void flexcan_device_detach(struct flexcan_device *flexcan)
+{
+ struct platform_device *pdev = flexcan->dev;
+ if (flexcan->clk) {
+ clk_put(flexcan->clk);
+ flexcan->clk = NULL;
+ }
+
+ if (flexcan->io_reg) {
+ regulator_put(flexcan->io_reg);
+ flexcan->io_reg = NULL;
+ }
+
+ if (flexcan->core_reg) {
+ regulator_put(flexcan->core_reg);
+ flexcan->core_reg = NULL;
+ }
+
+ if (flexcan->io_base)
+ iounmap(flexcan->io_base);
+}
+
+/*!
+ * @brief The function allocates can device.
+ *
+ * @param pdev the pointer of platform device.
+ * @param setup the initial function pointer of network device.
+ *
+ * @return none
+ */
+struct net_device *flexcan_device_alloc(struct platform_device *pdev,
+ void (*setup) (struct net_device *dev))
+{
+ struct flexcan_device *flexcan;
+ struct net_device *net;
+ int i, num;
+
+ net = alloc_netdev(sizeof(*flexcan), "can%d", setup);
+ if (net == NULL) {
+ printk(KERN_ERR "Allocate netdevice for FlexCAN fail!\n");
+ return NULL;
+ }
+ flexcan = netdev_priv(net);
+ memset(flexcan, 0, sizeof(*flexcan));
+
+ mutex_init(&flexcan->mutex);
+ init_timer(&flexcan->timer);
+
+ flexcan->dev = pdev;
+ if (flexcan_device_attach(flexcan)) {
+ printk(KERN_ERR "Attach FlexCAN fail!\n");
+ free_netdev(net);
+ return NULL;
+ }
+ flexcan_device_default(flexcan);
+ flexcan_update_bitrate(flexcan);
+
+ num = ARRAY_SIZE(flexcan_dev_attr);
+
+ for (i = 0; i < num; i++) {
+ if (device_create_file(&pdev->dev, flexcan_dev_attr + i)) {
+ printk(KERN_ERR "Create attribute file fail!\n");
+ break;
+ }
+ }
+
+ if (i != num) {
+ for (; i >= 0; i--)
+ device_remove_file(&pdev->dev, flexcan_dev_attr + i);
+ free_netdev(net);
+ return NULL;
+ }
+ dev_set_drvdata(&pdev->dev, net);
+ return net;
+}
+
+/*!
+ * @brief The function frees can device.
+ *
+ * @param pdev the pointer of platform device.
+ *
+ * @return none
+ */
+void flexcan_device_free(struct platform_device *pdev)
+{
+ struct net_device *net;
+ struct flexcan_device *flexcan;
+ int i, num;
+ net = (struct net_device *)dev_get_drvdata(&pdev->dev);
+
+ unregister_netdev(net);
+ flexcan = netdev_priv(net);
+ del_timer(&flexcan->timer);
+
+ num = ARRAY_SIZE(flexcan_dev_attr);
+
+ for (i = 0; i < num; i++)
+ device_remove_file(&pdev->dev, flexcan_dev_attr + i);
+
+ flexcan_device_detach(netdev_priv(net));
+ free_netdev(net);
+}
diff --git a/drivers/net/can/flexcan/drv.c b/drivers/net/can/flexcan/drv.c
new file mode 100644
index 000000000000..1eaeac50d6ee
--- /dev/null
+++ b/drivers/net/can/flexcan/drv.c
@@ -0,0 +1,624 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file drv.c
+ *
+ * @brief Driver for Freescale CAN Controller FlexCAN.
+ *
+ * @ingroup can
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <mach/hardware.h>
+#include "flexcan.h"
+
+static void flexcan_hw_start(struct flexcan_device *flexcan)
+{
+ unsigned int reg;
+ if ((flexcan->maxmb + 1) > 32) {
+ __raw_writel(0xFFFFFFFF, flexcan->io_base + CAN_HW_REG_IMASK1);
+ reg = (1 << (flexcan->maxmb - 31)) - 1;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_IMASK2);
+ } else {
+ reg = (1 << (flexcan->maxmb + 1)) - 1;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_IMASK1);
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_IMASK2);
+ }
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR) & (~__MCR_HALT);
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_MCR);
+}
+
+static void flexcan_hw_stop(struct flexcan_device *flexcan)
+{
+ unsigned int reg;
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ __raw_writel(reg | __MCR_HALT, flexcan->io_base + CAN_HW_REG_MCR);
+}
+
+static int flexcan_hw_reset(struct flexcan_device *flexcan)
+{
+ unsigned int reg;
+ int timeout = 100000;
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ __raw_writel(reg | __MCR_MDIS, flexcan->io_base + CAN_HW_REG_MCR);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_CTRL);
+ if (flexcan->br_clksrc)
+ reg |= __CTRL_CLK_SRC;
+ else
+ reg &= ~__CTRL_CLK_SRC;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_CTRL);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR) & (~__MCR_MDIS);
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_MCR);
+ reg |= __MCR_SOFT_RST;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_MCR);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ while (reg & __MCR_SOFT_RST) {
+ if (--timeout <= 0) {
+ printk(KERN_ERR "Flexcan software Reset Timeouted\n");
+ return -1;
+ }
+ udelay(10);
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ }
+ return 0;
+}
+
+static inline void flexcan_mcr_setup(struct flexcan_device *flexcan)
+{
+ unsigned int reg;
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ reg &= ~(__MCR_MAX_MB_MASK | __MCR_WAK_MSK | __MCR_MAX_IDAM_MASK);
+
+ if (flexcan->fifo)
+ reg |= __MCR_FEN;
+ else
+ reg &= ~__MCR_FEN;
+
+ if (flexcan->wakeup)
+ reg |= __MCR_SLF_WAK | __MCR_WAK_MSK;
+ else
+ reg &= ~(__MCR_SLF_WAK | __MCR_WAK_MSK);
+
+ if (flexcan->wak_src)
+ reg |= __MCR_WAK_SRC;
+ else
+ reg &= ~__MCR_WAK_SRC;
+
+ if (flexcan->srx_dis)
+ reg |= __MCR_SRX_DIS;
+ else
+ reg &= ~__MCR_SRX_DIS;
+
+ if (flexcan->bcc)
+ reg |= __MCR_BCC;
+ else
+ reg &= ~__MCR_BCC;
+
+ if (flexcan->lprio)
+ reg |= __MCR_LPRIO_EN;
+ else
+ reg &= ~__MCR_LPRIO_EN;
+
+ if (flexcan->abort)
+ reg |= __MCR_AEN;
+ else
+ reg &= ~__MCR_AEN;
+
+ reg |= (flexcan->maxmb << __MCR_MAX_MB_OFFSET);
+ reg |= __MCR_DOZE | __MCR_MAX_IDAM_C;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_MCR);
+}
+
+static inline void flexcan_ctrl_setup(struct flexcan_device *flexcan)
+{
+ unsigned int reg;
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_CTRL);
+ reg &= ~(__CTRL_PRESDIV_MASK | __CTRL_RJW_MASK | __CTRL_PSEG1_MASK |
+ __CTRL_PSEG2_MASK | __CTRL_PROPSEG_MASK);
+
+ if (flexcan->loopback)
+ reg |= __CTRL_LPB;
+ else
+ reg &= ~__CTRL_LPB;
+
+ if (flexcan->smp)
+ reg |= __CTRL_SMP;
+ else
+ reg &= ~__CTRL_SMP;
+
+ if (flexcan->boff_rec)
+ reg |= __CTRL_BOFF_REC;
+ else
+ reg &= ~__CTRL_BOFF_REC;
+
+ if (flexcan->tsyn)
+ reg |= __CTRL_TSYN;
+ else
+ reg &= ~__CTRL_TSYN;
+
+ if (flexcan->listen)
+ reg |= __CTRL_LOM;
+ else
+ reg &= ~__CTRL_LOM;
+
+ reg |= (flexcan->br_presdiv << __CTRL_PRESDIV_OFFSET) |
+ (flexcan->br_rjw << __CTRL_RJW_OFFSET) |
+ (flexcan->br_pseg1 << __CTRL_PSEG1_OFFSET) |
+ (flexcan->br_pseg2 << __CTRL_PSEG2_OFFSET) |
+ (flexcan->br_propseg << __CTRL_PROPSEG_OFFSET);
+
+ reg &= ~__CTRL_LBUF;
+
+ reg |= __CTRL_TWRN_MSK | __CTRL_RWRN_MSK | __CTRL_BOFF_MSK |
+ __CTRL_ERR_MSK;
+
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_CTRL);
+}
+
+static int flexcan_hw_restart(struct net_device *dev)
+{
+ unsigned int reg;
+ struct flexcan_device *flexcan = netdev_priv(dev);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ if (reg & __MCR_SOFT_RST)
+ return 1;
+
+ flexcan_mcr_setup(flexcan);
+
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_IMASK2);
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_IMASK1);
+
+ __raw_writel(0xFFFFFFFF, flexcan->io_base + CAN_HW_REG_IFLAG2);
+ __raw_writel(0xFFFFFFFF, flexcan->io_base + CAN_HW_REG_IFLAG1);
+
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_ECR);
+
+ flexcan_mbm_init(flexcan);
+ netif_carrier_on(dev);
+ flexcan_hw_start(flexcan);
+
+ if (netif_queue_stopped(dev))
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static void flexcan_hw_watch(unsigned long data)
+{
+ unsigned int reg, ecr;
+ struct net_device *dev = (struct net_device *)data;
+ struct flexcan_device *flexcan = dev ? netdev_priv(dev) : NULL;
+
+ BUG_ON(!flexcan);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ if (reg & __MCR_MDIS) {
+ if (flexcan_hw_restart(dev))
+ mod_timer(&flexcan->timer, HZ / 20);
+ return;
+ }
+ ecr = __raw_readl(flexcan->io_base + CAN_HW_REG_ECR);
+ if (flexcan->boff_rec) {
+ if (((reg & __ESR_FLT_CONF_MASK) >> __ESR_FLT_CONF_OFF) > 1) {
+ reg |= __MCR_SOFT_RST;
+ __raw_writel(reg, flexcan->io_base + CAN_HW_REG_MCR);
+ mod_timer(&flexcan->timer, HZ / 20);
+ return;
+ }
+ netif_carrier_on(dev);
+ }
+}
+
+static void flexcan_hw_busoff(struct net_device *dev)
+{
+ struct flexcan_device *flexcan = netdev_priv(dev);
+ unsigned int reg;
+
+ netif_carrier_off(dev);
+
+ flexcan->timer.function = flexcan_hw_watch;
+ flexcan->timer.data = (unsigned long)dev;
+
+ if (flexcan->boff_rec) {
+ mod_timer(&flexcan->timer, HZ / 10);
+ return;
+ }
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
+ __raw_writel(reg | __MCR_SOFT_RST, flexcan->io_base + CAN_HW_REG_MCR);
+ mod_timer(&flexcan->timer, HZ / 20);
+}
+
+static int flexcan_hw_open(struct flexcan_device *flexcan)
+{
+ if (flexcan_hw_reset(flexcan))
+ return -EFAULT;
+
+ flexcan_mcr_setup(flexcan);
+ flexcan_ctrl_setup(flexcan);
+
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_IMASK2);
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_IMASK1);
+
+ __raw_writel(0xFFFFFFFF, flexcan->io_base + CAN_HW_REG_IFLAG2);
+ __raw_writel(0xFFFFFFFF, flexcan->io_base + CAN_HW_REG_IFLAG1);
+
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_ECR);
+ return 0;
+}
+
+static void flexcan_err_handler(struct net_device *dev)
+{
+ struct flexcan_device *flexcan = netdev_priv(dev);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ unsigned int esr, ecr;
+
+ esr = __raw_readl(flexcan->io_base + CAN_HW_REG_ESR);
+ __raw_writel(esr & __ESR_INTERRUPTS, flexcan->io_base + CAN_HW_REG_ESR);
+
+ if (esr & __ESR_WAK_INT)
+ return;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ printk(KERN_ERR "%s: allocates skb fail in\n", __func__);
+ return;
+ }
+ frame = (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+ frame->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ frame->can_dlc = CAN_ERR_DLC;
+
+ if (esr & __ESR_TWRN_INT)
+ frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+
+ if (esr & __ESR_RWRN_INT)
+ frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+
+ if (esr & __ESR_BOFF_INT)
+ frame->can_id |= CAN_ERR_BUSOFF;
+
+ if (esr & __ESR_ERR_INT) {
+ if (esr & __ESR_BIT1_ERR)
+ frame->data[2] |= CAN_ERR_PROT_BIT1;
+
+ if (esr & __ESR_BIT0_ERR)
+ frame->data[2] |= CAN_ERR_PROT_BIT0;
+
+ if (esr & __ESR_ACK_ERR)
+ frame->can_id |= CAN_ERR_ACK;
+
+ /*TODO:// if (esr & __ESR_CRC_ERR) */
+
+ if (esr & __ESR_FRM_ERR)
+ frame->data[2] |= CAN_ERR_PROT_FORM;
+
+ if (esr & __ESR_STF_ERR)
+ frame->data[2] |= CAN_ERR_PROT_STUFF;
+
+ ecr = __raw_readl(flexcan->io_base + CAN_HW_REG_ECR);
+ switch ((esr & __ESR_FLT_CONF_MASK) >> __ESR_FLT_CONF_OFF) {
+ case 0:
+ if (__ECR_TX_ERR_COUNTER(ecr) >= __ECR_ACTIVE_THRESHOLD)
+ frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+ if (__ECR_RX_ERR_COUNTER(ecr) >= __ECR_ACTIVE_THRESHOLD)
+ frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+ break;
+ case 1:
+ if (__ECR_TX_ERR_COUNTER(ecr) >=
+ __ECR_PASSIVE_THRESHOLD)
+ frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+
+ if (__ECR_RX_ERR_COUNTER(ecr) >=
+ __ECR_PASSIVE_THRESHOLD)
+ frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+ break;
+ default:
+ frame->can_id |= CAN_ERR_BUSOFF;
+ }
+ }
+
+ if (frame->can_id & CAN_ERR_BUSOFF)
+ flexcan_hw_busoff(dev);
+
+ skb->dev = dev;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_receive_skb(skb);
+}
+
+static irqreturn_t flexcan_irq_handler(int irq, void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct flexcan_device *flexcan = dev ? netdev_priv(dev) : NULL;
+ unsigned int reg;
+
+ BUG_ON(!flexcan);
+
+ reg = __raw_readl(flexcan->io_base + CAN_HW_REG_ESR);
+ if (reg & __ESR_INTERRUPTS) {
+ flexcan_err_handler(dev);
+ return IRQ_HANDLED;
+ }
+
+ flexcan_mbm_isr(dev);
+ return IRQ_HANDLED;
+}
+
+static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_frame *frame = (struct can_frame *)skb->data;
+ struct flexcan_device *flexcan = netdev_priv(dev);
+ struct net_device_stats *stats = dev->get_stats(dev);
+
+ BUG_ON(!flexcan);
+
+ if (frame->can_dlc > 8)
+ return -EINVAL;
+
+ if (!flexcan_mbm_xmit(flexcan, frame)) {
+ dev_kfree_skb(skb);
+ stats->tx_bytes += frame->can_dlc;
+ stats->tx_packets++;
+ dev->trans_start = jiffies;
+ return NETDEV_TX_OK;
+ }
+ netif_stop_queue(dev);
+ return NETDEV_TX_BUSY;
+}
+
+static int flexcan_open(struct net_device *dev)
+{
+ struct flexcan_device *flexcan;
+ struct platform_device *pdev;
+ struct flexcan_platform_data *plat_data;
+
+ flexcan = netdev_priv(dev);
+ BUG_ON(!flexcan);
+
+ pdev = flexcan->dev;
+ plat_data = (pdev->dev).platform_data;
+ if (plat_data && plat_data->active)
+ plat_data->active(pdev->id);
+
+ if (flexcan->clk)
+ if (clk_enable(flexcan->clk))
+ goto clk_err;
+
+ if (flexcan->core_reg)
+ if (regulator_enable(flexcan->core_reg))
+ goto core_reg_err;
+
+ if (flexcan->io_reg)
+ if (regulator_enable(flexcan->io_reg))
+ goto io_reg_err;
+
+ if (plat_data && plat_data->xcvr_enable)
+ plat_data->xcvr_enable(pdev->id, 1);
+
+ if (request_irq(flexcan->irq, flexcan_irq_handler, IRQF_SAMPLE_RANDOM,
+ dev->name, dev))
+ goto irq_err;
+
+ if (flexcan_hw_open(flexcan))
+ goto open_err;
+
+ flexcan_mbm_init(flexcan);
+ netif_carrier_on(dev);
+ flexcan_hw_start(flexcan);
+ return 0;
+ open_err:
+ free_irq(flexcan->irq, dev);
+ irq_err:
+ if (plat_data && plat_data->xcvr_enable)
+ plat_data->xcvr_enable(pdev->id, 0);
+
+ if (flexcan->io_reg)
+ regulator_disable(flexcan->io_reg);
+ io_reg_err:
+ if (flexcan->core_reg)
+ regulator_disable(flexcan->core_reg);
+ core_reg_err:
+ if (flexcan->clk)
+ clk_disable(flexcan->clk);
+ clk_err:
+ if (plat_data && plat_data->inactive)
+ plat_data->inactive(pdev->id);
+ return -ENODEV;
+}
+
+static int flexcan_stop(struct net_device *dev)
+{
+ struct flexcan_device *flexcan;
+ struct platform_device *pdev;
+ struct flexcan_platform_data *plat_data;
+
+ flexcan = netdev_priv(dev);
+
+ BUG_ON(!flexcan);
+
+ pdev = flexcan->dev;
+ plat_data = (pdev->dev).platform_data;
+
+ flexcan_hw_stop(flexcan);
+
+ free_irq(flexcan->irq, dev);
+
+ if (plat_data && plat_data->xcvr_enable)
+ plat_data->xcvr_enable(pdev->id, 0);
+
+ if (flexcan->io_reg)
+ regulator_disable(flexcan->io_reg);
+ if (flexcan->core_reg)
+ regulator_disable(flexcan->core_reg);
+ if (flexcan->clk)
+ clk_disable(flexcan->clk);
+ if (plat_data && plat_data->inactive)
+ plat_data->inactive(pdev->id);
+ return 0;
+}
+
+static void flexcan_setup(struct net_device *dev)
+{
+ dev->type = ARPHRD_CAN;
+ dev->mtu = sizeof(struct can_frame);
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = FLEXCAN_MAX_MB;
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+
+ dev->open = flexcan_open;
+ dev->stop = flexcan_stop;
+ dev->hard_start_xmit = flexcan_start_xmit;
+}
+
+static int flexcan_probe(struct platform_device *pdev)
+{
+ struct net_device *net;
+ net = flexcan_device_alloc(pdev, flexcan_setup);
+ if (!net)
+ return -ENOMEM;
+
+ if (register_netdev(net)) {
+ flexcan_device_free(pdev);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int flexcan_remove(struct platform_device *pdev)
+{
+ flexcan_device_free(pdev);
+ return 0;
+}
+
+static int flexcan_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *net;
+ struct flexcan_device *flexcan;
+ struct flexcan_platform_data *plat_data;
+ net = (struct net_device *)dev_get_drvdata(&pdev->dev);
+ flexcan = netdev_priv(net);
+
+ BUG_ON(!flexcan);
+
+ if (!(net->flags & IFF_UP))
+ return 0;
+ if (flexcan->wakeup)
+ set_irq_wake(flexcan->irq, 1);
+ else {
+ plat_data = (pdev->dev).platform_data;
+
+ if (plat_data && plat_data->xcvr_enable)
+ plat_data->xcvr_enable(pdev->id, 0);
+
+ if (flexcan->io_reg)
+ regulator_disable(flexcan->io_reg);
+ if (flexcan->core_reg)
+ regulator_disable(flexcan->core_reg);
+ if (flexcan->clk)
+ clk_disable(flexcan->clk);
+ if (plat_data && plat_data->inactive)
+ plat_data->inactive(pdev->id);
+ }
+ return 0;
+}
+
+static int flexcan_resume(struct platform_device *pdev)
+{
+ struct net_device *net;
+ struct flexcan_device *flexcan;
+ struct flexcan_platform_data *plat_data;
+ net = (struct net_device *)dev_get_drvdata(&pdev->dev);
+ flexcan = netdev_priv(net);
+
+ BUG_ON(!flexcan);
+
+ if (!(net->flags & IFF_UP))
+ return 0;
+
+ if (flexcan->wakeup)
+ set_irq_wake(flexcan->irq, 0);
+ else {
+ plat_data = (pdev->dev).platform_data;
+ if (plat_data && plat_data->active)
+ plat_data->active(pdev->id);
+
+ if (flexcan->clk) {
+ if (clk_enable(flexcan->clk))
+ printk(KERN_ERR "%s:enable clock fail\n",
+ __func__);
+ }
+
+ if (flexcan->core_reg) {
+ if (regulator_enable(flexcan->core_reg))
+ printk(KERN_ERR "%s:enable core voltage\n",
+ __func__);
+ }
+ if (flexcan->io_reg) {
+ if (regulator_enable(flexcan->io_reg))
+ printk(KERN_ERR "%s:enable io voltage\n",
+ __func__);
+ }
+
+ if (plat_data && plat_data->xcvr_enable)
+ plat_data->xcvr_enable(pdev->id, 1);
+ }
+ return 0;
+}
+
+static struct platform_driver flexcan_driver = {
+ .driver = {
+ .name = FLEXCAN_DEVICE_NAME,
+ },
+ .probe = flexcan_probe,
+ .remove = flexcan_remove,
+ .suspend = flexcan_suspend,
+ .resume = flexcan_resume,
+};
+
+static __init int flexcan_init(void)
+{
+ pr_info("Freescale FlexCAN Driver \n");
+ return platform_driver_register(&flexcan_driver);
+}
+
+static __exit void flexcan_exit(void)
+{
+ return platform_driver_unregister(&flexcan_driver);
+}
+
+module_init(flexcan_init);
+module_exit(flexcan_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/can/flexcan/flexcan.h b/drivers/net/can/flexcan/flexcan.h
new file mode 100644
index 000000000000..d19cc1ee0620
--- /dev/null
+++ b/drivers/net/can/flexcan/flexcan.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file flexcan.h
+ *
+ * @brief FlexCan definitions.
+ *
+ * @ingroup can
+ */
+
+#ifndef __CAN_FLEXCAN_H__
+#define __CAN_FLEXCAN_H__
+
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <linux/can/error.h>
+
+#define FLEXCAN_DEVICE_NAME "FlexCAN"
+
+struct can_mb_cs {
+ unsigned int time_stamp:16;
+ unsigned int length:4;
+ unsigned int rtr:1;
+ unsigned int ide:1;
+ unsigned int srr:1;
+ unsigned int nouse1:1;
+ unsigned int code:4;
+ unsigned int nouse2:4;
+};
+
+#define CAN_MB_RX_INACTIVE 0x0
+#define CAN_MB_RX_EMPTY 0x4
+#define CAN_MB_RX_FULL 0x2
+#define CAN_MB_RX_OVERRUN 0x6
+#define CAN_MB_RX_BUSY 0x1
+
+#define CAN_MB_TX_INACTIVE 0x8
+#define CAN_MB_TX_ABORT 0x9
+#define CAN_MB_TX_ONCE 0xC
+#define CAN_MB_TX_REMOTE 0xA
+
+struct can_hw_mb {
+ union {
+ struct can_mb_cs cs;
+ unsigned int data;
+ } mb_cs;
+ unsigned int mb_id;
+ unsigned char mb_data[8];
+};
+
+#define CAN_HW_REG_MCR 0x00
+#define CAN_HW_REG_CTRL 0x04
+#define CAN_HW_REG_TIMER 0x08
+#define CAN_HW_REG_RXGMASK 0x10
+#define CAN_HW_REG_RX14MASK 0x14
+#define CAN_HW_REG_RX15MASK 0x18
+#define CAN_HW_REG_ECR 0x1C
+#define CAN_HW_REG_ESR 0x20
+#define CAN_HW_REG_IMASK2 0x24
+#define CAN_HW_REG_IMASK1 0x28
+#define CAN_HW_REG_IFLAG2 0x2C
+#define CAN_HW_REG_IFLAG1 0x30
+
+#define CAN_MB_BASE 0x0080
+#define CAN_RXMASK_BASE 0x0880
+#define CAN_FIFO_BASE 0xE0
+
+#define __MCR_MDIS (1 << 31)
+#define __MCR_FRZ (1 << 30)
+#define __MCR_FEN (1 << 29)
+#define __MCR_HALT (1 << 28)
+#define __MCR_NOTRDY (1 << 27)
+#define __MCR_WAK_MSK (1 << 26)
+#define __MCR_SOFT_RST (1 << 25)
+#define __MCR_FRZ_ACK (1 << 24)
+#define __MCR_SLF_WAK (1 << 22)
+#define __MCR_WRN_EN (1 << 21)
+#define __MCR_LPM_ACK (1 << 20)
+#define __MCR_WAK_SRC (1 << 19)
+#define __MCR_DOZE (1 << 18)
+#define __MCR_SRX_DIS (1 << 17)
+#define __MCR_BCC (1 << 16)
+#define __MCR_LPRIO_EN (1 << 13)
+#define __MCR_AEN (1 << 12)
+#define __MCR_MAX_IDAM_OFFSET 8
+#define __MCR_MAX_IDAM_MASK (0x3 << __MCR_MAX_IDAM_OFFSET)
+#define __MCR_MAX_IDAM_A (0x0 << __MCR_MAX_IDAM_OFFSET)
+#define __MCR_MAX_IDAM_B (0x1 << __MCR_MAX_IDAM_OFFSET)
+#define __MCR_MAX_IDAM_C (0x2 << __MCR_MAX_IDAM_OFFSET)
+#define __MCR_MAX_IDAM_D (0x3 << __MCR_MAX_IDAM_OFFSET)
+#define __MCR_MAX_MB_OFFSET 0
+#define __MCR_MAX_MB_MASK (0x3F)
+
+#define __CTRL_PRESDIV_OFFSET 24
+#define __CTRL_PRESDIV_MASK (0xFF << __CTRL_PRESDIV_OFFSET)
+#define __CTRL_RJW_OFFSET 22
+#define __CTRL_RJW_MASK (0x3 << __CTRL_RJW_OFFSET)
+#define __CTRL_PSEG1_OFFSET 19
+#define __CTRL_PSEG1_MASK (0x7 << __CTRL_PSEG1_OFFSET)
+#define __CTRL_PSEG2_OFFSET 16
+#define __CTRL_PSEG2_MASK (0x7 << __CTRL_PSEG2_OFFSET)
+#define __CTRL_BOFF_MSK (0x1 << 15)
+#define __CTRL_ERR_MSK (0x1 << 14)
+#define __CTRL_CLK_SRC (0x1 << 13)
+#define __CTRL_LPB (0x1 << 12)
+#define __CTRL_TWRN_MSK (0x1 << 11)
+#define __CTRL_RWRN_MSK (0x1 << 10)
+#define __CTRL_SMP (0x1 << 7)
+#define __CTRL_BOFF_REC (0x1 << 6)
+#define __CTRL_TSYN (0x1 << 5)
+#define __CTRL_LBUF (0x1 << 4)
+#define __CTRL_LOM (0x1 << 3)
+#define __CTRL_PROPSEG_OFFSET 0
+#define __CTRL_PROPSEG_MASK (0x7)
+
+#define __ECR_TX_ERR_COUNTER(x) ((x) & 0xFF)
+#define __ECR_RX_ERR_COUNTER(x) (((x) >> 8) & 0xFF)
+#define __ECR_PASSIVE_THRESHOLD 128
+#define __ECR_ACTIVE_THRESHOLD 96
+
+#define __ESR_TWRN_INT (0x1 << 17)
+#define __ESR_RWRN_INT (0x1 << 16)
+#define __ESR_BIT1_ERR (0x1 << 15)
+#define __ESR_BIT0_ERR (0x1 << 14)
+#define __ESR_ACK_ERR (0x1 << 13)
+#define __ESR_CRC_ERR (0x1 << 12)
+#define __ESR_FRM_ERR (0x1 << 11)
+#define __ESR_STF_ERR (0x1 << 10)
+#define __ESR_TX_WRN (0x1 << 9)
+#define __ESR_RX_WRN (0x1 << 8)
+#define __ESR_IDLE (0x1 << 7)
+#define __ESR_TXRX (0x1 << 6)
+#define __ESR_FLT_CONF_OFF 4
+#define __ESR_FLT_CONF_MASK (0x3 << __ESR_FLT_CONF_OFF)
+#define __ESR_BOFF_INT (0x1 << 2)
+#define __ESR_ERR_INT (0x1 << 1)
+#define __ESR_WAK_INT (0x1)
+
+#define __ESR_INTERRUPTS (__ESR_WAK_INT | __ESR_ERR_INT | \
+ __ESR_BOFF_INT | __ESR_TWRN_INT | \
+ __ESR_RWRN_INT)
+
+#define __FIFO_OV_INT 0x0080
+#define __FIFO_WARN_INT 0x0040
+#define __FIFO_RDY_INT 0x0020
+
+struct flexcan_device {
+ struct mutex mutex;
+ void *io_base;
+ struct can_hw_mb *hwmb;
+ unsigned int *rx_mask;
+ unsigned int xmit_mb;
+ unsigned int bitrate;
+ /* word 1 */
+ unsigned int br_presdiv:8;
+ unsigned int br_rjw:2;
+ unsigned int br_propseg:3;
+ unsigned int br_pseg1:3;
+ unsigned int br_pseg2:3;
+ unsigned int maxmb:6;
+ unsigned int xmit_maxmb:6;
+ unsigned int wd1_resv:1;
+
+ /* word 2 */
+ unsigned int fifo:1;
+ unsigned int wakeup:1;
+ unsigned int srx_dis:1;
+ unsigned int wak_src:1;
+ unsigned int bcc:1;
+ unsigned int lprio:1;
+ unsigned int abort:1;
+ unsigned int br_clksrc:1;
+ unsigned int loopback:1;
+ unsigned int smp:1;
+ unsigned int boff_rec:1;
+ unsigned int tsyn:1;
+ unsigned int listen:1;
+
+ unsigned int ext_msg:1;
+ unsigned int std_msg:1;
+
+ struct timer_list timer;
+ struct platform_device *dev;
+ struct regulator *core_reg;
+ struct regulator *io_reg;
+ struct clk *clk;
+ int irq;
+};
+
+#define FLEXCAN_MAX_FIFO_MB 8
+#define FLEXCAN_MAX_MB 64
+#define FLEXCAN_MAX_PRESDIV 256
+#define FLEXCAN_MAX_RJW 4
+#define FLEXCAN_MAX_PSEG1 8
+#define FLEXCAN_MAX_PSEG2 8
+#define FLEXCAN_MAX_PROPSEG 8
+#define FLEXCAN_MAX_BITRATE 1000000
+
+extern struct net_device *flexcan_device_alloc(struct platform_device *pdev,
+ void (*setup) (struct net_device
+ *dev));
+extern void flexcan_device_free(struct platform_device *pdev);
+
+extern void flexcan_mbm_init(struct flexcan_device *flexcan);
+extern void flexcan_mbm_isr(struct net_device *dev);
+extern int flexcan_mbm_xmit(struct flexcan_device *flexcan,
+ struct can_frame *frame);
+#endif /* __CAN_FLEXCAN_H__ */
diff --git a/drivers/net/can/flexcan/mbm.c b/drivers/net/can/flexcan/mbm.c
new file mode 100644
index 000000000000..ca1300503e02
--- /dev/null
+++ b/drivers/net/can/flexcan/mbm.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mbm.c
+ *
+ * @brief Driver for Freescale CAN Controller FlexCAN.
+ *
+ * @ingroup can
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "flexcan.h"
+
+#define flexcan_swab32(x) \
+ (((x) << 24) | ((x) >> 24) |\
+ (((x) & (__u32)0x0000ff00UL) << 8) |\
+ (((x) & (__u32)0x00ff0000UL) >> 8))
+
+static inline void flexcan_memcpy(void *dst, void *src, int len)
+{
+ int i;
+ unsigned int *d = (unsigned int *)dst, *s = (unsigned int *)src;
+ len = (len + 3) >> 2;
+ for (i = 0; i < len; i++, s++, d++)
+ *d = flexcan_swab32(*s);
+}
+
+static void flexcan_mb_bottom(struct net_device *dev, int index)
+{
+ struct flexcan_device *flexcan = netdev_priv(dev);
+ struct net_device_stats *stats = dev->get_stats(dev);
+ struct can_hw_mb *hwmb;
+ struct can_frame *frame;
+ struct sk_buff *skb;
+ unsigned int tmp;
+
+ hwmb = flexcan->hwmb + index;
+ if (flexcan->fifo || (index >= (flexcan->maxmb - flexcan->xmit_maxmb))) {
+ if (hwmb->mb_cs.cs.code == CAN_MB_TX_ABORT)
+ hwmb->mb_cs.cs.code = CAN_MB_TX_INACTIVE;
+
+ if (hwmb->mb_cs.cs.code & CAN_MB_TX_INACTIVE) {
+ if (netif_queue_stopped(dev))
+ netif_start_queue(dev);
+ return;
+ }
+ }
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb) {
+ frame = (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+ if (hwmb->mb_cs.cs.ide)
+ frame->can_id =
+ (hwmb->mb_id & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ frame->can_id = (hwmb->mb_id >> 18) & CAN_SFF_MASK;
+
+ if (hwmb->mb_cs.cs.rtr)
+ frame->can_id |= CAN_RTR_FLAG;
+
+ frame->can_dlc = hwmb->mb_cs.cs.length;
+
+ if (frame->can_dlc && frame->can_dlc)
+ flexcan_memcpy(frame->data, hwmb->mb_data,
+ frame->can_dlc);
+
+ if (flexcan->fifo
+ || (index >= (flexcan->maxmb - flexcan->xmit_maxmb))) {
+ hwmb->mb_cs.cs.code = CAN_MB_TX_INACTIVE;
+ if (netif_queue_stopped(dev))
+ netif_start_queue(dev);
+ }
+
+ tmp = __raw_readl(flexcan->io_base + CAN_HW_REG_TIMER);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+ } else {
+ tmp = hwmb->mb_cs.data;
+ tmp = hwmb->mb_id;
+ tmp = hwmb->mb_data[0];
+ if (flexcan->fifo
+ || (index >= (flexcan->maxmb - flexcan->xmit_maxmb))) {
+
+ hwmb->mb_cs.cs.code = CAN_MB_TX_INACTIVE;
+ if (netif_queue_stopped(dev))
+ netif_start_queue(dev);
+ }
+ tmp = __raw_readl(flexcan->io_base + CAN_HW_REG_TIMER);
+ stats->rx_dropped++;
+ }
+}
+
+static void flexcan_fifo_isr(struct net_device *dev, unsigned int iflag1)
+{
+ struct flexcan_device *flexcan = dev ? netdev_priv(dev) : NULL;
+ struct net_device_stats *stats = dev->get_stats(dev);
+ struct sk_buff *skb;
+ struct can_hw_mb *hwmb = flexcan->hwmb;
+ struct can_frame *frame;
+ unsigned int tmp;
+
+ if (iflag1 & __FIFO_RDY_INT) {
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb) {
+ frame =
+ (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+ if (hwmb->mb_cs.cs.ide)
+ frame->can_id =
+ (hwmb->mb_id & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ frame->can_id =
+ (hwmb->mb_id >> 18) & CAN_SFF_MASK;
+
+ if (hwmb->mb_cs.cs.rtr)
+ frame->can_id |= CAN_RTR_FLAG;
+
+ frame->can_dlc = hwmb->mb_cs.cs.length;
+
+ if (frame->can_dlc && (frame->can_dlc <= 8))
+ flexcan_memcpy(frame->data, hwmb->mb_data,
+ frame->can_dlc);
+ tmp = __raw_readl(flexcan->io_base + CAN_HW_REG_TIMER);
+
+ dev->last_rx = jiffies;
+
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+ } else {
+ tmp = hwmb->mb_cs.data;
+ tmp = hwmb->mb_id;
+ tmp = hwmb->mb_data[0];
+ tmp = __raw_readl(flexcan->io_base + CAN_HW_REG_TIMER);
+ }
+ }
+
+ if (iflag1 & (__FIFO_OV_INT | __FIFO_WARN_INT)) {
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb) {
+ frame =
+ (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+ frame->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ frame->can_dlc = CAN_ERR_DLC;
+ if (iflag1 & __FIFO_WARN_INT)
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING |
+ CAN_ERR_CRTL_RX_WARNING;
+ if (iflag1 & __FIFO_OV_INT)
+ frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+ }
+ }
+}
+
+/*!
+ * @brief The function call by CAN ISR to handle mb events.
+ *
+ * @param dev the pointer of network device.
+ *
+ * @return none
+ */
+void flexcan_mbm_isr(struct net_device *dev)
+{
+ int i, iflag1, iflag2, maxmb;
+ struct flexcan_device *flexcan = dev ? netdev_priv(dev) : NULL;
+
+ if (flexcan->maxmb > 31) {
+ maxmb = flexcan->maxmb + 1 - 32;
+ iflag1 = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG1) &
+ __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK1);
+ iflag2 = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG2) &
+ __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK2);
+ iflag2 &= (1 << maxmb) - 1;
+ maxmb = 32;
+ } else {
+ maxmb = flexcan->maxmb + 1;
+ iflag1 = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG1) &
+ __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK1);
+ iflag1 &= (1 << maxmb) - 1;
+ iflag2 = 0;
+ }
+
+ __raw_writel(iflag1, flexcan->io_base + CAN_HW_REG_IFLAG1);
+ __raw_writel(iflag2, flexcan->io_base + CAN_HW_REG_IFLAG2);
+
+ if (flexcan->fifo) {
+ flexcan_fifo_isr(dev, iflag1);
+ iflag1 &= 0xFFFFFF00;
+ }
+ for (i = 0; iflag1 && (i < maxmb); i++) {
+ if (iflag1 & (1 << i)) {
+ iflag1 &= ~(1 << i);
+ flexcan_mb_bottom(dev, i);
+ }
+ }
+
+ for (i = maxmb; iflag2 && (i <= flexcan->maxmb); i++) {
+ if (iflag2 & (1 << (i - 32))) {
+ iflag2 &= ~(1 << (i - 32));
+ flexcan_mb_bottom(dev, i);
+ }
+ }
+}
+
+/*!
+ * @brief function to xmit message buffer
+ *
+ * @param flexcan the pointer of can hardware device.
+ * @param frame the pointer of can message frame.
+ *
+ * @return Returns 0 if xmit is success. otherwise returns non-zero.
+ */
+int flexcan_mbm_xmit(struct flexcan_device *flexcan, struct can_frame *frame)
+{
+ int i = flexcan->xmit_mb;
+ struct can_hw_mb *hwmb = flexcan->hwmb;
+
+ do {
+ if (hwmb[i].mb_cs.cs.code == CAN_MB_TX_INACTIVE)
+ break;
+ if ((++i) > flexcan->maxmb) {
+ if (flexcan->fifo)
+ i = FLEXCAN_MAX_FIFO_MB;
+ else
+ i = flexcan->xmit_maxmb + 1;
+ }
+ if (i == flexcan->xmit_mb)
+ return -1;
+ } while (1);
+
+ flexcan->xmit_mb = i + 1;
+ if (flexcan->xmit_mb > flexcan->maxmb) {
+ if (flexcan->fifo)
+ flexcan->xmit_mb = FLEXCAN_MAX_FIFO_MB;
+ else
+ flexcan->xmit_mb = flexcan->xmit_maxmb + 1;
+ }
+
+ if (frame->can_id & CAN_RTR_FLAG)
+ hwmb[i].mb_cs.cs.rtr = 1;
+ else
+ hwmb[i].mb_cs.cs.rtr = 0;
+
+ if (frame->can_id & CAN_EFF_FLAG) {
+ hwmb[i].mb_cs.cs.ide = 1;
+ hwmb[i].mb_cs.cs.srr = 1;
+ hwmb[i].mb_id = frame->can_id & CAN_EFF_MASK;
+ } else {
+ hwmb[i].mb_cs.cs.ide = 0;
+ hwmb[i].mb_id = (frame->can_id & CAN_SFF_MASK) << 18;
+ }
+
+ hwmb[i].mb_cs.cs.length = frame->can_dlc;
+ flexcan_memcpy(hwmb[i].mb_data, frame->data, frame->can_dlc);
+ hwmb[i].mb_cs.cs.code = CAN_MB_TX_ONCE;
+ return 0;
+}
+
+/*!
+ * @brief function to initial message buffer
+ *
+ * @param flexcan the pointer of can hardware device.
+ *
+ * @return none
+ */
+void flexcan_mbm_init(struct flexcan_device *flexcan)
+{
+ struct can_hw_mb *hwmb;
+ int rx_mb, i;
+
+ /* Set global mask to receive all messages */
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_RXGMASK);
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_RX14MASK);
+ __raw_writel(0, flexcan->io_base + CAN_HW_REG_RX15MASK);
+
+ memset(flexcan->hwmb, 0, sizeof(*hwmb) * FLEXCAN_MAX_MB);
+ /* Set individual mask to receive all messages */
+ memset(flexcan->rx_mask, 0, sizeof(unsigned int) * FLEXCAN_MAX_MB);
+
+ if (flexcan->fifo)
+ rx_mb = FLEXCAN_MAX_FIFO_MB;
+ else
+ rx_mb = flexcan->maxmb - flexcan->xmit_maxmb;
+
+ hwmb = flexcan->hwmb;
+ if (flexcan->fifo) {
+ unsigned long *id_table = flexcan->io_base + CAN_FIFO_BASE;
+ for (i = 0; i < rx_mb; i++)
+ id_table[i] = 0;
+ } else {
+ for (i = 0; i < rx_mb; i++) {
+ hwmb[i].mb_cs.cs.code = CAN_MB_RX_EMPTY;
+ /*
+ * IDE bit can not control by mask registers
+ * So set message buffer to receive extend
+ * or standard message.
+ */
+ if (flexcan->ext_msg && flexcan->std_msg)
+ hwmb[i].mb_cs.cs.ide = i & 1;
+ else {
+ if (flexcan->ext_msg)
+ hwmb[i].mb_cs.cs.ide = 1;
+ }
+ }
+ }
+
+ for (; i <= flexcan->maxmb; i++)
+ hwmb[i].mb_cs.cs.code = CAN_MB_TX_INACTIVE;
+
+ flexcan->xmit_mb = rx_mb;
+}
diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c
new file mode 100644
index 000000000000..1dcda35e0d7d
--- /dev/null
+++ b/drivers/net/can/mcp251x.c
@@ -0,0 +1,1239 @@
+/*
+ *
+ * CAN bus driver for Microchip 251x CAN Controller with SPI Interface
+ *
+ * MCP2510 support and bug fixes by Christian Pellegrin
+ * <chripell@evolware.org>
+ *
+ * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved.
+ * Written under contract by:
+ * Chris Elston, Katalix Systems, Ltd.
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ *
+ * Based on CAN bus driver for the CCAN controller written by
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ * Copyright 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ *
+ * Your platform definition file should specify something like:
+ *
+ * static struct mcp251x_platform_data mcp251x_info = {
+ * .oscillator_frequency = 8000000,
+ * .board_specific_setup = &mcp251x_setup,
+ * .model = CAN_MCP251X_MCP2510,
+ * .power_enable = mcp251x_power_enable,
+ * .transceiver_enable = NULL,
+ * };
+ *
+ * static struct spi_board_info spi_board_info[] = {
+ * {
+ * .modalias = "mcp251x",
+ * .platform_data = &mcp251x_info,
+ * .irq = IRQ_EINT13,
+ * .max_speed_hz = 2*1000*1000,
+ * .chip_select = 2,
+ * },
+ * };
+ *
+ * Please see mcp251x.h for a description of the fields in
+ * struct mcp251x_platform_data.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/spi/spi.h>
+#include <linux/can/dev.h>
+#include <linux/can/core.h>
+#include <linux/if_arp.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/can/platform/mcp251x.h>
+
+/* SPI interface instruction set */
+#define INSTRUCTION_WRITE 0x02
+#define INSTRUCTION_READ 0x03
+#define INSTRUCTION_BIT_MODIFY 0x05
+#define INSTRUCTION_LOAD_TXB(n) (0x40 + 2 * (n))
+#define INSTRUCTION_READ_RXB(n) (((n) == 0) ? 0x90 : 0x94)
+#define INSTRUCTION_RESET 0xC0
+
+/* MPC251x registers */
+#define CANSTAT 0x0e
+#define CANCTRL 0x0f
+# define CANCTRL_REQOP_MASK 0xe0
+# define CANCTRL_REQOP_CONF 0x80
+# define CANCTRL_REQOP_LISTEN_ONLY 0x60
+# define CANCTRL_REQOP_LOOPBACK 0x40
+# define CANCTRL_REQOP_SLEEP 0x20
+# define CANCTRL_REQOP_NORMAL 0x00
+# define CANCTRL_OSM 0x08
+# define CANCTRL_ABAT 0x10
+#define TEC 0x1c
+#define REC 0x1d
+#define CNF1 0x2a
+#define CNF2 0x29
+# define CNF2_BTLMODE 0x80
+#define CNF3 0x28
+# define CNF3_SOF 0x08
+# define CNF3_WAKFIL 0x04
+# define CNF3_PHSEG2_MASK 0x07
+#define CANINTE 0x2b
+# define CANINTE_MERRE 0x80
+# define CANINTE_WAKIE 0x40
+# define CANINTE_ERRIE 0x20
+# define CANINTE_TX2IE 0x10
+# define CANINTE_TX1IE 0x08
+# define CANINTE_TX0IE 0x04
+# define CANINTE_RX1IE 0x02
+# define CANINTE_RX0IE 0x01
+#define CANINTF 0x2c
+# define CANINTF_MERRF 0x80
+# define CANINTF_WAKIF 0x40
+# define CANINTF_ERRIF 0x20
+# define CANINTF_TX2IF 0x10
+# define CANINTF_TX1IF 0x08
+# define CANINTF_TX0IF 0x04
+# define CANINTF_RX1IF 0x02
+# define CANINTF_RX0IF 0x01
+#define EFLG 0x2d
+# define EFLG_EWARN 0x01
+# define EFLG_RXWAR 0x02
+# define EFLG_TXWAR 0x04
+# define EFLG_RXEP 0x08
+# define EFLG_TXEP 0x10
+# define EFLG_TXBO 0x20
+# define EFLG_RX0OVR 0x40
+# define EFLG_RX1OVR 0x80
+#define TXBCTRL(n) ((n * 0x10) + 0x30)
+# define TXBCTRL_ABTF 0x40
+# define TXBCTRL_MLOA 0x20
+# define TXBCTRL_TXERR 0x10
+# define TXBCTRL_TXREQ 0x08
+#define RXBCTRL(n) ((n * 0x10) + 0x60)
+# define RXBCTRL_BUKT 0x04
+# define RXBCTRL_RXM0 0x20
+# define RXBCTRL_RXM1 0x40
+
+/* Buffer size required for the largest SPI transfer (i.e., reading a
+ * frame). */
+#define CAN_FRAME_MAX_DATA_LEN 8
+#define SPI_TRANSFER_BUF_LEN (2*(6 + CAN_FRAME_MAX_DATA_LEN))
+#define CAN_FRAME_MAX_BITS 128
+
+#define DEVICE_NAME "mcp251x"
+
+static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
+module_param(mcp251x_enable_dma, int, S_IRUGO);
+MODULE_PARM_DESC(mcp251x_enable_dma, "Enable SPI DMA. Default: 0 (Off)");
+
+static struct can_bittiming_const mcp251x_bittiming_const = {
+ .tseg1_min = 3,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+struct mcp251x_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct spi_device *spi;
+
+ struct mutex spi_lock; /* SPI buffer lock */
+ u8 *spi_tx_buf;
+ u8 *spi_rx_buf;
+ dma_addr_t spi_tx_dma;
+ dma_addr_t spi_rx_dma;
+
+ struct sk_buff *tx_skb;
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+ struct work_struct irq_work;
+ struct completion awake;
+ int wake;
+ int force_quit;
+ int after_suspend;
+#define AFTER_SUSPEND_UP 1
+#define AFTER_SUSPEND_DOWN 2
+#define AFTER_SUSPEND_POWER 4
+ int restart_tx;
+};
+
+static u8 mcp251x_read_reg(struct spi_device *spi, uint8_t reg)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 3,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ u8 val = 0;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_READ;
+ priv->spi_tx_buf[1] = reg;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
+ else
+ val = priv->spi_rx_buf[2];
+
+ mutex_unlock(&priv->spi_lock);
+
+ dev_dbg(&spi->dev, "%s: read %02x = %02x\n", __func__, reg, val);
+ return val;
+}
+
+static void mcp251x_write_reg(struct spi_device *spi, u8 reg, uint8_t val)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 3,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_WRITE;
+ priv->spi_tx_buf[1] = reg;
+ priv->spi_tx_buf[2] = val;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed\n", __func__);
+}
+
+static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
+ u8 mask, uint8_t val)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = 4,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY;
+ priv->spi_tx_buf[1] = reg;
+ priv->spi_tx_buf[2] = mask;
+ priv->spi_tx_buf[3] = val;
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed\n", __func__);
+}
+
+static int mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
+ int tx_buf_idx)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ u32 sid, eid, exide, rtr;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ exide = (frame->can_id & CAN_EFF_FLAG) ? 1 : 0; /* Extended ID Enable */
+ if (exide)
+ sid = (frame->can_id & CAN_EFF_MASK) >> 18;
+ else
+ sid = frame->can_id & CAN_SFF_MASK; /* Standard ID */
+ eid = frame->can_id & CAN_EFF_MASK; /* Extended ID */
+ rtr = (frame->can_id & CAN_RTR_FLAG) ? 1 : 0; /* Remote transmission */
+
+ if (pdata->model == CAN_MCP251X_MCP2510) {
+ int i;
+
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 1, sid >> 3);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 2,
+ ((sid & 7) << 5) | (exide << 3) |
+ ((eid >> 16) & 3));
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 3,
+ (eid >> 8) & 0xff);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 4, eid & 0xff);
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 5,
+ (rtr << 6) | frame->can_dlc);
+
+ for (i = 0; i < frame->can_dlc ; i++) {
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + 6 + i,
+ frame->data[i]);
+ }
+ } else {
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .cs_change = 0,
+ .len = 6 + CAN_FRAME_MAX_DATA_LEN,
+ };
+ struct spi_message m;
+ int ret;
+ u8 *tx_buf = priv->spi_tx_buf;
+
+ mutex_lock(&priv->spi_lock);
+
+ tx_buf[0] = INSTRUCTION_LOAD_TXB(tx_buf_idx);
+ tx_buf[1] = sid >> 3;
+ tx_buf[2] = ((sid & 7) << 5) | (exide << 3) |
+ ((eid >> 16) & 3);
+ tx_buf[3] = (eid >> 8) & 0xff;
+ tx_buf[4] = eid & 0xff;
+ tx_buf[5] = (rtr << 6) | frame->can_dlc;
+
+ memcpy(tx_buf + 6, frame->data, frame->can_dlc);
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__,
+ ret);
+ return -1;
+ }
+ }
+ mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ);
+ return 0;
+}
+
+static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ dev_dbg(&spi->dev, "%s: out of memory for Rx'd frame\n",
+ __func__);
+ priv->net->stats.rx_dropped++;
+ return;
+ }
+ skb->dev = priv->net;
+ frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ if (pdata->model == CAN_MCP251X_MCP2510) {
+ int i;
+ u8 rx_buf[6];
+
+ rx_buf[1] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 1);
+ rx_buf[2] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 2);
+ rx_buf[3] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 3);
+ rx_buf[4] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 4);
+ rx_buf[5] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + 5);
+
+ if ((rx_buf[2] >> 3) & 0x1) {
+ /* Extended ID format */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |= ((rx_buf[2] & 3) << 16) |
+ (rx_buf[3] << 8) | rx_buf[4] |
+ (((rx_buf[1] << 3) | (rx_buf[2] >> 5)) << 18);
+ } else {
+ /* Standard ID format */
+ frame->can_id = (rx_buf[1] << 3) | (rx_buf[2] >> 5);
+ }
+
+ if ((rx_buf[5] >> 6) & 0x1) {
+ /* Remote transmission request */
+ frame->can_id |= CAN_RTR_FLAG;
+ }
+
+ /* Data length */
+ frame->can_dlc = rx_buf[5] & 0x0f;
+ if (frame->can_dlc > 8) {
+ dev_warn(&spi->dev, "invalid frame recevied\n");
+ priv->net->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ for (i = 0; i < frame->can_dlc; i++) {
+ frame->data[i] = mcp251x_read_reg(spi,
+ RXBCTRL(buf_idx) +
+ 6 + i);
+ }
+ } else {
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .cs_change = 0,
+ .len = 14, /* RX buffer: RXBnCTRL to RXBnD7 */
+ };
+ struct spi_message m;
+ int ret;
+ u8 *tx_buf = priv->spi_tx_buf;
+ u8 *rx_buf = priv->spi_rx_buf;
+
+ mutex_lock(&priv->spi_lock);
+
+ tx_buf[0] = INSTRUCTION_READ_RXB(buf_idx);
+
+ spi_message_init(&m);
+
+ if (mcp251x_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ if (ret < 0) {
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n",
+ __func__, ret);
+ priv->net->stats.rx_errors++;
+ mutex_unlock(&priv->spi_lock);
+ return;
+ }
+
+ if ((rx_buf[2] >> 3) & 0x1) {
+ /* Extended ID format */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |= ((rx_buf[2] & 3) << 16) |
+ (rx_buf[3] << 8) | rx_buf[4] |
+ (((rx_buf[1] << 3) | (rx_buf[2] >> 5)) << 18);
+ } else {
+ /* Standard ID format */
+ frame->can_id = (rx_buf[1] << 3) | (rx_buf[2] >> 5);
+ }
+
+ if ((rx_buf[5] >> 6) & 0x1) {
+ /* Remote transmission request */
+ frame->can_id |= CAN_RTR_FLAG;
+ }
+
+ /* Data length */
+ frame->can_dlc = rx_buf[5] & 0x0f;
+ if (frame->can_dlc > 8) {
+ dev_warn(&spi->dev, "invalid frame recevied\n");
+ priv->net->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ mutex_unlock(&priv->spi_lock);
+ return;
+ }
+
+ memcpy(frame->data, rx_buf + 6, CAN_FRAME_MAX_DATA_LEN);
+
+ mutex_unlock(&priv->spi_lock);
+ }
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->can_dlc;
+
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+}
+
+static void mcp251x_hw_sleep(struct spi_device *spi)
+{
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_SLEEP);
+}
+
+static void mcp251x_hw_wakeup(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ priv->wake = 1;
+
+ /* Can only wake up by generating a wake-up interrupt. */
+ mcp251x_write_bits(spi, CANINTE, CANINTE_WAKIE, CANINTE_WAKIE);
+ mcp251x_write_bits(spi, CANINTF, CANINTF_WAKIF, CANINTF_WAKIF);
+
+ /* Wait until the device is awake */
+ if (!wait_for_completion_timeout(&priv->awake, HZ))
+ dev_err(&spi->dev, "MCP251x didn't wake-up\n");
+}
+
+static int mcp251x_hard_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (priv->tx_skb) {
+ dev_warn(&spi->dev, "hard_xmit called with not null tx_skb\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (skb->len != sizeof(struct can_frame)) {
+ dev_dbg(&spi->dev, "dropping packet - bad length\n");
+ dev_kfree_skb(skb);
+ net->stats.tx_dropped++;
+ return 0;
+ }
+
+ netif_stop_queue(net);
+ priv->tx_skb = skb;
+ net->trans_start = jiffies;
+ queue_work(priv->wq, &priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ dev_dbg(&spi->dev, "%s (unimplemented)\n", __func__);
+
+ switch (mode) {
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static void mcp251x_set_normal_mode(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ unsigned long timeout;
+
+ /* Enable interrupts */
+ mcp251x_write_reg(spi, CANINTE,
+ CANINTE_ERRIE | CANINTE_TX2IE | CANINTE_TX1IE |
+ CANINTE_TX0IE | CANINTE_RX1IE | CANINTE_RX0IE);
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+ /* Put device into loopback mode */
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LOOPBACK);
+ } else {
+ /* Put device into normal mode */
+ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL);
+
+ /* Wait for the device to enter normal mode */
+ timeout = jiffies + HZ;
+ while (mcp251x_read_reg(spi, CANSTAT) & 0xE0) {
+ udelay(10);
+ if (time_after(jiffies, timeout)) {
+ dev_err(&spi->dev, "MCP251x didn't"
+ " enter in normal mode\n");
+ break;
+ }
+ }
+ }
+}
+
+static int mcp251x_do_set_bittiming(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct spi_device *spi = priv->spi;
+ u8 state;
+
+ dev_dbg(&spi->dev, "%s: BRP = %d, PropSeg = %d, PS1 = %d,"
+ " PS2 = %d, SJW = %d\n", __func__, bt->brp,
+ bt->prop_seg, bt->phase_seg1, bt->phase_seg2,
+ bt->sjw);
+
+ /* Store original mode and set mode to config */
+ state = mcp251x_read_reg(spi, CANCTRL);
+ state = mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK;
+ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK,
+ CANCTRL_REQOP_CONF);
+
+ mcp251x_write_reg(spi, CNF1, ((bt->sjw - 1) << 6) | (bt->brp - 1));
+ mcp251x_write_reg(spi, CNF2, CNF2_BTLMODE |
+ ((bt->phase_seg1 - 1) << 3) |
+ (bt->prop_seg - 1));
+ mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK,
+ (bt->phase_seg2 - 1));
+
+ /* Restore original state */
+ mcp251x_write_bits(spi, CANCTRL, CANCTRL_REQOP_MASK, state);
+
+ return 0;
+}
+
+static void mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
+ struct spi_device *spi)
+{
+ int ret;
+
+ /* Set initial baudrate. Make sure that registers are updated
+ always by explicitly calling mcp251x_do_set_bittiming */
+ ret = can_set_bittiming(net);
+ if (ret)
+ dev_err(&spi->dev, "unable to set initial baudrate!\n");
+ else
+ mcp251x_do_set_bittiming(net);
+
+ /* Enable RX0->RX1 buffer roll over and disable filters */
+ mcp251x_write_bits(spi, RXBCTRL(0),
+ RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1,
+ RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1);
+ mcp251x_write_bits(spi, RXBCTRL(1),
+ RXBCTRL_RXM0 | RXBCTRL_RXM1,
+ RXBCTRL_RXM0 | RXBCTRL_RXM1);
+
+ dev_dbg(&spi->dev, "%s RXBCTL 0 and 1: %02x %02x\n", __func__,
+ mcp251x_read_reg(spi, RXBCTRL(0)),
+ mcp251x_read_reg(spi, RXBCTRL(1)));
+}
+
+static void mcp251x_hw_reset(struct spi_device *spi)
+{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ int ret;
+
+ mutex_lock(&priv->spi_lock);
+
+ priv->spi_tx_buf[0] = INSTRUCTION_RESET;
+
+ ret = spi_write(spi, priv->spi_tx_buf, 1);
+
+ mutex_unlock(&priv->spi_lock);
+
+ if (ret < 0)
+ dev_dbg(&spi->dev, "%s: failed: ret = %d\n", __func__, ret);
+ /* wait for reset to finish */
+ mdelay(10);
+}
+
+static int mcp251x_hw_probe(struct spi_device *spi)
+{
+ int st1, st2;
+
+ mcp251x_hw_reset(spi);
+
+ st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE;
+ st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17;
+
+ dev_dbg(&spi->dev, "%s: 0x%02x - 0x%02x\n", __func__,
+ st1, st2);
+
+ /* check for power up default values */
+ return (st1 == 0x80 && st2 == 0x07) ? 1 : 0;
+}
+
+static int mcp251x_open(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(1);
+
+ priv->force_quit = 0;
+ priv->tx_skb = NULL;
+ enable_irq(spi->irq);
+ mcp251x_hw_wakeup(spi);
+ mcp251x_hw_reset(spi);
+ mcp251x_setup(net, priv, spi);
+ mcp251x_set_normal_mode(spi);
+ netif_wake_queue(net);
+
+ return 0;
+}
+
+static int mcp251x_stop(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ /* Disable and clear pending interrupts */
+ mcp251x_write_reg(spi, CANINTE, 0x00);
+ mcp251x_write_reg(spi, CANINTF, 0x00);
+
+ priv->force_quit = 1;
+ disable_irq(spi->irq);
+ flush_workqueue(priv->wq);
+
+ mcp251x_write_reg(spi, TXBCTRL(0), 0);
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+
+ mcp251x_hw_sleep(spi);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+
+ return 0;
+}
+
+static int mcp251x_do_get_state(struct net_device *net, enum can_state *state)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ u8 eflag;
+
+ eflag = mcp251x_read_reg(spi, EFLG);
+
+ if (eflag & EFLG_TXBO)
+ *state = CAN_STATE_BUS_OFF;
+ else if (eflag & (EFLG_RXEP | EFLG_TXEP))
+ *state = CAN_STATE_BUS_PASSIVE;
+ else if (eflag & EFLG_EWARN)
+ *state = CAN_STATE_BUS_WARNING;
+ else
+ *state = CAN_STATE_ACTIVE;
+
+ return 0;
+}
+
+static void mcp251x_tx_work_handler(struct work_struct *ws)
+{
+ struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
+ tx_work);
+ struct spi_device *spi = priv->spi;
+ struct can_frame *frame;
+
+ dev_dbg(&spi->dev, "%s\n", __func__);
+
+ if (priv->tx_skb) {
+ frame = (struct can_frame *)priv->tx_skb->data;
+ if (frame->can_dlc > CAN_FRAME_MAX_DATA_LEN)
+ frame->can_dlc = CAN_FRAME_MAX_DATA_LEN;
+ mcp251x_hw_tx(spi, frame, 0);
+ }
+}
+
+static void mcp251x_irq_work_handler(struct work_struct *ws)
+{
+ struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv,
+ irq_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+ u8 intf;
+ u8 txbnctrl;
+
+ if (priv->after_suspend) {
+ /* Wait whilst the device wakes up */
+ mdelay(10);
+ mcp251x_hw_reset(spi);
+ mcp251x_setup(net, priv, spi);
+ if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ netif_device_attach(net);
+ /* clear since we lost tx buffer */
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ netif_wake_queue(net);
+ }
+ mcp251x_set_normal_mode(spi);
+ } else
+ mcp251x_hw_sleep(spi);
+ priv->after_suspend = 0;
+ return;
+ }
+
+ while (!priv->force_quit && !freezing(current)) {
+ if (priv->restart_tx) {
+ priv->restart_tx = 0;
+ dev_warn(&spi->dev,
+ "timeout in txing a packet, restarting\n");
+ mcp251x_write_reg(spi, TXBCTRL(0), 0);
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+
+ if (priv->wake) {
+ /* Wait whilst the device wakes up */
+ mdelay(10);
+ priv->wake = 0;
+ }
+
+ intf = mcp251x_read_reg(spi, CANINTF);
+ if (intf == 0x00)
+ break;
+ mcp251x_write_bits(spi, CANINTF, intf, 0x00);
+
+ dev_dbg(&spi->dev, "interrupt:%s%s%s%s%s%s%s%s\n",
+ (intf & CANINTF_MERRF) ? " MERR" : "",
+ (intf & CANINTF_WAKIF) ? " WAK" : "",
+ (intf & CANINTF_ERRIF) ? " ERR" : "",
+ (intf & CANINTF_TX2IF) ? " TX2" : "",
+ (intf & CANINTF_TX1IF) ? " TX1" : "",
+ (intf & CANINTF_TX0IF) ? " TX0" : "",
+ (intf & CANINTF_RX1IF) ? " RX1" : "",
+ (intf & CANINTF_RX0IF) ? " RX0" : "");
+
+ if (intf & CANINTF_WAKIF)
+ complete(&priv->awake);
+
+ if (intf & CANINTF_MERRF) {
+ /* if there are no pending Tx buffers, restart queue */
+ txbnctrl = mcp251x_read_reg(spi, TXBCTRL(0));
+ if (!(txbnctrl & TXBCTRL_TXREQ)) {
+ if (priv->tx_skb) {
+ net->stats.tx_errors++;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+ }
+
+ if (intf & CANINTF_ERRIF) {
+ struct sk_buff *skb;
+ struct can_frame *frame = NULL;
+ u8 eflag = mcp251x_read_reg(spi, EFLG);
+
+ dev_dbg(&spi->dev, "EFLG = 0x%02x\n", eflag);
+
+ /* Create error frame */
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb) {
+ frame = (struct can_frame *)
+ skb_put(skb, sizeof(struct can_frame));
+ *(unsigned long long *)&frame->data = 0ULL;
+ frame->can_id = CAN_ERR_FLAG;
+ frame->can_dlc = CAN_ERR_DLC;
+
+ skb->dev = net;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ /* Set error frame flags based on bus state */
+ if (eflag & EFLG_TXBO) {
+ frame->can_id |= CAN_ERR_BUSOFF;
+ } else if (eflag & EFLG_TXEP) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_PASSIVE;
+ } else if (eflag & EFLG_RXEP) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
+ } else if (eflag & EFLG_TXWAR) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
+ } else if (eflag & EFLG_RXWAR) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
+ }
+ }
+
+ if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) {
+ if (eflag & EFLG_RX0OVR)
+ net->stats.rx_over_errors++;
+ if (eflag & EFLG_RX1OVR)
+ net->stats.rx_over_errors++;
+ if (frame) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] =
+ CAN_ERR_CRTL_RX_OVERFLOW;
+ }
+ }
+ mcp251x_write_reg(spi, EFLG, 0x00);
+
+ if (skb)
+ netif_rx(skb);
+ }
+
+ if (intf & (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF)) {
+ if (priv->tx_skb) {
+ net->stats.tx_packets++;
+ net->stats.tx_bytes +=
+ ((struct can_frame *)
+ (priv->tx_skb->data))->can_dlc;
+ dev_kfree_skb(priv->tx_skb);
+ priv->tx_skb = NULL;
+ }
+ netif_wake_queue(net);
+ }
+
+ if (intf & CANINTF_RX0IF)
+ mcp251x_hw_rx(spi, 0);
+
+ if (intf & CANINTF_RX1IF)
+ mcp251x_hw_rx(spi, 1);
+
+ }
+
+ mcp251x_read_reg(spi, CANSTAT);
+
+ dev_dbg(&spi->dev, "interrupt ended\n");
+}
+
+static irqreturn_t mcp251x_can_isr(int irq, void *dev_id)
+{
+ struct net_device *net = (struct net_device *)dev_id;
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ dev_dbg(&priv->spi->dev, "%s: irq\n", __func__);
+ /* Schedule bottom half */
+ if (!work_pending(&priv->irq_work))
+ queue_work(priv->wq, &priv->irq_work);
+
+ return IRQ_HANDLED;
+}
+
+static void mcp251x_tx_timeout(struct net_device *net)
+{
+ struct mcp251x_priv *priv = netdev_priv(net);
+
+ priv->restart_tx = 1;
+ queue_work(priv->wq, &priv->irq_work);
+}
+
+static struct net_device *alloc_mcp251x_netdev(int sizeof_priv)
+{
+ struct net_device *net;
+ struct mcp251x_priv *priv;
+
+ net = alloc_candev(sizeof_priv);
+ if (!net)
+ return NULL;
+
+ priv = netdev_priv(net);
+
+ net->open = mcp251x_open;
+ net->stop = mcp251x_stop;
+ net->hard_start_xmit = mcp251x_hard_start_xmit;
+ net->tx_timeout = mcp251x_tx_timeout;
+ net->watchdog_timeo = HZ;
+
+ priv->can.bittiming_const = &mcp251x_bittiming_const;
+ priv->can.do_get_state = mcp251x_do_get_state;
+ priv->can.do_set_mode = mcp251x_do_set_mode;
+
+ priv->net = net;
+
+ return net;
+}
+
+static int __devinit mcp251x_can_probe(struct spi_device *spi)
+{
+ struct net_device *net;
+ struct mcp251x_priv *priv;
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ int ret = -ENODEV;
+
+ if (!pdata) {
+ /* Platform data is required for osc freq */
+ goto error_out;
+ }
+
+ /* Allocate can/net device */
+ net = alloc_mcp251x_netdev(sizeof(struct mcp251x_priv));
+ if (!net) {
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+
+ priv = netdev_priv(net);
+ dev_set_drvdata(&spi->dev, priv);
+
+ priv->spi = spi;
+ mutex_init(&priv->spi_lock);
+
+ priv->can.bittiming.clock = pdata->oscillator_frequency / 2;
+
+ /* If requested, allocate DMA buffers */
+ if (mcp251x_enable_dma) {
+ spi->dev.coherent_dma_mask = DMA_32BIT_MASK;
+
+ /* Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+ that much and share it between Tx and Rx DMA buffers. */
+ priv->spi_tx_buf = dma_alloc_coherent(&spi->dev,
+ PAGE_SIZE, &priv->spi_tx_dma, GFP_DMA);
+
+ if (priv->spi_tx_buf) {
+ priv->spi_rx_buf = (u8 *)(priv->spi_tx_buf +
+ (PAGE_SIZE / 2));
+ priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma +
+ (PAGE_SIZE / 2));
+ } else {
+ /* Fall back to non-DMA */
+ mcp251x_enable_dma = 0;
+ }
+ }
+
+ /* Allocate non-DMA buffers */
+ if (!mcp251x_enable_dma) {
+ priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_tx_buf;
+ }
+ priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_rx_buf;
+ }
+ }
+
+ if (pdata->power_enable)
+ pdata->power_enable(1);
+
+ /* Call out to platform specific setup */
+ if (pdata->board_specific_setup)
+ pdata->board_specific_setup(spi);
+
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ priv->wq = create_freezeable_workqueue("mcp251x_wq");
+
+ INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler);
+ INIT_WORK(&priv->irq_work, mcp251x_irq_work_handler);
+
+ init_completion(&priv->awake);
+
+ /* Configure the SPI bus */
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ /* Register IRQ */
+ if (request_irq(spi->irq, mcp251x_can_isr,
+ IRQF_TRIGGER_FALLING, DEVICE_NAME, net) < 0) {
+ dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
+ goto error_irq;
+ }
+ disable_irq(spi->irq);
+
+ if (!mcp251x_hw_probe(spi)) {
+ dev_info(&spi->dev, "Probe failed\n");
+ goto error_probe;
+ }
+ mcp251x_hw_sleep(spi);
+
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+
+ ret = register_candev(net);
+ if (ret >= 0) {
+ dev_info(&spi->dev, "probed\n");
+ return ret;
+ }
+error_probe:
+ free_irq(spi->irq, net);
+error_irq:
+ if (!mcp251x_enable_dma)
+ kfree(priv->spi_rx_buf);
+error_rx_buf:
+ if (!mcp251x_enable_dma)
+ kfree(priv->spi_tx_buf);
+error_tx_buf:
+ free_candev(net);
+ if (mcp251x_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ }
+error_alloc:
+ dev_err(&spi->dev, "probe failed\n");
+error_out:
+ return ret;
+}
+
+static int __devexit mcp251x_can_remove(struct spi_device *spi)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net = priv->net;
+
+ unregister_candev(net);
+ free_candev(net);
+
+ free_irq(spi->irq, net);
+ priv->force_quit = 1;
+ flush_workqueue(priv->wq);
+ destroy_workqueue(priv->wq);
+
+ if (mcp251x_enable_dma) {
+ dma_free_coherent(&spi->dev, PAGE_SIZE,
+ priv->spi_tx_buf, priv->spi_tx_dma);
+ } else {
+ kfree(priv->spi_tx_buf);
+ kfree(priv->spi_rx_buf);
+ }
+
+ if (pdata->power_enable)
+ pdata->power_enable(0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mcp251x_can_suspend(struct spi_device *spi, pm_message_t state)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net = priv->net;
+
+ if (netif_running(net)) {
+ netif_device_detach(net);
+
+ mcp251x_hw_sleep(spi);
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(0);
+ priv->after_suspend = AFTER_SUSPEND_UP;
+ } else
+ priv->after_suspend = AFTER_SUSPEND_DOWN;
+
+ if (pdata->power_enable) {
+ pdata->power_enable(0);
+ priv->after_suspend |= AFTER_SUSPEND_POWER;
+ }
+
+ return 0;
+}
+
+static int mcp251x_can_resume(struct spi_device *spi)
+{
+ struct mcp251x_platform_data *pdata = spi->dev.platform_data;
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
+
+ if (priv->after_suspend & AFTER_SUSPEND_POWER) {
+ pdata->power_enable(1);
+ queue_work(priv->wq, &priv->irq_work);
+ } else {
+ if (priv->after_suspend & AFTER_SUSPEND_UP) {
+ if (pdata->transceiver_enable)
+ pdata->transceiver_enable(1);
+ queue_work(priv->wq, &priv->irq_work);
+ } else
+ priv->after_suspend = 0;
+ }
+ return 0;
+}
+#else
+#define mcp251x_can_suspend NULL
+#define mcp251x_can_resume NULL
+#endif
+
+static struct spi_driver mcp251x_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+
+ .probe = mcp251x_can_probe,
+ .remove = __devexit_p(mcp251x_can_remove),
+ .suspend = mcp251x_can_suspend,
+ .resume = mcp251x_can_resume,
+};
+
+static int __init mcp251x_can_init(void)
+{
+ return spi_register_driver(&mcp251x_can_driver);
+}
+
+static void __exit mcp251x_can_exit(void)
+{
+ spi_unregister_driver(&mcp251x_can_driver);
+}
+
+module_init(mcp251x_can_init);
+module_exit(mcp251x_can_exit);
+
+MODULE_AUTHOR("Chris Elston <celston@katalix.com>, "
+ "Christian Pellegrin <chripell@evolware.org>");
+MODULE_DESCRIPTION("Microchip 251x CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/mscan/Makefile b/drivers/net/can/mscan/Makefile
new file mode 100644
index 000000000000..9f61b13db90e
--- /dev/null
+++ b/drivers/net/can/mscan/Makefile
@@ -0,0 +1,23 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+obj-$(CONFIG_CAN_MPC52XX) += mscan-mpc52xx.o
+
+mscan-mpc52xx-objs := mscan.o mpc52xx_can.o
+
+ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+
+endif
diff --git a/drivers/net/can/mscan/mpc52xx_can.c b/drivers/net/can/mscan/mpc52xx_can.c
new file mode 100644
index 000000000000..8878a5901aa5
--- /dev/null
+++ b/drivers/net/can/mscan/mpc52xx_can.c
@@ -0,0 +1,293 @@
+/*
+ * CAN bus driver for the Freescale MPC52xx embedded CPU.
+ *
+ * Copyright (C) 2004-2005 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/of_platform.h>
+#include <sysdev/fsl_soc.h>
+#include <linux/io.h>
+#include <asm/mpc52xx.h>
+
+#include "mscan.h"
+
+
+#define DRV_NAME "mpc52xx_can"
+
+static struct of_device_id mpc52xx_cdm_ids[] __devinitdata = {
+ { .compatible = "fsl,mpc5200-cdm", },
+ { .compatible = "fsl,mpc5200b-cdm", },
+ {}
+};
+
+/*
+ * Get the frequency of the external oscillator clock connected
+ * to the SYS_XTAL_IN pin, or retrun 0 if it cannot be determined.
+ */
+static unsigned int __devinit mpc52xx_can_xtal_freq(struct device_node *np)
+{
+ struct mpc52xx_cdm __iomem *cdm;
+ struct device_node *np_cdm;
+ unsigned int freq;
+ u32 val;
+
+ freq = mpc52xx_find_ipb_freq(np);
+ if (!freq)
+ return 0;
+
+ /*
+ * Detemine SYS_XTAL_IN frequency from the clock domain settings
+ */
+ np_cdm = of_find_matching_node(NULL, mpc52xx_cdm_ids);
+ cdm = of_iomap(np_cdm, 0);
+ of_node_put(np_cdm);
+ if (!np_cdm) {
+ printk(KERN_ERR "%s() failed abnormally\n", __func__);
+ return 0;
+ }
+
+ if (in_8(&cdm->ipb_clk_sel) & 0x1)
+ freq *= 2;
+ val = in_be32(&cdm->rstcfg);
+ if (val & (1 << 5))
+ freq *= 8;
+ else
+ freq *= 4;
+ if (val & (1 << 6))
+ freq /= 12;
+ else
+ freq /= 16;
+
+ iounmap(cdm);
+
+ return freq;
+}
+
+/*
+ * Get frequency of the MSCAN clock source
+ *
+ * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock (IP_CLK)
+ * can be selected. According to the MPC5200 user's manual, the oscillator
+ * clock is the better choice as it has less jitter but due to a hardware
+ * bug, it can not be selected for the old MPC5200 Rev. A chips.
+ */
+
+static unsigned int __devinit mpc52xx_can_clock_freq(struct device_node *np,
+ int clock_src)
+{
+ unsigned int pvr;
+
+ pvr = mfspr(SPRN_PVR);
+
+ if (clock_src == MSCAN_CLKSRC_BUS || pvr == 0x80822011)
+ return mpc52xx_find_ipb_freq(np);
+
+ return mpc52xx_can_xtal_freq(np);
+}
+
+static int __devinit mpc52xx_can_probe(struct of_device *ofdev,
+ const struct of_device_id *id)
+{
+ struct device_node *np = ofdev->node;
+ struct net_device *dev;
+ struct can_priv *priv;
+ struct resource res;
+ void __iomem *base;
+ int err, irq, res_size, clock_src;
+
+ err = of_address_to_resource(np, 0, &res);
+ if (err) {
+ dev_err(&ofdev->dev, "invalid address\n");
+ return err;
+ }
+
+ res_size = res.end - res.start + 1;
+
+ if (!request_mem_region(res.start, res_size, DRV_NAME)) {
+ dev_err(&ofdev->dev, "couldn't request %#x..%#x\n",
+ res.start, res.end);
+ return -EBUSY;
+ }
+
+ base = ioremap_nocache(res.start, res_size);
+ if (!base) {
+ dev_err(&ofdev->dev, "couldn't ioremap %#x..%#x\n",
+ res.start, res.end);
+ err = -ENOMEM;
+ goto exit_release_mem;
+ }
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq == NO_IRQ) {
+ dev_err(&ofdev->dev, "no irq found\n");
+ err = -ENODEV;
+ goto exit_unmap_mem;
+ }
+
+ dev = alloc_mscandev();
+ if (!dev) {
+ err = -ENOMEM;
+ goto exit_dispose_irq;
+ }
+
+ dev->base_addr = (unsigned long)base;
+ dev->irq = irq;
+
+ priv = netdev_priv(dev);
+
+ /*
+ * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock
+ * (IP_CLK) can be selected as MSCAN clock source. According to
+ * the MPC5200 user's manual, the oscillator clock is the better
+ * choice as it has less jitter. For this reason, it is selected
+ * by default.
+ */
+ if (of_get_property(np, "clock-ipb", NULL))
+ clock_src = MSCAN_CLKSRC_BUS;
+ else
+ clock_src = MSCAN_CLKSRC_XTAL;
+ priv->bittiming.clock = mpc52xx_can_clock_freq(np, clock_src);
+ if (!priv->bittiming.clock) {
+ dev_err(&ofdev->dev, "couldn't get MSCAN clock frequency\n");
+ err = -ENODEV;
+ goto exit_free_mscan;
+ }
+
+ SET_NETDEV_DEV(dev, &ofdev->dev);
+
+ err = register_mscandev(dev, clock_src);
+ if (err) {
+ dev_err(&ofdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, err);
+ goto exit_free_mscan;
+ }
+
+ dev_set_drvdata(&ofdev->dev, dev);
+
+ dev_info(&ofdev->dev, "MSCAN at 0x%lx, irq %d, clock %d Hz\n",
+ dev->base_addr, dev->irq, priv->bittiming.clock);
+
+ return 0;
+
+exit_free_mscan:
+ free_candev(dev);
+exit_dispose_irq:
+ irq_dispose_mapping(irq);
+exit_unmap_mem:
+ iounmap(base);
+exit_release_mem:
+ release_mem_region(res.start, res_size);
+
+ return err;
+}
+
+static int __devexit mpc52xx_can_remove(struct of_device *ofdev)
+{
+ struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct device_node *np = ofdev->node;
+ struct resource res;
+
+ dev_set_drvdata(&ofdev->dev, NULL);
+
+ unregister_mscandev(dev);
+ iounmap((void __iomem *)dev->base_addr);
+ irq_dispose_mapping(dev->irq);
+ free_candev(dev);
+
+ of_address_to_resource(np, 0, &res);
+ release_mem_region(res.start, res.end - res.start + 1);
+
+ return 0;
+}
+
+static struct mscan_regs saved_regs;
+static int mpc52xx_can_suspend(struct of_device *ofdev, pm_message_t state)
+{
+ struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
+
+ return 0;
+}
+
+static int mpc52xx_can_resume(struct of_device *ofdev)
+{
+ struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ regs->canctl0 |= MSCAN_INITRQ;
+ while ((regs->canctl1 & MSCAN_INITAK) == 0)
+ udelay(10);
+
+ regs->canctl1 = saved_regs.canctl1;
+ regs->canbtr0 = saved_regs.canbtr0;
+ regs->canbtr1 = saved_regs.canbtr1;
+ regs->canidac = saved_regs.canidac;
+
+ /* restore masks, buffers etc. */
+ _memcpy_toio(&regs->canidar1_0, (void *)&saved_regs.canidar1_0,
+ sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
+
+ regs->canctl0 &= ~MSCAN_INITRQ;
+ regs->cantbsel = saved_regs.cantbsel;
+ regs->canrier = saved_regs.canrier;
+ regs->cantier = saved_regs.cantier;
+ regs->canctl0 = saved_regs.canctl0;
+
+ return 0;
+}
+
+static struct of_device_id __devinitdata mpc52xx_can_table[] = {
+ {.compatible = "fsl,mpc5200-mscan"},
+ {.compatible = "fsl,mpc5200b-mscan"},
+ {},
+};
+
+static struct of_platform_driver mpc52xx_can_driver = {
+ .owner = THIS_MODULE,
+ .name = "mpc52xx_can",
+ .probe = mpc52xx_can_probe,
+ .remove = __devexit_p(mpc52xx_can_remove),
+ .suspend = mpc52xx_can_suspend,
+ .resume = mpc52xx_can_resume,
+ .match_table = mpc52xx_can_table,
+};
+
+static int __init mpc52xx_can_init(void)
+{
+ return of_register_platform_driver(&mpc52xx_can_driver);
+}
+module_init(mpc52xx_can_init);
+
+static void __exit mpc52xx_can_exit(void)
+{
+ return of_unregister_platform_driver(&mpc52xx_can_driver);
+};
+module_exit(mpc52xx_can_exit);
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c
new file mode 100644
index 000000000000..fe42b9e7dfb7
--- /dev/null
+++ b/drivers/net/can/mscan/mscan.c
@@ -0,0 +1,679 @@
+/*
+ * CAN bus driver for the alone generic (as possible as) MSCAN controller.
+ *
+ * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/list.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/io.h>
+
+#include "mscan.h"
+
+#define MSCAN_NORMAL_MODE 0
+#define MSCAN_SLEEP_MODE MSCAN_SLPRQ
+#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ)
+#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ)
+#define MSCAN_SET_MODE_RETRIES 255
+
+#define BTR0_BRP_MASK 0x3f
+#define BTR0_SJW_SHIFT 6
+#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT)
+
+#define BTR1_TSEG1_MASK 0xf
+#define BTR1_TSEG2_SHIFT 4
+#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT)
+#define BTR1_SAM_SHIFT 7
+
+#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK)
+#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \
+ BTR0_SJW_MASK)
+
+#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK)
+#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \
+ BTR1_TSEG2_MASK)
+#define BTR1_SET_SAM(sam) (((sam) & 1) << BTR1_SAM_SHIFT)
+
+static struct can_bittiming_const mscan_bittiming_const = {
+ .tseg1_min = 4,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+struct mscan_state {
+ u8 mode;
+ u8 canrier;
+ u8 cantier;
+};
+
+#define TX_QUEUE_SIZE 3
+
+struct tx_queue_entry {
+ struct list_head list;
+ u8 mask;
+ u8 id;
+};
+
+struct mscan_priv {
+ struct can_priv can;
+ long open_time;
+ unsigned long flags;
+ u8 shadow_statflg;
+ u8 shadow_canrier;
+ u8 cur_pri;
+ u8 tx_active;
+
+ struct list_head tx_head;
+ struct tx_queue_entry tx_queue[TX_QUEUE_SIZE];
+ struct napi_struct napi;
+ struct net_device *dev;
+};
+
+#define F_RX_PROGRESS 0
+#define F_TX_PROGRESS 1
+#define F_TX_WAIT_ALL 2
+
+static enum can_state state_map[] = {
+ CAN_STATE_ACTIVE,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF
+};
+
+static int mscan_set_mode(struct net_device *dev, u8 mode)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ int ret = 0;
+ int i;
+ u8 canctl1;
+
+ if (mode != MSCAN_NORMAL_MODE) {
+
+ if (priv->tx_active) {
+ /* Abort transfers before going to sleep */
+ out_8(&regs->cantier, 0);
+ out_8(&regs->cantarq, priv->tx_active);
+ out_8(&regs->cantier, priv->tx_active);
+ }
+
+ canctl1 = in_8(&regs->canctl1);
+ if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) {
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_SLPRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(&regs->canctl1) & MSCAN_SLPAK)
+ break;
+ udelay(100);
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+ if (!ret)
+ priv->can.state = CAN_STATE_SLEEPING;
+
+ if (!ret && (mode & MSCAN_INITRQ)
+ && (canctl1 & MSCAN_INITAK) == 0) {
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_INITRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(&regs->canctl1) & MSCAN_INITAK)
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+ if (!ret)
+ priv->can.state = CAN_STATE_STOPPED;
+
+ if (!ret && (mode & MSCAN_CSWAI))
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_CSWAI);
+
+ } else {
+ canctl1 = in_8(&regs->canctl1);
+ if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
+ out_8(&regs->canctl0, in_8(&regs->canctl0) &
+ ~(MSCAN_SLPRQ | MSCAN_INITRQ));
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ canctl1 = in_8(&regs->canctl1);
+ if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)))
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ else
+ priv->can.state = CAN_STATE_ACTIVE;
+ }
+ }
+ return ret;
+}
+
+static int mscan_start(struct net_device *dev)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 canrflg;
+ int err;
+
+ out_8(&regs->canrier, 0);
+
+ INIT_LIST_HEAD(&priv->tx_head);
+ priv->cur_pri = 0;
+ priv->tx_active = 0;
+ priv->shadow_canrier = 0;
+ priv->flags = 0;
+
+ err = mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+ if (err)
+ return err;
+
+ canrflg = in_8(&regs->canrflg);
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ priv->can.state = state_map[max(MSCAN_STATE_RX(canrflg),
+ MSCAN_STATE_TX(canrflg))];
+ out_8(&regs->cantier, 0);
+
+ /* Enable receive interrupts. */
+ out_8(&regs->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE |
+ MSCAN_RSTATE1 | MSCAN_RSTATE0 | MSCAN_TSTATE1 | MSCAN_TSTATE0);
+
+ return 0;
+}
+
+static int mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_frame *frame = (struct can_frame *)skb->data;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ int i, rtr, buf_id;
+ u32 can_id;
+
+ if (frame->can_dlc > 8)
+ return -EINVAL;
+
+ out_8(&regs->cantier, 0);
+
+ i = ~priv->tx_active & MSCAN_TXE;
+ buf_id = ffs(i) - 1;
+ switch (hweight8(i)) {
+ case 0:
+ netif_stop_queue(dev);
+ dev_err(ND2D(dev), "BUG! Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ case 1:
+ /* if buf_id < 3, then current frame will be send out of order,
+ since buffer with lower id have higher priority (hell..) */
+ if (buf_id < 3)
+ priv->cur_pri++;
+ if (priv->cur_pri == 0xff)
+ set_bit(F_TX_WAIT_ALL, &priv->flags);
+ netif_stop_queue(dev);
+ case 2:
+ set_bit(F_TX_PROGRESS, &priv->flags);
+ }
+ out_8(&regs->cantbsel, i);
+
+ rtr = frame->can_id & CAN_RTR_FLAG;
+
+ if (frame->can_id & CAN_EFF_FLAG) {
+ can_id = (frame->can_id & CAN_EFF_MASK) << 1;
+ if (rtr)
+ can_id |= 1;
+ out_be16(&regs->tx.idr3_2, can_id);
+
+ can_id >>= 16;
+ can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) | (3 << 3);
+ } else {
+ can_id = (frame->can_id & CAN_SFF_MASK) << 5;
+ if (rtr)
+ can_id |= 1 << 4;
+ }
+ out_be16(&regs->tx.idr1_0, can_id);
+
+ if (!rtr) {
+ void __iomem *data = &regs->tx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ /*Its safe to write into dsr[dlc+1] */
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ out_be16(data, *payload++);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(&regs->tx.dlr, frame->can_dlc);
+ out_8(&regs->tx.tbpr, priv->cur_pri);
+
+ /* Start transmission. */
+ out_8(&regs->cantflg, 1 << buf_id);
+
+ if (!test_bit(F_TX_PROGRESS, &priv->flags))
+ dev->trans_start = jiffies;
+
+ list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head);
+
+ can_put_echo_skb(skb, dev, buf_id);
+
+ /* Enable interrupt. */
+ priv->tx_active |= 1 << buf_id;
+ out_8(&regs->cantier, priv->tx_active);
+
+ return NETDEV_TX_OK;
+}
+
+static inline int check_set_state(struct net_device *dev, u8 canrflg)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ enum can_state state;
+ int ret = 0;
+
+ if (!(canrflg & MSCAN_CSCIF) || priv->can.state > CAN_STATE_BUS_OFF)
+ return 0;
+
+ state = state_map[max(MSCAN_STATE_RX(canrflg),
+ MSCAN_STATE_TX(canrflg))];
+ if (priv->can.state < state)
+ ret = 1;
+ if (state == CAN_STATE_BUS_OFF)
+ can_bus_off(dev);
+ priv->can.state = state;
+ return ret;
+}
+
+static int mscan_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
+ struct net_device *dev = priv->dev;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct net_device_stats *stats = &dev->stats;
+ int npackets = 0;
+ int ret = 1;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u32 can_id;
+ u8 canrflg;
+ int i;
+
+ while (npackets < quota && ((canrflg = in_8(&regs->canrflg)) &
+ (MSCAN_RXF | MSCAN_ERR_IF))) {
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(ND2D(dev), "packet dropped\n");
+ stats->rx_dropped++;
+ out_8(&regs->canrflg, canrflg);
+ continue;
+ }
+
+ frame = (struct can_frame *)skb_put(skb, sizeof(*frame));
+ memset(frame, 0, sizeof(*frame));
+
+ if (canrflg & MSCAN_RXF) {
+ can_id = in_be16(&regs->rx.idr1_0);
+ if (can_id & (1 << 3)) {
+ frame->can_id = CAN_EFF_FLAG;
+ can_id = ((can_id << 16) |
+ in_be16(&regs->rx.idr3_2));
+ can_id = ((can_id & 0xffe00000) |
+ ((can_id & 0x7ffff) << 2)) >> 2;
+ } else {
+ can_id >>= 4;
+ frame->can_id = 0;
+ }
+
+ frame->can_id |= can_id >> 1;
+ if (can_id & 1)
+ frame->can_id |= CAN_RTR_FLAG;
+ frame->can_dlc = in_8(&regs->rx.dlr) & 0xf;
+
+ if (!(frame->can_id & CAN_RTR_FLAG)) {
+ void __iomem *data = &regs->rx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ *payload++ = in_be16(data);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(&regs->canrflg, MSCAN_RXF);
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+ } else if (canrflg & MSCAN_ERR_IF) {
+ dev_dbg(ND2D(dev), "error interrupt (canrflg=%#x)\n",
+ canrflg);
+ frame->can_id = CAN_ERR_FLAG;
+
+ if (canrflg & MSCAN_OVRIF) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ stats->rx_over_errors++;
+ } else
+ frame->data[1] = 0;
+
+ if (check_set_state(dev, canrflg)) {
+ frame->can_id |= CAN_ERR_CRTL;
+ switch (priv->can.state) {
+ case CAN_STATE_BUS_WARNING:
+ if ((priv->shadow_statflg &
+ MSCAN_RSTAT_MSK) <
+ (canrflg & MSCAN_RSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
+
+ if ((priv->shadow_statflg &
+ MSCAN_TSTAT_MSK) <
+ (canrflg & MSCAN_TSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
+ break;
+ case CAN_STATE_BUS_PASSIVE:
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
+ break;
+ case CAN_STATE_BUS_OFF:
+ frame->can_id |= CAN_ERR_BUSOFF;
+ frame->can_id &= ~CAN_ERR_CRTL;
+ break;
+ default:
+ break;
+ }
+ }
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ frame->can_dlc = CAN_ERR_DLC;
+ out_8(&regs->canrflg, MSCAN_ERR_IF);
+ }
+
+ npackets++;
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_receive_skb(skb);
+ }
+
+ if (!(in_8(&regs->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
+ netif_rx_complete(dev, &priv->napi);
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ if (priv->can.state < CAN_STATE_BUS_OFF)
+ out_8(&regs->canrier, priv->shadow_canrier);
+ ret = 0;
+ }
+ return ret;
+}
+
+static irqreturn_t mscan_isr(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct net_device_stats *stats = &dev->stats;
+ u8 cantier, cantflg, canrflg;
+ irqreturn_t ret = IRQ_NONE;
+
+ cantier = in_8(&regs->cantier) & MSCAN_TXE;
+ cantflg = in_8(&regs->cantflg) & cantier;
+
+ if (cantier && cantflg) {
+
+ struct list_head *tmp, *pos;
+
+ list_for_each_safe(pos, tmp, &priv->tx_head) {
+ struct tx_queue_entry *entry =
+ list_entry(pos, struct tx_queue_entry, list);
+ u8 mask = entry->mask;
+
+ if (!(cantflg & mask))
+ continue;
+
+ out_8(&regs->cantbsel, mask);
+ stats->tx_bytes += in_8(&regs->tx.dlr);
+ stats->tx_packets++;
+ can_get_echo_skb(dev, entry->id);
+ priv->tx_active &= ~mask;
+ list_del(pos);
+ }
+
+ if (list_empty(&priv->tx_head)) {
+ clear_bit(F_TX_WAIT_ALL, &priv->flags);
+ clear_bit(F_TX_PROGRESS, &priv->flags);
+ priv->cur_pri = 0;
+ } else
+ dev->trans_start = jiffies;
+
+ if (!test_bit(F_TX_WAIT_ALL, &priv->flags))
+ netif_wake_queue(dev);
+
+ out_8(&regs->cantier, priv->tx_active);
+ ret = IRQ_HANDLED;
+ }
+
+ canrflg = in_8(&regs->canrflg);
+ if ((canrflg & ~MSCAN_STAT_MSK) &&
+ !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
+ if (canrflg & ~MSCAN_STAT_MSK) {
+ priv->shadow_canrier = in_8(&regs->canrier);
+ out_8(&regs->canrier, 0);
+ netif_rx_schedule(dev, &priv->napi);
+ ret = IRQ_HANDLED;
+ } else
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ }
+ return ret;
+}
+
+static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode)
+{
+
+ struct mscan_priv *priv = netdev_priv(dev);
+ int ret = 0;
+
+ if (!priv->open_time)
+ return -EINVAL;
+
+ switch (mode) {
+ case CAN_MODE_SLEEP:
+ case CAN_MODE_STOP:
+ netif_stop_queue(dev);
+ mscan_set_mode(dev,
+ (mode ==
+ CAN_MODE_STOP) ? MSCAN_INIT_MODE :
+ MSCAN_SLEEP_MODE);
+ break;
+ case CAN_MODE_START:
+ if (priv->can.state <= CAN_STATE_BUS_OFF)
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ ret = mscan_start(dev);
+ if (ret)
+ break;
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+static int mscan_do_set_bittiming(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u8 btr0, btr1;
+
+ btr0 = BTR0_SET_BRP(bt->brp) | BTR0_SET_SJW(bt->sjw);
+ btr1 = (BTR1_SET_TSEG1(bt->prop_seg + bt->phase_seg1) |
+ BTR1_SET_TSEG2(bt->phase_seg2) |
+ BTR1_SET_SAM(priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES));
+
+ dev_info(ND2D(dev), "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
+
+ out_8(&regs->canbtr0, btr0);
+ out_8(&regs->canbtr1, btr1);
+
+ return 0;
+}
+
+static int mscan_open(struct net_device *dev)
+{
+ int ret;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ /* determine and set bittime */
+ ret = can_set_bittiming(dev);
+ if (ret)
+ return ret;
+
+ napi_enable(&priv->napi);
+ ret = request_irq(dev->irq, mscan_isr, 0, dev->name, dev);
+
+ if (ret < 0) {
+ napi_disable(&priv->napi);
+ printk(KERN_ERR "%s - failed to attach interrupt\n",
+ dev->name);
+ return ret;
+ }
+
+ priv->open_time = jiffies;
+
+ out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_LISTEN);
+
+ ret = mscan_start(dev);
+ if (ret)
+ return ret;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int mscan_close(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+
+ napi_disable(&priv->napi);
+
+ out_8(&regs->cantier, 0);
+ out_8(&regs->canrier, 0);
+ free_irq(dev->irq, dev);
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ can_close_cleanup(dev);
+ netif_stop_queue(dev);
+ priv->open_time = 0;
+
+ return 0;
+}
+
+int register_mscandev(struct net_device *dev, int clock_src)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 ctl1;
+
+ ctl1 = in_8(&regs->canctl1);
+ if (clock_src)
+ ctl1 |= MSCAN_CLKSRC;
+ else
+ ctl1 &= ~MSCAN_CLKSRC;
+
+ ctl1 |= MSCAN_CANE;
+ out_8(&regs->canctl1, ctl1);
+ udelay(100);
+
+ /* acceptance mask/acceptance code (accept everything) */
+ out_be16(&regs->canidar1_0, 0);
+ out_be16(&regs->canidar3_2, 0);
+ out_be16(&regs->canidar5_4, 0);
+ out_be16(&regs->canidar7_6, 0);
+
+ out_be16(&regs->canidmr1_0, 0xffff);
+ out_be16(&regs->canidmr3_2, 0xffff);
+ out_be16(&regs->canidmr5_4, 0xffff);
+ out_be16(&regs->canidmr7_6, 0xffff);
+ /* Two 32 bit Acceptance Filters */
+ out_8(&regs->canidac, MSCAN_AF_32BIT);
+
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+
+ return register_candev(dev);
+}
+EXPORT_SYMBOL_GPL(register_mscandev);
+
+void unregister_mscandev(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_CANE);
+ unregister_candev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_mscandev);
+
+struct net_device *alloc_mscandev(void)
+{
+ struct net_device *dev;
+ struct mscan_priv *priv;
+ int i;
+
+ dev = alloc_candev(sizeof(struct mscan_priv));
+ if (!dev)
+ return NULL;
+ priv = netdev_priv(dev);
+
+ dev->open = mscan_open;
+ dev->stop = mscan_close;
+ dev->hard_start_xmit = mscan_start_xmit;
+
+ dev->flags |= IFF_ECHO; /* we support local echo */
+
+ priv->dev = dev;
+ netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8);
+
+ priv->can.bittiming_const = &mscan_bittiming_const;
+ priv->can.do_set_bittiming = mscan_do_set_bittiming;
+ priv->can.do_set_mode = mscan_do_set_mode;
+
+ for (i = 0; i < TX_QUEUE_SIZE; i++) {
+ priv->tx_queue[i].id = i;
+ priv->tx_queue[i].mask = 1 << i;
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_mscandev);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for a MSCAN based chips");
diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h
new file mode 100644
index 000000000000..81a2eb5a19e5
--- /dev/null
+++ b/drivers/net/can/mscan/mscan.h
@@ -0,0 +1,237 @@
+/*
+ * Definitions of consts/structs to drive the Freescale MSCAN.
+ *
+ * Copyright (C) 2005-2006 Andrey Volkov <avolkov@varma-el.com>,
+ * Varma Electronics Oy
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MSCAN_H__
+#define __MSCAN_H__
+
+#include <linux/types.h>
+
+/* MSCAN control register 0 (CANCTL0) bits */
+#define MSCAN_RXFRM 0x80
+#define MSCAN_RXACT 0x40
+#define MSCAN_CSWAI 0x20
+#define MSCAN_SYNCH 0x10
+#define MSCAN_TIME 0x08
+#define MSCAN_WUPE 0x04
+#define MSCAN_SLPRQ 0x02
+#define MSCAN_INITRQ 0x01
+
+/* MSCAN control register 1 (CANCTL1) bits */
+#define MSCAN_CANE 0x80
+#define MSCAN_CLKSRC 0x40
+#define MSCAN_LOOPB 0x20
+#define MSCAN_LISTEN 0x10
+#define MSCAN_WUPM 0x04
+#define MSCAN_SLPAK 0x02
+#define MSCAN_INITAK 0x01
+
+/* Use the MPC5200 MSCAN variant? */
+#ifdef CONFIG_PPC
+#define MSCAN_FOR_MPC5200
+#endif
+
+#ifdef MSCAN_FOR_MPC5200
+#define MSCAN_CLKSRC_BUS 0
+#define MSCAN_CLKSRC_XTAL MSCAN_CLKSRC
+#else
+#define MSCAN_CLKSRC_BUS MSCAN_CLKSRC
+#define MSCAN_CLKSRC_XTAL 0
+#endif
+
+/* MSCAN receiver flag register (CANRFLG) bits */
+#define MSCAN_WUPIF 0x80
+#define MSCAN_CSCIF 0x40
+#define MSCAN_RSTAT1 0x20
+#define MSCAN_RSTAT0 0x10
+#define MSCAN_TSTAT1 0x08
+#define MSCAN_TSTAT0 0x04
+#define MSCAN_OVRIF 0x02
+#define MSCAN_RXF 0x01
+#define MSCAN_ERR_IF (MSCAN_OVRIF | MSCAN_CSCIF)
+#define MSCAN_RSTAT_MSK (MSCAN_RSTAT1 | MSCAN_RSTAT0)
+#define MSCAN_TSTAT_MSK (MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK)
+
+#define MSCAN_STATE_BUS_OFF (MSCAN_RSTAT1 | MSCAN_RSTAT0 | \
+ MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STATE_TX(canrflg) (((canrflg)&MSCAN_TSTAT_MSK)>>2)
+#define MSCAN_STATE_RX(canrflg) (((canrflg)&MSCAN_RSTAT_MSK)>>4)
+#define MSCAN_STATE_ACTIVE 0
+#define MSCAN_STATE_WARNING 1
+#define MSCAN_STATE_PASSIVE 2
+#define MSCAN_STATE_BUSOFF 3
+
+/* MSCAN receiver interrupt enable register (CANRIER) bits */
+#define MSCAN_WUPIE 0x80
+#define MSCAN_CSCIE 0x40
+#define MSCAN_RSTATE1 0x20
+#define MSCAN_RSTATE0 0x10
+#define MSCAN_TSTATE1 0x08
+#define MSCAN_TSTATE0 0x04
+#define MSCAN_OVRIE 0x02
+#define MSCAN_RXFIE 0x01
+
+/* MSCAN transmitter flag register (CANTFLG) bits */
+#define MSCAN_TXE2 0x04
+#define MSCAN_TXE1 0x02
+#define MSCAN_TXE0 0x01
+#define MSCAN_TXE (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0)
+
+/* MSCAN transmitter interrupt enable register (CANTIER) bits */
+#define MSCAN_TXIE2 0x04
+#define MSCAN_TXIE1 0x02
+#define MSCAN_TXIE0 0x01
+#define MSCAN_TXIE (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0)
+
+/* MSCAN transmitter message abort request (CANTARQ) bits */
+#define MSCAN_ABTRQ2 0x04
+#define MSCAN_ABTRQ1 0x02
+#define MSCAN_ABTRQ0 0x01
+
+/* MSCAN transmitter message abort ack (CANTAAK) bits */
+#define MSCAN_ABTAK2 0x04
+#define MSCAN_ABTAK1 0x02
+#define MSCAN_ABTAK0 0x01
+
+/* MSCAN transmit buffer selection (CANTBSEL) bits */
+#define MSCAN_TX2 0x04
+#define MSCAN_TX1 0x02
+#define MSCAN_TX0 0x01
+
+/* MSCAN ID acceptance control register (CANIDAC) bits */
+#define MSCAN_IDAM1 0x20
+#define MSCAN_IDAM0 0x10
+#define MSCAN_IDHIT2 0x04
+#define MSCAN_IDHIT1 0x02
+#define MSCAN_IDHIT0 0x01
+
+#define MSCAN_AF_32BIT 0x00
+#define MSCAN_AF_16BIT MSCAN_IDAM0
+#define MSCAN_AF_8BIT MSCAN_IDAM1
+#define MSCAN_AF_CLOSED (MSCAN_IDAM0|MSCAN_IDAM1)
+#define MSCAN_AF_MASK (~(MSCAN_IDAM0|MSCAN_IDAM1))
+
+/* MSCAN Miscellaneous Register (CANMISC) bits */
+#define MSCAN_BOHOLD 0x01
+
+#ifdef MSCAN_FOR_MPC5200
+#define _MSCAN_RESERVED_(n, num) u8 _res##n[num]
+#define _MSCAN_RESERVED_DSR_SIZE 2
+#else
+#define _MSCAN_RESERVED_(n, num)
+#define _MSCAN_RESERVED_DSR_SIZE 0
+#endif
+
+/* Structure of the hardware registers */
+struct mscan_regs {
+ /* (see doco S12MSCANV3/D) MPC5200 MSCAN */
+ u8 canctl0; /* + 0x00 0x00 */
+ u8 canctl1; /* + 0x01 0x01 */
+ _MSCAN_RESERVED_(1, 2); /* + 0x02 */
+ u8 canbtr0; /* + 0x04 0x02 */
+ u8 canbtr1; /* + 0x05 0x03 */
+ _MSCAN_RESERVED_(2, 2); /* + 0x06 */
+ u8 canrflg; /* + 0x08 0x04 */
+ u8 canrier; /* + 0x09 0x05 */
+ _MSCAN_RESERVED_(3, 2); /* + 0x0a */
+ u8 cantflg; /* + 0x0c 0x06 */
+ u8 cantier; /* + 0x0d 0x07 */
+ _MSCAN_RESERVED_(4, 2); /* + 0x0e */
+ u8 cantarq; /* + 0x10 0x08 */
+ u8 cantaak; /* + 0x11 0x09 */
+ _MSCAN_RESERVED_(5, 2); /* + 0x12 */
+ u8 cantbsel; /* + 0x14 0x0a */
+ u8 canidac; /* + 0x15 0x0b */
+ u8 reserved; /* + 0x16 0x0c */
+ _MSCAN_RESERVED_(6, 5); /* + 0x17 */
+#ifndef MSCAN_FOR_MPC5200
+ u8 canmisc; /* 0x0d */
+#endif
+ u8 canrxerr; /* + 0x1c 0x0e */
+ u8 cantxerr; /* + 0x1d 0x0f */
+ _MSCAN_RESERVED_(7, 2); /* + 0x1e */
+ u16 canidar1_0; /* + 0x20 0x10 */
+ _MSCAN_RESERVED_(8, 2); /* + 0x22 */
+ u16 canidar3_2; /* + 0x24 0x12 */
+ _MSCAN_RESERVED_(9, 2); /* + 0x26 */
+ u16 canidmr1_0; /* + 0x28 0x14 */
+ _MSCAN_RESERVED_(10, 2); /* + 0x2a */
+ u16 canidmr3_2; /* + 0x2c 0x16 */
+ _MSCAN_RESERVED_(11, 2); /* + 0x2e */
+ u16 canidar5_4; /* + 0x30 0x18 */
+ _MSCAN_RESERVED_(12, 2); /* + 0x32 */
+ u16 canidar7_6; /* + 0x34 0x1a */
+ _MSCAN_RESERVED_(13, 2); /* + 0x36 */
+ u16 canidmr5_4; /* + 0x38 0x1c */
+ _MSCAN_RESERVED_(14, 2); /* + 0x3a */
+ u16 canidmr7_6; /* + 0x3c 0x1e */
+ _MSCAN_RESERVED_(15, 2); /* + 0x3e */
+ struct {
+ u16 idr1_0; /* + 0x40 0x20 */
+ _MSCAN_RESERVED_(16, 2); /* + 0x42 */
+ u16 idr3_2; /* + 0x44 0x22 */
+ _MSCAN_RESERVED_(17, 2); /* + 0x46 */
+ u16 dsr1_0; /* + 0x48 0x24 */
+ _MSCAN_RESERVED_(18, 2); /* + 0x4a */
+ u16 dsr3_2; /* + 0x4c 0x26 */
+ _MSCAN_RESERVED_(19, 2); /* + 0x4e */
+ u16 dsr5_4; /* + 0x50 0x28 */
+ _MSCAN_RESERVED_(20, 2); /* + 0x52 */
+ u16 dsr7_6; /* + 0x54 0x2a */
+ _MSCAN_RESERVED_(21, 2); /* + 0x56 */
+ u8 dlr; /* + 0x58 0x2c */
+ u8:8; /* + 0x59 0x2d */
+ _MSCAN_RESERVED_(22, 2); /* + 0x5a */
+ u16 time; /* + 0x5c 0x2e */
+ } rx;
+ _MSCAN_RESERVED_(23, 2); /* + 0x5e */
+ struct {
+ u16 idr1_0; /* + 0x60 0x30 */
+ _MSCAN_RESERVED_(24, 2); /* + 0x62 */
+ u16 idr3_2; /* + 0x64 0x32 */
+ _MSCAN_RESERVED_(25, 2); /* + 0x66 */
+ u16 dsr1_0; /* + 0x68 0x34 */
+ _MSCAN_RESERVED_(26, 2); /* + 0x6a */
+ u16 dsr3_2; /* + 0x6c 0x36 */
+ _MSCAN_RESERVED_(27, 2); /* + 0x6e */
+ u16 dsr5_4; /* + 0x70 0x38 */
+ _MSCAN_RESERVED_(28, 2); /* + 0x72 */
+ u16 dsr7_6; /* + 0x74 0x3a */
+ _MSCAN_RESERVED_(29, 2); /* + 0x76 */
+ u8 dlr; /* + 0x78 0x3c */
+ u8 tbpr; /* + 0x79 0x3d */
+ _MSCAN_RESERVED_(30, 2); /* + 0x7a */
+ u16 time; /* + 0x7c 0x3e */
+ } tx;
+ _MSCAN_RESERVED_(31, 2); /* + 0x7e */
+} __attribute__ ((packed));
+
+#undef _MSCAN_RESERVED_
+#define MSCAN_REGION sizeof(struct mscan)
+
+struct net_device *alloc_mscandev(void);
+/* clock_src:
+ * 1 = The MSCAN clock source is the onchip Bus Clock.
+ * 0 = The MSCAN clock source is the chip Oscillator Clock.
+ */
+extern int register_mscandev(struct net_device *dev, int clock_src);
+extern void unregister_mscandev(struct net_device *dev);
+
+#endif /* __MSCAN_H__ */
diff --git a/drivers/net/can/old/Kconfig b/drivers/net/can/old/Kconfig
new file mode 100644
index 000000000000..4b8567a20d03
--- /dev/null
+++ b/drivers/net/can/old/Kconfig
@@ -0,0 +1,67 @@
+
+config CAN_SJA1000_OLD
+ depends on CAN_OLD_DRIVERS
+ tristate "Philips SJA1000 (old)"
+ ---help---
+ The SJA1000 is one of the top CAN controllers out there. As it
+ has a multiplexed interface it fits directly to 8051
+ microcontrollers or into the PC I/O port space. The SJA1000
+ is a full CAN controller, with shadow registers for RX and TX.
+ It can send and receive any kinds of CAN frames (SFF/EFF/RTR)
+ with a single (simple) filter setup.
+ REMARK: This is the 'old' driver originally written by Matthias
+ Brukner and Oliver Hartkopp which uses a non-standard hardware
+ abstaction layer (HAL) inspired by the OCAN driver.
+
+config CAN_I82527_OLD
+ depends on CAN_OLD_DRIVERS
+ tristate "Intel 82527 (old)"
+ ---help---
+ The i82527 is a complex CAN controller that can handle RTR
+ frame replies on it's own. This feature (and diffent RX filters)
+ lead to an amount of 15 message objects (for RX & TX). Message
+ object 15 has (as only) a shadow register for a reliable
+ receiption of EFF or(!) SFF frames at high CAN traffic.
+ This driver can send each type of CAN frames (SFF/EFF/RTR).
+ Using 4 message objects it can also receive each type of CAN
+ frames. But due to the onchip filter matching trigger method
+ it is not possible to determine the received RTR CAN-ID.
+ The reliable message object 15 receives SFF frames by default.
+ This message object 15 usage maybe changed with the mo15 param.
+ REMARK: This is the 'old' driver originally written by Oliver
+ Hartkopp which uses a non-standard hardware abstaction layer (HAL)
+ inspired by the OCAN driver. http://ar.linux.it/software/#ocan
+
+config CAN_MSCAN_OLD
+ depends on CAN_OLD_DRIVERS && (PPC || M68K || M68KNOMMU)
+ tristate "Support for a Freescale MSCAN based chips (old)"
+ ---help---
+ The Motorola Scalable Controller Area Network (MSCAN) definition
+ is based on the MSCAN12 definition which is the specific
+ implementation of the Motorola Scalable CAN concept targeted for
+ the Motorola MC68HC12 Microcontroller Family.
+
+config CAN_MPC52XX_OLD
+ tristate "Freescale MPC5200 onboard CAN controller (old)"
+ depends on CAN_MSCAN_OLD && (PPC_MPC52xx || PPC_52xx)
+ default LITE5200
+ ---help---
+ If you say yes here you get support for Freescale MPC5200
+ onboard dualCAN controller.
+
+ This driver can also be built as a module. If so, the module
+ will be called mpc52xx_can.
+
+config CAN_CCAN_OLD
+ depends on CAN_OLD_DRIVERS
+ tristate "Bosch CCAN driver (old)"
+ ---help---
+ This is a driver for the Bosch CCAN controller found for example
+ on the hynix h7202 chip.
+
+config CAN_H7202_OLD
+ tristate "Hynix H7202 onboard CAN controller (old)"
+ depends on CAN_CCAN_OLD
+ ---help---
+ This is a driver for the hynix h7202 can controller.
+
diff --git a/drivers/net/can/old/ccan/Makefile b/drivers/net/can/old/ccan/Makefile
new file mode 100644
index 000000000000..c3db1800e6d7
--- /dev/null
+++ b/drivers/net/can/old/ccan/Makefile
@@ -0,0 +1,20 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+obj-$(CONFIG_CAN_CCAN) += ccan.o
+obj-$(CONFIG_CAN_H7202) += h7202_can.o
+
+endif
diff --git a/drivers/net/can/old/ccan/ccan.c b/drivers/net/can/old/ccan/ccan.c
new file mode 100644
index 000000000000..6a908a14eee4
--- /dev/null
+++ b/drivers/net/can/old/ccan/ccan.c
@@ -0,0 +1,557 @@
+/*
+ * drivers/can/c_can.c
+ *
+ * Copyright (C) 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...)
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/can.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include "ccan.h"
+
+static u32 ccan_read_reg32(struct net_device *dev, enum c_regs reg)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ u32 val = priv->read_reg(dev, reg);
+ val |= ((u32) priv->read_reg(dev, reg + 2)) << 16;
+
+ return val;
+}
+
+static void ccan_write_reg32(struct net_device *dev, enum c_regs reg, u32 val)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->write_reg(dev, reg, val & 0xffff);
+ priv->write_reg(dev, reg + 2, val >> 16);
+}
+
+static inline void ccan_object_get(struct net_device *dev,
+ int iface, int objno, int mask)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->write_reg(dev, CAN_IF_COMM(iface), mask);
+ priv->write_reg(dev, CAN_IF_COMR(iface), objno + 1);
+ while (priv->read_reg(dev, CAN_IF_COMR(iface)) & IF_COMR_BUSY)
+ DBG("busy\n");
+}
+
+static inline void ccan_object_put(struct net_device *dev,
+ int iface, int objno, int mask)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->write_reg(dev, CAN_IF_COMM(iface), IF_COMM_WR | mask);
+ priv->write_reg(dev, CAN_IF_COMR(iface), objno + 1);
+ while (priv->read_reg(dev, CAN_IF_COMR(iface)) & IF_COMR_BUSY)
+ DBG("busy\n");
+}
+
+static int ccan_write_object(struct net_device *dev,
+ int iface, struct can_frame *frame, int objno)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned int val;
+
+ if (frame->can_id & CAN_EFF_FLAG)
+ val = IF_ARB_MSGXTD | (frame->can_id & CAN_EFF_MASK);
+ else
+ val = ((frame->can_id & CAN_SFF_MASK) << 18);
+
+ if (!(frame->can_id & CAN_RTR_FLAG))
+ val |= IF_ARB_TRANSMIT;
+
+ val |= IF_ARB_MSGVAL;
+ ccan_write_reg32(dev, CAN_IF_ARB(iface), val);
+
+ memcpy(&val, &frame->data[0], 4);
+ ccan_write_reg32(dev, CAN_IF_DATAA(iface), val);
+ memcpy(&val, &frame->data[4], 4);
+ ccan_write_reg32(dev, CAN_IF_DATAB(iface), val);
+ priv->write_reg(dev, CAN_IF_MCONT(iface),
+ IF_MCONT_TXIE | IF_MCONT_TXRQST | IF_MCONT_EOB |
+ (frame->can_dlc & 0xf));
+
+ ccan_object_put(dev, 0, objno, IF_COMM_ALL);
+
+ return 0;
+}
+
+static int ccan_read_object(struct net_device *dev, int iface, int objno)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned int val, ctrl, data;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ skb->dev = dev;
+
+ ccan_object_get(dev, 0, objno, IF_COMM_ALL & ~IF_COMM_TXRQST);
+#ifdef CCAN_DEBUG
+ priv->bufstat[objno]++;
+#endif
+ frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ ctrl = priv->read_reg(dev, CAN_IF_MCONT(iface));
+
+ if (ctrl & IF_MCONT_MSGLST) {
+ priv->can.net_stats.rx_errors++;
+ DBG("%s: msg lost in buffer %d\n", __func__, objno);
+ }
+
+ frame->can_dlc = ctrl & 0xf;
+
+ val = ccan_read_reg32(dev, CAN_IF_ARB(iface));
+
+ data = ccan_read_reg32(dev, CAN_IF_DATAA(iface));
+ memcpy(&frame->data[0], &data, 4);
+ data = ccan_read_reg32(dev, CAN_IF_DATAB(iface));
+ memcpy(&frame->data[4], &data, 4);
+
+ if (val & IF_ARB_MSGXTD)
+ frame->can_id = (val & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ else
+ frame->can_id = (val >> 18) & CAN_SFF_MASK;
+
+ if (val & IF_ARB_TRANSMIT)
+ frame->can_id |= CAN_RTR_FLAG;
+
+ priv->write_reg(dev, CAN_IF_MCONT(iface), ctrl &
+ ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT));
+
+ ccan_object_put(dev, 0, objno, IF_COMM_CONTROL);
+
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ netif_rx(skb);
+
+ priv->can.net_stats.rx_packets++;
+ priv->can.net_stats.rx_bytes += frame->can_dlc;
+
+ return 0;
+}
+
+static int ccan_setup_receive_object(struct net_device *dev, int iface,
+ int objno, unsigned int mask,
+ unsigned int id, unsigned int mcont)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_write_reg32(dev, CAN_IF_MASK(iface), mask);
+ ccan_write_reg32(dev, CAN_IF_ARB(iface), IF_ARB_MSGVAL | id);
+
+ priv->write_reg(dev, CAN_IF_MCONT(iface), mcont);
+
+ ccan_object_put(dev, 0, objno, IF_COMM_ALL & ~IF_COMM_TXRQST);
+
+ DBG("%s: obj no %d msgval: 0x%08x\n", __func__,
+ objno, ccan_read_reg32(dev, CAN_MSGVAL));
+
+ return 0;
+}
+
+static int ccan_inval_object(struct net_device *dev, int iface, int objno)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_write_reg32(dev, CAN_IF_ARB(iface), 0);
+ priv->write_reg(dev, CAN_IF_MCONT(iface), 0);
+ ccan_object_put(dev, 0, objno, IF_COMM_ARB | IF_COMM_CONTROL);
+
+ DBG("%s: obj no %d msgval: 0x%08x\n", __func__,
+ objno, ccan_read_reg32(dev, CAN_MSGVAL));
+
+ return 0;
+}
+
+static int ccan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ struct can_frame *frame = (struct can_frame *)skb->data;
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ ccan_write_object(dev, 0, frame, priv->tx_object);
+#ifdef CCAN_DEBUG
+ priv->bufstat[priv->tx_object]++;
+#endif
+ priv->tx_object++;
+ if (priv->tx_object > 5)
+ netif_stop_queue(dev);
+
+ spin_unlock_irq(&priv->can.irq_lock);
+
+ priv->can.net_stats.tx_packets++;
+ priv->can.net_stats.tx_bytes += frame->can_dlc;
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+static void ccan_tx_timeout(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ priv->can.net_stats.tx_errors++;
+}
+
+static int ccan_set_bittime(struct net_device *dev, struct can_bittime *br)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned int reg_timing, ctrl_save;
+ u8 brp, sjw, tseg1, tseg2;
+
+ if (br->type != CAN_BITTIME_STD)
+ return -EINVAL;
+
+ brp = br->std.brp - 1;
+ sjw = br->std.sjw - 1;
+ tseg1 = br->std.prop_seg + br->std.phase_seg1 - 1;
+ tseg2 = br->std.phase_seg2 - 1;
+
+ reg_timing = (brp & BTR_BRP_MASK) |
+ ((sjw << BTR_SJW_SHIFT) & BTR_SJW_MASK) |
+ ((tseg1 << BTR_TSEG1_SHIFT) & BTR_TSEG1_MASK) |
+ ((tseg2 << BTR_TSEG2_SHIFT) & BTR_TSEG2_MASK);
+
+ DBG("%s: brp = %d sjw = %d seg1 = %d seg2 = %d\n", __func__,
+ brp, sjw, tseg1, tseg2);
+ DBG("%s: setting BTR to %04x\n", __func__, reg_timing);
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ ctrl_save = priv->read_reg(dev, CAN_CONTROL);
+ priv->write_reg(dev, CAN_CONTROL,
+ ctrl_save | CONTROL_CCE | CONTROL_INIT);
+ priv->write_reg(dev, CAN_BTR, reg_timing);
+ priv->write_reg(dev, CAN_CONTROL, ctrl_save);
+
+ spin_unlock_irq(&priv->can.irq_lock);
+
+ return 0;
+}
+
+static int ccan_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ switch (mode) {
+ case CAN_MODE_START:
+ DBG("%s: CAN_MODE_START requested\n", __func__);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ccan_get_state(struct net_device *dev, enum can_state *state)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ u32 reg_status;
+#ifdef CCAN_DEBUG
+ int i;
+#endif
+
+ reg_status = priv->read_reg(dev, CAN_STATUS);
+
+ if (reg_status & STATUS_EPASS)
+ *state = CAN_STATE_BUS_PASSIVE;
+ else if (reg_status & STATUS_EWARN)
+ *state = CAN_STATE_BUS_WARNING;
+ else if (reg_status & STATUS_BOFF)
+ *state = CAN_STATE_BUS_OFF;
+ else
+ *state = CAN_STATE_ACTIVE;
+#ifdef CCAN_DEBUG
+ DBG("buffer statistic:\n");
+ for (i = 0; i <= MAX_OBJECT; i++)
+ DBG("%d: %d\n", i, priv->bufstat[i]);
+#endif
+ return 0;
+}
+
+static int ccan_do_status_irq(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ int status, diff;
+
+ status = priv->read_reg(dev, CAN_STATUS);
+ status &= ~(STATUS_TXOK | STATUS_RXOK);
+ diff = status ^ priv->last_status;
+
+ if (diff & STATUS_EPASS) {
+ if (status & STATUS_EPASS)
+ dev_info(ND2D(dev), "entered error passive state\n");
+ else
+ dev_info(ND2D(dev), "left error passive state\n");
+ }
+ if (diff & STATUS_EWARN) {
+ if (status & STATUS_EWARN)
+ dev_info(ND2D(dev), "entered error warning state\n");
+ else
+ dev_info(ND2D(dev), "left error warning state\n");
+ }
+ if (diff & STATUS_BOFF) {
+ if (status & STATUS_BOFF)
+ dev_info(ND2D(dev), "entered busoff state\n");
+ else
+ dev_info(ND2D(dev), "left busoff state\n");
+ }
+
+ if (diff & STATUS_LEC_MASK) {
+ switch (status & STATUS_LEC_MASK) {
+ case LEC_STUFF_ERROR:
+ dev_info(ND2D(dev), "suffing error\n");
+ break;
+ case LEC_FORM_ERROR:
+ dev_info(ND2D(dev), "form error\n");
+ break;
+ case LEC_ACK_ERROR:
+ dev_info(ND2D(dev), "ack error\n");
+ break;
+ case LEC_BIT1_ERROR:
+ dev_info(ND2D(dev), "bit1 error\n");
+ break;
+ }
+ }
+
+ priv->write_reg(dev, CAN_STATUS, 0);
+ priv->last_status = status;
+
+ return diff ? 1 : 0;
+}
+
+static void ccan_do_object_irq(struct net_device *dev, u16 irqstatus)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ int i;
+ u32 val;
+
+ if (irqstatus > MAX_TRANSMIT_OBJECT) {
+ val = ccan_read_reg32(dev, CAN_NEWDAT);
+ while (val & RECEIVE_OBJECT_BITS) {
+ for (i = MAX_TRANSMIT_OBJECT + 1; i <= MAX_OBJECT; i++)
+ if (val & (1<<i))
+ ccan_read_object(dev, 0, i);
+ val = ccan_read_reg32(dev, CAN_NEWDAT);
+ }
+ } else {
+ ccan_inval_object(dev, 0, irqstatus - 1);
+ val = ccan_read_reg32(dev, CAN_TXRQST);
+ if (!val) {
+ priv->tx_object = 0;
+ netif_wake_queue(dev);
+ }
+ }
+}
+
+static void do_statuspoll(struct work_struct *work)
+{
+ struct ccan_priv *priv = container_of(((struct delayed_work *) work),
+ struct ccan_priv, work);
+
+ priv->write_reg(priv->dev, CAN_CONTROL,
+ CONTROL_SIE | CONTROL_EIE | CONTROL_IE);
+}
+
+static irqreturn_t ccan_isr(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct ccan_priv *priv = netdev_priv(dev);
+ u16 irqstatus;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->can.irq_lock, flags);
+
+ irqstatus = priv->read_reg(dev, CAN_IR);
+ while (irqstatus) {
+ if (irqstatus == 0x8000) {
+ if (ccan_do_status_irq(dev)) {
+ /* The c_can core tends to flood us with
+ * interrupts when certain error states don't
+ * disappear. Disable interrupts and see if it's
+ * getting better later. This is at least the
+ * case on the Magnachip h7202.
+ */
+ priv->write_reg(dev, CAN_CONTROL, CONTROL_EIE |
+ CONTROL_IE);
+ schedule_delayed_work(&priv->work, HZ / 10);
+ goto exit;
+ }
+ } else {
+ ccan_do_object_irq(dev, irqstatus);
+ }
+ irqstatus = priv->read_reg(dev, CAN_IR);
+ }
+
+exit:
+ spin_unlock_irqrestore(&priv->can.irq_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int ccan_open(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ if (request_irq(dev->irq, &ccan_isr, 0, dev->name, dev)) {
+ dev_err(ND2D(dev), "failed to attach interrupt\n");
+ return -EAGAIN;
+ }
+
+ priv->write_reg(dev, CAN_CONTROL,
+ CONTROL_EIE | CONTROL_SIE | CONTROL_IE);
+
+ netif_wake_queue(dev);
+
+ return 0;
+}
+
+static int ccan_stop(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ netif_stop_queue(dev);
+
+ cancel_delayed_work(&priv->work);
+ flush_scheduled_work();
+
+ /* mask all IRQs */
+ spin_lock_irqsave(&priv->can.irq_lock, flags);
+ priv->write_reg(dev, CAN_CONTROL, 0);
+ spin_unlock_irqrestore(&priv->can.irq_lock, flags);
+
+ free_irq(dev->irq, dev);
+
+ return 0;
+}
+
+static int ccan_chip_config(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+ int i;
+
+ /* setup message objects */
+ for (i = 0; i <= MAX_OBJECT; i++)
+ ccan_inval_object(dev, 0, i);
+
+ for (i = MAX_TRANSMIT_OBJECT + 1; i < MAX_OBJECT; i++)
+ ccan_setup_receive_object(dev, 0, i, 0, 0,
+ IF_MCONT_RXIE | IF_MCONT_UMASK);
+
+ ccan_setup_receive_object(dev, 0, MAX_OBJECT, 0, 0, IF_MCONT_EOB |
+ IF_MCONT_RXIE | IF_MCONT_UMASK);
+
+#ifdef CCAN_DEBUG
+ for (i = 0; i <= MAX_OBJECT; i++)
+ priv->bufstat[i] = 0;
+#endif
+
+ return 0;
+}
+
+struct net_device *alloc_ccandev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct ccan_priv *priv;
+
+ dev = alloc_candev(sizeof_priv);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+
+ dev->open = ccan_open;
+ dev->stop = ccan_stop;
+ dev->hard_start_xmit = ccan_hard_start_xmit;
+ dev->tx_timeout = ccan_tx_timeout;
+
+ priv->can.bitrate = 500000;
+
+ priv->can.do_set_bittime = ccan_set_bittime;
+ priv->can.do_get_state = ccan_get_state;
+ priv->can.do_set_mode = ccan_set_mode;
+
+ priv->dev = dev;
+ priv->tx_object = 0;
+
+ return dev;
+}
+EXPORT_SYMBOL(alloc_ccandev);
+
+void free_ccandev(struct net_device *dev)
+{
+ free_candev(dev);
+}
+EXPORT_SYMBOL(free_ccandev);
+
+int register_ccandev(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_set_mode(dev, CAN_MODE_START);
+
+ ccan_chip_config(dev);
+ INIT_DELAYED_WORK(&priv->work, do_statuspoll);
+
+ return register_netdev(dev);
+}
+EXPORT_SYMBOL(register_ccandev);
+
+void unregister_ccandev(struct net_device *dev)
+{
+ struct ccan_priv *priv = netdev_priv(dev);
+
+ ccan_set_mode(dev, CAN_MODE_START);
+
+ cancel_delayed_work(&priv->work);
+ flush_scheduled_work();
+
+ unregister_netdev(dev);
+}
+EXPORT_SYMBOL(unregister_ccandev);
+
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Simon Kallweit <simon.kallweit@intefo.ch>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for C_CAN based chips");
diff --git a/drivers/net/can/old/ccan/ccan.h b/drivers/net/can/old/ccan/ccan.h
new file mode 100644
index 000000000000..b7a5460195e1
--- /dev/null
+++ b/drivers/net/can/old/ccan/ccan.h
@@ -0,0 +1,140 @@
+/*
+ * drivers/can/c_can.h
+ *
+ * Copyright (C) 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __CCAN_H__
+#define __CCAN_H__
+
+#include <linux/can.h>
+#include <linux/platform_device.h>
+
+#undef CCAN_DEBUG
+
+enum c_regs {
+ CAN_CONTROL = 0x00,
+ CAN_STATUS = 0x02,
+ CAN_ERROR = 0x04,
+ CAN_BTR = 0x06,
+ CAN_IR = 0x08,
+ CAN_TEST = 0x0a,
+ CAN_BRP_EXT = 0x0c,
+ CAN_IF1 = 0x10,
+ CAN_IF2 = 0x40,
+ CAN_TXRQST = 0x80, /* 32bit */
+ CAN_NEWDAT = 0x90, /* 32bit */
+ CAN_INTPND = 0xa0, /* 32bit */
+ CAN_MSGVAL = 0xb0, /* 32bit */
+};
+
+#define CAN_IF_COMR(x) (CAN_IF1 + (x) * 0x30 + 0x00)
+#define CAN_IF_COMM(x) (CAN_IF1 + (x) * 0x30 + 0x02)
+#define CAN_IF_MASK(x) (CAN_IF1 + (x) * 0x30 + 0x04) /* 32bit */
+#define CAN_IF_ARB(x) (CAN_IF1 + (x) * 0x30 + 0x08) /* 32bit */
+#define CAN_IF_MCONT(x) (CAN_IF1 + (x) * 0x30 + 0x0c)
+#define CAN_IF_DATAA(x) (CAN_IF1 + (x) * 0x30 + 0x0e) /* 32bit */
+#define CAN_IF_DATAB(x) (CAN_IF1 + (x) * 0x30 + 0x12) /* 32bit */
+
+#define CONTROL_TEST (1<<7)
+#define CONTROL_CCE (1<<6)
+#define CONTROL_DAR (1<<5)
+#define CONTROL_EIE (1<<3)
+#define CONTROL_SIE (1<<2)
+#define CONTROL_IE (1<<1)
+#define CONTROL_INIT (1<<0)
+
+#define TEST_RX (1<<7)
+#define TEST_TX1 (1<<6)
+#define TEST_TX2 (1<<5)
+#define TEST_LBACK (1<<4)
+#define TEST_SILENT (1<<3)
+#define TEST_BASIC (1<<2)
+
+#define STATUS_BOFF (1<<7)
+#define STATUS_EWARN (1<<6)
+#define STATUS_EPASS (1<<5)
+#define STATUS_RXOK (1<<4)
+#define STATUS_TXOK (1<<3)
+#define STATUS_LEC_MASK (1<<2)
+#define LEC_STUFF_ERROR 1
+#define LEC_FORM_ERROR 2
+#define LEC_ACK_ERROR 3
+#define LEC_BIT1_ERROR 4
+
+#define BTR_BRP_MASK 0x3f
+#define BTR_BRP_SHIFT 0
+#define BTR_SJW_SHIFT 6
+#define BTR_SJW_MASK (0x3 << BTR_SJW_SHIFT)
+#define BTR_TSEG1_SHIFT 8
+#define BTR_TSEG1_MASK (0xf << BTR_TSEG1_SHIFT)
+#define BTR_TSEG2_SHIFT 12
+#define BTR_TSEG2_MASK (0x7 << BTR_TSEG2_SHIFT)
+
+#define IF_COMR_BUSY (1<<15)
+
+#define IF_COMM_WR (1<<7)
+#define IF_COMM_MASK (1<<6)
+#define IF_COMM_ARB (1<<5)
+#define IF_COMM_CONTROL (1<<4)
+#define IF_COMM_CLR_INT_PND (1<<3)
+#define IF_COMM_TXRQST (1<<2)
+#define IF_COMM_DATAA (1<<1)
+#define IF_COMM_DATAB (1<<0)
+
+#define IF_COMM_ALL (IF_COMM_MASK | IF_COMM_ARB | IF_COMM_CONTROL | \
+ IF_COMM_TXRQST | IF_COMM_DATAA | IF_COMM_DATAB)
+
+#define IF_ARB_MSGVAL (1<<31)
+#define IF_ARB_MSGXTD (1<<30)
+#define IF_ARB_TRANSMIT (1<<29)
+
+#define IF_MCONT_NEWDAT (1<<15)
+#define IF_MCONT_MSGLST (1<<14)
+#define IF_MCONT_INTPND (1<<13)
+#define IF_MCONT_UMASK (1<<12)
+#define IF_MCONT_TXIE (1<<11)
+#define IF_MCONT_RXIE (1<<10)
+#define IF_MCONT_RMTEN (1<<9)
+#define IF_MCONT_TXRQST (1<<8)
+#define IF_MCONT_EOB (1<<7)
+
+#define MAX_OBJECT 31
+#define MAX_TRANSMIT_OBJECT 15
+#define RECEIVE_OBJECT_BITS 0xffff0000
+
+struct ccan_priv {
+ struct can_priv can;
+ struct net_device *dev;
+ int tx_object;
+ int last_status;
+ struct delayed_work work;
+ u16 (*read_reg)(struct net_device *dev, enum c_regs reg);
+ void (*write_reg)(struct net_device *dev, enum c_regs reg, u16 val);
+#ifdef CCAN_DEBUG
+ unsigned int bufstat[MAX_OBJECT + 1];
+#endif
+};
+
+extern struct net_device *alloc_ccandev(int sizeof_priv);
+extern void free_ccandev(struct net_device *dev);
+extern int register_ccandev(struct net_device *dev);
+extern void unregister_ccandev(struct net_device *dev);
+
+#endif /* __CCAN_H__ */
diff --git a/drivers/net/can/old/ccan/h7202_can.c b/drivers/net/can/old/ccan/h7202_can.c
new file mode 100644
index 000000000000..7dcc3e749248
--- /dev/null
+++ b/drivers/net/can/old/ccan/h7202_can.c
@@ -0,0 +1,199 @@
+/*
+ * drivers/can/h7202_can.c
+ *
+ * Copyright (C) 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+#include <asm/hardware.h>
+
+#include "ccan.h"
+
+#define DRV_NAME "h7202can"
+#define DELAY 5
+#define CAN_ENABLE 0x0e
+
+static u16 h7202can_read_reg(struct net_device *dev, enum c_regs reg)
+{
+ u16 val;
+ volatile int i;
+
+ /* The big kernel lock is used to prevent any other AMBA devices from
+ * interfering with the current register read operation. The register
+ * is read twice because of braindamaged hynix cpu.
+ */
+ lock_kernel();
+ val = inw(dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ val = inw(dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ unlock_kernel();
+
+ return val;
+}
+
+static void h7202can_write_reg(struct net_device *dev, enum c_regs reg, u16 val)
+{
+ volatile int i;
+
+ lock_kernel();
+ outw(val, dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ unlock_kernel();
+}
+
+static int h7202can_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ccan_priv *priv;
+ struct resource *mem;
+ u32 mem_size;
+ int ret = -ENODEV;
+
+ dev = alloc_ccandev(sizeof(struct ccan_priv));
+ if (!dev)
+ return -ENOMEM;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (!mem || !dev->irq)
+ goto req_error;
+
+ mem_size = mem->end - mem->start + 1;
+ if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
+ dev_err(&pdev->dev, "resource unavailable\n");
+ goto req_error;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
+
+ if (!dev->base_addr) {
+ dev_err(&pdev->dev, "failed to map can port\n");
+ ret = -ENOMEM;
+ goto fail_map;
+ }
+
+ priv = netdev_priv(dev);
+ priv->can.can_sys_clock = 8000000;
+ priv->read_reg = h7202can_read_reg;
+ priv->write_reg = h7202can_write_reg;
+
+ platform_set_drvdata(pdev, dev);
+
+ /* configure ports */
+ switch (mem->start) {
+ case CAN0_PHYS:
+ CPU_REG(GPIO_C_VIRT, GPIO_EN) &= ~(3<<1);
+ CPU_REG(GPIO_C_VIRT, GPIO_DIR) &= ~(1<<1);
+ CPU_REG(GPIO_C_VIRT, GPIO_DIR) |= (1<<2);
+ break;
+ case CAN1_PHYS:
+ CPU_REG(GPIO_E_VIRT, GPIO_EN) &= ~(3<<16);
+ CPU_REG(GPIO_E_VIRT, GPIO_DIR) |= (1<<16);
+ CPU_REG(GPIO_E_VIRT, GPIO_DIR) &= ~(1<<17);
+ break;
+ }
+
+ /* enable can */
+ h7202can_write_reg(dev, CAN_ENABLE, 1);
+
+ ret = register_ccandev(dev);
+ if (ret >= 0) {
+ dev_info(&pdev->dev, "probe for a port 0x%lX done\n",
+ dev->base_addr);
+ return ret;
+ }
+
+ iounmap((unsigned long *)dev->base_addr);
+fail_map:
+ release_mem_region(mem->start, mem_size);
+req_error:
+ free_ccandev(dev);
+ dev_err(&pdev->dev, "probe failed\n");
+ return ret;
+}
+
+static int h7202can_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_ccandev(dev);
+
+ iounmap((volatile void __iomem *)(dev->base_addr));
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+ free_ccandev(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int h7202can_drv_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int h7202can_drv_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct platform_driver h7202can_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = h7202can_drv_probe,
+ .remove = h7202can_drv_remove,
+#ifdef CONFIG_PM
+ .suspend = h7202can_drv_suspend,
+ .resume = h7202can_drv_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init h7202can_init(void)
+{
+ printk(KERN_INFO "%s initializing\n", h7202can_driver.driver.name);
+ return platform_driver_register(&h7202can_driver);
+}
+
+static void __exit h7202can_cleanup(void)
+{
+ platform_driver_unregister(&h7202can_driver);
+ printk(KERN_INFO "%s unloaded\n", h7202can_driver.driver.name);
+}
+
+module_init(h7202can_init);
+module_exit(h7202can_cleanup);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Simon Kallweit <simon.kallweit@intefo.ch>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver Hynix H7202 processor");
diff --git a/drivers/net/can/old/hal/c200.c b/drivers/net/can/old/hal/c200.c
new file mode 100644
index 000000000000..47ea74ca51b0
--- /dev/null
+++ b/drivers/net/can/old/hal/c200.c
@@ -0,0 +1,206 @@
+/*
+ * c200.c - low cost parallelport CAN adaptor hardware abstraction layer
+ * ( direct register access without parport subsystem support )
+ *
+ * CAN200 project homepage http://private.addcom.de/horo/can200
+ *
+ * This hal is based on a patch from Uwe Bonnes.
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "c200"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x378UL;
+ irq[0] = 7;
+}
+
+#define ECR_REGS_OFFSET 0x400
+#define ECR_CTRL_OFFSET (ECR_REGS_OFFSET + 2)
+
+static u8 ecr_crtl_save;
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* grab ECR control registers and set parport to 'byte mode' */
+ if (request_region(rbase[dev_num] + ECR_REGS_OFFSET, 3, drv_name)) {
+
+ ecr_crtl_save = inb(rbase[dev_num] + ECR_CTRL_OFFSET);
+
+ outb((ecr_crtl_save & 0x1F) | 0x20,
+ rbase[dev_num] + ECR_CTRL_OFFSET);
+ } else
+ return 0;
+
+ if (request_region(rbase[dev_num], 4, drv_name))
+ return 1;
+
+ release_region(rbase[dev_num] + ECR_REGS_OFFSET, 3);
+
+ return 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ release_region(base[dev_num], 4);
+
+ /* restore original ECR control register value */
+ outb(ecr_crtl_save, base[dev_num] + ECR_CTRL_OFFSET);
+ release_region(base[dev_num] + ECR_REGS_OFFSET, 3);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num)
+{
+ extern unsigned long rbase[];
+ unsigned long pc = rbase[dev_num] + 2;
+
+ /* enable irq */
+ outb(inb(pc) | 0x10, pc);
+
+ return 0;
+}
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num)
+{
+ extern unsigned long rbase[];
+ unsigned long pc = rbase[dev_num] + 2;
+
+ /* disable irq */
+ outb(inb(pc) & ~0x10, pc);
+
+ return 0;
+}
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+#define WRITEP 0x01 /* inverted at port */
+#define DATASTB 0x02 /* inverted at port and at device*/
+#define ADDRSTB 0x08 /* inverted at port and at device*/
+#define PORTREAD 0x20
+
+static DEFINE_SPINLOCK(c200_lock);
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg)
+{
+ unsigned long pa = base;
+ unsigned long pc = pa + 2;
+ unsigned long flags;
+ u8 irqstatus = (inb(pc) & 0x10) | 0x04;
+ u8 val;
+
+ spin_lock_irqsave(&c200_lock, flags);
+
+ outb(irqstatus | ADDRSTB, pc);
+ outb((reg & 0x1F) | 0x80, pa);
+ outb(irqstatus, pc);
+ outb(irqstatus | PORTREAD, pc);
+ outb(irqstatus | DATASTB | PORTREAD, pc);
+ val = inb(pa);
+ outb(irqstatus, pc);
+
+ spin_unlock_irqrestore(&c200_lock, flags);
+
+ return val;
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val)
+{
+ unsigned long pa = base;
+ unsigned long pc = pa + 2;
+ unsigned long flags;
+ u8 irqstatus = (inb(pc) & 0x10) | 0x04;
+
+ spin_lock_irqsave(&c200_lock, flags);
+
+ outb(irqstatus | ADDRSTB, pc);
+ outb(reg & 0x1F, pa);
+ outb(irqstatus, pc);
+ outb(irqstatus | WRITEP, pc);
+ outb(irqstatus | DATASTB | WRITEP, pc);
+ outb(val, pa);
+ outb(irqstatus, pc);
+
+ spin_unlock_irqrestore(&c200_lock, flags);
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
diff --git a/drivers/net/can/old/hal/esdio.c b/drivers/net/can/old/hal/esdio.c
new file mode 100644
index 000000000000..a0132e2ed545
--- /dev/null
+++ b/drivers/net/can/old/hal/esdio.c
@@ -0,0 +1,186 @@
+/*
+ * esdio.c - multiplex register access CAN hardware abstraction layer
+ * for the esd 3xCAN pc104 board
+ * http://www.esd-electronics.de/products/CAN/can-pc104-200_e.htm
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2007 Fraunhofer FOKUS
+ *
+ * Provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ * History:
+ * 2007-05-22 Bjoern Riemer: initial release
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) printk(args)
+#else
+#define DBG(args...)
+#endif
+
+//#define DBG(args...) printk(args)
+
+int esd_ale_offset = 1; //default for the sja1000 chip
+int esd_cs_offset = 0; //default for the sja1000 chip
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "esdio"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x1e8UL;
+ irq[0] = 5;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ if (!memcmp(drv_name,"i82527-esdio",sizeof("i82527-esdio"))){
+ esd_ale_offset = 7;
+ esd_cs_offset = 4;
+ } else if (!memcmp(drv_name,"sja1000-esdio",sizeof("sja1000-esdio"))){
+ esd_ale_offset = 1;
+ esd_cs_offset = 0;
+ }
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base + esd_ale_offset */
+ /* data register = base + esd_cs_offset */
+ if (request_region(base[dev_num] + esd_ale_offset, 1, drv_name)){
+ if (request_region(base[dev_num] + esd_cs_offset, 1,drv_name)){
+ return 1;
+ } else {
+ release_region(base[dev_num]+esd_ale_offset, 1);
+ return 0; // error
+ }
+ }
+
+ return 0; // error
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base + esd_ale_offset */
+ /* data register = base + esd_cs_offset */
+ release_region(base[dev_num] + esd_cs_offset, 1);
+ release_region(base[dev_num] + esd_ale_offset, 1);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num)
+{
+ int i, stat, i1;
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ i1 = irq[dev_num]; //get IRQ number
+ DBG(KERN_INFO "esdio.c: enabling IRQ %d for dev_num %d\n",i1,dev_num);
+
+ for (i=0; i<4; i++){
+ stat=i; // bit 0,1 selects the latch bit to write
+ if (i1 & 0x01){
+ stat |= 0x80; //bit7 carrys the value of the latch bit
+ }
+ outb(stat,base[dev_num]+3);
+ i1 = i1>>1;
+ }
+
+ outb(0x87,base[dev_num]+3); //enable irq selection
+ outb(0x86,base[dev_num]+3); //enable irq tristate buffer
+
+ return 1;
+}
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num)
+{
+ int i;
+ extern unsigned long base[];
+
+ DBG(KERN_INFO "esdio.c: diabling IRQ for dev_num %d\n",dev_num);
+
+ outb(0x07,base[dev_num]+3); //disable irq selection
+ outb(0x06,base[dev_num]+3); //disable irq tristate buffer
+
+ for (i=0; i<4; i++)
+ outb(i,base[dev_num]+3);
+
+ return 1;
+}
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ outb(reg, base + esd_ale_offset); /* address */
+ return inb(base + esd_cs_offset); /* data */
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(reg, base + esd_ale_offset); /* address */
+ outb(val, base + esd_cs_offset); /* data */
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) {
+
+ outb(0x86,dev->base_addr+3); //enable irq tristate buffer
+ return;
+}
diff --git a/drivers/net/can/old/hal/gw2.c b/drivers/net/can/old/hal/gw2.c
new file mode 100644
index 000000000000..893b983e2ec2
--- /dev/null
+++ b/drivers/net/can/old/hal/gw2.c
@@ -0,0 +1,161 @@
+/*
+ * gw2.c - Trajet GW2 register access CAN hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+#define ADDR_GAP 1
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "gw2"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+ extern unsigned int speed[];
+
+ base[0] = 0xF0100200UL;
+ irq[0] = 26;
+ speed[0] = 500;
+
+ base[1] = 0xF0100300UL;
+ irq[1] = 26;
+ speed[1] = 100;
+
+ base[2] = 0xF0100400UL;
+ irq[2] = 26;
+ speed[2] = 100;
+
+ base[3] = 0xF0100500UL;
+ irq[3] = 26;
+ speed[3] = 500;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ unsigned int gw2_regs = num_regs * (ADDR_GAP + 1);
+
+ /* creating the region for IOMEM is pretty easy */
+ if (!request_mem_region(base[dev_num], gw2_regs, drv_name))
+ return 0; /* failed */
+
+ /* set device base_addr */
+ rbase[dev_num] = (unsigned long)ioremap(base[dev_num], gw2_regs);
+
+ if (rbase[dev_num])
+ return 1; /* success */
+
+ /* cleanup due to failed ioremap() */
+ release_mem_region(base[dev_num], gw2_regs);
+ return 0; /* failed */
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ unsigned int gw2_regs = num_regs * (ADDR_GAP + 1);
+
+ iounmap((void *)rbase[dev_num]);
+ release_mem_region(base[dev_num], gw2_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ static u8 val;
+ void __iomem *addr = (void __iomem *)base +
+ reg * (ADDR_GAP + 1) + ADDR_GAP;
+
+ val = (u8)readw(addr);
+ rmb();
+
+ return val;
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ void __iomem *addr = (void __iomem *)base +
+ reg * (ADDR_GAP + 1) + ADDR_GAP;
+
+ writew(val, addr);
+ wmb();
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
diff --git a/drivers/net/can/old/hal/hal.h b/drivers/net/can/old/hal/hal.h
new file mode 100644
index 000000000000..1c59b54cdb6a
--- /dev/null
+++ b/drivers/net/can/old/hal/hal.h
@@ -0,0 +1,99 @@
+/*
+ * hal.h - definitions for CAN controller hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_HAL_H
+#define CAN_HAL_H
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+
+/* Number of supported CAN devices for each HAL (default) */
+#define MAXDEV 8
+
+/* general function prototypes for CAN HAL */
+
+/* init the HAL - call at driver module init */
+int hal_init(void);
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void);
+
+/* get name of this CAN HAL */
+char *hal_name(void);
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void);
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name);
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs);
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num);
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num);
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num);
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg);
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val);
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev);
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev);
+
+#endif /* CAN_HAL_H */
diff --git a/drivers/net/can/old/hal/io.c b/drivers/net/can/old/hal/io.c
new file mode 100644
index 000000000000..85e3b1edc1bc
--- /dev/null
+++ b/drivers/net/can/old/hal/io.c
@@ -0,0 +1,123 @@
+/*
+ * io.c - linear register access CAN hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "io"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x2C0UL;
+ irq[0] = 10;
+
+ base[1] = 0x320UL;
+ irq[1] = 5;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* creating the region for IO is pretty easy */
+ return (request_region(base[dev_num], num_regs, drv_name))? 1 : 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ release_region(base[dev_num], num_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ return inb(base + reg);
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(val, base + reg);
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
diff --git a/drivers/net/can/old/hal/iomem.c b/drivers/net/can/old/hal/iomem.c
new file mode 100644
index 000000000000..6a89a9cc2fe0
--- /dev/null
+++ b/drivers/net/can/old/hal/iomem.c
@@ -0,0 +1,142 @@
+/*
+ * iomem.c - linear register access CAN hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "iomem"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0xd8000UL;
+ irq[0] = 5;
+
+ base[1] = 0xd8100UL;
+ irq[1] = 15;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* creating the region for IOMEM is pretty easy */
+ if (!request_mem_region(base[dev_num], num_regs, drv_name))
+ return 0; /* failed */
+
+ /* set device base_addr */
+ rbase[dev_num] = (unsigned long)ioremap(base[dev_num], num_regs);
+
+ if (rbase[dev_num])
+ return 1; /* success */
+
+ /* cleanup due to failed ioremap() */
+ release_mem_region(base[dev_num], num_regs);
+ return 0; /* failed */
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ iounmap((void *)rbase[dev_num]);
+ release_mem_region(base[dev_num], num_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ static u8 val;
+ void __iomem *addr = (void __iomem *)base + reg;
+
+ val = (u8)readb(addr);
+ rmb();
+
+ return val;
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ void __iomem *addr = (void __iomem *)base + reg;
+
+ writeb(val, addr);
+ wmb();
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
diff --git a/drivers/net/can/old/hal/iomux.c b/drivers/net/can/old/hal/iomux.c
new file mode 100644
index 000000000000..a4e8fba82ddd
--- /dev/null
+++ b/drivers/net/can/old/hal/iomux.c
@@ -0,0 +1,125 @@
+/*
+ * iomux.c - multiplex register access CAN hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "iomux"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x300UL;
+ irq[0] = 5;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base / data register = base + 1 */
+ return (request_region(base[dev_num], 2, drv_name))? 1 : 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ /* ignore num_regs and create the 2 register region: */
+ /* address register = base / data register = base + 1 */
+ release_region(base[dev_num], 2);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) { return 0; }
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ outb(reg, base); /* address */
+ return inb(base + 1); /* data */
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(reg, base); /* address */
+ outb(val, base + 1); /* data */
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
diff --git a/drivers/net/can/old/hal/pc7io.c b/drivers/net/can/old/hal/pc7io.c
new file mode 100644
index 000000000000..b223efd25f9f
--- /dev/null
+++ b/drivers/net/can/old/hal/pc7io.c
@@ -0,0 +1,126 @@
+/*
+ * pc7io.c - linear register access CAN hardware abstraction layer
+ *
+ * Inspired by the OCAN driver http://ar.linux.it/software/#ocan
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include "hal.h"
+
+/* init the HAL - call at driver module init */
+int hal_init(void) { return 0; }
+
+/* exit the HAL - call at driver module exit */
+int hal_exit(void) { return 0; }
+
+/* get name of this CAN HAL */
+char *hal_name(void) { return "pc7io"; }
+
+/* fill arrays base[] and irq[] with HAL specific defaults */
+void hal_use_defaults(void)
+{
+ extern unsigned long base[];
+ extern unsigned int irq[];
+
+ base[0] = 0x1000UL;
+ irq[0] = 9;
+}
+
+/* request controller register access space */
+int hal_request_region(int dev_num,
+ unsigned int num_regs,
+ char *drv_name)
+{
+ extern unsigned long base[];
+ extern unsigned long rbase[];
+
+ /* set for device base_addr */
+ rbase[dev_num] = base[dev_num];
+
+ /* creating the region for IO is pretty easy */
+ return (request_region(base[dev_num], num_regs, drv_name))? 1 : 0;
+}
+
+/* release controller register access space */
+void hal_release_region(int dev_num,
+ unsigned int num_regs)
+{
+ extern unsigned long base[];
+
+ release_region(base[dev_num], num_regs);
+}
+
+/* enable non controller hardware (e.g. irq routing, etc.) */
+int hw_attach(int dev_num) {
+
+ /* Unlock special function register */
+ outb(5, 0x169);
+
+ return 0;
+}
+
+/* disable non controller hardware (e.g. irq routing, etc.) */
+int hw_detach(int dev_num) { return 0; }
+
+/* reset controller hardware (with specific non controller hardware) */
+int hw_reset_dev(int dev_num) { return 0; }
+
+/* read from controller register */
+u8 hw_readreg(unsigned long base, int reg) {
+
+ return inb(base + reg);
+}
+
+/* write to controller register */
+void hw_writereg(unsigned long base, int reg, u8 val) {
+
+ outb(val, base + reg);
+}
+
+/* hardware specific work to do at start of irq handler */
+void hw_preirq(struct net_device *dev) { return; }
+
+/* hardware specific work to do at end of irq handler */
+void hw_postirq(struct net_device *dev) { return; }
+
diff --git a/drivers/net/can/old/i82527/Makefile b/drivers/net/can/old/i82527/Makefile
new file mode 100644
index 000000000000..eb025a8a10c5
--- /dev/null
+++ b/drivers/net/can/old/i82527/Makefile
@@ -0,0 +1,24 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/old/hal
+
+obj-m := i82527-pc7io.o i82527-iomem.o i82527-esdio.o
+
+i82527-pc7io-objs := i82527.o proc.o ../hal/pc7io.o
+i82527-iomem-objs := i82527.o proc.o ../hal/iomem.o
+i82527-esdio-objs := i82527.o proc.o ../hal/esdio.o
+
+endif
diff --git a/drivers/net/can/old/i82527/i82527.c b/drivers/net/can/old/i82527/i82527.c
new file mode 100644
index 000000000000..d45c4fab22de
--- /dev/null
+++ b/drivers/net/can/old/i82527/i82527.c
@@ -0,0 +1,1251 @@
+/*
+ * i82527.c - Intel I82527 network device driver
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h> /* for struct can_device_stats */
+#include "hal.h"
+#include "i82527.h"
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("LLCF/socketcan '" CHIP_NAME "' network device driver");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) ((priv->debug > 0) ? printk(args) : 0)
+/* logging in interrupt context! */
+#define iDBG(args...) ((priv->debug > 1) ? printk(args) : 0)
+#define iiDBG(args...) ((priv->debug > 2) ? printk(args) : 0)
+#else
+#define DBG(args...)
+#define iDBG(args...)
+#define iiDBG(args...)
+#endif
+
+char drv_name[DRV_NAME_LEN] = "undefined";
+
+/* driver and version information */
+static const char *drv_version = "0.0.4";
+static const char *drv_reldate = "2007-08-03";
+
+static const canid_t rxobjflags[] = {0, CAN_EFF_FLAG,
+ CAN_RTR_FLAG, CAN_RTR_FLAG | CAN_EFF_FLAG,
+ 0, CAN_EFF_FLAG};
+#define RXOBJBASE 10
+
+/* array of all can chips */
+struct net_device *can_dev[MAXDEV];
+
+/* module parameters */
+unsigned long base[MAXDEV] = { 0 }; /* hardware address */
+unsigned long rbase[MAXDEV] = { 0 }; /* (remapped) device address */
+unsigned int irq[MAXDEV] = { 0 };
+
+unsigned int speed[MAXDEV] = { DEFAULT_SPEED, DEFAULT_SPEED };
+unsigned int btr[MAXDEV] = { 0 };
+unsigned int bcr[MAXDEV] = { 0 }; /* bus configuration register */
+unsigned int cdv[MAXDEV] = { 0 }; /* CLKOUT clock divider */
+unsigned int mo15[MAXDEV] = { MO15_DEFLT, MO15_DEFLT }; /* msg obj 15 */
+
+static int rx_probe[MAXDEV] = { 0 };
+static int clk = DEFAULT_HW_CLK;
+static int force_dmc = DEFAULT_FORCE_DMC;
+static int irq_mode = DEFAULT_IRQ_MODE;
+static int debug = 0;
+static int restart_ms = 100;
+
+static int base_n;
+static int irq_n;
+static int speed_n;
+static int btr_n;
+static int bcr_n;
+static int cdv_n;
+static int mo15_n;
+static int rx_probe_n;
+
+static u8 dsc; /* devide system clock */
+static u8 dmc; /* devide memory clock */
+static unsigned long irqflags; /* for shared / disabled local interrupts */
+
+module_param_array(base, int, &base_n, 0);
+module_param_array(irq, int, &irq_n, 0);
+module_param_array(speed, int, &speed_n, 0);
+module_param_array(btr, int, &btr_n, 0);
+module_param_array(bcr, int, &bcr_n, 0);
+module_param_array(cdv, int, &cdv_n, 0);
+module_param_array(mo15, int, &mo15_n, 0);
+module_param_array(rx_probe, int, &rx_probe_n, 0);
+
+module_param(clk, int, 0);
+module_param(force_dmc, int, 0);
+module_param(irq_mode, int, 0);
+module_param(debug, int, 0);
+module_param(restart_ms, int, 0);
+
+MODULE_PARM_DESC(base, "CAN controller base address");
+MODULE_PARM_DESC(irq, "CAN controller interrupt");
+MODULE_PARM_DESC(speed, "CAN bus bitrate");
+MODULE_PARM_DESC(btr, "Bit Timing Register value 0x<btr0><btr1>, e.g. 0x4014");
+MODULE_PARM_DESC(bcr, "i82527 bus configuration register value (default: 0)");
+MODULE_PARM_DESC(cdv, "clockout devider value (0-14) (default: 0)");
+MODULE_PARM_DESC(mo15, "rx message object 15 usage. 0:none 1:sff(default) 2:eff");
+MODULE_PARM_DESC(rx_probe, "switch to trx mode after correct msg receiption. (default off)");
+
+MODULE_PARM_DESC(clk, "CAN controller chip clock (default: 16MHz)");
+MODULE_PARM_DESC(force_dmc, "set i82527 DMC bit (default: calculate from clk)");
+MODULE_PARM_DESC(irq_mode, "specify irq setup bits (1:shared 2:disable local irqs while processing) (default: 1)");
+MODULE_PARM_DESC(debug, "set debug mask (default: 0)");
+MODULE_PARM_DESC(restart_ms, "restart chip on heavy bus errors / bus off after x ms (default 100ms)");
+
+/* function declarations */
+
+static void chipset_init(struct net_device *dev, int wake);
+static void chipset_init_rx(struct net_device *dev);
+static void chipset_init_trx(struct net_device *dev);
+static void can_netdev_setup(struct net_device *dev);
+static struct net_device* can_create_netdev(int dev_num, int hw_regs);
+static int can_set_drv_name(void);
+int set_reset_mode(struct net_device *dev);
+
+static int i82527_probe_chip(unsigned long base)
+{
+ // Check if hardware reset is still inactive OR
+ // maybe there is no chip in this address space
+ if (CANin(base, cpuInterfaceReg) & iCPU_RST) {
+ printk(KERN_INFO "%s: probing @ 0x%lX failed (reset)\n",
+ drv_name, base);
+ return 0;
+ }
+
+ // Write test pattern
+ CANout(base, message1Reg.dataReg[1], 0x25);
+ CANout(base, message2Reg.dataReg[3], 0x52);
+ CANout(base, message10Reg.dataReg[6], 0xc3);
+
+ // Read back test pattern
+ if ((CANin(base, message1Reg.dataReg[1]) != 0x25 ) ||
+ (CANin(base, message2Reg.dataReg[3]) != 0x52 ) ||
+ (CANin(base, message10Reg.dataReg[6]) != 0xc3 )) {
+ printk(KERN_INFO "%s: probing @ 0x%lX failed (pattern)\n",
+ drv_name, base);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * set baud rate divisor values
+ */
+static void set_btr(struct net_device *dev, int btr0, int btr1)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ /* no bla bla when restarting the device */
+ if (priv->state == STATE_UNINITIALIZED)
+ printk(KERN_INFO "%s: setting BTR0=%02X BTR1=%02X\n",
+ dev->name, btr0, btr1);
+
+ CANout(base, bitTiming0Reg, btr0);
+ CANout(base, bitTiming1Reg, btr1);
+}
+
+/*
+ * calculate baud rate divisor values
+ */
+static void set_baud(struct net_device *dev, int baud, int clock)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ int error;
+ int brp;
+ int tseg;
+ int tseg1 = 0;
+ int tseg2 = 0;
+
+ int best_error = 1000000000;
+ int best_tseg = 0;
+ int best_brp = 0;
+ int best_baud = 0;
+
+ int SAM = (baud > 100000 ? 0 : 1);
+
+ if (dsc) /* devide system clock */
+ clock >>= 1; /* calculate BTR with this value */
+
+ for (tseg = (0 + 0 + 2) * 2;
+ tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1;
+ tseg++) {
+ brp = clock / ((1 + tseg / 2) * baud) + tseg % 2;
+ if ((brp > 0) && (brp <= 64)) {
+ error = baud - clock / (brp * (1 + tseg / 2));
+ if (error < 0) {
+ error = -error;
+ }
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg / 2;
+ best_brp = brp - 1;
+ best_baud = clock / (brp * (1 + tseg / 2));
+ }
+ }
+ }
+ if (best_error && (baud / best_error < 10)) {
+ printk("%s: unable to set baud rate %d (ext clock %dHz)\n",
+ dev->name, baud, clock * 2);
+ return;
+// return -EINVAL;
+ }
+ tseg2 = best_tseg - (SAMPLE_POINT * (best_tseg + 1)) / 100;
+ if (tseg2 < 0) {
+ tseg2 = 0;
+ } else if (tseg2 > MAX_TSEG2) {
+ tseg2 = MAX_TSEG2;
+ }
+ tseg1 = best_tseg - tseg2 - 2;
+ if (tseg1 > MAX_TSEG1) {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg - tseg1 - 2;
+ }
+
+ priv->btr = ((best_brp | JUMPWIDTH)<<8) +
+ ((SAM << 7) | (tseg2 << 4) | tseg1);
+
+ printk(KERN_INFO "%s: calculated best baudrate: %d / btr is 0x%04X\n",
+ dev->name, best_baud, priv->btr);
+
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+// set_btr(dev, best_brp | JUMPWIDTH, (SAM << 7) | (tseg2 << 4) | tseg1);
+}
+
+static inline int obj2rxo(int obj)
+{
+ /* obj4 = obj15 SFF, obj5 = obj15 EFF */
+ if (obj < 4)
+ return RXOBJBASE + obj;
+ else
+ return 15;
+}
+
+void enable_rx_obj(unsigned long base, int obj)
+{
+ u8 mcfg = 0;
+ int rxo = obj2rxo(obj);
+
+ // Configure message object for receiption
+ if (rxobjflags[obj] & CAN_EFF_FLAG)
+ mcfg = MCFG_XTD;
+
+ if (rxobjflags[obj] & CAN_RTR_FLAG) {
+ CANout(base, msgArr[rxo].messageReg.messageConfigReg,
+ mcfg | MCFG_DIR);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl0Reg,
+ MVAL_SET | TXIE_RES | RXIE_SET | INTPD_RES);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | CPUU_SET | TXRQ_RES | RMPD_RES);
+ } else {
+ CANout(base, msgArr[rxo].messageReg.messageConfigReg, mcfg);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl0Reg,
+ MVAL_SET | TXIE_RES | RXIE_SET | INTPD_RES);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_RES | RMPD_RES);
+ }
+}
+
+void disable_rx_obj(unsigned long base, int obj)
+{
+ int rxo = obj2rxo(obj);
+
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_RES | RMPD_RES);
+ CANout(base, msgArr[rxo].messageReg.msgCtrl0Reg,
+ MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES);
+}
+
+int set_reset_mode(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ // Configure cpu interface
+ CANout(base, cpuInterfaceReg,(dsc | dmc | iCPU_CEN));
+
+ // Enable configuration and puts chip in bus-off, disable interrupts
+ CANout(base, controlReg, iCTL_CCE | iCTL_INI);
+
+ // Clear interrupts
+ CANin(base, interruptReg);
+
+ // Clear status register
+ CANout(base, statusReg, 0);
+
+ // Clear message objects for receiption
+ if (priv->mo15 == MO15_SFF)
+ disable_rx_obj(base, 4); /* rx via obj15 SFF */
+ else
+ disable_rx_obj(base, 0); /* rx via obj10 SFF */
+
+ if (priv->mo15 == MO15_EFF)
+ disable_rx_obj(base, 5); /* rx via obj15 EFF */
+ else
+ disable_rx_obj(base, 1); /* rx via obj11 EFF */
+
+ disable_rx_obj(base, 2);
+ disable_rx_obj(base, 3);
+
+ // Clear message object for send
+ CANout(base, message1Reg.msgCtrl1Reg,
+ RMPD_RES | TXRQ_RES | CPUU_RES | NEWD_RES);
+ CANout(base, message1Reg.msgCtrl0Reg,
+ MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+
+ return 0;
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ // Clear interrupts
+ CANin(base, interruptReg);
+
+ // Clear status register
+ CANout(base, statusReg, 0);
+
+ // Configure message objects for receiption
+ if (priv->mo15 == MO15_SFF) {
+ enable_rx_obj(base, 4); /* rx via obj15 SFF */
+ printk(KERN_INFO "%s: %s: using msg object 15 for "
+ "SFF receiption.\n",
+ dev->name, CHIP_NAME);
+ } else
+ enable_rx_obj(base, 0); /* rx via obj10 SFF */
+
+ if (priv->mo15 == MO15_EFF) {
+ enable_rx_obj(base, 5); /* rx via obj15 EFF */
+ printk(KERN_INFO "%s: %s: using msg object 15 for "
+ "EFF receiption.\n",
+ dev->name, CHIP_NAME);
+ } else
+ enable_rx_obj(base, 1); /* rx via obj11 EFF */
+
+ enable_rx_obj(base, 2);
+ enable_rx_obj(base, 3);
+
+ // Clear message object for send
+ CANout(base, message1Reg.msgCtrl1Reg,
+ RMPD_RES | TXRQ_RES | CPUU_RES | NEWD_RES);
+ CANout(base, message1Reg.msgCtrl0Reg,
+ MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES);
+
+ return 0;
+}
+
+static int set_listen_mode(struct net_device *dev)
+{
+ return set_normal_mode(dev); /* for now */
+}
+
+/*
+ * Clear and invalidate message objects
+ */
+int i82527_clear_msg_objects(unsigned long base)
+{
+ int i;
+ int id;
+ int data;
+
+ for (i = 1; i <= 15; i++) {
+ CANout(base, msgArr[i].messageReg.msgCtrl0Reg,
+ INTPD_UNC | RXIE_RES | TXIE_RES | MVAL_RES);
+ CANout(base, msgArr[i].messageReg.msgCtrl0Reg,
+ INTPD_RES | RXIE_RES | TXIE_RES | MVAL_RES);
+ CANout(base, msgArr[i].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_RES | RMPD_RES);
+ for (data = 0; data < 8; data++)
+ CANout(base, msgArr[i].messageReg.dataReg[data], 0);
+ for (id = 0; id < 4; id++)
+ CANout(base, msgArr[i].messageReg.idReg[id], 0);
+ CANout(base, msgArr[i].messageReg.messageConfigReg, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * initialize I82527 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init_regs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ // Enable configuration and puts chip in bus-off, disable interrupts
+ CANout(base, controlReg, (iCTL_CCE | iCTL_INI));
+
+ // Set CLKOUT devider and slew rates is was done in i82527_init_module
+
+ // Bus configuration was done in i82527_init_module
+
+ // Clear interrupts
+ CANin(base, interruptReg);
+
+ // Clear status register
+ CANout(base, statusReg, 0);
+
+ i82527_clear_msg_objects(base);
+
+ // Set all global ID masks to "don't care"
+ CANout(base, globalMaskStandardReg[0], 0);
+ CANout(base, globalMaskStandardReg[1], 0);
+ CANout(base, globalMaskExtendedReg[0], 0);
+ CANout(base, globalMaskExtendedReg[1], 0);
+ CANout(base, globalMaskExtendedReg[2], 0);
+ CANout(base, globalMaskExtendedReg[3], 0);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+
+ // Note: At this stage the CAN ship is still in bus-off condition
+ // and must be started using StartChip()
+
+ /* set baudrate */
+ if (priv->btr) { /* no calculation when btr is provided */
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+ } else {
+ if (priv->speed == 0) {
+ priv->speed = DEFAULT_SPEED;
+ }
+ set_baud(dev, priv->speed * 1000, priv->clock);
+ }
+
+}
+
+static void chipset_init(struct net_device *dev, int wake)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->rx_probe)
+ chipset_init_rx(dev); /* wait for valid reception first */
+ else
+ chipset_init_trx(dev);
+
+ if ((wake) && netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+}
+
+static void chipset_init_rx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* automatic bit rate detection */
+ set_listen_mode(dev);
+
+ priv->state = STATE_PROBE;
+
+ // Clear bus-off, Interrupts only for errors, not for status change
+ CANout(base, controlReg, iCTL_IE | iCTL_EIE);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+}
+
+static void chipset_init_trx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+
+ priv->state = STATE_ACTIVE;
+
+ // Clear bus-off, Interrupts only for errors, not for status change
+ CANout(base, controlReg, iCTL_IE | iCTL_EIE);
+
+ DBG(KERN_INFO "%s: %s: CtrlReg 0x%x CPUifReg 0x%x\n",
+ dev->name, __FUNCTION__,
+ CANin(base, controlReg), CANin(base, cpuInterfaceReg));
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [len] [can data (up to 8 bytes]
+ */
+static int can_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf = (struct can_frame*)skb->data;
+ unsigned long base = dev->base_addr;
+ uint8_t dlc;
+ uint8_t rtr;
+ canid_t id;
+ int i;
+
+ if ((CANin(base, message1Reg.msgCtrl1Reg) & TXRQ_UNC) == TXRQ_SET) {
+ printk(KERN_ERR "%s: %s: TX register is occupied!\n",
+ dev->name, drv_name);
+ return 0;
+ }
+
+ netif_stop_queue(dev);
+
+ dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if ( cf->can_id & CAN_RTR_FLAG )
+ rtr = 0;
+ else
+ rtr = MCFG_DIR;
+
+ CANout(base, message1Reg.msgCtrl1Reg,
+ RMPD_RES | TXRQ_RES | CPUU_SET | NEWD_RES);
+ CANout(base, message1Reg.msgCtrl0Reg,
+ MVAL_SET | TXIE_SET | RXIE_RES | INTPD_RES);
+
+ if (id & CAN_EFF_FLAG) {
+ id &= CAN_EFF_MASK;
+ CANout(base, message1Reg.messageConfigReg,
+ (dlc << 4) + rtr + MCFG_XTD);
+ CANout(base, message1Reg.idReg[3], (id << 3) & 0xFFU);
+ CANout(base, message1Reg.idReg[2], (id >> 5) & 0xFFU);
+ CANout(base, message1Reg.idReg[1], (id >> 13) & 0xFFU);
+ CANout(base, message1Reg.idReg[0], (id >> 21) & 0xFFU);
+ }
+ else {
+ id &= CAN_SFF_MASK;
+ CANout(base, message1Reg.messageConfigReg,
+ ( dlc << 4 ) + rtr);
+ CANout(base, message1Reg.idReg[0], (id >> 3) & 0xFFU);
+ CANout(base, message1Reg.idReg[1], (id << 5) & 0xFFU);
+ }
+
+ dlc &= 0x0f; //restore length only
+ for ( i=0; i < dlc; i++ ) {
+ CANout(base, message1Reg.dataReg[i],
+ cf->data[i]);
+ }
+
+ CANout(base, message1Reg.msgCtrl1Reg,
+ (RMPD_RES | TXRQ_SET | CPUU_RES | NEWD_UNC));
+
+ // HM: We had some cases of repeated IRQs
+ // so make sure the INT is acknowledged
+ // I know it's already further up, but doing again fixed the issue
+ CANout(base, message1Reg.msgCtrl0Reg,
+ (MVAL_UNC | TXIE_UNC | RXIE_UNC | INTPD_RES));
+
+ stats->tx_bytes += dlc;
+
+ dev->trans_start = jiffies;
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void can_tx_timeout(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+
+ stats->tx_errors++;
+
+ /* do not conflict with e.g. bus error handling */
+ if (!(priv->timer.expires)){ /* no restart on the run */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ netif_wake_queue(dev); /* wakeup here */
+ }
+ else
+ DBG(KERN_INFO "%s: %s: can_restart_dev already active.\n",
+ dev->name, __FUNCTION__ );
+
+}
+
+# if 0
+static void can_restart_on(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (!(priv->timer.expires)){ /* no restart on the run */
+
+ set_reset_mode(dev);
+
+ priv->timer.function = can_restart_dev;
+ priv->timer.data = (unsigned long) dev;
+
+ /* restart chip on persistent error in <xxx> ms */
+ priv->timer.expires = jiffies + (priv->restart_ms * HZ) / 1000;
+ add_timer(&priv->timer);
+
+ iDBG(KERN_INFO "%s: %s start (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+ } else
+ iDBG(KERN_INFO "%s: %s already (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+}
+
+static void can_restart_dev(unsigned long data)
+{
+ struct net_device *dev = (struct net_device*) data;
+ struct can_priv *priv = netdev_priv(dev);
+
+ DBG(KERN_INFO "%s: can_restart_dev (%ld)\n",
+ dev->name, jiffies);
+
+ /* mark inactive timer */
+ priv->timer.expires = 0;
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+#endif
+
+#if 0
+/* the timerless version */
+
+static void can_restart_now(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+#endif
+
+/*
+ * Subroutine of ISR for RX interrupts.
+ *
+ */
+static void can_rx(struct net_device *dev, int obj)
+{
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long base = dev->base_addr;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t msgctlreg;
+ uint8_t ctl1reg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+ int rxo = obj2rxo(obj);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL) {
+ return;
+ }
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ ctl1reg = CANin(base, msgArr[rxo].messageReg.msgCtrl1Reg);
+ msgctlreg = CANin(base, msgArr[rxo].messageReg.messageConfigReg);
+
+ if( msgctlreg & MCFG_XTD ) {
+ id = CANin(base, msgArr[rxo].messageReg.idReg[3])
+ | (CANin(base, msgArr[rxo].messageReg.idReg[2]) << 8)
+ | (CANin(base, msgArr[rxo].messageReg.idReg[1]) << 16)
+ | (CANin(base, msgArr[rxo].messageReg.idReg[0]) << 24);
+ id >>= 3;
+ id |= CAN_EFF_FLAG;
+ } else {
+ id = CANin(base, msgArr[rxo].messageReg.idReg[1])
+ |(CANin(base, msgArr[rxo].messageReg.idReg[0]) << 8);
+ id >>= 5;
+ }
+
+ if (ctl1reg & RMPD_SET) {
+ id |= CAN_RTR_FLAG;
+ }
+
+ msgctlreg &= 0xf0;/* strip length code */
+ dlc = msgctlreg >> 4;
+ dlc %= 9; /* limit count to 8 bytes */
+
+ cf = (struct can_frame*)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++) {
+ cf->data[i] = CANin(base, msgArr[rxo].messageReg.dataReg[i]);
+ }
+
+ // Make the chip ready to receive the next message
+ enable_rx_obj(base, obj);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+/*
+ * I82527 interrupt handler
+ */
+static irqreturn_t can_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device*)dev_id;
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long base = dev->base_addr;
+ uint8_t irqreg;
+ uint8_t lastIrqreg;
+ int n = 0;
+
+ hw_preirq(dev);
+
+ iiDBG(KERN_INFO "%s: interrupt\n", dev->name);
+
+ if (priv->state == STATE_UNINITIALIZED) {
+ printk(KERN_ERR "%s: %s: uninitialized controller!\n",
+ dev->name, __FUNCTION__);
+ //chipset_init(dev, 1); /* should be possible at this stage */
+ return IRQ_NONE;
+ }
+
+ if (priv->state == STATE_RESET_MODE) {
+ iiDBG(KERN_ERR "%s: %s: controller is in reset mode!\n",
+ dev->name, __FUNCTION__);
+ return IRQ_NONE;
+ }
+
+
+ // Read the highest pending interrupt request
+ irqreg = CANin(base, interruptReg);
+ lastIrqreg = irqreg;
+
+ while ( irqreg ) {
+ n++;
+ switch (irqreg) {
+
+ case 1: // Status register
+ {
+ uint8_t status;
+
+ // Read the STATUS reg
+ status = CANin(base, statusReg);
+ CANout (base, statusReg, 0);
+
+ if ( status & iSTAT_RXOK ) {
+ // Intel: Software must clear this bit in ISR
+ CANout (base, statusReg, status & ~iSTAT_RXOK);
+ }
+ if ( status & iSTAT_TXOK ) {
+ // Intel: Software must clear this bit in ISR
+ CANout (base, statusReg, status & ~iSTAT_TXOK);
+ }
+ if ( status & iSTAT_WARN ) {
+ // Note: status bit is read-only, don't clear
+ /* error warning interrupt */
+ iDBG(KERN_INFO "%s: error warning\n",
+ dev->name);
+ priv->can_stats.error_warning++;
+ }
+ if ( status & iSTAT_BOFF ) {
+ uint8_t flags;
+
+ // Note: status bit is read-only, don't clear
+
+ priv->can_stats.bus_error++;
+
+ // Clear init flag and reenable interrupts
+ flags = CANin(base, controlReg) |
+ ( iCTL_IE | iCTL_EIE );
+
+ flags &= ~iCTL_INI; // Reset init flag
+ CANout(base, controlReg, flags);
+ }
+ }
+ break;
+
+ case 0x2: // Receiption, message object 15
+ {
+ uint8_t ctl1reg;
+
+ ctl1reg = CANin(base, message15Reg.msgCtrl1Reg);
+ while (ctl1reg & NEWD_SET) {
+ if (ctl1reg & MLST_SET)
+ priv->can_stats.data_overrun++;
+
+ if (priv->mo15 == MO15_SFF)
+ can_rx(dev, 4); /* rx via obj15 SFF */
+ else
+ can_rx(dev, 5); /* rx via obj15 EFF */
+
+ ctl1reg = CANin(base, message15Reg.msgCtrl1Reg);
+ }
+
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ break;
+
+ case 0xC: // Receiption, message object 10
+ case 0xD: // Receiption, message object 11
+ {
+ int obj = irqreg - 0xC;
+ int rxo = obj2rxo(obj);
+ uint8_t ctl1reg;
+ ctl1reg = CANin(base, msgArr[rxo].messageReg.msgCtrl1Reg);
+ while (ctl1reg & NEWD_SET) {
+ if (ctl1reg & MLST_SET)
+ priv->can_stats.data_overrun++;
+ CANout(base, msgArr[rxo].messageReg.msgCtrl1Reg,
+ NEWD_RES | MLST_RES | TXRQ_UNC | RMPD_UNC);
+ can_rx(dev, obj);
+ ctl1reg = CANin(base,
+ msgArr[rxo].messageReg.msgCtrl1Reg);
+ }
+
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ break;
+
+ case 0xE: // Receiption, message object 12 (RTR)
+ case 0xF: // Receiption, message object 13 (RTR)
+ {
+ int obj = irqreg - 0xC;
+ int rxo = obj2rxo(obj);
+ uint8_t ctl0reg;
+ ctl0reg = CANin(base, msgArr[rxo].messageReg.msgCtrl0Reg);
+ while (ctl0reg & INTPD_SET) {
+ can_rx(dev, obj);
+ ctl0reg = CANin(base, msgArr[rxo].messageReg.msgCtrl0Reg);
+ }
+
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ break;
+
+ case 3: // Message object 1 (our write object)
+ /* transmission complete interrupt */
+
+ // Nothing more to send, switch off interrupts
+ CANout(base, message1Reg.msgCtrl0Reg,
+ (MVAL_RES | TXIE_RES | RXIE_RES | INTPD_RES));
+ // We had some cases of repeated IRQ
+ // so make sure the INT is acknowledged
+ CANout(base, message1Reg.msgCtrl0Reg,
+ (MVAL_UNC | TXIE_UNC | RXIE_UNC | INTPD_RES));
+
+ stats->tx_packets++;
+ netif_wake_queue(dev);
+ break;
+
+ default: // Unexpected
+ iDBG(KERN_INFO "%s: Unexpected i82527 interrupt: "
+ "irqreq=0x%X\n", dev->name, irqreg);
+ break;
+ }
+
+ // Get irq status again for next loop iteration
+ irqreg = CANin(base, interruptReg);
+ if (irqreg == lastIrqreg)
+ iDBG(KERN_INFO "%s: i82527 interrupt repeated: "
+ "irqreq=0x%X\n", dev->name, irqreg);
+
+ lastIrqreg = irqreg;
+ } /* end while (irqreq) */
+
+ if (n > 1) {
+ iDBG(KERN_INFO "%s: handled %d IRQs\n", dev->name, n);
+ }
+
+ hw_postirq(dev);
+
+ return n == 0 ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/*
+ * initialize CAN bus driver
+ */
+static int can_open(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->state = STATE_UNINITIALIZED;
+
+ /* register interrupt handler */
+ if (request_irq(dev->irq, &can_interrupt, irqflags,
+ dev->name, (void*)dev))
+ return -EAGAIN;
+
+ /* init chip */
+ chipset_init(dev, 0);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * stop CAN bus activity
+ */
+static int can_close(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->open_time = 0;
+
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0;
+ }
+
+ free_irq(dev->irq, (void*)dev);
+ priv->state = STATE_UNINITIALIZED;
+
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+void can_netdev_setup(struct net_device *dev)
+{
+ /* Fill in the the fields of the device structure
+ with CAN netdev generic values */
+
+ dev->change_mtu = NULL;
+ dev->set_mac_address = NULL;
+ dev->header_ops = NULL;
+
+ dev->type = ARPHRD_CAN;
+ dev->hard_header_len = 0;
+ dev->mtu = sizeof(struct can_frame);
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+
+ dev->open = can_open;
+ dev->stop = can_close;
+ dev->hard_start_xmit = can_start_xmit;
+
+ dev->tx_timeout = can_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+}
+
+static struct net_device* can_create_netdev(int dev_num, int hw_regs)
+{
+ struct net_device *dev;
+ struct can_priv *priv;
+
+ const char mo15mode [3][6] = {"none", "sff", "eff"};
+
+ if (!(dev = alloc_netdev(sizeof(struct can_priv), CAN_NETDEV_NAME,
+ can_netdev_setup))) {
+ printk(KERN_ERR "%s: out of memory\n", CHIP_NAME);
+ return NULL;
+ }
+
+ printk(KERN_INFO "%s: base 0x%lX / irq %d / speed %d / "
+ "btr 0x%X / rx_probe %d / mo15 %s\n",
+ drv_name, rbase[dev_num], irq[dev_num],
+ speed[dev_num], btr[dev_num], rx_probe[dev_num],
+ mo15mode[mo15[dev_num]]);
+
+ /* fill net_device structure */
+
+ priv = netdev_priv(dev);
+
+ dev->irq = irq[dev_num];
+ dev->base_addr = rbase[dev_num];
+
+ priv->speed = speed[dev_num];
+ priv->btr = btr[dev_num];
+ priv->rx_probe = rx_probe[dev_num];
+ priv->mo15 = mo15[dev_num];
+ priv->clock = clk;
+ priv->hw_regs = hw_regs;
+ priv->restart_ms = restart_ms;
+ priv->debug = debug;
+
+ init_timer(&priv->timer);
+ priv->timer.expires = 0;
+
+ if (register_netdev(dev)) {
+ printk(KERN_INFO "%s: register netdev failed\n", CHIP_NAME);
+ free_netdev(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+int can_set_drv_name(void)
+{
+ char *hname = hal_name();
+
+ if (strlen(CHIP_NAME) + strlen(hname) >= DRV_NAME_LEN-1) {
+ printk(KERN_ERR "%s: driver name too long!\n", CHIP_NAME);
+ return -EINVAL;
+ }
+ sprintf(drv_name, "%s-%s", CHIP_NAME, hname);
+ return 0;
+}
+
+static void i82527_exit_module(void)
+{
+ int i, ret;
+
+ for (i = 0; i < MAXDEV; i++) {
+ if (can_dev[i] != NULL) {
+ struct can_priv *priv = netdev_priv(can_dev[i]);
+ unregister_netdev(can_dev[i]);
+ del_timer(&priv->timer);
+ hw_detach(i);
+ hal_release_region(i, I82527_IO_SIZE);
+ free_netdev(can_dev[i]);
+ }
+ }
+ can_proc_remove(drv_name);
+
+ if ((ret = hal_exit()))
+ printk(KERN_INFO "%s: hal_exit error %d.\n", drv_name, ret);
+}
+
+static __init int i82527_init_module(void)
+{
+ int i, ret;
+ struct net_device *dev;
+
+ if ((sizeof(canmessage_t) != 15) || (sizeof(canregs_t) != 256)) {
+ printk(KERN_WARNING "%s sizes: canmessage_t %d canregs_t %d\n",
+ CHIP_NAME, (int)sizeof(canmessage_t),
+ (int)sizeof(canregs_t));
+ return -EBUSY;
+ }
+
+ if ((ret = hal_init()))
+ return ret;
+
+ if ((ret = can_set_drv_name()))
+ return ret;
+
+ if (clk < 1000 ) /* MHz command line value */
+ clk *= 1000000;
+
+ if (clk < 1000000 ) /* kHz command line value */
+ clk *= 1000;
+
+ printk(KERN_INFO "%s driver v%s (%s)\n",
+ drv_name, drv_version, drv_reldate);
+ printk(KERN_INFO "%s - options [clk %d.%06d MHz] [restart_ms %dms]"
+ " [debug %d]\n",
+ drv_name, clk/1000000, clk%1000000, restart_ms, debug);
+ printk(KERN_INFO "%s - options [force_dmc %d] [irq_mode %d]\n",
+ drv_name, force_dmc, irq_mode);
+
+ if (!base[0]) {
+ printk(KERN_INFO "%s: loading defaults.\n", drv_name);
+ hal_use_defaults();
+ }
+
+ /* to ensure the proper access to the i82527 registers */
+ /* the timing dependend settings have to be done first */
+ if (clk > 10000000)
+ dsc = iCPU_DSC; /* devide system clock => MCLK is 8MHz save */
+ else if (clk > 8000000) /* 8MHz < clk <= 10MHz */
+ dmc = iCPU_DMC; /* devide memory clock */
+
+ /* devide memory clock even if it's not needed (regarding the spec) */
+ if (force_dmc)
+ dmc = iCPU_DMC;
+
+ if (irq_mode & IRQ_MODE_SHARED)
+ irqflags |= IRQF_SHARED;
+ if (irq_mode & IRQ_MODE_DISABLE_LOCAL_IRQS)
+ irqflags |= IRQF_DISABLED;
+
+ for (i = 0; base[i]; i++) {
+ int clkout;
+ u8 clockdiv;
+
+ printk(KERN_DEBUG "%s: checking for %s on address 0x%lX ...\n",
+ drv_name, CHIP_NAME, base[i]);
+
+ if (!hal_request_region(i, I82527_IO_SIZE, drv_name)) {
+ printk(KERN_ERR "%s: memory already in use\n",
+ drv_name);
+ i82527_exit_module();
+ return -EBUSY;
+ }
+
+ hw_attach(i);
+ hw_reset_dev(i);
+
+ // Enable configuration, put chip in bus-off, disable ints
+ CANout(rbase[i], controlReg, iCTL_CCE | iCTL_INI);
+
+ // Configure cpu interface / CLKOUT disable
+ CANout(rbase[i], cpuInterfaceReg,(dsc | dmc));
+
+ if (!i82527_probe_chip(rbase[i])) {
+ printk(KERN_ERR "%s: probably missing controller"
+ " hardware\n", drv_name);
+ hw_detach(i);
+ hal_release_region(i, I82527_IO_SIZE);
+ i82527_exit_module();
+ return -ENODEV;
+ }
+
+ /* CLKOUT devider and slew rate calculation */
+ if ((cdv[i] < 0) || (cdv[i] > 14)) {
+ printk(KERN_WARNING "%s: adjusted cdv[%d]=%d to 0.\n",
+ drv_name, i, cdv[i]);
+ cdv[i] = 0;
+ }
+
+ clkout = clk / (cdv[i] + 1); /* CLKOUT frequency */
+ clockdiv = (u8)cdv[i]; /* devider value (see i82527 spec) */
+
+ if (clkout <= 16000000) {
+ clockdiv |= iCLK_SL1;
+ if (clkout <= 8000000)
+ clockdiv |= iCLK_SL0;
+ } else if (clkout <= 24000000)
+ clockdiv |= iCLK_SL0;
+
+ // Set CLKOUT devider and slew rates
+ CANout(rbase[i], clkOutReg, clockdiv);
+
+ // Configure cpu interface / CLKOUT enable
+ CANout(rbase[i], cpuInterfaceReg,(dsc | dmc | iCPU_CEN));
+
+ CANout(rbase[i], busConfigReg, bcr[i]);
+
+ dev = can_create_netdev(i, I82527_IO_SIZE);
+
+ if (dev != NULL) {
+ can_dev[i] = dev;
+ set_reset_mode(dev);
+ can_proc_create(drv_name);
+ } else {
+ can_dev[i] = NULL;
+ hw_detach(i);
+ hal_release_region(i, I82527_IO_SIZE);
+ }
+ }
+ return 0;
+}
+
+module_init(i82527_init_module);
+module_exit(i82527_exit_module);
+
diff --git a/drivers/net/can/old/i82527/i82527.h b/drivers/net/can/old/i82527/i82527.h
new file mode 100644
index 000000000000..0484b21406f9
--- /dev/null
+++ b/drivers/net/can/old/i82527/i82527.h
@@ -0,0 +1,300 @@
+/*
+ * i82527.h - Intel I82527 network device driver
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Original version Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * This software is released under the GPL-License.
+ *
+ * Major Refactoring and Integration into can4linux version 3.1 by
+ * Henrik W Maier of FOCUS Software Engineering Pty Ltd <www.focus-sw.com>
+ *
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef I82527_H
+#define I82527_H
+
+#define I82527_IO_SIZE 0x100
+
+#define CHIP_NAME "i82527"
+
+#define DRV_NAME_LEN 30 /* for "<chip_name>-<hal_name>" */
+
+#define PROCBASE "driver" /* /proc/ ... */
+
+#define DEFAULT_HW_CLK 16000000
+#define DEFAULT_SPEED 500 /* kBit/s */
+#define DEFAULT_FORCE_DMC 0 /* for critical register access, e.g. ser1274 */
+
+#define IRQ_MODE_SHARED 1 /* enable shared interrupts */
+#define IRQ_MODE_DISABLE_LOCAL_IRQS 2 /* when processing the irq handler */
+#define DEFAULT_IRQ_MODE IRQ_MODE_SHARED
+
+/* The message object 15 has a shadow register for reliable data receiption */
+/* under heavy bus load. Therefore it makes sense to use this message object */
+/* (mo15) for the needed use case. The frame type (EFF/SFF) for the mo15 can */
+/* be defined on the module command line. The default is 11 bit SFF format. */
+
+#define MO15_NONE 0
+#define MO15_SFF 1
+#define MO15_EFF 2
+
+#define MO15_DEFLT MO15_SFF /* the default */
+
+#define CAN_NETDEV_NAME "can%d"
+
+#define TX_TIMEOUT (50*HZ/1000) /* 50ms */
+#define RESTART_MS 100 /* restart chip on persistent errors in 100ms */
+#define MAX_BUS_ERRORS 200 /* prevent from flooding bus error interrupts */
+
+/* bus timing */
+#define MAX_TSEG1 15
+#define MAX_TSEG2 7
+#define SAMPLE_POINT 62
+#define JUMPWIDTH 0x40
+
+typedef struct canmessage {
+ uint8_t msgCtrl0Reg;
+ uint8_t msgCtrl1Reg;
+ uint8_t idReg[4];
+ uint8_t messageConfigReg;
+ uint8_t dataReg[8];
+} canmessage_t; // __attribute__ ((packed));
+
+typedef struct canregs {
+ union
+ {
+ struct
+ {
+ canmessage_t messageReg;
+ uint8_t someOtherReg; // padding
+ } msgArr[16];
+ struct
+ {
+ uint8_t controlReg; // Control Register
+ uint8_t statusReg; // Status Register
+ uint8_t cpuInterfaceReg; // CPU Interface Register
+ uint8_t reserved1Reg;
+ uint8_t highSpeedReadReg[2]; // High Speed Read
+ uint8_t globalMaskStandardReg[2]; // Standard Global Mask byte 0
+ uint8_t globalMaskExtendedReg[4]; // Extended Global Mask bytes
+ uint8_t message15MaskReg[4]; // Message 15 Mask bytes
+ canmessage_t message1Reg;
+ uint8_t clkOutReg; // Clock Out Register
+ canmessage_t message2Reg;
+ uint8_t busConfigReg; // Bus Configuration Register
+ canmessage_t message3Reg;
+ uint8_t bitTiming0Reg; // Bit Timing Register byte 0
+ canmessage_t message4Reg;
+ uint8_t bitTiming1Reg; // Bit Timing Register byte 1
+ canmessage_t message5Reg;
+ uint8_t interruptReg; // Interrupt Register
+ canmessage_t message6Reg;
+ uint8_t reserved2Reg;
+ canmessage_t message7Reg;
+ uint8_t reserved3Reg;
+ canmessage_t message8Reg;
+ uint8_t reserved4Reg;
+ canmessage_t message9Reg;
+ uint8_t p1ConfReg;
+ canmessage_t message10Reg;
+ uint8_t p2ConfReg;
+ canmessage_t message11Reg;
+ uint8_t p1InReg;
+ canmessage_t message12Reg;
+ uint8_t p2InReg;
+ canmessage_t message13Reg;
+ uint8_t p1OutReg;
+ canmessage_t message14Reg;
+ uint8_t p2OutReg;
+ canmessage_t message15Reg;
+ uint8_t serialResetAddressReg;
+ };
+ };
+} canregs_t; // __attribute__ ((packed));
+
+/* Control Register (0x00) */
+enum i82527_iCTL {
+ iCTL_INI = 1, // Initialization
+ iCTL_IE = 1<<1, // Interrupt Enable
+ iCTL_SIE = 1<<2, // Status Interrupt Enable
+ iCTL_EIE = 1<<3, // Error Interrupt Enable
+ iCTL_CCE = 1<<6 // Change Configuration Enable
+};
+
+/* Status Register (0x01) */
+enum i82527_iSTAT {
+ iSTAT_TXOK = 1<<3, // Transmit Message Successfully
+ iSTAT_RXOK = 1<<4, // Receive Message Successfully
+ iSTAT_WAKE = 1<<5, // Wake Up Status
+ iSTAT_WARN = 1<<6, // Warning Status
+ iSTAT_BOFF = 1<<7 // Bus Off Status
+};
+
+/* CPU Interface Register (0x02) */
+enum i82527_iCPU {
+ iCPU_CEN = 1, // Clock Out Enable
+ iCPU_MUX = 1<<2, // Multiplex
+ iCPU_SLP = 1<<3, // Sleep
+ iCPU_PWD = 1<<4, // Power Down Mode
+ iCPU_DMC = 1<<5, // Divide Memory Clock
+ iCPU_DSC = 1<<6, // Divide System Clock
+ iCPU_RST = 1<<7, // Hardware Reset Status
+};
+
+/* Clock Out Register (0x1f) */
+enum i82527_iCLK {
+ iCLK_CD0 = 1, // Clock Divider bit 0
+ iCLK_CD1 = 1<<1,
+ iCLK_CD2 = 1<<2,
+ iCLK_CD3 = 1<<3,
+ iCLK_SL0 = 1<<4, // Slew Rate bit 0
+ iCLK_SL1 = 1<<5
+};
+
+/* Bus Configuration Register (0x2f) */
+enum i82527_iBUS {
+ iBUS_DR0 = 1, // Disconnect RX0 Input
+ iBUS_DR1 = 1<<1, // Disconnect RX1 Input
+ iBUS_DT1 = 1<<3, // Disconnect TX1 Output
+ iBUS_POL = 1<<5, // Polarity
+ iBUS_CBY = 1<<6 // Comparator Bypass
+};
+
+#define RESET 1 // Bit Pair Reset Status
+#define SET 2 // Bit Pair Set Status
+#define UNCHANGED 3 // Bit Pair Unchanged
+
+/* Message Control Register 0 (Base Address + 0x0) */
+enum i82527_iMSGCTL0 {
+ INTPD_SET = SET, // Interrupt pending
+ INTPD_RES = RESET, // No Interrupt pending
+ INTPD_UNC = UNCHANGED,
+ RXIE_SET = SET<<2, // Receive Interrupt Enable
+ RXIE_RES = RESET<<2, // Receive Interrupt Disable
+ RXIE_UNC = UNCHANGED<<2,
+ TXIE_SET = SET<<4, // Transmit Interrupt Enable
+ TXIE_RES = RESET<<4, // Transmit Interrupt Disable
+ TXIE_UNC = UNCHANGED<<4,
+ MVAL_SET = SET<<6, // Message Valid
+ MVAL_RES = RESET<<6, // Message Invalid
+ MVAL_UNC = UNCHANGED<<6
+};
+
+/* Message Control Register 1 (Base Address + 0x01) */
+enum i82527_iMSGCTL1 {
+ NEWD_SET = SET, // New Data
+ NEWD_RES = RESET, // No New Data
+ NEWD_UNC = UNCHANGED,
+ MLST_SET = SET<<2, // Message Lost
+ MLST_RES = RESET<<2, // No Message Lost
+ MLST_UNC = UNCHANGED<<2,
+ CPUU_SET = SET<<2, // CPU Updating
+ CPUU_RES = RESET<<2, // No CPU Updating
+ CPUU_UNC = UNCHANGED<<2,
+ TXRQ_SET = SET<<4, // Transmission Request
+ TXRQ_RES = RESET<<4, // No Transmission Request
+ TXRQ_UNC = UNCHANGED<<4,
+ RMPD_SET = SET<<6, // Remote Request Pending
+ RMPD_RES = RESET<<6, // No Remote Request Pending
+ RMPD_UNC = UNCHANGED<<6
+};
+
+/* Message Configuration Register (Base Address + 0x06) */
+enum i82527_iMSGCFG {
+ MCFG_XTD = 1<<2, // Extended Identifier
+ MCFG_DIR = 1<<3 // Direction is Transmit
+};
+
+#undef IOPRINT
+#undef IODEBUG
+
+#ifdef IOPRINT
+#define CANout(base,adr,v) \
+ printk("CANout: (%lx+%x)=%x\n", base,\
+ (int)(long)&((canregs_t *)0)->adr,v)
+
+#define CANin(base,adr) \
+ printk("CANin: (%lx+%x)\n", base, (int)(long)&((canregs_t *)0)->adr)
+
+#else /* IOPRINT */
+
+#ifdef IODEBUG
+#define CANout(base,adr,v) \
+ (printk("CANout: (%lx+%x)=%x\n", base,\
+ (int)(long)&((canregs_t *)0)->adr,v),\
+ hw_writereg(base, (int)(long)&((canregs_t *)0)->adr, v))
+#else
+#define CANout(base,adr,v) hw_writereg(base,\
+ (int)(long)&((canregs_t *)0)->adr, v)
+#endif
+
+#define CANin(base,adr) hw_readreg(base, (int)(long)&((canregs_t *)0)->adr)
+
+#endif /* IOPRINT */
+
+/* CAN private data structure */
+
+struct can_priv {
+ struct can_device_stats can_stats;
+ long open_time;
+ int clock;
+ int hw_regs;
+ int restart_ms;
+ int debug;
+ int speed;
+ int btr;
+ int rx_probe;
+ int mo15;
+ struct timer_list timer;
+ int state;
+};
+
+#define STATE_UNINITIALIZED 0
+#define STATE_PROBE 1
+#define STATE_ACTIVE 2
+#define STATE_ERROR_ACTIVE 3
+#define STATE_ERROR_PASSIVE 4
+#define STATE_BUS_OFF 5
+#define STATE_RESET_MODE 6
+
+void can_proc_create(const char *drv_name);
+void can_proc_remove(const char *drv_name);
+
+#endif /* I82527_H */
diff --git a/drivers/net/can/old/i82527/proc.c b/drivers/net/can/old/i82527/proc.c
new file mode 100644
index 000000000000..e11628fe387c
--- /dev/null
+++ b/drivers/net/can/old/i82527/proc.c
@@ -0,0 +1,209 @@
+/*
+ * proc.c - proc file system functions for I82527 CAN driver.
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h>
+#include "i82527.h"
+#include "hal.h"
+
+extern struct net_device *can_dev[];
+
+static struct proc_dir_entry *pde = NULL;
+static struct proc_dir_entry *pde_regs = NULL;
+static struct proc_dir_entry *pde_reset = NULL;
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "CAN bus device statistics:\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " errwarn overrun wakeup buserr "
+ "errpass arbitr restarts clock baud\n");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ struct net_device *dev = can_dev[i];
+ struct can_priv *priv = netdev_priv(dev);
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: %8d %8d %8d %8d %8d "
+ "%8d %8d %10d %8d\n", dev->name,
+ priv->can_stats.error_warning,
+ priv->can_stats.data_overrun,
+ priv->can_stats.wakeup,
+ priv->can_stats.bus_error,
+ priv->can_stats.error_passive,
+ priv->can_stats.arbitration_lost,
+ priv->can_stats.restarts,
+ priv->clock,
+ priv->speed
+ );
+
+ }
+ }
+
+ *eof = 1;
+ return len;
+}
+
+
+static int can_proc_dump_regs(char *page, int len, struct net_device *dev)
+{
+ int r,s;
+ struct can_priv *priv = netdev_priv(dev);
+ int regs = priv->hw_regs;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s registers:\n", dev->name);
+
+ for (r = 0; r < regs; r += 0x10) {
+ len += snprintf(page + len, PAGE_SIZE - len, "%02X: ", r);
+ for (s = 0; s < 0x10; s++) {
+ if (r+s < regs)
+ len += snprintf(page + len, PAGE_SIZE-len,
+ "%02X ",
+ hw_readreg(dev->base_addr,
+ r+s));
+ }
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ return len;
+}
+
+static int can_proc_read_regs(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i])
+ len = can_proc_dump_regs(page, len, can_dev[i]);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct net_device *dev;
+ int i;
+ struct can_priv *priv;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "resetting ");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ dev = can_dev[i];
+ priv = netdev_priv(can_dev[i]);
+ if ((priv->state != STATE_UNINITIALIZED)
+ && (priv->state != STATE_RESET_MODE)) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s ", dev->name);
+ dev->stop(dev);
+ dev->open(dev);
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ } else {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "(%s|%d) ", dev->name,
+ priv->state);
+ }
+ }
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len, "done\n");
+
+ *eof = 1;
+ return len;
+}
+
+void can_proc_create(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde == NULL) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ pde = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_stats, NULL);
+ }
+ if (pde_regs == NULL) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ pde_regs = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_regs, NULL);
+ }
+ if (pde_reset == NULL) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ pde_reset = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_reset, NULL);
+ }
+}
+
+void can_proc_remove(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_regs) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_reset) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+}
diff --git a/drivers/net/can/old/mscan/Makefile b/drivers/net/can/old/mscan/Makefile
new file mode 100644
index 000000000000..854012e9174d
--- /dev/null
+++ b/drivers/net/can/old/mscan/Makefile
@@ -0,0 +1,21 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+obj-$(CONFIG_CAN_MPC52XX_OLD) += mscan-mpc52xx-old.o
+
+mscan-mpc52xx-old-objs := mscan.o mpc52xx_can.o
+
+endif
diff --git a/drivers/net/can/old/mscan/mpc52xx_can.c b/drivers/net/can/old/mscan/mpc52xx_can.c
new file mode 100644
index 000000000000..da1278fa630a
--- /dev/null
+++ b/drivers/net/can/old/mscan/mpc52xx_can.c
@@ -0,0 +1,248 @@
+/*
+ * DESCRIPTION:
+ * CAN bus driver for the Freescale MPC52xx embedded CPU.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2004-2005, Varma Electronics Oy
+ *
+ * LICENCE:
+ * 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
+ *
+ * HISTORY:
+ * 2005-02-03 created
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+
+#include "mscan.h"
+
+
+#define PDEV_MAX 2
+
+struct platform_device *pdev[PDEV_MAX];
+
+static int __devinit mpc52xx_can_probe(struct platform_device *pdev)
+{
+ struct resource *mem;
+ struct net_device *dev;
+ struct mscan_platform_data *pdata = pdev->dev.platform_data;
+ struct can_priv *can;
+ u32 mem_size;
+ int ret = -ENODEV;
+
+ if (!pdata)
+ return ret;
+
+ dev = alloc_mscandev();
+ if (!dev)
+ return -ENOMEM;
+ can = netdev_priv(dev);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (!mem || !dev->irq)
+ goto req_error;
+
+ mem_size = mem->end - mem->start + 1;
+ if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
+ dev_err(&pdev->dev, "resource unavailable\n");
+ goto req_error;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
+
+ if (!dev->base_addr) {
+ dev_err(&pdev->dev, "failed to map can port\n");
+ ret = -ENOMEM;
+ goto fail_map;
+ }
+
+ can->can_sys_clock = pdata->clock_frq;
+
+ platform_set_drvdata(pdev, dev);
+
+ ret = register_mscandev(dev, pdata->clock_src);
+ if (ret >= 0) {
+ dev_info(&pdev->dev, "probe for a port 0x%lX done\n",
+ dev->base_addr);
+ return ret;
+ }
+
+ iounmap((unsigned long *)dev->base_addr);
+ fail_map:
+ release_mem_region(mem->start, mem_size);
+ req_error:
+ free_candev(dev);
+ dev_err(&pdev->dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit mpc52xx_can_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_mscandev(dev);
+
+ iounmap((volatile void __iomem *)dev->base_addr);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+ free_candev(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static struct mscan_regs saved_regs;
+static int mpc52xx_can_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
+
+ return 0;
+}
+
+static int mpc52xx_can_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ regs->canctl0 |= MSCAN_INITRQ;
+ while ((regs->canctl1 & MSCAN_INITAK) == 0)
+ udelay(10);
+
+ regs->canctl1 = saved_regs.canctl1;
+ regs->canbtr0 = saved_regs.canbtr0;
+ regs->canbtr1 = saved_regs.canbtr1;
+ regs->canidac = saved_regs.canidac;
+
+ /* restore masks, buffers etc. */
+ _memcpy_toio(&regs->canidar1_0, (void *)&saved_regs.canidar1_0,
+ sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
+
+ regs->canctl0 &= ~MSCAN_INITRQ;
+ regs->cantbsel = saved_regs.cantbsel;
+ regs->canrier = saved_regs.canrier;
+ regs->cantier = saved_regs.cantier;
+ regs->canctl0 = saved_regs.canctl0;
+
+ return 0;
+}
+#endif
+
+static struct platform_driver mpc52xx_can_driver = {
+ .driver = {
+ .name = "mpc52xx-mscan",
+ },
+ .probe = mpc52xx_can_probe,
+ .remove = __devexit_p(mpc52xx_can_remove),
+#ifdef CONFIG_PM
+ .suspend = mpc52xx_can_suspend,
+ .resume = mpc52xx_can_resume,
+#endif
+};
+
+#ifdef CONFIG_PPC_MERGE
+static int __init mpc52xx_of_to_pdev(void)
+{
+ struct device_node *np = NULL;
+ unsigned int i;
+ int err = -ENODEV;
+
+ for (i = 0;
+ (np = of_find_compatible_node(np, NULL, "fsl,mpc5200-mscan"));
+ i++) {
+ struct resource r[2] = { };
+ struct mscan_platform_data pdata;
+
+ if (i >= PDEV_MAX) {
+ printk(KERN_WARNING "%s: increase PDEV_MAX for more "
+ "than %i devices\n", __func__, PDEV_MAX);
+ break;
+ }
+
+ err = of_address_to_resource(np, 0, &r[0]);
+ if (err)
+ break;
+
+ of_irq_to_resource(np, 0, &r[1]);
+
+ pdev[i] =
+ platform_device_register_simple("mpc52xx-mscan", i, r, 2);
+ if (IS_ERR(pdev[i])) {
+ err = PTR_ERR(pdev[i]);
+ break;
+ }
+
+ pdata.clock_src = MSCAN_CLKSRC_BUS;
+ pdata.clock_frq = mpc52xx_find_ipb_freq(np);
+ err = platform_device_add_data(pdev[i], &pdata, sizeof(pdata));
+ if (err)
+ break;
+ }
+ return err;
+}
+#else
+#define mscan_of_to_pdev()
+#endif
+
+int __init mpc52xx_can_init(void)
+{
+ printk(KERN_WARNING
+ "This %s driver is DEPRECATED, please switch!\n",
+ mpc52xx_can_driver.driver.name);
+#ifdef CONFIG_PPC_MERGE
+ int err = mpc52xx_of_to_pdev();
+
+ if (err) {
+ printk(KERN_ERR "%s init failed with err=%d\n",
+ mpc52xx_can_driver.driver.name, err);
+ return err;
+ }
+#endif
+ return platform_driver_register(&mpc52xx_can_driver);
+}
+
+void __exit mpc52xx_can_exit(void)
+{
+ int i;
+ platform_driver_unregister(&mpc52xx_can_driver);
+ for (i = 0; i < PDEV_MAX; i++)
+ platform_device_unregister(pdev[i]);
+ printk(KERN_INFO "%s unloaded\n", mpc52xx_can_driver.driver.name);
+}
+
+module_init(mpc52xx_can_init);
+module_exit(mpc52xx_can_exit);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/old/mscan/mscan.c b/drivers/net/can/old/mscan/mscan.c
new file mode 100644
index 000000000000..05afeae1e2fe
--- /dev/null
+++ b/drivers/net/can/old/mscan/mscan.c
@@ -0,0 +1,708 @@
+/*
+ * mscan.c
+ *
+ * DESCRIPTION:
+ * CAN bus driver for the alone generic (as possible as) MSCAN controller.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2005-2006, Varma Electronics Oy
+ *
+ * LICENCE:
+ * 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/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/can.h>
+#include <linux/list.h>
+#include <asm/io.h>
+
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include "mscan.h"
+
+#define MSCAN_NORMAL_MODE 0
+#define MSCAN_SLEEP_MODE MSCAN_SLPRQ
+#define MSCAN_INIT_MODE (MSCAN_INITRQ | MSCAN_SLPRQ)
+#define MSCAN_POWEROFF_MODE (MSCAN_CSWAI | MSCAN_SLPRQ)
+#define MSCAN_SET_MODE_RETRIES 255
+
+
+#define BTR0_BRP_MASK 0x3f
+#define BTR0_SJW_SHIFT 6
+#define BTR0_SJW_MASK (0x3 << BTR0_SJW_SHIFT)
+
+#define BTR1_TSEG1_MASK 0xf
+#define BTR1_TSEG2_SHIFT 4
+#define BTR1_TSEG2_MASK (0x7 << BTR1_TSEG2_SHIFT)
+#define BTR1_SAM_SHIFT 7
+
+#define BTR0_SET_BRP(brp) (((brp) - 1) & BTR0_BRP_MASK)
+#define BTR0_SET_SJW(sjw) ((((sjw) - 1) << BTR0_SJW_SHIFT) & \
+ BTR0_SJW_MASK)
+
+#define BTR1_SET_TSEG1(tseg1) (((tseg1) - 1) & BTR1_TSEG1_MASK)
+#define BTR1_SET_TSEG2(tseg2) ((((tseg2) - 1) << BTR1_TSEG2_SHIFT) & \
+ BTR1_TSEG2_MASK)
+#define BTR1_SET_SAM(sam) (((sam) & 1) << BTR1_SAM_SHIFT)
+
+struct mscan_state {
+ u8 mode;
+ u8 canrier;
+ u8 cantier;
+};
+
+#define TX_QUEUE_SIZE 3
+
+typedef struct {
+ struct list_head list;
+ u8 mask;
+} tx_queue_entry_t;
+
+struct mscan_priv {
+ struct can_priv can;
+ volatile unsigned long flags;
+ u8 shadow_statflg;
+ u8 shadow_canrier;
+ u8 cur_pri;
+ u8 tx_active;
+
+ struct list_head tx_head;
+ tx_queue_entry_t tx_queue[TX_QUEUE_SIZE];
+ struct napi_struct napi;
+ struct net_device *dev;
+};
+
+#define F_RX_PROGRESS 0
+#define F_TX_PROGRESS 1
+#define F_TX_WAIT_ALL 2
+
+static int mscan_set_mode(struct net_device *dev, u8 mode)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ int ret = 0;
+ int i;
+ u8 canctl1;
+
+ if (mode != MSCAN_NORMAL_MODE) {
+ canctl1 = in_8(&regs->canctl1);
+ if ((mode & MSCAN_SLPRQ) && (canctl1 & MSCAN_SLPAK) == 0) {
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_SLPRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(&regs->canctl1) & MSCAN_SLPAK)
+ break;
+ udelay(100);
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+
+ if (!ret && (mode & MSCAN_INITRQ)
+ && (canctl1 & MSCAN_INITAK) == 0) {
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_INITRQ);
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ if (in_8(&regs->canctl1) & MSCAN_INITAK)
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+
+ if (!ret && (mode & MSCAN_CSWAI))
+ out_8(&regs->canctl0,
+ in_8(&regs->canctl0) | MSCAN_CSWAI);
+
+ } else {
+ canctl1 = in_8(&regs->canctl1);
+ if (canctl1 & (MSCAN_SLPAK | MSCAN_INITAK)) {
+ out_8(&regs->canctl0, in_8(&regs->canctl0) &
+ ~(MSCAN_SLPRQ | MSCAN_INITRQ));
+ for (i = 0; i < MSCAN_SET_MODE_RETRIES; i++) {
+ canctl1 = in_8(&regs->canctl1);
+ if (!(canctl1 & (MSCAN_INITAK | MSCAN_SLPAK)))
+ break;
+ }
+ if (i >= MSCAN_SET_MODE_RETRIES)
+ ret = -ENODEV;
+ }
+ }
+ return ret;
+}
+
+static void mscan_push_state(struct net_device *dev, struct mscan_state *state)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ state->mode = in_8(&regs->canctl0) & (MSCAN_SLPRQ | MSCAN_INITRQ |
+ MSCAN_CSWAI);
+ state->canrier = in_8(&regs->canrier);
+ state->cantier = in_8(&regs->cantier);
+}
+
+static int mscan_pop_state(struct net_device *dev, struct mscan_state *state)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ int ret;
+ ret = mscan_set_mode(dev, state->mode);
+ if (!ret) {
+ out_8(&regs->canrier, state->canrier);
+ out_8(&regs->cantier, state->cantier);
+ }
+ return ret;
+}
+
+static int mscan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_frame *frame = (struct can_frame *)skb->data;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+
+ int i, rtr, buf_id;
+ u32 can_id;
+
+ if (frame->can_dlc > 8)
+ return -EINVAL;
+
+ dev_dbg(ND2D(dev), "%s\n", __FUNCTION__);
+ out_8(&regs->cantier, 0);
+
+ i = ~priv->tx_active & MSCAN_TXE;
+ buf_id = ffs(i) - 1;
+ switch (hweight8(i)) {
+ case 0:
+ netif_stop_queue(dev);
+ dev_err(ND2D(dev), "BUG! Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ case 1:
+ /* if buf_id < 3, then current frame will be send out of order,
+ since buffer with lower id have higher priority (hell..) */
+ if (buf_id < 3)
+ priv->cur_pri++;
+ if (priv->cur_pri == 0xff)
+ set_bit(F_TX_WAIT_ALL, &priv->flags);
+ netif_stop_queue(dev);
+ case 2:
+ set_bit(F_TX_PROGRESS, &priv->flags);
+ }
+ out_8(&regs->cantbsel, i);
+
+ rtr = frame->can_id & CAN_RTR_FLAG;
+
+ if (frame->can_id & CAN_EFF_FLAG) {
+ dev_dbg(ND2D(dev), "sending extended frame\n");
+
+ can_id = (frame->can_id & CAN_EFF_MASK) << 1;
+ if (rtr)
+ can_id |= 1;
+ out_be16(&regs->tx.idr3_2, can_id);
+
+ can_id >>= 16;
+ can_id = (can_id & 0x7) | ((can_id << 2) & 0xffe0) | (3 << 3);
+ } else {
+ dev_dbg(ND2D(dev), "sending standard frame\n");
+ can_id = (frame->can_id & CAN_SFF_MASK) << 5;
+ if (rtr)
+ can_id |= 1 << 4;
+ }
+ out_be16(&regs->tx.idr1_0, can_id);
+
+ if (!rtr) {
+ volatile void __iomem *data = &regs->tx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ /*Its safe to write into dsr[dlc+1] */
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ out_be16(data, *payload++);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ out_8(&regs->tx.dlr, frame->can_dlc);
+ out_8(&regs->tx.tbpr, priv->cur_pri);
+
+ /* Start transmission. */
+ out_8(&regs->cantflg, 1 << buf_id);
+
+ if (!test_bit(F_TX_PROGRESS, &priv->flags))
+ dev->trans_start = jiffies;
+
+ list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head);
+
+ kfree_skb(skb);
+
+ /* Enable interrupt. */
+ priv->tx_active |= 1 << buf_id;
+ out_8(&regs->cantier, priv->tx_active);
+
+ return NETDEV_TX_OK;
+}
+
+static void mscan_tx_timeout(struct net_device *dev)
+{
+ struct sk_buff *skb;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct can_frame *frame;
+ u8 mask;
+
+ printk("%s\n", __FUNCTION__);
+
+ out_8(&regs->cantier, 0);
+
+ mask = list_entry(priv->tx_head.next, tx_queue_entry_t, list)->mask;
+ dev->trans_start = jiffies;
+ out_8(&regs->cantarq, mask);
+ out_8(&regs->cantier, priv->tx_active);
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(ND2D(dev), "TIMEOUT packet dropped\n");
+ return;
+ }
+ frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ frame->can_id = CAN_ERR_FLAG | CAN_ERR_TX_TIMEOUT;
+ frame->can_dlc = CAN_ERR_DLC;
+
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ netif_rx(skb);
+
+}
+
+static can_state_t state_map[] = {
+ CAN_STATE_ACTIVE,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF
+};
+
+static inline int check_set_state(struct net_device *dev, u8 canrflg)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ can_state_t state;
+ int ret = 0;
+
+ if (!(canrflg & MSCAN_CSCIF) || priv->can.state > CAN_STATE_BUS_OFF)
+ return 0;
+
+ state =
+ state_map[max(MSCAN_STATE_RX(canrflg), MSCAN_STATE_TX(canrflg))];
+ if (priv->can.state < state)
+ ret = 1;
+ if (state == CAN_STATE_BUS_OFF)
+ netif_carrier_off(dev);
+ else if (priv->can.state == CAN_STATE_BUS_OFF
+ && state != CAN_STATE_BUS_OFF)
+ netif_carrier_on(dev);
+ priv->can.state = state;
+ return ret;
+}
+
+static int mscan_rx_poll(struct napi_struct *napi, int quota)
+{
+ struct mscan_priv *priv = container_of(napi, struct mscan_priv, napi);
+ struct net_device *dev = priv->dev;
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct net_device_stats *stats = dev->get_stats(dev);
+ int npackets = 0;
+ int ret = 1;
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u32 can_id;
+ u8 canrflg;
+ int i;
+
+ while (npackets < quota && ((canrflg = in_8(&regs->canrflg)) &
+ (MSCAN_RXF | MSCAN_ERR_IF))) {
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb) {
+ if (printk_ratelimit())
+ dev_notice(ND2D(dev), "packet dropped\n");
+ stats->rx_dropped++;
+ out_8(&regs->canrflg, canrflg);
+ continue;
+ }
+
+ frame = (struct can_frame *)skb_put(skb,
+ sizeof(struct can_frame));
+
+ if (canrflg & MSCAN_RXF) {
+ can_id = in_be16(&regs->rx.idr1_0);
+ if (can_id & (1 << 3)) {
+ frame->can_id = CAN_EFF_FLAG;
+ can_id = ((can_id << 16) |
+ in_be16(&regs->rx.idr3_2));
+ can_id = ((can_id & 0xffe00000) |
+ ((can_id & 0x7ffff) << 2)) >> 2;
+ } else {
+ can_id >>= 4;
+ frame->can_id = 0;
+ }
+
+ frame->can_id |= can_id >> 1;
+ if (can_id & 1)
+ frame->can_id |= CAN_RTR_FLAG;
+ frame->can_dlc = in_8(&regs->rx.dlr) & 0xf;
+
+ if (!(frame->can_id & CAN_RTR_FLAG)) {
+ volatile void __iomem *data = &regs->rx.dsr1_0;
+ u16 *payload = (u16 *) frame->data;
+ for (i = 0; i < (frame->can_dlc + 1) / 2; i++) {
+ *payload++ = in_be16(data);
+ data += 2 + _MSCAN_RESERVED_DSR_SIZE;
+ }
+ }
+
+ dev_dbg(ND2D(dev),
+ "received pkt: id: %u dlc: %u data: ",
+ frame->can_id, frame->can_dlc);
+#ifdef DEBUG
+ for (i = 0;
+ i < frame->can_dlc && !(frame->can_id &
+ CAN_FLAG_RTR); i++)
+ printk("%2x ", frame->data[i]);
+ printk("\n");
+#endif
+
+ out_8(&regs->canrflg, MSCAN_RXF);
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += frame->can_dlc;
+ } else if (canrflg & MSCAN_ERR_IF) {
+ frame->can_id = CAN_ERR_FLAG;
+
+ if (canrflg & MSCAN_OVRIF) {
+ frame->can_id |= CAN_ERR_CRTL;
+ frame->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ stats->rx_over_errors++;
+ } else
+ frame->data[1] = 0;
+
+ if (check_set_state(dev, canrflg)) {
+ frame->can_id |= CAN_ERR_CRTL;
+ switch (priv->can.state) {
+ case CAN_STATE_BUS_WARNING:
+ if ((priv->shadow_statflg &
+ MSCAN_RSTAT_MSK) <
+ (canrflg & MSCAN_RSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_WARNING;
+
+ if ((priv->shadow_statflg &
+ MSCAN_TSTAT_MSK) <
+ (canrflg & MSCAN_TSTAT_MSK))
+ frame->data[1] |=
+ CAN_ERR_CRTL_TX_WARNING;
+ break;
+ case CAN_STATE_BUS_PASSIVE:
+ frame->data[1] |=
+ CAN_ERR_CRTL_RX_PASSIVE;
+ break;
+ case CAN_STATE_BUS_OFF:
+ frame->can_id |= CAN_ERR_BUSOFF;
+ frame->can_id &= ~CAN_ERR_CRTL;
+ break;
+ default:
+ break;
+ }
+ }
+ priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
+ frame->can_dlc = CAN_ERR_DLC;
+ out_8(&regs->canrflg, MSCAN_ERR_IF);
+ }
+
+ npackets++;
+ skb->dev = dev;
+ skb->protocol = __constant_htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_receive_skb(skb);
+ }
+
+ if (!(in_8(&regs->canrflg) & (MSCAN_RXF | MSCAN_ERR_IF))) {
+ netif_rx_complete(dev, &priv->napi);
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ out_8(&regs->canrier,
+ in_8(&regs->canrier) | MSCAN_ERR_IF | MSCAN_RXFIE);
+ ret = 0;
+ }
+ return ret;
+}
+
+
+static irqreturn_t mscan_isr(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ struct net_device_stats *stats = dev->get_stats(dev);
+ u8 cantier, cantflg, canrflg;
+ irqreturn_t ret = IRQ_NONE;
+
+ if ((cantier = in_8(&regs->cantier) & MSCAN_TXE) &&
+ (cantflg = in_8(&regs->cantflg) & cantier)) {
+ struct list_head *tmp, *pos;
+
+ list_for_each_safe(pos, tmp, &priv->tx_head) {
+ tx_queue_entry_t *entry =
+ list_entry(pos, tx_queue_entry_t, list);
+ u8 mask = entry->mask;
+
+ if (!(cantflg & mask))
+ continue;
+
+ if (in_8(&regs->cantaak) & mask) {
+ stats->tx_dropped++;
+ stats->tx_aborted_errors++;
+ } else {
+ out_8(&regs->cantbsel, mask);
+ stats->tx_bytes +=
+ in_8(&regs->tx.dlr);
+ stats->tx_packets++;
+ }
+ priv->tx_active &= ~mask;
+ list_del(pos);
+ }
+
+ if (list_empty(&priv->tx_head)) {
+ clear_bit(F_TX_WAIT_ALL, &priv->flags);
+ clear_bit(F_TX_PROGRESS, &priv->flags);
+ priv->cur_pri = 0;
+ } else
+ dev->trans_start = jiffies;
+
+ if (!test_bit(F_TX_WAIT_ALL, &priv->flags))
+ netif_wake_queue(dev);
+
+ out_8(&regs->cantier, priv->tx_active);
+ ret = IRQ_HANDLED;
+ }
+
+ if ((((canrflg = in_8(&regs->canrflg)) & ~MSCAN_STAT_MSK)) &&
+ !test_and_set_bit(F_RX_PROGRESS, &priv->flags)) {
+ if (check_set_state(dev, canrflg)) {
+ out_8(&regs->canrflg, MSCAN_CSCIF);
+ ret = IRQ_HANDLED;
+ }
+ if (canrflg & ~MSCAN_STAT_MSK) {
+ priv->shadow_canrier = in_8(&regs->canrier);
+ out_8(&regs->canrier, 0);
+ netif_rx_schedule(dev, &priv->napi);
+ ret = IRQ_HANDLED;
+ } else
+ clear_bit(F_RX_PROGRESS, &priv->flags);
+ }
+ return ret;
+}
+
+static int mscan_do_set_mode(struct net_device *dev, can_mode_t mode)
+{
+ switch (mode) {
+ case CAN_MODE_SLEEP:
+ case CAN_MODE_STOP:
+ netif_stop_queue(dev);
+ mscan_set_mode(dev,
+ (mode ==
+ CAN_MODE_STOP) ? MSCAN_INIT_MODE :
+ MSCAN_SLEEP_MODE);
+ break;
+ case CAN_MODE_START:
+ printk("%s: CAN_MODE_START requested\n", __FUNCTION__);
+ mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int mscan_do_set_bit_time(struct net_device *dev,
+ struct can_bittime *bt)
+{
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ int ret = 0;
+ u8 reg;
+ struct mscan_state state;
+
+ if (bt->type != CAN_BITTIME_STD)
+ return -EINVAL;
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ mscan_push_state(dev, &state);
+ ret = mscan_set_mode(dev, MSCAN_INIT_MODE);
+ if (!ret) {
+ reg = BTR0_SET_BRP(bt->std.brp) | BTR0_SET_SJW(bt->std.sjw);
+ out_8(&regs->canbtr0, reg);
+
+ reg = (BTR1_SET_TSEG1(bt->std.prop_seg + bt->std.phase_seg1) |
+ BTR1_SET_TSEG2(bt->std.phase_seg2) |
+ BTR1_SET_SAM(bt->std.sam));
+ out_8(&regs->canbtr1, reg);
+
+ ret = mscan_pop_state(dev, &state);
+ }
+
+ spin_unlock_irq(&priv->can.irq_lock);
+ return ret;
+}
+
+static int mscan_open(struct net_device *dev)
+{
+ int ret;
+ struct mscan_priv *priv = netdev_priv(dev);
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ napi_enable(&priv->napi);
+ ret = request_irq(dev->irq, mscan_isr, IRQF_SHARED, dev->name, dev);
+
+ if (ret < 0) {
+ napi_disable(&priv->napi);
+ printk(KERN_ERR "%s - failed to attach interrupt\n",
+ dev->name);
+ return ret;
+ }
+
+ INIT_LIST_HEAD(&priv->tx_head);
+ /* acceptance mask/acceptance code (accept everything) */
+ out_be16(&regs->canidar1_0, 0);
+ out_be16(&regs->canidar3_2, 0);
+ out_be16(&regs->canidar5_4, 0);
+ out_be16(&regs->canidar7_6, 0);
+
+ out_be16(&regs->canidmr1_0, 0xffff);
+ out_be16(&regs->canidmr3_2, 0xffff);
+ out_be16(&regs->canidmr5_4, 0xffff);
+ out_be16(&regs->canidmr7_6, 0xffff);
+ /* Two 32 bit Acceptance Filters */
+ out_8(&regs->canidac, MSCAN_AF_32BIT);
+
+ out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_LISTEN);
+ mscan_set_mode(dev, MSCAN_NORMAL_MODE);
+
+ priv->shadow_statflg = in_8(&regs->canrflg) & MSCAN_STAT_MSK;
+ priv->cur_pri = 0;
+ priv->tx_active = 0;
+
+ out_8(&regs->cantier, 0);
+ /* Enable receive interrupts. */
+ out_8(&regs->canrier, MSCAN_OVRIE | MSCAN_RXFIE | MSCAN_CSCIE |
+ MSCAN_RSTATE1 | MSCAN_RSTATE0 | MSCAN_TSTATE1 | MSCAN_TSTATE0);
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int mscan_close(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+ netif_stop_queue(dev);
+
+ /* disable interrupts */
+ out_8(&regs->cantier, 0);
+ out_8(&regs->canrier, 0);
+ free_irq(dev->irq, dev);
+
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ return 0;
+}
+
+int register_mscandev(struct net_device *dev, int clock_src)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ u8 ctl1;
+
+ ctl1 = in_8(&regs->canctl1);
+ if (clock_src)
+ ctl1 |= MSCAN_CLKSRC;
+ else
+ ctl1 &= ~MSCAN_CLKSRC;
+
+ ctl1 |= MSCAN_CANE;
+ out_8(&regs->canctl1, ctl1);
+ udelay(100);
+
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+
+ return register_netdev(dev);
+}
+
+EXPORT_SYMBOL(register_mscandev);
+
+void unregister_mscandev(struct net_device *dev)
+{
+ struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+ mscan_set_mode(dev, MSCAN_INIT_MODE);
+ out_8(&regs->canctl1, in_8(&regs->canctl1) & ~MSCAN_CANE);
+ unregister_netdev(dev);
+}
+
+EXPORT_SYMBOL(unregister_mscandev);
+
+struct net_device *alloc_mscandev(void)
+{
+ struct net_device *dev;
+ struct mscan_priv *priv;
+ int i;
+
+ dev = alloc_candev(sizeof(struct mscan_priv));
+ if (!dev)
+ return NULL;
+ priv = netdev_priv(dev);
+
+ dev->watchdog_timeo = MSCAN_WATCHDOG_TIMEOUT;
+ dev->open = mscan_open;
+ dev->stop = mscan_close;
+ dev->hard_start_xmit = mscan_hard_start_xmit;
+ dev->tx_timeout = mscan_tx_timeout;
+
+ priv->dev = dev;
+ netif_napi_add(dev, &priv->napi, mscan_rx_poll, 8);
+
+ priv->can.do_set_bittime = mscan_do_set_bit_time;
+ priv->can.do_set_mode = mscan_do_set_mode;
+
+ for (i = 0; i < TX_QUEUE_SIZE; i++)
+ priv->tx_queue[i].mask = 1 << i;
+
+ return dev;
+}
+
+EXPORT_SYMBOL(alloc_mscandev);
+
+MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver for a mscan based chips");
diff --git a/drivers/net/can/old/mscan/mscan.h b/drivers/net/can/old/mscan/mscan.h
new file mode 100644
index 000000000000..807ec3fae437
--- /dev/null
+++ b/drivers/net/can/old/mscan/mscan.h
@@ -0,0 +1,247 @@
+/*
+ * DESCRIPTION:
+ * Definitions of consts/structs to drive the Freescale MSCAN.
+ *
+ * AUTHOR:
+ * Andrey Volkov <avolkov@varma-el.com>
+ *
+ * COPYRIGHT:
+ * 2004-2006, Varma Electronics Oy
+ *
+ * LICENCE:
+ * 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 __MSCAN_H__
+#define __MSCAN_H__
+
+#include <linux/autoconf.h>
+#include <asm/types.h>
+
+/* MSCAN control register 0 (CANCTL0) bits */
+#define MSCAN_RXFRM 0x80
+#define MSCAN_RXACT 0x40
+#define MSCAN_CSWAI 0x20
+#define MSCAN_SYNCH 0x10
+#define MSCAN_TIME 0x08
+#define MSCAN_WUPE 0x04
+#define MSCAN_SLPRQ 0x02
+#define MSCAN_INITRQ 0x01
+
+/* MSCAN control register 1 (CANCTL1) bits */
+#define MSCAN_CANE 0x80
+#define MSCAN_CLKSRC 0x40
+#define MSCAN_LOOPB 0x20
+#define MSCAN_LISTEN 0x10
+#define MSCAN_WUPM 0x04
+#define MSCAN_SLPAK 0x02
+#define MSCAN_INITAK 0x01
+
+#ifdef CONFIG_PPC_MPC52xx
+#define MSCAN_CLKSRC_BUS 0
+#define MSCAN_CLKSRC_XTAL MSCAN_CLKSRC
+#else
+#define MSCAN_CLKSRC_BUS MSCAN_CLKSRC
+#define MSCAN_CLKSRC_XTAL 0
+#endif
+
+/* MSCAN receiver flag register (CANRFLG) bits */
+#define MSCAN_WUPIF 0x80
+#define MSCAN_CSCIF 0x40
+#define MSCAN_RSTAT1 0x20
+#define MSCAN_RSTAT0 0x10
+#define MSCAN_TSTAT1 0x08
+#define MSCAN_TSTAT0 0x04
+#define MSCAN_OVRIF 0x02
+#define MSCAN_RXF 0x01
+#define MSCAN_ERR_IF (MSCAN_OVRIF | MSCAN_CSCIF)
+#define MSCAN_RSTAT_MSK (MSCAN_RSTAT1 | MSCAN_RSTAT0)
+#define MSCAN_TSTAT_MSK (MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STAT_MSK (MSCAN_RSTAT_MSK | MSCAN_TSTAT_MSK)
+
+#define MSCAN_STATE_BUS_OFF (MSCAN_RSTAT1 | MSCAN_RSTAT0 | \
+ MSCAN_TSTAT1 | MSCAN_TSTAT0)
+#define MSCAN_STATE_TX(canrflg) (((canrflg)&MSCAN_TSTAT_MSK)>>2)
+#define MSCAN_STATE_RX(canrflg) (((canrflg)&MSCAN_RSTAT_MSK)>>4)
+#define MSCAN_STATE_ACTIVE 0
+#define MSCAN_STATE_WARNING 1
+#define MSCAN_STATE_PASSIVE 2
+#define MSCAN_STATE_BUSOFF 3
+
+/* MSCAN receiver interrupt enable register (CANRIER) bits */
+#define MSCAN_WUPIE 0x80
+#define MSCAN_CSCIE 0x40
+#define MSCAN_RSTATE1 0x20
+#define MSCAN_RSTATE0 0x10
+#define MSCAN_TSTATE1 0x08
+#define MSCAN_TSTATE0 0x04
+#define MSCAN_OVRIE 0x02
+#define MSCAN_RXFIE 0x01
+
+/* MSCAN transmitter flag register (CANTFLG) bits */
+#define MSCAN_TXE2 0x04
+#define MSCAN_TXE1 0x02
+#define MSCAN_TXE0 0x01
+#define MSCAN_TXE (MSCAN_TXE2 | MSCAN_TXE1 | MSCAN_TXE0)
+
+/* MSCAN transmitter interrupt enable register (CANTIER) bits */
+#define MSCAN_TXIE2 0x04
+#define MSCAN_TXIE1 0x02
+#define MSCAN_TXIE0 0x01
+#define MSCAN_TXIE (MSCAN_TXIE2 | MSCAN_TXIE1 | MSCAN_TXIE0)
+
+/* MSCAN transmitter message abort request (CANTARQ) bits */
+#define MSCAN_ABTRQ2 0x04
+#define MSCAN_ABTRQ1 0x02
+#define MSCAN_ABTRQ0 0x01
+
+/* MSCAN transmitter message abort ack (CANTAAK) bits */
+#define MSCAN_ABTAK2 0x04
+#define MSCAN_ABTAK1 0x02
+#define MSCAN_ABTAK0 0x01
+
+/* MSCAN transmit buffer selection (CANTBSEL) bits */
+#define MSCAN_TX2 0x04
+#define MSCAN_TX1 0x02
+#define MSCAN_TX0 0x01
+
+/* MSCAN ID acceptance control register (CANIDAC) bits */
+#define MSCAN_IDAM1 0x20
+#define MSCAN_IDAM0 0x10
+#define MSCAN_IDHIT2 0x04
+#define MSCAN_IDHIT1 0x02
+#define MSCAN_IDHIT0 0x01
+
+#define MSCAN_AF_32BIT 0x00
+#define MSCAN_AF_16BIT MSCAN_IDAM0
+#define MSCAN_AF_8BIT MSCAN_IDAM1
+#define MSCAN_AF_CLOSED (MSCAN_IDAM0|MSCAN_IDAM1)
+#define MSCAN_AF_MASK (~(MSCAN_IDAM0|MSCAN_IDAM1))
+
+/* MSCAN Miscellaneous Register (CANMISC) bits */
+#define MSCAN_BOHOLD 0x01
+
+#ifdef CONFIG_PPC_MPC52xx
+#define _MSCAN_RESERVED_(n,num) u8 _res##n[num]
+#define _MSCAN_RESERVED_DSR_SIZE 2
+#else
+#define _MSCAN_RESERVED_(n,num)
+#define _MSCAN_RESERVED_DSR_SIZE 0
+#endif
+
+/* Structure of the hardware registers */
+struct mscan_regs {
+ /* (see doco S12MSCANV3/D) MPC5200 MSCAN */
+ u8 canctl0; /* + 0x00 0x00 */
+ u8 canctl1; /* + 0x01 0x01 */
+ _MSCAN_RESERVED_(1, 2); /* + 0x02 */
+ u8 canbtr0; /* + 0x04 0x02 */
+ u8 canbtr1; /* + 0x05 0x03 */
+ _MSCAN_RESERVED_(2, 2); /* + 0x06 */
+ u8 canrflg; /* + 0x08 0x04 */
+ u8 canrier; /* + 0x09 0x05 */
+ _MSCAN_RESERVED_(3, 2); /* + 0x0a */
+ u8 cantflg; /* + 0x0c 0x06 */
+ u8 cantier; /* + 0x0d 0x07 */
+ _MSCAN_RESERVED_(4, 2); /* + 0x0e */
+ u8 cantarq; /* + 0x10 0x08 */
+ u8 cantaak; /* + 0x11 0x09 */
+ _MSCAN_RESERVED_(5, 2); /* + 0x12 */
+ u8 cantbsel; /* + 0x14 0x0a */
+ u8 canidac; /* + 0x15 0x0b */
+ u8 reserved; /* + 0x16 0x0c */
+ _MSCAN_RESERVED_(6, 5); /* + 0x17 */
+#ifndef CONFIG_PPC_MPC52xx
+ u8 canmisc; /* 0x0d */
+#endif
+ u8 canrxerr; /* + 0x1c 0x0e */
+ u8 cantxerr; /* + 0x1d 0x0f */
+ _MSCAN_RESERVED_(7, 2); /* + 0x1e */
+ u16 canidar1_0; /* + 0x20 0x10 */
+ _MSCAN_RESERVED_(8, 2); /* + 0x22 */
+ u16 canidar3_2; /* + 0x24 0x12 */
+ _MSCAN_RESERVED_(9, 2); /* + 0x26 */
+ u16 canidmr1_0; /* + 0x28 0x14 */
+ _MSCAN_RESERVED_(10, 2); /* + 0x2a */
+ u16 canidmr3_2; /* + 0x2c 0x16 */
+ _MSCAN_RESERVED_(11, 2); /* + 0x2e */
+ u16 canidar5_4; /* + 0x30 0x18 */
+ _MSCAN_RESERVED_(12, 2); /* + 0x32 */
+ u16 canidar7_6; /* + 0x34 0x1a */
+ _MSCAN_RESERVED_(13, 2); /* + 0x36 */
+ u16 canidmr5_4; /* + 0x38 0x1c */
+ _MSCAN_RESERVED_(14, 2); /* + 0x3a */
+ u16 canidmr7_6; /* + 0x3c 0x1e */
+ _MSCAN_RESERVED_(15, 2); /* + 0x3e */
+ struct {
+ u16 idr1_0; /* + 0x40 0x20 */
+ _MSCAN_RESERVED_(16, 2); /* + 0x42 */
+ u16 idr3_2; /* + 0x44 0x22 */
+ _MSCAN_RESERVED_(17, 2); /* + 0x46 */
+ u16 dsr1_0; /* + 0x48 0x24 */
+ _MSCAN_RESERVED_(18, 2); /* + 0x4a */
+ u16 dsr3_2; /* + 0x4c 0x26 */
+ _MSCAN_RESERVED_(19, 2); /* + 0x4e */
+ u16 dsr5_4; /* + 0x50 0x28 */
+ _MSCAN_RESERVED_(20, 2); /* + 0x52 */
+ u16 dsr7_6; /* + 0x54 0x2a */
+ _MSCAN_RESERVED_(21, 2); /* + 0x56 */
+ u8 dlr; /* + 0x58 0x2c */
+ u8:8; /* + 0x59 0x2d */
+ _MSCAN_RESERVED_(22, 2); /* + 0x5a */
+ u16 time; /* + 0x5c 0x2e */
+ } rx;
+ _MSCAN_RESERVED_(23, 2); /* + 0x5e */
+ struct {
+ u16 idr1_0; /* + 0x60 0x30 */
+ _MSCAN_RESERVED_(24, 2); /* + 0x62 */
+ u16 idr3_2; /* + 0x64 0x32 */
+ _MSCAN_RESERVED_(25, 2); /* + 0x66 */
+ u16 dsr1_0; /* + 0x68 0x34 */
+ _MSCAN_RESERVED_(26, 2); /* + 0x6a */
+ u16 dsr3_2; /* + 0x6c 0x36 */
+ _MSCAN_RESERVED_(27, 2); /* + 0x6e */
+ u16 dsr5_4; /* + 0x70 0x38 */
+ _MSCAN_RESERVED_(28, 2); /* + 0x72 */
+ u16 dsr7_6; /* + 0x74 0x3a */
+ _MSCAN_RESERVED_(29, 2); /* + 0x76 */
+ u8 dlr; /* + 0x78 0x3c */
+ u8 tbpr; /* + 0x79 0x3d */
+ _MSCAN_RESERVED_(30, 2); /* + 0x7a */
+ u16 time; /* + 0x7c 0x3e */
+ } tx;
+ _MSCAN_RESERVED_(31, 2); /* + 0x7e */
+} __attribute__ ((packed));
+
+#undef _MSCAN_RESERVED_
+#define MSCAN_REGION sizeof(struct mscan)
+
+#define MSCAN_WATCHDOG_TIMEOUT ((500*HZ)/1000)
+
+struct mscan_platform_data {
+ u8 clock_src; /* MSCAN_CLKSRC_BUS or MSCAN_CLKSRC_XTAL */
+ u32 clock_frq; /* can ref. clock, in Hz */
+};
+
+struct net_device *alloc_mscandev(void);
+/* @clock_src:
+ 1 = The MSCAN clock source is the onchip Bus Clock.
+ 0 = The MSCAN clock source is the chip Oscillator Clock.
+*/
+extern int register_mscandev(struct net_device *dev, int clock_src);
+extern void unregister_mscandev(struct net_device *dev);
+
+#endif /* __MSCAN_H__ */
diff --git a/drivers/net/can/old/sja1000/Makefile b/drivers/net/can/old/sja1000/Makefile
new file mode 100644
index 000000000000..cc8f1f917e99
--- /dev/null
+++ b/drivers/net/can/old/sja1000/Makefile
@@ -0,0 +1,27 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/old/hal
+
+obj-m := sja1000-io.o sja1000-iomem.o sja1000-iomux.o sja1000-gw2.o sja1000-esdio.o sja1000-c200.o
+
+sja1000-io-objs := sja1000.o proc.o ../hal/io.o
+sja1000-iomem-objs := sja1000.o proc.o ../hal/iomem.o
+sja1000-iomux-objs := sja1000.o proc.o ../hal/iomux.o
+sja1000-gw2-objs := sja1000.o proc.o ../hal/gw2.o
+sja1000-esdio-objs := sja1000.o proc.o ../hal/esdio.o
+sja1000-c200-objs := sja1000.o proc.o ../hal/c200.o
+
+endif
diff --git a/drivers/net/can/old/sja1000/proc.c b/drivers/net/can/old/sja1000/proc.c
new file mode 100644
index 000000000000..b98aa6180784
--- /dev/null
+++ b/drivers/net/can/old/sja1000/proc.c
@@ -0,0 +1,230 @@
+/*
+ * proc.c - proc file system functions for SJA1000 CAN driver.
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h>
+#include "sja1000.h"
+#include "hal.h"
+
+extern struct net_device *can_dev[];
+
+static struct proc_dir_entry *pde = NULL;
+static struct proc_dir_entry *pde_regs = NULL;
+static struct proc_dir_entry *pde_reset = NULL;
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "CAN bus device statistics:\n");
+ len += snprintf(page + len, PAGE_SIZE - len,
+ " errwarn overrun wakeup buserr "
+ "errpass arbitr restarts clock baud\n");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ struct net_device *dev = can_dev[i];
+ struct can_priv *priv = netdev_priv(dev);
+#ifdef SJA1000_H
+ u8 stat = hw_readreg(dev->base_addr, REG_SR);
+
+ if (stat & 0x80) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: bus status: "
+ "BUS OFF, ", dev->name);
+ } else if (stat & 0x40) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: bus status: ERROR "
+ "PASSIVE, ", dev->name);
+ } else {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: bus status: OK, ",
+ dev->name);
+ }
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "RXERR: %d, TXERR: %d\n",
+ hw_readreg(dev->base_addr, REG_RXERR),
+ hw_readreg(dev->base_addr, REG_TXERR));
+#endif
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s: %8d %8d %8d %8d %8d "
+ "%8d %8d %10d %8d\n", dev->name,
+ priv->can_stats.error_warning,
+ priv->can_stats.data_overrun,
+ priv->can_stats.wakeup,
+ priv->can_stats.bus_error,
+ priv->can_stats.error_passive,
+ priv->can_stats.arbitration_lost,
+ priv->can_stats.restarts,
+ priv->clock,
+ priv->speed
+ );
+
+ }
+ }
+
+ *eof = 1;
+ return len;
+}
+
+
+static int can_proc_dump_regs(char *page, int len, struct net_device *dev)
+{
+ int r,s;
+ struct can_priv *priv = netdev_priv(dev);
+ int regs = priv->hw_regs;
+
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s registers:\n", dev->name);
+
+ for (r = 0; r < regs; r += 0x10) {
+ len += snprintf(page + len, PAGE_SIZE - len, "%02X: ", r);
+ for (s = 0; s < 0x10; s++) {
+ if (r+s < regs)
+ len += snprintf(page + len, PAGE_SIZE-len,
+ "%02X ",
+ hw_readreg(dev->base_addr,
+ r+s));
+ }
+ len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ }
+
+ return len;
+}
+
+static int can_proc_read_regs(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ int i;
+
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i])
+ len = can_proc_dump_regs(page, len, can_dev[i]);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+static int can_proc_read_reset(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ struct net_device *dev;
+ int i;
+ struct can_priv *priv;
+
+ len += snprintf(page + len, PAGE_SIZE - len, "resetting ");
+ for (i = 0; (i < MAXDEV) && (len < PAGE_SIZE - 200); i++) {
+ if (can_dev[i]) {
+ dev = can_dev[i];
+ priv = netdev_priv(can_dev[i]);
+ if ((priv->state != STATE_UNINITIALIZED)
+ && (priv->state != STATE_RESET_MODE)) {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "%s ", dev->name);
+ dev->stop(dev);
+ dev->open(dev);
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ } else {
+ len += snprintf(page + len, PAGE_SIZE - len,
+ "(%s|%d) ", dev->name,
+ priv->state);
+ }
+ }
+ }
+
+ len += snprintf(page + len, PAGE_SIZE - len, "done\n");
+
+ *eof = 1;
+ return len;
+}
+
+void can_proc_create(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde == NULL) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ pde = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_stats, NULL);
+ }
+ if (pde_regs == NULL) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ pde_regs = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_regs, NULL);
+ }
+ if (pde_reset == NULL) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ pde_reset = create_proc_read_entry(fname, 0644, NULL,
+ can_proc_read_reset, NULL);
+ }
+}
+
+void can_proc_remove(const char *drv_name)
+{
+ char fname[256];
+
+ if (pde) {
+ sprintf(fname, PROCBASE "/%s_stats", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_regs) {
+ sprintf(fname, PROCBASE "/%s_regs", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+ if (pde_reset) {
+ sprintf(fname, PROCBASE "/%s_reset", drv_name);
+ remove_proc_entry(fname, NULL);
+ }
+}
diff --git a/drivers/net/can/old/sja1000/sja1000.c b/drivers/net/can/old/sja1000/sja1000.c
new file mode 100644
index 000000000000..8d7185a2962b
--- /dev/null
+++ b/drivers/net/can/old/sja1000/sja1000.c
@@ -0,0 +1,1140 @@
+/*
+ * sja1000.c - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h> /* for struct can_device_stats */
+#include "sja1000.h"
+#include "hal.h"
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("LLCF/socketcan '" CHIP_NAME "' network device driver");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) ((priv->debug > 0) ? printk(args) : 0)
+/* logging in interrupt context! */
+#define iDBG(args...) ((priv->debug > 1) ? printk(args) : 0)
+#define iiDBG(args...) ((priv->debug > 2) ? printk(args) : 0)
+#else
+#define DBG(args...)
+#define iDBG(args...)
+#define iiDBG(args...)
+#endif
+
+char drv_name[DRV_NAME_LEN] = "undefined";
+
+/* driver and version information */
+static const char *drv_version = "0.1.1";
+static const char *drv_reldate = "2007-04-13";
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static const char *ecc_errors[] = {
+ NULL,
+ NULL,
+ "ID.28 to ID.28",
+ "start of frame",
+ "bit SRTR",
+ "bit IDE",
+ "ID.20 to ID.18",
+ "ID.17 to ID.13",
+ "CRC sequence",
+ "reserved bit 0",
+ "data field",
+ "data length code",
+ "bit RTR",
+ "reserved bit 1",
+ "ID.4 to ID.0",
+ "ID.12 to ID.5",
+ NULL,
+ "active error flag",
+ "intermission",
+ "tolerate dominant bits",
+ NULL,
+ NULL,
+ "passive error flag",
+ "error delimiter",
+ "CRC delimiter",
+ "acknowledge slot",
+ "end of frame",
+ "acknowledge delimiter",
+ "overload flag",
+ NULL,
+ NULL,
+ NULL
+};
+
+static const char *ecc_types[] = {
+ "bit error",
+ "form error",
+ "stuff error",
+ "other type of error"
+};
+#endif
+
+/* array of all can chips */
+struct net_device *can_dev[MAXDEV];
+
+/* module parameters */
+unsigned long base[MAXDEV] = { 0 }; /* hardware address */
+unsigned long rbase[MAXDEV] = { 0 }; /* (remapped) device address */
+unsigned int irq[MAXDEV] = { 0 };
+
+unsigned int speed[MAXDEV] = { DEFAULT_SPEED, DEFAULT_SPEED };
+unsigned int btr[MAXDEV] = { 0 };
+
+static int rx_probe[MAXDEV] = { 0 };
+static int clk = DEFAULT_HW_CLK;
+static int debug = 0;
+static int restart_ms = 100;
+static int echo = 1;
+
+static int base_n;
+static int irq_n;
+static int speed_n;
+static int btr_n;
+static int rx_probe_n;
+
+module_param_array(base, int, &base_n, 0);
+module_param_array(irq, int, &irq_n, 0);
+module_param_array(speed, int, &speed_n, 0);
+module_param_array(btr, int, &btr_n, 0);
+module_param_array(rx_probe, int, &rx_probe_n, 0);
+
+module_param(clk, int, 0);
+module_param(debug, int, 0);
+module_param(restart_ms, int, 0);
+module_param(echo, int, S_IRUGO);
+
+MODULE_PARM_DESC(base, "CAN controller base address");
+MODULE_PARM_DESC(irq, "CAN controller interrupt");
+MODULE_PARM_DESC(speed, "CAN bus bitrate");
+MODULE_PARM_DESC(btr, "Bit Timing Register value 0x<btr0><btr1>, e.g. 0x4014");
+MODULE_PARM_DESC(rx_probe, "switch to trx mode after correct msg receiption. (default off)");
+
+MODULE_PARM_DESC(clk, "CAN controller chip clock (default: 16MHz)");
+MODULE_PARM_DESC(debug, "set debug mask (default: 0)");
+MODULE_PARM_DESC(restart_ms, "restart chip on heavy bus errors / bus off after x ms (default 100ms)");
+MODULE_PARM_DESC(echo, "Echo sent frames. default: 1 (On)");
+
+/*
+ * CAN network devices *should* support a local echo functionality
+ * (see Documentation/networking/can.txt). To test the handling of CAN
+ * interfaces that do not support the local echo both driver types are
+ * implemented inside this sja1000 driver. In the case that the driver does
+ * not support the echo the IFF_ECHO remains clear in dev->flags.
+ * This causes the PF_CAN core to perform the echo as a fallback solution.
+ */
+
+/* function declarations */
+
+static void can_restart_dev(unsigned long data);
+static void chipset_init(struct net_device *dev, int wake);
+static void chipset_init_rx(struct net_device *dev);
+static void chipset_init_trx(struct net_device *dev);
+static void can_netdev_setup(struct net_device *dev);
+static struct net_device* can_create_netdev(int dev_num, int hw_regs);
+static int can_set_drv_name(void);
+int set_reset_mode(struct net_device *dev);
+
+static int sja1000_probe_chip(unsigned long base)
+{
+ if (base && (hw_readreg(base, 0) == 0xFF)) {
+ printk(KERN_INFO "%s: probing @0x%lX failed\n",
+ drv_name, base);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * set baud rate divisor values
+ */
+static void set_btr(struct net_device *dev, int btr0, int btr1)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* no bla bla when restarting the device */
+ if (priv->state == STATE_UNINITIALIZED)
+ printk(KERN_INFO "%s: setting BTR0=%02X BTR1=%02X\n",
+ dev->name, btr0, btr1);
+
+ hw_writereg(dev->base_addr, REG_BTR0, btr0);
+ hw_writereg(dev->base_addr, REG_BTR1, btr1);
+}
+
+/*
+ * calculate baud rate divisor values
+ */
+static void set_baud(struct net_device *dev, int baud, int clock)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ int error;
+ int brp;
+ int tseg;
+ int tseg1 = 0;
+ int tseg2 = 0;
+
+ int best_error = 1000000000;
+ int best_tseg = 0;
+ int best_brp = 0;
+ int best_baud = 0;
+
+ int SAM = (baud > 100000 ? 0 : 1);
+
+ clock >>= 1;
+
+ for (tseg = (0 + 0 + 2) * 2;
+ tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1;
+ tseg++) {
+ brp = clock / ((1 + tseg / 2) * baud) + tseg % 2;
+ if ((brp > 0) && (brp <= 64)) {
+ error = baud - clock / (brp * (1 + tseg / 2));
+ if (error < 0) {
+ error = -error;
+ }
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg / 2;
+ best_brp = brp - 1;
+ best_baud = clock / (brp * (1 + tseg / 2));
+ }
+ }
+ }
+ if (best_error && (baud / best_error < 10)) {
+ printk("%s: unable to set baud rate %d (ext clock %dHz)\n",
+ dev->name, baud, clock * 2);
+ return;
+// return -EINVAL;
+ }
+ tseg2 = best_tseg - (SAMPLE_POINT * (best_tseg + 1)) / 100;
+ if (tseg2 < 0) {
+ tseg2 = 0;
+ } else if (tseg2 > MAX_TSEG2) {
+ tseg2 = MAX_TSEG2;
+ }
+ tseg1 = best_tseg - tseg2 - 2;
+ if (tseg1 > MAX_TSEG1) {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg - tseg1 - 2;
+ }
+
+ priv->btr = ((best_brp | JUMPWIDTH)<<8) +
+ ((SAM << 7) | (tseg2 << 4) | tseg1);
+
+ printk(KERN_INFO "%s: calculated best baudrate: %d / btr is 0x%04X\n",
+ dev->name, best_baud, priv->btr);
+
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+// set_btr(dev, best_brp | JUMPWIDTH, (SAM << 7) | (tseg2 << 4) | tseg1);
+}
+
+int set_reset_mode(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ priv->can_stats.bus_error_at_init = priv->can_stats.bus_error;
+
+ /* disable interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_OFF);
+
+ for (i = 0; i < 10; i++) {
+ /* check reset bit */
+ if (status & MOD_RM) {
+ if (i > 1) {
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+ priv->state = STATE_RESET_MODE;
+ return 0;
+ }
+
+ hw_writereg(dev->base_addr, REG_MOD, MOD_RM); /* reset chip */
+ status = hw_readreg(dev->base_addr, REG_MOD);
+
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into reset mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ /* check reset bit */
+ if ((status & MOD_RM) == 0) {
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ if (i > 1) {
+ struct can_priv *priv = netdev_priv(dev);
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+#endif
+ return 0;
+ }
+
+ /* set chip to normal mode */
+ hw_writereg(dev->base_addr, REG_MOD, 0x00);
+ status = hw_readreg(dev->base_addr, REG_MOD);
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into normal mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+static int set_listen_mode(struct net_device *dev)
+{
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ /* check reset mode bit */
+ if ((status & MOD_RM) == 0) {
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ if (i > 1) {
+ struct can_priv *priv = netdev_priv(dev);
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+#endif
+ return 0;
+ }
+
+ /* set listen only mode, clear reset */
+ hw_writereg(dev->base_addr, REG_MOD, MOD_LOM);
+ status = hw_readreg(dev->base_addr, REG_MOD);
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into listen mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+/*
+ * initialize SJA1000 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init_regs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ /* go into Pelican mode, disable clkout, disable comparator */
+ hw_writereg(base, REG_CDR, 0xCF);
+
+ /* output control */
+ /* connected to external transceiver */
+ hw_writereg(base, REG_OCR, 0x1A);
+
+ /* set acceptance filter (accept all) */
+ hw_writereg(base, REG_ACCC0, 0x00);
+ hw_writereg(base, REG_ACCC1, 0x00);
+ hw_writereg(base, REG_ACCC2, 0x00);
+ hw_writereg(base, REG_ACCC3, 0x00);
+
+ hw_writereg(base, REG_ACCM0, 0xFF);
+ hw_writereg(base, REG_ACCM1, 0xFF);
+ hw_writereg(base, REG_ACCM2, 0xFF);
+ hw_writereg(base, REG_ACCM3, 0xFF);
+
+ /* set baudrate */
+ if (priv->btr) { /* no calculation when btr is provided */
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+ } else {
+ if (priv->speed == 0) {
+ priv->speed = DEFAULT_SPEED;
+ }
+ set_baud(dev, priv->speed * 1000, priv->clock);
+ }
+
+ /* output control */
+ /* connected to external transceiver */
+ hw_writereg(base, REG_OCR, 0x1A);
+}
+
+static void chipset_init(struct net_device *dev, int wake)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->rx_probe)
+ chipset_init_rx(dev); /* wait for valid reception first */
+ else
+ chipset_init_trx(dev);
+
+ if ((wake) && netif_queue_stopped(dev)) {
+ if (priv->echo_skb) { /* pending echo? */
+ kfree_skb(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+ netif_wake_queue(dev);
+ }
+}
+
+static void chipset_init_rx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* automatic bit rate detection */
+ set_listen_mode(dev);
+
+ priv->state = STATE_PROBE;
+
+ /* enable receive and error interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_RI | IRQ_EI);
+}
+
+static void chipset_init_trx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+
+ priv->state = STATE_ACTIVE;
+
+ /* enable all interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_ALL);
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ff ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [flags] [len] [can data (up to 8 bytes]
+ */
+static int can_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf = (struct can_frame*)skb->data;
+ unsigned long base = dev->base_addr;
+ uint8_t fi;
+ uint8_t dlc;
+ canid_t id;
+ uint8_t dreg;
+ int loop;
+ int i;
+
+ netif_stop_queue(dev);
+
+ fi = dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if (id & CAN_RTR_FLAG)
+ fi |= FI_RTR;
+
+ if (id & CAN_EFF_FLAG) {
+ fi |= FI_FF;
+ dreg = EFF_BUF;
+ hw_writereg(base, REG_FI, fi);
+ hw_writereg(base, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
+ hw_writereg(base, REG_ID2, (id & 0x001fe000) >> (5 + 8));
+ hw_writereg(base, REG_ID3, (id & 0x00001fe0) >> 5);
+ hw_writereg(base, REG_ID4, (id & 0x0000001f) << 3);
+ } else {
+ dreg = SFF_BUF;
+ hw_writereg(base, REG_FI, fi);
+ hw_writereg(base, REG_ID1, (id & 0x000007f8) >> 3);
+ hw_writereg(base, REG_ID2, (id & 0x00000007) << 5);
+ }
+
+ for (i = 0; i < dlc; i++) {
+ hw_writereg(base, dreg++, cf->data[i]);
+ }
+
+ hw_writereg(base, REG_CMR, CMD_TR);
+
+ stats->tx_bytes += dlc;
+
+ dev->trans_start = jiffies;
+
+ /* set flag whether this packet has to be looped back */
+ loop = skb->pkt_type == PACKET_LOOPBACK;
+
+ if (!echo || !loop) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (!priv->echo_skb) {
+ struct sock *srcsk = skb->sk;
+
+ if (atomic_read(&skb->users) != 1) {
+ struct sk_buff *old_skb = skb;
+
+ skb = skb_clone(old_skb, GFP_ATOMIC);
+ DBG(KERN_INFO "%s: %s: freeing old skbuff %p, "
+ "using new skbuff %p\n",
+ dev->name, __FUNCTION__, old_skb, skb);
+ kfree_skb(old_skb);
+ if (!skb) {
+ return 0;
+ }
+ } else
+ skb_orphan(skb);
+
+ skb->sk = srcsk;
+
+ /* make settings for echo to reduce code in irq context */
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->dev = dev;
+
+ /* save this skb for tx interrupt echo handling */
+ priv->echo_skb = skb;
+
+ } else {
+ /* locking problem with netif_stop_queue() ?? */
+ printk(KERN_ERR "%s: %s: occupied echo_skb!\n",
+ dev->name, __FUNCTION__ );
+ kfree_skb(skb);
+ }
+
+ return 0;
+}
+
+static void can_tx_timeout(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+
+ stats->tx_errors++;
+
+ /* do not conflict with e.g. bus error handling */
+ if (!(priv->timer.expires)){ /* no restart on the run */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ if (priv->echo_skb) { /* pending echo? */
+ kfree_skb(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+ netif_wake_queue(dev); /* wakeup here */
+ }
+ else
+ DBG(KERN_INFO "%s: %s: can_restart_dev already active.\n",
+ dev->name, __FUNCTION__ );
+
+}
+
+static void can_restart_on(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (!(priv->timer.expires)){ /* no restart on the run */
+
+ set_reset_mode(dev);
+
+ priv->timer.function = can_restart_dev;
+ priv->timer.data = (unsigned long) dev;
+
+ /* restart chip on persistent error in <xxx> ms */
+ priv->timer.expires = jiffies + (priv->restart_ms * HZ) / 1000;
+ add_timer(&priv->timer);
+
+ iDBG(KERN_INFO "%s: %s start (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+ } else
+ iDBG(KERN_INFO "%s: %s already (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+}
+
+static void can_restart_dev(unsigned long data)
+{
+ struct net_device *dev = (struct net_device*) data;
+ struct can_priv *priv = netdev_priv(dev);
+
+ DBG(KERN_INFO "%s: can_restart_dev (%ld)\n",
+ dev->name, jiffies);
+
+ /* mark inactive timer */
+ priv->timer.expires = 0;
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+
+#if 0
+/* the timerless version */
+
+static void can_restart_now(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+#endif
+
+static void can_rx(struct net_device *dev)
+{
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long base = dev->base_addr;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t fi;
+ uint8_t dreg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL) {
+ return;
+ }
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ fi = hw_readreg(base, REG_FI);
+ dlc = fi & 0x0F;
+
+ if (fi & FI_FF) {
+ /* extended frame format (EFF) */
+ dreg = EFF_BUF;
+ id = (hw_readreg(base, REG_ID1) << (5+16))
+ | (hw_readreg(base, REG_ID2) << (5+8))
+ | (hw_readreg(base, REG_ID3) << 5)
+ | (hw_readreg(base, REG_ID4) >> 3);
+ id |= CAN_EFF_FLAG;
+ } else {
+ /* standard frame format (SFF) */
+ dreg = SFF_BUF;
+ id = (hw_readreg(base, REG_ID1) << 3)
+ | (hw_readreg(base, REG_ID2) >> 5);
+ }
+
+ if (fi & FI_RTR)
+ id |= CAN_RTR_FLAG;
+
+ cf = (struct can_frame*)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++) {
+ cf->data[i] = hw_readreg(base, dreg++);
+ }
+ while (i < 8)
+ cf->data[i++] = 0;
+
+ /* release receive buffer */
+ hw_writereg(base, REG_CMR, CMD_RRB);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+/*
+ * SJA1000 interrupt handler
+ */
+static irqreturn_t can_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device*)dev_id;
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long base = dev->base_addr;
+ uint8_t isrc, status, ecc, alc;
+ int n = 0;
+
+ hw_preirq(dev);
+
+ iiDBG(KERN_INFO "%s: interrupt\n", dev->name);
+
+ if (priv->state == STATE_UNINITIALIZED) {
+ printk(KERN_ERR "%s: %s: uninitialized controller!\n",
+ dev->name, __FUNCTION__);
+ chipset_init(dev, 1); /* should be possible at this stage */
+ return IRQ_NONE;
+ }
+
+ if (priv->state == STATE_RESET_MODE) {
+ iiDBG(KERN_ERR "%s: %s: controller is in reset mode! "
+ "MOD=0x%02X IER=0x%02X IR=0x%02X SR=0x%02X!\n",
+ dev->name, __FUNCTION__, hw_readreg(base, REG_MOD),
+ hw_readreg(base, REG_IER), hw_readreg(base, REG_IR),
+ hw_readreg(base, REG_SR));
+ return IRQ_NONE;
+ }
+
+ while ((isrc = hw_readreg(base, REG_IR)) && (n < 20)) {
+ n++;
+ status = hw_readreg(base, REG_SR);
+
+ if (isrc & IRQ_WUI) {
+ /* wake-up interrupt */
+ priv->can_stats.wakeup++;
+ }
+ if (isrc & IRQ_TI) {
+ /* transmission complete interrupt */
+ stats->tx_packets++;
+
+ if (echo && priv->echo_skb) {
+ netif_rx(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+
+ netif_wake_queue(dev);
+ }
+ if (isrc & IRQ_RI) {
+ /* receive interrupt */
+
+ while (status & SR_RBS) {
+ can_rx(dev);
+ status = hw_readreg(base, REG_SR);
+ }
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ iDBG(KERN_INFO "%s: RI #%d#\n", dev->name, n);
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ if (isrc & IRQ_DOI) {
+ /* data overrun interrupt */
+ iiDBG(KERN_INFO "%s: data overrun isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: DOI #%d#\n", dev->name, n);
+ priv->can_stats.data_overrun++;
+ hw_writereg(base, REG_CMR, CMD_CDO); /* clear bit */
+ }
+ if (isrc & IRQ_EI) {
+ /* error warning interrupt */
+ iiDBG(KERN_INFO "%s: error warning isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EI #%d#\n", dev->name, n);
+ priv->can_stats.error_warning++;
+ if (status & SR_BS) {
+ printk(KERN_INFO "%s: BUS OFF, "
+ "restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ } else if (status & SR_ES) {
+ iDBG(KERN_INFO "%s: error\n", dev->name);
+ }
+ }
+ if (isrc & IRQ_BEI) {
+ /* bus error interrupt */
+ iiDBG(KERN_INFO "%s: bus error isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: BEI #%d# [%d]\n", dev->name, n,
+ priv->can_stats.bus_error -
+ priv->can_stats.bus_error_at_init);
+ priv->can_stats.bus_error++;
+ ecc = hw_readreg(base, REG_ECC);
+ iDBG(KERN_INFO "%s: ECC = 0x%02X (%s, %s, %s)\n",
+ dev->name, ecc,
+ (ecc & ECC_DIR) ? "RX" : "TX",
+ ecc_types[ecc >> ECC_ERR],
+ ecc_errors[ecc & ECC_SEG]);
+
+ /* when the bus errors flood the system, */
+ /* restart the controller */
+ if (priv->can_stats.bus_error_at_init +
+ MAX_BUS_ERRORS < priv->can_stats.bus_error) {
+ iDBG(KERN_INFO "%s: heavy bus errors,"
+ " restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ }
+#if 1
+ /* don't know, if this is a good idea, */
+ /* but it works fine ... */
+ if (hw_readreg(base, REG_RXERR) > 128) {
+ iDBG(KERN_INFO "%s: RX_ERR > 128,"
+ " restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ }
+#endif
+ }
+ if (isrc & IRQ_EPI) {
+ /* error passive interrupt */
+ iiDBG(KERN_INFO "%s: error passive isrc=0x%02X"
+ " status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EPI #%d#\n", dev->name, n);
+ priv->can_stats.error_passive++;
+ if (status & SR_ES) {
+ iDBG(KERN_INFO "%s: -> ERROR PASSIVE, "
+ "restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ } else {
+ iDBG(KERN_INFO "%s: -> ERROR ACTIVE\n",
+ dev->name);
+ }
+ }
+ if (isrc & IRQ_ALI) {
+ /* arbitration lost interrupt */
+ iiDBG(KERN_INFO "%s: error arbitration lost "
+ "isrc=0x%02X status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: ALI #%d#\n", dev->name, n);
+ priv->can_stats.arbitration_lost++;
+ alc = hw_readreg(base, REG_ALC);
+ iDBG(KERN_INFO "%s: ALC = 0x%02X\n", dev->name, alc);
+ }
+ }
+ if (n > 1) {
+ iDBG(KERN_INFO "%s: handled %d IRQs\n", dev->name, n);
+ }
+out:
+ hw_postirq(dev);
+
+ return n == 0 ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/*
+ * initialize CAN bus driver
+ */
+static int can_open(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->state = STATE_UNINITIALIZED;
+
+ /* register interrupt handler */
+ if (request_irq(dev->irq, &can_interrupt, IRQF_SHARED,
+ dev->name, (void*)dev)) {
+ return -EAGAIN;
+ }
+
+ /* init chip */
+ chipset_init(dev, 0);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * stop CAN bus activity
+ */
+static int can_close(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->open_time = 0;
+
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0;
+ }
+
+ free_irq(dev->irq, (void*)dev);
+ priv->state = STATE_UNINITIALIZED;
+
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+#if 0
+static void test_if(struct net_device *dev)
+{
+ int i;
+ int j;
+ int x;
+
+ hw_writereg(base, REG_CDR, 0xCF);
+ for (i = 0; i < 10000; i++) {
+ for (j = 0; j < 256; j++) {
+ hw_writereg(base, REG_EWL, j);
+ x = hw_readreg(base, REG_EWL);
+ if (x != j) {
+ printk(KERN_INFO "%s: is: %02X expected: "
+ "%02X (%d)\n", dev->name, x, j, i);
+ }
+ }
+ }
+}
+#endif
+
+void can_netdev_setup(struct net_device *dev)
+{
+ /* Fill in the the fields of the device structure
+ with CAN netdev generic values */
+
+ dev->change_mtu = NULL;
+ dev->set_mac_address = NULL;
+ dev->header_ops = NULL;
+
+ dev->type = ARPHRD_CAN;
+ dev->hard_header_len = 0;
+ dev->mtu = sizeof(struct can_frame);
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->flags = IFF_NOARP;
+
+ /* set flags according to driver capabilities */
+ if (echo)
+ dev->flags |= IFF_ECHO;
+
+ dev->features = NETIF_F_NO_CSUM;
+
+ dev->open = can_open;
+ dev->stop = can_close;
+ dev->hard_start_xmit = can_start_xmit;
+
+ dev->tx_timeout = can_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+}
+
+static struct net_device* can_create_netdev(int dev_num, int hw_regs)
+{
+ struct net_device *dev;
+ struct can_priv *priv;
+
+ if (!(dev = alloc_netdev(sizeof(struct can_priv), CAN_NETDEV_NAME,
+ can_netdev_setup))) {
+ printk(KERN_ERR "%s: out of memory\n", CHIP_NAME);
+ return NULL;
+ }
+
+ printk(KERN_INFO "%s: base 0x%lX / irq %d / speed %d / "
+ "btr 0x%X / rx_probe %d\n",
+ drv_name, rbase[dev_num], irq[dev_num],
+ speed[dev_num], btr[dev_num], rx_probe[dev_num]);
+
+ /* fill net_device structure */
+
+ priv = netdev_priv(dev);
+
+ dev->irq = irq[dev_num];
+ dev->base_addr = rbase[dev_num];
+
+ priv->speed = speed[dev_num];
+ priv->btr = btr[dev_num];
+ priv->rx_probe = rx_probe[dev_num];
+ priv->clock = clk;
+ priv->hw_regs = hw_regs;
+ priv->restart_ms = restart_ms;
+ priv->debug = debug;
+
+ init_timer(&priv->timer);
+ priv->timer.expires = 0;
+
+ if (register_netdev(dev)) {
+ printk(KERN_INFO "%s: register netdev failed\n", CHIP_NAME);
+ free_netdev(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+int can_set_drv_name(void)
+{
+ char *hname = hal_name();
+
+ if (strlen(CHIP_NAME) + strlen(hname) >= DRV_NAME_LEN-1) {
+ printk(KERN_ERR "%s: driver name too long!\n", CHIP_NAME);
+ return -EINVAL;
+ }
+ sprintf(drv_name, "%s-%s", CHIP_NAME, hname);
+ return 0;
+}
+
+static void sja1000_exit_module(void)
+{
+ int i, ret;
+
+ for (i = 0; i < MAXDEV; i++) {
+ if (can_dev[i] != NULL) {
+ struct can_priv *priv = netdev_priv(can_dev[i]);
+ unregister_netdev(can_dev[i]);
+ del_timer(&priv->timer);
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ free_netdev(can_dev[i]);
+ }
+ }
+ can_proc_remove(drv_name);
+
+ if ((ret = hal_exit()))
+ printk(KERN_INFO "%s: hal_exit error %d.\n", drv_name, ret);
+}
+
+static __init int sja1000_init_module(void)
+{
+ int i, ret;
+ struct net_device *dev;
+
+ if ((ret = hal_init()))
+ return ret;
+
+ if ((ret = can_set_drv_name()))
+ return ret;
+
+ if (clk < 1000 ) /* MHz command line value */
+ clk *= 1000000;
+
+ if (clk < 1000000 ) /* kHz command line value */
+ clk *= 1000;
+
+ printk(KERN_INFO "%s driver v%s (%s)\n",
+ drv_name, drv_version, drv_reldate);
+ printk(KERN_INFO "%s - options [clk %d.%06d MHz] [restart_ms %dms]"
+ " [debug %d]\n",
+ drv_name, clk/1000000, clk%1000000, restart_ms, debug);
+
+ if (!base[0]) {
+ printk(KERN_INFO "%s: loading defaults.\n", drv_name);
+ hal_use_defaults();
+ }
+
+ for (i = 0; base[i]; i++) {
+ printk(KERN_DEBUG "%s: checking for %s on address 0x%lX ...\n",
+ drv_name, CHIP_NAME, base[i]);
+
+ if (!hal_request_region(i, SJA1000_IO_SIZE_BASIC, drv_name)) {
+ printk(KERN_ERR "%s: memory already in use\n",
+ drv_name);
+ sja1000_exit_module();
+ return -EBUSY;
+ }
+
+ hw_attach(i);
+ hw_reset_dev(i);
+
+ if (!sja1000_probe_chip(rbase[i])) {
+ printk(KERN_ERR "%s: probably missing controller"
+ " hardware\n", drv_name);
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ sja1000_exit_module();
+ return -ENODEV;
+ }
+
+ dev = can_create_netdev(i, SJA1000_IO_SIZE_BASIC);
+
+ if (dev != NULL) {
+ can_dev[i] = dev;
+ set_reset_mode(dev);
+ can_proc_create(drv_name);
+ } else {
+ can_dev[i] = NULL;
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ }
+ }
+ return 0;
+}
+
+module_init(sja1000_init_module);
+module_exit(sja1000_exit_module);
+
diff --git a/drivers/net/can/old/sja1000/sja1000.h b/drivers/net/can/old/sja1000/sja1000.h
new file mode 100644
index 000000000000..a47b2f629136
--- /dev/null
+++ b/drivers/net/can/old/sja1000/sja1000.h
@@ -0,0 +1,187 @@
+/*
+ * sja1000.h - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef SJA1000_H
+#define SJA1000_H
+
+#define SJA1000_IO_SIZE_BASIC 0x20
+#define SJA1000_IO_SIZE_PELICAN 0x80 /* unused */
+
+#define CHIP_NAME "sja1000"
+
+#define DRV_NAME_LEN 30 /* for "<chip_name>-<hal_name>" */
+
+#define PROCBASE "driver" /* /proc/ ... */
+
+#define DEFAULT_HW_CLK 16000000
+#define DEFAULT_SPEED 500 /* kBit/s */
+
+#define CAN_NETDEV_NAME "can%d"
+
+#define TX_TIMEOUT (50*HZ/1000) /* 50ms */
+#define RESTART_MS 100 /* restart chip on persistent errors in 100ms */
+#define MAX_BUS_ERRORS 200 /* prevent from flooding bus error interrupts */
+
+/* SJA1000 registers - manual section 6.4 (Pelican Mode) */
+#define REG_MOD 0x00
+#define REG_CMR 0x01
+#define REG_SR 0x02
+#define REG_IR 0x03
+#define REG_IER 0x04
+#define REG_ALC 0x0B
+#define REG_ECC 0x0C
+#define REG_EWL 0x0D
+#define REG_RXERR 0x0E
+#define REG_TXERR 0x0F
+#define REG_ACCC0 0x10
+#define REG_ACCC1 0x11
+#define REG_ACCC2 0x12
+#define REG_ACCC3 0x13
+#define REG_ACCM0 0x14
+#define REG_ACCM1 0x15
+#define REG_ACCM2 0x16
+#define REG_ACCM3 0x17
+#define REG_RMC 0x1D
+#define REG_RBSA 0x1E
+
+/* Common registers - manual section 6.5 */
+#define REG_BTR0 0x06
+#define REG_BTR1 0x07
+#define REG_OCR 0x08
+#define REG_CDR 0x1F
+
+#define REG_FI 0x10
+#define SFF_BUF 0x13
+#define EFF_BUF 0x15
+
+#define FI_FF 0x80
+#define FI_RTR 0x40
+
+#define REG_ID1 0x11
+#define REG_ID2 0x12
+#define REG_ID3 0x13
+#define REG_ID4 0x14
+
+#define CAN_RAM 0x20
+
+/* mode register */
+#define MOD_RM 0x01
+#define MOD_LOM 0x02
+#define MOD_STM 0x04
+#define MOD_AFM 0x08
+#define MOD_SM 0x10
+
+/* commands */
+#define CMD_SRR 0x10
+#define CMD_CDO 0x08
+#define CMD_RRB 0x04
+#define CMD_AT 0x02
+#define CMD_TR 0x01
+
+/* interrupt sources */
+#define IRQ_BEI 0x80
+#define IRQ_ALI 0x40
+#define IRQ_EPI 0x20
+#define IRQ_WUI 0x10
+#define IRQ_DOI 0x08
+#define IRQ_EI 0x04
+#define IRQ_TI 0x02
+#define IRQ_RI 0x01
+#define IRQ_ALL 0xFF
+#define IRQ_OFF 0x00
+
+/* status register content */
+#define SR_BS 0x80
+#define SR_ES 0x40
+#define SR_TS 0x20
+#define SR_RS 0x10
+#define SR_TCS 0x08
+#define SR_TBS 0x04
+#define SR_DOS 0x02
+#define SR_RBS 0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_DIR 0x20
+#define ECC_SEG 0x1F
+#define ECC_ERR 6
+
+/* bus timing */
+#define MAX_TSEG1 15
+#define MAX_TSEG2 7
+#define SAMPLE_POINT 75
+#define JUMPWIDTH 0x40
+
+/* CAN private data structure */
+
+struct can_priv {
+ struct can_device_stats can_stats;
+ long open_time;
+ int clock;
+ int hw_regs;
+ int restart_ms;
+ int debug;
+ int speed;
+ int btr;
+ int rx_probe;
+ struct timer_list timer;
+ int state;
+ struct sk_buff *echo_skb;
+};
+
+#define STATE_UNINITIALIZED 0
+#define STATE_PROBE 1
+#define STATE_ACTIVE 2
+#define STATE_ERROR_ACTIVE 3
+#define STATE_ERROR_PASSIVE 4
+#define STATE_BUS_OFF 5
+#define STATE_RESET_MODE 6
+
+void can_proc_create(const char *drv_name);
+void can_proc_remove(const char *drv_name);
+
+#endif /* SJA1000_H */
diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile
new file mode 100644
index 000000000000..176393a0001d
--- /dev/null
+++ b/drivers/net/can/sja1000/Makefile
@@ -0,0 +1,30 @@
+#
+#
+
+ifeq ($(KERNELRELEASE),)
+
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+#EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/can/hal
+
+obj-$(CONFIG_CAN_SJA1000) += sja1000.o
+obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o
+obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o
+obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o
+obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o
+obj-$(CONFIG_CAN_IXXAT_PCI) += ixxat_pci.o
+obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o
+obj-$(CONFIG_CAN_PIPCAN) += pipcan.o
+obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o
+
+ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
+
+endif
diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c
new file mode 100644
index 000000000000..6076375e2a96
--- /dev/null
+++ b/drivers/net/can/sja1000/ems_pci.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2008 Markus Plessing <plessing@ems-wuensche.com>
+ * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "ems_pci"
+
+MODULE_AUTHOR("Sebastian Haas <haas@ems-wuenche.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-PCI/PCIe CAN cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-PCI/PCIe CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define EMS_PCI_MAX_CHAN 2
+
+struct ems_pci_card {
+ int channels;
+
+ struct pci_dev *pci_dev;
+ struct net_device *net_dev[EMS_PCI_MAX_CHAN];
+
+ void __iomem *conf_addr;
+ void __iomem *base_addr;
+};
+
+#define EMS_PCI_CAN_CLOCK (16000000 / 2)
+
+/*
+ * Register definitions and descriptions are from LinCAN 0.3.3.
+ *
+ * PSB4610 PITA-2 bridge control registers
+ */
+#define PITA2_ICR 0x00 /* Interrupt Control Register */
+#define PITA2_ICR_INT0 0x00000002 /* [RC] INT0 Active/Clear */
+#define PITA2_ICR_INT0_EN 0x00020000 /* [RW] Enable INT0 */
+
+#define PITA2_MISC 0x1c /* Miscellaneous Register */
+#define PITA2_MISC_CONFIG 0x04000000 /* Multiplexed parallel interface */
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define EMS_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 7
+ * (meaning direct oscillator output) because the second SJA1000 chip
+ * is driven by the first one CLKOUT output.
+ */
+#define EMS_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+#define EMS_PCI_MEM_SIZE 4096 /* Size of the remapped io-memory */
+#define EMS_PCI_CAN_BASE_OFFSET 0x400 /* offset where the controllers starts */
+#define EMS_PCI_CAN_CTRL_SIZE 0x200 /* memory size for each controller */
+
+#define EMS_PCI_PORT_BYTES 0x4 /* Each register occupies 4 bytes */
+
+#define EMS_PCI_VENDOR_ID 0x110a /* PCI device and vendor ID */
+#define EMS_PCI_DEVICE_ID 0x2104
+
+static struct pci_device_id ems_pci_tbl[] = {
+ {EMS_PCI_VENDOR_ID, EMS_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, ems_pci_tbl);
+
+/*
+ * Helper to read internal registers from card logic (not CAN)
+ */
+static u8 ems_pci_readb(struct ems_pci_card *card, unsigned int port)
+{
+ return readb((void __iomem *)card->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static u8 ems_pci_read_reg(struct net_device *dev, int port)
+{
+ return readb((void __iomem *)dev->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static void ems_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)dev->base_addr
+ + (port * EMS_PCI_PORT_BYTES));
+}
+
+static void ems_pci_post_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct ems_pci_card *card = (struct ems_pci_card *)priv->priv;
+
+ /* reset int flag of pita */
+ writel(PITA2_ICR_INT0_EN | PITA2_ICR_INT0, card->conf_addr
+ + PITA2_ICR);
+}
+
+/*
+ * Check if a CAN controller is present at the specified location
+ * by trying to set 'em into the PeliCAN mode
+ */
+static inline int ems_pci_check_chan(struct net_device *dev)
+{
+ unsigned char res;
+
+ /* Make sure SJA1000 is in reset mode */
+ ems_pci_write_reg(dev, REG_MOD, 1);
+
+ ems_pci_write_reg(dev, REG_CDR, CDR_PELICAN);
+
+ /* read reset-values */
+ res = ems_pci_read_reg(dev, REG_CDR);
+
+ if (res == CDR_PELICAN)
+ return 1;
+
+ return 0;
+}
+
+static void ems_pci_del_card(struct pci_dev *pdev)
+{
+ struct ems_pci_card *card = pci_get_drvdata(pdev);
+ struct net_device *dev;
+ int i = 0;
+
+ for (i = 0; i < card->channels; i++) {
+ dev = card->net_dev[i];
+
+ if (!dev)
+ continue;
+
+ dev_info(&pdev->dev, "Removing %s.\n", dev->name);
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+ }
+
+ if (card->base_addr != NULL)
+ pci_iounmap(card->pci_dev, card->base_addr);
+
+ if (card->conf_addr != NULL)
+ pci_iounmap(card->pci_dev, card->conf_addr);
+
+ kfree(card);
+
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static void ems_pci_card_reset(struct ems_pci_card *card)
+{
+ /* Request board reset */
+ writeb(0, card->base_addr);
+}
+
+/*
+ * Probe PCI device for EMS CAN signature and register each available
+ * CAN channel to SJA1000 Socket-CAN subsystem.
+ */
+static int __devinit ems_pci_add_card(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct sja1000_priv *priv;
+ struct net_device *dev;
+ struct ems_pci_card *card;
+ int err, i;
+
+ /* Enabling PCI device */
+ if (pci_enable_device(pdev) < 0) {
+ dev_err(&pdev->dev, "Enabling PCI device failed\n");
+ return -ENODEV;
+ }
+
+ /* Allocating card structures to hold addresses, ... */
+ card = kzalloc(sizeof(struct ems_pci_card), GFP_KERNEL);
+ if (card == NULL) {
+ dev_err(&pdev->dev, "Unable to allocate memory\n");
+ pci_disable_device(pdev);
+ return -ENOMEM;
+ }
+
+ pci_set_drvdata(pdev, card);
+
+ card->pci_dev = pdev;
+
+ card->channels = 0;
+
+ /* Remap PITA configuration space, and controller memory area */
+ card->conf_addr = pci_iomap(pdev, 0, EMS_PCI_MEM_SIZE);
+ if (card->conf_addr == NULL) {
+ err = -ENOMEM;
+
+ goto failure_cleanup;
+ }
+
+ card->base_addr = pci_iomap(pdev, 1, EMS_PCI_MEM_SIZE);
+ if (card->base_addr == NULL) {
+ err = -ENOMEM;
+
+ goto failure_cleanup;
+ }
+
+ /* Configure PITA-2 parallel interface (enable MUX) */
+ writel(PITA2_MISC_CONFIG, card->conf_addr + PITA2_MISC);
+
+ /* Check for unique EMS CAN signature */
+ if (ems_pci_readb(card, 0) != 0x55 ||
+ ems_pci_readb(card, 1) != 0xAA ||
+ ems_pci_readb(card, 2) != 0x01 ||
+ ems_pci_readb(card, 3) != 0xCB ||
+ ems_pci_readb(card, 4) != 0x11) {
+ dev_err(&pdev->dev, "Not EMS Dr. Thomas Wuensche interface\n");
+
+ err = -ENODEV;
+ goto failure_cleanup;
+ }
+
+ ems_pci_card_reset(card);
+
+ /* Detect available channels */
+ for (i = 0; i < EMS_PCI_MAX_CHAN; i++) {
+ dev = alloc_sja1000dev(0);
+ if (dev == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ card->net_dev[i] = dev;
+ priv = netdev_priv(dev);
+ priv->priv = card;
+
+ dev->irq = pdev->irq;
+ dev->base_addr = (unsigned long)(card->base_addr
+ + EMS_PCI_CAN_BASE_OFFSET
+ + (i * EMS_PCI_CAN_CTRL_SIZE));
+
+ /* Check if channel is present */
+ if (ems_pci_check_chan(dev)) {
+ priv->read_reg = ems_pci_read_reg;
+ priv->write_reg = ems_pci_write_reg;
+ priv->post_irq = ems_pci_post_irq;
+ priv->can.bittiming.clock = EMS_PCI_CAN_CLOCK;
+ priv->ocr = EMS_PCI_OCR;
+ priv->cdr = EMS_PCI_CDR;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Enable interrupts from card */
+ writel(PITA2_ICR_INT0_EN, card->conf_addr + PITA2_ICR);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Registering device failed "
+ "(err=%d)\n", err);
+ free_sja1000dev(dev);
+ goto failure_cleanup;
+ }
+
+ card->channels++;
+
+ dev_info(&pdev->dev, "Channel #%d at %#lX, irq %d\n",
+ i + 1, dev->base_addr,
+ dev->irq);
+ } else {
+ free_sja1000dev(dev);
+ }
+ }
+
+ return 0;
+
+failure_cleanup:
+ dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err);
+
+ ems_pci_del_card(pdev);
+
+ return err;
+}
+
+static struct pci_driver ems_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ems_pci_tbl,
+ .probe = ems_pci_add_card,
+ .remove = ems_pci_del_card,
+};
+
+static int __init ems_pci_init(void)
+{
+ return pci_register_driver(&ems_pci_driver);
+}
+
+static void __exit ems_pci_exit(void)
+{
+ pci_unregister_driver(&ems_pci_driver);
+}
+
+module_init(ems_pci_init);
+module_exit(ems_pci_exit);
+
diff --git a/drivers/net/can/sja1000/ems_pcmcia.c b/drivers/net/can/sja1000/ems_pcmcia.c
new file mode 100644
index 000000000000..7c9b88b30301
--- /dev/null
+++ b/drivers/net/can/sja1000/ems_pcmcia.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2008 Sebastian Haas <haas@ems-wuensche.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "ems_pcmcia"
+
+MODULE_AUTHOR("Sebastian Haas <haas@ems-wuenche.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for EMS CPC-CARD cards");
+MODULE_SUPPORTED_DEVICE("EMS CPC-CARD CAN card");
+MODULE_LICENSE("GPL v2");
+
+static int debug;
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+MODULE_PARM_DESC(debug, "Set debug level (default: 0)");
+
+#define EMS_PCMCIA_MAX_CHAN 2
+
+struct ems_pcmcia_card {
+ int channels;
+
+ struct pcmcia_device *pcmcia_dev;
+ struct net_device *net_dev[EMS_PCMCIA_MAX_CHAN];
+
+ void __iomem *base_addr;
+};
+
+#define EMS_PCMCIA_CAN_CLOCK (16000000 / 2)
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define EMS_PCMCIA_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 7
+ * (meaning direct oscillator output) because the second SJA1000 chip
+ * is driven by the first one CLKOUT output.
+ */
+#define EMS_PCMCIA_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+#define EMS_PCMCIA_MEM_SIZE 4096 /* Size of the remapped io-memory */
+#define EMS_PCMCIA_CAN_BASE_OFFSET 0x100 /* Offset where controllers starts */
+#define EMS_PCMCIA_CAN_CTRL_SIZE 0x80 /* Memory size for each controller */
+
+#define EMS_CMD_RESET 0x00 /* Perform a reset of the card */
+#define EMS_CMD_MAP 0x03 /* Map CAN controllers into card' memory */
+#define EMS_CMD_UMAP 0x02 /* Unmap CAN controllers from card' memory */
+
+static struct pcmcia_device_id ems_pcmcia_tbl[] = {
+ PCMCIA_DEVICE_PROD_ID123("EMS_T_W", "CPC-Card", "V2.0", 0xeab1ea23,
+ 0xa338573f, 0xe4575800),
+ PCMCIA_DEVICE_NULL,
+};
+
+MODULE_DEVICE_TABLE (pcmcia, ems_pcmcia_tbl);
+
+static void ems_pcmcia_config(struct pcmcia_device *dev);
+
+static u8 ems_pcmcia_read_reg(struct net_device *dev, int port)
+{
+ return readb((void __iomem *)dev->base_addr + port);
+}
+
+static void ems_pcmcia_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)dev->base_addr + port);
+}
+
+static irqreturn_t ems_pcmcia_interrupt(int irq, void *dev_id)
+{
+ struct ems_pcmcia_card *card = dev_id;
+ struct net_device *dev;
+ irqreturn_t retval = IRQ_NONE;
+ int i, again;
+
+ do {
+ again = 0;
+
+ /* Check interrupt for each channel */
+ for (i = 0; i < EMS_PCMCIA_MAX_CHAN; i++) {
+ dev = card->net_dev[i];
+ if (!dev)
+ continue;
+
+ if (sja1000_interrupt(irq, dev) == IRQ_HANDLED)
+ again = 1;
+ }
+ /* At least one channel handled the interrupt */
+ if (again)
+ retval = IRQ_HANDLED;
+
+ } while (again);
+
+ return retval;
+}
+
+/*
+ * Check if a CAN controller is present at the specified location
+ * by trying to set 'em into the PeliCAN mode
+ */
+static inline int ems_pcmcia_check_chan(struct net_device *dev)
+{
+ unsigned char res;
+
+ /* Make sure SJA1000 is in reset mode */
+ ems_pcmcia_write_reg(dev, REG_MOD, 1);
+
+ ems_pcmcia_write_reg(dev, REG_CDR, CDR_PELICAN);
+
+ /* read reset-values */
+ res = ems_pcmcia_read_reg(dev, REG_CDR);
+
+ if (res == CDR_PELICAN)
+ return 1;
+
+ return 0;
+}
+
+static void ems_pcmcia_del_card(struct pcmcia_device *pdev)
+{
+ struct ems_pcmcia_card *card = pdev->priv;
+ struct net_device *dev;
+ int i = 0;
+
+ if (!card)
+ return;
+
+ free_irq(pdev->irq.AssignedIRQ, card);
+
+ for (i = 0; i < card->channels; i++) {
+ dev = card->net_dev[i];
+
+ if (!dev)
+ continue;
+
+ printk(KERN_INFO "%s: removing %s on channel #%d\n",
+ DRV_NAME, dev->name, i);
+ unregister_sja1000dev(dev);
+ free_sja1000dev(dev);
+ }
+
+ writeb(EMS_CMD_UMAP, card->base_addr);
+
+ if (card->base_addr != NULL )
+ iounmap(card->base_addr);
+
+ kfree(card);
+
+ pdev->priv = NULL;
+}
+
+static void ems_pcmcia_card_reset(struct ems_pcmcia_card *card)
+{
+ /* Request board reset */
+ writeb(EMS_CMD_RESET, card->base_addr);
+}
+
+/*
+ * Probe PCI device for EMS CAN signature and register each available
+ * CAN channel to SJA1000 Socket-CAN subsystem.
+ */
+static int __devinit ems_pcmcia_add_card(struct pcmcia_device *pdev,
+ unsigned long base)
+{
+ struct sja1000_priv *priv;
+ struct net_device *dev;
+ struct ems_pcmcia_card *card;
+ int err, i;
+
+ /* Allocating card structures to hold addresses, ... */
+ card = kzalloc(sizeof(struct ems_pcmcia_card), GFP_KERNEL);
+ if (card == NULL) {
+ printk(KERN_ERR "%s: unable to allocate memory\n", DRV_NAME);
+ return -ENOMEM;
+ }
+
+ pdev->priv = card;
+
+ card->channels = 0;
+
+ card->base_addr = ioremap(base, EMS_PCMCIA_MEM_SIZE);
+ if (card->base_addr == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ /* Check for unique EMS CAN signature */
+ if (readw(card->base_addr) != 0xAA55) {
+ printk(KERN_ERR "%s: No EMS CPC Card hardware found.\n",
+ DRV_NAME);
+
+ err = -ENODEV;
+ goto failure_cleanup;
+ }
+
+ ems_pcmcia_card_reset(card);
+
+ /* Make sure CAN controllers are mapped into card's memory space */
+ writeb(EMS_CMD_MAP, card->base_addr);
+
+ /* Detect available channels */
+ for (i = 0; i < EMS_PCMCIA_MAX_CHAN; i++) {
+ dev = alloc_sja1000dev(0);
+ if (dev == NULL) {
+ err = -ENOMEM;
+ goto failure_cleanup;
+ }
+
+ card->net_dev[i] = dev;
+ priv = netdev_priv(dev);
+ priv->priv = card;
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->irq = pdev->irq.AssignedIRQ;
+ dev->base_addr = (unsigned long)(card->base_addr
+ + EMS_PCMCIA_CAN_BASE_OFFSET
+ + (i * EMS_PCMCIA_CAN_CTRL_SIZE));
+
+ /* Check if channel is present */
+ if (ems_pcmcia_check_chan(dev)) {
+ priv->read_reg = ems_pcmcia_read_reg;
+ priv->write_reg = ems_pcmcia_write_reg;
+ priv->can.bittiming.clock = EMS_PCMCIA_CAN_CLOCK;
+ priv->ocr = EMS_PCMCIA_OCR;
+ priv->cdr = EMS_PCMCIA_CDR;
+ priv->flags |= SJA1000_CUSTOM_IRQ_HANDLER;
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ printk(KERN_INFO "%s: registering device "
+ "failed (err=%d)\n", DRV_NAME, err);
+ free_sja1000dev(dev);
+ goto failure_cleanup;
+ }
+
+ card->channels++;
+
+ printk(KERN_INFO "%s: registered %s on channel "
+ "#%d at %lX, irq %d\n", DRV_NAME, dev->name,
+ i, dev->base_addr, dev->irq);
+ } else {
+ free_sja1000dev(dev);
+ }
+ }
+
+ err = request_irq(dev->irq, &ems_pcmcia_interrupt, IRQF_SHARED,
+ DRV_NAME, (void *)card);
+ if (err) {
+ printk(KERN_INFO "Registering device failed (err=%d)\n", err);
+
+ goto failure_cleanup;
+ }
+
+ return 0;
+
+failure_cleanup:
+ printk(KERN_ERR "Error: %d. Cleaning Up.\n", err);
+
+ ems_pcmcia_del_card(pdev);
+
+ return err;
+}
+
+/*
+ * Setup PCMCIA socket and probe for EMS CPC-CARD
+ */
+static int __devinit ems_pcmcia_probe(struct pcmcia_device *dev)
+{
+ /* The io structure describes IO port mapping */
+ dev->io.NumPorts1 = 16;
+ dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ dev->io.NumPorts2 = 16;
+ dev->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
+ dev->io.IOAddrLines = 5;
+
+ /* Interrupt setup */
+ dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+ /* General socket configuration */
+ dev->conf.Attributes = CONF_ENABLE_IRQ;
+ dev->conf.IntType = INT_MEMORY_AND_IO;
+ dev->conf.ConfigIndex = 1;
+ dev->conf.Present = PRESENT_OPTION;
+
+ dev->win = NULL;
+
+ ems_pcmcia_config(dev);
+
+ return 0;
+}
+
+/*
+ * Configure PCMCIA socket
+ */
+static void ems_pcmcia_config(struct pcmcia_device *dev)
+{
+ win_req_t req;
+ memreq_t mem;
+
+ int csval;
+
+ /* Allocate a memory window */
+ req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+ req.Base = req.Size = 0;
+ req.AccessSpeed = 0;
+
+ csval = pcmcia_request_window(&dev, &req, &dev->win);
+ if (csval) {
+ cs_error(dev, RequestWindow, csval);
+ return;
+ }
+
+ mem.CardOffset = mem.Page = 0;
+ mem.CardOffset = dev->conf.ConfigBase;
+
+ csval = pcmcia_map_mem_page(dev->win, &mem);
+ if (csval) {
+ cs_error(dev, MapMemPage, csval);
+ return;
+ }
+
+ csval = pcmcia_request_irq(dev, &dev->irq);
+ if (csval) {
+ cs_error(dev, RequestIRQ, csval);
+ return;
+ }
+
+ /* This actually configures the PCMCIA socket */
+ csval = pcmcia_request_configuration(dev, &dev->conf);
+ if (csval) {
+ cs_error(dev, RequestConfiguration, csval);
+ return;
+ }
+
+ ems_pcmcia_add_card(dev, req.Base);
+}
+
+/*
+ * Release claimed resources
+ */
+static void ems_pcmcia_remove(struct pcmcia_device *dev)
+{
+ ems_pcmcia_del_card(dev);
+
+ pcmcia_disable_device(dev);
+}
+
+/*
+ * The dev_info variable is the "key" that is used to match up this
+ * device driver with appropriate cards, through the card configuration
+ * database.
+ */
+static dev_info_t dev_info = "can-ems-pcmcia";
+
+static struct pcmcia_driver ems_pcmcia_driver = {
+ .drv = {
+ .name = dev_info,
+ },
+
+ .probe = ems_pcmcia_probe,
+ .remove = ems_pcmcia_remove,
+
+ .id_table = ems_pcmcia_tbl,
+};
+
+static int __init ems_pcmcia_init(void)
+{
+ return pcmcia_register_driver(&ems_pcmcia_driver);
+}
+
+static void __exit ems_pcmcia_exit(void)
+{
+ pcmcia_unregister_driver(&ems_pcmcia_driver);
+}
+
+module_init(ems_pcmcia_init);
+module_exit(ems_pcmcia_exit);
+
diff --git a/drivers/net/can/sja1000/ixxat_pci.c b/drivers/net/can/sja1000/ixxat_pci.c
new file mode 100644
index 000000000000..ab98a9d160ec
--- /dev/null
+++ b/drivers/net/can/sja1000/ixxat_pci.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "ixxat_pci"
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
+MODULE_DESCRIPTION("Socket-CAN driver for IXXAT PC-I 04/PCI PCI cards");
+MODULE_SUPPORTED_DEVICE("IXXAT PC-I 04/PCI card");
+MODULE_LICENSE("GPL v2");
+
+/* Maximum number of interfaces supported on one card. Currently
+ * we only support a maximum of two interfaces, which is the maximum
+ * of what Ixxat sells anyway.
+ */
+#define IXXAT_PCI_MAX_CAN 2
+
+struct ixxat_pci {
+ struct pci_dev *pci_dev;
+ struct net_device *dev[IXXAT_PCI_MAX_CAN];
+ int conf_addr;
+ void __iomem *base_addr;
+};
+
+#define IXXAT_PCI_CAN_CLOCK (16000000 / 2)
+
+#define IXXAT_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX0_INVERT | \
+ OCR_TX1_PUSHPULL)
+#define IXXAT_PCI_CDR 0
+
+#define CHANNEL_RESET_OFFSET 0x110
+#define CHANNEL_OFFSET 0x200
+
+#define INTCSR_OFFSET 0x4c /* Offset in PLX9050 conf registers */
+#define INTCSR_LINTI1 (1 << 0)
+#define INTCSR_LINTI2 (1 << 3)
+#define INTCSR_PCI (1 << 6)
+
+/* PCI vender, device and sub-device ID */
+#define IXXAT_PCI_VENDOR_ID 0x10b5
+#define IXXAT_PCI_DEVICE_ID 0x9050
+#define IXXAT_PCI_SUB_SYS_ID 0x2540
+
+#define IXXAT_PCI_BASE_SIZE 0x400
+
+static struct pci_device_id ixxat_pci_tbl[] = {
+ {IXXAT_PCI_VENDOR_ID, IXXAT_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, ixxat_pci_tbl);
+
+static u8 ixxat_pci_read_reg(struct net_device *ndev, int port)
+{
+ u8 val;
+ val = readb((void __iomem *)(ndev->base_addr + port));
+ return val;
+}
+
+static void ixxat_pci_write_reg(struct net_device *ndev, int port, u8 val)
+{
+ writeb(val, (void __iomem *)(ndev->base_addr + port));
+}
+
+static void ixxat_pci_del_chan(struct pci_dev *pdev, struct net_device *ndev)
+{
+ dev_info(&pdev->dev, "Removing device %s\n", ndev->name);
+
+ unregister_sja1000dev(ndev);
+
+ free_sja1000dev(ndev);
+}
+
+static struct net_device *ixxat_pci_add_chan(struct pci_dev *pdev,
+ void __iomem *base_addr)
+{
+ struct net_device *ndev;
+ struct sja1000_priv *priv;
+ int err;
+
+ ndev = alloc_sja1000dev(0);
+ if (ndev == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ priv = netdev_priv(ndev);
+
+ ndev->base_addr = (unsigned long)base_addr;
+
+ priv->read_reg = ixxat_pci_read_reg;
+ priv->write_reg = ixxat_pci_write_reg;
+
+ priv->can.bittiming.clock = IXXAT_PCI_CAN_CLOCK;
+
+ priv->ocr = IXXAT_PCI_OCR;
+ priv->cdr = IXXAT_PCI_CDR;
+
+ /* Set and enable PCI interrupts */
+ ndev->irq = pdev->irq;
+
+ dev_dbg(&pdev->dev, "base_addr=%#lx irq=%d\n",
+ ndev->base_addr, ndev->irq);
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ err = register_sja1000dev(ndev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register (err=%d)\n", err);
+ goto failure;
+ }
+
+ return ndev;
+
+failure:
+ free_sja1000dev(ndev);
+ return ERR_PTR(err);
+}
+
+static int __devinit ixxat_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct ixxat_pci *board;
+ int err, intcsr = INTCSR_LINTI1 | INTCSR_PCI;
+ u16 sub_sys_id;
+ void __iomem *base_addr;
+
+ dev_info(&pdev->dev, "Initializing device %04x:%04x\n",
+ pdev->vendor, pdev->device);
+
+ board = kzalloc(sizeof(*board), GFP_KERNEL);
+ if (!board)
+ return -ENOMEM;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto failure;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure;
+
+ err = pci_read_config_word(pdev, 0x2e, &sub_sys_id);
+ if (err)
+ goto failure_release_pci;
+
+ if (sub_sys_id != IXXAT_PCI_SUB_SYS_ID)
+ return -ENODEV;
+
+ /* Enable memory and I/O space */
+ err = pci_write_config_word(pdev, 0x04, 0x3);
+ if (err)
+ goto failure_release_pci;
+
+ board->conf_addr = pci_resource_start(pdev, 1);
+
+ base_addr = pci_iomap(pdev, 2, IXXAT_PCI_BASE_SIZE);
+ if (base_addr == NULL) {
+ err = -ENODEV;
+ goto failure_release_pci;
+ }
+
+ board->base_addr = base_addr;
+
+ writeb(0x1, base_addr + CHANNEL_RESET_OFFSET);
+ writeb(0x1, base_addr + CHANNEL_OFFSET + CHANNEL_RESET_OFFSET);
+ udelay(100);
+
+ board->dev[0] = ixxat_pci_add_chan(pdev, base_addr);
+ if (IS_ERR(board->dev[0]))
+ goto failure_iounmap;
+
+ /* Check if second channel is available */
+ if ((readb(base_addr + CHANNEL_OFFSET + REG_MOD) & 0xa1) == 0x21 &&
+ readb(base_addr + CHANNEL_OFFSET + REG_SR) == 0x0c &&
+ readb(base_addr + CHANNEL_OFFSET + REG_IR) == 0xe0) {
+ board->dev[1] = ixxat_pci_add_chan(pdev,
+ base_addr + CHANNEL_OFFSET);
+ if (IS_ERR(board->dev[1]))
+ goto failure_unreg_dev0;
+
+ intcsr |= INTCSR_LINTI2;
+ }
+
+ /* enable interrupt(s) in PLX9050 */
+ outb(intcsr, board->conf_addr + INTCSR_OFFSET);
+
+ pci_set_drvdata(pdev, board);
+
+ return 0;
+
+failure_unreg_dev0:
+ ixxat_pci_del_chan(pdev, board->dev[0]);
+
+failure_iounmap:
+ pci_iounmap(pdev, board->base_addr);
+
+failure_release_pci:
+ pci_release_regions(pdev);
+
+failure:
+ kfree(board);
+
+ return err;
+}
+
+static void __devexit ixxat_pci_remove_one(struct pci_dev *pdev)
+{
+ struct ixxat_pci *board = pci_get_drvdata(pdev);
+ int i;
+
+ /* Disable interrupts in PLX9050*/
+ outb(0, board->conf_addr + INTCSR_OFFSET);
+
+ for (i = 0; i < IXXAT_PCI_MAX_CAN; i++) {
+ if (!board->dev[i])
+ break;
+ ixxat_pci_del_chan(pdev, board->dev[i]);
+ }
+
+ pci_iounmap(pdev, board->base_addr);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+
+ kfree(board);
+}
+
+static struct pci_driver ixxat_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = ixxat_pci_tbl,
+ .probe = ixxat_pci_init_one,
+ .remove = __devexit_p(ixxat_pci_remove_one),
+};
+
+static int __init ixxat_pci_init(void)
+{
+ return pci_register_driver(&ixxat_pci_driver);
+}
+
+static void __exit ixxat_pci_exit(void)
+{
+ pci_unregister_driver(&ixxat_pci_driver);
+}
+
+module_init(ixxat_pci_init);
+module_exit(ixxat_pci_exit);
diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c
new file mode 100644
index 000000000000..0b01ea3d5560
--- /dev/null
+++ b/drivers/net/can/sja1000/kvaser_pci.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2008 Per Dalen <per.dalen@cnw.se>
+ *
+ * Parts of this software are based on (derived) the following:
+ *
+ * - Kvaser linux driver, version 4.72 BETA
+ * Copyright (C) 2002-2007 KVASER AB
+ *
+ * - Lincan driver, version 0.3.3, OCERA project
+ * Copyright (C) 2004 Pavel Pisa
+ * Copyright (C) 2001 Arnaud Westenberg
+ *
+ * - Socketcan SJA1000 drivers
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "kvaser_pci"
+
+MODULE_AUTHOR("Per Dalen <per.dalen@cnw.se>");
+MODULE_DESCRIPTION("Socket-CAN driver for KVASER PCAN PCI cards");
+MODULE_SUPPORTED_DEVICE("KVASER PCAN PCI CAN card");
+MODULE_LICENSE("GPL v2");
+
+#define MAX_NO_OF_CHANNELS 4 /* max no of channels on
+ a single card */
+
+struct kvaser_pci {
+ int channel;
+ struct pci_dev *pci_dev;
+ struct net_device *slave_dev[MAX_NO_OF_CHANNELS-1];
+ void __iomem *conf_addr;
+ void __iomem *res_addr;
+ int no_channels;
+ u8 xilinx_ver;
+};
+
+#define KVASER_PCI_CAN_CLOCK (16000000 / 2)
+
+/*
+ * The board configuration is probably following:
+ * RX1 is connected to ground.
+ * TX1 is not connected.
+ * CLKO is not connected.
+ * Setting the OCR register to 0xDA is a good idea.
+ * This means normal output mode , push-pull and the correct polarity.
+ */
+#define KVASER_PCI_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)
+
+/*
+ * In the CDR register, you should set CBP to 1.
+ * You will probably also want to set the clock divider value to 0
+ * (meaning divide-by-2), the Pelican bit, and the clock-off bit
+ * (you will have no need for CLKOUT anyway).
+ */
+#define KVASER_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK)
+
+/*
+ * These register values are valid for revision 14 of the Xilinx logic.
+ */
+#define XILINX_VERINT 7 /* Lower nibble simulate interrupts,
+ high nibble version number. */
+
+#define XILINX_PRESUMED_VERSION 14
+
+/*
+ * Important S5920 registers
+ */
+#define S5920_INTCSR 0x38
+#define S5920_PTCR 0x60
+#define INTCSR_ADDON_INTENABLE_M 0x2000
+
+
+#define KVASER_PCI_PORT_BYTES 0x20
+
+#define PCI_CONFIG_PORT_SIZE 0x80 /* size of the config io-memory */
+#define PCI_PORT_SIZE 0x80 /* size of a channel io-memory */
+#define PCI_PORT_XILINX_SIZE 0x08 /* size of a xilinx io-memory */
+
+#define KVASER_PCI_VENDOR_ID1 0x10e8 /* the PCI device and vendor IDs */
+#define KVASER_PCI_DEVICE_ID1 0x8406
+
+#define KVASER_PCI_VENDOR_ID2 0x1a07 /* the PCI device and vendor IDs */
+#define KVASER_PCI_DEVICE_ID2 0x0008
+
+static struct pci_device_id kvaser_pci_tbl[] = {
+ {KVASER_PCI_VENDOR_ID1, KVASER_PCI_DEVICE_ID1, PCI_ANY_ID, PCI_ANY_ID,},
+ {KVASER_PCI_VENDOR_ID2, KVASER_PCI_DEVICE_ID2, PCI_ANY_ID, PCI_ANY_ID,},
+ { 0,}
+};
+
+MODULE_DEVICE_TABLE(pci, kvaser_pci_tbl);
+
+static u8 kvaser_pci_read_reg(struct net_device *dev, int port)
+{
+ return ioread8((void __iomem *)(dev->base_addr + port));
+}
+
+static void kvaser_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ iowrite8(val, (void __iomem *)(dev->base_addr + port));
+}
+
+static void kvaser_pci_disable_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct kvaser_pci *board = priv->priv;
+ u32 tmp;
+
+ /* Disable interrupts from card */
+ tmp = ioread32(board->conf_addr + S5920_INTCSR);
+ tmp &= ~INTCSR_ADDON_INTENABLE_M;
+ iowrite32(tmp, board->conf_addr + S5920_INTCSR);
+}
+
+static void kvaser_pci_enable_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct kvaser_pci *board = priv->priv;
+ u32 tmp;
+
+ /* Enable interrupts from card */
+ tmp = ioread32(board->conf_addr + S5920_INTCSR);
+ tmp |= INTCSR_ADDON_INTENABLE_M;
+ iowrite32(tmp, board->conf_addr + S5920_INTCSR);
+}
+
+static int number_of_sja1000_chip(void __iomem *base_addr)
+{
+ u8 status;
+ int i;
+
+ for (i = 0; i < MAX_NO_OF_CHANNELS; i++) {
+ /* reset chip */
+ iowrite8(MOD_RM, base_addr +
+ (i * KVASER_PCI_PORT_BYTES) + REG_MOD);
+ status = ioread8(base_addr +
+ (i * KVASER_PCI_PORT_BYTES) + REG_MOD);
+ udelay(10);
+ /* check reset bit */
+ if (!(status & MOD_RM))
+ break;
+ }
+
+ return i;
+}
+
+static void kvaser_pci_del_chan(struct net_device *dev)
+{
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int i;
+
+ if (!dev)
+ return;
+ priv = netdev_priv(dev);
+ if (!priv)
+ return;
+ board = priv->priv;
+ if (!board)
+ return;
+
+ dev_info(&board->pci_dev->dev, "Removing device %s\n",
+ dev->name);
+
+ for (i = 0; i < board->no_channels - 1; i++) {
+ if (board->slave_dev[i]) {
+ dev_info(&board->pci_dev->dev, "Removing device %s\n",
+ board->slave_dev[i]->name);
+ unregister_sja1000dev(board->slave_dev[i]);
+ free_sja1000dev(board->slave_dev[i]);
+ }
+ }
+ unregister_sja1000dev(dev);
+
+ /* Disable PCI interrupts */
+ kvaser_pci_disable_irq(dev);
+
+ pci_iounmap(board->pci_dev, (void __iomem *)dev->base_addr);
+ pci_iounmap(board->pci_dev, board->conf_addr);
+ pci_iounmap(board->pci_dev, board->res_addr);
+
+ free_sja1000dev(dev);
+}
+
+static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel,
+ struct net_device **master_dev,
+ void __iomem *conf_addr,
+ void __iomem *res_addr,
+ unsigned long base_addr)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int err, init_step;
+
+ dev = alloc_sja1000dev(sizeof(struct kvaser_pci));
+ if (dev == NULL)
+ return -ENOMEM;
+
+ priv = netdev_priv(dev);
+ board = priv->priv;
+
+ board->pci_dev = pdev;
+ board->channel = channel;
+
+ /*S5920*/
+ board->conf_addr = conf_addr;
+
+ /*XILINX board wide address*/
+ board->res_addr = res_addr;
+
+ if (channel == 0) {
+ board->xilinx_ver =
+ ioread8(board->res_addr + XILINX_VERINT) >> 4;
+ init_step = 2;
+
+ /* Assert PTADR# - we're in passive mode so the other bits are
+ not important */
+ iowrite32(0x80808080UL, board->conf_addr + S5920_PTCR);
+
+ /* Disable interrupts from card */
+ kvaser_pci_disable_irq(dev);
+ /* Enable interrupts from card */
+ kvaser_pci_enable_irq(dev);
+ } else {
+ struct sja1000_priv *master_priv = netdev_priv(*master_dev);
+ struct kvaser_pci *master_board = master_priv->priv;
+ master_board->slave_dev[channel - 1] = dev;
+ master_board->no_channels = channel + 1;
+ board->xilinx_ver = master_board->xilinx_ver;
+ }
+
+ dev->base_addr = base_addr + channel * KVASER_PCI_PORT_BYTES;
+
+ priv->read_reg = kvaser_pci_read_reg;
+ priv->write_reg = kvaser_pci_write_reg;
+
+ priv->can.bittiming.clock = KVASER_PCI_CAN_CLOCK;
+
+ priv->ocr = KVASER_PCI_OCR;
+ priv->cdr = KVASER_PCI_CDR;
+
+ /* Register and setup interrupt handling */
+ dev->irq = pdev->irq;
+ init_step = 4;
+
+ dev_info(&pdev->dev, "base_addr=%#lx conf_addr=%p irq=%d\n",
+ dev->base_addr, board->conf_addr, dev->irq);
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Registering device failed (err=%d)\n",
+ err);
+ goto failure;
+ }
+
+ if (channel == 0)
+ *master_dev = dev;
+
+ return 0;
+
+failure:
+ kvaser_pci_del_chan(dev);
+ return err;
+}
+
+static int __devinit kvaser_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err;
+ struct net_device *master_dev = NULL;
+ struct sja1000_priv *priv;
+ struct kvaser_pci *board;
+ int no_channels;
+ void __iomem *base_addr = NULL;
+ void __iomem *conf_addr = NULL;
+ void __iomem *res_addr = NULL;
+ int i;
+
+ dev_info(&pdev->dev, "initializing device %04x:%04x\n",
+ pdev->vendor, pdev->device);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto failure;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure_release_pci;
+
+ /*S5920*/
+ conf_addr = pci_iomap(pdev, 0, PCI_CONFIG_PORT_SIZE);
+ if (conf_addr == NULL) {
+ err = -ENODEV;
+ goto failure_iounmap;
+ }
+
+ /*XILINX board wide address*/
+ res_addr = pci_iomap(pdev, 2, PCI_PORT_XILINX_SIZE);
+ if (res_addr == NULL) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ base_addr = pci_iomap(pdev, 1, PCI_PORT_SIZE);
+ if (base_addr == NULL) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ no_channels = number_of_sja1000_chip(base_addr);
+ if (no_channels == 0) {
+ err = -ENOMEM;
+ goto failure_iounmap;
+ }
+
+ for (i = 0; i < no_channels; i++) {
+ err = kvaser_pci_add_chan(pdev, i, &master_dev,
+ conf_addr, res_addr,
+ (unsigned long)base_addr);
+ if (err)
+ goto failure_cleanup;
+ }
+
+ priv = netdev_priv(master_dev);
+ board = priv->priv;
+
+ dev_info(&pdev->dev, "xilinx version=%d number of channels=%d\n",
+ board->xilinx_ver, board->no_channels);
+
+ pci_set_drvdata(pdev, master_dev);
+ return 0;
+
+failure_cleanup:
+ kvaser_pci_del_chan(master_dev);
+
+failure_iounmap:
+ if (conf_addr == NULL)
+ pci_iounmap(pdev, conf_addr);
+ if (res_addr == NULL)
+ pci_iounmap(pdev, res_addr);
+ if (base_addr == NULL)
+ pci_iounmap(pdev, base_addr);
+
+ pci_release_regions(pdev);
+
+failure_release_pci:
+ pci_disable_device(pdev);
+
+failure:
+ return err;
+
+}
+
+static void __devexit kvaser_pci_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ kvaser_pci_del_chan(dev);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver kvaser_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = kvaser_pci_tbl,
+ .probe = kvaser_pci_init_one,
+ .remove = __devexit_p(kvaser_pci_remove_one),
+};
+
+static int __init kvaser_pci_init(void)
+{
+ return pci_register_driver(&kvaser_pci_driver);
+}
+
+static void __exit kvaser_pci_exit(void)
+{
+ pci_unregister_driver(&kvaser_pci_driver);
+}
+
+module_init(kvaser_pci_init);
+module_exit(kvaser_pci_exit);
diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c
new file mode 100644
index 000000000000..d1089a3d62e0
--- /dev/null
+++ b/drivers/net/can/sja1000/peak_pci.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Derived from the PCAN project file driver/src/pcan_pci.c:
+ *
+ * Copyright (C) 2001-2006 PEAK System-Technik GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "peak_pci"
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI cards");
+MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI CAN card");
+MODULE_LICENSE("GPL v2");
+
+struct peak_pci {
+ int channel;
+ struct pci_dev *pci_dev;
+ struct net_device *slave_dev;
+ volatile void __iomem *conf_addr;
+};
+
+#define PEAK_PCI_SINGLE 0 /* single channel device */
+#define PEAK_PCI_MASTER 1 /* multi channel master device */
+#define PEAK_PCI_SLAVE 2 /* multi channel slave device */
+
+#define PEAK_PCI_CAN_CLOCK (16000000 / 2)
+
+#define PEAK_PCI_CDR_SINGLE (CDR_CBP | CDR_CLKOUT_MASK | CDR_CLK_OFF)
+#define PEAK_PCI_CDR_MASTER (CDR_CBP | CDR_CLKOUT_MASK)
+
+#define PEAK_PCI_OCR OCR_TX0_PUSHPULL
+
+/*
+ * Important PITA registers
+ */
+#define PITA_ICR 0x00 /* interrupt control register */
+#define PITA_GPIOICR 0x18 /* general purpose I/O interface
+ control register */
+#define PITA_MISC 0x1C /* miscellanoes register */
+
+#define PCI_CONFIG_PORT_SIZE 0x1000 /* size of the config io-memory */
+#define PCI_PORT_SIZE 0x0400 /* size of a channel io-memory */
+
+#define PEAK_PCI_VENDOR_ID 0x001C /* the PCI device and vendor IDs */
+#define PEAK_PCI_DEVICE_ID 0x0001
+
+static struct pci_device_id peak_pci_tbl[] = {
+ {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, peak_pci_tbl);
+
+static u8 peak_pci_read_reg(struct net_device *dev, int port)
+{
+ u8 val;
+ val = readb((const volatile void __iomem *)
+ (dev->base_addr + (port << 2)));
+ return val;
+}
+
+static void peak_pci_write_reg(struct net_device *dev, int port, u8 val)
+{
+ writeb(val, (volatile void __iomem *)
+ (dev->base_addr + (port << 2)));
+}
+
+static void peak_pci_post_irq(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board = priv->priv;
+ u16 icr_low;
+
+ /* Select and clear in Pita stored interrupt */
+ icr_low = readw(board->conf_addr + PITA_ICR);
+ if (board->channel == PEAK_PCI_SLAVE) {
+ if (icr_low & 0x0001)
+ writew(0x0001, board->conf_addr + PITA_ICR);
+ } else {
+ if (icr_low & 0x0002)
+ writew(0x0002, board->conf_addr + PITA_ICR);
+ }
+}
+
+static void peak_pci_del_chan(struct net_device *dev, int init_step)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board;
+ u16 icr_high;
+
+ if (!dev)
+ return;
+ priv = netdev_priv(dev);
+ if (!priv)
+ return;
+ board = priv->priv;
+ if (!board)
+ return;
+
+ switch (init_step) {
+ case 0: /* Full cleanup */
+ printk(KERN_INFO "Removing %s device %s\n",
+ DRV_NAME, dev->name);
+ unregister_sja1000dev(dev);
+ case 4:
+ icr_high = readw(board->conf_addr + PITA_ICR + 2);
+ if (board->channel == PEAK_PCI_SLAVE)
+ icr_high &= ~0x0001;
+ else
+ icr_high &= ~0x0002;
+ writew(icr_high, board->conf_addr + PITA_ICR + 2);
+ case 3:
+ iounmap((void *)dev->base_addr);
+ case 2:
+ if (board->channel != PEAK_PCI_SLAVE)
+ iounmap((void *)board->conf_addr);
+ case 1:
+ free_sja1000dev(dev);
+ break;
+ }
+
+}
+
+static int peak_pci_add_chan(struct pci_dev *pdev, int channel,
+ struct net_device **master_dev)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct peak_pci *board;
+ u16 icr_high;
+ unsigned long addr;
+ int err, init_step;
+
+ dev = alloc_sja1000dev(sizeof(struct peak_pci));
+ if (dev == NULL)
+ return -ENOMEM;
+ init_step = 1;
+
+ priv = netdev_priv(dev);
+ board = priv->priv;
+
+ board->pci_dev = pdev;
+ board->channel = channel;
+
+ if (channel != PEAK_PCI_SLAVE) {
+
+ addr = pci_resource_start(pdev, 0);
+ board->conf_addr = ioremap(addr, PCI_CONFIG_PORT_SIZE);
+ if (board->conf_addr == 0) {
+ err = -ENODEV;
+ goto failure;
+ }
+ init_step = 2;
+
+ /* Set GPIO control register */
+ writew(0x0005, board->conf_addr + PITA_GPIOICR + 2);
+
+ /* Enable single or dual channel */
+ if (channel == PEAK_PCI_MASTER)
+ writeb(0x00, board->conf_addr + PITA_GPIOICR);
+ else
+ writeb(0x04, board->conf_addr + PITA_GPIOICR);
+ /* Toggle reset */
+ writeb(0x05, board->conf_addr + PITA_MISC + 3);
+ mdelay(5);
+ /* Leave parport mux mode */
+ writeb(0x04, board->conf_addr + PITA_MISC + 3);
+ } else {
+ struct sja1000_priv *master_priv = netdev_priv(*master_dev);
+ struct peak_pci *master_board = master_priv->priv;
+ master_board->slave_dev = dev;
+ board->conf_addr = master_board->conf_addr;
+ }
+
+ addr = pci_resource_start(pdev, 1);
+ if (channel == PEAK_PCI_SLAVE)
+ addr += PCI_PORT_SIZE;
+
+ dev->base_addr = (unsigned long)ioremap(addr, PCI_PORT_SIZE);
+ if (dev->base_addr == 0) {
+ err = -ENOMEM;
+ goto failure;
+ }
+ init_step = 3;
+
+ priv->read_reg = peak_pci_read_reg;
+ priv->write_reg = peak_pci_write_reg;
+ priv->post_irq = peak_pci_post_irq;
+
+ priv->can.bittiming.clock = PEAK_PCI_CAN_CLOCK;
+
+ priv->ocr = PEAK_PCI_OCR;
+
+ if (channel == PEAK_PCI_MASTER)
+ priv->cdr = PEAK_PCI_CDR_MASTER;
+ else
+ priv->cdr = PEAK_PCI_CDR_SINGLE;
+
+ /* Register and setup interrupt handling */
+ dev->irq = pdev->irq;
+ icr_high = readw(board->conf_addr + PITA_ICR + 2);
+ if (channel == PEAK_PCI_SLAVE)
+ icr_high |= 0x0001;
+ else
+ icr_high |= 0x0002;
+ writew(icr_high, board->conf_addr + PITA_ICR + 2);
+ init_step = 4;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* Register SJA1000 device */
+ err = register_sja1000dev(dev);
+ if (err) {
+ printk(KERN_ERR "Registering %s device failed (err=%d)\n",
+ DRV_NAME, err);
+ goto failure;
+ }
+
+ if (channel != PEAK_PCI_SLAVE)
+ *master_dev = dev;
+
+ printk(KERN_INFO "%s: %s at base_addr=%#lx conf_addr=%p irq=%d\n",
+ DRV_NAME, dev->name, dev->base_addr, board->conf_addr, dev->irq);
+
+ return 0;
+
+failure:
+ peak_pci_del_chan(dev, init_step);
+ return err;
+}
+
+static int __devinit peak_pci_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int err;
+ u16 sub_sys_id;
+ struct net_device *master_dev = NULL;
+
+ printk(KERN_INFO "%s: initializing device %04x:%04x\n",
+ DRV_NAME, pdev->vendor, pdev->device);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto failure;
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err)
+ goto failure;
+
+ err = pci_read_config_word(pdev, 0x2e, &sub_sys_id);
+ if (err)
+ goto failure_cleanup;
+
+ err = pci_write_config_word(pdev, 0x44, 0);
+ if (err)
+ goto failure_cleanup;
+
+ if (sub_sys_id > 3) {
+ err = peak_pci_add_chan(pdev,
+ PEAK_PCI_MASTER, &master_dev);
+ if (err)
+ goto failure_cleanup;
+
+ err = peak_pci_add_chan(pdev,
+ PEAK_PCI_SLAVE, &master_dev);
+ if (err)
+ goto failure_cleanup;
+ } else {
+ err = peak_pci_add_chan(pdev, PEAK_PCI_SINGLE,
+ &master_dev);
+ if (err)
+ goto failure_cleanup;
+ }
+
+ pci_set_drvdata(pdev, master_dev);
+ return 0;
+
+failure_cleanup:
+ if (master_dev)
+ peak_pci_del_chan(master_dev, 0);
+
+ pci_release_regions(pdev);
+
+failure:
+ return err;
+
+}
+
+static void __devexit peak_pci_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct peak_pci *board = priv->priv;
+
+ if (board->slave_dev)
+ peak_pci_del_chan(board->slave_dev, 0);
+ peak_pci_del_chan(dev, 0);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver peak_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = peak_pci_tbl,
+ .probe = peak_pci_init_one,
+ .remove = __devexit_p(peak_pci_remove_one),
+};
+
+static int __init peak_pci_init(void)
+{
+ return pci_register_driver(&peak_pci_driver);
+}
+
+static void __exit peak_pci_exit(void)
+{
+ pci_unregister_driver(&peak_pci_driver);
+}
+
+module_init(peak_pci_init);
+module_exit(peak_pci_exit);
diff --git a/drivers/net/can/sja1000/pipcan.c b/drivers/net/can/sja1000/pipcan.c
new file mode 100644
index 000000000000..b7414b218697
--- /dev/null
+++ b/drivers/net/can/sja1000/pipcan.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 David Müller, <d.mueller@elsoft.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "pipcan"
+
+MODULE_AUTHOR("David Müller <d.mueller@elsoft.ch>");
+MODULE_DESCRIPTION("Socket-CAN driver for MPL PIPCAN module");
+MODULE_SUPPORTED_DEVICE("MPL PIPCAN module");
+MODULE_LICENSE("GPL v2");
+
+#define PIPCAN_CAN_CLOCK (16000000 / 2)
+
+#define PIPCAN_OCR (OCR_TX1_PUSHPULL)
+#define PIPCAN_CDR (CDR_CBP | CDR_CLK_OFF)
+
+#define PIPCAN_IOSIZE (0x100)
+
+#define PIPCAN_RES (0x804)
+#define PIPCAN_RST (0x805)
+
+static u8 pc_read_reg(struct net_device *dev, int reg)
+{
+ return inb(dev->base_addr + reg);
+}
+
+static void pc_write_reg(struct net_device *dev, int reg, u8 val)
+{
+ outb(val, dev->base_addr + reg);
+}
+
+static int __init pc_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct resource *res;
+ int rc, irq;
+
+ rc = -ENODEV;
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || !irq)
+ goto exit;
+
+ rc = -EBUSY;
+ if (!request_region(res->start, res->end - res->start + 1, DRV_NAME))
+ goto exit;
+
+ rc = -ENOMEM;
+ dev = alloc_sja1000dev(0);
+ if (!dev)
+ goto exit_release;
+
+ priv = netdev_priv(dev);
+
+ priv->read_reg = pc_read_reg;
+ priv->write_reg = pc_write_reg;
+ priv->can.bittiming.clock = PIPCAN_CAN_CLOCK;
+ priv->ocr = PIPCAN_OCR;
+ priv->cdr = PIPCAN_CDR;
+
+ dev->irq = irq;
+ dev->base_addr = res->start;
+
+ dev_set_drvdata(&pdev->dev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ /* deactivate RST */
+ outb(inb(PIPCAN_RST) & ~0x01, PIPCAN_RST);
+
+ rc = register_sja1000dev(dev);
+ if (rc) {
+ dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, rc);
+ goto exit_free;
+ }
+
+ dev_info(&pdev->dev, "device registered (base_addr=%#lx, irq=%d)\n",
+ dev->base_addr, dev->irq);
+ return 0;
+
+exit_free:
+ free_sja1000dev(dev);
+
+exit_release:
+ release_region(res->start, res->end - res->start + 1);
+
+exit:
+ return rc;
+}
+
+static int __exit pc_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = dev_get_drvdata(&pdev->dev);
+ struct resource *res;
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ unregister_sja1000dev(dev);
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+
+ free_sja1000dev(dev);
+
+ release_region(res->start, res->end - res->start + 1);
+
+ /* activate RST */
+ outb(inb(PIPCAN_RST) | 0x01, PIPCAN_RST);
+
+ return 0;
+}
+
+static struct platform_driver pc_driver = {
+ .remove = __exit_p(pc_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device *pc_pdev;
+static const u16 pipcan_ioport[] = {0x1000, 0x8000, 0xE000};
+
+static int __init pc_init(void)
+{
+ struct resource r[2];
+ int rc, addr, irq, idx;
+ u8 pc_res;
+
+ /* get PIPCAN resources from EPLD */
+ pc_res = inb(PIPCAN_RES);
+
+ idx = (pc_res & 0x0F);
+ if ((idx <= 0) || (idx > ARRAY_SIZE(pipcan_ioport))) {
+ printk(KERN_ERR DRV_NAME " invalid base address\n");
+ return -EINVAL;
+ }
+ addr = pipcan_ioport[idx-1];
+
+ irq = (pc_res >> 4) & 0x0F;
+ if ((irq < 3) || (irq == 8) || (irq == 13)) {
+ printk(KERN_ERR DRV_NAME " invalid IRQ\n");
+ return -EINVAL;
+ }
+
+ /* fill in resources */
+ memset(&r, 0, sizeof(r));
+ r[0].start = addr;
+ r[0].end = addr + PIPCAN_IOSIZE - 1;
+ r[0].name = DRV_NAME;
+ r[0].flags = IORESOURCE_IO;
+ r[1].start = r[1].end = irq;
+ r[1].name = DRV_NAME;
+ r[1].flags = IORESOURCE_IRQ;
+
+ pc_pdev = platform_device_register_simple(DRV_NAME, 0, r,
+ ARRAY_SIZE(r));
+ if (IS_ERR(pc_pdev))
+ return PTR_ERR(pc_pdev);
+
+ rc = platform_driver_probe(&pc_driver, pc_probe);
+ if (rc) {
+ platform_device_unregister(pc_pdev);
+ printk(KERN_ERR DRV_NAME
+ " platform_driver_probe() failed (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static void __exit pc_exit(void)
+{
+ platform_driver_unregister(&pc_driver);
+ platform_device_unregister(pc_pdev);
+}
+
+module_init(pc_init);
+module_exit(pc_exit);
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c
new file mode 100644
index 000000000000..02f49fa703ce
--- /dev/null
+++ b/drivers/net/can/sja1000/sja1000.c
@@ -0,0 +1,677 @@
+/*
+ * sja1000.c - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/dev.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "sja1000"
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION(DRV_NAME " CAN netdevice driver");
+
+static struct can_bittiming_const sja1000_bittiming_const = {
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+static int sja1000_probe_chip(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ if (dev->base_addr && (priv->read_reg(dev, 0) == 0xFF)) {
+ printk(KERN_INFO "%s: probing @0x%lX failed\n",
+ DRV_NAME, dev->base_addr);
+ return 0;
+ }
+ return 1;
+}
+
+static int set_reset_mode(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ unsigned char status = priv->read_reg(dev, REG_MOD);
+ int i;
+
+ /* disable interrupts */
+ priv->write_reg(dev, REG_IER, IRQ_OFF);
+
+ for (i = 0; i < 100; i++) {
+ /* check reset bit */
+ if (status & MOD_RM) {
+ priv->can.state = CAN_STATE_STOPPED;
+ return 0;
+ }
+
+ priv->write_reg(dev, REG_MOD, MOD_RM); /* reset chip */
+ status = priv->read_reg(dev, REG_MOD);
+ udelay(10);
+ }
+
+ dev_err(ND2D(dev), "setting SJA1000 into reset mode failed!\n");
+ return 1;
+
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ unsigned char status = priv->read_reg(dev, REG_MOD);
+ int i;
+
+ for (i = 0; i < 100; i++) {
+ /* check reset bit */
+ if ((status & MOD_RM) == 0) {
+ priv->can.state = CAN_STATE_ACTIVE;
+ /* enable all interrupts */
+ priv->write_reg(dev, REG_IER, IRQ_ALL);
+
+ return 0;
+ }
+
+ /* set chip to normal mode */
+ priv->write_reg(dev, REG_MOD, 0x00);
+ status = priv->read_reg(dev, REG_MOD);
+ udelay(10);
+ }
+
+ dev_err(ND2D(dev), "setting SJA1000 into normal mode failed!\n");
+ return 1;
+
+}
+
+static void sja1000_start(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ /* leave reset mode */
+ if (priv->can.state != CAN_STATE_STOPPED)
+ set_reset_mode(dev);
+
+ /* Clear error counters and error code capture */
+ priv->write_reg(dev, REG_TXERR, 0x0);
+ priv->write_reg(dev, REG_RXERR, 0x0);
+ priv->read_reg(dev, REG_ECC);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+}
+
+static int sja1000_set_mode(struct net_device *dev, enum can_mode mode)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ switch (mode) {
+ case CAN_MODE_START:
+ if (!priv->open_time)
+ return -EINVAL;
+
+ sja1000_start(dev);
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int sja1000_get_state(struct net_device *dev, enum can_state *state)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ u8 status;
+
+ /* FIXME: inspecting the status register to get the current state
+ * is not really necessary, because state changes are handled by
+ * in the ISR and the variable priv->can.state gets updated. The
+ * CAN devicde interface needs fixing!
+ */
+
+ spin_lock_irq(&priv->can.irq_lock);
+
+ if (priv->can.state == CAN_STATE_STOPPED) {
+ *state = CAN_STATE_STOPPED;
+ } else {
+ status = priv->read_reg(dev, REG_SR);
+ if (status & SR_BS)
+ *state = CAN_STATE_BUS_OFF;
+ else if (status & SR_ES) {
+ if (priv->read_reg(dev, REG_TXERR) > 127 ||
+ priv->read_reg(dev, REG_RXERR) > 127)
+ *state = CAN_STATE_BUS_PASSIVE;
+ else
+ *state = CAN_STATE_BUS_WARNING;
+ } else
+ *state = CAN_STATE_ACTIVE;
+ }
+ /* Check state */
+ if (*state != priv->can.state)
+ dev_err(ND2D(dev),
+ "Oops, state mismatch: hard %d != soft %d\n",
+ *state, priv->can.state);
+ spin_unlock_irq(&priv->can.irq_lock);
+ return 0;
+}
+
+static int sja1000_set_bittiming(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ u8 btr0, btr1;
+
+ btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+ btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+ (((bt->phase_seg2 - 1) & 0x7) << 4) |
+ ((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) << 7);
+
+ dev_info(ND2D(dev), "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1);
+
+ priv->write_reg(dev, REG_BTR0, btr0);
+ priv->write_reg(dev, REG_BTR1, btr1);
+
+ return 0;
+}
+
+/*
+ * initialize SJA1000 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ /* set clock divider and output control register */
+ priv->write_reg(dev, REG_CDR, priv->cdr | CDR_PELICAN);
+
+ /* set acceptance filter (accept all) */
+ priv->write_reg(dev, REG_ACCC0, 0x00);
+ priv->write_reg(dev, REG_ACCC1, 0x00);
+ priv->write_reg(dev, REG_ACCC2, 0x00);
+ priv->write_reg(dev, REG_ACCC3, 0x00);
+
+ priv->write_reg(dev, REG_ACCM0, 0xFF);
+ priv->write_reg(dev, REG_ACCM1, 0xFF);
+ priv->write_reg(dev, REG_ACCM2, 0xFF);
+ priv->write_reg(dev, REG_ACCM3, 0xFF);
+
+ priv->write_reg(dev, REG_OCR, priv->ocr | OCR_MODE_NORMAL);
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ff ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [flags] [len] [can data (up to 8 bytes]
+ */
+static int sja1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ uint8_t fi;
+ uint8_t dlc;
+ canid_t id;
+ uint8_t dreg;
+ int i;
+
+ netif_stop_queue(dev);
+
+ fi = dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if (id & CAN_RTR_FLAG)
+ fi |= FI_RTR;
+
+ if (id & CAN_EFF_FLAG) {
+ fi |= FI_FF;
+ dreg = EFF_BUF;
+ priv->write_reg(dev, REG_FI, fi);
+ priv->write_reg(dev, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
+ priv->write_reg(dev, REG_ID2, (id & 0x001fe000) >> (5 + 8));
+ priv->write_reg(dev, REG_ID3, (id & 0x00001fe0) >> 5);
+ priv->write_reg(dev, REG_ID4, (id & 0x0000001f) << 3);
+ } else {
+ dreg = SFF_BUF;
+ priv->write_reg(dev, REG_FI, fi);
+ priv->write_reg(dev, REG_ID1, (id & 0x000007f8) >> 3);
+ priv->write_reg(dev, REG_ID2, (id & 0x00000007) << 5);
+ }
+
+ for (i = 0; i < dlc; i++)
+ priv->write_reg(dev, dreg++, cf->data[i]);
+
+ stats->tx_bytes += dlc;
+ dev->trans_start = jiffies;
+
+ can_put_echo_skb(skb, dev, 0);
+
+ priv->write_reg(dev, REG_CMR, CMD_TR);
+
+ return 0;
+}
+
+static void sja1000_rx(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t fi;
+ uint8_t dreg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ fi = priv->read_reg(dev, REG_FI);
+ dlc = fi & 0x0F;
+
+ if (fi & FI_FF) {
+ /* extended frame format (EFF) */
+ dreg = EFF_BUF;
+ id = (priv->read_reg(dev, REG_ID1) << (5 + 16))
+ | (priv->read_reg(dev, REG_ID2) << (5 + 8))
+ | (priv->read_reg(dev, REG_ID3) << 5)
+ | (priv->read_reg(dev, REG_ID4) >> 3);
+ id |= CAN_EFF_FLAG;
+ } else {
+ /* standard frame format (SFF) */
+ dreg = SFF_BUF;
+ id = (priv->read_reg(dev, REG_ID1) << 3)
+ | (priv->read_reg(dev, REG_ID2) >> 5);
+ }
+
+ if (fi & FI_RTR)
+ id |= CAN_RTR_FLAG;
+
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++)
+ cf->data[i] = priv->read_reg(dev, dreg++);
+
+ while (i < 8)
+ cf->data[i++] = 0;
+
+ /* release receive buffer */
+ priv->write_reg(dev, REG_CMR, CMD_RRB);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ enum can_state state = priv->can.state;
+ uint8_t ecc, alc;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return -ENOMEM;
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = CAN_ERR_FLAG;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ if (isrc & IRQ_DOI) {
+ /* data overrun interrupt */
+ dev_dbg(ND2D(dev), "data overrun interrupt\n");
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ priv->can.can_stats.data_overrun++;
+ priv->write_reg(dev, REG_CMR, CMD_CDO); /* clear bit */
+ }
+
+ if (isrc & IRQ_EI) {
+ /* error warning interrupt */
+ priv->can.can_stats.error_warning++;
+ dev_dbg(ND2D(dev), "error warning interrupt\n");
+
+ if (status & SR_BS) {
+ state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+ can_bus_off(dev);
+ } else if (status & SR_ES) {
+ state = CAN_STATE_BUS_WARNING;
+ } else
+ state = CAN_STATE_ACTIVE;
+ }
+ if (isrc & IRQ_BEI) {
+ /* bus error interrupt */
+ priv->can.can_stats.bus_error++;
+ ecc = priv->read_reg(dev, REG_ECC);
+
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ switch (ecc & ECC_MASK) {
+ case ECC_BIT:
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ break;
+ case ECC_FORM:
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ break;
+ case ECC_STUFF:
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ break;
+ default:
+ cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+ cf->data[3] = ecc & ECC_SEG;
+ break;
+ }
+ /* Error occured during transmission? */
+ if ((ecc & ECC_DIR) == 0)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+ if (isrc & IRQ_EPI) {
+ /* error passive interrupt */
+ dev_dbg(ND2D(dev), "error passive interrupt\n");
+ priv->can.can_stats.error_passive++;
+ if (status & SR_ES)
+ state = CAN_STATE_BUS_PASSIVE;
+ else
+ state = CAN_STATE_ACTIVE;
+ }
+ if (isrc & IRQ_ALI) {
+ /* arbitration lost interrupt */
+ dev_dbg(ND2D(dev), "arbitration lost interrupt\n");
+ alc = priv->read_reg(dev, REG_ALC);
+ priv->can.can_stats.arbitration_lost++;
+ cf->can_id |= CAN_ERR_LOSTARB;
+ cf->data[0] = alc & 0x1f;
+ }
+
+ if (state != priv->can.state && (state == CAN_STATE_BUS_WARNING ||
+ state == CAN_STATE_BUS_PASSIVE)) {
+ uint8_t rxerr = priv->read_reg(dev, REG_RXERR);
+ uint8_t txerr = priv->read_reg(dev, REG_TXERR);
+ cf->can_id |= CAN_ERR_CRTL;
+ if (state == CAN_STATE_BUS_WARNING)
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_WARNING :
+ CAN_ERR_CRTL_RX_WARNING;
+ else
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_PASSIVE :
+ CAN_ERR_CRTL_RX_PASSIVE;
+ }
+
+ priv->can.state = state;
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+
+ return 0;
+}
+
+irqreturn_t sja1000_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct sja1000_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ uint8_t isrc, status;
+ int n = 0;
+
+ /* Shared interrupts and IRQ off? */
+ if (priv->read_reg(dev, REG_IER) == IRQ_OFF)
+ return IRQ_NONE;
+
+ if (priv->pre_irq)
+ priv->pre_irq(dev);
+
+ while ((isrc = priv->read_reg(dev, REG_IR)) && (n < SJA1000_MAX_IRQ)) {
+ n++;
+ status = priv->read_reg(dev, REG_SR);
+
+ if (isrc & IRQ_WUI) {
+ /* wake-up interrupt */
+ priv->can.can_stats.wakeup++;
+ }
+ if (isrc & IRQ_TI) {
+ /* transmission complete interrupt */
+ stats->tx_packets++;
+ can_get_echo_skb(dev, 0);
+ netif_wake_queue(dev);
+ }
+ if (isrc & IRQ_RI) {
+ /* receive interrupt */
+ while (status & SR_RBS) {
+ sja1000_rx(dev);
+ status = priv->read_reg(dev, REG_SR);
+ }
+ }
+ if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) {
+ /* error interrupt */
+ if (sja1000_err(dev, isrc, status))
+ break;
+ }
+ }
+
+ if (priv->post_irq)
+ priv->post_irq(dev);
+
+ if (n >= SJA1000_MAX_IRQ)
+ dev_dbg(ND2D(dev), "%d messages handled in ISR", n);
+
+ return (n) ? IRQ_HANDLED : IRQ_NONE;
+}
+EXPORT_SYMBOL_GPL(sja1000_interrupt);
+
+static int sja1000_open(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* determine and set bittime */
+ err = can_set_bittiming(dev);
+ if (err)
+ return err;
+
+ /* register interrupt handler, if not done by the device driver */
+ if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) {
+ err = request_irq(dev->irq, &sja1000_interrupt, IRQF_SHARED,
+ dev->name, (void *)dev);
+ if (err)
+ return -EAGAIN;
+ }
+
+ /* init and start chi */
+ sja1000_start(dev);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int sja1000_close(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+
+ set_reset_mode(dev);
+ netif_stop_queue(dev);
+ priv->open_time = 0;
+ can_close_cleanup(dev);
+
+ if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER))
+ free_irq(dev->irq, (void *)dev);
+
+ return 0;
+}
+
+struct net_device *alloc_sja1000dev(int sizeof_priv)
+{
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+
+ dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv);
+ if (!dev)
+ return NULL;
+
+ priv = netdev_priv(dev);
+ priv->dev = dev;
+
+ if (sizeof_priv)
+ priv->priv = (void *)priv + sizeof(struct sja1000_priv);
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_sja1000dev);
+
+void free_sja1000dev(struct net_device *dev)
+{
+ free_candev(dev);
+}
+EXPORT_SYMBOL_GPL(free_sja1000dev);
+
+int register_sja1000dev(struct net_device *dev)
+{
+ struct sja1000_priv *priv = netdev_priv(dev);
+ int err;
+
+ if (!sja1000_probe_chip(dev))
+ return -ENODEV;
+
+ dev->flags |= IFF_ECHO; /* we support local echo */
+
+ dev->open = sja1000_open;
+ dev->stop = sja1000_close;
+ dev->hard_start_xmit = sja1000_start_xmit;
+
+ priv->can.bittiming_const = &sja1000_bittiming_const;
+ priv->can.do_set_bittiming = sja1000_set_bittiming;
+ priv->can.do_get_state = sja1000_get_state;
+ priv->can.do_set_mode = sja1000_set_mode;
+ priv->dev = dev;
+
+ err = register_candev(dev);
+ if (err) {
+ printk(KERN_INFO
+ "%s: registering netdev failed\n", DRV_NAME);
+ free_netdev(dev);
+ return err;
+ }
+
+ set_reset_mode(dev);
+ chipset_init(dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(register_sja1000dev);
+
+void unregister_sja1000dev(struct net_device *dev)
+{
+ set_reset_mode(dev);
+ unregister_candev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_sja1000dev);
+
+static __init int sja1000_init(void)
+{
+ printk(KERN_INFO "%s CAN netdevice driver\n", DRV_NAME);
+
+ return 0;
+}
+
+module_init(sja1000_init);
+
+static __exit void sja1000_exit(void)
+{
+ printk(KERN_INFO "%s: driver removed\n", DRV_NAME);
+}
+
+module_exit(sja1000_exit);
diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h
new file mode 100644
index 000000000000..60d4cd690a24
--- /dev/null
+++ b/drivers/net/can/sja1000/sja1000.h
@@ -0,0 +1,177 @@
+/*
+ * sja1000.h - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef SJA1000_DEV_H
+#define SJA1000_DEV_H
+
+#include <linux/can/dev.h>
+#include <linux/can/platform/sja1000.h>
+
+#define SJA1000_MAX_IRQ 20 /* max. number of interrupts handled in ISR */
+
+/* SJA1000 registers - manual section 6.4 (Pelican Mode) */
+#define REG_MOD 0x00
+#define REG_CMR 0x01
+#define REG_SR 0x02
+#define REG_IR 0x03
+#define REG_IER 0x04
+#define REG_ALC 0x0B
+#define REG_ECC 0x0C
+#define REG_EWL 0x0D
+#define REG_RXERR 0x0E
+#define REG_TXERR 0x0F
+#define REG_ACCC0 0x10
+#define REG_ACCC1 0x11
+#define REG_ACCC2 0x12
+#define REG_ACCC3 0x13
+#define REG_ACCM0 0x14
+#define REG_ACCM1 0x15
+#define REG_ACCM2 0x16
+#define REG_ACCM3 0x17
+#define REG_RMC 0x1D
+#define REG_RBSA 0x1E
+
+/* Common registers - manual section 6.5 */
+#define REG_BTR0 0x06
+#define REG_BTR1 0x07
+#define REG_OCR 0x08
+#define REG_CDR 0x1F
+
+#define REG_FI 0x10
+#define SFF_BUF 0x13
+#define EFF_BUF 0x15
+
+#define FI_FF 0x80
+#define FI_RTR 0x40
+
+#define REG_ID1 0x11
+#define REG_ID2 0x12
+#define REG_ID3 0x13
+#define REG_ID4 0x14
+
+#define CAN_RAM 0x20
+
+/* mode register */
+#define MOD_RM 0x01
+#define MOD_LOM 0x02
+#define MOD_STM 0x04
+#define MOD_AFM 0x08
+#define MOD_SM 0x10
+
+/* commands */
+#define CMD_SRR 0x10
+#define CMD_CDO 0x08
+#define CMD_RRB 0x04
+#define CMD_AT 0x02
+#define CMD_TR 0x01
+
+/* interrupt sources */
+#define IRQ_BEI 0x80
+#define IRQ_ALI 0x40
+#define IRQ_EPI 0x20
+#define IRQ_WUI 0x10
+#define IRQ_DOI 0x08
+#define IRQ_EI 0x04
+#define IRQ_TI 0x02
+#define IRQ_RI 0x01
+#define IRQ_ALL 0xFF
+#define IRQ_OFF 0x00
+
+/* status register content */
+#define SR_BS 0x80
+#define SR_ES 0x40
+#define SR_TS 0x20
+#define SR_RS 0x10
+#define SR_TCS 0x08
+#define SR_TBS 0x04
+#define SR_DOS 0x02
+#define SR_RBS 0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_SEG 0x1F
+#define ECC_DIR 0x20
+#define ECC_ERR 6
+#define ECC_BIT 0x00
+#define ECC_FORM 0x40
+#define ECC_STUFF 0x80
+#define ECC_MASK 0xc0
+
+/*
+ * Flags for sja1000priv.flags
+ */
+#define SJA1000_CUSTOM_IRQ_HANDLER 0x1
+
+/*
+ * SJA1000 private data structure
+ */
+struct sja1000_priv {
+ struct can_priv can; /* must be the first member! */
+ long open_time;
+ struct sk_buff *echo_skb;
+
+ u8 (*read_reg) (struct net_device *dev, int reg);
+ void (*write_reg) (struct net_device *dev, int reg, u8 val);
+ void (*pre_irq) (struct net_device *dev);
+ void (*post_irq) (struct net_device *dev);
+
+ void *priv; /* for board-specific data */
+ struct net_device *dev;
+
+ u8 ocr;
+ u8 cdr;
+ u32 flags;
+};
+
+struct net_device *alloc_sja1000dev(int sizeof_priv);
+void free_sja1000dev(struct net_device *dev);
+int register_sja1000dev(struct net_device *dev);
+void unregister_sja1000dev(struct net_device *dev);
+
+irqreturn_t sja1000_interrupt(int irq, void *dev_id);
+
+#endif /* SJA1000_DEV_H */
diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c
new file mode 100644
index 000000000000..a6c5d760895f
--- /dev/null
+++ b/drivers/net/can/sja1000/sja1000_platform.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2005 Sascha Hauer, Pengutronix
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/platform/sja1000.h>
+#include <linux/io.h>
+
+#include "sja1000.h"
+
+#define DRV_NAME "sja1000_platform"
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus");
+MODULE_LICENSE("GPL v2");
+
+static u8 sp_read_reg(struct net_device *dev, int reg)
+{
+ return ioread8((void __iomem *)(dev->base_addr + reg));
+}
+
+static void sp_write_reg(struct net_device *dev, int reg, u8 val)
+{
+ iowrite8(val, (void __iomem *)(dev->base_addr + reg));
+}
+
+static int sp_probe(struct platform_device *pdev)
+{
+ int err, irq;
+ void __iomem *addr;
+ struct net_device *dev;
+ struct sja1000_priv *priv;
+ struct resource *res_mem, *res_irq;
+ struct sja1000_platform_data *pdata;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data provided!\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res_mem || !res_irq) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ if (!request_mem_region(res_mem->start,
+ res_mem->end - res_mem->start + 1,
+ DRV_NAME)) {
+ err = -EBUSY;
+ goto exit;
+ }
+
+ addr = ioremap_nocache(res_mem->start,
+ res_mem->end - res_mem->start + 1);
+ if (!addr) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+
+ irq = res_irq->start;
+ if (res_irq->flags & IRQF_TRIGGER_MASK)
+ set_irq_type(irq, res_irq->flags & IRQF_TRIGGER_MASK);
+
+ dev = alloc_sja1000dev(0);
+ if (!dev) {
+ err = -ENOMEM;
+ goto exit_iounmap;
+ }
+ priv = netdev_priv(dev);
+
+ priv->read_reg = sp_read_reg;
+ priv->write_reg = sp_write_reg;
+ priv->can.bittiming.clock = pdata->clock;
+ priv->ocr = pdata->ocr;
+ priv->cdr = pdata->cdr;
+
+ dev->irq = irq;
+ dev->base_addr = (unsigned long)addr;
+
+ dev_set_drvdata(&pdev->dev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ err = register_sja1000dev(dev);
+ if (err) {
+ dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
+ DRV_NAME, err);
+ goto exit_free;
+ }
+
+ dev_info(&pdev->dev, "%s device registered (base_addr=%#lx, irq=%d)\n",
+ DRV_NAME, dev->base_addr, dev->irq);
+ return 0;
+
+ exit_free:
+ free_sja1000dev(dev);
+ exit_iounmap:
+ iounmap(addr);
+ exit_release:
+ release_mem_region(res_mem->start, res_mem->end - res_mem->start + 1);
+ exit:
+ return err;
+}
+
+static int sp_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = dev_get_drvdata(&pdev->dev);
+ struct resource *res;
+
+ unregister_sja1000dev(dev);
+ dev_set_drvdata(&pdev->dev, NULL);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ if (dev->base_addr)
+ iounmap((void __iomem *)dev->base_addr);
+
+ free_sja1000dev(dev);
+
+ return 0;
+}
+
+static struct platform_driver sp_driver = {
+ .probe = sp_probe,
+ .remove = sp_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sp_init(void)
+{
+ return platform_driver_register(&sp_driver);
+}
+
+static void __exit sp_exit(void)
+{
+ platform_driver_unregister(&sp_driver);
+}
+
+module_init(sp_init);
+module_exit(sp_exit);
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
new file mode 100644
index 000000000000..80fcff3e69d8
--- /dev/null
+++ b/drivers/net/can/slcan.c
@@ -0,0 +1,904 @@
+/*
+ * slcan.c - serial line CAN interface driver (using tty line discipline)
+ *
+ * Copyright (c) 2007 Volkswagen Group Electronic Research
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+/*
+ * This file is derived from linux/drivers/net/slip.c
+ *
+ * Therefore it has the same (strange?) behaviour not to unregister the
+ * netdevice when detaching the tty. Is there any better solution?
+ *
+ * Do not try to attach, detach and re-attach a tty for this reason ...
+ *
+ * slip.c Authors: Laurence Culhane, <loz@holmes.demon.co.uk>
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/bitops.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/if_slip.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <linux/can.h>
+
+static __initdata const char banner[] =
+ KERN_INFO "slcan: serial line CAN interface driver\n";
+
+MODULE_ALIAS_LDISC(N_SLCAN);
+MODULE_DESCRIPTION("serial line CAN interface");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static int debug;
+module_param(debug, int, S_IRUGO);
+#define DBG(args...) (debug & 1 ? \
+ (printk(KERN_DEBUG "slcan %s: ", __func__), \
+ printk(args)) : 0)
+#else
+#define DBG(args...)
+#endif
+
+#ifndef N_SLCAN
+#error Your kernel does not support tty line discipline N_SLCAN
+#endif
+/*
+ * As there is currently no line discipline N_SLCAN in the mainstream kernel
+ * you will have to modify two kernel includes & recompile the kernel.
+ *
+ * Add this in include/asm/termios.h after the definition of N_HCI:
+ * #define N_SLCAN 16
+ *
+ * Increment NR_LDICS in include/linux/tty.h from 16 to 17
+ *
+ * NEW: Since Kernel 2.6.21 you only have to change include/linux/tty.h
+ *
+ */
+
+#define SLC_CHECK_TRANSMIT
+#define SLCAN_MAGIC 0x53CA
+
+static int maxdev = 10; /* MAX number of SLCAN channels;
+ This can be overridden with
+ insmod slcan.ko maxdev=nnn */
+module_param(maxdev, int, 0);
+MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces");
+
+/* maximum rx buffer len: extended CAN frame with timestamp */
+#define SLC_MTU (sizeof("T1111222281122334455667788EA5F\r")+1)
+
+struct slcan {
+ int magic;
+
+ /* Various fields. */
+ struct tty_struct *tty; /* ptr to TTY structure */
+ struct net_device *dev; /* easy for intr handling */
+ spinlock_t lock;
+
+ /* These are pointers to the malloc()ed frame buffers. */
+ unsigned char rbuff[SLC_MTU]; /* receiver buffer */
+ int rcount; /* received chars counter */
+ unsigned char xbuff[SLC_MTU]; /* transmitter buffer */
+ unsigned char *xhead; /* pointer to next XMIT byte */
+ int xleft; /* bytes left in XMIT queue */
+
+ unsigned long flags; /* Flag values/ mode etc */
+#define SLF_INUSE 0 /* Channel in use */
+#define SLF_ERROR 1 /* Parity, etc. error */
+
+ unsigned char leased;
+ dev_t line;
+ pid_t pid;
+};
+
+static struct net_device **slcan_devs;
+
+
+ /************************************************************************
+ * SLCAN ENCAPSULATION FORMAT *
+ ************************************************************************/
+
+/*
+ * A CAN frame has a can_id (11 bit standard frame format OR 29 bit extended
+ * frame format) a data length code (can_dlc) which can be from 0 to 8
+ * and up to <can_dlc> data bytes as payload.
+ * Additionally a CAN frame may become a remote transmission frame if the
+ * RTR-bit is set. This causes another ECU to send a CAN frame with the
+ * given can_id.
+ *
+ * The SLCAN ASCII representation of these different frame types is:
+ * <type> <id> <dlc> <data>*
+ *
+ * Extended frames (29 bit) are defined by capital characters in the type.
+ * RTR frames are defined as 'r' types - normal frames have 't' type:
+ * t => 11 bit data frame
+ * r => 11 bit RTR frame
+ * T => 29 bit data frame
+ * R => 29 bit RTR frame
+ *
+ * The <id> is 3 (standard) or 8 (extended) bytes in ASCII Hex (base64).
+ * The <dlc> is a one byte ASCII number ('0' - '8')
+ * The <data> section has at much ASCII Hex bytes as defined by the <dlc>
+ *
+ * Examples:
+ *
+ * t1230 : can_id 0x123, can_dlc 0, no data
+ * t4563112233 : can_id 0x456, can_dlc 3, data 0x11 0x22 0x33
+ * T12ABCDEF2AA55 : extended can_id 0x12ABCDEF, can_dlc 2, data 0xAA 0x55
+ * r1230 : can_id 0x123, can_dlc 0, no data, remote transmission request
+ *
+ */
+
+ /************************************************************************
+ * STANDARD SLCAN DECAPSULATION *
+ ************************************************************************/
+
+static int asc2nibble(char c)
+{
+
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+
+ if ((c >= 'A') && (c <= 'F'))
+ return c - 'A' + 10;
+
+ if ((c >= 'a') && (c <= 'f'))
+ return c - 'a' + 10;
+
+ return 16; /* error */
+}
+
+/* Send one completely decapsulated can_frame to the network layer */
+static void slc_bump(struct slcan *sl)
+{
+ struct net_device_stats *stats = &sl->dev->stats;
+ struct sk_buff *skb;
+ struct can_frame cf;
+ int i, dlc_pos, tmp;
+ unsigned long ultmp;
+ char cmd = sl->rbuff[0];
+
+ if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R'))
+ return;
+
+ if (cmd & 0x20) /* tiny chars 'r' 't' => standard frame format */
+ dlc_pos = 4; /* dlc position tiiid */
+ else
+ dlc_pos = 9; /* dlc position Tiiiiiiiid */
+
+ if (!((sl->rbuff[dlc_pos] >= '0') && (sl->rbuff[dlc_pos] < '9')))
+ return;
+
+ cf.can_dlc = sl->rbuff[dlc_pos] - '0'; /* get can_dlc from ASCII val */
+
+ sl->rbuff[dlc_pos] = 0; /* terminate can_id string */
+
+ if (strict_strtoul(sl->rbuff+1, 16, &ultmp))
+ return;
+ cf.can_id = ultmp;
+
+ if (!(cmd & 0x20)) /* NO tiny chars => extended frame format */
+ cf.can_id |= CAN_EFF_FLAG;
+
+ if ((cmd | 0x20) == 'r') /* RTR frame */
+ cf.can_id |= CAN_RTR_FLAG;
+
+ *(u64 *) (&cf.data) = 0; /* clear payload */
+
+ for (i = 0, dlc_pos++; i < cf.can_dlc; i++) {
+
+ tmp = asc2nibble(sl->rbuff[dlc_pos++]);
+ if (tmp > 0x0F)
+ return;
+ cf.data[i] = (tmp << 4);
+ tmp = asc2nibble(sl->rbuff[dlc_pos++]);
+ if (tmp > 0x0F)
+ return;
+ cf.data[i] |= tmp;
+ }
+
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (!skb)
+ return;
+
+ skb->dev = sl->dev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(struct can_frame)),
+ &cf, sizeof(struct can_frame));
+ netif_rx(skb);
+
+ sl->dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf.can_dlc;
+}
+
+/* parse tty input stream */
+static void slcan_unesc(struct slcan *sl, unsigned char s)
+{
+ struct net_device_stats *stats = &sl->dev->stats;
+
+ if ((s == '\r') || (s == '\a')) { /* CR or BEL ends the pdu */
+ if (!test_and_clear_bit(SLF_ERROR, &sl->flags) &&
+ (sl->rcount > 4)) {
+ slc_bump(sl);
+ }
+ sl->rcount = 0;
+ } else {
+ if (!test_bit(SLF_ERROR, &sl->flags)) {
+ if (sl->rcount < SLC_MTU) {
+ sl->rbuff[sl->rcount++] = s;
+ return;
+ } else {
+ stats->rx_over_errors++;
+ set_bit(SLF_ERROR, &sl->flags);
+ }
+ }
+ }
+}
+
+ /************************************************************************
+ * STANDARD SLCAN ENCAPSULATION *
+ ************************************************************************/
+
+/* Encapsulate one can_frame and stuff into a TTY queue. */
+static void slc_encaps(struct slcan *sl, struct can_frame *cf)
+{
+ struct net_device_stats *stats = &sl->dev->stats;
+ int actual, idx, i;
+ char cmd;
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ cmd = 'R'; /* becomes 'r' in standard frame format */
+ else
+ cmd = 'T'; /* becomes 't' in standard frame format */
+
+ if (cf->can_id & CAN_EFF_FLAG)
+ sprintf(sl->xbuff, "%c%08X%d", cmd,
+ cf->can_id & CAN_EFF_MASK, cf->can_dlc);
+ else
+ sprintf(sl->xbuff, "%c%03X%d", cmd | 0x20,
+ cf->can_id & CAN_SFF_MASK, cf->can_dlc);
+
+ idx = strlen(sl->xbuff);
+
+ for (i = 0; i < cf->can_dlc; i++)
+ sprintf(&sl->xbuff[idx + 2*i], "%02X", cf->data[i]);
+
+ DBG("ASCII frame = '%s'\n", sl->xbuff);
+
+ strcat(sl->xbuff, "\r"); /* add terminating character */
+
+ /* Order of next two lines is *very* important.
+ * When we are sending a little amount of data,
+ * the transfer may be completed inside driver.write()
+ * routine, because it's running with interrupts enabled.
+ * In this case we *never* got WRITE_WAKEUP event,
+ * if we did not request it before write operation.
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+ sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ actual = sl->tty->ops->write(sl->tty, sl->xbuff, strlen(sl->xbuff));
+#ifdef SLC_CHECK_TRANSMIT
+ sl->dev->trans_start = jiffies;
+#endif
+ sl->xleft = strlen(sl->xbuff) - actual;
+ sl->xhead = sl->xbuff + actual;
+ stats->tx_bytes += cf->can_dlc;
+}
+
+/*
+ * Called by the driver when there's room for more data. If we have
+ * more packets to send, we send them here.
+ */
+static void slcan_write_wakeup(struct tty_struct *tty)
+{
+ int actual;
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+ struct net_device_stats *stats = &sl->dev->stats;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev))
+ return;
+
+ if (sl->xleft <= 0) {
+ /* Now serial buffer is almost free & we can start
+ * transmission of another packet */
+ stats->tx_packets++;
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ netif_wake_queue(sl->dev);
+ return;
+ }
+
+ actual = tty->ops->write(tty, sl->xhead, sl->xleft);
+ sl->xleft -= actual;
+ sl->xhead += actual;
+}
+
+static void slc_tx_timeout(struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ spin_lock(&sl->lock);
+
+ if (netif_queue_stopped(dev)) {
+ if (!netif_running(dev))
+ goto out;
+
+ /* May be we must check transmitter timeout here ?
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+#ifdef SLC_CHECK_TRANSMIT
+ if (time_before(jiffies, dev->trans_start + 20 * HZ)) {
+ /* 20 sec timeout not reached */
+ goto out;
+ }
+ printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
+ (tty_chars_in_buffer(sl->tty) || sl->xleft)
+ ? "bad line quality" : "driver error");
+ sl->xleft = 0;
+ sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ netif_wake_queue(sl->dev);
+#endif
+ }
+out:
+ spin_unlock(&sl->lock);
+}
+
+
+/******************************************
+ * Routines looking at netdevice side.
+ ******************************************/
+
+/* Send a can_frame to a TTY queue. */
+static int slc_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ if (skb->len != sizeof(struct can_frame))
+ goto out;
+
+ spin_lock(&sl->lock);
+ if (!netif_running(dev)) {
+ spin_unlock(&sl->lock);
+ printk(KERN_WARNING "%s: xmit: iface is down\n", dev->name);
+ goto out;
+ }
+
+ if (sl->tty == NULL) {
+ spin_unlock(&sl->lock);
+ goto out;
+ }
+
+ netif_stop_queue(sl->dev);
+ slc_encaps(sl, (struct can_frame *) skb->data); /* encaps & send */
+ spin_unlock(&sl->lock);
+
+out:
+ kfree_skb(skb);
+ return 0;
+}
+
+
+/* Netdevice UP -> DOWN routine */
+static int slc_close(struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ spin_lock_bh(&sl->lock);
+ if (sl->tty) {
+ /* TTY discipline is running. */
+ sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ }
+ netif_stop_queue(dev);
+ sl->rcount = 0;
+ sl->xleft = 0;
+ spin_unlock_bh(&sl->lock);
+
+ return 0;
+}
+
+/* Netdevice DOWN -> UP routine */
+static int slc_open(struct net_device *dev)
+{
+ struct slcan *sl = netdev_priv(dev);
+
+ if (sl->tty == NULL)
+ return -ENODEV;
+
+ sl->flags &= (1 << SLF_INUSE);
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Netdevice register callback */
+static void slc_setup(struct net_device *dev)
+{
+ dev->open = slc_open;
+ dev->destructor = free_netdev;
+ dev->stop = slc_close;
+ dev->hard_start_xmit = slc_xmit;
+
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->mtu = sizeof(struct can_frame);
+ dev->type = ARPHRD_CAN;
+#ifdef SLC_CHECK_TRANSMIT
+ dev->tx_timeout = slc_tx_timeout;
+ dev->watchdog_timeo = 20*HZ;
+#endif
+
+ /* New-style flags. */
+ dev->flags = IFF_NOARP;
+ dev->features = NETIF_F_NO_CSUM;
+}
+
+/******************************************
+ * Routines looking at TTY side.
+ ******************************************/
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of SLCAN data has been received, which can now be decapsulated
+ * and sent on to some IP layer for further processing. This will not
+ * be re-entered while running but other ldisc functions may be called
+ * in parallel
+ */
+
+static void slcan_receive_buf(struct tty_struct *tty,
+ const unsigned char *cp, char *fp, int count)
+{
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+ struct net_device_stats *stats = &sl->dev->stats;
+
+ if (!sl || sl->magic != SLCAN_MAGIC ||
+ !netif_running(sl->dev))
+ return;
+
+ /* Read the characters out of the buffer */
+ while (count--) {
+ if (fp && *fp++) {
+ if (!test_and_set_bit(SLF_ERROR, &sl->flags))
+ stats->rx_errors++;
+ cp++;
+ continue;
+ }
+ slcan_unesc(sl, *cp++);
+ }
+}
+
+/************************************
+ * slcan_open helper routines.
+ ************************************/
+
+/* Collect hanged up channels */
+
+static void slc_sync(void)
+{
+ int i;
+ struct net_device *dev;
+ struct slcan *sl;
+
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (dev == NULL)
+ break;
+
+ sl = netdev_priv(dev);
+ if (sl->tty || sl->leased)
+ continue;
+ if (dev->flags&IFF_UP)
+ dev_close(dev);
+ }
+}
+
+
+/* Find a free SLCAN channel, and link in this `tty' line. */
+static struct slcan *slc_alloc(dev_t line)
+{
+ int i;
+ int sel = -1;
+ int score = -1;
+ struct net_device *dev = NULL;
+ struct slcan *sl;
+
+ if (slcan_devs == NULL)
+ return NULL; /* Master array missing ! */
+
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (dev == NULL)
+ break;
+
+ sl = netdev_priv(dev);
+ if (sl->leased) {
+ if (sl->line != line)
+ continue;
+ if (sl->tty)
+ return NULL;
+
+ /* Clear ESCAPE & ERROR flags */
+ sl->flags &= (1 << SLF_INUSE);
+ return sl;
+ }
+
+ if (sl->tty)
+ continue;
+
+ if (current->pid == sl->pid) {
+ if (sl->line == line && score < 3) {
+ sel = i;
+ score = 3;
+ continue;
+ }
+ if (score < 2) {
+ sel = i;
+ score = 2;
+ }
+ continue;
+ }
+ if (sl->line == line && score < 1) {
+ sel = i;
+ score = 1;
+ continue;
+ }
+ if (score < 0) {
+ sel = i;
+ score = 0;
+ }
+ }
+
+ if (sel >= 0) {
+ i = sel;
+ dev = slcan_devs[i];
+ if (score > 1) {
+ sl = netdev_priv(dev);
+ sl->flags &= (1 << SLF_INUSE);
+ return sl;
+ }
+ }
+
+ /* Sorry, too many, all slots in use */
+ if (i >= maxdev)
+ return NULL;
+
+ if (dev) {
+ sl = netdev_priv(dev);
+ if (test_bit(SLF_INUSE, &sl->flags)) {
+ unregister_netdevice(dev);
+ dev = NULL;
+ slcan_devs[i] = NULL;
+ }
+ }
+
+ if (!dev) {
+ char name[IFNAMSIZ];
+ sprintf(name, "slc%d", i);
+
+ dev = alloc_netdev(sizeof(*sl), name, slc_setup);
+ if (!dev)
+ return NULL;
+ dev->base_addr = i;
+ }
+
+ sl = netdev_priv(dev);
+
+ /* Initialize channel control data */
+ sl->magic = SLCAN_MAGIC;
+ sl->dev = dev;
+ spin_lock_init(&sl->lock);
+ slcan_devs[i] = dev;
+
+ return sl;
+}
+
+/*
+ * Open the high-level part of the SLCAN channel.
+ * This function is called by the TTY module when the
+ * SLCAN line discipline is called for. Because we are
+ * sure the tty line exists, we only have to link it to
+ * a free SLCAN channel...
+ *
+ * Called in process context serialized from other ldisc calls.
+ */
+
+static int slcan_open(struct tty_struct *tty)
+{
+ struct slcan *sl;
+ int err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (tty->ops->write == NULL)
+ return -EOPNOTSUPP;
+
+ /* RTnetlink lock is misused here to serialize concurrent
+ opens of slcan channels. There are better ways, but it is
+ the simplest one.
+ */
+ rtnl_lock();
+
+ /* Collect hanged up channels. */
+ slc_sync();
+
+ sl = (struct slcan *) tty->disc_data;
+
+ err = -EEXIST;
+ /* First make sure we're not already connected. */
+ if (sl && sl->magic == SLCAN_MAGIC)
+ goto err_exit;
+
+ /* OK. Find a free SLCAN channel to use. */
+ err = -ENFILE;
+ sl = slc_alloc(tty_devnum(tty));
+ if (sl == NULL)
+ goto err_exit;
+
+ sl->tty = tty;
+ tty->disc_data = sl;
+ sl->line = tty_devnum(tty);
+ sl->pid = current->pid;
+
+ if (!test_bit(SLF_INUSE, &sl->flags)) {
+ /* Perform the low-level SLCAN initialization. */
+ sl->rcount = 0;
+ sl->xleft = 0;
+
+ set_bit(SLF_INUSE, &sl->flags);
+
+ err = register_netdevice(sl->dev);
+ if (err)
+ goto err_free_chan;
+ }
+
+ /* Done. We have linked the TTY line to a channel. */
+ rtnl_unlock();
+
+ tty->receive_room = 65536; /* We don't flow control */
+
+ return sl->dev->base_addr;
+
+err_free_chan:
+ sl->tty = NULL;
+ tty->disc_data = NULL;
+ clear_bit(SLF_INUSE, &sl->flags);
+
+err_exit:
+ rtnl_unlock();
+
+ /* Count references from TTY module */
+ return err;
+}
+
+/*
+
+ FIXME: 1,2 are fixed 3 was never true anyway.
+
+ Let me to blame a bit.
+ 1. TTY module calls this funstion on soft interrupt.
+ 2. TTY module calls this function WITH MASKED INTERRUPTS!
+ 3. TTY module does not notify us about line discipline
+ shutdown,
+
+ Seems, now it is clean. The solution is to consider netdevice and
+ line discipline sides as two independent threads.
+
+ By-product (not desired): slc? does not feel hangups and remains open.
+ It is supposed, that user level program (dip, diald, slattach...)
+ will catch SIGHUP and make the rest of work.
+
+ I see no way to make more with current tty code. --ANK
+ */
+
+/*
+ * Close down a SLCAN channel.
+ * This means flushing out any pending queues, and then returning. This
+ * call is serialized against other ldisc functions.
+ */
+static void slcan_close(struct tty_struct *tty)
+{
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLCAN_MAGIC || sl->tty != tty)
+ return;
+
+ tty->disc_data = NULL;
+ sl->tty = NULL;
+ if (!sl->leased)
+ sl->line = 0;
+
+ /* Count references from TTY module */
+}
+
+/* Perform I/O control on an active SLCAN channel. */
+static int slcan_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct slcan *sl = (struct slcan *) tty->disc_data;
+ unsigned int tmp;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SLCAN_MAGIC)
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCGIFNAME:
+ tmp = strlen(sl->dev->name) + 1;
+ if (copy_to_user((void __user *)arg, sl->dev->name, tmp))
+ return -EFAULT;
+ return 0;
+
+ case SIOCSIFHWADDR:
+ return -EINVAL;
+
+ default:
+ return tty_mode_ioctl(tty, file, cmd, arg);
+ }
+}
+
+static struct tty_ldisc_ops slc_ldisc = {
+ .owner = THIS_MODULE,
+ .magic = TTY_LDISC_MAGIC,
+ .name = "slcan",
+ .open = slcan_open,
+ .close = slcan_close,
+ .ioctl = slcan_ioctl,
+ .receive_buf = slcan_receive_buf,
+ .write_wakeup = slcan_write_wakeup,
+};
+
+/************************************
+ * general slcan module init/exit
+ ************************************/
+
+static int __init slcan_init(void)
+{
+ int status;
+
+ if (maxdev < 4)
+ maxdev = 4; /* Sanity */
+
+ printk(banner);
+ printk(KERN_INFO "slcan: %d dynamic interface channels.\n", maxdev);
+
+ slcan_devs = kmalloc(sizeof(struct net_device *)*maxdev, GFP_KERNEL);
+ if (!slcan_devs) {
+ printk(KERN_ERR "slcan: can't allocate slcan device array!\n");
+ return -ENOMEM;
+ }
+
+ /* Clear the pointer array, we allocate devices when we need them */
+ memset(slcan_devs, 0, sizeof(struct net_device *)*maxdev);
+
+ /* Fill in our line protocol discipline, and register it */
+ status = tty_register_ldisc(N_SLCAN, &slc_ldisc);
+ if (status != 0) {
+ printk(KERN_ERR "slcan: can't register line discipline\n");
+ kfree(slcan_devs);
+ }
+ return status;
+}
+
+static void __exit slcan_exit(void)
+{
+ int i;
+ struct net_device *dev;
+ struct slcan *sl;
+ unsigned long timeout = jiffies + HZ;
+ int busy = 0;
+
+ if (slcan_devs == NULL)
+ return;
+
+ /* First of all: check for active disciplines and hangup them.
+ */
+ do {
+ if (busy)
+ msleep_interruptible(100);
+
+ busy = 0;
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (!dev)
+ continue;
+ sl = netdev_priv(dev);
+ spin_lock_bh(&sl->lock);
+ if (sl->tty) {
+ busy++;
+ tty_hangup(sl->tty);
+ }
+ spin_unlock_bh(&sl->lock);
+ }
+ } while (busy && time_before(jiffies, timeout));
+
+
+ for (i = 0; i < maxdev; i++) {
+ dev = slcan_devs[i];
+ if (!dev)
+ continue;
+ slcan_devs[i] = NULL;
+
+ sl = netdev_priv(dev);
+ if (sl->tty) {
+ printk(KERN_ERR "%s: tty discipline still running\n",
+ dev->name);
+ /* Intentionally leak the control block. */
+ dev->destructor = NULL;
+ }
+
+ unregister_netdev(dev);
+ }
+
+ kfree(slcan_devs);
+ slcan_devs = NULL;
+
+ i = tty_unregister_ldisc(N_SLCAN);
+ if (i)
+ printk(KERN_ERR "slcan: can't unregister ldisc (err %d)\n", i);
+}
+
+module_init(slcan_init);
+module_exit(slcan_exit);
diff --git a/drivers/net/can/softing/Makefile b/drivers/net/can/softing/Makefile
new file mode 100644
index 000000000000..df3fceca7bd0
--- /dev/null
+++ b/drivers/net/can/softing/Makefile
@@ -0,0 +1,20 @@
+# Makefile for softing CAN driver
+
+ifeq ($(KERNELRELEASE),)
+# necessary when used outside kernel
+KERNELDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+TOPDIR := $(PWD)/../../../..
+
+modules modules_install clean:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR)
+
+else
+
+-include $(TOPDIR)/Makefile.common
+
+softing-y := softing_main.o softing_fw.o
+obj-$(CONFIG_CAN_SOFTING) += softing.o
+obj-$(CONFIG_CAN_SOFTING_CS) += softing_cs.o
+
+endif
diff --git a/drivers/net/can/softing/softing.h b/drivers/net/can/softing/softing.h
new file mode 100644
index 000000000000..e43c7f116799
--- /dev/null
+++ b/drivers/net/can/softing/softing.h
@@ -0,0 +1,268 @@
+/*
+ * softing common interfaces
+ *
+ * by Kurt Van Dijck, 06-2008
+ */
+
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+struct softing;
+struct sofing_desc;
+
+/* special attribute, so we should not rely on the ->priv pointers
+ * before knowing how to interpret these
+ */
+struct softing_attribute;
+
+struct softing_priv {
+ struct can_priv can; /* must be the first member! */
+ struct net_device *netdev;
+ struct softing *card;
+ struct {
+ int pending;
+ /* variables wich hold the circular buffer */
+ int echo_put;
+ int echo_get;
+ } tx;
+ struct can_bittiming_const btr_const;
+ int index;
+ u8 output;
+ u16 chip;
+ struct attribute_group sysfs;
+};
+#define netdev2softing(netdev) ((struct softing_priv *)netdev_priv(netdev))
+
+/* the 'all cards have the same' fields definition */
+extern const struct can_bittiming_const softing_btr_const;
+
+struct softing_desc {
+ unsigned int manf;
+ unsigned int prod;
+ /* generation
+ * 1st with NEC or SJA1000
+ * 8bit, exclusive interrupt, ...
+ * 2nd only SJA11000
+ * 16bit, shared interrupt
+ */
+ int generation;
+ unsigned int freq; /*crystal in MHz */
+ unsigned int max_brp;
+ unsigned int max_sjw;
+ unsigned long dpram_size;
+ char name[32];
+ struct {
+ unsigned long offs;
+ unsigned long addr;
+ char fw[32];
+ } boot, load, app;
+};
+
+struct softing {
+ int nbus;
+ struct softing_priv *bus[2];
+ spinlock_t spin; /* protect this structure & DPRAM access */
+
+ struct {
+ /* indication of firmware status */
+ int up;
+ /* protection of the 'up' variable */
+ struct mutex lock;
+ } fw;
+ struct {
+ int nr;
+ int requested;
+ struct tasklet_struct bh;
+ int svc_count;
+ } irq;
+ struct {
+ int pending;
+ int last_bus;
+ /* keep the bus that last tx'd a message,
+ * in order to let every netdev queue resume
+ */
+ } tx;
+ struct {
+ unsigned long phys;
+ unsigned long size;
+ unsigned char *virt;
+ unsigned char *end;
+ struct softing_fct *fct;
+ struct softing_info *info;
+ struct softing_rx *rx;
+ struct softing_tx *tx;
+ struct softing_irq *irq;
+ unsigned short *command;
+ unsigned short *receipt;
+ } dpram;
+ struct {
+ unsigned short manf;
+ unsigned short prod;
+ u32 serial, fw, hw, lic;
+ u16 chip [2];
+ u32 freq;
+ const char *name;
+ } id;
+ const struct softing_desc *desc;
+ struct {
+ int (*reset) (struct softing *, int);
+ int (*enable_irq)(struct softing *, int);
+ } fn;
+ struct device *dev;
+ /* sysfs */
+ struct attribute_group sysfs;
+ struct softing_attribute *attr;
+ struct attribute **grp;
+};
+
+extern int mk_softing(struct softing *);
+/* fields that must be set already are :
+ * ncan
+ * id.manf
+ * id.prod
+ * fn.reset
+ * fn.enable_irq
+ */
+extern void rm_softing(struct softing *);
+/* usefull functions during operation */
+
+extern const struct softing_desc *
+ softing_lookup_desc(unsigned int manf, unsigned int prod);
+
+extern int softing_default_output(struct softing *card
+ , struct softing_priv *priv);
+extern u32 softing_time2usec(struct softing *card, u32 raw);
+
+extern int softing_fct_cmd(struct softing *card
+ , int cmd, int vector, const char *msg);
+
+extern int softing_bootloader_command(struct softing *card
+ , int command, const char *msg);
+
+/* Load firmware after reset */
+extern int softing_load_fw(const char *file, struct softing *card,
+ unsigned char *virt, unsigned int size, int offset);
+
+/* Load final application firmware after bootloader */
+extern int softing_load_app_fw(const char *file, struct softing *card);
+
+extern int softing_reset_chip(struct softing *card);
+
+/* enable or disable irq
+ * only called with fw.lock locked
+ */
+extern int softing_card_irq(struct softing *card, int enable);
+
+/* called when tx queue is flushed */
+extern void softing_flush_echo_skb(struct softing_priv *priv);
+
+/* reinitaliase the card, apply -1 for bus[01] for 'no change' */
+extern int softing_reinit(struct softing *card, int bus0, int bus1);
+
+/* SOFTING DPRAM mappings */
+struct softing_rx {
+ u8 fifo[16][32];
+ u8 dummy1;
+ u16 rd;
+ u16 dummy2;
+ u16 wr;
+ u16 dummy3;
+ u16 lost_msg;
+} __attribute__((packed));
+
+#define TXMAX 31
+struct softing_tx {
+ u8 fifo[32][16];
+ u8 dummy1;
+ u16 rd;
+ u16 dummy2;
+ u16 wr;
+ u8 dummy3;
+} __attribute__((packed));
+
+struct softing_irq {
+ u8 to_host;
+ u8 to_card;
+} __attribute__((packed));
+
+struct softing_fct {
+ s16 param[20]; /* 0 is index */
+ s16 returned;
+ u8 dummy;
+ u16 host_access;
+} __attribute__((packed));
+
+struct softing_info {
+ u8 dummy1;
+ u16 bus_state;
+ u16 dummy2;
+ u16 bus_state2;
+ u16 dummy3;
+ u16 error_state;
+ u16 dummy4;
+ u16 error_state2;
+ u16 dummy5;
+ u16 reset;
+ u16 dummy6;
+ u16 clear_rcv_fifo;
+ u16 dummy7;
+ u16 dummyxx;
+ u16 dummy8;
+ u16 time_reset;
+ u8 dummy9;
+ u32 time;
+ u32 time_wrap;
+ u8 wr_start;
+ u8 wr_end;
+ u8 dummy10;
+ u16 dummy12;
+ u16 dummy12x;
+ u16 dummy13;
+ u16 reset_rcv_fifo;
+ u8 dummy14;
+ u8 reset_xmt_fifo;
+ u8 read_fifo_levels;
+ u16 rcv_fifo_level;
+ u16 xmt_fifo_level;
+} __attribute__((packed));
+
+/* DPRAM return codes */
+#define RES_NONE 0
+#define RES_OK 1
+#define RES_NOK 2
+#define RES_UNKNOWN 3
+/* DPRAM flags */
+#define CMD_TX 0x01
+#define CMD_ACK 0x02
+#define CMD_XTD 0x04
+#define CMD_RTR 0x08
+#define CMD_ERR 0x10
+#define CMD_BUS2 0x80
+
+/* debug */
+extern int softing_debug;
+
+#define mod_alert(fmt,arg...) { \
+ if (softing_debug >= 0) \
+ printk(KERN_ALERT "[%s] %s:" fmt "\n" \
+ , THIS_MODULE->name \
+ , __func__ \
+ , ##arg); \
+ }
+#define mod_info(fmt,arg...) { \
+ if (softing_debug >= 1) \
+ printk(KERN_INFO "[%s] %s:" fmt "\n"\
+ , THIS_MODULE->name \
+ , __func__ \
+ , ##arg); \
+ }
+#define mod_trace(fmt,arg...) { \
+ if (softing_debug >= 2) \
+ printk(KERN_DEBUG "[%s] %s:" fmt "\n" \
+ , THIS_MODULE->name \
+ , __func__ \
+ , ##arg); \
+ }
+
diff --git a/drivers/net/can/softing/softing_cs.c b/drivers/net/can/softing/softing_cs.c
new file mode 100644
index 000000000000..e33b614de28f
--- /dev/null
+++ b/drivers/net/can/softing/softing_cs.c
@@ -0,0 +1,427 @@
+/*
+* drivers/net/can/softing/softing_cs.c
+*
+* Copyright (C) 2008
+*
+* - Kurt Van Dijck, EIA Electronics
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/system.h>
+
+#include "softing.h"
+
+struct softing_cs {
+ struct softing softing;
+ win_req_t win;
+};
+#define softing2cs(x) container_of((x), struct softing_cs, softing)
+
+struct lookup {
+ int i;
+ const char *a;
+};
+
+static const char __devinit *lookup_mask(const struct lookup *lp, int *i)
+{
+ for (; lp->a; ++lp) {
+ if (lp->i & *i) {
+ *i &= ~lp->i;
+ return lp->a;
+ }
+ }
+ return 0;
+}
+
+static int card_reset_via_pcmcia(struct softing *sdev, int v)
+{
+ struct pcmcia_device *pcmcia = to_pcmcia_dev(sdev->dev);
+ conf_reg_t reg;
+ reg.Function = 0; /* socket */
+ reg.Action = CS_WRITE;
+ reg.Offset = 2;
+ reg.Value = v ? 0 : 0x20;
+ return pcmcia_access_configuration_register(pcmcia, &reg);
+}
+
+static int card_reset_via_dpram(struct softing *sdev, int v)
+{
+ if (v) {
+ spin_lock_bh(&sdev->spin);
+ sdev->dpram.virt[0xe00] &= ~1;
+ spin_unlock_bh(&sdev->spin);
+ card_reset_via_pcmcia(sdev, v);
+ } else {
+ card_reset_via_pcmcia(sdev, v);
+ spin_lock_bh(&sdev->spin);
+ sdev->dpram.virt[0xe00] |= 1;
+ spin_unlock_bh(&sdev->spin);
+ }
+ return 0;
+}
+
+static int card_enable_irq_via_pcmcia(struct softing *sdev, int v)
+{
+ int ret;
+ struct pcmcia_device *pcmcia = to_pcmcia_dev(sdev->dev);
+ conf_reg_t reg;
+ memset(&reg, 0, sizeof(reg));
+ reg.Function = 0; /* socket */
+ reg.Action = CS_WRITE;
+ reg.Offset = 0;
+ reg.Value = v ? 0x60 : 0;
+ ret = pcmcia_access_configuration_register(pcmcia, &reg);
+ if (ret)
+ mod_alert("failed %u", ret);
+ return ret;
+}
+
+/* TODO: in 2.6.26, __devinitconst works*/
+static const __devinitdata struct lookup pcmcia_io_attr[] = {
+ { IO_DATA_PATH_WIDTH_AUTO , "[auto]" , },
+ { IO_DATA_PATH_WIDTH_8 , "8bit" , },
+ { IO_DATA_PATH_WIDTH_16 , "16bit" , },
+ { 0, 0, },
+};
+
+static const __devinitdata struct lookup pcmcia_mem_attr[] = {
+ { WIN_ADDR_SPACE_IO , "IO" , },
+ { WIN_MEMORY_TYPE_AM , "typeAM" , },
+ { WIN_ENABLE , "enable" , },
+ { WIN_DATA_WIDTH_8 , "8bit" , },
+ { WIN_DATA_WIDTH_16 , "16bit" , },
+ { WIN_DATA_WIDTH_32 , "32bit" , },
+ { WIN_PAGED , "paged" , },
+ { WIN_SHARED , "shared" , },
+ { WIN_FIRST_SHARED , "first_shared", },
+ { WIN_USE_WAIT , "wait" , },
+ { WIN_STRICT_ALIGN , "strict_align", },
+ { WIN_MAP_BELOW_1MB , "below_1MB" , },
+ { WIN_PREFETCH , "prefetch" , },
+ { WIN_CACHEABLE , "cacheable" , },
+ { 0, 0, },
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
+/* backported */
+struct pcmcia_cfg_mem {
+ tuple_t tuple;
+ cisparse_t parse;
+ u8 buf[256];
+ cistpl_cftable_entry_t dflt;
+};
+static int pcmcia_loop_config(struct pcmcia_device *p_dev,
+ int (*conf_check) (struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data),
+ void *priv_data)
+{
+ struct pcmcia_cfg_mem *cfg_mem;
+
+ tuple_t *tuple;
+ int ret = -ENODEV;
+ unsigned int vcc;
+
+ cfg_mem = kzalloc(sizeof(*cfg_mem), GFP_KERNEL);
+ if (cfg_mem == NULL)
+ return -ENOMEM;
+
+ /* get the current Vcc setting */
+ vcc = p_dev->socket->socket.Vcc;
+
+ tuple = &cfg_mem->tuple;
+ tuple->TupleData = cfg_mem->buf;
+ tuple->TupleDataMax = sizeof(cfg_mem->buf)-1;
+ tuple->TupleOffset = 0;
+ tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ tuple->Attributes = 0;
+
+ ret = pcmcia_get_first_tuple(p_dev, tuple);
+ while (!ret) {
+ cistpl_cftable_entry_t *cfg = &cfg_mem->parse.cftable_entry;
+
+ if (pcmcia_get_tuple_data(p_dev, tuple))
+ goto next_entry;
+
+ if (pcmcia_parse_tuple(p_dev, tuple, &cfg_mem->parse))
+ goto next_entry;
+
+ /* default values */
+ p_dev->conf.ConfigIndex = cfg->index;
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ cfg_mem->dflt = *cfg;
+
+ ret = conf_check(p_dev, cfg, &cfg_mem->dflt, vcc, priv_data);
+ if (!ret)
+ break;
+
+next_entry:
+ ret = pcmcia_get_next_tuple(p_dev, tuple);
+ }
+ kfree(cfg_mem);
+ return ret;
+}
+#endif
+
+static int dev_conf_check(struct pcmcia_device *pdev,
+ cistpl_cftable_entry_t *cf, cistpl_cftable_entry_t *def_cf,
+ unsigned int vcc, void *priv_data)
+{
+ struct softing_cs *csdev = priv_data;
+ struct softing *sdev = &csdev->softing;
+ int ret;
+
+ if (!cf->index)
+ goto do_next;
+ /* power settings (Vcc & Vpp) */
+ if (cf->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cf->vcc.param[CISTPL_POWER_VNOM]/10000) {
+ mod_alert("%s: cf->Vcc mismatch\n", __FILE__);
+ goto do_next;
+ }
+ } else if (def_cf->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != def_cf->vcc.param[CISTPL_POWER_VNOM]/10000) {
+ mod_alert("%s: cf->Vcc mismatch\n", __FILE__);
+ goto do_next;
+ }
+ }
+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ pdev->conf.Vpp
+ = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ else if (def_cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ pdev->conf.Vpp
+ = def_cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ /* interrupt ? */
+ if (cf->irq.IRQInfo1 || def_cf->irq.IRQInfo1)
+ pdev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window */
+ pdev->io.NumPorts1
+ = pdev->io.NumPorts2
+ = 0;
+ /* Memory window */
+ if ((cf->mem.nwin > 0) || (def_cf->mem.nwin > 0)) {
+ memreq_t map;
+ cistpl_mem_t *mem
+ = (cf->mem.nwin) ? &cf->mem : &def_cf->mem;
+ /* softing specific: choose 8 or 16bit access */
+ csdev->win.Attributes = ((sdev->desc->generation >= 2)
+ ? WIN_DATA_WIDTH_16 : WIN_DATA_WIDTH_8)
+ | WIN_MEMORY_TYPE_CM
+ | WIN_ENABLE;
+ csdev->win.Base = mem->win[0].host_addr;
+ csdev->win.Size = mem->win[0].len;
+ csdev->win.AccessSpeed = 0;
+ ret = pcmcia_request_window(&pdev, &csdev->win, &pdev->win);
+ if (ret) {
+ mod_alert("pcmcia_request_window() mismatch\n");
+ goto do_next;
+ }
+ /* softing specific: choose slower access for old cards */
+ if (sdev->desc->generation < 2) {
+ pdev->win->ctl.flags
+ = MAP_ACTIVE | MAP_USE_WAIT;
+ pdev->win->ctl.speed = 3;
+ }
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(pdev->win, &map)) {
+ mod_alert("pcmcia_map_mem_page() mismatch\n");
+ goto do_next_win;
+ }
+ } else {
+ mod_info("no memory window in tuple %u", cf->index);
+ goto do_next;
+ }
+ return 0;
+do_next_win:
+do_next:
+ pcmcia_disable_device(pdev);
+ return -ENODEV;
+}
+
+static void driver_remove(struct pcmcia_device *pcmcia)
+{
+ struct softing *card = (struct softing *)pcmcia->priv;
+ struct softing_cs *cs = softing2cs(card);
+ mod_trace("%s,device'%s'", card->id.name, pcmcia->devname);
+ rm_softing(card);
+ /* release pcmcia stuff */
+ pcmcia_disable_device(pcmcia);
+ /* free bits */
+ kfree(cs);
+}
+
+static int __devinit driver_probe(struct pcmcia_device *pcmcia)
+{
+ struct softing_cs *cs;
+ struct softing *card;
+
+ mod_trace("on %s", pcmcia->devname);
+
+ /* Create new softing device */
+ cs = kzalloc(sizeof(*cs), GFP_KERNEL);
+ if (!cs)
+ goto no_mem;
+ /* setup links */
+ card = &cs->softing;
+ pcmcia->priv = card;
+ card->dev = &pcmcia->dev;
+ /* properties */
+ card->id.manf = pcmcia->manf_id;
+ card->id.prod = pcmcia->card_id;
+ card->desc = softing_lookup_desc(card->id.manf, card->id.prod);
+ if (card->desc->generation >= 2) {
+ card->fn.reset = card_reset_via_dpram;
+ } else {
+ card->fn.reset = card_reset_via_pcmcia;
+ card->fn.enable_irq = card_enable_irq_via_pcmcia;
+ }
+
+ card->nbus = 2;
+ /* pcmcia presets */
+ pcmcia->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+ pcmcia->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ pcmcia->irq.Handler = 0;
+ pcmcia->conf.Attributes = 0;
+ pcmcia->conf.IntType = INT_MEMORY_AND_IO;
+
+ if (pcmcia_loop_config(pcmcia, dev_conf_check, cs))
+ goto config_failed;
+
+ if (pcmcia_request_irq(pcmcia, &pcmcia->irq))
+ goto config_failed;
+
+ if (pcmcia_request_configuration(pcmcia, &pcmcia->conf))
+ goto config_failed;
+
+ card->dpram.phys = cs->win.Base;
+ card->dpram.size = cs->win.Size;
+
+ if (card->dpram.size != 0x1000) {
+ mod_alert("dpram size 0x%lx mismatch\n", card->dpram.size);
+ goto wrong_dpram;
+ }
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "[%s] %s: index 0x%02x",
+ THIS_MODULE->name,
+ pcmcia->devname,
+ pcmcia->conf.ConfigIndex);
+ if (pcmcia->conf.Vpp)
+ printk(", Vpp %d.%d", pcmcia->conf.Vpp/10, pcmcia->conf.Vpp%10);
+ if (pcmcia->conf.Attributes & CONF_ENABLE_IRQ) {
+ printk(", irq %d", pcmcia->irq.AssignedIRQ);
+ card->irq.nr = pcmcia->irq.AssignedIRQ;
+ }
+ if (pcmcia->win) {
+ int tmp;
+ const char *p;
+ printk(", mem 0x%08lx-0x%08lx"
+ , card->dpram.phys
+ , card->dpram.phys + card->dpram.size-1);
+ tmp = cs->win.Attributes;
+ while (tmp) {
+ p = lookup_mask(pcmcia_mem_attr, &tmp);
+ if (p)
+ printk(" %s", p);
+ }
+ }
+ printk("\n");
+
+ if (mk_softing(card))
+ goto softing_failed;
+ return 0;
+
+softing_failed:
+wrong_dpram:
+config_failed:
+ kfree(cs);
+no_mem:
+ pcmcia_disable_device(pcmcia);
+ return -ENODEV;
+}
+
+static struct pcmcia_device_id driver_ids[] = {
+ /* softing */
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0001),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0002),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0004),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0005),
+ /* vector , manufacturer? */
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0081),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0084),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0085),
+ /* EDIC */
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0102),
+ PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0105),
+ PCMCIA_DEVICE_NULL,
+};
+
+MODULE_DEVICE_TABLE(pcmcia, driver_ids);
+
+static struct pcmcia_driver softing_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "softing_cs",
+ },
+ .probe = driver_probe,
+ .remove = driver_remove,
+ .id_table = driver_ids,
+};
+
+static int __init mod_start(void)
+{
+ mod_trace("");
+ return pcmcia_register_driver(&softing_cs_driver);
+}
+
+static void __exit mod_stop(void)
+{
+ mod_trace("");
+ pcmcia_unregister_driver(&softing_cs_driver);
+}
+
+module_init(mod_start);
+module_exit(mod_stop);
+
+MODULE_DESCRIPTION("softing CANcard driver"
+ ", links PCMCIA card to softing driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("softing CANcard2");
+
diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c
new file mode 100644
index 000000000000..4d20774c9cb9
--- /dev/null
+++ b/drivers/net/can/softing/softing_fw.c
@@ -0,0 +1,685 @@
+/*
+* drivers/net/can/softing/softing_fw.c
+*
+* Copyright (C) 2008
+*
+* - Kurt Van Dijck, EIA Electronics
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "softing.h"
+
+#define fw_dir "softing-4.6/"
+
+const struct can_bittiming_const softing_btr_const = {
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4, /* overruled */
+ .brp_min = 1,
+ .brp_max = 32, /* overruled */
+ .brp_inc = 1,
+};
+
+static const struct softing_desc carddescs[] = {
+{
+ .name = "CANcard",
+ .manf = 0x0168, .prod = 0x001,
+ .generation = 1,
+ .freq = 16, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "CANcard-NEC",
+ .manf = 0x0168, .prod = 0x002,
+ .generation = 1,
+ .freq = 16, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "CANcard-SJA",
+ .manf = 0x0168, .prod = 0x004,
+ .generation = 1,
+ .freq = 20, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",},
+}, {
+ .name = "CANcard-2",
+ .manf = 0x0168, .prod = 0x005,
+ .generation = 2,
+ .freq = 24, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",},
+}, {
+ .name = "Vector-CANcard",
+ .manf = 0x0168, .prod = 0x081,
+ .generation = 1,
+ .freq = 16, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "Vector-CANcard-SJA",
+ .manf = 0x0168, .prod = 0x084,
+ .generation = 1,
+ .freq = 20, .max_brp = 32, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",},
+}, {
+ .name = "Vector-CANcard-2",
+ .manf = 0x0168, .prod = 0x085,
+ .generation = 2,
+ .freq = 24, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",},
+}, {
+ .name = "EDICcard-NEC",
+ .manf = 0x0168, .prod = 0x102,
+ .generation = 1,
+ .freq = 16, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",},
+}, {
+ .name = "EDICcard-2",
+ .manf = 0x0168, .prod = 0x105,
+ .generation = 2,
+ .freq = 24, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x0800,
+ .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",},
+ .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",},
+ .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",},
+ },
+
+/* never tested, but taken from original softing */
+{ .name = "CAN-AC2-104",
+ .manf = 0x0000, .prod = 0x009,
+ .generation = 1,
+ .freq = 25, .max_brp = 64, .max_sjw = 4,
+ .dpram_size = 0x1000,
+ .boot = {0x0000, 0x000000, fw_dir "boot104.bin",},
+ .load = {0x0800, 0x035000, fw_dir "ld104.bin",},
+ .app = {0x0010, 0x120000, fw_dir "canpc104.bin",},
+ },
+{0, 0,},
+};
+
+const struct softing_desc *softing_lookup_desc
+ (unsigned int manf, unsigned int prod)
+{
+ const struct softing_desc *lp = carddescs;
+ for (; lp->name; ++lp) {
+ if ((lp->manf == manf) && (lp->prod == prod))
+ return lp;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(softing_lookup_desc);
+
+int softing_fct_cmd(struct softing *card, int cmd, int vector, const char *msg)
+{
+ int ret;
+ unsigned long stamp;
+ if (vector == RES_OK)
+ vector = RES_NONE;
+ card->dpram.fct->param[0] = cmd;
+ card->dpram.fct->host_access = vector;
+ /* be sure to flush this to the card */
+ wmb();
+ stamp = jiffies;
+ /*wait for card */
+ do {
+ ret = card->dpram.fct->host_access;
+ /* don't have any cached variables */
+ rmb();
+ if (ret == RES_OK) {
+ /*don't read return-value now */
+ ret = card->dpram.fct->returned;
+ if (ret)
+ mod_alert("%s returned %u", msg, ret);
+ return 0;
+ }
+ if ((jiffies - stamp) >= 1 * HZ)
+ break;
+ if (in_interrupt())
+ /* go as fast as possible */
+ continue;
+ /* process context => relax */
+ schedule();
+ } while (!signal_pending(current));
+
+ if (ret == RES_NONE) {
+ mod_alert("%s, no response from card on %u/0x%02x"
+ , msg, cmd, vector);
+ return 1;
+ } else {
+ mod_alert("%s, bad response from card on %u/0x%02x, 0x%04x"
+ , msg, cmd, vector, ret);
+ /*make sure to return something not 0 */
+ return ret ? ret : 1;
+ }
+}
+
+int softing_bootloader_command(struct softing *card
+ , int command, const char *msg)
+{
+ int ret;
+ unsigned long stamp;
+ card->dpram.receipt[0] = RES_NONE;
+ card->dpram.command[0] = command;
+ /* be sure to flush this to the card */
+ wmb();
+ stamp = jiffies;
+ /*wait for card */
+ do {
+ ret = card->dpram.receipt[0];
+ /* don't have any cached variables */
+ rmb();
+ if (ret == RES_OK)
+ return 0;
+ if ((jiffies - stamp) >= (3 * HZ))
+ break;
+ schedule();
+ } while (!signal_pending(current));
+
+ switch (ret) {
+ case RES_NONE:
+ mod_alert("%s: no response from card", msg);
+ break;
+ case RES_NOK:
+ mod_alert("%s: response from card nok", msg);
+ break;
+ case RES_UNKNOWN:
+ mod_alert("%s: command 0x%04x unknown", msg, command);
+ break;
+ default:
+ mod_alert("%s: bad response from card (%u)]", msg, ret);
+ break;
+ }
+ return ret ? ret : 1;
+}
+
+struct fw_hdr {
+ u16 type;
+ u32 addr;
+ u16 len;
+ u16 checksum;
+ const unsigned char *base;
+} __attribute__ ((packed));
+
+static int fw_parse(const unsigned char **pmem, struct fw_hdr *hdr)
+{
+ u16 tmp;
+ const unsigned char *mem;
+ const unsigned char *end;
+ mem = *pmem;
+ hdr->type = (mem[0] << 0) | (mem[1] << 8);
+ hdr->addr = (mem[2] << 0) | (mem[3] << 8)
+ | (mem[4] << 16) | (mem[5] << 24);
+ hdr->len = (mem[6] << 0) | (mem[7] << 8);
+ hdr->base = &mem[8];
+ hdr->checksum =
+ (hdr->base[hdr->len] << 0) | (hdr->base[hdr->len + 1] << 8);
+ for (tmp = 0, mem = *pmem, end = &hdr->base[hdr->len]; mem < end; ++mem)
+ tmp += *mem;
+ if (tmp != hdr->checksum)
+ return EINVAL;
+ *pmem += 10 + hdr->len;
+ return 0;
+}
+
+int softing_load_fw(const char *file, struct softing *card,
+ unsigned char *virt, unsigned int size, int offset)
+{
+ const struct firmware *fw;
+ const unsigned char *mem;
+ const unsigned char *end;
+ int ret;
+ u32 start_addr;
+ struct fw_hdr rec;
+ int ok = 0;
+ unsigned char buf[256];
+
+ ret = request_firmware(&fw, file, card->dev);
+ if (ret) {
+ mod_alert("request_firmware(%s) got %i", file, ret);
+ return ret;
+ }
+ mod_trace("%s, firmware(%s) got %u bytes, offset %c0x%04x"
+ , card->id.name, file, (unsigned int)fw->size,
+ (offset >= 0) ? '+' : '-', abs(offset));
+ /* parse the firmware */
+ mem = fw->data;
+ end = &mem[fw->size];
+ /* look for header record */
+ if (fw_parse(&mem, &rec))
+ goto fw_end;
+ if (rec.type != 0xffff) {
+ mod_alert("firware starts with type 0x%04x", rec.type);
+ goto fw_end;
+ }
+ if (strncmp("Structured Binary Format, Softing GmbH"
+ , rec.base, rec.len)) {
+ mod_info("firware string '%.*s'", rec.len, rec.base);
+ goto fw_end;
+ }
+ ok |= 1;
+ /* ok, we had a header */
+ while (mem < end) {
+ if (fw_parse(&mem, &rec))
+ break;
+ if (rec.type == 3) {
+ /*start address */
+ start_addr = rec.addr;
+ ok |= 2;
+ continue;
+ } else if (rec.type == 1) {
+ /*eof */
+ ok |= 4;
+ goto fw_end;
+ } else if (rec.type != 0) {
+ mod_alert("unknown record type 0x%04x", rec.type);
+ break;
+ }
+
+ if ((rec.addr + rec.len + offset) > size) {
+ mod_alert("firmware out of range (0x%08x / 0x%08x)"
+ , (rec.addr + rec.len + offset), size);
+ goto fw_end;
+ }
+ memcpy_toio(&virt[rec.addr + offset],
+ rec.base, rec.len);
+ /* be sure to flush caches from IO space */
+ mb();
+ if (rec.len > sizeof(buf)) {
+ mod_info("record is big (%u bytes), not verifying"
+ , rec.len);
+ continue;
+ }
+ /* verify record data */
+ memcpy_fromio(buf, &virt[rec.addr + offset], rec.len);
+ if (!memcmp(buf, rec.base, rec.len))
+ /* is ok */
+ continue;
+ mod_alert("0x%08x:0x%03x at 0x%p failed", rec.addr, rec.len
+ , &virt[rec.addr + offset]);
+ goto fw_end;
+ }
+fw_end:
+ release_firmware(fw);
+ if (0x5 == (ok & 0x5)) {
+ /*got eof & start */
+ return 0;
+ }
+ mod_alert("failed");
+ return EINVAL;
+}
+
+int softing_load_app_fw(const char *file, struct softing *card)
+{
+ const struct firmware *fw;
+ const unsigned char *mem;
+ const unsigned char *end;
+ int ret;
+ struct fw_hdr rec;
+ int ok = 0;
+ u32 start_addr = 0;
+ u16 rx_sum;
+ unsigned int sum;
+ const unsigned char *mem_lp;
+ const unsigned char *mem_end;
+ struct cpy {
+ u32 src;
+ u32 dst;
+ u16 len;
+ u8 do_cs;
+ } __attribute__((packed)) *pcpy =
+ (struct cpy *)&card->dpram.command[1];
+
+ ret = request_firmware(&fw, file, card->dev);
+ if (ret) {
+ mod_alert("request_firmware(%s) got %i", file, ret);
+ return ret;
+ }
+ mod_trace("%s, firmware(%s) got %lu bytes", card->id.name, file,
+ (unsigned long)fw->size);
+ /* parse the firmware */
+ mem = fw->data;
+ end = &mem[fw->size];
+ /* look for header record */
+ if (fw_parse(&mem, &rec))
+ goto fw_end;
+ if (rec.type != 0xffff) {
+ mod_alert("firware starts with type 0x%04x", rec.type);
+ goto fw_end;
+ }
+ if (strncmp("Structured Binary Format, Softing GmbH"
+ , rec.base, rec.len)) {
+ mod_info("firware string '%.*s'", rec.len, rec.base);
+ goto fw_end;
+ }
+ ok |= 1;
+ /* ok, we had a header */
+ while (mem < end) {
+ if (fw_parse(&mem, &rec))
+ break;
+
+ if (rec.type == 3) {
+ /*start address */
+ start_addr = rec.addr;
+ ok |= 2;
+ continue;
+ } else if (rec.type == 1) {
+ /*eof */
+ ok |= 4;
+ goto fw_end;
+ } else if (rec.type != 0) {
+ mod_alert("unknown record type 0x%04x", rec.type);
+ break;
+ }
+ /* regualar data */
+ for (sum = 0, mem_lp = rec.base, mem_end = &mem_lp[rec.len];
+ mem_lp < mem_end; ++mem_lp)
+ sum += *mem_lp;
+
+ memcpy_toio(&card->dpram. virt[card->desc->app.offs],
+ rec.base, rec.len);
+ pcpy->src = card->desc->app.offs + card->desc->app.addr;
+ pcpy->dst = rec.addr;
+ pcpy->len = rec.len;
+ pcpy->do_cs = 1;
+ if (softing_bootloader_command(card, 1, "loading app."))
+ goto fw_end;
+ /*verify checksum */
+ rx_sum = card->dpram.receipt[1];
+ if (rx_sum != (sum & 0xffff)) {
+ mod_alert("SRAM seems to be damaged"
+ ", wanted 0x%04x, got 0x%04x", sum, rx_sum);
+ goto fw_end;
+ }
+ }
+fw_end:
+ release_firmware(fw);
+ if (ok == 7) {
+ /*got start, start_addr, & eof */
+ struct cmd {
+ u32 start;
+ u8 autorestart;
+ } *pcmd = (struct cmd *)&card->dpram.command[1];
+ pcmd->start = start_addr;
+ pcmd->autorestart = 1;
+ if (!softing_bootloader_command(card, 3, "start app.")) {
+ mod_trace("%s: card app. run at 0x%06x"
+ , card->id.name, start_addr);
+ return 0;
+ }
+ }
+ mod_alert("failed");
+ return EINVAL;
+}
+
+int softing_reset_chip(struct softing *card)
+{
+ mod_trace("%s", card->id.name);
+ do {
+ /*reset chip */
+ card->dpram.info->reset_rcv_fifo = 0;
+ card->dpram.info->reset = 1;
+ if (!softing_fct_cmd(card, 0, 0, "reset_chip"))
+ break;
+ if (signal_pending(current))
+ goto failed;
+ /*sync */
+ if (softing_fct_cmd(card, 99, 0x55, "sync-a"))
+ goto failed;
+ if (softing_fct_cmd(card, 99, 0xaa, "sync-a"))
+ goto failed;
+ } while (1);
+ card->tx.pending = 0;
+ return 0;
+failed:
+ return -EIO;
+}
+
+int softing_reinit(struct softing *card, int bus0, int bus1)
+{
+ int ret;
+ int restarted_bus = -1;
+ mod_trace("%s", card->id.name);
+ if (!card->fw.up)
+ return -EIO;
+ if (bus0 < 0) {
+ bus0 = (card->bus[0]->netdev->flags & IFF_UP) ? 1 : 0;
+ if (bus0)
+ restarted_bus = 0;
+ } else if (bus1 < 0) {
+ bus1 = (card->bus[1]->netdev->flags & IFF_UP) ? 1 : 0;
+ if (bus1)
+ restarted_bus = 1;
+ }
+ /* collect info */
+ if (card->bus[0]) {
+ card->bus[0]->can.state = CAN_STATE_STOPPED;
+ softing_flush_echo_skb(card->bus[0]);
+ }
+ if (card->bus[1]) {
+ card->bus[1]->can.state = CAN_STATE_STOPPED;
+ softing_flush_echo_skb(card->bus[1]);
+ }
+
+ /* start acting */
+ if (!bus0 && !bus1) {
+ softing_card_irq(card, 0);
+ softing_reset_chip(card);
+ if (card->bus[0])
+ netif_carrier_off(card->bus[0]->netdev);
+ if (card->bus[1])
+ netif_carrier_off(card->bus[1]->netdev);
+ return 0;
+ }
+ ret = softing_reset_chip(card);
+ if (ret) {
+ softing_card_irq(card, 0);
+ return ret;
+ }
+ if (bus0) {
+ /*init chip */
+ card->dpram.fct->param[1] = card->bus[0]->can.bittiming.brp;
+ card->dpram.fct->param[2] = card->bus[0]->can.bittiming.sjw;
+ card->dpram.fct->param[3] =
+ card->bus[0]->can.bittiming.phase_seg1 +
+ card->bus[0]->can.bittiming.prop_seg;
+ card->dpram.fct->param[4] =
+ card->bus[0]->can.bittiming.phase_seg2;
+ card->dpram.fct->param[5] = (card->bus[0]->can.ctrlmode &
+ CAN_CTRLMODE_3_SAMPLES)?1:0;
+ if (softing_fct_cmd(card, 1, 0, "initialize_chip[0]"))
+ goto failed;
+ /*set mode */
+ card->dpram.fct->param[1] = 0;
+ card->dpram.fct->param[2] = 0;
+ if (softing_fct_cmd(card, 3, 0, "set_mode[0]"))
+ goto failed;
+ /*set filter */
+ card->dpram.fct->param[1] = 0x0000;/*card->bus[0].s.msg; */
+ card->dpram.fct->param[2] = 0x07ff;/*card->bus[0].s.msk; */
+ card->dpram.fct->param[3] = 0x0000;/*card->bus[0].l.msg; */
+ card->dpram.fct->param[4] = 0xffff;/*card->bus[0].l.msk; */
+ card->dpram.fct->param[5] = 0x0000;/*card->bus[0].l.msg >> 16;*/
+ card->dpram.fct->param[6] = 0x1fff;/*card->bus[0].l.msk >> 16;*/
+ if (softing_fct_cmd(card, 7, 0, "set_filter[0]"))
+ goto failed;
+ /*set output control */
+ card->dpram.fct->param[1] = card->bus[0]->output;
+ if (softing_fct_cmd(card, 5, 0, "set_output[0]"))
+ goto failed;
+ }
+ if (bus1) {
+ /*init chip2 */
+ card->dpram.fct->param[1] = card->bus[1]->can.bittiming.brp;
+ card->dpram.fct->param[2] = card->bus[1]->can.bittiming.sjw;
+ card->dpram.fct->param[3] =
+ card->bus[1]->can.bittiming.phase_seg1 +
+ card->bus[1]->can.bittiming.prop_seg;
+ card->dpram.fct->param[4] =
+ card->bus[1]->can.bittiming.phase_seg2;
+ card->dpram.fct->param[5] = (card->bus[1]->can.ctrlmode &
+ CAN_CTRLMODE_3_SAMPLES)?1:0;
+ if (softing_fct_cmd(card, 2, 0, "initialize_chip[1]"))
+ goto failed;
+ /*set mode2 */
+ card->dpram.fct->param[1] = 0;
+ card->dpram.fct->param[2] = 0;
+ if (softing_fct_cmd(card, 4, 0, "set_mode[1]"))
+ goto failed;
+ /*set filter2 */
+ card->dpram.fct->param[1] = 0x0000;/*card->bus[1].s.msg; */
+ card->dpram.fct->param[2] = 0x07ff;/*card->bus[1].s.msk; */
+ card->dpram.fct->param[3] = 0x0000;/*card->bus[1].l.msg; */
+ card->dpram.fct->param[4] = 0xffff;/*card->bus[1].l.msk; */
+ card->dpram.fct->param[5] = 0x0000;/*card->bus[1].l.msg >> 16;*/
+ card->dpram.fct->param[6] = 0x1fff;/*card->bus[1].l.msk >> 16;*/
+ if (softing_fct_cmd(card, 8, 0, "set_filter[1]"))
+ goto failed;
+ /*set output control2 */
+ card->dpram.fct->param[1] = card->bus[1]->output;
+ if (softing_fct_cmd(card, 6, 0, "set_output[1]"))
+ goto failed;
+ }
+ /*set interrupt */
+ /*enable_error_frame */
+ if (softing_fct_cmd(card, 51, 0, "enable_error_frame"))
+ goto failed;
+ /*initialize interface */
+ card->dpram.fct->param[1] = 1;
+ card->dpram.fct->param[2] = 1;
+ card->dpram.fct->param[3] = 1;
+ card->dpram.fct->param[4] = 1;
+ card->dpram.fct->param[5] = 1;
+ card->dpram.fct->param[6] = 1;
+ card->dpram.fct->param[7] = 1;
+ card->dpram.fct->param[8] = 1;
+ card->dpram.fct->param[9] = 1;
+ card->dpram.fct->param[10] = 1;
+ if (softing_fct_cmd(card, 17, 0, "initialize_interface"))
+ goto failed;
+ /*enable_fifo */
+ if (softing_fct_cmd(card, 36, 0, "enable_fifo"))
+ goto failed;
+ /*enable fifo tx ack */
+ if (softing_fct_cmd(card, 13, 0, "fifo_tx_ack[0]"))
+ goto failed;
+ /*enable fifo tx ack2 */
+ if (softing_fct_cmd(card, 14, 0, "fifo_tx_ack[1]"))
+ goto failed;
+ /*enable timestamps */
+ /*is default, no code found */
+ /*start_chip */
+ if (softing_fct_cmd(card, 11, 0, "start_chip"))
+ goto failed;
+ card->dpram.info->bus_state = 0;
+ card->dpram.info->bus_state2 = 0;
+ mod_info("ok for %s, %s/%s\n", card->bus[0]->netdev->name,
+ card->bus[1]->netdev->name, card->id.name);
+ if (card->desc->generation < 2) {
+ card->dpram.irq->to_host = 0;
+ /* flush the DPRAM caches */
+ wmb();
+ }
+ /*run once */
+ /*the bottom halve will start flushing the tx-queue too */
+ tasklet_schedule(&card->irq.bh);
+
+ ret = softing_card_irq(card, 1);
+ if (ret)
+ goto failed;
+
+ /*TODO: generate RESTARTED messages */
+
+ if (card->bus[0] && bus0) {
+ card->bus[0]->can.state = CAN_STATE_ACTIVE;
+ netif_carrier_on(card->bus[0]->netdev);
+ }
+ if (card->bus[1] && bus1) {
+ card->bus[1]->can.state = CAN_STATE_ACTIVE;
+ netif_carrier_on(card->bus[1]->netdev);
+ }
+ return 0;
+failed:
+ softing_card_irq(card, 0);
+ softing_reset_chip(card);
+ if (card->bus[0])
+ netif_carrier_off(card->bus[0]->netdev);
+ if (card->bus[1])
+ netif_carrier_off(card->bus[1]->netdev);
+ return -EIO;
+}
+
+
+int softing_default_output(struct softing *card, struct softing_priv *priv)
+{
+ switch (priv->chip) {
+ case 1000:
+ if (card->desc->generation < 2)
+ return 0xfb;
+ return 0xfa;
+ case 5:
+ return 0x60;
+ default:
+ return 0x40;
+ }
+}
+
+u32 softing_time2usec(struct softing *card, u32 raw)
+{
+ /*TODO : don't loose higher order bits in computation */
+ switch (card->desc->freq) {
+ case 20:
+ return raw * 4 / 5;
+ case 24:
+ return raw * 2 / 3;
+ case 25:
+ return raw * 16 / 25;
+ case 0:
+ case 16:
+ default:
+ return raw;
+ }
+}
+
+
diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c
new file mode 100644
index 000000000000..955f9c85a3a4
--- /dev/null
+++ b/drivers/net/can/softing/softing_main.c
@@ -0,0 +1,1058 @@
+/*
+* drivers/net/can/softing/softing_main.c
+*
+* Copyright (C) 2008
+*
+* - Kurt Van Dijck, EIA Electronics
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "softing.h"
+
+/* this is the worst thing on the softing API
+ * 2 busses are driven together, I don't know how
+ * to recover a single of them.
+ * Therefore, when one bus is modified, the other
+ * is flushed too
+ */
+void softing_flush_echo_skb(struct softing_priv *priv)
+{
+ can_close_cleanup(priv->netdev);
+ priv->tx.pending = 0;
+ priv->tx.echo_put = 0;
+ priv->tx.echo_get = 0;
+}
+
+/*softing_unlocked_tx_run:*/
+/*trigger the tx queue-ing*/
+/*no locks are grabbed, so be sure to have the spin spinlock*/
+static int netdev_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct softing_priv *priv = (struct softing_priv *)netdev_priv(dev);
+ struct softing *card = priv->card;
+ int ret;
+ int bhlock;
+ u8 *ptr;
+ u8 cmd;
+ unsigned int fifo_wr;
+ struct can_frame msg;
+
+ ret = -ENOTTY;
+ if (in_interrupt()) {
+ bhlock = 0;
+ spin_lock(&card->spin);
+ } else {
+ bhlock = 1;
+ spin_lock_bh(&card->spin);
+ }
+ if (!card->fw.up) {
+ ret = -EIO;
+ goto xmit_done;
+ }
+ if (netif_carrier_ok(priv->netdev) <= 0) {
+ ret = -EBADF;
+ goto xmit_done;
+ }
+ if (card->tx.pending >= TXMAX) {
+ ret = -EBUSY;
+ goto xmit_done;
+ }
+ if (priv->tx.pending >= CAN_ECHO_SKB_MAX) {
+ ret = -EBUSY;
+ goto xmit_done;
+ }
+ fifo_wr = card->dpram.tx->wr;
+ if (fifo_wr == card->dpram.tx->rd) {
+ /*fifo full */
+ ret = -EAGAIN;
+ goto xmit_done;
+ }
+ memcpy(&msg, skb->data, sizeof(msg));
+ ptr = &card->dpram.tx->fifo[fifo_wr][0];
+ cmd = CMD_TX;
+ if (msg.can_id & CAN_RTR_FLAG)
+ cmd |= CMD_RTR;
+ if (msg.can_id & CAN_EFF_FLAG)
+ cmd |= CMD_XTD;
+ if (priv->index)
+ cmd |= CMD_BUS2;
+ *ptr++ = cmd;
+ *ptr++ = msg.can_dlc;
+ *ptr++ = (msg.can_id >> 0);
+ *ptr++ = (msg.can_id >> 8);
+ if (msg.can_id & CAN_EFF_FLAG) {
+ *ptr++ = (msg.can_id >> 16);
+ *ptr++ = (msg.can_id >> 24);
+ } else {
+ /*increment 1, not 2 as you might think */
+ ptr += 1;
+ }
+ if (!(msg.can_id & CAN_RTR_FLAG))
+ memcpy_toio(ptr, &msg.data[0], msg.can_dlc);
+ if (++fifo_wr >=
+ sizeof(card->dpram.tx->fifo) /
+ sizeof(card->dpram.tx->fifo[0]))
+ fifo_wr = 0;
+ card->dpram.tx->wr = fifo_wr;
+ ret = 0;
+ ++card->tx.pending;
+ ++priv->tx.pending;
+ can_put_echo_skb(skb, dev, priv->tx.echo_put);
+ ++priv->tx.echo_put;
+ if (priv->tx.echo_put >= CAN_ECHO_SKB_MAX)
+ priv->tx.echo_put = 0;
+ /* clear pointer, so don't erase later */
+ skb = 0;
+xmit_done:
+ if (bhlock)
+ spin_unlock_bh(&card->spin);
+ else
+ spin_unlock(&card->spin);
+ if (card->tx.pending >= TXMAX) {
+ struct softing_priv *bus;
+ int j;
+ for (j = 0; j < card->nbus; ++j) {
+ bus = card->bus[j];
+ if (!bus)
+ continue;
+ netif_stop_queue(bus->netdev);
+ }
+ }
+
+ /* free skb, if not handled by the driver */
+ if (skb)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static int softing_dev_svc_once(struct softing *card)
+{
+ int j;
+ struct softing_priv *bus;
+ struct sk_buff *skb;
+ struct can_frame msg;
+
+ unsigned int fifo_rd;
+ unsigned int cnt = 0;
+ struct net_device_stats *stats;
+
+ memset(&msg, 0, sizeof(msg));
+ if (card->dpram.rx->lost_msg) {
+ /*reset condition */
+ card->dpram.rx->lost_msg = 0;
+ /* prepare msg */
+ msg.can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
+ msg.can_dlc = CAN_ERR_DLC;
+ msg.data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+ /*service to both busses, we don't know which one generated */
+ for (j = 0; j < card->nbus; ++j) {
+ bus = card->bus[j];
+ if (!bus)
+ continue;
+ if (!netif_carrier_ok(bus->netdev))
+ continue;
+ ++bus->can.can_stats.data_overrun;
+ skb = dev_alloc_skb(sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
+ skb->dev = bus->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(msg)), &msg, sizeof(msg));
+ if (netif_rx(skb))
+ dev_kfree_skb_irq(skb);
+ }
+ memset(&msg, 0, sizeof(msg));
+ ++cnt;
+ }
+
+ fifo_rd = card->dpram.rx->rd;
+ if (++fifo_rd >=
+ sizeof(card->dpram.rx->fifo) / sizeof(card->dpram.rx->fifo[0]))
+ fifo_rd = 0;
+ if (card->dpram.rx->wr != fifo_rd) {
+ u8 *ptr;
+ u32 tmp;
+ u8 cmd;
+ int do_skb;
+
+ ptr = &card->dpram.rx->fifo[fifo_rd][0];
+
+ cmd = *ptr++;
+ if (cmd == 0xff) {
+ /*not quite usefull, probably the card has got out */
+ mod_alert("got cmd 0x%02x, I suspect the card is lost"
+ , cmd);
+ }
+ /*mod_trace("0x%02x", cmd);*/
+ bus = card->bus[0];
+ if (cmd & CMD_BUS2)
+ bus = card->bus[1];
+
+ stats = &bus->netdev->stats;
+ if (cmd & CMD_ERR) {
+ u8 can_state;
+ u8 state;
+ state = *ptr++;
+
+ msg.can_id = CAN_ERR_FLAG;
+ msg.can_dlc = CAN_ERR_DLC;
+
+ if (state & 0x80) {
+ can_state = CAN_STATE_BUS_OFF;
+ msg.can_id |= CAN_ERR_BUSOFF;
+ state = 2;
+ } else if (state & 0x60) {
+ can_state = CAN_STATE_BUS_PASSIVE;
+ msg.can_id |= CAN_ERR_BUSERROR;
+ state = 1;
+ } else {
+ can_state = CAN_STATE_ACTIVE;
+ state = 0;
+ do_skb = 0;
+ }
+ /*update DPRAM */
+ if (!bus->index)
+ card->dpram.info->bus_state = state;
+ else
+ card->dpram.info->bus_state2 = state;
+ /*timestamp */
+ tmp = (ptr[0] << 0)
+ |(ptr[1] << 8)
+ |(ptr[2] << 16)
+ |(ptr[3] << 24);
+ ptr += 4;
+ /*msg.time = */ softing_time2usec(card, tmp);
+ /*trigger dual port RAM */
+ mb();
+ card->dpram.rx->rd = fifo_rd;
+ /*update internal status */
+ if (can_state != bus->can.state) {
+ bus->can.state = can_state;
+ if (can_state == 1)
+ bus->can.can_stats.error_passive += 1;
+ }
+ bus->can.can_stats.bus_error += 1;
+
+ /*trigger socketcan */
+ if (state == 2) {
+ /* this calls can_close_cleanup() */
+ softing_flush_echo_skb(bus);
+ can_bus_off(bus->netdev);
+ netif_stop_queue(bus->netdev);
+ }
+ if ((state == CAN_STATE_BUS_OFF)
+ || (state == CAN_STATE_BUS_PASSIVE)) {
+ skb = dev_alloc_skb(sizeof(msg));
+ if (!skb)
+ return -ENOMEM;
+ skb->dev = bus->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(msg)), &msg,
+ sizeof(msg));
+ if (netif_rx(skb))
+ dev_kfree_skb_irq(skb);
+ }
+ } else {
+ if (cmd & CMD_RTR)
+ msg.can_id |= CAN_RTR_FLAG;
+ /* acknowledge, was tx msg
+ * no real tx flag to set
+ if (cmd & CMD_ACK) {
+ }
+ */
+ msg.can_dlc = *ptr++;
+ if (msg.can_dlc > 8)
+ msg.can_dlc = 8;
+ if (cmd & CMD_XTD) {
+ msg.can_id |= CAN_EFF_FLAG;
+ msg.can_id |=
+ (ptr[0] << 0)
+ | (ptr[1] << 8)
+ | (ptr[2] << 16)
+ | (ptr[3] << 24);
+ ptr += 4;
+ } else {
+ msg.can_id |= (ptr[0] << 0) | (ptr[1] << 8);
+ ptr += 2;
+ }
+ tmp = (ptr[0] << 0)
+ | (ptr[1] << 8)
+ | (ptr[2] << 16)
+ | (ptr[3] << 24);
+ ptr += 4;
+ /*msg.time = */ softing_time2usec(card, tmp);
+ memcpy_fromio(&msg.data[0], ptr, 8);
+ ptr += 8;
+ /*trigger dual port RAM */
+ mb();
+ card->dpram.rx->rd = fifo_rd;
+ /*update socket */
+ if (cmd & CMD_ACK) {
+ can_get_echo_skb(bus->netdev, bus->tx.echo_get);
+ ++bus->tx.echo_get;
+ if (bus->tx.echo_get >= CAN_ECHO_SKB_MAX)
+ bus->tx.echo_get = 0;
+ if (bus->tx.pending)
+ --bus->tx.pending;
+ if (card->tx.pending)
+ --card->tx.pending;
+ stats->tx_packets += 1;
+ stats->tx_bytes += msg.can_dlc;
+ } else {
+ stats->rx_packets += 1;
+ stats->rx_bytes += msg.can_dlc;
+ bus->netdev->last_rx = jiffies;
+ skb = dev_alloc_skb(sizeof(msg));
+ if (skb) {
+ skb->dev = bus->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ memcpy(skb_put(skb, sizeof(msg)), &msg,
+ sizeof(msg));
+ if (netif_rx(skb))
+ dev_kfree_skb_irq(skb);
+ }
+ }
+ }
+ ++cnt;
+ }
+ return cnt;
+}
+
+static void softing_dev_svc(unsigned long param)
+{
+ struct softing *card = (struct softing *)param;
+ struct softing_priv *bus;
+ int j;
+ int offset;
+
+ spin_lock(&card->spin);
+ while (softing_dev_svc_once(card) > 0)
+ ++card->irq.svc_count;
+ /*resume tx queue's */
+ offset = card->tx.last_bus;
+ for (j = 0; j < card->nbus; ++j) {
+ if (card->tx.pending >= TXMAX)
+ break;
+ bus = card->bus[(j + offset) % card->nbus];
+ if (netif_carrier_ok(bus->netdev))
+ netif_wake_queue(bus->netdev);
+ }
+ spin_unlock(&card->spin);
+}
+
+static void card_seems_down(struct softing *card)
+{
+ /* free interrupt, but probably
+ * in wrong (interrupt) context
+ if (card->irq.requested) {
+ free_irq(card->irq.nr, card);
+ card->irq.requested = 0;
+ card->fw.up = 0;
+ }
+ */
+ mod_alert("I think the card is vanished");
+}
+
+static
+irqreturn_t dev_interrupt_shared(int irq, void *dev_id)
+{
+ struct softing *card = (struct softing *)dev_id;
+ unsigned char ir;
+ ir = card->dpram.virt[0xe02];
+ card->dpram.virt[0xe02] = 0;
+ if (card->dpram.rx->rd == 0xffff) {
+ card_seems_down(card);
+ return IRQ_NONE;
+ }
+ if (ir == 1) {
+ tasklet_schedule(&card->irq.bh);
+ return IRQ_HANDLED;
+ } else if (ir == 0x10) {
+ return IRQ_NONE;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static
+irqreturn_t dev_interrupt_nshared(int irq, void *dev_id)
+{
+ struct softing *card = (struct softing *)dev_id;
+ unsigned char irq_host;
+ irq_host = card->dpram.irq->to_host;
+ /* make sure we have a copy, before clearing the variable in DPRAM */
+ rmb();
+ card->dpram.irq->to_host = 0;
+ /* make sure we cleared it */
+ wmb();
+ mod_trace("0x%02x", irq_host);
+ if (card->dpram.rx->rd == 0xffff) {
+ card_seems_down(card);
+ return IRQ_NONE;
+ }
+ tasklet_schedule(&card->irq.bh);
+ return IRQ_HANDLED;
+}
+
+static int netdev_open(struct net_device *ndev)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ struct softing *card = priv->card;
+ int fw;
+ int ret;
+
+ mod_trace("%s", ndev->name);
+ /* determine and set bittime */
+ ret = can_set_bittiming(ndev);
+ if (ret)
+ return ret;
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ fw = card->fw.up;
+ if (fw)
+ softing_reinit(card
+ , (card->bus[0] == priv) ? 1 : -1
+ , (card->bus[1] == priv) ? 1 : -1);
+ mutex_unlock(&card->fw.lock);
+ if (!fw)
+ return -EIO;
+ netif_start_queue(ndev);
+ return 0;
+}
+
+static int netdev_stop(struct net_device *ndev)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ struct softing *card = priv->card;
+ int fw;
+
+ mod_trace("%s", ndev->name);
+ netif_stop_queue(ndev);
+ netif_carrier_off(ndev);
+ softing_flush_echo_skb(priv);
+ can_close_cleanup(ndev);
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ fw = card->fw.up;
+ if (fw)
+ softing_reinit(card
+ , (card->bus[0] == priv) ? 0 : -1
+ , (card->bus[1] == priv) ? 0 : -1);
+ mutex_unlock(&card->fw.lock);
+ if (!fw)
+ return -EIO;
+ return 0;
+}
+
+static int candev_get_state(struct net_device *ndev, enum can_state *state)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ mod_trace("%s", ndev->name);
+ if (priv->netdev->flags & IFF_UP)
+ *state = CAN_STATE_STOPPED;
+ else if (priv->can.state == CAN_STATE_STOPPED)
+ *state = CAN_STATE_STOPPED;
+ else
+ *state = CAN_STATE_ACTIVE;
+ return 0;
+}
+
+static int candev_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+ struct softing_priv *priv = netdev_priv(ndev);
+ struct softing *card = priv->card;
+ mod_trace("%s %u", ndev->name, mode);
+ switch (mode) {
+ case CAN_MODE_START:
+ /*recovery from busoff? */
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ softing_reinit(card, -1, -1);
+ mutex_unlock(&card->fw.lock);
+ break;
+ case CAN_MODE_STOP:
+ case CAN_MODE_SLEEP:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+/*assume the card->lock is held*/
+
+int softing_card_irq(struct softing *card, int enable)
+{
+ int ret;
+ if (!enable) {
+ if (card->irq.requested && card->irq.nr) {
+ free_irq(card->irq.nr, card);
+ card->irq.requested = 0;
+ }
+ return 0;
+ }
+ if (!card->irq.requested && (card->irq.nr)) {
+ irqreturn_t(*fn) (int, void *);
+ unsigned int flags;
+ flags = IRQF_DISABLED | IRQF_SHARED;/*| IRQF_TRIGGER_LOW; */
+ fn = dev_interrupt_nshared;
+ if (card->desc->generation >= 2)
+ fn = dev_interrupt_shared;
+ ret = request_irq(card->irq.nr, fn, flags, card->id.name, card);
+ if (ret) {
+ mod_alert("%s, request_irq(%u) failed"
+ , card->id.name, card->irq.nr
+ );
+ return ret;
+ }
+ card->irq.requested = 1;
+ }
+ return 0;
+}
+
+static void shutdown_card(struct softing *card)
+{
+ int fw_up = 0;
+ mod_trace("%s", card->id.name);
+ if (mutex_lock_interruptible(&card->fw.lock))
+ /* return -ERESTARTSYS*/;
+ fw_up = card->fw.up;
+ card->fw.up = 0;
+
+ if (card->irq.requested && card->irq.nr) {
+ free_irq(card->irq.nr, card);
+ card->irq.requested = 0;
+ }
+ if (fw_up) {
+ if (card->fn.enable_irq)
+ card->fn.enable_irq(card, 0);
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ }
+ mutex_unlock(&card->fw.lock);
+ tasklet_kill(&card->irq.bh);
+}
+
+static int boot_card(struct softing *card)
+{
+ mod_trace("%s", card->id.name);
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ if (card->fw.up) {
+ mutex_unlock(&card->fw.lock);
+ return 0;
+ }
+ /*reset board */
+
+ if (card->fn.enable_irq)
+ card->fn.enable_irq(card, 1);
+ /*boot card */
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ /*test dp ram */
+ if (card->dpram.virt) {
+ unsigned char *lp;
+ static const unsigned char stream[]
+ = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, };
+ unsigned char back[sizeof(stream)];
+ for (lp = card->dpram.virt;
+ &lp[sizeof(stream)] <= card->dpram.end;
+ lp += sizeof(stream)) {
+ memcpy_toio(lp, stream, sizeof(stream));
+ /* flush IO cache */
+ mb();
+ memcpy_fromio(back, lp, sizeof(stream));
+
+ if (memcmp(back, stream, sizeof(stream))) {
+ char line[3 * sizeof(stream)
+ / sizeof(stream[0]) + 1];
+ char *pline = line;
+ unsigned char *addr = lp;
+ for (lp = back; lp < &back[sizeof(stream)
+ / sizeof(stream[0])]; ++lp)
+ pline += sprintf(pline, " %02x", *lp);
+
+ mod_alert("write to dpram failed at 0x%p, %s"
+ , addr, line);
+ goto open_failed;
+ }
+ }
+ /*fill dpram with 0x55 */
+ /*for (lp = card->dpram.virt; lp <= card->dpram.end; ++lp) {
+ *lp = 0x55;
+ }*/
+ wmb();
+ } else {
+ goto open_failed;
+ }
+ /*load boot firmware */
+ if (softing_load_fw(card->desc->boot.fw, card, card->dpram.virt,
+ card->dpram.size,
+ card->desc->boot.offs -
+ card->desc->boot.addr))
+ goto open_failed;
+ /*load load firmware */
+ if (softing_load_fw(card->desc->load.fw, card, card->dpram.virt,
+ card->dpram.size,
+ card->desc->load.offs -
+ card->desc->load.addr))
+ goto open_failed;
+
+ if (card->fn.reset)
+ card->fn.reset(card, 0);
+ if (softing_bootloader_command(card, 0, "card boot"))
+ goto open_failed;
+ if (softing_load_app_fw(card->desc->app.fw, card))
+ goto open_failed;
+ /*reset chip */
+ card->dpram.info->reset_rcv_fifo = 0;
+ card->dpram.info->reset = 1;
+ /*sync */
+ if (softing_fct_cmd(card, 99, 0x55, "sync-a"))
+ goto open_failed;
+ if (softing_fct_cmd(card, 99, 0xaa, "sync-a"))
+ goto open_failed;
+ /*reset chip */
+ if (softing_fct_cmd(card, 0, 0, "reset_chip"))
+ goto open_failed;
+ /*get_serial */
+ if (softing_fct_cmd(card, 43, 0, "get_serial_number"))
+ goto open_failed;
+ card->id.serial =
+ (u16) card->dpram.fct->param[1] +
+ (((u16) card->dpram.fct->param[2]) << 16);
+ /*get_version */
+ if (softing_fct_cmd(card, 12, 0, "get_version"))
+ goto open_failed;
+ card->id.fw = (u16) card->dpram.fct->param[1];
+ card->id.hw = (u16) card->dpram.fct->param[2];
+ card->id.lic = (u16) card->dpram.fct->param[3];
+ card->id.chip[0] = (u16) card->dpram.fct->param[4];
+ card->id.chip[1] = (u16) card->dpram.fct->param[5];
+
+ mod_info("%s, card booted, "
+ "serial %u, fw %u, hw %u, lic %u, chip (%u,%u)",
+ card->id.name, card->id.serial, card->id.fw, card->id.hw,
+ card->id.lic, card->id.chip[0], card->id.chip[1]);
+
+ card->fw.up = 1;
+ mutex_unlock(&card->fw.lock);
+ return 0;
+open_failed:
+ card->fw.up = 0;
+ if (card->fn.enable_irq)
+ card->fn.enable_irq(card, 0);
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ mutex_unlock(&card->fw.lock);
+ return EINVAL;
+}
+
+/*sysfs stuff*/
+
+/* Because the struct softing may be used by pcmcia devices
+ * as well as pci devices, * we have no clue how to get
+ * from a struct device * towards the struct softing *.
+ * It may go over a pci_device->priv or over a pcmcia_device->priv.
+ * Therefore, provide the struct softing pointer within the attribute.
+ * Then we don't need driver/bus specific things in these attributes
+ */
+struct softing_attribute {
+ struct device_attribute dev;
+ ssize_t (*show) (struct softing *card, char *buf);
+ ssize_t (*store)(struct softing *card, const char *buf, size_t count);
+ struct softing *card;
+};
+
+static ssize_t rd_card_attr(struct device *dev, struct device_attribute *attr
+ , char *buf) {
+ struct softing_attribute *cattr
+ = container_of(attr, struct softing_attribute, dev);
+ return cattr->show ? cattr->show(cattr->card, buf) : 0;
+}
+static ssize_t wr_card_attr(struct device *dev, struct device_attribute *attr
+ , const char *buf, size_t count) {
+ struct softing_attribute *cattr
+ = container_of(attr, struct softing_attribute, dev);
+ return cattr->store ? cattr->store(cattr->card, buf, count) : 0;
+}
+
+#define declare_attr(_name, _mode, _show, _store) { \
+ .dev = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = _mode, \
+ }, \
+ .show = rd_card_attr, \
+ .store = wr_card_attr, \
+ }, \
+ .show = _show, \
+ .store = _store, \
+}
+
+#define CARD_SHOW(name, member) \
+static ssize_t show_##name(struct softing *card, char *buf) { \
+ return sprintf(buf, "%u\n", card->member); \
+}
+CARD_SHOW(serial , id.serial);
+CARD_SHOW(firmware , id.fw);
+CARD_SHOW(hardware , id.hw);
+CARD_SHOW(license , id.lic);
+CARD_SHOW(freq , id.freq);
+CARD_SHOW(txpending , tx.pending);
+
+static const struct softing_attribute card_attr_proto [] = {
+ declare_attr(serial , 0444, show_serial , 0),
+ declare_attr(firmware , 0444, show_firmware , 0),
+ declare_attr(hardware , 0444, show_hardware , 0),
+ declare_attr(license , 0444, show_license , 0),
+ declare_attr(freq , 0444, show_freq , 0),
+ declare_attr(txpending , 0644, show_txpending , 0),
+};
+
+static int mk_card_sysfs(struct softing *card)
+{
+ int size;
+ int j;
+
+ size = sizeof(card_attr_proto)/sizeof(card_attr_proto[0]);
+ card->attr = kmalloc((size+1)*sizeof(card->attr[0]), GFP_KERNEL);
+ if (!card->attr)
+ goto attr_mem_failed;
+ memcpy(card->attr, card_attr_proto, size * sizeof(card->attr[0]));
+ memset(&card->attr[size], 0, sizeof(card->attr[0]));
+
+ card->grp = kmalloc((size+1)*sizeof(card->grp [0]), GFP_KERNEL);
+ if (!card->grp)
+ goto grp_mem_failed;
+
+ for (j = 0; j < size; ++j) {
+ card->attr[j].card = card;
+ card->grp[j] = &card->attr[j].dev.attr;
+ if (!card->attr[j].show)
+ card->attr[j].dev.attr.mode &= ~(S_IRUGO);
+ if (!card->attr[j].store)
+ card->attr[j].dev.attr.mode &= ~(S_IWUGO);
+ }
+ card->grp[size] = 0;
+ card->sysfs.name = "softing";
+ card->sysfs.attrs = card->grp;
+ if (sysfs_create_group(&card->dev->kobj, &card->sysfs) < 0)
+ goto sysfs_failed;
+
+ return 0;
+
+sysfs_failed:
+ kfree(card->grp);
+grp_mem_failed:
+ kfree(card->attr);
+attr_mem_failed:
+ return -1;
+}
+static void rm_card_sysfs(struct softing *card)
+{
+ sysfs_remove_group(&card->dev->kobj, &card->sysfs);
+ kfree(card->grp);
+ kfree(card->attr);
+}
+
+static ssize_t show_chip(struct device *dev
+ , struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct softing_priv *priv = netdev2softing(ndev);
+ return sprintf(buf, "%i\n", priv->chip);
+}
+
+static ssize_t show_output(struct device *dev
+ , struct device_attribute *attr, char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct softing_priv *priv = netdev2softing(ndev);
+ return sprintf(buf, "0x%02x\n", priv->output);
+}
+
+static ssize_t store_output(struct device *dev
+ , struct device_attribute *attr
+ , const char *buf, size_t count)
+{
+ struct net_device *ndev = to_net_dev(dev);
+ struct softing_priv *priv = netdev2softing(ndev);
+ struct softing *card = priv->card;
+
+ u8 v = simple_strtoul(buf, NULL, 10) & 0xFFU;
+
+ if (mutex_lock_interruptible(&card->fw.lock))
+ return -ERESTARTSYS;
+ if (ndev->flags & IFF_UP) {
+ int j;
+ /* we will need a restart */
+ for (j = 0; j < card->nbus; ++j) {
+ if (j == priv->index)
+ /* me, myself & I */
+ continue;
+ if (card->bus[j]->netdev->flags & IFF_UP) {
+ mutex_unlock(&card->fw.lock);
+ return -EBUSY;
+ }
+ }
+ priv->output = v;
+ softing_reinit(card, -1, -1);
+ } else {
+ priv->output = v;
+ }
+ mutex_unlock(&card->fw.lock);
+ return count;
+}
+/* TODO
+ * the latest softing cards support sleep mode too
+ */
+
+static const DEVICE_ATTR(chip, S_IRUGO, show_chip, 0);
+static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output);
+
+static const struct attribute *const netdev_sysfs_entries [] = {
+ &dev_attr_chip .attr,
+ &dev_attr_output .attr,
+ 0,
+};
+static const struct attribute_group netdev_sysfs = {
+ .name = 0,
+ .attrs = (struct attribute **)netdev_sysfs_entries,
+};
+
+static int mk_netdev_sysfs(struct softing_priv *priv)
+{
+ if (!priv->netdev->dev.kobj.sd) {
+ mod_alert("sysfs_create_group would fail");
+ return ENODEV;
+ }
+ return sysfs_create_group(&priv->netdev->dev.kobj, &netdev_sysfs);
+}
+static void rm_netdev_sysfs(struct softing_priv *priv)
+{
+ sysfs_remove_group(&priv->netdev->dev.kobj, &netdev_sysfs);
+}
+
+static struct softing_priv *mk_netdev(struct softing *card, u16 chip_id)
+{
+ struct net_device *ndev;
+ struct softing_priv *priv;
+
+ ndev = alloc_candev(sizeof(*priv));
+ if (!ndev) {
+ mod_alert("alloc_candev failed");
+ return 0;
+ }
+ priv = netdev_priv(ndev);
+ priv->netdev = ndev;
+ priv->card = card;
+ memcpy(&priv->btr_const, &softing_btr_const, sizeof(priv->btr_const));
+ priv->btr_const.brp_max = card->desc->max_brp;
+ priv->btr_const.sjw_max = card->desc->max_sjw;
+ priv->can.bittiming_const = &priv->btr_const;
+ priv->can.bittiming.clock = 8000000;
+ priv->chip = chip_id;
+ priv->output = softing_default_output(card, priv);
+ SET_NETDEV_DEV(ndev, card->dev);
+
+ ndev->flags |= IFF_ECHO;
+ ndev->open = netdev_open;
+ ndev->stop = netdev_stop;
+ ndev->hard_start_xmit = netdev_start_xmit;
+ priv->can.do_get_state = candev_get_state;
+ priv->can.do_set_mode = candev_set_mode;
+
+ return priv;
+}
+
+static void rm_netdev(struct softing_priv *priv)
+{
+ free_candev(priv->netdev);
+}
+
+static int reg_netdev(struct softing_priv *priv)
+{
+ int ret;
+ netif_carrier_off(priv->netdev);
+ ret = register_candev(priv->netdev);
+ if (ret) {
+ mod_alert("%s, register failed", priv->card->id.name);
+ goto reg_failed;
+ }
+ ret = mk_netdev_sysfs(priv);
+ if (ret) {
+ mod_alert("%s, sysfs failed", priv->card->id.name);
+ goto sysfs_failed;
+ }
+ return 0;
+sysfs_failed:
+ unregister_candev(priv->netdev);
+reg_failed:
+ return EINVAL;
+}
+
+static void unreg_netdev(struct softing_priv *priv)
+{
+ rm_netdev_sysfs(priv);
+ unregister_candev(priv->netdev);
+}
+
+void rm_softing(struct softing *card)
+{
+ int j;
+
+ /*first, disable card*/
+ shutdown_card(card);
+
+ for (j = 0; j < card->nbus; ++j) {
+ unreg_netdev(card->bus[j]);
+ rm_netdev(card->bus[j]);
+ }
+
+ rm_card_sysfs(card);
+
+ iounmap(card->dpram.virt);
+}
+EXPORT_SYMBOL(rm_softing);
+
+int mk_softing(struct softing *card)
+{
+ int j;
+
+ /* try_module_get(THIS_MODULE); */
+ mutex_init(&card->fw.lock);
+ spin_lock_init(&card->spin);
+ tasklet_init(&card->irq.bh, softing_dev_svc, (unsigned long)card);
+
+ card->desc = softing_lookup_desc(card->id.manf, card->id.prod);
+ if (!card->desc) {
+ mod_alert("0x%04x:0x%04x not supported\n", card->id.manf,
+ card->id.prod);
+ goto lookup_failed;
+ }
+ card->id.name = card->desc->name;
+ mod_trace("can (%s)", card->id.name);
+
+ card->dpram.virt = ioremap(card->dpram.phys, card->dpram.size);
+ if (!card->dpram.virt) {
+ mod_alert("dpram ioremap failed\n");
+ goto ioremap_failed;
+ }
+
+ card->dpram.size = card->desc->dpram_size;
+ card->dpram.end = &card->dpram.virt[card->dpram.size];
+ /*initialize_board */
+ card->dpram.rx = (struct softing_rx *)&card->dpram.virt[0x0000];
+ card->dpram.tx = (struct softing_tx *)&card->dpram.virt[0x0400];
+ card->dpram.fct = (struct softing_fct *)&card->dpram.virt[0x0300];
+ card->dpram.info = (struct softing_info *)&card->dpram.virt[0x0330];
+ card->dpram.command = (unsigned short *)&card->dpram.virt[0x07e0];
+ card->dpram.receipt = (unsigned short *)&card->dpram.virt[0x07f0];
+ card->dpram.irq = (struct softing_irq *)&card->dpram.virt[0x07fe];
+
+ /*reset card */
+ if (card->fn.reset)
+ card->fn.reset(card, 1);
+ if (boot_card(card)) {
+ mod_alert("%s, failed to boot", card->id.name);
+ goto boot_failed;
+ }
+
+ /*only now, the chip's are known */
+ card->id.freq = card->desc->freq * 1000000UL;
+
+ if (mk_card_sysfs(card)) {
+ mod_alert("%s, sysfs failed", card->id.name);
+ goto sysfs_failed;
+ }
+
+ if (card->nbus > (sizeof(card->bus) / sizeof(card->bus[0]))) {
+ card->nbus = sizeof(card->bus) / sizeof(card->bus[0]);
+ mod_alert("%s, going for %u busses", card->id.name, card->nbus);
+ }
+
+ for (j = 0; j < card->nbus; ++j) {
+ card->bus[j] = mk_netdev(card, card->id.chip[j]);
+ if (!card->bus[j]) {
+ mod_alert("%s: failed to make can[%i]", card->id.name,
+ j);
+ goto netdev_failed;
+ }
+ card->bus[j]->index = j;
+ }
+ for (j = 0; j < card->nbus; ++j) {
+ if (reg_netdev(card->bus[j])) {
+ mod_alert("%s: failed to register can[%i]",
+ card->id.name, j);
+ goto reg_failed;
+ }
+ }
+ mod_trace("card initialised");
+ return 0;
+
+reg_failed:
+ for (j = 0; j < card->nbus; ++j)
+ unreg_netdev(card->bus[j]);
+netdev_failed:
+ for (j = 0; j < card->nbus; ++j) {
+ if (card->bus[j])
+ rm_netdev(card->bus[j]);
+ }
+ rm_card_sysfs(card);
+sysfs_failed:
+ shutdown_card(card);
+boot_failed:
+ iounmap(card->dpram.virt);
+ card->dpram.virt = 0;
+ card->dpram.end = 0;
+ioremap_failed:
+lookup_failed:
+ tasklet_kill(&card->irq.bh);
+ return EINVAL;
+}
+EXPORT_SYMBOL(mk_softing);
+
+static int __init mod_start(void)
+{
+ mod_trace("");
+ return 0;
+}
+
+static void __exit mod_stop(void)
+{
+ mod_trace("");
+}
+
+module_init(mod_start);
+module_exit(mod_stop);
+
+MODULE_DESCRIPTION("socketcan softing driver");
+MODULE_AUTHOR("Kurt Van Dijck <kurt.van.dijck@eia.be>");
+MODULE_LICENSE("GPL");
+
+int softing_debug = 1;
+EXPORT_SYMBOL(softing_debug);
+module_param(softing_debug, int , S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(softing_debug, "trace softing functions");
diff --git a/drivers/net/can/sysfs.c b/drivers/net/can/sysfs.c
new file mode 100644
index 000000000000..746f6410cbeb
--- /dev/null
+++ b/drivers/net/can/sysfs.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2007-2008 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/capability.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <net/sock.h>
+#include <linux/rtnetlink.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+#include "sysfs.h"
+
+#ifdef CONFIG_SYSFS
+
+/*
+ * SYSFS access functions and attributes. Use same locking as
+ * net/core/net-sysfs.c does.
+ */
+static inline int dev_isalive(const struct net_device *dev)
+{
+ return dev->reg_state <= NETREG_REGISTERED;
+}
+
+/* use same locking rules as GIF* ioctl's */
+static ssize_t can_dev_show(struct device *d,
+ struct device_attribute *attr, char *buf,
+ ssize_t (*fmt)(struct net_device *, char *))
+{
+ struct net_device *dev = to_net_dev(d);
+ ssize_t ret = -EINVAL;
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev))
+ ret = (*fmt)(dev, buf);
+ read_unlock(&dev_base_lock);
+
+ return ret;
+}
+
+/* generate a show function for simple field */
+#define CAN_DEV_SHOW(field, fmt_string) \
+static ssize_t fmt_can_##field(struct net_device *dev, char *buf) \
+{ \
+ struct can_priv *priv = netdev_priv(dev); \
+ return sprintf(buf, fmt_string, priv->field); \
+} \
+static ssize_t show_can_##field(struct device *d, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return can_dev_show(d, attr, buf, fmt_can_##field); \
+}
+
+/* use same locking and permission rules as SIF* ioctl's */
+static ssize_t can_dev_store(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t len,
+ int (*set)(struct net_device *, unsigned long))
+{
+ struct net_device *dev = to_net_dev(d);
+ unsigned long new;
+ int ret = -EINVAL;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ ret = strict_strtoul(buf, 0, &new);
+ if (ret)
+ goto out;
+
+ rtnl_lock();
+ if (dev_isalive(dev)) {
+ ret = (*set)(dev, new);
+ if (!ret)
+ ret = len;
+ }
+ rtnl_unlock();
+out:
+ return ret;
+}
+
+#define CAN_CREATE_FILE(_dev, _name) \
+ if (device_create_file(&_dev->dev, &dev_attr_##_name)) \
+ dev_err(ND2D(_dev), \
+ "Couldn't create device file for ##_name\n")
+
+#define CAN_REMOVE_FILE(_dev, _name) \
+ device_remove_file(&_dev->dev, &dev_attr_##_name) \
+
+CAN_DEV_SHOW(ctrlmode, "0x%x\n");
+
+static int change_can_ctrlmode(struct net_device *dev, unsigned long ctrlmode)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err = 0;
+
+ if (priv->state != CAN_STATE_STOPPED)
+ return -EBUSY;
+
+ if (priv->do_set_ctrlmode)
+ err = priv->do_set_ctrlmode(dev, ctrlmode);
+
+ if (!err)
+ priv->ctrlmode = ctrlmode;
+
+ return err;
+}
+
+static ssize_t store_can_ctrlmode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_ctrlmode);
+}
+
+static DEVICE_ATTR(can_ctrlmode, S_IRUGO | S_IWUSR,
+ show_can_ctrlmode, store_can_ctrlmode);
+
+static const char *can_state_names[] = {
+ "active", "bus-warn", "bus-pass" , "bus-off",
+ "stopped", "sleeping", "unkown"
+};
+
+static ssize_t printf_can_state(struct net_device *dev, char *buf)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ enum can_state state;
+ int err = 0;
+
+ if (priv->do_get_state) {
+ err = priv->do_get_state(dev, &state);
+ if (err)
+ goto out;
+ priv->state = state;
+ } else
+ state = priv->state;
+
+ if (state >= ARRAY_SIZE(can_state_names))
+ state = ARRAY_SIZE(can_state_names) - 1;
+ err = sprintf(buf, "%s\n", can_state_names[state]);
+out:
+ return err;
+}
+
+static ssize_t show_can_state(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ return can_dev_show(d, attr, buf, printf_can_state);
+}
+
+static DEVICE_ATTR(can_state, S_IRUGO, show_can_state, NULL);
+
+CAN_DEV_SHOW(restart_ms, "%d\n");
+
+static int change_can_restart_ms(struct net_device *dev, unsigned long ms)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->restart_ms < 0)
+ return -EOPNOTSUPP;
+ priv->restart_ms = ms;
+ return 0;
+}
+
+static ssize_t store_can_restart_ms(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_restart_ms);
+}
+
+static DEVICE_ATTR(can_restart_ms, S_IRUGO | S_IWUSR,
+ show_can_restart_ms, store_can_restart_ms);
+
+static ssize_t printf_can_echo(struct net_device *dev, char *buf)
+{
+ return sprintf(buf, "%d\n", dev->flags & IFF_ECHO ? 1 : 0);
+}
+
+static ssize_t show_can_echo(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ return can_dev_show(d, attr, buf, printf_can_echo);
+}
+
+static int change_can_echo(struct net_device *dev, unsigned long on)
+{
+ if (on)
+ dev->flags |= IFF_ECHO;
+ else
+ dev->flags &= ~IFF_ECHO;
+ return 0;
+}
+
+static ssize_t store_can_echo(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_echo);
+}
+
+static DEVICE_ATTR(can_echo, S_IRUGO | S_IWUSR, show_can_echo, store_can_echo);
+
+static int change_can_restart(struct net_device *dev, unsigned long on)
+{
+ return can_restart_now(dev);
+}
+
+static ssize_t store_can_restart(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ return can_dev_store(dev, attr, buf, len, change_can_restart);
+}
+
+static DEVICE_ATTR(can_restart, S_IWUSR, NULL, store_can_restart);
+
+/* Show a given attribute if the CAN bittiming group */
+static ssize_t can_btc_show(const struct device *d,
+ struct device_attribute *attr, char *buf,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming_const *btc = priv->bittiming_const;
+ ssize_t ret = -EINVAL;
+
+ WARN_ON(offset >= sizeof(struct can_bittiming_const) ||
+ offset % sizeof(u32) != 0);
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev) && btc)
+ ret = sprintf(buf, "%d\n",
+ *(u32 *)(((u8 *)btc) + offset));
+
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+/* Generate a read-only bittiming const attribute */
+#define CAN_BT_CONST_ENTRY(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_btc_show(d, attr, buf, \
+ offsetof(struct can_bittiming_const, name));\
+} \
+static DEVICE_ATTR(hw_##name, S_IRUGO, show_##name, NULL)
+
+CAN_BT_CONST_ENTRY(tseg1_min);
+CAN_BT_CONST_ENTRY(tseg1_max);
+CAN_BT_CONST_ENTRY(tseg2_min);
+CAN_BT_CONST_ENTRY(tseg2_max);
+CAN_BT_CONST_ENTRY(sjw_max);
+CAN_BT_CONST_ENTRY(brp_min);
+CAN_BT_CONST_ENTRY(brp_max);
+CAN_BT_CONST_ENTRY(brp_inc);
+
+static ssize_t can_bt_show(const struct device *d,
+ struct device_attribute *attr, char *buf,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ ssize_t ret = -EINVAL;
+ u32 *ptr, val;
+
+ WARN_ON(offset >= sizeof(struct can_bittiming) ||
+ offset % sizeof(u32) != 0);
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev)) {
+ ptr = (u32 *)(((u8 *)bt) + offset);
+ if (ptr == &bt->sample_point &&
+ priv->state != CAN_STATE_STOPPED)
+ val = can_sample_point(bt);
+ else
+ val = *ptr;
+ ret = sprintf(buf, "%d\n", val);
+ }
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+static ssize_t can_bt_store(const struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_bittiming *bt = &priv->bittiming;
+ unsigned long new;
+ ssize_t ret = -EINVAL;
+ u32 *ptr;
+
+ if (priv->state != CAN_STATE_STOPPED)
+ return -EBUSY;
+
+ WARN_ON(offset >= sizeof(struct can_bittiming) ||
+ offset % sizeof(u32) != 0);
+
+ ret = strict_strtoul(buf, 0, &new);
+ if (ret)
+ goto out;
+
+ ptr = (u32 *)(((u8 *)bt) + offset);
+ rtnl_lock();
+ if (dev_isalive(dev)) {
+ *ptr = (u32)new;
+
+ if ((ptr == &bt->bitrate) || (ptr == &bt->sample_point)) {
+ bt->tq = 0;
+ bt->brp = 0;
+ bt->sjw = 0;
+ bt->prop_seg = 0;
+ bt->phase_seg1 = 0;
+ bt->phase_seg2 = 0;
+ } else {
+ bt->bitrate = 0;
+ bt->sample_point = 0;
+ }
+ ret = count;
+ }
+ rtnl_unlock();
+out:
+ return ret;
+}
+
+#define CAN_BT_ENTRY_RO(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_bt_show(d, attr, buf, \
+ offsetof(struct can_bittiming, name)); \
+} \
+static DEVICE_ATTR(hw_##name, S_IRUGO, show_##name, NULL)
+
+CAN_BT_ENTRY_RO(clock);
+
+#define CAN_BT_ENTRY(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_bt_show(d, attr, buf, \
+ offsetof(struct can_bittiming, name)); \
+} \
+static ssize_t store_##name(struct device *d, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ return can_bt_store(d, attr, buf, count, \
+ offsetof(struct can_bittiming, name)); \
+} \
+static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_##name, store_##name)
+
+CAN_BT_ENTRY(bitrate);
+CAN_BT_ENTRY(sample_point);
+CAN_BT_ENTRY(tq);
+CAN_BT_ENTRY(prop_seg);
+CAN_BT_ENTRY(phase_seg1);
+CAN_BT_ENTRY(phase_seg2);
+CAN_BT_ENTRY(sjw);
+
+static struct attribute *can_bittiming_attrs[] = {
+ &dev_attr_hw_tseg1_min.attr,
+ &dev_attr_hw_tseg1_max.attr,
+ &dev_attr_hw_tseg2_max.attr,
+ &dev_attr_hw_tseg2_min.attr,
+ &dev_attr_hw_sjw_max.attr,
+ &dev_attr_hw_brp_min.attr,
+ &dev_attr_hw_brp_max.attr,
+ &dev_attr_hw_brp_inc.attr,
+ &dev_attr_hw_clock.attr,
+ &dev_attr_bitrate.attr,
+ &dev_attr_sample_point.attr,
+ &dev_attr_tq.attr,
+ &dev_attr_prop_seg.attr,
+ &dev_attr_phase_seg1.attr,
+ &dev_attr_phase_seg2.attr,
+ &dev_attr_sjw.attr,
+ NULL
+};
+
+static struct attribute_group can_bittiming_group = {
+ .name = "can_bittiming",
+ .attrs = can_bittiming_attrs,
+};
+
+/* Show a given attribute in the CAN statistics group */
+static ssize_t can_stat_show(const struct device *d,
+ struct device_attribute *attr, char *buf,
+ unsigned long offset)
+{
+ struct net_device *dev = to_net_dev(d);
+ struct can_priv *priv = netdev_priv(dev);
+ struct can_device_stats *stats = &priv->can_stats;
+ ssize_t ret = -EINVAL;
+
+ WARN_ON(offset >= sizeof(struct can_device_stats) ||
+ offset % sizeof(unsigned long) != 0);
+
+ read_lock(&dev_base_lock);
+ if (dev_isalive(dev))
+ ret = sprintf(buf, "%ld\n",
+ *(unsigned long *)(((u8 *)stats) + offset));
+
+ read_unlock(&dev_base_lock);
+ return ret;
+}
+
+/* Generate a read-only CAN statistics attribute */
+#define CAN_STAT_ENTRY(name) \
+static ssize_t show_##name(struct device *d, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ return can_stat_show(d, attr, buf, \
+ offsetof(struct can_device_stats, name)); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
+
+CAN_STAT_ENTRY(error_warning);
+CAN_STAT_ENTRY(error_passive);
+CAN_STAT_ENTRY(bus_error);
+CAN_STAT_ENTRY(arbitration_lost);
+CAN_STAT_ENTRY(data_overrun);
+CAN_STAT_ENTRY(wakeup);
+CAN_STAT_ENTRY(restarts);
+
+static struct attribute *can_statistics_attrs[] = {
+ &dev_attr_error_warning.attr,
+ &dev_attr_error_passive.attr,
+ &dev_attr_bus_error.attr,
+ &dev_attr_arbitration_lost.attr,
+ &dev_attr_data_overrun.attr,
+ &dev_attr_wakeup.attr,
+ &dev_attr_restarts.attr,
+ NULL
+};
+
+static struct attribute_group can_statistics_group = {
+ .name = "can_statistics",
+ .attrs = can_statistics_attrs,
+};
+
+void can_create_sysfs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ int err;
+
+ CAN_CREATE_FILE(dev, can_ctrlmode);
+ CAN_CREATE_FILE(dev, can_echo);
+ CAN_CREATE_FILE(dev, can_restart);
+ CAN_CREATE_FILE(dev, can_state);
+ CAN_CREATE_FILE(dev, can_restart_ms);
+
+ err = sysfs_create_group(&(dev->dev.kobj),
+ &can_statistics_group);
+ if (err) {
+ printk(KERN_EMERG
+ "couldn't create sysfs group for CAN statistics\n");
+ }
+
+ if (priv->bittiming_const) {
+ err = sysfs_create_group(&(dev->dev.kobj),
+ &can_bittiming_group);
+ if (err) {
+ printk(KERN_EMERG "couldn't create sysfs "
+ "group for CAN bittiming\n");
+ }
+ }
+}
+
+void can_remove_sysfs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ CAN_REMOVE_FILE(dev, can_ctrlmode);
+ CAN_REMOVE_FILE(dev, can_echo);
+ CAN_REMOVE_FILE(dev, can_state);
+ CAN_REMOVE_FILE(dev, can_restart);
+ CAN_REMOVE_FILE(dev, can_restart_ms);
+
+ sysfs_remove_group(&(dev->dev.kobj), &can_statistics_group);
+ if (priv->bittiming_const)
+ sysfs_remove_group(&(dev->dev.kobj), &can_bittiming_group);
+}
+
+#endif /* CONFIG_SYSFS */
+
+
+
diff --git a/drivers/net/can/sysfs.h b/drivers/net/can/sysfs.h
new file mode 100644
index 000000000000..e21f2fa4b158
--- /dev/null
+++ b/drivers/net/can/sysfs.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CAN_SYSFS_H
+#define CAN_SYSFS_H
+
+void can_create_sysfs(struct net_device *dev);
+void can_remove_sysfs(struct net_device *dev);
+
+#endif /* CAN_SYSFS_H */
diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c
index 103f0f1df280..69e6b804c477 100644
--- a/drivers/net/can/vcan.c
+++ b/drivers/net/can/vcan.c
@@ -128,26 +128,27 @@ static int vcan_tx(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
+
static void vcan_setup(struct net_device *dev)
{
- dev->type = ARPHRD_CAN;
- dev->mtu = sizeof(struct can_frame);
- dev->hard_header_len = 0;
- dev->addr_len = 0;
- dev->tx_queue_len = 0;
- dev->flags = IFF_NOARP;
+ dev->type = ARPHRD_CAN;
+ dev->mtu = sizeof(struct can_frame);
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->tx_queue_len = 0;
+ dev->flags = IFF_NOARP;
/* set flags according to driver capabilities */
if (echo)
dev->flags |= IFF_ECHO;
- dev->hard_start_xmit = vcan_tx;
- dev->destructor = free_netdev;
+ dev->hard_start_xmit = vcan_tx;
+ dev->destructor = free_netdev;
}
static struct rtnl_link_ops vcan_link_ops __read_mostly = {
- .kind = "vcan",
- .setup = vcan_setup,
+ .kind = "vcan",
+ .setup = vcan_setup,
};
static __init int vcan_init_module(void)
diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
index 7107620f615d..d56846d8d5b4 100644
--- a/drivers/net/cs89x0.c
+++ b/drivers/net/cs89x0.c
@@ -193,12 +193,17 @@ static unsigned int cs8900_irq_map[] = {IRQ_IXDP2X01_CS8900, 0, 0, 0};
#define CIRRUS_DEFAULT_IRQ VH_INTC_INT_NUM_CASCADED_INTERRUPT_1 /* Event inputs bank 1 - ID 35/bit 3 */
static unsigned int netcard_portlist[] __used __initdata = {CIRRUS_DEFAULT_BASE, 0};
static unsigned int cs8900_irq_map[] = {CIRRUS_DEFAULT_IRQ, 0, 0, 0};
-#elif defined(CONFIG_MACH_MX31ADS)
-#include <mach/board-mx31ads.h>
-static unsigned int netcard_portlist[] __used __initdata = {
- PBC_BASE_ADDRESS + PBC_CS8900A_IOBASE + 0x300, 0
-};
-static unsigned cs8900_irq_map[] = {EXPIO_INT_ENET_INT, 0, 0, 0};
+#elif defined(CONFIG_ARCH_MXC)
+/*! Null terminated portlist used to probe for the CS8900A device on ISA Bus
+ * Add 3 to reset the page window before probing (fixes eth probe when deployed
+ * using nand_boot)
+ */
+extern unsigned int netcard_portlist[2];
+/*!
+ * The CS8900A has 4 IRQ pins, which is software selectable, CS8900A interrupt
+ * pin 0 is used for interrupt generation.
+ */
+extern unsigned int cs8900_irq_map[4];
#else
static unsigned int netcard_portlist[] __used __initdata =
{ 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
@@ -1034,7 +1039,7 @@ skip_this_frame:
void __init reset_chip(struct net_device *dev)
{
-#if !defined(CONFIG_MACH_MX31ADS)
+#if !defined(CONFIG_ARCH_MXC)
#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01)
struct net_local *lp = netdev_priv(dev);
int ioaddr = dev->base_addr;
@@ -1063,7 +1068,7 @@ void __init reset_chip(struct net_device *dev)
reset_start_time = jiffies;
while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2)
;
-#endif /* !CONFIG_MACH_MX31ADS */
+#endif /* !CONFIG_ARCH_MXC */
}
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c
index 36cb6e95b465..98c07af8a2f9 100644
--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -36,6 +36,7 @@
#define DRV_VERSION "1.01"
#define SPI_OPLEN 1
+#define MAX_ENC_CARDS 1
#define ENC28J60_MSG_DEFAULT \
(NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_LINK)
@@ -49,6 +50,10 @@
/* Max TX retries in case of collision as suggested by errata datasheet */
#define MAX_TX_RETRYCOUNT 16
+#ifdef CONFIG_ARCH_STMP3XXX
+#include <mach/stmp3xxx.h>
+#include <mach/regs-ocotp.h>
+#endif
enum {
RXFILTER_NORMAL,
RXFILTER_MULTI,
@@ -81,6 +86,69 @@ static struct {
u32 msg_enable;
} debug = { -1 };
+static int random_mac; /* = 0 */
+static char *mac[MAX_ENC_CARDS];
+
+static int enc28j60_get_mac(unsigned char *dev_addr, int idx)
+{
+ int i, r;
+ char *p, *item;
+ unsigned long v;
+ unsigned char sv[10];
+
+ if (idx < 0)
+ idx = 0;
+ if (idx > MAX_ENC_CARDS)
+ return false;
+
+ if (!mac[idx]) {
+#ifdef CONFIG_ARCH_STMP3XXX
+ if (get_evk_board_version() >= 1) {
+ int mac1 , mac2 , retry = 0;
+
+ HW_OCOTP_CTRL_SET(BM_OCOTP_CTRL_RD_BANK_OPEN);
+ while (HW_OCOTP_CTRL_RD() & BM_OCOTP_CTRL_BUSY) {
+ msleep(10);
+ retry++;
+ if (retry > 10)
+ return false;
+ }
+
+ mac1 = HW_OCOTP_CUSTn_RD(0);
+ mac2 = HW_OCOTP_CUSTn_RD(1);
+ if (MAX_ADDR_LEN < 6)
+ return false;
+
+ dev_addr[0] = (mac1 >> 24) & 0xFF;
+ dev_addr[1] = (mac1 >> 16) & 0xFF;
+ dev_addr[2] = (mac1 >> 8) & 0xFF;
+ dev_addr[3] = (mac1 >> 0) & 0xFF;
+ dev_addr[4] = (mac2 >> 8) & 0xFF;
+ dev_addr[5] = (mac2 >> 0) & 0xFF;
+ return true;
+ }
+#endif
+ return false;
+ }
+
+ item = mac[idx];
+ for (i = 0; i < MAX_ADDR_LEN; i++) {
+ p = strchr(item, ':');
+ if (!p)
+ sprintf(sv, "0x%s", item);
+ else
+ sprintf(sv, "0x%*.*s", p - item, p-item, item);
+ r = strict_strtoul(sv, 0, &v);
+ dev_addr[i] = v;
+ if (p)
+ item = p + 1;
+ else
+ break;
+ if (r < 0)
+ return false;
+ }
+ return true;
+}
/*
* SPI read buffer
* wait for the SPI transfer and copy received data to destination
@@ -90,10 +158,13 @@ spi_read_buf(struct enc28j60_net *priv, int len, u8 *data)
{
u8 *rx_buf = priv->spi_transfer_buf + 4;
u8 *tx_buf = priv->spi_transfer_buf;
- struct spi_transfer t = {
- .tx_buf = tx_buf,
- .rx_buf = rx_buf,
- .len = SPI_OPLEN + len,
+ struct spi_transfer tt = {
+ .tx_buf = tx_buf,
+ .len = SPI_OPLEN,
+ };
+ struct spi_transfer tr = {
+ .rx_buf = rx_buf,
+ .len = len,
};
struct spi_message msg;
int ret;
@@ -102,10 +173,11 @@ spi_read_buf(struct enc28j60_net *priv, int len, u8 *data)
tx_buf[1] = tx_buf[2] = tx_buf[3] = 0; /* don't care */
spi_message_init(&msg);
- spi_message_add_tail(&t, &msg);
+ spi_message_add_tail(&tt, &msg);
+ spi_message_add_tail(&tr, &msg);
ret = spi_sync(priv->spi, &msg);
if (ret == 0) {
- memcpy(data, &rx_buf[SPI_OPLEN], len);
+ memcpy(data, rx_buf, len);
ret = msg.status;
}
if (ret && netif_msg_drv(priv))
@@ -196,16 +268,32 @@ static void enc28j60_soft_reset(struct enc28j60_net *priv)
*/
static void enc28j60_set_bank(struct enc28j60_net *priv, u8 addr)
{
- if ((addr & BANK_MASK) != priv->bank) {
- u8 b = (addr & BANK_MASK) >> 5;
+ u8 b = (addr & BANK_MASK) >> 5;
+
+ /* These registers (EIE, EIR, ESTAT, ECON2, ECON1)
+ * are present in all banks, no need to switch bank
+ */
+ if (addr >= EIE && addr <= ECON1)
+ return;
- if (b != (ECON1_BSEL1 | ECON1_BSEL0))
+ /* Clear or set each bank selection bit as needed */
+ if ((b & ECON1_BSEL0) != (priv->bank & ECON1_BSEL0)) {
+ if (b & ECON1_BSEL0)
+ spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1,
+ ECON1_BSEL0);
+ else
+ spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1,
+ ECON1_BSEL0);
+ }
+ if ((b & ECON1_BSEL1) != (priv->bank & ECON1_BSEL1)) {
+ if (b & ECON1_BSEL1)
+ spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1,
+ ECON1_BSEL1);
+ else
spi_write_op(priv, ENC28J60_BIT_FIELD_CLR, ECON1,
- ECON1_BSEL1 | ECON1_BSEL0);
- if (b != 0)
- spi_write_op(priv, ENC28J60_BIT_FIELD_SET, ECON1, b);
- priv->bank = (addr & BANK_MASK);
+ ECON1_BSEL1);
}
+ priv->bank = b;
}
/*
@@ -930,7 +1018,7 @@ static void enc28j60_hw_rx(struct net_device *ndev)
if (netif_msg_rx_status(priv))
enc28j60_dump_rsv(priv, __func__, next_packet, len, rxstat);
- if (!RSV_GETBIT(rxstat, RSV_RXOK)) {
+ if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) {
if (netif_msg_rx_err(priv))
dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat);
ndev->stats.rx_errors++;
@@ -938,6 +1026,8 @@ static void enc28j60_hw_rx(struct net_device *ndev)
ndev->stats.rx_crc_errors++;
if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
ndev->stats.rx_frame_errors++;
+ if (len > MAX_FRAMELEN)
+ ndev->stats.rx_over_errors++;
} else {
skb = dev_alloc_skb(len + NET_IP_ALIGN);
if (!skb) {
@@ -1093,8 +1183,24 @@ static int enc28j60_rx_interrupt(struct net_device *ndev)
priv->max_pk_counter);
}
ret = pk_counter;
- while (pk_counter-- > 0)
+ while (pk_counter-- > 0) {
+ if (!priv->full_duplex) {
+ /*
+ * This works only in HALF DUPLEX mode:
+ * when more than 2 packets are available, start
+ * transmission of 11111.. frame by setting
+ * FCON0 (0x01) in EFLOCON
+ *
+ * This bit can be cleared either explicitly, or by
+ * trasmitting the packet in enc28j60_hw_tx.
+ */
+ if (pk_counter > 2)
+ locked_reg_bfset(priv, EFLOCON, 0x01);
+ if (pk_counter == 1)
+ locked_reg_bfclr(priv, EFLOCON, 0x01);
+ }
enc28j60_hw_rx(ndev);
+ }
return ret;
}
@@ -1220,6 +1326,11 @@ static void enc28j60_irq_work_handler(struct work_struct *work)
*/
static void enc28j60_hw_tx(struct enc28j60_net *priv)
{
+ if (!priv->tx_skb) {
+ enc28j60_tx_clear(priv->netdev, false);
+ return;
+ }
+
if (netif_msg_tx_queued(priv))
printk(KERN_DEBUG DRV_NAME
": Tx Packet Len:%d\n", priv->tx_skb->len);
@@ -1523,6 +1634,7 @@ static int __devinit enc28j60_probe(struct spi_device *spi)
struct net_device *dev;
struct enc28j60_net *priv;
int ret = 0;
+ int set;
if (netif_msg_drv(&debug))
dev_info(&spi->dev, DRV_NAME " Ethernet driver %s loaded\n",
@@ -1556,7 +1668,11 @@ static int __devinit enc28j60_probe(struct spi_device *spi)
ret = -EIO;
goto error_irq;
}
- random_ether_addr(dev->dev_addr);
+
+ /* need a counter here, to count instances of enc28j60 devices */
+ set = enc28j60_get_mac(dev->dev_addr, -1);
+ if (!set || random_mac)
+ random_ether_addr(dev->dev_addr);
enc28j60_set_hw_macaddr(dev);
/* Board setup must set the relevant edge trigger type;
@@ -1616,6 +1732,40 @@ static int __devexit enc28j60_remove(struct spi_device *spi)
return 0;
}
+#ifdef CONFIG_PM
+static int
+enc28j60_suspend(struct spi_device *spi, pm_message_t state)
+{
+ struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net_dev = priv ? priv->netdev : NULL;
+
+ if (net_dev && netif_running(net_dev)) {
+ netif_stop_queue(net_dev);
+ netif_device_detach(net_dev);
+ disable_irq(spi->irq);
+ }
+ return 0;
+}
+
+static int
+enc28j60_resume(struct spi_device *spi)
+{
+ struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
+ struct net_device *net_dev = priv ? priv->netdev : NULL;
+
+ if (net_dev && netif_running(net_dev)) {
+ enable_irq(spi->irq);
+ netif_device_attach(net_dev);
+ netif_start_queue(net_dev);
+ schedule_work(&priv->restart_work);
+ }
+ return 0;
+}
+#else
+#define enc28j60_resume NULL
+#define enc28j60_suspend NULL
+#endif
+
static struct spi_driver enc28j60_driver = {
.driver = {
.name = DRV_NAME,
@@ -1623,6 +1773,8 @@ static struct spi_driver enc28j60_driver = {
},
.probe = enc28j60_probe,
.remove = __devexit_p(enc28j60_remove),
+ .suspend = enc28j60_suspend,
+ .resume = enc28j60_resume,
};
static int __init enc28j60_init(void)
@@ -1645,4 +1797,6 @@ MODULE_DESCRIPTION(DRV_NAME " ethernet driver");
MODULE_AUTHOR("Claudio Lanconelli <lanconelli.claudio@eptar.com>");
MODULE_LICENSE("GPL");
module_param_named(debug, debug.msg_enable, int, 0);
+module_param(random_mac, int, 0444);
+module_param_array(mac, charp, NULL, 0);
MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., ffff=all)");
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index ecd5c71a7a8a..94d55a17d11c 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -18,6 +18,9 @@
* Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
* Copyright (c) 2004-2006 Macq Electronique SA.
*/
+/*
+ * Copyright 2006-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
#include <linux/module.h>
#include <linux/kernel.h>
@@ -36,16 +39,29 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
+#include <linux/clk.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
+#include <asm/mach-types.h>
+#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
+ defined(CONFIG_M520x) || defined(CONFIG_M532x)
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#include "fec.h"
+#define FEC_ALIGNMENT (0x03) /*FEC needs 4bytes alignment*/
+#elif defined(CONFIG_ARCH_MXC)
+#include <mach/hardware.h>
+#include <mach/iim.h>
+#include "fec.h"
+#define FEC_ALIGNMENT (0x0F) /*FEC needs 128bits(32bytes) alignment*/
+#endif
+
+#define FEC_ADDR_ALIGNMENT(x) ((unsigned char *)(((unsigned long )(x) + (FEC_ALIGNMENT)) & (~FEC_ALIGNMENT)))
#if defined(CONFIG_FEC2)
#define FEC_MAX_PORTS 2
@@ -53,7 +69,7 @@
#define FEC_MAX_PORTS 1
#endif
-#if defined(CONFIG_M5272)
+#if defined(CONFIG_M5272) || defined(CONFIG_ARCH_MXC)
#define HAVE_mii_link_interrupt
#endif
@@ -72,6 +88,8 @@ static unsigned int fec_hw[] = {
(MCF_MBAR+0x30000),
#elif defined(CONFIG_M532x)
(MCF_MBAR+0xfc030000),
+#elif defined(CONFIG_ARCH_MXC)
+ (IO_ADDRESS(FEC_BASE_ADDR)),
#else
&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec),
#endif
@@ -149,6 +167,12 @@ typedef struct {
#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
+#ifndef CONFIG_ARCH_MXC
+#define FEC_ENET_MASK ((uint)0xffc00000)
+#else
+#define FEC_ENET_MASK ((uint)0xfff80000)
+#endif
+
/* The FEC stores dest/src/type, data, and checksum for receive packets.
*/
#define PKT_MAXBUF_SIZE 1518
@@ -162,7 +186,7 @@ typedef struct {
* account when setting it.
*/
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x)
+ defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC)
#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16)
#else
#define OPT_FRAME_SIZE 0
@@ -185,11 +209,13 @@ struct fec_enet_private {
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
unsigned char *tx_bounce[TX_RING_SIZE];
struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
ushort skb_cur;
ushort skb_dirty;
/* CPM dual port RAM relative addresses.
*/
+ void * cbd_mem_base; /* save the virtual base address of rx&tx buffer descripter */
cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */
cbd_t *tx_bd_base;
cbd_t *cur_rx, *cur_tx; /* The next free ring entry */
@@ -206,6 +232,7 @@ struct fec_enet_private {
uint phy_speed;
phy_info_t const *phy;
struct work_struct phy_task;
+ struct net_device *net;
uint sequence_done;
uint mii_phy_task_queued;
@@ -217,6 +244,8 @@ struct fec_enet_private {
int link;
int old_link;
int full_duplex;
+
+ struct clk *clk;
};
static int fec_enet_open(struct net_device *dev);
@@ -231,6 +260,17 @@ static void fec_restart(struct net_device *dev, int duplex);
static void fec_stop(struct net_device *dev);
static void fec_set_mac_address(struct net_device *dev);
+static void __inline__ fec_dcache_inv_range(void * start, void * end);
+static void __inline__ fec_dcache_flush_range(void * start, void * end);
+
+/*
+ * fec_copy_threshold controls the copy when recieving ethernet frame.
+ * If ethernet header aligns 4bytes, the ip header and upper header will not aligns 4bytes.
+ * The resean is ethernet header is 14bytes.
+ * And the max size of tcp & ip header is 128bytes. Normally it is 40bytes.
+ * So I set the default value between 128 to 256.
+ */
+static int fec_copy_threshold = -1;
/* MII processing. We keep this as simple as possible. Requests are
* placed on the list (if there is room). When the request is finished
@@ -342,10 +382,10 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
* 4-byte boundaries. Use bounce buffers to copy data
* and get it aligned. Ugh.
*/
- if (bdp->cbd_bufaddr & 0x3) {
+ if ((bdp->cbd_bufaddr) & FEC_ALIGNMENT) {
unsigned int index;
index = bdp - fep->tx_bd_base;
- memcpy(fep->tx_bounce[index], (void *) bdp->cbd_bufaddr, bdp->cbd_datlen);
+ memcpy(fep->tx_bounce[index], (void *) skb->data, skb->len);
bdp->cbd_bufaddr = __pa(fep->tx_bounce[index]);
}
@@ -359,8 +399,8 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Push the data cache so the CPM does not get stale memory
* data.
*/
- flush_dcache_range((unsigned long)skb->data,
- (unsigned long)skb->data + skb->len);
+ fec_dcache_flush_range(__va(bdp->cbd_bufaddr), __va(bdp->cbd_bufaddr) +
+ bdp->cbd_datlen);
/* Send it on its way. Tell FEC it's ready, interrupt when done,
* it's the last BD of the frame, and to put the CRC on the end.
@@ -373,7 +413,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->trans_start = jiffies;
/* Trigger transmission start */
- fecp->fec_x_des_active = 0;
+ fecp->fec_x_des_active = 0x01000000;
/* If this was the last BD in the ring, start at the beginning again.
*/
@@ -460,7 +500,7 @@ fec_enet_interrupt(int irq, void * dev_id)
/* Handle receive event in its own function.
*/
- if (int_events & FEC_ENET_RXF) {
+ if (int_events & (FEC_ENET_RXF | FEC_ENET_RXB)) {
ret = IRQ_HANDLED;
fec_enet_rx(dev);
}
@@ -469,7 +509,7 @@ fec_enet_interrupt(int irq, void * dev_id)
descriptors. FEC handles all errors, we just discover
them as part of the transmit process.
*/
- if (int_events & FEC_ENET_TXF) {
+ if (int_events & (FEC_ENET_TXF | FEC_ENET_TXB)) {
ret = IRQ_HANDLED;
fec_enet_tx(dev);
}
@@ -572,6 +612,7 @@ fec_enet_rx(struct net_device *dev)
struct sk_buff *skb;
ushort pkt_len;
__u8 *data;
+ int rx_index ;
#ifdef CONFIG_M532x
flush_cache_all();
@@ -588,7 +629,7 @@ fec_enet_rx(struct net_device *dev)
bdp = fep->cur_rx;
while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
-
+ rx_index = bdp - fep->rx_bd_base;
#ifndef final_version
/* Since we have allocated space to hold a complete frame,
* the last indicator should be set.
@@ -638,14 +679,31 @@ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
* include that when passing upstream as it messes up
* bridging applications.
*/
- skb = dev_alloc_skb(pkt_len-4);
+ if ((pkt_len - 4) < fec_copy_threshold) {
+ skb = dev_alloc_skb(pkt_len);
+ } else {
+ skb = dev_alloc_skb(FEC_ENET_RX_FRSIZE);
+ }
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
dev->stats.rx_dropped++;
} else {
- skb_put(skb,pkt_len-4); /* Make room */
- skb_copy_to_linear_data(skb, data, pkt_len-4);
+ if ((pkt_len - 4) < fec_copy_threshold) {
+ skb_reserve(skb, 2); /*skip 2bytes, so ipheader is align 4bytes*/
+ skb_put(skb,pkt_len-4); /* Make room */
+ skb_copy_to_linear_data(skb, data, pkt_len-4);
+ } else {
+ struct sk_buff * pskb = fep->rx_skbuff[rx_index];
+
+ fec_dcache_inv_range(skb->data, skb->data +
+ FEC_ENET_RX_FRSIZE);
+ fep->rx_skbuff[rx_index] = skb;
+ skb->data = FEC_ADDR_ALIGNMENT(skb->data);
+ bdp->cbd_bufaddr = __pa(skb->data);
+ skb_put(pskb,pkt_len-4); /* Make room */
+ skb = pskb;
+ }
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
}
@@ -672,7 +730,7 @@ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
* incoming frames. On a heavily loaded network, we should be
* able to keep up at the expense of system resources.
*/
- fecp->fec_r_des_active = 0;
+ fecp->fec_r_des_active = 0x01000000;
#endif
} /* while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) */
fep->cur_rx = (cbd_t *)bdp;
@@ -1207,6 +1265,48 @@ static phy_info_t phy_info_dp83848= {
},
};
+static phy_info_t phy_info_lan8700 = {
+ 0x0007C0C,
+ "LAN8700",
+ (const phy_cmd_t []) { /* config */
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup */
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* act_int */
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown */
+ { mk_mii_end, }
+ },
+};
+
+static phy_info_t phy_info_lan8710 = {
+ 0x0007C0F,
+ "LAN8710",
+ (const phy_cmd_t []) { /* config */
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup */
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* act_int */
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown */
+ { mk_mii_end, }
+ },
+};
+
/* ------------------------------------------------------------------------- */
static phy_info_t const * const phy_info[] = {
@@ -1216,6 +1316,8 @@ static phy_info_t const * const phy_info[] = {
&phy_info_am79c874,
&phy_info_ks8721bl,
&phy_info_dp83848,
+ &phy_info_lan8700,
+ &phy_info_lan8710,
NULL
};
@@ -1227,6 +1329,21 @@ mii_link_interrupt(int irq, void * dev_id);
#if defined(CONFIG_M5272)
/*
+ * * do some initializtion based architecture of this chip
+ * */
+static void __inline__ fec_arch_init(void)
+{
+ return;
+}
+/*
+ * * do some cleanup based architecture of this chip
+ * */
+static void __inline__ fec_arch_exit(void)
+{
+ return;
+}
+
+/*
* Code specific to Coldfire 5272 setup.
*/
static void __inline__ fec_request_intrs(struct net_device *dev)
@@ -1327,15 +1444,40 @@ static void __inline__ fec_phy_ack_intr(void)
*icrp = 0x0d000000;
}
-static void __inline__ fec_localhw_setup(void)
+static void __inline__ fec_localhw_setup(struct net_device *dev)
+{
+}
+
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * flush dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
{
+ return addr;
}
/*
- * Do not need to make region uncached on 5272.
+ * unmap memory erea started with addr from uncachable erea.
*/
-static void __inline__ fec_uncache(unsigned long addr)
+static void __inline__ fec_unmap_uncache(void * addr)
{
+ return ;
}
/* ------------------------------------------------------------------------- */
@@ -1343,6 +1485,22 @@ static void __inline__ fec_uncache(unsigned long addr)
#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x)
/*
+ * do some initializtion based architecture of this chip
+ */
+static void __inline__ fec_arch_init(void)
+{
+ return;
+}
+
+/*
+ * do some cleanup based architecture of this chip
+ */
+static void __inline__ fec_arch_exit(void)
+{
+ return;
+}
+
+/*
* Code specific to Coldfire 5230/5231/5232/5234/5235,
* the 5270/5271/5274/5275 and 5280/5282 setups.
*/
@@ -1489,20 +1647,58 @@ static void __inline__ fec_phy_ack_intr(void)
{
}
-static void __inline__ fec_localhw_setup(void)
+static void __inline__ fec_localhw_setup(struct net_device *dev)
{
}
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ return ;
+}
/*
- * Do not need to make region uncached on 5272.
+ * flush dcache related with the virtual memory range(start, end)
*/
-static void __inline__ fec_uncache(unsigned long addr)
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
{
+ return ;
+}
+
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
+{
+ return addr;
+}
+
+/*
+ * unmap memory erea started with addr from uncachable erea.
+ */
+static void __inline__ fec_unmap_uncache(void * addr)
+{
+ return ;
}
/* ------------------------------------------------------------------------- */
#elif defined(CONFIG_M520x)
+/*
+ * do some initializtion based architecture of this chip
+ */
+static void __inline__ fec_arch_init(void)
+{
+ return;
+}
+/*
+ * do some cleanup based architecture of this chip
+ */
+static void __inline__ fec_arch_exit(void)
+{
+ return;
+}
/*
* Code specific to Coldfire 520x
@@ -1610,17 +1806,63 @@ static void __inline__ fec_phy_ack_intr(void)
{
}
-static void __inline__ fec_localhw_setup(void)
+static void __inline__ fec_localhw_setup(struct net_device *dev)
+{
+}
+
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * flush dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
{
+ return ;
}
-static void __inline__ fec_uncache(unsigned long addr)
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
{
+ return addr;
}
+/*
+ * unmap memory erea started with addr from uncachable erea.
+ */
+static void __inline__ fec_unmap_uncache(void * addr)
+{
+ return ;
+}
+
+
/* ------------------------------------------------------------------------- */
#elif defined(CONFIG_M532x)
+
+/*
+ * do some initializtion based architecture of this chip
+ */
+static void __inline__ fec_arch_init(void)
+{
+ return;
+}
+
+/*
+ * do some cleanup based architecture of this chip
+ */
+static void __inline__ fec_arch_exit(void)
+{
+ return;
+}
+
/*
* Code specific for M532x
*/
@@ -1749,21 +1991,297 @@ static void __inline__ fec_phy_ack_intr(void)
{
}
-static void __inline__ fec_localhw_setup(void)
+static void __inline__ fec_localhw_setup(struct net_device *dev)
{
}
/*
- * Do not need to make region uncached on 532x.
+ * invalidate dcache related with the virtual memory range(start, end)
*/
-static void __inline__ fec_uncache(unsigned long addr)
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
{
+ return ;
+}
+
+/*
+ * flush dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
+{
+ return addr;
+}
+
+/*
+ * unmap memory erea started with addr from uncachable erea.
+ */
+static void __inline__ fec_unmap_uncache(void * addr)
+{
+ return ;
}
/* ------------------------------------------------------------------------- */
+#elif defined(CONFIG_ARCH_MXC)
+
+extern void gpio_fec_active(void);
+extern void gpio_fec_inactive(void);
+extern unsigned int expio_intr_fec;
+
+/*
+ * do some initializtion based architecture of this chip
+ */
+static void __inline__ fec_arch_init(void)
+{
+ struct clk *clk;
+ gpio_fec_active();
+ clk = clk_get(NULL, "fec_clk");
+ clk_enable(clk);
+ clk_put(clk);
+ return;
+}
+/*
+ * do some cleanup based architecture of this chip
+ */
+static void __inline__ fec_arch_exit(void)
+{
+ struct clk *clk;
+ clk = clk_get(NULL, "fec_clk");
+ clk_disable(clk);
+ clk_put(clk);
+ gpio_fec_inactive();
+ return;
+}
+
+/*
+ * Code specific to Freescale i.MXC
+ */
+static void __inline__ fec_request_intrs(struct net_device *dev)
+{
+ /* Setup interrupt handlers. */
+ if (request_irq(MXC_INT_FEC, fec_enet_interrupt, 0, "fec", dev) != 0)
+ panic("FEC: Could not allocate FEC IRQ(%d)!\n", MXC_INT_FEC);
+ /* TODO: disable now due to CPLD issue */
+ if ((expio_intr_fec > 0) &&
+ (request_irq(expio_intr_fec, mii_link_interrupt, 0, "fec(MII)", dev) != 0))
+ panic("FEC: Could not allocate FEC(MII) IRQ(%d)!\n", expio_intr_fec);
+ disable_irq(expio_intr_fec);
+}
+
+static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
+{
+ u32 rate;
+ struct clk *clk;
+ volatile fec_t *fecp;
+ fecp = fep->hwp;
+ fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
+ fecp->fec_x_cntrl = 0x00;
+
+ /*
+ * Set MII speed to 2.5 MHz
+ */
+ clk = clk_get(NULL, "fec_clk");
+ rate = clk_get_rate(clk);
+ clk_put(clk);
+
+ fep->phy_speed =
+ ((((rate / 2 + 4999999) / 2500000) / 2) & 0x3F) << 1;
+ fecp->fec_mii_speed = fep->phy_speed;
+ fec_restart(dev, 0);
+}
+
+#define FEC_IIM_BASE IO_ADDRESS(IIM_BASE_ADDR)
+static void __inline__ fec_get_mac(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ volatile fec_t *fecp;
+ unsigned char *iap, tmpaddr[ETH_ALEN];
+ int i;
+ unsigned long fec_mac_base = FEC_IIM_BASE + MXC_IIMKEY0;
+ fecp = fep->hwp;
+
+ if (fecp->fec_addr_low || fecp->fec_addr_high) {
+ *((unsigned long *) &tmpaddr[0]) =
+ be32_to_cpu(fecp->fec_addr_low);
+ *((unsigned short *) &tmpaddr[4]) =
+ be32_to_cpu(fecp->fec_addr_high);
+ iap = &tmpaddr[0];
+ } else {
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0)
+ fec_mac_base = FEC_IIM_BASE + MXC_IIMMAC;
+
+ memset(tmpaddr, 0, ETH_ALEN);
+ if (!(machine_is_mx35_3ds() || cpu_is_mx51())) {
+ /*
+ * Get MAC address from IIM.
+ * If it is all 1's or 0's, use the default.
+ */
+ for (i = 0; i < ETH_ALEN; i++)
+ tmpaddr[ETH_ALEN-1-i] =
+ __raw_readb(fec_mac_base + i * 4);
+ }
+ iap = &tmpaddr[0];
+
+ if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
+ (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
+ iap = fec_mac_default;
+ if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
+ (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
+ iap = fec_mac_default;
+ }
+
+ memcpy(dev->dev_addr, iap, ETH_ALEN);
+
+ /* Adjust MAC if using default MAC address */
+ if (iap == fec_mac_default)
+ dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
+}
+
+#ifndef MODULE
+static int fec_mac_setup(char *new_mac)
+{
+ char *ptr, *p = new_mac;
+ int i = 0;
+
+ while (p && (*p) && i < 6) {
+ ptr = strchr(p, ':');
+ if (ptr)
+ *ptr++ = '\0';
+
+ if (strlen(p)) {
+ unsigned long tmp = simple_strtoul(p, NULL, 16);
+ if (tmp > 0xff)
+ break;
+ fec_mac_default[i++] = tmp;
+ }
+ p = ptr;
+ }
+
+ return 0;
+}
+
+__setup("fec_mac=", fec_mac_setup);
+#endif
+
+static void __inline__ fec_enable_phy_intr(void)
+{
+ if (expio_intr_fec > 0)
+ enable_irq(expio_intr_fec);
+}
+
+static void __inline__ fec_disable_phy_intr(void)
+{
+ if (expio_intr_fec > 0)
+ disable_irq(expio_intr_fec);
+}
+
+static void __inline__ fec_phy_ack_intr(void)
+{
+ if (expio_intr_fec > 0)
+ disable_irq(expio_intr_fec);
+}
+
+#ifdef CONFIG_ARCH_MX25
+/*
+ * i.MX25 allows RMII mode to be configured via a gasket
+ */
+#define FEC_MIIGSK_CFGR_FRCONT (1 << 6)
+#define FEC_MIIGSK_CFGR_LBMODE (1 << 4)
+#define FEC_MIIGSK_CFGR_EMODE (1 << 3)
+#define FEC_MIIGSK_CFGR_IF_MODE_MASK (3 << 0)
+#define FEC_MIIGSK_CFGR_IF_MODE_MII (0 << 0)
+#define FEC_MIIGSK_CFGR_IF_MODE_RMII (1 << 0)
+
+#define FEC_MIIGSK_ENR_READY (1 << 2)
+#define FEC_MIIGSK_ENR_EN (1 << 1)
+
+static void __inline__ fec_localhw_setup(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ volatile fec_t *fecp = fep->hwp;
+ /*
+ * Set up the MII gasket for RMII mode
+ */
+ printk("%s: enable RMII gasket\n", dev->name);
+ /* disable the gasket and wait */
+ fecp->fec_miigsk_enr = 0;
+ while (fecp->fec_miigsk_enr & FEC_MIIGSK_ENR_READY)
+ udelay(1);
+
+ /* configure the gasket for RMII, 50 MHz, no loopback, no echo */
+ fecp->fec_miigsk_cfgr = FEC_MIIGSK_CFGR_IF_MODE_RMII;
+
+ /* re-enable the gasket */
+ fecp->fec_miigsk_enr = FEC_MIIGSK_ENR_EN;
+}
#else
+static void __inline__ fec_localhw_setup(struct net_device *dev)
+{
+}
+#endif
+
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ dma_sync_single_for_device(NULL, (unsigned long)__pa(start),
+ (unsigned long)(end - start),
+ DMA_FROM_DEVICE);
+ return ;
+}
+
+/*
+ * flush dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
+{
+ dma_sync_single_for_device(NULL, (unsigned long)__pa(start),
+ (unsigned long)(end - start), DMA_TO_DEVICE);
+ return ;
+}
+
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
+{
+ return (unsigned long)ioremap(__pa(addr), size);
+}
+
+/*
+ * unmap memory erea started with addr from uncachable erea.
+ */
+static void __inline__ fec_unmap_uncache(void * addr)
+{
+ return iounmap(addr);
+}
+
+/* ------------------------------------------------------------------------- */
+
+#else
+/*
+ * do some initializtion based architecture of this chip
+ */
+static void __inline__ fec_arch_init(void)
+{
+ return;
+}
+/*
+ * do some cleanup based architecture of this chip
+ */
+static void __inline__ fec_arch_exit(void)
+{
+ return;
+}
/*
* Code specific to the MPC860T setup.
@@ -1831,7 +2349,7 @@ static void __inline__ fec_phy_ack_intr(void)
{
}
-static void __inline__ fec_localhw_setup(void)
+static void __inline__ fec_localhw_setup(struct net_device *dev)
{
volatile fec_t *fecp;
@@ -1842,12 +2360,40 @@ static void __inline__ fec_localhw_setup(void)
fecp->fec_fun_code = 0x78000000;
}
-static void __inline__ fec_uncache(unsigned long addr)
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * flush dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_flush_range(void * start, void * end)
+{
+ return ;
+}
+
+/*
+ * map memory space (addr, addr+size) to uncachable erea.
+ */
+static unsigned long __inline__ fec_map_uncache(unsigned long addr, int size)
{
pte_t *pte;
pte = va_to_pte(mem_addr);
pte_val(*pte) |= _PAGE_NO_CACHE;
flush_tlb_page(init_mm.mmap, mem_addr);
+ return addr;
+}
+
+/*
+ * * unmap memory erea started with addr from uncachable erea.
+ * */
+static void __inline__ fec_unmap_uncache(void * addr)
+{
+ return ;
}
#endif
@@ -2073,10 +2619,14 @@ mii_link_interrupt(int irq, void * dev_id)
#if 0
disable_irq(fep->mii_irq); /* disable now, enable later */
#endif
-
- mii_do_cmd(dev, fep->phy->ack_int);
- mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */
-
+ /*
+ * Some board will trigger phy interrupt before phy enable.
+ * And at that moment , fep->phy is not initialized.
+ */
+ if (fep->phy) {
+ mii_do_cmd(dev, fep->phy->ack_int);
+ mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */
+ }
return IRQ_HANDLED;
}
#endif
@@ -2086,6 +2636,7 @@ fec_enet_open(struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
+ fec_arch_init();
/* I should reset the ring buffers here, but I don't yet know
* a simple way to do that.
*/
@@ -2094,6 +2645,8 @@ fec_enet_open(struct net_device *dev)
fep->sequence_done = 0;
fep->link = 0;
+ fec_restart(dev, 1);
+
if (fep->phy) {
mii_do_cmd(dev, fep->phy->ack_int);
mii_do_cmd(dev, fep->phy->config);
@@ -2110,18 +2663,14 @@ fec_enet_open(struct net_device *dev)
schedule();
mii_do_cmd(dev, fep->phy->startup);
-
- /* Set the initial link state to true. A lot of hardware
- * based on this device does not implement a PHY interrupt,
- * so we are never notified of link change.
- */
- fep->link = 1;
- } else {
- fep->link = 1; /* lets just try it and see */
- /* no phy, go full duplex, it's most likely a hub chip */
- fec_restart(dev, 1);
}
+ /* Set the initial link state to true. A lot of hardware
+ * based on this device does not implement a PHY interrupt,
+ * so we are never notified of link change.
+ */
+ fep->link = 1;
+
netif_start_queue(dev);
fep->opened = 1;
return 0; /* Success */
@@ -2135,9 +2684,10 @@ fec_enet_close(struct net_device *dev)
/* Don't know what to do yet.
*/
fep->opened = 0;
- netif_stop_queue(dev);
- fec_stop(dev);
-
+ if (fep->link) {
+ fec_stop(dev);
+ }
+ fec_arch_exit();
return 0;
}
@@ -2248,6 +2798,7 @@ int __init fec_enet_init(struct net_device *dev)
unsigned long mem_addr;
volatile cbd_t *bdp;
cbd_t *cbd_base;
+ struct sk_buff* pskb;
volatile fec_t *fecp;
int i, j;
static int index = 0;
@@ -2256,6 +2807,8 @@ int __init fec_enet_init(struct net_device *dev)
if (index >= FEC_MAX_PORTS)
return -ENXIO;
+ fep->net = dev;
+
/* Allocate memory for buffer descriptors.
*/
mem_addr = __get_free_page(GFP_KERNEL);
@@ -2264,6 +2817,7 @@ int __init fec_enet_init(struct net_device *dev)
return -ENOMEM;
}
+ fep->cbd_mem_base = (void *)mem_addr;
spin_lock_init(&fep->hw_lock);
spin_lock_init(&fep->mii_lock);
@@ -2288,10 +2842,14 @@ int __init fec_enet_init(struct net_device *dev)
*/
fec_get_mac(dev);
- cbd_base = (cbd_t *)mem_addr;
- /* XXX: missing check for allocation failure */
+ cbd_base = (cbd_t *)fec_map_uncache(mem_addr, PAGE_SIZE);
+ if (cbd_base == NULL) {
+ free_page(mem_addr);
+ printk("FEC: map descriptor memory to uncacheable failed?\n");
+ return -ENOMEM;
+ }
- fec_uncache(mem_addr);
+ /* XXX: missing check for allocation failure */
/* Set receive and transmit descriptor base.
*/
@@ -2306,25 +2864,26 @@ int __init fec_enet_init(struct net_device *dev)
/* Initialize the receive buffer descriptors.
*/
bdp = fep->rx_bd_base;
- for (i=0; i<FEC_ENET_RX_PAGES; i++) {
-
- /* Allocate a page.
- */
- mem_addr = __get_free_page(GFP_KERNEL);
- /* XXX: missing check for allocation failure */
-
- fec_uncache(mem_addr);
-
- /* Initialize the BD for every fragment in the page.
- */
- for (j=0; j<FEC_ENET_RX_FRPPG; j++) {
- bdp->cbd_sc = BD_ENET_RX_EMPTY;
- bdp->cbd_bufaddr = __pa(mem_addr);
- mem_addr += FEC_ENET_RX_FRSIZE;
- bdp++;
+ for (i=0; i<RX_RING_SIZE; i++, bdp++) {
+ pskb = dev_alloc_skb(FEC_ENET_RX_FRSIZE);
+ if(pskb == NULL) {
+ for(; i>0; i--) {
+ if( fep->rx_skbuff[i-1] ) {
+ kfree_skb(fep->rx_skbuff[i-1]);
+ fep->rx_skbuff[i-1] = NULL;
+ }
+ }
+ printk("FEC: allocate skb fail when initializing rx buffer \n");
+ free_page(mem_addr);
+ return -ENOMEM;
}
+ fep->rx_skbuff[i] = pskb;
+ fec_dcache_inv_range(pskb->data, pskb->data +
+ FEC_ENET_RX_FRSIZE);
+ pskb->data = FEC_ADDR_ALIGNMENT(pskb->data);
+ bdp->cbd_sc = BD_ENET_RX_EMPTY;
+ bdp->cbd_bufaddr = __pa(pskb->data);
}
-
/* Set the last buffer to wrap.
*/
bdp--;
@@ -2357,19 +2916,23 @@ int __init fec_enet_init(struct net_device *dev)
/* Set receive and transmit descriptor base.
*/
- fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base));
- fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base));
+ fecp->fec_r_des_start = __pa((uint)(fep->cbd_mem_base));
+ fecp->fec_x_des_start = __pa((uint)(fep->cbd_mem_base + RX_RING_SIZE*sizeof(cbd_t)));
/* Install our interrupt handlers. This varies depending on
* the architecture.
*/
fec_request_intrs(dev);
+ /* Clear and enable interrupts */
+ fecp->fec_ievent = FEC_ENET_MASK;
+ fecp->fec_imask = FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII;
+
fecp->fec_grp_hash_table_high = 0;
fecp->fec_grp_hash_table_low = 0;
fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
fecp->fec_ecntrl = 2;
- fecp->fec_r_des_active = 0;
+ fecp->fec_r_des_active = 0x01000000;
#ifndef CONFIG_M5272
fecp->fec_hash_table_high = 0;
fecp->fec_hash_table_low = 0;
@@ -2392,10 +2955,6 @@ int __init fec_enet_init(struct net_device *dev)
/* setup MII interface */
fec_set_mii(dev, fep);
- /* Clear and enable interrupts */
- fecp->fec_ievent = 0xffc00000;
- fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII);
-
/* Queue up command to detect the PHY and initialize the
* remainder of the interface.
*/
@@ -2427,9 +2986,15 @@ fec_restart(struct net_device *dev, int duplex)
fecp->fec_ecntrl = 1;
udelay(10);
+ /* Enable interrupts we wish to service.
+ */
+ fecp->fec_imask = FEC_ENET_TXF | FEC_ENET_TXB | FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII;
+
/* Clear any outstanding interrupt.
- */
- fecp->fec_ievent = 0xffc00000;
+ *
+ */
+ fecp->fec_ievent = FEC_ENET_MASK;
+
fec_enable_phy_intr();
/* Set station address.
@@ -2445,12 +3010,12 @@ fec_restart(struct net_device *dev, int duplex)
*/
fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
- fec_localhw_setup();
+ fec_localhw_setup(dev);
/* Set receive and transmit descriptor base.
*/
- fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base));
- fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base));
+ fecp->fec_r_des_start = __pa((uint)(fep->cbd_mem_base));
+ fecp->fec_x_des_start = __pa((uint)(fep->cbd_mem_base + RX_RING_SIZE*sizeof(cbd_t)));
fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;
fep->cur_rx = fep->rx_bd_base;
@@ -2517,11 +3082,7 @@ fec_restart(struct net_device *dev, int duplex)
/* And last, enable the transmit and receive processing.
*/
fecp->fec_ecntrl = 2;
- fecp->fec_r_des_active = 0;
-
- /* Enable interrupts we wish to service.
- */
- fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII);
+ fecp->fec_r_des_active = 0x01000000;
}
static void
@@ -2530,6 +3091,8 @@ fec_stop(struct net_device *dev)
volatile fec_t *fecp;
struct fec_enet_private *fep;
+ netif_stop_queue(dev);
+
fep = netdev_priv(dev);
fecp = fep->hwp;
@@ -2561,15 +3124,18 @@ fec_stop(struct net_device *dev)
static int __init fec_enet_module_init(void)
{
struct net_device *dev;
- int i, err;
+ int i, err, ret = 0;
DECLARE_MAC_BUF(mac);
printk("FEC ENET Version 0.2\n");
+ fec_arch_init();
for (i = 0; (i < FEC_MAX_PORTS); i++) {
dev = alloc_etherdev(sizeof(struct fec_enet_private));
- if (!dev)
- return -ENOMEM;
+ if (!dev) {
+ ret = -ENOMEM;
+ goto exit;
+ }
err = fec_enet_init(dev);
if (err) {
free_netdev(dev);
@@ -2578,13 +3144,19 @@ static int __init fec_enet_module_init(void)
if (register_netdev(dev) != 0) {
/* XXX: missing cleanup here */
free_netdev(dev);
- return -EIO;
+ ret = -EIO;
+ goto exit;
}
printk("%s: ethernet %s\n",
dev->name, print_mac(mac, dev->dev_addr));
}
- return 0;
+
+exit:
+ fec_arch_exit();
+ return ret;
+
+
}
module_init(fec_enet_module_init);
diff --git a/drivers/net/fec.h b/drivers/net/fec.h
index 292719daceff..cb21ef1a8052 100644
--- a/drivers/net/fec.h
+++ b/drivers/net/fec.h
@@ -14,7 +14,7 @@
/****************************************************************************/
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x)
+ defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC)
/*
* Just figures, Motorola would have to change the offsets for
* registers in the same peripheral device on different models
@@ -56,6 +56,10 @@ typedef struct fec {
unsigned long fec_r_des_start; /* Receive descriptor ring */
unsigned long fec_x_des_start; /* Transmit descriptor ring */
unsigned long fec_r_buff_size; /* Maximum receive buff size */
+ unsigned long fec_reserved12[93];
+ unsigned long fec_miigsk_cfgr; /* MIIGSK config register */
+ unsigned long fec_reserved13;
+ unsigned long fec_miigsk_enr; /* MIIGSK enable register */
} fec_t;
#else
@@ -103,12 +107,22 @@ typedef struct fec {
/*
* Define the buffer descriptor structure.
*/
+/* Please see "Receive Buffer Descriptor Field Definitions" in Specification.
+ * It's LE.
+ */
+#ifdef CONFIG_ARCH_MXC
+typedef struct bufdesc {
+ unsigned short cbd_datlen; /* Data length */
+ unsigned short cbd_sc; /* Control and status info */
+ unsigned long cbd_bufaddr; /* Buffer address */
+} cbd_t;
+#else
typedef struct bufdesc {
unsigned short cbd_sc; /* Control and status info */
unsigned short cbd_datlen; /* Data length */
unsigned long cbd_bufaddr; /* Buffer address */
} cbd_t;
-
+#endif
/*
* The following definitions courtesy of commproc.h, which where
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index e6317557a531..e372f35ae038 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -342,5 +342,9 @@ config MCS_FIR
To compile it as a module, choose M here: the module will be called
mcs7780.
+config MXC_FIR
+ tristate "Freescale MXC FIR driver"
+ depends on ARCH_MXC && IRDA
+
endmenu
diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile
index 5d20fde32a24..d32839cf9e73 100644
--- a/drivers/net/irda/Makefile
+++ b/drivers/net/irda/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o
obj-$(CONFIG_VIA_FIR) += via-ircc.o
obj-$(CONFIG_PXA_FICP) += pxaficp_ir.o
obj-$(CONFIG_MCS_FIR) += mcs7780.o
+obj-$(CONFIG_MXC_FIR) += mxc_ir.o
obj-$(CONFIG_AU1000_FIR) += au1k_ir.o
# SIR drivers
obj-$(CONFIG_IRTTY_SIR) += irtty-sir.o sir-dev.o
diff --git a/drivers/net/irda/mxc_ir.c b/drivers/net/irda/mxc_ir.c
new file mode 100644
index 000000000000..7adcf5363078
--- /dev/null
+++ b/drivers/net/irda/mxc_ir.c
@@ -0,0 +1,1777 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * Based on sa1100_ir.c - Copyright 2000-2001 Russell King
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_ir.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC FIRI.
+ *
+ * This driver is based on drivers/net/irda/sa1100_ir.c, by Russell King.
+ *
+ * @ingroup FIRI
+ */
+
+/*
+ * Include Files
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+#include <mach/mxc_uart.h>
+#include "mxc_ir.h"
+
+#define IS_SIR(mi) ( (mi)->speed <= 115200 )
+#define IS_MIR(mi) ( (mi)->speed < 4000000 && (mi)->speed >= 576000 )
+#define IS_FIR(mi) ( (mi)->speed >= 4000000 )
+
+#define SDMA_START_DELAY() { \
+ volatile int j,k;\
+ int i;\
+ for(i=0;i<10000;i++)\
+ k=j;\
+ }
+
+#define IRDA_FRAME_SIZE_LIMIT 2047
+#define UART_BUFF_SIZE 14384
+
+#define UART4_UFCR_TXTL 16
+#define UART4_UFCR_RXTL 1
+
+#define FIRI_SDMA_TX
+#define FIRI_SDMA_RX
+
+/*!
+ * This structure is a way for the low level driver to define their own
+ * \b mxc_irda structure. This structure includes SK buffers, DMA buffers.
+ * and has other elements that are specifically required by this driver.
+ */
+struct mxc_irda {
+ /*!
+ * This keeps track of device is running or not
+ */
+ unsigned char open;
+
+ /*!
+ * This holds current FIRI communication speed
+ */
+ int speed;
+
+ /*!
+ * This holds FIRI communication speed for next packet
+ */
+ int newspeed;
+
+ /*!
+ * SK buffer for transmitter
+ */
+ struct sk_buff *txskb;
+
+ /*!
+ * SK buffer for receiver
+ */
+ struct sk_buff *rxskb;
+
+#ifdef FIRI_SDMA_RX
+ /*!
+ * SK buffer for tasklet
+ */
+ struct sk_buff *tskb;
+#endif
+
+ /*!
+ * DMA address for transmitter
+ */
+ dma_addr_t dma_rx_buff_phy;
+
+ /*!
+ * DMA address for receiver
+ */
+ dma_addr_t dma_tx_buff_phy;
+
+ /*!
+ * DMA Transmit buffer length
+ */
+ unsigned int dma_tx_buff_len;
+
+ /*!
+ * DMA channel for transmitter
+ */
+ int txdma_ch;
+
+ /*!
+ * DMA channel for receiver
+ */
+ int rxdma_ch;
+
+ /*!
+ * IrDA network device statistics
+ */
+ struct net_device_stats stats;
+
+ /*!
+ * The device structure used to get FIRI information
+ */
+ struct device *dev;
+
+ /*!
+ * Resource structure for UART, which will maintain base addresses and IRQs.
+ */
+ struct resource *uart_res;
+
+ /*!
+ * Base address of UART, used in readl and writel.
+ */
+ void *uart_base;
+
+ /*!
+ * Resource structure for FIRI, which will maintain base addresses and IRQs.
+ */
+ struct resource *firi_res;
+
+ /*!
+ * Base address of FIRI, used in readl and writel.
+ */
+ void *firi_base;
+
+ /*!
+ * UART IRQ number.
+ */
+ int uart_irq;
+
+ /*!
+ * Second UART IRQ number in case the interrupt lines are not muxed.
+ */
+ int uart_irq1;
+
+ /*!
+ * UART clock needed for baud rate calculations
+ */
+ struct clk *uart_clk;
+
+ /*!
+ * UART clock needed for baud rate calculations
+ */
+ unsigned long uart_clk_rate;
+
+ /*!
+ * FIRI clock needed for baud rate calculations
+ */
+ struct clk *firi_clk;
+
+ /*!
+ * FIRI IRQ number.
+ */
+ int firi_irq;
+
+ /*!
+ * IrLAP layer instance
+ */
+ struct irlap_cb *irlap;
+
+ /*!
+ * Driver supported baudrate capabilities
+ */
+ struct qos_info qos;
+
+ /*!
+ * Temporary transmit buffer used by the driver
+ */
+ iobuff_t tx_buff;
+
+ /*!
+ * Temporary receive buffer used by the driver
+ */
+ iobuff_t rx_buff;
+
+ /*!
+ * Pointer to platform specific data structure.
+ */
+ struct mxc_ir_platform_data *mxc_ir_plat;
+
+ /*!
+ * This holds the power management status of this module.
+ */
+ int suspend;
+
+};
+
+extern void gpio_firi_active(void *, unsigned int);
+extern void gpio_firi_inactive(void);
+extern void gpio_firi_init(void);
+
+void mxc_irda_firi_init(struct mxc_irda *si);
+#ifdef FIRI_SDMA_RX
+static void mxc_irda_fir_dma_rx_irq(void *id, int error_status,
+ unsigned int count);
+#endif
+#ifdef FIRI_SDMA_TX
+static void mxc_irda_fir_dma_tx_irq(void *id, int error_status,
+ unsigned int count);
+#endif
+
+/*!
+ * This function allocates and maps the receive buffer,
+ * unless it is already allocated.
+ *
+ * @param si FIRI device specific structure.
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_rx_alloc(struct mxc_irda *si)
+{
+#ifdef FIRI_SDMA_RX
+ mxc_dma_requestbuf_t dma_request;
+#endif
+ if (si->rxskb) {
+ return 0;
+ }
+
+ si->rxskb = alloc_skb(IRDA_FRAME_SIZE_LIMIT + 1, GFP_ATOMIC);
+
+ if (!si->rxskb) {
+ dev_err(si->dev, "mxc_ir: out of memory for RX SKB\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Align any IP headers that may be contained
+ * within the frame.
+ */
+ skb_reserve(si->rxskb, 1);
+
+#ifdef FIRI_SDMA_RX
+ si->dma_rx_buff_phy =
+ dma_map_single(si->dev, si->rxskb->data, IRDA_FRAME_SIZE_LIMIT,
+ DMA_FROM_DEVICE);
+
+ dma_request.num_of_bytes = IRDA_FRAME_SIZE_LIMIT;
+ dma_request.dst_addr = si->dma_rx_buff_phy;
+ dma_request.src_addr = si->firi_res->start;
+
+ mxc_dma_config(si->rxdma_ch, &dma_request, 1, MXC_DMA_MODE_READ);
+#endif
+ return 0;
+}
+
+/*!
+ * This function is called to disable the FIRI dma
+ *
+ * @param si FIRI port specific structure.
+ */
+static void mxc_irda_disabledma(struct mxc_irda *si)
+{
+ /* Stop all DMA activity. */
+#ifdef FIRI_SDMA_TX
+ mxc_dma_disable(si->txdma_ch);
+#endif
+#ifdef FIRI_SDMA_RX
+ mxc_dma_disable(si->rxdma_ch);
+#endif
+}
+
+/*!
+ * This function is called to set the IrDA communications speed.
+ *
+ * @param si FIRI specific structure.
+ * @param speed new Speed to be configured for.
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_set_speed(struct mxc_irda *si, int speed)
+{
+ unsigned long flags;
+ int ret = 0;
+ unsigned int num, denom, baud;
+ unsigned int cr;
+
+ dev_dbg(si->dev, "speed:%d\n", speed);
+ switch (speed) {
+ case 9600:
+ case 19200:
+ case 38400:
+ case 57600:
+ case 115200:
+ dev_dbg(si->dev, "starting SIR\n");
+ baud = speed;
+ if (IS_FIR(si)) {
+#ifdef FIRI_SDMA_RX
+ mxc_dma_disable(si->rxdma_ch);
+#endif
+ cr = readl(si->firi_base + FIRITCR);
+ cr &= ~FIRITCR_TE;
+ writel(cr, si->firi_base + FIRITCR);
+
+ cr = readl(si->firi_base + FIRIRCR);
+ cr &= ~FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+
+ }
+ local_irq_save(flags);
+
+ /* Disable Tx and Rx */
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ cr &= ~(MXC_UARTUCR2_RXEN | MXC_UARTUCR2_TXEN);
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+
+ gpio_firi_inactive();
+
+ num = baud / 100 - 1;
+ denom = si->uart_clk_rate / 1600 - 1;
+ if ((denom < 65536) && (si->uart_clk_rate > 1600)) {
+ writel(num, si->uart_base + MXC_UARTUBIR);
+ writel(denom, si->uart_base + MXC_UARTUBMR);
+ }
+
+ si->speed = speed;
+
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR1);
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR2);
+
+ /* Enable Receive Overrun and Data Ready interrupts. */
+ cr = readl(si->uart_base + MXC_UARTUCR4);
+ cr |= (MXC_UARTUCR4_OREN | MXC_UARTUCR4_DREN);
+ writel(cr, si->uart_base + MXC_UARTUCR4);
+
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ cr |= (MXC_UARTUCR2_RXEN | MXC_UARTUCR2_TXEN);
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+
+ local_irq_restore(flags);
+ break;
+ case 4000000:
+ local_irq_save(flags);
+
+ /* Disable Receive Overrun and Data Ready interrupts. */
+ cr = readl(si->uart_base + MXC_UARTUCR4);
+ cr &= ~(MXC_UARTUCR4_OREN | MXC_UARTUCR4_DREN);
+ writel(cr, si->uart_base + MXC_UARTUCR4);
+
+ /* Disable Tx and Rx */
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ cr &= ~(MXC_UARTUCR2_RXEN | MXC_UARTUCR2_TXEN);
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+
+ /*
+ * FIR configuration
+ */
+ mxc_irda_disabledma(si);
+
+ cr = readl(si->firi_base + FIRITCR);
+ cr &= ~FIRITCR_TE;
+ writel(cr, si->firi_base + FIRITCR);
+
+ gpio_firi_active(si->firi_base + FIRITCR, FIRITCR_TPP);
+
+ si->speed = speed;
+
+ cr = readl(si->firi_base + FIRIRCR);
+ cr |= FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+
+ dev_dbg(si->dev, "Going for fast IRDA ...\n");
+ ret = mxc_irda_rx_alloc(si);
+
+ /* clear RX status register */
+ writel(0xFFFF, si->firi_base + FIRIRSR);
+#ifdef FIRI_SDMA_RX
+ if (si->rxskb) {
+ mxc_dma_enable(si->rxdma_ch);
+ }
+#endif
+ local_irq_restore(flags);
+
+ break;
+ default:
+ dev_err(si->dev, "speed not supported by FIRI\n");
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * This function is called to set the IrDA communications speed.
+ *
+ * @param si FIRI specific structure.
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static inline int mxc_irda_fir_error(struct mxc_irda *si)
+{
+ struct sk_buff *skb = si->rxskb;
+ unsigned int dd_error, crc_error, overrun_error;
+ unsigned int sr;
+
+ if (!skb) {
+ dev_err(si->dev, "no skb!\n");
+ return -1;
+ }
+
+ sr = readl(si->firi_base + FIRIRSR);
+ dd_error = sr & FIRIRSR_DDE;
+ crc_error = sr & FIRIRSR_CRCE;
+ overrun_error = sr & FIRIRSR_RFO;
+
+ if (!(dd_error | crc_error | overrun_error)) {
+ return 0;
+ }
+ dev_err(si->dev, "dde,crce,rfo=%d,%d,%d.\n", dd_error, crc_error,
+ overrun_error);
+ si->stats.rx_errors++;
+ if (crc_error) {
+ si->stats.rx_crc_errors++;
+ }
+ if (dd_error) {
+ si->stats.rx_frame_errors++;
+ }
+ if (overrun_error) {
+ si->stats.rx_frame_errors++;
+ }
+ writel(sr, si->firi_base + FIRIRSR);
+
+ return -1;
+}
+
+#ifndef FIRI_SDMA_RX
+/*!
+ * FIR interrupt service routine to handle receive.
+ *
+ * @param dev pointer to the net_device structure
+ */
+void mxc_irda_fir_irq_rx(struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ struct sk_buff *skb = si->rxskb;
+ unsigned int sr, len;
+ int i;
+ unsigned char *p = skb->data;
+
+ /*
+ * Deal with any receive errors.
+ */
+ if (mxc_irda_fir_error(si) != 0) {
+ return;
+ }
+
+ sr = readl(si->firi_base + FIRIRSR);
+
+ if (!(sr & FIRIRSR_RPE)) {
+ return;
+ }
+
+ /*
+ * Coming here indicates that fir rx packet has been successfully recieved.
+ * And No error happened so far.
+ */
+ writel(sr | FIRIRSR_RPE, si->firi_base + FIRIRSR);
+
+ len = (sr & FIRIRSR_RFP) >> 8;
+
+ /* 4 bytes of CRC */
+ len -= 4;
+
+ skb_put(skb, len);
+
+ for (i = 0; i < len; i++) {
+ *p++ = readb(si->firi_base + FIRIRXFIFO);
+ }
+
+ /* Discard the four CRC bytes */
+ for (i = 0; i < 4; i++) {
+ readb(si->firi_base + FIRIRXFIFO);
+ }
+
+ /*
+ * Deal with the case of packet complete.
+ */
+ skb->dev = dev;
+ skb->mac.raw = skb->data;
+ skb->protocol = htons(ETH_P_IRDA);
+ si->stats.rx_packets++;
+ si->stats.rx_bytes += len;
+ netif_rx(skb);
+
+ si->rxskb = NULL;
+ mxc_irda_rx_alloc(si);
+
+ writel(0xFFFF, si->firi_base + FIRIRSR);
+
+}
+#endif
+
+/*!
+ * FIR interrupt service routine to handle transmit.
+ *
+ * @param dev pointer to the net_device structure
+ */
+void mxc_irda_fir_irq_tx(struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ struct sk_buff *skb = si->txskb;
+ unsigned int cr, sr;
+
+ sr = readl(si->firi_base + FIRITSR);
+ writel(sr, si->firi_base + FIRITSR);
+
+ if (sr & FIRITSR_TC) {
+
+#ifdef FIRI_SDMA_TX
+ mxc_dma_disable(si->txdma_ch);
+#endif
+ cr = readl(si->firi_base + FIRITCR);
+ cr &= ~(FIRITCR_TCIE | FIRITCR_TE);
+ writel(cr, si->firi_base + FIRITCR);
+
+ if (si->newspeed) {
+ mxc_irda_set_speed(si, si->newspeed);
+ si->newspeed = 0;
+ }
+ si->txskb = NULL;
+
+ cr = readl(si->firi_base + FIRIRCR);
+ cr |= FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+
+ writel(0xFFFF, si->firi_base + FIRIRSR);
+ /*
+ * Account and free the packet.
+ */
+ if (skb) {
+#ifdef FIRI_SDMA_TX
+ dma_unmap_single(si->dev, si->dma_tx_buff_phy, skb->len,
+ DMA_TO_DEVICE);
+#endif
+ si->stats.tx_packets++;
+ si->stats.tx_bytes += skb->len;
+ dev_kfree_skb_irq(skb);
+ }
+ /*
+ * Make sure that the TX queue is available for sending
+ * (for retries). TX has priority over RX at all times.
+ */
+ netif_wake_queue(dev);
+ }
+}
+
+/*!
+ * This is FIRI interrupt handler.
+ *
+ * @param dev pointer to the net_device structure
+ */
+void mxc_irda_fir_irq(struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ unsigned int sr1, sr2;
+
+ sr1 = readl(si->firi_base + FIRIRSR);
+ sr2 = readl(si->firi_base + FIRITSR);
+
+ if (sr2 & FIRITSR_TC)
+ mxc_irda_fir_irq_tx(dev);
+#ifndef FIRI_SDMA_RX
+ if (sr1 & (FIRIRSR_RPE | FIRIRSR_RFO))
+ mxc_irda_fir_irq_rx(dev);
+#endif
+
+}
+
+/*!
+ * This is the SIR transmit routine.
+ *
+ * @param si FIRI specific structure.
+ *
+ * @param dev pointer to the net_device structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_sir_txirq(struct mxc_irda *si, struct net_device *dev)
+{
+ unsigned int sr1, sr2, cr;
+ unsigned int status;
+
+ sr1 = readl(si->uart_base + MXC_UARTUSR1);
+ sr2 = readl(si->uart_base + MXC_UARTUSR2);
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+
+ /*
+ * Echo cancellation for IRDA Transmit chars
+ * Disable the receiver and enable Transmit complete.
+ */
+ cr &= ~MXC_UARTUCR2_RXEN;
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+ cr = readl(si->uart_base + MXC_UARTUCR4);
+ cr |= MXC_UARTUCR4_TCEN;
+ writel(cr, si->uart_base + MXC_UARTUCR4);
+
+ while ((sr1 & MXC_UARTUSR1_TRDY) && si->tx_buff.len) {
+
+ writel(*si->tx_buff.data++, si->uart_base + MXC_UARTUTXD);
+ si->tx_buff.len -= 1;
+ sr1 = readl(si->uart_base + MXC_UARTUSR1);
+ }
+
+ if (si->tx_buff.len == 0) {
+ si->stats.tx_packets++;
+ si->stats.tx_bytes += si->tx_buff.data - si->tx_buff.head;
+
+ /*Yoohoo...we are done...Lets stop Tx */
+ cr = readl(si->uart_base + MXC_UARTUCR1);
+ cr &= ~MXC_UARTUCR1_TRDYEN;
+ writel(cr, si->uart_base + MXC_UARTUCR1);
+
+ do {
+ status = readl(si->uart_base + MXC_UARTUSR2);
+ } while (!(status & MXC_UARTUSR2_TXDC));
+
+ if (si->newspeed) {
+ mxc_irda_set_speed(si, si->newspeed);
+ si->newspeed = 0;
+ }
+ /* I'm hungry! */
+ netif_wake_queue(dev);
+
+ /* Is the transmit complete to reenable the receiver? */
+ if (status & MXC_UARTUSR2_TXDC) {
+
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ cr |= MXC_UARTUCR2_RXEN;
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+ /* Disable the Transmit complete interrupt bit */
+ cr = readl(si->uart_base + MXC_UARTUCR4);
+ cr &= ~MXC_UARTUCR4_TCEN;
+ writel(cr, si->uart_base + MXC_UARTUCR4);
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * This is the SIR receive routine.
+ *
+ * @param si FIRI specific structure.
+ *
+ * @param dev pointer to the net_device structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_sir_rxirq(struct mxc_irda *si, struct net_device *dev)
+{
+ unsigned int data, status;
+ volatile unsigned int sr2;
+
+ sr2 = readl(si->uart_base + MXC_UARTUSR2);
+ while ((sr2 & MXC_UARTUSR2_RDR) == 1) {
+ data = readl(si->uart_base + MXC_UARTURXD);
+ status = data & 0xf400;
+ if (status & MXC_UARTURXD_ERR) {
+ dev_err(si->dev, "Receive an incorrect data =0x%x.\n",
+ data);
+ si->stats.rx_errors++;
+ if (status & MXC_UARTURXD_OVRRUN) {
+ si->stats.rx_fifo_errors++;
+ dev_err(si->dev, "Rx overrun.\n");
+ }
+ if (status & MXC_UARTURXD_FRMERR) {
+ si->stats.rx_frame_errors++;
+ dev_err(si->dev, "Rx frame error.\n");
+ }
+ if (status & MXC_UARTURXD_PRERR) {
+ dev_err(si->dev, "Rx parity error.\n");
+ }
+ /* Other: it is the Break char.
+ * Do nothing for it. throw out the data.
+ */
+ async_unwrap_char(dev, &si->stats, &si->rx_buff,
+ (data & 0xFF));
+ } else {
+ /* It is correct data. */
+ data &= 0xFF;
+ async_unwrap_char(dev, &si->stats, &si->rx_buff, data);
+
+ dev->last_rx = jiffies;
+ }
+ sr2 = readl(si->uart_base + MXC_UARTUSR2);
+
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR1);
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR2);
+ } /*while */
+ return 0;
+
+}
+
+static irqreturn_t mxc_irda_irq(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct mxc_irda *si = dev->priv;
+
+ if (IS_FIR(si)) {
+ mxc_irda_fir_irq(dev);
+ return IRQ_HANDLED;
+ }
+
+ if (readl(si->uart_base + MXC_UARTUCR2) & MXC_UARTUCR2_RXEN) {
+ mxc_irda_sir_rxirq(si, dev);
+ }
+ if ((readl(si->uart_base + MXC_UARTUCR1) & MXC_UARTUCR1_TRDYEN) &&
+ (readl(si->uart_base + MXC_UARTUSR1) & MXC_UARTUSR1_TRDY)) {
+ mxc_irda_sir_txirq(si, dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxc_irda_tx_irq(int irq, void *dev_id)
+{
+
+ struct net_device *dev = dev_id;
+ struct mxc_irda *si = dev->priv;
+
+ mxc_irda_sir_txirq(si, dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxc_irda_rx_irq(int irq, void *dev_id)
+{
+
+ struct net_device *dev = dev_id;
+ struct mxc_irda *si = dev->priv;
+
+ /* Clear the aging timer bit */
+ writel(MXC_UARTUSR1_AGTIM, si->uart_base + MXC_UARTUSR1);
+
+ mxc_irda_sir_rxirq(si, dev);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef FIRI_SDMA_RX
+struct tasklet_struct dma_rx_tasklet;
+
+static void mxc_irda_rx_task(unsigned long tparam)
+{
+ struct mxc_irda *si = (struct mxc_irda *)tparam;
+ struct sk_buff *lskb = si->tskb;
+
+ si->tskb = NULL;
+ if (lskb) {
+ lskb->mac_header = lskb->data;
+ lskb->protocol = htons(ETH_P_IRDA);
+ netif_rx(lskb);
+ }
+}
+
+/*!
+ * Receiver DMA callback routine.
+ *
+ * @param id pointer to network device structure
+ * @param error_status used to pass error status to this callback function
+ * @param count number of bytes received
+ */
+static void mxc_irda_fir_dma_rx_irq(void *id, int error_status,
+ unsigned int count)
+{
+ struct net_device *dev = id;
+ struct mxc_irda *si = dev->priv;
+ struct sk_buff *skb = si->rxskb;
+ unsigned int cr;
+ unsigned int len;
+
+ cr = readl(si->firi_base + FIRIRCR);
+ cr &= ~FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+ cr = readl(si->firi_base + FIRIRCR);
+ cr |= FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+ len = count - 4; /* remove 4 bytes for CRC */
+ skb_put(skb, len);
+ skb->dev = dev;
+ si->tskb = skb;
+ tasklet_schedule(&dma_rx_tasklet);
+
+ if (si->dma_rx_buff_phy != 0)
+ dma_unmap_single(si->dev, si->dma_rx_buff_phy,
+ IRDA_FRAME_SIZE_LIMIT, DMA_FROM_DEVICE);
+
+ si->rxskb = NULL;
+ mxc_irda_rx_alloc(si);
+
+ SDMA_START_DELAY();
+ writel(0xFFFF, si->firi_base + FIRIRSR);
+
+ if (si->rxskb) {
+ mxc_dma_enable(si->rxdma_ch);
+ }
+}
+#endif
+
+#ifdef FIRI_SDMA_TX
+/*!
+ * This function is called by SDMA Interrupt Service Routine to indicate
+ * requested DMA transfer is completed.
+ *
+ * @param id pointer to network device structure
+ * @param error_status used to pass error status to this callback function
+ * @param count number of bytes sent
+ */
+static void mxc_irda_fir_dma_tx_irq(void *id, int error_status,
+ unsigned int count)
+{
+ struct net_device *dev = id;
+ struct mxc_irda *si = dev->priv;
+
+ mxc_dma_disable(si->txdma_ch);
+}
+#endif
+
+/*!
+ * This function is called by Linux IrDA network subsystem to
+ * transmit the Infrared data packet. The TX DMA channel is configured
+ * to transfer SK buffer data to FIRI TX FIFO along with DMA transfer
+ * completion routine.
+ *
+ * @param skb The packet that is queued to be sent
+ * @param dev net_device structure.
+ *
+ * @return The function returns 0 on success and a negative value on
+ * failure.
+ */
+static int mxc_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ int speed = irda_get_next_speed(skb);
+ unsigned int cr;
+
+ /*
+ * Does this packet contain a request to change the interface
+ * speed? If so, remember it until we complete the transmission
+ * of this frame.
+ */
+ if (speed != si->speed && speed != -1) {
+ si->newspeed = speed;
+ }
+
+ /* If this is an empty frame, we can bypass a lot. */
+ if (skb->len == 0) {
+ if (si->newspeed) {
+ si->newspeed = 0;
+ mxc_irda_set_speed(si, speed);
+ }
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ /* We must not be transmitting... */
+ netif_stop_queue(dev);
+ if (IS_SIR(si)) {
+
+ si->tx_buff.data = si->tx_buff.head;
+ si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data,
+ si->tx_buff.truesize);
+ cr = readl(si->uart_base + MXC_UARTUCR1);
+ cr |= MXC_UARTUCR1_TRDYEN;
+ writel(cr, si->uart_base + MXC_UARTUCR1);
+ dev_kfree_skb(skb);
+ } else {
+ unsigned int mtt = irda_get_mtt(skb);
+ unsigned char *p = skb->data;
+ unsigned int skb_len = skb->len;
+#ifdef FIRI_SDMA_TX
+ mxc_dma_requestbuf_t dma_request;
+#else
+ unsigned int i, sr;
+#endif
+
+ skb_len = skb_len + ((4 - (skb_len % 4)) % 4);
+
+ if (si->txskb) {
+ BUG();
+ }
+ si->txskb = skb;
+
+ /*
+ * If we have a mean turn-around time, impose the specified
+ * specified delay. We could shorten this by timing from
+ * the point we received the packet.
+ */
+ if (mtt) {
+ udelay(mtt);
+ }
+
+ cr = readl(si->firi_base + FIRIRCR);
+ cr &= ~FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+
+ writel(skb->len - 1, si->firi_base + FIRITCTR);
+
+#ifdef FIRI_SDMA_TX
+ /*
+ * Configure DMA Tx Channel for source and destination addresses,
+ * Number of bytes in SK buffer to transfer and Transfer complete
+ * callback function.
+ */
+ si->dma_tx_buff_len = skb_len;
+ si->dma_tx_buff_phy =
+ dma_map_single(si->dev, p, skb_len, DMA_TO_DEVICE);
+
+ dma_request.num_of_bytes = skb_len;
+ dma_request.dst_addr = si->firi_res->start + FIRITXFIFO;
+ dma_request.src_addr = si->dma_tx_buff_phy;
+
+ mxc_dma_config(si->txdma_ch, &dma_request, 1,
+ MXC_DMA_MODE_WRITE);
+
+ mxc_dma_enable(si->txdma_ch);
+#endif
+ cr = readl(si->firi_base + FIRITCR);
+ cr |= FIRITCR_TCIE;
+ writel(cr, si->firi_base + FIRITCR);
+
+ cr |= FIRITCR_TE;
+ writel(cr, si->firi_base + FIRITCR);
+
+#ifndef FIRI_SDMA_TX
+ for (i = 0; i < skb->len;) {
+ sr = readl(si->firi_base + FIRITSR);
+ /* TFP = number of bytes in the TX FIFO for the
+ * Transmitter
+ * */
+ if ((sr >> 8) < 128) {
+ writeb(*p, si->firi_base + FIRITXFIFO);
+ p++;
+ i++;
+ }
+ }
+#endif
+ }
+
+ dev->trans_start = jiffies;
+ return 0;
+}
+
+/*!
+ * This function handles network interface ioctls passed to this driver..
+ *
+ * @param dev net device structure
+ * @param ifreq user request data
+ * @param cmd command issued
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
+{
+ struct if_irda_req *rq = (struct if_irda_req *)ifreq;
+ struct mxc_irda *si = dev->priv;
+ int ret = -EOPNOTSUPP;
+
+ switch (cmd) {
+ /* This function will be used by IrLAP to change the speed */
+ case SIOCSBANDWIDTH:
+ dev_dbg(si->dev, "%s:with cmd SIOCSBANDWIDTH\n", __FUNCTION__);
+ if (capable(CAP_NET_ADMIN)) {
+ /*
+ * We are unable to set the speed if the
+ * device is not running.
+ */
+ if (si->open) {
+ ret = mxc_irda_set_speed(si, rq->ifr_baudrate);
+ } else {
+ dev_err(si->dev, "mxc_ir_ioctl: SIOCSBANDWIDTH:\
+ !netif_running\n");
+ ret = 0;
+ }
+ }
+ break;
+ case SIOCSMEDIABUSY:
+ dev_dbg(si->dev, "%s:with cmd SIOCSMEDIABUSY\n", __FUNCTION__);
+ ret = -EPERM;
+ if (capable(CAP_NET_ADMIN)) {
+ irda_device_set_media_busy(dev, TRUE);
+ ret = 0;
+ }
+ break;
+ case SIOCGRECEIVING:
+ rq->ifr_receiving =
+ IS_SIR(si) ? si->rx_buff.state != OUTSIDE_FRAME : 0;
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+/*!
+ * Kernel interface routine to get current statistics of the device
+ * which includes the number bytes/packets transmitted/received,
+ * receive errors, CRC errors, framing errors etc.
+ *
+ * @param dev the net_device structure
+ *
+ * @return This function returns IrDA network statistics
+ */
+static struct net_device_stats *mxc_irda_stats(struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ return &si->stats;
+}
+
+/*!
+ * FIRI init function
+ *
+ * @param si FIRI device specific structure.
+ */
+void mxc_irda_firi_init(struct mxc_irda *si)
+{
+ unsigned int firi_baud, osf = 6;
+ unsigned int tcr, rcr, cr;
+
+ si->firi_clk = clk_get(si->dev, "firi_clk");
+ firi_baud = clk_round_rate(si->firi_clk, 48004500);
+ if ((firi_baud < 47995500) ||
+ (clk_set_rate(si->firi_clk, firi_baud) < 0)) {
+ dev_err(si->dev, "Unable to set FIR clock to 48MHz.\n");
+ return;
+ }
+ clk_enable(si->firi_clk);
+
+ writel(0xFFFF, si->firi_base + FIRITSR);
+ writel(0xFFFF, si->firi_base + FIRIRSR);
+ writel(0x00, si->firi_base + FIRITCR);
+ writel(0x00, si->firi_base + FIRIRCR);
+
+ /* set _BL & _OSF */
+ cr = (osf - 1) | (16 << 5);
+ writel(cr, si->firi_base + FIRICR);
+
+#ifdef FIRI_SDMA_TX
+ tcr =
+ FIRITCR_TDT_FIR | FIRITCR_TM_FIR | FIRITCR_TCIE |
+ FIRITCR_PCF | FIRITCR_PC;
+#else
+ tcr = FIRITCR_TM_FIR | FIRITCR_TCIE | FIRITCR_PCF | FIRITCR_PC;
+#endif
+
+#ifdef FIRI_SDMA_RX
+ rcr =
+ FIRIRCR_RPEDE | FIRIRCR_RM_FIR | FIRIRCR_RDT_FIR |
+ FIRIRCR_RPA | FIRIRCR_RPP;
+#else
+ rcr =
+ FIRIRCR_RPEDE | FIRIRCR_RM_FIR | FIRIRCR_RDT_FIR | FIRIRCR_RPEIE |
+ FIRIRCR_RPA | FIRIRCR_PAIE | FIRIRCR_RFOIE | FIRIRCR_RPP;
+#endif
+
+ writel(tcr, si->firi_base + FIRITCR);
+ writel(rcr, si->firi_base + FIRIRCR);
+ cr = 0;
+ writel(cr, si->firi_base + FIRITCTR);
+}
+
+/*!
+ * This function initialises the UART.
+ *
+ * @param si FIRI port specific structure.
+ *
+ * @return The function returns 0 on success.
+ */
+static int mxc_irda_uart_init(struct mxc_irda *si)
+{
+ unsigned int per_clk;
+ unsigned int num, denom, baud, ufcr = 0;
+ unsigned int cr;
+ int d = 1;
+ int uart_ir_mux = 0;
+
+ if (si->mxc_ir_plat)
+ uart_ir_mux = si->mxc_ir_plat->uart_ir_mux;
+ /*
+ * Clear Status Registers 1 and 2
+ **/
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR1);
+ writel(0xFFFF, si->uart_base + MXC_UARTUSR2);
+
+ /* Configure the IOMUX for the UART */
+ gpio_firi_init();
+
+ per_clk = clk_get_rate(si->uart_clk);
+ baud = per_clk / 16;
+ if (baud > 1500000) {
+ baud = 1500000;
+ d = per_clk / ((baud * 16) + 1000);
+ if (d > 6) {
+ d = 6;
+ }
+ }
+ clk_enable(si->uart_clk);
+
+ si->uart_clk_rate = per_clk / d;
+ writel(si->uart_clk_rate / 1000, si->uart_base + MXC_UARTONEMS);
+
+ writel(si->mxc_ir_plat->ir_rx_invert | MXC_UARTUCR4_IRSC,
+ si->uart_base + MXC_UARTUCR4);
+
+ if (uart_ir_mux) {
+ writel(MXC_UARTUCR3_RXDMUXSEL | si->mxc_ir_plat->ir_tx_invert |
+ MXC_UARTUCR3_DSR, si->uart_base + MXC_UARTUCR3);
+ } else {
+ writel(si->mxc_ir_plat->ir_tx_invert | MXC_UARTUCR3_DSR,
+ si->uart_base + MXC_UARTUCR3);
+ }
+
+ writel(MXC_UARTUCR2_IRTS | MXC_UARTUCR2_CTS | MXC_UARTUCR2_WS |
+ MXC_UARTUCR2_ATEN | MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN,
+ si->uart_base + MXC_UARTUCR2);
+ /* Wait till we are out of software reset */
+ do {
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ } while (!(cr & MXC_UARTUCR2_SRST));
+
+ ufcr |= (UART4_UFCR_TXTL << MXC_UARTUFCR_TXTL_OFFSET) |
+ ((6 - d) << MXC_UARTUFCR_RFDIV_OFFSET) | UART4_UFCR_RXTL;
+ writel(ufcr, si->uart_base + MXC_UARTUFCR);
+
+ writel(MXC_UARTUCR1_UARTEN | MXC_UARTUCR1_IREN,
+ si->uart_base + MXC_UARTUCR1);
+
+ baud = 9600;
+ num = baud / 100 - 1;
+ denom = si->uart_clk_rate / 1600 - 1;
+
+ if ((denom < 65536) && (si->uart_clk_rate > 1600)) {
+ writel(num, si->uart_base + MXC_UARTUBIR);
+ writel(denom, si->uart_base + MXC_UARTUBMR);
+ }
+
+ writel(0x0000, si->uart_base + MXC_UARTUTS);
+ return 0;
+
+}
+
+/*!
+ * This function enables FIRI port.
+ *
+ * @param si FIRI port specific structure.
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int mxc_irda_startup(struct mxc_irda *si)
+{
+ int ret = 0;
+
+ mxc_irda_uart_init(si);
+ mxc_irda_firi_init(si);
+
+ /* configure FIRI device for speed */
+ ret = mxc_irda_set_speed(si, si->speed = 9600);
+
+ return ret;
+}
+
+/*!
+ * When an ifconfig is issued which changes the device flag to include
+ * IFF_UP this function is called. It is only called when the change
+ * occurs, not when the interface remains up. The function grabs the interrupt
+ * resources and registers FIRI interrupt service routines, requests for DMA
+ * channels, configures the DMA channel. It then initializes the IOMUX
+ * registers to configure the pins for FIRI signals and finally initializes the
+ * various FIRI registers and enables the port for reception.
+ *
+ * @param dev net device structure that is being opened
+ *
+ * @return The function returns 0 for a successful open and non-zero value
+ * on failure.
+ */
+static int mxc_irda_start(struct net_device *dev)
+{
+ struct mxc_irda *si = dev->priv;
+ int err;
+ int ints_muxed = 0;
+ mxc_dma_device_t dev_id = 0;
+
+ if (si->uart_irq == si->uart_irq1)
+ ints_muxed = 1;
+
+ si->speed = 9600;
+
+ if (si->uart_irq == si->firi_irq) {
+ err =
+ request_irq(si->uart_irq, mxc_irda_irq, 0, dev->name, dev);
+ if (err) {
+ dev_err(si->dev, "%s:Failed to request the IRQ\n",
+ __FUNCTION__);
+ return err;
+ }
+ /*
+ * The interrupt must remain disabled for now.
+ */
+ disable_irq(si->uart_irq);
+ } else {
+ err =
+ request_irq(si->firi_irq, mxc_irda_irq, 0, dev->name, dev);
+ if (err) {
+ dev_err(si->dev, "%s:Failed to request FIRI IRQ\n",
+ __FUNCTION__);
+ return err;
+ }
+ /*
+ * The interrupt must remain disabled for now.
+ */
+ disable_irq(si->firi_irq);
+ if (ints_muxed) {
+
+ err = request_irq(si->uart_irq, mxc_irda_irq, 0,
+ dev->name, dev);
+ if (err) {
+ dev_err(si->dev,
+ "%s:Failed to request UART IRQ\n",
+ __FUNCTION__);
+ goto err_irq1;
+ }
+ /*
+ * The interrupt must remain disabled for now.
+ */
+ disable_irq(si->uart_irq);
+ } else {
+ err = request_irq(si->uart_irq, mxc_irda_tx_irq, 0,
+ dev->name, dev);
+ if (err) {
+ dev_err(si->dev,
+ "%s:Failed to request UART IRQ\n",
+ __FUNCTION__);
+ goto err_irq1;
+ }
+ err = request_irq(si->uart_irq1, mxc_irda_rx_irq, 0,
+ dev->name, dev);
+ if (err) {
+ dev_err(si->dev,
+ "%s:Failed to request UART1 IRQ\n",
+ __FUNCTION__);
+ goto err_irq2;
+ }
+ /*
+ * The interrupts must remain disabled for now.
+ */
+ disable_irq(si->uart_irq);
+ disable_irq(si->uart_irq1);
+ }
+ }
+#ifdef FIRI_SDMA_RX
+ dev_id = MXC_DMA_FIR_RX;
+ si->rxdma_ch = mxc_dma_request(dev_id, "MXC FIRI RX");
+ if (si->rxdma_ch < 0) {
+ dev_err(si->dev, "Cannot allocate FIR DMA channel\n");
+ goto err_rx_dma;
+ }
+ mxc_dma_callback_set(si->rxdma_ch, mxc_irda_fir_dma_rx_irq,
+ (void *)dev_get_drvdata(si->dev));
+#endif
+#ifdef FIRI_SDMA_TX
+
+ dev_id = MXC_DMA_FIR_TX;
+ si->txdma_ch = mxc_dma_request(dev_id, "MXC FIRI TX");
+ if (si->txdma_ch < 0) {
+ dev_err(si->dev, "Cannot allocate FIR DMA channel\n");
+ goto err_tx_dma;
+ }
+ mxc_dma_callback_set(si->txdma_ch, mxc_irda_fir_dma_tx_irq,
+ (void *)dev_get_drvdata(si->dev));
+#endif
+ /* Setup the serial port port for the initial speed. */
+ err = mxc_irda_startup(si);
+ if (err) {
+ goto err_startup;
+ }
+
+ /* Open a new IrLAP layer instance. */
+ si->irlap = irlap_open(dev, &si->qos, "mxc");
+ err = -ENOMEM;
+ if (!si->irlap) {
+ goto err_irlap;
+ }
+
+ /* Now enable the interrupt and start the queue */
+ si->open = 1;
+ si->suspend = 0;
+
+ if (si->uart_irq == si->firi_irq) {
+ enable_irq(si->uart_irq);
+ } else {
+ enable_irq(si->firi_irq);
+ if (ints_muxed == 1) {
+ enable_irq(si->uart_irq);
+ } else {
+ enable_irq(si->uart_irq);
+ enable_irq(si->uart_irq1);
+ }
+ }
+
+ netif_start_queue(dev);
+ return 0;
+
+ err_irlap:
+ si->open = 0;
+ mxc_irda_disabledma(si);
+ err_startup:
+#ifdef FIRI_SDMA_TX
+ mxc_dma_free(si->txdma_ch);
+ err_tx_dma:
+#endif
+#ifdef FIRI_SDMA_RX
+ mxc_dma_free(si->rxdma_ch);
+ err_rx_dma:
+#endif
+ if (si->uart_irq1 && !ints_muxed)
+ free_irq(si->uart_irq1, dev);
+ err_irq2:
+ if (si->uart_irq != si->firi_irq)
+ free_irq(si->uart_irq, dev);
+ err_irq1:
+ if (si->firi_irq)
+ free_irq(si->firi_irq, dev);
+ return err;
+}
+
+/*!
+ * This function is called when IFF_UP flag has been cleared by the user via
+ * the ifconfig irda0 down command. This function stops any further
+ * transmissions being queued, and then disables the interrupts.
+ * Finally it resets the device.
+ * @param dev the net_device structure
+ *
+ * @return int the function always returns 0 indicating a success.
+ */
+static int mxc_irda_stop(struct net_device *dev)
+{
+ struct mxc_irda *si = netdev_priv(dev);
+ unsigned long flags;
+
+ /* Stop IrLAP */
+ if (si->irlap) {
+ irlap_close(si->irlap);
+ si->irlap = NULL;
+ }
+
+ netif_stop_queue(dev);
+
+ /*Save flags and disable the FIRI interrupts.. */
+ if (si->open) {
+ local_irq_save(flags);
+ disable_irq(si->uart_irq);
+ free_irq(si->uart_irq, dev);
+ if (si->uart_irq != si->firi_irq) {
+ disable_irq(si->firi_irq);
+ free_irq(si->firi_irq, dev);
+ if (si->uart_irq1 != si->uart_irq) {
+ disable_irq(si->uart_irq1);
+ free_irq(si->uart_irq1, dev);
+ }
+ }
+ local_irq_restore(flags);
+ si->open = 0;
+ }
+#ifdef FIRI_SDMA_RX
+ if (si->rxdma_ch) {
+ mxc_dma_disable(si->rxdma_ch);
+ mxc_dma_free(si->rxdma_ch);
+ if (si->dma_rx_buff_phy) {
+ dma_unmap_single(si->dev, si->dma_rx_buff_phy,
+ IRDA_FRAME_SIZE_LIMIT,
+ DMA_FROM_DEVICE);
+ si->dma_rx_buff_phy = 0;
+ }
+ si->rxdma_ch = 0;
+ }
+ tasklet_kill(&dma_rx_tasklet);
+#endif
+#ifdef FIRI_SDMA_TX
+ if (si->txdma_ch) {
+ mxc_dma_disable(si->txdma_ch);
+ mxc_dma_free(si->txdma_ch);
+ if (si->dma_tx_buff_phy) {
+ dma_unmap_single(si->dev, si->dma_tx_buff_phy,
+ si->dma_tx_buff_len, DMA_TO_DEVICE);
+ si->dma_tx_buff_phy = 0;
+ }
+ si->txdma_ch = 0;
+ }
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function is called to put the FIRI in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which FIRI
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_irda_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct mxc_irda *si = netdev_priv(ndev);
+ unsigned int cr;
+ unsigned long flags;
+
+ if (!si) {
+ return 0;
+ }
+ if (si->suspend == 1) {
+ dev_err(si->dev,
+ " suspend - Device is already suspended ... \n");
+ return 0;
+ }
+ if (si->open) {
+
+ netif_device_detach(ndev);
+ mxc_irda_disabledma(si);
+
+ /*Save flags and disable the FIRI interrupts.. */
+ local_irq_save(flags);
+ disable_irq(si->uart_irq);
+ if (si->uart_irq != si->firi_irq) {
+ disable_irq(si->firi_irq);
+ if (si->uart_irq != si->uart_irq1) {
+ disable_irq(si->uart_irq1);
+ }
+ }
+ local_irq_restore(flags);
+
+ /* Disable Tx and Rx and then disable the UART clock */
+ cr = readl(si->uart_base + MXC_UARTUCR2);
+ cr &= ~(MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
+ writel(cr, si->uart_base + MXC_UARTUCR2);
+ cr = readl(si->uart_base + MXC_UARTUCR1);
+ cr &= ~MXC_UARTUCR1_UARTEN;
+ writel(cr, si->uart_base + MXC_UARTUCR1);
+ clk_disable(si->uart_clk);
+
+ /*Disable Tx and Rx for FIRI and then disable the FIRI clock.. */
+ cr = readl(si->firi_base + FIRITCR);
+ cr &= ~FIRITCR_TE;
+ writel(cr, si->firi_base + FIRITCR);
+ cr = readl(si->firi_base + FIRIRCR);
+ cr &= ~FIRIRCR_RE;
+ writel(cr, si->firi_base + FIRIRCR);
+ clk_disable(si->firi_clk);
+
+ gpio_firi_inactive();
+
+ si->suspend = 1;
+ si->open = 0;
+ }
+ return 0;
+}
+
+/*!
+ * This function is called to bring the FIRI back from a low power state. Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which FIRI
+ * to resume
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_irda_resume(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct mxc_irda *si = netdev_priv(ndev);
+ unsigned long flags;
+
+ if (!si) {
+ return 0;
+ }
+
+ if (si->suspend == 1 && !si->open) {
+
+ /*Initialise the UART first */
+ clk_enable(si->uart_clk);
+
+ /*Now init FIRI */
+ gpio_firi_active(si->firi_base + FIRITCR, FIRITCR_TPP);
+ mxc_irda_startup(si);
+
+ /* Enable the UART and FIRI interrupts.. */
+ local_irq_save(flags);
+ enable_irq(si->uart_irq);
+ if (si->uart_irq != si->firi_irq) {
+ enable_irq(si->firi_irq);
+ if (si->uart_irq != si->uart_irq1) {
+ enable_irq(si->uart_irq1);
+ }
+ }
+ local_irq_restore(flags);
+
+ /* Let the kernel know that we are alive and kicking.. */
+ netif_device_attach(ndev);
+
+ si->suspend = 0;
+ si->open = 1;
+ }
+ return 0;
+}
+#else
+#define mxc_irda_suspend NULL
+#define mxc_irda_resume NULL
+#endif
+
+static int mxc_irda_init_iobuf(iobuff_t * io, int size)
+{
+ io->head = kmalloc(size, GFP_KERNEL | GFP_DMA);
+ if (io->head != NULL) {
+ io->truesize = size;
+ io->in_frame = FALSE;
+ io->state = OUTSIDE_FRAME;
+ io->data = io->head;
+ }
+ return io->head ? 0 : -ENOMEM;
+
+}
+
+/*!
+ * This function is called during the driver binding process.
+ * This function requests for memory, initializes net_device structure and
+ * registers with kernel.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions
+ *
+ * @return The function returns 0 on success and a non-zero value on failure
+ */
+static int mxc_irda_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct mxc_irda *si;
+ struct resource *uart_res, *firi_res;
+ int uart_irq, firi_irq, uart_irq1;
+ unsigned int baudrate_mask = 0;
+ int err;
+
+ uart_res = &pdev->resource[0];
+ uart_irq = pdev->resource[1].start;
+
+ firi_res = &pdev->resource[2];
+ firi_irq = pdev->resource[3].start;
+
+ uart_irq1 = pdev->resource[4].start;
+
+ if (!uart_res || uart_irq == NO_IRQ || !firi_res || firi_irq == NO_IRQ) {
+ dev_err(&pdev->dev, "Unable to find resources\n");
+ return -ENXIO;
+ }
+
+ err =
+ request_mem_region(uart_res->start, SZ_16K,
+ "MXC_IRDA") ? 0 : -EBUSY;
+ if (err) {
+ dev_err(&pdev->dev, "Failed to request UART memory region\n");
+ return -ENOMEM;
+ }
+
+ err =
+ request_mem_region(firi_res->start, SZ_16K,
+ "MXC_IRDA") ? 0 : -EBUSY;
+ if (err) {
+ dev_err(&pdev->dev, "Failed to request FIRI memory region\n");
+ goto err_mem_1;
+ }
+
+ dev = alloc_irdadev(sizeof(struct mxc_irda));
+ if (!dev) {
+ goto err_mem_2;
+ }
+
+ si = netdev_priv(dev);
+ si->dev = &pdev->dev;
+
+ si->mxc_ir_plat = pdev->dev.platform_data;
+ si->uart_clk = si->mxc_ir_plat->uart_clk;
+
+ si->uart_res = uart_res;
+ si->firi_res = firi_res;
+ si->uart_irq = uart_irq;
+ si->firi_irq = firi_irq;
+ si->uart_irq1 = uart_irq1;
+
+ si->uart_base = ioremap(uart_res->start, SZ_16K);
+ si->firi_base = ioremap(firi_res->start, SZ_16K);
+
+ if (!(si->uart_base || si->firi_base)) {
+ err = -ENOMEM;
+ goto err_mem_3;
+ }
+
+ /*
+ * Initialise the SIR buffers
+ */
+ err = mxc_irda_init_iobuf(&si->rx_buff, UART_BUFF_SIZE);
+ if (err) {
+ goto err_mem_4;
+ }
+
+ err = mxc_irda_init_iobuf(&si->tx_buff, UART_BUFF_SIZE);
+ if (err) {
+ goto err_mem_5;
+ }
+
+ dev->hard_start_xmit = mxc_irda_hard_xmit;
+ dev->open = mxc_irda_start;
+ dev->stop = mxc_irda_stop;
+ dev->do_ioctl = mxc_irda_ioctl;
+ dev->get_stats = mxc_irda_stats;
+
+ irda_init_max_qos_capabilies(&si->qos);
+
+ /*
+ * We support
+ * SIR(9600, 19200,38400, 57600 and 115200 bps)
+ * FIR(4 Mbps)
+ * Min Turn Time set to 1ms or greater.
+ */
+ baudrate_mask |= IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200;
+ baudrate_mask |= IR_4000000 << 8;
+
+ si->qos.baud_rate.bits &= baudrate_mask;
+ si->qos.min_turn_time.bits = 0x7;
+
+ irda_qos_bits_to_value(&si->qos);
+
+#ifdef FIRI_SDMA_RX
+ si->tskb = NULL;
+ tasklet_init(&dma_rx_tasklet, mxc_irda_rx_task, (unsigned long)si);
+#endif
+ err = register_netdev(dev);
+ if (err == 0) {
+ platform_set_drvdata(pdev, dev);
+ } else {
+ kfree(si->tx_buff.head);
+ err_mem_5:
+ kfree(si->rx_buff.head);
+ err_mem_4:
+ iounmap(si->uart_base);
+ iounmap(si->firi_base);
+ err_mem_3:
+ free_netdev(dev);
+ err_mem_2:
+ release_mem_region(firi_res->start, SZ_16K);
+ err_mem_1:
+ release_mem_region(uart_res->start, SZ_16K);
+ }
+ return err;
+}
+
+/*!
+ * Dissociates the driver from the FIRI device. Removes the appropriate FIRI
+ * port structure from the kernel.
+ *
+ * @param pdev the device structure used to give information on which FIRI
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_irda_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct mxc_irda *si = netdev_priv(dev);
+
+ if (si->uart_base)
+ iounmap(si->uart_base);
+ if (si->firi_base)
+ iounmap(si->firi_base);
+ if (si->firi_res->start)
+ release_mem_region(si->firi_res->start, SZ_16K);
+ if (si->uart_res->start)
+ release_mem_region(si->uart_res->start, SZ_16K);
+ if (si->tx_buff.head)
+ kfree(si->tx_buff.head);
+ if (si->rx_buff.head)
+ kfree(si->rx_buff.head);
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_netdev(dev);
+ free_netdev(dev);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcir_driver = {
+ .driver = {
+ .name = "mxcir",
+ },
+ .probe = mxc_irda_probe,
+ .remove = mxc_irda_remove,
+ .suspend = mxc_irda_suspend,
+ .resume = mxc_irda_resume,
+};
+
+/*!
+ * This function is used to initialize the FIRI driver module. The function
+ * registers the power management callback functions with the kernel and also
+ * registers the FIRI callback functions.
+ *
+ * @return The function returns 0 on success and a non-zero value on failure.
+ */
+static int __init mxc_irda_init(void)
+{
+ return platform_driver_register(&mxcir_driver);
+}
+
+/*!
+ * This function is used to cleanup all resources before the driver exits.
+ */
+static void __exit mxc_irda_exit(void)
+{
+ platform_driver_unregister(&mxcir_driver);
+}
+
+module_init(mxc_irda_init);
+module_exit(mxc_irda_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("MXC IrDA(SIR/FIR) driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/irda/mxc_ir.h b/drivers/net/irda/mxc_ir.h
new file mode 100644
index 000000000000..6b22ca129f27
--- /dev/null
+++ b/drivers/net/irda/mxc_ir.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MXC_FIRI_REG_H__
+#define __MXC_FIRI_REG_H__
+
+#include <mach/hardware.h>
+
+/*!
+ * @defgroup FIRI Fast IR Driver
+ */
+
+/*!
+ * @file mxc_ir.h
+ *
+ * @brief MXC FIRI header file
+ *
+ * This file defines base address and bits of FIRI registers
+ *
+ * @ingroup FIRI
+ */
+
+/*!
+ * FIRI maximum packet length
+ */
+#define FIR_MAX_RXLEN 2047
+
+/*
+ * FIRI Transmitter Control Register
+ */
+#define FIRITCR 0x00
+/*
+ * FIRI Transmitter Count Register
+ */
+#define FIRITCTR 0x04
+/*
+ * FIRI Receiver Control Register
+ */
+#define FIRIRCR 0x08
+/*
+ * FIRI Transmitter Status Register
+ */
+#define FIRITSR 0x0C
+/*
+ * FIRI Receiver Status Register
+ */
+#define FIRIRSR 0x10
+/*
+ * FIRI Transmitter FIFO
+ */
+#define FIRITXFIFO 0x14
+/*
+ * FIRI Receiver FIFO
+ */
+#define FIRIRXFIFO 0x18
+/*
+ * FIRI Control Register
+ */
+#define FIRICR 0x1C
+
+/*
+ * Bit definitions of Transmitter Controller Register
+ */
+#define FIRITCR_HAG (1<<24) /* H/W address generator */
+#define FIRITCR_SRF_FIR (0<<13) /* Start field repeat factor */
+#define FIRITCR_SRF_MIR (1<<13) /* Start field Repeat Factor */
+#define FIRITCR_TDT_MIR (2<<10) /* TX trigger for MIR is set to 32 bytes) */
+#define FIRITCR_TDT_FIR (1<<10) /* TX trigger for FIR is set to 16 bytes) */
+#define FIRITCR_TCIE (1<<9) /* TX Complete Interrupt Enable */
+#define FIRITCR_TPEIE (1<<8) /* TX Packet End Interrupt Enable */
+#define FIRITCR_TFUIE (1<<7) /* TX FIFO Under-run Interrupt Enable */
+#define FIRITCR_PCF (1<<6) /* Packet Complete by FIFO */
+#define FIRITCR_PC (1<<5) /* Packet Complete */
+#define FIRITCR_SIP (1<<4) /* TX Enable of SIP */
+#define FIRITCR_TPP (1<<3) /* TX Pulse Polarity bit */
+#define FIRITCR_TM_FIR (0<<1) /* TX Mode 4 Mbps */
+#define FIRITCR_TM_MIR1 (1<<1) /* TX Mode 0.576 Mbps */
+#define FIRITCR_TM_MIR2 (1<<2) /* TX Mode 1.152 Mbps */
+#define FIRITCR_TE (1<<0) /* TX Enable */
+
+/*
+ * Bit definitions of Transmitter Count Register
+ */
+#define FIRITCTR_TPL 511 /* TX Packet Length set to 512 bytes */
+
+/*
+ * Bit definitions of Receiver Control Register
+ */
+#define FIRIRCR_RAM (1<<24) /* RX Address Match */
+#define FIRIRCR_RPEDE (1<<11) /* Packet End DMA request Enable */
+#define FIRIRCR_RDT_MIR (2<<8) /* DMA Trigger level(64 bytes in RXFIFO) */
+#define FIRIRCR_RDT_FIR (1<<8) /* DMA Trigger level(16 bytes in RXFIFO) */
+#define FIRIRCR_RPA (1<<7) /* RX Packet Abort */
+#define FIRIRCR_RPEIE (1<<6) /* RX Packet End Interrupt Enable */
+#define FIRIRCR_PAIE (1<<5) /* Packet Abort Interrupt Enable */
+#define FIRIRCR_RFOIE (1<<4) /* RX FIFO Overrun Interrupt Enable */
+#define FIRIRCR_RPP (1<<3) /* RX Pulse Polarity bit */
+#define FIRIRCR_RM_FIR (0<<1) /* 4 Mbps */
+#define FIRIRCR_RM_MIR1 (1<<1) /* 0.576 Mbps */
+#define FIRIRCR_RM_MIR2 (1<<2) /* 1.152 Mbps */
+#define FIRIRCR_RE (1<<0) /* RX Enable */
+
+/* Transmitter Status Register */
+#define FIRITSR_TFP 0xFF00 /* Mask for available bytes in TX FIFO */
+#define FIRITSR_TC (1<<3) /* Transmit Complete bit */
+#define FIRITSR_SIPE (1<<2) /* SIP End bit */
+#define FIRITSR_TPE (1<<1) /* Transmit Packet End */
+#define FIRITSR_TFU (1<<0) /* TX FIFO Under-run */
+
+/* Receiver Status Register */
+#define FIRIRSR_RFP 0xFF00 /* mask for available bytes RX FIFO */
+#define FIRIRSR_PAS (1<<5) /* preamble search */
+#define FIRIRSR_RPE (1<<4) /* RX Packet End */
+#define FIRIRSR_RFO (1<<3) /* RX FIFO Overrun */
+#define FIRIRSR_BAM (1<<2) /* Broadcast Address Match */
+#define FIRIRSR_CRCE (1<<1) /* CRC error */
+#define FIRIRSR_DDE (1<<0) /* Address, control or data field error */
+
+/* FIRI Control Register */
+#define FIRICR_BL (32<<5) /* Burst Length is set to 32 */
+#define FIRICR_OSF (0<<1) /* Over Sampling Factor */
+
+#endif /* __MXC_FIRI_REG_H__ */
diff --git a/drivers/net/ns9xxx-eth.c b/drivers/net/ns9xxx-eth.c
new file mode 100755
index 000000000000..4fc84c7a6d41
--- /dev/null
+++ b/drivers/net/ns9xxx-eth.c
@@ -0,0 +1,1499 @@
+/*
+ * drivers/net/ns9xxx-eth.c
+ *
+ * Copyright (C) 2007,2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/* open issues
+ * - use phy irq
+ * - VLAN
+ * - clk_enable only at open time?
+ * - PM for hibernate
+ */
+#include <linux/clk.h>
+#include <linux/crc32.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/ns9xxx-eth.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/timer.h>
+#include <asm/gpio.h>
+
+#define DRIVER_NAME "ns9xxx-eth"
+
+#define ETH_EGCR1 0x0000
+#define ETH_EGCR1_ERX (1 << 31)
+#define ETH_EGCR1_ERXDMA (1 << 30)
+#define ETH_EGCR1_ERXSHT (1 << 28)
+#define ETH_EGCR1_ETX (1 << 23)
+#define ETH_EGCR1_ETXDMA (1 << 22)
+#define ETH_EGCR1_ERXINIT (1 << 19)
+#define ETH_EGCR1_PM (3 << 14)
+#define ETH_EGCR1_PM_MII 0
+#define ETH_EGCR1_MACHRST (1 << 9)
+#define ETH_EGCR1_ITXA (1 << 8)
+#define ETH_EGCR1_MB1 (1 << 21)
+
+#define ETH_EGCR2 0x0004
+/* TCLER is different comparing the ns9360 vs ns921x */
+/* FIXME TCLER should be used differently per processor */
+#define ETH_EGCR2_TCLER_NS921X (1 << 7)
+#define ETH_EGCR2_TCLER_NS9360 (1 << 3)
+#define ETH_EGCR2_TCLER (1 << 7)
+#define ETH_EGCR2_TKICK (1 << 3)
+#define ETH_EGCR2_AUTOZ (1 << 2)
+#define ETH_EGCR2_CLRCNT (1 << 1)
+#define ETH_EGCR2_STEN (1 << 0)
+
+#define ETH_EGSR 0x0008
+#define ETH_EGSR_RXINIT (1 << 20)
+
+#define ETH_TS 0x0018
+#define ETH_TS_OK ( 1 << 15)
+#define ETH_TS_BR ( 1 << 14)
+#define ETH_TS_MC ( 1 << 13)
+#define ETH_TS_AL ( 1 << 12)
+#define ETH_TS_AED ( 1 << 11)
+#define ETH_TS_AEC ( 1 << 10)
+#define ETH_TS_AUR ( 1 << 9)
+#define ETH_TS_AJ ( 1 << 8)
+#define ETH_TS_DEF ( 1 << 6)
+#define ETH_TS_CRC ( 1 << 5)
+#define ETH_TS_COLC (15 << 0)
+
+#define ETH_MAC1 0x0400
+#define ETH_MAC1_SRST (1 << 15)
+#define ETH_MAC1_RXEN (1 << 0)
+
+#define ETH_MAC2 0x0404
+#define ETH_MAC2_PADEN (1 << 5)
+#define ETH_MAC2_CRCEN (1 << 4)
+#define ETH_MAC2_FULLD (1 << 0)
+
+#define ETH_B2BIPG 0x0408
+
+#define ETH_EPSR 0x0418
+#define ETH_EPSR_SPEED_MASK (1 << 8)
+#define ETH_EPSR_SPEED_100 (1 << 8)
+#define ETH_EPSR_SPEED_10 (0 << 8)
+
+
+#define ETH_MIIMCFG 0x0420
+#define ETH_MIIMCFG_RMIIM (1 << 15)
+#define ETH_MIIMCFG_CLKS (7 << 2)
+#define ETH_MIIMCFG_CLKS_DIV40 (7 << 2)
+
+#define ETH_MIIMCMD 0x0424
+#define ETH_MIIMCMD_READ (1 << 0)
+#define ETH_MIIMADDR 0x0428
+#define ETH_MIIMWD 0x042c
+#define ETH_MIIMRD 0x0430
+#define ETH_MIIMIR 0x0434
+#define ETH_MIIMIR_LF (1 << 3)
+#define ETH_MIIMIR_BUSY (1 << 0)
+
+#define ETH_SA1 0x0440
+#define ETH_SA2 0x0444
+#define ETH_SA3 0x0448
+
+#define ETH_SAF 0x0500
+#define ETH_SAF_PRO (1 << 3)
+#define ETH_SAF_PRM (1 << 2)
+#define ETH_SAF_PRA (1 << 1)
+#define ETH_SAF_BROAD (1 << 0)
+
+#define ETH_HT1 0x0504
+#define ETH_HT2 0x0508
+
+#define ETH_STAT_TR64 0x0680
+#define ETH_STAT_TR127 0x0684
+#define ETH_STAT_TR255 0x0688
+#define ETH_STAT_TR511 0x068C
+#define ETH_STAT_TR1K 0x0690
+#define ETH_STAT_TRMAX 0x0694
+#define ETH_STAT_TRMGV 0x0698
+#define ETH_STAT_RBYT 0x069C
+#define ETH_STAT_RPKT 0x06A0
+#define ETH_STAT_RFCS 0x06A4
+#define ETH_STAT_RMCA 0x06A8
+#define ETH_STAT_RBCA 0x06AC
+#define ETH_STAT_RXCF 0x06B0
+#define ETH_STAT_RXPF 0x06B4
+#define ETH_STAT_RXUO 0x06B8
+#define ETH_STAT_RALN 0x06BC
+#define ETH_STAT_RFLR 0x06C0
+#define ETH_STAT_RCDE 0x06C4
+#define ETH_STAT_RCSE 0x06C8
+#define ETH_STAT_RUND 0x06CC
+#define ETH_STAT_ROVR 0x06D0
+#define ETH_STAT_RFRG 0x06D4
+#define ETH_STAT_RJBR 0x06D8
+#define ETH_STAT_TBYT 0x06E0
+#define ETH_STAT_TPKT 0x06E4
+#define ETH_STAT_TMCA 0x06E8
+#define ETH_STAT_TBCA 0x06EC
+#define ETH_STAT_TDFR 0x06F4
+#define ETH_STAT_TEDF 0x06F8
+#define ETH_STAT_TSCL 0x06FC
+#define ETH_STAT_TMCL 0x0700
+#define ETH_STAT_TLCL 0x0704
+#define ETH_STAT_TXCL 0x0708
+#define ETH_STAT_TNCL 0x070C
+#define ETH_STAT_TJBR 0x0718
+#define ETH_STAT_TFCS 0x071C
+#define ETH_STAT_TOVR 0x0724
+#define ETH_STAT_TUND 0x0728
+#define ETH_STAT_TFRG 0x072C
+
+#define ETH_RXABDP 0x0a00
+#define ETH_RXBBDP 0x0a04
+#define ETH_RXCBDP 0x0a08
+#define ETH_RXDBDP 0x0a0c
+
+#define ETH_IS 0x0a10
+#define ETH_IS_RXOVFLDATA (1 << 25)
+#define ETH_IS_RXOVFLSTAT (1 << 24)
+#define ETH_IS_RXDONEA (1 << 22)
+#define ETH_IS_RXDONEB (1 << 21)
+#define ETH_IS_RXDONEC (1 << 20)
+#define ETH_IS_RXDONED (1 << 19)
+#define ETH_IS_RXNOBUF (1 << 18)
+#define ETH_IS_RXBUFFUL (1 << 17)
+#define ETH_IS_RXBR (1 << 16)
+#define ETH_IS_TXDONE (1 << 2)
+#define ETH_IS_TXERR (1 << 1)
+#define ETH_IS_TXIDLE (1 << 0)
+
+#define ETH_IE 0x0a14
+#define ETH_IE_RXOVFLDATA (1 << 25)
+#define ETH_IE_RXOVFLSTAT (1 << 24)
+#define ETH_IE_RXDONEA (1 << 22)
+#define ETH_IE_RXDONEB (1 << 21)
+#define ETH_IE_RXDONEC (1 << 20)
+#define ETH_IE_RXDONED (1 << 19)
+#define ETH_IE_RXNOBUF (1 << 18)
+#define ETH_IE_RXBUFFUL (1 << 17)
+#define ETH_IE_RXBR (1 << 16)
+#define ETH_IE_TXDONE (1 << 2)
+#define ETH_IE_TXERR (1 << 1)
+#define ETH_IE_TXIDLE (1 << 0)
+
+#define ETH_RX_IRQS (ETH_IE_RXOVFLDATA | \
+ ETH_IE_RXDONEA | \
+ ETH_IE_RXDONEB | \
+ ETH_IE_RXDONEC | \
+ ETH_IE_RXDONED | \
+ ETH_IE_RXNOBUF | \
+ ETH_IE_RXBUFFUL)
+
+#define ETH_TX_IRQS (ETH_IE_TXDONE | ETH_IE_TXERR)
+
+#define ETH_TXBDP 0x0a18
+
+#define ETH_TRBDP 0x0a1c
+
+#define ETH_RXFREE 0x0a3c
+#define ETH_RXFREE_A (1 << 0)
+
+#define ETH_TXBDR 0x1000
+
+/* hardware limits sets from ethernet controller */
+#define HW_RX_RINGS (4)
+#define MAX_ETH_FRAME_LEN (1522)
+
+/* software limits sets by driver */
+#define TOTAL_NR_TXDESC (64)
+#define NR_RXDESC_PER_RING (45)
+#define TOTAL_NR_RXDESC (HW_RX_RINGS*NR_RXDESC_PER_RING) /* total number of descriptors */
+
+/* masks for DMA descriptors handling */
+#define DMADESC_WRAP (1 << 15)
+#define DMADESC_INTR (1 << 14)
+#define DMATXDESC_LAST (1 << 13)
+#define DMARXDESC_EN (1 << 13)
+#define DMADESC_FULL (1 << 12)
+
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ #define ACTIVITYLED_TOGGLE_TIMEOUT 2 /* in jiffies */
+ #define ACTIVITYLED_OFF_TIMEOUT 20 /* in jiffies */
+ unsigned int rxtx_activity = 0;
+ struct timer_list activityled_timer;
+#endif
+
+union ns9xxx_dma_desc {
+ struct {
+ u32 source;
+ u16 len;
+ u16 reserved;
+ u32 dest;
+ u16 status;
+ u16 flags;
+ };
+ u32 data[4];
+};
+
+struct ns9xxx_eth_priv {
+ unsigned char __iomem *membase;
+ resource_size_t mapbase;
+ spinlock_t lock;
+ struct net_device_stats stats;
+ struct clk *clk;
+ unsigned int irqtx;
+ unsigned int irqrx;
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ unsigned int activityled;
+#endif
+
+ /* phy management */
+ int lastlink;
+ int lastduplex;
+ int lastspeed;
+ struct mii_bus *mdiobus;
+ struct phy_device *phy;
+
+ /* rx stuff */
+ struct sk_buff **rxskb;
+ union ns9xxx_dma_desc *rxdesc;
+ dma_addr_t rxdeschandle;
+
+ /* tx stuff */
+ struct sk_buff **txskb;
+ u16 txbusy;
+ u16 txfree;
+
+ /* work to recover from rx stall condition that can happen at 100 Mbps HD */
+ struct delayed_work recover_from_rx_stall;
+};
+
+static inline u32 ethread32(struct net_device *dev, unsigned int offset)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ u32 ret = ioread32(priv->membase + offset);
+
+ dev_vdbg(&dev->dev, "read 0x%p -> 0x%08x\n",
+ priv->membase + offset, ret);
+
+ return ret;
+}
+
+static inline void ethwrite32(struct net_device *dev,
+ u32 value, unsigned int offset)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ dev_vdbg(&dev->dev, "write 0x%p <- 0x%08x\n",
+ priv->membase + offset, value);
+ iowrite32(value, priv->membase + offset);
+}
+
+static inline void ethupdate32(struct net_device *dev,
+ u32 and, u32 or, unsigned int offset)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ u32 reg;
+
+ reg = ioread32(priv->membase + offset) & and;
+ iowrite32(reg | or, priv->membase + offset);
+ dev_vdbg(&dev->dev, "update 0x%p <- 0x%08x\n",
+ priv->membase + offset, reg | or);
+}
+
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+static void toggle_activityled(void)
+{
+ /* set activity flag */
+ rxtx_activity = 1;
+ /* run timer if not already running */
+ if(!timer_pending(&activityled_timer))
+ mod_timer(&activityled_timer, jiffies + ACTIVITYLED_TOGGLE_TIMEOUT);
+}
+
+static void activityled_timer_fn(unsigned long gpio)
+{
+ static int cnt = 0;
+
+ if(rxtx_activity) {
+ /* toggle RX/TX Ethernet activity LED */
+ gpio_set_value(gpio, !gpio_get_value(gpio));
+ mod_timer(&activityled_timer, jiffies + ACTIVITYLED_TOGGLE_TIMEOUT);
+ cnt = 0;
+ rxtx_activity = 0;
+ } else {
+ if(cnt++ < ACTIVITYLED_OFF_TIMEOUT / ACTIVITYLED_TOGGLE_TIMEOUT)
+ mod_timer(&activityled_timer, jiffies + ACTIVITYLED_TOGGLE_TIMEOUT);
+ else {
+ gpio_set_value(gpio, 1); /* switch LED off */
+ cnt = 0;
+ }
+ }
+
+}
+#endif
+
+static int ns9xxx_eth_miibus_pollbusy(struct mii_bus *bus)
+{
+ unsigned int timeout = 0x3000;
+ struct net_device *dev = bus->priv;
+
+ while (timeout--) {
+ u32 miimir;
+
+ miimir = ethread32(dev, ETH_MIIMIR);
+
+ if (!(miimir & ETH_MIIMIR_BUSY))
+ break;
+
+ cpu_relax();
+ }
+
+ return timeout ? 0 : -EBUSY;
+}
+
+static int ns9xxx_eth_mdiobus_read(struct mii_bus *bus, int phyid, int regnum)
+{
+ struct net_device *dev = bus->priv;
+ int ret;
+
+ if ((phyid & ~0x1f) || (regnum & ~0x1f))
+ return -EINVAL;
+
+ ret = ns9xxx_eth_miibus_pollbusy(bus);
+ if (ret)
+ goto out;
+
+ ethwrite32(dev, phyid << 8 | regnum, ETH_MIIMADDR);
+ ethwrite32(dev, 0, ETH_MIIMCMD);
+ ethwrite32(dev, ETH_MIIMCMD_READ, ETH_MIIMCMD);
+
+ ret = ns9xxx_eth_miibus_pollbusy(bus);
+ if (ret)
+ goto out;
+
+ ret = ethread32(dev, ETH_MIIMRD) & 0xffff;
+
+out:
+ dev_vdbg(bus->parent, "%s, phyid = %d, regnum = %d -> %04x\n",
+ __func__, phyid, regnum, ret);
+
+ return ret;
+}
+
+static int ns9xxx_eth_mdiobus_write(struct mii_bus *bus,
+ int phyid, int regnum, u16 val)
+{
+ struct net_device *dev = bus->priv;
+ int ret;
+
+ if ((phyid & ~0x1f) || (regnum & ~0x1f))
+ return -EINVAL;
+
+ ret = ns9xxx_eth_miibus_pollbusy(bus);
+ if (ret)
+ goto out;
+
+ ethwrite32(dev, phyid << 8 | regnum, ETH_MIIMADDR);
+ ethwrite32(dev, val, ETH_MIIMWD);
+
+ ret = ns9xxx_eth_miibus_pollbusy(bus);
+
+out:
+ dev_vdbg(bus->parent, "%s: phyid = %d, regnum = %d, val = %04hx -> %04x\n",
+ __func__, phyid, regnum, val, ret);
+
+ return ret;
+}
+
+static int ns9xxx_eth_mdiobus_reset(struct mii_bus *bus)
+{
+ struct net_device *dev = bus->priv;
+
+ dev_dbg(bus->parent, "%s\n", __func__);
+
+ ethwrite32(dev, ETH_MIIMCFG_RMIIM, ETH_MIIMCFG);
+
+ /* TODO: currently the biggest divider (40) is used. This could be
+ * tuned depending on the PHY. phylib doesn't provide the needed
+ * information, though :-( */
+ ethwrite32(dev, ETH_MIIMCFG_CLKS_DIV40, ETH_MIIMCFG);
+
+ return 0;
+}
+
+static inline int ns9xxx_eth_create_skbuff(struct net_device* dev, const int descr)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ struct sk_buff *skb;
+ int ret;
+
+ skb = dev_alloc_skb(MAX_ETH_FRAME_LEN);
+ if (likely(skb)) {
+ priv->rxskb[descr] = skb;
+ skb->dev = dev;
+
+ priv->rxdesc[descr].source = dma_map_single(&dev->dev, skb->data,
+ skb->len, DMA_FROM_DEVICE);
+
+ priv->rxdesc[descr].len = MAX_ETH_FRAME_LEN;
+ priv->rxdesc[descr].flags = DMADESC_INTR | DMARXDESC_EN |
+ (descr == (NR_RXDESC_PER_RING - 1) ? DMADESC_WRAP : 0);
+ ret = 0;
+ } else {
+ printk(KERN_ERR "%s: out of memory\n", __func__);
+ priv->rxdesc[descr].flags = 0;
+ ret = -ENOMEM;
+ }
+
+ return ret;
+}
+
+static inline void ns9xxx_eth_rx_process_ring(struct net_device* dev, unsigned int ring)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ union ns9xxx_dma_desc *desc;
+ unsigned int i, endring;
+
+ endring = (ring + 1) * NR_RXDESC_PER_RING;
+ desc = &priv->rxdesc[ring * NR_RXDESC_PER_RING];
+
+ for (i = ring * NR_RXDESC_PER_RING; i < endring; i++, desc++) {
+ if (desc->flags & DMADESC_FULL) {
+ struct sk_buff *skb;
+ skb = dev_alloc_skb(MAX_ETH_FRAME_LEN);
+ if (likely(skb)) {
+ skb_reserve(skb, 2); /* 16 byte IP header align */
+ memcpy(skb_put(skb, desc->len), (unsigned char *)priv->rxskb[i]->data,
+ desc->len);
+ skb->protocol = eth_type_trans(skb, dev);
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += desc->len;
+ dev->last_rx = jiffies;
+ netif_rx(skb);
+ } else {
+ printk(KERN_ERR "%s: out of memory, dropping packet\n", __func__);
+ dev->stats.rx_dropped++;
+ }
+ desc->len = MAX_ETH_FRAME_LEN;
+ desc->flags = DMADESC_INTR | DMARXDESC_EN | (i == (NR_RXDESC_PER_RING - 1) ? DMADESC_WRAP : 0);
+ }
+ }
+}
+
+static irqreturn_t ns9xxx_eth_rx_int(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+ u32 is, rxdonemask, ring;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ is = ethread32(dev, ETH_IS);
+ /* Acknowledge interrupts */
+ ethwrite32(dev, is & ETH_RX_IRQS, ETH_IS);
+ dev_vdbg(&dev->dev, "%s: ETH_IS=%08x\n", __func__, is);
+
+ if (is & ETH_IS_RXOVFLDATA) {
+ if (!(is & (ETH_IS_RXDONEA | ETH_IS_RXDONEB |
+ ETH_IS_RXDONEC | ETH_IS_RXDONEA))) {
+ /* The ETH_IS_RXOVFLDATA bit is set, then the receiver front
+ * end has apparently locked up. We schedule a work that resets
+ * the interface. We check the DONE bits to try to empty the
+ * receive rings of packets before the receiver reset. Note
+ * that once we get into this lockup state, the ETH_IS_RXOVFLDATA
+ * interrupt will happen continously until we reset the receiver.
+ */
+ ethupdate32(dev, ~(ETH_EGCR1_ERX | ETH_EGCR1_ERXDMA), 0, ETH_EGCR1);
+ ethupdate32(dev, ~ETH_MAC1_RXEN, 0, ETH_MAC1);
+ ethupdate32(dev, ~ETH_IE_RXOVFLDATA, 0, ETH_IE);
+
+ schedule_delayed_work(&priv->recover_from_rx_stall, 0);
+ }
+ }
+
+ if (is & (ETH_IS_RXNOBUF | ETH_IS_RXBUFFUL)) {
+ priv->stats.rx_dropped++;
+ }
+
+ for (ring = 0, rxdonemask = ETH_IS_RXDONEA; ring < HW_RX_RINGS; ring++) {
+ if (is & rxdonemask) {
+ ns9xxx_eth_rx_process_ring(dev, ring);
+ ethwrite32(dev, 1 << ring, ETH_RXFREE);
+ }
+ rxdonemask >>= 1;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ toggle_activityled();
+#endif
+ return IRQ_HANDLED;
+}
+
+static void ns9xxx_eth_recover_from_rx_stall(struct work_struct *work)
+{
+ struct ns9xxx_eth_priv *priv =
+ container_of(work, struct ns9xxx_eth_priv,
+ recover_from_rx_stall.work);
+
+ struct net_device *dev =
+ container_of((void *)priv, struct net_device, priv);
+ unsigned long flags;
+ int i, timeout = 20;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ for (i = 0; i < HW_RX_RINGS; i++) {
+ ethwrite32(dev, priv->rxdeschandle + (i * NR_RXDESC_PER_RING),
+ ETH_RXABDP + (i * 4));
+ }
+
+ ethupdate32(dev, 0xffffffff, ETH_EGSR_RXINIT, ETH_EGSR);
+ ethupdate32(dev, 0xffffffff, ETH_EGCR1_ERX, ETH_EGCR1);
+ ethupdate32(dev, 0xffffffff, ETH_EGCR1_ERXINIT, ETH_EGCR1);
+
+ while (!(ethread32(dev, ETH_EGSR) & ETH_EGSR_RXINIT) && timeout--)
+ udelay(1);
+
+ ethupdate32(dev, ~ETH_EGCR1_ERXINIT, 0, ETH_EGCR1);
+
+ /* Re-enable the overflow interrupt */
+ ethupdate32(dev, 0xffffffff, ETH_IE_RXOVFLDATA, ETH_IE);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static inline int ns9xxx_eth_num_txbusy(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ return (TOTAL_NR_TXDESC + priv->txfree - priv->txbusy) %
+ TOTAL_NR_TXDESC;
+}
+
+static inline int ns9xxx_eth_num_txfree(struct net_device *dev)
+{
+ return TOTAL_NR_TXDESC - ns9xxx_eth_num_txbusy(dev);
+}
+
+static void ns9xxx_eth_read_txdesc(struct net_device *dev,
+ union ns9xxx_dma_desc *txbuffer)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ int i;
+
+ for (i = 0; i < 4; ++i)
+ txbuffer->data[i] = ethread32(dev,
+ ETH_TXBDR + 16 * priv->txbusy + 4 * i);
+}
+
+static void ns9xxx_eth_start_tx_dma(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ u32 egcr2, ie;
+ u32 start;
+
+ dev_vdbg(&dev->dev, "%s\n", __func__);
+
+ dev->trans_start = jiffies;
+
+ /* XXX: really kick TCLER? */
+
+ egcr2 = ethread32(dev, ETH_EGCR2);
+ if (egcr2 & (ETH_EGCR2_TCLER | ETH_EGCR2_TKICK)) {
+ egcr2 &= ~(ETH_EGCR2_TCLER | ETH_EGCR2_TKICK);
+ ethwrite32(dev, egcr2, ETH_EGCR2);
+ }
+
+ start = 4 * priv->txbusy;
+
+ ie = ethread32(dev, ETH_IE);
+ if ((ie & ETH_TX_IRQS) == 0) {
+ u32 egcr1 = ethread32(dev, ETH_EGCR1);
+ ethwrite32(dev, start, ETH_TXBDP);
+
+ ethwrite32(dev, ie | ETH_TX_IRQS, ETH_IE);
+ ethwrite32(dev, egcr1 | ETH_EGCR1_ETXDMA, ETH_EGCR1);
+ } else
+ ethwrite32(dev, start, ETH_TRBDP);
+
+ egcr2 |= ETH_EGCR2_TCLER | ETH_EGCR2_TKICK;
+ ethwrite32(dev, egcr2, ETH_EGCR2);
+}
+
+static irqreturn_t ns9xxx_eth_tx_int(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ u32 is;
+ struct sk_buff *skb;
+ union ns9xxx_dma_desc txbuffer;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ is = ethread32(dev, ETH_IS);
+ dev_vdbg(&dev->dev, "%s: ETH_IS=%08x\n", __func__, is);
+
+ /* ack */
+ ethwrite32(dev, is & (ETH_IS_TXDONE | ETH_IS_TXERR), ETH_IS);
+
+ while (1) {
+ if (!ns9xxx_eth_num_txbusy(dev))
+ break;
+
+ skb = priv->txskb[priv->txbusy];
+
+ ns9xxx_eth_read_txdesc(dev, &txbuffer);
+
+ if (txbuffer.flags & DMADESC_FULL)
+ break;
+
+ dma_unmap_single(&dev->dev, txbuffer.source,
+ skb->len, DMA_TO_DEVICE);
+
+ priv->txbusy = (priv->txbusy + 1) % TOTAL_NR_TXDESC;
+
+ if (txbuffer.status & ETH_TS_OK) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len;
+ } else {
+ priv->stats.tx_errors++;
+
+ /* XXX: fill in tx_aborted_errors etc. */
+ /* kick TCLER? */
+ }
+
+ dev_kfree_skb_irq(skb);
+ }
+
+ if (ns9xxx_eth_num_txbusy(dev) &&
+ (is & ETH_IS_TXIDLE))
+ ns9xxx_eth_start_tx_dma(dev);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ toggle_activityled();
+#endif
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void ns9xxx_eth_netpoll(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ ns9xxx_eth_rx_int(priv->irqrx, dev);
+ ns9xxx_eth_tx_int(priv->irqtx, dev);
+ local_irq_restore(flags);
+}
+#endif
+
+static void ns9xxx_eth_adjust_link(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phy;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if ((phydev->link != priv->lastlink) ||
+ (priv->lastspeed != phydev->speed) ||
+ (priv->lastduplex != phydev->duplex)){
+
+ if (phydev->link) {
+ u32 mac2 = ethread32(dev, ETH_MAC2);
+ u32 epsr = ethread32(dev, ETH_EPSR);
+
+ /* Adjsut speed */
+ epsr &= ~ETH_EPSR_SPEED_MASK;
+ epsr |= (phydev->speed == SPEED_100) ?
+ ETH_EPSR_SPEED_100 : 0;
+ ethwrite32(dev, epsr, ETH_EPSR);
+ priv->lastspeed = phydev->speed;
+
+ /* Adjsut duplex */
+ mac2 &= ~ETH_MAC2_FULLD;
+ mac2 |= phydev->duplex ? ETH_MAC2_FULLD : 0;
+ ethwrite32(dev, mac2, ETH_MAC2);
+ ethwrite32(dev, phydev->duplex ? 0x15 : 0x12,
+ ETH_B2BIPG);
+ priv->lastduplex = phydev->duplex;
+
+ dev_info(&dev->dev, "link up (%d/%s)\n", phydev->speed,
+ (phydev->duplex == DUPLEX_FULL) ?
+ "full" : "half");
+ } else {
+ /* link down */
+ priv->lastspeed = 0;
+ priv->lastduplex = -1;
+ dev_info(&dev->dev, "link down\n");
+ }
+ priv->lastlink = phydev->link;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int ns9xxx_eth_hwinit(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ u32 egcr1, ie;
+ int timeout, i;
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ /* disable everything */
+ ethwrite32(dev, ETH_EGCR1_PM_MII, ETH_EGCR1);
+ ethwrite32(dev, 0, ETH_IE);
+
+ /* ack any pending irq */
+ ethwrite32(dev, ethread32(dev, ETH_IS), ETH_IS);
+
+ /* program station address */
+ ethwrite32(dev, dev->dev_addr[0] | dev->dev_addr[1] << 8, ETH_SA3);
+ ethwrite32(dev, dev->dev_addr[2] | dev->dev_addr[3] << 8, ETH_SA2);
+ ethwrite32(dev, dev->dev_addr[4] | dev->dev_addr[5] << 8, ETH_SA1);
+
+ for (i = 0; i < HW_RX_RINGS; i++) {
+ ethwrite32(dev, priv->rxdeschandle + (i * NR_RXDESC_PER_RING),
+ ETH_RXABDP + (i * 4));
+ }
+
+ ethwrite32(dev, ETH_SAF_BROAD, ETH_SAF);
+
+ egcr1 = ETH_EGCR1_ERX | ETH_EGCR1_ERXDMA | ETH_EGCR1_ETX |
+ ETH_EGCR1_PM_MII | ETH_EGCR1_ITXA | ETH_EGCR1_MB1;
+ ethwrite32(dev, egcr1 | ETH_EGCR1_ERXINIT, ETH_EGCR1);
+
+ timeout = 6;
+ while (!(ethread32(dev, ETH_EGSR) & ETH_EGSR_RXINIT) && timeout--)
+ udelay(1);
+
+ if (!timeout)
+ return -EBUSY;
+
+ ethwrite32(dev, ETH_EGSR_RXINIT, ETH_EGSR);
+ ethwrite32(dev, egcr1, ETH_EGCR1);
+
+ ethwrite32(dev, ETH_MAC1_RXEN, ETH_MAC1);
+ ethwrite32(dev, ETH_MAC2_CRCEN | ETH_MAC2_PADEN, ETH_MAC2);
+ ethwrite32(dev, 0x12, ETH_B2BIPG);
+
+ /* clear and enable statistics */
+ ethupdate32(dev, 0xffffffff, ETH_EGCR2_CLRCNT, ETH_EGCR2);
+ ethupdate32(dev, ~ETH_EGCR2_CLRCNT, 0, ETH_EGCR2);
+ ethupdate32(dev, 0xffffffff, ETH_EGCR2_AUTOZ | ETH_EGCR2_STEN, ETH_EGCR2);
+
+ ie = ethread32(dev, ETH_IE);
+ ethwrite32(dev, ie | ETH_RX_IRQS, ETH_IE);
+
+ ethwrite32(dev, 0xf, ETH_RXFREE);
+ return 0;
+}
+
+static void ns9xxx_eth_hwdisable(struct net_device *dev)
+{
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ ethwrite32(dev, 0, ETH_EGCR1);
+ ethwrite32(dev, 0, ETH_IE);
+ ethwrite32(dev, 0, ETH_MAC1);
+ ethwrite32(dev, 0, ETH_MAC2);
+}
+
+static int ns9xxx_eth_open(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ struct resource *res;
+ int ret = -ENOMEM;
+ int i;
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ res = request_mem_region(priv->mapbase, 0x2800, DRIVER_NAME);
+ if (!res) {
+ dev_dbg(&dev->dev, "%s: err_request_mem\n", __func__);
+ goto err_request_mem;
+ }
+
+ phy_start(priv->phy);
+
+ priv->txfree = 1;
+ priv->txbusy = 1;
+
+ priv->rxdesc = dma_alloc_coherent(&dev->dev,
+ sizeof(*priv->rxdesc) * TOTAL_NR_RXDESC,
+ &priv->rxdeschandle, GFP_KERNEL);
+ if (!priv->rxdesc) {
+ dev_dbg(&dev->dev, "%s: err_alloc_rxdesc\n", __func__);
+ ret = -ENOMEM;
+ goto err_alloc_rxdesc;
+ }
+
+ priv->rxskb = kmalloc(sizeof(*priv->rxskb) * TOTAL_NR_RXDESC,
+ GFP_KERNEL);
+ if (!priv->rxskb) {
+ dev_dbg(&dev->dev, "%s: err_alloc_rxskb\n", __func__);
+ goto err_alloc_rxskb;
+ }
+
+ for (i = 0; i < TOTAL_NR_RXDESC; ++i) {
+ ret = ns9xxx_eth_create_skbuff(dev, i);
+
+ if (ret) {
+ dev_dbg(&dev->dev, "%s: err_setup_rxskb (i = %d)\n",
+ __func__, i);
+ goto err_setup_rxskb;
+ }
+ }
+
+ priv->txskb = kmalloc(sizeof(*priv->txskb) * TOTAL_NR_TXDESC,
+ GFP_KERNEL);
+ if (!priv->txskb) {
+ dev_dbg(&dev->dev, "%s: err_alloc_txskb\n", __func__);
+ goto err_alloc_txskb;
+ }
+
+ ret = ns9xxx_eth_hwinit(dev);
+ if (ret) {
+ dev_dbg(&dev->dev, "%s: err_hwinit -> %d\n", __func__, ret);
+ goto err_hwinit;
+ }
+
+ ret = request_irq(priv->irqtx, ns9xxx_eth_tx_int, 0, DRIVER_NAME, dev);
+ if (ret) {
+ dev_dbg(&dev->dev, "%s: err_request_irq_tx -> %d\n",
+ __func__, ret);
+ goto err_request_irq_tx;
+ }
+
+ ret = request_irq(priv->irqrx, ns9xxx_eth_rx_int, 0, DRIVER_NAME, dev);
+ if (ret) {
+ dev_dbg(&dev->dev, "%s: err_request_irq_rx -> %d\n",
+ __func__, ret);
+
+ free_irq(priv->irqtx, dev);
+err_request_irq_tx:
+
+ ns9xxx_eth_hwdisable(dev);
+err_hwinit:
+
+err_setup_rxskb:
+ for (i = 0; priv->rxskb[i]; ++i) {
+ dma_unmap_single(&dev->dev, priv->rxdesc[i].source,
+ priv->rxskb[i]->len, DMA_FROM_DEVICE);
+ kfree_skb(priv->rxskb[i]);
+ }
+
+ kfree(priv->txskb);
+err_alloc_txskb:
+
+ kfree(priv->rxskb);
+err_alloc_rxskb:
+
+ dma_free_coherent(&dev->dev,
+ sizeof(*priv->rxdesc) * TOTAL_NR_RXDESC,
+ priv->rxdesc, priv->rxdeschandle);
+err_alloc_rxdesc:
+
+ release_mem_region(priv->mapbase, 0x2800);
+err_request_mem:
+
+ return ret;
+ }
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int ns9xxx_eth_stop(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ free_irq(priv->irqrx, dev);
+ free_irq(priv->irqtx, dev);
+
+ cancel_delayed_work(&priv->recover_from_rx_stall);
+
+ ns9xxx_eth_hwdisable(dev);
+ kfree(priv->txskb);
+ kfree(priv->rxskb);
+ dma_free_coherent(&dev->dev,
+ sizeof(*priv->rxdesc) * TOTAL_NR_RXDESC,
+ priv->rxdesc, priv->rxdeschandle);
+
+ release_mem_region(priv->mapbase, 0x2800);
+
+ return 0;
+}
+
+static int ns9xxx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ unsigned long flags;
+ int ret;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!priv->phy)
+ return -EINVAL;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ ret = phy_mii_ioctl(priv->phy, if_mii(ifr), cmd);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static void ns9xxx_eth_add_txdesc(struct net_device *dev,
+ union ns9xxx_dma_desc *txbuffer)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ int i;
+
+ dev_vdbg(&dev->dev, "%s: txfree=%hu, txbusy=%hu\n",
+ __func__, priv->txfree, priv->txbusy);
+
+ for (i = 0; i < 4; ++i)
+ ethwrite32(dev, txbuffer->data[i],
+ ETH_TXBDR + 16 * priv->txfree + 4 * i);
+
+ priv->txfree = (priv->txfree + 1) % TOTAL_NR_TXDESC;
+}
+
+static int ns9xxx_eth_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ union ns9xxx_dma_desc txbuffer = {};
+ unsigned long flags;
+ int ret;
+
+ /* do I need to lock already here? */
+ spin_lock_irqsave(&priv->lock, flags);
+
+ dev_vdbg(&dev->dev, "%s(skb=%p): skb->data=%p\n",
+ __func__, skb, skb->data);
+
+ if (likely(ns9xxx_eth_num_txfree(dev) > 0)) {
+ txbuffer.source = dma_map_single(&dev->dev, skb->data,
+ skb->len, DMA_TO_DEVICE);
+ txbuffer.len = skb->len;
+ txbuffer.flags = DMATXDESC_LAST | DMADESC_FULL;
+
+ if (unlikely(priv->txfree == TOTAL_NR_TXDESC - 1))
+ txbuffer.flags |= DMADESC_WRAP;
+
+ priv->txskb[priv->txfree] = skb;
+ ns9xxx_eth_add_txdesc(dev, &txbuffer);
+
+ if (ns9xxx_eth_num_txbusy(dev) == 1)
+ ns9xxx_eth_start_tx_dma(dev);
+
+ ret = NETDEV_TX_OK;
+ } else {
+ netif_stop_queue(dev);
+ ret = NETDEV_TX_BUSY;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static void ns9xxx_eth_tx_timeout(struct net_device *dev)
+{
+ /* XXX */
+ dev_warn(&dev->dev, "%s unimplemented\n", __func__);
+}
+
+static struct net_device_stats *ns9xxx_eth_get_stats(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ priv->stats.rx_length_errors += ethread32(dev, ETH_STAT_RFLR);
+ priv->stats.rx_over_errors += ethread32(dev, ETH_STAT_ROVR);
+ priv->stats.rx_crc_errors += ethread32(dev, ETH_STAT_RFCS);
+ priv->stats.rx_frame_errors += ethread32(dev, ETH_STAT_RALN);
+ priv->stats.rx_errors += ethread32(dev, ETH_STAT_RCDE) +
+ ethread32(dev, ETH_STAT_RCSE) +
+ ethread32(dev, ETH_STAT_RUND) +
+ ethread32(dev, ETH_STAT_RFRG) +
+ ethread32(dev, ETH_STAT_RJBR);
+ priv->stats.multicast += ethread32(dev, ETH_STAT_RMCA);
+ priv->stats.tx_aborted_errors += ethread32(dev, ETH_STAT_TXCL);
+ priv->stats.collisions += ethread32(dev, ETH_STAT_TNCL);
+
+ return &priv->stats;
+}
+
+static void ns9xxx_set_multicast_list(struct net_device *dev)
+{
+ u32 saf = ETH_SAF_BROAD;
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ /* TODO: use the hash tables to improve multicast reception */
+ if (dev->flags & IFF_PROMISC)
+ /* get everything */
+ saf |= ETH_SAF_PRO;
+
+ else if (dev->flags & IFF_ALLMULTI)
+ /* get all multicast traffic */
+ saf |= ETH_SAF_PRM;
+
+ else if (dev->mc_count > 0) {
+ struct dev_addr_list *m;
+ u32 ht[2] = {0, 0};
+
+ for (m = dev->mc_list; m; m = m->next) {
+ /*
+ * The HRM of ns9360 and ns9215 state that the upper 6
+ * bits are used to calculate the bit in the hash table,
+ * but the sample code (and the NET+OS driver) uses bits
+ * 28:23 ...
+ */
+ u32 crc = ether_crc(ETH_ALEN, m->dmi_addr) >> 23;
+ crc &= 0x3f;
+
+ ht[crc & 0x20 ? 1 : 0] |= 1 << (crc & 0x1f);
+ }
+
+ saf |= ETH_SAF_PRA;
+
+ ethwrite32(dev, ht[0], ETH_HT1);
+ ethwrite32(dev, ht[1], ETH_HT2);
+ }
+
+ ethwrite32(dev, saf, ETH_SAF);
+}
+
+static int ns9xxx_eth_mdiobus_init(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ struct plat_ns9xxx_eth *pdata = pdev->dev.platform_data;
+ int i;
+ char phyid[BUS_ID_SIZE];
+ int ret = -ENOMEM;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ priv->mdiobus = mdiobus_alloc();
+ if (priv->mdiobus == NULL)
+ goto err_out;
+
+ priv->mdiobus->name = DRIVER_NAME "-mii";
+ snprintf(priv->mdiobus->id, MII_BUS_ID_SIZE, "0");
+ priv->mdiobus->priv = dev;
+ priv->mdiobus->read = ns9xxx_eth_mdiobus_read;
+ priv->mdiobus->write = ns9xxx_eth_mdiobus_write;
+ priv->mdiobus->reset = ns9xxx_eth_mdiobus_reset;
+ priv->mdiobus->phy_mask = pdata->phy_mask;
+ priv->mdiobus->parent = &pdev->dev;
+ priv->mdiobus->irq = kmalloc(sizeof(*priv->mdiobus->irq) * PHY_MAX_ADDR,
+ GFP_KERNEL);
+
+ if (!priv->mdiobus->irq) {
+ dev_dbg(&pdev->dev, "%s: err_alloc_irq\n", __func__);
+ goto err_alloc_irq;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ priv->mdiobus->irq[i] = PHY_POLL;
+
+ ret = mdiobus_register(priv->mdiobus);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_mdiobus_register -> %d\n",
+ __func__, ret);
+ goto err_mdiobus_register;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ if (priv->mdiobus->phy_map[i])
+ break;
+
+ if (i >= PHY_MAX_ADDR) {
+ dev_dbg(&pdev->dev, "%s: no phy found\n", __func__);
+ ret = -ENODEV;
+ goto err_find_phy;
+ }
+
+ dev_dbg(&pdev->dev, "%s: using phy at address %d\n", __func__, i);
+
+ snprintf(phyid, sizeof(phyid), PHY_ID_FMT,
+ priv->mdiobus->id, i);
+
+ priv->phy = phy_connect(dev, phyid, &ns9xxx_eth_adjust_link,
+ 0, PHY_INTERFACE_MODE_MII);
+
+ if (IS_ERR(priv->phy)) {
+ ret = PTR_ERR(priv->phy);
+ dev_dbg(&pdev->dev, "%s: err_phy_connect -> %d\n",
+ __func__, ret);
+err_find_phy:
+
+ mdiobus_unregister(priv->mdiobus);
+err_mdiobus_register:
+
+ kfree(priv->mdiobus->irq);
+err_alloc_irq:
+ mdiobus_free(priv->mdiobus);
+err_out:
+ return ret;
+ }
+
+ priv->phy->supported &= SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_MII;
+
+ priv->phy->advertising = priv->phy->supported;
+
+ return 0;
+}
+
+static int ns9xxx_eth_mdiobus_disable(struct net_device *dev)
+{
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ dev_dbg(&dev->dev, "%s\n", __func__);
+
+ phy_disconnect(priv->phy);
+ mdiobus_unregister(priv->mdiobus);
+ kfree(priv->mdiobus->irq);
+ mdiobus_free(priv->mdiobus);
+
+ return 0;
+}
+
+static __devinit int ns9xxx_eth_pdrv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ns9xxx_eth_priv *priv;
+
+ struct resource *mem;
+ struct plat_ns9xxx_eth *pdata = pdev->dev.platform_data;
+ unsigned char __iomem *membase;
+ u32 sa;
+ int ret = -ENODEV;
+ int i;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ if (!pdata)
+ goto err_pdata;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_dbg(&pdev->dev, "%s: err_get_mem\n", __func__);
+ goto err_get_mem;
+ }
+
+ ret = -ENOMEM;
+
+ membase = ioremap(mem->start, 0x2800);
+ if (!membase) {
+ dev_dbg(&pdev->dev, "%s: err_ioremap\n", __func__);
+ goto err_ioremap;
+ }
+
+ dev = alloc_etherdev(sizeof(*priv));
+ if (!dev) {
+ dev_dbg(&pdev->dev, "%s: err_alloc_etherdev\n", __func__);
+ goto err_alloc_etherdev;
+ }
+
+ dev->dev.coherent_dma_mask = (u32)-1;
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
+
+ dev->open = ns9xxx_eth_open;
+ dev->stop = ns9xxx_eth_stop;
+ dev->hard_start_xmit = ns9xxx_eth_hard_start_xmit;
+ dev->tx_timeout = ns9xxx_eth_tx_timeout;
+ dev->get_stats = ns9xxx_eth_get_stats;
+ dev->set_multicast_list = ns9xxx_set_multicast_list;
+ dev->do_ioctl = ns9xxx_eth_ioctl;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = ns9xxx_eth_netpoll;
+#endif
+
+ /* TODO: implement VLAN */
+ dev->features = 0;
+
+ priv = netdev_priv(dev);
+
+ spin_lock_init(&priv->lock);
+ INIT_DELAYED_WORK(&priv->recover_from_rx_stall, ns9xxx_eth_recover_from_rx_stall);
+
+ priv->membase = membase;
+ priv->mapbase = mem->start;
+ priv->irqrx = pdata->irqrx;
+ priv->irqtx = pdata->irqtx;
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ priv->activityled = pdata->activityled;
+ /* init kernel timer for toggling
+ * the activity LED */
+ activityled_timer.data = (unsigned long)priv->activityled;
+ activityled_timer.function = activityled_timer_fn;
+ activityled_timer.expires = jiffies + ACTIVITYLED_TOGGLE_TIMEOUT;
+ init_timer(&activityled_timer);
+ add_timer(&activityled_timer);
+#endif
+
+ priv->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_dbg(&pdev->dev, "%s: err_clk_get -> %d\n", __func__, ret);
+ goto err_clk_get;
+ }
+
+ ret = clk_enable(priv->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_clk_enable -> %d\n",
+ __func__, ret);
+ goto err_clk_enable;
+ }
+
+ sa = ethread32(dev, ETH_SA1);
+ if (sa == 0) {
+ dev_warn(&pdev->dev, "Warning: Using default mac address "
+ "00:04:f3:ff:ff:fa\n");
+ memcpy(dev->dev_addr, "\x00\x04\xf3\xff\xff\xfa", ETH_ALEN);
+ } else {
+ DECLARE_MAC_BUF(mac);
+
+ /* assume the bootloader has provided a valid mac address */
+ dev->dev_addr[4] = sa;
+ dev->dev_addr[5] = sa >> 8;
+ sa = ethread32(dev, ETH_SA2);
+ dev->dev_addr[2] = sa;
+ dev->dev_addr[3] = sa >> 8;
+ sa = ethread32(dev, ETH_SA3);
+ dev->dev_addr[0] = sa;
+ dev->dev_addr[1] = sa >> 8;
+ dev_dbg(&pdev->dev, "mac address: %s\n",
+ print_mac(mac, dev->dev_addr));
+ }
+
+ ethwrite32(dev, ETH_EGCR1_PM_MII | ETH_EGCR1_MACHRST, ETH_EGCR1);
+ udelay(5);
+ ethwrite32(dev, ETH_EGCR1_PM_MII | ETH_EGCR1_ETX, ETH_EGCR1);
+
+ ethwrite32(dev, 0, ETH_MAC1);
+
+ dev_dbg(&pdev->dev, "%s: clear tx DMA descs\n", __func__);
+ for (i = 0; i < 4 * TOTAL_NR_TXDESC; ++i)
+ ethwrite32(dev, 0, ETH_TXBDR + 4 * i);
+
+ ret = ns9xxx_eth_mdiobus_init(pdev);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_mdiobus_init -> %d\n",
+ __func__, ret);
+ goto err_mdiobus_init;
+ }
+
+ /* Make the device wakeup capable, but disabled by default */
+ device_init_wakeup(&pdev->dev, 1);
+ device_set_wakeup_enable(&pdev->dev, 0);
+
+ ret = register_netdev(dev);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_register_netdev -> %d\n",
+ __func__, ret);
+
+ ns9xxx_eth_mdiobus_disable(dev);
+err_mdiobus_init:
+
+ clk_disable(priv->clk);
+err_clk_enable:
+
+ clk_put(priv->clk);
+err_clk_get:
+
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+err_alloc_etherdev:
+
+ iounmap(membase);
+err_ioremap:
+err_get_mem:
+err_pdata:
+ return ret;
+ }
+
+ /* When using nfsroot, there are RPC requests which can be sent
+ * before the link is up (long autonegotiation...). Then the
+ * RPC is not completed until the autonegotiation timeouts (which
+ * adds long time to the boot process)
+ * Following line tries to workaround that situation by starting
+ * the phy here, so we have some worked completed in advance to
+ * the driver open call */
+ phy_start(priv->phy);
+
+ dev_info(&pdev->dev, "%s at MMIO %p\n", dev->name, membase);
+ dev_dbg(&pdev->dev, "&priv = %p\n", &priv);
+ dev_dbg(&pdev->dev, "&priv->txfree = %p\n", &priv->txfree);
+ dev_dbg(&pdev->dev, "&priv->txbusy = %p\n", &priv->txbusy);
+
+ return 0;
+}
+
+static __devexit int ns9xxx_eth_pdrv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ unregister_netdev(dev);
+
+ ns9xxx_eth_mdiobus_disable(dev);
+
+ clk_disable(priv->clk);
+
+ clk_put(priv->clk);
+
+ platform_set_drvdata(pdev, NULL);
+
+ iounmap(priv->membase);
+
+ free_netdev(dev);
+
+#ifdef CONFIG_GPIO_ETH_ACTIVITY_LED
+ del_timer(&activityled_timer);
+#endif
+
+ return 0;
+}
+
+#if defined(CONFIG_PM)
+static int ns9xxx_eth_pdrv_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ if (state.event != PM_EVENT_SUSPEND)
+ /* XXX: implement full state saving */
+ return -EBUSY;
+
+ netif_device_detach(dev);
+
+ if (device_may_wakeup(&pdev->dev)) {
+
+ ret = enable_irq_wake(priv->irqrx);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_enable_irq_wake -> %d\n",
+ __func__, ret);
+ goto err_enable_irq_wake;
+ }
+
+ } else {
+ dev_dbg(&pdev->dev, "%s: !device_may_wakeup\n", __func__);
+ clk_disable(priv->clk);
+ }
+
+ return 0;
+
+err_enable_irq_wake:
+ netif_device_attach(dev);
+
+ return ret;
+}
+
+static int ns9xxx_eth_pdrv_resume(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct ns9xxx_eth_priv *priv = netdev_priv(dev);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(priv->irqrx);
+ else {
+ ret = clk_enable(priv->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_clk_enable -> %d",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ netif_device_attach(dev);
+
+ return 0;
+}
+#endif
+
+static struct platform_driver ns9xxx_eth_pdriver = {
+ .probe = ns9xxx_eth_pdrv_probe,
+ .remove = __devexit_p(ns9xxx_eth_pdrv_remove),
+#if defined(CONFIG_PM)
+ .suspend = ns9xxx_eth_pdrv_suspend,
+ .resume = ns9xxx_eth_pdrv_resume,
+#endif
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ns9xxx_eth_init(void)
+{
+ int ret;
+
+ /* catch compiler bugs, endian issues etc pp */
+ BUG_ON(offsetof(union ns9xxx_dma_desc, data) != 0x0);
+ BUG_ON(offsetof(union ns9xxx_dma_desc, source) != 0x0);
+ BUG_ON(offsetof(union ns9xxx_dma_desc, len) != 0x4);
+ BUG_ON(offsetof(union ns9xxx_dma_desc, dest) != 0x8);
+ BUG_ON(offsetof(union ns9xxx_dma_desc, flags) != 0xe);
+ BUG_ON(offsetof(union ns9xxx_dma_desc, status) != 0xc);
+
+ ret = platform_driver_register(&ns9xxx_eth_pdriver);
+ if (ret) {
+ pr_debug("%s: err_pdrv_register\n", __func__);
+
+ return ret;
+ }
+ pr_info("Digi NS9XXX Ethernet driver\n");
+
+ return 0;
+}
+
+static void __exit ns9xxx_eth_exit(void)
+{
+ platform_driver_unregister(&ns9xxx_eth_pdriver);
+}
+
+module_init(ns9xxx_eth_init);
+module_exit(ns9xxx_eth_exit);
+
+MODULE_AUTHOR("Uwe Kleine-Koenig");
+MODULE_DESCRIPTION("Digi NS9XXX ethernet driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/net/smc911x.h b/drivers/net/smc911x.h
index cc7d85bdfb3e..2ea8f4aa1d91 100644
--- a/drivers/net/smc911x.h
+++ b/drivers/net/smc911x.h
@@ -52,6 +52,14 @@
#ifdef SMC_USE_PXA_DMA
#define SMC_USE_DMA
+ #define SMC_USE_32BIT 1
+ #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW
+#endif
+
+#ifdef CONFIG_ARCH_MXC
+ #define SMC_USE_16BIT 0
+ #define SMC_USE_32BIT 1
+ #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW
#endif
/* store this information for the driver.. */
diff --git a/drivers/net/smsc9118/Makefile b/drivers/net/smsc9118/Makefile
new file mode 100644
index 000000000000..d33fe1dc8e38
--- /dev/null
+++ b/drivers/net/smsc9118/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SMSC9118) += smsc911x.o
diff --git a/drivers/net/smsc9118/smsc911x.c b/drivers/net/smsc9118/smsc911x.c
new file mode 100644
index 000000000000..3a55f78cee8a
--- /dev/null
+++ b/drivers/net/smsc9118/smsc911x.c
@@ -0,0 +1,2711 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2007 SMSC
+ * Copyright (C) 2005 ARM
+ *
+ * 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.
+ *
+ ***************************************************************************
+ * Rewritten, heavily based on smsc911x simple driver by SMSC.
+ * Partly uses io macros from smc91x.c by Nicolas Pitre
+ *
+ * Supported devices:
+ * LAN9115, LAN9116, LAN9117, LAN9118
+ * LAN9215, LAN9216, LAN9217, LAN9218
+ *
+ * History:
+ * 05/05/2005 bahadir.balban@arm.com
+ * - Transition to linux coding style
+ * - Platform driver and module interface
+ *
+ * 17/07/2006 steve.glendinning@smsc.com
+ * - Added support for LAN921x family
+ * - Added workaround for multicast filters
+ *
+ * 31/07/2006 steve.glendinning@smsc.com
+ * - Removed tasklet, using NAPI poll instead
+ * - Multiple device support
+ * - Large tidy-up following feedback from netdev list
+ *
+ * 03/08/2006 steve.glendinning@smsc.com
+ * - Added ethtool support
+ * - Convert to use generic MII interface
+ *
+ * 04/08/2006 bahadir.balban@arm.com
+ * - Added ethtool eeprom r/w support
+ *
+ * 17/06/2007 steve.glendinning@smsc.com
+ * - Incorporate changes from Bill Gatliff and Russell King
+ *
+ * 04/07/2007 steve.glendinning@smsc.com
+ * - move irq configuration to platform_device
+ * - fix link poller after interface is stopped and restarted
+ *
+ * 13/07/2007 bahadir.balban@arm.com
+ * - set irq polarity before requesting irq
+ *
+ * 26/06/2007 hennerich@blackfin.uclinux.org
+ * - Fixed minor style issue to pass checkpatch.pl
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/bug.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+
+/* For having the same platform-data as in the Vanilla kernel */
+#include <linux/smc911x.h>
+
+#include "smsc911x.h"
+
+#define SMSC_CHIPNAME "smsc911x"
+#define SMSC_DRV_VERSION "2007-07-13"
+
+MODULE_LICENSE("GPL");
+
+
+/* Base address of the connected controller: S3C2410_CS5 = 0x28000000 */
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] smsc911x: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "smsc911x: " fmt, ## args)
+
+#if 0
+#define SMSC911X_DEBUG
+#endif
+
+#ifdef SMSC911X_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "smsc911x: " fmt, ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+/* Enables the debug messages for the PM-operations (WOL, suspend, etc.) */
+#if 0
+#define SMSC911X_PM_DEBUG
+#endif
+
+#ifdef SMSC911X_PM_DEBUG
+# define printk_pmdbg(fmt, args...) printk(KERN_DEBUG "smsc911x: " fmt, ## args)
+#else
+# define printk_pmdbg(fmt, args...)
+#endif
+
+struct smsc911x_data {
+ void __iomem *ioaddr;
+
+ unsigned int idrev;
+ unsigned int generation; /* used to decide which workarounds apply */
+
+ /* device configuration */
+ unsigned int irq_polarity;
+ unsigned int irq_type;
+ unsigned int irq_flags;
+
+ /* This needs to be acquired before calling any of below:
+ * smsc911x_mac_read(), smsc911x_mac_write()
+ * smsc911x_phy_read(), smsc911x_phy_write()
+ */
+ spinlock_t phy_lock;
+
+ struct mii_if_info mii;
+ unsigned int using_extphy;
+ u32 msg_enable;
+#ifdef USE_LED1_WORK_AROUND
+ unsigned int gpio_setting;
+ unsigned int gpio_orig_setting;
+#endif
+ struct net_device *netdev;
+ struct napi_struct napi;
+ struct timer_list link_poll_timer;
+ unsigned int stop_link_poll;
+
+ unsigned int software_irq_signal;
+
+#ifdef USE_PHY_WORK_AROUND
+#define MIN_PACKET_SIZE (64)
+ char loopback_tx_pkt[MIN_PACKET_SIZE];
+ char loopback_rx_pkt[MIN_PACKET_SIZE];
+ unsigned int resetcount;
+#endif
+
+ /* Members for Multicast filter workaround */
+ unsigned int multicast_update_pending;
+ unsigned int set_bits_mask;
+ unsigned int clear_bits_mask;
+ unsigned int hashhi;
+ unsigned int hashlo;
+ unsigned int last_rxstat;
+
+ /* Registers for the internal PM */
+ unsigned long mac_wucsr;
+ unsigned long pmt_ctrl;
+ unsigned long phy_intmsk;
+};
+
+
+static int smsc911x_set_mac(struct net_device *dev, void *addr);
+
+#if SMSC_CAN_USE_32BIT
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ return readl(pdata->ioaddr + reg);
+}
+
+static inline void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
+ u32 reg)
+{
+ writel(val, pdata->ioaddr + reg);
+}
+
+#else /* SMSC_CAN_USE_32BIT */
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ u32 reg_val;
+ unsigned long flags;
+
+ /* these two 16-bit reads must be performed consecutively, so must
+ * not be interrupted by our own ISR (which would start another
+ * read operation) */
+ local_irq_save(flags);
+ reg_val = ((readw(pdata->ioaddr + reg) & 0xFFFF) |
+ ((readw(pdata->ioaddr + reg + 2) & 0xFFFF) << 16));
+ local_irq_restore(flags);
+
+ return reg_val;
+}
+
+static inline void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
+ u32 reg)
+{
+ unsigned long flags;
+
+ /* these two 16-bit writes must be performed consecutively, so must
+ * not be interrupted by our own ISR (which would start another
+ * read operation) */
+ local_irq_save(flags);
+ writew(val & 0xFFFF, pdata->ioaddr + reg);
+ writew((val >> 16) & 0xFFFF, pdata->ioaddr + reg + 2);
+ local_irq_restore(flags);
+}
+
+#endif /* SMSC_CAN_USE_32BIT */
+
+#ifndef CONFIG_BLACKFIN
+/* Writes a packet to the TX_DATA_FIFO */
+static inline void
+smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ while (wordcount--)
+ smsc911x_reg_write(*buf++, pdata, TX_DATA_FIFO);
+}
+
+/* Reads a packet out of the RX_DATA_FIFO */
+static inline void
+smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ while (wordcount--)
+ *buf++ = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+}
+#else
+/* Writes a packet to the TX_DATA_FIFO */
+static inline void
+smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ if (wordcount > 24)
+ dma_outsl((u_long)pdata->ioaddr + TX_DATA_FIFO, buf, wordcount);
+ else
+ outsl((u_long)pdata->ioaddr + TX_DATA_FIFO, buf, wordcount);
+}
+
+/* Reads a packet out of the RX_DATA_FIFO */
+static inline void
+smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ if (wordcount > 24)
+ dma_insl((u_long)pdata->ioaddr + RX_DATA_FIFO, buf, wordcount);
+ else
+ insl((u_long)pdata->ioaddr + RX_DATA_FIFO, buf, wordcount);
+}
+#endif
+
+/* waits for MAC not busy, with timeout. Only called by smsc911x_mac_read
+ * and smsc911x_mac_write, so assumes phy_lock is held */
+static int smsc911x_mac_notbusy(struct smsc911x_data *pdata)
+{
+ int i;
+ u32 val;
+
+ for (i = 0; i < 40; i++) {
+ val = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (!(val & MAC_CSR_CMD_CSR_BUSY_))
+ return 1;
+ }
+ SMSC_WARNING("Timed out waiting for MAC not BUSY. "
+ "MAC_CSR_CMD: 0x%08X", val);
+ return 0;
+}
+
+/* Fetches a MAC register value. Assumes phy_lock is acquired */
+static u32 smsc911x_mac_read(struct smsc911x_data *pdata, unsigned int offset)
+{
+ unsigned int temp;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARNING("smsc911x_mac_read failed, MAC busy at entry");
+ return 0xFFFFFFFF;
+ }
+
+ /* Send the MAC cmd */
+ smsc911x_reg_write(((offset & 0xFF) | MAC_CSR_CMD_CSR_BUSY_
+ | MAC_CSR_CMD_R_NOT_W_), pdata, MAC_CSR_CMD);
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the read to happen */
+ if (likely(smsc911x_mac_notbusy(pdata)))
+ return smsc911x_reg_read(pdata, MAC_CSR_DATA);
+
+ SMSC_WARNING("smsc911x_mac_read failed, MAC busy after read");
+ return 0xFFFFFFFF;
+}
+
+/* Set a mac register, phy_lock must be acquired before calling */
+static void smsc911x_mac_write(struct smsc911x_data *pdata,
+ unsigned int offset, u32 val)
+{
+ unsigned int temp;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARNING("smsc911x_mac_write failed, MAC busy at entry");
+ return;
+ }
+
+ /* Send data to write */
+ smsc911x_reg_write(val, pdata, MAC_CSR_DATA);
+
+ /* Write the actual data */
+ smsc911x_reg_write(((offset & 0xFF) | MAC_CSR_CMD_CSR_BUSY_), pdata,
+ MAC_CSR_CMD);
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the write to complete */
+ if (likely(smsc911x_mac_notbusy(pdata)))
+ return;
+
+ SMSC_WARNING("smsc911x_mac_write failed, MAC busy after write");
+}
+
+/* Gets a phy register, phy_lock must be acquired before calling */
+static u16 smsc911x_phy_read(struct smsc911x_data *pdata, unsigned int index)
+{
+ unsigned int addr;
+ int i;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARNING("MII is busy in smsc911x_phy_read???");
+ return 0;
+ }
+
+ /* Set the address, index & direction (read from PHY) */
+ addr = (((pdata->mii.phy_id) & 0x1F) << 11)
+ | ((index & 0x1F) << 6);
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for read to complete w/ timeout */
+ for (i = 0; i < 100; i++) {
+ /* See if MII is finished yet */
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ return smsc911x_mac_read(pdata, MII_DATA);
+ }
+ }
+ SMSC_WARNING("Timed out waiting for MII write to finish");
+ return 0xFFFF;
+}
+
+/* Sets a phy register, phy_lock must be acquired before calling */
+static void smsc911x_phy_write(struct smsc911x_data *pdata,
+ unsigned int index, u16 val)
+{
+ unsigned int addr;
+ int i;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARNING("MII is busy in smsc911x_write_phy???");
+ return;
+ }
+
+ /* Put the data to write in the MAC */
+ smsc911x_mac_write(pdata, MII_DATA, val);
+
+ /* Set the address, index & direction (write to PHY) */
+ addr = (((pdata->mii.phy_id) & 0x1F) << 11) |
+ ((index & 0x1F) << 6) | MII_ACC_MII_WRITE_;
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for write to complete w/ timeout */
+ for (i = 0; i < 100; i++) {
+ /* See if MII is finished yet */
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_))
+ return;
+ }
+ SMSC_WARNING("Timed out waiting for MII write to finish");
+}
+
+static int smsc911x_mdio_read(struct net_device *dev, int phy_id, int location)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+ int reg;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ reg = smsc911x_phy_read(pdata, location);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ return reg;
+}
+
+static void smsc911x_mdio_write(struct net_device *dev, int phy_id,
+ int location, int val)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, location, val);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+/* Autodetects and initialises external phy for SMSC9115 and SMSC9117 flavors.
+ * If something goes wrong, returns -ENODEV to revert back to internal phy.
+ * Performed at initialisation only, so interrupts are enabled */
+static int smsc911x_phy_initialise_external(struct smsc911x_data *pdata)
+{
+ unsigned int address;
+ unsigned int hwcfg;
+ unsigned int phyid1;
+ unsigned int phyid2;
+
+ hwcfg = smsc911x_reg_read(pdata, HW_CFG);
+
+ /* External phy is requested, supported, and detected */
+ if (hwcfg & HW_CFG_EXT_PHY_DET_) {
+
+ /* Attempt to switch to external phy for auto-detecting
+ * its address. Assuming tx and rx are stopped because
+ * smsc911x_phy_initialise is called before
+ * smsc911x_rx_initialise and tx_initialise.
+ */
+
+ /* Disable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to stop */
+
+ /* Switch to external phy */
+ hwcfg |= HW_CFG_EXT_PHY_EN_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Enable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to restart */
+
+ hwcfg |= HW_CFG_SMI_SEL_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Auto-detect PHY */
+ spin_lock_irq(&pdata->phy_lock);
+ for (address = 0; address <= 31; address++) {
+ pdata->mii.phy_id = address;
+ phyid1 = smsc911x_phy_read(pdata, MII_PHYSID1);
+ phyid2 = smsc911x_phy_read(pdata, MII_PHYSID2);
+ if ((phyid1 != 0xFFFFU) || (phyid2 != 0xFFFFU)) {
+ SMSC_TRACE("Detected PHY at address = "
+ "0x%02X = %d", address, address);
+ break;
+ }
+ }
+ spin_unlock_irq(&pdata->phy_lock);
+
+ if ((phyid1 == 0xFFFFU) && (phyid2 == 0xFFFFU)) {
+ SMSC_WARNING("External PHY is not accessable, "
+ "using internal PHY instead");
+ /* Revert back to internal phy settings. */
+
+ /* Disable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to stop */
+
+ /* Switch to internal phy */
+ hwcfg &= (~HW_CFG_EXT_PHY_EN_);
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Enable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_INT_PHY_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to restart */
+
+ hwcfg &= (~HW_CFG_SMI_SEL_);
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ /* Use internal phy */
+ return -ENODEV;
+ } else {
+ SMSC_TRACE("Successfully switched to external PHY");
+ pdata->using_extphy = 1;
+ }
+ } else {
+ SMSC_WARNING("No external PHY detected.");
+ SMSC_WARNING("Using internal PHY instead.");
+ /* Use internal phy */
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* called by phy_initialise and loopback test */
+static int smsc911x_phy_reset(struct smsc911x_data *pdata)
+{
+ unsigned int temp;
+ unsigned int i = 100000;
+ unsigned long flags;
+
+ SMSC_TRACE("Performing PHY BCR Reset");
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_RESET);
+ do {
+ udelay(10);
+ temp = smsc911x_phy_read(pdata, MII_BMCR);
+ } while ((i--) && (temp & BMCR_RESET));
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ if (temp & BMCR_RESET) {
+ SMSC_WARNING("PHY reset failed to complete.");
+ return 0;
+ }
+ /* Extra delay required because the phy may not be completed with
+ * its reset when BMCR_RESET is cleared. Specs say 256 uS is
+ * enough delay but using 1ms here to be safe
+ */
+ msleep(1);
+
+ return 1;
+}
+
+/* Fetches a tx status out of the status fifo */
+static unsigned int smsc911x_tx_get_txstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, TX_STATUS_FIFO);
+
+ return result;
+}
+
+/* Fetches the next rx status */
+static unsigned int smsc911x_rx_get_rxstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, RX_STATUS_FIFO);
+
+ return result;
+}
+
+#ifdef USE_PHY_WORK_AROUND
+static int smsc911x_phy_check_loopbackpkt(struct smsc911x_data *pdata)
+{
+ unsigned int tries;
+ u32 wrsz;
+ u32 rdsz;
+ u32 bufp;
+
+ for (tries = 0; tries < 10; tries++) {
+ unsigned int txcmd_a;
+ unsigned int txcmd_b;
+ unsigned int status;
+ unsigned int pktlength;
+ unsigned int i;
+
+ /* Zero-out rx packet memory */
+ memset(pdata->loopback_rx_pkt, 0, MIN_PACKET_SIZE);
+
+ /* Write tx packet to 118 */
+ txcmd_a = (((unsigned int)pdata->loopback_tx_pkt)
+ & 0x03) << 16;
+ txcmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ txcmd_a |= MIN_PACKET_SIZE;
+
+ txcmd_b = MIN_PACKET_SIZE << 16 | MIN_PACKET_SIZE;
+
+ smsc911x_reg_write(txcmd_a, pdata, TX_DATA_FIFO);
+ smsc911x_reg_write(txcmd_b, pdata, TX_DATA_FIFO);
+
+ bufp = ((u32) pdata->loopback_tx_pkt) & 0xFFFFFFFC;
+ wrsz = MIN_PACKET_SIZE + 3;
+ wrsz += (((u32) pdata->loopback_tx_pkt) & 0x3);
+ wrsz >>= 2;
+
+ smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+
+ /* Wait till transmit is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_tx_get_txstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARNING("Failed to transmit during loopback test");
+ continue;
+ }
+ if (status & TX_STS_ES_) {
+ SMSC_WARNING("Transmit encountered errors during "
+ "loopback test");
+ continue;
+ }
+
+ /* Wait till receive is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_rx_get_rxstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARNING("Failed to receive during loopback test");
+ continue;
+ }
+ if (status & RX_STS_ES_) {
+ SMSC_WARNING("Receive encountered errors during "
+ "loopback test");
+ continue;
+ }
+
+ pktlength = ((status & 0x3FFF0000UL) >> 16);
+ bufp = (u32)pdata->loopback_rx_pkt;
+ rdsz = pktlength + 3;
+ rdsz += ((u32)pdata->loopback_rx_pkt) & 0x3;
+ rdsz >>= 2;
+
+ smsc911x_rx_readfifo(pdata, (unsigned int *)bufp, rdsz);
+
+ if (pktlength != (MIN_PACKET_SIZE + 4)) {
+ SMSC_WARNING("Unexpected packet size during "
+ "loop back test, size=%d, "
+ "will retry", pktlength);
+ } else {
+ unsigned int j;
+ int mismatch = 0;
+ for (j = 0; j < MIN_PACKET_SIZE; j++) {
+ if (pdata->loopback_tx_pkt[j]
+ != pdata->loopback_rx_pkt[j]) {
+ mismatch = 1;
+ break;
+ }
+ }
+ if (!mismatch) {
+ SMSC_TRACE("Successfully verified "
+ "loopback packet");
+ return 1;
+ } else {
+ SMSC_WARNING("Data miss match during "
+ "loop back test, will retry.");
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int smsc911x_phy_loopbacktest(struct smsc911x_data *pdata)
+{
+ int result = 0;
+ unsigned int i;
+ unsigned int val;
+ unsigned long flags;
+
+ /* Initialise tx packet */
+ for (i = 0; i < 6; i++) {
+ /* Use broadcast destination address */
+ pdata->loopback_tx_pkt[i] = (char)0xFF;
+ }
+
+ for (i = 6; i < 12; i++) {
+ /* Use incrementing source address */
+ pdata->loopback_tx_pkt[i] = (char)i;
+ }
+
+ /* Set length type field */
+ pdata->loopback_tx_pkt[12] = 0x00;
+ pdata->loopback_tx_pkt[13] = 0x00;
+ for (i = 14; i < MIN_PACKET_SIZE; i++) {
+ pdata->loopback_tx_pkt[i] = (char)i;
+ }
+
+ val = smsc911x_reg_read(pdata, HW_CFG);
+ val &= HW_CFG_TX_FIF_SZ_;
+ val |= HW_CFG_SF_;
+ smsc911x_reg_write(val, pdata, HW_CFG);
+
+ smsc911x_reg_write(TX_CFG_TX_ON_, pdata, TX_CFG);
+ smsc911x_reg_write((((unsigned int)pdata->loopback_rx_pkt)
+ & 0x03) << 8, pdata, RX_CFG);
+
+ for (i = 0; i < 10; i++) {
+ /* Set PHY to 10/FD, no ANEG, and loopback mode */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, 0x4100);
+
+ /* Enable MAC tx/rx, FD */
+ smsc911x_mac_write(pdata, MAC_CR, MAC_CR_FDPX_
+ | MAC_CR_TXEN_ | MAC_CR_RXEN_);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ if (smsc911x_phy_check_loopbackpkt(pdata)) {
+ result = 1;
+ break;
+ }
+ pdata->resetcount++;
+
+ /* Disable MAC rx */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ smsc911x_phy_reset(pdata);
+ }
+
+ /* Disable MAC */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+
+ /* Cancel PHY loopback mode */
+ smsc911x_phy_write(pdata, MII_BMCR, 0);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ smsc911x_reg_write(0, pdata, TX_CFG);
+ smsc911x_reg_write(0, pdata, RX_CFG);
+
+ return result;
+}
+#endif /* USE_PHY_WORK_AROUND */
+
+
+inline static void smsc911x_phy_dump_regs(struct smsc911x_data *pdata)
+{
+ printk("BCR = 0x%04x\n", smsc911x_phy_read(pdata, MII_BMCR));
+ printk("BSR = 0x%04x\n", smsc911x_phy_read(pdata, MII_BMSR));
+ printk("ID1 = 0x%04x\n", smsc911x_phy_read(pdata, MII_PHYSID1));
+ printk("ID2 = 0x%04x\n", smsc911x_phy_read(pdata, MII_PHYSID2));
+ printk("ADVER = 0x%04x\n", smsc911x_phy_read(pdata, MII_ADVERTISE));
+ printk("LPA = 0x%04x\n", smsc911x_phy_read(pdata, MII_LPA));
+ printk("EXP = 0x%04x\n", smsc911x_phy_read(pdata, MII_EXPANSION));
+}
+
+/* assumes phy_lock is held */
+static void smsc911x_phy_update_flowcontrol(struct smsc911x_data *pdata)
+{
+ unsigned int temp;
+
+ if (pdata->mii.full_duplex) {
+ unsigned int phy_adv;
+ unsigned int phy_lpa;
+ phy_adv = smsc911x_phy_read(pdata, MII_ADVERTISE);
+ phy_lpa = smsc911x_phy_read(pdata, MII_LPA);
+ if (phy_adv & phy_lpa & LPA_PAUSE_CAP) {
+ /* Both ends support symmetric pause, enable
+ * PAUSE receive and transmit */
+ smsc911x_mac_write(pdata, FLOW, 0xFFFF0002);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp |= 0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ } else if (((phy_adv & ADVERTISE_PAUSE_ALL) ==
+ ADVERTISE_PAUSE_ALL) &&
+ ((phy_lpa & LPA_PAUSE_ALL) == LPA_PAUSE_ASYM)) {
+ /* We support symmetric and asym pause, the
+ * other end only supports asym, Enable PAUSE
+ * receive, disable PAUSE transmit */
+ smsc911x_mac_write(pdata, FLOW, 0xFFFF0002);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp &= ~0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ } else {
+ /* Disable PAUSE receive and transmit */
+ smsc911x_mac_write(pdata, FLOW, 0);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp &= ~0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ }
+ } else {
+ smsc911x_mac_write(pdata, FLOW, 0);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp |= 0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ }
+}
+
+static void smsc911x_phy_update_duplex(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+ unsigned int mac_cr;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ if (pdata->mii.full_duplex) {
+ SMSC_TRACE("configuring for full duplex mode");
+ mac_cr |= MAC_CR_FDPX_;
+ } else {
+ SMSC_TRACE("configuring for half duplex mode");
+ mac_cr &= ~MAC_CR_FDPX_;
+ }
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+ smsc911x_phy_update_flowcontrol(pdata);
+
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+
+/* Update link mode if any thing has changed */
+static void smsc911x_phy_update_linkmode(struct net_device *dev, int init)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ if (mii_check_media(&pdata->mii, netif_msg_link(pdata), init))
+ smsc911x_phy_update_duplex(dev);
+ /* mii_check_media() exists if the media is forced... */
+ if (pdata->mii.force_media) {
+ int cur_link = mii_link_ok(&pdata->mii);
+ int prev_link = netif_carrier_ok(dev);
+
+ if (!prev_link && cur_link) {
+ printk(KERN_INFO "%s: link up\n", dev->name);
+ netif_carrier_on(dev);
+ } else if (prev_link && !cur_link) {
+ printk(KERN_INFO "%s: link down\n", dev->name);
+ netif_carrier_off(dev);
+ }
+ }
+
+#ifdef USE_LED1_WORK_AROUND
+ if (netif_carrier_ok(dev)) {
+ if ((pdata->gpio_orig_setting & GPIO_CFG_LED1_EN_) &&
+ (!pdata->using_extphy)) {
+ /* Restore orginal GPIO configuration */
+ pdata->gpio_setting = pdata->gpio_orig_setting;
+ smsc911x_reg_write(pdata->gpio_setting, pdata,
+ GPIO_CFG);
+ }
+ } else {
+ /* Check global setting that LED1
+ * usage is 10/100 indicator */
+ pdata->gpio_setting = smsc911x_reg_read(pdata, GPIO_CFG);
+ if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_)
+ && (!pdata->using_extphy)) {
+ /* Force 10/100 LED off, after saving
+ * orginal GPIO configuration */
+ pdata->gpio_orig_setting = pdata->gpio_setting;
+
+ pdata->gpio_setting &= ~GPIO_CFG_LED1_EN_;
+ pdata->gpio_setting |= (GPIO_CFG_GPIOBUF0_
+ | GPIO_CFG_GPIODIR0_
+ | GPIO_CFG_GPIOD0_);
+ smsc911x_reg_write(pdata->gpio_setting, pdata,
+ GPIO_CFG);
+ }
+ }
+#endif /* USE_LED1_WORK_AROUND */
+}
+
+/* Entry point for the link poller */
+static void smsc911x_phy_checklink(unsigned long ptr)
+{
+ struct net_device *dev = (struct net_device *)ptr;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ smsc911x_phy_update_linkmode(dev, 0);
+
+ if (!(pdata->stop_link_poll)) {
+ pdata->link_poll_timer.expires = jiffies + 2 * HZ;
+ add_timer(&pdata->link_poll_timer);
+ } else {
+ pdata->stop_link_poll = 0;
+ }
+}
+
+static void smsc911x_phy_set_automdx(struct smsc911x_data *pdata)
+{
+ u16 ctrlstatus;
+
+ ctrlstatus = smsc911x_phy_read(pdata, 27);
+ ctrlstatus &= 0x1fff;
+ ctrlstatus |= (0x6 << 13);
+ smsc911x_phy_write(pdata, 27, ctrlstatus);
+}
+
+/* Initialises the PHY layer. Called at initialisation by open() so
+ * interrupts are enabled */
+static int smsc911x_phy_initialise(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int phyid1 = 0;
+ unsigned int phyid2 = 0;
+ unsigned int temp;
+
+ printk_debug("Calling phy_initialise()\n");
+
+ pdata->using_extphy = 0;
+
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01170000:
+ case 0x01150000:
+ /* External PHY supported, try to autodetect */
+ if (smsc911x_phy_initialise_external(pdata) < 0) {
+ SMSC_TRACE("External PHY is not detected, using "
+ "internal PHY instead");
+ pdata->mii.phy_id = 1;
+ }
+ break;
+ default:
+ SMSC_TRACE("External PHY is not supported, using internal PHY "
+ "instead");
+ pdata->mii.phy_id = 1;
+ break;
+ }
+
+ spin_lock_irq(&pdata->phy_lock);
+ phyid1 = smsc911x_phy_read(pdata, MII_PHYSID1);
+ phyid2 = smsc911x_phy_read(pdata, MII_PHYSID2);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ if ((phyid1 == 0xFFFF) && (phyid2 == 0xFFFF)) {
+ SMSC_WARNING("Internal PHY not detected!");
+ return 0;
+ }
+
+ /* Reset the phy */
+ if (!smsc911x_phy_reset(pdata)) {
+ SMSC_WARNING("PHY reset failed to complete.");
+ return 0;
+ }
+#ifdef USE_PHY_WORK_AROUND
+ if (!smsc911x_phy_loopbacktest(pdata)) {
+ SMSC_WARNING("Failed Loop Back Test");
+ return 0;
+ } else {
+ SMSC_TRACE("Passed Loop Back Test");
+ }
+#endif /* USE_PHY_WORK_AROUND */
+
+
+ smsc911x_phy_set_automdx(pdata);
+
+ /* Advertise all speeds and pause capabilities */
+ spin_lock_irq(&pdata->phy_lock);
+ temp = smsc911x_phy_read(pdata, MII_ADVERTISE);
+ temp |= (ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+ smsc911x_phy_write(pdata, MII_ADVERTISE, temp);
+ pdata->mii.advertising = temp;
+
+ if (!pdata->using_extphy) {
+ /* using internal phy, enable PHY interrupts */
+ smsc911x_phy_read(pdata, MII_INTSTS);
+ smsc911x_phy_write(pdata, MII_INTMSK, PHY_INTMSK_DEFAULT_);
+ }
+
+ /* begin to establish link */
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ smsc911x_phy_update_linkmode(dev, 1);
+
+ pdata->stop_link_poll = 0;
+ setup_timer(&pdata->link_poll_timer, smsc911x_phy_checklink,
+ (unsigned long)dev);
+ pdata->link_poll_timer.expires = jiffies + 2 * HZ;
+ add_timer(&pdata->link_poll_timer);
+
+ printk_debug("PHY initialised succesfully\n");
+ return 1;
+}
+
+/* Gets the number of tx statuses in the fifo */
+static unsigned int smsc911x_tx_get_txstatcount(struct smsc911x_data *pdata)
+{
+ unsigned int result = (smsc911x_reg_read(pdata, TX_FIFO_INF)
+ & TX_FIFO_INF_TSUSED_) >> 16;
+ return result;
+}
+
+/* Reads tx statuses and increments counters where necessary */
+static void smsc911x_tx_update_txcounters(struct smsc911x_data *pdata)
+{
+ struct net_device *netdev = pdata->netdev;
+ unsigned int tx_stat;
+
+ while ((tx_stat = smsc911x_tx_get_txstatus(pdata)) != 0) {
+ if (unlikely(tx_stat & 0x80000000)) {
+ /* In this driver the packet tag is used as the packet
+ * length. Since a packet length can never reach the
+ * size of 0x8000, this bit is reserved. It is worth
+ * noting that the "reserved bit" in the warning above
+ * does not reference a hardware defined reserved bit
+ * but rather a driver defined one.
+ */
+ SMSC_WARNING("Packet tag reserved bit is high");
+ } else {
+ if (unlikely(tx_stat & 0x00008000)) {
+ printk_debug("TX status error: 0x%08x (MAC 0x%08x)\n",
+ tx_stat, smsc911x_mac_read(pdata, MAC_CR));
+ netdev->stats.tx_errors++;
+ } else {
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes += (tx_stat >> 16);
+ }
+ if (unlikely(tx_stat & 0x00000100)) {
+ netdev->stats.collisions += 16;
+ netdev->stats.tx_aborted_errors += 1;
+ } else {
+ netdev->stats.collisions +=
+ ((tx_stat >> 3) & 0xF);
+ }
+ if (unlikely(tx_stat & 0x00000800)) {
+ netdev->stats.tx_carrier_errors += 1;
+ }
+ if (unlikely(tx_stat & 0x00000200)) {
+ netdev->stats.collisions++;
+ netdev->stats.tx_aborted_errors++;
+ }
+ }
+ }
+}
+
+/* Increments the Rx error counters */
+static void
+smsc911x_rx_counterrors(struct smsc911x_data *pdata, unsigned int rxstat)
+{
+ struct net_device *netdev = pdata->netdev;
+ int crc_err = 0;
+
+ if (unlikely(rxstat & 0x00008000)) {
+ netdev->stats.rx_errors++;
+ if (unlikely(rxstat & 0x00000002)) {
+ netdev->stats.rx_crc_errors++;
+ crc_err = 1;
+ }
+ }
+ if (likely(!crc_err)) {
+ if (unlikely((rxstat & 0x00001020) == 0x00001020)) {
+ /* Frame type indicates length,
+ * and length error is set */
+ netdev->stats.rx_length_errors++;
+ }
+ if (rxstat & RX_STS_MCAST_)
+ netdev->stats.multicast++;
+ }
+}
+
+/* Quickly dumps bad packets */
+static void
+smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktbytes)
+{
+ unsigned int pktwords = (pktbytes + NET_IP_ALIGN + 3) >> 2;
+
+ if (likely(pktwords >= 4)) {
+ unsigned int timeout = 500;
+ unsigned int val;
+ smsc911x_reg_write(RX_DP_CTRL_RX_FFWD_, pdata, RX_DP_CTRL);
+ do {
+ udelay(1);
+ val = smsc911x_reg_read(pdata, RX_DP_CTRL);
+ } while (timeout-- && (val & RX_DP_CTRL_RX_FFWD_));
+
+ if (unlikely(timeout == 0))
+ SMSC_WARNING("Timed out waiting for RX FFWD "
+ "to finish, RX_DP_CTRL: 0x%08X", val);
+ } else {
+ unsigned int temp;
+ while (pktwords--)
+ temp = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+ }
+}
+
+/* NAPI poll function */
+static int smsc911x_poll(struct napi_struct *napi, int budget)
+{
+ struct smsc911x_data *pdata = container_of(napi, struct smsc911x_data, napi);
+ struct net_device *dev = pdata->netdev;
+ int npackets = 0;
+
+ while (npackets < budget) {
+ unsigned int pktlength;
+ unsigned int pktwords;
+ unsigned int rxstat = smsc911x_rx_get_rxstatus(pdata);
+
+ /* break out of while loop if there are no more packets waiting */
+ if (!rxstat) {
+ printk_debug("Stopping the RX poll\n");
+ break;
+ }
+
+ pktlength = ((rxstat & 0x3FFF0000) >> 16);
+ pktwords = (pktlength + NET_IP_ALIGN + 3) >> 2;
+ printk_debug("Going to read %i words (pktlen %i)\n",
+ pktwords, pktlength);
+
+ smsc911x_rx_counterrors(pdata, rxstat);
+
+ if (likely((rxstat & RX_STS_ES_) == 0)) {
+ struct sk_buff *skb;
+ skb = dev_alloc_skb(pktlength + NET_IP_ALIGN);
+ if (likely(skb)) {
+ skb->data = skb->head;
+ skb->tail = skb->head;
+ /* Align IP on 16B boundary */
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, pktlength - 4);
+ smsc911x_rx_readfifo(pdata,
+ (unsigned int *)skb->head,
+ pktwords);
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_NONE;
+ netif_receive_skb(skb);
+
+ /* Update counters */
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += (pktlength - 4);
+ dev->last_rx = jiffies;
+ npackets++;
+ continue;
+ } else {
+ SMSC_WARNING("Unable to allocate sk_buff "
+ "for rx packet, in PIO path");
+ dev->stats.rx_dropped++;
+ }
+ }
+ /* At this point, the packet is to be read out
+ * of the fifo and discarded */
+ smsc911x_rx_fastforward(pdata, pktlength);
+ }
+
+ dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ smsc911x_reg_write(INT_STS_RSFL_, pdata, INT_STS);
+
+ if (npackets < budget) {
+ unsigned int temp;
+ /* We processed all packets available. Tell NAPI it can
+ * stop polling then re-enable rx interrupts */
+ netif_rx_complete(dev, napi);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RSFL_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ }
+
+ /* Return total received packets */
+ return npackets;
+}
+
+/* Returns hash bit number for given MAC address
+ * Example:
+ * 01 00 5E 00 00 01 -> returns bit number 31 */
+static unsigned int smsc911x_hash(char addr[ETH_ALEN])
+{
+ unsigned int crc;
+ unsigned int result;
+
+ crc = ether_crc(ETH_ALEN, addr);
+ result = (crc >> 26) & 0x3f;
+
+ return result;
+}
+
+static void smsc911x_rx_multicast_update(struct smsc911x_data *pdata)
+{
+ /* Performs the multicast & mac_cr update. This is called when
+ * safe on the current hardware, and with the phy_lock held */
+ unsigned int mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= pdata->set_bits_mask;
+ mac_cr &= ~(pdata->clear_bits_mask);
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+ smsc911x_mac_write(pdata, HASHH, pdata->hashhi);
+ smsc911x_mac_write(pdata, HASHL, pdata->hashlo);
+}
+
+static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
+{
+ unsigned int mac_cr;
+
+ /* This function is only called for older LAN911x devices
+ * (revA or revB), where MAC_CR, HASHH and HASHL should not
+ * be modified during Rx - newer devices immediately update the
+ * registers.
+ *
+ * This is called from interrupt context */
+
+ spin_lock(&pdata->phy_lock);
+
+ /* Check Rx has stopped */
+ if (smsc911x_mac_read(pdata, MAC_CR) & MAC_CR_RXEN_)
+ SMSC_WARNING("Rx not stopped\n");
+
+ /* Perform the update - safe to do now Rx has stopped */
+ smsc911x_rx_multicast_update(pdata);
+
+ /* Re-enable Rx */
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= MAC_CR_RXEN_;
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+
+ pdata->multicast_update_pending = 0;
+
+ spin_unlock(&pdata->phy_lock);
+}
+
+/* Sets the device MAC address to dev_addr, called with phy_lock held */
+static void
+smsc911x_set_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6])
+{
+ u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4];
+ u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) |
+ (dev_addr[1] << 8) | dev_addr[0];
+
+ smsc911x_mac_write(pdata, ADDRH, mac_high16);
+ smsc911x_mac_write(pdata, ADDRL, mac_low32);
+}
+
+static int smsc911x_open(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int timeout;
+ unsigned int temp;
+ unsigned int intcfg = 0;
+ struct sockaddr addr;
+
+ /* Reset the LAN911x */
+ smsc911x_reg_write(HW_CFG_SRST_, pdata, HW_CFG);
+ timeout = 10;
+ do {
+ udelay(10);
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ } while ((--timeout) && (temp & HW_CFG_SRST_));
+
+ if (unlikely(temp & HW_CFG_SRST_)) {
+ printk_err("Failed to complete reset");
+ return -ENODEV;
+ }
+
+ smsc911x_reg_write(0x00050000, pdata, HW_CFG);
+ smsc911x_reg_write(0x006E3740, pdata, AFC_CFG);
+
+ /* Make sure EEPROM has finished loading before setting GPIO_CFG */
+ timeout = 50;
+ while ((timeout--) &&
+ (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_)) {
+ udelay(10);
+ }
+
+ if (unlikely(timeout == 0)) {
+ SMSC_WARNING("Timed out waiting for EEPROM "
+ "busy bit to clear");
+ }
+#if USE_DEBUG >= 1
+ smsc911x_reg_write(0x00670700, pdata, GPIO_CFG);
+#else
+ smsc911x_reg_write(0x70070000, pdata, GPIO_CFG);
+#endif
+
+ /* Initialise irqs, but leave all sources disabled */
+ smsc911x_reg_write(0, pdata, INT_EN);
+ smsc911x_reg_write(0xFFFFFFFF, pdata, INT_STS);
+
+ /* Set interrupt deassertion to 100uS */
+ //intcfg = ((0x38 << 24) | INT_CFG_IRQ_EN_);
+ intcfg = ((0x00 << 24) | INT_CFG_IRQ_EN_);
+ // PPH modified intcfg = ((10 << 24) | INT_CFG_IRQ_EN_);
+
+ if (pdata->irq_polarity) {
+ SMSC_TRACE("irq polarity: active high");
+ intcfg |= INT_CFG_IRQ_POL_;
+ } else {
+ SMSC_TRACE("irq polarity: active low");
+ }
+
+ if (pdata->irq_type) {
+ SMSC_TRACE("irq type: push-pull");
+ intcfg |= INT_CFG_IRQ_TYPE_;
+ } else {
+ SMSC_TRACE("irq type: open drain");
+ }
+
+ smsc911x_reg_write(intcfg, pdata, INT_CFG);
+
+ printk_debug("Testing irq handler using IRQ %d\n", dev->irq);
+ pdata->software_irq_signal = 0;
+ smp_wmb();
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_SW_INT_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ timeout = 1000;
+ while (timeout--) {
+ smp_rmb();
+ if (pdata->software_irq_signal)
+ break;
+ msleep(1);
+ }
+
+ if (!pdata->software_irq_signal) {
+ printk(KERN_WARNING "%s: ISR failed signaling test (IRQ %d)\n",
+ dev->name, dev->irq);
+ return -ENODEV;
+ }
+
+ printk_debug("IRQ handler passed test using IRQ %d\n", dev->irq);
+ netif_carrier_off(dev);
+
+ if (!smsc911x_phy_initialise(dev)) {
+ printk_err("Failed to initialize the PHY");
+ return -ENODEV;
+ }
+
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ temp &= HW_CFG_TX_FIF_SZ_;
+ temp |= HW_CFG_SF_;
+ smsc911x_reg_write(temp, pdata, HW_CFG);
+
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ temp &= ~(FIFO_INT_RX_STS_LEVEL_);
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+
+ /* set RX Data offset to 2 bytes for alignment */
+ smsc911x_reg_write((2 << 8), pdata, RX_CFG);
+
+ /* enable the polling before enabling the interrupts */
+ napi_enable(&pdata->napi);
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_ | INT_EN_RDFL_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ spin_lock_irq(&pdata->phy_lock);
+
+ /*
+ * Reenable the full duplex mode, otherwise the TX engine will generate
+ * status errors (Luis Galdos)
+ */
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp |= (MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_ | MAC_CR_FDPX_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ smsc911x_reg_write(TX_CFG_TX_ON_, pdata, TX_CFG);
+
+ /* Set the MAC once again */
+ memcpy(addr.sa_data, dev->dev_addr, dev->addr_len);
+ if(smsc911x_set_mac(dev, &addr))
+ printk_err("Couldn't set the MAC address.\n");
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Entry point for stopping the interface */
+static int smsc911x_stop(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ printk_info("Stopping the interface\n");
+
+ napi_disable(&pdata->napi);
+
+ /* disable interrupts */
+ smsc911x_reg_write(0, pdata, INT_EN);
+
+ pdata->stop_link_poll = 1;
+ del_timer_sync(&pdata->link_poll_timer);
+
+ netif_stop_queue(dev);
+
+ /* At this point all Rx and Tx activity is stopped */
+ dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ smsc911x_tx_update_txcounters(pdata);
+
+ /* Stop sending data after the last transmission */
+ smsc911x_reg_write(TX_CFG_STOP_TX_, pdata, TX_CFG);
+
+ SMSC_TRACE("Interface stopped");
+ return 0;
+}
+
+/* Entry point for transmitting a packet */
+static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int freespace;
+ unsigned int tx_cmd_a;
+ unsigned int tx_cmd_b;
+ unsigned int temp;
+ u32 wrsz;
+ u32 bufp;
+
+ freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
+
+ if (unlikely(freespace < TX_FIFO_LOW_THRESHOLD))
+ SMSC_WARNING("Tx data fifo low, space available: %d",
+ freespace);
+
+ /* Word alignment adjustment */
+ tx_cmd_a = ((((unsigned int)(skb->data)) & 0x03) << 16);
+ tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ tx_cmd_a |= (unsigned int)skb->len;
+
+ tx_cmd_b = ((unsigned int)skb->len) << 16;
+ tx_cmd_b |= (unsigned int)skb->len;
+
+ smsc911x_reg_write(tx_cmd_a, pdata, TX_DATA_FIFO);
+ smsc911x_reg_write(tx_cmd_b, pdata, TX_DATA_FIFO);
+
+ bufp = ((u32)skb->data) & 0xFFFFFFFC;
+ wrsz = (u32)skb->len + 3;
+ wrsz += ((u32)skb->data) & 0x3;
+ wrsz >>= 2;
+
+ smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+ freespace -= (skb->len + 32);
+ dev_kfree_skb(skb);
+ dev->trans_start = jiffies;
+
+ if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
+ smsc911x_tx_update_txcounters(pdata);
+
+ if (freespace < TX_FIFO_LOW_THRESHOLD) {
+ netif_stop_queue(dev);
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp &= 0x00FFFFFF;
+ temp |= 0x32000000;
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+/* Entry point for getting status counters */
+static struct net_device_stats *smsc911x_get_stats(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ smsc911x_tx_update_txcounters(pdata);
+ dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ return &dev->stats;
+}
+
+/* Entry point for setting addressing modes */
+static void smsc911x_set_multicast_list(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Enabling promiscuous mode */
+ pdata->set_bits_mask = MAC_CR_PRMS_;
+ pdata->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* Enabling all multicast mode */
+ pdata->set_bits_mask = MAC_CR_MCPAS_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->mc_count > 0) {
+ /* Enabling specific multicast addresses */
+ unsigned int hash_high = 0;
+ unsigned int hash_low = 0;
+ unsigned int count = 0;
+ struct dev_mc_list *mc_list = dev->mc_list;
+
+ pdata->set_bits_mask = MAC_CR_HPFILT_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ while (mc_list) {
+ count++;
+ if ((mc_list->dmi_addrlen) == ETH_ALEN) {
+ unsigned int bitnum =
+ smsc911x_hash(mc_list->dmi_addr);
+ unsigned int mask = 0x01 << (bitnum & 0x1F);
+ if (bitnum & 0x20)
+ hash_high |= mask;
+ else
+ hash_low |= mask;
+ } else {
+ SMSC_WARNING("dmi_addrlen != 6");
+ }
+ mc_list = mc_list->next;
+ }
+ if (count != (unsigned int)dev->mc_count)
+ SMSC_WARNING("mc_count != dev->mc_count");
+
+ pdata->hashhi = hash_high;
+ pdata->hashlo = hash_low;
+ } else {
+ /* Enabling local MAC address only */
+ pdata->set_bits_mask = 0;
+ pdata->clear_bits_mask =
+ (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ }
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+
+ if (pdata->generation <= 1) {
+ /* Older hardware revision - cannot change these flags while
+ * receiving data */
+ if (!pdata->multicast_update_pending) {
+ unsigned int temp;
+ SMSC_TRACE("scheduling mcast update");
+ pdata->multicast_update_pending = 1;
+
+ /* Request the hardware to stop, then perform the
+ * update when we get an RX_STOP interrupt */
+ smsc911x_reg_write(INT_STS_RXSTOP_INT_, pdata, INT_STS);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RXSTOP_INT_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp &= ~(MAC_CR_RXEN_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ } else {
+ /* There is another update pending, this should now
+ * use the newer values */
+ }
+ } else {
+ /* Newer hardware revision - can write immediately */
+ smsc911x_rx_multicast_update(pdata);
+ }
+
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int intsts;
+ unsigned int inten;
+ unsigned int temp;
+ unsigned int intcfg;
+ int serviced = IRQ_NONE;
+
+ intcfg = smsc911x_reg_read(pdata, INT_CFG);
+ intsts = smsc911x_reg_read(pdata, INT_STS);
+ inten = smsc911x_reg_read(pdata, INT_EN);
+
+ printk_debug("New IRQ: intsts 0x%08x\n", intsts);
+
+ if ((intcfg & (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) != (INT_CFG_IRQ_INT_ |
+ INT_CFG_IRQ_EN_))
+ return serviced;
+
+
+ if (unlikely(intsts & inten & INT_STS_SW_INT_)) {
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_SW_INT_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ smsc911x_reg_write(INT_STS_SW_INT_, pdata, INT_STS);
+ pdata->software_irq_signal = 1;
+ smp_wmb();
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXSTOP_INT_)) {
+ /* Called when there is a multicast update scheduled and
+ * it is now safe to complete the update */
+ SMSC_TRACE("RX Stop interrupt");
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RXSTOP_INT_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ smsc911x_reg_write(INT_STS_RXSTOP_INT_, pdata, INT_STS);
+ smsc911x_rx_multicast_update_workaround(pdata);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (intsts & inten & INT_STS_TDFA_) {
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+ smsc911x_reg_write(INT_STS_TDFA_, pdata, INT_STS);
+ netif_wake_queue(dev);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXE_)) {
+ smsc911x_reg_write(INT_STS_RXE_, pdata, INT_STS);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (likely(intsts & inten & INT_STS_RSFL_)) {
+ if (likely(netif_rx_schedule_prep(dev, &pdata->napi))) {
+ /* Disable Rx interrupts and schedule NAPI poll */
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RSFL_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ __netif_rx_schedule(dev, &pdata->napi);
+ }
+
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_PHY_INT_)) {
+ smsc911x_reg_write(INT_STS_PHY_INT_, pdata, INT_STS);
+ spin_lock(&pdata->phy_lock);
+ temp = smsc911x_phy_read(pdata, MII_INTSTS);
+ spin_unlock(&pdata->phy_lock);
+ SMSC_TRACE("PHY interrupt, sts 0x%04X", (u16)temp);
+ smsc911x_phy_update_linkmode(dev, 0);
+ serviced = IRQ_HANDLED;
+ }
+ return serviced;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void smsc911x_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smsc911x_irqhandler(0, dev);
+ enable_irq(dev->irq);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+/* Standard ioctls for mii-tool */
+static int smsc911x_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int chg_in_duplex;
+ int ret;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+ ret = generic_mii_ioctl(&pdata->mii, if_mii(ifr), cmd, &chg_in_duplex);
+ if ((ret == 0) && (chg_in_duplex != 0))
+ smsc911x_phy_update_duplex(dev);
+
+ return ret;
+}
+
+static int
+smsc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+ return mii_ethtool_gset(&pdata->mii, cmd);
+}
+
+static int
+smsc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return mii_ethtool_sset(&pdata->mii, cmd);
+}
+
+static void smsc911x_ethtool_getdrvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strncpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver));
+ strncpy(info->version, SMSC_DRV_VERSION, sizeof(info->version));
+ strncpy(info->bus_info, dev->dev.bus_id, sizeof(info->bus_info));
+}
+
+static int smsc911x_ethtool_nwayreset(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return mii_nway_restart(&pdata->mii);
+}
+
+static u32 smsc911x_ethtool_getmsglevel(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ return pdata->msg_enable;
+}
+
+static void smsc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ pdata->msg_enable = level;
+}
+
+static int smsc911x_ethtool_getregslen(struct net_device *dev)
+{
+ return (((E2P_CMD - ID_REV) / 4 + 1) + (WUCSR - MAC_CR) + 1 + 32) *
+ sizeof(u32);
+}
+
+static void
+smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+ unsigned int i;
+ unsigned int j = 0;
+ u32 *data = buf;
+
+ regs->version = pdata->idrev;
+ for (i = ID_REV; i <= E2P_CMD; i += (sizeof(u32)))
+ data[j++] = smsc911x_reg_read(pdata, i);
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ for (i = MAC_CR; i <= WUCSR; i++)
+ data[j++] = smsc911x_mac_read(pdata, i);
+ for (i = 0; i <= 31; i++)
+ data[j++] = smsc911x_phy_read(pdata, i);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
+{
+ unsigned int temp = smsc911x_reg_read(pdata, GPIO_CFG);
+ temp &= ~GPIO_CFG_EEPR_EN_;
+ smsc911x_reg_write(temp, pdata, GPIO_CFG);
+ msleep(1);
+}
+
+static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op)
+{
+ int timeout = 100;
+ u32 e2cmd;
+
+ SMSC_TRACE("op 0x%08x", op);
+ if (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING("Busy at start");
+ return -EBUSY;
+ }
+
+ e2cmd = op | E2P_CMD_EPC_BUSY_;
+ smsc911x_reg_write(e2cmd, pdata, E2P_CMD);
+
+ do {
+ msleep(1);
+ e2cmd = smsc911x_reg_read(pdata, E2P_CMD);
+ } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--));
+
+ if (!timeout) {
+ SMSC_TRACE("TIMED OUT");
+ return -EAGAIN;
+ }
+
+ if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+ SMSC_TRACE("Error occured during eeprom operation");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smsc911x_eeprom_read_location(struct smsc911x_data *pdata,
+ u8 address, u8 *data)
+{
+ u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+ int ret;
+
+ SMSC_TRACE("address 0x%x", address);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret)
+ data[address] = smsc911x_reg_read(pdata, E2P_DATA);
+
+ return ret;
+}
+
+static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
+ u8 address, u8 data)
+{
+ u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+ int ret;
+
+ SMSC_TRACE("address 0x%x, data 0x%x", address, data);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret) {
+ op = E2P_CMD_EPC_CMD_WRITE_ | address;
+ smsc911x_reg_write((u32)data, pdata, E2P_DATA);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+ }
+
+ return ret;
+}
+
+static int smsc911x_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return SMSC911X_EEPROM_SIZE;
+}
+
+static int smsc911x_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ u8 eeprom_data[SMSC911X_EEPROM_SIZE];
+ int len;
+ int i;
+
+ smsc911x_eeprom_enable_access(pdata);
+
+ len = min(eeprom->len, SMSC911X_EEPROM_SIZE);
+ for (i = 0; i < len; i++) {
+ int ret = smsc911x_eeprom_read_location(pdata, i, eeprom_data);
+ if (ret < 0) {
+ eeprom->len = 0;
+ return ret;
+ }
+ }
+
+ memcpy(data, &eeprom_data[eeprom->offset], len);
+ eeprom->len = len;
+ return 0;
+}
+
+static int smsc911x_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ int ret;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ smsc911x_eeprom_enable_access(pdata);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWEN_);
+ ret = smsc911x_eeprom_write_location(pdata, eeprom->offset, *data);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWDS_);
+
+ /* Single byte write, according to man page */
+ eeprom->len = 1;
+
+ return ret;
+}
+
+static int smsc911x_ethtool_set_wol(struct net_device *dev,
+ struct ethtool_wolinfo *wol)
+{
+ struct smsc911x_data *pdata;
+
+ /* Check for unsupported options */
+ if (wol->wolopts & (WAKE_MAGICSECURE | WAKE_UCAST | WAKE_MCAST
+ | WAKE_BCAST | WAKE_ARP))
+ return -EINVAL;
+
+ pdata = netdev_priv(dev);
+
+ /* When disable the WOL options need to disable the PHY-interrupts too */
+ if (!wol->wolopts) {
+ printk_pmdbg("[ WOL ] Disabling all sources\n");
+ pdata->pmt_ctrl &= ~(PMT_CTRL_WOL_EN_ | PMT_CTRL_ED_EN_);
+ pdata->phy_intmsk &= ~PHY_INTMSK_ENERGYON_;
+ pdata->mac_wucsr = 0;
+ goto exit_set_wol;
+ }
+
+ /*
+ * For the magic packet we MUST configure the MAC too, but we can't do it
+ * at this point, cause the controller stops working.
+ */
+ if (wol->wolopts & WAKE_MAGIC) {
+ printk_pmdbg("WOL: Enabling magic frame\n");
+ pdata->mac_wucsr |= WUCSR_MPEN_;
+ pdata->pmt_ctrl |= PMT_CTRL_WOL_EN_;
+ }
+
+ /* For the PHY-wakeup we must use the energy detection */
+ if (wol->wolopts & WAKE_PHY) {
+ printk_pmdbg("[ WOL ] Enabling PHY energy\n");
+ pdata->phy_intmsk |= PHY_INTMSK_ENERGYON_;
+ pdata->pmt_ctrl |= PMT_CTRL_ED_EN_;
+ }
+
+ exit_set_wol:
+ return 0;
+}
+
+/* Function for getting the infos about the WOL */
+static void smsc911x_ethtool_get_wol(struct net_device *net_dev,
+ struct ethtool_wolinfo *wol)
+{
+ /* Only for magic and PHY power detection available up now */
+ wol->supported = WAKE_MAGIC | WAKE_PHY;
+}
+
+static struct ethtool_ops smsc911x_ethtool_ops = {
+ .get_settings = smsc911x_ethtool_getsettings,
+ .set_settings = smsc911x_ethtool_setsettings,
+ .get_link = ethtool_op_get_link,
+ .get_drvinfo = smsc911x_ethtool_getdrvinfo,
+ .nway_reset = smsc911x_ethtool_nwayreset,
+ .get_msglevel = smsc911x_ethtool_getmsglevel,
+ .set_msglevel = smsc911x_ethtool_setmsglevel,
+ .get_regs_len = smsc911x_ethtool_getregslen,
+ .get_regs = smsc911x_ethtool_getregs,
+ .get_eeprom_len = smsc911x_ethtool_get_eeprom_len,
+ .get_eeprom = smsc911x_ethtool_get_eeprom,
+ .set_eeprom = smsc911x_ethtool_set_eeprom,
+ .get_wol = smsc911x_ethtool_get_wol,
+ .set_wol = smsc911x_ethtool_set_wol,
+};
+
+
+static int smsc911x_set_mac(struct net_device *dev, void *addr)
+{
+ unsigned int reg;
+ int retval;
+ unsigned long flags;
+ struct smsc911x_data *pdata;
+ unsigned int low, high;
+ struct sockaddr *paddr = addr;
+
+ printk_debug("Set mac called\n");
+
+ pdata = netdev_priv(dev);
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+
+ /* First check that the MAC is not busy */
+ reg = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(reg & MAC_CSR_CMD_CSR_BUSY_)) {
+ printk_err("smsc911x_mac_read failed, MAC busy at entry");
+ retval = -EBUSY;
+ goto exit_unlock;
+ }
+
+ /* Get the MAC address */
+ high = 0;
+ memcpy(&low, &(paddr->sa_data[0]), 4);
+ memcpy(&high, &(paddr->sa_data[4]), 2);
+ printk_debug("Going to set the MAC %04X%08X\n", high, low);
+
+ /* Now set the high address */
+ smsc911x_reg_write(high, pdata, MAC_CSR_DATA);
+ smsc911x_reg_write(ADDRH | MAC_CSR_CMD_CSR_BUSY_, pdata, MAC_CSR_CMD);
+ reg = smsc911x_reg_read(pdata, BYTE_TEST);
+ if (!smsc911x_mac_notbusy(pdata)) {
+ retval = -EBUSY;
+ goto exit_unlock;
+ }
+
+ /* First set the low address */
+ smsc911x_reg_write(low, pdata, MAC_CSR_DATA);
+ smsc911x_reg_write(ADDRL | MAC_CSR_CMD_CSR_BUSY_, pdata, MAC_CSR_CMD);
+ reg = smsc911x_reg_read(pdata, BYTE_TEST);
+ if (!smsc911x_mac_notbusy(pdata)) {
+ retval = -EBUSY;
+ goto exit_unlock;
+ }
+
+ /* And save the IP inside the driver structure */
+ memcpy(dev->dev_addr, paddr->sa_data, dev->addr_len);
+
+ printk_debug("MAC successful changed to %02X%08X\n",
+ smsc911x_mac_read(pdata, ADDRH),
+ smsc911x_mac_read(pdata, ADDRL));
+
+ retval = 0;
+
+ exit_unlock:
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ return retval;
+}
+
+/* Initializing private device structures */
+static int smsc911x_init(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ SMSC_TRACE("Driver Parameters:");
+ SMSC_TRACE("LAN base: 0x%08lX", (unsigned long)pdata->ioaddr);
+ SMSC_TRACE("IRQ: %d", dev->irq);
+ SMSC_TRACE("PHY will be autodetected.");
+
+ if (pdata->ioaddr == 0) {
+ SMSC_WARNING("pdata->ioaddr: 0x00000000");
+ return -ENODEV;
+ }
+
+ /* Default generation to zero (all workarounds apply) */
+ pdata->generation = 0;
+
+ pdata->idrev = smsc911x_reg_read(pdata, ID_REV);
+ if (((pdata->idrev >> 16) & 0xFFFF) == (pdata->idrev & 0xFFFF)) {
+ SMSC_WARNING("idrev top 16 bits equal to bottom 16 bits, "
+ "idrev: 0x%08X", pdata->idrev);
+ SMSC_TRACE("This may mean the chip is set for 32 bit while "
+ "the bus is reading as 16 bit");
+ return -ENODEV;
+ }
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01180000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE("LAN9118 Beacon identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 0;
+ break;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9118 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9118 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9118 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01170000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE("LAN9117 Beacon identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 0;
+ break;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9117 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9117 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9117 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01160000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9116 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9116 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9116 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01150000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9115 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9115 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9115 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x118A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9218 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9218 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x117A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9217 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9217 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x116A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9216 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9216 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x115A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9215 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9215 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x92100000UL:
+ case 0x92110000UL:
+ case 0x92200000UL:
+ case 0x92210000UL:
+ /* LAN9210/LAN9211/LAN9220/LAN9221 */
+ pdata->generation = 4;
+ break;
+
+ default:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ }
+
+ if (pdata->generation == 0)
+ SMSC_WARNING("This driver is not intended "
+ "for this chip revision");
+
+ ether_setup(dev);
+ dev->open = smsc911x_open;
+ dev->stop = smsc911x_stop;
+ dev->hard_start_xmit = smsc911x_hard_start_xmit;
+ dev->get_stats = smsc911x_get_stats;
+ dev->set_multicast_list = smsc911x_set_multicast_list;
+ dev->flags |= IFF_MULTICAST;
+ dev->do_ioctl = smsc911x_do_ioctl;
+ dev->set_mac_address = smsc911x_set_mac;
+ netif_napi_add(dev, &pdata->napi, smsc911x_poll, 64);
+ dev->ethtool_ops = &smsc911x_ethtool_ops;
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = smsc911x_poll_controller;
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+ pdata->mii.phy_id_mask = 0x1f;
+ pdata->mii.reg_num_mask = 0x1f;
+ pdata->mii.force_media = 0;
+ pdata->mii.full_duplex = 0;
+ pdata->mii.dev = dev;
+ pdata->mii.mdio_read = smsc911x_mdio_read;
+ pdata->mii.mdio_write = smsc911x_mdio_write;
+
+ pdata->msg_enable = NETIF_MSG_LINK;
+
+ return 0;
+}
+
+static int smsc911x_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct resource *res;
+
+ dev = platform_get_drvdata(pdev);
+ BUG_ON(!dev);
+ pdata = netdev_priv(dev);
+ BUG_ON(!pdata);
+ BUG_ON(!pdata->ioaddr);
+
+ SMSC_TRACE("Stopping driver.");
+ platform_set_drvdata(pdev, NULL);
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ release_mem_region(res->start, res->end - res->start);
+
+ iounmap(pdata->ioaddr);
+
+ free_netdev(dev);
+
+ return 0;
+}
+
+static int smsc911x_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct resource *res;
+ unsigned int intcfg = 0;
+ int res_size;
+ int retval;
+
+ printk(KERN_INFO "%s: Driver version %s.\n", SMSC_CHIPNAME,
+ SMSC_DRV_VERSION);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ printk(KERN_WARNING "%s: Could not allocate resource.\n",
+ SMSC_CHIPNAME);
+ retval = -ENODEV;
+ goto out_0;
+ }
+ res_size = res->end - res->start;
+
+ if (!request_mem_region(res->start, res_size, SMSC_CHIPNAME)) {
+ retval = -EBUSY;
+ goto out_0;
+ }
+
+ dev = alloc_etherdev(sizeof(struct smsc911x_data));
+ if (!dev) {
+ printk(KERN_WARNING "%s: Could not allocate device.\n",
+ SMSC_CHIPNAME);
+ retval = -ENOMEM;
+ goto out_release_io_1;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ pdata = netdev_priv(dev);
+ pdata->netdev = dev;
+
+ dev->irq = platform_get_irq(pdev, 0);
+ pdata->ioaddr = ioremap_nocache(res->start, res_size);
+
+ /* copy config parameters across if present, otherwise pdata
+ * defaults to zeros */
+ if (pdev->dev.platform_data) {
+ struct smc911x_platdata *config = pdev->dev.platform_data;
+ pdata->irq_polarity = config->irq_polarity;
+ pdata->irq_flags = config->irq_flags;
+ }
+
+ if (pdata->ioaddr == NULL) {
+ SMSC_WARNING("Error smsc911x base address invalid");
+ retval = -ENOMEM;
+ goto out_free_netdev_2;
+ }
+
+ retval = smsc911x_init(dev);
+ if (retval < 0)
+ goto out_unmap_io_3;
+
+ /* configure irq polarity and type before connecting isr */
+ if (pdata->irq_polarity)
+ intcfg |= INT_CFG_IRQ_POL_;
+
+ /*
+ * @XXX: The "irq_type" is not used at this moment, because we are using
+ * the same platform-data as the driver from the Vanilla-kernel.
+ * (Luis Galdos)
+ */
+ if (pdata->irq_type)
+ intcfg |= INT_CFG_IRQ_TYPE_;
+
+ smsc911x_reg_write(intcfg, pdata, INT_CFG);
+
+ retval = request_irq(dev->irq, smsc911x_irqhandler,
+ pdata->irq_flags,
+ SMSC_CHIPNAME, dev);
+ if (retval) {
+ SMSC_WARNING("Unable to claim requested irq: %d", dev->irq);
+ goto out_unmap_io_3;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ retval = register_netdev(dev);
+ if (retval) {
+ SMSC_WARNING("Error %i registering device", retval);
+ goto out_unset_drvdata_4;
+ } else {
+ SMSC_TRACE("Network interface: \"%s\"", dev->name);
+ }
+
+ spin_lock_init(&pdata->phy_lock);
+
+ spin_lock_irq(&pdata->phy_lock);
+
+ /* Check if mac address has been specified when bringing interface up */
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ smsc911x_set_mac(dev, dev->dev_addr);
+ SMSC_TRACE("MAC Address is specified by configuration");
+ } else {
+ /* Try reading mac address from device. if EEPROM is present
+ * it will already have been set */
+ u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH);
+ u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL);
+ dev->dev_addr[0] = (u8)(mac_low32);
+ dev->dev_addr[1] = (u8)(mac_low32 >> 8);
+ dev->dev_addr[2] = (u8)(mac_low32 >> 16);
+ dev->dev_addr[3] = (u8)(mac_low32 >> 24);
+ dev->dev_addr[4] = (u8)(mac_high16);
+ dev->dev_addr[5] = (u8)(mac_high16 >> 8);
+
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ /* eeprom values are valid so use them */
+ SMSC_TRACE("Mac Address is read from LAN911x EEPROM");
+ } else {
+ /* eeprom values are invalid, generate random MAC */
+ random_ether_addr(dev->dev_addr);
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+ SMSC_TRACE("MAC Address is set to random_ether_addr");
+ }
+ }
+
+ spin_unlock_irq(&pdata->phy_lock);
+
+ printk_info("%s: MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+ /* Enable the wakeup over this device (Luis Galdos) */
+ device_init_wakeup(&pdev->dev, 1);
+ device_set_wakeup_enable(&pdev->dev, 0);
+ return 0;
+
+out_unset_drvdata_4:
+ platform_set_drvdata(pdev, NULL);
+ free_irq(dev->irq, dev);
+out_unmap_io_3:
+ iounmap(pdata->ioaddr);
+out_free_netdev_2:
+ free_netdev(dev);
+out_release_io_1:
+ release_mem_region(res->start, res->end - res->start);
+out_0:
+ return retval;
+}
+
+/* Enter in the suspend mode */
+#if defined(CONFIG_PM)
+
+/*
+ * For the mode D1 we MUST left the interrupts enabled
+ */
+static int smsc911x_drv_state_wakeup(struct smsc911x_data *pdata, int mode)
+{
+ int retval;
+ unsigned long regval;
+
+ retval = 0;
+
+ if (mode != 1 && mode != 2)
+ return -EINVAL;
+
+ /* Clear already received WUs */
+ regval = smsc911x_mac_read(pdata, WUCSR);
+ regval &= ~(WUCSR_MPR_ | WUCSR_WUFR_);
+ regval |= pdata->mac_wucsr; /* Magic packet enable 'WUCSR_MPEN_' */
+ printk_pmdbg("[ SUSP ] WUCSR 0x%08lx\n", regval);
+ smsc911x_mac_write(pdata, WUCSR, regval);
+
+ /* For the D2 we must enable the PHY interrupt for the energy detection */
+ regval = smsc911x_reg_read(pdata, INT_EN);
+ regval |= (INT_EN_PME_INT_EN_ | INT_EN_PHY_INT_EN_);
+ printk_pmdbg("[ SUSP ] INT_EN 0x%08lx\n", regval);
+ smsc911x_reg_write(regval, pdata, INT_EN);
+
+ if (mode /* @FIXME: Enabled only for D2 */) {
+ u16 phy_mode;
+
+ phy_mode = smsc911x_phy_read(pdata, MII_INTMSK);
+ phy_mode |= PHY_INTMSK_ENERGYON_;
+ smsc911x_phy_write(pdata, MII_INTMSK, phy_mode);
+ }
+
+ /* Clear the PM mode and clear the current wakeup status */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ regval &= ~PMT_CTRL_PM_MODE_;
+ regval |= PMT_CTRL_WUPS_;
+ printk_pmdbg("[ SUSP ] PMT_CTRL 0x%08lx\n", regval);
+ smsc911x_reg_write(regval, pdata, PMT_CTRL);
+
+ /* Enable the PME at prior and the wake on LAN */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ regval |= pdata->pmt_ctrl; /* Enable the ENERGY detect or WOL interrupt */
+ regval |= PMT_CTRL_PME_EN_;
+
+ if (mode == 1)
+ regval |= PMT_CTRL_PM_MODE_D1_;
+ else
+ regval |= PMT_CTRL_PM_MODE_D2_;
+
+ printk_pmdbg("[ SUSP ] PMT_CTRL 0x%08lx\n", regval);
+ smsc911x_reg_write(regval, pdata, PMT_CTRL);
+
+ return retval;
+}
+
+/* For the state D2 we must disable the host-interrupts */
+static int smsc911x_drv_state_d2(struct smsc911x_data *pdata)
+{
+ unsigned long regval;
+
+ /* Disable the interrupts of the controller */
+ regval = smsc911x_reg_read(pdata, INT_CFG);
+ regval &= ~INT_CFG_IRQ_EN_;
+ smsc911x_reg_write(regval, pdata, INT_CFG);
+
+ /* Set the phy to the power down mode */
+ regval = smsc911x_phy_read(pdata, MII_BMCR);
+ regval |= BMCR_PDOWN;
+ smsc911x_phy_write(pdata, MII_BMCR, regval);
+
+ /*
+ * Enter into the power mode D2 (the controller doesn't
+ * support the mode D3)
+ */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ regval &= ~PMT_CTRL_PM_MODE_;
+ regval |= PMT_CTRL_PM_MODE_D2_;
+ smsc911x_reg_write(regval, pdata, PMT_CTRL);
+
+ return 0;
+}
+
+static int smsc911x_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct net_device *ndev;
+ struct smsc911x_data *pdata;
+ int retval;
+
+ ndev = platform_get_drvdata(pdev);
+ pdata = netdev_priv(ndev);
+
+ if (!ndev)
+ return -ENODEV;
+
+ /* @FIXME: Implement the other supported power modes of the smsc911x */
+ if (state.event != PM_EVENT_SUSPEND)
+ return -ENOTSUPP;
+
+ if (netif_running(ndev)) {
+
+ /* The below code is coming from the WinCE guys */
+ netif_device_detach(ndev);
+
+ /*
+ * If configured as wakeup-source enter the mode D1 for packet
+ * detection using the standard IRQ-line
+ */
+ if (device_may_wakeup(&pdev->dev)) {
+
+ /*
+ * Sanity check for verifying that a wakeup-source was
+ * configured from the user space. If the energy-detect
+ * wakeup was enabled, then use the D2 for entering into the
+ * power mode
+ */
+ if (!(pdata->pmt_ctrl & (PMT_CTRL_WOL_EN_ | PMT_CTRL_ED_EN_))) {
+ printk_err("[ SUSP ] No WOL source defined.\n");
+ retval = -EINVAL;
+ goto err_attach;
+ }
+
+ /*
+ * By the WOL (magic packet, etc.) we can ONLY use the D1, but
+ * for the energy detect over the PHY we can change into D2
+ */
+ if (pdata->pmt_ctrl & PMT_CTRL_WOL_EN_) {
+ printk_pmdbg("[ SUSP ] Preparing D1 with wakeup\n");
+ smsc911x_drv_state_wakeup(pdata, 1);
+ } else {
+ /* @TEST: Use first only D1 for the wakups */
+ printk_pmdbg("[ SUSP ] Preparing D2 with wakeup\n");
+ smsc911x_drv_state_wakeup(pdata, 2);
+ }
+
+ enable_irq_wake(ndev->irq);
+
+ } else {
+ /*
+ * Enter into the power mode D2 (the controller doesn't
+ * support the mode D3)
+ */
+ smsc911x_drv_state_d2(pdata);
+ }
+ }
+
+ return 0;
+
+err_attach:
+ netif_device_attach(ndev);
+ return retval;
+}
+
+static int smsc911x_drv_resume(struct platform_device *pdev)
+{
+ int retval;
+ struct net_device *ndev;
+ unsigned long pmt_ctrl;
+
+ pmt_ctrl = 0;
+ ndev = platform_get_drvdata(pdev);
+ retval = 0;
+ if (ndev) {
+ struct smsc911x_data *pdata = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ unsigned long timeout;
+ unsigned long regval, pmt_ctrl;
+
+ /* Assert the byte test register for waking up */
+ smsc911x_reg_write(0x0, pdata, BYTE_TEST);
+
+ timeout = 100000;
+ do {
+ timeout--;
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ udelay(1);
+ } while (timeout && !(regval & PMT_CTRL_READY_));
+
+ if (!timeout) {
+ printk_err("Wakeup timeout by the controller\n");
+ retval = -EBUSY;
+ goto exit_resume;
+ }
+
+ /*
+ * Check if we received a PM interrupt
+ * Please take note that we are supporting ONLY the Wake On LAN
+ * interrupts, and not the energy-on
+ * (Luis Galdos)
+ */
+ pmt_ctrl = smsc911x_reg_read(pdata, PMT_CTRL);
+ regval = smsc911x_reg_read(pdata, INT_STS);
+ printk_pmdbg("[ WAKE ] PMT_CTRL 0x%08lx\n", pmt_ctrl);
+ printk_pmdbg("[ WAKE ] INT_STS 0x%08lx\n", regval);
+ if (regval & (INT_STS_PME_INT_ | INT_STS_PHY_INT_)) {
+
+ /* Disable the power management interrupts */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ pmt_ctrl = regval & (PMT_CTRL_WOL_EN_ | PMT_CTRL_ED_EN_);
+ regval &= ~(PMT_CTRL_WOL_EN_ | PMT_CTRL_PME_EN_ |
+ PMT_CTRL_ED_EN_);
+ smsc911x_reg_write(regval, pdata, PMT_CTRL);
+
+ /* Disable the PM interrupts */
+ regval = smsc911x_reg_read(pdata, INT_EN);
+ regval &= ~(INT_EN_PME_INT_EN_ | INT_EN_PHY_INT_EN_);
+ smsc911x_reg_write(regval, pdata, INT_EN);
+
+ /* Disable the wakeup-events on the MAC */
+ regval = smsc911x_mac_read(pdata, WUCSR);
+ regval &= ~(WUCSR_MPEN_);
+ smsc911x_mac_write(pdata, WUCSR, regval);
+ }
+
+ /* @XXX: Clear only the interrupts that were generated */
+ regval = (INT_STS_PME_INT_ | INT_STS_PHY_INT_);
+ smsc911x_reg_write(regval, pdata, INT_STS);
+
+ /* Set the controller into the state D0 */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ regval &= ~PMT_CTRL_PM_MODE_;
+ regval |= PMT_CTRL_PM_MODE_D0_;
+ smsc911x_reg_write(regval, pdata, PMT_CTRL);
+
+ /* Paranoic sanity checks */
+ regval = smsc911x_reg_read(pdata, PMT_CTRL);
+ if (regval & PMT_CTRL_PM_MODE_)
+ printk_err("PM mode isn't disabled (0x%04lx)\n", regval);
+
+ if (!(regval & PMT_CTRL_READY_)) {
+ retval = -EBUSY;
+ printk_err("Device is still NOT ready.\n");
+ goto exit_resume;
+ }
+
+ regval = smsc911x_phy_read(pdata, MII_BMCR);
+ regval &= ~BMCR_PDOWN;
+ smsc911x_phy_write(pdata, MII_BMCR, regval);
+
+ /* Reenable the interrupts now */
+ regval = smsc911x_reg_read(pdata, INT_CFG);
+ regval |= INT_CFG_IRQ_EN_;
+ smsc911x_reg_write(regval, pdata, INT_CFG);
+
+ /* Reset the wakeup control and status register */
+ smsc911x_mac_write(pdata, WUCSR, 0x00);
+
+ netif_device_attach(ndev);
+ }
+ }
+
+exit_resume:
+ return retval;
+}
+#else
+#define smsc911x_drv_suspend NULL
+#define smsc911x_drv_resume NULL
+#endif /* defined(CONFIG_PM) */
+
+static struct platform_driver smsc911x_driver = {
+ .probe = smsc911x_drv_probe,
+ .remove = smsc911x_drv_remove,
+ .suspend = smsc911x_drv_suspend,
+ .resume = smsc911x_drv_resume,
+ .driver = {
+ .name = SMSC_CHIPNAME,
+ },
+};
+
+/* Entry point for loading the module */
+static int __init smsc911x_init_module(void)
+{
+ printk(KERN_INFO "SMSC 911X: Starting the platform driver.\n");
+ return platform_driver_register(&smsc911x_driver);
+}
+
+/* entry point for unloading the module */
+static void __exit smsc911x_cleanup_module(void)
+{
+ platform_driver_unregister(&smsc911x_driver);
+}
+
+module_init(smsc911x_init_module);
+module_exit(smsc911x_cleanup_module);
diff --git a/drivers/net/smsc9118/smsc911x.h b/drivers/net/smsc9118/smsc911x.h
new file mode 100644
index 000000000000..a2ee96aeb989
--- /dev/null
+++ b/drivers/net/smsc9118/smsc911x.h
@@ -0,0 +1,393 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2007 SMSC
+ * Copyright (C) 2005 ARM
+ *
+ * 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 __SMSC911X_H__
+#define __SMSC911X_H__
+
+#if defined(CONFIG_MACH_CC9M2443JS) || defined(CONFIG_MACH_CCW9M2443JS)
+# define SMSC_CAN_USE_32BIT 0
+#else
+# define SMSC_CAN_USE_32BIT 1
+#endif
+
+
+//#define SMSC_CAN_USE_32BIT 1
+#define TX_FIFO_LOW_THRESHOLD (u32)1600
+#define SMSC911X_EEPROM_SIZE (u32)7
+#define USE_DEBUG 0
+//#define USE_DEBUG 2
+
+/* implements a PHY loopback test at initialisation time, to ensure a packet
+ * can be succesfully looped back */
+#define USE_PHY_WORK_AROUND
+
+/* 10/100 LED link-state inversion when media is disconnected */
+/* #define USE_LED1_WORK_AROUND */
+
+/* platform_device configuration data, should be assigned to
+ * the platform_device's dev.platform_data */
+struct smsc911x_platform_config {
+ unsigned int irq_polarity;
+ unsigned int irq_type;
+};
+
+#if USE_DEBUG >= 1
+#define SMSC_WARNING(fmt, args...) \
+ printk(KERN_EMERG "SMSC_WARNING: %s: " fmt "\n", \
+ __FUNCTION__ , ## args)
+#else
+#define SMSC_WARNING(msg, args...)
+#endif /* USE_DEBUG >= 1 */
+
+#if USE_DEBUG >= 2
+#define SMSC_TRACE(fmt,args...) \
+ printk(KERN_EMERG "SMSC_TRACE: %s: " fmt "\n", \
+ __FUNCTION__ , ## args)
+#else
+#define SMSC_TRACE(msg, args...)
+#endif /* USE_DEBUG >= 2 */
+
+/* SMSC911x registers and bitfields */
+#define RX_DATA_FIFO 0x00
+
+#define TX_DATA_FIFO 0x20
+#define TX_CMD_A_ON_COMP_ 0x80000000
+#define TX_CMD_A_BUF_END_ALGN_ 0x03000000
+#define TX_CMD_A_4_BYTE_ALGN_ 0x00000000
+#define TX_CMD_A_16_BYTE_ALGN_ 0x01000000
+#define TX_CMD_A_32_BYTE_ALGN_ 0x02000000
+#define TX_CMD_A_DATA_OFFSET_ 0x001F0000
+#define TX_CMD_A_FIRST_SEG_ 0x00002000
+#define TX_CMD_A_LAST_SEG_ 0x00001000
+#define TX_CMD_A_BUF_SIZE_ 0x000007FF
+#define TX_CMD_B_PKT_TAG_ 0xFFFF0000
+#define TX_CMD_B_ADD_CRC_DISABLE_ 0x00002000
+#define TX_CMD_B_DISABLE_PADDING_ 0x00001000
+#define TX_CMD_B_PKT_BYTE_LENGTH_ 0x000007FF
+
+#define RX_STATUS_FIFO 0x40
+#define RX_STS_ES_ 0x00008000
+#define RX_STS_MCAST_ 0x00000400
+
+#define RX_STATUS_FIFO_PEEK 0x44
+
+#define TX_STATUS_FIFO 0x48
+#define TX_STS_ES_ 0x00008000
+
+#define TX_STATUS_FIFO_PEEK 0x4C
+
+#define ID_REV 0x50
+#define ID_REV_CHIP_ID_ 0xFFFF0000
+#define ID_REV_REV_ID_ 0x0000FFFF
+
+#define INT_CFG 0x54
+#define INT_CFG_INT_DEAS_ 0xFF000000
+#define INT_CFG_INT_DEAS_CLR_ 0x00004000
+#define INT_CFG_INT_DEAS_STS_ 0x00002000
+#define INT_CFG_IRQ_INT_ 0x00001000
+#define INT_CFG_IRQ_EN_ 0x00000100
+#define INT_CFG_IRQ_POL_ 0x00000010
+#define INT_CFG_IRQ_TYPE_ 0x00000001
+
+#define INT_STS 0x58
+#define INT_STS_SW_INT_ 0x80000000
+#define INT_STS_TXSTOP_INT_ 0x02000000
+#define INT_STS_RXSTOP_INT_ 0x01000000
+#define INT_STS_RXDFH_INT_ 0x00800000
+#define INT_STS_RXDF_INT_ 0x00400000
+#define INT_STS_TX_IOC_ 0x00200000
+#define INT_STS_RXD_INT_ 0x00100000
+#define INT_STS_GPT_INT_ 0x00080000
+#define INT_STS_PHY_INT_ 0x00040000
+#define INT_STS_PME_INT_ 0x00020000
+#define INT_STS_TXSO_ 0x00010000
+#define INT_STS_RWT_ 0x00008000
+#define INT_STS_RXE_ 0x00004000
+#define INT_STS_TXE_ 0x00002000
+#define INT_STS_TDFU_ 0x00000800
+#define INT_STS_TDFO_ 0x00000400
+#define INT_STS_TDFA_ 0x00000200
+#define INT_STS_TSFF_ 0x00000100
+#define INT_STS_TSFL_ 0x00000080
+#define INT_STS_RXDF_ 0x00000040
+#define INT_STS_RDFL_ 0x00000020
+#define INT_STS_RSFF_ 0x00000010
+#define INT_STS_RSFL_ 0x00000008
+#define INT_STS_GPIO2_INT_ 0x00000004
+#define INT_STS_GPIO1_INT_ 0x00000002
+#define INT_STS_GPIO0_INT_ 0x00000001
+
+#define INT_EN 0x5C
+#define INT_EN_SW_INT_EN_ 0x80000000
+#define INT_EN_TXSTOP_INT_EN_ 0x02000000
+#define INT_EN_RXSTOP_INT_EN_ 0x01000000
+#define INT_EN_RXDFH_INT_EN_ 0x00800000
+#define INT_EN_TIOC_INT_EN_ 0x00200000
+#define INT_EN_RXD_INT_EN_ 0x00100000
+#define INT_EN_GPT_INT_EN_ 0x00080000
+#define INT_EN_PHY_INT_EN_ 0x00040000
+#define INT_EN_PME_INT_EN_ 0x00020000
+#define INT_EN_TXSO_EN_ 0x00010000
+#define INT_EN_RWT_EN_ 0x00008000
+#define INT_EN_RXE_EN_ 0x00004000
+#define INT_EN_TXE_EN_ 0x00002000
+#define INT_EN_TDFU_EN_ 0x00000800
+#define INT_EN_TDFO_EN_ 0x00000400
+#define INT_EN_TDFA_EN_ 0x00000200
+#define INT_EN_TSFF_EN_ 0x00000100
+#define INT_EN_TSFL_EN_ 0x00000080
+#define INT_EN_RXDF_EN_ 0x00000040
+#define INT_EN_RDFL_EN_ 0x00000020
+#define INT_EN_RSFF_EN_ 0x00000010
+#define INT_EN_RSFL_EN_ 0x00000008
+#define INT_EN_GPIO2_INT_ 0x00000004
+#define INT_EN_GPIO1_INT_ 0x00000002
+#define INT_EN_GPIO0_INT_ 0x00000001
+
+#define BYTE_TEST 0x64
+
+#define FIFO_INT 0x68
+#define FIFO_INT_TX_AVAIL_LEVEL_ 0xFF000000
+#define FIFO_INT_TX_STS_LEVEL_ 0x00FF0000
+#define FIFO_INT_RX_AVAIL_LEVEL_ 0x0000FF00
+#define FIFO_INT_RX_STS_LEVEL_ 0x000000FF
+
+#define RX_CFG 0x6C
+#define RX_CFG_RX_END_ALGN_ 0xC0000000
+#define RX_CFG_RX_END_ALGN4_ 0x00000000
+#define RX_CFG_RX_END_ALGN16_ 0x40000000
+#define RX_CFG_RX_END_ALGN32_ 0x80000000
+#define RX_CFG_RX_DMA_CNT_ 0x0FFF0000
+#define RX_CFG_RX_DUMP_ 0x00008000
+#define RX_CFG_RXDOFF_ 0x00001F00
+
+#define TX_CFG 0x70
+#define TX_CFG_TXS_DUMP_ 0x00008000
+#define TX_CFG_TXD_DUMP_ 0x00004000
+#define TX_CFG_TXSAO_ 0x00000004
+#define TX_CFG_TX_ON_ 0x00000002
+#define TX_CFG_STOP_TX_ 0x00000001
+
+#define HW_CFG 0x74
+#define HW_CFG_TTM_ 0x00200000
+#define HW_CFG_SF_ 0x00100000
+#define HW_CFG_TX_FIF_SZ_ 0x000F0000
+#define HW_CFG_TR_ 0x00003000
+#define HW_CFG_SRST_ 0x00000001
+
+/* only available on 115/117 */
+#define HW_CFG_PHY_CLK_SEL_ 0x00000060
+#define HW_CFG_PHY_CLK_SEL_INT_PHY_ 0x00000000
+#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ 0x00000020
+#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ 0x00000040
+#define HW_CFG_SMI_SEL_ 0x00000010
+#define HW_CFG_EXT_PHY_DET_ 0x00000008
+#define HW_CFG_EXT_PHY_EN_ 0x00000004
+#define HW_CFG_SRST_TO_ 0x00000002
+
+/* only available on 116/118 */
+#define HW_CFG_32_16_BIT_MODE_ 0x00000004
+
+#define RX_DP_CTRL 0x78
+#define RX_DP_CTRL_RX_FFWD_ 0x80000000
+
+#define RX_FIFO_INF 0x7C
+#define RX_FIFO_INF_RXSUSED_ 0x00FF0000
+#define RX_FIFO_INF_RXDUSED_ 0x0000FFFF
+
+#define TX_FIFO_INF 0x80
+#define TX_FIFO_INF_TSUSED_ 0x00FF0000
+#define TX_FIFO_INF_TDFREE_ 0x0000FFFF
+
+#define PMT_CTRL 0x84
+#define PMT_CTRL_PM_MODE_ 0x00003000
+#define PMT_CTRL_PM_MODE_D0_ 0x00000000
+#define PMT_CTRL_PM_MODE_D1_ 0x00001000
+#define PMT_CTRL_PM_MODE_D2_ 0x00002000
+#define PMT_CTRL_PM_MODE_D3_ 0x00003000
+#define PMT_CTRL_PHY_RST_ 0x00000400
+#define PMT_CTRL_WOL_EN_ 0x00000200
+#define PMT_CTRL_ED_EN_ 0x00000100
+#define PMT_CTRL_PME_TYPE_ 0x00000040
+#define PMT_CTRL_WUPS_ 0x00000030
+#define PMT_CTRL_WUPS_NOWAKE_ 0x00000000
+#define PMT_CTRL_WUPS_ED_ 0x00000010
+#define PMT_CTRL_WUPS_WOL_ 0x00000020
+#define PMT_CTRL_WUPS_MULTI_ 0x00000030
+#define PMT_CTRL_PME_IND_ 0x00000008
+#define PMT_CTRL_PME_POL_ 0x00000004
+#define PMT_CTRL_PME_EN_ 0x00000002
+#define PMT_CTRL_READY_ 0x00000001
+
+#define GPIO_CFG 0x88
+#define GPIO_CFG_LED3_EN_ 0x40000000
+#define GPIO_CFG_LED2_EN_ 0x20000000
+#define GPIO_CFG_LED1_EN_ 0x10000000
+#define GPIO_CFG_GPIO2_INT_POL_ 0x04000000
+#define GPIO_CFG_GPIO1_INT_POL_ 0x02000000
+#define GPIO_CFG_GPIO0_INT_POL_ 0x01000000
+#define GPIO_CFG_EEPR_EN_ 0x00700000
+#define GPIO_CFG_GPIOBUF2_ 0x00040000
+#define GPIO_CFG_GPIOBUF1_ 0x00020000
+#define GPIO_CFG_GPIOBUF0_ 0x00010000
+#define GPIO_CFG_GPIODIR2_ 0x00000400
+#define GPIO_CFG_GPIODIR1_ 0x00000200
+#define GPIO_CFG_GPIODIR0_ 0x00000100
+#define GPIO_CFG_GPIOD4_ 0x00000020
+#define GPIO_CFG_GPIOD3_ 0x00000010
+#define GPIO_CFG_GPIOD2_ 0x00000004
+#define GPIO_CFG_GPIOD1_ 0x00000002
+#define GPIO_CFG_GPIOD0_ 0x00000001
+
+#define GPT_CFG 0x8C
+#define GPT_CFG_TIMER_EN_ 0x20000000
+#define GPT_CFG_GPT_LOAD_ 0x0000FFFF
+
+#define GPT_CNT 0x90
+#define GPT_CNT_GPT_CNT_ 0x0000FFFF
+
+#define ENDIAN 0x98
+
+#define FREE_RUN 0x9C
+
+#define RX_DROP 0xA0
+
+#define MAC_CSR_CMD 0xA4
+#define MAC_CSR_CMD_CSR_BUSY_ 0x80000000
+#define MAC_CSR_CMD_R_NOT_W_ 0x40000000
+#define MAC_CSR_CMD_CSR_ADDR_ 0x000000FF
+
+#define MAC_CSR_DATA 0xA8
+
+#define AFC_CFG 0xAC
+#define AFC_CFG_AFC_HI_ 0x00FF0000
+#define AFC_CFG_AFC_LO_ 0x0000FF00
+#define AFC_CFG_BACK_DUR_ 0x000000F0
+#define AFC_CFG_FCMULT_ 0x00000008
+#define AFC_CFG_FCBRD_ 0x00000004
+#define AFC_CFG_FCADD_ 0x00000002
+#define AFC_CFG_FCANY_ 0x00000001
+
+#define E2P_CMD 0xB0
+#define E2P_CMD_EPC_BUSY_ 0x80000000
+#define E2P_CMD_EPC_CMD_ 0x70000000
+#define E2P_CMD_EPC_CMD_READ_ 0x00000000
+#define E2P_CMD_EPC_CMD_EWDS_ 0x10000000
+#define E2P_CMD_EPC_CMD_EWEN_ 0x20000000
+#define E2P_CMD_EPC_CMD_WRITE_ 0x30000000
+#define E2P_CMD_EPC_CMD_WRAL_ 0x40000000
+#define E2P_CMD_EPC_CMD_ERASE_ 0x50000000
+#define E2P_CMD_EPC_CMD_ERAL_ 0x60000000
+#define E2P_CMD_EPC_CMD_RELOAD_ 0x70000000
+#define E2P_CMD_EPC_TIMEOUT_ 0x00000200
+#define E2P_CMD_MAC_ADDR_LOADED_ 0x00000100
+#define E2P_CMD_EPC_ADDR_ 0x000000FF
+
+#define E2P_DATA 0xB4
+#define E2P_DATA_EEPROM_DATA_ 0x000000FF
+#define LAN_REGISTER_EXTENT 0x00000100
+
+/*
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ */
+#define MAC_CR 0x01
+#define MAC_CR_RXALL_ 0x80000000
+#define MAC_CR_HBDIS_ 0x10000000
+#define MAC_CR_RCVOWN_ 0x00800000
+#define MAC_CR_LOOPBK_ 0x00200000
+#define MAC_CR_FDPX_ 0x00100000
+#define MAC_CR_MCPAS_ 0x00080000
+#define MAC_CR_PRMS_ 0x00040000
+#define MAC_CR_INVFILT_ 0x00020000
+#define MAC_CR_PASSBAD_ 0x00010000
+#define MAC_CR_HFILT_ 0x00008000
+#define MAC_CR_HPFILT_ 0x00002000
+#define MAC_CR_LCOLL_ 0x00001000
+#define MAC_CR_BCAST_ 0x00000800
+#define MAC_CR_DISRTY_ 0x00000400
+#define MAC_CR_PADSTR_ 0x00000100
+#define MAC_CR_BOLMT_MASK_ 0x000000C0
+#define MAC_CR_DFCHK_ 0x00000020
+#define MAC_CR_TXEN_ 0x00000008
+#define MAC_CR_RXEN_ 0x00000004
+
+#define ADDRH 0x02
+
+#define ADDRL 0x03
+
+#define HASHH 0x04
+
+#define HASHL 0x05
+
+#define MII_ACC 0x06
+#define MII_ACC_PHY_ADDR_ 0x0000F800
+#define MII_ACC_MIIRINDA_ 0x000007C0
+#define MII_ACC_MII_WRITE_ 0x00000002
+#define MII_ACC_MII_BUSY_ 0x00000001
+
+#define MII_DATA 0x07
+
+#define FLOW 0x08
+#define FLOW_FCPT_ 0xFFFF0000
+#define FLOW_FCPASS_ 0x00000004
+#define FLOW_FCEN_ 0x00000002
+#define FLOW_FCBSY_ 0x00000001
+
+#define VLAN1 0x09
+
+#define VLAN2 0x0A
+
+#define WUFF 0x0B
+
+#define WUCSR 0x0C
+#define WUCSR_GUE_ 0x00000200
+#define WUCSR_WUFR_ 0x00000040
+#define WUCSR_MPR_ 0x00000020
+#define WUCSR_WAKE_EN_ 0x00000004
+#define WUCSR_MPEN_ 0x00000002
+
+/*
+ * Phy definitions (vendor-specific)
+ */
+#define LAN9118_PHY_ID 0x00C0001C
+
+#define MII_INTSTS 0x1D
+
+#define MII_INTMSK 0x1E
+#define PHY_INTMSK_AN_RCV_ (1 << 1)
+#define PHY_INTMSK_PDFAULT_ (1 << 2)
+#define PHY_INTMSK_AN_ACK_ (1 << 3)
+#define PHY_INTMSK_LNKDOWN_ (1 << 4)
+#define PHY_INTMSK_RFAULT_ (1 << 5)
+#define PHY_INTMSK_AN_COMP_ (1 << 6)
+#define PHY_INTMSK_ENERGYON_ (1 << 7)
+#define PHY_INTMSK_DEFAULT_ (PHY_INTMSK_ENERGYON_ | \
+ PHY_INTMSK_AN_COMP_ | \
+ PHY_INTMSK_RFAULT_ | \
+ PHY_INTMSK_LNKDOWN_)
+
+#define ADVERTISE_PAUSE_ALL (ADVERTISE_PAUSE_CAP | \
+ ADVERTISE_PAUSE_ASYM)
+
+#define LPA_PAUSE_ALL (LPA_PAUSE_CAP | \
+ LPA_PAUSE_ASYM)
+
+#endif /* __SMSC911X_H__ */
diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c
new file mode 100644
index 000000000000..6af63187bd3a
--- /dev/null
+++ b/drivers/net/smsc911x.c
@@ -0,0 +1,2253 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2007 SMSC
+ * Copyright (C) 2005 ARM
+ *
+ * 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.
+ *
+ ***************************************************************************
+ * Rewritten, heavily based on smsc911x simple driver by SMSC.
+ * Partly uses io macros from smc91x.c by Nicolas Pitre
+ *
+ * Supported devices:
+ * LAN9115, LAN9116, LAN9117, LAN9118
+ * LAN9215, LAN9216, LAN9217, LAN9218
+ *
+ * History:
+ * 05/05/2005 bahadir.balban@arm.com
+ * - Transition to linux coding style
+ * - Platform driver and module interface
+ *
+ * 17/07/2006 steve.glendinning@smsc.com
+ * - Added support for LAN921x family
+ * - Added workaround for multicast filters
+ *
+ * 31/07/2006 steve.glendinning@smsc.com
+ * - Removed tasklet, using NAPI poll instead
+ * - Multiple device support
+ * - Large tidy-up following feedback from netdev list
+ *
+ * 03/08/2006 steve.glendinning@smsc.com
+ * - Added ethtool support
+ * - Convert to use generic MII interface
+ *
+ * 04/08/2006 bahadir.balban@arm.com
+ * - Added ethtool eeprom r/w support
+ *
+ * 17/06/2007 steve.glendinning@smsc.com
+ * - Incorporate changes from Bill Gatliff and Russell King
+ *
+ * 04/07/2007 steve.glendinning@smsc.com
+ * - move irq configuration to platform_device
+ * - fix link poller after interface is stopped and restarted
+ *
+ * 13/07/2007 bahadir.balban@arm.com
+ * - set irq polarity before requesting irq
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/bug.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include "smsc911x.h"
+
+#define SMSC_CHIPNAME "smsc911x"
+#define SMSC_DRV_VERSION "2007-07-13"
+
+MODULE_LICENSE("GPL");
+
+struct smsc911x_data {
+ void __iomem *ioaddr;
+
+ unsigned int idrev;
+ unsigned int generation; /* used to decide which workarounds apply */
+
+ /* device configuration */
+ unsigned int irq_polarity;
+ unsigned int irq_type;
+
+ /* This needs to be acquired before calling any of below:
+ * smsc911x_mac_read(), smsc911x_mac_write()
+ * smsc911x_phy_read(), smsc911x_phy_write()
+ */
+ spinlock_t phy_lock;
+
+ struct net_device_stats stats;
+ struct mii_if_info mii;
+ unsigned int using_extphy;
+ u32 msg_enable;
+#ifdef USE_LED1_WORK_AROUND
+ unsigned int gpio_setting;
+ unsigned int gpio_orig_setting;
+#endif
+ struct net_device *netdev;
+ struct napi_struct napi;
+ struct timer_list link_poll_timer;
+ unsigned int stop_link_poll;
+
+ unsigned int software_irq_signal;
+
+#ifdef USE_PHY_WORK_AROUND
+#define MIN_PACKET_SIZE (64)
+ char loopback_tx_pkt[MIN_PACKET_SIZE];
+ char loopback_rx_pkt[MIN_PACKET_SIZE];
+ unsigned int resetcount;
+#endif
+
+ /* Members for Multicast filter workaround */
+ unsigned int multicast_update_pending;
+ unsigned int set_bits_mask;
+ unsigned int clear_bits_mask;
+ unsigned int hashhi;
+ unsigned int hashlo;
+};
+
+#if SMSC_CAN_USE_32BIT
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ return readl(pdata->ioaddr + reg);
+}
+
+static inline void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
+ u32 reg)
+{
+ writel(val, pdata->ioaddr + reg);
+}
+
+#elif SMSC_CAN_USE_SPI
+
+static u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ u32 reg_val;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ reg_val = spi_cpld_read(reg);
+ local_irq_restore(flags);
+
+ return reg_val;
+}
+
+static void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
+ u32 reg)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ spi_cpld_write(reg, val);
+ local_irq_restore(flags);
+}
+
+#else /* SMSC_CAN_USE_32BIT */
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ u32 reg_val;
+ unsigned long flags;
+
+ /* these two 16-bit reads must be performed consecutively, so must
+ * not be interrupted by our own ISR (which would start another
+ * read operation) */
+ local_irq_save(flags);
+ reg_val = ((readw(pdata->ioaddr + reg) & 0xFFFF) |
+ ((readw(pdata->ioaddr + reg + 2) & 0xFFFF) << 16));
+ local_irq_restore(flags);
+
+ return reg_val;
+}
+
+static inline void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
+ u32 reg)
+{
+ unsigned long flags;
+
+ /* these two 16-bit writes must be performed consecutively, so must
+ * not be interrupted by our own ISR (which would start another
+ * read operation) */
+ local_irq_save(flags);
+ writew(val & 0xFFFF, pdata->ioaddr + reg);
+ writew((val >> 16) & 0xFFFF, pdata->ioaddr + reg + 2);
+ local_irq_restore(flags);
+}
+
+#endif /* SMSC_CAN_USE_32BIT */
+
+/* Writes a packet to the TX_DATA_FIFO */
+static inline void
+smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ while (wordcount--)
+ smsc911x_reg_write(*buf++, pdata, TX_DATA_FIFO);
+}
+
+/* Reads a packet out of the RX_DATA_FIFO */
+static inline void
+smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ while (wordcount--)
+ *buf++ = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+}
+
+/* waits for MAC not busy, with timeout. Only called by smsc911x_mac_read
+ * and smsc911x_mac_write, so assumes phy_lock is held */
+static int smsc911x_mac_notbusy(struct smsc911x_data *pdata)
+{
+ int i;
+ u32 val;
+
+ for (i = 0; i < 40; i++) {
+ val = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (!(val & MAC_CSR_CMD_CSR_BUSY_))
+ return 1;
+ }
+ SMSC_WARNING("Timed out waiting for MAC not BUSY. "
+ "MAC_CSR_CMD: 0x%08X", val);
+ return 0;
+}
+
+/* Fetches a MAC register value. Assumes phy_lock is acquired */
+static u32 smsc911x_mac_read(struct smsc911x_data *pdata, unsigned int offset)
+{
+ unsigned int temp;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARNING("smsc911x_mac_read failed, MAC busy at entry");
+ return 0xFFFFFFFF;
+ }
+
+ /* Send the MAC cmd */
+ smsc911x_reg_write(((offset & 0xFF) | MAC_CSR_CMD_CSR_BUSY_
+ | MAC_CSR_CMD_R_NOT_W_), pdata, MAC_CSR_CMD);
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the read to happen */
+ if (likely(smsc911x_mac_notbusy(pdata)))
+ return smsc911x_reg_read(pdata, MAC_CSR_DATA);
+
+ SMSC_WARNING("smsc911x_mac_read failed, MAC busy after read");
+ return 0xFFFFFFFF;
+}
+
+/* Set a mac register, phy_lock must be acquired before calling */
+static void smsc911x_mac_write(struct smsc911x_data *pdata,
+ unsigned int offset, u32 val)
+{
+ unsigned int temp;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARNING("smsc911x_mac_write failed, MAC busy at entry");
+ return;
+ }
+
+ /* Send data to write */
+ smsc911x_reg_write(val, pdata, MAC_CSR_DATA);
+
+ /* Write the actual data */
+ smsc911x_reg_write(((offset & 0xFF) | MAC_CSR_CMD_CSR_BUSY_), pdata,
+ MAC_CSR_CMD);
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the write to complete */
+ if (likely(smsc911x_mac_notbusy(pdata)))
+ return;
+
+ SMSC_WARNING("smsc911x_mac_write failed, MAC busy after write");
+}
+
+/* Gets a phy register, phy_lock must be acquired before calling */
+static u16 smsc911x_phy_read(struct smsc911x_data *pdata, unsigned int index)
+{
+ unsigned int addr;
+ int i;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARNING("MII is busy in smsc911x_phy_read???");
+ return 0;
+ }
+
+ /* Set the address, index & direction (read from PHY) */
+ addr = (((pdata->mii.phy_id) & 0x1F) << 11)
+ | ((index & 0x1F) << 6);
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for read to complete w/ timeout */
+ for (i = 0; i < 100; i++) {
+ /* See if MII is finished yet */
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ return smsc911x_mac_read(pdata, MII_DATA);
+ }
+ }
+ SMSC_WARNING("Timed out waiting for MII write to finish");
+ return 0xFFFF;
+}
+
+/* Sets a phy register, phy_lock must be acquired before calling */
+static void smsc911x_phy_write(struct smsc911x_data *pdata,
+ unsigned int index, u16 val)
+{
+ unsigned int addr;
+ int i;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+ if (!spin_is_locked(&pdata->phy_lock))
+ SMSC_WARNING("phy_lock not held");
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARNING("MII is busy in smsc911x_write_phy???");
+ return;
+ }
+
+ /* Put the data to write in the MAC */
+ smsc911x_mac_write(pdata, MII_DATA, val);
+
+ /* Set the address, index & direction (write to PHY) */
+ addr = (((pdata->mii.phy_id) & 0x1F) << 11) |
+ ((index & 0x1F) << 6) | MII_ACC_MII_WRITE_;
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for write to complete w/ timeout */
+ for (i = 0; i < 100; i++) {
+ /* See if MII is finished yet */
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_))
+ return;
+ }
+ SMSC_WARNING("Timed out waiting for MII write to finish");
+}
+
+static int smsc911x_mdio_read(struct net_device *dev, int phy_id, int location)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+ int reg;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ reg = smsc911x_phy_read(pdata, location);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ return reg;
+}
+
+static void smsc911x_mdio_write(struct net_device *dev, int phy_id,
+ int location, int val)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, location, val);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+/* Autodetects and initialises external phy for SMSC9115 and SMSC9117 flavors.
+ * If something goes wrong, returns -ENODEV to revert back to internal phy.
+ * Performed at initialisation only, so interrupts are enabled */
+static int smsc911x_phy_initialise_external(struct smsc911x_data *pdata)
+{
+ unsigned int address;
+ unsigned int hwcfg;
+ unsigned int phyid1;
+ unsigned int phyid2;
+
+ hwcfg = smsc911x_reg_read(pdata, HW_CFG);
+
+ /* External phy is requested, supported, and detected */
+ if (hwcfg & HW_CFG_EXT_PHY_DET_) {
+
+ /* Attempt to switch to external phy for auto-detecting
+ * its address. Assuming tx and rx are stopped because
+ * smsc911x_phy_initialise is called before
+ * smsc911x_rx_initialise and tx_initialise.
+ */
+
+ /* Disable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to stop */
+
+ /* Switch to external phy */
+ hwcfg |= HW_CFG_EXT_PHY_EN_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Enable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to restart */
+
+ hwcfg |= HW_CFG_SMI_SEL_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Auto-detect PHY */
+ spin_lock_irq(&pdata->phy_lock);
+ for (address = 0; address <= 31; address++) {
+ pdata->mii.phy_id = address;
+ phyid1 = smsc911x_phy_read(pdata, MII_PHYSID1);
+ phyid2 = smsc911x_phy_read(pdata, MII_PHYSID2);
+ if ((phyid1 != 0xFFFFU) || (phyid2 != 0xFFFFU)) {
+ SMSC_TRACE("Detected PHY at address = "
+ "0x%02X = %d", address, address);
+ break;
+ }
+ }
+ spin_unlock_irq(&pdata->phy_lock);
+
+ if ((phyid1 == 0xFFFFU) && (phyid2 == 0xFFFFU)) {
+ SMSC_WARNING("External PHY is not accessable, "
+ "using internal PHY instead");
+ /* Revert back to internal phy settings. */
+
+ /* Disable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to stop */
+
+ /* Switch to internal phy */
+ hwcfg &= (~HW_CFG_EXT_PHY_EN_);
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+
+ /* Enable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_INT_PHY_;
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ udelay(10); /* Enough time for clocks to restart */
+
+ hwcfg &= (~HW_CFG_SMI_SEL_);
+ smsc911x_reg_write(hwcfg, pdata, HW_CFG);
+ /* Use internal phy */
+ return -ENODEV;
+ } else {
+ SMSC_TRACE("Successfully switched to external PHY");
+ pdata->using_extphy = 1;
+ }
+ } else {
+ SMSC_WARNING("No external PHY detected.");
+ SMSC_WARNING("Using internal PHY instead.");
+ /* Use internal phy */
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* called by phy_initialise and loopback test */
+static int smsc911x_phy_reset(struct smsc911x_data *pdata)
+{
+ unsigned int temp;
+ unsigned int i = 100000;
+ unsigned long flags;
+
+ SMSC_TRACE("Performing PHY BCR Reset");
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_RESET);
+ do {
+ udelay(10);
+ temp = smsc911x_phy_read(pdata, MII_BMCR);
+ } while ((i--) && (temp & BMCR_RESET));
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ if (temp & BMCR_RESET) {
+ SMSC_WARNING("PHY reset failed to complete.");
+ return 0;
+ }
+ /* Extra delay required because the phy may not be completed with
+ * its reset when BMCR_RESET is cleared. Specs say 256 uS is
+ * enough delay but using 1ms here to be safe
+ */
+ msleep(1);
+
+ return 1;
+}
+
+/* Fetches a tx status out of the status fifo */
+static unsigned int smsc911x_tx_get_txstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, TX_STATUS_FIFO);
+
+ return result;
+}
+
+/* Fetches the next rx status */
+static unsigned int smsc911x_rx_get_rxstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, RX_STATUS_FIFO);
+
+ return result;
+}
+
+#ifdef USE_PHY_WORK_AROUND
+static int smsc911x_phy_check_loopbackpkt(struct smsc911x_data *pdata)
+{
+ unsigned int tries;
+ u32 wrsz;
+ u32 rdsz;
+ u32 bufp;
+
+ for (tries = 0; tries < 10; tries++) {
+ unsigned int txcmd_a;
+ unsigned int txcmd_b;
+ unsigned int status;
+ unsigned int pktlength;
+ unsigned int i;
+
+ /* Zero-out rx packet memory */
+ memset(pdata->loopback_rx_pkt, 0, MIN_PACKET_SIZE);
+
+ /* Write tx packet to 118 */
+ txcmd_a = (((unsigned int)pdata->loopback_tx_pkt)
+ & 0x03) << 16;
+ txcmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ txcmd_a |= MIN_PACKET_SIZE;
+
+ txcmd_b = MIN_PACKET_SIZE << 16 | MIN_PACKET_SIZE;
+
+ smsc911x_reg_write(txcmd_a, pdata, TX_DATA_FIFO);
+ smsc911x_reg_write(txcmd_b, pdata, TX_DATA_FIFO);
+
+ bufp = ((u32) pdata->loopback_tx_pkt) & 0xFFFFFFFC;
+ wrsz = MIN_PACKET_SIZE + 3;
+ wrsz += (((u32) pdata->loopback_tx_pkt) & 0x3);
+ wrsz >>= 2;
+
+ smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+
+ /* Wait till transmit is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_tx_get_txstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARNING("Failed to transmit during loopback test");
+ continue;
+ }
+ if (status & TX_STS_ES_) {
+ SMSC_WARNING("Transmit encountered errors during "
+ "loopback test");
+ continue;
+ }
+
+ /* Wait till receive is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_rx_get_rxstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARNING("Failed to receive during loopback test");
+ continue;
+ }
+ if (status & RX_STS_ES_) {
+ SMSC_WARNING("Receive encountered errors during "
+ "loopback test");
+ continue;
+ }
+
+ pktlength = ((status & 0x3FFF0000UL) >> 16);
+ bufp = (u32)pdata->loopback_rx_pkt;
+ rdsz = pktlength + 3;
+ rdsz += ((u32)pdata->loopback_rx_pkt) & 0x3;
+ rdsz >>= 2;
+
+ smsc911x_rx_readfifo(pdata, (unsigned int *)bufp, rdsz);
+
+ if (pktlength != (MIN_PACKET_SIZE + 4)) {
+ SMSC_WARNING("Unexpected packet size during "
+ "loop back test, size=%d, "
+ "will retry", pktlength);
+ } else {
+ unsigned int j;
+ int mismatch = 0;
+ for (j = 0; j < MIN_PACKET_SIZE; j++) {
+ if (pdata->loopback_tx_pkt[j]
+ != pdata->loopback_rx_pkt[j]) {
+ mismatch = 1;
+ break;
+ }
+ }
+ if (!mismatch) {
+ SMSC_TRACE("Successfully verified "
+ "loopback packet");
+ return 1;
+ } else {
+ SMSC_WARNING("Data miss match during "
+ "loop back test, will retry.");
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int smsc911x_phy_loopbacktest(struct smsc911x_data *pdata)
+{
+ int result = 0;
+ unsigned int i;
+ unsigned int val;
+ unsigned long flags;
+
+ /* Initialise tx packet */
+ for (i = 0; i < 6; i++) {
+ /* Use broadcast destination address */
+ pdata->loopback_tx_pkt[i] = (char)0xFF;
+ }
+
+ for (i = 6; i < 12; i++) {
+ /* Use incrementing source address */
+ pdata->loopback_tx_pkt[i] = (char)i;
+ }
+
+ /* Set length type field */
+ pdata->loopback_tx_pkt[12] = 0x00;
+ pdata->loopback_tx_pkt[13] = 0x00;
+ for (i = 14; i < MIN_PACKET_SIZE; i++) {
+ pdata->loopback_tx_pkt[i] = (char)i;
+ }
+
+ val = smsc911x_reg_read(pdata, HW_CFG);
+ val &= HW_CFG_TX_FIF_SZ_;
+ val |= HW_CFG_SF_;
+ smsc911x_reg_write(val, pdata, HW_CFG);
+
+ smsc911x_reg_write(TX_CFG_TX_ON_, pdata, TX_CFG);
+ smsc911x_reg_write((((unsigned int)pdata->loopback_rx_pkt)
+ & 0x03) << 8, pdata, RX_CFG);
+
+ for (i = 0; i < 10; i++) {
+ /* Set PHY to 10/FD, no ANEG, and loopback mode */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, 0x4100);
+
+ /* Enable MAC tx/rx, FD */
+ smsc911x_mac_write(pdata, MAC_CR, MAC_CR_FDPX_
+ | MAC_CR_TXEN_ | MAC_CR_RXEN_);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ if (smsc911x_phy_check_loopbackpkt(pdata)) {
+ result = 1;
+ break;
+ }
+ pdata->resetcount++;
+
+ /* Disable MAC rx */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ smsc911x_phy_reset(pdata);
+ }
+
+ /* Disable MAC */
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+
+ /* Cancel PHY loopback mode */
+ smsc911x_phy_write(pdata, MII_BMCR, 0);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ smsc911x_reg_write(0, pdata, TX_CFG);
+ smsc911x_reg_write(0, pdata, RX_CFG);
+
+ return result;
+}
+#endif /* USE_PHY_WORK_AROUND */
+
+/* assumes phy_lock is held */
+static void smsc911x_phy_update_flowcontrol(struct smsc911x_data *pdata)
+{
+ unsigned int temp;
+
+ if (pdata->mii.full_duplex) {
+ unsigned int phy_adv;
+ unsigned int phy_lpa;
+ phy_adv = smsc911x_phy_read(pdata, MII_ADVERTISE);
+ phy_lpa = smsc911x_phy_read(pdata, MII_LPA);
+ if (phy_adv & phy_lpa & LPA_PAUSE_CAP) {
+ /* Both ends support symmetric pause, enable
+ * PAUSE receive and transmit */
+ smsc911x_mac_write(pdata, FLOW, 0xFFFF0002);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp |= 0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ } else if (((phy_adv & ADVERTISE_PAUSE_ALL) ==
+ ADVERTISE_PAUSE_ALL) &&
+ ((phy_lpa & LPA_PAUSE_ALL) == LPA_PAUSE_ASYM)) {
+ /* We support symmetric and asym pause, the
+ * other end only supports asym, Enable PAUSE
+ * receive, disable PAUSE transmit */
+ smsc911x_mac_write(pdata, FLOW, 0xFFFF0002);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp &= ~0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ } else {
+ /* Disable PAUSE receive and transmit */
+ smsc911x_mac_write(pdata, FLOW, 0);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp &= ~0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ }
+ } else {
+ smsc911x_mac_write(pdata, FLOW, 0);
+ temp = smsc911x_reg_read(pdata, AFC_CFG);
+ temp |= 0xF;
+ smsc911x_reg_write(temp, pdata, AFC_CFG);
+ }
+}
+
+/* Update link mode if any thing has changed */
+static void smsc911x_phy_update_linkmode(struct net_device *dev, int init)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ if (mii_check_media(&pdata->mii, netif_msg_link(pdata), init)) {
+ /* duplex state has changed */
+ unsigned int mac_cr;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ if (pdata->mii.full_duplex) {
+ SMSC_TRACE("configuring for full duplex mode");
+ mac_cr |= MAC_CR_FDPX_;
+ } else {
+ SMSC_TRACE("configuring for half duplex mode");
+ mac_cr &= ~MAC_CR_FDPX_;
+ }
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+
+ smsc911x_phy_update_flowcontrol(pdata);
+
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+ }
+#ifdef USE_LED1_WORK_AROUND
+ if (netif_carrier_ok(dev)) {
+ if ((pdata->gpio_orig_setting & GPIO_CFG_LED1_EN_) &&
+ (!pdata->using_extphy)) {
+ /* Restore orginal GPIO configuration */
+ pdata->gpio_setting = pdata->gpio_orig_setting;
+ smsc911x_reg_write(pdata->gpio_setting, pdata,
+ GPIO_CFG);
+ }
+ } else {
+ /* Check global setting that LED1
+ * usage is 10/100 indicator */
+ pdata->gpio_setting = smsc911x_reg_read(pdata, GPIO_CFG);
+ if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_)
+ && (!pdata->using_extphy)) {
+ /* Force 10/100 LED off, after saving
+ * orginal GPIO configuration */
+ pdata->gpio_orig_setting = pdata->gpio_setting;
+
+ pdata->gpio_setting &= ~GPIO_CFG_LED1_EN_;
+ pdata->gpio_setting |= (GPIO_CFG_GPIOBUF0_
+ | GPIO_CFG_GPIODIR0_
+ | GPIO_CFG_GPIOD0_);
+ smsc911x_reg_write(pdata->gpio_setting, pdata,
+ GPIO_CFG);
+ }
+ }
+#endif /* USE_LED1_WORK_AROUND */
+}
+
+/* Entry point for the link poller */
+static void smsc911x_phy_checklink(unsigned long ptr)
+{
+ struct net_device *dev = (struct net_device *)ptr;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ smsc911x_phy_update_linkmode(dev, 0);
+
+ if (!(pdata->stop_link_poll)) {
+ pdata->link_poll_timer.expires = jiffies + 2 * HZ;
+ add_timer(&pdata->link_poll_timer);
+ } else {
+ pdata->stop_link_poll = 0;
+ }
+}
+
+/* Initialises the PHY layer. Called at initialisation by open() so
+ * interrupts are enabled */
+static int smsc911x_phy_initialise(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int phyid1 = 0;
+ unsigned int phyid2 = 0;
+ unsigned int temp;
+
+ pdata->using_extphy = 0;
+
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01170000:
+ case 0x01150000:
+ /* External PHY supported, try to autodetect */
+ if (smsc911x_phy_initialise_external(pdata) < 0) {
+ SMSC_TRACE("External PHY is not detected, using "
+ "internal PHY instead");
+ pdata->mii.phy_id = 1;
+ }
+ break;
+ default:
+ SMSC_TRACE("External PHY is not supported, using internal PHY "
+ "instead");
+ pdata->mii.phy_id = 1;
+ break;
+ }
+
+ spin_lock_irq(&pdata->phy_lock);
+ phyid1 = smsc911x_phy_read(pdata, MII_PHYSID1);
+ phyid2 = smsc911x_phy_read(pdata, MII_PHYSID2);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ if ((phyid1 == 0xFFFF) && (phyid2 == 0xFFFF)) {
+ SMSC_WARNING("Internal PHY not detected!");
+ return 0;
+ }
+
+ /* Reset the phy */
+ if (!smsc911x_phy_reset(pdata)) {
+ SMSC_WARNING("PHY reset failed to complete.");
+ return 0;
+ }
+#ifdef USE_PHY_WORK_AROUND
+ if (!smsc911x_phy_loopbacktest(pdata)) {
+ SMSC_WARNING("Failed Loop Back Test");
+ return 0;
+ } else {
+ SMSC_TRACE("Passed Loop Back Test");
+ }
+#endif /* USE_PHY_WORK_AROUND */
+
+ /* Advertise all speeds and pause capabilities */
+ spin_lock_irq(&pdata->phy_lock);
+ temp = smsc911x_phy_read(pdata, MII_ADVERTISE);
+ temp |= (ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+ smsc911x_phy_write(pdata, MII_ADVERTISE, temp);
+ pdata->mii.advertising = temp;
+
+ if (!pdata->using_extphy) {
+ /* using internal phy, enable PHY interrupts */
+ smsc911x_phy_read(pdata, MII_INTSTS);
+ smsc911x_phy_write(pdata, MII_INTMSK, PHY_INTMSK_DEFAULT_);
+ }
+
+ /* begin to establish link */
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ smsc911x_phy_update_linkmode(dev, 1);
+
+ setup_timer(&pdata->link_poll_timer, smsc911x_phy_checklink,
+ (unsigned long)dev);
+ pdata->link_poll_timer.expires = jiffies + 2 * HZ;
+ add_timer(&pdata->link_poll_timer);
+
+ SMSC_TRACE("phy initialised succesfully");
+ return 1;
+}
+
+/* Gets the number of tx statuses in the fifo */
+static unsigned int smsc911x_tx_get_txstatcount(struct smsc911x_data *pdata)
+{
+ unsigned int result = (smsc911x_reg_read(pdata, TX_FIFO_INF)
+ & TX_FIFO_INF_TSUSED_) >> 16;
+ return result;
+}
+
+/* Reads tx statuses and increments counters where necessary */
+static void smsc911x_tx_update_txcounters(struct smsc911x_data *pdata)
+{
+ unsigned int tx_stat;
+
+ while ((tx_stat = smsc911x_tx_get_txstatus(pdata)) != 0) {
+ if (unlikely(tx_stat & 0x80000000)) {
+ /* In this driver the packet tag is used as the packet
+ * length. Since a packet length can never reach the
+ * size of 0x8000, this bit is reserved. It is worth
+ * noting that the "reserved bit" in the warning above
+ * does not reference a hardware defined reserved bit
+ * but rather a driver defined one.
+ */
+ SMSC_WARNING("Packet tag reserved bit is high");
+ } else {
+ if (unlikely(tx_stat & 0x00008000)) {
+ pdata->stats.tx_errors++;
+ } else {
+ pdata->stats.tx_packets++;
+ pdata->stats.tx_bytes += (tx_stat >> 16);
+ }
+ if (unlikely(tx_stat & 0x00000100)) {
+ pdata->stats.collisions += 16;
+ pdata->stats.tx_aborted_errors += 1;
+ } else {
+ pdata->stats.collisions +=
+ ((tx_stat >> 3) & 0xF);
+ }
+ if (unlikely(tx_stat & 0x00000800)) {
+ pdata->stats.tx_carrier_errors += 1;
+ }
+ if (unlikely(tx_stat & 0x00000200)) {
+ pdata->stats.collisions++;
+ pdata->stats.tx_aborted_errors++;
+ }
+ }
+ }
+}
+
+/* Increments the Rx error counters */
+static void
+smsc911x_rx_counterrors(struct smsc911x_data *pdata, unsigned int rxstat)
+{
+ int crc_err = 0;
+
+ if (unlikely(rxstat & 0x00008000)) {
+ pdata->stats.rx_errors++;
+ if (unlikely(rxstat & 0x00000002)) {
+ pdata->stats.rx_crc_errors++;
+ crc_err = 1;
+ }
+ }
+ if (likely(!crc_err)) {
+ if (unlikely((rxstat & 0x00001020) == 0x00001020)) {
+ /* Frame type indicates length,
+ * and length error is set */
+ pdata->stats.rx_length_errors++;
+ }
+ if (rxstat & RX_STS_MCAST_)
+ pdata->stats.multicast++;
+ }
+}
+
+/* Quickly dumps bad packets */
+static void
+smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktbytes)
+{
+ unsigned int pktwords = (pktbytes + NET_IP_ALIGN + 3) >> 2;
+
+ if (likely(pktwords >= 4)) {
+ unsigned int timeout = 500;
+ unsigned int val;
+ smsc911x_reg_write(RX_DP_CTRL_RX_FFWD_, pdata, RX_DP_CTRL);
+ do {
+ udelay(1);
+ val = smsc911x_reg_read(pdata, RX_DP_CTRL);
+ } while (timeout-- && (val & RX_DP_CTRL_RX_FFWD_));
+
+ if (unlikely(timeout == 0))
+ SMSC_WARNING("Timed out waiting for RX FFWD "
+ "to finish, RX_DP_CTRL: 0x%08X", val);
+ } else {
+ unsigned int temp;
+ while (pktwords--)
+ temp = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+ }
+}
+
+/* NAPI poll function */
+static int smsc911x_poll(struct napi_struct *napi, int budget)
+{
+ struct smsc911x_data *pdata = container_of(napi, struct smsc911x_data, napi);
+ struct net_device *dev = pdata->netdev;
+ int npackets = 0;
+
+ while (npackets < budget) {
+ unsigned int pktlength;
+ unsigned int pktwords;
+ unsigned int rxstat = smsc911x_rx_get_rxstatus(pdata);
+
+ /* break out of while loop if there are no more packets waiting */
+ if (!rxstat)
+ break;
+
+ pktlength = ((rxstat & 0x3FFF0000) >> 16);
+ pktwords = (pktlength + NET_IP_ALIGN + 3) >> 2;
+ smsc911x_rx_counterrors(pdata, rxstat);
+
+ if (likely((rxstat & RX_STS_ES_) == 0)) {
+ struct sk_buff *skb;
+ skb = dev_alloc_skb(pktlength + NET_IP_ALIGN);
+ if (likely(skb)) {
+ skb->data = skb->head;
+ skb->tail = skb->head;
+ /* Align IP on 16B boundary */
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, pktlength - 4);
+ smsc911x_rx_readfifo(pdata,
+ (unsigned int *)skb->head,
+ pktwords);
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_NONE;
+ netif_receive_skb(skb);
+
+ /* Update counters */
+ pdata->stats.rx_packets++;
+ pdata->stats.rx_bytes += (pktlength - 4);
+ dev->last_rx = jiffies;
+ npackets++;
+ continue;
+ } else {
+ SMSC_WARNING("Unable to allocate sk_buff "
+ "for rx packet, in PIO path");
+ pdata->stats.rx_dropped++;
+ }
+ }
+ /* At this point, the packet is to be read out
+ * of the fifo and discarded */
+ smsc911x_rx_fastforward(pdata, pktlength);
+ }
+
+ pdata->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ smsc911x_reg_write(INT_STS_RSFL_, pdata, INT_STS);
+
+ if (npackets < budget) {
+ unsigned int temp;
+ /* We processed all packets available. Tell NAPI it can
+ * stop polling then re-enable rx interrupts */
+ netif_rx_complete(dev, napi);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RSFL_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ }
+
+ /* There are still packets waiting */
+ return npackets;
+}
+
+/* Returns hash bit number for given MAC address
+ * Example:
+ * 01 00 5E 00 00 01 -> returns bit number 31 */
+static unsigned int smsc911x_hash(char addr[ETH_ALEN])
+{
+ unsigned int crc;
+ unsigned int result;
+
+ crc = ether_crc(ETH_ALEN, addr);
+ result = (crc >> 26) & 0x3f;
+
+ return result;
+}
+
+static void smsc911x_rx_multicast_update(struct smsc911x_data *pdata)
+{
+ /* Performs the multicast & mac_cr update. This is called when
+ * safe on the current hardware, and with the phy_lock held */
+ unsigned int mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= pdata->set_bits_mask;
+ mac_cr &= ~(pdata->clear_bits_mask);
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+ smsc911x_mac_write(pdata, HASHH, pdata->hashhi);
+ smsc911x_mac_write(pdata, HASHL, pdata->hashlo);
+ SMSC_TRACE("maccr 0x%08X, HASHH 0x%08X, HASHL 0x%08X", mac_cr,
+ pdata->hashhi, pdata->hashlo);
+}
+
+static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
+{
+ unsigned int mac_cr;
+
+ /* This function is only called for older LAN911x devices
+ * (revA or revB), where MAC_CR, HASHH and HASHL should not
+ * be modified during Rx - newer devices immediately update the
+ * registers.
+ *
+ * This is called from interrupt context */
+
+ spin_lock(&pdata->phy_lock);
+
+ /* Check Rx has stopped */
+ if (smsc911x_mac_read(pdata, MAC_CR) & MAC_CR_RXEN_)
+ SMSC_WARNING("Rx not stopped\n");
+
+ /* Perform the update - safe to do now Rx has stopped */
+ smsc911x_rx_multicast_update(pdata);
+
+ /* Re-enable Rx */
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= MAC_CR_RXEN_;
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+
+ pdata->multicast_update_pending = 0;
+
+ spin_unlock(&pdata->phy_lock);
+}
+
+/* Sets the device MAC address to dev_addr, called with phy_lock held */
+static void
+smsc911x_set_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6])
+{
+ u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4];
+ u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) |
+ (dev_addr[1] << 8) | dev_addr[0];
+
+ smsc911x_mac_write(pdata, ADDRH, mac_high16);
+ smsc911x_mac_write(pdata, ADDRL, mac_low32);
+}
+
+static int smsc911x_open(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int timeout;
+ unsigned int temp;
+ unsigned int intcfg;
+
+ spin_lock_init(&pdata->phy_lock);
+
+ /* Reset the LAN911x */
+ smsc911x_reg_write(HW_CFG_SRST_, pdata, HW_CFG);
+ timeout = 10;
+ do {
+ udelay(10);
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ } while ((--timeout) && (temp & HW_CFG_SRST_));
+
+ if (unlikely(temp & HW_CFG_SRST_)) {
+ SMSC_WARNING("Failed to complete reset");
+ return -ENODEV;
+ }
+
+ smsc911x_reg_write(0x00050000, pdata, HW_CFG);
+ smsc911x_reg_write(0x006E3740, pdata, AFC_CFG);
+
+ /* Make sure EEPROM has finished loading before setting GPIO_CFG */
+ timeout = 50;
+ while ((timeout--) &&
+ (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_)) {
+ udelay(10);
+ }
+
+ if (unlikely(timeout == 0)) {
+ SMSC_WARNING("Timed out waiting for EEPROM "
+ "busy bit to clear");
+ }
+#if USE_DEBUG >= 1
+ smsc911x_reg_write(0x00670700, pdata, GPIO_CFG);
+#else
+ smsc911x_reg_write(0x70070000, pdata, GPIO_CFG);
+#endif
+
+ /* Initialise irqs, but leave all sources disabled */
+ smsc911x_reg_write(0, pdata, INT_EN);
+ smsc911x_reg_write(0xFFFFFFFF, pdata, INT_STS);
+
+ /* Set interrupt deassertion to 100uS */
+ intcfg = ((10 << 24) | INT_CFG_IRQ_EN_);
+
+ if (pdata->irq_polarity) {
+ SMSC_TRACE("irq polarity: active high");
+ intcfg |= INT_CFG_IRQ_POL_;
+ } else {
+ SMSC_TRACE("irq polarity: active low");
+ }
+
+ if (pdata->irq_type) {
+ SMSC_TRACE("irq type: push-pull");
+ intcfg |= INT_CFG_IRQ_TYPE_;
+ } else {
+ SMSC_TRACE("irq type: open drain");
+ }
+
+ smsc911x_reg_write(intcfg, pdata, INT_CFG);
+
+ SMSC_TRACE("Testing irq handler using IRQ %d", dev->irq);
+ pdata->software_irq_signal = 0;
+ smp_wmb();
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_SW_INT_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ timeout = 1000;
+ while (timeout--) {
+ smp_rmb();
+ if (pdata->software_irq_signal)
+ break;
+ msleep(1);
+ }
+
+ if (!pdata->software_irq_signal) {
+ printk(KERN_WARNING "%s: ISR failed signaling test (IRQ %d)\n",
+ dev->name, dev->irq);
+ return -ENODEV;
+ }
+ SMSC_TRACE("IRQ handler passed test using IRQ %d", dev->irq);
+
+ printk(KERN_INFO "%s: SMSC911x/921x identified at %#08lx, IRQ: %d\n",
+ dev->name, (unsigned long)pdata->ioaddr, dev->irq);
+
+ netif_carrier_off(dev);
+
+ if (!smsc911x_phy_initialise(dev)) {
+ SMSC_WARNING("Failed to initialize PHY");
+ return -ENODEV;
+ }
+
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ temp &= HW_CFG_TX_FIF_SZ_;
+ temp |= HW_CFG_SF_;
+ smsc911x_reg_write(temp, pdata, HW_CFG);
+
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ temp &= ~(FIFO_INT_RX_STS_LEVEL_);
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+
+ /* set RX Data offset to 2 bytes for alignment */
+ smsc911x_reg_write((2 << 8), pdata, RX_CFG);
+
+ napi_enable(&pdata->napi);
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_ | INT_EN_PHY_INT_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ spin_lock_irq(&pdata->phy_lock);
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp |= (MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ spin_unlock_irq(&pdata->phy_lock);
+
+ smsc911x_reg_write(TX_CFG_TX_ON_, pdata, TX_CFG);
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Entry point for stopping the interface */
+static int smsc911x_stop(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ napi_disable(&pdata->napi);
+
+ pdata->stop_link_poll = 1;
+ del_timer_sync(&pdata->link_poll_timer);
+
+ smsc911x_reg_write((smsc911x_reg_read(pdata, INT_CFG) &
+ (~INT_CFG_IRQ_EN_)), pdata, INT_CFG);
+ netif_stop_queue(dev);
+
+ /* At this point all Rx and Tx activity is stopped */
+ pdata->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ smsc911x_tx_update_txcounters(pdata);
+
+ SMSC_TRACE("Interface stopped");
+ return 0;
+}
+
+/* Entry point for transmitting a packet */
+static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int freespace;
+ unsigned int tx_cmd_a;
+ unsigned int tx_cmd_b;
+ unsigned int temp;
+ u32 wrsz;
+ u32 bufp;
+
+ freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
+
+ if (unlikely(freespace < TX_FIFO_LOW_THRESHOLD))
+ SMSC_WARNING("Tx data fifo low, space available: %d",
+ freespace);
+
+ /* Word alignment adjustment */
+ tx_cmd_a = ((((unsigned int)(skb->data)) & 0x03) << 16);
+ tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ tx_cmd_a |= (unsigned int)skb->len;
+
+ tx_cmd_b = ((unsigned int)skb->len) << 16;
+ tx_cmd_b |= (unsigned int)skb->len;
+
+ smsc911x_reg_write(tx_cmd_a, pdata, TX_DATA_FIFO);
+ smsc911x_reg_write(tx_cmd_b, pdata, TX_DATA_FIFO);
+
+ bufp = ((u32)skb->data) & 0xFFFFFFFC;
+ wrsz = (u32)skb->len + 3;
+ wrsz += ((u32)skb->data) & 0x3;
+ wrsz >>= 2;
+
+ smsc911x_tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+ freespace -= (skb->len + 32);
+ dev_kfree_skb(skb);
+ dev->trans_start = jiffies;
+
+ if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
+ smsc911x_tx_update_txcounters(pdata);
+
+ if (freespace < TX_FIFO_LOW_THRESHOLD) {
+ netif_stop_queue(dev);
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp &= 0x00FFFFFF;
+ temp |= 0x32000000;
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+/* Entry point for getting status counters */
+static struct net_device_stats *smsc911x_get_stats(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ smsc911x_tx_update_txcounters(pdata);
+ pdata->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ return &pdata->stats;
+}
+
+/* Entry point for setting addressing modes */
+static void smsc911x_set_multicast_list(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Enabling promiscuous mode */
+ pdata->set_bits_mask = MAC_CR_PRMS_;
+ pdata->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* Enabling all multicast mode */
+ pdata->set_bits_mask = MAC_CR_MCPAS_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->mc_count > 0) {
+ /* Enabling specific multicast addresses */
+ unsigned int hash_high = 0;
+ unsigned int hash_low = 0;
+ unsigned int count = 0;
+ struct dev_mc_list *mc_list = dev->mc_list;
+
+ pdata->set_bits_mask = MAC_CR_HPFILT_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ while (mc_list) {
+ count++;
+ if ((mc_list->dmi_addrlen) == ETH_ALEN) {
+ unsigned int bitnum =
+ smsc911x_hash(mc_list->dmi_addr);
+ unsigned int mask = 0x01 << (bitnum & 0x1F);
+ if (bitnum & 0x20)
+ hash_high |= mask;
+ else
+ hash_low |= mask;
+ } else {
+ SMSC_WARNING("dmi_addrlen != 6");
+ }
+ mc_list = mc_list->next;
+ }
+ if (count != (unsigned int)dev->mc_count)
+ SMSC_WARNING("mc_count != dev->mc_count");
+
+ pdata->hashhi = hash_high;
+ pdata->hashlo = hash_low;
+ } else {
+ /* Enabling local MAC address only */
+ pdata->set_bits_mask = 0;
+ pdata->clear_bits_mask =
+ (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ }
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+
+ if (pdata->generation <= 1) {
+ /* Older hardware revision - cannot change these flags while
+ * receiving data */
+ if (!pdata->multicast_update_pending) {
+ unsigned int temp;
+ SMSC_TRACE("scheduling mcast update");
+ pdata->multicast_update_pending = 1;
+
+ /* Request the hardware to stop, then perform the
+ * update when we get an RX_STOP interrupt */
+ smsc911x_reg_write(INT_STS_RXSTOP_INT_, pdata, INT_STS);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RXSTOP_INT_EN_;
+ smsc911x_reg_write(temp, pdata, INT_EN);
+
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp &= ~(MAC_CR_RXEN_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ } else {
+ /* There is another update pending, this should now
+ * use the newer values */
+ }
+ } else {
+ /* Newer hardware revision - can write immediately */
+ smsc911x_rx_multicast_update(pdata);
+ }
+
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int intsts;
+ unsigned int inten;
+ unsigned int temp;
+ int serviced = IRQ_NONE;
+
+ intsts = smsc911x_reg_read(pdata, INT_STS);
+ inten = smsc911x_reg_read(pdata, INT_EN);
+
+ if (unlikely(intsts & inten & INT_STS_SW_INT_)) {
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_SW_INT_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ smsc911x_reg_write(INT_STS_SW_INT_, pdata, INT_STS);
+ pdata->software_irq_signal = 1;
+ smp_wmb();
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXSTOP_INT_)) {
+ /* Called when there is a multicast update scheduled and
+ * it is now safe to complete the update */
+ SMSC_TRACE("RX Stop interrupt");
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RXSTOP_INT_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ smsc911x_reg_write(INT_STS_RXSTOP_INT_, pdata, INT_STS);
+ smsc911x_rx_multicast_update_workaround(pdata);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (intsts & inten & INT_STS_TDFA_) {
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ smsc911x_reg_write(temp, pdata, FIFO_INT);
+ smsc911x_reg_write(INT_STS_TDFA_, pdata, INT_STS);
+ netif_wake_queue(dev);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXE_)) {
+ smsc911x_reg_write(INT_STS_RXE_, pdata, INT_STS);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (likely(intsts & inten & INT_STS_RSFL_)) {
+ /* Disable Rx interrupts and schedule NAPI poll */
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RSFL_EN_);
+ smsc911x_reg_write(temp, pdata, INT_EN);
+ netif_rx_schedule(dev, &pdata->napi);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_PHY_INT_)) {
+ smsc911x_reg_write(INT_STS_PHY_INT_, pdata, INT_STS);
+ spin_lock(&pdata->phy_lock);
+ temp = smsc911x_phy_read(pdata, MII_INTSTS);
+ spin_unlock(&pdata->phy_lock);
+ SMSC_TRACE("PHY interrupt, sts 0x%04X", (u16)temp);
+ smsc911x_phy_update_linkmode(dev, 0);
+ serviced = IRQ_HANDLED;
+ }
+ return serviced;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+void smsc911x_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smsc911x_irqhandler(0, dev);
+ enable_irq(dev->irq);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+/* Standard ioctls for mii-tool */
+static int smsc911x_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct mii_ioctl_data *data = if_mii(ifr);
+ unsigned long flags;
+
+ SMSC_TRACE("ioctl cmd 0x%x", cmd);
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ data->phy_id = pdata->mii.phy_id;
+ return 0;
+ case SIOCGMIIREG:
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ data->val_out = smsc911x_phy_read(pdata, data->reg_num);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+ return 0;
+ case SIOCSMIIREG:
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, data->reg_num, data->val_in);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+ return 0;
+ }
+
+ SMSC_TRACE("unsupported ioctl cmd");
+ return -1;
+}
+
+static int
+smsc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+ return mii_ethtool_gset(&pdata->mii, cmd);
+}
+
+static int
+smsc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return mii_ethtool_sset(&pdata->mii, cmd);
+}
+
+static void smsc911x_ethtool_getdrvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strncpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver));
+ strncpy(info->version, SMSC_DRV_VERSION, sizeof(info->version));
+ strncpy(info->bus_info, dev->dev.bus_id, sizeof(info->bus_info));
+}
+
+static int smsc911x_ethtool_nwayreset(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return mii_nway_restart(&pdata->mii);
+}
+
+static u32 smsc911x_ethtool_getmsglevel(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ return pdata->msg_enable;
+}
+
+static void smsc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ pdata->msg_enable = level;
+}
+
+static int smsc911x_ethtool_getregslen(struct net_device *dev)
+{
+ return (((E2P_CMD - ID_REV) / 4 + 1) + (WUCSR - MAC_CR) + 1 + 32) *
+ sizeof(u32);
+}
+
+static void
+smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+ unsigned int i;
+ unsigned int j = 0;
+ u32 *data = buf;
+
+ regs->version = pdata->idrev;
+ for (i = ID_REV; i <= E2P_CMD; i += (sizeof(u32)))
+ data[j++] = smsc911x_reg_read(pdata, i);
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ for (i = MAC_CR; i <= WUCSR; i++)
+ data[j++] = smsc911x_mac_read(pdata, i);
+ for (i = 0; i <= 31; i++)
+ data[j++] = smsc911x_phy_read(pdata, i);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+}
+
+static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
+{
+ unsigned int temp = smsc911x_reg_read(pdata, GPIO_CFG);
+ temp &= ~GPIO_CFG_EEPR_EN_;
+ smsc911x_reg_write(temp, pdata, GPIO_CFG);
+ msleep(1);
+}
+
+static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op)
+{
+ int timeout = 100;
+ u32 e2cmd;
+
+ SMSC_TRACE("op 0x%08x", op);
+ if (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ SMSC_WARNING("Busy at start");
+ return -EBUSY;
+ }
+
+ e2cmd = op | E2P_CMD_EPC_BUSY_;
+ smsc911x_reg_write(e2cmd, pdata, E2P_CMD);
+
+ do {
+ msleep(1);
+ e2cmd = smsc911x_reg_read(pdata, E2P_CMD);
+ } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (timeout--));
+
+ if (!timeout) {
+ SMSC_TRACE("TIMED OUT");
+ return -EAGAIN;
+ }
+
+ if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+ SMSC_TRACE("Error occured during eeprom operation");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smsc911x_eeprom_read_location(struct smsc911x_data *pdata,
+ u8 address, u8 *data)
+{
+ u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+ int ret;
+
+ SMSC_TRACE("address 0x%x", address);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret)
+ data[address] = smsc911x_reg_read(pdata, E2P_DATA);
+
+ return ret;
+}
+
+static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
+ u8 address, u8 data)
+{
+ u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+ int ret;
+
+ SMSC_TRACE("address 0x%x, data 0x%x", address, data);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret) {
+ op = E2P_CMD_EPC_CMD_WRITE_ | address;
+ smsc911x_reg_write((u32)data, pdata, E2P_DATA);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+ }
+
+ return ret;
+}
+
+static int smsc911x_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return SMSC911X_EEPROM_SIZE;
+}
+
+static int smsc911x_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ u8 eeprom_data[SMSC911X_EEPROM_SIZE];
+ int len;
+ int i;
+
+ smsc911x_eeprom_enable_access(pdata);
+
+ len = min(eeprom->len, SMSC911X_EEPROM_SIZE);
+ for (i = 0; i < len; i++) {
+ int ret = smsc911x_eeprom_read_location(pdata, i, eeprom_data);
+ if (ret < 0) {
+ eeprom->len = 0;
+ return ret;
+ }
+ }
+
+ memcpy(data, &eeprom_data[eeprom->offset], len);
+ eeprom->len = len;
+ return 0;
+}
+
+static int smsc911x_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ int ret;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ smsc911x_eeprom_enable_access(pdata);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWEN_);
+ ret = smsc911x_eeprom_write_location(pdata, eeprom->offset, *data);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWDS_);
+
+ /* Single byte write, according to man page */
+ eeprom->len = 1;
+
+ return ret;
+}
+
+static struct ethtool_ops smsc911x_ethtool_ops = {
+ .get_settings = smsc911x_ethtool_getsettings,
+ .set_settings = smsc911x_ethtool_setsettings,
+ .get_link = ethtool_op_get_link,
+ .get_drvinfo = smsc911x_ethtool_getdrvinfo,
+ .nway_reset = smsc911x_ethtool_nwayreset,
+ .get_msglevel = smsc911x_ethtool_getmsglevel,
+ .set_msglevel = smsc911x_ethtool_setmsglevel,
+ .get_regs_len = smsc911x_ethtool_getregslen,
+ .get_regs = smsc911x_ethtool_getregs,
+ .get_eeprom_len = smsc911x_ethtool_get_eeprom_len,
+ .get_eeprom = smsc911x_ethtool_get_eeprom,
+ .set_eeprom = smsc911x_ethtool_set_eeprom,
+};
+
+/* Initializing private device structures */
+static int smsc911x_init(struct net_device *dev)
+{
+ u32 mac_high16;
+ u32 mac_low32;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ SMSC_TRACE("Driver Parameters:");
+ SMSC_TRACE("LAN base: 0x%08lX", (unsigned long)pdata->ioaddr);
+ SMSC_TRACE("IRQ: %d", dev->irq);
+ SMSC_TRACE("PHY will be autodetected.");
+
+ if (pdata->ioaddr == 0) {
+ SMSC_WARNING("pdata->ioaddr: 0x00000000");
+ return -ENODEV;
+ }
+
+ /* Default generation to zero (all workarounds apply) */
+ pdata->generation = 0;
+
+ pdata->idrev = smsc911x_reg_read(pdata, ID_REV);
+ if (((pdata->idrev >> 16) & 0xFFFF) == (pdata->idrev & 0xFFFF)) {
+ SMSC_WARNING("idrev top 16 bits equal to bottom 16 bits, "
+ "idrev: 0x%08X", pdata->idrev);
+ SMSC_TRACE("This may mean the chip is set for 32 bit while "
+ "the bus is reading as 16 bit");
+ return -ENODEV;
+ }
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01180000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE("LAN9118 Beacon identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 0;
+ break;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9118 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9118 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9118 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01170000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE("LAN9117 Beacon identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 0;
+ break;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9117 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9117 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9117 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01160000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9116 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9116 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9116 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x01150000:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ case 1UL:
+ SMSC_TRACE
+ ("LAN9115 Concord A0 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 1;
+ break;
+ case 2UL:
+ SMSC_TRACE
+ ("LAN9115 Concord A1 identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9115 Concord A1 identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 2;
+ break;
+ }
+ break;
+
+ case 0x118A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9218 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9218 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x117A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9217 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9217 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x116A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9216 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9216 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x115A0000UL:
+ switch (pdata->idrev & 0x0000FFFFUL) {
+ case 0UL:
+ SMSC_TRACE
+ ("LAN9215 Boylston identified, idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ default:
+ SMSC_TRACE
+ ("LAN9215 Boylston identified (NEW), idrev: 0x%08X",
+ pdata->idrev);
+ pdata->generation = 3;
+ break;
+ }
+ break;
+
+ case 0x92100000UL:
+ case 0x92110000UL:
+ case 0x92200000UL:
+ case 0x92210000UL:
+ /* LAN9210/LAN9211/LAN9220/LAN9221 */
+ pdata->generation = 4;
+ break;
+
+ default:
+ SMSC_WARNING("LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ }
+
+ if (pdata->generation == 0)
+ SMSC_WARNING("This driver is not intended "
+ "for this chip revision");
+
+ ether_setup(dev);
+ dev->open = smsc911x_open;
+ dev->stop = smsc911x_stop;
+ dev->hard_start_xmit = smsc911x_hard_start_xmit;
+ dev->get_stats = smsc911x_get_stats;
+ dev->set_multicast_list = smsc911x_set_multicast_list;
+ dev->flags |= IFF_MULTICAST;
+ dev->do_ioctl = smsc911x_do_ioctl;
+ netif_napi_add(dev, &pdata->napi, smsc911x_poll, 64);
+ dev->ethtool_ops = &smsc911x_ethtool_ops;
+
+ /* Check if mac address has been specified when bringing interface up */
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+ SMSC_TRACE("MAC Address is specified by configuration");
+ } else {
+ /* Try reading mac address from device. if EEPROM is present
+ * it will already have been set */
+ u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH);
+ u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL);
+ dev->dev_addr[0] = (u8)(mac_low32);
+ dev->dev_addr[1] = (u8)(mac_low32 >> 8);
+ dev->dev_addr[2] = (u8)(mac_low32 >> 16);
+ dev->dev_addr[3] = (u8)(mac_low32 >> 24);
+ dev->dev_addr[4] = (u8)(mac_high16);
+ dev->dev_addr[5] = (u8)(mac_high16 >> 8);
+
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ /* eeprom values are valid so use them */
+ SMSC_TRACE("Mac Address is read from LAN911x EEPROM");
+ } else {
+ /* eeprom values are invalid, generate random MAC */
+ random_ether_addr(dev->dev_addr);
+ smsc911x_set_mac_address(pdata, dev->dev_addr);
+ SMSC_TRACE("MAC Address is set to random_ether_addr");
+ }
+ }
+
+ printk(KERN_INFO
+ "%s: SMSC911x MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = smsc911x_poll_controller;
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+ pdata->mii.phy_id_mask = 0x1f;
+ pdata->mii.reg_num_mask = 0x1f;
+ pdata->mii.force_media = 0;
+ pdata->mii.full_duplex = 0;
+ pdata->mii.dev = dev;
+ pdata->mii.mdio_read = smsc911x_mdio_read;
+ pdata->mii.mdio_write = smsc911x_mdio_write;
+
+ pdata->msg_enable = NETIF_MSG_LINK;
+
+ return 0;
+}
+
+static int smsc911x_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct resource *res;
+
+ dev = platform_get_drvdata(pdev);
+ BUG_ON(!dev);
+ pdata = netdev_priv(dev);
+ BUG_ON(!pdata);
+ BUG_ON(!pdata->ioaddr);
+
+ SMSC_TRACE("Stopping driver.");
+ platform_set_drvdata(pdev, NULL);
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ release_mem_region(res->start, res->end - res->start);
+
+ iounmap(pdata->ioaddr);
+
+ free_netdev(dev);
+
+ return 0;
+}
+
+static int smsc911x_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct resource *res;
+ unsigned int intcfg = 0;
+ int res_size;
+ int retval;
+
+ printk(KERN_INFO "%s: Driver version %s.\n", SMSC_CHIPNAME,
+ SMSC_DRV_VERSION);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ printk(KERN_WARNING "%s: Could not allocate resource.\n",
+ SMSC_CHIPNAME);
+ retval = -ENODEV;
+ goto out_0;
+ }
+ res_size = res->end - res->start;
+
+ if (!request_mem_region(res->start, res_size, SMSC_CHIPNAME)) {
+ retval = -EBUSY;
+ goto out_0;
+ }
+
+ dev = alloc_etherdev(sizeof(struct smsc911x_data));
+ if (!dev) {
+ printk(KERN_WARNING "%s: Could not allocate device.\n",
+ SMSC_CHIPNAME);
+ retval = -ENOMEM;
+ goto out_release_io_1;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ pdata = netdev_priv(dev);
+ pdata->netdev = dev;
+
+ dev->irq = platform_get_irq(pdev, 0);
+ pdata->ioaddr = ioremap_nocache(res->start, res_size);
+
+ /* copy config parameters across if present, otherwise pdata
+ * defaults to zeros */
+ if (pdev->dev.platform_data) {
+ struct smsc911x_platform_config *config = pdev->dev.platform_data;
+ pdata->irq_polarity = config->irq_polarity;
+ pdata->irq_type = config->irq_type;
+ }
+
+ if (pdata->ioaddr == NULL) {
+ SMSC_WARNING("Error smsc911x base address invalid");
+ retval = -ENOMEM;
+ goto out_free_netdev_2;
+ }
+
+ if ((retval = smsc911x_init(dev)) < 0)
+ goto out_unmap_io_3;
+
+ /* configure irq polarity and type before connecting isr */
+ if (pdata->irq_polarity)
+ intcfg |= INT_CFG_IRQ_POL_;
+
+ if (pdata->irq_type)
+ intcfg |= INT_CFG_IRQ_TYPE_;
+
+ smsc911x_reg_write(intcfg, pdata, INT_CFG);
+
+ retval = request_irq(dev->irq, smsc911x_irqhandler, IRQF_DISABLED,
+ SMSC_CHIPNAME, dev);
+ if (retval) {
+ SMSC_WARNING("Unable to claim requested irq: %d", dev->irq);
+ goto out_unmap_io_3;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ retval = register_netdev(dev);
+ if (retval) {
+ SMSC_WARNING("Error %i registering device", retval);
+ goto out_unset_drvdata_4;
+ } else {
+ SMSC_TRACE("Network interface: \"%s\"", dev->name);
+ }
+
+ return 0;
+
+out_unset_drvdata_4:
+ platform_set_drvdata(pdev, NULL);
+ free_irq(dev->irq, dev);
+out_unmap_io_3:
+ iounmap(pdata->ioaddr);
+out_free_netdev_2:
+ free_netdev(dev);
+out_release_io_1:
+ release_mem_region(res->start, res->end - res->start);
+out_0:
+ return retval;
+}
+
+static int smsc911x_drv_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ unsigned long flags;
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+
+ dev = platform_get_drvdata(pdev);
+ BUG_ON(!dev);
+ pdata = netdev_priv(dev);
+ BUG_ON(!pdata);
+ BUG_ON(!pdata->ioaddr);
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_PDOWN);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+
+ smsc911x_reg_write(PMT_CTRL_PM_MODE_D2_, pdata, PMT_CTRL);
+
+ return 0;
+}
+
+static int smsc911x_drv_resume(struct platform_device *pdev)
+{
+ unsigned long flags;
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ unsigned int temp;
+
+ dev = platform_get_drvdata(pdev);
+ BUG_ON(!dev);
+ pdata = netdev_priv(dev);
+ BUG_ON(!pdata);
+ BUG_ON(!pdata->ioaddr);
+
+ smsc911x_reg_write(0xFFFFFFFF, pdata, BYTE_TEST);
+ while (!(smsc911x_reg_read(pdata, PMT_CTRL) & PMT_CTRL_READY_))
+ ;
+
+ spin_lock_irqsave(&pdata->phy_lock, flags);
+ smsc911x_phy_write(pdata, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+ spin_unlock_irqrestore(&pdata->phy_lock, flags);
+ return 0;
+}
+
+static struct platform_driver smsc911x_driver = {
+ .probe = smsc911x_drv_probe,
+ .remove = smsc911x_drv_remove,
+ .suspend = smsc911x_drv_suspend,
+ .resume = smsc911x_drv_resume,
+ .driver = {
+ .name = SMSC_CHIPNAME,
+ },
+};
+
+/* Entry point for loading the module */
+static int __init smsc911x_init_module(void)
+{
+ return platform_driver_register(&smsc911x_driver);
+}
+
+/* entry point for unloading the module */
+static void __exit smsc911x_cleanup_module(void)
+{
+ platform_driver_unregister(&smsc911x_driver);
+}
+
+module_init(smsc911x_init_module);
+module_exit(smsc911x_cleanup_module);
diff --git a/drivers/net/smsc911x.h b/drivers/net/smsc911x.h
new file mode 100644
index 000000000000..d67c9971b136
--- /dev/null
+++ b/drivers/net/smsc911x.h
@@ -0,0 +1,395 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2007 SMSC
+ * Copyright (C) 2005 ARM
+ *
+ * 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 __SMSC911X_H__
+#define __SMSC911X_H__
+
+#if defined(CONFIG_MACH_MX37_3DS) || defined(CONFIG_MACH_MX25_3DS)
+#define SMSC_CAN_USE_SPI 1
+#define SMSC_CAN_USE_32BIT 0
+#elif defined(CONFIG_MACH_MX31_3DS) || defined(CONFIG_MACH_MX35_3DS)
+#define SMSC_CAN_USE_32BIT 0
+#define SMSC_CAN_USE_SPI 0
+#else
+#define SMSC_CAN_USE_32BIT 1
+#define SMSC_CAN_USE_SPI 0
+#endif
+
+#define TX_FIFO_LOW_THRESHOLD (u32)1600
+#define SMSC911X_EEPROM_SIZE (u32)7
+#define USE_DEBUG 0
+
+/* implements a PHY loopback test at initialisation time, to ensure a packet
+ * can be succesfully looped back */
+#define USE_PHY_WORK_AROUND
+
+/* 10/100 LED link-state inversion when media is disconnected */
+#define USE_LED1_WORK_AROUND
+
+/* platform_device configuration data, should be assigned to
+ * the platform_device's dev.platform_data */
+struct smsc911x_platform_config {
+ unsigned int irq_polarity;
+ unsigned int irq_type;
+};
+
+#if USE_DEBUG >= 1
+#define SMSC_WARNING(fmt, args...) \
+ printk(KERN_EMERG "SMSC_WARNING: %s: " fmt "\n", \
+ __FUNCTION__ , ## args)
+#else
+#define SMSC_WARNING(msg, args...)
+#endif /* USE_DEBUG >= 1 */
+
+#if USE_DEBUG >= 2
+#define SMSC_TRACE(fmt,args...) \
+ printk(KERN_EMERG "SMSC_TRACE: %s: " fmt "\n", \
+ __FUNCTION__ , ## args)
+#else
+#define SMSC_TRACE(msg,args...)
+#endif /* USE_DEBUG >= 2 */
+
+/* SMSC911x registers and bitfields */
+#define RX_DATA_FIFO 0x00
+
+#define TX_DATA_FIFO 0x20
+#define TX_CMD_A_ON_COMP_ 0x80000000
+#define TX_CMD_A_BUF_END_ALGN_ 0x03000000
+#define TX_CMD_A_4_BYTE_ALGN_ 0x00000000
+#define TX_CMD_A_16_BYTE_ALGN_ 0x01000000
+#define TX_CMD_A_32_BYTE_ALGN_ 0x02000000
+#define TX_CMD_A_DATA_OFFSET_ 0x001F0000
+#define TX_CMD_A_FIRST_SEG_ 0x00002000
+#define TX_CMD_A_LAST_SEG_ 0x00001000
+#define TX_CMD_A_BUF_SIZE_ 0x000007FF
+#define TX_CMD_B_PKT_TAG_ 0xFFFF0000
+#define TX_CMD_B_ADD_CRC_DISABLE_ 0x00002000
+#define TX_CMD_B_DISABLE_PADDING_ 0x00001000
+#define TX_CMD_B_PKT_BYTE_LENGTH_ 0x000007FF
+
+#define RX_STATUS_FIFO 0x40
+#define RX_STS_ES_ 0x00008000
+#define RX_STS_MCAST_ 0x00000400
+
+#define RX_STATUS_FIFO_PEEK 0x44
+
+#define TX_STATUS_FIFO 0x48
+#define TX_STS_ES_ 0x00008000
+
+#define TX_STATUS_FIFO_PEEK 0x4C
+
+#define ID_REV 0x50
+#define ID_REV_CHIP_ID_ 0xFFFF0000
+#define ID_REV_REV_ID_ 0x0000FFFF
+
+#define INT_CFG 0x54
+#define INT_CFG_INT_DEAS_ 0xFF000000
+#define INT_CFG_INT_DEAS_CLR_ 0x00004000
+#define INT_CFG_INT_DEAS_STS_ 0x00002000
+#define INT_CFG_IRQ_INT_ 0x00001000
+#define INT_CFG_IRQ_EN_ 0x00000100
+#define INT_CFG_IRQ_POL_ 0x00000010
+#define INT_CFG_IRQ_TYPE_ 0x00000001
+
+#define INT_STS 0x58
+#define INT_STS_SW_INT_ 0x80000000
+#define INT_STS_TXSTOP_INT_ 0x02000000
+#define INT_STS_RXSTOP_INT_ 0x01000000
+#define INT_STS_RXDFH_INT_ 0x00800000
+#define INT_STS_RXDF_INT_ 0x00400000
+#define INT_STS_TX_IOC_ 0x00200000
+#define INT_STS_RXD_INT_ 0x00100000
+#define INT_STS_GPT_INT_ 0x00080000
+#define INT_STS_PHY_INT_ 0x00040000
+#define INT_STS_PME_INT_ 0x00020000
+#define INT_STS_TXSO_ 0x00010000
+#define INT_STS_RWT_ 0x00008000
+#define INT_STS_RXE_ 0x00004000
+#define INT_STS_TXE_ 0x00002000
+#define INT_STS_TDFU_ 0x00000800
+#define INT_STS_TDFO_ 0x00000400
+#define INT_STS_TDFA_ 0x00000200
+#define INT_STS_TSFF_ 0x00000100
+#define INT_STS_TSFL_ 0x00000080
+#define INT_STS_RXDF_ 0x00000040
+#define INT_STS_RDFL_ 0x00000020
+#define INT_STS_RSFF_ 0x00000010
+#define INT_STS_RSFL_ 0x00000008
+#define INT_STS_GPIO2_INT_ 0x00000004
+#define INT_STS_GPIO1_INT_ 0x00000002
+#define INT_STS_GPIO0_INT_ 0x00000001
+
+#define INT_EN 0x5C
+#define INT_EN_SW_INT_EN_ 0x80000000
+#define INT_EN_TXSTOP_INT_EN_ 0x02000000
+#define INT_EN_RXSTOP_INT_EN_ 0x01000000
+#define INT_EN_RXDFH_INT_EN_ 0x00800000
+#define INT_EN_TIOC_INT_EN_ 0x00200000
+#define INT_EN_RXD_INT_EN_ 0x00100000
+#define INT_EN_GPT_INT_EN_ 0x00080000
+#define INT_EN_PHY_INT_EN_ 0x00040000
+#define INT_EN_PME_INT_EN_ 0x00020000
+#define INT_EN_TXSO_EN_ 0x00010000
+#define INT_EN_RWT_EN_ 0x00008000
+#define INT_EN_RXE_EN_ 0x00004000
+#define INT_EN_TXE_EN_ 0x00002000
+#define INT_EN_TDFU_EN_ 0x00000800
+#define INT_EN_TDFO_EN_ 0x00000400
+#define INT_EN_TDFA_EN_ 0x00000200
+#define INT_EN_TSFF_EN_ 0x00000100
+#define INT_EN_TSFL_EN_ 0x00000080
+#define INT_EN_RXDF_EN_ 0x00000040
+#define INT_EN_RDFL_EN_ 0x00000020
+#define INT_EN_RSFF_EN_ 0x00000010
+#define INT_EN_RSFL_EN_ 0x00000008
+#define INT_EN_GPIO2_INT_ 0x00000004
+#define INT_EN_GPIO1_INT_ 0x00000002
+#define INT_EN_GPIO0_INT_ 0x00000001
+
+#define BYTE_TEST 0x64
+
+#define FIFO_INT 0x68
+#define FIFO_INT_TX_AVAIL_LEVEL_ 0xFF000000
+#define FIFO_INT_TX_STS_LEVEL_ 0x00FF0000
+#define FIFO_INT_RX_AVAIL_LEVEL_ 0x0000FF00
+#define FIFO_INT_RX_STS_LEVEL_ 0x000000FF
+
+#define RX_CFG 0x6C
+#define RX_CFG_RX_END_ALGN_ 0xC0000000
+#define RX_CFG_RX_END_ALGN4_ 0x00000000
+#define RX_CFG_RX_END_ALGN16_ 0x40000000
+#define RX_CFG_RX_END_ALGN32_ 0x80000000
+#define RX_CFG_RX_DMA_CNT_ 0x0FFF0000
+#define RX_CFG_RX_DUMP_ 0x00008000
+#define RX_CFG_RXDOFF_ 0x00001F00
+
+#define TX_CFG 0x70
+#define TX_CFG_TXS_DUMP_ 0x00008000
+#define TX_CFG_TXD_DUMP_ 0x00004000
+#define TX_CFG_TXSAO_ 0x00000004
+#define TX_CFG_TX_ON_ 0x00000002
+#define TX_CFG_STOP_TX_ 0x00000001
+
+#define HW_CFG 0x74
+#define HW_CFG_TTM_ 0x00200000
+#define HW_CFG_SF_ 0x00100000
+#define HW_CFG_TX_FIF_SZ_ 0x000F0000
+#define HW_CFG_TR_ 0x00003000
+#define HW_CFG_SRST_ 0x00000001
+
+/* only available on 115/117 */
+#define HW_CFG_PHY_CLK_SEL_ 0x00000060
+#define HW_CFG_PHY_CLK_SEL_INT_PHY_ 0x00000000
+#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ 0x00000020
+#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ 0x00000040
+#define HW_CFG_SMI_SEL_ 0x00000010
+#define HW_CFG_EXT_PHY_DET_ 0x00000008
+#define HW_CFG_EXT_PHY_EN_ 0x00000004
+#define HW_CFG_SRST_TO_ 0x00000002
+
+/* only available on 116/118 */
+#define HW_CFG_32_16_BIT_MODE_ 0x00000004
+
+#define RX_DP_CTRL 0x78
+#define RX_DP_CTRL_RX_FFWD_ 0x80000000
+
+#define RX_FIFO_INF 0x7C
+#define RX_FIFO_INF_RXSUSED_ 0x00FF0000
+#define RX_FIFO_INF_RXDUSED_ 0x0000FFFF
+
+#define TX_FIFO_INF 0x80
+#define TX_FIFO_INF_TSUSED_ 0x00FF0000
+#define TX_FIFO_INF_TDFREE_ 0x0000FFFF
+
+#define PMT_CTRL 0x84
+#define PMT_CTRL_PM_MODE_ 0x00003000
+#define PMT_CTRL_PM_MODE_D0_ 0x00000000
+#define PMT_CTRL_PM_MODE_D1_ 0x00001000
+#define PMT_CTRL_PM_MODE_D2_ 0x00002000
+#define PMT_CTRL_PM_MODE_D3_ 0x00003000
+#define PMT_CTRL_PHY_RST_ 0x00000400
+#define PMT_CTRL_WOL_EN_ 0x00000200
+#define PMT_CTRL_ED_EN_ 0x00000100
+#define PMT_CTRL_PME_TYPE_ 0x00000040
+#define PMT_CTRL_WUPS_ 0x00000030
+#define PMT_CTRL_WUPS_NOWAKE_ 0x00000000
+#define PMT_CTRL_WUPS_ED_ 0x00000010
+#define PMT_CTRL_WUPS_WOL_ 0x00000020
+#define PMT_CTRL_WUPS_MULTI_ 0x00000030
+#define PMT_CTRL_PME_IND_ 0x00000008
+#define PMT_CTRL_PME_POL_ 0x00000004
+#define PMT_CTRL_PME_EN_ 0x00000002
+#define PMT_CTRL_READY_ 0x00000001
+
+#define GPIO_CFG 0x88
+#define GPIO_CFG_LED3_EN_ 0x40000000
+#define GPIO_CFG_LED2_EN_ 0x20000000
+#define GPIO_CFG_LED1_EN_ 0x10000000
+#define GPIO_CFG_GPIO2_INT_POL_ 0x04000000
+#define GPIO_CFG_GPIO1_INT_POL_ 0x02000000
+#define GPIO_CFG_GPIO0_INT_POL_ 0x01000000
+#define GPIO_CFG_EEPR_EN_ 0x00700000
+#define GPIO_CFG_GPIOBUF2_ 0x00040000
+#define GPIO_CFG_GPIOBUF1_ 0x00020000
+#define GPIO_CFG_GPIOBUF0_ 0x00010000
+#define GPIO_CFG_GPIODIR2_ 0x00000400
+#define GPIO_CFG_GPIODIR1_ 0x00000200
+#define GPIO_CFG_GPIODIR0_ 0x00000100
+#define GPIO_CFG_GPIOD4_ 0x00000020
+#define GPIO_CFG_GPIOD3_ 0x00000010
+#define GPIO_CFG_GPIOD2_ 0x00000004
+#define GPIO_CFG_GPIOD1_ 0x00000002
+#define GPIO_CFG_GPIOD0_ 0x00000001
+
+#define GPT_CFG 0x8C
+#define GPT_CFG_TIMER_EN_ 0x20000000
+#define GPT_CFG_GPT_LOAD_ 0x0000FFFF
+
+#define GPT_CNT 0x90
+#define GPT_CNT_GPT_CNT_ 0x0000FFFF
+
+#define ENDIAN 0x98
+
+#define FREE_RUN 0x9C
+
+#define RX_DROP 0xA0
+
+#define MAC_CSR_CMD 0xA4
+#define MAC_CSR_CMD_CSR_BUSY_ 0x80000000
+#define MAC_CSR_CMD_R_NOT_W_ 0x40000000
+#define MAC_CSR_CMD_CSR_ADDR_ 0x000000FF
+
+#define MAC_CSR_DATA 0xA8
+
+#define AFC_CFG 0xAC
+#define AFC_CFG_AFC_HI_ 0x00FF0000
+#define AFC_CFG_AFC_LO_ 0x0000FF00
+#define AFC_CFG_BACK_DUR_ 0x000000F0
+#define AFC_CFG_FCMULT_ 0x00000008
+#define AFC_CFG_FCBRD_ 0x00000004
+#define AFC_CFG_FCADD_ 0x00000002
+#define AFC_CFG_FCANY_ 0x00000001
+
+#define E2P_CMD 0xB0
+#define E2P_CMD_EPC_BUSY_ 0x80000000
+#define E2P_CMD_EPC_CMD_ 0x70000000
+#define E2P_CMD_EPC_CMD_READ_ 0x00000000
+#define E2P_CMD_EPC_CMD_EWDS_ 0x10000000
+#define E2P_CMD_EPC_CMD_EWEN_ 0x20000000
+#define E2P_CMD_EPC_CMD_WRITE_ 0x30000000
+#define E2P_CMD_EPC_CMD_WRAL_ 0x40000000
+#define E2P_CMD_EPC_CMD_ERASE_ 0x50000000
+#define E2P_CMD_EPC_CMD_ERAL_ 0x60000000
+#define E2P_CMD_EPC_CMD_RELOAD_ 0x70000000
+#define E2P_CMD_EPC_TIMEOUT_ 0x00000200
+#define E2P_CMD_MAC_ADDR_LOADED_ 0x00000100
+#define E2P_CMD_EPC_ADDR_ 0x000000FF
+
+#define E2P_DATA 0xB4
+#define E2P_DATA_EEPROM_DATA_ 0x000000FF
+#define LAN_REGISTER_EXTENT 0x00000100
+
+/*
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ */
+#define MAC_CR 0x01
+#define MAC_CR_RXALL_ 0x80000000
+#define MAC_CR_HBDIS_ 0x10000000
+#define MAC_CR_RCVOWN_ 0x00800000
+#define MAC_CR_LOOPBK_ 0x00200000
+#define MAC_CR_FDPX_ 0x00100000
+#define MAC_CR_MCPAS_ 0x00080000
+#define MAC_CR_PRMS_ 0x00040000
+#define MAC_CR_INVFILT_ 0x00020000
+#define MAC_CR_PASSBAD_ 0x00010000
+#define MAC_CR_HFILT_ 0x00008000
+#define MAC_CR_HPFILT_ 0x00002000
+#define MAC_CR_LCOLL_ 0x00001000
+#define MAC_CR_BCAST_ 0x00000800
+#define MAC_CR_DISRTY_ 0x00000400
+#define MAC_CR_PADSTR_ 0x00000100
+#define MAC_CR_BOLMT_MASK_ 0x000000C0
+#define MAC_CR_DFCHK_ 0x00000020
+#define MAC_CR_TXEN_ 0x00000008
+#define MAC_CR_RXEN_ 0x00000004
+
+#define ADDRH 0x02
+
+#define ADDRL 0x03
+
+#define HASHH 0x04
+
+#define HASHL 0x05
+
+#define MII_ACC 0x06
+#define MII_ACC_PHY_ADDR_ 0x0000F800
+#define MII_ACC_MIIRINDA_ 0x000007C0
+#define MII_ACC_MII_WRITE_ 0x00000002
+#define MII_ACC_MII_BUSY_ 0x00000001
+
+#define MII_DATA 0x07
+
+#define FLOW 0x08
+#define FLOW_FCPT_ 0xFFFF0000
+#define FLOW_FCPASS_ 0x00000004
+#define FLOW_FCEN_ 0x00000002
+#define FLOW_FCBSY_ 0x00000001
+
+#define VLAN1 0x09
+
+#define VLAN2 0x0A
+
+#define WUFF 0x0B
+
+#define WUCSR 0x0C
+#define WUCSR_GUE_ 0x00000200
+#define WUCSR_WUFR_ 0x00000040
+#define WUCSR_MPR_ 0x00000020
+#define WUCSR_WAKE_EN_ 0x00000004
+#define WUCSR_MPEN_ 0x00000002
+
+/*
+ * Phy definitions (vendor-specific)
+ */
+#define LAN9118_PHY_ID 0x00C0001C
+
+#define MII_INTSTS 0x1D
+
+#define MII_INTMSK 0x1E
+#define PHY_INTMSK_AN_RCV_ (1 << 1)
+#define PHY_INTMSK_PDFAULT_ (1 << 2)
+#define PHY_INTMSK_AN_ACK_ (1 << 3)
+#define PHY_INTMSK_LNKDOWN_ (1 << 4)
+#define PHY_INTMSK_RFAULT_ (1 << 5)
+#define PHY_INTMSK_AN_COMP_ (1 << 6)
+#define PHY_INTMSK_ENERGYON_ (1 << 7)
+#define PHY_INTMSK_DEFAULT_ (PHY_INTMSK_ENERGYON_ | \
+ PHY_INTMSK_AN_COMP_ | \
+ PHY_INTMSK_RFAULT_ | \
+ PHY_INTMSK_LNKDOWN_)
+
+#define ADVERTISE_PAUSE_ALL (ADVERTISE_PAUSE_CAP | \
+ ADVERTISE_PAUSE_ASYM)
+
+#define LPA_PAUSE_ALL (LPA_PAUSE_CAP | \
+ LPA_PAUSE_ASYM)
+
+#endif /* __SMSC911X_H__ */
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 45bdf0b339bb..46bcaca38b46 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -717,5 +717,44 @@ source "drivers/net/wireless/b43/Kconfig"
source "drivers/net/wireless/b43legacy/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/rt2x00/Kconfig"
+source "drivers/net/wireless/digiPiper/Kconfig"
+
+config DIGI_WI_G
+ tristate "Digi Wireless Module 802.11ab/g"
+ depends on WLAN_80211 && MACH_CCW9C
+ select IEEE80211
+ select IEEE80211_SOFTMAC
+ ---help---
+ A 802.11ab/g wireless driver for Digi ConnectCore Wi-9C modules.
+
+config DIGI_WI_G_HW_ENCRYPTION
+ bool "Use HW encryption"
+ depends on DIGI_WI_G
+ default y
+ help
+ AES/CCMP decoding is performed by using hardware logic instead of
+ software emulation. This can also be adjusted at run time with the
+ kernel command line option dw_sw_aes=0
+
+config DIGI_WI_G_DEBUG
+ bool "Debugging support"
+ depends on DIGI_WI_G
+ default n
+ help
+ The digi_wi_g driver is compiled with debugging support.
+ Only the driver maintainer may need to enable it.
+
+choice
+ prompt "UBEC Transceiver Revision"
+ depends on DIGI_WI_G
+ default DIGI_WI_G_UBEC_JD
+
+config DIGI_WI_G_UBEC_JD
+ bool "UBEC Revision JD"
+
+config DIGI_WI_G_UBEC_HC
+ bool "UBEC Revision HC"
+
+endchoice
endmenu
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 59d2d805f60b..5fcf43bbf8c5 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_HOSTAP) += hostap/
obj-$(CONFIG_B43) += b43/
obj-$(CONFIG_B43LEGACY) += b43legacy/
obj-$(CONFIG_ZD1211RW) += zd1211rw/
+obj-$(CONFIG_DIGI_PIPER_WIFI) += digiPiper/
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
@@ -67,3 +68,4 @@ obj-$(CONFIG_ATH5K) += ath5k/
obj-$(CONFIG_ATH9K) += ath9k/
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
+obj-$(CONFIG_DIGI_WI_G) += digi_wi_g.o
diff --git a/drivers/net/wireless/digiPiper/Kconfig b/drivers/net/wireless/digiPiper/Kconfig
new file mode 100644
index 000000000000..aa0659f0e499
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/Kconfig
@@ -0,0 +1,27 @@
+config DIGI_PIPER_WIFI
+ bool "Digi Piper Wifi support"
+ depends on MAC80211 && WLAN_80211 && I2C && (MACH_CCW9P9215JS || MACH_CCW9M2443JS)
+ ---help---
+ This driver is for the Piper 802.11 MAC by Digi. This MAC is
+ supported on the Digi ConnectCore Wi-9P 9215 and Digi ConnectCore Wi-9M 2443
+ embedded modules.
+
+config PIPER_STATUS_LED
+ bool "Enable GPIO for Wifi status LED"
+ depends on DIGI_PIPER_WIFI && MACH_CCW9M2443JS
+ default y
+ help
+ ConnectCore Wi-9M 2443 does not provide any dedicated LED
+ in the module for showing the Wifi status.
+ This option lets you define one available CPU GPIO for this purpose.
+ Default value is set to USER LED 1 on JumpStart board.
+
+config PIPER_STATUS_LED_GPIO
+ int "GPIO for Wifi status LED (0-144)"
+ range 0 144
+ depends on PIPER_STATUS_LED
+ default "141"
+ help
+ Set CPU GPIO for Wifi status LED.
+
+ Default: User LED 1 on JumpStart board (LE5)
diff --git a/drivers/net/wireless/digiPiper/Makefile b/drivers/net/wireless/digiPiper/Makefile
new file mode 100644
index 000000000000..a3686b942088
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/Makefile
@@ -0,0 +1,16 @@
+
+mpiper-y := piper.o
+mpiper-y += airoha.o
+mpiper-y += digiDebug.o
+mpiper-y += digiIsr.o
+mpiper-y += digiTx.o
+mpiper-y += digiRx.o
+mpiper-y += digiMac80211.o
+mpiper-y += piperDsp.o
+mpiper-y += piperMacAssist.o
+mpiper-y += phy.o
+mpiper-y += adc121c027.o
+mpiper-y += airohaCalibration.o
+mpiper-y += digiPs.o
+obj-$(CONFIG_DIGI_PIPER_WIFI) := mpiper.o
+
diff --git a/drivers/net/wireless/digiPiper/adc121c027.c b/drivers/net/wireless/digiPiper/adc121c027.c
new file mode 100644
index 000000000000..17564bcd7a66
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/adc121c027.c
@@ -0,0 +1,161 @@
+/*
+ adc121C027.c - Analog to Digital converter integrated into Piper.
+
+ Copyright (C) 2009 Digi International <sales2@digi.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/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon-sysfs.h>
+
+#include "airohaCalibration.h"
+#include "pipermain.h"
+
+/* Addresses to scan: none, device is not autodetected */
+/* static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; */
+
+#define ADC_I2C_ADDR (0x51)
+#define ADC_CYCLE_TIME (0x20)
+
+static const unsigned short normal_i2c[] = { ADC_I2C_ADDR, I2C_CLIENT_END };
+static const unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr = {
+ .normal_i2c = normal_i2c,
+ .probe = dummy_i2c_addrlist,
+ .ignore = dummy_i2c_addrlist,
+};
+
+enum adc121C027_cmd {
+ ADC_RESULT = 0,
+ ADC_ALERT_STATUS = 1,
+ ADC_CONFIGURATION = 2,
+ ADC_LOW_LIMIT = 3,
+ ADC_HIGH_LIMIT = 4,
+ ADC_HYSTERESIS = 5,
+ ADC_LOWEST_VALUE = 6,
+ ADC_HIGHEST_VALUE = 7,
+};
+
+static u16 adc121C027_read_peak(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ return be16_to_cpu(i2c_smbus_read_word_data(i2cclient, ADC_HIGHEST_VALUE));
+}
+
+static void adc121C027_clear_peak(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ i2c_smbus_write_word_data(i2cclient, ADC_HIGHEST_VALUE, 0);
+}
+
+static u16 adc121C027_read_last_sample(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ return be16_to_cpu(i2c_smbus_read_word_data(i2cclient, ADC_RESULT));
+}
+
+static int adc121C027_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ return (client->addr != ADC_I2C_ADDR) ? -EINVAL : 0;
+}
+
+static int adc121C027_remove(struct i2c_client *client)
+{
+ /* Real shut down will be done by adc121C027_shutdown() */
+ return 0;
+}
+
+static const struct i2c_device_id adc121C027_id[] = {
+ { "adc121C027", 0 },
+ {}
+};
+
+static struct i2c_driver adc121C027_driver = {
+ .driver = {
+ .name = "adc121C027",
+ },
+ .probe = adc121C027_probe,
+ .remove = __devexit_p(adc121C027_remove),
+ .id_table = adc121C027_id,
+ .address_data = &addr,
+};
+
+/* Turn on automatic A/D process by setting a non zero cycle time */
+static void adc121C027_hw_init(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ i2c_smbus_write_word_data(i2cclient, ADC_CONFIGURATION, ADC_CYCLE_TIME);
+}
+
+void adc121C027_shutdown(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ if (i2cclient) {
+ i2c_unregister_device(i2cclient);
+ cal->priv = NULL;
+ }
+ i2c_del_driver(&adc121C027_driver);
+}
+
+int adc121C027_init(struct airohaCalibrationData *cal, int i2cadapter)
+{
+ struct i2c_board_info board_info = {
+ .type = "adc121C027",
+ .addr = ADC_I2C_ADDR,
+ };
+ struct i2c_adapter *adapter;
+ struct i2c_client *adc_i2c_client;
+ int ret;
+
+ ret = i2c_add_driver(&adc121C027_driver);
+ if (ret) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error adding driver adc121C027_driver (%d)\n", ret);
+ return ret;
+ }
+
+ adapter = i2c_get_adapter(i2cadapter);
+ if (!adapter) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error getting i2c adapter\n");
+ return -EINVAL;
+ }
+
+ adc_i2c_client = i2c_new_device(adapter, &board_info);
+ if (!adc_i2c_client) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error creating new i2c client\n");
+ return -EINVAL;
+ }
+
+ cal->priv = (void *)adc_i2c_client;
+ adc121C027_hw_init(cal);
+
+ cal->cops = kmalloc(sizeof(struct calibration_ops), GFP_KERNEL);
+ if (!cal->cops) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": unable to allocate memory for cal->cops\n");
+ return -ENOMEM;
+ }
+ cal->cops->adc_read_peak = adc121C027_read_peak;
+ cal->cops->adc_clear_peak = adc121C027_clear_peak;
+ cal->cops->adc_read_last_val = adc121C027_read_last_sample;
+ cal->cops->adc_shutdown = adc121C027_shutdown;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adc121C027_init);
+
+
diff --git a/drivers/net/wireless/digiPiper/adc121c027.h b/drivers/net/wireless/digiPiper/adc121c027.h
new file mode 100644
index 000000000000..1453d8a66963
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/adc121c027.h
@@ -0,0 +1,17 @@
+/*
+ adc121C027.h - Analog to Digital converter integrated into Piper.
+
+ Copyright (C) 2009 Digi International <sales2@digi.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 ADC121C027_H
+#define ADC121C027_H
+
+int adc121C027_init(struct airohaCalibrationData *cal, int i2cadapter);
+
+#endif /* ADC121C027_H */
+
diff --git a/drivers/net/wireless/digiPiper/airoha.c b/drivers/net/wireless/digiPiper/airoha.c
new file mode 100644
index 000000000000..17ef8a812f98
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airoha.c
@@ -0,0 +1,986 @@
+/*
+ * Ubec AH7230 radio support.
+ *
+ * Copyright © 2009 Digi International, Inc
+ *
+ * Author: Contact support@digi.com for information about this software.
+ *
+ * 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>
+#include <linux/delay.h>
+#include <net/mac80211.h>
+#include <net/wireless.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airohaCalibration.h"
+#include "airoha.h"
+
+/*
+ * Number of us to change channels. I counted the number of udelays once
+ * and it was about 2030, plus the 1 us delays for each register write.
+ * So probably about 2200 in reality, I'm saying 2500 to be safe.
+ */
+#define CHANNEL_CHANGE_TIME (2500)
+
+/*
+ * Maximum possible receive signal strength in dbm. Most of the
+ * values will be negative.
+ */
+#define MAX_SIGNAL_IN_DBM (5)
+
+#define read_reg(reg) priv->ac->rd_reg(priv,reg)
+#define write_reg(reg,val,op) priv->ac->wr_reg(priv,reg,val,op)
+#define mac_set_tx_power(x) al7230_set_txpwr(hw,x)
+
+static unsigned int hw_revision = WCD_HW_REV_A;
+static unsigned int hw_platform = WCD_CCW9P_PLATFORM;
+
+static void InitializeRF(struct ieee80211_hw *hw, int band_selection);
+static int al7230_set_txpwr(struct ieee80211_hw *hw, uint8_t val);
+
+static const struct {
+ unsigned int integer;
+ unsigned int fraction;
+ unsigned int address4;
+ unsigned int tracking;
+} freqTableAiroha_7230[] = {
+ { 0, 0, 0, 0 }, // 0
+
+ // 2.4 GHz band (802.11b/g)
+ { 0x00379, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-1 (2412 MHz) 1
+ { 0x00379, 0x1B333, 0x7FD78, TRACK_BG_BAND }, // B-2 (2417 MHz) 2
+ { 0x00379, 0x03333, 0x7FD78, TRACK_BG_BAND }, // B-3 (2422 MHz) 3
+ { 0x00379, 0x0B333, 0x7FD78, TRACK_BG_BAND }, // B-4 (2427 MHz) 4
+ { 0x0037A, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-5 (2432 MHz) 5
+ { 0x0037A, 0x1B333, 0x7FD78, TRACK_BG_BAND }, // B-6 (2437 MHz) 6
+ { 0x0037A, 0x03333, 0x7FD78, TRACK_BG_BAND }, // B-7 (2442 MHz) 7
+ { 0x0037A, 0x0B333, 0x7FD78, TRACK_BG_BAND }, // B-8 (2447 MHz) 8
+ { 0x0037B, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-9 (2452 MHz) 9
+ { 0x0037B, 0x1B333, 0x7FD78, TRACK_BG_BAND }, // B-10 (2457 MHz) 10
+ { 0x0037B, 0x03333, 0x7FD78, TRACK_BG_BAND }, // B-11 (2462 MHz) 11
+ { 0x0037B, 0x0B333, 0x7FD78, TRACK_BG_BAND }, // B-12 (2467 MHz) 12
+ { 0x0037C, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-13 (2472 MHz) 13
+ { 0x0037C, 0x06666, 0x7FD78, TRACK_BG_BAND }, // B-14 (2484 MHz) 14
+
+ { 0, 0, 0, 0 }, // reserved for future b/g expansion 15
+ { 0, 0, 0, 0 }, // reserved for future b/g expansion 16
+
+ // Extended 4 GHz bands (802.11a) - Lower Band
+ { 0x0FF52, 0x00000, 0x67F78, TRACK_4920_4980_A_BAND }, // L-184 (4920 MHz) 17
+ { 0x0FF52, 0x0AAAA, 0x77F78, TRACK_4920_4980_A_BAND }, // L-188 (4940 MHz) 18
+ { 0x0FF53, 0x15555, 0x77F78, TRACK_4920_4980_A_BAND }, // L-192 (4960 MHz) 19
+ { 0x0FF53, 0x00000, 0x67F78, TRACK_4920_4980_A_BAND }, // L-196 (4980 MHz) 20
+
+ // Extended 5 GHz bands (802.11a)
+ { 0x0FF54, 0x00000, 0x67F78, TRACK_5150_5350_A_BAND }, // A-8 (5040 MHz) 21 tracking?
+ { 0x0FF54, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-12 (5060 MHz) 22 tracking?
+ { 0x0FF55, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-16 (5080 MHz) 23 tracking?
+ { 0x0FF56, 0x05555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-34 (5170 MHz) 24
+ { 0x0FF56, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-36 (5180 MHz) 25
+ { 0x0FF57, 0x10000, 0x77F78, TRACK_5150_5350_A_BAND }, // A-38 (5190 MHz) 26
+ { 0x0FF57, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-40 (5200 MHz) 27
+ { 0x0FF57, 0x1AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-42 (5210 MHz) 28
+ { 0x0FF57, 0x00000, 0x67F78, TRACK_5150_5350_A_BAND }, // A-44 (5220 MHz) 29
+ { 0x0FF57, 0x05555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-46 (5230 MHz) 30
+ { 0x0FF57, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-48 (5240 MHz) 31
+
+ { 0x0FF58, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-52 (5260 MHz) 32
+ { 0x0FF58, 0x00000, 0x67F78, TRACK_5150_5350_A_BAND }, // A-56 (5280 MHz) 33
+ { 0x0FF58, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-60 (5300 MHz) 34
+ { 0x0FF59, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-64 (5320 MHz) 35
+
+ { 0x0FF5C, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-100 (5500 MHz) 36
+ { 0x0FF5C, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-104 (5520 MHz) 37
+ { 0x0FF5C, 0x0AAAA, 0x77F78, TRACK_5470_5725_A_BAND }, // A-108 (5540 MHz) 38
+ { 0x0FF5D, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-112 (5560 MHz) 39
+ { 0x0FF5D, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-116 (5580 MHz) 40
+ { 0x0FF5D, 0x0AAAA, 0x77F78, TRACK_5470_5725_A_BAND }, // A-120 (5600 MHz) 41
+ { 0x0FF5E, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-124 (5620 MHz) 42
+ { 0x0FF5E, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-128 (5640 MHz) 43
+ { 0x0FF5E, 0x0AAAA, 0x77F78, TRACK_5470_5725_A_BAND }, // A-132 (5660 MHz) 44
+ { 0x0FF5F, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-136 (5680 MHz) 45
+ { 0x0FF5F, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-140 (5700 MHz) 46
+
+ { 0x0FF60, 0x18000, 0x77F78, TRACK_5725_5825_A_BAND }, // A-149 (5745 MHz) 47
+ { 0x0FF60, 0x02AAA, 0x77F78, TRACK_5725_5825_A_BAND }, // A-153 (5765 MHz) 48
+ { 0x0FF60, 0x0D555, 0x77F78, TRACK_5725_5825_A_BAND }, // A-157 (5785 MHz) 49
+ { 0x0FF61, 0x18000, 0x77F78, TRACK_5725_5825_A_BAND }, // A-161 (5805 MHz) 50
+ { 0x0FF61, 0x02AAA, 0x77F78, TRACK_5725_5825_A_BAND }, // A-165 (5825 MHz) 51
+};
+
+#define CHAN4G(idx, _freq) \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = idx, \
+ .max_antenna_gain = 0, \
+ .max_power = 12
+
+static struct ieee80211_channel al7230_bg_channels[] = {
+ { CHAN4G(1, 2412) },
+ { CHAN4G(2, 2417) },
+ { CHAN4G(3, 2422) },
+ { CHAN4G(4, 2427) },
+ { CHAN4G(5, 2432) },
+ { CHAN4G(6, 2437) },
+ { CHAN4G(7, 2442) },
+ { CHAN4G(8, 2447) },
+ { CHAN4G(9, 2452) },
+ { CHAN4G(10, 2457) },
+ { CHAN4G(11, 2462) },
+ { CHAN4G(12, 2467) },
+ { CHAN4G(13, 2472) },
+ { CHAN4G(14, 2484) },
+};
+
+static const struct ieee80211_rate al7230_bg_rates[] = {
+ /* psk/cck rates */
+ {
+ .bitrate = 10,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ {
+ .bitrate = 20,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ {
+ .bitrate = 55,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ {
+ .bitrate = 110,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ /* ofdm rates */
+ {
+ .bitrate = 60,
+ .hw_value = 0xb,
+ },
+ {
+ .bitrate = 90,
+ .hw_value = 0xf,
+ },
+ {
+ .bitrate = 120,
+ .hw_value = 0xa,
+ },
+ {
+ .bitrate = 180,
+ .hw_value = 0xe,
+ },
+ {
+ .bitrate = 240,
+ .hw_value = 0x9,
+ },
+ {
+ .bitrate = 360,
+ .hw_value = 0xd,
+ },
+ {
+ .bitrate = 480,
+ .hw_value = 0x8,
+ },
+ {
+ .bitrate = 540,
+ .hw_value = 0xc,
+ },
+};
+
+#define CHAN5G(idx, frequency) \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = frequency, \
+ .max_antenna_gain = 0, \
+ .max_power = 8, \
+ .hw_value = idx
+
+static struct ieee80211_channel al7230_a_channels[] = {
+ { CHAN5G(17, 4920) },
+ { CHAN5G(18, 4940) },
+ { CHAN5G(19, 4960) },
+ { CHAN5G(20, 4980) },
+
+ { CHAN5G(21, 5040) },
+ { CHAN5G(22, 5060) },
+ { CHAN5G(23, 5080) },
+ { CHAN5G(24, 5170) },
+ { CHAN5G(25, 5180) },
+ { CHAN5G(26, 5190) },
+ { CHAN5G(27, 5200) },
+ { CHAN5G(28, 5210) },
+ { CHAN5G(29, 5220) },
+ { CHAN5G(30, 5230) },
+ { CHAN5G(31, 5240) },
+
+ { CHAN5G(32, 5260) },
+ { CHAN5G(33, 5280) },
+ { CHAN5G(34, 5300) },
+ { CHAN5G(35, 5320) },
+
+ { CHAN5G(36, 5500) },
+ { CHAN5G(37, 5520) },
+ { CHAN5G(38, 5540) },
+ { CHAN5G(39, 5560) },
+ { CHAN5G(40, 5580) },
+ { CHAN5G(41, 5600) },
+ { CHAN5G(42, 5620) },
+ { CHAN5G(43, 5640) },
+ { CHAN5G(44, 5660) },
+ { CHAN5G(45, 5680) },
+ { CHAN5G(46, 5700) },
+
+ { CHAN5G(47, 5745) },
+ { CHAN5G(48, 5765) },
+ { CHAN5G(49, 5785) },
+ { CHAN5G(50, 5805) },
+ { CHAN5G(51, 5825) }
+};
+
+static const struct ieee80211_rate al7230_a_rates[] = {
+ /* ofdm rates */
+ {
+ .bitrate = 60,
+ .hw_value = 0xb,
+ },
+ {
+ .bitrate = 90,
+ .hw_value = 0xf,
+ },
+ {
+ .bitrate = 120,
+ .hw_value = 0xa,
+ },
+ {
+ .bitrate = 180,
+ .hw_value = 0xe,
+ },
+ {
+ .bitrate = 240,
+ .hw_value = 0x9,
+ },
+ {
+ .bitrate = 360,
+ .hw_value = 0xd,
+ },
+ {
+ .bitrate = 480,
+ .hw_value = 0x8,
+ },
+ {
+ .bitrate = 540,
+ .hw_value = 0xc,
+ },
+};
+
+static enum ieee80211_band getBand(int channelIndex)
+{
+ enum ieee80211_band result;
+
+ if (channelIndex >= BAND_A_OFFSET) {
+ result = IEEE80211_BAND_5GHZ;
+ } else {
+ result = IEEE80211_BAND_2GHZ;
+ }
+
+ return result;
+}
+
+static int getFrequency(int channelIndex)
+{
+ int result;
+
+ if (getBand(channelIndex) == IEEE80211_BAND_5GHZ) {
+ result = al7230_a_channels[channelIndex - BAND_A_OFFSET].center_freq;
+ } else {
+ result = al7230_bg_channels[channelIndex - 1].center_freq;
+ }
+
+ return result;
+}
+
+static int write_rf(struct ieee80211_hw *hw, unsigned char reg, unsigned int val)
+{
+ struct piper_priv *priv = hw->priv;
+ int err;
+
+ err = write_reg(BB_SPI_DATA, val << 4 | reg, op_write);
+ udelay(3); /* Mike Schaffner says to allow 2 us or more between all writes */
+ return err;
+}
+
+
+/*
+ * This function is called to set the value of Airoha register
+ * 0xc. This register must be set to different values depending
+ * on the H/W revision of the board due to changes in the board
+ * design.
+ */
+static void set_hw_specific_parameters(struct ieee80211_hw *hw,
+ unsigned int band,
+ unsigned int hw_revision,
+ unsigned int hw_platform)
+{
+ switch (hw_platform) {
+ case WCD_CCW9P_PLATFORM:
+ switch (hw_revision) {
+ case WCD_HW_REV_PROTOTYPE:
+ case WCD_HW_REV_PILOT:
+ case WCD_HW_REV_A:
+ default:
+ if (band == IEEE80211_BAND_2GHZ) {
+ write_rf(hw, 0xc, 0x2b);
+ } else {
+ write_rf(hw, 0xc, 0x00143 );
+ }
+ break;
+ }
+ break;
+ case WCD_CCW9M_PLATFORM:
+ switch (hw_revision) {
+ case WCD_HW_REV_PROTOTYPE:
+ case WCD_HW_REV_PILOT:
+ if (band == IEEE80211_BAND_2GHZ) {
+ write_rf(hw, 0xc, 0xa3);
+ } else {
+ write_rf(hw, 0xc, 0x00143 );
+ }
+ break;
+
+ case WCD_HW_REV_A:
+ default:
+ if (band == IEEE80211_BAND_2GHZ) {
+ write_rf(hw, 0xc, 0x70);
+ } else {
+ write_rf(hw, 0xc, 0x00143 );
+ }
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int al7230_rf_set_chan_private(struct ieee80211_hw *hw, int channelIndex, bool enable_rx)
+{
+ struct piper_priv *priv = hw->priv;
+ static int rf_band;
+#ifdef WANT_DEBUG
+ const char *channelLookup[] = {
+ "invalid 0",
+ "B-1",
+ "B-2",
+ "B-3",
+ "B-4",
+ "B-5",
+ "B-6",
+ "B-7",
+ "B-8",
+ "B-9",
+ "B-10",
+ "B-11",
+ "B-12",
+ "B-13",
+ "B-14",
+ "invalid 15",
+ "invalid 16",
+ "L-184",
+ "L-188",
+ "L-192",
+ "L-196",
+ "A-8",
+ "A-12",
+ "A-16",
+ "A-34",
+ "A-36",
+ "A-38",
+ "A-40",
+ "A-42",
+ "A-44",
+ "A-46",
+ "A-48",
+ "A-52",
+ "A-56",
+ "A-60",
+ "A-64",
+ "A-100",
+ "A-104",
+ "A-108",
+ "A-112",
+ "A-116",
+ "A-120",
+ "A-124",
+ "A-128",
+ "A-132",
+ "A-136",
+ "A-140",
+ "A-149",
+ "A-153",
+ "A-157",
+ "A-161",
+ "A-165"
+ };
+printk(KERN_ERR "Setting channel %s\n", channelLookup[channelIndex]);
+#endif
+ if (channelIndex >= BAND_A_OFFSET)
+ rf_band = IEEE80211_BAND_5GHZ;
+ else
+ rf_band = IEEE80211_BAND_2GHZ;
+ /* Disable the rx processing path */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and);
+
+ write_reg(BB_OUTPUT_CONTROL, 0xfffff33f, op_and);
+ write_reg(BB_OUTPUT_CONTROL, 0x00000880, op_or);
+
+ if (priv->pdata->rf_transceiver == RF_AIROHA_2236) {
+/* TODO, when using this transceiver, resolve this commented code */
+#ifdef BUILD_THIS_CODE_SECTION
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_B_EN, op_or);
+
+ if (macParams.band == WLN_BAND_B) {
+ /* turn off OFDM */
+ write_reg(BB_GENERAL_STAT, ~BB_GENERAL_STAT_A_EN, op_and);
+ } else {
+ /* turn on OFDM */
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_A_EN, op_or);
+ }
+ /* set the 802.11b/g frequency band specific tracking constant */
+ write_reg(BB_TRACK_CONTROL, 0xff00ffff, op_and);
+
+ write_reg(BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+
+ /* perform chip and frequency-band specific RF initialization */
+ InitializeRF(hw, rf_band);
+
+ mac_set_tx_power(priv->tx_power);
+
+ write_rf(hw, 0, freqTableAiroha_2236[channelIndex].integer);
+ write_rf(hw, 1, freqTableAiroha_2236[channelIndex].fraction);
+
+ /* critical delay for correct calibration */
+ udelay(150);
+
+ /*
+ * TXON, PAON and RXON should all be low before Calibration
+ * TXON and PAON will be low as long as no frames are written to the TX
+ * DATA fifo.
+ * RXON will be low as long as the receive path is not enabled (bit 0 of
+ * GEN CTL register is 0).
+ */
+
+ /* calibrate RF transceiver */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x00D87);
+ udelay(50);
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x00787);
+ udelay(50);
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x00587);
+ udelay(50);
+
+ /* configure the baseband processing engine */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_GEN_5GEN, op_and);
+
+ /*Re-enable the rx processing path */
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_RX_EN, op_or);
+#endif
+ } else if (priv->pdata->rf_transceiver == RF_AIROHA_7230) {
+ /* enable the frequency-band specific PA */
+ if (rf_band == IEEE80211_BAND_2GHZ) {
+ //HW_GEN_CONTROL &= ~GEN_PA_ON;
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_A_EN | BB_GENERAL_STAT_B_EN,
+ op_or);
+
+ /* set the 802.11b/g frequency band specific tracking constant */
+ write_reg(BB_TRACK_CONTROL, 0xff00ffff, op_and);
+
+ write_reg(BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+
+ } else {
+ //HW_GEN_CONTROL |= GEN_PA_ON;
+
+ // turn off PSK/CCK
+ write_reg(BB_GENERAL_STAT, ~BB_GENERAL_STAT_B_EN, op_and);
+
+ // turn on OFDM
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_A_EN, op_or);
+
+ /* Set the 802.11a frequency sub-band specific tracking constant */
+ /* All 8 supported 802.11a channels are in this 802.11a frequency sub-band */
+ write_reg(BB_TRACK_CONTROL, 0xff00ffff, op_and);
+
+ write_reg(BB_TRACK_CONTROL, freqTableAiroha_7230[channelIndex].tracking,
+ op_or);
+ }
+
+ /* perform chip and frequency-band specific RF initialization */
+ InitializeRF(hw, rf_band);
+
+ mac_set_tx_power(priv->tx_power);
+
+ /* Set the channel frequency */
+ write_rf(hw, 0, freqTableAiroha_7230[channelIndex].integer);
+ udelay(150); /* Mike Schaffner says this is needed here */
+ write_rf(hw, 1, freqTableAiroha_7230[channelIndex].fraction);
+ udelay(150); /* Mike Schaffner says this is needed here */
+ write_rf(hw, 4, freqTableAiroha_7230[channelIndex].address4);
+ udelay(150); /* Mike Schaffner says this is needed here */
+
+
+ // Select the frequency band: 5Ghz or 2.4Ghz
+ if (rf_band == IEEE80211_BAND_5GHZ) {
+ /* calibrate RF transceiver */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x12BAC);
+ udelay(50);
+
+ /* configure the baseband processing engine */
+ /*
+ * This bit always as to be turned off when we are using
+ * the Airoha chip, even though it's named the 5G EN bit.
+ * It has to do with how they hooked up the Airoha.
+ */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_GEN_5GEN, op_and);
+ } else {
+ /* calibrate RF transceiver */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x1ABA8);
+ udelay(50);
+
+ /* configure the baseband processing engine */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_GEN_5GEN, op_and);
+ /*
+ * No short preambles allowed for ODFM.
+ */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_SH_PRE, op_and);
+ }
+
+ /*Re-enable the rx processing path */
+ if (enable_rx)
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_RX_EN, op_or);
+
+ /* re-enable transmitter */
+ write_reg(BB_OUTPUT_CONTROL, 0xfffff33f, op_and);
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME ": undefined rf transceiver!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * This is a patch for a problem which should be corrected in
+ * hardware on new units. We are rewriting the MAC address
+ * because on units without the H/W patch the address can
+ * be corrupted when we change channels.
+ */
+ piper_set_macaddr(priv);
+digiWifiDumpRegisters(priv, MAIN_REGS);
+
+ return 0;
+}
+
+static int al7230_rf_set_chan(struct ieee80211_hw *hw, int channelIndex)
+{
+ return al7230_rf_set_chan_private(hw, channelIndex, true);
+}
+
+static int al7230_rf_set_chan_no_rx(struct ieee80211_hw *hw, int channelIndex)
+{
+ return al7230_rf_set_chan_private(hw, channelIndex, false);
+}
+
+
+
+static int al7230_set_txpwr(struct ieee80211_hw *hw, uint8_t value)
+{
+ struct piper_priv *priv = hw->priv;
+
+ if (priv->pdata->rf_transceiver == RF_AIROHA_2236) {
+ const unsigned char powerTable_2236[] = {
+ 4, 10, 10, 18, 22, 22, 28, 28,
+ 33, 33, 36, 38, 40, 43, 45, 47
+ };
+ write_rf(hw, 9, 0x05440 | powerTable_2236[value & 0xf]);
+ } else if (priv->pdata->rf_transceiver == RF_AIROHA_7230) {
+ const unsigned char powerTable_7230[] = {
+ 0x14, 0x14, 0x14, 0x18, 0x18, 0x1c, 0x1c, 0x20,
+ 0x20, 0x24, 0x24, 0x29, 0x29, 0x2c, 0x2c, 0x30
+ };
+ int correctedPowerIndex = digiWifiCalibrationPowerIndex(priv);
+
+ if (correctedPowerIndex != -1) {
+ write_rf(hw, 11, 0x08040 | correctedPowerIndex);
+ } else {
+ write_rf(hw, 11, 0x08040 | powerTable_7230[value & 0xf]);
+ }
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": undefined rf transceiver!\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void al7230_set_power_index(struct ieee80211_hw *hw, unsigned int value)
+{
+ write_rf(hw, 11, 0x08040 | value);
+}
+
+static void InitializeRF(struct ieee80211_hw *hw, int band_selection)
+{
+ struct piper_priv *priv = hw->priv;
+
+ if (priv->pdata->rf_transceiver == RF_AIROHA_2236) {
+ digi_dbg("**** transceiver == RF_AIROHA_2236\n");
+ /* Initial settings for 20 MHz reference frequency, 802.11b/g */
+
+ /* CH_integer: Frequency register 0 */
+ write_rf(hw, 0, 0x01f79 );
+
+ /* CH_fraction: Frequency register 1 */
+ write_rf(hw, 1, 0x03333 );
+
+ /*Config 1 = default value */
+ write_rf(hw, 2, 0x00B80 );
+
+ /*Config 2 = default value */
+ write_rf(hw, 3, 0x00E7F );
+
+ /*Config 3 = default value */
+ write_rf(hw, 4, 0x0905A );
+
+ /*Config 4 = default value */
+ write_rf(hw, 5, 0x0F4DC );
+
+ /*Config 5 = default value */
+ write_rf(hw, 6, 0x0805B );
+
+ /*Config 6 = Crystal frequency /2 to pll reference divider */
+ write_rf(hw, 7, 0x0116C );
+
+ /*Config 7 = RSSI = default value */
+ write_rf(hw, 8, 0x05B68 );
+
+ /* TX gain control for LA2236 */
+ write_rf(hw, 9, 0x05460 ); // sit at the middle
+
+ /* RX Gain = digi specific value: AGC adjustment is done over the GC1-GC7
+ IC pins interface. AGC MAX GAIN value is configured in the FPGA BB register
+ instead of the RF register here below */
+ write_rf(hw, 10, 0x001BB );
+
+ /* TX Gain = digi specific vaue: TX GAIN set using the register */
+ write_rf(hw, 11, 0x000f9 );
+
+ /* PA current = default value */
+ write_rf(hw, 12, 0x039D8 );
+
+ /* Config 8 = default value */
+ write_rf(hw, 13, 0x08000 );
+
+ /* Config 9 = default value */
+ write_rf(hw, 14, 0x00000 );
+
+ /* Config 10 = default value */
+ write_rf(hw, 15, 0x00587 );
+
+ //mac_set_tx_power (macParams.tx_power);
+
+ /* Calibration procedure */
+ write_reg(BB_OUTPUT_CONTROL, 0x00000300, op_or);
+ udelay(150);
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x00D87 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x00787 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x00587 );
+ udelay(50);
+ } else if (priv->pdata->rf_transceiver == RF_AIROHA_7230) {
+ switch (band_selection) {
+ case IEEE80211_BAND_2GHZ:
+ /* Initial settings for 20 MHz reference frequency, 802.11b/g */
+ write_reg(BB_OUTPUT_CONTROL, 0xfffffcff, op_and);
+ write_reg(BB_OUTPUT_CONTROL, 0x00000200, op_or);
+ udelay(150);
+
+ /* Frequency register 0 */
+ write_rf(hw, 0, 0x00379 );
+
+ /* Frequency register 1 */
+ write_rf(hw, 1, 0x13333 );
+ udelay(10);
+
+ /*Config 1 = default value */
+ write_rf(hw, 2, 0x841FF );
+
+ /*Config 2 = default value */
+ write_rf(hw, 3, 0x3FDFA );
+
+ /*Config 3 = default value */
+ write_rf(hw, 4, 0x7FD78 );
+
+ /*Config 4 = default value */
+ write_rf(hw, 5, 0x802BF );
+
+ /*Config 5 = default value */
+ write_rf(hw, 6, 0x56AF3 );
+
+ /*Config 6 = Crystal frequency /2 to pll reference divider */
+ write_rf(hw, 7, 0xCE000 );
+
+ /*Config 7 = RSSI = default value */
+ write_rf(hw, 8, 0x6EBC0 );
+
+ /* Filter BW = default value */
+ write_rf(hw, 9, 0x221BB );
+
+ /* RX Gain = digi specific value: AGC adjustment is done over the GC1-GC7
+ IC pins interface. AGC MAX GAIN value is configured in the FPGA BB register
+ instead of the RF register here below */
+ write_rf(hw, 10, 0xE0040 );
+
+ /* TX Gain = digi specific vaue: TX GAIN set using the register */
+ // write_rf(hw, 11, 0x08070);
+ mac_set_tx_power (priv->tx_power); //Digi value
+
+ /* PA current = default value */
+ set_hw_specific_parameters(hw, IEEE80211_BAND_2GHZ, hw_revision, hw_platform);
+
+ /* Config 8 = default value */
+ write_rf(hw, 13, 0xFFFFF );
+
+ /* Config 9 = default value */
+ write_rf(hw, 14, 0x00000 );
+
+ /* Config 10 = default value */
+ write_rf(hw, 15, 0x1ABA8 );
+
+ /* Calibration procedure */
+ write_reg(BB_OUTPUT_CONTROL, 0x00000300, op_or);
+
+ udelay(150);
+
+ /* Calibration procedure */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x1ABA8 );
+ udelay(50);
+
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_MAX_GAIN_MASK, op_and);
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_DEFAULT_MAX_GAIN_BG, op_or);
+ break;
+
+ case IEEE80211_BAND_5GHZ:
+ /* Initial settings for 20 MHz reference frequency, 802.11a */
+ write_reg(BB_OUTPUT_CONTROL, 0xfffffcff, op_and);
+ write_reg(BB_OUTPUT_CONTROL, 0x00000200, op_or);
+ udelay(150);
+
+ /* Frequency register 0 */
+ write_rf(hw, 0, 0x0FF56 );
+
+ /* Frequency register 1 */
+ write_rf(hw, 1, 0x0AAAA );
+
+ udelay(10);
+
+ /*Config 1 = default value */
+ write_rf(hw, 2, 0x451FE );
+
+ /*Config 2 = default value */
+ write_rf(hw, 3, 0x5FDFA );
+
+ /*Config 3 = default value */
+ write_rf(hw, 4, 0x67f78 );
+
+ /*Config 4 = default value */
+ write_rf(hw, 5, 0x853FF );
+
+ /*Config 5 = default value */
+ write_rf(hw, 6, 0x56AF3 );
+
+ /*Config 6 = Crystal frequency /2 to pll reference divider */
+ write_rf(hw, 7, 0xCE000 );
+
+ /*Config 7 = RSSI = default value */
+ write_rf(hw, 8, 0x6EBC0 );
+
+ /* Filter BW = default value */
+ write_rf(hw, 9, 0x221BB );
+
+ /* RX Gain = digi value */
+ write_rf(hw, 10, 0xE0600 );
+
+ /* TX Gain = digi specific vaue: TX GAIN set using the register */
+ // write_rf(hw, 11, 0x08070 );
+ mac_set_tx_power (priv->tx_power); //Digi value
+
+ /* PA current = default value */
+ set_hw_specific_parameters(hw, IEEE80211_BAND_5GHZ, hw_revision, hw_platform);
+
+ /* Config 8 = default value */
+ write_rf(hw, 13, 0xFFFFF );
+
+ /* Config 9 = default value */
+ write_rf(hw, 14, 0x00000 );
+
+ /* Config 10 = default value */
+ write_rf(hw, 15, 0x12BAC );
+
+ /* Calibration procedure */
+ write_reg(BB_OUTPUT_CONTROL, 0x00000300, op_or);
+
+ udelay(150);
+
+ /* Calibration procedure */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x12BAC );
+ udelay(50);
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_MAX_GAIN_MASK, op_and);
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_DEFAULT_MAX_GAIN_A, op_or);
+ break;
+ }
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": undefined rf transceiver!\n");
+ }
+}
+
+static int al7230_rf_stop(struct ieee80211_hw *hw)
+{
+ return 0;
+}
+
+static void getOfdmBrs(int channelIndex, u64 brsBitMask, unsigned int *ofdm, unsigned int *psk)
+{
+ /*
+ * brsBitMask is a bit mask into the al7230_bg_rates array. Bit 0 refers
+ * to the first entry in the array, bit 1 the second, and so on. The first
+ * 4 bits/array entries refer to the PSK bit rates we support, the next 8
+ * bits/array entries refer to the OFDM rates we support. So the PSK BRS
+ * mask is bits 0-3, the OFDM bit mask is bits 4-11.
+ */
+
+ if (getBand(channelIndex) == IEEE80211_BAND_2GHZ)
+ {
+ *psk = brsBitMask & 0xf;
+ *ofdm = (brsBitMask & 0xff0) >> 4;
+ }
+ else
+ {
+ *psk = 0;
+ *ofdm = (brsBitMask & 0xff);
+ }
+}
+
+static struct ieee80211_supported_band al7230_bands[] = {
+ {
+ .band = IEEE80211_BAND_2GHZ,
+ .n_channels = ARRAY_SIZE(al7230_bg_channels),
+ .n_bitrates = ARRAY_SIZE(al7230_bg_rates),
+ .channels = (struct ieee80211_channel *) al7230_bg_channels,
+ .bitrates = (struct ieee80211_rate *) al7230_bg_rates,
+ },
+ {
+ .band = IEEE80211_BAND_5GHZ,
+ .n_channels = ARRAY_SIZE(al7230_a_channels),
+ .n_bitrates = ARRAY_SIZE(al7230_a_rates),
+ .channels = (struct ieee80211_channel *) al7230_a_channels,
+ .bitrates = (struct ieee80211_rate *) al7230_a_rates,
+ },
+};
+
+static const struct ieee80211_rate *getRate(unsigned int rateIndex)
+{
+ return &al7230_bg_rates[rateIndex];
+}
+
+
+/*
+ * This routine can power up or power down the airoha transceiver.
+ * When the transceiver is powered back up, you must delay 1 ms and
+ * then call the set channel routine to make it operational again.
+ */
+static void power_on(struct ieee80211_hw *hw, bool want_power_on)
+{
+ if (want_power_on) {
+ write_rf(hw, 15, 0x1ABA8 ); /* this is actually for 2 Ghz */
+ } else {
+ write_rf(hw, 15, 0x1ABAE );
+ }
+}
+
+static void al7230_set_hw_info(struct ieee80211_hw *hw, int channel,
+ u16 hw_platform_code)
+{
+ hw_revision = hw_platform_code & WCD_HW_REV_MASK;
+ hw_platform = (hw_platform_code & WCD_PLATFORM_MASK);
+
+ set_hw_specific_parameters(hw, getBand(channel), hw_revision, hw_platform);
+}
+
+struct digi_rf_ops al7230_rf_ops = {
+ .name = "Airoha 7230",
+ .init = InitializeRF,
+ .stop = al7230_rf_stop,
+ .set_chan = al7230_rf_set_chan,
+ .set_chan_no_rx = al7230_rf_set_chan_no_rx,
+ .set_pwr = al7230_set_txpwr,
+ .set_pwr_index = al7230_set_power_index,
+ .set_hw_info = al7230_set_hw_info,
+ .channelChangeTime = CHANNEL_CHANGE_TIME,
+ .maxSignal = MAX_SIGNAL_IN_DBM,
+ .getOfdmBrs = getOfdmBrs,
+ .getBand = getBand,
+ .getFrequency = getFrequency,
+ .getRate = getRate,
+ .bands = al7230_bands,
+ .power_on = power_on,
+ .n_bands = ARRAY_SIZE(al7230_bands),
+};
+EXPORT_SYMBOL_GPL(al7230_rf_ops);
diff --git a/drivers/net/wireless/digiPiper/airoha.h b/drivers/net/wireless/digiPiper/airoha.h
new file mode 100644
index 000000000000..aa3b1d10fbe3
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airoha.h
@@ -0,0 +1,30 @@
+#ifndef DIGI_RF_AH7230_H_
+#define DIGI_RF_AH7230_H_
+
+/* 0 is reserved for unknown transceiver */
+#define RF_AIROHA_7230 (1)
+#define RF_AIROHA_2236 (2)
+
+#define SPI_INIT_AIROHA (0x00000018) /* AIROHA-specific SPI length */
+#define SPI_INIT_AIROHA2236 (0x00000014) /* AIROHA 2236-specific SPI length */
+#define GEN_INIT_AIROHA_24GHZ (0x31720005) /* Initial state; 2.4GHZ_PA_ON= active low; bit 25 */
+#define GEN_INIT_AIROHA_50GHZ (0x33760008) /* Initial state; 5.0GHZ_PA_ON= active high; bit 25 */
+
+#define AIROHA_LOWEST_PSK_RATE_INDEX (0)
+#define AIROHA_LOWEST_OFDM_RATE_INDEX (4)
+#define AIROHA_55_MBPS_RATE_INDEX (2)
+
+/*
+ * Subtract this number from the channel index to index into
+ * the 802.11a channel array.
+ */
+#define BAND_A_OFFSET (17)
+
+struct digi_rf_freq {
+ uint16_t integer;
+ uint16_t fract;
+};
+
+extern struct digi_rf_ops al7230_rf_ops;
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/airohaCalibration.c b/drivers/net/wireless/digiPiper/airohaCalibration.c
new file mode 100644
index 000000000000..9da912f320ba
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airohaCalibration.c
@@ -0,0 +1,974 @@
+/*
+ * This file contains the code which performs automatic recalibration of the
+ * Airoha transceiver.
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free softbware; 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/kthread.h>
+#include <linux/timer.h>
+#include <linux/crc32.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airoha.h"
+#include "airohaCalibration.h"
+#include "adc121c027.h"
+
+#define POWER_INDEX_STEP (1) /* TODO: come up with a rational value for this */
+
+#define SAMPLE_TIMEOUT (HZ * 10) /* TODO: What is a good sample timeout? Do we need one? */
+#define RECALIBRATION_PERIOD (HZ * 15) /* amount of time to wait between recalibrations */
+
+/*
+ * This defines the amount of time to wait for the power levels
+ * to settle down after making a large correction (user has
+ * changed power level).
+ */
+#define DEBOUNCE_DELAY (HZ * 2)
+
+#define MAX_TOLERATED_ERROR_HIGH (300) /* maximum over target we will allow */
+#define MAX_TOLERATED_ERROR_LOW (500) /* maximum under the target we will allow */
+#define CONVERT_TO_MDBM(x) (1000 * (x)) /* power levels are dBm externally, but dBm/1000 internally */
+
+#define NVRAM_WCAL_SIGNATURE "WCALDATA"
+
+#define MINIMUM_POWER_INDEX (10)
+
+/*
+ * Set piperp->calibrationTxRate to this value to make the
+ * transmit routine use the data rate specified by the
+ * mac80211 library.
+ */
+#define USE_MAC80211_DATA_RATE (NULL)
+
+/*
+ * Events we will wait for, also return values for waitForEvent().
+ */
+#define TIMED_OUT_EVENT (1 << 0)
+#define TRANSMIT_DONE_EVENT (1 << 1)
+#define SHUTDOWN_AUTOCALIBRATION_EVENT (1 << 2)
+#define RESTART_AUTOCALIBRATION_EVENT (1 << 3)
+
+/*
+ * Set this constant to (1) if you want to force calibration status to
+ * be printed.
+ */
+#define WANT_CALIBRATION_STATUS (0)
+
+
+static struct airohaCalibrationData calibration;
+
+static DECLARE_WAIT_QUEUE_HEAD(waitQueue);
+
+
+
+/*
+ * This routine is called to shut down the transmit ADC sampler.
+ */
+static void stopSampler(struct piper_priv *digi)
+{
+ digi->tx_calib_cb = NULL;
+}
+
+/*
+ * This routine is called to update the state of the calibration state machine
+ * and wake up its thread.
+ */
+static void kickCalibrationThread(struct piper_priv *digi, unsigned int event)
+{
+ unsigned long spinlockFlags;
+
+ spin_lock_irqsave(&calibration.lock, spinlockFlags);
+ calibration.events |= event;
+ spin_unlock_irqrestore(&calibration.lock, spinlockFlags);
+ wake_up_interruptible(&waitQueue);
+}
+
+
+/*
+ * This routine is called each time we complete a transmit while we are in
+ * the sampling state. We record the peak ADC reading. We kick the state
+ * machine if we now have all the samples we need.
+ */
+static void processSample(struct piper_priv *digi)
+{
+#define MINIMUM_ADC_VALUE (10) /* if ADC is below this value, it's probably bad */
+ if (calibration.sampleCount < MAX_SAMPLES) {
+ /*
+ * Read the ADC value. It is a 12-bit value. We shift it 4 bits to
+ * create an 8-bit value.
+ */
+ calibration.sample[calibration.sampleCount].sample =
+ (calibration.cops->adc_read_peak(&calibration) >> 4);
+ if (calibration.sample[calibration.sampleCount].sample >
+ MINIMUM_ADC_VALUE) {
+ calibration.sampleCount++;
+ }
+ }
+}
+
+static void transmitHasCompleted(struct piper_priv *digi)
+{
+ stopSampler(digi);
+ kickCalibrationThread(digi, TRANSMIT_DONE_EVENT);
+}
+
+
+
+/*
+ * Determine the appropriate transmit rate to use during calibration.
+ */
+static struct ieee80211_rate *determineCalibrationTxRate(struct piper_priv
+ *digi)
+{
+ unsigned int rates = digi->ac->rd_reg(digi, MAC_SSID_LEN);
+ struct ieee80211_rate *calibrationTxRate;
+
+ rates &= (MAC_PSK_BRS_MASK | MAC_OFDM_BRS_MASK);
+
+ if ((digi->rf->getBand(digi->channel) == IEEE80211_BAND_2GHZ)
+ && (rates & MAC_PSK_BRS_MASK)) {
+ calibrationTxRate =
+ (struct ieee80211_rate *) digi->rf->
+ getRate(AIROHA_LOWEST_PSK_RATE_INDEX);
+ } else {
+ calibrationTxRate =
+ (struct ieee80211_rate *) digi->rf->
+ getRate(AIROHA_LOWEST_OFDM_RATE_INDEX);
+ }
+
+ return calibrationTxRate;
+}
+
+
+
+
+/*
+ * Start collecting sample ADC peak measurements for calibration. Start
+ * the process by installing the callbacks which the transmit code will
+ * use to notify us when transmit frames go out.
+ */
+static void startSampleCollection(struct piper_priv *digi)
+{
+ calibration.cops->adc_clear_peak(&calibration);
+ digi->tx_calib_cb = transmitHasCompleted;
+}
+
+
+static unsigned int waitForEvent(unsigned int timeout, unsigned int eventToWaitFor)
+{
+#define ALL_EVENTS_TO_WAIT_FOR(x) (eventToWaitFor \
+ | SHUTDOWN_AUTOCALIBRATION_EVENT \
+ | RESTART_AUTOCALIBRATION_EVENT)
+
+ unsigned long spinlockFlags;
+ int ccode;
+ unsigned int event;
+ int result = TIMED_OUT_EVENT;
+
+ if (timeout != 0) {
+ ccode = wait_event_interruptible_timeout(waitQueue,
+ ((calibration.
+ events &
+ ALL_EVENTS_TO_WAIT_FOR
+ (eventToWaitFor))
+ != 0), timeout);
+ __set_current_state(TASK_RUNNING);
+
+ } else {
+ ccode = 0;
+ }
+ spin_lock_irqsave(&calibration.lock, spinlockFlags);
+ event = calibration.events;
+ calibration.events = 0;
+ spin_unlock_irqrestore(&calibration.lock, spinlockFlags);
+
+ if ((ccode < 0) || (event & SHUTDOWN_AUTOCALIBRATION_EVENT)) {
+ result = SHUTDOWN_AUTOCALIBRATION_EVENT;
+ } else if (event & RESTART_AUTOCALIBRATION_EVENT) {
+ result = RESTART_AUTOCALIBRATION_EVENT;
+ } else if (event & eventToWaitFor) {
+ result = (event & eventToWaitFor);
+ } else {
+ result = TIMED_OUT_EVENT;
+ }
+
+ return result;
+}
+
+
+#ifdef WANT_CAL_DEBUG
+static void printPoint(wcd_point_t * p)
+{
+ printk("(%d, %d, %d)", p->out_power, p->adc_val, p->power_index);
+}
+#endif
+
+
+/*
+ * This routine finds the closest pair of points in a calibration curve.
+ *
+ * curve calibration curve
+ * value look for the pr of points closest to this value
+ * p1 storage for one point
+ * p2 storage for another point
+ * field tells us which field in the point struct to compare
+ */
+static void findClosestPoints(wcd_curve_t * curve, int value,
+ wcd_point_t ** p1, wcd_point_t ** p2, int field)
+{
+ if (value <= curve->points[0].out_power) {
+ *p1 = &curve->points[0];
+ *p2 = &curve->points[1];
+ } else if (value >=
+ curve->points[calibration.nvram->header.numcalpoints - 1].out_power) {
+ *p1 = &curve->points[calibration.nvram->header.numcalpoints - 2];
+ *p2 = &curve->points[calibration.nvram->header.numcalpoints - 1];
+ } else {
+ unsigned int idx;
+
+ for (idx = 1; idx < calibration.nvram->header.numcalpoints; idx++) {
+ if ((value < curve->points[idx].out_power)
+ || (idx == (calibration.nvram->header.numcalpoints - 1))) {
+ *p1 = &curve->points[idx - 1];
+ *p2 = &curve->points[idx];
+ break;
+ } else if (value == curve->points[idx].out_power) {
+ /*
+ * Note that the if statement befpre the for loop already tested for the
+ * value being equal to the first or last point in the curve, so we don't
+ * have to worry about that condition in the code below.
+ */
+ if ((value -
+ curve->points[idx - 1].out_power) >=
+ (curve->points[idx + 1].out_power - value)) {
+ /*
+ * If the two points are equal distant, then favor the larger pair
+ * because I think the values on the low end are screwy.
+ */
+ *p1 = &curve->points[idx];
+ *p2 = &curve->points[idx + 1];
+ } else {
+ *p1 = &curve->points[idx - 1];
+ *p2 = &curve->points[idx];
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Compute the slope of a curve between 2 points. The slope is the rise over the run,
+ * or (Y2 - Y1)/(X2 - X1). This function handles more than one type of slope.
+ */
+static int computeSlopeTimes1000(wcd_point_t * p1, wcd_point_t * p2, int slopeType)
+{
+ int slope = 0;
+ int divisor;
+
+ switch (slopeType) {
+ default:
+ digi_dbg("Unexpected slope type %d.\n", slopeType);
+ break;
+ case POWER_INDEX_OVER_OUT_POWER:
+ divisor = (p2->out_power - p1->out_power);
+ if (divisor != 0) {
+ slope =
+ (((p2->power_index - p1->power_index) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ case ADC_OVER_OUT_POWER:
+ divisor = (p2->out_power - p1->out_power);
+ if (divisor != 0) {
+ slope =
+ (((p2->adc_val - p1->adc_val) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ case OUT_POWER_OVER_ADC:
+ divisor = (p2->adc_val - p1->adc_val);
+ if (divisor != 0) {
+ slope =
+ (((p2->out_power - p1->out_power) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ case POWER_INDEX_OVER_ADC:
+ divisor = (p2->adc_val - p1->adc_val);
+ if (divisor != 0) {
+ slope =
+ (((p2->power_index - p1->power_index) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ }
+
+ return slope;
+}
+
+
+/*
+ * If (x,y) is a point on a curve, then compute y given x, the slope of the curve,
+ * and a known point on the curve.
+ *
+ * If (Xd, Yd) is the desired point, p1 is the known point, and m the slope, then
+ *
+ * Yd - p1->y = m(Xd - p1->x)
+ * Yd = m(Xd - p1->x) + p1->y
+ * Yd = m(Xd) - m(p1->x) + p1->y
+ */
+static int computeY(wcd_point_t * p1, int slopeTimes1000, int x, int slopeType)
+{
+ int y = 0;
+
+ switch (slopeType) {
+ default:
+ digi_dbg("Unexpected slope type %d.\n", slopeType);
+ break;
+ case POWER_INDEX_OVER_OUT_POWER:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->out_power) + 500) / 1000) + p1->power_index;
+ break;
+ case ADC_OVER_OUT_POWER:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->out_power) + 500) / 1000) + p1->adc_val;
+ break;
+ case OUT_POWER_OVER_ADC:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->adc_val) + 500) / 1000) + p1->out_power;
+ break;
+ case POWER_INDEX_OVER_ADC:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->adc_val) + 500) / 1000) + p1->power_index;
+ break;
+ }
+
+ return y;
+}
+
+
+/*
+ * Return a pointer to the curve we should use for the currently selected
+ * channel.
+ */
+static wcd_curve_t *determineCurve(struct piper_priv *digi)
+{
+ unsigned int rates = digi->ac->rd_reg(digi, MAC_SSID_LEN);
+ wcd_curve_t *curve = NULL;
+
+ if (digi->rf->getBand(digi->channel) == IEEE80211_BAND_2GHZ) {
+ if (rates & MAC_PSK_BRS_MASK) {
+ /*
+ * This is the normal case for 802.11b and 802.11bg. We select
+ * the PSK curve.
+ */
+ digi_dbg("Using bg curve [%d][%d]\n",
+ digi->channel, WCD_B_CURVE_INDEX);
+ curve = &calibration.nvram->cal_curves_bg[digi->channel]
+ [WCD_B_CURVE_INDEX];
+ } else { /* if associated with AP that only supports G rates */
+
+ /*
+ * This is a very unusual, but theoretically possible case. We
+ * are associated with an AP that only supports OFDM modulation
+ * (G only, no B). We determine this by looking at the BRS
+ * setting. If no PSK rates are set for BRS, then we assume that
+ * this must be a G only AP. Obviously, we must select the OFDM curve.
+ */
+ digi_dbg("Using bg curve [%d][%d]\n",
+ digi->channel, WCD_G_CURVE_INDEX);
+ curve = &calibration.nvram->cal_curves_bg[digi->channel]
+ [WCD_G_CURVE_INDEX];
+ }
+ } else {
+ /*
+ * An 802.11a channel is selected.
+ */
+ curve = &calibration.nvram->cal_curves_a[digi->channel - BAND_A_OFFSET];
+ digi_dbg("Using A curve [%d]\n", digi->channel - BAND_A_OFFSET);
+ }
+
+ return curve;
+}
+
+/*
+ * Determine the maximum mdBm allowed for the current channel. Return the
+ * lesser of the max and the mdBm value passed to us.
+ */
+static int getFilteredPower(struct piper_priv *digi, int mdBm)
+{
+ int max = 0;
+
+ if (digi->channel < BAND_A_OFFSET) {
+ max = 16000; /* all BG channels can go to 16 dBm */
+ } else if (digi->channel < (BAND_A_OFFSET + 4)) {
+ max = 3000; /* first 4 A channels max out at 3 dBm */
+ } else {
+ max = 8000; /* all other A channels can handle 8 dBm */
+ }
+
+ if (mdBm > max) {
+ mdBm = max;
+ }
+
+ return mdBm;
+}
+
+
+/*
+ * This routine performs open loop calibration for Airoha. It takes a value in mdbm
+ * and uses the factory calibration routines to determine the appropriate register
+ * value to write to airoha.
+ */
+static void setInitialPowerLevel(struct piper_priv *digi, int mdBm)
+{
+ wcd_point_t *p1, *p2;
+
+ mdBm = getFilteredPower(digi, mdBm);
+ digi_dbg("Setting initial powerlevel %d milli dBm.\n", mdBm);
+ calibration.curve = determineCurve(digi);
+ findClosestPoints(calibration.curve, mdBm, &p1, &p2, OUT_POWER);
+ calibration.slopeTimes1000 =
+ computeSlopeTimes1000(p1, p2, POWER_INDEX_OVER_OUT_POWER);
+ if (abs(p1->out_power - mdBm) < abs(p2->out_power - mdBm)) {
+ calibration.p1 = p1;
+ } else {
+ calibration.p1 = p2;
+ }
+ calibration.powerIndex =
+ computeY(calibration.p1, calibration.slopeTimes1000, mdBm,
+ POWER_INDEX_OVER_OUT_POWER);
+ calibration.correctedPowerIndex = calibration.powerIndex;
+
+ digi_dbg("Computed power index = %d.\n", calibration.powerIndex);
+ digi->rf->set_pwr_index(digi->hw, calibration.powerIndex);
+
+ /*
+ * Let's compute and save the expected ADC value while we have all the necessary
+ * information handy.
+ */
+#ifdef WANT_CAL_DEBUG
+ digi_dbg("Using points ");
+ printPoint(p1);
+ printPoint(p2);
+ printk("\n");
+ digi_dbg("Max ADC = %d.\n", calibration.curve->max_adc_value);
+#endif
+ calibration.adcSlopeTimes1000 = computeSlopeTimes1000(p1, p2, ADC_OVER_OUT_POWER);
+ calibration.expectedAdc =
+ computeY(calibration.p1, calibration.adcSlopeTimes1000, mdBm,
+ ADC_OVER_OUT_POWER);
+ if (calibration.expectedAdc > calibration.curve->max_adc_value) {
+ calibration.expectedAdc = calibration.curve->max_adc_value;
+ }
+ digi_dbg("adcSlopeTimes1000 = %d, expectedAdc = %d\n",
+ calibration.adcSlopeTimes1000, calibration.expectedAdc);
+ calibration.outPowerSlopeTimes1000 =
+ computeSlopeTimes1000(p1, p2, OUT_POWER_OVER_ADC);
+ calibration.powerIndexSlopeTimes1000 =
+ computeSlopeTimes1000(p1, p2, POWER_INDEX_OVER_ADC);
+}
+
+
+
+/*
+ * This routine performs closed loop recalibration. It is called periodically
+ * to adjust the transmit power level. It will be called after the ADC levels
+ * for several transmit frames have been sampled. It does the following:
+ *
+ * 1. Average the samples together.
+ * 2. If the measured ADC level is too low, then increase the power
+ * level one step.
+ * 3. If the measured ADC level is too high, then decrease the power
+ * level one step.
+ */
+static void recalibrate(struct piper_priv *digi)
+{
+ unsigned int idx;
+ int actualAdc = 0;
+ int errorInAdc, errorInMdbm;
+ wcd_point_t p = {
+ .out_power = 0,
+ .adc_val = 0
+ };
+ int needCorrection = 0;
+
+#ifdef WANT_CAL_DEBUG_1
+ digi_dbg("Samples: ");
+#endif
+ for (idx = 0; idx < calibration.sampleCount; idx++) {
+#ifdef WANT_CAL_DEBUG_1
+ printk("%d, ", calibration.sample[idx].sample);
+#endif
+ actualAdc += calibration.sample[idx].sample;
+ }
+#ifdef WANT_CAL_DEBUG_1
+ printk("\n");
+#endif
+ actualAdc = actualAdc / calibration.sampleCount;
+
+#ifdef WANT_TO_FORCE_26
+
+ digi->rf->set_pwr_index(digi->hw, 26);
+#else
+
+ errorInAdc = actualAdc - calibration.expectedAdc;
+ errorInMdbm =
+ computeY(&p, calibration.outPowerSlopeTimes1000, errorInAdc,
+ OUT_POWER_OVER_ADC);
+ needCorrection = (errorInMdbm > MAX_TOLERATED_ERROR_HIGH);
+ if (errorInMdbm < 0) {
+ needCorrection = ((MAX_TOLERATED_ERROR_LOW + errorInMdbm) < 0);
+ }
+ if (needCorrection) {
+ int correction = computeY(calibration.p1,
+ calibration.powerIndexSlopeTimes1000,
+ actualAdc, POWER_INDEX_OVER_ADC);
+#if defined(WANT_CAL_DEBUG)
+ int oldIndex = calibration.correctedPowerIndex;
+#endif
+ correction = (3 * (calibration.powerIndex - correction)) / 4;
+ if (correction == 0) {
+ if (errorInAdc > 0) {
+ /*
+ * If power level is too high but the minimum correction would
+ * overshoot the target, do it anyway because we would rather
+ * be below the target rather than over the target.
+ */
+ correction = -1;
+ } else {
+ /*
+ * But if the power level is too low, but the minimum correction
+ * would overshoot, then leave it be. Better too low than too high.
+ */
+ }
+ }
+ calibration.correctedPowerIndex += correction;
+ if (calibration.correctedPowerIndex < MINIMUM_POWER_INDEX) {
+ calibration.correctedPowerIndex = MINIMUM_POWER_INDEX;
+ }
+ digi->rf->set_pwr_index(digi->hw, calibration.correctedPowerIndex);
+#ifdef WANT_CAL_DEBUG
+ digi_dbg
+ ("actualAdc = %d, expectedAdc = %d, error mdbm = %d\n",
+ actualAdc, calibration.expectedAdc, errorInMdbm);
+ digi_dbg
+ ("Power index corrected by %d: was %d, set to %d.\n",
+ correction, oldIndex, calibration.correctedPowerIndex);
+#endif
+#if WANT_CALIBRATION_STATUS
+ if (correction != 0)
+ printk(KERN_ERR
+ "error mdBm = %d, correcting airoha index by %d\n",
+ errorInMdbm, correction);
+#endif
+ }
+#endif
+}
+
+
+/*
+ * This routine is called by the 80211mac library to set a new power level. We
+ * update the value in context memory and then kick the autocalibration thread.
+ */
+static int setNewPowerLevel(struct ieee80211_hw *hw, uint8_t newPowerLevel)
+{
+ struct piper_priv *digi = hw->priv;
+
+ (void) newPowerLevel; /* has already been written into piper->tx_power */
+ /*
+ * Kick the calibration thread.
+ */
+ stopSampler(digi);
+ kickCalibrationThread(digi, RESTART_AUTOCALIBRATION_EVENT);
+
+ return 0;
+}
+
+
+
+
+/*
+ * Compute the maximum ADC value for a curve given the maximum
+ * allowed power in dBm.
+ */
+static u8 getMaxAdcValue(wcd_curve_t * curve, int dBm)
+{
+ wcd_point_t *p1, *p2;
+ int slopeTimes1000;
+ u8 result = 0;
+ int mdBm = 1000 * dBm;
+
+ findClosestPoints(curve, mdBm, &p1, &p2, OUT_POWER);
+ slopeTimes1000 = computeSlopeTimes1000(p1, p2, ADC_OVER_OUT_POWER);
+
+ if (abs(p1->out_power - mdBm) < abs(p2->out_power - mdBm)) {
+ result = computeY(p1, slopeTimes1000, mdBm, ADC_OVER_OUT_POWER);
+ } else {
+ result = computeY(p2, slopeTimes1000, mdBm, ADC_OVER_OUT_POWER);
+ }
+
+ return result;
+}
+
+
+/*
+ * The version 1.0 calibration data provided maximum Airoha index
+ * values, but later versions provided maximum ADC values. This
+ * routine replaces the max Airoha indexes with max ADC values.
+ */
+static void determineMaxAdcValues(wcd_data_t * cdata)
+{
+ int i;
+
+ for (i = 0; i < WCD_CHANNELS_BG; i++) {
+ /*
+ * All BG channels are limited to 16 dBm.
+ */
+ cdata->cal_curves_bg[i][0].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_bg[i][0], 16);
+ cdata->cal_curves_bg[i][1].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_bg[i][1], 16);
+ }
+ for (i = 0; i < 4; i++) {
+ /*
+ * First 4 802.11a channels are limited to 3 dBm.
+ */
+ cdata->cal_curves_a[i].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_a[i], 3);
+ }
+ for (i = 4; i < WCD_CHANNELS_A; i++) {
+ /*
+ * All other 802.11a channels are limited to 8 dBm.
+ */
+ cdata->cal_curves_a[i].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_a[i], 8);
+ }
+}
+
+
+
+/*
+ * This routine writes a default set of calibration values into the
+ * calibration data structure. Maximum power output is severely limited.
+ */
+static void setDefaultCalibrationValues(struct piper_priv *piperp)
+{
+#define MIN_MDBM (-2905)
+#define MAX_BG_MDBM (6000)
+#define DEFAULT_NUM_POINTS (DEFAULT_NUM_POINTS)
+#define BAND_A_1_START (0)
+#define BAND_A_2_START (4)
+#define BAND_A_3_START (7)
+#define BAND_A_4_START (15)
+#define BAND_A_5_START (19)
+#define BAND_A_6_START (30)
+
+ int i;
+
+ calibration.nvram->header.numcalpoints = 2;
+
+ for (i = 0; i < WCD_CHANNELS_BG; i++) {
+ calibration.nvram->cal_curves_bg[i][0].max_adc_value = 52;
+ calibration.nvram->cal_curves_bg[i][0].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_bg[i][0].points[0].adc_val = 19;
+ calibration.nvram->cal_curves_bg[i][0].points[0].power_index = 0;
+ calibration.nvram->cal_curves_bg[i][0].points[1].out_power = 6000;
+ calibration.nvram->cal_curves_bg[i][0].points[1].adc_val = 52;
+ calibration.nvram->cal_curves_bg[i][0].points[1].power_index = 16;
+
+ calibration.nvram->cal_curves_bg[i][0].max_adc_value = 48;
+ calibration.nvram->cal_curves_bg[i][1].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_bg[i][1].points[0].adc_val = 12;
+ calibration.nvram->cal_curves_bg[i][1].points[0].power_index = 0;
+ calibration.nvram->cal_curves_bg[i][1].points[1].out_power = 6000;
+ calibration.nvram->cal_curves_bg[i][1].points[1].adc_val = 48;
+ calibration.nvram->cal_curves_bg[i][1].points[1].power_index = 24;
+ }
+
+ for (i = BAND_A_1_START; i < BAND_A_2_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 22;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 11;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 0;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 22;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 19;
+ }
+ for (i = BAND_A_2_START; i < BAND_A_3_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 29;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 13;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 29;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 20;
+ }
+ for (i = BAND_A_3_START; i < BAND_A_4_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 42;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 15;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 4000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 42;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 22;
+ }
+ for (i = BAND_A_4_START; i < BAND_A_5_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 54;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 21;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 54;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 18;
+ }
+ for (i = BAND_A_5_START; i < BAND_A_6_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 39;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 13;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 39;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 26;
+ }
+ for (i = BAND_A_6_START; i < WCD_CHANNELS_A; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 31;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 11;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 31;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 30;
+ }
+}
+
+
+/*
+ * The calibration data is passed to the kernel from U-Boot. The kernel
+ * start up routines will have copied the data into digi->pdata->wcd.
+ * We do a few sanity checks on the data and set up our own pointers to
+ * it.
+ */
+static int getCalibrationData(struct piper_priv *digi)
+{
+ int result = 0;
+
+ calibration.nvram = &digi->pdata->wcd;
+
+ if (strncmp(calibration.nvram->header.magic_string,
+ NVRAM_WCAL_SIGNATURE, strlen(NVRAM_WCAL_SIGNATURE)) == 0) {
+ unsigned int crc = ~crc32_le(~0,
+ (unsigned char const *) calibration.nvram->
+ cal_curves_bg,
+ calibration.nvram->header.wcd_len);
+
+ if (crc == calibration.nvram->header.wcd_crc) {
+ digi_dbg("CRC and signature for calibration data is okay\n");
+ result = 0;
+ if ((calibration.nvram->header.ver_major == '1')
+ && (calibration.nvram->header.ver_minor == '0')) {
+ digi_dbg("Converting version 1.0 calibration data\n");
+ determineMaxAdcValues(calibration.nvram);
+ /*
+ * Now that we have updated the format of the data, we need
+ * to recompute the check sum and set the new version.
+ */
+ calibration.nvram->header.ver_minor = '1';
+ calibration.nvram->header.wcd_crc =
+ ~crc32_le(~0, (unsigned char const *)
+ calibration.nvram->
+ cal_curves_bg,
+ calibration.nvram->header.wcd_len);
+ }
+ digi->rf->set_hw_info(digi->hw, digi->channel,
+ calibration.nvram->header.hw_platform);
+ } else {
+ digi_dbg("Calibration data has invalid CRC.\n");
+ setDefaultCalibrationValues(digi);
+ }
+ } else {
+ digi_dbg("Calibration data has invalid signature.\n");
+ setDefaultCalibrationValues(digi);
+ }
+
+ return result;
+}
+
+
+
+/*
+ * This routine:
+ *
+ * 1. Loads the ADC driver.
+ * 2. Loads the calibration data.
+ * 3. Implements the automatic calibration state machine.
+ *
+ */
+static int digiWifiCalibrationThreadEntry(void *data)
+{
+ struct piper_priv *digi = data;
+ int state;
+
+ __set_current_state(TASK_RUNNING);
+
+ while (1) {
+ /*
+ * We, the wireless driver, may be loaded before the I2C core has
+ * loaded. Therefore we may not be able to load our ADC driver,
+ * which is an I2C client driver, when we load. This loop tries
+ * and retries to load the ADC driver until it succeeds.
+ */
+
+ /* TODO, FIXME make following code dependent on platform information
+ * allowign to initialize different adc */
+ if (adc121C027_init(&calibration, digi->pdata->i2c_adapter_num) == 0) {
+ digi_dbg("ADC driver loaded...\n");
+ break;
+ }
+ digi_dbg("Will try to load ADC driver again...\n");
+ ssleep(10);
+ }
+
+ if (getCalibrationData(digi) == 0) {
+ digi->rf->set_pwr = setNewPowerLevel;
+
+ state = RESTART_STATE;
+
+ digi_dbg("Starting autocalibration state machine.\n");
+ do {
+ int event;
+ int timeout = 0;
+ int expectedEvent = TIMED_OUT_EVENT;
+ int nextState = RESTART_STATE;
+
+ switch (state) {
+ case RESTART_STATE:
+ setInitialPowerLevel(digi,
+ CONVERT_TO_MDBM(digi->tx_power));
+ calibration.initialized = true;
+ calibration.sampleCount = 0;
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = DEBOUNCE_DELAY;
+ expectedEvent = TIMED_OUT_EVENT;
+ break;
+ case COLLECT_SAMPLES_STATE:
+ digi->calibrationTxRate =
+ determineCalibrationTxRate(digi);
+ startSampleCollection(digi);
+ nextState = GOT_SAMPLE_STATE;
+ timeout = SAMPLE_TIMEOUT;
+ expectedEvent = TRANSMIT_DONE_EVENT;
+ break;
+ case GOT_SAMPLE_STATE:
+ processSample(digi);
+ if (calibration.sampleCount < MAX_SAMPLES) {
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = 0;
+ break;
+ }
+ /* fall through is intended operation */
+ case RECALIBRATE_STATE:
+ recalibrate(digi);
+ calibration.sampleCount = 0;
+ digi->calibrationTxRate = USE_MAC80211_DATA_RATE;
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = RECALIBRATION_PERIOD;
+ expectedEvent = TIMED_OUT_EVENT;
+ break;
+ default:
+ digi_dbg("Unknown state %d\n", state);
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = RECALIBRATION_PERIOD;
+ expectedEvent = TIMED_OUT_EVENT;
+ break;
+ }
+
+ state = nextState;
+ event = waitForEvent(timeout, expectedEvent);
+
+ switch (event) {
+ case SHUTDOWN_AUTOCALIBRATION_EVENT:
+ digi_dbg("Received SHUTDOWN_AUTOCALIBRATION_EVENT\n");
+ break;
+ case RESTART_AUTOCALIBRATION_EVENT:
+ digi_dbg("Received RESTART_AUTOCALIBRATION_EVENT\n");
+ state = RESTART_STATE;
+ break;
+ case TRANSMIT_DONE_EVENT:
+ break;
+ case TIMED_OUT_EVENT:
+ if (state == GOT_SAMPLE_STATE) {
+ state = COLLECT_SAMPLES_STATE;
+ }
+ }
+ } while (!kthread_should_stop());
+ } else {
+ printk(KERN_ERR
+ "\nWarning: Wireless interface calibration data is corrupted.\n");
+ printk(KERN_ERR
+ " The wireless interface will operate at a low power level.\n");
+ while (!kthread_should_stop()) {
+ ssleep(1);
+ }
+ }
+ calibration.cops->adc_shutdown(&calibration);
+
+ return 0;
+}
+
+
+
+/*
+ * This routine is called at initialization to set up the Airoha calibration routines.
+ */
+void digiWifiInitCalibration(struct piper_priv *digi)
+{
+
+ calibration.events = 0;
+ calibration.sampleCount = 0;
+ calibration.initialized = false;
+
+ spin_lock_init(&calibration.lock);
+
+ calibration.threadCB =
+ kthread_run(digiWifiCalibrationThreadEntry, digi,
+ PIPER_DRIVER_NAME " - calibration");
+}
+
+int digiWifiCalibrationPowerIndex(struct piper_priv *piperp)
+{
+ if (calibration.initialized)
+ return calibration.correctedPowerIndex;
+ else
+ return -1;
+}
+
+
+void digiWifiDeInitCalibration(struct piper_priv *digi)
+{
+ calibration.events = SHUTDOWN_AUTOCALIBRATION_EVENT;
+ wake_up_interruptible(&waitQueue);
+ kthread_stop(calibration.threadCB);
+ calibration.initialized = false;
+}
+
+EXPORT_SYMBOL_GPL(digiWifiDeInitCalibration);
diff --git a/drivers/net/wireless/digiPiper/airohaCalibration.h b/drivers/net/wireless/digiPiper/airohaCalibration.h
new file mode 100644
index 000000000000..36543c87fc51
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airohaCalibration.h
@@ -0,0 +1,108 @@
+/*
+ * This file contains the code which performs automatic recalibration of the
+ * Airoha transceiver.
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef AIROHA_CALIBRATION_H
+#define AIROHA_CALIBRATION_H
+
+#include "pipermain.h"
+
+
+#define MAX_SAMPLES (3)
+
+/*
+ * Field values used for computing ABS values.
+ */
+enum {
+ OUT_POWER,
+ ADC_VAL,
+ POWER_INDEX
+};
+
+#ifdef WANT_CAL_DEBUG
+static const char *fieldDescription[] = {
+ "OUT_POWER",
+ "ADC_VAL",
+ "POWER_INDEX"
+};
+#endif
+
+/*
+ * States for the auto calibration thread.
+ */
+enum {
+ RESTART_STATE,
+ COLLECT_SAMPLES_STATE,
+ GOT_SAMPLE_STATE,
+ RECALIBRATE_STATE
+};
+
+#ifdef WANT_CAL_DEBUG
+static const char *stateText[] = {
+ "RESTART_STATE",
+ "COLLECT_SAMPLES_STATE",
+ "GOT_SAMPLE_STATE",
+ "RECALIBRATE_STATE"
+};
+#endif
+
+
+/*
+ * Slope types accepted by computeSlope().
+ */
+enum {
+ POWER_INDEX_OVER_OUT_POWER,
+ ADC_OVER_OUT_POWER,
+ OUT_POWER_OVER_ADC,
+ POWER_INDEX_OVER_ADC
+};
+
+
+typedef struct {
+ unsigned rate; /* rate packet transmitted at */
+ unsigned int sample; /* measured sample */
+} sampleInfo_t;
+
+struct airohaCalibrationData {
+ struct task_struct *threadCB;
+ spinlock_t lock;
+ volatile unsigned int events;
+ unsigned int sampleCount;
+ sampleInfo_t sample[MAX_SAMPLES];
+ wcd_data_t *nvram;
+ wcd_curve_t *curve;
+ int slopeTimes1000;
+ int adcSlopeTimes1000;
+ int outPowerSlopeTimes1000;
+ int powerIndexSlopeTimes1000;
+ int expectedAdc;
+ int powerIndex, correctedPowerIndex;
+ wcd_point_t *p1;
+
+
+ void *priv;
+ struct calibration_ops *cops;
+ bool initialized;
+};
+
+struct calibration_ops {
+ u16(*adc_read_peak) (struct airohaCalibrationData *);
+ void (*adc_clear_peak) (struct airohaCalibrationData *);
+ u16(*adc_read_last_val) (struct airohaCalibrationData *);
+ void (*adc_shutdown) (struct airohaCalibrationData *);
+};
+
+
+void digiWifiInitCalibration(struct piper_priv *digi);
+void digiWifiDeInitCalibration(struct piper_priv *digi);
+int digiWifiCalibrationPowerIndex(struct piper_priv *piperp);
+
+#endif /* AIROHA_CALIBRATION_H */
diff --git a/drivers/net/wireless/digiPiper/digiDebug.c b/drivers/net/wireless/digiPiper/digiDebug.c
new file mode 100644
index 000000000000..627dd8c698ec
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiDebug.c
@@ -0,0 +1,205 @@
+/*
+ * digiDebug.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains some debugging routines.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+
+#define DUMP_WORDS_MAX (700)
+static unsigned int dumpWordsWord[DUMP_WORDS_MAX];
+static unsigned int dumpWordsCount = 0;
+
+void digiWifiDumpWordsAdd(unsigned int word)
+{
+ if (dumpWordsCount < DUMP_WORDS_MAX)
+ {
+ dumpWordsWord[dumpWordsCount++] = word;
+ }
+}
+void digiWifiDumpWordsDump(void)
+{
+ unsigned int *p = dumpWordsWord;
+ unsigned int wordsToGo = dumpWordsCount;
+
+ dumpWordsCount = 0;
+
+ while (wordsToGo >= 4)
+ {
+ digi_dbg("%8.8X %8.8X - %8.8X %8.8X\n", p[0], p[1], p[2], p[3]);
+ p += 4;
+ wordsToGo -= 4;
+ }
+ if (wordsToGo == 3)
+ {
+ digi_dbg("%8.8X %8.8X - %8.8X\n", p[0], p[1], p[2]);
+ }
+ else if (wordsToGo == 2)
+ {
+ digi_dbg("%8.8X %8.8X \n", p[0], p[1]);
+ }
+ else if (wordsToGo == 1)
+ {
+ digi_dbg("%8.8X \n", p[0]);
+ }
+ digi_dbg("--------------\n");
+}
+
+void digiWifiDumpWordsReset(void)
+{
+ dumpWordsCount = 0;
+}
+
+
+void digiWifiDumpBuffer(u8 *buffer, unsigned int length)
+{
+ unsigned int i, word;
+
+ digiWifiDumpWordsReset();
+
+ for (i = 0; i < length / sizeof(unsigned int); i++)
+ {
+ memcpy(&word, &buffer[i*sizeof(unsigned int)], sizeof(word));
+ digiWifiDumpWordsAdd(cpu_to_be32(word));
+ }
+
+ digiWifiDumpWordsDump();
+}
+
+
+void digiWifiDumpSkb(struct sk_buff *skb)
+{
+ unsigned int bytesLeft = skb->len;
+ unsigned char *p = skb->data;
+
+ digi_dbg("skb has %d bytes\n", skb->len);
+ while (bytesLeft >= 16)
+ {
+ digi_dbg("%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X - %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ p += 16;
+ bytesLeft -= 16;
+ }
+ if (bytesLeft >= 8)
+ {
+ digi_dbg("%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+ p += 8;
+ bytesLeft -= 8;
+ }
+ if (bytesLeft >= 4)
+ {
+ digi_dbg("%2.2X %2.2X %2.2X %2.2X \n",
+ p[0], p[1], p[2], p[3]);
+ p += 4;
+ bytesLeft -= 4;
+ }
+ if (bytesLeft >= 2)
+ {
+ digi_dbg("%2.2X %2.2X \n",
+ p[0], p[1]);
+ p += 2;
+ bytesLeft -= 2;
+ }
+ if (bytesLeft >= 1)
+ {
+ digi_dbg("%2.2X \n",
+ p[0]);
+ p += 1;
+ bytesLeft -= 1;
+ }
+}
+
+EXPORT_SYMBOL_GPL(digiWifiDumpSkb);
+
+
+void digiWifiDumpRegisters(struct piper_priv *digi, unsigned int regs)
+{
+#ifdef WANT_DEBUG
+ unsigned int i;
+
+ if (regs & CTRL_STATUS_REGS)
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", "Gen Ctrl", digi->ac->rd_reg(digi, BB_GENERAL_CTL),
+ "Gen Status", digi->ac->rd_reg(digi, BB_GENERAL_STAT));
+ }
+ else if (regs & IRQ_REGS)
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", "IRQ Mask", digi->ac->rd_reg(digi, BB_IRQ_MASK),
+ "IRQ Status", digi->ac->rd_reg(digi, BB_IRQ_STAT));
+ }
+ else if (regs & MAIN_REGS)
+ {
+ const char *regNames[] = {"Version", "Gen Ctrl", "Gen Status", "RSSI/AES",
+ "Int Mask", "Int Status", "SPI Data", "SPI Ctrl",
+ "Data FIFO", "not used", "conf-1", "conf-2", "AES FIFO",
+ "not used", "AES Ctrl", "IO Ctrl"};
+ printk(KERN_ERR "Main Registers:\n");
+ for (i = BB_VERSION; i <= BB_OUTPUT_CONTROL; i = i+8)
+ {
+ if ((i != BB_DATA_FIFO) && (i != BB_AES_FIFO))
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", regNames[i>>2], digi->ac->rd_reg(digi, i), regNames[(i>>2) + 1], digi->ac->rd_reg(digi, i+4));
+ }
+ }
+ }
+ if (regs & MAC_REGS)
+ {
+ const char *regNames[] = {"STA ID0", "STA ID1", "BSS ID0", "BSS ID1",
+ "OFDM/PSK", "Backoff", "DTIM/List", "B Int",
+ "Rev/M Stat", "C C/M CTL", "Measure", "Beac Fltr"};
+
+ printk(KERN_ERR "Secondary Registers:\n");
+ for (i = MAC_STA_ID0; i <= MAC_BEACON_FILT; i = i+8)
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", regNames[((i - MAC_STA_ID0) >>2)], digi->ac->rd_reg(digi, i), regNames[((i - MAC_STA_ID0)>>2) + 1], digi->ac->rd_reg(digi, i+4));
+ }
+ }
+ if (regs & FRAME_BUFFER_REGS)
+ {
+ unsigned int word[4];
+ printk(KERN_ERR "Real time frame buffer\n");
+
+ word[0] = be32_to_cpu(digi->ac->rd_reg(digi, 0xc0));
+ word[1] = be32_to_cpu(digi->ac->rd_reg(digi, 0xc4));
+ word[2] = be32_to_cpu(digi->ac->rd_reg(digi, 0xc8));
+ word[3] = be32_to_cpu(digi->ac->rd_reg(digi, 0xcc));
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ word[0] = be32_to_cpu(digi->ac->rd_reg(digi, 0xd0));
+ word[1] = be32_to_cpu(digi->ac->rd_reg(digi, 0xd4));
+ word[2] = be32_to_cpu(digi->ac->rd_reg(digi, 0xd8));
+ word[3] = be32_to_cpu(digi->ac->rd_reg(digi, 0xdc));
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ }
+ if (regs & FIFO_REGS)
+ {
+ unsigned int word[4];
+ printk(KERN_ERR "FIFO contents\n");
+
+ word[0] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[1] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[2] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[3] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ word[0] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[1] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[2] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[3] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ }
+#endif
+}
+EXPORT_SYMBOL_GPL(digiWifiDumpRegisters);
diff --git a/drivers/net/wireless/digiPiper/digiIsr.c b/drivers/net/wireless/digiPiper/digiIsr.c
new file mode 100644
index 000000000000..e2128e254055
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiIsr.c
@@ -0,0 +1,159 @@
+/*
+ * digiIsr.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains the routines that are related to processing interrupts
+ * from the MAC.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+
+#define IRQ_DEBUG (0)
+#if IRQ_DEBUG
+static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * This routine handles interrupts from the MAC.
+ */
+irqreturn_t piper_irq_handler(int irq, void *dev_id)
+{
+ struct piper_priv *piperp = dev_id;
+ u32 status;
+
+ /* Acknowledge pending interrupts */
+ status = piperp->ac->rd_reg(piperp, BB_IRQ_STAT);
+ status &= piperp->ac->rd_reg(piperp, BB_IRQ_MASK);
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, status, op_write);
+
+ if (status & BB_IRQ_MASK_RX_FIFO) {
+ /*
+ * This interrupt indicates we have a frame in the FIFO.
+ * Set up to receive the packet. Disable further interrupts
+ * until the receive is complete.
+ */
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_RX_FIFO);
+ /*
+ * Call the receive routine directly inside the irq handler
+ * or in the tasklet, depending on configuration.
+ */
+#if WANT_TO_RECEIVE_FRAMES_IN_ISR
+ piper_rx_tasklet((unsigned long) piperp);
+#else
+ tasklet_hi_schedule(&piperp->rx_tasklet);
+#endif
+ }
+
+ if (status & BB_IRQ_MASK_TX_FIFO_EMPTY) {
+ /*
+ * Transmit complete interrupt. This IRQ is only unmasked if we are
+ * not expecting the packet to be ACKed. This will be the case for
+ * broadcasts. In this case, tell mac80211 the transmit occurred and
+ * restart the tx queue.
+ */
+ if (piper_tx_getqueue(piperp) != NULL) {
+ piperp->tx_signal_strength = 0;
+ piperp->tx_result = TX_COMPLETE;
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ dprintk(DWARNING, "BB_IRQ_MASK_TX_FIFO_EMPTY and null packet?\n");
+ }
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT | BB_IRQ_MASK_TX_ABORT);
+ }
+
+ if (status & BB_IRQ_MASK_TIMEOUT) {
+ /* AP did not ACK our TX packet */
+ if (piper_tx_getqueue(piperp) != NULL) {
+ /* Update retry counter */
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ dprintk(DWARNING, "BB_IRQ_MASK_TIMEOUT and null packet?\n");
+ }
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT | BB_IRQ_MASK_TX_ABORT);
+ }
+
+ if (unlikely(status & BB_IRQ_MASK_TX_ABORT)) {
+ dprintk(DWARNING, "TX abort\n");
+
+ /* Could not transmit a packet because the media was busy */
+ if (piper_tx_getqueue(piperp) != NULL) {
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ dprintk(DWARNING, "BB_IRQ_MASK_TX_ABORT and null packet?\n");
+ }
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT | BB_IRQ_MASK_TX_ABORT);
+ }
+
+ if (status & BB_IRQ_MASK_TBTT) {
+ /*
+ * This interrupt occurs at the start of a beacon period. The only thing
+ * we need to do is to write a new beacon backoff value.
+ */
+ u32 reg = piperp->ac->rd_reg(piperp, MAC_BEACON_FILT) & ~MAC_BEACON_BACKOFF_MASK;
+ piperp->ac->wr_reg(piperp, MAC_BEACON_FILT,
+ reg | piperp->get_next_beacon_backoff(), op_write);
+ /*
+ * TODO:
+ * Improve the way we keep track of whether or not we sent the last
+ * beacon. What we are doing now is to assume that we did until and
+ * unless we receive a beacon. What we should do is look for either
+ * a beacon or a TX end interrupt. However, since mac80211 doesn't
+ * tell us what the ATIM window is, we have to assume it is zero,
+ * which means we could be transmitting a frame at the same
+ * time we are sending the beacon, so there isn't really any easy
+ * way for us to do this. In fact, even if there was an ATIM
+ * window, we could have started a transmit just before we get this
+ * interrupt, so I'm not sure how we are really suppose to keep
+ * track of this.
+ */
+ /* assume we sent last beacon unless we receive one */
+ piperp->beacon.weSentLastOne = true;
+ }
+
+ if (status & BB_IRQ_MASK_ATIM) {
+ /*
+ * This interrupt should not occur since we are not using it. When in
+ * IBSS mode, the beacon period starts at the TBTT interrupt and ends
+ * at this interrupt. We are not suppose to send packets between the
+ * two interrupts. However, mac80211 does not seem to provide a way
+ * for us to find out how long the ATIM period is, so we have to assume
+ * that there isn't one.
+ *
+ * If we were supporting this interrupt we would have to synchronize
+ * with the transmit routine so that transmit is paused during this
+ * time.
+ */
+ dprintk(DWARNING, "BB_IRQ_MASK_ATIM irq (0x%08x)\n", status);
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_ATIM);
+ }
+
+ if (unlikely(status & BB_IRQ_MASK_RX_OVERRUN))
+ piperp->pstats.rx_overruns++;
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(piper_irq_handler);
+
+
+
diff --git a/drivers/net/wireless/digiPiper/digiMac80211.c b/drivers/net/wireless/digiPiper/digiMac80211.c
new file mode 100644
index 000000000000..b9ca3ef10230
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiMac80211.c
@@ -0,0 +1,1000 @@
+/*
+ * digiMac80211.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains the routines that interface with the mac80211
+ * library.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include <crypto/aes.h>
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "digiPs.h"
+
+#define MAC_DEBUG (1)
+
+#if MAC_DEBUG
+static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * This constant determines how many times per second the led_timer_fn
+ * function will be called. (HZ >> 3) means 8 times a second.
+ */
+#define LED_TIMER_RATE (HZ >> 3)
+#define LED_MAX_COUNT (15)
+
+/*
+ * This function is called from a timer to blink an LED in order to
+ * indicate what are current state is.
+ */
+static void led_timer_fn(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *) context;
+ static unsigned int count = 0;
+
+ if(!piperp->pdata->set_led)
+ return;
+
+ switch (piperp->led_state) {
+ case led_shutdown:
+ /* Turn LED off if we are shut down */
+ piperp->pdata->set_led(piperp, STATUS_LED, 0);
+ break;
+ case led_adhoc:
+ /* Blink LED slowly in ad-hoc mode */
+ piperp->pdata->set_led(piperp, STATUS_LED, (count & 8) ? 0 : 1);
+ break;
+ case led_not_associated:
+ /* Blink LED rapidly when not associated with an AP */
+ piperp->pdata->set_led(piperp, STATUS_LED, (count & 1) ? 0 : 1);
+ break;
+ case led_associated:
+ /* LED steadily on when associated */
+ piperp->pdata->set_led(piperp, STATUS_LED, 1);
+ break;
+ default:
+ /* Oops. How did we get here? */
+ break;
+ }
+ count++;
+ if (count > LED_MAX_COUNT) {
+ count = 0;
+ }
+
+ piperp->led_timer.expires += LED_TIMER_RATE;
+ add_timer(&piperp->led_timer);
+}
+
+/*
+ * This function sets the current LED state.
+ */
+static int piper_set_status_led(struct ieee80211_hw *hw, enum led_states state)
+{
+ struct piper_priv *piperp = (struct piper_priv *)hw->priv;
+
+ piperp->led_state = state;
+
+ if(!piperp->pdata->set_led)
+ return -ENOSYS;
+
+ if (state == led_shutdown)
+ piperp->pdata->set_led(piperp, STATUS_LED, 0);
+
+ return 0;
+}
+
+/*
+ * This routine is called to enable IBSS support whenever we receive
+ * configuration commands from mac80211 related to IBSS support. The
+ * routine examines the configuration settings to determine if IBSS
+ * support should be enabled and, if so, turns on automatic beacon
+ * generation.
+ *
+ * TODO: This code may need to be moved into piper.c since other
+ * H/W does not implement automatic generate of beacons.
+ */
+static void piper_enable_ibss(struct piper_priv *piperp, enum nl80211_iftype iftype)
+{
+ dprintk(DVVERBOSE, "\n");
+
+ if (((iftype == NL80211_IFTYPE_ADHOC) || (iftype == NL80211_IFTYPE_MESH_POINT))
+ && (piperp->beacon.loaded) && (piperp->beacon.enabled)
+ && ((piperp->ac->rd_reg(piperp, MAC_CFP_ATIM) & MAC_BEACON_INTERVAL_MASK) != 0)) {
+ /*
+ * If we come here, then we are running in IBSS mode, beacons are enabled,
+ * and we have the information we need, so start sending beacons.
+ */
+ /* TODO: Handle non-zero ATIM period. mac80211 currently has no way to
+ * tell us what the ATIM period is, but someday they might fix that.*/
+
+ u32 reg = piperp->ac->rd_reg(piperp, MAC_BEACON_FILT) & ~MAC_BEACON_BACKOFF_MASK;
+ piperp->ac->wr_reg(piperp, MAC_BEACON_FILT,
+ reg | piperp->get_next_beacon_backoff(), op_write);
+ /* enable beacon interrupts*/
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_TBTT);
+ piperp->ac->wr_reg(piperp,
+ MAC_CTL, MAC_CTL_BEACON_TX | MAC_CTL_IBSS, op_or);
+ dprintk(DVERBOSE, "IBSS turned ON\n");
+ piper_set_status_led(piperp->hw, led_adhoc);
+ } else {
+ /*
+ * If we come here, then either we are not suppose to transmit beacons,
+ * or we do not yet have all the information we need to transmit
+ * beacons. Make sure the automatic beacon function is disabled.
+ */
+ /* shut down beacons */
+ piperp->ac->wr_reg(piperp, MAC_CTL,
+ ~(MAC_CTL_BEACON_TX | MAC_CTL_IBSS), op_and);
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_TBTT);
+ dprintk(DVERBOSE, "IBSS turned OFF\n");
+ }
+}
+
+/*
+ * Set the transmit power level. The real work is done in the
+ * transceiver code.
+ */
+static int piper_set_tx_power(struct ieee80211_hw *hw, int power)
+{
+ struct piper_priv *digi = hw->priv;
+ int err;
+ int oldTxPower = digi->tx_power;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (power == digi->tx_power)
+ return 0;
+
+ digi->tx_power = power;
+ err = digi->rf->set_pwr(hw, power);
+ if (err)
+ digi->tx_power = oldTxPower;
+
+ return err;
+}
+
+/*
+ * Utility routine that sets a sequence number for a data packet.
+ */
+static void assign_seq_number(struct sk_buff *skb, bool increment)
+{
+#define SEQUENCE_NUMBER_MASK (0xfff0)
+ static u16 seq_number = 0;
+ _80211HeaderType *header = (_80211HeaderType *)skb->data;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (skb->len >= sizeof(*header)) {
+ u16 seq_field;
+
+ /*
+ * TODO: memcpy's are here because I am concerned we may get
+ * an unaligned frame. Is this a real possibility? Or
+ * am I just wasting CPU time?
+ */
+ memcpy(&seq_field, &header->squ.sq16, sizeof(header->squ.sq16));
+ seq_field &= ~SEQUENCE_NUMBER_MASK;
+ seq_field |= (SEQUENCE_NUMBER_MASK & (seq_number << 4));
+ memcpy(&header->squ.sq16, &seq_field, sizeof(header->squ.sq16));
+ if (increment)
+ seq_number++;
+ }
+}
+
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+/* Get 16 bits at byte pointer */
+#define GET16(bp) ((bp)[0] | ((bp)[1] << 8))
+/* Get 32 bits at byte pointer */
+#define GET32(bp) ((bp)[0] | ((bp)[1] << 8) | ((bp)[2] << 16) | ((bp)[3] << 24))
+/* Store 16 bits at byte pointer */
+#define SET16(bp, data) { (bp)[0] = (data); \
+ (bp)[1] = (data) >> 8; }
+/* Store 32 bits at byte pointer */
+#define SET32(bp, data) { (bp)[0] = (data); \
+ (bp)[1] = (data) >> 8; \
+ (bp)[2] = (data) >> 16; \
+ (bp)[3] = (data) >> 24; }
+
+static inline void dw_inc_48(u48* n)
+{
+ (*n)++;
+ *n &= ((u64) 1 << 48) - 1;
+}
+
+/*
+ * This function prepares a blob of data we will have to send to the AES
+ * H/W encryption engine. The data consists of the AES initialization
+ * vector and 2 16 byte headers.
+ *
+ * Returns true if successful, or false if something goes wrong
+ */
+bool piper_prepare_aes_datablob(struct piper_priv *piperp, unsigned int keyIndex,
+ u8 *aesBlob, u8 *frame, u32 length, bool isTransmit)
+{
+ _80211HeaderType *header = (_80211HeaderType *)frame;
+ u8 *body = &frame[sizeof(*header)];
+ int dlen = length - (_80211_HEADER_LENGTH + PIPER_EXTIV_SIZE);
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (keyIndex >= PIPER_MAX_KEYS) {
+ dprintk(DWARNING, "encryption key index %d is out of range.\n",
+ keyIndex);
+ return false;
+ }
+
+ if (piperp->key[keyIndex].valid == false)
+ return false;
+
+ /* Set up CCM initial block for MIC IV */
+ memset(aesBlob, 0, AES_BLOB_LENGTH);
+ aesBlob[0] = 0x59;
+ aesBlob[1] = 0;
+ memcpy (&aesBlob[2], header->addr2, ETH_ALEN);
+ aesBlob[8] = body[7];
+ aesBlob[9] = body[6];
+ aesBlob[10] = body[5];
+ aesBlob[11] = body[4];
+ aesBlob[12] = body[1];
+ aesBlob[13] = body[0];
+ aesBlob[14] = dlen >> 8;
+ aesBlob[15] = dlen;
+
+ /* Set up MIC header blocks */
+#define AES_HEADER_0_OFFSET (16)
+#define AES_HEADER_1_OFFSET (32)
+ aesBlob[AES_HEADER_0_OFFSET+0] = 0;
+ aesBlob[AES_HEADER_0_OFFSET+1] = 22;
+ aesBlob[AES_HEADER_0_OFFSET+2] = frame[0] & 0xcf;
+ aesBlob[AES_HEADER_0_OFFSET+3] = frame[1] & 0xd7;
+ /*
+ * This memcpy writes data into the last 12 bytes of the first header
+ * and the first 6 bytes of the 2nd header. I did it as one memcpy
+ * call for efficiency.
+ */
+ memcpy(&aesBlob[AES_HEADER_0_OFFSET+4], header->addr1, 3*ETH_ALEN);
+ aesBlob[AES_HEADER_1_OFFSET+6] = header->squ.sq.frag;
+ aesBlob[AES_HEADER_1_OFFSET+7] = 0;
+ memset (&aesBlob[AES_HEADER_1_OFFSET+8], 0, 8); /* clear vector location in data */
+
+ return true;
+}
+
+/*
+ * mac80211 calls this routine to transmit a frame. We set up
+ * up the information the trasmit tasklet will need, and then
+ * schedule the tasklet.
+ */
+int piper_hw_tx_private(struct ieee80211_hw *hw, struct sk_buff *skb, tx_skb_return_cb_t skb_return_cb)
+{
+ struct piper_priv *piperp = hw->priv;
+ struct ieee80211_tx_info *txInfo = IEEE80211_SKB_CB(skb);
+ unsigned long flags;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (piperp->is_radio_on == false) {
+ dprintk(DERROR, "called with radio off\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Our H/W can only transmit a single packet at a time. mac80211
+ * already maintains a queue of packets, so there is no reason
+ * for us to set up another one. We stop the mac80211 queue everytime
+ * we get a transmit request and restart it when the transmit
+ * operation completes.
+ */
+ ieee80211_stop_queues(hw);
+
+ if (txInfo->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+ assign_seq_number(skb,
+ !!(txInfo->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT));
+ }
+
+ piperp->use_hw_aes = false;
+ if (txInfo->control.hw_key != NULL) {
+ /*
+ * We've been passed an encryption key, so mac80211 must want us
+ * to encrypt the packet with our fancy H/W encryptor. Let's get
+ * set up for that now.
+ */
+ piperp->tx_aes_key = txInfo->control.hw_key->hw_key_idx;
+ piperp->use_hw_aes =
+ piper_prepare_aes_datablob(piperp,
+ txInfo->control.hw_key->hw_key_idx,
+ (u8 *)piperp->tx_aes_blob, skb->data,
+ skb->len, true);
+ }
+ piper_ps_set_header_flag(piperp, ((_80211HeaderType *) skb->data)); /* set power management bit as appropriate*/
+
+ /*
+ * Add space at the start of the frame for the H/W level transmit header.
+ * We can't generate the header now. It must be generated everytime we
+ * transmit because the transmit rate changes when we do retries.
+ */
+ skb_push(skb, TX_HEADER_LENGTH);
+
+ piperp->pstats.tx_retry_index = 0;
+ piperp->pstats.tx_total_tetries = 0;
+ memset(piperp->pstats.tx_retry_count, 0, sizeof(piperp->pstats.tx_retry_count));
+ txInfo->flags &= ~(IEEE80211_TX_STAT_TX_FILTERED |
+ IEEE80211_TX_STAT_ACK |
+ IEEE80211_TX_STAT_AMPDU |
+ IEEE80211_TX_STAT_AMPDU_NO_BACK);
+ piperp->pstats.tx_queue.len++;
+ piperp->pstats.tx_queue.count++;
+
+ if (piper_tx_enqueue(piperp, skb, skb_return_cb) == -1) {
+ skb_pull(skb, TX_HEADER_LENGTH); /* undo the skb_push above */
+ return -EBUSY;
+ }
+
+ spin_lock_irqsave(&piperp->tx_tasklet_lock, flags);
+ if (!piperp->tx_tasklet_running) {
+ piperp->tx_tasklet_running = true;
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ }
+
+ spin_unlock_irqrestore(&piperp->tx_tasklet_lock, flags);
+
+ piperp->pstats.tx_start_count++;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(piper_hw_tx_private);
+
+
+int piper_hw_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ return piper_hw_tx_private(hw, skb, ieee80211_tx_status_irqsafe);
+}
+
+
+/*
+ * mac80211 calls this routine to initialize the H/W.
+ */
+static int piper_hw_start(struct ieee80211_hw *hw)
+{
+ struct piper_priv *piperp = hw->priv;
+ int ret = 0;
+
+ dprintk(DVVERBOSE, "\n");
+ piperp->if_type = __NL80211_IFTYPE_AFTER_LAST;
+
+ /* Initialize the MAC H/W */
+ if ((ret = piperp->init_hw(piperp, IEEE80211_BAND_2GHZ)) != 0) {
+ dprintk(DERROR, "unable to initialize piper HW (%d)\n", ret);
+ return ret;
+ }
+
+ piperp->is_radio_on = true;
+
+ /*
+ * Initialize the antenna with the defualt setting defined in the
+ * probe function. This can be changed, currently, through a sysfs
+ * entry in the device directory
+ */
+ if ((ret = piperp->set_antenna(piperp, piperp->antenna)) != 0) {
+ dprintk(DERROR, "piper_set_antenna_div() failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* set status led to link off */
+ piper_set_status_led(hw, led_shutdown);
+
+ /* Get the tasklets ready to roll */
+ piperp->tx_result = TX_NOT_DONE;
+ tasklet_enable(&piperp->rx_tasklet);
+ tasklet_enable(&piperp->tx_tasklet);
+
+ /*
+ * Enable receive interrupts, but leave the transmit interrupts
+ * and beacon interrupts off for now.
+ */
+ piperp->clear_irq_mask_bit(piperp, 0xffffffff);
+ piperp->set_irq_mask_bit(piperp,
+ BB_IRQ_MASK_RX_OVERRUN | BB_IRQ_MASK_RX_FIFO);
+ enable_irq(piperp->irq);
+
+ memset(piperp->bssid, 0, ETH_ALEN);
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this routine to shut us down.
+ */
+static void piper_hw_stop(struct ieee80211_hw *hw)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+
+ /* Initialize the MAC H/W */
+ if (piperp->deinit_hw)
+ piperp->deinit_hw(piperp);
+
+ /* set status led to link off */
+ if (piper_set_status_led(hw, led_shutdown))
+ return; /* hardware's probably gone, give up */
+
+ /* turn off phy */
+ piperp->rf->stop(hw);
+
+ /* Disable interrupts before turning off */
+ disable_irq(piperp->irq);
+
+ /* turn off MAX_GAIN, ADC clocks, and so on */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RESET, op_and);
+
+ /* turn off MAC control/mac filt/aes key */
+ piperp->ac->wr_reg(piperp, MAC_CTL, 0, op_write);
+
+ /* turn off interrupts */
+ tasklet_disable(&piperp->rx_tasklet);
+ tasklet_disable(&piperp->tx_tasklet);
+ piperp->clear_irq_mask_bit(piperp, 0xffffffff);
+}
+
+/*
+ * mac80211 calls this routine to really start the H/W.
+ * The device type is also set here.
+ */
+static int piper_hw_add_intf(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "if type: %x\n", conf->type);
+
+ /* __NL80211_IFTYPE_AFTER_LAST means no mode selected */
+ if (piperp->if_type != __NL80211_IFTYPE_AFTER_LAST) {
+ dprintk(DERROR, "unsupported interface type %x, expected %x\n",
+ conf->type, __NL80211_IFTYPE_AFTER_LAST);
+ return -EOPNOTSUPP;
+ }
+
+ switch (conf->type) {
+ case NL80211_IFTYPE_ADHOC:
+ piper_set_status_led(piperp->hw, led_adhoc);
+ piperp->if_type = conf->type;
+ break;
+
+ case NL80211_IFTYPE_STATION:
+ piper_set_status_led(hw, led_not_associated);
+ piperp->if_type = conf->type;
+ break;
+
+ case NL80211_IFTYPE_MESH_POINT:
+ piperp->if_type = conf->type;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this function to shut down us down.
+ */
+static void piper_hw_rm_intf(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct piper_priv *digi = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ digi->if_type = __NL80211_IFTYPE_AFTER_LAST;
+}
+
+/*
+ * mac80211 calls this function to pass us configuration information.
+ */
+static int piper_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct piper_priv *piperp = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+ u32 tempval;
+ int err;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ /*
+ * Enable power save mode if bit set in flags, and if we are in station
+ * mode. Power save is not supported in ad-hoc/mesh mode.
+ */
+ piper_ps_scan_event(piperp);
+ piper_ps_set(piperp, ( (conf->flags & IEEE80211_CONF_PS)
+ && (piperp->if_type == NL80211_IFTYPE_STATION)
+ && (piperp->areWeAssociated)));
+ }
+ /* Should we turn the radio off? */
+ if ((piperp->is_radio_on = (conf->radio_enabled != 0)) != 0) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_RX_EN, op_or);
+ } else {
+ dprintk(DNORMAL, "Turning radio off\n");
+ return piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_RX_EN, op_and);
+ }
+
+ /* Adjust the beacon interval and listen interval */
+ tempval = piperp->ac->rd_reg(piperp, MAC_CFP_ATIM) & ~MAC_BEACON_INTERVAL_MASK;
+ tempval |= conf->beacon_int << MAC_BEACON_INTERVAL_SHIFT;
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, tempval, op_write);
+
+ tempval = piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD) & ~MAC_LISTEN_INTERVAL_MASK;
+ tempval |= conf->listen_interval;
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, tempval, op_write);
+
+ /* Adjust the power level */
+ if ((err = piper_set_tx_power(hw, conf->power_level)) != 0) {
+ dprintk(DERROR, "unable to set tx power to %d\n",
+ conf->power_level);
+ return err;
+ }
+
+ /* Set channel */
+ if (conf->channel->hw_value != piperp->channel) {
+ piper_ps_scan_event(piperp);
+ if ((err = piperp->rf->set_chan(hw, conf->channel->hw_value)) !=0) {
+ dprintk(DERROR, "unable to set ch to %d\n",
+ conf->channel->hw_value);
+ return err;
+ }
+ piperp->channel = conf->channel->hw_value;
+ }
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this routine to set BSS related configuration settings.
+ */
+static int piper_hw_config_intf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_if_conf *conf)
+{
+ struct piper_priv *piperp = hw->priv;
+ u32 bssid[2];
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (conf->changed & IEEE80211_IFCC_BSSID &&
+ !is_zero_ether_addr(conf->bssid) &&
+ !is_multicast_ether_addr(conf->bssid)) {
+
+ piper_ps_scan_event(piperp);
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ dprintk(DVERBOSE, "BSSID: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+ conf->bssid[0], conf->bssid[1], conf->bssid[2],
+ conf->bssid[3], conf->bssid[4], conf->bssid[5]);
+
+ bssid[0] = conf->bssid[3] | conf->bssid[2] << 8 |
+ conf->bssid[1] << 16 | conf->bssid[0] << 24;
+ bssid[1] = conf->bssid[5] << 16 | conf->bssid[4] << 24;
+
+ if ((bssid[0] == 0) && (bssid[1] == 0)) {
+ /*
+ * If we come here, then the MAC layer is telling us to set a 0
+ * SSID. In this case, we really want to set the SSID to the
+ * broadcast address so that we receive broadcasts.
+ */
+ bssid[0] = 0xffffffff;
+ bssid[1] = 0xffffffff;
+ }
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID0, bssid[0], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID1, bssid[1], op_write);
+ memcpy(piperp->bssid, conf->bssid, ETH_ALEN);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((conf->changed & IEEE80211_IFCC_BEACON) &&
+ (piperp->if_type == NL80211_IFTYPE_ADHOC)) {
+ struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+ struct ieee80211_rate rate;
+
+ if (!beacon)
+ return -ENOMEM;
+
+ rate.bitrate = 10; /* beacons always sent at 1 Megabit*/
+ skb_push(beacon, TX_HEADER_LENGTH);
+ phy_set_plcp(beacon->data, beacon->len - TX_HEADER_LENGTH, &rate, 0);
+ piperp->ac->wr_reg(piperp, MAC_CTL, ~MAC_CTL_BEACON_TX, op_and);
+ piperp->load_beacon(piperp, beacon->data, beacon->len);
+
+ /* TODO: digi->beacon.enabled should be set by IEEE80211_IFCC_BEACON_ENABLED
+ when we update to latest mac80211 */
+ piperp->beacon.enabled = true;
+ piper_enable_ibss(piperp, vif->type);
+ dev_kfree_skb(beacon); /* we are responsible for freeing this buffer*/
+ }
+
+ return 0;
+}
+
+/*
+ * mac80211 wants to change our frame filtering settings. We don't
+ * actually support this.
+ */
+static void piper_hw_config_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags, unsigned int *total_flags,
+ int mc_count, struct dev_addr_list *mclist)
+{
+ dprintk(DVVERBOSE, "\n");
+
+ /* we don't support filtering so clear all flags; however, we also
+ * can't pass failed FCS/PLCP frames, so don't clear those. */
+ *total_flags &= (FIF_FCSFAIL | FIF_PLCPFAIL);
+}
+
+/*
+ * There are 1024 TU's (time units) to a second, and HZ jiffies to a
+ * second. This macro converts TUs to jiffies.
+ */
+#define TU_TO_JIFFIES(x) (((x*HZ) + 512) / 1024)
+
+
+/*
+ * mac80211 calls this routine when something about our BSS has changed.
+ * Usually, this routine only gets called when we associate, or disassociate.
+ */
+static void piper_hw_bss_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf, u32 changed)
+{
+ struct piper_priv *piperp = hw->priv;
+ u32 reg;
+
+ dprintk(DVVERBOSE, " changed = 0x%08x\n", changed);
+
+ if (changed & BSS_CHANGED_ASSOC) {
+ piper_ps_scan_event(piperp);
+ /* Our association status has changed */
+ if (piperp->if_type == NL80211_IFTYPE_STATION) {
+ piper_set_status_led(hw, conf->assoc ? led_associated : led_not_associated);
+ }
+ piperp->areWeAssociated = conf->assoc;
+ piperp->ps.aid = conf->aid;
+
+ digi_dbg(" AP %s\n", conf->assoc ?
+ "associated" : "disassociated");
+ }
+ if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+ piperp->tx_cts = conf->use_cts_prot;
+ }
+ if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+#define WANT_SHORT_PREAMBLE_SUPPORT (1)
+/* TODO: Determine if short preambles really hurt us, or if I'm just seeing things. */
+#if WANT_SHORT_PREAMBLE_SUPPORT
+ if (conf->use_short_preamble) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_SH_PRE, op_or);
+ } else {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_SH_PRE, op_and);
+ }
+ piperp->use_short_preamble = conf->use_short_preamble;
+#else
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_SH_PRE, op_or);
+#endif
+ }
+
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ /*
+ * The list of transmit rates has changed. Update the
+ * rates we will receive at to match those the AP will
+ * transmit at. This should improve our receive performance
+ * since we won't listen to junk at the wrong rate.
+ */
+ unsigned int ofdm = 0, psk = 0;
+
+ reg = piperp->ac->rd_reg(piperp, MAC_SSID_LEN) &
+ ~(MAC_OFDM_BRS_MASK | MAC_PSK_BRS_MASK);
+
+ piperp->rf->getOfdmBrs(piperp->channel, conf->basic_rates, &ofdm, &psk);
+ reg |= ofdm << MAC_OFDM_BRS_SHIFT;
+ reg |= psk << MAC_PSK_BRS_SHIFT;
+ piperp->ac->wr_reg(piperp, MAC_SSID_LEN, reg, op_write);
+
+ dprintk(DVERBOSE, "BRS mask set to 0x%8.8X\n", reg);
+
+ if (ofdm == 0) {
+ /* Disable ofdm receiver if no ofdm rates supported */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT,
+ ~BB_GENERAL_STAT_A_EN, op_and);
+ } else {
+ /* Enable ofdm receiver if any ofdm rates supported */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT,
+ BB_GENERAL_STAT_A_EN, op_or);
+ }
+ }
+
+ /* Save new DTIM period */
+ reg = piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD) & ~MAC_DTIM_PERIOD_MASK;
+ reg |= conf->dtim_period << MAC_DTIM_PERIOD_SHIFT;
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, reg, op_write);
+ reg = piperp->ac->rd_reg(piperp, MAC_CFP_ATIM) & ~MAC_DTIM_CFP_MASK;
+ piperp->ps.beacon_int = conf->beacon_int;
+ reg |= conf->beacon_int << 16;
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, reg, op_write);
+}
+
+/*
+ * Use the SSL library routines to expand the AES key.
+ */
+static int piper_expand_aes_key(struct ieee80211_key_conf *key,
+ u32 *expandedKey)
+{
+ struct crypto_aes_ctx aes;
+ int ret;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (key->keylen != AES_KEYSIZE_128)
+ return -EOPNOTSUPP;
+
+ ret = crypto_aes_expand_key(&aes, key->key, key->keylen);
+ if (ret)
+ return -EOPNOTSUPP;
+
+ memcpy(expandedKey, aes.key_enc, EXPANDED_KEY_LENGTH);
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this routine to set a new encryption key, or to
+ * retire an old one. We support H/W AES encryption/decryption, so
+ * save the AES related keys.
+ */
+static int piper_hw_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ const u8 *local_address, const u8 *address,
+ struct ieee80211_key_conf *key)
+{
+ struct piper_priv *piperp = hw->priv;
+ int ret = 0;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if ((key->alg != ALG_CCMP) || (key->keyidx >= PIPER_MAX_KEYS)) {
+ /*
+ * If we come here, then mac80211 was trying to set a key for some
+ * algorithm other than AES, or trying to set a key index greater
+ * than 3. We only support AES, and only 4 keys.
+ */
+ ret = -EOPNOTSUPP;
+ goto set_key_error;
+ }
+ key->hw_key_idx = key->keyidx;
+
+ if (cmd == SET_KEY) {
+ ret = piper_expand_aes_key(key, piperp->key[key->keyidx].expandedKey);
+ if (ret)
+ goto set_key_error;
+
+ if (!piperp->key[key->keyidx].valid)
+ piperp->aes_key_count++;
+ piperp->key[key->keyidx].txPn = 0;
+ piperp->key[key->keyidx].rxPn = 0;
+ piperp->key[key->keyidx].valid = (ret == 0);
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ } else {
+ /* disable key */
+ if (piperp->key[key->keyidx].valid)
+ piperp->aes_key_count--;
+ piperp->key[key->keyidx].valid = false;
+ }
+
+ if (piperp->aes_key_count > 0)
+ piperp->ac->wr_reg(piperp, MAC_CTL, ~MAC_CTL_AES_DISABLE, op_and);
+ else
+ piperp->ac->wr_reg(piperp, MAC_CTL, MAC_CTL_AES_DISABLE, op_or);
+
+set_key_error:
+ if (ret)
+ dprintk(DVERBOSE, "unable to set AES key\n");
+
+ return ret;
+}
+
+/*
+ * mac80211 calls this routine to determine if we transmitted the
+ * last beacon. Unfortunately, we can't tell for sure. We give
+ * mac80211 our best guess.
+ */
+static int piper_hw_tx_last_beacon(struct ieee80211_hw *hw)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ return piperp->beacon.weSentLastOne ? 1 : 0;
+}
+
+static int piper_get_tx_stats(struct ieee80211_hw *hw,
+ struct ieee80211_tx_queue_stats *stats)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ if (stats)
+ memcpy(stats, &piperp->pstats.tx_queue, sizeof(piperp->pstats.tx_queue));
+
+ return 0;
+}
+
+static int piper_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ if (stats)
+ memcpy(stats, &piperp->pstats.ll_stats, sizeof(piperp->pstats.ll_stats));
+
+ return 0;
+}
+
+const struct ieee80211_ops hw_ops = {
+ .tx = piper_hw_tx,
+ .start = piper_hw_start,
+ .stop = piper_hw_stop,
+ .add_interface = piper_hw_add_intf,
+ .remove_interface = piper_hw_rm_intf,
+ .config = piper_config,
+ .config_interface = piper_hw_config_intf,
+ .configure_filter = piper_hw_config_filter,
+ .bss_info_changed = piper_hw_bss_changed,
+ .tx_last_beacon = piper_hw_tx_last_beacon,
+ .set_key = piper_hw_set_key,
+ .get_tx_stats = piper_get_tx_stats,
+ .get_stats = piper_get_stats,
+};
+
+/*
+ * This routine is called by the probe routine to allocate the
+ * data structure we need to communicate with mac80211.
+ */
+int piper_alloc_hw(struct piper_priv **priv, size_t priv_sz)
+{
+ struct piper_priv *piperp;
+ struct ieee80211_hw *hw;
+
+ hw = ieee80211_alloc_hw(priv_sz, &hw_ops);
+ if (!hw)
+ return -ENOMEM;
+
+ hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS
+ | IEEE80211_HW_SIGNAL_DBM
+ | IEEE80211_HW_NOISE_DBM
+ | IEEE80211_HW_SPECTRUM_MGMT
+ | IEEE80211_HW_NO_STACK_DYNAMIC_PS
+#if !WANT_SHORT_PREAMBLE_SUPPORT
+ | IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE
+#endif
+ /* | IEEE80211_HW_SPECTRUM_MGMT TODO: Turn this on when we are ready*/;
+
+ hw->queues = 1;
+ hw->ampdu_queues = 0;
+ hw->extra_tx_headroom = 4 + sizeof(struct ofdm_hdr);
+ piperp = hw->priv;
+ *priv = piperp;
+ piperp->pstats.tx_queue.len = 0;
+ piperp->pstats.tx_queue.limit = 1;
+ piperp->pstats.tx_queue.count = 0;
+ piperp->areWeAssociated = false;
+ memset(&piperp->pstats.ll_stats, 0, sizeof(piperp->pstats.ll_stats));
+ piperp->hw = hw;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(piper_alloc_hw);
+
+/*
+ * This routine is called by the remove function to free the memory
+ * allocated by piper_alloc_hw.
+ */
+void piper_free_hw(struct piper_priv *piperp)
+{
+ ieee80211_free_hw(piperp->hw);
+}
+EXPORT_SYMBOL_GPL(piper_free_hw);
+
+/*
+ * This routine is called by the probe routine to register
+ * with mac80211.
+ */
+int piper_register_hw(struct piper_priv *priv, struct device *dev,
+ struct digi_rf_ops *rf)
+{
+ struct ieee80211_hw *hw = priv->hw;
+ u8 macaddr[8];
+ u32 temp;
+ int i, ret;
+
+ dprintk(DVVERBOSE, "\n");
+
+ priv->rf = rf;
+ for (i = 0; i < rf->n_bands; i++) {
+ enum ieee80211_band b = rf->bands[i].band;
+ hw->wiphy->bands[b] = &rf->bands[i];
+ }
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_ADHOC)
+ | BIT(NL80211_IFTYPE_STATION)
+/* TODO: Enable this | BIT(NL80211_IFTYPE_MESH_POINT) */
+ ;
+ hw->channel_change_time = rf->channelChangeTime;
+ hw->vif_data_size = 0;
+ hw->sta_data_size = 0;
+ hw->max_rates = IEEE80211_TX_MAX_RATES;
+ hw->max_rate_tries = 5; /* completely arbitrary, and apparently ignored by the rate algorithm */
+ hw->max_signal = rf->maxSignal;
+ hw->max_listen_interval = 10; /* I don't think APs will work with values larger than 4 actually */
+
+ SET_IEEE80211_DEV(hw, dev);
+
+ temp = cpu_to_be32(priv->ac->rd_reg(priv, MAC_STA_ID0));
+ memcpy(macaddr, &temp, sizeof(temp));
+ temp = cpu_to_be32(priv->ac->rd_reg(priv, MAC_STA_ID1));
+ memcpy(&macaddr[4], &temp, sizeof(temp));
+ SET_IEEE80211_PERM_ADDR(hw, macaddr);
+
+ if ((ret = ieee80211_register_hw(hw)) != 0) {
+ dprintk(DERROR, "unable to register ieee80211 hw\n");
+ goto error;
+ }
+
+ printk(KERN_INFO PIPER_DRIVER_NAME ": registered as %s\n",
+ wiphy_name(hw->wiphy));
+
+ init_timer(&priv->led_timer);
+ priv->led_state = led_shutdown;
+ priv->led_timer.function = led_timer_fn;
+ priv->led_timer.data = (unsigned long) priv;
+ priv->led_timer.expires = jiffies + LED_TIMER_RATE;
+ add_timer(&priv->led_timer);
+
+error:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(piper_register_hw);
+
+
+void piper_unregister_hw(struct piper_priv *piperp)
+{
+ dprintk(DVVERBOSE, "\n");
+ del_timer_sync(&piperp->led_timer);
+ piper_set_status_led(piperp->hw, led_shutdown);
+ ieee80211_unregister_hw(piperp->hw);
+}
+EXPORT_SYMBOL_GPL(piper_unregister_hw);
+
+
+MODULE_DESCRIPTION("Digi Piper WLAN core");
+MODULE_AUTHOR("contact support@digi.com for information about this code");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/digiPiper/digiPs.c b/drivers/net/wireless/digiPiper/digiPs.c
new file mode 100644
index 000000000000..c6ae6b0b656a
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiPs.c
@@ -0,0 +1,1164 @@
+/*
+ * digiPs.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains the routines that are related to transmitting
+ * frames.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "airoha.h"
+#include "digiPs.h"
+
+/*
+ * Macro converts milliseconds to HZ.
+ *
+ * TODO: Look for standard Linux version of this.
+ */
+#define MILLS_TO_JIFFIES(x) ((((x)*HZ) + 500) / 1000)
+
+
+
+
+#define MINIMUM_SLEEP_PERIOD (20)
+
+/*
+ * Amount of time before shutdown deadline to start the shutdown process. This
+ * should allow enough time to get one last frame out, which will be the
+ * null-data frame notifying the AP that we are shutting down.
+ */
+#define PS_TRANSMITTER_SHUTDOWN_MS (10)
+
+/*
+ * Amount of time we will wake up before the next beacon. We try to wake up before
+ * the next beacon so that we don't miss it.
+ */
+#define PS_WAKE_BEFORE_BEACON_MS (20)
+
+/*
+ * Minimum amount of time we we keep awake for.
+ */
+#define PS_MINUMUM_POWER_UP_PERIOD_MS (10)
+
+/*
+ * Minimum amount of time we will sleep. If we will end up sleeping
+ * for less than this, then don't go to sleep.
+ */
+#define PS_MINIMUM_SLEEP_TIME (10)
+
+/*
+ * Length of one tick of the transmit clean up timer in ms. This is
+ * the minimum amount of time we will sleep for.
+ */
+#define PS_TRANSMIT_TIMER_TICK_MS ((1000/HZ) ? (1000/HZ) : 1)
+
+/*
+ * Length of time we will wait past the expected arrival of a beacon before assuming
+ * that we missed it.
+ */
+#define PS_BEACON_TIMEOUT_MS (40)
+
+
+/*
+ * Minimum beacon interval we will support for duty cycling. There is so much overhead
+ * in duty cycling that it doesn't make sense to do it for short beacon intervals.
+ */
+#define PS_MINIMUM_BEACON_INT (100)
+
+/*
+ * Amount of time we will pause duty cycling for after receiving an event that suggests
+ * wpa_supplicant is attempting to reassociate with an AP.
+ */
+#define PS_SCAN_DELAY (5000)
+
+
+// Powersave register index
+#define INDX_GEN_CONTROL 0 // General control
+#define INDX_GEN_STATUS 1 // General status
+#define INDX_RSSI_AES 2 // RSSI and AES status
+#define INDX_INTR_MASK 3 // Interrupt mask
+#define INDX_SPI_CTRL 4 // RF SPI control
+#define INDX_CONF1 5 // Configuration 1
+#define INDX_CONF2 6 // Configuration 2
+#define INDX_AES_MODE 7 // ARS mode
+#define INDX_OUT_CTRL 8 // Output control
+#define INDX_MAC_CONTROL 9 // MAC control
+#define INDX_STAID_1 10 // first part of stations ID
+#define INDX_STAID_2 11 // 2nd part of station ID
+#define INDX_BSSID_1 12 // 1st part of BSS ID
+#define INDX_BSSID_2 13 // 2nd part of BSS ID
+#define INDX_BRS_SSID 14 // BRS mask and SSID length
+#define INDX_BACKOFF 15 // backoff
+#define INDX_DTIM_LISTEN 16 // DTIM perido and listen interval
+#define INDX_BEACON_INT 17 // beacon interval
+#define INDX_MAC_CTL 18 // MAC control register
+#define INDX_BEACON_MASK 19 // beacon mask and backoff
+#define INDX_TOTAL 20
+
+static u32 savedRegs[INDX_TOTAL]; // Used to save registers for sleep mode
+
+
+/*
+ * TODO: Delete this.
+ */
+struct ps_stats {
+ unsigned int modeStart;
+ unsigned int cycleStart;
+ unsigned int receivedBeacons;
+ unsigned int missedBeacons;
+ unsigned int jiffiesOn;
+ unsigned int jiffiesOff;
+} stats;
+
+
+
+static void ps_free_frame(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ if (skb) {
+ dev_kfree_skb(skb);
+ piperp->ps.frames_pending--;
+ }
+}
+
+
+
+
+
+#define ACK_SIZE (14) /* length of ACK in bytes */
+
+// Length (in usecs) of a MAC frame of bytes at rate (in 500kbps units)
+// not including SIFS and PLCP preamble/header
+#define CCK_DURATION(bytes, rate) ((16*(bytes)+(rate)-1)/(rate))
+
+#define USE_SHORTPREAMBLE(is_1_Mbit, use_short_preamble) ((!is_1_Mbit) & use_short_preamble)
+
+// Length (in usecs) of SIFS and PLCP preamble/header.
+#define PRE_LEN(is_1_Mbit, use_short_preamble) (USE_SHORTPREAMBLE(is_1_Mbit, use_short_preamble) ? 106 : 202)
+
+// Duration (in usecs) of an OFDM frame at rate (in 500kbps units)
+// including SIFS and PLCP preamble/header
+#define OFDM_DURATION(bytes, rate) (36 + 4*((4*(bytes)+(rate)+10)/(rate)))
+
+static unsigned int getRateIndex(struct piper_priv *piperp)
+{
+ unsigned int rates = piperp->ac->rd_reg(piperp, MAC_SSID_LEN);
+ unsigned int result = AIROHA_LOWEST_OFDM_RATE_INDEX;
+
+ if (piperp->rf->getBand(piperp->channel) == IEEE80211_BAND_2GHZ) {
+ if ((rates & MAC_PSK_BRS_MASK) != 0) {
+ result = AIROHA_LOWEST_PSK_RATE_INDEX;
+ }
+ }
+
+ return result;
+}
+
+static int getAckDuration(struct piper_priv *piperp)
+{
+ bool is_1_Mbit = (getRateIndex(piperp) == AIROHA_LOWEST_PSK_RATE_INDEX);
+ int duration = 0;
+
+ if (is_1_Mbit) {
+ duration = CCK_DURATION(ACK_SIZE, 1);
+ } else {
+ duration = OFDM_DURATION(ACK_SIZE, 6);
+ }
+
+ duration += PRE_LEN(is_1_Mbit, piperp->use_short_preamble);
+
+ return duration;
+}
+
+
+/*
+ * This function is used to notify the AP about the current state of
+ * power save. One of the bits in the 802.11 header field is set to
+ * indicate the status of power save. This bit is actually set appropriately
+ * for all frames sent, we just send a null data frame just to make
+ * sure something is sent to the AP in a reasonable amount of time.
+ */
+/*
+ * Possible values for is_power_management_on argument
+ */
+#define POWERING_DOWN (true)
+#define POWERED_UP (false)
+void piper_sendNullDataFrame(struct piper_priv *piperp, bool is_power_management_on)
+{
+ struct sk_buff *skb = NULL;
+ _80211HeaderType *header;
+ struct ieee80211_tx_info *tx_info;
+
+ skb =
+ __dev_alloc_skb(sizeof(_80211HeaderType) +
+ piperp->hw->extra_tx_headroom, GFP_ATOMIC);
+ if (skb == NULL)
+ goto piper_sendNullDataFrame_Exit;
+
+ tx_info = (struct ieee80211_tx_info *) skb->cb;
+
+ skb_reserve(skb, piperp->hw->extra_tx_headroom);
+ header = (_80211HeaderType *) skb_put(skb, sizeof(_80211HeaderType));
+ memset(header, 0, sizeof(*header));
+ header->fc.type = TYPE_NULL_DATA;
+ header->fc.pwrMgt = is_power_management_on;
+ header->duration = getAckDuration(piperp);
+ memcpy(header->addr1, piperp->bssid, sizeof(header->addr1));
+ memcpy(header->addr2, piperp->pdata->macaddr, sizeof(header->addr2));
+ memcpy(header->addr3, piperp->bssid, sizeof(header->addr3));
+
+ tx_info->flags = IEEE80211_TX_CTL_ASSIGN_SEQ | IEEE80211_TX_CTL_FIRST_FRAGMENT;
+ tx_info->band = piperp->rf->getBand(piperp->channel);
+ tx_info->antenna_sel_tx = 1; /* actually this is ignored for now */
+ tx_info->control.rates[0].idx = 0;
+ tx_info->control.rates[0].count = 2; /* no retries. Don't tie us up waiting for an ACK */
+ tx_info->control.rates[0].flags = 0;
+ tx_info->control.rates[1].idx = -1;
+ tx_info->control.rts_cts_rate_idx = -1;
+ piperp->ps.frames_pending++;
+
+ if (piper_hw_tx_private(piperp->hw, skb, ps_free_frame) != 0) {
+ /* printk(KERN_ERR
+ "piper_hw_tx() failed unexpectedly when sending null data frame.\n"); */
+ ps_free_frame(piperp->hw, skb);
+ }
+
+piper_sendNullDataFrame_Exit:
+ return;
+}
+
+
+
+
+#define RESET_PIPER (1) /* must be set to 1 (0 case is only for debugging)*/
+#define PS_DONT_FORCE (false) /* set force to this value if we want to be safe */
+#define PS_FORCE_POWER_DOWN (true) /* set force to this value to shut down the H/W reguardless*/
+/*
+ * This routine shuts down Piper and the Airoha transceiver. First we check to
+ * make sure the driver and H/W are idle. Then we save the state of the H/W.
+ * Then we shut down the Airoha and place piper into reset.
+ */
+int piper_MacEnterSleepMode(struct piper_priv *piperp, bool force)
+{
+ /*
+ * Interrupts are already disabled when we get here.
+ */
+
+ if (piperp->ps.poweredDown)
+ return 0;
+
+ savedRegs[INDX_INTR_MASK] = piperp->ac->rd_reg(piperp, BB_IRQ_MASK);
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);
+
+ if (!force) {
+ if ( (piperp->ps.rxTaskletRunning)
+ || ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY))
+ || ( (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL)
+ & BB_GENERAL_CTL_TX_FIFO_EMPTY) == 0)
+ || (piperp->tx_tasklet_running)
+ || ( (piperp->ac->rd_reg(piperp, BB_GENERAL_STAT)
+ & BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0)
+ || (piperp->ac->rd_reg(piperp, BB_IRQ_STAT) & savedRegs[INDX_INTR_MASK])) {
+
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, savedRegs[INDX_INTR_MASK], op_write);
+ return -1;
+ }
+ }
+
+ disable_irq(piperp->irq);
+
+ savedRegs[INDX_GEN_CONTROL] = piperp->ac->rd_reg(piperp, BB_GENERAL_CTL);
+ savedRegs[INDX_GEN_STATUS] = piperp->ac->rd_reg(piperp, BB_GENERAL_STAT);
+ savedRegs[INDX_RSSI_AES] = piperp->ac->rd_reg(piperp, BB_RSSI) & ~BB_RSSI_EAS_BUSY;
+ savedRegs[INDX_SPI_CTRL] = piperp->ac->rd_reg(piperp, BB_SPI_CTRL);
+ savedRegs[INDX_CONF1] = piperp->ac->rd_reg(piperp, BB_TRACK_CONTROL);
+ savedRegs[INDX_CONF2] = piperp->ac->rd_reg(piperp, BB_CONF_2);
+ savedRegs[INDX_OUT_CTRL] = piperp->ac->rd_reg(piperp, BB_OUTPUT_CONTROL);
+ savedRegs[INDX_MAC_CONTROL] = piperp->ac->rd_reg(piperp, MAC_CTL);
+
+ savedRegs[INDX_STAID_1] = piperp->ac->rd_reg(piperp, MAC_STA_ID0);
+ savedRegs[INDX_STAID_2] = piperp->ac->rd_reg(piperp, MAC_STA_ID1);
+ savedRegs[INDX_BSSID_1] = piperp->ac->rd_reg(piperp, MAC_BSS_ID0);
+ savedRegs[INDX_BSSID_2] = piperp->ac->rd_reg(piperp, MAC_BSS_ID1);
+ savedRegs[INDX_BRS_SSID] = piperp->ac->rd_reg(piperp, MAC_SSID_LEN);
+ savedRegs[INDX_BACKOFF] = piperp->ac->rd_reg(piperp, MAC_BACKOFF);
+ savedRegs[INDX_DTIM_LISTEN] = piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD);
+ savedRegs[INDX_BEACON_INT] = piperp->ac->rd_reg(piperp, MAC_CFP_ATIM);
+ savedRegs[INDX_MAC_CTL] = piperp->ac->rd_reg(piperp, MAC_CTL);
+ savedRegs[INDX_BEACON_MASK] = piperp->ac->rd_reg(piperp, MAC_BEACON_FILT);
+
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and); //disable receiving
+ piperp->ac->wr_reg(piperp, MAC_CTL, 0, op_write);
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);
+
+#if RESET_PIPER
+ // Power down the airoha transceiver
+ piperp->rf->power_on(piperp->hw, false);
+ udelay(10);
+ // hold the transceiver in reset mode
+ if (piperp->pdata->reset)
+ piperp->pdata->reset(piperp, 1);
+#endif
+ stats.jiffiesOn += jiffies - stats.cycleStart;
+ stats.cycleStart = jiffies;
+ piperp->ps.poweredDown = true;
+
+ return 0;
+}
+
+
+#define PS_NO_SPIKE_SUPPRESSION (false) /* want_spike_suppression = don't want spike suppression*/
+#define PS_WANT_SPIKE_SUPPRESSION (true) /* want_spike_suppression = do spike suppression*/
+/*
+ * Turn the H/W back on after it was shutdown with piper_MacEnterSleepMode.
+ *
+ * 1. Power up the hardware.
+ * 2. Perform the spike suppression routine if desired. The spike suppression
+ * routine resyncs the clocks in Piper in order to prevent us from generating
+ * noise spikes at 1 and 2 MHz. Unfortunately it takes an indeterminate amount
+ * of time, so we normally don't do it and just make sure we do not send at
+ * at those data rates while duty cycling.
+ * 3. Set the channel. The Airoha was shut off so we have to set the channel
+ * again.
+ * 4. Restore the state of piper registers.
+ * 5. Zero out and reset the transmitter FIFO. Mike Schaffner claims this should
+ * not be necessary, but we seem to run into trouble when we don't do it.
+ * 6. Restore the interrupts.
+ */
+void piper_MacEnterActiveMode(struct piper_priv *piperp, bool want_spike_suppression)
+{
+ int i;
+// #define WANT_DEBUG
+#ifdef WANT_DEBUG
+ static unsigned int run = 0;
+#endif
+
+#if RESET_PIPER
+
+ if (piperp->pdata->reset) {
+#ifdef WANT_DEBUG
+ if (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_FULL) {
+ printk(KERN_ERR "**** While in reset, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+ if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ printk(KERN_ERR "**** While in reset AES busy set, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+#endif
+ piperp->pdata->reset(piperp, 0);
+ udelay(10);
+
+#ifdef WANT_DEBUG
+ if (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_FULL) {
+ printk(KERN_ERR "**** After reset, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+ if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ printk(KERN_ERR "**** After reset AES busy set, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+ run++;
+#endif
+ piperp->rf->power_on(piperp->hw, true);
+ mdelay(1);
+ piper_spike_suppression(piperp, want_spike_suppression);
+ }
+#endif
+
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);
+
+
+#if RESET_PIPER
+ piperp->rf->set_chan_no_rx(piperp->hw, piperp->channel);
+#endif
+
+ // store the registers back
+
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, 0x30000000, op_write);
+ piperp->ac->wr_reg(piperp, BB_RSSI, savedRegs[INDX_RSSI_AES] & ~BB_RSSI_EAS_BUSY, op_write);
+
+// piperp->ac->wr_reg(piperp, BB_IRQ_MASK, savedRegs[INDX_INTR_MASK], op_write);
+ piperp->ac->wr_reg(piperp, BB_SPI_CTRL, savedRegs[INDX_SPI_CTRL], op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, savedRegs[INDX_CONF1], op_write);
+ piperp->ac->wr_reg(piperp, BB_CONF_2, savedRegs[INDX_CONF2], op_write);
+ piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, savedRegs[INDX_OUT_CTRL], op_write);
+ piperp->ac->wr_reg(piperp, MAC_CTL, savedRegs[INDX_MAC_CONTROL], op_write);
+
+ piperp->ac->wr_reg(piperp, MAC_STA_ID0, savedRegs[INDX_STAID_1], op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA_ID1, savedRegs[INDX_STAID_2], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID0, savedRegs[INDX_BSSID_1], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID1, savedRegs[INDX_BSSID_2], op_write);
+ piperp->ac->wr_reg(piperp, MAC_SSID_LEN, savedRegs[INDX_BRS_SSID], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BACKOFF, savedRegs[INDX_BACKOFF], op_write);
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, savedRegs[INDX_DTIM_LISTEN], op_write);
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, savedRegs[INDX_BEACON_INT], op_write);
+ piperp->ac->wr_reg(piperp, MAC_CTL, savedRegs[INDX_MAC_CTL], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BEACON_FILT, savedRegs[INDX_BEACON_MASK], op_write);
+
+//****
+ // set bit-11 in the general control register to a 1 to start the processors
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_MAC_ASSIST_ENABLE, op_or);
+
+ // set the TX-hold bit
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720080, op_write);
+
+ // clear the TX-FIFO memory
+ for (i = 0; i < 448; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write);
+
+ // clear RX-FIFO memory
+ for (i = 0; i < 512; i++)
+ piperp->ac->rd_reg(piperp, BB_DATA_FIFO);
+
+ // reset the TX-FIFO and RX-FIFO
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x377200E0, op_write);
+
+
+ // release the TX-hold and reset
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720000, op_write);
+
+
+//***
+ /*
+ * Reset the interrupt mask. We could have been receiving a frame when we
+ * powered down. This could cause us to store the wrong mask, so we want
+ * to make sure we enabe the RX interrupts. However, we should not have the
+ * TX interrupts enabled when we come out of power save mode.
+ */
+ udelay(50);
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);
+ piperp->clear_irq_mask_bit(piperp, 0xffffffff);
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_RX_OVERRUN | BB_IRQ_MASK_RX_FIFO);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ (savedRegs[INDX_GEN_CONTROL] | 0x37000000 |
+ BB_GENERAL_CTL_RX_EN), op_write);
+
+ stats.jiffiesOff += jiffies - stats.cycleStart;
+ stats.cycleStart = jiffies;
+
+ /* TODO, this is a temporary workaround and should be better analyzed in future.
+ * The problem is that the general power save code is not synchronized with the
+ * dynamic PW and this is causing that, the following line, unbalances the
+ * piper wireless irq */
+ if (piperp->ps.poweredDown != false)
+ enable_irq(piperp->irq);
+
+ piperp->ps.poweredDown = false;
+}
+
+
+
+
+/*
+ * So what crazy thing are we doing here? Well, Piper has a bug where it
+ * can send noise spikes at 1 Mbps and 2 Mbps if it is powered up without
+ * running a special spike suppression routine. The spike suppression code
+ * takes an average of 30 ms, and I have timed it taking as long as 300 ms.
+ * This is not something you want to use for duty cycling. The solution is
+ * to avoid sending at those two rates. After the transmit routine determines
+ * the rate mac80211 specified, it will call us and we will decide whether
+ * we like that rate. If it is one of our bad rates, then we will bump it
+ * up to a good rate.
+ */
+struct ieee80211_rate *piper_ps_check_rate(struct piper_priv *piperp,
+ struct ieee80211_rate *rate)
+{
+#define BAD_RATE_1MBPS (10)
+#define BAD_RATE_2MBPS (20)
+ if ((piperp->ps.mode == PS_MODE_LOW_POWER) && (rate != NULL)) {
+ if ((rate->bitrate == BAD_RATE_1MBPS)
+ || (rate->bitrate == BAD_RATE_2MBPS)) {
+ rate =
+ (struct ieee80211_rate *) piperp->rf->
+ getRate(AIROHA_55_MBPS_RATE_INDEX);
+ }
+ }
+
+ return rate;
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_check_rate);
+
+
+
+/*
+ * This routine restarts the transmitter after powering back
+ * up, or failing to power down.
+ *
+ * 1) Clear the power management bit so that frames will be
+ * sent indicating that we are poweredup.
+ * 2) Set the flag to allow transmits again.
+ * 3) If we stopped the mac80211 transmitter queue, then start
+ * it back up again.
+ * 4) Notify the AP that we are awake by sending a null-data frame
+ * with the power management bit clear.
+ */
+static void ps_resume_transmits(struct piper_priv *piperp)
+{
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ piper_sendNullDataFrame(piperp, POWERED_UP);
+ ieee80211_wake_queues(piperp->hw);
+}
+
+
+
+
+
+
+/*
+ * This routine sets an event timer. The ps_state_machine routine
+ * will be executed when the event timer expires.
+ */
+static void ps_set_timer_event(struct piper_priv *piperp,
+ enum piper_ps_event next_event,
+ unsigned int timeout_ms)
+{
+ unsigned int delay_in_jiffies = MILLS_TO_JIFFIES(timeout_ms);
+
+ if (delay_in_jiffies == 0)
+ delay_in_jiffies++;
+
+ del_timer_sync(&piperp->ps.timer);
+ piperp->ps.this_event = next_event;
+ piperp->ps.timer.expires = delay_in_jiffies + jiffies;
+ add_timer(&piperp->ps.timer);
+}
+
+
+/*
+ * This routine cancels an event timer set by ps_set_timer_event.
+ */
+static void ps_cancel_timer_event(struct piper_priv *piperp)
+{
+ del_timer_sync(&piperp->ps.timer);
+}
+
+
+#define WANT_STATE_MACHINE_DEBUG (0)
+#if WANT_STATE_MACHINE_DEBUG
+
+struct event_record {
+ enum piper_ps_event event;
+ enum piper_ps_state state;
+};
+
+#define MAX_EVENTS_RECORDED (15)
+static unsigned int debug_events_index = 0;
+static struct event_record debug_events[MAX_EVENTS_RECORDED];
+
+void debug_track_event(enum piper_ps_event event, enum piper_ps_state state)
+{
+ debug_events[debug_events_index].event = event;
+ debug_events[debug_events_index].state = state;
+ if (debug_events_index == MAX_EVENTS_RECORDED) {
+ unsigned int i;
+
+ printk(KERN_ERR);
+ for (i = 0; i < MAX_EVENTS_RECORDED; i++) {
+ printk("(%d,%d)", debug_events[i].event,
+ debug_events[i].state);
+ }
+ printk("\n");
+ debug_events_index = 0;
+ } else {
+ debug_events_index++;
+ }
+}
+
+
+#else
+ #define debug_track_event(event, state)
+#endif
+
+/*
+ * This is the entry point into the duty cycle state machine. It may be called
+ * by:
+ *
+ * 1) piper_ps_handle_beacon when a beacon frame is received.
+ * 2) the receiver task when we receive the ACK for the last pending frame
+ * when we are waiting to power down.
+ * 3) by ps_timer for many timer events.
+ */
+static void ps_state_machine(struct piper_priv *piperp, enum piper_ps_event event)
+{
+ unsigned long flags;
+
+ debug_track_event(event, piperp->ps.state);
+
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+
+
+ switch (event) {
+ case PS_EVENT_BEACON_RECEIVED:
+ /*
+ * We just received a beacon. This is really the driving event in this state
+ * machine. Everything else is synchronized from it.
+ *
+ * We know we are powered up because we just received the beacon. The normal
+ * control path is to set a timer which will expire when we need to start
+ * preparations for powering down again.
+ */
+ ps_cancel_timer_event(piperp); /* cancel missed beacon timer*/
+ stats.receivedBeacons++;
+ if ( (!piperp->areWeAssociated)
+ || (piperp->ps.beacon_int < PS_MINIMUM_BEACON_INT)
+ || (piperp->ps.scan_timer != 0)) {
+ /*
+ * Don't duty cyle while we are trying to associate.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ if (piperp->ps.scan_timer) {
+ piperp->ps.scan_timer--;
+ }
+ break;
+ }
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_BEACON) {
+ int timeout;
+ /*
+ * Calculate amount of time to sleep.
+ */
+ piperp->ps.sleep_time = (piperp->ps.beacon_int * (100 - piperp->power_duty)) / 100;
+
+ /*
+ * Now figure out how long we have before it's time to go to sleep. We
+ * have to wake up at least PS_WAKE_BEFORE_BEACON_MS before we expect to
+ * receive the next beacon, and we need to start powering down at least
+ * PS_TRANSMITTER_SHUTDOWN_MS ahead of time.
+ */
+ timeout = piperp->ps.beacon_int - (piperp->ps.sleep_time + PS_TRANSMITTER_SHUTDOWN_MS + PS_WAKE_BEFORE_BEACON_MS);
+ if ((timeout > 0) && (piperp->ps.sleep_time > PS_MINIMUM_SLEEP_TIME)) {
+ /*
+ * If there is enough time left that it makes sense to duty
+ * cycle, then program the timer and advance to the next state.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT;
+ ps_set_timer_event(piperp, PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED, timeout);
+
+ break;
+ }
+ } else {
+ if ( (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)
+ || (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT)) {
+ ps_resume_transmits(piperp);
+ }
+ digi_dbg("*** Beacon event in state %d.\n", piperp->ps.state);
+ }
+ /*
+ * We will come here if we were in the wrong state when we received the
+ * beacon, or if the duty cycle is too short.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
+ break;
+ case PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED:
+ /*
+ * This event is hit when it's time to start powering down. Unfortunately, this is
+ * not a simple thing to do. The first things to do are:
+ *
+ * 1. Set the power save on flag. This will cause any transmit frame to be
+ * sent with the power management bit set.
+ * 2. Stop the mac80211 layer from sending us anymore frames.
+ * 3. Signal the AP that we are powering down by sending a null-data frame with the
+ * power management bit set.
+ */
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT) {
+ if (piperp->ps.scan_timer) {
+ /*
+ * Don't shut down if we are scanning.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ break;
+ }
+ piperp->ps.power_management = POWERING_DOWN;
+ piperp->ps.allowTransmits = false;
+ ieee80211_stop_queues(piperp->hw);
+ piperp->ps.stopped_tx_queues = true;
+ piper_sendNullDataFrame(piperp, POWERING_DOWN);
+ piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE;
+ ps_set_timer_event(piperp, PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
+ PS_TRANSMIT_TIMER_TICK_MS);
+ } else {
+ /*
+ * This should never happen (famous last words)
+ */
+ digi_dbg("** stop tx event, state = %d.\n", piperp->ps.state);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
+ }
+ break;
+ case PS_EVENT_TRANSMITTER_DONE:
+ /*
+ * This event is triggered when the receive task finishes processing the ACK
+ * from the null-data frame sent by the PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED event.
+ * We try to power down now.
+ */
+ if (piperp->ps.scan_timer == 0) {
+ if ( (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)
+ || (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT)) {
+ ps_cancel_timer_event(piperp); /* cancel transmitter done timeout timer*/
+ if (piper_MacEnterSleepMode(piperp, PS_DONT_FORCE) == 0) {
+ piperp->ps.state = PS_STATE_WAIT_FOR_WAKEUP_ALARM;
+ /*
+ * Note that the value PS_EVENT_TRANSMITTER_DONE_TIMER_TICK is
+ * updated as necessary by the PS_EVENT_TRANSMITTER_DONE_TIMER_TICK
+ * event to take into account the amount of time it took for the
+ * transmitter to finish sending the last frame.
+ */
+ ps_set_timer_event(piperp, PS_EVENT_WAKEUP, piperp->ps.sleep_time);
+ break;
+ }
+ } else {
+#ifdef WANT_DEBUG
+ printk(KERN_ERR "couldn't sleep, rxt=%d, AES busy = %d, txfifo=%d, txt=%d, rxfifo=%d\n",
+ (piperp->ps.rxTaskletRunning),((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) != 0),
+ ( (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL)& BB_GENERAL_CTL_TX_FIFO_EMPTY) == 0),
+ (piperp->tx_tasklet_running),
+ ( (piperp->ac->rd_reg(piperp, BB_GENERAL_STAT)
+ & BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0));
+#endif
+ digi_dbg("** PS_EVENT_TRANSMITTER_DONE event, but state == %d.\n", piperp->ps.state);
+ }
+ }
+ /*
+ * If we fall through to here, then either we are in the wrong state, or we were
+ * not able to power down the H/W.
+ */
+ ps_resume_transmits(piperp);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
+ break;
+ case PS_EVENT_TRANSMITTER_DONE_TIMER_TICK:
+ /*
+ * This event is triggered periodically while we are waiting for the
+ * transmitter to finish sending that last packet. We decrement
+ * piperp->ps.sleep_time (which is used by the PS_EVENT_TRANSMITTER_DONE
+ * event). If piperp->ps.sleep_time is still larger than our minimum
+ * required sleep time, then we just restart the timer.
+ */
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE) {
+ piperp->ps.sleep_time -= PS_TRANSMIT_TIMER_TICK_MS;
+ if (piperp->ps.sleep_time >= PS_MINIMUM_SLEEP_TIME) {
+ piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE;
+ ps_set_timer_event(piperp, PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
+ PS_TRANSMIT_TIMER_TICK_MS);
+ } else {
+ /*
+ * Transmitter did not shut down in time. Resume normal operations
+ * and stay awake until the next beacon.
+ */
+ ps_resume_transmits(piperp);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.sleep_time + PS_WAKE_BEFORE_BEACON_MS
+ + PS_BEACON_TIMEOUT_MS);
+ }
+ } else if (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT) {
+ piperp->ps.sleep_time -= PS_TRANSMIT_TIMER_TICK_MS;
+ /*
+ * The piper_ps_rx_task_exiting routine sets this state just before it
+ * releases the lock on ps.state and calls this event. If we ever
+ * come here, then the timer tick occurred just between the time
+ * piper_ps_rx_task_exiting released the lock and ps_state_machine
+ * reset it. Since the tx done event is in progress, we should ignore
+ * this tick.
+ */
+ break;
+ } else {
+ digi_dbg("** done tick in state %d.\n", piperp->ps.state);
+ }
+ break;
+ case PS_EVENT_WAKEUP:
+ /*
+ * This event is called when we have powered down and it is time
+ * to power back up again.
+ *
+ * 1) Power up the H/W.
+ * 2) Resume normal operations
+ * 3) Update our state.
+ * 4) Set a timeout for receiving the next beacom frame.
+ */
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_WAKEUP_ALARM) {
+ piper_MacEnterActiveMode(piperp, PS_NO_SPIKE_SUPPRESSION);
+ ps_resume_transmits(piperp);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ PS_BEACON_TIMEOUT_MS + PS_WAKE_BEFORE_BEACON_MS);
+ } else {
+ digi_dbg("** wake event in state %d.\n", piperp->ps.state);
+ }
+ break;
+ case PS_EVENT_MISSED_BEACON:
+ /*
+ * This event is called when we miss a beacon. For now just update
+ * our statistics.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ if ((piperp->areWeAssociated) && (piperp->ps.scan_timer == 0)) {
+ stats.missedBeacons++;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON, piperp->ps.beacon_int);
+ }
+ break;
+ default:
+ digi_dbg("**** ps_state_machine received unknown event %d.\n", event);
+ break;
+ }
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+
+}
+
+/*
+ * This routine is called by the receiver task when it exits. We use it to generate
+ * the PS_EVENT_TRANSMITTER_DONE event. The event signifies that the transmitter has
+ * sent our null-data frame and is idle. We determine this by checking frames_pending,
+ * while will be nonzero if a null-data frame is waiting to be sent, and the machine's
+ * state.
+ */
+void piper_ps_rx_task_exiting(struct piper_priv *piperp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+
+ if ( (piperp->ps.frames_pending == 0)
+ && (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)) {
+ /*
+ * We have a race condition between the transmitter done tick and
+ * this routine. So this routine changes the state to
+ * PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT before it releases the
+ * lock so that we don't get confused.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT;
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ ps_state_machine(piperp, PS_EVENT_TRANSMITTER_DONE);
+ } else {
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ }
+}
+
+/*
+ * Called when the event timer expires. Call the state machine to handle
+ * the event.
+ */
+static void ps_timer(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *) context;
+
+ ps_state_machine(piperp, piperp->ps.this_event);
+}
+
+
+
+/*
+ * This routine is called when we receive a beacon. We extract the beacon interval
+ * in case it has changed and then call the state machine.
+ */
+static void piper_ps_handle_beacon(struct piper_priv *piperp, struct sk_buff *skb)
+{
+#define BEACON_INT_LSB (8)
+#define BEACON_INT_MSB (9)
+ u32 beacon_int;
+ _80211HeaderType *header = (_80211HeaderType *) skb->data;
+ bool fromOurAp = piperp->areWeAssociated
+ && (memcmp(piperp->bssid, header->addr3, sizeof (header->addr3)) == 0);
+
+ /*
+ * mac80211 does not inform us when the beacon interval changes, so we have
+ * to read this information from the beacon ourselves.
+ */
+
+ if (fromOurAp) {
+ beacon_int = skb->data[sizeof(_80211HeaderType) + BEACON_INT_LSB];
+ beacon_int |= (skb->data[sizeof(_80211HeaderType) + BEACON_INT_MSB] << 8);
+ piperp->ps.beacon_int = beacon_int;
+
+ if (piperp->ps.mode == PS_MODE_LOW_POWER) {
+ ps_state_machine(piperp, PS_EVENT_BEACON_RECEIVED);
+ }
+ }
+}
+
+
+
+/*
+ * This routine is called when mac80211 starts doing things that might indicate it
+ * is attempting to scan or reassociate. Things like changing the channel or
+ * disassociating. When we receive an event like that, we stop duty cycling for
+ * a while since it may interfere with attempts to reassociate with an access point.
+ */
+void piper_ps_scan_event(struct piper_priv *piperp)
+{
+ (void) piperp;
+#if 0
+ /*
+ * It appears that pausing duty cycling during association events may actually
+ * worsen performance I suspect that either the AP or mac80211 is measuring
+ * our throughput and adjusting the load accordingly, and that momentary changes
+ * in performance caused by pausing duty cyling interfere with this.
+ *
+ * TODO: Consider removing this code. I left it in for now in case we decide
+ * to try it again, but if we're not going to use it, it just makes the
+ * driver more confusing and should be removed.
+ */
+ if (piperp->ps.beacon_int != 0) {
+ piperp->ps.scan_timer = PS_SCAN_DELAY / piperp->ps.beacon_int;
+ } else {
+ piperp->ps.scan_timer = PS_SCAN_DELAY / 100;
+ }
+#endif
+}
+
+
+
+/*
+ * This routine is called so we can process incoming frames. We do the
+ * handshaking to receive buffered frames in PS mode here.
+ */
+void piper_ps_process_receive_frame(struct piper_priv *piperp, struct sk_buff *skb)
+{
+ _80211HeaderType *header = (_80211HeaderType *) skb->data;
+
+ if (header->fc.type == TYPE_BEACON) {
+ piper_ps_handle_beacon(piperp, skb);
+ } else if ( (header->fc.type == TYPE_ASSOC_RESP)
+ || (header->fc.type == TYPE_REASSOC_RESP)
+ || (header->fc.type == TYPE_PROBE_RESP)
+ || (header->fc.type == TYPE_DISASSOC)
+ || (header->fc.type == TYPE_DEAUTH)
+ || (header->fc.type == TYPE_ACTION)) {
+ piper_ps_scan_event(piperp);
+ }
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_process_receive_frame);
+
+
+
+/*
+ * This function turns power save mode on or off.
+ */
+void piper_ps_set(struct piper_priv *piperp, bool powerSaveOn)
+{
+#define MAX_SHUTDOWN_TIMEOUT (100)
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+
+ piper_ps_scan_event(piperp);
+ if (powerSaveOn) {
+ if (piperp->ps.beacon_int >= PS_MINIMUM_BEACON_INT) {
+ if (piperp->ps.mode != PS_MODE_LOW_POWER) {
+ piperp->ps.aid = 0;
+ piperp->ps.mode = PS_MODE_LOW_POWER;
+ piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.poweredDown = false;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ stats.receivedBeacons = 0;
+ stats.missedBeacons = 0;
+ stats.modeStart = jiffies;
+ stats.cycleStart = jiffies;
+ stats.jiffiesOff = 0;
+ stats.jiffiesOn = 0;
+ piper_sendNullDataFrame(piperp, POWERED_UP);
+ /*
+ * Will start it the next time we receive a beacon.
+ */
+ }
+ } else {
+ printk(KERN_ERR
+ "\nUnable to set power save mode because the beacon \n"
+ "interval set on this access point less than 100ms.\n");
+ }
+ } else {
+ ps_cancel_timer_event(piperp);
+ if (piperp->ps.mode == PS_MODE_LOW_POWER) {
+ piperp->ps.mode = PS_MODE_FULL_POWER; /* stop duty cycle timer */
+ if (piperp->ps.poweredDown) {
+ /*
+ * If we were powered down, then power up and do the spike suppression.
+ */
+ piper_MacEnterActiveMode(piperp, PS_WANT_SPIKE_SUPPRESSION);
+ } else {
+ unsigned int timeout = 50;
+ int result;
+ /*
+ * If we branch here, then we were already powered up. You would think
+ * that we would be all set, but it's not that easy. Piper has a bug in
+ * it where we have to run a special spike suppression routine when we
+ * power it up. However, this routine takes an average of 30 ms to run,
+ * and I've see it take as long as 300 ms. This is not acceptable when
+ * we are duty cycling every 100 ms. To get around this, we do NOT do
+ * the spike suppression while duty cycling. Instead, we simply avoid
+ * transmitting at those rates which would cause spikes. Now, however,
+ * we are ending duty cycling and returning to normal operations so we
+ * have to do the spike suppression. Since we are powered up, the first
+ * thing to do is to power down.
+ */
+ if (piperp->ps.state != PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT) {
+ /*
+ * If we come here, then we did not happen to be trying to power down
+ * just as we got the command from mac80211, so we have to start
+ * the procedure. This is the normal case.
+ *
+ * 1. Set the power save on flag. This will cause frames to be
+ * transmitted with the power management bit on. The reason
+ * for doing that is to tell the AP to stop sending us frames.
+ * 2. Stop the mac80211 layer from sending us more frames by stopping
+ * the transmit queues.
+ * 3. Send a null-data frame to the AP with the power management bit
+ * set. This should cause it to stop sending us frames.
+ */
+ piperp->ps.power_management = POWERING_DOWN;
+ piperp->ps.allowTransmits = false;
+ ieee80211_stop_queues(piperp->hw);
+ piperp->ps.stopped_tx_queues = true;
+ piper_sendNullDataFrame(piperp, POWERING_DOWN);
+ }
+ /*
+ * Now wait for that last frame to go and and then shut down.
+ */
+ result = -1;
+ for (timeout = 0; (timeout < MAX_SHUTDOWN_TIMEOUT) && (result != 0); timeout++) {
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ mdelay(10);
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+ result = piper_MacEnterSleepMode(piperp, PS_DONT_FORCE);
+ }
+ if (result != 0) {
+ /*
+ * This is bad. For some reason we are not able to power down. We
+ * will try to force it now, but this may end up putting the driver
+ * or H/W into a bad state. However, we can't sit in the loop above
+ * forever either.
+ */
+#ifdef WANT_DEBUG
+ printk(KERN_ERR "Forcing Piper to power down\n");
+ printk(KERN_ERR "BB_RSSI_EAS_BUSY = %d\n", piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY);
+ printk(KERN_ERR "BB_GENERAL_CTL_TX_FIFO_EMPTY = %d\n",
+ piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_EMPTY);
+ printk(KERN_ERR "BB_GENERAL_STAT_RX_FIFO_EMPTY = %d\n",
+ piperp->ac->rd_reg(piperp, BB_GENERAL_STAT) & BB_GENERAL_STAT_RX_FIFO_EMPTY);
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+#endif
+ piper_MacEnterSleepMode(piperp, PS_FORCE_POWER_DOWN);
+ }
+ /*
+ * Wait a moment and then power the H/W back up and execute the spike suppression
+ * routine.
+ */
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ mdelay(30);
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+ piper_MacEnterActiveMode(piperp, PS_WANT_SPIKE_SUPPRESSION);
+ ps_resume_transmits(piperp);
+ }
+ stats.jiffiesOn += jiffies - stats.cycleStart;
+#define WANT_STATS (0)
+#if WANT_STATS
+ if ((piperp->ps.beacon_int != 0)
+ && ((jiffies - stats.modeStart) != 0)) {
+ printk(KERN_ERR
+ "jiffiesOff = %u, jiffiesOn = %u, total time = %lu\n",
+ stats.jiffiesOff, stats.jiffiesOn,
+ (jiffies - stats.modeStart));
+ printk(KERN_ERR
+ "Powered down %ld percent of the time.\n",
+ (stats.jiffiesOff * 100) / (jiffies - stats.modeStart));
+ printk(KERN_ERR
+ "Received %u of %lu beacons while in powersave mode.\n",
+ stats.receivedBeacons,
+ (jiffies -
+ stats.modeStart) /
+ MILLS_TO_JIFFIES(piperp->ps.beacon_int));
+ printk(KERN_ERR "received %d beacons, missed %d\n",
+ stats.receivedBeacons, stats.missedBeacons);
+ printk(KERN_ERR "allowTransmits = %d, stopped_tx_queues = %d, q_count = %d\n",
+ piperp->ps.allowTransmits, piperp->ps.stopped_tx_queues,
+ piperp->tx_queue_count);
+ if ((stats.receivedBeacons + stats.missedBeacons) != 0)
+ printk(KERN_ERR "%d%% beacons were missed\n",
+ (100 * stats.missedBeacons) / (stats.receivedBeacons + stats.missedBeacons));
+ }
+#endif
+ }
+ piperp->ps.aid = 0;
+ piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.poweredDown = false;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ ps_resume_transmits(piperp);
+ piper_sendNullDataFrame(piperp, POWERED_UP);
+ }
+
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_set);
+
+
+
+/*
+ * Called when driver is loaded. Initialize our context.
+ */
+void piper_ps_init(struct piper_priv *piperp)
+{
+ memset(&piperp->ps, 0, sizeof(piperp->ps));
+ piperp->ps.beacon_int = 100;
+ piperp->ps.aid = 0;
+ init_timer(&piperp->ps.timer);
+ piperp->ps.timer.function = ps_timer;
+ piperp->ps.timer.data = (unsigned long) piperp;
+ piperp->ps.mode = PS_MODE_FULL_POWER;
+ piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
+ spin_lock_init(&piperp->ps.lock);
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.poweredDown = false;
+ piperp->ps.rxTaskletRunning;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ piperp->ps.frames_pending = 0;
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_init);
+
+
+/*
+ * Called when driver is unloaded. Make sure the PS
+ * timer is shut down.
+ */
+void piper_ps_deinit(struct piper_priv *piperp)
+{
+ piper_ps_set(piperp, true);
+ piperp->ps.mode = PS_MODE_FULL_POWER;
+ del_timer_sync(&piperp->ps.timer);
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_deinit);
diff --git a/drivers/net/wireless/digiPiper/digiPs.h b/drivers/net/wireless/digiPiper/digiPs.h
new file mode 100644
index 000000000000..1feedd87e228
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiPs.h
@@ -0,0 +1,50 @@
+/*
+ * Header file for digiPs.c. Declares functions used for power save mode.
+ */
+
+#ifndef digiPs_h
+
+#define digiPS_h
+
+#include <net/mac80211.h>
+#include "pipermain.h"
+
+enum piper_ps_event {
+ PS_EVENT_BEACON_RECEIVED,
+ PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED,
+ PS_EVENT_TRANSMITTER_DONE,
+ PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
+ PS_EVENT_WAKEUP,
+ PS_EVENT_MISSED_BEACON
+};
+
+enum piper_ps_tx_completion_result {
+ PS_RETURN_SKB_TO_MAC80211,
+ PS_DONT_RETURN_SKB_TO_MAC80211
+};
+
+enum piper_ps_active_result {
+ PS_CONTINUE_TRANSMIT,
+ PS_STOP_TRANSMIT
+};
+
+
+/*
+ * Current version of mac80211 doesn't set power management bit in frame headers,
+ * so I guess we have to for now.
+ *
+ * TODO: See if we still have to do this in the next drop.
+ */
+#define piper_ps_set_header_flag(piperp, header) \
+ header->fc.pwrMgt = (piperp->ps.power_management)
+
+int piper_ps_active(struct piper_priv *piperp);
+void piper_ps_process_receive_frame(struct piper_priv *piperp,
+ struct sk_buff *skb);
+void piper_ps_init(struct piper_priv *piperp);
+void piper_ps_deinit(struct piper_priv *piperp);
+void piper_ps_set(struct piper_priv *piperp, bool powerSaveOn);
+struct ieee80211_rate *piper_ps_check_rate(struct piper_priv *piperp,
+ struct ieee80211_rate *rate);
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/digiRx.c b/drivers/net/wireless/digiPiper/digiRx.c
new file mode 100644
index 000000000000..01ff486311b1
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiRx.c
@@ -0,0 +1,412 @@
+/*
+ * digiRx.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains the routines that are related to transmitting
+ * frames.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "digiPs.h"
+
+#define WANT_RECEIVE_COUNT_SCROLL (0)
+#define AES_TIMEOUT (200)
+#define RX_DEBUG (1)
+
+#if RX_DEBUG
+static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+
+/*
+ * This routine is called to flush the receive and transmit FIFOs. It is used
+ * for error recovery when we detect corrupted data in the FIFO's. It should be
+ * called with the AES lock set.
+ */
+static void reset_fifo(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ piperp->ac->rd_reg(piperp, BB_AES_CTL);
+ piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write);
+ // clear the TX-FIFO memory
+ for (i = 0; i < 448; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write);
+
+ // clear RX-FIFO memory
+ for (i = 0; i < 512; i++)
+ piperp->ac->rd_reg(piperp, BB_DATA_FIFO);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_RXFIFORST, op_or);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RXFIFORST, op_and);
+}
+
+/*
+ * This routine is called to receive a frame. The hardware header has
+ * already been read from the FIFO. We need to read out the frame. If
+ * the frame was encrypted with AES and we have the correct key, then
+ * we use the AES H/W encryption engine to decrypt the frame. We also
+ * set up a ieee80211_rx_status structure with the appropriate info.
+ *
+ * Arguments
+ * digi context information
+ * skb empty buffer to receive packet into
+ * length number of bytes in FIFO
+ * fr_ctrl_field buffer to copy frame control header into
+ * status status structure we must write status into
+ *
+ * Returns
+ * true frame was received
+ * false an encryption error was detected
+ */
+static bool receive_packet(struct piper_priv *piperp, struct sk_buff *skb, int length,
+ frameControlFieldType_t * fr_ctrl_field,
+ struct ieee80211_rx_status *status)
+{
+ _80211HeaderType *header;
+ bool result = true;
+ int originalLength = length;
+ int headerlen;
+#if WANT_RECEIVE_COUNT_SCROLL
+ static int packetCount = 0;
+#endif
+
+ headerlen = _80211_HEADER_LENGTH;
+ if (length < _80211_HEADER_LENGTH) {
+ /*
+ * If we branch here, then there is not enough data to make a
+ * complete header. This is possible if this is a control frame.
+ * Adjust our length so that we do not read too much data from
+ * the FIFO.
+ */
+ headerlen = length;
+ }
+
+ /*
+ * Read the frame header. This includes the frame control fields
+ * as well as the 802.11 header.
+ */
+ header = (_80211HeaderType *) skb_put(skb, headerlen);
+ length -= headerlen;
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, (uint8_t *) header, headerlen);
+ memcpy(fr_ctrl_field, &header->fc, sizeof(fr_ctrl_field));
+
+ if (((u32)(skb->tail)) & 0x3) {
+ /* align data */
+ skb_reserve(skb, 4 - ((u32)(skb->tail) & 0x3));
+ }
+
+ if (header->fc.protected) {
+ /*
+ * If we branch here, then the frame is encrypted. We need
+ * to figure out if we should try to decrypt it.
+ */
+ unsigned char *rsnHeader;
+ unsigned int aesDataBlob[AES_BLOB_LENGTH / sizeof(unsigned int)];
+ unsigned int keyIndex;
+
+ rsnHeader = skb_put(skb, PIPER_EXTIV_SIZE);
+
+ /*
+ * Step 1: Read the rest of the unencrypted data, which should
+ * consist of the extiv fields.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, rsnHeader, PIPER_EXTIV_SIZE);
+ length -= PIPER_EXTIV_SIZE;
+ keyIndex = rsnHeader[3] >> 6;
+
+ if (piper_prepare_aes_datablob(piperp, keyIndex, (u8 *) aesDataBlob,
+ (unsigned char *)header, originalLength - 12,
+ false)) {
+ /*
+ * If we come here, then we have the correct encryption key for
+ * the frame and will now try to decrypt it.
+ */
+ unsigned int timeout = AES_TIMEOUT;
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->aesLock, flags);
+
+ /*
+ * Step 2: Wait for AES to become ready.
+ */
+ while (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ timeout--;
+ if (timeout == 0) {
+ /*
+ * If we come here, then AES busy appears to be stuck high. It should only be
+ * high for a maximum of about 80 us when it is encrypting a transmit frame.
+ * Our timeout value is high enough to guarantee that the engine has had enough
+ * time to complete the transmit. Apparently there is data stuck in the FIFO
+ * from either a previous transmit or receive.
+ */
+ dprintk(DWARNING, "1st AES busy never became ready\n");
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ /*
+ * Recover by flushing the FIFO and returning in error.
+ */
+ reset_fifo(piperp);
+#if 0
+ /*
+ * TODO: Figure out why this code snippet doesn't work. I would think
+ * that if we reset the fifo, we should just return in error since we will
+ * have discarded the frame. However, when we do that the system hangs
+ * (after a while). This doesn't make sense.
+ */
+ spin_unlock_irqrestore(&piperp->aesLock, flags);
+ result = false;
+ goto receive_packet_exit;
+#else
+ break;
+#endif
+ }
+ udelay(1);
+ }
+
+ /*
+ * Step 3: Set the AES mode, and then read from the AES control
+ * register to put the AES engine into receive mode.
+ */
+ piperp->ac->rd_reg(piperp, BB_AES_CTL);
+
+ /*
+ * Step 4: Write the expanded AES key into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO,
+ (unsigned char *)piperp->key[keyIndex].expandedKey,
+ EXPANDED_KEY_LENGTH);
+
+ /*
+ * Step 5: Write the AES IV and headers into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO, (unsigned char *)aesDataBlob,
+ AES_BLOB_LENGTH);
+
+ /*
+ * Step 6: Now, finally, read the unencrypted frame from the
+ * AES FIFO. Adjust the length so that we don't try
+ * to read the MIC or the ICV which the AES engine will
+ * process for us.
+ */
+ length -= MIC_SIZE + ICV_SIZE;
+ piperp->ac->rd_fifo(piperp, BB_AES_FIFO, skb_put(skb, length), length);
+ /*
+ * mac80211 seems to expect there to be a MIC even if the packet
+ * has already been decrypted. It will drop off what it thinks
+ * are the extra MIC bytes, so add some extra bytes that it
+ * can drop off without losing any data.
+ */
+ skb_put(skb, MIC_SIZE); /* add fake MIC */
+
+ /*
+ * Step 7: Wait for AES to become ready.
+ */
+ while (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ timeout--;
+ if (timeout == 0) {
+ dprintk(DWARNING, "2nd AES busy never became ready\n");
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ }
+ udelay(1);
+ }
+ result = ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_MIC) != 0);
+ timeout = 500;
+ while ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_FIFO_EMPTY) == 0) {
+ timeout--;
+ piperp->ac->rd_reg(piperp, BB_AES_FIFO);
+ udelay(1);
+ }
+#ifdef WANT_DEBUG
+ if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ digi_dbg("AES busy set at end of rx\n");
+ }
+#endif
+ spin_unlock_irqrestore(&piperp->aesLock, flags);
+
+ /* pad an extra 8 bytes for the MIC which the H/W strips */
+ skb_put(skb, 8);
+ if (result) {
+ status->flag |= RX_FLAG_DECRYPTED;
+ } else {
+ digi_dbg("Error decrypting packet\n");
+ }
+ } else {
+ /*
+ * If we branch here, then we are not able to decrypt the
+ * packet possibly because we don't have the key, or because
+ * the packet was encrypted using TKIP. Read the rest of the
+ * encrypted data. mac80211 will have to decrypt it in software.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, skb_put(skb, length),
+ length);
+ }
+ } else {
+ /*
+ * Frame is not encrypted, so just read it.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, skb_put(skb, length), length);
+ }
+
+#if WANT_RECEIVE_COUNT_SCROLL
+ if (((++packetCount) & 1023) == 0) {
+ printk(KERN_ERR "\n%d recd, tx_start_count = %d, tx_complete_count = %d.\n",
+ packetCount, piperp->pstats.tx_start_count,
+ piperp->pstats.tx_complete_count);
+ }
+#endif
+#if 0
+receive_packet_exit:
+#endif
+ return result;
+}
+
+/*
+ * This routine is called when we receive an ACK. This should be after
+ * we have transmitted a packet. We need to tell the upper layer we
+ * have a packet by calling ieee80211_tx_status_irqsafe with status
+ * information. The transmit routine also disables queuing whenever we
+ * transmit since we can only transmit one packet at a time, so we need
+ * to reenable to transmit queue too.
+ */
+static inline void handle_ack(struct piper_priv *piperp, int signal_strength)
+{
+ if (piper_tx_getqueue(piperp) && piperp->expectingAck) {
+ struct ieee80211_tx_info *txInfo = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+ if ((txInfo->flags & IEEE80211_TX_CTL_NO_ACK) == 0) {
+ piperp->clear_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT);
+ piperp->tx_signal_strength = signal_strength;
+ piperp->tx_result = RECEIVED_ACK;
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ }
+ }
+}
+
+
+/*
+ * This is the entry point for the receive tasklet. It is executed
+ * to process receive packets. It allocates an SKB and receives
+ * the packet into it.
+ *
+ * We may be called from the receive ISR if WANT_TO_RECEIVE_FRAMES_IN_ISR
+ * is set.
+ */
+void piper_rx_tasklet(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *)context;
+
+ /*
+ * This while loop will keep executing as long as the H/W indicates there
+ * are more frames in the FIFO to be received.
+ */
+
+ piperp->ps.rxTaskletRunning = true;
+
+ while (((piperp->ac->rd_reg(piperp, BB_GENERAL_STAT) &
+ BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0)
+ && (!piperp->ps.poweredDown)) {
+ struct sk_buff *skb = NULL;
+ struct ieee80211_rx_status status = { 0 };
+ struct rx_frame_hdr header;
+ unsigned int length = 0;
+ frameControlFieldType_t fr_ctrl_field;
+
+ /*
+ * Read and process the H/W header. This header is created by
+ * the hardware is is not part of the frame.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, (u8 *)&header, sizeof(header));
+ phy_process_plcp(piperp, &header, &status, &length);
+ if ((length == 0) || (length > (RX_FIFO_SIZE - 48))) { /* 48 bytes for padding and related stuff */
+ unsigned long flags;
+
+ dprintk(DERROR, "bogus frame length (%d)\n", length);
+ dprintk(DERROR, "0x%08x 0x%08x\n", *(u32 *)&header, *(((u32 *)&header) + 1));
+ spin_lock_irqsave(&piperp->aesLock, flags);
+ reset_fifo(piperp);
+ spin_unlock_irqrestore(&piperp->aesLock, flags);
+ continue;
+ }
+
+ if (length != 0) {
+
+ skb = __dev_alloc_skb(RX_FIFO_SIZE + 100, GFP_ATOMIC);
+ if (skb == NULL) {
+ /* Oops. Out of memory. Exit the tasklet */
+ dprintk(DERROR, "__dev_alloc_skb failed\n");
+ break;
+ }
+
+ if (receive_packet(piperp, skb, length, &fr_ctrl_field, &status)) {
+
+ if (length >= _80211_HEADER_LENGTH)
+ {
+ /*
+ * If using the Airoha transceiver, then we want to monitor
+ * the receive signal strength and continuously adjust the
+ * receive amplifier so that we get the best possible signal
+ * to noise ratio.
+ */
+ unsigned int rssi = phy_determine_rssi(&header);
+
+ piperp->adjust_max_agc(piperp, rssi, (_80211HeaderType *) skb->data);
+ }
+
+ if (fr_ctrl_field.type == TYPE_ACK)
+ handle_ack(piperp, status.signal);
+
+ if ((fr_ctrl_field.type == TYPE_ACK)
+ || (fr_ctrl_field.type == TYPE_RTS)
+ || (fr_ctrl_field.type == TYPE_CTS)) {
+ /*
+ * Don't pass up RTS, CTS, or ACK frames. They just
+ * confuse the stack
+ */
+ dev_kfree_skb(skb);
+ } else {
+ if (fr_ctrl_field.type == TYPE_BEACON) {
+ piperp->beacon.weSentLastOne = false;
+ }
+
+ piper_ps_process_receive_frame(piperp, skb);
+#if WANT_TO_RECEIVE_FRAMES_IN_ISR
+ ieee80211_rx_irqsafe(piperp->hw, skb, &status);
+#else
+ ieee80211_rx(piperp->hw, skb, &status);
+#endif
+ }
+ } else {
+ /* Frame failed MIC, so discard it */
+ dprintk(DWARNING, "dropping bad frame\n");
+ dev_kfree_skb(skb);
+ }
+ }
+ }
+
+ piperp->ps.rxTaskletRunning = false;
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_RX_FIFO);
+ piper_ps_rx_task_exiting(piperp);
+}
+EXPORT_SYMBOL_GPL(piper_rx_tasklet);
+
+
+
diff --git a/drivers/net/wireless/digiPiper/digiTx.c b/drivers/net/wireless/digiPiper/digiTx.c
new file mode 100644
index 000000000000..4bea846a42a4
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiTx.c
@@ -0,0 +1,604 @@
+/*
+ * digiTx.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This file contains the routines that are related to transmitting
+ * frames.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "digiPs.h"
+
+#define FRAME_CONTROL_FIELD_OFFSET (sizeof(struct tx_frame_hdr) + sizeof(struct psk_cck_hdr))
+#define AES_TIMEOUT (200)
+
+#define TX_DEBUG (1)
+
+#if TX_DEBUG
+//static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * Adds an entry into the tx queue.
+ */
+int piper_tx_enqueue(struct piper_priv *piperp, struct sk_buff *skb, tx_skb_return_cb_t skb_return_cb)
+{
+ unsigned long flags;
+ int result = -1;
+
+ spin_lock_irqsave(&piperp->tx_queue_lock, flags);
+ if (NEXT_TX_QUEUE_INDEX(piperp->tx_queue_head) != piperp->tx_queue_tail) {
+ piperp->tx_queue[piperp->tx_queue_head].skb = skb;
+ piperp->tx_queue[piperp->tx_queue_head].skb_return_cb = skb_return_cb;
+ piperp->tx_queue_head = NEXT_TX_QUEUE_INDEX(piperp->tx_queue_head);
+ piperp->tx_queue_count++;
+ result = 0;
+ }
+ spin_unlock_irqrestore(&piperp->tx_queue_lock, flags);
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(piper_tx_enqueue);
+
+
+/*
+ * Returns the skb for the current element.
+ */
+struct sk_buff *piper_tx_getqueue(struct piper_priv *piperp)
+{
+ if (piperp->tx_queue_head == piperp->tx_queue_tail) {
+ return NULL;
+ } else {
+ return piperp->tx_queue[piperp->tx_queue_tail].skb;
+ }
+}
+EXPORT_SYMBOL_GPL(piper_tx_getqueue);
+
+/*
+ * Returns the skb buffer call back for the current element.
+ */
+static inline tx_skb_return_cb_t piper_tx_getqueue_return_fn(struct piper_priv *piperp)
+{
+ return piperp->tx_queue[piperp->tx_queue_tail].skb_return_cb;
+}
+
+
+/*
+ * Called to advance the queue tail.
+ */
+static inline void piper_tx_queue_next(struct piper_priv *piperp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->tx_queue_lock, flags);
+ if (piperp->tx_queue_head != piperp->tx_queue_tail) {
+ piperp->tx_queue[piperp->tx_queue_tail].skb = NULL;
+ piperp->tx_queue[piperp->tx_queue_tail].skb_return_cb = NULL;
+ piperp->tx_queue_tail = NEXT_TX_QUEUE_INDEX(piperp->tx_queue_tail);
+ piperp->tx_queue_count--;
+ }
+ spin_unlock_irqrestore(&piperp->tx_queue_lock, flags);
+}
+
+/*
+ * Called when we unload to clear any remaining queue entries.
+ */
+void piper_empty_tx_queue(struct piper_priv *piperp)
+{
+ while (piper_tx_getqueue(piperp)) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+
+ ieee80211_tx_info_clear_status(info);
+ piper_tx_getqueue_return_fn(piperp)(piperp->hw, piper_tx_getqueue(piperp));
+ piper_tx_queue_next(piperp);
+ }
+}
+EXPORT_SYMBOL_GPL(piper_empty_tx_queue);
+
+
+bool piper_tx_queue_half_full(struct piper_priv *piperp)
+{
+ return (piperp->tx_queue_count >= (PIPER_TX_QUEUE_SIZE >> 1));
+}
+EXPORT_SYMBOL_GPL(piper_tx_queue_half_full);
+
+
+
+/*
+ * This routine writes a frame using H/W AES encryption.
+ *
+ * Arguments
+ * digi context
+ * buffer pointer to start of frame
+ * length number of bytes in frame
+ *
+ * Return Values
+ * 0 success
+ * !0 transmit failed
+ */
+static int piper_write_aes(struct piper_priv *piperp, unsigned char *buffer,
+ unsigned int length)
+{
+ int result;
+ int timeout = AES_TIMEOUT;
+ unsigned long spinLockFlags;
+
+ /*
+ * Step 1: Wait for AES to become ready.
+ */
+ spin_lock_irqsave(&piperp->aesLock, spinLockFlags);
+ while (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ timeout--;
+ if (timeout == 0) {
+ /*
+ * If we come here, then AES busy appears to be stuck high. It should only be
+ * high for a maximum of about 80 us when it is encrypting a transmit frame.
+ * Our timeout value is high enough to guarantee that the engine has had enough
+ * time to complete the transmit. Apparently there is data stuck in the FIFO
+ * from either a previous transmit or receive.
+ */
+ digi_dbg("write AES, AES busy stuck on\n");
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ /*
+ * We recover by simply continuing on. Step 3 writes to the AES control
+ * register. This will reset the AES engine and clear the error condition.
+ */
+ break;
+ }
+ udelay(1);
+ }
+
+ /*
+ * Step 2: Write the unencrypted part of the frame into the normal
+ * data FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, buffer,
+ _80211_HEADER_LENGTH + TX_HEADER_LENGTH + PIPER_EXTIV_SIZE);
+
+ /*
+ * Step 3: Write to the AES control register. Writing to it puts
+ * AES H/W engine into transmit mode. We also make sure
+ * the AES mode is set correctly.
+ */
+ piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write);
+
+ /*
+ * Step 4: Write the expanded AES key into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO,
+ (unsigned char *)piperp->key[piperp->tx_aes_key].expandedKey,
+ EXPANDED_KEY_LENGTH);
+
+ /*
+ * Step 5: Write the AES IV and headers into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO, (unsigned char *)piperp->tx_aes_blob,
+ AES_BLOB_LENGTH);
+
+ /*
+ * Step 6: Now, finally, write the part of the frame that needs to
+ * be encrypted into the AES FIFO.
+ */
+ result =
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO,
+ &buffer[_80211_HEADER_LENGTH + TX_HEADER_LENGTH + PIPER_EXTIV_SIZE],
+ length - (_80211_HEADER_LENGTH + TX_HEADER_LENGTH +
+ PIPER_EXTIV_SIZE));
+
+ spin_unlock_irqrestore(&piperp->aesLock, spinLockFlags);
+
+ return result;
+}
+
+/*
+ * Determine what bit rate the next retry should be sent at.
+ *
+ * The mac80211 library passes us an array of tx bit rates. Each entry
+ * has a rate index and a limit (max number of retries at that rate).
+ * We use the rate index to build the H/W transmit header. The limit
+ * is decremented each time we retry. When it reaches zero, we try the
+ * next rate in the array.
+ */
+static struct ieee80211_rate *get_tx_rate(struct piper_priv *piperp, struct ieee80211_tx_info *info)
+{
+ struct ieee80211_rate *ret = NULL;
+
+ if (piperp->pstats.tx_retry_count[piperp->pstats.tx_retry_index] >=
+ info->control.rates[piperp->pstats.tx_retry_index].count) {
+ piperp->pstats.tx_retry_index++;
+ }
+
+ if (piperp->pstats.tx_retry_index >= IEEE80211_TX_MAX_RATES) {
+ return NULL; /* don't go beyond the end of the rates array */
+ }
+
+ if (piperp->pstats.tx_retry_index == 0) {
+ ret = ieee80211_get_tx_rate(piperp->hw, info);
+ } else {
+ ret = ieee80211_get_alt_retry_rate(piperp->hw, info, piperp->pstats.tx_retry_index - 1);
+ }
+
+ if (ret != NULL) {
+ if (piperp->calibrationTxRate) {
+ ret = piperp->calibrationTxRate;
+ }
+ }
+
+ ret = piper_ps_check_rate(piperp, ret);
+
+ return ret;
+}
+
+/*
+ * This function returns a value for the contention window in microseconds. We
+ * start with the contention window at CW_MIN and double it everytime we have to
+ * retry.
+ */
+static u16 piper_get_cw(struct piper_priv *piperp, bool isFirstTime)
+{
+ static u16 cw = DEFAULT_CW_MIN;
+
+ if (isFirstTime) {
+ cw = DEFAULT_CW_MIN;
+ } else {
+ cw <<= 1;
+ if (cw > DEFAULT_CW_MAX) {
+ cw = DEFAULT_CW_MAX;
+ }
+ }
+ return (cw + (10 * (piperp->rand() & (cw - 1)))) & 0xffff;
+}
+
+/*
+ * This function will prepend an RTS or CTS to self frame ahead of the current
+ * TX frame. This is done if the wantRts or wantCts flag is set. The mac80211
+ * library determines if either of these flags is set.
+ *
+ * The RTS or CTS message is written into the transmit FIFO ahead of the
+ * data frame. Note that RTS and CTS messages are always sent in the clear
+ * so we do not have to worry about encryption.
+ *
+ * Our caller, the master transmit routine, is responsible for setting the
+ * transmit hold bit before calling us and clearing it after the data frame
+ * has been written into the FIFO. This ensures that the RTS/CTS frame is
+ * not transmitted until after the data frame is ready to go.
+ *
+ * Also note that if we are unable to send the RTS/CTS frame, then the H/W
+ * is smart enough to also about the data frame. So we will not send
+ * the data frame without the RTS/CTS frame.
+ */
+static void handle_rts_cts(struct piper_priv *piperp,
+ struct ieee80211_tx_info *txInfo, unsigned int frameType)
+{
+ piperp->tx_rts = false;
+
+ if (frameType == TYPE_DATA) {
+ unsigned int header[2];
+ struct ieee80211_rate *rate = NULL;
+ bool wantCts = (!!(txInfo->control.rates[piperp->pstats.tx_retry_index].flags
+ & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ | piperp->tx_cts);
+ bool wantRts = !!(txInfo->control.rates[piperp->pstats.tx_retry_index].flags
+ & IEEE80211_TX_RC_USE_RTS_CTS);
+
+ if ((wantRts) || (wantCts)) {
+ /*
+ * If we are sending an RTS or a CTS, then get the rate information.
+ */
+ if (piperp->calibrationTxRate) {
+ rate = piperp->calibrationTxRate;
+ } else {
+ rate = ieee80211_get_rts_cts_rate(piperp->hw, txInfo);
+ }
+ if (rate == NULL) {
+ digi_dbg
+ ("ieee80211_get_rts_cts_rate(digi->hw, txInfo) returned NULL!\n");
+ }
+ }
+ if ((wantRts) && (rate)) {
+ /*
+ * We're sending an RTS, so load it into the FIFO.
+ */
+ struct ieee80211_rts rtsFrame;
+
+ ieee80211_rts_get(piperp->hw, txInfo->control.vif,
+ piper_tx_getqueue(piperp)->data + TX_HEADER_LENGTH,
+ piper_tx_getqueue(piperp)->len - TX_HEADER_LENGTH, txInfo,
+ &rtsFrame);
+ /*
+ * If we come here, then we need to send an RTS frame ahead of the
+ * current data frame.
+ */
+ phy_set_plcp((unsigned char *)header, sizeof(struct ieee80211_rts),
+ rate, 0);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)header,
+ TX_HEADER_LENGTH);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)&rtsFrame,
+ sizeof(rtsFrame));
+ if (piperp->pstats.tx_total_tetries != 0) {
+ piperp->pstats.ll_stats.dot11RTSFailureCount++;
+ }
+ piperp->tx_rts = true;
+ } else if ((wantCts) && (rate)) {
+ /*
+ * We're sending a CTS, so load it into the FIFO.
+ */
+ struct ieee80211_cts ctsFrame;
+
+ ieee80211_ctstoself_get(piperp->hw, txInfo->control.vif,
+ piper_tx_getqueue(piperp)->data + TX_HEADER_LENGTH,
+ piper_tx_getqueue(piperp)->len - TX_HEADER_LENGTH,
+ txInfo, &ctsFrame);
+ /*
+ * At the time this code was written, the mac80211 library had
+ * a bug in the ieee80211_ctstoself_get which caused it to copy
+ * the wrong MAC address into the cts frame. So we copy the
+ * right one (ours) in now.
+ */
+ memcpy(piperp->ctsFrame.ra, piperp->hw->wiphy->perm_addr, ETH_ALEN);
+
+ /*
+ * If we come here, then we need to send a CTS to self frame ahead of the
+ * current data frame.
+ */
+ phy_set_plcp((unsigned char *)header, sizeof(struct ieee80211_cts),
+ rate, 0);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)header,
+ TX_HEADER_LENGTH);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)&ctsFrame,
+ sizeof(ctsFrame));
+ }
+ }
+}
+
+/*
+ * This routine is called to report the result of a transmit operation to
+ * mac80211. It is used for both successful transmissions and failures.
+ * It sends the result to the stack, removes the current tx frame from the
+ * queue, and then wakes
+ * up the transmit queue.
+ */
+void packet_tx_done(struct piper_priv *piperp, tx_result_t result,
+ int signal_strength)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+ int i;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+#define WANT_TRANSMIT_RESULT (0)
+#if WANT_TRANSMIT_RESULT
+ const char *resultText[] =
+ {
+ "RECEIVED_ACK",
+ "TX_COMPLETE",
+ "OUT_OF_RETRIES",
+ "TX_NOT_DONE"
+ };
+#endif
+ del_timer_sync(&piperp->tx_timer);
+ piperp->expectingAck = false;
+
+#if WANT_TRANSMIT_RESULT
+ printk(KERN_ERR "Transmit result %s\n", resultText[result]);
+#endif
+ if (piperp->tx_calib_cb)
+ piperp->tx_calib_cb(piperp);
+
+ if (piper_tx_getqueue(piperp) != NULL) {
+ skb_pull(piper_tx_getqueue(piperp), TX_HEADER_LENGTH);
+
+ ieee80211_tx_info_clear_status(info);
+
+ /* prepare statistics and pass them to the stack */
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ info->status.rates[i].count = piperp->pstats.tx_retry_count[i];
+ if (info->status.rates[i].count == 0)
+ info->status.rates[i].idx = -1;
+ }
+
+ info->status.ack_signal = signal_strength;
+ info->flags |= (result == RECEIVED_ACK) ? IEEE80211_TX_STAT_ACK : 0;
+ piperp->pstats.tx_complete_count++;
+ piperp->pstats.tx_queue.len--;
+ if (piperp->tx_rts)
+ piperp->pstats.ll_stats.dot11RTSSuccessCount++;
+
+ skb = piper_tx_getqueue(piperp);
+ piper_tx_getqueue_return_fn(piperp)(piperp->hw, skb);
+
+ piper_tx_queue_next(piperp);
+
+ if (piper_tx_getqueue(piperp) == NULL) {
+ spin_lock_irqsave(&piperp->tx_tasklet_lock, flags);
+ piperp->tx_tasklet_running = false;
+ spin_unlock_irqrestore(&piperp->tx_tasklet_lock, flags);
+ if (piperp->ps.allowTransmits) {
+ ieee80211_wake_queues(piperp->hw);
+ } else {
+ /*
+ * Do not wake up the mac80211 Tx queue if we are trying to power
+ * down. Make sure we set the stopped_tx_queues flag so that we
+ * know to restart the queues.
+ */
+ piperp->ps.stopped_tx_queues = true;
+ }
+ } else {
+ if (result == OUT_OF_RETRIES) {
+ /*
+ * If we come here, then we are being called from the middle of the
+ * transmit routine and we have to reschedule the transmit task to
+ * start dequeueing the next frame.
+ */
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ }
+ }
+ } else {
+ digi_dbg("packet_tx_done called with empty queue\n");
+ }
+
+ piperp->tx_result = TX_NOT_DONE;
+}
+EXPORT_SYMBOL_GPL(packet_tx_done);
+
+/*
+ * This function is the entry point for the transmit tasklet. It
+ * is called to transmit frames. It will first be called to transmit
+ * the frame and then to retry if the original transmit fails. So
+ * it does both the first transmit and the subsequent retries.
+ *
+ * Arguments
+ * context context information
+ */
+void piper_tx_tasklet(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *)context;
+ frameControlFieldType_t *fc;
+ int err;
+
+ piperp->expectingAck = false;
+ del_timer_sync(&piperp->tx_timer);
+ if ((piperp->tx_result == RECEIVED_ACK) || (piperp->tx_result == TX_COMPLETE)) {
+ /*
+ * We will come here if the receiver task received an ACK, or if we got
+ * a tx fifo empty interrupt. In these cases the receiver thread or ISR
+ * schedule the tx tasklet to handle the event rather than calling
+ * packet_tx_done directly.
+ */
+ packet_tx_done(piperp, piperp->tx_result, piperp->tx_signal_strength);
+ }
+
+
+ /*
+ * Clear flags here to cover ACK case. We do not clear the flags in the ACK
+ * routine since it is possible to receive an ACK after we have started the
+ * next packet. The appropriate interrupts will be reenabled if we decide
+ * to retransmit.
+ */
+ piperp->clear_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TX_FIFO_EMPTY | BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT);
+
+ if (piper_tx_getqueue(piperp) != NULL) {
+ struct ieee80211_tx_info *txInfo = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+ struct ieee80211_rate *txRate = get_tx_rate(piperp, txInfo);
+
+ if (txRate != NULL) {
+ fc = (frameControlFieldType_t *)
+ &piper_tx_getqueue(piperp)->data[FRAME_CONTROL_FIELD_OFFSET];
+
+ /* set the retry bit if this is not the first try */
+ if (piperp->pstats.tx_retry_count[0] != 0)
+ fc->retry = 1;
+
+ piperp->ac->wr_reg(piperp, MAC_BACKOFF,
+ piper_get_cw(piperp,
+ (piperp->pstats.tx_retry_count[0] == 0)),
+ op_write);
+
+ /*
+ * Build the H/W transmit header. The transmit header is rebuilt on each
+ * retry because it has the TX rate information which may change for
+ * retries.
+ */
+ phy_set_plcp(piper_tx_getqueue(piperp)->data,
+ piper_tx_getqueue(piperp)->len - TX_HEADER_LENGTH,
+ txRate, piperp->use_hw_aes ? 8 : 0);
+
+ /*
+ * Pause the transmitter so that we don't start transmitting before we
+ * are ready.
+ */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_TX_HOLD, op_or);
+
+ handle_rts_cts(piperp, txInfo, fc->type);
+
+ if (piperp->use_hw_aes == true && txInfo->control.hw_key != NULL) {
+ err =
+ piper_write_aes(piperp, piper_tx_getqueue(piperp)->data,
+ piper_tx_getqueue(piperp)->len);
+ } else {
+ err =
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, piper_tx_getqueue(piperp)->data,
+ piper_tx_getqueue(piperp)->len);
+ }
+
+ /* Clear any pending TX interrupts */
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT,
+ BB_IRQ_MASK_TX_FIFO_EMPTY | BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT, op_write);
+
+ /*
+ * Now start the transmitter.
+ */
+ piperp->expectingAck = ((txInfo->flags & IEEE80211_TX_CTL_NO_ACK) == 0);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_TX_HOLD,
+ op_and);
+
+ /*
+ * Set interrupt flags. Use the timeout interrupt if we expect
+ * an ACK. Use the FIFO empty interrupt if we do not expect an ACK.
+ */
+ if (txInfo->flags & IEEE80211_TX_CTL_NO_ACK) {
+ piperp->set_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TX_ABORT);
+ } else {
+ /*
+ * We set up a timer to fire in 1/4 second. We should not need it, but somehow
+ * we seem to miss a timeout interrupt occasionally. Perhaps we encounter a receive
+ * overrun which causes the H/W to discard the ACK packet without generating
+ * a timeout.
+ */
+ piperp->tx_timer.expires = jiffies + (HZ >> 2);
+ add_timer(&piperp->tx_timer);
+
+ /*
+ * Also set the IRQ mask to listen for timeouts and TX aborts. We will receive
+ * an ACK (which is handled by the RX routine) if the TX is successful.
+ */
+ piperp->set_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT);
+ }
+ if ((piperp->pstats.tx_total_tetries != 0) &&
+ ((txInfo->flags & IEEE80211_TX_CTL_NO_ACK) == 0)) {
+ piperp->pstats.ll_stats.dot11ACKFailureCount++;
+ }
+ piperp->pstats.tx_retry_count[piperp->pstats.tx_retry_index]++;
+ piperp->pstats.tx_total_tetries++;
+ } else {
+ packet_tx_done(piperp, OUT_OF_RETRIES, 0);
+ }
+ } else {
+ long unsigned int flags;
+
+ spin_lock_irqsave(&piperp->tx_tasklet_lock, flags);
+ piperp->tx_tasklet_running = false;
+ spin_unlock_irqrestore(&piperp->tx_tasklet_lock, flags);
+ }
+}
+EXPORT_SYMBOL_GPL(piper_tx_tasklet);
+
+
+
diff --git a/drivers/net/wireless/digiPiper/mac.h b/drivers/net/wireless/digiPiper/mac.h
new file mode 100644
index 000000000000..892a15a8b67c
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/mac.h
@@ -0,0 +1,375 @@
+#ifndef DIGI_MAC_H_
+#define DIGI_MAC_H_
+
+enum baseband_control_regs {
+ BB_VERSION = 0x00,
+ BB_GENERAL_CTL = 0x04,
+ BB_GENERAL_STAT = 0x08,
+ BB_RSSI = 0x0c,
+ BB_IRQ_MASK = 0x10,
+ BB_IRQ_STAT = 0x14,
+ BB_SPI_DATA = 0x18,
+ BB_SPI_CTRL = 0x1c,
+ BB_DATA_FIFO = 0x20,
+ BB_TRACK_CONTROL = 0x28,
+ BB_CONF_2 = 0x2c,
+ BB_AES_FIFO = 0x30,
+ BB_AES_CTL = 0x38,
+ BB_OUTPUT_CONTROL = 0x3c
+};
+
+#define BB_VERSION_MASK(v) ((v) & 0xffff)
+
+#define BB_GENERAL_CTL_RX_EN (1<<0)
+#define BB_GENERAL_CTL_ANT_DIV (1<<1)
+#define BB_GENERAL_CTL_ANT_SEL (1<<2)
+#define BB_GENERAL_CTL_GEN_5GEN (1<<3) // 5 GHz band enable
+#define BB_GENERAL_CTL_SH_PRE (1<<4)
+#define BB_GENERAL_CTL_RXFIFORST (1<<5)
+#define BB_GENERAL_CTL_TXFIFORST (1<<6)
+#define BB_GENERAL_CTL_TX_HOLD (1<<7)
+#define BB_GENERAL_CTL_BEACON_EN (1<<8)
+#define BB_GENERAL_CTL_FW_LOAD_ENABLE (1 << 9)
+#define BB_GENERAL_CTL_DSP_LOAD_ENABLE (1 << 10)
+#define BB_GENERAL_CTL_MAC_ASSIST_ENABLE (1 << 11)
+#define BB_GENERAL_CTL_TX_FIFO_FULL (1<<15)
+#define BB_GENERAL_CTL_TX_FIFO_EMPTY (1<<14)
+/* TODO: verify max gain value for Piper and Wi9p*/
+#define BB_GENERAL_CTL_MAX_GAIN(g) (((g) & 0x7f)<<16)
+#define BB_GENERAL_CTL_PWR_UP (1<<24)
+#define BB_GENERAL_CTL_ADC_CLK_EN (1<<25)
+#define BB_GENERAL_CTL_BOOT_STAT (1<<28)
+#define BB_GENERAL_CTL_CLK_EN (1<<29)
+#define BB_GENERAL_CTL_SPI_RST (1<<30)
+
+#define BB_GENERAL_CTL_MAX_GAIN_MASK (0x007F0000)
+#define BB_GENERAL_CTL_DEFAULT_MAX_GAIN_A (0x00790000)
+#define BB_GENERAL_CTL_DEFAULT_MAX_GAIN_BG (0x007c0000)
+
+#if defined(CONFIG_PIPER_WIFI)
+#if 0
+#define BB_GENERAL_CTL_INIT (BB_GENERAL_CTL_MAX_GAIN(0x7a) | \
+ BB_GENERAL_CTL_PWR_UP | BB_GENERAL_CTL_ADC_CLK_EN | \
+ BB_GENERAL_CTL_BOOT_STAT | BB_GENERAL_CTL_CLK_EN)
+#define BB_GENERAL_CTL_RESET (BB_GENERAL_CTL_MAX_GAIN(0x7f) | \
+ BB_GENERAL_CTL_ADC_CLK_EN | BB_GENERAL_CTL_BOOT_STAT | \
+ BB_GENERAL_CTL_SPI_RST)
+#else
+#define BB_GENERAL_CTL_INIT (BB_GENERAL_CTL_MAX_GAIN(0x7a)
+
+#define BB_GENERAL_CTL_RESET (BB_GENERAL_CTL_MAX_GAIN(0x7f) | \
+ BB_GENERAL_CTL_SPI_RST
+#endif
+#else
+#define BB_GENERAL_CTL_INIT (BB_GENERAL_CTL_MAX_GAIN(0x7a) | \
+ BB_GENERAL_CTL_PWR_UP | BB_GENERAL_CTL_ADC_CLK_EN | \
+ BB_GENERAL_CTL_BOOT_STAT | BB_GENERAL_CTL_CLK_EN)
+
+#define BB_GENERAL_CTL_RESET (BB_GENERAL_CTL_MAX_GAIN(0x7f) | \
+ BB_GENERAL_CTL_ADC_CLK_EN | BB_GENERAL_CTL_BOOT_STAT | \
+ BB_GENERAL_CTL_SPI_RST)
+#endif
+#define BB_RSSI_LED (1<<8)
+#define BB_RSSI_EAS_FIFO_EMPTY (1 << 16)
+#define BB_RSSI_EAS_FIFO_FULL (1 << 17)
+#define BB_RSSI_EAS_BUSY (1 << 18)
+#define BB_RSSI_EAS_MIC (1 << 19)
+#define BB_RSSI_ANT_MASK (0xff<<24)
+#define BB_RSSI_ANT_NO_DIV_MAP (0x96000000)
+#define BB_RSSI_ANT_DIV_MAP (0x1E000000)
+
+#define BB_GENERAL_STAT_RESET (1<<30)
+/*
+ * STAT_B_EN is a constant that defines a bit in the Wireless Controller FPGA Baseband Control Register
+ * for General Status, which enables the PSK/CCK receiver baseband circuitry (802.11b receiver).
+ * STAT_A_EN is a constant that defines a bit in the Wireless Controller FPGA Baseband Control Register
+ * for General Status, which enables the OFDM receive baseband circuitry (802.11a receiver).
+ */
+
+#define BB_GENERAL_STAT_B_EN 0x10000000 // B EN (PSK/CCK)
+#define BB_GENERAL_STAT_A_EN 0x20000000 // A EN (OFDM)
+#define BB_GENERAL_STAT_RX_FIFO_EMPTY (1 << 4)
+#define BB_GENERAL_STAT_DC_DIS (1 << 24)
+#define BB_GENERAL_STAT_SRC_DIS (1 << 16)
+#define BB_GENERAL_STAT_SPRD_DIS (1 << 17)
+#define BB_GENERAL_STAT_DLL_DIS (1 << 18)
+#define TRACK_TX_B_GAIN_MASK 0xff000000 // Mask word for B_TX_GAIN
+#define TRACK_TX_B_GAIN_NORMAL 0xA0000000 // normal setting for B_TX_GAIN
+#define TRACK_BG_BAND 0x00430000 // Tracking constant for 802.11 b/g frequency band
+#define TRACK_CONSTANT_MASK 0x00ff0000 // mask for tracking constant
+#define TRACK_4920_4980_A_BAND 0x00210000 // Tracking constant for 802.11 a sub-frequency band
+#define TRACK_5150_5350_A_BAND 0x001F0000 // Tracking constant for 802.11 a sub-frequency band
+#define TRACK_5470_5725_A_BAND 0x001D0000 // Tracking constant for 802.11 a sub-frequency band
+#define TRACK_5725_5825_A_BAND 0x001C0000 // Tracking constant for 802.11 a sub-frequency band
+
+
+#define BB_IRQ_MASK_RX_FIFO (1<<0)
+#define BB_IRQ_MASK_TX_FIFO_EMPTY (1<<1)
+#define BB_IRQ_MASK_TIMEOUT (1<<2)
+#define BB_IRQ_MASK_TX_ABORT (1<<3)
+#define BB_IRQ_MASK_TBTT (1<<4)
+#define BB_IRQ_MASK_ATIM (1<<5)
+#define BB_IRQ_MASK_RX_OVERRUN (1<<6)
+
+#define BB_AES_CTL_KEY_LOAD (1<<2)
+#define BB_AES_CTL_AES_MODE (1<<4)
+
+enum mac_control_regs {
+ MAC_STA_ID0 = 0x40,
+ MAC_STA_ID1 = 0x44,
+ MAC_BSS_ID0 = 0x48,
+ MAC_BSS_ID1 = 0x4c,
+ MAC_SSID_LEN = 0x50, /* OFDM_BRS, PSK_BRS, TX_CTL, SSID_LEN */
+ MAC_BACKOFF = 0x54, /* actually 0x56; 2 low order bytes are empty */
+ MAC_DTIM_PERIOD = 0x58,
+ /*MAC_CFP_PERIOD = 0x59,*/
+ /*MAC_LISTEN_INTERVAL = 0x5a,*/
+ MAC_CFP_ATIM = 0x5c, /* beacon interval, CFP/ATIM duration */
+ MAC_STATUS = 0x60,
+ /*MAC_TXP_TIMING = 0x62,*/
+ /*MAC_STATUS = 0x63,*/
+ MAC_CTL = 0x64, /* MAC_AES_KEY_DIS (8 bits), MAC_CTL (8 bits) */
+ MAC_MEASURE = 0x68, /* actually 0x69 */
+ /*MAC_REMAIN_BO = 0x6a,*/
+ MAC_BEACON_FILT = 0x6c, /* actally 0x6d */
+ /*MAC_BEACON_BO = 0x6e,*/
+ MAC_STA2_ID0 = 0xb0,
+ MAC_STA2_ID1 = 0xb4,
+ MAC_STA3_ID0 = 0xb8,
+ MAC_STA3_ID1 = 0xbc,
+
+ MAC_EEPROM_CTL = 0xf0,
+ MAC_EEPROM_DATA = 0xf8,
+
+ MAC_SSID = 0x80,
+
+ BEACON_FIFO = 0x85, /* dummy value used to select data fifo for beacon load */
+};
+
+
+#define MAC_SSID_LEN_MASK (0x000000ff)
+#define MAC_REVISION_MASK(v) (((v) >> 16) & 0xffff)
+
+#define MAC_BEACON_INTERVAL_SHIFT (16)
+#define MAC_BEACON_INTERVAL_MASK (0xffff0000)
+
+#define MAC_ATIM_PERIOD_MASK (0x0000ffff)
+
+#define MAC_LISTEN_INTERVAL_MASK (0x0000ffff)
+
+#define MAC_DTIM_PERIOD_SHIFT (24)
+#define MAC_DTIM_PERIOD_MASK (0xff000000)
+
+#define MAC_DTIM_CFP_SHIFT (16)
+#define MAC_DTIM_CFP_MASK (0x00ff0000)
+
+#define MAC_OFDM_BRS_MASK (0xff000000)
+#define MAC_OFDM_BRS_SHIFT (24)
+#define MAC_PSK_BRS_MASK (0x000f0000)
+#define MAC_PSK_BRS_SHIFT (16)
+
+#define MAC_BEACON_BACKOFF_MASK (0x0000ffff)
+
+#define MAC_BRS_MASK (MAC_OFDM_BRS_MASK | MAC_PSK_BRS_MASK)
+
+#define MAC_CTL_TX_REQ (1)
+#define MAC_CTL_BEACON_TX (1<<2)
+#define MAC_CTL_IBSS (1<<4)
+#define MAC_CTL_AES_DISABLE (1<<5)
+#define MAC_CTL_MAC_FLTR (1<<6)
+#define MAC_CTL_KEY0_DISABLE (1<<8)
+#define MAC_CTL_KEY1_DISABLE (1<<9)
+#define MAC_CTL_KEY2_DISABLE (1<<10)
+#define MAC_CTL_KEY3_DISABLE (1<<11)
+
+#define MAC_EEPROM_CTL_WAIT_MS 21
+
+/*
+ * RX packets look something like:
+ * <custom bus protocol header(s)> - protocol-dependent
+ * <rx_frame_hdr> - 4 bytes
+ * <plcp; either psk_cck_hdr or ofdm_hdr> - 4 bytes
+ * <mac header> - dealt with by the mac80211 stack, not the driver
+ * <data>
+ *
+ * TX packets are similar:
+ * <custom bus protocol header(s)>
+ * <tx_frame_hdr> - 4 bytes
+ * <plcp; either psk_cck_hdr or ofdm_hdr> - 4 bytes
+ * <mac header>
+ * <data>
+ */
+
+
+
+// MAC type field values
+#define TYPE_ASSOC_REQ 0x00 // Association request
+#define TYPE_ASSOC_RESP 0x10 // Association response
+#define TYPE_REASSOC_REQ 0x20 // Reassociation request
+#define TYPE_REASSOC_RESP 0x30 // Reassociation response
+#define TYPE_PROBE_REQ 0x40 // Probe request
+#define TYPE_PROBE_RESP 0x50 // Probe response
+
+#define TYPE_BEACON 0x80 // Beacon
+#define TYPE_ATIM 0x90 // Annoucement traffice indication
+#define TYPE_DISASSOC 0xa0 // Disassociation
+#define TYPE_AUTH 0xb0 // Authentication
+#define TYPE_DEAUTH 0xc0 // Deauthentication
+#define TYPE_ACTION 0xd0 // Action
+
+#define TYPE_RTS 0xb4 // Request to send
+#define TYPE_CTS 0xc4 // Clear to send
+#define TYPE_ACK 0xd4 // Acknowledgement
+#define TYPE_PSPOLL 0xa4 // Power Save(PS)-Poll
+
+#define TYPE_DATA 0x08 // Data
+#define TYPE_NULL_DATA 0x48 // Null Data
+
+struct tx_frame_hdr {
+#if 1
+ unsigned int modulation_type:8;
+ unsigned int length:9;
+ unsigned int pad:15;
+#else
+ uint8_t modulation_type;
+ __le16 length:9;
+ unsigned int pad:15;
+#endif
+} __attribute__((packed));
+
+
+#define MOD_TYPE_PSKCCK 0x00
+#define MOD_TYPE_OFDM 0xee
+
+struct psk_cck_hdr {
+ uint8_t signal; /* x100Kbps */
+ uint8_t service;
+ __le16 length; /* usecs */
+} __attribute__((packed));
+
+/* PSK/CCK PLCP service field bits */
+#define PLCP_SERVICE_LOCKED 0x04 /* locked clocks */
+#define PLCP_SERVICE_MODSEL 0x08 /* modulation selection */
+#define PLCP_SERVICE_LENEXT 0x80 /* length extension */
+
+struct ofdm_hdr {
+ unsigned int rate:4;
+ unsigned int pad_a:1;
+ unsigned int length:12; /* in bytes */
+ unsigned int parity:1;
+ unsigned int pad_b:14;
+} __attribute__((packed));
+
+
+struct rx_frame_hdr {
+ uint8_t modulation_type;
+ unsigned int rssi_variable_gain_attenuator:5;
+ unsigned int rssi_low_noise_amp:2;
+ unsigned int antenna:1;
+ __be16 freq_offset;
+ union
+ {
+ struct psk_cck_hdr psk;
+ struct ofdm_hdr ofdm;
+ } mod;
+} __attribute__((packed));
+
+typedef struct
+{
+ unsigned type :8; // Type, subtype, version
+ unsigned toDS :1; // To distribution service (AP)
+ unsigned fromDS :1; // From distribution service (AP)
+ unsigned moreFrag :1; // More fragments
+ unsigned retry :1; // Retransmission
+ unsigned pwrMgt :1; // Power management state
+ unsigned moreData :1; // More data buffered
+ unsigned protectd :1; // Encrypted
+ unsigned order :1; // Strictly ordered
+} frameControlFieldType_t;
+
+#define WLN_ADDR_SIZE (6)
+
+typedef unsigned char MacAddr[WLN_ADDR_SIZE];
+
+
+#define PACKED_H
+#define PACKED_F __attribute__ ((packed))
+
+typedef PACKED_H struct {
+ unsigned type :8; // Type, subtype, version
+#ifdef BIG_ENDIAN
+ unsigned order :1; // Strictly ordered
+ unsigned protected :1; // Encrypted
+ unsigned moreData :1; // More data buffered
+ unsigned pwrMgt :1; // Power management state
+ unsigned retry :1; // Retransmission
+ unsigned moreFrag :1; // More fragments
+ unsigned fromDS :1; // From distribution service (AP)
+ unsigned toDS :1; // To distribution service (AP)
+#else
+ unsigned toDS :1; // To distribution service (AP)
+ unsigned fromDS :1; // From distribution service (AP)
+ unsigned moreFrag :1; // More fragments
+ unsigned retry :1; // Retransmission
+ unsigned pwrMgt :1; // Power management state
+ unsigned moreData :1; // More data buffered
+ unsigned protected :1; // Encrypted
+ unsigned order :1; // Strictly ordered
+#endif
+} PACKED_F FrameControl_t;
+
+
+// Sequence control field
+// Need to swap bytes on BIG_ENDIAN
+typedef PACKED_H struct {
+#ifdef BIG_ENDIAN
+ unsigned seq :12; // Sequence number
+ unsigned frag :4; // Fragment number
+#else
+ unsigned frag :4; // Fragment number
+ unsigned seq :12; // Sequence number
+#endif
+} PACKED_F SeqControl;
+
+// Union of sequence control types
+typedef PACKED_H union {
+ SeqControl sq; // Sequence control fields
+ unsigned short sq16; // Sequence control as 16-bit int (needs byte swap)
+} PACKED_F SeqControlU;
+
+
+
+typedef PACKED_H struct {
+ FrameControl_t fc; // Frame control
+ unsigned short duration; // Duration/ID (needs byte swap)
+ MacAddr addr1; // Address 1
+ MacAddr addr2; // Address 2
+ MacAddr addr3; // Address 3
+ SeqControlU squ; // Sequence control fields
+} PACKED_F _80211HeaderType;
+
+typedef PACKED_H struct {
+ FrameControl_t fc; // Frame control
+ unsigned short aid; // association identifier
+ MacAddr addr1; // Address 1
+ MacAddr addr2; // Address 2
+} PACKED_F _80211PSPollType;
+
+#define _80211_HEADER_LENGTH (sizeof(_80211HeaderType))
+#define TX_HEADER_LENGTH (sizeof(struct ofdm_hdr) + sizeof(struct tx_frame_hdr))
+/* FIFO sizes in bytes */
+#define TX_FIFO_SIZE 1792
+#define RX_FIFO_SIZE 2048
+
+#define RATE_MASK_BASIC 0x0153
+#define RATE_MASK_OFDM 0x0ff0
+#define RATE_MASK_PSK_CCK 0x000f
+
+#define BEACON_INT 100 /* in TU */
+
+
+#define DEFAULT_CW_MIN 32 // Min contention window size
+#define DEFAULT_CW_MAX 1024 // Max contention window size
+
+#define ASLOT_TIME 20
+#endif
diff --git a/drivers/net/wireless/digiPiper/phy.c b/drivers/net/wireless/digiPiper/phy.c
new file mode 100644
index 000000000000..5a6c53638e81
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/phy.c
@@ -0,0 +1,269 @@
+/*
+ * Linux device driver for Digi's WiWave WLAN card
+ *
+ * Copyright © 2008 Digi International, Inc
+ *
+ * Author: Andres Salomon <dilinger@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airoha.h"
+#include "phy.h"
+
+#define PHY_DEBUG (0)
+
+#if PHY_DEBUG
+static int dlevel = DVERBOSE;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+#define NUMBER_OF_WORD32(x) ((x + 3) >> 2)
+
+static int is_ofdm_rate(int rate)
+{
+ return (rate % 3) == 0;
+}
+
+void phy_set_plcp(unsigned char *frame, unsigned length, struct ieee80211_rate *rate, int aes_len)
+{
+ int ofdm = is_ofdm_rate(rate->bitrate);
+ int plcp_len = length + FCS_LEN + aes_len;
+ struct tx_frame_hdr *hdr;
+
+ if (ofdm) {
+ /* OFDM header */
+ struct ofdm_hdr *ofdm;
+
+ ofdm = (struct ofdm_hdr *) &frame[sizeof(struct tx_frame_hdr)];
+ memset(ofdm, 0, sizeof(*ofdm));
+ ofdm->rate = rate->hw_value;
+ ofdm->length = cpu_to_le16(plcp_len);
+ } else {
+ /* PSK/CCK header */
+ struct psk_cck_hdr *pskcck;
+ int us_len;
+
+ pskcck = (struct psk_cck_hdr *) &frame[sizeof(struct tx_frame_hdr)];
+ pskcck->signal = rate->bitrate;
+ pskcck->service = PLCP_SERVICE_LOCKED;
+
+ /* convert length from bytes to usecs */
+ switch (rate->bitrate) {
+ case 10:
+ us_len = plcp_len * 8;
+ break;
+ case 20:
+ us_len = plcp_len * 4;
+ break;
+ case 55:
+ us_len = (16 * plcp_len + 10) / 11;
+ break;
+ case 110:
+ us_len = (8 * plcp_len + 10) / 11;
+
+ /* set length extension bit if needed */
+ dprintk(DALL, "us_len = %d, plcp_len = %d, (11 * us_len) = %d, \
+ (11 * us_len) / 8 = %d\n", us_len, plcp_len,
+ (11 * us_len), (11 * us_len) / 8);
+
+ if ((11 * us_len) / 8 > plcp_len) {
+ pskcck->service |= PLCP_SERVICE_LENEXT;
+ dprintk(DALL, "Set PLCP_SERVICE_LENEXT, \
+ pskcck->service = 0x%4.4X\n", pskcck->service);
+ } else {
+ dprintk(DALL, "Did not set PLCP_SERVICE_LENEXT, \
+ pskcck->service = 0x%4.4X\n", pskcck->service);
+ }
+ break;
+ default:
+ digi_dbg("rate = %p, rate->bitrate%d\n", rate, rate->bitrate);
+ WARN_ON(1);
+ us_len = 0;
+ }
+
+ pskcck->length = cpu_to_le16(us_len);
+
+ dprintk(DALL, "pskcck .length = %d, signal = %d, service = %d\n",
+ pskcck->length, pskcck->signal, pskcck->service);
+ dprintk(DALL, "rate->bitrate=%x (@%dM), pckcck->length=%d\n",
+ rate->bitrate, rate->bitrate/10, pskcck->length);
+ }
+
+ hdr = (struct tx_frame_hdr *) frame;
+ hdr->pad = 0;
+ hdr->length = NUMBER_OF_WORD32((length + aes_len + TX_HEADER_LENGTH));
+ hdr->modulation_type = ofdm ? MOD_TYPE_OFDM : MOD_TYPE_PSKCCK;
+
+ dprintk(DVVERBOSE, "frame hdr .length = %d, .modulation_type = %d\n",
+ hdr->length, hdr->modulation_type);
+
+ dprintk(DVERBOSE, "TX: %d byte %s packet @ %dmbit\n", length,
+ ofdm ? "OFDM" : "PSK/CCK", rate->bitrate/10);
+}
+EXPORT_SYMBOL_GPL(phy_set_plcp);
+
+
+static int get_signal(struct rx_frame_hdr *hdr, enum ieee80211_band rf_band, int transceiver)
+{
+ int gain;
+ int signal;
+
+ if (transceiver == RF_AIROHA_2236) {
+ const u8 lnaTable_al2236[] =
+ {
+ 0, 0, 20, 36
+ };
+
+ // Map high gain values to dbm
+ const signed char gainTable_al2236[] =
+ {
+ -85, -85, -88, -88, -92
+ };
+ // Convert received signal strength to dbm
+ gain = lnaTable_al2236[hdr->rssi_low_noise_amp] + 2*hdr->rssi_variable_gain_attenuator;
+ if (gain > 92)
+ signal = -96;
+ else if (gain > 87)
+ signal = gainTable_al2236[gain - 88];
+ else
+ signal = 4 - gain;
+ } else {
+ static const u8 lnaTable_al7230_24ghz[] =
+ {
+ 0, 0, 18, 42
+ };
+ static const u8 lnaTable_al7230_50ghz[] =
+ {
+ 0, 0, 17, 37
+ };
+ /* Convert received signal strength to dbm for RF_AIROHA_7230 */
+ if (rf_band == IEEE80211_BAND_2GHZ) {
+ gain = lnaTable_al7230_24ghz[hdr->rssi_low_noise_amp] + 2*hdr->rssi_variable_gain_attenuator;
+ signal = 2 - gain;
+ } else {
+ gain = lnaTable_al7230_50ghz[hdr->rssi_low_noise_amp] + 2*hdr->rssi_variable_gain_attenuator;
+ signal = -5 - gain;
+ }
+ }
+
+ return signal;
+}
+
+
+
+
+/* FIXME, following limtis should depend on the platform */
+#define PIPER_MAX_SIGNAL_DBM (-30)
+#define PIPER_MIN_SIGNAL_DBM (-90)
+
+static int calculate_link_quality(int signal)
+{
+ int quality;
+
+ if (signal < PIPER_MIN_SIGNAL_DBM)
+ quality = 0;
+ else if (signal > PIPER_MAX_SIGNAL_DBM)
+ quality = 100;
+ else
+ quality = (signal - PIPER_MIN_SIGNAL_DBM) * 100 /
+ (PIPER_MAX_SIGNAL_DBM - PIPER_MIN_SIGNAL_DBM);
+
+ dprintk(DVERBOSE, "signal: %d, quality: %d/100\n", signal, quality);
+
+ return quality;
+}
+
+unsigned int phy_determine_rssi(struct rx_frame_hdr *hdr)
+{
+ return (hdr->rssi_low_noise_amp << 5) | hdr->rssi_variable_gain_attenuator;
+}
+EXPORT_SYMBOL_GPL(phy_determine_rssi);
+
+
+
+void phy_process_plcp(struct piper_priv *piper, struct rx_frame_hdr *hdr,
+ struct ieee80211_rx_status *status, unsigned int *length)
+{
+ unsigned rate, i;
+ struct digi_rf_ops *rf = piper->rf;
+
+ memset(status, 0, sizeof(*status));
+ status->band = piper->rf->getBand(piper->channel);
+ status->signal = get_signal(hdr, status->band, piper->pdata->rf_transceiver);
+ status->antenna = hdr->antenna;
+ status->freq = piper->rf->getFrequency(piper->channel);
+ status->qual = calculate_link_quality(status->signal);
+
+ if (hdr->modulation_type == MOD_TYPE_OFDM) {
+ /* OFDM */
+ struct ofdm_hdr *ofdm = &hdr->mod.ofdm;
+ const int ofdm_rate[] = {
+ 480, 240, 120, 60, 540, 360, 180, 90
+ };
+
+ rate = ofdm_rate[ofdm->rate & 0x7];
+ *length = le16_to_cpu(ofdm->length);
+ dprintk(DVVERBOSE, "%d byte OFDM packet @ %dmbit\n",
+ *length, rate/10);
+ } else {
+ /* PSK/CCK */
+ struct psk_cck_hdr *pskcck = &hdr->mod.psk;
+
+ rate = pskcck->signal;
+
+ *length = le16_to_cpu(pskcck->length);
+ switch (rate) {
+ case 10:
+ *length /= 8;
+ break;
+ case 20:
+ *length /= 4;
+ break;
+ case 55:
+ *length = (11 * (*length)) / 16;
+ break;
+ case 110:
+ *length = (11 * (*length)) / 8;
+ if (pskcck->service & PLCP_SERVICE_LENEXT)
+ (*length)--;
+ break;
+ default:
+ /* WARN_ON(1); This happens to often for us to generate that long error message */
+ *length = 0;
+ }
+
+ dprintk(DVVERBOSE, "%d byte PSK/CCK packet @ %dmbit\n",
+ *length, rate/10);
+ }
+
+ /* match rate with the list of bitrates that we supplied the stack */
+ for (i = 0; i < rf->bands[status->band].n_bitrates; i++) {
+ if (rf->bands[status->band].bitrates[i].bitrate == rate)
+ break;
+ }
+
+ if (i != rf->bands[status->band].n_bitrates)
+ status->rate_idx = i;
+ else {
+ *length = 0;
+ digi_dbg(PIPER_DRIVER_NAME
+ ": couldn't find bitrate index for %d?\n",
+ rate);
+ status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(phy_process_plcp);
diff --git a/drivers/net/wireless/digiPiper/phy.h b/drivers/net/wireless/digiPiper/phy.h
new file mode 100644
index 000000000000..96500ab92e3e
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/phy.h
@@ -0,0 +1,28 @@
+/*
+ * Linux device driver for Digi's WiWave WLAN card
+ *
+ * Copyright © 2008 Digi International, Inc
+ *
+ * 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 phy_h_
+#define phy_h_
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+
+#include "pipermain.h"
+#include "mac.h"
+
+void phy_set_plcp(unsigned char *frame, unsigned length, struct ieee80211_rate *rate, int aes_len);
+void phy_process_plcp(struct piper_priv *piper, struct rx_frame_hdr *hdr,
+ struct ieee80211_rx_status *status, unsigned int *length);
+unsigned int phy_determine_rssi(struct rx_frame_hdr *hdr);
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/piper.c b/drivers/net/wireless/digiPiper/piper.c
new file mode 100644
index 000000000000..a3c24720dd11
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piper.c
@@ -0,0 +1,1032 @@
+/*
+ * piper.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <linux/usb.h>
+#include <linux/kthread.h>
+#include <linux/platform_device.h>
+#include <asm/gpio.h>
+#include <linux/timer.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "airoha.h"
+#include "airohaCalibration.h"
+#include "piperDsp.h"
+#include "piperMacAssist.h"
+#include "digiPs.h"
+
+#define WANT_AIROHA_CALIBRATION (1)
+#define WANT_DEBUG_COMMANDS (1)
+
+
+static void piper_clear_irq_mask(struct piper_priv *piperp, unsigned int bits)
+{
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, ~bits, op_and);
+}
+
+static void piper_set_irq_mask(struct piper_priv *piperp, unsigned int bits)
+{
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, bits, op_or);
+}
+
+/* Generate a random number */
+static int local_rand(void)
+{
+ static unsigned long next = 1;
+
+ /* RAND_MAX assumed to be 32767 */
+ next = next * 1103515245 + 12345;
+ return((unsigned)(next/65536) % 32768);
+}
+
+/*
+ * Load the MAC Assist firmware into the chip. This is done by setting a bit
+ * in the control register to enable MAC Assist firmware download, and then
+ * writing the firmware into the data FIFO.
+ */
+void piper_load_mac_firmware(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG PIPER_DRIVER_NAME ": loading MAC Assist firmware\n");
+
+ /* Zero out MAC assist SRAM (put into known state before enabling MAC assist) */
+ for (i = 0; i < 0x100; i += 4)
+ piperp->ac->wr_reg(piperp, i, 0, op_write);
+
+ /* Enable download the MAC Assist program RAM */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_FW_LOAD_ENABLE, op_or);
+
+ /* load MAC Assist data */
+ for (i = 0; i < piper_macassist_data_len; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, piper_wifi_macassist_ucode[i],
+ op_write);
+
+ /* disable MAC Assist download */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_FW_LOAD_ENABLE, op_and);
+}
+
+/*
+ * Load the DSP firmware into the chip. This is done by setting a bit
+ * in the control register to enable DSP firmware download, and then
+ * writing the firmware into the data FIFO.
+ */
+void piper_load_dsp_firmware(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG PIPER_DRIVER_NAME ": loading DSP firmware\n");
+
+ /* Enable load of DSP firmware */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_DSP_LOAD_ENABLE, op_or);
+
+ /* load DSP data */
+ for (i = 0; i < piper_dsp_data_len; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, piper_wifi_dsp_ucode[i],
+ op_write);
+
+ /* Disable load of DSP firmware */
+ udelay(10);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_DSP_LOAD_ENABLE, op_and);
+
+ /* Let her rip */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_MAC_ASSIST_ENABLE, op_or);
+}
+
+
+/*
+ * This routine corrects a bug in the Piper chip where internal clocks would
+ * be out of sync with each other and cause the chip to generate noise spikes.
+ * This problem should be fixed in the next chip (Chopper).
+ *
+ * I'm not sure exactly what this code is doing. It comes straight from the
+ * guy who designed the chip.
+ */
+int piper_spike_suppression(struct piper_priv *piperp, bool retry)
+{
+ int timeout1 = 300, timeout2 = 300;
+ int ret = 0;
+
+ /*
+ * Initial timing measurement to avoid spike
+ * The new "magic" value is 0x63 at address 0xA62. Bit-0 indicates the
+ * timing measurement is complete. Bit-1 indicates that a second timing
+ * measurment was performed. The upper nibble is the timing measurement
+ * value. This code should eliminate the possibility of spikes at the
+ * beginning of all PSK/CCK frames and eliminate the spikes at the end of
+ * all PSK (1M, 2M) frames.
+ */
+
+ /* reset the timing value */
+ piperp->ac->wr_reg(piperp, MAC_STATUS, 0xffff00ff, op_and);
+
+ while ((piperp->ac->rd_reg(piperp, MAC_STATUS) & 0x0000ff00) != 0x00006300) {
+
+ /* reset the timing value */
+ piperp->ac->wr_reg(piperp, MAC_STATUS, 0xffff00ff, op_and);
+
+ /* issue WiFi soft reset */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, 0x40000000, op_write);
+
+ /* Set TX_ON Low */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xffffff3f, op_and);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x00000080, op_or);
+
+ /* Set PA_2G Low */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xfffff0ff, op_and);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x00000a00, op_or);
+
+ /* Set RX_ON low */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xcfffffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x20000000, op_or);
+
+ /* start the WiFi mac & dsp */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720820, op_write);
+ timeout1 = 500;
+
+ /* Wait for timing measurement to finish */
+ while ((piperp->ac->rd_reg(piperp, MAC_STATUS) & 0x0000ff00) != 0x00000100) {
+ udelay(2);
+ timeout1--;
+ if (!timeout1)
+ break;
+ }
+
+ timeout2--;
+ if (!timeout2) {
+ ret = -EIO;
+ break;
+ }
+
+ if (!retry) {
+ ret = -EIO;
+ break;
+ }
+ }
+
+ /* Set TX_ON/RXHP_ON and RX to normal wifi, restore the reset value to HW_OUT_CTRL */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x1, op_write);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(piper_spike_suppression);
+
+void piper_reset_mac(struct piper_priv *piperp)
+{
+ int i;
+
+ /* set the TX-hold bit */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720080, op_write);
+
+ /* clear the TX-FIFO memory */
+ for (i = 0; i < 448; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write);
+
+ /* reset the TX-FIFO */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x377200C0, op_write);
+
+ /* release the TX-hold and reset */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720000, op_write);
+
+/* iowrite32(ioread32(piperp->vbase + MAC_STATUS) & ~0x40000000,
+ piperp->vbase + BB_GENERAL_STAT);*/
+ mdelay(1);
+}
+
+/*
+ * Load the MAC address into the chip. Use the value stored in the
+ * environment, if there is one, otherwise use the default value.
+ */
+void piper_set_macaddr(struct piper_priv *piperp)
+{
+ /* Default MAC Addr used if the nvram parameters are corrupted */
+ u8 mac[6] = {0x00, 0x04, 0xf3, 0x11, 0x43, 0x35};
+ u8 *pmac = piperp->pdata->macaddr;
+ int i;
+ static bool firstTime = true;
+
+ for (i = 0; i < 6; i++) {
+ if (*(pmac + i) != 0xff)
+ break;
+ if (i == 5) {
+ /* There is a problem with the parameters, use default */
+ if (firstTime) {
+ printk(KERN_INFO PIPER_DRIVER_NAME
+ ": invalid mac address, using default\n");
+ }
+ memcpy(piperp->pdata->macaddr, mac, sizeof(piperp->pdata->macaddr));
+ }
+ }
+
+ firstTime = false;
+ memcpy(piperp->hw->wiphy->perm_addr, piperp->pdata->macaddr,
+ sizeof(piperp->hw->wiphy->perm_addr));
+
+ /* configure ethernet address */
+ piperp->ac->wr_reg(piperp, MAC_STA_ID0, *(pmac + 3) | *(pmac + 2) << 8 |
+ *(pmac + 1) << 16 | *(pmac + 0) << 24, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA_ID1, *(pmac + 5) << 16 | *(pmac + 4) << 24,
+ op_write);
+}
+EXPORT_SYMBOL_GPL(piper_set_macaddr);
+
+
+/* Configure the H/W with the antenna settings */
+static int piper_set_antenna(struct piper_priv *piperp, enum antenna_select sel)
+{
+ if (sel == ANTENNA_BOTH) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_ANT_DIV, op_or);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_ANT_SEL, op_and);
+ } else {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_ANT_DIV, op_and);
+ /* select the antenna if !diversity */
+ if (sel == ANTENNA_1)
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_ANT_SEL, op_and);
+ else
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_ANT_SEL, op_or);
+ }
+
+ /* select which antenna to transmit on */
+ piperp->ac->wr_reg(piperp, BB_RSSI, ~BB_RSSI_ANT_MASK, op_and);
+ if (sel == ANTENNA_BOTH)
+ piperp->ac->wr_reg(piperp, BB_RSSI, BB_RSSI_ANT_DIV_MAP, op_or);
+ else
+ piperp->ac->wr_reg(piperp, BB_RSSI, BB_RSSI_ANT_NO_DIV_MAP, op_or);
+
+ return 0;
+}
+
+/*
+ * Compute a beacon backoff time as described in section 11.1.2.2 of 802.11 spec.
+ *
+ */
+static u16 get_next_beacon_backoff(void)
+{
+#define MAX_BEACON_BACKOFF (2 * ASLOT_TIME * DEFAULT_CW_MIN)
+
+ /*
+ * We shift the result of local_rand() by 4 bits because the notes
+ * for the algorithm say that we shouldn't rely on the last few
+ * bits being random. Other than that, we just take the random
+ * value and make sure it is less than MAX_BEACON_BACKOFF.
+ */
+ return (local_rand() >> 4) % MAX_BEACON_BACKOFF;
+}
+
+static int load_beacon(struct piper_priv *digi, unsigned char *buffer,
+ unsigned int length)
+{
+ return digi->ac->wr_fifo(digi, BEACON_FIFO, buffer, length);
+}
+
+static int piper_init_rx_tx(struct piper_priv *piperp)
+{
+ tasklet_init(&piperp->rx_tasklet, piper_rx_tasklet, (unsigned long)piperp);
+ tasklet_disable(&piperp->rx_tasklet);
+ piperp->expectingAck = false;
+
+ spin_lock_init(&piperp->tx_tasklet_lock);
+ spin_lock_init(&piperp->tx_queue_lock);
+ piperp->tx_tasklet_running = false;
+ memset(&piperp->tx_queue, 0, sizeof(piperp->tx_queue));
+ piperp->tx_queue_head = 0;
+ piperp->tx_queue_tail = 0;
+ piperp->tx_queue_count = 0;
+ tasklet_init(&piperp->tx_tasklet, piper_tx_tasklet, (unsigned long)piperp);
+ tasklet_disable(&piperp->tx_tasklet);
+
+ return 0;
+}
+
+static void piper_free_rx_tx(struct piper_priv *piperp)
+{
+ tasklet_disable(&piperp->rx_tasklet);
+ tasklet_kill(&piperp->rx_tasklet);
+ tasklet_disable(&piperp->tx_tasklet);
+ tasklet_kill(&piperp->tx_tasklet);
+ piper_empty_tx_queue(piperp);
+}
+
+/*
+ * This function sets the tracking control according to a channel's
+ * frequency.
+ */
+static int piper_set_tracking_constant(struct piper_priv *piperp, unsigned megahertz)
+{
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, ~TRACK_CONSTANT_MASK, op_and);
+ if (megahertz < 4920)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+ }
+ else if (megahertz <= 4980)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_4920_4980_A_BAND, op_or);
+ }
+ else if (megahertz <= 5350)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5150_5350_A_BAND, op_or);
+ }
+ else if (megahertz <= 5725)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5470_5725_A_BAND, op_or);
+ }
+ else
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5725_5825_A_BAND, op_or);
+ }
+
+ return 0;
+}
+
+/*
+ * This function is called to set the value of the B_TX_GAIN field of the
+ * HW_CONF1 mac register. This register must be set to different values depending
+ * on the H/W revision of the board due to changes in the board design.
+ */
+static unsigned int get_b_tx_gain(struct piper_priv *piperp)
+{
+ u16 platform = piperp->pdata->wcd.header.hw_platform & WCD_PLATFORM_MASK;
+ u16 hw_revision = piperp->pdata->wcd.header.hw_platform & WCD_HW_REV_MASK;
+ unsigned int tx_gain = 0;
+
+ switch (platform) {
+ case WCD_CCW9P_PLATFORM:
+ tx_gain = TRACK_TX_B_GAIN_NORMAL;
+ break;
+ case WCD_CCW9M_PLATFORM:
+ switch (hw_revision) {
+ case WCD_HW_REV_PROTOTYPE:
+ case WCD_HW_REV_PILOT:
+ tx_gain = 0xc0000000;
+ break;
+ case WCD_HW_REV_A:
+ default:
+ tx_gain = 0x90000000;
+ break;
+ }
+ break;
+ }
+ return tx_gain;
+}
+
+
+static int piper_init_hw(struct piper_priv *piperp, enum ieee80211_band band)
+{
+ int ret = 0;
+
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_INIT, op_write);
+
+ /* Initialize baseband general control register for the specific transceiver */
+ if (piperp->pdata->rf_transceiver == RF_AIROHA_7230) {
+ if (band == IEEE80211_BAND_2GHZ) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_24GHZ, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+ digi_dbg("piper_init_hw Initialized for band B / BG\n");
+ } else {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_50GHZ, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5150_5350_A_BAND, op_or);
+ digi_dbg("piper_init_hw Initialized for band A\n");
+ }
+ piperp->ac->wr_reg(piperp, BB_CONF_2, 0x09325ad4, op_write);
+ /* Initialize the SPI word length */
+ piperp->ac->wr_reg(piperp, BB_SPI_CTRL, SPI_INIT_AIROHA, op_write);
+ } else if (piperp->pdata->rf_transceiver == RF_AIROHA_2236) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_24GHZ, op_write);
+ piperp->ac->wr_reg(piperp, BB_CONF_2, 0x09325ad4, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+ /* Initialize the SPI word length */
+ piperp->ac->wr_reg(piperp, BB_SPI_CTRL, SPI_INIT_AIROHA2236, op_write);
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME ": undefined rf transceiver!\n");
+ return -EINVAL;
+ }
+
+ /*
+ *Clear the Intretupt Mask Register before enabling external intretupts.
+ * Also clear out any status bits in the Intretupt Status Register.
+ */
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);
+
+ /*
+ * If this firmware supports additional MAC addresses.
+ */
+ if (((piperp->ac->rd_reg(piperp, MAC_STATUS) >> 16) & 0xff) >= 8) {
+ /* Disable additional addresses to start with */
+ piperp->ac->wr_reg(piperp, MAC_CTL, ~MAC_CTL_MAC_FLTR, op_and);
+ piperp->ac->wr_reg(piperp, MAC_STA2_ID0, 0, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA2_ID1, 0, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA3_ID0, 0, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA3_ID1, 0, op_write);
+ }
+ /* TODO: Set this register programatically */
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, 0x0, op_write);
+
+ /*
+ * Note that antenna diversity will be set by hw_start, which is the
+ * caller of this function.
+ */
+
+ /* reset RX and TX FIFOs */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_RXFIFORST
+ | BB_GENERAL_CTL_TXFIFORST, op_or);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~(BB_GENERAL_CTL_RXFIFORST
+ | BB_GENERAL_CTL_TXFIFORST), op_and);
+
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xC043002C, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, ~TRACK_TX_B_GAIN_MASK, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, get_b_tx_gain(piperp), op_or);
+
+ /* Initialize RF transceiver */
+ piperp->rf->init(piperp->hw, band);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x04000001, op_or);
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, 0x0, op_write);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, ~(BB_GENERAL_STAT_DC_DIS
+ | BB_GENERAL_STAT_SPRD_DIS), op_and);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, ~(BB_GENERAL_STAT_SRC_DIS
+ | BB_GENERAL_STAT_DLL_DIS), op_and);
+
+ piperp->ac->wr_reg(piperp, MAC_SSID_LEN, (MAC_OFDM_BRS_MASK | MAC_PSK_BRS_MASK),
+ op_write);
+
+ /*
+ * Set BSSID to the broadcast address so that we receive all packets. The stack
+ * will set a real BSSID when it's ready.
+ */
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID0, 0xffffffff, op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID1, 0xffffffff, op_write);
+
+ piperp->ps.poweredDown = false;
+
+#if WANT_AIROHA_CALIBRATION
+ digi_dbg("Calling digiWifiInitCalibration()\n");
+ digiWifiInitCalibration(piperp);
+#endif
+
+ return ret;
+}
+
+static int piper_deinit_hw(struct piper_priv *piperp)
+{
+ int ret = 0;
+#if WANT_AIROHA_CALIBRATION
+ digi_dbg("Calling digiWifiDeInitCalibration()\n");
+ digiWifiDeInitCalibration(piperp);
+#endif
+
+ return ret;
+}
+
+
+static void adjust_max_agc(struct piper_priv *piperp, unsigned int rssi, _80211HeaderType *header)
+{
+#define LOWEST_MAXAGC_AL2236 0x76
+#define HIGHEST_MAXAGC_AL2236 0x7B
+#define HIGHEST_MAXAGC_AL7230_24GHZ 0x7c
+#define LOWEST_MAXAGC_AL7230_24GHZ 0x76
+#define HIGHEST_MAXAGC_AL7230_50GHZ 0x79
+#define LOWEST_MAXAGC_AL7230_50GHZ 0x73
+#define RSSI_AVG_COUNT 8
+
+ unsigned char maxgain = 0;
+ static unsigned char lowest = 0, highest = 0;
+ static int k=0, j=0, i =0, tempRssi=0;
+ static unsigned int savedRSSI[RSSI_AVG_COUNT]; /****/
+
+ savedRSSI[k % RSSI_AVG_COUNT] = rssi;
+ if ( (piperp->pdata->rf_transceiver == RF_AIROHA_2236)
+ || (piperp->pdata->rf_transceiver == RF_AIROHA_7230)) {
+
+ if (piperp->pdata->rf_transceiver == RF_AIROHA_2236)
+ {
+ lowest = LOWEST_MAXAGC_AL2236;
+ highest = HIGHEST_MAXAGC_AL2236;
+ }
+ else
+ {
+
+ if (piperp->rf->getBand(piperp->channel) == IEEE80211_BAND_5GHZ) {
+ highest = HIGHEST_MAXAGC_AL7230_50GHZ;
+ lowest = LOWEST_MAXAGC_AL7230_50GHZ;
+ }
+ else {
+ highest = HIGHEST_MAXAGC_AL7230_24GHZ;
+ lowest = LOWEST_MAXAGC_AL7230_24GHZ;
+ }
+ }
+
+ if (piperp->areWeAssociated)
+ {
+
+ if ( (piperp->if_type == NL80211_IFTYPE_ADHOC)
+ || (piperp->if_type == NL80211_IFTYPE_MESH_POINT))
+ {
+ //Monitor the receive signal strength from Ad-hoc network
+ if (memcmp (piperp->bssid, header->addr3, sizeof(piperp->bssid)) == 0)
+ {
+ /* we don't do avareging on all the signals here because it may come from different
+ * unit in that Ad-hoc network. Instead, we do avareging on the signals with higher rssi
+ */
+
+ if ((rssi + 4) > lowest)
+ {
+ k++;
+ tempRssi += rssi;
+
+ if (k >= RSSI_AVG_COUNT)
+ {
+ maxgain = (((tempRssi/k) + 4) > highest)? highest : ((tempRssi/k) + 4) ;
+ k = 0;
+ tempRssi = 0;
+ i =0;
+ }
+ }
+ else
+ {
+ i++;
+ if (i >= (RSSI_AVG_COUNT*4))
+ {
+ maxgain = lowest;
+ i = 0;
+ }
+
+ }
+ }
+ }
+ else
+ {
+ //Monitor the receive signal strength from the frames we received from the associated AP
+ if (memcmp (piperp->bssid, header->addr2, sizeof(piperp->bssid)) == 0)
+ {
+ //averaging all the signals because they come from the same AP
+ k++;
+ tempRssi += rssi;
+
+ if (k >= RSSI_AVG_COUNT*2)
+ {
+ if (((tempRssi/k) + 4) > lowest)
+ maxgain = (((tempRssi/k) + 4) > highest)? highest : ((tempRssi/k) + 4) ;
+ else
+ maxgain = lowest;
+
+ k = 0;
+ tempRssi = 0;
+ }
+ }
+ }
+ j = 0;
+ }
+ else
+ {
+ j++;
+ if (j >= (RSSI_AVG_COUNT*4))
+ {
+ maxgain = highest;
+ j = 0;
+ }
+ k = 0;
+ tempRssi = 0;
+ }
+
+ if( (maxgain != 0)
+ && (maxgain != ((piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_MAX_GAIN_MASK) >> 16)))
+ {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_MAX_GAIN_MASK, op_and);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, (maxgain << 16) & BB_GENERAL_CTL_MAX_GAIN_MASK, op_or);
+ }
+ }
+}
+
+
+/* Make sure all keys are disabled when we start */
+static void piper_init_keys(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ for (i = 0; i < PIPER_MAX_KEYS; i++)
+ piperp->key[i].valid = false;
+
+ piperp->aes_key_count = 0;
+}
+
+static void tx_timer_timeout(unsigned long arg)
+{
+ struct piper_priv *piperp = (struct piper_priv *) arg;
+
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+}
+
+/* sysfs entries to get/set antenna mode */
+static ssize_t show_antenna_sel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", piperp->antenna == ANTENNA_BOTH ? "diversity" :
+ piperp->antenna == ANTENNA_1 ? "primary" : "secondary");
+}
+
+static ssize_t store_antenna_sel(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+ enum antenna_select ant;
+ size_t len = count;
+ int ret;
+
+ ant = piperp->antenna;
+
+ if (buf[count - 1] == '\n')
+ len--;
+
+ /* TODO check also string length */
+ if (!strncmp("diversity", buf, len))
+ ant = ANTENNA_BOTH;
+ else if (!strncmp("primary", buf, len))
+ ant = ANTENNA_1;
+ else if (!strncmp("secondary", buf, len))
+ ant = ANTENNA_2;
+
+ if (ant != piperp->antenna) {
+ if ((ret = piperp->set_antenna(piperp, ant)) != 0) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error setting antenna to %d (err: %d)\n", ant, ret);
+ } else
+ piperp->antenna = ant;
+ }
+
+ return count;
+}
+static DEVICE_ATTR(antenna_sel, S_IWUSR | S_IRUGO, show_antenna_sel, store_antenna_sel);
+
+static ssize_t show_power_duty(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", piperp->power_duty);
+}
+
+static ssize_t store_power_duty(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#define MINIMUM_DUTY_CYCLE (33)
+#define LIMIT_LINEAL_DUTY_CYCLE (75)
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+ int pw_duty;
+ ssize_t ret = -EINVAL;
+
+ ret = sscanf(buf, "%d\n", &pw_duty);
+ if (ret > 0) {
+ if (pw_duty < MINIMUM_DUTY_CYCLE) {
+ piperp->power_duty = MINIMUM_DUTY_CYCLE;
+ } else if (pw_duty > LIMIT_LINEAL_DUTY_CYCLE && pw_duty < 100) {
+ piperp->power_duty = LIMIT_LINEAL_DUTY_CYCLE;
+ } else if (pw_duty == 100 ||
+ (pw_duty >= MINIMUM_DUTY_CYCLE && pw_duty <= LIMIT_LINEAL_DUTY_CYCLE)) {
+ piperp->power_duty = pw_duty;
+ }
+ }
+
+ return ret < 0 ? ret : count;
+}
+static DEVICE_ATTR(power_duty, S_IWUSR | S_IRUGO, show_power_duty, store_power_duty);
+
+#if WANT_DEBUG_COMMANDS
+
+static ssize_t show_debug_cmd(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", piperp->debug_cmd);
+}
+
+static ssize_t store_debug_cmd(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+ ssize_t ret = -EINVAL;
+
+ if (strlen(buf) < sizeof(piperp->debug_cmd))
+ {
+ if (strstr(buf, "dump") != NULL) {
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ ret = 1;
+ } else if (strstr(buf, "ps_state") != NULL) {
+ printk(KERN_ERR "rxTaskletRunning = %d, allowTransmits = %d, stopped_tx_queues = %d\n",
+ piperp->ps.rxTaskletRunning, piperp->ps.allowTransmits, piperp->ps.stopped_tx_queues);
+ ret = 1;
+ } else if (strstr(buf, "rssi_dump") != NULL) {
+ spinlock_t lock;
+ unsigned long flags;
+ unsigned int rssi;
+
+ spin_lock_init(&lock);
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and);
+ udelay(15);
+ rssi = piperp->ac->rd_reg(piperp, BB_RSSI);
+ printk(KERN_ERR "\n**rssi = 0x%8.8X\n", rssi);
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ ret = 1;
+ spin_unlock_irqrestore(&lock, flags);
+ } else {
+ strcpy(piperp->debug_cmd, buf);
+ piperp->debug_cmd[strlen(buf)-1] = 0; /* truncate the \n */
+ }
+ ret = count;
+ }
+
+ return ret < 0 ? ret : count;
+}
+static DEVICE_ATTR(debug_cmd, S_IWUSR | S_IRUGO, show_debug_cmd, store_debug_cmd);
+
+#endif
+
+#ifdef CONFIG_PM
+
+static int piper_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct piper_priv *piperp = platform_get_drvdata(dev);
+ unsigned long flags;
+
+ /* TODO, use in future the ps.lock instead of fully disabling interrupts here */
+ piperp->power_save_was_on_when_suspended = (piperp->ps.mode == PS_MODE_LOW_POWER);
+ if (piperp->power_save_was_on_when_suspended)
+ piper_ps_set(piperp, false);
+ mdelay(10);
+ piper_sendNullDataFrame(piperp, true);
+ ssleep(1);
+
+ local_irq_save(flags);
+ /*
+ * Save power save state and then make sure power save is turned off.
+ */
+ piper_MacEnterSleepMode(piperp, true);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static int piper_resume(struct platform_device *dev)
+{
+ struct piper_priv *piperp = platform_get_drvdata(dev);
+ unsigned long flags;
+
+ if (piperp->pdata->early_resume)
+ piperp->pdata->early_resume(piperp);
+
+ /* TODO, use in future the ps.lock instead of fully disabling interrupts here */
+ local_irq_save(flags);
+ piper_MacEnterActiveMode(piperp, true);
+ if (piperp->tx_tasklet_running) {
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ ieee80211_wake_queues(piperp->hw);
+ }
+ local_irq_restore(flags);
+
+ /*
+ * Restore power save if it was on before
+ */
+ if (piperp->power_save_was_on_when_suspended) {
+ piper_ps_set(piperp, true);
+ } else {
+ piper_sendNullDataFrame(piperp, false);
+ }
+
+ return 0;
+}
+#else
+#define piper_suspend NULL
+#define piper_resume NULL
+#endif
+
+static int __init piper_probe(struct platform_device* pdev)
+{
+ struct piper_pdata *pdata = pdev->dev.platform_data;
+ struct piper_priv *piperp;
+ int ret = 0;
+
+ if (!pdata)
+ return -EINVAL;
+
+ ret = piper_alloc_hw(&piperp, sizeof(*piperp));
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to alloc piper_priv\n");
+ return ret;
+ }
+
+ piperp->ac = kzalloc(sizeof(struct access_ops), GFP_KERNEL);
+ if (!piperp->ac){
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to alloc memory for ac struct\n");
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+
+ piperp->drv_name = PIPER_DRIVER_NAME;
+ dev_set_drvdata(&pdev->dev, piperp);
+ piperp->pdata = pdata;
+ pdata->piperp = piperp;
+ spin_lock_init(&piperp->ac->reg_lock);
+ spin_lock_init(&piperp->aesLock);
+
+ piperp->vbase = ioremap(pdev->resource[0].start,
+ pdev[0].resource->end - pdev->resource[0].start);
+
+ if (!piperp->vbase) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": ioremap base %x, len %x error\n",
+ pdev->resource[0].start, pdev[0].resource->end - pdev->resource[0].start);
+ ret = -ENOMEM;
+ goto error_remap;
+ }
+
+ piperp->pstats.tx_start_count = 0;
+ piperp->pstats.tx_complete_count = 0;
+
+ /*
+ * Platform initialization. This will initialize the hardware, including the load
+ * of the mac and dsp firmware into the piper chip
+ */
+ if (pdata->init) {
+ if ((ret = pdata->init(piperp)) != 0) {
+ printk(KERN_ERR PIPER_DRIVER_NAME
+ ": platform init() returned error (%d)\n", ret);
+ goto error_init;
+ }
+ }
+
+ piper_ps_init(piperp);
+ init_timer(&piperp->tx_timer);
+ piperp->tx_timer.function = tx_timer_timeout;
+ piperp->tx_timer.data = (unsigned long) piperp;
+ piper_init_rx_tx(piperp);
+ piper_init_keys(piperp);
+
+ piperp->init_hw = piper_init_hw;
+ piperp->deinit_hw = piper_deinit_hw;
+ piperp->set_irq_mask_bit = piper_set_irq_mask;
+ piperp->clear_irq_mask_bit = piper_clear_irq_mask;
+ piperp->load_beacon = load_beacon;
+ piperp->rand = local_rand;
+ piperp->get_next_beacon_backoff = get_next_beacon_backoff;
+ piperp->set_antenna = piper_set_antenna;
+ piperp->set_tracking_constant = piper_set_tracking_constant;
+ piperp->antenna = ANTENNA_1;
+ piperp->adjust_max_agc = adjust_max_agc;
+
+ /*
+ * Set the default duty cycle value. Note that duty cycling
+ * is disabled reguardless of what this variable is set to until
+ * the user types "iwconfig wlan0 power on". I just love the
+ * "power on" syntax to turn *down* the power.
+ */
+ piperp->power_duty = 100;
+
+ /* TODO this should be read earlier and actions should be taken
+ * based on different revisions at driver initialization or runtime */
+ piperp->version = piperp->ac->rd_reg(piperp, BB_VERSION);
+
+ piperp->irq = pdev->resource[1].start;
+ piperp->tx_cts = false;
+ piperp->beacon.loaded = false;
+ piperp->beacon.enabled = false;
+ piperp->beacon.weSentLastOne = false;
+
+ ret = request_irq(piperp->irq, piper_irq_handler,
+ IRQF_TRIGGER_HIGH, PIPER_DRIVER_NAME, piperp);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": unable to request irq %d (%d)",
+ piperp->irq, ret);
+ goto retor_irq;
+ }
+
+ disable_irq(piperp->irq);
+
+ ret = piper_register_hw(piperp, &pdev->dev, &al7230_rf_ops);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to register priv\n");
+ goto error_reg_hw;
+ }
+
+ if (pdata->late_init)
+ pdata->late_init(piperp);
+
+ ret = device_create_file(&pdev->dev, &dev_attr_antenna_sel);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n");
+ goto error_sysfs;
+ }
+
+ ret = device_create_file(&pdev->dev, &dev_attr_power_duty);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n");
+ goto error_sysfs;
+ }
+
+ strcpy(piperp->debug_cmd, "off");
+#if WANT_DEBUG_COMMANDS
+ ret = device_create_file(&pdev->dev, &dev_attr_debug_cmd);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n");
+ goto error_sysfs;
+ }
+#endif
+
+ printk(KERN_INFO PIPER_DRIVER_NAME ": driver loaded (fw ver = 0x%08x)\n",
+ piperp->version);
+
+ return 0;
+
+error_sysfs:
+ piper_unregister_hw(piperp);
+error_reg_hw:
+ piper_ps_deinit(piperp);
+ piper_free_rx_tx(piperp);
+retor_irq:
+ free_irq(piperp->irq, piperp);
+error_init:
+ iounmap(piperp->vbase);
+ piperp->vbase = NULL;
+error_remap:
+ release_resource(pdev->resource);
+error_alloc:
+ piper_free_hw(piperp);
+ return ret;
+}
+
+static int piper_remove(struct platform_device *pdev)
+{
+ struct piper_priv *piperp = dev_get_drvdata(&pdev->dev);
+
+ printk(KERN_DEBUG PIPER_DRIVER_NAME " %s\n", __func__);
+
+ device_remove_file(&pdev->dev, &dev_attr_antenna_sel);
+ device_remove_file(&pdev->dev, &dev_attr_power_duty);
+#if WANT_DEBUG_COMMANDS
+ device_remove_file(&pdev->dev, &dev_attr_debug_cmd);
+#endif
+
+ piper_ps_deinit(piperp);
+ piper_unregister_hw(piperp);
+ disable_irq(piperp->irq);
+ piper_clear_irq_mask(piperp, 0xffffffff);
+ free_irq(piperp->irq, piperp);
+ piper_free_rx_tx(piperp);
+ release_resource(pdev->resource);
+ piper_free_hw(piperp);
+
+ return 0;
+}
+
+/* describes the driver */
+static struct platform_driver piper_driver = {
+ .probe = piper_probe,
+ .remove = piper_remove,
+ .suspend = piper_suspend,
+ .resume = piper_resume,
+ .driver = {
+ .name = PIPER_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init piper_init_module(void)
+{
+ return platform_driver_register(&piper_driver);
+}
+
+static void __exit piper_exit_module(void)
+{
+ platform_driver_unregister(&piper_driver);
+}
+
+module_init(piper_init_module);
+module_exit(piper_exit_module);
+
+MODULE_DESCRIPTION("Digi Piper WLAN Driver");
+MODULE_AUTHOR("Contact support@digi.com for questions on this code");
+MODULE_VERSION(DRV_VERS);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/digiPiper/piperDsp.c b/drivers/net/wireless/digiPiper/piperDsp.c
new file mode 100644
index 000000000000..0f3d0e1b9b5e
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperDsp.c
@@ -0,0 +1,285 @@
+/*
+ Copyright (c) 2007-2008 Digi International Inc., All Rights Reserved
+
+ This software contains proprietary and confidential information of Digi
+ International Inc. By accepting transfer of this copy, Recipient agrees
+ to retain this software in confidence, to prevent disclosure to others,
+ and to make no use of this software other than that for which it was
+ delivered. This is an unpublished copyrighted work of Digi International
+ Inc. Except as permitted by federal law, 17 USC 117, copying is strictly
+ prohibited.
+
+ Restricted Rights Legend
+
+ Use, duplication, or disclosure by the Government is subject to
+ restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
+ Technical Data and Computer Software clause at DFARS 252.227-7031 or
+ subparagraphs (c)(1) and (2) of the Commercial Computer Software -
+ Restricted Rights at 48 CFR 52.227-19, as applicable.
+
+ Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
+
+ WiFi DSP Code for Piper
+*/
+
+
+const unsigned long piper_wifi_dsp_ucode[1024] = {
+ 0x32000, 0x3200F, 0x320AB, 0x0403F,
+ 0x0C000, 0x38000, 0x38060, 0x10004,
+ 0x10008, 0x10009, 0x1000A, 0x1000B,
+ 0x10001, 0x10010, 0x08000, 0x08000,
+ 0x10001, 0x08000, 0x08000, 0x08000,
+ 0x08000, 0x08000, 0x08000, 0x08000,
+ 0x32030, 0x39010, 0x39010, 0x30802,
+ 0x2B030, 0x10001, 0x10800, 0x38000,
+ 0x10004, 0x10040, 0x11002, 0x320D1,
+ 0x041AD, 0x0C000, 0x38000, 0x38160,
+ 0x38400, 0x061AD, 0x38100, 0x39171,
+ 0x39571, 0x32072, 0x11002, 0x320E1,
+ 0x38100, 0x39171, 0x39571, 0x36002,
+ 0x29039, 0x38000, 0x11002, 0x38000,
+ 0x38060, 0x10001, 0x083D5, 0x10050,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10104, 0x10013, 0x321D1, 0x0422D,
+ 0x0E000, 0x38000, 0x38260, 0x38400,
+ 0x0622D, 0x0C000, 0x38200, 0x39271,
+ 0x39671, 0x32002, 0x10003, 0x321E1,
+ 0x38200, 0x39271, 0x39671, 0x10013,
+ 0x321E1, 0x38200, 0x39271, 0x39671,
+ 0x36002, 0x29034, 0x38000, 0x10003,
+ 0x10001, 0x38060, 0x083B1, 0x10050,
+ 0x10104, 0x10040, 0x10213, 0x320D1,
+ 0x0422D, 0x0E000, 0x38000, 0x38260,
+ 0x38400, 0x0622D, 0x0C000, 0x38200,
+ 0x39271, 0x39671, 0x10003, 0x321E1,
+ 0x38200, 0x39271, 0x39671, 0x10013,
+ 0x321E1, 0x38200, 0x39271, 0x39671,
+ 0x10003, 0x10001, 0x38060, 0x10004,
+ 0x10000, 0x08392, 0x10010, 0x08380,
+ 0x30802, 0x29003, 0x37012, 0x082BF,
+ 0x38000, 0x29002, 0x37012, 0x08078,
+ 0x29002, 0x37012, 0x08105, 0x29002,
+ 0x37012, 0x081C2, 0x29002, 0x37172,
+ 0x0822F, 0x04236, 0x10801, 0x29002,
+ 0x370F2, 0x0838A, 0x29002, 0x370F2,
+ 0x083A7, 0x29002, 0x370F2, 0x083C4,
+ 0x38000, 0x08002, 0x38000, 0x38000,
+ 0x30822, 0x10104, 0x10040, 0x10213,
+ 0x320D1, 0x0422D, 0x0E000, 0x38000,
+ 0x38260, 0x38400, 0x0622D, 0x0C000,
+ 0x38200, 0x39271, 0x39671, 0x10003,
+ 0x321E1, 0x38200, 0x39271, 0x39671,
+ 0x10013, 0x321E1, 0x38200, 0x39271,
+ 0x39671, 0x10003, 0x38000, 0x38060,
+ 0x36002, 0x2A005, 0x0422D, 0x38400,
+ 0x0402D, 0x38060, 0x38400, 0x10004,
+ 0x10000, 0x10001, 0x08349, 0x10010,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x3601F, 0x30033, 0x23103, 0x38000,
+ 0x08030, 0x38000, 0x08031, 0x38000,
+ 0x327F0, 0x300F1, 0x37091, 0x21034,
+ 0x30803, 0x300F2, 0x23009, 0x32081,
+ 0x10002, 0x04180, 0x0C100, 0x38000,
+ 0x38040, 0x04400, 0x320A1, 0x38060,
+ 0x04900, 0x0E630, 0x38000, 0x38000,
+ 0x38060, 0x38000, 0x2103E, 0x38060,
+ 0x04000, 0x0C000, 0x300F1, 0x22004,
+ 0x37041, 0x23003, 0x04401, 0x38000,
+ 0x38020, 0x3601F, 0x30033, 0x2310C,
+ 0x38000, 0x082EA, 0x10010, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x38000, 0x10800, 0x38000,
+ 0x300F2, 0x32081, 0x35021, 0x20017,
+ 0x300F2, 0x37052, 0x28036, 0x0440A,
+ 0x0C000, 0x38000, 0x38060, 0x30011,
+ 0x20004, 0x0440A, 0x0E000, 0x2103F,
+ 0x38060, 0x05410, 0x0E000, 0x38000,
+ 0x38060, 0x08005, 0x38000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x04000,
+ 0x10008, 0x05C02, 0x0C000, 0x38000,
+ 0x38020, 0x04C12, 0x0E000, 0x10002,
+ 0x38020, 0x0C209, 0x04914, 0x38000,
+ 0x0C409, 0x38040, 0x38000, 0x38000,
+ 0x38020, 0x32011, 0x36001, 0x0E000,
+ 0x05E02, 0x38000, 0x38120, 0x0E201,
+ 0x04914, 0x38000, 0x0E401, 0x38040,
+ 0x38000, 0x38020, 0x21034, 0x38000,
+ 0x10202, 0x04924, 0x38000, 0x38400,
+ 0x10202, 0x041A4, 0x0C100, 0x38060,
+ 0x04424, 0x38060, 0x04924, 0x0E630,
+ 0x320A1, 0x38000, 0x38060, 0x38000,
+ 0x2103E, 0x38060, 0x04403, 0x0C000,
+ 0x300F0, 0x38020, 0x0E000, 0x37030,
+ 0x39030, 0x39030, 0x0401B, 0x10401,
+ 0x38040, 0x10001, 0x05C1C, 0x38000,
+ 0x38040, 0x08282, 0x10010, 0x00000,
+ 0x10023, 0x10104, 0x0422D, 0x0C080,
+ 0x32180, 0x38260, 0x0E080, 0x38200,
+ 0x39270, 0x39270, 0x05C2E, 0x0C000,
+ 0x38060, 0x100D3, 0x0422D, 0x0C080,
+ 0x32180, 0x38260, 0x0E080, 0x38200,
+ 0x39270, 0x39270, 0x05C2E, 0x0E000,
+ 0x38060, 0x10004, 0x10003, 0x10402,
+ 0x0422D, 0x0C000, 0x323F0, 0x38200,
+ 0x39270, 0x39770, 0x04401, 0x0C000,
+ 0x1000B, 0x38020, 0x06409, 0x38020,
+ 0x38020, 0x38020, 0x1000A, 0x08254,
+ 0x10010, 0x38000, 0x38000, 0x38000,
+ 0x10462, 0x10014, 0x041AD, 0x0C000,
+ 0x32180, 0x38160, 0x0E000, 0x38100,
+ 0x39170, 0x39170, 0x05C2E, 0x0C000,
+ 0x38060, 0x10512, 0x041AD, 0x0C000,
+ 0x32180, 0x38160, 0x0E000, 0x38100,
+ 0x39170, 0x39170, 0x05C2E, 0x0E000,
+ 0x38060, 0x10004, 0x05036, 0x0E008,
+ 0x10212, 0x38060, 0x38400, 0x04427,
+ 0x0C000, 0x38060, 0x0403F, 0x38000,
+ 0x38020, 0x0493F, 0x0E630, 0x32051,
+ 0x38000, 0x38060, 0x38000, 0x2103E,
+ 0x38060, 0x300F1, 0x37061, 0x0443E,
+ 0x0C000, 0x38020, 0x0643F, 0x0C000,
+ 0x38020, 0x38020, 0x38020, 0x0563F,
+ 0x0E000, 0x2000A, 0x38000, 0x21004,
+ 0x38020, 0x04C37, 0x08014, 0x38020,
+ 0x2000C, 0x38000, 0x21006, 0x38000,
+ 0x04637, 0x38020, 0x05C37, 0x0800B,
+ 0x38020, 0x38020, 0x21005, 0x04437,
+ 0x38020, 0x05637, 0x08004, 0x38020,
+ 0x05C37, 0x38020, 0x38000, 0x11102,
+ 0x041B6, 0x0C000, 0x38000, 0x38020,
+ 0x04E37, 0x0E000, 0x38020, 0x05C3F,
+ 0x0C000, 0x38020, 0x04438, 0x0E000,
+ 0x38020, 0x04000, 0x38000, 0x10008,
+ 0x04401, 0x0C000, 0x38020, 0x06439,
+ 0x0E000, 0x38020, 0x38020, 0x38020,
+ 0x38020, 0x38020, 0x38020, 0x04439,
+ 0x38000, 0x38020, 0x04409, 0x0C000,
+ 0x1000B, 0x06409, 0x38020, 0x38020,
+ 0x38020, 0x1000A, 0x10212, 0x04136,
+ 0x0C800, 0x38000, 0x38060, 0x11112,
+ 0x041B6, 0x0E000, 0x38000, 0x38060,
+ 0x25004, 0x38000, 0x10800, 0x38000,
+ 0x38000, 0x10400, 0x081CD, 0x38000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10422, 0x10023, 0x10114, 0x04BAD,
+ 0x0C080, 0x32190, 0x38300, 0x39370,
+ 0x39F70, 0x104D2, 0x100D3, 0x32190,
+ 0x38300, 0x39370, 0x39F70, 0x38000,
+ 0x10433, 0x0422D, 0x0C000, 0x32030,
+ 0x39270, 0x39A10, 0x10063, 0x10334,
+ 0x32030, 0x39270, 0x39A10, 0x10512,
+ 0x041AD, 0x32020, 0x39170, 0x39510,
+ 0x10782, 0x10114, 0x32020, 0x39170,
+ 0x39510, 0x104D3, 0x0422D, 0x0C070,
+ 0x10334, 0x38260, 0x0E070, 0x32060,
+ 0x39210, 0x39270, 0x107B2, 0x104D3,
+ 0x10020, 0x05BAD, 0x0E060, 0x32190,
+ 0x38300, 0x39370, 0x39B70, 0x13FC3,
+ 0x0422D, 0x0C070, 0x10114, 0x38260,
+ 0x0E070, 0x32060, 0x39210, 0x39270,
+ 0x104E2, 0x13FC3, 0x05BAD, 0x0E060,
+ 0x32190, 0x38300, 0x39370, 0x39B70,
+ 0x10004, 0x08326, 0x10000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10114, 0x10173, 0x10902, 0x0616D,
+ 0x0C800, 0x321B0, 0x38260, 0x38260,
+ 0x38260, 0x39270, 0x39770, 0x0416D,
+ 0x321B0, 0x10173, 0x10912, 0x38260,
+ 0x38260, 0x38260, 0x39270, 0x39770,
+ 0x10003, 0x10C42, 0x0616D, 0x0C800,
+ 0x321B0, 0x38260, 0x38260, 0x38260,
+ 0x39270, 0x39770, 0x0416D, 0x321B0,
+ 0x10003, 0x10C52, 0x38260, 0x38260,
+ 0x38260, 0x39270, 0x39770, 0x32028,
+ 0x0C070, 0x10973, 0x0423F, 0x38060,
+ 0x10B33, 0x0E070, 0x108E3, 0x38060,
+ 0x38060, 0x0E060, 0x10AA3, 0x38000,
+ 0x38060, 0x0C000, 0x0503F, 0x10222,
+ 0x38400, 0x080C6, 0x38000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x108D3, 0x10104, 0x10140, 0x040F6,
+ 0x0C202, 0x32181, 0x38200, 0x38080,
+ 0x382C0, 0x380A0, 0x216FF, 0x380A0,
+ 0x10823, 0x0C202, 0x32181, 0x38200,
+ 0x38080, 0x382C0, 0x380A0, 0x216FF,
+ 0x380A0, 0x080FA, 0x10010, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10902, 0x108D3, 0x10114, 0x10140,
+ 0x047F6, 0x0D202, 0x32181, 0x100E1,
+ 0x38280, 0x38040, 0x381A0, 0x216FE,
+ 0x38440, 0x10C42, 0x10823, 0x047F6,
+ 0x0D202, 0x32171, 0x38280, 0x38040,
+ 0x381A0, 0x216FE, 0x38440, 0x381A0,
+ 0x386E0, 0x321A1, 0x2103F, 0x10001,
+ 0x38400, 0x080D2, 0x10010, 0x00000,
+ 0x38000, 0x38000, 0x38000, 0x0423F,
+ 0x10973, 0x0C000, 0x38060, 0x10B33,
+ 0x0E000, 0x108E3, 0x38060, 0x38060,
+ 0x0E030, 0x10AA3, 0x38000, 0x38060,
+ 0x18083, 0x0C0C0, 0x0443F, 0x36018,
+ 0x38060, 0x21002, 0x0C000, 0x32018,
+ 0x0503F, 0x10222, 0x38400, 0x041A4,
+ 0x0C100, 0x38060, 0x04424, 0x38060,
+ 0x048A4, 0x0E630, 0x32061, 0x38000,
+ 0x38060, 0x38000, 0x2103E, 0x38060,
+ 0x10080, 0x04401, 0x0C000, 0x38020,
+ 0x06639, 0x0E000, 0x38020, 0x04409,
+ 0x0C000, 0x1000B, 0x06409, 0x38020,
+ 0x38020, 0x38020, 0x1000A, 0x30811,
+ 0x22007, 0x37021, 0x20003, 0x38000,
+ 0x08383, 0x38000, 0x083A1, 0x38000,
+ 0x10902, 0x108D3, 0x10104, 0x10140,
+ 0x047F6, 0x0D202, 0x32181, 0x100E1,
+ 0x38080, 0x38240, 0x381A0, 0x38460,
+ 0x214FD, 0x38740, 0x10C42, 0x10823,
+ 0x047F6, 0x0D202, 0x32171, 0x38080,
+ 0x38240, 0x381A0, 0x38460, 0x214FD,
+ 0x38740, 0x381A0, 0x38460, 0x384E0,
+ 0x38000, 0x38000, 0x38000, 0x38000,
+ 0x38000, 0x38000, 0x38000, 0x38000,
+ 0x38000, 0x38400, 0x10001, 0x08068,
+ 0x10010, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x041A4, 0x0C100, 0x38000, 0x38060,
+ 0x04424, 0x32061, 0x38060, 0x04924,
+ 0x0E630, 0x38000, 0x38000, 0x38060,
+ 0x38000, 0x2103E, 0x38060, 0x10080,
+ 0x108D3, 0x10104, 0x10040, 0x040F6,
+ 0x0C202, 0x32181, 0x38200, 0x38200,
+ 0x38200, 0x21240, 0x21640, 0x10823,
+ 0x32181, 0x38200, 0x38200, 0x38200,
+ 0x21240, 0x21640, 0x04401, 0x0C000,
+ 0x38020, 0x06639, 0x0E000, 0x38020,
+ 0x04409, 0x0C000, 0x1000B, 0x06409,
+ 0x38020, 0x38020, 0x38020, 0x1000A,
+ 0x0801F, 0x10010, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000
+};
+
+const int piper_dsp_data_len = (sizeof piper_wifi_dsp_ucode)/4;
diff --git a/drivers/net/wireless/digiPiper/piperDsp.h b/drivers/net/wireless/digiPiper/piperDsp.h
new file mode 100644
index 000000000000..836ffcb96341
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperDsp.h
@@ -0,0 +1,8 @@
+#ifndef PIPER_DSP_H_
+#define PIPER_DSP_H_
+
+
+extern const unsigned long piper_wifi_dsp_ucode[];
+extern const int piper_dsp_data_len;
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/piperMacAssist.c b/drivers/net/wireless/digiPiper/piperMacAssist.c
new file mode 100644
index 000000000000..1a8ada81b84b
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperMacAssist.c
@@ -0,0 +1,285 @@
+/*
+ Copyright (c) 2007-2008 Digi International Inc., All Rights Reserved
+
+ This software contains proprietary and confidential information of Digi
+ International Inc. By accepting transfer of this copy, Recipient agrees
+ to retain this software in confidence, to prevent disclosure to others,
+ and to make no use of this software other than that for which it was
+ delivered. This is an unpublished copyrighted work of Digi International
+ Inc. Except as permitted by federal law, 17 USC 117, copying is strictly
+ prohibited.
+
+ Restricted Rights Legend
+
+ Use, duplication, or disclosure by the Government is subject to
+ restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
+ Technical Data and Computer Software clause at DFARS 252.227-7031 or
+ subparagraphs (c)(1) and (2) of the Commercial Computer Software -
+ Restricted Rights at 48 CFR 52.227-19, as applicable.
+
+ Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
+
+ WiFi MAC assist Code for Piper
+*/
+
+const unsigned long piper_wifi_macassist_ucode[2048] = {
+ 0x00F00, 0x2CF22, 0x3033B, 0x00E20, 0x2CED0, 0x2CFD2, 0x2CFD8, 0x2CFD9,
+ 0x2CF31, 0x2CFDB, 0x00E20, 0x2CEDA, 0x00E64, 0x2CE1D, 0x00EFF, 0x2CEEA,
+ 0x2CEEB, 0x00E01, 0x2CE20, 0x00E10, 0x2CE21, 0x04522, 0x00603, 0x2C6CE,
+ 0x00001, 0x2C040, 0x00000, 0x2C041, 0x2C042, 0x2C043, 0x0016E, 0x2C144,
+ 0x00000, 0x0010A, 0x00200, 0x0A5FF, 0x35027, 0x0010B, 0x00280, 0x2C245,
+ 0x2C146, 0x2C047, 0x00180, 0x2C148, 0x2C049, 0x2C0CE, 0x00000, 0x00100,
+ 0x00223, 0x040C0, 0x302E2, 0x3031A, 0x00030, 0x0011C, 0x00801, 0x042C0,
+ 0x0A204, 0x35037, 0x042C0, 0x0A5FF, 0x35040, 0x00031, 0x00117, 0x00806,
+ 0x30338, 0x1C001, 0x35C40, 0x1C101, 0x35C43, 0x00000, 0x042C0, 0x043C0,
+ 0x044C0, 0x045C0, 0x046C0, 0x047C0, 0x00000, 0x1C801, 0x35C4D, 0x048C1,
+ 0x049C1, 0x04AC1, 0x04BC1, 0x04CC1, 0x04DC1, 0x0D320, 0x0D430, 0x0D540,
+ 0x0D650, 0x0D760, 0x19230, 0x19240, 0x19250, 0x19260, 0x19270, 0x0A880,
+ 0x35062, 0x18208, 0x0A980, 0x35065, 0x18208, 0x0AA80, 0x35068, 0x18208,
+ 0x0AB80, 0x3506B, 0x18208, 0x0AC80, 0x3506E, 0x18208, 0x0AD80, 0x35071,
+ 0x18208, 0x20206, 0x04122, 0x0A1FF, 0x3547B, 0x2C222, 0x0E260, 0x35015,
+ 0x0E260, 0x00151, 0x3407C, 0x00103, 0x19120, 0x2C122, 0x00E00, 0x2CECE,
+ 0x3C001, 0x04EC3, 0x0AE20, 0x04FC2, 0x0AF80, 0x0DEF0, 0x04F30, 0x2CE30,
+ 0x0FFE0, 0x35095, 0x0EE20, 0x35495, 0x3C000, 0x04EC1, 0x0AE80, 0x3548D,
+ 0x04E17, 0x2CED4, 0x04E16, 0x2CED6, 0x3C001, 0x04E27, 0x0AE10, 0x04F33,
+ 0x2CE33, 0x0EF10, 0x0BFE0, 0x350AC, 0x00E00, 0x00FE0, 0x2DEF0, 0x18F01,
+ 0x0EFE8, 0x350A4, 0x0EFE8, 0x3409E, 0x04F1D, 0x00E00, 0x1DEF0, 0x2CEEA,
+ 0x04F1C, 0x00E00, 0x1FEF0, 0x2CEEB, 0x04E27, 0x0AE80, 0x350C8, 0x3C000,
+ 0x00E01, 0x2CECE, 0x04EC1, 0x0AE80, 0x354B2, 0x04EE0, 0x2CE27, 0x04EE1,
+ 0x2CE26, 0x04EE2, 0x2CE25, 0x04EE3, 0x2CE24, 0x04EE4, 0x2CE23, 0x04EE5,
+ 0x2CE22, 0x04EE6, 0x04FE7, 0x2CE21, 0x2CF20, 0x00E00, 0x2CECE, 0x3C001,
+ 0x04E35, 0x04F36, 0x0DEF0, 0x3507E, 0x04E3A, 0x04FEB, 0x15EF0, 0x3547E,
+ 0x04FEA, 0x04E37, 0x15EF0, 0x3547E, 0x3C000, 0x04EC1, 0x0AE80, 0x354D5,
+ 0x04E36, 0x12EFF, 0x354E7, 0x04F35, 0x14F40, 0x35CE7, 0x00E00, 0x2CED8,
+ 0x20F06, 0x20F06, 0x2CFD9, 0x3C001, 0x2CE36, 0x2CE35, 0x3407E, 0x04F35,
+ 0x1CF01, 0x1EE00, 0x2CF35, 0x2CE36, 0x00E0F, 0x2CED8, 0x2CED9, 0x3C001,
+ 0x04E37, 0x04F3A, 0x18E01, 0x1AF00, 0x2CE37, 0x2CF3A, 0x3407E, 0x2CF32,
+ 0x04FC0, 0x20F0E, 0x3190B, 0x20F0E, 0x31BFC, 0x20F0E, 0x3195B, 0x20F0E,
+ 0x31A72, 0x20F0E, 0x31ACA, 0x20F0E, 0x319DC, 0x20F0E, 0x319E3, 0x20F0E,
+ 0x31A63, 0x04F32, 0x38001, 0x04031, 0x0C001, 0x2C031, 0x040C2, 0x0A008,
+ 0x35120, 0x04031, 0x0A004, 0x35517, 0x04031, 0x0C080, 0x2C031, 0x302E2,
+ 0x040C3, 0x0A010, 0x3511C, 0x00016, 0x18011, 0x00100, 0x00200, 0x3031A,
+ 0x302FF, 0x0E3FF, 0x35127, 0x04031, 0x0A0BF, 0x2C031, 0x3412A, 0x04031,
+ 0x0C040, 0x2C031, 0x04244, 0x040C3, 0x0A010, 0x35547, 0x302E8, 0x00400,
+ 0x04510, 0x0A5FF, 0x35534, 0x0C515, 0x13350, 0x3513B, 0x01430, 0x1232F,
+ 0x35544, 0x2030E, 0x34134, 0x1232F, 0x3553F, 0x2030E, 0x34134, 0x124FF,
+ 0x35544, 0x2030E, 0x35D34, 0x00401, 0x302F4, 0x2C33B, 0x2A000, 0x04511,
+ 0x1426E, 0x3514F, 0x14237, 0x35152, 0x14214, 0x35155, 0x34158, 0x0026E,
+ 0x12508, 0x35559, 0x00237, 0x12504, 0x35559, 0x00214, 0x12502, 0x35559,
+ 0x0020A, 0x2C23B, 0x2A000, 0x04127, 0x00001, 0x2C0CE, 0x040C2, 0x0A040,
+ 0x35163, 0x00005, 0x2C0CE, 0x0A110, 0x35192, 0x04048, 0x0E080, 0x3516C,
+ 0x04048, 0x0E050, 0x3516C, 0x34192, 0x00012, 0x04140, 0x0E1EE, 0x35175,
+ 0x041C1, 0x0A120, 0x35574, 0x18020, 0x18007, 0x302FF, 0x00100, 0x19030,
+ 0x1A100, 0x30338, 0x042E0, 0x043E1, 0x044E2, 0x045E3, 0x046E4, 0x047E5,
+ 0x048E6, 0x049E7, 0x19200, 0x1B310, 0x1A400, 0x1A500, 0x1A600, 0x1A700,
+ 0x1A800, 0x1A900, 0x2C260, 0x2C361, 0x2C462, 0x2C563, 0x2C664, 0x2C765,
+ 0x2C866, 0x2C967, 0x0002C, 0x00100, 0x042C2, 0x044C1, 0x0A480, 0x35595,
+ 0x2C0D0, 0x2C1D2, 0x043C2, 0x0A3E0, 0x2C3C2, 0x04140, 0x04348, 0x00000,
+ 0x2C0CE, 0x04031, 0x0A0FB, 0x2C031, 0x0A204, 0x355B6, 0x0A30F, 0x0E304,
+ 0x351B6, 0x04012, 0x0A001, 0x351B6, 0x0E1EE, 0x355AF, 0x0C004, 0x0C00A,
+ 0x2C012, 0x00064, 0x1C001, 0x355B2, 0x00040, 0x2C0C4, 0x040C2, 0x0A040,
+ 0x2B400, 0x0403E, 0x0A0FF, 0x2B400, 0x00001, 0x2C0CE, 0x04040, 0x0A0FF,
+ 0x351C5, 0x0E001, 0x351C5, 0x0E0EF, 0x355D4, 0x04042, 0x0A0FF, 0x351CB,
+ 0x0E001, 0x355D4, 0x341CE, 0x04041, 0x0A0FF, 0x351D4, 0x04043, 0x0A0FF,
+ 0x355D4, 0x00000, 0x2C0CE, 0x2A000, 0x00080, 0x2C0C4, 0x0403F, 0x18001,
+ 0x2C03F, 0x00000, 0x2C0CE, 0x2A000, 0x00008, 0x2C0C4, 0x00100, 0x2C13E,
+ 0x00012, 0x00200, 0x3431A, 0x00101, 0x2C1CE, 0x0402B, 0x12001, 0x35203,
+ 0x04128, 0x121FF, 0x351EE, 0x1C101, 0x2C128, 0x35603, 0x04129, 0x0422D,
+ 0x0432C, 0x0442F, 0x0452E, 0x12002, 0x351F7, 0x2C128, 0x341F9, 0x0A0FE,
+ 0x2C02B, 0x00000, 0x2C0CE, 0x0401D, 0x1D200, 0x0401C, 0x1F300, 0x2C237,
+ 0x2C33A, 0x2C435, 0x2C536, 0x00000, 0x2C0CE, 0x04027, 0x0A010, 0x35236,
+ 0x3033B, 0x040D5, 0x2C02B, 0x040D7, 0x2C02A, 0x00100, 0x0401F, 0x1D100,
+ 0x2C1E8, 0x00100, 0x0401E, 0x1F100, 0x2C1E9, 0x040C2, 0x0A004, 0x3561D,
+ 0x3033B, 0x00004, 0x2C0D0, 0x00000, 0x2C0D2, 0x04027, 0x0A004, 0x3522C,
+ 0x30338, 0x30338, 0x040C2, 0x0C080, 0x2C0C2, 0x040C2, 0x0A040, 0x3522C,
+ 0x0402F, 0x2C0D4, 0x0402E, 0x2C0D6, 0x00110, 0x040C1, 0x0A010, 0x35234,
+ 0x04031, 0x0D010, 0x2C031, 0x34246, 0x2C1C4, 0x34246, 0x00010, 0x2C0C4,
+ 0x04034, 0x0C000, 0x35246, 0x040ED, 0x0C000, 0x35646, 0x3033B, 0x0421F,
+ 0x0431E, 0x2C2D8, 0x2C3D9, 0x04119, 0x1D010, 0x2C0ED, 0x0421D, 0x0431C,
+ 0x00000, 0x00100, 0x1D020, 0x1F130, 0x3033B, 0x2C0EA, 0x2C1EB, 0x040EC,
+ 0x0C000, 0x35655, 0x04118, 0x1D010, 0x2C0EC, 0x3033B, 0x040EE, 0x0C000,
+ 0x2B400, 0x041EF, 0x0C100, 0x2B400, 0x0421B, 0x0431A, 0x1D020, 0x1F130,
+ 0x2C0EE, 0x2C1EF, 0x2A000, 0x3033B, 0x040D5, 0x2C02B, 0x040D7, 0x2C02A,
+ 0x040C1, 0x0A010, 0x3566E, 0x00020, 0x2C0C4, 0x2A000, 0x04031, 0x0C020,
+ 0x2C031, 0x2A000, 0x04012, 0x0A0F7, 0x2C012, 0x00500, 0x00601, 0x00100,
+ 0x00700, 0x04031, 0x0A0F7, 0x2C031, 0x2C53E, 0x040C3, 0x0A0C0, 0x35287,
+ 0x00603, 0x0A080, 0x35684, 0x00605, 0x2C6CE, 0x04440, 0x342A4, 0x00702,
+ 0x2C6CE, 0x04440, 0x0424C, 0x0A201, 0x356A4, 0x04248, 0x0E2B4, 0x35293,
+ 0x04249, 0x0A204, 0x35298, 0x2C5CE, 0x04231, 0x0C208, 0x2C231, 0x2C6CE,
+ 0x00203, 0x04048, 0x2C5CE, 0x0E0C4, 0x352B9, 0x00030, 0x0A4EE, 0x356A2,
+ 0x00080, 0x1805E, 0x00209, 0x342B9, 0x2C5CE, 0x00000, 0x00100, 0x04231,
+ 0x0A240, 0x356AD, 0x042C1, 0x0A201, 0x356B2, 0x0A4EE, 0x356B0, 0x000FE,
+ 0x1803C, 0x1A100, 0x00200, 0x0A4EE, 0x356B7, 0x18016, 0x1A100, 0x18012,
+ 0x1A100, 0x3031A, 0x12702, 0x352C2, 0x0A202, 0x356C2, 0x04131, 0x12108,
+ 0x356C2, 0x0C780, 0x04131, 0x0A130, 0x0D710, 0x2C7C4, 0x04731, 0x0A7CF,
+ 0x2C731, 0x2A000, 0x04531, 0x12504, 0x352D4, 0x3033B, 0x0A5FB, 0x2C531,
+ 0x00500, 0x2C5D8, 0x2C5D9, 0x342DE, 0x04031, 0x0A080, 0x2B400, 0x04131,
+ 0x0A1F7, 0x2C131, 0x00184, 0x2C1C4, 0x00101, 0x2C1CE, 0x04440, 0x302E2,
+ 0x00500, 0x342A4, 0x040C1, 0x0A080, 0x356E2, 0x00020, 0x2C0DB, 0x2A000,
+ 0x00310, 0x12202, 0x352EC, 0x00301, 0x12201, 0x356F0, 0x20306, 0x20306,
+ 0x12204, 0x352F3, 0x20306, 0x2A000, 0x00308, 0x12455, 0x356F8, 0x0030C,
+ 0x12433, 0x352FB, 0x0C301, 0x1240F, 0x352FE, 0x0C302, 0x2A000, 0x04240,
+ 0x0E2EE, 0x35708, 0x04244, 0x003FF, 0x0A208, 0x2B000, 0x00300, 0x2A000,
+ 0x003FF, 0x04244, 0x0E26E, 0x3570D, 0x00311, 0x04244, 0x0E237, 0x35711,
+ 0x00323, 0x04244, 0x0E214, 0x35715, 0x00360, 0x04244, 0x0E20A, 0x2B400,
+ 0x003C0, 0x2A000, 0x0C000, 0x35322, 0x04325, 0x01430, 0x20306, 0x19430,
+ 0x19040, 0x1A100, 0x1C01A, 0x1E100, 0x04AC1, 0x0AA80, 0x35724, 0x043DB,
+ 0x19030, 0x1A100, 0x12180, 0x3532F, 0x00001, 0x00100, 0x34331, 0x18001,
+ 0x1A100, 0x2C0D0, 0x2C1D2, 0x043C2, 0x0A3E0, 0x0D230, 0x2C2C2, 0x2A000,
+ 0x04AC1, 0x0AA80, 0x35338, 0x04AC1, 0x0AA80, 0x3573B, 0x2A000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00001, 0x2C0CF, 0x2A000, 0x340F7,
+ 0x040C3, 0x0A002, 0x35008, 0x300EA, 0x04031, 0x0C002, 0x2C031, 0x343FC,
+ 0x04031, 0x12002, 0x3540E, 0x12001, 0x353FC, 0x300EA, 0x04031, 0x0A0F8,
+ 0x2C031, 0x30058, 0x04031, 0x0A0F7, 0x2C031, 0x04123, 0x01010, 0x0A00C,
+ 0x0E004, 0x35020, 0x04031, 0x0A080, 0x35042, 0x00084, 0x2C0C4, 0x34042,
+ 0x04023, 0x0C010, 0x2C023, 0x04027, 0x0A008, 0x35429, 0x01010, 0x0A003,
+ 0x3501A, 0x04031, 0x0A080, 0x35036, 0x04048, 0x0A0EC, 0x0E0C4, 0x3503A,
+ 0x00084, 0x2C0C4, 0x04031, 0x0A07F, 0x2C031, 0x3403A, 0x04048, 0x0A0EC,
+ 0x0E0C4, 0x35042, 0x040C1, 0x0A002, 0x35040, 0x00040, 0x2C0C4, 0x3401A,
+ 0x00001, 0x2C0C4, 0x04031, 0x0A07F, 0x2C031, 0x04023, 0x0A00C, 0x0E004,
+ 0x357FC, 0x0404C, 0x0A001, 0x3504E, 0x300B0, 0x343FC, 0x04023, 0x0A001,
+ 0x35453, 0x300BF, 0x343FC, 0x04048, 0x0E050, 0x357FC, 0x300DB, 0x343FC,
+ 0x04031, 0x0A040, 0x3505F, 0x04023, 0x0C020, 0x2C023, 0x3406B, 0x040C1,
+ 0x0A001, 0x3506B, 0x04023, 0x0C004, 0x2C023, 0x04048, 0x0A003, 0x35073,
+ 0x04023, 0x0C008, 0x2C023, 0x040C3, 0x0A010, 0x3506F, 0x00054, 0x1804E,
+ 0x00100, 0x00200, 0x34306, 0x040C2, 0x0A004, 0x2B400, 0x040C3, 0x0A010,
+ 0x3507A, 0x00016, 0x18012, 0x00100, 0x00200, 0x30306, 0x0004C, 0x05100,
+ 0x0A101, 0x2B000, 0x04008, 0x0A001, 0x35088, 0x04048, 0x0E080, 0x3509A,
+ 0x04049, 0x0A002, 0x3548C, 0x00008, 0x18050, 0x05100, 0x0A101, 0x35494,
+ 0x00208, 0x3032E, 0x0C400, 0x2B400, 0x04048, 0x0E040, 0x3549A, 0x04027,
+ 0x0A010, 0x2B000, 0x0402D, 0x0A0FF, 0x350AC, 0x04048, 0x0E080, 0x354AC,
+ 0x04063, 0x04139, 0x2C039, 0x0F010, 0x354AC, 0x04062, 0x04138, 0x0422D,
+ 0x0B020, 0x2C038, 0x0F010, 0x2B000, 0x04023, 0x0C002, 0x2C023, 0x2A000,
+ 0x04048, 0x0A0EC, 0x0E0E4, 0x354BC, 0x04036, 0x04135, 0x0D010, 0x2B400,
+ 0x3033C, 0x2C0D8, 0x2C0D9, 0x2A000, 0x04048, 0x0E080, 0x310DB, 0x0414B,
+ 0x0A180, 0x2B400, 0x0414A, 0x0424B, 0x1C10A, 0x1E200, 0x35CC9, 0x00100,
+ 0x00200, 0x30340, 0x2BC00, 0x04048, 0x0E0B4, 0x2B400, 0x2A000, 0x04031,
+ 0x0C004, 0x2C031, 0x00078, 0x00100, 0x042C3, 0x0A210, 0x350D9, 0x0006A,
+ 0x00101, 0x00209, 0x34306, 0x0466D, 0x1866F, 0x05160, 0x19610, 0x0416A,
+ 0x0A10C, 0x2C134, 0x350E9, 0x1860A, 0x05160, 0x18601, 0x05260, 0x30340,
+ 0x1C60B, 0x34213, 0x00000, 0x2C023, 0x04031, 0x0A040, 0x2B400, 0x040C3,
+ 0x0A010, 0x354FB, 0x04046, 0x0A001, 0x354FB, 0x04045, 0x14005, 0x35CFB,
+ 0x040C3, 0x0A002, 0x354F8, 0x04048, 0x0A003, 0x2B400, 0x0004C, 0x05100,
+ 0x0A101, 0x2B400, 0x0424C, 0x0434D, 0x0444E, 0x0454F, 0x04750, 0x04851,
+ 0x04100, 0x0F120, 0x04001, 0x0F030, 0x0D100, 0x04002, 0x0F040, 0x0D100,
+ 0x04003, 0x0F050, 0x0D100, 0x04004, 0x0F070, 0x0D100, 0x04005, 0x0F080,
+ 0x0D100, 0x35146, 0x04027, 0x0A040, 0x35145, 0x00001, 0x2C0CE, 0x04130,
+ 0x0F120, 0x04038, 0x0F200, 0x04031, 0x0F030, 0x0D100, 0x04039, 0x0F030,
+ 0x0D200, 0x04032, 0x0F040, 0x0D100, 0x0403A, 0x0F040, 0x0D200, 0x04033,
+ 0x0F050, 0x0D100, 0x0403B, 0x0F050, 0x0D200, 0x04034, 0x0F070, 0x0D100,
+ 0x0403C, 0x0F070, 0x0D200, 0x04035, 0x0433D, 0x00400, 0x2C4CE, 0x0F080,
+ 0x0D100, 0x35146, 0x0F380, 0x0D230, 0x35146, 0x2A000, 0x04023, 0x0C001,
+ 0x2C023, 0x040C1, 0x0A002, 0x2B400, 0x04048, 0x0A00C, 0x0E004, 0x35564,
+ 0x04048, 0x0A0E0, 0x0E0A0, 0x35164, 0x0E060, 0x2B400, 0x04131, 0x0A108,
+ 0x2B000, 0x3020A, 0x040C3, 0x0A002, 0x3555A, 0x040C1, 0x0A002, 0x35577,
+ 0x040C1, 0x0A001, 0x35177, 0x2A000, 0x3020A, 0x0413B, 0x040C3, 0x0A010,
+ 0x355C5, 0x0C1C0, 0x002EE, 0x00300, 0x00701, 0x00603, 0x040C3, 0x0A002,
+ 0x3556E, 0x040C1, 0x0A002, 0x35577, 0x040C1, 0x0A001, 0x3557C, 0x042C2,
+ 0x0A2E0, 0x0C203, 0x2C2C2, 0x2A000, 0x2C6CE, 0x2C240, 0x2C341, 0x2C342,
+ 0x2C343, 0x2C144, 0x2C745, 0x2C346, 0x2C347, 0x2C3CE, 0x04248, 0x0434A,
+ 0x0444B, 0x01540, 0x0E580, 0x35193, 0x01520, 0x0E5B4, 0x35195, 0x04549,
+ 0x0A504, 0x35595, 0x0A400, 0x0A300, 0x341AB, 0x00528, 0x01010, 0x0A007,
+ 0x351A9, 0x0E004, 0x351A9, 0x0E001, 0x351A9, 0x18504, 0x0E004, 0x351A9,
+ 0x0E007, 0x351A9, 0x18508, 0x0E004, 0x351A9, 0x18504, 0x0E005, 0x351A9,
+ 0x1850C, 0x1D350, 0x1E400, 0x00000, 0x2C6CE, 0x0E2B4, 0x351B1, 0x005D4,
+ 0x341B2, 0x005C4, 0x2C548, 0x2C049, 0x2C34A, 0x2C44B, 0x00306, 0x00152,
+ 0x0024C, 0x2C0CE, 0x05810, 0x2C6CE, 0x2D820, 0x18101, 0x18201, 0x1C301,
+ 0x355B9, 0x2C0CE, 0x00001, 0x2C03E, 0x2A000, 0x045C1, 0x0A540, 0x351CC,
+ 0x01510, 0x0E50A, 0x355CC, 0x00114, 0x01510, 0x00784, 0x00800, 0x0E56E,
+ 0x351D9, 0x00704, 0x0E559, 0x351D8, 0x0E523, 0x351D7, 0x18838, 0x18823,
+ 0x1880A, 0x1880B, 0x00603, 0x00300, 0x040C3, 0x0A002, 0x355DC, 0x040C1,
+ 0x0A002, 0x35577, 0x040C1, 0x0A001, 0x35177, 0x2C6CE, 0x2C340, 0x2C341,
+ 0x2C342, 0x2C343, 0x2C144, 0x2C745, 0x2C846, 0x2C347, 0x2C3CE, 0x04248,
+ 0x0434A, 0x0444B, 0x01540, 0x0E580, 0x351FC, 0x01520, 0x0E5B4, 0x351FE,
+ 0x04549, 0x0A504, 0x355FE, 0x0A400, 0x0A300, 0x341AB, 0x01580, 0x1850A,
+ 0x1D350, 0x1E400, 0x045C1, 0x0A540, 0x35606, 0x185A0, 0x18520, 0x1D350,
+ 0x1E400, 0x341AB, 0x00000, 0x00100, 0x00217, 0x04348, 0x0E3B4, 0x35211,
+ 0x00207, 0x30306, 0x2A000, 0x18603, 0x04027, 0x0A010, 0x0416A, 0x0A102,
+ 0x20106, 0x20106, 0x20106, 0x0F010, 0x2B400, 0x0C100, 0x35237, 0x0426D,
+ 0x04013, 0x0F020, 0x2B400, 0x0006E, 0x00100, 0x0A2FF, 0x2B000, 0x00500,
+ 0x05300, 0x00701, 0x2C7CE, 0x05410, 0x00700, 0x2C7CE, 0x0F340, 0x0D530,
+ 0x18001, 0x18101, 0x1C201, 0x35628, 0x0C500, 0x2B400, 0x34257, 0x00058,
+ 0x00108, 0x00206, 0x00500, 0x05300, 0x05410, 0x0F340, 0x0D530, 0x18001,
+ 0x18101, 0x1C201, 0x3563B, 0x0C500, 0x2B400, 0x04034, 0x0C000, 0x35257,
+ 0x18603, 0x05060, 0x00100, 0x1D100, 0x2C1ED, 0x18601, 0x05060, 0x2C019,
+ 0x18601, 0x05060, 0x2C01F, 0x18601, 0x05060, 0x2C01E, 0x18602, 0x04048,
+ 0x0A0EC, 0x0E080, 0x35676, 0x04023, 0x0C002, 0x2C023, 0x040C2, 0x0A080,
+ 0x35265, 0x040C2, 0x0A05F, 0x2C0C2, 0x2C0C2, 0x18603, 0x04027, 0x0A010,
+ 0x3526F, 0x05060, 0x2C01F, 0x18601, 0x05060, 0x2C01E, 0x34276, 0x05060,
+ 0x00100, 0x1D100, 0x2C1EC, 0x18601, 0x05060, 0x2C018, 0x040C3, 0x0A010,
+ 0x35685, 0x04044, 0x04145, 0x04246, 0x00304, 0x2020E, 0x20108, 0x20008,
+ 0x2010E, 0x20008, 0x1C301, 0x35680, 0x34294, 0x04046, 0x04147, 0x04244,
+ 0x003C0, 0x0E26E, 0x3568C, 0x00311, 0x04244, 0x0E237, 0x35690, 0x00323,
+ 0x04244, 0x0E214, 0x35694, 0x00360, 0x1D030, 0x1E100, 0x30339, 0x1801A,
+ 0x1A100, 0x04260, 0x19200, 0x04361, 0x1B310, 0x04462, 0x1A400, 0x04563,
+ 0x1A500, 0x04664, 0x1A600, 0x04765, 0x1A700, 0x04866, 0x1A800, 0x04967,
+ 0x1A900, 0x0406A, 0x0A002, 0x352BF, 0x040E0, 0x1D020, 0x040E1, 0x1F030,
+ 0x040E2, 0x1F040, 0x040E3, 0x1F050, 0x040E4, 0x1F060, 0x040E5, 0x1F070,
+ 0x040E6, 0x1F080, 0x040E7, 0x1F090, 0x2BC00, 0x00000, 0x342C0, 0x30339,
+ 0x2C2E0, 0x2C3E1, 0x2C4E2, 0x2C5E3, 0x2C6E4, 0x2C7E5, 0x2C8E6, 0x2C9E7,
+ 0x01230, 0x0401D, 0x0411C, 0x00D27, 0x00C01, 0x20006, 0x20100, 0x35AD5,
+ 0x18C01, 0x0EC10, 0x352F4, 0x0EC10, 0x342CD, 0x20108, 0x342D8, 0x2010E,
+ 0x20008, 0x1CC01, 0x352EC, 0x01A80, 0x01B90, 0x1DA00, 0x1FB10, 0x35AD7,
+ 0x018A0, 0x019B0, 0x342D7, 0x1CD01, 0x352F8, 0x20306, 0x20400, 0x20500,
+ 0x20600, 0x20700, 0x20800, 0x20900, 0x01A80, 0x01B90, 0x1DA00, 0x1FB10,
+ 0x35AE3, 0x018A0, 0x019B0, 0x342E3, 0x00100, 0x008FF, 0x009FF, 0x34301,
+ 0x30339, 0x0401D, 0x0411C, 0x04AE1, 0x0FA20, 0x0AAFC, 0x35301, 0x18801,
+ 0x1A900, 0x1D800, 0x1F910, 0x2C8EA, 0x2C9EB, 0x2A000, 0x0442C, 0x0C000,
+ 0x3530D, 0x04325, 0x01430, 0x20306, 0x19430, 0x19040, 0x1A100, 0x043C3,
+ 0x0A310, 0x35717, 0x04344, 0x0A302, 0x35317, 0x0031A, 0x34318, 0x0031E,
+ 0x1D030, 0x1E100, 0x04AC1, 0x0AA80, 0x3571A, 0x04ADB, 0x190A0, 0x1A100,
+ 0x12180, 0x35325, 0x00002, 0x00100, 0x34327, 0x18002, 0x1A100, 0x2C0D0,
+ 0x2C1D2, 0x043C2, 0x0A3E0, 0x0D230, 0x2C2C2, 0x2A000, 0x00506, 0x00400,
+ 0x05100, 0x05320, 0x0F130, 0x0D410, 0x18201, 0x18001, 0x1C501, 0x35730,
+ 0x2A000, 0x04AC1, 0x0AA80, 0x35339, 0x04AC1, 0x0AA80, 0x3573C, 0x2A000,
+ 0x30339, 0x043D8, 0x044D9, 0x18301, 0x1A400, 0x1D310, 0x1F420, 0x2BC00,
+ 0x2C1D8, 0x2C2D9, 0x2A000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x2C0CF, 0x34000, 0x00000
+};
+
+
+const int piper_macassist_data_len = (sizeof piper_wifi_macassist_ucode)/4;
diff --git a/drivers/net/wireless/digiPiper/piperMacAssist.h b/drivers/net/wireless/digiPiper/piperMacAssist.h
new file mode 100644
index 000000000000..27b2b77fb493
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperMacAssist.h
@@ -0,0 +1,9 @@
+
+#ifndef PIPER_MACASSIST_H_
+#define PIPER_MACASSIST_H_
+
+extern const unsigned long piper_wifi_macassist_ucode[];
+extern const int piper_macassist_data_len;
+
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/pipermain.h b/drivers/net/wireless/digiPiper/pipermain.h
new file mode 100644
index 000000000000..dba31309e081
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/pipermain.h
@@ -0,0 +1,381 @@
+#ifndef __PIPER_H_
+#define __PIPER_H_
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+#include <linux/spinlock.h>
+#include <net/mac80211.h>
+#include <linux/i2c.h>
+#include "mac.h"
+
+
+// #define WANT_DEBUG
+#ifdef WANT_DEBUG
+#define digi_dbg(fmt, arg...) \
+ printk(KERN_ERR PIPER_DRIVER_NAME ": %s - " fmt, __func__, ##arg)
+#else
+#define digi_dbg(fmt, arg...) \
+ do { } while (0)
+#endif
+
+#define ERROR(x) printk(KERN_ALERT x)
+
+/* Debug levels */
+#define DSILENT 0xffff
+#define DERROR 0xfff0
+#define DWARNING 0xffe0
+#define DNORMAL 0xffd0
+#define DVERBOSE 0xffc0
+#define DVVERBOSE 0xffb0
+#define DALL 0xffa0
+
+
+#define PIPER_DRIVER_NAME "piper"
+#define DRV_VERS "0.1"
+
+/* Useful defines for AES */
+#define PIPER_EXTIV_SIZE 8 /* IV and extended IV size */
+#define MIC_SIZE 8 /* Message integrity check size */
+#define ICV_SIZE 4
+#define DATA_SIZE 28 /* Data frame header+FCS size */
+#define CCMP_SIZE (PIPER_EXTIV_SIZE + MIC_SIZE) /* Total CCMP size */
+#define EXPANDED_KEY_LENGTH (176) /* length of expanded AES key */
+#define PIPER_MAX_KEYS (4)
+#define AES_BLOB_LENGTH (48) /* length of AES IV and headers */
+
+/* Calibration constants */
+#define WCD_MAGIC "WCALDATA"
+#define WCD_MAX_CAL_POINTS (8)
+#define WCD_CHANNELS_BG (14)
+#define WCD_CHANNELS_A (35)
+#define WCD_B_CURVE_INDEX (0)
+#define WCD_G_CURVE_INDEX (1)
+
+/*
+ * Set this #define to receive frames in the ISR. This may improve
+ * performance under heavy load at the expense of interrupt latency.
+ */
+#define WANT_TO_RECEIVE_FRAMES_IN_ISR (0)
+
+
+typedef u64 u48;
+
+/*
+ * This enum lists the possible LED states.
+ */
+enum led_states {
+ led_shutdown,
+ led_adhoc,
+ led_not_associated,
+ led_associated
+};
+
+/* Available leds */
+enum wireless_led {
+ STATUS_LED,
+ ACTIVITY_LED,
+};
+
+#define WCD_HW_REV_MASK 0xf000
+#define WCD_HW_REV_PROTOTYPE 0x0000
+#define WCD_HW_REV_PILOT 0x1000
+#define WCD_HW_REV_A 0x2000
+#define WCD_PLATFORM_MASK 0x0ff0
+#define WCD_CCW9P_PLATFORM 0x0010
+#define WCD_CCW9M_PLATFORM 0x0020
+
+
+typedef struct nv_wcd_header {
+ char magic_string[8]; /* WCALDATA */
+ char ver_major; /* Major version in ascii */
+ char ver_minor; /* Minor version in ascii */
+ u16 hw_platform; /* Hardware Platform used for calibration */
+ u8 numcalpoints; /* Number of points per curve */
+ u8 padding[107]; /* Reserved for future use */
+ u32 wcd_len; /* Total length of the data section */
+ u32 wcd_crc; /* Data section crc32 */
+} nv_wcd_header_t;
+
+typedef struct wcd_point {
+ s16 out_power; /* Output Power */
+ u16 adc_val; /* Measured ADC val */
+ u8 power_index; /* Airoha Power Index */
+ u8 reserved[3]; /* For future use */
+} wcd_point_t;
+
+typedef struct wcd_curve {
+ u8 max_adc_value; /* maximum allowed ADC value for this curve */
+ u8 reserved[3]; /* Resered for future use */
+ /* Calibration curve points */
+ wcd_point_t points[WCD_MAX_CAL_POINTS];
+} wcd_curve_t;
+
+typedef struct wcd_data {
+ nv_wcd_header_t header;
+ wcd_curve_t cal_curves_bg[WCD_CHANNELS_BG][2];
+ wcd_curve_t cal_curves_a[WCD_CHANNELS_A];
+} wcd_data_t;
+
+typedef enum {
+ op_write,
+ op_or,
+ op_and
+} reg_op_t;
+
+enum antenna_select {
+ ANTENNA_BOTH = 0,
+ ANTENNA_1,
+ ANTENNA_2,
+};
+
+typedef struct {
+ bool loaded;
+ bool enabled;
+ bool weSentLastOne;
+} piperBeaconInfo_t;
+
+typedef enum {
+ RECEIVED_ACK,
+ TX_COMPLETE,
+ OUT_OF_RETRIES,
+ TX_NOT_DONE
+} tx_result_t;
+
+
+/* Structure that holds the information we need to support H/W AES encryption */
+struct piperKeyInfo {
+ bool valid; /* indicates if this record is valid */
+ u8 addr[ETH_ALEN]; /* MAC address associated with key */
+ u32 expandedKey[EXPANDED_KEY_LENGTH / sizeof(u32)];
+ u48 txPn; /* packet number for transmit */
+ u48 rxPn; /* expected receive packet number */
+};
+
+/* rf */
+struct digi_rf_ops {
+ const char *name;
+ void (*init) (struct ieee80211_hw *, int);
+ int (*stop) (struct ieee80211_hw *);
+ int (*set_chan) (struct ieee80211_hw *, int chan);
+ int (*set_chan_no_rx) (struct ieee80211_hw *, int chan);
+ int (*set_pwr) (struct ieee80211_hw *, uint8_t val);
+ void (*set_pwr_index) (struct ieee80211_hw *, unsigned int val);
+ void (*power_on) (struct ieee80211_hw *, bool want_power_on);
+ void (*getOfdmBrs) (int channel, u64 brsBitMask, unsigned int *ofdm,
+ unsigned int *psk);
+ enum ieee80211_band (*getBand) (int);
+ int (*getFrequency) (int);
+ void (*set_hw_info)(struct ieee80211_hw *, int channel, u16 hw_platform);
+ const struct ieee80211_rate *(*getRate) (unsigned int);
+ int channelChangeTime;
+ s8 maxSignal;
+ struct ieee80211_supported_band *bands;
+ u8 n_bands;
+};
+
+struct piper_stats {
+ u32 rx_overruns;
+ u32 tx_complete_count;
+ u32 tx_start_count;
+ u32 tx_total_tetries;
+ u32 tx_retry_count[IEEE80211_TX_MAX_RATES];
+ u32 tx_retry_index;
+ struct ieee80211_tx_queue_stats tx_queue;
+ struct ieee80211_low_level_stats ll_stats;
+ spinlock_t lock;
+};
+
+enum piper_ps_mode {
+ PS_MODE_LOW_POWER,
+ PS_MODE_FULL_POWER
+};
+
+enum piper_ps_state {
+ PS_STATE_WAIT_FOR_BEACON,
+ PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT,
+ PS_STATE_WAIT_FOR_TRANSMITTER_DONE,
+ PS_STATE_WAIT_FOR_WAKEUP_ALARM,
+ PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT
+};
+
+struct piper_priv;
+
+struct piper_ps {
+ u32 beacon_int;
+ u16 aid;
+ volatile unsigned int scan_timer;
+ unsigned int sleep_time;
+ struct timer_list timer;
+ enum piper_ps_mode mode;
+ enum piper_ps_state state;
+ unsigned int this_event;
+ spinlock_t lock;
+ volatile bool power_management;
+ volatile bool poweredDown;
+ volatile bool rxTaskletRunning;
+ volatile bool allowTransmits;
+ volatile bool stopped_tx_queues;
+ volatile unsigned int frames_pending;
+};
+
+typedef void (*tx_skb_return_cb_t)(struct ieee80211_hw *hw,
+ struct sk_buff *skb);
+
+struct piper_queue {
+ struct sk_buff *skb;
+ tx_skb_return_cb_t skb_return_cb;
+};
+
+#define PIPER_TX_QUEUE_SIZE (16)
+#define NEXT_TX_QUEUE_INDEX(x) ((x+1) & 0xf)
+
+struct piper_priv {
+ const char *drv_name;
+ char debug_cmd[32];
+ u32 version;
+ struct piper_pdata *pdata;
+ struct ieee80211_hw *hw;
+ struct ieee80211_key_conf txKeyInfo;
+ struct ieee80211_cts ctsFrame;
+ struct ieee80211_rts rtsFrame;
+ struct ieee80211_rate *calibrationTxRate;
+ struct piper_stats pstats;
+ struct tasklet_struct rx_tasklet;
+ struct tasklet_struct tx_tasklet;
+ spinlock_t tx_tasklet_lock;
+ bool tx_tasklet_running;
+ spinlock_t tx_queue_lock;
+ struct piper_queue tx_queue[PIPER_TX_QUEUE_SIZE];
+ unsigned int tx_queue_head;
+ unsigned int tx_queue_tail;
+ unsigned int tx_queue_count;
+ bool expectingAck;
+ struct timer_list tx_timer;
+ struct timer_list led_timer;
+ enum led_states led_state;
+ struct piper_ps ps;
+ bool power_save_was_on_when_suspended;
+ struct access_ops *ac;
+ spinlock_t aesLock;
+ struct digi_rf_ops *rf;
+ void *__iomem vbase;
+ int irq;
+ tx_result_t tx_result;
+ int tx_signal_strength;
+
+ /* Function callbacks */
+ int (*init_hw) (struct piper_priv *, enum ieee80211_band);
+ int (*deinit_hw) (struct piper_priv *);
+ void (*set_irq_mask_bit) (struct piper_priv *, u32);
+ void (*clear_irq_mask_bit) (struct piper_priv *, u32);
+ u16(*get_next_beacon_backoff) (void);
+ int (*load_beacon) (struct piper_priv *, u8 *, u32);
+ int (*rand) (void);
+ void (*tx_calib_cb) (struct piper_priv *);
+ int (*set_antenna) (struct piper_priv *, enum antenna_select);
+ int (*set_tracking_constant) (struct piper_priv * piperp,
+ unsigned megahertz);
+ void (*adjust_max_agc) (struct piper_priv * piperp, unsigned int rssi,
+ _80211HeaderType * header);
+
+ /* General settings */
+ enum nl80211_iftype if_type;
+ bool areWeAssociated;
+ bool is_radio_on;
+ bool use_short_preamble;
+ int channel;
+ int tx_power;
+ u8 bssid[ETH_ALEN];
+ bool tx_cts;
+ bool tx_rts;
+ enum antenna_select antenna;
+ int power_duty;
+
+ /* AES stuff */
+ bool use_hw_aes;
+ u32 aes_key_count;
+ struct piperKeyInfo key[PIPER_MAX_KEYS];
+ u32 tx_aes_key;
+ u32 tx_aes_blob[AES_BLOB_LENGTH / sizeof(u32)];
+
+ /* IBSS */
+ piperBeaconInfo_t beacon;
+};
+
+struct access_ops {
+ spinlock_t reg_lock;
+ int (*wr_reg) (struct piper_priv *, u8 reg, u32 val, reg_op_t op);
+ u32(*rd_reg) (struct piper_priv *, u8 reg);
+ int (*wr_fifo) (struct piper_priv *, u8 addr, u8 * buf, int len);
+ int (*rd_fifo) (struct piper_priv *, u8 addr, u8 * buf, int len);
+};
+
+struct piper_pdata {
+ u8 macaddr[6];
+ int rst_gpio;
+ int irq_gpio;
+ int status_led_gpio;
+ int rf_transceiver;
+ wcd_data_t wcd;
+ int i2c_adapter_num;
+ struct piper_priv *piperp;
+
+ /* Platform callbacks */
+ void (*reset) (struct piper_priv *, int);
+ int (*init) (struct piper_priv *);
+ int (*late_init) (struct piper_priv *);
+ void (*set_led) (struct piper_priv *, enum wireless_led, int);
+ void (*early_resume) (struct piper_priv *);
+};
+
+/* main */
+int piper_alloc_hw(struct piper_priv **priv, size_t priv_sz);
+void piper_free_hw(struct piper_priv *priv);
+int piper_register_hw(struct piper_priv *priv, struct device *dev,
+ struct digi_rf_ops *rf);
+void piper_unregister_hw(struct piper_priv *priv);
+irqreturn_t piper_irq_handler(int irq, void *dev_id);
+void packet_tx_done(struct piper_priv *piperp,
+ tx_result_t result, int singalstrength);
+void piper_rx_tasklet(unsigned long context);
+void piper_tx_tasklet(unsigned long context);
+bool piper_prepare_aes_datablob(struct piper_priv *digi,
+ unsigned int keyIndex, u8 * aesBlob,
+ u8 * frame, u32 length, bool isTransmit);
+void piper_load_mac_firmware(struct piper_priv *piperp);
+void piper_load_dsp_firmware(struct piper_priv *piperp);
+int piper_spike_suppression(struct piper_priv *piperp, bool retry);
+void piper_reset_mac(struct piper_priv *piperp);
+void piper_set_macaddr(struct piper_priv *piperp);
+int piper_hw_tx_private(struct ieee80211_hw *hw, struct sk_buff *skb, tx_skb_return_cb_t fn);
+void piper_empty_tx_queue(struct piper_priv *piperp);
+int piper_tx_enqueue(struct piper_priv *piperp, struct sk_buff *skb, tx_skb_return_cb_t skb_return_cb);
+struct sk_buff *piper_tx_getqueue(struct piper_priv *piperp);
+bool piper_tx_queue_half_full(struct piper_priv *piperp);
+void piper_set_macaddr(struct piper_priv *piperp);
+void piper_MacEnterActiveMode(struct piper_priv *piperp, bool want_spike_suppression);
+int piper_MacEnterSleepMode(struct piper_priv *piperp, bool force);
+void piper_sendNullDataFrame(struct piper_priv *piperp, bool isPowerSaveOn);
+void piper_ps_rx_task_exiting(struct piper_priv *piperp);
+void piper_ps_scan_event(struct piper_priv *piperp);
+
+/*
+ * Defines for debugging function dumpRegisters
+ */
+#define MAIN_REGS (1)
+#define MAC_REGS (2)
+#define RF_REGS (4)
+#define FRAME_BUFFER_REGS (8)
+#define CTRL_STATUS_REGS (0x10)
+#define FIFO_REGS (0x20)
+#define IRQ_REGS (0x40)
+#define ALL_REGS (0xf)
+
+void digiWifiDumpRegisters(struct piper_priv *digi, unsigned int regs);
+void digiWifiDumpSkb(struct sk_buff *skb);
+
+extern void digiWifiDumpWordsAdd(unsigned int word);
+extern void digiWifiDumpWordsDump(void);
+extern void digiWifiDumpWordsReset(void);
+
+#endif /* __PIPER_H_ */
diff --git a/drivers/net/wireless/digi_wi_g.c b/drivers/net/wireless/digi_wi_g.c
new file mode 100644
index 000000000000..af9dd8eb4694
--- /dev/null
+++ b/drivers/net/wireless/digi_wi_g.c
@@ -0,0 +1,5021 @@
+/*
+ * wifi/digi_wi_g.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version2 as published by
+ * the Free Software Foundation.
+*/
+/*
+ * !Revision: $Revision: 1.147 $
+ * !Author: Bernd Westermann/Markus Pietrek/Miriam Ruiz
+ * !Descr:
+ * !References: [1] 802.11G_Programming_Guide_1.0
+ * [2] /net/m/ISO_STANDARDS/802.11g-2003.pdf
+ * [3] Wireless LANs: 802.11 WLAN Technologie und praktische
+ * Umsetzung im Detail
+*/
+
+#include <linux/module.h>
+#include <linux/platform_device.h> /* platform_get */
+#include <linux/init.h> /* __init */
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay */
+#include <linux/crc32.h>
+#include <linux/vmalloc.h> /* vmalloc */
+#include <linux/random.h> /* get_random_bytes */
+#include <linux/workqueue.h>
+
+#include <asm/uaccess.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/irq.h> /* NO_IRQ */
+#include <mach/regs-mem.h>
+#include <mach/regs-sys-common.h>
+
+/* for supported_rates */
+#include <linux/../../net/ieee80211/softmac/ieee80211softmac_priv.h>
+#undef assert
+
+#include "digi_wi_g.h"
+
+#define REMOVE_ME
+
+#ifdef CONFIG_DIGI_WI_G_DEBUG
+# define DBG_INIT 0x0001
+# define DBG_MINOR 0x0002
+# define DBG_RX 0x0004
+# define DBG_TX 0x0008
+# define DBG_INT 0x0010
+# define DBG_INTERFACE 0x0020
+# define DBG_LINK 0x0040
+# define DBG_SECURITY 0x0080
+# define DBG_UPDATE_RATE 0x0100
+# define DBG_TIMEOUT 0x0200
+# define DBG_TIME 0x0800
+# define DBG_MAJOR 0x1000
+# define DBG_HW_IO 0x2000
+# define DBG_ERROR 0x4000
+# define DBG_ERROR_CRIT 0x8000
+
+static unsigned int dw_dbg_level = DBG_ERROR_CRIT;
+
+# define DBG(flag, format, ...) \
+ do { \
+ if ((dw_dbg_level & (flag)) == (flag)) \
+ printk(KERN_INFO format "\n", ##__VA_ARGS__); \
+ } while (0)
+# define DBG_FN(flag) \
+ do { \
+ if ((dw_dbg_level & (flag)) == (flag)) \
+ printk(KERN_INFO DRIVER_NAME "@%s:line %d\n", __func__, __LINE__); \
+ } while (0)
+# define DBG_EXEC(expr) \
+ do { \
+ expr; \
+ } while (0)
+
+# define ASSERT(expr) \
+ do { \
+ if (!(expr)) { \
+ ERROR("Assertion failed! line %d %s", \
+ __LINE__,#expr); \
+ } \
+ } while (0)
+# define REQUIRE_LOCKED_ANY(lock) \
+ do { \
+ if (unlikely(!spin_is_locked(&lock))) { \
+ ERROR(#lock " should be locked\n"); \
+ dump_stack(); \
+ } \
+ } while (0)
+
+# define REQUIRE_UNLOCKED_ANY(lock) \
+ do { \
+ if (unlikely(spin_is_locked(&lock))) { \
+ ERROR(#lock " should be unlocked\n"); \
+ dump_stack(); \
+ } \
+ } while (0)
+
+# define REQUIRE_LOCKED(priv) REQUIRE_LOCKED_ANY(priv->lock)
+# define REQUIRE_UNLOCKED(priv) REQUIRE_UNLOCKED_ANY(priv->lock)
+
+/* for timing collection */
+enum {
+ INT = 0,
+ INT_DONE,
+ INT_TASKLET,
+ INT_TASKLET_DONE,
+ RX_FIFO,
+ RX_AES_FIFO,
+ TX_FIFO,
+ TX_AES_FIFO,
+ RX_TASKLET,
+ RX_TASKLET_DONE,
+ RX_FRAME_TO_STACK,
+ RX_FRAME_TO_STACK_DONE,
+ RX_PROCESS_FRAME,
+ RX_PROCESS_FRAME_RX,
+ RX_PROCESS_FRAME_DONE,
+ RX_DECRYPT,
+ RX_DECRYPT_DONE,
+ RX_OVERRUN,
+ RX_WORK,
+ RX_WORK_DONE,
+ RX_PAUSE,
+ START_XMIT,
+ START_XMIT_DONE,
+ TX_TASKLET,
+ TX_TASKLET_DONE,
+ TX_SEND,
+ INTERMEDIATE,
+ INTERMEDIATE2,
+ SET_CHANNEL,
+};
+
+# define NAME(x) [x] = #x
+
+static const char* ns_hperf_type_names[] = {
+ NAME(INT),
+ NAME(INT_DONE),
+ NAME(INT_TASKLET),
+ NAME(INT_TASKLET_DONE),
+ NAME(RX_FIFO),
+ NAME(RX_AES_FIFO),
+ NAME(TX_FIFO),
+ NAME(TX_AES_FIFO),
+ NAME(RX_TASKLET),
+ NAME(RX_TASKLET_DONE),
+ NAME(RX_FRAME_TO_STACK),
+ NAME(RX_FRAME_TO_STACK_DONE),
+ NAME(RX_PROCESS_FRAME),
+ NAME(RX_PROCESS_FRAME_RX),
+ NAME(RX_PROCESS_FRAME_DONE),
+ NAME(RX_DECRYPT),
+ NAME(RX_DECRYPT_DONE),
+ NAME(RX_OVERRUN),
+ NAME(RX_WORK),
+ NAME(RX_WORK_DONE),
+ NAME(RX_PAUSE),
+ NAME(START_XMIT),
+ NAME(START_XMIT_DONE),
+ NAME(TX_TASKLET),
+ NAME(TX_TASKLET_DONE),
+ NAME(TX_SEND),
+ NAME(INTERMEDIATE),
+ NAME(INTERMEDIATE2),
+ NAME(SET_CHANNEL),
+};
+
+#define NS_HPERF_SIZE 2048
+
+/* ns_hperf_type_names needs to be define first before including */
+//# include <asm/arch-ns9xxx/ns9xxx_hperf.h>
+
+/* only in debug case we are interested in revision */
+# define REVISION " $Revision: 1.147 $"
+#else /* CONFIG_DIGI_WI_G_DEBUG */
+# define DBG(flag, format, ...) do {} while (0)
+# define DBG_FN(flag) do {} while (0)
+# define DBG_EXEC(flag) do {} while (0)
+# define ASSERT(expr) do {} while (0)
+# define REQUIRE_LOCKED(lock) do {} while (0)
+# define REQUIRE_LOCKED_ANY(lock) do {} while (0)
+# define REQUIRE_UNLOCKED(lock) do {} while (0)
+# define REQUIRE_UNLOCKED_ANY(lock) do {} while (0)
+# define REVISION ""
+#endif /* CONFIG_DIGI_WI_G_DEBUG */
+
+#define CLEAR(x) memset(&(x), 0, sizeof((x)))
+#define dw_iosetbits32(offs, mask) dw_iowrite32(dw_ioread32(offs)|(mask),(offs))
+#define dw_iocleanbits32(offs, mask) dw_iowrite32(dw_ioread32(offs)&~(mask),(offs))
+
+#define BASIC_RATE_MASK(x) ((x) & ((unsigned char) ~IEEE80211_BASIC_RATE_MASK))
+#define BASIC_RATE(x) ((x) | IEEE80211_BASIC_RATE_MASK)
+
+#define ERROR(format, ...) printk(KERN_ERR "*** ERROR " DRIVER_NAME\
+ " @ %s: " format "\n", \
+ __func__, \
+ ##__VA_ARGS__)
+#define ERRORL(format, ...) printkl(KERN_ERR "*** ERROR " DRIVER_NAME\
+ " @ %s: " format "\n", \
+ __func__, \
+ ##__VA_ARGS__)
+#define WARNL(format, ...) printkl(KERN_INFO DRIVER_NAME\
+ " @ %s: " format "\n", \
+ __func__, \
+ ##__VA_ARGS__)
+#define to_dev(pdev) platform_get_drvdata(pdev)
+
+#define rate_is_enough(priv) ((priv)->rate.tx_data_any >= 4)
+/* at least 3/4 are acknowledged */
+#define rate_is_success(priv) \
+ (4 * (priv)->rate.tx_data_ack > 3 * (priv)->rate.tx_data_any)
+/* less than 1/4 are not acknowledged */
+#define rate_is_failure(priv) \
+ (4 * (priv)->rate.tx_data_ack < (priv)->rate.tx_data_any)
+
+#define MAC_GROUP 0x01 /* broadcast or multicast address */
+#define IS_GROUP_ADDR(addr) (addr[ 0 ] & MAC_GROUP)
+
+#define IS_FRAME(fc, ftype, stype) \
+ (((fc) & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == (ftype | stype))
+#define IS_DATA(fc) IS_FRAME(fc, IEEE80211_FTYPE_DATA, IEEE80211_STYPE_DATA)
+#define IS_MGMT(fc) IS_FRAME(fc, IEEE80211_FTYPE_MGMT, 0 /* any */)
+
+
+#define ACK_SIZE 14 /* ACK frame size */
+
+#define USE_SHORTPRE(fi, rate) \
+ (((rate) != IEEE80211_CCK_RATE_1MB) && fi->use_short_preamble)
+/* from Net+OS: mac_rate.c */
+/* not including SIFS and PLCP preamble/header */
+#define LENGTH(bytes, rate) ((16 * (bytes) + (rate) - 1) / (rate))
+/* Length (in usecs) of SIFS and PLCP preamble/header. */
+#define PRE_LEN( fi, rate) (USE_SHORTPRE(fi, rate) ? 106 : 202)
+/* Duration (in usecs) of an OFDM frame at rate (in 500kbps units)
+ * including SIFS and PLCP preamble/header */
+#define OFDM_DUR(bytes, rate) (36 + 4 * ((4 * (bytes) + (rate) + 10) / (rate)))
+
+typedef struct {
+ u8 bps; /* bit rate in 500kbps units */
+ u8 ofdm_code; /* ofdm rate code, 0 if not ofdm */
+ u16 ack_len; /* duration of ack or cts in usecs */
+} rate_info_t;
+
+typedef int dw_rate_index_t;
+
+/* separate maintained static because of polling performance */
+static void* __iomem vbase = NULL;
+
+static void dw_cw_set(const dw_priv_t* priv, u16 fc);
+static void dw_rx_fifo_error(dw_priv_t* priv);
+static int dw_rx_frame_get_length(const dw_frame_rx_t* frame);
+static int dw_rx_frame_is_duplicate(dw_priv_t* priv,
+ const struct ieee80211_hdr_3addr* hdr);
+static void dw_rx_frame_give_to_stack(dw_priv_t* priv, dw_frame_rx_t* frame);
+static void dw_rx_frame_fetch(dw_priv_t* priv);
+static void dw_rx_tasklet_handler(unsigned long data);
+static void dw_tx_set_plcp(dw_frame_tx_info_t* fi, int fragment, int rate);
+static int dw_tx_frame_prepare(dw_priv_t* priv,
+ dw_frame_tx_info_t* fi, struct ieee80211_txb* txb);
+static void dw_tx_fragment_send(dw_priv_t* priv, dw_frame_tx_t* frame);
+static void dw_tx_tasklet_handler(unsigned long data);
+static irqreturn_t dw_int(int irq, void *dev_id);
+static void dw_tx_reset(dw_priv_t* priv);
+static void dw_tx_wait_for_idle_and_pause(dw_priv_t* priv);
+static void dw_tx_continue_queue(dw_priv_t* priv);
+static int dw_ieee80211_hard_start_xmit(struct ieee80211_txb* txb,
+ struct net_device* dev, int priority);
+static void dw_update_status_led(dw_priv_t* priv);
+static void dw_rate_reset(dw_priv_t* priv);
+static void dw_rate_update(dw_priv_t* priv);
+static void dw_management_timer(u_long a);
+
+/* initialization stuff */
+static int __init dw_hw_init_card(struct net_device* dev);
+static int __init dw_start_dev(struct platform_device* pdev);
+static int __init dw_probe( struct platform_device* pdev);
+static void dw_stop_dev(struct platform_device* pdev);
+static int dw_remove(struct platform_device* pdev);
+static void dw_release_device(struct device* dev);
+
+static void dw_set_channel(dw_priv_t*priv, u8 channel);
+
+/* softmac/ieee interface stuff */
+static void dw_softmac_txrates_change(struct net_device* dev, u32 changes);
+static int dw_wx_set_encode(struct net_device* dev,
+ struct iw_request_info* info,
+ union iwreq_data* data, char* extra);
+static int dw_wx_set_encodeext(struct net_device* dev,
+ struct iw_request_info* info, union iwreq_data* data,
+ char* extra);
+static void dw_softmac_notify_authenticated(
+ struct net_device* dev, int event_type, void* context);
+static void dw_softmac_set_chan(struct net_device* dev, u8 channel);
+static void dw_softmac_set_bssid_filter(
+ struct net_device *dev, const u8* bssid);
+static void dw_ieee80211_set_security(struct net_device* dev,
+ struct ieee80211_security* sec);
+static int dw_geo_init(dw_priv_t* priv);
+static int dw_open(struct net_device* dev);
+static int dw_close(struct net_device* dev);
+static void dw_set_multicast_list(struct net_device* dev);
+
+/* ********** Local Variables ********** */
+
+static const char* dw_version =
+ "WiFi: " DRIVER_NAME " driver " COMPILE_TIME REVISION;
+
+/* define the resources the driver will use */
+static struct resource dw_mem = {
+ .name = DRIVER_NAME,
+ .start = MAC_BASE_PHYS,
+ .end = MAC_BASE_PHYS + MAC_BASE_SIZE,
+ .flags = IORESOURCE_MEM,
+};
+
+/* describes the device */
+static struct platform_device dw_device = {
+ .id = -1,
+ .name = DRIVER_NAME,/* must be equal to platform-driver.driver.name*/
+ .resource = &dw_mem,
+ .dev = {
+ .release = dw_release_device,
+ },
+};
+
+/* describes the driver */
+static struct platform_driver dw_driver = {
+ .probe = dw_probe,
+ .remove = dw_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/* RF transceiver frequency divider for each channel */
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+static const struct {
+ u16 integer;
+ u16 fraction;
+} freq_table[] = {
+ { 0, 0 },
+ { 0x6847, 0x0999 }, /* 1 (2412 MHz) */
+ { 0x6847, 0x099b }, /* 2 (2417 MHz) */
+ { 0x6867, 0x0998 }, /* 3 */
+ { 0x6867, 0x099a }, /* 4 */
+ { 0x6867, 0x0999 }, /* 5 */
+ { 0x6867, 0x099b }, /* 6 */
+ { 0x6857, 0x0998 }, /* 7 */
+ { 0x6857, 0x099a }, /* 8 */
+ { 0x6857, 0x0999 }, /* 9 */
+ { 0x6857, 0x099b }, /* 10 */
+ { 0x6877, 0x0998 }, /* 11 */
+ { 0x6877, 0x099a }, /* 12 */
+ { 0x6877, 0x0999 }, /* 13 (2472 MHz) */
+ { 0x684f, 0x0ccc }, /* 14 (2484 MHz) */
+};
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+static const struct {
+ u16 integer;
+ u16 fraction;
+} freq_table[] = {
+ { 0, 0 },
+ { 0x04c7, 0x0999 }, /* 1 (2412 MHz) */
+ { 0x04c7, 0x099b }, /* 2 (2417 MHz) */
+ { 0x04e7, 0x0998 }, /* 3 */
+ { 0x04e7, 0x099a }, /* 4 */
+ { 0x04e7, 0x0999 }, /* 5 */
+ { 0x04e7, 0x099b }, /* 6 */
+ { 0x04d7, 0x0998 }, /* 7 */
+ { 0x04d7, 0x099a }, /* 8 */
+ { 0x04d7, 0x0999 }, /* 9 */
+ { 0x04d7, 0x099b }, /* 10 */
+ { 0x04f7, 0x0998 }, /* 11 */
+ { 0x04f7, 0x099a }, /* 12 */
+ { 0x04f7, 0x0999 }, /* 13 (2472 MHz) */
+ { 0x04cf, 0x0ccc }, /* 14 (2484 MHz) */
+};
+#endif
+/* see [2] 10.4.42. */
+static const u8 dw_rates[ RATES_SUPPORTED ] = {
+ BASIC_RATE(IEEE80211_CCK_RATE_1MB),
+ BASIC_RATE(IEEE80211_CCK_RATE_2MB),
+ BASIC_RATE(IEEE80211_CCK_RATE_5MB),
+ /* we need to give 11MB also to AP, as otherwise we are not
+ * authenticated or softmac ignores it because rates don't match */
+ BASIC_RATE(IEEE80211_CCK_RATE_11MB),
+ IEEE80211_OFDM_RATE_6MB,
+ IEEE80211_OFDM_RATE_9MB,
+ IEEE80211_OFDM_RATE_12MB,
+ IEEE80211_OFDM_RATE_18MB,
+ IEEE80211_OFDM_RATE_24MB,
+ IEEE80211_OFDM_RATE_36MB,
+ IEEE80211_OFDM_RATE_48MB,
+ IEEE80211_OFDM_RATE_54MB,
+};
+
+/* basic rate will be calculated */
+#define MK_CCK(rate, ofdm) \
+ { .bps = rate, .ofdm_code = ofdm, .ack_len = LENGTH(ACK_SIZE, rate) }
+#define MK_OFDM(rate, ofdm) \
+ { .bps = rate, .ofdm_code = ofdm, .ack_len = OFDM_DUR(ACK_SIZE, rate) }
+/* they need to be ordered in their bitrate, because softmac returns us
+ * ap_ri.rate this way, and rates_info uses the indexes. */
+static const rate_info_t rates_info[ RATES_SUPPORTED ] = {
+ MK_CCK( IEEE80211_CCK_RATE_1MB, 0 ),
+ MK_CCK( IEEE80211_CCK_RATE_2MB, 0 ),
+ MK_CCK( IEEE80211_CCK_RATE_5MB, 0 ),
+ MK_OFDM(IEEE80211_OFDM_RATE_6MB, 0xb),
+ MK_OFDM(IEEE80211_OFDM_RATE_9MB, 0xf),
+ MK_CCK( IEEE80211_CCK_RATE_11MB, 0 ),
+ MK_OFDM(IEEE80211_OFDM_RATE_12MB, 0xa),
+ MK_OFDM(IEEE80211_OFDM_RATE_18MB, 0xe),
+ MK_OFDM(IEEE80211_OFDM_RATE_24MB, 0x9),
+ MK_OFDM(IEEE80211_OFDM_RATE_36MB, 0xd),
+ MK_OFDM(IEEE80211_OFDM_RATE_48MB, 0x8),
+ MK_OFDM(IEEE80211_OFDM_RATE_54MB, 0xc),
+};
+#undef MK_OFDM
+#undef MK_CCK
+
+#ifdef CONFIG_DIGI_WI_G_HW_ENCRYPTION
+# define DW_SW_AES_DEFAULT 0
+#else
+# define DW_SW_AES_DEFAULT 1
+#endif /* CONFIG_DIGI_WI_G_HW_ENCRYPTION */
+
+static unsigned int dw_sw_aes = DW_SW_AES_DEFAULT;
+static unsigned int dw_cfg_vco = 0; /* use built-in */
+
+/* Tables for the conversion of the signal strenght to dBm */
+/* Map LNA value to gain value */
+static const unsigned char lnatable[] = {0, 0, 23, 39};
+/* Map high gain values to dBm */
+static const char gaintable[] = {-82, -84, -85, -86, -87, -89, -90, -92, -94, -98};
+
+/* ********** Local Functions ********** */
+
+/* ********** inline stuff ********** */
+
+/**
+ * dw_to_48 - converts to numbers to an 48bit number
+ */
+static inline u48 dw_to_48(u32 n1, u16 n0)
+{
+ return (((u64) n1) << 16) | n0;
+}
+
+/**
+ * dw_48_inc - increments a 48bit number, wrapping aroung on 1<<48
+ */
+static inline void dw_inc_48(u48* n)
+{
+ (*n)++;
+ *n &= ((u64) 1 << 48) - 1;
+}
+
+/**
+ * dw_ioread32 - reads from memory mapped FPGA
+ */
+static inline u32 dw_ioread32(u32 offs)
+{
+ u32 val = ioread32(vbase + offs);
+
+ DBG(DBG_HW_IO, "R %04x = %x", offs, val);
+
+ return val;
+}
+
+/**
+ * dw_write32 - writes to memory mapped FPGA
+ */
+static inline void dw_iowrite32(u32 val, u32 offs)
+{
+ DBG(DBG_HW_IO, "W %04x = %x", offs, val);
+ iowrite32(val, vbase + offs);
+}
+
+/**
+ * dw_hw_get_status - returns the FPGA's status
+ */
+static inline u32 dw_hw_get_status(const dw_priv_t* priv)
+{
+ REQUIRE_LOCKED(priv);
+
+ return dw_ioread32(HW_GEN_STATUS);
+}
+
+/**
+ * dw_hw_memcpy_to - copies memory to FPGA @ offs
+ */
+static inline void dw_hw_memcpy_to(u32 offs, const void* _src, int len)
+{
+ u32* src = (u32 *) _src;
+
+ for (; len > 0; len -= 4, offs += 4)
+ dw_iowrite32(cpu_to_be32(*src++), offs);
+}
+
+/**
+ * dw_hw_memcpy_from - copies memory from FPGA @ offs
+ */
+static inline void dw_hw_memcpy_from(void* _dst, u32 offs, int len)
+{
+ u32* dst = (u32 *) _dst;
+
+ for (; len > 0; len -= 4, offs += 4)
+ *dst++ = be32_to_cpu(dw_ioread32(offs));
+}
+
+static inline void dw_hw_write_rf(u8 addr, u32 data)
+{
+ dw_iowrite32((((u32) addr) << 20) | data, HW_SPI_DATA);
+
+ udelay(10);
+}
+
+/**
+ * dw_hw_read_fifo - read's the FIFO's and swaps data
+ *
+ * no check for underrun is performed
+ */
+static inline void dw_hw_read_fifo(void * _dst, int _len)
+{
+ u32* dst = (u32 *) _dst;
+ int len = _len;
+
+ if (dw_ioread32(HW_GEN_STATUS) & STAT_RXFE)
+ printk("Reading from an EMPTY RX FIFO\n");
+
+ for (; len >= 32; len -= 32) {
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+ }
+
+ for (; len > 0; len -= 4)
+ *dst++ = be32_to_cpu(dw_ioread32(HW_DATA_FIFO));
+}
+
+/**
+ * dw_hw_write_fifo - writes swapped data to FIFO
+ *
+ * no check for overrun is performed
+ */
+static inline void dw_hw_write_fifo(const void* _src, int _len)
+{
+ const u32* src = (const u32 *) _src;
+ int len = _len;
+
+ for (; len >= 32; len -= 32) {
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+ }
+
+ for (; len > 0; len -= 4)
+ dw_iowrite32(cpu_to_be32(*src++), HW_DATA_FIFO);
+}
+
+/**
+ * dw_hw_aes_read_fifo - read's the AES FIFO's and swaps data
+ *
+ */
+static inline void dw_hw_aes_read_fifo(void* _dst, int len)
+{
+ int timeout = AES_BUSY_TIMEOUT;
+ u32* dst = (u32 *) _dst;
+
+ while (len > 0) {
+ if (!(dw_ioread32(HW_RSSI_AES) & AES_EMPTY)) {
+ *dst++ = be32_to_cpu(dw_ioread32(HW_AES_FIFO));
+ len -= 4;
+ } else {
+ /* !TODO. No calibration. When interrupts are enabled, use jiffies */
+ if (!timeout) {
+ ERROR("Timeout on read AES FIFO @ %i", len);
+ break;
+ }
+ timeout--;
+ }
+ } /* while (len > 0) */
+}
+
+/**
+ * dw_hw_aes_read_fifo_noswap - read's the AES FIFO's
+ *
+ */
+static inline void dw_hw_aes_read_fifo_noswap(void* _dst, int len)
+{
+ int timeout = AES_BUSY_TIMEOUT;
+ u32* dst = (u32 *) _dst;
+
+ while (len > 0) {
+ if (!(dw_ioread32(HW_RSSI_AES) & AES_EMPTY)) {
+ *dst++ = dw_ioread32(HW_AES_FIFO);
+ len -= 4;
+ } else {
+ /* !TODO: No calibration. When interrupts are enabled, use jiffies */
+ if (!timeout) {
+ ERROR("Timeout on read AES FIFO @ %i", len);
+ break;
+ }
+ timeout--;
+ }
+ } /* while (len > 0) */
+}
+
+/**
+ * dw_hw_aes_write_fifo - writes swapped data to AES FIFO
+ *
+ * no check for overrun is performed
+ */
+static inline void dw_hw_aes_write_fifo(const void* _src, int len)
+{
+ const u32* src = (const u32 *) _src;
+ int timeout = AES_BUSY_TIMEOUT;
+
+ while (len > 0) {
+ if (!(dw_ioread32(HW_RSSI_AES) & AES_FULL)) {
+ dw_iowrite32(cpu_to_be32(*src++), HW_AES_FIFO);
+ len -= 4;
+ } else {
+ /* !TODO: No calibration. When interrupts are enabled, use jiffies */
+ if (!timeout) {
+ ERROR("Timeout on write AES FIFO");
+ break;
+ }
+ timeout--;
+ }
+ } /* while (len > 0) */
+}
+
+/**
+ * dw_hw_aes_write_fifo_noswap - writes to AES FIFO
+ *
+ * no check for overrun is performed
+ */
+static inline void dw_hw_aes_write_fifo_noswap(const void* _src, int len)
+{
+ const u32* src = (const u32 *) _src;
+ int timeout = AES_BUSY_TIMEOUT;
+
+ while (len > 0) {
+ if (!(dw_ioread32(HW_RSSI_AES) & AES_FULL)) {
+ dw_iowrite32(*src++, HW_AES_FIFO);
+ len -= 4;
+ } else {
+ /* !TODO: No calibration. When interrupts are enabled, use jiffies */
+ if (!timeout) {
+ ERROR("Timeout on write AES FIFO");
+ break;
+ }
+ timeout--;
+ }
+ } /* while (len > 0) */
+}
+
+/**
+ * dw_hw_aes_wait - waits until AES is finished
+ *
+ * @return: 0 on timeout, otherwise > 1
+ */
+static inline int dw_hw_aes_wait(void)
+{
+ int timeout = AES_BUSY_TIMEOUT;
+
+ /* !TODO: redesign it to run with interrupts enabled, then use jiffies */
+ DBG_FN(DBG_TIMEOUT);
+
+ while (timeout && (dw_ioread32(HW_RSSI_AES) & AES_BUSY)) {
+ timeout--;
+ ndelay(1);
+ }
+
+ if (!timeout)
+ ERROR("Timedout on AES");
+
+ return timeout;
+}
+
+/**
+ * dw_channel_to_freq_a - calculates frequency out of channel (for 802.11a)
+ */
+static inline int dw_channel_to_freq_a(u8 channel)
+{
+ return (5000 + (5 * channel));
+}
+
+/**
+ * dw_channel_to_freq_bg - calculates frequency out of channel (for 802.11b/g)
+ */
+static inline int dw_channel_to_freq_bg(u8 channel)
+{
+ int freq;
+
+ if (14 == channel)
+ freq = 2484;
+ else
+ freq = 2407 + (5 * channel);
+
+ return freq;
+}
+
+static inline void dw_set_led_on(int led, u8 on)
+{
+ gpio_set_value(led, !on);
+}
+
+/**
+ * dw_list_move - moves a list from src and appends it to dst
+ */
+static inline void dw_list_move(struct list_head* dst, struct list_head* src)
+{
+ struct list_head* cursor;
+ struct list_head* next;
+
+ list_for_each_safe(cursor, next, src)
+ list_move_tail(cursor, dst);
+}
+
+static inline void dw_hw_set_vco(int channel)
+{
+ u32 vco = dw_cfg_vco;
+
+ if (!vco)
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+ vco = 0x46662;
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+ vco = 0x3020;
+#else
+ BUG();
+#endif
+ dw_hw_write_rf(3, vco);
+}
+
+/**
+ * dw_rate_info - returns the index to rates_basic/rates_info
+ *
+ * bitrate is in 500kbps
+ */
+static inline dw_rate_index_t dw_rate_info_index(int bitrate)
+{
+ dw_rate_index_t i = 0;
+
+ while (i < ARRAY_SIZE(rates_info)) {
+ if (rates_info[ i ].bps == bitrate)
+ return i;
+ i++;
+ }
+
+ ERROR("Unsupported rate %i\n", bitrate);
+
+ return 0;
+}
+
+/**
+ * dw_rx_pause - pauses the receiver
+ *
+ * This will lead probably to FIFO overruns. In this case, the FPGA will not
+ * send the acknowledgment, so the sender will try again.
+ * And this gives the stack time to clear the queue.
+ */
+static inline void dw_rx_pause(dw_priv_t* priv, char pause)
+{
+ DBG_FN(DBG_RX | DBG_MINOR);
+ REQUIRE_LOCKED(priv);
+
+ priv->rx.pause = pause;
+
+ if (pause)
+ dw_iocleanbits32(HW_INTR_MASK, INTR_RXFIFO);
+ else
+ dw_iosetbits32(HW_INTR_MASK, INTR_RXFIFO);
+}
+
+/* ********** now the not inline stuff ********** */
+
+/***********************************************************************
+ * @Function: dw_ccmp_get_data_tx
+ * @Return:
+ * @Descr: Get AES encryption data for a frame
+ ***********************************************************************/
+static void dw_ccmp_get_data_tx(dw_priv_t *priv, struct ieee80211_hdr_3addr * hdr,
+ const dw_fragment_tx_t* frag, ccmp_data_t* data,
+ u8* extiv, int dlen)
+{
+ ccmp_key_t *key = frag->crypt.key;
+ u8 *bp;
+
+ DBG_FN(DBG_TX);
+
+ /* Increment packet number */
+ dw_inc_48(&key->tx_pn);
+
+ CLEAR(*data);
+
+ memset(extiv, 0, EXTIV_SIZE);
+
+ SET16(extiv, key->tx_pn & 0xffff);
+ extiv[3] = priv->ieee->tx_keyidx << 6 | EXT_IV;
+
+ SET32(&extiv[4], key->tx_pn >> 16);
+
+ /* Set up CCM initial block for MIC IV */
+ data->init[0] = 0x59;
+ data->init[1] = 0;
+ memcpy (&data->init[2], hdr->addr2, ETH_ALEN);
+ data->init[8] = extiv[7];
+ data->init[9] = extiv[6];
+ data->init[10] = extiv[5];
+ data->init[11] = extiv[4];
+ data->init[12] = extiv[1];
+ data->init[13] = extiv[0];
+ data->init[14] = dlen >> 8;
+ data->init[15] = dlen;
+
+ /* Set up MIC header blocks */
+ bp = (u8 *) &hdr->frame_ctl;
+
+ data->header[0] = 0;
+ data->header[1] = 22;
+ data->header[2] = bp[0] & 0xcf;
+ data->header[3] = bp[1] & 0xd7;
+ memcpy(&data->header[4], hdr->addr1, 3*ETH_ALEN);
+ data->header[22] = WLAN_GET_SEQ_FRAG(le16_to_cpu(hdr->seq_ctl));
+ data->header[23] = 0;
+ memset (&data->header[24], 0, 8);
+}
+
+/***********************************************************************
+ * @Function: dw_ccmp_get_data_rx
+ * @Return:
+ * @Descr: Get AES encryption data for a frame
+ ***********************************************************************/
+static int dw_ccmp_get_data_rx(dw_priv_t *priv,
+ const struct ieee80211_hdr_3addr * hdr,
+ int dlen, ccmp_data_t *data)
+{
+ ccmp_key_t *key;
+ u8 *bp;
+
+ DBG_FN(DBG_RX);
+
+ /* Not encrypted */
+ if (dlen < 0 || !(hdr->payload[3] & EXT_IV)) {
+ return 0;
+ }
+
+ /* Key not set */
+ key = &priv->aeskeys[hdr->payload[3] >> 6];
+ if (!key->valid) {
+ return 0;
+ }
+
+ CLEAR(*data);
+
+ /* Set up CCM initial block for MIC IV */
+ data->init[0] = 0x59;
+ data->init[1] = 0;
+ memcpy (data->init+2, hdr->addr2, ETH_ALEN);
+
+ /* extiv */
+ data->init[8] = hdr->payload[7];
+ data->init[9] = hdr->payload[6];
+ data->init[10] = hdr->payload[5];
+ data->init[11] = hdr->payload[4];
+ data->init[12] = hdr->payload[1];
+ data->init[13] = hdr->payload[0];
+ data->init[14] = dlen >> 8;
+ data->init[15] = dlen;
+
+ /* Set up MIC header blocks */
+ bp = (u8 *) &hdr->frame_ctl;
+
+ data->header[0] = 0;
+ data->header[1] = 22;
+ data->header[2] = bp[0] & 0xcf;
+ data->header[3] = bp[1] & 0xd7;
+ memcpy (data->header+4, hdr->addr1, 3*ETH_ALEN);
+ data->header[22] = WLAN_GET_SEQ_FRAG(le16_to_cpu(hdr->seq_ctl));
+ data->header[23] = 0;
+ memset (data->header+24, 0, 8);
+
+ return 1;
+}
+
+/***********************************************************************
+ * Function: dump_hex_buffer
+ * Return: nothing
+ * Descr: prints a buffer hexadecimal and with character if printable
+ ***********************************************************************/
+static void dump_hex_buffer(const void* buffer, const int len)
+{
+ const unsigned char* hexbuffer = (const unsigned char*)buffer;
+ int i;
+ const int colcount = 16;
+ const int colnum = 4;
+ const int colcut = colcount / colnum;
+
+ for (i = 0; i < len; i += colcount) {
+ /* print one row*/
+ int j, rowlen;
+
+ if (i+colcount <= len)
+ rowlen = colcount;
+ else
+ rowlen = len - i;
+
+ printk("%08X ", (int) hexbuffer);
+ printk(" ");
+
+ /* print hexadecimal representation */
+ for (j = 0; j < rowlen; j++) {
+ printk("%02X ", *(hexbuffer+j));
+ if ((j + 1) % colcut == 0)
+ /* additional separator*/
+ printk(" ");
+ }
+
+ for (j = rowlen; j < colcount; j++)
+ printk(" ");
+
+ if (rowlen != colcount)
+ for (j = 0; j <= (colcount - rowlen - 1) / colcut; j++)
+ printk(" ");
+
+ printk(" ");
+
+ /* print character representation row */
+ for (j=0; j < rowlen; j++) {
+ unsigned char c = *(hexbuffer+j);
+ if (c < 32 || c > 127)
+ c = '.';
+
+ printk("%c", c);
+ }
+ printk("\n");
+ hexbuffer += colcount;
+ }
+}
+
+/**
+ * dw_plcp_get_bitrate_cck - returns the bitrate of HW's internal rate code
+ *
+ * retval is rate_code/5. But division is 1us slower than switch (5us to 4us)
+ */
+static int dw_plcp_get_bitrate_cck(int rate_code)
+{
+ volatile int ret;
+
+ DBG_FN(DBG_RX | DBG_MINOR);
+
+ switch(rate_code) {
+ case 0x0A: ret = IEEE80211_CCK_RATE_1MB; break;
+ case 0x14: ret = IEEE80211_CCK_RATE_2MB; break;
+ case 0x37: ret = IEEE80211_CCK_RATE_5MB; break;
+ case 0x6E: ret = IEEE80211_CCK_RATE_11MB; break;
+ default:
+// ERROR("Unknown rate_code %i cck, using 1MB\n", rate_code);
+ ret = IEEE80211_CCK_RATE_1MB; break;
+ }
+
+ return ret;
+}
+
+/**
+ * dw_plcp_get_ratecode_cck - returns the HW's internal rate code from bitrate
+ *
+ * retval is 5*bitrate. But switch is 1us slower than multiplication (2us to 3us)
+ */
+static int dw_plcp_get_ratecode_cck(int bitrate)
+{
+ DBG_FN(DBG_TX | DBG_MINOR);
+
+ /* no check for unsupported bitrates, but upper layer should have handled it */
+ return 5 * bitrate;
+}
+
+/**
+ * dw_plcp_get_bitrate_ofdm - returns the bitrate of HW's internal rate code.
+ *
+ * see [1], 2.5.2
+ */
+static int dw_plcp_get_bitrate_ofdm(int rate_code)
+{
+ int ret;
+
+ DBG_FN(DBG_RX | DBG_MINOR);
+
+ switch(rate_code) {
+ case 0xB: ret = IEEE80211_OFDM_RATE_6MB; break;
+ case 0xF: ret = IEEE80211_OFDM_RATE_9MB; break;
+ case 0xA: ret = IEEE80211_OFDM_RATE_12MB; break;
+ case 0xE: ret = IEEE80211_OFDM_RATE_18MB; break;
+ case 0x9: ret = IEEE80211_OFDM_RATE_24MB; break;
+ case 0xD: ret = IEEE80211_OFDM_RATE_36MB; break;
+ case 0x8: ret = IEEE80211_OFDM_RATE_48MB; break;
+ case 0xC: ret = IEEE80211_OFDM_RATE_54MB; break;
+ default:
+ ERROR("Unknown rate_code %i ofdm, using 6MB\n", rate_code);
+ ret = IEEE80211_OFDM_RATE_6MB; break;
+ }
+
+ return ret;
+}
+
+/**
+ * dw_plcp_get_ratecode_ofdm - returns the HW's internal rate code from bitrate
+ *
+ * see [1], 2.5.2
+ */
+static int dw_plcp_get_ratecode_ofdm(int bitrate)
+{
+ DBG_FN(DBG_TX | DBG_MINOR);
+
+ return rates_info[ dw_rate_info_index(bitrate) ].ofdm_code;
+}
+
+/**
+ * dw_cw_set - updates the contention window depending on the frame type
+ */
+static void dw_cw_set(const dw_priv_t* priv, u16 fc)
+{
+ int cw;
+
+ DBG_FN(DBG_INIT);
+ REQUIRE_LOCKED(priv);
+
+ if (fc & IEEE80211_STYPE_BEACON)
+ cw = 2 * CW_MIN + 1;
+ else if (fc & IEEE80211_STYPE_PROBE_RESP)
+ cw = CW_MIN;
+ else
+ cw = priv->cw;
+
+ /* Set backoff timer */
+ if (cw) {
+ u16 rand;
+ get_random_bytes(&rand, 2);
+
+ /* Pick a random time up to cw, convert to usecs */
+ cw = 10 * (rand & cw);
+ if (fc & IEEE80211_STYPE_BEACON)
+ dw_iowrite32(cw, HW_BEACON_BO);
+ else
+ dw_iowrite32(cw, HW_BACKOFF);
+ }
+}
+
+/**
+ * RF transceiver transmitter gain for each power level.
+ * This is the 0-15 power level, bit reversed.
+ */
+static unsigned char powerTable[] = {
+ 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
+ 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
+};
+
+/**
+ * This is used to set the transmit power. Values can range
+ * from 0-15, where 8 is the default and I am told provides about
+ * 12dBm (16mW) output power.
+ */
+static int dw_set_tx_power(struct dw_priv *priv, unsigned char value)
+{
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+ if (value > DW_TX_POWER_MAX_DBM)
+ value = DW_TX_POWER_MAX_DBM;
+
+ dw_hw_write_rf(5, 0x19e40 | powerTable[value]);
+ priv->tx_power = value;
+ return 1;
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+ if (value < 0)
+ value = 0;
+ // Map max value (15) to 14 to avoid hardware problems
+ else if (value > 14)
+ value = 14;
+#ifdef _UNDEFINED_
+ dw_hw_write_rf(5, 0x09e40 | powerTable[value]);
+ priv->tx_power = value;
+ return 1;
+#else
+ dw_hw_write_rf(5, 0x09ee0);
+ priv->tx_power = 0;
+ return -EIO;
+#endif
+
+#endif // defined(CONFIG_DIGI_WI_G_UBEC_JD)
+}
+
+/**
+ * dw_rx_fifo_error - reports a fifo error and resets it
+ */
+static void dw_rx_fifo_error(dw_priv_t* priv)
+{
+ DBG_FN(DBG_RX);
+ REQUIRE_LOCKED(priv);
+ REQUIRE_LOCKED_ANY(priv->ieee->lock);
+
+ /* the reason of the error should have been reported already */
+
+ /* give a pulse */
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_RXFIFORST);
+ wmb();
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_RXFIFORST);
+
+ priv->wstats.discard.misc++;
+}
+
+/**
+ * dw_rx_frame_get_length - determines the frame length from hardware values
+ *
+ * @return frame length in bytes or -1 on failure
+ */
+static int dw_rx_frame_get_length(const dw_frame_rx_t* frame)
+{
+ const dw_hw_hdr_rx_t* hdr = &frame->hdr;
+ int len = 0;
+
+ DBG_FN(DBG_RX);
+
+ if (MOD_OFDM == hdr->mod.mod_type) {
+ const dw_hw_ofdm_t* ofdm = &hdr->plcp.ofdm;
+ /* switch and no table because check for bad data from FPGA */
+ switch(ofdm->rate) {
+ case 0xB:
+ case 0xF:
+ case 0xA:
+ case 0xE:
+ case 0x9:
+ case 0xD:
+ case 0x8:
+ case 0xC:
+ break;
+ default:
+ ERROR("Wrong rate %i", ofdm->rate);
+ goto error;
+ }
+ len = ofdm->length;
+ } else {
+ const dw_hw_pskcck_t* pskcck = &hdr->plcp.pskcck;
+ int service;
+
+ /* service check */
+ service = pskcck->service;
+ service &= ~SERVICE_LOCKED;
+ service &= ~SERVICE_MODSEL;
+ service &= ~SERVICE_LENEXT;
+
+ /* Use a switch to avoid doing a divide operation.*/
+ len = pskcck->length;
+ switch(pskcck->signal) {
+ case 10:
+ len /= 8;
+ break;
+ case 20:
+ len /= 4;
+ break;
+ case 55:
+ len = (11 * len) / 16;
+ break;
+ case 110:
+ len = (11 * len) / 8;
+ if (pskcck->service & SERVICE_LENEXT)
+ len--;
+ break;
+ default:
+ ERRORL("Signal not defined %i %i", pskcck->signal,
+ pskcck->length);
+
+ /* !TODO: Remove me when solved */
+ dump_hex_buffer(hdr, sizeof(*hdr));
+ goto error;
+ }
+ }
+
+ /* check length for integrity? */
+ if (unlikely(len > HW_RX_FIFO_SIZE)) {
+ ERRORL("Wrong size %i", len);
+ goto error;
+ }
+
+ return len;
+
+error:
+ return -1;
+}
+
+/**
+ * dw_rx_frame_is_duplicate - checks whether the frame has been already
+ * received. This is not done by hardware.
+ *
+ * @return 1 if duplicate otherwise 1
+ */
+static int dw_rx_frame_is_duplicate(dw_priv_t* priv,
+ const struct ieee80211_hdr_3addr* hdr)
+{
+ struct list_head* it;
+ dw_duplicate_t* sender = NULL;
+ int is_duplicate = 0;
+
+ REQUIRE_LOCKED(priv);
+
+ /* do we had anything from that sender already? */
+ list_for_each(it, &priv->rx.dups.known.list) {
+ /* addr2 is sender */
+ dw_duplicate_t* entry = list_entry(it, dw_duplicate_t, list);
+ if (!memcmp(entry->src, hdr->addr2, ARRAY_SIZE(entry->src))) {
+ /* found sender */
+ sender = entry;
+ break;
+ }
+ } /* list_for_each */
+
+ if (NULL == sender) {
+ /* create an entry for the new sender */
+ struct list_head* new;
+
+ if (unlikely(list_empty(&priv->rx.dups.free.list)))
+ /* the last one in known list was the first
+ * added and so may possible be the oldest.
+ * Using jiffies is probably not necessary */
+ new = priv->rx.dups.known.list.prev;
+ else
+ new = priv->rx.dups.free.list.next;
+
+ /* move it to head of known entries */
+ list_del(new);
+ list_add(new, &priv->rx.dups.known.list);
+
+ sender = list_entry(new, dw_duplicate_t, list);
+
+ memcpy(sender->src, hdr->addr2, ARRAY_SIZE(sender->src));
+ } else {
+ /* did we receive the frame already? */
+ u16 fc;
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ if ((fc & IEEE80211_FCTL_RETRY) &&
+ (sender->seq_ctl == hdr->seq_ctl))
+ /* we did see already the sequence number */
+ is_duplicate = 1;
+ } /* if (NULL == sender) */
+
+ if (!is_duplicate)
+ /* update sequence control field */
+ sender->seq_ctl = hdr->seq_ctl;
+
+ return is_duplicate;
+}
+
+/**
+ * dw_rx_frame_decrypt - decrypts a frame when necessary.
+ *
+ * skb->data contains undecrypted data on return
+ */
+#ifndef REMOVE_ME
+static void dw_rx_frame_decrypt(dw_priv_t* priv, struct sk_buff* skb)
+{
+ DBG_FN(DBG_RX | DBG_MINOR);
+}
+#endif
+
+/**
+ * dw_rx_frame_give_to_stack - give the kernel/user the data
+ */
+static void dw_rx_frame_give_to_stack(dw_priv_t* priv, dw_frame_rx_t* frame)
+{
+ static struct ieee80211_rx_stats stats;
+ int gain; /* For received signal strength to dBm conversion */
+
+ DBG_FN(DBG_RX | DBG_MAJOR);
+
+ /* processing needs time, but we want to be able to stil copy frames
+ * from Rx FIFO. Therefore we are unlocked. */
+ REQUIRE_UNLOCKED(priv);
+
+#ifndef REMOVE_ME
+ dw_rx_frame_decrypt(priv, frame->skb);
+#endif
+
+ /* update stats */
+ CLEAR(stats);
+ stats.mask =
+ IEEE80211_STATMASK_RSSI |
+ IEEE80211_STATMASK_SIGNAL |
+ IEEE80211_STATMASK_RATE;
+ stats.mac_time = jiffies;
+ stats.rate = ((MOD_OFDM == frame->hdr.mod.mod_type) ?
+ dw_plcp_get_bitrate_ofdm(frame->hdr.plcp.ofdm.rate) :
+ dw_plcp_get_bitrate_cck(frame->hdr.plcp.pskcck.signal));
+ stats.freq = IEEE80211_24GHZ_BAND;
+ stats.len = frame->skb->len;
+
+
+ /* Convert received signal strength to dBm */
+ gain = lnatable[frame->hdr.mod.rssi_lna] + 2 * frame->hdr.mod.rssi_vga;
+ if (gain > 96)
+ stats.signal = -98;
+ else if (gain > 86)
+ stats.signal = gaintable[gain-87];
+ else
+ stats.signal = 5 - gain;
+
+ /* RSSI is used only internally to determine best network and not reported back */
+
+ /* RSSI (Received Signal Strength Indication) is a measurement of the
+ * power present in a received radio signal. In an IEEE 802.11 system
+ * RSSI is the received signal strength in a wireless environment, in
+ * arbitrary units. RSSI measurements will vary from 0 to 255 depending
+ * on the vendor. It consists of a one byte integer value. A value of 1
+ * will indicate the minimum signal strength detectable by the wireless
+ * card, while 0 indicates no signal. The value has a maximum of
+ * RSSI_Max. - http://en.wikipedia.org/wiki/RSSI
+ *
+ * See also: http://www.ces.clemson.edu/linux/dbm-rssi.shtml
+ */
+ stats.rssi = MAC_RSSI_MAX
+ - 15 * (frame->hdr.mod.rssi_lna - 1)
+ - 2 * (frame->hdr.mod.rssi_vga);
+
+ /* we don't know it here, but ieee80211 stack will look into it with
+ our patch in ieee80211_rx.c before
+ + if (255 == stats->received_channel)
+ + stats->received_channel = network->channel;
+ memcpy(&network->stats, stats, sizeof(network->stats));
+ */
+ stats.received_channel = 255;
+
+ /* put it on stack */
+ ieee80211_rx_any(priv->ieee, frame->skb, &stats);
+
+ /* allocate next buffer */
+ frame->skb = dev_alloc_skb(DW_MTU);
+ if (unlikely(NULL == frame->skb)) {
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_RXEN);
+ panic(DRIVER_NAME ": Out of memory\n");
+ }
+}
+
+/**
+ * dw_rx_frame_fetch - Copies a frame from FIFO
+ */
+static void dw_rx_frame_fetch(dw_priv_t* priv)
+{
+ struct list_head* element;
+ dw_frame_rx_t* frame;
+ int len;
+ int ignore_frame = 0;
+ u16 fc;
+ char spy_buf[2312];
+ unsigned int spy_len = 0;
+
+ DBG_FN(DBG_RX | DBG_INT);
+ REQUIRE_LOCKED(priv);
+
+ if (unlikely(list_empty(&priv->rx.queue.free.list))) {
+ ERROR("Frame received, but queue empty. Receiver should be paused");
+ len = 0xffff; /* for DBG_MARK */
+
+ /* nowhere to store. Reset FIFO */
+ spin_lock(&priv->ieee->lock);
+ priv->ieee->stats.rx_over_errors++;
+ dw_rx_fifo_error(priv);
+ spin_unlock(&priv->ieee->lock);
+
+ return;
+ }
+
+ /* use a free skb and fill it with the frame */
+ element = priv->rx.queue.free.list.next;
+ frame = list_entry(element, dw_frame_rx_t, list);
+
+ memset(spy_buf, 0, sizeof(spy_buf)); spy_len = 0;
+
+ /* copy frame header, swapped, we need it's header for length */
+ dw_hw_read_fifo(&frame->hdr, sizeof(frame->hdr));
+ memcpy(spy_buf + spy_len, &frame->hdr, sizeof(frame->hdr)); spy_len += sizeof(frame->hdr);
+
+ len = dw_rx_frame_get_length(frame);
+
+ if ((len >= sizeof(struct ieee80211_hdr)) && (len <= DW_MTU)) {
+ /* read data and if necessary decrypt it */
+ size_t remaining_len = len;
+ /* */
+ size_t delta_decrypt_header = sizeof(struct ieee80211_hdr_3addr) + EXTIV_SIZE - sizeof(struct ieee80211_hdr);
+
+ struct ieee80211_hdr* hdr = (struct ieee80211_hdr*) frame->skb->data;
+ struct ieee80211_hdr_3addr* hdr3 = (struct ieee80211_hdr_3addr*) hdr;
+ char* data = frame->skb->data;
+
+
+ /* reads data and keeps track of remaining_len */
+#define READ_FIFO(to_read) do { \
+ dw_hw_read_fifo(data, to_read); \
+ memcpy(spy_buf + spy_len, data, to_read); spy_len += to_read; \
+ data += to_read; \
+ remaining_len -= to_read; \
+ ASSERT(remaining_len >= 0); } while (0)
+
+ /* read header */
+ READ_FIFO(sizeof(struct ieee80211_hdr));
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ if (!dw_sw_aes &&
+ (fc & IEEE80211_FCTL_PROTECTED) &&
+ (remaining_len > delta_decrypt_header)) {
+ /* there is a encrypted frame present */
+ ccmp_data_t cdata;
+ ccmp_key_t* key;
+ size_t data_len;
+ int index;
+ u48 pn;
+
+ /* get frame headers and init vector from rx FIFO */
+ READ_FIFO(delta_decrypt_header);
+
+ /* get key index from message */
+ index = (hdr3->payload[ 3 ] >> 6) & (WEP_KEYS - 1);
+ key = &priv->aeskeys[ index ];
+
+ /* get packet number from IV and check for replay.
+ packet number must be greated or equal than
+ expected one. Takes care of wrap around. */
+ pn = dw_to_48(GET32(&hdr3->payload[ 4 ]),
+ GET16(&hdr3->payload[ 0 ]));
+
+ data_len = remaining_len - IEEE80211_FCS_LEN - MIC_SIZE;
+
+ if (key->valid && /* we know the key */
+ (pn - key->rx_pn >= 0) &&
+ dw_ccmp_get_data_rx(priv, hdr3, data_len, &cdata)) {
+ /* payload doesn't include MIC or CCMP */
+ len -= CCMP_SIZE;
+
+ /* !TODO. Convert AES_wait into a non-busy
+ polling function */
+ /* retreive and decrypt encoded payload data */
+ dw_hw_aes_wait();
+
+ /* configure mode and key */
+ dw_iowrite32(HW_AES_MODE_1 | (index & 0xf),
+ HW_AES_MODE);
+
+ /* this read puts AES into decrypt mode */
+ dw_ioread32(HW_AES_MODE);
+
+ /* write key and init vector to AES engine */
+ dw_hw_aes_write_fifo(&cdata, sizeof(cdata));
+
+ /* get decrypted payload data.
+ * We will overvwrite the CCMP header
+ * previously read. But the stack doesn't want
+ * to see it anyway because it expects unencrypted data */
+ dw_hw_aes_read_fifo(hdr3->payload, data_len);
+
+ /* wait for MIC calculation to finish.
+ !TODO: convert to non-busy */
+ dw_hw_aes_wait();
+
+ if (dw_ioread32(HW_RSSI_AES) & AES_MIC) {
+ /* frame was ok */
+ dw_inc_48(&key->rx_pn);
+ /* the stack should not convert it */
+ hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_PROTECTED);
+ } else {
+ ignore_frame = 1;
+ spin_lock(&priv->ieee->lock);
+ priv->ieee->ieee_stats.rx_discards_undecryptable++;
+ spin_unlock(&priv->ieee->lock);
+ ERROR("Wrong MIC");
+ }
+ } else {
+ /* TKIP decrypted? Le'ts handle it by SW */
+ READ_FIFO(remaining_len);
+ }
+#undef READ_FIFO
+ } else {
+ /* retrieve remaining unencrypted data.
+ We read FCS, but stack will ignore it. */
+ dw_hw_read_fifo(data, remaining_len);
+ memcpy(spy_buf + spy_len, data, remaining_len); spy_len += remaining_len;
+ }
+ } else {
+ if (len > DW_MTU)
+ {
+ ERROR("Oversized frame with %i bytes, ignoring it\n", len);
+
+ }
+
+ spin_lock(&priv->ieee->lock);
+ priv->ieee->stats.rx_length_errors++;
+ dw_rx_fifo_error(priv);
+ spin_unlock(&priv->ieee->lock);
+
+ ignore_frame = 1;
+ }
+
+ if (!ignore_frame) {
+ /* process frame */
+
+ int skblen = len - IEEE80211_FCS_LEN;/* need no FCS further */
+ int ignore = 0;
+ int control = ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL);
+
+ switch(fc & IEEE80211_FCTL_FTYPE) {
+ case IEEE80211_FTYPE_MGMT: /* no break */
+ case IEEE80211_FTYPE_DATA:
+ break;
+
+ case IEEE80211_FTYPE_CTL: /* no break */
+ default:
+ ignore = 1;
+ break;
+ }
+
+ if (!ignore &&
+ (len >= sizeof(struct ieee80211_hdr_3addr)) &&
+ dw_rx_frame_is_duplicate(priv,
+ (const struct ieee80211_hdr_3addr*) frame->skb->data))
+ ignore = 1;
+
+ if (!ignore) {
+ /* the layer ignores the above frames, so we don't
+ * need to alloc/free skb's for them. CTL are
+ * ignored anyway and not freed by layer (bug)?
+ * -> out of memory
+ *
+ * We have read the whole frame because a CTL frame is
+ * typically 10 Bytes large, the header 4 Bytes.
+ * Not much gain for reading only header, but a loose
+ * for all other frames and still some overhead of
+ * calculation */
+ skb_put(frame->skb, skblen);
+
+ if (IS_DATA(fc))
+ priv->rate.have_activity = 1;
+
+ list_move_tail(element, &priv->rx.queue.filled.list);
+
+ if (unlikely(list_empty(&priv->rx.queue.free.list)))
+ /* no room to store any longer. So, FIFO may
+ * overrun, but in this case, no 802.11
+ * acknowledgements are transmitted and the
+ * frame is repeatedly sent => less errors */
+
+ dw_rx_pause(priv, 1);
+ } else {
+ if (priv->tx.last_was_data && control &&
+ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ACK))
+ {
+ priv->tx.data_pending_ack = NULL;
+ priv->rate.tx_data_ack++;
+ }
+
+ } /* fc */
+ } else {
+ spin_lock(&priv->ieee->lock);
+ priv->ieee->stats.rx_dropped++;
+ spin_unlock(&priv->ieee->lock);
+ }
+}
+
+/**
+ * dw_rx_tasklet_handler - Read's all frames in fifo
+ */
+static void dw_rx_tasklet_handler(unsigned long data)
+{
+ dw_priv_t* priv = (dw_priv_t*) data;
+ struct list_head frames;
+ struct list_head* cursor;
+ unsigned long flags;
+
+ DBG_FN(DBG_RX | DBG_INT);
+
+ /* move the filled list to our context, so the tasklet is able to
+ * continue mostly unlocked. */
+ INIT_LIST_HEAD(&frames);
+
+ dw_lock(priv, flags);
+ dw_list_move(&frames, &priv->rx.queue.filled.list);
+ dw_unlock(priv, flags);
+
+ list_for_each(cursor, &frames) {
+ dw_frame_rx_t* frame = list_entry(cursor, dw_frame_rx_t, list);
+ dw_rx_frame_give_to_stack(priv, frame);
+ }
+
+ /* now move the processed frame list that is now empty again back to
+ free */
+ dw_lock(priv, flags);
+ dw_list_move(&priv->rx.queue.free.list, &frames);
+ dw_rx_pause(priv, 0);
+ dw_unlock(priv, flags);
+}
+
+/**
+ * dw_tx_reset - reset's the Tx FIFO when a timeout is detected
+ */
+static void dw_tx_reset(dw_priv_t* priv)
+{
+ DBG_FN(DBG_TX | DBG_ERROR);
+ REQUIRE_LOCKED(priv);
+
+ ERRORL("TX Reset and retrying transmission");
+
+ /* reset FIFO */
+
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_TXFIFORST);
+ wmb();
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_TXFIFORST);
+
+ priv->tx.data_pending_ack = NULL;
+
+ /* retransmit it */
+
+ if (likely(!list_empty(&priv->tx.queued))) {
+ dw_frame_tx_t* frame = list_entry(priv->tx.queued.prev, dw_frame_tx_t, list);
+ dw_tx_fragment_send(priv, frame);
+ } else {
+ ERROR("Tx Queue is empty, but we have a tx timeout????");
+ }
+}
+
+/**
+ * dw_tx_wait_for_idle_and_pause - on return, no frame is processed any more by
+ * Tx
+ *
+ * After the return it is safe to configure the transmitter
+ */
+static void dw_tx_wait_for_idle_and_pause(dw_priv_t* priv)
+{
+ unsigned long flags;
+ int sleep;
+
+ DBG_FN(DBG_TX);
+ REQUIRE_UNLOCKED(priv);
+
+ /* no more new transmits until dw_tx_continue_queue */
+ dw_lock(priv, flags);
+ priv->tx.pause = 1;
+ sleep = priv->tx.pending;
+ netif_stop_queue(priv->dev);
+ dw_unlock(priv, flags);
+
+ if (sleep)
+ /* wait for queue to be emptied.
+ * we mustn't be locked, because we can sleep. But that's not a
+ * problem. If the interrupt handler is faster, it will
+ * already have freed semaphore, so we run through */
+ down(&priv->tx.pause_sem);
+}
+
+static void dw_tx_continue_queue(dw_priv_t* priv)
+{
+ unsigned long flags;
+
+ DBG_FN(DBG_TX);
+ REQUIRE_UNLOCKED(priv);
+
+ dw_lock(priv, flags);
+
+ priv->tx.pause = 0;
+ netif_wake_queue(priv->dev);
+ if (!list_empty(&priv->tx.queued)) {
+ /* transmitter is paused, so nothing is pending. Send next
+ queued entry */
+ dw_frame_tx_t* frame = list_entry(priv->tx.queued.prev, dw_frame_tx_t, list);
+ dw_tx_fragment_send(priv, frame);
+ }
+
+ dw_unlock(priv, flags);
+}
+
+
+/**
+ * dw_tx_set_plcp - set's PLCP Header of the frame.
+ */
+static void dw_tx_set_plcp(dw_frame_tx_info_t* fi, int fragment, int rate)
+{
+ dw_fragment_tx_t* frag = &fi->frags[ fragment ];
+ dw_hw_hdr_tx_t* hdr = &frag->hdr;
+ size_t length = frag->phys_len;
+
+ DBG_FN(DBG_TX);
+
+ CLEAR(*hdr);
+ /* FIFO length in words of complete frame with header,
+ rounded up. FCS is added automatically */
+ hdr->mod.length = (sizeof(*hdr) + length + 3) / 4;
+
+ /* FCS length is required for signal */
+ length += IEEE80211_FCS_LEN;
+ if (!ieee80211_is_cck_rate(rate)) {
+ hdr->mod.mod_type = MOD_OFDM;
+ hdr->plcp.ofdm.rate = dw_plcp_get_ratecode_ofdm(rate);
+ hdr->plcp.ofdm.length = length;
+ hdr->plcp.ofdm.raw32 = cpu_to_le32(hdr->plcp.ofdm.raw32);
+ } else {
+ int signal = dw_plcp_get_ratecode_cck(rate);
+ hdr->mod.mod_type = MOD_PSKCCK;
+ hdr->plcp.pskcck.signal = signal;
+ hdr->plcp.pskcck.service = SERVICE_LOCKED;
+
+ /* convert length from bytes to microseconds */
+ switch(signal) {
+ case 10: length *= 8; break;
+ case 20: length *= 4; break;
+ case 55: length = (16 * length + 10) / 11; break;
+ case 110:
+ length = (8 * length + 10) / 11;
+ /* set length extension bit if needed */
+ if ((11 * length) / 8 > ( frag->phys_len + IEEE80211_FCS_LEN))
+ hdr->plcp.pskcck.service |= SERVICE_LENEXT;
+ break;
+ default:
+ ERRORL("Unsupported signal/rate %i/%i", signal,rate);
+ break;
+ }
+ hdr->plcp.pskcck.length = cpu_to_le16(length);
+ hdr->plcp.pskcck.raw32 = cpu_to_le32(hdr->plcp.pskcck.raw32);
+ }
+ hdr->mod.raw32 = cpu_to_le32(hdr->mod.raw32);
+}
+
+/**
+ * dw_tx_ack_duration -
+ *
+ * @return the duration for the acknowledgement of our data package in us
+ */
+static int dw_tx_ack_duration(
+ dw_priv_t* priv,
+ const dw_frame_tx_info_t* fi,
+ dw_rate_index_t index)
+{
+ const rate_info_t* rate_info = &rates_info[ priv->tx.basics[ index ] ];
+ /* ack/crts is sent at equal or lower basic rate */
+ int dur = rate_info->ack_len;
+
+ REQUIRE_LOCKED(priv);
+
+ /* add psk/cck preamble time */
+ if (!rate_info->ofdm_code)
+ dur += PRE_LEN(fi, rate_info->bps);
+
+ return dur;
+}
+
+/**
+ * dw_tx_duration -
+ *
+ * PLCP must be set.
+ * @return the duration for the frame in us
+ */
+static int dw_tx_duration(
+ dw_priv_t* priv,
+ const dw_frame_tx_info_t* fi,
+ int fragment,
+ int rate)
+{
+ int index = dw_rate_info_index(rate);
+ int dur;
+
+ /* frame duration */
+ if (rates_info[ index ].ofdm_code)
+ dur = OFDM_DUR(fi->frags[ fragment ].phys_len, rate);
+ else
+ dur = PRE_LEN(fi, rates_info[ index ].bps) + le16_to_cpu(fi->frags[ fragment ].hdr.plcp.pskcck.length);
+
+ return dur + dw_tx_ack_duration(priv, fi, index);
+}
+
+
+/**
+ * dw_tx_frame_prepare - prepare everything that is needed to send linux frame
+ *
+ * Determine PLCP, rate, sequence/fragment number
+ *
+ * @return 0 on failure. Frame needs to be dropped
+ */
+static int dw_tx_frame_prepare(
+ dw_priv_t* priv,
+ dw_frame_tx_info_t* fi,
+ struct ieee80211_txb* txb)
+{
+ struct ieee80211_hdr_3addr* hdr3 = NULL;
+ const struct ieee80211_hdr_1addr* hdr1 = NULL;
+ struct ieee80211_hdr* hdr = (struct ieee80211_hdr *)txb->fragments[ 0 ]->data;
+ int fc = le16_to_cpu(hdr->frame_ctl);
+ int i;
+ int rate;
+ int rate_last_fragment;
+ int rate_index;
+ int duration;
+
+ REQUIRE_LOCKED(priv);
+
+ if (txb->fragments[ 0 ]->len >= sizeof(struct ieee80211_hdr_3addr))
+ /* we need it for sequence number */
+ hdr3 = (struct ieee80211_hdr_3addr*) hdr;
+ if (txb->fragments[ 0 ]->len >= sizeof(struct ieee80211_hdr_1addr))
+ /* we need it for group addressing */
+ hdr1 = (struct ieee80211_hdr_1addr*) hdr;
+
+ CLEAR(*fi);
+ fi->txb = txb;
+ fi->is_data = IS_DATA(fc);
+
+ /* the stack sets encrypted whenenver the frame needs to be encrypted,
+ * but set's FCTL_PROTECTED only when it has encrypted it itself. This
+ * leaves CCMP for us.
+ * encrypted can also be set to 1 if we don't provide hardware. */
+ fi->use_hw_encryption = fi->txb->encrypted && !(fc & IEEE80211_FCTL_PROTECTED);
+ if (fi->use_hw_encryption) {
+ fc |= IEEE80211_FCTL_PROTECTED;
+ hdr->frame_ctl = cpu_to_le16(fc);
+ }
+
+ /* determine bit rate for fragment */
+ spin_lock(&priv->softmac->lock);
+ rate = rate_last_fragment = priv->softmac->txrates.default_rate;
+
+ if (IS_MGMT(fc))
+ rate_last_fragment = dw_rates[ 0 ];
+ else if ((NULL != hdr3) && is_multicast_ether_addr(hdr3->addr1))
+ rate = rate_last_fragment = priv->softmac->txrates.mcast_rate;
+ else if (fc & IEEE80211_FCTL_PROTECTED)
+ /* send all but the last at broadcast_rate */
+ rate_last_fragment = priv->softmac->txrates.mcast_rate;
+ spin_unlock(&priv->softmac->lock);
+
+ /* generate sequence number, encryption and plcp */
+ for (i = 0; i < fi->txb->nr_frags; i++) {
+ dw_fragment_tx_t* frag = &fi->frags[ i ];
+ struct sk_buff* skb = txb->fragments[ i ];
+ int last_frag = (i == fi->txb->nr_frags - 1);
+ int rate_frag = BASIC_RATE_MASK(last_frag ? rate_last_fragment : rate);
+
+ if (txb->fragments[ i ]->len >= sizeof(struct ieee80211_hdr_3addr)) {
+ int fragment = (fi->txb->rts_included ? (i - 1) : i);
+ /* it holds a sequence/fragment number */
+ struct ieee80211_hdr_3addr* frag_hdr3 = (struct ieee80211_hdr_3addr*) txb->fragments[ i ]->data;
+ /* see ieee80211.h: WLAN_GET_SEQ_FRAG(seq) */
+ frag_hdr3->seq_ctl = cpu_to_le16((atomic_read(&priv->tx.seq_nr) << 4) |
+ (fragment & 0xf));
+ }
+
+ fi->use_short_preamble =
+ (rate_frag != IEEE80211_CCK_RATE_1MB) && priv->softmac->bssinfo.short_preamble;
+
+ frag->phys_len = skb->len;
+
+ /* provide encryption. Requires sequence number for MIC */
+ if (fi->use_hw_encryption) {
+ frag->crypt.key_index = priv->ieee->tx_keyidx;
+ frag->crypt.key = &priv->aeskeys[ frag->crypt.key_index ];
+
+ /* Key not set */
+ if (!frag->crypt.key->valid)
+ goto error;
+
+ /* EXTIV and MIC are automatically appended */
+ frag->phys_len += CCMP_SIZE;
+ }
+
+ /* requires fragment length with encryption */
+ dw_tx_set_plcp(fi, i, rate_frag);
+ } /* plcp */
+
+ /* add duration, for initial fragments */
+ rate_index = dw_rate_info_index(BASIC_RATE_MASK(rate));
+ for (i = 0; i < fi->txb->nr_frags - 1; i++) {
+ struct ieee80211_hdr* frag_hdr = (struct ieee80211_hdr *)txb->fragments[ i ]->data;
+
+ /* ack of this frame + data of next frame */
+ duration = dw_tx_ack_duration(priv, fi, rate_index) +
+ dw_tx_duration(priv, fi, i + 1, rate);
+
+ frag_hdr->duration_id = cpu_to_le16(duration);
+ }
+
+ /* set duration for final or last fragment */
+ if ((NULL != hdr1) && IS_GROUP_ADDR(hdr1->addr1))
+ duration = 0;
+ else
+ duration = dw_tx_ack_duration(priv, fi,
+ dw_rate_info_index(BASIC_RATE_MASK(rate_last_fragment)));
+
+ hdr->duration_id = cpu_to_le16(duration);
+
+ if (NULL != hdr3)
+ /* no need for cutting it at 0xffff, will be done on
+ assignment to seq_ctl */
+ atomic_inc(&priv->tx.seq_nr);
+
+ return 1;
+
+error:
+ return 0;
+}
+
+/**
+ * dw_tx_fragment_send - copies a fragment to FIFO and sets all registers.
+ */
+static void dw_tx_fragment_send(
+ dw_priv_t* priv,
+ dw_frame_tx_t* frame)
+{
+ const dw_fragment_tx_t* frag = &frame->s.frags[ priv->tx.fragment ];
+ const struct sk_buff* skb = frame->s.txb->fragments[ priv->tx.fragment ];
+ struct ieee80211_hdr* hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_hdr_3addr* hdr3 = (struct ieee80211_hdr_3addr*) hdr;
+ int hw_gen_ctrl = dw_ioread32(HW_GEN_CONTROL);
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+
+ DBG_FN(DBG_TX);
+ REQUIRE_LOCKED(priv);
+
+ //if (priv->tx.retries) {
+ if (priv->tx.times_sent) {
+ /* Add RETRY flag, sequence number is already set */
+ fc |= IEEE80211_FCTL_RETRY;
+ hdr->frame_ctl = cpu_to_le16(fc);
+ if (priv->cw < CW_MAX)
+ /* code starts always with 2^n-1. Therefore, we still
+ * always have 2^n-1 and never overrun CW_MAX. */
+ priv->cw = priv->cw * 2 + 1;
+
+ priv->wstats.discard.retries++;
+ } else {
+ priv->cw = CW_MIN;
+ }
+
+ dw_cw_set(priv, fc);
+
+ if (frame->s.use_short_preamble)
+ hw_gen_ctrl |= GEN_SHPRE;
+ else
+ /* may be set from a previous frame */
+ hw_gen_ctrl &= ~GEN_SHPRE;
+
+ priv->tx.last_was_data = frame->s.is_data;
+ if (frame->s.is_data) {
+ priv->rate.have_activity = 1;
+ priv->rate.tx_data_any++;
+ }
+
+ /* sent frame, either bye AES or unencrypted engine */
+ if (!frame->s.use_hw_encryption) {
+ /* Prevent transmitting until all data have been written into the tx fifo */
+ dw_iowrite32(hw_gen_ctrl | GEN_TXHOLD, HW_GEN_CONTROL);
+ dw_hw_write_fifo(&frag->hdr, sizeof(frag->hdr));
+ dw_hw_write_fifo(skb->data, skb->len);
+ dw_iowrite32(hw_gen_ctrl, HW_GEN_CONTROL);
+ } else {
+ ccmp_data_t cdata;
+ u8 extiv[ EXTIV_SIZE ];
+
+ dw_ccmp_get_data_tx(priv, hdr3, frag, &cdata, extiv, skb->len - sizeof(*hdr3));
+
+ /* frame ctl is part of data used for MIC. */
+ dw_hw_aes_wait();
+
+ /* prevent transmitting until encrypted data is ready */
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_TXHOLD);
+
+ /* write MAC, Frame Header and IV FIFO */
+ dw_hw_write_fifo(&frag->hdr, sizeof(frag->hdr) );
+ dw_hw_write_fifo(hdr3, sizeof(*hdr3) );
+ dw_hw_write_fifo(&extiv, sizeof(extiv));
+
+ /* configure mode and key */
+ dw_iowrite32(HW_AES_MODE_1 | (frag->crypt.key_index & 0xf),
+ HW_AES_MODE);
+
+ /* write init block to AES FIFO */
+ dw_hw_aes_write_fifo(&cdata, sizeof(cdata));
+
+ /* start transmit */
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_TXHOLD);
+
+ /* write plaintext data to AES FIFO */
+ dw_hw_aes_write_fifo(hdr3->payload, skb->len - sizeof(*hdr3));
+ }
+ priv->dev->trans_start = jiffies;
+ priv->tx.pending = 1;
+ priv->tx.times_sent++;
+
+ if (priv->ieee->iw_mode != IW_MODE_ADHOC) {
+ if (((*(char *)(skb->data)) & 0x0C) == 0x08) { /* If it is a data frame */
+ priv->tx.data_pending_ack = (void *)frag;
+ priv->tx.jiffies_pending_ack = jiffies;
+ }
+ }
+}
+
+/**
+ * dw_tx_tasklet_handler - One fragment has been completed.
+ *
+ * is only called when entries are in tx.queued list
+ */
+static void dw_tx_tasklet_handler(unsigned long data)
+{
+ dw_priv_t* priv = (dw_priv_t*) data;
+ dw_frame_tx_t* frame = NULL;
+ unsigned int retry_frame = 0;
+ unsigned long flags;
+
+ DBG_FN(DBG_TX | DBG_INT);
+
+ dw_lock(priv, flags);
+
+ /* check if there are further fragments left */
+ if (list_empty(&priv->tx.queued) && !priv->tx.timeout)
+ /* Spurious Interrupt. May happen if we slow everything down
+ * with debug messages */
+ goto out;
+
+ spin_lock(&priv->ieee->lock); /* interrupts already disabled */
+
+ frame = list_entry(priv->tx.queued.next, dw_frame_tx_t, list);
+ if (priv->tx.timeout) {
+ int max_retries =
+ ((frame->s.txb->fragments[ priv->tx.fragment ]->len >= priv->ieee->rts) ?
+ priv->long_retry_limit :
+ priv->short_retry_limit);
+
+ priv->tx.retries++;
+ priv->tx.timeout = 0;
+
+ if (priv->tx.retries <= max_retries) {
+ retry_frame = 1;
+ } else {
+ priv->tx.data_pending_ack = NULL;
+ priv->ieee->ieee_stats.tx_retry_limit_exceeded++;
+ }
+ } /* if (priv->tx.timeout) */
+
+ if (!retry_frame && priv->tx.data_pending_ack == NULL) {
+ /* send next fragment */
+ priv->tx.fragment++;
+ if (priv->tx.fragment == frame->s.txb->nr_frags) {
+ /* frame is complete. Free resources */
+ ieee80211_txb_free(frame->s.txb);
+ frame->s.txb = NULL;
+
+ /* queue entry can be reused */
+ list_move_tail(priv->tx.queued.next, &priv->tx.free);
+ if (!list_empty(&priv->tx.queued))
+ /* take next frame's first fragment */
+ frame = list_entry(priv->tx.queued.next, dw_frame_tx_t, list);
+ else
+ frame = NULL;
+
+ priv->tx.fragment = 0;
+ priv->tx.retries = 0;
+ priv->tx.times_sent = 0;
+
+ if (!priv->tx.pause &&
+ netif_queue_stopped(priv->dev))
+ /* we have space again */
+ netif_wake_queue(priv->dev);
+
+ }
+ }
+
+ if (priv->tx.data_pending_ack && time_after(jiffies, priv->tx.jiffies_pending_ack + ACK_TIMEOUT)) {
+ priv->tx.data_pending_ack = NULL;
+ }
+
+ spin_unlock(&priv->ieee->lock);
+
+ if (NULL != frame)
+ {
+ dw_tx_fragment_send(priv, frame);
+ } else {
+ /* no more frames */
+ priv->tx.pending = 0;
+ }
+
+ if (priv->tx.pause && list_empty(&priv->tx.queued))
+ /* nothing is being processed any longer.
+ Awake listener */
+ up(&priv->tx.pause_sem);
+
+out:
+ dw_unlock(priv, flags);
+}
+
+/* Supported rates info elements */
+static const u8 ratesA[] = { DW_ELEM_SUPRATES, 8, DW_RATE_BASIC+12, 18, DW_RATE_BASIC+24, 36, DW_RATE_BASIC+48, 72, 96, 108 };
+static const u8 ratesB[] = { DW_ELEM_SUPRATES, 4, DW_RATE_BASIC+2, DW_RATE_BASIC+4, 11, 22 };
+static const u8 ratesG[] = { DW_ELEM_SUPRATES, 8, DW_RATE_BASIC+2, DW_RATE_BASIC+4, 11, 22, 12, 18, 24, 36 };
+static const u8 ratesGx[] = { DW_ELEM_EXTSUPRATES, 4, 48, 72, 96, 108 };
+
+static void dw_beacon_set_plcp(dw_hw_hdr_tx_t * hdr, int rate, size_t phys_len)
+{
+ size_t length = phys_len;
+ memset(hdr, 0, sizeof(*hdr));
+
+ /**
+ * Length in words, including Frame Header and PLCP Header, and excluding FCS, rounded up
+ * FCS is added automatically
+ */
+ hdr->mod.length = (phys_len - FCS_SIZE + 3) / 4;
+
+ /* FCS length is required for signal */
+ length += IEEE80211_FCS_LEN;
+ if (!ieee80211_is_cck_rate(rate)) {
+ hdr->mod.mod_type = MOD_OFDM;
+ hdr->plcp.ofdm.rate = dw_plcp_get_ratecode_ofdm(rate);
+ hdr->plcp.ofdm.length = length;
+ hdr->plcp.ofdm.raw32 = cpu_to_le32(hdr->plcp.ofdm.raw32);
+ } else {
+ int signal = dw_plcp_get_ratecode_cck(rate);
+ hdr->mod.mod_type = MOD_PSKCCK;
+ hdr->plcp.pskcck.signal = signal;
+ hdr->plcp.pskcck.service = SERVICE_LOCKED;
+
+ /* convert length from bytes to microseconds */
+ switch(signal) {
+ case 10: length *= 8; break;
+ case 20: length *= 4; break;
+ case 55: length = (16 * length + 10) / 11; break;
+ case 110:
+ length = (8 * length + 10) / 11;
+ /* set length extension bit if needed */
+ if ((11 * length) / 8 > ( phys_len + IEEE80211_FCS_LEN))
+ hdr->plcp.pskcck.service |= SERVICE_LENEXT;
+ break;
+ default:
+ ERRORL("Unsupported signal/rate %i/%i", signal, rate);
+ break;
+ }
+ hdr->plcp.pskcck.length = cpu_to_le16(length);
+ hdr->plcp.pskcck.raw32 = cpu_to_le32(hdr->plcp.pskcck.raw32);
+ }
+ hdr->mod.raw32 = cpu_to_le32(hdr->mod.raw32);
+}
+
+/**
+ * Store supported rates elements into a buffer
+ * @param bp Pointer into buffer
+ * @param elem Element to store: DW_ELEM_SUPRATES, DW_ELEM_EXTSUPRATES, or 0 for both
+ * @param channel Channel number
+ * @return Updated buffer pointer
+ */
+static u8 *dw_beacon_set_rates(u8 *bp, int elem, int channel)
+{
+ const u8 *sr;
+
+ if (DW_CHAN_5G (channel))
+ sr = ratesA;
+ //else if (OPT_BONLY)
+ // sr = ratesB;
+ else
+ sr = ratesG;
+
+ /* Store up to 8 supported rates */
+ if (elem != DW_ELEM_EXTSUPRATES) {
+ memcpy (bp, sr, sr[1]+2);
+ bp += bp[1] + 2;
+ }
+
+ /* Store remaining extended supported rates */
+ if (elem != DW_ELEM_SUPRATES && sr == ratesG) {
+ memcpy (bp, ratesGx, ratesGx[1]+2);
+ bp += bp[1] + 2;
+ }
+
+ return bp;
+}
+
+/**
+ * Create beacon and probe response frames to send in an IBSS
+ * @param interval Beacon interval in TU
+ * @return 1 if success, 0 if error
+ */
+static void dw_beacon_make_beacon(dw_priv_t * priv, int interval)
+{
+ dw_beacon_frame * bcnFrame = &priv->beacon_frame;
+ u16 atimWindow = 0; /* ATIM window size, 0 if none */
+
+ u8 *bp = bcnFrame->body;
+ u16 bss_caps = 0, caps;
+
+ u8 bss_addr[ ETH_ALEN ];
+
+ priv->beacon_body_length = 0;
+
+ bss_caps = DW_CAP_IBSS;
+ //if (!(macParams.encrypt & WLN_ENCR_OPEN))
+ // bss_caps |= CAP_PRIVACY;
+ //if (OPT_SHORTPRE)
+ // bss_caps |= CAP_SHORTPRE;
+
+ memcpy(bss_addr, priv->adhoc.bssid, ETH_ALEN);
+
+ /* Init beacon MAC header */
+ memset (bcnFrame, 0, sizeof (dw_beacon_frame));
+ bcnFrame->fc = IEEE80211_STYPE_BEACON;
+ memset (bcnFrame->addr1, 0xff, ETH_ALEN);
+ DW_SET_ADDR (bcnFrame->addr2, priv->dev->dev_addr); /* station MAC address */
+ DW_SET_ADDR (bcnFrame->addr3, bss_addr); /* BSS to associate with */
+
+ /** Set fixed params
+ * Timestamp is set by hardware */
+ SET16 (&bp[8], interval);
+
+ /* Set capabilities */
+ caps = bss_caps & (DW_CAP_ESS|DW_CAP_IBSS|DW_CAP_PRIVACY);
+ /* Use short preamble if allowed in BSS and params and rate > 1 mbps. */
+ /* caps |= DW_CAP_SHORTPRE; */
+ SET16 (&bp[10], caps);
+ bp += 12;
+
+ /* Set SSID */
+ bp[0] = DW_ELEM_SSID;
+ bp[1] = priv->softmac->associnfo.req_essid.len;
+ memcpy (&bp[2], priv->softmac->associnfo.req_essid.data, priv->softmac->associnfo.req_essid.len);
+ bp += bp[1] + 2;
+
+ /* Set supported rates */
+ bp = dw_beacon_set_rates(bp, DW_ELEM_SUPRATES, priv->adhoc.channel);
+
+ /* Set channel number */
+ if (!DW_CHAN_5G (priv->adhoc.channel)) {
+ bp[0] = DW_ELEM_DSPARAM;
+ bp[1] = 1;
+ bp[2] = priv->adhoc.channel;
+ bp += bp[1] + 2;
+ }
+
+ /* Set IBSS ATIM window */
+ bp[0] = DW_ELEM_IBSSPARAM;
+ bp[1] = 2;
+ SET16 (&bp[2], atimWindow);
+ bp += bp[1] + 2;
+
+ /* Set ERP info. */
+ //if (!DW_CHAN_5G (priv->adhoc.channel) && !(OPT_BONLY)))
+ {
+ bp[0] = DW_ELEM_ERPINFO;
+ bp[1] = 1;
+ bp[2] = 0;
+ bp += bp[1] + 2;
+ }
+
+ /* Set extended supported rates */
+ bp = dw_beacon_set_rates(bp, DW_ELEM_EXTSUPRATES, priv->adhoc.channel);
+ priv->beacon_body_length = ((u8 *)bp - (u8 *)bcnFrame->body);
+ dw_beacon_set_plcp(&bcnFrame->hwHdr, IEEE80211_CCK_RATE_1MB , priv->beacon_body_length + sizeof(dw_beacon_frame) - BEACON_BODY_SIZE + FCS_SIZE);
+ bcnFrame->hwHdr.plcp.pskcck.raw32 = cpu_to_be32(0x0a046802);
+ priv->beacon_ready = 1;
+}
+
+static void dw_beacon_start_IBSS(struct dw_priv *priv)
+{
+ int atim = 0;
+
+ dw_beacon_make_beacon(priv, DW_BEACON_INT);
+ /* If starting IBSS, set beacon and ATIM intervals */
+ dw_iowrite32(atim | (DW_BEACON_INT << 16), HW_CFP_ATIM);
+ dw_cw_set(priv, IEEE80211_STYPE_BEACON);
+ /* Write beacon frame to beacon buffer */
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_BEACEN);
+ dw_hw_write_fifo((void *)&priv->beacon_frame, priv->beacon_body_length + sizeof(dw_beacon_frame) - BEACON_BODY_SIZE);
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_BEACEN);
+ /* Set interrupt mask to enable TBTT and ATIM interrupts */
+ dw_iosetbits32(HW_INTR_MASK, INTR_TBTT|INTR_ATIM);
+ /* Enable IBSS mode */
+ dw_iosetbits32(HW_MAC_CONTROL, CTRL_IBSS|CTRL_BEACONTX);
+}
+
+/**
+ * dw_beacon_associate - does whatever is necessary for association
+ */
+static void dw_beacon_associate(struct ieee80211softmac_device *mac,
+ struct ieee80211_assoc_response *resp,
+ struct ieee80211softmac_network *net)
+{
+ u16 cap = 0;
+ u8 erp_value = net->erp_value;
+
+ if (resp != NULL)
+ cap = le16_to_cpu(resp->capability);
+ mac->associnfo.associating = 0;
+ mac->bssinfo.supported_rates = net->supported_rates;
+ ieee80211softmac_recalc_txrates(mac);
+
+ mac->associnfo.associated = 1;
+
+ if (resp != NULL)
+ mac->associnfo.short_preamble_available =
+ (cap & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0;
+ ieee80211softmac_process_erp(mac, erp_value);
+
+ if (mac->set_bssid_filter)
+ mac->set_bssid_filter(mac->dev, net->bssid);
+ memcpy(mac->ieee->bssid, net->bssid, ETH_ALEN);
+ netif_carrier_on(mac->dev);
+
+ if (resp != NULL)
+ mac->association_id = le16_to_cpup(&resp->aid);
+}
+
+static void dw_beacon_start(struct work_struct *work)
+{
+ dw_priv_t *priv = container_of((struct delayed_work *)work, struct dw_priv, beacon_work);
+ unsigned long flags;
+ int channel;
+ struct ieee80211softmac_network *net;
+ struct ieee80211softmac_device *mac = priv->softmac;
+
+ if (IW_MODE_ADHOC != priv->ieee->iw_mode
+ || mac->associnfo.associating
+ || mac->scanning
+ || priv->beacon_ready
+ ) return;
+
+ if (!mac->associnfo.req_essid.len
+ || *mac->associnfo.req_essid.data == '\0'
+ ) return;
+
+ channel = priv->adhoc.channel;
+ if (!channel) channel = priv->channel;
+ if (!channel) channel = DW_IBSS_DEFAULT_CHANNEL;
+
+ memset(&priv->adhoc, 0, sizeof(priv->adhoc));
+ get_random_bytes(priv->adhoc.bssid, ETH_ALEN); // Select a random BSSID
+ priv->adhoc.bssid[0] &= ~DW_MAC_GROUP; // clear group bit
+ priv->adhoc.bssid[0] |= DW_MAC_LOCAL; // set local bit
+ priv->adhoc.channel = channel;
+ priv->adhoc.mode=IW_MODE_ADHOC;
+ priv->adhoc.essid.len = mac->associnfo.req_essid.len;
+ memcpy(priv->adhoc.essid.data, mac->associnfo.req_essid.data, IW_ESSID_MAX_SIZE + 1);
+
+ net = ieee80211softmac_get_network_by_essid_locked(mac, &mac->associnfo.associate_essid);
+ if (!net) {
+ net = &priv->adhoc;
+
+ mac->set_channel(mac->dev, net->channel);
+ if (mac->set_bssid_filter)
+ mac->set_bssid_filter(mac->dev, net->bssid);
+
+ spin_lock_irqsave(&mac->lock, flags);
+ dw_beacon_associate(mac, NULL, net);
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATED, net);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ mac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
+ mac->associnfo.bssvalid = 1;
+ mac->associnfo.channel = net->channel;
+ memcpy(mac->associnfo.bssid, net->bssid, ETH_ALEN);
+ mac->associnfo.associate_essid.len = net->essid.len;
+ memcpy(mac->associnfo.associate_essid.data, net->essid.data, IW_ESSID_MAX_SIZE + 1);
+ } else {
+ memcpy(priv->adhoc.bssid, net->bssid, ETH_ALEN);
+ priv->adhoc.essid.len = net->essid.len;
+ memcpy(priv->adhoc.essid.data, net->essid.data, IW_ESSID_MAX_SIZE + 1);
+ }
+
+ dw_beacon_start_IBSS(priv);
+}
+
+/**
+ * dw_int - interrupt handler for WiFi FPGA
+ */
+static irqreturn_t dw_int(int irq, void *dev_id)
+{
+ dw_priv_t* priv = dev_id;
+ u32 status;
+
+ DBG_FN(DBG_INT);
+
+ /* acknowledge interrupt */
+ spin_lock(&priv->lock);
+ status = dw_ioread32(HW_INTR_STATUS);
+ dw_iowrite32(status, HW_INTR_STATUS);
+
+ if (status & INTR_RXFIFO) {
+ int frame_received = 0;
+ /* process all frames */
+ while (!priv->rx.pause &&
+ (!(dw_hw_get_status(priv) & STAT_RXFE))) {
+ frame_received = 1;
+ dw_rx_frame_fetch(priv);
+ }
+
+ if (frame_received)
+ /* the FIFO is prone to run full, do it with high
+ priority */
+ tasklet_hi_schedule(&priv->rx.tasklet);
+
+ DBG_EXEC(status &= ~INTR_RXFIFO);
+ } /* if (status & INTR_RXFIFO) */
+
+ if (status & INTR_TIMEOUT) {
+ priv->tx.timeout = 1;
+ DBG_EXEC(status &= ~INTR_TIMEOUT);
+ }
+
+ if (unlikely(status & INTR_ABORT)) {
+ priv->tx.timeout = 1;
+ /* retransmit it */
+ DBG_EXEC(status &= ~INTR_ABORT);
+ }
+
+ if (status & INTR_TXEND) {
+ tasklet_schedule(&priv->tx.tasklet);
+ DBG_EXEC(status &= ~INTR_TXEND);
+ }
+
+ if (status & (INTR_TBTT | INTR_ATIM)) {
+ /* is emitted by hardware even if masked out :-(. */
+
+ /** Beacon frame is already in beacon buffer.
+ * Only set backoff timer here. */
+ dw_cw_set(priv, IEEE80211_STYPE_BEACON);
+ DBG_EXEC(status &= ~(INTR_TBTT | INTR_ATIM));
+ }
+
+ if (unlikely(status & INTR_RXOVERRUN)) {
+ DBG(DBG_RX, "Receiver overrun");
+ spin_lock(&priv->ieee->lock);
+ priv->ieee->stats.rx_over_errors++;
+ spin_unlock(&priv->ieee->lock);
+ DBG_EXEC(status &= ~INTR_RXOVERRUN);
+ }
+
+#ifdef CONFIG_DIGI_WI_G_DEBUG
+ /* when not developing/debugging, we don't run extra tests. */
+ if (unlikely(status))
+ /* check that we had everything handled*/
+ ERRORL("Unhandled interrupt 0x%08x, mask is 0x%08x", status,
+ dw_ioread32(HW_INTR_MASK));
+#endif /* CONFIG_DIGI_WI_G_DEBUG */
+
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int dw_ieee80211_handle_beacon (
+ struct net_device * dev,
+ struct ieee80211_beacon * beacon,
+ struct ieee80211_network * network)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ if ( !priv->softmac->associnfo.associated &&
+ !priv->softmac->associnfo.associating &&
+ IW_MODE_INFRA == priv->ieee->iw_mode &&
+ priv->jiffies_last_beacon + RESCAN_TIMEOUT < jiffies &&
+ (strncmp(priv->softmac->associnfo.req_essid.data,
+ beacon->info_element->data, beacon->info_element->len) == 0) &&
+ priv->reconnection_attempts &&
+ priv->reconnection_attempts < MAX_RECONNECTION_ATTEMPTS ) {
+ dw_lock(priv, flags);
+ dw_set_channel(priv, network->channel);
+ ieee80211softmac_try_reassoc(priv->softmac);
+ priv->jiffies_last_beacon = jiffies;
+ priv->reconnection_attempts++;
+ dw_unlock(priv, flags);
+ }
+
+ if ( priv->softmac->associnfo.associated &&
+ IW_MODE_INFRA == priv->ieee->iw_mode &&
+ memcmp(network->bssid, beacon->header.addr3, ETH_ALEN) == 0 ) {
+ priv->jiffies_last_beacon = jiffies;
+ priv->reconnection_attempts = 0;
+ }
+ return ieee80211softmac_handle_beacon(dev, beacon, network);
+}
+
+/* Allocate a management frame */
+static u8 * ieee80211softmac_alloc_mgt(u32 size)
+{
+ u8 * data;
+
+ /* Add the header and FCS to the size */
+ size = size + IEEE80211_3ADDR_LEN;
+ if (size > IEEE80211_DATA_LEN)
+ return NULL;
+ /* Allocate the frame */
+ data = kzalloc(size, GFP_ATOMIC);
+ return data;
+}
+
+static void
+ieee80211softmac_hdr_2addr(struct ieee80211softmac_device *mac,
+ struct ieee80211_hdr_2addr *header, u32 type, u8 *dest)
+{
+ /* Fill in the frame control flags */
+ header->frame_ctl = cpu_to_le16(type);
+ /* Control packets always have WEP turned off */
+ if (type > IEEE80211_STYPE_CFENDACK && type < IEEE80211_STYPE_PSPOLL)
+ header->frame_ctl |= mac->ieee->sec.level ? cpu_to_le16(IEEE80211_FCTL_PROTECTED) : 0;
+
+ /* Fill in the duration */
+ header->duration_id = 0;
+ /* FIXME: How do I find this?
+ * calculate. But most drivers just fill in 0 (except if it's a station id of course) */
+
+ /* Fill in the Destination Address */
+ if (dest == NULL)
+ memset(header->addr1, 0xFF, ETH_ALEN);
+ else
+ memcpy(header->addr1, dest, ETH_ALEN);
+ /* Fill in the Source Address */
+ memcpy(header->addr2, mac->ieee->dev->dev_addr, ETH_ALEN);
+}
+
+static void
+ieee80211softmac_hdr_3addr(struct ieee80211softmac_device *mac,
+ struct ieee80211_hdr_3addr *header, u32 type, u8 *dest, u8 *bssid)
+{
+ /* This is common with 2addr, so use that instead */
+ ieee80211softmac_hdr_2addr(mac, (struct ieee80211_hdr_2addr *)header, type, dest);
+
+ /* Fill in the BSS ID */
+ if (bssid == NULL)
+ memset(header->addr3, 0xFF, ETH_ALEN);
+ else
+ memcpy(header->addr3, bssid, ETH_ALEN);
+
+ /* Fill in the sequence # */
+ /* FIXME: I need to add this to the softmac struct
+ * shouldn't the sequence number be in ieee80211? */
+}
+
+static int dw_ieee80211_handle_probe_request (
+ struct net_device * dev,
+ struct ieee80211_probe_request * req,
+ struct ieee80211_rx_stats * stats)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+
+ if (priv->softmac->associnfo.associate_essid.len == req->info_element->len &&
+ memcmp(priv->softmac->associnfo.associate_essid.data, req->info_element->data, priv->softmac->associnfo.associate_essid.len) == 0) {
+ struct ieee80211_probe_response *pkt = NULL;
+ struct ieee80211softmac_device *mac = priv->softmac;
+ struct ieee80211softmac_network *net = &priv->adhoc;
+ u8 *data;
+ u32 pkt_size = 0;
+ int encrypt_mpdu = 0;
+
+ pkt = (struct ieee80211_probe_response *)ieee80211softmac_alloc_mgt(priv->beacon_body_length);
+
+ if (unlikely(pkt == NULL)) {
+ printk("Error, packet is nonexistant or 0 length\n");
+ return -ENOMEM;
+ }
+
+ ieee80211softmac_hdr_3addr(mac, &(pkt->header), IEEE80211_STYPE_PROBE_RESP, net->bssid, priv->adhoc.bssid);
+ data = (u8 *)pkt->info_element;
+ memcpy(pkt->info_element, priv->beacon_frame.body, priv->beacon_body_length);
+ data += priv->beacon_body_length;
+ pkt_size = (data - (u8 *)pkt);
+ ieee80211_tx_frame(priv->ieee, (struct ieee80211_hdr *)pkt, IEEE80211_3ADDR_LEN, pkt_size, encrypt_mpdu);
+ kfree(pkt);
+ }
+
+ return 0;
+}
+
+/**
+ * dw_ieee80211_hard_start_xmit - prepares and sends a frame or put it in tx
+ * queue for transmission when current frames are completed.
+ */
+static int dw_ieee80211_hard_start_xmit(
+ struct ieee80211_txb* txb,
+ struct net_device* dev,
+ int priority)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ dw_frame_tx_t* frame = NULL;
+ int err = -ENOMEM;
+ unsigned long flags;
+ unsigned long ieeeflags;
+ int i;
+ dw_frame_tx_info_t info;
+
+ DBG_FN(DBG_TX);
+
+ /* sanity checks of txb */
+ BUG_ON(!txb->nr_frags); /* nothing to do */
+ if (txb->nr_frags >= TX_MAX_FRAGS) {
+ /* we are lazy and use a fixed-size array */
+ ERROR("Too many fragments, dropping it: %i", txb->nr_frags);
+ goto error;
+ }
+
+ /* Check if the device queue is big enough for every fragment. If not,
+ * drop the whole packet. */
+ for (i = 0; i < txb->nr_frags; i++) {
+ if (unlikely(txb->fragments[ i ]->len >
+ (HW_TX_FIFO_SIZE + sizeof(dw_hw_hdr_tx_t)))) {
+ ERROR("PIO Tx Device queue too small, dropping it");
+
+ goto error;
+ }
+ }
+
+ if (netif_queue_stopped(dev)) {
+ /* apperently, softmac doesn't honor netif_queue_stopped,
+ at least for control messages. Block it ourself. */
+ err = -EBUSY;
+ goto error;
+ }
+
+ /* take the next free queue entry and provide it's data */
+ dw_lock(priv, flags);
+
+ /* prepare everything. */
+ if (!dw_tx_frame_prepare(priv, &info, txb))
+ goto error_unlock;
+
+ frame = list_entry(priv->tx.free.next, dw_frame_tx_t, list);
+ frame->s = info;
+
+ /* put it to tx queue*/
+ if (list_empty(&priv->tx.queued) && !priv->tx.pending) {
+ /* no transmission is running yet, so the queue won't be
+ processed by int handler. Write it directly to FIFO
+ start with first fragment. */
+ priv->tx.retries = 0;
+ priv->tx.times_sent = 0;
+ dw_tx_fragment_send(priv, frame);
+ }
+ /* int handler is locked, so we can manage queue list after sending */
+ list_move_tail(priv->tx.free.next, &priv->tx.queued);
+
+ if (list_empty(&priv->tx.free)) {
+ /* Driver starts always with free entries.
+ But now they are all in use. Wait until we have some free
+ entries */
+ netif_stop_queue(dev);
+ }
+
+ dw_unlock(priv, flags);
+
+ return 0;
+
+error_unlock:
+ dw_unlock(priv, flags);
+
+error:
+ /* txb is free'd on !0 return */
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ priv->ieee->stats.tx_dropped++;
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ return err;
+}
+
+/**
+ * dw_update_status_led - updates the LED depending on association info
+ */
+static void dw_update_status_led(dw_priv_t* priv)
+{
+ static int reduce_rate = 0;
+ reduce_rate++; /* slow it down to 500ms, don't divide */
+
+ if (reduce_rate == 9) {
+ static int count = 0;
+ reduce_rate = 0;
+
+ count++;
+ spin_lock(&priv->ieee->lock);
+ spin_lock(&priv->softmac->lock);
+ if (priv->softmac->associnfo.associated) {
+ if (IW_MODE_ADHOC == priv->ieee->iw_mode)
+ /* slow blink, 7/8 on */
+ dw_set_led_on(PIN_LED, count & 7);
+ else
+ dw_set_led_on(PIN_LED, 1); /* solid on */
+ } else
+ dw_set_led_on(PIN_LED, count & 1); /* faster blink */
+ spin_unlock(&priv->softmac->lock);
+ spin_unlock(&priv->ieee->lock);
+ }
+}
+
+/**
+ * dw_rate_reset - resets rate statistics
+ */
+static void dw_rate_reset(dw_priv_t* priv)
+{
+ int i;
+
+ DBG_FN(DBG_INTERFACE | DBG_UPDATE_RATE);
+ REQUIRE_LOCKED(priv);
+
+ CLEAR(priv->rate);
+
+ priv->rate.success_threshold = THRESH_MIN;
+
+ /* find index in ap_ri that matches default_rate */
+ for (i = 0; i < priv->ap_ri.count; i++) {
+ if (priv->softmac->txrates.default_rate == BASIC_RATE_MASK(priv->ap_ri.rates[ i ])) {
+ priv->rate.index = i;
+ break;
+ }
+ }
+}
+
+/**
+ * dw_rate_update - selects a better rate depending on frame error count
+ *
+ * taken from wifi_mac:mac_rate.c:UpdateRate
+ */
+static void dw_rate_update(dw_priv_t* priv)
+{
+ int changed = 0;
+
+ DBG_FN(DBG_UPDATE_RATE);
+ REQUIRE_LOCKED(priv);
+
+ if (atomic_read(&priv->fix_rate) || !rate_is_enough(priv))
+ /* user requested fix rate or not enough data*/
+ return;
+
+ spin_lock(&priv->softmac->lock);
+ if (!priv->softmac->associnfo.associated)
+ goto out;
+
+ if (rate_is_success(priv)) {
+ /* try to increase rate */
+ priv->rate.success++;
+ if ((priv->rate.success >= priv->rate.success_threshold) &&
+ (priv->rate.index < (priv->ap_ri.count - 1))) {
+ /* we are successfull long enough */
+ priv->rate.recovery = 1;
+ priv->rate.success = 0;
+ priv->rate.index++;
+ changed = 1;
+ } else
+ priv->rate.recovery = 0;
+ } else if (rate_is_failure(priv)) {
+ /* decrease rate */
+ if (priv->rate.index > 0) {
+ if (priv->rate.recovery) {
+ /* errors resulted from a rate increase.
+ double successfull intervals needed to
+ incrase next time */
+ priv->rate.success_threshold *= 2;
+ if (priv->rate.success_threshold > THRESH_MAX)
+ priv->rate.success_threshold = THRESH_MAX;
+ } else
+ priv->rate.success_threshold = THRESH_MIN;
+
+ priv->rate.index--;
+ changed = 1;
+ }
+
+ priv->rate.success = 0;
+ priv->rate.recovery = 0;
+ }
+
+ if (changed) {
+ priv->softmac->txrates.default_rate = BASIC_RATE_MASK(priv->ap_ri.rates[ priv->rate.index ]);
+ DBG(DBG_UPDATE_RATE, "Rate changed to %i",
+ priv->softmac->txrates.default_rate);
+ }
+
+ /* reset counter so we work on the next time slice */
+ priv->rate.tx_data_any = priv->rate.tx_data_ack = 0;
+
+out:
+ spin_unlock(&priv->softmac->lock);
+}
+
+/**
+ * dw_management_timer - performs all periodic background non tx/rx work
+ */
+static void dw_management_timer(unsigned long a)
+{
+ dw_priv_t* priv = (dw_priv_t*) a;
+ unsigned long flags;
+
+ DBG_FN(DBG_UPDATE_RATE);
+
+ dw_lock(priv, flags);
+
+ switch (priv->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ if (!priv->softmac->scanning && !priv->beacon_ready) {
+ schedule_delayed_work(&priv->beacon_work, 0);
+ }
+ break;
+ case IW_MODE_INFRA:
+ if (priv->softmac->associnfo.associated && priv->jiffies_last_beacon + BEACON_TIMEOUT < jiffies)
+ {
+ priv->softmac->associnfo.associated = 0;
+ ieee80211softmac_start_scan(priv->softmac);
+ priv->jiffies_last_beacon = jiffies;
+ ieee80211softmac_try_reassoc(priv->softmac);
+ priv->reconnection_attempts = 1;
+ } else if (
+ !priv->softmac->associnfo.associated
+ && !priv->softmac->associnfo.associating
+ && priv->reconnection_attempts
+ && priv->softmac->associnfo.req_essid.len
+ && priv->softmac->associnfo.req_essid.data
+ && *priv->softmac->associnfo.req_essid.data
+ && priv->jiffies_last_beacon + RESCAN_TIMEOUT < jiffies)
+ {
+ if (priv->reconnection_attempts < MAX_RECONNECTION_ATTEMPTS) {
+ priv->softmac->associnfo.associated = 0;
+ ieee80211softmac_start_scan(priv->softmac);
+ priv->jiffies_last_beacon = jiffies;
+ ieee80211softmac_try_reassoc(priv->softmac);
+ priv->reconnection_attempts++;
+ } else {
+ ieee80211softmac_disassoc(priv->softmac);
+ memset(priv->softmac->associnfo.bssid, 0, ETH_ALEN);
+ priv->reconnection_attempts = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ dw_update_status_led(priv);
+
+ if (priv->rate.have_activity) {
+ priv->rate.have_activity = 0;
+ /* blink when data arrives */
+ dw_set_led_on(PIN_ACTIVITY_LED,
+ priv->activity.counter & 1);
+
+ priv->activity.counter++;
+ } else
+ /* turn it off */
+ dw_set_led_on(PIN_ACTIVITY_LED, 0);
+
+ priv->rate.counter++;
+ if (!(priv->rate.counter % MANAGEMENT_TICKS_FOR_UPDATE))
+ /* we don't need to update rate each LED timer event */
+ dw_rate_update(priv);
+
+ if (priv->tx.pending && time_after(jiffies, priv->dev->trans_start + TX_TIMEOUT)) {
+ /* we can't use tx_timeout. It only works when
+ * netif_carrier_ok, but that is set only when associated.
+ * But we could loose association and run into a tx
+ * hanger. Therefore we;d never get associated, and never
+ * reset.So do it homemade. */
+ dw_tx_reset(priv);
+ }
+
+ mod_timer(&priv->management_timer, jiffies + MANAGEMENT_JIFFIES);
+ dw_unlock(priv, flags);
+}
+
+/**
+ * dw_hw_init_card - initializes card and memory settings
+ */
+static int __init dw_hw_init_card(struct net_device* dev)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ u32 firmware_id;
+ u32 ctrl;
+ u32 mac_status;
+
+ DBG_FN(DBG_INIT);
+
+ dw_lock(priv, flags);
+
+ /* Configure PIO pins */
+ gpio_direction_output(PIN_LED, 0);
+ gpio_direction_output(PIN_ACTIVITY_LED, 0);
+ dw_set_led_on(PIN_LED, 0);
+ dw_set_led_on(PIN_ACTIVITY_LED, 0);
+ gpio_configure_ns9360(PIN_INTR, 0, 1, 0);
+
+ /* CS2 is used for wireless baseband registers.
+ Map to MAC_BASE address, 32 bit width, 8 wait states.*/
+ iowrite32(0x82, MEM_DMCONF(2));
+ iowrite32(0, MEM_SMWED(2));
+ iowrite32(2, MEM_SMOED(2));
+ iowrite32(8, MEM_SMRD(2));
+ iowrite32(0, MEM_SMPMRD(2));
+ iowrite32(4, MEM_SMWD(2));
+ iowrite32(2, MEM_SWT(2));
+
+ iowrite32(MAC_BASE_PHYS, SYS_SMCSSMB(2));
+ iowrite32(MAC_MASK, SYS_SMCSSMM(2));
+
+ /* Init baseband processor and MAC assist */
+
+ ctrl = GEN_INIT | GEN_ANTDIV;
+
+ /* we can't do a softreset here */
+
+ /* go back to normal state */
+
+ dw_iowrite32(ctrl, HW_GEN_CONTROL);
+ dw_iowrite32(0, HW_MAC_CONTROL);
+ dw_iowrite32(0, HW_INTR_MASK);
+ /* Mike's recommendation for antenna diversity and antenna map value.
+ Transmit only on primary antenna, receive on diversity */
+ dw_iowrite32(0x88000000, HW_RSSI_AES);
+
+ /* get MAC from fpga */
+ dw_hw_memcpy_from(dev->dev_addr, HW_STAID0, ETH_ALEN);
+
+ /* Initialize RF tranceiver */
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+ dw_hw_write_rf(7, 0x27ffe); /* TEST register */
+ dw_hw_write_rf(6, 0xf81ac); /* Filter Register */
+ dw_set_tx_power(priv, DW_TX_POWER_DEFAULT); /* Transmitter Gain */
+ dw_hw_write_rf(4, 0x0002b); /* Receiver Gain */
+
+ dw_hw_set_vco(0); /* no channel selected yet */
+
+ dw_hw_write_rf(0, 0x25f9c); /* Mode Control - Calibrate Filter */
+ udelay(10);
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+ dw_hw_set_vco(0); /* no channel selected yet */
+ dw_hw_write_rf(4, 0x0007b); /* Receiver Gain */
+ dw_set_tx_power(priv, DW_TX_POWER_DEFAULT); /* Transmitter Gain */
+ dw_hw_write_rf(6, 0xf81ac); /* Filter Register */
+ dw_hw_write_rf(7, 0x3fffe); /* TEST register */
+
+ dw_hw_write_rf(0, 0x27fdc); /* Mode Control - Calibrate Filter */
+ udelay(10);
+ dw_hw_write_rf(0, 0x27fd4); /* Mode Control - RX/TX mode */
+#endif
+
+ /* Firmware version */
+ firmware_id = dw_ioread32(HW_VERSION);
+ mac_status = dw_ioread32(HW_MAC_STATUS);
+ printk(KERN_DEBUG DRIVER_NAME ": FPGA HW version: %i.%02i FW Version: %i.%02i\n",
+ (firmware_id >> 8) & 0xff, firmware_id & 0xff,
+ (mac_status >> 24) & 0xff, (mac_status >> 16) & 0xff);
+
+ dw_unlock(priv, flags);
+
+ return 1;
+}
+
+/**
+ * dw_reset_rx_dups - resets the duplicate list and forgets the senders
+ */
+static void dw_reset_rx_dups(dw_priv_t* priv)
+{
+ unsigned long flags;
+ int i;
+
+ REQUIRE_UNLOCKED(priv);
+
+ dw_lock(priv, flags);
+
+ CLEAR(priv->rx.dups);
+
+ /* for detecting duplicates */
+ INIT_LIST_HEAD(&priv->rx.dups.known.list);
+ INIT_LIST_HEAD(&priv->rx.dups.free.list);
+ for (i = 0; i < ARRAY_SIZE(priv->rx.dups.entries); i++) {
+ struct list_head* entry = &priv->rx.dups.entries[ i ].list;
+ INIT_LIST_HEAD(entry);
+ list_add_tail(entry, &priv->rx.dups.free.list);
+ } /* for (i = 0) */
+
+ dw_unlock(priv, flags);
+}
+
+/**
+ * dw_setup_rx_queue - creates the list structure
+ *
+ * @return: -ENOMEM on no failures otherwise 0
+ */
+static int __init dw_setup_rx_queue(dw_priv_t* priv)
+{
+ dw_frame_rx_t* tmp = NULL;
+ size_t aligned_size;
+ size_t buffer_size;
+ int i;
+
+ INIT_LIST_HEAD(&priv->rx.queue.free.list);
+ INIT_LIST_HEAD(&priv->rx.queue.filled.list);
+
+ aligned_size = ((char*) &tmp[ 1 ]) - ((char*) &tmp[ 0 ]);
+ buffer_size = aligned_size * RX_QUEUE_SIZE;
+ priv->rx.queue.buffer =
+ (dw_frame_rx_t*) vmalloc(buffer_size);
+ if (NULL == priv->rx.queue.buffer)
+ goto error;
+
+ memset(priv->rx.queue.buffer, 0, buffer_size);
+
+ /* create frame skb and list for moving unswapped frames */
+ for (i = 0; i < RX_QUEUE_SIZE; i++) {
+ dw_frame_rx_t* frame = &priv->rx.queue.buffer[ i ];
+ struct list_head* entry = &frame->list;
+
+ frame->skb = dev_alloc_skb(DW_MTU);
+ if (NULL == frame->skb) {
+ for (; i > 0; i--) {
+ dev_kfree_skb(frame->skb);
+ frame->skb = NULL;
+ }
+
+ goto error_skb;
+ }
+
+ INIT_LIST_HEAD(entry);
+ list_add(entry, &priv->rx.queue.free.list);
+ }
+
+ return 0;
+
+error_skb:
+ vfree(priv->rx.queue.buffer);
+ priv->rx.queue.buffer = NULL;
+error:
+ ERROR("No Memory for Rx Queue");
+ return -ENOMEM;
+}
+
+/**
+ * dw_start_dev - starts resource
+ */
+static int dw_start_dev(struct platform_device* pdev)
+{
+ struct net_device* dev = to_dev(pdev);
+ struct dw_priv* priv = ieee80211softmac_priv(dev);
+ struct list_head* cursor;
+ int err = -ENODEV;
+ int i;
+
+ DBG_FN(DBG_INIT);
+
+ CLEAR(*priv);
+
+ spin_lock_init(&priv->lock);
+ priv->dev = dev;
+ priv->short_retry_limit = DW_DEFAULT_SHORT_RETRY_LIMIT;
+ priv->long_retry_limit = DW_DEFAULT_LONG_RETRY_LIMIT;
+ snprintf(priv->nick, IW_ESSID_MAX_SIZE, "Digi Wireless b/g");
+
+ atomic_set(&priv->fix_rate, 0);
+
+ /* the stack may later reduce the number of supported rates if the
+ * access point can't provide them */
+ ASSERT(ARRAY_SIZE(dw_rates) <= ARRAY_SIZE(priv->ap_ri.rates));
+ for (i = 0; i < ARRAY_SIZE(dw_rates); i++)
+ priv->ap_ri.rates[ i ] = dw_rates[ i ];
+ priv->ap_ri.count = i;
+
+ /* rx */
+ err = dw_setup_rx_queue(priv);
+ if (err < 0)
+ goto error_rx_queue;
+
+ tasklet_init(&priv->rx.tasklet, dw_rx_tasklet_handler,
+ (unsigned long) priv);
+ tasklet_disable(&priv->rx.tasklet); /* will be enabled in open */
+
+ /* tx */
+ atomic_set(&priv->tx.seq_nr, 0);
+ /* first down should sleep */
+ init_MUTEX_LOCKED(&priv->tx.pause_sem);
+ INIT_LIST_HEAD(&priv->tx.queued);
+ INIT_LIST_HEAD(&priv->tx.free);
+ for (i = 0; i < ARRAY_SIZE(priv->tx.frames); i++) {
+ struct list_head* entry = &priv->tx.frames[ i ].list;
+ INIT_LIST_HEAD(entry);
+ list_add_tail(entry, &priv->tx.free);
+ }
+
+ tasklet_init(&priv->tx.tasklet, dw_tx_tasklet_handler,
+ (unsigned long) priv);
+ tasklet_disable(&priv->tx.tasklet);
+
+ init_timer(&priv->management_timer);
+ priv->management_timer.function = dw_management_timer;
+ priv->management_timer.data = (unsigned long) priv;
+
+ if (!dw_hw_init_card(dev))
+ goto error_init;
+
+ /* card is setup, now we can register the irq */
+ err = request_irq(dev->irq, dw_int, 0, dev->name,
+ priv);
+ if (err) {
+ ERROR("register interrupt %d failed, err %d", dev->irq, err);
+ goto error_irq;
+ }
+ disable_irq(dev->irq);
+
+ priv->softmac = ieee80211_priv(dev);
+ priv->softmac->set_channel = dw_softmac_set_chan;
+ priv->softmac->set_bssid_filter = dw_softmac_set_bssid_filter;
+ priv->softmac->txrates_change = dw_softmac_txrates_change;
+ priv->ieee = netdev_priv(dev);
+ priv->ieee->modulation = IEEE80211_OFDM_MODULATION | IEEE80211_CCK_MODULATION;
+ priv->ieee->hard_start_xmit = dw_ieee80211_hard_start_xmit;
+ priv->ieee->set_security = dw_ieee80211_set_security;
+ priv->ieee->handle_beacon = dw_ieee80211_handle_beacon;
+ priv->ieee->handle_probe_request = dw_ieee80211_handle_probe_request;
+ priv->ieee->iw_mode = IW_MODE_INFRA;
+ priv->ieee->freq_band = IEEE80211_24GHZ_BAND;
+ priv->ieee->rts = DW_MAX_RTS_THRESHOLD;
+ priv->ieee->perfect_rssi = MAC_RSSI_MAX;
+ priv->ieee->worst_rssi = 0;
+ priv->ieee->config |= CFG_IEEE80211_RTS;
+
+ if (dw_geo_init(priv) < 0)
+ goto error_init2;
+
+ if (register_netdev(dev))
+ goto error_init2;
+
+ ieee80211softmac_notify(dev, IEEE80211SOFTMAC_EVENT_AUTHENTICATED,
+ dw_softmac_notify_authenticated, NULL);
+
+ return 0;
+
+error_init2:
+ free_irq(dev->irq, priv);
+error_irq:
+error_init:
+ list_for_each(cursor, &priv->rx.queue.free.list) {
+ dw_frame_rx_t* frame = list_entry(cursor, dw_frame_rx_t, list);
+ dev_kfree_skb(frame->skb);
+ frame->skb = NULL;
+ }
+ vfree(priv->rx.queue.buffer);
+ priv->rx.queue.buffer = NULL;
+
+error_rx_queue:
+ return err;
+}
+
+/**
+ * dw_stop_dev - stops device
+ */
+static void dw_stop_dev(struct platform_device* pdev)
+{
+ struct net_device* dev = to_dev(pdev);
+ struct dw_priv* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ struct list_head* it;
+
+ DBG_FN(DBG_INIT);
+
+ dw_lock(priv, flags);
+ dw_iowrite32(0, HW_MAC_CONTROL);
+ dw_unlock(priv, flags);
+
+ list_for_each(it, &priv->rx.queue.free.list) {
+ dw_frame_rx_t* frame = list_entry(it, dw_frame_rx_t, list);
+ dev_kfree_skb(frame->skb);
+ frame->skb = NULL;
+ }
+ vfree(priv->rx.queue.buffer);
+
+ unregister_netdev(dev);
+
+ dw_set_led_on(PIN_LED, 0);
+ dw_set_led_on(PIN_ACTIVITY_LED, 0);
+
+ gpio_free(PIN_LED);
+ gpio_free(PIN_ACTIVITY_LED);
+ gpio_free(PIN_INTR);
+}
+
+/**
+ * dw_remove - stops device and removes all resources
+ */
+static int dw_remove(struct platform_device* pdev)
+{
+ struct net_device* dev = to_dev(pdev);
+
+ DBG_FN(DBG_INIT);
+
+ dw_stop_dev(pdev);
+
+ /* unmap resources */
+ if (0 != dev->base_addr) {
+ iounmap(vbase);
+ vbase = NULL;
+ }
+ free_irq(dev->irq, ieee80211softmac_priv(dev));
+
+ free_ieee80211softmac(dev);
+ dev = NULL;
+
+ platform_set_drvdata(pdev, NULL);
+
+ release_resource(pdev->resource);
+
+ return 0;
+}
+
+/**
+ * dw_release_device - dummy function
+ */
+static void dw_release_device(struct device* dev)
+{
+ DBG_FN(DBG_INIT);
+
+ /* nothing to do. But we need a !NULL function */
+}
+
+/**
+ * dw_softmac_txrates_change - adjusts rate to allowed values.
+ *
+ * Select a better on.
+ */
+static void dw_softmac_txrates_change(struct net_device* dev, u32 changes)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ int was_locked = spin_is_locked(&priv->lock);
+ unsigned long flags = 0;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ if (!was_locked)
+ /* we are called either directly from open/user interface,
+ or from our tasklet context when processing a frame.*/
+ dw_lock(priv, flags);
+
+ /* new rate */
+ CLEAR(priv->rate);
+
+ dw_rate_reset(priv);
+
+ if (!was_locked)
+ dw_unlock(priv, flags);
+}
+
+/**
+ * dw_wx_set_encode - changes the encoding
+ *
+ * Used by wireless tools <= 28 for WEP-1. Not AES
+ */
+static int dw_wx_set_encode(struct net_device* dev,
+ struct iw_request_info* info, union iwreq_data* data,
+ char* extra)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+
+ priv->ieee->host_encrypt = 1;
+ priv->ieee->host_encrypt_msdu = 1;
+ priv->ieee->host_decrypt = 1;
+ priv->ieee->host_mc_decrypt = 1;
+
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ return ieee80211_wx_set_encode(priv->ieee, info, data, extra);
+}
+
+/**
+ * dw_wx_set_encodeext - changes the encoding
+ *
+ * Only used by wpa_supplicant or wireless_tool >= 29
+ */
+static int dw_wx_set_encodeext(struct net_device* dev,
+ struct iw_request_info* info, union iwreq_data* data,
+ char* extra)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ struct iw_encode_ext* ext = (struct iw_encode_ext*) extra;
+ unsigned long ieeeflags;
+ int host = (dw_sw_aes || (IW_ENCODE_ALG_CCMP != ext->alg));
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+
+ priv->ieee->host_encrypt = host;
+ priv->ieee->host_encrypt_msdu = host;
+ priv->ieee->host_decrypt = host;
+ priv->ieee->host_mc_decrypt = host;
+
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ /* wx_set_encodeext will initialize itself depending on host_encrypt.
+ * After wx_set_encodeext, it will be of no further use. */
+ return ieee80211_wx_set_encodeext(priv->ieee, info, data, extra);
+}
+
+/**
+ * dw_softmac_notify_authenticated - called when authenticated
+ *
+ * Resets statistics
+ */
+static void dw_softmac_notify_authenticated(struct net_device* dev,
+ int event_type, void* context)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE);
+
+ dw_lock(priv, flags);
+ dw_rate_reset(priv);
+ dw_unlock(priv, flags);
+}
+
+/***********************************************************************
+ * @Function: dw_ieee80211_get_network_by_bssid
+ * @Return:
+ * @Descr: Get a network from the list by BSSID with locking
+ ***********************************************************************/
+struct ieee80211_network *
+dw_ieee80211_get_network_by_bssid(struct dw_priv *priv, u8 *bssid)
+{
+ unsigned long ieeeflags;
+ struct ieee80211_network *ieee_net = NULL, *tmp_net;
+ struct list_head *list_ptr;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ list_for_each(list_ptr, &priv->ieee->network_list) {
+ tmp_net = list_entry(list_ptr, struct ieee80211_network, list);
+ if (!memcmp(tmp_net->bssid, bssid, ETH_ALEN)) {
+ if (ieee_net==NULL) {
+ ieee_net = tmp_net;
+ } else {
+ if (tmp_net->last_scanned > ieee_net->last_scanned)
+ ieee_net = tmp_net;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ return ieee_net;
+}
+
+/* CCMP encryption data */
+static const u32 te4[256] = {
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+
+static const u32 rcon[] = {
+ /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+ 0x01000000, 0x02000000,
+ 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000,
+ 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000,
+};
+
+/***********************************************************************
+ * @Function: dw_aes_set_encrypt_key
+ * @Return:
+ * @Descr: Expands the cipher key into the encryption key schedule
+ ***********************************************************************/
+static int dw_aes_set_encrypt_key(const unsigned char * user_key,
+ const int bits, struct aes_key_st * key)
+{
+ u32 *rk;
+ int i = 0;
+ u32 temp;
+
+ DBG_FN(DBG_SECURITY);
+
+ if (!user_key || !key)
+ return -1;
+ if (bits != 128 && bits != 192 && bits != 256)
+ return -2;
+
+ rk = (u32*)key->rd_key;
+
+ rk[0] = GETU32(user_key );
+ rk[1] = GETU32(user_key + 4);
+ rk[2] = GETU32(user_key + 8);
+ rk[3] = GETU32(user_key + 12);
+ if (bits == 128) {
+ while (1) {
+ temp = rk[3];
+ rk[4] = rk[0] ^
+ (te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ if (++i == 10) {
+ return 0;
+ }
+ rk += 4;
+ }
+ }
+ rk[4] = GETU32(user_key + 16);
+ rk[5] = GETU32(user_key + 20);
+ if (bits == 192) {
+ while (1) {
+ temp = rk[ 5];
+ rk[ 6] = rk[ 0] ^
+ (te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 7] = rk[ 1] ^ rk[ 6];
+ rk[ 8] = rk[ 2] ^ rk[ 7];
+ rk[ 9] = rk[ 3] ^ rk[ 8];
+ if (++i == 8) {
+ return 0;
+ }
+ rk[10] = rk[ 4] ^ rk[ 9];
+ rk[11] = rk[ 5] ^ rk[10];
+ rk += 6;
+ }
+ }
+ rk[6] = GETU32(user_key + 24);
+ rk[7] = GETU32(user_key + 28);
+ if (bits == 256) {
+ while (1) {
+ temp = rk[ 7];
+ rk[ 8] = rk[ 0] ^
+ (te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 9] = rk[ 1] ^ rk[ 8];
+ rk[10] = rk[ 2] ^ rk[ 9];
+ rk[11] = rk[ 3] ^ rk[10];
+ if (++i == 7) {
+ return 0;
+ }
+ temp = rk[11];
+ rk[12] = rk[ 4] ^
+ (te4[(temp >> 24) ] & 0xff000000) ^
+ (te4[(temp >> 16) & 0xff] & 0x00ff0000) ^
+ (te4[(temp >> 8) & 0xff] & 0x0000ff00) ^
+ (te4[(temp ) & 0xff] & 0x000000ff);
+ rk[13] = rk[ 5] ^ rk[12];
+ rk[14] = rk[ 6] ^ rk[13];
+ rk[15] = rk[ 7] ^ rk[14];
+
+ rk += 8;
+ }
+ }
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_ccmp_set_key
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static u8 dw_ccmp_set_key(struct dw_priv *priv, int id, u8 *data, int len, u8 *seq)
+{
+ ccmp_key_t *key;
+
+ DBG_FN(DBG_SECURITY);
+
+ key = &priv->aeskeys[id];
+
+ if (len == CCMP_KEY_SIZE)
+ {
+ CLEAR(*key);
+ key->valid = 1;
+ /* Set sequence counter if given */
+ if (seq)
+ key->rx_pn = dw_to_48(GET32(seq+2), GET16(seq));
+
+ /* Store only AES key schedule for this key */
+ dw_aes_set_encrypt_key(data, AES_BITS, &key->rk);
+ }
+ else if (len == 0) {
+ key->valid = 0;
+ }
+ else
+ return 0;
+
+ return 1;
+}
+
+
+/***********************************************************************
+ * @Function: dw_send_key
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_send_key(int index, unsigned long *key)
+{
+ DBG_FN(DBG_SECURITY);
+
+ if (!dw_hw_aes_wait())
+ goto error;
+
+ /* Set key load mode */
+ dw_iowrite32(0x14 | index, HW_AES_MODE);
+
+ /* Write key to hw */
+ dw_hw_aes_write_fifo_noswap(key, 4*44);
+
+ return 0;
+
+error:
+ return -1;
+}
+
+
+/***********************************************************************
+ * @Function: dw_send_tx_key
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_send_tx_key(struct dw_priv *priv, int index)
+{
+ ccmp_key_t *key;
+
+ DBG_FN(DBG_SECURITY);
+
+ if (!(priv->ieee->sec.flags & (1 << index)))
+ return 0;
+
+ /* expand key */
+ if (0 == dw_ccmp_set_key(priv, index, priv->ieee->sec.keys[index], priv->ieee->sec.key_sizes[index], 0)) {
+ printk(KERN_ERR "Error while expanding tx Key!!!\n");
+ return -1;
+ }
+ key = &priv->aeskeys[index];
+
+ return dw_send_key(index, key->rk.rd_key);
+}
+
+
+/***********************************************************************
+ * @Function: dw_send_keys
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_send_keys(struct dw_priv *priv)
+{
+ ccmp_key_t *key;
+ int i, len, err = 0;
+
+ DBG_FN(DBG_SECURITY);
+ REQUIRE_LOCKED(priv);
+
+ /* Note: AES keys cannot be set for multiple times.
+ * Only set it at the first time. */
+ for (i = 0; i < 4; i++) {
+ key = &priv->aeskeys[i];
+ len = priv->ieee->sec.key_sizes[i];
+ if (!(priv->ieee->sec.flags & (1 << i))) {
+ continue;
+ }
+
+ /* expand key */
+ if (0 == dw_ccmp_set_key(priv, i, priv->ieee->sec.keys[i], len, 0)) {
+ printk(KERN_ERR "Error while expanding Key!!!\n");
+ continue;
+ }
+
+ if (dw_send_key(i, key->rk.rd_key)<0)
+ err = -1;
+ }
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_set_hwcrypto_keys
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static void dw_set_hwcrypto_keys(struct dw_priv *priv)
+{
+ int err = 0;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ switch (priv->ieee->sec.level) {
+ case SEC_LEVEL_3:
+ if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
+ if (dw_send_tx_key(priv, priv->ieee->sec.active_key) < 0)
+ err = -1;
+
+ if (!dw_sw_aes)
+ if (dw_send_keys(priv) < 0)
+ err = -1;
+ break;
+ default:
+ break;
+ }
+ if (err < 0)
+ printk(KERN_ERR "Error while sending keys to hardware!!!\n");
+}
+
+
+/***********************************************************************
+ * @Function: dw_ieee80211_set_security
+ * @Return:
+ * @Descr: set_security() callback in struct ieee80211_device
+ ***********************************************************************/
+static void dw_ieee80211_set_security(struct net_device *dev,
+ struct ieee80211_security *sec)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int i, flag;
+ unsigned long flags;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ for (i = 0; i < 4; i++) {
+ flag = 1 << i;
+ if (sec->flags & (flag)) {
+ priv->ieee->sec.encode_alg[i] = sec->encode_alg[i];
+ priv->ieee->sec.key_sizes[i] = sec->key_sizes[i];
+ if (sec->key_sizes[i] == 0)
+ priv->ieee->sec.flags &= ~(flag);
+ else {
+ memcpy(priv->ieee->sec.keys[i], sec->keys[i],
+ sec->key_sizes[i]);
+ priv->ieee->sec.flags |= (flag);
+ }
+ } else if (sec->level != SEC_LEVEL_1)
+ priv->ieee->sec.flags &= ~(flag);
+ }
+
+ if (sec->flags & SEC_ACTIVE_KEY) {
+ if (sec->active_key <= 3) {
+ priv->ieee->sec.active_key = sec->active_key;
+ priv->ieee->sec.flags |= SEC_ACTIVE_KEY;
+ } else
+ priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+ } else
+ priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY;
+
+ if ((sec->flags & SEC_AUTH_MODE) &&
+ (priv->ieee->sec.auth_mode != sec->auth_mode)) {
+ priv->ieee->sec.auth_mode = sec->auth_mode;
+ priv->ieee->sec.flags |= SEC_AUTH_MODE;
+ }
+
+ if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) {
+ priv->ieee->sec.flags |= SEC_ENABLED;
+ priv->ieee->sec.enabled = sec->enabled;
+ }
+
+ if (sec->flags & SEC_ENCRYPT)
+ priv->ieee->sec.encrypt = sec->encrypt;
+
+ if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) {
+ priv->ieee->sec.level = sec->level;
+ priv->ieee->sec.flags |= SEC_LEVEL;
+ }
+
+ if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
+ dw_set_hwcrypto_keys(priv);
+
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ dw_unlock(priv, flags);
+}
+
+
+/***********************************************************************
+ * @Function: dw_softmac_set_bssid_filter
+ * @Return:
+ * @Descr: Set BSS mode and IDs (irq)
+ ***********************************************************************/
+static void dw_softmac_set_bssid_filter(struct net_device *dev, const u8 *bssid)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ struct ieee80211softmac_network *net;
+ int i;
+ int was_locked = spin_is_locked(&priv->lock);
+ unsigned long flags = 0;
+ int last_basic = 0;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ if (!was_locked)
+ dw_lock(priv, flags);
+
+ priv->beacon_ready = 0;
+
+ /* Set BSSID in hardware */
+ dw_hw_memcpy_to(HW_BSSID0, bssid, ETH_ALEN);
+
+ if (priv->softmac->associnfo.associated) {
+ /* Set SSID in hardware */
+ dw_iowrite32(priv->softmac->associnfo.associate_essid.len |
+ 0x000e0000, HW_SSID_LEN);
+ /*TODO set rates*/
+
+ /* get ratesinfo from assocnetwork */
+ net = ieee80211softmac_get_network_by_essid_locked(priv->softmac, &priv->softmac->associnfo.associate_essid);
+
+ if (net) {
+ /* provide only the subset of the rates we support */
+ priv->ap_ri.count = 0;
+
+ for (i=0; i < net->supported_rates.count; i++) {
+ int j = 0;
+ for (j = 0; j < ARRAY_SIZE(dw_rates); j++) {
+ if ((net->supported_rates.rates[ i ] & 0x7F) == (dw_rates[ j ] & 0x7F)) {
+ priv->ap_ri.rates[ priv->ap_ri.count++ ] = net->supported_rates.rates[ i ];
+
+ break;
+ }
+ }
+ }
+ }
+
+
+ dw_hw_memcpy_to(HW_SSID, priv->softmac->associnfo.associate_essid.data, priv->softmac->associnfo.associate_essid.len);
+ /* Disable IBSS mode */
+ //dw_iocleanbits32(HW_MAC_CONTROL, CTRL_IBSS | CTRL_BEACONTX);
+ } else {
+ /* reset it */
+ for (i = 0; i < ARRAY_SIZE(dw_rates); i++)
+ priv->ap_ri.rates[ i ] = dw_rates[ i ];
+ priv->ap_ri.count = i;
+ }
+
+ /* determine equal or lower basic rate for the rate being used.
+ The acknowledge of AP is send with
+ api_ri.rates[ basics[ data_rate_index ] ]*/
+ for (i = 0; i < ARRAY_SIZE(priv->tx.basics); i++) {
+ if ((priv->ap_ri.rates[ i ] & IEEE80211_BASIC_RATE_MASK) == IEEE80211_BASIC_RATE_MASK)
+ last_basic = i;
+ priv->tx.basics[ i ] = last_basic;
+ }
+
+ priv->softmac->txrates.default_rate = BASIC_RATE_MASK(priv->ap_ri.rates[ priv->rate.index ]);
+ DBG(DBG_UPDATE_RATE, "Rate set to %i", priv->softmac->txrates.default_rate);
+ priv->rate.tx_data_any = priv->rate.tx_data_ack = 0; // reset counter so we work on the next time slice
+
+ if (!was_locked)
+ dw_unlock(priv, flags);
+
+}
+
+/***********************************************************************
+ * @Function: dw_set_channel
+ * @Return:
+ * @Descr: Select a channel
+ ***********************************************************************/
+static void dw_set_channel(dw_priv_t* priv, u8 channel)
+{
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+ REQUIRE_LOCKED(priv);
+
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_RXEN);
+ priv->beacon_ready = 0;
+
+ /* Set frequency divider for channel */
+ dw_hw_write_rf (1, freq_table[channel].integer);
+ dw_hw_write_rf (2, freq_table[channel].fraction);
+
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+ /* Filter calibration */
+ dw_hw_write_rf (0, 0x25f9c);
+ udelay (10);
+
+ /* VCO calibration */
+ dw_hw_write_rf (0, 0x25f9a);
+ udelay (80);
+
+ /* Mode Control - RX/TX mode */
+ dw_hw_write_rf (0, 0x25f94);
+
+ /* Allow the trannsceiver to settle in the new mode of operation */
+ udelay (10);
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+ /* VCO Calibration */
+ dw_hw_write_rf (0, 0x27fda);
+ udelay(40);
+
+ /* Set frequency divider for channel again */
+ dw_hw_write_rf (1, freq_table[channel].integer);
+ dw_hw_write_rf (2, freq_table[channel].fraction);
+
+ /* Mode Control - RX/TX mode */
+ dw_hw_write_rf (0, 0x27fd4);
+
+ dw_hw_set_vco(channel);
+#endif
+ priv->channel = channel;
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_RXEN);
+}
+
+/***********************************************************************
+ * @Function: dw_softmac_set_chan
+ * @Return:
+ * @Descr: Select a channel)
+ ***********************************************************************/
+static void dw_softmac_set_chan(struct net_device* dev, u8 channel)
+{
+ dw_priv_t* priv = ieee80211softmac_priv(dev);
+ //struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ //if (IW_MODE_ADHOC == priv->ieee->iw_mode && !mac->scanning)
+ // printk("Selected channel = %d\n", channel);
+
+ /* we really don't want to change the channel while there is something
+ * in the queue for a different channel */
+ dw_tx_wait_for_idle_and_pause(priv);
+
+ dw_lock(priv, flags);
+ dw_set_channel( priv, channel);
+ dw_unlock(priv, flags);
+
+ dw_tx_continue_queue(priv);
+}
+
+
+/***********************************************************************
+ * @Function: dw_geo_init
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_geo_init(dw_priv_t *priv)
+{
+ struct ieee80211_geo *geo;
+ struct ieee80211_channel *chan;
+ int i;
+ u8 channel;
+ const char *iso_country;
+
+ DBG_FN(DBG_INIT);
+
+ geo = kzalloc(sizeof(*geo), GFP_KERNEL);
+ if (!geo)
+ return -ENOMEM;
+
+ iso_country = "XX";
+
+ for (i = 0, channel = IEEE80211_52GHZ_MIN_CHANNEL;
+ channel <= IEEE80211_52GHZ_MAX_CHANNEL; channel++) {
+ chan = &geo->a[i++];
+ chan->freq = dw_channel_to_freq_a(channel);
+ chan->channel = channel;
+ }
+ geo->a_channels = i;
+ for (i = 0, channel = IEEE80211_24GHZ_MIN_CHANNEL;
+ channel <= IEEE80211_24GHZ_MAX_CHANNEL; channel++) {
+ chan = &geo->bg[i++];
+ chan->freq = dw_channel_to_freq_bg(channel);
+ chan->channel = channel;
+ }
+ geo->bg_channels = i;
+ memcpy(geo->name, iso_country, 2);
+ if (0 /*TODO: Outdoor use only */)
+ geo->name[2] = 'O';
+ else if (0 /*TODO: Indoor use only */)
+ geo->name[2] = 'I';
+ else
+ geo->name[2] = ' ';
+ geo->name[3] = '\0';
+
+ ieee80211_set_geo(priv->ieee, geo);
+ kfree(geo);
+
+ return 1;
+}
+
+
+/***********************************************************************
+ * @Function: dw_get_name
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_get_name(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ strcpy(data->name, "IEEE 802.11b/g");
+ return 0;
+}
+
+/***********************************************************************
+ * @Function: dw_wx_get_encode
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_encode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int err;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ err = ieee80211_wx_get_encode(priv->ieee, info, data, extra);
+
+ return err;
+}
+
+/***********************************************************************
+ * @Function: dw_wx_get_encodeext
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_encodeext(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int err;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ err = ieee80211_wx_get_encodeext(priv->ieee, info, data, extra);
+
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_channelfreq
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_channelfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ u8 channel;
+ int err = -EINVAL;
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_tx_wait_for_idle_and_pause(priv);
+
+ dw_lock(priv, flags);
+
+ if ((data->freq.m >= 0) && (data->freq.m <= 1000)) {
+ channel = data->freq.m;
+ } else {
+ channel = ieee80211_freq_to_channel(priv->ieee, data->freq.m);
+ }
+ if (ieee80211_is_valid_channel(priv->ieee, channel)) {
+ dw_set_channel(priv, channel);
+ err = 0;
+ }
+
+ dw_unlock(priv, flags);
+
+ dw_tx_continue_queue(priv);
+
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_channelfreq
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_channelfreq(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ data->freq.e = 1;
+ data->freq.m = ieee80211_channel_to_freq(priv->ieee, priv->channel) * 100000;
+ data->freq.flags = IW_FREQ_FIXED;
+ dw_unlock(priv, flags);
+ return 0;
+}
+
+/***********************************************************************
+ * @Function: dw_wx_set_mode
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_mode(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+
+ priv->ieee->iw_mode = data->mode;
+
+ if (IW_MODE_ADHOC == priv->ieee->iw_mode) {
+ if (priv->channel)
+ priv->adhoc.channel = priv->channel;
+ } else {
+ dw_iocleanbits32(HW_MAC_CONTROL, CTRL_IBSS|CTRL_BEACONTX); /* Disable IBSS mode */
+ }
+
+ priv->beacon_ready = 0;
+ priv->beacon_body_length=0;
+
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_mode
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_mode(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ data->mode = priv->ieee->iw_mode;
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_rangeparams
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_rangeparams(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ struct iw_range *range = (struct iw_range *)extra;
+ const struct ieee80211_geo *geo;
+ int i, j, level;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ data->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /*TODO: What about 802.11b? Have to meassure!!! */
+ /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */
+ range->throughput = 27 * 1000 * 1000;
+ range->min_nwid = 0x0000;
+ range->max_nwid = 0x0020;
+
+ range->max_qual.qual = 100;
+ range->max_qual.level = MAC_RSSI_MAX;
+ range->max_qual.noise = 0;
+ range->max_qual.updated =
+ IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED;
+
+ range->avg_qual.qual = 0;
+ range->avg_qual.level = 0;
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = IW_QUAL_NOISE_INVALID;
+
+ range->min_rts = DW_MIN_RTS_THRESHOLD;
+ range->max_rts = DW_MAX_RTS_THRESHOLD;
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13;
+ range->num_encoding_sizes = 2;
+ range->max_encoding_tokens = WEP_KEYS;
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 19;
+
+ range->enc_capa = IW_ENC_CAPA_WPA |
+ IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP |
+ IW_ENC_CAPA_CIPHER_CCMP;
+
+ for (i = 0; i < ARRAY_SIZE(dw_rates); i++)
+ range->bitrate[ i ] = RATE_IN_BS(BASIC_RATE_MASK(dw_rates[ i ]));
+ range->num_bitrates = i;
+
+ geo = ieee80211_get_geo(priv->ieee);
+ range->num_channels = geo->bg_channels;
+ j = 0;
+ for (i = 0; i < geo->bg_channels; i++) {
+ if (j == IW_MAX_FREQUENCIES)
+ break;
+ range->freq[j].i = j + 1;
+ range->freq[j].m = geo->bg[i].freq * 100000;
+ range->freq[j].e = 1;
+ j++;
+ }
+ range->num_frequency = j;
+
+ /* retry limit capabilities */
+ range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->r_time_flags = IW_RETRY_LIFETIME;
+
+ /* I don't know the range.*/
+ range->min_retry = 1;
+ range->max_retry = 65535;
+ range->min_r_time = 1024;
+ range->max_r_time = 65535 * 1024;
+
+ /* txpower is supported in dBm's */
+// if (priv->ieee->iw_mode == IW_MODE_ADHOC) {
+ range->txpower_capa = IW_TXPOW_DBM;
+ range->num_txpower = IW_MAX_TXPOWER;
+ for (i = 0, level = (DW_TX_POWER_MAX_DBM * 16);
+ i < IW_MAX_TXPOWER;
+ i++, level -=
+ ((DW_TX_POWER_MAX_DBM -
+ DW_TX_POWER_MIN_DBM) * 16) / (IW_MAX_TXPOWER - 1))
+ range->txpower[i] = level / 16;
+// } else {
+// range->txpower_capa = 0;
+// range->num_txpower = 0;
+// }
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_get_wireless_stats
+ * @Return:
+ * @Descr: Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS
+ ***********************************************************************/
+static struct iw_statistics *dw_get_wireless_stats(struct net_device *dev)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct iw_statistics *wstats;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ wstats = &priv->wstats;
+ if (!mac->associnfo.associated) {
+ wstats->miss.beacon = 0;
+ wstats->discard.retries = 0;
+ wstats->discard.nwid = 0;
+ wstats->discard.code = 0;
+ wstats->discard.fragment = 0;
+ wstats->discard.misc = 0;
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ wstats->qual.noise = 0;
+ wstats->qual.updated = IW_QUAL_NOISE_INVALID |
+ IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+ } else {
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ /* fill in the real statistics when iface associated */
+ wstats->qual.qual =
+ (100 *
+ (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) *
+ (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) -
+ (priv->ieee->perfect_rssi - priv->ieee->networks->stats.rssi) *
+ (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) +
+ 62 * (priv->ieee->perfect_rssi -
+ priv->ieee->networks->stats.rssi))) /
+ ((priv->ieee->perfect_rssi -
+ priv->ieee->worst_rssi) * (priv->ieee->perfect_rssi -
+ priv->ieee->worst_rssi));
+ /*
+ * !TODO. Seems they are not calculated. Take
+ * one from the last frame */
+ wstats->qual.level = priv->ieee->networks->stats.signal;
+ wstats->qual.noise = priv->ieee->networks->stats.noise;
+ wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED;
+ wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable;
+ wstats->discard.retries = priv->ieee->ieee_stats.tx_retry_limit_exceeded;
+ wstats->discard.nwid = priv->ieee->ieee_stats.tx_discards_wrong_sa;
+ wstats->discard.fragment = priv->ieee->ieee_stats.rx_fragments;
+ wstats->discard.misc = priv->wstats.discard.misc;
+ wstats->miss.beacon = 0; /* FIXME */
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ }
+ return wstats;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_nick
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ size_t len;
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE);
+ memcpy(priv->nick, extra, len);
+ priv->nick[len] = '\0';
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_nick
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_nick(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ size_t len;
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ len = strlen(priv->nick) + 1;
+ memcpy(extra, priv->nick, len);
+ data->data.length = (__u16)len;
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_rts
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_rts(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int err = -EINVAL;
+ unsigned long flags;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ if (data->rts.disabled) {
+ priv->ieee->rts = DW_MAX_RTS_THRESHOLD;
+ err = 0;
+ } else {
+ if (data->rts.value >= DW_MIN_RTS_THRESHOLD &&
+ data->rts.value <= DW_MAX_RTS_THRESHOLD) {
+ priv->ieee->rts = data->rts.value;
+ err = 0;
+ }
+ }
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ dw_unlock(priv, flags);
+
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_rts
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_rts(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ data->rts.value = priv->ieee->rts;
+ data->rts.fixed = 0;
+ data->rts.disabled = (priv->ieee->rts == DW_MAX_RTS_THRESHOLD);
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_rate
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ s32 in_rate = data->bitrate.value;
+ int err = 0;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+
+ if (in_rate == -1)
+ /* auto */
+ atomic_set(&priv->fix_rate, 0);
+ else
+ atomic_set(&priv->fix_rate, 1);
+
+ err = ieee80211softmac_wx_set_rate(dev, info, data, extra);
+
+ dw_unlock(priv, flags);
+
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_frag
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int err = -EINVAL;
+ unsigned long flags;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ if (data->frag.disabled) {
+ priv->ieee->fts = MAX_FRAG_THRESHOLD;
+ err = 0;
+ } else {
+ if (data->frag.value >= MIN_FRAG_THRESHOLD &&
+ data->frag.value <= MAX_FRAG_THRESHOLD) {
+ priv->ieee->fts = data->frag.value & ~0x1;
+ err = 0;
+ }
+ }
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ dw_unlock(priv, flags);
+
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_frag
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ unsigned long ieeeflags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ spin_lock_irqsave(&priv->ieee->lock, ieeeflags);
+ data->frag.value = priv->ieee->fts;
+ data->frag.fixed = 0;
+ data->frag.disabled = (priv->ieee->fts == MAX_FRAG_THRESHOLD);
+ spin_unlock_irqrestore(&priv->ieee->lock, ieeeflags);
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+/***********************************************************************
+ * @Function: dw_wx_set_xmitpower
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_xmitpower(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ int err = 0, value;
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) return -EINVAL;
+ if (data->txpower.fixed == 0) return -EINVAL;
+ if (data->txpower.value < DW_TX_POWER_MIN_DBM || data->txpower.value > DW_TX_POWER_MAX_DBM) return -EINVAL;
+
+ value = data->txpower.value;
+ dw_lock(priv, flags);
+ err = dw_set_tx_power(priv, value);
+ dw_unlock(priv, flags);
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_xmitpower
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_xmitpower(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *data,
+ char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ data->txpower.fixed = 1;
+ data->txpower.value = priv->tx_power;
+ data->txpower.flags = IW_TXPOW_DBM;
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_set_retry
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_set_retry(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled)
+ return -EINVAL;
+
+ if (!(wrqu->retry.flags & IW_RETRY_LIMIT))
+ return 0;
+
+ if (wrqu->retry.value < 0 || wrqu->retry.value > 255)
+ return -EINVAL;
+
+ dw_lock(priv, flags);
+ if (wrqu->retry.flags & IW_RETRY_MIN)
+ priv->short_retry_limit = (u8) wrqu->retry.value;
+ else if (wrqu->retry.flags & IW_RETRY_MAX)
+ priv->long_retry_limit = (u8) wrqu->retry.value;
+ else {
+ priv->short_retry_limit = (u8) wrqu->retry.value;
+ priv->long_retry_limit = (u8) wrqu->retry.value;
+ }
+
+ dw_unlock(priv, flags);
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: dw_wx_get_retry
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_wx_get_retry(struct net_device *dev, struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ wrqu->retry.disabled = 0;
+
+ if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
+ dw_unlock(priv, flags);
+ return -EINVAL;
+ }
+
+ if (wrqu->retry.flags & IW_RETRY_MAX) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ wrqu->retry.value = priv->long_retry_limit;
+ } else if (wrqu->retry.flags & IW_RETRY_MIN) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+ wrqu->retry.value = priv->short_retry_limit;
+ } else {
+ wrqu->retry.flags = IW_RETRY_LIMIT;
+ wrqu->retry.value = priv->short_retry_limit;
+ }
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+#ifdef WX
+# undef WX
+#endif
+#define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT]
+static const iw_handler dw_wx_handlers[] = {
+ /* Wireless Identification */
+ WX(SIOCGIWNAME) = dw_get_name,
+ /* Basic operations */
+ WX(SIOCSIWFREQ) = dw_wx_set_channelfreq,
+ WX(SIOCGIWFREQ) = dw_wx_get_channelfreq,
+ WX(SIOCSIWMODE) = dw_wx_set_mode,
+ WX(SIOCGIWMODE) = dw_wx_get_mode,
+ /* Informative stuff */
+ WX(SIOCGIWRANGE) = dw_wx_get_rangeparams,
+ /* Spy support (statistics per MAC address - used for Mobile IP support) */
+ WX(SIOCSIWSPY) = iw_handler_set_spy,
+ WX(SIOCGIWSPY) = iw_handler_get_spy,
+ WX(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
+ WX(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
+ /* Access Point manipulation */
+ WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap,
+ WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap,
+ WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan,
+ WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results,
+ /* 802.11 specific support */
+ WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid,
+ WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid,
+ WX(SIOCSIWNICKN) = dw_wx_set_nick,
+ WX(SIOCGIWNICKN) = dw_wx_get_nick,
+ /* Other parameters */
+ WX(SIOCSIWRATE) = dw_wx_set_rate,
+ WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate,
+ WX(SIOCSIWRTS) = dw_wx_set_rts,
+ WX(SIOCGIWRTS) = dw_wx_get_rts,
+ WX(SIOCSIWFRAG) = dw_wx_set_frag,
+ WX(SIOCGIWFRAG) = dw_wx_get_frag,
+ WX(SIOCSIWTXPOW) = dw_wx_set_xmitpower,
+ WX(SIOCGIWTXPOW) = dw_wx_get_xmitpower,
+ WX(SIOCSIWRETRY) = dw_wx_set_retry,
+ WX(SIOCGIWRETRY) = dw_wx_get_retry,
+ /* Encoding */
+ WX(SIOCSIWENCODE) = dw_wx_set_encode,
+ WX(SIOCGIWENCODE) = dw_wx_get_encode,
+ WX(SIOCSIWENCODEEXT) = dw_wx_set_encodeext,
+ WX(SIOCGIWENCODEEXT) = dw_wx_get_encodeext,
+ /* Power saving */
+/*TODO WX(SIOCSIWPOWER) = dw_wx_set_power, */
+/*TODO WX(SIOCGIWPOWER) = dw_wx_get_power, */
+ WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie,
+ WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie,
+ WX(SIOCSIWMLME) = ieee80211softmac_wx_set_mlme,
+ WX(SIOCSIWAUTH) = ieee80211_wx_set_auth,
+ WX(SIOCGIWAUTH) = ieee80211_wx_get_auth,
+};
+#undef WX
+
+#ifdef NEED_PRIVATE_HANDLER
+# include "dw_priv_handler.c"
+#else
+static const iw_handler dw_priv_wx_handlers[] = {};
+static const struct iw_priv_args dw_priv_wx_args[] = {};
+#endif
+
+const struct iw_handler_def dw_wx_handlers_def = {
+ .standard = dw_wx_handlers,
+ .num_standard = ARRAY_SIZE(dw_wx_handlers),
+ .num_private = ARRAY_SIZE(dw_priv_wx_handlers),
+ .num_private_args = ARRAY_SIZE(dw_priv_wx_args),
+ .private = dw_priv_wx_handlers,
+ .private_args = dw_priv_wx_args,
+ .get_wireless_stats = dw_get_wireless_stats,
+};
+
+/***********************************************************************
+ * @Function: dw_close
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int dw_close(struct net_device* dev)
+{
+ struct dw_priv* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ struct list_head* it;
+
+ DBG_FN(DBG_INIT);
+
+ ieee80211softmac_stop(dev);
+
+ /* don't disable anything before softmac_stop. Maybe SoftMAC will run a
+ * disassoc cycle somewhat later. */
+ del_timer_sync(&priv->management_timer);
+
+ cancel_delayed_work(&priv->beacon_work);
+
+ disable_irq(dev->irq);
+
+ tasklet_disable(&priv->rx.tasklet);
+ /* from this point, rx queue.filled is empty, and it is not paused */
+ tasklet_disable(&priv->tx.tasklet);
+
+ dw_lock(priv, flags);
+
+ priv->tx.data_pending_ack = NULL;
+
+ /* delete unsend frames */
+ list_for_each(it, &priv->tx.queued) {
+ dw_frame_tx_t* frame = list_entry(it, dw_frame_tx_t, list);
+ ieee80211_txb_free(frame->s.txb);
+ frame->s.txb = NULL;
+ }
+ dw_list_move(&priv->tx.free, &priv->tx.queued);
+
+ dw_iocleanbits32(HW_GEN_CONTROL, GEN_RXEN);
+ dw_iowrite32(0, HW_INTR_MASK);
+ dw_iowrite32(dw_ioread32(HW_INTR_STATUS), HW_INTR_STATUS);
+
+ dw_set_led_on(PIN_LED, 0);
+ dw_set_led_on(PIN_ACTIVITY_LED, 0);
+
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+/***********************************************************************
+ * @Function: dw_open
+ * @Return:
+ * @Descr: init/reinit
+ ***********************************************************************/
+static int dw_open(struct net_device* dev)
+{
+ struct dw_priv* priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INIT);
+
+ dw_reset_rx_dups(priv);
+
+ priv->tx.data_pending_ack = NULL;
+
+ priv->jiffies_last_beacon = jiffies;
+ priv->reconnection_attempts = 0;
+
+ tasklet_enable(&priv->rx.tasklet);
+ tasklet_enable(&priv->tx.tasklet);
+
+ ieee80211softmac_start(dev);
+
+ ieee80211softmac_set_rates(dev, ARRAY_SIZE(dw_rates), (u8*) dw_rates); /* cast away const */
+
+ /* softmac sets user_rate to 24MB, but we start much slower to work
+ always */
+ priv->softmac->txrates.default_rate = priv->softmac->txrates.user_rate = IEEE80211_CCK_RATE_1MB;
+
+ dw_lock(priv, flags);
+
+ priv->beacon_ready = 0;
+ INIT_DELAYED_WORK(&priv->beacon_work, dw_beacon_start);
+ priv->beacon_body_length=0;
+ priv->adhoc.channel = DW_IBSS_DEFAULT_CHANNEL;
+
+ /* acknowledge pending interrupts */
+ dw_iowrite32(dw_ioread32(HW_INTR_STATUS), HW_INTR_STATUS);
+ dw_iowrite32(INTR_ALL, HW_INTR_MASK);
+ dw_iowrite32(HW_AES_MODE_1, HW_AES_MODE );
+ mod_timer(&priv->management_timer, jiffies + MANAGEMENT_JIFFIES);
+
+ enable_irq(dev->irq);
+
+ dw_iosetbits32(HW_GEN_CONTROL, GEN_RXEN);
+
+ dw_unlock(priv, flags);
+
+ return 0;
+}
+
+/***********************************************************************
+ * @Function: dw_set_multicast_list
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static void dw_set_multicast_list(struct net_device* dev)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+
+ DBG_FN(DBG_INTERFACE | DBG_MINOR);
+
+ dw_lock(priv, flags);
+ if (dev->flags & IFF_PROMISC) {
+ dw_iosetbits32(HW_MAC_CONTROL, CTRL_PROMISC);
+ } else {
+ dw_iocleanbits32(HW_MAC_CONTROL, CTRL_PROMISC);
+ }
+ dw_unlock(priv, flags);
+}
+
+/***********************************************************************
+ * @Function: dw_net_get_stats
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static struct net_device_stats * dw_net_get_stats(struct net_device *dev)
+{
+ struct dw_priv *priv = ieee80211softmac_priv(dev);
+
+ DBG_FN(DBG_INTERFACE);
+
+ return &priv->ieee->stats;
+}
+
+/**
+ * dw_probe - probe for resources and for chip being present
+ *
+ * @return <0 on failure
+ */
+static int __init dw_probe(struct platform_device* pdev)
+{
+ struct net_device* dev = NULL;
+ int err = -ENODEV;
+
+ DBG_FN(DBG_INIT);
+
+ /* Create the network device object. */
+ dev = alloc_ieee80211softmac(sizeof(dw_priv_t));
+ if (NULL == dev) {
+ ERROR(": Couldn't alloc_ieee80211softmac ");
+ err = -ENOMEM;
+ goto error_alloc;
+ }
+ platform_set_drvdata(pdev, dev);
+ dev->irq = INTR_ID;
+
+ if (dev_alloc_name(dev, "wlan%d") < 0) {
+ ERROR("Couldn't get name");
+ goto error_name;
+ }
+
+ /* allocate resources and map memory */
+ if (gpio_request(PIN_LED, DRIVER_NAME) != 0) {
+ ERROR("GPIO %i already in use", PIN_LED);
+ goto error_pin_led;
+ }
+ if (gpio_request(PIN_ACTIVITY_LED, DRIVER_NAME) != 0) {
+ ERROR("GPIO %i already in use", PIN_ACTIVITY_LED);
+ goto error_pin_activity_led;
+ }
+ if (gpio_request(PIN_INTR, DRIVER_NAME) != 0) {
+ ERROR("GPIO %i already in use", PIN_INTR);
+ goto error_pin_intr;
+ }
+
+ err = request_resource(&iomem_resource, pdev->resource);
+ if (err) {
+ ERROR("Memory already in used: 0x%08x...0x%08x",
+ pdev->resource->start, pdev->resource->end);
+ goto error_res;
+ }
+
+ vbase = ioremap(pdev->resource->start,
+ pdev->resource->end - pdev->resource->start);
+ if (NULL == vbase) {
+ ERROR("ioremap failed");
+ err = -ENOMEM;
+ goto error_remap;
+ }
+ dev->base_addr = (int) vbase;
+
+ /* test if fpga is programmed */
+ if (dw_ioread32(HW_VERSION) == 0xffffffff) {
+ ERROR("FPGA not present");
+ goto error_fpga;
+ }
+
+ /* all resources available, initializw remaining stuff */
+ dev->open = dw_open;
+ dev->stop = dw_close;
+ dev->set_multicast_list = dw_set_multicast_list;
+ dev->wireless_handlers = &dw_wx_handlers_def;
+ dev->get_stats = dw_net_get_stats;
+
+ /* initialize hardware/private stuff */
+ err = dw_start_dev(pdev);
+ if (err)
+ goto error;
+
+ return 0;
+
+error_fpga:
+ iounmap(vbase);
+ vbase = NULL;
+error_remap:
+ release_resource(pdev->resource);
+error_res:
+ gpio_free(PIN_INTR);
+error_pin_intr:
+ gpio_free(PIN_ACTIVITY_LED);
+error_pin_activity_led:
+ gpio_free(PIN_LED);
+error_pin_led:
+error_name:
+ /* the IRQ hasn't been setup, so unwind a part and don't go to
+ * dw_remove */
+ free_ieee80211softmac(dev);
+error_alloc:
+ return err;
+
+error:
+ /* dev memory is setup, so we can use the remove function */
+ dw_remove(pdev);
+
+ return err;
+}
+
+/***********************************************************************
+ * @Function: dw_init_module
+ * @Return: 0 if successfull; -ENODEV if interrupt or device is not available
+ * @Descr: initialize module
+ ***********************************************************************/
+static int __init dw_init_module(void)
+{
+ int err;
+
+ DBG_FN(DBG_INIT);
+
+ printk(KERN_INFO "%s\n", dw_version);
+
+ if (dw_sw_aes)
+ printk(KERN_NOTICE "AES encoding/decoding is done in software\n");
+
+ err = platform_device_register(&dw_device);
+ if (err) {
+ ERROR("Device Register Failed");
+ goto error;
+ }
+
+ err = platform_driver_register(&dw_driver);
+ if (err) {
+ ERROR("Driver Register Failed");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return err;
+}
+
+
+/***********************************************************************
+ * @Function: dw_cleanup_module
+ * @Return: nothing
+ * @Descr: deinitializes module. Actually will never be called due to
+ * MOD_INC_USE_COUNT > 1. Will be fixed if hardware watchdog can be disabled.
+ ***********************************************************************/
+static void __exit dw_cleanup_module(void)
+{
+ DBG_FN(DBG_INIT);
+
+ platform_driver_unregister(&dw_driver);
+ platform_device_unregister(&dw_device);
+}
+
+#ifndef MODULE
+/**
+ * dw_sw_cmdline - parses kernel commandline
+ *
+ * @return always 1
+ */
+static int __init dw_sw_cmdline(char* arg)
+{
+ if ((NULL != arg) && *arg)
+ dw_sw_aes = simple_strtoul(arg, NULL, 10);
+
+ return 1;
+}
+#endif /* MODULE */
+
+module_init(dw_init_module);
+module_exit(dw_cleanup_module);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bernd Westermann");
+MODULE_AUTHOR("Markus Pietrek");
+MODULE_AUTHOR("Miriam Ruiz");
+MODULE_DESCRIPTION("WiFi driver for ConnectCore Wi-9C");
+
+module_param(dw_sw_aes, int, 0x00);
+MODULE_PARM_DESC(dw_sw_aes, "If set, AES encoding/decoding is done in software.");
+
+module_param(dw_cfg_vco, int, 0x00);
+MODULE_PARM_DESC(dw_cfg_vco, "Config Value of VCO (0 use built-in)");
+
+#ifdef CONFIG_DIGI_WI_G_DEBUG
+module_param(dw_dbg_level, int, 0x00);
+MODULE_PARM_DESC(dw_dbg_level, "debug output level");
+#endif /* CONFIG_DIGI_WI_G_DEBUG */
+
+#ifndef MODULE
+__setup("dw_sw_aes=", dw_sw_cmdline);
+#endif /* MODULE */
diff --git a/drivers/net/wireless/digi_wi_g.h b/drivers/net/wireless/digi_wi_g.h
new file mode 100644
index 000000000000..95bc6f06b1a5
--- /dev/null
+++ b/drivers/net/wireless/digi_wi_g.h
@@ -0,0 +1,660 @@
+/*******************************************************************************
+ * digi_wi_g.h
+ *
+ * Support for DIGI Wireless Module.
+ *
+ * $Id: digi_wi_g.h,v 1.64 2008-02-13 11:02:34 mruiz Exp $
+ * @Author: Bernd Westermann
+ * @References: [1] NET+OS code mac_hw_wi9c.c
+ *
+ * Copyright (C) 2007 by FS Forth-Systeme GmbH
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 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
+ *
+ *******************************************************************************
+ * History:
+ * 14/03/06 Initial Version
+ *
+ *******************************************************************************/
+
+#ifndef FS_DIGI_WI_G_DRIVER_H
+#define FS_DIGI_WI_G_DRIVER_H
+
+#include <net/ieee80211.h>
+#include <net/ieee80211softmac.h>
+#include <net/ieee80211softmac_wx.h>
+
+#include <net/iw_handler.h> /* New driver API */
+#include <mach/regs-bbu.h>
+#include <mach/hardware.h> /* BBUS_CLK_FREQ */
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#ifdef dprintkl
+#undef dprintkl
+#endif
+#ifdef dprintk
+#undef dprintk
+#endif
+
+
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 60
+#define DRIVER_NAME "digi_wi_g"
+
+/* for LEDs, update rate etc. */
+/* LED time is 50ms */
+#define MANAGEMENT_TIME_MS 50
+#define MANAGEMENT_JIFFIES ( ( MANAGEMENT_TIME_MS * HZ ) / 1000 )
+/* update rate only every 500ms */
+#define MANAGEMENT_TICKS_FOR_UPDATE ( 500 / MANAGEMENT_TIME_MS )
+#define TX_TIMEOUT (4*HZ/2)
+#define ACK_TIMEOUT (HZ/2)
+#define BEACON_TIMEOUT (HZ)
+#define RESCAN_TIMEOUT (15*HZ)
+
+#define MAX_RECONNECTION_ATTEMPTS 5
+
+/* Min/max successful intervals to increase rate */
+#define THRESH_MIN 1
+#define THRESH_MAX 10
+
+#define TX_QUEUE_SIZE 8 /* not tuned yet */
+#define TX_MAX_FRAGS 16
+#define RX_QUEUE_SIZE 44 /* one ICMP frame */
+
+#define RX_DUP_SENDER_SIZE 16 /* somewhat lower than MAX_NETWORK_COUNT
+ * because in worst case we need to scan
+ * RX_DUP_SENDER_SIZE until we found the
+ * active one */
+
+#define DW_PIO_MAXTXPACKETS 4
+
+#define DW_DEFAULT_SHORT_RETRY_LIMIT 7
+#define DW_DEFAULT_LONG_RETRY_LIMIT 4
+#define AES_BUSY_TIMEOUT 1000000 /* in ns */
+
+#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0)
+
+#ifdef CONFIG_DIGI_WI_G_DEBUG
+# define assert(expr) \
+ do { \
+ if (unlikely(!(expr))) { \
+ printk(KERN_ERR PFX "ASSERTION FAILED (%s) \nat: %s:%d:%s()\n", \
+ #expr, __FILE__, __LINE__, __FUNCTION__); \
+ } \
+ } while (0)
+# define dprintkl printkl
+# define dprintk(f, x...) do { printk(f ,##x); } while (0)
+# define COMPILE_TIME " from ("__TIME__ ") "
+#else
+# define assert(expr) do { /* nothing */ } while (0)
+# define dprintkl(f, x...) do { /* nothing */ } while (0)
+# define dprintk(f, x...) do { /* nothing */ } while (0)
+# define COMPILE_TIME ""
+#endif /* CONFIG_DIGI_WI_G_DEBUG */
+
+#define DIGI_WIRELESS_G_REG_PHYS NS9XXX_CSxSTAT_PHYS(2)
+
+/* Hardware register defines */
+#define MAC_BASE_PHYS DIGI_WIRELESS_G_REG_PHYS
+#define MAC_BASE_SIZE 0xe0000/*100U*/ /* Register segment size */
+#define MAC_MASK 0xffffc001 /* Size mask and enable bit */
+
+/* Baseband control registers */
+#define HW_VERSION (0x00) /* Version */
+#define HW_GEN_CONTROL (0x04) /* General control */
+#define HW_GEN_STATUS (0x08) /* General status */
+#define HW_RSSI_AES (0x0c) /* RSSI and AES status */
+#define HW_INTR_MASK (0x10) /* Interrupt mask */
+#define HW_INTR_STATUS (0x14) /* Interrupt status */
+#define HW_SPI_DATA (0x18) /* RF SPI data register */
+#define HW_SPI_CONTROL (0x1c) /* RF SPI control register */
+#define HW_DATA_FIFO (0x20) /* Data FIFO */
+#define HW_AES_FIFO (0x30) /* AES FIFO */
+#define HW_AES_MODE (0x38) /* AES mode */
+
+/* MAC control registers */
+#define HW_STAID0 (0x40) /* Station ID (6 bytes) */
+#define HW_STAID1 (0x44)
+#define HW_BSSID0 (0x48) /* BSS ID (6 bytes) */
+#define HW_BSSID1 (0x4c)
+#define HW_SSID_LEN (0x50) /* SSID length (8 bits) */
+#define HW_BACKOFF (0x54) /* Backoff period (16 bits) */
+#define HW_LISTEN (0x58) /* Listen interval (16 bits), CFP (8 bits), DTIM (8 bits) */
+#define HW_CFP_ATIM (0x5c) /* CFP max duration/ATIM period (16 bits), beacon interval (16 bits) */
+#define HW_MAC_STATUS (0x60) /* MAC status (8 bits) */
+#define HW_MAC_CONTROL (0x64) /* MAC control (8 bits) */
+#define HW_REMAIN_BO (0x68) /* Remaining backoff (16 bits) */
+#define HW_BEACON_BO (0x6c) /* Beacon backoff (16 bits), beacon mask (8 bits) */
+#define HW_SSID (0x80) /* Service set ID (32 bytes) */
+
+/* FIFO sizes in bytes */
+#define HW_TX_FIFO_SIZE 1792
+#define HW_RX_FIFO_SIZE 2048
+
+
+/* General control register bits */
+#define GEN_RXEN 0x00000001 /* Receive enable */
+#define GEN_ANTDIV 0x00000002 /* Antenna diversity */
+#define GEN_ANTSEL 0x00000004 /* Antenna select */
+#define GEN_5GEN 0x00000008 /* 5 GHz band enable */
+#define GEN_SHPRE 0x00000010 /* Transmit short preamble */
+#define GEN_RXFIFORST 0x00000020 /* Receive FIFO reset */
+#define GEN_TXFIFORST 0x00000040 /* Transmit FIFO reset */
+#define GEN_TXHOLD 0x00000080 /* Transmit FIFO hold */
+#define GEN_BEACEN 0x00000100 /* Beacon enable */
+#define GEN_BST 0x00001000 /* Boot status */
+#define GEN_CLKEN 0x00002000 /* Clock enable */
+#define GEN_TXFIFOEMPTY 0x00004000 /* Transmit FIFO empty */
+#define GEN_TXFIFOFULL 0x00008000 /* Transmit FIFO full */
+
+#if defined(CONFIG_DIGI_WI_G_UBEC_JD)
+# define GEN_INIT 0x377a0000 /* Initial state */
+#elif defined(CONFIG_DIGI_WI_G_UBEC_HC)
+# define GEN_INIT 0x37700000 /* Initial state */
+#else
+# error "You need to choose an UBEC Transceiver Revision"
+#endif
+
+/* General status register bits */
+#define STAT_RXFE 0x00000010 /* Receive FIFO empty */
+#define SFT_RESET 0x04000000 /* WiFi Baseband soft reset */
+/* AES status register bits */
+#define AES_EMPTY 0x00010000 /* AES receive FIFO empty */
+#define AES_FULL 0x00020000 /* AES transmit FIFO full */
+#define AES_BUSY 0x00040000 /* AES engine busy */
+#define AES_MIC 0x00080000 /* AES MIC correct */
+
+/* Interrupt mask and status register bits */
+#define INTR_RXFIFO 0x00000001 /* Receive FIFO not empty */
+#define INTR_TXEND 0x00000002 /* Transmit complete */
+#define INTR_TIMEOUT 0x00000004 /* CTS/ACK receive timeout */
+#define INTR_ABORT 0x00000008 /* CTS transmit abort */
+#define INTR_TBTT 0x00000010 /* Beacon transmission time */
+#define INTR_ATIM 0x00000020 /* ATIM interval end */
+#define INTR_RXOVERRUN 0x00000040 /* Receive FIFO overrun */
+#define INTR_ALL ( INTR_RXFIFO | \
+ INTR_TIMEOUT | \
+ INTR_RXOVERRUN | \
+ INTR_TXEND )
+
+/* MAC control register bits */
+#define CTRL_TXREQ 0x00000001 /* Transmit request */
+#define CTRL_AUTOTXDIS 0x00000002 /* Auto-transmit disable */
+#define CTRL_BEACONTX 0x00000004 /* Beacon transmit enable */
+#define CTRL_PROMISC 0x00000008 /* Promiscuous mode */
+#define CTRL_IBSS 0x00000010 /* IBBS mode */
+
+#define HW_AES_MODE_0 0x00000000
+#define HW_AES_MODE_1 0x00000010
+
+/* BOOTMUX bit */
+#define BOOTMUX_LOW 0x00000001 /* Bootmux,bit 0, must go low after load */
+
+/* PIO pins */
+#define PIN_INIT 58 /* FPGA program init */
+#define PIN_INTR 65 /* Interrupt (same as done) */
+#define PIN_LED 67 /* Link status LED */
+#define PIN_ACTIVITY_LED 66 /* Link activity LED */
+#define PIN_BOOTMUX 66 /* Enables serial ports */
+
+#define INTR_ID IRQ_NS9XXX_ETHPHY /* Interrupt ID for PIN_INTR */
+
+/* Get/set/clear a PIO pin */
+#define PIO_SET(pin) ns9xxx_gpio_setpin(pin, 1)
+#define PIO_CLR(pin) ns9xxx_gpio_setpin(pin, 0)
+
+#define CW_MIN 31 /* Min contention window size */
+#define CW_MAX 1023 /* Max contention window size */
+
+/* Maximum rxsignal strength we expect to see (this is in dB) */
+#define MAC_RSSI_MAX 0x4f
+
+/* the CCK/OFDM base is 2 * Mb/s */
+#define RATE_IN_BS(x) ((x) * 500000)
+
+/* see email from Mike Schaffner from 16.03.07 (RE: Verification of
+ * initialization to mpietrek and hbujanda */
+#define VCO_DEFAULT_CHANNEL_12 0x7160
+#define VCO_DEFAULT 0x7020
+
+#define RATES_SUPPORTED 12
+
+#define AES_MAXNR 14
+
+#define CCMP_KEY_SIZE 16 /* CCMP key size */
+#define AES_BLOCK_SIZE 16
+#define EXTIV_SIZE 8 /* IV and extended IV size */
+#define MIC_SIZE 8 /* Message integrity check size */
+#define CCMP_SIZE (EXTIV_SIZE+MIC_SIZE) /* Total CCMP size */
+#define FCS_SIZE 4 // FCS (CRC-32) size
+#define DATA_SIZE 28 /* Data frame header+FCS size */
+/* Key ID byte in data frame body */
+#define EXT_IV 0x20 /* Extended IV is present */
+
+#define DW_TX_POWER_MIN_DBM 0
+#define DW_TX_POWER_MAX_DBM 15
+#define DW_TX_POWER_DEFAULT 10
+
+typedef u64 u48;
+
+struct aes_key_st {
+ unsigned long rd_key[4 *(AES_MAXNR + 1)];
+};
+
+typedef union {
+ struct {
+ u8 signal; /* (rate in 100 kbps) */
+ u8 service; /* Service: OR of SERVICE_xxx */
+ u16 length; /* Length in usecs (needs byte swap) */
+ };
+ u8 raw[4];
+ u32 raw32;
+} __attribute__((__packed__)) dw_hw_pskcck_t;
+
+typedef union {
+ struct {
+ unsigned rate :4; /* Data rate */
+ unsigned reserved :1;
+ unsigned length :12; /* Length in bytes */
+ unsigned parity :1; /* Even parity bit */
+ unsigned _pad :14; /* Service field */
+ };
+ u8 raw[4];
+ u32 raw32;
+} __attribute__((__packed__)) dw_hw_ofdm_t;
+
+/* modulation header (rx ) */
+typedef union {
+ struct {
+ unsigned mod_type : 8;
+ unsigned rssi_vga : 5;
+ unsigned rssi_lna : 2;
+ unsigned ant : 1;
+ unsigned freq_off : 16; /* Frequency offset (needs byte swap) */
+ };
+ u32 raw32;
+} __attribute__((__packed__)) dw_hw_mod_rx_t;
+
+/* modulation header ( tx ) */
+typedef union {
+ struct {
+ unsigned int mod_type :8;
+ unsigned int length :9;
+ unsigned int pad :15;
+ };
+ u32 raw32;
+} __attribute__((__packed__)) dw_hw_mod_tx_t;
+
+typedef union {
+ dw_hw_pskcck_t pskcck;
+ dw_hw_ofdm_t ofdm;
+} __attribute__((__packed__)) dw_hw_plcp_t;
+
+typedef struct {
+ dw_hw_mod_tx_t mod;
+ dw_hw_plcp_t plcp;
+} __attribute__((__packed__)) dw_hw_hdr_tx_t;
+
+typedef struct {
+ dw_hw_mod_rx_t mod;
+ dw_hw_plcp_t plcp;
+} __attribute__((__packed__)) dw_hw_hdr_rx_t;
+
+/* CCMP key data */
+
+typedef struct {
+ u8 init[AES_BLOCK_SIZE];
+ u8 header[2*AES_BLOCK_SIZE];
+} __attribute__ ((__packed__)) ccmp_data_t;
+
+typedef struct {
+ u8 valid; /* TRUE if key is valid */
+ u48 tx_pn; /* transmit packet number */
+ u48 rx_pn; /* next receive packet number */
+ struct aes_key_st rk; /* AES key schedule */
+} ccmp_key_t;
+
+/* stores the necessary data to detect duplicate frames */
+typedef struct {
+ struct list_head list;
+ u8 src[ ETH_ALEN ]; /* addr2/sender */
+ u16 seq_ctl; /* seq_ctl of last received frame */
+} dw_duplicate_t;
+
+typedef struct {
+ struct list_head list;
+ struct sk_buff* skb; /* message data */
+ dw_hw_hdr_rx_t hdr;
+} dw_frame_rx_t;
+
+typedef struct {
+ dw_frame_rx_t free;
+ dw_frame_rx_t filled;
+ dw_frame_rx_t* buffer;
+} dw_frame_rx_queue_t;
+
+typedef struct {
+ dw_hw_hdr_tx_t hdr;
+
+ /* physical length of data, including CCMP. Is not included skb->len if
+ * hardware encryption is performed. */
+ size_t phys_len;
+
+ struct {
+ ccmp_key_t* key;
+ int key_index;
+ } crypt;
+} dw_fragment_tx_t;
+
+typedef struct {
+ struct ieee80211_txb* txb;
+ dw_fragment_tx_t frags[ TX_MAX_FRAGS ];
+
+ u8 is_data : 1;
+ u8 use_hw_encryption : 1;
+ u8 use_short_preamble : 1;
+} dw_frame_tx_info_t;
+
+typedef struct {
+ struct list_head list;
+ dw_frame_tx_info_t s;
+} dw_frame_tx_t;
+
+// IBSS
+
+#define DW_BEACON_INT 100 // IBSS Beacon interval (in TU)
+
+//
+// 802.11 MIB constants
+//
+#define DW_SHORT_RETRY_LIMIT 7 // Small frame transmit retry limit
+#define DW_LONG_RETRY_LIMIT 4 // Large frame transmit retry limit
+
+#define DW_TU 1024L/1000 // Time unit (in msecs)
+#define DW_MAX_TX_LIFETIME (512*TU) // Transmit lifetime limit (in msecs)
+#define DW_MAX_RX_LIFETIME (512*TU) // Receive lifetime limit (in msecs)
+
+// Max number of fragments
+#define DW_MAX_FRAGS 16
+
+// Frame header modulation type field
+#define DW_MOD_PSKCCK 0x00 // PSK/CCK modulation
+#define DW_MOD_OFDM 0xee // OFDM modulation
+
+// PSK/CCK PLCP service field bits
+#define DW_SERVICE_LOCKED 0x04 // Locked clocks
+#define DW_SERVICE_MODSEL 0x08 // Modulation selection
+#define DW_SERVICE_LENEXT 0x80 // Length extension
+
+// MAC type field values
+#define DW_TYPE_ASSOC_REQ 0x00 // Association request
+#define DW_TYPE_ASSOC_RESP 0x10 // Association response
+#define DW_TYPE_REASSOC_REQ 0x20 // Reassociation request
+#define DW_TYPE_REASSOC_RESP 0x30 // Reassociation response
+#define DW_TYPE_PROBE_REQ 0x40 // Probe request
+#define DW_TYPE_PROBE_RESP 0x50 // Probe response
+
+#define DW_TYPE_BEACON 0x80 // Beacon
+#define DW_TYPE_ATIM 0x90 // Annoucement traffice indication
+#define DW_TYPE_DISASSOC 0xa0 // Disassociation
+#define DW_TYPE_AUTH 0xb0 // Authentication
+#define DW_TYPE_DEAUTH 0xc0 // Deauthentication
+
+#define DW_TYPE_RTS 0xb4 // Request to send
+#define DW_TYPE_CTS 0xc4 // Clear to send
+#define DW_TYPE_ACK 0xd4 // Acknowledgement
+#define DW_TYPE_PSPOLL 0xa4 // Power Save(PS)-Poll
+
+#define DW_TYPE_DATA 0x08 // Data
+
+// TRUE if buf is data or management frame
+#define DW_IS_DATA(buf) (((buf)->macHdr.fc.type & 0xcf) == TYPE_DATA)
+#define DW_IS_MGMT(buf) (((buf)->macHdr.fc.type & 0x0f) == 0)
+
+// MAC address macros
+#define DW_MAC_GROUP 0x01 // Broadcast or multicast address
+#define DW_MAC_LOCAL 0x02 // Locally administered address
+
+#define DW_EQUAL_ADDR(a1, a2) (memcmp (a1, a2, ETH_ALEN) == 0)
+#define DW_SET_ADDR(a1, a2) (memcpy (a1, a2, ETH_ALEN))
+
+// Authentication algorithm number field values
+#define DW_AUTH_OPEN 0x00 // Open system
+#define DW_AUTH_SHAREDKEY 0x01 // Shared key
+#define DW_AUTH_LEAP 0x80 // LEAP
+
+// Capability information field bits
+#define DW_CAP_ESS 0x0001 // Extended service set (infrastructure)
+#define DW_CAP_IBSS 0x0002 // Independent BSS (ad hoc)
+#define DW_CAP_POLLABLE 0x0004 // Contention free pollable
+#define DW_CAP_POLLREQ 0x0008 // Contention free poll request
+#define DW_CAP_PRIVACY 0x0010 // Privacy (WEP) required
+#define DW_CAP_SHORTPRE 0x0020 // Short preambles allowed
+#define DW_CAP_PBCC 0x0040 // PBCC modulation allowed
+#define DW_CAP_AGILITY 0x0080 // Channel agility in use
+#define DW_CAP_SHORTSLOT 0x0400 // Short slot time in use
+#define DW_CAP_DSSSOFDM 0x2000 // DSSS-OFDM in use
+
+// Status code field values
+#define DW_STAT_SUCCESS 0
+
+// Reason code field values
+#define DW_REAS_NOLONGERVALID 2
+#define DW_REAS_DEAUTH_LEAVING 3
+#define DW_REAS_INACTIVITY 4
+#define DW_REAS_INCORRECT_FRAME_UNAUTH 6
+#define DW_REAS_INCORRECT_FRAME_UNASSO 7
+
+// Information element IDs
+#define DW_ELEM_SSID 0 // Service set ID
+#define DW_ELEM_SUPRATES 1 // Supported rates
+#define DW_ELEM_DSPARAM 3 // DS parameter set
+#define DW_ELEM_IBSSPARAM 6 // IBSS parameter set
+#define DW_ELEM_COUNTRY 7 // Country information
+#define DW_ELEM_CHALLENGE 16 // Challenge text
+#define DW_ELEM_ERPINFO 42 // Extended rate PHY info
+#define DW_ELEM_RSN 48 // Robust security network (WPA2)
+#define DW_ELEM_EXTSUPRATES 50 // Extended supported rates
+#define DW_ELEM_VENDOR 221 // Vendor extension (WPA)
+
+// 802.11d related defines
+// minimum length field value in country information elelment
+#define DW_COUNTRY_INFO_MIN_LEN 6
+
+// Supported rates bits
+#define DW_RATE_BASIC 0x80 // Bit set if basic rate
+
+// TRUE if channel number in 5 GHz band
+#define DW_CHAN_5G(chan) ((chan) > 14)
+
+// ERP info bits
+#define DW_ERP_NONERP 0x01 // Non-ERP present
+#define DW_ERP_USEPROTECT 0x02 // Use protection
+#define DW_ERP_BARKER 0x04 // Barker (long) preamble mode
+
+// Key ID byte in data frame body
+#define DW_EXT_IV 0x20 // Extended IV is present
+
+// Correct CRC-32 check value
+#define DW_GOOD_CRC32 0x2144df1c
+
+#pragma pack()
+
+#define BEACON_BODY_SIZE 64
+
+// MAC buffer, including complete MAC frame
+typedef struct {
+ dw_hw_hdr_tx_t hwHdr; // Frame and PLCP headers
+ u16 fc; // Frame control
+ u16 duration; // Duration/ID (needs byte swap)
+ u8 addr1[ ETH_ALEN ]; // Address 1
+ u8 addr2[ ETH_ALEN ]; // Address 2
+ u8 addr3[ ETH_ALEN ]; // Address 3
+ u16 seq_ctl; // Sequence control fields
+ u8 body[BEACON_BODY_SIZE];
+} __attribute__ ((packed)) dw_beacon_frame;
+
+// Length (in usecs) of a MAC frame of bytes at rate (in 500kbps units)
+// not including SIFS and PLCP preamble/header
+#define DW_LENGTH_uS(bytes, rate) ((16*(bytes)+(rate)-1)/(rate))
+
+// Length (in usecs) of SIFS and PLCP preamble/header.
+#define DW_PRE_LEN_uS(rate) (USE_SHORTPRE(rate) ? 106 : 202)
+
+// Duration (in usecs) of an OFDM frame at rate (in 500kbps units)
+// including SIFS and PLCP preamble/header
+#define DW_OFDM_DUR(bytes, rate) (36 + 4*((4*(bytes)+(rate)+10)/(rate)))
+
+// Information on each supported rate
+typedef struct {
+ u8 bps; // Bit rate in 500kbps units
+ u8 ofdmCode; // OFDM rate code, 0 if not OFDM
+ u16 ackLen; // Duration of ACK or CTS in usecs
+} RateInfo;
+
+#define DW_IBSS_DEFAULT_CHANNEL 6
+
+// End of IBSS
+
+typedef struct dw_priv {
+ struct net_device* dev;
+ struct ieee80211_device* ieee;
+ struct ieee80211softmac_device* softmac;
+
+ struct iw_statistics wstats;
+
+ spinlock_t lock;
+
+ struct timer_list management_timer;
+
+ /* Additional information, specific to the 80211 cores. */
+ /* Driver status flags. */
+ u32 recovery:1; /* TRUE if interval follows rate increase */
+ /* Interrupt Service Routine tasklet (bottom-half) */
+ u8 channel;
+ /* Informational stuff. */
+ char nick[IW_ESSID_MAX_SIZE + 1];
+ int short_retry_limit;
+ int long_retry_limit;
+ ccmp_key_t aeskeys[WEP_KEYS];
+ int cw; /* Contention window size */
+ int success; /* Successful intervals */
+ int success_threshold; /* Successful intervals needed to increase rate */
+ atomic_t fix_rate;
+
+ struct ieee80211softmac_ratesinfo ap_ri;
+
+ struct {
+ int counter;
+ } activity;
+
+ struct {
+ int index;
+ int counter;
+ int recovery;
+ int tx_data_any;
+ int tx_data_ack;
+ int have_activity;
+ int success;
+ int success_threshold;
+ } rate;
+
+ struct {
+ struct tasklet_struct tasklet;
+ dw_frame_rx_queue_t queue;
+ char pause;
+
+ /* maintains the list of received frames */
+ struct {
+ dw_duplicate_t known;
+ dw_duplicate_t free;
+ dw_duplicate_t entries[ RX_DUP_SENDER_SIZE ];
+ } dups;
+ } rx;
+
+ struct {
+ struct tasklet_struct tasklet;
+ dw_frame_tx_t frames[ TX_QUEUE_SIZE ];
+ struct list_head queued;
+ struct list_head free;
+ atomic_t seq_nr;
+ int timeout;
+ int times_sent;
+ int retries;
+ int fragment;
+ char pending;
+ char pause;
+ struct semaphore pause_sem;
+ char last_was_data;
+ int basics[ RATES_SUPPORTED ];
+ void * data_pending_ack;
+ unsigned long jiffies_pending_ack;
+ } tx;
+
+ unsigned char tx_power;
+
+ int beacon_ready;
+ struct delayed_work beacon_work;
+ int beacon_body_length;
+ dw_beacon_frame beacon_frame;
+ struct ieee80211softmac_network adhoc;
+
+ unsigned long jiffies_last_beacon;
+ int reconnection_attempts;
+} dw_priv_t;
+
+/* dw_(un)lock() protect struct dw_private.
+ */
+#define dw_lock(priv, flags) spin_lock_irqsave(&(priv)->lock, flags)
+#define dw_unlock(priv, flags) spin_unlock_irqrestore(&(priv)->lock, flags)
+
+/* Frame header modulation type field */
+#define MOD_PSKCCK 0x00 /* PSK/CCK modulation */
+#define MOD_OFDM 0xee /* OFDM modulation */
+
+/* PLCP service field bits */
+#define SERVICE_LOCKED 0x04 /* Locked clocks */
+#define SERVICE_MODSEL 0x08 /* Modulation selection */
+#define SERVICE_LENEXT 0x80 /* Length extension */
+
+/* Threshold values. */
+#define DW_MIN_RTS_THRESHOLD 1U
+#define DW_MAX_RTS_THRESHOLD 2304U
+/* 1536 is default for most routers, but our FIFO is larger, so we could accept
+ * more data, e.g. for improvements when doing AES etc. This has been reported
+ * being done with Apple iTunes */
+#define DW_MTU 2048
+
+#define AES_BITS 128 /* 128 bit keys, 10 rounds */
+
+# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+/* Get 16 bits at byte pointer */
+#define GET16(bp) ((bp)[0] | ((bp)[1] << 8))
+/* Get 32 bits at byte pointer */
+#define GET32(bp) ((bp)[0] | ((bp)[1] << 8) | ((bp)[2] << 16) | ((bp)[3] << 24))
+/* Store 16 bits at byte pointer */
+#define SET16(bp, data) { (bp)[0] = (data); \
+ (bp)[1] = (data) >> 8; }
+/* Store 32 bits at byte pointer */
+#define SET32(bp, data) { (bp)[0] = (data); \
+ (bp)[1] = (data) >> 8; \
+ (bp)[2] = (data) >> 16; \
+ (bp)[3] = (data) >> 24; }
+
+#endif /* FS_DIGI_WI_G_DRIVER_H */
diff --git a/drivers/net/wireless/digi_wi_g_priv_handler.c b/drivers/net/wireless/digi_wi_g_priv_handler.c
new file mode 100644
index 000000000000..c7ab82a2e335
--- /dev/null
+++ b/drivers/net/wireless/digi_wi_g_priv_handler.c
@@ -0,0 +1,345 @@
+/*
+ * net/wireless/digi_wi_g_priv_handler.c
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version2 as published by
+ * the Free Software Foundation.
+*/
+/*
+ * !Revision: $Revision: 1.1 $
+ * !Author: Markus Pietrek
+ * !Descr: Contains private user handler stuff.
+*/
+
+#warning fix the getter functions, they compare 0 instead of '0'
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_set_swencryption
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_set_swencryption(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ char on;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ on = *extra;
+
+ digi_wi_g_lock(priv, flags);
+ if (on)
+ priv->hw_aes = 0;
+ else
+ priv->hw_aes = 1;
+ digi_wi_g_unlock(priv, flags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_get_swencryption
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_get_swencryption(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ int on;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ digi_wi_g_lock(priv, flags);
+ if (priv->hw_aes)
+ on = 0;
+ else
+ on = 1;
+ digi_wi_g_unlock(priv, flags);
+
+ if (on)
+ strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING);
+ else
+ strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING);
+ data->data.length = strlen(extra + 1);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_set_ant_div
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_set_ant_div(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ char on;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ on = *extra;
+
+ digi_wi_g_lock(priv, flags);
+ if (on) {
+ priv->ant_div = 1;
+ HW_GEN_CONTROL |= GEN_ANTDIV;
+ } else {
+ priv->ant_div = 0;
+ HW_GEN_CONTROL &= ~GEN_ANTDIV;
+ }
+ digi_wi_g_unlock(priv, flags);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_get_ant_div
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_get_ant_div(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ int on;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ digi_wi_g_lock(priv, flags);
+ if (priv->ant_div)
+ on = 1;
+ else
+ on = 0;
+ digi_wi_g_unlock(priv, flags);
+
+ if (on)
+ strncpy(extra, "1 (Antenna Diversity is enabled) ", MAX_WX_STRING);
+ else
+ strncpy(extra, "0 (Antenna Diversity is disabled) ", MAX_WX_STRING);
+ data->data.length = strlen(extra + 1);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_set_antenna
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_set_antenna(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ char ant;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ ant = *extra;
+
+ if ((ant == 0) || (ant == 1)) {
+ digi_wi_g_lock(priv, flags);
+ if (ant == 0) {
+ priv->antenna = 0;
+ HW_GEN_CONTROL &= ~GEN_ANTSEL;
+ } else {
+ priv->antenna = 1;
+ HW_GEN_CONTROL |= GEN_ANTSEL;
+ }
+ digi_wi_g_unlock(priv, flags);
+ } else {
+ printk(PFX KERN_ERR "Value should be 0 or 1.\n");
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_get_antenna
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_get_antenna(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ int ant;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ digi_wi_g_lock(priv, flags);
+ ant = priv->antenna;
+ digi_wi_g_unlock(priv, flags);
+
+ if (ant == 0)
+ strncpy(extra, "0 (Antenna 1 selected) ", MAX_WX_STRING);
+ else
+ strncpy(extra, "1 (Antenna 2 selected) ", MAX_WX_STRING);
+ data->data.length = strlen(extra + 1);
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_set_rx_off
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_set_rx_off(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ char rx_off;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ rx_off = *extra;
+
+ if ((rx_off == 0) || (rx_off == 1)) {
+ digi_wi_g_lock(priv, flags);
+ if (rx_off == 0) {
+ priv->rx_off = 0;
+ } else {
+ priv->rx_off = 1;
+ }
+ digi_wi_g_unlock(priv, flags);
+ } else {
+ printk(PFX KERN_ERR "Value should be 0 or 1.\n");
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+
+/***********************************************************************
+ * @Function: digi_wi_g_wx_get_rx_off
+ * @Return:
+ * @Descr:
+ ***********************************************************************/
+static int digi_wi_g_wx_get_rx_off(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct digi_wi_g_private *priv = ieee80211softmac_priv(dev);
+ unsigned long flags;
+ int rx_off;
+
+ DBG_FN( DBG_INTERFACE | DBG_MINOR );
+
+ digi_wi_g_lock(priv, flags);
+ rx_off = priv->rx_off;
+ digi_wi_g_unlock(priv, flags);
+
+ if (rx_off == 0)
+ strncpy(extra, "0 (rx is on) ", MAX_WX_STRING);
+ else
+ strncpy(extra, "1 (rx is off) ", MAX_WX_STRING);
+ data->data.length = strlen(extra + 1);
+
+ return 0;
+}
+
+static const iw_handler digi_wi_g_priv_wx_handlers[] = {
+ /* Enable/Disable Software Encryption mode */
+ digi_wi_g_wx_set_swencryption,
+ /* Get Software Encryption mode */
+ digi_wi_g_wx_get_swencryption,
+ /* Enable/Disable Antenna Diversity mode */
+ digi_wi_g_wx_set_ant_div,
+ /* Get Antenna Diversity mode */
+ digi_wi_g_wx_get_ant_div,
+ /* Select Antenna */
+ digi_wi_g_wx_set_antenna,
+ /* Get Antenna */
+ digi_wi_g_wx_get_antenna,
+ /* Set rx off */
+ digi_wi_g_wx_set_rx_off,
+ /* Get rx off */
+ digi_wi_g_wx_get_rx_off,
+};
+
+#define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 0)
+#define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 1)
+#define PRIV_WX_SET_ANTDIV (SIOCIWFIRSTPRIV + 2)
+#define PRIV_WX_GET_ANTDIV (SIOCIWFIRSTPRIV + 3)
+#define PRIV_WX_SET_ANTSEL (SIOCIWFIRSTPRIV + 4)
+#define PRIV_WX_GET_ANTSEL (SIOCIWFIRSTPRIV + 5)
+#define PRIV_WX_SET_RX_OFF (SIOCIWFIRSTPRIV + 6)
+#define PRIV_WX_GET_RX_OFF (SIOCIWFIRSTPRIV + 7)
+
+static const struct iw_priv_args digi_wi_g_priv_wx_args[] = {
+ {
+ .cmd = PRIV_WX_SET_SWENCRYPTION,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_swencrypt",
+ },
+ {
+ .cmd = PRIV_WX_GET_SWENCRYPTION,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_swencrypt",
+ },
+ {
+ .cmd = PRIV_WX_SET_ANTDIV,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_ant_div",
+ },
+ {
+ .cmd = PRIV_WX_GET_ANTDIV,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_ant_div",
+ },
+ {
+ .cmd = PRIV_WX_SET_ANTSEL,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_ant_sel",
+ },
+ {
+ .cmd = PRIV_WX_GET_ANTSEL,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_ant_sel",
+ },
+ {
+ .cmd = PRIV_WX_SET_RX_OFF,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_rx_off",
+ },
+ {
+ .cmd = PRIV_WX_GET_RX_OFF,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_rx_off",
+ },
+};
+
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 222904411a13..835082cb1637 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -275,6 +275,21 @@ config AT91_CF
Say Y here to support the CompactFlash controller on AT91 chips.
Or choose M to compile the driver as a module named "at91_cf".
+config S3C2443_PCMCIA
+ tristate "S3C2443 PCMCIA driver"
+ depends on PCMCIA && CPU_S3C2443
+ help
+ Say Y here to support the CompactFlash/PCMCIA interface with the
+ S3C2443 SoC
+
+config PCMCIA_MX31ADS
+ tristate "MX31ADS PCMCIA support"
+ depends on ARM && MACH_MX31ADS && PCMCIA
+ help
+ Say Y here to include support for the Freescale i.MX31 PCMCIA controller.
+
+ This driver is also available as a module called mx31ads_pcmcia.
+
config ELECTRA_CF
tristate "Electra CompactFlash Controller"
depends on PCMCIA && PPC_PASEMI
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 238629ad7f7c..44a60c9bc0e5 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -32,7 +32,9 @@ obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o
obj-$(CONFIG_OMAP_CF) += omap_cf.o
obj-$(CONFIG_BFIN_CFPCMCIA) += bfin_cf_pcmcia.o
obj-$(CONFIG_AT91_CF) += at91_cf.o
+obj-$(CONFIG_S3C2443_PCMCIA) += s3c2443_pcmcia.o
obj-$(CONFIG_ELECTRA_CF) += electra_cf.o
+obj-$(CONFIG_PCMCIA_MX31ADS) += mx31ads-pcmcia.o
sa11xx_core-y += soc_common.o sa11xx_base.o
pxa2xx_core-y += soc_common.o pxa2xx_base.o
diff --git a/drivers/pcmcia/mx31ads-pcmcia.c b/drivers/pcmcia/mx31ads-pcmcia.c
new file mode 100644
index 000000000000..8031da1af6c7
--- /dev/null
+++ b/drivers/pcmcia/mx31ads-pcmcia.c
@@ -0,0 +1,1291 @@
+/*======================================================================
+ drivers/pcmcia/mx31ads-pcmica.c
+
+ Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+
+ Device driver for the PCMCIA control functionality of i.Mx31
+ microprocessors.
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is John G. Dorsey
+ <john+@cs.cmu.edu>. Portions created by John G. Dorsey are
+ Copyright (C) 1999 John G. Dorsey. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU Public License version 2 (the "GPL"), in which
+ case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <asm/mach-types.h>
+#include <mach/pcmcia.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+
+#include <mach/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "mx31ads-pcmcia.h"
+#include <linux/irq.h>
+
+#define MX31ADS_PCMCIA_IRQ MXC_INT_PCMCIA
+
+/*
+ * The mapping of window size to bank size value
+ */
+static bsize_map_t bsize_map[] = {
+ /* Window size Bank size */
+ {POR_1, POR_BSIZE_1},
+ {POR_2, POR_BSIZE_2},
+ {POR_4, POR_BSIZE_4},
+ {POR_8, POR_BSIZE_8},
+ {POR_16, POR_BSIZE_16},
+ {POR_32, POR_BSIZE_32},
+ {POR_64, POR_BSIZE_64},
+ {POR_128, POR_BSIZE_128},
+ {POR_256, POR_BSIZE_256},
+ {POR_512, POR_BSIZE_512},
+
+ {POR_1K, POR_BSIZE_1K},
+ {POR_2K, POR_BSIZE_2K},
+ {POR_4K, POR_BSIZE_4K},
+ {POR_8K, POR_BSIZE_8K},
+ {POR_16K, POR_BSIZE_16K},
+ {POR_32K, POR_BSIZE_32K},
+ {POR_64K, POR_BSIZE_64K},
+ {POR_128K, POR_BSIZE_128K},
+ {POR_256K, POR_BSIZE_256K},
+ {POR_512K, POR_BSIZE_512K},
+
+ {POR_1M, POR_BSIZE_1M},
+ {POR_2M, POR_BSIZE_2M},
+ {POR_4M, POR_BSIZE_4M},
+ {POR_8M, POR_BSIZE_8M},
+ {POR_16M, POR_BSIZE_16M},
+ {POR_32M, POR_BSIZE_32M},
+ {POR_64M, POR_BSIZE_64M}
+};
+
+#define to_mx31ads_pcmcia_socket(x) container_of(x, struct mx31ads_pcmcia_socket, socket)
+
+/* mx31ads_pcmcia_find_bsize()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * Find the bsize according to the window size passed in
+ *
+ * Return:
+ */
+static int mx31ads_pcmcia_find_bsize(unsigned long win_size)
+{
+ int i, nr = sizeof(bsize_map) / sizeof(bsize_map_t);
+ int bsize = -1;
+
+ for (i = 0; i < nr; i++) {
+ if (bsize_map[i].win_size == win_size) {
+ bsize = bsize_map[i].bsize;
+ break;
+ }
+ }
+
+ pr_debug(KERN_INFO "nr = %d bsize = 0x%0x\n", nr, bsize);
+ if (bsize < 0 || i > nr) {
+ pr_debug(KERN_INFO "No such bsize\n");
+ return -ENODEV;
+ }
+
+ return bsize;
+}
+
+/* mx31ads_common_pcmcia_sock_init()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * (Re-)Initialise the socket, turning on status interrupts
+ * and PCMCIA bus. This must wait for power to stabilise
+ * so that the card status signals report correctly.
+ *
+ * Returns: 0
+ */
+static int mx31ads_common_pcmcia_sock_init(struct pcmcia_socket *sock)
+{
+ struct mx31ads_pcmcia_socket *skt = to_mx31ads_pcmcia_socket(sock);
+
+ pr_debug(KERN_INFO "initializing socket\n");
+
+ skt->ops->socket_init(skt);
+ return 0;
+}
+
+/*
+ * mx31ads_common_pcmcia_config_skt
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * Convert PCMCIA socket state to our socket configure structure.
+ */
+static int
+mx31ads_common_pcmcia_config_skt(struct mx31ads_pcmcia_socket *skt,
+ socket_state_t * state)
+{
+ int ret;
+
+ ret = skt->ops->configure_socket(skt, state);
+ if (ret == 0) {
+ /*
+ * This really needs a better solution. The IRQ
+ * may or may not be claimed by the driver.
+ */
+ if (skt->irq_state != 1 && state->io_irq) {
+ skt->irq_state = 1;
+ set_irq_type(skt->irq, IRQF_TRIGGER_FALLING);
+ } else if (skt->irq_state == 1 && state->io_irq == 0) {
+ skt->irq_state = 0;
+ set_irq_type(skt->irq, IRQF_TRIGGER_RISING);
+ }
+
+ skt->cs_state = *state;
+ }
+
+ if (ret < 0)
+ pr_debug(KERN_ERR "mx31ads_common_pcmcia: unable to configure"
+ " socket\n");
+
+ return ret;
+}
+
+/*
+ * mx31ads_common_pcmcia_suspend()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * Remove power on the socket, disable IRQs from the card.
+ * Turn off status interrupts, and disable the PCMCIA bus.
+ *
+ * Returns: 0
+ */
+static int mx31ads_common_pcmcia_suspend(struct pcmcia_socket *sock)
+{
+ struct mx31ads_pcmcia_socket *skt = to_mx31ads_pcmcia_socket(sock);
+ int ret;
+
+ pr_debug(KERN_INFO "suspending socket\n");
+
+ ret = mx31ads_common_pcmcia_config_skt(skt, &dead_socket);
+ if (ret == 0)
+ skt->ops->socket_suspend(skt);
+
+ return ret;
+}
+
+static unsigned int mx31ads_common_pcmcia_skt_state(struct mx31ads_pcmcia_socket
+ *skt)
+{
+ struct pcmcia_state state;
+ unsigned int stat;
+
+ memset(&state, 0, sizeof(struct pcmcia_state));
+
+ skt->ops->socket_state(skt, &state);
+
+ stat = state.detect ? SS_DETECT : 0;
+ stat |= state.ready ? SS_READY : 0;
+ stat |= state.wrprot ? SS_WRPROT : 0;
+ stat |= state.vs_3v ? SS_3VCARD : 0;
+ stat |= state.vs_Xv ? SS_XVCARD : 0;
+
+ /* The power status of individual sockets is not available
+ * explicitly from the hardware, so we just remember the state
+ * and regurgitate it upon request:
+ */
+ stat |= skt->cs_state.Vcc ? SS_POWERON : 0;
+
+ if (skt->cs_state.flags & SS_IOCARD)
+ stat |= state.bvd1 ? SS_STSCHG : 0;
+ else {
+ if (state.bvd1 == 0)
+ stat |= SS_BATDEAD;
+ else if (state.bvd2 == 0)
+ stat |= SS_BATWARN;
+ }
+
+ pr_debug(KERN_INFO "stat = 0x%08x\n", stat);
+
+ return stat;
+}
+
+/*
+ * Implements the get_status() operation for the in-kernel PCMCIA
+ * service (formerly SS_GetStatus in Card Services). Essentially just
+ * fills in bits in `status' according to internal driver state or
+ * the value of the voltage detect chipselect register.
+ *
+ * As a debugging note, during card startup, the PCMCIA core issues
+ * three set_socket() commands in a row the first with RESET deasserted,
+ * the second with RESET asserted, and the last with RESET deasserted
+ * again. Following the third set_socket(), a get_status() command will
+ * be issued. The kernel is looking for the SS_READY flag (see
+ * setup_socket(), reset_socket(), and unreset_socket() in cs.c).
+ *
+ * Returns: 0
+ */
+static int mx31ads_common_pcmcia_get_status(struct pcmcia_socket *sock,
+ unsigned int *status)
+{
+ struct mx31ads_pcmcia_socket *skt = to_mx31ads_pcmcia_socket(sock);
+
+ skt->status = mx31ads_common_pcmcia_skt_state(skt);
+ *status = skt->status;
+
+ return 0;
+}
+
+/*
+ * Implements the set_socket() operation for the in-kernel PCMCIA
+ * service (formerly SS_SetSocket in Card Services). We more or
+ * less punt all of this work and let the kernel handle the details
+ * of power configuration, reset, &c. We also record the value of
+ * `state' in order to regurgitate it to the PCMCIA core later.
+ *
+ * Returns: 0
+ */
+static int mx31ads_common_pcmcia_set_socket(struct pcmcia_socket *sock,
+ socket_state_t * state)
+{
+ struct mx31ads_pcmcia_socket *skt = to_mx31ads_pcmcia_socket(sock);
+
+ pr_debug(KERN_INFO
+ "mask: %s%s%s%s%s%sflags: %s%s%s%s%s%sVcc %d Vpp %d irq %d\n",
+ (state->csc_mask == 0) ? "<NONE> " : "",
+ (state->csc_mask & SS_DETECT) ? "DETECT " : "",
+ (state->csc_mask & SS_READY) ? "READY " : "",
+ (state->csc_mask & SS_BATDEAD) ? "BATDEAD " : "",
+ (state->csc_mask & SS_BATWARN) ? "BATWARN " : "",
+ (state->csc_mask & SS_STSCHG) ? "STSCHG " : "",
+ (state->flags == 0) ? "<NONE> " : "",
+ (state->flags & SS_PWR_AUTO) ? "PWR_AUTO " : "",
+ (state->flags & SS_IOCARD) ? "IOCARD " : "",
+ (state->flags & SS_RESET) ? "RESET " : "",
+ (state->flags & SS_SPKR_ENA) ? "SPKR_ENA " : "",
+ (state->flags & SS_OUTPUT_ENA) ? "OUTPUT_ENA " : "",
+ state->Vcc, state->Vpp, state->io_irq);
+
+ pr_debug(KERN_INFO
+ "csc_mask: %08x flags: %08x Vcc: %d Vpp: %d io_irq: %d\n",
+ state->csc_mask, state->flags, state->Vcc, state->Vpp,
+ state->io_irq);
+
+ return mx31ads_common_pcmcia_config_skt(skt, state);
+}
+
+/*
+ * Set address and profile to window registers PBR, POR, POFR
+ */
+static int mx31ads_pcmcia_set_window_reg(ulong start, ulong end, u_int window)
+{
+ int bsize;
+ ulong size = end - start + 1;
+
+ bsize = mx31ads_pcmcia_find_bsize(size);
+ if (bsize < 0) {
+ pr_debug("Cannot set the window register\n");
+ return -1;
+ }
+ /* Disable the window */
+ _reg_PCMCIA_POR(window) &= ~PCMCIA_POR_PV;
+
+ /* Set PBR, POR, POFR */
+ _reg_PCMCIA_PBR(window) = start;
+ _reg_PCMCIA_POR(window) &= ~(PCMCIA_POR_PRS_MASK
+ | PCMCIA_POR_WPEN
+ | PCMCIA_POR_WP
+ | PCMCIA_POR_BSIZE_MASK
+ | PCMCIA_POR_PPS_8);
+ _reg_PCMCIA_POR(window) |= bsize | PCMCIA_POR_PPS_16;
+
+ switch (window) {
+ case IO_WINDOW:
+ _reg_PCMCIA_POR(window) |= PCMCIA_POR_PRS(PCMCIA_POR_PRS_IO);
+ break;
+
+ case ATTRIBUTE_MEMORY_WINDOW:
+ _reg_PCMCIA_POR(window) |=
+ PCMCIA_POR_PRS(PCMCIA_POR_PRS_ATTRIBUTE);
+ break;
+
+ case COMMON_MEMORY_WINDOW:
+ _reg_PCMCIA_POR(window) |=
+ PCMCIA_POR_PRS(PCMCIA_POR_PRS_COMMON);
+ break;
+
+ default:
+ pr_debug("Window %d is not support\n", window);
+ return -1;
+ }
+ _reg_PCMCIA_POFR(window) = 0;
+
+ /* Enable the window */
+ _reg_PCMCIA_POR(window) |= PCMCIA_POR_PV;
+
+ return 0;
+}
+
+/*
+ * Implements the set_io_map() operation for the in-kernel PCMCIA
+ * service (formerly SS_SetIOMap in Card Services). We configure
+ * the map speed as requested, but override the address ranges
+ * supplied by Card Services.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+static int
+mx31ads_common_pcmcia_set_io_map(struct pcmcia_socket *sock,
+ struct pccard_io_map *map)
+{
+ struct mx31ads_pcmcia_socket *skt = to_mx31ads_pcmcia_socket(sock);
+ unsigned short speed = map->speed;
+
+ pr_debug("map %u speed %u start 0x%08lx stop 0x%08lx\n",
+ map->map, map->speed, map->start, map->stop);
+ pr_debug("flags: %s%s%s%s%s%s%s%s\n",
+ (map->flags == 0) ? "<NONE>" : "",
+ (map->flags & MAP_ACTIVE) ? "ACTIVE " : "",
+ (map->flags & MAP_16BIT) ? "16BIT " : "",
+ (map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "",
+ (map->flags & MAP_0WS) ? "0WS " : "",
+ (map->flags & MAP_WRPROT) ? "WRPROT " : "",
+ (map->flags & MAP_USE_WAIT) ? "USE_WAIT " : "",
+ (map->flags & MAP_PREFETCH) ? "PREFETCH " : "");
+
+ if (map->map >= MAX_IO_WIN) {
+ pr_debug(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
+ map->map);
+ return -1;
+ }
+
+ if (map->flags & MAP_ACTIVE) {
+ if (speed == 0)
+ speed = PCMCIA_IO_ACCESS;
+ } else {
+ speed = 0;
+ }
+
+ skt->spd_io[map->map] = speed;
+ skt->ops->set_timing(skt);
+
+ if (map->stop == 1)
+ map->stop = PAGE_SIZE - 1;
+
+ skt->socket.io_offset = (unsigned long)skt->virt_io;
+ map->stop -= map->start;
+ map->stop += (unsigned long)skt->virt_io;
+ map->start = (unsigned long)skt->virt_io;
+
+ mx31ads_pcmcia_set_window_reg(skt->res_io.start, skt->res_io.end,
+ IO_WINDOW);
+
+ pr_debug(KERN_ERR "IO window: _reg_PCMCIA_PBR(%d) = %08x\n",
+ IO_WINDOW, _reg_PCMCIA_PBR(IO_WINDOW));
+ pr_debug(KERN_ERR "IO window: _reg_PCMCIA_POR(%d) = %08x\n",
+ IO_WINDOW, _reg_PCMCIA_POR(IO_WINDOW));
+
+ return 0;
+}
+
+/*
+ * Implements the set_mem_map() operation for the in-kernel PCMCIA
+ * service (formerly SS_SetMemMap in Card Services). We configure
+ * the map speed as requested, but override the address ranges
+ * supplied by Card Services.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+static int
+mx31ads_common_pcmcia_set_mem_map(struct pcmcia_socket *sock,
+ struct pccard_mem_map *map)
+{
+ struct mx31ads_pcmcia_socket *skt = to_mx31ads_pcmcia_socket(sock);
+ struct resource *res;
+ unsigned short speed = map->speed;
+
+ pr_debug
+ (KERN_INFO
+ "map %u speed %u card_start %08x flags%08x static_start %08lx\n",
+ map->map, map->speed, map->card_start, map->flags,
+ map->static_start);
+ pr_debug(KERN_INFO "flags: %s%s%s%s%s%s%s%s\n",
+ (map->flags == 0) ? "<NONE>" : "",
+ (map->flags & MAP_ACTIVE) ? "ACTIVE " : "",
+ (map->flags & MAP_16BIT) ? "16BIT " : "",
+ (map->flags & MAP_AUTOSZ) ? "AUTOSZ " : "",
+ (map->flags & MAP_0WS) ? "0WS " : "",
+ (map->flags & MAP_WRPROT) ? "WRPROT " : "",
+ (map->flags & MAP_ATTRIB) ? "ATTRIB " : "",
+ (map->flags & MAP_USE_WAIT) ? "USE_WAIT " : "");
+
+ if (map->map >= MAX_WIN)
+ return -EINVAL;
+
+ if (map->flags & MAP_ACTIVE) {
+ if (speed == 0)
+ speed = 300;
+ } else {
+ speed = 0;
+ }
+
+ if (map->flags & MAP_ATTRIB) {
+ res = &skt->res_attr;
+ skt->spd_attr[map->map] = speed;
+ skt->spd_mem[map->map] = 0;
+ mx31ads_pcmcia_set_window_reg(res->start, res->end,
+ ATTRIBUTE_MEMORY_WINDOW);
+
+ pr_debug(KERN_INFO "Attr window: _reg_PCMCIA_PBR(%d) = %08x\n",
+ ATTRIBUTE_MEMORY_WINDOW,
+ _reg_PCMCIA_PBR(ATTRIBUTE_MEMORY_WINDOW));
+ pr_debug(KERN_INFO "_reg_PCMCIA_POR(%d) = %08x\n",
+ ATTRIBUTE_MEMORY_WINDOW,
+ _reg_PCMCIA_POR(ATTRIBUTE_MEMORY_WINDOW));
+
+ } else {
+ res = &skt->res_mem;
+ skt->spd_attr[map->map] = 0;
+ skt->spd_mem[map->map] = speed;
+ mx31ads_pcmcia_set_window_reg(res->start, res->end,
+ COMMON_MEMORY_WINDOW);
+
+ pr_debug(KERN_INFO "Com window: _reg_PCMCIA_PBR(%d) = %08x\n",
+ COMMON_MEMORY_WINDOW,
+ _reg_PCMCIA_PBR(COMMON_MEMORY_WINDOW));
+ pr_debug(KERN_INFO "Com window: _reg_PCMCIA_POR(%d) = %08x\n",
+ COMMON_MEMORY_WINDOW,
+ _reg_PCMCIA_POR(COMMON_MEMORY_WINDOW));
+ }
+
+ skt->ops->set_timing(skt);
+
+ map->static_start = res->start + map->card_start;
+
+ return 0;
+}
+
+static struct pccard_operations mx31ads_common_pcmcia_operations = {
+ .init = mx31ads_common_pcmcia_sock_init,
+ .suspend = mx31ads_common_pcmcia_suspend,
+ .get_status = mx31ads_common_pcmcia_get_status,
+ .set_socket = mx31ads_common_pcmcia_set_socket,
+ .set_io_map = mx31ads_common_pcmcia_set_io_map,
+ .set_mem_map = mx31ads_common_pcmcia_set_mem_map,
+};
+
+/* ============================================================================== */
+
+static inline void mx31ads_pcmcia_irq_config(void)
+{
+ /* Setup irq */
+ _reg_PCMCIA_PER =
+ (PCMCIA_PER_RDYLE | PCMCIA_PER_CDE1 | PCMCIA_PER_CDE2);
+}
+
+static inline void mx31ads_pcmcia_invalidate_windows(void)
+{
+ int i;
+
+ for (i = 0; i < PCMCIA_WINDOWS; i++) {
+ _reg_PCMCIA_PBR(i) = 0;
+ _reg_PCMCIA_POR(i) = 0;
+ _reg_PCMCIA_POFR(i) = 0;
+ }
+}
+
+extern void gpio_pcmcia_active(void);
+extern void gpio_pcmcia_inactive(void);
+
+static int mx31ads_pcmcia_hw_init(struct mx31ads_pcmcia_socket *skt)
+{
+ /* Configure the pins for PCMCIA */
+ gpio_pcmcia_active();
+
+ /*
+ * enabling interrupts at this time causes a flood of interrupts
+ * if a card is present, so wait for configure_socket
+ * to enable them when requested.
+ *
+ * mx31ads_pcmcia_irq_config();
+ */
+ mx31ads_pcmcia_invalidate_windows();
+
+ /* Register interrupt. */
+ skt->irq = MX31ADS_PCMCIA_IRQ;
+
+ return 0;
+}
+
+static void mx31ads_pcmcia_free_irq(struct mx31ads_pcmcia_socket *skt,
+ unsigned int irq)
+{
+ free_irq(irq, skt);
+}
+
+static void mx31ads_pcmcia_hw_shutdown(struct mx31ads_pcmcia_socket *skt)
+{
+ mx31ads_pcmcia_invalidate_windows();
+ mx31ads_pcmcia_free_irq(skt, MX31ADS_PCMCIA_IRQ);
+
+ /* Disable the pins */
+ gpio_pcmcia_inactive();
+}
+
+/*
+ * Get the socket state
+ */
+static void
+mx31ads_pcmcia_socket_state(struct mx31ads_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ unsigned long pins;
+
+ pins = _reg_PCMCIA_PIPR;
+ pr_debug(KERN_INFO "_reg_PCMCIA_PIPR = 0x%08lx\n", pins);
+
+ state->ready = (pins & PCMCIA_PIPR_RDY) ? 1 : 0;
+ state->bvd2 = (pins & PCMCIA_PIPR_BVD2) ? 1 : 0;
+ state->bvd1 = (pins & PCMCIA_PIPR_BVD1) ? 1 : 0;
+
+ if ((pins & PCMCIA_PIPR_CD) == PCMCIA_PIPR_CD) {
+ state->detect = 0;
+ skt->cs_state.csc_mask |= SS_INSERTION;
+ } else {
+ state->detect = 1;
+ }
+ state->detect = (pins & PCMCIA_PIPR_CD) ? 0 : 1;
+ state->wrprot = (pins & PCMCIA_PIPR_WP) ? 1 : 0;
+ state->poweron = (pins & PCMCIA_PIPR_POWERON) ? 1 : 0;
+#if 0
+ if ((pins & PCMCIA_PIPR_CD) == PCMCIA_PIPR_CD) {
+ state->detect = 0;
+ skt->cs_state.csc_mask |= SS_INSERTION;
+ } else {
+ state->detect = 1;
+ }
+ if (pins & PCMCIA_PIPR_VS_5V) {
+ state->vs_3v = 0;
+ skt->cs_state.Vcc = 33;
+ } else {
+ state->vs_3v = 1;
+ skt->cs_state.Vcc = 50;
+ }
+#endif
+ state->vs_3v = (pins & PCMCIA_PIPR_VS_5V) ? 0 : 1;
+ state->vs_Xv = 0;
+}
+
+static __inline__ void mx31ads_pcmcia_low_power(bool enable)
+{
+ if (enable)
+ _reg_PCMCIA_PGCR |= PCMCIA_PGCR_LPMEN;
+ else
+ _reg_PCMCIA_PGCR &= ~PCMCIA_PGCR_LPMEN;
+}
+
+static __inline__ void mx31ads_pcmcia_soft_reset(void)
+{
+ _reg_PCMCIA_PGCR |= PCMCIA_PGCR_RESET;
+ msleep(2);
+
+ _reg_PCMCIA_PGCR &= ~(PCMCIA_PGCR_RESET | PCMCIA_PGCR_LPMEN);
+ _reg_PCMCIA_PGCR |= PCMCIA_PGCR_POE;
+ msleep(2);
+ pr_debug(KERN_INFO "_reg_PCMCIA_PGCR = %08x\n", _reg_PCMCIA_PGCR);
+}
+
+static int
+mx31ads_pcmcia_configure_socket(struct mx31ads_pcmcia_socket *skt,
+ const socket_state_t * state)
+{
+ int ret = 0;
+
+ if (state->Vcc != 0 && state->Vcc != 33 && state->Vcc != 50) {
+ pr_debug(KERN_ERR "mx31ads-pcmcia: unrecognized Vcc %d\n",
+ state->Vcc);
+ return -1;
+ }
+
+ pr_debug(KERN_INFO "PIPR = %x, desired Vcc = %d.%dV\n",
+ _reg_PCMCIA_PIPR, state->Vcc / 10, state->Vcc % 10);
+
+ if (!(skt->socket.state & SOCKET_PRESENT) && (skt->pre_stat == 1)) {
+ pr_debug(KERN_INFO "Socket enter low power mode\n");
+ skt->pre_stat = 0;
+ mx31ads_pcmcia_low_power(1);
+ }
+
+ if (state->flags & SS_RESET) {
+ mx31ads_pcmcia_soft_reset();
+
+ /* clean out previous tenant's trash */
+ _reg_PCMCIA_PGSR = (PCMCIA_PGSR_NWINE
+ | PCMCIA_PGSR_LPE
+ | PCMCIA_PGSR_SE
+ | PCMCIA_PGSR_CDE | PCMCIA_PGSR_WPE);
+ }
+ /* enable interrupts if requested, else turn 'em off */
+ if (skt->irq)
+ mx31ads_pcmcia_irq_config();
+ else
+ _reg_PCMCIA_PER = 0;
+
+ if (skt->socket.state & SOCKET_PRESENT) {
+ skt->pre_stat = 1;
+ }
+ return ret;
+}
+
+static void mx31ads_pcmcia_enable_irq(struct mx31ads_pcmcia_socket *skt,
+ unsigned int irq)
+{
+ set_irq_type(irq, IRQF_TRIGGER_RISING);
+ set_irq_type(irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING);
+}
+
+static void mx31ads_pcmcia_disable_irq(struct mx31ads_pcmcia_socket *skt,
+ unsigned int irq)
+{
+ set_irq_type(irq, IRQF_TRIGGER_NONE);
+}
+
+/*
+ * Enable card status IRQs on (re-)initialisation. This can
+ * be called at initialisation, power management event, or
+ * pcmcia event.
+ */
+static void mx31ads_pcmcia_socket_init(struct mx31ads_pcmcia_socket *skt)
+{
+ mx31ads_pcmcia_soft_reset();
+
+ mx31ads_pcmcia_enable_irq(skt, MX31ADS_PCMCIA_IRQ);
+}
+
+/*
+ * Disable card status IRQ on suspend.
+ */
+static void mx31ads_pcmcia_socket_suspend(struct mx31ads_pcmcia_socket *skt)
+{
+ mx31ads_pcmcia_disable_irq(skt, MX31ADS_PCMCIA_IRQ);
+ mx31ads_pcmcia_low_power(1);
+}
+
+/* ==================================================================================== */
+
+/*
+ * PCMCIA strobe hold time
+ */
+static inline u_int mx31ads_pcmcia_por_psht(u_int pcmcia_cycle_ns,
+ u_int hclk_cycle_ns)
+{
+ u_int psht;
+
+ return psht = pcmcia_cycle_ns / hclk_cycle_ns;
+}
+
+/*
+ * PCMCIA strobe set up time
+ */
+static inline u_int mx31ads_pcmcia_por_psst(u_int pcmcia_cycle_ns,
+ u_int hclk_cycle_ns)
+{
+ u_int psst;
+
+ return psst = pcmcia_cycle_ns / hclk_cycle_ns;
+}
+
+/*
+ * PCMCIA strobe length time
+ */
+static inline u_int mx31ads_pcmcia_por_pslt(u_int pcmcia_cycle_ns,
+ u_int hclk_cycle_ns)
+{
+ u_int pslt;
+
+ return pslt = pcmcia_cycle_ns / hclk_cycle_ns + 2;
+}
+
+/*
+ * mx31ads_pcmcia_default_mecr_timing
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * Calculate MECR clock wait states for given CPU clock
+ * speed and command wait state. This function can be over-
+ * written by a board specific version.
+ *
+ * The default is to simply calculate the BS values as specified in
+ * the INTEL SA1100 development manual
+ * "Expansion Memory (PCMCIA) Configuration Register (MECR)"
+ * that's section 10.2.5 in _my_ version of the manual ;)
+ */
+static unsigned int mx31ads_pcmcia_default_mecr_timing(struct
+ mx31ads_pcmcia_socket
+ *skt,
+ unsigned int cpu_speed,
+ unsigned int cmd_time)
+{
+ return 0;
+}
+
+/*
+ * Calculate the timing code
+ */
+static u_int mx31ads_pcmcia_cal_code(u_int speed_ns, u_int clk_ns)
+{
+ u_int code;
+
+ code = PCMCIA_POR_PSHT(mx31ads_pcmcia_por_psht(speed_ns, clk_ns))
+ | PCMCIA_POR_PSST(mx31ads_pcmcia_por_psst(speed_ns, clk_ns))
+ | PCMCIA_POR_PSL(mx31ads_pcmcia_por_pslt(speed_ns, clk_ns));
+
+ return code;
+}
+
+/*
+ * set MECR value for socket <sock> based on this sockets
+ * io, mem and attribute space access speed.
+ * Call board specific BS value calculation to allow boards
+ * to tweak the BS values.
+ */
+static int mx31ads_pcmcia_set_window_timing(u_int speed_ns, u_int window,
+ u_int clk_ns)
+{
+ u_int code = 0;
+
+ switch (window) {
+ case IO_WINDOW:
+ code = mx31ads_pcmcia_cal_code(speed_ns, clk_ns);
+ break;
+ case COMMON_MEMORY_WINDOW:
+ code = mx31ads_pcmcia_cal_code(speed_ns, clk_ns);
+ break;
+ case ATTRIBUTE_MEMORY_WINDOW:
+ code = mx31ads_pcmcia_cal_code(speed_ns, clk_ns);
+ break;
+ default:
+ break;
+ }
+
+ /* Disable the window */
+ _reg_PCMCIA_POR(window) &= ~PCMCIA_POR_PV;
+
+ /* Clear the register fisrt */
+ _reg_PCMCIA_POR(window) &= ~(PCMCIA_POR_PSST_MASK
+ | PCMCIA_POR_PSL_MASK
+ | PCMCIA_POR_PSHT_MASK);
+ /* And then set the register */
+ _reg_PCMCIA_POR(window) |= code;
+
+ /* Enable the window */
+ _reg_PCMCIA_POR(window) |= PCMCIA_POR_PV;
+
+ return 0;
+}
+
+static unsigned short calc_speed(unsigned short *spds, int num,
+ unsigned short dflt)
+{
+ unsigned short speed = 0;
+ int i;
+
+ for (i = 0; i < num; i++)
+ if (speed < spds[i])
+ speed = spds[i];
+ if (speed == 0)
+ speed = dflt;
+
+ return speed;
+}
+
+static void
+mx31ads_common_pcmcia_get_timing(struct mx31ads_pcmcia_socket *skt,
+ struct mx31ads_pcmcia_timing *timing)
+{
+ timing->io = calc_speed(skt->spd_io, MAX_IO_WIN, PCMCIA_IO_ACCESS);
+ timing->mem = calc_speed(skt->spd_mem, MAX_WIN, PCMCIA_3V_MEM_ACCESS);
+ timing->attr =
+ calc_speed(skt->spd_attr, MAX_WIN, PCMCIA_ATTR_MEM_ACCESS);
+}
+
+static int mx31ads_pcmcia_set_timing(struct mx31ads_pcmcia_socket *skt)
+{
+ u_int clk_ns;
+ struct mx31ads_pcmcia_timing timing;
+
+ /* How many nanoseconds */
+ clk_ns = (1000 * 1000 * 1000) / clk_get_rate(skt->clk);
+ pr_debug(KERN_INFO "clk_ns = %d\n", clk_ns);
+
+ mx31ads_common_pcmcia_get_timing(skt, &timing);
+ pr_debug(KERN_INFO "timing: io %d, mem %d, attr %d\n", timing.io,
+ timing.mem, timing.attr);
+
+ mx31ads_pcmcia_set_window_timing(timing.io, IO_WINDOW, clk_ns);
+ mx31ads_pcmcia_set_window_timing(timing.mem, COMMON_MEMORY_WINDOW,
+ clk_ns);
+ mx31ads_pcmcia_set_window_timing(timing.attr, ATTRIBUTE_MEMORY_WINDOW,
+ clk_ns);
+
+ return 0;
+}
+
+static int mx31ads_pcmcia_show_timing(struct mx31ads_pcmcia_socket *skt,
+ char *buf)
+{
+ return 0;
+}
+
+static struct pcmcia_low_level mx31ads_pcmcia_ops = {
+ .owner = THIS_MODULE,
+ .hw_init = mx31ads_pcmcia_hw_init,
+ .hw_shutdown = mx31ads_pcmcia_hw_shutdown,
+ .socket_state = mx31ads_pcmcia_socket_state,
+ .configure_socket = mx31ads_pcmcia_configure_socket,
+
+ .socket_init = mx31ads_pcmcia_socket_init,
+ .socket_suspend = mx31ads_pcmcia_socket_suspend,
+
+ .get_timing = mx31ads_pcmcia_default_mecr_timing,
+ .set_timing = mx31ads_pcmcia_set_timing,
+ .show_timing = mx31ads_pcmcia_show_timing,
+};
+
+/* =================================================================================== */
+
+LIST_HEAD(mx31ads_pcmcia_sockets);
+DECLARE_MUTEX(mx31ads_pcmcia_sockets_lock);
+
+static DEFINE_SPINLOCK(status_lock);
+
+struct bittbl {
+ unsigned int mask;
+ const char *name;
+};
+
+static struct bittbl status_bits[] = {
+ {SS_WRPROT, "SS_WRPROT"},
+ {SS_BATDEAD, "SS_BATDEAD"},
+ {SS_BATWARN, "SS_BATWARN"},
+ {SS_READY, "SS_READY"},
+ {SS_DETECT, "SS_DETECT"},
+ {SS_POWERON, "SS_POWERON"},
+ {SS_STSCHG, "SS_STSCHG"},
+ {SS_3VCARD, "SS_3VCARD"},
+ {SS_XVCARD, "SS_XVCARD"},
+};
+
+static struct bittbl conf_bits[] = {
+ {SS_PWR_AUTO, "SS_PWR_AUTO"},
+ {SS_IOCARD, "SS_IOCARD"},
+ {SS_RESET, "SS_RESET"},
+ {SS_DMA_MODE, "SS_DMA_MODE"},
+ {SS_SPKR_ENA, "SS_SPKR_ENA"},
+ {SS_OUTPUT_ENA, "SS_OUTPUT_ENA"},
+};
+
+static void
+dump_bits(char **p, const char *prefix, unsigned int val, struct bittbl *bits,
+ int sz)
+{
+ char *b = *p;
+ int i;
+
+ b += sprintf(b, "%-9s:", prefix);
+ for (i = 0; i < sz; i++)
+ if (val & bits[i].mask)
+ b += sprintf(b, " %s", bits[i].name);
+ *b++ = '\n';
+ *p = b;
+}
+
+/*
+ * Implements the /sys/class/pcmcia_socket/??/status file.
+ *
+ * Returns: the number of characters added to the buffer
+ */
+static ssize_t show_status(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mx31ads_pcmcia_socket *skt =
+ container_of(dev, struct mx31ads_pcmcia_socket, socket.dev);
+ char *p = buf;
+
+ p += sprintf(p, "slot : %d\n", skt->nr);
+
+ dump_bits(&p, "status", skt->status,
+ status_bits, ARRAY_SIZE(status_bits));
+ dump_bits(&p, "csc_mask", skt->cs_state.csc_mask,
+ status_bits, ARRAY_SIZE(status_bits));
+ dump_bits(&p, "cs_flags", skt->cs_state.flags,
+ conf_bits, ARRAY_SIZE(conf_bits));
+
+ p += sprintf(p, "Vcc : %d\n", skt->cs_state.Vcc);
+ p += sprintf(p, "Vpp : %d\n", skt->cs_state.Vpp);
+ p += sprintf(p, "IRQ : %d (%d)\n", skt->cs_state.io_irq, skt->irq);
+ if (skt->ops->show_timing)
+ p += skt->ops->show_timing(skt, p);
+
+ return p - buf;
+}
+
+static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+static void mx31ads_common_check_status(struct mx31ads_pcmcia_socket *skt)
+{
+ unsigned int events;
+
+ pr_debug(KERN_INFO "entering PCMCIA monitoring thread\n");
+
+ do {
+ unsigned int status;
+ unsigned long flags;
+
+ status = mx31ads_common_pcmcia_skt_state(skt);
+
+ spin_lock_irqsave(&status_lock, flags);
+ events = (status ^ skt->status) & skt->cs_state.csc_mask;
+ skt->status = status;
+ spin_unlock_irqrestore(&status_lock, flags);
+
+ pr_debug(KERN_INFO "events: %s%s%s%s%s%s\n",
+ events == 0 ? "<NONE>" : "",
+ events & SS_DETECT ? "DETECT " : "",
+ events & SS_READY ? "READY " : "",
+ events & SS_BATDEAD ? "BATDEAD " : "",
+ events & SS_BATWARN ? "BATWARN " : "",
+ events & SS_STSCHG ? "STSCHG " : "");
+
+ if (events)
+ pcmcia_parse_events(&skt->socket, events);
+ } while (events);
+}
+
+/*
+ * Service routine for socket driver interrupts (requested by the
+ * low-level PCMCIA init() operation via mx31ads_common_pcmcia_thread()).
+ * The actual interrupt-servicing work is performed by
+ * mx31ads_common_pcmcia_thread(), largely because the Card Services event-
+ * handling code performs scheduling operations which cannot be
+ * executed from within an interrupt context.
+ */
+static irqreturn_t mx31ads_common_pcmcia_interrupt(int irq, void *dev)
+{
+ struct mx31ads_pcmcia_socket *skt = dev;
+ volatile u32 pscr, pgsr;
+
+ dev_dbg(dev, "servicing IRQ %d\n", irq);
+
+ /* clear interrupt states */
+ pscr = _reg_PCMCIA_PSCR;
+ _reg_PCMCIA_PSCR = pscr;
+
+ pgsr = _reg_PCMCIA_PGSR;
+ _reg_PCMCIA_PGSR = pgsr;
+
+ mx31ads_common_check_status(skt);
+
+ return IRQ_HANDLED;
+}
+
+/* Let's poll for events in addition to IRQs since IRQ only is unreliable... */
+static void mx31ads_common_pcmcia_poll_event(unsigned long dummy)
+{
+ struct mx31ads_pcmcia_socket *skt =
+ (struct mx31ads_pcmcia_socket *)dummy;
+ pr_debug(KERN_INFO "polling for events\n");
+
+ mod_timer(&skt->poll_timer, jiffies + PCMCIA_POLL_PERIOD);
+
+ mx31ads_common_check_status(skt);
+}
+
+#define mx31ads_pcmcia_cpufreq_register()
+#define mx31ads_pcmcia_cpufreq_unregister()
+
+static int mx31ads_common_drv_pcmcia_probe(struct platform_device *pdev,
+ struct pcmcia_low_level *ops)
+{
+ struct mx31ads_pcmcia_socket *skt;
+ int vs, value, ret;
+ struct pccard_io_map map;
+
+ down(&mx31ads_pcmcia_sockets_lock);
+
+ skt = kzalloc(sizeof(struct mx31ads_pcmcia_socket), GFP_KERNEL);
+ if (!skt) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Initialise the socket structure.
+ */
+ skt->socket.ops = &mx31ads_common_pcmcia_operations;
+ skt->socket.owner = ops->owner;
+ skt->socket.driver_data = skt;
+
+ init_timer(&skt->poll_timer);
+ skt->poll_timer.function = mx31ads_common_pcmcia_poll_event;
+ skt->poll_timer.data = (unsigned long)skt;
+ skt->poll_timer.expires = jiffies + PCMCIA_POLL_PERIOD;
+
+ skt->irq = MX31ADS_PCMCIA_IRQ;
+ skt->socket.dev.parent = &pdev->dev;
+ skt->ops = ops;
+
+ skt->clk = clk_get(NULL, "ahb_clk");
+
+ skt->res_skt.start = _PCMCIA(0);
+ skt->res_skt.end = _PCMCIA(0) + PCMCIASp - 1;
+ skt->res_skt.name = MX31ADS_PCMCIA;
+ skt->res_skt.flags = IORESOURCE_MEM;
+
+ ret = request_resource(&iomem_resource, &skt->res_skt);
+ if (ret)
+ goto out_err_1;
+
+ skt->res_io.start = _PCMCIAIO(0);
+ skt->res_io.end = _PCMCIAIO(0) + PCMCIAIOSp - 1;
+ skt->res_io.name = "io";
+ skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+
+ ret = request_resource(&skt->res_skt, &skt->res_io);
+ if (ret)
+ goto out_err_2;
+
+ skt->res_mem.start = _PCMCIAMem(0);
+ skt->res_mem.end = _PCMCIAMem(0) + PCMCIAMemSp - 1;
+ skt->res_mem.name = "memory";
+ skt->res_mem.flags = IORESOURCE_MEM;
+
+ ret = request_resource(&skt->res_skt, &skt->res_mem);
+ if (ret)
+ goto out_err_3;
+
+ skt->res_attr.start = _PCMCIAAttr(0);
+ skt->res_attr.end = _PCMCIAAttr(0) + PCMCIAAttrSp - 1;
+ skt->res_attr.name = "attribute";
+ skt->res_attr.flags = IORESOURCE_MEM;
+
+ ret = request_resource(&skt->res_skt, &skt->res_attr);
+ if (ret)
+ goto out_err_4;
+
+ skt->virt_io = ioremap(skt->res_io.start, 0x10000);
+ if (skt->virt_io == NULL) {
+ ret = -ENOMEM;
+ goto out_err_5;
+ }
+
+ if (list_empty(&mx31ads_pcmcia_sockets))
+ mx31ads_pcmcia_cpufreq_register();
+
+ list_add(&skt->node, &mx31ads_pcmcia_sockets);
+
+ /*
+ * We initialize default socket timing here, because
+ * we are not guaranteed to see a SetIOMap operation at
+ * runtime.
+ */
+ ops->set_timing(skt);
+
+ ret = ops->hw_init(skt);
+ if (ret)
+ goto out_err_6;
+
+ ret = request_irq(skt->irq, mx31ads_common_pcmcia_interrupt,
+ IRQF_SHARED | IRQF_DISABLED, "PCMCIA IRQ", skt);
+ if (ret)
+ goto out_err_6;
+
+ skt->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
+ skt->socket.resource_ops = &pccard_static_ops;
+ skt->socket.irq_mask = 0;
+ skt->socket.map_size = PCMCIAPrtSp;
+ skt->socket.pci_irq = skt->irq;
+ skt->socket.io_offset = (unsigned long)skt->virt_io;
+
+ skt->status = mx31ads_common_pcmcia_skt_state(skt);
+ skt->pre_stat = 0;
+ ret = pcmcia_register_socket(&skt->socket);
+ if (ret)
+ goto out_err_7;
+ /* FIXED ME workaround for binding with ide-cs. ide usage io port 0x100~0x107 and 0x10e */
+ map.map = 0;
+ map.flags = MAP_ACTIVE | MAP_16BIT;
+ map.start = 0;
+ map.stop = PCMCIAIOSp - 1;
+ map.speed = 0;
+ mx31ads_common_pcmcia_set_io_map(&skt->socket, &map);
+
+ vs = _reg_PCMCIA_PIPR & PCMCIA_PIPR_VS;
+ value = vs & PCMCIA_PIPR_VS_5V ? 50 : 33;
+ dev_dbg(&pdev->dev, "PCMCIA: Voltage the card supports: %d.%dV\n",
+ value / 10, value % 10);
+
+ add_timer(&skt->poll_timer);
+
+ ret = device_create_file(&skt->socket.dev, &dev_attr_status);
+ if (ret < 0)
+ goto out_err_8;
+
+ platform_set_drvdata(pdev, skt);
+ ret = 0;
+ goto out;
+
+ out_err_8:
+ del_timer_sync(&skt->poll_timer);
+ pcmcia_unregister_socket(&skt->socket);
+
+ out_err_7:
+ flush_scheduled_work();
+ free_irq(skt->irq, skt);
+ ops->hw_shutdown(skt);
+ out_err_6:
+ list_del(&skt->node);
+ iounmap(skt->virt_io);
+ out_err_5:
+ release_resource(&skt->res_attr);
+ out_err_4:
+ release_resource(&skt->res_mem);
+ out_err_3:
+ release_resource(&skt->res_io);
+ out_err_2:
+ release_resource(&skt->res_skt);
+ out_err_1:
+
+ kfree(skt);
+ out:
+ up(&mx31ads_pcmcia_sockets_lock);
+ return ret;
+}
+
+static int mx31ads_drv_pcmcia_remove(struct platform_device *pdev)
+{
+ struct mx31ads_pcmcia_socket *skt = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ down(&mx31ads_pcmcia_sockets_lock);
+
+ del_timer_sync(&skt->poll_timer);
+
+ pcmcia_unregister_socket(&skt->socket);
+
+ flush_scheduled_work();
+
+ skt->ops->hw_shutdown(skt);
+
+ mx31ads_common_pcmcia_config_skt(skt, &dead_socket);
+
+ list_del(&skt->node);
+ iounmap(skt->virt_io);
+ skt->virt_io = NULL;
+ release_resource(&skt->res_attr);
+ release_resource(&skt->res_mem);
+ release_resource(&skt->res_io);
+ release_resource(&skt->res_skt);
+
+ if (list_empty(&mx31ads_pcmcia_sockets))
+ mx31ads_pcmcia_cpufreq_unregister();
+
+ up(&mx31ads_pcmcia_sockets_lock);
+
+ kfree(skt);
+
+ return 0;
+}
+
+static int mx31ads_drv_pcmcia_probe(struct platform_device *pdev)
+{
+ if (!machine_is_mx31ads())
+ return -ENODEV;
+
+ return mx31ads_common_drv_pcmcia_probe(pdev, &mx31ads_pcmcia_ops);
+}
+
+static int mx31ads_drv_pcmcia_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return pcmcia_socket_dev_suspend(&pdev->dev, state);
+}
+
+static int mx31ads_drv_pcmcia_resume(struct platform_device *pdev)
+{
+ return pcmcia_socket_dev_resume(&pdev->dev);
+}
+
+/*
+ * Low level functions
+ */
+static struct platform_driver mx31ads_pcmcia_driver = {
+ .driver = {
+ .name = MX31ADS_PCMCIA,
+ },
+ .probe = mx31ads_drv_pcmcia_probe,
+ .remove = mx31ads_drv_pcmcia_remove,
+ .suspend = mx31ads_drv_pcmcia_suspend,
+ .resume = mx31ads_drv_pcmcia_resume,
+};
+
+/* mx31ads_pcmcia_init()
+ *
+ */
+static int __init mx31ads_pcmcia_init(void)
+{
+ int ret;
+
+ if ((ret = platform_driver_register(&mx31ads_pcmcia_driver)))
+ return ret;
+ pr_debug(KERN_INFO "PCMCIA: Initialize i.Mx31 pcmcia socket\n");
+
+ return ret;
+}
+
+/* mx31ads_pcmcia_exit()
+ *
+ */
+static void __exit mx31ads_pcmcia_exit(void)
+{
+ platform_driver_unregister(&mx31ads_pcmcia_driver);
+}
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX31 PCMCIA Socket Controller");
+MODULE_LICENSE("GPL");
+
+module_init(mx31ads_pcmcia_init);
+module_exit(mx31ads_pcmcia_exit);
diff --git a/drivers/pcmcia/mx31ads-pcmcia.h b/drivers/pcmcia/mx31ads-pcmcia.h
new file mode 100644
index 000000000000..3f7014b3e8bf
--- /dev/null
+++ b/drivers/pcmcia/mx31ads-pcmcia.h
@@ -0,0 +1,155 @@
+/*
+ * linux/drivers/pcmcia/mx31ads-pcmcia.h
+ *
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This file contains definitions for the PCMCIA support code common to
+ * integrated SOCs like the i.Mx31 microprocessors.
+ */
+#ifndef _ASM_ARCH_PCMCIA
+#define _ASM_ARCH_PCMCIA
+
+/* include the world */
+#include <linux/cpufreq.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+
+#define MX31ADS_PCMCIA "Mx31ads_pcmcia_socket"
+
+struct device;
+struct pcmcia_low_level;
+
+/*
+ * This structure encapsulates per-socket state which we might need to
+ * use when responding to a Card Services query of some kind.
+ */
+struct mx31ads_pcmcia_socket {
+ struct pcmcia_socket socket;
+
+ /*
+ * Info from low level handler
+ */
+ struct device *dev;
+ unsigned int nr;
+ unsigned int irq;
+
+ struct clk *clk;
+
+ /*
+ * Core PCMCIA state
+ */
+ struct pcmcia_low_level *ops;
+
+ unsigned int status;
+ unsigned int pre_stat;
+ socket_state_t cs_state;
+
+ unsigned short spd_io[MAX_IO_WIN];
+ unsigned short spd_mem[MAX_WIN];
+ unsigned short spd_attr[MAX_WIN];
+
+ struct resource res_skt;
+ struct resource res_io;
+ struct resource res_mem;
+ struct resource res_attr;
+ void *virt_io;
+
+ unsigned int irq_state;
+
+ struct timer_list poll_timer;
+ struct list_head node;
+};
+
+struct pcmcia_state {
+ unsigned detect:1,
+ ready:1, bvd1:1, bvd2:1, wrprot:1, vs_3v:1, vs_Xv:1, poweron:1;
+};
+
+struct pcmcia_low_level {
+ struct module *owner;
+
+ /* first socket in system */
+ int first;
+ /* nr of sockets */
+ int nr;
+
+ int (*hw_init) (struct mx31ads_pcmcia_socket *);
+ void (*hw_shutdown) (struct mx31ads_pcmcia_socket *);
+
+ void (*socket_state) (struct mx31ads_pcmcia_socket *,
+ struct pcmcia_state *);
+ int (*configure_socket) (struct mx31ads_pcmcia_socket *,
+ const socket_state_t *);
+
+ /*
+ * Enable card status IRQs on (re-)initialisation. This can
+ * be called at initialisation, power management event, or
+ * pcmcia event.
+ */
+ void (*socket_init) (struct mx31ads_pcmcia_socket *);
+
+ /*
+ * Disable card status IRQs and PCMCIA bus on suspend.
+ */
+ void (*socket_suspend) (struct mx31ads_pcmcia_socket *);
+
+ /*
+ * Hardware specific timing routines.
+ * If provided, the get_timing routine overrides the SOC default.
+ */
+ unsigned int (*get_timing) (struct mx31ads_pcmcia_socket *,
+ unsigned int, unsigned int);
+ int (*set_timing) (struct mx31ads_pcmcia_socket *);
+ int (*show_timing) (struct mx31ads_pcmcia_socket *, char *);
+
+#ifdef CONFIG_CPU_FREQ
+ /*
+ * CPUFREQ support.
+ */
+ int (*frequency_change) (struct mx31ads_pcmcia_socket *, unsigned long,
+ struct cpufreq_freqs *);
+#endif
+};
+
+struct mx31ads_pcmcia_timing {
+ unsigned short io;
+ unsigned short mem;
+ unsigned short attr;
+};
+
+typedef struct {
+ ulong win_size;
+ int bsize;
+} bsize_map_t;
+
+/*
+ * The PC Card Standard, Release 7, section 4.13.4, says that twIORD
+ * has a minimum value of 165ns. Section 4.13.5 says that twIOWR has
+ * a minimum value of 165ns, as well. Section 4.7.2 (describing
+ * common and attribute memory write timing) says that twWE has a
+ * minimum value of 150ns for a 250ns cycle time (for 5V operation;
+ * see section 4.7.4), or 300ns for a 600ns cycle time (for 3.3V
+ * operation, also section 4.7.4). Section 4.7.3 says that taOE
+ * has a maximum value of 150ns for a 300ns cycle time (for 5V
+ * operation), or 300ns for a 600ns cycle time (for 3.3V operation).
+ *
+ * When configuring memory maps, Card Services appears to adopt the policy
+ * that a memory access time of "0" means "use the default." The default
+ * PCMCIA I/O command width time is 165ns. The default PCMCIA 5V attribute
+ * and memory command width time is 150ns; the PCMCIA 3.3V attribute and
+ * memory command width time is 300ns.
+ */
+#define PCMCIA_IO_ACCESS (165)
+#define PCMCIA_5V_MEM_ACCESS (150)
+#define PCMCIA_3V_MEM_ACCESS (300)
+#define PCMCIA_ATTR_MEM_ACCESS (300)
+
+/*
+ * The socket driver actually works nicely in interrupt-driven form,
+ * so the (relatively infrequent) polling is "just to be sure."
+ */
+#define PCMCIA_POLL_PERIOD (2*HZ)
+#endif
diff --git a/drivers/pcmcia/s3c2443_pcmcia.c b/drivers/pcmcia/s3c2443_pcmcia.c
new file mode 100755
index 000000000000..4ee321bf0e78
--- /dev/null
+++ b/drivers/pcmcia/s3c2443_pcmcia.c
@@ -0,0 +1,491 @@
+/* linux/drivers/pcmcia/s3c2443_pcmcia.c
+ *
+ * Copyright (c) 2009 Digi Internationa Inc.
+ * http://www.digi.com
+ *
+ * Based on similar driver for other ARM SoC, like the at91_cf.c, from
+ * David Brownell and other authors.
+ *
+ * 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/cpufreq.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cistpl.h>
+
+#include <plat/pcmcia.h>
+#include <mach/regs-cfata.h>
+#include <mach/regs-bus.h>
+#include <mach/gpio.h>
+
+#define DRV_NAME "s3c2443-pcmcia"
+#define SZ_2K (2 * SZ_1K)
+
+struct s3c2443_pcmcia_socket {
+ struct pcmcia_socket socket;
+ struct platform_device *pdev;
+ struct s3c2443_pcmcia_pdata *pdata;
+ unsigned present:1;
+ int irq;
+ int irq_cd;
+ unsigned long phys_io;
+ unsigned long phys_attr;
+ unsigned long phys_comm;
+ void __iomem *pcmcia_base;
+};
+
+static inline int s3c2443_pcmcia_present(struct s3c2443_pcmcia_socket *psock)
+{
+ return s3c2410_gpio_getpin(psock->pdata->gpio_detect) ? 0 : 1;
+}
+
+static int s3c2443_pcmcia_socket_init(struct pcmcia_socket *s)
+{
+ return 0;
+}
+
+static irqreturn_t s3c2443_pcmcia_irq_cd(int irq, void *data)
+{
+ struct s3c2443_pcmcia_socket *psock = data;
+ unsigned present;
+
+ present = s3c2443_pcmcia_present(psock);
+ if (present != psock->present) {
+ psock->present = present;
+ pr_debug("%s: card %s\n", DRV_NAME,
+ present ? "present" : "gone");
+
+ pcmcia_parse_events(&psock->socket, SS_DETECT);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t s3c2443_pcmcia_irq(int irq, void *data)
+{
+ struct s3c2443_pcmcia_socket *psock = data;
+ irqreturn_t ret = IRQ_NONE;
+ u32 irqreg;
+
+ /* read interrupt sources and acknowledge irqs */
+ irqreg = readl(psock->pcmcia_base + S3C2443_PCCARD_INT);
+ writel(irqreg, psock->pcmcia_base + S3C2443_PCCARD_INT);
+
+ /* Mask before checking the interrupt source */
+ irqreg = irqreg & ~(irqreg >> 8) & 0x07;
+
+ if (irqreg) {
+ ret = IRQ_HANDLED;
+
+ if (irqreg & S3C2443_PCC_INTSRC_IREQ)
+ pcmcia_parse_events(&psock->socket, SS_STSCHG);
+ if (irqreg & S3C2443_PCC_INTSRC_ERR_N)
+ printk(KERN_WARNING "%s: %s, error interrupt\n",
+ DRV_NAME, __func__);
+ if (irqreg & S3C2443_PCC_INTSRC_CD)
+ printk(KERN_WARNING "%s: %s, card detect irq ???\n",
+ DRV_NAME, __func__);
+ }
+
+ return ret;
+}
+
+static int s3c2443_pcmcia_get_status(struct pcmcia_socket *s, u_int *sp)
+{
+ struct s3c2443_pcmcia_socket *psock;
+ u32 muxreg;
+
+ if (!sp)
+ return -EINVAL;
+
+ psock = container_of(s, struct s3c2443_pcmcia_socket, socket);
+
+ /* NOTE: CF is always 3VCARD */
+ if (s3c2443_pcmcia_present(psock)) {
+ *sp = SS_READY | SS_DETECT | SS_3VCARD;
+
+ muxreg = readl(psock->pcmcia_base + S3C2443_MUX_REG);
+ if (!(muxreg & S3C2443_MUX_PWREN_PWOFF))
+ *sp |= SS_POWERON;
+
+ s->irq.AssignedIRQ = 0;
+ s->pci_irq = psock->irq;
+ } else
+ *sp = 0;
+
+ return 0;
+}
+
+static int
+s3c2443_pcmcia_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
+{
+ struct s3c2443_pcmcia_socket *psock =
+ container_of(sock, struct s3c2443_pcmcia_socket, socket);
+ u32 muxreg;
+ u32 cfg;
+
+ muxreg = readl(psock->pcmcia_base + S3C2443_MUX_REG);
+ muxreg &= ~S3C2443_MUX_PWREN_PWOFF;
+ cfg = readl(psock->pcmcia_base + S3C2443_PCCARD_CFG);
+ cfg &= ~S3C2443_PCC_CARD_RESET;
+
+ switch (s->Vcc) {
+ case 0:
+ writel(muxreg | S3C2443_MUX_PWREN_PWOFF,
+ psock->pcmcia_base + S3C2443_MUX_REG);
+ break;
+ case 33:
+ writel(muxreg | S3C2443_MUX_PWREN_PWON,
+ psock->pcmcia_base + S3C2443_MUX_REG);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (s->flags & SS_RESET)
+ cfg |= S3C2443_PCC_CARD_RESET;
+
+ writel(cfg, psock->pcmcia_base + S3C2443_PCCARD_CFG);
+ pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n",
+ DRV_NAME, s->Vcc, s->io_irq, s->flags, s->csc_mask);
+
+ return 0;
+}
+
+static int s3c2443_pcmcia_suspend(struct pcmcia_socket *s)
+{
+ return s3c2443_pcmcia_set_socket(s, &dead_socket);
+}
+
+/* we already mapped the I/O region */
+static int s3c2443_pcmcia_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
+{
+ struct s3c2443_pcmcia_socket *psock;
+ u32 cfg;
+
+ psock = container_of(s, struct s3c2443_pcmcia_socket, socket);
+ io->flags &= MAP_ACTIVE | MAP_16BIT | MAP_AUTOSZ;
+
+ /* Use 16 bit accesses */
+ cfg = readl(psock->pcmcia_base + S3C2443_PCCARD_CFG);
+ cfg |= S3C2443_PCC_DEVICE_IO_16;
+ writel(cfg, psock->pcmcia_base + S3C2443_PCCARD_CFG);
+
+ io->start = psock->socket.io_offset;
+ io->stop = io->start + SZ_2K - 1;
+
+ return 0;
+}
+
+/* pcmcia layer maps/unmaps mem regions */
+static int
+s3c2443_pcmcia_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
+{
+ struct s3c2443_pcmcia_socket *psock;
+
+ if (map->card_start)
+ return -EINVAL;
+
+ psock = container_of(s, struct s3c2443_pcmcia_socket, socket);
+
+ map->flags &= (MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT);
+ map->static_start = (map->flags & MAP_ATTRIB) ?
+ psock->phys_attr : psock->phys_comm;
+
+ return 0;
+}
+
+static struct pccard_operations s3c2443_pcmcia_ops = {
+ .init = s3c2443_pcmcia_socket_init,
+ .suspend = s3c2443_pcmcia_suspend,
+ .get_status = s3c2443_pcmcia_get_status,
+ .set_socket = s3c2443_pcmcia_set_socket,
+ .set_io_map = s3c2443_pcmcia_set_io_map,
+ .set_mem_map = s3c2443_pcmcia_set_mem_map,
+};
+
+
+static int s3c2443_pcmcia_hw_config(struct s3c2443_pcmcia_socket *psock)
+{
+ void __iomem *ebicon;
+ int ret = 0;
+ u32 reg;
+
+ ebicon = ioremap(S3C2443_PA_EBI + 0x8, 0x4);
+ if (ebicon == NULL) {
+ ret =-EBUSY;
+ goto err_unmap;
+ }
+
+ /* Configure EBI bank 2 and 3 for the CF interface */
+ reg = readl(ebicon);
+ reg |= S3C2443_EBICON_BANK3_CFG | S3C2443_EBICON_BANK2_CFG;
+ writel(reg, ebicon);
+
+ /* Configure the IO lines*/
+ s3c2443_gpio_cfgpin(S3C2410_GPG15, S3C2443_GPG15_CF_PWR);
+ s3c2443_gpio_cfgpin(S3C2410_GPG14, S3C2443_GPG14_CF_RESET);
+ s3c2443_gpio_cfgpin(S3C2410_GPG13, S3C2443_GPG13_CF_nREG);
+ s3c2443_gpio_cfgpin(S3C2410_GPG12, S3C2443_GPG12_nINPACK);
+ s3c2443_gpio_cfgpin(S3C2410_GPG11, S3C2443_GPG11_CF_nIREQ);
+
+ s3c2443_gpio_cfgpin(S3C2410_GPA10, 0);
+ s3c2443_gpio_cfgpin(S3C2410_GPA11, 1);
+ s3c2443_gpio_cfgpin(S3C2410_GPA12, 1);
+ s3c2443_gpio_cfgpin(S3C2410_GPA15, 1);
+
+ /* TODO use the proper api after adding s3c2443_gpio_pullup */
+ /* Enable the pullup for interrupt line */
+ reg = readl(S3C2410_GPGUP);
+ writel(reg & ~(3 << 22), S3C2410_GPGUP);
+
+ /* Clear the card detect condition */
+ reg = readl(S3C24XX_MISCCR) & ~S3C2443_MISCCR_nCD_CF;
+ writel(reg, S3C24XX_MISCCR);
+
+ /* Output Port enabled, Card power off, PCCARD mode */
+ writel(S3C2443_MUX_OUTPUT_ENABLE | S3C2443_MUX_PWREN_PWOFF |
+ S3C2443_MUX_MODE_PCCARD, psock->pcmcia_base + S3C2443_MUX_REG);
+
+ /* Acknowledge any pending interrupt and mask all irqs to get started */
+ writel(S3C2443_PCC_INTSRC_ALL, psock->pcmcia_base + S3C2443_PCCARD_INT);
+ writel(S3C2443_PCC_INTMSK_ALL, psock->pcmcia_base + S3C2443_PCCARD_INT);
+
+ writel(S3C2443_PCC_INTMSK_CD | S3C2443_PCC_INTSRC_ALL,
+ psock->pcmcia_base + S3C2443_PCCARD_INT);
+
+ /* Configure timmings for IO, ATTR and COMM areas */
+ writel(0x061D04, psock->pcmcia_base + S3C2443_PCCARD_ATTR);
+ writel(0x031109, psock->pcmcia_base + S3C2443_PCCARD_IO);
+ writel(0x061D04, psock->pcmcia_base + S3C2443_PCCARD_COMM);
+
+err_unmap:
+ iounmap(ebicon);
+ return ret;
+}
+
+static int __init s3c2443_pcmcia_probe(struct platform_device *pdev)
+{
+ struct s3c2443_pcmcia_socket *psock;
+ struct s3c2443_pcmcia_pdata *pdata;
+ struct resource *res;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+
+ if (!pdata || !pdata->gpio_detect) {
+ pr_debug("%s: %s, platform data not available\n", DRV_NAME, __func__);
+ ret = -ENODEV;
+ goto error;
+ }
+
+ psock = kzalloc(sizeof(struct s3c2443_pcmcia_socket), GFP_KERNEL);
+ if (psock == NULL) {
+ pr_debug("%s: %s, can not allocate memory\n", DRV_NAME, __func__);
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ psock->irq = platform_get_irq(pdev, 0);
+ if (psock->irq < 0) {
+ pr_debug("%s: %s, can not get IORESOURCE_IRQ\n", DRV_NAME, __func__);
+ ret =-ENOENT;
+ goto error_get_irq;
+ }
+ psock->socket.pci_irq = psock->irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ pr_debug("%s: %s, can not get IORESOURCE_MEM\n", DRV_NAME, __func__);
+ ret = -ENOENT;
+ goto error_get_irq;
+ }
+
+ psock->pdata = pdata;
+ psock->pdev = pdev;
+ platform_set_drvdata(pdev, psock);
+ psock->phys_io = res->start + S3C2443_IO_BASE;
+ psock->phys_attr = res->start + S3C2443_ATTR_BASE;
+
+ if (!request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name)) {
+ pr_debug("%s: %s,request_mem_region failed\n", DRV_NAME, __func__);
+ ret = -EBUSY;
+ goto error_get_irq;
+ }
+
+ psock->socket.io_offset = (unsigned long)ioremap(psock->phys_io, SZ_2K);
+ if (!psock->socket.io_offset) {
+ pr_debug("%s: %s, can not map IO space at 0x%08x\n",
+ DRV_NAME, __func__, (unsigned int)psock->phys_io);
+ ret = -ENXIO;
+ goto error_map;
+ }
+
+ psock->pcmcia_base = ioremap(res->start, res->end - res->start + 1);
+ if (!psock->pcmcia_base) {
+ pr_debug("%s: %s, can not map IO space at 0x%08x\n",
+ DRV_NAME, __func__, (unsigned int)(res->start + S3C2443_MUX_REG));
+ ret = -ENXIO;
+ goto error_map2;
+ }
+
+ ret = s3c2443_pcmcia_hw_config(psock);
+ if (ret) {
+ pr_debug("%s: %s, failed to configure PCMCIA hardware\n", DRV_NAME, __func__);
+ goto error_hwcfg;
+ }
+
+ psock->irq_cd = s3c2410_gpio_getirq(pdata->gpio_detect);
+ if (psock->irq_cd < 0) {
+ pr_debug("%s: %s, not irq available for card detection\n",
+ DRV_NAME, __func__);
+ ret = -ENOENT;
+ goto error_irq_cd;
+ }
+
+ ret = request_irq(psock->irq_cd, s3c2443_pcmcia_irq_cd,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DRV_NAME, psock);
+ if (ret < 0) {
+ pr_debug("%s: %s, can not request irq %d (ret %d)\n",
+ DRV_NAME, __func__, psock->irq_cd, ret);
+ ret = -EBUSY;
+ goto error_irq_cd;
+ }
+
+ ret = request_irq(psock->irq, s3c2443_pcmcia_irq,
+ IRQF_SHARED, DRV_NAME, psock);
+ if (ret < 0) {
+ pr_debug("%s: %s, can not request irq %d (ret %d)\n",
+ DRV_NAME, __func__, psock->irq, ret);
+ ret = -EBUSY;
+ goto error_req_irq;
+ }
+
+ psock->socket.owner = THIS_MODULE;
+ psock->socket.dev.parent = &pdev->dev;
+ psock->socket.ops = &s3c2443_pcmcia_ops;
+ psock->socket.resource_ops = &pccard_static_ops;
+ psock->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
+ | SS_CAP_MEM_ALIGN;
+ psock->socket.map_size = SZ_2K;
+ psock->socket.io[0].res = res;
+
+
+ ret = pcmcia_register_socket(&psock->socket);
+ if (ret < 0) {
+ pr_debug("%s: %s, can not register pcmcia socket\n",
+ DRV_NAME, __func__);
+ goto error_reqister;
+ }
+
+ pr_info("%s: PCMCIA driver\n", DRV_NAME);
+ return 0;
+
+error_reqister:
+ free_irq(psock->irq, psock);
+error_req_irq:
+ free_irq(psock->irq_cd, psock);
+error_irq_cd:
+error_hwcfg:
+ iounmap(psock->pcmcia_base);
+error_map2:
+ iounmap((void __iomem *)psock->socket.io_offset);
+error_map:
+ release_mem_region(res->start, res->end - res->start + 1);
+error_get_irq:
+ kfree(psock);
+error:
+ return ret;
+}
+
+static int __exit s3c2443_pcmcia_remove(struct platform_device *pdev)
+{
+ struct s3c2443_pcmcia_socket *psock = platform_get_drvdata(pdev);
+ struct s3c2443_pcmcia_pdata *pdata = psock->pdata;
+ struct resource *res = psock->socket.io[0].res;
+
+ pcmcia_unregister_socket(&psock->socket);
+ iounmap((void __iomem *)psock->socket.io_offset);
+ iounmap(psock->pcmcia_base);
+ release_mem_region(res->start, res->end - res->start + 1);
+ free_irq(psock->irq, psock);
+ device_init_wakeup(&pdev->dev, 0);
+
+ if (pdata->gpio_detect) {
+ free_irq(psock->irq_cd, psock);
+ gpio_free(pdata->gpio_detect);
+ }
+ kfree(psock);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int
+s3c2443_pcmcia_drv_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ return pcmcia_socket_dev_suspend(&pdev->dev, mesg);
+}
+
+static int
+s3c2443_pcmcia_drv_resume(struct platform_device *pdev)
+{
+ struct s3c2443_pcmcia_socket *psock = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = s3c2443_pcmcia_hw_config(psock);
+ if (ret)
+ return ret;
+
+ return pcmcia_socket_dev_resume(&pdev->dev);
+}
+#else
+#define s3c2443_pcmcia_drv_suspend NULL
+#define s3c2443_pcmcia_drv_resume NULL
+#endif
+
+static struct platform_driver s3c2443_pcmcia_driver = {
+ .probe = s3c2443_pcmcia_probe,
+ .remove = __exit_p(s3c2443_pcmcia_remove),
+ .suspend = s3c2443_pcmcia_drv_suspend,
+ .resume = s3c2443_pcmcia_drv_resume,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s3c2443_pcmcia_init(void)
+{
+ return platform_driver_register(&s3c2443_pcmcia_driver);
+}
+
+static void __exit s3c2443_pcmcia_exit(void)
+{
+ platform_driver_unregister(&s3c2443_pcmcia_driver);
+}
+
+module_init(s3c2443_pcmcia_init);
+module_exit(s3c2443_pcmcia_exit);
+
+MODULE_AUTHOR("Pedro Perez de Heredia");
+MODULE_DESCRIPTION("S3C2443 PCMCIA core socket driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:s3c2443-pcmcia");
+
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 8e0c2b47803c..f1eb6b3f9cab 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -68,4 +68,11 @@ config BATTERY_BQ27x00
help
Say Y here to enable support for batteries with BQ27200(I2C) chip.
+config BATTERY_STMP3XXX
+ tristate "Sigmatel STMP3xxx SoC battery charger driver"
+ depends on ARCH_STMP3XXX
+ help
+ Say Y to enable support for the battery charger state machine
+ for the Sigmatel STMP3xxx based SoC's.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index e8f1ecec5d8f..588e7d70f20f 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
+obj-$(CONFIG_BATTERY_STMP3XXX) += stmp37xx/
diff --git a/drivers/power/stmp37xx/Makefile b/drivers/power/stmp37xx/Makefile
new file mode 100644
index 000000000000..0d4f16e78ddf
--- /dev/null
+++ b/drivers/power/stmp37xx/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the STMP3xxx battery charger driver
+#
+
+obj-$(CONFIG_BATTERY_STMP3XXX) += stmp3xxx-battery.o
+
+stmp3xxx-battery-objs := ddi_bc_api.o ddi_bc_hw.o ddi_bc_init.o \
+ ddi_bc_ramp.o ddi_bc_sm.o ddi_power_battery.o linux.o
+
diff --git a/drivers/power/stmp37xx/ddi_bc_api.c b/drivers/power/stmp37xx/ddi_bc_api.c
new file mode 100644
index 000000000000..5fd062ec4eb9
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_bc_api.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_bc
+//! @{
+//
+// Copyright (c) 2004-2005 SigmaTel, Inc.
+//
+//! \file ddi_bc_api.c
+//! \brief Contains the Battery Charger API.
+//! \date 06/2005
+//!
+//! This file contains Battery Charger API.
+//!
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Includes
+////////////////////////////////////////////////////////////////////////////////
+
+#include "ddi_bc_internal.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Variables
+////////////////////////////////////////////////////////////////////////////////
+
+//! This structure holds the current Battery Charger configuration.
+
+ddi_bc_Cfg_t g_ddi_bc_Configuration;
+
+extern uint32_t g_ddi_bc_u32StateTimer;
+extern ddi_bc_BrokenReason_t ddi_bc_gBrokenReason;
+extern bool bRestartChargeCycle;
+
+////////////////////////////////////////////////////////////////////////////////
+// Code
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the Battery Charger configuration.
+//!
+//! \fntype Function
+//!
+//! This function reports the Battery Charger configuration.
+//!
+//! Note that, if the Battery Charger has not yet been initialized, the data
+//! returned by this function is unknown.
+//!
+//! \param[in,out] pCfg A pointer to a structure that will receive the data.
+//!
+////////////////////////////////////////////////////////////////////////////////
+void ddi_bc_QueryCfg(ddi_bc_Cfg_t * pCfg)
+{
+
+ //--------------------------------------------------------------------------
+ // Return the current configuration.
+ //--------------------------------------------------------------------------
+
+ *pCfg = g_ddi_bc_Configuration;
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Shut down the Battery Charger.
+//!
+//! \fntype Function
+//!
+//! This function immediately shuts down the Battery Charger hardware and
+//! returns the state machine to the Uninitialized state. Use this function to
+//! safely “mummify” the battery charger before retiring it from memory.
+//!
+////////////////////////////////////////////////////////////////////////////////
+void ddi_bc_ShutDown()
+{
+
+ //--------------------------------------------------------------------------
+ // Reset the current ramp.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampReset();
+
+ //--------------------------------------------------------------------------
+ // Move to the Uninitialized state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_UNINITIALIZED;
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Advances the state machine.
+//!
+//! \fntype Function
+//!
+//! This function advances the state machine.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If all goes well
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//! \retval DDI_BC_STATUS_BROKEN If the battery violated a time-out
+//! and has been declared broken.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_StateMachine()
+{
+
+ //--------------------------------------------------------------------------
+ // Check if we've been initialized yet.
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) {
+ return (DDI_BC_STATUS_NOT_INITIALIZED);
+ }
+ //--------------------------------------------------------------------------
+ // Execute the function for the current state.
+ //--------------------------------------------------------------------------
+
+ return (stateFunctionTable[g_ddi_bc_State] ());
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Get the Battery Charger's current state.
+//!
+//! \fntype Function
+//!
+//! This function returns the current state.
+//!
+//! \retval The current state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_State_t ddi_bc_GetState()
+{
+ //--------------------------------------------------------------------------
+ // Return the current state.
+ //--------------------------------------------------------------------------
+
+ return (g_ddi_bc_State);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Disable the Battery Charger.
+//!
+//! \fntype Function
+//!
+//! This function forces the Battery Charger into the Disabled state.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If all goes well
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_SetDisable()
+{
+
+ //--------------------------------------------------------------------------
+ // Check if we've been initialized yet.
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) {
+ return (DDI_BC_STATUS_NOT_INITIALIZED);
+ }
+ //--------------------------------------------------------------------------
+ // Check if we've been initialized yet.
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_State == DDI_BC_STATE_BROKEN) {
+ return (DDI_BC_STATUS_BROKEN);
+ }
+ //--------------------------------------------------------------------------
+ // Reset the current ramp. This will jam the current to zero and power off
+ // the charging hardware.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampReset();
+
+ //--------------------------------------------------------------------------
+ // Reset the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer = 0;
+
+ //--------------------------------------------------------------------------
+ // Move to the Disabled state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_DISABLED;
+
+ //--------------------------------------------------------------------------
+ // Return success.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Enable the Battery Charger.
+//!
+//! \fntype Function
+//!
+//! If the Battery Charger is in the Disabled state, this function moves it to
+//! the Waiting to Charge state.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If all goes well
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//! \retval DDI_BC_STATUS_NOT_DISABLED If the Battery Charger is not
+//! disabled.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_SetEnable()
+{
+
+ //--------------------------------------------------------------------------
+ // Check if we've been initialized yet.
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) {
+ return (DDI_BC_STATUS_NOT_INITIALIZED);
+ }
+ //--------------------------------------------------------------------------
+ // If we're not in the Disabled state, this is pointless.
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_State != DDI_BC_STATE_DISABLED) {
+ return (DDI_BC_STATUS_NOT_DISABLED);
+ }
+ //--------------------------------------------------------------------------
+ // Reset the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer = 0;
+ //--------------------------------------------------------------------------
+ // Move to the Waiting to Charge state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_WAITING_TO_CHARGE;
+
+ //--------------------------------------------------------------------------
+ // Return success.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Declare the battery to be broken.
+//!
+//! \fntype Function
+//!
+//! This function forces the Battery Charger into the Broken state.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If all goes well
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_SetBroken()
+{
+
+ //--------------------------------------------------------------------------
+ // Check if we've been initialized yet.
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) {
+ return (DDI_BC_STATUS_NOT_INITIALIZED);
+ }
+ //--------------------------------------------------------------------------
+ // Reset the current ramp. This will jam the current to zero and power off
+ // the charging hardware.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampReset();
+
+ //--------------------------------------------------------------------------
+ // Reset the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer = 0;
+
+ //--------------------------------------------------------------------------
+ // Move to the Broken state.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_gBrokenReason = DDI_BC_BROKEN_CHARGING_TIMEOUT;
+
+ g_ddi_bc_State = DDI_BC_STATE_BROKEN;
+
+ //--------------------------------------------------------------------------
+ // Return success.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Declare the battery to be fixed.
+//!
+//! \fntype Function
+//!
+//! If the Battery Charger is in the Broken state, this function moves it to
+//! the Disabled state.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If all goes well
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//! \retval DDI_BC_STATUS_NOT_BROKEN If the Battery Charger is not broken.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_SetFixed()
+{
+
+ //--------------------------------------------------------------------------
+ // Check if we've been initialized yet.
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) {
+ return (DDI_BC_STATUS_NOT_INITIALIZED);
+ }
+ //--------------------------------------------------------------------------
+ // If we're not in the Broken state, this is pointless.
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_State != DDI_BC_STATE_BROKEN) {
+ return (DDI_BC_STATUS_NOT_BROKEN);
+ }
+ //--------------------------------------------------------------------------
+ // Reset the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer = 0;
+
+ //--------------------------------------------------------------------------
+ // Unitialize the Broken Reason
+ //--------------------------------------------------------------------------
+
+ ddi_bc_gBrokenReason = DDI_BC_BROKEN_UNINITIALIZED;
+
+ //--------------------------------------------------------------------------
+ // Move to the Disabled state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_DISABLED;
+
+ //--------------------------------------------------------------------------
+ // Return success.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Set the current limit.
+//!
+//! \fntype Function
+//!
+//! This function applies a limit to the current that the Battery Charger can
+//! draw.
+//!
+//! \param[in] u16Limit The maximum current the Battery Charger can draw
+//! (in mA).
+//!
+//! \retval The expressible version of the limit.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_SetCurrentLimit(uint16_t u16Limit)
+{
+
+ //--------------------------------------------------------------------------
+ // Set the limit and return what is actually expressible.
+ //--------------------------------------------------------------------------
+
+ return (ddi_bc_RampSetLimit(u16Limit));
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the current limit.
+//!
+//! \fntype Function
+//!
+//! This function reports the limit to the current that the Battery Charger can
+//! draw.
+//!
+//! \retval The current limit.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_GetCurrentLimit(void)
+{
+
+ //--------------------------------------------------------------------------
+ // Set the limit and return what is actually expressible.
+ //--------------------------------------------------------------------------
+
+ return (ddi_bc_RampGetLimit());
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Set the battery charger state machine period.
+//!
+//! \fntype Function
+//!
+//! This function sets a new state machine period. The Period and Slope should
+//! be coordinated to achieve the minimal ramp step current which will minimize
+//! transients on the system.
+//!
+//! \param[in] u32StateMachinePeriod (in milliseconds)
+//! \param[in] u16CurrentRampSlope (in mA/s)
+//!
+//! \retval SUCCESS If all goes well
+//! \retval ERROR_DDI_BCM_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_SetNewPeriodAndSlope(uint32_t u32StateMachinePeriod,
+ uint16_t u16CurrentRampSlope)
+{
+ //--------------------------------------------------------------------------
+ // Check if we've been initialized yet.
+ //--------------------------------------------------------------------------
+ bool bDisableRequired;
+
+ if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) {
+ return (DDI_BC_STATUS_NOT_INITIALIZED);
+ }
+
+ if (g_ddi_bc_State == DDI_BC_STATE_DISABLED)
+ bDisableRequired = false;
+ else {
+ bDisableRequired = true;
+ ddi_bc_SetDisable();
+ }
+
+ // Looking at the code, changing the period while the battery charger is running
+ // doesn't seem to have a negative affect. One could wrap this in the mutex
+ // or implement further coordination if it did.
+ g_ddi_bc_Configuration.u32StateMachinePeriod = u32StateMachinePeriod;
+ g_ddi_bc_Configuration.u16CurrentRampSlope = u16CurrentRampSlope;
+
+ if (bDisableRequired)
+ ddi_bc_SetEnable();
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the state machine period.
+//!
+//! \fntype Function
+//!
+//! This function reports the battery charger period.
+//!
+//! \retval The battery charger period (in milliseconds).
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint32_t ddi_bc_GetStateMachinePeriod()
+{
+ return (g_ddi_bc_Configuration.u32StateMachinePeriod);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the current ramp slope.
+//!
+//! \fntype Function
+//!
+//! This function reports the current ramp slope.
+//!
+//! \retval The current ramp slope (in mA/s).
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint32_t ddi_bc_GetCurrentRampSlope()
+{
+ return (g_ddi_bc_Configuration.u16CurrentRampSlope);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the time spent in the present state (milliseconds)
+//!
+//! \fntype Function
+//!
+//! This function reports the time spent in the present charging state. Note that
+//! for the states that actually charge the battery, this time does not include the
+//! time spent under alarm conditions such as die termperature alarm or battery
+//! temperature alarm.
+//!
+//! \retval The time spent in the current state in milliseconds.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint32_t ddi_bc_GetStateTime(void)
+{
+ return g_ddi_bc_u32StateTimer;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the reason for being in the broken state
+//!
+//! \fntype Function
+//!
+//!
+//! \retval ddi_bc_BrokenReason_t enumeration
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_BrokenReason_t ddi_bc_GetBrokenReason(void)
+{
+ return ddi_bc_gBrokenReason;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Restart the charge cycle
+//!
+//! \fntype Function
+//!
+//!
+//! \retval SUCCESS
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_ForceChargingToStart(void)
+{
+ static int16_t restarts = 0;
+
+ if (restarts < DDI_BC_MAX_RESTART_CYCLES) {
+ restarts++;
+ bRestartChargeCycle = true;
+ }
+
+ return (DDI_BC_STATUS_SUCCESS);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+//! @}
diff --git a/drivers/power/stmp37xx/ddi_bc_hw.c b/drivers/power/stmp37xx/ddi_bc_hw.c
new file mode 100644
index 000000000000..fab82a738b01
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_bc_hw.c
@@ -0,0 +1,474 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include "ddi_bc_internal.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_bc
+//! @{
+//
+// Copyright (c) 2004-2005 SigmaTel, Inc.
+//
+//! \file ddi_bc_hw.c
+//! \brief Contains the Battery Charger hardware operations.
+//! \date 06/2005
+//!
+//! This file contains Battery Charger hardware operations.
+//!
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Includes and external references
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Variables
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Code
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report if the battery charging hardware is available.
+//!
+//! \fntype Function
+//!
+//! This function reports if the battery charging hardware is available by
+//! reading the corresponding laser fuse bit.
+//!
+//! \retval Zero if the battery charging hardware is not available. Non-zero
+//! otherwise.
+//!
+////////////////////////////////////////////////////////////////////////////////
+int ddi_bc_hwBatteryChargerIsEnabled(void)
+{
+ //TODO: replace ddi_bc_hwBatteryChargerIsEnabled with the function below in the code
+ return (int)ddi_power_GetBatteryChargerEnabled();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the battery configuration.
+//!
+//! \fntype Function
+//!
+//! This function reports the hardware battery configuration.
+//!
+//! \retval A value that indicates the battery configuration.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_BatteryMode_t ddi_bc_hwGetBatteryMode(void)
+{
+ //TODO: replace ddi_bc_hwGetBatteryMode() with the function below.
+ return (ddi_bc_BatteryMode_t) ddi_power_GetBatteryMode();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the bias current source.
+//!
+//! \fntype Function
+//!
+//! This function Battery Charger.
+//!
+//! \retval A value that indicates the current bias current source.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_BiasCurrentSource_t ddi_bc_hwGetBiasCurrentSource(void)
+{
+ // TODO: replace ddi_bc_BiasCurrentSource_t() with the function below.
+ return (ddi_bc_BiasCurrentSource_t) ddi_power_GetBiasCurrentSource();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Sets the bias current source.
+//!
+//! \fntype Function
+//!
+//! This function sets the bias current source used by the battery charger
+//! hardware. The battery charger is usually configured to use an externally
+//! generated bias current, which is expected to be quite precise. To reduce
+//! component count, for example, it can be configured to generate a lesser-
+//! quality bias current internally.
+//!
+//! \param[in] source Indicates the source of the bias current.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If the operation succeeded.
+//! \retval DDI_BC_STATUS_BAD_ARGUMENT If the given source argument isn't one
+//! of the expected values.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_hwSetBiasCurrentSource(ddi_bc_BiasCurrentSource_t source)
+{
+ ddi_power_BiasCurrentSource_t eSource =
+ (ddi_power_BiasCurrentSource_t) source;
+
+ //--------------------------------------------------------------------------
+ // In the following code, there are two points that can be confusing. Both
+ // of these problems derive from silliness in the data sheet.
+ //
+ // The field of interest here is HW_POWER_BATTCHRG.USE_EXTERN_R. This is
+ // poorly named for two reasons:
+ //
+ // 1) What we're really doing is selecting the source of the bias current.
+ // If the application chooses to use an external bias current, then
+ // there will, of course, be a resistor involved (apparently, a good-
+ // quality one at about 620 Ohms).
+ //
+ // 2) If the bit is SET, that tells the hardware to use the INTERNAL bias
+ // current. If this bit is CLEAR, that tells the hardware to use the
+ // EXTERNAL bias current. Thus, the actual logic for this bit is
+ // reversed from the sense indicated by its name.
+ //--------------------------------------------------------------------------
+
+ // TODO: replace all ddi_bc_hwSetBiasCurrentSource() with function below.
+ return (ddi_bc_Status_t) ddi_power_SetBiasCurrentSource(eSource);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the voltage across the battery.
+//!
+//! \fntype Function
+//!
+//! This function reports the voltage across the battery.
+//!
+//! \retval The voltage across the battery, in mV.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_hwGetBatteryVoltage(void)
+{
+ //TODO: replace ddi_bc_hwGetBattery with function below
+ return ddi_power_GetBattery();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report on the presence of the power supply.
+//!
+//! \fntype Function
+//!
+//! This function repots on whether or not the 5V power supply is present.
+//!
+//! \retval Zero if the power supply is not present. Non-zero otherwise.
+//!
+////////////////////////////////////////////////////////////////////////////////
+int ddi_bc_hwPowerSupplyIsPresent(void)
+{
+ // TODO: replace ddi_bc_hwPowerSupplyIsPresent with the functino below.
+ return (int)ddi_power_Get5vPresentFlag();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the maximum charging current.
+//!
+//! \fntype Function
+//!
+//! This function reports the maximum charging current that will be offered to
+//! the battery, as currently set in the hardware.
+//!
+//! \retval The maximum current setting in the hardware.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_hwGetMaxCurrent(void)
+{
+ // TODO: replace ddi_bc_hwGetMaxCurrent() with the below function
+ return (uint16_t) ddi_power_GetMaxBatteryChargeCurrent();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Set the maximum charging current.
+//!
+//! \fntype Function
+//!
+//! This function sets the maximum charging current that will be offered to the
+//! battery.
+//!
+//! Note that the hardware has a minimum resolution of 10mA and a maximum
+//! expressible value of 780mA (see the data sheet for details). If the given
+//! current cannot be expressed exactly, then the largest expressible smaller
+//! value will be used. The return reports the actual value that was effected.
+//!
+//! \param[in] u16Limit The maximum charging current, in mA.
+//!
+//! \retval The actual value that was effected.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_hwSetMaxCurrent(uint16_t u16Limit)
+{
+ //TODO: replace ddi_bc_hwSetMaxChargeCurrent
+ return ddi_power_SetMaxBatteryChargeCurrent(u16Limit);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Set the charging current threshold.
+//!
+//! \fntype Function
+//!
+//! This function sets the charging current threshold. When the actual current
+//! flow to the battery is less than this threshold, the HW_POWER_STS.CHRGSTS
+//! flag is clear.
+//!
+//! Note that the hardware has a minimum resolution of 10mA and a maximum
+//! expressible value of 180mA (see the data sheet for details). If the given
+//! current cannot be expressed exactly, then the largest expressible smaller
+//! value will be used. The return reports the actual value that was effected.
+//!
+//! \param[in] u16Threshold The charging current threshold, in mA.
+//!
+//! \retval The actual value that was effected.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_hwSetCurrentThreshold(uint16_t u16Threshold)
+{
+ //TODO: replace calls to ddi_bc_hwSetCurrentThreshold with the one below
+ return ddi_power_SetBatteryChargeCurrentThreshold(u16Threshold);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the charging current threshold.
+//!
+//! \fntype Function
+//!
+//! This function reports the charging current threshold. When the actual
+//! current flow to the battery is less than this threshold, the
+//! HW_POWER_STS.CHRGSTS flag is clear.
+//!
+//! Note that the hardware has a minimum resolution of 10mA and a maximum
+//! expressible value of 180mA (see the data sheet for details).
+//!
+//! \retval The charging current threshold, in mA.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_hwGetCurrentThreshold(void)
+{
+ //TODO: replace calls to ddi_bc_hwGetCurrentThreshold with function below
+ return ddi_power_GetBatteryChargeCurrentThreshold();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report if the charger hardware power is on.
+//!
+//! \fntype Function
+//!
+//! This function reports if the charger hardware power is on.
+//!
+//! \retval Zero if the charger hardware is not powered. Non-zero otherwise.
+//!
+////////////////////////////////////////////////////////////////////////////////
+int ddi_bc_hwChargerPowerIsOn(void)
+{
+
+ //--------------------------------------------------------------------------
+ // Note that the bit we're looking at is named PWD_BATTCHRG. The "PWD"
+ // stands for "power down". Thus, when the bit is set, the battery charger
+ // hardware is POWERED DOWN.
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Read the register and return the result.
+ //--------------------------------------------------------------------------
+
+ //TODO: replace ddi_bc_hwChargerPowerIsOn with function below
+ return ddi_power_GetChargerPowered();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Turn the charging hardware on or off.
+//!
+//! \fntype Function
+//!
+//! This function turns the charging hardware on or off.
+//!
+//! \param[in] on Indicates whether the charging hardware should be on or off.
+//!
+////////////////////////////////////////////////////////////////////////////////
+void ddi_bc_hwSetChargerPower(int on)
+{
+
+ //--------------------------------------------------------------------------
+ // Note that the bit we're looking at is named PWD_BATTCHRG. The "PWD"
+ // stands for "power down". Thus, when the bit is set, the battery charger
+ // hardware is POWERED DOWN.
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Hit the power switch.
+ //--------------------------------------------------------------------------
+
+ //TODO: replace ddi_bc_hwSetChargerPower with functino below
+ ddi_power_SetChargerPowered(on);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Reports if the charging current has fallen below the threshold.
+//!
+//! \fntype Function
+//!
+//! This function reports if the charging current that the battery is accepting
+//! has fallen below the threshold.
+//!
+//! Note that this bit is regarded by the hardware guys as very slightly
+//! unreliable. They recommend that you don't believe a value of zero until
+//! you've sampled it twice.
+//!
+//! \retval Zero if the battery is accepting less current than indicated by the
+//! charging threshold. Non-zero otherwise.
+//!
+////////////////////////////////////////////////////////////////////////////////
+int ddi_bc_hwGetChargeStatus(void)
+{
+ return ddi_power_GetChargeStatus();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report on the die temperature.
+//!
+//! \fntype Function
+//!
+//! This function reports on the die temperature.
+//!
+//! \param[out] pLow The low end of the temperature range.
+//! \param[out] pHigh The high end of the temperature range.
+//!
+////////////////////////////////////////////////////////////////////////////////
+void ddi_bc_hwGetDieTemp(int16_t * pLow, int16_t * pHigh)
+{
+ // TODO: replace ddi_bc_hwGetDieTemp with function below
+ ddi_power_GetDieTemp(pLow, pHigh);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the battery temperature reading.
+//!
+//! \fntype Function
+//!
+//! This function examines the configured LRADC channel and reports the battery
+//! temperature reading.
+//!
+//! \param[out] pReading A pointer to a variable that will receive the
+//! temperature reading.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If the operation succeeded.
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_hwGetBatteryTemp(uint16_t * pReading)
+{
+ return (ddi_bc_Status_t)DDI_BC_STATUS_HARDWARE_DISABLED;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Convert a current in mA to a hardware setting.
+//!
+//! \fntype Function
+//!
+//! This function converts a current measurement in mA to a hardware setting
+//! used by HW_POWER_BATTCHRG.STOP_ILIMIT or HW_POWER_BATTCHRG.BATTCHRG_I.
+//!
+//! Note that the hardware has a minimum resolution of 10mA and a maximum
+//! expressible value of 780mA (see the data sheet for details). If the given
+//! current cannot be expressed exactly, then the largest expressible smaller
+//! value will be used.
+//!
+//! \param[in] u16Current The current of interest.
+//!
+//! \retval The corresponding setting.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint8_t ddi_bc_hwCurrentToSetting(uint16_t u16Current)
+{
+ return ddi_power_convert_current_to_setting(u16Current);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Convert a hardware current setting to a value in mA.
+//!
+//! \fntype Function
+//!
+//! This function converts a setting used by HW_POWER_BATTCHRG.STOP_ILIMIT or
+//! HW_POWER_BATTCHRG.BATTCHRG_I into an actual current measurement in mA.
+//!
+//! Note that the hardware current fields are 6 bits wide. The higher bits in
+//! the 8-bit input parameter are ignored.
+//!
+//! \param[in] u8Setting A hardware current setting.
+//!
+//! \retval The corresponding current in mA.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_hwSettingToCurrent(uint8_t u8Setting)
+{
+ return ddi_power_convert_setting_to_current(u8Setting);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Compute the actual current expressible in the hardware.
+//!
+//! \fntype Function
+//!
+//! Given a desired current, this function computes the actual current
+//! expressible in the hardware.
+//!
+//! Note that the hardware has a minimum resolution of 10mA and a maximum
+//! expressible value of 780mA (see the data sheet for details). If the given
+//! current cannot be expressed exactly, then the largest expressible smaller
+//! value will be used.
+//!
+//! \param[in] u16Current The current of interest.
+//!
+//! \retval The corresponding current in mA.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_hwExpressibleCurrent(uint16_t u16Current)
+{
+ //TODO: replace the bc function with this one
+ return ddi_power_ExpressibleCurrent(u16Current);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Checks to see if the DCDC has been manually enabled
+//!
+//! \fntype Function
+//!
+//! \retval true if DCDC is ON, false if DCDC is OFF.
+//!
+////////////////////////////////////////////////////////////////////////////////
+bool ddi_bc_hwIsDcdcOn(void)
+{
+ return ddi_power_IsDcdcOn();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+//! @}
diff --git a/drivers/power/stmp37xx/ddi_bc_hw.h b/drivers/power/stmp37xx/ddi_bc_hw.h
new file mode 100644
index 000000000000..68ef98aa4303
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_bc_hw.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_bc
+//! @{
+//
+// Copyright (c) 2004-2005 SigmaTel, Inc.
+//
+//! \file ddi_bc_hw.h
+//! \brief Internal header file for Battery Charger hardware operations.
+//! \date 06/2005
+//!
+//! This file contains internal declarations for Battery Charger hardware
+//! operations.
+//!
+//! \see ddi_bc.c and related files.
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _DDI_BC_HW_H
+#define _DDI_BC_HW_H
+
+////////////////////////////////////////////////////////////////////////////////
+// Definitions
+////////////////////////////////////////////////////////////////////////////////
+
+//! The enumeration of battery modes.
+
+typedef enum _ddi_bc_BatteryMode {
+ DDI_BC_BATTERY_MODE_LI_ION_2_CELLS = 0,
+ DDI_BC_BATTERY_MODE_LI_ION_1_CELL = 1,
+ DDI_BC_BATTERY_MODE_2_CELLS = 2,
+ DDI_BC_BATTERY_MODE_1_CELL = 3
+} ddi_bc_BatteryMode_t;
+
+//! The enumeration of bias current sources.
+
+typedef enum _ddi_bc_BiasCurrentSource {
+ DDI_BC_EXTERNAL_BIAS_CURRENT = 0,
+ DDI_BC_INTERNAL_BIAS_CURRENT = 1,
+} ddi_bc_BiasCurrentSource_t;
+
+////////////////////////////////////////////////////////////////////////////////
+// Prototypes
+////////////////////////////////////////////////////////////////////////////////
+
+extern int ddi_bc_hwBatteryChargerIsEnabled(void);
+extern ddi_bc_BatteryMode_t ddi_bc_hwGetBatteryMode(void);
+extern ddi_bc_BiasCurrentSource_t ddi_bc_hwGetBiasCurrentSource(void);
+extern ddi_bc_Status_t
+ddi_bc_hwSetBiasCurrentSource(ddi_bc_BiasCurrentSource_t);
+extern ddi_bc_Status_t ddi_bc_hwSetChargingVoltage(uint16_t);
+extern uint16_t ddi_bc_hwGetBatteryVoltage(void);
+extern int ddi_bc_hwPowerSupplyIsPresent(void);
+extern uint16_t ddi_bc_hwSetMaxCurrent(uint16_t);
+extern uint16_t ddi_bc_hwGetMaxCurrent(void);
+extern uint16_t ddi_bc_hwSetCurrentThreshold(uint16_t);
+extern uint16_t ddi_bc_hwGetCurrentThreshold(void);
+extern int ddi_bc_hwChargerPowerIsOn(void);
+extern void ddi_bc_hwSetChargerPower(int);
+extern int ddi_bc_hwGetChargeStatus(void);
+extern void ddi_bc_hwGetDieTemp(int16_t *, int16_t *);
+extern ddi_bc_Status_t ddi_bc_hwGetBatteryTemp(uint16_t *);
+uint8_t ddi_bc_hwCurrentToSetting(uint16_t);
+uint16_t ddi_bc_hwSettingToCurrent(uint8_t);
+uint16_t ddi_bc_hwExpressibleCurrent(uint16_t);
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Checks to see if the DCDC has been manually enabled
+//!
+//! \fntype Function
+//!
+//! \retval true if DCDC is ON, false if DCDC is OFF.
+//!
+////////////////////////////////////////////////////////////////////////////////
+bool ddi_bc_hwIsDcdcOn(void);
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+#endif // _DDI_BC_H
+//! @}
diff --git a/drivers/power/stmp37xx/ddi_bc_init.c b/drivers/power/stmp37xx/ddi_bc_init.c
new file mode 100644
index 000000000000..ef323b33b74f
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_bc_init.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include "ddi_bc_internal.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_bc
+//! @{
+//
+// Copyright (c) 2004-2005 SigmaTel, Inc.
+//
+//! \file ddi_bc_init.c
+//! \brief Contains the Battery Charger initialization function.
+//! \date 06/2005
+//!
+//! This file contains Battery Charger initialization function.
+//!
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Includes and external references
+////////////////////////////////////////////////////////////////////////////////
+#include <mach/ddi_bc.h>
+#include "ddi_bc_internal.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Code
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+//! \brief Initialize the Battery Charger.
+//!
+//! \fntype Function
+//!
+//! This function initializes the Battery Charger.
+//!
+//! \param[in] pCfg A pointer to the new configuration.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS
+//! If the operation succeeded.
+//! \retval DDI_BC_STATUS_ALREADY_INITIALIZED
+//! If the Battery Charger is already initialized.
+//! \retval DDI_BC_STATUS_HARDWARE_DISABLED
+//! If the Battery Charger hardware is disabled by a laser fuse.
+//! \retval DDI_BC_STATUS_BAD_BATTERY_MODE
+//! If the power supply is set up for a non-rechargeable battery.
+//! \retval DDI_BC_STATUS_CLOCK_GATE_CLOSED
+//! If the clock gate for the power supply registers is closed.
+//! \retval DDI_BC_STATUS_CFG_BAD_CHARGING_VOLTAGE
+//! If the charging voltage is not either 4100 or 4200.
+//! \retval DDI_BC_STATUS_CFG_BAD_BATTERY_TEMP_CHANNEL
+//! If the LRADC channel number for monitoring battery temperature
+//! is bad.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_Init(ddi_bc_Cfg_t * pCfg)
+{
+
+ //--------------------------------------------------------------------------
+ // We can only be initialized if we're in the Uninitialized state.
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_State != DDI_BC_STATE_UNINITIALIZED) {
+ return (DDI_BC_STATUS_ALREADY_INITIALIZED);
+ }
+ //--------------------------------------------------------------------------
+ // Check if the battery charger hardware has been disabled by laser fuse.
+ //--------------------------------------------------------------------------
+
+ if (!ddi_power_GetBatteryChargerEnabled())
+ return (DDI_BC_STATUS_HARDWARE_DISABLED);
+
+ //--------------------------------------------------------------------------
+ // Check if the power supply has been set up for a non-rechargeable battery.
+ //--------------------------------------------------------------------------
+
+ switch (ddi_power_GetBatteryMode()) {
+
+ case DDI_POWER_BATT_MODE_LIION:
+ break;
+
+ // TODO: we'll need to do NiMH also
+ default:
+ return (DDI_BC_STATUS_BAD_BATTERY_MODE);
+ //break;
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Make sure that the clock gate has been opened for the power supply
+ // registers. If not, then none of our writes to those registers will
+ // succeed, which will kind of slow us down...
+ //--------------------------------------------------------------------------
+
+ if (ddi_power_GetPowerClkGate()) {
+ return (DDI_BC_STATUS_CLOCK_GATE_CLOSED);
+ }
+ //--------------------------------------------------------------------------
+ // Check the incoming configuration for nonsense.
+ //--------------------------------------------------------------------------
+
+ //
+ // Only permitted charging voltage: 4200mV.
+ //
+
+ if (pCfg->u16ChargingVoltage != DDI_BC_LIION_CHARGING_VOLTAGE) {
+ return (DDI_BC_STATUS_CFG_BAD_CHARGING_VOLTAGE);
+ }
+ //
+ // There are 8 LRADC channels.
+ //
+
+ if (pCfg->u8BatteryTempChannel > 7) {
+ return (DDI_BC_STATUS_CFG_BAD_BATTERY_TEMP_CHANNEL);
+ }
+ //--------------------------------------------------------------------------
+ // Accept the configuration.
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // ddi_bc_Cfg_t.u16ChargingThresholdCurrent is destined for the
+ // register field HW_POWER_BATTCHRG.STOP_ILIMIT. This 4-bit field
+ // is unevenly quantized to provide a useful range of currents. A
+ // side effect of the quantization is that the field can only be
+ // set to certain unevenly-spaced values.
+ //
+ // Here, we use the two functions that manipulate the register field
+ // to adjust u16ChargingThresholdCurrent to match the quantized value.
+ //--------------------------------------------------------------------------
+ pCfg->u16ChargingThresholdCurrent =
+ ddi_power_ExpressibleCurrent(pCfg->u16ChargingThresholdCurrent);
+
+ //--------------------------------------------------------------------------
+ // ...similar situation with ddi_bc_Cfg_t.u16BatteryTempSafeCurrent and
+ // u16DieTempSafeCurrent.
+ //--------------------------------------------------------------------------
+ pCfg->u16BatteryTempSafeCurrent =
+ ddi_power_ExpressibleCurrent(pCfg->u16BatteryTempSafeCurrent);
+ pCfg->u16DieTempSafeCurrent =
+ ddi_power_ExpressibleCurrent(pCfg->u16DieTempSafeCurrent);
+
+ g_ddi_bc_Configuration = *pCfg;
+
+ //--------------------------------------------------------------------------
+ // Set the bias current source.
+ //--------------------------------------------------------------------------
+
+ {
+ ddi_power_BiasCurrentSource_t Flag;
+
+ Flag =
+ g_ddi_bc_Configuration.useInternalBias ?
+ DDI_POWER_INTERNAL_BIAS_CURRENT :
+ DDI_POWER_EXTERNAL_BIAS_CURRENT;
+
+ ddi_power_SetBiasCurrentSource(Flag);
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Turn the charger hardware off. This is a very important initial condition
+ // because we only flip the power switch on the hardware when we make
+ // transitions. Baseline, it needs to be off.
+ //--------------------------------------------------------------------------
+
+ ddi_power_SetChargerPowered(0);
+
+ //--------------------------------------------------------------------------
+ // Reset the current ramp. This will jam the current to zero and power off
+ // the charging hardware.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampReset();
+
+ //--------------------------------------------------------------------------
+ // Move to the Disabled state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_DISABLED;
+
+ //--------------------------------------------------------------------------
+ // Return success.
+ //--------------------------------------------------------------------------
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("%s: success\n", __func__);
+#endif
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+//! @}
diff --git a/drivers/power/stmp37xx/ddi_bc_internal.h b/drivers/power/stmp37xx/ddi_bc_internal.h
new file mode 100644
index 000000000000..c1fb1b20f271
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_bc_internal.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_bc
+//! @{
+//
+// Copyright (c) 2004-2005 SigmaTel, Inc.
+//
+//! \file ddi_bc_internal.h
+//! \brief Internal header file for the Battery Charger device driver.
+//! \date 06/2005
+//!
+//! This file contains internal declarations for the Battery Charger device
+//! driver.
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _DDI_BC_INTERNAL_H
+#define _DDI_BC_INTERNAL_H
+
+////////////////////////////////////////////////////////////////////////////////
+// Includes
+////////////////////////////////////////////////////////////////////////////////
+
+#include <mach/ddi_bc.h>
+#include "ddi_bc_hw.h"
+#include "ddi_bc_ramp.h"
+#include "ddi_bc_sm.h"
+#include "ddi_power_battery.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Externs
+////////////////////////////////////////////////////////////////////////////////
+
+extern bool g_ddi_bc_Configured;
+extern ddi_bc_Cfg_t g_ddi_bc_Configuration;
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+#endif // _DDI_BC_H
+//! @}
diff --git a/drivers/power/stmp37xx/ddi_bc_ramp.c b/drivers/power/stmp37xx/ddi_bc_ramp.c
new file mode 100644
index 000000000000..1de3a53259f8
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_bc_ramp.c
@@ -0,0 +1,724 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_bc
+//! @{
+//
+// Copyright (c) 2004-2005 SigmaTel, Inc.
+//
+//! \file ddi_bc_ramp.c
+//! \brief Contains the Battery Charger current ramp controller.
+//! \date 06/2005
+//!
+//! This file contains Battery Charger current ramp controller.
+//!
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Includes and external references
+////////////////////////////////////////////////////////////////////////////////
+
+#include <mach/ddi_bc.h>
+#include "ddi_bc_internal.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Definitions
+////////////////////////////////////////////////////////////////////////////////
+
+//! This is the control structure for the current ramp.
+
+typedef struct _ddi_bc_RampControl {
+
+ uint32_t u32AccumulatedTime;
+
+ //!< The accumulated time since we last changed the actual
+ //!< current setting in the hardware. If the time between
+ //!< steps is quite short, we may have to wait for several steps
+ //!< before we can actually change the hardware setting.
+
+ uint16_t u16Target;
+
+ //!< The target current, regardless of expressibility.
+
+ uint16_t u16Limit;
+
+ //!< The current limit, regardless of expressibility.
+
+ uint8_t dieTempAlarm:1;
+
+ //!< Indicates if we are operating under a die temperature
+ //!< alarm.
+
+ uint8_t batteryTempAlarm:1;
+
+ //!< Indicates if we are operating under a battery temperature
+ //!< alarm.
+
+ uint8_t ambientTempAlarm:1;
+
+ //!< Indicates if we are operating under an ambient temperature
+ //!< alarm.
+
+} ddi_bc_RampControl_t;
+
+////////////////////////////////////////////////////////////////////////////////
+// Variables
+////////////////////////////////////////////////////////////////////////////////
+
+//! This structure contains control information for the current ramp.
+
+static ddi_bc_RampControl_t g_RampControl;
+
+////////////////////////////////////////////////////////////////////////////////
+// Code
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Reset the current ramp.
+//!
+//! \fntype Function
+//!
+//! This function resets the current ramp.
+//!
+//! Note that this function does NOT reset the temperature alarms or the current
+//! limit. Those can only be changed explicitly.
+//!
+////////////////////////////////////////////////////////////////////////////////
+void ddi_bc_RampReset()
+{
+
+ //--------------------------------------------------------------------------
+ // Reset the control structure.
+ //--------------------------------------------------------------------------
+
+ g_RampControl.u32AccumulatedTime = 0;
+ g_RampControl.u16Target = 0;
+
+ //--------------------------------------------------------------------------
+ // Step the ramp. Note that we don't care if this function returns an error.
+ // We're stepping the ramp to make sure it takes immediate effect, if
+ // possible. But, for example, if the Battery Charger is not yet
+ // initialized, it doesn't matter.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampStep(0);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Set the target current.
+//!
+//! \fntype Function
+//!
+//! This function sets the target current and implements it immediately.
+//!
+//! Note that this function does NOT reset the temperature alarms. Those can
+//! only be reset explicitly.
+//!
+//! \param[in] u16Target The target current.
+//!
+//! \retval The expressible version of the target.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_RampSetTarget(uint16_t u16Target)
+{
+
+ //--------------------------------------------------------------------------
+ // Set the target.
+ //--------------------------------------------------------------------------
+
+ g_RampControl.u16Target = u16Target;
+
+ //--------------------------------------------------------------------------
+ // Step the ramp. Note that we don't care if this function returns an error.
+ // We're stepping the ramp to make sure it takes immediate effect, if
+ // possible. But, for example, if the Battery Charger is not yet
+ // initialized, it doesn't matter.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampStep(0);
+
+ //--------------------------------------------------------------------------
+ // Compute and return the expressible target.
+ //--------------------------------------------------------------------------
+
+ return (ddi_bc_hwExpressibleCurrent(u16Target));
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the target.
+//!
+//! \fntype Function
+//!
+//! This function reports the target.
+//!
+//! \retval The target.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_RampGetTarget(void)
+{
+
+ //--------------------------------------------------------------------------
+ // Return the target.
+ //--------------------------------------------------------------------------
+
+ return (g_RampControl.u16Target);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Set the current limit.
+//!
+//! \fntype Function
+//!
+//! This function sets the current limit and implements it immediately.
+//!
+//! \param[in] u16Limit The current limit.
+//!
+//! \retval The expressible version of the limit.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_RampSetLimit(uint16_t u16Limit)
+{
+
+ //--------------------------------------------------------------------------
+ // Set the limit.
+ //--------------------------------------------------------------------------
+
+ g_RampControl.u16Limit = u16Limit;
+
+ //--------------------------------------------------------------------------
+ // Step the ramp. Note that we don't care if this function returns an error.
+ // We're stepping the ramp to make sure it takes immediate effect, if
+ // possible. But, for example, if the Battery Charger is not yet
+ // initialized, it doesn't matter.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampStep(0);
+
+ //--------------------------------------------------------------------------
+ // Compute and return the expressible limit.
+ //--------------------------------------------------------------------------
+
+ return (ddi_bc_hwExpressibleCurrent(u16Limit));
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the current limit.
+//!
+//! \fntype Function
+//!
+//! This function reports the current limit.
+//!
+//! \retval The current limit.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_bc_RampGetLimit(void)
+{
+
+ //--------------------------------------------------------------------------
+ // Return the current limit.
+ //--------------------------------------------------------------------------
+
+ return (g_RampControl.u16Limit);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Update alarms.
+//!
+//! \fntype Function
+//!
+//! This function checks for all alarms and updates the current ramp
+//! accordingly.
+//!
+////////////////////////////////////////////////////////////////////////////////
+void ddi_bc_RampUpdateAlarms()
+{
+
+ // Set to true if something changed and we need to step the ramp right away.
+
+ int iStepTheRamp = 0;
+
+ //--------------------------------------------------------------------------
+ // Are we monitoring die temperature?
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_Configuration.monitorDieTemp) {
+
+ //----------------------------------------------------------------------
+ // Get the die temperature range.
+ //----------------------------------------------------------------------
+
+ int16_t i16Low;
+ int16_t i16High;
+
+ ddi_bc_hwGetDieTemp(&i16Low, &i16High);
+
+ //----------------------------------------------------------------------
+ // Now we need to decide if it's time to raise or lower the alarm. The
+ // first question to ask is: Were we already under an alarm?
+ //----------------------------------------------------------------------
+
+ if (g_RampControl.dieTempAlarm) {
+
+ //------------------------------------------------------------------
+ // If control arrives here, we were already under an alarm. We'll
+ // change that if the high end of the temperature range drops below
+ // the low temperature mark.
+ //------------------------------------------------------------------
+
+ if (i16High < g_ddi_bc_Configuration.u8DieTempLow) {
+
+ //--------------------------------------------------------------
+ // If control arrives here, we're safe now. Drop the alarm.
+ //--------------------------------------------------------------
+
+ g_RampControl.dieTempAlarm = 0;
+
+ iStepTheRamp = !0;
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("Battery charger: releasing "
+ "die temp alarm: [%d, %d] < %d\r\n",
+ (int32_t) i16Low, (int32_t) i16High,
+ (int32_t) g_ddi_bc_Configuration.
+ u8DieTempLow);
+#endif
+
+ }
+
+ } else {
+
+ //------------------------------------------------------------------
+ // If control arrives here, we were not under an alarm. We'll change
+ // that if the high end of the temperature range rises above the
+ // high temperature mark.
+ //------------------------------------------------------------------
+
+ if (i16High >= g_ddi_bc_Configuration.u8DieTempHigh) {
+
+ //--------------------------------------------------------------
+ // If control arrives here, we're running too hot. Raise the
+ // alarm.
+ //--------------------------------------------------------------
+
+ g_RampControl.dieTempAlarm = 1;
+
+ iStepTheRamp = !0;
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("Battery charger: declaring "
+ "die temp alarm: [%d, %d] >= %d\r\n",
+ (int32_t) i16Low, (int32_t) i16High,
+ (int32_t) g_ddi_bc_Configuration.
+ u8DieTempLow);
+#endif
+ }
+
+ }
+
+ }
+ //--------------------------------------------------------------------------
+ // Are we monitoring battery temperature?
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_Configuration.monitorBatteryTemp) {
+
+ ddi_bc_Status_t status;
+
+ //----------------------------------------------------------------------
+ // Get the battery temperature reading.
+ //----------------------------------------------------------------------
+
+ uint16_t u16Reading;
+
+ status = ddi_bc_hwGetBatteryTemp(&u16Reading);
+
+ //----------------------------------------------------------------------
+ // If there was a problem, then we ignore the reading. Otherwise, let's
+ // have a look.
+ //----------------------------------------------------------------------
+
+ if (status == DDI_BC_STATUS_SUCCESS) {
+
+ //------------------------------------------------------------------
+ // Now we need to decide if it's time to raise or lower the alarm.
+ // The first question to ask is: Were we already under an alarm?
+ //------------------------------------------------------------------
+
+ if (g_RampControl.batteryTempAlarm) {
+
+ //--------------------------------------------------------------
+ // If control arrives here, we were already under an alarm.
+ // We'll change that if the reading drops below the low mark.
+ //--------------------------------------------------------------
+
+ if (u16Reading <
+ g_ddi_bc_Configuration.u16BatteryTempLow) {
+
+ //----------------------------------------------------------
+ // If control arrives here, we're safe now. Drop the alarm.
+ //----------------------------------------------------------
+
+ g_RampControl.batteryTempAlarm = 0;
+
+ iStepTheRamp = !0;
+
+ }
+
+ } else {
+
+ //--------------------------------------------------------------
+ // If control arrives here, we were not under an alarm. We'll
+ // change that if the reading rises above the high mark.
+ //--------------------------------------------------------------
+
+ if (u16Reading >=
+ g_ddi_bc_Configuration.u16BatteryTempHigh) {
+
+ //----------------------------------------------------------
+ // If control arrives here, we're running too hot. Raise the
+ // alarm.
+ //----------------------------------------------------------
+
+ g_RampControl.batteryTempAlarm = 1;
+
+ iStepTheRamp = !0;
+
+ }
+
+ }
+
+ }
+
+ }
+ //--------------------------------------------------------------------------
+ // Do we need to step the ramp?
+ //--------------------------------------------------------------------------
+
+ if (iStepTheRamp)
+ ddi_bc_RampStep(0);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Reports the state of the die temperature alarm.
+//!
+//! \fntype Function
+//!
+//! This function reports the state of the die temperature alarm.
+//!
+//! \retval The state of the die temperature alarm.
+//!
+////////////////////////////////////////////////////////////////////////////////
+int ddi_bc_RampGetDieTempAlarm(void)
+{
+ return (g_RampControl.dieTempAlarm);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Reports the state of the battery temperature alarm.
+//!
+//! \fntype Function
+//!
+//! This function reports the state of the battery temperature alarm.
+//!
+//! \retval The state of the battery temperature alarm.
+//!
+////////////////////////////////////////////////////////////////////////////////
+int ddi_bc_RampGetBatteryTempAlarm(void)
+{
+ return (g_RampControl.batteryTempAlarm);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Reports the state of the ambient temperature alarm.
+//!
+//! \fntype Function
+//!
+//! This function reports the state of the ambient temperature alarm.
+//!
+//! \retval The state of the ambient temperature alarm.
+//!
+////////////////////////////////////////////////////////////////////////////////
+int ddi_bc_RampGetAmbientTempAlarm(void)
+{
+ return (g_RampControl.ambientTempAlarm);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Step the current ramp.
+//!
+//! \fntype Function
+//!
+//! This function steps the current ramp forward through the given amount of time.
+//!
+//! \param[in] u32Time The time increment to add.
+//!
+//! \retval DDI_BC_STATUS_SUCCESS If the operation succeeded.
+//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet
+//! initialized.
+//!
+////////////////////////////////////////////////////////////////////////////////
+ddi_bc_Status_t ddi_bc_RampStep(uint32_t u32Time)
+{
+
+ uint16_t u16MaxNow;
+ uint16_t u16Target;
+ uint16_t u16Cart;
+ int32_t i32Delta;
+
+ //--------------------------------------------------------------------------
+ // Make sure the Battery Charger is initialized.
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) {
+ return (DDI_BC_STATUS_NOT_INITIALIZED);
+ }
+ //--------------------------------------------------------------------------
+ // Figure out how much current the hardware is set to draw right now.
+ //--------------------------------------------------------------------------
+
+ u16MaxNow = ddi_bc_hwGetMaxCurrent();
+
+ //--------------------------------------------------------------------------
+ // Start with the target.
+ //--------------------------------------------------------------------------
+
+ u16Target = g_RampControl.u16Target;
+
+ //--------------------------------------------------------------------------
+ // Check the target against the hard limit.
+ //--------------------------------------------------------------------------
+
+ if (u16Target > g_RampControl.u16Limit)
+ u16Target = g_RampControl.u16Limit;
+
+ //--------------------------------------------------------------------------
+ // Check if the die temperature alarm is active.
+ //--------------------------------------------------------------------------
+
+ if (g_RampControl.dieTempAlarm) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, we are under a die temperature alarm. Clamp
+ // the target current.
+ //----------------------------------------------------------------------
+
+ if (u16Target > g_ddi_bc_Configuration.u16DieTempSafeCurrent) {
+ u16Target =
+ g_ddi_bc_Configuration.u16DieTempSafeCurrent;
+ }
+
+ }
+ //--------------------------------------------------------------------------
+ // Check if the battery temperature alarm is active.
+ //--------------------------------------------------------------------------
+
+ if (g_RampControl.batteryTempAlarm) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, we are under a battery temperature alarm.
+ // Clamp the target current.
+ //----------------------------------------------------------------------
+
+ if (u16Target >
+ g_ddi_bc_Configuration.u16BatteryTempSafeCurrent) {
+ u16Target =
+ g_ddi_bc_Configuration.u16BatteryTempSafeCurrent;
+ }
+
+ }
+ //--------------------------------------------------------------------------
+ // Now we know the target current. Figure out what is actually expressible
+ // in the hardware.
+ //--------------------------------------------------------------------------
+
+ u16Target = ddi_bc_hwExpressibleCurrent(u16Target);
+
+ //--------------------------------------------------------------------------
+ // Compute the difference between the expressible target and what's actually
+ // set in the hardware right now.
+ //--------------------------------------------------------------------------
+
+ i32Delta = ((int32_t) u16Target) - ((int32_t) u16MaxNow);
+
+ //--------------------------------------------------------------------------
+ // Check if the delta is zero.
+ //--------------------------------------------------------------------------
+
+ if (i32Delta == 0) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, there is no difference between what we want
+ // and what's set in the hardware.
+ //
+ // Before we leave, though, we don't want to leave any accumulated time
+ // laying around for the next ramp up. Zero it out.
+ //----------------------------------------------------------------------
+
+ g_RampControl.u32AccumulatedTime = 0;
+
+ //----------------------------------------------------------------------
+ // Return success.
+ //----------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+ }
+ //--------------------------------------------------------------------------
+ // Check if the delta is negative.
+ //--------------------------------------------------------------------------
+
+ if (i32Delta < 0) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, the new target is lower than what's
+ // currently set in the hardware. Since that means we're *reducing* the
+ // current draw, we can do it right now. Just gimme a sec here...
+ //----------------------------------------------------------------------
+
+ ddi_bc_hwSetMaxCurrent(u16Target);
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("Battery charger: setting max charge "
+ "current to: %hdmA\r\n", u16Target);
+#endif
+
+ //----------------------------------------------------------------------
+ // Flip the power switch on the charging hardware according to the new
+ // current setting.
+ //----------------------------------------------------------------------
+
+ ddi_bc_hwSetChargerPower(u16Target != 0);
+
+ //----------------------------------------------------------------------
+ // We don't want to leave any accumulated time laying around for the
+ // next ramp up. Zero it out.
+ //----------------------------------------------------------------------
+
+ g_RampControl.u32AccumulatedTime = 0;
+
+ //----------------------------------------------------------------------
+ // Return success.
+ //----------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+ }
+ //--------------------------------------------------------------------------
+ // If control arrives here, the target current is higher than what's set in
+ // the hardware right now. That means we're going to ramp it up. To do that,
+ // we're going to "buy" more milliamps by "spending" milliseconds of time.
+ // Add the time we've "banked" to the time we've been credited in this call.
+ //--------------------------------------------------------------------------
+
+ u32Time += g_RampControl.u32AccumulatedTime;
+
+ //--------------------------------------------------------------------------
+ // Now we know how much we can spend. How much current will it buy?
+ //--------------------------------------------------------------------------
+
+ u16Cart = (g_ddi_bc_Configuration.u16CurrentRampSlope * u32Time) / 1000;
+
+ //--------------------------------------------------------------------------
+ // Check how the current we can afford stacks up against the target we want.
+ //--------------------------------------------------------------------------
+
+ if ((u16MaxNow + u16Cart) < u16Target) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, we can't afford to buy all the current we
+ // want. Compute the maximum we can afford, and then figure out what we
+ // can actually express in the hardware.
+ //----------------------------------------------------------------------
+
+ u16Target = ddi_bc_hwExpressibleCurrent(u16MaxNow + u16Cart);
+
+ //----------------------------------------------------------------------
+ // Check if the result isn't actually different from what's set in the
+ // the hardware right now.
+ //----------------------------------------------------------------------
+
+ if (u16Target == u16MaxNow) {
+
+ //------------------------------------------------------------------
+ // If control arrives here, we are so poor that we can't yet afford
+ // to buy enough current to make a change in the expressible
+ // hardware setting. Since we didn't spend any of our time, put the
+ // new balance back in the bank.
+ //------------------------------------------------------------------
+
+ g_RampControl.u32AccumulatedTime = u32Time;
+
+ //------------------------------------------------------------------
+ // Leave dispiritedly.
+ //------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+ }
+
+ }
+ //--------------------------------------------------------------------------
+ // If control arrives here, we can afford to buy enough current to get us
+ // all the way to the target. Set it.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_hwSetMaxCurrent(u16Target);
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("Battery charger: setting max charge"
+ "current to: %hdmA\r\n", u16Target);
+#endif
+
+ //--------------------------------------------------------------------------
+ // Flip the power switch on the charging hardware according to the new
+ // current setting.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_hwSetChargerPower(u16Target != 0);
+
+ //--------------------------------------------------------------------------
+ // We're at the target, so we're finished buying current. Zero out the
+ // account.
+ //--------------------------------------------------------------------------
+
+ g_RampControl.u32AccumulatedTime = 0;
+
+ //--------------------------------------------------------------------------
+ // Return success.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+//! @}
diff --git a/drivers/power/stmp37xx/ddi_bc_ramp.h b/drivers/power/stmp37xx/ddi_bc_ramp.h
new file mode 100644
index 000000000000..5111a5496fcf
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_bc_ramp.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_bc
+//! @{
+//
+// Copyright (c) 2004-2005 SigmaTel, Inc.
+//
+//! \file ddi_bc_ramp.h
+//! \brief Internal header file for Battery Charger current ramp controller.
+//! \date 06/2005
+//!
+//! This file contains internal declarations for Battery current ramp
+//! controller.
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _DDI_BC_RAMP_H
+#define _DDI_BC_RAMP_H
+
+////////////////////////////////////////////////////////////////////////////////
+// Prototypes
+////////////////////////////////////////////////////////////////////////////////
+
+extern void ddi_bc_RampReset(void);
+extern uint16_t ddi_bc_RampSetTarget(uint16_t);
+extern uint16_t ddi_bc_RampGetTarget(void);
+extern uint16_t ddi_bc_RampSetLimit(uint16_t);
+extern uint16_t ddi_bc_RampGetLimit(void);
+extern void ddi_bc_RampUpdateAlarms(void);
+extern int ddi_bc_RampGetDieTempAlarm(void);
+extern int ddi_bc_RampGetBatteryTempAlarm(void);
+extern int ddi_bc_RampGetAmbientTempAlarm(void);
+extern ddi_bc_Status_t ddi_bc_RampStep(uint32_t);
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+#endif // _DDI_BC_H
+//! @}
diff --git a/drivers/power/stmp37xx/ddi_bc_sm.c b/drivers/power/stmp37xx/ddi_bc_sm.c
new file mode 100644
index 000000000000..8bd8d2cbb58b
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_bc_sm.c
@@ -0,0 +1,1122 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_bc
+//! @{
+//
+// Copyright (c) 2004-2005 SigmaTel, Inc.
+//
+//! \file ddi_bc_sm.c
+//! \brief Contains the Battery Charger state machine.
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Includes
+////////////////////////////////////////////////////////////////////////////////
+
+#include <mach/ddi_bc.h>
+#include "ddi_bc_internal.h"
+
+#include <linux/delay.h>
+
+////////////////////////////////////////////////////////////////////////////////
+// Definitions
+////////////////////////////////////////////////////////////////////////////////
+
+// This is the minimum time we must charge before we transition from
+// the charging state to the topping off. If we reach the
+// u16ChargingThresholdCurrent charge curent before then, the battery was
+// already full so we can avoid the risk of charging it past .1C for
+// too long.
+
+#define TRANSITION_TO_TOPOFF_MINIMUM_CHARGE_TIME_mS 1 * 60 * 1000 // 1 minute
+
+// If DCDCs are active, we can't complete the charging cycle. Once
+// they are stay off for this amount of time, we will restart the
+// charge cycle.
+#define DCDC_INACTIVITY_TIMER_THRESHOLD 1 * 60 * 1000 // 1 minute
+
+////////////////////////////////////////////////////////////////////////////////
+// Variables
+////////////////////////////////////////////////////////////////////////////////
+
+// The current state.
+
+ddi_bc_State_t g_ddi_bc_State = DDI_BC_STATE_UNINITIALIZED;
+
+// This table contains pointers to the functions that implement states. The
+// table is indexed by state. Note that it's critically important for this
+// table to agree with the state enumeration in ddi_bc.h.
+
+static ddi_bc_Status_t ddi_bc_Uninitialized(void);
+static ddi_bc_Status_t ddi_bc_Broken(void);
+static ddi_bc_Status_t ddi_bc_Disabled(void);
+static ddi_bc_Status_t ddi_bc_WaitingToCharge(void);
+static ddi_bc_Status_t ddi_bc_Conditioning(void);
+static ddi_bc_Status_t ddi_bc_Charging(void);
+static ddi_bc_Status_t ddi_bc_ToppingOff(void);
+static ddi_bc_Status_t ddi_bc_DcdcModeWaitingToCharge(void);
+
+ddi_bc_Status_t(*const (stateFunctionTable[])) (void) = {
+ddi_bc_Uninitialized,
+ ddi_bc_Broken,
+ ddi_bc_Disabled,
+ ddi_bc_WaitingToCharge,
+ ddi_bc_Conditioning,
+ ddi_bc_Charging, ddi_bc_ToppingOff, ddi_bc_DcdcModeWaitingToCharge};
+
+// Used by states that need to watch the time.
+uint32_t g_ddi_bc_u32StateTimer = 0;
+
+// If DCDCs are active, we can't complete the charging cycle. Once
+// they are stay off for this amount of time, we will restart the
+// charge cycle.
+static uint32_t DcdcInactityTimer = DCDC_INACTIVITY_TIMER_THRESHOLD;
+
+// Always attempt to charge on first 5V connection even if DCDCs are ON.
+bool bRestartChargeCycle = true;
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+static uint16_t u16ExternalBatteryPowerVoltageCheck = 0;
+#endif
+
+ddi_bc_BrokenReason_t ddi_bc_gBrokenReason = DDI_BC_BROKEN_UNINITIALIZED;
+
+////////////////////////////////////////////////////////////////////////////////
+// Code
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Transition to the DCDC mode Waiting to Charge state.
+//!
+//! \fntype Function
+//!
+//! This function implements the transition to the DCDC Mode Waiting
+//! to Charge state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Transition to the DCDC mode Waiting to Charge state.
+//!
+//! \fntype Function
+//!
+//! This function implements the transition to the DCDC Mode Waiting
+//! to Charge state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static void TransitionToDcdcModeWaitingToCharge(void)
+{
+
+ //--------------------------------------------------------------------------
+ // Reset the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer = 0;
+
+ //--------------------------------------------------------------------------
+ // Reset the current ramp.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampReset();
+
+ //--------------------------------------------------------------------------
+ // Move to the Waiting to Charge state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_DCDC_MODE_WAITING_TO_CHARGE;
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("Battery charger: now waiting to charge (DCDC Mode)\n");
+#endif
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Transition to the Waiting to Charge state.
+//!
+//! \fntype Function
+//!
+//! This function implements the transition to the Waiting to Charge state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static void TransitionToWaitingToCharge(void)
+{
+
+ //--------------------------------------------------------------------------
+ // Reset the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer = 0;
+
+ //--------------------------------------------------------------------------
+ // Reset the current ramp.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampReset();
+
+ //--------------------------------------------------------------------------
+ // Move to the Waiting to Charge state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_WAITING_TO_CHARGE;
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("Battery charger: now waiting to charge\n");
+#endif
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Transition to the Conditioning state.
+//!
+//! \fntype Function
+//!
+//! This function implements the transition to the Conditioning state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static void TransitionToConditioning(void)
+{
+
+ //--------------------------------------------------------------------------
+ // Reset the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer = 0;
+
+ //--------------------------------------------------------------------------
+ // Set up the current ramp for conditioning.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampSetTarget(g_ddi_bc_Configuration.u16ConditioningCurrent);
+
+ //--------------------------------------------------------------------------
+ // Move to the Conditioning state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_CONDITIONING;
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("Battery charger: now conditioning\n");
+#endif
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Transition to the Charging state.
+//!
+//! \fntype Function
+//!
+//! This function implements the transition to the Charging state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static void TransitionToCharging(void)
+{
+
+ //--------------------------------------------------------------------------
+ // Reset the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer = 0;
+
+ //--------------------------------------------------------------------------
+ // Set up the current ramp for charging.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampSetTarget(g_ddi_bc_Configuration.u16ChargingCurrent);
+
+ //--------------------------------------------------------------------------
+ // We'll be finished charging when the current flow drops below this level.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_hwSetCurrentThreshold(g_ddi_bc_Configuration.
+ u16ChargingThresholdCurrent);
+
+ //--------------------------------------------------------------------------
+ // Move to the Charging state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_CHARGING;
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("Battery charger: now charging\n");
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Transition to the Topping Off state.
+//!
+//! \fntype Function
+//!
+//! This function implements the transition to the Topping Off state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static void TransitionToToppingOff(void)
+{
+
+ //--------------------------------------------------------------------------
+ // Reset the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer = 0;
+
+ //--------------------------------------------------------------------------
+ // Set up the current ramp for topping off.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampSetTarget(g_ddi_bc_Configuration.u16ChargingCurrent);
+
+ //--------------------------------------------------------------------------
+ // Move to the Topping Off state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_TOPPING_OFF;
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("Battery charger: now topping off\n");
+#endif
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Transition to the Broken state.
+//!
+//! \fntype Function
+//!
+//! This function implements the transition to the Broken state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static void TransitionToBroken(void)
+{
+
+ //--------------------------------------------------------------------------
+ // Reset the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer = 0;
+
+ //--------------------------------------------------------------------------
+ // Reset the current ramp.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampReset();
+
+ //--------------------------------------------------------------------------
+ // Move to the Broken state.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_State = DDI_BC_STATE_BROKEN;
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ printk("Battery charger: declaring a broken battery\n");
+#endif
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Uninitialized state function.
+//!
+//! \fntype Function
+//!
+//! This function implements the Uninitialized state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static ddi_bc_Status_t ddi_bc_Uninitialized(void)
+{
+
+ //--------------------------------------------------------------------------
+ // The first order of business is to update alarms.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampUpdateAlarms();
+
+ //--------------------------------------------------------------------------
+ // Increment the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod;
+
+ //--------------------------------------------------------------------------
+ // The only way to leave this state is with a call to ddi_bc_Initialize. So,
+ // calling this state function does nothing.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Broken state function.
+//!
+//! \fntype Function
+//!
+//! This function implements the Broken state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static ddi_bc_Status_t ddi_bc_Broken(void)
+{
+
+ //--------------------------------------------------------------------------
+ // The first order of business is to update alarms.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampUpdateAlarms();
+
+ //--------------------------------------------------------------------------
+ // Increment the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod;
+
+ //--------------------------------------------------------------------------
+ // The only way to leave this state is with a call to ddi_bc_SetFixed. So,
+ // calling this state function does nothing.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Disabled state function.
+//!
+//! \fntype Function
+//!
+//! This function implements the Disabled state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static ddi_bc_Status_t ddi_bc_Disabled(void)
+{
+
+ //--------------------------------------------------------------------------
+ // The first order of business is to update alarms.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampUpdateAlarms();
+
+ //--------------------------------------------------------------------------
+ // Increment the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod;
+
+ //--------------------------------------------------------------------------
+ // The only way to leave this state is with a call to ddi_bc_SetEnable. So,
+ // calling this state function does nothing.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Waitin to Charge state function.
+//!
+//! \fntype Function
+//!
+//! This function implements the Waiting to Charge state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static ddi_bc_Status_t ddi_bc_DcdcModeWaitingToCharge(void)
+{
+
+ uint16_t u16BatteryVoltage;
+
+ //--------------------------------------------------------------------------
+ // The first order of business is to update alarms.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampUpdateAlarms();
+
+ //--------------------------------------------------------------------------
+ // Increment the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod;
+
+ //--------------------------------------------------------------------------
+ // Check if the power supply is present. If not, we're not going anywhere.
+ //--------------------------------------------------------------------------
+
+ if (!ddi_bc_hwPowerSupplyIsPresent()) {
+ TransitionToCharging();
+ return (DDI_BC_STATUS_SUCCESS);
+ }
+ //--------------------------------------------------------------------------
+ // If control arrives here, we're connected to a power supply. Have a look
+ // at the battery voltage.
+ //--------------------------------------------------------------------------
+
+ u16BatteryVoltage = ddi_bc_hwGetBatteryVoltage();
+
+ //--------------------------------------------------------------------------
+ // If topping off has not yet occurred, we do not care if DCDCs are on or not.
+ // If we have already topped off at least once, then we want to delay so that
+ // we give the battery a chance to discharge some instead of constantly topping
+ // it off.
+ //--------------------------------------------------------------------------
+
+ if (ddi_bc_hwIsDcdcOn()) {
+
+ //--------------------------------------------------------------------------
+ // If DCDCs have turned on, restert the DCDCInactivityTimer;
+ //--------------------------------------------------------------------------
+ DcdcInactityTimer = 0;
+
+ //--------------------------------------------------------------------------
+ // If the battery voltage measurement is at or below the LowDcdcBatteryVoltage
+ // level, we should definetly start charging the battery. The
+ // LowDcdcBatteryVoltage value should be low enough to account for IR voltage
+ // drop from the battery under heavy DCDC load.
+ //--------------------------------------------------------------------------
+
+ if (u16BatteryVoltage <
+ g_ddi_bc_Configuration.u16LowDcdcBatteryVoltage_mv) {
+ bRestartChargeCycle = true;
+
+ } else {
+ return (DDI_BC_STATUS_SUCCESS);
+ }
+ } else if (DcdcInactityTimer < DCDC_INACTIVITY_TIMER_THRESHOLD) {
+ DcdcInactityTimer +=
+ g_ddi_bc_Configuration.u32StateMachinePeriod;
+ if (DcdcInactityTimer >= DCDC_INACTIVITY_TIMER_THRESHOLD) {
+ bRestartChargeCycle = true;
+ }
+ }
+
+ if (bRestartChargeCycle) {
+ bRestartChargeCycle = false;
+ // start charging
+ if (u16BatteryVoltage <
+ g_ddi_bc_Configuration.u16ConditioningThresholdVoltage) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, the battery is very low and it needs to be
+ // conditioned.
+ //----------------------------------------------------------------------
+
+ TransitionToConditioning();
+ } else {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, the battery isn't too terribly low.
+ //----------------------------------------------------------------------
+
+ TransitionToCharging();
+ }
+
+ }
+
+ return (DDI_BC_STATUS_SUCCESS);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Waitin to Charge state function.
+//!
+//! \fntype Function
+//!
+//! This function implements the Waiting to Charge state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static ddi_bc_Status_t ddi_bc_WaitingToCharge(void)
+{
+ uint16_t u16BatteryVoltage;
+ //--------------------------------------------------------------------------
+ // The first order of business is to update alarms.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampUpdateAlarms();
+
+ //--------------------------------------------------------------------------
+ // Increment the state timer.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod;
+
+ //--------------------------------------------------------------------------
+ // Check if the power supply is present. If not, we're not going anywhere.
+ //--------------------------------------------------------------------------
+
+ if (!ddi_bc_hwPowerSupplyIsPresent()) {
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ u16ExternalBatteryPowerVoltageCheck = 0;
+#endif
+ return (DDI_BC_STATUS_SUCCESS);
+ }
+ //--------------------------------------------------------------------------
+ // If control arrives here, we're connected to a power supply. Have a look
+ // at the battery voltage.
+ //--------------------------------------------------------------------------
+
+ u16BatteryVoltage = ddi_bc_hwGetBatteryVoltage();
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ if (u16ExternalBatteryPowerVoltageCheck) {
+ if ((u16ExternalBatteryPowerVoltageCheck - u16BatteryVoltage) >
+ 300) {
+ //----------------------------------------------------------------------
+ // If control arrives here, battery voltage has dropped too quickly after
+ // the first charge cycle. We think an external voltage regulator is
+ // connected. If the DCDCs are on and this voltage drops too quickly,
+ // it will cause large droops and possible brownouts so we disable
+ // charging.
+ //----------------------------------------------------------------------
+
+ ddi_bc_gBrokenReason =
+ DDI_BC_BROKEN_EXTERNAL_BATTERY_VOLTAGE_DETECTED;
+
+ TransitionToBroken();
+
+ //----------------------------------------------------------------------
+ // Tell our caller the battery appears to be broken.
+ //----------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_BROKEN);
+ } else {
+ // reset this check
+ u16ExternalBatteryPowerVoltageCheck = 0;
+ }
+
+ }
+#endif
+
+ //--------------------------------------------------------------------------
+ // In addition to 5V, is DCDC on also? If so, swith to DCDC Mode Waiting
+ // to Charge state.
+ //--------------------------------------------------------------------------
+
+ if (ddi_bc_hwIsDcdcOn()) {
+ TransitionToDcdcModeWaitingToCharge();
+ return (DDI_BC_STATUS_SUCCESS);
+ }
+
+ //--------------------------------------------------------------------------
+ // If the battery voltage isn't low, we don't need to be charging it. We
+ // use a 5% margin to decide.
+ //--------------------------------------------------------------------------
+
+ if (!bRestartChargeCycle) {
+ uint16_t x;
+
+ x = u16BatteryVoltage + (u16BatteryVoltage / 20);
+
+ if (x >= g_ddi_bc_Configuration.u16ChargingVoltage)
+ return (DDI_BC_STATUS_SUCCESS);
+
+ }
+
+ bRestartChargeCycle = false;
+ //--------------------------------------------------------------------------
+ // If control arrives here, the battery is low. How low?
+ //--------------------------------------------------------------------------
+
+ if (u16BatteryVoltage <
+ g_ddi_bc_Configuration.u16ConditioningThresholdVoltage) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, the battery is very low and it needs to be
+ // conditioned.
+ //----------------------------------------------------------------------
+
+ TransitionToConditioning();
+
+ } else {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, the battery isn't too terribly low.
+ //----------------------------------------------------------------------
+
+ TransitionToCharging();
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Return success.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Conditioning state function.
+//!
+//! \fntype Function
+//!
+//! This function implements the Conditioning state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static ddi_bc_Status_t ddi_bc_Conditioning(void)
+{
+
+ //--------------------------------------------------------------------------
+ // The first order of business is to update alarms.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampUpdateAlarms();
+
+ //--------------------------------------------------------------------------
+ // If we're not under an alarm, increment the state timer.
+ //--------------------------------------------------------------------------
+
+ if (!ddi_bc_RampGetDieTempAlarm() && !ddi_bc_RampGetBatteryTempAlarm()) {
+ g_ddi_bc_u32StateTimer +=
+ g_ddi_bc_Configuration.u32StateMachinePeriod;
+ }
+ //--------------------------------------------------------------------------
+ // Check if the power supply is still around.
+ //--------------------------------------------------------------------------
+
+ if (!ddi_bc_hwPowerSupplyIsPresent()) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, the power supply has been removed. Go back
+ // and wait.
+ //----------------------------------------------------------------------
+
+ TransitionToWaitingToCharge();
+
+ //----------------------------------------------------------------------
+ // Return success.
+ //----------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+ }
+
+ //--------------------------------------------------------------------------
+ // If control arrives here, we're still connected to a power supply.
+ // Check if a battery is connected. If the voltage rises to high with only
+ // conditioning charge current, we determine that a battery is not connected.
+ // If that is not the case and a battery is connected, check
+ // if the battery voltage indicates it still needs conditioning.
+ //--------------------------------------------------------------------------
+
+// if (ddi_bc_hwGetBatteryVoltage() >= 3900) {
+ if ((ddi_bc_hwGetBatteryVoltage() >
+ g_ddi_bc_Configuration.u16ConditioningMaxVoltage) &&
+ (ddi_power_GetMaxBatteryChargeCurrent() <
+ g_ddi_bc_Configuration.u16ConditioningCurrent)) {
+ //----------------------------------------------------------------------
+ // If control arrives here, voltage has risen too quickly for so
+ // little charge being applied so their must be no battery connected.
+ //----------------------------------------------------------------------
+
+ ddi_bc_gBrokenReason = DDI_BC_BROKEN_NO_BATTERY_DETECTED;
+
+ TransitionToBroken();
+
+ //----------------------------------------------------------------------
+ // Tell our caller the battery appears to be broken.
+ //----------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_BROKEN);
+
+ }
+
+ if (ddi_bc_hwGetBatteryVoltage() >=
+ g_ddi_bc_Configuration.u16ConditioningMaxVoltage) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, this battery no longer needs conditioning.
+ //----------------------------------------------------------------------
+
+ TransitionToCharging();
+
+ //----------------------------------------------------------------------
+ // Return success.
+ //----------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+ }
+ //--------------------------------------------------------------------------
+ // Have we been in this state too long?
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_u32StateTimer >=
+ g_ddi_bc_Configuration.u32ConditioningTimeout) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, we've been here too long.
+ //----------------------------------------------------------------------
+
+ ddi_bc_gBrokenReason = DDI_BC_BROKEN_CHARGING_TIMEOUT;
+
+ TransitionToBroken();
+
+ //----------------------------------------------------------------------
+ // Tell our caller the battery appears to be broken.
+ //----------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_BROKEN);
+
+ }
+ //--------------------------------------------------------------------------
+ // If control arrives here, we're staying in this state. Step the current
+ // ramp.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampStep(g_ddi_bc_Configuration.u32StateMachinePeriod);
+
+ //--------------------------------------------------------------------------
+ // Return success.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Charging state function.
+//!
+//! \fntype Function
+//!
+//! This function implements the Charging state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static ddi_bc_Status_t ddi_bc_Charging(void)
+{
+
+ //--------------------------------------------------------------------------
+ // This variable counts the number of times we've seen the charging status
+ // bit cleared.
+ //--------------------------------------------------------------------------
+
+ static int iStatusCount = 0;
+ bool bIsDcdcOn;
+
+ //--------------------------------------------------------------------------
+ // The first order of business is to update alarms.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampUpdateAlarms();
+
+ //--------------------------------------------------------------------------
+ // If we're not under an alarm, increment the state timer.
+ //--------------------------------------------------------------------------
+
+ if (!ddi_bc_RampGetDieTempAlarm() && !ddi_bc_RampGetBatteryTempAlarm()) {
+ g_ddi_bc_u32StateTimer +=
+ g_ddi_bc_Configuration.u32StateMachinePeriod;
+ }
+ //--------------------------------------------------------------------------
+ // Check if the power supply is still around.
+ //--------------------------------------------------------------------------
+
+ if (!ddi_bc_hwPowerSupplyIsPresent()) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, the power supply has been removed. Go back
+ // and wait.
+ //----------------------------------------------------------------------
+
+ TransitionToWaitingToCharge();
+
+ //----------------------------------------------------------------------
+ // Return success.
+ //----------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+ }
+ //--------------------------------------------------------------------------
+ // If control arrives here, we're still connected to a power supply. We need
+ // to decide now if the battery is still charging, or if it's nearly full.
+ // If it's still charging, we'll stay in this state. Otherwise, we'll move
+ // to the Topping Off state.
+ //
+ // Most of the time, we decide that the battery is still charging simply by
+ // checking if the the actual current flow is above the charging threshold
+ // current (as indicated by the charge status bit). However, if we're
+ // still ramping up to full charging current, the hardware may still be set
+ // to deliver an amount that's less than the threshold. In that case, the
+ // charging status bit would *definitely* show a low charging current, but
+ // that doesn't mean the battery is ready for topping off.
+ //
+ // So, in summary, we will move to the Topping Off state if both of the
+ // following are true:
+ //
+ // 1) The maximum current set in the hardware is greater than the charging
+ // threshold.
+ // -AND-
+ // 2) The actual current flow is also higher than the threshold (as
+ // indicated by the charge status bit).
+ //
+ //--------------------------------------------------------------------------
+
+ bIsDcdcOn = ddi_bc_hwIsDcdcOn();
+ if (bIsDcdcOn) {
+ ddi_bc_hwSetCurrentThreshold(g_ddi_bc_Configuration.
+ u16DdcdModeChargingThresholdCurrent);
+ } else {
+ ddi_bc_hwSetCurrentThreshold(g_ddi_bc_Configuration.
+ u16ChargingThresholdCurrent);
+ }
+
+ {
+ uint16_t u16ActualProgrammedCurrent = ddi_bc_hwGetMaxCurrent();
+
+ //----------------------------------------------------------------------
+ // Get the Maximum current that we will ramp to.
+ //----------------------------------------------------------------------
+
+ //----------------------------------------------------------------------
+ // Not all possible values are expressible by the BATTCHRG_I bitfield.
+ // The following coverts the max current value into the the closest hardware
+ // expressible bitmask equivalent. Then, it converts this back to the actual
+ // decimal current value that this bitmask represents.
+ //----------------------------------------------------------------------
+
+ uint16_t u16CurrentRampTarget = ddi_bc_RampGetTarget();
+
+ if (u16CurrentRampTarget > ddi_bc_RampGetLimit())
+ u16CurrentRampTarget = ddi_bc_RampGetLimit();
+
+ //----------------------------------------------------------------------
+ // Not all possible values are expressible by the BATTCHRG_I bitfield.
+ // The following coverts the max current value into the the closest hardware
+ // expressible bitmask equivalent. Then, it converts this back to the actual
+ // decimal current value that this bitmask represents.
+ //----------------------------------------------------------------------
+
+ u16CurrentRampTarget =
+ ddi_bc_hwExpressibleCurrent(u16CurrentRampTarget);
+
+ //----------------------------------------------------------------------
+ // We want to wait before we check the charge status bit until the ramping
+ // up is complete. Because the charge status bit is noisy, we want to
+ // disregard it until the programmed charge currint in BATTCHRG_I is well
+ // beyond the STOP_ILIMIT value.
+ //----------------------------------------------------------------------
+ if ((u16ActualProgrammedCurrent >= u16CurrentRampTarget) &&
+ !ddi_bc_hwGetChargeStatus()) {
+ uint8_t u8IlimitThresholdLimit;
+ //----------------------------------------------------------------------
+ // If control arrives here, the hardware flag is telling us that the
+ // charging current has fallen below the threshold. We need to see this
+ // happen twice consecutively before we believe it. Increment the count.
+ //----------------------------------------------------------------------
+
+ iStatusCount++;
+
+ //----------------------------------------------------------------------
+ // If we are in DCDC operting mode, we only need this criteria to be true
+ // once before we advance to topping off. In 5V only mode, we want 10
+ // consecutive times before advancing to topping off.
+ //----------------------------------------------------------------------
+
+ if (bIsDcdcOn)
+ u8IlimitThresholdLimit = 1;
+ else
+ u8IlimitThresholdLimit = 10;
+
+ //----------------------------------------------------------------------
+ // How many times in a row have we seen this status bit low?
+ //----------------------------------------------------------------------
+
+ if (iStatusCount >= u8IlimitThresholdLimit) {
+
+ //------------------------------------------------------------------
+ // If control arrives here, we've seen the CHRGSTS bit low too many
+ // times. This means it's time to move back to the waiting to charge
+ // state if DCDCs are present or move to the Topping Off state if
+ // no DCDCs are present. Because we can't measure only the current
+ // going to the battery when the DCDCs are active, we don't know when
+ // to start topping off or how long to top off.
+ //
+ // First, reset the status count for the next time we're in this
+ // state.
+ //------------------------------------------------------------------
+
+ iStatusCount = 0;
+
+#ifdef CONFIG_POWER_SUPPLY_DEBUG
+ u16ExternalBatteryPowerVoltageCheck =
+ ddi_bc_hwGetBatteryVoltage();
+#endif
+
+ if (bIsDcdcOn) {
+ // We will restart the charge cycle once the DCDCs are no
+ // longer present.
+ DcdcInactityTimer = 0;
+
+ TransitionToWaitingToCharge();
+ } else if (g_ddi_bc_u32StateTimer <=
+ TRANSITION_TO_TOPOFF_MINIMUM_CHARGE_TIME_mS)
+ {
+ //------------------------------------------------------------------
+ // If we haven't actually didn't have to charge very long
+ // then the battery was already full. In this case, we do
+ // not top off so that we do not needlessly overcharge the
+ // battery.
+ //------------------------------------------------------------------
+ TransitionToWaitingToCharge();
+ } else {
+
+ //------------------------------------------------------------------
+ // Move to the Topping Off state.
+ //------------------------------------------------------------------
+
+ TransitionToToppingOff();
+ }
+ //------------------------------------------------------------------
+ // Return success.
+ //------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+ }
+
+ } else {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, the battery is still charging. Clear the
+ // status count.
+ //----------------------------------------------------------------------
+
+ iStatusCount = 0;
+
+ }
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Have we been in this state too long?
+ //--------------------------------------------------------------------------
+
+ if (g_ddi_bc_u32StateTimer >= g_ddi_bc_Configuration.u32ChargingTimeout) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, we've been here too long.
+ //----------------------------------------------------------------------
+
+ ddi_bc_gBrokenReason = DDI_BC_BROKEN_CHARGING_TIMEOUT;
+
+ TransitionToBroken();
+
+ //----------------------------------------------------------------------
+ // Tell our caller the battery appears to be broken.
+ //----------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_BROKEN);
+
+ }
+ //--------------------------------------------------------------------------
+ // If control arrives here, we're staying in this state. Step the current
+ // ramp.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampStep(g_ddi_bc_Configuration.u32StateMachinePeriod);
+
+ //--------------------------------------------------------------------------
+ // Return success.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Topping Off state function.
+//!
+//! \fntype Function
+//!
+//! This function implements the Topping Off state.
+//!
+////////////////////////////////////////////////////////////////////////////////
+static ddi_bc_Status_t ddi_bc_ToppingOff(void)
+{
+
+ //--------------------------------------------------------------------------
+ // The first order of business is to update alarms.
+
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampUpdateAlarms();
+
+ //--------------------------------------------------------------------------
+ // Increment the state timer. Notice that, unlike other states, we increment
+ // the state timer whether or not we're under an alarm.
+ //--------------------------------------------------------------------------
+
+ g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod;
+
+ //--------------------------------------------------------------------------
+ // Check if the power supply is still around.
+ //--------------------------------------------------------------------------
+
+ if (!ddi_bc_hwPowerSupplyIsPresent()) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, the power supply has been removed. Go back
+ // and wait.
+ //---------------------------------------------------------------------
+
+ TransitionToWaitingToCharge();
+
+ //----------------------------------------------------------------------
+ // Return success.
+ //----------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Are we done topping off?
+ //--------------------------------------------------------------------------
+ if (g_ddi_bc_u32StateTimer >= g_ddi_bc_Configuration.u32TopOffPeriod) {
+
+ //----------------------------------------------------------------------
+ // If control arrives here, we're done topping off.
+ //----------------------------------------------------------------------
+
+ TransitionToWaitingToCharge();
+
+ }
+ //--------------------------------------------------------------------------
+ // If control arrives here, we're staying in this state. Step the current
+ // ramp.
+ //--------------------------------------------------------------------------
+
+ ddi_bc_RampStep(g_ddi_bc_Configuration.u32StateMachinePeriod);
+
+ //--------------------------------------------------------------------------
+ // Return success.
+ //--------------------------------------------------------------------------
+
+ return (DDI_BC_STATUS_SUCCESS);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+//! @}
diff --git a/drivers/power/stmp37xx/ddi_bc_sm.h b/drivers/power/stmp37xx/ddi_bc_sm.h
new file mode 100644
index 000000000000..0e5fd8a1f144
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_bc_sm.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_bc
+//! @{
+//
+// Copyright (c) 2004-2005 SigmaTel, Inc.
+//
+//! \file ddi_bc_sm.h
+//! \brief Header file for the Battery Charger state machine.
+//! \date 06/2005
+//!
+//! This file contains declarations for the Battery Charger state machine.
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef _DDI_BC_SM_H
+#define _DDI_BC_SM_H
+
+////////////////////////////////////////////////////////////////////////////////
+// Externs
+////////////////////////////////////////////////////////////////////////////////
+
+//! The current state.
+
+extern ddi_bc_State_t g_ddi_bc_State;
+
+//! The state function table.
+
+extern ddi_bc_Status_t(*const (stateFunctionTable[])) (void);
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+#endif // _DDI_BC_H
+//! @}
diff --git a/drivers/power/stmp37xx/ddi_power_battery.c b/drivers/power/stmp37xx/ddi_power_battery.c
new file mode 100644
index 000000000000..b474e53ac9fa
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_power_battery.c
@@ -0,0 +1,1035 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//! \addtogroup ddi_power
+//! @{
+//
+// Copyright(C) 2005 SigmaTel, Inc.
+//
+//! \file ddi_power_battery.c
+//! \brief Implementation file for the power driver battery charger.
+//!
+////////////////////////////////////////////////////////////////////////////////
+// Includes and external references
+////////////////////////////////////////////////////////////////////////////////
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <asm/processor.h> /* cpu_relax */
+#include <mach/hardware.h>
+#include <mach/ddi_bc.h>
+#include <mach/lradc.h>
+#include <mach/regs-power.h>
+#include <mach/regs-lradc.h>
+#include "ddi_bc_internal.h"
+
+//! \brief Base voltage to start battery calculations for LiIon
+#define BATT_BRWNOUT_LIION_BASE_MV 2800
+//! \brief Constant to help with determining whether to round up or
+//! not during calculation
+#define BATT_BRWNOUT_LIION_CEILING_OFFSET_MV 39
+//! \brief Number of mV to add if rounding up in LiIon mode
+#define BATT_BRWNOUT_LIION_LEVEL_STEP_MV 40
+//! \brief Constant value to be calculated by preprocessing
+#define BATT_BRWNOUT_LIION_EQN_CONST \
+ (BATT_BRWNOUT_LIION_BASE_MV - BATT_BRWNOUT_LIION_CEILING_OFFSET_MV)
+//! \brief Base voltage to start battery calculations for Alkaline/NiMH
+#define BATT_BRWNOUT_ALKAL_BASE_MV 800
+//! \brief Constant to help with determining whether to round up or
+//! not during calculation
+#define BATT_BRWNOUT_ALKAL_CEILING_OFFSET_MV 19
+//! \brief Number of mV to add if rounding up in Alkaline/NiMH mode
+#define BATT_BRWNOUT_ALKAL_LEVEL_STEP_MV 20
+//! \brief Constant value to be calculated by preprocessing
+#define BATT_BRWNOUT_ALKAL_EQN_CONST \
+ (BATT_BRWNOUT_ALKAL_BASE_MV - BATT_BRWNOUT_ALKAL_CEILING_OFFSET_MV)
+
+#define GAIN_CORRECTION 1012 // 1.012
+
+#define VBUSVALID_THRESH_2_90V 0x0
+#define VBUSVALID_THRESH_4_00V 0x1
+#define VBUSVALID_THRESH_4_10V 0x2
+#define VBUSVALID_THRESH_4_20V 0x3
+#define VBUSVALID_THRESH_4_30V 0x4
+#define VBUSVALID_THRESH_4_40V 0x5
+#define VBUSVALID_THRESH_4_50V 0x6
+#define VBUSVALID_THRESH_4_60V 0x7
+
+#define LINREG_OFFSET_STEP_BELOW 0x2
+#define BP_POWER_BATTMONITOR_BATT_VAL 16
+#define BP_POWER_CHARGE_BATTCHRG_I 0
+#define BP_POWER_CHARGE_STOP_ILIMIT 8
+
+#define VDD4P2_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+// Globals & Variables
+////////////////////////////////////////////////////////////////////////////////
+
+
+/* Select your 5V Detection method */
+ static ddi_power_5vDetection_t DetectionMethod =
+ DDI_POWER_5V_VDD5V_GT_VDDIO;
+/* static ddi_power_5vDetection_t DetectionMethod = DDI_POWER_5V_VBUSVALID; */
+
+////////////////////////////////////////////////////////////////////////////////
+// Code
+////////////////////////////////////////////////////////////////////////////////
+
+#if 0
+static void dump_regs(void)
+{
+ printk("HW_POWER_CHARGE 0x%08x\n", HW_POWER_CHARGE_RD());
+ printk("HW_POWER_STS 0x%08x\n", HW_POWER_STS_RD());
+ printk("HW_POWER_BATTMONITOR 0x%08x\n", HW_POWER_BATTMONITOR_RD());
+}
+#endif
+
+//! This array maps bit numbers to current increments, as used in the register
+//! fields HW_POWER_CHARGE.STOP_ILIMIT and HW_POWER_CHARGE.BATTCHRG_I.
+static const uint16_t currentPerBit[] = { 10, 20, 50, 100, 200, 400 };
+
+uint16_t ddi_power_convert_current_to_setting(uint16_t u16Current)
+{
+ int i;
+ uint16_t u16Mask;
+ uint16_t u16Setting = 0;
+
+ // Scan across the bit field, adding in current increments.
+ u16Mask = (0x1 << 5);
+
+ for (i = 5; (i >= 0) && (u16Current > 0); i--, u16Mask >>= 1) {
+ if (u16Current >= currentPerBit[i]) {
+ u16Current -= currentPerBit[i];
+ u16Setting |= u16Mask;
+ }
+ }
+
+ // Return the result.
+ return(u16Setting);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//! See hw_power.h for details.
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_power_convert_setting_to_current(uint16_t u16Setting)
+{
+ int i;
+ uint16_t u16Mask;
+ uint16_t u16Current = 0;
+
+ // Scan across the bit field, adding in current increments.
+ u16Mask = (0x1 << 5);
+
+ for (i = 5; i >= 0; i--, u16Mask >>= 1) {
+ if (u16Setting & u16Mask) u16Current += currentPerBit[i];
+ }
+
+ // Return the result.
+ return(u16Current);
+}
+
+void ddi_power_Enable5vDetection(void)
+{
+ // Disable hardware power down when 5V is inserted or removed
+ HW_POWER_5VCTRL_CLR(BM_POWER_5VCTRL_PWDN_5VBRNOUT);
+
+ /* Enabling VBUSVALID hardware detection even if VDD5V_GT_VDDIO
+ * is the detection method being used for 5V status (hardware
+ * or software). This is in case any other drivers (such as
+ * USB) are specifically monitoring VBUSVALID status
+ */
+ HW_POWER_5VCTRL_SET(BM_POWER_5VCTRL_VBUSVALID_5VDETECT);
+
+ // Set 5V detection threshold to 4.3V for VBUSVALID.
+ HW_POWER_5VCTRL_SET(
+ BF_POWER_5VCTRL_VBUSVALID_TRSH(VBUSVALID_THRESH_4_30V));
+
+ // gotta set LINREG_OFFSET to STEP_BELOW according to manual
+ HW_POWER_VDDIOCTRL_SET(
+ BF_POWER_VDDIOCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW));
+ HW_POWER_VDDACTRL_SET(
+ BF_POWER_VDDACTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW));
+ HW_POWER_VDDDCTRL_SET(
+ BF_POWER_VDDDCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW));
+
+ /* Clear vbusvalid interrupt flag */
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_VBUSVALID_IRQ);
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ);
+
+
+ /* enable 5V Detection interrupt vbusvalid irq */
+ switch (DetectionMethod) {
+ case DDI_POWER_5V_VBUSVALID:
+ /* Check VBUSVALID for 5V present */
+ HW_POWER_CTRL_SET(BM_POWER_CTRL_ENIRQ_VBUS_VALID);
+ break;
+ case DDI_POWER_5V_VDD5V_GT_VDDIO:
+ /* Check VDD5V_GT_VDDIO for 5V present */
+ HW_POWER_CTRL_SET(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO);
+ break;
+ }
+}
+
+/*
+ * This function prepares the hardware for a 5V-to-battery handoff. It assumes
+ * the current configuration is using 5V as the power source. The 5V
+ * interrupt will be set up for a 5V removal.
+ */
+void ddi_power_enable_5v_to_battery_handoff(void)
+{
+ /* Clear vbusvalid interrupt flag */
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_VBUSVALID_IRQ);
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ);
+
+ /* detect 5v unplug */
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_POLARITY_VBUSVALID);
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO);
+
+#ifndef VDD4P2_ENABLED
+ // Enable automatic transition to DCDC
+ HW_POWER_5VCTRL_SET(BM_POWER_5VCTRL_DCDC_XFER);
+#endif
+}
+
+/*
+ * This function will handle all the power rail transitions necesarry to power
+ * the chip from the battery when it was previously powered from the 5V power
+ * source.
+ */
+void ddi_power_execute_5v_to_battery_handoff(void)
+{
+
+
+#ifdef VDD4P2_ENABLED
+
+ HW_POWER_DCDC4P2_CLR(BM_POWER_DCDC4P2_ENABLE_DCDC |
+ BM_POWER_DCDC4P2_ENABLE_4P2);
+ HW_POWER_5VCTRL_SET(BM_POWER_5VCTRL_PWD_CHARGE_4P2);
+#else
+ // VDDD has different configurations depending on the battery type
+ // and battery level.
+
+ // For LiIon battery, we will use the DCDC to power VDDD.
+ // Use LinReg offset for DCDC mode.
+ HW_POWER_VDDDCTRL_SET(
+ BF_POWER_VDDDCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW));
+ // Turn on the VDDD DCDC output and turn off the VDDD LinReg output.
+ HW_POWER_VDDDCTRL_CLR(BM_POWER_VDDDCTRL_DISABLE_FET);
+ HW_POWER_VDDDCTRL_CLR(BM_POWER_VDDDCTRL_ENABLE_LINREG);
+ // Make sure stepping is enabled when using DCDC.
+ HW_POWER_VDDDCTRL_CLR(BM_POWER_VDDDCTRL_DISABLE_STEPPING);
+
+ // Power VDDA and VDDIO from the DCDC.
+
+ // Use LinReg offset for DCDC mode.
+ HW_POWER_VDDACTRL_SET(
+ BF_POWER_VDDACTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW));
+ // Turn on the VDDA DCDC converter output and turn off LinReg output.
+ HW_POWER_VDDACTRL_CLR(BM_POWER_VDDACTRL_DISABLE_FET);
+ HW_POWER_VDDACTRL_CLR(BM_POWER_VDDACTRL_ENABLE_LINREG);
+ // Make sure stepping is enabled when using DCDC.
+ HW_POWER_VDDACTRL_CLR(BM_POWER_VDDACTRL_DISABLE_STEPPING);
+
+ // Use LinReg offset for DCDC mode.
+ HW_POWER_VDDIOCTRL_SET(
+ BF_POWER_VDDIOCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW));
+ // Turn on the VDDIO DCDC output and turn on the LinReg output.
+ HW_POWER_VDDIOCTRL_CLR(BM_POWER_VDDIOCTRL_DISABLE_FET);
+ HW_POWER_5VCTRL_CLR(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO);
+ // Make sure stepping is enabled when using DCDC.
+ HW_POWER_VDDIOCTRL_CLR(BM_POWER_VDDIOCTRL_DISABLE_STEPPING);
+#endif
+}
+
+/*
+ * This function sets up battery-to-5V handoff. The power switch from
+ * battery to 5V is automatic. This funtion enables the 5V present detection
+ * such that the 5V interrupt can be generated if it is enabled. (The interrupt
+ * handler can inform software the 5V present event.) To deal with noise or
+ * a high current, this function enables DCDC1/2 based on the battery mode.
+ */
+void ddi_power_enable_battery_to_5v_handoff(void)
+{
+ /* Clear vbusvalid interrupt flag */
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_VBUSVALID_IRQ);
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ);
+
+ /* detect 5v plug-in */
+ HW_POWER_CTRL_SET(BM_POWER_CTRL_POLARITY_VBUSVALID);
+ HW_POWER_CTRL_SET(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO);
+
+#ifndef VDD4P2_ENABLED
+ // Force current from 5V to be zero by disabling its entry source.
+ HW_POWER_5VCTRL_SET(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO);
+#endif
+ // Allow DCDC be to active when 5V is present.
+ HW_POWER_5VCTRL_SET(BM_POWER_5VCTRL_ENABLE_DCDC);
+}
+
+/* This function handles the transitions on each of theVDD5V_GT_VDDIO power
+ * rails necessary to power the chip from the 5V power supply when it was
+ * previously powered from the battery power supply.
+ */
+void ddi_power_execute_battery_to_5v_handoff(void)
+{
+
+#ifdef VDD4P2_ENABLED
+
+ bool orig_vbusvalid_5vdetect = false;
+ bool orig_pwd_bo = false;
+ uint8_t orig_vbusvalid_threshold;
+
+
+ /* recording orignal values that will be modified
+ * temporarlily to handle a chip bug. See chip errata
+ * for CQ ENGR00115837
+ */
+ orig_vbusvalid_threshold =
+ (__raw_readl(HW_POWER_5VCTRL_ADDR)
+ & BM_POWER_5VCTRL_VBUSVALID_TRSH)
+ >> BP_POWER_5VCTRL_VBUSVALID_TRSH;
+
+ if (__raw_readl(HW_POWER_5VCTRL_ADDR) &
+ BM_POWER_5VCTRL_VBUSVALID_5VDETECT)
+ orig_vbusvalid_5vdetect = true;
+
+ if (__raw_readl(HW_POWER_MINPWR_ADDR) &
+ BM_POWER_MINPWR_PWD_BO)
+ orig_pwd_bo = true;
+
+ /* disable mechanisms that get erroneously tripped by
+ * when setting the DCDC4P2 EN_DCDC
+ */
+ HW_POWER_5VCTRL_CLR(BM_POWER_5VCTRL_VBUSVALID_5VDETECT);
+ HW_POWER_5VCTRL_CLR(BF_POWER_5VCTRL_VBUSVALID_TRSH(0x7));
+ HW_POWER_5VCTRL_SET(BM_POWER_MINPWR_PWD_BO);
+
+ HW_POWER_DCDC4P2_SET(BM_POWER_DCDC4P2_ENABLE_4P2);
+
+
+ /* as a todo, we'll want to ramp up the charge current first
+ * to minimize disturbances on the VDD5V rail
+ */
+ ddi_power_SetChargerPowered(1);
+
+ /* Until the previous todo is completed, we'll want to give a delay
+ * to allow the charging up of the 4p2 capacitor.
+ */
+ udelay(10);
+
+
+ HW_POWER_DCDC4P2_SET(BM_POWER_DCDC4P2_ENABLE_DCDC);
+
+ udelay(20);
+ /* coming from a known value of 0 so this is ok */
+ HW_POWER_5VCTRL_SET(BF_POWER_5VCTRL_VBUSVALID_TRSH(
+ orig_vbusvalid_threshold));
+
+ if (orig_vbusvalid_5vdetect)
+ HW_POWER_5VCTRL_SET(BM_POWER_5VCTRL_VBUSVALID_5VDETECT);
+
+
+ if (!orig_pwd_bo)
+ HW_POWER_5VCTRL_CLR(BM_POWER_MINPWR_PWD_BO);
+
+
+
+
+
+#else
+
+ // Disable the DCDC during 5V connections.
+ HW_POWER_5VCTRL_CLR(BM_POWER_5VCTRL_ENABLE_DCDC);
+
+ // Power the VDDD/VDDA/VDDIO rail from the linear regulator. The DCDC
+ // is ready to automatically power the chip when 5V is removed.
+ // Use this configuration when powering from 5V
+
+ // Use LinReg offset for LinReg mode
+ HW_POWER_VDDDCTRL_SET(
+ BF_POWER_VDDDCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW));
+ // Turn on the VDDD LinReg and turn on the VDDD DCDC output. The
+ // ENABLE_DCDC must be cleared to avoid LinReg and DCDC conflict.
+ HW_POWER_VDDDCTRL_SET(BM_POWER_VDDDCTRL_ENABLE_LINREG);
+ HW_POWER_VDDDCTRL_CLR(BM_POWER_VDDDCTRL_DISABLE_FET);
+ // Make sure stepping is disabled when using linear regulators
+ HW_POWER_VDDDCTRL_SET(BM_POWER_VDDDCTRL_DISABLE_STEPPING);
+
+ // Use LinReg offset for LinReg mode
+ HW_POWER_VDDACTRL_SET(
+ BF_POWER_VDDACTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW));
+ // Turn on the VDDA LinReg output and prepare the DCDC for transfer.
+ // ENABLE_DCDC must be clear to avoid DCDC and LinReg conflict.
+ HW_POWER_VDDACTRL_SET(BM_POWER_VDDACTRL_ENABLE_LINREG);
+ HW_POWER_VDDACTRL_CLR(BM_POWER_VDDACTRL_DISABLE_FET);
+ // Make sure stepping is disabled when using linear regulators
+ HW_POWER_VDDACTRL_SET(BM_POWER_VDDACTRL_DISABLE_STEPPING);
+
+ // Use LinReg offset for LinReg mode.
+ HW_POWER_VDDIOCTRL_SET(
+ BF_POWER_VDDIOCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW));
+ // Turn on the VDDIO LinReg output and prepare the VDDIO DCDC output.
+ // ENABLE_DCDC must be cleared to prevent DCDC and LinReg conflict.
+ HW_POWER_VDDIOCTRL_CLR(BM_POWER_VDDIOCTRL_DISABLE_FET);
+ HW_POWER_5VCTRL_CLR(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO);
+ // Make sure stepping is disabled when using DCDC.
+ HW_POWER_VDDIOCTRL_SET(BM_POWER_VDDIOCTRL_DISABLE_STEPPING);
+#endif
+}
+
+void ddi_power_init_handoff(void)
+{
+ /* The following settings give optimal power supply capability */
+
+
+
+ // enable 5v presence detection
+ ddi_power_Enable5vDetection();
+
+ if (ddi_power_Get5vPresentFlag())
+ /* It's 5V mode, enable 5V-to-battery handoff */
+ ddi_power_enable_5v_to_battery_handoff();
+ else
+ /* It's battery mode, enable battery-to-5V handoff */
+ ddi_power_enable_battery_to_5v_handoff();
+
+ // Finally enable the battery adjust
+ HW_POWER_BATTMONITOR_SET(BM_POWER_BATTMONITOR_EN_BATADJ);
+}
+
+int ddi_power_init_battery(void)
+{
+ int ret;
+
+
+ /* the following code to enable automatic battery measurement
+ * should have already been enabled in the boot prep files. Not
+ * sure if this is necessary or possibly suceptible to mis-coordination
+ */
+ // Init LRADC channel 7
+ ret = hw_lradc_init_ladder(BATTERY_VOLTAGE_CH,
+ LRADC_DELAY_TRIGGER_BATTERY,
+ 200);
+ if (ret) {
+ printk(KERN_ERR "%s: hw_lradc_init_ladder failed\n", __func__);
+ return ret;
+ }
+
+ HW_LRADC_CONVERSION_SET(BM_LRADC_CONVERSION_AUTOMATIC);
+
+ // Set li-ion mode
+ HW_LRADC_CONVERSION_SET(BF_LRADC_CONVERSION_SCALE_FACTOR(2));
+
+ // Turn off divide-by-two - we already have a divide-by-four
+ // as part of the hardware
+ HW_LRADC_CTRL2_CLR(
+ BF_LRADC_CTRL2_DIVIDE_BY_TWO(1 << BATTERY_VOLTAGE_CH));
+
+ HW_POWER_CHARGE_SET(BM_POWER_CHARGE_ENABLE_FAULT_DETECT);
+
+ // kick off the trigger
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_BATTERY, 1);
+
+ HW_POWER_LOOPCTRL_SET(BM_POWER_LOOPCTRL_RCSCALE_THRESH);
+ HW_POWER_LOOPCTRL_SET(BF_POWER_LOOPCTRL_EN_RCSCALE(3));
+
+ HW_POWER_MINPWR_CLR(BM_POWER_MINPWR_HALF_FETS);
+ HW_POWER_MINPWR_SET(BM_POWER_MINPWR_DOUBLE_FETS);
+
+ /* prepare handoff */
+ ddi_power_init_handoff();
+
+ return 0;
+}
+
+/*
+ * Use the the lradc7 channel dedicated for battery voltage measurement to
+ * get the die temperature from on-chip sensor.
+ */
+uint16_t MeasureInternalDieTemperature(void)
+{
+ uint32_t ch8Value, ch9Value;
+
+ /* power up internal tep sensor block */
+ HW_LRADC_CTRL2_CLR(BM_LRADC_CTRL2_TEMPSENSE_PWD);
+
+ /* mux to the lradc 8th temp channel */
+ HW_LRADC_CTRL4_CLR(BF_LRADC_CTRL4_LRADC7SELECT(0xF));
+ HW_LRADC_CTRL4_SET(BF_LRADC_CTRL4_LRADC7SELECT(8));
+
+ /* Clear the interrupt flag */
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC7_IRQ);
+ HW_LRADC_CTRL0_SET(BF_LRADC_CTRL0_SCHEDULE(1 << BATTERY_VOLTAGE_CH));
+ // Wait for conversion complete
+ while (!(HW_LRADC_CTRL1_RD() & BM_LRADC_CTRL1_LRADC7_IRQ))
+ cpu_relax();
+ /* Clear the interrupt flag again */
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC7_IRQ);
+ // read temperature value and clr lradc
+ ch8Value = HW_LRADC_CHn_RD(BATTERY_VOLTAGE_CH) & BM_LRADC_CHn_VALUE;
+ HW_LRADC_CHn_CLR(BATTERY_VOLTAGE_CH, BM_LRADC_CHn_VALUE);
+
+ /* mux to the lradc 9th temp channel */
+ HW_LRADC_CTRL4_CLR(BF_LRADC_CTRL4_LRADC7SELECT(0xF));
+ HW_LRADC_CTRL4_SET(BF_LRADC_CTRL4_LRADC7SELECT(9));
+
+ /* Clear the interrupt flag */
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC7_IRQ);
+ HW_LRADC_CTRL0_SET(BF_LRADC_CTRL0_SCHEDULE(1 << BATTERY_VOLTAGE_CH));
+ // Wait for conversion complete
+ while (!(HW_LRADC_CTRL1_RD() & BM_LRADC_CTRL1_LRADC7_IRQ))
+ cpu_relax();
+ /* Clear the interrupt flag */
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC7_IRQ);
+ // read temperature value
+ ch9Value = HW_LRADC_CHn_RD(BATTERY_VOLTAGE_CH) & BM_LRADC_CHn_VALUE;
+ HW_LRADC_CHn_CLR(BATTERY_VOLTAGE_CH, BM_LRADC_CHn_VALUE);
+
+ /* power down temp sensor block */
+ HW_LRADC_CTRL2_SET(BM_LRADC_CTRL2_TEMPSENSE_PWD);
+
+ /* mux back to the lradc 7th battery voltage channel */
+ HW_LRADC_CTRL4_CLR(BF_LRADC_CTRL4_LRADC7SELECT(0xF));
+ HW_LRADC_CTRL4_SET(BF_LRADC_CTRL4_LRADC7SELECT(7));
+
+ /* Clear the interrupt flag */
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC7_IRQ);
+ HW_LRADC_CTRL0_SET(BF_LRADC_CTRL0_SCHEDULE(1 << BATTERY_VOLTAGE_CH));
+ // Wait for conversion complete
+ while (!(HW_LRADC_CTRL1_RD() & BM_LRADC_CTRL1_LRADC7_IRQ))
+ cpu_relax();
+ /* Clear the interrupt flag */
+ HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC7_IRQ);
+
+ return (uint16_t)((ch9Value-ch8Value)*GAIN_CORRECTION/4000);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//! Name: ddi_power_GetBatteryMode
+//!
+//! \brief
+////////////////////////////////////////////////////////////////////////////////
+ddi_power_BatteryMode_t ddi_power_GetBatteryMode(void)
+{
+#if 0
+ return (HW_POWER_STS_RD() & BM_POWER_STS_MODE) ?
+ DDI_POWER_BATT_MODE_ALKALINE_NIMH :
+ DDI_POWER_BATT_MODE_LIION;
+#endif
+ return DDI_POWER_BATT_MODE_LIION;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//! Name: ddi_power_GetBatteryChargerEnabled
+//!
+//! \brief
+////////////////////////////////////////////////////////////////////////////////
+bool ddi_power_GetBatteryChargerEnabled(void)
+{
+#if 0
+ return (HW_POWER_STS_RD() & BM_POWER_STS_BATT_CHRG_PRESENT) ? 1 : 0;
+#endif
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report if the charger hardware power is on.
+//!
+//! \fntype Function
+//!
+//! This function reports if the charger hardware power is on.
+//!
+//! \retval Zero if the charger hardware is not powered. Non-zero otherwise.
+//!
+//! Note that the bit we're looking at is named PWD_BATTCHRG. The "PWD"
+//! stands for "power down". Thus, when the bit is set, the battery charger
+//! hardware is POWERED DOWN.
+////////////////////////////////////////////////////////////////////////////////
+bool ddi_power_GetChargerPowered(void)
+{
+ return (HW_POWER_CHARGE_RD() & BM_POWER_CHARGE_PWD_BATTCHRG) ? 0 : 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Turn the charging hardware on or off.
+//!
+//! \fntype Function
+//!
+//! This function turns the charging hardware on or off.
+//!
+//! \param[in] on Indicates whether the charging hardware should be on or off.
+//!
+//! Note that the bit we're looking at is named PWD_BATTCHRG. The "PWD"
+//! stands for "power down". Thus, when the bit is set, the battery charger
+//! hardware is POWERED DOWN.
+////////////////////////////////////////////////////////////////////////////////
+void ddi_power_SetChargerPowered(bool bPowerOn)
+{
+ // Hit the battery charge power switch.
+ if (bPowerOn) {
+ HW_POWER_CHARGE_CLR(BM_POWER_CHARGE_PWD_BATTCHRG);
+ HW_POWER_5VCTRL_CLR(BM_POWER_5VCTRL_PWD_CHARGE_4P2);
+ } else {
+ HW_POWER_CHARGE_SET(BM_POWER_CHARGE_PWD_BATTCHRG);
+#ifndef VDD4P2_ENABLED
+ HW_POWER_5VCTRL_SET(BM_POWER_5VCTRL_PWD_CHARGE_4P2);
+#endif
+ }
+
+//#ifdef CONFIG_POWER_SUPPLY_DEBUG
+#if 0
+ printk("Battery charger: charger %s\n", bPowerOn ? "ON!" : "OFF");
+ dump_regs();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Reports if the charging current has fallen below the threshold.
+//!
+//! \fntype Function
+//!
+//! This function reports if the charging current that the battery is accepting
+//! has fallen below the threshold.
+//!
+//! Note that this bit is regarded by the hardware guys as very slightly
+//! unreliable. They recommend that you don't believe a value of zero until
+//! you've sampled it twice.
+//!
+//! \retval Zero if the battery is accepting less current than indicated by the
+//! charging threshold. Non-zero otherwise.
+//!
+////////////////////////////////////////////////////////////////////////////////
+int ddi_power_GetChargeStatus(void)
+{
+ return (HW_POWER_STS_RD() & BM_POWER_STS_CHRGSTS) ? 1 : 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Battery Voltage
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the voltage across the battery.
+//!
+//! \fntype Function
+//!
+//! This function reports the voltage across the battery. Should return a
+//! value in range ~3000 - 4200 mV.
+//!
+//! \retval The voltage across the battery, in mV.
+//!
+////////////////////////////////////////////////////////////////////////////////
+
+//! \brief Constant value for 8mV steps used in battery translation
+#define BATT_VOLTAGE_8_MV 8
+
+uint16_t ddi_power_GetBattery(void)
+{
+ uint32_t u16BattVolt;
+
+ // Get the raw result of battery measurement
+ u16BattVolt = HW_POWER_BATTMONITOR_RD();
+ u16BattVolt &= BM_POWER_BATTMONITOR_BATT_VAL;
+ u16BattVolt >>= BP_POWER_BATTMONITOR_BATT_VAL;
+
+ // Adjust for 8-mV LSB resolution and return
+ u16BattVolt *= BATT_VOLTAGE_8_MV;
+
+//#ifdef CONFIG_POWER_SUPPLY_DEBUG
+#if 0
+ printk("Battery charger: %u mV\n", u16BattVolt);
+#endif
+
+ return u16BattVolt;
+}
+
+#if 0
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report the voltage across the battery.
+//!
+//! \fntype Function
+//!
+//! This function reports the voltage across the battery.
+//!
+//! \retval The voltage across the battery, in mV.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_power_GetBatteryBrownout(void)
+{
+ uint32_t u16BatteryBrownoutLevel;
+
+ // Get battery brownout level
+ u16BatteryBrownoutLevel = HW_POWER_BATTMONITOR_RD();
+ u16BatteryBrownoutLevel &= BM_POWER_BATTMONITOR_BRWNOUT_LVL;
+ u16BatteryBrownoutLevel >>= BP_POWER_BATTMONITOR_BRWNOUT_LVL;
+
+ // Calculate battery brownout level
+ switch (ddi_power_GetBatteryMode()) {
+ case DDI_POWER_BATT_MODE_LIION:
+ u16BatteryBrownoutLevel *= BATT_BRWNOUT_LIION_LEVEL_STEP_MV;
+ u16BatteryBrownoutLevel += BATT_BRWNOUT_LIION_BASE_MV;
+ break;
+ case DDI_POWER_BATT_MODE_ALKALINE_NIMH:
+ u16BatteryBrownoutLevel *= BATT_BRWNOUT_ALKAL_LEVEL_STEP_MV;
+ u16BatteryBrownoutLevel += BATT_BRWNOUT_ALKAL_BASE_MV;
+ break;
+ default:
+ u16BatteryBrownoutLevel = 0;
+ break;
+ }
+ return u16BatteryBrownoutLevel;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Set battery brownout level
+//!
+//! \fntype Reentrant Function
+//!
+//! This function sets the battery brownout level in millivolt. It transforms the
+//! input brownout value from millivolts to the hardware register bit field value
+//! taking the ceiling value in the calculation.
+//!
+//! \param[in] u16BattBrownout_mV Battery battery brownout level in mV
+//!
+//! \return SUCCESS
+//!
+////////////////////////////////////////////////////////////////////////////////
+int ddi_power_SetBatteryBrownout(uint16_t u16BattBrownout_mV)
+{
+ int16_t i16BrownoutLevel;
+ int ret = 0;
+
+ // Calculate battery brownout level
+ switch (ddi_power_GetBatteryMode()) {
+ case DDI_POWER_BATT_MODE_LIION:
+ i16BrownoutLevel = u16BattBrownout_mV -
+ BATT_BRWNOUT_LIION_EQN_CONST;
+ i16BrownoutLevel /= BATT_BRWNOUT_LIION_LEVEL_STEP_MV;
+ break;
+ case DDI_POWER_BATT_MODE_ALKALINE_NIMH:
+ i16BrownoutLevel = u16BattBrownout_mV -
+ BATT_BRWNOUT_ALKAL_EQN_CONST;
+ i16BrownoutLevel /= BATT_BRWNOUT_ALKAL_LEVEL_STEP_MV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ // Do a check to make sure nothing went wrong.
+ if (i16BrownoutLevel <= 0x0f) {
+ //Write the battery brownout level
+ HW_POWER_BATTMONITOR_SET(
+ BF_POWER_BATTMONITOR_BRWNOUT_LVL(i16BrownoutLevel));
+ } else
+ ret = -EINVAL;
+
+ return ret;
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Currents
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+//! Name: ddi_power_SetBiasCurrentSource
+//!
+//! \brief
+////////////////////////////////////////////////////////////////////////////////
+int ddi_power_SetBiasCurrentSource(ddi_power_BiasCurrentSource_t eSource)
+{
+ switch (eSource) {
+ case DDI_POWER_INTERNAL_BIAS_CURRENT:
+ HW_POWER_CHARGE_SET(BM_POWER_CHARGE_USE_EXTERN_R);
+ break;
+ case DDI_POWER_EXTERNAL_BIAS_CURRENT:
+ HW_POWER_CHARGE_CLR(BM_POWER_CHARGE_USE_EXTERN_R);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//! Name: ddi_power_GetBiasCurrentSource
+//!
+//! \brief
+////////////////////////////////////////////////////////////////////////////////
+ddi_power_BiasCurrentSource_t ddi_power_GetBiasCurrentSource(void)
+{
+ return (HW_POWER_CHARGE_RD() & BM_POWER_CHARGE_USE_EXTERN_R) ?
+ DDI_POWER_INTERNAL_BIAS_CURRENT :
+ DDI_POWER_EXTERNAL_BIAS_CURRENT;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//! Name: ddi_power_SetMaxBatteryChargeCurrent
+//!
+//! \brief
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_power_SetMaxBatteryChargeCurrent(uint16_t u16MaxCur)
+{
+ uint32_t u16OldSetting;
+ uint32_t u16NewSetting;
+ uint32_t u16ToggleMask;
+
+ // Get the old setting.
+ u16OldSetting = (HW_POWER_CHARGE_RD() & BM_POWER_CHARGE_BATTCHRG_I) >>
+ BP_POWER_CHARGE_BATTCHRG_I;
+
+ // Convert the new threshold into a setting.
+ u16NewSetting = ddi_power_convert_current_to_setting(u16MaxCur);
+
+ // Compute the toggle mask.
+ u16ToggleMask = u16OldSetting ^ u16NewSetting;
+
+ // Write to the toggle register.
+ HW_POWER_CHARGE_TOG(u16ToggleMask << BP_POWER_CHARGE_BATTCHRG_I);
+
+ // Tell the caller what current we're set at now.
+ return ddi_power_convert_setting_to_current(u16NewSetting);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//! Name: ddi_power_GetMaxBatteryChargeCurrent
+//!
+//! \brief
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_power_GetMaxBatteryChargeCurrent(void)
+{
+ uint32_t u8Bits;
+
+ // Get the raw data from register
+ u8Bits = (HW_POWER_CHARGE_RD() & BM_POWER_CHARGE_BATTCHRG_I) >>
+ BP_POWER_CHARGE_BATTCHRG_I;
+
+ // Translate raw data to current (in mA) and return it
+ return ddi_power_convert_setting_to_current(u8Bits);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//! Name: ddi_power_GetMaxChargeCurrent
+//!
+//! \brief
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_power_SetBatteryChargeCurrentThreshold(uint16_t u16Thresh)
+{
+ uint32_t u16OldSetting;
+ uint32_t u16NewSetting;
+ uint32_t u16ToggleMask;
+
+ //-------------------------------------------------------------------
+ // See ddi_power_SetMaxBatteryChargeCurrent for an explanation of
+ // why we're using the toggle register here.
+ //
+ // Since this function doesn't have any major hardware effect,
+ // we could use the usual macros for writing to this bit field. But,
+ // for the sake of parallel construction and any potentially odd
+ // effects on the status bit, we use the toggle register in the same
+ // way as ddi_bc_hwSetMaxCurrent.
+ //-------------------------------------------------------------------
+
+ //-------------------------------------------------------------------
+ // The threshold hardware can't express as large a range as the max
+ // current setting, but we can use the same functions as long as we
+ // add an extra check here.
+ //
+ // Thresholds larger than 180mA can't be expressed.
+ //-------------------------------------------------------------------
+
+ if (u16Thresh > 180)
+ u16Thresh = 180;
+
+ ////////////////////////////////////////
+ // Create the mask
+ ////////////////////////////////////////
+
+ // Get the old setting.
+ u16OldSetting = (HW_POWER_CHARGE_RD() & BM_POWER_CHARGE_STOP_ILIMIT) >>
+ BP_POWER_CHARGE_STOP_ILIMIT;
+
+ // Convert the new threshold into a setting.
+ u16NewSetting = ddi_power_convert_current_to_setting(u16Thresh);
+
+ // Compute the toggle mask.
+ u16ToggleMask = u16OldSetting ^ u16NewSetting;
+
+ /////////////////////////////////////////
+ // Write to the register
+ /////////////////////////////////////////
+
+ // Write to the toggle register.
+ HW_POWER_CHARGE_TOG(BF_POWER_CHARGE_STOP_ILIMIT(u16ToggleMask));
+
+ // Tell the caller what current we're set at now.
+ return ddi_power_convert_setting_to_current(u16NewSetting);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//! Name: ddi_power_GetBatteryChargeCurrentThreshold
+//!
+//! \brief
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_power_GetBatteryChargeCurrentThreshold(void)
+{
+ uint32_t u16Threshold;
+
+ u16Threshold = (HW_POWER_CHARGE_RD() & BM_POWER_CHARGE_STOP_ILIMIT) >>
+ BP_POWER_CHARGE_STOP_ILIMIT;
+
+ return ddi_power_convert_setting_to_current(u16Threshold);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Conversion
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Compute the actual current expressible in the hardware.
+//!
+//! \fntype Function
+//!
+//! Given a desired current, this function computes the actual current
+//! expressible in the hardware.
+//!
+//! Note that the hardware has a minimum resolution of 10mA and a maximum
+//! expressible value of 780mA (see the data sheet for details). If the given
+//! current cannot be expressed exactly, then the largest expressible smaller
+//! value will be used.
+//!
+//! \param[in] u16Current The current of interest.
+//!
+//! \retval The corresponding current in mA.
+//!
+////////////////////////////////////////////////////////////////////////////////
+uint16_t ddi_power_ExpressibleCurrent(uint16_t u16Current)
+{
+ return ddi_power_convert_setting_to_current(
+ ddi_power_convert_current_to_setting(u16Current));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//! Name: ddi_power_Get5VPresent
+//!
+//! \brief
+////////////////////////////////////////////////////////////////////////////////
+bool ddi_power_Get5vPresentFlag(void)
+{
+ switch (DetectionMethod) {
+ case DDI_POWER_5V_VBUSVALID:
+ // Check VBUSVALID for 5V present
+ return ((HW_POWER_STS_RD() & BM_POWER_STS_VBUSVALID) != 0);
+ case DDI_POWER_5V_VDD5V_GT_VDDIO:
+ // Check VDD5V_GT_VDDIO for 5V present
+ return ((HW_POWER_STS_RD() & BM_POWER_STS_VDD5V_GT_VDDIO) != 0);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Report on the die temperature.
+//!
+//! \fntype Function
+//!
+//! This function reports on the die temperature.
+//!
+//! \param[out] pLow The low end of the temperature range.
+//! \param[out] pHigh The high end of the temperature range.
+//!
+////////////////////////////////////////////////////////////////////////////////
+// Temperature constant
+#define TEMP_READING_ERROR_MARGIN 5
+#define KELVIN_TO_CELSIUS_CONST 273
+
+void ddi_power_GetDieTemp(int16_t * pLow, int16_t * pHigh)
+{
+ int16_t i16High, i16Low;
+ uint16_t u16Reading;
+
+ // Get the reading in Kelvins
+ u16Reading = MeasureInternalDieTemperature();
+
+ // Adjust for error margin
+ i16High = u16Reading + TEMP_READING_ERROR_MARGIN;
+ i16Low = u16Reading - TEMP_READING_ERROR_MARGIN;
+
+ // Convert to Celsius
+ i16High -= KELVIN_TO_CELSIUS_CONST;
+ i16Low -= KELVIN_TO_CELSIUS_CONST;
+
+//#ifdef CONFIG_POWER_SUPPLY_DEBUG
+#if 0
+ printk("Battery charger: Die temp %d to %d C\n", i16Low, i16High);
+#endif
+ // Return the results
+ *pHigh = i16High;
+ *pLow = i16Low;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//!
+//! \brief Checks to see if the DCDC has been manually enabled
+//!
+//! \fntype Function
+//!
+//! \retval true if DCDC is ON, false if DCDC is OFF.
+//!
+////////////////////////////////////////////////////////////////////////////////
+bool ddi_power_IsDcdcOn(void)
+{
+ return (HW_POWER_5VCTRL_RD() & BM_POWER_5VCTRL_ENABLE_DCDC) ? 1 : 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//! See hw_power.h for details.
+////////////////////////////////////////////////////////////////////////////////
+void ddi_power_SetPowerClkGate(bool bGate)
+{
+ // Gate/Ungate the clock to the power block
+ if (bGate) {
+ HW_POWER_CTRL_SET(BM_POWER_CTRL_CLKGATE);
+ } else {
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_CLKGATE);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//! See hw_power.h for details.
+////////////////////////////////////////////////////////////////////////////////
+bool ddi_power_GetPowerClkGate(void)
+{
+ return (HW_POWER_CTRL_RD() & BM_POWER_CTRL_CLKGATE) ? 1 : 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// End of file
+////////////////////////////////////////////////////////////////////////////////
+//! @}
diff --git a/drivers/power/stmp37xx/ddi_power_battery.h b/drivers/power/stmp37xx/ddi_power_battery.h
new file mode 100644
index 000000000000..2d7c1a5411ad
--- /dev/null
+++ b/drivers/power/stmp37xx/ddi_power_battery.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+//! \brief Battery modes
+typedef enum {
+ // 37xx battery modes
+ //! \brief LiIon battery powers the player
+ DDI_POWER_BATT_MODE_LIION = 0,
+ //! \brief Alkaline/NiMH battery powers the player
+ DDI_POWER_BATT_MODE_ALKALINE_NIMH = 1,
+} ddi_power_BatteryMode_t;
+
+
+//! \brief Available sources for bias currents
+typedef enum {
+ //! \brief Use external resistor to generate bias current
+ DDI_POWER_EXTERNAL_BIAS_CURRENT = 0x0,
+ //! \brief Use internal resistor to generate bias current
+ DDI_POWER_INTERNAL_BIAS_CURRENT = 0x1
+} ddi_power_BiasCurrentSource_t;
+
+//! \brief Possible 5V detection methods
+typedef enum {
+ //! \brief Use VBUSVALID comparator for detection
+ DDI_POWER_5V_VBUSVALID,
+ //! \brief Use VDD5V_GT_VDDIO comparison for detection
+ DDI_POWER_5V_VDD5V_GT_VDDIO
+} ddi_power_5vDetection_t;
+
+
+uint16_t ddi_power_convert_current_to_setting(uint16_t u16Current);
+uint16_t ddi_power_convert_setting_to_current(uint16_t u16Setting);
+void ddi_power_enable_5v_to_battery_handoff(void);
+void ddi_power_execute_5v_to_battery_handoff(void);
+void ddi_power_enable_battery_to_5v_handoff(void);
+void ddi_power_execute_battery_to_5v_handoff(void);
+int ddi_power_init_battery(void);
+ddi_power_BatteryMode_t ddi_power_GetBatteryMode(void);
+bool ddi_power_GetBatteryChargerEnabled(void);
+bool ddi_power_GetChargerPowered(void);
+void ddi_power_SetChargerPowered(bool bPowerOn);
+int ddi_power_GetChargeStatus(void);
+uint16_t ddi_power_GetBattery(void);
+uint16_t ddi_power_GetBatteryBrownout(void);
+int ddi_power_SetBatteryBrownout(uint16_t u16BattBrownout_mV);
+int ddi_power_SetBiasCurrentSource(ddi_power_BiasCurrentSource_t eSource);
+ddi_power_BiasCurrentSource_t ddi_power_GetBiasCurrentSource(void);
+uint16_t ddi_power_SetMaxBatteryChargeCurrent(uint16_t u16MaxCur);
+uint16_t ddi_power_GetMaxBatteryChargeCurrent(void);
+uint16_t ddi_power_SetBatteryChargeCurrentThreshold(uint16_t u16Thresh);
+uint16_t ddi_power_GetBatteryChargeCurrentThreshold(void);
+uint16_t ddi_power_ExpressibleCurrent(uint16_t u16Current);
+bool ddi_power_Get5vPresentFlag(void);
+void ddi_power_GetDieTemp(int16_t * pLow, int16_t * pHigh);
+bool ddi_power_IsDcdcOn(void);
+void ddi_power_SetPowerClkGate(bool bGate);
+bool ddi_power_GetPowerClkGate(void);
diff --git a/drivers/power/stmp37xx/linux.c b/drivers/power/stmp37xx/linux.c
new file mode 100644
index 000000000000..664896004dc7
--- /dev/null
+++ b/drivers/power/stmp37xx/linux.c
@@ -0,0 +1,622 @@
+/*
+ * Linux glue to STMP3xxx battery state machine.
+ *
+ * Author: Steve Longerbeam <stevel@embeddedalley.com>
+ *
+ * Copyright (C) 2008 EmbeddedAlley Solutions Inc.
+ * Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * 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>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <mach/ddi_bc.h>
+#include "ddi_bc_internal.h"
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <mach/regulator.h>
+#include <mach/regs-power.h>
+#include <mach/regs-usbphy.h>
+#include <linux/delay.h>
+
+#include <linux/interrupt.h>
+
+
+struct stmp3xxx_info {
+ struct device *dev;
+ struct regulator *regulator;
+
+ struct power_supply bat;
+ struct power_supply ac;
+ struct power_supply usb;
+
+ ddi_bc_Cfg_t *sm_cfg;
+ struct mutex sm_lock;
+ struct timer_list sm_timer;
+ struct work_struct sm_work;
+ struct resource *vdd5v_irq;
+ int is_ac_online;
+#define USB_ONLINE 0x01
+#define USB_REG_SET 0x02
+#define USB_SM_RESTART 0x04
+#define USB_SHUTDOWN 0x08
+#define USB_N_SEND 0x10
+ int is_usb_online;
+};
+
+#define to_stmp3xxx_info(x) container_of((x), struct stmp3xxx_info, bat)
+
+/* There is no direct way to detect wall power presence, so assume the AC
+ * power source is valid if 5V presents and USB device is disconnected.
+ * If USB device is connected then assume that AC is offline and USB power
+ * is online.
+ */
+#define is_usb_plugged()(HW_USBPHY_STATUS_RD() & \
+ BM_USBPHY_STATUS_DEVPLUGIN_STATUS)
+#define is_ac_online() \
+ (ddi_power_Get5vPresentFlag() ? (!is_usb_plugged()) : 0)
+#define is_usb_online() \
+ (ddi_power_Get5vPresentFlag() ? (!!is_usb_plugged()) : 0)
+
+/*
+ * Power properties
+ */
+static enum power_supply_property stmp3xxx_power_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int stmp3xxx_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
+ /* ac online */
+ val->intval = is_ac_online();
+ else
+ /* usb online */
+ val->intval = is_usb_online();
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+/*
+ * Battery properties
+ */
+static enum power_supply_property stmp3xxx_bat_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+};
+
+static int stmp3xxx_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct stmp3xxx_info *info = to_stmp3xxx_info(psy);
+ ddi_bc_State_t state;
+ ddi_bc_BrokenReason_t reason;
+ int temp_alarm;
+ int16_t temp_lo, temp_hi;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ state = ddi_bc_GetState();
+ switch (state) {
+ case DDI_BC_STATE_CONDITIONING:
+ case DDI_BC_STATE_CHARGING:
+ case DDI_BC_STATE_TOPPING_OFF:
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case DDI_BC_STATE_DISABLED:
+ val->intval = ddi_power_Get5vPresentFlag() ?
+ POWER_SUPPLY_STATUS_NOT_CHARGING :
+ POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ default:
+ /* TODO: detect full */
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ }
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ /* is battery present */
+ state = ddi_bc_GetState();
+ switch (state) {
+ case DDI_BC_STATE_WAITING_TO_CHARGE:
+ case DDI_BC_STATE_DCDC_MODE_WAITING_TO_CHARGE:
+ case DDI_BC_STATE_CONDITIONING:
+ case DDI_BC_STATE_CHARGING:
+ case DDI_BC_STATE_TOPPING_OFF:
+ case DDI_BC_STATE_DISABLED:
+ val->intval = 1;
+ break;
+ case DDI_BC_STATE_BROKEN:
+ val->intval = !(ddi_bc_GetBrokenReason() ==
+ DDI_BC_BROKEN_NO_BATTERY_DETECTED);
+ break;
+ default:
+ val->intval = 0;
+ break;
+ }
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ temp_alarm = ddi_bc_RampGetDieTempAlarm();
+ if (temp_alarm) {
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ } else {
+ state = ddi_bc_GetState();
+ switch (state) {
+ case DDI_BC_STATE_BROKEN:
+ reason = ddi_bc_GetBrokenReason();
+ val->intval =
+ (reason == DDI_BC_BROKEN_CHARGING_TIMEOUT) ?
+ POWER_SUPPLY_HEALTH_DEAD :
+ POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ break;
+ case DDI_BC_STATE_UNINITIALIZED:
+ val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ }
+ }
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ /* uV */
+ val->intval = ddi_power_GetBattery() * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ /* uA */
+ val->intval = ddi_power_GetMaxBatteryChargeCurrent() * 1000;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ mutex_lock(&info->sm_lock);
+ ddi_power_GetDieTemp(&temp_lo, &temp_hi);
+ mutex_unlock(&info->sm_lock);
+ val->intval = temp_lo + (temp_hi - temp_lo) / 2;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void state_machine_timer(unsigned long data)
+{
+ struct stmp3xxx_info *info = (struct stmp3xxx_info *)data;
+ ddi_bc_Cfg_t *cfg = info->sm_cfg;
+ int ret;
+
+ /* schedule next call to state machine */
+ mod_timer(&info->sm_timer,
+ jiffies + msecs_to_jiffies(cfg->u32StateMachinePeriod));
+
+ ret = schedule_work(&info->sm_work);
+ if (!ret)
+ dev_dbg(info->dev, "state machine failed to schedule\n");
+
+}
+/*
+ * Assumtion:
+ * AC power can't be switched to USB w/o system reboot
+ * and vice-versa
+ */
+static void state_machine_work(struct work_struct *work)
+{
+ struct stmp3xxx_info *info =
+ container_of(work, struct stmp3xxx_info, sm_work);
+
+ mutex_lock(&info->sm_lock);
+
+ if (info->is_usb_online & USB_SHUTDOWN) {
+ info->is_usb_online = 0;
+ if (!info->regulator)
+ goto out;
+ regulator_set_current_limit(info->regulator, 0, 0);
+ goto out;
+ }
+
+ if (is_ac_online()) {
+ if (info->is_ac_online)
+ goto done;
+
+ /* ac supply connected */
+ dev_info(info->dev, "changed power connection to ac/5v \n");
+
+ info->is_ac_online = 1;
+ info->is_usb_online = 0;
+ ddi_bc_SetCurrentLimit(600 /*mA*/);
+ ddi_power_execute_battery_to_5v_handoff();
+ ddi_power_enable_5v_to_battery_handoff();
+ goto done;
+ }
+
+ if (!is_usb_online())
+ goto out;
+
+ if (info->is_usb_online & USB_REG_SET)
+ goto done;
+
+ info->is_ac_online = 0;
+ info->is_usb_online |= USB_ONLINE;
+
+ if (!info->regulator) {
+ info->regulator = regulator_get(NULL, "charger-1");
+ if (!info->regulator || IS_ERR(info->regulator)) {
+ dev_err(info->dev,
+ "%s: failed to get regulator\n", __func__);
+ info->regulator = NULL;
+ ddi_bc_SetCurrentLimit(350 /*mA*/);
+ ddi_power_execute_battery_to_5v_handoff();
+ ddi_power_enable_5v_to_battery_handoff();
+ goto done;
+ } else
+ regulator_set_mode(info->regulator,
+ REGULATOR_MODE_FAST);
+ }
+
+ if (!(info->is_usb_online & USB_N_SEND)) {
+ info->is_usb_online |= USB_N_SEND;
+ }
+
+ if (regulator_set_current_limit(info->regulator, 150000, 150000)) {
+ dev_err(info->dev, "reg_set_current(150000) failed\n");
+
+ ddi_bc_SetCurrentLimit(0 /*mA*/);
+ dev_dbg(info->dev, "charge current set to 0\n");
+ mod_timer(&info->sm_timer, jiffies + msecs_to_jiffies(1000));
+ goto done;
+ }
+
+ dev_dbg(info->dev, "%s: charge current set to 100mA\n", __func__);
+ ddi_bc_SetCurrentLimit(100 /*mA*/);
+ regulator_set_current_limit(info->regulator, 100000, 100000);
+ if (info->is_usb_online & USB_SM_RESTART) {
+ info->is_usb_online &= ~USB_SM_RESTART;
+ ddi_bc_SetEnable();
+ }
+
+ info->is_usb_online |= USB_REG_SET;
+
+ dev_info(info->dev, "changed power connection to usb/5v present\n");
+ ddi_power_execute_battery_to_5v_handoff();
+ ddi_power_enable_5v_to_battery_handoff();
+ ddi_bc_SetEnable();
+
+done:
+ ddi_bc_StateMachine();
+out:
+ mutex_unlock(&info->sm_lock);
+}
+
+static int bc_sm_restart(struct stmp3xxx_info *info)
+{
+ ddi_bc_Status_t bcret;
+ int ret = 0;
+
+ mutex_lock(&info->sm_lock);
+
+ /* ungate power clk */
+ ddi_power_SetPowerClkGate(0);
+
+ /*
+ * config battery charger state machine and move it to the Disabled
+ * state. This must be done before starting the state machine.
+ */
+ bcret = ddi_bc_Init(info->sm_cfg);
+ if (bcret != DDI_BC_STATUS_SUCCESS) {
+ dev_err(info->dev, "state machine init failed: %d\n", bcret);
+ ret = -EIO;
+ goto out;
+ }
+
+ /*
+ * Check what power supply options we have right now. If
+ * we're able to do any battery charging, then set the
+ * appropriate current limit and enable. Otherwise, leave
+ * the battery charger disabled.
+ */
+ if (is_ac_online()) {
+ /* ac supply connected */
+ dev_info(info->dev, "ac/5v present, enabling state machine\n");
+
+ info->is_ac_online = 1;
+ info->is_usb_online = 0;
+ ddi_bc_SetCurrentLimit(600 /*mA*/);
+ ddi_bc_SetEnable();
+ } else if (is_usb_online()) {
+ /* usb supply connected */
+ dev_info(info->dev, "usb/5v present, enabling state machine\n");
+
+ info->is_ac_online = 0;
+ info->is_usb_online = USB_ONLINE | USB_SM_RESTART;
+ } else {
+ /* not powered */
+ dev_info(info->dev, "%s: 5v not present\n", __func__);
+
+ info->is_ac_online = 0;
+ info->is_usb_online = 0;
+ ddi_bc_SetDisable();
+ }
+
+ /* schedule first call to state machine */
+ mod_timer(&info->sm_timer, jiffies + 1);
+out:
+ mutex_unlock(&info->sm_lock);
+ return ret;
+}
+
+static irqreturn_t stmp3xxx_vdd5v_irq(int irq, void *cookie)
+{
+ struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie;
+
+ if (ddi_power_Get5vPresentFlag()) {
+ dev_info(info->dev, "5v present, reenable state machine\n");
+
+ ddi_bc_SetEnable();
+
+ /*
+ * We only ack/negate the interrupt here,
+ * as we can't decide yet if we really can
+ * switch to 5V (USB bits not ready)
+ */
+ ddi_power_enable_5v_to_battery_handoff();
+ } else {
+ dev_info(info->dev, "5v went away, disabling state machine\n");
+
+ ddi_bc_SetDisable();
+
+ info->is_ac_online = 0;
+ if (info->is_usb_online)
+ info->is_usb_online = USB_SHUTDOWN;
+
+ ddi_power_execute_5v_to_battery_handoff();
+ ddi_power_enable_battery_to_5v_handoff();
+ mod_timer(&info->sm_timer, jiffies + 1);
+
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int stmp3xxx_bat_probe(struct platform_device *pdev)
+{
+ struct stmp3xxx_info *info;
+ int ret = 0;
+
+ if (!pdev->dev.platform_data) {
+ printk(KERN_ERR "%s: missing platform data\n", __func__);
+ return -ENODEV;
+ }
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->vdd5v_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (info->vdd5v_irq == NULL) {
+ printk(KERN_ERR "%s: failed to get irq resouce\n", __func__);
+ goto free_info;
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ info->dev = &pdev->dev;
+ info->sm_cfg = pdev->dev.platform_data;
+
+ /* initialize bat power_supply struct */
+ info->bat.name = "battery";
+ info->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ info->bat.properties = stmp3xxx_bat_props;
+ info->bat.num_properties = ARRAY_SIZE(stmp3xxx_bat_props);
+ info->bat.get_property = stmp3xxx_bat_get_property;
+
+ /* initialize ac power_supply struct */
+ info->ac.name = "ac";
+ info->ac.type = POWER_SUPPLY_TYPE_MAINS;
+ info->ac.properties = stmp3xxx_power_props;
+ info->ac.num_properties = ARRAY_SIZE(stmp3xxx_power_props);
+ info->ac.get_property = stmp3xxx_power_get_property;
+
+ /* initialize usb power_supply struct */
+ info->usb.name = "usb";
+ info->usb.type = POWER_SUPPLY_TYPE_USB;
+ info->usb.properties = stmp3xxx_power_props;
+ info->usb.num_properties = ARRAY_SIZE(stmp3xxx_power_props);
+ info->usb.get_property = stmp3xxx_power_get_property;
+
+ init_timer(&info->sm_timer);
+ info->sm_timer.data = (unsigned long)info;
+ info->sm_timer.function = state_machine_timer;
+
+ mutex_init(&info->sm_lock);
+ INIT_WORK(&info->sm_work, state_machine_work);
+
+ /* init LRADC channels to measure battery voltage and die temp */
+ ddi_power_init_battery();
+ HW_POWER_5VCTRL_CLR(BM_POWER_5VCTRL_ENABLE_LINREG_ILIMIT);
+
+ ret = bc_sm_restart(info);
+ if (ret)
+ goto free_info;
+
+ ret = request_irq(info->vdd5v_irq->start,
+ stmp3xxx_vdd5v_irq, IRQF_DISABLED | IRQF_SHARED,
+ pdev->name, info);
+ if (ret) {
+ dev_err(info->dev, "failed to request irq\n");
+ goto stop_sm;
+ }
+
+ ret = power_supply_register(&pdev->dev, &info->bat);
+ if (ret) {
+ dev_err(info->dev, "failed to register battery\n");
+ goto free_irq;
+ }
+
+ ret = power_supply_register(&pdev->dev, &info->ac);
+ if (ret) {
+ dev_err(info->dev, "failed to register ac power supply\n");
+ goto unregister_bat;
+ }
+
+ ret = power_supply_register(&pdev->dev, &info->usb);
+ if (ret) {
+ dev_err(info->dev, "failed to register usb power supply\n");
+ goto unregister_ac;
+ }
+
+ /* enable usb device presence detection */
+ HW_USBPHY_CTRL_SET(BM_USBPHY_CTRL_ENDEVPLUGINDETECT);
+
+ return 0;
+
+unregister_ac:
+ power_supply_unregister(&info->ac);
+unregister_bat:
+ power_supply_unregister(&info->bat);
+free_irq:
+ free_irq(info->vdd5v_irq->start, pdev);
+stop_sm:
+ ddi_bc_ShutDown();
+free_info:
+ kfree(info);
+ return ret;
+}
+
+static int stmp3xxx_bat_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_info *info = platform_get_drvdata(pdev);
+
+ if (info->regulator)
+ regulator_put(info->regulator);
+ free_irq(info->vdd5v_irq->start, pdev);
+ ddi_bc_ShutDown();
+ power_supply_unregister(&info->usb);
+ power_supply_unregister(&info->ac);
+ power_supply_unregister(&info->bat);
+ return 0;
+}
+
+static void stmp3xxx_bat_shutdown(struct platform_device *pdev)
+{
+ ddi_bc_ShutDown();
+}
+
+
+#ifdef CONFIG_PM
+
+static int stmp3xxx_bat_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ struct stmp3xxx_info *info = platform_get_drvdata(pdev);
+
+ mutex_lock(&info->sm_lock);
+
+ /* disable 5v irq */
+ HW_POWER_CTRL_CLR(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO);
+
+ ddi_bc_SetDisable();
+ /* cancel state machine timer */
+ del_timer_sync(&info->sm_timer);
+
+ mutex_unlock(&info->sm_lock);
+ return 0;
+}
+
+static int stmp3xxx_bat_resume(struct platform_device *pdev)
+{
+ struct stmp3xxx_info *info = platform_get_drvdata(pdev);
+ ddi_bc_Cfg_t *cfg = info->sm_cfg;
+
+ mutex_lock(&info->sm_lock);
+
+ if (is_ac_online()) {
+ /* ac supply connected */
+ dev_info(info->dev, "ac/5v present, enabling state machine\n");
+
+ info->is_ac_online = 1;
+ info->is_usb_online = 0;
+ ddi_bc_SetCurrentLimit(600 /*mA*/);
+ ddi_bc_SetEnable();
+ } else if (is_usb_online()) {
+ /* usb supply connected */
+ dev_info(info->dev, "usb/5v present, enabling state machine\n");
+
+ info->is_ac_online = 0;
+ info->is_usb_online = 1;
+ ddi_bc_SetCurrentLimit(350 /*mA*/);
+ ddi_bc_SetEnable();
+ } else {
+ /* not powered */
+ dev_info(info->dev, "%s: 5v not present\n", __func__);
+
+ info->is_ac_online = 0;
+ info->is_usb_online = 0;
+ }
+
+ /* enable 5v irq */
+ HW_POWER_CTRL_SET(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO);
+
+ /* reschedule calls to state machine */
+ mod_timer(&info->sm_timer,
+ jiffies + msecs_to_jiffies(cfg->u32StateMachinePeriod));
+
+ mutex_unlock(&info->sm_lock);
+ return 0;
+}
+
+#else
+#define stmp3xxx_bat_suspend NULL
+#define stmp3xxx_bat_resume NULL
+#endif
+
+static struct platform_driver stmp3xxx_batdrv = {
+ .probe = stmp3xxx_bat_probe,
+ .remove = stmp3xxx_bat_remove,
+ .shutdown = stmp3xxx_bat_shutdown,
+ .suspend = stmp3xxx_bat_suspend,
+ .resume = stmp3xxx_bat_resume,
+ .driver = {
+ .name = "stmp3xxx-battery",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmp3xxx_bat_init(void)
+{
+ return platform_driver_register(&stmp3xxx_batdrv);
+}
+
+static void __exit stmp3xxx_bat_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_batdrv);
+}
+
+module_init(stmp3xxx_bat_init);
+module_exit(stmp3xxx_bat_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Steve Longerbeam <stevel@embeddedalley.com>");
+MODULE_DESCRIPTION("Linux glue to STMP3xxx battery state machine");
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
new file mode 100644
index 000000000000..5aeb7b7453b1
--- /dev/null
+++ b/drivers/pwm/Kconfig
@@ -0,0 +1,35 @@
+#
+# PWM infrastructure and devices
+#
+
+menuconfig GENERIC_PWM
+ tristate "PWM Support"
+ help
+ This enables PWM support through the generic PWM library.
+ If unsure, say N.
+
+if GENERIC_PWM
+
+config ATMEL_PWM
+ tristate "Atmel AT32/AT91 PWM support"
+ depends on AVR32 || ARCH_AT91
+ help
+ This option enables device driver support for the PWMC
+ peripheral channels found on certain Atmel processors.
+ Pulse Width Modulation is used many for purposes, including
+ software controlled power-efficent backlights on LCD
+ displays, motor control, and waveform generation. If
+ unsure, say N.
+
+config NS921X_PWM
+ tristate "Digi NS921X PWM support"
+ depends on PROCESSOR_NS9215 && !CC9P9215JS_SERIAL_PORTA && !CC9P9215JS_SPI && !CC9P9215JS_SERIAL_PORTC_CTSRTSRXTX && !CC9P9215JS_SERIAL_PORTC_FULL
+ help
+ This option enables device driver support for the PWMC
+ peripheral channels found on certain NetSilicon processors.
+ Pulse Width Modulation is used many for purposes, including
+ software controlled power-efficent backlights on LCD
+ displays, motor control, and waveform generation. If
+ unsure, say N.
+
+endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
new file mode 100644
index 000000000000..f04d64f39f9f
--- /dev/null
+++ b/drivers/pwm/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for pwm devices
+#
+obj-$(CONFIG_GENERIC_PWM) := pwm.o
+
+obj-$(CONFIG_ATMEL_PWM) += atmel-pwm.o
+obj-$(CONFIG_NS921X_PWM) += ns9xxx-pwm.o
diff --git a/drivers/pwm/atmel-pwm.c b/drivers/pwm/atmel-pwm.c
new file mode 100644
index 000000000000..e522b16e3cd6
--- /dev/null
+++ b/drivers/pwm/atmel-pwm.c
@@ -0,0 +1,633 @@
+/*
+ * drivers/pwm/atmel-pwm.c
+ *
+ * Copyright (C) 2008 Bill Gatliff
+ * Copyright (C) 2007 David Brownell
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+enum {
+ /* registers common to the PWMC peripheral */
+ PWMC_MR = 0,
+ PWMC_ENA = 4,
+ PWMC_DIS = 8,
+ PWMC_SR = 0xc,
+ PWMC_IER = 0x10,
+ PWMC_IDR = 0x14,
+ PWMC_IMR = 0x18,
+ PWMC_ISR = 0x1c,
+
+ /* registers per each PWMC channel */
+ PWMC_CMR = 0,
+ PWMC_CDTY = 4,
+ PWMC_CPRD = 8,
+ PWMC_CCNT = 0xc,
+ PWMC_CUPD = 0x10,
+
+ /* how to find each channel */
+ PWMC_CHAN_BASE = 0x200,
+ PWMC_CHAN_STRIDE = 0x20,
+
+ /* CMR bits of interest */
+ PWMC_CMR_CPD = 10,
+ PWMC_CMR_CPOL = 9,
+ PWMC_CMR_CALG = 8,
+ PWMC_CMR_CPRE_MASK = 0xf,
+};
+
+struct atmel_pwm {
+ struct pwm_device pwm;
+ spinlock_t lock;
+ void __iomem *iobase;
+ struct clk *clk;
+ u32 *sync_mask;
+ int irq;
+ u32 ccnt_mask;
+};
+
+
+static inline void
+pwmc_writel(const struct atmel_pwm *p,
+ unsigned offset, u32 val)
+{
+ __raw_writel(val, p->iobase + offset);
+}
+
+
+static inline u32
+pwmc_readl(const struct atmel_pwm *p,
+ unsigned offset)
+{
+ return __raw_readl(p->iobase + offset);
+}
+
+
+static inline void
+pwmc_chan_writel(const struct pwm_channel *p,
+ u32 offset, u32 val)
+{
+ const struct atmel_pwm *ap
+ = container_of(p->pwm, struct atmel_pwm, pwm);
+
+ if (PWMC_CMR == offset)
+ val &= ((1 << PWMC_CMR_CPD)
+ | (1 << PWMC_CMR_CPOL)
+ | (1 << PWMC_CMR_CALG)
+ | (PWMC_CMR_CPRE_MASK));
+ else
+ val &= ap->ccnt_mask;
+
+ pwmc_writel(ap, offset + PWMC_CHAN_BASE
+ + (p->chan * PWMC_CHAN_STRIDE), val);
+}
+
+
+static inline u32
+pwmc_chan_readl(const struct pwm_channel *p,
+ u32 offset)
+{
+ const struct atmel_pwm *ap
+ = container_of(p->pwm, struct atmel_pwm, pwm);
+
+ return pwmc_readl(ap, offset + PWMC_CHAN_BASE
+ + (p->chan * PWMC_CHAN_STRIDE));
+}
+
+
+static inline int
+__atmel_pwm_is_on(struct pwm_channel *p)
+{
+ struct atmel_pwm *ap = container_of(p->pwm, struct atmel_pwm, pwm);
+ return (pwmc_readl(ap, PWMC_SR) & (1 << p->chan)) ? 1 : 0;
+}
+
+
+static inline void
+__atmel_pwm_unsynchronize(struct pwm_channel *p,
+ struct pwm_channel *to_p)
+{
+ const struct atmel_pwm *ap
+ = container_of(p->pwm, struct atmel_pwm, pwm);
+ int wchan;
+
+ if (to_p) {
+ ap->sync_mask[p->chan] &= ~(1 << to_p->chan);
+ ap->sync_mask[to_p->chan] &= ~(1 << p->chan);
+ goto done;
+ }
+
+ ap->sync_mask[p->chan] = 0;
+ for (wchan = 0; wchan < ap->pwm.nchan; wchan++)
+ ap->sync_mask[wchan] &= ~(1 << p->chan);
+done:
+ pr_debug("%s:%d sync_mask %x\n",
+ p->pwm->bus_id, p->chan, ap->sync_mask[p->chan]);
+}
+
+
+static inline void
+__atmel_pwm_synchronize(struct pwm_channel *p,
+ struct pwm_channel *to_p)
+{
+ const struct atmel_pwm *ap
+ = container_of(p->pwm, struct atmel_pwm, pwm);
+
+ if (!to_p)
+ return;
+
+ ap->sync_mask[p->chan] |= (1 << to_p->chan);
+ ap->sync_mask[to_p->chan] |= (1 << p->chan);
+
+ pr_debug("%s:%d sync_mask %x\n",
+ p->pwm->bus_id, p->chan, ap->sync_mask[p->chan]);
+}
+
+
+static inline void
+__atmel_pwm_stop(struct pwm_channel *p)
+{
+ struct atmel_pwm *ap = container_of(p->pwm, struct atmel_pwm, pwm);
+ u32 chid = 1 << p->chan;
+
+ pwmc_writel(ap, PWMC_DIS, ap->sync_mask[p->chan] | chid);
+}
+
+
+static inline void
+__atmel_pwm_start(struct pwm_channel *p)
+{
+ struct atmel_pwm *ap = container_of(p->pwm, struct atmel_pwm, pwm);
+ u32 chid = 1 << p->chan;
+
+ pwmc_writel(ap, PWMC_ENA, ap->sync_mask[p->chan] | chid);
+}
+
+
+static int
+atmel_pwm_synchronize(struct pwm_channel *p,
+ struct pwm_channel *to_p)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&p->lock, flags);
+ __atmel_pwm_synchronize(p, to_p);
+ spin_unlock_irqrestore(&p->lock, flags);
+ return 0;
+}
+
+
+static int
+atmel_pwm_unsynchronize(struct pwm_channel *p,
+ struct pwm_channel *from_p)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&p->lock, flags);
+ __atmel_pwm_unsynchronize(p, from_p);
+ spin_unlock_irqrestore(&p->lock, flags);
+ return 0;
+}
+
+
+static inline int
+__atmel_pwm_config_polarity(struct pwm_channel *p,
+ struct pwm_channel_config *c)
+{
+ u32 cmr = pwmc_chan_readl(p, PWMC_CMR);
+
+ if (c->polarity)
+ cmr &= ~BIT(PWMC_CMR_CPOL);
+ else
+ cmr |= BIT(PWMC_CMR_CPOL);
+ pwmc_chan_writel(p, PWMC_CMR, cmr);
+
+ pr_debug("%s:%d polarity %d\n", p->pwm->bus_id,
+ p->chan, c->polarity);
+ return 0;
+}
+
+
+static inline int
+__atmel_pwm_config_duty_ticks(struct pwm_channel *p,
+ struct pwm_channel_config *c)
+{
+ u32 cmr, cprd, cpre, cdty;
+
+ cmr = pwmc_chan_readl(p, PWMC_CMR);
+ cprd = pwmc_chan_readl(p, PWMC_CPRD);
+
+ cpre = cmr & PWMC_CMR_CPRE_MASK;
+ cmr &= ~BIT(PWMC_CMR_CPD);
+
+ cdty = cprd - (c->duty_ticks >> cpre);
+
+ p->duty_ticks = c->duty_ticks;
+
+ if (__atmel_pwm_is_on(p)) {
+ pwmc_chan_writel(p, PWMC_CMR, cmr);
+ pwmc_chan_writel(p, PWMC_CUPD, cdty);
+ } else
+ pwmc_chan_writel(p, PWMC_CDTY, cdty);
+
+ pr_debug("%s:%d duty_ticks = %lu cprd = %x"
+ " cdty = %x cpre = %x\n",
+ p->pwm->bus_id, p->chan, p->duty_ticks,
+ cprd, cdty, cpre);
+
+ return 0;
+}
+
+
+static inline int
+__atmel_pwm_config_period_ticks(struct pwm_channel *p,
+ struct pwm_channel_config *c)
+{
+ u32 cmr, cprd, cpre;
+
+ cpre = fls(c->period_ticks);
+ if (cpre < 16)
+ cpre = 0;
+ else {
+ cpre -= 15;
+ if (cpre > 10)
+ return -EINVAL;
+ }
+
+ cmr = pwmc_chan_readl(p, PWMC_CMR);
+ cmr &= ~PWMC_CMR_CPRE_MASK;
+ cmr |= cpre;
+
+ cprd = c->period_ticks >> cpre;
+
+ pwmc_chan_writel(p, PWMC_CMR, cmr);
+ pwmc_chan_writel(p, PWMC_CPRD, cprd);
+ p->period_ticks = c->period_ticks;
+
+ pr_debug("%s:%d period_ticks = %lu cprd = %x cpre = %x\n",
+ p->pwm->bus_id, p->chan, p->period_ticks, cprd, cpre);
+
+ return 0;
+}
+
+
+
+static int
+atmel_pwm_config_nosleep(struct pwm_channel *p,
+ struct pwm_channel_config *c)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ switch (c->config_mask) {
+
+ case PWM_CONFIG_DUTY_TICKS:
+ __atmel_pwm_config_duty_ticks(p, c);
+ break;
+
+ case PWM_CONFIG_STOP:
+ __atmel_pwm_stop(p);
+ pr_debug("%s:%d stop\n", p->pwm->bus_id, p->chan);
+ break;
+
+ case PWM_CONFIG_START:
+ __atmel_pwm_start(p);
+ pr_debug("%s:%d start\n", p->pwm->bus_id, p->chan);
+ break;
+
+ case PWM_CONFIG_POLARITY:
+ __atmel_pwm_config_polarity(p, c);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ spin_unlock_irqrestore(&p->lock, flags);
+ return ret;
+}
+
+
+static int
+atmel_pwm_stop_sync(struct pwm_channel *p)
+{
+ struct atmel_pwm *ap = container_of(p->pwm, struct atmel_pwm, pwm);
+ int ret;
+ int was_on = __atmel_pwm_is_on(p);
+
+ if (was_on) {
+ do {
+ init_completion(&p->complete);
+ set_bit(FLAG_STOP, &p->flags);
+ pwmc_writel(ap, PWMC_IER, 1 << p->chan);
+
+ pr_debug("%s:%d waiting on stop_sync completion...\n",
+ p->pwm->bus_id, p->chan);
+
+ ret = wait_for_completion_interruptible(&p->complete);
+
+ pr_debug("%s:%d stop_sync complete (%d)\n",
+ p->pwm->bus_id, p->chan, ret);
+
+ if (ret)
+ return ret;
+ } while (p->flags & BIT(FLAG_STOP));
+ }
+
+ pr_debug("%s:%d stop_sync returning %d\n",
+ p->pwm->bus_id, p->chan, was_on);
+
+ return was_on;
+}
+
+
+static int
+atmel_pwm_config(struct pwm_channel *p,
+ struct pwm_channel_config *c)
+{
+ int was_on = 0;
+
+ if (p->pwm->config_nosleep) {
+ if (!p->pwm->config_nosleep(p, c))
+ return 0;
+ }
+
+ might_sleep();
+
+ pr_debug("%s:%d config_mask %x\n",
+ p->pwm->bus_id, p->chan, c->config_mask);
+
+ was_on = atmel_pwm_stop_sync(p);
+ if (was_on < 0)
+ return was_on;
+
+ if (c->config_mask & PWM_CONFIG_PERIOD_TICKS) {
+ __atmel_pwm_config_period_ticks(p, c);
+ if (!(c->config_mask & PWM_CONFIG_DUTY_TICKS)) {
+ struct pwm_channel_config d = {
+ .config_mask = PWM_CONFIG_DUTY_TICKS,
+ .duty_ticks = p->duty_ticks,
+ };
+ __atmel_pwm_config_duty_ticks(p, &d);
+ }
+ }
+
+ if (c->config_mask & PWM_CONFIG_DUTY_TICKS)
+ __atmel_pwm_config_duty_ticks(p, c);
+
+ if (c->config_mask & PWM_CONFIG_POLARITY)
+ __atmel_pwm_config_polarity(p, c);
+
+ if ((c->config_mask & PWM_CONFIG_START)
+ || (was_on && !(c->config_mask & PWM_CONFIG_STOP)))
+ __atmel_pwm_start(p);
+
+ return 0;
+}
+
+
+static void
+__atmel_pwm_set_callback(struct pwm_channel *p,
+ pwm_callback_t callback)
+{
+ struct atmel_pwm *ap = container_of(p->pwm, struct atmel_pwm, pwm);
+
+ p->callback = callback;
+ pwmc_writel(ap, p->callback ? PWMC_IER : PWMC_IDR, 1 << p->chan);
+ pr_debug("%s:%d set_callback %p\n", p->pwm->bus_id, p->chan, callback);
+}
+
+
+static int
+atmel_pwm_set_callback(struct pwm_channel *p,
+ pwm_callback_t callback)
+{
+ struct atmel_pwm *ap = container_of(p->pwm, struct atmel_pwm, pwm);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ap->lock, flags);
+ __atmel_pwm_set_callback(p, callback);
+ spin_unlock_irqrestore(&ap->lock, flags);
+
+ return 0;
+}
+
+
+static int
+atmel_pwm_request(struct pwm_channel *p)
+{
+ struct atmel_pwm *ap = container_of(p->pwm, struct atmel_pwm, pwm);
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->lock, flags);
+ clk_enable(ap->clk);
+ p->tick_hz = clk_get_rate(ap->clk);
+ __atmel_pwm_unsynchronize(p, NULL);
+ __atmel_pwm_stop(p);
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ return 0;
+}
+
+
+static void
+atmel_pwm_free(struct pwm_channel *p)
+{
+ struct atmel_pwm *ap = container_of(p->pwm, struct atmel_pwm, pwm);
+ clk_disable(ap->clk);
+}
+
+
+static irqreturn_t
+atmel_pwmc_irq(int irq, void *data)
+{
+ struct atmel_pwm *ap = data;
+ struct pwm_channel *p;
+ u32 isr;
+ int chid;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ap->lock, flags);
+
+ isr = pwmc_readl(ap, PWMC_ISR);
+ for (chid = 0; isr; chid++, isr >>= 1) {
+ p = &ap->pwm.channels[chid];
+ if (isr & 1) {
+ if (p->callback)
+ p->callback(p);
+ if (p->flags & BIT(FLAG_STOP)) {
+ __atmel_pwm_stop(p);
+ clear_bit(FLAG_STOP, &p->flags);
+ }
+ complete_all(&p->complete);
+ }
+ }
+
+ spin_unlock_irqrestore(&ap->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+
+static int __init
+atmel_pwmc_probe(struct platform_device *pdev)
+{
+ struct atmel_pwm *ap;
+ struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int ret = 0;
+
+ ap = kzalloc(sizeof(*ap), GFP_KERNEL);
+ if (!ap) {
+ ret = -ENOMEM;
+ goto err_atmel_pwm_alloc;
+ }
+
+ spin_lock_init(&ap->lock);
+ platform_set_drvdata(pdev, ap);
+
+ ap->pwm.bus_id = pdev->dev.bus_id;
+
+ ap->pwm.nchan = 4; /* TODO: true only for SAM9263 and AP7000 */
+ ap->ccnt_mask = 0xffffUL; /* TODO: true only for SAM9263 */
+
+ ap->sync_mask = kzalloc(ap->pwm.nchan * sizeof(u32), GFP_KERNEL);
+ if (!ap->sync_mask) {
+ ret = -ENOMEM;
+ goto err_alloc_sync_masks;
+ }
+
+ ap->pwm.owner = THIS_MODULE;
+ ap->pwm.request = atmel_pwm_request;
+ ap->pwm.free = atmel_pwm_free;
+ ap->pwm.config_nosleep = atmel_pwm_config_nosleep;
+ ap->pwm.config = atmel_pwm_config;
+ ap->pwm.synchronize = atmel_pwm_synchronize;
+ ap->pwm.unsynchronize = atmel_pwm_unsynchronize;
+ ap->pwm.set_callback = atmel_pwm_set_callback;
+
+ ap->clk = clk_get(&pdev->dev, "pwm_clk");
+ if (IS_ERR(ap->clk)) {
+ pr_info("%s: clk_get error %ld\n",
+ ap->pwm.bus_id, PTR_ERR(ap->clk));
+ ret = -ENODEV;
+ goto err_clk_get;
+ }
+
+ ap->iobase = ioremap_nocache(r->start, r->end - r->start + 1);
+ if (!ap->iobase) {
+ ret = -ENODEV;
+ goto err_ioremap;
+ }
+
+ clk_enable(ap->clk);
+ pwmc_writel(ap, PWMC_DIS, -1);
+ pwmc_writel(ap, PWMC_IDR, -1);
+ clk_disable(ap->clk);
+
+ ap->irq = platform_get_irq(pdev, 0);
+ if (ap->irq != -ENXIO) {
+ ret = request_irq(ap->irq, atmel_pwmc_irq, 0,
+ ap->pwm.bus_id, ap);
+ if (ret)
+ goto err_request_irq;
+ }
+
+ ret = pwm_register(&ap->pwm);
+ if (ret)
+ goto err_pwm_register;
+
+ return 0;
+
+err_pwm_register:
+ if (ap->irq != -ENXIO)
+ free_irq(ap->irq, ap);
+err_request_irq:
+ iounmap(ap->iobase);
+err_ioremap:
+ clk_put(ap->clk);
+err_clk_get:
+ platform_set_drvdata(pdev, NULL);
+err_alloc_sync_masks:
+ kfree(ap);
+err_atmel_pwm_alloc:
+ return ret;
+}
+
+
+static int __devexit
+atmel_pwmc_remove(struct platform_device *pdev)
+{
+ struct atmel_pwm *ap = platform_get_drvdata(pdev);
+ int ret;
+
+ /* TODO: what can we do if this fails? */
+ ret = pwm_unregister(&ap->pwm);
+
+ clk_enable(ap->clk);
+ pwmc_writel(ap, PWMC_IDR, -1);
+ pwmc_writel(ap, PWMC_DIS, -1);
+ clk_disable(ap->clk);
+
+ if (ap->irq != -ENXIO)
+ free_irq(ap->irq, ap);
+
+ clk_put(ap->clk);
+ iounmap(ap->iobase);
+
+ kfree(ap);
+
+ return 0;
+}
+
+
+static struct platform_driver atmel_pwm_driver = {
+ .driver = {
+ .name = "atmel_pwmc",
+ .owner = THIS_MODULE,
+ },
+ .probe = atmel_pwmc_probe,
+ .remove = __devexit_p(atmel_pwmc_remove),
+};
+
+
+static int __init atmel_pwm_init(void)
+{
+ return platform_driver_register(&atmel_pwm_driver);
+}
+module_init(atmel_pwm_init);
+
+
+static void atmel_pwm_exit(void)
+{
+ platform_driver_unregister(&atmel_pwm_driver);
+}
+module_exit(atmel_pwm_exit);
+
+
+MODULE_AUTHOR("Bill Gatliff <bgat <at> billgatliff.com>");
+MODULE_DESCRIPTION("Driver for Atmel PWMC peripheral");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atmel_pwmc");
+
diff --git a/drivers/pwm/ns9xxx-pwm.c b/drivers/pwm/ns9xxx-pwm.c
new file mode 100644
index 000000000000..b128c971da33
--- /dev/null
+++ b/drivers/pwm/ns9xxx-pwm.c
@@ -0,0 +1,554 @@
+/*
+ * drivers/pwm/ns9xxx-pwm.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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
+ */
+
+# define LAST_COUNT 0xFFFFFFFF /* 32 bit counter */
+// # define DEBUG
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <mach/gpio.h>
+#include <mach/regs-sys-ns921x.h>
+#include <mach/regs-io-ns921x.h>
+#include <mach/ns9xxx-pwm.h>
+
+struct ns9xxx_pwm {
+ struct pwm_device pwm;
+ spinlock_t lock;
+ struct clk *clk;
+
+ struct ns9xxx_pwm_pdata *pdata;
+};
+
+/* @XXX: Return NULL by failures */
+static inline struct ns9xxx_pwm_channel *ns9xxx_pwm_to_channel(struct pwm_channel *p)
+{
+ struct ns9xxx_pwm *np;
+ struct ns9xxx_pwm_channel *retval;
+
+
+ if (!p) {
+ pr_err("%s: NULL pointer passed!\n", __func__);
+ return NULL;
+ }
+
+ np = container_of(p->pwm, struct ns9xxx_pwm, pwm);
+ if (!np) {
+ pr_err("%s: NO np found!\n", __func__);
+ return NULL;
+ }
+
+ if (!np->pdata) {
+ pr_err("%s: NO platform data found!\n", __func__);
+ return NULL;
+ }
+
+ if (!np->pdata->channels)
+ pr_err("NO channels passed?\n");
+
+ retval = np->pdata->channels + p->chan;
+ if (!retval)
+ pr_err("%s: Invalid channel number passed?\n", __func__);
+
+ return retval;
+}
+
+/* Checks if pwm is being used */
+static inline int __ns9xxx_pwm_is_on(struct pwm_channel *p)
+{
+ struct ns9xxx_pwm *np = container_of(p->pwm, struct ns9xxx_pwm, pwm);
+ u32 timer_control;
+// struct ns9xxx_pwm_pdata *pdata;
+ struct ns9xxx_pwm_channel *pch;
+
+ if (!np) {
+ pr_err("Unable to fetch structure ns9xxx_pwm.\n");
+ return -EINVAL;
+ }
+
+ //pdata = np->pdata;
+ //pch = pdata->channels + p->chan;
+ pch = ns9xxx_pwm_to_channel(p);
+ timer_control = readl(SYS_TC(pch->timer));
+
+ return (timer_control & NS921X_TCR_TE) ? 1 : 0;
+}
+
+/* Starts PWM output */
+static inline void __ns9xxx_pwm_start(struct pwm_channel *p)
+{
+ //struct ns9xxx_pwm *np = container_of(p->pwm, struct ns9xxx_pwm, pwm);
+ struct ns9xxx_pwm_channel *pch;
+
+ pch = ns9xxx_pwm_to_channel(p);
+
+ writel(NS921X_TCR_RELOADEN | NS921X_TCR_BITTIMER |
+ NS921X_TCR_TCLKSEL_AHB | NS921X_TCR_TE |
+ NS921X_TCR_TMODE2_PWM, SYS_TC(pch->timer));
+}
+
+/* Stops PWM output */
+static inline void __ns9xxx_pwm_stop(struct pwm_channel *p)
+{
+ struct ns9xxx_pwm_channel *ch;
+
+ ch = ns9xxx_pwm_to_channel(p);
+ writel(0x0, SYS_TC(ch->timer));
+}
+
+/* Configures GPIO */
+static inline int __ns9xxx_pwm_config_gpio(struct pwm_channel *p, int invert)
+{
+ int gpionr;
+ struct ns9xxx_pwm_channel *ch;
+
+ if (!p) {
+ pr_err("Unable to fetch structure ns9xxx_pwm.\n");
+ return -EINVAL;
+ }
+
+ ch = ns9xxx_pwm_to_channel(p);
+ gpionr = ch->gpio;
+ gpio_configure_ns921x_unlocked(gpionr, 0,
+ invert ? NS921X_GPIO_INVERT : NS921X_GPIO_DONT_INVERT,
+ NS921X_GPIO_FUNC_2, 0);
+
+ return 0;
+}
+
+/* Toggles PWM output polarity */
+static inline int
+__ns9xxx_pwm_config_polarity(struct pwm_channel *p, struct pwm_channel_config *c)
+{
+ return __ns9xxx_pwm_config_gpio(p, c->polarity);
+}
+
+/* Configures duty_ticks */
+static inline int
+__ns9xxx_pwm_config_duty_ticks(struct pwm_channel *p, struct pwm_channel_config *c)
+{
+ struct ns9xxx_pwm *np = container_of(p->pwm, struct ns9xxx_pwm, pwm);
+ struct ns9xxx_pwm_channel *ch;
+ u32 reload, low;
+
+ ch = ns9xxx_pwm_to_channel(p);
+
+ if ((np == NULL) || (ch == NULL)) {
+ pr_err("Incorrect parameters in __ns9xxx_pwm_config_duty_ticks()\n");
+ return -EINVAL;
+ }
+
+ /* While trying to configure duty without period, assume 50% duty cycle */
+ if (p->period_ticks == 0)
+ p->period_ticks = c->duty_ticks * 2;
+
+ reload = LAST_COUNT - p->period_ticks;
+ low = reload + c->duty_ticks;
+
+ /*
+ * There are [0-9] Timers, but PWM extended runs on [6-9] SYS_THR
+ * and SYS_TLR is just available for [6-9], so the mapping
+ * would be: 0-> Timer 6, .., 3-> Timer 9
+ */
+ writel(reload, SYS_TRELCCR(ch->timer));
+ writel(reload, SYS_THR(ch->timer - 6)); /* high equals to reload */
+ writel(low, SYS_TLR(ch->timer - 6));
+
+ p->duty_ticks = c->duty_ticks;
+
+ pr_debug
+ ("\np->period_ticks: %lu chan: %i p->duty_ticks: %lu cduty_ticks: %lu cperiod_ticks: %lu \n",
+ p->period_ticks, p->chan, p->duty_ticks, c->duty_ticks, c->period_ticks);
+ pr_debug("period_ns: %lu chan: %i duty_ns: %lu config_mask: %i gpio: %i\n",
+ c->period_ns, p->chan, c->duty_ns, c->config_mask, ch->gpio);
+ pr_debug("LAST_COUNT: %X reload: %X low: %X\n", LAST_COUNT, reload, low);
+
+ return 0;
+}
+
+
+/*
+ * Figure to understand PWM concepts
+ * -----------------
+ * | |
+ * | |
+ * -------------- -----------------
+ * ^ ^ ^ ^
+ * RELOAD HIGH LOW LAST_COUNT
+ *
+ * Following driver assumes RELOAD = HIGH
+ */
+
+/* Configures period_ticks */
+static inline int
+__ns9xxx_pwm_config_period_ticks(struct pwm_channel *p, struct pwm_channel_config *c)
+{
+ struct ns9xxx_pwm *np = container_of(p->pwm, struct ns9xxx_pwm, pwm);
+ struct ns9xxx_pwm_channel *ch;
+ u32 reload, low;
+
+ ch = ns9xxx_pwm_to_channel(p);
+
+ if ((np == NULL) || (ch == NULL)) {
+ pr_err("Incorrect parameters in __ns9xxx_pwm_config_duty_ticks()\n");
+ return -EINVAL;
+ }
+
+ reload = LAST_COUNT - c->period_ticks;
+
+ /* While trying to configure period without duty, assume 50% duty cycle */
+ if (p->duty_ticks == 0)
+ p->duty_ticks = c->period_ticks / 2;
+
+ low = reload + p->duty_ticks;
+
+ /*
+ * There are [0-9] Timers, but PWM extended runs on [6-9]
+ * SYS_THR and SYS_TLR is just available for [6-9], so the mnpping
+ * would be: 0-> Timer 6, .., 3-> Timer 9
+ */
+ writel(reload, SYS_TRELCCR(ch->timer));
+ writel(reload, SYS_THR(ch->timer - 6));
+ writel(low, SYS_TLR(ch->timer - 6));
+
+ p->period_ticks = c->period_ticks;
+
+ pr_debug
+ ("\np->period_ticks: %lu chan: %i p->duty_ticks: %lu cduty_ticks: %lu cperiod_ticks: %lu\n",
+ p->period_ticks, p->chan, p->duty_ticks, c->duty_ticks, c->period_ticks);
+ pr_debug("period_ns: %lu chan: %i duty_ns: %lu config_mask: %i gpio: %i\n",
+ c->period_ns, p->chan, c->duty_ns, c->config_mask, ch->gpio);
+ pr_debug("LAST_COUNT: %X reload: %X low: %X \n", LAST_COUNT, reload, low);
+
+ return 0;
+}
+
+/* No sleeping configuration */
+static int ns9xxx_pwm_config_nosleep(struct pwm_channel *p, struct pwm_channel_config *c)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ switch (c->config_mask) {
+
+ case PWM_CONFIG_DUTY_TICKS:
+ __ns9xxx_pwm_config_duty_ticks(p, c);
+ pr_debug("duty_ns: %lu period_ns: %lu\n", c->duty_ns, c->period_ns);
+ break;
+
+ case PWM_CONFIG_PERIOD_TICKS:
+ __ns9xxx_pwm_config_period_ticks(p, c);
+ pr_debug("duty_ns: %lu period_ns: %lu\n", c->duty_ns, c->period_ns);
+ break;
+
+ case PWM_CONFIG_STOP:
+ __ns9xxx_pwm_stop(p);
+ pr_debug("%s:%d stop\n", p->pwm->bus_id, p->chan);
+ break;
+
+ case PWM_CONFIG_START:
+ __ns9xxx_pwm_start(p);
+ pr_debug("%s:%d start\n", p->pwm->bus_id, p->chan);
+ break;
+
+ case PWM_CONFIG_POLARITY:
+ __ns9xxx_pwm_config_polarity(p, c);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ spin_unlock_irqrestore(&p->lock, flags);
+ return ret;
+}
+
+/* PWM configuration */
+static int ns9xxx_pwm_config(struct pwm_channel *p, struct pwm_channel_config *c)
+{
+ int was_on = 0;
+
+ if (p->pwm->config_nosleep) {
+ if (!p->pwm->config_nosleep(p, c))
+ return 0;
+ }
+
+ might_sleep();
+
+ was_on = __ns9xxx_pwm_is_on(p);
+ if (was_on < 0)
+ return was_on;
+
+ if (c->config_mask & PWM_CONFIG_PERIOD_TICKS) {
+ __ns9xxx_pwm_config_period_ticks(p, c);
+ if (!(c->config_mask & PWM_CONFIG_DUTY_TICKS)) {
+ struct pwm_channel_config d = {
+ .config_mask = PWM_CONFIG_DUTY_TICKS,
+ .duty_ticks = p->duty_ticks,
+ };
+ __ns9xxx_pwm_config_duty_ticks(p, &d);
+ }
+ }
+
+ if (c->config_mask & PWM_CONFIG_DUTY_TICKS)
+ __ns9xxx_pwm_config_duty_ticks(p, c);
+
+ if (c->config_mask & PWM_CONFIG_POLARITY)
+ __ns9xxx_pwm_config_polarity(p, c);
+
+ if ((c->config_mask & PWM_CONFIG_START)
+ || (was_on && !(c->config_mask & PWM_CONFIG_STOP)))
+ __ns9xxx_pwm_start(p);
+
+ pr_debug("%s:%d config_mask %x\n", p->pwm->bus_id, p->chan, c->config_mask);
+
+ return 0;
+}
+
+/* PWM request function */
+static int ns9xxx_pwm_request(struct pwm_channel *p)
+{
+ struct ns9xxx_pwm *np = container_of(p->pwm, struct ns9xxx_pwm, pwm);
+ struct ns9xxx_pwm_channel *chi;
+ unsigned long flags;
+ char pwmgpio[20];
+ int ret;
+
+ chi = ns9xxx_pwm_to_channel(p);
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ sprintf(pwmgpio, "%s.%d", p->pwm->bus_id, p->chan);
+ ret = gpio_request(chi->gpio, pwmgpio);
+ if (ret) {
+ pr_err("Request of GPIO %i failure\n", chi->gpio);
+ goto gpio_err;
+ }
+
+ ret = __ns9xxx_pwm_config_gpio(p, 0);
+ if (ret) {
+ pr_err("Configuring GPIO %i failure\n", chi->gpio);
+ goto gpio_cfg;
+ }
+
+ /* TODO, request also the timer? use the enable bit of the timer to
+ * determine if the timer is alredy enabled == requested
+ *
+ * This can not be done, ATM, as Uboot sets TE active by default
+ */
+
+ clk_enable(np->clk);
+ p->tick_hz = clk_get_rate(np->clk);
+ __ns9xxx_pwm_stop(p);
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ pr_debug("%s: tick_hz = %lu\n", __func__, p->tick_hz);
+
+ return 0;
+
+gpio_cfg:
+ gpio_configure_ns921x_unlocked(chi->gpio, 1, NS921X_GPIO_DONT_INVERT,
+ NS921X_GPIO_FUNC_3, 0);
+ gpio_free(chi->gpio);
+gpio_err:
+ return ret;
+}
+
+/* Frees resources taken by PWM */
+static void ns9xxx_pwm_free(struct pwm_channel *p)
+{
+ struct ns9xxx_pwm *np = container_of(p->pwm, struct ns9xxx_pwm, pwm);
+ struct ns9xxx_pwm_channel *chi;
+ chi = ns9xxx_pwm_to_channel(p);
+
+
+ pr_debug("%s\n", __func__);
+
+ clk_disable(np->clk);
+
+ gpio_configure_ns921x_unlocked(chi->gpio, 1, NS921X_GPIO_DONT_INVERT,
+ NS921X_GPIO_FUNC_3, 0);
+ gpio_free(chi->gpio);
+}
+
+static int __init ns9xxx_pwmc_probe(struct platform_device *pdev)
+{
+ struct ns9xxx_pwm *np;
+ struct ns9xxx_pwm_pdata *pdata;
+ int ret;
+
+ pr_debug("Probing a new device (ID %i)\n", pdev->id);
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ pr_err("No platform data passed? Aborting.\n");
+ return -EINVAL;
+ }
+
+ /* Sanity check */
+ if (pdata->number_channels > NS9XXX_PWM_CHANNEL_MAX) {
+ pr_err("Invalid number '%i' of channels\n", pdata->number_channels);
+ return -EINVAL;
+ }
+
+ pr_debug("%i PWM channels registered.\n", pdata->number_channels);
+
+ np = kzalloc(sizeof(*np), GFP_KERNEL);
+ if (!np) {
+ ret = -ENOMEM;
+ goto err_ns9xxx_pwm_alloc;
+ }
+
+ spin_lock_init(&np->lock);
+ platform_set_drvdata(pdev, np);
+
+ /* @XXX: Probably not the best place for this assignment */
+ np->pdata = pdata;
+
+ np->pwm.bus_id = pdev->dev.bus_id;
+ np->pwm.nchan = pdata->number_channels;
+
+ /* Copy the external platform data to our internal structure */
+ //memcpy(&np->pdata, pdata, sizeof(*pdata));
+
+ np->pwm.owner = THIS_MODULE;
+ np->pwm.request = ns9xxx_pwm_request;
+ np->pwm.free = ns9xxx_pwm_free;
+ np->pwm.config_nosleep = ns9xxx_pwm_config_nosleep;
+ np->pwm.config = ns9xxx_pwm_config;
+
+ ret = pwm_register(&np->pwm);
+ if (ret) {
+ pr_debug("Failure registering pwm\n");
+ goto err_pwm_register;
+ }
+
+ np->clk = clk_get(&pdev->dev, "ahbclock");
+ if (IS_ERR(np->clk)) {
+ pr_info("%s: clk_get error %ld\n", np->pwm.bus_id, PTR_ERR(np->clk));
+ ret = -ENODEV;
+ goto err_clk_get;
+ }
+
+ return 0;
+
+err_pwm_register:
+ clk_put(np->clk);
+err_clk_get:
+ platform_set_drvdata(pdev, NULL);
+ kfree(np);
+err_ns9xxx_pwm_alloc:
+ return ret;
+}
+
+static int __devexit ns9xxx_pwmc_remove(struct platform_device *pdev)
+{
+ struct ns9xxx_pwm *np;
+ struct ns9xxx_pwm_pdata *pdata;
+ struct device *d = &pdev->dev;
+ struct ns9xxx_pwm_channel *chi;
+ struct pwm_channel *p;
+ int ret;
+ u32 i;
+
+ pr_debug("Removing device (ID %i)\n", pdev->id);
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ pr_err("No platform data passed? Aborting.\n");
+ return -EINVAL;
+ }
+
+ np = platform_get_drvdata(pdev);
+ p = np->pwm.channels;
+
+ ret = pwm_unregister(&np->pwm);
+ if (ret) {
+ pr_debug("Failure unregistering pwm\n");
+ }
+
+ /*
+ * @TODO: We might want to reset Read and Capture Register, but we can not write to it.
+ */
+ for (i=0; i < pdata->number_channels; i++) {
+
+ chi = ns9xxx_pwm_to_channel(p+i);
+
+ /* Resets Timer Control Register */
+ writel(0x00, SYS_TC(chi->timer));
+
+ /* Resets High, Low, Reload Registers*/
+ writel(0x00, SYS_TRELCCR(chi->timer));
+ writel(0x00, SYS_THR(chi->timer - 6)); /* high equals to reload */
+ writel(0x00, SYS_TLR(chi->timer - 6));
+
+ /* Sets GPIO as inputs */
+ gpio_configure_ns921x_unlocked(chi->gpio, 1, NS921X_GPIO_DONT_INVERT,
+ NS921X_GPIO_FUNC_3, 0);
+
+ /* Frees GPIO */
+ gpio_free(chi->gpio);
+ }
+
+ clk_put(np->clk);
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(np);
+ module_put(d->driver->owner);
+
+ return 0;
+}
+
+static struct platform_driver ns9xxx_pwm_driver = {
+ .driver = {
+ .name = "ns9xxx_pwmc",
+ .owner = THIS_MODULE,
+ },
+ .probe = ns9xxx_pwmc_probe,
+ .remove = __devexit_p(ns9xxx_pwmc_remove),
+};
+
+static int __init ns9xxx_pwm_init(void)
+{
+ return platform_driver_register(&ns9xxx_pwm_driver);
+}
+
+module_init(ns9xxx_pwm_init);
+
+static void ns9xxx_pwm_exit(void)
+{
+ platform_driver_unregister(&ns9xxx_pwm_driver);
+}
+
+module_exit(ns9xxx_pwm_exit);
+
+MODULE_AUTHOR("Hector Oron <Hector.Oron <at> digi.com>");
+MODULE_DESCRIPTION("Driver for ns9xxx PWMC peripheral");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ns9xxx_pwmc");
diff --git a/drivers/pwm/pwm.c b/drivers/pwm/pwm.c
new file mode 100644
index 000000000000..32e84f86bf62
--- /dev/null
+++ b/drivers/pwm/pwm.c
@@ -0,0 +1,626 @@
+/*
+ * drivers/pwm/pwm.c
+ *
+ * Copyright (C) 2008 Bill Gatliff
+ *
+ * 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 0
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/pwm.h>
+
+static int __pwm_create_sysfs(struct pwm_device *pwm);
+
+static LIST_HEAD(pwm_device_list);
+static DEFINE_MUTEX(device_list_mutex);
+static struct class pwm_class;
+static struct workqueue_struct *pwm_handler_workqueue;
+
+int pwm_register(struct pwm_device *pwm)
+{
+ struct pwm_channel *p;
+ int wchan;
+ int ret = 0;
+
+ spin_lock_init(&pwm->list_lock);
+
+ p = kcalloc(pwm->nchan, sizeof(struct pwm_channel), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ for (wchan = 0; wchan < pwm->nchan; wchan++) {
+ spin_lock_init(&p[wchan].lock);
+ init_completion(&p[wchan].complete);
+ p[wchan].chan = wchan;
+ p[wchan].pwm = pwm;
+ }
+
+ pwm->channels = p;
+
+ mutex_lock(&device_list_mutex);
+
+ list_add_tail(&pwm->list, &pwm_device_list);
+ ret = __pwm_create_sysfs(pwm);
+ if (ret) {
+ mutex_unlock(&device_list_mutex);
+ goto err_create_sysfs;
+ }
+
+ mutex_unlock(&device_list_mutex);
+
+ pr_info("%s: %d channels\n", pwm->bus_id, pwm->nchan);
+ return 0;
+
+err_create_sysfs:
+ kfree(p);
+
+ return ret;
+}
+
+EXPORT_SYMBOL(pwm_register);
+
+static int __match_device(struct device *dev, void *data)
+{
+ return dev_get_drvdata(dev) == data;
+}
+
+int pwm_unregister(struct pwm_device *pwm)
+{
+ int wchan;
+ struct device *dev;
+
+ mutex_lock(&device_list_mutex);
+
+ for (wchan = 0; wchan < pwm->nchan; wchan++) {
+ if (pwm->channels[wchan].flags & FLAG_REQUESTED) {
+ mutex_unlock(&device_list_mutex);
+ return -EBUSY;
+ }
+ }
+
+ for (wchan = 0; wchan < pwm->nchan; wchan++) {
+ dev = class_find_device(&pwm_class, NULL,
+ &pwm->channels[wchan], __match_device);
+ if (dev) {
+ put_device(dev);
+ device_unregister(dev);
+ }
+ }
+
+ kfree(pwm->channels);
+ list_del(&pwm->list);
+ mutex_unlock(&device_list_mutex);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(pwm_unregister);
+
+static struct pwm_device *__pwm_find_device(const char *bus_id)
+{
+ struct pwm_device *p;
+
+ list_for_each_entry(p, &pwm_device_list, list) {
+ if (!strcmp(bus_id, p->bus_id))
+ return p;
+ }
+ return NULL;
+}
+
+static int __pwm_request_channel(struct pwm_channel *p, const char *requester)
+{
+ int ret;
+
+ if (test_and_set_bit(FLAG_REQUESTED, &p->flags))
+ return -EBUSY;
+
+ if (p->pwm->request) {
+ ret = p->pwm->request(p);
+ if (ret) {
+ clear_bit(FLAG_REQUESTED, &p->flags);
+ return ret;
+ }
+ }
+
+ p->requester = requester;
+ return 0;
+}
+
+struct pwm_channel *pwm_request(const char *bus_id, int chan, const char *requester)
+{
+ struct pwm_device *p;
+ int ret;
+
+ mutex_lock(&device_list_mutex);
+
+ p = __pwm_find_device(bus_id);
+ if (!p || chan >= p->nchan)
+ goto err_no_device;
+
+ if (!try_module_get(p->owner))
+ goto err_module_get_failed;
+
+ ret = __pwm_request_channel(&p->channels[chan], requester);
+ if (ret)
+ goto err_request_failed;
+
+ mutex_unlock(&device_list_mutex);
+
+ pr_debug("%s: %s:%d returns %p\n", __func__, bus_id, chan, &p->channels[chan]);
+
+ return &p->channels[chan];
+
+err_request_failed:
+ module_put(p->owner);
+err_module_get_failed:
+err_no_device:
+
+ mutex_unlock(&device_list_mutex);
+
+ pr_debug("%s: %s:%d returns NULL\n", __func__, bus_id, chan);
+
+ return NULL;
+}
+
+EXPORT_SYMBOL(pwm_request);
+
+void pwm_free(struct pwm_channel *p)
+{
+ mutex_lock(&device_list_mutex);
+
+ if (!test_and_clear_bit(FLAG_REQUESTED, &p->flags))
+ goto done;
+
+ pwm_stop(p);
+ pwm_unsynchronize(p, NULL);
+ pwm_set_handler(p, NULL, NULL);
+
+ if (p->pwm->free)
+ p->pwm->free(p);
+ module_put(p->pwm->owner);
+
+ pr_debug("%s: %s:%d free\n", __func__, p->pwm->bus_id, p->chan);
+
+done:
+ mutex_unlock(&device_list_mutex);
+}
+
+EXPORT_SYMBOL(pwm_free);
+
+unsigned long pwm_ns_to_ticks(struct pwm_channel *p, unsigned long nsecs)
+{
+ unsigned long long ticks;
+
+ ticks = nsecs;
+ ticks *= p->tick_hz;
+ do_div(ticks, 1000000000);
+
+ return ticks;
+}
+
+EXPORT_SYMBOL(pwm_ns_to_ticks);
+
+unsigned long pwm_ticks_to_ns(struct pwm_channel *p, unsigned long ticks)
+{
+ unsigned long long ns;
+
+ if (!p->tick_hz)
+ return 0;
+
+ ns = ticks;
+ ns *= 1000000000UL;
+ do_div(ns, p->tick_hz);
+ return ns;
+}
+
+EXPORT_SYMBOL(pwm_ticks_to_ns);
+
+static void pwm_config_ns_to_ticks(struct pwm_channel *p, struct pwm_channel_config *c)
+{
+ if (c->config_mask & PWM_CONFIG_PERIOD_NS) {
+ c->period_ticks = pwm_ns_to_ticks(p, c->period_ns);
+ c->config_mask &= ~PWM_CONFIG_PERIOD_NS;
+ c->config_mask |= PWM_CONFIG_PERIOD_TICKS;
+ }
+
+ if (c->config_mask & PWM_CONFIG_DUTY_NS) {
+ c->duty_ticks = pwm_ns_to_ticks(p, c->duty_ns);
+ c->config_mask &= ~PWM_CONFIG_DUTY_NS;
+ c->config_mask |= PWM_CONFIG_DUTY_TICKS;
+ }
+}
+
+static void pwm_config_percent_to_ticks(struct pwm_channel *p, struct pwm_channel_config *c)
+{
+ if (c->config_mask & PWM_CONFIG_DUTY_PERCENT) {
+ if (c->config_mask & PWM_CONFIG_PERIOD_TICKS)
+ c->duty_ticks = c->period_ticks;
+ else
+ c->duty_ticks = p->period_ticks;
+
+ c->duty_ticks *= c->duty_percent;
+ c->duty_ticks /= 100;
+ c->config_mask &= ~PWM_CONFIG_DUTY_PERCENT;
+ c->config_mask |= PWM_CONFIG_DUTY_TICKS;
+ }
+}
+
+int pwm_config_nosleep(struct pwm_channel *p, struct pwm_channel_config *c)
+{
+ if (!p->pwm->config_nosleep)
+ return -EINVAL;
+
+ pwm_config_ns_to_ticks(p, c);
+ pwm_config_percent_to_ticks(p, c);
+
+ return p->pwm->config_nosleep(p, c);
+}
+
+EXPORT_SYMBOL(pwm_config_nosleep);
+
+int pwm_config(struct pwm_channel *p, struct pwm_channel_config *c)
+{
+ int ret = 0;
+
+ if (unlikely(!p->pwm->config)) {
+ pr_debug("%s: %s:%d has no config handler (-EINVAL)\n",
+ __func__, p->pwm->bus_id, p->chan);
+ return -EINVAL;
+ }
+
+ pwm_config_ns_to_ticks(p, c);
+ pwm_config_percent_to_ticks(p, c);
+
+ switch (c->config_mask & (PWM_CONFIG_PERIOD_TICKS | PWM_CONFIG_DUTY_TICKS)) {
+ case PWM_CONFIG_PERIOD_TICKS:
+ if (p->duty_ticks > c->period_ticks) {
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ case PWM_CONFIG_DUTY_TICKS:
+ if (p->period_ticks < c->duty_ticks) {
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ case PWM_CONFIG_DUTY_TICKS | PWM_CONFIG_PERIOD_TICKS:
+ if (c->duty_ticks > c->period_ticks) {
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ default:
+ break;
+ }
+
+err:
+ pr_debug("%s: config_mask %d period_ticks %lu duty_ticks %lu"
+ " polarity %d duty_ns %lu period_ns %lu duty_percent %d\n",
+ __func__, c->config_mask, c->period_ticks, c->duty_ticks,
+ c->polarity, c->duty_ns, c->period_ns, c->duty_percent);
+
+ if (ret)
+ return ret;
+ return p->pwm->config(p, c);
+}
+
+EXPORT_SYMBOL(pwm_config);
+
+int pwm_set_period_ns(struct pwm_channel *p, unsigned long period_ns)
+{
+ struct pwm_channel_config c = {
+ .config_mask = PWM_CONFIG_PERIOD_TICKS,
+ .period_ticks = pwm_ns_to_ticks(p, period_ns),
+ };
+
+ return pwm_config(p, &c);
+}
+
+EXPORT_SYMBOL(pwm_set_period_ns);
+
+unsigned long pwm_get_period_ns(struct pwm_channel *p)
+{
+ return pwm_ticks_to_ns(p, p->period_ticks);
+}
+
+EXPORT_SYMBOL(pwm_get_period_ns);
+
+int pwm_set_duty_ns(struct pwm_channel *p, unsigned long duty_ns)
+{
+ struct pwm_channel_config c = {
+ .config_mask = PWM_CONFIG_DUTY_TICKS,
+ .duty_ticks = pwm_ns_to_ticks(p, duty_ns),
+ };
+ return pwm_config(p, &c);
+}
+
+EXPORT_SYMBOL(pwm_set_duty_ns);
+
+unsigned long pwm_get_duty_ns(struct pwm_channel *p)
+{
+ return pwm_ticks_to_ns(p, p->duty_ticks);
+}
+
+EXPORT_SYMBOL(pwm_get_duty_ns);
+
+int pwm_set_duty_percent(struct pwm_channel *p, int percent)
+{
+ struct pwm_channel_config c = {
+ .config_mask = PWM_CONFIG_DUTY_PERCENT,
+ .duty_percent = percent,
+ };
+ return pwm_config(p, &c);
+}
+
+EXPORT_SYMBOL(pwm_set_duty_percent);
+
+int pwm_set_polarity(struct pwm_channel *p, int active_high)
+{
+ struct pwm_channel_config c = {
+ .config_mask = PWM_CONFIG_POLARITY,
+ .polarity = active_high,
+ };
+ return pwm_config(p, &c);
+}
+
+EXPORT_SYMBOL(pwm_set_polarity);
+
+int pwm_start(struct pwm_channel *p)
+{
+ struct pwm_channel_config c = {
+ .config_mask = PWM_CONFIG_START,
+ };
+ return pwm_config(p, &c);
+}
+
+EXPORT_SYMBOL(pwm_start);
+
+int pwm_stop(struct pwm_channel *p)
+{
+ struct pwm_channel_config c = {
+ .config_mask = PWM_CONFIG_STOP,
+ };
+ return pwm_config(p, &c);
+}
+
+EXPORT_SYMBOL(pwm_stop);
+
+int pwm_synchronize(struct pwm_channel *p, struct pwm_channel *to_p)
+{
+ if (p->pwm != to_p->pwm) {
+ /* TODO: support cross-device synchronization */
+ return -EINVAL;
+ }
+
+ if (!p->pwm->synchronize)
+ return -EINVAL;
+
+ return p->pwm->synchronize(p, to_p);
+}
+
+EXPORT_SYMBOL(pwm_synchronize);
+
+int pwm_unsynchronize(struct pwm_channel *p, struct pwm_channel *from_p)
+{
+ if (from_p && (p->pwm != from_p->pwm)) {
+ /* TODO: support cross-device synchronization */
+ return -EINVAL;
+ }
+
+ if (!p->pwm->unsynchronize)
+ return -EINVAL;
+
+ return p->pwm->unsynchronize(p, from_p);
+}
+
+EXPORT_SYMBOL(pwm_unsynchronize);
+
+static void pwm_handler(struct work_struct *w)
+{
+ struct pwm_channel *p = container_of(w, struct pwm_channel,
+ handler_work);
+ if (p->handler && p->handler(p, p->handler_data))
+ pwm_stop(p);
+}
+
+static void __pwm_callback(struct pwm_channel *p)
+{
+ queue_work(pwm_handler_workqueue, &p->handler_work);
+ pr_debug("%s:%d handler %p scheduled with data %p\n",
+ p->pwm->bus_id, p->chan, p->handler, p->handler_data);
+}
+
+int pwm_set_handler(struct pwm_channel *p, pwm_handler_t handler, void *data)
+{
+ if (p->pwm->set_callback) {
+ p->handler_data = data;
+ p->handler = handler;
+ INIT_WORK(&p->handler_work, pwm_handler);
+ return p->pwm->set_callback(p, handler ? __pwm_callback : NULL);
+ }
+ return -EINVAL;
+}
+
+EXPORT_SYMBOL(pwm_set_handler);
+
+static ssize_t pwm_run_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct pwm_channel *p = dev_get_drvdata(dev);
+ if (sysfs_streq(buf, "1"))
+ pwm_start(p);
+ else if (sysfs_streq(buf, "0"))
+ pwm_stop(p);
+ return len;
+}
+
+static DEVICE_ATTR(run, 0200, NULL, pwm_run_store);
+
+static ssize_t pwm_duty_ns_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pwm_channel *p = dev_get_drvdata(dev);
+ return sprintf(buf, "%lu\n", pwm_get_duty_ns(p));
+}
+
+static ssize_t pwm_duty_ns_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long duty_ns;
+ struct pwm_channel *p = dev_get_drvdata(dev);
+
+ if (1 == sscanf(buf, "%lu", &duty_ns))
+ pwm_set_duty_ns(p, duty_ns);
+ return len;
+}
+
+static DEVICE_ATTR(duty_ns, 0644, pwm_duty_ns_show, pwm_duty_ns_store);
+
+static ssize_t pwm_period_ns_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pwm_channel *p = dev_get_drvdata(dev);
+ return sprintf(buf, "%lu\n", pwm_get_period_ns(p));
+}
+
+static ssize_t pwm_period_ns_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long period_ns;
+ struct pwm_channel *p = dev_get_drvdata(dev);
+
+ if (1 == sscanf(buf, "%lu", &period_ns))
+ pwm_set_period_ns(p, period_ns);
+
+ return len;
+}
+
+static DEVICE_ATTR(period_ns, 0644, pwm_period_ns_show, pwm_period_ns_store);
+
+static ssize_t pwm_polarity_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pwm_channel *p = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n", p->active_low ? 0 : 1);
+}
+
+static ssize_t pwm_polarity_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ int polarity;
+ struct pwm_channel *p = dev_get_drvdata(dev);
+
+ if (1 == sscanf(buf, "%d", &polarity))
+ pwm_set_polarity(p, polarity);
+ return len;
+}
+
+static DEVICE_ATTR(polarity, 0644, pwm_polarity_show, pwm_polarity_store);
+
+static ssize_t pwm_request_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pwm_channel *p = dev_get_drvdata(dev);
+ mutex_lock(&device_list_mutex);
+ __pwm_request_channel(p, "sysfs");
+ mutex_unlock(&device_list_mutex);
+
+ return sprintf(buf, "%s\n", p->requester);
+}
+
+static ssize_t pwm_request_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct pwm_channel *p = dev_get_drvdata(dev);
+ pwm_free(p);
+ return len;
+}
+
+static DEVICE_ATTR(request, 0644, pwm_request_show, pwm_request_store);
+
+static const struct attribute *pwm_attrs[] = {
+ &dev_attr_run.attr,
+ &dev_attr_polarity.attr,
+ &dev_attr_duty_ns.attr,
+ &dev_attr_period_ns.attr,
+ &dev_attr_request.attr,
+ NULL,
+};
+
+static const struct attribute_group pwm_device_attr_group = {
+ .attrs = (struct attribute **)pwm_attrs,
+};
+
+static int __pwm_create_sysfs(struct pwm_device *pwm)
+{
+ int ret = 0;
+ struct device *dev;
+ int wchan;
+
+ for (wchan = 0; wchan < pwm->nchan; wchan++) {
+ dev = device_create(&pwm_class, pwm->dev, MKDEV(0, 0),
+ pwm->channels + wchan, "%s:%d", pwm->bus_id, wchan);
+ if (!dev)
+ goto err_dev_create;
+ ret = sysfs_create_group(&dev->kobj, &pwm_device_attr_group);
+ if (ret)
+ goto err_dev_group_create;
+ }
+
+ return ret;
+
+err_dev_group_create:
+err_dev_create:
+ /* TODO: undo all the successful device_create calls */
+ return -ENODEV;
+}
+
+static struct class_attribute pwm_class_attrs[] = {
+ __ATTR_NULL,
+};
+
+static struct class pwm_class = {
+ .name = "pwm",
+ .owner = THIS_MODULE,
+
+ .class_attrs = pwm_class_attrs,
+};
+
+static int __init pwm_init(void)
+{
+ int ret;
+
+ /* TODO: how to deal with devices that register very early? */
+
+ ret = class_register(&pwm_class);
+ if (ret < 0)
+ return ret;
+
+ pwm_handler_workqueue = create_workqueue("pwmd");
+
+ return 0;
+}
+
+postcore_initcall(pwm_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 39360e2a4540..b6c600dac022 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -73,4 +73,34 @@ config REGULATOR_DA903X
Say y here to support the BUCKs and LDOs regulators found on
Dialog Semiconductor DA9030/DA9034 PMIC.
+config REGULATOR_MC13892
+ tristate "MC13892 Regulator Support"
+ depends on REGULATOR
+ depends on MXC_PMIC_MC13892
+ default y
+
+config REGULATOR_MC13783
+ tristate "MC13783 Regulator Support"
+ depends on REGULATOR
+ depends on MXC_PMIC_MC13783
+ default y
+
+config REGULATOR_MC34704
+ tristate "MC34704 Regulator Support"
+ depends on REGULATOR
+ depends on MXC_PMIC_MC34704
+ default y
+
+config REGULATOR_STMP3XXX
+ tristate "STMP3xxx Regulator Support"
+ depends on REGULATOR
+ depends on ARCH_STMP3XXX
+ default y
+
+config REGULATOR_MC9S08DZ60
+ tristate "mc9s08dz60 Regulator Support"
+ depends on REGULATOR
+ depends on MXC_PMIC_MC9S08DZ60
+ default y
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 254d40c02ee8..bc47cb77d7c5 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -2,7 +2,6 @@
# Makefile for regulator drivers.
#
-
obj-$(CONFIG_REGULATOR) += core.o
obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
@@ -12,4 +11,11 @@ obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
+obj-$(CONFIG_REGULATOR_MC13892) += reg-mc13892.o
+obj-$(CONFIG_REGULATOR_MC13783) += reg-mc13783.o
+obj-$(CONFIG_REGULATOR_MC34704) += reg-mc34704.o
+obj-$(CONFIG_REGULATOR_STMP3XXX) += stmp3xxx.o
+
+obj-$(CONFIG_REGULATOR_MC9S08DZ60) += reg-mc9s08dz60.o
+
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 02a774424e8d..d2a23066e3fb 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -927,6 +927,11 @@ struct regulator *regulator_get(struct device *dev, const char *id)
goto found;
}
}
+ list_for_each_entry(rdev, &regulator_list, list) {
+ if (strcmp(rdev->desc->name, id) == 0) {
+ goto found;
+ }
+ }
printk(KERN_ERR "regulator: Unable to get requested regulator: %s\n",
id);
mutex_unlock(&regulator_list_mutex);
diff --git a/drivers/regulator/reg-mc13783.c b/drivers/regulator/reg-mc13783.c
new file mode 100644
index 000000000000..c761c7b99243
--- /dev/null
+++ b/drivers/regulator/reg-mc13783.c
@@ -0,0 +1,2661 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/mc13783/core.h>
+#include <linux/platform_device.h>
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+/*
+ * Convenience conversion.
+ * Here atm, maybe there is somewhere better for this.
+ */
+#define mV_to_uV(mV) (mV * 1000)
+#define uV_to_mV(uV) (uV / 1000)
+#define V_to_uV(V) (mV_to_uV(V * 1000))
+#define uV_to_V(uV) (uV_to_mV(uV) / 1000)
+
+/*!
+ * @enum regulator_voltage_sw
+ * @brief PMIC regulator SW output voltage.
+ */
+enum {
+ SW_0_9V = 0, /*!< 0.900 V */
+ SW_0_925V, /*!< 0.925 V */
+ SW_0_95V, /*!< 0.950 V */
+ SW_0_975V, /*!< 0.975 V */
+ SW_1V, /*!< 1.000 V */
+ SW_1_025V, /*!< 1.025 V */
+ SW_1_05V, /*!< 1.050 V */
+ SW_1_075V, /*!< 1.075 V */
+ SW_1_1V, /*!< 1.100 V */
+ SW_1_125V, /*!< 1.125 V */
+ SW_1_15V, /*!< 1.150 V */
+ SW_1_175V, /*!< 1.175 V */
+ SW_1_2V, /*!< 1.200 V */
+ SW_1_225V, /*!< 1.225 V */
+ SW_1_25V, /*!< 1.250 V */
+ SW_1_275V, /*!< 1.275 V */
+ SW_1_3V, /*!< 1.300 V */
+ SW_1_325V, /*!< 1.325 V */
+ SW_1_35V, /*!< 1.350 V */
+ SW_1_375V, /*!< 1.375 V */
+ SW_1_4V, /*!< 1.400 V */
+ SW_1_425V, /*!< 1.425 V */
+ SW_1_45V, /*!< 1.450 V */
+ SW_1_475V, /*!< 1.475 V */
+ SW_1_5V, /*!< 1.500 V */
+ SW_1_525V, /*!< 1.525 V */
+ SW_1_55V, /*!< 1.550 V */
+ SW_1_575V, /*!< 1.575 V */
+ SW_1_6V, /*!< 1.600 V */
+ SW_1_625V, /*!< 1.625 V */
+ SW_1_65V, /*!< 1.650 V */
+ SW_1_675V, /*!< 1.675 V */
+ SW_1_7V, /*!< 1.700 V */
+ SW_1_8V = 36, /*!< 1.800 V */
+ SW_1_85V = 40, /*!< 1.850 V */
+ SW_2V = 44, /*!< 2_000 V */
+ SW_2_1V = 48, /*!< 2_100 V */
+ SW_2_2V = 52, /*!< 2_200 V */
+} regulator_voltage_sw;
+
+/*!
+ * @enum regulator_voltage_violo
+ * @brief PMIC regulator VIOLO output voltage.
+ */
+enum {
+ VIOLO_1_2V = 0, /*!< 1.2 V */
+ VIOLO_1_3V, /*!< 1.3 V */
+ VIOLO_1_5V, /*!< 1.5 V */
+ VIOLO_1_8V, /*!< 1.8 V */
+} regulator_voltage_violo;
+
+/*!
+ * @enum regulator_voltage_vdig
+ * @brief PMIC regulator VDIG output voltage.
+ */
+enum {
+ VDIG_1_2V = 0, /*!< 1.2 V */
+ VDIG_1_3V, /*!< 1.3 V */
+ VDIG_1_5V, /*!< 1.5 V */
+ VDIG_1_8V, /*!< 1.8 V */
+} regulator_voltage_vdig;
+
+/*!
+ * @enum regulator_voltage_vgen
+ * @brief PMIC regulator VGEN output voltage.
+ */
+enum {
+ VGEN_1_2V = 0, /*!< 1.2 V */
+ VGEN_1_3V, /*!< 1.3 V */
+ VGEN_1_5V, /*!< 1.5 V */
+ VGEN_1_8V, /*!< 1.8 V */
+ VGEN_1_1V, /*!< 1.1 V */
+ VGEN_2V, /*!< 2 V */
+ VGEN_2_775V, /*!< 2.775 V */
+ VGEN_2_4V, /*!< 2.4 V */
+} regulator_voltage_vgen;
+
+/*!
+ * @enum regulator_voltage_vrfdig
+ * @brief PMIC regulator VRFDIG output voltage.
+ */
+enum {
+ VRFDIG_1_2V = 0, /*!< 1.2 V */
+ VRFDIG_1_5V, /*!< 1.5 V */
+ VRFDIG_1_8V, /*!< 1.8 V */
+ VRFDIG_1_875V, /*!< 1.875 V */
+} regulator_voltage_vrfdig;
+
+/*!
+ * @enum regulator_voltage_vrfref
+ * @brief PMIC regulator VRFREF output voltage.
+ */
+enum {
+ VRFREF_2_475V = 0, /*!< 2.475 V */
+ VRFREF_2_6V, /*!< 2.600 V */
+ VRFREF_2_7V, /*!< 2.700 V */
+ VRFREF_2_775V, /*!< 2.775 V */
+} regulator_voltage_vrfref;
+
+/*!
+ * @enum regulator_voltage_vrfcp
+ * @brief PMIC regulator VRFCP output voltage.
+ */
+enum {
+ VRFCP_2_7V = 0, /*!< 2.700 V */
+ VRFCP_2_775V, /*!< 2.775 V */
+} regulator_voltage_vrfcp;
+
+/*!
+ * @enum regulator_voltage_vsim
+ * @brief PMIC linear regulator VSIM output voltage.
+ */
+enum {
+ VSIM_1_8V = 0, /*!< 1.8 V */
+ VSIM_2_9V, /*!< 2.90 V */
+ VSIM_3V = 1, /*!< 3 V */
+} regulator_voltage_vsim;
+
+/*!
+ * @enum regulator_voltage_vesim
+ * @brief PMIC regulator VESIM output voltage.
+ */
+enum {
+ VESIM_1_8V = 0, /*!< 1.80 V */
+ VESIM_2_9V, /*!< 2.90 V */
+} regulator_voltage_vesim;
+
+/*!
+ * @enum regulator_voltage_vcam
+ * @brief PMIC regulator VCAM output voltage.
+ */
+enum {
+ VCAM_1_5V = 0, /*!< 1.50 V */
+ VCAM_1_8V, /*!< 1.80 V */
+ VCAM_2_5V, /*!< 2.50 V */
+ VCAM_2_55V, /*!< 2.55 V */
+ VCAM_2_6V, /*!< 2.60 V */
+ VCAM_2_75V, /*!< 2.75 V */
+ VCAM_2_8V, /*!< 2.80 V */
+ VCAM_3V, /*!< 3.00 V */
+} regulator_voltage_vcam;
+
+/*!
+ * @enum regulator_voltage_vvib
+ * @brief PMIC linear regulator V_VIB output voltage.
+ */
+enum {
+ VVIB_1_3V = 0, /*!< 1.30 V */
+ VVIB_1_8V, /*!< 1.80 V */
+ VVIB_2V, /*!< 2 V */
+ VVIB_3V, /*!< 3 V */
+} regulator_voltage_vvib;
+
+/*!
+ * @enum regulator_voltage_vmmc
+ * @brief MC13783 PMIC regulator VMMC output voltage.
+ */
+enum {
+ VMMC_1_6V = 0, /*!< 1.60 V */
+ VMMC_1_8V, /*!< 1.80 V */
+ VMMC_2V, /*!< 2.00 V */
+ VMMC_2_6V, /*!< 2.60 V */
+ VMMC_2_7V, /*!< 2.70 V */
+ VMMC_2_8V, /*!< 2.80 V */
+ VMMC_2_9V, /*!< 2.90 V */
+ VMMC_3V, /*!< 3.00 V */
+} regulator_voltage_vmmc;
+
+/*!
+ * @enum regulator_voltage_vrf
+ * @brief PMIC regulator VRF output voltage.
+ */
+enum {
+ VRF_1_5V = 0, /*!< 1.500 V */
+ VRF_1_875V, /*!< 1.875 V */
+ VRF_2_7V, /*!< 2.700 V */
+ VRF_2_775V, /*!< 2.775 V */
+} regulator_voltage_vrf;
+
+/*!
+ * @enum regulator_voltage_sw3
+ * @brief PMIC Switch mode regulator SW3 output voltages.
+ */
+enum {
+ SW3_5V = 0, /*!< 5.0 V */
+ SW3_5_5V = 3, /*!< 5.5 V */
+} regulator_voltage_sw3;
+
+/*!
+ * The \b TPmicDVSTransitionSpeed enum defines the rate with which the
+ * voltage transition occurs.
+ */
+enum {
+ ESysDependent,
+ E25mVEach4us,
+ E25mVEach8us,
+ E25mvEach16us
+} DVS_transition_speed;
+
+/*
+ * Reg Regulator Mode 0
+ */
+#define VAUDIO_EN_LSH 0
+#define VAUDIO_EN_WID 1
+#define VAUDIO_EN_ENABLE 1
+#define VAUDIO_EN_DISABLE 0
+#define VIOHI_EN_LSH 3
+#define VIOHI_EN_WID 1
+#define VIOHI_EN_ENABLE 1
+#define VIOHI_EN_DISABLE 0
+#define VIOLO_EN_LSH 6
+#define VIOLO_EN_WID 1
+#define VIOLO_EN_ENABLE 1
+#define VIOLO_EN_DISABLE 0
+#define VDIG_EN_LSH 9
+#define VDIG_EN_WID 1
+#define VDIG_EN_ENABLE 1
+#define VDIG_EN_DISABLE 0
+#define VGEN_EN_LSH 12
+#define VGEN_EN_WID 1
+#define VGEN_EN_ENABLE 1
+#define VGEN_EN_DISABLE 0
+#define VRFDIG_EN_LSH 15
+#define VRFDIG_EN_WID 1
+#define VRFDIG_EN_ENABLE 1
+#define VRFDIG_EN_DISABLE 0
+#define VRFREF_EN_LSH 18
+#define VRFREF_EN_WID 1
+#define VRFREF_EN_ENABLE 1
+#define VRFREF_EN_DISABLE 0
+#define VRFCP_EN_LSH 21
+#define VRFCP_EN_WID 1
+#define VRFCP_EN_ENABLE 1
+#define VRFCP_EN_DISABLE 0
+
+/*
+ * Reg Regulator Mode 1
+ */
+#define VSIM_EN_LSH 0
+#define VSIM_EN_WID 1
+#define VSIM_EN_ENABLE 1
+#define VSIM_EN_DISABLE 0
+#define VESIM_EN_LSH 3
+#define VESIM_EN_WID 1
+#define VESIM_EN_ENABLE 1
+#define VESIM_EN_DISABLE 0
+#define VCAM_EN_LSH 6
+#define VCAM_EN_WID 1
+#define VCAM_EN_ENABLE 1
+#define VCAM_EN_DISABLE 0
+#define VRFBG_EN_LSH 9
+#define VRFBG_EN_WID 1
+#define VRFBG_EN_ENABLE 1
+#define VRFBG_EN_DISABLE 0
+#define VVIB_EN_LSH 11
+#define VVIB_EN_WID 1
+#define VVIB_EN_ENABLE 1
+#define VVIB_EN_DISABLE 0
+#define VRF1_EN_LSH 12
+#define VRF1_EN_WID 1
+#define VRF1_EN_ENABLE 1
+#define VRF1_EN_DISABLE 0
+#define VRF2_EN_LSH 15
+#define VRF2_EN_WID 1
+#define VRF2_EN_ENABLE 1
+#define VRF2_EN_DISABLE 0
+#define VMMC1_EN_LSH 18
+#define VMMC1_EN_WID 1
+#define VMMC1_EN_ENABLE 1
+#define VMMC1_EN_DISABLE 0
+#define VMMC2_EN_LSH 21
+#define VMMC2_EN_WID 1
+#define VMMC2_EN_ENABLE 1
+#define VMMC2_EN_DISABLE 0
+
+/*
+ * Reg Regulator Setting 0
+ */
+#define VIOLO_LSH 2
+#define VIOLO_WID 2
+#define VDIG_LSH 4
+#define VDIG_WID 2
+#define VGEN_LSH 6
+#define VGEN_WID 3
+#define VRFDIG_LSH 9
+#define VRFDIG_WID 2
+#define VRFREF_LSH 11
+#define VRFREF_WID 2
+#define VRFCP_LSH 13
+#define VRFCP_WID 1
+#define VSIM_LSH 14
+#define VSIM_WID 1
+#define VESIM_LSH 15
+#define VESIM_WID 1
+#define VCAM_LSH 16
+#define VCAM_WID 3
+
+/*
+ * Reg Regulator Setting 1
+ */
+#define VVIB_LSH 0
+#define VVIB_WID 2
+#define VRF1_LSH 2
+#define VRF1_WID 2
+#define VRF2_LSH 4
+#define VRF2_WID 2
+#define VMMC1_LSH 6
+#define VMMC1_WID 3
+#define VMMC2_LSH 9
+#define VMMC2_WID 3
+
+/*
+ * Reg Switcher 0
+ */
+#define SW1A_LSH 0
+#define SW1A_WID 6
+#define SW1A_DVS_LSH 6
+#define SW1A_DVS_WID 6
+#define SW1A_STDBY_LSH 12
+#define SW1A_STDBY_WID 6
+
+/*
+ * Reg Switcher 1
+ */
+#define SW1B_LSH 0
+#define SW1B_WID 6
+#define SW1B_DVS_LSH 6
+#define SW1B_DVS_WID 6
+#define SW1B_STDBY_LSH 12
+#define SW1B_STDBY_WID 6
+
+/*
+ * Reg Switcher 2
+ */
+#define SW2A_LSH 0
+#define SW2A_WID 6
+#define SW2A_DVS_LSH 6
+#define SW2A_DVS_WID 6
+#define SW2A_STDBY_LSH 12
+#define SW2A_STDBY_WID 6
+
+/*
+ * Reg Switcher 3
+ */
+#define SW2B_LSH 0
+#define SW2B_WID 6
+#define SW2B_DVS_LSH 6
+#define SW2B_DVS_WID 6
+#define SW2B_STDBY_LSH 12
+#define SW2B_STDBY_WID 6
+
+/*
+ * Reg Switcher 4
+ */
+#define SW1A_MODE_LSH 0
+#define SW1A_MODE_WID 2
+#define SW1A_STBY_MODE_LSH 2
+#define SW1A_STBY_MODE_WID 2
+#define SW1A_DVS_SPEED_LSH 6
+#define SW1A_DVS_SPEED_WID 2
+#define SW1B_MODE_LSH 10
+#define SW1B_MODE_WID 2
+#define SW1B_STBY_MODE_LSH 12
+#define SW1B_STBY_MODE_WID 2
+#define SW1B_DVS_SPEED_LSH 14
+#define SW1B_DVS_SPEED_WID 2
+
+/*
+ * Reg Switcher 5
+ */
+#define SW2A_MODE_LSH 0
+#define SW2A_MODE_WID 2
+#define SW2A_STBY_MODE_LSH 2
+#define SW2A_STBY_MODE_WID 2
+#define SW2A_DVS_SPEED_LSH 6
+#define SW2A_DVS_SPEED_WID 2
+#define SW2B_MODE_LSH 10
+#define SW2B_MODE_WID 2
+#define SW2B_STBY_MODE_LSH 12
+#define SW2B_STBY_MODE_WID 2
+#define SW2B_DVS_SPEED_LSH 14
+#define SW2B_DVS_SPEED_WID 2
+#define SW3_LSH 18
+#define SW3_WID 2
+#define SW3_EN_LSH 20
+#define SW3_EN_WID 2
+#define SW3_EN_ENABLE 1
+#define SW3_EN_DISABLE 0
+
+/*
+ * Reg Regulator Misc.
+ */
+#define GPO1_EN_LSH 6
+#define GPO1_EN_WID 1
+#define GPO1_EN_ENABLE 1
+#define GPO1_EN_DISABLE 0
+#define GPO2_EN_LSH 8
+#define GPO2_EN_WID 1
+#define GPO2_EN_ENABLE 1
+#define GPO2_EN_DISABLE 0
+#define GPO3_EN_LSH 10
+#define GPO3_EN_WID 1
+#define GPO3_EN_ENABLE 1
+#define GPO3_EN_DISABLE 0
+#define GPO4_EN_LSH 12
+#define GPO4_EN_WID 1
+#define GPO4_EN_ENABLE 1
+#define GPO4_EN_DISABLE 0
+
+/*
+ * Switcher mode configuration
+ */
+#define SW_MODE_SYNC_RECT_EN 0
+#define SW_MODE_PULSE_NO_SKIP_EN 1
+#define SW_MODE_PULSE_SKIP_EN 2
+#define SW_MODE_LOW_POWER_EN 3
+
+#define dvs_speed E25mvEach16us
+
+static int mc13783_vaudio_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VAUDIO_EN, VAUDIO_EN_ENABLE);
+ register_mask = BITFMASK(VAUDIO_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vaudio_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VAUDIO_EN, VAUDIO_EN_DISABLE);
+ register_mask = BITFMASK(VAUDIO_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_viohi_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VIOHI_EN, VIOHI_EN_ENABLE);
+ register_mask = BITFMASK(VIOHI_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_viohi_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VIOHI_EN, VIOHI_EN_DISABLE);
+ register_mask = BITFMASK(VIOHI_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_violo_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0, register1 = 0;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1200) && (mV < 1300))
+ voltage = VIOLO_1_2V;
+ else if ((mV >= 1300) && (mV < 1500))
+ voltage = VIOLO_1_3V;
+ else if ((mV >= 1500) && (mV < 1800))
+ voltage = VIOLO_1_5V;
+ else
+ voltage = VIOLO_1_8V;
+
+ register_val = BITFVAL(VIOLO, voltage);
+ register_mask = BITFMASK(VIOLO);
+ register1 = REG_REGULATOR_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_violo_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VIOLO);
+
+ switch (voltage) {
+ case VIOLO_1_2V:
+ mV = 1200;
+ break;
+ case VIOLO_1_3V:
+ mV = 1300;
+ break;
+ case VIOLO_1_5V:
+ mV = 1500;
+ break;
+ case VIOLO_1_8V:
+ mV = 1800;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_violo_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VIOLO_EN, VIOLO_EN_ENABLE);
+ register_mask = BITFMASK(VIOLO_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_violo_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VIOLO_EN, VIOLO_EN_DISABLE);
+ register_mask = BITFMASK(VIOLO_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vdig_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1200) && (mV < 1300))
+ voltage = VDIG_1_2V;
+ else if ((mV >= 1300) && (mV < 1500))
+ voltage = VDIG_1_3V;
+ else if ((mV >= 1500) && (mV < 1800))
+ voltage = VDIG_1_5V;
+ else
+ voltage = VDIG_1_8V;
+
+ register_val = BITFVAL(VDIG, voltage);
+ register_mask = BITFMASK(VDIG);
+ register1 = REG_REGULATOR_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vdig_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VDIG);
+
+ switch (voltage) {
+ case VDIG_1_2V:
+ mV = 1200;
+ break;
+ case VDIG_1_3V:
+ mV = 1300;
+ break;
+ case VDIG_1_5V:
+ mV = 1500;
+ break;
+ case VDIG_1_8V:
+ mV = 1800;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vdig_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VDIG_EN, VDIG_EN_ENABLE);
+ register_mask = BITFMASK(VDIG_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vdig_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VDIG_EN, VDIG_EN_DISABLE);
+ register_mask = BITFMASK(VDIG_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vgen_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+ int vgenid = rdev_get_id(reg);
+
+ printk(KERN_INFO "VGEN ID is %d\n", vgenid);
+
+ if ((mV >= 1100) && (mV < 1200))
+ voltage = VGEN_1_1V;
+ else if ((mV >= 1200) && (mV < 1300))
+ voltage = VGEN_1_2V;
+ else if ((mV >= 1300) && (mV < 1500))
+ voltage = VGEN_1_3V;
+ else if ((mV >= 1500) && (mV < 1800))
+ voltage = VGEN_1_5V;
+ else if ((mV >= 1800) && (mV < 2000))
+ voltage = VGEN_1_8V;
+ else if ((mV >= 2000) && (mV < 2400))
+ voltage = VGEN_2V;
+ else if ((mV >= 2400) && (mV < 2775))
+ voltage = VGEN_2_4V;
+ else
+ voltage = VGEN_2_775V;
+
+ register_val = BITFVAL(VGEN, voltage);
+ register_mask = BITFMASK(VGEN);
+ register1 = REG_REGULATOR_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vgen_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VGEN);
+
+ switch (voltage) {
+ case VGEN_1_2V:
+ mV = 1200;
+ break;
+ case VGEN_1_3V:
+ mV = 1300;
+ break;
+ case VGEN_1_5V:
+ mV = 1500;
+ break;
+ case VGEN_1_8V:
+ mV = 1800;
+ break;
+ case VGEN_1_1V:
+ mV = 1100;
+ break;
+ case VGEN_2V:
+ mV = 2000;
+ break;
+ case VGEN_2_775V:
+ mV = 2775;
+ break;
+ case VGEN_2_4V:
+ mV = 2400;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vgen_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VGEN_EN, VGEN_EN_ENABLE);
+ register_mask = BITFMASK(VGEN_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vgen_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VGEN_EN, VGEN_EN_DISABLE);
+ register_mask = BITFMASK(VGEN_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrfdig_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1200) && (mV < 1500))
+ voltage = VRFDIG_1_2V;
+ else if ((mV >= 1500) && (mV < 1300))
+ voltage = VRFDIG_1_5V;
+ else if ((mV >= 1800) && (mV < 1875))
+ voltage = VRFDIG_1_8V;
+ else
+ voltage = VRFDIG_1_875V;
+
+ register_val = BITFVAL(VRFDIG, voltage);
+ register_mask = BITFMASK(VRFDIG);
+ register1 = REG_REGULATOR_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrfdig_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VRFDIG);
+
+ switch (voltage) {
+ case VRFDIG_1_2V:
+ mV = 1200;
+ break;
+ case VRFDIG_1_5V:
+ mV = 1500;
+ break;
+ case VRFDIG_1_8V:
+ mV = 1800;
+ break;
+ case VRFDIG_1_875V:
+ mV = 1875;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vrfdig_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VRFDIG_EN, VRFDIG_EN_ENABLE);
+ register_mask = BITFMASK(VRFDIG_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrfdig_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VRFDIG_EN, VRFDIG_EN_DISABLE);
+ register_mask = BITFMASK(VRFDIG_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrfref_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 2475) && (mV < 2600))
+ voltage = VRFREF_2_475V;
+ else if ((mV >= 2600) && (mV < 2700))
+ voltage = VRFREF_2_6V;
+ else if ((mV >= 2700) && (mV < 2775))
+ voltage = VRFREF_2_7V;
+ else
+ voltage = VRFREF_2_775V;
+
+ register_val = BITFVAL(VRFREF, voltage);
+ register_mask = BITFMASK(VRFREF);
+ register1 = REG_REGULATOR_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrfref_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VRFREF);
+
+ switch (voltage) {
+ case VRFREF_2_475V:
+ mV = 2475;
+ break;
+ case VRFREF_2_6V:
+ mV = 2600;
+ break;
+ case VRFREF_2_7V:
+ mV = 2700;
+ break;
+ case VRFREF_2_775V:
+ mV = 2775;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vrfref_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VRFREF_EN, VRFREF_EN_ENABLE);
+ register_mask = BITFMASK(VRFREF_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrfref_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VRFREF_EN, VRFREF_EN_DISABLE);
+ register_mask = BITFMASK(VRFREF_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrfcp_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 2700) && (mV < 2775))
+ voltage = VRFCP_2_7V;
+ else
+ voltage = VRFCP_2_775V;
+
+ register_val = BITFVAL(VRFCP, voltage);
+ register_mask = BITFMASK(VRFCP);
+ register1 = REG_REGULATOR_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrfcp_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VRFCP);
+
+ switch (voltage) {
+ case VRFCP_2_7V:
+ mV = 2700;
+ break;
+ case VRFCP_2_775V:
+ mV = 2775;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vrfcp_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VRFCP_EN, VRFCP_EN_ENABLE);
+ register_mask = BITFMASK(VRFCP_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrfcp_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VRFCP_EN, VRFCP_EN_DISABLE);
+ register_mask = BITFMASK(VRFCP_EN);
+ register1 = REG_REGULATOR_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vsim_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1800) && (mV < 2900))
+ voltage = VSIM_1_8V;
+ else
+ voltage = VSIM_2_9V;
+
+ register_val = BITFVAL(VSIM, voltage);
+ register_mask = BITFMASK(VSIM);
+ register1 = REG_REGULATOR_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vsim_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VSIM);
+
+ switch (voltage) {
+ case VSIM_1_8V:
+ mV = 1800;
+ break;
+ case VSIM_2_9V:
+ mV = 1900;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vsim_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VSIM_EN, VSIM_EN_ENABLE);
+ register_mask = BITFMASK(VSIM_EN);
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vsim_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VSIM_EN, VSIM_EN_DISABLE);
+ register_mask = BITFMASK(VSIM_EN);
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vesim_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1800) && (mV < 2900))
+ voltage = VESIM_1_8V;
+ else
+ voltage = VESIM_2_9V;
+
+ register_val = BITFVAL(VESIM, voltage);
+ register_mask = BITFMASK(VESIM);
+ register1 = REG_REGULATOR_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vesim_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VESIM);
+
+ switch (voltage) {
+ case VESIM_1_8V:
+ mV = 1800;
+ break;
+ case VESIM_2_9V:
+ mV = 1900;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vesim_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VESIM_EN, VESIM_EN_ENABLE);
+ register_mask = BITFMASK(VESIM_EN);
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vesim_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VESIM_EN, VESIM_EN_DISABLE);
+ register_mask = BITFMASK(VESIM_EN);
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vcam_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1500) && (mV < 1800))
+ voltage = VCAM_1_5V;
+ else if ((mV >= 1800) && (mV < 2500))
+ voltage = VCAM_1_8V;
+ else if ((mV >= 2500) && (mV < 2550))
+ voltage = VCAM_2_5V;
+ else if ((mV >= 2550) && (mV < 2600))
+ voltage = VCAM_2_55V;
+ if ((mV >= 2600) && (mV < 2750))
+ voltage = VCAM_2_6V;
+ else if ((mV >= 2750) && (mV < 2800))
+ voltage = VCAM_2_75V;
+ else if ((mV >= 2800) && (mV < 3000))
+ voltage = VCAM_2_8V;
+ else
+ voltage = VCAM_3V;
+
+ register_val = BITFVAL(VCAM, voltage);
+ register_mask = BITFMASK(VCAM);
+ register1 = REG_REGULATOR_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vcam_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VCAM);
+
+ switch (voltage) {
+ case VCAM_1_5V:
+ mV = 1500;
+ break;
+ case VCAM_1_8V:
+ mV = 1800;
+ break;
+ case VCAM_2_5V:
+ mV = 2500;
+ break;
+ case VCAM_2_55V:
+ mV = 2550;
+ break;
+ case VCAM_2_6V:
+ mV = 2600;
+ break;
+ case VCAM_2_75V:
+ mV = 2750;
+ break;
+ case VCAM_2_8V:
+ mV = 2800;
+ break;
+ case VCAM_3V:
+ mV = 3000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vcam_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VCAM_EN, VCAM_EN_ENABLE);
+ register_mask = BITFMASK(VCAM_EN);
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vcam_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VCAM_EN, VCAM_EN_DISABLE);
+ register_mask = BITFMASK(VCAM_EN);
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vvib_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1300) && (mV < 1800))
+ voltage = VVIB_1_3V;
+ else if ((mV >= 1800) && (mV < 2000))
+ voltage = VVIB_1_8V;
+ else if ((mV >= 2000) && (mV < 3000))
+ voltage = VVIB_2V;
+ else
+ voltage = VVIB_3V;
+
+ register_val = BITFVAL(VVIB, voltage);
+ register_mask = BITFMASK(VVIB);
+ register1 = REG_REGULATOR_SETTING_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vvib_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_1,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VVIB);
+
+ switch (voltage) {
+ case VVIB_1_3V:
+ mV = 1300;
+ break;
+ case VVIB_1_8V:
+ mV = 1800;
+ break;
+ case VVIB_2V:
+ mV = 2000;
+ break;
+ case VVIB_3V:
+ mV = 3000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vvib_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VVIB_EN, VVIB_EN_ENABLE);
+ register_mask = BITFMASK(VVIB_EN);
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vvib_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VVIB_EN, VVIB_EN_DISABLE);
+ register_mask = BITFMASK(VVIB_EN);
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrf_set_voltage(struct regulator_dev *reg, int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, rf = rdev_get_id(reg), mV = uV / 1000;
+
+ if ((mV >= 1500) && (mV < 1875))
+ voltage = VRF_1_5V;
+ else if ((mV >= 1875) && (mV < 2700))
+ voltage = VRF_1_875V;
+ else if ((mV >= 2700) && (mV < 2775))
+ voltage = VRF_2_7V;
+ else
+ voltage = VRF_2_775V;
+
+ switch (rf) {
+ case MC13783_VRF1:
+ register_val = BITFVAL(VRF1, voltage);
+ register_mask = BITFMASK(VRF1);
+ break;
+ case MC13783_VRF2:
+ register_val = BITFVAL(VRF2, voltage);
+ register_mask = BITFMASK(VRF2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ register1 = REG_REGULATOR_SETTING_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrf_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, rf = rdev_get_id(reg), mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_1,
+ &register_val, PMIC_ALL_BITS));
+
+ switch (rf) {
+ case MC13783_VRF1:
+ voltage = BITFEXT(register_val, VRF1);
+ break;
+ case MC13783_VRF2:
+ voltage = BITFEXT(register_val, VRF2);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ switch (voltage) {
+ case VRF_1_5V:
+ mV = 1500;
+ break;
+ case VRF_1_875V:
+ mV = 1875;
+ break;
+ case VRF_2_7V:
+ mV = 2700;
+ break;
+ case VRF_2_775V:
+ mV = 2775;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vrf_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int vrf = rdev_get_id(reg);
+
+ switch (vrf) {
+ case MC13783_VRF1:
+ register_val = BITFVAL(VRF1_EN, VRF1_EN_ENABLE);
+ register_mask = BITFMASK(VRF1_EN);
+ break;
+ case MC13783_VRF2:
+ register_val = BITFVAL(VRF2_EN, VRF2_EN_ENABLE);
+ register_mask = BITFMASK(VRF2_EN);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vrf_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int vrf = rdev_get_id(reg);
+
+ switch (vrf) {
+ case MC13783_VRF1:
+ register_val = BITFVAL(VRF1_EN, VRF1_EN_DISABLE);
+ register_mask = BITFMASK(VRF1_EN);
+ break;
+ case MC13783_VRF2:
+ register_val = BITFVAL(VRF2_EN, VRF2_EN_DISABLE);
+ register_mask = BITFMASK(VRF2_EN);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vmmc_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mmc = rdev_get_id(reg), mV = uV / 1000;
+
+ printk(KERN_INFO "VMMC ID is %d\n", mmc);
+
+ if ((mV >= 1600) && (mV < 1800))
+ voltage = VMMC_1_6V;
+ else if ((mV >= 1800) && (mV < 2000))
+ voltage = VMMC_1_8V;
+ else if ((mV >= 2000) && (mV < 2600))
+ voltage = VMMC_2V;
+ else if ((mV >= 2600) && (mV < 2700))
+ voltage = VMMC_2_6V;
+ else if ((mV >= 2700) && (mV < 2800))
+ voltage = VMMC_2_7V;
+ else if ((mV >= 2800) && (mV < 2900))
+ voltage = VMMC_2_8V;
+ else if ((mV >= 2900) && (mV < 3000))
+ voltage = VMMC_2_9V;
+ else
+ voltage = VMMC_3V;
+
+ switch (mmc) {
+ case MC13783_VMMC1:
+ register_val = BITFVAL(VMMC1, voltage);
+ register_mask = BITFMASK(VMMC1);
+ break;
+ case MC13783_VMMC2:
+ register_val = BITFVAL(VMMC2, voltage);
+ register_mask = BITFMASK(VMMC2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ register1 = REG_REGULATOR_SETTING_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vmmc_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mmc = rdev_get_id(reg), mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_REGULATOR_SETTING_1,
+ &register_val, PMIC_ALL_BITS));
+
+ switch (mmc) {
+ case MC13783_VMMC1:
+ voltage = BITFEXT(register_val, VMMC1);
+ break;
+ case MC13783_VMMC2:
+ voltage = BITFEXT(register_val, VMMC2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (voltage) {
+ case VMMC_1_6V:
+ mV = 1600;
+ break;
+ case VMMC_1_8V:
+ mV = 1800;
+ break;
+ case VMMC_2V:
+ mV = 2000;
+ break;
+ case VMMC_2_6V:
+ mV = 2600;
+ break;
+ case VMMC_2_7V:
+ mV = 2700;
+ break;
+ case VMMC_2_8V:
+ mV = 2800;
+ break;
+ case VMMC_2_9V:
+ mV = 2900;
+ break;
+ case VMMC_3V:
+ mV = 3000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_vmmc_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int vmmc = rdev_get_id(reg);
+
+ switch (vmmc) {
+ case MC13783_VMMC1:
+ register_val = BITFVAL(VMMC1_EN, VMMC1_EN_ENABLE);
+ register_mask = BITFMASK(VMMC1_EN);
+ break;
+ case MC13783_VMMC2:
+ register_val = BITFVAL(VMMC2_EN, VMMC2_EN_ENABLE);
+ register_mask = BITFMASK(VMMC2_EN);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_vmmc_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int vmmc = rdev_get_id(reg);
+
+ switch (vmmc) {
+ case MC13783_VMMC1:
+ register_val = BITFVAL(VMMC1_EN, VMMC1_EN_DISABLE);
+ register_mask = BITFMASK(VMMC1_EN);
+ break;
+ case MC13783_VMMC2:
+ register_val = BITFVAL(VMMC2_EN, VMMC2_EN_DISABLE);
+ register_mask = BITFMASK(VMMC2_EN);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ register1 = REG_REGULATOR_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_gpo_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int gpo = rdev_get_id(reg);
+
+ switch (gpo) {
+ case MC13783_GPO1:
+ register_val = BITFVAL(GPO1_EN, GPO1_EN_ENABLE);
+ register_mask = BITFMASK(GPO1_EN);
+ break;
+ case MC13783_GPO2:
+ register_val = BITFVAL(GPO2_EN, GPO2_EN_ENABLE);
+ register_mask = BITFMASK(GPO2_EN);
+ break;
+ case MC13783_GPO3:
+ register_val = BITFVAL(GPO3_EN, GPO3_EN_ENABLE);
+ register_mask = BITFMASK(GPO3_EN);
+ break;
+ case MC13783_GPO4:
+ register_val = BITFVAL(GPO4_EN, GPO4_EN_ENABLE);
+ register_mask = BITFMASK(GPO4_EN);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ register1 = REG_POWER_MISCELLANEOUS;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_gpo_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int gpo = rdev_get_id(reg);
+
+ switch (gpo) {
+ case MC13783_GPO1:
+ register_val = BITFVAL(GPO1_EN, GPO1_EN_DISABLE);
+ register_mask = BITFMASK(GPO1_EN);
+ break;
+ case MC13783_GPO2:
+ register_val = BITFVAL(GPO2_EN, GPO2_EN_DISABLE);
+ register_mask = BITFMASK(GPO2_EN);
+ break;
+ case MC13783_GPO3:
+ register_val = BITFVAL(GPO3_EN, GPO3_EN_DISABLE);
+ register_mask = BITFMASK(GPO3_EN);
+ break;
+ case MC13783_GPO4:
+ register_val = BITFVAL(GPO4_EN, GPO4_EN_DISABLE);
+ register_mask = BITFMASK(GPO4_EN);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ register1 = REG_POWER_MISCELLANEOUS;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_sw3_set_voltage(struct regulator_dev *reg, int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0, register1 = 0;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 5000) && (mV < 5500))
+ voltage = SW3_5V;
+ else
+ voltage = SW3_5_5V;
+
+ register_val = BITFVAL(SW3, voltage);
+ register_mask = BITFMASK(SW3);
+ register1 = REG_SWITCHERS_5;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_sw3_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_5,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW3);
+
+ if (voltage == SW3_5_5V)
+ mV = 5500;
+ else
+ mV = 5000;
+
+ return mV * 1000;
+}
+
+static int mc13783_sw3_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(SW3_EN, SW3_EN_ENABLE);
+ register_mask = BITFMASK(SW3_EN);
+ register1 = REG_SWITCHERS_5;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_sw3_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(SW3_EN, SW3_EN_DISABLE);
+ register_mask = BITFMASK(SW3_EN);
+ register1 = REG_SWITCHERS_5;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_sw_set_normal_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1 = 0;
+ int voltage, sw = rdev_get_id(reg), mV = uV / 1000;
+
+ if ((mV >= 900) && (mV < 925))
+ voltage = SW_0_9V;
+ else if ((mV >= 925) && (mV < 950))
+ voltage = SW_0_925V;
+ else if ((mV >= 950) && (mV < 975))
+ voltage = SW_0_95V;
+ else if ((mV >= 975) && (mV < 1000))
+ voltage = SW_0_975V;
+ else if ((mV >= 1000) && (mV < 1025))
+ voltage = SW_1V;
+ else if ((mV >= 1025) && (mV < 1050))
+ voltage = SW_1_025V;
+ else if ((mV >= 1050) && (mV < 1075))
+ voltage = SW_1_05V;
+ else if ((mV >= 1075) && (mV < 1100))
+ voltage = SW_1_075V;
+ else if ((mV >= 1100) && (mV < 1125))
+ voltage = SW_1_1V;
+ else if ((mV >= 1125) && (mV < 1150))
+ voltage = SW_1_125V;
+ else if ((mV >= 1150) && (mV < 1175))
+ voltage = SW_1_15V;
+ else if ((mV >= 1175) && (mV < 1200))
+ voltage = SW_1_175V;
+ else if ((mV >= 1200) && (mV < 1225))
+ voltage = SW_1_2V;
+ else if ((mV >= 1225) && (mV < 1250))
+ voltage = SW_1_225V;
+ else if ((mV >= 1250) && (mV < 1275))
+ voltage = SW_1_25V;
+ else if ((mV >= 1275) && (mV < 1300))
+ voltage = SW_1_275V;
+ else if ((mV >= 1300) && (mV < 1325))
+ voltage = SW_1_3V;
+ else if ((mV >= 1325) && (mV < 1350))
+ voltage = SW_1_325V;
+ else if ((mV >= 1350) && (mV < 1375))
+ voltage = SW_1_35V;
+ else if ((mV >= 1375) && (mV < 1400))
+ voltage = SW_1_375V;
+ else if ((mV >= 1400) && (mV < 1425))
+ voltage = SW_1_4V;
+ else if ((mV >= 1425) && (mV < 1450))
+ voltage = SW_1_425V;
+ else if ((mV >= 1450) && (mV < 1475))
+ voltage = SW_1_45V;
+ else if ((mV >= 1475) && (mV < 1500))
+ voltage = SW_1_475V;
+ else if ((mV >= 1500) && (mV < 1525))
+ voltage = SW_1_5V;
+ else if ((mV >= 1525) && (mV < 1550))
+ voltage = SW_1_525V;
+ else if ((mV >= 1550) && (mV < 1575))
+ voltage = SW_1_55V;
+ else if ((mV >= 1575) && (mV < 1600))
+ voltage = SW_1_575V;
+ else if ((mV >= 1600) && (mV < 1625))
+ voltage = SW_1_6V;
+ else if ((mV >= 1625) && (mV < 1650))
+ voltage = SW_1_625V;
+ else if ((mV >= 1650) && (mV < 1675))
+ voltage = SW_1_65V;
+ else if ((mV >= 1675) && (mV < 1700))
+ voltage = SW_1_675V;
+ else if ((mV >= 1700) && (mV < 1800))
+ voltage = SW_1_7V;
+ else if ((mV >= 1800) && (mV < 1850))
+ voltage = SW_1_8V;
+ else if ((mV >= 1850) && (mV < 2000))
+ voltage = SW_1_85V;
+ else if ((mV >= 2000) && (mV < 2100))
+ voltage = SW_2V;
+ else if ((mV >= 2100) && (mV < 2200))
+ voltage = SW_2_1V;
+ else
+ voltage = SW_2_2V;
+
+ switch (sw) {
+ case MC13783_SW1A:
+ register1 = REG_SWITCHERS_0;
+ register_val = BITFVAL(SW1A, voltage);
+ register_mask = BITFMASK(SW1A);
+ break;
+ case MC13783_SW1B:
+ register1 = REG_SWITCHERS_1;
+ register_val = BITFVAL(SW1B, voltage);
+ register_mask = BITFMASK(SW1B);
+ break;
+ case MC13783_SW2A:
+ register1 = REG_SWITCHERS_2;
+ register_val = BITFVAL(SW2A, voltage);
+ register_mask = BITFMASK(SW2A);
+ break;
+ case MC13783_SW2B:
+ register1 = REG_SWITCHERS_3;
+ register_val = BITFVAL(SW2B, voltage);
+ register_mask = BITFMASK(SW2B);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_sw_get_normal_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0, sw = rdev_get_id(reg);
+
+ switch (sw) {
+ case MC13783_SW1A:
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW1A);
+ break;
+ case MC13783_SW1B:
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_1,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW1B);
+ break;
+ case MC13783_SW2A:
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_2,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW2A);
+ break;
+ case MC13783_SW2B:
+ CHECK_ERROR(pmic_read_reg(REG_SWITCHERS_3,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW2B);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (voltage) {
+ case SW_0_9V:
+ mV = 900;
+ break;
+ case SW_0_925V:
+ mV = 925;
+ break;
+ case SW_0_95V:
+ mV = 950;
+ break;
+ case SW_0_975V:
+ mV = 975;
+ break;
+ case SW_1V:
+ mV = 1000;
+ break;
+ case SW_1_025V:
+ mV = 1025;
+ break;
+ case SW_1_05V:
+ mV = 1050;
+ break;
+ case SW_1_075V:
+ mV = 1075;
+ break;
+ case SW_1_1V:
+ mV = 1100;
+ break;
+ case SW_1_125V:
+ mV = 1125;
+ break;
+ case SW_1_15V:
+ mV = 1150;
+ break;
+ case SW_1_175V:
+ mV = 1175;
+ break;
+ case SW_1_2V:
+ mV = 1200;
+ break;
+ case SW_1_225V:
+ mV = 1225;
+ break;
+ case SW_1_25V:
+ mV = 1250;
+ break;
+ case SW_1_275V:
+ mV = 1275;
+ break;
+ case SW_1_3V:
+ mV = 1300;
+ break;
+ case SW_1_325V:
+ mV = 1325;
+ break;
+ case SW_1_35V:
+ mV = 1350;
+ break;
+ case SW_1_375V:
+ mV = 1375;
+ break;
+ case SW_1_4V:
+ mV = 1400;
+ break;
+ case SW_1_425V:
+ mV = 1425;
+ break;
+ case SW_1_45V:
+ mV = 1450;
+ break;
+ case SW_1_475V:
+ mV = 1475;
+ break;
+ case SW_1_5V:
+ mV = 1500;
+ break;
+ case SW_1_525V:
+ mV = 1525;
+ break;
+ case SW_1_55V:
+ mV = 1550;
+ break;
+ case SW_1_575V:
+ mV = 1575;
+ break;
+ case SW_1_6V:
+ mV = 1600;
+ break;
+ case SW_1_625V:
+ mV = 1625;
+ break;
+ case SW_1_65V:
+ mV = 1650;
+ break;
+ case SW_1_675V:
+ mV = 1675;
+ break;
+ case SW_1_7V:
+ mV = 1700;
+ break;
+ case SW_1_8V:
+ mV = 1800;
+ break;
+ case SW_1_85V:
+ mV = 1850;
+ break;
+ case SW_2V:
+ mV = 2000;
+ break;
+ case SW_2_1V:
+ mV = 2100;
+ break;
+ case SW_2_2V:
+ mV = 2200;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13783_sw_normal_enable(struct regulator_dev *reg)
+{
+ return 0;
+}
+
+static int mc13783_sw_normal_disable(struct regulator_dev *reg)
+{
+ return 0;
+}
+
+static int mc13783_sw_stby_enable(struct regulator_dev *reg)
+{
+ return 0;
+}
+
+static int mc13783_sw_stby_disable(struct regulator_dev *reg)
+{
+ return 0;
+}
+
+static int mc13783_sw_set_stby_voltage(struct regulator_dev *reg, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1 = 0;
+ int voltage, sw = rdev_get_id(reg), mV = uV / 1000;
+
+ if ((mV >= 900) && (mV < 925))
+ voltage = SW_0_9V;
+ else if ((mV >= 925) && (mV < 950))
+ voltage = SW_0_925V;
+ else if ((mV >= 950) && (mV < 975))
+ voltage = SW_0_95V;
+ else if ((mV >= 975) && (mV < 1000))
+ voltage = SW_0_975V;
+ else if ((mV >= 1000) && (mV < 1025))
+ voltage = SW_1V;
+ else if ((mV >= 1025) && (mV < 1050))
+ voltage = SW_1_025V;
+ else if ((mV >= 1050) && (mV < 1075))
+ voltage = SW_1_05V;
+ else if ((mV >= 1075) && (mV < 1100))
+ voltage = SW_1_075V;
+ else if ((mV >= 1100) && (mV < 1125))
+ voltage = SW_1_1V;
+ else if ((mV >= 1125) && (mV < 1150))
+ voltage = SW_1_125V;
+ else if ((mV >= 1150) && (mV < 1175))
+ voltage = SW_1_15V;
+ else if ((mV >= 1175) && (mV < 1200))
+ voltage = SW_1_175V;
+ else if ((mV >= 1200) && (mV < 1225))
+ voltage = SW_1_2V;
+ else if ((mV >= 1225) && (mV < 1250))
+ voltage = SW_1_225V;
+ else if ((mV >= 1250) && (mV < 1275))
+ voltage = SW_1_25V;
+ else if ((mV >= 1275) && (mV < 1300))
+ voltage = SW_1_275V;
+ else if ((mV >= 1300) && (mV < 1325))
+ voltage = SW_1_3V;
+ else if ((mV >= 1325) && (mV < 1350))
+ voltage = SW_1_325V;
+ else if ((mV >= 1350) && (mV < 1375))
+ voltage = SW_1_35V;
+ else if ((mV >= 1375) && (mV < 1400))
+ voltage = SW_1_375V;
+ else if ((mV >= 1400) && (mV < 1425))
+ voltage = SW_1_4V;
+ else if ((mV >= 1425) && (mV < 1450))
+ voltage = SW_1_425V;
+ else if ((mV >= 1450) && (mV < 1475))
+ voltage = SW_1_45V;
+ else if ((mV >= 1475) && (mV < 1500))
+ voltage = SW_1_475V;
+ else if ((mV >= 1500) && (mV < 1525))
+ voltage = SW_1_5V;
+ else if ((mV >= 1525) && (mV < 1550))
+ voltage = SW_1_525V;
+ else if ((mV >= 1550) && (mV < 1575))
+ voltage = SW_1_55V;
+ else if ((mV >= 1575) && (mV < 1600))
+ voltage = SW_1_575V;
+ else if ((mV >= 1600) && (mV < 1625))
+ voltage = SW_1_6V;
+ else if ((mV >= 1625) && (mV < 1650))
+ voltage = SW_1_625V;
+ else if ((mV >= 1650) && (mV < 1675))
+ voltage = SW_1_65V;
+ else if ((mV >= 1675) && (mV < 1700))
+ voltage = SW_1_675V;
+ else if ((mV >= 1700) && (mV < 1800))
+ voltage = SW_1_7V;
+ else if ((mV >= 1800) && (mV < 1850))
+ voltage = SW_1_8V;
+ else if ((mV >= 1850) && (mV < 2000))
+ voltage = SW_1_85V;
+ else if ((mV >= 2000) && (mV < 2100))
+ voltage = SW_2V;
+ else if ((mV >= 2100) && (mV < 2200))
+ voltage = SW_2_1V;
+ else
+ voltage = SW_2_2V;
+
+ switch (sw) {
+ case MC13783_SW1A:
+ register1 = REG_SWITCHERS_0;
+ register_val = BITFVAL(SW1A_STDBY, voltage);
+ register_mask = BITFMASK(SW1A_STDBY);
+ break;
+ case MC13783_SW1B:
+ register1 = REG_SWITCHERS_1;
+ register_val = BITFVAL(SW1B_STDBY, voltage);
+ register_mask = BITFMASK(SW1B_STDBY);
+ break;
+ case MC13783_SW2A:
+ register1 = REG_SWITCHERS_2;
+ register_val = BITFVAL(SW2A_STDBY, voltage);
+ register_mask = BITFMASK(SW2A_STDBY);
+ break;
+ case MC13783_SW2B:
+ register1 = REG_SWITCHERS_3;
+ register_val = BITFVAL(SW2B_STDBY, voltage);
+ register_mask = BITFMASK(SW2B_STDBY);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13783_sw_set_normal_mode(struct regulator_dev *reg,
+ unsigned int mode)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int register1 = 0;
+ unsigned int l_mode;
+ int sw = rdev_get_id(reg);
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ /* SYNC RECT mode */
+ l_mode = SW_MODE_SYNC_RECT_EN;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ /* PULSE SKIP mode */
+ l_mode = SW_MODE_PULSE_SKIP_EN;
+ break;
+ case REGULATOR_MODE_IDLE:
+ /* LOW POWER mode */
+ l_mode = SW_MODE_LOW_POWER_EN;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ /* NO PULSE SKIP mode */
+ l_mode = SW_MODE_PULSE_NO_SKIP_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (sw) {
+ case MC13783_SW1A:
+ reg_val = BITFVAL(SW1A_MODE, l_mode);
+ reg_mask = BITFMASK(SW1A_MODE);
+ register1 = REG_SWITCHERS_4;
+ break;
+ case MC13783_SW1B:
+ reg_val = BITFVAL(SW1B_MODE, l_mode);
+ reg_mask = BITFMASK(SW1B_MODE);
+ register1 = REG_SWITCHERS_4;
+ break;
+ case MC13783_SW2A:
+ reg_val = BITFVAL(SW2A_MODE, l_mode);
+ reg_mask = BITFMASK(SW2A_MODE);
+ register1 = REG_SWITCHERS_5;
+ break;
+ case MC13783_SW2B:
+ reg_val = BITFVAL(SW2B_MODE, l_mode);
+ reg_mask = BITFMASK(SW2B_MODE);
+ register1 = REG_SWITCHERS_5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return pmic_write_reg(register1, reg_val, reg_mask);
+}
+
+static unsigned int mc13783_sw_get_normal_mode(struct regulator_dev *reg)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int register1 = 0;
+ unsigned int l_mode = 0;
+ int sw = rdev_get_id(reg);
+ int ret = 0;
+
+ switch (sw) {
+ case MC13783_SW1A:
+ reg_mask = BITFMASK(SW1A_MODE);
+ register1 = REG_SWITCHERS_4;
+ break;
+ case MC13783_SW1B:
+ reg_mask = BITFMASK(SW1B_MODE);
+ register1 = REG_SWITCHERS_4;
+ break;
+ case MC13783_SW2A:
+ reg_mask = BITFMASK(SW2A_MODE);
+ register1 = REG_SWITCHERS_5;
+ break;
+ case MC13783_SW2B:
+ reg_mask = BITFMASK(SW2B_MODE);
+ register1 = REG_SWITCHERS_5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = pmic_read_reg(register1, &reg_val, reg_mask);
+ if (ret != 0)
+ return ret;
+
+ switch (sw) {
+ case MC13783_SW1A:
+ l_mode = BITFEXT(reg_val, SW1A_MODE);
+ break;
+ case MC13783_SW1B:
+ l_mode = BITFEXT(reg_val, SW1B_MODE);
+ break;
+ case MC13783_SW2A:
+ l_mode = BITFEXT(reg_val, SW2A_MODE);
+ break;
+ case MC13783_SW2B:
+ l_mode = BITFEXT(reg_val, SW2B_MODE);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (l_mode == SW_MODE_SYNC_RECT_EN) {
+ return REGULATOR_MODE_FAST;
+ } else if (l_mode == SW_MODE_PULSE_NO_SKIP_EN) {
+ return REGULATOR_MODE_STANDBY;
+ } else if (l_mode == SW_MODE_PULSE_SKIP_EN) {
+ return REGULATOR_MODE_NORMAL;
+ } else if (l_mode == SW_MODE_LOW_POWER_EN) {
+ return REGULATOR_MODE_IDLE;
+ } else {
+ return -EINVAL;
+ }
+}
+
+static int mc13783_sw_set_stby_mode(struct regulator_dev *reg,
+ unsigned int mode)
+{
+ unsigned int reg_val = 0, reg_mask = 0;
+ unsigned int register1 = 0;
+ unsigned int l_mode;
+ int sw = rdev_get_id(reg);
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ /* SYNC RECT mode */
+ l_mode = SW_MODE_SYNC_RECT_EN;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ /* PULSE SKIP mode */
+ l_mode = SW_MODE_PULSE_SKIP_EN;
+ break;
+ case REGULATOR_MODE_IDLE:
+ /* LOW POWER mode */
+ l_mode = SW_MODE_LOW_POWER_EN;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ /* NO PULSE SKIP mode */
+ l_mode = SW_MODE_PULSE_NO_SKIP_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (sw) {
+ case MC13783_SW1A:
+ reg_val = BITFVAL(SW1A_STBY_MODE, l_mode);
+ reg_mask = BITFMASK(SW1A_STBY_MODE);
+ register1 = REG_SWITCHERS_4;
+ break;
+ case MC13783_SW1B:
+ reg_val = BITFVAL(SW1B_STBY_MODE, l_mode);
+ reg_mask = BITFMASK(SW1B_STBY_MODE);
+ register1 = REG_SWITCHERS_4;
+ break;
+ case MC13783_SW2A:
+ reg_val = BITFVAL(SW2A_STBY_MODE, l_mode);
+ reg_mask = BITFMASK(SW2A_STBY_MODE);
+ register1 = REG_SWITCHERS_5;
+ break;
+ case MC13783_SW2B:
+ reg_val = BITFVAL(SW2B_STBY_MODE, l_mode);
+ reg_mask = BITFMASK(SW2B_STBY_MODE);
+ register1 = REG_SWITCHERS_5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return pmic_write_reg(register1, reg_val, reg_mask);
+}
+
+static struct regulator_ops mc13783_vaudio_ops = {
+ .enable = mc13783_vaudio_enable,
+ .disable = mc13783_vaudio_disable,
+};
+
+static struct regulator_ops mc13783_viohi_ops = {
+ .enable = mc13783_viohi_enable,
+ .disable = mc13783_viohi_disable,
+};
+
+static struct regulator_ops mc13783_violo_ops = {
+ .set_voltage = mc13783_violo_set_voltage,
+ .get_voltage = mc13783_violo_get_voltage,
+ .enable = mc13783_violo_enable,
+ .disable = mc13783_violo_disable,
+};
+
+static struct regulator_ops mc13783_vdig_ops = {
+ .set_voltage = mc13783_vdig_set_voltage,
+ .get_voltage = mc13783_vdig_get_voltage,
+ .enable = mc13783_vdig_enable,
+ .disable = mc13783_vdig_disable,
+};
+
+static struct regulator_ops mc13783_vgen_ops = {
+ .set_voltage = mc13783_vgen_set_voltage,
+ .get_voltage = mc13783_vgen_get_voltage,
+ .enable = mc13783_vgen_enable,
+ .disable = mc13783_vgen_disable,
+};
+
+static struct regulator_ops mc13783_vrfdig_ops = {
+ .set_voltage = mc13783_vrfdig_set_voltage,
+ .get_voltage = mc13783_vrfdig_get_voltage,
+ .enable = mc13783_vrfdig_enable,
+ .disable = mc13783_vrfdig_disable,
+};
+
+static struct regulator_ops mc13783_vrfref_ops = {
+ .set_voltage = mc13783_vrfref_set_voltage,
+ .get_voltage = mc13783_vrfref_get_voltage,
+ .enable = mc13783_vrfref_enable,
+ .disable = mc13783_vrfref_disable,
+};
+
+static struct regulator_ops mc13783_vrfcp_ops = {
+ .set_voltage = mc13783_vrfcp_set_voltage,
+ .get_voltage = mc13783_vrfcp_get_voltage,
+ .enable = mc13783_vrfcp_enable,
+ .disable = mc13783_vrfcp_disable,
+};
+
+static struct regulator_ops mc13783_vsim_ops = {
+ .set_voltage = mc13783_vsim_set_voltage,
+ .get_voltage = mc13783_vsim_get_voltage,
+ .enable = mc13783_vsim_enable,
+ .disable = mc13783_vsim_disable,
+};
+
+static struct regulator_ops mc13783_vesim_ops = {
+ .set_voltage = mc13783_vesim_set_voltage,
+ .get_voltage = mc13783_vesim_get_voltage,
+ .enable = mc13783_vesim_enable,
+ .disable = mc13783_vesim_disable,
+};
+
+static struct regulator_ops mc13783_vcam_ops = {
+ .set_voltage = mc13783_vcam_set_voltage,
+ .get_voltage = mc13783_vcam_get_voltage,
+ .enable = mc13783_vcam_enable,
+ .disable = mc13783_vcam_disable,
+};
+
+static struct regulator_ops mc13783_vvib_ops = {
+ .set_voltage = mc13783_vvib_set_voltage,
+ .get_voltage = mc13783_vvib_get_voltage,
+ .enable = mc13783_vvib_enable,
+ .disable = mc13783_vvib_disable,
+};
+
+static struct regulator_ops mc13783_vrf_ops = {
+ .set_voltage = mc13783_vrf_set_voltage,
+ .get_voltage = mc13783_vrf_get_voltage,
+ .enable = mc13783_vrf_enable,
+ .disable = mc13783_vrf_disable,
+};
+
+static struct regulator_ops mc13783_vmmc_ops = {
+ .set_voltage = mc13783_vmmc_set_voltage,
+ .get_voltage = mc13783_vmmc_get_voltage,
+ .enable = mc13783_vmmc_enable,
+ .disable = mc13783_vmmc_disable,
+};
+
+static struct regulator_ops mc13783_gpo_ops = {
+ .enable = mc13783_gpo_enable,
+ .disable = mc13783_gpo_disable,
+};
+
+static struct regulator_ops mc13783_sw3_ops = {
+ .set_voltage = mc13783_sw3_set_voltage,
+ .get_voltage = mc13783_sw3_get_voltage,
+ .enable = mc13783_sw3_enable,
+ .disable = mc13783_sw3_disable,
+};
+
+static struct regulator_ops mc13783_sw1_ops = {
+ .set_voltage = mc13783_sw_set_normal_voltage,
+ .get_voltage = mc13783_sw_get_normal_voltage,
+ .get_mode = mc13783_sw_get_normal_mode,
+ .set_mode = mc13783_sw_set_normal_mode,
+ .set_suspend_voltage = mc13783_sw_set_stby_voltage,
+ .set_suspend_enable = mc13783_sw_stby_enable,
+ .set_suspend_disable = mc13783_sw_stby_disable,
+ .set_suspend_mode = mc13783_sw_set_stby_mode,
+};
+
+static struct regulator_ops mc13783_sw_normal_ops = {
+ .set_voltage = mc13783_sw_set_normal_voltage,
+ .get_voltage = mc13783_sw_get_normal_voltage,
+ .get_mode = mc13783_sw_get_normal_mode,
+ .set_mode = mc13783_sw_set_normal_mode,
+ .enable = mc13783_sw_normal_enable,
+ .disable = mc13783_sw_normal_disable,
+};
+
+static struct regulator_desc reg_mc13783[] = {
+ {
+ .name = "SW1A",
+ .id = MC13783_SW1A,
+ .ops = &mc13783_sw1_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "SW1B",
+ .id = MC13783_SW1B,
+ .ops = &mc13783_sw_normal_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "SW2A",
+ .id = MC13783_SW2A,
+ .ops = &mc13783_sw_normal_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "SW2B",
+ .id = MC13783_SW2B,
+ .ops = &mc13783_sw_normal_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "SW3",
+ .id = MC13783_SW3,
+ .ops = &mc13783_sw3_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VAUDIO",
+ .id = MC13783_VAUDIO,
+ .ops = &mc13783_vaudio_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VIOHI",
+ .id = MC13783_VIOHI,
+ .ops = &mc13783_viohi_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VIOLO",
+ .id = MC13783_VIOLO,
+ .ops = &mc13783_violo_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VDIG",
+ .id = MC13783_VDIG,
+ .ops = &mc13783_vdig_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VGEN",
+ .id = MC13783_VGEN,
+ .ops = &mc13783_vgen_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VRFDIG",
+ .id = MC13783_VRFDIG,
+ .ops = &mc13783_vrfdig_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VRFREF",
+ .id = MC13783_VRFREF,
+ .ops = &mc13783_vrfref_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VRFCP",
+ .id = MC13783_VRFCP,
+ .ops = &mc13783_vrfcp_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VSIM",
+ .id = MC13783_VSIM,
+ .ops = &mc13783_vsim_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VESIM",
+ .id = MC13783_VESIM,
+ .ops = &mc13783_vesim_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VCAM",
+ .id = MC13783_VCAM,
+ .ops = &mc13783_vcam_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VRFBG",
+ .id = MC13783_VRFBG,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VVIB",
+ .id = MC13783_VVIB,
+ .ops = &mc13783_vvib_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VRF1",
+ .id = MC13783_VRF1,
+ .ops = &mc13783_vrf_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VRF2",
+ .id = MC13783_VRF2,
+ .ops = &mc13783_vrf_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VMMC1",
+ .id = MC13783_VMMC1,
+ .ops = &mc13783_vmmc_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VMMC2",
+ .id = MC13783_VMMC2,
+ .ops = &mc13783_vmmc_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "GPO1",
+ .id = MC13783_GPO1,
+ .ops = &mc13783_gpo_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "GPO2",
+ .id = MC13783_GPO2,
+ .ops = &mc13783_gpo_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "GPO3",
+ .id = MC13783_GPO3,
+ .ops = &mc13783_gpo_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "GPO4",
+ .id = MC13783_GPO4,
+ .ops = &mc13783_gpo_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+};
+
+/*
+ * Init and Exit
+ */
+
+static int reg_mc13783_probe(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev;
+
+ /* register regulator */
+ rdev = regulator_register(&reg_mc13783[pdev->id], &pdev->dev,
+ dev_get_drvdata(&pdev->dev));
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n",
+ reg_mc13783[pdev->id].name);
+ return PTR_ERR(rdev);
+ }
+
+ return 0;
+}
+
+static int mc13783_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+
+ return 0;
+}
+
+int mc13783_register_regulator(struct mc13783 *mc13783, int reg,
+ struct regulator_init_data *initdata)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ if (mc13783->pmic.pdev[reg])
+ return -EBUSY;
+
+ pdev = platform_device_alloc("mc13783-regulatr", reg);
+ if (!pdev)
+ return -ENOMEM;
+
+ mc13783->pmic.pdev[reg] = pdev;
+
+ initdata->driver_data = mc13783;
+
+ pdev->dev.platform_data = initdata;
+ pdev->dev.parent = mc13783->dev;
+ platform_set_drvdata(pdev, mc13783);
+ ret = platform_device_add(pdev);
+
+ if (ret != 0) {
+ dev_err(mc13783->dev, "Failed to register regulator %d: %d\n",
+ reg, ret);
+ platform_device_del(pdev);
+ mc13783->pmic.pdev[reg] = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mc13783_register_regulator);
+
+static struct platform_driver mc13783_regulator_driver = {
+ .probe = reg_mc13783_probe,
+ .remove = mc13783_regulator_remove,
+ .driver = {
+ .name = "mc13783-regulatr",
+ /* o left out due to string length */
+ },
+};
+
+static int __init mc13783_regulator_subsys_init(void)
+{
+ return platform_driver_register(&mc13783_regulator_driver);
+}
+subsys_initcall(mc13783_regulator_subsys_init);
+
+static void __exit mc13783_regulator_exit(void)
+{
+ platform_driver_unregister(&mc13783_regulator_driver);
+}
+module_exit(mc13783_regulator_exit);
+
+
+/* Module information */
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MC13783 Regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/reg-mc13892.c b/drivers/regulator/reg-mc13892.c
new file mode 100644
index 000000000000..70b0d529a94d
--- /dev/null
+++ b/drivers/regulator/reg-mc13892.c
@@ -0,0 +1,1849 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/mc13892/core.h>
+#include <linux/platform_device.h>
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+/*
+ * Convenience conversion.
+ * Here atm, maybe there is somewhere better for this.
+ */
+#define mV_to_uV(mV) (mV * 1000)
+#define uV_to_mV(uV) (uV / 1000)
+#define V_to_uV(V) (mV_to_uV(V * 1000))
+#define uV_to_V(uV) (uV_to_mV(uV) / 1000)
+
+enum {
+ VDIG_1_05V = 0,
+ VDIG_1_25V,
+ VDIG_1_65V,
+ VDIG_1_80V,
+} regulator_voltage_vdig;
+
+enum {
+ VPLL_1_05V = 0,
+ VPLL_1_25V,
+ VPLL_1_65V,
+ VPLL_1_80V,
+} regulator_voltage_vpll;
+
+enum {
+ VGEN1_1_2V = 0,
+ VGEN1_1_5V,
+ VGEN1_2_775V,
+ VGEN1_3_15V,
+} regulator_voltage_vgen1;
+
+enum {
+ VGEN2_1_2V = 0,
+ VGEN2_1_5V,
+ VGEN2_1_6V,
+ VGEN2_1_8V,
+ VGEN2_2_7V,
+ VGEN2_2_8V,
+ VGEN2_3_0V,
+ VGEN2_3_15V,
+} regulator_voltage_vgen2;
+
+enum {
+ VGEN3_1_8V = 0,
+ VGEN3_2_9V,
+} regulator_voltage_vgen3;
+
+enum {
+ VSD_1_8V = 0,
+ VSD_2_0V,
+ VSD_2_6V,
+ VSD_2_7V,
+ VSD_2_8V,
+ VSD_2_9V,
+ VSD_3_0V,
+ VSD_3_15V,
+} regulator_voltage_vsd;
+
+enum {
+ VCAM_2_5V,
+ VCAM_2_6V,
+ VCAM_2_75V,
+ VCAM_3_0V,
+} regulator_voltage_vcam;
+
+enum {
+ VAUDIO_2_3V,
+ VAUDIO_2_5V,
+ VAUDIO_2_775V,
+ VAUDIO_3V,
+} regulator_voltage_vaudio;
+
+enum {
+ VUSB2_2_4V,
+ VUSB2_2_6V,
+ VUSB2_2_7V,
+ VUSB2_2_775V,
+} regulator_voltage_vusb2;
+
+enum {
+ VVIDEO_2_7V,
+ VVIDEO_2_775V,
+ VVIDEO_2_5V,
+ VVIDEO_2_6V,
+} regulator_voltage_vvideo;
+
+#define VAUDIO_LSH 4
+#define VAUDIO_WID 2
+#define VAUDIO_EN_LSH 15
+#define VAUDIO_EN_WID 1
+#define VAUDIO_EN_ENABLE 1
+#define VAUDIO_EN_DISABLE 0
+
+#define VUSB2_LSH 11
+#define VUSB2_WID 2
+#define VUSB2_EN_LSH 18
+#define VUSB2_EN_WID 1
+#define VUSB2_EN_ENABLE 1
+#define VUSB2_EN_DISABLE 0
+
+#define VVIDEO_LSH 2
+#define VVIDEO_WID 2
+#define VVIDEO_EN_LSH 12
+#define VVIDEO_EN_WID 1
+#define VVIDEO_EN_ENABLE 1
+#define VVIDEO_EN_DISABLE 0
+
+#define SWBST_EN_LSH 20
+#define SWBST_EN_WID 1
+#define SWBST_EN_ENABLE 1
+#define SWBST_EN_DISABLE 0
+
+#define VIOHI_EN_LSH 3
+#define VIOHI_EN_WID 1
+#define VIOHI_EN_ENABLE 1
+#define VIOHI_EN_DISABLE 0
+
+#define VDIG_LSH 4
+#define VDIG_WID 2
+#define VDIG_EN_LSH 9
+#define VDIG_EN_WID 1
+#define VDIG_EN_ENABLE 1
+#define VDIG_EN_DISABLE 0
+
+#define VPLL_LSH 9
+#define VPLL_WID 2
+#define VPLL_EN_LSH 15
+#define VPLL_EN_WID 1
+#define VPLL_EN_ENABLE 1
+#define VPLL_EN_DISABLE 0
+
+#define VGEN1_LSH 0
+#define VGEN1_WID 2
+#define VGEN1_EN_LSH 0
+#define VGEN1_EN_WID 1
+#define VGEN1_EN_ENABLE 1
+#define VGEN1_EN_DISABLE 0
+
+#define VGEN2_LSH 6
+#define VGEN2_WID 3
+#define VGEN2_EN_LSH 12
+#define VGEN2_EN_WID 1
+#define VGEN2_EN_ENABLE 1
+#define VGEN2_EN_DISABLE 0
+
+#define VGEN3_LSH 14
+#define VGEN3_WID 1
+#define VGEN3_EN_LSH 0
+#define VGEN3_EN_WID 1
+#define VGEN3_EN_ENABLE 1
+#define VGEN3_EN_DISABLE 0
+
+#define VSD_LSH 6
+#define VSD_WID 3
+#define VSD_EN_LSH 18
+#define VSD_EN_WID 1
+#define VSD_EN_ENABLE 1
+#define VSD_EN_DISABLE 0
+
+#define VCAM_LSH 16
+#define VCAM_WID 2
+#define VCAM_EN_LSH 6
+#define VCAM_EN_WID 1
+#define VCAM_EN_ENABLE 1
+#define VCAM_EN_DISABLE 0
+#define VCAM_CONFIG_LSH 9
+#define VCAM_CONFIG_WID 1
+#define VCAM_CONFIG_EXT 1
+#define VCAM_CONFIG_INT 0
+
+#define SW1_LSH 0
+#define SW1_WID 5
+#define SW1_DVS_LSH 5
+#define SW1_DVS_WID 5
+#define SW1_STDBY_LSH 10
+#define SW1_STDBY_WID 5
+
+#define SW2_LSH 0
+#define SW2_WID 5
+#define SW2_DVS_LSH 5
+#define SW2_DVS_WID 5
+#define SW2_STDBY_LSH 10
+#define SW2_STDBY_WID 5
+
+#define SW3_LSH 0
+#define SW3_WID 5
+#define SW3_STDBY_LSH 10
+#define SW3_STDBY_WID 5
+
+#define SW4_LSH 0
+#define SW4_WID 5
+#define SW4_STDBY_LSH 10
+#define SW4_STDBY_WID 5
+
+#define VUSB_EN_LSH 3
+#define VUSB_EN_WID 1
+#define VUSB_EN_ENABLE 1
+#define VUSB_EN_DISABLE 0
+
+#define GPO1_EN_LSH 6
+#define GPO1_EN_WID 1
+#define GPO1_EN_ENABLE 1
+#define GPO1_EN_DISABLE 0
+
+#define GPO2_EN_LSH 8
+#define GPO2_EN_WID 1
+#define GPO2_EN_ENABLE 1
+#define GPO2_EN_DISABLE 0
+
+#define GPO3_EN_LSH 10
+#define GPO3_EN_WID 1
+#define GPO3_EN_ENABLE 1
+#define GPO3_EN_DISABLE 0
+
+#define GPO4_EN_LSH 12
+#define GPO4_EN_WID 1
+#define GPO4_EN_ENABLE 1
+#define GPO4_EN_DISABLE 0
+
+#define GPO4_ADIN_LSH 21
+#define GPO4_ADIN_WID 1
+#define GPO4_ADIN_ENABLE 1
+#define GPO4_ADIN_DISABLE 0
+
+#define PWGT1SPI_EN_LSH 15
+#define PWGT1SPI_EN_WID 1
+#define PWGT1SPI_EN_ENABLE 0
+#define PWGT1SPI_EN_DISABLE 1
+
+#define PWGT2SPI_EN_LSH 16
+#define PWGT2SPI_EN_WID 1
+#define PWGT2SPI_EN_ENABLE 0
+#define PWGT2SPI_EN_DISABLE 1
+
+#define SWXHI_LSH 23
+#define SWXHI_WID 1
+#define SWXHI_ON 1
+#define SWXHI_OFF 0
+
+static int mc13892_get_sw_hi_bit(int sw)
+{
+ unsigned int register_val = 0;
+ unsigned int reg = 0;
+
+ switch (sw) {
+ case MC13892_SW1:
+ reg = REG_SW_0;
+ break;
+ case MC13892_SW2:
+ reg = REG_SW_1;
+ break;
+ case MC13892_SW3:
+ reg = REG_SW_2;
+ break;
+ case MC13892_SW4:
+ reg = REG_SW_3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ CHECK_ERROR(pmic_read_reg(reg, &register_val, PMIC_ALL_BITS));
+ return (register_val & 0x800000) >> SWXHI_LSH;
+}
+
+static int mc13892_get_voltage_value(int *hi, int mV)
+{
+ int voltage;
+
+ if (mV < 600)
+ mV = 600;
+ if (mV > 1850)
+ mV = 1850;
+
+ if (mV > 1375)
+ *hi = 1;
+ if (mV < 1100)
+ *hi = 0;
+
+ if (*hi == 0)
+ voltage = (mV - 600) / 25;
+ else
+ voltage = (mV - 1100) / 25;
+
+ return voltage;
+}
+
+static int mc13892_get_voltage_mV(int hi, int voltage)
+{
+ int mV;
+
+ if (hi == 0)
+ mV = voltage * 25 + 600;
+ else
+ mV = voltage * 25 + 1100;
+
+ return mV;
+}
+
+static int mc13892_sw_set_voltage(struct regulator_dev *reg, int MiniV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1 = 0;
+ int voltage;
+ int sw = rdev_get_id(reg);
+ int mV = uV / 1000;
+ int hi;
+
+ hi = mc13892_get_sw_hi_bit(sw);
+ voltage = mc13892_get_voltage_value(&hi, mV);
+
+ switch (sw) {
+ case MC13892_SW1:
+ register1 = REG_SW_0;
+ register_val = BITFVAL(SW1, voltage);
+ register_mask = BITFMASK(SW1);
+ break;
+ case MC13892_SW2:
+ register1 = REG_SW_1;
+ register_val = BITFVAL(SW2, voltage);
+ register_mask = BITFMASK(SW2);
+ break;
+ case MC13892_SW3:
+ register1 = REG_SW_2;
+ register_val = BITFVAL(SW3, voltage);
+ register_mask = BITFMASK(SW3);
+ break;
+ case MC13892_SW4:
+ register1 = REG_SW_3;
+ register_val = BITFVAL(SW4, voltage);
+ register_mask = BITFMASK(SW4);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ register_val |= (hi << SWXHI_LSH);
+ register_mask |= (1 << SWXHI_LSH);
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_sw_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0;
+ int mV = 0;
+ int sw = rdev_get_id(reg);
+ int hi;
+
+ switch (sw) {
+ case MC13892_SW1:
+ CHECK_ERROR(pmic_read_reg(REG_SW_0,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW1);
+ break;
+ case MC13892_SW2:
+ CHECK_ERROR(pmic_read_reg(REG_SW_1,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW2);
+ break;
+ case MC13892_SW3:
+ CHECK_ERROR(pmic_read_reg(REG_SW_2,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW3);
+ break;
+ case MC13892_SW4:
+ CHECK_ERROR(pmic_read_reg(REG_SW_3,
+ &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, SW4);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ hi = mc13892_get_sw_hi_bit(sw);
+ mV = mc13892_get_voltage_mV(hi, voltage);
+
+ return mV * 1000;
+}
+
+static int mc13892_sw_stby_enable(struct regulator_dev *reg)
+{
+ return 0;
+}
+
+static int mc13892_sw_stby_disable(struct regulator_dev *reg)
+{
+ return 0;
+}
+
+static int mc13892_sw_stby_set_mode(struct regulator_dev *reg, unsigned int mode)
+{
+ return 0;
+}
+
+static int mc13892_sw_stby_set_voltage(struct regulator_dev *reg, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1 = 0;
+ int voltage, mV = uV / 1000, hi;
+ int sw = rdev_get_id(reg);
+
+ hi = mc13892_get_sw_hi_bit(sw);
+ voltage = mc13892_get_voltage_value(&hi, mV);
+
+ switch (sw) {
+ case MC13892_SW1:
+ register1 = REG_SW_0;
+ register_val = BITFVAL(SW1_STDBY, voltage);
+ register_mask = BITFMASK(SW1_STDBY);
+ break;
+ case MC13892_SW2:
+ register1 = REG_SW_1;
+ register_val = BITFVAL(SW2_STDBY, voltage);
+ register_mask = BITFMASK(SW2_STDBY);
+ break;
+ case MC13892_SW3:
+ register1 = REG_SW_2;
+ register_val = BITFVAL(SW3_STDBY, voltage);
+ register_mask = BITFMASK(SW3_STDBY);
+ break;
+ case MC13892_SW4:
+ register1 = REG_SW_3;
+ register_val = BITFVAL(SW4_STDBY, voltage);
+ register_mask = BITFMASK(SW4_STDBY);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ register_val |= (hi << SWXHI_LSH);
+ register_mask |= (1 << SWXHI_LSH);
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_swbst_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(SWBST_EN, SWBST_EN_ENABLE);
+ register_mask = BITFMASK(SWBST_EN);
+ register1 = REG_SW_5;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_swbst_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(SWBST_EN, SWBST_EN_DISABLE);
+ register_mask = BITFMASK(SWBST_EN);
+ register1 = REG_SW_5;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_viohi_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VIOHI_EN, VIOHI_EN_ENABLE);
+ register_mask = BITFMASK(VIOHI_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_viohi_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VIOHI_EN, VIOHI_EN_DISABLE);
+ register_mask = BITFMASK(VIOHI_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vusb_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VUSB_EN, VUSB_EN_ENABLE);
+ register_mask = BITFMASK(VUSB_EN);
+ register1 = REG_USB1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vusb_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VUSB_EN, VUSB_EN_DISABLE);
+ register_mask = BITFMASK(VUSB_EN);
+ register1 = REG_USB1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vdig_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1050) && (mV < 1250))
+ voltage = VDIG_1_05V;
+ else if ((mV >= 1250) && (mV < 1650))
+ voltage = VDIG_1_25V;
+ else if ((mV >= 1650) && (mV < 1800))
+ voltage = VDIG_1_65V;
+ else
+ voltage = VDIG_1_80V;
+
+ register_val = BITFVAL(VDIG, voltage);
+ register_mask = BITFMASK(VDIG);
+ register1 = REG_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vdig_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SETTING_0, &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VDIG);
+
+ switch (voltage) {
+ case VDIG_1_05V:
+ mV = 1050;
+ break;
+ case VDIG_1_25V:
+ mV = 1250;
+ break;
+ case VDIG_1_65V:
+ mV = 1650;
+ break;
+ case VDIG_1_80V:
+ mV = 1800;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13892_vdig_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VDIG_EN, VDIG_EN_ENABLE);
+ register_mask = BITFMASK(VDIG_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vdig_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VDIG_EN, VDIG_EN_DISABLE);
+ register_mask = BITFMASK(VDIG_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vpll_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1050) && (mV < 1250))
+ voltage = VPLL_1_05V;
+ else if ((mV >= 1250) && (mV < 1650))
+ voltage = VPLL_1_25V;
+ else if ((mV >= 1650) && (mV < 1800))
+ voltage = VPLL_1_65V;
+ else
+ voltage = VPLL_1_80V;
+
+ register_val = BITFVAL(VPLL, voltage);
+ register_mask = BITFMASK(VPLL);
+ register1 = REG_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vpll_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SETTING_0, &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VPLL);
+
+ switch (voltage) {
+ case VPLL_1_05V:
+ mV = 1050;
+ break;
+ case VPLL_1_25V:
+ mV = 1250;
+ break;
+ case VPLL_1_65V:
+ mV = 1650;
+ break;
+ case VPLL_1_80V:
+ mV = 1800;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13892_vpll_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VPLL_EN, VPLL_EN_ENABLE);
+ register_mask = BITFMASK(VPLL_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vpll_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VPLL_EN, VPLL_EN_DISABLE);
+ register_mask = BITFMASK(VPLL_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vaudio_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 2300) && (mV < 2500))
+ voltage = VAUDIO_2_3V;
+ else if ((mV >= 2500) && (mV < 2775))
+ voltage = VAUDIO_2_5V;
+ else if ((mV >= 2775) && (mV < 3000))
+ voltage = VAUDIO_2_775V;
+ else
+ voltage = VAUDIO_3V;
+
+ register_val = BITFVAL(VAUDIO, voltage);
+ register_mask = BITFMASK(VAUDIO);
+ register1 = REG_SETTING_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vaudio_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SETTING_1, &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VAUDIO);
+
+ switch (voltage) {
+ case VAUDIO_2_3V:
+ mV = 2300;
+ break;
+ case VAUDIO_2_5V:
+ mV = 2500;
+ break;
+ case VAUDIO_2_775V:
+ mV = 2775;
+ break;
+ case VAUDIO_3V:
+ mV = 3000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13892_vaudio_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VAUDIO_EN, VAUDIO_EN_ENABLE);
+ register_mask = BITFMASK(VAUDIO_EN);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vaudio_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VAUDIO_EN, VAUDIO_EN_DISABLE);
+ register_mask = BITFMASK(VAUDIO_EN);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vusb2_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 2400) && (mV < 2600))
+ voltage = VUSB2_2_4V;
+ else if ((mV >= 2600) && (mV < 2700))
+ voltage = VUSB2_2_6V;
+ else if ((mV >= 2700) && (mV < 2775))
+ voltage = VUSB2_2_7V;
+ else
+ voltage = VUSB2_2_775V;
+
+ register_val = BITFVAL(VUSB2, voltage);
+ register_mask = BITFMASK(VUSB2);
+ register1 = REG_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vusb2_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SETTING_0, &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VUSB2);
+
+ switch (voltage) {
+ case VUSB2_2_4V:
+ mV = 2400;
+ break;
+ case VUSB2_2_6V:
+ mV = 2600;
+ break;
+ case VUSB2_2_7V:
+ mV = 2700;
+ break;
+ case VUSB2_2_775V:
+ mV = 2775;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13892_vusb2_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VUSB2_EN, VUSB2_EN_ENABLE);
+ register_mask = BITFMASK(VUSB2_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vusb2_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VUSB2_EN, VUSB2_EN_DISABLE);
+ register_mask = BITFMASK(VUSB2_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vvideo_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 2500) && (mV < 2600))
+ voltage = VVIDEO_2_5V;
+ else if ((mV >= 2600) && (mV < 2700))
+ voltage = VVIDEO_2_6V;
+ else if ((mV >= 2700) && (mV < 2775))
+ voltage = VVIDEO_2_7V;
+ else
+ voltage = VVIDEO_2_775V;
+
+ register_val = BITFVAL(VVIDEO, voltage);
+ register_mask = BITFMASK(VVIDEO);
+ register1 = REG_SETTING_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vvideo_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SETTING_1, &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VVIDEO);
+
+ switch (voltage) {
+ case VVIDEO_2_5V:
+ mV = 2500;
+ break;
+ case VVIDEO_2_6V:
+ mV = 2600;
+ break;
+ case VVIDEO_2_7V:
+ mV = 2700;
+ break;
+ case VVIDEO_2_775V:
+ mV = 2775;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13892_vvideo_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VVIDEO_EN, VVIDEO_EN_ENABLE);
+ register_mask = BITFMASK(VVIDEO_EN);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vvideo_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VVIDEO_EN, VVIDEO_EN_DISABLE);
+ register_mask = BITFMASK(VVIDEO_EN);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vsd_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1800) && (mV < 2000))
+ voltage = VSD_1_8V;
+ else if ((mV >= 2000) && (mV < 2600))
+ voltage = VSD_2_0V;
+ else if ((mV >= 2600) && (mV < 2700))
+ voltage = VSD_2_6V;
+ else if ((mV >= 2700) && (mV < 2800))
+ voltage = VSD_2_7V;
+ else if ((mV >= 2800) && (mV < 2900))
+ voltage = VSD_2_8V;
+ else if ((mV >= 2900) && (mV < 3000))
+ voltage = VSD_2_9V;
+ else if ((mV >= 3000) && (mV < 3150))
+ voltage = VSD_3_0V;
+ else
+ voltage = VSD_3_15V;
+
+ register_val = BITFVAL(VSD, voltage);
+ register_mask = BITFMASK(VSD);
+ register1 = REG_SETTING_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vsd_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SETTING_1, &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VSD);
+
+ switch (voltage) {
+ case VSD_1_8V:
+ mV = 1800;
+ break;
+ case VSD_2_0V:
+ mV = 2000;
+ break;
+ case VSD_2_6V:
+ mV = 2600;
+ break;
+ case VSD_2_7V:
+ mV = 2700;
+ break;
+ case VSD_2_8V:
+ mV = 2800;
+ break;
+ case VSD_2_9V:
+ mV = 2900;
+ break;
+ case VSD_3_0V:
+ mV = 3000;
+ break;
+ case VSD_3_15V:
+ mV = 3150;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13892_vsd_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VSD_EN, VSD_EN_ENABLE);
+ register_mask = BITFMASK(VSD_EN);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vsd_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VSD_EN, VSD_EN_DISABLE);
+ register_mask = BITFMASK(VSD_EN);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vcam_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 2500) && (mV < 2600))
+ voltage = VCAM_2_5V;
+ else if ((mV >= 2600) && (mV < 2750))
+ voltage = VCAM_2_6V;
+ else if ((mV >= 2750) && (mV < 3000))
+ voltage = VCAM_2_75V;
+ else
+ voltage = VCAM_3_0V;
+
+ register_val = BITFVAL(VCAM, voltage);
+ register_mask = BITFMASK(VCAM);
+ register1 = REG_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vcam_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SETTING_0, &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VCAM);
+
+ switch (voltage) {
+ case VCAM_2_5V:
+ mV = 2500;
+ break;
+ case VCAM_2_6V:
+ mV = 2600;
+ break;
+ case VCAM_2_75V:
+ mV = 2750;
+ break;
+ case VCAM_3_0V:
+ mV = 3000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13892_vcam_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VCAM_EN, VCAM_EN_ENABLE);
+ register_mask = BITFMASK(VCAM_EN);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vcam_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VCAM_EN, VCAM_EN_DISABLE);
+ register_mask = BITFMASK(VCAM_EN);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vcam_set_mode(struct regulator_dev *reg, unsigned int mode)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ register_val = BITFVAL(VCAM_CONFIG, VCAM_CONFIG_EXT);
+ break;
+ case REGULATOR_MODE_NORMAL:
+ register_val = BITFVAL(VCAM_CONFIG, VCAM_CONFIG_INT);
+ break;
+ default:
+ return -EINVAL;
+ }
+ register_mask = BITFMASK(VCAM_CONFIG);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+unsigned int mc13892_vcam_get_mode(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int config = 0, mode = VCAM_CONFIG_INT;
+
+ CHECK_ERROR(pmic_read_reg(REG_MODE_1, &register_val, PMIC_ALL_BITS));
+ config = BITFEXT(register_val, VCAM_CONFIG);
+
+ switch (config) {
+ case VCAM_CONFIG_EXT:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ case VCAM_CONFIG_INT:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return mode;
+}
+
+static int mc13892_vgen1_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1200) && (mV < 1500))
+ voltage = VGEN1_1_2V;
+ else if ((mV >= 1500) && (mV < 2775))
+ voltage = VGEN1_1_5V;
+ else if ((mV >= 2775) && (mV < 3150))
+ voltage = VGEN1_2_775V;
+ else
+ voltage = VGEN1_3_15V;
+
+ register_val = BITFVAL(VGEN1, voltage);
+ register_mask = BITFMASK(VGEN1);
+ register1 = REG_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vgen1_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SETTING_0, &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VGEN1);
+
+ switch (voltage) {
+ case VGEN1_1_2V:
+ mV = 1200;
+ break;
+ case VGEN1_1_5V:
+ mV = 1500;
+ break;
+ case VGEN1_2_775V:
+ mV = 2775;
+ break;
+ case VGEN1_3_15V:
+ mV = 3150;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13892_vgen1_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VGEN1_EN, VGEN1_EN_ENABLE);
+ register_mask = BITFMASK(VGEN1_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vgen1_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VGEN1_EN, VGEN1_EN_DISABLE);
+ register_mask = BITFMASK(VGEN1_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vgen2_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1200) && (mV < 1500))
+ voltage = VGEN2_1_2V;
+ else if ((mV >= 1500) && (mV < 1600))
+ voltage = VGEN2_1_5V;
+ else if ((mV >= 1600) && (mV < 1800))
+ voltage = VGEN2_1_6V;
+ else if ((mV >= 1800) && (mV < 2700))
+ voltage = VGEN2_1_8V;
+ else if ((mV >= 2700) && (mV < 2800))
+ voltage = VGEN2_2_7V;
+ else if ((mV >= 2800) && (mV < 3000))
+ voltage = VGEN2_2_8V;
+ else if ((mV >= 3000) && (mV < 3150))
+ voltage = VGEN2_3_0V;
+ else
+ voltage = VGEN2_3_15V;
+
+ register_val = BITFVAL(VGEN2, voltage);
+ register_mask = BITFMASK(VGEN2);
+ register1 = REG_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vgen2_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SETTING_0, &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VGEN2);
+
+ switch (voltage) {
+ case VGEN2_1_2V:
+ mV = 1200;
+ break;
+ case VGEN2_1_5V:
+ mV = 1500;
+ break;
+ case VGEN2_1_6V:
+ mV = 1600;
+ break;
+ case VGEN2_1_8V:
+ mV = 1800;
+ break;
+ case VGEN2_2_7V:
+ mV = 2700;
+ break;
+ case VGEN2_2_8V:
+ mV = 2800;
+ break;
+ case VGEN2_3_0V:
+ mV = 3000;
+ break;
+ case VGEN2_3_15V:
+ mV = 3150;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13892_vgen2_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VGEN2_EN, VGEN2_EN_ENABLE);
+ register_mask = BITFMASK(VGEN2_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vgen2_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VGEN2_EN, VGEN2_EN_DISABLE);
+ register_mask = BITFMASK(VGEN2_EN);
+ register1 = REG_MODE_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vgen3_set_voltage(struct regulator_dev *reg,
+ int minuV, int uV)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int voltage, mV = uV / 1000;
+
+ if ((mV >= 1800) && (mV < 2900))
+ voltage = VGEN3_1_8V;
+ else
+ voltage = VGEN3_2_9V;
+
+ register_val = BITFVAL(VGEN3, voltage);
+ register_mask = BITFMASK(VGEN3);
+ register1 = REG_SETTING_0;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vgen3_get_voltage(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0;
+ int voltage = 0, mV = 0;
+
+ CHECK_ERROR(pmic_read_reg(REG_SETTING_0, &register_val, PMIC_ALL_BITS));
+ voltage = BITFEXT(register_val, VGEN3);
+
+ switch (voltage) {
+ case VGEN3_1_8V:
+ mV = 1800;
+ break;
+ case VGEN3_2_9V:
+ mV = 2900;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mV * 1000;
+}
+
+static int mc13892_vgen3_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VGEN3_EN, VGEN3_EN_ENABLE);
+ register_mask = BITFMASK(VGEN3_EN);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_vgen3_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+
+ register_val = BITFVAL(VGEN3_EN, VGEN3_EN_DISABLE);
+ register_mask = BITFMASK(VGEN3_EN);
+ register1 = REG_MODE_1;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_gpo_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int gpo = rdev_get_id(reg);
+
+ switch (gpo) {
+ case MC13892_GPO1:
+ register_val = BITFVAL(GPO1_EN, GPO1_EN_ENABLE);
+ register_mask = BITFMASK(GPO1_EN);
+ break;
+ case MC13892_GPO2:
+ register_val = BITFVAL(GPO2_EN, GPO2_EN_ENABLE);
+ register_mask = BITFMASK(GPO2_EN);
+ break;
+ case MC13892_GPO3:
+ register_val = BITFVAL(GPO3_EN, GPO3_EN_ENABLE);
+ register_mask = BITFMASK(GPO3_EN);
+ break;
+ case MC13892_GPO4:
+ register_val = BITFVAL(GPO4_EN, GPO4_EN_ENABLE) +
+ BITFVAL(GPO4_ADIN, GPO4_ADIN_DISABLE);
+ register_mask = BITFMASK(GPO4_EN) + BITFMASK(GPO4_ADIN);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ register1 = REG_POWER_MISC;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_gpo_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int gpo = rdev_get_id(reg);
+
+ switch (gpo) {
+ case MC13892_GPO1:
+ register_val = BITFVAL(GPO1_EN, GPO1_EN_DISABLE);
+ register_mask = BITFMASK(GPO1_EN);
+ break;
+ case MC13892_GPO2:
+ register_val = BITFVAL(GPO2_EN, GPO2_EN_DISABLE);
+ register_mask = BITFMASK(GPO2_EN);
+ break;
+ case MC13892_GPO3:
+ register_val = BITFVAL(GPO3_EN, GPO3_EN_DISABLE);
+ register_mask = BITFMASK(GPO3_EN);
+ break;
+ case MC13892_GPO4:
+ register_val = BITFVAL(GPO4_EN, GPO4_EN_DISABLE);
+ register_mask = BITFMASK(GPO4_EN);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ register1 = REG_POWER_MISC;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_power_gating_enable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int gpo = rdev_get_id(reg);
+
+ switch (gpo) {
+ case MC13892_PWGT1:
+ register_val = BITFVAL(PWGT1SPI_EN, PWGT1SPI_EN_ENABLE);
+ register_mask = BITFMASK(PWGT1SPI_EN);
+ break;
+ case MC13892_PWGT2:
+ register_val = BITFVAL(PWGT2SPI_EN, PWGT2SPI_EN_ENABLE);
+ register_mask = BITFMASK(PWGT2SPI_EN);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ register1 = REG_POWER_MISC;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static int mc13892_power_gating_disable(struct regulator_dev *reg)
+{
+ unsigned int register_val = 0, register_mask = 0;
+ unsigned int register1;
+ int gpo = rdev_get_id(reg);
+
+ switch (gpo) {
+ case MC13892_PWGT1:
+ register_val = BITFVAL(PWGT1SPI_EN, PWGT1SPI_EN_DISABLE);
+ register_mask = BITFMASK(PWGT1SPI_EN);
+ break;
+ case MC13892_PWGT2:
+ register_val = BITFVAL(PWGT2SPI_EN, PWGT2SPI_EN_DISABLE);
+ register_mask = BITFMASK(PWGT2SPI_EN);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ register1 = REG_POWER_MISC;
+
+ return pmic_write_reg(register1, register_val, register_mask);
+}
+
+static struct regulator_ops mc13892_sw_ops = {
+ .enable = mc13892_sw_stby_enable,
+ .disable = mc13892_sw_stby_disable,
+ .set_voltage = mc13892_sw_set_voltage,
+ .get_voltage = mc13892_sw_get_voltage,
+ .set_suspend_voltage = mc13892_sw_stby_set_voltage,
+ .set_suspend_enable = mc13892_sw_stby_enable,
+ .set_suspend_disable = mc13892_sw_stby_disable,
+ .set_suspend_mode = mc13892_sw_stby_set_mode,
+};
+
+static struct regulator_ops mc13892_swbst_ops = {
+ .enable = mc13892_swbst_enable,
+ .disable = mc13892_swbst_disable,
+};
+
+static struct regulator_ops mc13892_viohi_ops = {
+ .enable = mc13892_viohi_enable,
+ .disable = mc13892_viohi_disable,
+};
+
+static struct regulator_ops mc13892_vusb_ops = {
+ .enable = mc13892_vusb_enable,
+ .disable = mc13892_vusb_disable,
+};
+
+static struct regulator_ops mc13892_vdig_ops = {
+ .set_voltage = mc13892_vdig_set_voltage,
+ .get_voltage = mc13892_vdig_get_voltage,
+ .enable = mc13892_vdig_enable,
+ .disable = mc13892_vdig_disable,
+};
+
+static struct regulator_ops mc13892_vpll_ops = {
+ .set_voltage = mc13892_vpll_set_voltage,
+ .get_voltage = mc13892_vpll_get_voltage,
+ .enable = mc13892_vpll_enable,
+ .disable = mc13892_vpll_disable,
+};
+
+static struct regulator_ops mc13892_vusb2_ops = {
+ .set_voltage = mc13892_vusb2_set_voltage,
+ .get_voltage = mc13892_vusb2_get_voltage,
+ .enable = mc13892_vusb2_enable,
+ .disable = mc13892_vusb2_disable,
+};
+
+static struct regulator_ops mc13892_vvideo_ops = {
+ .set_voltage = mc13892_vvideo_set_voltage,
+ .get_voltage = mc13892_vvideo_get_voltage,
+ .enable = mc13892_vvideo_enable,
+ .disable = mc13892_vvideo_disable,
+};
+
+static struct regulator_ops mc13892_vaudio_ops = {
+ .set_voltage = mc13892_vaudio_set_voltage,
+ .get_voltage = mc13892_vaudio_get_voltage,
+ .enable = mc13892_vaudio_enable,
+ .disable = mc13892_vaudio_disable,
+};
+
+static struct regulator_ops mc13892_vsd_ops = {
+ .set_voltage = mc13892_vsd_set_voltage,
+ .get_voltage = mc13892_vsd_get_voltage,
+ .enable = mc13892_vsd_enable,
+ .disable = mc13892_vsd_disable,
+};
+
+static struct regulator_ops mc13892_vcam_ops = {
+ .set_voltage = mc13892_vcam_set_voltage,
+ .get_voltage = mc13892_vcam_get_voltage,
+ .enable = mc13892_vcam_enable,
+ .disable = mc13892_vcam_disable,
+ .set_mode = mc13892_vcam_set_mode,
+ .get_mode = mc13892_vcam_get_mode,
+};
+
+static struct regulator_ops mc13892_vgen1_ops = {
+ .set_voltage = mc13892_vgen1_set_voltage,
+ .get_voltage = mc13892_vgen1_get_voltage,
+ .enable = mc13892_vgen1_enable,
+ .disable = mc13892_vgen1_disable,
+};
+
+static struct regulator_ops mc13892_vgen2_ops = {
+ .set_voltage = mc13892_vgen2_set_voltage,
+ .get_voltage = mc13892_vgen2_get_voltage,
+ .enable = mc13892_vgen2_enable,
+ .disable = mc13892_vgen2_disable,
+};
+
+static struct regulator_ops mc13892_vgen3_ops = {
+ .set_voltage = mc13892_vgen3_set_voltage,
+ .get_voltage = mc13892_vgen3_get_voltage,
+ .enable = mc13892_vgen3_enable,
+ .disable = mc13892_vgen3_disable,
+};
+
+static struct regulator_ops mc13892_gpo_ops = {
+ .enable = mc13892_gpo_enable,
+ .disable = mc13892_gpo_disable,
+};
+
+static struct regulator_ops mc13892_power_gating_ops = {
+ .enable = mc13892_power_gating_enable,
+ .disable = mc13892_power_gating_disable,
+
+};
+
+static struct regulator_desc mc13892_reg[] = {
+ {
+ .name = "SW1",
+ .id = MC13892_SW1,
+ .ops = &mc13892_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "SW2",
+ .id = MC13892_SW2,
+ .ops = &mc13892_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "SW3",
+ .id = MC13892_SW3,
+ .ops = &mc13892_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "SW4",
+ .id = MC13892_SW4,
+ .ops = &mc13892_sw_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "SWBST",
+ .id = MC13892_SWBST,
+ .ops = &mc13892_swbst_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VIOHI",
+ .id = MC13892_VIOHI,
+ .ops = &mc13892_viohi_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VPLL",
+ .id = MC13892_VPLL,
+ .ops = &mc13892_vpll_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VDIG",
+ .id = MC13892_VDIG,
+ .ops = &mc13892_vdig_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VSD",
+ .id = MC13892_VSD,
+ .ops = &mc13892_vsd_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VUSB2",
+ .id = MC13892_VUSB2,
+ .ops = &mc13892_vusb2_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VVIDEO",
+ .id = MC13892_VVIDEO,
+ .ops = &mc13892_vvideo_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VAUDIO",
+ .id = MC13892_VAUDIO,
+ .ops = &mc13892_vaudio_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VCAM",
+ .id = MC13892_VCAM,
+ .ops = &mc13892_vcam_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VGEN1",
+ .id = MC13892_VGEN1,
+ .ops = &mc13892_vgen1_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VGEN2",
+ .id = MC13892_VGEN2,
+ .ops = &mc13892_vgen2_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VGEN3",
+ .id = MC13892_VGEN3,
+ .ops = &mc13892_vgen3_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "VUSB",
+ .id = MC13892_VUSB,
+ .ops = &mc13892_vusb_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "GPO1",
+ .id = MC13892_GPO1,
+ .ops = &mc13892_gpo_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "GPO2",
+ .id = MC13892_GPO2,
+ .ops = &mc13892_gpo_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "GPO3",
+ .id = MC13892_GPO3,
+ .ops = &mc13892_gpo_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "GPO4",
+ .id = MC13892_GPO4,
+ .ops = &mc13892_gpo_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "PWGT1",
+ .id = MC13892_PWGT1,
+ .ops = &mc13892_power_gating_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "PWGT2",
+ .id = MC13892_PWGT2,
+ .ops = &mc13892_power_gating_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+};
+
+static int mc13892_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev;
+
+ /* register regulator */
+ rdev = regulator_register(&mc13892_reg[pdev->id], &pdev->dev,
+ dev_get_drvdata(&pdev->dev));
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n",
+ mc13892_reg[pdev->id].name);
+ return PTR_ERR(rdev);
+ }
+
+ return 0;
+}
+
+
+static int mc13892_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+
+ return 0;
+}
+
+int mc13892_register_regulator(struct mc13892 *mc13892, int reg,
+ struct regulator_init_data *initdata)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ if (mc13892->pmic.pdev[reg])
+ return -EBUSY;
+
+ pdev = platform_device_alloc("mc13892-regulatr", reg);
+ if (!pdev)
+ return -ENOMEM;
+
+ mc13892->pmic.pdev[reg] = pdev;
+
+ initdata->driver_data = mc13892;
+
+ pdev->dev.platform_data = initdata;
+ pdev->dev.parent = mc13892->dev;
+ platform_set_drvdata(pdev, mc13892);
+ ret = platform_device_add(pdev);
+
+ if (ret != 0) {
+ dev_err(mc13892->dev, "Failed to register regulator %d: %d\n",
+ reg, ret);
+ platform_device_del(pdev);
+ mc13892->pmic.pdev[reg] = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mc13892_register_regulator);
+
+static struct platform_driver mc13892_regulator_driver = {
+ .probe = mc13892_regulator_probe,
+ .remove = mc13892_regulator_remove,
+ .driver = {
+ .name = "mc13892-regulatr",
+ },
+};
+
+static int __init mc13892_regulator_init(void)
+{
+ return platform_driver_register(&mc13892_regulator_driver);
+}
+subsys_initcall(mc13892_regulator_init);
+
+static void __exit mc13892_regulator_exit(void)
+{
+ platform_driver_unregister(&mc13892_regulator_driver);
+}
+module_exit(mc13892_regulator_exit);
+
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MC13892 Regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/reg-mc34704.c b/drivers/regulator/reg-mc34704.c
new file mode 100644
index 000000000000..a832a639b8ec
--- /dev/null
+++ b/drivers/regulator/reg-mc34704.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/ioctl.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/mc34704/core.h>
+#include <linux/platform_device.h>
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+#define MC34704_ONOFFA 0x8
+#define MC34704_ONOFFC 0x4
+#define MC34704_ONOFFD 0x2
+#define MC34704_ONOFFE 0x1
+
+/* Private data for MC34704 regulators */
+
+struct reg_mc34704_priv {
+ short enable; /* enable bit, if available */
+ short v_default; /* default regulator voltage in mV */
+ int dvs_min; /* minimum voltage change in units of 2.5% */
+ int dvs_max; /* maximum voltage change in units of 2.5% */
+ char i2c_dvs; /* i2c DVS register number */
+ char i2c_stat; /* i2c status register number */
+};
+struct reg_mc34704_priv mc34704_reg_priv[] = {
+ {
+ .v_default = REG1_V_MV,
+ .dvs_min = REG1_DVS_MIN_PCT / 2.5,
+ .dvs_max = REG1_DVS_MAX_PCT / 2.5,
+ .i2c_dvs = 0x4,
+ .i2c_stat = 0x5,
+ .enable = MC34704_ONOFFA,
+ },
+ {
+ .v_default = REG2_V_MV,
+ .dvs_min = REG2_DVS_MIN_PCT / 2.5,
+ .dvs_max = REG2_DVS_MAX_PCT / 2.5,
+ .i2c_dvs = 0x6,
+ .i2c_stat = 0x7,
+ },
+ {
+ .v_default = REG3_V_MV,
+ .dvs_min = REG3_DVS_MIN_PCT / 2.5,
+ .dvs_max = REG3_DVS_MAX_PCT / 2.5,
+ .i2c_dvs = 0x8,
+ .i2c_stat = 0x9,
+ },
+ {
+ .v_default = REG4_V_MV,
+ .dvs_min = REG4_DVS_MIN_PCT / 2.5,
+ .dvs_max = REG4_DVS_MAX_PCT / 2.5,
+ .i2c_dvs = 0xA,
+ .i2c_stat = 0xB,
+ },
+ {
+ .v_default = REG5_V_MV,
+ .dvs_min = REG5_DVS_MIN_PCT / 2.5,
+ .dvs_max = REG5_DVS_MAX_PCT / 2.5,
+ .i2c_dvs = 0xC,
+ .i2c_stat = 0xE,
+ .enable = MC34704_ONOFFE,
+ },
+};
+
+static int mc34704_set_voltage(struct regulator_dev *reg, int MiniV, int uV)
+{
+ struct reg_mc34704_priv *priv = rdev_get_drvdata(reg);
+ int mV = uV / 1000;
+ int dV = mV - priv->v_default;
+
+ /* compute dynamic voltage scaling value */
+ int dvs = 1000 * dV / priv->v_default / 25;
+
+ /* clip to regulator limits */
+ if (dvs > priv->dvs_max)
+ dvs = priv->dvs_max;
+ if (dvs < priv->dvs_min)
+ dvs = priv->dvs_min;
+
+ return pmic_write_reg(priv->i2c_dvs, dvs << 1, 0x1E);
+}
+
+static int mc34704_get_voltage(struct regulator_dev *reg)
+{
+ int mV;
+ struct reg_mc34704_priv *priv = rdev_get_drvdata(reg);
+ int val, dvs;
+
+ CHECK_ERROR(pmic_read_reg(priv->i2c_dvs, &val, 0xF));
+
+ dvs = (val >> 1) & 0xF;
+
+ /* dvs is 4-bit 2's complement; sign-extend it */
+ if (dvs & 8)
+ dvs |= -1 & ~0xF;
+
+ /* Regulator voltage is adjusted by (dvs * 2.5%) */
+ mV = priv->v_default * (1000 + 25 * dvs) / 1000;
+
+ return 1000 * mV;
+}
+
+static int mc34704_enable_reg(struct regulator_dev *reg)
+{
+ struct reg_mc34704_priv *priv = rdev_get_drvdata(reg);
+
+ if (priv->enable)
+ return pmic_write_reg(REG_MC34704_GENERAL2, -1, priv->enable);
+
+ return PMIC_ERROR;
+}
+
+static int mc34704_disable_reg(struct regulator_dev *reg)
+{
+ struct reg_mc34704_priv *priv = rdev_get_drvdata(reg);
+
+ if (priv->enable)
+ return pmic_write_reg(REG_MC34704_GENERAL2, 0, priv->enable);
+
+ return PMIC_ERROR;
+}
+
+static int mc34704_is_reg_enabled(struct regulator_dev *reg)
+{
+ struct reg_mc34704_priv *priv = rdev_get_drvdata(reg);
+ int val;
+
+ if (priv->enable) {
+ CHECK_ERROR(pmic_read_reg(REG_MC34704_GENERAL2, &val,
+ priv->enable));
+ return val ? 1 : 0;
+ } else {
+ return PMIC_ERROR;
+ }
+}
+
+static struct regulator_ops mc34704_full_ops = {
+ .set_voltage = mc34704_set_voltage,
+ .get_voltage = mc34704_get_voltage,
+ .enable = mc34704_enable_reg,
+ .disable = mc34704_disable_reg,
+ .is_enabled = mc34704_is_reg_enabled,
+};
+
+static struct regulator_ops mc34704_partial_ops = {
+ .set_voltage = mc34704_set_voltage,
+ .get_voltage = mc34704_get_voltage,
+};
+
+static struct regulator_desc reg_mc34704[] = {
+ {
+ .name = "REG1_BKLT",
+ .id = MC34704_BKLT,
+ .ops = &mc34704_full_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "REG2_CPU",
+ .id = MC34704_CPU,
+ .ops = &mc34704_partial_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "REG3_CORE",
+ .id = MC34704_CORE,
+ .ops = &mc34704_partial_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "REG4_DDR",
+ .id = MC34704_DDR,
+ .ops = &mc34704_partial_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+ {
+ .name = "REG5_PERS",
+ .id = MC34704_PERS,
+ .ops = &mc34704_full_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE},
+};
+
+static int mc34704_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev;
+
+ /* register regulator */
+ rdev = regulator_register(&reg_mc34704[pdev->id], &pdev->dev,
+ (void *)&mc34704_reg_priv[pdev->id]);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n",
+ reg_mc34704[pdev->id].name);
+ return PTR_ERR(rdev);
+ }
+
+ return 0;
+}
+
+static int mc34704_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+
+ return 0;
+}
+
+int mc34704_register_regulator(struct mc34704 *mc34704, int reg,
+ struct regulator_init_data *initdata)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ if (mc34704->pmic.pdev[reg])
+ return -EBUSY;
+
+ pdev = platform_device_alloc("mc34704-regulatr", reg);
+ if (!pdev)
+ return -ENOMEM;
+
+ mc34704->pmic.pdev[reg] = pdev;
+
+ initdata->driver_data = mc34704;
+
+ pdev->dev.platform_data = initdata;
+ pdev->dev.driver_data = &mc34704_reg_priv[reg];
+ pdev->dev.parent = mc34704->dev;
+ platform_set_drvdata(pdev, mc34704);
+ ret = platform_device_add(pdev);
+
+ if (ret != 0) {
+ dev_err(mc34704->dev, "Failed to register regulator %d: %d\n",
+ reg, ret);
+ platform_device_del(pdev);
+ mc34704->pmic.pdev[reg] = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mc34704_register_regulator);
+
+static struct platform_driver mc34704_regulator_driver = {
+ .probe = mc34704_regulator_probe,
+ .remove = mc34704_regulator_remove,
+ .driver = {
+ .name = "mc34704-regulatr",
+ },
+};
+
+static int __init mc34704_regulator_init(void)
+{
+ return platform_driver_register(&mc34704_regulator_driver);
+}
+subsys_initcall(mc34704_regulator_init);
+
+static void __exit mc34704_regulator_exit(void)
+{
+ platform_driver_unregister(&mc34704_regulator_driver);
+}
+module_exit(mc34704_regulator_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MC34704 Regulator Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/reg-mc9s08dz60.c b/drivers/regulator/reg-mc9s08dz60.c
new file mode 100644
index 000000000000..c55100ded635
--- /dev/null
+++ b/drivers/regulator/reg-mc9s08dz60.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/mc9s08dz60/core.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include <linux/platform_device.h>
+#include <linux/pmic_status.h>
+
+/* lcd */
+static int mc9s08dz60_lcd_enable(struct regulator_dev *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 6, 1);
+}
+
+static int mc9s08dz60_lcd_disable(struct regulator_dev *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 6, 0);
+}
+
+static struct regulator_ops mc9s08dz60_lcd_ops = {
+ .enable = mc9s08dz60_lcd_enable,
+ .disable = mc9s08dz60_lcd_disable,
+};
+
+/* wifi */
+static int mc9s08dz60_wifi_enable(struct regulator_dev *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 5, 1);
+}
+
+static int mc9s08dz60_wifi_disable(struct regulator_dev *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 5, 0);
+}
+
+static struct regulator_ops mc9s08dz60_wifi_ops = {
+ .enable = mc9s08dz60_wifi_enable,
+ .disable = mc9s08dz60_wifi_disable,
+};
+
+/* hdd */
+static int mc9s08dz60_hdd_enable(struct regulator_dev *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 4, 1);
+}
+
+static int mc9s08dz60_hdd_disable(struct regulator_dev *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 4, 0);
+}
+
+static struct regulator_ops mc9s08dz60_hdd_ops = {
+ .enable = mc9s08dz60_hdd_enable,
+ .disable = mc9s08dz60_hdd_disable,
+};
+
+/* gps */
+static int mc9s08dz60_gps_enable(struct regulator_dev *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 0, 1);
+}
+
+static int mc9s08dz60_gps_disable(struct regulator_dev *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_2, 0, 0);
+}
+
+static struct regulator_ops mc9s08dz60_gps_ops = {
+ .enable = mc9s08dz60_gps_enable,
+ .disable = mc9s08dz60_gps_disable,
+};
+
+/* speaker */
+static int mc9s08dz60_speaker_enable(struct regulator_dev *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 0, 1);
+}
+
+static int mc9s08dz60_speaker_disable(struct regulator_dev *reg)
+{
+ return pmic_gpio_set_bit_val(MCU_GPIO_REG_GPIO_CONTROL_1, 0, 0);
+}
+
+static struct regulator_ops mc9s08dz60_speaker_ops = {
+ .enable = mc9s08dz60_speaker_enable,
+ .disable = mc9s08dz60_speaker_disable,
+};
+
+static struct regulator_desc mc9s08dz60_reg[] = {
+ {
+ .name = "LCD",
+ .id = MC9S08DZ60_LCD,
+ .ops = &mc9s08dz60_lcd_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "WIFI",
+ .id = MC9S08DZ60_WIFI,
+ .ops = &mc9s08dz60_wifi_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "HDD",
+ .id = MC9S08DZ60_HDD,
+ .ops = &mc9s08dz60_hdd_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "GPS",
+ .id = MC9S08DZ60_GPS,
+ .ops = &mc9s08dz60_gps_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "SPKR",
+ .id = MC9S08DZ60_SPKR,
+ .ops = &mc9s08dz60_speaker_ops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+
+};
+
+static int mc9s08dz60_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev;
+
+ /* register regulator */
+ rdev = regulator_register(&mc9s08dz60_reg[pdev->id], &pdev->dev,
+ dev_get_drvdata(&pdev->dev));
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n",
+ mc9s08dz60_reg[pdev->id].name);
+ return PTR_ERR(rdev);
+ }
+
+ return 0;
+}
+
+
+static int mc9s08dz60_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+
+ return 0;
+}
+
+int mc9s08dz60_register_regulator(struct mc9s08dz60 *mc9s08dz60, int reg,
+ struct regulator_init_data *initdata)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ if (mc9s08dz60->pmic.pdev[reg])
+ return -EBUSY;
+
+ pdev = platform_device_alloc("mc9s08dz60-regu", reg);
+ if (!pdev)
+ return -ENOMEM;
+
+ mc9s08dz60->pmic.pdev[reg] = pdev;
+
+ initdata->driver_data = mc9s08dz60;
+
+ pdev->dev.platform_data = initdata;
+ pdev->dev.parent = mc9s08dz60->dev;
+ platform_set_drvdata(pdev, mc9s08dz60);
+ ret = platform_device_add(pdev);
+
+ if (ret != 0) {
+ dev_err(mc9s08dz60->dev,
+ "Failed to register regulator %d: %d\n",
+ reg, ret);
+ platform_device_del(pdev);
+ mc9s08dz60->pmic.pdev[reg] = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mc9s08dz60_register_regulator);
+
+static struct platform_driver mc9s08dz60_regulator_driver = {
+ .probe = mc9s08dz60_regulator_probe,
+ .remove = mc9s08dz60_regulator_remove,
+ .driver = {
+ .name = "mc9s08dz60-regu",
+ },
+};
+
+static int __init mc9s08dz60_regulator_init(void)
+{
+ return platform_driver_register(&mc9s08dz60_regulator_driver);
+}
+subsys_initcall(mc9s08dz60_regulator_init);
+
+static void __exit mc9s08dz60_regulator_exit(void)
+{
+ platform_driver_unregister(&mc9s08dz60_regulator_driver);
+}
+module_exit(mc9s08dz60_regulator_exit);
+
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MC9S08DZ60 Regulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/stmp3xxx.c b/drivers/regulator/stmp3xxx.c
new file mode 100644
index 000000000000..762a18496347
--- /dev/null
+++ b/drivers/regulator/stmp3xxx.c
@@ -0,0 +1,300 @@
+/*
+ * Freescale STMP378X voltage regulators
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <mach/power.h>
+#include <mach/regulator.h>
+
+static int stmp3xxx_set_voltage(struct regulator_dev *reg, int MiniV, int uv)
+{
+ struct stmp3xxx_regulator *stmp_reg = rdev_get_drvdata(reg);
+
+ if (stmp_reg->rdata->set_voltage)
+ return stmp_reg->rdata->set_voltage(stmp_reg, uv);
+ else
+ return -ENOTSUPP;
+}
+
+
+static int stmp3xxx_get_voltage(struct regulator_dev *reg)
+{
+ struct stmp3xxx_regulator *stmp_reg = rdev_get_drvdata(reg);
+
+ if (stmp_reg->rdata->get_voltage)
+ return stmp_reg->rdata->get_voltage(stmp_reg);
+ else
+ return -ENOTSUPP;
+}
+
+static int stmp3xxx_set_current(struct regulator_dev *reg, int min_uA, int uA)
+{
+ struct stmp3xxx_regulator *stmp_reg = rdev_get_drvdata(reg);
+
+ if (stmp_reg->rdata->set_current)
+ return stmp_reg->rdata->set_current(stmp_reg, uA);
+ else
+ return -ENOTSUPP;
+}
+
+static int stmp3xxx_get_current(struct regulator_dev *reg)
+{
+ struct stmp3xxx_regulator *stmp_reg = rdev_get_drvdata(reg);
+
+ if (stmp_reg->rdata->get_current)
+ return stmp_reg->rdata->get_current(stmp_reg);
+ else
+ return -ENOTSUPP;
+}
+
+static int stmp3xxx_enable(struct regulator_dev *reg)
+{
+ struct stmp3xxx_regulator *stmp_reg = rdev_get_drvdata(reg);
+
+ return stmp_reg->rdata->enable(stmp_reg);
+}
+
+static int stmp3xxx_disable(struct regulator_dev *reg)
+{
+ struct stmp3xxx_regulator *stmp_reg = rdev_get_drvdata(reg);
+
+ return stmp_reg->rdata->disable(stmp_reg);
+}
+
+static int stmp3xxx_is_enabled(struct regulator_dev *reg)
+{
+ struct stmp3xxx_regulator *stmp_reg = rdev_get_drvdata(reg);
+
+ return stmp_reg->rdata->is_enabled(stmp_reg);
+}
+
+static int stmp3xxx_set_mode(struct regulator_dev *reg, unsigned int mode)
+{
+ struct stmp3xxx_regulator *stmp_reg = rdev_get_drvdata(reg);
+
+ return stmp_reg->rdata->set_mode(stmp_reg, mode);
+}
+
+static unsigned int stmp3xxx_get_mode(struct regulator_dev *reg)
+{
+ struct stmp3xxx_regulator *stmp_reg = rdev_get_drvdata(reg);
+
+ return stmp_reg->rdata->get_mode(stmp_reg);
+}
+
+static unsigned int stmp3xxx_get_optimum_mode(struct regulator_dev *reg,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct stmp3xxx_regulator *stmp_reg = rdev_get_drvdata(reg);
+
+ if (stmp_reg->rdata->get_optimum_mode)
+ return stmp_reg->rdata->get_optimum_mode(stmp_reg, input_uV,
+ output_uV, load_uA);
+ else
+ return -ENOTSUPP;
+}
+
+static struct regulator_ops stmp3xxx_rops = {
+ .set_voltage = stmp3xxx_set_voltage,
+ .get_voltage = stmp3xxx_get_voltage,
+ .set_current_limit = stmp3xxx_set_current,
+ .get_current_limit = stmp3xxx_get_current,
+ .enable = stmp3xxx_enable,
+ .disable = stmp3xxx_disable,
+ .is_enabled = stmp3xxx_is_enabled,
+ .set_mode = stmp3xxx_set_mode,
+ .get_mode = stmp3xxx_get_mode,
+ .get_optimum_mode = stmp3xxx_get_optimum_mode,
+};
+
+static struct regulator_desc stmp3xxx_reg_desc[] = {
+ {
+ .name = "vddd",
+ .id = STMP3XXX_VDDD,
+ .ops = &stmp3xxx_rops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "vdda",
+ .id = STMP3XXX_VDDA,
+ .ops = &stmp3xxx_rops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "vddio",
+ .id = STMP3XXX_VDDIO,
+ .ops = &stmp3xxx_rops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "vddd_bo",
+ .id = STMP3XXX_VDDDBO,
+ .ops = &stmp3xxx_rops,
+ .irq = 0,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE
+ },
+ {
+ .name = "overall_current",
+ .id = STMP3XXX_OVERALL_CUR,
+ .ops = &stmp3xxx_rops,
+ .irq = 0,
+ .type = REGULATOR_CURRENT,
+ .owner = THIS_MODULE
+ },
+};
+
+static int reg_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ unsigned long flags;
+ struct stmp3xxx_regulator *sreg =
+ container_of(self, struct stmp3xxx_regulator , nb);
+
+ switch (event) {
+ case STMP3XXX_REG5V_IS_USB:
+ spin_lock_irqsave(&sreg->lock, flags);
+ sreg->rdata->max_current = 500000;
+ spin_unlock_irqrestore(&sreg->lock, flags);
+ break;
+ case STMP3XXX_REG5V_NOT_USB:
+ spin_lock_irqsave(&sreg->lock, flags);
+ sreg->rdata->max_current = 0x7fffffff;
+ spin_unlock_irqrestore(&sreg->lock, flags);
+ break;
+ }
+
+ return 0;
+}
+
+int stmp3xxx_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_desc *rdesc;
+ struct regulator_dev *rdev;
+ struct stmp3xxx_regulator *sreg;
+
+ sreg = platform_get_drvdata(pdev);
+ sreg->cur_current = 0;
+ sreg->next_current = 0;
+ sreg->cur_voltage = 0;
+
+ init_waitqueue_head(&sreg->wait_q);
+ spin_lock_init(&sreg->lock);
+
+ if (pdev->id > STMP3XXX_OVERALL_CUR) {
+ rdesc = kzalloc(sizeof(struct regulator_desc), GFP_KERNEL);
+ memcpy(rdesc, &stmp3xxx_reg_desc[STMP3XXX_OVERALL_CUR],
+ sizeof(struct regulator_desc));
+ rdesc->name = kstrdup(sreg->rdata->name, GFP_KERNEL);
+ } else
+ rdesc = &stmp3xxx_reg_desc[pdev->id];
+
+ pr_debug("probing regulator %s %s %d\n",
+ sreg->rdata->name,
+ rdesc->name,
+ pdev->id);
+
+ /* register regulator */
+ rdev = regulator_register(rdesc, &pdev->dev,
+ sreg);
+
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "failed to register %s\n",
+ rdesc->name);
+ return PTR_ERR(rdev);
+ }
+
+ if (sreg->rdata->max_current) {
+ struct regulator *regu;
+ regu = regulator_get(NULL, sreg->rdata->name);
+ sreg->nb.notifier_call = reg_callback;
+ regulator_register_notifier(regu, &sreg->nb);
+ }
+
+ return 0;
+}
+
+
+int stmp3xxx_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+
+ return 0;
+
+}
+
+int stmp3xxx_register_regulator(
+ struct stmp3xxx_regulator *reg_data, int reg,
+ struct regulator_init_data *initdata)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = platform_device_alloc("stmp3xxx_reg", reg);
+ if (!pdev)
+ return -ENOMEM;
+
+ pdev->dev.platform_data = initdata;
+
+ platform_set_drvdata(pdev, reg_data);
+ ret = platform_device_add(pdev);
+
+ if (ret != 0) {
+ pr_debug("Failed to register regulator %d: %d\n",
+ reg, ret);
+ platform_device_del(pdev);
+ }
+ pr_debug("register regulator %s, %d: %d\n",
+ reg_data->rdata->name, reg, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stmp3xxx_register_regulator);
+
+struct platform_driver stmp3xxx_reg = {
+ .driver = {
+ .name = "stmp3xxx_reg",
+ },
+ .probe = stmp3xxx_regulator_probe,
+ .remove = stmp3xxx_regulator_remove,
+};
+
+int stmp3xxx_regulator_init(void)
+{
+ return platform_driver_register(&stmp3xxx_reg);
+}
+
+void stmp3xxx_regulator_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_reg);
+}
+
+postcore_initcall(stmp3xxx_regulator_init);
+module_exit(stmp3xxx_regulator_exit);
+
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 123092d8a984..541908343d97 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -522,6 +522,17 @@ config RTC_DRV_S3C
This driver can also be build as a module. If so, the module
will be called rtc-s3c.
+config RTC_DRV_STMP3XXX
+ tristate "Sigmatel STMP3xxx series SoC RTC"
+ depends on ARCH_STMP3XXX && RTC_CLASS
+ help
+ Say Y here to get support for the real-time clock peripheral
+ on Sigmatel STMP3xxx series SoCs (tested on STMP3700).
+
+ This driver can also be build as a module. If so, the module
+ will be called rtc-stmp3xxx.
+
+
config RTC_DRV_EP93XX
tristate "Cirrus Logic EP93XX"
depends on ARCH_EP93XX
@@ -657,9 +668,38 @@ config RTC_DRV_PARISC
firmware calls. If you do not know what you are doing, you should
just say Y.
+config RTC_MXC
+ tristate "Freescale MXC Real Time Clock"
+ depends on ARCH_MXC
+ depends on RTC_CLASS
+ help
+ Support for Freescale RTC MXC
+
+config RTC_DRV_MXC_V2
+ tristate "Freescale MXC Secure Real Time Clock"
+ depends on ARCH_MXC
+ depends on RTC_CLASS
+ help
+ Support for Freescale SRTC MXC
+
+config RTC_DRV_IMXDI
+ tristate "Freescale IMX DryIce Real Time Clock"
+ depends on ARCH_MXC
+ depends on RTC_CLASS
+ help
+ Support for Freescale IMX DryIce RTC
+
+config RTC_MC13892
+ tristate "Freescale MC13892 Real Time Clock"
+ depends on ARCH_MXC && MXC_PMIC_MC13892
+ depends on RTC_CLASS
+ help
+ Support for Freescale MC13892 RTC
+
config RTC_DRV_PPC
tristate "PowerPC machine dependent RTC support"
depends on PPC
+ depends on RTC_CLASS
help
The PowerPC kernel has machine-specific functions for accessing
the RTC. This exposes that functionality through the generic RTC
@@ -679,4 +719,14 @@ config RTC_DRV_STARFIRE
If you say Y here you will get support for the RTC found on
Starfire systems.
+config RTC_DRV_NS9XXX
+ tristate "Digi ns9xxx On-Chip RTC"
+ depends on PROCESSOR_NS9215 || PROCESSOR_NS9360
+ help
+ Driver for the internal RTC (Realtime Clock) module found on
+ Digi ns9xxx CPUs.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ns9xxx.
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e79c912bf9e..990f49c743bc 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_NS9XXX) += rtc-ns9xxx.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
@@ -64,9 +65,14 @@ obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
+obj-$(CONFIG_RTC_DRV_STMP3XXX) += rtc-stmp3xxx.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
+obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
+obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o
+obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
+obj-$(CONFIG_RTC_MC13892) += rtc-mc13892.o
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 162330b9d1dc..1efa70d20968 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -16,7 +16,7 @@
#include <linux/string.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
-
+#include <linux/platform_device.h>
/* We can't determine type by probing, but if we expect pre-Linux code
@@ -748,6 +748,17 @@ read_rtc:
bin2bcd(tmp));
}
+ /*
+ * Assume that with an IRQ-line, we can wakeup the system by using it.
+ * But for this purpose it's required to enable the wakeup-support before
+ * registering the RTC (otherwise it will fail)
+ * (Luis Galdos)
+ */
+ if (want_irq) {
+ device_init_wakeup(&client->dev, 1);
+ device_set_wakeup_enable(&client->dev, 0);
+ }
+
ds1307->rtc = rtc_device_register(client->name, &client->dev,
&ds13xx_rtc_ops, THIS_MODULE);
if (IS_ERR(ds1307->rtc)) {
@@ -811,6 +822,37 @@ static int __devexit ds1307_remove(struct i2c_client *client)
return 0;
}
+#if defined(CONFIG_PM)
+static int ds1307_suspend(struct i2c_client *client, pm_message_t state)
+{
+ if (device_may_wakeup(&client->dev)) {
+ enable_irq_wake(client->irq);
+ }
+
+ return 0;
+}
+
+static int ds1307_resume(struct i2c_client *client)
+{
+ if (device_may_wakeup(&client->dev)) {
+ struct ds1307 *ds1307;
+
+ ds1307 = i2c_get_clientdata(client);
+
+ disable_irq_wake(client->irq);
+
+ /* Disable the IRQ, then the work function will enable it */
+ disable_irq_nosync(client->irq);
+ schedule_work(&ds1307->work);
+ }
+
+ return 0;
+}
+#else
+#define ds1307_suspend NULL
+#define ds1307_resume NULL
+#endif /* defined(CONFIG_PM) */
+
static struct i2c_driver ds1307_driver = {
.driver = {
.name = "rtc-ds1307",
@@ -818,6 +860,8 @@ static struct i2c_driver ds1307_driver = {
},
.probe = ds1307_probe,
.remove = __devexit_p(ds1307_remove),
+ .resume = ds1307_resume,
+ .suspend = ds1307_suspend,
.id_table = ds1307_id,
};
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 000000000000..b54fb638c840
--- /dev/null
+++ b/drivers/rtc/rtc-imxdi.c
@@ -0,0 +1,580 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF. Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+/* #define DEBUG */
+/* #define DI_DEBUG_REGIO */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR 0x00 /* Time Counter MSB Reg */
+#define DTCLR 0x04 /* Time Counter LSB Reg */
+
+#define DCAMR 0x08 /* Clock Alarm MSB Reg */
+#define DCALR 0x0c /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */
+
+#define DCR 0x10 /* Control Reg */
+#define DCR_TCE (1 << 3) /* Time Counter Enable */
+
+#define DSR 0x14 /* Status Reg */
+#define DSR_WBF (1 << 10) /* Write Busy Flag */
+#define DSR_WNF (1 << 9) /* Write Next Flag */
+#define DSR_WCF (1 << 8) /* Write Complete Flag */
+#define DSR_WEF (1 << 7) /* Write Error Flag */
+#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
+#define DSR_NVF (1 << 1) /* Non-Valid Flag */
+#define DSR_SVF (1 << 0) /* Security Violation Flag */
+
+#define DIER 0x18 /* Interrupt Enable Reg */
+#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
+
+#ifndef DI_DEBUG_REGIO
+/* dryice read register */
+#define di_read(pdata, reg) __raw_readl((pdata)->ioaddr + (reg))
+
+/* dryice write register */
+#define di_write(pdata, val, reg) __raw_writel((val), (pdata)->ioaddr + (reg))
+#else
+/* dryice read register - debug version */
+static inline u32 di_read(struct rtc_drv_data *pdata, int reg)
+{
+ u32 val = __raw_readl(pdata->ioaddr + reg);
+ pr_info("di_read(0x%02x) = 0x%08x\n", reg, val);
+ return val;
+}
+
+/* dryice write register - debug version */
+static inline void di_write(struct rtc_drv_data *pdata, u32 val, int reg)
+{
+ printk(KERN_INFO "di_write(0x%08x, 0x%02x)\n", val, reg);
+ __raw_writel(val, pdata->ioaddr + reg);
+}
+#endif
+
+/*
+ * dryice write register with wait and error handling.
+ * all registers, except for DIER, should use this method.
+ */
+#define di_write_wait_err(pdata, val, reg, rc, label) \
+ do { \
+ if (di_write_wait((pdata), (val), (reg))) { \
+ rc = -EIO; \
+ goto label; \
+ } \
+ } while (0)
+
+struct rtc_drv_data {
+ struct platform_device *pdev; /* pointer to platform dev */
+ struct rtc_device *rtc; /* pointer to rtc struct */
+ unsigned long baseaddr; /* physical bass address */
+ void __iomem *ioaddr; /* virtual base address */
+ int size; /* size of register region */
+ int irq; /* dryice normal irq */
+ struct clk *clk; /* dryice clock control */
+ u32 dsr; /* copy of dsr reg from isr */
+ spinlock_t irq_lock; /* irq resource lock */
+ wait_queue_head_t write_wait; /* write-complete queue */
+ struct mutex write_mutex; /* force reg writes to be sequential */
+ struct work_struct work; /* schedule alarm work */
+};
+
+/*
+ * enable a dryice interrupt
+ */
+static inline void di_int_enable(struct rtc_drv_data *pdata, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->irq_lock, flags);
+ di_write(pdata, di_read(pdata, DIER) | intr, DIER);
+ spin_unlock_irqrestore(&pdata->irq_lock, flags);
+}
+
+/*
+ * disable a dryice interrupt
+ */
+static inline void di_int_disable(struct rtc_drv_data *pdata, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->irq_lock, flags);
+ di_write(pdata, di_read(pdata, DIER) & ~intr, DIER);
+ spin_unlock_irqrestore(&pdata->irq_lock, flags);
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation. Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct rtc_drv_data *pdata)
+{
+ int cnt;
+
+ dev_warn(&pdata->pdev->dev, "WARNING: Register write error!\n");
+
+ for (;;) {
+ /* clear the write error flag */
+ di_write(pdata, DSR_WEF, DSR);
+
+ /* wait for it to take effect */
+ for (cnt = 0; cnt < 100; cnt++) {
+ if ((di_read(pdata, DSR) & DSR_WEF) == 0)
+ return;
+ udelay(10);
+ }
+ dev_err(&pdata->pdev->dev,
+ "ERROR: Cannot clear write-error flag!\n");
+ }
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct rtc_drv_data *pdata, u32 val, int reg)
+{
+ int ret;
+ int rc = 0;
+
+ /* serialize register writes */
+ mutex_lock(&pdata->write_mutex);
+
+ /* enable the write-complete interrupt */
+ di_int_enable(pdata, DIER_WCIE);
+
+ pdata->dsr = 0;
+
+ /* do the register write */
+ di_write(pdata, val, reg);
+
+ /* wait for the write to finish */
+ ret = wait_event_interruptible_timeout(pdata->write_wait,
+ pdata->dsr & (DSR_WCF | DSR_WEF),
+ 1 * HZ);
+ if (ret == 0)
+ dev_warn(&pdata->pdev->dev, "Write-wait timeout\n");
+
+ /* check for write error */
+ if (pdata->dsr & DSR_WEF) {
+ clear_write_error(pdata);
+ rc = -EIO;
+ }
+ mutex_unlock(&pdata->write_mutex);
+ return rc;
+}
+
+/*
+ * rtc device ioctl
+ *
+ * The rtc framework handles the basic rtc ioctls on behalf
+ * of the driver by calling the functions registered in the
+ * rtc_ops structure.
+ */
+static int dryice_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s(0x%x)\n", __func__, cmd);
+ switch (cmd) {
+ case RTC_AIE_OFF: /* alarm disable */
+ di_int_disable(pdata, DIER_CAIE);
+ return 0;
+
+ case RTC_AIE_ON: /* alarm enable */
+ di_int_enable(pdata, DIER_CAIE);
+ return 0;
+ }
+ return -ENOIOCTLCMD;
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ unsigned long now;
+
+ dev_dbg(dev, "%s\n", __func__);
+ now = di_read(pdata, DTCMR);
+ rtc_time_to_tm(now, tm);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ unsigned long now;
+ int rc;
+
+ dev_dbg(dev, "%s\n", __func__);
+ rc = rtc_tm_to_time(tm, &now);
+ if (rc == 0) {
+ /* zero the fractional part first */
+ di_write_wait_err(pdata, 0, DTCLR, rc, err);
+ di_write_wait_err(pdata, now, DTCMR, rc, err);
+ }
+err:
+ return rc;
+}
+
+/*
+ * read the seconds portion of the alarm register.
+ * the fractional part of the alarm register is always zero.
+ */
+static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ u32 dcamr;
+
+ dev_dbg(dev, "%s\n", __func__);
+ dcamr = di_read(pdata, DCAMR);
+ rtc_time_to_tm(dcamr, &alarm->time);
+
+ /* alarm is enabled if the interrupt is enabled */
+ alarm->enabled = (di_read(pdata, DIER) & DIER_CAIE) != 0;
+
+ /* don't allow the DSR read to mess up DSR_WCF */
+ mutex_lock(&pdata->write_mutex);
+
+ /* alarm is pending if the alarm flag is set */
+ alarm->pending = (di_read(pdata, DSR) & DSR_CAF) != 0;
+
+ mutex_unlock(&pdata->write_mutex);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice alarm register
+ */
+static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ unsigned long now;
+ unsigned long alarm_time;
+ int rc;
+
+ dev_dbg(dev, "%s\n", __func__);
+ rc = rtc_tm_to_time(&alarm->time, &alarm_time);
+ if (rc)
+ return rc;
+
+ /* don't allow setting alarm in the past */
+ now = di_read(pdata, DTCMR);
+ if (alarm_time < now)
+ return -EINVAL;
+
+ /* write the new alarm time */
+ di_write_wait_err(pdata, (u32)alarm_time, DCAMR, rc, err);
+
+ if (alarm->enabled)
+ di_int_enable(pdata, DIER_CAIE); /* enable alarm intr */
+ else
+ di_int_disable(pdata, DIER_CAIE); /* disable alarm intr */
+err:
+ return rc;
+}
+
+static struct rtc_class_ops dryice_rtc_ops = {
+ .ioctl = dryice_rtc_ioctl,
+ .read_time = dryice_rtc_read_time,
+ .set_time = dryice_rtc_set_time,
+ .read_alarm = dryice_rtc_read_alarm,
+ .set_alarm = dryice_rtc_set_alarm,
+};
+
+/*
+ * dryice "normal" interrupt handler
+ */
+static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
+{
+ struct rtc_drv_data *pdata = dev_id;
+ u32 dsr, dier;
+ irqreturn_t rc = IRQ_NONE;
+
+ dier = di_read(pdata, DIER);
+
+ /* handle write complete and write error cases */
+ if ((dier & DIER_WCIE)) {
+ /*If the write wait queue is empty then there is no pending
+ operations. It means the interrupt is for DryIce -Security.
+ IRQ must be returned as none.*/
+ if (list_empty_careful(&pdata->write_wait.task_list))
+ return rc;
+
+ /* DSR_WCF clears itself on DSR read */
+ dsr = di_read(pdata, DSR);
+ if ((dsr & (DSR_WCF | DSR_WEF))) {
+ /* mask the interrupt */
+ di_int_disable(pdata, DIER_WCIE);
+
+ /* save the dsr value for the wait queue */
+ pdata->dsr |= dsr;
+
+ wake_up_interruptible(&pdata->write_wait);
+ rc = IRQ_HANDLED;
+ }
+ }
+
+ /* handle the alarm case */
+ if ((dier & DIER_CAIE)) {
+ /* DSR_WCF clears itself on DSR read */
+ dsr = di_read(pdata, DSR);
+ if (dsr & DSR_CAF) {
+ /* mask the interrupt */
+ di_int_disable(pdata, DIER_CAIE);
+
+ /* finish alarm in user context */
+ schedule_work(&pdata->work);
+ rc = IRQ_HANDLED;
+ }
+ }
+ return rc;
+}
+
+/*
+ * post the alarm event from user context so it can sleep
+ * on the write completion.
+ */
+static void dryice_work(struct work_struct *work)
+{
+ struct rtc_drv_data *pdata = container_of(work, struct rtc_drv_data,
+ work);
+ int rc;
+
+ /* dismiss the interrupt (ignore error) */
+ di_write_wait_err(pdata, DSR_CAF, DSR, rc, err);
+err:
+ /*
+ * pass the alarm event to the rtc framework. note that
+ * rtc_update_irq expects to be called with interrupts off.
+ */
+ local_irq_disable();
+ rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF);
+ local_irq_enable();
+}
+
+/*
+ * probe for dryice rtc device
+ */
+static int dryice_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct resource *res;
+ struct rtc_drv_data *pdata = NULL;
+ void __iomem *ioaddr = NULL;
+ int rc = 0;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->pdev = pdev;
+ pdata->irq = -1;
+ pdata->size = res->end - res->start + 1;
+
+ if (!request_mem_region(res->start, pdata->size, pdev->name)) {
+ rc = -EBUSY;
+ goto err;
+ }
+ pdata->baseaddr = res->start;
+ ioaddr = ioremap(pdata->baseaddr, pdata->size);
+ if (!ioaddr) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ pdata->ioaddr = ioaddr;
+ pdata->irq = platform_get_irq(pdev, 0);
+
+ init_waitqueue_head(&pdata->write_wait);
+
+ INIT_WORK(&pdata->work, dryice_work);
+
+ mutex_init(&pdata->write_mutex);
+
+ pdata->clk = clk_get(NULL, "dryice_clk");
+ clk_enable(pdata->clk);
+
+ if (pdata->irq >= 0) {
+ if (request_irq(pdata->irq, dryice_norm_irq, IRQF_SHARED,
+ pdev->name, pdata) < 0) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = -1;
+ goto err;
+ }
+ }
+
+ /*
+ * Initialize dryice hardware
+ */
+
+ /* put dryice into valid state */
+ if (di_read(pdata, DSR) & DSR_NVF)
+ di_write_wait_err(pdata, DSR_NVF | DSR_SVF, DSR, rc, err);
+
+ /* mask alarm interrupt */
+ di_int_disable(pdata, DIER_CAIE);
+
+ /* initialize alarm */
+ di_write_wait_err(pdata, DCAMR_UNSET, DCAMR, rc, err);
+ di_write_wait_err(pdata, 0, DCALR, rc, err);
+
+ /* clear alarm flag */
+ if (di_read(pdata, DSR) & DSR_CAF)
+ di_write_wait_err(pdata, DSR_CAF, DSR, rc, err);
+
+ /* the timer won't count if it has never been written to */
+ if (!di_read(pdata, DTCMR))
+ di_write_wait_err(pdata, 0, DTCMR, rc, err);
+
+ /* start keeping time */
+ if (!(di_read(pdata, DCR) & DCR_TCE))
+ di_write_wait_err(pdata, di_read(pdata, DCR) | DCR_TCE, DCR,
+ rc, err);
+
+ rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &dryice_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ rc = PTR_ERR(rtc);
+ goto err;
+ }
+ pdata->rtc = rtc;
+ platform_set_drvdata(pdev, pdata);
+
+ return 0;
+err:
+ if (pdata->rtc)
+ rtc_device_unregister(pdata->rtc);
+
+ if (pdata->irq >= 0)
+ free_irq(pdata->irq, pdata);
+
+ if (pdata->clk) {
+ clk_disable(pdata->clk);
+ clk_put(pdata->clk);
+ }
+
+ if (pdata->ioaddr)
+ iounmap(pdata->ioaddr);
+
+ if (pdata->baseaddr)
+ release_mem_region(pdata->baseaddr, pdata->size);
+
+ kfree(pdata);
+
+ return rc;
+}
+
+static int __exit dryice_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
+
+ flush_scheduled_work();
+
+ if (pdata->rtc)
+ rtc_device_unregister(pdata->rtc);
+
+ /* mask alarm interrupt */
+ di_int_disable(pdata, DIER_CAIE);
+
+ if (pdata->irq >= 0)
+ free_irq(pdata->irq, pdata);
+
+ if (pdata->clk) {
+ clk_disable(pdata->clk);
+ clk_put(pdata->clk);
+ }
+
+ if (pdata->ioaddr)
+ iounmap(pdata->ioaddr);
+
+ if (pdata->baseaddr)
+ release_mem_region(pdata->baseaddr, pdata->size);
+
+ kfree(pdata);
+
+ return 0;
+}
+
+static struct platform_driver dryice_rtc_driver = {
+ .driver = {
+ .name = "imxdi_rtc",
+ .owner = THIS_MODULE,
+ },
+ .probe = dryice_rtc_probe,
+ .remove = __exit_p(dryice_rtc_remove),
+};
+
+static int __init dryice_rtc_init(void)
+{
+ pr_info("IMXDI Realtime Clock Driver (RTC)\n");
+ return platform_driver_register(&dryice_rtc_driver);
+}
+
+static void __exit dryice_rtc_exit(void)
+{
+ platform_driver_unregister(&dryice_rtc_driver);
+}
+
+module_init(dryice_rtc_init);
+module_exit(dryice_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("IMXDI Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-mc13892.c b/drivers/rtc/rtc-mc13892.c
new file mode 100644
index 000000000000..e579c1853a26
--- /dev/null
+++ b/drivers/rtc/rtc-mc13892.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/rtc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+#define RTC_TIME_LSH 0
+#define RTC_DAY_LSH 0
+#define RTCALARM_TIME_LSH 0
+#define RTCALARM_DAY_LSH 0
+
+#define RTC_TIME_WID 17
+#define RTC_DAY_WID 15
+#define RTCALARM_TIME_WID 17
+#define RTCALARM_DAY_WID 15
+
+static unsigned long rtc_status;
+
+static int mxc_rtc_open(struct device *dev)
+{
+ if (test_and_set_bit(1, &rtc_status))
+ return -EBUSY;
+ return 0;
+}
+
+static void mxc_rtc_release(struct device *dev)
+{
+ clear_bit(1, &rtc_status);
+}
+
+static int mxc_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ pr_debug("alarm off\n");
+ CHECK_ERROR(pmic_write_reg(REG_RTC_ALARM, 0x100000, 0x100000));
+ return 0;
+ case RTC_AIE_ON:
+ pr_debug("alarm on\n");
+ CHECK_ERROR(pmic_write_reg(REG_RTC_ALARM, 0, 0x100000));
+ return 0;
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned int tod_reg_val = 0;
+ unsigned int day_reg_val = 0, day_reg_val2;
+ unsigned int mask, value;
+ unsigned long time;
+
+ do {
+ mask = BITFMASK(RTC_DAY);
+ CHECK_ERROR(pmic_read_reg(REG_RTC_DAY, &value, mask));
+ day_reg_val = BITFEXT(value, RTC_DAY);
+
+ mask = BITFMASK(RTC_TIME);
+ CHECK_ERROR(pmic_read_reg(REG_RTC_TIME, &value, mask));
+ tod_reg_val = BITFEXT(value, RTC_TIME);
+
+ mask = BITFMASK(RTC_DAY);
+ CHECK_ERROR(pmic_read_reg(REG_RTC_DAY, &value, mask));
+ day_reg_val2 = BITFEXT(value, RTC_DAY);
+ } while (day_reg_val != day_reg_val2);
+
+ time = (unsigned long)((unsigned long)(tod_reg_val &
+ 0x0001FFFF) +
+ (unsigned long)(day_reg_val * 86400));
+
+ rtc_time_to_tm(time, tm);
+
+ return 0;
+}
+
+static int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned int tod_reg_val = 0;
+ unsigned int day_reg_val, day_reg_val2 = 0;
+ unsigned int mask, value;
+ unsigned long time;
+
+ if (rtc_valid_tm(tm))
+ return -1;
+
+ rtc_tm_to_time(tm, &time);
+
+ tod_reg_val = time % 86400;
+ day_reg_val = time / 86400;
+
+ do {
+ mask = BITFMASK(RTC_DAY);
+ value = BITFVAL(RTC_DAY, day_reg_val);
+ CHECK_ERROR(pmic_write_reg(REG_RTC_DAY, value, mask));
+
+ mask = BITFMASK(RTC_TIME);
+ value = BITFVAL(RTC_TIME, tod_reg_val);
+ CHECK_ERROR(pmic_write_reg(REG_RTC_TIME, value, mask));
+
+ mask = BITFMASK(RTC_DAY);
+ CHECK_ERROR(pmic_read_reg(REG_RTC_DAY, &value, mask));
+ day_reg_val2 = BITFEXT(value, RTC_DAY);
+ } while (day_reg_val != day_reg_val2);
+
+ return 0;
+}
+
+static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ unsigned int tod_reg_val = 0;
+ unsigned int day_reg_val = 0;
+ unsigned int mask, value;
+ unsigned long time;
+
+ mask = BITFMASK(RTCALARM_TIME);
+ CHECK_ERROR(pmic_read_reg(REG_RTC_ALARM, &value, mask));
+ tod_reg_val = BITFEXT(value, RTCALARM_TIME);
+
+ mask = BITFMASK(RTCALARM_DAY);
+ CHECK_ERROR(pmic_read_reg(REG_RTC_DAY_ALARM, &value, mask));
+ day_reg_val = BITFEXT(value, RTCALARM_DAY);
+
+ time = (unsigned long)((unsigned long)(tod_reg_val &
+ 0x0001FFFF) +
+ (unsigned long)(day_reg_val * 86400));
+ rtc_time_to_tm(time, &(alrm->time));
+
+ return 0;
+}
+
+static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ unsigned int tod_reg_val = 0;
+ unsigned int day_reg_val = 0;
+ unsigned int mask, value;
+ unsigned long time;
+
+ if (rtc_valid_tm(&alrm->time))
+ return -1;
+
+ rtc_tm_to_time(&alrm->time, &time);
+
+ tod_reg_val = time % 86400;
+ day_reg_val = time / 86400;
+
+ mask = BITFMASK(RTCALARM_TIME);
+ value = BITFVAL(RTCALARM_TIME, tod_reg_val);
+ CHECK_ERROR(pmic_write_reg(REG_RTC_ALARM, value, mask));
+
+ mask = BITFMASK(RTCALARM_DAY);
+ value = BITFVAL(RTCALARM_DAY, day_reg_val);
+ CHECK_ERROR(pmic_write_reg(REG_RTC_DAY_ALARM, value, mask));
+
+ return 0;
+}
+
+struct rtc_drv_data {
+ struct rtc_device *rtc;
+ pmic_event_callback_t event;
+};
+
+static struct rtc_class_ops mxc_rtc_ops = {
+ .open = mxc_rtc_open,
+ .release = mxc_rtc_release,
+ .ioctl = mxc_rtc_ioctl,
+ .read_time = mxc_rtc_read_time,
+ .set_time = mxc_rtc_set_time,
+ .read_alarm = mxc_rtc_read_alarm,
+ .set_alarm = mxc_rtc_set_alarm,
+};
+
+static void mxc_rtc_alarm_int(void *data)
+{
+ struct rtc_drv_data *pdata = data;
+
+ rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF);
+}
+
+static int mxc_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_drv_data *pdata = NULL;
+
+ printk(KERN_INFO "mc13892 rtc probe start\n");
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->event.func = mxc_rtc_alarm_int;
+ pdata->event.param = pdata;
+ CHECK_ERROR(pmic_event_subscribe(EVENT_TODAI, pdata->event));
+
+ device_init_wakeup(&pdev->dev, 1);
+ pdata->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &mxc_rtc_ops, THIS_MODULE);
+
+ platform_set_drvdata(pdev, pdata);
+ if (IS_ERR(pdata->rtc))
+ return -1;
+
+ printk(KERN_INFO "mc13892 rtc probe succeed\n");
+ return 0;
+}
+
+static int __exit mxc_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(pdata->rtc);
+ CHECK_ERROR(pmic_event_unsubscribe(EVENT_TODAI, pdata->event));
+
+ return 0;
+}
+
+static struct platform_driver mxc_rtc_driver = {
+ .driver = {
+ .name = "pmic_rtc",
+ },
+ .probe = mxc_rtc_probe,
+ .remove = __exit_p(mxc_rtc_remove),
+};
+
+static int __init mxc_rtc_init(void)
+{
+ return platform_driver_register(&mxc_rtc_driver);
+}
+
+static void __exit mxc_rtc_exit(void)
+{
+ platform_driver_unregister(&mxc_rtc_driver);
+
+}
+
+module_init(mxc_rtc_init);
+module_exit(mxc_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MC13892 Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
new file mode 100644
index 000000000000..b6b168d8ac6b
--- /dev/null
+++ b/drivers/rtc/rtc-mxc.c
@@ -0,0 +1,806 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*
+ * Implementation based on rtc-ds1553.c
+ */
+
+/*!
+ * @defgroup RTC Real Time Clock (RTC) Driver
+ */
+/*!
+ * @file rtc-mxc.c
+ * @brief Real Time Clock interface
+ *
+ * This file contains Real Time Clock interface for Linux.
+ *
+ * @ingroup RTC
+ */
+
+#include <linux/rtc.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/uaccess.h>
+
+#include <mach/hardware.h>
+#define RTC_INPUT_CLK_32768HZ (0x00 << 5)
+#define RTC_INPUT_CLK_32000HZ (0x01 << 5)
+#define RTC_INPUT_CLK_38400HZ (0x02 << 5)
+
+#define RTC_SW_BIT (1 << 0)
+#define RTC_ALM_BIT (1 << 2)
+#define RTC_1HZ_BIT (1 << 4)
+#define RTC_2HZ_BIT (1 << 7)
+#define RTC_SAM0_BIT (1 << 8)
+#define RTC_SAM1_BIT (1 << 9)
+#define RTC_SAM2_BIT (1 << 10)
+#define RTC_SAM3_BIT (1 << 11)
+#define RTC_SAM4_BIT (1 << 12)
+#define RTC_SAM5_BIT (1 << 13)
+#define RTC_SAM6_BIT (1 << 14)
+#define RTC_SAM7_BIT (1 << 15)
+#define PIT_ALL_ON (RTC_2HZ_BIT | RTC_SAM0_BIT | RTC_SAM1_BIT | \
+ RTC_SAM2_BIT | RTC_SAM3_BIT | RTC_SAM4_BIT | \
+ RTC_SAM5_BIT | RTC_SAM6_BIT | RTC_SAM7_BIT)
+
+#define RTC_ENABLE_BIT (1 << 7)
+
+#define MAX_PIE_NUM 9
+#define MAX_PIE_FREQ 512
+const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = {
+ {2, RTC_2HZ_BIT},
+ {4, RTC_SAM0_BIT},
+ {8, RTC_SAM1_BIT},
+ {16, RTC_SAM2_BIT},
+ {32, RTC_SAM3_BIT},
+ {64, RTC_SAM4_BIT},
+ {128, RTC_SAM5_BIT},
+ {256, RTC_SAM6_BIT},
+ {MAX_PIE_FREQ, RTC_SAM7_BIT},
+};
+
+/* Those are the bits from a classic RTC we want to mimic */
+#define RTC_IRQF 0x80 /* any of the following 3 is active */
+#define RTC_PF 0x40 /* Periodic interrupt */
+#define RTC_AF 0x20 /* Alarm interrupt */
+#define RTC_UF 0x10 /* Update interrupt for 1Hz RTC */
+
+#define MXC_RTC_TIME 0
+#define MXC_RTC_ALARM 1
+
+#define RTC_HOURMIN 0x00 /* 32bit rtc hour/min counter reg */
+#define RTC_SECOND 0x04 /* 32bit rtc seconds counter reg */
+#define RTC_ALRM_HM 0x08 /* 32bit rtc alarm hour/min reg */
+#define RTC_ALRM_SEC 0x0C /* 32bit rtc alarm seconds reg */
+#define RTC_RTCCTL 0x10 /* 32bit rtc control reg */
+#define RTC_RTCISR 0x14 /* 32bit rtc interrupt status reg */
+#define RTC_RTCIENR 0x18 /* 32bit rtc interrupt enable reg */
+#define RTC_STPWCH 0x1C /* 32bit rtc stopwatch min reg */
+#define RTC_DAYR 0x20 /* 32bit rtc days counter reg */
+#define RTC_DAYALARM 0x24 /* 32bit rtc day alarm reg */
+#define RTC_TEST1 0x28 /* 32bit rtc test reg 1 */
+#define RTC_TEST2 0x2C /* 32bit rtc test reg 2 */
+#define RTC_TEST3 0x30 /* 32bit rtc test reg 3 */
+
+struct rtc_plat_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ unsigned long baseaddr;
+ int irq;
+ struct clk *clk;
+ unsigned int irqen;
+ int alrm_sec;
+ int alrm_min;
+ int alrm_hour;
+ int alrm_mday;
+};
+
+/*!
+ * @defgroup RTC Real Time Clock (RTC) Driver
+ */
+/*!
+ * @file rtc-mxc.c
+ * @brief Real Time Clock interface
+ *
+ * This file contains Real Time Clock interface for Linux.
+ *
+ * @ingroup RTC
+ */
+
+#if defined(CONFIG_MXC_PMIC_SC55112_RTC) || defined(CONFIG_MXC_MC13783_RTC) ||\
+ defined(CONFIG_MXC_MC9SDZ60_RTC)
+#include <linux/pmic_rtc.h>
+#else
+#define pmic_rtc_get_time(args) MXC_EXTERNAL_RTC_NONE
+#define pmic_rtc_set_time(args) MXC_EXTERNAL_RTC_NONE
+#define pmic_rtc_loaded() 0
+#endif
+
+#define RTC_VERSION "1.0"
+#define MXC_EXTERNAL_RTC_OK 0
+#define MXC_EXTERNAL_RTC_ERR -1
+#define MXC_EXTERNAL_RTC_NONE -2
+
+/*!
+ * This function reads the RTC value from some external source.
+ *
+ * @param second pointer to the returned value in second
+ *
+ * @return 0 if successful; non-zero otherwise
+ */
+int get_ext_rtc_time(u32 * second)
+{
+ int ret = 0;
+ struct timeval tmp;
+ if (!pmic_rtc_loaded()) {
+ return MXC_EXTERNAL_RTC_NONE;
+ }
+
+ ret = pmic_rtc_get_time(&tmp);
+
+ if (0 == ret)
+ *second = tmp.tv_sec;
+ else
+ ret = MXC_EXTERNAL_RTC_ERR;
+
+ return ret;
+}
+
+/*!
+ * This function sets external RTC
+ *
+ * @param second value in second to be set to external RTC
+ *
+ * @return 0 if successful; non-zero otherwise
+ */
+int set_ext_rtc_time(u32 second)
+{
+ int ret = 0;
+ struct timeval tmp;
+
+ if (!pmic_rtc_loaded()) {
+ return MXC_EXTERNAL_RTC_NONE;
+ }
+
+ tmp.tv_sec = second;
+
+ ret = pmic_rtc_set_time(&tmp);
+
+ if (0 != ret)
+ ret = MXC_EXTERNAL_RTC_ERR;
+
+ return ret;
+}
+
+static u32 rtc_freq = 2; /* minimun value for PIE */
+static unsigned long rtc_status;
+
+static struct rtc_time g_rtc_alarm = {
+ .tm_year = 0,
+ .tm_mon = 0,
+ .tm_mday = 0,
+ .tm_hour = 0,
+ .tm_mon = 0,
+ .tm_sec = 0,
+};
+
+static DEFINE_SPINLOCK(rtc_lock);
+
+/*!
+ * This function is used to obtain the RTC time or the alarm value in
+ * second.
+ *
+ * @param time_alarm use MXC_RTC_TIME for RTC time value; MXC_RTC_ALARM for alarm value
+ *
+ * @return The RTC time or alarm time in second.
+ */
+static u32 get_alarm_or_time(struct device *dev, int time_alarm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 day, hr, min, sec, hr_min;
+ if (time_alarm == MXC_RTC_TIME) {
+ day = readw(ioaddr + RTC_DAYR);
+ hr_min = readw(ioaddr + RTC_HOURMIN);
+ sec = readw(ioaddr + RTC_SECOND);
+ } else if (time_alarm == MXC_RTC_ALARM) {
+ day = readw(ioaddr + RTC_DAYALARM);
+ hr_min = (0x0000FFFF) & readw(ioaddr + RTC_ALRM_HM);
+ sec = readw(ioaddr + RTC_ALRM_SEC);
+ } else {
+ panic("wrong value for time_alarm=%d\n", time_alarm);
+ }
+
+ hr = hr_min >> 8;
+ min = hr_min & 0x00FF;
+
+ return ((((day * 24 + hr) * 60) + min) * 60 + sec);
+}
+
+/*!
+ * This function sets the RTC alarm value or the time value.
+ *
+ * @param time_alarm the new alarm value to be updated in the RTC
+ * @param time use MXC_RTC_TIME for RTC time value; MXC_RTC_ALARM for alarm value
+ */
+static void set_alarm_or_time(struct device *dev, int time_alarm, u32 time)
+{
+ u32 day, hr, min, sec, temp;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ day = time / 86400;
+ time -= day * 86400;
+ /* time is within a day now */
+ hr = time / 3600;
+ time -= hr * 3600;
+ /* time is within an hour now */
+ min = time / 60;
+ sec = time - min * 60;
+
+ temp = (hr << 8) + min;
+
+ if (time_alarm == MXC_RTC_TIME) {
+ writew(day, ioaddr + RTC_DAYR);
+ writew(sec, ioaddr + RTC_SECOND);
+ writew(temp, ioaddr + RTC_HOURMIN);
+ } else if (time_alarm == MXC_RTC_ALARM) {
+ writew(day, ioaddr + RTC_DAYALARM);
+ writew(sec, ioaddr + RTC_ALRM_SEC);
+ writew(temp, ioaddr + RTC_ALRM_HM);
+ } else {
+ panic("wrong value for time_alarm=%d\n", time_alarm);
+ }
+}
+
+/*!
+ * This function updates the RTC alarm registers and then clears all the
+ * interrupt status bits.
+ *
+ * @param alrm the new alarm value to be updated in the RTC
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int rtc_update_alarm(struct device *dev, struct rtc_time *alrm)
+{
+ struct rtc_time alarm_tm, now_tm;
+ unsigned long now, time;
+ int ret;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ now = get_alarm_or_time(dev, MXC_RTC_TIME);
+ rtc_time_to_tm(now, &now_tm);
+ alarm_tm.tm_year = now_tm.tm_year;
+ alarm_tm.tm_mon = now_tm.tm_mon;
+ alarm_tm.tm_mday = now_tm.tm_mday;
+ alarm_tm.tm_hour = alrm->tm_hour;
+ alarm_tm.tm_min = alrm->tm_min;
+ alarm_tm.tm_sec = alrm->tm_sec;
+ rtc_tm_to_time(&now_tm, &now);
+ rtc_tm_to_time(&alarm_tm, &time);
+ if (time < now) {
+ time += 60 * 60 * 24;
+ rtc_time_to_tm(time, &alarm_tm);
+ }
+ ret = rtc_tm_to_time(&alarm_tm, &time);
+
+ /* clear all the interrupt status bits */
+ writew(readw(ioaddr + RTC_RTCISR), ioaddr + RTC_RTCISR);
+
+ set_alarm_or_time(dev, MXC_RTC_ALARM, time);
+
+ return ret;
+}
+
+/*!
+ * This function is the RTC interrupt service routine.
+ *
+ * @param irq RTC IRQ number
+ * @param dev_id device ID which is not used
+ *
+ * @return IRQ_HANDLED as defined in the include/linux/interrupt.h file.
+ */
+static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 status;
+ u32 events = 0;
+ spin_lock(&rtc_lock);
+ status = readw(ioaddr + RTC_RTCISR) & readw(ioaddr + RTC_RTCIENR);
+ /* clear interrupt sources */
+ writew(status, ioaddr + RTC_RTCISR);
+
+ /* clear alarm interrupt if it has occurred */
+ if (status & RTC_ALM_BIT) {
+ status &= ~RTC_ALM_BIT;
+ }
+
+ /* update irq data & counter */
+ if (status & RTC_ALM_BIT) {
+ events |= (RTC_AF | RTC_IRQF);
+ }
+ if (status & RTC_1HZ_BIT) {
+ events |= (RTC_UF | RTC_IRQF);
+ }
+ if (status & PIT_ALL_ON) {
+ events |= (RTC_PF | RTC_IRQF);
+ }
+
+ if ((status & RTC_ALM_BIT) && rtc_valid_tm(&g_rtc_alarm)) {
+ rtc_update_alarm(&pdev->dev, &g_rtc_alarm);
+ }
+
+ spin_unlock(&rtc_lock);
+ rtc_update_irq(pdata->rtc, 1, events);
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function is used to open the RTC driver by registering the RTC
+ * interrupt service routine.
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int mxc_rtc_open(struct device *dev)
+{
+ if (test_and_set_bit(1, &rtc_status))
+ return -EBUSY;
+ return 0;
+}
+
+/*!
+ * clear all interrupts and release the IRQ
+ */
+static void mxc_rtc_release(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ spin_lock_irq(&rtc_lock);
+ writew(0, ioaddr + RTC_RTCIENR); /* Disable all rtc interrupts */
+ writew(0xFFFFFFFF, ioaddr + RTC_RTCISR); /* Clear all interrupt status */
+ spin_unlock_irq(&rtc_lock);
+ rtc_status = 0;
+}
+
+/*!
+ * This function is used to support some ioctl calls directly.
+ * Other ioctl calls are supported indirectly through the
+ * arm/common/rtctime.c file.
+ *
+ * @param cmd ioctl command as defined in include/linux/rtc.h
+ * @param arg value for the ioctl command
+ *
+ * @return 0 if successful or negative value otherwise.
+ */
+static int mxc_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ int i;
+ switch (cmd) {
+ case RTC_PIE_OFF:
+ writew((readw(ioaddr + RTC_RTCIENR) & ~PIT_ALL_ON),
+ ioaddr + RTC_RTCIENR);
+ return 0;
+ case RTC_IRQP_SET:
+ if (arg < 2 || arg > MAX_PIE_FREQ || (arg % 2) != 0)
+ return -EINVAL; /* Also make sure a power of 2Hz */
+ if ((arg > 64) && (!capable(CAP_SYS_RESOURCE)))
+ return -EACCES;
+ rtc_freq = arg;
+ return 0;
+ case RTC_IRQP_READ:
+ return put_user(rtc_freq, (u32 *) arg);
+ case RTC_PIE_ON:
+ for (i = 0; i < MAX_PIE_NUM; i++) {
+ if (PIE_BIT_DEF[i][0] == rtc_freq) {
+ break;
+ }
+ }
+ if (i == MAX_PIE_NUM) {
+ return -EACCES;
+ }
+ spin_lock_irq(&rtc_lock);
+ writew((readw(ioaddr + RTC_RTCIENR) | PIE_BIT_DEF[i][1]),
+ ioaddr + RTC_RTCIENR);
+ spin_unlock_irq(&rtc_lock);
+ return 0;
+ case RTC_AIE_OFF:
+ spin_lock_irq(&rtc_lock);
+ writew((readw(ioaddr + RTC_RTCIENR) & ~RTC_ALM_BIT),
+ ioaddr + RTC_RTCIENR);
+ spin_unlock_irq(&rtc_lock);
+ return 0;
+
+ case RTC_AIE_ON:
+ spin_lock_irq(&rtc_lock);
+ writew((readw(ioaddr + RTC_RTCIENR) | RTC_ALM_BIT),
+ ioaddr + RTC_RTCIENR);
+ spin_unlock_irq(&rtc_lock);
+ return 0;
+
+ case RTC_UIE_OFF: /* UIE is for the 1Hz interrupt */
+ spin_lock_irq(&rtc_lock);
+ writew((readw(ioaddr + RTC_RTCIENR) & ~RTC_1HZ_BIT),
+ ioaddr + RTC_RTCIENR);
+ spin_unlock_irq(&rtc_lock);
+ return 0;
+
+ case RTC_UIE_ON:
+ spin_lock_irq(&rtc_lock);
+ writew((readw(ioaddr + RTC_RTCIENR) | RTC_1HZ_BIT),
+ ioaddr + RTC_RTCIENR);
+ spin_unlock_irq(&rtc_lock);
+ return 0;
+ }
+ return -ENOIOCTLCMD;
+}
+
+/*!
+ * This function reads the current RTC time into tm in Gregorian date.
+ *
+ * @param tm contains the RTC time value upon return
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ u32 val;
+
+ /* Avoid roll-over from reading the different registers */
+ do {
+ val = get_alarm_or_time(dev, MXC_RTC_TIME);
+ } while (val != get_alarm_or_time(dev, MXC_RTC_TIME));
+
+ rtc_time_to_tm(val, tm);
+ return 0;
+}
+
+/*!
+ * This function sets the internal RTC time based on tm in Gregorian date.
+ *
+ * @param tm the time value to be set in the RTC
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long time;
+ int ret;
+ ret = rtc_tm_to_time(tm, &time);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Avoid roll-over from reading the different registers */
+ do {
+ set_alarm_or_time(dev, MXC_RTC_TIME, time);
+ } while (time != get_alarm_or_time(dev, MXC_RTC_TIME));
+
+ ret = set_ext_rtc_time(time);
+
+ if (ret != MXC_EXTERNAL_RTC_OK) {
+ if (ret == MXC_EXTERNAL_RTC_NONE) {
+ pr_info("No external RTC\n");
+ ret = 0;
+ } else
+ pr_info("Failed to set external RTC\n");
+ }
+
+ return ret;
+}
+
+/*!
+ * This function reads the current alarm value into the passed in \b alrm
+ * argument. It updates the \b alrm's pending field value based on the whether
+ * an alarm interrupt occurs or not.
+ *
+ * @param alrm contains the RTC alarm value upon return
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ rtc_time_to_tm(get_alarm_or_time(dev, MXC_RTC_ALARM), &alrm->time);
+ alrm->pending =
+ ((readw(ioaddr + RTC_RTCISR) & RTC_ALM_BIT) != 0) ? 1 : 0;
+
+ return 0;
+}
+
+/*!
+ * This function sets the RTC alarm based on passed in alrm.
+ *
+ * @param alrm the alarm value to be set in the RTC
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ int ret;
+
+ spin_lock_irq(&rtc_lock);
+ if (rtc_valid_tm(&alrm->time)) {
+ if (alrm->time.tm_sec > 59 ||
+ alrm->time.tm_hour > 23 || alrm->time.tm_min > 59) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = rtc_update_alarm(dev, &alrm->time);
+ } else {
+ if ((ret = rtc_valid_tm(&alrm->time)))
+ goto out;
+ ret = rtc_update_alarm(dev, &alrm->time);
+ }
+
+ if (ret == 0) {
+ memcpy(&g_rtc_alarm, &alrm->time, sizeof(struct rtc_time));
+
+ if (alrm->enabled) {
+ writew((readw(ioaddr + RTC_RTCIENR) | RTC_ALM_BIT),
+ ioaddr + RTC_RTCIENR);
+ } else {
+ writew((readw(ioaddr + RTC_RTCIENR) & ~RTC_ALM_BIT),
+ ioaddr + RTC_RTCIENR);
+ }
+ }
+ out:
+ spin_unlock_irq(&rtc_lock);
+
+ return ret;
+}
+
+/*!
+ * This function is used to provide the content for the /proc/driver/rtc
+ * file.
+ *
+ * @param buf the buffer to hold the information that the driver wants to write
+ *
+ * @return The number of bytes written into the rtc file.
+ */
+static int mxc_rtc_proc(struct device *dev, struct seq_file *sq)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ char *p = sq->buf;
+
+ p += sprintf(p, "alarm_IRQ\t: %s\n",
+ (((readw(ioaddr + RTC_RTCIENR)) & RTC_ALM_BIT) !=
+ 0) ? "yes" : "no");
+ p += sprintf(p, "update_IRQ\t: %s\n",
+ (((readw(ioaddr + RTC_RTCIENR)) & RTC_1HZ_BIT) !=
+ 0) ? "yes" : "no");
+ p += sprintf(p, "periodic_IRQ\t: %s\n",
+ (((readw(ioaddr + RTC_RTCIENR)) & PIT_ALL_ON) !=
+ 0) ? "yes" : "no");
+ p += sprintf(p, "periodic_freq\t: %d\n", rtc_freq);
+
+ return p - (sq->buf);
+}
+
+/*!
+ * The RTC driver structure
+ */
+static struct rtc_class_ops mxc_rtc_ops = {
+ .open = mxc_rtc_open,
+ .release = mxc_rtc_release,
+ .ioctl = mxc_rtc_ioctl,
+ .read_time = mxc_rtc_read_time,
+ .set_time = mxc_rtc_set_time,
+ .read_alarm = mxc_rtc_read_alarm,
+ .set_alarm = mxc_rtc_set_alarm,
+ .proc = mxc_rtc_proc,
+};
+
+/*! MXC RTC Power management control */
+
+static struct timespec mxc_rtc_delta;
+
+static int mxc_rtc_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+ struct timespec tv;
+ struct resource *res;
+ struct rtc_device *rtc;
+ struct rtc_plat_data *pdata = NULL;
+ u32 reg;
+ int ret = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->clk = clk_get(&pdev->dev, "rtc_clk");
+ clk_enable(pdata->clk);
+
+ pdata->baseaddr = res->start;
+ pdata->ioaddr = ((void *)(IO_ADDRESS(pdata->baseaddr)));
+ /* Configure and enable the RTC */
+ pdata->irq = platform_get_irq(pdev, 0);
+ if (pdata->irq >= 0) {
+ if (request_irq(pdata->irq, mxc_rtc_interrupt, IRQF_SHARED,
+ pdev->name, pdev) < 0) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = -1;
+ }
+ }
+ rtc =
+ rtc_device_register(pdev->name, &pdev->dev, &mxc_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ if (pdata->irq >= 0)
+ free_irq(pdata->irq, pdev);
+ kfree(pdata);
+ return ret;
+ }
+ pdata->rtc = rtc;
+ platform_set_drvdata(pdev, pdata);
+ tv.tv_nsec = 0;
+ tv.tv_sec = get_alarm_or_time(&pdev->dev, MXC_RTC_TIME);
+ clk = clk_get(NULL, "ckil");
+ if (clk_get_rate(clk) == 32768)
+ reg = RTC_INPUT_CLK_32768HZ;
+ else if (clk_get_rate(clk) == 32000)
+ reg = RTC_INPUT_CLK_32000HZ;
+ else if (clk_get_rate(clk) == 38400)
+ reg = RTC_INPUT_CLK_38400HZ;
+ else {
+ printk(KERN_ALERT "rtc clock is not valid");
+ return -EINVAL;
+ }
+ clk_put(clk);
+ reg |= RTC_ENABLE_BIT;
+ writew(reg, (pdata->ioaddr + RTC_RTCCTL));
+ if (((readw(pdata->ioaddr + RTC_RTCCTL)) & RTC_ENABLE_BIT) == 0) {
+ printk(KERN_ALERT "rtc : hardware module can't be enabled!\n");
+ return -EPERM;
+ }
+ printk("Real TIme clock Driver v%s \n", RTC_VERSION);
+ return ret;
+}
+
+static int __exit mxc_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ rtc_device_unregister(pdata->rtc);
+ if (pdata->irq >= 0) {
+ free_irq(pdata->irq, pdev);
+ }
+ clk_disable(pdata->clk);
+ clk_put(pdata->clk);
+ kfree(pdata);
+ mxc_rtc_release(NULL);
+ return 0;
+}
+
+/*!
+ * This function is called to save the system time delta relative to
+ * the MXC RTC when enterring a low power state. This time delta is
+ * then used on resume to adjust the system time to account for time
+ * loss while suspended.
+ *
+ * @param pdev not used
+ * @param state Power state to enter.
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct timespec tv;
+
+ /* calculate time delta for suspend */
+ /* RTC precision is 1 second; adjust delta for avg 1/2 sec err */
+ tv.tv_nsec = NSEC_PER_SEC >> 1;
+ tv.tv_sec = get_alarm_or_time(&pdev->dev, MXC_RTC_TIME);
+ set_normalized_timespec(&mxc_rtc_delta,
+ xtime.tv_sec - tv.tv_sec,
+ xtime.tv_nsec - tv.tv_nsec);
+
+ return 0;
+}
+
+/*!
+ * This function is called to correct the system time based on the
+ * current MXC RTC time relative to the time delta saved during
+ * suspend.
+ *
+ * @param pdev not used
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_rtc_resume(struct platform_device *pdev)
+{
+ struct timespec tv;
+ struct timespec ts;
+
+ tv.tv_nsec = 0;
+ tv.tv_sec = get_alarm_or_time(&pdev->dev, MXC_RTC_TIME);
+
+ /* restore wall clock using delta against this RTC;
+ * adjust again for avg 1/2 second RTC sampling error
+ */
+ set_normalized_timespec(&ts,
+ tv.tv_sec + mxc_rtc_delta.tv_sec,
+ (NSEC_PER_SEC >> 1) + mxc_rtc_delta.tv_nsec);
+ do_settimeofday(&ts);
+
+ return 0;
+}
+
+/*!
+ * Contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_rtc_driver = {
+ .driver = {
+ .name = "mxc_rtc",
+ },
+ .probe = mxc_rtc_probe,
+ .remove = __exit_p(mxc_rtc_remove),
+ .suspend = mxc_rtc_suspend,
+ .resume = mxc_rtc_resume,
+};
+
+/*!
+ * This function creates the /proc/driver/rtc file and registers the device RTC
+ * in the /dev/misc directory. It also reads the RTC value from external source
+ * and setup the internal RTC properly.
+ *
+ * @return -1 if RTC is failed to initialize; 0 is successful.
+ */
+static int __init mxc_rtc_init(void)
+{
+ return platform_driver_register(&mxc_rtc_driver);
+}
+
+/*!
+ * This function removes the /proc/driver/rtc file and un-registers the
+ * device RTC from the /dev/misc directory.
+ */
+static void __exit mxc_rtc_exit(void)
+{
+ platform_driver_unregister(&mxc_rtc_driver);
+
+}
+
+device_initcall_sync(mxc_rtc_init);
+module_exit(mxc_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-mxc_v2.c b/drivers/rtc/rtc-mxc_v2.c
new file mode 100644
index 000000000000..2e886cf2715c
--- /dev/null
+++ b/drivers/rtc/rtc-mxc_v2.c
@@ -0,0 +1,719 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*
+ * Implementation based on rtc-ds1553.c
+ */
+
+/*!
+ * @defgroup RTC Real Time Clock (RTC) Driver
+ */
+/*!
+ * @file rtc-mxc_v2.c
+ * @brief Real Time Clock interface
+ *
+ * This file contains Real Time Clock interface for Linux.
+ *
+ * @ingroup RTC
+ */
+
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/uaccess.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+
+#define SRTC_LPPDR_INIT 0x41736166 /* init for glitch detect */
+
+#define SRTC_LPCR_SWR_LP (1 << 0) /* lp software reset */
+#define SRTC_LPCR_EN_LP (1 << 3) /* lp enable */
+#define SRTC_LPCR_WAE (1 << 4) /* lp wakeup alarm enable */
+#define SRTC_LPCR_SAE (1 << 5) /* lp security alarm enable */
+#define SRTC_LPCR_SI (1 << 6) /* lp security interrupt enable */
+#define SRTC_LPCR_ALP (1 << 7) /* lp alarm flag */
+#define SRTC_LPCR_LTC (1 << 8) /* lp lock time counter */
+#define SRTC_LPCR_LMC (1 << 9) /* lp lock monotonic counter */
+#define SRTC_LPCR_SV (1 << 10) /* lp security violation */
+#define SRTC_LPCR_NSA (1 << 11) /* lp non secure access */
+#define SRTC_LPCR_NVEIE (1 << 12) /* lp non valid state exit int en */
+#define SRTC_LPCR_IEIE (1 << 13) /* lp init state exit int enable */
+#define SRTC_LPCR_NVE (1 << 14) /* lp non valid state exit bit */
+#define SRTC_LPCR_IE (1 << 15) /* lp init state exit bit */
+
+#define SRTC_LPCR_ALL_INT_EN (SRTC_LPCR_WAE | SRTC_LPCR_SAE | \
+ SRTC_LPCR_SI | SRTC_LPCR_ALP | \
+ SRTC_LPCR_NVEIE | SRTC_LPCR_IEIE)
+
+#define SRTC_LPSR_TRI (1 << 0) /* lp time read invalidate */
+#define SRTC_LPSR_PGD (1 << 1) /* lp power supply glitc detected */
+#define SRTC_LPSR_CTD (1 << 2) /* lp clock tampering detected */
+#define SRTC_LPSR_ALP (1 << 3) /* lp alarm flag */
+#define SRTC_LPSR_MR (1 << 4) /* lp monotonic counter rollover */
+#define SRTC_LPSR_TR (1 << 5) /* lp time rollover */
+#define SRTC_LPSR_EAD (1 << 6) /* lp external alarm detected */
+#define SRTC_LPSR_IT0 (1 << 7) /* lp IIM throttle */
+#define SRTC_LPSR_IT1 (1 << 8)
+#define SRTC_LPSR_IT2 (1 << 9)
+#define SRTC_LPSR_SM0 (1 << 10) /* lp security mode */
+#define SRTC_LPSR_SM1 (1 << 11)
+#define SRTC_LPSR_STATE_LP0 (1 << 12) /* lp state */
+#define SRTC_LPSR_STATE_LP1 (1 << 13)
+#define SRTC_LPSR_NVES (1 << 14) /* lp non-valid state exit status */
+#define SRTC_LPSR_IES (1 << 15) /* lp init state exit status */
+
+#define MAX_PIE_NUM 15
+#define MAX_PIE_FREQ 32768
+#define MIN_PIE_FREQ 1
+
+#define SRTC_PI0 (1 << 0)
+#define SRTC_PI1 (1 << 1)
+#define SRTC_PI2 (1 << 2)
+#define SRTC_PI3 (1 << 3)
+#define SRTC_PI4 (1 << 4)
+#define SRTC_PI5 (1 << 5)
+#define SRTC_PI6 (1 << 6)
+#define SRTC_PI7 (1 << 7)
+#define SRTC_PI8 (1 << 8)
+#define SRTC_PI9 (1 << 9)
+#define SRTC_PI10 (1 << 10)
+#define SRTC_PI11 (1 << 11)
+#define SRTC_PI12 (1 << 12)
+#define SRTC_PI13 (1 << 13)
+#define SRTC_PI14 (1 << 14)
+#define SRTC_PI15 (1 << 15)
+
+#define PIT_ALL_ON (SRTC_PI1 | SRTC_PI2 | SRTC_PI3 | \
+ SRTC_PI4 | SRTC_PI5 | SRTC_PI6 | SRTC_PI7 | \
+ SRTC_PI8 | SRTC_PI9 | SRTC_PI10 | SRTC_PI11 | \
+ SRTC_PI12 | SRTC_PI13 | SRTC_PI14 | SRTC_PI15)
+
+#define SRTC_SWR_HP (1 << 0) /* hp software reset */
+#define SRTC_EN_HP (1 << 3) /* hp enable */
+#define SRTC_TS (1 << 4) /* time syncronize hp with lp */
+
+#define SRTC_IE_AHP (1 << 16) /* Alarm HP Interrupt Enable bit */
+#define SRTC_IE_WDHP (1 << 18) /* Write Done HP Interrupt Enable bit */
+#define SRTC_IE_WDLP (1 << 19) /* Write Done LP Interrupt Enable bit */
+
+#define SRTC_ISR_AHP (1 << 16) /* interrupt status: alarm hp */
+#define SRTC_ISR_WDHP (1 << 18) /* interrupt status: write done hp */
+#define SRTC_ISR_WDLP (1 << 19) /* interrupt status: write done lp */
+#define SRTC_ISR_WPHP (1 << 20) /* interrupt status: write pending hp */
+#define SRTC_ISR_WPLP (1 << 21) /* interrupt status: write pending lp */
+
+#define SRTC_LPSCMR 0x00 /* LP Secure Counter MSB Reg */
+#define SRTC_LPSCLR 0x04 /* LP Secure Counter LSB Reg */
+#define SRTC_LPSAR 0x08 /* LP Secure Alarm Reg */
+#define SRTC_LPSMCR 0x0C /* LP Secure Monotonic Counter Reg */
+#define SRTC_LPCR 0x10 /* LP Control Reg */
+#define SRTC_LPSR 0x14 /* LP Status Reg */
+#define SRTC_LPPDR 0x18 /* LP Power Supply Glitch Detector Reg */
+#define SRTC_LPGR 0x1C /* LP General Purpose Reg */
+#define SRTC_HPCMR 0x20 /* HP Counter MSB Reg */
+#define SRTC_HPCLR 0x24 /* HP Counter LSB Reg */
+#define SRTC_HPAMR 0x28 /* HP Alarm MSB Reg */
+#define SRTC_HPALR 0x2C /* HP Alarm LSB Reg */
+#define SRTC_HPCR 0x30 /* HP Control Reg */
+#define SRTC_HPISR 0x34 /* HP Interrupt Status Reg */
+#define SRTC_HPIENR 0x38 /* HP Interrupt Enable Reg */
+
+#define SRTC_SECMODE_MASK 0x3 /* the mask of SRTC security mode */
+#define SRTC_SECMODE_LOW 0x0 /* Low Security */
+#define SRTC_SECMODE_MED 0x1 /* Medium Security */
+#define SRTC_SECMODE_HIGH 0x2 /* High Security */
+#define SRTC_SECMODE_RESERVED 0x3 /* Reserved */
+
+struct rtc_drv_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ unsigned long baseaddr;
+ int irq;
+ struct clk *clk;
+ bool irq_enable;
+};
+
+/*!
+ * @defgroup RTC Real Time Clock (RTC) Driver
+ */
+/*!
+ * @file rtc-mxc.c
+ * @brief Real Time Clock interface
+ *
+ * This file contains Real Time Clock interface for Linux.
+ *
+ * @ingroup RTC
+ */
+
+static unsigned long rtc_status;
+
+static DEFINE_SPINLOCK(rtc_lock);
+
+/*!
+ * This function does write synchronization for writes to the lp srtc block.
+ * To take care of the asynchronous CKIL clock, all writes from the IP domain
+ * will be synchronized to the CKIL domain.
+ */
+static inline void rtc_write_sync_lp(void __iomem *ioaddr)
+{
+ unsigned int i, count;
+ /* Wait for 3 CKIL cycles */
+ for (i = 0; i < 3; i++) {
+ count = __raw_readl(ioaddr + SRTC_LPSCLR);
+ while
+ ((__raw_readl(ioaddr + SRTC_LPSCLR)) == count);
+ }
+}
+
+/*!
+ * This function updates the RTC alarm registers and then clears all the
+ * interrupt status bits.
+ *
+ * @param alrm the new alarm value to be updated in the RTC
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int rtc_update_alarm(struct device *dev, struct rtc_time *alrm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ struct rtc_time alarm_tm, now_tm;
+ unsigned long now, time;
+ int ret;
+
+ now = __raw_readl(ioaddr + SRTC_LPSCMR);
+ rtc_time_to_tm(now, &now_tm);
+
+ alarm_tm.tm_year = now_tm.tm_year;
+ alarm_tm.tm_mon = now_tm.tm_mon;
+ alarm_tm.tm_mday = now_tm.tm_mday;
+
+ alarm_tm.tm_hour = alrm->tm_hour;
+ alarm_tm.tm_min = alrm->tm_min;
+ alarm_tm.tm_sec = alrm->tm_sec;
+
+ rtc_tm_to_time(&now_tm, &now);
+ rtc_tm_to_time(&alarm_tm, &time);
+
+ if (time < now) {
+ time += 60 * 60 * 24;
+ rtc_time_to_tm(time, &alarm_tm);
+ }
+ ret = rtc_tm_to_time(&alarm_tm, &time);
+
+ __raw_writel(time, ioaddr + SRTC_LPSAR);
+
+ /* clear alarm interrupt status bit */
+ __raw_writel(SRTC_LPSR_ALP, ioaddr + SRTC_LPSR);
+
+ return ret;
+}
+
+/*!
+ * This function is the RTC interrupt service routine.
+ *
+ * @param irq RTC IRQ number
+ * @param dev_id device ID which is not used
+ *
+ * @return IRQ_HANDLED as defined in the include/linux/interrupt.h file.
+ */
+static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 lp_status, lp_cr;
+ u32 events = 0;
+
+ clk_enable(pdata->clk);
+ lp_status = __raw_readl(ioaddr + SRTC_LPSR);
+ lp_cr = __raw_readl(ioaddr + SRTC_LPCR);
+
+ /* update irq data & counter */
+ if (lp_status & SRTC_LPSR_ALP) {
+ if (lp_cr & SRTC_LPCR_ALP)
+ events |= (RTC_AF | RTC_IRQF);
+
+ /* disable further lp alarm interrupts */
+ lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE);
+ }
+
+ /* Update interrupt enables */
+ __raw_writel(lp_cr, ioaddr + SRTC_LPCR);
+
+ /* If no interrupts are enabled, turn off interrupts in kernel */
+ if (((lp_cr & SRTC_LPCR_ALL_INT_EN) == 0) && (pdata->irq_enable)) {
+ disable_irq(pdata->irq);
+ pdata->irq_enable = false;
+ }
+
+ /* clear interrupt status */
+ __raw_writel(lp_status, ioaddr + SRTC_LPSR);
+ clk_disable(pdata->clk);
+
+ rtc_update_irq(pdata->rtc, 1, events);
+ return IRQ_HANDLED;
+}
+
+/*!
+ * This function is used to open the RTC driver.
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int mxc_rtc_open(struct device *dev)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ clk_enable(pdata->clk);
+
+ if (test_and_set_bit(1, &rtc_status))
+ return -EBUSY;
+ return 0;
+}
+
+/*!
+ * clear all interrupts and release the IRQ
+ */
+static void mxc_rtc_release(struct device *dev)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+
+ clk_disable(pdata->clk);
+
+ rtc_status = 0;
+}
+
+/*!
+ * This function is used to support some ioctl calls directly.
+ * Other ioctl calls are supported indirectly through the
+ * arm/common/rtctime.c file.
+ *
+ * @param cmd ioctl command as defined in include/linux/rtc.h
+ * @param arg value for the ioctl command
+ *
+ * @return 0 if successful or negative value otherwise.
+ */
+static int mxc_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long lock_flags = 0;
+ u32 lp_cr;
+
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ spin_lock_irqsave(&rtc_lock, lock_flags);
+ lp_cr = __raw_readl(ioaddr + SRTC_LPCR);
+ lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE);
+ if (((lp_cr & SRTC_LPCR_ALL_INT_EN) == 0)
+ && (pdata->irq_enable)) {
+ disable_irq(pdata->irq);
+ pdata->irq_enable = false;
+ }
+ __raw_writel(lp_cr, ioaddr + SRTC_LPCR);
+ spin_unlock_irqrestore(&rtc_lock, lock_flags);
+ return 0;
+
+ case RTC_AIE_ON:
+ spin_lock_irqsave(&rtc_lock, lock_flags);
+ if (!pdata->irq_enable) {
+ enable_irq(pdata->irq);
+ pdata->irq_enable = true;
+ }
+ lp_cr = __raw_readl(ioaddr + SRTC_LPCR);
+ lp_cr |= SRTC_LPCR_ALP | SRTC_LPCR_WAE;
+ __raw_writel(lp_cr, ioaddr + SRTC_LPCR);
+ spin_unlock_irqrestore(&rtc_lock, lock_flags);
+ return 0;
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+/*!
+ * This function reads the current RTC time into tm in Gregorian date.
+ *
+ * @param tm contains the RTC time value upon return
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ rtc_time_to_tm(__raw_readl(ioaddr + SRTC_LPSCMR), tm);
+ return 0;
+}
+
+/*!
+ * This function sets the internal RTC time based on tm in Gregorian date.
+ *
+ * @param tm the time value to be set in the RTC
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long time;
+ int ret;
+ ret = rtc_tm_to_time(tm, &time);
+ if (ret != 0)
+ return ret;
+
+ __raw_writel(time, ioaddr + SRTC_LPSCMR);
+ rtc_write_sync_lp(ioaddr);
+
+ return 0;
+}
+
+/*!
+ * This function reads the current alarm value into the passed in \b alrm
+ * argument. It updates the \b alrm's pending field value based on the whether
+ * an alarm interrupt occurs or not.
+ *
+ * @param alrm contains the RTC alarm value upon return
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ rtc_time_to_tm(__raw_readl(ioaddr + SRTC_LPSAR), &alrm->time);
+ alrm->pending =
+ ((__raw_readl(ioaddr + SRTC_LPSR) & SRTC_LPSR_ALP) != 0) ? 1 : 0;
+
+ return 0;
+}
+
+/*!
+ * This function sets the RTC alarm based on passed in alrm.
+ *
+ * @param alrm the alarm value to be set in the RTC
+ *
+ * @return 0 if successful; non-zero otherwise.
+ */
+static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long lock_flags = 0;
+ u32 lp_cr;
+ int ret;
+
+ if (rtc_valid_tm(&alrm->time)) {
+ if (alrm->time.tm_sec > 59 ||
+ alrm->time.tm_hour > 23 || alrm->time.tm_min > 59) {
+ return -EINVAL;
+ }
+ }
+
+ spin_lock_irqsave(&rtc_lock, lock_flags);
+ lp_cr = __raw_readl(ioaddr + SRTC_LPCR);
+
+ ret = rtc_update_alarm(dev, &alrm->time);
+ if (ret)
+ goto out;
+
+ if (alrm->enabled)
+ lp_cr |= (SRTC_LPCR_ALP | SRTC_LPCR_WAE);
+ else
+ lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE);
+
+ if (lp_cr & SRTC_LPCR_ALL_INT_EN) {
+ if (!pdata->irq_enable) {
+ enable_irq(pdata->irq);
+ pdata->irq_enable = true;
+ }
+ } else {
+ if (pdata->irq_enable) {
+ disable_irq(pdata->irq);
+ pdata->irq_enable = false;
+ }
+ }
+
+ __raw_writel(lp_cr, ioaddr + SRTC_LPCR);
+
+out:
+ spin_unlock_irqrestore(&rtc_lock, lock_flags);
+ rtc_write_sync_lp(ioaddr);
+ return ret;
+}
+
+/*!
+ * This function is used to provide the content for the /proc/driver/rtc
+ * file.
+ *
+ * @param seq buffer to hold the information that the driver wants to write
+ *
+ * @return The number of bytes written into the rtc file.
+ */
+static int mxc_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct rtc_drv_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ clk_enable(pdata->clk);
+ seq_printf(seq, "alarm_IRQ\t: %s\n",
+ (((__raw_readl(ioaddr + SRTC_LPCR)) & SRTC_LPCR_ALP) !=
+ 0) ? "yes" : "no");
+ clk_disable(pdata->clk);
+
+ return 0;
+}
+
+/*!
+ * The RTC driver structure
+ */
+static struct rtc_class_ops mxc_rtc_ops = {
+ .open = mxc_rtc_open,
+ .release = mxc_rtc_release,
+ .ioctl = mxc_rtc_ioctl,
+ .read_time = mxc_rtc_read_time,
+ .set_time = mxc_rtc_set_time,
+ .read_alarm = mxc_rtc_read_alarm,
+ .set_alarm = mxc_rtc_set_alarm,
+ .proc = mxc_rtc_proc,
+};
+
+/*! MXC RTC Power management control */
+static int mxc_rtc_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+ struct timespec tv;
+ struct resource *res;
+ struct rtc_device *rtc;
+ struct rtc_drv_data *pdata = NULL;
+ struct mxc_srtc_platform_data *plat_data = NULL;
+ void __iomem *ioaddr;
+ void __iomem *srtc_secmode_addr;
+ int ret = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->clk = clk_get(&pdev->dev, "rtc_clk");
+ clk_enable(pdata->clk);
+ pdata->baseaddr = res->start;
+ pdata->ioaddr = ioremap(pdata->baseaddr, 0x40);
+ ioaddr = pdata->ioaddr;
+
+ /* Configure and enable the RTC */
+ pdata->irq = platform_get_irq(pdev, 0);
+ if (pdata->irq >= 0) {
+ if (request_irq(pdata->irq, mxc_rtc_interrupt, IRQF_SHARED,
+ pdev->name, pdev) < 0) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = -1;
+ } else {
+ disable_irq(pdata->irq);
+ pdata->irq_enable = false;
+ }
+ }
+
+ clk = clk_get(NULL, "rtc_clk");
+ if (clk_get_rate(clk) != 32768) {
+ printk(KERN_ALERT "rtc clock is not valid");
+ ret = -EINVAL;
+ clk_put(clk);
+ goto err_out;
+ }
+ clk_put(clk);
+
+ /* initialize glitch detect */
+ __raw_writel(SRTC_LPPDR_INIT, ioaddr + SRTC_LPPDR);
+ udelay(100);
+
+ /* clear lp interrupt status */
+ __raw_writel(0xFFFFFFFF, ioaddr + SRTC_LPSR);
+ udelay(100);;
+
+ plat_data = (struct mxc_srtc_platform_data *)pdev->dev.platform_data;
+ clk = clk_get(NULL, "iim_clk");
+ clk_enable(clk);
+ srtc_secmode_addr = ioremap(plat_data->srtc_sec_mode_addr, 1);
+
+ /* Check SRTC security mode */
+ if (((__raw_readl(srtc_secmode_addr) & SRTC_SECMODE_MASK) ==
+ SRTC_SECMODE_LOW) && (cpu_is_mx51_rev(CHIP_REV_1_0) == 1)) {
+ /* Workaround for MX51 TO1 due to inaccurate CKIL clock */
+ __raw_writel(SRTC_LPCR_EN_LP, ioaddr + SRTC_LPCR);
+ udelay(100);
+ } else {
+ /* move out of init state */
+ __raw_writel((SRTC_LPCR_IE | SRTC_LPCR_NSA),
+ ioaddr + SRTC_LPCR);
+
+ udelay(100);
+
+ while ((__raw_readl(ioaddr + SRTC_LPSR) & SRTC_LPSR_IES) == 0);
+
+ /* move out of non-valid state */
+ __raw_writel((SRTC_LPCR_IE | SRTC_LPCR_NVE | SRTC_LPCR_NSA |
+ SRTC_LPCR_EN_LP), ioaddr + SRTC_LPCR);
+
+ udelay(100);
+
+ while ((__raw_readl(ioaddr + SRTC_LPSR) & SRTC_LPSR_NVES) == 0);
+
+ __raw_writel(0xFFFFFFFF, ioaddr + SRTC_LPSR);
+ udelay(100);
+ }
+ clk_disable(clk);
+ clk_put(clk);
+
+ rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &mxc_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto err_out;
+ }
+
+ pdata->rtc = rtc;
+ platform_set_drvdata(pdev, pdata);
+
+ tv.tv_nsec = 0;
+ tv.tv_sec = __raw_readl(ioaddr + SRTC_LPSCMR);
+
+ /* By default, devices should wakeup if they can */
+ /* So srtc is set as "should wakeup" as it can */
+ device_init_wakeup(&pdev->dev, 1);
+
+ clk_disable(pdata->clk);
+
+ return ret;
+
+err_out:
+ clk_disable(pdata->clk);
+ iounmap(ioaddr);
+ if (pdata->irq >= 0)
+ free_irq(pdata->irq, pdev);
+ kfree(pdata);
+ return ret;
+}
+
+static int __exit mxc_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
+ rtc_device_unregister(pdata->rtc);
+ if (pdata->irq >= 0)
+ free_irq(pdata->irq, pdev);
+
+ clk_disable(pdata->clk);
+ clk_put(pdata->clk);
+ kfree(pdata);
+ return 0;
+}
+
+/*!
+ * This function is called to save the system time delta relative to
+ * the MXC RTC when enterring a low power state. This time delta is
+ * then used on resume to adjust the system time to account for time
+ * loss while suspended.
+ *
+ * @param pdev not used
+ * @param state Power state to enter.
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev)) {
+ enable_irq_wake(pdata->irq);
+ } else {
+ if (pdata->irq_enable)
+ disable_irq(pdata->irq);
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is called to correct the system time based on the
+ * current MXC RTC time relative to the time delta saved during
+ * suspend.
+ *
+ * @param pdev not used
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_rtc_resume(struct platform_device *pdev)
+{
+ struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(&pdev->dev)) {
+ disable_irq_wake(pdata->irq);
+ } else {
+ if (pdata->irq_enable)
+ enable_irq(pdata->irq);
+ }
+
+ return 0;
+}
+
+/*!
+ * Contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_rtc_driver = {
+ .driver = {
+ .name = "mxc_rtc",
+ },
+ .probe = mxc_rtc_probe,
+ .remove = __exit_p(mxc_rtc_remove),
+ .suspend = mxc_rtc_suspend,
+ .resume = mxc_rtc_resume,
+};
+
+/*!
+ * This function creates the /proc/driver/rtc file and registers the device RTC
+ * in the /dev/misc directory. It also reads the RTC value from external source
+ * and setup the internal RTC properly.
+ *
+ * @return -1 if RTC is failed to initialize; 0 is successful.
+ */
+static int __init mxc_rtc_init(void)
+{
+ return platform_driver_register(&mxc_rtc_driver);
+}
+
+/*!
+ * This function removes the /proc/driver/rtc file and un-registers the
+ * device RTC from the /dev/misc directory.
+ */
+static void __exit mxc_rtc_exit(void)
+{
+ platform_driver_unregister(&mxc_rtc_driver);
+
+}
+
+module_init(mxc_rtc_init);
+module_exit(mxc_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ns9xxx.c b/drivers/rtc/rtc-ns9xxx.c
new file mode 100644
index 000000000000..a34689bff9ff
--- /dev/null
+++ b/drivers/rtc/rtc-ns9xxx.c
@@ -0,0 +1,946 @@
+/*
+ * linux/drivers/rtc/rtc-ns9xxx.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/clk.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/mach/time.h>
+
+/* registers */
+#define RTC_CONFIG (0x00)
+#define RTC_CONFIG_TIMEON (0)
+#define RTC_CONFIG_CALON (0)
+#define RTC_CONFIG_TIMEOFF (1 << 0)
+#define RTC_CONFIG_CALOFF (1 << 1)
+
+#define RTC_1224H (0x04)
+#define RTC_1224H_MODE (1 << 0)
+#define RTC_1224H_MODE_12H (1 << 0)
+#define RTC_1224H_MODE_24H (0 << 0)
+
+#define RTC_TIME (0x08)
+#define RTC_TIME_PM (1 << 30)
+#define RTC_TIME_HOUR_MASK (0x3f000000)
+#define RTC_TIME_MINUTE_MASK (0x007f0000)
+#define RTC_TIME_SECOND_MASK (0x00007f00)
+#define RTC_TIME_MONTH_MASK (0x000000f8)
+#define RTC_TIME_DATE_MASK (0x00003f00)
+#define RTC_TIME_DAY_MASK (0x00000007)
+#define RTC_TIME_CENTURY_MASK (0x3f000000)
+#define RTC_TIME_YEAR_MASK (0x00ff0000)
+
+#define RTC_CAL (0x0c)
+#define RTC_ALARMTIME (0x10)
+#define RTC_ALARMCAL (0x14)
+
+#define RTC_ALARMENABLE (0x18)
+#define RTC_ALARMENABLE_HSEC (1 << 0)
+#define RTC_ALARMENABLE_SEC (1 << 1)
+#define RTC_ALARMENABLE_MIN (1 << 2)
+#define RTC_ALARMENABLE_HOUR (1 << 3)
+#define RTC_ALARMENABLE_DATE (1 << 4)
+#define RTC_ALARMENABLE_MNTH (1 << 5)
+#define RTC_ALARMENABLE_ALL (0x3f)
+
+#define RTC_EVENT (0x1c)
+
+#define RTC_IRQENABLE (0x20)
+#define RTC_IRQDISABLE (0x24)
+#define RTC_IRQSTATUS (0x28)
+#define RTC_IRQ_HUNDREDTH (1 << 0)
+#define RTC_IRQ_SECOND (1 << 1)
+#define RTC_IRQ_MINUTE (1 << 2)
+#define RTC_IRQ_HOUR (1 << 3)
+#define RTC_IRQ_DATE (1 << 4)
+#define RTC_IRQ_MONTH (1 << 5)
+#define RTC_IRQ_ALARM (1 << 6)
+
+#define RTC_STATUS (0x2c)
+#define RTC_STATUS_TIMEVALID (1 << 0)
+#define RTC_STATUS_CALVALID (1 << 1)
+#define RTC_STATUS_ALARMTIMEVALID (1 << 2)
+#define RTC_STATUS_ALARMCALVALID (1 << 3)
+
+/* configuration/status shifts */
+#define RTC_TIME_HOUR_SHIFT (24)
+#define RTC_TIME_MINUTE_SHIFT (16)
+#define RTC_TIME_SECOND_SHIFT (8)
+#define RTC_CAL_MONTH_SHIFT (3)
+#define RTC_CAL_DATE_SHIFT (8)
+#define RTC_CAL_DAY_SHIFT (0)
+#define RTC_CAL_CENTURY_SHIFT (24)
+#define RTC_CAL_YEAR_SHIFT (16)
+
+#define RTC_IRQ_PERIOD RTC_IRQ_SECOND
+#define RTC_IRQ_ALL \
+ (RTC_IRQ_HUNDREDTH | RTC_IRQ_SECOND | \
+ RTC_IRQ_MINUTE | RTC_IRQ_HOUR | \
+ RTC_IRQ_DATE | RTC_IRQ_MONTH | \
+ RTC_IRQ_ALARM)
+
+#define RTC_RAM (0xc0)
+#define RTC_RAM_SIZE (0x100 - RTC_RAM)
+
+#define DRIVER_NAME "rtc-ns9xxx"
+
+struct ns9xxx_rtc_pdata {
+ struct rtc_device *rtc;
+ struct clk *clk;
+ struct clk *pmclk;
+ void __iomem *ioaddr;
+ struct resource *mem;
+ int irq;
+ u8 irq_status;
+ u8 event;
+};
+
+static int __ns9xxx_rtc_timeon(struct ns9xxx_rtc_pdata *pdata, u32 config)
+{
+ if (config & (RTC_CONFIG_TIMEOFF | RTC_CONFIG_CALOFF)) {
+ dev_dbg(&pdata->rtc->dev,
+ "time or cal is stopped, RTC_CONFIG=0x%08x\n",
+ config);
+
+ return 0;
+ }
+ return 1;
+}
+
+static int ns9xxx_rtc_timeon(struct ns9xxx_rtc_pdata *pdata)
+{
+ u32 config = ioread32(pdata->ioaddr + RTC_CONFIG);
+ return __ns9xxx_rtc_timeon(pdata, config);
+}
+
+static int __ns9xxx_rtc_timevalid(struct ns9xxx_rtc_pdata *pdata, u32 status)
+{
+ if (~status & (RTC_STATUS_TIMEVALID | RTC_STATUS_CALVALID)) {
+ dev_dbg(&pdata->rtc->dev,
+ "time is invalid, RTC_STATUS=0x%08x\n",
+ status);
+ return 0;
+ }
+
+ return 1;
+}
+static int ns9xxx_rtc_timevalid(struct ns9xxx_rtc_pdata *pdata)
+{
+ u32 status = ioread32(pdata->ioaddr + RTC_STATUS);
+ return __ns9xxx_rtc_timevalid(pdata, status);
+}
+
+static int ns9xxx_rtc_timeonandvalid(struct ns9xxx_rtc_pdata *pdata)
+{
+ return ns9xxx_rtc_timeon(pdata) && ns9xxx_rtc_timevalid(pdata);
+}
+
+static int ns9xxx_rtc_alarmvalid(struct ns9xxx_rtc_pdata *pdata)
+{
+ int ret;
+ u32 status;
+
+ ret = ns9xxx_rtc_timeon(pdata);
+ if (!ret)
+ return ret;
+
+ status = ioread32(pdata->ioaddr + RTC_STATUS);
+ ret = __ns9xxx_rtc_timevalid(pdata, status);
+ if (!ret)
+ return ret;
+
+ if (~status & (RTC_STATUS_ALARMTIMEVALID |
+ (RTC_STATUS_ALARMCALVALID))) {
+ dev_dbg(&pdata->rtc->dev,
+ "alarm is invalid, RTC_STATUS=0x%08x\n",
+ status);
+ return 0;
+ }
+
+ return 1;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+
+static int ns9xxx_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ struct ns9xxx_rtc_pdata *pdata = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "%s(cmd=%u, arg=%lu)\n", __func__, cmd, arg);
+ ret = clk_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ pdata->event |= ioread32(pdata->ioaddr + RTC_EVENT);
+
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ /* disable alarm-irq */
+ pdata->irq_status &= ~RTC_IRQ_ALARM;
+ iowrite32(RTC_IRQ_ALARM, pdata->ioaddr + RTC_IRQDISABLE);
+ break;
+
+ case RTC_AIE_ON:
+ if (ns9xxx_rtc_alarmvalid(pdata)) {
+ /* enable alarm-irq */
+ pdata->irq_status |= RTC_IRQ_ALARM;
+ iowrite32(RTC_IRQ_ALARM, pdata->ioaddr + RTC_IRQENABLE);
+ pdata->event &= ~RTC_IRQ_ALARM;
+ } else {
+ ret = -EINVAL;
+ dev_dbg(dev, "%s: alarm not valid\n", __func__);
+ }
+
+ break;
+
+ case RTC_UIE_OFF:
+ /* disable periodic irq */
+ pdata->irq_status &= ~RTC_IRQ_PERIOD;
+ iowrite32(RTC_IRQ_PERIOD, pdata->ioaddr + RTC_IRQDISABLE);
+ break;
+
+ case RTC_UIE_ON:
+ if (ns9xxx_rtc_timeonandvalid(pdata)) {
+ /* enable periodic irq */
+ pdata->irq_status |= RTC_IRQ_PERIOD;
+ iowrite32(RTC_IRQ_PERIOD,
+ pdata->ioaddr + RTC_IRQENABLE);
+ pdata->event &= ~RTC_IRQ_PERIOD;
+ } else {
+ ret = -EINVAL;
+ dev_dbg(dev, "%s: time not valid\n", __func__);
+ }
+
+ break;
+
+ default:
+ ret = -ENOIOCTLCMD;
+ }
+
+ clk_disable(pdata->clk);
+
+ dev_dbg(dev, "%s(cmd=%u, arg=%lu) -> %d\n", __func__, cmd, arg, ret);
+ return ret;
+}
+
+#else
+# define ns9xxx_rtc_ioctl NULL
+#endif /* CONFIG_RTC_INTF_DEV */
+
+static int ns9xxx_rtc_get_time_sync(struct ns9xxx_rtc_pdata *pdata,
+ unsigned int *time, unsigned int *cal)
+{
+ u32 cal1, time1, cal2;
+ int tries = 5;
+
+ dev_dbg(&pdata->rtc->dev, "%s\n", __func__);
+
+ if (!ns9xxx_rtc_timeonandvalid(pdata)) {
+ dev_dbg(&pdata->rtc->dev, "%s -> %d\n", __func__, -EINVAL);
+ return -EINVAL;
+ }
+
+ /* necessary to avoid time and date beeing out of sync */
+ do {
+ cal1 = ioread32(pdata->ioaddr + RTC_CAL);
+ time1 = ioread32(pdata->ioaddr + RTC_TIME);
+ cal2 = ioread32(pdata->ioaddr + RTC_CAL);
+ } while ((cal1 != cal2) && --tries);
+
+ if (!tries) {
+ dev_dbg(&pdata->rtc->dev, "%s -> %d\n", __func__, -EBUSY);
+ return -EBUSY;
+ }
+
+ *time = time1;
+ *cal = cal1;
+
+ dev_dbg(&pdata->rtc->dev, "%s: cal/time = %08x/%08x -> 0\n",
+ __func__, cal1, time1);
+ return 0;
+}
+
+static void ns9xxx_rtc_decode_time(struct rtc_time *tm,
+ u32 time, u32 cal, u32 mode1224)
+{
+ tm->tm_hour = bcd2bin((time & RTC_TIME_HOUR_MASK)
+ >> RTC_TIME_HOUR_SHIFT);
+ if (unlikely((mode1224 & RTC_1224H_MODE) == RTC_1224H_MODE_12H)) {
+ if (time & RTC_TIME_PM)
+ tm->tm_hour += 12;
+ }
+ tm->tm_min = bcd2bin((time & RTC_TIME_MINUTE_MASK)
+ >> RTC_TIME_MINUTE_SHIFT);
+ tm->tm_sec = bcd2bin((time & RTC_TIME_SECOND_MASK)
+ >> RTC_TIME_SECOND_SHIFT);
+
+ tm->tm_mon = (bcd2bin((cal & RTC_TIME_MONTH_MASK)
+ >> RTC_CAL_MONTH_SHIFT) - 1);
+ tm->tm_mday = bcd2bin((cal & RTC_TIME_DATE_MASK)
+ >> RTC_CAL_DATE_SHIFT);
+ tm->tm_wday = (bcd2bin(cal & RTC_TIME_DAY_MASK)
+ >> RTC_CAL_DAY_SHIFT) - 1;
+ tm->tm_year = bcd2bin((cal & RTC_TIME_CENTURY_MASK)
+ >> RTC_CAL_CENTURY_SHIFT) * 100;
+ tm->tm_year += bcd2bin((cal & RTC_TIME_YEAR_MASK)
+ >> RTC_CAL_YEAR_SHIFT);
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+ tm->tm_year -= 1900;
+
+ pr_debug("%s: %08x/%08x -> %d-%d-%d %d:%d:%d\n",
+ __func__, cal, time, tm->tm_year,
+ tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+static void ns9xxx_rtc_encode_time(struct rtc_time *tm,
+ unsigned int *time, unsigned int *cal)
+{
+ *time = bin2bcd(tm->tm_hour) << RTC_TIME_HOUR_SHIFT;
+ *time |= bin2bcd(tm->tm_min) << RTC_TIME_MINUTE_SHIFT;
+ *time |= bin2bcd(tm->tm_sec) << RTC_TIME_SECOND_SHIFT;
+
+ *cal = bin2bcd(tm->tm_mon + 1) << RTC_CAL_MONTH_SHIFT;
+ *cal |= bin2bcd(tm->tm_mday) << RTC_CAL_DATE_SHIFT;
+ /* NET+OS uses 1 to encode Sunday, so we follow that practise */
+ *cal |= bin2bcd(tm->tm_wday + 1) << RTC_CAL_DAY_SHIFT;
+ *cal |= bin2bcd((tm->tm_year + 1900) % 100)
+ << RTC_CAL_YEAR_SHIFT;
+ *cal |= bin2bcd((tm->tm_year + 1900) / 100)
+ << RTC_CAL_CENTURY_SHIFT;
+ pr_debug("%s: cal/time = %08x/%08x\n", __func__, *cal, *time);
+}
+
+static int ns9xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ u32 uninitialized_var(time), uninitialized_var(cal);
+ u32 mode1224;
+ int ret;
+ struct ns9xxx_rtc_pdata *pdata = dev_get_drvdata(dev);
+
+ ret = clk_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ ret = ns9xxx_rtc_get_time_sync(pdata, &time, &cal);
+
+ mode1224 = ioread32(pdata->ioaddr + RTC_1224H);
+
+ clk_disable(pdata->clk);
+
+ if (!ret)
+ ns9xxx_rtc_decode_time(tm, time, cal, mode1224);
+
+ return ret;
+}
+
+static int ns9xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned int time, cal, stat;
+ int ret;
+ struct ns9xxx_rtc_pdata *pdata = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (rtc_valid_tm(tm) != 0)
+ return -EINVAL;
+
+ ns9xxx_rtc_encode_time(tm, &time, &cal);
+
+ ret = clk_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ /* disable operation */
+ iowrite32(RTC_CONFIG_TIMEOFF | RTC_CONFIG_CALOFF,
+ pdata->ioaddr + RTC_CONFIG);
+
+ iowrite32(time, pdata->ioaddr + RTC_TIME);
+ iowrite32(cal, pdata->ioaddr + RTC_CAL);
+
+ iowrite32(RTC_1224H_MODE_24H, pdata->ioaddr + RTC_1224H);
+
+ /* reenable operation */
+ iowrite32(RTC_CONFIG_TIMEON | RTC_CONFIG_CALON,
+ pdata->ioaddr + RTC_CONFIG);
+
+ /* make sure configs are valid */
+ stat = ioread32(pdata->ioaddr + RTC_STATUS);
+
+ clk_disable(pdata->clk);
+
+ if (~stat & (RTC_STATUS_TIMEVALID | RTC_STATUS_CALVALID)) {
+ dev_dbg(dev, "%s: invalid date/time: RTC_STATUS = 0x%08x\n",
+ __func__, stat);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ns9xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned int time, cal;
+ int ret;
+ struct ns9xxx_rtc_pdata *pdata = dev_get_drvdata(dev);
+ u32 mode1224, status;
+ u32 almenable;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ ret = clk_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ status = ioread32(pdata->ioaddr + RTC_STATUS);
+ if (~status & (RTC_STATUS_ALARMTIMEVALID | RTC_STATUS_ALARMCALVALID)) {
+ dev_dbg(dev, "time or cal is invalid, RTC_STATUS=0x%08x\n",
+ status);
+ return -EINVAL;
+ }
+
+ mode1224 = ioread32(pdata->ioaddr + RTC_1224H);
+ time = ioread32(pdata->ioaddr + RTC_ALARMTIME);
+ cal = ioread32(pdata->ioaddr + RTC_ALARMCAL);
+
+ dev_dbg(dev, "%s: cal/time = %08x/%08x\n", __func__, cal, time);
+ ns9xxx_rtc_decode_time(&(alm->time), time, cal, mode1224);
+
+ almenable = ioread32(pdata->ioaddr + RTC_ALARMENABLE);
+ dev_dbg(dev, "%s: ALARMENABLE = %08x\n", __func__, almenable);
+
+ if (!(almenable & RTC_ALARMENABLE_SEC))
+ alm->time.tm_sec = -1;
+
+ if (!(almenable & RTC_ALARMENABLE_MIN))
+ alm->time.tm_min = -1;
+
+ if (!(almenable & RTC_ALARMENABLE_HOUR))
+ alm->time.tm_hour = -1;
+
+ if (!(almenable & RTC_ALARMENABLE_DATE))
+ alm->time.tm_mday = -1;
+
+ if (!(almenable & RTC_ALARMENABLE_MNTH))
+ alm->time.tm_mon = -1;
+
+ alm->time.tm_year = -1;
+
+ alm->enabled = !!(pdata->irq_status & RTC_IRQ_ALARM);
+
+ clk_disable(pdata->clk);
+
+ return 0;
+}
+
+static int ns9xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned int time, cal, stat;
+ int ret = 0;
+ struct ns9xxx_rtc_pdata *pdata = dev_get_drvdata(dev);
+
+ if (rtc_valid_tm(&(alm->time)) != 0)
+ return -EINVAL;
+
+ ns9xxx_rtc_encode_time(&(alm->time), &time, &cal);
+
+ dev_dbg(dev, "%s: %08x/%08x\n", __func__, cal, time);
+
+ ret = clk_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ if (!alm->enabled) {
+ iowrite32(RTC_IRQ_ALARM, pdata->ioaddr + RTC_IRQDISABLE);
+ pdata->irq_status &= ~RTC_IRQ_ALARM;
+ }
+
+ iowrite32(time, pdata->ioaddr + RTC_ALARMTIME);
+ iowrite32(cal, pdata->ioaddr + RTC_ALARMCAL);
+
+ if (alm->enabled) {
+ iowrite32(RTC_ALARMENABLE_ALL, pdata->ioaddr + RTC_ALARMENABLE);
+
+ /* make sure configs are valid */
+ stat = ioread32(pdata->ioaddr + RTC_STATUS);
+ if (!(stat & (RTC_STATUS_ALARMTIMEVALID |
+ RTC_STATUS_ALARMCALVALID))) {
+ dev_dbg(dev, "%s: invalid date/time: "
+ "RTC_STATUS = 0x%08x\n",
+ __func__, stat);
+ ret = -EINVAL;
+ }
+
+ iowrite32(RTC_IRQ_ALARM, pdata->ioaddr + RTC_IRQENABLE);
+ pdata->irq_status |= RTC_IRQ_ALARM;
+ }
+
+ clk_disable(pdata->clk);
+
+ return ret;
+}
+
+static int ns9xxx_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ unsigned int stat;
+ int ret;
+ struct ns9xxx_rtc_pdata *pdata = dev_get_drvdata(dev);
+
+ ret = clk_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ stat = ioread32(pdata->ioaddr + RTC_IRQSTATUS);
+
+ clk_disable(pdata->clk);
+
+ return seq_printf(seq,
+ "IRQ on:%s%s%s%s%s%s\n",
+ (stat & RTC_IRQ_ALARM) ? "" : " alarm",
+ (stat & RTC_IRQ_MONTH) ? "" : " month_rollover",
+ (stat & RTC_IRQ_DATE) ? "" : " day_rollover",
+ (stat & RTC_IRQ_HOUR) ? "" : " hour_rollover",
+ (stat & RTC_IRQ_MINUTE) ? "" : " minute_rollover",
+ (stat & RTC_IRQ_SECOND) ? "" : " second_rollover");
+}
+
+static struct rtc_class_ops ns9xxx_rtc_ops = {
+ .ioctl = ns9xxx_rtc_ioctl,
+ .read_time = ns9xxx_rtc_read_time,
+ .set_time = ns9xxx_rtc_set_time,
+ .read_alarm = ns9xxx_rtc_read_alarm,
+ .set_alarm = ns9xxx_rtc_set_alarm,
+ .proc = ns9xxx_rtc_proc,
+};
+
+static irqreturn_t ns9xxx_rtc_irq_handler(int irq, void *irq_data)
+{
+ u8 event;
+ struct ns9xxx_rtc_pdata *pdata = irq_data;
+ int handled = 0, ret;
+
+ ret = clk_enable(pdata->clk);
+ if (unlikely(ret)) {
+ dev_warn(&pdata->rtc->dev, "%s: Could not enable clock, "
+ "error is %d\n", __func__, ret);
+ return IRQ_NONE;
+ }
+
+ pdata->event |= ioread32(pdata->ioaddr + RTC_EVENT);
+ event = pdata->event & pdata->irq_status;
+ dev_dbg(&pdata->rtc->dev, "%s: RTC_IRQSTATUS = %x, RTC_EVENT = %x\n",
+ __func__, pdata->irq_status, pdata->event);
+ pdata->event = 0;
+
+ if (event & (RTC_IRQ_ALARM | RTC_IRQ_PERIOD)) {
+ unsigned long rtcevents = RTC_IRQF;
+
+ if (event & RTC_IRQ_ALARM) {
+ rtcevents |= RTC_AF;
+
+ /* according to the comment in rtc_sysfs_show_wakealarm
+ * further alarm irqs must not occur.
+ */
+ pdata->irq_status &= ~RTC_IRQ_ALARM;
+ iowrite32(RTC_IRQ_ALARM,
+ pdata->ioaddr + RTC_IRQDISABLE);
+ }
+
+ if (event & RTC_IRQ_PERIOD)
+ rtcevents |= RTC_UF;
+
+ rtc_update_irq(pdata->rtc, 1, rtcevents);
+
+ handled = 1;
+ }
+
+ clk_disable(pdata->clk);
+
+ return IRQ_RETVAL(handled);
+}
+
+static ssize_t ns9xxx_rtc_nvram_read(struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t pos, size_t size)
+{
+ loff_t npos = pos;
+ struct ns9xxx_rtc_pdata *pdata = bin_attr->private;
+ u32 data;
+ int ret;
+
+ if (pos >= RTC_RAM_SIZE)
+ return 0;
+
+ if (pos + size > RTC_RAM_SIZE)
+ size = RTC_RAM_SIZE - pos;
+
+ ret = clk_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ if (npos & 3) {
+ data = ioread32(pdata->ioaddr + RTC_RAM + (npos & ~3));
+ pr_debug("%s: nvram[0x%04llx] -> 0x%08x\n", __func__, npos & ~3, data);
+ } else
+ data = 0; /* silence gcc */
+
+ for (; size > 0; npos++, size--) {
+ if (!(npos & 3)) {
+ data = ioread32(pdata->ioaddr + RTC_RAM + npos);
+ pr_debug("%s: nvram[0x%04llx] -> 0x%08x\n", __func__, npos, data);
+ }
+
+ *buf++ = data >> ((npos & 3) << 3);
+ }
+
+ clk_disable(pdata->clk);
+
+ return npos - pos;
+}
+
+static ssize_t ns9xxx_rtc_nvram_write(struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t pos, size_t size)
+{
+ loff_t npos = pos;
+ struct ns9xxx_rtc_pdata *pdata = bin_attr->private;
+ int ret;
+ u32 data;
+
+ if (pos >= RTC_RAM_SIZE)
+ return 0;
+
+ if (pos + size > RTC_RAM_SIZE)
+ size = RTC_RAM_SIZE - pos;
+
+ pr_debug("%s: pos = 0x%04llx, size = 0x%x\n", __func__, pos, size);
+ ret = clk_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ if (npos & 3) {
+ data = ioread32(pdata->ioaddr + RTC_RAM + (npos & ~3));
+ pr_debug("%s a: nvram[0x%04llx] -> 0x%08x\n", __func__, npos & ~3, data);
+ data &= (1 << ((npos & 3) << 3)) - 1;
+ } else
+ data = 0;
+
+ for (; size > 0; npos++, size--) {
+ data |= *buf++ << ((npos & 3) << 3);
+
+ if ((npos & 3) == 3) {
+ pr_debug("%s b: nvram[0x%04llx] <- 0x%08x\n", __func__, npos & ~3, data);
+ iowrite32(data, pdata->ioaddr + RTC_RAM + (npos & ~3));
+
+ /* the next write doesn't fill a complete u32. Note
+ * that size isn't decremented yet for the last byte */
+ if (size < 5 && size > 1) {
+ data = ioread32(pdata->ioaddr + RTC_RAM + npos + 1);
+ pr_debug("%s c: nvram[0x%04llx] -> 0x%08x\n", __func__, npos + 1, data);
+ data &= ~((1 << ((size - 1) << 3)) - 1);
+ } else
+ data = 0;
+ }
+ }
+ if (npos & 3) {
+ pr_debug("%s d: nvram[0x%04llx] <- 0x%08x\n", __func__, npos & ~3, data);
+ iowrite32(data, pdata->ioaddr + RTC_RAM + (npos & ~3));
+ }
+
+ clk_disable(pdata->clk);
+
+ return npos - pos;
+}
+
+static struct bin_attribute ns9xxx_rtc_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUGO,
+ .owner = THIS_MODULE,
+ },
+ .size = RTC_RAM_SIZE,
+ .read = ns9xxx_rtc_nvram_read,
+ .write = ns9xxx_rtc_nvram_write,
+};
+
+static int __init ns9xxx_rtc_probe(struct platform_device *pdev)
+{
+ struct ns9xxx_rtc_pdata *pdata;
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, pdata);
+
+ pdata->irq = platform_get_irq(pdev, 0);
+ if (pdata->irq < 0) {
+ ret = -ENOENT;
+ dev_dbg(&pdev->dev, "err_get_irq\n");
+ goto err_get_irq;
+ }
+
+ pdata->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pdata->mem) {
+ ret = -ENOENT;
+ dev_dbg(&pdev->dev, "err_get_resource\n");
+ goto err_get_resource;
+ }
+
+ if (!request_mem_region(pdata->mem->start,
+ pdata->mem->end - pdata->mem->start + 1,
+ DRIVER_NAME)) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "err_request_mem_region\n");
+ goto err_request_mem_region;
+ }
+
+ pdata->ioaddr = ioremap(pdata->mem->start,
+ pdata->mem->end - pdata->mem->start + 1);
+ if (!pdata->ioaddr) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "err_ioremap\n");
+ goto err_ioremap;
+ } else
+ dev_dbg(&pdev->dev, "ioaddr = 0x%p\n", pdata->ioaddr);
+
+ pdata->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(pdata->clk)) {
+ ret = PTR_ERR(pdata->clk);
+ dev_dbg(&pdev->dev, "err_clk_get -> %d\n", ret);
+ goto err_clk_get;
+ }
+
+ ret = clk_enable(pdata->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "err_clk_enable -> %d\n", ret);
+ goto err_clk_enable;
+ }
+
+#if defined(CONFIG_PM)
+ pdata->pmclk = clk_get(&pdev->dev, DRIVER_NAME "-pm");
+ if (IS_ERR(pdata->pmclk)) {
+ ret = PTR_ERR(pdata->pmclk);
+ dev_dbg(&pdev->dev, "err_clk_get_pm -> %d\n", ret);
+ goto err_clk_get_pm;
+ }
+
+ /* Make the device wakeup capable, but disabled by default */
+ device_init_wakeup(&pdev->dev, 1);
+ device_set_wakeup_enable(&pdev->dev, 0);
+#endif
+
+ /* disable interrupts and clear event flags */
+ iowrite32(RTC_IRQ_ALL, pdata->ioaddr + RTC_IRQDISABLE);
+ ioread32(pdata->ioaddr + RTC_EVENT);
+
+ pdata->rtc = rtc_device_register(DRIVER_NAME, &pdev->dev,
+ &ns9xxx_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pdata->rtc)) {
+ ret = PTR_ERR(pdata->rtc);
+ dev_dbg(&pdev->dev, "err_rtc_device_register -> %d\n", ret);
+ goto err_rtc_device_register;
+ }
+
+ ns9xxx_rtc_nvram_attr.private = pdata;
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, &ns9xxx_rtc_nvram_attr);
+ if (ret)
+ goto err_sysfs_bin;
+
+ ret = request_irq(pdata->irq, ns9xxx_rtc_irq_handler, IRQF_SHARED,
+ pdata->rtc->dev.bus_id, pdata);
+ if (ret) {
+ dev_dbg(&pdev->dev, "err_request_irq -> %d\n", ret);
+
+ rtc_device_unregister(pdata->rtc);
+err_rtc_device_register:
+ sysfs_remove_bin_file(&pdev->dev.kobj, &ns9xxx_rtc_nvram_attr);
+err_sysfs_bin:
+#if defined(CONFIG_PM)
+ clk_put(pdata->pmclk);
+err_clk_get_pm:
+
+#endif
+ clk_disable(pdata->clk);
+err_clk_enable:
+
+ clk_put(pdata->clk);
+err_clk_get:
+
+ iounmap(pdata->ioaddr);
+err_ioremap:
+
+ release_mem_region(pdata->mem->start,
+ pdata->mem->end - pdata->mem->start + 1);
+err_request_mem_region:
+err_get_resource:
+err_get_irq:
+
+ kfree(pdata);
+ return ret;
+ }
+
+ clk_disable(pdata->clk);
+
+ return 0;
+}
+
+static int __devexit ns9xxx_rtc_remove(struct platform_device *pdev)
+{
+ struct ns9xxx_rtc_pdata *pdata = platform_get_drvdata(pdev);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+ ret = clk_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ /* disable interrupts */
+ iowrite32(RTC_IRQ_ALL, pdata->ioaddr + RTC_IRQDISABLE);
+
+ clk_disable(pdata->clk);
+
+ free_irq(pdata->irq, pdata);
+ sysfs_remove_bin_file(&pdev->dev.kobj, &ns9xxx_rtc_nvram_attr);
+ rtc_device_unregister(pdata->rtc);
+ clk_put(pdata->clk);
+ iounmap(pdata->ioaddr);
+ release_mem_region(pdata->mem->start,
+ pdata->mem->end - pdata->mem->start + 1);
+ kfree(pdata);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int ns9xxx_rtc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct ns9xxx_rtc_pdata *pdata = platform_get_drvdata(pdev);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ ret = clk_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ if (device_may_wakeup(&pdev->dev)) {
+
+ ret = clk_enable(pdata->pmclk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_enable_pmclk -> %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ enable_irq_wake(pdata->irq);
+ dev_dbg(&pdev->dev, "%s: irq_status = %08x\n",
+ __func__, pdata->irq_status);
+
+ } else
+ /* disable irqs */
+ iowrite32(RTC_IRQ_ALL, pdata->ioaddr + RTC_IRQDISABLE);
+
+err:
+ clk_disable(pdata->clk);
+
+ return ret;
+}
+
+static int ns9xxx_rtc_resume(struct platform_device *pdev)
+{
+ struct ns9xxx_rtc_pdata *pdata = platform_get_drvdata(pdev);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ ret = clk_enable(pdata->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_clk_enable -> %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (device_may_wakeup(&pdev->dev)) {
+ disable_irq_wake(pdata->irq);
+ clk_disable(pdata->pmclk);
+ } else
+ iowrite32(pdata->irq_status, pdata->ioaddr + RTC_IRQENABLE);
+
+ clk_disable(pdata->clk);
+
+ return 0;
+}
+
+#else
+# define ns9xxx_rtc_suspend NULL
+# define ns9xxx_rtc_resume NULL
+#endif /* CONFIG_PM */
+
+static void ns9xxx_rtc_shutdown(struct platform_device *pdev)
+{
+ struct ns9xxx_rtc_pdata *pdata = platform_get_drvdata(pdev);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ ret = clk_enable(pdata->clk);
+ if (ret) {
+ dev_warn(&pdev->dev, "%s: cannot enable rtc clock to disable "
+ "interrupts\n", __func__);
+ return;
+ }
+
+ /* just disable interrupts */
+ iowrite32(RTC_IRQ_ALL,
+ pdata->ioaddr + RTC_IRQDISABLE);
+
+ clk_disable(pdata->clk);
+}
+
+static struct platform_driver ns9xxx_rtc_driver = {
+ .probe = ns9xxx_rtc_probe,
+ .remove = __devexit_p(ns9xxx_rtc_remove),
+ .suspend = ns9xxx_rtc_suspend,
+ .resume = ns9xxx_rtc_resume,
+ .shutdown = ns9xxx_rtc_shutdown,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ns9xxx_rtc_init(void)
+{
+ return platform_driver_register(&ns9xxx_rtc_driver);
+}
+
+static void __exit ns9xxx_rtc_exit(void)
+{
+ platform_driver_unregister(&ns9xxx_rtc_driver);
+}
+
+module_init(ns9xxx_rtc_init);
+module_exit(ns9xxx_rtc_exit);
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <Uwe.Kleine-Koenig@digi.com>");
+MODULE_DESCRIPTION("RTC driver for Digi ns9xxx");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c
new file mode 100644
index 000000000000..51499cd0ba81
--- /dev/null
+++ b/drivers/rtc/rtc-stmp3xxx.c
@@ -0,0 +1,277 @@
+/*
+ * Freescale STMP37XX/STMP378X Real Time Clock driver
+ *
+ * Copyright (c) 2007 Sigmatel, Inc.
+ * Peter Hartley, <peter.hartley@sigmatel.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/uaccess.h>
+
+#include <mach/stmp3xxx.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/regs-rtc.h>
+
+struct stmp3xxx_rtc_data {
+ struct rtc_device *rtc;
+ unsigned irq_count;
+};
+
+/* Time read/write */
+static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ while (HW_RTC_STAT_RD() & BF_RTC_STAT_STALE_REGS(0x80))
+ cpu_relax();
+
+ rtc_time_to_tm(HW_RTC_SECONDS_RD(), rtc_tm);
+ return 0;
+}
+
+static int stmp3xxx_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ unsigned long t;
+ int rc = rtc_tm_to_time(rtc_tm, &t);
+
+ if (rc == 0) {
+ HW_RTC_SECONDS_WR(t);
+
+ /* The datasheet doesn't say which way round the
+ * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0,
+ * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS,
+ */
+ while (HW_RTC_STAT_RD() & BF_RTC_STAT_NEW_REGS(0x80))
+ cpu_relax();
+ }
+ return rc;
+}
+
+static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = to_platform_device(dev_id);
+ struct stmp3xxx_rtc_data *data = platform_get_drvdata(pdev);
+ u32 status;
+ u32 events = 0;
+
+ status = HW_RTC_CTRL_RD() &
+ (BM_RTC_CTRL_ALARM_IRQ | BM_RTC_CTRL_ONEMSEC_IRQ);
+ if (status & BM_RTC_CTRL_ALARM_IRQ) {
+ HW_RTC_CTRL_CLR(BM_RTC_CTRL_ALARM_IRQ);
+ events |= RTC_AF | RTC_IRQF;
+ }
+ if (status & BM_RTC_CTRL_ONEMSEC_IRQ) {
+ HW_RTC_CTRL_CLR(BM_RTC_CTRL_ONEMSEC_IRQ);
+ if (++data->irq_count % 1000 == 0) {
+ events |= RTC_UF | RTC_IRQF;
+ data->irq_count = 0;
+ }
+ }
+
+ if (events)
+ rtc_update_irq(data->rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static int stmp3xxx_rtc_open(struct device *dev)
+{
+ int r;
+
+ r = request_irq(IRQ_RTC_ALARM, stmp3xxx_rtc_interrupt,
+ IRQF_DISABLED, "RTC alarm", dev);
+ if (r) {
+ dev_err(dev, "Cannot claim IRQ%d\n", IRQ_RTC_ALARM);
+ goto fail_1;
+ }
+ r = request_irq(IRQ_RTC_1MSEC, stmp3xxx_rtc_interrupt,
+ IRQF_DISABLED, "RTC tick", dev);
+ if (r) {
+ dev_err(dev, "Cannot claim IRQ%d\n", IRQ_RTC_1MSEC);
+ goto fail_2;
+ }
+
+ return 0;
+fail_2:
+ free_irq(IRQ_RTC_ALARM, dev);
+fail_1:
+ return r;
+}
+
+static void stmp3xxx_rtc_release(struct device *dev)
+{
+ HW_RTC_CTRL_CLR(BM_RTC_CTRL_ALARM_IRQ_EN | BM_RTC_CTRL_ONEMSEC_IRQ_EN);
+ free_irq(IRQ_RTC_ALARM, dev);
+ free_irq(IRQ_RTC_1MSEC, dev);
+}
+
+static int stmp3xxx_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ struct stmp3xxx_rtc_data *data = dev_get_drvdata(dev);
+
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ HW_RTC_PERSISTENT0_CLR(BM_RTC_PERSISTENT0_ALARM_EN |
+ BM_RTC_PERSISTENT0_ALARM_WAKE_EN);
+ HW_RTC_CTRL_CLR(BM_RTC_CTRL_ALARM_IRQ_EN);
+ break;
+ case RTC_AIE_ON:
+ HW_RTC_PERSISTENT0_SET(BM_RTC_PERSISTENT0_ALARM_EN |
+ BM_RTC_PERSISTENT0_ALARM_WAKE_EN);
+ HW_RTC_CTRL_SET(BM_RTC_CTRL_ALARM_IRQ_EN);
+ break;
+ case RTC_UIE_ON:
+ data->irq_count = 0;
+ HW_RTC_CTRL_SET(BM_RTC_CTRL_ONEMSEC_IRQ_EN);
+ break;
+ case RTC_UIE_OFF:
+ HW_RTC_CTRL_CLR(BM_RTC_CTRL_ONEMSEC_IRQ_EN);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ u32 t = HW_RTC_ALARM_RD();
+
+ rtc_time_to_tm(t, &alm->time);
+
+ return 0;
+}
+
+static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned long t;
+
+ rtc_tm_to_time(&alm->time, &t);
+ HW_RTC_ALARM_WR(t);
+ return 0;
+}
+
+static struct rtc_class_ops stmp3xxx_rtc_ops = {
+ .open = stmp3xxx_rtc_open,
+ .release = stmp3xxx_rtc_release,
+ .ioctl = stmp3xxx_rtc_ioctl,
+ .read_time = stmp3xxx_rtc_gettime,
+ .set_time = stmp3xxx_rtc_settime,
+ .read_alarm = stmp3xxx_rtc_read_alarm,
+ .set_alarm = stmp3xxx_rtc_set_alarm,
+};
+
+static int stmp3xxx_rtc_remove(struct platform_device *dev)
+{
+ struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev);
+
+ if (rtc_data) {
+ rtc_device_unregister(rtc_data->rtc);
+ kfree(rtc_data);
+ }
+
+ return 0;
+}
+
+static int stmp3xxx_rtc_probe(struct platform_device *pdev)
+{
+ u32 hwversion = HW_RTC_VERSION_RD();
+ u32 rtc_stat = HW_RTC_STAT_RD();
+ struct stmp3xxx_rtc_data *rtc_data = kzalloc(sizeof *rtc_data,
+ GFP_KERNEL);
+
+ if ((rtc_stat & BM_RTC_STAT_RTC_PRESENT) == 0)
+ return -ENODEV;
+ if (!rtc_data)
+ return -ENOMEM;
+
+ stmp3xxx_reset_block(REGS_RTC_BASE, 1);
+
+ HW_RTC_PERSISTENT0_CLR(BM_RTC_PERSISTENT0_ALARM_EN |
+ BM_RTC_PERSISTENT0_ALARM_WAKE_EN |
+ BM_RTC_PERSISTENT0_ALARM_WAKE);
+
+ printk(KERN_INFO "STMP3xxx RTC driver v1.0 hardware v%u.%u.%u\n",
+ (hwversion >> 24),
+ (hwversion >> 16) & 0xFF,
+ hwversion & 0xFFFF);
+
+ rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &stmp3xxx_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc_data->rtc)) {
+ kfree(rtc_data);
+ return PTR_ERR(rtc_data->rtc);
+ }
+
+ platform_set_drvdata(pdev, rtc_data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stmp3xxx_rtc_suspend(struct platform_device *dev, pm_message_t state)
+{
+ return 0;
+}
+
+static int stmp3xxx_rtc_resume(struct platform_device *dev)
+{
+ stmp3xxx_reset_block(REGS_RTC_BASE, 1);
+ HW_RTC_PERSISTENT0_CLR(BM_RTC_PERSISTENT0_ALARM_EN |
+ BM_RTC_PERSISTENT0_ALARM_WAKE_EN |
+ BM_RTC_PERSISTENT0_ALARM_WAKE);
+ return 0;
+}
+#else
+#define stmp3xxx_rtc_suspend NULL
+#define stmp3xxx_rtc_resume NULL
+#endif
+
+static struct platform_driver stmp3xxx_rtcdrv = {
+ .probe = stmp3xxx_rtc_probe,
+ .remove = stmp3xxx_rtc_remove,
+ .suspend = stmp3xxx_rtc_suspend,
+ .resume = stmp3xxx_rtc_resume,
+ .driver = {
+ .name = "stmp3xxx-rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmp3xxx_rtc_init(void)
+{
+ return platform_driver_register(&stmp3xxx_rtcdrv);
+}
+
+static void __exit stmp3xxx_rtc_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_rtcdrv);
+}
+
+module_init(stmp3xxx_rtc_init);
+module_exit(stmp3xxx_rtc_exit);
+
+MODULE_DESCRIPTION("STMP3xxx RTC Driver");
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 403ecad48d4b..be8272b22341 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -257,10 +257,19 @@ config SCSI_SCAN_ASYNC
or async on the kernel's command line.
config SCSI_WAIT_SCAN
- tristate
+ tristate "create module which waits for SCSI scanning to finish"
default m
depends on SCSI
depends on MODULES
+ help
+ When this module is loaded, it will do nothing else than wait for
+ SCSI low-level drivers to finish asynchronous scanning for devices.
+ This module will be called scsi_wait_scan.
+
+ It's default and safe to create this module even if it's not used
+ in your distro's init scripts. You can override this if you are
+ really sure you don't need or want it. It does not make sense to
+ compile this into the kernel with Y.
menu "SCSI Transports"
depends on SCSI
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 303272af386e..45412dff2c72 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -862,6 +862,10 @@ static void autoconfig_16550a(struct uart_8250_port *up)
* Check for presence of the EFR when DLAB is set.
* Only ST16C650V1 UARTs pass this test.
*/
+#ifndef CONFIG_ARCH_MXC
+ /* This test fails as EFR reads 0, but our uart requires LCR=0xBF
+ * to access EFR.
+ */
serial_outp(up, UART_LCR, UART_LCR_DLAB);
if (serial_in(up, UART_EFR) == 0) {
serial_outp(up, UART_EFR, 0xA8);
@@ -875,6 +879,7 @@ static void autoconfig_16550a(struct uart_8250_port *up)
serial_outp(up, UART_EFR, 0);
return;
}
+#endif
/*
* Maybe it requires 0xbf to be written to the LCR.
@@ -1397,7 +1402,12 @@ static void transmit_chars(struct uart_8250_port *up)
count = up->tx_loadsz;
do {
+#ifdef CONFIG_ARCH_MXC
+ /* Seems like back-to-back accesses are a problem */
+ serial_out_sync(up, UART_TX, xmit->buf[xmit->tail]);
+#else
serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+#endif
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
up->port.icount.tx++;
if (uart_circ_empty(xmit))
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 579d63a81aa2..4ef33d8623a8 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -304,6 +304,31 @@ config SERIAL_AMBA_PL010_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
+config SERIAL_MXC
+ tristate "MXC Internal serial port support"
+ depends on ARCH_MXC
+ select SERIAL_CORE
+ help
+ This selects the Freescale Semiconductor MXC Internal UART driver.
+ If unsure, say N.
+
+config SERIAL_MXC_CONSOLE
+ bool "Support for console on a MXC/MX27/MX21 Internal serial port"
+ depends on SERIAL_MXC=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Say Y here if you wish to use an MXC Internal UART as the system
+ console (the system console is the device which receives all kernel
+ messages and warnings and which allows logins in single user mode).
+
+ Even if you say Y here, the currently visible framebuffer console
+ (/dev/tty0) will still be used as the system console by default, but
+ you can alter that using a kernel command line option such as
+ "console=ttymxc". (Try "man bootparam" or see the documentation of
+ your boot loader (lilo or loadlin) about how to pass options to the
+ kernel at boot time.)
+
+
config SERIAL_AMBA_PL011
tristate "ARM AMBA PL011 serial port support"
depends on ARM_AMBA
@@ -508,6 +533,62 @@ config SERIAL_S3C2440
help
Serial port support for the Samsung S3C2440 and S3C2442 SoC
+config SERIAL_S3C2410_ENABLE_PORTA
+ bool "Enable the port A support on the S3C24XX"
+ depends on SERIAL_S3C2410 || SERIAL_S3C2440
+ default y
+ help
+ Enable the serial port A of the S3C24XX
+
+config SERIAL_S3C2410_ENABLE_PORTB
+ bool "Enable the port B support on the S3C24XX"
+ depends on SERIAL_S3C2410 || SERIAL_S3C2440
+ default y
+ help
+ Enable the serial port B of the S3C24XX
+
+config SERIAL_S3C2410_ENABLE_PORTC
+ bool "Enable the port C support on the S3C24XX"
+ depends on SERIAL_S3C2410 || SERIAL_S3C2440
+ default y
+ help
+ Enable the serial port C of the S3C24XX
+
+config SERIAL_S3C2410_ENABLE_PORTD
+ bool "Enable the port D support on the S3C24XX"
+ depends on SERIAL_S3C2410 || SERIAL_S3C2440
+ default y
+ help
+ Enable the serial port D of the S3C24XX. Enabling this option will
+ disable the HW flow control of the port C.
+
+config SERIAL_S3C2410_HWCTRL
+ bool "Enable the HW flow control on the S3C24XX ports"
+ depends on SERIAL_S3C2410 || SERIAL_S3C2440
+ help
+ Enables the support for the hardware handshake on the S3C24XX
+
+config SERIAL_S3C2410_PORTA_HWCTRL
+ bool "Enable HW flow control of the port A"
+ depends on (SERIAL_S3C2410 || SERIAL_S3C2440) && SERIAL_S3C2410_HWCTRL && SERIAL_S3C2410_ENABLE_PORTA
+ default n
+ help
+ Enable the HW flow control on the port A of the S3C24XX
+
+config SERIAL_S3C2410_PORTB_HWCTRL
+ bool "Enable HW flow control of the port B"
+ depends on (SERIAL_S3C2410 || SERIAL_S3C2440) && SERIAL_S3C2410_HWCTRL && SERIAL_S3C2410_ENABLE_PORTB
+ default n
+ help
+ Enable the HW flow control on the port B of the S3C24XX
+
+config SERIAL_S3C2410_PORTC_HWCTRL
+ bool "Enable HW flow control of the port C"
+ depends on (SERIAL_S3C2410 || SERIAL_S3C2440) && SERIAL_S3C2410_HWCTRL && \
+ SERIAL_S3C2410_ENABLE_PORTC && !SERIAL_S3C2410_ENABLE_PORTD
+ default n
+ help
+ Enable the HW flow control on the port C of the S3C24XX
config SERIAL_DZ
@@ -1372,4 +1453,77 @@ config SPORT_BAUD_RATE
default 19200 if (SERIAL_SPORT_BAUD_RATE_19200)
default 9600 if (SERIAL_SPORT_BAUD_RATE_9600)
+config SERIAL_NS921X
+ tristate "Digi NS921x serial port support"
+ depends on ARM && ARCH_NS9XXX && PROCESSOR_NS921X
+ select SERIAL_CORE
+ help
+ If you have a Digi NS921x based system and wish to use its serial
+ ports, say Y or M here
+
+config SERIAL_NS921X_CONSOLE
+ bool "Console on Digi NS921x serial"
+ depends on SERIAL_NS921X=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Console support for serial ports of Digi NS921x based systems
+
+config SERIAL_NS9360
+ tristate "Digi NS9360 serial port support"
+ depends on ARM && ARCH_NS9XXX && PROCESSOR_NS9360
+ select SERIAL_CORE
+ help
+ If you have a Digi NS9360 based system and wish to use its serial
+ ports, say Y or M here
+
+config SERIAL_NS9360_CONSOLE
+ bool "Console on Digi NS9360 serial"
+ depends on SERIAL_NS9360=y
+ select SERIAL_CORE_CONSOLE
+ help
+ Console support for serial ports of Digi NS9360 based systems
+
+config SERIAL_NS9360_COMPAT
+ bool "Keep backward compatility"
+ depends on SERIAL_NS9360
+ default y
+ help
+ The serial driver for ns9360 used ttyS device node in the past
+ which leads to problems, if there are also 8215 compatible serial
+ ports in the system. In newer versions this changed to ttyNS.
+ Keep this config enabled to still get the old device names.
+ If you are sure your bootloader can handle this feel free
+ to dectivate this.
+
+config SERIAL_STMP_DBG
+ tristate "STMP debug serial port support"
+ depends on ARCH_STMP3XXX
+ select SERIAL_CORE
+ help
+ Driver for Sigmatel 36XX/37XX internal debug serial port
+
+config SERIAL_STMP_DBG_CONSOLE
+ bool "Support for console on STMP37XX DBG serial port"
+ depends on SERIAL_STMP_DBG=y
+ select SERIAL_CORE_CONSOLE
+ ---help---
+ Say Y here if you wish to use the STMP36XX/37XX debug serial port as the
+ system console (the system console is the device which receives all
+ kernel messages and warnings and which allows logins in single user
+ mode).
+
+ Even if you say Y here, the currently visible framebuffer console
+ (/dev/tty0) will still be used as the system console by default, but
+ you can alter that using a kernel command line option such as
+ "console=ttyAM0". (Try "man bootparam" or see the documentation of
+ your boot loader (lilo or loadlin) about how to pass options to the
+ kernel at boot time.)
+
+config SERIAL_STMP_APP
+ tristate "STMP app serial port support"
+ depends on ARCH_STMP3XXX
+ select SERIAL_CORE
+ help
+ Driver for Sigmatel 36XX/37XX internal application serial port
+
endmenu
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 0c17c8ddb19d..e5a112b79d79 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -69,7 +69,13 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
+obj-$(CONFIG_SERIAL_MXC) += mxc_uart.o
+obj-$(CONFIG_SERIAL_MXC_CONSOLE) += mxc_uart_early.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
+obj-$(CONFIG_SERIAL_NS921X) += ns921x-serial.o
+obj-$(CONFIG_SERIAL_NS9360) += ns9360-serial.o
+obj-$(CONFIG_SERIAL_STMP_DBG) += stmp-dbg.o
+obj-$(CONFIG_SERIAL_STMP_APP) += stmp-app.o
diff --git a/drivers/serial/mxc_uart.c b/drivers/serial/mxc_uart.c
new file mode 100644
index 000000000000..6827590a605d
--- /dev/null
+++ b/drivers/serial/mxc_uart.c
@@ -0,0 +1,1948 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file drivers/serial/mxc_uart.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC serial ports based on
+ * drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ * @ingroup UART
+ */
+
+/*
+ * Include Files
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/platform_device.h>
+#include <linux/sysrq.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/div64.h>
+#include <mach/hardware.h>
+#include <mach/mxc_uart.h>
+
+#if defined(CONFIG_SERIAL_MXC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+#define SERIAL_MXC_MAJOR 207
+#define SERIAL_MXC_MINOR 16
+#define MXC_ISR_PASS_LIMIT 256
+#define UART_CREAD_BIT 256
+
+/* IRDA minimum pulse duration in micro seconds */
+#define MIN_PULSE_DUR 2
+/*
+ * Transmit DMA buffer size is set to 1024 bytes, this is limited
+ * by UART_XMIT_SIZE.
+ */
+#define TXDMA_BUFF_SIZE UART_XMIT_SIZE
+/*
+ * Receive DMA sub-buffer size
+ */
+#define RXDMA_BUFF_SIZE 128
+
+/*!
+ * This structure is used to store the information for DMA data transfer.
+ */
+typedef struct {
+ /*!
+ * Holds the read channel number.
+ */
+ int rd_channel;
+ /*!
+ * Holds the write channel number.
+ */
+ int wr_channel;
+ /*!
+ * UART Transmit Event ID
+ */
+ int tx_event_id;
+ /*!
+ * UART Receive Event ID
+ */
+ int rx_event_id;
+ /*!
+ * DMA Transmit tasklet
+ */
+ struct tasklet_struct dma_tx_tasklet;
+ /*!
+ * Flag indicates if the channel is in use
+ */
+ int dma_txchnl_inuse;
+} dma_info;
+
+/*!
+ * This is used to indicate if we want echo cancellation in the Irda mode.
+ */
+static int echo_cancel;
+extern void gpio_uart_active(int port, int no_irda);
+extern void gpio_uart_inactive(int port, int no_irda);
+extern void config_uartdma_event(int port);
+
+static uart_mxc_port *mxc_ports[MXC_UART_NR];
+
+/*!
+ * This array holds the DMA channel information for each MXC UART
+ */
+static dma_info dma_list[MXC_UART_NR];
+
+/*!
+ * This function is called by the core driver to stop UART transmission.
+ * This might be due to the TTY layer indicating that the user wants to stop
+ * transmission.
+ *
+ * @param port the port structure for the UART passed in by the core
+ * driver
+ */
+static void mxcuart_stop_tx(struct uart_port *port)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) port;
+ volatile unsigned int cr1;
+
+ cr1 = readl(port->membase + MXC_UARTUCR1);
+ /* Disable Transmitter rdy interrupt */
+ if (umxc->dma_enabled == 1) {
+ cr1 &= ~MXC_UARTUCR1_TXDMAEN;
+ } else {
+ cr1 &= ~MXC_UARTUCR1_TRDYEN;
+ }
+ writel(cr1, port->membase + MXC_UARTUCR1);
+}
+
+/*!
+ * DMA Transmit tasklet method is scheduled on completion of a DMA transmit
+ * to send out any more data that is available in the UART xmit buffer.
+ *
+ * @param arg driver private data
+ */
+static void dma_tx_do_tasklet(unsigned long arg)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) arg;
+ struct circ_buf *xmit = &umxc->port.info->xmit;
+ mxc_dma_requestbuf_t writechnl_request;
+ int tx_num;
+ unsigned long flags;
+
+ spin_lock_irqsave(&umxc->port.lock, flags);
+ tx_num = uart_circ_chars_pending(xmit);
+ if (tx_num > 0) {
+ if (xmit->tail > xmit->head) {
+ memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
+ UART_XMIT_SIZE - xmit->tail);
+ memcpy(umxc->tx_buf + (UART_XMIT_SIZE - xmit->tail),
+ xmit->buf, xmit->head);
+ } else {
+ memcpy(umxc->tx_buf, xmit->buf + xmit->tail, tx_num);
+ }
+ umxc->tx_handle = dma_map_single(umxc->port.dev, umxc->tx_buf,
+ TXDMA_BUFF_SIZE,
+ DMA_TO_DEVICE);
+
+ writechnl_request.dst_addr = umxc->port.mapbase + MXC_UARTUTXD;
+ writechnl_request.src_addr = umxc->tx_handle;
+ writechnl_request.num_of_bytes = tx_num;
+
+ if ((mxc_dma_config(dma_list[umxc->port.line].wr_channel,
+ &writechnl_request, 1,
+ MXC_DMA_MODE_WRITE)) == 0) {
+ mxc_dma_enable(dma_list[umxc->port.line].wr_channel);
+ }
+ } else {
+ /* No more data available in the xmit queue, clear the flag */
+ dma_list[umxc->port.line].dma_txchnl_inuse = 0;
+ }
+ spin_unlock_irqrestore(&umxc->port.lock, flags);
+}
+
+/*!
+ * DMA Write callback is called by the SDMA controller after it has sent out all
+ * the data from the user buffer. This function updates the xmit buffer pointers.
+ *
+ * @param arg driver private data
+ * @param error any DMA error
+ * @param count amount of data that was transferred
+ */
+static void mxcuart_dma_writecallback(void *arg, int error, unsigned int count)
+{
+ uart_mxc_port *umxc = arg;
+ struct circ_buf *xmit = &umxc->port.info->xmit;
+ int tx_num;
+
+ if (error != MXC_DMA_TRANSFER_ERROR) {
+ tx_num = count;
+ umxc->port.icount.tx += tx_num;
+ xmit->tail = (xmit->tail + tx_num) & (UART_XMIT_SIZE - 1);
+ }
+
+ dma_unmap_single(umxc->port.dev, umxc->tx_handle, TXDMA_BUFF_SIZE,
+ DMA_TO_DEVICE);
+ tx_num = uart_circ_chars_pending(xmit);
+ /* Schedule a tasklet to send out the pending characters */
+ if (tx_num > 0) {
+ tasklet_schedule(&dma_list[umxc->port.line].dma_tx_tasklet);
+ } else {
+ dma_list[umxc->port.line].dma_txchnl_inuse = 0;
+ }
+ if (tx_num < WAKEUP_CHARS) {
+ uart_write_wakeup(&umxc->port);
+ }
+}
+
+/*!
+ * This function is called by the core driver to start transmitting characters.
+ * This function enables the transmit interrupts.
+ *
+ * @param port the port structure for the UART passed in by the core
+ * driver
+ */
+static void mxcuart_start_tx(struct uart_port *port)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) port;
+ struct circ_buf *xmit = &umxc->port.info->xmit;
+ volatile unsigned int cr1;
+ mxc_dma_requestbuf_t writechnl_request;
+ int tx_num;
+
+ cr1 = readl(port->membase + MXC_UARTUCR1);
+ /* Enable Transmitter rdy interrupt */
+ if (umxc->dma_enabled == 1) {
+ /*
+ * If the channel is in use then return immediately and use
+ * the dma_tx tasklet to transfer queued data when current DMA
+ * transfer is complete
+ */
+ if (dma_list[umxc->port.line].dma_txchnl_inuse == 1) {
+ return;
+ }
+ tx_num = uart_circ_chars_pending(xmit);
+ if (tx_num > 0) {
+ dma_list[umxc->port.line].dma_txchnl_inuse = 1;
+ if (xmit->tail > xmit->head) {
+ memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
+ UART_XMIT_SIZE - xmit->tail);
+ memcpy(umxc->tx_buf +
+ (UART_XMIT_SIZE - xmit->tail), xmit->buf,
+ xmit->head);
+ } else {
+ memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
+ tx_num);
+ }
+ umxc->tx_handle =
+ dma_map_single(umxc->port.dev, umxc->tx_buf,
+ TXDMA_BUFF_SIZE, DMA_TO_DEVICE);
+
+ writechnl_request.dst_addr =
+ umxc->port.mapbase + MXC_UARTUTXD;
+ writechnl_request.src_addr = umxc->tx_handle;
+ writechnl_request.num_of_bytes = tx_num;
+ if ((mxc_dma_config
+ (dma_list[umxc->port.line].wr_channel,
+ &writechnl_request, 1,
+ MXC_DMA_MODE_WRITE)) == 0) {
+ mxc_dma_enable(dma_list[umxc->port.line].
+ wr_channel);
+ }
+ cr1 |= MXC_UARTUCR1_TXDMAEN;
+ }
+ } else {
+ cr1 |= MXC_UARTUCR1_TRDYEN;
+ }
+ writel(cr1, port->membase + MXC_UARTUCR1);
+}
+
+/*!
+ * This function is called by the core driver to stop receiving characters; the
+ * port is in the process of being closed.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ */
+static void mxcuart_stop_rx(struct uart_port *port)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) port;
+ volatile unsigned int cr1;
+
+ cr1 = readl(port->membase + MXC_UARTUCR1);
+ if (umxc->dma_enabled == 1) {
+ cr1 &= ~MXC_UARTUCR1_RXDMAEN;
+ } else {
+ cr1 &= ~MXC_UARTUCR1_RRDYEN;
+ }
+ writel(cr1, port->membase + MXC_UARTUCR1);
+}
+
+/*!
+ * This function is called by the core driver to enable the modem status
+ * interrupts. If the port is configured to be in DTE mode then it enables the
+ * DCDDELT and RIDELT interrupts in addition to the DTRDEN interrupt. The RTSDEN
+ * interrupt is enabled only for interrupt-driven hardware flow control.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ */
+static void mxcuart_enable_ms(struct uart_port *port)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) port;
+ volatile unsigned int cr1, cr3;
+
+ /*
+ * RTS interrupt is enabled only if we are using interrupt-driven
+ * software controlled hardware flow control
+ */
+ if (umxc->hardware_flow == 0) {
+ cr1 = readl(umxc->port.membase + MXC_UARTUCR1);
+ cr1 |= MXC_UARTUCR1_RTSDEN;
+ writel(cr1, umxc->port.membase + MXC_UARTUCR1);
+ }
+ cr3 = readl(umxc->port.membase + MXC_UARTUCR3);
+ cr3 |= MXC_UARTUCR3_DTRDEN;
+ if (umxc->mode == MODE_DTE) {
+ cr3 |= MXC_UARTUCR3_DCD | MXC_UARTUCR3_RI;
+ }
+ writel(cr3, umxc->port.membase + MXC_UARTUCR3);
+}
+
+/*!
+ * This function is called from the interrupt service routine if the status bit
+ * indicates that the receive fifo data level is above the set threshold. The
+ * function reads the character and queues them into the TTY layers read
+ * buffer. The function also looks for break characters, parity and framing
+ * errors in the received character and sets the appropriate flag in the TTY
+ * receive buffer.
+ *
+ * @param umxc the MXC UART port structure, this includes the \b uart_port
+ * structure and other members that are specific to MXC UARTs
+ */
+static void mxcuart_rx_chars(uart_mxc_port * umxc)
+{
+ volatile unsigned int ch, sr2;
+ unsigned int status, flag, max_count = 256;
+
+ sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+ while (((sr2 & MXC_UARTUSR2_RDR) == 1) && (max_count-- > 0)) {
+ ch = readl(umxc->port.membase + MXC_UARTURXD);
+
+ flag = TTY_NORMAL;
+ status = ch | UART_CREAD_BIT;
+ ch &= 0xFF; /* Clear the upper bits */
+ umxc->port.icount.rx++;
+
+ /*
+ * Check to see if there is an error in the received
+ * character. Perform the appropriate actions based on the
+ * error bit that was set.
+ */
+ if (status & MXC_UARTURXD_ERR) {
+ if (status & MXC_UARTURXD_BRK) {
+ /*
+ * Clear the frame and parity error bits
+ * as these always get set on receiving a
+ * break character
+ */
+ status &= ~(MXC_UARTURXD_FRMERR |
+ MXC_UARTURXD_PRERR);
+ umxc->port.icount.brk++;
+ if (uart_handle_break(&umxc->port)) {
+ goto ignore_char;
+ }
+ } else if (status & MXC_UARTURXD_FRMERR) {
+ umxc->port.icount.frame++;
+ } else if (status & MXC_UARTURXD_PRERR) {
+ umxc->port.icount.parity++;
+ }
+ if (status & MXC_UARTURXD_OVRRUN) {
+ umxc->port.icount.overrun++;
+ }
+
+ status &= umxc->port.read_status_mask;
+
+ if (status & MXC_UARTURXD_BRK) {
+ flag = TTY_BREAK;
+ } else if (status & MXC_UARTURXD_FRMERR) {
+ flag = TTY_FRAME;
+ } else if (status & MXC_UARTURXD_PRERR) {
+ flag = TTY_PARITY;
+ }
+ }
+
+ if (uart_handle_sysrq_char(&umxc->port, ch)) {
+ goto ignore_char;
+ }
+
+ uart_insert_char(&umxc->port, status, MXC_UARTURXD_OVRRUN, ch,
+ flag);
+ ignore_char:
+ sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+ }
+ tty_flip_buffer_push(umxc->port.info->port.tty);
+}
+
+/*!
+ * This function is called from the interrupt service routine if the status bit
+ * indicates that the transmit fifo is emptied below its set threshold and
+ * requires data. The function pulls characters from the TTY layers write
+ * buffer and writes it out to the UART transmit fifo.
+ *
+ * @param umxc the MXC UART port structure, this includes the \b uart_port
+ * structure and other members that are specific to MXC UARTs
+ */
+static void mxcuart_tx_chars(uart_mxc_port * umxc)
+{
+ struct circ_buf *xmit = &umxc->port.info->xmit;
+ int count;
+
+ /*
+ * Transmit the XON/XOFF character if required
+ */
+ if (umxc->port.x_char) {
+ writel(umxc->port.x_char, umxc->port.membase + MXC_UARTUTXD);
+ umxc->port.icount.tx++;
+ umxc->port.x_char = 0;
+ return;
+ }
+
+ /*
+ * Check to see if there is any data to be sent and that the
+ * port has not been currently stopped by anything.
+ */
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&umxc->port)) {
+ mxcuart_stop_tx(&umxc->port);
+ return;
+ }
+
+ count = umxc->port.fifosize - umxc->tx_threshold;
+ do {
+ writel(xmit->buf[xmit->tail],
+ umxc->port.membase + MXC_UARTUTXD);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ umxc->port.icount.tx++;
+ if (uart_circ_empty(xmit)) {
+ break;
+ }
+ } while (--count > 0);
+
+ /*
+ * Check to see if we have flushed enough characters to ask for more
+ * to be sent to us, if so, we notify the user space that we can
+ * accept more data
+ */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
+ uart_write_wakeup(&umxc->port);
+ }
+
+ if (uart_circ_empty(xmit)) {
+ mxcuart_stop_tx(&umxc->port);
+ }
+}
+
+/*!
+ * This function is called from the interrupt service routine if there is a
+ * change in the modem signals. This function handles these signal changes and
+ * also clears the appropriate status register bits.
+ *
+ * @param umxc the MXC UART port structure, this includes the \b uart_port
+ * structure and other members that are specific to MXC UARTs
+ * @param sr1 contents of status register 1
+ * @param sr2 contents of status register 2
+ */
+static void mxcuart_modem_status(uart_mxc_port * umxc, unsigned int sr1,
+ unsigned int sr2)
+{
+ if (umxc->mode == MODE_DTE) {
+ if (sr2 & MXC_UARTUSR2_DCDDELT) {
+ uart_handle_dcd_change(&umxc->port,
+ !(sr2 & MXC_UARTUSR2_DCDIN));
+ }
+ if (sr2 & MXC_UARTUSR2_RIDELT) {
+ umxc->port.icount.rng++;
+ }
+ }
+ if (sr1 & MXC_UARTUSR1_DTRD) {
+ umxc->port.icount.dsr++;
+ }
+ if ((umxc->hardware_flow == 0) && (sr1 & MXC_UARTUSR1_RTSD)) {
+ uart_handle_cts_change(&umxc->port, sr1 & MXC_UARTUSR1_RTSS);
+ }
+
+ wake_up_interruptible(&umxc->port.info->delta_msr_wait);
+}
+
+/*!
+ * Interrupt service routine registered to handle the muxed ANDed interrupts.
+ * This routine is registered only in the case where the UART interrupts are
+ * muxed.
+ *
+ * @param irq the interrupt number
+ * @param dev_id driver private data
+ *
+ * @return The function returns \b IRQ_RETVAL(1) if interrupt was handled,
+ * returns \b IRQ_RETVAL(0) if the interrupt was not handled.
+ * \b IRQ_RETVAL is defined in \b include/linux/interrupt.h.
+ */
+static irqreturn_t mxcuart_int(int irq, void *dev_id)
+{
+ uart_mxc_port *umxc = dev_id;
+ volatile unsigned int sr1, sr2, cr1, cr;
+ unsigned int pass_counter = MXC_ISR_PASS_LIMIT;
+ unsigned int term_cond = 0;
+ int handled = 0;
+
+ sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
+ sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+ cr1 = readl(umxc->port.membase + MXC_UARTUCR1);
+
+ do {
+ /* Clear the bits that triggered the interrupt */
+ writel(sr1, umxc->port.membase + MXC_UARTUSR1);
+ writel(sr2, umxc->port.membase + MXC_UARTUSR2);
+ /*
+ * Read if there is data available
+ */
+ if (sr2 & MXC_UARTUSR2_RDR) {
+ mxcuart_rx_chars(umxc);
+ }
+
+ if ((sr1 & (MXC_UARTUSR1_RTSD | MXC_UARTUSR1_DTRD)) ||
+ (sr2 & (MXC_UARTUSR2_DCDDELT | MXC_UARTUSR2_RIDELT))) {
+ mxcuart_modem_status(umxc, sr1, sr2);
+ }
+
+ /*
+ * Send data if there is data to be sent
+ */
+ if ((cr1 & MXC_UARTUCR1_TRDYEN) && (sr1 & MXC_UARTUSR1_TRDY)) {
+ /* Echo cancellation for IRDA Transmit chars */
+ if (umxc->ir_mode == IRDA && echo_cancel) {
+ /* Disable the receiver */
+ cr = readl(umxc->port.membase + MXC_UARTUCR2);
+ cr &= ~MXC_UARTUCR2_RXEN;
+ writel(cr, umxc->port.membase + MXC_UARTUCR2);
+ /* Enable Transmit complete intr to reenable RX */
+ cr = readl(umxc->port.membase + MXC_UARTUCR4);
+ cr |= MXC_UARTUCR4_TCEN;
+ writel(cr, umxc->port.membase + MXC_UARTUCR4);
+ }
+ mxcuart_tx_chars(umxc);
+ }
+
+ if (pass_counter-- == 0) {
+ break;
+ }
+
+ sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
+ sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+
+ /* Is the transmit complete to reenable the receiver? */
+ if (umxc->ir_mode == IRDA && echo_cancel) {
+ if (sr2 & MXC_UARTUSR2_TXDC) {
+ cr = readl(umxc->port.membase + MXC_UARTUCR2);
+ cr |= MXC_UARTUCR2_RXEN;
+ writel(cr, umxc->port.membase + MXC_UARTUCR2);
+ /* Disable the Transmit complete interrupt bit */
+ cr = readl(umxc->port.membase + MXC_UARTUCR4);
+ cr &= ~MXC_UARTUCR4_TCEN;
+ writel(cr, umxc->port.membase + MXC_UARTUCR4);
+ }
+ }
+
+ /*
+ * If there is no data to send or receive and if there is no
+ * change in the modem status signals then quit the routine
+ */
+ term_cond = sr1 & (MXC_UARTUSR1_RTSD | MXC_UARTUSR1_DTRD);
+ term_cond |= sr2 & (MXC_UARTUSR2_RDR | MXC_UARTUSR2_DCDDELT);
+ term_cond |= !(sr2 & MXC_UARTUSR2_TXFE);
+ } while (term_cond > 0);
+
+ handled = 1;
+ return IRQ_RETVAL(handled);
+}
+
+/*!
+ * Interrupt service routine registered to handle the transmit interrupts. This
+ * routine is registered only in the case where the UART interrupts are not
+ * muxed.
+ *
+ * @param irq the interrupt number
+ * @param dev_id driver private data
+ *
+ * @return The function returns \b IRQ_RETVAL(1) if interrupt was handled,
+ * returns \b IRQ_RETVAL(0) if the interrupt was not handled.
+ * \b IRQ_RETVAL is defined in include/linux/interrupt.h.
+ */
+static irqreturn_t mxcuart_tx_int(int irq, void *dev_id)
+{
+ uart_mxc_port *umxc = dev_id;
+ int handled = 0;
+ volatile unsigned int sr2, cr;
+
+ /* Echo cancellation for IRDA Transmit chars */
+ if (umxc->ir_mode == IRDA && echo_cancel) {
+ /* Disable the receiver */
+ cr = readl(umxc->port.membase + MXC_UARTUCR2);
+ cr &= ~MXC_UARTUCR2_RXEN;
+ writel(cr, umxc->port.membase + MXC_UARTUCR2);
+ /* Enable Transmit complete to reenable receiver */
+ cr = readl(umxc->port.membase + MXC_UARTUCR4);
+ cr |= MXC_UARTUCR4_TCEN;
+ writel(cr, umxc->port.membase + MXC_UARTUCR4);
+ }
+
+ mxcuart_tx_chars(umxc);
+
+ /* Is the transmit complete to reenable the receiver? */
+ if (umxc->ir_mode == IRDA && echo_cancel) {
+ sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+ if (sr2 & MXC_UARTUSR2_TXDC) {
+ cr = readl(umxc->port.membase + MXC_UARTUCR2);
+ cr |= MXC_UARTUCR2_RXEN;
+ writel(cr, umxc->port.membase + MXC_UARTUCR2);
+ /* Disable the Transmit complete interrupt bit */
+ cr = readl(umxc->port.membase + MXC_UARTUCR4);
+ cr &= ~MXC_UARTUCR4_TCEN;
+ writel(cr, umxc->port.membase + MXC_UARTUCR4);
+ }
+ }
+
+ handled = 1;
+
+ return IRQ_RETVAL(handled);
+}
+
+/*!
+ * Interrupt service routine registered to handle the receive interrupts. This
+ * routine is registered only in the case where the UART interrupts are not
+ * muxed.
+ *
+ * @param irq the interrupt number
+ * @param dev_id driver private data
+ *
+ * @return The function returns \b IRQ_RETVAL(1) if interrupt was handled,
+ * returns \b IRQ_RETVAL(0) if the interrupt was not handled.
+ * \b IRQ_RETVAL is defined in include/linux/interrupt.h.
+ */
+static irqreturn_t mxcuart_rx_int(int irq, void *dev_id)
+{
+ uart_mxc_port *umxc = dev_id;
+ int handled = 0;
+
+ /* Clear the aging timer bit */
+ writel(MXC_UARTUSR1_AGTIM, umxc->port.membase + MXC_UARTUSR1);
+ mxcuart_rx_chars(umxc);
+ handled = 1;
+
+ return IRQ_RETVAL(handled);
+}
+
+/*!
+ * Interrupt service routine registered to handle the master interrupts. This
+ * routine is registered only in the case where the UART interrupts are not
+ * muxed.
+ *
+ * @param irq the interrupt number
+ * @param dev_id driver private data
+ *
+ * @return The function returns \b IRQ_RETVAL(1) if interrupt was handled,
+ * returns \b IRQ_RETVAL(0) if the interrupt was not handled.
+ * \b IRQ_RETVAL is defined in include/linux/interrupt.h.
+ */
+static irqreturn_t mxcuart_mint_int(int irq, void *dev_id)
+{
+ uart_mxc_port *umxc = dev_id;
+ int handled = 0;
+ volatile unsigned int sr1, sr2;
+
+ sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
+ sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+ /* Clear the modem status interrupt bits */
+ writel(MXC_UARTUSR1_RTSD | MXC_UARTUSR1_DTRD,
+ umxc->port.membase + MXC_UARTUSR1);
+ writel(MXC_UARTUSR2_DCDDELT | MXC_UARTUSR2_RIDELT,
+ umxc->port.membase + MXC_UARTUSR2);
+ mxcuart_modem_status(umxc, sr1, sr2);
+ handled = 1;
+
+ return IRQ_RETVAL(handled);
+}
+
+/*!
+ * This function is called by the core driver to test whether the transmitter
+ * fifo and shift register for the UART port are empty.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ *
+ * @return The function returns TIOCSER_TEMT if it is empty, else returns 0.
+ */
+static unsigned int mxcuart_tx_empty(struct uart_port *port)
+{
+ volatile unsigned int sr2;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ sr2 = readl(port->membase + MXC_UARTUSR2);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return sr2 & MXC_UARTUSR2_TXDC ? TIOCSER_TEMT : 0;
+}
+
+/*!
+ * This function is called by the core driver to get the current status of the
+ * modem input signals. The state of the output signals is not collected.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ *
+ * @return The function returns an integer that contains the ORed value of the
+ * status of all the modem input signals or error.
+ */
+static unsigned int mxcuart_get_mctrl(struct uart_port *port)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) port;
+ unsigned int result = 0;
+ volatile unsigned int sr1, sr2;
+
+ sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
+ sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+
+ if (sr1 & MXC_UARTUSR1_RTSS) {
+ result |= TIOCM_CTS;
+ }
+ if (umxc->mode == MODE_DTE) {
+ if (!(sr2 & MXC_UARTUSR2_DCDIN)) {
+ result |= TIOCM_CAR;
+ }
+ if (!(sr2 & MXC_UARTUSR2_RIIN)) {
+ result |= TIOCM_RI;
+ }
+ }
+ return result;
+}
+
+/*!
+ * This function is called by the core driver to set the state of the modem
+ * control lines.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ * @param mctrl the state that the modem control lines should be changed to
+ */
+static void mxcuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) port;
+ volatile unsigned int cr2 = 0, cr3 = 0, uts = 0;
+
+ cr2 = readl(port->membase + MXC_UARTUCR2);
+ cr3 = readl(port->membase + MXC_UARTUCR3);
+ uts = readl(port->membase + MXC_UARTUTS);
+
+ if (mctrl & TIOCM_RTS) {
+ /*
+ * Return to hardware-driven hardware flow control if the
+ * option is enabled
+ */
+ if (umxc->hardware_flow == 1) {
+ cr2 |= MXC_UARTUCR2_CTSC;
+ } else {
+ cr2 |= MXC_UARTUCR2_CTS;
+ cr2 &= ~MXC_UARTUCR2_CTSC;
+ }
+ } else {
+ cr2 &= ~(MXC_UARTUCR2_CTS | MXC_UARTUCR2_CTSC);
+ }
+ writel(cr2, port->membase + MXC_UARTUCR2);
+
+ if (mctrl & TIOCM_DTR) {
+ cr3 |= MXC_UARTUCR3_DSR;
+ } else {
+ cr3 &= ~MXC_UARTUCR3_DSR;
+ }
+ writel(cr3, port->membase + MXC_UARTUCR3);
+
+ if (mctrl & TIOCM_LOOP) {
+ if (umxc->ir_mode == IRDA) {
+ echo_cancel = 0;
+ } else {
+ uts |= MXC_UARTUTS_LOOP;
+ }
+ } else {
+ if (umxc->ir_mode == IRDA) {
+ echo_cancel = 1;
+ } else {
+ uts &= ~MXC_UARTUTS_LOOP;
+ }
+ }
+ writel(uts, port->membase + MXC_UARTUTS);
+}
+
+/*!
+ * This function is called by the core driver to control the transmission of
+ * the break signal. If break_state is non-zero, the break signal is
+ * transmitted, the signal is terminated when another call is made with
+ * break_state set to 0.
+ *
+ * @param port the port structure for the UART passed in by the core
+ * driver
+ * @param break_state the requested state of the break signal
+ */
+static void mxcuart_break_ctl(struct uart_port *port, int break_state)
+{
+ volatile unsigned int cr1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ cr1 = readl(port->membase + MXC_UARTUCR1);
+ if (break_state == -1) {
+ cr1 |= MXC_UARTUCR1_SNDBRK;
+ } else {
+ cr1 &= ~MXC_UARTUCR1_SNDBRK;
+ }
+ writel(cr1, port->membase + MXC_UARTUCR1);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/*!
+ * The read DMA callback, this method is called when the DMA buffer has received its
+ * data. This functions copies the data to the tty buffer and updates the tty buffer
+ * pointers. It also queues the DMA buffer back to the DMA system.
+ *
+ * @param arg driver private data
+ * @param error any DMA error
+ * @param cnt amount of data that was transferred
+ */
+static void mxcuart_dmaread_callback(void *arg, int error, unsigned int cnt)
+{
+ uart_mxc_port *umxc = arg;
+ struct tty_struct *tty = umxc->port.info->port.tty;
+ int buff_id, flip_cnt, num_bufs;
+ mxc_dma_requestbuf_t readchnl_request;
+ mxc_uart_rxdmamap *rx_buf_elem = NULL;
+ unsigned int sr1, sr2;
+ char flag;
+
+ num_bufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
+ /* Clear the aging timer bit */
+ writel(MXC_UARTUSR1_AGTIM, umxc->port.membase + MXC_UARTUSR1);
+
+ buff_id = umxc->dma_rxbuf_id;
+ flag = TTY_NORMAL;
+
+ if ((umxc->dma_rxbuf_id += 1) >= num_bufs) {
+ umxc->dma_rxbuf_id = 0;
+ }
+
+ rx_buf_elem = (mxc_uart_rxdmamap *) (umxc->rx_dmamap + buff_id);
+
+ if (error == MXC_DMA_TRANSFER_ERROR) {
+
+ sr1 = __raw_readl(umxc->port.membase + MXC_UARTUSR1);
+ sr2 = __raw_readl(umxc->port.membase + MXC_UARTUSR2);
+
+ if (sr2 & MXC_UARTUSR2_BRCD) {
+ umxc->port.icount.brk++;
+ if (uart_handle_break(&umxc->port)) {
+ goto drop_data;
+ }
+ } else if (sr1 & MXC_UARTUSR1_PARITYERR) {
+ umxc->port.icount.parity++;
+ } else if (sr1 & MXC_UARTUSR1_FRAMERR) {
+ umxc->port.icount.frame++;
+ } else if (sr2 & MXC_UARTUSR2_ORE) {
+ umxc->port.icount.overrun++;
+
+ }
+
+ if (umxc->port.read_status_mask & MXC_UARTURXD_BRK) {
+ if (sr2 & MXC_UARTUSR2_BRCD)
+ flag = TTY_BREAK;
+ } else if (umxc->port.read_status_mask & MXC_UARTURXD_PRERR) {
+ if (sr1 & MXC_UARTUSR1_PARITYERR)
+ flag = TTY_PARITY;
+ } else if (umxc->port.read_status_mask & MXC_UARTURXD_FRMERR) {
+ if (sr1 & MXC_UARTUSR1_FRAMERR)
+ flag = TTY_FRAME;
+ } else if (umxc->port.read_status_mask & MXC_UARTURXD_OVRRUN) {
+ if (sr2 & MXC_UARTUSR2_ORE)
+ flag = TTY_OVERRUN;
+ }
+/* By default clearing all error bits in status reg */
+ __raw_writel((MXC_UARTUSR2_BRCD | MXC_UARTUSR2_ORE),
+ umxc->port.membase + MXC_UARTUSR2);
+ __raw_writel((MXC_UARTUSR1_PARITYERR | MXC_UARTUSR1_FRAMERR),
+ umxc->port.membase + MXC_UARTUSR1);
+ }
+
+ flip_cnt = tty_buffer_request_room(tty, cnt);
+
+ /* Check for space availability in the TTY Flip buffer */
+ if (flip_cnt <= 0) {
+ goto drop_data;
+ }
+ umxc->port.icount.rx += flip_cnt;
+
+ tty_insert_flip_string(tty, rx_buf_elem->rx_buf, flip_cnt);
+
+ if (flag != TTY_NORMAL) {
+ tty_insert_flip_char(tty, 0, flag);
+ }
+
+ tty_flip_buffer_push(tty);
+ umxc->port.info->port.tty->real_raw = 1;
+
+ drop_data:
+ readchnl_request.src_addr = umxc->port.mapbase;
+ readchnl_request.dst_addr = rx_buf_elem->rx_handle;
+ readchnl_request.num_of_bytes = RXDMA_BUFF_SIZE;
+ mxc_dma_config(dma_list[umxc->port.line].rd_channel, &readchnl_request,
+ 1, MXC_DMA_MODE_READ);
+ mxc_dma_enable(dma_list[umxc->port.line].rd_channel);
+}
+
+/*!
+ * Allocates DMA read and write channels, creates DMA read and write buffers and
+ * sets the channel specific parameters.
+ *
+ * @param d_info the structure that holds all the DMA information for a
+ * particular MXC UART
+ * @param umxc the MXC UART port structure, this includes the \b uart_port
+ * structure and other members that are specific to MXC UARTs
+ *
+ * @return The function returns 0 on success and a non-zero value on failure.
+ */
+static int mxcuart_initdma(dma_info * d_info, uart_mxc_port * umxc)
+{
+ int ret = 0, rxbufs, i, j;
+ mxc_dma_requestbuf_t *readchnl_reqelem;
+ mxc_uart_rxdmamap *rx_buf_elem;
+
+ /* Request for the read and write channels */
+ d_info->rd_channel = mxc_dma_request(umxc->dma_rx_id, "MXC UART Read");
+ if (d_info->rd_channel < 0) {
+ printk(KERN_ERR "MXC UART: Cannot allocate DMA read channel\n");
+ return -1;
+ } else {
+ d_info->wr_channel =
+ mxc_dma_request(umxc->dma_tx_id, "MXC UART Write");
+ if (d_info->wr_channel < 0) {
+ mxc_dma_free(d_info->rd_channel);
+ printk(KERN_ERR
+ "MXC UART: Cannot allocate DMA write channel\n");
+ return -1;
+ }
+ }
+
+ /* Allocate the DMA Transmit Buffer */
+ if ((umxc->tx_buf = kmalloc(TXDMA_BUFF_SIZE, GFP_KERNEL)) == NULL) {
+ ret = -1;
+ goto err_dma_tx_buff;
+ }
+ rxbufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
+ /* Allocate the DMA Virtual Receive Buffer */
+ if ((umxc->rx_dmamap = kmalloc(rxbufs * sizeof(mxc_uart_rxdmamap),
+ GFP_KERNEL)) == NULL) {
+ ret = -1;
+ goto err_dma_rx_buff;
+ }
+
+ /* Allocate the DMA Receive Request structures */
+ if ((readchnl_reqelem =
+ kmalloc(rxbufs * sizeof(mxc_dma_requestbuf_t),
+ GFP_KERNEL)) == NULL) {
+ ret = -1;
+ goto err_request;
+ }
+
+ for (i = 0; i < rxbufs; i++) {
+ rx_buf_elem = (mxc_uart_rxdmamap *) (umxc->rx_dmamap + i);
+ rx_buf_elem->rx_buf =
+ dma_alloc_coherent(NULL, RXDMA_BUFF_SIZE,
+ &rx_buf_elem->rx_handle, GFP_DMA);
+ if (rx_buf_elem->rx_buf == NULL) {
+ for (j = 0; j < i; j++) {
+ rx_buf_elem =
+ (mxc_uart_rxdmamap *) (umxc->rx_dmamap + j);
+ dma_free_coherent(NULL, RXDMA_BUFF_SIZE,
+ rx_buf_elem->rx_buf,
+ rx_buf_elem->rx_handle);
+ }
+ ret = -1;
+ goto cleanup;
+ }
+ }
+
+ umxc->dma_rxbuf_id = 0;
+ /* Setup the DMA read request structures */
+ for (i = 0; i < rxbufs; i++) {
+ rx_buf_elem = (mxc_uart_rxdmamap *) (umxc->rx_dmamap + i);
+ (readchnl_reqelem + i)->src_addr = umxc->port.mapbase;
+ (readchnl_reqelem + i)->dst_addr = rx_buf_elem->rx_handle;
+ (readchnl_reqelem + i)->num_of_bytes = RXDMA_BUFF_SIZE;
+ }
+ mxc_dma_config(d_info->rd_channel, readchnl_reqelem, rxbufs,
+ MXC_DMA_MODE_READ);
+ mxc_dma_callback_set(d_info->rd_channel, mxcuart_dmaread_callback,
+ umxc);
+ mxc_dma_callback_set(d_info->wr_channel, mxcuart_dma_writecallback,
+ umxc);
+
+ /* Start the read channel */
+ mxc_dma_enable(d_info->rd_channel);
+ kfree(readchnl_reqelem);
+ tasklet_init(&d_info->dma_tx_tasklet, dma_tx_do_tasklet,
+ (unsigned long)umxc);
+ d_info->dma_txchnl_inuse = 0;
+ return ret;
+ cleanup:
+ kfree(readchnl_reqelem);
+ err_request:
+ kfree(umxc->rx_dmamap);
+ err_dma_rx_buff:
+ kfree(umxc->tx_buf);
+ err_dma_tx_buff:
+ mxc_dma_free(d_info->rd_channel);
+ mxc_dma_free(d_info->wr_channel);
+
+ return ret;
+}
+
+/*!
+ * Stops DMA and frees the DMA resources
+ *
+ * @param d_info the structure that holds all the DMA information for a
+ * particular MXC UART
+ * @param umxc the MXC UART port structure, this includes the \b uart_port
+ * structure and other members that are specific to MXC UARTs
+ */
+static void mxcuart_freedma(dma_info * d_info, uart_mxc_port * umxc)
+{
+ int i, rxbufs;
+ mxc_uart_rxdmamap *rx_buf_elem;
+
+ rxbufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
+
+ for (i = 0; i < rxbufs; i++) {
+ rx_buf_elem = (mxc_uart_rxdmamap *) (umxc->rx_dmamap + i);
+ dma_free_coherent(NULL, RXDMA_BUFF_SIZE,
+ rx_buf_elem->rx_buf, rx_buf_elem->rx_handle);
+ }
+ kfree(umxc->rx_dmamap);
+ kfree(umxc->tx_buf);
+ mxc_dma_free(d_info->rd_channel);
+ mxc_dma_free(d_info->wr_channel);
+}
+
+/*!
+ * This function is called to free the interrupts.
+ *
+ * @param umxc the MXC UART port structure, this includes the \b uart_port
+ * structure and other members that are specific to MXC UARTs
+ */
+static void mxcuart_free_interrupts(uart_mxc_port * umxc)
+{
+ free_irq(umxc->port.irq, umxc);
+ if (umxc->ints_muxed == 0) {
+ free_irq(umxc->irqs[0], umxc);
+ free_irq(umxc->irqs[1], umxc);
+ }
+}
+
+/*!
+ * Calculate and set the UART port clock value
+ *
+ * @param umxc the MXC UART port structure, this includes the \b uart_port
+ * structure and other members that are specific to MXC UARTs
+ * @param per_clk peripheral clock coming into the MXC UART module
+ * @param req_baud current baudrate requested
+ * @param div returns the reference frequency divider value
+ */
+static void mxcuart_set_ref_freq(uart_mxc_port * umxc, unsigned long per_clk,
+ unsigned int req_baud, int *div)
+{
+ unsigned int d = 1;
+
+ /*
+ * Choose the smallest possible prescaler to maximize
+ * the chance of using integer scaling. Ensure that
+ * the calculation won't overflow. Limit the denom
+ * to 15 bits since a 16-bit denom doesn't work.
+ */
+ if (req_baud < (1 << (31 - (4 + 15))))
+ d = per_clk / (req_baud << (4 + 15)) + 1;
+
+ umxc->port.uartclk = per_clk / d;
+
+ /*
+ * Set the ONEMS register that is used by IR special case bit and
+ * the Escape character detect logic
+ */
+ writel(umxc->port.uartclk / 1000, umxc->port.membase + MXC_UARTONEMS);
+ *div = d;
+}
+
+/*!
+ * This function is called by the core driver to initialize the low-level
+ * driver. The function grabs the interrupt resources and registers its
+ * interrupt service routines. It then initializes the IOMUX registers to
+ * configure the pins for UART signals and finally initializes the various
+ * UART registers and enables the port for reception.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ *
+ * @return The function returns 0 on success and a non-zero value on failure.
+ */
+static int mxcuart_startup(struct uart_port *port)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) port;
+ int retval;
+ volatile unsigned int cr, cr1 = 0, cr2 = 0, ufcr = 0;
+
+ /*
+ * Some UARTs need separate registrations for the interrupts as
+ * they do not take the muxed interrupt output to the ARM core
+ */
+ if (umxc->ints_muxed == 1) {
+ retval = request_irq(umxc->port.irq, mxcuart_int, 0,
+ "mxcintuart", umxc);
+ if (retval != 0) {
+ return retval;
+ }
+ } else {
+ retval = request_irq(umxc->port.irq, mxcuart_tx_int,
+ 0, "mxcintuart", umxc);
+ if (retval != 0) {
+ return retval;
+ } else {
+ retval = request_irq(umxc->irqs[0], mxcuart_rx_int,
+ 0, "mxcintuart", umxc);
+ if (retval != 0) {
+ free_irq(umxc->port.irq, umxc);
+ return retval;
+ } else {
+ retval =
+ request_irq(umxc->irqs[1], mxcuart_mint_int,
+ 0, "mxcintuart", umxc);
+ if (retval != 0) {
+ free_irq(umxc->port.irq, umxc);
+ free_irq(umxc->irqs[0], umxc);
+ return retval;
+ }
+ }
+ }
+ }
+
+ /* Initialize the DMA if we need SDMA data transfer */
+ if (umxc->dma_enabled == 1) {
+ retval = mxcuart_initdma(dma_list + umxc->port.line, umxc);
+ if (retval != 0) {
+ printk
+ (KERN_ERR
+ "MXC UART: Failed to initialize DMA for UART %d\n",
+ umxc->port.line);
+ mxcuart_free_interrupts(umxc);
+ return retval;
+ }
+ /* Configure the GPR register to receive SDMA events */
+ config_uartdma_event(umxc->port.line);
+ }
+
+ /*
+ * Clear Status Registers 1 and 2
+ */
+ writel(0xFFFF, umxc->port.membase + MXC_UARTUSR1);
+ writel(0xFFFF, umxc->port.membase + MXC_UARTUSR2);
+
+ /* Configure the IOMUX for the UART */
+ gpio_uart_active(umxc->port.line, umxc->ir_mode);
+
+ /*
+ * Set the transceiver invert bits if required
+ */
+ if (umxc->ir_mode == IRDA) {
+ echo_cancel = 1;
+ writel(umxc->ir_rx_inv | MXC_UARTUCR4_IRSC, umxc->port.membase
+ + MXC_UARTUCR4);
+ writel(umxc->rxd_mux | umxc->ir_tx_inv,
+ umxc->port.membase + MXC_UARTUCR3);
+ } else {
+ writel(umxc->rxd_mux, umxc->port.membase + MXC_UARTUCR3);
+ }
+
+ /*
+ * Initialize UCR1,2 and UFCR registers
+ */
+ if (umxc->dma_enabled == 1) {
+ cr2 = (MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
+ } else {
+ cr2 =
+ (MXC_UARTUCR2_ATEN | MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
+ }
+
+ writel(cr2, umxc->port.membase + MXC_UARTUCR2);
+ /* Wait till we are out of software reset */
+ do {
+ cr = readl(umxc->port.membase + MXC_UARTUCR2);
+ } while (!(cr & MXC_UARTUCR2_SRST));
+
+ if (umxc->mode == MODE_DTE) {
+ ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |
+ MXC_UARTUFCR_DCEDTE | MXC_UARTUFCR_RFDIV | umxc->
+ rx_threshold);
+ } else {
+ ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |
+ MXC_UARTUFCR_RFDIV | umxc->rx_threshold);
+ }
+ writel(ufcr, umxc->port.membase + MXC_UARTUFCR);
+
+ /*
+ * Finally enable the UART and the Receive interrupts
+ */
+ if (umxc->ir_mode == IRDA) {
+ cr1 |= MXC_UARTUCR1_IREN;
+ }
+ if (umxc->dma_enabled == 1) {
+ cr1 |= (MXC_UARTUCR1_RXDMAEN | MXC_UARTUCR1_ATDMAEN |
+ MXC_UARTUCR1_UARTEN);
+ } else {
+ cr1 |= (MXC_UARTUCR1_RRDYEN | MXC_UARTUCR1_UARTEN);
+ }
+ writel(cr1, umxc->port.membase + MXC_UARTUCR1);
+
+ return 0;
+}
+
+/*!
+ * This function is called by the core driver for the low-level driver to free
+ * its resources. The function frees all its interrupts and disables the UART.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ */
+static void mxcuart_shutdown(struct uart_port *port)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) port;
+
+ /* Disable the IOMUX for the UART */
+ gpio_uart_inactive(umxc->port.line, umxc->ir_mode);
+ mxcuart_free_interrupts(umxc);
+ /* Disable all interrupts, port and break condition */
+ writel(0, umxc->port.membase + MXC_UARTUCR1);
+ writel(0, umxc->port.membase + MXC_UARTUCR3);
+ if (umxc->dma_enabled == 1) {
+ mxcuart_freedma(dma_list + umxc->port.line, umxc);
+ }
+}
+
+/*!
+ * This function is called while changing the UART parameters. It is called to
+ * check if the Infrared special case bit (IRSC) in control register 4 should
+ * be set.
+ *
+ * @param baudrate the desired baudrate
+ *
+ * @return The functions returns 0 if the IRSC bit does not have to be set,
+ * else it returns a 1.
+ */
+/*
+static int mxcuart_setir_special(u_int baudrate)
+{
+ u_int thresh_val;
+
+ thresh_val = 1000000 / (8 * MIN_PULSE_DUR);
+ if (baudrate > thresh_val) {
+ return 0;
+ }
+
+ return 1;
+}
+*/
+
+/*!
+ * This function is called by the core driver to change the UART parameters,
+ * including baudrate, word length, parity, stop bits. The function also updates
+ * the port structures mask registers to indicate the types of events the user is
+ * interested in receiving.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ * @param termios the desired termios settings
+ * @param old old termios
+ */
+static void mxcuart_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) port;
+ volatile unsigned int cr4 = 0, cr2 = 0, ufcr;
+ u_int num, denom, baud;
+ u_int cr2_mask; /* Used to add the changes to CR2 */
+ unsigned long flags, per_clk;
+ int div;
+
+ cr2_mask = ~(MXC_UARTUCR2_IRTS | MXC_UARTUCR2_CTSC | MXC_UARTUCR2_PREN |
+ MXC_UARTUCR2_PROE | MXC_UARTUCR2_STPB | MXC_UARTUCR2_WS);
+
+ per_clk = clk_get_rate(umxc->clk);
+
+ /*
+ * Ask the core to get the baudrate, if requested baudrate is not
+ * between max and min, then either use the baudrate in old termios
+ * setting. If it's still invalid, we try 9600 baud.
+ */
+ baud = uart_get_baud_rate(&umxc->port, termios, old, 0, per_clk / 16);
+ /* Set the Reference frequency divider */
+ mxcuart_set_ref_freq(umxc, per_clk, baud, &div);
+
+ /* Byte size, default is 8-bit mode */
+ switch (termios->c_cflag & CSIZE) {
+ case CS7:
+ cr2 = 0;
+ break;
+ default:
+ cr2 = MXC_UARTUCR2_WS;
+ break;
+ }
+ /* Check to see if we need 2 Stop bits */
+ if (termios->c_cflag & CSTOPB) {
+ cr2 |= MXC_UARTUCR2_STPB;
+ }
+
+ /* Check to see if we need Parity checking */
+ if (termios->c_cflag & PARENB) {
+ cr2 |= MXC_UARTUCR2_PREN;
+ if (termios->c_cflag & PARODD) {
+ cr2 |= MXC_UARTUCR2_PROE;
+ }
+ }
+ spin_lock_irqsave(&umxc->port.lock, flags);
+
+ ufcr = readl(umxc->port.membase + MXC_UARTUFCR);
+ ufcr = (ufcr & (~MXC_UARTUFCR_RFDIV_MASK)) |
+ ((6 - div) << MXC_UARTUFCR_RFDIV_OFFSET);
+ writel(ufcr, umxc->port.membase + MXC_UARTUFCR);
+
+ /*
+ * Update the per-port timeout
+ */
+ uart_update_timeout(&umxc->port, termios->c_cflag, baud);
+
+ umxc->port.read_status_mask = MXC_UARTURXD_OVRRUN;
+ /*
+ * Enable appropriate events to be passed to the TTY layer
+ */
+ if (termios->c_iflag & INPCK) {
+ umxc->port.read_status_mask |= MXC_UARTURXD_FRMERR |
+ MXC_UARTURXD_PRERR;
+ }
+ if (termios->c_iflag & (BRKINT | PARMRK)) {
+ umxc->port.read_status_mask |= MXC_UARTURXD_BRK;
+ }
+
+ /*
+ * Characters to ignore
+ */
+ umxc->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR) {
+ umxc->port.ignore_status_mask |= MXC_UARTURXD_FRMERR |
+ MXC_UARTURXD_PRERR;
+ }
+ if (termios->c_iflag & IGNBRK) {
+ umxc->port.ignore_status_mask |= MXC_UARTURXD_BRK;
+ /*
+ * If we are ignoring parity and break indicators,
+ * ignore overruns too (for real raw support)
+ */
+ if (termios->c_iflag & IGNPAR) {
+ umxc->port.ignore_status_mask |= MXC_UARTURXD_OVRRUN;
+ }
+ }
+
+ /*
+ * Ignore all characters if CREAD is not set, still receive characters
+ * from the port, but throw them away.
+ */
+ if ((termios->c_cflag & CREAD) == 0) {
+ umxc->port.ignore_status_mask |= UART_CREAD_BIT;
+ }
+
+ cr4 = readl(umxc->port.membase + MXC_UARTUCR4);
+ if (UART_ENABLE_MS(port, termios->c_cflag)) {
+ mxcuart_enable_ms(port);
+ if (umxc->hardware_flow == 1) {
+ cr4 = (cr4 & (~MXC_UARTUCR4_CTSTL_MASK)) |
+ (umxc->cts_threshold << MXC_UARTUCR4_CTSTL_OFFSET);
+ cr2 |= MXC_UARTUCR2_CTSC;
+ umxc->port.info->port.tty->hw_stopped = 0;
+ } else {
+ cr2 |= MXC_UARTUCR2_IRTS;
+ }
+ } else {
+ cr2 |= MXC_UARTUCR2_IRTS;
+ }
+
+ /* Add Parity, character length and stop bits information */
+ cr2 |= (readl(umxc->port.membase + MXC_UARTUCR2) & cr2_mask);
+ writel(cr2, umxc->port.membase + MXC_UARTUCR2);
+ /*
+ if (umxc->ir_mode == IRDA) {
+ ret = mxcuart_setir_special(baud);
+ if (ret == 0) {
+ cr4 &= ~MXC_UARTUCR4_IRSC;
+ } else {
+ cr4 |= MXC_UARTUCR4_IRSC;
+ }
+ } */
+ writel(cr4, umxc->port.membase + MXC_UARTUCR4);
+
+ /*
+ * Set baud rate
+ */
+
+ /* Use integer scaling, if possible. Limit the denom to 15 bits. */
+ num = 0;
+ denom = (umxc->port.uartclk + 8 * baud) / (16 * baud) - 1;
+
+ /* Use fractional scaling if needed to limit the max error to 0.5% */
+ if (denom < 100) {
+ u64 n64 = (u64) 16 * 0x8000 * baud + (umxc->port.uartclk / 2);
+ do_div(n64, umxc->port.uartclk);
+ num = (u_int) n64 - 1;
+ denom = 0x7fff;
+ }
+ writel(num, umxc->port.membase + MXC_UARTUBIR);
+ writel(denom, umxc->port.membase + MXC_UARTUBMR);
+
+ spin_unlock_irqrestore(&umxc->port.lock, flags);
+}
+
+/*!
+ * This function is called by the core driver to know the UART type.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ *
+ * @return The function returns a pointer to a string describing the UART port.
+ */
+static const char *mxcuart_type(struct uart_port *port)
+{
+ return port->type == PORT_MXC ? "Freescale MXC" : NULL;
+}
+
+/*!
+ * This function is called by the core driver to release the memory resources
+ * currently in use by the UART port.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ */
+static void mxcuart_release_port(struct uart_port *port)
+{
+ release_mem_region(port->mapbase, SZ_4K);
+}
+
+/*!
+ * This function is called by the core driver to request memory resources for
+ * the UART port.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ *
+ * @return The function returns \b -EBUSY on failure, else it returns 0.
+ */
+static int mxcuart_request_port(struct uart_port *port)
+{
+ return request_mem_region(port->mapbase, SZ_4K, "serial_mxc")
+ != NULL ? 0 : -EBUSY;
+}
+
+/*!
+ * This function is called by the core driver to perform any autoconfiguration
+ * steps required for the UART port. This function sets the port->type field.
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ * @param flags bit mask of the required configuration
+ */
+static void mxcuart_config_port(struct uart_port *port, int flags)
+{
+ if ((flags & UART_CONFIG_TYPE) && (mxcuart_request_port(port) == 0)) {
+ port->type = PORT_MXC;
+ }
+}
+
+/*!
+ * This function is called by the core driver to verify that the new serial
+ * port information contained within \a ser is suitable for this UART port type.
+ * The function checks to see if the UART port type specified by the user
+ * application while setting the UART port information matches what is stored
+ * in the define \b PORT_MXC found in the header file include/linux/serial_core.h
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ * @param ser the new serial port information
+ *
+ * @return The function returns 0 on success or \b -EINVAL if the port type
+ * specified is not equal to \b PORT_MXC.
+ */
+static int mxcuart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ int ret = 0;
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_MXC) {
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+/*!
+ * This function is used to send a high priority XON/XOFF character
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ * @param ch the character to send
+ */
+static void mxcuart_send_xchar(struct uart_port *port, char ch)
+{
+ unsigned long flags;
+
+ port->x_char = ch;
+ if (port->info->port.tty->hw_stopped) {
+ return;
+ }
+
+ if (ch) {
+ spin_lock_irqsave(&port->lock, flags);
+ port->ops->start_tx(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
+}
+
+/*!
+ * This function is used enable/disable the MXC UART clocks
+ *
+ * @param port the port structure for the UART passed in by the core driver
+ * @param state New PM state
+ * @param oldstate Current PM state
+ */
+static void
+mxcuart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+ uart_mxc_port *umxc = (uart_mxc_port *) port;
+
+ if (state)
+ clk_disable(umxc->clk);
+ else
+ clk_enable(umxc->clk);
+}
+
+/*!
+ * This structure contains the pointers to the control functions that are
+ * invoked by the core serial driver to access the UART hardware. The
+ * structure is passed to serial_core.c file during registration.
+ */
+static struct uart_ops mxc_ops = {
+ .tx_empty = mxcuart_tx_empty,
+ .set_mctrl = mxcuart_set_mctrl,
+ .get_mctrl = mxcuart_get_mctrl,
+ .stop_tx = mxcuart_stop_tx,
+ .start_tx = mxcuart_start_tx,
+ .stop_rx = mxcuart_stop_rx,
+ .enable_ms = mxcuart_enable_ms,
+ .break_ctl = mxcuart_break_ctl,
+ .startup = mxcuart_startup,
+ .shutdown = mxcuart_shutdown,
+ .set_termios = mxcuart_set_termios,
+ .type = mxcuart_type,
+ .pm = mxcuart_pm,
+ .release_port = mxcuart_release_port,
+ .request_port = mxcuart_request_port,
+ .config_port = mxcuart_config_port,
+ .verify_port = mxcuart_verify_port,
+ .send_xchar = mxcuart_send_xchar,
+};
+
+#ifdef CONFIG_SERIAL_MXC_CONSOLE
+
+/*
+ * Write out a character once the UART is ready
+ */
+static inline void mxcuart_console_write_char(struct uart_port *port, char ch)
+{
+ volatile unsigned int status;
+
+ do {
+ status = readl(port->membase + MXC_UARTUSR1);
+ } while ((status & MXC_UARTUSR1_TRDY) == 0);
+ writel(ch, port->membase + MXC_UARTUTXD);
+}
+
+/*!
+ * This function is called to write the console messages through the UART port.
+ *
+ * @param co the console structure
+ * @param s the log message to be written to the UART
+ * @param count length of the message
+ */
+static void mxcuart_console_write(struct console *co, const char *s,
+ u_int count)
+{
+ struct uart_port *port = &mxc_ports[co->index]->port;
+ volatile unsigned int status, oldcr1, oldcr2, oldcr3, cr2, cr3;
+ int i;
+
+ /*
+ * First save the control registers and then disable the interrupts
+ */
+ oldcr1 = readl(port->membase + MXC_UARTUCR1);
+ oldcr2 = readl(port->membase + MXC_UARTUCR2);
+ oldcr3 = readl(port->membase + MXC_UARTUCR3);
+ cr2 =
+ oldcr2 & ~(MXC_UARTUCR2_ATEN | MXC_UARTUCR2_RTSEN |
+ MXC_UARTUCR2_ESCI);
+ cr3 =
+ oldcr3 & ~(MXC_UARTUCR3_DCD | MXC_UARTUCR3_RI |
+ MXC_UARTUCR3_DTRDEN);
+ writel(MXC_UARTUCR1_UARTEN, port->membase + MXC_UARTUCR1);
+ writel(cr2, port->membase + MXC_UARTUCR2);
+ writel(cr3, port->membase + MXC_UARTUCR3);
+ /*
+ * Do each character
+ */
+ for (i = 0; i < count; i++) {
+ mxcuart_console_write_char(port, s[i]);
+ if (s[i] == '\n') {
+ mxcuart_console_write_char(port, '\r');
+ }
+ }
+ /*
+ * Finally, wait for the transmitter to become empty
+ */
+ do {
+ status = readl(port->membase + MXC_UARTUSR2);
+ } while (!(status & MXC_UARTUSR2_TXDC));
+
+ /*
+ * Restore the control registers
+ */
+ writel(oldcr1, port->membase + MXC_UARTUCR1);
+ writel(oldcr2, port->membase + MXC_UARTUCR2);
+ writel(oldcr3, port->membase + MXC_UARTUCR3);
+}
+
+/*!
+ * Initializes the UART port to be used to print console message with the
+ * options specified. If no options are specified, then the function
+ * initializes the UART with the default options of baudrate=115200, 8 bit
+ * word size, no parity, no flow control.
+ *
+ * @param co The console structure
+ * @param options Any console options passed in from the command line
+ *
+ * @return The function returns 0 on success or error.
+ */
+static int __init mxcuart_console_setup(struct console *co, char *options)
+{
+ uart_mxc_port *umxc;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ volatile unsigned int cr = 0;
+
+ /*
+ * Check whether an invalid uart number had been specified, and if
+ * so, search for the first available port that does have console
+ * support
+ */
+ if (co->index >= MXC_UART_NR) {
+ co->index = 0;
+ }
+ umxc = mxc_ports[co->index];
+
+ if (umxc == NULL) {
+ return -ENODEV;
+ }
+
+ clk_enable(umxc->clk);
+
+ /* initialize port.lock else oops */
+ spin_lock_init(&umxc->port.lock);
+
+ /*
+ * Initialize the UART registers
+ */
+ writel(MXC_UARTUCR1_UARTEN, umxc->port.membase + MXC_UARTUCR1);
+ /* Enable the transmitter and do a software reset */
+ writel(MXC_UARTUCR2_TXEN, umxc->port.membase + MXC_UARTUCR2);
+ /* Wait till we are out of software reset */
+ do {
+ cr = readl(umxc->port.membase + MXC_UARTUCR2);
+ } while (!(cr & MXC_UARTUCR2_SRST));
+
+ writel(0x0, umxc->port.membase + MXC_UARTUCR3);
+ writel(0x0, umxc->port.membase + MXC_UARTUCR4);
+ /* Set TXTL to 2, RXTL to 1 and RFDIV to 2 */
+ cr = 0x0800 | MXC_UARTUFCR_RFDIV | 0x1;
+ if (umxc->mode == MODE_DTE) {
+ cr |= MXC_UARTUFCR_DCEDTE;
+ }
+ writel(cr, umxc->port.membase + MXC_UARTUFCR);
+ writel(0xFFFF, umxc->port.membase + MXC_UARTUSR1);
+ writel(0xFFFF, umxc->port.membase + MXC_UARTUSR2);
+
+ if (options != NULL) {
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ }
+ gpio_uart_active(umxc->port.line, umxc->ir_mode);
+ return uart_set_options(&umxc->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver mxc_reg;
+
+/*!
+ * This structure contains the pointers to the UART console functions. It is
+ * passed as an argument when registering the console.
+ */
+static struct console mxc_console = {
+ .name = "ttymxc",
+ .write = mxcuart_console_write,
+ .device = uart_console_device,
+ .setup = mxcuart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &mxc_reg,
+};
+
+/*!
+ * This function registers the console callback functions with the kernel.
+ */
+static int __init mxcuart_console_init(void)
+{
+ register_console(&mxc_console);
+ return 0;
+}
+
+console_initcall(mxcuart_console_init);
+
+static int __init find_port(struct uart_port *p)
+{
+ int line;
+ struct uart_port *port;
+ for (line = 0; line < MXC_UART_NR; line++) {
+ if (!mxc_ports[line])
+ continue;
+ port = &mxc_ports[line]->port;
+ if (uart_match_port(p, port))
+ return line;
+ }
+ return -ENODEV;
+}
+
+int __init mxc_uart_start_console(struct uart_port *port, char *options)
+{
+ int line;
+ line = find_port(port);
+ if (line < 0)
+ return -ENODEV;
+
+ add_preferred_console("ttymxc", line, options);
+ printk("Switching Console to ttymxc%d at %s 0x%lx (options '%s')\n",
+ line, port->iotype == UPIO_MEM ? "MMIO" : "I/O port",
+ port->iotype ==
+ UPIO_MEM ? (unsigned long)port->mapbase : (unsigned long)port->
+ iobase, options);
+
+ if (!(mxc_console.flags & CON_ENABLED)) {
+ mxc_console.flags &= ~CON_PRINTBUFFER;
+ register_console(&mxc_console);
+ }
+ return 0;
+}
+
+#define MXC_CONSOLE &mxc_console
+#else
+#define MXC_CONSOLE NULL
+#endif /* CONFIG_SERIAL_MXC_CONSOLE */
+
+/*!
+ * This structure contains the information such as the name of the UART driver
+ * that appears in the /dev folder, major and minor numbers etc. This structure
+ * is passed to the serial_core.c file.
+ */
+static struct uart_driver mxc_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "ttymxc",
+ .dev_name = "ttymxc",
+ .major = SERIAL_MXC_MAJOR,
+ .minor = SERIAL_MXC_MINOR,
+ .nr = MXC_UART_NR,
+ .cons = MXC_CONSOLE,
+};
+
+/*!
+ * This function is called to put the UART in a low power state. Refer to the
+ * document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which UART
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxcuart_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ uart_mxc_port *umxc = platform_get_drvdata(pdev);
+
+ if (umxc == NULL)
+ return 0; /* skip disabled ports */
+
+ if (umxc->port.info && umxc->port.info->flags & UIF_INITIALIZED)
+ uart_suspend_port(&mxc_reg, &umxc->port);
+
+ if (umxc->port.info && umxc->port.info->flags & UIF_SUSPENDED)
+ umxc->port.info->port.tty->hw_stopped = 1;
+
+ return 0;
+}
+
+/*!
+ * This function is called to bring the UART back from a low power state. Refer
+ * to the document driver-model/driver.txt in the kernel source tree for more
+ * information.
+ *
+ * @param pdev the device structure used to give information on which UART
+ * to resume
+ *
+ * @return The function returns 0 on success and -1 on failure
+ */
+static int mxcuart_resume(struct platform_device *pdev)
+{
+ uart_mxc_port *umxc = platform_get_drvdata(pdev);
+
+ if (umxc == NULL)
+ return 0; /* skip disabled ports */
+
+ if (umxc->port.info && umxc->port.info->flags & UIF_SUSPENDED) {
+ umxc->port.info->port.tty->hw_stopped = 0;
+ uart_resume_port(&mxc_reg, &umxc->port);
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is called during the driver binding process. Based on the UART
+ * that is being probed this function adds the appropriate UART port structure
+ * in the core driver.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions
+ *
+ * @return The function returns 0 if successful; -1 otherwise.
+ */
+static int mxcuart_probe(struct platform_device *pdev)
+{
+ int id = pdev->id;
+
+ mxc_ports[id] = pdev->dev.platform_data;
+ mxc_ports[id]->port.ops = &mxc_ops;
+
+ /* Do not use UARTs that are disabled during integration */
+ if (mxc_ports[id]->enabled == 1) {
+ mxc_ports[id]->port.dev = &pdev->dev;
+ spin_lock_init(&mxc_ports[id]->port.lock);
+ /* Enable the low latency flag for DMA UART ports */
+ if (mxc_ports[id]->dma_enabled == 1) {
+ mxc_ports[id]->port.flags |= UPF_LOW_LATENCY;
+ }
+
+ mxc_ports[id]->clk = clk_get(&pdev->dev, "uart_clk");
+ if (mxc_ports[id]->clk == NULL)
+ return -1;
+
+ uart_add_one_port(&mxc_reg, &mxc_ports[id]->port);
+ platform_set_drvdata(pdev, mxc_ports[id]);
+ }
+ return 0;
+}
+
+/*!
+ * Dissociates the driver from the UART device. Removes the appropriate UART
+ * port structure from the core driver.
+ *
+ * @param pdev the device structure used to give information on which UART
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mxcuart_remove(struct platform_device *pdev)
+{
+ uart_mxc_port *umxc = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (umxc) {
+ uart_remove_one_port(&mxc_reg, &umxc->port);
+ }
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcuart_driver = {
+ .driver = {
+ .name = "mxcintuart",
+ },
+ .probe = mxcuart_probe,
+ .remove = mxcuart_remove,
+ .suspend = mxcuart_suspend,
+ .resume = mxcuart_resume,
+};
+
+/*!
+ * This function is used to initialize the UART driver module. The function
+ * registers the power management callback functions with the kernel and also
+ * registers the UART callback functions with the core serial driver.
+ *
+ * @return The function returns 0 on success and a non-zero value on failure.
+ */
+static int __init mxcuart_init(void)
+{
+ int ret = 0;
+
+ printk(KERN_INFO "Serial: MXC Internal UART driver\n");
+ ret = uart_register_driver(&mxc_reg);
+ if (ret == 0) {
+ /* Register the device driver structure. */
+ ret = platform_driver_register(&mxcuart_driver);
+ if (ret != 0) {
+ uart_unregister_driver(&mxc_reg);
+ }
+ }
+ return ret;
+}
+
+/*!
+ * This function is used to cleanup all resources before the driver exits.
+ */
+static void __exit mxcuart_exit(void)
+{
+ platform_driver_unregister(&mxcuart_driver);
+ uart_unregister_driver(&mxc_reg);
+}
+
+module_init(mxcuart_init);
+module_exit(mxcuart_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/mxc_uart_early.c b/drivers/serial/mxc_uart_early.c
new file mode 100644
index 000000000000..0b5ceaef3ec0
--- /dev/null
+++ b/drivers/serial/mxc_uart_early.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file drivers/serial/mxc_uart_early.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC serial ports based on
+ * drivers/char/8250_early.c, Copyright 2004 Hewlett-Packard Development Company,
+ * L.P. by Bjorn Helgaasby.
+ *
+ * Early serial console for MXC UARTS.
+ *
+ * This is for use before the serial driver has initialized, in
+ * particular, before the UARTs have been discovered and named.
+ * Instead of specifying the console device as, e.g., "ttymxc0",
+ * we locate the device directly by its MMIO or I/O port address.
+ *
+ * The user can specify the device directly, e.g.,
+ * console=mxcuart,0x43f90000,115200n8
+ * or platform code can call early_uart_console_init() to set
+ * the early UART device.
+ *
+ * After the normal serial driver starts, we try to locate the
+ * matching ttymxc device and start a console there.
+ */
+
+/*
+ * Include Files
+ */
+
+#include <linux/tty.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/clk.h>
+#include <mach/mxc_uart.h>
+
+struct mxc_early_uart_device {
+ struct uart_port port;
+ char options[16]; /* e.g., 115200n8 */
+ unsigned int baud;
+};
+
+int __init mxc_uart_start_console(struct uart_port *, char *);
+static struct mxc_early_uart_device mxc_early_device __initdata;
+static int mxc_early_uart_registered __initdata;
+static struct clk *clk;
+
+/*
+ * Write out a character once the UART is ready
+ */
+static void __init mxcuart_console_write_char(struct uart_port *port, int ch)
+{
+ unsigned int status;
+
+ do {
+ status = readl(port->membase + MXC_UARTUSR1);
+ } while ((status & MXC_UARTUSR1_TRDY) == 0);
+ writel(ch, port->membase + MXC_UARTUTXD);
+}
+
+/*!
+ * This function is called to write the console messages through the UART port.
+ *
+ * @param co the console structure
+ * @param s the log message to be written to the UART
+ * @param count length of the message
+ */
+void __init early_mxcuart_console_write(struct console *co, const char *s,
+ u_int count)
+{
+ struct uart_port *port = &mxc_early_device.port;
+ volatile unsigned int status, oldcr1, oldcr2, oldcr3, cr2, cr3;
+
+ /*
+ * First save the control registers and then disable the interrupts
+ */
+ oldcr1 = readl(port->membase + MXC_UARTUCR1);
+ oldcr2 = readl(port->membase + MXC_UARTUCR2);
+ oldcr3 = readl(port->membase + MXC_UARTUCR3);
+ cr2 =
+ oldcr2 & ~(MXC_UARTUCR2_ATEN | MXC_UARTUCR2_RTSEN |
+ MXC_UARTUCR2_ESCI);
+ cr3 =
+ oldcr3 & ~(MXC_UARTUCR3_DCD | MXC_UARTUCR3_RI |
+ MXC_UARTUCR3_DTRDEN);
+ writel(MXC_UARTUCR1_UARTEN, port->membase + MXC_UARTUCR1);
+ writel(cr2, port->membase + MXC_UARTUCR2);
+ writel(cr3, port->membase + MXC_UARTUCR3);
+
+ /* Transmit string */
+ uart_console_write(port, s, count, mxcuart_console_write_char);
+
+ /*
+ * Finally, wait for the transmitter to become empty
+ */
+ do {
+ status = readl(port->membase + MXC_UARTUSR2);
+ } while (!(status & MXC_UARTUSR2_TXDC));
+
+ /*
+ * Restore the control registers
+ */
+ writel(oldcr1, port->membase + MXC_UARTUCR1);
+ writel(oldcr2, port->membase + MXC_UARTUCR2);
+ writel(oldcr3, port->membase + MXC_UARTUCR3);
+}
+
+static unsigned int __init probe_baud(struct uart_port *port)
+{
+ /* FIXME Return Default Baud Rate */
+ return 115200;
+}
+
+static int __init parse_options(struct mxc_early_uart_device *device,
+ char *options)
+{
+ struct uart_port *port = &device->port;
+ int mapsize = 64;
+ int length;
+
+ if (!options)
+ return -ENODEV;
+
+ port->uartclk = 5600000;
+ port->iotype = UPIO_MEM;
+ port->mapbase = simple_strtoul(options, &options, 0);
+ port->membase = ioremap(port->mapbase, mapsize);
+
+ if ((options = strchr(options, ','))) {
+ options++;
+ device->baud = simple_strtoul(options, NULL, 0);
+ length = min(strcspn(options, " "), sizeof(device->options));
+ strncpy(device->options, options, length);
+ } else {
+ device->baud = probe_baud(port);
+ snprintf(device->options, sizeof(device->options), "%u",
+ device->baud);
+ }
+ printk(KERN_INFO
+ "MXC_Early serial console at MMIO 0x%x (options '%s')\n",
+ port->mapbase, device->options);
+ return 0;
+}
+
+static int __init mxc_early_uart_setup(struct console *console, char *options)
+{
+ struct mxc_early_uart_device *device = &mxc_early_device;
+ int err;
+ if (device->port.membase || device->port.iobase)
+ return 0;
+ if ((err = parse_options(device, options)) < 0)
+ return err;
+ return 0;
+}
+
+static struct console mxc_early_uart_console __initdata = {
+ .name = "mxcuart",
+ .write = early_mxcuart_console_write,
+ .setup = mxc_early_uart_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+static int __init mxc_early_uart_console_init(void)
+{
+
+ if (!mxc_early_uart_registered) {
+ register_console(&mxc_early_uart_console);
+ mxc_early_uart_registered = 1;
+ }
+
+ return 0;
+}
+
+int __init mxc_early_serial_console_init(char *cmdline)
+{
+ char *options;
+ int err;
+ int uart_paddr;
+
+ options = strstr(cmdline, "console=mxcuart");
+ if (!options)
+ return -ENODEV;
+
+ /* Extracting MXC UART Uart Port Address from cmdline */
+ options = strchr(cmdline, ',') + 1;
+ uart_paddr = simple_strtoul(options, NULL, 16);
+
+#ifdef UART1_BASE_ADDR
+ if (uart_paddr == UART1_BASE_ADDR)
+ clk = clk_get(NULL, "uart_clk.0");
+#endif
+#ifdef UART2_BASE_ADDR
+ if (uart_paddr == UART2_BASE_ADDR)
+ clk = clk_get(NULL, "uart_clk.1");
+#endif
+#ifdef UART3_BASE_ADDR
+ if (uart_paddr == UART3_BASE_ADDR)
+ clk = clk_get(NULL, "uart_clk.2");
+#endif
+ if (clk == NULL)
+ return -1;
+
+ /* Enable Early MXC UART Clock */
+ clk_enable(clk);
+
+ options = strchr(cmdline, ',') + 1;
+ if ((err = mxc_early_uart_setup(NULL, options)) < 0)
+ return err;
+ return mxc_early_uart_console_init();
+}
+
+int __init mxc_early_uart_console_switch(void)
+{
+ struct mxc_early_uart_device *device = &mxc_early_device;
+ struct uart_port *port = &device->port;
+ int mmio, line;
+
+ if (!(mxc_early_uart_console.flags & CON_ENABLED))
+ return 0;
+ /* Try to start the normal driver on a matching line. */
+ mmio = (port->iotype == UPIO_MEM);
+ line = mxc_uart_start_console(port, device->options);
+
+ if (line < 0)
+ printk("No ttymxc device at %s 0x%lx for console\n",
+ mmio ? "MMIO" : "I/O port",
+ mmio ? port->mapbase : (unsigned long)port->iobase);
+
+ unregister_console(&mxc_early_uart_console);
+ if (mmio)
+ iounmap(port->membase);
+
+ clk_disable(clk);
+ clk_put(clk);
+
+ return 0;
+}
+
+late_initcall(mxc_early_uart_console_switch);
diff --git a/drivers/serial/mxc_uart_reg.h b/drivers/serial/mxc_uart_reg.h
new file mode 100644
index 000000000000..c0d1e812fe6a
--- /dev/null
+++ b/drivers/serial/mxc_uart_reg.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __MXC_UART_REG_H__
+#define __MXC_UART_REG_H__
+
+/* Address offsets of the UART registers */
+#define MXC_UARTURXD 0x000 /* Receive reg */
+#define MXC_UARTUTXD 0x040 /* Transmitter reg */
+#define MXC_UARTUCR1 0x080 /* Control reg 1 */
+#define MXC_UARTUCR2 0x084 /* Control reg 2 */
+#define MXC_UARTUCR3 0x088 /* Control reg 3 */
+#define MXC_UARTUCR4 0x08C /* Control reg 4 */
+#define MXC_UARTUFCR 0x090 /* FIFO control reg */
+#define MXC_UARTUSR1 0x094 /* Status reg 1 */
+#define MXC_UARTUSR2 0x098 /* Status reg 2 */
+#define MXC_UARTUESC 0x09C /* Escape character reg */
+#define MXC_UARTUTIM 0x0A0 /* Escape timer reg */
+#define MXC_UARTUBIR 0x0A4 /* BRM incremental reg */
+#define MXC_UARTUBMR 0x0A8 /* BRM modulator reg */
+#define MXC_UARTUBRC 0x0AC /* Baud rate count reg */
+#define MXC_UARTONEMS 0x0B0 /* One millisecond reg */
+#define MXC_UARTUTS 0x0B4 /* Test reg */
+
+/* Bit definations of UCR1 */
+#define MXC_UARTUCR1_ADEN 0x8000
+#define MXC_UARTUCR1_ADBR 0x4000
+#define MXC_UARTUCR1_TRDYEN 0x2000
+#define MXC_UARTUCR1_IDEN 0x1000
+#define MXC_UARTUCR1_RRDYEN 0x0200
+#define MXC_UARTUCR1_RXDMAEN 0x0100
+#define MXC_UARTUCR1_IREN 0x0080
+#define MXC_UARTUCR1_TXMPTYEN 0x0040
+#define MXC_UARTUCR1_RTSDEN 0x0020
+#define MXC_UARTUCR1_SNDBRK 0x0010
+#define MXC_UARTUCR1_TXDMAEN 0x0008
+#define MXC_UARTUCR1_ATDMAEN 0x0004
+#define MXC_UARTUCR1_DOZE 0x0002
+#define MXC_UARTUCR1_UARTEN 0x0001
+
+/* Bit definations of UCR2 */
+#define MXC_UARTUCR2_ESCI 0x8000
+#define MXC_UARTUCR2_IRTS 0x4000
+#define MXC_UARTUCR2_CTSC 0x2000
+#define MXC_UARTUCR2_CTS 0x1000
+#define MXC_UARTUCR2_PREN 0x0100
+#define MXC_UARTUCR2_PROE 0x0080
+#define MXC_UARTUCR2_STPB 0x0040
+#define MXC_UARTUCR2_WS 0x0020
+#define MXC_UARTUCR2_RTSEN 0x0010
+#define MXC_UARTUCR2_ATEN 0x0008
+#define MXC_UARTUCR2_TXEN 0x0004
+#define MXC_UARTUCR2_RXEN 0x0002
+#define MXC_UARTUCR2_SRST 0x0001
+
+/* Bit definations of UCR3 */
+#define MXC_UARTUCR3_DTREN 0x2000
+#define MXC_UARTUCR3_PARERREN 0x1000
+#define MXC_UARTUCR3_FRAERREN 0x0800
+#define MXC_UARTUCR3_DSR 0x0400
+#define MXC_UARTUCR3_DCD 0x0200
+#define MXC_UARTUCR3_RI 0x0100
+#define MXC_UARTUCR3_RXDSEN 0x0040
+#define MXC_UARTUCR3_AWAKEN 0x0010
+#define MXC_UARTUCR3_DTRDEN 0x0008
+#define MXC_UARTUCR3_RXDMUXSEL 0x0004
+#define MXC_UARTUCR3_INVT 0x0002
+
+/* Bit definations of UCR4 */
+#define MXC_UARTUCR4_CTSTL_OFFSET 10
+#define MXC_UARTUCR4_CTSTL_MASK (0x3F << 10)
+#define MXC_UARTUCR4_INVR 0x0200
+#define MXC_UARTUCR4_ENIRI 0x0100
+#define MXC_UARTUCR4_REF16 0x0040
+#define MXC_UARTUCR4_IRSC 0x0020
+#define MXC_UARTUCR4_TCEN 0x0008
+#define MXC_UARTUCR4_OREN 0x0002
+#define MXC_UARTUCR4_DREN 0x0001
+
+/* Bit definations of UFCR */
+#define MXC_UARTUFCR_RFDIV 0x0200 /* Ref freq div is set to 2 */
+#define MXC_UARTUFCR_RFDIV_OFFSET 7
+#define MXC_UARTUFCR_RFDIV_MASK (0x7 << 7)
+#define MXC_UARTUFCR_TXTL_OFFSET 10
+#define MXC_UARTUFCR_DCEDTE 0x0040
+
+/* Bit definations of URXD */
+#define MXC_UARTURXD_ERR 0x4000
+#define MXC_UARTURXD_OVRRUN 0x2000
+#define MXC_UARTURXD_FRMERR 0x1000
+#define MXC_UARTURXD_BRK 0x0800
+#define MXC_UARTURXD_PRERR 0x0400
+
+/* Bit definations of USR1 */
+#define MXC_UARTUSR1_PARITYERR 0x8000
+#define MXC_UARTUSR1_RTSS 0x4000
+#define MXC_UARTUSR1_TRDY 0x2000
+#define MXC_UARTUSR1_RTSD 0x1000
+#define MXC_UARTUSR1_FRAMERR 0x0400
+#define MXC_UARTUSR1_RRDY 0x0200
+#define MXC_UARTUSR1_AGTIM 0x0100
+#define MXC_UARTUSR1_DTRD 0x0080
+#define MXC_UARTUSR1_AWAKE 0x0010
+
+/* Bit definations of USR2 */
+#define MXC_UARTUSR2_TXFE 0x4000
+#define MXC_UARTUSR2_IDLE 0x1000
+#define MXC_UARTUSR2_RIDELT 0x0400
+#define MXC_UARTUSR2_RIIN 0x0200
+#define MXC_UARTUSR2_DCDDELT 0x0040
+#define MXC_UARTUSR2_DCDIN 0x0020
+#define MXC_UARTUSR2_TXDC 0x0008
+#define MXC_UARTUSR2_ORE 0x0002
+#define MXC_UARTUSR2_RDR 0x0001
+
+/* Bit definations of UTS */
+#define MXC_UARTUTS_LOOP 0x1000
+
+#endif /* __MXC_UART_REG_H__ */
diff --git a/drivers/serial/ns921x-serial.c b/drivers/serial/ns921x-serial.c
new file mode 100644
index 000000000000..7aa8f7f2b103
--- /dev/null
+++ b/drivers/serial/ns921x-serial.c
@@ -0,0 +1,1401 @@
+/*
+ * drivers/serial/ns921x-serial.c
+ *
+ * Copyright (C) 2007,2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#if defined(CONFIG_SERIAL_NS921X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+
+#include <mach/ns921x-serial.h>
+#include <mach/gpio.h>
+
+#define HUB_IFS 0x0000
+#define HUB_IFS_RXFSRIP (1 << 25)
+#define HUB_IFS_TXFSRIP (1 << 19)
+#define HUB_IFS_MODIP (1 << 18)
+#define HUB_IFS_RXFE (1 << 13)
+#define HUB_IFS_TXFF (1 << 11)
+#define HUB_IFS_TXFE (1 << 10)
+
+#define HUB_DMARXCTRL 0x0004
+#define HUB_DMARXCTRL_DIRECT (1 << 28)
+
+#define HUB_RXIC 0x000c
+#define HUB_RXIC_RXTHRS (15 << 28)
+#define HUB_RXIC_RXFSRIE (1 << 25)
+
+#define HUB_DMRXSF 0x0010
+#define HUB_DMRXSF_BYTE (7 << 9)
+#define HUB_DMRXSF_FULL (1 << 7)
+
+#define HUB_DMRXDF 0x0014
+
+#define HUB_DMATXCTRL 0x0018
+#define HUB_DMATXCTRL_DIRECT (1 << 28)
+
+#define HUB_TXIC 0x0020
+#define HUB_TXIC_TXTHRS (15 << 28)
+#define HUB_TXIC_TXFUFIE (1 << 26)
+#define HUB_TXIC_TXFSRIE (1 << 25)
+
+#define HUB_DMTXDF 0x0028
+
+#define UART_WC 0x1000
+#define UART_WC_RXEN (1 << 30)
+#define UART_WC_TXEN (1 << 29)
+#define UART_WC_RTSEN (1 << 19)
+#define UART_WC_RXFLUSH (1 << 17)
+#define UART_WC_TXFLUSH (1 << 16)
+#define UART_WC_TXFLOW_CHAR (1 << 11)
+#define UART_WC_TXFLOW_SOFT (1 << 10)
+#define UART_WC_TXFLOW_RI (1 << 9)
+#define UART_WC_TXFLOW_DSR (1 << 8)
+#define UART_WC_TXFLOW_DCD (1 << 7)
+#define UART_WC_TXFLOW_CTS (1 << 6)
+#define UART_WC_TXFLOW (UART_WC_TXFLOW_RI | UART_WC_TXFLOW_DSR | \
+ UART_WC_TXFLOW_DCD | UART_WC_TXFLOW_CTS)
+
+#define UART_IE 0x1004
+#define UART_IE_OFLOW (1 << 19)
+#define UART_IE_PARITY (1 << 18)
+#define UART_IE_FRAME (1 << 17)
+#define UART_IE_BREAK (1 << 16)
+#define UART_IE_DSR (1 << 7)
+#define UART_IE_DCD (1 << 6)
+#define UART_IE_CTS (1 << 5)
+#define UART_IE_RI (1 << 4)
+#define UART_IE_TBC (1 << 3)
+#define UART_IE_RBC (1 << 2)
+#define UART_IE_TXIDLE (1 << 1)
+
+#define UART_IS 0x1008
+#define UART_IS_OFLOW (1 << 19)
+#define UART_IS_PARITY (1 << 18)
+#define UART_IS_FRAME (1 << 17)
+#define UART_IS_BREAK (1 << 16)
+#define UART_IS_DSR (1 << 7)
+#define UART_IS_DCD (1 << 6)
+#define UART_IS_CTS (1 << 5)
+#define UART_IS_RI (1 << 4)
+#define UART_IS_TBC (1 << 3)
+#define UART_IS_RBC (1 << 2)
+#define UART_IS_TXIDLE (1 << 1)
+
+#define UIE_RX (UART_IE_OFLOW | UART_IE_PARITY | UART_IE_FRAME | \
+ UART_IE_BREAK | UART_IE_RBC)
+#define UIS_RX (UART_IS_OFLOW | UART_IS_PARITY | UART_IS_FRAME | \
+ UART_IS_BREAK | UART_IS_RBC)
+#define UIE_MS (UART_IE_DSR | UART_IE_DCD | UART_IE_CTS | UART_IE_RI)
+#define UIS_MS (UART_IS_DSR | UART_IS_DCD | UART_IS_CTS | UART_IS_RI)
+
+#define UART_CGAPCTRL 0x100c
+#define UART_CGAPCTRL_EN (1 << 31)
+
+#define UART_BGAPCTRL 0x1010
+#define UART_BGAPCTRL_EN (1 << 31)
+
+#define UART_RCMC0 0x1014
+#define UART_RCMC0_ENABLE (1 << 31)
+#define UART_RCMC0_MASK (0xff << 16)
+#define UART_RCMC0_DATA (0xff << 0)
+
+#define UART_AWC 0x1030
+#define UART_AWC_ENABLE (1 << 0)
+
+#define UART_BRDL 0x1100 /* DLAB = 1 */
+
+#define UART_UIE 0x1104 /* DLAB = 0 */
+#define UART_UIE_ETBEI (1 << 1)
+
+#define UART_BRDM 0x1104 /* DLAB = 1 */
+
+#define UART_FCR 0x1108
+#define UART_FCR_TXCLR (1 << 2)
+#define UART_FCR_RXCLR (1 << 1)
+#define UART_FCR_FIFOEN (1 << 0)
+
+#define UART_LCR 0x110c
+#define UART_LCR_DLAB (1 << 7)
+#define UART_LCR_SBC (1 << 6)
+#define UART_LCR_SPAR (1 << 5)
+#define UART_LCR_EPAR (1 << 4)
+#define UART_LCR_PARITY (1 << 3)
+#define UART_LCR_STOP (1 << 2)
+#define UART_LCR_WLEN 0x0003
+#define UART_LCR_WLEN_5 0x0000
+#define UART_LCR_WLEN_6 0x0001
+#define UART_LCR_WLEN_7 0x0002
+#define UART_LCR_WLEN_8 0x0003
+
+#define UART_MCR 0x1110
+#define UART_MCR_AFE (1 << 5)
+#define UART_MCR_LOOP (1 << 4)
+#define UART_MCR_RTS (1 << 1)
+#define UART_MCR_DTR (1 << 0)
+
+#define UART_LSR 0x1114
+#define UART_LSR_TEMT (1 << 6)
+
+#define UART_MSR 0x1118
+#define UART_MSR_DCD (1 << 7)
+#define UART_MSR_RI (1 << 6)
+#define UART_MSR_DSR (1 << 5)
+#define UART_MSR_CTS (1 << 4)
+#define UART_MSR_DDCD (1 << 3)
+#define UART_MSR_TERI (1 << 2)
+#define UART_MSR_DDSR (1 << 1)
+#define UART_MSR_DCTS (1 << 0)
+
+#define UMSR_ANYDELTA (UART_MSR_DDCD | UART_MSR_TERI | \
+ UART_MSR_DDSR | UART_MSR_DCTS)
+
+#define DRIVER_NAME "ns921x-serial"
+#define NS921X_TTY_NAME "ttyNS"
+#define NS921X_TTY_MAJOR 204 /* XXX */
+#define NS921X_TTY_MINOR_START 196 /* XXX */
+
+#define NS921X_UART_NR 4
+
+#define FIFOSIZE 16
+#define TXTHRESHOLD 1
+#define RXTHRESHOLD 5
+
+#define up2unp(up) container_of(up, struct uart_ns921x_port, port)
+struct uart_ns921x_port {
+ struct uart_port port;
+ struct clk *clk;
+#define NSUPF_TXIRQPENDING 1
+#define NSUPF_SCHEDSTOPTX 2
+ unsigned int flags;
+ u32 ifs2ack;
+ u32 is2ack;
+ struct ns921x_uart_data *data;
+};
+
+#if defined(CONFIG_DEBUG_LL) && 0
+void printch(char);
+void printhex2(unsigned);
+void printhex4(unsigned);
+void printhex8(unsigned);
+void printascii(const char *);
+#else
+#define printch(c) ((void)0)
+#define printhex2(u) ((void)0)
+#define printhex4(u) ((void)0)
+#define printhex8(u) ((void)0)
+#define printascii(s) ((void)0)
+#endif
+
+static inline u32 uartread32(struct uart_port *port, unsigned int offset)
+{
+ u32 ret = ioread32(port->membase + offset);
+
+#if defined(DEBUG_UARTRW)
+ dev_dbg(port->dev, "read 0x%p -> 0x%08x\n",
+ port->membase + offset, ret);
+#endif
+
+ return ret;
+}
+
+static inline void uartwrite32(struct uart_port *port,
+ u32 value, unsigned int offset)
+{
+#if defined(DEBUG_UARTRW)
+ dev_dbg(port->dev, "write 0x%p <- 0x%08x\n",
+ port->membase + offset, value);
+#endif
+ iowrite32(value, port->membase + offset);
+}
+
+static inline void uartwrite8(struct uart_port *port, u8 value,
+ unsigned int offset)
+{
+#if defined(DEBUG_UARTRW)
+ dev_dbg(port->dev, "write 0x%p <- 0x%02x\n",
+ port->membase + offset, value);
+#endif
+ iowrite8(value, port->membase + offset);
+}
+
+/*
+ * bits 19 to 31 of HUB_IFS need to be cleard by writing a 1 to it.
+ * ns921x_uart_read_ifs and ns921x_uart_clear_ifs help debugging missed clears
+ * by tracking the bits set
+ */
+#define IFS_BITSTOACK 0xfff80000
+static inline u32 ns921x_uart_read_ifs(struct uart_ns921x_port *unp)
+{
+ u32 ret = uartread32(&unp->port, HUB_IFS);
+ if (unp->ifs2ack)
+ dev_dbg(unp->port.dev, "%s: %s still unacked: %x "
+ "(called from %p)\n", __func__, "IFS",
+ unp->ifs2ack, __builtin_return_address(0));
+ unp->ifs2ack |= ret & IFS_BITSTOACK;
+
+ if (unp->ifs2ack & ~ret)
+ dev_dbg(unp->port.dev, "%s: rereading %s doesn't "
+ "yield unacked bits (called from %p): "
+ "%08x %08x\n", __func__, "IFS",
+ __builtin_return_address(0), unp->ifs2ack, ret);
+
+ return ret;
+}
+
+static inline void ns921x_uart_clear_ifs(struct uart_ns921x_port *unp,
+ unsigned int mask)
+{
+ if (mask & ~unp->ifs2ack) {
+ dev_dbg(unp->port.dev, "%s: unp->%s = %08x, mask = %08x\n",
+ __func__, "ifs2ack", unp->ifs2ack, mask);
+ BUG();
+ }
+ BUG_ON(mask & ~IFS_BITSTOACK);
+ unp->ifs2ack &= ~mask;
+ uartwrite32(&unp->port, mask, HUB_IFS);
+}
+
+/*
+ * all bits of UART_IS need to be cleard by writing a 1 to it.
+ * ns921x_uart_read_is and ns921x_uart_clear_is help debugging missed clears by
+ * tracking the bits set
+ */
+static inline u32 ns921x_uart_read_is(struct uart_ns921x_port *unp)
+{
+ u32 ret = uartread32(&unp->port, UART_IS);
+ if (unp->is2ack)
+ dev_dbg(unp->port.dev, "%s: %s still unacked: %x "
+ "(called from %p)\n", __func__, "IS",
+ unp->is2ack, __builtin_return_address(0));
+ unp->is2ack |= ret;
+
+ if (unp->is2ack & ~ret)
+ dev_dbg(unp->port.dev, "%s: rereading %s doesn't "
+ "yield unacked bits (called from %p): "
+ "%08x %08x\n", __func__, "IS",
+ __builtin_return_address(0), unp->is2ack, ret);
+
+ return ret;
+}
+
+static inline void ns921x_uart_clear_is(struct uart_ns921x_port *unp,
+ u32 mask)
+{
+ if (mask & ~unp->is2ack) {
+ dev_dbg(unp->port.dev, "%s: unp->%s = %08x, mask = %08x\n",
+ __func__, "is2ack", unp->is2ack, mask);
+ BUG();
+ }
+ unp->is2ack &= ~mask;
+ uartwrite32(&unp->port, mask, UART_IS);
+}
+
+static inline void ns921x_uart_rmw_uartie(struct uart_port *port,
+ u32 mask, u32 value)
+{
+ u32 ie = uartread32(port, UART_IE);
+
+ BUG_ON((value & mask) != value);
+
+ ie = (ie & ~mask) | value;
+
+ uartwrite32(port, ie, UART_IE);
+}
+
+static unsigned int ns921x_uart_tx_empty(struct uart_port *port)
+{
+ struct uart_ns921x_port *unp = up2unp(port);
+ /* Should I use UART_IS_TXIDLE? HUB_IFS_TXFE? */
+
+ u32 lsr;
+
+#if defined(NSUPF_TXIRQPENDING)
+ if (unp->flags & NSUPF_TXIRQPENDING)
+ return 0;
+#endif
+
+ lsr = uartread32(&unp->port, UART_LSR);
+
+ if (!(lsr & UART_LSR_TEMT))
+ return 0;
+
+ return TIOCSER_TEMT;
+}
+
+static void ns921x_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ u32 mcr = 0;
+
+ /* RTS signal should be controlled as gpio to allow using AFE */
+ if (mctrl & TIOCM_RTS)
+ mcr |= UART_MCR_RTS;
+
+ if (mctrl & TIOCM_DTR)
+ mcr |= UART_MCR_DTR;
+
+ if (mctrl & TIOCM_LOOP)
+ mcr |= UART_MCR_LOOP;
+
+ uartwrite32(port, mcr, UART_MCR);
+}
+
+
+/*
+ * Depending on the configuration of the HW flow control, the CTS line is
+ * not be passed to the uart, making impossible to read the status of some
+ * modem lines through the modem status register.
+ * Following function workarounds that by reading the CTS line as gpio.
+ */
+static unsigned int ns921x_uart_check_msr(struct uart_port *port, u32 irqstat)
+{
+ struct uart_ns921x_port *unp = up2unp(port);
+ u32 status, cts_gpio;
+
+ /* We can read the status of the modem lines through the
+ * modem status register, but we need to add the value of
+ * CTS line from the gpio value */
+ status = uartread32(port, UART_MSR);
+ cts_gpio = unp->data->gpios[2];
+ status |= (gpio_get_value(cts_gpio) != 0) ? 0 : UART_MSR_CTS;
+
+ /* For event detection on the modem lines, we use the interrupt status
+ * register instead of the delta section of the modem status register.
+ * The interrupt flags are valid in all the cases */
+ if ((irqstat & (UART_IS_DSR|UART_IS_DCD|UART_IS_CTS|UART_IS_RI)) &&
+ port->info != NULL) {
+ if (irqstat & UART_IS_RI)
+ port->icount.rng++;
+ if (irqstat & UART_IS_DSR)
+ port->icount.dsr++;
+ if (irqstat & UART_IS_DCD)
+ uart_handle_dcd_change(port, status & UART_MSR_DCD);
+ if (irqstat & UART_IS_CTS)
+ uart_handle_cts_change(port, status & UART_MSR_CTS);
+
+ wake_up_interruptible(&port->info->delta_msr_wait);
+ }
+
+ return status;
+}
+
+static unsigned int ns921x_uart_get_mctrl(struct uart_port *port)
+{
+ unsigned int status;
+ unsigned int ret = 0;
+
+ status = ns921x_uart_check_msr(port, 0);
+
+ if (status & UART_MSR_DCD)
+ ret |= TIOCM_CAR;
+ if (status & UART_MSR_RI)
+ ret |= TIOCM_RNG;
+ if (status & UART_MSR_DSR)
+ ret |= TIOCM_DSR;
+ if (status & UART_MSR_CTS)
+ ret |= TIOCM_CTS;
+
+ return ret;
+}
+
+static void ns921x_uart_stop_tx_real(struct uart_port *port)
+{
+ struct uart_ns921x_port *unp = up2unp(port);
+
+ u32 txic = uartread32(&unp->port, HUB_TXIC);
+
+ printch('.');
+
+ ns921x_uart_rmw_uartie(&unp->port, UART_IE_TXIDLE, 0);
+
+ uartwrite32(port, txic & ~HUB_TXIC_TXFSRIE, HUB_TXIC);
+
+#if defined(NSUPF_TXIRQPENDING)
+ unp->flags &= ~NSUPF_TXIRQPENDING;
+#endif
+}
+
+/* called with port->lock taken */
+static void ns921x_uart_stop_tx(struct uart_port *port)
+{
+#if defined(NSUPF_TXIRQPENDING) && defined(NSUPF_SCHEDSTOPTX)
+ struct uart_ns921x_port *unp = up2unp(port);
+
+ /* don't stop the port if there is a tx irq pending */
+ if (unp->flags & NSUPF_TXIRQPENDING) {
+ unp->flags |= NSUPF_SCHEDSTOPTX;
+ } else {
+ ns921x_uart_stop_tx_real(port);
+
+ unp->flags &= ~NSUPF_SCHEDSTOPTX;
+ }
+#else
+ ns921x_uart_stop_tx_real(port);
+#endif
+}
+
+/* send out chars in xmit buffer. This is called with port->lock taken */
+static void ns921x_uart_tx_chars(struct uart_ns921x_port *unp,
+ unsigned int freebuffers)
+{
+ struct circ_buf *xmit = &unp->port.info->xmit;
+ unsigned long ifs;
+
+ BUG_ON(!freebuffers);
+
+ assert_spin_locked(&unp->port.lock);
+
+ /*
+ * If the FIFO is not empty return at this point, then the
+ * interrupt-handler will recall this function
+ */
+ ifs = ns921x_uart_read_ifs(unp);
+ if (!(ifs & HUB_IFS_TXFE))
+ return;
+
+ if (unp->port.x_char) {
+ uartwrite8(&unp->port, unp->port.x_char, HUB_DMTXDF);
+ unp->port.icount.tx++;
+ unp->port.x_char = 0;
+ freebuffers--;
+#if defined(NSUPF_TXIRQPENDING)
+ unp->flags |= NSUPF_TXIRQPENDING;
+#endif
+ }
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&unp->port)) {
+ ns921x_uart_stop_tx(&unp->port);
+ return;
+ }
+
+ printch('T');
+ printhex4(uart_circ_chars_pending(xmit));
+ while (freebuffers && uart_circ_chars_pending(xmit)) {
+ if (uart_circ_chars_pending(xmit) >= 4) {
+ u32 fourchars = xmit->buf[xmit->tail];
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ fourchars |= xmit->buf[xmit->tail] << 8;
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ fourchars |= xmit->buf[xmit->tail] << 16;
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ fourchars |= xmit->buf[xmit->tail] << 24;
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+
+ uartwrite32(&unp->port, fourchars, HUB_DMTXDF);
+ printhex8(cpu_to_be32(fourchars));
+ unp->port.icount.tx += 4;
+ } else {
+ uartwrite8(&unp->port,
+ xmit->buf[xmit->tail], HUB_DMTXDF);
+ printhex2(xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ unp->port.icount.tx++;
+ }
+#if defined(NSUPF_TXIRQPENDING)
+ unp->flags |= NSUPF_TXIRQPENDING;
+#endif
+ --freebuffers;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&unp->port);
+
+ if (uart_circ_empty(xmit))
+ ns921x_uart_stop_tx(&unp->port);
+}
+
+#define maskshiftfac(mask) ((mask) & (-(mask)))
+#define im2mask(val, mask) \
+ (((val) * maskshiftfac(mask)) & (mask))
+
+/* called with port->lock taken */
+static void ns921x_uart_start_tx(struct uart_port *port)
+{
+ struct uart_ns921x_port *unp = up2unp(port);
+
+ u32 txic = uartread32(port, HUB_TXIC);
+
+ uartwrite32(port, txic | im2mask(TXTHRESHOLD, HUB_TXIC_TXTHRS) |
+ HUB_TXIC_TXFSRIE, HUB_TXIC);
+ ns921x_uart_rmw_uartie(&unp->port, UART_IE_TXIDLE, UART_IE_TXIDLE);
+
+#if defined(NSUPF_SCHEDSTOPTX)
+ unp->flags &= ~NSUPF_SCHEDSTOPTX;
+#endif
+
+ if (
+#if defined(NSUPF_TXIRQPENDING)
+ !(unp->flags & NSUPF_TXIRQPENDING) &&
+#endif
+ !(ns921x_uart_read_ifs(unp) & HUB_IFS_TXFF)) {
+ unsigned int freebuffers = 1;
+ ns921x_uart_tx_chars(up2unp(port), freebuffers);
+ }
+}
+
+static void ns921x_uart_stop_rx(struct uart_port *port)
+{
+ dev_vdbg(port->dev, "%s\n", __func__);
+
+ ns921x_uart_rmw_uartie(port, UIE_RX, 0);
+}
+
+static void ns921x_uart_enable_ms(struct uart_port *port)
+{
+ dev_vdbg(port->dev, "%s\n", __func__);
+
+ ns921x_uart_rmw_uartie(port, UIE_MS, UIE_MS);
+}
+
+static void ns921x_uart_break_ctl(struct uart_port *port, int break_state)
+{
+ unsigned long flags;
+ u32 lcr;
+
+ dev_vdbg(port->dev, "%s\n", __func__);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ lcr = uartread32(port, UART_LCR);
+
+ /* XXX: amba_pl011_break_ctl tests for break_state being -1,
+ * Documentation/serial/driver tells to tests for != 0 */
+ if (break_state)
+ lcr |= UART_LCR_SBC;
+ else
+ lcr &= ~UART_LCR_SBC;
+
+ uartwrite32(port, lcr, UART_LCR);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void ns921x_uart_rx_char(struct uart_ns921x_port *unp,
+ unsigned int ch, u32 is)
+{
+ unsigned int flag = TTY_NORMAL;
+
+ unp->port.icount.rx++;
+#define ISERR (UART_IS_BREAK | UART_IS_PARITY | UART_IS_FRAME | UART_IS_OFLOW)
+
+ dev_vdbg(unp->port.dev, "%s: IS=%x\n", __func__, is);
+ if (unlikely(is & ISERR)) {
+ if (is & UART_IS_BREAK) {
+ dev_vdbg(unp->port.dev, "break, IS=%x\n", is);
+ unp->port.icount.brk++;
+ if (uart_handle_break(&unp->port))
+ return;
+ } else if (is & UART_IS_PARITY)
+ unp->port.icount.parity++;
+ else if (is & UART_IS_FRAME)
+ unp->port.icount.frame++;
+
+ if (is & UART_IS_OFLOW)
+ unp->port.icount.overrun++;
+
+ is &= unp->port.read_status_mask;
+
+ if (is & UART_IS_BREAK)
+ flag = TTY_BREAK;
+ else if (is & UART_IS_PARITY)
+ flag = TTY_PARITY;
+ else if (is & UART_IS_FRAME)
+ flag = TTY_FRAME;
+ }
+
+ if (uart_handle_sysrq_char(&unp->port, ch))
+ return;
+
+ dev_vdbg(unp->port.dev, "%s: insert %x (IS=%x, ism=%x, flag=%x)\n",
+ __func__, ch, is, unp->port.ignore_status_mask, flag);
+ uart_insert_char(&unp->port, is, UART_IS_OFLOW, ch, flag);
+}
+
+/* This is called with port->lock taken */
+static void ns921x_uart_rx_chars(struct uart_ns921x_port *unp,
+ u32 ifs, u32 is)
+{
+ struct tty_struct *tty = unp->port.info->port.tty;
+
+ if (is & (UART_IS_RBC | UIS_RX))
+ ns921x_uart_clear_is(unp, is & (UART_IS_RBC | UIS_RX));
+
+ if (ifs & HUB_IFS_RXFE) {
+ if (is & (UART_IS_BREAK | UART_IS_OFLOW))
+ ns921x_uart_rx_char(unp, 0, is);
+ }
+
+ while (!(ifs & HUB_IFS_RXFE)) {
+ u32 dmrxsf = uartread32(&unp->port, HUB_DMRXSF);
+ u32 dmrxdf = uartread32(&unp->port, HUB_DMRXDF);
+ /* XXX: better put this into a macro */
+ unsigned int bytes = (dmrxsf & HUB_DMRXSF_BYTE) >> 9;
+ int i;
+
+ dev_vdbg(unp->port.dev, "%s: DMRXSF = %x, DMRXDF = %x\n",
+ __func__, dmrxsf, dmrxdf);
+
+ BUG_ON(bytes > 4);
+
+ for (i = 0; i < bytes; ++i) {
+ unsigned int ch = (dmrxdf >> (8 * i)) & 0xff;
+
+ /*
+ * assume break and errors only apply to the last
+ * character.
+ */
+ ns921x_uart_rx_char(unp, ch, i == bytes - 1 ? is : 0);
+ }
+ ifs = ns921x_uart_read_ifs(unp);
+ }
+ spin_unlock(&unp->port.lock);
+ tty_flip_buffer_push(tty);
+ spin_lock(&unp->port.lock);
+}
+
+static irqreturn_t ns921x_uart_int(int irq, void *dev_id)
+{
+ struct uart_ns921x_port *unp = dev_id;
+ u32 ifs, uninitialized_var(is);
+
+ spin_lock(&unp->port.lock);
+
+ ifs = ns921x_uart_read_ifs(unp);
+
+ if (ifs & HUB_IFS_TXFSRIP) {
+ ns921x_uart_clear_ifs(unp, HUB_IFS_TXFSRIP);
+
+ /* don't clear NSUPF_TXIRQPENDING, we still wait for TXdone */
+
+ ns921x_uart_tx_chars(unp, FIFOSIZE - TXTHRESHOLD - 1);
+ }
+
+ if ((ifs & HUB_IFS_RXFSRIP) || (ifs & HUB_IFS_MODIP))
+ is = ns921x_uart_read_is(unp);
+
+ if (ifs & HUB_IFS_RXFSRIP) {
+ ns921x_uart_clear_ifs(unp, HUB_IFS_RXFSRIP);
+
+ ns921x_uart_rx_chars(unp, ifs, is);
+ }
+
+ if (ifs & HUB_IFS_MODIP) {
+ dev_vdbg(unp->port.dev, "%s: IS=%x\n", __func__, is);
+
+ /* test for HUB_IFS_RXFSRIP not being set to only fetch
+ * characters once.
+ */
+ if (is & UIS_RX && !(ifs & HUB_IFS_RXFSRIP))
+ ns921x_uart_rx_chars(unp, ifs, is);
+
+ if (is & UIS_MS)
+ ns921x_uart_clear_is(unp, is & UIS_MS);
+
+ /* XXX: call this only after an MS irq? */
+ ns921x_uart_check_msr(&unp->port, is);
+
+ if (is & UART_IS_TXIDLE) {
+ unsigned int freebuffers = TXTHRESHOLD;
+#if defined(NSUPF_TXIRQPENDING)
+ unp->flags &= ~NSUPF_TXIRQPENDING;
+#endif
+
+ ns921x_uart_clear_is(unp, UART_IS_TXIDLE);
+
+ /* The fifo might not be empty. The following can
+ * happen:
+ * - irq for HUB_IFS_TXFSRIP serviced without
+ * UART_IS_TXIDLE
+ * - before new data is written the fifo becomes empty
+ * and UART_IS_TXIDLE is pending
+ * - UART_IS_TXIDLE is serviced with fifo still having
+ * some data from servicing UART_IS_TXIDLE.
+ *
+ * So we only write TXTHRESHOLD buffers. Only if the
+ * fifo is reported to be empty and we didn't wrote
+ * something above, the full fifo is used.
+ */
+ if ((ifs & (HUB_IFS_TXFE | HUB_IFS_TXFSRIP)) ==
+ HUB_IFS_TXFE)
+ freebuffers = FIFOSIZE;
+
+ ns921x_uart_tx_chars(unp, freebuffers);
+ }
+
+ if (unp->is2ack)
+ dev_dbg(unp->port.dev,
+ "%s: unacked flags in UART_IS: %x %x\n",
+ __func__, unp->is2ack, is);
+ }
+
+ if (unp->ifs2ack)
+ dev_dbg(unp->port.dev, "%s: unacked flags in HUB_IFS: %x\n",
+ __func__, unp->ifs2ack);
+
+#if defined(NSUPF_SCHEDSTOPTX) && defined(NSUPF_TXIRQPENDING)
+ if ((unp->flags & (NSUPF_TXIRQPENDING | NSUPF_SCHEDSTOPTX)) ==
+ NSUPF_SCHEDSTOPTX) {
+ ns921x_uart_stop_tx_real(&unp->port);
+ unp->flags &= ~NSUPF_SCHEDSTOPTX;
+ }
+#endif
+
+ spin_unlock(&unp->port.lock);
+
+ return IRQ_HANDLED;
+}
+
+static int ns921x_uart_startup(struct uart_port *port)
+{
+ struct uart_ns921x_port *unp = up2unp(port);
+ int ret;
+ u32 rxic;
+ u32 wc;
+
+ unp->flags = 0;
+
+ ret = clk_enable(unp->clk);
+ if (ret) {
+ dev_dbg(port->dev, "%s: err_clkenable", __func__);
+ goto err_clkenable;
+ }
+
+ unp->port.uartclk = clk_get_rate(unp->clk);
+
+ ret = request_irq(unp->port.irq, ns921x_uart_int, 0, DRIVER_NAME, unp);
+ if (ret) {
+ dev_dbg(port->dev, "%s: err_request_irq", __func__);
+
+ clk_disable(unp->clk);
+err_clkenable:
+
+ return ret;
+ }
+
+ ns921x_uart_rmw_uartie(port, UIE_RX, UIE_RX);
+
+ rxic = uartread32(port, HUB_RXIC);
+ uartwrite32(port, rxic | im2mask(RXTHRESHOLD, HUB_RXIC_RXTHRS)
+ | HUB_RXIC_RXFSRIE, HUB_RXIC);
+
+ wc = UART_WC_RXEN | UART_WC_TXEN;
+ if (unp->data->rtsen)
+ wc |= UART_WC_RTSEN;
+ uartwrite32(port, wc, UART_WC);
+ uartwrite32(&unp->port, UART_FCR_FIFOEN, UART_FCR);
+
+ return 0;
+}
+
+static void ns921x_uart_shutdown(struct uart_port *port)
+{
+ struct uart_ns921x_port *unp = up2unp(port);
+
+ /* wait up to 10ms for the fifo to empty */
+ unsigned long expire = jiffies + msecs_to_jiffies(10);
+
+ printch('\\');
+
+ while (!ns921x_uart_tx_empty(port)) {
+ msleep(1);
+ if (time_after(jiffies, expire))
+ break;
+ }
+
+ ns921x_uart_rmw_uartie(port, -1, 0);
+ uartwrite32(port, 0, UART_WC);
+
+ ns921x_uart_break_ctl(port, 0);
+
+ free_irq(port->irq, unp);
+
+ clk_disable(unp->clk);
+}
+
+static void ns921x_uart_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old)
+{
+ unsigned long flags;
+ u32 cval = 0;
+
+ unsigned int baud, quot;
+ u32 saved_wc;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ cval = UART_LCR_WLEN_5;
+ break;
+ case CS6:
+ cval = UART_LCR_WLEN_6;
+ break;
+ case CS7:
+ cval = UART_LCR_WLEN_7;
+ break;
+ default:
+ case CS8:
+ cval = UART_LCR_WLEN_8;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ cval |= UART_LCR_STOP;
+
+ if (termios->c_cflag & PARENB) {
+ cval |= UART_LCR_PARITY;
+
+ if (!(termios->c_cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ /* stick parity:
+ * - MARK parity requires flags PARENB | CMSPAR | PARODD
+ * and in the controller the EPS and PEN bitfields
+ * must be set to 0
+ * - SPACE parity requires flags PARENB | CMSPAR | ~PARODD
+ * and in the controller the EPS and PEN bitfields
+ * must be set to 1 (these are already set by the
+ * code above this, so there is nothing to do here)
+ * In any case SP bitfield must be set to 1
+ */
+ if(termios->c_cflag & CMSPAR) {
+ cval |= UART_LCR_SPAR; /* set stick parity */
+ if (termios->c_cflag & PARODD) {
+ cval &= ~UART_LCR_EPAR;
+ cval &= ~UART_LCR_PARITY;
+ }
+ }
+#endif
+ }
+
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+ quot = uart_get_divisor(port, baud);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ port->read_status_mask = UART_IS_OFLOW;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= UART_IS_FRAME | UART_IS_PARITY;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ port->read_status_mask |= UART_IS_BREAK;
+
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= UART_IS_FRAME | UART_IS_PARITY;
+ if (termios->c_iflag & IGNBRK) {
+ port->ignore_status_mask |= UART_IS_BREAK;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= UART_IS_OFLOW;
+ }
+
+ /* disable everything, prepare fifo flushing */
+ saved_wc = uartread32(port, UART_WC);
+ uartwrite32(port, UART_WC_RXFLUSH | UART_WC_TXFLUSH, UART_WC);
+
+ if (UART_ENABLE_MS(port, termios->c_cflag))
+ ns921x_uart_enable_ms(port);
+
+ /* set character gap period */
+ uartwrite32(port, UART_CGAPCTRL_EN | (10 * (port->uartclk / baud) - 1),
+ UART_CGAPCTRL);
+
+ /* set buffer gap period */
+ uartwrite32(port, UART_BGAPCTRL_EN | (640 * (port->uartclk / baud) - 1),
+ UART_BGAPCTRL);
+
+ /* prepare to access baud rate registers */
+ uartwrite32(port, UART_LCR_DLAB, UART_LCR);
+
+ /* set baud rate */
+ uartwrite32(port, quot & 0xff, UART_BRDL);
+ uartwrite32(port, (quot >> 8) & 0xff, UART_BRDM);
+
+ udelay(1);
+ uartwrite32(port, cval, UART_LCR);
+
+ /* flush fifos and restore state */
+ uartwrite32(port, saved_wc, UART_WC);
+ uartwrite32(port, UART_UIE_ETBEI, UART_UIE);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *ns921x_uart_type(struct uart_port *port)
+{
+ return port->type == PORT_NS921X ? "NS921X" : NULL;
+}
+
+static void ns921x_uart_release_port(struct uart_port *port)
+{
+ /* XXX: release_mem_region is marked as Compatibility cruft ??? */
+ release_mem_region(port->mapbase, 0x8000);
+}
+
+static int ns921x_uart_request_port(struct uart_port *port)
+{
+ return request_mem_region(port->mapbase,
+ 0x8000, DRIVER_NAME) ? 0 : -EBUSY;
+}
+
+static void ns921x_uart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = PORT_NS921X;
+ ns921x_uart_request_port(port);
+ }
+}
+
+static int ns921x_uart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ int ret = 0;
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_NS921X)
+ ret = -EINVAL;
+
+ if (ser->irq < 0 || ser->irq >= NR_IRQS)
+ ret = -EINVAL;
+
+ if (ser->baud_base < 9600)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static struct uart_ops ns921x_uart_pops = {
+ .tx_empty = ns921x_uart_tx_empty,
+ .set_mctrl = ns921x_uart_set_mctrl,
+ .get_mctrl = ns921x_uart_get_mctrl,
+ .stop_tx = ns921x_uart_stop_tx,
+ .start_tx = ns921x_uart_start_tx,
+ .stop_rx = ns921x_uart_stop_rx,
+ .enable_ms = ns921x_uart_enable_ms,
+ .break_ctl = ns921x_uart_break_ctl,
+ .startup = ns921x_uart_startup,
+ .shutdown = ns921x_uart_shutdown,
+ .set_termios = ns921x_uart_set_termios,
+ /* .pm = ns921x_uart_pm, */
+ /* .set_wake = ns921x_uart_set_wake, */
+ .type = ns921x_uart_type,
+ .release_port = ns921x_uart_release_port,
+ .request_port = ns921x_uart_request_port,
+ .config_port = ns921x_uart_config_port,
+ .verify_port = ns921x_uart_verify_port,
+ /* .ioctl */
+};
+
+static struct uart_ns921x_port *ns921x_uart_ports[NS921X_UART_NR];
+
+#if defined(CONFIG_SERIAL_NS921X_CONSOLE)
+
+static void ns921x_uart_console_putchar(struct uart_port *port, int ch)
+{
+ struct uart_ns921x_port *unp = up2unp(port);
+
+ while (ns921x_uart_read_ifs(unp) & HUB_IFS_TXFF)
+ barrier();
+
+ uartwrite8(&unp->port, ch, HUB_DMTXDF);
+}
+
+/* called with console_sem hold. irqs locally disabled */
+static void ns921x_uart_console_write(struct console *co,
+ const char *s, unsigned int count)
+{
+ struct uart_ns921x_port *unp = ns921x_uart_ports[co->index];
+ u32 saved_txic, saved_wc, saved_uie;
+ u32 new_wc;
+
+ BUG_ON(!irqs_disabled());
+
+ saved_txic = uartread32(&unp->port, HUB_TXIC);
+ saved_wc = uartread32(&unp->port, UART_WC);
+ saved_uie = uartread32(&unp->port, UART_UIE);
+
+ new_wc = saved_wc | UART_WC_RXEN | UART_WC_TXEN;
+ new_wc &= ~(UART_WC_RXFLUSH | UART_WC_TXFLUSH);
+
+ /* XXX: assert baud, bits, parity etc. to be correct. */
+
+ uartwrite32(&unp->port, 0, HUB_TXIC);
+ uartwrite32(&unp->port, UART_FCR_FIFOEN, UART_FCR);
+ uartwrite32(&unp->port, UART_UIE_ETBEI, UART_UIE);
+ uartwrite32(&unp->port, new_wc, UART_WC);
+
+ uart_console_write(&unp->port, s, count, ns921x_uart_console_putchar);
+
+ /* wait for HUB fifo to become empty */
+ while (!(ns921x_uart_read_ifs(unp) & HUB_IFS_TXFE))
+ barrier();
+
+ while (!(uartread32(&unp->port, UART_LSR) & UART_LSR_TEMT))
+ barrier();
+
+ uartwrite32(&unp->port, saved_wc, UART_WC);
+ uartwrite32(&unp->port, saved_uie, UART_UIE);
+ uartwrite32(&unp->port, saved_txic, HUB_TXIC);
+}
+
+static void __init ns921x_uart_console_get_options(struct uart_ns921x_port *unp,
+ int *baud, int *parity, int *bits, int *flow)
+{
+ if (uartread32(&unp->port, UART_WC) & UART_WC_TXEN) {
+ u32 cval = uartread32(&unp->port, UART_LCR);
+ unsigned int quot = 0;
+
+ uartwrite32(&unp->port, UART_LCR_DLAB, UART_LCR);
+
+ /* XXX: write barrier */
+
+ quot = (uartread32(&unp->port, UART_BRDM) & 0xff) << 8;
+ quot |= uartread32(&unp->port, UART_BRDL) & 0xff;
+
+ uartwrite32(&unp->port, cval, UART_LCR);
+
+ *baud = unp->port.uartclk / (16 * quot);
+
+ *parity = 'n';
+ if (cval & UART_LCR_PARITY) {
+ if (cval & UART_LCR_EPAR)
+ *parity = 'e';
+ else
+ *parity = 'o';
+ }
+ if (cval & UART_LCR_SPAR) {
+ if( (cval & UART_LCR_PARITY) &&
+ (cval & UART_LCR_EPAR) )
+ *parity = 'm';
+ else
+ *parity = 's';
+ }
+
+ switch (cval & UART_LCR_WLEN) {
+ case UART_LCR_WLEN_5:
+ *bits = 5;
+ break;
+ case UART_LCR_WLEN_6:
+ *bits = 6;
+ break;
+ case UART_LCR_WLEN_7:
+ *bits = 7;
+ break;
+ case UART_LCR_WLEN_8:
+ *bits = 8;
+ break;
+ }
+
+ /* XXX: *flow = ... */
+ }
+}
+
+static int __init ns921x_uart_console_setup(struct console *co, char *options)
+{
+ struct uart_ns921x_port *unp;
+ int baud = 38400;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ int ret;
+
+ if (co->index >= NS921X_UART_NR)
+ co->index = 0;
+ unp = ns921x_uart_ports[co->index];
+ if (!unp)
+ return -ENODEV;
+
+ ret = clk_enable(unp->clk);
+ if (ret)
+ return ret;
+
+ unp->port.uartclk = clk_get_rate(unp->clk);
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ ns921x_uart_console_get_options(unp,
+ &baud, &parity, &bits, &flow);
+
+ ret = uart_set_options(&unp->port, co, baud, parity, bits, flow);
+
+ /* disable the clock only in the error path, because
+ * otherwise it would need to be enabled and disabled in console_write
+ * where it must not sleep. But as clk_disable does a gpio_free it
+ * might.
+ */
+ if (ret)
+ clk_disable(unp->clk);
+
+ return ret;
+}
+
+static struct uart_driver ns921x_uart_reg;
+static struct console ns921x_uart_console = {
+ .name = NS921X_TTY_NAME,
+ .write = ns921x_uart_console_write,
+ /* .read */
+ .device = uart_console_device,
+ /* .unblank */
+ .setup = ns921x_uart_console_setup,
+ /* .early_setup */
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ /* .cflag */
+ .data = &ns921x_uart_reg,
+ /* .next */
+};
+
+#define NS921X_UART_CONSOLE (&ns921x_uart_console)
+#else
+#define NS921X_UART_CONSOLE NULL
+#endif
+
+static struct uart_driver ns921x_uart_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = NS921X_TTY_NAME,
+ .major = NS921X_TTY_MAJOR,
+ .minor = NS921X_TTY_MINOR_START,
+ .nr = NS921X_UART_NR,
+ .cons = NS921X_UART_CONSOLE,
+};
+
+static __devinit int ns921x_uart_pdrv_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct uart_ns921x_port *unp;
+ struct resource *mem;
+ void __iomem *base;
+ int line, irq;
+
+ line = pdev->id;
+ if (line < 0 || line >= ARRAY_SIZE(ns921x_uart_ports)) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "%s: err_line\n", __func__);
+ goto err_line;
+ }
+
+ if (ns921x_uart_ports[line] != NULL) {
+ ret = -EBUSY;
+ dev_dbg(&pdev->dev, "%s: err_line\n", __func__);
+ goto err_line;
+ }
+
+ unp = kzalloc(sizeof(struct uart_ns921x_port), GFP_KERNEL);
+ ns921x_uart_ports[line] = unp;
+
+ if (unp == NULL) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc\n", __func__);
+ goto err_alloc;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENOENT;
+ dev_info(&pdev->dev, "%s: err_get_irq\n", __func__);
+ goto err_get_irq;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "%s: err_get_mem\n", __func__);
+ goto err_get_mem;
+ }
+
+ base = ioremap(mem->start, 0x8000);
+ if (!base) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_ioremap\n", __func__);
+ goto err_ioremap;
+ }
+ dev_dbg(&pdev->dev, "%s: membase = %p, unp = %p\n",
+ __func__, base, unp);
+
+ unp->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(unp->clk)) {
+ ret = PTR_ERR(unp->clk);
+ dev_dbg(&pdev->dev, "%s: err_clk_get -> %d\n", __func__, ret);
+ goto err_clk_get;
+ }
+
+ unp->data = pdev->dev.platform_data;
+ unp->port.dev = &pdev->dev;
+ unp->port.mapbase = mem->start;
+ unp->port.membase = base;
+ unp->port.iotype = UPIO_MEM;
+ unp->port.irq = irq;
+ unp->port.fifosize = 64; /* XXX */
+ unp->port.ops = &ns921x_uart_pops;
+ unp->port.flags = UPF_BOOT_AUTOCONF;
+ unp->port.line = line;
+
+ /* no DMA */
+ uartwrite32(&unp->port, HUB_DMARXCTRL_DIRECT, HUB_DMARXCTRL);
+ uartwrite32(&unp->port, HUB_DMATXCTRL_DIRECT, HUB_DMATXCTRL);
+
+ ret = uart_add_one_port(&ns921x_uart_reg, &unp->port);
+ if (ret) {
+
+ dev_dbg(&pdev->dev, "%s: err_uart_add1port -> %d\n",
+ __func__, ret);
+
+ clk_put(unp->clk);
+err_clk_get:
+
+ iounmap(base);
+err_ioremap:
+err_get_mem:
+err_get_irq:
+
+ kfree(unp);
+ ns921x_uart_ports[line] = NULL;
+err_alloc:
+
+err_line:
+
+ return ret;
+ }
+
+ /* Make the device wakeup capable, but disabled by default */
+ device_init_wakeup(&pdev->dev, 1);
+ device_set_wakeup_enable(&pdev->dev, 0);
+
+ platform_set_drvdata(pdev, unp);
+
+ return 0;
+}
+
+static __devexit int ns921x_uart_pdrv_remove(struct platform_device *pdev)
+{
+ int line;
+ struct uart_ns921x_port *unp = platform_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ line = unp->port.line;
+
+ uart_remove_one_port(&ns921x_uart_reg, &unp->port);
+ clk_put(unp->clk);
+ iounmap(unp->port.membase);
+ kfree(unp);
+ ns921x_uart_ports[line] = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ns921x_uart_pdrv_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct uart_ns921x_port *unp = platform_get_drvdata(pdev);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ if (device_may_wakeup(&pdev->dev)) {
+ ret = enable_irq_wake(unp->port.irq);
+ if (ret)
+ dev_dbg(&pdev->dev, "%s: err_enable_irq_wake -> %d\n",
+ __func__, ret);
+
+ else {
+ uartwrite32(&unp->port, UART_RCMC0_ENABLE |
+ UART_RCMC0_MASK, UART_RCMC0);
+ /* Write a 0 and then enable to clear previous interrupts */
+ uartwrite32(&unp->port, 0x0, UART_AWC);
+ uartwrite32(&unp->port, UART_AWC_ENABLE, UART_AWC);
+ }
+ } else
+ ret = uart_suspend_port(&ns921x_uart_reg, &unp->port);
+
+ return ret;
+}
+
+static int ns921x_uart_pdrv_resume(struct platform_device *pdev)
+{
+ struct uart_ns921x_port *unp = platform_get_drvdata(pdev);
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ if (device_may_wakeup(&pdev->dev)) {
+ uartwrite32(&unp->port, 0x0, UART_RCMC0);
+ ret = disable_irq_wake(unp->port.irq);
+ }
+ else
+ ret = uart_resume_port(&ns921x_uart_reg, &unp->port);
+
+ return ret;
+}
+#else
+#define ns921x_uart_pdrv_suspend NULL
+#define ns921x_uart_pdrv_resume NULL
+#endif
+
+static struct platform_driver ns921x_uart_pdriver = {
+ .probe = ns921x_uart_pdrv_probe,
+ .remove = __devexit_p(ns921x_uart_pdrv_remove),
+ .suspend = ns921x_uart_pdrv_suspend,
+ .resume = ns921x_uart_pdrv_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ns921x_uart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&ns921x_uart_reg);
+ if (ret) {
+ pr_debug("%s: err_uart_register_driver\n", __func__);
+ goto err_uart_register_driver;
+ }
+
+ ret = platform_driver_register(&ns921x_uart_pdriver);
+
+ if (ret) {
+ pr_debug("%s: err_pdrv_register\n", __func__);
+
+ uart_unregister_driver(&ns921x_uart_reg);
+err_uart_register_driver:
+
+ return ret;
+ }
+ pr_info("Digi NS921x UART driver\n");
+
+ return 0;
+}
+
+static void __exit ns921x_uart_exit(void)
+{
+ platform_driver_unregister(&ns921x_uart_pdriver);
+ uart_unregister_driver(&ns921x_uart_reg);
+}
+
+module_init(ns921x_uart_init);
+module_exit(ns921x_uart_exit);
+
+MODULE_AUTHOR("Uwe Kleine-Koenig");
+MODULE_DESCRIPTION("Digi NS921x UART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/serial/ns9360-serial.c b/drivers/serial/ns9360-serial.c
new file mode 100644
index 000000000000..ac37f6f5fcab
--- /dev/null
+++ b/drivers/serial/ns9360-serial.c
@@ -0,0 +1,878 @@
+/*
+ * drivers/serial/ns9360-serial.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Based on drivers/serial/ns9xxx_serial.c by Markus Pietrek
+ */
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+
+/* register offsets */
+
+#define UART_CTRLA 0x00
+#define UART_CTRLA_CE (1 << 31)
+#define UART_CTRLA_BRK (1 << 30)
+#define UART_CTRLA_EPS (1 << 28)
+#define UART_CTRLA_PE (1 << 27)
+#define UART_CTRLA_STOP (1 << 26)
+#define UART_CTRLA_WLS_5 (0)
+#define UART_CTRLA_WLS_6 (1 << 24)
+#define UART_CTRLA_WLS_7 (2 << 24)
+#define UART_CTRLA_WLS_8 (3 << 24)
+#define UART_CTRLA_DTR (1 << 17)
+#define UART_CTRLA_RTS (1 << 16)
+#define UART_CTRLA_RIE_RDY (1 << 11)
+#define UART_CTRLA_RIE_CLOSED (1 << 9)
+#define UART_CTRLA_RIE_MASK (0xe00)
+#define UART_CTRLA_RIC_MASK (0xe0)
+#define UART_CTRLA_TIC_HALF (1 << 2)
+#define UART_CTRLA_TIC_MASK (0x1e)
+
+#define UART_CTRLB 0x04
+#define UART_CTRLB_RCGT (1 << 26)
+
+#define UART_STATUSA 0x08
+#define UART_STATUSA_RXFDB_MASK (3 << 20)
+#define UART_STATUSA_DCD (1 << 19)
+#define UART_STATUSA_RI (1 << 18)
+#define UART_STATUSA_DSR (1 << 17)
+#define UART_STATUSA_CTS (1 << 16)
+#define UART_STATUSA_RBRK (1 << 15)
+#define UART_STATUSA_RFE (1 << 14)
+#define UART_STATUSA_RPE (1 << 13)
+#define UART_STATUSA_ROVER (1 << 12)
+#define UART_STATUSA_RRDY (1 << 11)
+#define UART_STATUSA_RBC (1 << 9)
+#define UART_STATUSA_TRDY (1 << 3)
+#define UART_STATUSA_TEMPTY (1 << 0)
+
+#define UART_FIFO 0x10
+
+#define UART_BITRATE 0x0c
+#define UART_BITRATE_EBIT (1 << 31)
+#define UART_BITRATE_TMODE (1 << 30)
+#define UART_BITRATE_CLKMUX_BCLK (1 << 24)
+#define UART_BITRATE_TCDR_16 (1 << 20)
+#define UART_BITRATE_RCDR_16 (1 << 18)
+#define UART_BITRATE_N_MASK (0x7fff)
+
+#define UART_RXCHARTIMER 0x18
+#define UART_RXCHARTIMER_TRUN (1 << 31)
+
+#if defined(CONFIG_SERIAL_NS9360_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#define DRIVER_NAME "ns9360-serial"
+
+#ifdef CONFIG_SERIAL_NS9360_COMPAT
+# define NS9360_TTY_NAME "ttyS"
+# define NS9360_TTY_MAJOR TTY_MAJOR
+# define NS9360_TTY_MINOR_START 64
+#else
+# define NS9360_TTY_NAME "ttyNS"
+# define NS9360_TTY_MAJOR 204
+# define NS9360_TTY_MINOR_START 200
+#endif
+
+#define NS9360_UART_NR 4
+
+#define up2unp(up) container_of(up, struct uart_ns9360_port, port)
+struct uart_ns9360_port {
+ struct uart_port port;
+ struct clk *clk;
+};
+
+#ifdef CONFIG_SERIAL_NS9750_CONSOLE
+
+#endif
+
+static inline u32 uartread32(struct uart_port *port, unsigned int offset)
+{
+ u32 ret = ioread32(port->membase + offset);
+
+#if defined(DEBUG_UARTRW)
+ dev_info(port->dev, "read 0x%p -> 0x%08x\n",
+ port->membase + offset, ret);
+#endif
+
+ return ret;
+}
+
+static inline void uartwrite32(struct uart_port *port,
+ u32 value, unsigned int offset)
+{
+#if defined(DEBUG_UARTRW)
+ dev_info(port->dev, "write 0x%p <- 0x%08x\n",
+ port->membase + offset, value);
+#endif
+ iowrite32(value, port->membase + offset);
+}
+
+static inline void uartwrite8(struct uart_port *port, u8 value,
+ unsigned int offset)
+{
+#if defined(DEBUG_UARTRW)
+ dev_info(port->dev, "write 0x%p <- 0x%02x\n",
+ port->membase + offset, value);
+#endif
+ iowrite8(value, port->membase + offset);
+}
+
+static inline void ns9360_uart_wait_fifo_empty(struct uart_port *port)
+{
+ while (!(uartread32(port, UART_STATUSA) & UART_STATUSA_TEMPTY))
+ udelay(1);
+
+ /* TODO: until we have buffer closed again, do just a delay, enough
+ * for 38400 baud */
+ mdelay(1);
+}
+
+static inline void ns9360_uart_wait_xmitr(struct uart_port *port)
+{
+ while (!(uartread32(port, UART_STATUSA) & UART_STATUSA_TRDY))
+ udelay(1);
+}
+
+/* called with port->lock taken */
+static void ns9360_uart_stop_tx(struct uart_port *port)
+{
+ u32 ctrl;
+ struct uart_ns9360_port *unp = up2unp(port);
+
+ assert_spin_locked(&unp->port.lock);
+
+ /* disable TIC_HALF */
+ ctrl = uartread32(port, UART_CTRLA);
+ ctrl &= ~UART_CTRLA_TIC_HALF;
+ uartwrite32(port, ctrl, UART_CTRLA);
+}
+
+/* receive chars from serial fifo and store them in tty
+ * This is called with port->lock taken */
+static void ns9360_uart_rx_chars(struct uart_ns9360_port *unp)
+{
+ struct tty_struct *tty = unp->port.info->port.tty;
+ u32 status, available, characters, flag;
+
+ /* acknowledge rbc if set */
+ status = uartread32(&unp->port, UART_STATUSA);
+ if (status & UART_STATUSA_RBC) {
+ uartwrite32(&unp->port, UART_STATUSA_RBC, UART_STATUSA);
+ status = uartread32(&unp->port, UART_STATUSA);
+ }
+
+ while (status & UART_STATUSA_RRDY) {
+ available = (status & UART_STATUSA_RXFDB_MASK) >> 20;
+ available = available ? : 4;
+
+ flag = TTY_NORMAL;
+
+ if (status & UART_STATUSA_ROVER) {
+ unp->port.icount.overrun++;
+ flag |= TTY_OVERRUN;
+ }
+ if (status & UART_STATUSA_RFE) {
+ unp->port.icount.frame++;
+ flag |= TTY_FRAME;
+ }
+ if (status & UART_STATUSA_RPE) {
+ unp->port.icount.parity++;
+ flag |= TTY_PARITY;
+ }
+ if (status & UART_STATUSA_RBRK) {
+ unp->port.icount.brk++;
+ flag |= TTY_BREAK;
+ }
+
+ /* read characters from fifo */
+ characters = uartread32(&unp->port, UART_FIFO);
+ do {
+ unp->port.icount.rx++;
+ /* TODO: switch to uart_insert_char */
+ tty_insert_flip_char(tty, characters & 0xff, flag);
+ characters >>= 8;
+ } while (--available);
+
+ status = uartread32(&unp->port, UART_STATUSA);
+ }
+
+ if (!tty->low_latency)
+ tty_flip_buffer_push(tty);
+
+#ifdef SUPPORT_SYSRQ
+ sport->port.sysrq = 0;
+#endif
+}
+
+/* send out chars in xmit buffer. This is called with port->lock taken */
+static void ns9360_uart_tx_chars(struct uart_ns9360_port *unp)
+{
+ struct circ_buf *xmit = &unp->port.info->xmit;
+
+ assert_spin_locked(&unp->port.lock);
+
+ if (unp->port.x_char) {
+ uartwrite8(&unp->port, unp->port.x_char, UART_FIFO);
+ unp->port.icount.tx++;
+ unp->port.x_char = 0;
+ } else
+ if (!uart_circ_empty(xmit) && !uart_tx_stopped(&unp->port)) {
+ while (uartread32(&unp->port, UART_STATUSA) &
+ UART_STATUSA_TRDY) {
+ uartwrite8(&unp->port, xmit->buf[xmit->tail],
+ UART_FIFO);
+ xmit->tail = (xmit->tail + 1) &
+ (UART_XMIT_SIZE - 1);
+ unp->port.icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ }
+
+ /* wakup ourself if number of pending chars do not reach
+ * the minimum wakeup level */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&unp->port);
+ }
+
+ /* if uart is empty now ? */
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&unp->port))
+ ns9360_uart_stop_tx(&unp->port);
+}
+
+static irqreturn_t ns9360_uart_rxint(int irq, void *dev_id)
+{
+ struct uart_ns9360_port *unp = dev_id;
+
+ spin_lock(&unp->port.lock);
+ ns9360_uart_rx_chars(unp);
+ spin_unlock(&unp->port.lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ns9360_uart_txint(int irq, void *dev_id)
+{
+ struct uart_ns9360_port *unp = dev_id;
+
+ spin_lock(&unp->port.lock);
+ ns9360_uart_tx_chars(unp);
+ spin_unlock(&unp->port.lock);
+
+ return IRQ_HANDLED;
+}
+
+static void ns9360_uart_release_port(struct uart_port *port)
+{
+ /* XXX: release_mem_region is marked as Compatibility cruft ??? */
+ release_mem_region(port->mapbase, 0x3f);
+}
+
+static int ns9360_uart_request_port(struct uart_port *port)
+{
+ return request_mem_region(port->mapbase, 0x3f,
+ DRIVER_NAME) ? 0 : -EBUSY;
+}
+
+static void ns9360_uart_enable_ms(struct uart_port *port)
+{
+ u32 status;
+
+ status = uartread32(port, UART_STATUSA);
+ /* status |= NS_SER_CTRL_A_RIC_MA; */
+ /* FIXME: sense? */
+ uartwrite32(port, status, UART_STATUSA);
+}
+
+static void ns9360_uart_shutdown(struct uart_port *port)
+{
+ struct uart_ns9360_port *unp = up2unp(port);
+ unsigned long flags;
+ u32 ctrl;
+
+ ns9360_uart_wait_fifo_empty(port);
+
+ spin_lock_irqsave(&port->lock, flags);
+ ctrl = uartread32(port, UART_CTRLA);
+ ctrl &= ~UART_CTRLA_RIE_RDY;
+ uartwrite32(port, ctrl, UART_CTRLA);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ free_irq(port->irq, unp);
+ free_irq(port->irq + 1, unp);
+
+ clk_disable(unp->clk);
+}
+
+static int ns9360_uart_startup(struct uart_port *port)
+{
+ struct uart_ns9360_port *unp = up2unp(port);
+ int ret;
+ u32 ctrl;
+ unsigned long flags;
+
+ ret = clk_enable(unp->clk);
+ if (ret) {
+ dev_info(port->dev, "%s: err_clk_enable", __func__);
+ goto err_clk_enable;
+ }
+
+ unp->port.uartclk = clk_get_rate(unp->clk);
+
+ ret = request_irq(unp->port.irq, ns9360_uart_rxint, 0,
+ DRIVER_NAME, unp);
+ if (ret) {
+ dev_info(port->dev, "%s: err_request_irq_rx", __func__);
+ goto err_request_irq_rx;
+ }
+
+ ret = request_irq(unp->port.irq + 1, ns9360_uart_txint, 0,
+ DRIVER_NAME, unp);
+ if (ret) {
+ dev_info(port->dev, "%s: err_request_irq_tx", __func__);
+ goto err_request_irq_tx;
+ }
+
+ /* enable receive interrupts */
+ spin_lock_irqsave (&unp->port.lock, flags);
+ ctrl = uartread32(&unp->port, UART_CTRLA);
+ ctrl |= UART_CTRLA_CE | UART_CTRLA_RIE_RDY | UART_CTRLA_RIE_CLOSED;
+ uartwrite32(&unp->port, ctrl, UART_CTRLA);
+
+ /* enable modem status interrupts */
+ ns9360_uart_enable_ms(&unp->port);
+ spin_unlock_irqrestore (&unp->port.lock, flags);
+
+ /* enable character gap timer */
+ uartwrite32(&unp->port, UART_CTRLB_RCGT, UART_CTRLB);
+
+ return 0;
+
+err_request_irq_tx:
+ free_irq(unp->port.irq, unp);
+err_request_irq_rx:
+ clk_disable(unp->clk);
+err_clk_enable:
+ return ret;
+}
+
+static void ns9360_uart_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old)
+{
+ struct uart_ns9360_port *unp = up2unp(port);
+ unsigned long flags;
+ unsigned int baud, quot, nr_bits;
+ u32 ctrl, bitrate, gap_timer;
+
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+ quot = uart_get_divisor(port, baud);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ /* read out configuration and mask out bits going to be updated */
+ ctrl = uartread32(port, UART_CTRLA);
+ ctrl &= ~(UART_CTRLA_WLS_8 | UART_CTRLA_STOP | UART_CTRLA_PE |
+ UART_CTRLA_EPS);
+
+ ctrl |= UART_CTRLA_CE;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ ctrl |= UART_CTRLA_WLS_5;
+ nr_bits = 5;
+ break;
+ case CS6:
+ ctrl |= UART_CTRLA_WLS_6;
+ nr_bits = 6;
+ break;
+ case CS7:
+ ctrl |= UART_CTRLA_WLS_7;
+ nr_bits = 7;
+ break;
+ default:
+ case CS8:
+ ctrl |= UART_CTRLA_WLS_8;
+ nr_bits = 8;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB) {
+ ctrl |= UART_CTRLA_STOP;
+ nr_bits++;
+ }
+
+ if (termios->c_cflag & PARENB) {
+ ctrl |= UART_CTRLA_PE;
+ nr_bits++;
+ }
+
+ if (!(termios->c_cflag & PARODD)) {
+ ctrl |= UART_CTRLA_EPS;
+ nr_bits++;
+ }
+
+ /* set configuration */
+ ns9360_uart_wait_fifo_empty(port);
+ uartwrite32(port, ctrl, UART_CTRLA);
+
+ /* set baudrate */
+ bitrate = UART_BITRATE_EBIT | UART_BITRATE_CLKMUX_BCLK |
+ UART_BITRATE_TMODE | UART_BITRATE_TCDR_16 |
+ UART_BITRATE_RCDR_16 | ((quot - 1) & UART_BITRATE_N_MASK);
+ uartwrite32(port, bitrate, UART_BITRATE);
+
+ /* set character gap timer */
+ gap_timer = UART_RXCHARTIMER_TRUN;
+
+ /* calculate gap timer */
+ gap_timer |= (clk_get_rate(unp->clk) * nr_bits / baud / 8) - 1;
+ pr_debug(DRIVER_NAME " %s: gap-timer = %08x (baud=%i, clk=%li)\n",
+ __func__, gap_timer, baud, clk_get_rate(unp->clk));
+
+ uartwrite32(port, gap_timer, UART_RXCHARTIMER);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static unsigned int ns9360_uart_tx_empty(struct uart_port *port)
+{
+ u32 status;
+
+ status = uartread32(port, UART_STATUSA);
+ return (status & UART_STATUSA_TRDY) ? TIOCSER_TEMT : 0;
+}
+
+static void ns9360_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ u32 mcr;
+
+ mcr = uartread32(port, UART_CTRLA);
+
+ if (mctrl & TIOCM_RTS)
+ mcr &= ~UART_CTRLA_RTS;
+ else
+ mcr |= UART_CTRLA_RTS;
+
+ if (mctrl & TIOCM_DTR)
+ mcr &= ~UART_CTRLA_DTR;
+ else
+ mcr |= UART_CTRLA_DTR;
+
+ uartwrite32(port, mcr, UART_CTRLA);
+}
+
+static unsigned int ns9360_uart_get_mctrl(struct uart_port *port)
+{
+ unsigned int status;
+ unsigned int ret = 0;
+
+ status = uartread32(port, UART_STATUSA);
+
+ if (!(status & UART_STATUSA_DCD))
+ ret |= TIOCM_CD;
+ if (!(status & UART_STATUSA_CTS))
+ ret |= TIOCM_CTS;
+ if (!(status & UART_STATUSA_DSR))
+ ret |= TIOCM_DSR;
+ if (!(status & UART_STATUSA_RI))
+ ret |= TIOCM_RI;
+
+ return ret;
+}
+
+static int ns9360_uart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ struct uart_ns9360_port *unp = up2unp(port);
+ int ret = 0;
+
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_NS9360)
+ ret = -EINVAL;
+
+ if (ser->irq != unp->port.irq)
+ ret = -EINVAL;
+
+ if (ser->io_type != UPIO_MEM)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static void ns9360_uart_start_tx(struct uart_port *port)
+{
+ u32 ctrl;
+
+ ctrl = uartread32(port, UART_CTRLA);
+ ctrl |= UART_CTRLA_TIC_HALF;
+ uartwrite32(port, ctrl, UART_CTRLA);
+}
+
+static void ns9360_uart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ if (!ns9360_uart_request_port(port))
+ port->type = PORT_NS9360;
+}
+
+static const char *ns9360_uart_type(struct uart_port *port)
+{
+ return port->type == PORT_NS9360 ? "NS9360" : NULL;
+}
+
+static void ns9360_uart_break_ctl(struct uart_port *port, int break_state)
+{
+ u32 ctrl;
+
+ ctrl = uartread32(port, UART_CTRLA);
+
+ if (break_state == -1)
+ ctrl |= UART_CTRLA_BRK;
+ else
+ ctrl &= ~UART_CTRLA_BRK;
+
+ uartwrite32(port, ctrl, UART_CTRLA);
+}
+
+static void ns9360_uart_stop_rx(struct uart_port *port)
+{
+ u32 ctrl;
+
+ ctrl = uartread32(port, UART_CTRLA);
+ ctrl &= ~(UART_CTRLA_RIE_RDY | UART_CTRLA_RIE_CLOSED);
+ uartwrite32(port, ctrl, UART_CTRLA);
+}
+
+static struct uart_ops ns9360_uart_pops = {
+ .stop_rx = ns9360_uart_stop_rx,
+ .enable_ms = ns9360_uart_enable_ms,
+ .break_ctl = ns9360_uart_break_ctl,
+ .type = ns9360_uart_type,
+ .config_port = ns9360_uart_config_port,
+ .start_tx = ns9360_uart_start_tx,
+ .verify_port = ns9360_uart_verify_port,
+ .set_mctrl = ns9360_uart_set_mctrl,
+ .get_mctrl = ns9360_uart_get_mctrl,
+ .tx_empty = ns9360_uart_tx_empty,
+ .set_termios = ns9360_uart_set_termios,
+ .shutdown = ns9360_uart_shutdown,
+ .startup = ns9360_uart_startup,
+ .stop_tx = ns9360_uart_stop_tx,
+ .release_port = ns9360_uart_release_port,
+ .request_port = ns9360_uart_request_port,
+};
+
+static struct uart_ns9360_port *ns9360_uart_ports[NS9360_UART_NR];
+
+#ifdef CONFIG_SERIAL_NS9360_CONSOLE
+
+static void ns9360_uart_console_write(struct console *co,
+ const char *s, unsigned int count)
+{
+ struct uart_ns9360_port *unp = ns9360_uart_ports[co->index];
+ u32 ctrl, saved_ctrl;
+
+ /* save current state */
+ saved_ctrl = uartread32(&unp->port, UART_CTRLA);
+
+ /* keep configuration, disable interrupts and enable uart */
+ ctrl = (saved_ctrl | UART_CTRLA_CE) & ~(UART_CTRLA_TIC_MASK |
+ UART_CTRLA_RIC_MASK | UART_CTRLA_RIE_MASK);
+ uartwrite32(&unp->port, ctrl, UART_CTRLA);
+
+ while (count) {
+ ns9360_uart_wait_xmitr(&unp->port);
+
+ if (*s == '\n') {
+ uartwrite8(&unp->port, '\r', UART_FIFO);
+ ns9360_uart_wait_xmitr(&unp->port);
+ }
+
+ uartwrite8(&unp->port, *s, UART_FIFO);
+
+ count--;
+ s++;
+ }
+
+ /* wait for fifo empty if uart was (and will be) disabled */
+ if (!(saved_ctrl & UART_CTRLA_CE))
+ ns9360_uart_wait_fifo_empty(&unp->port);
+
+ /* recover state */
+ uartwrite32(&unp->port, saved_ctrl, UART_CTRLA);
+}
+
+/* If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup. */
+static void __init ns9360_uart_console_get_options(struct uart_ns9360_port *unp,
+ int *baud, int *parity, int *bits, int *flow)
+{
+ u32 ctrl, bitrate;
+
+ ctrl = uartread32(&unp->port, UART_CTRLA);
+ bitrate = uartread32(&unp->port, UART_BITRATE);
+
+ /* not enabled, don't modify defaults */
+ if (!(ctrl & UART_CTRLA_CE))
+ return;
+
+ switch (ctrl & UART_CTRLA_WLS_8) {
+ case UART_CTRLA_WLS_5:
+ *bits = 5;
+ break;
+ case UART_CTRLA_WLS_6:
+ *bits = 6;
+ break;
+ case UART_CTRLA_WLS_7:
+ *bits = 7;
+ break;
+ default:
+ case UART_CTRLA_WLS_8:
+ *bits = 8;
+ break;
+ }
+
+ *parity = 'n';
+ if (ctrl & UART_CTRLA_PE) {
+ /* parity enabled */
+ if (ctrl & UART_CTRLA_EPS)
+ *parity = 'o';
+ else
+ *parity = 'e';
+ }
+
+ *baud = unp->port.uartclk /
+ (((bitrate & UART_BITRATE_N_MASK) + 1) * 16);
+}
+
+static int __init ns9360_uart_console_setup(struct console *co, char *options)
+{
+ struct uart_ns9360_port *unp;
+ int baud = 38400;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index >= NS9360_UART_NR)
+ co->index = 0;
+
+ unp = ns9360_uart_ports[co->index];
+ if (!unp)
+ return -ENODEV;
+
+ /* XXX: assert unp->clk is enabled */
+ unp->port.uartclk = clk_get_rate(unp->clk);
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ ns9360_uart_console_get_options(unp,
+ &baud, &parity, &bits, &flow);
+
+ /* Enable UART. For some strange reason, in NS9360 the UART
+ * must be enabled in the setup, rather than in the write
+ * function, otherwise nothing is printed and the system
+ * doesn't boot */
+ uartwrite32(&unp->port,
+ uartread32(&unp->port, UART_CTRLA) | UART_CTRLA_CE,
+ UART_CTRLA);
+
+ return uart_set_options(&unp->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver ns9360_uart_reg;
+static struct console ns9360_uart_console = {
+ .name = NS9360_TTY_NAME,
+ .write = ns9360_uart_console_write,
+ .device = uart_console_device,
+ .setup = ns9360_uart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &ns9360_uart_reg,
+};
+
+#define NS9360_UART_CONSOLE (&ns9360_uart_console)
+#else
+#define NS9360_UART_CONSOLE NULL
+#endif
+
+static struct uart_driver ns9360_uart_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = NS9360_TTY_NAME,
+ .major = NS9360_TTY_MAJOR,
+ .minor = NS9360_TTY_MINOR_START,
+ .nr = NS9360_UART_NR,
+ .cons = NS9360_UART_CONSOLE,
+};
+
+static __devinit int ns9360_uart_pdrv_probe(struct platform_device *pdev)
+{
+ int ret, line, irq;
+ struct uart_ns9360_port *unp;
+ struct resource *mem;
+ void __iomem *base;
+
+ line = pdev->id;
+ if (line < 0 || line >= ARRAY_SIZE(ns9360_uart_ports)) {
+ ret = -ENODEV;
+ dev_info(&pdev->dev, "%s: err_line\n", __func__);
+ goto err_line;
+ }
+
+ if (ns9360_uart_ports[line] != NULL) {
+ ret = -EBUSY;
+ dev_info(&pdev->dev, "%s: err_line\n", __func__);
+ goto err_line;
+ }
+
+ unp = kzalloc(sizeof(struct uart_ns9360_port), GFP_KERNEL);
+ ns9360_uart_ports[line] = unp;
+
+ if (unp == NULL) {
+ ret = -ENOMEM;
+ dev_info(&pdev->dev, "%s: err_alloc\n", __func__);
+ goto err_alloc;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENOENT;
+ dev_info(&pdev->dev, "%s: err_get_irq\n", __func__);
+ goto err_get_irq;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ ret = -ENODEV;
+ dev_info(&pdev->dev, "%s: err_get_mem\n", __func__);
+ goto err_get_mem;
+ }
+
+ base = ioremap(mem->start, 0x3f);
+ if (!base) {
+ ret = -ENOMEM;
+ dev_info(&pdev->dev, "%s: err_ioremap\n", __func__);
+ goto err_ioremap;
+ }
+ dev_info(&pdev->dev, "%s: membase = %p, unp = %p\n",
+ __func__, base, unp);
+
+ unp->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(unp->clk)) {
+ ret = PTR_ERR(unp->clk);
+ dev_info(&pdev->dev, "%s: err_clk_get -> %d\n", __func__, ret);
+ goto err_clk_get;
+ }
+
+ unp->port.dev = &pdev->dev;
+ unp->port.mapbase = mem->start;
+ unp->port.membase = base;
+ unp->port.iotype = UPIO_MEM;
+ unp->port.irq = irq;
+ unp->port.fifosize = 64; /* XXX */
+ unp->port.ops = &ns9360_uart_pops;
+ unp->port.flags = UPF_BOOT_AUTOCONF;
+ unp->port.line = line;
+
+ ret = uart_add_one_port(&ns9360_uart_reg, &unp->port);
+ if (ret) {
+ dev_info(&pdev->dev, "%s: err_uart_add1port -> %d\n",
+ __func__, ret);
+ goto err_uart_add1port;
+ }
+
+ return 0;
+
+err_uart_add1port:
+ clk_put(unp->clk);
+err_clk_get:
+ iounmap(base);
+err_ioremap:
+ release_resource(mem);
+err_get_mem:
+err_get_irq:
+ kfree(unp);
+ ns9360_uart_ports[line] = NULL;
+err_alloc:
+err_line:
+ return ret;
+}
+
+static __devexit int ns9360_uart_pdrv_remove(struct platform_device *pdev)
+{
+ int line;
+ struct uart_ns9360_port *unp = platform_get_drvdata(pdev);
+
+ line = unp->port.line;
+
+ uart_remove_one_port(&ns9360_uart_reg, &unp->port);
+ clk_put(unp->clk);
+ iounmap(unp->port.membase);
+ kfree(unp);
+ ns9360_uart_ports[line] = NULL;
+
+ return 0;
+}
+
+static struct platform_driver ns9360_uart_pdriver = {
+ .probe = ns9360_uart_pdrv_probe,
+ .remove = __devexit_p(ns9360_uart_pdrv_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ns9360_uart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&ns9360_uart_reg);
+ if (ret) {
+ pr_debug("%s: unable to register uart driver\n", __func__);
+ goto err_uart_register_driver;
+ }
+
+ ret = platform_driver_register(&ns9360_uart_pdriver);
+ if (ret) {
+ pr_debug("%s: unable to register platform driver\n", __func__);
+ goto err_platform_driver_register;
+ }
+
+ pr_info("Digi NS9360 UART driver\n");
+
+ return 0;
+
+err_platform_driver_register:
+ uart_unregister_driver(&ns9360_uart_reg);
+err_uart_register_driver:
+ return ret;
+}
+
+static void __exit ns9360_uart_exit(void)
+{
+ platform_driver_unregister(&ns9360_uart_pdriver);
+ uart_unregister_driver(&ns9360_uart_reg);
+}
+
+module_init(ns9360_uart_init);
+module_exit(ns9360_uart_exit);
+
+MODULE_DESCRIPTION("Digi NS9360 UART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c
index 1e219d3d0352..ea30bc6d84e3 100644
--- a/drivers/serial/samsung.c
+++ b/drivers/serial/samsung.c
@@ -63,8 +63,12 @@
#ifdef CONFIG_CPU_S3C2400
#define NR_PORTS (2)
#else
+#if defined(CONFIG_MACH_CC9M2443JS) || defined(CONFIG_MACH_CCW9M2443JS)
+#define NR_PORTS (4)
+#else
#define NR_PORTS (3)
#endif
+#endif
/* port irq numbers */
@@ -827,7 +831,7 @@ static struct uart_ops s3c24xx_serial_ops = {
static struct uart_driver s3c24xx_uart_drv = {
.owner = THIS_MODULE,
.dev_name = "s3c2410_serial",
- .nr = 3,
+ .nr = NR_PORTS,
.cons = S3C24XX_SERIAL_CONSOLE,
.driver_name = S3C24XX_SERIAL_NAME,
.major = S3C24XX_SERIAL_MAJOR,
@@ -872,7 +876,20 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
}
- }
+ },
+ [3] = {
+ .port = {
+ .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
+ .iotype = UPIO_MEM,
+ .irq = IRQ_S3C2443_RX3,
+ .uartclk = 0,
+ .fifosize = 16,
+ .ops = &s3c24xx_serial_ops,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 3,
+ }
+ }
+
#endif
};
@@ -945,6 +962,10 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
port->mapbase = res->start;
port->membase = S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART);
+
+ /* Use the number of the HW port as line number (Luis Galdos) */
+ port->line = cfg->hwport;
+
ret = platform_get_irq(platdev, 0);
if (ret < 0)
port->irq = 0;
diff --git a/drivers/serial/stmp-app.c b/drivers/serial/stmp-app.c
new file mode 100644
index 000000000000..6a56c444cf20
--- /dev/null
+++ b/drivers/serial/stmp-app.c
@@ -0,0 +1,1064 @@
+/*
+ * Freescale STMP37XX/STMP378X Application UART driver
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
+#include <asm/cacheflush.h>
+#include <mach/hardware.h>
+#include <mach/regs-apbx.h>
+#include <mach/regs-uartapp.h>
+#include <mach/regs-pinctrl.h>
+#include <mach/stmp3xxx.h>
+
+#include <asm/mach-types.h>
+
+#include "stmp-app.h"
+
+static int pio_mode /* = 0 */; /* PIO mode = 1, DMA mode = 0 */
+
+static struct platform_driver stmp_appuart_driver = {
+ .probe = stmp_appuart_probe,
+ .remove = __devexit_p(stmp_appuart_remove),
+ .suspend = stmp_appuart_suspend,
+ .resume = stmp_appuart_resume,
+ .driver = {
+ .name = "stmp37xx-appuart",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct uart_driver stmp_appuart_uart = {
+ .owner = THIS_MODULE,
+ .driver_name = "appuart",
+ .dev_name = "ttySP",
+ .major = 242,
+ .minor = 0,
+ .nr = 1,
+};
+
+static inline struct stmp_appuart_port *to_appuart(struct uart_port *u)
+{
+ return container_of(u, struct stmp_appuart_port, port);
+}
+
+static struct uart_ops stmp_appuart_ops = {
+ .tx_empty = stmp_appuart_tx_empty,
+ .start_tx = stmp_appuart_start_tx,
+ .stop_tx = stmp_appuart_stop_tx,
+ .stop_rx = stmp_appuart_stop_rx,
+ .enable_ms = stmp_appuart_enable_ms,
+ .break_ctl = stmp_appuart_break_ctl,
+ .set_mctrl = stmp_appuart_set_mctrl,
+ .get_mctrl = stmp_appuart_get_mctrl,
+ .startup = stmp_appuart_startup,
+ .shutdown = stmp_appuart_shutdown,
+ .set_termios = stmp_appuart_settermios,
+ .type = stmp_appuart_type,
+ .release_port = stmp_appuart_release_port,
+ .request_port = stmp_appuart_request_port,
+ .config_port = stmp_appuart_config_port,
+ .verify_port = stmp_appuart_verify_port,
+};
+
+static inline int chr(int c)
+{
+ if (c < 0x20 || c > 0x7F)
+ return '#';
+ return c;
+}
+
+/* Allocate and initialize rx and tx DMA chains */
+static inline int stmp_appuart_dma_init(struct stmp_appuart_port *s)
+{
+ int err = 0;
+ struct stmp3xxx_dma_descriptor *t = &s->tx_desc;
+#ifndef RX_CHAIN
+ struct stmp3xxx_dma_descriptor *r = &s->rx_desc;
+#else
+ int i;
+#endif
+
+ err = stmp3xxx_dma_request(s->dma_rx, s->dev, s->dev->bus_id);
+ if (err)
+ goto out;
+ err = stmp3xxx_dma_request(s->dma_tx, s->dev, s->dev->bus_id);
+ if (err)
+ goto out1;
+
+#ifndef RX_CHAIN
+ err = stmp3xxx_dma_allocate_command(s->dma_rx, r);
+ if (err)
+ goto out2;
+#endif
+ err = stmp3xxx_dma_allocate_command(s->dma_tx, t);
+ if (err)
+ goto out3;
+ t->virtual_buf_ptr = dma_alloc_coherent(s->dev,
+ TX_BUFFER_SIZE,
+ &t->command->buf_ptr,
+ GFP_DMA);
+ if (!t->virtual_buf_ptr)
+ goto out4;
+#ifdef DEBUG
+ memset(t->virtual_buf_ptr, 0x4B, TX_BUFFER_SIZE);
+#endif
+
+#ifndef RX_CHAIN
+ r->virtual_buf_ptr = dma_alloc_coherent(s->dev,
+ RX_BUFFER_SIZE,
+ &r->command->buf_ptr,
+ GFP_DMA);
+ if (!r->virtual_buf_ptr)
+ goto out5;
+#ifdef DEBUG
+ memset(r->virtual_buf_ptr, 0x4C, RX_BUFFER_SIZE);
+#endif
+#else
+ stmp3xxx_dma_make_chain(s->dma_rx, &s->rx_chain, s->rxd, RX_CHAIN);
+ for (i = 0; i < RX_CHAIN; i++) {
+ struct stmp3xxx_dma_descriptor *r = s->rxd + i;
+
+ r->command->cmd =
+ BF_APBX_CHn_CMD_XFER_COUNT(RX_BUFFER_SIZE) |
+ BF_APBX_CHn_CMD_CMDWORDS(1) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_SEMAPHORE |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BM_APBX_CHn_CMD_CHAIN |
+ BF_APBX_CHn_CMD_COMMAND(
+ BV_APBX_CHn_CMD_COMMAND__DMA_WRITE);
+ r->virtual_buf_ptr = dma_alloc_coherent(s->dev,
+ RX_BUFFER_SIZE,
+ &r->command->buf_ptr,
+ GFP_DMA);
+ r->command->pio_words[0] = /* BM_UARTAPP_CTRL0_RUN | */
+ BF_UARTAPP_CTRL0_XFER_COUNT(RX_BUFFER_SIZE)|
+ BM_UARTAPP_CTRL0_RXTO_ENABLE |
+ BF_UARTAPP_CTRL0_RXTIMEOUT(3);
+ }
+#endif
+ return 0;
+
+ /*
+ * would be necessary on other error paths
+
+ dma_free_coherent( s->dev, RX_BUFFER_SIZE, r->virtual_buf_ptr,
+ r->command->buf_ptr);
+ */
+out5:
+ dma_free_coherent(s->dev, TX_BUFFER_SIZE, t->virtual_buf_ptr,
+ t->command->buf_ptr);
+out4:
+ stmp3xxx_dma_free_command(s->dma_tx, t);
+out3:
+#ifndef RX_CHAIN
+ stmp3xxx_dma_free_command(s->dma_rx, r);
+#endif
+out2:
+ stmp3xxx_dma_release(s->dma_tx);
+out1:
+ stmp3xxx_dma_release(s->dma_rx);
+out:
+ WARN_ON(err);
+ return err;
+}
+
+
+static void stmp_appuart_on(struct platform_device *dev)
+{
+ struct stmp_appuart_port *s = platform_get_drvdata(dev);
+
+ if (!pio_mode) {
+ /*
+ Tell DMA to select UART.
+ Both DMA channels are shared between app UART and IrDA.
+ Target id of 0 means UART, 1 means IrDA
+ */
+ stmp3xxx_dma_set_alt_target(s->dma_rx, 0);
+ stmp3xxx_dma_set_alt_target(s->dma_tx, 0);
+ /*
+ Reset DMA channels
+ */
+ stmp3xxx_dma_reset_channel(s->dma_rx);
+ stmp3xxx_dma_reset_channel(s->dma_tx);
+ stmp3xxx_dma_enable_interrupt(s->dma_rx);
+ stmp3xxx_dma_enable_interrupt(s->dma_tx);
+ }
+}
+
+#ifdef CONFIG_CPU_FREQ
+static int stmp_appuart_updateclk(struct device *dev, void *clkdata)
+{
+ struct stmp_appuart_port *s = dev_get_drvdata(dev);
+
+ if (s) {
+ s->port.uartclk = clk_get_rate(s->clk) * 1000;
+ /* FIXME: perform actual update */
+ }
+ return 0;
+}
+
+static int stmp_appuart_notifier(struct notifier_block *self,
+ unsigned long phase, void *p)
+{
+ int r = 0;
+
+ if ((phase == CPUFREQ_POSTCHANGE) ||
+ (phase == CPUFREQ_RESUMECHANGE)) {
+ /* get new uartclock and setspeed */
+ r = driver_for_each_device(
+ &stmp_appuart_driver.driver,
+ NULL, p,
+ stmp_appuart_updateclk);
+ }
+ return (r == 0) ? NOTIFY_OK : NOTIFY_DONE;
+}
+
+static struct notifier_block stmp_appuart_nb = {
+ .notifier_call = &stmp_appuart_notifier,
+};
+#endif /* CONFIG_CPU_FREQ */
+
+static int __devinit stmp_appuart_probe(struct platform_device *device)
+{
+ struct stmp_appuart_port *s;
+ int err = 0;
+ struct resource *r;
+ int i;
+ u32 version;
+ int (*pinctl)(int req, int id);
+
+ s = kzalloc(sizeof(struct stmp_appuart_port), GFP_KERNEL);
+ if (!s) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ spin_lock_init(&s->lock);
+
+ s->clk = clk_get(NULL, "uart");
+ if (IS_ERR(s->clk)) {
+ err = PTR_ERR(s->clk);
+ goto out_free;
+ }
+ clk_enable(s->clk);
+ r = platform_get_resource(device, IORESOURCE_MEM, 0);
+ if (!r) {
+ err = -ENXIO;
+ goto out_free_clk;
+ }
+ s->port.mapbase = r->start;
+ s->port.irq = platform_get_irq(device, 0);
+ s->port.ops = &stmp_appuart_ops;
+ s->port.iotype = UPIO_MEM;
+ s->port.line = device->id < 0 ? 0 : device->id;
+ s->port.fifosize = 16;
+ s->port.timeout = HZ/10;
+ s->port.uartclk = clk_get_rate(s->clk) * 1000;
+ s->port.type = PORT_STMP37xx;
+ s->port.dev = s->dev = get_device(&device->dev);
+ s->ctrl = 0;
+ s->keep_irq = 0;
+
+ r = platform_get_resource(device, IORESOURCE_MEM, 0);
+ if (!r) {
+ err = -ENXIO;
+ goto out_free_clk;
+ }
+
+ dev_dbg(s->dev, "%s\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
+ s->irq[i] = platform_get_irq(device, i);
+ dev_dbg(s->dev, "Resources: irq[%d] = %d\n", i, s->irq[i]);
+ if (s->irq[i] < 0) {
+ err = s->irq[i];
+ goto out_free_clk;
+ }
+ }
+
+ r = platform_get_resource(device, IORESOURCE_DMA, 0);
+ if (!r) {
+ err = -ENXIO;
+ goto out_free;
+ }
+ s->dma_rx = r->start;
+
+ r = platform_get_resource(device, IORESOURCE_DMA, 1);
+ if (!r) {
+ err = -ENXIO;
+ goto out_free;
+ }
+ s->dma_tx = r->start;
+
+ r = platform_get_resource(device, IORESOURCE_MEM, 0);
+ if (!r) {
+ err = -ENXIO;
+ goto out_free;
+ }
+ s->mem = r->start;
+ s->memsize = r->end - r->start;
+
+
+#ifdef CONFIG_CPU_FREQ
+ cpufreq_register_notifier(&stmp_appuart_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+#endif
+ platform_set_drvdata(device, s);
+
+ device_init_wakeup(&device->dev, 1);
+
+ stmp_appuart_dma_init(s);
+ stmp_appuart_on(device);
+
+ pinctl = device->dev.platform_data;
+ if (pinctl) {
+ err = pinctl(1, device->id);
+ if (err)
+ goto out_free_clk;
+ }
+
+ err = uart_add_one_port(&stmp_appuart_uart, &s->port);
+ if (err)
+ goto out_free_pins;
+
+ version = HW_UARTAPP_VERSION_RD();
+ printk(KERN_INFO"Found APPUART %d.%d.%d\n",
+ (version >> 24) & 0xFF,
+ (version >> 16) & 0xFF,
+ version & 0xFFFF);
+ return 0;
+
+out_free_pins:
+ if (pinctl)
+ pinctl(0, device->id);
+out_free_clk:
+ clk_put(s->clk);
+out_free:
+ platform_set_drvdata(device, NULL);
+ kfree(s);
+out:
+ return err;
+}
+
+static int __devexit stmp_appuart_remove(struct platform_device *device)
+{
+ struct stmp_appuart_port *s;
+ void (*pinctl)(int req, int id);
+
+ s = platform_get_drvdata(device);
+ if (s) {
+ pinctl = device->dev.platform_data;
+ put_device(s->dev);
+ clk_disable(s->clk);
+ clk_put(s->clk);
+ uart_remove_one_port(&stmp_appuart_uart, &s->port);
+ if (pinctl)
+ pinctl(0, device->id);
+ kfree(s);
+ platform_set_drvdata(device, NULL);
+ }
+
+ return 0;
+}
+
+static int stmp_appuart_suspend(struct platform_device *device,
+ pm_message_t state)
+{
+#ifdef CONFIG_PM
+ struct stmp_appuart_port *s = platform_get_drvdata(device);
+
+ if (!s)
+ return 0;
+ s->keep_irq = device_may_wakeup(&device->dev);
+ uart_suspend_port(&stmp_appuart_uart, &s->port);
+ if (!s->keep_irq)
+ clk_disable(s->clk);
+#endif
+ return 0;
+}
+
+static int stmp_appuart_resume(struct platform_device *device)
+{
+#ifdef CONFIG_PM
+ struct stmp_appuart_port *s = platform_get_drvdata(device);
+
+ if (!s)
+ return 0;
+
+ if (!s->keep_irq)
+ clk_enable(s->clk);
+ stmp_appuart_on(device);
+ uart_resume_port(&stmp_appuart_uart, &s->port);
+ s->keep_irq = 0;
+#endif
+ return 0;
+}
+
+static int __init stmp_appuart_init()
+{
+ int r;
+
+ r = uart_register_driver(&stmp_appuart_uart);
+ if (r)
+ goto out;
+ r = platform_driver_register(&stmp_appuart_driver);
+ if (r)
+ goto out_err;
+ return 0;
+out_err:
+ uart_unregister_driver(&stmp_appuart_uart);
+out:
+ return r;
+}
+
+static void __exit stmp_appuart_exit()
+{
+ platform_driver_unregister(&stmp_appuart_driver);
+ uart_unregister_driver(&stmp_appuart_uart);
+}
+
+module_init(stmp_appuart_init)
+module_exit(stmp_appuart_exit)
+
+static void stmp_appuart_stop_rx(struct uart_port *u)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+
+ dev_dbg(s->dev, "%s\n", __func__);
+ HW_UARTAPP_CTRL2_CLR_NB(s->mem, BM_UARTAPP_CTRL2_RXE);
+}
+
+static void stmp_appuart_break_ctl(struct uart_port *u, int ctl)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+
+ dev_dbg(s->dev, "%s: break = %s\n", __func__, ctl ? "on" : "off");
+ if (ctl)
+ HW_UARTAPP_LINECTRL_SET_NB(s->mem, BM_UARTAPP_LINECTRL_BRK);
+ else
+ HW_UARTAPP_LINECTRL_CLR_NB(s->mem, BM_UARTAPP_LINECTRL_BRK);
+}
+
+static void stmp_appuart_enable_ms(struct uart_port *port)
+{
+ /* just empty */
+}
+
+static void stmp_appuart_set_mctrl(struct uart_port *u, unsigned mctrl)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+
+ u32 ctrl = HW_UARTAPP_CTRL2_RD_NB(s->mem);
+
+ dev_dbg(s->dev, "%s (%x)\n", __func__, mctrl);
+ ctrl &= ~BM_UARTAPP_CTRL2_RTS;
+ if (mctrl & TIOCM_RTS) {
+ dev_dbg(s->dev, "...RTS\n");
+ ctrl |= BM_UARTAPP_CTRL2_RTS;
+ }
+ s->ctrl = mctrl;
+ dev_dbg(s->dev, "...%x; ctrl = %x\n", s->ctrl, ctrl);
+ HW_UARTAPP_CTRL2_WR_NB(s->mem, ctrl);
+}
+
+static u32 stmp_appuart_get_mctrl(struct uart_port *u)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+ u32 stat = HW_UARTAPP_STAT_RD_NB(s->mem);
+ int ctrl2 = HW_UARTAPP_CTRL2_RD_NB(s->mem);
+ u32 mctrl = s->ctrl;
+
+ dev_dbg(s->dev, "%s:\n", __func__);
+ mctrl &= ~TIOCM_CTS;
+ if (stat & BM_UARTAPP_STAT_CTS) {
+ dev_dbg(s->dev, "CTS");
+ mctrl |= TIOCM_CTS;
+ }
+ if (ctrl2 & BM_UARTAPP_CTRL2_RTS) {
+ dev_dbg(s->dev, "RTS");
+ mctrl |= TIOCM_RTS;
+ }
+ dev_dbg(s->dev, "...%x\n", mctrl);
+ return mctrl;
+}
+
+static int stmp_appuart_request_port(struct uart_port *u)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+ int err = 0;
+
+ if (!request_mem_region(s->mem, s->memsize, s->dev->bus_id))
+ err = -ENXIO;
+ return err;
+
+}
+
+static void stmp_appuart_release_port(struct uart_port *u)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+
+ release_mem_region(s->mem, s->memsize);
+}
+
+static int stmp_appuart_verify_port(struct uart_port *u,
+ struct serial_struct *ser)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+
+ dev_dbg(s->dev, "%s\n", __func__);
+ return 0;
+}
+
+static void stmp_appuart_config_port(struct uart_port *u, int flags)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+
+ dev_dbg(s->dev, "%s\n", __func__);
+}
+
+
+static const char *stmp_appuart_type(struct uart_port *u)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+
+ dev_dbg(s->dev, "%s\n", __func__);
+ return s->dev->bus_id;
+}
+
+static void stmp_appuart_settermios(struct uart_port *u,
+ struct ktermios *nw, struct ktermios *old)
+{
+ static struct ktermios saved;
+ struct stmp_appuart_port *s = to_appuart(u);
+ unsigned int cflag;
+ u32 bm, ctrl, ctrl2, div;
+ int err = 0;
+ unsigned baud;
+
+ dev_dbg(s->dev, "%s\n", __func__);
+
+ if (nw)
+ memcpy(&saved, nw, sizeof *nw);
+ else
+ nw = old = &saved;
+
+ cflag = nw->c_cflag;
+
+ ctrl = BM_UARTAPP_LINECTRL_FEN;
+ ctrl2 = HW_UARTAPP_CTRL2_RD_NB(s->mem);
+
+ /* byte size */
+ switch (cflag & CSIZE) {
+ case CS5: bm = 0; break;
+ case CS6: bm = 1; break;
+ case CS7: bm = 2; break;
+ case CS8: bm = 3; break;
+ default: err = -EINVAL; break;
+ }
+ if (err)
+ goto out;
+
+ dev_dbg(s->dev, "Byte size %d bytes, mask %x\n",
+ bm + 5, BF_UARTAPP_LINECTRL_WLEN(bm));
+ ctrl |= BF_UARTAPP_LINECTRL_WLEN(bm);
+
+ /* parity */
+ if (cflag & PARENB) {
+ dev_dbg(s->dev, "Parity check enabled\n");
+ ctrl |= BM_UARTAPP_LINECTRL_PEN | BM_UARTAPP_LINECTRL_SPS;
+ if ((cflag & PARODD) == 0) {
+ dev_dbg(s->dev, "(Even) mask = %x\n",
+ BM_UARTAPP_LINECTRL_PEN |
+ BM_UARTAPP_LINECTRL_SPS |
+ BM_UARTAPP_LINECTRL_EPS);
+ ctrl |= BM_UARTAPP_LINECTRL_EPS;
+ } else
+ dev_dbg(s->dev, "(Odd) mask = %x\n",
+ BM_UARTAPP_LINECTRL_PEN |
+ BM_UARTAPP_LINECTRL_SPS);
+ } else
+ dev_dbg(s->dev, "Parity check disabled.\n");
+
+ /* figure out the stop bits requested */
+ if (cflag & CSTOPB) {
+ dev_dbg(s->dev, "Stop bits, mask = %x\n",
+ BM_UARTAPP_LINECTRL_STP2);
+ ctrl |= BM_UARTAPP_LINECTRL_STP2;
+ } else
+ dev_dbg(s->dev, "No stop bits\n");
+
+ /* figure out the hardware flow control settings */
+ if (cflag & CRTSCTS) {
+ dev_dbg(s->dev, "RTS/CTS flow control\n");
+ ctrl2 |= BM_UARTAPP_CTRL2_CTSEN /* | BM_UARTAPP_CTRL2_RTSEN */;
+ } else {
+ dev_dbg(s->dev, "RTS/CTS disabled\n");
+ ctrl2 &= ~BM_UARTAPP_CTRL2_CTSEN;
+ }
+
+ /* set baud rate */
+ baud = uart_get_baud_rate(u, nw, old, 0, u->uartclk);
+ dev_dbg(s->dev, "Baud rate requested: %d (clk = %d)\n",
+ baud, u->uartclk);
+ div = u->uartclk * 32 / baud;
+ ctrl |= BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(div & 0x3F);
+ ctrl |= BF_UARTAPP_LINECTRL_BAUD_DIVINT(div >> 6);
+
+ if ((cflag & CREAD) != 0) {
+ dev_dbg(s->dev, "RX started\n");
+ ctrl2 |= BM_UARTAPP_CTRL2_RXE | BM_UARTAPP_CTRL2_RXDMAE;
+ }
+
+ if (!err) {
+ dev_dbg(s->dev, "CTRLS = %x + %x\n", ctrl, ctrl2);
+ HW_UARTAPP_LINECTRL_WR_NB(s->mem, ctrl);
+ HW_UARTAPP_CTRL2_WR_NB(s->mem, ctrl2);
+ }
+out:
+ return /* err */;
+}
+
+static int stmp_appuart_free_irqs(struct stmp_appuart_port *s)
+{
+ int irqn = 0;
+
+ if (s->keep_irq) {
+ dev_dbg(s->dev, "keep_irq != 0, ignoring\n");
+ return 0;
+ }
+ for (irqn = 0; irqn < ARRAY_SIZE(s->irq); irqn++)
+ free_irq(s->irq[irqn], s);
+ return 0;
+}
+
+void stmp_appuart_rx(struct stmp_appuart_port *s, u8 *rx_buffer, int count)
+{
+ u8 c;
+ int flag;
+ struct tty_struct *tty = s->port.info->port.tty;
+ u32 stat;
+
+ spin_lock(&s->lock);
+ stat = HW_UARTAPP_STAT_RD_NB(s->mem);
+
+ if (count < 0) {
+ count = HW_UARTAPP_STAT_RD_NB(s->mem) & BM_UARTAPP_STAT_RXCOUNT;
+ dev_dbg(s->dev, "count = %d\n", count);
+ }
+
+ for (;;) {
+ if (!rx_buffer) {
+ if (stat & BM_UARTAPP_STAT_RXFE)
+ break;
+ c = HW_UARTAPP_DATA_RD_NB(s->mem) & 0xFF;
+ } else {
+ if (count-- <= 0)
+ break;
+ c = *rx_buffer++;
+ dev_dbg(s->dev, "Received: %x(%c)\n", c, chr(c));
+ }
+
+ flag = TTY_NORMAL;
+ if (stat & BM_UARTAPP_STAT_BERR) {
+ stat &= ~BM_UARTAPP_STAT_BERR;
+ s->port.icount.brk++;
+ if (uart_handle_break(&s->port))
+ goto ignore;
+ flag = TTY_BREAK;
+ } else if (stat & BM_UARTAPP_STAT_PERR) {
+ stat &= ~BM_UARTAPP_STAT_PERR;
+ s->port.icount.parity++;
+ flag = TTY_PARITY;
+ } else if (stat & BM_UARTAPP_STAT_FERR) {
+ stat &= ~BM_UARTAPP_STAT_FERR;
+ s->port.icount.frame++;
+ flag = TTY_FRAME;
+ }
+
+ if (stat & BM_UARTAPP_STAT_OERR)
+ s->port.icount.overrun++;
+
+ if (uart_handle_sysrq_char(&s->port, c))
+ goto ignore;
+
+ uart_insert_char(&s->port, stat,
+ BM_UARTAPP_STAT_OERR, c, flag);
+ignore:
+ if (pio_mode) {
+ HW_UARTAPP_STAT_WR_NB(s->mem, stat);
+ stat = HW_UARTAPP_STAT_RD_NB(s->mem);
+ }
+ }
+
+ HW_UARTAPP_STAT_WR_NB(s->mem, stat);
+ tty_flip_buffer_push(tty);
+ spin_unlock(&s->lock);
+}
+
+static inline void stmp_appuart_submit_rx(struct stmp_appuart_port *s)
+{
+#ifndef RX_CHAIN
+ struct stmp3xxx_dma_descriptor *r = &s->rx_desc;
+
+ dev_dbg(s->dev, "Submitting RX DMA request\n");
+ r->command->cmd =
+ BM_APBX_CHn_CMD_HALTONTERMINATE |
+ BF_APBX_CHn_CMD_XFER_COUNT(RX_BUFFER_SIZE) |
+ BF_APBX_CHn_CMD_CMDWORDS(1) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_SEMAPHORE |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BF_APBX_CHn_CMD_COMMAND(
+ BV_APBX_CHn_CMD_COMMAND__DMA_WRITE);
+ r->command->pio_words[0] = HW_UARTAPP_CTRL0_RD() |
+ BF_UARTAPP_CTRL0_XFER_COUNT(RX_BUFFER_SIZE)|
+ BM_UARTAPP_CTRL0_RXTO_ENABLE |
+ BF_UARTAPP_CTRL0_RXTIMEOUT(3);
+ r->command->pio_words[0] &= ~BM_UARTAPP_CTRL0_RUN;
+
+ stmp3xxx_dma_reset_channel(s->dma_rx);
+ stmp3xxx_dma_go(s->dma_rx, r, 1);
+#endif
+}
+
+static irqreturn_t stmp_appuart_irq_int(int irq, void *context)
+{
+ u32 istatus;
+ struct stmp_appuart_port *s = context;
+ u32 stat = HW_UARTAPP_STAT_RD_NB(s->mem);
+
+ istatus = HW_UARTAPP_INTR_RD_NB(s->mem);
+ dev_dbg(s->dev, "IRQ: int(%d), status = %08X\n", irq, istatus);
+
+ if (istatus & BM_UARTAPP_INTR_CTSMIS) {
+ uart_handle_cts_change(&s->port, stat & BM_UARTAPP_STAT_CTS);
+ dev_dbg(s->dev, "CTS change: %x\n", stat & BM_UARTAPP_STAT_CTS);
+ HW_UARTAPP_INTR_CLR_NB(s->mem, BM_UARTAPP_INTR_CTSMIS);
+ }
+
+ else if (istatus & BM_UARTAPP_INTR_RTIS) {
+ dev_dbg(s->dev, "RX timeout, draining out\n");
+ stmp_appuart_submit_rx(s);
+ }
+
+ else
+ dev_info(s->dev, "Unhandled status %x\n", istatus);
+
+ HW_UARTAPP_INTR_CLR_NB(s->mem, istatus & 0xFFFF);
+
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t stmp_appuart_irq_rx(int irq, void *context)
+{
+ struct stmp_appuart_port *s = context;
+ int count = -1;
+
+ stmp3xxx_dma_clear_interrupt(s->dma_rx);
+ dev_dbg(s->dev, "%s(%d), count = %d\n", __func__, irq, count);
+
+#ifndef RX_CHAIN
+ stmp_appuart_rx(s, s->rx_desc.virtual_buf_ptr, count);
+ stmp_appuart_submit_rx(s);
+#else
+ if (circ_advance_cooked(&s->rx_chain) == 0) {
+ BUG();
+ return IRQ_HANDLED;
+ }
+
+ circ_advance_active(&s->rx_chain, 1);
+ while (s->rx_chain.cooked_count) {
+ stmp_appuart_rx(s,
+ stmp3xxx_dma_circ_get_cooked_head(&s->rx_chain)->
+ virtual_buf_ptr, -1);
+ circ_advance_free(&s->rx_chain, 1);
+ }
+#endif
+ return IRQ_HANDLED;
+}
+
+static void stmp_appuart_submit_tx(struct stmp_appuart_port *s, int size)
+{
+ struct stmp3xxx_dma_descriptor *d = &s->tx_desc;
+
+ dev_dbg(s->dev, "Submitting TX DMA request, %d bytes\n", size);
+ d->command->pio_words[0] =
+ /* BM_UARTAPP_CTRL1_RUN | */ BF_UARTAPP_CTRL1_XFER_COUNT(size);
+ d->command->cmd = BF_APBX_CHn_CMD_XFER_COUNT(size) |
+ BF_APBX_CHn_CMD_CMDWORDS(1) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_SEMAPHORE |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BF_APBX_CHn_CMD_COMMAND(BV_APBX_CHn_CMD_COMMAND__DMA_READ);
+ stmp3xxx_dma_go(s->dma_tx, d, 1);
+}
+
+static irqreturn_t stmp_appuart_irq_tx(int irq, void *context)
+{
+ struct stmp_appuart_port *s = context;
+ struct uart_port *u = &s->port;
+ int bytes;
+
+ stmp3xxx_dma_clear_interrupt(s->dma_tx);
+ dev_dbg(s->dev, "%s(%d)\n", __func__, irq);
+
+ bytes = stmp_appuart_copy_tx(u, s->tx_desc.virtual_buf_ptr,
+ TX_BUFFER_SIZE);
+ if (bytes > 0) {
+ dev_dbg(s->dev, "Sending %d bytes\n", bytes);
+ stmp_appuart_submit_tx(s, bytes);
+ }
+ return IRQ_HANDLED;
+}
+
+static int stmp_appuart_request_irqs(struct stmp_appuart_port *s)
+{
+ int err = 0;
+
+ /*
+ * order counts. resources should be listed in the same order
+ */
+ irq_handler_t handlers[] = {
+ stmp_appuart_irq_int,
+ stmp_appuart_irq_rx,
+ stmp_appuart_irq_tx,
+ };
+ char *handlers_names[] = {
+ "appuart internal",
+ "appuart rx",
+ "appuart tx",
+ };
+ int irqn;
+
+ if (s->keep_irq) {
+ dev_dbg(s->dev, "keep_irq is set, skipping request_irq");
+ return 0;
+ }
+ for (irqn = 0; irqn < ARRAY_SIZE(handlers); irqn++) {
+ err = request_irq(s->irq[irqn], handlers[irqn],
+ 0, handlers_names[irqn], s);
+ dev_dbg(s->dev, "Requested IRQ %d with status %d\n",
+ s->irq[irqn], err);
+ if (err)
+ goto out;
+ }
+ return 0;
+out:
+ stmp_appuart_free_irqs(s);
+ return err;
+}
+
+static struct timer_list timer_task;
+
+static void stmp_appuart_check_rx(unsigned long data)
+{
+ stmp_appuart_rx((struct stmp_appuart_port *)data, NULL, -1);
+ mod_timer(&timer_task, jiffies + 2 * HZ);
+}
+
+static int stmp_appuart_startup(struct uart_port *u)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+ int err;
+
+ dev_dbg(s->dev, "%s\n", __func__);
+
+ s->tx_buffer_index = 0;
+
+ err = stmp_appuart_request_irqs(s);
+ if (err)
+ goto out;
+
+ if (!s->keep_irq)
+ /* Release the block from reset and start the clocks. */
+ stmp3xxx_reset_block(s->mem, 0);
+
+ HW_UARTAPP_CTRL2_SET_NB(s->mem, BM_UARTAPP_CTRL2_UARTEN);
+ /* Enable the Application UART DMA bits. */
+ if (!pio_mode) {
+ HW_UARTAPP_CTRL2_SET_NB(s->mem,
+ BM_UARTAPP_CTRL2_TXDMAE | BM_UARTAPP_CTRL2_RXDMAE |
+ BM_UARTAPP_CTRL2_DMAONERR);
+ /* clear any pending interrupts */
+ HW_UARTAPP_INTR_WR_NB(s->mem, 0);
+
+ /* reset all dma channels */
+ stmp3xxx_dma_reset_channel(s->dma_tx);
+ stmp3xxx_dma_reset_channel(s->dma_rx);
+ } else {
+ HW_UARTAPP_INTR_WR_NB(s->mem, BM_UARTAPP_INTR_RXIEN |
+ BM_UARTAPP_INTR_RTIEN);
+ }
+ HW_UARTAPP_INTR_SET_NB(s->mem, BM_UARTAPP_INTR_CTSMIEN);
+
+ /*
+ * Enable fifo so all four bytes of a DMA word are written to
+ * output (otherwise, only the LSB is written, ie. 1 in 4 bytes)
+ */
+ HW_UARTAPP_LINECTRL_SET_NB(s->mem, BM_UARTAPP_LINECTRL_FEN);
+
+ if (!pio_mode) {
+#ifndef RX_CHAIN
+ stmp_appuart_submit_rx(s);
+#else
+ circ_clear_chain(&s->rx_chain);
+ stmp3xxx_dma_go(s->dma_rx, &s->rxd[0], 0);
+ circ_advance_active(&s->rx_chain, 1);
+#endif
+ } else {
+ init_timer(&timer_task);
+ timer_task.function = stmp_appuart_check_rx;
+ timer_task.expires = jiffies + HZ;
+ timer_task.data = (unsigned long)s;
+ add_timer(&timer_task);
+ }
+
+out:
+ return err;
+}
+
+static void stmp_appuart_shutdown(struct uart_port *u)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+
+ dev_dbg(s->dev, "%s\n", __func__);
+
+ if (!s->keep_irq)
+ /* set the IP block to RESET; this should disable clock too. */
+ HW_UARTAPP_CTRL0_SET_NB(s->mem, BM_UARTAPP_CTRL0_SFTRST);
+
+ if (!pio_mode) {
+ /* reset all dma channels */
+ stmp3xxx_dma_reset_channel(s->dma_tx);
+ stmp3xxx_dma_reset_channel(s->dma_rx);
+ } else {
+ del_timer(&timer_task);
+ }
+ stmp_appuart_free_irqs(s);
+}
+
+static unsigned int stmp_appuart_tx_empty(struct uart_port *u)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+
+ if (pio_mode)
+ if (HW_UARTAPP_STAT_RD_NB(s->mem) & BM_UARTAPP_STAT_TXFE)
+ return TIOCSER_TEMT;
+ else
+ return 0;
+ else
+ return stmp3xxx_dma_running(s->dma_tx) ? 0: TIOCSER_TEMT;
+}
+
+static void stmp_appuart_start_tx(struct uart_port *u)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+ int bytes;
+
+ dev_dbg(s->dev, "%s\n", __func__);
+
+ /* enable transmitter */
+ HW_UARTAPP_CTRL2_SET_NB(s->mem, BM_UARTAPP_CTRL2_TXE);
+
+ if (!pio_mode) {
+ if (stmp3xxx_dma_running(s->dma_tx))
+ return;
+ bytes = stmp_appuart_copy_tx(u, s->tx_desc.virtual_buf_ptr,
+ TX_BUFFER_SIZE);
+ if (bytes <= 0)
+ return;
+
+ dev_dbg(s->dev, "Started DMA transfer with descriptor %p, "
+ "command %p, %d bytes long\n",
+ &s->tx_desc, s->tx_desc.command, bytes);
+ stmp_appuart_submit_tx(s, bytes);
+ } else {
+ int count = 0;
+ u8 c;
+
+ while (!(HW_UARTAPP_STAT_RD_NB(s->mem) &
+ BM_UARTAPP_STAT_TXFF)) {
+ if (stmp_appuart_copy_tx(u, &c, 1) <= 0)
+ break;
+ dev_dbg(s->dev, "%d: '%c'/%x\n",
+ ++count, chr(c), c);
+ HW_UARTAPP_DATA_WR_NB(s->mem, c);
+ }
+ }
+}
+
+static void stmp_appuart_stop_tx(struct uart_port *u)
+{
+ struct stmp_appuart_port *s = to_appuart(u);
+
+ dev_dbg(s->dev, "%s\n", __func__);
+ HW_UARTAPP_CTRL2_CLR_NB(s->mem, BM_UARTAPP_CTRL2_TXE);
+}
+
+static int stmp_appuart_copy_tx(struct uart_port *u, u8 *target,
+ int tx_buffer_size)
+{
+ int last = 0, portion;
+ struct circ_buf *xmit = &u->info->xmit;
+
+ while (last < tx_buffer_size) { /* let's fill the only descriptor */
+ if (u->x_char) {
+ target[last++] = u->x_char;
+ u->x_char = 0;
+ } else if (!uart_circ_empty(xmit) && !uart_tx_stopped(u)) {
+ portion = min((u32)tx_buffer_size,
+ (u32)uart_circ_chars_pending(xmit));
+ portion = min((u32)portion,
+ (u32)CIRC_CNT_TO_END(xmit->head, xmit->tail,
+ UART_XMIT_SIZE));
+ memcpy(target + last, &xmit->buf[xmit->tail], portion);
+ xmit->tail = (xmit->tail + portion) &
+ (UART_XMIT_SIZE - 1);
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(u);
+ last += portion;
+ } else { /* All tx data copied into buffer */
+ return last;
+ }
+ }
+ return last;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("stmp3xxx app uart driver");
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
+module_param(pio_mode, int, 0);
diff --git a/drivers/serial/stmp-app.h b/drivers/serial/stmp-app.h
new file mode 100644
index 000000000000..a4fee4217bba
--- /dev/null
+++ b/drivers/serial/stmp-app.h
@@ -0,0 +1,81 @@
+/*
+ * Freescale STMP37XX/STMP378X Application UART driver
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __STMP_APPUART_H
+#define __STMP_APPUART_H
+
+#define RX_BUFFER_SIZE 4
+#define TX_BUFFER_SIZE 0xFFF0
+
+#include <mach/dma.h>
+
+/* #define RX_CHAIN 2 */
+
+struct stmp_appuart_port {
+ int keep_irq;
+ int irq[3];
+ u32 mem, memsize;
+ int dma_rx, dma_tx;
+ struct clk *clk;
+ struct device *dev;
+ struct uart_port port;
+ unsigned tx_buffer_index;
+ struct stmp3xxx_dma_descriptor tx_desc;
+#ifndef RX_CHAIN
+ struct stmp3xxx_dma_descriptor rx_desc;
+#else
+ struct stmp3xxx_dma_descriptor rxd[RX_CHAIN];
+ struct stmp37xx_circ_dma_chain rx_chain;
+#endif
+
+ u32 ctrl;
+ u8 running;
+ spinlock_t lock; /* protects irq handler */
+};
+
+#ifdef CONFIG_CPU_FREQ
+static int stmp_appuart_updateclk(struct device *dev, void *clkdata);
+static int stmp_appuart_notifier(struct notifier_block *self,
+ unsigned long phase, void *p);
+#endif /* CONFIG_CPU_FREQ */
+static int __init stmp_appuart_probe(struct platform_device *device);
+static int stmp_appuart_remove(struct platform_device *device);
+static int stmp_appuart_suspend(struct platform_device *device,
+ pm_message_t state);
+static int stmp_appuart_resume(struct platform_device *device);
+static int __init stmp_appuart_init(void);
+static void __exit stmp_appuart_exit(void);
+static int stmp_appuart_request_port(struct uart_port *u);
+static void stmp_appuart_release_port(struct uart_port *u);
+static int stmp_appuart_verify_port(struct uart_port *u,
+ struct serial_struct *);
+static void stmp_appuart_config_port(struct uart_port *u, int flags);
+static const char *stmp_appuart_type(struct uart_port *u);
+static void stmp_appuart_settermios(struct uart_port *u,
+ struct ktermios *nw, struct ktermios *old);
+static void stmp_appuart_shutdown(struct uart_port *u);
+static int stmp_appuart_startup(struct uart_port *u);
+static u32 stmp_appuart_get_mctrl(struct uart_port *u);
+static void stmp_appuart_set_mctrl(struct uart_port *u, unsigned mctrl);
+static void stmp_appuart_enable_ms(struct uart_port *port);
+static void stmp_appuart_break_ctl(struct uart_port *port, int ctl);
+static unsigned int stmp_appuart_tx_empty(struct uart_port *u);
+static void stmp_appuart_stop_rx(struct uart_port *u);
+static void stmp_appuart_start_tx(struct uart_port *u);
+static void stmp_appuart_stop_tx(struct uart_port *u);
+static int stmp_appuart_copy_tx(struct uart_port *u, u8 *target, int size);
+#endif
diff --git a/drivers/serial/stmp-dbg.c b/drivers/serial/stmp-dbg.c
new file mode 100644
index 000000000000..2848bb88ea7c
--- /dev/null
+++ b/drivers/serial/stmp-dbg.c
@@ -0,0 +1,839 @@
+/*
+ * Freescale STMP37XX/STMP378X Debug UART driver
+ *
+ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ * Copyright 1999 ARM Limited
+ * Copyright (C) 2000 Deep Blue Solutions Ltd.
+ * Modifications for STMP36XX Debug Serial (c) 2005 Sigmatel Inc
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/autoconf.h>
+
+#if defined(CONFIG_SERIAL_STMP_DBG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <mach/regs-uartdbg.h>
+
+#include <mach/stmp3xxx.h>
+
+#include "stmp-dbg.h"
+
+/* treated as variable unless submitted to open-source */
+#define PORT_STMPDBG 100
+#define UART_NR 1
+#define SERIAL_STMPDBG_MAJOR 204
+#define SERIAL_STMPDBG_MINOR 16
+
+#define ISR_PASS_LIMIT 256
+
+#define STMPDBG_DEVID "Debug UART"
+
+
+static int force_cd = 1;
+
+static struct uart_driver stmpdbg_reg;
+
+/*
+ * We wrap our port structure around the generic uart_port.
+ */
+struct uart_stmpdbg_port {
+ struct uart_port port;
+ struct clk *clk;
+ unsigned int im; /* interrupt mask */
+ unsigned int old_status;
+ int suspended;
+};
+
+
+static void stmpdbg_stop_tx(struct uart_port *port)
+{
+ struct uart_stmpdbg_port *uap = (struct uart_stmpdbg_port *)port;
+
+ uap->im &= ~UART011_TXIM;
+ __raw_writel(uap->im, uap->port.membase + UART011_IMSC);
+}
+
+static void stmpdbg_start_tx(struct uart_port *port)
+{
+ struct uart_stmpdbg_port *uap = (struct uart_stmpdbg_port *)port;
+
+ uap->im |= UART011_TXIM;
+ __raw_writel(uap->im, uap->port.membase + UART011_IMSC);
+}
+
+static void stmpdbg_stop_rx(struct uart_port *port)
+{
+ struct uart_stmpdbg_port *uap = (struct uart_stmpdbg_port *)port;
+
+ uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM|
+ UART011_PEIM|UART011_BEIM|UART011_OEIM);
+ __raw_writel(uap->im, uap->port.membase + UART011_IMSC);
+}
+
+static void stmpdbg_enable_ms(struct uart_port *port)
+{
+ struct uart_stmpdbg_port *uap = (struct uart_stmpdbg_port *)port;
+
+ uap->im |= UART011_RIMIM|UART011_CTSMIM|UART011_DCDMIM|UART011_DSRMIM;
+ __raw_writel(uap->im, uap->port.membase + UART011_IMSC);
+}
+
+static void stmpdbg_rx_chars(struct uart_stmpdbg_port *uap)
+{
+ struct tty_struct *tty = uap->port.info->port.tty;
+ unsigned int status, ch, flag, rsr, max_count = 256;
+
+ status = __raw_readl(uap->port.membase + UART01x_FR);
+ while ((status & UART01x_FR_RXFE) == 0 && max_count--) {
+#if 0
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ if (tty->low_latency)
+ tty_flip_buffer_push(tty);
+ /*
+ * If this failed then we will throw away the
+ * bytes but must do so to clear interrupts
+ */
+ }
+#endif
+
+ ch = __raw_readl(uap->port.membase + UART01x_DR);
+ flag = TTY_NORMAL;
+ uap->port.icount.rx++;
+
+ /*
+ * Note that the error handling code is
+ * out of the main execution path
+ */
+ rsr = __raw_readl(uap->port.membase + UART01x_RSR)
+ | UART_DUMMY_RSR_RX;
+ if (unlikely(rsr & UART01x_RSR_ANY)) {
+ if (rsr & UART01x_RSR_BE) {
+ rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE);
+ uap->port.icount.brk++;
+ if (uart_handle_break(&uap->port))
+ goto ignore_char;
+ } else if (rsr & UART01x_RSR_PE)
+ uap->port.icount.parity++;
+ else if (rsr & UART01x_RSR_FE)
+ uap->port.icount.frame++;
+ if (rsr & UART01x_RSR_OE)
+ uap->port.icount.overrun++;
+
+ rsr &= uap->port.read_status_mask;
+
+ if (rsr & UART01x_RSR_BE)
+ flag = TTY_BREAK;
+ else if (rsr & UART01x_RSR_PE)
+ flag = TTY_PARITY;
+ else if (rsr & UART01x_RSR_FE)
+ flag = TTY_FRAME;
+ }
+
+ if (uart_handle_sysrq_char(&uap->port, ch))
+ goto ignore_char;
+
+ uart_insert_char(&uap->port, rsr, UART01x_RSR_OE, ch, flag);
+
+ignore_char:
+ status = __raw_readl(uap->port.membase + UART01x_FR);
+ }
+ tty_flip_buffer_push(tty);
+ return;
+}
+
+static void stmpdbg_tx_chars(struct uart_stmpdbg_port *uap)
+{
+ struct circ_buf *xmit = &uap->port.info->xmit;
+ int count;
+
+ if (uap->port.x_char) {
+ __raw_writel(uap->port.x_char, uap->port.membase + UART01x_DR);
+ uap->port.icount.tx++;
+ uap->port.x_char = 0;
+ return;
+ }
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
+ stmpdbg_stop_tx(&uap->port);
+ return;
+ }
+
+ count = uap->port.fifosize >> 1;
+ do {
+ __raw_writel(xmit->buf[xmit->tail],
+ uap->port.membase + UART01x_DR);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ uap->port.icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ } while (--count > 0);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&uap->port);
+
+ if (uart_circ_empty(xmit))
+ stmpdbg_stop_tx(&uap->port);
+}
+
+static void stmpdbg_modem_status(struct uart_stmpdbg_port *uap)
+{
+ unsigned int status, delta;
+
+ status = __raw_readl(uap->port.membase + UART01x_FR) &
+ UART01x_FR_MODEM_ANY;
+
+ delta = status ^ uap->old_status;
+ uap->old_status = status;
+
+ if (!delta)
+ return;
+
+ if (delta & UART01x_FR_DCD)
+ uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD);
+
+ if (delta & UART01x_FR_DSR)
+ uap->port.icount.dsr++;
+
+ if (delta & UART01x_FR_CTS)
+ uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS);
+
+ wake_up_interruptible(&uap->port.info->delta_msr_wait);
+}
+
+static irqreturn_t stmpdbg_int(int irq, void *dev_id)
+{
+ struct uart_stmpdbg_port *uap = dev_id;
+ unsigned int status, pass_counter = ISR_PASS_LIMIT;
+ int handled = 0;
+
+ spin_lock(&uap->port.lock);
+
+ status = __raw_readl(uap->port.membase + UART011_MIS);
+ if (status) {
+ do {
+ __raw_writel(status & ~(UART011_TXIS|UART011_RTIS|
+ UART011_RXIS),
+ uap->port.membase + UART011_ICR);
+
+ if (status & (UART011_RTIS|UART011_RXIS))
+ stmpdbg_rx_chars(uap);
+ if (status & (UART011_DSRMIS|UART011_DCDMIS|
+ UART011_CTSMIS|UART011_RIMIS))
+ stmpdbg_modem_status(uap);
+ if (status & UART011_TXIS)
+ stmpdbg_tx_chars(uap);
+
+ if (pass_counter-- == 0)
+ break;
+
+ status = __raw_readl(uap->port.membase + UART011_MIS);
+ } while (status != 0);
+ handled = 1;
+ }
+
+ spin_unlock(&uap->port.lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+static unsigned int stmpdbg_tx_empty(struct uart_port *port)
+{
+ struct uart_stmpdbg_port *uap = (struct uart_stmpdbg_port *)port;
+ unsigned int status = __raw_readl(uap->port.membase + UART01x_FR);
+ return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int stmpdbg_get_mctrl(struct uart_port *port)
+{
+ struct uart_stmpdbg_port *uap = (struct uart_stmpdbg_port *)port;
+ unsigned int result = 0;
+ unsigned int status = __raw_readl(uap->port.membase + UART01x_FR);
+
+#define TEST_AND_SET_BIT(uartbit, tiocmbit) do { \
+ if (status & uartbit) \
+ result |= tiocmbit; \
+ } while (0)
+
+ TEST_AND_SET_BIT(UART01x_FR_DCD, TIOCM_CAR);
+ TEST_AND_SET_BIT(UART01x_FR_DSR, TIOCM_DSR);
+ TEST_AND_SET_BIT(UART01x_FR_CTS, TIOCM_CTS);
+ TEST_AND_SET_BIT(UART011_FR_RI, TIOCM_RNG);
+#undef TEST_AND_SET_BIT
+ if (force_cd)
+ result |= TIOCM_CAR;
+ return result;
+}
+
+static void stmpdbg_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct uart_stmpdbg_port *uap = (struct uart_stmpdbg_port *)port;
+ unsigned int cr;
+
+ cr = __raw_readl(uap->port.membase + UART011_CR);
+
+#define TEST_AND_SET_BIT(tiocmbit, uartbit) do { \
+ if (mctrl & tiocmbit) \
+ cr |= uartbit; \
+ else \
+ cr &= ~uartbit; \
+ } while (0)
+
+ TEST_AND_SET_BIT(TIOCM_RTS, UART011_CR_RTS);
+ TEST_AND_SET_BIT(TIOCM_DTR, UART011_CR_DTR);
+ TEST_AND_SET_BIT(TIOCM_OUT1, UART011_CR_OUT1);
+ TEST_AND_SET_BIT(TIOCM_OUT2, UART011_CR_OUT2);
+ TEST_AND_SET_BIT(TIOCM_LOOP, UART011_CR_LBE);
+#undef TEST_AND_SET_BIT
+
+ __raw_writel(cr, uap->port.membase + UART011_CR);
+}
+
+static void stmpdbg_break_ctl(struct uart_port *port, int break_state)
+{
+ struct uart_stmpdbg_port *uap = (struct uart_stmpdbg_port *)port;
+ unsigned long flags;
+ unsigned int lcr_h;
+
+ spin_lock_irqsave(&uap->port.lock, flags);
+ lcr_h = __raw_readl(uap->port.membase + UART011_LCRH);
+ if (break_state == -1)
+ lcr_h |= UART01x_LCRH_BRK;
+ else
+ lcr_h &= ~UART01x_LCRH_BRK;
+ __raw_writel(lcr_h, uap->port.membase + UART011_LCRH);
+ spin_unlock_irqrestore(&uap->port.lock, flags);
+}
+
+static int stmpdbg_startup(struct uart_port *port)
+{
+ struct uart_stmpdbg_port *uap = (struct uart_stmpdbg_port *)port;
+ u32 cr, lcr;
+ int retval;
+
+ /*
+ * Allocate the IRQ
+ */
+ retval = request_irq(uap->port.irq, stmpdbg_int, 0, STMPDBG_DEVID, uap);
+ if (retval)
+ return retval;
+
+ __raw_writel(0, uap->port.membase + UART01x_DR); /* wake up the UART */
+
+ __raw_writel(UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
+ uap->port.membase + UART011_IFLS);
+
+ /*
+ * Provoke TX FIFO interrupt into asserting.
+ */
+ cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
+ __raw_writel(cr, uap->port.membase + UART011_CR);
+
+ lcr = __raw_readl(uap->port.membase + UART011_LCRH);
+ lcr |= UART01x_LCRH_FEN;
+ __raw_writel(lcr, uap->port.membase + UART011_LCRH);
+
+ /*
+ * initialise the old status of the modem signals
+ */
+ uap->old_status = __raw_readl(uap->port.membase + UART01x_FR) &
+ UART01x_FR_MODEM_ANY;
+
+ /*
+ * Finally, enable interrupts
+ */
+ spin_lock_irq(&uap->port.lock);
+ uap->im = UART011_RXIM | UART011_RTIM;
+ __raw_writel(uap->im, uap->port.membase + UART011_IMSC);
+ spin_unlock_irq(&uap->port.lock);
+
+ return 0;
+}
+
+static void stmpdbg_shutdown(struct uart_port *port)
+{
+ struct uart_stmpdbg_port *uap = (struct uart_stmpdbg_port *)port;
+ unsigned long val;
+
+ /*
+ * disable all interrupts
+ */
+ spin_lock_irq(&uap->port.lock);
+ uap->im = 0;
+ __raw_writel(uap->im, uap->port.membase + UART011_IMSC);
+ __raw_writel(0xffff, uap->port.membase + UART011_ICR);
+ spin_unlock_irq(&uap->port.lock);
+
+ free_irq(uap->port.irq, uap);
+
+ /*
+ * disable the port
+ */
+ __raw_writel(UART01x_CR_UARTEN | UART011_CR_TXE,
+ uap->port.membase + UART011_CR);
+
+ /*
+ * disable break condition and fifos
+ */
+ val = __raw_readl(uap->port.membase + UART011_LCRH);
+ val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN);
+ __raw_writel(val, uap->port.membase + UART011_LCRH);
+}
+
+static void
+stmpdbg_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned int lcr_h, old_cr;
+ unsigned long flags;
+ unsigned int baud, quot;
+
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+ quot = port->uartclk * 4 / baud;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ lcr_h = UART01x_LCRH_WLEN_5;
+ break;
+ case CS6:
+ lcr_h = UART01x_LCRH_WLEN_6;
+ break;
+ case CS7:
+ lcr_h = UART01x_LCRH_WLEN_7;
+ break;
+ default: /* CS8 */
+ lcr_h = UART01x_LCRH_WLEN_8;
+ break;
+ }
+ if (termios->c_cflag & CSTOPB)
+ lcr_h |= UART01x_LCRH_STP2;
+ if (termios->c_cflag & PARENB) {
+ lcr_h |= UART01x_LCRH_PEN;
+ if (!(termios->c_cflag & PARODD))
+ lcr_h |= UART01x_LCRH_EPS;
+ }
+ lcr_h |= UART01x_LCRH_FEN;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /*
+ * Update the per-port timeout.
+ */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ port->read_status_mask = UART01x_RSR_OE;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ port->read_status_mask |= UART01x_RSR_BE;
+
+ /*
+ * Characters to ignore
+ */
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
+ if (termios->c_iflag & IGNBRK) {
+ port->ignore_status_mask |= UART01x_RSR_BE;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= UART01x_RSR_OE;
+ }
+
+ /*
+ * Ignore all characters if CREAD is not set.
+ */
+ if ((termios->c_cflag & CREAD) == 0)
+ port->ignore_status_mask |= UART_DUMMY_RSR_RX;
+
+ if (UART_ENABLE_MS(port, termios->c_cflag))
+ stmpdbg_enable_ms(port);
+
+ /* first, disable everything */
+ old_cr = __raw_readl(port->membase + UART011_CR);
+ __raw_writel(0, port->membase + UART011_CR);
+
+ /* Set baud rate */
+ __raw_writel(quot & 0x3f, port->membase + UART011_FBRD);
+ __raw_writel(quot >> 6, port->membase + UART011_IBRD);
+
+ /*
+ * ----------v----------v----------v----------v-----
+ * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
+ * ----------^----------^----------^----------^-----
+ */
+ __raw_writel(lcr_h, port->membase + UART011_LCRH);
+ __raw_writel(old_cr, port->membase + UART011_CR);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *stmpdbg_type(struct uart_port *port)
+{
+ return port->type == PORT_STMPDBG ? STMPDBG_DEVID : NULL;
+}
+
+
+/*
+ * Release the memory region(s) being used by 'port'
+ */
+static void stmpdbg_release_port(struct uart_port *port)
+{
+ release_mem_region(port->mapbase, UART_PORT_SIZE);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'
+ */
+static int stmpdbg_request_port(struct uart_port *port)
+{
+ return request_mem_region(port->mapbase, UART_PORT_SIZE, STMPDBG_DEVID)
+ != NULL ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void stmpdbg_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = PORT_STMPDBG;
+ stmpdbg_request_port(port);
+ }
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int stmpdbg_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ int ret = 0;
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_STMPDBG)
+ ret = -EINVAL;
+ if (ser->irq < 0 || ser->irq >= NR_IRQS)
+ ret = -EINVAL;
+ if (ser->baud_base < 9600)
+ ret = -EINVAL;
+ return ret;
+}
+
+static struct uart_ops stmpdbg_pops = {
+ .tx_empty = stmpdbg_tx_empty,
+ .set_mctrl = stmpdbg_set_mctrl,
+ .get_mctrl = stmpdbg_get_mctrl,
+ .stop_tx = stmpdbg_stop_tx,
+ .start_tx = stmpdbg_start_tx,
+ .stop_rx = stmpdbg_stop_rx,
+ .enable_ms = stmpdbg_enable_ms,
+ .break_ctl = stmpdbg_break_ctl,
+ .startup = stmpdbg_startup,
+ .shutdown = stmpdbg_shutdown,
+ .set_termios = stmpdbg_set_termios,
+ .type = stmpdbg_type,
+ .release_port = stmpdbg_release_port,
+ .request_port = stmpdbg_request_port,
+ .config_port = stmpdbg_config_port,
+ .verify_port = stmpdbg_verify_port,
+};
+
+static struct uart_stmpdbg_port stmpdbg_ports[UART_NR] = {
+ {
+ .port = {
+ /* This *is* the virtual address */
+ .membase = (void *)HW_UARTDBGDR_ADDR,
+ .mapbase = HW_UARTDBGDR_ADDR,
+ .iotype = SERIAL_IO_MEM,
+ .irq = IRQ_DEBUG_UART,
+ .fifosize = 16,
+ .ops = &stmpdbg_pops,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 0,
+ .uartclk = 24000000,
+ },
+ }
+};
+
+#ifdef CONFIG_SERIAL_STMP_DBG_CONSOLE
+
+static void
+stmpdbg_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct uart_port *port = &stmpdbg_ports[co->index].port;
+ unsigned int status, old_cr;
+ int i;
+
+ /*
+ * First save the CR then disable the interrupts
+ */
+ old_cr = UART_GET_CR(port);
+ UART_PUT_CR(port, UART01x_CR_UARTEN);
+
+ /*
+ * Now, do each character
+ */
+ for (i = 0; i < count; i++) {
+ do {
+ status = UART_GET_FR(port);
+ } while (!UART_TX_READY(status));
+ UART_PUT_CHAR(port, s[i]);
+ if (s[i] == '\n') {
+ do {
+ status = UART_GET_FR(port);
+ } while (!UART_TX_READY(status));
+ UART_PUT_CHAR(port, '\r');
+ }
+ }
+
+ /*
+ * Finally, wait for transmitter to become empty
+ * and restore the TCR
+ */
+ do {
+ status = UART_GET_FR(port);
+ } while (status & UART01x_FR_BUSY);
+ UART_PUT_CR(port, old_cr);
+}
+
+static void __init
+stmpdbg_console_get_options(struct uart_port *port, int *baud,
+ int *parity, int *bits)
+{
+ if (UART_GET_CR(port) & UART01x_CR_UARTEN) {
+ unsigned int lcr_h, quot;
+ lcr_h = UART_GET_LCRH(port);
+
+ *parity = 'n';
+ if (lcr_h & UART01x_LCRH_PEN) {
+ if (lcr_h & UART01x_LCRH_EPS)
+ *parity = 'e';
+ else
+ *parity = 'o';
+ }
+
+ if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7)
+ *bits = 7;
+ else
+ *bits = 8;
+
+ quot = UART_GET_LCRL(port) | UART_GET_LCRM(port) << 8;
+ *baud = port->uartclk / (16 * (quot + 1));
+ }
+}
+
+static int __init stmpdbg_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index >= UART_NR)
+ co->index = 0;
+ port = &stmpdbg_ports[co->index].port;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ stmpdbg_console_get_options(port, &baud, &parity, &bits);
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+
+static struct console stmpdbg_console = {
+ .name = "ttyAM",
+ .write = stmpdbg_console_write,
+ .device = uart_console_device,
+ .setup = stmpdbg_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &stmpdbg_reg,
+};
+
+static int __init stmpdbg_console_init(void)
+{
+ /*
+ * All port initializations are done statically
+ */
+ register_console(&stmpdbg_console);
+ return 0;
+}
+console_initcall(stmpdbg_console_init);
+
+static int __init stmpdbg_late_console_init(void)
+{
+ if (!(stmpdbg_console.flags & CON_ENABLED))
+ register_console(&stmpdbg_console);
+ return 0;
+}
+late_initcall(stmpdbg_late_console_init);
+
+#endif
+
+static struct uart_driver stmpdbg_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "ttyAM",
+ .dev_name = "ttyAM",
+ .major = SERIAL_STMPDBG_MAJOR,
+ .minor = SERIAL_STMPDBG_MINOR,
+ .nr = UART_NR,
+#ifdef CONFIG_SERIAL_STMP_DBG_CONSOLE
+ .cons = &stmpdbg_console,
+#endif
+};
+
+static int __devinit stmpdbguart_probe(struct platform_device *device)
+{
+ int ret = 0;
+ int i;
+ void (*cfg)(int request, int port) = NULL;
+
+ if (device->dev.platform_data)
+ cfg = device->dev.platform_data;
+
+ device_init_wakeup(&device->dev, 1);
+
+ for (i = 0; i < UART_NR; i++) {
+ stmpdbg_ports[i].clk = clk_get(NULL, "uart");
+ if (IS_ERR(stmpdbg_ports[i].clk))
+ continue;
+ stmpdbg_ports[i].suspended = 0;
+ stmpdbg_ports[i].port.dev = &device->dev;
+ stmpdbg_ports[i].port.uartclk =
+ clk_get_rate(stmpdbg_ports[i].clk) * 1000;
+ if (cfg)
+ (*cfg)(1, i);
+ uart_add_one_port(&stmpdbg_reg, &stmpdbg_ports[i].port);
+ }
+ return ret;
+}
+
+static int __devexit stmpdbguart_remove(struct platform_device *device)
+{
+ int i;
+ void (*cfg)(int request, int port) = NULL;
+
+ if (device->dev.platform_data)
+ cfg = device->dev.platform_data;
+ for (i = 0; i < UART_NR; i++) {
+ clk_put(stmpdbg_ports[i].clk);
+ uart_remove_one_port(&stmpdbg_reg, &stmpdbg_ports[0].port);
+ if (cfg)
+ (*cfg)(0, i);
+ }
+ return 0;
+}
+
+static int stmpdbguart_suspend(struct platform_device *device,
+ pm_message_t state)
+{
+#ifdef CONFIG_PM
+ int i;
+ int deep_sleep = (stmp37xx_pm_get_target() != PM_SUSPEND_STANDBY);
+
+ for (i = 0; i < UART_NR; i++) {
+ if (deep_sleep) {
+ uart_suspend_port(&stmpdbg_reg,
+ &stmpdbg_ports[i].port);
+ clk_disable(stmpdbg_ports[i].clk);
+ stmpdbg_ports[i].suspended = 1;
+ }
+ }
+#endif
+ return 0;
+}
+
+static int stmpdbguart_resume(struct platform_device *device)
+{
+ int ret = 0;
+#ifdef CONFIG_PM
+ int i;
+
+ for (i = 0; i < UART_NR; i++) {
+ if (stmpdbg_ports[i].suspended) {
+ clk_enable(stmpdbg_ports[i].clk);
+ uart_resume_port(&stmpdbg_reg, &stmpdbg_ports[i].port);
+ }
+ stmpdbg_ports[i].suspended = 0;
+ }
+#endif
+ return ret;
+}
+
+static struct platform_driver stmpdbguart_driver = {
+ .probe = stmpdbguart_probe,
+ .remove = __devexit_p(stmpdbguart_remove),
+ .suspend = stmpdbguart_suspend,
+ .resume = stmpdbguart_resume,
+ .driver = {
+ .name = "stmp37xx-dbguart",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmpdbg_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&stmpdbg_reg);
+ if (ret)
+ goto out;
+
+ ret = platform_driver_register(&stmpdbguart_driver);
+ if (ret)
+ uart_unregister_driver(&stmpdbg_reg);
+out:
+ return ret;
+}
+
+static void __exit stmpdbg_exit(void)
+{
+ platform_driver_unregister(&stmpdbguart_driver);
+ uart_unregister_driver(&stmpdbg_reg);
+}
+
+module_init(stmpdbg_init);
+module_exit(stmpdbg_exit);
+module_param(force_cd, int, 0644);
+MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd/Sigmatel Inc");
+MODULE_DESCRIPTION("STMP37xx debug uart");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/stmp-dbg.h b/drivers/serial/stmp-dbg.h
new file mode 100644
index 000000000000..38f3e6747fb3
--- /dev/null
+++ b/drivers/serial/stmp-dbg.h
@@ -0,0 +1,180 @@
+/*
+ * Freescale STMP37XX/STMP378X Debug UART driver
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __STMP_DBG_H
+#define __STMP_DBG_H
+/*
+ * UART register offsets
+ */
+#define UART01x_DR 0x00 /* Data read or written */
+#define UART01x_RSR 0x04 /* Receive status register */
+#define UART01x_ECR 0x04 /* Error clear register (write) */
+#define UART010_LCRH 0x08 /* Line control register, MSB */
+#define UART010_LCRM 0x0C /* Line control register, */
+#define UART010_LCRL 0x10 /* Line control register, LSB */
+#define UART010_CR 0x14 /* Control register. */
+#define UART01x_FR 0x18 /* Flag register (Read only). */
+#define UART010_IIR 0x1C /* Interrupt identification */
+#define UART010_ICR 0x1C /* Interrupt clear register */
+#define UART01x_ILPR 0x20 /* IrDA low power counter */
+#define UART011_IBRD 0x24 /* Integer baud rate divisor */
+#define UART011_FBRD 0x28 /* Fractional baud rate divisor */
+#define UART011_LCRH 0x2c /* Line control */
+#define UART011_CR 0x30 /* Control */
+#define UART011_IFLS 0x34 /* Interrupt fifo level select */
+#define UART011_IMSC 0x38 /* Interrupt mask */
+#define UART011_RIS 0x3c /* Raw */
+#define UART011_MIS 0x40 /* Masked */
+#define UART011_ICR 0x44 /* Interrupt clear register */
+#define UART011_DMACR 0x48 /* DMA control register */
+
+#define UART011_DR_OE (1 << 11)
+#define UART011_DR_BE (1 << 10)
+#define UART011_DR_PE (1 << 9)
+#define UART011_DR_FE (1 << 8)
+
+#define UART01x_RSR_OE 0x08
+#define UART01x_RSR_BE 0x04
+#define UART01x_RSR_PE 0x02
+#define UART01x_RSR_FE 0x01
+
+#define UART011_FR_RI 0x100
+#define UART011_FR_TXFE 0x080
+#define UART011_FR_RXFF 0x040
+#define UART01x_FR_TXFF 0x020
+#define UART01x_FR_RXFE 0x010
+#define UART01x_FR_BUSY 0x008
+#define UART01x_FR_DCD 0x004
+#define UART01x_FR_DSR 0x002
+#define UART01x_FR_CTS 0x001
+#define UART01x_FR_TMSK (UART01x_FR_TXFF + UART01x_FR_BUSY)
+
+#define UART011_CR_CTSEN 0x8000 /* CTS hardware flow control */
+#define UART011_CR_RTSEN 0x4000 /* RTS hardware flow control */
+#define UART011_CR_OUT2 0x2000 /* OUT2 */
+#define UART011_CR_OUT1 0x1000 /* OUT1 */
+#define UART011_CR_RTS 0x0800 /* RTS */
+#define UART011_CR_DTR 0x0400 /* DTR */
+#define UART011_CR_RXE 0x0200 /* receive enable */
+#define UART011_CR_TXE 0x0100 /* transmit enable */
+#define UART011_CR_LBE 0x0080 /* loopback enable */
+#define UART010_CR_RTIE 0x0040
+#define UART010_CR_TIE 0x0020
+#define UART010_CR_RIE 0x0010
+#define UART010_CR_MSIE 0x0008
+#define UART01x_CR_IIRLP 0x0004 /* SIR low power mode */
+#define UART01x_CR_SIREN 0x0002 /* SIR enable */
+#define UART01x_CR_UARTEN 0x0001 /* UART enable */
+
+#define UART011_LCRH_SPS 0x80
+#define UART01x_LCRH_WLEN_8 0x60
+#define UART01x_LCRH_WLEN_7 0x40
+#define UART01x_LCRH_WLEN_6 0x20
+#define UART01x_LCRH_WLEN_5 0x00
+#define UART01x_LCRH_FEN 0x10
+#define UART01x_LCRH_STP2 0x08
+#define UART01x_LCRH_EPS 0x04
+#define UART01x_LCRH_PEN 0x02
+#define UART01x_LCRH_BRK 0x01
+
+#define UART010_IIR_RTIS 0x08
+#define UART010_IIR_TIS 0x04
+#define UART010_IIR_RIS 0x02
+#define UART010_IIR_MIS 0x01
+
+#define UART011_IFLS_RX1_8 (0 << 3)
+#define UART011_IFLS_RX2_8 (1 << 3)
+#define UART011_IFLS_RX4_8 (2 << 3)
+#define UART011_IFLS_RX6_8 (3 << 3)
+#define UART011_IFLS_RX7_8 (4 << 3)
+#define UART011_IFLS_TX1_8 (0 << 0)
+#define UART011_IFLS_TX2_8 (1 << 0)
+#define UART011_IFLS_TX4_8 (2 << 0)
+#define UART011_IFLS_TX6_8 (3 << 0)
+#define UART011_IFLS_TX7_8 (4 << 0)
+
+/* Interrupt masks */
+#define UART011_OEIM (1 << 10) /* overrun error */
+#define UART011_BEIM (1 << 9) /* break error */
+#define UART011_PEIM (1 << 8) /* parity error */
+#define UART011_FEIM (1 << 7) /* framing error */
+#define UART011_RTIM (1 << 6) /* receive timeout */
+#define UART011_TXIM (1 << 5) /* transmit */
+#define UART011_RXIM (1 << 4) /* receive */
+#define UART011_DSRMIM (1 << 3) /* DSR interrupt mask */
+#define UART011_DCDMIM (1 << 2) /* DCD interrupt mask */
+#define UART011_CTSMIM (1 << 1) /* CTS interrupt mask */
+#define UART011_RIMIM (1 << 0) /* RI interrupt mask */
+
+/* Interrupt statuses */
+#define UART011_OEIS (1 << 10) /* overrun error */
+#define UART011_BEIS (1 << 9) /* break error */
+#define UART011_PEIS (1 << 8) /* parity error */
+#define UART011_FEIS (1 << 7) /* framing error */
+#define UART011_RTIS (1 << 6) /* receive timeout */
+#define UART011_TXIS (1 << 5) /* transmit */
+#define UART011_RXIS (1 << 4) /* receive */
+#define UART011_DSRMIS (1 << 3) /* DSR */
+#define UART011_DCDMIS (1 << 2) /* DCD */
+#define UART011_CTSMIS (1 << 1) /* CTS */
+#define UART011_RIMIS (1 << 0) /* RI */
+
+/* Interrupt clear masks */
+#define UART011_OEIC (1 << 10) /* overrun error */
+#define UART011_BEIC (1 << 9) /* break error */
+#define UART011_PEIC (1 << 8) /* parity error */
+#define UART011_FEIC (1 << 7) /* framing error */
+#define UART011_RTIC (1 << 6) /* receive timeout */
+#define UART011_TXIC (1 << 5) /* transmit */
+#define UART011_RXIC (1 << 4) /* receive */
+#define UART011_DSRMIC (1 << 3) /* DSR */
+#define UART011_DCDMIC (1 << 2) /* DCD */
+#define UART011_CTSMIC (1 << 1) /* CTS */
+#define UART011_RIMIC (1 << 0) /* RI */
+
+#define UART011_DMAONERR (1 << 2) /* disable dma on error */
+#define UART011_TXDMAE (1 << 1) /* enable transmit dma */
+#define UART011_RXDMAE (1 << 0) /* enable receive dma */
+
+#define UART01x_RSR_ANY (UART01x_RSR_OE | UART01x_RSR_BE | \
+ UART01x_RSR_PE | UART01x_RSR_FE)
+#define UART01x_FR_MODEM_ANY (UART01x_FR_DCD | UART01x_FR_DSR | \
+ UART01x_FR_CTS)
+
+/*
+ * Access macros for the AMBA UARTs
+ */
+#define UART_GET_INT_STATUS(p) readb((p)->membase + UART010_IIR)
+#define UART_PUT_ICR(p, c) writel((c), (p)->membase + UART010_ICR)
+#define UART_GET_FR(p) readb((p)->membase + UART01x_FR)
+#define UART_GET_CHAR(p) readb((p)->membase + UART01x_DR)
+#define UART_PUT_CHAR(p, c) writel((c), (p)->membase + UART01x_DR)
+#define UART_GET_RSR(p) readb((p)->membase + UART01x_RSR)
+#define UART_GET_CR(p) readb((p)->membase + UART010_CR)
+#define UART_PUT_CR(p, c) writel((c), (p)->membase + UART010_CR)
+#define UART_GET_LCRL(p) readb((p)->membase + UART010_LCRL)
+#define UART_PUT_LCRL(p, c) writel((c), (p)->membase + UART010_LCRL)
+#define UART_GET_LCRM(p) readb((p)->membase + UART010_LCRM)
+#define UART_PUT_LCRM(p, c) writel((c), (p)->membase + UART010_LCRM)
+#define UART_GET_LCRH(p) readb((p)->membase + UART010_LCRH)
+#define UART_PUT_LCRH(p, c) writel((c), (p)->membase + UART010_LCRH)
+#define UART_RX_DATA(s) (((s) & UART01x_FR_RXFE) == 0)
+#define UART_TX_READY(s) (((s) & UART01x_FR_TXFF) == 0)
+#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & UART01x_FR_TMSK) == 0)
+
+#define UART_DUMMY_RSR_RX /*256*/0
+#define UART_PORT_SIZE 64
+
+#endif /* STMP_DBG_H */
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b9d0efb6803f..189adbfee1c0 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -181,6 +181,12 @@ config SPI_S3C24XX_GPIO
the inbuilt hardware cannot provide the transfer mode, or
where the board is using non hardware connected pins.
+config SPI_S3C2443
+ tristate "Samsung S3C2443 High Speed SPI"
+ depends on SPI_MASTER && CPU_S3C2443
+ help
+ High Speed SPI driver for Samsung S3C2443
+
config SPI_SH_SCI
tristate "SuperH SCI SPI controller"
depends on SUPERH
@@ -204,6 +210,58 @@ config SPI_XILINX
See the "OPB Serial Peripheral Interface (SPI) (v1.00e)"
Product Specification document (DS464) for hardware details.
+config SPI_NS921X
+ tristate "Digi ns921x SPI controller"
+ depends on PROCESSOR_NS921X
+ help
+ Driver for SPI controller in Digi ns921x CPUs
+
+ This driver can also be built as a module. If so, the module
+ will be called spi_ns921x.
+
+config SPI_NS9360
+ tristate "Digi ns9360 SPI controller"
+ depends on PROCESSOR_NS9360
+ help
+ Driver for SPI controller in Digi ns9360 CPUs
+
+ This driver can also be built as a module. If so, the module
+ will be called spi_ns9360.
+
+config SPI_MXC
+ tristate "MXC CSPI controller as SPI Master"
+ depends on ARCH_MXC && SPI_MASTER
+ select SPI_BITBANG
+ help
+ This implements the SPI master mode using MXC CSPI.
+
+config SPI_MXC_TEST_LOOPBACK
+ bool "LOOPBACK Testing of CSPIs"
+ depends on SPI_MXC
+ select SPI_SPIDEV
+ default n
+
+config SPI_MXC_SELECT1
+ bool "CSPI1"
+ depends on SPI_MXC
+ default y
+
+config SPI_MXC_SELECT2
+ bool "CSPI2"
+ depends on SPI_MXC && (!ARCH_MXC91221)
+ default n
+
+config SPI_MXC_SELECT3
+ bool "CSPI3"
+ depends on SPI_MXC && (ARCH_MX3 || ARCH_MX27 || ARCH_MX25 || ARCH_MX37 || ARCH_MX51)
+ default n
+
+config SPI_STMP3XXX
+ tristate "Freescale STMP37xx/378x SPI/SSP controller"
+ depends on ARCH_STMP3XXX && SPI_MASTER
+ help
+ SPI driver for Freescale STMP37xx/378x SoC SSP interface
+
#
# Add new SPI master controllers in alphabetical order above this line
#
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index ccf18de34e1e..d1190a82a22f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -27,8 +27,13 @@ obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
+obj-$(CONFIG_SPI_MXC) += mxc_spi.o
obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
+obj-$(CONFIG_SPI_NS921X) += spi_ns921x.o
+obj-$(CONFIG_SPI_NS9360) += spi_ns9360.o
+obj-$(CONFIG_SPI_S3C2443) += spi_s3c2443.o
+obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o
# ... add above this line ...
# SPI protocol drivers (device/link on bus)
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
new file mode 100644
index 000000000000..32e764059a68
--- /dev/null
+++ b/drivers/spi/mxc_spi.c
@@ -0,0 +1,1302 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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-licensisr_locke.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @defgroup SPI Configurable Serial Peripheral Interface (CSPI) Driver
+ */
+
+/*!
+ * @file mxc_spi.c
+ * @brief This file contains the implementation of the SPI master controller services
+ *
+ *
+ * @ingroup SPI
+ */
+
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <mach/hardware.h>
+
+#define MXC_CSPIRXDATA 0x00
+#define MXC_CSPITXDATA 0x04
+#define MXC_CSPICTRL 0x08
+#define MXC_CSPICONFIG 0x08
+#define MXC_CSPIINT 0x0C
+
+#define MXC_CSPICTRL_DISABLE 0x0
+#define MXC_CSPICTRL_SLAVE 0x0
+#define MXC_CSPICTRL_CSMASK 0x3
+#define MXC_CSPICTRL_SMC (1 << 3)
+
+#define MXC_CSPIINT_TEEN_SHIFT 0
+#define MXC_CSPIINT_THEN_SHIFT 1
+#define MXC_CSPIINT_TFEN_SHIFT 2
+#define MXC_CSPIINT_RREN_SHIFT 3
+#define MXC_CSPIINT_RHEN_SHIFT 4
+#define MXC_CSPIINT_RFEN_SHIFT 5
+#define MXC_CSPIINT_ROEN_SHIFT 6
+
+#define MXC_HIGHPOL 0x0
+#define MXC_NOPHA 0x0
+#define MXC_LOWSSPOL 0x0
+
+#define MXC_CSPISTAT_TE 0
+#define MXC_CSPISTAT_TH 1
+#define MXC_CSPISTAT_TF 2
+#define MXC_CSPISTAT_RR 3
+#define MXC_CSPISTAT_RH 4
+#define MXC_CSPISTAT_RF 5
+#define MXC_CSPISTAT_RO 6
+
+#define MXC_CSPIPERIOD_32KHZ (1 << 15)
+
+/*!
+ * @struct mxc_spi_unique_def
+ * @brief This structure contains information that differs with
+ * SPI master controller hardware version
+ */
+struct mxc_spi_unique_def {
+ /* Width of valid bits in MXC_CSPIINT */
+ unsigned int intr_bit_shift;
+ /* Chip Select shift */
+ unsigned int cs_shift;
+ /* Bit count shift */
+ unsigned int bc_shift;
+ /* Bit count mask */
+ unsigned int bc_mask;
+ /* Data Control shift */
+ unsigned int drctrl_shift;
+ /* Transfer Complete shift */
+ unsigned int xfer_complete;
+ /* Bit counnter overflow shift */
+ unsigned int bc_overflow;
+ /* FIFO Size */
+ unsigned int fifo_size;
+ /* Control reg address */
+ unsigned int ctrl_reg_addr;
+ /* Status reg address */
+ unsigned int stat_reg_addr;
+ /* Period reg address */
+ unsigned int period_reg_addr;
+ /* Test reg address */
+ unsigned int test_reg_addr;
+ /* Reset reg address */
+ unsigned int reset_reg_addr;
+ /* SPI mode mask */
+ unsigned int mode_mask;
+ /* SPI enable */
+ unsigned int spi_enable;
+ /* XCH bit */
+ unsigned int xch;
+ /* Spi mode shift */
+ unsigned int mode_shift;
+ /* Spi master mode enable */
+ unsigned int master_enable;
+ /* TX interrupt enable diff */
+ unsigned int tx_inten_dif;
+ /* RX interrupt enable bit diff */
+ unsigned int rx_inten_dif;
+ /* Interrupt status diff */
+ unsigned int int_status_dif;
+ /* Low pol shift */
+ unsigned int low_pol_shift;
+ /* Phase shift */
+ unsigned int pha_shift;
+ /* SS control shift */
+ unsigned int ss_ctrl_shift;
+ /* SS pol shift */
+ unsigned int ss_pol_shift;
+ /* Maximum data rate */
+ unsigned int max_data_rate;
+ /* Data mask */
+ unsigned int data_mask;
+ /* Data shift */
+ unsigned int data_shift;
+ /* Loopback control */
+ unsigned int lbc;
+ /* RX count off */
+ unsigned int rx_cnt_off;
+ /* RX count mask */
+ unsigned int rx_cnt_mask;
+ /* Reset start */
+ unsigned int reset_start;
+ /* SCLK control inactive state shift */
+ unsigned int sclk_ctl_shift;
+};
+
+struct mxc_spi;
+
+/*!
+ * Structure to group together all the data buffers and functions
+ * used in data transfers.
+ */
+struct mxc_spi_xfer {
+ /* Transmit buffer */
+ const void *tx_buf;
+ /* Receive buffer */
+ void *rx_buf;
+ /* Data transfered count */
+ unsigned int count;
+ /* Data received count, descending sequence, zero means no more data to
+ be received */
+ unsigned int rx_count;
+ /* Function to read the FIFO data to rx_buf */
+ void (*rx_get) (struct mxc_spi *, u32 val);
+ /* Function to get the data to be written to FIFO */
+ u32(*tx_get) (struct mxc_spi *);
+};
+
+/*!
+ * This structure is a way for the low level driver to define their own
+ * \b spi_master structure. This structure includes the core \b spi_master
+ * structure that is provided by Linux SPI Framework/driver as an
+ * element and has other elements that are specifically required by this
+ * low-level driver.
+ */
+struct mxc_spi {
+ /* SPI Master and a simple I/O queue runner */
+ struct spi_bitbang mxc_bitbang;
+ /* Completion flags used in data transfers */
+ struct completion xfer_done;
+ /* Data transfer structure */
+ struct mxc_spi_xfer transfer;
+ /* Resource structure, which will maintain base addresses and IRQs */
+ struct resource *res;
+ /* Base address of CSPI, used in readl and writel */
+ void *base;
+ /* CSPI IRQ number */
+ int irq;
+ /* CSPI Clock id */
+ struct clk *clk;
+ /* CSPI input clock SCLK */
+ unsigned long spi_ipg_clk;
+ /* CSPI registers' bit pattern */
+ struct mxc_spi_unique_def *spi_ver_def;
+ /* Control reg address */
+ void *ctrl_addr;
+ /* Status reg address */
+ void *stat_addr;
+ /* Period reg address */
+ void *period_addr;
+ /* Test reg address */
+ void *test_addr;
+ /* Reset reg address */
+ void *reset_addr;
+ /* Chipselect active function */
+ void (*chipselect_active) (int cspi_mode, int status, int chipselect);
+ /* Chipselect inactive function */
+ void (*chipselect_inactive) (int cspi_mode, int status, int chipselect);
+};
+
+#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK
+struct spi_chip_info {
+ int lb_enable;
+};
+
+static struct spi_chip_info lb_chip_info = {
+ .lb_enable = 1,
+};
+
+static struct spi_board_info loopback_info[] = {
+#ifdef CONFIG_SPI_MXC_SELECT1
+ {
+ .modalias = "spidev",
+ .controller_data = &lb_chip_info,
+ .irq = 0,
+ .max_speed_hz = 4000000,
+ .bus_num = 1,
+ .chip_select = 4,
+ },
+#endif
+#ifdef CONFIG_SPI_MXC_SELECT2
+ {
+ .modalias = "spidev",
+ .controller_data = &lb_chip_info,
+ .irq = 0,
+ .max_speed_hz = 4000000,
+ .bus_num = 2,
+ .chip_select = 4,
+ },
+#endif
+#ifdef CONFIG_SPI_MXC_SELECT3
+ {
+ .modalias = "spidev",
+ .controller_data = &lb_chip_info,
+ .irq = 0,
+ .max_speed_hz = 4000000,
+ .bus_num = 3,
+ .chip_select = 4,
+ },
+#endif
+};
+#endif
+
+static struct mxc_spi_unique_def spi_ver_2_3 = {
+ .intr_bit_shift = 8,
+ .cs_shift = 18,
+ .bc_shift = 20,
+ .bc_mask = 0xFFF,
+ .drctrl_shift = 16,
+ .xfer_complete = (1 << 7),
+ .bc_overflow = 0,
+ .fifo_size = 64,
+ .ctrl_reg_addr = 4,
+ .stat_reg_addr = 0x18,
+ .period_reg_addr = 0x1C,
+ .test_reg_addr = 0x20,
+ .reset_reg_addr = 0x8,
+ .mode_mask = 0xF,
+ .spi_enable = 0x1,
+ .xch = (1 << 2),
+ .mode_shift = 4,
+ .master_enable = 0,
+ .tx_inten_dif = 0,
+ .rx_inten_dif = 0,
+ .int_status_dif = 0,
+ .low_pol_shift = 4,
+ .pha_shift = 0,
+ .ss_ctrl_shift = 8,
+ .ss_pol_shift = 12,
+ .max_data_rate = 0xF,
+ .data_mask = 0xFF,
+ .data_shift = 8,
+ .lbc = (1 << 31),
+ .rx_cnt_off = 8,
+ .rx_cnt_mask = (0x7F << 8),
+ .reset_start = 0,
+ .sclk_ctl_shift = 20,
+};
+
+static struct mxc_spi_unique_def spi_ver_0_7 = {
+ .intr_bit_shift = 8,
+ .cs_shift = 12,
+ .bc_shift = 20,
+ .bc_mask = 0xFFF,
+ .drctrl_shift = 8,
+ .xfer_complete = (1 << 7),
+ .bc_overflow = 0,
+ .fifo_size = 8,
+ .ctrl_reg_addr = 0,
+ .stat_reg_addr = 0x14,
+ .period_reg_addr = 0x18,
+ .test_reg_addr = 0x1C,
+ .reset_reg_addr = 0x0,
+ .mode_mask = 0x1,
+ .spi_enable = 0x1,
+ .xch = (1 << 2),
+ .mode_shift = 1,
+ .master_enable = 1 << 1,
+ .tx_inten_dif = 0,
+ .rx_inten_dif = 0,
+ .int_status_dif = 0,
+ .low_pol_shift = 4,
+ .pha_shift = 5,
+ .ss_ctrl_shift = 6,
+ .ss_pol_shift = 7,
+ .max_data_rate = 0x7,
+ .data_mask = 0x7,
+ .data_shift = 16,
+ .lbc = (1 << 14),
+ .rx_cnt_off = 4,
+ .rx_cnt_mask = (0xF << 4),
+ .reset_start = 1,
+};
+
+static struct mxc_spi_unique_def spi_ver_0_5 = {
+ .intr_bit_shift = 9,
+ .cs_shift = 12,
+ .bc_shift = 20,
+ .bc_mask = 0xFFF,
+ .drctrl_shift = 8,
+ .xfer_complete = (1 << 8),
+ .bc_overflow = (1 << 7),
+ .fifo_size = 8,
+ .ctrl_reg_addr = 0,
+ .stat_reg_addr = 0x14,
+ .period_reg_addr = 0x18,
+ .test_reg_addr = 0x1C,
+ .reset_reg_addr = 0x0,
+ .mode_mask = 0x1,
+ .spi_enable = 0x1,
+ .xch = (1 << 2),
+ .mode_shift = 1,
+ .master_enable = 1 << 1,
+ .tx_inten_dif = 0,
+ .rx_inten_dif = 0,
+ .int_status_dif = 0,
+ .low_pol_shift = 4,
+ .pha_shift = 5,
+ .ss_ctrl_shift = 6,
+ .ss_pol_shift = 7,
+ .max_data_rate = 0x7,
+ .data_mask = 0x7,
+ .data_shift = 16,
+ .lbc = (1 << 14),
+ .rx_cnt_off = 4,
+ .rx_cnt_mask = (0xF << 4),
+ .reset_start = 1,
+};
+
+static struct mxc_spi_unique_def spi_ver_0_4 = {
+ .intr_bit_shift = 9,
+ .cs_shift = 24,
+ .bc_shift = 8,
+ .bc_mask = 0x1F,
+ .drctrl_shift = 20,
+ .xfer_complete = (1 << 8),
+ .bc_overflow = (1 << 7),
+ .fifo_size = 8,
+ .ctrl_reg_addr = 0,
+ .stat_reg_addr = 0x14,
+ .period_reg_addr = 0x18,
+ .test_reg_addr = 0x1C,
+ .reset_reg_addr = 0x0,
+ .mode_mask = 0x1,
+ .spi_enable = 0x1,
+ .xch = (1 << 2),
+ .mode_shift = 1,
+ .master_enable = 1 << 1,
+ .tx_inten_dif = 0,
+ .rx_inten_dif = 0,
+ .int_status_dif = 0,
+ .low_pol_shift = 4,
+ .pha_shift = 5,
+ .ss_ctrl_shift = 6,
+ .ss_pol_shift = 7,
+ .max_data_rate = 0x7,
+ .data_mask = 0x7,
+ .data_shift = 16,
+ .lbc = (1 << 14),
+ .rx_cnt_off = 4,
+ .rx_cnt_mask = (0xF << 4),
+ .reset_start = 1,
+};
+
+static struct mxc_spi_unique_def spi_ver_0_0 = {
+ .intr_bit_shift = 18,
+ .cs_shift = 19,
+ .bc_shift = 0,
+ .bc_mask = 0x1F,
+ .drctrl_shift = 12,
+ .xfer_complete = (1 << 3),
+ .bc_overflow = (1 << 8),
+ .fifo_size = 8,
+ .ctrl_reg_addr = 0,
+ .stat_reg_addr = 0x0C,
+ .period_reg_addr = 0x14,
+ .test_reg_addr = 0x10,
+ .reset_reg_addr = 0x1C,
+ .mode_mask = 0x1,
+ .spi_enable = (1 << 10),
+ .xch = (1 << 9),
+ .mode_shift = 11,
+ .master_enable = 1 << 11,
+ .tx_inten_dif = 9,
+ .rx_inten_dif = 10,
+ .int_status_dif = 1,
+ .low_pol_shift = 5,
+ .pha_shift = 6,
+ .ss_ctrl_shift = 7,
+ .ss_pol_shift = 8,
+ .max_data_rate = 0x10,
+ .data_mask = 0x1F,
+ .data_shift = 14,
+ .lbc = (1 << 14),
+ .rx_cnt_off = 4,
+ .rx_cnt_mask = (0xF << 4),
+ .reset_start = 1,
+};
+
+extern void gpio_spi_active(int cspi_mod);
+extern void gpio_spi_inactive(int cspi_mod);
+
+#define MXC_SPI_BUF_RX(type) \
+void mxc_spi_buf_rx_##type(struct mxc_spi *master_drv_data, u32 val)\
+{\
+ type *rx = master_drv_data->transfer.rx_buf;\
+ *rx++ = (type)val;\
+ master_drv_data->transfer.rx_buf = rx;\
+}
+
+#define MXC_SPI_BUF_TX(type) \
+u32 mxc_spi_buf_tx_##type(struct mxc_spi *master_drv_data)\
+{\
+ u32 val;\
+ const type *tx = master_drv_data->transfer.tx_buf;\
+ val = *tx++;\
+ master_drv_data->transfer.tx_buf = tx;\
+ return val;\
+}
+
+MXC_SPI_BUF_RX(u8)
+ MXC_SPI_BUF_TX(u8)
+ MXC_SPI_BUF_RX(u16)
+ MXC_SPI_BUF_TX(u16)
+ MXC_SPI_BUF_RX(u32)
+ MXC_SPI_BUF_TX(u32)
+
+/*!
+ * This function enables CSPI interrupt(s)
+ *
+ * @param master_data the pointer to mxc_spi structure
+ * @param irqs the irq(s) to set (can be a combination)
+ *
+ * @return This function returns 0 if successful, -1 otherwise.
+ */
+static int spi_enable_interrupt(struct mxc_spi *master_data, unsigned int irqs)
+{
+ if (irqs & ~((1 << master_data->spi_ver_def->intr_bit_shift) - 1)) {
+ return -1;
+ }
+
+ __raw_writel((irqs | __raw_readl(MXC_CSPIINT + master_data->ctrl_addr)),
+ MXC_CSPIINT + master_data->ctrl_addr);
+ return 0;
+}
+
+/*!
+ * This function disables CSPI interrupt(s)
+ *
+ * @param master_data the pointer to mxc_spi structure
+ * @param irqs the irq(s) to reset (can be a combination)
+ *
+ * @return This function returns 0 if successful, -1 otherwise.
+ */
+static int spi_disable_interrupt(struct mxc_spi *master_data, unsigned int irqs)
+{
+ if (irqs & ~((1 << master_data->spi_ver_def->intr_bit_shift) - 1)) {
+ return -1;
+ }
+
+ __raw_writel((~irqs &
+ __raw_readl(MXC_CSPIINT + master_data->ctrl_addr)),
+ MXC_CSPIINT + master_data->ctrl_addr);
+ return 0;
+}
+
+/*!
+ * This function sets the baud rate for the SPI module.
+ *
+ * @param master_data the pointer to mxc_spi structure
+ * @param baud the baud rate
+ *
+ * @return This function returns the baud rate divisor.
+ */
+static unsigned int spi_find_baudrate(struct mxc_spi *master_data,
+ unsigned int baud)
+{
+ unsigned int divisor;
+ unsigned int shift = 0;
+
+ /* Calculate required divisor (rounded) */
+ divisor = (master_data->spi_ipg_clk + baud / 2) / baud;
+ while (divisor >>= 1)
+ shift++;
+
+ if (master_data->spi_ver_def == &spi_ver_0_0) {
+ shift = (shift - 1) * 2;
+ } else if (master_data->spi_ver_def == &spi_ver_2_3) {
+ shift = shift;
+ } else {
+ shift -= 2;
+ }
+
+ if (shift > master_data->spi_ver_def->max_data_rate)
+ shift = master_data->spi_ver_def->max_data_rate;
+
+ return (shift << master_data->spi_ver_def->data_shift);
+}
+
+/*!
+ * This function loads the transmit fifo.
+ *
+ * @param base the CSPI base address
+ * @param count number of words to put in the TxFIFO
+ * @param master_drv_data spi master structure
+ */
+static void spi_put_tx_data(void *base, unsigned int count,
+ struct mxc_spi *master_drv_data)
+{
+ unsigned int ctrl_reg;
+ unsigned int data;
+ int i = 0;
+
+ /* Perform Tx transaction */
+ for (i = 0; i < count; i++) {
+ data = master_drv_data->transfer.tx_get(master_drv_data);
+ __raw_writel(data, base + MXC_CSPITXDATA);
+ }
+
+ ctrl_reg = __raw_readl(base + MXC_CSPICTRL);
+
+ ctrl_reg |= master_drv_data->spi_ver_def->xch;
+
+ __raw_writel(ctrl_reg, base + MXC_CSPICTRL);
+
+ return;
+}
+
+/*!
+ * This function configures the hardware CSPI for the current SPI device.
+ * It sets the word size, transfer mode, data rate for this device.
+ *
+ * @param spi the current SPI device
+ * @param is_active indicates whether to active/deactivate the current device
+ */
+void mxc_spi_chipselect(struct spi_device *spi, int is_active)
+{
+ struct mxc_spi *master_drv_data;
+ struct mxc_spi_xfer *ptransfer;
+ struct mxc_spi_unique_def *spi_ver_def;
+ unsigned int ctrl_reg = 0;
+ unsigned int config_reg = 0;
+ unsigned int xfer_len;
+ unsigned int cs_value;
+
+ if (is_active == BITBANG_CS_INACTIVE) {
+ /*Need to deselect the slave */
+ return;
+ }
+
+ /* Get the master controller driver data from spi device's master */
+
+ master_drv_data = spi_master_get_devdata(spi->master);
+ clk_enable(master_drv_data->clk);
+ spi_ver_def = master_drv_data->spi_ver_def;
+
+ xfer_len = spi->bits_per_word;
+
+ if (spi_ver_def == &spi_ver_2_3) {
+ /* Control Register Settings for transfer to this slave */
+ ctrl_reg = master_drv_data->spi_ver_def->spi_enable;
+ ctrl_reg |=
+ ((spi->chip_select & MXC_CSPICTRL_CSMASK) << spi_ver_def->
+ cs_shift);
+ ctrl_reg |=
+ (((1 << (spi->chip_select & MXC_CSPICTRL_CSMASK)) &
+ spi_ver_def->mode_mask) << spi_ver_def->mode_shift);
+ ctrl_reg |=
+ spi_find_baudrate(master_drv_data, spi->max_speed_hz);
+ ctrl_reg |=
+ (((xfer_len -
+ 1) & spi_ver_def->bc_mask) << spi_ver_def->bc_shift);
+
+ if (spi->mode & SPI_CPHA)
+ config_reg |=
+ (((1 << (spi->chip_select & MXC_CSPICTRL_CSMASK)) &
+ spi_ver_def->mode_mask) <<
+ spi_ver_def->pha_shift);
+
+ if ((spi->mode & SPI_CPOL)) {
+ config_reg |=
+ (((1 << (spi->chip_select & MXC_CSPICTRL_CSMASK)) &
+ spi_ver_def->mode_mask) <<
+ spi_ver_def->low_pol_shift);
+ config_reg |=
+ (((1 << (spi->chip_select & MXC_CSPICTRL_CSMASK)) &
+ spi_ver_def->mode_mask) <<
+ spi_ver_def->sclk_ctl_shift);
+ }
+ cs_value = (__raw_readl(MXC_CSPICONFIG +
+ master_drv_data->ctrl_addr) >>
+ spi_ver_def->ss_pol_shift) & spi_ver_def->mode_mask;
+ if (spi->mode & SPI_CS_HIGH) {
+ config_reg |=
+ ((((1 << (spi->chip_select & MXC_CSPICTRL_CSMASK)) &
+ spi_ver_def->mode_mask) | cs_value) <<
+ spi_ver_def->ss_pol_shift);
+ } else
+ config_reg |=
+ ((~((1 << (spi->chip_select &
+ MXC_CSPICTRL_CSMASK)) &
+ spi_ver_def->mode_mask) & cs_value) <<
+ spi_ver_def->ss_pol_shift);
+ config_reg |=
+ (((1 << (spi->chip_select & MXC_CSPICTRL_CSMASK)) &
+ spi_ver_def->mode_mask) << spi_ver_def->ss_ctrl_shift);
+ __raw_writel(0, master_drv_data->base + MXC_CSPICTRL);
+ __raw_writel(ctrl_reg, master_drv_data->base + MXC_CSPICTRL);
+ __raw_writel(config_reg,
+ MXC_CSPICONFIG + master_drv_data->ctrl_addr);
+ } else {
+ /* Control Register Settings for transfer to this slave */
+ ctrl_reg = master_drv_data->spi_ver_def->spi_enable;
+ ctrl_reg |=
+ (((spi->chip_select & MXC_CSPICTRL_CSMASK) << spi_ver_def->
+ cs_shift) | spi_ver_def->mode_mask <<
+ spi_ver_def->mode_shift);
+ ctrl_reg |=
+ spi_find_baudrate(master_drv_data, spi->max_speed_hz);
+ ctrl_reg |=
+ (((xfer_len -
+ 1) & spi_ver_def->bc_mask) << spi_ver_def->bc_shift);
+ if (spi->mode & SPI_CPHA)
+ ctrl_reg |=
+ spi_ver_def->mode_mask << spi_ver_def->pha_shift;
+ if (!(spi->mode & SPI_CPOL))
+ ctrl_reg |=
+ spi_ver_def->mode_mask << spi_ver_def->
+ low_pol_shift;
+ if (spi->mode & SPI_CS_HIGH)
+ ctrl_reg |=
+ spi_ver_def->mode_mask << spi_ver_def->ss_pol_shift;
+ if (spi_ver_def == &spi_ver_0_7)
+ ctrl_reg |=
+ spi_ver_def->mode_mask << spi_ver_def->
+ ss_ctrl_shift;
+
+ __raw_writel(ctrl_reg, master_drv_data->base + MXC_CSPICTRL);
+ }
+
+ /* Initialize the functions for transfer */
+ ptransfer = &master_drv_data->transfer;
+ if (xfer_len <= 8) {
+ ptransfer->rx_get = mxc_spi_buf_rx_u8;
+ ptransfer->tx_get = mxc_spi_buf_tx_u8;
+ } else if (xfer_len <= 16) {
+ ptransfer->rx_get = mxc_spi_buf_rx_u16;
+ ptransfer->tx_get = mxc_spi_buf_tx_u16;
+ } else {
+ ptransfer->rx_get = mxc_spi_buf_rx_u32;
+ ptransfer->tx_get = mxc_spi_buf_tx_u32;
+ }
+#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK
+ {
+ struct spi_chip_info *lb_chip =
+ (struct spi_chip_info *)spi->controller_data;
+ if (!lb_chip)
+ __raw_writel(0, master_drv_data->test_addr);
+ else if (lb_chip->lb_enable)
+ __raw_writel(spi_ver_def->lbc,
+ master_drv_data->test_addr);
+ }
+#endif
+ clk_disable(master_drv_data->clk);
+ return;
+}
+
+/*!
+ * This function is called when an interrupt occurs on the SPI modules.
+ * It is the interrupt handler for the SPI modules.
+ *
+ * @param irq the irq number
+ * @param dev_id the pointer on the device
+ *
+ * @return The function returns IRQ_HANDLED when handled.
+ */
+static irqreturn_t mxc_spi_isr(int irq, void *dev_id)
+{
+ struct mxc_spi *master_drv_data = dev_id;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned int status;
+ int fifo_size;
+ unsigned int pass_counter;
+
+ fifo_size = master_drv_data->spi_ver_def->fifo_size;
+ pass_counter = fifo_size;
+
+ /* Read the interrupt status register to determine the source */
+ status = __raw_readl(master_drv_data->stat_addr);
+ do {
+ u32 rx_tmp =
+ __raw_readl(master_drv_data->base + MXC_CSPIRXDATA);
+
+ if (master_drv_data->transfer.rx_buf)
+ master_drv_data->transfer.rx_get(master_drv_data,
+ rx_tmp);
+ (master_drv_data->transfer.count)--;
+ (master_drv_data->transfer.rx_count)--;
+ ret = IRQ_HANDLED;
+ if (pass_counter-- == 0) {
+ break;
+ }
+ status = __raw_readl(master_drv_data->stat_addr);
+ } while (status &
+ (1 <<
+ (MXC_CSPISTAT_RR +
+ master_drv_data->spi_ver_def->int_status_dif)));
+
+ if (master_drv_data->transfer.rx_count)
+ return ret;
+
+ if (master_drv_data->transfer.count) {
+ if (master_drv_data->transfer.tx_buf) {
+ u32 count = (master_drv_data->transfer.count >
+ fifo_size) ? fifo_size :
+ master_drv_data->transfer.count;
+ master_drv_data->transfer.rx_count = count;
+ spi_put_tx_data(master_drv_data->base, count,
+ master_drv_data);
+ }
+ } else {
+ complete(&master_drv_data->xfer_done);
+ }
+
+ return ret;
+}
+
+/*!
+ * This function initialize the current SPI device.
+ *
+ * @param spi the current SPI device.
+ *
+ */
+int mxc_spi_setup(struct spi_device *spi)
+{
+ if (spi->max_speed_hz < 0) {
+ return -EINVAL;
+ }
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+
+ pr_debug("%s: mode %d, %u bpw, %d hz\n", __FUNCTION__,
+ spi->mode, spi->bits_per_word, spi->max_speed_hz);
+
+ return 0;
+}
+
+/*!
+ * This function is called when the data has to transfer from/to the
+ * current SPI device in poll mode
+ *
+ * @param spi the current spi device
+ * @param t the transfer request - read/write buffer pairs
+ *
+ * @return Returns 0 on success.
+ */
+int mxc_spi_poll_transfer(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct mxc_spi *master_drv_data = NULL;
+ int count, i;
+ volatile unsigned int status;
+ u32 rx_tmp;
+ u32 fifo_size;
+ int chipselect_status;
+
+ mxc_spi_chipselect(spi, BITBANG_CS_ACTIVE);
+
+ /* Get the master controller driver data from spi device's master */
+ master_drv_data = spi_master_get_devdata(spi->master);
+
+ chipselect_status = __raw_readl(MXC_CSPICONFIG +
+ master_drv_data->ctrl_addr);
+ chipselect_status >>= master_drv_data->spi_ver_def->ss_pol_shift &
+ master_drv_data->spi_ver_def->mode_mask;
+ if (master_drv_data->chipselect_active)
+ master_drv_data->chipselect_active(spi->master->bus_num,
+ chipselect_status,
+ (spi->chip_select &
+ MXC_CSPICTRL_CSMASK) + 1);
+
+ clk_enable(master_drv_data->clk);
+
+ /* Modify the Tx, Rx, Count */
+ master_drv_data->transfer.tx_buf = t->tx_buf;
+ master_drv_data->transfer.rx_buf = t->rx_buf;
+ master_drv_data->transfer.count = t->len;
+ fifo_size = master_drv_data->spi_ver_def->fifo_size;
+
+ count = (t->len > fifo_size) ? fifo_size : t->len;
+ spi_put_tx_data(master_drv_data->base, count, master_drv_data);
+
+ while ((((status = __raw_readl(master_drv_data->test_addr)) &
+ master_drv_data->spi_ver_def->rx_cnt_mask) >> master_drv_data->
+ spi_ver_def->rx_cnt_off) != count) ;
+
+ for (i = 0; i < count; i++) {
+ rx_tmp = __raw_readl(master_drv_data->base + MXC_CSPIRXDATA);
+ master_drv_data->transfer.rx_get(master_drv_data, rx_tmp);
+ }
+
+ clk_disable(master_drv_data->clk);
+ if (master_drv_data->chipselect_inactive)
+ master_drv_data->chipselect_inactive(spi->master->bus_num,
+ chipselect_status,
+ (spi->chip_select &
+ MXC_CSPICTRL_CSMASK) + 1);
+ return 0;
+}
+
+/*!
+ * This function is called when the data has to transfer from/to the
+ * current SPI device. It enables the Rx interrupt, initiates the transfer.
+ * When Rx interrupt occurs, the completion flag is set. It then disables
+ * the Rx interrupt.
+ *
+ * @param spi the current spi device
+ * @param t the transfer request - read/write buffer pairs
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+int mxc_spi_transfer(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct mxc_spi *master_drv_data = NULL;
+ int count;
+ int chipselect_status;
+ u32 fifo_size;
+
+ /* Get the master controller driver data from spi device's master */
+
+ master_drv_data = spi_master_get_devdata(spi->master);
+
+ chipselect_status = __raw_readl(MXC_CSPICONFIG +
+ master_drv_data->ctrl_addr);
+ chipselect_status >>= master_drv_data->spi_ver_def->ss_pol_shift &
+ master_drv_data->spi_ver_def->mode_mask;
+ if (master_drv_data->chipselect_active)
+ master_drv_data->chipselect_active(spi->master->bus_num,
+ chipselect_status,
+ (spi->chip_select &
+ MXC_CSPICTRL_CSMASK) + 1);
+
+ clk_enable(master_drv_data->clk);
+ /* Modify the Tx, Rx, Count */
+ master_drv_data->transfer.tx_buf = t->tx_buf;
+ master_drv_data->transfer.rx_buf = t->rx_buf;
+ master_drv_data->transfer.count = t->len;
+ fifo_size = master_drv_data->spi_ver_def->fifo_size;
+ INIT_COMPLETION(master_drv_data->xfer_done);
+
+ /* Enable the Rx Interrupts */
+
+ spi_enable_interrupt(master_drv_data,
+ 1 << (MXC_CSPIINT_RREN_SHIFT +
+ master_drv_data->spi_ver_def->rx_inten_dif));
+ count = (t->len > fifo_size) ? fifo_size : t->len;
+
+ /* Perform Tx transaction */
+ master_drv_data->transfer.rx_count = count;
+ spi_put_tx_data(master_drv_data->base, count, master_drv_data);
+
+ /* Wait for transfer completion */
+ wait_for_completion(&master_drv_data->xfer_done);
+
+ /* Disable the Rx Interrupts */
+
+ spi_disable_interrupt(master_drv_data,
+ 1 << (MXC_CSPIINT_RREN_SHIFT +
+ master_drv_data->spi_ver_def->
+ rx_inten_dif));
+
+ clk_disable(master_drv_data->clk);
+ if (master_drv_data->chipselect_inactive)
+ master_drv_data->chipselect_inactive(spi->master->bus_num,
+ chipselect_status,
+ (spi->chip_select &
+ MXC_CSPICTRL_CSMASK) + 1);
+ return (t->len - master_drv_data->transfer.count);
+}
+
+/*!
+ * This function releases the current SPI device's resources.
+ *
+ * @param spi the current SPI device.
+ *
+ */
+void mxc_spi_cleanup(struct spi_device *spi)
+{
+}
+
+/*!
+ * This function is called during the driver binding process. Based on the CSPI
+ * hardware module that is being probed this function adds the appropriate SPI module
+ * structure in the SPI core driver.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions.
+ *
+ * @return The function returns 0 on successful registration and initialization
+ * of CSPI module. Otherwise returns specific error code.
+ */
+static int mxc_spi_probe(struct platform_device *pdev)
+{
+ struct mxc_spi_master *mxc_platform_info;
+ struct spi_master *master;
+ struct mxc_spi *master_drv_data = NULL;
+ unsigned int spi_ver;
+ int ret = -ENODEV;
+
+ /* Get the platform specific data for this master device */
+
+ mxc_platform_info = (struct mxc_spi_master *)pdev->dev.platform_data;
+ if (!mxc_platform_info) {
+ dev_err(&pdev->dev, "can't get the platform data for CSPI\n");
+ return -EINVAL;
+ }
+
+ /* Allocate SPI master controller */
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct mxc_spi));
+ if (!master) {
+ dev_err(&pdev->dev, "can't alloc for spi_master\n");
+ return -ENOMEM;
+ }
+
+ /* Set this device's driver data to master */
+
+ platform_set_drvdata(pdev, master);
+
+ /* Set this master's data from platform_info */
+
+ master->bus_num = pdev->id + 1;
+ master->num_chipselect = mxc_platform_info->maxchipselect;
+#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK
+ master->num_chipselect += 1;
+#endif
+ /* Set the master controller driver data for this master */
+
+ master_drv_data = spi_master_get_devdata(master);
+ master_drv_data->mxc_bitbang.master = spi_master_get(master);
+ if (mxc_platform_info->chipselect_active)
+ master_drv_data->chipselect_active =
+ mxc_platform_info->chipselect_active;
+ if (mxc_platform_info->chipselect_inactive)
+ master_drv_data->chipselect_inactive =
+ mxc_platform_info->chipselect_inactive;
+
+ /* Identify SPI version */
+
+ spi_ver = mxc_platform_info->spi_version;
+ if (spi_ver == 7) {
+ master_drv_data->spi_ver_def = &spi_ver_0_7;
+ } else if (spi_ver == 5) {
+ master_drv_data->spi_ver_def = &spi_ver_0_5;
+ } else if (spi_ver == 4) {
+ master_drv_data->spi_ver_def = &spi_ver_0_4;
+ } else if (spi_ver == 0) {
+ master_drv_data->spi_ver_def = &spi_ver_0_0;
+ } else if (spi_ver == 23) {
+ master_drv_data->spi_ver_def = &spi_ver_2_3;
+ }
+
+ dev_dbg(&pdev->dev, "SPI_REV 0.%d\n", spi_ver);
+
+ /* Set the master bitbang data */
+
+ master_drv_data->mxc_bitbang.chipselect = mxc_spi_chipselect;
+ master_drv_data->mxc_bitbang.txrx_bufs = mxc_spi_transfer;
+ master_drv_data->mxc_bitbang.master->setup = mxc_spi_setup;
+ master_drv_data->mxc_bitbang.master->cleanup = mxc_spi_cleanup;
+
+ /* Initialize the completion object */
+
+ init_completion(&master_drv_data->xfer_done);
+
+ /* Set the master controller register addresses and irqs */
+
+ master_drv_data->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!master_drv_data->res) {
+ dev_err(&pdev->dev, "can't get platform resource for CSPI%d\n",
+ master->bus_num);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (!request_mem_region(master_drv_data->res->start,
+ master_drv_data->res->end -
+ master_drv_data->res->start + 1, pdev->name)) {
+ dev_err(&pdev->dev, "request_mem_region failed for CSPI%d\n",
+ master->bus_num);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ master_drv_data->base = (void *)IO_ADDRESS(master_drv_data->res->start);
+ if (!master_drv_data->base) {
+ dev_err(&pdev->dev, "invalid base address for CSPI%d\n",
+ master->bus_num);
+ ret = -EINVAL;
+ goto err1;
+ }
+
+ master_drv_data->irq = platform_get_irq(pdev, 0);
+ if (master_drv_data->irq < 0) {
+ dev_err(&pdev->dev, "can't get IRQ for CSPI%d\n",
+ master->bus_num);
+ ret = -EINVAL;
+ goto err1;
+ }
+
+ /* Register for SPI Interrupt */
+
+ ret = request_irq(master_drv_data->irq, mxc_spi_isr,
+ 0, "CSPI_IRQ", master_drv_data);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "request_irq failed for CSPI%d\n",
+ master->bus_num);
+ goto err1;
+ }
+
+ /* Setup any GPIO active */
+
+ gpio_spi_active(master->bus_num - 1);
+
+ /* Enable the CSPI Clock, CSPI Module, set as a master */
+
+ master_drv_data->ctrl_addr =
+ master_drv_data->base + master_drv_data->spi_ver_def->ctrl_reg_addr;
+ master_drv_data->stat_addr =
+ master_drv_data->base + master_drv_data->spi_ver_def->stat_reg_addr;
+ master_drv_data->period_addr =
+ master_drv_data->base +
+ master_drv_data->spi_ver_def->period_reg_addr;
+ master_drv_data->test_addr =
+ master_drv_data->base + master_drv_data->spi_ver_def->test_reg_addr;
+ master_drv_data->reset_addr =
+ master_drv_data->base +
+ master_drv_data->spi_ver_def->reset_reg_addr;
+
+ master_drv_data->clk = clk_get(&pdev->dev, "cspi_clk");
+ clk_enable(master_drv_data->clk);
+ master_drv_data->spi_ipg_clk = clk_get_rate(master_drv_data->clk);
+
+ __raw_writel(master_drv_data->spi_ver_def->reset_start,
+ master_drv_data->reset_addr);
+ udelay(1);
+ __raw_writel((master_drv_data->spi_ver_def->spi_enable +
+ master_drv_data->spi_ver_def->master_enable),
+ master_drv_data->base + MXC_CSPICTRL);
+ __raw_writel(MXC_CSPIPERIOD_32KHZ, master_drv_data->period_addr);
+ __raw_writel(0, MXC_CSPIINT + master_drv_data->ctrl_addr);
+
+ /* Start the SPI Master Controller driver */
+
+ ret = spi_bitbang_start(&master_drv_data->mxc_bitbang);
+
+ if (ret != 0)
+ goto err2;
+
+ printk(KERN_INFO "CSPI: %s-%d probed\n", pdev->name, pdev->id);
+
+#ifdef CONFIG_SPI_MXC_TEST_LOOPBACK
+ {
+ int i;
+ struct spi_board_info *bi = &loopback_info[0];
+ for (i = 0; i < ARRAY_SIZE(loopback_info); i++, bi++) {
+ if (bi->bus_num != master->bus_num)
+ continue;
+
+ dev_info(&pdev->dev,
+ "registering loopback device '%s'\n",
+ bi->modalias);
+
+ spi_new_device(master, bi);
+ }
+ }
+#endif
+ clk_disable(master_drv_data->clk);
+ return ret;
+
+ err2:
+ gpio_spi_inactive(master->bus_num - 1);
+ clk_disable(master_drv_data->clk);
+ clk_put(master_drv_data->clk);
+ free_irq(master_drv_data->irq, master_drv_data);
+ err1:
+ release_mem_region(pdev->resource[0].start,
+ pdev->resource[0].end - pdev->resource[0].start + 1);
+ err:
+ spi_master_put(master);
+ kfree(master);
+ platform_set_drvdata(pdev, NULL);
+ return ret;
+}
+
+/*!
+ * Dissociates the driver from the SPI master controller. Disables the CSPI module.
+ * It handles the release of SPI resources like IRQ, memory,..etc.
+ *
+ * @param pdev the device structure used to give information on which SPI
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+
+ if (master) {
+ struct mxc_spi *master_drv_data =
+ spi_master_get_devdata(master);
+
+ gpio_spi_inactive(master->bus_num - 1);
+
+ /* Disable the CSPI module */
+ clk_enable(master_drv_data->clk);
+ __raw_writel(MXC_CSPICTRL_DISABLE,
+ master_drv_data->base + MXC_CSPICTRL);
+ clk_disable(master_drv_data->clk);
+ /* Unregister for SPI Interrupt */
+
+ free_irq(master_drv_data->irq, master_drv_data);
+
+ release_mem_region(master_drv_data->res->start,
+ master_drv_data->res->end -
+ master_drv_data->res->start + 1);
+
+ /* Stop the SPI Master Controller driver */
+
+ spi_bitbang_stop(&master_drv_data->mxc_bitbang);
+
+ spi_master_put(master);
+ }
+
+ printk(KERN_INFO "CSPI: %s-%d removed\n", pdev->name, pdev->id);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int spi_bitbang_suspend(struct spi_bitbang *bitbang)
+{
+ unsigned long flags;
+ unsigned limit = 500;
+
+ spin_lock_irqsave(&bitbang->lock, flags);
+ while (!list_empty(&bitbang->queue) && limit--) {
+ spin_unlock_irqrestore(&bitbang->lock, flags);
+
+ dev_dbg(&bitbang->master->dev, "wait for queue\n");
+ msleep(10);
+
+ spin_lock_irqsave(&bitbang->lock, flags);
+ }
+ if (!list_empty(&bitbang->queue)) {
+ dev_err(&bitbang->master->dev, "queue didn't empty\n");
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&bitbang->lock, flags);
+
+ return 0;
+}
+
+static void spi_bitbang_resume(struct spi_bitbang *bitbang)
+{
+ spin_lock_init(&bitbang->lock);
+ INIT_LIST_HEAD(&bitbang->queue);
+
+ bitbang->busy = 0;
+}
+
+/*!
+ * This function puts the SPI master controller in low-power mode/state.
+ *
+ * @param pdev the device structure used to give information on which SDHC
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_spi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct mxc_spi *master_drv_data = spi_master_get_devdata(master);
+ int ret = 0;
+
+ spi_bitbang_suspend(&master_drv_data->mxc_bitbang);
+ clk_enable(master_drv_data->clk);
+ __raw_writel(MXC_CSPICTRL_DISABLE,
+ master_drv_data->base + MXC_CSPICTRL);
+ clk_disable(master_drv_data->clk);
+ gpio_spi_inactive(master->bus_num - 1);
+
+ return ret;
+}
+
+/*!
+ * This function brings the SPI master controller back from low-power state.
+ *
+ * @param pdev the device structure used to give information on which SDHC
+ * to resume
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_spi_resume(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct mxc_spi *master_drv_data = spi_master_get_devdata(master);
+
+ gpio_spi_active(master->bus_num - 1);
+
+ spi_bitbang_resume(&master_drv_data->mxc_bitbang);
+ clk_enable(master_drv_data->clk);
+ __raw_writel(master_drv_data->spi_ver_def->spi_enable,
+ master_drv_data->base + MXC_CSPICTRL);
+ clk_disable(master_drv_data->clk);
+ return 0;
+}
+#else
+#define mxc_spi_suspend NULL
+#define mxc_spi_resume NULL
+#endif /* CONFIG_PM */
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_spi_driver = {
+ .driver = {
+ .name = "mxc_spi",
+ .bus = &platform_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = mxc_spi_probe,
+ .remove = mxc_spi_remove,
+ .suspend_late = mxc_spi_suspend,
+ .resume_early = mxc_spi_resume,
+};
+
+/*!
+ * This function implements the init function of the SPI device.
+ * It is called when the module is loaded. It enables the required
+ * clocks to CSPI module(if any) and activates necessary GPIO pins.
+ *
+ * @return This function returns 0.
+ */
+static int __init mxc_spi_init(void)
+{
+ pr_debug("Registering the SPI Controller Driver\n");
+ return platform_driver_register(&mxc_spi_driver);
+}
+
+/*!
+ * This function implements the exit function of the SPI device.
+ * It is called when the module is unloaded. It deactivates the
+ * the GPIO pin associated with CSPI hardware modules.
+ *
+ */
+static void __exit mxc_spi_exit(void)
+{
+ pr_debug("Unregistering the SPI Controller Driver\n");
+ platform_driver_unregister(&mxc_spi_driver);
+}
+
+subsys_initcall(mxc_spi_init);
+module_exit(mxc_spi_exit);
+
+MODULE_DESCRIPTION("SPI Master Controller driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 3734dc9708e1..de9f5cb36ea4 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -660,7 +660,7 @@ int spi_write_then_read(struct spi_device *spi,
int status;
struct spi_message message;
- struct spi_transfer x;
+ struct spi_transfer x[2];
u8 *local_buf;
/* Use preallocated DMA-safe buffer. We can't avoid copying here,
@@ -671,9 +671,15 @@ int spi_write_then_read(struct spi_device *spi,
return -EINVAL;
spi_message_init(&message);
- memset(&x, 0, sizeof x);
- x.len = n_tx + n_rx;
- spi_message_add_tail(&x, &message);
+ memset(x, 0, sizeof x);
+ if (n_tx) {
+ x[0].len = n_tx;
+ spi_message_add_tail(&x[0], &message);
+ }
+ if (n_rx) {
+ x[1].len = n_rx;
+ spi_message_add_tail(&x[1], &message);
+ }
/* ... unless someone else is using the pre-allocated buffer */
if (!mutex_trylock(&lock)) {
@@ -684,15 +690,15 @@ int spi_write_then_read(struct spi_device *spi,
local_buf = buf;
memcpy(local_buf, txbuf, n_tx);
- x.tx_buf = local_buf;
- x.rx_buf = local_buf;
+ x[0].tx_buf = local_buf;
+ x[1].rx_buf = local_buf + n_tx;
/* do the i/o */
status = spi_sync(spi, &message);
if (status == 0)
- memcpy(rxbuf, x.rx_buf + n_tx, n_rx);
+ memcpy(rxbuf, x[1].rx_buf, n_rx);
- if (x.tx_buf == buf)
+ if (x[0].tx_buf == buf)
mutex_unlock(&lock);
else
kfree(local_buf);
diff --git a/drivers/spi/spi_ns921x.c b/drivers/spi/spi_ns921x.c
new file mode 100644
index 000000000000..dedff9b8c369
--- /dev/null
+++ b/drivers/spi/spi_ns921x.c
@@ -0,0 +1,852 @@
+/*
+ * linux/drivers/spi/spi_ns921x.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ * (C) Copyright 2009, Emerald Electronics Design, LLC,
+ * Mark Litwack <mlitwack@employees.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spinlock.h>
+//#include <linux/uaccess.h>
+//#include <linux/workqueue.h>
+
+#include <mach/dma.h>
+#include <mach/gpio.h>
+#include <mach/spi.h>
+
+/* registers */
+#define DMA_IRQSTATUS (0x0000)
+#define DMA_RXCTL (0x0004)
+#define DMA_RXBUF (0x0008)
+#define DMA_RXIRQCFG (0x000c)
+#define DMA_TXCTL (0x0018)
+#define DMA_TXBUF (0x001c)
+#define DMA_TXIRQCFG (0x0020)
+
+#define SPI_CONFIG (0x1000)
+#define SPI_CLOCK (0x1010)
+#define SPI_IRQENABLE (0x1020)
+#define SPI_IRQSTATUS (0x1024)
+
+/* DMA config bit fields */
+#define DMA_ENABLECHANNEL (1 << 31)
+/* DMA interrupt/FIFO status flags */
+#define DMA_IRQSTAT_RXNCIP (1 << 31)
+#define DMA_IRQSTAT_RXECIP (1 << 30)
+#define DMA_IRQSTAT_RXNRIP (1 << 29)
+#define DMA_IRQSTAT_RXCAIP (1 << 28)
+#define DMA_IRQSTAT_RXPCIP (1 << 27)
+#define DMA_IRQSTAT_TXNCIP (1 << 24)
+#define DMA_IRQSTAT_TXECIP (1 << 23)
+#define DMA_IRQSTAT_TXNRIP (1 << 22)
+#define DMA_IRQSTAT_TXCAIP (1 << 21)
+/* DMA interrupt configuration flags */
+#define DMA_IRQCFG_NCIE (1 << 24)
+#define DMA_IRQCFG_ECIE (1 << 23)
+#define DMA_IRQCFG_NRIE (1 << 22)
+#define DMA_IRQCFG_CAIE (1 << 21)
+#define DMA_IRQCFG_PCIE (1 << 20)
+#define DMA_IRQCFG_BLENSTAT (0xFFFF)
+/* SPI config bit fields */
+#define SPI_CONFIG_MASTER (1 << 0)
+#define SPI_CONFIG_SLAVE (1 << 1)
+#define SPI_CONFIG_BITORDRMSB (1 << 2)
+#define SPI_CONFIG_RXBYTE (1 << 3)
+#define SPI_CONFIG_MODE0 (0x00)
+#define SPI_CONFIG_MODE1 (0x10)
+#define SPI_CONFIG_MODE2 (0x20)
+#define SPI_CONFIG_MODE3 (0x30)
+#define SPI_CLOCK_ENABLE (1 << 16)
+#define SPI_IRQENABLE_RXIDLE (1 << 0)
+#define SPI_IRQENABLE_TXIDLE (1 << 1)
+#define SPI_IRQSTATUS_RXNRIP (1 << 29)
+#define SPI_IRQSTATUS_TXNRIP (1 << 22)
+
+/* config masks */
+#define SPI_CONFIG_MODE_MASK (0x30)
+#define SPI_CONFIG_DISCARD_MASK (0x700)
+#define SPI_CLOCK_DIVISOR_MASK (0x3FF)
+
+/* config shifts */
+#define SPI_CONFIG_MODE_SHIFT (4)
+#define SPI_CONFIG_DISCARD_SHIFT (8)
+#define SPI_CLOCK_DIVISOR_SHIFT (0)
+
+#define SPI_IDLE_LEVEL 0xff
+#define MAX_RATE 33330000
+#define MAX_BUFFER_SIZE 65532
+
+#define SPI_CS_ACTIVE 1
+#define SPI_CS_INACTIVE 0
+
+#define DRIVER_NAME "spi_ns921x"
+
+union ns9xxx_dma_desc {
+ struct {
+ u32 source;
+ u16 len;
+ u16 reserved1;
+ u32 reserved2;
+
+ u16 status;
+#define DMADESC_WRAP (1 << 15)
+#define DMADESC_INTR (1 << 14)
+#define DMATXDESC_LAST (1 << 13)
+#define DMADESC_FULL (1 << 12)
+ u16 flags;
+ };
+ u32 data[4];
+};
+
+struct spi_ns921x {
+ void __iomem *ioaddr; /* SPI DMA base address */
+ struct resource *mem;
+ struct clk *clk;
+
+// struct workqueue_struct *workqueue;
+ struct work_struct work;
+ spinlock_t lock;
+ struct list_head queue;
+ struct spi_transfer *current_transfer;
+ unsigned long remaining_bytes;
+ unsigned dma_xfer_len;
+ u32 flags;
+ wait_queue_head_t waitq;
+
+ int irq;
+ unsigned int speed;
+
+/* union ns9xxx_dma_desc *rxdesc;
+ union ns9xxx_dma_desc *txdesc;
+ dma_addr_t rxdesc_real;
+ dma_addr_t txdesc_real;
+*/
+ void *buf_tx;
+ void *buf_rx;
+ dma_addr_t buf_tx_dma;
+ dma_addr_t buf_rx_dma;
+ union ns9xxx_dma_desc *desc_tx;
+ union ns9xxx_dma_desc *desc_rx;
+ dma_addr_t desc_tx_dma;
+ dma_addr_t desc_rx_dma;
+
+ struct spi_ns9xxx_data *pdata;
+ //struct tasklet_struct xmit_tasklet;
+};
+
+#define BUFFER_SIZE PAGE_SIZE
+#define INVALID_DMA_ADDRESS 0xffffffff
+#define SPI_RX_IDONE 0x00000001
+#define SPI_TX_IDONE 0x00000002
+
+static void spi_ns921x_chipsel(struct spi_device *spi, int active)
+{
+unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
+void (* func)(struct spi_device *spi, int active) = spi->controller_data;
+struct spi_ns921x *info = spi_master_get_devdata(spi->master);
+
+ /* use special function to toggle CS? */
+ if( func != NULL ) {
+ /* Reconfigure CS line as GPIO input */
+ if( SPI_CS_ACTIVE == active )
+ gpio_configure_ns921x(info->pdata->gpios[SPI_EN_GPIO_OFFSET],
+ NS921X_GPIO_INPUT,
+ NS921X_GPIO_DONT_INVERT,
+ NS921X_GPIO_FUNC_3,
+ NS921X_GPIO_ENABLE_PULLUP );
+
+ /* Call the special function */
+ func( spi, cspol == active);
+
+ /* Reconfigure CS line as SPI_EN */
+ if( SPI_CS_INACTIVE == active )
+ gpio_configure_ns921x(info->pdata->gpios[SPI_EN_GPIO_OFFSET],
+ 0,
+ 0,
+ info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET],
+ 0);
+ }
+ else {
+ /* Use a GPIO to toggle CS? */
+ if( info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET] == NS921X_GPIO_FUNC_GPIO )
+ gpio_set_value( info->pdata->gpios[SPI_EN_GPIO_OFFSET], cspol == active );
+ /* otherwise CS will be automatically controlled by the spi engine */
+ }
+}
+
+/*
+ * Configure the controller for the passed SPI-device. Additionally it will
+ * use the configuration values of the SPI-transfer if it's different than NULL
+ */
+static int spi_ns921x_setupxfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ struct spi_ns921x *info = spi_master_get_devdata(spi->master);
+ unsigned int div,reg;
+ unsigned int bpw;
+ unsigned int hz;
+
+ pr_debug("Setup for next xfer called\n");
+
+ /* If the bits per word is zero, then use the default value of the SPI-device */
+ bpw = t ? t->bits_per_word : spi->bits_per_word;
+ if (!bpw)
+ bpw = spi->bits_per_word;
+
+ /* We only support eight bits per word */
+ if (bpw != 8) {
+ pr_err("Invalid bits-per-word (%d)\n", bpw);
+ return -EINVAL;
+ }
+
+ /*
+ * If the clock rate of the xfer is zero, then use the already configured
+ * rate of this SPI-device
+ */
+ hz = t ? t->speed_hz : spi->max_speed_hz;
+ if (!hz)
+ hz = spi->max_speed_hz;
+
+ if( hz > MAX_RATE ) {
+ pr_err("Invalid speed (%d). Max is %d\n", hz, spi->max_speed_hz );
+ return -EINVAL;
+ }
+
+ /* calculate divisor */
+ div = (clk_get_rate(info->clk) / hz + 1);
+ if(div > SPI_CLOCK_DIVISOR_MASK) {
+ pr_err("Invalid speed (%d). Min is %ld\n",
+ hz,
+ clk_get_rate(info->clk)/(SPI_CLOCK_DIVISOR_MASK-1));
+ return -EINVAL;
+ }
+
+ /* configure clock divisor (3 steps, see hardware reference) */
+ /* step1: unset SPI_CLOCK_ENABLE, don't touch other bits */
+ reg = ioread32(info->ioaddr + SPI_CLOCK);
+ iowrite32(reg & ~SPI_CLOCK_ENABLE, info->ioaddr + SPI_CLOCK);
+
+ reg = div & (SPI_CLOCK_DIVISOR_MASK << SPI_CLOCK_DIVISOR_SHIFT);
+ iowrite32(reg, info->ioaddr + SPI_CLOCK);
+
+ /* step3: set SPI_CLOCK_ENABLE, don't touch other bits */
+ iowrite32(reg | SPI_CLOCK_ENABLE, info->ioaddr + SPI_CLOCK);
+
+ return 0;
+}
+
+static int spi_ns921x_setup(struct spi_device *spi)
+{
+ struct spi_ns921x *info = spi_master_get_devdata(spi->master);
+ unsigned int reg;
+ int ret;
+
+ if (spi->chip_select > spi->master->num_chipselect) {
+ dev_dbg(&spi->dev,
+ "setup: invalid chipselect %u (%u defined)\n",
+ spi->chip_select, spi->master->num_chipselect);
+ return -EINVAL;
+ }
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+
+ /* configure SPI controller */
+ reg = SPI_CONFIG_MASTER;
+
+ if (!(spi->mode & SPI_LSB_FIRST))
+ reg |= SPI_CONFIG_BITORDRMSB;
+
+ if (spi->mode & SPI_CPOL) {
+ if (spi->mode & SPI_CPHA)
+ reg |= SPI_CONFIG_MODE3;
+ else
+ reg |= SPI_CONFIG_MODE2;
+ } else if (spi->mode & SPI_CPHA)
+ reg |= SPI_CONFIG_MODE1;
+ else
+ reg |= SPI_CONFIG_MODE0;
+
+ iowrite32(reg, info->ioaddr + SPI_CONFIG);
+
+ ret = spi_ns921x_setupxfer(spi, NULL);
+ if (!ret)
+ pr_debug("Mode %d, %u bpw, %d HZ\n",
+ spi->mode, spi->bits_per_word,
+ spi->max_speed_hz);
+
+ return ret;
+}
+
+static void spi_ns921x_map_xfer(struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS;
+ /* TODO: Check this DMA mapping
+ if (!(xfer->len & (L1_CACHE_BYTES - 1))) {
+ if (xfer->tx_buf && !((unsigned long)xfer->tx_buf &
+ (L1_CACHE_BYTES - 1)))
+ xfer->tx_dma = dma_map_single(&spi->dev, (void *)xfer->tx_buf,
+ xfer->len, DMA_TO_DEVICE);
+ if (xfer->rx_buf && !((unsigned long)xfer->rx_buf &
+ (L1_CACHE_BYTES - 1)))
+ xfer->rx_dma = dma_map_single(&spi->dev, xfer->rx_buf,
+ xfer->len, DMA_FROM_DEVICE);
+ }
+ */
+}
+
+static void spi_ns921x_next_xfer(struct spi_master *master,
+ struct spi_message *msg)
+{
+ struct spi_ns921x *info = spi_master_get_devdata(master);
+ struct spi_transfer *xfer;
+ dma_addr_t tx_dma, rx_dma;
+ u32 len;
+ int err;
+
+ xfer = info->current_transfer;
+ if (!xfer || info->remaining_bytes == 0) {
+ if (xfer)
+ xfer = list_entry(xfer->transfer_list.next,
+ struct spi_transfer, transfer_list);
+ else
+ xfer = list_entry(msg->transfers.next,
+ struct spi_transfer, transfer_list);
+ info->remaining_bytes = xfer->len;
+ info->current_transfer = xfer;
+ }
+
+ len = info->remaining_bytes;
+
+ /* Set the requested configuration for this new SPI-transfer */
+ err = spi_ns921x_setupxfer(msg->spi, xfer);
+ if (err) {
+ pr_err("Setting up the next SPI-transfer\n");
+ list_del(&msg->queue);
+ msg->status = err;
+
+ spin_unlock(&info->lock);
+ msg->complete(msg->context);
+ spin_lock(&info->lock);
+
+ info->current_transfer = NULL;
+ return;
+ }
+
+ tx_dma = xfer->tx_dma;
+ rx_dma = xfer->rx_dma;
+
+ /* Some drivers (like mmc_spi) set rx_dma=0 if they
+ * don't need any return data. If they do this, we
+ * put the unwanted rx data in rx_dma and hope they
+ * didn't ask to send more than BUFFER_SIZE (4096)
+ * bytes. This is at least better than trashing
+ * page 0.
+ *
+ * - mwl
+ */
+ if (!rx_dma || rx_dma == INVALID_DMA_ADDRESS) {
+ rx_dma = info->buf_rx_dma;
+ if (len > BUFFER_SIZE)
+ len = BUFFER_SIZE;
+ }
+ if (tx_dma == INVALID_DMA_ADDRESS) {
+ if (xfer->tx_buf) {
+ tx_dma = info->buf_tx_dma;
+ if (len > BUFFER_SIZE)
+ len = BUFFER_SIZE;
+ memcpy(info->buf_tx, xfer->tx_buf +
+ xfer->len - info->remaining_bytes, len);
+ dma_sync_single_for_device(&master->dev,
+ info->buf_tx_dma, len, DMA_TO_DEVICE);
+ } else {
+ /* Send undefined data; rx_dma is handy */
+ tx_dma = rx_dma;
+ }
+ }
+
+ /* The IO hub DMA controller doesn't cope well with
+ * non-aligned rx buffers (and possibly tx too). On
+ * rx, at least, it causes the last byte read by the
+ * peripheral on the *previous* transfer to be
+ * delivered again if the current transfer is
+ * unaligned.
+ *
+ * For now, just warn about it so someone can fix
+ * the offending caller when it occurs. It might
+ * not be a problem on tx.
+ *
+ * - mwl
+ */
+ if (rx_dma & 0x3) {
+ dev_err(&master->dev, "unaligned rx_dma (=0x%08x, len=%u)\n",
+ rx_dma, len);
+ }
+ if (tx_dma & 0x3) {
+ dev_err(&master->dev, "unaligned tx_dma (=0x%08x, len=%u)\n",
+ tx_dma, len);
+ }
+
+ /* Setup the DMA channels */
+ /* reset dma controller */
+ iowrite32(0, info->ioaddr + DMA_RXCTL);
+ iowrite32(0, info->ioaddr + DMA_TXCTL);
+ /* set DMA descriptor */
+ memset(info->desc_tx, 0, sizeof(*info->desc_tx));
+ info->desc_tx->source = (u32)(tx_dma);
+ info->desc_tx->len = len;
+ info->desc_tx->flags =
+ DMADESC_WRAP | DMADESC_INTR | DMATXDESC_LAST | DMADESC_FULL;
+
+ memset(info->desc_rx, 0, sizeof(*info->desc_rx));
+ info->desc_rx->source = (u32)(rx_dma);
+ info->desc_rx->len = len;
+ info->desc_rx->flags = DMADESC_WRAP;
+
+ info->dma_xfer_len = len;
+
+ /* configure dma channels */
+ iowrite32( DMA_IRQCFG_NCIE | DMA_IRQCFG_ECIE |
+ DMA_IRQCFG_NRIE | DMA_IRQCFG_CAIE | DMA_IRQCFG_PCIE,
+ info->ioaddr + DMA_RXIRQCFG );
+ iowrite32((u32)info->desc_tx_dma, info->ioaddr + DMA_TXBUF);
+ iowrite32( DMA_IRQCFG_NCIE | DMA_IRQCFG_ECIE |
+ DMA_IRQCFG_NRIE | DMA_IRQCFG_CAIE,
+ info->ioaddr + DMA_TXIRQCFG );
+ iowrite32((u32)info->desc_rx_dma, info->ioaddr + DMA_RXBUF);
+
+ /* fire up dma channels */
+ iowrite32(DMA_ENABLECHANNEL, info->ioaddr + DMA_RXCTL);
+ iowrite32(DMA_ENABLECHANNEL, info->ioaddr + DMA_TXCTL);
+}
+
+static void spi_ns921x_next_message(struct spi_master *master)
+{
+ struct spi_ns921x *info = spi_master_get_devdata(master);
+ struct spi_message *msg;
+
+ BUG_ON(info->current_transfer);
+
+ msg = list_entry(info->queue.next, struct spi_message, queue);
+
+ spi_ns921x_chipsel(msg->spi, SPI_CS_ACTIVE);
+ spi_ns921x_next_xfer(master, msg);
+}
+
+static int spi_ns921x_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct spi_ns921x *info = spi_master_get_devdata(spi->master);
+ struct spi_transfer *xfer;
+ unsigned long flags;
+ if (unlikely(list_empty(&msg->transfers)
+ || !spi->max_speed_hz))
+ return -EINVAL;
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ if (!(xfer->tx_buf || xfer->rx_buf)) {
+ dev_dbg(&spi->dev, "missing rx or tx buf\n");
+ return -EINVAL;
+ }
+ }
+
+ /* scrub dcache "early" */
+ if (!msg->is_dma_mapped) {
+ list_for_each_entry(xfer, &msg->transfers, transfer_list)
+ spi_ns921x_map_xfer(spi, xfer);
+ }
+
+ msg->status = -EINPROGRESS;
+ msg->actual_length = 0;
+
+ spin_lock_irqsave(&info->lock, flags);
+ list_add_tail(&msg->queue, &info->queue);
+ if (!info->current_transfer)
+ spi_ns921x_next_message(spi->master);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ return 0;
+}
+
+static void spi_ns921x_irq_trigger_next(struct spi_master *master)
+{
+ struct spi_ns921x *info = spi_master_get_devdata(master);
+ struct spi_message *msg;
+ struct spi_transfer *xfer;
+ u32 len, ctrl;
+
+ BUG_ON((info->flags & (SPI_TX_IDONE|SPI_RX_IDONE)) != (SPI_TX_IDONE|SPI_RX_IDONE));
+
+ info->flags &= ~(SPI_TX_IDONE|SPI_RX_IDONE);
+ xfer = info->current_transfer;
+ msg = list_entry(info->queue.next, struct spi_message, queue);
+
+ len = info->dma_xfer_len;
+ if (len > BUFFER_SIZE)
+ len = BUFFER_SIZE;
+ /*
+ * If the rx buffer wasn't aligned, we used a bounce
+ * buffer for the transfer. Copy the data back and
+ * make the bounce buffer ready for re-use.
+ */
+ if (xfer->rx_buf && xfer->rx_dma == INVALID_DMA_ADDRESS) {
+ dma_sync_single_for_cpu(&master->dev, info->buf_rx_dma,
+ len, DMA_FROM_DEVICE);
+ memcpy(xfer->rx_buf, info->buf_rx + xfer->len -
+ info->remaining_bytes, len);
+ }
+
+ info->remaining_bytes -= len;
+
+ if (info->remaining_bytes == 0) {
+ msg->actual_length += xfer->len;
+
+ if (!msg->is_dma_mapped) {
+ if (xfer->tx_dma != INVALID_DMA_ADDRESS)
+ dma_unmap_single(&master->dev,
+ xfer->tx_dma,
+ xfer->len,
+ DMA_TO_DEVICE);
+ if (xfer->rx_dma != INVALID_DMA_ADDRESS)
+ dma_unmap_single(&master->dev,
+ xfer->rx_dma,
+ xfer->len,
+ DMA_FROM_DEVICE);
+ }
+
+ /* REVISIT: udelay in irq is unfriendly */
+ if (xfer->delay_usecs)
+ udelay(xfer->delay_usecs);
+
+ if (msg->transfers.prev == &xfer->transfer_list) {
+
+ /* Message complete */
+ spi_ns921x_chipsel(msg->spi, SPI_CS_INACTIVE);
+
+ list_del(&msg->queue);
+ msg->status = 0;
+
+ spin_unlock(&info->lock);
+ msg->complete(msg->context);
+ spin_lock(&info->lock);
+
+ info->current_transfer = NULL;
+
+ /* continue; complete() may have queued requests */
+ if (list_empty(&info->queue)) {
+ ctrl = ioread32(info->ioaddr +
+ DMA_RXCTL);
+ ctrl &= ~DMA_ENABLECHANNEL;
+ iowrite32(ctrl, info->ioaddr +
+ DMA_RXCTL);
+ ctrl = ioread32(info->ioaddr +
+ DMA_TXCTL);
+ ctrl &= ~DMA_ENABLECHANNEL;
+ iowrite32(ctrl, info->ioaddr +
+ DMA_TXCTL);
+ } else
+ spi_ns921x_next_message(master);
+ } else {
+ if (xfer->cs_change) {
+ spi_ns921x_chipsel(msg->spi, SPI_CS_INACTIVE);
+ udelay(1);
+ spi_ns921x_chipsel(msg->spi, SPI_CS_ACTIVE);
+ }
+
+ spi_ns921x_next_xfer(master, msg);
+ }
+ } else {
+ /* There are still pending data in the current transfer */
+ spi_ns921x_next_xfer(master, msg);
+ }
+}
+
+static irqreturn_t spi_ns921x_irq(int irq, void *dev_id)
+{
+ struct spi_master *master = dev_id;
+ struct spi_ns921x *info = spi_master_get_devdata(master);
+ u32 status, dlen, rx_status;
+
+ spin_lock(&info->lock);
+
+ /* get status and mask all pending interrupts */
+ status = ioread32(info->ioaddr + DMA_IRQSTATUS);
+ iowrite32(status, info->ioaddr + DMA_IRQSTATUS);
+
+ /* TX interrupt error conditions */
+ if (status & (DMA_IRQSTAT_TXECIP | DMA_IRQSTAT_TXCAIP )) {
+ dev_err(&master->dev, "tx dma failure! status = 0x%08x\n",
+ status);
+ /* retrigger the xfer */
+ spi_ns921x_next_xfer(master, list_entry(info->queue.next,
+ struct spi_message, queue));
+ }
+ /* RX interrupt error conditions */
+ rx_status = ioread32(info->ioaddr + DMA_RXIRQCFG);
+ dlen = rx_status & DMA_IRQCFG_BLENSTAT;
+
+ if (info->dma_xfer_len != dlen)
+ /* ?? what to do... ?? */
+ dev_err(&master->dev, "incomplete dma xfer"
+ "(%d/%d bytes transfered) \n",
+ dlen, info->dma_xfer_len);
+
+ if (status & (DMA_IRQSTAT_RXECIP | DMA_IRQSTAT_RXCAIP |
+ DMA_IRQSTAT_RXPCIP)) {
+ dev_err(&master->dev, "rx dma failure! status = 0x%08x\n",
+ status);
+
+ /* retrigger the xfer */
+ spi_ns921x_next_xfer(master, list_entry(info->queue.next,
+ struct spi_message, queue));
+ }
+ /* TX normal completion */
+ if (status & DMA_IRQSTAT_TXNCIP) {
+ info->flags |= SPI_TX_IDONE;
+ if (info->flags & SPI_RX_IDONE)
+ spi_ns921x_irq_trigger_next(master);
+ }
+ /* RX normal completion */
+ if (status & DMA_IRQSTAT_RXNCIP) {
+ info->flags |= SPI_RX_IDONE;
+ if (info->flags & SPI_TX_IDONE)
+ spi_ns921x_irq_trigger_next(master);
+ }
+ spin_unlock(&info->lock);
+
+ return IRQ_HANDLED;
+}
+
+static __devinit int spi_ns921x_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct spi_ns921x *info;
+ int ret;
+ int gpio_cs, gpio_cs_func;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*info));
+ if (!master) {
+ pr_debug(DRIVER_NAME ": cannot allocate spi master\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, master);
+
+ info = spi_master_get_devdata(master);
+ info->pdata = pdev->dev.platform_data;
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ pr_debug(DRIVER_NAME ": no interrupt available\n");
+ ret = -ENOENT;
+ goto err_res;
+ }
+
+ info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!info->mem) {
+ pr_debug(DRIVER_NAME ": memory not available\n");
+ ret = -ENOENT;
+ goto err_res;
+ }
+
+ if (!request_mem_region(info->mem->start,
+ info->mem->end - info->mem->start + 1,
+ DRIVER_NAME)) {
+ pr_debug(DRIVER_NAME ": memory already mapped\n");
+ ret = -EIO;
+ goto err_res;
+ }
+
+ info->ioaddr = ioremap(info->mem->start,
+ info->mem->end - info->mem->start + 1);
+ if (!info->ioaddr) {
+ pr_debug(DRIVER_NAME ": unable to remap IO memory\n");
+ ret = -EIO;
+ goto err_remap;
+ }
+
+ master->bus_num = pdev->id;
+ /* hardware controlled cs */
+ master->num_chipselect = 1;
+
+ /* If CS is GPIO controlled, configure it as output */
+ gpio_cs = info->pdata->gpios[SPI_EN_GPIO_OFFSET];
+ gpio_cs_func = info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET];
+ if( NS921X_GPIO_FUNC_GPIO == gpio_cs_func )
+ gpio_direction_output( gpio_cs,
+ gpio_get_value( gpio_cs ) );
+
+ master->setup = spi_ns921x_setup;
+ master->transfer = spi_ns921x_transfer;
+
+ /* prepare waitq and spinlock */
+ spin_lock_init(&info->lock);
+ INIT_LIST_HEAD(&info->queue);
+ init_waitqueue_head(&info->waitq);
+
+ /* register and map memory for dma-descriptors */
+ info->buf_tx = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
+ &info->buf_tx_dma, GFP_KERNEL);
+ if (!info->buf_tx) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc_buf_tx\n", __func__);
+ goto err_alloc_buf_tx;
+ }
+
+ info->buf_rx = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
+ &info->buf_rx_dma, GFP_KERNEL);
+ if (!info->buf_rx) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc_buf_rx\n", __func__);
+ goto err_alloc_buf_rx;
+ }
+
+ info->desc_tx = dma_alloc_coherent(&pdev->dev,
+ sizeof(info->desc_tx), &info->desc_tx_dma, GFP_DMA);
+ if (!info->desc_tx) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc_desc_tx\n", __func__);
+ goto err_alloc_desc_tx;
+ }
+
+ info->desc_rx = dma_alloc_coherent(&pdev->dev,
+ sizeof(info->desc_rx), &info->desc_rx_dma, GFP_DMA);
+ if (!info->desc_rx) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc_desc_rx\n", __func__);
+ goto err_alloc_desc_rx;
+ }
+
+ info->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(info->clk)) {
+ pr_debug(DRIVER_NAME ": no clock available\n");
+ ret = PTR_ERR(info->clk);
+ goto err_clk;
+ }
+
+ ret = clk_enable(info->clk);
+ if (ret) {
+ pr_debug(DRIVER_NAME ": cannot enable clock\n");
+ goto err_clk;
+ }
+
+ /* reset dma controller */
+ iowrite32(0, info->ioaddr + DMA_RXCTL);
+ iowrite32(0, info->ioaddr + DMA_TXCTL);
+
+ /* register interrupt service routine */
+ ret = request_irq(info->irq, spi_ns921x_irq, 0,
+ pdev->name, master);
+ if (ret) {
+ pr_debug(DRIVER_NAME ": cannot register SPI interrupt\n");
+ goto err_irq;
+ }
+
+ dev_info(&pdev->dev, "NS921x SPI controller at 0x%p (irq: %d)\n",
+ info->ioaddr, info->irq );
+
+ ret = spi_register_master(master);
+ if (ret) {
+ pr_debug(DRIVER_NAME ": cannot register spi master\n");
+ goto err_reg;
+ }
+
+ return 0;
+
+err_reg:
+ //destroy_workqueue(info->workqueue);
+//err_workqueue:
+ free_irq(info->irq, info);
+err_irq:
+ clk_disable(info->clk);
+err_clk:
+ dma_free_coherent(&pdev->dev, sizeof(info->desc_rx),
+ info->desc_rx, info->desc_rx_dma);
+err_alloc_desc_rx:
+ dma_free_coherent(&pdev->dev, sizeof(info->desc_tx),
+ info->desc_tx, info->desc_tx_dma);
+err_alloc_desc_tx:
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE,
+ info->buf_rx, info->buf_rx_dma);
+err_alloc_buf_rx:
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE,
+ info->buf_tx, info->buf_tx_dma);
+err_alloc_buf_tx:
+ iounmap(info->ioaddr);
+
+err_remap:
+ release_mem_region(info->mem->start,
+ info->mem->end - info->mem->start + 1);
+err_res:
+ spi_master_put(master);
+
+ return ret;
+}
+
+static __devexit int spi_ns921x_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+ struct spi_ns921x *info = spi_master_get_devdata(master);
+
+ spi_unregister_master(master);
+// destroy_workqueue(info->workqueue);
+ free_irq(info->irq, info);
+ clk_disable(info->clk);
+ dma_free_coherent(&pdev->dev, sizeof(info->desc_rx),
+ info->desc_rx, info->desc_rx_dma);
+ dma_free_coherent(&pdev->dev, sizeof(info->desc_tx),
+ info->desc_tx, info->desc_tx_dma);
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE,
+ info->buf_rx, info->buf_rx_dma);
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE,
+ info->buf_tx, info->buf_tx_dma);
+ iounmap(info->ioaddr);
+ release_mem_region(info->mem->start,
+ info->mem->end - info->mem->start + 1);
+ spi_master_put(master);
+
+ return 0;
+}
+
+static struct platform_driver spi_ns9xxx_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = spi_ns921x_probe,
+ .remove = __devexit_p(spi_ns921x_remove),
+};
+
+static __init int spi_ns921x_init(void)
+{
+ return platform_driver_register(&spi_ns9xxx_driver);
+}
+
+static __exit void spi_ns921x_exit(void)
+{
+ platform_driver_unregister(&spi_ns9xxx_driver);
+}
+
+module_init(spi_ns921x_init);
+module_exit(spi_ns921x_exit);
+
+MODULE_AUTHOR("Digi International Inc.");
+MODULE_DESCRIPTION("Digi ns921x SPI Controller driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/spi/spi_ns9360.c b/drivers/spi/spi_ns9360.c
new file mode 100644
index 000000000000..8dde47409f3a
--- /dev/null
+++ b/drivers/spi/spi_ns9360.c
@@ -0,0 +1,890 @@
+/*
+ * linux/drivers/spi/spi_ns9360.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spinlock.h>
+
+#include <asm/dma.h>
+#include <mach/gpio.h>
+#include <mach/spi.h>
+
+#define DRIVER_NAME "spi_ns9360"
+
+union ns9xxx_dma_desc {
+ struct {
+ u32 source;
+ u16 len;
+ u16 reserved1;
+ u32 reserved2;
+
+ u16 status;
+#define DMADESC_WRAP (1 << 15)
+#define DMADESC_INTR (1 << 14)
+#define DMATXDESC_LAST (1 << 13)
+#define DMADESC_FULL (1 << 12)
+ u16 flags;
+ };
+ u32 data[4];
+};
+
+struct spi_ns9360 {
+ void __iomem *ioaddr;
+ void __iomem *ioaddr_bbus;
+ struct resource *mem;
+ struct resource *mem_bbus;
+ struct clk *clk;
+ struct spi_ns9xxx_data *pdata;
+ unsigned irq_tx;
+ unsigned irq_rx;
+
+ spinlock_t lock;
+ struct list_head queue;
+ struct spi_transfer *current_transfer;
+ unsigned long remaining_bytes;
+ unsigned dma_xfer_len;
+ u32 flags;
+
+ void *buf_tx;
+ void *buf_rx;
+ dma_addr_t buf_tx_dma;
+ dma_addr_t buf_rx_dma;
+ union ns9xxx_dma_desc *desc_tx;
+ union ns9xxx_dma_desc *desc_rx;
+ dma_addr_t desc_tx_dma;
+ dma_addr_t desc_rx_dma;
+};
+
+#define BUFFER_SIZE PAGE_SIZE
+#define INVALID_DMA_ADDRESS 0xffffffff
+#define SPI_RX_IDONE 0x00000001
+#define SPI_TX_IDONE 0x00000002
+#define SPI_DEFAULT_BPS 1000000
+#define SPI_CS_ACTIVE 1
+#define SPI_CS_INACTIVE 0
+
+#define BBUS_DMA_RXBUFP 0x00
+#define BBUS_DMA_TXBUFP 0x20
+#define BBUS_DMA_RXCTRL 0x10
+#define BBUS_DMA_TXCTRL 0x30
+#define BBUS_DMA_CTRL_CE (1 << 31)
+#define BBUS_DMA_CTRL_MODE_FBR (0x01 << 26)
+#define BBUS_DMA_CTRL_MODE_FBW (0x00 << 26)
+#define BBUS_DMA_CTRL_RESET (1 << 18)
+#define BBUS_DMA_CTRL_BTE1 0
+
+#define BBUS_DMA_RXSTAT 0x14
+#define BBUS_DMA_TXSTAT 0x34
+#define BBUS_DMA_STAT_NCIP (1 << 31)
+#define BBUS_DMA_STAT_ECIP (1 << 30)
+#define BBUS_DMA_STAT_CAIP (1 << 28)
+#define BBUS_DMA_STAT_PCIP (1 << 27)
+#define BBUS_DMA_STAT_NCIE (1 << 24)
+#define BBUS_DMA_STAT_ECIE (1 << 23)
+#define BBUS_DMA_STAT_NRIE (1 << 22)
+#define BBUS_DMA_STAT_CAIE (1 << 21)
+#define BBUS_DMA_STAT_PCIE (1 << 20)
+#define BBUS_DMA_STAT_BLEN_MA (0x0000FFFF)
+
+#define SPI_CTRLA 0x00
+#define SPI_CTRLA_CE (1 << 31)
+#define SPI_CTRLA_WLS8 (3 << 24)
+#define SPI_CTRLA_ERXDMA (1 << 8)
+#define SPI_CTRLA_ETXDMA (1 << 0)
+
+#define SPI_CTRLB 0x04
+#define SPI_CTRLB_MODE_SPIM (1 << 21)
+#define SPI_CTRLB_BITORDR (1 << 19)
+#define SPI_BITRATE 0x0C
+#define SPI_BITRATE_EBIT (1 << 31)
+#define SPI_BITRATE_TMODE (1 << 30)
+#define SPI_BITRATE_CLKMUX (0x01 << 24)
+#define SPI_BITRATE_TXCINV (1 << 23)
+#define SPI_BITRATE_RXCINV (1 << 22)
+#define SPI_BITRATE_SPCPOL_LOW (1 << 21)
+#define SER_BITRATE_N_MA (0x00007FFF)
+
+#define SPI_DMA_TX_STAT_OPT \
+ (BBUS_DMA_STAT_NCIE | BBUS_DMA_STAT_ECIE | BBUS_DMA_STAT_NRIE | \
+ BBUS_DMA_STAT_CAIE | BBUS_DMA_STAT_PCIE)
+#define SPI_DMA_RX_STAT_OPT \
+ (BBUS_DMA_STAT_NCIE | BBUS_DMA_STAT_ECIE | BBUS_DMA_STAT_NRIE | \
+ BBUS_DMA_STAT_CAIE | BBUS_DMA_STAT_PCIE)
+#define SPI_DMA_TX_CTRL_OPT \
+ (BBUS_DMA_CTRL_MODE_FBR | BBUS_DMA_CTRL_BTE1 | BBUS_DMA_CTRL_CE)
+#define SPI_DMA_RX_CTRL_OPT \
+ (BBUS_DMA_CTRL_MODE_FBW | BBUS_DMA_CTRL_BTE1 | BBUS_DMA_CTRL_CE)
+
+static void spi_ns9360_chipsel(struct spi_device *spi, int active)
+{
+unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
+void (* func)(struct spi_device *spi, int active) = spi->controller_data;
+struct spi_ns9360 *info = spi_master_get_devdata(spi->master);
+
+ /* use special function to toggle CS? */
+ if( func )
+ {
+ /* Reconfigure CS line as GPIO input */
+ if( SPI_CS_ACTIVE == active )
+ gpio_configure_ns9360(info->pdata->gpios[SPI_EN_GPIO_OFFSET],
+ NS9360_GPIO_INPUT,
+ NS9360_GPIO_DONT_INVERT,
+ NS9360_GPIO_FUNC_3);
+
+ /* Call the special function */
+ func( spi, cspol == active);
+
+ /* Reconfigure CS line as SPI_EN */
+ if( SPI_CS_INACTIVE == active )
+ gpio_configure_ns9360(info->pdata->gpios[SPI_EN_GPIO_OFFSET],
+ 0,
+ 0,
+ info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET]);
+ }
+ else {
+ /* Use a GPIO to toggle CS? */
+ if( info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET] == NS9360_GPIO_FUNC_GPIO )
+ gpio_set_value( info->pdata->gpios[SPI_EN_GPIO_OFFSET], cspol == active );
+ /* otherwise CS will be automatically controlled by the spi engine */
+ }
+}
+
+/*
+ * Configure the controller for the passed SPI-device. Additionally it will
+ * use the configuration values of the SPI-transfer if it's different than NULL
+ */
+static int spi_ns9360_setupxfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ struct spi_ns9360 *info = spi_master_get_devdata(spi->master);
+ unsigned int bpw;
+ unsigned int hz;
+ u32 brate;
+ u32 ctrl;
+
+ pr_debug("Setup for next xfer called\n");
+
+ /* If the bits per word is zero, then use the default value of the SPI-device */
+ bpw = t ? t->bits_per_word : spi->bits_per_word;
+ if (!bpw)
+ bpw = spi->bits_per_word;
+
+ /* We only support eight bits per word */
+ if (bpw != 8) {
+ pr_err("Invalid bits-per-word (%d)\n", bpw);
+ return -EINVAL;
+ }
+
+ /*
+ * If the clock rate of the xfer is zero, then use the already configured
+ * rate of this SPI-device
+ */
+ if(!spi->max_speed_hz)
+ spi->max_speed_hz = SPI_DEFAULT_BPS;
+ hz = t ? t->speed_hz : spi->max_speed_hz;
+ if (!hz)
+ hz = spi->max_speed_hz;
+
+ if( hz > (clk_get_rate(info->clk) / 4) ) {
+ pr_err("Invalid speed (%d)\n", hz );
+ return -EINVAL;
+ }
+ /* reset previous configuration */
+ iowrite32(0x00, info->ioaddr + SPI_CTRLA);
+
+ /* configure bitrate register */
+ brate = clk_get_rate(info->clk) / hz / 2 - 1;
+ if ((brate & SER_BITRATE_N_MA) != brate) {
+ dev_err(&spi->dev, "invalid bitrate %d\n", hz);
+ return -EINVAL;
+ }
+
+ brate |= SPI_BITRATE_EBIT | SPI_BITRATE_CLKMUX | SPI_BITRATE_TMODE;
+
+ if ((spi->mode & SPI_MODE_3) == SPI_MODE_0)
+ brate |= SPI_BITRATE_SPCPOL_LOW;
+ else if ((spi->mode & SPI_MODE_3) == SPI_MODE_1)
+ brate |= SPI_BITRATE_TXCINV | SPI_BITRATE_RXCINV;
+ else if ((spi->mode & SPI_MODE_3) == SPI_MODE_2)
+ brate |= SPI_BITRATE_SPCPOL_LOW | SPI_BITRATE_TXCINV |
+ SPI_BITRATE_RXCINV;
+
+ iowrite32(brate, info->ioaddr + SPI_BITRATE);
+
+ /* configure bitorder and master operation */
+ ctrl = SPI_CTRLB_MODE_SPIM;
+
+ if (!(spi->mode & SPI_LSB_FIRST))
+ ctrl |= SPI_CTRLB_BITORDR;
+
+ iowrite32(ctrl, info->ioaddr + SPI_CTRLB);
+
+ /* enable this channel */
+ ctrl = SPI_CTRLA_CE | SPI_CTRLA_WLS8;
+ iowrite32(ctrl, info->ioaddr + SPI_CTRLA);
+
+ return 0;
+}
+
+static int spi_ns9360_setup(struct spi_device *spi)
+{
+ struct spi_ns9360 *info = spi_master_get_devdata(spi->master);
+ int ret;
+
+ if (spi->chip_select > spi->master->num_chipselect) {
+ dev_dbg(&spi->dev,
+ "setup: invalid chipselect %u (%u defined)\n",
+ spi->chip_select, spi->master->num_chipselect);
+ return -EINVAL;
+ }
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+
+ if ((spi->mode & SPI_CS_HIGH) && !info->pdata) {
+ /* FIXME: switch inverter on CS-pin */
+ }
+
+ /* Configure per-transfer settings (speed/bpw/mode) */
+ ret = spi_ns9360_setupxfer(spi, NULL);
+
+ return ret;
+}
+
+static void spi_ns9360_map_xfer(struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS;
+ /* TODO: Check this DMA mapping
+ if (!(xfer->len & (L1_CACHE_BYTES - 1))) {
+ if (xfer->tx_buf && !((unsigned long)xfer->tx_buf &
+ (L1_CACHE_BYTES - 1)))
+ xfer->tx_dma = dma_map_single(&spi->dev, (void *)xfer->tx_buf,
+ xfer->len, DMA_TO_DEVICE);
+ if (xfer->rx_buf && !((unsigned long)xfer->rx_buf &
+ (L1_CACHE_BYTES - 1)))
+ xfer->rx_dma = dma_map_single(&spi->dev, xfer->rx_buf,
+ xfer->len, DMA_FROM_DEVICE);
+ }
+ */
+}
+
+static void spi_ns9360_next_xfer(struct spi_master *master,
+ struct spi_message *msg)
+{
+ struct spi_ns9360 *info = spi_master_get_devdata(master);
+ struct spi_transfer *xfer;
+ dma_addr_t tx_dma, rx_dma;
+ u32 len, ctrl;
+ int err;
+
+ xfer = info->current_transfer;
+ if (!xfer || info->remaining_bytes == 0) {
+ if (xfer)
+ xfer = list_entry(xfer->transfer_list.next,
+ struct spi_transfer, transfer_list);
+ else
+ xfer = list_entry(msg->transfers.next,
+ struct spi_transfer, transfer_list);
+ info->remaining_bytes = xfer->len;
+ info->current_transfer = xfer;
+ }
+
+ len = info->remaining_bytes;
+
+ /* Set the requested configuration for this new SPI-transfer */
+ err = spi_ns9360_setupxfer(msg->spi, xfer);
+ if (err) {
+ pr_err("Setting up the next SPI-transfer\n");
+ list_del(&msg->queue);
+ msg->status = err;
+
+ spin_unlock(&info->lock);
+ msg->complete(msg->context);
+ spin_lock(&info->lock);
+
+ info->current_transfer = NULL;
+ return;
+ }
+
+ tx_dma = xfer->tx_dma;
+ rx_dma = xfer->rx_dma;
+
+ /* Some drivers (like mmc_spi) set rx_dma=0 if they
+ * don't need any return data. If they do this, we
+ * put the unwanted rx data in rx_dma and hope they
+ * didn't ask to send more than BUFFER_SIZE (4096)
+ * bytes. This is at least better than trashing
+ * page 0.
+ *
+ * - mwl
+ */
+ if (!rx_dma || rx_dma == INVALID_DMA_ADDRESS) {
+ rx_dma = info->buf_rx_dma;
+ if (len > BUFFER_SIZE)
+ len = BUFFER_SIZE;
+ }
+ if (tx_dma == INVALID_DMA_ADDRESS) {
+ if (xfer->tx_buf) {
+ tx_dma = info->buf_tx_dma;
+ if (len > BUFFER_SIZE)
+ len = BUFFER_SIZE;
+ memcpy(info->buf_tx, xfer->tx_buf +
+ xfer->len - info->remaining_bytes, len);
+ dma_sync_single_for_device(&master->dev,
+ info->buf_tx_dma, len, DMA_TO_DEVICE);
+ } else {
+ /* Send undefined data; rx_dma is handy */
+ tx_dma = rx_dma;
+ }
+ }
+
+ /* The IO hub DMA controller doesn't cope well with
+ * non-aligned rx buffers (and possibly tx too). On
+ * rx, at least, it causes the last byte read by the
+ * peripheral on the *previous* transfer to be
+ * delivered again if the current transfer is
+ * unaligned.
+ *
+ * For now, just warn about it so someone can fix
+ * the offending caller when it occurs. It might
+ * not be a problem on tx.
+ *
+ * - mwl
+ */
+ if (rx_dma & 0x3) {
+ dev_err(&master->dev, "unaligned rx_dma (=0x%08x, len=%u)\n",
+ rx_dma, len);
+ }
+ if (tx_dma & 0x3) {
+ dev_err(&master->dev, "unaligned tx_dma (=0x%08x, len=%u)\n",
+ tx_dma, len);
+ }
+
+ /* setup the DMA channels */
+ iowrite32(BBUS_DMA_CTRL_RESET, info->ioaddr_bbus + BBUS_DMA_RXCTRL);
+ iowrite32(BBUS_DMA_CTRL_RESET, info->ioaddr_bbus + BBUS_DMA_TXCTRL);
+
+ memset(info->desc_tx, 0, sizeof(*info->desc_tx));
+ info->desc_tx->source = (u32)(tx_dma);
+ info->desc_tx->len = len;
+ info->desc_tx->flags =
+ DMADESC_WRAP | DMADESC_INTR | DMATXDESC_LAST | DMADESC_FULL;
+
+ memset(info->desc_rx, 0, sizeof(*info->desc_rx));
+ info->desc_rx->source = (u32)(rx_dma);
+ info->desc_rx->len = len;
+ info->desc_rx->flags = DMADESC_WRAP;
+
+ info->dma_xfer_len = len;
+
+ /* configure dma channels */
+ iowrite32(SPI_DMA_TX_STAT_OPT, info->ioaddr_bbus + BBUS_DMA_TXSTAT);
+ iowrite32(SPI_DMA_RX_STAT_OPT, info->ioaddr_bbus + BBUS_DMA_RXSTAT);
+ iowrite32((u32)info->desc_tx_dma, info->ioaddr_bbus + BBUS_DMA_TXBUFP);
+ iowrite32((u32)info->desc_rx_dma, info->ioaddr_bbus + BBUS_DMA_RXBUFP);
+ iowrite32(SPI_DMA_TX_CTRL_OPT, info->ioaddr_bbus + BBUS_DMA_TXCTRL);
+ iowrite32(SPI_DMA_RX_CTRL_OPT, info->ioaddr_bbus + BBUS_DMA_RXCTRL);
+
+ /* fire up dma channels */
+ ctrl = ioread32(info->ioaddr + SPI_CTRLA);
+ ctrl |= SPI_CTRLA_ETXDMA | SPI_CTRLA_ERXDMA;
+ iowrite32(ctrl, info->ioaddr + SPI_CTRLA);
+}
+
+static void spi_ns9360_next_message(struct spi_master *master)
+{
+ struct spi_ns9360 *info = spi_master_get_devdata(master);
+ struct spi_message *msg;
+
+ BUG_ON(info->current_transfer);
+
+ msg = list_entry(info->queue.next, struct spi_message, queue);
+
+ spi_ns9360_chipsel(msg->spi, SPI_CS_ACTIVE);
+ spi_ns9360_next_xfer(master, msg);
+}
+
+static int spi_ns9360_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct spi_ns9360 *info = spi_master_get_devdata(spi->master);
+ struct spi_transfer *xfer;
+ unsigned long flags;
+
+ if (unlikely(list_empty(&msg->transfers)
+ || !spi->max_speed_hz))
+ return -EINVAL;
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ if (!(xfer->tx_buf || xfer->rx_buf)) {
+ dev_dbg(&spi->dev, "missing rx or tx buf\n");
+ return -EINVAL;
+ }
+ }
+
+ /* scrub dcache "early" */
+ if (!msg->is_dma_mapped) {
+ list_for_each_entry(xfer, &msg->transfers, transfer_list)
+ spi_ns9360_map_xfer(spi, xfer);
+ }
+
+ msg->status = -EINPROGRESS;
+ msg->actual_length = 0;
+
+ spin_lock_irqsave(&info->lock, flags);
+ list_add_tail(&msg->queue, &info->queue);
+ if (!info->current_transfer)
+ spi_ns9360_next_message(spi->master);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ return 0;
+}
+
+static void spi_ns9360_irq_trigger_next(struct spi_master *master)
+{
+ struct spi_ns9360 *info = spi_master_get_devdata(master);
+ struct spi_message *msg;
+ struct spi_transfer *xfer;
+ u32 len, ctrl;
+
+ BUG_ON((info->flags & (SPI_TX_IDONE|SPI_RX_IDONE)) != (SPI_TX_IDONE|SPI_RX_IDONE));
+
+ info->flags &= ~(SPI_TX_IDONE|SPI_RX_IDONE);
+ xfer = info->current_transfer;
+ msg = list_entry(info->queue.next, struct spi_message, queue);
+
+ len = info->dma_xfer_len;
+ if (len > BUFFER_SIZE)
+ len = BUFFER_SIZE;
+
+ /*
+ * If the rx buffer wasn't aligned, we used a bounce
+ * buffer for the transfer. Copy the data back and
+ * make the bounce buffer ready for re-use.
+ */
+ if (xfer->rx_buf && xfer->rx_dma == INVALID_DMA_ADDRESS) {
+ dma_sync_single_for_cpu(&master->dev, info->buf_rx_dma,
+ len, DMA_FROM_DEVICE);
+ memcpy(xfer->rx_buf, info->buf_rx + xfer->len -
+ info->remaining_bytes, len);
+ }
+
+ info->remaining_bytes -= len;
+
+ if (info->remaining_bytes == 0) {
+ msg->actual_length += xfer->len;
+
+ if (!msg->is_dma_mapped) {
+ if (xfer->tx_dma != INVALID_DMA_ADDRESS)
+ dma_unmap_single(&master->dev,
+ xfer->tx_dma,
+ xfer->len,
+ DMA_TO_DEVICE);
+ if (xfer->rx_dma != INVALID_DMA_ADDRESS)
+ dma_unmap_single(&master->dev,
+ xfer->rx_dma,
+ xfer->len,
+ DMA_FROM_DEVICE);
+ }
+
+ /* REVISIT: udelay in irq is unfriendly */
+ if (xfer->delay_usecs)
+ udelay(xfer->delay_usecs);
+
+ if (msg->transfers.prev == &xfer->transfer_list) {
+
+ /* Message complete */
+ spi_ns9360_chipsel(msg->spi, SPI_CS_INACTIVE);
+
+ list_del(&msg->queue);
+ msg->status = 0;
+
+ spin_unlock(&info->lock);
+ msg->complete(msg->context);
+ spin_lock(&info->lock);
+
+ info->current_transfer = NULL;
+
+ /* continue; complete() may have queued requests */
+ if (list_empty(&info->queue)) {
+ ctrl = ioread32(info->ioaddr_bbus +
+ BBUS_DMA_RXCTRL);
+ ctrl &= ~BBUS_DMA_CTRL_CE;
+ iowrite32(ctrl, info->ioaddr_bbus +
+ BBUS_DMA_RXCTRL);
+ ctrl = ioread32(info->ioaddr_bbus +
+ BBUS_DMA_TXCTRL);
+ ctrl &= ~BBUS_DMA_CTRL_CE;
+ iowrite32(ctrl, info->ioaddr_bbus +
+ BBUS_DMA_TXCTRL);
+ } else
+ spi_ns9360_next_message(master);
+ } else {
+ if (xfer->cs_change) {
+ spi_ns9360_chipsel(msg->spi, SPI_CS_INACTIVE);
+ udelay(1);
+ spi_ns9360_chipsel(msg->spi, SPI_CS_ACTIVE);
+ }
+
+ spi_ns9360_next_xfer(master, msg);
+ }
+ } else {
+ /* There are still pending data in the current transfer */
+ spi_ns9360_next_xfer(master, msg);
+ }
+}
+
+static irqreturn_t spi_ns9360_irq_tx(int irq, void *dev_id)
+{
+ struct spi_master *master = dev_id;
+ struct spi_ns9360 *info = spi_master_get_devdata(master);
+ u32 status;
+
+ spin_lock(&info->lock);
+
+ /* get status and mask all pending interrupts */
+ status = ioread32(info->ioaddr_bbus + BBUS_DMA_TXSTAT);
+ iowrite32(status, info->ioaddr_bbus + BBUS_DMA_TXSTAT);
+
+ if (status & (BBUS_DMA_STAT_ECIP | BBUS_DMA_STAT_CAIP |
+ BBUS_DMA_STAT_PCIP)) {
+ dev_err(&master->dev, "tx dma failure! status = 0x%08x\n",
+ status);
+
+ /* retrigger the xfer */
+ spi_ns9360_next_xfer(master, list_entry(info->queue.next,
+ struct spi_message, queue));
+ }
+
+ if (status & BBUS_DMA_STAT_NCIP) {
+ info->flags |= SPI_TX_IDONE;
+ if (info->flags & SPI_RX_IDONE)
+ spi_ns9360_irq_trigger_next(master);
+ }
+
+ spin_unlock(&info->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t spi_ns9360_irq_rx(int irq, void *dev_id)
+{
+ struct spi_master *master = dev_id;
+ struct spi_ns9360 *info = spi_master_get_devdata(master);
+ u32 status, dlen;
+
+ spin_lock(&info->lock);
+
+ /* get status and mask all pending interrupts */
+ status = ioread32(info->ioaddr_bbus + BBUS_DMA_RXSTAT);
+ iowrite32(status, info->ioaddr_bbus + BBUS_DMA_RXSTAT);
+
+ dlen = status & BBUS_DMA_STAT_BLEN_MA;
+ if (info->dma_xfer_len != dlen)
+ /* ?? what to do... ?? */
+ dev_err(&master->dev, "incomplete dma xfer"
+ "(%d/%d bytes transfered) \n",
+ dlen, info->dma_xfer_len);
+
+ if (status & (BBUS_DMA_STAT_ECIP | BBUS_DMA_STAT_CAIP |
+ BBUS_DMA_STAT_PCIP)) {
+ dev_err(&master->dev, "rx dma failure! status = 0x%08x\n",
+ status);
+
+ /* retrigger the xfer */
+ spi_ns9360_next_xfer(master, list_entry(info->queue.next,
+ struct spi_message, queue));
+ }
+
+ if (status & BBUS_DMA_STAT_NCIP) {
+ info->flags |= SPI_RX_IDONE;
+ if (info->flags & SPI_TX_IDONE)
+ spi_ns9360_irq_trigger_next(master);
+ }
+
+ spin_unlock(&info->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit spi_ns9360_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct spi_master *master;
+ struct spi_ns9360 *info;
+ int gpio_cs, gpio_cs_func;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*info));
+ if (!master) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc_master\n", __func__);
+ goto err_alloc_master;
+ }
+
+ platform_set_drvdata(pdev, master);
+
+ info = spi_master_get_devdata(master);
+ info->pdata = pdev->dev.platform_data;
+
+ info->irq_rx = platform_get_irq(pdev, 0);
+ if (info->irq_rx < 0) {
+ ret = -ENOENT;
+ dev_dbg(&pdev->dev, "%s: err_get_irq_rx\n", __func__);
+ goto err_get_irq_rx;
+ }
+
+ info->irq_tx = platform_get_irq(pdev, 1);
+ if (info->irq_tx < 0) {
+ ret = -ENOENT;
+ dev_dbg(&pdev->dev, "%s: err_get_irq_tx\n", __func__);
+ goto err_get_irq_tx;
+ }
+
+ info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!info->mem) {
+ ret = -ENOENT;
+ dev_dbg(&pdev->dev, "%s: err_get_mem\n", __func__);
+ goto err_get_mem;
+ }
+
+ if (!request_mem_region(info->mem->start,
+ info->mem->end - info->mem->start + 1,
+ pdev->name)) {
+ ret = -ENXIO;
+ dev_dbg(&pdev->dev, "%s: err_req_mem\n", __func__);
+ goto err_req_mem;
+ }
+
+ info->ioaddr = ioremap(info->mem->start,
+ info->mem->end - info->mem->start + 1);
+ if (!info->ioaddr) {
+ ret = -ENXIO;
+ dev_dbg(&pdev->dev, "%s: err_map_mem\n", __func__);
+ goto err_map_mem;
+ }
+
+ info->mem_bbus = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!info->mem_bbus) {
+ ret = -ENOENT;
+ dev_dbg(&pdev->dev, "%s: err_get_mem_bbus\n", __func__);
+ goto err_get_mem_bbus;
+ }
+
+ if (!request_mem_region(info->mem_bbus->start,
+ info->mem_bbus->end - info->mem_bbus->start + 1,
+ pdev->name)) {
+ ret = -ENXIO;
+ dev_dbg(&pdev->dev, "%s: err_req_mem_bbus\n", __func__);
+ goto err_req_mem_bbus;
+ }
+
+ info->ioaddr_bbus = ioremap(info->mem_bbus->start,
+ info->mem_bbus->end - info->mem_bbus->start + 1);
+ if (!info->ioaddr_bbus) {
+ ret = -ENXIO;
+ dev_dbg(&pdev->dev, "%s: err_map_mem_bbus\n", __func__);
+ goto err_map_mem_bbus;
+ }
+
+ master->bus_num = pdev->id;
+ /* hardware controlled cs */
+ master->num_chipselect = 1;
+
+ /* If CS is GPIO controlled, configure it as output */
+ gpio_cs = info->pdata->gpios[SPI_EN_GPIO_OFFSET];
+ gpio_cs_func = info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET];
+ if( NS9360_GPIO_FUNC_GPIO == gpio_cs_func )
+ gpio_direction_output( gpio_cs,
+ gpio_get_value( gpio_cs ) );
+
+ master->setup = spi_ns9360_setup;
+ master->transfer = spi_ns9360_transfer;
+
+ spin_lock_init(&info->lock);
+ INIT_LIST_HEAD(&info->queue);
+
+ info->buf_tx = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
+ &info->buf_tx_dma, GFP_KERNEL);
+ if (!info->buf_tx) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc_buf_tx\n", __func__);
+ goto err_alloc_buf_tx;
+ }
+
+ info->buf_rx = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
+ &info->buf_rx_dma, GFP_KERNEL);
+ if (!info->buf_rx) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc_buf_rx\n", __func__);
+ goto err_alloc_buf_rx;
+ }
+
+ info->desc_tx = dma_alloc_coherent(&pdev->dev,
+ sizeof(info->desc_tx), &info->desc_tx_dma, GFP_DMA);
+ if (!info->desc_tx) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc_desc_tx\n", __func__);
+ goto err_alloc_desc_tx;
+ }
+
+ info->desc_rx = dma_alloc_coherent(&pdev->dev,
+ sizeof(info->desc_rx), &info->desc_rx_dma, GFP_DMA);
+ if (!info->desc_rx) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc_desc_rx\n", __func__);
+ goto err_alloc_desc_rx;
+ }
+
+ info->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(info->clk)) {
+ ret = PTR_ERR(info->clk);
+ dev_dbg(&pdev->dev, "%s: err_clk_get\n", __func__);
+ goto err_clk_get;
+ }
+
+ ret = clk_enable(info->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_clk_enable\n", __func__);
+ goto err_clk_enable;
+ }
+
+ ret = request_irq(info->irq_rx, spi_ns9360_irq_rx, 0,
+ pdev->name, master);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_req_irq_rx\n", __func__);
+ goto err_req_irq_rx;
+ }
+
+ ret = request_irq(info->irq_tx, spi_ns9360_irq_tx, 0,
+ pdev->name, master);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_req_irq_tx\n", __func__);
+ goto err_req_irq_tx;
+ }
+
+ ret = spi_register_master(master);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_reg_reg_master\n", __func__);
+ goto err_reg_master;
+ }
+
+ dev_info(&pdev->dev, "NS9360 SPI controller at 0x%p (irq: %d/%d)\n",
+ info->ioaddr, info->irq_rx, info->irq_tx);
+
+ return 0;
+
+err_reg_master:
+ free_irq(info->irq_tx, master);
+err_req_irq_tx:
+ free_irq(info->irq_rx, master);
+err_req_irq_rx:
+ clk_disable(info->clk);
+err_clk_enable:
+ clk_put(info->clk);
+err_clk_get:
+ dma_free_coherent(&pdev->dev, sizeof(info->desc_rx),
+ info->desc_rx, info->desc_rx_dma);
+err_alloc_desc_rx:
+ dma_free_coherent(&pdev->dev, sizeof(info->desc_tx),
+ info->desc_tx, info->desc_tx_dma);
+err_alloc_desc_tx:
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE,
+ info->buf_rx, info->buf_rx_dma);
+err_alloc_buf_rx:
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE,
+ info->buf_tx, info->buf_tx_dma);
+err_alloc_buf_tx:
+ iounmap(info->ioaddr_bbus);
+err_map_mem_bbus:
+ release_mem_region(info->mem_bbus->start,
+ info->mem_bbus->end - info->mem_bbus->start + 1);
+err_req_mem_bbus:
+err_get_mem_bbus:
+ iounmap(info->ioaddr);
+err_map_mem:
+ release_mem_region(info->mem->start,
+ info->mem->end - info->mem->start + 1);
+err_req_mem:
+err_get_mem:
+err_get_irq_tx:
+err_get_irq_rx:
+ spi_master_put(master);
+err_alloc_master:
+
+ return ret;
+}
+
+static int __devexit spi_ns9360_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct spi_ns9360 *info = spi_master_get_devdata(master);
+
+ free_irq(info->irq_tx, master);
+ free_irq(info->irq_rx, master);
+
+ clk_disable(info->clk);
+ clk_put(info->clk);
+
+ dma_free_coherent(&pdev->dev, sizeof(info->desc_rx),
+ info->desc_rx, info->desc_rx_dma);
+ dma_free_coherent(&pdev->dev, sizeof(info->desc_tx),
+ info->desc_tx, info->desc_tx_dma);
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE,
+ info->buf_rx, info->buf_rx_dma);
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE,
+ info->buf_tx, info->buf_tx_dma);
+
+ iounmap(info->ioaddr_bbus);
+ release_mem_region(info->mem_bbus->start,
+ info->mem_bbus->end - info->mem_bbus->start + 1);
+
+ iounmap(info->ioaddr);
+ release_mem_region(info->mem->start,
+ info->mem->end - info->mem->start + 1);
+
+ spi_master_put(master);
+
+ return 0;
+}
+
+static struct platform_driver spi_ns9360_driver = {
+ .probe = spi_ns9360_probe,
+ .remove = __devexit_p(spi_ns9360_remove),
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init spi_ns9360_init(void)
+{
+ return platform_driver_register(&spi_ns9360_driver);
+}
+
+static void __exit spi_ns9360_exit(void)
+{
+ platform_driver_unregister(&spi_ns9360_driver);
+}
+
+module_init(spi_ns9360_init);
+module_exit(spi_ns9360_exit);
+
+MODULE_DESCRIPTION("NS9360 SPI Driver");
+MODULE_AUTHOR("Digi International Inc.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi_s3c2443.c b/drivers/spi/spi_s3c2443.c
new file mode 100644
index 000000000000..7b0d087776dc
--- /dev/null
+++ b/drivers/spi/spi_s3c2443.c
@@ -0,0 +1,1229 @@
+/* -*- linux-c -*-
+ *
+ * linux/drivers/spi/spi_s3c2443.c
+ *
+ * Copyright (c) 2008 Digi International
+ *
+ * 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.
+ *
+ * !Revision: $Revision: 1.1.1.1 $
+ * !Author: Luis Galdos
+ * !Descr: High Speed SPI driver for the S3C2443
+ * !References: Based on the driver of the Samsung SMDK for the S3C2443
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+#include <asm/delay.h>
+
+#include <mach/gpio.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-gpioj.h>
+#include <asm/plat-s3c24xx/regs-spi.h>
+#include <mach/dma.h>
+#include <asm/plat-s3c24xx/spi.h>
+#include <mach/dma.h>
+#include <mach/regs-s3c2443-clock.h>
+
+
+/* This macro selects the virtual DMA-channel to use */
+#define SPI_TX_CHANNEL DMACH_SPI0
+#define SPI_RX_CHANNEL DMACH_SPI0_RX
+
+#define S3C2443_SPI_DMA_BUFFER (PAGE_SIZE)
+
+/* Supported modes */
+#define S3C2443_SPI_SUPPORTED_MODES (SPI_CPOL | SPI_CPHA)
+
+/* DMA transfer unit (byte). */
+#define S3C24XX_DMA_XFER_BYTE 1
+#define S3C24XX_DMA_XFER_HWORD 2
+#define S3C24XX_DMA_XFER_WORD 4
+
+/* Used for setting the CS on the different SPI-operations */
+#define SPI_CS_ACTIVE 1
+#define SPI_CS_INACTIVE 0
+
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] spi-s3c2443: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "spi-s3c2443: " fmt, ## args)
+
+#if 0
+#define S3C2443_SPI_DEBUG
+#endif
+
+#ifdef S3C2443_SPI_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "spi-s3c2443: %s() " fmt, __FUNCTION__ , ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+
+enum s3c2443_xfer_t {
+ S3C2443_DMA_TX = 0,
+ S3C2443_DMA_RX = 1,
+};
+
+
+struct s3c2443_spi {
+ void __iomem *regs;
+ int irq;
+ struct clk *clk;
+ struct resource *ioarea;
+ struct spi_master *master;
+ struct device *dev;
+ struct s3c2443_spi_info *pdata;
+
+ struct list_head xmit_queue;
+ spinlock_t xmit_lock;
+ struct spi_transfer *current_xfer;
+ long remaining_bytes;
+ unsigned long dma_xfer_len;
+ struct tasklet_struct xmit_tasklet;
+ void *tx_buf;
+ void *rx_buf;
+ dma_addr_t tx_dma;
+ dma_addr_t rx_dma;
+
+ dmach_t dma_ch;
+ struct s3c2410_dma_client dmach;
+ dmach_t dma_ch_rx;
+ struct s3c2410_dma_client dmach_rx;
+};
+
+
+/* Internal functions */
+static void s3c2443_spi_next_message(struct spi_master *master);
+static void s3c2443_spi_next_xfer(struct spi_master *master, struct spi_message *msg);
+
+
+static inline struct s3c2443_spi *spi_to_hw(struct spi_device *sdev)
+{
+ return spi_master_get_devdata(sdev->master);
+}
+
+static inline struct s3c2443_spi *master_to_hw(struct spi_master *master)
+{
+ return spi_master_get_devdata(master);
+}
+
+
+static void s3c2443_spi_chipsel(struct spi_device *spi, int active)
+{
+ /* unsigned int cspol; */
+ struct s3c2443_spi *hw;
+ void (* cs_callback)(struct spi_device *_spi, int _act);
+ unsigned long slavecfg;
+ struct s3c2443_spi_info *info;
+ struct s3c24xx_spi_gpio *cs;
+
+ hw = spi_to_hw(spi);
+ if (!hw) {
+ printk_err("HW null pointer found!\n");
+ return;
+ }
+
+ /*
+ * If the SPI-device has a pointer function for controlling the
+ * chip select, then we will call its passed function. Otherwise the
+ * CS will be controlled by the SPI-hardware
+ */
+ if (spi->controller_data) {
+ info = hw->pdata;
+ cs = info->cs;
+
+ printk_debug("Calling the controller data\n");
+
+ /*
+ * Since the SPI-device will control its own CS disable ours by
+ * configuring it as input, then we have enabled the pull-up, right?
+ */
+ if (active == SPI_CS_ACTIVE)
+ s3c2410_gpio_cfgpin(cs->nr, S3C2410_GPIO_INPUT);
+
+ cs_callback = spi->controller_data;
+ cs_callback(spi, !active);
+
+ /* Now reenable our CS, but before disable the last CS-activation! */
+ if (active == SPI_CS_INACTIVE) {
+ writel(S3C2443_SPI0_SLAVE_SIG_INACT,
+ hw->regs + S3C2443_SPI0_SLAVE_SEL);
+ s3c2410_gpio_cfgpin(cs->nr, cs->cfg);
+ }
+ }
+
+ /*
+ * Even an external interrupt is being used, we MUST enable the slave
+ * register for being able to shift the data to the bus. Otherwise the
+ * FIFO will not send the data to the bus, and an error from the DMA-engine
+ * will be generated: "... timeout ... load buffer ..."
+ */
+ printk_debug("Using the HW-CS (%i)\n", active);
+ slavecfg = S3C2443_SPI0_SLAVE_SIG_INACT;
+ if (active == SPI_CS_ACTIVE)
+ slavecfg = S3C2443_SPI0_SLAVE_SIG_ACT;
+
+ writel(slavecfg, hw->regs + S3C2443_SPI0_SLAVE_SEL);
+}
+
+/*
+ * Please reset the SPI-controller before starting a transfer. Otherwise the
+ * configuration of the last transfer will interfere with the new transfer
+ */
+static void s3c2443_spi_sw_reset(struct s3c2443_spi *spi)
+{
+ unsigned long cfg;
+
+ cfg = readl(spi->regs + S3C2443_SPI0_CH_CFG);
+ writel(cfg | S3C2443_SPI0_CH_SW_RST, spi->regs + S3C2443_SPI0_CH_CFG);
+ writel(cfg, spi->regs + S3C2443_SPI0_CH_CFG);
+}
+
+
+static int s3c2443_spi_hw_init(struct s3c2443_spi *hw)
+{
+ unsigned long misccr, clkcfg;
+ struct s3c2443_spi_info *info;
+ int cnt;
+ struct s3c24xx_spi_gpio *cs;
+
+ info = hw->pdata;
+
+ writel(readl(S3C2443_SCLKCON) | S3C2443_SCLKCON_HSSPICLK, S3C2443_SCLKCON);
+ writel(readl(S3C2443_PCLKCON) | S3C2443_PCLKCON_HSSPI, S3C2443_PCLKCON);
+
+ if (info->input_clk == S3C2443_HSSPI_INCLK_PCLK)
+ clkcfg = S3C2443_SPI0_CLKSEL_PCLK;
+ else
+ clkcfg = S3C2443_SPI0_CLKSEL_EPLL;
+ writel(clkcfg, hw->regs + S3C2443_SPI0_CLK_CFG);
+
+ misccr = __raw_readl(S3C24XX_MISCCR);
+ __raw_writel(misccr | (1 << 31), S3C24XX_MISCCR);
+
+ /* HSSPI software reset */
+ s3c2443_spi_sw_reset(hw);
+
+ /* Configure the chip selects depending on the passed platform data */
+ s3c2410_gpio_cfgpin(info->miso.nr, info->miso.cfg);
+ s3c2410_gpio_cfgpin(info->mosi.nr, info->mosi.cfg);
+ s3c2410_gpio_cfgpin(info->clk.nr, info->clk.cfg);
+ for (cnt = 0; cnt < info->num_chipselect; cnt++) {
+
+ /*
+ * We need to activate the pullup for being able to reconfigure the
+ * CS as an input IO
+ */
+ cs = info->cs + cnt;
+ s3c2410_gpio_cfgpin(cs->nr, cs->cfg);
+ s3c2410_gpio_pullup(cs->nr, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * Configure the controller for the passed SPI-device. Additionally it will
+ * use the configuration values of the SPI-transfer if it's different than NULL
+ */
+static int s3c2443_spi_setupxfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ struct s3c2443_spi *hw = spi_to_hw(spi);
+ unsigned int bpw;
+ unsigned int hz, max_hz, min_hz;
+ unsigned int div;
+ unsigned long clkreg, chcfg, mode;
+
+ printk_debug("Setup for next xfer called\n");
+
+ /* If the bits per word is zero, then use the default value of the SPI-device */
+ bpw = t ? t->bits_per_word : spi->bits_per_word;
+ if (!bpw)
+ bpw = spi->bits_per_word;
+
+ /* We only support eigth bits per word */
+ if (bpw != 8) {
+ printk_err("Invalid bits-per-word (%d)\n", bpw);
+ return -EINVAL;
+ }
+
+ /*
+ * If the clock rate of the xfer is zero, then use the already configured
+ * rate of this SPI-device
+ */
+ hz = t ? t->speed_hz : spi->max_speed_hz;
+ if (!hz)
+ hz = spi->max_speed_hz;
+
+ /* The below calculation is coming from the SMDK */
+ max_hz = clk_get_rate(hw->clk) / 2;
+ min_hz = max_hz / 256;
+ if (hz > max_hz || hz < min_hz) {
+ printk_err("Speed %d Hz out of range (Min %i | Max %i)\n",
+ hz, min_hz, max_hz);
+ return -EINVAL;
+ }
+
+ div = clk_get_rate(hw->clk) / hz;
+ div /= 2;
+ div = (div) ? (div - 1) : (0);
+ if (div > S3C2443_SPI0_CLK_PRE_MASK) {
+ printk_err("Invalid clock divider %d (%i Hz)\n", div, hz);
+ return -EINVAL;
+ }
+
+ /* Only modify the prescaler */
+ printk_debug("Setting pre-scaler to %d (%d Hz)\n", div, hz);
+ clkreg = readl(hw->regs + S3C2443_SPI0_CLK_CFG) & ~(S3C2443_SPI0_CLK_PRE_MASK);
+ writel(clkreg | div, hw->regs + S3C2443_SPI0_CLK_CFG);
+
+ /*
+ * For some reasons the clock configuration isn't working correctly by
+ * the FIRST execution. For this reason let's make a sanity check and
+ * if required reset the hardware and reconfigure the clock again. This seems
+ * to solve the problem.
+ * Luis Galdos
+ */
+ clkreg = readl(hw->regs + S3C2443_SPI0_CLK_CFG);
+ if ((clkreg & S3C2443_SPI0_CLK_PRE_MASK) != div) {
+ printk_debug("Need to reconfigure the clock\n");
+ s3c2443_spi_hw_init(hw);
+ s3c2443_spi_sw_reset(hw);
+ writel(clkreg | div, hw->regs + S3C2443_SPI0_CLK_CFG);
+ }
+
+ /* Set the correct mode for the passed SPI-transfer */
+ mode = spi->mode & 0x03;
+ switch (mode) {
+ case SPI_MODE_2:
+ /* Sampled by falling edges with a high inactive clock (MODE_2) */
+ chcfg = S3C2443_SPI0_CH_FALLING | S3C2443_SPI0_CH_FORMAT_A;
+ break;
+
+ case SPI_MODE_1:
+ /* Sampled by falling edges with a low inactive clock (MODE_1) */
+ chcfg = S3C2443_SPI0_CH_RISING | S3C2443_SPI0_CH_FORMAT_B;
+ break;
+
+ case SPI_MODE_3:
+ /* Sampled by rising edges with a high inactive clock (MODE_3) */
+ chcfg = S3C2443_SPI0_CH_FALLING | S3C2443_SPI0_CH_FORMAT_B;
+ break;
+
+ case SPI_MODE_0:
+ default:
+ /* Sampled by rising edges with a low inactive clock (MODE_0) */
+ chcfg = S3C2443_SPI0_CH_RISING | S3C2443_SPI0_CH_FORMAT_A;
+ break;
+ }
+
+ printk_debug("Configuring the mode %lu\n", mode);
+ writel(chcfg , hw->regs + S3C2443_SPI0_CH_CFG);
+ return 0;
+}
+
+/*
+ * Configure the master-controller. This function is only called when the higher
+ * layer wants to MODIFY the current configuration of the controller.
+ */
+static int s3c2443_spi_setup(struct spi_device *spi)
+{
+ int ret;
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+
+ if (spi->mode & ~S3C2443_SPI_SUPPORTED_MODES) {
+ printk_debug("Unsupported mode bits %x\n",
+ spi->mode & ~S3C2443_SPI_SUPPORTED_MODES);
+ return -EINVAL;
+ }
+
+ ret = s3c2443_spi_setupxfer(spi, NULL);
+ if (!ret)
+ printk_debug("Mode %d, %u bpw, %d HZ\n",
+ spi->mode, spi->bits_per_word,
+ spi->max_speed_hz);
+
+ return ret;
+}
+
+/*
+ * Tasklet function for checking the message queue. If there is another transfer
+ * pending, then this function will start the transfer, otherwise it calls the
+ * complete function of the SPI-message and reset the internal data for the
+ * next available message of the internal message queue and start it too
+ */
+static void s3c2443_spi_next_tasklet(unsigned long data)
+{
+ struct s3c2443_spi *hw;
+ struct spi_transfer *xfer;
+ struct spi_message *msg;
+ unsigned long stat;
+ int timeout, err;
+
+ hw = (struct s3c2443_spi *)data;
+ if (!hw)
+ return;
+
+ xfer = hw->current_xfer;
+ msg = list_entry(hw->xmit_queue.next, struct spi_message, queue);
+ if (!xfer || !msg)
+ return;
+
+ /*
+ * If we used the own internal DMA-buffer for the DMA-transfer then copy
+ * the data to the higher SPI-message. If not, then cause we have
+ * passed the transfer-buffer to the DMA-controller
+ */
+ if (!msg->is_dma_mapped && xfer->rx_buf && hw->dma_xfer_len) {
+ printk_debug("Copying %lu bytes to SPI-buffer %p\n",
+ hw->dma_xfer_len, xfer->rx_buf);
+ memcpy(xfer->rx_buf, hw->rx_buf, hw->dma_xfer_len);
+ }
+
+ /*
+ * We have a problem at this place, then we don't know if
+ * the shift-register is really empty. For this reason we
+ * use the below loop. Please note that by high configured
+ * clocks the shift register will be already empty at this point
+ * and by low clocks the usleep is not really relevant
+ * compared with the complete transmission time. The problem is
+ * that udelay will cause a higher CPU-load, but we don't
+ * want to sleep before calling the tasklet, and at this place
+ * is no more possible to take a siesta.
+ * Luis Galdos
+ */
+ err = (hw->remaining_bytes < 0) ? (hw->remaining_bytes) : 0;
+ if (!err && xfer->tx_buf) {
+ stat = readl(hw->regs + S3C2443_SPI0_STATUS);
+ timeout = (err) ? 0 : 0xffff;
+ while (!(stat & S3C2443_SPI0_STUS_TX_DONE) && timeout) {
+ udelay(2);
+ stat = readl(hw->regs + S3C2443_SPI0_STATUS);
+ timeout--;
+ }
+
+ if (!timeout)
+ err = -ETIME;
+ }
+
+ /*
+ * If NO more data is remaining then update the internal variables of the
+ * SPI-message and call the complete-function. The errors are passed to us
+ * with an negative value of remaining_bytes
+ */
+ if (hw->remaining_bytes == 0 || err) {
+
+ /* Disable the IRQs and FIFOs, then we are done */
+ writel(0x00, hw->regs + S3C2443_SPI0_CH_CFG);
+
+ msg->actual_length += hw->dma_xfer_len;
+
+ if (xfer->delay_usecs)
+ udelay(xfer->delay_usecs);
+
+ /* Check if the current SPI-transfer was the last of the message */
+ if (msg->transfers.prev == &xfer->transfer_list || err) {
+ msg->status = err;
+
+ /* Disable the slave selection (chip select inactive) */
+ s3c2443_spi_chipsel(msg->spi, SPI_CS_INACTIVE);
+
+ stat = readl(hw->regs + S3C2443_SPI0_STATUS);
+
+ printk_debug("Msg complete %i | stat 0x08%x)\n",
+ err, (unsigned int)stat);
+
+ list_del(&msg->queue);
+ msg->complete(msg->context);
+
+ /*
+ * Now, check for the next message of the internal xmit-queue.
+ * But first lock the code segment, then probably somebody is
+ * trying to start a new transfer at this moment
+ */
+ spin_lock(&hw->xmit_lock);
+ hw->current_xfer = NULL;
+ if (!list_empty(&hw->xmit_queue))
+ s3c2443_spi_next_message(hw->master);
+ spin_unlock(&hw->xmit_lock);
+
+ } else {
+ printk_debug("Calling the next XFER of the msg %p\n", msg);
+
+ /* Toggle the CS if requested */
+ if (xfer->cs_change) {
+ s3c2443_spi_chipsel(msg->spi, SPI_CS_INACTIVE);
+ udelay(1);
+ s3c2443_spi_chipsel(msg->spi, SPI_CS_ACTIVE);
+ }
+
+ s3c2443_spi_next_xfer(hw->master, msg);
+ }
+
+ } else {
+ /*
+ * Start the next write operation if there is remaining data of the
+ * last SPI-transfer
+ */
+ printk_debug("Need to send more data (%lu)\n", hw->remaining_bytes);
+ s3c2443_spi_next_xfer(hw->master, msg);
+ }
+
+}
+
+/*
+ * The DMA-subsytem calls this function from an interrupt context. For this
+ * reason we only update the remaining-bytes counter and check if can
+ * schedule the tasklet for the SPI-complete. BUT the called will be scheduled
+ * only if the FIFO has no more data to send, otherwise we will modify the interrupt
+ * registers for receiving an interrupt from the TX-FIFO later.
+ */
+static void s3c2443_spi_dma_callback(struct s3c2410_dma_chan *ch,
+ void *_hw, int size,
+ enum s3c2410_dma_buffresult result)
+{
+ struct s3c2443_spi *hw;
+ unsigned long mode, inten, stat;
+ int is_tx;
+
+ hw = (struct s3c2443_spi *)_hw;
+ if (!hw) {
+ printk_err("Got a NULL pointer from the DMA-callback!\n");
+ return;
+ }
+
+ /* Check if the callback corresponds to the RX or TX-channel */
+ is_tx = (ch->client == &hw->dmach) ? (1) : (0);
+ printk_debug("%s DMA callback | Ch %i\n", (is_tx) ? "TX" : "RX", ch->number);
+
+ /* If the transfer has a RX-channel running then need to wait for it */
+ if (is_tx && hw->current_xfer->rx_buf && result == S3C2410_RES_OK) {
+ printk_debug("Need to wait for the RX-DMA\n");
+ return;
+ }
+
+ /*
+ * If an error is detected pass the info to the tasklet too (place the error
+ * message into the status of the message).
+ * By aborts only set the number of remaining bytes to zero, so that the
+ * tasklet will complete the message
+ */
+ switch (result) {
+ case S3C2410_RES_OK:
+ printk_debug("Result OK\n");
+ hw->remaining_bytes -= size;
+ hw->dma_xfer_len += size;
+ break;
+ case S3C2410_RES_ERR:
+ printk_err("Result ERROR\n");
+ hw->dma_xfer_len = 0;
+ hw->remaining_bytes = -ERESTART;
+ break;
+ case S3C2410_RES_ABORT:
+ printk_debug("Result ABORT\n");
+ hw->dma_xfer_len += size;
+ hw->remaining_bytes = -ECONNABORTED;
+ break;
+ }
+
+ /*
+ * At this point, normally the TX-FIFO has still data to transfer to the
+ * bus. That means for us, we can enable the interrupt for the TX-FIFO
+ * which informs us about the TX-done. So, only enable
+ * the level IRQ, so that the IRQ-handler will call the tasklet later.
+ * BTW, we only enable the interrupt when we are transferring data using
+ * the DMA-controller, otherwise the DMA-subsystem calls this function when
+ * all the bytes were already read
+ */
+ if (likely(result == S3C2410_RES_OK && hw->current_xfer->tx_buf)) {
+ stat = readl(hw->regs + S3C2443_SPI0_STATUS);
+
+ printk_debug("Need to wait for the TX-FIFO | stat 0x%x\n",
+ (unsigned int)stat);
+
+ /* Disable the DMA-mode for the TX-FIFO */
+ mode = readl(hw->regs + S3C2443_SPI0_MODE_CFG);
+ mode &= ~S3C2443_SPI0_MODE_TXDMA_ON;
+ mode |= (1 << 5);
+ writel(mode, hw->regs + S3C2443_SPI0_MODE_CFG);
+
+ /* Enable the ready interrupt */
+ inten = readl(hw->regs + S3C2443_SPI0_INT_EN);
+ inten |= S3C2443_SPI0_INT_TX_UNDERRUN_EN |
+ S3C2443_SPI0_INT_TX_FIFORDY_EN;
+ writel(inten , hw->regs + S3C2443_SPI0_INT_EN);
+ } else {
+ printk_debug("Calling the tasklet from the DMA-callback\n");
+ tasklet_schedule(&hw->xmit_tasklet);
+ }
+}
+
+/*
+ * IMPORTANT: If the data transfer is smaller than four bytes, then the SPI-controller
+ * must be configured without the burst-mode! Otherwise the channel will run
+ * amok!
+ * Luis Galdos
+ */
+static int s3c2443_spi_dma_init(dmach_t dma_ch, enum s3c2443_xfer_t mode,
+ int length)
+{
+ int xmode;
+
+ printk_debug("DMA init for next %s\n", (mode == S3C2443_DMA_TX) ? "TX" : "RX");
+
+ if (mode == S3C2443_DMA_TX) {
+ /*
+ * For TX-transfers configure the destination with fixed address and
+ * inside the APB-bus. The source of transfers will be the
+ * system memory
+ */
+ s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_MEM,
+ S3C2410_DISRCC_INC | S3C2410_DISRCC_APB,
+ S3C2443_SPI0_TX_DATA_PA);
+ } else if (mode == S3C2443_DMA_RX) {
+ /*
+ * By RX-transfer the source is the RX-FIFO of the SPI-controller.
+ * The controller is part of the APB and should not increment
+ * the address
+ */
+ s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_HW,
+ S3C2410_DISRCC_INC | S3C2410_DISRCC_APB,
+ S3C2443_SPI0_RX_DATA_PA);
+ } else {
+ printk_err("Invalid DMA transfer mode (%i)\n", mode);
+ return -EINVAL;
+ }
+
+ /*
+ * Select the correct transfer mode depending on the number of bytes
+ * to transfer. Unfortunately the DMA-controller can transfer only data
+ * quantums depending on the number of the data size to be transferred
+ * (see register: DCON, field DSZ)
+ * Luis Galdos
+ */
+ if (!(length & 0x3))
+ xmode = S3C24XX_DMA_XFER_WORD;
+ else
+ xmode = S3C24XX_DMA_XFER_BYTE;
+
+ /* @XXX: Don't use the burst mode, then it's not working with this driver */
+ s3c2410_dma_config(dma_ch,
+ xmode,
+ S3C2410_DCON_HANDSHAKE |
+ S3C2410_DCON_SYNC_PCLK |
+ S3C2410_DCON_HWTRIG);
+
+ s3c2410_dma_setflags(dma_ch, S3C2410_DMAF_AUTOSTART);
+ return 0;
+}
+
+/*
+ * This function is used to handle the next transfer of a SPI-message.
+ * If the configuration of the transfer failed, then this function will
+ * set the error code and will schedule the tasklet for completing the
+ * SPI-message correctly.
+ */
+static void s3c2443_spi_next_xfer(struct spi_master *master,
+ struct spi_message *msg)
+{
+ struct spi_transfer *xfer;
+ struct s3c2443_spi *hw;
+ unsigned long len;
+ dma_addr_t rx_dma, tx_dma;
+ int err;
+ unsigned long clkcfg, chcfg, modecfg, spi_packet;
+
+ hw = master_to_hw(master);
+
+ /* Always reset the controller first */
+ s3c2443_spi_sw_reset(hw);
+
+ /*
+ * Check if we are going to start sending a complete new message, or if
+ * the passed message was already used for sending a SPI-transfer
+ */
+ xfer = hw->current_xfer;
+ if (!xfer || hw->remaining_bytes == 0) {
+ if (xfer)
+ xfer = list_entry(xfer->transfer_list.next,
+ struct spi_transfer, transfer_list);
+ else
+ xfer = list_entry(msg->transfers.next,
+ struct spi_transfer, transfer_list);
+
+ hw->current_xfer = xfer;
+ hw->remaining_bytes = xfer->len;
+ hw->dma_xfer_len = 0;
+ }
+
+ /* Set the requested configuration for this new SPI-transfer */
+ err = s3c2443_spi_setupxfer(msg->spi, xfer);
+ if (err) {
+ printk_err("Setting up the next SPI-transfer\n");
+ hw->remaining_bytes = err;
+ goto exit_err;
+ }
+
+ len = hw->remaining_bytes;
+
+ /* 2. Enable the output clock (but don't modify the clock prescaler) */
+ clkcfg = readl(hw->regs + S3C2443_SPI0_CLK_CFG);
+ clkcfg |= S3C2443_SPI0_ENCLK_ENABLE;
+ writel(clkcfg, hw->regs + S3C2443_SPI0_CLK_CFG);
+
+ /*
+ * Configure the transfer mode depending on the number of bytes to send
+ * @XXX: For some unknown reasons we can't enable the burst mode. Please be
+ * aware then the burst mode for the FIFOs will work ONLY if the
+ * DMA-channel is configured for the burst mode too (so far so good).
+ * Luis Galdos
+ */
+ modecfg = S3C2443_SPI0_MODE_CH_TSZ_BYTE;
+ if (!(len & 0x3))
+ modecfg = S3C2443_SPI0_MODE_CH_TSZ_WORD;
+
+ /*
+ * Enable the DMA-modes depending on the transfer type
+ */
+ if (xfer->tx_buf)
+ modecfg |= S3C2443_SPI0_MODE_TXDMA_ON;
+ if (xfer->rx_buf)
+ modecfg |= S3C2443_SPI0_MODE_RXDMA_ON;
+
+ writel(modecfg, hw->regs + S3C2443_SPI0_MODE_CFG);
+
+ /*
+ * 4. Set SPI INT_EN register
+ * Disable all the interrupts then we only use the DMA-controller for
+ * transferring the data from the RAM to the FIFO
+ */
+ writel(0x00, hw->regs + S3C2443_SPI0_INT_EN);
+
+ /* Clear the pending register */
+ writel(0x1f, hw->regs + S3C2443_SPI0_PENDING_CLR);
+
+ /*
+ * If the passed SPI-buffer isn't DMA-mapped, then use the internal
+ * DMA-buffers for sending the data to the DMA-layer. If we are using
+ * the internal buffer, then check that we can only send the maximal
+ * number of bytes. In the tasklet the transfer of the rest data can
+ * be triggered.
+ */
+ tx_dma = xfer->tx_dma;
+ rx_dma = xfer->rx_dma;
+ if (!msg->is_dma_mapped) {
+
+ printk_debug("SPI-message not DMA mapped\n");
+
+ rx_dma = hw->rx_dma;
+ if (len > S3C2443_SPI_DMA_BUFFER)
+ len = S3C2443_SPI_DMA_BUFFER;
+
+ if (xfer->tx_buf) {
+ tx_dma = hw->tx_dma;
+
+ if (len > S3C2443_SPI_DMA_BUFFER)
+ len = S3C2443_SPI_DMA_BUFFER;
+
+ /* IMPORTANT: Copy the TX-data from the correct place! */
+ memcpy(hw->tx_buf,
+ xfer->tx_buf + hw->dma_xfer_len, len);
+ }
+ }
+
+ /*
+ * 5. Set Packet Count configuration register
+ * For some unknown reasons the transfer of only ONE byte isn't
+ * working correctly. The data byte transferred from the DMA-controller
+ * isn't flushed from the TX-FIFO to the bus. But disabling the trailling
+ * count and reconfiguring the TX-FIFO helps to fix this problem.
+ * (Luis Galdos)
+ */
+ spi_packet = len | S3C2443_SPI0_PACKET_CNT_EN;
+ if (unlikely(len == 1)) {
+ spi_packet = 0;
+ s3c2443_spi_sw_reset(hw);
+ s3c2443_spi_setupxfer(msg->spi, xfer);
+ }
+ writel(spi_packet, hw->regs + S3C2443_SPI0_PACKET_CNT);
+
+ printk_debug("%s : %lu bytes | Rx %p | Tx %p\n", (xfer->tx_buf) ? "TX" : "RX",
+ len, xfer->rx_buf, xfer->tx_buf);
+
+ s3c2443_spi_chipsel(msg->spi, SPI_CS_ACTIVE);
+
+ /*
+ * Prepare the DMA-channel with the correct direction, and callback-function
+ * before sending the data to the DMA-subsystem
+ * If the TX-channel is being used, then only enable the TX-callback, otherwise
+ * use the RX-channel for the callback
+ */
+ s3c2410_dma_set_buffdone_fn(SPI_TX_CHANNEL, NULL);
+ s3c2410_dma_set_buffdone_fn(SPI_RX_CHANNEL, NULL);
+ if (xfer->tx_buf)
+ s3c2410_dma_set_buffdone_fn(SPI_TX_CHANNEL, s3c2443_spi_dma_callback);
+ if (xfer->rx_buf)
+ s3c2410_dma_set_buffdone_fn(SPI_RX_CHANNEL, s3c2443_spi_dma_callback);
+
+
+ if (xfer->rx_buf) {
+ s3c2443_spi_dma_init(hw->dma_ch_rx, S3C2443_DMA_RX, len);
+ err = s3c2410_dma_enqueue(hw->dma_ch_rx, hw, rx_dma, len);
+ if (err) {
+ printk_err("Starting the RX DMA-channel, %i\n", err);
+ s3c2410_dma_ctrl(hw->dma_ch_rx, S3C2410_DMAOP_FLUSH);
+ hw->remaining_bytes = -EBUSY;
+ goto exit_err;
+ }
+ }
+
+ if (xfer->tx_buf) {
+ s3c2443_spi_dma_init(hw->dma_ch, S3C2443_DMA_TX, len);
+ err = s3c2410_dma_enqueue(hw->dma_ch, hw, tx_dma, len);
+ if (err) {
+ printk_err("Starting the TX DMA-channel, %i\n", err);
+ hw->remaining_bytes = -EBUSY;
+ goto exit_err;
+ }
+ }
+
+ /* Init the FIFOs */
+ chcfg = readl(hw->regs + S3C2443_SPI0_CH_CFG);
+ chcfg &= ~(S3C2443_SPI0_CH_TXCH_ON | S3C2443_SPI0_CH_RXCH_ON);
+ if (xfer->tx_buf)
+ chcfg |= S3C2443_SPI0_CH_TXCH_ON;
+ if (xfer->rx_buf)
+ chcfg |= S3C2443_SPI0_CH_RXCH_ON;
+
+ writel(chcfg, hw->regs + S3C2443_SPI0_CH_CFG);
+
+ return;
+
+ /* By errors only schedule the tasklet */
+ exit_err:
+ tasklet_schedule(&hw->xmit_tasklet);
+}
+
+/* Get the head of the message queue and start the xmit */
+static void s3c2443_spi_next_message(struct spi_master *master)
+{
+ struct s3c2443_spi *hw;
+ struct spi_message *msg;
+
+ hw = master_to_hw(master);
+
+ msg = list_entry(hw->xmit_queue.next, struct spi_message, queue);
+
+ printk_debug("Starting to handle a new message %p\n", msg);
+
+ /* And start to send the new message now */
+ /* s3c2443_spi_chipsel(msg->spi, SPI_CS_ACTIVE); */
+ s3c2443_spi_next_xfer(master, msg);
+}
+
+/*
+ * This function will be called from the external SPI-layer for sending data
+ * to the bus. It will return inmediately, if another message is being
+ * transmitted, otherwise it will start sending the data to the bus
+ */
+static int s3c2443_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct s3c2443_spi *hw = spi_to_hw(spi);
+ struct spi_transfer *xfer;
+
+ if (unlikely(list_empty(&msg->transfers)))
+ return -EINVAL;
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ if (!xfer->tx_buf && !xfer->rx_buf) {
+ printk_err("Missing rx/tx buffer.\n");
+ return -EINVAL;
+ }
+ }
+
+ msg->status = -EINPROGRESS;
+ msg->actual_length = 0;
+
+ /*
+ * Add the new arrived message to the internal queue and start the
+ * transfer if the xmit-queue is IDLE
+ */
+ spin_lock(&hw->xmit_lock);
+ list_add_tail(&msg->queue, &hw->xmit_queue);
+ if (!hw->current_xfer)
+ s3c2443_spi_next_message(spi->master);
+ spin_unlock(&hw->xmit_lock);
+
+ return 0;
+}
+
+/*
+ * Normally the IRQs are not used for transferring data to the FIFOs, we use only
+ * the DMA-controller for this purpose.
+ *
+ * @FIXME: For some reasons we are not clearing all the interrupts correctly. With
+ * the activated debug prints you will see what I'm talking about.
+ * (Luis Galdos)
+ */
+static irqreturn_t s3c2443_spi_irq(int irq, void *_hw)
+{
+ struct s3c2443_spi *hw = _hw;
+ unsigned int spsta = readl(hw->regs + S3C2443_SPI0_STATUS);
+
+ spin_lock(&hw->xmit_lock);
+
+ /* Disable all the interrupts and clear the pending errors */
+ writel(0x00, hw->regs + S3C2443_SPI0_INT_EN);
+ writel(spsta, hw->regs + S3C2443_SPI0_STATUS);
+ writel(0x1f, hw->regs + S3C2443_SPI0_PENDING_CLR);
+
+ printk_debug("IRQ stat 0x%08x\n", spsta);
+
+ /*
+ * If we detect an error than force to the complete of the SPI-message by
+ * setting the number of remaining bytes to zero
+ */
+ if (spsta &
+ (S3C2443_SPI0_STUS_RX_OVERRUN_ERR | S3C2443_SPI0_STUS_RX_UNDERRUN_ERR)) {
+ hw->remaining_bytes = 0;
+ printk_err("RX error (0x%08x)\n", spsta);
+ }
+
+ if (spsta &
+ (S3C2443_SPI0_STUS_TX_OVERRUN_ERR | S3C2443_SPI0_STUS_TX_UNDERRUN_ERR)) {
+ hw->remaining_bytes = 0;
+ printk_err("TX error (0x%08x)\n", spsta);
+ }
+
+ /* Force the complete of the current transfer */
+ tasklet_schedule(&hw->xmit_tasklet);
+
+ spin_unlock(&hw->xmit_lock);
+ return IRQ_HANDLED;
+}
+
+static int __devinit s3c2443_spi_probe(struct platform_device *pdev)
+{
+ struct s3c2443_spi_info *pdata;
+ struct s3c2443_spi *hw;
+ struct spi_master *master;
+ struct resource *res;
+ int err = 0;
+
+ printk_info("Probing device with the ID %i\n", pdev->id);
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct s3c2443_spi));
+ if (master == NULL) {
+ dev_err(&pdev->dev, "No memory for spi_master\n");
+ err = -ENOMEM;
+ goto err_exit;
+ }
+
+ hw = spi_master_get_devdata(master);
+ memset(hw, 0, sizeof(struct s3c2443_spi));
+
+ hw->master = spi_master_get(master);
+ hw->pdata = pdev->dev.platform_data;
+ hw->dev = &pdev->dev;
+
+ pdata = pdev->dev.platform_data;
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ err = -ENOENT;
+ goto err_put_spi;
+ }
+
+ /* Sanity checks for the passed platform data */
+ hw->pdata = pdata;
+ if (hw->pdata->num_chipselect > 1) {
+ printk_err("Only supports one chip select (%i passed)\n",
+ hw->pdata->num_chipselect);
+ goto err_put_spi;
+ }
+
+ platform_set_drvdata(pdev, hw);
+
+ /* find and map our resources */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ printk_err("Cannot get IORESOURCE_MEM\n");
+ err = -ENOENT;
+ goto err_put_spi;
+ }
+
+ hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1,
+ pdev->name);
+ if (hw->ioarea == NULL) {
+ printk_err("Cannot reserve region\n");
+ err = -ENXIO;
+ goto err_put_spi;
+ }
+
+ hw->regs = ioremap(res->start, (res->end - res->start) + 1);
+ if (hw->regs == NULL) {
+ printk_err("Cannot map IO\n");
+ err = -ENXIO;
+ goto err_release_iomem;
+ }
+
+ hw->irq = platform_get_irq(pdev, 0);
+ if (hw->irq <= 0) {
+ printk_err("No IRQ specified? Aborting.\n");
+ err = -ENOENT;
+ goto err_unmap_iomem;
+ }
+
+ err = request_irq(hw->irq, s3c2443_spi_irq, 0, pdev->name, hw);
+ if (err) {
+ printk_err("Cannot claim IRQ\n");
+ goto err_unmap_iomem;
+ }
+
+ /* Use the passed input clock */
+ if (pdata->input_clk == S3C2443_HSSPI_INCLK_PCLK)
+ hw->clk = clk_get(&pdev->dev, "pclk");
+ else if (pdata->input_clk == S3C2443_HSSPI_INCLK_EPLL)
+ hw->clk = clk_get(&pdev->dev, "epll");
+ else {
+ printk_err("Invalid input clock passed (%i)\n", pdata->input_clk);
+ goto err_free_irq;
+ }
+
+ if (IS_ERR(hw->clk)) {
+ printk_err("No clock for device\n");
+ err = PTR_ERR(hw->clk);
+ goto err_free_irq;
+ }
+
+ /* @FIXME: For the moment, permanently enable the clock */
+ clk_enable(hw->clk);
+
+ printk_info("Input clock frequency: %lu Hz\n", clk_get_rate(hw->clk));
+
+ /* Init the internal data */
+ spin_lock_init(&hw->xmit_lock);
+ INIT_LIST_HEAD(&hw->xmit_queue);
+
+ /* Create the DMA-pool for DMA-unmapped message */
+ hw->tx_buf = dma_alloc_coherent(&pdev->dev, S3C2443_SPI_DMA_BUFFER,
+ &hw->tx_dma, GFP_DMA);
+ if (!hw->tx_buf) {
+ printk_err("Couldn't get the TX DMA-memory\n");
+ err = -ENOMEM;
+ goto err_put_clk;
+ }
+
+ hw->rx_buf = dma_alloc_coherent(&pdev->dev, S3C2443_SPI_DMA_BUFFER,
+ &hw->rx_dma, GFP_DMA);
+ if (!hw->rx_buf) {
+ printk_err("Couldn't get the RX DMA-memory\n");
+ err = -ENOMEM;
+ goto err_free_txdma;
+ }
+
+ /* We are safe, init the internal data for receiving the transfer requests */
+ tasklet_init(&hw->xmit_tasklet, s3c2443_spi_next_tasklet,
+ (unsigned long)hw);
+
+ /* Request the channel for the TX-transfers */
+ hw->dmach.name = (char *)pdev->name;
+ err = s3c2410_dma_request(SPI_TX_CHANNEL, &hw->dmach, hw);
+ if (err < 0) {
+ printk_err("TX DMA channel %i request failed\n.", SPI_TX_CHANNEL);
+ goto err_free_rxdma;
+ }
+ hw->dma_ch = SPI_TX_CHANNEL;
+ printk_debug("Got the TX DMA-channel %i (%i)\n", hw->dma_ch, SPI_TX_CHANNEL);
+
+ /* Request the channel for the RX-transfers */
+ hw->dmach_rx.name = (char *)pdev->name;
+ err = s3c2410_dma_request(SPI_RX_CHANNEL, &hw->dmach_rx, hw);
+ if (err < 0) {
+ printk_err("RX DMA channel %i request failed\n.", SPI_RX_CHANNEL);
+ goto err_free_chtx;
+ }
+ hw->dma_ch_rx = SPI_RX_CHANNEL;
+ printk_debug("Got the RX DMA-channel %i (%i)\n", hw->dma_ch_rx, SPI_RX_CHANNEL);
+
+ /* Set the callback function for the DMA-channel */
+ s3c2410_dma_set_buffdone_fn(SPI_TX_CHANNEL, NULL);
+ s3c2410_dma_set_opfn(SPI_TX_CHANNEL, NULL);
+ s3c2410_dma_set_buffdone_fn(SPI_RX_CHANNEL, NULL);
+ s3c2410_dma_set_opfn(SPI_RX_CHANNEL, NULL);
+
+ /* Setup and register the SPI master */
+ master->num_chipselect = pdata->num_chipselect;
+ master->setup = s3c2443_spi_setup;
+ master->transfer = s3c2443_spi_transfer;
+ master->bus_num = pdata->bus_num;
+ err = spi_register_master(hw->master);
+ if (err) {
+ printk_err("Failed to register the SPI master\n");
+ goto err_free_chrx;
+ }
+
+ /* Init the hardware (reset, enable clock, etc.) */
+ s3c2443_spi_hw_init(hw);
+
+ return 0;
+
+ err_free_chrx:
+ s3c2410_dma_free(SPI_RX_CHANNEL, &hw->dmach);
+
+ err_free_chtx:
+ s3c2410_dma_free(SPI_TX_CHANNEL, &hw->dmach);
+
+ err_free_rxdma:
+ dma_free_coherent(&pdev->dev, S3C2443_SPI_DMA_BUFFER, hw->rx_buf, hw->rx_dma);
+
+ err_free_txdma:
+ dma_free_coherent(&pdev->dev, S3C2443_SPI_DMA_BUFFER, hw->tx_buf, hw->tx_dma);
+
+ err_put_clk:
+ clk_disable(hw->clk);
+ clk_put(hw->clk);
+
+ err_free_irq:
+ free_irq(hw->irq, hw);
+
+ err_unmap_iomem:
+ iounmap(hw->regs);
+
+ err_release_iomem:
+ release_resource(hw->ioarea);
+ kfree(hw->ioarea);
+
+ err_put_spi:
+ spi_master_put(hw->master);;
+
+ err_exit:
+ platform_set_drvdata(pdev, NULL);
+ return err;
+}
+
+static int __devexit s3c2443_spi_remove(struct platform_device *pdev)
+{
+ struct s3c2443_spi *hw = platform_get_drvdata(pdev);
+
+ spi_unregister_master(hw->master);
+
+
+ tasklet_kill(&hw->xmit_tasklet);
+
+ clk_disable(hw->clk);
+ clk_put(hw->clk);
+
+ free_irq(hw->irq, hw);
+ iounmap(hw->regs);
+
+ release_resource(hw->ioarea);
+ kfree(hw->ioarea);
+
+ /* Free the allocated DMA-buffers */
+ dma_free_coherent(&pdev->dev, S3C2443_SPI_DMA_BUFFER, hw->tx_buf, hw->tx_dma);
+ dma_free_coherent(&pdev->dev, S3C2443_SPI_DMA_BUFFER, hw->rx_buf, hw->rx_dma);
+
+ /* Free the DMA-channels */
+ s3c2410_dma_free(SPI_TX_CHANNEL, &hw->dmach);
+ s3c2410_dma_free(SPI_RX_CHANNEL, &hw->dmach_rx);
+
+ /* Free the SPI-master */
+ spi_master_put(hw->master);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int s3c2443_spi_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+ struct s3c2443_spi *hw = platform_get_drvdata(pdev);
+
+ clk_disable(hw->clk);
+ return 0;
+}
+
+static int s3c2443_spi_resume(struct platform_device *pdev)
+{
+ struct s3c2443_spi *hw;
+ int retval;
+
+ hw = platform_get_drvdata(pdev);
+
+ /* @FIXME: Why do we need to free the DMA-channels? */
+ s3c2410_dma_free(SPI_TX_CHANNEL, &hw->dmach);
+ s3c2410_dma_free(SPI_RX_CHANNEL, &hw->dmach_rx);
+
+ retval = s3c2410_dma_request(SPI_TX_CHANNEL, &hw->dmach, hw);
+ if (retval < 0)
+ goto exit_resume;
+
+ retval = s3c2410_dma_request(SPI_RX_CHANNEL, &hw->dmach_rx, hw);
+ if (retval < 0)
+ goto exit_free_tx;
+
+ clk_enable(hw->clk);
+ return 0;
+
+exit_free_tx:
+ s3c2410_dma_free(SPI_TX_CHANNEL, &hw->dmach);
+
+exit_resume:
+ return retval;
+}
+#else
+#define s3c2443_spi_suspend NULL
+#define s3c2443_spi_resume NULL
+#endif
+
+MODULE_ALIAS("platform:s3c2443-spi");
+
+static struct platform_driver s3c2443_spi_driver = {
+ .probe = s3c2443_spi_probe,
+ .remove = __devexit_p(s3c2443_spi_remove),
+ .suspend = s3c2443_spi_suspend,
+ .resume = s3c2443_spi_resume,
+ .driver = {
+ .name = "spi-s3c2443",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s3c2443_spi_init(void)
+{
+ return platform_driver_register(&s3c2443_spi_driver);
+}
+
+static void __exit s3c2443_spi_exit(void)
+{
+ platform_driver_unregister(&s3c2443_spi_driver);
+}
+
+module_init(s3c2443_spi_init);
+module_exit(s3c2443_spi_exit);
+
+MODULE_DESCRIPTION("S3C2443 SPI Driver");
+MODULE_AUTHOR("Luis Galdos, <luis.galdos@digi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_stmp.c b/drivers/spi/spi_stmp.c
new file mode 100644
index 000000000000..a0ae2087fdbb
--- /dev/null
+++ b/drivers/spi/spi_stmp.c
@@ -0,0 +1,693 @@
+/*
+ * Freescale STMP378X SPI master driver
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <asm/dma.h>
+
+#include <mach/stmp3xxx.h>
+#include <mach/regs-ssp.h>
+#include <mach/regs-apbh.h>
+#include "spi_stmp.h"
+
+/* 0 means DMA modei(recommended, default), !0 - PIO mode */
+static int pio /* = 0 */;
+static int debug;
+
+/**
+ * stmp_spi_init_hw
+ *
+ * Initialize the SSP port
+ */
+static int stmp_spi_init_hw(struct stmp_spi *ss)
+{
+ int err;
+
+ err = stmp37xx_spi_pins_request(ss->master_dev->bus_id, ss->id);
+ if (err)
+ goto out;
+
+ ss->clk = clk_get(NULL, "ssp");
+ if (IS_ERR(ss->clk)) {
+ err = PTR_ERR(ss->clk);
+ goto out_free_pins;
+ }
+ clk_enable(ss->clk);
+
+ stmp3xxx_reset_block(ss->regs, 0);
+ stmp3xxx_dma_reset_channel(ss->dma);
+
+ return 0;
+
+out_free_pins:
+ stmp37xx_spi_pins_release(ss->master_dev->bus_id, ss->id);
+out:
+ return err;
+}
+
+static void stmp_spi_release_hw(struct stmp_spi *ss)
+{
+ if (ss->clk && !IS_ERR(ss->clk)) {
+ clk_disable(ss->clk);
+ clk_put(ss->clk);
+ }
+ stmp37xx_spi_pins_release(ss->master_dev->bus_id, ss->id);
+}
+
+static int stmp_spi_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ u8 bits_per_word;
+ u32 hz;
+ struct stmp_spi *ss /* = spi_master_get_devdata(spi->master) */;
+ u16 rate;
+
+ ss = spi_master_get_devdata(spi->master);
+
+ bits_per_word = spi->bits_per_word;
+ if (t && t->bits_per_word)
+ bits_per_word = t->bits_per_word;
+
+ /*
+ Calculate speed:
+ - by default, use maximum speed from ssp clk
+ - if device overrides it, use it
+ - if transfer specifies other speed, use transfer's one
+ */
+ hz = 1000 * ss->speed_khz / ss->divider;
+ if (spi->max_speed_hz)
+ hz = min(hz, spi->max_speed_hz);
+ if (t && t->speed_hz)
+ hz = min(hz, t->speed_hz);
+
+ if (hz == 0) {
+ dev_err(&spi->dev, "Cannot continue with zero clock\n");
+ return -EINVAL;
+ }
+
+ if (bits_per_word != 8) {
+ dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
+ __func__, bits_per_word);
+ return -EINVAL;
+ }
+
+ dev_dbg(&spi->dev, "Requested clk rate = %uHz, max = %uHz/%d = %uHz\n",
+ hz, ss->speed_khz, ss->divider,
+ ss->speed_khz * 1000 / ss->divider);
+
+ if (ss->speed_khz * 1000 / ss->divider < hz) {
+ dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n",
+ __func__, hz);
+ return -EINVAL;
+ }
+
+ rate = 1000 * ss->speed_khz/ss->divider/hz;
+
+ HW_SSP_TIMING_WR_NB(ss->regs,
+ BF_SSP_TIMING_CLOCK_DIVIDE(ss->divider) |
+ BF_SSP_TIMING_CLOCK_RATE(rate - 1));
+
+ HW_SSP_CTRL1_WR_NB(ss->regs,
+ BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) |
+ BF_SSP_CTRL1_WORD_LENGTH(BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) |
+ ((spi->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) |
+ ((spi->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0) |
+ (pio ? 0 : BM_SSP_CTRL1_DMA_ENABLE));
+
+ HW_SSP_CMD0_SET(0x00);
+
+ return 0;
+}
+
+
+static void stmp_spi_cleanup(struct spi_device *spi)
+{
+ struct stmp37xx_spi_platform_data *pdata = spi->dev.platform_data;
+
+ if (pdata && pdata->hw_release)
+ pdata->hw_release(spi);
+}
+
+/* the spi->mode bits understood by this driver: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA)
+static int stmp_spi_setup(struct spi_device *spi)
+{
+ struct stmp37xx_spi_platform_data *pdata;
+ struct stmp_spi *ss;
+ int err = 0;
+
+ ss = spi_master_get_devdata(spi->master);
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+
+ if (spi->mode & ~MODEBITS) {
+ dev_err(&spi->dev, "%s: unsupported mode bits %x\n",
+ __func__, spi->mode & ~MODEBITS);
+ err = -EINVAL;
+ goto out;
+ }
+
+ dev_dbg(&spi->dev, "%s, mode %d, %u bits/w\n",
+ __func__, spi->mode & MODEBITS, spi->bits_per_word);
+
+ pdata = spi->dev.platform_data;
+
+ if (pdata && pdata->hw_init) {
+ err = pdata->hw_init(spi);
+ if (err)
+ goto out;
+ }
+
+ err = stmp_spi_setup_transfer(spi, NULL);
+ if (err)
+ goto out2;
+ return 0;
+
+out2:
+ if (pdata)
+ pdata->hw_release(spi);
+out:
+ dev_err(&spi->dev, "Failed to setup transfer, error = %d\n", err);
+ return err;
+}
+
+static inline u32 stmp_spi_cs(unsigned cs)
+{
+ return ((cs & 1) ? BM_SSP_CTRL0_WAIT_FOR_CMD : 0) |
+ ((cs & 2) ? BM_SSP_CTRL0_WAIT_FOR_IRQ : 0);
+}
+
+static int stmp_spi_txrx_dma(struct stmp_spi *ss, int cs,
+ unsigned char *buf, dma_addr_t dma_buf, int len,
+ int *first, int *last, int write)
+{
+ u32 c0 = 0;
+ dma_addr_t spi_buf_dma = dma_buf;
+ int count, status = 0;
+ enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+
+ c0 |= (*first ? BM_SSP_CTRL0_LOCK_CS : 0);
+ c0 |= (*last ? BM_SSP_CTRL0_IGNORE_CRC : 0);
+ c0 |= (write ? 0 : BM_SSP_CTRL0_READ);
+ c0 |= BM_SSP_CTRL0_DATA_XFER;
+
+ c0 |= stmp_spi_cs(cs);
+
+ c0 |= BF_SSP_CTRL0_XFER_COUNT(len);
+
+ if (!dma_buf)
+ spi_buf_dma = dma_map_single(ss->master_dev, buf, len, dir);
+
+ ss->d.command->cmd =
+ BF_APBH_CHn_CMD_XFER_COUNT(len) |
+ BF_APBH_CHn_CMD_CMDWORDS(1) |
+ BM_APBH_CHn_CMD_WAIT4ENDCMD |
+ BM_APBH_CHn_CMD_IRQONCMPLT |
+ BF_APBH_CHn_CMD_COMMAND(
+ write ? BV_APBH_CHn_CMD_COMMAND__DMA_READ :
+ BV_APBH_CHn_CMD_COMMAND__DMA_WRITE);
+ ss->d.command->pio_words[0] = c0;
+ ss->d.command->buf_ptr = spi_buf_dma;
+
+ stmp3xxx_dma_reset_channel(ss->dma);
+ stmp3xxx_dma_clear_interrupt(ss->dma);
+ stmp3xxx_dma_enable_interrupt(ss->dma);
+ init_completion(&ss->done);
+ stmp3xxx_dma_go(ss->dma, &ss->d, 1);
+ wait_for_completion(&ss->done);
+ count = 10000;
+ while ((HW_SSP_CTRL0_RD() & BM_SSP_CTRL0_RUN) && count--)
+ continue;
+ if (count <= 0) {
+ printk(KERN_ERR"%c: timeout on line %s:%d\n",
+ write ? 'W':'C', __func__, __LINE__);
+ status = -ETIMEDOUT;
+ }
+
+ if (!dma_buf)
+ dma_unmap_single(ss->master_dev, spi_buf_dma, len, dir);
+
+ return status;
+}
+
+static inline void stmp_spi_enable(struct stmp_spi *ss)
+{
+ HW_SSP_CTRL0_SET_NB(ss->regs, BM_SSP_CTRL0_LOCK_CS);
+ HW_SSP_CTRL0_CLR_NB(ss->regs, BM_SSP_CTRL0_IGNORE_CRC);
+}
+
+static inline void stmp_spi_disable(struct stmp_spi *ss)
+{
+ HW_SSP_CTRL0_CLR_NB(ss->regs, BM_SSP_CTRL0_LOCK_CS);
+ HW_SSP_CTRL0_SET_NB(ss->regs, BM_SSP_CTRL0_IGNORE_CRC);
+}
+
+static int stmp_spi_txrx_pio(struct stmp_spi *ss, int cs,
+ unsigned char *buf, int len,
+ int *first, int *last, int write)
+{
+ int count;
+
+ if (*first) {
+ stmp_spi_enable(ss);
+ *first = 0;
+ }
+
+ HW_SSP_CTRL0_SET(stmp_spi_cs(cs));
+
+ while (len--) {
+ if (*last && len == 0) {
+ stmp_spi_disable(ss);
+ *last = 0;
+ }
+ HW_SSP_CTRL0_CLR_NB(ss->regs, BM_SSP_CTRL0_XFER_COUNT);
+ HW_SSP_CTRL0_SET_NB(ss->regs, 1); /* byte-by-byte */
+
+ if (write)
+ HW_SSP_CTRL0_CLR_NB(ss->regs, BM_SSP_CTRL0_READ);
+ else
+ HW_SSP_CTRL0_SET_NB(ss->regs, BM_SSP_CTRL0_READ);
+
+ /* Run! */
+ HW_SSP_CTRL0_SET_NB(ss->regs, BM_SSP_CTRL0_RUN);
+ count = 10000;
+ while (((HW_SSP_CTRL0_RD() & BM_SSP_CTRL0_RUN) == 0) && count--)
+ continue;
+ if (count <= 0) {
+ printk(KERN_ERR"%c: timeout on line %s:%d\n",
+ write ? 'W':'C', __func__, __LINE__);
+ break;
+ }
+
+ if (write)
+ HW_SSP_DATA_WR_NB(ss->regs, *buf);
+
+ /* Set TRANSFER */
+ HW_SSP_CTRL0_SET_NB(ss->regs, BM_SSP_CTRL0_DATA_XFER);
+
+ if (!write) {
+ count = 10000;
+ while (count-- &&
+ (HW_SSP_STATUS_RD_NB(ss->regs) &
+ BM_SSP_STATUS_FIFO_EMPTY))
+ continue;
+ if (count <= 0) {
+ printk(KERN_ERR"%c: timeout on line %s:%d\n",
+ write ? 'W':'C', __func__, __LINE__);
+ break;
+ }
+ *buf = (HW_SSP_DATA_RD_NB(ss->regs) & 0xFF);
+ }
+
+ count = 10000;
+ while ((HW_SSP_CTRL0_RD() & BM_SSP_CTRL0_RUN) && count--)
+ continue;
+ if (count <= 0) {
+ printk(KERN_ERR"%c: timeout on line %s:%d\n",
+ write ? 'W':'C', __func__, __LINE__);
+ break;
+ }
+
+ /* advance to the next byte */
+ buf++;
+ }
+ return len < 0 ? 0 : -ETIMEDOUT;
+}
+
+static int stmp_spi_handle_message(struct stmp_spi *ss, struct spi_message *m)
+{
+ int first, last;
+ struct spi_transfer *t, *tmp_t;
+ int status = 0;
+ int cs;
+
+ first = last = 0;
+
+ cs = m->spi->chip_select;
+
+ list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) {
+
+ stmp_spi_setup_transfer(m->spi, t);
+
+ if (&t->transfer_list == m->transfers.next)
+ first = !0;
+ if (&t->transfer_list == m->transfers.prev)
+ last = !0;
+ if (t->rx_buf && t->tx_buf) {
+ pr_debug("%s: cannot send and receive simultaneously\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /*
+ REVISIT:
+ here driver completely ignores setting of t->cs_change
+ */
+ if (t->tx_buf) {
+ status = pio ?
+ stmp_spi_txrx_pio(ss, cs, (void *)t->tx_buf,
+ t->len, &first, &last, 1) :
+ stmp_spi_txrx_dma(ss, cs, (void *)t->tx_buf,
+ t->tx_dma, t->len, &first, &last, 1);
+ if (debug) {
+ if (t->len < 0x10)
+ print_hex_dump_bytes("Tx ",
+ DUMP_PREFIX_OFFSET,
+ t->tx_buf, t->len);
+ else
+ pr_debug("Tx: %d bytes\n", t->len);
+ }
+ }
+ if (t->rx_buf) {
+ status = pio ?
+ stmp_spi_txrx_pio(ss, cs, t->rx_buf,
+ t->len, &first, &last, 0):
+ stmp_spi_txrx_dma(ss, cs, t->rx_buf,
+ t->rx_dma, t->len, &first, &last, 0);
+ if (debug) {
+ if (t->len < 0x10)
+ print_hex_dump_bytes("Rx ",
+ DUMP_PREFIX_OFFSET,
+ t->rx_buf, t->len);
+ else
+ pr_debug("Rx: %d bytes\n", t->len);
+ }
+ }
+
+ if (status)
+ break;
+
+ first = last = 0;
+
+ }
+ return status;
+}
+
+/**
+ * stmp_spi_handle
+ *
+ * The workhorse of the driver - it handles messages from the list
+ *
+ **/
+static void stmp_spi_handle(struct work_struct *w)
+{
+ struct stmp_spi *ss = container_of(w, struct stmp_spi, work);
+ unsigned long flags;
+ struct spi_message *m;
+
+ BUG_ON(w == NULL);
+
+ spin_lock_irqsave(&ss->lock, flags);
+ while (!list_empty(&ss->queue)) {
+ m = list_entry(ss->queue.next, struct spi_message, queue);
+ list_del_init(&m->queue);
+ spin_unlock_irqrestore(&ss->lock, flags);
+
+ m->status = stmp_spi_handle_message(ss, m);
+ if (m->complete)
+ m->complete(m->context);
+
+ spin_lock_irqsave(&ss->lock, flags);
+ }
+ spin_unlock_irqrestore(&ss->lock, flags);
+
+ return;
+}
+
+/**
+ * stmp_spi_transfer
+ *
+ * Called indirectly from spi_async, queues all the messages to
+ * spi_handle_message
+ *
+ * @spi: spi device
+ * @m: message to be queued
+**/
+static int stmp_spi_transfer(struct spi_device *spi, struct spi_message *m)
+{
+ struct stmp_spi *ss = spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ m->status = -EINPROGRESS;
+ spin_lock_irqsave(&ss->lock, flags);
+ list_add_tail(&m->queue, &ss->queue);
+ queue_work(ss->workqueue, &ss->work);
+ spin_unlock_irqrestore(&ss->lock, flags);
+ return 0;
+}
+
+static irqreturn_t stmp_spi_irq(int irq, void *dev_id)
+{
+ struct stmp_spi *ss = dev_id;
+
+ stmp3xxx_dma_clear_interrupt(ss->dma);
+ complete(&ss->done);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t stmp_spi_irq_err(int irq, void *dev_id)
+{
+ struct stmp_spi *ss = dev_id;
+ u32 c1, st;
+
+ c1 = HW_SSP_CTRL1_RD_NB(ss->regs);
+ st = HW_SSP_STATUS_RD_NB(ss->regs);
+ printk(KERN_ERR"IRQ - ERROR!, status = 0x%08X, c1 = 0x%08X\n", st, c1);
+ HW_SSP_CTRL1_CLR_NB(ss->regs, c1 & 0xCCCC0000);
+
+ return IRQ_HANDLED;
+}
+
+static int __init stmp_spi_probe(struct platform_device *dev)
+{
+ int err = 0;
+ struct spi_master *master;
+ struct stmp_spi *ss;
+ struct resource *r;
+ u32 mem;
+
+ /* Get resources(memory, IRQ) associated with the device */
+ master = spi_alloc_master(&dev->dev, sizeof(struct stmp_spi));
+
+ if (master == NULL) {
+ err = -ENOMEM;
+ goto out0;
+ }
+
+ platform_set_drvdata(dev, master);
+
+ r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ err = -ENODEV;
+ goto out_put_master;
+ }
+
+ ss = spi_master_get_devdata(master);
+ ss->master_dev = &dev->dev;
+ ss->id = dev->id;
+
+ INIT_WORK(&ss->work, stmp_spi_handle);
+ INIT_LIST_HEAD(&ss->queue);
+ spin_lock_init(&ss->lock);
+ ss->workqueue = create_singlethread_workqueue(dev->dev.bus_id);
+ master->transfer = stmp_spi_transfer;
+ master->setup = stmp_spi_setup;
+ master->cleanup = stmp_spi_cleanup;
+
+ if (!request_mem_region(r->start,
+ r->end - r->start + 1, dev->dev.bus_id)) {
+ err = -ENXIO;
+ goto out_put_master;
+ }
+ mem = r->start;
+
+ ss->regs = r->start;
+
+ ss->irq = platform_get_irq(dev, 0);
+ if (ss->irq < 0) {
+ err = -ENXIO;
+ goto out_put_master;
+ }
+
+ r = platform_get_resource(dev, IORESOURCE_DMA, 0);
+ if (r == NULL) {
+ err = -ENODEV;
+ goto out_put_master;
+ }
+
+ ss->dma = r->start;
+ err = stmp3xxx_dma_request(ss->dma, &dev->dev, dev->dev.bus_id);
+ if (err)
+ goto out_put_master;
+
+ err = stmp3xxx_dma_allocate_command(ss->dma, &ss->d);
+ if (err)
+ goto out_free_dma;
+
+ master->bus_num = dev->id;
+ master->num_chipselect = 1;
+
+ /* SPI controller initializations */
+ err = stmp_spi_init_hw(ss);
+ if (err) {
+ dev_dbg(&dev->dev, "cannot initialize hardware\n");
+ goto out_free_dma_desc;
+ }
+
+ clk_set_rate(ss->clk, 120000);
+ ss->speed_khz = clk_get_rate(ss->clk);
+ ss->divider = 2;
+ dev_info(&dev->dev, "Max possible speed %d = %ld/%d kHz\n",
+ ss->speed_khz, clk_get_rate(ss->clk), ss->divider);
+
+ /* Register for SPI Interrupt */
+ err = request_irq(ss->irq, stmp_spi_irq, 0,
+ dev->dev.bus_id, ss);
+ if (err) {
+ dev_dbg(&dev->dev, "request_irq failed, %d\n", err);
+ goto out_release_hw;
+ }
+ err = request_irq(IRQ_SSP_ERROR, stmp_spi_irq_err, IRQF_SHARED,
+ dev->dev.bus_id, ss);
+ if (err) {
+ dev_dbg(&dev->dev, "request_irq(error) failed, %d\n", err);
+ goto out_free_irq;
+ }
+
+ err = spi_register_master(master);
+ if (err) {
+ dev_dbg(&dev->dev, "cannot register spi master, %d\n", err);
+ goto out_free_irq_2;
+ }
+ dev_info(&dev->dev, "at 0x%08X mapped to 0x%08X, irq=%d, bus %d, %s\n",
+ mem, (u32)ss->regs, ss->irq,
+ master->bus_num, pio ? "PIO" : "DMA");
+ return 0;
+
+out_free_irq_2:
+ free_irq(IRQ_SSP_ERROR, ss);
+out_free_irq:
+ free_irq(ss->irq, ss);
+out_free_dma_desc:
+ stmp3xxx_dma_free_command(ss->dma, &ss->d);
+out_free_dma:
+ stmp3xxx_dma_release(ss->dma);
+out_release_hw:
+ stmp_spi_release_hw(ss);
+out_put_master:
+ spi_master_put(master);
+out0:
+ return err;
+}
+
+static int __devexit stmp_spi_remove(struct platform_device *dev)
+{
+ struct stmp_spi *ss;
+ struct spi_master *master;
+
+ master = platform_get_drvdata(dev);
+ if (master == NULL)
+ goto out0;
+ ss = spi_master_get_devdata(master);
+ if (ss == NULL)
+ goto out1;
+ free_irq(ss->irq, ss);
+ if (ss->workqueue)
+ destroy_workqueue(ss->workqueue);
+ stmp3xxx_dma_free_command(ss->dma, &ss->d);
+ stmp3xxx_dma_release(ss->dma);
+ stmp_spi_release_hw(ss);
+ platform_set_drvdata(dev, 0);
+out1:
+ spi_master_put(master);
+out0:
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stmp_spi_suspend(struct platform_device *pdev, pm_message_t pmsg)
+{
+ struct stmp_spi *ss;
+ struct spi_master *master;
+
+ master = platform_get_drvdata(pdev);
+ ss = spi_master_get_devdata(master);
+
+ ss->saved_timings = HW_SSP_TIMING_RD_NB(ss->regs);
+ clk_disable(ss->clk);
+
+ return 0;
+}
+
+static int stmp_spi_resume(struct platform_device *pdev)
+{
+ struct stmp_spi *ss;
+ struct spi_master *master;
+
+ master = platform_get_drvdata(pdev);
+ ss = spi_master_get_devdata(master);
+
+ clk_enable(ss->clk);
+ HW_SSP_CTRL0_CLR_NB(ss->regs,
+ BM_SSP_CTRL0_SFTRST | BM_SSP_CTRL0_CLKGATE);
+ HW_SSP_TIMING_SET_NB(ss->regs, ss->saved_timings);
+
+ return 0;
+}
+
+#else
+#define stmp_spi_suspend NULL
+#define stmp_spi_resume NULL
+#endif
+
+static struct platform_driver stmp_spi_driver = {
+ .probe = stmp_spi_probe,
+ .remove = __devexit_p(stmp_spi_remove),
+ .driver = {
+ .name = "stmp37xx_ssp",
+ .owner = THIS_MODULE,
+ },
+ .suspend = stmp_spi_suspend,
+ .resume = stmp_spi_resume,
+};
+
+static int __init stmp_spi_init(void)
+{
+ return platform_driver_register(&stmp_spi_driver);
+}
+
+static void __exit stmp_spi_exit(void)
+{
+ platform_driver_unregister(&stmp_spi_driver);
+}
+
+module_init(stmp_spi_init);
+module_exit(stmp_spi_exit);
+module_param(pio, int, S_IRUGO);
+module_param(debug, int, S_IRUGO);
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
+MODULE_DESCRIPTION("STMP37xx SPI/SSP");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_stmp.h b/drivers/spi/spi_stmp.h
new file mode 100644
index 000000000000..aef6fac2747e
--- /dev/null
+++ b/drivers/spi/spi_stmp.h
@@ -0,0 +1,49 @@
+/*
+ * Freescale STMP378X SPI master driver
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __SPI_STMP_H
+#define __SPI_STMP_H
+
+/* These two come from arch/arm/mach-xxxxx/spi.c */
+int stmp37xx_spi_pins_request(char *id, int ssp);
+void stmp37xx_spi_pins_release(char *id, int ssp);
+
+struct stmp_spi {
+ int id;
+
+ u32 regs; /* vaddr of the control registers */
+
+ u32 irq;
+ u32 dma;
+ struct stmp3xxx_dma_descriptor d;
+
+ u32 speed_khz;
+ u32 saved_timings;
+ u32 divider;
+
+ struct clk *clk;
+ struct device *master_dev;
+
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+ spinlock_t lock;
+ struct list_head queue;
+
+ struct completion done;
+};
+
+#endif /* __SPI_STMP_H */
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 289d81adfb9c..83babb0a1df7 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -150,4 +150,6 @@ source "drivers/usb/atm/Kconfig"
source "drivers/usb/gadget/Kconfig"
+source "drivers/usb/otg/Kconfig"
+
endif # USB_SUPPORT
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 8b7c419b876e..ee6b18a80c03 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -37,3 +37,6 @@ obj-$(CONFIG_USB) += misc/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
+
+obj-$(CONFIG_USB_OTG) += otg/
+
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index e1b42626d04d..3208360664c8 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -117,6 +117,10 @@ static inline int is_root_hub(struct usb_device *udev)
return (udev->parent == NULL);
}
+#if CONFIG_PM
+extern int usb_host_wakeup_irq(struct device *wkup_dev);
+extern void usb_host_set_wakeup(struct device *wkup_dev, bool para);
+#endif
/*-------------------------------------------------------------------------*/
/*
@@ -1725,8 +1729,17 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
*/
local_irq_save(flags);
- if (unlikely(hcd->state == HC_STATE_HALT ||
- !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
+ if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ /* if receive a remote wakeup interrrupt after suspend */
+ if (usb_host_wakeup_irq(hcd->self.controller)) {
+ /* disable remote wake up irq */
+ usb_host_set_wakeup(hcd->self.controller, false);
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ hcd->driver->irq(hcd);
+ rc = IRQ_HANDLED;
+ } else
+ rc = IRQ_NONE;
+ } else if (unlikely(hcd->state == HC_STATE_HALT)) {
rc = IRQ_NONE;
} else if (hcd->driver->irq(hcd) == IRQ_NONE) {
rc = IRQ_NONE;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b19cbfcd51da..d89645f8f680 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -37,6 +37,15 @@
#endif
#endif
+#ifdef CONFIG_ARCH_STMP3XXX
+#define STMP3XXX_USB_HOST_HACK
+#endif
+
+#ifdef STMP3XXX_USB_HOST_HACK
+#include <linux/fsl_devices.h>
+#include <mach/regs-usbphy.h>
+#endif
+
struct usb_hub {
struct device *intfdev; /* the "interface" device */
struct usb_device *hdev;
@@ -884,6 +893,17 @@ static int hub_configure(struct usb_hub *hub,
goto fail;
}
+ /*
+ * The second USB-port of the S3C2443 is generating interrupts, although
+ * it's configured for the USB-device controller, and not for the USB-host.
+ * This seems to be a hardware BUG.
+ * (Luis Galdos)
+ */
+#if (defined(CONFIG_MACH_CC9M2443JS) || defined(CONFIG_MACH_CCW9M2443JS)) && defined(CONFIG_USB_GADGET_S3C2443)
+ /* Only use one port by the internal Root-Hub (devnum = 1) */
+ if (hdev->devnum == 1)
+ hub->descriptor->bNbrPorts = 1;
+#endif
hdev->maxchild = hub->descriptor->bNbrPorts;
dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
(hdev->maxchild == 1) ? "" : "s");
@@ -1140,6 +1160,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
"hub nested too deep\n");
return -E2BIG;
}
+
+ /* With OTG enabled, suspending root hub results in gadget not
+ * working because gadget uses the same root hub. We disable
+ * this feature when OTG is selected.
+ */
+#if defined(CONFIG_PM) && defined(CONFIG_USB_EHCI_ARC_OTG)
+ hdev->autosuspend_disabled = 1;
+#endif
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
@@ -2631,6 +2659,19 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
break;
}
}
+
+#ifdef STMP3XXX_USB_HOST_HACK
+ { /*Must enable HOSTDISCONDETECT after second reset*/
+ if (port1 == 1) {
+ if (udev->speed == USB_SPEED_HIGH) {
+ HW_USBPHY_CTRL_SET(
+ BM_USBPHY_CTRL_ENHOSTDISCONDETECT
+ );
+ }
+ }
+ }
+#endif
+
if (retval)
goto fail;
@@ -2755,6 +2796,34 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
"port %d, status %04x, change %04x, %s\n",
port1, portstatus, portchange, portspeed (portstatus));
+#ifdef STMP3XXX_USB_HOST_HACK
+ {
+ /*
+ * FIXME: the USBPHY of STMP3xxx SoC has bug. The usb port power
+ * is never enabled during standard ehci reset procedure if the
+ * external device once passed plug/unplug procedure. This work-
+ * around resets and reinitiates USBPHY before the ehci port reset
+ * sequence started.
+ */
+ struct device *dev = hcd->self.controller;
+ struct fsl_usb2_platform_data *pdata;
+
+ pdata = (struct fsl_usb2_platform_data *)dev->platform_data;
+ if (dev->parent && dev->type) {
+ if (port1 == 1 && pdata->platform_init)
+ pdata->platform_init(NULL);
+ }
+ if (port1 == 1) {
+ if (!(portstatus&USB_PORT_STAT_CONNECTION)) {
+ /* Must clear HOSTDISCONDETECT when disconnect*/
+ HW_USBPHY_CTRL_CLR(
+ BM_USBPHY_CTRL_ENHOSTDISCONDETECT);
+ }
+ }
+ }
+#endif
+
+
if (hub->has_indicators) {
set_port_led(hub, port1, HUB_LED_AUTO);
hub->indicator[port1-1] = INDICATOR_AUTO;
@@ -2833,6 +2902,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
return;
}
+
for (i = 0; i < SET_CONFIG_TRIES; i++) {
/* reallocate for each attempt, since references
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index dd4cd5a51370..86a74f1c43f1 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -208,17 +208,6 @@ config USB_OMAP
default USB_GADGET
select USB_GADGET_SELECTED
-config USB_OTG
- boolean "OTG Support"
- depends on USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD
- help
- The most notable feature of USB OTG is support for a
- "Dual-Role" device, which can act as either a device
- or a host. The initial role choice can be changed
- later, when two dual-role devices talk to each other.
-
- Select this only if your OMAP board has a Mini-AB connector.
-
config USB_GADGET_PXA25X
boolean "PXA 25x or IXP 4xx"
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
@@ -270,6 +259,21 @@ config USB_PXA27X
default USB_GADGET
select USB_GADGET_SELECTED
+config USB_GADGET_S3C2443
+ boolean "S3C2443 USB Device Controller"
+ depends on S3C2443_USB_PHY_UDC
+ select USB_GADGET_SELECTED
+ help
+ Samsung's S3C2443 with an integrated full speed USB 2.0 device
+ controller.
+
+config USB_S3C2443
+ tristate
+ depends on USB_GADGET_S3C2443
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+ select USB_GADGET_DUALSPEED
+
config USB_GADGET_S3C2410
boolean "S3C2410 USB Device Controller"
depends on ARCH_S3C2410
@@ -290,7 +294,7 @@ config USB_S3C2410
config USB_S3C2410_DEBUG
boolean "S3C2410 udc debug messages"
depends on USB_GADGET_S3C2410
-
+
#
# Controllers available in both integrated and discrete versions
#
@@ -419,6 +423,31 @@ config USB_GOKU
default USB_GADGET
select USB_GADGET_SELECTED
+config USB_GADGET_ARC
+ boolean "Freescale USB Device Controller"
+ depends on ARCH_MXC || ARCH_STMP3XXX
+ select USB_GADGET_DUALSPEED if USB_GADGET_FSL_1504 || USB_GADGET_FSL_UTMI
+ help
+ Some Freescale processors have a USBOTG controller,
+ which supports device mode.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "arc_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_STATIC_IRAM_PPH
+ bool "Apply static IRAM patch"
+ depends on USB_GADGET_ARC && (ARCH_MX37 || ARCH_MX3 || ARCH_MX25)
+ help
+ Apply static IRAM patch to peripheral driver.
+
+config USB_ARC
+ tristate
+ depends on USB_GADGET_ARC
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
+
#
# LAST -- dummy/emulated controller
@@ -466,6 +495,69 @@ config USB_GADGET_DUALSPEED
Means that gadget drivers should include extra descriptors
and code to handle dual-speed controllers.
+config USB_GADGET_ARC_OTG
+ bool "Support for DR peripheral port on Freescale controller"
+ depends on USB_GADGET_ARC
+ default y
+ help
+ Enable support for the Freescale Dual Role port in peripheral mode.
+
+choice
+ prompt "Select transceiver for DR port"
+ depends on USB_GADGET_ARC_OTG
+ help
+ Choose the transceiver to use with the Freescale DR port.
+
+config USB_GADGET_FSL_MC13783
+ bool "Freescale MC13783"
+ depends on !USB_EHCI_FSL_1301 && !USB_EHCI_FSL_1504 && !USB_EHCI_FSL_UTMI && !MACH_MX25_3DS
+ ---help---
+ Enable support for the Full Speed Freescale MC13783 transceiver.
+
+ The mx27ads, mx31ads and mx32ads boards require modifications
+ to support this transceiver.
+
+config USB_GADGET_FSL_1301
+ bool "Philips ISP1301"
+ depends on !USB_EHCI_FSL_MC13783 && !USB_EHCI_FSL_1504 && !USB_EHCI_FSL_UTMI && !MACH_MX25_3DS
+ ---help---
+ Enable support for the Full Speed Philips ISP1301 transceiver.
+
+ This is the factory default for the mx27ads board.
+ The mx31ads and mx32ads boards require modifications
+ to support this transceiver.
+
+config USB_GADGET_FSL_1504
+ bool "Philips ISP1504"
+ depends on !USB_EHCI_FSL_MC13783 && !USB_EHCI_FSL_1301 && !USB_EHCI_FSL_UTMI && !MACH_MX25_3DS
+ ---help---
+ Enable support for the High Speed Philips ISP1504 transceiver.
+
+ This is the factory default for the mx31ads and mx32ads boards.
+ The mx27ads board requires modifications to support this transceiver.
+
+config USB_GADGET_FSL_UTMI
+ bool "On-chip UTMI"
+ depends on !USB_EHCI_FSL_MC13783 && !USB_EHCI_FSL_1301 && !USB_EHCI_FSL_1504
+ ---help---
+ Enable support for the High Speed Philips ISP1504 transceiver.
+
+ This is the factory default for the mx35 board.
+
+endchoice
+
+config USB_OTG
+ boolean "OTG Support"
+ depends on (USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD) || \
+ (USB_GADGET_ARC && (ARCH_MXC || ARCH_STMP3XXX) && USB_EHCI_HCD)
+ help
+ The most notable feature of USB OTG is support for a
+ "Dual-Role" device, which can act as either a device
+ or a host. The initial role choice can be changed
+ later, when two dual-role devices talk to each other.
+
+ Select this only if your OMAP board has a Mini-AB connector.
+
#
# USB Gadget Drivers
#
@@ -601,6 +693,12 @@ config USB_FILE_STORAGE
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_file_storage".
+config STMP_UTP
+ bool "UTP over Storage Gadget"
+ depends on USB_FILE_STORAGE && ARCH_STMP3XXX
+ help
+ Freescale's extension to MSC protocol
+
config USB_FILE_STORAGE_TEST
bool "File-backed Storage Gadget testing version"
depends on USB_FILE_STORAGE
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index bd4041b47dce..1c6e3c29c0d4 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -14,11 +14,13 @@ obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
+obj-$(CONFIG_USB_S3C2443) += s3c2443_udc.o
obj-$(CONFIG_USB_AT91) += at91_udc.o
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
obj-$(CONFIG_USB_M66592) += m66592-udc.o
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
+obj-$(CONFIG_USB_ARC) += arcotg_udc.o
#
# USB gadget drivers
diff --git a/drivers/usb/gadget/arcotg_udc.c b/drivers/usb/gadget/arcotg_udc.c
new file mode 100644
index 000000000000..367a9b059196
--- /dev/null
+++ b/drivers/usb/gadget/arcotg_udc.c
@@ -0,0 +1,3045 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#undef DEBUG
+#undef VERBOSE
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/dmapool.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/dma.h>
+#include <asm/cacheflush.h>
+
+#include "arcotg_udc.h"
+#include <mach/arc_otg.h>
+
+#define DRIVER_DESC "ARC USBOTG Device Controller driver"
+#define DRIVER_AUTHOR "Freescale Semiconductor"
+#define DRIVER_VERSION "1 August 2005"
+
+#ifdef CONFIG_PPC_MPC512x
+#define BIG_ENDIAN_DESC
+#endif
+
+#ifdef BIG_ENDIAN_DESC
+#define cpu_to_hc32(x) (x)
+#define hc32_to_cpu(x) (x)
+#else
+#define cpu_to_hc32(x) cpu_to_le32((x))
+#define hc32_to_cpu(x) le32_to_cpu((x))
+#endif
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+static const char driver_name[] = "fsl-usb2-udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+volatile static struct usb_dr_device *dr_regs;
+volatile static struct usb_sys_interface *usb_sys_regs;
+
+/* it is initialized in probe() */
+static struct fsl_udc *udc_controller;
+
+#ifdef POSTPONE_FREE_LAST_DTD
+static struct ep_td_struct *last_free_td;
+#endif
+static const struct usb_endpoint_descriptor
+fsl_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD,
+};
+static const size_t g_iram_size = IRAM_TD_PPH_SIZE;
+
+typedef int (*dev_sus)(struct device *dev, pm_message_t state);
+typedef int (*dev_res) (struct device *dev);
+static int udc_suspend(struct fsl_udc *udc);
+static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state);
+static int fsl_udc_resume(struct platform_device *pdev);
+static void fsl_ep_fifo_flush(struct usb_ep *_ep);
+
+#ifdef CONFIG_USB_OTG
+/* Get platform resource from OTG driver */
+extern struct resource *otg_get_resources(void);
+#endif
+
+extern void fsl_platform_set_test_mode(struct fsl_usb2_platform_data *pdata, enum usb_test_mode mode);
+
+static inline void
+dr_wake_up_enable(struct fsl_udc *udc, bool enable)
+{
+ struct fsl_usb2_platform_data *pdata;
+ pdata = udc->pdata;
+
+ if (device_can_wakeup(udc_controller->gadget.dev.parent)) {
+ if (pdata->wake_up_enable)
+ pdata->wake_up_enable(pdata, enable);
+ }
+}
+
+#ifdef CONFIG_PPC32
+#define fsl_readl(addr) in_le32((addr))
+#define fsl_writel(addr, val32) out_le32((val32), (addr))
+#else
+#define fsl_readl(addr) readl((addr))
+#define fsl_writel(addr, val32) writel((addr), (val32))
+#endif
+
+/********************************************************************
+ * Internal Used Function
+********************************************************************/
+
+#ifdef DUMP_QUEUES
+static void dump_ep_queue(struct fsl_ep *ep)
+{
+ int ep_index;
+ struct fsl_req *req;
+ struct ep_td_struct *dtd;
+
+ if (list_empty(&ep->queue)) {
+ pr_debug("udc: empty\n");
+ return;
+ }
+
+ ep_index = ep_index(ep) * 2 + ep_is_in(ep);
+ pr_debug("udc: ep=0x%p index=%d\n", ep, ep_index);
+
+ list_for_each_entry(req, &ep->queue, queue) {
+ pr_debug("udc: req=0x%p dTD count=%d\n", req, req->dtd_count);
+ pr_debug("udc: dTD head=0x%p tail=0x%p\n", req->head,
+ req->tail);
+
+ dtd = req->head;
+
+ while (dtd) {
+ if (le32_to_cpu(dtd->next_td_ptr) & DTD_NEXT_TERMINATE)
+ break; /* end of dTD list */
+
+ dtd = dtd->next_td_virt;
+ }
+ }
+}
+#else
+static inline void dump_ep_queue(struct fsl_ep *ep)
+{
+}
+#endif
+
+/*-----------------------------------------------------------------
+ * done() - retire a request; caller blocked irqs
+ * @status : request status to be set, only works when
+ * request is still in progress.
+ *--------------------------------------------------------------*/
+static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
+{
+ struct fsl_udc *udc = NULL;
+ unsigned char stopped = ep->stopped;
+ struct ep_td_struct *curr_td, *next_td;
+ int j;
+
+ udc = (struct fsl_udc *)ep->udc;
+ /* Removed the req from fsl_ep->queue */
+ list_del_init(&req->queue);
+
+ /* req.status should be set as -EINPROGRESS in ep_queue() */
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ /* Free dtd for the request */
+ next_td = req->head;
+ for (j = 0; j < req->dtd_count; j++) {
+ curr_td = next_td;
+ if (j != req->dtd_count - 1) {
+ next_td = curr_td->next_td_virt;
+#ifdef POSTPONE_FREE_LAST_DTD
+ dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
+ } else {
+ if (last_free_td != NULL)
+ dma_pool_free(udc->td_pool, last_free_td,
+ last_free_td->td_dma);
+ last_free_td = curr_td;
+ }
+#else
+ }
+
+ dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
+#endif
+ }
+
+ if (USE_MSC_WR(req->req.length)) {
+ req->req.dma -= 1;
+ memmove(req->req.buf, req->req.buf + 1, MSC_BULK_CB_WRAP_LEN);
+ }
+
+ if (req->mapped) {
+ dma_unmap_single(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ } else
+ dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+
+ if (status && (status != -ESHUTDOWN))
+ VDBG("complete %s req %p stat %d len %u/%u",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+
+ ep->stopped = 1;
+
+ spin_unlock(&ep->udc->lock);
+ /* complete() is from gadget layer,
+ * eg fsg->bulk_in_complete() */
+ if (req->req.complete)
+ req->req.complete(&ep->ep, &req->req);
+
+ spin_lock(&ep->udc->lock);
+ ep->stopped = stopped;
+}
+
+/*-----------------------------------------------------------------
+ * nuke(): delete all requests related to this ep
+ * called with spinlock held
+ *--------------------------------------------------------------*/
+static void nuke(struct fsl_ep *ep, int status)
+{
+ ep->stopped = 1;
+
+ /* Flush fifo */
+ fsl_ep_fifo_flush(&ep->ep);
+
+ /* Whether this eq has request linked */
+ while (!list_empty(&ep->queue)) {
+ struct fsl_req *req = NULL;
+
+ req = list_entry(ep->queue.next, struct fsl_req, queue);
+ done(ep, req, status);
+ }
+ dump_ep_queue(ep);
+}
+
+/*------------------------------------------------------------------
+ Internal Hardware related function
+ ------------------------------------------------------------------*/
+
+static void dr_phy_low_power_mode(struct fsl_udc *udc, bool enable)
+{
+ u32 temp;
+
+ if (!device_can_wakeup(udc_controller->gadget.dev.parent))
+ return;
+ temp = fsl_readl(&dr_regs->portsc1);
+ if ((enable) && !(temp & PORTSCX_PHY_LOW_POWER_SPD)) {
+ temp |= PORTSCX_PHY_LOW_POWER_SPD;
+ fsl_writel(temp, &dr_regs->portsc1);
+
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(false);
+ } else if ((!enable) && (temp & PORTSCX_PHY_LOW_POWER_SPD)) {
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(true);
+
+ temp &= ~PORTSCX_PHY_LOW_POWER_SPD;
+ fsl_writel(temp, &dr_regs->portsc1);
+ }
+}
+
+
+
+static int dr_controller_setup(struct fsl_udc *udc)
+{
+ unsigned int tmp = 0, portctrl = 0;
+ unsigned int __attribute((unused)) ctrl = 0;
+ unsigned long timeout;
+ struct fsl_usb2_platform_data *pdata;
+
+#define FSL_UDC_RESET_TIMEOUT 1000
+
+ /* before here, make sure dr_regs has been initialized */
+ if (!udc)
+ return -EINVAL;
+ pdata = udc->pdata;
+
+ /* Stop and reset the usb controller */
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ tmp &= ~USB_CMD_RUN_STOP;
+ fsl_writel(tmp, &dr_regs->usbcmd);
+
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ tmp |= USB_CMD_CTRL_RESET;
+ fsl_writel(tmp, &dr_regs->usbcmd);
+
+ /* Wait for reset to complete */
+ timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
+ while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
+ if (time_after(jiffies, timeout)) {
+ ERR("udc reset timeout! \n");
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ }
+
+ /* Set the controller as device mode */
+ tmp = fsl_readl(&dr_regs->usbmode);
+ tmp &= ~USB_MODE_CTRL_MODE_MASK; /* clear mode bits */
+ tmp |= USB_MODE_CTRL_MODE_DEVICE;
+ /* Disable Setup Lockout */
+ tmp |= USB_MODE_SETUP_LOCK_OFF;
+ if (pdata->es)
+ tmp |= USB_MODE_ES;
+ fsl_writel(tmp, &dr_regs->usbmode);
+
+ fsl_platform_set_device_mode(pdata);
+
+ /* Clear the setup status */
+ fsl_writel(0, &dr_regs->usbsts);
+
+ tmp = udc->ep_qh_dma;
+ tmp &= USB_EP_LIST_ADDRESS_MASK;
+ fsl_writel(tmp, &dr_regs->endpointlistaddr);
+
+ VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x",
+ (int)udc->ep_qh, (int)tmp,
+ fsl_readl(&dr_regs->endpointlistaddr));
+
+ /* Config PHY interface */
+ portctrl = fsl_readl(&dr_regs->portsc1);
+ portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
+ switch (udc->phy_mode) {
+ case FSL_USB2_PHY_ULPI:
+ portctrl |= PORTSCX_PTS_ULPI;
+ break;
+ case FSL_USB2_PHY_UTMI_WIDE:
+ portctrl |= PORTSCX_PTW_16BIT;
+ /* fall through */
+ case FSL_USB2_PHY_UTMI:
+ portctrl |= PORTSCX_PTS_UTMI;
+ break;
+ case FSL_USB2_PHY_SERIAL:
+ portctrl |= PORTSCX_PTS_FSLS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ fsl_writel(portctrl, &dr_regs->portsc1);
+
+ if (pdata->change_ahb_burst) {
+ /* if usb should not work in default INCRx mode */
+ tmp = fsl_readl(&dr_regs->sbuscfg);
+ tmp = (tmp & ~0x07) | pdata->ahb_burst_mode;
+ fsl_writel(tmp, &dr_regs->sbuscfg);
+ }
+
+ if (pdata->have_sysif_regs) {
+ /* Config control enable i/o output, cpu endian register */
+ ctrl = __raw_readl(&usb_sys_regs->control);
+ ctrl |= USB_CTRL_IOENB;
+ __raw_writel(ctrl, &usb_sys_regs->control);
+ }
+
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+ /* Turn on cache snooping hardware, since some PowerPC platforms
+ * wholly rely on hardware to deal with cache coherent. */
+
+ if (pdata->have_sysif_regs) {
+ /* Setup Snooping for all the 4GB space */
+ tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */
+ __raw_writel(tmp, &usb_sys_regs->snoop1);
+ tmp |= 0x80000000; /* starts from 0x8000000, size 2G */
+ __raw_writel(tmp, &usb_sys_regs->snoop2);
+ }
+#endif
+
+ return 0;
+}
+
+/* Enable DR irq and set controller to run state */
+static void dr_controller_run(struct fsl_udc *udc)
+{
+ u32 temp;
+
+ fsl_platform_pullup_enable(udc->pdata);
+
+ /* Enable DR irq reg */
+ temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN
+ | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN
+ | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN;
+
+ fsl_writel(temp, &dr_regs->usbintr);
+
+ /* If PHY clock is disabled, enable it */
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(1);
+
+ if (device_can_wakeup(udc_controller->gadget.dev.parent)) {
+ /* enable BSV irq */
+ temp = fsl_readl(&dr_regs->otgsc);
+ temp |= OTGSC_B_SESSION_VALID_IRQ_EN;
+ fsl_writel(temp, &dr_regs->otgsc);
+ }
+
+ /* If vbus not on and used low power mode */
+ if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_B_SESSION_VALID)
+ && device_can_wakeup(udc_controller->gadget.dev.parent)) {
+ /* enable wake up */
+ dr_wake_up_enable(udc, true);
+ /* Set stopped before low power mode */
+ udc->stopped = 1;
+ /* close PHY clock */
+ dr_phy_low_power_mode(udc, true);
+ printk(KERN_INFO "udc enter low power mode \n");
+ } else {
+ /*
+ add some delay for USB timing issue. USB may be
+ recognize as FS device
+ during USB gadget remote wake up function
+ */
+ mdelay(100);
+ /* Clear stopped bit */
+ udc->stopped = 0;
+ /* Set controller to Run */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ temp |= USB_CMD_RUN_STOP;
+ fsl_writel(temp, &dr_regs->usbcmd);
+ printk(KERN_INFO "udc run \n");
+ }
+
+ return;
+}
+
+static void dr_controller_stop(struct fsl_udc *udc)
+{
+ unsigned int tmp;
+
+ pr_debug("%s\n", __func__);
+
+ /* if we're in OTG mode, and the Host is currently using the port,
+ * stop now and don't rip the controller out from under the
+ * ehci driver
+ */
+ if (udc->gadget.is_otg) {
+ if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) {
+ pr_debug("udc: Leaving early\n");
+ return;
+ }
+ }
+
+ /* disable all INTR */
+ fsl_writel(0, &dr_regs->usbintr);
+
+ /* disable wake up */
+ dr_wake_up_enable(udc, false);
+ /* disable BSV irq */
+ tmp = fsl_readl(&dr_regs->otgsc);
+ tmp &= ~OTGSC_B_SESSION_VALID_IRQ_EN;
+ fsl_writel(tmp, &dr_regs->otgsc);
+
+ /* Set stopped bit for isr */
+ udc->stopped = 1;
+
+ /* disable IO output */
+/* usb_sys_regs->control = 0; */
+
+ fsl_platform_pullup_disable(udc->pdata);
+
+ /* set controller to Stop */
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ tmp &= ~USB_CMD_RUN_STOP;
+ fsl_writel(tmp, &dr_regs->usbcmd);
+
+ return;
+}
+
+void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type)
+{
+ unsigned int tmp_epctrl = 0;
+
+ tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (dir) {
+ if (ep_num)
+ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+ tmp_epctrl |= EPCTRL_TX_ENABLE;
+ tmp_epctrl |= ((unsigned int)(ep_type)
+ << EPCTRL_TX_EP_TYPE_SHIFT);
+ } else {
+ if (ep_num)
+ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+ tmp_epctrl |= EPCTRL_RX_ENABLE;
+ tmp_epctrl |= ((unsigned int)(ep_type)
+ << EPCTRL_RX_EP_TYPE_SHIFT);
+ }
+
+ fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
+}
+
+static void
+dr_ep_change_stall(unsigned char ep_num, unsigned char dir, int value)
+{
+ u32 tmp_epctrl = 0;
+
+ tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+
+ if (value) {
+ /* set the stall bit */
+ if (dir)
+ tmp_epctrl |= EPCTRL_TX_EP_STALL;
+ else
+ tmp_epctrl |= EPCTRL_RX_EP_STALL;
+ } else {
+ /* clear the stall bit and reset data toggle */
+ if (dir) {
+ tmp_epctrl &= ~EPCTRL_TX_EP_STALL;
+ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+ } else {
+ tmp_epctrl &= ~EPCTRL_RX_EP_STALL;
+ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
+ }
+ }
+ fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
+}
+
+/* Get stall status of a specific ep
+ Return: 0: not stalled; 1:stalled */
+static int dr_ep_get_stall(unsigned char ep_num, unsigned char dir)
+{
+ u32 epctrl;
+
+ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (dir)
+ return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0;
+ else
+ return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0;
+}
+
+/********************************************************************
+ Internal Structure Build up functions
+********************************************************************/
+
+/*------------------------------------------------------------------
+* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH
+ * @zlt: Zero Length Termination Select (1: disable; 0: enable)
+ * @mult: Mult field
+ ------------------------------------------------------------------*/
+static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
+ unsigned char dir, unsigned char ep_type,
+ unsigned int max_pkt_len,
+ unsigned int zlt, unsigned char mult)
+{
+ struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir];
+ unsigned int tmp = 0;
+
+ /* set the Endpoint Capabilites in QH */
+ switch (ep_type) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ /* Interrupt On Setup (IOS). for control ep */
+ tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+ | EP_QUEUE_HEAD_IOS;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+ | (mult << EP_QUEUE_HEAD_MULT_POS);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
+ break;
+ default:
+ VDBG("error ep type is %d", ep_type);
+ return;
+ }
+ if (zlt)
+ tmp |= EP_QUEUE_HEAD_ZLT_SEL;
+ p_QH->max_pkt_length = cpu_to_hc32(tmp);
+
+ return;
+}
+
+/* Setup qh structure and ep register for ep0. */
+static void ep0_setup(struct fsl_udc *udc)
+{
+ /* the intialization of an ep includes: fields in QH, Regs,
+ * fsl_ep struct */
+ struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL,
+ USB_MAX_CTRL_PAYLOAD, 0, 0);
+ struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL,
+ USB_MAX_CTRL_PAYLOAD, 0, 0);
+ dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL);
+ dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL);
+
+ return;
+
+}
+
+/***********************************************************************
+ Endpoint Management Functions
+***********************************************************************/
+
+/*-------------------------------------------------------------------------
+ * when configurations are set, or when interface settings change
+ * for example the do_set_interface() in gadget layer,
+ * the driver will enable or disable the relevant endpoints
+ * ep0 doesn't use this routine. It is always enabled.
+-------------------------------------------------------------------------*/
+static int fsl_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct fsl_udc *udc = NULL;
+ struct fsl_ep *ep = NULL;
+ unsigned short max = 0;
+ unsigned char mult = 0, zlt;
+ int retval = -EINVAL;
+ unsigned long flags = 0;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+
+ pr_debug("udc: %s ep.name=%s\n", __func__, ep->ep.name);
+ /* catch various bogus parameters */
+ if (!_ep || !desc || ep->desc
+ || (desc->bDescriptorType != USB_DT_ENDPOINT))
+ return -EINVAL;
+
+ udc = ep->udc;
+
+ if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
+ return -ESHUTDOWN;
+
+ max = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* Disable automatic zlp generation. Driver is reponsible to indicate
+ * explicitly through req->req.zero. This is needed to enable multi-td
+ * request. */
+ zlt = 1;
+
+ /* Assume the max packet size from gadget is always correct */
+ switch (desc->bmAttributes & 0x03) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ /* mult = 0. Execute N Transactions as demonstrated by
+ * the USB variable length packet protocol where N is
+ * computed using the Maximum Packet Length (dQH) and
+ * the Total Bytes field (dTD) */
+ mult = 0;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ /* Calculate transactions needed for high bandwidth iso */
+ mult = (unsigned char)(1 + ((max >> 11) & 0x03));
+ max = max & 0x8ff; /* bit 0~10 */
+ /* 3 transactions at most */
+ if (mult > 3)
+ goto en_done;
+ break;
+ default:
+ goto en_done;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+ ep->ep.maxpacket = max;
+ ep->desc = desc;
+ ep->stopped = 0;
+
+ /* Controller related setup */
+ /* Init EPx Queue Head (Ep Capabilites field in QH
+ * according to max, zlt, mult) */
+ struct_ep_qh_setup(udc, (unsigned char) ep_index(ep),
+ (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
+ ? USB_SEND : USB_RECV),
+ (unsigned char) (desc->bmAttributes
+ & USB_ENDPOINT_XFERTYPE_MASK),
+ max, zlt, mult);
+
+ /* Init endpoint ctrl register */
+ dr_ep_setup((unsigned char) ep_index(ep),
+ (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
+ ? USB_SEND : USB_RECV),
+ (unsigned char) (desc->bmAttributes
+ & USB_ENDPOINT_XFERTYPE_MASK));
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ retval = 0;
+
+ VDBG("enabled %s (ep%d%s) maxpacket %d", ep->ep.name,
+ ep->desc->bEndpointAddress & 0x0f,
+ (desc->bEndpointAddress & USB_DIR_IN)
+ ? "in" : "out", max);
+en_done:
+ return retval;
+}
+
+/*---------------------------------------------------------------------
+ * @ep : the ep being unconfigured. May not be ep0
+ * Any pending and uncomplete req will complete with status (-ESHUTDOWN)
+*---------------------------------------------------------------------*/
+static int fsl_ep_disable(struct usb_ep *_ep)
+{
+ struct fsl_udc *udc = NULL;
+ struct fsl_ep *ep = NULL;
+ unsigned long flags = 0;
+ u32 epctrl;
+ int ep_num;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+ if (!_ep || !ep->desc) {
+ VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
+ return -EINVAL;
+ }
+
+ /* disable ep on controller */
+ ep_num = ep_index(ep);
+ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (ep_is_in(ep))
+ epctrl &= ~EPCTRL_TX_ENABLE;
+ else
+ epctrl &= ~EPCTRL_RX_ENABLE;
+ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+
+ udc = (struct fsl_udc *)ep->udc;
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* nuke all pending requests (does flush) */
+ nuke(ep, -ESHUTDOWN);
+
+ ep->desc = 0;
+ ep->stopped = 1;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ VDBG("disabled %s OK", _ep->name);
+ return 0;
+}
+
+/*---------------------------------------------------------------------
+ * allocate a request object used by this endpoint
+ * the main operation is to insert the req->queue to the eq->queue
+ * Returns the request, or null if one could not be allocated
+*---------------------------------------------------------------------*/
+static struct usb_request *
+fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct fsl_req *req = NULL;
+
+ req = kzalloc(sizeof *req, gfp_flags);
+ if (!req)
+ return NULL;
+
+ req->req.dma = DMA_ADDR_INVALID;
+ pr_debug("udc: req=0x%p set req.dma=0x%x\n", req, req->req.dma);
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fsl_req *req = NULL;
+
+ req = container_of(_req, struct fsl_req, req);
+
+ if (_req)
+ kfree(req);
+}
+
+static void update_qh(struct fsl_req *req)
+{
+ struct fsl_ep *ep = req->ep;
+ int i = ep_index(ep) * 2 + ep_is_in(ep);
+ u32 temp;
+ struct ep_queue_head *dQH = &ep->udc->ep_qh[i];
+
+ /* Write dQH next pointer and terminate bit to 0 */
+ temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
+ if (NEED_IRAM(req->ep)) {
+ /* set next dtd stop bit,ensure only one dtd in this list */
+ req->cur->next_td_ptr |= cpu_to_hc32(DTD_NEXT_TERMINATE);
+ temp = req->cur->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
+ }
+ dQH->next_dtd_ptr = cpu_to_hc32(temp);
+ /* Clear active and halt bit */
+ temp = cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
+ | EP_QUEUE_HEAD_STATUS_HALT));
+ dQH->size_ioc_int_sts &= temp;
+
+ /* Prime endpoint by writing 1 to ENDPTPRIME */
+ temp = ep_is_in(ep)
+ ? (1 << (ep_index(ep) + 16))
+ : (1 << (ep_index(ep)));
+ fsl_writel(temp, &dr_regs->endpointprime);
+}
+
+/*-------------------------------------------------------------------------*/
+static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
+{
+ u32 temp, bitmask, tmp_stat;
+
+ /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr);
+ VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */
+
+ bitmask = ep_is_in(ep)
+ ? (1 << (ep_index(ep) + 16))
+ : (1 << (ep_index(ep)));
+
+ /* check if the pipe is empty */
+ if (!(list_empty(&ep->queue))) {
+ /* Add td to the end */
+ struct fsl_req *lastreq;
+ lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
+ if (NEED_IRAM(ep)) {
+ /* only one dtd in dqh */
+ lastreq->tail->next_td_ptr =
+ cpu_to_hc32(req->head->td_dma | DTD_NEXT_TERMINATE);
+ goto out;
+ } else {
+ lastreq->tail->next_td_ptr =
+ cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK);
+ }
+ /* Read prime bit, if 1 goto done */
+ if (fsl_readl(&dr_regs->endpointprime) & bitmask)
+ goto out;
+ do {
+ /* Set ATDTW bit in USBCMD */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp | USB_CMD_ATDTW, &dr_regs->usbcmd);
+
+ /* Read correct status bit */
+ tmp_stat = fsl_readl(&dr_regs->endptstatus) & bitmask;
+
+ } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_ATDTW));
+
+ /* Write ATDTW bit to 0 */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd);
+
+ if (tmp_stat)
+ goto out;
+ }
+ update_qh(req);
+out:
+ return 0;
+}
+
+/* Fill in the dTD structure
+ * @req: request that the transfer belongs to
+ * @length: return actually data length of the dTD
+ * @dma: return dma address of the dTD
+ * @is_last: return flag if it is the last dTD of the request
+ * return: pointer to the built dTD */
+static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
+ dma_addr_t *dma, int *is_last)
+{
+ u32 swap_temp;
+ struct ep_td_struct *dtd;
+
+ /* how big will this transfer be? */
+ *length = min(req->req.length - req->req.actual,
+ (unsigned)EP_MAX_LENGTH_TRANSFER);
+ if (NEED_IRAM(req->ep))
+ *length = min(*length, g_iram_size);
+ dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma);
+ if (dtd == NULL)
+ return dtd;
+
+ dtd->td_dma = *dma;
+ /* Clear reserved field */
+ swap_temp = hc32_to_cpu(dtd->size_ioc_sts);
+ swap_temp &= ~DTD_RESERVED_FIELDS;
+ dtd->size_ioc_sts = cpu_to_hc32(swap_temp);
+
+ /* Init all of buffer page pointers */
+ swap_temp = (u32) (req->req.dma + req->req.actual);
+ if (NEED_IRAM(req->ep))
+ swap_temp = (u32) (req->req.dma);
+ dtd->buff_ptr0 = cpu_to_hc32(swap_temp);
+ dtd->buff_ptr1 = cpu_to_hc32(swap_temp + 0x1000);
+ dtd->buff_ptr2 = cpu_to_hc32(swap_temp + 0x2000);
+ dtd->buff_ptr3 = cpu_to_hc32(swap_temp + 0x3000);
+ dtd->buff_ptr4 = cpu_to_hc32(swap_temp + 0x4000);
+
+ req->req.actual += *length;
+
+ /* zlp is needed if req->req.zero is set */
+ if (req->req.zero) {
+ if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+ *is_last = 1;
+ else
+ *is_last = 0;
+ } else if (req->req.length == req->req.actual)
+ *is_last = 1;
+ else
+ *is_last = 0;
+
+ if ((*is_last) == 0)
+ VDBG("multi-dtd request!\n");
+ /* Fill in the transfer size; set active bit */
+ swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
+
+ /* Enable interrupt for the last dtd of a request */
+ if (*is_last && !req->req.no_interrupt)
+ swap_temp |= DTD_IOC;
+ if (NEED_IRAM(req->ep))
+ swap_temp |= DTD_IOC;
+
+ dtd->size_ioc_sts = cpu_to_hc32(swap_temp);
+
+ mb();
+
+ VDBG("length = %d address= 0x%x", *length, (int)*dma);
+
+ return dtd;
+}
+
+/* Generate dtd chain for a request */
+static int fsl_req_to_dtd(struct fsl_req *req)
+{
+ unsigned count;
+ int is_last;
+ int is_first = 1;
+ struct ep_td_struct *last_dtd = NULL, *dtd;
+ dma_addr_t dma;
+
+ if (NEED_IRAM(req->ep)) {
+ req->oridma = req->req.dma;
+ /* here, replace user buffer to iram buffer */
+ if (ep_is_in(req->ep)) {
+ req->req.dma = req->ep->udc->iram_buffer[1];
+ if ((list_empty(&req->ep->queue))) {
+ /* copy data only when no bulk in transfer is
+ running */
+ memcpy((char *)req->ep->udc->iram_buffer_v[1],
+ req->req.buf, min(req->req.length,
+ g_iram_size));
+ }
+ } else {
+ req->req.dma = req->ep->udc->iram_buffer[0];
+ }
+ }
+
+ if (USE_MSC_WR(req->req.length))
+ req->req.dma += 1;
+
+ do {
+ dtd = fsl_build_dtd(req, &count, &dma, &is_last);
+ if (dtd == NULL)
+ return -ENOMEM;
+
+ if (is_first) {
+ is_first = 0;
+ req->head = dtd;
+ } else {
+ last_dtd->next_td_ptr = cpu_to_hc32(dma);
+ last_dtd->next_td_virt = dtd;
+ }
+ last_dtd = dtd;
+
+ req->dtd_count++;
+ } while (!is_last);
+
+ dtd->next_td_ptr = cpu_to_hc32(DTD_NEXT_TERMINATE);
+ req->cur = req->head;
+ req->tail = dtd;
+
+ return 0;
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int
+fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
+ struct fsl_req *req = container_of(_req, struct fsl_req, req);
+ struct fsl_udc *udc;
+ unsigned long flags;
+ int is_iso = 0;
+
+ /* catch various bogus parameters */
+ if (!_req || !req->req.buf || (ep_index(ep)
+ && !list_empty(&req->queue))) {
+ VDBG("%s, bad params\n", __func__);
+ return -EINVAL;
+ }
+ if (!_ep || (!ep->desc && ep_index(ep))) {
+ VDBG("%s, bad ep\n", __func__);
+ return -EINVAL;
+ }
+ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ if (req->req.length > ep->ep.maxpacket)
+ return -EMSGSIZE;
+ is_iso = 1;
+ }
+
+ udc = ep->udc;
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ req->ep = ep;
+
+ /* map virtual address to hardware */
+ if (req->req.dma == DMA_ADDR_INVALID) {
+ req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+ req->req.buf,
+ req->req.length, ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->mapped = 1;
+ } else {
+ dma_sync_single_for_device(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->mapped = 0;
+ }
+
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->dtd_count = 0;
+ if (NEED_IRAM(ep)) {
+ req->last_one = 0;
+ req->buffer_offset = 0;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* build dtds and push them to device queue */
+ if (!fsl_req_to_dtd(req)) {
+ fsl_queue_td(ep, req);
+ } else {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -ENOMEM;
+ }
+
+ /* irq handler advances the queue */
+ if (req != NULL)
+ list_add_tail(&req->queue, &ep->queue);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
+ struct fsl_req *req;
+ unsigned long flags;
+ int ep_num, stopped, ret = 0;
+ u32 epctrl;
+
+ if (!_ep || !_req)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->udc->lock, flags);
+ stopped = ep->stopped;
+
+ /* Stop the ep before we deal with the queue */
+ ep->stopped = 1;
+ ep_num = ep_index(ep);
+ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (ep_is_in(ep))
+ epctrl &= ~EPCTRL_TX_ENABLE;
+ else
+ epctrl &= ~EPCTRL_RX_ENABLE;
+ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+
+ /* make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* The request is in progress, or completed but not dequeued */
+ if (ep->queue.next == &req->queue) {
+ _req->status = -ECONNRESET;
+ fsl_ep_fifo_flush(_ep); /* flush current transfer */
+
+ /* The request isn't the last request in this ep queue */
+ if (req->queue.next != &ep->queue) {
+ struct ep_queue_head *qh;
+ struct fsl_req *next_req;
+
+ qh = ep->qh;
+ next_req = list_entry(req->queue.next, struct fsl_req,
+ queue);
+
+ /* Point the QH to the first TD of next request */
+ fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr);
+ }
+
+ /* The request hasn't been processed, patch up the TD chain */
+ } else {
+ struct fsl_req *prev_req;
+
+ prev_req = list_entry(req->queue.prev, struct fsl_req, queue);
+ fsl_writel(fsl_readl(&req->tail->next_td_ptr),
+ &prev_req->tail->next_td_ptr);
+
+ }
+
+ done(ep, req, -ECONNRESET);
+
+ /* Enable EP */
+out: epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (ep_is_in(ep))
+ epctrl |= EPCTRL_TX_ENABLE;
+ else
+ epctrl |= EPCTRL_RX_ENABLE;
+ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
+ ep->stopped = stopped;
+
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+ return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------
+ * modify the endpoint halt feature
+ * @ep: the non-isochronous endpoint being stalled
+ * @value: 1--set halt 0--clear halt
+ * Returns zero, or a negative error code.
+*----------------------------------------------------------------*/
+static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct fsl_ep *ep = NULL;
+ unsigned long flags = 0;
+ int status = -EOPNOTSUPP; /* operation not supported */
+ unsigned char ep_dir = 0, ep_num = 0;
+ struct fsl_udc *udc = NULL;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+ udc = ep->udc;
+ if (!_ep || !ep->desc) {
+ status = -EINVAL;
+ goto out;
+ }
+
+ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ status = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Attempt to halt IN ep will fail if any transfer requests
+ * are still queue */
+ if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+ status = -EAGAIN;
+ goto out;
+ }
+
+ status = 0;
+ ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+ ep_num = (unsigned char)(ep_index(ep));
+ spin_lock_irqsave(&ep->udc->lock, flags);
+ dr_ep_change_stall(ep_num, ep_dir, value);
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+ if (ep_index(ep) == 0) {
+ udc->ep0_dir = 0;
+ }
+out:
+ VDBG(" %s %s halt stat %d", ep->ep.name,
+ value ? "set" : "clear", status);
+
+ return status;
+}
+
+static int arcotg_fifo_status(struct usb_ep *_ep)
+{
+ struct fsl_ep *ep;
+ struct fsl_udc *udc;
+ int size = 0;
+ u32 bitmask;
+ struct ep_queue_head *d_qh;
+
+ ep = container_of(_ep, struct fsl_ep, ep);
+ if (!_ep || (!ep->desc && ep_index(ep) != 0))
+ return -ENODEV;
+
+ udc = (struct fsl_udc *)ep->udc;
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ d_qh = &ep->udc->ep_qh[ep_index(ep) * 2 + ep_is_in(ep)];
+
+ bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) :
+ (1 << (ep_index(ep)));
+
+ if (fsl_readl(&dr_regs->endptstatus) & bitmask)
+ size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE)
+ >> DTD_LENGTH_BIT_POS;
+
+ pr_debug("%s %u\n", __func__, size);
+ return size;
+}
+
+static void fsl_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct fsl_ep *ep;
+ int ep_num, ep_dir;
+ u32 bits;
+ unsigned long timeout;
+#define FSL_UDC_FLUSH_TIMEOUT 1000
+
+ if (!_ep) {
+ return;
+ } else {
+ ep = container_of(_ep, struct fsl_ep, ep);
+ if (!ep->desc)
+ return;
+ }
+ ep_num = ep_index(ep);
+ ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
+
+ if (ep_num == 0)
+ bits = (1 << 16) | 1;
+ else if (ep_dir == USB_SEND)
+ bits = 1 << (16 + ep_num);
+ else
+ bits = 1 << ep_num;
+
+ timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT;
+ do {
+ fsl_writel(bits, &dr_regs->endptflush);
+
+ /* Wait until flush complete */
+ while (fsl_readl(&dr_regs->endptflush)) {
+ if (time_after(jiffies, timeout)) {
+ ERR("ep flush timeout\n");
+ return;
+ }
+ cpu_relax();
+ }
+ /* See if we need to flush again */
+ } while (fsl_readl(&dr_regs->endptstatus) & bits);
+}
+
+static struct usb_ep_ops fsl_ep_ops = {
+ .enable = fsl_ep_enable,
+ .disable = fsl_ep_disable,
+
+ .alloc_request = fsl_alloc_request,
+ .free_request = fsl_free_request,
+
+ .queue = fsl_ep_queue,
+ .dequeue = fsl_ep_dequeue,
+
+ .set_halt = fsl_ep_set_halt,
+ .fifo_status = arcotg_fifo_status,
+ .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */
+};
+
+/*-------------------------------------------------------------------------
+ Gadget Driver Layer Operations
+-------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------
+ * Get the current frame number (from DR frame_index Reg )
+ *----------------------------------------------------------------------*/
+static int fsl_get_frame(struct usb_gadget *gadget)
+{
+ return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS);
+}
+
+/*-----------------------------------------------------------------------
+ * Tries to wake up the host connected to this gadget
+ -----------------------------------------------------------------------*/
+static int fsl_wakeup(struct usb_gadget *gadget)
+{
+ struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget);
+ u32 portsc;
+
+ /* Remote wakeup feature not enabled by host */
+ if (!udc->remote_wakeup)
+ return -ENOTSUPP;
+
+ portsc = fsl_readl(&dr_regs->portsc1);
+ /* not suspended? */
+ if (!(portsc & PORTSCX_PORT_SUSPEND))
+ return 0;
+ /* trigger force resume */
+ portsc |= PORTSCX_PORT_FORCE_RESUME;
+ fsl_writel(portsc, &dr_regs->portsc1);
+ return 0;
+}
+
+static int can_pullup(struct fsl_udc *udc)
+{
+ return udc->driver && udc->softconnect && udc->vbus_active;
+}
+
+/* Notify controller that VBUS is powered, Called by whatever
+ detects VBUS sessions */
+static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct fsl_udc *udc;
+ unsigned long flags;
+
+ udc = container_of(gadget, struct fsl_udc, gadget);
+ spin_lock_irqsave(&udc->lock, flags);
+ VDBG("VBUS %s\n", is_active ? "on" : "off");
+ udc->vbus_active = (is_active != 0);
+ if (can_pullup(udc))
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+ else
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/* constrain controller's VBUS power usage
+ * This call is used by gadget drivers during SET_CONFIGURATION calls,
+ * reporting how much power the device may consume. For example, this
+ * could affect how quickly batteries are recharged.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+ struct fsl_udc *udc;
+
+ udc = container_of(gadget, struct fsl_udc, gadget);
+ if (udc->transceiver)
+ return otg_set_power(udc->transceiver, mA);
+ return -ENOTSUPP;
+}
+
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int fsl_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct fsl_udc *udc;
+
+ udc = container_of(gadget, struct fsl_udc, gadget);
+ udc->softconnect = (is_on != 0);
+ if (can_pullup(udc))
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+ else
+ fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
+ &dr_regs->usbcmd);
+
+ return 0;
+}
+
+/* defined in gadget.h */
+static struct usb_gadget_ops fsl_gadget_ops = {
+ .get_frame = fsl_get_frame,
+ .wakeup = fsl_wakeup,
+/* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */
+ .vbus_session = fsl_vbus_session,
+ .vbus_draw = fsl_vbus_draw,
+ .pullup = fsl_pullup,
+};
+
+/* Set protocol stall on ep0, protocol stall will automatically be cleared
+ on new transaction */
+static void ep0stall(struct fsl_udc *udc)
+{
+ u32 tmp;
+
+ /* must set tx and rx to stall at the same time */
+ tmp = fsl_readl(&dr_regs->endptctrl[0]);
+ tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
+ fsl_writel(tmp, &dr_regs->endptctrl[0]);
+ udc->ep0_dir = 0;
+}
+
+/* Prime a status phase for ep0 */
+static int ep0_prime_status(struct fsl_udc *udc, int direction)
+{
+ struct fsl_req *req = udc->status_req;
+ struct fsl_ep *ep;
+ int status = 0;
+
+ if (direction == EP_DIR_IN)
+ udc->ep0_dir = USB_DIR_IN;
+ else
+ udc->ep0_dir = USB_DIR_OUT;
+
+ ep = &udc->eps[0];
+
+ req->ep = ep;
+ req->req.length = 0;
+ req->req.status = -EINPROGRESS;
+
+ status = fsl_ep_queue(&ep->ep, &req->req, GFP_ATOMIC);
+ return status;
+}
+
+static inline int udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe)
+{
+ struct fsl_ep *ep = get_ep_by_pipe(udc, pipe);
+
+ if (!ep->name)
+ return 0;
+
+ nuke(ep, -ESHUTDOWN);
+
+ return 0;
+}
+
+/*
+ * ch9 Set address
+ */
+static void ch9setaddress(struct fsl_udc *udc, u16 value, u16 index, u16 length)
+{
+ /* Save the new address to device struct */
+ udc->device_address = (u8) value;
+ /* Update usb state */
+ udc->usb_state = USB_STATE_ADDRESS;
+ /* Status phase */
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ ep0stall(udc);
+}
+
+/*
+ * ch9 Get status
+ */
+static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
+ u16 index, u16 length)
+{
+ u16 tmp = 0; /* Status, cpu endian */
+
+ struct fsl_req *req;
+ struct fsl_ep *ep;
+ int status = 0;
+
+ ep = &udc->eps[0];
+
+ if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+ /* Get device status */
+ tmp = 1 << USB_DEVICE_SELF_POWERED;
+ tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+ /* Get interface status */
+ /* We don't have interface information in udc driver */
+ tmp = 0;
+ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+ /* Get endpoint status */
+ struct fsl_ep *target_ep;
+
+ target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index));
+
+ /* stall if endpoint doesn't exist */
+ if (!target_ep->desc)
+ goto stall;
+ tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep))
+ << USB_ENDPOINT_HALT;
+ }
+
+ udc->ep0_dir = USB_DIR_IN;
+ /* Borrow the per device data_req */
+ /* status_req had been used to prime status */
+ req = udc->data_req;
+ /* Fill in the reqest structure */
+ *((u16 *) req->req.buf) = cpu_to_le16(tmp);
+ req->ep = ep;
+ req->req.length = 2;
+
+ status = fsl_ep_queue(&ep->ep, &req->req, GFP_ATOMIC);
+ if (status) {
+ udc_reset_ep_queue(udc, 0);
+ ERR("Can't respond to getstatus request \n");
+ goto stall;
+ }
+ return;
+stall:
+ ep0stall(udc);
+
+}
+
+static void setup_received_irq(struct fsl_udc *udc,
+ struct usb_ctrlrequest *setup)
+{
+ u16 wValue = le16_to_cpu(setup->wValue);
+ u16 wIndex = le16_to_cpu(setup->wIndex);
+ u16 wLength = le16_to_cpu(setup->wLength);
+
+ udc_reset_ep_queue(udc, 0);
+
+ if (wLength) {
+ int dir;
+ dir = EP_DIR_IN;
+ if (setup->bRequestType & USB_DIR_IN) {
+ dir = EP_DIR_OUT;
+ }
+ if (ep0_prime_status(udc, dir))
+ ep0stall(udc);
+ }
+ /* We process some stardard setup requests here */
+ switch (setup->bRequest) {
+ case USB_REQ_GET_STATUS:
+ /* Data+Status phase from udc */
+ if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+ != (USB_DIR_IN | USB_TYPE_STANDARD))
+ break;
+ ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength);
+ return;
+
+ case USB_REQ_SET_ADDRESS:
+ /* Status phase from udc */
+ if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
+ | USB_RECIP_DEVICE))
+ break;
+ ch9setaddress(udc, wValue, wIndex, wLength);
+ return;
+
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ /* Status phase from udc */
+ {
+ int rc = -EOPNOTSUPP;
+ u16 ptc = 0;
+
+ if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK))
+ == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) {
+ int pipe = get_pipe_by_windex(wIndex);
+ struct fsl_ep *ep;
+
+ if (wValue != 0 || wLength != 0 || pipe > udc->max_ep)
+ break;
+ ep = get_ep_by_pipe(udc, pipe);
+
+ spin_unlock(&udc->lock);
+ rc = fsl_ep_set_halt(&ep->ep,
+ (setup->bRequest == USB_REQ_SET_FEATURE)
+ ? 1 : 0);
+ spin_lock(&udc->lock);
+
+ } else if ((setup->bRequestType & (USB_RECIP_MASK
+ | USB_TYPE_MASK)) == (USB_RECIP_DEVICE
+ | USB_TYPE_STANDARD)) {
+ /* Note: The driver has not include OTG support yet.
+ * This will be set when OTG support is added */
+ if (setup->wValue == USB_DEVICE_TEST_MODE)
+ ptc = setup->wIndex >> 8;
+ else if (gadget_is_otg(&udc->gadget)) {
+ if (setup->bRequest ==
+ USB_DEVICE_B_HNP_ENABLE)
+ udc->gadget.b_hnp_enable = 1;
+ else if (setup->bRequest ==
+ USB_DEVICE_A_HNP_SUPPORT)
+ udc->gadget.a_hnp_support = 1;
+ else if (setup->bRequest ==
+ USB_DEVICE_A_ALT_HNP_SUPPORT)
+ udc->gadget.a_alt_hnp_support = 1;
+ }
+ rc = 0;
+ } else
+ break;
+
+ if (rc == 0) {
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ ep0stall(udc);
+ }
+ if (ptc) {
+ u32 tmp;
+
+ mdelay(10);
+ fsl_platform_set_test_mode(udc->pdata, ptc);
+ tmp = fsl_readl(&dr_regs->portsc1) | (ptc << 16);
+ fsl_writel(tmp, &dr_regs->portsc1);
+ printk(KERN_INFO "udc: switch to test mode 0x%x.\n", ptc);
+ }
+
+ return;
+ }
+
+ default:
+ break;
+ }
+
+ /* Requests handled by gadget */
+ if (wLength) {
+ /* Data phase from gadget, status phase from udc */
+ udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+ ? USB_DIR_IN : USB_DIR_OUT;
+ spin_unlock(&udc->lock);
+ if (udc->driver->setup(&udc->gadget,
+ &udc->local_setup_buff) < 0) {
+ /* cancel status phase */
+ udc_reset_ep_queue(udc, 0);
+ ep0stall(udc);
+ }
+ } else {
+ /* No data phase, IN status from gadget */
+ udc->ep0_dir = USB_DIR_IN;
+ spin_unlock(&udc->lock);
+ if (udc->driver->setup(&udc->gadget,
+ &udc->local_setup_buff) < 0)
+ ep0stall(udc);
+ }
+ spin_lock(&udc->lock);
+}
+
+/* Process request for Data or Status phase of ep0
+ * prime status phase if needed */
+static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,
+ struct fsl_req *req)
+{
+ if (udc->usb_state == USB_STATE_ADDRESS) {
+ /* Set the new address */
+ u32 new_address = (u32) udc->device_address;
+ fsl_writel(new_address << USB_DEVICE_ADDRESS_BIT_POS,
+ &dr_regs->deviceaddr);
+ }
+
+ done(ep0, req, 0);
+}
+
+/* Tripwire mechanism to ensure a setup packet payload is extracted without
+ * being corrupted by another incoming setup packet */
+static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr)
+{
+ u32 temp;
+ struct ep_queue_head *qh;
+ struct fsl_usb2_platform_data *pdata = udc->pdata;
+
+ qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT];
+
+ /* Clear bit in ENDPTSETUPSTAT */
+ temp = fsl_readl(&dr_regs->endptsetupstat);
+ fsl_writel(temp | (1 << ep_num), &dr_regs->endptsetupstat);
+
+ /* while a hazard exists when setup package arrives */
+ do {
+ /* Set Setup Tripwire */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd);
+
+ /* Copy the setup packet to local buffer */
+ if (pdata->le_setup_buf) {
+ u32 *p = (u32 *)buffer_ptr;
+ u32 *s = (u32 *)qh->setup_buffer;
+
+ /* Convert little endian setup buffer to CPU endian */
+ *p++ = le32_to_cpu(*s++);
+ *p = le32_to_cpu(*s);
+ } else {
+ memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
+ }
+ } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW));
+
+ /* Clear Setup Tripwire */
+ temp = fsl_readl(&dr_regs->usbcmd);
+ fsl_writel(temp & ~USB_CMD_SUTW, &dr_regs->usbcmd);
+}
+
+static void iram_process_ep_complete(struct fsl_req *curr_req,
+ int cur_transfer)
+{
+ char *buf;
+ u32 len;
+ int in = ep_is_in(curr_req->ep);
+
+ if (in)
+ buf = (char *)udc_controller->iram_buffer_v[1];
+ else
+ buf = (char *)udc_controller->iram_buffer_v[0];
+
+ if (curr_req->cur->next_td_ptr == cpu_to_hc32(DTD_NEXT_TERMINATE)
+ || (cur_transfer < g_iram_size)
+ || (curr_req->req.length == curr_req->req.actual))
+ curr_req->last_one = 1;
+
+ if (curr_req->last_one) {
+ /* the last transfer */
+ if (!in) {
+ memcpy(curr_req->req.buf + curr_req->buffer_offset, buf,
+ cur_transfer);
+ }
+ if (curr_req->tail->next_td_ptr !=
+ cpu_to_hc32(DTD_NEXT_TERMINATE)) {
+ /* have next request,queue it */
+ struct fsl_req *next_req;
+ next_req =
+ list_entry(curr_req->queue.next,
+ struct fsl_req, queue);
+ if (in)
+ memcpy(buf, next_req->req.buf,
+ min(g_iram_size, next_req->req.length));
+ update_qh(next_req);
+ }
+ curr_req->req.dma = curr_req->oridma;
+ } else {
+ /* queue next dtd */
+ /* because had next dtd, so should finish */
+ /* tranferring g_iram_size data */
+ curr_req->buffer_offset += g_iram_size;
+ /* pervious set stop bit,now clear it */
+ curr_req->cur->next_td_ptr &= ~cpu_to_hc32(DTD_NEXT_TERMINATE);
+ curr_req->cur = curr_req->cur->next_td_virt;
+ if (in) {
+ len =
+ min(curr_req->req.length - curr_req->buffer_offset,
+ g_iram_size);
+ memcpy(buf, curr_req->req.buf + curr_req->buffer_offset,
+ len);
+ } else {
+ memcpy(curr_req->req.buf + curr_req->buffer_offset -
+ g_iram_size, buf, g_iram_size);
+ }
+ update_qh(curr_req);
+ }
+}
+
+/* process-ep_req(): free the completed Tds for this req */
+static int process_ep_req(struct fsl_udc *udc, int pipe,
+ struct fsl_req *curr_req)
+{
+ struct ep_td_struct *curr_td;
+ int td_complete, actual, remaining_length, j, tmp;
+ int status = 0;
+ int errors = 0;
+ struct ep_queue_head *curr_qh = &udc->ep_qh[pipe];
+ int direction = pipe % 2;
+ int total = 0, real_len;
+
+ curr_td = curr_req->head;
+ td_complete = 0;
+ actual = curr_req->req.length;
+ real_len = curr_req->req.length;
+
+ for (j = 0; j < curr_req->dtd_count; j++) {
+ remaining_length = (hc32_to_cpu(curr_td->size_ioc_sts)
+ & DTD_PACKET_SIZE)
+ >> DTD_LENGTH_BIT_POS;
+ if (NEED_IRAM(curr_req->ep)) {
+ if (real_len >= g_iram_size) {
+ actual = g_iram_size;
+ real_len -= g_iram_size;
+ } else { /* the last packet */
+ actual = real_len;
+ curr_req->last_one = 1;
+ }
+ }
+ actual -= remaining_length;
+ total += actual;
+
+ errors = hc32_to_cpu(curr_td->size_ioc_sts) & DTD_ERROR_MASK;
+ if (errors) {
+ if (errors & DTD_STATUS_HALTED) {
+ ERR("dTD error %08x QH=%d\n", errors, pipe);
+ /* Clear the errors and Halt condition */
+ tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts);
+ tmp &= ~errors;
+ curr_qh->size_ioc_int_sts = cpu_to_hc32(tmp);
+ status = -EPIPE;
+ /* FIXME: continue with next queued TD? */
+
+ break;
+ }
+ if (errors & DTD_STATUS_DATA_BUFF_ERR) {
+ VDBG("Transfer overflow");
+ status = -EPROTO;
+ break;
+ } else if (errors & DTD_STATUS_TRANSACTION_ERR) {
+ VDBG("ISO error");
+ status = -EILSEQ;
+ break;
+ } else
+ ERR("Unknown error has occured (0x%x)!\r\n",
+ errors);
+
+ } else if (hc32_to_cpu(curr_td->size_ioc_sts)
+ & DTD_STATUS_ACTIVE) {
+ VDBG("Request not complete");
+ status = REQ_UNCOMPLETE;
+ return status;
+ } else if (remaining_length) {
+ if (direction) {
+ VDBG("Transmit dTD remaining length not zero");
+ status = -EPROTO;
+ break;
+ } else {
+ td_complete++;
+ break;
+ }
+ } else {
+ td_complete++;
+ VDBG("dTD transmitted successful ");
+ }
+ if (NEED_IRAM(curr_req->ep))
+ if (curr_td->
+ next_td_ptr & cpu_to_hc32(DTD_NEXT_TERMINATE))
+ break;
+ if (j != curr_req->dtd_count - 1)
+ curr_td = (struct ep_td_struct *)curr_td->next_td_virt;
+ }
+
+ if (status)
+ return status;
+ curr_req->req.actual = total;
+ if (NEED_IRAM(curr_req->ep))
+ iram_process_ep_complete(curr_req, actual);
+ return 0;
+}
+
+/* Process a DTD completion interrupt */
+static void dtd_complete_irq(struct fsl_udc *udc)
+{
+ u32 bit_pos;
+ int i, ep_num, direction, bit_mask, status;
+ struct fsl_ep *curr_ep;
+ struct fsl_req *curr_req, *temp_req;
+
+ /* Clear the bits in the register */
+ bit_pos = fsl_readl(&dr_regs->endptcomplete);
+ fsl_writel(bit_pos, &dr_regs->endptcomplete);
+
+ if (!bit_pos)
+ return;
+
+ for (i = 0; i < udc->max_ep * 2; i++) {
+ ep_num = i >> 1;
+ direction = i % 2;
+
+ bit_mask = 1 << (ep_num + 16 * direction);
+
+ if (!(bit_pos & bit_mask))
+ continue;
+
+ curr_ep = get_ep_by_pipe(udc, i);
+
+ /* If the ep is configured */
+ if (curr_ep->name == NULL) {
+ INFO("Invalid EP?");
+ continue;
+ }
+
+ /* process the req queue until an uncomplete request */
+ list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue,
+ queue) {
+ status = process_ep_req(udc, i, curr_req);
+
+ VDBG("status of process_ep_req= %d, ep = %d",
+ status, ep_num);
+ if (status == REQ_UNCOMPLETE)
+ break;
+ /* write back status to req */
+ curr_req->req.status = status;
+
+ if (ep_num == 0) {
+ ep0_req_complete(udc, curr_ep, curr_req);
+ break;
+ } else {
+ if (NEED_IRAM(curr_ep)) {
+ if (curr_req->last_one)
+ done(curr_ep, curr_req, status);
+ /* only check the 1th req */
+ break;
+ } else
+ done(curr_ep, curr_req, status);
+ }
+ }
+ dump_ep_queue(curr_ep);
+ }
+}
+
+/* Process a port change interrupt */
+static void port_change_irq(struct fsl_udc *udc)
+{
+ u32 speed;
+
+ if (udc->bus_reset)
+ udc->bus_reset = 0;
+
+ /* Bus resetting is finished */
+ if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) {
+ /* Get the speed */
+ speed = (fsl_readl(&dr_regs->portsc1)
+ & PORTSCX_PORT_SPEED_MASK);
+ switch (speed) {
+ case PORTSCX_PORT_SPEED_HIGH:
+ udc->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case PORTSCX_PORT_SPEED_FULL:
+ udc->gadget.speed = USB_SPEED_FULL;
+ break;
+ case PORTSCX_PORT_SPEED_LOW:
+ udc->gadget.speed = USB_SPEED_LOW;
+ break;
+ default:
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ break;
+ }
+ }
+
+ /* Update USB state */
+ if (!udc->resume_state)
+ udc->usb_state = USB_STATE_DEFAULT;
+}
+
+/* Process suspend interrupt */
+static void suspend_irq(struct fsl_udc *udc)
+{
+ pr_debug("%s\n", __func__);
+
+ udc->resume_state = udc->usb_state;
+ udc->usb_state = USB_STATE_SUSPENDED;
+
+ /* report suspend to the driver, serial.c does not support this */
+ if (udc->driver->suspend)
+ udc->driver->suspend(&udc->gadget);
+}
+
+/* Process Wake up interrupt */
+static void wake_up_irq(struct fsl_udc *udc)
+{
+ pr_debug("%s\n", __func__);
+
+ /* disable wake up irq */
+ dr_wake_up_enable(udc_controller, false);
+ if (udc_controller->pdata->usb_clock_for_pm)
+ udc_controller->pdata->usb_clock_for_pm(true);
+ udc->stopped = 0;
+}
+
+static void bus_resume(struct fsl_udc *udc)
+{
+ udc->usb_state = udc->resume_state;
+ udc->resume_state = 0;
+
+ /* report resume to the driver, serial.c does not support this */
+ if (udc->driver->resume)
+ udc->driver->resume(&udc->gadget);
+}
+
+/* Clear up all ep queues */
+static int reset_queues(struct fsl_udc *udc)
+{
+ u8 pipe;
+
+ for (pipe = 0; pipe < udc->max_pipes; pipe++)
+ udc_reset_ep_queue(udc, pipe);
+
+ /* report disconnect; the driver is already quiesced */
+ udc->driver->disconnect(&udc->gadget);
+
+ return 0;
+}
+
+/* Process reset interrupt */
+static void reset_irq(struct fsl_udc *udc)
+{
+ u32 temp;
+
+ /* Clear the device address */
+ temp = fsl_readl(&dr_regs->deviceaddr);
+ fsl_writel(temp & ~USB_DEVICE_ADDRESS_MASK, &dr_regs->deviceaddr);
+
+ udc->device_address = 0;
+
+ /* Clear usb state */
+ udc->resume_state = 0;
+ udc->ep0_dir = 0;
+ udc->remote_wakeup = 0; /* default to 0 on reset */
+ udc->gadget.b_hnp_enable = 0;
+ udc->gadget.a_hnp_support = 0;
+ udc->gadget.a_alt_hnp_support = 0;
+
+ /* Clear all the setup token semaphores */
+ temp = fsl_readl(&dr_regs->endptsetupstat);
+ fsl_writel(temp, &dr_regs->endptsetupstat);
+
+ /* Clear all the endpoint complete status bits */
+ temp = fsl_readl(&dr_regs->endptcomplete);
+ fsl_writel(temp, &dr_regs->endptcomplete);
+
+ /* Write 1s to the flush register */
+ fsl_writel(0xffffffff, &dr_regs->endptflush);
+
+ if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
+ VDBG("Bus reset");
+ /* Bus is reseting */
+ udc->bus_reset = 1;
+ /* Reset all the queues, include XD, dTD, EP queue
+ * head and TR Queue */
+ reset_queues(udc);
+ udc->usb_state = USB_STATE_DEFAULT;
+ } else {
+ VDBG("Controller reset");
+ /* initialize usb hw reg except for regs for EP, not
+ * touch usbintr reg */
+ dr_controller_setup(udc);
+
+ /* Reset all internal used Queues */
+ reset_queues(udc);
+
+ ep0_setup(udc);
+
+ /* Enable DR IRQ reg, Set Run bit, change udc state */
+ dr_controller_run(udc);
+ udc->usb_state = USB_STATE_ATTACHED;
+ }
+}
+
+/* if wakup udc, return true; else return false*/
+bool try_wake_up_udc(struct fsl_udc *udc)
+{
+ u32 irq_src;
+
+ /* when udc is stopped, only handle wake up irq */
+ if (udc->stopped) {
+ if (!device_can_wakeup(&(udc->pdata->pdev->dev)))
+ return false;
+ /* check to see if wake up irq */
+ irq_src = fsl_readl(&dr_regs->usbctrl);
+ if (irq_src & USB_CTRL_OTG_WUIR) {
+ wake_up_irq(udc);
+ }
+ }
+
+ if (!device_can_wakeup(udc_controller->gadget.dev.parent))
+ return true;
+
+ /* check if Vbus change irq */
+ irq_src = fsl_readl(&dr_regs->otgsc);
+ if (irq_src & OTGSC_B_SESSION_VALID_IRQ_STS) {
+ u32 tmp;
+ fsl_writel(irq_src, &dr_regs->otgsc);
+ tmp = fsl_readl(&dr_regs->usbcmd);
+ /* check BSV bit to see if fall or rise */
+ if (irq_src & OTGSC_B_SESSION_VALID) {
+ udc->stopped = 0;
+ fsl_writel(tmp | USB_CMD_RUN_STOP, &dr_regs->usbcmd);
+ printk(KERN_INFO "udc out low power mode\n");
+ } else {
+ printk(KERN_INFO "udc enter low power mode \n");
+ fsl_writel(tmp & ~USB_CMD_RUN_STOP, &dr_regs->usbcmd);
+ /* enable wake up */
+ dr_wake_up_enable(udc, true);
+ udc->stopped = 1;
+ /* close USB PHY clock */
+ dr_phy_low_power_mode(udc, true);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * USB device controller interrupt handler
+ */
+static irqreturn_t fsl_udc_irq(int irq, void *_udc)
+{
+ struct fsl_udc *udc = _udc;
+ u32 irq_src;
+ irqreturn_t status = IRQ_NONE;
+ unsigned long flags;
+
+ if (try_wake_up_udc(udc) == false)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr);
+ /* Clear notification bits */
+ fsl_writel(irq_src, &dr_regs->usbsts);
+
+ /* VDBG("irq_src [0x%8x]", irq_src); */
+
+ /* Need to resume? */
+ if (udc->usb_state == USB_STATE_SUSPENDED)
+ if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0)
+ bus_resume(udc);
+
+ /* USB Interrupt */
+ if (irq_src & USB_STS_INT) {
+ VDBG("Packet int");
+ /* Setup package, we only support ep0 as control ep */
+ if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
+ tripwire_handler(udc, 0,
+ (u8 *) (&udc->local_setup_buff));
+ setup_received_irq(udc, &udc->local_setup_buff);
+ status = IRQ_HANDLED;
+ }
+
+ /* completion of dtd */
+ if (fsl_readl(&dr_regs->endptcomplete)) {
+ dtd_complete_irq(udc);
+ status = IRQ_HANDLED;
+ }
+ }
+
+ /* SOF (for ISO transfer) */
+ if (irq_src & USB_STS_SOF) {
+ status = IRQ_HANDLED;
+ }
+
+ /* Port Change */
+ if (irq_src & USB_STS_PORT_CHANGE) {
+ port_change_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ /* Reset Received */
+ if (irq_src & USB_STS_RESET) {
+ VDBG("reset int");
+ reset_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ /* Sleep Enable (Suspend) */
+ if (irq_src & USB_STS_SUSPEND) {
+ suspend_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
+ VDBG("Error IRQ %x ", irq_src);
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return status;
+}
+
+/*----------------------------------------------------------------*
+ * Hook to gadget drivers
+ * Called by initialization code of gadget drivers
+*----------------------------------------------------------------*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ int retval = -ENODEV;
+ unsigned long flags = 0;
+
+ if (!udc_controller)
+ return -ENODEV;
+
+ if (!driver || (driver->speed != USB_SPEED_FULL
+ && driver->speed != USB_SPEED_HIGH)
+ || !driver->bind || !driver->disconnect
+ || !driver->setup)
+ return -EINVAL;
+
+ if (udc_controller->driver)
+ return -EBUSY;
+
+ /* lock is needed but whether should use this lock or another */
+ spin_lock_irqsave(&udc_controller->lock, flags);
+
+ driver->driver.bus = 0;
+ /* hook up the driver */
+ udc_controller->driver = driver;
+ udc_controller->gadget.dev.driver = &driver->driver;
+ spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+ dr_phy_low_power_mode(udc_controller, false);
+ /* bind udc driver to gadget driver */
+ retval = driver->bind(&udc_controller->gadget);
+ if (retval) {
+ VDBG("bind to %s --> %d", driver->driver.name, retval);
+ udc_controller->gadget.dev.driver = 0;
+ udc_controller->driver = 0;
+ goto out;
+ }
+
+ if (udc_controller->transceiver) {
+ /* Suspend the controller until OTG enable it */
+ udc_controller->stopped = 1;
+ printk(KERN_INFO "Suspend udc for OTG auto detect\n");
+ dr_wake_up_enable(udc_controller, true);
+ dr_phy_low_power_mode(udc_controller, true);
+
+ /* export udc suspend/resume call to OTG */
+ udc_controller->gadget.dev.driver->suspend = (dev_sus)fsl_udc_suspend;
+ udc_controller->gadget.dev.driver->resume = (dev_res)fsl_udc_resume;
+
+ /* connect to bus through transceiver */
+ if (udc_controller->transceiver) {
+ retval = otg_set_peripheral(udc_controller->transceiver,
+ &udc_controller->gadget);
+ if (retval < 0) {
+ ERR("can't bind to transceiver\n");
+ driver->unbind(&udc_controller->gadget);
+ udc_controller->gadget.dev.driver = 0;
+ udc_controller->driver = 0;
+ return retval;
+ }
+ }
+ } else {
+ /* Enable DR IRQ reg and Set usbcmd reg Run bit */
+ dr_controller_run(udc_controller);
+ udc_controller->usb_state = USB_STATE_ATTACHED;
+ udc_controller->ep0_dir = 0;
+ }
+ printk(KERN_INFO "%s: bind to driver %s \n",
+ udc_controller->gadget.name, driver->driver.name);
+
+out:
+ if (retval)
+ printk(KERN_DEBUG "retval %d \n", retval);
+ return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/* Disconnect from gadget driver */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct fsl_ep *loop_ep;
+ unsigned long flags;
+
+ if (!udc_controller)
+ return -ENODEV;
+
+ if (!driver || driver != udc_controller->driver || !driver->unbind)
+ return -EINVAL;
+
+ if (udc_controller->transceiver)
+ (void)otg_set_peripheral(udc_controller->transceiver, 0);
+
+ /* stop DR, disable intr */
+ dr_controller_stop(udc_controller);
+ /* open phy clock for following operation */
+ dr_phy_low_power_mode(udc_controller, false);
+
+ /* in fact, no needed */
+ udc_controller->usb_state = USB_STATE_ATTACHED;
+ udc_controller->ep0_dir = 0;
+
+ /* stand operation */
+ spin_lock_irqsave(&udc_controller->lock, flags);
+ udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+ nuke(&udc_controller->eps[0], -ESHUTDOWN);
+ list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
+ ep.ep_list)
+ nuke(loop_ep, -ESHUTDOWN);
+ spin_unlock_irqrestore(&udc_controller->lock, flags);
+
+ /* disconnect gadget before unbinding */
+ driver->disconnect(&udc_controller->gadget);
+
+ /* unbind gadget and unhook driver. */
+ driver->unbind(&udc_controller->gadget);
+ udc_controller->gadget.dev.driver = 0;
+ udc_controller->driver = 0;
+
+ dr_wake_up_enable(udc_controller, false);
+ dr_phy_low_power_mode(udc_controller, true);
+
+ printk(KERN_INFO "unregistered gadget driver '%s'\r\n",
+ driver->driver.name);
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*-------------------------------------------------------------------------
+ PROC File System Support
+-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+#include <linux/seq_file.h>
+
+static const char proc_filename[] = "driver/fsl_usb2_udc";
+
+static int fsl_proc_read(char *page, char **start, off_t off, int count,
+ int *eof, void *_dev)
+{
+ char *buf = page;
+ char *next = buf;
+ unsigned size = count;
+ unsigned long flags;
+ int t, i;
+ u32 tmp_reg;
+ struct fsl_ep *ep = NULL;
+ struct fsl_req *req;
+ struct fsl_usb2_platform_data *pdata;
+
+ struct fsl_udc *udc = udc_controller;
+ pdata = udc->pdata;
+ if (off != 0)
+ return 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* ------basic driver infomation ---- */
+ t = scnprintf(next, size,
+ DRIVER_DESC "\n"
+ "%s version: %s\n"
+ "Gadget driver: %s\n\n",
+ driver_name, DRIVER_VERSION,
+ udc->driver ? udc->driver->driver.name : "(none)");
+ size -= t;
+ next += t;
+
+ /* ------ DR Registers ----- */
+ tmp_reg = fsl_readl(&dr_regs->usbcmd);
+ t = scnprintf(next, size,
+ "USBCMD reg:\n"
+ "SetupTW: %d\n"
+ "Run/Stop: %s\n\n",
+ (tmp_reg & USB_CMD_SUTW) ? 1 : 0,
+ (tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop");
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->usbsts);
+ t = scnprintf(next, size,
+ "USB Status Reg:\n"
+ "Dr Suspend: %d" "Reset Received: %d" "System Error: %s"
+ "USB Error Interrupt: %s\n\n",
+ (tmp_reg & USB_STS_SUSPEND) ? 1 : 0,
+ (tmp_reg & USB_STS_RESET) ? 1 : 0,
+ (tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal",
+ (tmp_reg & USB_STS_ERR) ? "Err detected" : "No err");
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->usbintr);
+ t = scnprintf(next, size,
+ "USB Intrrupt Enable Reg:\n"
+ "Sleep Enable: %d" "SOF Received Enable: %d"
+ "Reset Enable: %d\n"
+ "System Error Enable: %d"
+ "Port Change Dectected Enable: %d\n"
+ "USB Error Intr Enable: %d" "USB Intr Enable: %d\n\n",
+ (tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0,
+ (tmp_reg & USB_INTR_SOF_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_RESET_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_SYS_ERR_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0,
+ (tmp_reg & USB_INTR_INT_EN) ? 1 : 0);
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->frindex);
+ t = scnprintf(next, size,
+ "USB Frame Index Reg:" "Frame Number is 0x%x\n\n",
+ (tmp_reg & USB_FRINDEX_MASKS));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->deviceaddr);
+ t = scnprintf(next, size,
+ "USB Device Address Reg:" "Device Addr is 0x%x\n\n",
+ (tmp_reg & USB_DEVICE_ADDRESS_MASK));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->endpointlistaddr);
+ t = scnprintf(next, size,
+ "USB Endpoint List Address Reg:"
+ "Device Addr is 0x%x\n\n",
+ (tmp_reg & USB_EP_LIST_ADDRESS_MASK));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->portsc1);
+ t = scnprintf(next, size,
+ "USB Port Status&Control Reg:\n"
+ "Port Transceiver Type : %s" "Port Speed: %s \n"
+ "PHY Low Power Suspend: %s" "Port Reset: %s"
+ "Port Suspend Mode: %s \n" "Over-current Change: %s"
+ "Port Enable/Disable Change: %s\n"
+ "Port Enabled/Disabled: %s"
+ "Current Connect Status: %s\n\n", ({
+ char *s;
+ switch (tmp_reg & PORTSCX_PTS_FSLS) {
+ case PORTSCX_PTS_UTMI:
+ s = "UTMI"; break;
+ case PORTSCX_PTS_ULPI:
+ s = "ULPI "; break;
+ case PORTSCX_PTS_FSLS:
+ s = "FS/LS Serial"; break;
+ default:
+ s = "None"; break;
+ }
+ s; }), ({
+ char *s;
+ switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) {
+ case PORTSCX_PORT_SPEED_FULL:
+ s = "Full Speed"; break;
+ case PORTSCX_PORT_SPEED_LOW:
+ s = "Low Speed"; break;
+ case PORTSCX_PORT_SPEED_HIGH:
+ s = "High Speed"; break;
+ default:
+ s = "Undefined"; break;
+ }
+ s;
+ }),
+ (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ?
+ "Normal PHY mode" : "Low power mode",
+ (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" :
+ "Not in Reset",
+ (tmp_reg & PORTSCX_PORT_SUSPEND) ? "In " : "Not in",
+ (tmp_reg & PORTSCX_OVER_CURRENT_CHG) ? "Dected" :
+ "No",
+ (tmp_reg & PORTSCX_PORT_EN_DIS_CHANGE) ? "Disable" :
+ "Not change",
+ (tmp_reg & PORTSCX_PORT_ENABLE) ? "Enable" :
+ "Not correct",
+ (tmp_reg & PORTSCX_CURRENT_CONNECT_STATUS) ?
+ "Attached" : "Not-Att");
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->usbmode);
+ t = scnprintf(next, size,
+ "USB Mode Reg:" "Controller Mode is : %s\n\n", ({
+ char *s;
+ switch (tmp_reg & USB_MODE_CTRL_MODE_HOST) {
+ case USB_MODE_CTRL_MODE_IDLE:
+ s = "Idle"; break;
+ case USB_MODE_CTRL_MODE_DEVICE:
+ s = "Device Controller"; break;
+ case USB_MODE_CTRL_MODE_HOST:
+ s = "Host Controller"; break;
+ default:
+ s = "None"; break;
+ }
+ s;
+ }));
+ size -= t;
+ next += t;
+
+ tmp_reg = fsl_readl(&dr_regs->endptsetupstat);
+ t = scnprintf(next, size,
+ "Endpoint Setup Status Reg:" "SETUP on ep 0x%x\n\n",
+ (tmp_reg & EP_SETUP_STATUS_MASK));
+ size -= t;
+ next += t;
+
+ for (i = 0; i < udc->max_ep / 2; i++) {
+ tmp_reg = fsl_readl(&dr_regs->endptctrl[i]);
+ t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n",
+ i, tmp_reg);
+ size -= t;
+ next += t;
+ }
+ tmp_reg = fsl_readl(&dr_regs->endpointprime);
+ t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ if (pdata->have_sysif_regs) {
+ tmp_reg = usb_sys_regs->snoop1;
+ t = scnprintf(next, size, "\nSnoop1 Reg = [0x%x]\n\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = usb_sys_regs->control;
+ t = scnprintf(next, size, "General Control Reg = [0x%x]\n\n",
+ tmp_reg);
+ size -= t;
+ next += t;
+ }
+
+ /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
+ ep = &udc->eps[0];
+ t = scnprintf(next, size, "For %s Maxpkt is 0x%x index is 0x%x\n",
+ ep->ep.name, ep_maxpacket(ep), ep_index(ep));
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size, "its req queue is empty\n\n");
+ size -= t;
+ next += t;
+ } else {
+ list_for_each_entry(req, &ep->queue, queue) {
+ t = scnprintf(next, size,
+ "req %p actual 0x%x length 0x%x buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ size -= t;
+ next += t;
+ }
+ }
+ /* other gadget->eplist ep */
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+ if (ep->desc) {
+ t = scnprintf(next, size,
+ "\nFor %s Maxpkt is 0x%x "
+ "index is 0x%x\n",
+ ep->ep.name, ep_maxpacket(ep),
+ ep_index(ep));
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size,
+ "its req queue is empty\n\n");
+ size -= t;
+ next += t;
+ } else {
+ list_for_each_entry(req, &ep->queue, queue) {
+ t = scnprintf(next, size,
+ "req %p actual 0x%x length"
+ "0x%x buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ size -= t;
+ next += t;
+ } /* end for each_entry of ep req */
+ } /* end for else */
+ } /* end for if(ep->queue) */
+ } /* end (ep->desc) */
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ *eof = 1;
+ return count - size;
+}
+
+#define create_proc_file() create_proc_read_entry(proc_filename, \
+ 0, NULL, fsl_proc_read, NULL)
+
+#define remove_proc_file() remove_proc_entry(proc_filename, NULL)
+
+#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_proc_file() do {} while (0)
+#define remove_proc_file() do {} while (0)
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+/*-------------------------------------------------------------------------*/
+
+/* Release udc structures */
+static void fsl_udc_release(struct device *dev)
+{
+ complete(udc_controller->done);
+ dma_free_coherent(dev, udc_controller->ep_qh_size,
+ udc_controller->ep_qh, udc_controller->ep_qh_dma);
+ kfree(udc_controller);
+}
+
+/******************************************************************
+ Internal structure setup functions
+*******************************************************************/
+/*------------------------------------------------------------------
+ * init resource for globle controller
+ * Return the udc handle on success or NULL on failure
+ ------------------------------------------------------------------*/
+static int __init struct_udc_setup(struct fsl_udc *udc,
+ struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata;
+ size_t size;
+
+ pdata = pdev->dev.platform_data;
+ udc->phy_mode = pdata->phy_mode;
+
+ udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL);
+ if (!udc->eps) {
+ ERR("malloc fsl_ep failed\n");
+ return -1;
+ }
+
+ /* initialized QHs, take care of alignment */
+ size = udc->max_ep * sizeof(struct ep_queue_head);
+ if (size < QH_ALIGNMENT)
+ size = QH_ALIGNMENT;
+ else if ((size % QH_ALIGNMENT) != 0) {
+ size += QH_ALIGNMENT + 1;
+ size &= ~(QH_ALIGNMENT - 1);
+ }
+ udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
+ &udc->ep_qh_dma, GFP_KERNEL);
+ if (!udc->ep_qh) {
+ ERR("malloc QHs for udc failed\n");
+ kfree(udc->eps);
+ return -1;
+ }
+
+ udc->ep_qh_size = size;
+
+ /* Initialize ep0 status request structure */
+ /* FIXME: fsl_alloc_request() ignores ep argument */
+ udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
+ struct fsl_req, req);
+ /* allocate a small amount of memory to get valid address */
+ udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
+ udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf);
+ /* Initialize ep0 data request structure */
+ udc->data_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
+ struct fsl_req, req);
+ udc->data_req->req.buf = kmalloc(8, GFP_KERNEL);
+ udc->data_req->req.dma = virt_to_phys(udc->data_req->req.buf);
+
+ udc->resume_state = USB_STATE_NOTATTACHED;
+ udc->usb_state = USB_STATE_POWERED;
+ udc->ep0_dir = 0;
+ udc->remote_wakeup = 0; /* default to 0 on reset */
+ spin_lock_init(&udc->lock);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+ * Setup the fsl_ep struct for eps
+ * Link fsl_ep->ep to gadget->ep_list
+ * ep0out is not used so do nothing here
+ * ep0in should be taken care
+ *--------------------------------------------------------------*/
+static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
+ char *name, int link)
+{
+ struct fsl_ep *ep = &udc->eps[index];
+
+ ep->udc = udc;
+ strcpy(ep->name, name);
+ ep->ep.name = ep->name;
+
+ ep->ep.ops = &fsl_ep_ops;
+ ep->stopped = 0;
+
+ /* for ep0: maxP defined in desc
+ * for other eps, maxP is set by epautoconfig() called by gadget layer
+ */
+ ep->ep.maxpacket = (unsigned short) ~0;
+
+ /* the queue lists any req for this ep */
+ INIT_LIST_HEAD(&ep->queue);
+
+ /* gagdet.ep_list used for ep_autoconfig so no ep0 */
+ if (link)
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+ ep->gadget = &udc->gadget;
+ ep->qh = &udc->ep_qh[index];
+
+ return 0;
+}
+
+/* Driver probe function
+ * all intialization operations implemented here except enabling usb_intr reg
+ * board setup should have been done in the platform code
+ */
+static int __init fsl_udc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ int ret = -ENODEV;
+ unsigned int i;
+ u32 dccparams;
+
+ if (strcmp(pdev->name, driver_name)) {
+ VDBG("Wrong device\n");
+ return -ENODEV;
+ }
+
+ udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
+ if (udc_controller == NULL) {
+ ERR("malloc udc failed\n");
+ return -ENOMEM;
+ }
+ udc_controller->pdata = pdata;
+
+#ifdef CONFIG_USB_OTG
+ /* Memory and interrupt resources will be passed from OTG */
+ udc_controller->transceiver = otg_get_transceiver();
+ if (!udc_controller->transceiver) {
+ printk(KERN_ERR "Can't find OTG driver!\n");
+ ret = -ENODEV;
+ goto err1a;
+ }
+
+ res = otg_get_resources();
+ if (!res) {
+ DBG("resource not registered!\n");
+ return -ENODEV;
+ }
+#else
+ if ((pdev->dev.parent) &&
+ (to_platform_device(pdev->dev.parent)->resource)) {
+ pdev->resource =
+ to_platform_device(pdev->dev.parent)->resource;
+ pdev->num_resources =
+ to_platform_device(pdev->dev.parent)->num_resources;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENXIO;
+ goto err1a;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res),
+ driver_name)) {
+ ERR("request mem region for %s failed \n", pdev->name);
+ ret = -EBUSY;
+ goto err1a;
+ }
+#endif
+
+ dr_regs = ioremap(res->start, resource_size(res));
+ if (!dr_regs) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+ pdata->regs = (void *)dr_regs;
+ /*
+ * do platform specific init: check the clock, grab/config pins, etc.
+ */
+ if (pdata->platform_init && pdata->platform_init(pdev)) {
+ ret = -ENODEV;
+ goto err2a;
+ }
+
+ if (pdata->have_sysif_regs)
+ usb_sys_regs = (struct usb_sys_interface *)
+ ((u32)dr_regs + USB_DR_SYS_OFFSET);
+
+ /* Read Device Controller Capability Parameters register */
+ dccparams = fsl_readl(&dr_regs->dccparams);
+ if (!(dccparams & DCCPARAMS_DC)) {
+ ERR("This SOC doesn't support device role\n");
+ ret = -ENODEV;
+ goto err2;
+ }
+ /* Get max device endpoints */
+ /* DEN is bidirectional ep number, max_ep doubles the number */
+ udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;
+
+#ifdef CONFIG_USB_OTG
+ res++;
+ udc_controller->irq = res->start;
+#else
+ udc_controller->irq = platform_get_irq(pdev, 0);
+#endif
+ if (!udc_controller->irq) {
+ ret = -ENODEV;
+ goto err2;
+ }
+
+ ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
+ driver_name, udc_controller);
+ if (ret != 0) {
+ ERR("cannot request irq %d err %d \n",
+ udc_controller->irq, ret);
+ goto err2;
+ }
+
+ /* Initialize the udc structure including QH member and other member */
+ if (struct_udc_setup(udc_controller, pdev)) {
+ ERR("Can't initialize udc data structure\n");
+ ret = -ENOMEM;
+ goto err3;
+ }
+
+ if (!udc_controller->transceiver) {
+ /* initialize usb hw reg except for regs for EP,
+ * leave usbintr reg untouched */
+ dr_controller_setup(udc_controller);
+ }
+
+ /* Setup gadget structure */
+ udc_controller->gadget.ops = &fsl_gadget_ops;
+ udc_controller->gadget.is_dualspeed = 1;
+ udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
+ INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
+ udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
+ udc_controller->gadget.name = driver_name;
+
+ /* Setup gadget.dev and register with kernel */
+ strcpy(udc_controller->gadget.dev.bus_id, "gadget");
+ udc_controller->gadget.dev.release = fsl_udc_release;
+ udc_controller->gadget.dev.parent = &pdev->dev;
+ ret = device_register(&udc_controller->gadget.dev);
+ if (ret < 0)
+ goto err3;
+
+ if (udc_controller->transceiver) {
+ udc_controller->gadget.is_otg = 1;
+ /* now didn't support lpm in OTG mode*/
+ device_set_wakeup_capable(&pdev->dev, 0);
+ }
+
+ /* setup QH and epctrl for ep0 */
+ ep0_setup(udc_controller);
+
+ /* setup udc->eps[] for ep0 */
+ struct_ep_setup(udc_controller, 0, "ep0", 0);
+ /* for ep0: the desc defined here;
+ * for other eps, gadget layer called ep_enable with defined desc
+ */
+ udc_controller->eps[0].desc = &fsl_ep0_desc;
+ udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD;
+
+ /* setup the udc->eps[] for non-control endpoints and link
+ * to gadget.ep_list */
+ for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) {
+ char name[14];
+
+ sprintf(name, "ep%dout", i);
+ struct_ep_setup(udc_controller, i * 2, name, 1);
+ sprintf(name, "ep%din", i);
+ struct_ep_setup(udc_controller, i * 2 + 1, name, 1);
+ }
+
+ /* use dma_pool for TD management */
+ udc_controller->td_pool = dma_pool_create("udc_td", &pdev->dev,
+ sizeof(struct ep_td_struct),
+ DTD_ALIGNMENT, UDC_DMA_BOUNDARY);
+ if (udc_controller->td_pool == NULL) {
+ ret = -ENOMEM;
+ goto err4;
+ }
+ if (g_iram_size) {
+ for (i = 0; i < IRAM_PPH_NTD; i++) {
+ udc_controller->iram_buffer[i] =
+ USB_IRAM_BASE_ADDR + i * g_iram_size;
+ udc_controller->iram_buffer_v[i] =
+ IO_ADDRESS(udc_controller->iram_buffer[i]);
+ }
+ }
+#ifdef POSTPONE_FREE_LAST_DTD
+ last_free_td = NULL;
+#endif
+
+ dr_wake_up_enable(udc_controller, false);
+ udc_controller->stopped = 1;
+ dr_phy_low_power_mode(udc_controller, true);
+
+ create_proc_file();
+ return 0;
+
+err4:
+ device_unregister(&udc_controller->gadget.dev);
+err3:
+ free_irq(udc_controller->irq, udc_controller);
+err2:
+ if (pdata->platform_uninit)
+ pdata->platform_uninit(pdata);
+err2a:
+ iounmap((u8 __iomem *)dr_regs);
+err1:
+ if (!udc_controller->transceiver)
+ release_mem_region(res->start, resource_size(res));
+err1a:
+ kfree(udc_controller);
+ udc_controller = NULL;
+ return ret;
+}
+
+/* Driver removal function
+ * Free resources and finish pending transactions
+ */
+static int __exit fsl_udc_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+ DECLARE_COMPLETION(done);
+
+ if (!udc_controller)
+ return -ENODEV;
+ udc_controller->done = &done;
+ /* open USB PHY clock */
+ dr_phy_low_power_mode(udc_controller, false);
+
+ /* DR has been stopped in usb_gadget_unregister_driver() */
+ remove_proc_file();
+
+ /* Free allocated memory */
+ kfree(udc_controller->status_req->req.buf);
+ kfree(udc_controller->status_req);
+ kfree(udc_controller->data_req->req.buf);
+ kfree(udc_controller->data_req);
+ kfree(udc_controller->eps);
+#ifdef POSTPONE_FREE_LAST_DTD
+ if (last_free_td != NULL)
+ dma_pool_free(udc_controller->td_pool, last_free_td,
+ last_free_td->td_dma);
+#endif
+ dma_pool_destroy(udc_controller->td_pool);
+ free_irq(udc_controller->irq, udc_controller);
+ iounmap((u8 __iomem *)dr_regs);
+
+#ifndef CONFIG_USB_OTG
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+#endif
+
+ device_unregister(&udc_controller->gadget.dev);
+ /* free udc --wait for the release() finished */
+ wait_for_completion(&done);
+
+ /*
+ * do platform specific un-initialization:
+ * release iomux pins, etc.
+ */
+ if (pdata->platform_uninit)
+ pdata->platform_uninit(pdata);
+
+ return 0;
+}
+
+static int udc_suspend(struct fsl_udc *udc)
+{
+ u32 mode, usbcmd;
+
+ mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK;
+ usbcmd = fsl_readl(&dr_regs->usbcmd);
+
+ pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped);
+
+ /*
+ * If the controller is already stopped, then this must be a
+ * PM suspend. Remember this fact, so that we will leave the
+ * controller stopped at PM resume time.
+ */
+ if (udc->stopped) {
+ pr_debug("gadget already stopped, leaving early\n");
+ udc->already_stopped = 1;
+ return 0;
+ }
+
+ if (mode != USB_MODE_CTRL_MODE_DEVICE) {
+ pr_debug("gadget not in device mode, leaving early\n");
+ return 0;
+ }
+
+ udc->stopped = 1;
+ /* if the suspend is not for switch to host in otg mode */
+ if ((!(udc->gadget.is_otg)) ||
+ (fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) {
+ dr_wake_up_enable(udc, true);
+ dr_phy_low_power_mode(udc, true);
+ }
+
+ /* stop the controller */
+ usbcmd = fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP;
+ fsl_writel(usbcmd, &dr_regs->usbcmd);
+
+ printk(KERN_INFO "USB Gadget suspended\n");
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Modify Power management attributes
+ * Used by OTG statemachine to disable gadget temporarily
+ -----------------------------------------------------------------*/
+static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if ((udc_controller->usb_state > USB_STATE_POWERED) &&
+ (udc_controller->usb_state < USB_STATE_SUSPENDED))
+ return -EBUSY;
+
+ return udc_suspend(udc_controller);
+}
+
+/*-----------------------------------------------------------------
+ * Invoked on USB resume. May be called in_interrupt.
+ * Here we start the DR controller and enable the irq
+ *-----------------------------------------------------------------*/
+static int fsl_udc_resume(struct platform_device *pdev)
+{
+ pr_debug("%s(): stopped %d already_stopped %d\n", __func__,
+ udc_controller->stopped, udc_controller->already_stopped);
+
+ /*
+ * If the controller was stopped at suspend time, then
+ * don't resume it now.
+ */
+ if (udc_controller->already_stopped) {
+ udc_controller->already_stopped = 0;
+ pr_debug("gadget was already stopped, leaving early\n");
+ return 0;
+ }
+
+ /* Enable DR irq reg and set controller Run */
+ if (udc_controller->stopped) {
+ dr_wake_up_enable(udc_controller, false);
+ dr_phy_low_power_mode(udc_controller, false);
+ mdelay(1);
+
+ dr_controller_setup(udc_controller);
+ dr_controller_run(udc_controller);
+ }
+ udc_controller->usb_state = USB_STATE_ATTACHED;
+ udc_controller->ep0_dir = 0;
+
+ printk(KERN_INFO "USB Gadget resumed\n");
+ return 0;
+}
+
+/*-------------------------------------------------------------------------
+ Register entry point for the peripheral controller driver
+--------------------------------------------------------------------------*/
+
+static struct platform_driver udc_driver = {
+ .remove = __exit_p(fsl_udc_remove),
+ /* these suspend and resume are not usb suspend and resume */
+ .suspend = fsl_udc_suspend,
+ .resume = fsl_udc_resume,
+ .probe = fsl_udc_probe,
+ .driver = {
+ .name = driver_name,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init udc_init(void)
+{
+ printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION);
+ return platform_driver_register(&udc_driver);
+}
+
+module_init(udc_init);
+
+static void __exit udc_exit(void)
+{
+ platform_driver_unregister(&udc_driver);
+ printk(KERN_INFO "%s unregistered \n", driver_desc);
+}
+
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/arcotg_udc.h b/drivers/usb/gadget/arcotg_udc.h
new file mode 100644
index 000000000000..f7950c25b45b
--- /dev/null
+++ b/drivers/usb/gadget/arcotg_udc.h
@@ -0,0 +1,703 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file arcotg_udc.h
+ * @brief Freescale USB device/endpoint management registers
+ * @ingroup USB
+ */
+
+#ifndef __ARCOTG_UDC_H
+#define __ARCOTG_UDC_H
+
+#define TRUE 1
+#define FALSE 0
+
+#define MSC_BULK_CB_WRAP_LEN 31
+#if CONFIG_ARCH_MXC
+#define USE_MSC_WR(len) (((cpu_is_mx37_rev(CHIP_REV_1_0) == 1) ||\
+ (cpu_is_mx51_rev(CHIP_REV_2_0) < 0)) && ((len) == MSC_BULK_CB_WRAP_LEN))
+#else
+#define USE_MSC_WR(len) false
+#endif
+
+/* Iram patch */
+#ifdef CONFIG_USB_STATIC_IRAM_PPH
+/* size of 1 qTD's buffer,one is for BULK IN and other is BULK OUT */
+#define IRAM_TD_PPH_SIZE (USB_IRAM_SIZE / 2)
+#define IRAM_PPH_NTD 2 /* number of TDs in IRAM */
+#else
+#define IRAM_TD_PPH_SIZE 0
+#define IRAM_PPH_NTD 0
+#endif
+
+#ifndef USB_IRAM_BASE_ADDR
+#define USB_IRAM_BASE_ADDR 0
+#endif
+
+#define NEED_IRAM(ep) ((g_iram_size) && \
+ ((ep)->desc->bmAttributes == USB_ENDPOINT_XFER_BULK))
+
+#ifdef CONFIG_ARCH_MX51
+#define POSTPONE_FREE_LAST_DTD
+#else
+#undef POSTPONE_FREE_LAST_DTD
+#endif
+
+/* ### define USB registers here
+ */
+#define USB_MAX_ENDPOINTS 8
+#define USB_MAX_PIPES (USB_MAX_ENDPOINTS*2)
+#define USB_MAX_CTRL_PAYLOAD 64
+#define USB_DR_SYS_OFFSET 0x400
+
+#define USB_DR_OFFSET 0x3100
+
+struct usb_dr_device {
+ /* Capability register */
+ u32 id;
+ u32 res1[35];
+ u32 sbuscfg; /* sbuscfg ahb burst */
+ u32 res11[27];
+ u16 caplength; /* Capability Register Length */
+ u16 hciversion; /* Host Controller Interface Version */
+ u32 hcsparams; /* Host Controller Structual Parameters */
+ u32 hccparams; /* Host Controller Capability Parameters */
+ u32 res2[5];
+ u32 dciversion; /* Device Controller Interface Version */
+ u32 dccparams; /* Device Controller Capability Parameters */
+ u32 res3[6];
+ /* Operation register */
+ u32 usbcmd; /* USB Command Register */
+ u32 usbsts; /* USB Status Register */
+ u32 usbintr; /* USB Interrupt Enable Register */
+ u32 frindex; /* Frame Index Register */
+ u32 res4;
+ u32 deviceaddr; /* Device Address */
+ u32 endpointlistaddr; /* Endpoint List Address Register */
+ u32 res5;
+ u32 burstsize; /* Master Interface Data Burst Size Register */
+ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */
+ u32 res6[6];
+ u32 configflag; /* Configure Flag Register */
+ u32 portsc1; /* Port 1 Status and Control Register */
+ u32 res7[7];
+ u32 otgsc; /* On-The-Go Status and Control */
+ u32 usbmode; /* USB Mode Register */
+ u32 endptsetupstat; /* Endpoint Setup Status Register */
+ u32 endpointprime; /* Endpoint Initialization Register */
+ u32 endptflush; /* Endpoint Flush Register */
+ u32 endptstatus; /* Endpoint Status Register */
+ u32 endptcomplete; /* Endpoint Complete Register */
+ u32 endptctrl[8 * 2]; /* Endpoint Control Registers */
+ u32 res8[256];
+#ifdef CONFIG_ARCH_MX51
+ u32 res9[128]; /* i.MX51 start from 0x800 */
+#endif
+ u32 usbctrl;
+ u32 otgmirror;
+ u32 phyctrl0;
+ u32 phyctrl1;
+ u32 ctrl1;
+ u32 uh2ctrl;
+};
+
+ /* non-EHCI USB system interface registers (Big Endian) */
+struct usb_sys_interface {
+ u32 snoop1;
+ u32 snoop2;
+ u32 age_cnt_thresh; /* Age Count Threshold Register */
+ u32 pri_ctrl; /* Priority Control Register */
+ u32 si_ctrl; /* System Interface Control Register */
+ u8 res[236];
+ u32 control; /* General Purpose Control Register */
+};
+
+/* ep0 transfer state */
+#define WAIT_FOR_SETUP 0
+#define DATA_STATE_XMIT 1
+#define DATA_STATE_NEED_ZLP 2
+#define WAIT_FOR_OUT_STATUS 3
+#define DATA_STATE_RECV 4
+
+/* Device Controller Capability Parameter register */
+#define DCCPARAMS_DC 0x00000080
+#define DCCPARAMS_DEN_MASK 0x0000001f
+
+/* Frame Index Register Bit Masks */
+#define USB_FRINDEX_MASKS (0x3fff)
+/* USB CMD Register Bit Masks */
+#define USB_CMD_RUN_STOP (0x00000001)
+#define USB_CMD_CTRL_RESET (0x00000002)
+#define USB_CMD_PERIODIC_SCHEDULE_EN (0x00000010)
+#define USB_CMD_ASYNC_SCHEDULE_EN (0x00000020)
+#define USB_CMD_INT_AA_DOORBELL (0x00000040)
+#define USB_CMD_ASP (0x00000300)
+#define USB_CMD_ASYNC_SCH_PARK_EN (0x00000800)
+#define USB_CMD_SUTW (0x00002000)
+#define USB_CMD_ATDTW (0x00004000)
+#define USB_CMD_ITC (0x00FF0000)
+
+/* bit 15,3,2 are frame list size */
+#define USB_CMD_FRAME_SIZE_1024 (0x00000000)
+#define USB_CMD_FRAME_SIZE_512 (0x00000004)
+#define USB_CMD_FRAME_SIZE_256 (0x00000008)
+#define USB_CMD_FRAME_SIZE_128 (0x0000000C)
+#define USB_CMD_FRAME_SIZE_64 (0x00008000)
+#define USB_CMD_FRAME_SIZE_32 (0x00008004)
+#define USB_CMD_FRAME_SIZE_16 (0x00008008)
+#define USB_CMD_FRAME_SIZE_8 (0x0000800C)
+
+/* bit 9-8 are async schedule park mode count */
+#define USB_CMD_ASP_00 (0x00000000)
+#define USB_CMD_ASP_01 (0x00000100)
+#define USB_CMD_ASP_10 (0x00000200)
+#define USB_CMD_ASP_11 (0x00000300)
+#define USB_CMD_ASP_BIT_POS (8)
+
+/* bit 23-16 are interrupt threshold control */
+#define USB_CMD_ITC_NO_THRESHOLD (0x00000000)
+#define USB_CMD_ITC_1_MICRO_FRM (0x00010000)
+#define USB_CMD_ITC_2_MICRO_FRM (0x00020000)
+#define USB_CMD_ITC_4_MICRO_FRM (0x00040000)
+#define USB_CMD_ITC_8_MICRO_FRM (0x00080000)
+#define USB_CMD_ITC_16_MICRO_FRM (0x00100000)
+#define USB_CMD_ITC_32_MICRO_FRM (0x00200000)
+#define USB_CMD_ITC_64_MICRO_FRM (0x00400000)
+#define USB_CMD_ITC_BIT_POS (16)
+
+/* USB STS Register Bit Masks */
+#define USB_STS_INT (0x00000001)
+#define USB_STS_ERR (0x00000002)
+#define USB_STS_PORT_CHANGE (0x00000004)
+#define USB_STS_FRM_LST_ROLL (0x00000008)
+#define USB_STS_SYS_ERR (0x00000010)
+#define USB_STS_IAA (0x00000020)
+#define USB_STS_RESET (0x00000040)
+#define USB_STS_SOF (0x00000080)
+#define USB_STS_SUSPEND (0x00000100)
+#define USB_STS_HC_HALTED (0x00001000)
+#define USB_STS_RCL (0x00002000)
+#define USB_STS_PERIODIC_SCHEDULE (0x00004000)
+#define USB_STS_ASYNC_SCHEDULE (0x00008000)
+
+/* USB INTR Register Bit Masks */
+#define USB_INTR_INT_EN (0x00000001)
+#define USB_INTR_ERR_INT_EN (0x00000002)
+#define USB_INTR_PTC_DETECT_EN (0x00000004)
+#define USB_INTR_FRM_LST_ROLL_EN (0x00000008)
+#define USB_INTR_SYS_ERR_EN (0x00000010)
+#define USB_INTR_ASYN_ADV_EN (0x00000020)
+#define USB_INTR_RESET_EN (0x00000040)
+#define USB_INTR_SOF_EN (0x00000080)
+#define USB_INTR_DEVICE_SUSPEND (0x00000100)
+
+/* Device Address bit masks */
+#define USB_DEVICE_ADDRESS_MASK (0xFE000000)
+#define USB_DEVICE_ADDRESS_BIT_POS (25)
+
+/* endpoint list address bit masks */
+#define USB_EP_LIST_ADDRESS_MASK (0xfffff800)
+
+/* PORTSCX Register Bit Masks */
+#define PORTSCX_CURRENT_CONNECT_STATUS (0x00000001)
+#define PORTSCX_CONNECT_STATUS_CHANGE (0x00000002)
+#define PORTSCX_PORT_ENABLE (0x00000004)
+#define PORTSCX_PORT_EN_DIS_CHANGE (0x00000008)
+#define PORTSCX_OVER_CURRENT_ACT (0x00000010)
+#define PORTSCX_OVER_CURRENT_CHG (0x00000020)
+#define PORTSCX_PORT_FORCE_RESUME (0x00000040)
+#define PORTSCX_PORT_SUSPEND (0x00000080)
+#define PORTSCX_PORT_RESET (0x00000100)
+#define PORTSCX_LINE_STATUS_BITS (0x00000C00)
+#define PORTSCX_PORT_POWER (0x00001000)
+#define PORTSCX_PORT_INDICTOR_CTRL (0x0000C000)
+#define PORTSCX_PORT_TEST_CTRL (0x000F0000)
+#define PORTSCX_WAKE_ON_CONNECT_EN (0x00100000)
+#define PORTSCX_WAKE_ON_CONNECT_DIS (0x00200000)
+#define PORTSCX_WAKE_ON_OVER_CURRENT (0x00400000)
+#define PORTSCX_PHY_LOW_POWER_SPD (0x00800000)
+#define PORTSCX_PORT_FORCE_FULL_SPEED (0x01000000)
+#define PORTSCX_PORT_SPEED_MASK (0x0C000000)
+#define PORTSCX_PORT_WIDTH (0x10000000)
+#define PORTSCX_PHY_TYPE_SEL (0xC0000000)
+
+/* bit 11-10 are line status */
+#define PORTSCX_LINE_STATUS_SE0 (0x00000000)
+#define PORTSCX_LINE_STATUS_JSTATE (0x00000400)
+#define PORTSCX_LINE_STATUS_KSTATE (0x00000800)
+#define PORTSCX_LINE_STATUS_UNDEF (0x00000C00)
+#define PORTSCX_LINE_STATUS_BIT_POS (10)
+
+/* bit 15-14 are port indicator control */
+#define PORTSCX_PIC_OFF (0x00000000)
+#define PORTSCX_PIC_AMBER (0x00004000)
+#define PORTSCX_PIC_GREEN (0x00008000)
+#define PORTSCX_PIC_UNDEF (0x0000C000)
+#define PORTSCX_PIC_BIT_POS (14)
+
+/* bit 19-16 are port test control */
+#define PORTSCX_PTC_DISABLE (0x00000000)
+#define PORTSCX_PTC_JSTATE (0x00010000)
+#define PORTSCX_PTC_KSTATE (0x00020000)
+#define PORTSCX_PTC_SEQNAK (0x00030000)
+#define PORTSCX_PTC_PACKET (0x00040000)
+#define PORTSCX_PTC_FORCE_EN (0x00050000)
+#define PORTSCX_PTC_BIT_POS (16)
+
+/* bit 27-26 are port speed */
+#define PORTSCX_PORT_SPEED_FULL (0x00000000)
+#define PORTSCX_PORT_SPEED_LOW (0x04000000)
+#define PORTSCX_PORT_SPEED_HIGH (0x08000000)
+#define PORTSCX_PORT_SPEED_UNDEF (0x0C000000)
+#define PORTSCX_SPEED_BIT_POS (26)
+
+/* OTGSC Register Bit Masks */
+#define OTGSC_B_SESSION_VALID_IRQ_EN (1 << 27)
+#define OTGSC_B_SESSION_VALID_IRQ_STS (1 << 19)
+#define OTGSC_B_SESSION_VALID (1 << 11)
+
+/* bit 28 is parallel transceiver width for UTMI interface */
+#define PORTSCX_PTW (0x10000000)
+#define PORTSCX_PTW_8BIT (0x00000000)
+#define PORTSCX_PTW_16BIT (0x10000000)
+
+/* bit 31-30 are port transceiver select */
+#define PORTSCX_PTS_UTMI (0x00000000)
+#define PORTSCX_PTS_ULPI (0x80000000)
+#define PORTSCX_PTS_FSLS (0xC0000000)
+#define PORTSCX_PTS_BIT_POS (30)
+
+/* USB MODE Register Bit Masks */
+#define USB_MODE_CTRL_MODE_IDLE (0x00000000)
+#define USB_MODE_CTRL_MODE_DEVICE (0x00000002)
+#define USB_MODE_CTRL_MODE_HOST (0x00000003)
+#define USB_MODE_CTRL_MODE_MASK 0x00000003
+#define USB_MODE_CTRL_MODE_RSV (0x00000001)
+#define USB_MODE_ES 0x00000004 /* (big) Endian Sel */
+#define USB_MODE_SETUP_LOCK_OFF (0x00000008)
+#define USB_MODE_STREAM_DISABLE (0x00000010)
+/* Endpoint Flush Register */
+#define EPFLUSH_TX_OFFSET (0x00010000)
+#define EPFLUSH_RX_OFFSET (0x00000000)
+
+/* Endpoint Setup Status bit masks */
+#define EP_SETUP_STATUS_MASK (0x0000003F)
+#define EP_SETUP_STATUS_EP0 (0x00000001)
+
+/* ENDPOINTCTRLx Register Bit Masks */
+#define EPCTRL_TX_ENABLE (0x00800000)
+#define EPCTRL_TX_DATA_TOGGLE_RST (0x00400000) /* Not EP0 */
+#define EPCTRL_TX_DATA_TOGGLE_INH (0x00200000) /* Not EP0 */
+#define EPCTRL_TX_TYPE (0x000C0000)
+#define EPCTRL_TX_DATA_SOURCE (0x00020000) /* Not EP0 */
+#define EPCTRL_TX_EP_STALL (0x00010000)
+#define EPCTRL_RX_ENABLE (0x00000080)
+#define EPCTRL_RX_DATA_TOGGLE_RST (0x00000040) /* Not EP0 */
+#define EPCTRL_RX_DATA_TOGGLE_INH (0x00000020) /* Not EP0 */
+#define EPCTRL_RX_TYPE (0x0000000C)
+#define EPCTRL_RX_DATA_SINK (0x00000002) /* Not EP0 */
+#define EPCTRL_RX_EP_STALL (0x00000001)
+
+/* bit 19-18 and 3-2 are endpoint type */
+#define EPCTRL_EP_TYPE_CONTROL (0)
+#define EPCTRL_EP_TYPE_ISO (1)
+#define EPCTRL_EP_TYPE_BULK (2)
+#define EPCTRL_EP_TYPE_INTERRUPT (3)
+#define EPCTRL_TX_EP_TYPE_SHIFT (18)
+#define EPCTRL_RX_EP_TYPE_SHIFT (2)
+
+/* SNOOPn Register Bit Masks */
+#define SNOOP_ADDRESS_MASK (0xFFFFF000)
+#define SNOOP_SIZE_ZERO (0x00) /* snooping disable */
+#define SNOOP_SIZE_4KB (0x0B) /* 4KB snoop size */
+#define SNOOP_SIZE_8KB (0x0C)
+#define SNOOP_SIZE_16KB (0x0D)
+#define SNOOP_SIZE_32KB (0x0E)
+#define SNOOP_SIZE_64KB (0x0F)
+#define SNOOP_SIZE_128KB (0x10)
+#define SNOOP_SIZE_256KB (0x11)
+#define SNOOP_SIZE_512KB (0x12)
+#define SNOOP_SIZE_1MB (0x13)
+#define SNOOP_SIZE_2MB (0x14)
+#define SNOOP_SIZE_4MB (0x15)
+#define SNOOP_SIZE_8MB (0x16)
+#define SNOOP_SIZE_16MB (0x17)
+#define SNOOP_SIZE_32MB (0x18)
+#define SNOOP_SIZE_64MB (0x19)
+#define SNOOP_SIZE_128MB (0x1A)
+#define SNOOP_SIZE_256MB (0x1B)
+#define SNOOP_SIZE_512MB (0x1C)
+#define SNOOP_SIZE_1GB (0x1D)
+#define SNOOP_SIZE_2GB (0x1E) /* 2GB snoop size */
+
+/* pri_ctrl Register Bit Masks */
+#define PRI_CTRL_PRI_LVL1 (0x0000000C)
+#define PRI_CTRL_PRI_LVL0 (0x00000003)
+
+/* si_ctrl Register Bit Masks */
+#define SI_CTRL_ERR_DISABLE (0x00000010)
+#define SI_CTRL_IDRC_DISABLE (0x00000008)
+#define SI_CTRL_RD_SAFE_EN (0x00000004)
+#define SI_CTRL_RD_PREFETCH_DISABLE (0x00000002)
+#define SI_CTRL_RD_PREFEFETCH_VAL (0x00000001)
+
+/* control Register Bit Masks */
+#define USB_CTRL_IOENB (0x00000004)
+#define USB_CTRL_ULPI_INT0EN (0x00000001)
+#define USB_CTRL_OTG_WUIR (0x80000000)
+#define USB_CTRL_OTG_WUIE (0x08000000)
+#define USB_CTRL_OTG_VWUE (0x00001000)
+#define USB_CTRL_OTG_IWUE (0x00100000)
+
+/* PHY control0 Register Bit Masks */
+#define PHY_CTRL0_CONF2 (1 << 26)
+
+/* USB UH2 CTRL Register Bits */
+#define USB_UH2_OVBWK_EN (1 << 6) /* OTG VBUS Wakeup Enable */
+#define USB_UH2_OIDWK_EN (1 << 5) /* OTG ID Wakeup Enable */
+/*!
+ * Endpoint Queue Head data struct
+ * Rem: all the variables of qh are LittleEndian Mode
+ * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr
+ */
+struct ep_queue_head {
+ /*!
+ * Mult(31-30) , Zlt(29) , Max Pkt len and IOS(15)
+ */
+ u32 max_pkt_length;
+
+ /*!
+ * Current dTD Pointer(31-5)
+ */
+ u32 curr_dtd_ptr;
+
+ /*!
+ * Next dTD Pointer(31-5), T(0)
+ */
+ u32 next_dtd_ptr;
+
+ /*!
+ * Total bytes (30-16), IOC (15), MultO(11-10), STS (7-0)
+ */
+ u32 size_ioc_int_sts;
+
+ /*!
+ * Buffer pointer Page 0 (31-12)
+ */
+ u32 buff_ptr0;
+
+ /*!
+ * Buffer pointer Page 1 (31-12)
+ */
+ u32 buff_ptr1;
+
+ /*!
+ * Buffer pointer Page 2 (31-12)
+ */
+ u32 buff_ptr2;
+
+ /*!
+ * Buffer pointer Page 3 (31-12)
+ */
+ u32 buff_ptr3;
+
+ /*!
+ * Buffer pointer Page 4 (31-12)
+ */
+ u32 buff_ptr4;
+
+ /*!
+ * reserved field 1
+ */
+ u32 res1;
+ /*!
+ * Setup data 8 bytes
+ */
+ u8 setup_buffer[8]; /* Setup data 8 bytes */
+
+ /*!
+ * reserved field 2,pad out to 64 bytes
+ */
+ u32 res2[4];
+};
+
+/* Endpoint Queue Head Bit Masks */
+#define EP_QUEUE_HEAD_MULT_POS (30)
+#define EP_QUEUE_HEAD_ZLT_SEL (0x20000000)
+#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS (16)
+#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
+#define EP_QUEUE_HEAD_IOS (0x00008000)
+#define EP_QUEUE_HEAD_NEXT_TERMINATE (0x00000001)
+#define EP_QUEUE_HEAD_IOC (0x00008000)
+#define EP_QUEUE_HEAD_MULTO (0x00000C00)
+#define EP_QUEUE_HEAD_STATUS_HALT (0x00000040)
+#define EP_QUEUE_HEAD_STATUS_ACTIVE (0x00000080)
+#define EP_QUEUE_CURRENT_OFFSET_MASK (0x00000FFF)
+#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0
+#define EP_QUEUE_FRINDEX_MASK (0x000007FF)
+#define EP_MAX_LENGTH_TRANSFER (0x4000)
+
+/*!
+ * Endpoint Transfer Descriptor data struct
+ * Rem: all the variables of td are LittleEndian Mode
+ * must be 32-byte aligned
+ */
+struct ep_td_struct {
+ /*!
+ * Next TD pointer(31-5), T(0) set indicate invalid
+ */
+ u32 next_td_ptr;
+
+ /*!
+ * Total bytes (30-16), IOC (15),MultO(11-10), STS (7-0)
+ */
+ u32 size_ioc_sts;
+
+ /*!
+ * Buffer pointer Page 0
+ */
+ u32 buff_ptr0;
+
+ /*!
+ * Buffer pointer Page 1
+ */
+ u32 buff_ptr1;
+
+ /*!
+ * Buffer pointer Page 2
+ */
+ u32 buff_ptr2;
+
+ /*!
+ * Buffer pointer Page 3
+ */
+ u32 buff_ptr3;
+
+ /*!
+ * Buffer pointer Page 4
+ */
+ u32 buff_ptr4;
+
+ /*!
+ * dma address of this td
+ * */
+ dma_addr_t td_dma;
+
+ /*!
+ * virtual address of next td
+ * */
+ struct ep_td_struct *next_td_virt;
+
+ /*!
+ * make it an even 16 words
+ * */
+ u32 res[7];
+};
+
+/*!
+ * Endpoint Transfer Descriptor bit Masks
+ */
+#define DTD_NEXT_TERMINATE (0x00000001)
+#define DTD_IOC (0x00008000)
+#define DTD_STATUS_ACTIVE (0x00000080)
+#define DTD_STATUS_HALTED (0x00000040)
+#define DTD_STATUS_DATA_BUFF_ERR (0x00000020)
+#define DTD_STATUS_TRANSACTION_ERR (0x00000008)
+#define DTD_RESERVED_FIELDS (0x80007300)
+#define DTD_ADDR_MASK 0xFFFFFFE0
+#define DTD_PACKET_SIZE (0x7FFF0000)
+#define DTD_LENGTH_BIT_POS (16)
+#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \
+ DTD_STATUS_DATA_BUFF_ERR | \
+ DTD_STATUS_TRANSACTION_ERR)
+/* Alignment requirements; must be a power of two */
+#define DTD_ALIGNMENT 0x20
+#define QH_ALIGNMENT 2048
+
+/* Controller dma boundary */
+#define UDC_DMA_BOUNDARY 0x1000
+
+/* -----------------------------------------------------------------------*/
+/* ##### enum data
+*/
+typedef enum {
+ e_ULPI,
+ e_UTMI_8BIT,
+ e_UTMI_16BIT,
+ e_SERIAL
+} e_PhyInterface;
+
+/*-------------------------------------------------------------------------*/
+
+struct fsl_req {
+ struct usb_request req;
+ struct list_head queue;
+ /* ep_queue() func will add
+ a request->queue into a udc_ep->queue 'd tail */
+ struct fsl_ep *ep;
+ unsigned mapped;
+
+ struct ep_td_struct *head, *tail; /* For dTD List
+ this is a BigEndian Virtual addr */
+ unsigned int dtd_count;
+ /* just for IRAM patch */
+ dma_addr_t oridma; /* original dma */
+ size_t buffer_offset; /* offset of user buffer */
+ int last_one; /* mark if reach to last packet */
+ struct ep_td_struct *cur; /* current tranfer dtd */
+};
+
+#define REQ_UNCOMPLETE (1)
+
+struct fsl_ep {
+ struct usb_ep ep;
+ struct list_head queue;
+ struct fsl_udc *udc;
+ struct ep_queue_head *qh;
+ const struct usb_endpoint_descriptor *desc;
+ struct usb_gadget *gadget;
+
+ char name[14];
+ unsigned stopped:1;
+};
+
+#define EP_DIR_IN 1
+#define EP_DIR_OUT 0
+
+struct fsl_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct fsl_usb2_platform_data *pdata;
+ struct fsl_ep *eps;
+ unsigned int max_ep;
+ unsigned int irq;
+
+ struct usb_ctrlrequest local_setup_buff;
+ spinlock_t lock;
+ u32 xcvr_type;
+ struct otg_transceiver *transceiver;
+ unsigned softconnect:1;
+ unsigned vbus_active:1;
+ unsigned stopped:1;
+ unsigned remote_wakeup:1;
+ unsigned already_stopped:1;
+
+ struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */
+ struct fsl_req *status_req; /* ep0 status request */
+ struct fsl_req *data_req; /* ep0 data request */
+ struct dma_pool *td_pool; /* dma pool for DTD */
+ enum fsl_usb2_phy_modes phy_mode;
+
+ size_t ep_qh_size; /* size after alignment adjustment*/
+ dma_addr_t ep_qh_dma; /* dma address of QH */
+
+ u32 max_pipes; /* Device max pipes */
+ u32 max_use_endpts; /* Max endpointes to be used */
+ u32 bus_reset; /* Device is bus reseting */
+ u32 resume_state; /* USB state to resume */
+ u32 usb_state; /* USB current state */
+ u32 usb_next_state; /* USB next state */
+ u32 ep0_dir; /* Endpoint zero direction: can be
+ USB_DIR_IN or USB_DIR_OUT */
+ u32 usb_sof_count; /* SOF count */
+ u32 errors; /* USB ERRORs count */
+ u8 device_address; /* Device USB address */
+
+ struct completion *done; /* to make sure release() is done */
+ u32 iram_buffer[IRAM_PPH_NTD];
+ u32 iram_buffer_v[IRAM_PPH_NTD];
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt "\n", \
+ __func__, ## args)
+#else
+#define DBG(fmt, args...) do {} while (0)
+#endif
+
+#if 0
+static void dump_msg(const char *label, const u8 * buf, unsigned int length)
+{
+ unsigned int start, num, i;
+ char line[52], *p;
+
+ if (length >= 512)
+ return;
+ pr_debug("udc: %s, length %u:\n", label, length);
+ start = 0;
+ while (length > 0) {
+ num = min(length, 16u);
+ p = line;
+ for (i = 0; i < num; ++i) {
+ if (i == 8)
+ *p++ = ' ';
+ sprintf(p, " %02x", buf[i]);
+ p += 3;
+ }
+ *p = 0;
+ printk(KERN_DEBUG "%6x: %s\n", start, line);
+ buf += num;
+ start += num;
+ length -= num;
+ }
+}
+#endif
+
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(stuff...) do {} while (0)
+#endif
+
+#define ERR(stuff...) printk(KERN_ERR "udc: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
+
+/*-------------------------------------------------------------------------*/
+
+/* ### Add board specific defines here
+ */
+
+/*
+ * ### pipe direction macro from device view
+ */
+#define USB_RECV (0) /* OUT EP */
+#define USB_SEND (1) /* IN EP */
+
+/*
+ * ### internal used help routines.
+ */
+#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF)
+#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
+
+#define ep_is_in(EP) ( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
+ USB_DIR_IN ):((EP)->desc->bEndpointAddress \
+ & USB_DIR_IN)==USB_DIR_IN)
+
+#define get_ep_by_pipe(udc, pipe) ((pipe == 1)? &udc->eps[0]: \
+ &udc->eps[pipe])
+#define get_pipe_by_windex(windex) ((windex & USB_ENDPOINT_NUMBER_MASK) \
+ * 2 + ((windex & USB_DIR_IN) ? 1 : 0))
+
+/* Bulk only class request */
+#define USB_BULK_RESET_REQUEST 0xff
+
+#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_STMP3XXX)
+#include <mach/fsl_usb_gadget.h>
+#elif CONFIG_PPC32
+#include <asm/fsl_usb_gadget.h>
+#endif
+
+#endif /* __ARCOTG_UDC_H */
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 9462e30192d8..8e20d46e3b83 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -162,7 +162,11 @@ ep_matches (
desc->bEndpointAddress &= USB_DIR_IN;
if (isdigit (ep->name [2])) {
u8 num = simple_strtol (&ep->name [2], NULL, 10);
- desc->bEndpointAddress |= num;
+ if (desc->bEndpointAddress & 0x7) {
+ if (num != (desc->bEndpointAddress & 0x7))
+ return 0;
+ } else
+ desc->bEndpointAddress |= num;
#ifdef MANY_ENDPOINTS
} else if (desc->bEndpointAddress & USB_DIR_IN) {
if (++in_epnum > 15)
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 37252d0012a7..8e34d9dd0fe7 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -385,7 +385,7 @@ static int __init init(void)
{
return usb_composite_register(&eth_driver);
}
-module_init(init);
+late_initcall(init);
static void __exit cleanup(void)
{
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index c4e62a6297d7..90199d4ec3fc 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -692,8 +692,16 @@ struct fsg_dev {
unsigned int nluns;
struct lun *luns;
struct lun *curlun;
+
+#ifdef CONFIG_STMP_UTP
+ void *utp;
+#endif
};
+#ifdef CONFIG_STMP_UTP
+#include "stmp_updater.h"
+#endif
+
typedef void (*fsg_routine_t)(struct fsg_dev *);
static int exception_in_progress(struct fsg_dev *fsg)
@@ -712,6 +720,10 @@ static void set_bulk_out_req_length(struct fsg_dev *fsg,
if (rem > 0)
length += fsg->bulk_out_maxpacket - rem;
bh->outreq->length = length;
+#if 0
+ if (bh->bulk_out_intended_length == 31)
+ bh->outreq->length = 31;
+#endif
}
static struct fsg_dev *the_fsg;
@@ -731,7 +743,7 @@ static void dump_msg(struct fsg_dev *fsg, const char *label,
if (length < 512) {
DBG(fsg, "%s, length %u:\n", label, length);
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,
- 16, 1, buf, length, 0);
+ 16, 1, buf, length, !0);
}
}
@@ -791,6 +803,11 @@ static u32 get_be32(u8 *buf)
((u32) buf[2] << 8) | ((u32) buf[3]);
}
+static u64 get_be64(u8 *buf)
+{
+ return ((u64)get_be32(buf) << 32) | get_be32(buf + 4);
+}
+
static void put_be16(u8 *buf, u16 val)
{
buf[0] = val >> 8;
@@ -837,7 +854,11 @@ device_desc = {
.iManufacturer = STRING_MANUFACTURER,
.iProduct = STRING_PRODUCT,
+#ifdef CONFIG_STMP_UTP
+ .iSerialNumber = 0,
+#else
.iSerialNumber = STRING_SERIAL,
+#endif
.bNumConfigurations = 1,
};
@@ -2073,6 +2094,13 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
}
#endif
+#ifdef CONFIG_STMP_UTP
+ if (utp_get_sense(fsg) == 0) { /* got the sense from the UTP */
+ sd = UTP_CTX(fsg)->sd;
+ sdinfo = UTP_CTX(fsg)->sdinfo;
+ valid = 0;
+ } else
+#endif
if (!curlun) { // Unsupported LUNs are okay
fsg->bad_lun_okay = 1;
sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
@@ -2094,6 +2122,9 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
buf[7] = 18 - 8; // Additional sense length
buf[12] = ASC(sd);
buf[13] = ASCQ(sd);
+#ifdef CONFIG_STMP_UTP
+ put_be32(&buf[8], UTP_CTX(fsg)->sdinfo_h);
+#endif
return 18;
}
@@ -2780,6 +2811,13 @@ static int do_scsi_command(struct fsg_dev *fsg)
fsg->phase_error = 0;
fsg->short_packet_received = 0;
+#ifdef CONFIG_STMP_UTP
+ reply = utp_handle_message(fsg, fsg->cmnd, reply);
+
+ if (reply != -EINVAL)
+ return reply;
+#endif
+
down_read(&fsg->filesem); // We're using the backing file
switch (fsg->cmnd[0]) {
@@ -3443,10 +3481,12 @@ static int fsg_main_thread(void *fsg_)
/* Allow the thread to be frozen */
set_freezable();
+#ifndef CONFIG_STMP_UTP
/* Arrange for userspace references to be interpreted as kernel
* pointers. That way we can pass a kernel pointer to a routine
* that expects a __user pointer and it will work okay. */
set_fs(get_ds());
+#endif
/* The main loop */
while (fsg->state != FSG_STATE_TERMINATED) {
@@ -3754,6 +3794,9 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
}
set_gadget_data(gadget, NULL);
+#ifdef CONFIG_STMP_UTP
+ utp_exit(fsg);
+#endif
}
@@ -3788,6 +3831,17 @@ static int __init check_parameters(struct fsg_dev *fsg)
prot = simple_strtol(mod_data.protocol_parm, NULL, 0);
+#ifdef CONFIG_STMP_UTP
+ mod_data.can_stall = 0;
+ mod_data.removable = 1;
+ mod_data.nluns = 1;
+ mod_data.file[0] = NULL;
+ mod_data.vendor = 0x066F;
+ mod_data.product = 0x37FF;
+ pr_info("%s:UTP settings are in place now, overriding defaults\n",
+ __func__);
+#endif
+
#ifdef CONFIG_USB_FILE_STORAGE_TEST
if (strnicmp(mod_data.transport_parm, "BBB", 10) == 0) {
; // Use default setting
@@ -3840,8 +3894,9 @@ static int __init check_parameters(struct fsg_dev *fsg)
return 0;
}
-
-
+#ifdef CONFIG_STMP_UTP
+#include "stmp_updater.c"
+#endif
static int __init fsg_bind(struct usb_gadget *gadget)
{
struct fsg_dev *fsg = the_fsg;
@@ -3866,6 +3921,10 @@ static int __init fsg_bind(struct usb_gadget *gadget)
dev_attr_file.store = store_file;
}
+#ifdef CONFIG_STMP_UTP
+ utp_init(fsg);
+#endif
+
/* Find out how many LUNs there should be */
i = mod_data.nluns;
if (i == 0)
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 4e3107dd2f34..79b90416e8de 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -92,6 +92,12 @@
#define gadget_is_pxa27x(g) 0
#endif
+#ifdef CONFIG_USB_GADGET_ARC
+#define gadget_is_arcotg(g) !strcmp("fsl-usb2-udc", (g)->name)
+#else
+#define gadget_is_arcotg(g) 0
+#endif
+
#ifdef CONFIG_USB_GADGET_ATMEL_USBA
#define gadget_is_atmel_usba(g) !strcmp("atmel_usba_udc", (g)->name)
#else
@@ -158,6 +164,12 @@
#define gadget_is_fsl_qe(g) 0
#endif
+/* Samsung S3C2443 UDC */
+#ifdef CONFIG_USB_GADGET_S3C2443
+#define gadget_is_s3c2443(g) !strcmp("s3c2443_udc", (g)->name)
+#else
+#define gadget_is_s3c2443(g) 0
+#endif
// CONFIG_USB_GADGET_SX2
// CONFIG_USB_GADGET_AU1X00
@@ -225,6 +237,10 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x21;
else if (gadget_is_fsl_qe(gadget))
return 0x22;
+ else if (gadget_is_s3c2443(gadget))
+ return 0x23;
+ else if (gadget_is_arcotg(gadget))
+ return 0x24;
return -ENOENT;
}
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index eeb26c0f88e5..9c9c612ec691 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -40,6 +40,8 @@
#include <linux/usb/gadgetfs.h>
#include <linux/usb/gadget.h>
+#include <linux/delay.h>
+#include <linux/time.h>
/*
* The gadgetfs API maps each endpoint to a file descriptor so that you
@@ -81,6 +83,9 @@ MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_AUTHOR ("David Brownell");
MODULE_LICENSE ("GPL");
+/* Cancel IO, To store the bulkin and bulkout ep data. */
+static struct ep_data *gp_ep_bulkin_data;
+static struct ep_data *gp_ep_bulkout_data;
/*----------------------------------------------------------------------*/
@@ -265,6 +270,10 @@ static const char *CHIP;
#define INFO(dev,fmt,args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
+/* Cancel IO */
+static int mtp_ctrl_cmd;
+static int gbCancelFlag;
+static unsigned long mtptimestamp;
/*----------------------------------------------------------------------*/
@@ -275,6 +284,17 @@ static const char *CHIP;
* precise FIFO status when recovering from cancellation.
*/
+/* Cancel IO */
+static void cancel_io_process(struct work_struct *work)
+{
+ if (gp_ep_bulkout_data->req->status == -EINPROGRESS)
+ usb_ep_dequeue(gp_ep_bulkout_data->ep, gp_ep_bulkout_data->req);
+
+ if (gp_ep_bulkin_data->req->status == -EINPROGRESS)
+ usb_ep_dequeue(gp_ep_bulkin_data->ep, gp_ep_bulkin_data->req);
+}
+static DECLARE_DELAYED_WORK(cancel_work, cancel_io_process);
+
static void epio_complete (struct usb_ep *ep, struct usb_request *req)
{
struct ep_data *epdata = ep->driver_data;
@@ -870,10 +890,13 @@ ep_open (struct inode *inode, struct file *fd)
{
struct ep_data *data = inode->i_private;
int value = -EBUSY;
+ char *epin = "ep1in";
+ char *epout = "ep1out";
if (down_interruptible (&data->lock) != 0)
return -EINTR;
spin_lock_irq (&data->dev->lock);
+
if (data->dev->state == STATE_DEV_UNBOUND)
value = -ENOENT;
else if (data->state == STATE_EP_DISABLED) {
@@ -882,9 +905,16 @@ ep_open (struct inode *inode, struct file *fd)
get_ep (data);
fd->private_data = data;
VDEBUG (data->dev, "%s ready\n", data->name);
+ /* Cancel IO */
+ if (0 == strcmp(data->name, epin))
+ gp_ep_bulkin_data = fd->private_data;
+
+ if (0 == strcmp(data->name, epout))
+ gp_ep_bulkout_data = fd->private_data;
} else
DBG (data->dev, "%s state %d\n",
data->name, data->state);
+
spin_unlock_irq (&data->dev->lock);
up (&data->lock);
return value;
@@ -996,7 +1026,7 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
retval = -EL2HLT;
dev->state = STATE_DEV_CONNECTED;
- } else if (len == 0) { /* ack SET_CONFIGURATION etc */
+ } else if (len == 0) { /* ack SET_CONFIGURATION etc */
struct usb_ep *ep = dev->gadget->ep0;
struct usb_request *req = dev->req;
@@ -1017,7 +1047,7 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
usb_gadget_vbus_draw(dev->gadget, 2 * power);
}
- } else { /* collect OUT data */
+ } else { /* collect OUT data */
if ((fd->f_flags & O_NONBLOCK) != 0
&& !dev->setup_out_ready) {
retval = -EAGAIN;
@@ -1042,11 +1072,15 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
retval = -EIO;
else {
len = min (len, (size_t)dev->req->actual);
-// FIXME don't call this with the spinlock held ...
+/* FIXME don't call this with the spinlock held ... */
if (copy_to_user (buf, dev->req->buf, len))
retval = -EFAULT;
+ else
+ /* Bug of Cancel IO 6 bytes read. */
+ retval = len;
clean_req (dev->gadget->ep0, dev->req);
/* NOTE userspace can't yet choose to stall */
+ dev->state = STATE_DEV_CONNECTED; /* Cancel IO */
}
}
goto done;
@@ -1060,6 +1094,12 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
len -= len % sizeof (struct usb_gadgetfs_event);
dev->usermode_setup = 1;
+ /* Cancel IO, signal abort blocked IO. */
+ if (mtp_ctrl_cmd == 1) {
+ mtp_ctrl_cmd = 0;
+ schedule_delayed_work(&cancel_work, HZ / 100);
+ }
+
scan:
/* return queued events right away */
if (dev->ev_next != 0) {
@@ -1386,6 +1426,16 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
struct usb_gadgetfs_event *event;
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
+ struct timeval tv;
+
+ /* Cancel IO */
+ if (0x67 == ctrl->bRequest && 1 == gbCancelFlag
+ && dev->state == STATE_DEV_SETUP)
+ dev->state = STATE_DEV_CONNECTED;
+
+ if (0x67 == ctrl->bRequest && 2 == mtp_ctrl_cmd
+ && dev->state == STATE_DEV_SETUP)
+ dev->state = STATE_DEV_CONNECTED;
spin_lock (&dev->lock);
dev->setup_abort = 0;
@@ -1413,6 +1463,11 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
*/
} else if (dev->state == STATE_DEV_SETUP)
dev->setup_abort = 1;
+ /*Cancel IO */
+ if (mtp_ctrl_cmd == 1 && gbCancelFlag == 1 && dev->setup_abort == 1) {
+ INFO(dev, "0x64->setup\n");
+ dev->setup_abort = 0;
+ }
req->buf = dev->rbuf;
req->dma = DMA_ADDR_INVALID;
@@ -1438,7 +1493,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
make_qualifier (dev);
break;
case USB_DT_OTHER_SPEED_CONFIG:
- // FALLTHROUGH
+ /* FALLTHROUGH */
#endif
case USB_DT_CONFIG:
value = config_buf (dev,
@@ -1545,11 +1600,63 @@ delegate:
/* we can't currently stall these */
dev->setup_can_stall = 0;
}
+ /* Cancel IO */
+ if (0x67 == ctrl->bRequest && 1 == gbCancelFlag) {
+ gbCancelFlag = 0;
+
+ setup_req(gadget->ep0, dev->req, 4);
+ *(unsigned long *)dev->req->buf = 0x20190004;
+ usb_ep_queue(gadget->ep0, dev->req, GFP_ATOMIC);
+
+ spin_unlock(&dev->lock);
+ return 0;
+ }
+ if (ctrl->bRequest == 0x67 && mtp_ctrl_cmd == 2) {
+ /* get status */
+ mtp_ctrl_cmd = 0;
+ }
/* state changes when reader collects event */
event = next_event (dev, GADGETFS_SETUP);
event->u.setup = *ctrl;
+ /* Cancel IO */
+ if (0x64 == ctrl->bRequest) {
+ mtp_ctrl_cmd = 1;
+ gbCancelFlag = 1;
+
+ /* get the timestamp */
+ do_gettimeofday(&tv);
+ mtptimestamp = tv.tv_usec;
+ event->u.setup.wValue =
+ (unsigned short)tv.tv_usec;
+ }
+ if (0x66 == ctrl->bRequest) {
+ /* get the timestamp */
+ do_gettimeofday(&tv);
+ mtptimestamp = tv.tv_usec;
+ event->u.setup.wValue =
+ (unsigned short)tv.tv_usec;
+ }
+
ep0_readable (dev);
+ /* Reset request. */
+ if (ctrl->bRequest == 0x66) { /* reset ,send ZLP */
+ mtp_ctrl_cmd = 2;
+
+ if (gp_ep_bulkout_data->req->status ==
+ -EINPROGRESS) {
+ usb_ep_dequeue(gp_ep_bulkout_data->ep,
+ gp_ep_bulkout_data->req);
+ }
+ if (gp_ep_bulkin_data->req->status ==
+ -EINPROGRESS) {
+ usb_ep_dequeue(gp_ep_bulkin_data->ep,
+ gp_ep_bulkin_data->req);
+ }
+ }
+ if (ctrl->bRequest == 0x65)
+ pr_debug("i:0x65,not supported\n");
+
spin_unlock (&dev->lock);
return 0;
}
diff --git a/drivers/usb/gadget/s3c2443_udc.c b/drivers/usb/gadget/s3c2443_udc.c
new file mode 100644
index 000000000000..3eaf7d14f84a
--- /dev/null
+++ b/drivers/usb/gadget/s3c2443_udc.c
@@ -0,0 +1,2810 @@
+/* -*- linux-c -*-
+ *
+ * drivers/usb/gadget/s3c24xx_udc.c
+ *
+ * Samsung S3C on-chip full/high speed USB device controllers
+ *
+ * $Id: s3c-udc-hs.c,v 1.26 2007/02/22 09:45:04 ihlee215 Exp $*
+ *
+ * Copyright (C) 2006 for Samsung Electronics
+ *
+ * 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 "s3c2443_udc.h"
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include <linux/bug.h>
+
+/* @TODO: USB Device DMA support */
+#define RX_DMA_MODE 0
+#define TX_DMA_MODE 0
+
+#if 0
+#define DEBUG_S3C2443_UDC
+#endif
+
+#define pk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] s3c2443-udc: " fmt, ## args)
+#define pk_info(fmt, args...) printk(KERN_DEBUG "s3c2443-udc: " fmt, ## args)
+
+#ifdef DEBUG_S3C2443_UDC
+#define pk_dbg(fmt, args...) printk(KERN_DEBUG "s3c2443-udc: %s() " fmt, __func__ , \
+ ## args)
+#else
+#define pk_dbg(fmt, args...) do { } while(0)
+#endif
+
+#if 0
+#define S3C2443_UDC_DBG_OUT
+#endif
+
+#if defined(S3C2443_UDC_DBG_OUT)
+#define pk_dbg_out(fmt, args...) printk(KERN_DEBUG "[OUT] " fmt, ## args)
+#else
+#define pk_dbg_out(fmt, args...) do { } while(0)
+#endif /* S3C2443_UDC_DBG_OUT */
+
+/*
+ * This macro enables the debug messages when the driver is going to access to the
+ * internal queue of the IN-endpoints
+ */
+#if 0
+#define DEBUG_S3C2443_UDC_QUEUE
+#endif
+
+/* Some driver infos */
+#define DRIVER_DESC "S3C2443 Dual-speed USB Device"
+#define DRIVER_NAME "s3c2443_udc"
+#define DRIVER_BUILD_TIME __TIME__
+#define DRIVER_BUILD_DATE __DATE__
+
+#define IOMEMSIZE(s) (s->end - s->start + 1)
+
+/* Internal variables */
+struct s3c24xx_udc *the_controller;
+static const char driver_desc[] = DRIVER_DESC;
+static const char ep0name[] = "ep0-control";
+
+/* Max packet sizes */
+static u32 ep0_fifo_size = 64;
+static u32 ep_fifo_size = 512;
+static u32 ep_fifo_size2 = 1024;
+
+/* Internal functions */
+static int s3c24xx_udc_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *);
+static int s3c24xx_udc_ep_disable(struct usb_ep *ep);
+static struct usb_request *s3c24xx_udc_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
+static void s3c24xx_udc_free_request(struct usb_ep *ep, struct usb_request *);
+static int s3c24xx_udc_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags);
+static int s3c24xx_udc_dequeue(struct usb_ep *ep, struct usb_request *);
+static int s3c24xx_udc_set_halt(struct usb_ep *ep, int);
+static int s3c24xx_udc_fifo_status(struct usb_ep *ep);
+static void s3c24xx_udc_fifo_flush(struct usb_ep *ep);
+static void s3c24xx_udc_ep0_kick(struct s3c24xx_udc *udc, struct s3c_ep *ep);
+static void s3c24xx_handle_ep0(struct s3c24xx_udc *udc);
+static void done(struct s3c_ep *ep, struct s3c_request *req, int status);
+static void stop_activity(struct s3c24xx_udc *dev, struct usb_gadget_driver *driver);
+static int s3c24xx_udc_enable(struct s3c24xx_udc *udc);
+static void s3c24xx_udc_set_address(struct s3c24xx_udc *dev, unsigned char address);
+static void reconfig_usbd(struct s3c24xx_udc *udc);
+static void s3c24xx_ep0_setup(struct s3c24xx_udc *udc);
+static int s3c24xx_udc_write_fifo(struct s3c_ep *ep, struct s3c_request *req);
+
+
+static inline struct s3c24xx_udc *gadget_to_udc(struct usb_gadget *gadget)
+{
+ return container_of(gadget, struct s3c24xx_udc, gadget);
+}
+
+static spinlock_t regs_lock = SPIN_LOCK_UNLOCKED;
+
+static inline void s3c2443_print_err_packet_setup(int errcode,
+ struct usb_ctrlrequest *pctrl)
+{
+ printk(KERN_DEBUG "[ ERROR ] s3c2443-udc: Err %i | bRequestType 0x%02x | "
+ "bRequest 0x%02x | wValue 0x%04x | wIndex 0x%04x | wLength %u\n",
+ errcode, pctrl->bRequestType, pctrl->bRequest,
+ pctrl->wValue, pctrl->wIndex, pctrl->wLength);
+}
+
+/* Read access to one of the indexed registers */
+static inline ulong usb_read(struct s3c24xx_udc *udc, ulong port, u8 ind)
+{
+ ulong retval;
+
+ spin_lock(&regs_lock);
+ writel(ind, udc->base + S3C24XX_UDC_IR_REG);
+ retval = readl(udc->base + port);
+ spin_unlock(&regs_lock);
+ return retval;
+}
+
+/* Write access to one of the indexed registers */
+static inline void usb_write(struct s3c24xx_udc *udc, ulong val, ulong port, u8 ind)
+{
+ spin_lock(&regs_lock);
+ writel(ind, udc->base + S3C24XX_UDC_IR_REG);
+ writel(val, udc->base + port);
+ spin_unlock(&regs_lock);
+}
+
+static inline void usb_set(struct s3c24xx_udc *udc, ulong val, ulong port, u8 ind)
+{
+ spin_lock(&regs_lock);
+ writel(ind, udc->base + S3C24XX_UDC_IR_REG);
+ writel(readl(udc->base + port) | val, udc->base + port);
+ spin_unlock(&regs_lock);
+}
+
+static inline void usb_clear(struct s3c24xx_udc *udc, ulong val, ulong port, u8 ind)
+{
+ spin_lock(&regs_lock);
+ writel(ind, udc->base + S3C24XX_UDC_IR_REG);
+ writel(readl(udc->base + port) & ~val, udc->base + port);
+ spin_unlock(&regs_lock);
+}
+
+/* Return a value different than zero if the EP is enabled */
+static inline int s3c24xx_ep_enabled(struct s3c24xx_udc *udc, int epnr)
+{
+ ulong regval;
+
+ regval = readl(udc->base + S3C24XX_UDC_EIER_REG);
+ return (regval & (1 << epnr));
+}
+
+/* Enable/disable the interrupt of the passed EP-number */
+static inline void s3c24xx_ep_irq_enable(struct s3c24xx_udc *udc, int epnr, int enable)
+{
+ ulong eier;
+
+ eier = readl(udc->base + S3C24XX_UDC_EIER_REG);
+ if (enable)
+ eier |= (1 << epnr);
+ else
+ eier &= ~(1 << epnr);
+ writel(eier, udc->base + S3C24XX_UDC_EIER_REG);
+}
+
+static inline void s3c2443_udc_print_regs(char *marke, struct s3c24xx_udc *udc, int epnr)
+{
+ struct regs_t {
+ char *name;
+ ulong addr;
+ };
+
+ int pos, old_epnr;
+ ulong regval;
+ static const struct regs_t regs[] = {
+ { "EIR ", S3C24XX_UDC_EIR_REG },
+ { "EIER ", S3C24XX_UDC_EIER_REG },
+ { "EDR ", S3C24XX_UDC_EDR_REG },
+ { "TR ", S3C24XX_UDC_TR_REG },
+ { "SSR ", S3C24XX_UDC_SSR_REG },
+ { "SCR ", S3C24XX_UDC_SCR_REG },
+ { "EP0SR ", S3C24XX_UDC_EP0SR_REG },
+ { "FCON ", S3C24XX_UDC_FIFO_CON_REG },
+ { "FSTAT ", S3C24XX_UDC_FIFO_STATUS_REG },
+ { "ESR ", S3C24XX_UDC_ESR_REG },
+ { "ECR ", S3C24XX_UDC_ECR_REG },
+ { "BRCR ", S3C24XX_UDC_BRCR_REG },
+ { "BWCR ", S3C24XX_UDC_BWCR_REG },
+ };
+
+ /* First get a backup of the current EP number */
+ old_epnr = readl(udc->base + S3C24XX_UDC_IR_REG);
+ writel(epnr, udc->base + S3C24XX_UDC_IR_REG);
+
+ /* Now print all the registers */
+ printk(KERN_DEBUG "%s\n", marke);
+ for (pos = 0; pos < ARRAY_SIZE(regs); pos++) {
+ regval = usb_read(udc, regs[pos].addr, epnr);
+ printk(KERN_DEBUG "%s: 0x%08lx\n", regs[pos].name, regval);
+ }
+
+ writel(old_epnr, udc->base + S3C24XX_UDC_IR_REG);
+}
+
+static struct usb_ep_ops s3c24xx_ep_ops = {
+ .enable = s3c24xx_udc_ep_enable,
+ .disable = s3c24xx_udc_ep_disable,
+
+ .alloc_request = s3c24xx_udc_alloc_request,
+ .free_request = s3c24xx_udc_free_request,
+
+ .queue = s3c24xx_udc_queue,
+ .dequeue = s3c24xx_udc_dequeue,
+
+ .set_halt = s3c24xx_udc_set_halt,
+ .fifo_status = s3c24xx_udc_fifo_status,
+ .fifo_flush = s3c24xx_udc_fifo_flush,
+};
+
+/*
+ * Function for writing from the request buffer into the EP-FIFO
+ * The function updates the internal actual length of the USB-request for a possible
+ * next transfer of the same request.
+ * The return value is the number of remaining bytes in the request. If the return
+ * value is equal zero, then there is no more data to process in the request
+ * (Luis Galdos)
+ */
+static inline int s3c24xx_udc_write_packet(struct s3c_ep *ep, struct s3c_request *req)
+{
+ u16 *buf;
+ int length, count;
+ u32 fifo = ep->fifo;
+ struct s3c24xx_udc *udc;
+ int max, remaining, epnr;
+ u8 *ptr;
+
+ /* @XXX: Need some sanity checks (Luis Galdos) */
+ udc = ep->dev;
+ max = ep->ep.maxpacket;
+ epnr = ep_index(ep);
+
+ /* Get the number of remaining bytes */
+ remaining = req->req.length - req->req.actual;
+ if (!remaining) {
+ pk_dbg("EP%i: Sending ZLP (actual: %i)\n",
+ epnr, req->req.actual);
+
+ /* Send a frame with zero length */
+ /* usb_set(udc, S3C24XX_UDC_ECR_TZLS, S3C24XX_UDC_ECR_REG, epnr); */
+ usb_write(udc, 0, S3C24XX_UDC_BWCR_REG, epnr);
+
+ length = remaining;
+ goto exit_write_packet;
+ }
+
+ /* Use first a u8 pointer for obtaining the correct buffer address */
+ ptr = req->req.buf + req->req.actual;
+ buf = (u16 *)ptr;
+ prefetch(buf);
+
+ /* Only send the maximal allowed number of bytes */
+ length = min(remaining, max);
+ req->req.actual += length;
+
+ /* First write the number of bytes to transfer, and then fill the FIFO */
+ usb_write(udc, length, S3C24XX_UDC_BWCR_REG, epnr);
+ for (count = 0; count < length; count += 2)
+ writel(*buf++, udc->base + fifo);
+
+ /* Return the number of remaining bytes of the passed request */
+exit_write_packet:
+ return (remaining - length);
+}
+
+/*
+ * Test function which returns the number of bytes written into the FIFO.
+ * This new function was implemented due an unknown issue registered with the
+ * Ethernet-gadget (the UDC send packets with size of 514 bytes!)
+ * (Luis Galdos)
+ */
+static inline int s3c24xx_udc_write_packet2(struct s3c_ep *ep, struct s3c_request *req)
+{
+ u16 *buf;
+ int length, count;
+ u32 fifo = ep->fifo;
+ struct s3c24xx_udc *udc;
+ int max, remaining, epnr;
+ u8 *ptr;
+
+ udc = ep->dev;
+ max = ep->ep.maxpacket;
+ epnr = ep_index(ep);
+
+ /* Get the number of remaining bytes */
+ remaining = req->req.length - req->req.actual;
+ if (!remaining) {
+
+ pk_dbg("EP%i: Sending ZLP (actual: %i)\n", epnr,
+ req->req.actual);
+
+ /*
+ * Send a frame with zero length.
+ * DONT use the TZLS control bit of the EP control register ECR.
+ */
+ usb_write(udc, 0, S3C24XX_UDC_BWCR_REG, epnr);
+ length = 0;
+ goto exit_write_packet;
+ }
+
+ /* Use first an u8 pointer for obtaining the correct buffer address */
+ ptr = req->req.buf + req->req.actual;
+ buf = (u16 *)ptr;
+ prefetch(buf);
+
+ /* Only send the maximal allowed number of bytes */
+ length = min(remaining, max);
+ req->req.actual += length;
+
+ /* First write the number of bytes to transfer, and then fill the FIFO */
+ usb_write(udc, length, S3C24XX_UDC_BWCR_REG, epnr);
+ for (count = 0; count < length; count += 2)
+ writel(*buf++, udc->base + fifo);
+
+ /* Sanity check before writting into the FIFO */
+#if defined(DEBUG_S3C2443_UDC_QUEUE)
+ {
+ ulong esr;
+
+ esr = usb_read(udc, S3C24XX_UDC_ESR_REG, ep_index(ep));
+ printk(KERN_DEBUG
+ "%p: len=%i, act=%i, ep=%02x, bwcr=0x%04x, esr=0x%04lx\n",
+ req, req->req.length, req->req.actual, epnr, length, esr);
+ }
+#endif
+
+ /* Return the number of remaining bytes of the passed request */
+ exit_write_packet:
+ return length;
+}
+
+/*
+ * Check the current state of the VBUS pin. If no VBUS pin was passed through the
+ * platform data, then assume the bus is always ON.
+ * (Luis Galdos)
+ */
+static inline int s3c2443_udc_vbus_state(struct s3c24xx_udc *udc)
+{
+ int retval;
+ struct s3c2410_udc_mach_info *info;
+
+ info = udc->mach_info;
+ retval = 1;
+ if (info && info->vbus_pin) {
+ unsigned long iocfg;
+
+ /* @XXX: Do we really need to change to INPUT first? */
+ iocfg = s3c2410_gpio_getcfg(info->vbus_pin);
+ s3c2410_gpio_cfgpin(info->vbus_pin, S3C2410_GPIO_INPUT);
+ retval = s3c2410_gpio_getpin(info->vbus_pin);
+ s3c2410_gpio_cfgpin(info->vbus_pin, iocfg);
+
+ if (info->vbus_pin_inverted)
+ retval = !retval;
+ }
+
+ return retval;
+}
+
+/*
+ * Disable the controller by resetting the PHY for informing the USB-host
+ * that the device was disconnected
+ * (Luis Galdos)
+ */
+static void s3c24xx_udc_disable(struct s3c24xx_udc *udc)
+{
+ ulong regval;
+
+ pk_dbg("UDC disable called\n");
+
+ /* Disable the EP interrupts */
+ writel(0, udc->base + S3C24XX_UDC_EIER_REG);
+ writel(0xff, udc->base + S3C24XX_UDC_EIR_REG);
+
+ /* Clear all the status bits of the EP0 and flush it */
+ writel(S3C24XX_UDC_EP0SR_RSR | S3C24XX_UDC_EP0SR_TST |
+ S3C24XX_UDC_EP0SR_SHT | S3C24XX_UDC_EP0SR_LWO,
+ udc->base + S3C24XX_UDC_EP0SR_REG);
+ writel(0, udc->base + S3C24XX_UDC_EP0CR_REG);
+
+ /* Unset the function address */
+ s3c24xx_udc_set_address(udc, 0);
+
+ udc->ep0state = WAIT_FOR_SETUP;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->usb_address = 0;
+
+ /* Clear all the status bits from the system status register */
+ regval = S3C24XX_UDC_INT_RESET | S3C24XX_UDC_INT_SUSPEND |
+ S3C24XX_UDC_INT_RESUME | S3C24XX_UDC_INT_SDE |
+ S3C24XX_UDC_SSR_TBM | S3C24XX_UDC_INT_VBUSON |
+ S3C24XX_UDC_SSR_VBUSOFF;
+ writel(regval, udc->base + S3C24XX_UDC_SSR_REG);
+
+ /* Reset the USB-function and the PHY */
+ writel(S3C2443_URSTCON_PHY | S3C2443_URSTCON_FUNC, S3C2443_URSTCON);
+
+ /* PHY power disable */
+ regval = readl(S3C2443_PWRCFG);
+ regval &= ~S3C2443_PWRCFG_USBPHY_ON;
+ writel(regval, S3C2443_PWRCFG);
+}
+
+/*
+ * Function for sending request data to the FIFO
+ * This function uses the EP-lock for avoiding the wrong queue order of the packets
+ * that are incoming from the Gadget-driver
+ * (Luis Galdos)
+ */
+static void s3c24xx_udc_epin_tasklet_func(unsigned long data)
+{
+ struct s3c_ep *ep;
+ struct s3c_request *req;
+ int retval;
+ ulong esr;
+ struct s3c24xx_udc *udc;
+
+ ep = (struct s3c_ep *)data;
+ if (!ep) {
+ pk_err("Invalid EP pointer. Aborting %s\n", __func__);
+ return;
+ }
+
+ spin_lock(&ep->lock);
+
+ udc = ep->dev;
+ esr = usb_read(udc, S3C24XX_UDC_ESR_REG, ep_index(ep));
+
+ /*
+ * Paranoic sanity check: If the FIFO has still a packet, then abort this
+ * tasklet and wait for the call from the interrupt handler (TPS)
+ */
+ if (S3C24XX_UDC_ESR_PSIFNR(esr) == 2) {
+ pk_dbg("The FIFO seems to have still a packet\n");
+ goto exit_unlock;
+ }
+
+ /* Check if there is a pending request for us */
+ if (list_empty(&ep->queue))
+ goto exit_unlock;
+
+ /* Get the next request from the queue of the endpoint */
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+ if (!req) {
+ pk_err("EP%i: NULL request pointer.\n", ep_index(ep));
+ goto exit_unlock;
+ }
+
+#if defined(DEBUG_S3C2443_UDC_QUEUE)
+ {
+ u8 ch1, ch2;
+ int len, act;
+ u8 *ptr = (u8 *)req->req.buf;
+ len = req->req.length;
+ act = req->req.actual;
+ ch1 = *ptr;
+ ch2 = *(ptr + len - 1);
+ printk(KERN_DEBUG "%p: act=%i, ep=%02x, 0x%02x ... 0x%02x\n",
+ req, act, ep_index(ep), ch1, ch2);
+ }
+#endif
+ retval = s3c24xx_udc_write_fifo(ep, req);
+
+ exit_unlock:
+ spin_unlock(&ep->lock);
+}
+
+/*
+ * Restart the UDC and the corresponding resources (tasklet, queues, etc.)
+ * (Luis Galdos)
+ */
+static void s3c24xx_udc_reinit(struct s3c24xx_udc *udc)
+{
+ u32 i;
+
+ /* device/ep0 records init */
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+ INIT_LIST_HEAD(&udc->gadget.ep0->ep_list);
+ udc->ep0state = WAIT_FOR_SETUP;
+
+ /* basic endpoint records init */
+ for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
+ struct s3c_ep *ep = &udc->ep[i];
+
+ if (i != 0)
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+
+ ep->desc = 0;
+ ep->stopped = 0;
+ INIT_LIST_HEAD(&ep->queue);
+ ep->pio_irqs = 0;
+ }
+
+ /* the rest was statically initialized, and is read-only */
+}
+
+#define BYTES2MAXP(x) (x / 8)
+#define MAXP2BYTES(x) (x * 8)
+
+/*
+ * Until it's enabled, this UDC should be completely invisible
+ * to any USB host.
+ */
+static int s3c24xx_udc_enable(struct s3c24xx_udc *udc)
+{
+ unsigned long regval;
+
+ pk_dbg("UDC enable called\n");
+
+ /* First disable the HOST functionality! */
+ regval = __raw_readl(S3C2443_UCLKCON);
+ regval &= ~S3C2443_UCLKCON_HOST_ENABLE;
+ regval |= S3C2443_UCLKCON_THOST_DISABLE;
+ __raw_writel(regval, S3C2443_UCLKCON);
+
+ /* if reset by sleep wakeup, control the retention I/O cell */
+ if (__raw_readl(S3C2443_RSTSTAT) & 0x8)
+ __raw_writel(__raw_readl(S3C2443_RSTCON)|(1<<16), S3C2443_RSTCON);
+
+ /* PHY power enable */
+ regval = __raw_readl(S3C2443_PWRCFG);
+ regval |= S3C2443_PWRCFG_USBPHY_ON;
+ __raw_writel(regval, S3C2443_PWRCFG);
+
+ /*
+ * USB device 2.0 must reset like bellow,
+ * 1st phy reset and after at least 10us, func_reset & host reset
+ * phy reset can reset bellow registers.
+ */
+ /* PHY 2.0 S/W reset */
+ regval = S3C2443_URSTCON_PHY;
+ __raw_writel(regval, S3C2443_URSTCON);
+ udelay(20);
+ __raw_writel(0x00, S3C2443_URSTCON);
+
+ /* Function reset, but DONT TOUCH THE HOST! */
+ regval = S3C2443_URSTCON_FUNC;
+ __raw_writel(regval, S3C2443_URSTCON);
+ __raw_writel(0x00, S3C2443_URSTCON);
+
+ /* 48Mhz, Oscillator, External X-tal, device */
+ regval = S3C2443_PHYCTRL_EXTCLK_OSCI;
+ __raw_writel(regval, S3C2443_PHYCTRL);
+
+ /*
+ * D+ pull up disable(VBUS detect), USB2.0 Function clock Enable,
+ * USB1.1 HOST disable, USB2.0 PHY test enable
+ */
+ regval = __raw_readl(S3C2443_UCLKCON);
+ regval |= S3C2443_UCLKCON_FUNC_ENABLE;
+ __raw_writel(regval, S3C2443_UCLKCON);
+
+ reconfig_usbd(udc);
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /*
+ * So, now enable the pull up, USB2.0 Function clock Enable and
+ * USB2.0 PHY test enable
+ */
+ regval = __raw_readl(S3C2443_UCLKCON);
+ regval |= S3C2443_UCLKCON_VBUS_PULLUP | S3C2443_UCLKCON_FUNC_ENABLE |
+ S3C2443_UCLKCON_TFUNC_ENABLE | S3C2443_UCLKCON_THOST_DISABLE;
+ __raw_writel(regval, S3C2443_UCLKCON);
+ return 0;
+}
+
+/*
+ * Function called from the Gadget-drivers for registering a new profile.
+ */
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct s3c24xx_udc *udc = the_controller;
+ int retval;
+
+ if (!driver)
+ return -EINVAL;
+
+ pk_dbg("Starting to register '%s'\n", driver->driver.name);
+
+ if (driver->speed != USB_SPEED_FULL && driver->speed != USB_SPEED_HIGH) {
+ pk_err("Only Full and High speed supported.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The 'unbind' function is not required when the Gadget driver is compiled
+ * as built-in (Luis Galdos)
+ */
+ if (!driver->bind || !driver->disconnect || !driver->setup) {
+ pk_err("Missing function: Bind %p | Disconnect %p | Setup %p\n",
+ driver->bind, driver->disconnect, driver->setup);
+ return -EINVAL;
+ }
+
+ if (!udc) {
+ pk_err("No UDC-controller probed? Aborting.\n");
+ return -ENODEV;
+ }
+
+ if (udc->driver) {
+ pk_err("UDC already in use by '%s'\n", udc->driver->driver.name);
+ return -EBUSY;
+ }
+
+ /* first hook up the driver ... */
+ udc->driver = driver;
+ udc->gadget.dev.driver = &driver->driver;
+
+ retval = device_add(&udc->gadget.dev);
+ if (retval) {
+ pk_err("Couldn't add the new Gadget device (%i)\n", retval);
+ goto err_exit;
+ }
+
+ retval = driver->bind(&udc->gadget);
+ if (retval) {
+ pk_err("%s: bind to driver %s --> error %d\n", udc->gadget.name,
+ driver->driver.name, retval);
+ goto err_del_device;
+ }
+
+ enable_irq(IRQ_USBD);
+
+ /*
+ * If a host was already detected, then only call the UDC enable function,
+ * otherwise check over the configured GPIO if a host is connected.
+ */
+ if (udc->vbus)
+ s3c24xx_udc_enable(udc);
+ else {
+ struct s3c2410_udc_mach_info *info;
+ unsigned long state, iocfg;
+
+ info = udc->mach_info;
+
+ iocfg = s3c2410_gpio_getcfg(info->vbus_pin);
+ s3c2410_gpio_cfgpin(info->vbus_pin, S3C2410_GPIO_INPUT);
+ state = s3c2410_gpio_getpin(info->vbus_pin);
+ s3c2410_gpio_cfgpin(info->vbus_pin, iocfg);
+
+ if (info->vbus_pin_inverted)
+ state = !state;
+
+ if (state)
+ s3c24xx_udc_enable(udc);
+ }
+
+ pk_dbg("Gadget '%s' registered\n", driver->driver.name);
+ return 0;
+
+ err_del_device:
+ device_del(&udc->gadget.dev);
+
+ err_exit:
+ udc->driver = NULL;
+ udc->gadget.dev.driver = NULL;
+ return retval;
+}
+
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/*
+ * Unregister entry point for the peripheral controller driver.
+ */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct s3c24xx_udc *udc = the_controller;
+ unsigned long flags;
+
+ if (!udc)
+ return -ENODEV;
+
+ if (!driver || driver != udc->driver || !driver->unbind)
+ return -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ udc->driver = NULL;
+ stop_activity(udc, driver);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ driver->unbind(&udc->gadget);
+
+ device_del(&udc->gadget.dev);
+
+ disable_irq(IRQ_USBD);
+
+ pk_dbg("Unregistered gadget driver '%s'\n", driver->driver.name);
+
+ /* Disable the pull-up for informing the host about the removed driver */
+ s3c24xx_udc_disable(udc);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*
+ * Write request to FIFO (max write == maxp size)
+ * Return: 0 = still running, 1 = completed, negative = errno
+ */
+static int s3c24xx_udc_write_fifo(struct s3c_ep *ep, struct s3c_request *req)
+{
+ int max, count;
+ int is_last, is_short;
+
+ count = s3c24xx_udc_write_packet2(ep, req);
+ max = le16_to_cpu(ep->desc->wMaxPacketSize);
+
+ is_short = (count != max) ? (1) : (0);
+
+ /* If the packet is short, we dont need to send an additional ZLP */
+ if (is_short) {
+ pk_dbg("EP%i: Short packet\n", ep_index(ep));
+ is_last = 1;
+ } else {
+ if (req->req.length != req->req.actual || req->req.zero)
+ is_last = 0;
+ else {
+ pk_dbg("EP%i: Clear ZLP\n", ep_index(ep));
+ is_last = 1;
+ }
+ }
+
+ pk_dbg("TX EP%i: C %i | L %i - A %i | %c %c %c\n",
+ ep_index(ep), count, req->req.length, req->req.actual,
+ is_last ? 'L':' ', is_short ? 'S':' ', req->req.zero ? 'Z':' ');
+
+ /* If this was the last packet, then call the done callback */
+ if (is_last) {
+
+#if defined(DEBUG_S3C2443_UDC_QUEUE)
+ int len, act;
+ len = req->req.length;
+ act = req->req.actual;
+ printk(KERN_DEBUG "%p: len=%i, act=%i, ep=%02x [D]\n",
+ req, len, act, ep_index(ep));
+#endif
+
+ done(ep, req, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * Read to request from FIFO (max read == bytes in fifo)
+ * Return: 0 = still running, 1 = completed, negative = errno
+ */
+static int s3c24xx_udc_read_fifo(struct s3c_ep *ep, struct s3c_request *req)
+{
+ u32 csr;
+ u16 *buf;
+ unsigned bufferspace, count, count_bytes, is_short = 0, is_done = 0;
+ u32 fifo = ep->fifo;
+ struct s3c24xx_udc *udc;
+
+ udc = ep->dev;
+ csr = usb_read(udc, S3C24XX_UDC_ESR_REG, ep_index(ep));
+
+ /*
+ * If the FIFO is empty then return zero, so that a caller, like the queue-
+ * function, doesn't fail. Returning zero means that the request is not done
+ * and it can be added to the internal EP-request queue
+ * (Luis Galdos)
+ */
+ if (!(csr & S3C24XX_UDC_ESR_RPS)) {
+ pk_dbg("EP%i: No packet to read.\n", ep_index(ep));
+ return 0;
+ }
+
+ buf = req->req.buf + req->req.actual;
+ prefetchw(buf);
+
+ /* Calculate the current buffer space */
+ bufferspace = req->req.length - req->req.actual;
+
+ /* Read all bytes from this packet */
+ count = usb_read(udc, S3C24XX_UDC_BRCR_REG, ep_index(ep));
+ if (csr & S3C24XX_UDC_ESR_LWO)
+ count_bytes = count * 2 - 1;
+ else
+ count_bytes = count * 2;
+
+ /* Update the actual variable of the request */
+ req->req.actual += min(count_bytes, bufferspace);
+
+ is_short = (count_bytes < ep->ep.maxpacket);
+ is_done = (req->req.actual == req->req.length) ? 1 : 0;
+
+ /*
+ * G : Got
+ * A : Actual
+ * T : Total to get
+ */
+ if (is_short || is_done) {
+ pk_dbg_out("EP%u: G %d | A %d | T %d [%c%c]\n",
+ ep_index(ep), count_bytes, req->req.actual, req->req.length,
+ is_short ? 'S' : ' ', is_done ? 'D' : ' ');
+ }
+
+ while (likely(count-- != 0)) {
+ u16 byte = (u16)readl(udc->base + fifo);
+
+ /*
+ * If there is no more space in the request-buffer, then continue
+ * reading from the FIFO and return with the done value
+ * (Luis Galdos)
+ */
+ if (unlikely(bufferspace == 0)) {
+ req->req.status = -EOVERFLOW;
+ is_short = 1;
+ } else {
+ *buf++ = byte;
+ bufferspace--;
+ }
+ }
+
+ /*
+ * If the complete FIFO-data passed into the request-buffer, then
+ * return one, otherwise skip the return
+ * (Luis Galdos)
+ */
+ if (is_short || is_done) {
+ done(ep, req, 0);
+ return 1;
+ }
+
+ /* finished that packet. the next one may be waiting... */
+ return 0;
+}
+
+/*
+ * Retire a request from the internal EP-queue and call the complete
+ * function of the Gadget-request
+ * (Luis Galdos)
+ */
+static void done(struct s3c_ep *ep, struct s3c_request *req, int status)
+{
+ unsigned int stopped = ep->stopped;
+
+ list_del_init(&req->queue);
+
+ /*
+ * If the queue is empty and the EP has the OUT direction, then disable
+ * the receive operation, otherwise we will lost some packets from
+ * the host.
+ */
+ if (!ep_is_in(ep) && list_empty(&ep->queue)) {
+ ulong ecr;
+ struct s3c24xx_udc *udc;
+
+ udc = ep->dev;
+ ecr = usb_read(udc, S3C24XX_UDC_ECR_REG, ep_index(ep));
+ ecr |= S3C24XX_UDC_ECR_OUTPKTHLD;
+ usb_write(udc, ecr, S3C24XX_UDC_ECR_REG, ep_index(ep));
+ }
+
+ if (likely(req->req.status == -EINPROGRESS))
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ if (status && status != -ESHUTDOWN) {
+ pk_dbg("EP%i: done req %p | stat %d | actual %u | length %u\n",
+ ep_index(ep),
+ &req->req, status, req->req.actual, req->req.length);
+ }
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+
+ /*
+ * We must unlock the queue of the EP at this place, then the Gadget-driver
+ * probably will try to enqueue a new request by calling our queue-function.
+ * (Luis Galdos)
+ */
+/* spin_unlock(&ep->lock); */
+/* spin_unlock(&ep->dev->lock); */
+ req->req.complete(&ep->ep, &req->req);
+/* spin_lock(&ep->dev->lock); */
+/* spin_lock(&ep->lock); */
+
+ ep->stopped = stopped;
+}
+
+/* Nuke/dequeue all the requested transfers */
+void nuke(struct s3c_ep *ep, int status)
+{
+ struct s3c_request *req;
+
+ pk_dbg("EP%i: Nuke function called\n", ep_index(ep));
+
+ /* called with irqs blocked */
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+ done(ep, req, status);
+ }
+}
+
+/*
+ * This function handles the IN-operations of the endpoints different than zero
+ */
+static void s3c24xx_udc_in_epn(struct s3c24xx_udc *udc, u32 epnr)
+{
+ ulong esr, handled;
+ struct s3c_ep *ep = &udc->ep[epnr];
+
+ handled = 0;
+
+ spin_lock(&ep->lock);
+
+ esr = usb_read(udc, S3C24XX_UDC_ESR_REG, epnr);
+
+ /* ACK the function stall condition */
+ if (esr & S3C24XX_UDC_ESR_FSC) {
+ pk_dbg("EP%i: Function stall\n", epnr);
+ usb_set(udc, S3C24XX_UDC_ESR_FSC, S3C24XX_UDC_ESR_REG, epnr);
+ handled = 1;
+ }
+
+ /* The flush operation generates an interrupt too */
+ if (esr & S3C24XX_UDC_ESR_FFS) {
+ pk_dbg("EP%i: FIFO flush detected\n", epnr);
+ usb_set(udc, S3C24XX_UDC_ESR_FFS, S3C24XX_UDC_ESR_REG, epnr);
+ handled = 1;
+ }
+
+ /* Underflow check */
+ if (esr & S3C24XX_UDC_ESR_FUDR) {
+ pk_dbg("EP%i: Underflow detected\n", epnr);
+ usb_set(udc, S3C24XX_UDC_ESR_FUDR, S3C24XX_UDC_ESR_REG, epnr);
+ handled = 1;
+ }
+
+ /* Overflow check */
+ if (esr & S3C24XX_UDC_ESR_FOVF) {
+ pk_dbg("EP%i: Overflow detected\n", epnr);
+ usb_set(udc, S3C24XX_UDC_ESR_FOVF, S3C24XX_UDC_ESR_REG, epnr);
+ handled = 1;
+ }
+
+ /* By successed transfer of a IN-packet then only schedule the tasklet */
+ if (esr & S3C24XX_UDC_ESR_TPS) {
+ usb_set(udc, S3C24XX_UDC_ESR_TPS, S3C24XX_UDC_ESR_REG, epnr);
+ tasklet_hi_schedule(&ep->in_tasklet);
+ handled = 1;
+ }
+
+ spin_unlock(&ep->lock);
+
+ if (!handled)
+ pk_info("EP%i: Unhandled IRQ (ESR 0x%04lx)\n", epnr, esr);
+}
+
+/*
+ * This function is used for reading OUT-frames from the EP0. We can't use the same
+ * function for the SETUP-requests, then here we must pass the data to the
+ * higher Gadget-driver.
+ */
+static void s3c2443_udc_ep0_read(struct s3c24xx_udc *udc)
+{
+ ulong ep0sr;
+ int bytes, count, bufferspace;
+ struct s3c_ep *ep;
+ struct s3c_request *req;
+ u16 *buf;
+
+ ep = &udc->ep[0];
+
+ spin_lock(&ep->lock);
+
+ /*
+ * @FIXME: Remove this delay. At this moment we need it for having a
+ * working RNDIS-support when connected to a WinXP host machine.
+ * (Luis Galdos)
+ */
+ if (udc->ep0state == DATA_STATE_RECV)
+ udelay(100);
+
+ /* If there is nothing to read only return at this point */
+ ep0sr = readl(udc->base + S3C24XX_UDC_EP0SR_REG);
+ if (!(ep0sr & S3C24XX_UDC_EP0SR_RSR))
+ goto exit_unlock;
+
+ /* Check if we are waiting for a setup frame */
+ if (udc->ep0state == WAIT_FOR_SETUP) {
+ s3c24xx_ep0_setup(udc);
+ goto exit_unlock;
+ }
+
+ pk_dbg("Current state of EP0 is %i\n", udc->ep0state);
+
+ /* Now get the number of bytes to read from the FIFO */
+ count = usb_read(udc, S3C24XX_UDC_BRCR_REG, ep_index(ep));
+ if (ep0sr & S3C24XX_UDC_EP0SR_LWO)
+ bytes = count * 2 - 1;
+ else
+ bytes = count * 2;
+
+ /* Check if we have a request for this data */
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+ if (!req) {
+ pk_err("Going to flush a EP0 frame\n");
+ goto exit_ack;
+ }
+
+ buf = req->req.buf + req->req.actual;
+ prefetchw(buf);
+ bufferspace = req->req.length - req->req.actual;
+ req->req.actual += min(bytes, bufferspace);
+
+ pk_dbg("EP0 READ: %i bytes | space %i | req.len %i | reg.act %i\n",
+ bytes, bufferspace, req->req.length, req->req.actual);
+ while (likely(count-- != 0)) {
+ u16 byte = (u16)readl(udc->base + ep->fifo);
+ *buf++ = byte;
+ }
+
+ /* If we are done with this request then call the corresponding function */
+ if (req->req.length == req->req.actual) {
+ udc->ep0state = WAIT_FOR_SETUP;
+ done(ep, req, 0);
+ }
+
+ exit_ack:
+ writel(S3C24XX_UDC_EP0_RX_SUCCESS, udc->base + S3C24XX_UDC_EP0SR_REG);
+
+ exit_unlock:
+ spin_unlock(&ep->lock);
+}
+
+
+/*
+ * The below function is called when data was received with an OUT-transaction
+ */
+static void s3c24xx_udc_out_epn(struct s3c24xx_udc *udc, u32 ep_idx)
+{
+ struct s3c_ep *ep;
+ struct s3c_request *req;
+ ulong esr, epnr, handled;
+
+ ep = &udc->ep[ep_idx];
+ epnr = ep_index(ep);
+ if (epnr != ep_idx) {
+ pk_err("Invalid EP structure (%lu) or index (%u) passed\n",
+ epnr, ep_idx);
+ return;
+ }
+
+ /* Read the status register of the EP */
+ handled = 0;
+ esr = usb_read(udc, S3C24XX_UDC_ESR_REG, epnr);
+ pk_dbg("EP%lu: Status reg 0x%08lx\n", epnr, esr);
+
+ if (unlikely(!(ep->desc))) {
+ pk_err("No descriptor for EP%lu\n", epnr);
+ return;
+ }
+
+ if (esr & S3C24XX_UDC_ESR_FSC) {
+ pk_dbg("EP%lu stall sent\n", epnr);
+ usb_set(udc, S3C24XX_UDC_ESR_FSC, S3C24XX_UDC_ESR_REG, epnr);
+ handled = 1;
+ }
+
+ if (esr & S3C24XX_UDC_ESR_FFS) {
+ pk_dbg("EP%lu FIFO flush\n", epnr);
+ usb_set(udc, S3C24XX_UDC_ESR_FFS, S3C24XX_UDC_ESR_REG, epnr);
+ handled = 1;
+ }
+
+ /* RX means we have received some data over an OUT-transaction */
+ if (esr & S3C24XX_UDC_ESR_RPS) {
+ int retval;
+ ulong packets;
+
+ /*
+ * The read-fifo function returns zero if there is
+ * additional data in the FIFO. In that case read once
+ * again from the FIFO
+ */
+ packets = (esr & S3C24XX_UDC_ESR_PSIF_TWO) ? 2 : 1;
+ while (packets--) {
+
+ req = (list_empty(&ep->queue)) ? NULL :
+ list_entry(ep->queue.next, struct s3c_request, queue);
+
+ /*
+ * If we dont have a request for the received data, then only
+ * break the loop and return without flushing the FIFO.
+ */
+ if (unlikely(!req)) {
+ ulong count_bytes, count;
+ ulong csr;
+
+ /* Read all bytes from this packet */
+ csr = usb_read(udc, S3C24XX_UDC_ESR_REG, ep_index(ep));
+ count = usb_read(udc, S3C24XX_UDC_BRCR_REG, ep_idx);
+ if (csr & S3C24XX_UDC_ESR_LWO)
+ count_bytes = count * 2 - 1;
+ else
+ count_bytes = count * 2;
+
+ pk_dbg_out("EP%lu: No OUT req. queued (len %lu)\n",
+ epnr, count_bytes);
+ break;
+ }
+
+ retval = s3c24xx_udc_read_fifo(ep, req);
+ if (retval < 0) {
+ pk_err("EP%lu: FIFO read (%i)\n", epnr, retval);
+ break;
+ }
+ }
+
+ handled = 1;
+ }
+
+ /* Handlings for the overflow and underrun */
+ if (esr & S3C24XX_UDC_ESR_FOVF) {
+ pk_err("EP%lu FIFO overflow\n", epnr);
+ usb_set(udc, S3C24XX_UDC_ESR_FOVF, S3C24XX_UDC_ESR_REG, epnr);
+ }
+
+ if (esr & S3C24XX_UDC_ESR_FUDR) {
+ pk_err("EP%lu FIFO underrun\n", epnr);
+ usb_set(udc, S3C24XX_UDC_ESR_FUDR, S3C24XX_UDC_ESR_REG, epnr);
+ }
+
+ /*
+ * Check if the function was handled, otherwise only uses a debug message
+ * for informing about the error
+ */
+ if (!handled) {
+ pk_dbg("EP%lu: Unhandled OUT | ESR 0x%08lx.\n", epnr, esr);
+ }
+}
+
+static void stop_activity(struct s3c24xx_udc *udc,
+ struct usb_gadget_driver *driver)
+{
+ int i;
+
+ /* don't disconnect drivers more than once */
+ if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
+ struct s3c_ep *ep = &udc->ep[i];
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+
+ /* report disconnect; the driver is already quiesced */
+ if (driver) {
+ spin_unlock(&udc->lock);
+ driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+
+ /* re-init driver-visible data structures */
+ s3c24xx_udc_reinit(udc);
+}
+
+static void reconfig_usbd(struct s3c24xx_udc *udc)
+{
+ struct s3c_ep *ep;
+ int cnt;
+ unsigned long edr;
+
+ /*
+ * Configure the endpoints depending on the defined structure which
+ * will be used by the gadget-drivers (see below)
+ */
+ edr = 0;
+ for (cnt = 1; cnt < S3C_MAX_ENDPOINTS; cnt++) {
+ ep = &the_controller->ep[cnt];
+ if (ep->bEndpointAddress & USB_DIR_IN)
+ edr |= (1 << cnt);
+ }
+ writel(edr, udc->base + S3C24XX_UDC_EDR_REG);
+
+ /* Reset the endpoint configuration registers */
+ for (cnt = 1; cnt < S3C_MAX_ENDPOINTS; cnt++) {
+ ep = &the_controller->ep[cnt];
+ usb_write(udc, 0, S3C24XX_UDC_ECR_REG, ep_index(ep));
+ }
+
+ /* Only enable the EP0 */
+ writel(0x1, udc->base + S3C24XX_UDC_EIER_REG);
+
+ writel(0x0, udc->base + S3C24XX_UDC_TR_REG);
+
+ /* error interrupt enable, 16bit bus, Little format,
+ * suspend&reset enable
+ */
+ writel(S3C24XX_UDC_EIE_EN |
+ S3C24XX_UDC_RRD_EN |
+ S3C24XX_UDC_SUS_EN |
+ S3C24XX_UDC_RST_EN,
+ udc->base + S3C24XX_UDC_SCR_REG);
+
+ writel(0x0000, udc->base + S3C24XX_UDC_EP0CR_REG);
+
+ writel(0, udc->base + S3C24XX_UDC_IR_REG);
+}
+
+static void s3c24xx_set_max_pktsize(struct s3c24xx_udc *udc, enum usb_device_speed speed)
+{
+ if (speed == USB_SPEED_HIGH) {
+ ep0_fifo_size = 64;
+ ep_fifo_size = 512;
+ ep_fifo_size2 = 1024;
+ udc->gadget.speed = USB_SPEED_HIGH;
+ } else {
+ ep0_fifo_size = 64;
+ ep_fifo_size = 64;
+ ep_fifo_size2 = 64;
+ udc->gadget.speed = USB_SPEED_FULL;
+ }
+
+ udc->ep[0].ep.maxpacket = ep0_fifo_size;
+ udc->ep[1].ep.maxpacket = ep_fifo_size;
+ udc->ep[2].ep.maxpacket = ep_fifo_size;
+ udc->ep[3].ep.maxpacket = ep_fifo_size;
+ udc->ep[4].ep.maxpacket = ep_fifo_size;
+ udc->ep[5].ep.maxpacket = ep_fifo_size;
+ udc->ep[6].ep.maxpacket = ep_fifo_size;
+ udc->ep[7].ep.maxpacket = ep_fifo_size;
+ udc->ep[8].ep.maxpacket = ep_fifo_size;
+
+ usb_write(udc, ep0_fifo_size, S3C24XX_UDC_MAXP_REG, 0);
+ usb_write(udc, ep_fifo_size, S3C24XX_UDC_MAXP_REG, 1);
+ usb_write(udc, ep_fifo_size, S3C24XX_UDC_MAXP_REG, 2);
+ usb_write(udc, ep_fifo_size, S3C24XX_UDC_MAXP_REG, 3);
+ usb_write(udc, ep_fifo_size, S3C24XX_UDC_MAXP_REG, 4);
+ usb_write(udc, ep_fifo_size, S3C24XX_UDC_MAXP_REG, 5);
+ usb_write(udc, ep_fifo_size, S3C24XX_UDC_MAXP_REG, 6);
+ usb_write(udc, ep_fifo_size, S3C24XX_UDC_MAXP_REG, 7);
+ usb_write(udc, ep_fifo_size, S3C24XX_UDC_MAXP_REG, 8);
+}
+
+static int s3c24xx_udc_set_pullup(struct s3c24xx_udc *udc, int is_on)
+{
+ struct s3c2410_udc_mach_info *info;
+ enum s3c2410_udc_cmd_e cmd;
+
+ info = udc->mach_info;
+
+ if (is_on)
+ cmd = S3C2410_UDC_P_ENABLE;
+ else
+ cmd = S3C2410_UDC_P_DISABLE;
+
+ /* Call the platform dependet function if it's available */
+ if (info && info->udc_command) {
+ info->udc_command(cmd);
+ } else {
+ if (is_on) {
+ /*
+ * Only enable the UDC if a Gadget-driver was already registered,
+ * otherwise by the registration the UDC-enable function should
+ * be called.
+ * (Luis Galdos)
+ */
+ if (udc->driver)
+ s3c24xx_udc_enable(udc);
+ } else {
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+ if (udc->driver && udc->driver->disconnect)
+ udc->driver->disconnect(&udc->gadget);
+
+ }
+ s3c24xx_udc_disable(udc);
+ }
+ }
+
+ return 0;
+}
+
+static int s3c24xx_udc_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct s3c24xx_udc *udc = gadget_to_udc(gadget);
+
+ udc->vbus = (is_active != 0);
+ s3c24xx_udc_set_pullup(udc, is_active);
+ return 0;
+}
+
+/*
+ * This function is called by detection of the bus-power over the requested IRQ
+ */
+static irqreturn_t s3c24xx_udc_vbus_irq(int irq, void *_udc)
+{
+ struct s3c24xx_udc *udc = _udc;
+ unsigned int value;
+ struct s3c2410_udc_mach_info *info;
+ ulong cfg;
+
+ info = udc->mach_info;
+
+ /* Some cpus cannot read from an line configured to IRQ! */
+ cfg = s3c2410_gpio_getcfg(info->vbus_pin);
+ s3c2410_gpio_cfgpin(info->vbus_pin, S3C2410_GPIO_INPUT);
+ value = s3c2410_gpio_getpin(info->vbus_pin);
+ s3c2410_gpio_cfgpin(info->vbus_pin, cfg);
+
+ if (info->vbus_pin_inverted)
+ value = !value;
+
+ pk_dbg("Bus detect %s: vbus %i | value %i\n",
+ info->vbus_pin_inverted ? "inverted" : "", udc->vbus, value);
+
+ if (value != udc->vbus)
+ s3c24xx_udc_vbus_session(&udc->gadget, value);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Interrupt handler of the USB-function. The interrupts to detect are coming
+ * from the SMDK, but it doesn't consider the speed detection, which can lead
+ * to some failures.
+ *
+ * (Luis Galdos)
+ */
+#define S3C2443_UDC_INT_CHECK (0xff8f | S3C24XX_UDC_INT_HSP)
+static irqreturn_t s3c24xx_udc_irq(int irq, void *_udc)
+{
+ struct s3c24xx_udc *udc = _udc;
+ u32 intr_out, intr_in, intr_all;
+ u32 sys_stat, sys_stat_chk;
+ u32 stat, cnt;
+ unsigned long flags;
+ struct s3c_ep *ep;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ sys_stat = readl(udc->base + S3C24XX_UDC_SSR_REG);
+ stat = sys_stat;
+ intr_all = readl(udc->base + S3C24XX_UDC_EIR_REG);
+
+ /* We have only 3 usable eps now */
+ sys_stat_chk = sys_stat & S3C2443_UDC_INT_CHECK;
+
+ /* Only check for the correct endpoints (Luis Galdos) */
+ for (cnt = 0, intr_in = 0; cnt < S3C_MAX_ENDPOINTS; cnt++) {
+ ep = &udc->ep[cnt];
+
+ /* Skip the OUT-endpoints different than zero */
+ if (!(ep->bEndpointAddress & USB_DIR_IN) && cnt != 0)
+ continue;
+
+ if (s3c24xx_ep_enabled(udc, cnt))
+ intr_in |= (1 << cnt);
+ }
+ intr_in &= intr_all;
+
+ /* Check for the OUT-EPs that have generated an interrupt (Luis Galdos) */
+ for (cnt = 0, intr_out = 0; cnt < S3C_MAX_ENDPOINTS; cnt++) {
+ ep = &udc->ep[cnt];
+
+ /* Skip the IN-endpoints */
+ if (ep->bEndpointAddress & USB_DIR_IN)
+ continue;
+
+ if (s3c24xx_ep_enabled(udc, cnt))
+ intr_out |= (1 << cnt);
+ }
+ intr_out &= intr_all;
+
+ pk_dbg("UDC IRQ: stat 0x%08x (0x%08x) | in 0x%08x | out 0x%08x\n",
+ stat, sys_stat_chk, intr_in, intr_out);
+
+ if (!intr_out && !intr_in && !sys_stat_chk)
+ goto exit_ack;
+
+ if (sys_stat) {
+ if (sys_stat & S3C24XX_UDC_INT_VBUSON) {
+ pk_dbg("Vbus ON interrupt\n");
+ writel(S3C24XX_UDC_INT_VBUSON, udc->base + S3C24XX_UDC_SSR_REG);
+ udc->vbus = 1;
+ }
+
+ if (sys_stat & S3C24XX_UDC_INT_ERR) {
+ pk_dbg("ERROR interrupt\n");
+ writel(S3C24XX_UDC_INT_ERR,
+ udc->base + S3C24XX_UDC_SSR_REG);
+ }
+
+ if (sys_stat & S3C24XX_UDC_INT_SDE) {
+
+ writel(S3C24XX_UDC_INT_SDE,
+ udc->base + S3C24XX_UDC_SSR_REG);
+
+ if (sys_stat & S3C24XX_UDC_INT_HSP) {
+ pk_dbg("HIGH SPEED detection\n");
+ s3c24xx_set_max_pktsize(udc, USB_SPEED_HIGH);
+ } else {
+ pk_dbg("FULL SPEED detection\n");
+ s3c24xx_set_max_pktsize(udc, USB_SPEED_FULL);
+ }
+ }
+
+ if (sys_stat & S3C24XX_UDC_INT_HSP) {
+
+ writel(S3C24XX_UDC_INT_HSP, udc->base + S3C24XX_UDC_SSR_REG);
+ pk_dbg("High Speed interrupt\n");
+ }
+
+ /*
+ * @HW-BUG: If we get a suspend interrupt BUT are still connected to
+ * the bus, then the host-port number 2 registers a status change
+ * and tries to enable the port. This leads to a failure (see [1])
+ * since we are using the USB-PHY as device, and NOT as host-port.
+ *
+ * [1] hub 1-0:1.0: Cannot enable port 2. Maybe the USB cable is bad?
+ *
+ * The only option to avoid this error is disabling the USB-PHY so that
+ * a RESUME condition is generated and the host will ONLY try to
+ * enumerate an apparently connected USB-device
+ */
+ if (sys_stat & S3C24XX_UDC_INT_SUSPEND) {
+
+ pk_dbg("SUSPEND interrupt\n");
+
+ /* First ACK the interrupt after the bug fix! */
+ writel(S3C24XX_UDC_INT_SUSPEND,
+ udc->base + S3C24XX_UDC_SSR_REG);
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN
+ && udc->driver
+ && udc->driver->suspend) {
+ udc->driver->suspend(&udc->gadget);
+ }
+ }
+
+ /*
+ * By the resume interrupts the following error message is printed:
+ * hub 1-0:1.0: unable to enumerate USB device on port 2
+ */
+ if (sys_stat & S3C24XX_UDC_INT_RESUME) {
+ pk_dbg("RESUME interrupt\n");
+ writel(S3C24XX_UDC_INT_RESUME,
+ udc->base + S3C24XX_UDC_SSR_REG);
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN
+ && udc->driver
+ && udc->driver->resume) {
+ udc->driver->resume(&udc->gadget);
+ }
+ }
+
+ if (sys_stat & S3C24XX_UDC_INT_RESET) {
+ pk_dbg("RESET interrupt\n");
+ writel(S3C24XX_UDC_INT_RESET,
+ udc->base + S3C24XX_UDC_SSR_REG);
+ reconfig_usbd(udc);
+ udc->ep0state = WAIT_FOR_SETUP;
+ }
+
+ if (sys_stat & (S3C24XX_UDC_SSR_TBM | S3C24XX_UDC_SSR_EOERR |
+ S3C24XX_UDC_SSR_DCERR))
+ pk_err("Unexpected sys failure: 0x%08x\n", sys_stat);
+
+ }
+
+ if (intr_in) {
+ unsigned long cnt, epm;
+
+ /* FIXME: Workaround for preventing FIFO under/overrun */
+ udelay(300);
+
+ if (intr_in & S3C24XX_UDC_INT_EP0) {
+ ulong ep0sr;
+
+ /* First handle the arrived data, and then clear the IRQ */
+ s3c24xx_handle_ep0(udc);
+ writel(S3C24XX_UDC_INT_EP0, udc->base + S3C24XX_UDC_EIR_REG);
+
+ /*
+ * By long setup-handlings it's possible to have a TST at
+ * this point.
+ */
+ ep0sr = readl(udc->base + S3C24XX_UDC_EP0SR_REG);
+ if (ep0sr & S3C24XX_UDC_EP0SR_TST)
+ writel(S3C24XX_UDC_EP0SR_TST,
+ udc->base + S3C24XX_UDC_EP0SR_REG);
+ }
+
+
+ /* First get the EP-number that generated the interrupt */
+ for (cnt = 1; cnt < S3C_MAX_ENDPOINTS; cnt++) {
+ epm = (1 << cnt);
+ if (intr_in & epm) {
+ writel(epm, udc->base + S3C24XX_UDC_EIR_REG);
+ s3c24xx_udc_in_epn(udc, cnt);
+ /* writel(epm, udc->base + S3C24XX_UDC_EIR_REG); */
+ }
+ }
+ }
+
+ /* Check for OUT-frames by the endpoints */
+ if (intr_out) {
+ unsigned long cnt, epm;
+
+ /* FIXME: Workaround for preventing FIFO under/overrun */
+ udelay(100);
+
+ /* And the EP0 can receive OUT-transfers too! */
+ if (intr_out & 0x1)
+ s3c2443_udc_ep0_read(udc);
+
+ for (cnt = 1; cnt < S3C_MAX_ENDPOINTS; cnt++) {
+ epm = (1 << cnt);
+ if (intr_out & epm) {
+ writel(epm, udc->base + S3C24XX_UDC_EIR_REG);
+ s3c24xx_udc_out_epn(udc, cnt);
+ }
+ }
+ }
+
+ exit_ack:
+ /* writel(stat, udc->base + S3C24XX_UDC_SSR_REG); */
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Enable one EP, but only if it's different than the EP zero
+ * This function is called from the gadget-drivers over the UDC-operation functions
+ */
+static int s3c24xx_udc_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct s3c_ep *ep;
+ struct s3c24xx_udc *udc;
+ unsigned long flags, epnr, regval;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep || !desc || ep->desc) {
+ pk_err("NULL pointer or bad EP or descriptor found.\n");
+ return -EINVAL;
+ } else if (_ep->name == ep0name) {
+ pk_err("Invalid EP name (%s) to enable.\n", _ep->name);
+ return -EINVAL;
+ } else if (desc->bDescriptorType != USB_DT_ENDPOINT) {
+ pk_err("Invalid descriptor type (USB_DT_ENDPOINT)\n");
+ return -EINVAL;
+ } else if (ep->bEndpointAddress != desc->bEndpointAddress) {
+ pk_err("Invalid EP address found (valid %x | invalid %x)\n",
+ ep->bEndpointAddress, desc->bEndpointAddress);
+ return -EINVAL;
+ } else if (ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) {
+ pk_err("Invalid EP size %u (max. %u)\n",
+ le16_to_cpu(desc->wMaxPacketSize), ep_maxpacket(ep));
+ return -EINVAL;
+ }
+
+ /* xfer types must match, except that interrupt ~= bulk */
+ if (ep->bmAttributes != desc->bmAttributes
+ && ep->bmAttributes != USB_ENDPOINT_XFER_BULK
+ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+ pk_err("Type mismatch by EP %s\n", _ep->name);
+ return -EINVAL;
+ }
+
+ /* hardware _could_ do smaller, but driver doesn't */
+ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+ && le16_to_cpu(desc->wMaxPacketSize) > ep_maxpacket(ep))
+ || !desc->wMaxPacketSize) {
+ pk_err("Bad %s maxpacket (desc %u | max %u)\n", _ep->name,
+ le16_to_cpu(desc->wMaxPacketSize), ep_maxpacket(ep));
+ return -ERANGE;
+ }
+
+ udc = ep->dev;
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
+ pk_err("Bogus device state\n");
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ ep->stopped = 0;
+ ep->desc = desc;
+ ep->pio_irqs = 0;
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* Enable the interrupt for this EP */
+ epnr = ep_index(ep);
+ regval = readl(udc->base + S3C24XX_UDC_EIER_REG);
+ regval |= (1 << epnr);
+ writel(regval, udc->base + S3C24XX_UDC_EIER_REG);
+
+ /* Enable the dual FIFO mode */
+ regval = usb_read(udc, S3C24XX_UDC_ECR_REG, epnr);
+ regval |= S3C24XX_UDC_ECR_DUEN;
+ usb_write(udc, regval, S3C24XX_UDC_ECR_REG, epnr);
+
+ /* Reset halt state */
+ s3c24xx_udc_set_halt(_ep, 0);
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+ pk_dbg("Enabled %s | Addr. 0x%02x\n", _ep->name, ep->bEndpointAddress);
+ return 0;
+}
+
+static int s3c24xx_udc_ep_disable(struct usb_ep *_ep)
+{
+ struct s3c_ep *ep;
+ unsigned long flags;
+ int epnr;
+ struct s3c24xx_udc *udc;
+ ulong regval;
+
+ if (!_ep) {
+ pk_err("Null pointer passed! Aborting.\n");
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!ep->desc) {
+ pk_dbg("%s has an empty descriptor\n", ep->ep.name);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ /* Disable the corresponding IRQ */
+ udc = ep->dev;
+ epnr = ep_index(ep);
+ regval = readl(udc->base + S3C24XX_UDC_EIER_REG);
+ regval &= ~(1 << epnr);
+ writel(regval, udc->base + S3C24XX_UDC_EIER_REG);
+
+ /* Nuke all pending requests */
+ nuke(ep, -ESHUTDOWN);
+
+ ep->desc = 0;
+ ep->stopped = 1;
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+static struct usb_request *s3c24xx_udc_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
+{
+ struct s3c_request *req;
+
+ req = kmalloc(sizeof *req, gfp_flags);
+ if (!req)
+ return NULL;
+
+ memset(req, 0, sizeof *req);
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void s3c24xx_udc_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+ struct s3c_request *req;
+
+ req = container_of(_req, struct s3c_request, req);
+ WARN_ON(!list_empty(&req->queue));
+ kfree(req);
+}
+
+/*
+ * This function is called by the Gadget-drivers when they have a request for
+ * the UDC (for us).
+ */
+static int s3c24xx_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct s3c_request *req;
+ struct s3c_ep *ep;
+ struct s3c24xx_udc *udc;
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ req = container_of(_req, struct s3c_request, req);
+ if (unlikely(!_req || !_req->complete || !_req->buf ||
+ !list_empty(&req->queue))) {
+ pk_err("Bad params for a new EP queue\n");
+ retval = -EINVAL;
+ goto exit_queue_unlock;
+ }
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+ pk_err("Bad EP or invalid descriptor\n");
+ retval = -EINVAL;
+ goto exit_queue_unlock;
+ }
+
+ udc = ep->dev;
+ if (unlikely(!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)) {
+ pk_err("Bogus device state %p\n", udc->driver);
+ retval = -ESHUTDOWN;
+ goto exit_queue_unlock;
+ }
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* Is this possible? */
+ if (!_req->length) {
+ pk_dbg("EP%i: Empty request | Zero %i\n",
+ ep_index(ep), req->req.zero);
+ done(ep, req, 0);
+ retval = 0;
+ goto exit_queue_unlock;
+ }
+
+ /*
+ * By the IN-endpoints only add the new request to the
+ * internal EP-queue and schedule the tasklet. The tasklet
+ * function will check if the request data can be sent or
+ * not.
+ * (Luis Galdos)
+ */
+ if (ep_is_in(ep) && ep_index(ep) != 0) {
+ /* unsigned long flags; */
+
+ /* spin_lock_irqsave(&ep->lock, flags); */
+#if defined(DEBUG_S3C2443_UDC_QUEUE)
+ {
+ u8 ch1, ch2;
+ int len;
+ u8 *ptr;
+
+ ptr = (u8 *)_req->buf;
+ len = _req->length;
+ ch1 = *ptr;
+ ch2 = *(ptr + len - 1);
+ printk(KERN_DEBUG "%p: len=%i, ep=%02x, 0x%02x ... 0x%02x [N]\n",
+ req, len, ep_index(ep), ch1, ch2);
+ }
+#endif
+ list_add_tail(&req->queue, &ep->queue);
+ /* spin_unlock_irqrestore(&ep->lock, flags); */
+ tasklet_hi_schedule(&ep->in_tasklet);
+ } else {
+ int handled;
+
+ /*
+ * If the request couldn't be handled, then tail it into the
+ * queue of the endpoint
+ */
+ handled = 0;
+ if (list_empty(&ep->queue) && likely(!ep->stopped)) {
+
+ if (unlikely(ep_index(ep) == 0)) {
+ list_add_tail(&req->queue, &ep->queue);
+ s3c24xx_udc_ep0_kick(udc, ep);
+ handled = 1;
+
+ } else {
+ /*
+ * The read-function returns zero if the request is not
+ * done (there is free available buffer space). In this
+ * case we must add the request to the internal queue.
+ * (Luis Galdos)
+ */
+ retval = s3c24xx_udc_read_fifo(ep, req);
+ handled = (retval == 1) ? (1) : (0);
+
+ /* Error handling */
+ if (retval < 0) {
+ pk_err("EP%i: Read FIFO error\n",
+ ep_index(ep));
+ goto exit_queue_unlock;
+ }
+ }
+ }
+
+ /* Advances the queue with the non handled request */
+ if (!handled) {
+
+ if (!ep_is_in(ep)) {
+ /* Free the hold configuration bit for receiving further packets */
+ ulong ecr;
+
+ ecr = usb_read(udc, S3C24XX_UDC_ECR_REG, ep_index(ep));
+ ecr &= ~S3C24XX_UDC_ECR_OUTPKTHLD;
+ usb_write(udc, ecr, S3C24XX_UDC_ECR_REG, ep_index(ep));
+
+ pk_dbg_out("EP%i: Queued len %u\n", ep_index(ep), _req->length);
+ }
+
+ list_add_tail(&req->queue, &ep->queue);
+ }
+ }
+
+ retval = 0;
+
+ exit_queue_unlock:
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return retval;
+}
+
+/* Dequeue one USB-request */
+static int s3c24xx_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct s3c_ep *ep;
+ struct s3c_request *req;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep || ep->ep.name == ep0name)
+ return -EINVAL;
+
+ pk_dbg("EP%i: Dequeue called\n", ep_index(ep));
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+
+ /* Make sure it's actually queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return -EINVAL;
+ }
+
+ done(ep, req, -ECONNRESET);
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+/*
+ * Halt specific EP. If the value is equal one, then halt the EP, by zero
+ * enable the EP once again
+ */
+static int s3c24xx_udc_set_halt(struct usb_ep *_ep, int halt)
+{
+ struct s3c_ep *ep;
+ unsigned long flags;
+ int retval, epnr;
+ struct s3c24xx_udc *udc;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ udc = ep->dev;
+ epnr = ep_index(ep);
+
+ pk_dbg("%s the EP%i\n", halt ? "Halting" : "Enabling", epnr);
+
+ if (!ep->desc) {
+ pk_err("Attempted to halt uninitialized ep %s\n", ep->ep.name);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /*
+ * Don't halt the EP if it's an IN and not empty
+ */
+ retval = 0;
+ if (ep_is_in(ep) && !list_empty(&ep->queue)) {
+ retval = -EAGAIN;
+ } else {
+ if (halt)
+ usb_set(udc, S3C24XX_UDC_ECR_ESS, S3C24XX_UDC_ECR_REG, epnr);
+ else
+ usb_clear(udc, S3C24XX_UDC_ECR_ESS, S3C24XX_UDC_ECR_REG, epnr);
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return retval;
+}
+
+/*
+ * Return the available data bytes of the EP-FIFO
+ */
+static int s3c24xx_udc_fifo_status(struct usb_ep *_ep)
+{
+ u32 csr;
+ int count = 0;
+ struct s3c_ep *ep;
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ if (!_ep) {
+ pk_dbg("%s: bad ep\n", __FUNCTION__);
+ return -ENODEV;
+ }
+
+ /* LPD can't report unclaimed bytes from IN fifos */
+ if (ep_is_in(ep))
+ return -EOPNOTSUPP;
+
+ csr = usb_read(ep->dev, S3C24XX_UDC_EP_STATUS_REG, ep_index(ep));
+ if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN ||
+ csr & S3C24XX_UDC_EP_RX_SUCCESS) {
+
+ count = usb_read(ep->dev, S3C24XX_UDC_BYTE_READ_CNT_REG, ep_index(ep));
+
+ if (usb_read(ep->dev, S3C24XX_UDC_EP_STATUS_REG, ep_index(ep))
+ & S3C24XX_UDC_EP_LWO)
+ count = count * 2 -1;
+ else
+ count = count * 2;
+ }
+
+ return count;
+}
+
+/*
+ * Flush the FIFO of the endpoint
+ */
+static void s3c24xx_udc_fifo_flush(struct usb_ep *_ep)
+{
+ struct s3c_ep *ep;
+ struct s3c24xx_udc *udc;
+ int epnr;
+
+ if (!_ep) {
+ pk_err("Can't flush an EP, NULL pointer passed\n");
+ return;
+ }
+
+ ep = container_of(_ep, struct s3c_ep, ep);
+ epnr = ep_index(ep);
+ udc = ep->dev;
+
+ if (unlikely(epnr == 0)) {
+ pk_err("EP0 can't be flushed. Aborting.\n");
+ return;
+ }
+
+ /*
+ * Flush the EP by using the control register
+ * IMPORTANT: Dont enable the below code, otherwise is not possible to
+ * recover the EP from it, we need to restart the UDC for this purpose (we dont
+ * really want to do it!).
+ */
+#if 0
+ {
+ ulong ecr;
+
+ ecr = usb_read(udc, S3C24XX_UDC_ECR_REG, epnr);
+ pk_err("EP%i: Flushing now [ecr 0x%08lx]\n", epnr, ecr);
+ usb_write(udc, ecr | S3C24XX_UDC_ECR_FLUSH, S3C24XX_UDC_ECR_REG, epnr);
+ }
+#endif
+}
+
+/*
+ * Function used for reading the data from the FIFO to the passed buffer
+ * This function is only used for the setup-handling
+ */
+static inline int s3c24xx_udc_ep0_setup_read(struct s3c_ep *ep, u16 *cp, int max)
+{
+ int bytes;
+ int count, pending;
+ struct s3c24xx_udc *udc;
+ ulong ep0sr;
+
+ udc = ep->dev;
+
+ ep0sr = readl(udc->base + S3C24XX_UDC_EP0SR_REG);
+ if (!(ep0sr & S3C24XX_UDC_EP0SR_RSR)) {
+ pk_dbg("RSR-bit unset. Aborting setup read.\n");
+ return 0;
+ }
+
+ /* Now get the number of bytes to read from the FIFO */
+ count = usb_read(udc, S3C24XX_UDC_BRCR_REG, ep_index(ep));
+ if (ep0sr & S3C24XX_UDC_EP0SR_LWO)
+ bytes = count * 2 - 1;
+ else
+ bytes = count * 2;
+
+ /*
+ * If we not enough space, then only process maximal number of bytes
+ */
+ pending = 0;
+ if (bytes > max) {
+ pk_info("Setup packet length %i exceeds max. %i\n", bytes, max);
+ count = max / 2;
+ bytes = max;
+ pending = 1;
+ }
+
+ while (count--)
+ *cp++ = (u16)readl(udc->base + S3C24XX_UDC_EP0BR_REG);
+
+ /*
+ * IMPORTANT: Dont delete the below line, then otherwise the controller will
+ * not work (but why not?)
+ * (Luis Galdos)
+ */
+ if (!pending)
+ writel(S3C24XX_UDC_EP0_RX_SUCCESS, udc->base + S3C24XX_UDC_EP0SR_REG);
+
+ return bytes;
+}
+
+/*
+ * udc_set_address - set the USB address for this device
+ * @address:
+ *
+ * Called from control endpoint function
+ * after it decodes a set address setup packet.
+ */
+static void s3c24xx_udc_set_address(struct s3c24xx_udc *udc, unsigned char address)
+{
+ udc->usb_address = address;
+}
+
+/* Write data into the FIFO of the EP0 */
+static void s3c24xx_udc_ep0_write(struct s3c24xx_udc *udc)
+{
+ struct s3c_request *req;
+ struct s3c_ep *ep = &udc->ep[0];
+ int ret, completed, need_zlp = 0;
+
+ if (list_empty(&ep->queue))
+ req = 0;
+ else
+ req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+ if (!req) {
+ pk_dbg("EP0: NULL write request?\n");
+ return;
+ }
+
+ if (req->req.length == 0) {
+ udc->ep0state = WAIT_FOR_SETUP;
+ done(ep, req, 0);
+ return;
+ }
+
+ if (req->req.length - req->req.actual == ep0_fifo_size) {
+ /* Next write will end with the packet size, */
+ /* so we need Zero-length-packet */
+ need_zlp = 1;
+ }
+
+ /* The write function returns the number of remaining bytes of this request */
+ ret = s3c24xx_udc_write_packet(ep, req);
+ completed = (ret == 0) ? 1 : 0;
+
+ if (completed && !need_zlp) {
+ pk_dbg("EP0: Finished, waiting for status\n");
+ udc->ep0state = WAIT_FOR_SETUP;
+ done(ep, req, 0);
+ } else if (need_zlp) {
+ /* The next TX-interrupt will send the ZLP */
+ udc->ep0state = DATA_STATE_NEED_ZLP;
+ } else
+ /* We need to send more data to the host in the next transfer */
+ pk_dbg("EP0: not finished | %p\n", req);
+}
+
+/* Return zero if NO additional request is available */
+static inline int s3c2443_ep0_fix_set_setup(struct s3c24xx_udc *udc,
+ struct usb_ctrlrequest *ctrl)
+{
+ int timeout_us, cnt;
+ ulong ep0sr;
+ int retval;
+ struct s3c_ep *ep;
+
+ ep = &udc->ep[0];
+ timeout_us = 1000;
+ do {
+ udelay(1);
+ ep0sr = readl(udc->base + S3C24XX_UDC_EP0SR_REG);
+ timeout_us--;
+ } while (timeout_us && !(ep0sr & S3C24XX_UDC_EP0SR_RSR));
+
+ /* If a timeout happens then returns zero */
+ retval = 0;
+ if (timeout_us) {
+ cnt = s3c24xx_udc_ep0_setup_read(ep, (u16 *)ctrl,
+ sizeof(struct usb_ctrlrequest));
+ if (cnt > 0)
+ retval = 1;
+ }
+
+ return retval;
+}
+
+/*
+ * Wait for a setup packet and read if from the FIFO before passint it to the
+ * gadget driver
+ */
+#define S3C2443_SETUP_IS_SET_REQ(ctrl) \
+ (ctrl->bRequest == USB_REQ_SET_CONFIGURATION || \
+ ctrl->bRequest == USB_REQ_SET_INTERFACE || \
+ ctrl->bRequest == USB_REQ_SET_DESCRIPTOR || \
+ ctrl->bRequest == USB_REQ_SET_FEATURE || \
+ ctrl->bRequest == USB_REQ_SET_ADDRESS)
+
+#define S3C2443_MAX_NUMBER_SETUPS (10)
+static void s3c24xx_ep0_setup(struct s3c24xx_udc *udc)
+{
+ struct s3c_ep *ep;
+ int retval, bytes, cnt;
+ struct usb_ctrlrequest *pctrl;
+ struct usb_ctrlrequest ctrls[S3C2443_MAX_NUMBER_SETUPS];
+ int recv_ctrls[S3C2443_MAX_NUMBER_SETUPS];
+
+ memset(recv_ctrls, 0x0, sizeof(recv_ctrls));
+ memset(ctrls, 0x0, sizeof(ctrls));
+
+ /* Nuke all previous transfers of this control EP */
+ ep = &udc->ep[0];
+ nuke(ep, -EPROTO);
+
+ /*
+ * @FIXME: This is a really bad code, but at this moment there is no better
+ * proposal for a workaround, since I dont know why the HELL the controller
+ * stops working by some SETUP-frames which require a status stage.
+ * Probably the problem is that the controller sends automatically the
+ * IN-Data of the status stage (with length zero) and it doesn't NAK the
+ * proceeded IN-request. Since, we have still data from the first IN-frame in
+ * the FIFO and the second arrived very fast, the FIFO gets stuck.
+ * Unfortunately we can't reset the FIFO (the control bit for flushing the
+ * FIFO seems to be only for the EPs different than zero).
+ *
+ * For avoiding the described problem probably a delayed-work function can help,
+ * otherwise the below code can't be removed.
+ * (Luis Galdos)
+ */
+
+ /* Read the first SETUP frame as a normal frame */
+ pctrl = &ctrls[0];
+ recv_ctrls[0] = s3c24xx_udc_ep0_setup_read(ep, (u16 *)pctrl, sizeof(* pctrl));
+ if (recv_ctrls[0] <= 0) {
+ pk_dbg("Nothing to process? Aborting.\n");
+ return;
+ }
+
+ /* Here we need to read the other packets */
+ for (cnt = 1; S3C2443_SETUP_IS_SET_REQ(pctrl) && cnt < S3C2443_MAX_NUMBER_SETUPS;
+ cnt++) {
+ pctrl = &ctrls[cnt];
+ bytes = s3c2443_ep0_fix_set_setup(udc, pctrl);
+
+ if (bytes <= 0)
+ break;
+
+ recv_ctrls[cnt] = bytes;
+ }
+
+ if (cnt == S3C2443_MAX_NUMBER_SETUPS)
+ pk_err("@FIXME: Handling by SETUP overflows\n");
+
+ /* Now start with the processing of the SETUP-frames */
+ retval = 0;
+ for (cnt = 0; recv_ctrls[cnt] > 0 && cnt < S3C2443_MAX_NUMBER_SETUPS; cnt++) {
+
+ pctrl = &ctrls[cnt];
+ bytes = recv_ctrls[cnt];
+
+ /* Set the correct direction for this SETUP-frame */
+ if (likely(pctrl->bRequestType & USB_DIR_IN)) {
+ pk_dbg("EP0: Preparing new IN frame (%i bytes)\n", bytes);
+ ep->bEndpointAddress |= USB_DIR_IN;
+ } else {
+ pk_dbg("EP0: Preparing new OUT frame (%i bytes)\n", bytes);
+ ep->bEndpointAddress &= ~USB_DIR_IN;
+ }
+
+ /* Handle some SETUP packets ourselves */
+ retval = 0;
+ switch (pctrl->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ if (pctrl->bRequestType != (USB_TYPE_STANDARD |
+ USB_RECIP_DEVICE))
+ break;
+
+ /*
+ * If the setup frame was for us, then continue with the
+ * next available packet
+ */
+ pk_dbg("Set address request (%d)\n", pctrl->wValue);
+ s3c24xx_udc_set_address(udc, pctrl->wValue);
+ continue;
+
+ default:
+ pk_dbg("bRequestType 0x%02x | bRequest 0x%02x | "
+ "wValue 0x%04x | wIndex 0x%04x | wLength %u\n",
+ pctrl->bRequestType, pctrl->bRequest,
+ pctrl->wValue, pctrl->wIndex, pctrl->wLength);
+ break;
+ }
+
+ /* Check if need to call the Gadget-setup handler */
+ if (likely(udc->driver)) {
+ spin_unlock(&udc->lock);
+ retval = udc->driver->setup(&udc->gadget, pctrl);
+ spin_lock(&udc->lock);
+
+ /* Error values are littler than zero */
+ if (retval < 0)
+ s3c2443_print_err_packet_setup(retval, pctrl);
+ }
+ }
+
+ /* By error-free setups return at this place */
+ if (!retval)
+ return;
+
+ /* @XXX: Test if the STALL is really send to the host */
+ udc->ep0state = WAIT_FOR_SETUP;
+ writel(S3C24XX_UDC_EP0CR_ESS, udc->base + S3C24XX_UDC_EP0CR_REG);
+}
+
+/*
+ * handle ep0 interrupt
+ */
+static void s3c24xx_handle_ep0(struct s3c24xx_udc *udc)
+{
+ struct s3c_ep *ep = &udc->ep[0];
+ u32 csr;
+ unsigned long handled;
+
+ handled = 0;
+ csr = readl(udc->base + S3C24XX_UDC_EP0SR_REG);
+
+ /* Clear the STALL bit */
+ if (csr & S3C24XX_UDC_EP0_STALL) {
+ pk_dbg("EP0: Stall success\n");
+ writel(S3C24XX_UDC_EP0_STALL, udc->base + S3C24XX_UDC_EP0SR_REG);
+ nuke(ep, -ECONNABORTED);
+ udc->ep0state = WAIT_FOR_SETUP;
+ handled = 1;
+ }
+
+ /*
+ * We must check if there is additional data to send. We send at this
+ * place the ZLP too (DONT try to do it inside the EP0-write function!)
+ */
+ if (csr & S3C24XX_UDC_EP0_TX_SUCCESS) {
+ struct s3c_ep *ep0 = &udc->ep[0];
+ struct s3c_request *req;
+ int left;
+
+ req = list_entry(ep0->queue.next, struct s3c_request, queue);
+ if (req) {
+ left = req->req.length - req->req.actual;
+ pk_dbg("EP0: TX success | %p: left %i\n", req, left);
+
+ /* Send the pending ZLP of the last request */
+ if (left || udc->ep0state == DATA_STATE_NEED_ZLP)
+ s3c24xx_udc_ep0_write(udc);
+ }
+
+ /*
+ * Clear the status bit ONLY if we are not going to send a ZLP in the
+ * next IN-transfer
+ */
+ if (udc->ep0state != DATA_STATE_NEED_ZLP)
+ writel(S3C24XX_UDC_EP0_TX_SUCCESS,
+ udc->base + S3C24XX_UDC_EP0SR_REG);
+
+ handled = 1;
+ }
+
+ /* Check if we have received data from the host (SETUP-frames) */
+ if (csr & S3C24XX_UDC_EP0_RX_SUCCESS) {
+ if (udc->ep0state == WAIT_FOR_SETUP) {
+ pk_dbg("EP0: RX success | Wait for setup\n");
+ s3c24xx_ep0_setup(udc);
+ } else if (udc->ep0state == DATA_STATE_RECV) {
+ s3c2443_udc_ep0_read(udc);
+ } else {
+ pk_err("EP0: RX success | Strange state %i\n",
+ udc->ep0state);
+ udc->ep0state = WAIT_FOR_SETUP;
+ writel(S3C24XX_UDC_EP0SR_RSR, udc->base + S3C24XX_UDC_EP0SR_REG);
+ }
+
+ handled = 1;
+ }
+
+ /*
+ * Under unknown conditions the EP0 is generating some interrupts which we can't
+ * identify. Since these IRQs seem to have none side effects, we only print
+ * a debug message and return inmediately. Please note that the IRQs are
+ * being generated only by the enumeration of the device, just when the EP0 is
+ * in use.
+ * @XXX: Add some additional register outprints (EP0SR, EP0CR, SSR, etc.)
+ * (Luis Galdos)
+ */
+ if (!handled) {
+ pk_dbg("[ ERROR ] s3c24xx-udc: Unhandled EP0 IRQ.\n");
+ }
+}
+
+/*
+ * This function is called for the gadget-drivers which uses the EP0 for transferring
+ * data to/from the host. This is the case of the RNDIS driver.
+ * (Luis Galdos)
+ */
+static void s3c24xx_udc_ep0_kick(struct s3c24xx_udc *udc, struct s3c_ep *ep)
+{
+ if (ep_is_in(ep)) {
+ udc->ep0state = DATA_STATE_XMIT;
+ s3c24xx_udc_ep0_write(udc);
+ } else {
+ /* Always set the state of the endpoint first */
+ udc->ep0state = DATA_STATE_RECV;
+ s3c2443_udc_ep0_read(udc);
+ }
+}
+
+static int s3c_udc_get_frame(struct usb_gadget *_gadget)
+{
+ u32 frame;
+ struct s3c24xx_udc *udc;
+
+ udc = the_controller;
+ if (!udc)
+ return -ENODEV;
+
+ frame = readl(udc->base + S3C24XX_UDC_FNR_REG);
+ return (frame & 0x7ff);
+}
+
+static int s3c_udc_wakeup(struct usb_gadget *_gadget)
+{
+ return -ENOTSUPP;
+}
+
+static const struct usb_gadget_ops s3c_udc_ops = {
+ .get_frame = s3c_udc_get_frame,
+ .wakeup = s3c_udc_wakeup,
+ /* current versions must always be self-powered */
+};
+
+static void nop_release(struct device *dev)
+{
+ pk_dbg("%s %s\n", __FUNCTION__, dev->bus_id);
+}
+
+/*
+ * At this moment provide only non-configurable endpoints (address, direction and
+ * type fixed). The syntax definition is coming from the file[1].
+ *
+ * [1] drivers/usb/gadget/autoconf.c:ep_matches()
+ */
+static struct s3c24xx_udc memory = {
+ .usb_address = 0,
+
+ .gadget = {
+ .ops = &s3c_udc_ops,
+ .ep0 = &memory.ep[0].ep,
+ .name = DRIVER_NAME,
+ .dev = {
+ .bus_id = "gadget",
+ .release = nop_release,
+ },
+ },
+
+ .ep[0] = {
+ .ep = {
+ .name = ep0name,
+ .ops = &s3c24xx_ep_ops,
+ .maxpacket = EP0_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .bEndpointAddress = 0,
+ .bmAttributes = 0,
+ .ep_type = ep_control,
+ .fifo = S3C24XX_UDC_EP0BR_REG,
+ },
+
+ .ep[1] = {
+ .ep = {
+ .name = "ep1in-bulk",
+ .ops = &s3c24xx_ep_ops,
+ .maxpacket = EP_FIFO_SIZE2,
+ },
+ .dev = &memory,
+ .bEndpointAddress = USB_DIR_IN | 1,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .ep_type = ep_bulk_in,
+ .fifo = S3C24XX_UDC_EP1BR_REG,
+ },
+
+ .ep[2] = {
+ .ep = {
+ .name = "ep2out-bulk",
+ .ops = &s3c24xx_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .bEndpointAddress = 2,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .ep_type = ep_bulk_out,
+ .fifo = S3C24XX_UDC_EP2BR_REG,
+ },
+
+ .ep[3] = {
+ .ep = {
+ .name = "ep3in-int",
+ .ops = &s3c24xx_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .bEndpointAddress = USB_DIR_IN | 3,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .ep_type = ep_interrupt,
+ .fifo = S3C24XX_UDC_EP3BR_REG,
+ },
+
+ .ep[4] = {
+ .ep = {
+ .name = "ep4out-int",
+ .ops = &s3c24xx_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .bEndpointAddress = 4,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .ep_type = ep_interrupt,
+ .fifo = S3C24XX_UDC_EP4BR_REG,
+ },
+ .ep[5] = {
+ .ep = {
+ .name = "ep5in-int",
+ .ops = &s3c24xx_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .bEndpointAddress = USB_DIR_IN | 5,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .ep_type = ep_interrupt,
+ .fifo = S3C24XX_UDC_EP5BR_REG,
+ },
+ .ep[6] = {
+ .ep = {
+ .name = "ep6out-int",
+ .ops = &s3c24xx_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .bEndpointAddress = 6,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .ep_type = ep_interrupt,
+ .fifo = S3C24XX_UDC_EP6BR_REG,
+ },
+ .ep[7] = {
+ .ep = {
+ .name = "ep7in-int",
+ .ops = &s3c24xx_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .bEndpointAddress = USB_DIR_IN | 7,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .ep_type = ep_interrupt,
+ .fifo = S3C24XX_UDC_EP7BR_REG,
+ },
+ .ep[8] = {
+ .ep = {
+ .name = "ep8out-int",
+ .ops = &s3c24xx_ep_ops,
+ .maxpacket = EP_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .bEndpointAddress = 8,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .ep_type = ep_interrupt,
+ .fifo = S3C24XX_UDC_EP8BR_REG,
+ },
+};
+
+
+/*
+ * probe - binds to the platform device
+ */
+static int s3c24xx_udc_probe(struct platform_device *pdev)
+{
+ struct s3c24xx_udc *udc = &memory;
+ int retval;
+ struct s3c2410_udc_mach_info *mach_info;
+ int cnt;
+
+ pk_dbg("Probing a new device ID %i\n", pdev->id);
+
+ /* Get the platform data */
+ mach_info = pdev->dev.platform_data;
+ if (!mach_info) {
+ pk_err("No platform data? Aborting.\n");
+ retval = -EINVAL;
+ goto err_exit;
+ }
+
+ udc->mach_info = mach_info;
+ udc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!udc->mem) {
+ pk_err("Couldn't get the IO memory resource\n");
+ retval = -EINVAL;
+ goto err_exit;
+ }
+
+ udc->mem = request_mem_region(udc->mem->start, IOMEMSIZE(udc->mem), pdev->name);
+ if (!udc->mem) {
+ pk_err("Failed to request IO memory region.\n");
+ retval = -ENOENT;
+ goto err_exit;
+ }
+
+ udc->base = ioremap(udc->mem->start, IOMEMSIZE(udc->mem));
+ if (!udc->base) {
+ pk_err("Couldn't ioremap the IO memory region\n");
+ retval = -EINVAL;
+ goto err_free_mem;
+ }
+
+ /* Init the internal gadget device */
+ device_initialize(&udc->gadget.dev);
+ udc->gadget.dev.parent = &pdev->dev;
+ udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
+
+ /* @XXX: We can use only one device, right? */
+ the_controller = udc;
+ platform_set_drvdata(pdev, udc);
+
+ /* Init the EPs */
+ s3c24xx_udc_reinit(udc);
+
+ /* Disable the platform dependent UDC-hardware */
+ s3c24xx_udc_disable(udc);
+
+ spin_lock_init(&udc->lock);
+ udc->dev = pdev;
+
+ udc->gadget.is_dualspeed = 1;
+ udc->gadget.is_otg = 0;
+ udc->gadget.is_a_peripheral = 0;
+ udc->gadget.b_hnp_enable = 0;
+ udc->gadget.a_hnp_support = 0;
+ udc->gadget.a_alt_hnp_support = 0;
+
+ /* Get the IRQ for the internal handling of the EPs */
+ retval = request_irq(IRQ_USBD, s3c24xx_udc_irq,
+ IRQF_DISABLED, pdev->name, udc);
+ if (retval) {
+ pk_err("Cannot get irq %i, err %d\n", IRQ_USBD, retval);
+ retval = -EBUSY;
+ goto err_iounmap;
+ }
+
+ /* Activate the driver first when is going to be used */
+ disable_irq(IRQ_USBD);
+ pk_dbg("IRQ %i for the UDC\n", IRQ_USBD);
+
+ if (mach_info && mach_info->vbus_pin > 0) {
+ udc->irq_vbus = s3c2410_gpio_getirq(mach_info->vbus_pin);
+ retval = request_irq(udc->irq_vbus,
+ s3c24xx_udc_vbus_irq,
+ IRQF_DISABLED | IRQF_SHARED |
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ pdev->name, udc);
+ if (retval) {
+ pk_err("Can't get vbus IRQ (%i) for IO %i (err %d)\n",
+ udc->irq_vbus, mach_info->vbus_pin, retval);
+ goto err_free_udc_irq;
+ }
+ pk_dbg("IRQ %i for vbus detection\n", udc->irq_vbus);
+ } else
+ udc->vbus = 1;
+
+ /*
+ * Init the tasklet for the IN-endpoints at this place, so that we can kill
+ * the tasklets when the module is going to be removed
+ * (Luis Galdos)
+ */
+ for (cnt = 0; cnt < S3C_MAX_ENDPOINTS; cnt++) {
+ struct s3c_ep *ep = &udc->ep[cnt];
+
+ if (ep_is_in(ep)) {
+ tasklet_init(&ep->in_tasklet,
+ s3c24xx_udc_epin_tasklet_func,
+ (unsigned long)ep);
+ }
+
+ spin_lock_init(&ep->lock);
+ }
+
+ /*
+ * Enable the wakeup support only if a interrupt IO for the bus detection
+ * was passed with the platform data
+ */
+ if (udc->irq_vbus) {
+ pk_dbg("Enabling the wakeup IRQ %i\n", udc->irq_vbus);
+ device_init_wakeup(&pdev->dev, 1);
+ device_set_wakeup_enable(&pdev->dev, 0);
+ }
+
+ return 0;
+
+ err_free_udc_irq:
+ free_irq(IRQ_USBD, udc);
+
+ err_iounmap:
+ iounmap(udc->base);
+
+ err_free_mem:
+ release_mem_region(udc->mem->start, IOMEMSIZE(udc->mem));
+
+ err_exit:
+ platform_set_drvdata(pdev, NULL);
+
+ /*
+ * Unset the controller pointer otherwise the function for registering
+ * a new gadget can crash the system (it uses this pointer)
+ * (Luis Galdos)
+ */
+ the_controller = NULL;
+ return retval;
+}
+
+
+static int s3c24xx_udc_remove(struct platform_device *pdev)
+{
+ struct s3c2410_udc_mach_info *imach;
+ struct s3c24xx_udc *udc;
+ int cnt;
+ struct s3c_ep *ep;
+
+ pk_dbg("Removing the UDC driver (ID %i)\n", pdev->id);
+
+ udc = platform_get_drvdata(pdev);
+ imach = pdev->dev.platform_data;
+
+ /* Kill the tasklet of all the IN-endpoints */
+ for (cnt = 0; cnt < S3C_MAX_ENDPOINTS; cnt++) {
+ ep = &udc->ep[cnt];
+ if (ep_is_in(ep))
+ tasklet_kill(&ep->in_tasklet);
+ }
+
+ s3c24xx_udc_disable(udc);
+ usb_gadget_unregister_driver(udc->driver);
+
+ free_irq(IRQ_USBD, udc);
+
+ if (udc->irq_vbus) {
+ pk_dbg("Disabling the wakeup IRQ %i\n", udc->irq_vbus);
+ device_init_wakeup(&pdev->dev, 0);
+ }
+
+ /*
+ * If an IRQ for the vbus was passed, then disable it too
+ * (Luis Galdos)
+ */
+ if (imach && udc->irq_vbus) {
+ pk_dbg("Freeing the vbus IRQ %i\n", udc->irq_vbus);
+ free_irq(udc->irq_vbus, udc);
+ udc->irq_vbus = 0;
+ }
+
+ release_mem_region(udc->mem->start, IOMEMSIZE(udc->mem));
+
+ platform_set_drvdata(pdev, NULL);
+
+ the_controller = NULL;
+
+ return 0;
+}
+
+/*
+ * From another UDC-drivers seems to be, that is required to disconnect the
+ * USB-device if it's connected to a host.
+ */
+#ifdef CONFIG_PM
+static int s3c2443_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct s3c24xx_udc *udc;
+ int retval;
+
+ udc = platform_get_drvdata(pdev);
+
+ /* Need to disconnect first */
+ if (udc->vbus)
+ s3c24xx_udc_disable(udc);
+
+ /* Enable the wakeup if requested */
+ retval = 0;
+ if (device_may_wakeup(&pdev->dev)) {
+
+ /* Paranoic sanity check! */
+ if (!udc->irq_vbus) {
+ pk_err("No wakeup IRQ defined?\n");
+ retval = -EINVAL;
+ goto exit_suspend;
+ }
+
+ retval = enable_irq_wake(udc->irq_vbus);
+ if (retval)
+ goto exit_suspend;
+ }
+
+exit_suspend:
+ return retval;
+}
+
+static int s3c2443_udc_resume(struct platform_device *pdev)
+{
+ struct s3c24xx_udc *udc;
+ int retval;
+
+ udc = platform_get_drvdata(pdev);
+
+ retval = 0;
+ if (device_may_wakeup(&pdev->dev)) {
+
+ /* Paranoic sanity check */
+ if (!udc->irq_vbus) {
+ pk_err("No wakeup IRQ defined?\n");
+ retval = -EINVAL;
+ goto exit_resume;
+ }
+
+ disable_irq_wake(udc->irq_vbus);
+ }
+
+ /*
+ * Check the current state of the VBUS, then probably a host was connected
+ * during the suspend-time and this will not generate an interrupt
+ */
+ udc->vbus = s3c2443_udc_vbus_state(udc);
+
+ /* Enable the UDC for starting the enumeration */
+ if (udc->vbus && udc->driver)
+ retval = s3c24xx_udc_enable(udc);
+
+exit_resume:
+ return retval;
+}
+#else
+#define s3c2443_udc_suspend NULL
+#define s3c2443_udc_resume NULL
+#endif
+
+static struct platform_driver s3c24xx_udc_driver = {
+ .probe = s3c24xx_udc_probe,
+ .remove = s3c24xx_udc_remove,
+ .suspend = s3c2443_udc_suspend,
+ .resume = s3c2443_udc_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init udc_init(void)
+{
+ int ret;
+
+ pk_info("Loading (%s - %s)\n", DRIVER_BUILD_DATE, DRIVER_BUILD_TIME);
+ ret = platform_driver_register(&s3c24xx_udc_driver);
+ return ret;
+}
+
+static void __exit udc_exit(void)
+{
+ pk_info("Unloading (%s - %s)\n", DRIVER_BUILD_DATE, DRIVER_BUILD_TIME);
+ platform_driver_unregister(&s3c24xx_udc_driver);
+}
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Luis Galdos, luis.galdos[at]digi.com");
+MODULE_AUTHOR("Samsung Electronics");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/s3c2443_udc.h b/drivers/usb/gadget/s3c2443_udc.h
new file mode 100644
index 000000000000..f22985128cff
--- /dev/null
+++ b/drivers/usb/gadget/s3c2443_udc.h
@@ -0,0 +1,159 @@
+/*
+ * drivers/usb/gadget/s3c-udc.h
+ * Samsung S3C on-chip full/high speed USB device controllers
+ * $Id: s3c-udc.h,v 1.6 2006/11/30 22:55:18 dasan Exp $*
+ * Copyright (C) 2005 for Samsung Electronics
+ * - by Jaswinder Singh Brar <jassi@samsung.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 __S3C_USB_GADGET
+#define __S3C_USB_GADGET
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <mach/hardware.h>
+
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+
+
+#include <asm/plat-s3c24xx/regs-udc.h>
+#include <asm/plat-s3c24xx/udc.h>
+#include <linux/io.h>
+#include <mach/regs-s3c2443-clock.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-irq.h>
+
+
+// Max packet size
+#ifdef CONFIG_USB_GADGET_S3C_FS
+#define EP0_FIFO_SIZE 8
+#define EP_FIFO_SIZE 64
+#define S3C_MAX_ENDPOINTS 5
+#else
+#define EP0_FIFO_SIZE 64
+#define EP_FIFO_SIZE 512
+#define EP_FIFO_SIZE2 1024
+#define S3C_MAX_ENDPOINTS 9
+#endif
+
+#define WAIT_FOR_SETUP 0
+#define DATA_STATE_XMIT 1
+#define DATA_STATE_NEED_ZLP 2
+#define WAIT_FOR_OUT_STATUS 3
+#define DATA_STATE_RECV 4
+
+
+
+
+
+typedef enum ep_type {
+ ep_control,
+ ep_bulk_in,
+ ep_bulk_out,
+ ep_interrupt
+} ep_type_t;
+
+
+
+struct s3c_ep {
+ struct usb_ep ep;
+ struct s3c24xx_udc *dev;
+
+ const struct usb_endpoint_descriptor *desc;
+ struct list_head queue;
+ unsigned long pio_irqs;
+
+ u8 stopped;
+ u8 bEndpointAddress;
+ u8 bmAttributes;
+
+ ep_type_t ep_type;
+ u32 fifo;
+#ifdef CONFIG_USB_GADGET_S3C_FS
+ u32 csr1;
+ u32 csr2;
+#endif
+
+ /* Tasklet for the data handling of the IN-endpoints (Luis Galdos) */
+ struct tasklet_struct in_tasklet;
+ spinlock_t lock;
+};
+
+
+
+struct s3c_request {
+ struct usb_request req;
+ struct list_head queue;
+};
+
+
+
+struct s3c24xx_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct platform_device *dev;
+ spinlock_t lock;
+
+ int ep0state;
+ struct s3c_ep ep[S3C_MAX_ENDPOINTS];
+
+ unsigned char usb_address;
+
+ unsigned req_pending:1, req_std:1, req_config:1;
+
+
+ struct s3c2410_udc_mach_info *mach_info;
+ struct resource *mem;
+ void __iomem *base;
+ u8 vbus;
+
+ /* Interrupts for bus detection and UDC internal */
+ int irq_vbus;
+ int irq_udc;
+};
+
+extern struct s3c24xx_udc *the_controller;
+
+#define ep_is_in(ep) (((ep)->bEndpointAddress&USB_DIR_IN) == USB_DIR_IN)
+#define ep_index(ep) ((ep)->bEndpointAddress&0xF)
+#define ep_maxpacket(ep) ((ep)->ep.maxpacket)
+
+#endif
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index 37879af1c433..e7bd43d15bed 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -273,7 +273,7 @@ static int __init init(void)
return usb_composite_register(&gserial_driver);
}
-module_init(init);
+late_initcall(init);
static void __exit cleanup(void)
{
diff --git a/drivers/usb/gadget/stmp_updater.c b/drivers/usb/gadget/stmp_updater.c
new file mode 100644
index 000000000000..6642bb5bed76
--- /dev/null
+++ b/drivers/usb/gadget/stmp_updater.c
@@ -0,0 +1,473 @@
+/*
+ * Freescale STMP378X UUT driver
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+static int utp_init(struct fsg_dev *fsg)
+{
+ init_waitqueue_head(&utp_context.wq);
+ INIT_LIST_HEAD(&utp_context.read);
+ INIT_LIST_HEAD(&utp_context.write);
+ mutex_init(&utp_context.lock);
+ utp_context.buffer = vmalloc(0x10000);
+ if (!utp_context.buffer)
+ return -EIO;
+ utp_context.utp_version = 0x1ull;
+ fsg->utp = &utp_context;
+ return misc_register(&utp_dev);
+}
+
+static void utp_exit(struct fsg_dev *fsg)
+{
+ vfree(utp_context.buffer);
+ misc_deregister(&utp_dev);
+}
+
+static struct utp_user_data *utp_user_data_alloc(size_t size)
+{
+ struct utp_user_data *uud;
+
+ uud = kzalloc(size + sizeof(*uud), GFP_KERNEL);
+ if (!uud)
+ return uud;
+ uud->data.size = size + sizeof(uud->data);
+ INIT_LIST_HEAD(&uud->link);
+ return uud;
+}
+
+static void utp_user_data_free(struct utp_user_data *uud)
+{
+ list_del(&uud->link);
+ kfree(uud);
+}
+
+#define WAIT_ACTIVITY(queue) \
+ wait_event_interruptible(utp_context.wq, !list_empty(&utp_context.queue))
+
+static ssize_t utp_file_read(struct file *file,
+ char __user *buf,
+ size_t size,
+ loff_t *off)
+{
+ struct utp_user_data *uud;
+ size_t size_to_put;
+ int free = 0;
+
+ WAIT_ACTIVITY(read);
+
+ mutex_lock(&utp_context.lock);
+ uud = list_first_entry(&utp_context.read, struct utp_user_data, link);
+ mutex_unlock(&utp_context.lock);
+ size_to_put = uud->data.size;
+ if (size >= size_to_put)
+ free = !0;
+ if (copy_to_user(buf, &uud->data, size_to_put))
+ return -EACCES;
+ if (free)
+ utp_user_data_free(uud);
+ else {
+ pr_info("sizeof = %d, size = %d\n",
+ sizeof(uud->data),
+ uud->data.size);
+
+ pr_err("Will not free utp_user_data, because buffer size = %d,"
+ "need to put %d\n", size, size_to_put);
+ }
+
+ return size_to_put;
+}
+
+
+static ssize_t utp_file_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *off)
+{
+ struct utp_user_data *uud;
+
+ if (size < sizeof(uud->data))
+ return -EINVAL;
+ uud = utp_user_data_alloc(size);
+ if (copy_from_user(&uud->data, buf, size))
+ return -EACCES;
+ mutex_lock(&utp_context.lock);
+ list_add_tail(&uud->link, &utp_context.write);
+ wake_up(&utp_context.wq);
+ mutex_unlock(&utp_context.lock);
+ return size;
+}
+
+static int utp_get_sense(struct fsg_dev *fsg)
+{
+ if (UTP_CTX(fsg)->processed == 0)
+ return -1;
+
+ UTP_CTX(fsg)->processed = 0;
+ return 0;
+}
+
+static int utp_do_read(struct fsg_dev *fsg, void *data, size_t size)
+{
+ struct fsg_buffhd *bh;
+ int rc;
+ u32 amount_left;
+ unsigned int amount;
+
+ /* Get the starting Logical Block Address and check that it's
+ * not too big */
+
+ amount_left = size;
+ if (unlikely(amount_left == 0))
+ return -EIO; /* No default reply*/
+
+ pr_debug("%s: sending %d\n", __func__, size);
+ for (;;) {
+ /* Figure out how much we need to read:
+ * Try to read the remaining amount.
+ * But don't read more than the buffer size.
+ * And don't try to read past the end of the file.
+ * Finally, if we're not at a page boundary, don't read past
+ * the next page.
+ * If this means reading 0 then we were asked to read past
+ * the end of file. */
+ amount = min((unsigned int) amount_left, mod_data.buflen);
+
+ /* Wait for the next buffer to become available */
+ bh = fsg->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(fsg);
+ if (rc)
+ return rc;
+ }
+
+ /* If we were asked to read past the end of file,
+ * end with an empty buffer. */
+ if (amount == 0) {
+ bh->inreq->length = 0;
+ bh->state = BUF_STATE_FULL;
+ break;
+ }
+
+ /* Perform the read */
+ pr_info("Copied to %p, %d bytes started from %d\n",
+ bh->buf, amount, size - amount_left);
+ memcpy(bh->buf, data + size - amount_left, amount);
+ amount_left -= amount;
+ fsg->residue -= amount;
+
+ bh->inreq->length = amount;
+ bh->state = BUF_STATE_FULL;
+
+ /* Send this buffer and go read some more */
+ bh->inreq->zero = 0;
+
+ start_transfer(fsg, fsg->bulk_in, bh->inreq,
+ &bh->inreq_busy, &bh->state);
+
+ fsg->next_buffhd_to_fill = bh->next;
+
+ if (amount_left <= 0)
+ break;
+ }
+
+ return size - amount_left;
+}
+
+static int utp_do_write(struct fsg_dev *fsg, void *data, size_t size)
+{
+ struct fsg_buffhd *bh;
+ int get_some_more;
+ u32 amount_left_to_req, amount_left_to_write;
+ unsigned int amount;
+ int rc;
+ loff_t offset;
+
+ /* Carry out the file writes */
+ get_some_more = 1;
+ amount_left_to_req = amount_left_to_write = size;
+
+ if (unlikely(amount_left_to_write == 0))
+ return -EIO;
+
+ offset = 0;
+ while (amount_left_to_write > 0) {
+
+ /* Queue a request for more data from the host */
+ bh = fsg->next_buffhd_to_fill;
+ if (bh->state == BUF_STATE_EMPTY && get_some_more) {
+
+ /* Figure out how much we want to get:
+ * Try to get the remaining amount.
+ * But don't get more than the buffer size.
+ * And don't try to go past the end of the file.
+ * If we're not at a page boundary,
+ * don't go past the next page.
+ * If this means getting 0, then we were asked
+ * to write past the end of file.
+ * Finally, round down to a block boundary. */
+ amount = min(amount_left_to_req, mod_data.buflen);
+
+ if (amount == 0) {
+ get_some_more = 0;
+ /* cry now */
+ continue;
+ }
+
+ /* Get the next buffer */
+ amount_left_to_req -= amount;
+ if (amount_left_to_req == 0)
+ get_some_more = 0;
+
+ /* amount is always divisible by 512, hence by
+ * the bulk-out maxpacket size */
+ bh->outreq->length = bh->bulk_out_intended_length =
+ amount;
+ bh->outreq->short_not_ok = 1;
+ start_transfer(fsg, fsg->bulk_out, bh->outreq,
+ &bh->outreq_busy, &bh->state);
+ fsg->next_buffhd_to_fill = bh->next;
+ continue;
+ }
+
+ /* Write the received data to the backing file */
+ bh = fsg->next_buffhd_to_drain;
+ if (bh->state == BUF_STATE_EMPTY && !get_some_more)
+ break; /* We stopped early */
+ if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
+ fsg->next_buffhd_to_drain = bh->next;
+ bh->state = BUF_STATE_EMPTY;
+
+ /* Did something go wrong with the transfer? */
+ if (bh->outreq->status != 0)
+ /* cry again, COMMUNICATION_FAILURE */
+ break;
+
+ amount = bh->outreq->actual;
+
+ /* Perform the write */
+ memcpy(data + offset, bh->buf, amount);
+
+ offset += amount;
+ if (signal_pending(current))
+ return -EINTR; /* Interrupted!*/
+ amount_left_to_write -= amount;
+ fsg->residue -= amount;
+
+ /* Did the host decide to stop early? */
+ if (bh->outreq->actual != bh->outreq->length) {
+ fsg->short_packet_received = 1;
+ break;
+ }
+ continue;
+ }
+
+ /* Wait for something to happen */
+ rc = sleep_thread(fsg);
+ if (rc)
+ return rc;
+ }
+
+ return -EIO;
+}
+
+static inline void utp_set_sense(struct fsg_dev *fsg, u16 code, u64 reply)
+{
+ UTP_CTX(fsg)->processed = true;
+ UTP_CTX(fsg)->sdinfo = reply & 0xFFFFFFFF;
+ UTP_CTX(fsg)->sdinfo_h = (reply >> 32) & 0xFFFFFFFF;
+ UTP_CTX(fsg)->sd = (UTP_SENSE_KEY << 16) | code;
+}
+
+static void utp_poll(struct fsg_dev *fsg)
+{
+ struct utp_context *ctx = UTP_CTX(fsg);
+ struct utp_user_data *uud = NULL;
+
+ mutex_lock(&ctx->lock);
+ if (!list_empty(&ctx->write))
+ uud = list_first_entry(&ctx->write, struct utp_user_data, link);
+ mutex_unlock(&ctx->lock);
+
+ if (uud) {
+ if (uud->data.flags & UTP_FLAG_STATUS) {
+ pr_debug("%s: exit with status %d\n", __func__,
+ uud->data.status);
+ UTP_SS_EXIT(fsg, uud->data.status);
+ } else {
+ pr_debug("%s: pass\n", __func__);
+ UTP_SS_PASS(fsg);
+ }
+ utp_user_data_free(uud);
+ } else {
+ pr_debug("%s: still busy...\n", __func__);
+ UTP_SS_BUSY(fsg, --ctx->counter);
+ }
+}
+
+static int utp_exec(struct fsg_dev *fsg,
+ char *command,
+ int cmdsize,
+ unsigned long long payload)
+{
+ struct utp_user_data *uud = NULL, *uud2r;
+ struct utp_context *ctx = UTP_CTX(fsg);
+
+ uud2r = utp_user_data_alloc(cmdsize + 1);
+ uud2r->data.flags = UTP_FLAG_COMMAND;
+ uud2r->data.payload = payload;
+ strncpy(uud2r->data.command, command, cmdsize);
+
+ mutex_lock(&ctx->lock);
+ list_add_tail(&uud2r->link, &ctx->read);
+ mutex_unlock(&ctx->lock);
+ wake_up(&ctx->wq);
+
+ if (command[0] == '!') /* there will be no response */
+ return 0;
+
+ WAIT_ACTIVITY(write);
+
+ mutex_lock(&ctx->lock);
+ if (!list_empty(&ctx->write)) {
+ uud = list_first_entry(&ctx->write, struct utp_user_data, link);
+#ifdef DEBUG
+ pr_info("UUD:\n\tFlags = %02X\n", uud->data.flags);
+ if (uud->data.flags & UTP_FLAG_DATA) {
+ pr_info("\tbufsize = %d\n", uud->data.bufsize);
+ print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_NONE,
+ 16, 2, uud->data.data, uud->data.bufsize, true);
+ }
+ if (uud->data.flags & UTP_FLAG_REPORT_BUSY)
+ pr_info("\tBUSY\n");
+#endif
+ }
+ mutex_unlock(&ctx->lock);
+
+ if (uud->data.flags & UTP_FLAG_DATA) {
+ memcpy(ctx->buffer, uud->data.data, uud->data.bufsize);
+ UTP_SS_SIZE(fsg, uud->data.bufsize);
+ utp_user_data_free(uud);
+ return 0;
+ }
+
+ if (uud->data.flags & UTP_FLAG_REPORT_BUSY) {
+ utp_user_data_free(uud);
+ ctx->counter = 0xFFFF;
+ UTP_SS_BUSY(fsg, ctx->counter);
+ return 0;
+ }
+
+ utp_user_data_free(uud);
+ UTP_SS_PASS(fsg);
+
+ return -1;
+}
+
+static int utp_send_status(struct fsg_dev *fsg)
+{
+ struct fsg_buffhd *bh;
+ u8 status = USB_STATUS_PASS;
+ struct bulk_cs_wrap *csw;
+ int rc;
+
+ /* Wait for the next buffer to become available */
+ bh = fsg->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(fsg);
+ if (rc)
+ return rc;
+ }
+
+ if (fsg->phase_error) {
+ DBG(fsg, "sending phase-error status\n");
+ status = USB_STATUS_PHASE_ERROR;
+
+ } else if ((UTP_CTX(fsg)->sd & 0xFFFF) != UTP_REPLY_PASS) {
+ status = USB_STATUS_FAIL;
+ }
+
+ csw = bh->buf;
+
+ /* Store and send the Bulk-only CSW */
+ csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG);
+ csw->Tag = fsg->tag;
+ csw->Residue = cpu_to_le32(fsg->residue);
+ csw->Status = status;
+
+ bh->inreq->length = USB_BULK_CS_WRAP_LEN;
+ bh->inreq->zero = 0;
+ start_transfer(fsg, fsg->bulk_in, bh->inreq,
+ &bh->inreq_busy, &bh->state);
+ fsg->next_buffhd_to_fill = bh->next;
+ return 0;
+}
+
+static int utp_handle_message(struct fsg_dev *fsg,
+ char *cdb_data,
+ int default_reply)
+{
+ struct utp_msg *m = (struct utp_msg *)cdb_data;
+ void *data = NULL;
+ int r;
+ struct utp_user_data *uud2r;
+ unsigned long long param;
+ unsigned long tag;
+
+ if (m->f0 != 0xF0)
+ return default_reply;
+
+ tag = get_be32((void *)&m->utp_msg_tag);
+ param = get_be64((void *)&m->param);
+ pr_debug("Type 0x%x, tag 0x%08lx, param %llx\n",
+ m->utp_msg_type, tag, param);
+
+ switch ((enum utp_msg_type)m->utp_msg_type) {
+
+ case UTP_POLL:
+ if (get_be64((void *)&m->param) == 1) {
+ pr_debug("%s: version request\n", __func__);
+ UTP_SS_EXIT(fsg, UTP_CTX(fsg)->utp_version);
+ break;
+ }
+ utp_poll(fsg);
+ break;
+ case UTP_EXEC:
+ pr_debug("%s: EXEC\n", __func__);
+ data = kzalloc(fsg->data_size, GFP_KERNEL);
+ utp_do_write(fsg, data, fsg->data_size);
+ utp_exec(fsg, data, fsg->data_size, param);
+ kfree(data);
+ break;
+ case UTP_GET:
+ pr_debug("%s: GET, %d bytes\n", __func__, fsg->data_size);
+ r = utp_do_read(fsg, UTP_CTX(fsg)->buffer, fsg->data_size);
+ UTP_SS_PASS(fsg);
+ break;
+ case UTP_PUT:
+ pr_debug("%s: PUT, %d bytes\n", __func__, fsg->data_size);
+ uud2r = utp_user_data_alloc(fsg->data_size);
+ uud2r->data.bufsize = fsg->data_size;
+ uud2r->data.flags = UTP_FLAG_DATA;
+ utp_do_write(fsg, uud2r->data.data, fsg->data_size);
+ /* don't know what will be written */
+ mutex_lock(&UTP_CTX(fsg)->lock);
+ list_add_tail(&uud2r->link, &UTP_CTX(fsg)->read);
+ mutex_unlock(&UTP_CTX(fsg)->lock);
+ wake_up(&UTP_CTX(fsg)->wq);
+ UTP_SS_PASS(fsg);
+ break;
+ }
+
+ utp_send_status(fsg);
+ return -1;
+}
+
diff --git a/drivers/usb/gadget/stmp_updater.h b/drivers/usb/gadget/stmp_updater.h
new file mode 100644
index 000000000000..e1248d1093c8
--- /dev/null
+++ b/drivers/usb/gadget/stmp_updater.h
@@ -0,0 +1,139 @@
+/*
+ * Freescale STMP378X UUT driver
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __STMP_UPDATER_H
+#define __STMP_UPDATER_H
+
+#include <linux/miscdevice.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+
+static int utp_init(struct fsg_dev *fsg);
+static void utp_exit(struct fsg_dev *fsg);
+static ssize_t utp_file_read(struct file *file,
+ char __user *buf,
+ size_t size,
+ loff_t *off);
+
+static ssize_t utp_file_write(struct file *file,
+ const char __user *buf,
+ size_t size,
+ loff_t *off);
+
+static struct utp_user_data *utp_user_data_alloc(size_t size);
+static void utp_user_data_free(struct utp_user_data *uud);
+static int utp_get_sense(struct fsg_dev *fsg);
+static int utp_do_read(struct fsg_dev *fsg, void *data, size_t size);
+static int utp_do_write(struct fsg_dev *fsg, void *data, size_t size);
+static inline void utp_set_sense(struct fsg_dev *fsg, u16 code, u64 reply);
+static int utp_handle_message(struct fsg_dev *fsg,
+ char *cdb_data,
+ int default_reply);
+
+#define UTP_REPLY_PASS 0
+#define UTP_REPLY_EXIT 0x8001
+#define UTP_REPLY_BUSY 0x8002
+#define UTP_REPLY_SIZE 0x8003
+#define UTP_SENSE_KEY 9
+
+#define UTP_MINOR 222
+/* MISC_DYNAMIC_MINOR would be better, but... */
+
+#define UTP_COMMAND_SIZE 80
+
+#define UTP_SS_EXIT(fsg, r) utp_set_sense(fsg, UTP_REPLY_EXIT, (u64)r)
+#define UTP_SS_PASS(fsg) utp_set_sense(fsg, UTP_REPLY_PASS, 0)
+#define UTP_SS_BUSY(fsg, r) utp_set_sense(fsg, UTP_REPLY_BUSY, (u64)r)
+#define UTP_SS_SIZE(fsg, r) utp_set_sense(fsg, UTP_REPLY_SIZE, (u64)r)
+
+#pragma pack(1)
+struct utp_msg {
+ u8 f0;
+ u8 utp_msg_type;
+ u32 utp_msg_tag;
+ union {
+ struct {
+ u32 param_lsb;
+ u32 param_msb;
+ };
+ u64 param;
+ };
+};
+
+enum utp_msg_type {
+ UTP_POLL = 0,
+ UTP_EXEC,
+ UTP_GET,
+ UTP_PUT,
+};
+
+static struct utp_context {
+ wait_queue_head_t wq;
+ struct mutex lock;
+ struct list_head read;
+ struct list_head write;
+ u32 sd, sdinfo, sdinfo_h; /* sense data */
+ int processed;
+ u8 *buffer;
+ u32 counter;
+ u64 utp_version;
+} utp_context;
+
+static const struct file_operations utp_fops = {
+ .open = nonseekable_open,
+ .read = utp_file_read,
+ .write = utp_file_write,
+};
+
+static struct miscdevice utp_dev = {
+ .minor = UTP_MINOR,
+ .name = "utp",
+ .fops = &utp_fops,
+};
+
+#define UTP_FLAG_COMMAND 0x00000001
+#define UTP_FLAG_DATA 0x00000002
+#define UTP_FLAG_STATUS 0x00000004
+#define UTP_FLAG_REPORT_BUSY 0x10000000
+struct utp_message {
+ u32 flags;
+ size_t size;
+ union {
+ struct {
+ u64 payload;
+ char command[1];
+ };
+ struct {
+ size_t bufsize;
+ u8 data[1];
+ };
+ u32 status;
+ };
+};
+
+struct utp_user_data {
+ struct list_head link;
+ struct utp_message data;
+};
+#pragma pack()
+
+static inline struct utp_context *UTP_CTX(struct fsg_dev *fsg)
+{
+ return (struct utp_context *)fsg->utp;
+}
+
+#endif /* __STMP_UPDATER_H */
+
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index f3a75a929e0a..4e539e30f1f6 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -42,9 +42,100 @@ config USB_EHCI_HCD
To compile this driver as a module, choose M here: the
module will be called ehci-hcd.
+config USB_EHCI_ARC
+ bool "Support for Freescale controller"
+ depends on USB_EHCI_HCD && (ARCH_MXC || ARCH_STMP3XXX)
+ ---help---
+ Some Freescale processors have an integrated High Speed
+ USBOTG controller, which supports EHCI host mode.
+
+ Say "y" here to add support for this controller
+ to the EHCI HCD driver.
+
+config USB_EHCI_ARC_H1
+ bool "Support for Host1 port on Freescale controller"
+ depends on USB_EHCI_ARC && (ARCH_MX27 || ARCH_MX3 || ARCH_MX51)
+ ---help---
+ Enable support for the USB Host1 port.
+
+config USB_EHCI_ARC_H2
+ bool "Support for Host2 port on Freescale controller"
+ depends on USB_EHCI_ARC && \
+ (ARCH_MX25 || ARCH_MX27 || ARCH_MX3 || ARCH_MX35 || ARCH_MX51)
+ ---help---
+ Enable support for the USB Host2 port.
+
+config USB_EHCI_ARC_OTG
+ bool "Support for DR host port on Freescale controller"
+ depends on USB_EHCI_ARC
+ default y
+ ---help---
+ Enable support for the USB OTG port in HS/FS Host mode.
+
+config USB_STATIC_IRAM
+ bool "Use IRAM for USB"
+ depends on USB_EHCI_ARC
+ ---help---
+ Enable this option to use IRAM instead of DRAM for USB
+ structures and buffers. This option will reduce bus
+ contention on systems with large (VGA+) framebuffer
+ devices and heavy USB activity. There are performance
+ penalties and usage restrictions when using this option.
+
+ If in doubt, say N.
+
+choice
+ prompt "Select transceiver for DR port"
+ depends on USB_EHCI_ARC_OTG
+ default USB_EHCI_FSL_1504 if ARCH_MX3
+ default USB_EHCI_FSL_1301 if ARCH_MX27
+ default USB_EHCI_FSL_UTMI if (ARCH_MX25 || ARCH_MX35 || ARCH_MX37 || ARCH_MX51 || ARCH_STMP3XXX)
+ ---help---
+ Choose the transceiver to use with the Freescale DR port.
+
+config USB_EHCI_FSL_MC13783
+ bool "Freescale MC13783"
+ depends on !MACH_MX25_3DS
+ ---help---
+ Enable support for the Full Speed Freescale MC13783 transceiver.
+
+ The mx27ads, mx31ads and mx32ads boards require modifications
+ to support this transceiver.
+
+config USB_EHCI_FSL_1301
+ bool "Philips ISP1301"
+ depends on !MACH_MX25_3DS
+ ---help---
+ Enable support for the Full Speed Philips ISP1301 transceiver.
+
+ This is the factory default for the mx27ads board.
+ The mx31ads and mx32ads boards require modifications
+ to support this transceiver.
+
+config USB_EHCI_FSL_1504
+ bool "Philips ISP1504"
+ depends on MACH_MX27ADS || MACH_MX31ADS || MACH_MX32ADS ||MACH_MX31_3DS
+ ---help---
+ Enable support for the High Speed Philips ISP1504 transceiver.
+
+ This is the factory default for the mx31ads and mx32ads boards.
+ The mx27ads board requires modifications to support this transceiver.
+
+config USB_EHCI_FSL_UTMI
+ bool "Internal UTMI"
+ depends on (ARCH_MX25 || ARCH_MX35 || ARCH_MX37 || ARCH_MX51 || ARCH_STMP3XXX)
+ ---help---
+ Enable support for the on-chip High Speed UTMI transceiver.
+
+ This is the factory default for the mx35ads board.
+
+endchoice
+
+
config USB_EHCI_ROOT_HUB_TT
bool "Root Hub Transaction Translators"
depends on USB_EHCI_HCD
+ default y if USB_EHCI_ARC
---help---
Some EHCI chips have vendor-specific extensions to integrate
transaction translators, so that no OHCI or UHCI companion
diff --git a/drivers/usb/host/ehci-arc.c b/drivers/usb/host/ehci-arc.c
new file mode 100644
index 000000000000..dd2929f472e8
--- /dev/null
+++ b/drivers/usb/host/ehci-arc.c
@@ -0,0 +1,635 @@
+/*
+ * (C) Copyright David Brownell 2000-2002
+ * Copyright (c) 2005 MontaVista Software
+ *
+ * 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.
+ *
+ * Ported to 834x by Randy Vinson <rvinson@mvista.com> using code provided
+ * by Hunter Wu.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/otg.h>
+
+#include "ehci-fsl.h"
+
+extern struct resource *otg_get_resources(void);
+
+#undef EHCI_PROC_PTC
+#ifdef EHCI_PROC_PTC /* /proc PORTSC:PTC support */
+/*
+ * write a PORTSC:PTC value to /proc/driver/ehci-ptc
+ * to put the controller into test mode.
+ */
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#define EFPSL 3 /* ehci fsl proc string length */
+
+static int ehci_fsl_proc_read(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ return 0;
+}
+
+static int ehci_fsl_proc_write(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ int ptc;
+ u32 portsc;
+ struct ehci_hcd *ehci = (struct ehci_hcd *) data;
+ char str[EFPSL] = {0};
+
+ if (count > EFPSL-1)
+ return -EINVAL;
+
+ if (copy_from_user(str, buffer, count))
+ return -EFAULT;
+
+ str[count] = '\0';
+
+ ptc = simple_strtoul(str, NULL, 0);
+
+ portsc = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ portsc &= ~(0xf << 16);
+ portsc |= (ptc << 16);
+ printk(KERN_INFO "PTC %x portsc %08x\n", ptc, portsc);
+
+ ehci_writel(ehci, portsc, &ehci->regs->port_status[0]);
+
+ return count;
+}
+
+static int ehci_testmode_init(struct ehci_hcd *ehci)
+{
+ struct proc_dir_entry *entry;
+
+ entry = create_proc_read_entry("driver/ehci-ptc", 0644, NULL,
+ ehci_fsl_proc_read, ehci);
+ if (!entry)
+ return -ENODEV;
+
+ entry->write_proc = ehci_fsl_proc_write;
+ return 0;
+}
+#else
+static int ehci_testmode_init(struct ehci_hcd *ehci)
+{
+ return 0;
+}
+#endif /* /proc PORTSC:PTC support */
+
+
+/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_fsl_probe - initialize FSL-based HCDs
+ * @drvier: Driver to be used for this HCD
+ * @pdev: USB Host Controller being probed
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller.
+ *
+ */
+static int usb_hcd_fsl_probe(const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq;
+ int retval;
+
+ pr_debug("initializing FSL-SOC USB Controller\n");
+
+ /* Need platform data for setup */
+ if (!pdata) {
+ dev_err(&pdev->dev,
+ "No platform data for %s.\n", pdev->dev.bus_id);
+ return -ENODEV;
+ }
+
+ /*
+ * This is a host mode driver, verify that we're supposed to be
+ * in host mode.
+ */
+ if (!((pdata->operating_mode == FSL_USB2_DR_HOST) ||
+ (pdata->operating_mode == FSL_USB2_MPH_HOST) ||
+ (pdata->operating_mode == FSL_USB2_DR_OTG))) {
+ dev_err(&pdev->dev,
+ "Non Host Mode configured for %s. "
+ "Wrong driver linked.\n", pdev->dev.bus_id);
+ return -ENODEV;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+#if defined(CONFIG_USB_OTG)
+ if (pdata->operating_mode == FSL_USB2_DR_OTG) {
+ res = otg_get_resources();
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ pdev->dev.bus_id);
+ return -ENODEV;
+ }
+ irq = res[1].start;
+ hcd->rsrc_start = res[0].start;
+ hcd->rsrc_len = res[0].end - res[0].start + 1;
+ } else
+#endif
+ {
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ pdev->dev.bus_id);
+ return -ENODEV;
+ }
+ irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto err2;
+ }
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+
+ if (hcd->regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto err3;
+ }
+ pdata->regs = hcd->regs;
+
+ /*
+ * do platform specific init: check the clock, grab/config pins, etc.
+ */
+ if (pdata->platform_init && pdata->platform_init(pdev)) {
+ retval = -ENODEV;
+ goto err3;
+ }
+
+ fsl_platform_set_host_mode(hcd);
+ hcd->power_budget = pdata->power_budget;
+
+ retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ if (retval != 0)
+ goto err4;
+
+ fsl_platform_set_vbus_power(pdata, 1);
+
+#ifdef CONFIG_USB_OTG
+ if (pdata->operating_mode == FSL_USB2_DR_OTG) {
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+ dbg("pdev=0x%p hcd=0x%p ehci=0x%p\n", pdev, hcd, ehci);
+
+ ehci->transceiver = otg_get_transceiver();
+ dbg("ehci->transceiver=0x%p\n", ehci->transceiver);
+
+ if (ehci->transceiver) {
+ retval = otg_set_host(ehci->transceiver,
+ &ehci_to_hcd(ehci)->self);
+ if (retval) {
+ if (ehci->transceiver)
+ put_device(ehci->transceiver->dev);
+ goto err4;
+ }
+ } else {
+ printk(KERN_ERR "can't find transceiver\n");
+ retval = -ENODEV;
+ goto err4;
+ }
+ }
+#endif
+
+ if (pdata->suspended) {
+ pdata->suspended = 0;
+ if (pdata->already_suspended)
+ pdata->already_suspended = 0;
+ }
+
+ fsl_platform_set_ahb_burst(hcd);
+ ehci_testmode_init(hcd_to_ehci(hcd));
+ return retval;
+
+err4:
+ iounmap(hcd->regs);
+err3:
+ if (pdata->operating_mode != FSL_USB2_DR_OTG)
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err2:
+ usb_put_hcd(hcd);
+err1:
+ dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval);
+ if (pdata->platform_uninit)
+ pdata->platform_uninit(pdata);
+ return retval;
+}
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_fsl_probe().
+ *
+ */
+static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
+ struct platform_device *pdev)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+ /* DDD shouldn't we turn off the power here? */
+ fsl_platform_set_vbus_power(pdata, 0);
+
+ if (ehci->transceiver) {
+ (void)otg_set_host(ehci->transceiver, 0);
+ put_device(ehci->transceiver->dev);
+ } else {
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ }
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+
+ /*
+ * do platform specific un-initialization:
+ * release iomux pins, etc.
+ */
+ if (pdata->platform_uninit)
+ pdata->platform_uninit(pdata);
+
+ iounmap(hcd->regs);
+}
+
+static void fsl_setup_phy(struct ehci_hcd *ehci,
+ enum fsl_usb2_phy_modes phy_mode, int port_offset)
+{
+ u32 portsc;
+
+ portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]);
+ portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW);
+
+ switch (phy_mode) {
+ case FSL_USB2_PHY_ULPI:
+ portsc |= PORT_PTS_ULPI;
+ break;
+ case FSL_USB2_PHY_SERIAL:
+ portsc |= PORT_PTS_SERIAL;
+ break;
+ case FSL_USB2_PHY_UTMI_WIDE:
+ portsc |= PORT_PTS_PTW;
+ /* fall through */
+ case FSL_USB2_PHY_UTMI:
+ portsc |= PORT_PTS_UTMI;
+ break;
+ case FSL_USB2_PHY_NONE:
+ break;
+ }
+ ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]);
+}
+
+/* called after powerup, by probe or system-pm "wakeup" */
+static int ehci_fsl_reinit(struct ehci_hcd *ehci)
+{
+ fsl_platform_usb_setup(ehci);
+ ehci_port_power(ehci, 0);
+ return 0;
+}
+
+/* called during probe() after chip reset completes */
+static int ehci_fsl_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+ struct fsl_usb2_platform_data *pdata;
+ pdata = hcd->self.controller->platform_data;
+
+ ehci->big_endian_desc = pdata->big_endian_desc;
+ ehci->big_endian_mmio = pdata->big_endian_mmio;
+
+ /* EHCI registers start at offset 0x100 */
+ ehci->caps = hcd->regs + 0x100;
+ ehci->regs = hcd->regs + 0x100 +
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+ retval = ehci_halt(ehci);
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ hcd->has_tt = 1;
+
+ ehci->sbrn = 0x20;
+
+ ehci_reset(ehci);
+
+ retval = ehci_fsl_reinit(ehci);
+ return retval;
+}
+
+static const struct hc_driver ehci_fsl_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Freescale On-Chip EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = FSL_PLATFORM_HC_FLAGS,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_fsl_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .start_port_reset = ehci_start_port_reset,
+};
+
+static int ehci_fsl_drv_probe(struct platform_device *pdev)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev);
+}
+
+static int ehci_fsl_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_hcd_fsl_remove(hcd, pdev);
+ return 0;
+}
+
+static void ehci_fsl_drv_shutdown(struct platform_device *pdev)
+{
+ usb_hcd_platform_shutdown(pdev);
+}
+
+#ifdef CONFIG_PM
+extern void usb_host_set_wakeup(struct device *wkup_dev, bool para);
+/* suspend/resume, section 4.3 */
+
+/* These routines rely on the bus (pci, platform, etc)
+ * to handle powerdown and wakeup, and currently also on
+ * transceivers that don't need any software attention to set up
+ * the right sort of wakeup.
+ *
+ * They're also used for turning on/off the port when doing OTG.
+ */
+static int ehci_fsl_drv_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ u32 tmp, port_status;
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+#ifdef DEBUG
+ u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE);
+ mode &= USBMODE_CM_MASK;
+ tmp = ehci_readl(ehci, hcd->regs + 0x140); /* usbcmd */
+
+ printk(KERN_DEBUG "%s('%s'): suspend=%d already_suspended=%d "
+ "mode=%d usbcmd %08x\n", __func__, pdata->name,
+ pdata->suspended, pdata->already_suspended, mode, tmp);
+#endif
+
+ /*
+ * If the controller is already suspended, then this must be a
+ * PM suspend. Remember this fact, so that we will leave the
+ * controller suspended at PM resume time.
+ */
+ if (pdata->suspended) {
+ pr_debug("%s: already suspended, leaving early\n", __func__);
+ pdata->already_suspended = 1;
+ return 0;
+ }
+
+ pr_debug("%s: suspending...\n", __func__);
+
+ printk(KERN_INFO "USB Host suspended\n");
+
+ port_status = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ hcd->state = HC_STATE_SUSPENDED;
+ pdev->dev.power.power_state = PMSG_SUSPEND;
+
+ /* ignore non-host interrupts */
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+ /* stop the controller */
+ tmp = ehci_readl(ehci, &ehci->regs->command);
+ tmp &= ~CMD_RUN;
+ ehci_writel(ehci, tmp, &ehci->regs->command);
+
+ /* save EHCI registers */
+ pdata->pm_command = ehci_readl(ehci, &ehci->regs->command);
+ pdata->pm_command &= ~CMD_RUN;
+ pdata->pm_status = ehci_readl(ehci, &ehci->regs->status);
+ pdata->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable);
+ pdata->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index);
+ pdata->pm_segment = ehci_readl(ehci, &ehci->regs->segment);
+ pdata->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list);
+ pdata->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next);
+ pdata->pm_configured_flag =
+ ehci_readl(ehci, &ehci->regs->configured_flag);
+ pdata->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]);
+
+ /* clear the W1C bits */
+ pdata->pm_portsc &= cpu_to_hc32(ehci, ~PORT_RWC_BITS);
+
+ pdata->suspended = 1;
+
+ if (!device_may_wakeup(&(pdev->dev))) {
+ /* clear PP to cut power to the port */
+ tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ tmp &= ~PORT_POWER;
+ ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
+ return 0;
+ }
+
+ /* device_may_wakeup */
+ if (!((ehci->transceiver) &&
+ (readl(hcd->regs + 0x1A4) & (1 << 8)))) {
+ /* enable remote wake up irq */
+ usb_host_set_wakeup(&(pdev->dev), true);
+
+ /* We CAN NOT enable wake up by connetion and disconnection
+ * concurrently */
+ tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ /* if there is no usb device connectted */
+ if (port_status & PORT_CONNECT) {
+ /* enable wake up by usb device disconnection */
+ tmp |= PORT_WKDISC_E;
+ tmp &= ~(PORT_WKOC_E | PORT_WKCONN_E);
+ } else {
+ /* enable wake up by usb device insertion */
+ tmp |= PORT_WKCONN_E;
+ tmp &= ~(PORT_WKOC_E | PORT_WKDISC_E);
+ }
+ ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
+
+ /* Set the port into suspend */
+ tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ tmp |= PORT_SUSPEND;
+ ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
+
+ /* Disable PHY clock */
+ tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ tmp |= (1 << 23);
+ ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
+ }
+
+ if (pdata->platform_suspend)
+ pdata->platform_suspend(pdata);
+
+ return 0;
+}
+
+static int ehci_fsl_drv_resume(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ u32 tmp;
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+ pr_debug("%s('%s'): suspend=%d already_suspended=%d\n", __func__,
+ pdata->name, pdata->suspended, pdata->already_suspended);
+
+ /*
+ * If the controller was already suspended at suspend time,
+ * then don't resume it now.
+ */
+ if (pdata->already_suspended) {
+ pr_debug("already suspended, leaving early\n");
+ pdata->already_suspended = 0;
+ return 0;
+ }
+
+ if (!pdata->suspended) {
+ pr_debug("not suspended, leaving early\n");
+ return 0;
+ }
+
+ if (device_may_wakeup(&(pdev->dev))) {
+ tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ if (tmp & (1 << 23)) {
+ tmp &= ~(1 << 23);
+ ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
+ msleep(10);
+ }
+ }
+
+ pdata->suspended = 0;
+
+ pr_debug("%s resuming...\n", __func__);
+
+ /* set host mode */
+ fsl_platform_set_host_mode(hcd);
+
+ if (pdata->platform_resume)
+ pdata->platform_resume(pdata);
+
+ /* restore EHCI registers */
+ ehci_writel(ehci, pdata->pm_command, &ehci->regs->command);
+ ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable);
+ ehci_writel(ehci, pdata->pm_frame_index, &ehci->regs->frame_index);
+ ehci_writel(ehci, pdata->pm_segment, &ehci->regs->segment);
+ ehci_writel(ehci, pdata->pm_frame_list, &ehci->regs->frame_list);
+ ehci_writel(ehci, pdata->pm_async_next, &ehci->regs->async_next);
+ ehci_writel(ehci, pdata->pm_configured_flag,
+ &ehci->regs->configured_flag);
+ ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]);
+
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+ hcd->state = HC_STATE_RUNNING;
+ pdev->dev.power.power_state = PMSG_ON;
+
+ tmp = ehci_readl(ehci, &ehci->regs->command);
+ tmp |= CMD_RUN;
+ ehci_writel(ehci, tmp, &ehci->regs->command);
+
+ usb_hcd_resume_root_hub(hcd);
+
+ printk(KERN_INFO "USB Host resumed\n");
+ return 0;
+}
+#endif /* CONFIG_USB_OTG */
+
+MODULE_ALIAS("fsl-ehci");
+
+static struct platform_driver ehci_fsl_driver = {
+ .probe = ehci_fsl_drv_probe,
+ .remove = ehci_fsl_drv_remove,
+ .shutdown = ehci_fsl_drv_shutdown,
+#ifdef CONFIG_PM
+ .suspend = ehci_fsl_drv_suspend,
+ .resume = ehci_fsl_drv_resume,
+#endif
+ .driver = {
+ .name = "fsl-ehci",
+ },
+};
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
index b5e59db53347..f3db681f5025 100644
--- a/drivers/usb/host/ehci-fsl.h
+++ b/drivers/usb/host/ehci-fsl.h
@@ -19,6 +19,9 @@
#define _EHCI_FSL_H
/* offsets for the non-ehci registers in the FSL SOC USB controller */
+#define FSL_SOC_USB_SBUSCFG 0x90
+#define FSL_SOC_USB_BURSTSIZE 0x160
+#define FSL_SOC_USB_TXFILLTUNING 0x164
#define FSL_SOC_USB_ULPIVP 0x170
#define FSL_SOC_USB_PORTSC1 0x184
#define PORT_PTS_MSK (3<<30)
@@ -26,8 +29,12 @@
#define PORT_PTS_ULPI (2<<30)
#define PORT_PTS_SERIAL (3<<30)
#define PORT_PTS_PTW (1<<28)
+#define PORT_PTS_PHCD (1<<23)
#define FSL_SOC_USB_PORTSC2 0x188
#define FSL_SOC_USB_USBMODE 0x1a8
+#define USBMODE_CM_HOST (3 << 0) /* controller mode: host */
+#define USBMODE_ES (1 << 2) /* (Big) Endian Select */
+
#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
@@ -35,4 +42,11 @@
#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */
#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */
#define SNOOP_SIZE_2GB 0x1e
+
+#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_STMP3XXX)
+#include <mach/fsl_usb.h>
+#elif CONFIG_PPC32
+#include <asm/fsl_usb.h>
+#endif
+
#endif /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 4725d15d096f..50b6fa08e987 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -253,8 +253,13 @@ static void end_unlink_async(struct ehci_hcd *ehci);
static void ehci_work(struct ehci_hcd *ehci);
#include "ehci-hub.c"
+#ifdef CONFIG_USB_STATIC_IRAM
+#include "ehci-mem-iram.c"
+#include "ehci-q-iram.c"
+#else
#include "ehci-mem.c"
#include "ehci-q.c"
+#endif
#include "ehci-sched.c"
/*-------------------------------------------------------------------------*/
@@ -589,6 +594,14 @@ static int ehci_run (struct usb_hcd *hcd)
#endif
}
+ /* For those designs that contain both host & device capability,
+ * the controller will default to an idle state and will need to
+ * be initialized to the desired operating mode after reset.
+ * For combination host/device controllers of FSL, SW need program
+ * For combination host/device controllers of FSL, SW need program
+ */
+ temp = readl(hcd->regs + 0x1a8);
+ writel(temp | (3 << 0), hcd->regs + 0x1a8);
// Philips, Intel, and maybe others need CMD_RUN before the
// root hub will detect new devices (why?); NEC doesn't
@@ -1014,6 +1027,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ehci_hcd_au1xxx_driver
#endif
+#ifdef CONFIG_USB_EHCI_ARC
+#include "ehci-arc.c"
+#define PLATFORM_DRIVER ehci_fsl_driver
+#endif
+
#ifdef CONFIG_PPC_PS3
#include "ehci-ps3.c"
#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 218f9660d7ee..6a607d240949 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -530,6 +530,37 @@ ehci_hub_descriptor (
desc->wHubCharacteristics = cpu_to_le16(temp);
}
+#ifdef CONFIG_USB_OTG
+static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ u32 status;
+
+ if (!port)
+ return -EINVAL;
+ port--;
+
+ /* start port reset before HNP protocol time out */
+ status = readl(&ehci->regs->port_status[port]);
+ if (!(status & PORT_CONNECT))
+ return -ENODEV;
+
+ /* khubd will finish the reset later */
+ if (ehci_is_TDI(ehci))
+ writel(PORT_RESET | (status & ~(PORT_CSC | PORT_PEC
+ | PORT_OCC)), &ehci->regs->port_status[port]);
+ else
+ writel(PORT_RESET, &ehci->regs->port_status[port]);
+
+ return 0;
+}
+#else
+static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port)
+{
+ return 0;
+}
+#endif /* CONFIG_USB_OTG */
+
/*-------------------------------------------------------------------------*/
static int ehci_hub_control (
diff --git a/drivers/usb/host/ehci-mem-iram.c b/drivers/usb/host/ehci-mem-iram.c
new file mode 100644
index 000000000000..66dd6e484fdd
--- /dev/null
+++ b/drivers/usb/host/ehci-mem-iram.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (c) 2001 by David Brownell
+ *
+ * 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.
+ */
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * There's basically three types of memory:
+ * - data used only by the HCD ... kmalloc is fine
+ * - async and periodic schedules, shared by HC and HCD ... these
+ * need to use dma_pool or dma_alloc_coherent
+ * - driver buffers, read/written by HC ... single shot DMA mapped
+ *
+ * There's also "register" data (e.g. PCI or SOC), which is memory mapped.
+ * No memory seen by this driver is pageable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* Allocate the key transfer structures from the previously allocated pool */
+#include <linux/smp_lock.h>
+
+bool use_iram_qtd;
+
+struct memDesc {
+ u32 start;
+ u32 end;
+ struct memDesc *next;
+} ;
+
+static u32 g_usb_pool_start;
+static s32 g_usb_pool_count;
+static u32 g_total_pages;
+static u32 g_alignment = 32;
+struct memDesc *g_allocated_desc;
+static spinlock_t g_usb_sema;
+static u32 g_debug_qtd_allocated;
+static u32 g_debug_qH_allocated;
+static int g_alloc_map;
+
+/*!
+ * usb_pool_initialize
+ *
+ * @param memPool start address of the pool
+ * @param poolSize memory pool size
+ * @param alignment alignment for example page alignmnet will be 4K
+ *
+ * @return 0 for success -1 for errors
+ */
+static int usb_pool_initialize(u32 memPool, u32 poolSize, u32 alignment)
+{
+ if (g_usb_pool_count) {
+ printk(KERN_INFO "usb_pool_initialize : already initialzed.\n");
+ return 0;
+ }
+
+ g_alignment = alignment;
+ if (g_alignment == 0) {
+ printk(KERN_INFO
+ "usb_pool_initialize : g_alignment can not be zero.\n");
+ g_alignment = 32;
+ }
+
+ g_total_pages = poolSize / g_alignment;
+ g_usb_pool_start = (u32) memPool;
+
+ g_allocated_desc = kmalloc(sizeof(struct memDesc), GFP_KERNEL);
+ if (!g_allocated_desc) {
+ printk(KERN_ALERT "usb_pool_initialize : kmalloc failed \n");
+ return (-1);
+ }
+
+ g_allocated_desc->start = 0;
+ g_allocated_desc->end = 0;
+ g_allocated_desc->next = NULL;
+
+ spin_lock_init(&g_usb_sema);
+ g_usb_pool_count++;
+ return (0);
+}
+
+static void usb_pool_deinit()
+{
+ if (--g_usb_pool_count < 0)
+ g_usb_pool_count = 0;
+}
+
+/*!
+ * usb_malloc
+ *
+ * @param size memory pool size
+ *
+ * @return physical address, 0 for error
+ */
+static u32 usb_malloc(u32 size, gfp_t mem_flags)
+{
+ unsigned long flags;
+ struct memDesc *prevDesc = NULL;
+ struct memDesc *nextDesc = NULL;
+ struct memDesc *currentDesc = NULL;
+ u32 pages = (size + g_alignment - 1) / g_alignment;
+
+ if ((size == 0) || (pages > g_total_pages))
+ return 0;
+
+ currentDesc = kmalloc(sizeof(struct memDesc), mem_flags);
+ if (!currentDesc) {
+ printk(KERN_ALERT "usb_malloc: kmalloc failed \n");
+ return 0;
+ }
+
+ spin_lock_irqsave(&g_usb_sema, flags);
+
+ /* Create the first Allocated descriptor */
+ if (!g_allocated_desc->next) {
+ g_allocated_desc->next = currentDesc;
+ currentDesc->start = 0;
+ currentDesc->end = pages;
+ currentDesc->next = NULL;
+ spin_unlock_irqrestore(&g_usb_sema, flags);
+ return (g_usb_pool_start + currentDesc->start * g_alignment);
+ }
+
+ /* Find the free spot */
+ prevDesc = g_allocated_desc;
+ while (prevDesc->next) {
+ nextDesc = prevDesc->next;
+ if (pages <= nextDesc->start - prevDesc->end) {
+ currentDesc->start = prevDesc->end;
+ currentDesc->end = currentDesc->start + pages;
+ currentDesc->next = nextDesc;
+ prevDesc->next = currentDesc;
+ break;
+ }
+ prevDesc = nextDesc;
+ }
+
+ /* Do not find the free spot inside the chain, append to the end */
+ if (!prevDesc->next) {
+ if (pages > (g_total_pages - prevDesc->end)) {
+ spin_unlock_irqrestore(&g_usb_sema, flags);
+ kfree(currentDesc);
+ return 0;
+ } else {
+ currentDesc->start = prevDesc->end;
+ currentDesc->end = currentDesc->start + pages;
+ currentDesc->next = NULL;
+ prevDesc->next = currentDesc;
+ }
+ }
+
+ spin_unlock_irqrestore(&g_usb_sema, flags);
+ return (g_usb_pool_start + currentDesc->start * g_alignment);
+}
+
+/*!
+ * usb_free
+ *
+ * @param physical physical address try to free
+ *
+ */
+static void usb_free(u32 physical)
+{
+ unsigned long flags;
+ struct memDesc *prevDesc = NULL;
+ struct memDesc *nextDesc = NULL;
+ u32 pages = (physical - g_usb_pool_start) / g_alignment;
+
+ /* Protect the memory pool data structures. */
+ spin_lock_irqsave(&g_usb_sema, flags);
+
+ prevDesc = g_allocated_desc;
+ while (prevDesc->next) {
+ nextDesc = prevDesc->next;
+ if (nextDesc->start == pages) {
+ prevDesc->next = nextDesc->next;
+ kfree(nextDesc);
+ break;
+ }
+ prevDesc = prevDesc->next;
+ }
+ /* All done with memory pool data structures. */
+ spin_unlock_irqrestore(&g_usb_sema, flags);
+}
+
+static int address_to_buffer(struct ehci_hcd *ehci, int address)
+{
+ int i;
+
+ for (i = 0; i < IRAM_NTD; i++) {
+ if (ehci->usb_address[i] == address)
+ return i;
+ }
+ return IRAM_NTD;
+}
+
+static void use_buffer(struct ehci_hcd *ehci, int address)
+{
+ int i;
+
+ for (i = 0; i < IRAM_NTD; i++) {
+ if (ehci->usb_address[i] == address)
+ return;
+ }
+
+ if (ehci->usb_address[0] == 0) {
+ ehci->usb_address[0] = address;
+ printk(KERN_INFO "usb_address[0] %x\n", address);
+ return;
+ } else if (ehci->usb_address[1] == 0) {
+ ehci->usb_address[1] = address;
+ printk(KERN_INFO "usb_address[1] %x\n", address);
+ return;
+ } else
+ printk(KERN_ALERT "qh_make run out of iRAM, already be used");
+}
+
+static u32 alloc_iram_buf(void)
+{
+ int i;
+
+ for (i = 0; i < IRAM_NTD; i++) {
+ if (!(g_alloc_map & (1 << i))) {
+ g_alloc_map |= (1 << i);
+ return USB_IRAM_BASE_ADDR + i * (IRAM_TD_SIZE * 2);
+ }
+ }
+ panic("Out of IRAM buffers\n");
+}
+
+void free_iram_buf(u32 buf)
+{
+ int i = (buf - USB_IRAM_BASE_ADDR) / (IRAM_TD_SIZE * 2);
+
+ g_alloc_map &= ~(1 << i);
+}
+
+static inline void ehci_qtd_init(struct ehci_hcd *ehci, struct ehci_qtd *qtd,
+ dma_addr_t dma)
+{
+ memset(qtd, 0, sizeof *qtd);
+ qtd->qtd_dma = dma;
+ qtd->hw_token = cpu_to_le32(QTD_STS_HALT);
+ qtd->hw_next = EHCI_LIST_END(ehci);
+ qtd->hw_alt_next = EHCI_LIST_END(ehci);
+ INIT_LIST_HEAD(&qtd->qtd_list);
+}
+
+static struct ehci_qtd *ehci_qtd_alloc(struct ehci_hcd *ehci, gfp_t flags)
+{
+ struct ehci_qtd *qtd;
+ dma_addr_t dma;
+
+ if (use_iram_qtd) {
+ dma = usb_malloc(sizeof(struct ehci_qtd), flags);
+ if (dma != 0)
+ qtd = (struct ehci_qtd *)IO_ADDRESS(dma);
+ else
+ qtd = dma_pool_alloc(ehci->qtd_pool, flags, &dma);
+ }
+ else
+ qtd = dma_pool_alloc(ehci->qtd_pool, flags, &dma);
+
+ if (qtd != NULL) {
+ ehci_qtd_init(ehci, qtd, dma);
+ ++g_debug_qtd_allocated;
+ } else {
+ panic
+ ("out of i-ram for qtd allocation g_debug_qtd_allocated %d \
+ size%d \n", g_debug_qtd_allocated,
+ sizeof(struct ehci_qtd));
+ }
+ return qtd;
+}
+
+static inline void ehci_qtd_free(struct ehci_hcd *ehci, struct ehci_qtd *qtd)
+{
+ if ((qtd->qtd_dma & (USB_IRAM_BASE_ADDR & 0xFFF00000)) ==
+ (USB_IRAM_BASE_ADDR & 0xFFF00000))
+ usb_free(qtd->qtd_dma);
+ else
+ dma_pool_free(ehci->qtd_pool, qtd, qtd->qtd_dma);
+ --g_debug_qtd_allocated;
+}
+
+static void qh_destroy(struct ehci_qh *qh)
+{
+ struct ehci_hcd *ehci = qh->ehci;
+
+ /* clean qtds first, and know this is not linked */
+ if (!list_empty(&qh->qtd_list) || qh->qh_next.ptr) {
+ ehci_dbg(ehci, "unused qh not empty!\n");
+ BUG();
+ }
+ if (qh->dummy)
+ ehci_qtd_free(ehci, qh->dummy);
+ int i;
+ for (i = 0; i < IRAM_NTD; i++) {
+ if (ehci->usb_address[i] == (qh->hw_info1 & 0x7F))
+ ehci->usb_address[i] = 0;
+ }
+
+ if ((qh->qh_dma & (USB_IRAM_BASE_ADDR & 0xFFF00000)) ==
+ (USB_IRAM_BASE_ADDR & 0xFFF00000))
+ usb_free(qh->qh_dma);
+ else
+ dma_pool_free(ehci->qh_pool, qh, qh->qh_dma);
+ --g_debug_qH_allocated;
+}
+
+static struct ehci_qh *ehci_qh_alloc(struct ehci_hcd *ehci, gfp_t flags)
+{
+ struct ehci_qh *qh;
+ dma_addr_t dma;
+
+ dma = usb_malloc(sizeof(struct ehci_qh), flags);
+ if (dma != 0)
+ qh = (struct ehci_qh *)IO_ADDRESS(dma);
+ else
+ qh = (struct ehci_qh *)
+ dma_pool_alloc(ehci->qh_pool, flags, &dma);
+ ++g_debug_qH_allocated;
+ if (qh == NULL) {
+ panic("run out of i-ram for qH allocation\n");
+ return qh;
+ }
+
+ memset(qh, 0, sizeof *qh);
+ qh->refcount = 1;
+ qh->ehci = ehci;
+ qh->qh_dma = dma;
+ INIT_LIST_HEAD(&qh->qtd_list);
+
+ /* dummy td enables safe urb queuing */
+ qh->dummy = ehci_qtd_alloc(ehci, flags);
+ if (qh->dummy == NULL) {
+ ehci_dbg(ehci, "no dummy td\n");
+ dma_pool_free(ehci->qh_pool, qh, qh->qh_dma);
+ qh = NULL;
+ }
+ return qh;
+}
+
+/* to share a qh (cpu threads, or hc) */
+static inline struct ehci_qh *qh_get(struct ehci_qh *qh)
+{
+ WARN_ON(!qh->refcount);
+ qh->refcount++;
+ return qh;
+}
+
+static inline void qh_put(struct ehci_qh *qh)
+{
+ if (!--qh->refcount)
+ qh_destroy(qh);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* The queue heads and transfer descriptors are managed from pools tied
+ * to each of the "per device" structures.
+ * This is the initialisation and cleanup code.
+ */
+
+static void ehci_mem_cleanup(struct ehci_hcd *ehci)
+{
+ if (ehci->async)
+ qh_put(ehci->async);
+ ehci->async = NULL;
+
+ /* DMA consistent memory and pools */
+ if (ehci->qtd_pool)
+ dma_pool_destroy(ehci->qtd_pool);
+ ehci->qtd_pool = NULL;
+
+ if (ehci->qh_pool) {
+ dma_pool_destroy(ehci->qh_pool);
+ ehci->qh_pool = NULL;
+ }
+
+ if (ehci->itd_pool)
+ dma_pool_destroy(ehci->itd_pool);
+ ehci->itd_pool = NULL;
+
+ if (ehci->sitd_pool)
+ dma_pool_destroy(ehci->sitd_pool);
+ ehci->sitd_pool = NULL;
+
+ if (ehci->periodic)
+ dma_free_coherent(ehci_to_hcd(ehci)->self.controller,
+ ehci->periodic_size * sizeof(u32),
+ ehci->periodic, ehci->periodic_dma);
+ ehci->periodic = NULL;
+
+ if (ehci->iram_buffer[0])
+ free_iram_buf(ehci->iram_buffer[0]);
+ if (ehci->iram_buffer[1])
+ free_iram_buf(ehci->iram_buffer[1]);
+
+ /* shadow periodic table */
+ kfree(ehci->pshadow);
+ ehci->pshadow = NULL;
+ usb_pool_deinit();
+}
+
+/* remember to add cleanup code (above) if you add anything here */
+static int ehci_mem_init(struct ehci_hcd *ehci, gfp_t flags)
+{
+ int i;
+ g_usb_pool_count = 0;
+ g_debug_qtd_allocated = 0;
+ g_debug_qH_allocated = 0;
+ g_alloc_map = 0;
+
+ if (cpu_is_mx37())
+ use_iram_qtd = 0;
+ else
+ use_iram_qtd = 1;
+
+ usb_pool_initialize(USB_IRAM_BASE_ADDR + IRAM_TD_SIZE * IRAM_NTD * 2,
+ USB_IRAM_SIZE - IRAM_TD_SIZE * IRAM_NTD * 2, 32);
+
+ if (!ehci->iram_buffer[0]) {
+ ehci->iram_buffer[0] = alloc_iram_buf();
+ ehci->iram_buffer_v[0] = IO_ADDRESS(ehci->iram_buffer[0]);
+ ehci->iram_buffer[1] = alloc_iram_buf();
+ ehci->iram_buffer_v[1] = IO_ADDRESS(ehci->iram_buffer[1]);
+ }
+
+ /* QTDs for control/bulk/intr transfers */
+ ehci->qtd_pool = dma_pool_create("ehci_qtd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof(struct ehci_qtd),
+ 32/* byte alignment (for hw parts) */
+ , 4096 /* can't cross 4K */);
+ if (!ehci->qtd_pool)
+ goto fail;
+
+ /* QHs for control/bulk/intr transfers */
+ ehci->qh_pool = dma_pool_create("ehci_qh",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof(struct ehci_qh),
+ 32 /* byte alignment (for hw parts) */ ,
+ 4096 /* can't cross 4K */);
+ if (!ehci->qh_pool)
+ goto fail;
+
+ ehci->async = ehci_qh_alloc(ehci, flags);
+ if (!ehci->async)
+ goto fail;
+
+ /* ITD for high speed ISO transfers */
+ ehci->itd_pool = dma_pool_create("ehci_itd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof(struct ehci_itd),
+ 32/* byte alignment (for hw parts) */
+ , 4096 /* can't cross 4K */);
+ if (!ehci->itd_pool)
+ goto fail;
+
+ /* SITD for full/low speed split ISO transfers */
+ ehci->sitd_pool = dma_pool_create("ehci_sitd",
+ ehci_to_hcd(ehci)->self.controller,
+ sizeof(struct ehci_sitd),
+ 32/* byte alignment (for hw parts) */
+ , 4096 /* can't cross 4K */);
+ if (!ehci->sitd_pool)
+ goto fail;
+
+ ehci->periodic = (__le32 *)
+ dma_alloc_coherent(ehci_to_hcd(ehci)->self.controller,
+ ehci->periodic_size * sizeof(__le32),
+ &ehci->periodic_dma, 0);
+
+ if (ehci->periodic == NULL)
+ goto fail;
+
+ for (i = 0; i < ehci->periodic_size; i++)
+ ehci->periodic[i] = EHCI_LIST_END(ehci);
+
+ /* software shadow of hardware table */
+ ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags);
+ if (ehci->pshadow != NULL)
+ return 0;
+
+fail:
+ ehci_dbg(ehci, "couldn't init memory\n");
+ ehci_mem_cleanup(ehci);
+ return -ENOMEM;
+}
diff --git a/drivers/usb/host/ehci-q-iram.c b/drivers/usb/host/ehci-q-iram.c
new file mode 100644
index 000000000000..318888563380
--- /dev/null
+++ b/drivers/usb/host/ehci-q-iram.c
@@ -0,0 +1,1345 @@
+/*
+ * Copyright (C) 2001-2004 by David Brownell
+ *
+ * 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.
+ */
+#undef EHCI_NO_ERR_COUNT
+static size_t g_iram_size = IRAM_TD_SIZE;
+
+/* this file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI hardware queue manipulation ... the core. QH/QTD manipulation.
+ *
+ * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd"
+ * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned
+ * buffers needed for the larger number). We use one QH per endpoint, queue
+ * multiple urbs (all three types) per endpoint. URBs may need several qtds.
+ *
+ * ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
+ * interrupts) needs careful scheduling. Performance improvements can be
+ * an ongoing challenge. That's in "ehci-sched.c".
+ *
+ * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
+ * or otherwise through transaction translators (TTs) in USB 2.0 hubs using
+ * (b) special fields in qh entries or (c) split iso entries. TTs will
+ * buffer low/full speed data so the host collects it at high speed.
+ */
+
+/*-------------------------------------------------------------------------*/
+/* fill a qtd, returning how much of the buffer we were able to queue up */
+static int qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf,
+ size_t len, int token, int maxpacket)
+{
+ int i, count;
+ u64 addr = buf;
+ struct urb *urb = qtd->urb;
+
+ if (usb_pipebulk(urb->pipe) &&
+ (address_to_buffer(ehci, usb_pipedevice(urb->pipe)) != 2)) {
+ urb->use_iram = 1;
+ qtd->buffer_offset = (size_t) (buf - urb->transfer_dma);
+ token |= QTD_IOC;
+ if (usb_pipeout(urb->pipe)) {
+ addr = ehci->iram_buffer[address_to_buffer(ehci,
+ usb_pipedevice(urb->pipe))];
+ } else if (usb_pipein(urb->pipe)) {
+ addr = ehci->iram_buffer[address_to_buffer(ehci,
+ usb_pipedevice(urb->pipe))] +
+ g_iram_size;
+ }
+ } else {
+ urb->use_iram = 0;
+ addr = buf;
+ }
+ len = min(g_iram_size, len);
+
+ /* one buffer entry per 4K ... first might be short or unaligned */
+ qtd->hw_buf[0] = cpu_to_hc32(ehci, (u32) addr);
+ qtd->hw_buf_hi[0] = cpu_to_hc32(ehci, (u32) (addr >> 32));
+ count = 0x1000 - (buf & 0x0fff); /* rest of that page */
+ if (likely(len < count)) /* ... iff needed */
+ count = len;
+ else {
+ buf += 0x1000;
+ buf &= ~0x0fff;
+
+ /* per-qtd limit: from 16K to 20K (best alignment) */
+ for (i = 1; count < len && i < 5; i++) {
+ addr = buf;
+ qtd->hw_buf[i] = cpu_to_hc32(ehci, (u32) addr);
+ qtd->hw_buf_hi[i] =
+ cpu_to_hc32(ehci, (u32) (addr >> 32));
+ buf += 0x1000;
+ if ((count + 0x1000) < len)
+ count += 0x1000;
+ else
+ count = len;
+ }
+
+ /* short packets may only terminate transfers */
+ if (count != len)
+ count -= (count % maxpacket);
+ }
+ qtd->hw_token = cpu_to_hc32(ehci, (count << 16) | token);
+ qtd->length = count;
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void
+qh_update(struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
+{
+ /* writes to an active overlay are unsafe */
+ BUG_ON(qh->qh_state != QH_STATE_IDLE);
+
+ qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
+ qh->hw_alt_next = EHCI_LIST_END(ehci);
+
+ /* Except for control endpoints, we make hardware maintain data
+ * toggle (like OHCI) ... here (re)initialize the toggle in the QH,
+ * and set the pseudo-toggle in udev. Only usb_clear_halt() will
+ * ever clear it.
+ */
+ if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
+ unsigned is_out, epnum;
+
+ is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
+ epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
+ if (unlikely(!usb_gettoggle(qh->dev, epnum, is_out))) {
+ qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
+ usb_settoggle(qh->dev, epnum, is_out, 1);
+ }
+ }
+
+ /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
+ wmb();
+ qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
+}
+
+/* if it weren't for a common silicon quirk (writing the dummy into the qh
+ * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault
+ * recovery (including urb dequeue) would need software changes to a QH...
+ */
+static void qh_refresh(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ struct ehci_qtd *qtd;
+
+ if (list_empty(&qh->qtd_list))
+ qtd = qh->dummy;
+ else {
+ qtd = list_entry(qh->qtd_list.next, struct ehci_qtd, qtd_list);
+ /* first qtd may already be partially processed */
+ if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw_current)
+ qtd = NULL;
+ }
+
+ if (qtd)
+ qh_update(ehci, qh, qtd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int qtd_copy_status(struct ehci_hcd *ehci,
+ struct urb *urb, size_t length, u32 token)
+{
+ int status = -EINPROGRESS;
+
+ /* count IN/OUT bytes, not SETUP (even short packets) */
+ if (likely(QTD_PID(token) != 2))
+ urb->actual_length += length - QTD_LENGTH(token);
+
+ /* don't modify error codes */
+ if (unlikely(urb->unlinked))
+ return status;
+
+ /* force cleanup after short read; not always an error */
+ if (unlikely(IS_SHORT_READ(token)))
+ status = -EREMOTEIO;
+
+ /* serious "can't proceed" faults reported by the hardware */
+ if (token & QTD_STS_HALT) {
+ if (token & QTD_STS_BABBLE) {
+ /* FIXME "must" disable babbling device's port too */
+ status = -EOVERFLOW;
+ } else if (token & QTD_STS_MMF) {
+ /* fs/ls interrupt xfer missed the complete-split */
+ status = -EPROTO;
+ } else if (token & QTD_STS_DBE) {
+ status = (QTD_PID(token) == 1) /* IN ? */
+ ? -ENOSR /* hc couldn't read data */
+ : -ECOMM; /* hc couldn't write data */
+ } else if (token & QTD_STS_XACT) {
+ /* timeout, bad crc, wrong PID, etc; retried */
+ if (QTD_CERR(token))
+ status = -EPIPE;
+ else {
+ ehci_dbg(ehci, "devpath %s ep%d%s 3strikes\n",
+ urb->dev->devpath,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out");
+ status = -EPROTO;
+ }
+ /* CERR nonzero + no errors + halt --> stall */
+ } else if (QTD_CERR(token))
+ status = -EPIPE;
+ else /* unknown */
+ status = -EPROTO;
+
+ ehci_vdbg(ehci,
+ "dev%d ep%d%s qtd token %08x --> status %d\n",
+ usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out", token, status);
+
+ /* if async CSPLIT failed, try cleaning out the TT buffer */
+ if (status != -EPIPE && urb->dev->tt && !usb_pipeint(urb->pipe)
+ && ((token & QTD_STS_MMF) != 0 || QTD_CERR(token) == 0)
+ && (!ehci_is_TDI(ehci)
+ || urb->dev->tt->hub !=
+ ehci_to_hcd(ehci)->self.root_hub)) {
+#ifdef DEBUG
+ struct usb_device *tt = urb->dev->tt->hub;
+ dev_dbg(&tt->dev,
+ "clear tt buffer port %d, a%d ep%d t%08x\n",
+ urb->dev->ttport, urb->dev->devnum,
+ usb_pipeendpoint(urb->pipe), token);
+#endif /* DEBUG */
+ /* REVISIT ARC-derived cores don't clear the root
+ * hub TT buffer in this way...
+ */
+ usb_hub_tt_clear_buffer(urb->dev, urb->pipe);
+ }
+ }
+
+ return status;
+}
+
+static void
+ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
+__releases(ehci->lock) __acquires(ehci->lock)
+{
+ if (likely(urb->hcpriv != NULL)) {
+ struct ehci_qh *qh = (struct ehci_qh *)urb->hcpriv;
+
+ /* S-mask in a QH means it's an interrupt urb */
+ if ((qh->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) {
+
+ /* ... update hc-wide periodic stats (for usbfs) */
+ ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
+ }
+ qh_put(qh);
+ }
+
+ if (unlikely(urb->unlinked)) {
+ COUNT(ehci->stats.unlink);
+ } else {
+ /* report non-error and short read status as zero */
+ if (status == -EINPROGRESS || status == -EREMOTEIO)
+ status = 0;
+ COUNT(ehci->stats.complete);
+ }
+
+#ifdef EHCI_URB_TRACE
+ ehci_dbg(ehci,
+ "%s %s urb %p ep%d%s status %d len %d/%d\n",
+ __func__, urb->dev->devpath, urb,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out",
+ status, urb->actual_length, urb->transfer_buffer_length);
+#endif
+
+ /* complete() can reenter this HCD */
+ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+ spin_unlock(&ehci->lock);
+ usb_hcd_giveback_urb(ehci_to_hcd(ehci), urb, status);
+ spin_lock(&ehci->lock);
+}
+
+static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh);
+static void unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh);
+
+static void intr_deschedule(struct ehci_hcd *ehci, struct ehci_qh *qh);
+static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh);
+
+/*
+ * Process and free completed qtds for a qh, returning URBs to drivers.
+ * Chases up to qh->hw_current. Returns number of completions called,
+ * indicating how much "real" work we did.
+ */
+static unsigned qh_completions(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ struct ehci_qtd *last = NULL, *end = qh->dummy;
+ struct list_head *entry, *tmp;
+ int last_status = -EINPROGRESS;
+ int stopped;
+ unsigned count = 0;
+ u8 state;
+ __le32 halt = HALT_BIT(ehci);
+ __hc32 temp_hw_qtd_next = 0;
+
+ if (unlikely(list_empty(&qh->qtd_list)))
+ return count;
+
+ /* completions (or tasks on other cpus) must never clobber HALT
+ * till we've gone through and cleaned everything up, even when
+ * they add urbs to this qh's queue or mark them for unlinking.
+ *
+ * NOTE: unlinking expects to be done in queue order.
+ */
+ state = qh->qh_state;
+ qh->qh_state = QH_STATE_COMPLETING;
+ stopped = (state == QH_STATE_IDLE);
+
+ /* remove de-activated QTDs from front of queue.
+ * after faults (including short reads), cleanup this urb
+ * then let the queue advance.
+ * if queue is stopped, handles unlinks.
+ */
+ list_for_each_safe(entry, tmp, &qh->qtd_list) {
+ struct ehci_qtd *qtd;
+ struct urb *urb;
+ struct ehci_qtd *qtd2;
+ struct urb *urb2;
+
+ u32 token = 0;
+
+ qtd = list_entry(entry, struct ehci_qtd, qtd_list);
+ urb = qtd->urb;
+
+ /* clean up any state from previous QTD ... */
+ if (last) {
+ if (likely(last->urb != urb)) {
+ ehci_urb_done(ehci, last->urb, last_status);
+ count++;
+ last_status = -EINPROGRESS;
+ }
+ ehci_qtd_free(ehci, last);
+ last = NULL;
+ }
+
+ /* ignore urbs submitted during completions we reported */
+ if (qtd == end)
+ break;
+
+ /* hardware copies qtd out of qh overlay */
+ rmb();
+ token = hc32_to_cpu(ehci, qtd->hw_token);
+
+ /* always clean up qtds the hc de-activated */
+ if ((token & QTD_STS_ACTIVE) == 0) {
+
+ /* on STALL, error, and short reads this urb must
+ * complete and all its qtds must be recycled.
+ */
+ if ((token & QTD_STS_HALT) != 0) {
+ stopped = 1;
+
+ /* magic dummy for some short reads; qh won't advance.
+ * that silicon quirk can kick in with this dummy too.
+ *
+ * other short reads won't stop the queue, including
+ * control transfers (status stage handles that) or
+ * most other single-qtd reads ... the queue stops if
+ * URB_SHORT_NOT_OK was set so the driver submitting
+ * the urbs could clean it up.
+ */
+ } else if (IS_SHORT_READ(token)
+ && !(qtd->hw_alt_next & EHCI_LIST_END(ehci))) {
+ if (urb->use_iram && usb_pipein(urb->pipe)) {
+ if (urb->transfer_buffer == NULL) {
+ memcpy(phys_to_virt
+ (urb->transfer_dma) +
+ qtd->buffer_offset,
+ ehci->
+ iram_buffer_v
+ [address_to_buffer
+ (ehci,
+ usb_pipedevice(urb->
+ pipe))]
+ + g_iram_size,
+ min(g_iram_size,
+ qtd->length));
+ } else {
+ memcpy(urb->transfer_buffer +
+ qtd->buffer_offset,
+ ehci->
+ iram_buffer_v
+ [address_to_buffer
+ (ehci,
+ usb_pipedevice(urb->
+ pipe))]
+ + g_iram_size,
+ min(g_iram_size,
+ qtd->length));
+ }
+ }
+ stopped = 1;
+ goto halt;
+ } else if (urb->use_iram && (!qtd->last_one)
+ && usb_pipeout(urb->pipe)) {
+ ehci->
+ iram_in_use[address_to_buffer
+ (ehci,
+ usb_pipedevice(urb->pipe))] =
+ 1;
+ qtd2 =
+ list_entry(tmp, struct ehci_qtd, qtd_list);
+ if (urb->transfer_buffer == NULL) {
+ memcpy(ehci->
+ iram_buffer_v[address_to_buffer
+ (ehci,
+ usb_pipedevice
+ (urb->pipe))],
+ phys_to_virt(urb->transfer_dma) +
+ qtd->buffer_offset + qtd->length,
+ min(g_iram_size, qtd2->length));
+ } else {
+ memcpy(ehci->
+ iram_buffer_v[address_to_buffer
+ (ehci,
+ usb_pipedevice
+ (urb->pipe))],
+ urb->transfer_buffer +
+ qtd->buffer_offset + qtd->length,
+ min(g_iram_size, qtd2->length));
+ }
+ temp_hw_qtd_next =
+ QTD_NEXT(ehci, qtd->hw_next) & 0xFFFFFFFE;
+ } else if (urb->use_iram && (qtd->last_one)
+ && usb_pipeout(urb->pipe)) {
+ urb->use_iram = 0;
+ qtd2 =
+ list_entry(tmp, struct ehci_qtd, qtd_list);
+ if (tmp != &qh->qtd_list) {
+ urb2 = qtd2->urb;
+ if (urb2 && urb2->use_iram == 1) {
+ ehci->
+ iram_in_use
+ [address_to_buffer
+ (ehci,
+ usb_pipedevice(urb->
+ pipe))] =
+ 1;
+ if (urb2->transfer_buffer ==
+ NULL) {
+ memcpy(ehci->
+ iram_buffer_v
+ [address_to_buffer
+ (ehci,
+ usb_pipedevice
+ (urb->pipe))],
+ phys_to_virt
+ (urb2->
+ transfer_dma),
+ min(g_iram_size,
+ qtd2->
+ length));
+ } else {
+ memcpy(ehci->
+ iram_buffer_v
+ [address_to_buffer
+ (ehci,
+ usb_pipedevice
+ (urb->pipe))],
+ urb2->
+ transfer_buffer,
+ min(g_iram_size,
+ qtd2->
+ length));
+ }
+ } else {
+ ehci->
+ iram_in_use
+ [address_to_buffer
+ (ehci,
+ usb_pipedevice(urb->
+ pipe))] =
+ 0;
+ }
+ } else {
+ ehci->
+ iram_in_use[address_to_buffer
+ (ehci,
+ usb_pipedevice(urb->
+ pipe))]
+ = 0;
+ }
+ temp_hw_qtd_next =
+ QTD_NEXT(ehci, qtd->hw_next) & 0xFFFFFFFE;
+ } else if (urb->use_iram && usb_pipein(urb->pipe)) {
+ if (urb->transfer_buffer == NULL) {
+ memcpy(phys_to_virt(urb->transfer_dma) +
+ qtd->buffer_offset,
+ ehci->
+ iram_buffer_v[address_to_buffer
+ (ehci,
+ usb_pipedevice
+ (urb->pipe))] +
+ g_iram_size, min(g_iram_size,
+ qtd->length));
+ } else {
+ memcpy(urb->transfer_buffer +
+ qtd->buffer_offset,
+ ehci->
+ iram_buffer_v[address_to_buffer
+ (ehci,
+ usb_pipedevice
+ (urb->pipe))] +
+ g_iram_size, min(g_iram_size,
+ qtd->length));
+ }
+ temp_hw_qtd_next =
+ QTD_NEXT(ehci, qtd->hw_next) & 0xFFFFFFFE;
+ }
+ /* stop scanning when we reach qtds the hc is using */
+ } else if (likely(!stopped
+ && HC_IS_RUNNING(ehci_to_hcd(ehci)->state))) {
+ break;
+
+ /* scan the whole queue for unlinks whenever it stops */
+ } else {
+ stopped = 1;
+
+ /* cancel everything if we halt, suspend, etc */
+ if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))
+ last_status = -ESHUTDOWN;
+
+ /* this qtd is active; skip it unless a previous qtd
+ * for its urb faulted, or its urb was canceled.
+ */
+ else if (last_status == -EINPROGRESS && !urb->unlinked)
+ continue;
+
+ /* qh unlinked; token in overlay may be most current */
+ if (state == QH_STATE_IDLE
+ && cpu_to_hc32(ehci, qtd->qtd_dma)
+ == qh->hw_current)
+ token = hc32_to_cpu(ehci, qh->hw_token);
+
+ /* qh unlinked; token in overlay may be most current */
+ if (state == QH_STATE_IDLE
+ && cpu_to_hc32(ehci, qtd->qtd_dma)
+ == qh->hw_current)
+ token = hc32_to_cpu(ehci, qh->hw_token);
+
+ /* force halt for unlinked or blocked qh, so we'll
+ * patch the qh later and so that completions can't
+ * activate it while we "know" it's stopped.
+ */
+ if ((halt & qh->hw_token) == 0) {
+halt:
+ qh->hw_token |= halt;
+ wmb();
+ }
+ }
+
+ /* unless we already know the urb's status, collect qtd status
+ * and update count of bytes transferred. in common short read
+ * cases with only one data qtd (including control transfers),
+ * queue processing won't halt. but with two or more qtds (for
+ * example, with a 32 KB transfer), when the first qtd gets a
+ * short read the second must be removed by hand.
+ */
+ if (last_status == -EINPROGRESS) {
+ last_status = qtd_copy_status(ehci, urb,
+ qtd->length, token);
+ if (last_status == -EREMOTEIO
+ && (qtd->hw_alt_next
+ & EHCI_LIST_END(ehci)))
+ last_status = -EINPROGRESS;
+ }
+
+ /* if we're removing something not at the queue head,
+ * patch the hardware queue pointer.
+ */
+
+ if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
+ last = list_entry(qtd->qtd_list.prev,
+ struct ehci_qtd, qtd_list);
+ last->hw_next = qtd->hw_next;
+ }
+
+/* remove qtd; it's recycled after possible urb completion */
+ list_del(&qtd->qtd_list);
+ last = qtd;
+ }
+
+ /* last urb's completion might still need calling */
+ if (likely(last != NULL)) {
+ ehci_urb_done(ehci, last->urb, last_status);
+ count++;
+ ehci_qtd_free(ehci, last);
+ }
+
+ /* restore original state; caller must unlink or relink */
+ qh->qh_state = state;
+
+ /* be sure the hardware's done with the qh before refreshing
+ * it after fault cleanup, or recovering from silicon wrongly
+ * overlaying the dummy qtd (which reduces DMA chatter).
+ */
+ if ((stopped != 0) || (qh->hw_qtd_next == EHCI_LIST_END(ehci))
+ && (temp_hw_qtd_next == 0)) {
+ switch (state) {
+ case QH_STATE_IDLE:
+ qh_refresh(ehci, qh);
+ break;
+ case QH_STATE_LINKED:
+ /* We won't refresh a QH that's linked (after the HC
+ * stopped the queue). That avoids a race:
+ * - HC reads first part of QH;
+ * - CPU updates that first part and the token;
+ * - HC reads rest of that QH, including token
+ * Result: HC gets an inconsistent image, and then
+ * DMAs to/from the wrong memory (corrupting it).
+ *
+ * That should be rare for interrupt transfers,
+ * except maybe high bandwidth ...
+ */
+ if ((cpu_to_hc32(ehci, QH_SMASK)
+ & qh->hw_info2) != 0) {
+ intr_deschedule(ehci, qh);
+ (void)qh_schedule(ehci, qh);
+ } else
+ unlink_async(ehci, qh);
+ break;
+ /* otherwise, unlink already started */
+ }
+ }
+ if (temp_hw_qtd_next)
+ qh->hw_qtd_next = temp_hw_qtd_next;
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+/* ... and packet size, for any kind of endpoint descriptor */
+#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
+
+/*
+ * reverse of qh_urb_transaction: free a list of TDs.
+ * used for cleanup after errors, before HC sees an URB's TDs.
+ */
+static void qtd_list_free(struct ehci_hcd *ehci,
+ struct urb *urb, struct list_head *qtd_list)
+{
+ struct list_head *entry, *temp;
+
+ list_for_each_safe(entry, temp, qtd_list) {
+ struct ehci_qtd *qtd;
+
+ qtd = list_entry(entry, struct ehci_qtd, qtd_list);
+ list_del(&qtd->qtd_list);
+ ehci_qtd_free(ehci, qtd);
+ }
+}
+
+/*
+ * create a list of filled qtds for this URB; won't link into qh.
+ */
+static struct list_head *qh_urb_transaction(struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *head, gfp_t flags)
+{
+ struct ehci_qtd *qtd, *qtd_prev;
+ dma_addr_t buf;
+ int len, maxpacket;
+ int is_input;
+ u32 token;
+
+ /*
+ * URBs map to sequences of QTDs: one logical transaction
+ */
+ qtd = ehci_qtd_alloc(ehci, flags);
+ if (unlikely(!qtd))
+ return NULL;
+ list_add_tail(&qtd->qtd_list, head);
+ qtd->urb = urb;
+
+ token = QTD_STS_ACTIVE;
+ token |= (EHCI_TUNE_CERR << 10);
+ /* for split transactions, SplitXState initialized to zero */
+
+ len = urb->transfer_buffer_length;
+ is_input = usb_pipein(urb->pipe);
+ if (usb_pipecontrol(urb->pipe)) {
+ /* SETUP pid */
+ qtd_fill(ehci, qtd, urb->setup_dma,
+ sizeof(struct usb_ctrlrequest),
+ token | (2 /* "setup" */ << 8), 8);
+
+ /* ... and always at least one more pid */
+ token ^= QTD_TOGGLE;
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc(ehci, flags);
+ if (unlikely(!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+
+ /* for zero length DATA stages, STATUS is always IN */
+ if (len == 0)
+ token |= (1 /* "in" */ << 8);
+ }
+
+ /*
+ * data transfer stage: buffer setup
+ */
+ buf = urb->transfer_dma;
+
+ if (is_input)
+ token |= (1 /* "in" */ << 8);
+ /* else it's already initted to "out" pid (0 << 8) */
+
+ maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
+
+ /*
+ * buffer gets wrapped in one or more qtds;
+ * last one may be "short" (including zero len)
+ * and may serve as a control status ack
+ */
+ for (;;) {
+ int this_qtd_len;
+ this_qtd_len = qtd_fill(ehci, qtd, buf, len, token, maxpacket);
+ if (urb->use_iram && (!qtd->buffer_offset)
+ && usb_pipeout(urb->pipe)
+ && (ehci->
+ iram_in_use[address_to_buffer
+ (ehci, usb_pipedevice(urb->pipe))] == 0)) {
+ ehci->
+ iram_in_use[address_to_buffer
+ (ehci, usb_pipedevice(urb->pipe))] = 1;
+ if (urb->transfer_buffer == NULL) {
+ memcpy(ehci->
+ iram_buffer_v[address_to_buffer
+ (ehci,
+ usb_pipedevice(urb->
+ pipe))],
+ phys_to_virt(urb->transfer_dma),
+ min((int)g_iram_size, len));
+ } else {
+ memcpy(ehci->
+ iram_buffer_v[address_to_buffer
+ (ehci,
+ usb_pipedevice(urb->
+ pipe))],
+ urb->transfer_buffer,
+ min((int)g_iram_size, len));
+ }
+ }
+ len -= this_qtd_len;
+ buf += this_qtd_len;
+
+ /*
+ * short reads advance to a "magic" dummy instead of the next
+ * qtd ... that forces the queue to stop, for manual cleanup.
+ * (this will usually be overridden later.)
+ */
+ if (is_input)
+ qtd->hw_alt_next = ehci->async->hw_alt_next;
+
+ /* qh makes control packets use qtd toggle; maybe switch it */
+ if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
+ token ^= QTD_TOGGLE;
+
+ if (likely(len <= 0)) {
+ qtd->last_one = 1;
+ break;
+ }
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc(ehci, flags);
+ if (unlikely(!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ if (urb->use_iram)
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma) | 0x1;
+ else
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
+
+ list_add_tail(&qtd->qtd_list, head);
+ }
+
+ /*
+ * unless the caller requires manual cleanup after short reads,
+ * have the alt_next mechanism keep the queue running after the
+ * last data qtd (the only one, for control and most other cases).
+ */
+ if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
+ || usb_pipecontrol(urb->pipe)))
+ qtd->hw_alt_next = EHCI_LIST_END(ehci);
+
+ /*
+ * control requests may need a terminating data "status" ack;
+ * bulk ones may need a terminating short packet (zero length).
+ */
+ if (likely(urb->transfer_buffer_length != 0)) {
+ int one_more = 0;
+
+ if (usb_pipecontrol(urb->pipe)) {
+ one_more = 1;
+ token ^= 0x0100; /* "in" <--> "out" */
+ token |= QTD_TOGGLE; /* force DATA1 */
+ } else if (usb_pipebulk(urb->pipe)
+ && (urb->transfer_flags & URB_ZERO_PACKET)
+ && !(urb->transfer_buffer_length % maxpacket))
+ one_more = 1;
+ if (one_more) {
+ qtd_prev = qtd;
+ qtd = ehci_qtd_alloc(ehci, flags);
+ if (unlikely(!qtd))
+ goto cleanup;
+ qtd->urb = urb;
+ qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma);
+ list_add_tail(&qtd->qtd_list, head);
+
+ /* never any data in such packets */
+ qtd_fill(ehci, qtd, 0, 0, token, 0);
+ }
+ }
+
+ /* by default, enable interrupt on urb completion */
+ if (likely(!(urb->transfer_flags & URB_NO_INTERRUPT)))
+ qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC);
+ return head;
+
+cleanup:
+ qtd_list_free(ehci, urb, head);
+ return NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Would be best to create all qh's from config descriptors,
+ * when each interface/altsetting is established. Unlink
+ * any previous qh and cancel its urbs first; endpoints are
+ * implicitly reset then (data toggle too).
+ * That'd mean updating how usbcore talks to HCDs. (2.7?)
+ */
+
+/*
+ * Each QH holds a qtd list; a QH is used for everything except iso.
+ *
+ * For interrupt urbs, the scheduler must set the microframe scheduling
+ * mask(s) each time the QH gets scheduled. For highspeed, that's
+ * just one microframe in the s-mask. For split interrupt transactions
+ * there are additional complications: c-mask, maybe FSTNs.
+ */
+static struct ehci_qh *qh_make(struct ehci_hcd *ehci,
+ struct urb *urb, gfp_t flags)
+{
+ struct ehci_qh *qh = ehci_qh_alloc(ehci, flags);
+ u32 info1 = 0, info2 = 0;
+ int is_input, type;
+ int maxp = 0;
+ struct usb_tt *tt = urb->dev->tt;
+
+ if (!qh)
+ return qh;
+
+ /*
+ * init endpoint/device data for this QH
+ */
+ info1 |= usb_pipeendpoint(urb->pipe) << 8;
+ info1 |= usb_pipedevice(urb->pipe) << 0;
+
+ is_input = usb_pipein(urb->pipe);
+ type = usb_pipetype(urb->pipe);
+ maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+
+ /* 1024 byte maxpacket is a hardware ceiling. High bandwidth
+ * acts like up to 3KB, but is built from smaller packets.
+ */
+ if (max_packet(maxp) > 1024) {
+ ehci_dbg(ehci, "bogus qh maxpacket %d\n", max_packet(maxp));
+ goto done;
+ }
+
+ /* Compute interrupt scheduling parameters just once, and save.
+ * - allowing for high bandwidth, how many nsec/uframe are used?
+ * - split transactions need a second CSPLIT uframe; same question
+ * - splits also need a schedule gap (for full/low speed I/O)
+ * - qh has a polling interval
+ *
+ * For control/bulk requests, the HC or TT handles these.
+ */
+ if (type == PIPE_INTERRUPT) {
+ qh->usecs =
+ NS_TO_US(usb_calc_bus_time
+ (USB_SPEED_HIGH, is_input, 0,
+ hb_mult(maxp) * max_packet(maxp)));
+ qh->start = NO_FRAME;
+
+ if (urb->dev->speed == USB_SPEED_HIGH) {
+ qh->c_usecs = 0;
+ qh->gap_uf = 0;
+
+ qh->period = urb->interval >> 3;
+ if (qh->period == 0 && urb->interval != 1) {
+ /* NOTE interval 2 or 4 uframes could work.
+ * But interval 1 scheduling is simpler, and
+ * includes high bandwidth.
+ */
+ dbg("intr period %d uframes, NYET!",
+ urb->interval);
+ goto done;
+ }
+ } else {
+ int think_time;
+
+ /* gap is f(FS/LS transfer times) */
+ qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed,
+ is_input, 0,
+ maxp) / (125 * 1000);
+
+ /* FIXME this just approximates SPLIT/CSPLIT times */
+ if (is_input) {
+ qh->c_usecs = qh->usecs + HS_USECS(0);
+ qh->usecs = HS_USECS(1);
+ } else {
+ qh->usecs += HS_USECS(1);
+ qh->c_usecs = HS_USECS(0);
+ }
+
+ think_time = tt ? tt->think_time : 0;
+ qh->tt_usecs = NS_TO_US(think_time +
+ usb_calc_bus_time(urb->dev->
+ speed,
+ is_input, 0,
+ max_packet
+ (maxp)));
+ qh->period = urb->interval;
+ }
+ }
+
+ /* support for tt scheduling, and access to toggles */
+ qh->dev = urb->dev;
+
+ /* using TT? */
+ switch (urb->dev->speed) {
+ case USB_SPEED_LOW:
+ info1 |= (1 << 12); /* EPS "low" */
+ /* FALL THROUGH */
+
+ case USB_SPEED_FULL:
+ /* EPS 0 means "full" */
+ if (type != PIPE_INTERRUPT)
+ info1 |= (EHCI_TUNE_RL_TT << 28);
+ if (type == PIPE_CONTROL) {
+ info1 |= (1 << 27); /* for TT */
+ info1 |= 1 << 14; /* toggle from qtd */
+ }
+ info1 |= maxp << 16;
+
+ info2 |= (EHCI_TUNE_MULT_TT << 30);
+
+ /* Some Freescale processors have an erratum in which the
+ * port number in the queue head was 0..N-1 instead of 1..N.
+ */
+ if (ehci_has_fsl_portno_bug(ehci))
+ info2 |= (urb->dev->ttport - 1) << 23;
+ else
+ info2 |= urb->dev->ttport << 23;
+
+ /* set the address of the TT; for TDI's integrated
+ * root hub tt, leave it zeroed.
+ */
+ if (tt && tt->hub != ehci_to_hcd(ehci)->self.root_hub)
+ info2 |= tt->hub->devnum << 16;
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */
+
+ break;
+
+ case USB_SPEED_HIGH: /* no TT involved */
+ info1 |= (2 << 12); /* EPS "high" */
+ if (type == PIPE_CONTROL) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ info1 |= 64 << 16; /* usb2 fixed maxpacket */
+ info1 |= 1 << 14; /* toggle from qtd */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ } else if (type == PIPE_BULK) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
+ /* The USB spec says that high speed bulk endpoints
+ * always use 512 byte maxpacket. But some device
+ * vendors decided to ignore that, and MSFT is happy
+ * to help them do so. So now people expect to use
+ * such nonconformant devices with Linux too; sigh.
+ */
+ info1 |= max_packet(maxp) << 16;
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
+ use_buffer(ehci, usb_pipedevice(urb->pipe));
+ } else { /* PIPE_INTERRUPT */
+ info1 |= max_packet(maxp) << 16;
+ info2 |= hb_mult(maxp) << 30;
+ }
+ break;
+ default:
+ dbg("bogus dev %p speed %d", urb->dev, urb->dev->speed);
+done:
+ qh_put(qh);
+ return NULL;
+ }
+
+ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
+
+ /* init as live, toggle clear, advance to dummy */
+ qh->qh_state = QH_STATE_IDLE;
+ qh->hw_info1 = cpu_to_hc32(ehci, info1);
+ qh->hw_info2 = cpu_to_hc32(ehci, info2);
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, 1);
+ qh_refresh(ehci, qh);
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* move qh (and its qtds) onto async queue; maybe enable queue. */
+
+static void qh_link_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ __hc32 dma = QH_NEXT(ehci, qh->qh_dma);
+ struct ehci_qh *head;
+
+ /* (re)start the async schedule? */
+ head = ehci->async;
+ timer_action_done(ehci, TIMER_ASYNC_OFF);
+ if (!head->qh_next.qh) {
+ u32 cmd = ehci_readl(ehci, &ehci->regs->command);
+
+ if (!(cmd & CMD_ASE)) {
+ /* in case a clear of CMD_ASE didn't take yet */
+ (void)handshake(ehci, &ehci->regs->status,
+ STS_ASS, 0, 150);
+ cmd |= CMD_ASE | CMD_RUN;
+ ehci_writel(ehci, cmd, &ehci->regs->command);
+ ehci_to_hcd(ehci)->state = HC_STATE_RUNNING;
+ /* posted write need not be known to HC yet ... */
+ }
+ }
+
+ /* clear halt and/or toggle; and maybe recover from silicon quirk */
+ if (qh->qh_state == QH_STATE_IDLE)
+ qh_refresh(ehci, qh);
+
+ /* splice right after start */
+ qh->qh_next = head->qh_next;
+ qh->hw_next = head->hw_next;
+ wmb();
+
+ head->qh_next.qh = qh;
+ head->hw_next = dma;
+
+ qh->qh_state = QH_STATE_LINKED;
+ /* qtd completions reported later by interrupt */
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * For control/bulk/interrupt, return QH with these TDs appended.
+ * Allocates and initializes the QH if necessary.
+ * Returns null if it can't allocate a QH it needs to.
+ * If the QH has TDs (urbs) already, that's great.
+ */
+static struct ehci_qh *qh_append_tds(struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list,
+ int epnum, void **ptr)
+{
+ struct ehci_qh *qh = NULL;
+ __hc32 qh_addr_mask = cpu_to_hc32(ehci, 0x7f);
+
+ qh = (struct ehci_qh *)*ptr;
+ if (unlikely(qh == NULL)) {
+ /* can't sleep here, we have ehci->lock... */
+ qh = qh_make(ehci, urb, GFP_ATOMIC);
+ *ptr = qh;
+ }
+ if (likely(qh != NULL)) {
+ struct ehci_qtd *qtd;
+
+ if (unlikely(list_empty(qtd_list)))
+ qtd = NULL;
+ else
+ qtd = list_entry(qtd_list->next, struct ehci_qtd,
+ qtd_list);
+
+ /* control qh may need patching ... */
+ if (unlikely(epnum == 0)) {
+
+ /* usb_reset_device() briefly reverts to address 0 */
+ if (usb_pipedevice(urb->pipe) == 0)
+ qh->hw_info1 &= ~qh_addr_mask;
+ }
+
+ /* just one way to queue requests: swap with the dummy qtd.
+ * only hc or qh_refresh() ever modify the overlay.
+ */
+ if (likely(qtd != NULL)) {
+ struct ehci_qtd *dummy;
+ dma_addr_t dma;
+ __hc32 token;
+
+ /* to avoid racing the HC, use the dummy td instead of
+ * the first td of our list (becomes new dummy). both
+ * tds stay deactivated until we're done, when the
+ * HC is allowed to fetch the old dummy (4.10.2).
+ */
+ token = qtd->hw_token;
+ qtd->hw_token = HALT_BIT(ehci);
+ wmb();
+ dummy = qh->dummy;
+
+ dma = dummy->qtd_dma;
+ *dummy = *qtd;
+ dummy->qtd_dma = dma;
+
+ list_del(&qtd->qtd_list);
+ list_add(&dummy->qtd_list, qtd_list);
+ __list_splice(qtd_list, qh->qtd_list.prev);
+
+ ehci_qtd_init(ehci, qtd, qtd->qtd_dma);
+ qh->dummy = qtd;
+
+ /* hc must see the new dummy at list end */
+ dma = qtd->qtd_dma;
+ qtd = list_entry(qh->qtd_list.prev,
+ struct ehci_qtd, qtd_list);
+ if (urb->use_iram)
+ qtd->hw_next = QTD_NEXT(ehci, dma) | 0x1;
+ else
+ qtd->hw_next = QTD_NEXT(ehci, dma);
+
+ /* let the hc process these next qtds */
+ wmb();
+ dummy->hw_token = token;
+
+ urb->hcpriv = qh_get(qh);
+ }
+ }
+ return qh;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+submit_async(struct ehci_hcd *ehci,
+ struct urb *urb, struct list_head *qtd_list, gfp_t mem_flags)
+{
+ struct ehci_qtd *qtd;
+ int epnum;
+ unsigned long flags;
+ struct ehci_qh *qh = NULL;
+ int rc;
+
+ qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
+ epnum = urb->ep->desc.bEndpointAddress;
+
+#ifdef EHCI_URB_TRACE
+ ehci_dbg(ehci,
+ "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ __func__, urb->dev->devpath, urb,
+ epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out",
+ urb->transfer_buffer_length, qtd, urb->ep->hcpriv);
+#endif
+
+ spin_lock_irqsave(&ehci->lock, flags);
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
+ &ehci_to_hcd(ehci)->flags))) {
+ rc = -ESHUTDOWN;
+ goto done;
+ }
+ rc = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
+ if (unlikely(rc))
+ goto done;
+
+ qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv);
+ if (unlikely(qh == NULL)) {
+ usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ /* Control/bulk operations through TTs don't need scheduling,
+ * the HC and TT handle it when the TT has a buffer ready.
+ */
+ if (likely(qh->qh_state == QH_STATE_IDLE))
+ qh_link_async(ehci, qh_get(qh));
+done:
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ if (unlikely(qh == NULL))
+ qtd_list_free(ehci, urb, qtd_list);
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* the async qh for the qtds being reclaimed are now unlinked from the HC */
+
+static void end_unlink_async(struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh = ehci->reclaim;
+ struct ehci_qh *next;
+
+ iaa_watchdog_done(ehci);
+
+ qh->qh_state = QH_STATE_IDLE;
+ qh->qh_next.qh = NULL;
+ qh_put(qh); /* refcount from reclaim */
+
+ /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
+ next = qh->reclaim;
+ ehci->reclaim = next;
+ qh->reclaim = NULL;
+
+ qh_completions(ehci, qh);
+
+ if (!list_empty(&qh->qtd_list)
+ && HC_IS_RUNNING(ehci_to_hcd(ehci)->state))
+ qh_link_async(ehci, qh);
+ else {
+ qh_put(qh); /* refcount from async list */
+
+ /* it's not free to turn the async schedule on/off; leave it
+ * active but idle for a while once it empties.
+ */
+ if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)
+ && ehci->async->qh_next.qh == NULL)
+ timer_action(ehci, TIMER_ASYNC_OFF);
+ }
+
+ if (next) {
+ ehci->reclaim = NULL;
+ start_unlink_async(ehci, next);
+ }
+}
+
+/* makes sure the async qh will become idle */
+/* caller must own ehci->lock */
+
+static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh)
+{
+ int cmd = ehci_readl(ehci, &ehci->regs->command);
+ struct ehci_qh *prev;
+
+#ifdef DEBUG
+ assert_spin_locked(&ehci->lock);
+ if (ehci->reclaim
+ || (qh->qh_state != QH_STATE_LINKED
+ && qh->qh_state != QH_STATE_UNLINK_WAIT)
+ )
+ BUG();
+#endif
+
+ /* stop async schedule right now? */
+ if (unlikely(qh == ehci->async)) {
+ /* can't get here without STS_ASS set */
+ if (ehci_to_hcd(ehci)->state != HC_STATE_HALT &&
+ !ehci->reclaim) {
+ /* ... and CMD_IAAD clear */
+ ehci_writel(ehci, cmd & ~CMD_ASE, &ehci->regs->command);
+ wmb();
+ /* handshake later, if we need to */
+ timer_action_done(ehci, TIMER_ASYNC_OFF);
+ }
+ return;
+ }
+
+ qh->qh_state = QH_STATE_UNLINK;
+ ehci->reclaim = qh = qh_get(qh);
+
+ prev = ehci->async;
+ while (prev->qh_next.qh != qh)
+ prev = prev->qh_next.qh;
+
+ prev->hw_next = qh->hw_next;
+ prev->qh_next = qh->qh_next;
+ wmb();
+
+ if (unlikely(ehci_to_hcd(ehci)->state == HC_STATE_HALT)) {
+ /* if (unlikely (qh->reclaim != 0))
+ * this will recurse, probably not much
+ */
+ end_unlink_async(ehci);
+ return;
+ }
+
+ cmd |= CMD_IAAD;
+ ehci_writel(ehci, cmd, &ehci->regs->command);
+ (void)ehci_readl(ehci, &ehci->regs->command);
+ iaa_watchdog_start(ehci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void scan_async(struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+ enum ehci_timer_action action = TIMER_IO_WATCHDOG;
+
+ if (!++(ehci->stamp))
+ ehci->stamp++;
+ timer_action_done(ehci, TIMER_ASYNC_SHRINK);
+rescan:
+ qh = ehci->async->qh_next.qh;
+ if (likely(qh != NULL)) {
+ do {
+ /* clean any finished work for this qh */
+ if (!list_empty(&qh->qtd_list)
+ && qh->stamp != ehci->stamp) {
+ int temp;
+
+ /* unlinks could happen here; completion
+ * reporting drops the lock. rescan using
+ * the latest schedule, but don't rescan
+ * qhs we already finished (no looping).
+ */
+ qh = qh_get(qh);
+ qh->stamp = ehci->stamp;
+ temp = qh_completions(ehci, qh);
+ qh_put(qh);
+ if (temp != 0)
+ goto rescan;
+ }
+
+ /* unlink idle entries, reducing HC PCI usage as well
+ * as HCD schedule-scanning costs. delay for any qh
+ * we just scanned, there's a not-unusual case that it
+ * doesn't stay idle for long.
+ * (plus, avoids some kind of re-activation race.)
+ */
+ if (list_empty(&qh->qtd_list)) {
+ if (qh->stamp == ehci->stamp)
+ action = TIMER_ASYNC_SHRINK;
+ else if (!ehci->reclaim
+ && qh->qh_state == QH_STATE_LINKED)
+ start_unlink_async(ehci, qh);
+ }
+
+ qh = qh->qh_next.qh;
+ } while (qh);
+ }
+ if (action == TIMER_ASYNC_SHRINK)
+ timer_action(ehci, TIMER_ASYNC_SHRINK);
+}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index c7d4b5a06bdb..5336a6ff08c9 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -123,6 +123,18 @@ struct ehci_hcd { /* one per controller */
u8 sbrn; /* packed release number */
+ /*
+ * OTG controllers and transceivers need software interaction;
+ * other external transceivers should be software-transparent
+ */
+ struct otg_transceiver *transceiver;
+#ifdef CONFIG_USB_STATIC_IRAM
+ u32 iram_buffer[2];
+ u32 iram_buffer_v[2];
+ int iram_in_use[2];
+ int usb_address[2];
+#endif
+
/* irq statistics */
#ifdef EHCI_STATS
struct ehci_stats stats;
@@ -257,6 +269,10 @@ struct ehci_qtd {
struct list_head qtd_list; /* sw qtd list */
struct urb *urb; /* qtd's urb */
size_t length; /* length of buffer */
+#ifdef CONFIG_USB_STATIC_IRAM
+ size_t buffer_offset;
+ int last_one;
+#endif
} __attribute__ ((aligned (32)));
/* mask NakCnt+T in qh->hw_alt_next */
@@ -698,6 +714,10 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
#define STUB_DEBUG_FILES
#endif /* DEBUG */
+#ifdef CONFIG_USB_STATIC_IRAM
+#define IRAM_TD_SIZE 1024 /* size of 1 qTD's buffer */
+#define IRAM_NTD 2 /* number of TDs in IRAM */
+#endif
/*-------------------------------------------------------------------------*/
#endif /* __LINUX_EHCI_HCD_H */
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 8aa3f4556a32..f2a4a785506e 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1080,6 +1080,11 @@ MODULE_LICENSE ("GPL");
#define TMIO_OHCI_DRIVER ohci_hcd_tmio_driver
#endif
+#ifdef CONFIG_PROCESSOR_NS9360
+#include "ohci-ns9360.c"
+#define PLATFORM_DRIVER ohci_hcd_ns9360_driver
+#endif
+
#if !defined(PCI_DRIVER) && \
!defined(PLATFORM_DRIVER) && \
!defined(OF_PLATFORM_DRIVER) && \
diff --git a/drivers/usb/host/ohci-ns9360.c b/drivers/usb/host/ohci-ns9360.c
new file mode 100644
index 000000000000..b56e31c8738d
--- /dev/null
+++ b/drivers/usb/host/ohci-ns9360.c
@@ -0,0 +1,263 @@
+/*
+ * drivers/usb/host/ohci-ns9360.c
+ *
+ * based on drivers/usb/host/ohci-sa1111.c which has
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#define NS9360_UHFE_IEN 0x0c
+#define NS9360_UHFE_IEN_OHCIIRQ (1 << 1)
+#define NS9360_UHFE_ISTAT 0x10
+#define NS9360_UHFE_ISTAT_OHCIIRQ (1 << 1)
+
+#define DRIVER_NAME "ns9360-ohci"
+
+struct ns9360_ohci_device {
+ struct ohci_hcd ohci;
+
+ struct resource *uhfe;
+ void __iomem *ioaddr;
+ struct clk *clk;
+};
+
+#define hcd2nod(up) ((struct ns9360_ohci_device *)(up->hcd_priv))
+
+extern int usb_disabled(void);
+
+static irqreturn_t usb_hcd_ns9360_hcim_irq(struct usb_hcd *hcd)
+{
+ u32 stat;
+ struct ns9360_ohci_device *priv = hcd2nod(hcd);
+
+ /* let OHCI driver handles this */
+ irqreturn_t ret = ohci_irq(hcd);
+
+ /* acknowledge interrupt in UHFE */
+ stat = ioread32(priv->ioaddr + NS9360_UHFE_ISTAT);
+ iowrite32(stat, priv->ioaddr + NS9360_UHFE_ISTAT);
+
+ return ret;
+}
+
+static int __devinit ohci_ns9360_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ ret = ohci_init(ohci);
+ if (ret < 0)
+ return ret;
+
+ ret = ohci_run(ohci);
+ if (ret < 0) {
+ ohci_err(ohci, "can't start %s", hcd->self.bus_name);
+ ohci_stop(hcd);
+ return ret;
+ }
+
+ return 0;
+}
+
+int usb_hcd_ns9360_probe(const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ int ret, irq;
+ struct usb_hcd *hcd;
+ struct resource *mem;
+ struct ns9360_ohci_device *priv;
+
+ hcd = usb_create_hcd(driver, &pdev->dev, "ns9360");
+ if (!hcd) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_create_hcd\n", __func__);
+ goto err_create_hcd;
+ }
+ priv = hcd2nod(hcd);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "%s: err_get_irq\n", __func__);
+ goto err_get_irq;
+ }
+
+ priv->uhfe = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!priv->uhfe) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "%s: err_get_uhfe\n", __func__);
+ goto err_get_uhfe;
+ }
+
+ if (!request_mem_region(priv->uhfe->start,
+ priv->uhfe->end - priv->uhfe->start + 1,
+ DRIVER_NAME)) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_request_uhfe\n", __func__);
+ goto err_request_uhfe;
+ }
+
+ priv->ioaddr = ioremap(priv->uhfe->start,
+ priv->uhfe->end - priv->uhfe->start + 1);
+ if (!priv->ioaddr) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "%s: err_map_uhfe\n", __func__);
+ goto err_map_uhfe;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "%s: err_get_mem\n", __func__);
+ goto err_get_mem;
+ }
+
+ hcd->rsrc_start = mem->start;
+ hcd->rsrc_len = mem->end - mem->start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_request_mem\n", __func__);
+ goto err_request_mem;
+}
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ ret = -EIO;
+ dev_dbg(&pdev->dev, "%s: err_map_mem\n", __func__);
+ goto err_map_mem;
+ }
+
+ priv->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_dbg(&pdev->dev, "%s: err_clk_get\n", __func__);
+ goto err_clk_get;
+ }
+
+ ret = clk_enable(priv->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_clk_enable\n", __func__);
+ goto err_clk_enable;
+ }
+
+ /* enable irq */
+ iowrite32(NS9360_UHFE_IEN_OHCIIRQ, priv->ioaddr + NS9360_UHFE_IEN);
+
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ if (ret != 0) {
+ dev_dbg(&pdev->dev, "%s: err_add_hcd\n", __func__);
+ goto err_add_hcd;
+ }
+
+ return 0;
+
+err_add_hcd:
+ clk_disable(priv->clk);
+err_clk_enable:
+ clk_put(priv->clk);
+err_clk_get:
+ iounmap(hcd->regs);
+err_map_mem:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err_request_mem:
+err_get_mem:
+ iounmap(priv->ioaddr);
+err_map_uhfe:
+ release_mem_region(priv->uhfe->start,
+ priv->uhfe->end - priv->uhfe->start + 1);
+err_request_uhfe:
+err_get_uhfe:
+err_get_irq:
+ usb_put_hcd(hcd);
+err_create_hcd:
+ return ret;
+}
+
+static int usb_hcd_ns9360_remove(struct usb_hcd *hcd,
+ struct platform_device *pdev)
+{
+ struct ns9360_ohci_device *priv = hcd2nod(hcd);
+
+ usb_remove_hcd(hcd);
+
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+
+ iounmap(priv->ioaddr);
+ release_mem_region(priv->uhfe->start,
+ priv->uhfe->end - priv->uhfe->start + 1);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static const struct hc_driver ohci_ns9360_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "NS9360 OHCI",
+ .hcd_priv_size = sizeof(struct ns9360_ohci_device),
+
+ /* generic hardware linkage */
+ .irq = usb_hcd_ns9360_hcim_irq,
+ .flags = HCD_USB11 | HCD_MEMORY ,
+
+ /* basic lifecycle operations */
+ .start = ohci_ns9360_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ /* managing i/o requests and associated device resources */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /* scheduling support */
+ .get_frame_number = ohci_get_frame,
+
+ /* root hub support */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ohci_bus_suspend,
+ .bus_resume = ohci_bus_resume,
+#endif
+ .start_port_reset = ohci_start_port_reset,
+};
+
+static int ohci_hcd_ns9360_drv_probe(struct platform_device *dev)
+{
+ return usb_hcd_ns9360_probe(&ohci_ns9360_hc_driver, dev);
+}
+
+static int ohci_hcd_ns9360_drv_remove(struct platform_device *dev)
+{
+ return usb_hcd_ns9360_remove(platform_get_drvdata(dev), dev);
+}
+
+static struct platform_driver ohci_hcd_ns9360_driver = {
+ .probe = ohci_hcd_ns9360_drv_probe,
+ .remove = ohci_hcd_ns9360_drv_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index f46af7a718d4..f35ea2184830 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -25,6 +25,11 @@
#include <mach/hardware.h>
#include <mach/usb-control.h>
+/* For enabling the second root hub port of the S3C2443 */
+#if defined(CONFIG_S3C2443_USB_PHY_PORT)
+#include <mach/regs-s3c2443-clock.h>
+#endif
+
#define valid_port(idx) ((idx) == 1 || (idx) == 2)
/* clock device associated with the hcd */
@@ -32,6 +37,57 @@
static struct clk *clk;
static struct clk *usb_clk;
+
+/*
+ * If the second port is enabled, then only connect the USB PHY to the
+ * host controller path
+ */
+#if defined(CONFIG_S3C2443_USB_PHY_PORT)
+static void usb_hcd_s3c2443_enable_second_port(void)
+{
+ ulong regval;
+
+ /* PHY power enable */
+ regval = __raw_readl(S3C2443_PWRCFG);
+ regval |= S3C2443_PWRCFG_USBPHY_ON;
+ __raw_writel(regval, S3C2443_PWRCFG);
+
+ /*
+ * USB device 2.0 must reset like bellow,
+ * 1st phy reset and after at least 10us, func_reset & host reset
+ * phy reset can reset bellow registers.
+ */
+ regval = S3C2443_URSTCON_PHY;
+ __raw_writel(regval, S3C2443_URSTCON);
+ udelay(20);
+ __raw_writel(0x00, S3C2443_URSTCON);
+
+ /* Function reset, but DONT TOUCH THE HOST! */
+ regval = S3C2443_URSTCON_FUNC;
+ __raw_writel(regval, S3C2443_URSTCON);
+ __raw_writel(0x00, S3C2443_URSTCON);
+
+ /* 48Mhz, Oscillator, External X-tal, device */
+ regval = S3C2443_PHYCTRL_EXTCLK_OSCI | S3C2443_PHYCTRL_DOWN_DEV;
+ __raw_writel(regval, S3C2443_PHYCTRL);
+}
+static inline void usb_hcd_s3c2443_disable_second_port(void)
+{
+ ulong regval;
+
+ /* Reset the USB-function and the PHY */
+ writel(S3C2443_URSTCON_PHY, S3C2443_URSTCON);
+
+ /* PHY power disable */
+ regval = readl(S3C2443_PWRCFG);
+ regval &= ~S3C2443_PWRCFG_USBPHY_ON;
+ writel(regval, S3C2443_PWRCFG);
+}
+#else
+static inline void usb_hcd_s3c2443_enable_second_port(void) { }
+static inline void usb_hcd_s3c2443_disable_second_port(void) { }
+#endif
+
/* forward definitions */
static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc);
@@ -136,7 +192,7 @@ static void s3c2410_usb_set_power(struct s3c2410_hcd_info *info,
if (info->power_control != NULL) {
info->port[port-1].power = to;
- (info->power_control)(port-1, to);
+ (info->power_control)(info, port-1, to);
}
}
@@ -285,6 +341,8 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc)
unsigned long flags;
int portno;
+ //printk(KERN_ERR "s3c2410-ohci: Over current (%i)\n", port_oc);
+
if (info == NULL)
return;
@@ -301,8 +359,9 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc)
/* ok, once over-current is detected,
the port needs to be powered down */
- s3c2410_usb_set_power(info, portno+1, 0);
- }
+ s3c2410_usb_set_power(info, portno + 1, 0);
+ } else
+ s3c2410_usb_set_power(info, portno + 1, 1);
}
local_irq_restore(flags);
@@ -350,6 +409,9 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,
s3c2410_usb_set_power(dev->dev.platform_data, 1, 1);
s3c2410_usb_set_power(dev->dev.platform_data, 2, 1);
+ /* Enable the second port for the S3C2443 */
+ usb_hcd_s3c2443_enable_second_port();
+
hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");
if (hcd == NULL)
return -ENOMEM;
@@ -473,6 +535,37 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
.start_port_reset = ohci_start_port_reset,
};
+#if defined(CONFIG_PM)
+static int ohci_hcd_s3c2410_drv_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ /* Disable the clocks before entering the suspend mode */
+ clk_disable(clk);
+ clk_disable(usb_clk);
+
+ usb_hcd_s3c2443_disable_second_port();
+
+ return 0;
+}
+
+static int ohci_hcd_s3c2410_drv_resume(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+
+ usb_hcd_s3c2443_enable_second_port();
+
+ /* Restart the clocks */
+ clk_enable(clk);
+ clk_enable(usb_clk);
+
+ hcd = platform_get_drvdata(pdev);
+ ohci_finish_controller_resume(hcd);
+ return 0;
+}
+#else
+#define ohci_hcd_s3c2410_drv_suspend NULL
+#define ohci_hcd_s3c2410_drv_resume NULL
+#endif /* defined(CONFIG_PM) */
+
/* device driver */
static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev)
@@ -492,8 +585,8 @@ static struct platform_driver ohci_hcd_s3c2410_driver = {
.probe = ohci_hcd_s3c2410_drv_probe,
.remove = ohci_hcd_s3c2410_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
- /*.suspend = ohci_hcd_s3c2410_drv_suspend, */
- /*.resume = ohci_hcd_s3c2410_drv_resume, */
+ .suspend = ohci_hcd_s3c2410_drv_suspend,
+ .resume = ohci_hcd_s3c2410_drv_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-ohci",
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
new file mode 100644
index 000000000000..f9f868c15e11
--- /dev/null
+++ b/drivers/usb/otg/Kconfig
@@ -0,0 +1,11 @@
+config TRANSCEIVER_MXC_OTG
+ tristate "USB OTG pin detect support"
+ depends on (MC13783_MXC || ISP1504_MXC) && USB_GADGET && USB_EHCI_HCD && USB_OTG
+ help
+ Support for USB OTG PIN detect on MXC platforms.
+
+config UTMI_MXC_OTG
+ tristate "USB OTG pin detect support for UTMI PHY"
+ depends on USB_EHCI_FSL_UTMI && USB_GADGET && USB_EHCI_HCD && USB_OTG
+ help
+ Support for USB OTG PIN detect for UTMI PHY on MXC platforms.
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
new file mode 100644
index 000000000000..85979c00620f
--- /dev/null
+++ b/drivers/usb/otg/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for USB OTG controller driver
+#
+# USB transceiver
+fsl_otg_arc-objs := fsl_otg.o otg_fsm.o
+obj-$(CONFIG_ISP1504_MXC_OTG) += fsl_otg_arc.o
+obj-$(CONFIG_UTMI_MXC_OTG) += fsl_otg_arc.o
diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c
new file mode 100644
index 000000000000..d5c8cf0434e3
--- /dev/null
+++ b/drivers/usb/otg/fsl_otg.c
@@ -0,0 +1,1200 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Author: Li Yang <LeoLi@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.com>
+ *
+ * Initialization based on code from Shlomi Gridish.
+ *
+ * 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/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/reboot.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+#include <linux/time.h>
+#include <linux/fsl_devices.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include "fsl_otg.h"
+
+#define CONFIG_USB_OTG_DEBUG_FILES
+#define DRIVER_VERSION "$Revision: 1.55 $"
+#define DRIVER_AUTHOR "Jerry Huang/Li Yang"
+#define DRIVER_DESC "Freescale USB OTG Driver"
+#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+
+MODULE_DESCRIPTION("Freescale USB OTG Transceiver Driver");
+
+static const char driver_name[] = "fsl-usb2-otg";
+
+const pm_message_t otg_suspend_state = {
+ .event = 1,
+};
+
+#define HA_DATA_PULSE 1
+
+volatile static struct usb_dr_mmap *usb_dr_regs;
+static struct fsl_otg *fsl_otg_dev;
+static int srp_wait_done;
+
+/* FSM timers */
+struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr,
+ *b_ase0_brst_tmr, *b_se0_srp_tmr;
+
+/* Driver specific timers */
+struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr,
+ *b_srp_wait_tmr, *a_wait_enum_tmr;
+
+static struct list_head active_timers;
+
+static struct fsl_otg_config fsl_otg_initdata = {
+ .otg_port = 1,
+};
+
+int write_ulpi(u8 addr, u8 data)
+{
+ u32 temp;
+ temp = 0x60000000 | (addr << 16) | data;
+ temp = cpu_to_le32(temp);
+ usb_dr_regs->ulpiview = temp;
+ return 0;
+}
+
+/* prototype declaration */
+void fsl_otg_add_timer(void *timer);
+void fsl_otg_del_timer(void *timer);
+
+/* -------------------------------------------------------------*/
+/* Operations that will be called from OTG Finite State Machine */
+
+/* Charge vbus for vbus pulsing in SRP */
+void fsl_otg_chrg_vbus(int on)
+{
+ if (on)
+ usb_dr_regs->otgsc =
+ cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) &
+ ~OTGSC_INTSTS_MASK &
+ ~OTGSC_CTRL_VBUS_DISCHARGE) |
+ OTGSC_CTRL_VBUS_CHARGE);
+ else
+ usb_dr_regs->otgsc =
+ cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) &
+ ~OTGSC_INTSTS_MASK & ~OTGSC_CTRL_VBUS_CHARGE));
+}
+
+/* Discharge vbus through a resistor to ground */
+void fsl_otg_dischrg_vbus(int on)
+{
+ if (on)
+ usb_dr_regs->otgsc =
+ cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) &
+ ~OTGSC_INTSTS_MASK)
+ | OTGSC_CTRL_VBUS_DISCHARGE);
+ else
+ usb_dr_regs->otgsc =
+ cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) &
+ ~OTGSC_INTSTS_MASK &
+ ~OTGSC_CTRL_VBUS_DISCHARGE));
+}
+
+/* A-device driver vbus, controlled through PP bit in PORTSC */
+void fsl_otg_drv_vbus(int on)
+{
+/* if (on)
+ usb_dr_regs->portsc =
+ cpu_to_le32((le32_to_cpu(usb_dr_regs->portsc) &
+ ~PORTSC_W1C_BITS) | PORTSC_PORT_POWER);
+ else
+ usb_dr_regs->portsc =
+ cpu_to_le32(le32_to_cpu(usb_dr_regs->portsc) &
+ ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER);
+*/
+}
+
+/*
+ * Pull-up D+, signalling connect by periperal. Also used in
+ * data-line pulsing in SRP
+ */
+void fsl_otg_loc_conn(int on)
+{
+ if (on)
+ usb_dr_regs->otgsc =
+ cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) &
+ ~OTGSC_INTSTS_MASK) | OTGSC_CTRL_DATA_PULSING);
+ else
+ usb_dr_regs->otgsc =
+ cpu_to_le32(le32_to_cpu(usb_dr_regs->otgsc) &
+ ~OTGSC_INTSTS_MASK & ~OTGSC_CTRL_DATA_PULSING);
+}
+
+/* Generate SOF by host. This is controlled through suspend/resume the
+ * port. In host mode, controller will automatically send SOF.
+ * Suspend will block the data on the port.
+ */
+void fsl_otg_loc_sof(int on)
+{
+ u32 tmpval;
+
+ tmpval = readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS;
+ if (on)
+ tmpval |= PORTSC_PORT_FORCE_RESUME;
+ else
+ tmpval |= PORTSC_PORT_SUSPEND;
+ writel(tmpval, &fsl_otg_dev->dr_mem_map->portsc);
+
+}
+
+/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */
+void fsl_otg_start_pulse(void)
+{
+ srp_wait_done = 0;
+#ifdef HA_DATA_PULSE
+ usb_dr_regs->otgsc =
+ cpu_to_le32((le32_to_cpu(usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK)
+ | OTGSC_HA_DATA_PULSE);
+#else
+ fsl_otg_loc_conn(1);
+#endif
+
+ fsl_otg_add_timer(b_data_pulse_tmr);
+}
+
+void fsl_otg_pulse_vbus(void);
+
+void b_data_pulse_end(unsigned long foo)
+{
+#ifdef HA_DATA_PULSE
+#else
+ fsl_otg_loc_conn(0);
+#endif
+
+ /* Do VBUS pulse after data pulse */
+ fsl_otg_pulse_vbus();
+}
+
+void fsl_otg_pulse_vbus(void)
+{
+ srp_wait_done = 0;
+ fsl_otg_chrg_vbus(1);
+ /* start the timer to end vbus charge */
+ fsl_otg_add_timer(b_vbus_pulse_tmr);
+}
+
+void b_vbus_pulse_end(unsigned long foo)
+{
+ fsl_otg_chrg_vbus(0);
+
+ /* As USB3300 using the same a_sess_vld and b_sess_vld voltage
+ * we need to discharge the bus for a while to distinguish
+ * residual voltage of vbus pulsing and A device pull up */
+ fsl_otg_dischrg_vbus(1);
+ fsl_otg_add_timer(b_srp_wait_tmr);
+}
+
+void b_srp_end(unsigned long foo)
+{
+ fsl_otg_dischrg_vbus(0);
+ srp_wait_done = 1;
+
+ if ((fsl_otg_dev->otg.state == OTG_STATE_B_SRP_INIT) &&
+ fsl_otg_dev->fsm.b_sess_vld)
+ fsl_otg_dev->fsm.b_srp_done = 1;
+}
+
+/* Workaround for a_host suspending too fast. When a_bus_req=0,
+ * a_host will start by SRP. It needs to set b_hnp_enable before
+ * actually suspending to start HNP
+ */
+void a_wait_enum(unsigned long foo)
+{
+ VDBG("a_wait_enum timeout\n");
+ if (!fsl_otg_dev->otg.host->b_hnp_enable)
+ fsl_otg_add_timer(a_wait_enum_tmr);
+ else
+ otg_statemachine(&fsl_otg_dev->fsm);
+}
+
+/* ------------------------------------------------------*/
+
+/* The timeout callback function to set time out bit */
+void set_tmout(unsigned long indicator)
+{
+ *(int *)indicator = 1;
+}
+
+/* Initialize timers */
+int fsl_otg_init_timers(struct otg_fsm *fsm)
+{
+ /* FSM used timers */
+ a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE,
+ (unsigned long)&fsm->a_wait_vrise_tmout);
+ if (a_wait_vrise_tmr == NULL)
+ return -ENOMEM;
+
+ a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON,
+ (unsigned long)&fsm->a_wait_bcon_tmout);
+ if (a_wait_bcon_tmr == NULL)
+ return -ENOMEM;
+
+ a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS,
+ (unsigned long)&fsm->a_aidl_bdis_tmout);
+ if (a_aidl_bdis_tmr == NULL)
+ return -ENOMEM;
+
+ b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST,
+ (unsigned long)&fsm->b_ase0_brst_tmout);
+ if (b_ase0_brst_tmr == NULL)
+ return -ENOMEM;
+
+ b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP,
+ (unsigned long)&fsm->b_se0_srp);
+ if (b_se0_srp_tmr == NULL)
+ return -ENOMEM;
+
+ b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL,
+ (unsigned long)&fsm->b_srp_done);
+ if (b_srp_fail_tmr == NULL)
+ return -ENOMEM;
+
+ a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10,
+ (unsigned long)&fsm);
+ if (a_wait_enum_tmr == NULL)
+ return -ENOMEM;
+
+ /* device driver used timers */
+ b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0);
+ if (b_srp_wait_tmr == NULL)
+ return -ENOMEM;
+
+ b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end,
+ TB_DATA_PLS, 0);
+ if (b_data_pulse_tmr == NULL)
+ return -ENOMEM;
+
+ b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end,
+ TB_VBUS_PLS, 0);
+ if (b_vbus_pulse_tmr == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/* Uninitialize timers */
+void fsl_otg_uninit_timers(void)
+{
+ /* FSM used timers */
+ if (a_wait_vrise_tmr != NULL)
+ kfree(a_wait_vrise_tmr);
+ if (a_wait_bcon_tmr != NULL)
+ kfree(a_wait_bcon_tmr);
+ if (a_aidl_bdis_tmr != NULL)
+ kfree(a_aidl_bdis_tmr);
+ if (b_ase0_brst_tmr != NULL)
+ kfree(b_ase0_brst_tmr);
+ if (b_se0_srp_tmr != NULL)
+ kfree(b_se0_srp_tmr);
+ if (b_srp_fail_tmr != NULL)
+ kfree(b_srp_fail_tmr);
+ if (a_wait_enum_tmr != NULL)
+ kfree(a_wait_enum_tmr);
+
+ /* device driver used timers */
+ if (b_srp_wait_tmr != NULL)
+ kfree(b_srp_wait_tmr);
+ if (b_data_pulse_tmr != NULL)
+ kfree(b_data_pulse_tmr);
+ if (b_vbus_pulse_tmr != NULL)
+ kfree(b_vbus_pulse_tmr);
+}
+
+/* Add timer to timer list */
+void fsl_otg_add_timer(void *gtimer)
+{
+ struct fsl_otg_timer *timer = (struct fsl_otg_timer *)gtimer;
+ struct fsl_otg_timer *tmp_timer;
+
+ /* Check if the timer is already in the active list,
+ * if so update timer count
+ */
+ list_for_each_entry(tmp_timer, &active_timers, list)
+ if (tmp_timer == timer) {
+ timer->count = timer->expires;
+ return;
+ }
+ timer->count = timer->expires;
+ list_add_tail(&timer->list, &active_timers);
+}
+
+/* Remove timer from the timer list; clear timeout status */
+void fsl_otg_del_timer(void *gtimer)
+{
+ struct fsl_otg_timer *timer = (struct fsl_otg_timer *)gtimer;
+ struct fsl_otg_timer *tmp_timer, *del_tmp;
+
+ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list)
+ if (tmp_timer == timer)
+ list_del(&timer->list);
+}
+
+/* Reduce timer count by 1, and find timeout conditions.
+ * Called by fsl_otg 1ms timer interrupt
+ */
+int fsl_otg_tick_timer(void)
+{
+ struct fsl_otg_timer *tmp_timer, *del_tmp;
+ int expired = 0;
+
+ list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) {
+ tmp_timer->count--;
+ /* check if timer expires */
+ if (!tmp_timer->count) {
+ list_del(&tmp_timer->list);
+ tmp_timer->function(tmp_timer->data);
+ expired = 1;
+ }
+ }
+
+ return expired;
+}
+
+/* Reset controller, not reset the bus */
+void otg_reset_controller(void)
+{
+ u32 command;
+
+ command = readl(&usb_dr_regs->usbcmd);
+ command |= (1 << 1);
+ writel(command, &usb_dr_regs->usbcmd);
+ while (readl(&usb_dr_regs->usbcmd) & (1 << 1)) ;
+}
+
+/* Call suspend/resume routines in host driver */
+int fsl_otg_start_host(struct otg_fsm *fsm, int on)
+{
+ struct otg_transceiver *xceiv = fsm->transceiver;
+ struct device *dev;
+ struct fsl_otg *otg_dev = container_of(xceiv, struct fsl_otg, otg);
+ u32 retval = 0;
+
+ if (!xceiv->host)
+ return -ENODEV;
+ dev = xceiv->host->controller;
+
+ /* Update a_vbus_vld state as a_vbus_vld int is disabled
+ * in device mode
+ */
+ fsm->a_vbus_vld =
+ (le32_to_cpu(usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID) ? 1 : 0;
+ if (on) {
+ /* start fsl usb host controller */
+ if (otg_dev->host_working)
+ goto end;
+ else {
+ otg_reset_controller();
+ VDBG("host on......\n");
+ if (dev->driver->resume) {
+ retval = dev->driver->resume(dev);
+ if (fsm->id) {
+ /* default-b */
+ fsl_otg_drv_vbus(1);
+ /* Workaround: b_host can't driver
+ * vbus, but PP in PORTSC needs to
+ * be 1 for host to work.
+ * So we set drv_vbus bit in
+ * transceiver to 0 thru ULPI. */
+#if defined(CONFIG_ISP1504_MXC)
+ write_ulpi(0x0c, 0x20);
+#endif
+ }
+ }
+
+ otg_dev->host_working = 1;
+ }
+ } else {
+ /* stop fsl usb host controller */
+ if (!otg_dev->host_working)
+ goto end;
+ else {
+ VDBG("host off......\n");
+ if (dev && dev->driver) {
+ retval = dev->driver->suspend(dev,
+ otg_suspend_state);
+ if (fsm->id)
+ /* default-b */
+ fsl_otg_drv_vbus(0);
+ }
+ otg_dev->host_working = 0;
+ }
+ }
+end:
+ return retval;
+}
+
+/* Call suspend and resume function in udc driver
+ * to stop and start udc driver.
+ */
+int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+ struct otg_transceiver *xceiv = fsm->transceiver;
+ struct device *dev;
+
+ if (!xceiv->gadget || !xceiv->gadget->dev.parent)
+ return -ENODEV;
+
+ VDBG("gadget %s \n", on ? "on" : "off");
+ dev = xceiv->gadget->dev.parent;
+
+ if (on)
+ dev->driver->resume(dev);
+ else
+ dev->driver->suspend(dev, otg_suspend_state);
+
+ return 0;
+}
+
+/* Called by initialization code of host driver. Register host controller
+ * to the OTG. Suspend host for OTG role detection.
+ */
+static int fsl_otg_set_host(struct otg_transceiver *otg_p, struct usb_bus *host)
+{
+ struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg);
+
+ if (!otg_p || otg_dev != fsl_otg_dev)
+ return -ENODEV;
+
+ otg_p->host = host;
+
+ otg_dev->fsm.a_bus_drop = 0;
+ otg_dev->fsm.a_bus_req = 1;
+
+ if (host) {
+ VDBG("host off......\n");
+
+ otg_p->host->otg_port = fsl_otg_initdata.otg_port;
+ otg_p->host->is_b_host = otg_dev->fsm.id;
+ /* must leave time for khubd to finish its thing
+ * before yanking the host driver out from under it,
+ * so suspend the host after a short delay.
+ */
+ otg_dev->host_working = 1;
+ schedule_delayed_work(&otg_dev->otg_event, 100);
+ return 0;
+ } else { /* host driver going away */
+
+ if (!(le32_to_cpu(otg_dev->dr_mem_map->otgsc) &
+ OTGSC_STS_USB_ID)) {
+ /* Mini-A cable connected */
+ struct otg_fsm *fsm = &otg_dev->fsm;
+
+ otg_p->state = OTG_STATE_UNDEFINED;
+ fsm->protocol = PROTO_UNDEF;
+ }
+ }
+
+ otg_dev->host_working = 0;
+
+ otg_statemachine(&otg_dev->fsm);
+
+ return 0;
+}
+
+/* Called by initialization code of udc. Register udc to OTG.*/
+static int fsl_otg_set_peripheral(struct otg_transceiver *otg_p,
+ struct usb_gadget *gadget)
+{
+ struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg);
+
+ VDBG("otg_dev 0x%x\n", (int)otg_dev);
+ VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev);
+
+ if (!otg_p || otg_dev != fsl_otg_dev)
+ return -ENODEV;
+
+ if (!gadget) {
+ if (!otg_dev->otg.default_a)
+ otg_p->gadget->ops->vbus_draw(otg_p->gadget, 0);
+ usb_gadget_vbus_disconnect(otg_dev->otg.gadget);
+ otg_dev->otg.gadget = 0;
+ otg_dev->fsm.b_bus_req = 0;
+ otg_statemachine(&otg_dev->fsm);
+ return 0;
+ }
+#ifdef DEBUG
+ /*
+ * debug the initial state of the ID pin when only
+ * the gadget driver is loaded and no cable is connected.
+ * sometimes, we get an ID irq right
+ * after the udc driver's otg_get_transceiver() call
+ * that indicates that IDpin=0, which means a Mini-A
+ * connector is attached. not good.
+ */
+ DBG("before: fsm.id ID pin=%d", otg_dev->fsm.id);
+ otg_dev->fsm.id = (otg_dev->dr_mem_map->otgsc & OTGSC_STS_USB_ID) ?
+ 1 : 0;
+ DBG("after: fsm.id ID pin=%d", otg_dev->fsm.id);
+ /*if (!otg_dev->fsm.id) {
+ printk("OTG Control = 0x%x\n",
+ isp1504_read(ISP1504_OTGCTL,
+ &otg_dev->dr_mem_map->ulpiview));
+ } */
+#endif
+
+ otg_p->gadget = gadget;
+ otg_p->gadget->is_a_peripheral = !otg_dev->fsm.id;
+
+ otg_dev->fsm.b_bus_req = 1;
+
+ /* start the gadget right away if the ID pin says Mini-B */
+ DBG("ID pin=%d\n", otg_dev->fsm.id);
+ if (otg_dev->fsm.id == 1) {
+ fsl_otg_start_host(&otg_dev->fsm, 0);
+ otg_drv_vbus(&otg_dev->fsm, 0);
+ fsl_otg_start_gadget(&otg_dev->fsm, 1);
+ }
+
+ return 0;
+}
+
+/* Set OTG port power, only for B-device */
+static int fsl_otg_set_power(struct otg_transceiver *otg_p, unsigned mA)
+{
+ if (!fsl_otg_dev)
+ return -ENODEV;
+ if (otg_p->state == OTG_STATE_B_PERIPHERAL)
+ printk(KERN_INFO "FSL OTG:Draw %d mA\n", mA);
+
+ return 0;
+}
+
+/* Delayed pin detect interrupt processing.
+ *
+ * When the Mini-A cable is disconnected from the board,
+ * the pin-detect interrupt happens before the disconnnect
+ * interrupts for the connected device(s). In order to
+ * process the disconnect interrupt(s) prior to switching
+ * roles, the pin-detect interrupts are delayed, and handled
+ * by this routine.
+ */
+static void fsl_otg_event(struct work_struct *work)
+{
+ struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work);
+ struct otg_fsm *fsm = &og->fsm;
+
+ if (fsm->id) { /* switch to gadget */
+ fsl_otg_start_host(fsm, 0);
+ otg_drv_vbus(fsm, 0);
+ fsl_otg_start_gadget(fsm, 1);
+ }
+}
+
+/* B-device start SRP */
+static int fsl_otg_start_srp(struct otg_transceiver *otg_p)
+{
+ struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg);
+
+ if (!otg_p || otg_dev != fsl_otg_dev
+ || otg_p->state != OTG_STATE_B_IDLE)
+ return -ENODEV;
+
+ otg_dev->fsm.b_bus_req = 1;
+ otg_statemachine(&otg_dev->fsm);
+
+ return 0;
+}
+
+/* A_host suspend will call this function to start hnp */
+static int fsl_otg_start_hnp(struct otg_transceiver *otg_p)
+{
+ struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg);
+
+ if (!otg_p || otg_dev != fsl_otg_dev)
+ return -ENODEV;
+
+ /* printk("start_hnp.............\n"); */
+ /* clear a_bus_req to enter a_suspend state */
+ otg_dev->fsm.a_bus_req = 0;
+ otg_statemachine(&otg_dev->fsm);
+
+ return 0;
+}
+
+/* Interrupt handler. OTG/host/peripheral share the same int line.
+ * OTG driver clears OTGSC interrupts and leaves USB interrupts
+ * intact. It needs to have knowledge of some USB interrupts
+ * such as port change.
+ */
+irqreturn_t fsl_otg_isr(int irq, void *dev_id)
+{
+ struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm;
+ struct otg_transceiver *otg = &((struct fsl_otg *)dev_id)->otg;
+ u32 otg_int_src, otg_sc;
+
+ otg_sc = le32_to_cpu(usb_dr_regs->otgsc);
+ otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8);
+
+ /* Only clear otg interrupts */
+ usb_dr_regs->otgsc |= cpu_to_le32(otg_sc & OTGSC_INTSTS_MASK);
+
+ /*FIXME: ID change not generate when init to 0 */
+ fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
+ otg->default_a = (fsm->id == 0);
+
+ /* process OTG interrupts */
+ if (otg_int_src) {
+ if (otg_int_src & OTGSC_INTSTS_USB_ID) {
+ fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
+ otg->default_a = (fsm->id == 0);
+ /* clear conn information */
+ if (fsm->id)
+ fsm->b_conn = 0;
+ else
+ fsm->a_conn = 0;
+
+ if (otg->host)
+ otg->host->is_b_host = fsm->id;
+ if (otg->gadget)
+ otg->gadget->is_a_peripheral = !fsm->id;
+ VDBG("ID int (ID is %d)\n", fsm->id);
+
+ if (fsm->id) { /* switch to gadget */
+ schedule_delayed_work(&((struct fsl_otg *)
+ dev_id)->otg_event,
+ 100);
+ } else { /* switch to host */
+ cancel_delayed_work(&
+ ((struct fsl_otg *)dev_id)->
+ otg_event);
+ fsl_otg_start_gadget(fsm, 0);
+ otg_drv_vbus(fsm, 1);
+ fsl_otg_start_host(fsm, 1);
+ }
+
+ return IRQ_HANDLED;
+ }
+ }
+
+ return IRQ_NONE;
+}
+
+static struct otg_fsm_ops fsl_otg_ops = {
+ .chrg_vbus = fsl_otg_chrg_vbus,
+ .drv_vbus = fsl_otg_drv_vbus,
+ .loc_conn = fsl_otg_loc_conn,
+ .loc_sof = fsl_otg_loc_sof,
+ .start_pulse = fsl_otg_start_pulse,
+
+ .add_timer = fsl_otg_add_timer,
+ .del_timer = fsl_otg_del_timer,
+
+ .start_host = fsl_otg_start_host,
+ .start_gadget = fsl_otg_start_gadget,
+};
+
+/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */
+static int fsl_otg_conf(struct platform_device *pdev)
+{
+ int status;
+ struct fsl_otg *fsl_otg_tc;
+ struct fsl_usb2_platform_data *pdata;
+
+ pdata = pdev->dev.platform_data;
+
+ DBG();
+
+ if (fsl_otg_dev)
+ return 0;
+
+ /* allocate space to fsl otg device */
+ fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL);
+ if (!fsl_otg_tc)
+ return -ENODEV;
+
+ INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event);
+
+ INIT_LIST_HEAD(&active_timers);
+ status = fsl_otg_init_timers(&fsl_otg_tc->fsm);
+ if (status) {
+ printk(KERN_INFO "Couldn't init OTG timers\n");
+ fsl_otg_uninit_timers();
+ kfree(fsl_otg_tc);
+ return status;
+ }
+ spin_lock_init(&fsl_otg_tc->fsm.lock);
+
+ /* Set OTG state machine operations */
+ fsl_otg_tc->fsm.ops = &fsl_otg_ops;
+
+ /* initialize the otg structure */
+ fsl_otg_tc->otg.label = DRIVER_DESC;
+ fsl_otg_tc->otg.set_host = fsl_otg_set_host;
+ fsl_otg_tc->otg.set_peripheral = fsl_otg_set_peripheral;
+ fsl_otg_tc->otg.set_power = fsl_otg_set_power;
+ fsl_otg_tc->otg.start_hnp = fsl_otg_start_hnp;
+ fsl_otg_tc->otg.start_srp = fsl_otg_start_srp;
+
+ fsl_otg_dev = fsl_otg_tc;
+
+ /* Store the otg transceiver */
+ status = otg_set_transceiver(&fsl_otg_tc->otg);
+ if (status) {
+ printk(KERN_WARNING ": unable to register OTG transceiver.\n");
+ return status;
+ }
+
+ return 0;
+}
+
+/* OTG Initialization*/
+int usb_otg_start(struct platform_device *pdev)
+{
+ struct fsl_otg *p_otg;
+ struct otg_transceiver *otg_trans = otg_get_transceiver();
+ struct otg_fsm *fsm;
+ volatile unsigned long *p;
+ int status;
+ struct resource *res;
+ u32 temp;
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+ p_otg = container_of(otg_trans, struct fsl_otg, otg);
+ fsm = &p_otg->fsm;
+
+ /* Initialize the state machine structure with default values */
+ SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED);
+ fsm->transceiver = &p_otg->otg;
+
+ /* We don't require predefined MEM/IRQ resource index */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ /* We don't request_mem_region here to enable resource sharing
+ * with host/device */
+
+ usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap));
+ p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs;
+ pdata->regs = (void *)usb_dr_regs;
+
+ /* request irq */
+ p_otg->irq = platform_get_irq(pdev, 0);
+ status = request_irq(p_otg->irq, fsl_otg_isr,
+ IRQF_SHARED, driver_name, p_otg);
+ if (status) {
+ dev_dbg(p_otg->otg.dev, "can't get IRQ %d, error %d\n",
+ p_otg->irq, status);
+ iounmap(p_otg->dr_mem_map);
+ kfree(p_otg);
+ return status;
+ }
+
+ if (pdata->platform_init && pdata->platform_init(pdev) != 0)
+ return -EINVAL;
+
+
+ /* Export DR controller resources */
+ otg_set_resources(pdev->resource);
+
+ /* stop the controller */
+ temp = readl(&p_otg->dr_mem_map->usbcmd);
+ temp &= ~USB_CMD_RUN_STOP;
+ writel(temp, &p_otg->dr_mem_map->usbcmd);
+
+ /* reset the controller */
+ temp = readl(&p_otg->dr_mem_map->usbcmd);
+ temp |= USB_CMD_CTRL_RESET;
+ writel(temp, &p_otg->dr_mem_map->usbcmd);
+
+ /* wait reset completed */
+ while (readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET) ;
+
+ /* configure the VBUSHS as IDLE(both host and device) */
+ temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0);
+ writel(temp, &p_otg->dr_mem_map->usbmode);
+
+ /* configure PHY interface */
+ temp = readl(&p_otg->dr_mem_map->portsc);
+ temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW);
+ switch (pdata->phy_mode) {
+ case FSL_USB2_PHY_ULPI:
+ temp |= PORTSC_PTS_ULPI;
+ break;
+ case FSL_USB2_PHY_UTMI_WIDE:
+ temp |= PORTSC_PTW_16BIT;
+ /* fall through */
+ case FSL_USB2_PHY_UTMI:
+ temp |= PORTSC_PTS_UTMI;
+ /* fall through */
+ default:
+ break;
+ }
+ writel(temp, &p_otg->dr_mem_map->portsc);
+
+ if (pdata->have_sysif_regs) {
+ /* configure control enable IO output, big endian register */
+ p = (volatile unsigned long *)(&p_otg->dr_mem_map->control);
+ temp = *p;
+ temp |= USB_CTRL_IOENB;
+ *p = temp;
+ }
+
+ /* disable all interrupt and clear all OTGSC status */
+ temp = readl(&p_otg->dr_mem_map->otgsc);
+ temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK;
+ temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE;
+ writel(temp, &p_otg->dr_mem_map->otgsc);
+
+
+ /*
+ * The identification (id) input is FALSE when a Mini-A plug is inserted
+ * in the devices Mini-AB receptacle. Otherwise, this input is TRUE.
+ * Also: record initial state of ID pin
+ */
+ if (le32_to_cpu(p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) {
+ p_otg->otg.state = OTG_STATE_UNDEFINED;
+ p_otg->fsm.id = 1;
+ } else {
+ p_otg->otg.state = OTG_STATE_A_IDLE;
+ p_otg->fsm.id = 0;
+ }
+
+ DBG("initial ID pin=%d\n", p_otg->fsm.id);
+
+ /* enable OTG ID pin interrupt */
+ temp = readl(&p_otg->dr_mem_map->otgsc);
+ temp |= OTGSC_INTR_USB_ID_EN;
+ temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN);
+ writel(temp, &p_otg->dr_mem_map->otgsc);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------
+ PROC File System Support
+-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_OTG_DEBUG_FILES
+
+#include <linux/seq_file.h>
+
+static const char proc_filename[] = "driver/isp1504_otg";
+
+static int otg_proc_read(char *page, char **start, off_t off, int count,
+ int *eof, void *_dev)
+{
+ struct otg_fsm *fsm = &fsl_otg_dev->fsm;
+ char *buf = page;
+ char *next = buf;
+ unsigned size = count;
+ unsigned long flags;
+ int t;
+ u32 tmp_reg;
+
+ if (off != 0)
+ return 0;
+
+ spin_lock_irqsave(&fsm->lock, flags);
+
+ /* ------basic driver infomation ---- */
+ t = scnprintf(next, size,
+ DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n",
+ DRIVER_VERSION);
+ size -= t;
+ next += t;
+
+ /* ------ Registers ----- */
+ tmp_reg = le32_to_cpu(usb_dr_regs->otgsc);
+ t = scnprintf(next, size, "OTGSC reg: %08x\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = le32_to_cpu(usb_dr_regs->portsc);
+ t = scnprintf(next, size, "PORTSC reg: %08x\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = le32_to_cpu(usb_dr_regs->usbmode);
+ t = scnprintf(next, size, "USBMODE reg: %08x\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = le32_to_cpu(usb_dr_regs->usbcmd);
+ t = scnprintf(next, size, "USBCMD reg: %08x\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = le32_to_cpu(usb_dr_regs->usbsts);
+ t = scnprintf(next, size, "USBSTS reg: %08x\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ /* ------ State ----- */
+ t = scnprintf(next, size,
+ "OTG state: %s\n\n",
+ state_string(fsl_otg_dev->otg.state));
+ size -= t;
+ next += t;
+
+#if 1 || defined DEBUG
+ /* ------ State Machine Variables ----- */
+ t = scnprintf(next, size, "a_bus_req: %d\n", fsm->a_bus_req);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "b_bus_req: %d\n", fsm->b_bus_req);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "a_bus_resume: %d\n", fsm->a_bus_resume);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "a_bus_suspend: %d\n", fsm->a_bus_suspend);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "a_conn: %d\n", fsm->a_conn);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "a_sess_vld: %d\n", fsm->a_sess_vld);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "a_srp_det: %d\n", fsm->a_srp_det);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "a_vbus_vld: %d\n", fsm->a_vbus_vld);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "b_bus_resume: %d\n", fsm->b_bus_resume);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "b_bus_suspend: %d\n", fsm->b_bus_suspend);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "b_conn: %d\n", fsm->b_conn);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "b_se0_srp: %d\n", fsm->b_se0_srp);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "b_sess_end: %d\n", fsm->b_sess_end);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "b_sess_vld: %d\n", fsm->b_sess_vld);
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size, "id: %d\n", fsm->id);
+ size -= t;
+ next += t;
+#endif
+
+ spin_unlock_irqrestore(&fsm->lock, flags);
+
+ *eof = 1;
+ return count - size;
+}
+
+#define create_proc_file() create_proc_read_entry(proc_filename, \
+ 0, NULL, otg_proc_read, NULL)
+
+#define remove_proc_file() remove_proc_entry(proc_filename, NULL)
+
+#else /* !CONFIG_USB_OTG_DEBUG_FILES */
+
+#define create_proc_file() do {} while (0)
+#define remove_proc_file() do {} while (0)
+
+#endif /*CONFIG_USB_OTG_DEBUG_FILES */
+
+/*----------------------------------------------------------*/
+/* Char driver interface to control some OTG input */
+
+/* This function handle some ioctl command,such as get otg
+ * status and set host suspend
+ */
+static int fsl_otg_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ u32 retval = 0;
+
+ switch (cmd) {
+ case GET_OTG_STATUS:
+ retval = fsl_otg_dev->host_working;
+ break;
+
+ case SET_A_SUSPEND_REQ:
+ fsl_otg_dev->fsm.a_suspend_req = arg;
+ break;
+
+ case SET_A_BUS_DROP:
+ fsl_otg_dev->fsm.a_bus_drop = arg;
+ break;
+
+ case SET_A_BUS_REQ:
+ fsl_otg_dev->fsm.a_bus_req = arg;
+ break;
+
+ case SET_B_BUS_REQ:
+ fsl_otg_dev->fsm.b_bus_req = arg;
+ break;
+
+ default:
+ break;
+ }
+
+ otg_statemachine(&fsl_otg_dev->fsm);
+
+ return retval;
+}
+
+static int fsl_otg_open(struct inode *inode, struct file *file)
+{
+
+ return 0;
+}
+
+static int fsl_otg_release(struct inode *inode, struct file *file)
+{
+
+ return 0;
+}
+
+static struct file_operations otg_fops = {
+ .owner = THIS_MODULE,
+ .llseek = NULL,
+ .read = NULL,
+ .write = NULL,
+ .ioctl = fsl_otg_ioctl,
+ .open = fsl_otg_open,
+ .release = fsl_otg_release,
+};
+
+static int __init fsl_otg_probe(struct platform_device *pdev)
+{
+ int status;
+ struct fsl_usb2_platform_data *pdata;
+
+ DBG("pdev=0x%p\n", pdev);
+
+ if (!pdev)
+ return -ENODEV;
+
+ if (!pdev->dev.platform_data)
+ return -ENOMEM;
+
+ pdata = pdev->dev.platform_data;
+
+ /* configure the OTG */
+ status = fsl_otg_conf(pdev);
+ if (status) {
+ printk(KERN_INFO "Couldn't init OTG module\n");
+ return -status;
+ }
+
+ /* start OTG */
+ status = usb_otg_start(pdev);
+
+ if (register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops)) {
+ printk(KERN_WARNING FSL_OTG_NAME
+ ": unable to register FSL OTG device\n");
+ return -EIO;
+ }
+
+ create_proc_file();
+ return status;
+}
+
+static int fsl_otg_remove(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+ otg_set_transceiver(NULL);
+ free_irq(fsl_otg_dev->irq, fsl_otg_dev);
+
+ iounmap((void *)usb_dr_regs);
+
+ kfree(fsl_otg_dev);
+
+ remove_proc_file();
+
+ unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME);
+
+ if (pdata->platform_uninit)
+ pdata->platform_uninit(pdata);
+
+ return 0;
+}
+
+struct platform_driver fsl_otg_driver = {
+ .probe = fsl_otg_probe,
+ .remove = fsl_otg_remove,
+ .driver = {
+ .name = driver_name,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init fsl_usb_otg_init(void)
+{
+ printk(KERN_INFO DRIVER_DESC " loaded, %s\n", DRIVER_VERSION);
+ return platform_driver_register(&fsl_otg_driver);
+}
+
+static void __exit fsl_usb_otg_exit(void)
+{
+ platform_driver_unregister(&fsl_otg_driver);
+ printk(KERN_INFO DRIVER_DESC " unloaded\n");
+}
+
+module_init(fsl_usb_otg_init);
+module_exit(fsl_usb_otg_exit);
+
+MODULE_DESCRIPTION(DRIVER_INFO);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/otg/fsl_otg.h b/drivers/usb/otg/fsl_otg.h
new file mode 100644
index 000000000000..03e232112b83
--- /dev/null
+++ b/drivers/usb/otg/fsl_otg.h
@@ -0,0 +1,413 @@
+/* Copyright 2005-2009 Freescale Semiconductor, 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "otg_fsm.h"
+#include <linux/usb/otg.h>
+#include <linux/ioctl.h>
+
+ /* USB Command Register Bit Masks */
+#define USB_CMD_RUN_STOP (0x1<<0 )
+#define USB_CMD_CTRL_RESET (0x1<<1 )
+#define USB_CMD_PERIODIC_SCHEDULE_EN (0x1<<4 )
+#define USB_CMD_ASYNC_SCHEDULE_EN (0x1<<5 )
+#define USB_CMD_INT_AA_DOORBELL (0x1<<6 )
+#define USB_CMD_ASP (0x3<<8 )
+#define USB_CMD_ASYNC_SCH_PARK_EN (0x1<<11 )
+#define USB_CMD_SUTW (0x1<<13 )
+#define USB_CMD_ATDTW (0x1<<14 )
+#define USB_CMD_ITC (0xFF<<16)
+
+/* bit 15,3,2 are frame list size */
+#define USB_CMD_FRAME_SIZE_1024 (0x0<<15 | 0x0<<2)
+#define USB_CMD_FRAME_SIZE_512 (0x0<<15 | 0x1<<2)
+#define USB_CMD_FRAME_SIZE_256 (0x0<<15 | 0x2<<2)
+#define USB_CMD_FRAME_SIZE_128 (0x0<<15 | 0x3<<2)
+#define USB_CMD_FRAME_SIZE_64 (0x1<<15 | 0x0<<2)
+#define USB_CMD_FRAME_SIZE_32 (0x1<<15 | 0x1<<2)
+#define USB_CMD_FRAME_SIZE_16 (0x1<<15 | 0x2<<2)
+#define USB_CMD_FRAME_SIZE_8 (0x1<<15 | 0x3<<2)
+
+/* bit 9-8 are async schedule park mode count */
+#define USB_CMD_ASP_00 (0x0<<8)
+#define USB_CMD_ASP_01 (0x1<<8)
+#define USB_CMD_ASP_10 (0x2<<8)
+#define USB_CMD_ASP_11 (0x3<<8)
+#define USB_CMD_ASP_BIT_POS (8)
+
+/* bit 23-16 are interrupt threshold control */
+#define USB_CMD_ITC_NO_THRESHOLD (0x00<<16)
+#define USB_CMD_ITC_1_MICRO_FRM (0x01<<16)
+#define USB_CMD_ITC_2_MICRO_FRM (0x02<<16)
+#define USB_CMD_ITC_4_MICRO_FRM (0x04<<16)
+#define USB_CMD_ITC_8_MICRO_FRM (0x08<<16)
+#define USB_CMD_ITC_16_MICRO_FRM (0x10<<16)
+#define USB_CMD_ITC_32_MICRO_FRM (0x20<<16)
+#define USB_CMD_ITC_64_MICRO_FRM (0x40<<16)
+#define USB_CMD_ITC_BIT_POS (16)
+
+/* USB Status Register Bit Masks */
+#define USB_STS_INT (0x1<<0 )
+#define USB_STS_ERR (0x1<<1 )
+#define USB_STS_PORT_CHANGE (0x1<<2 )
+#define USB_STS_FRM_LST_ROLL (0x1<<3 )
+#define USB_STS_SYS_ERR (0x1<<4 )
+#define USB_STS_IAA (0x1<<5 )
+#define USB_STS_RESET_RECEIVED (0x1<<6 )
+#define USB_STS_SOF (0x1<<7 )
+#define USB_STS_DCSUSPEND (0x1<<8 )
+#define USB_STS_HC_HALTED (0x1<<12)
+#define USB_STS_RCL (0x1<<13)
+#define USB_STS_PERIODIC_SCHEDULE (0x1<<14)
+#define USB_STS_ASYNC_SCHEDULE (0x1<<15)
+
+/* USB Interrupt Enable Register Bit Masks */
+#define USB_INTR_INT_EN (0x1<<0 )
+#define USB_INTR_ERR_INT_EN (0x1<<1 )
+#define USB_INTR_PC_DETECT_EN (0x1<<2 )
+#define USB_INTR_FRM_LST_ROLL_EN (0x1<<3 )
+#define USB_INTR_SYS_ERR_EN (0x1<<4 )
+#define USB_INTR_ASYN_ADV_EN (0x1<<5 )
+#define USB_INTR_RESET_EN (0x1<<6 )
+#define USB_INTR_SOF_EN (0x1<<7 )
+#define USB_INTR_DEVICE_SUSPEND (0x1<<8 )
+
+/* Device Address bit masks */
+#define USB_DEVICE_ADDRESS_MASK (0x7F<<25)
+#define USB_DEVICE_ADDRESS_BIT_POS (25)
+/* PORTSC Register Bit Masks,Only one PORT in OTG mode*/
+#define PORTSC_CURRENT_CONNECT_STATUS (0x1<<0)
+#define PORTSC_CONNECT_STATUS_CHANGE (0x1<<1)
+#define PORTSC_PORT_ENABLE (0x1<<2)
+#define PORTSC_PORT_EN_DIS_CHANGE (0x1<<3)
+#define PORTSC_OVER_CURRENT_ACT (0x1<<4)
+#define PORTSC_OVER_CUURENT_CHG (0x1<<5)
+#define PORTSC_PORT_FORCE_RESUME (0x1<<6)
+#define PORTSC_PORT_SUSPEND (0x1<<7)
+#define PORTSC_PORT_RESET (0x1<<8)
+#define PORTSC_LINE_STATUS_BITS (0x3<<10)
+#define PORTSC_PORT_POWER (0x1<<12)
+#define PORTSC_PORT_INDICTOR_CTRL (0x3<<14)
+#define PORTSC_PORT_TEST_CTRL (0xF<<16)
+#define PORTSC_WAKE_ON_CONNECT_EN (0x1<<20)
+#define PORTSC_WAKE_ON_CONNECT_DIS (0x1<<21)
+#define PORTSC_WAKE_ON_OVER_CURRENT (0x1<<22)
+#define PORTSC_PHY_LOW_POWER_SPD (0x1<<23)
+#define PORTSC_PORT_FORCE_FULL_SPEED (0x1<<24)
+#define PORTSC_PORT_SPEED_MASK (0x3<<26)
+#define PORTSC_TRANSCEIVER_WIDTH (0x1<<28)
+#define PORTSC_PHY_TYPE_SEL (0x3<<30)
+/* bit 11-10 are line status */
+#define PORTSC_LINE_STATUS_SE0 (0x0<<10)
+#define PORTSC_LINE_STATUS_JSTATE (0x1<<10)
+#define PORTSC_LINE_STATUS_KSTATE (0x2<<10)
+#define PORTSC_LINE_STATUS_UNDEF (0x3<<10)
+#define PORTSC_LINE_STATUS_BIT_POS (10)
+
+/* bit 15-14 are port indicator control */
+#define PORTSC_PIC_OFF (0x0<<14)
+#define PORTSC_PIC_AMBER (0x1<<14)
+#define PORTSC_PIC_GREEN (0x2<<14)
+#define PORTSC_PIC_UNDEF (0x3<<14)
+#define PORTSC_PIC_BIT_POS (14)
+
+/* bit 19-16 are port test control */
+#define PORTSC_PTC_DISABLE (0x0<<16)
+#define PORTSC_PTC_JSTATE (0x1<<16)
+#define PORTSC_PTC_KSTATE (0x2<<16)
+#define PORTSC_PTC_SEQNAK (0x3<<16)
+#define PORTSC_PTC_PACKET (0x4<<16)
+#define PORTSC_PTC_FORCE_EN (0x5<<16)
+#define PORTSC_PTC_BIT_POS (16)
+
+/* bit 27-26 are port speed */
+#define PORTSC_PORT_SPEED_FULL (0x0<<26)
+#define PORTSC_PORT_SPEED_LOW (0x1<<26)
+#define PORTSC_PORT_SPEED_HIGH (0x2<<26)
+#define PORTSC_PORT_SPEED_UNDEF (0x3<<26)
+#define PORTSC_SPEED_BIT_POS (26)
+
+/* bit 28 is parallel transceiver width for UTMI interface */
+#define PORTSC_PTW (0x1<<28)
+#define PORTSC_PTW_8BIT (0x0<<28)
+#define PORTSC_PTW_16BIT (0x1<<28)
+
+/* bit 31-30 are port transceiver select */
+#define PORTSC_PTS_UTMI (0x0<<30)
+#define PORTSC_PTS_ULPI (0x2<<30)
+#define PORTSC_PTS_FSLS_SERIAL (0x3<<30)
+#define PORTSC_PTS_BIT_POS (30)
+
+#define PORTSC_W1C_BITS \
+ (PORTSC_CONNECT_STATUS_CHANGE | \
+ PORTSC_PORT_EN_DIS_CHANGE | \
+ PORTSC_OVER_CUURENT_CHG)
+
+/* OTG Status Control Register Bit Masks */
+#define OTGSC_CTRL_VBUS_DISCHARGE (0x1<<0)
+#define OTGSC_CTRL_VBUS_CHARGE (0x1<<1)
+#define OTGSC_CTRL_OTG_TERMINATION (0x1<<3)
+#define OTGSC_CTRL_DATA_PULSING (0x1<<4)
+#define OTGSC_CTRL_ID_PULL_EN (0x1<<5)
+#define OTGSC_HA_DATA_PULSE (0x1<<6)
+#define OTGSC_HA_BA (0x1<<7)
+#define OTGSC_STS_USB_ID (0x1<<8)
+#define OTGSC_STS_A_VBUS_VALID (0x1<<9)
+#define OTGSC_STS_A_SESSION_VALID (0x1<<10)
+#define OTGSC_STS_B_SESSION_VALID (0x1<<11)
+#define OTGSC_STS_B_SESSION_END (0x1<<12)
+#define OTGSC_STS_1MS_TOGGLE (0x1<<13)
+#define OTGSC_STS_DATA_PULSING (0x1<<14)
+#define OTGSC_INTSTS_USB_ID (0x1<<16)
+#define OTGSC_INTSTS_A_VBUS_VALID (0x1<<17)
+#define OTGSC_INTSTS_A_SESSION_VALID (0x1<<18)
+#define OTGSC_INTSTS_B_SESSION_VALID (0x1<<19)
+#define OTGSC_INTSTS_B_SESSION_END (0x1<<20)
+#define OTGSC_INTSTS_1MS (0x1<<21)
+#define OTGSC_INTSTS_DATA_PULSING (0x1<<22)
+#define OTGSC_INTR_USB_ID_EN (0x1<<24)
+#define OTGSC_INTR_A_VBUS_VALID_EN (0x1<<25)
+#define OTGSC_INTR_A_SESSION_VALID_EN (0x1<<26)
+#define OTGSC_INTR_B_SESSION_VALID_EN (0x1<<27)
+#define OTGSC_INTR_B_SESSION_END_EN (0x1<<28)
+#define OTGSC_INTR_1MS_TIMER_EN (0x1<<29)
+#define OTGSC_INTR_DATA_PULSING_EN (0x1<<30)
+#define OTGSC_INTSTS_MASK (0x00ff0000)
+
+/* USB MODE Register Bit Masks */
+#define USB_MODE_CTRL_MODE_IDLE (0x0<<0)
+#define USB_MODE_CTRL_MODE_DEVICE (0x2<<0)
+#define USB_MODE_CTRL_MODE_HOST (0x3<<0)
+#define USB_MODE_CTRL_MODE_RSV (0x1<<0)
+#define USB_MODE_SETUP_LOCK_OFF (0x1<<3)
+#define USB_MODE_STREAM_DISABLE (0x1<<4)
+#define USB_MODE_ES (0x1<<2) /* (big) Endian Select */
+
+#define MPC8349_OTG_IRQ (38)
+#define CFG_IMMR_BASE (0xfe000000)
+#define MPC83xx_USB_DR_BASE (CFG_IMMR_BASE + 0x23000)
+
+/* control Register Bit Masks */
+#define USB_CTRL_IOENB (0x1<<2)
+#define USB_CTRL_ULPI_INT0EN (0x1<<0)
+
+/* BCSR5 */
+#define BCSR5_INT_USB (0x02)
+
+/* USB module clk cfg */
+#define SCCR_OFFS (0xA08)
+#define SCCR_USB_CLK_DISABLE (0x00000000) /* USB clk disable */
+#define SCCR_USB_MPHCM_11 (0x00c00000)
+#define SCCR_USB_MPHCM_01 (0x00400000)
+#define SCCR_USB_MPHCM_10 (0x00800000)
+#define SCCR_USB_DRCM_11 (0x00300000)
+#define SCCR_USB_DRCM_01 (0x00100000)
+#define SCCR_USB_DRCM_10 (0x00200000)
+
+#define SICRL_OFFS (0x114)
+#define SICRL_USB0 (0x40000000)
+#define SICRL_USB1 (0x20000000)
+
+#define SICRH_OFFS (0x118)
+#define SICRH_USB_UTMI (0x00020000)
+
+/* OTG interrupt enable bit masks */
+#define OTGSC_INTERRUPT_ENABLE_BITS_MASK \
+ (OTGSC_INTR_USB_ID_EN | \
+ OTGSC_INTR_1MS_TIMER_EN | \
+ OTGSC_INTR_A_VBUS_VALID_EN | \
+ OTGSC_INTR_A_SESSION_VALID_EN | \
+ OTGSC_INTR_B_SESSION_VALID_EN | \
+ OTGSC_INTR_B_SESSION_END_EN | \
+ OTGSC_INTR_DATA_PULSING_EN)
+
+/* OTG interrupt status bit masks */
+#define OTGSC_INTERRUPT_STATUS_BITS_MASK \
+ (OTGSC_INTSTS_USB_ID | \
+ OTGSC_INTR_1MS_TIMER_EN | \
+ OTGSC_INTSTS_A_VBUS_VALID | \
+ OTGSC_INTSTS_A_SESSION_VALID | \
+ OTGSC_INTSTS_B_SESSION_VALID | \
+ OTGSC_INTSTS_B_SESSION_END | \
+ OTGSC_INTSTS_DATA_PULSING)
+
+/*
+ * A-DEVICE timing constants
+ */
+
+/* Wait for VBUS Rise */
+#define TA_WAIT_VRISE (100) /* a_wait_vrise 100 ms, section: 6.6.5.1 */
+
+/* Wait for B-Connect */
+#define TA_WAIT_BCON (10000) /* a_wait_bcon > 1 sec, section: 6.6.5.2
+ * This is only used to get out of
+ * OTG_STATE_A_WAIT_BCON state if there was
+ * no connection for these many milliseconds
+ */
+
+/* A-Idle to B-Disconnect */
+/* It is necessary for this timer to be more than 750 ms because of a bug in OPT
+ * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated
+ * in the test description
+ */
+#define TA_AIDL_BDIS (5000) /* a_suspend minimum 200 ms, section: 6.6.5.3 */
+
+/* B-Idle to A-Disconnect */
+#define TA_BIDL_ADIS (12) /* 3 to 200 ms */
+
+/* B-device timing constants */
+
+
+/* Data-Line Pulse Time*/
+#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms, section:5.3.3 */
+#define TB_DATA_PLS_MIN (5) /* minimum 5 ms */
+#define TB_DATA_PLS_MAX (10) /* maximum 10 ms */
+
+/* SRP Initiate Time */
+#define TB_SRP_INIT (100) /* b_srp_init,maximum 100 ms, section:5.3.8 */
+
+/* SRP Fail Time */
+#define TB_SRP_FAIL (7000) /* b_srp_init,Fail time 5~30s, section:6.8.2.2*/
+
+/* SRP result wait time */
+#define TB_SRP_WAIT (60)
+
+/* VBus time */
+#define TB_VBUS_PLS (30) /* time to keep vbus pulsing asserted */
+
+/* Discharge time */
+/* This time should be less than 10ms. It varies from system to system. */
+#define TB_VBUS_DSCHRG (8)
+
+/* A-SE0 to B-Reset */
+#define TB_ASE0_BRST (20) /* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */
+
+/* A bus suspend timer before we can switch to b_wait_aconn */
+#define TB_A_SUSPEND (7)
+#define TB_BUS_RESUME (12)
+
+/* SE0 Time Before SRP */
+#define TB_SE0_SRP (2) /* b_idle,minimum 2 ms, section:5.3.2 */
+
+
+#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state=newstate)
+
+struct usb_dr_mmap {
+ /* Capability register */
+ u8 res1[256];
+ u16 caplength; /* Capability Register Length */
+ u16 hciversion; /* Host Controller Interface Version */
+ u32 hcsparams; /* Host Controller Structual Parameters */
+ u32 hccparams; /* Host Controller Capability Parameters */
+ u8 res2[20];
+ u32 dciversion; /* Device Controller Interface Version */
+ u32 dccparams; /* Device Controller Capability Parameters */
+ u8 res3[24];
+ /* Operation register */
+ u32 usbcmd; /* USB Command Register */
+ u32 usbsts; /* USB Status Register */
+ u32 usbintr; /* USB Interrupt Enable Register */
+ u32 frindex; /* Frame Index Register */
+ u8 res4[4];
+ u32 deviceaddr; /* Device Address */
+ u32 endpointlistaddr; /* Endpoint List Address Register */
+ u8 res5[4];
+ u32 burstsize; /* Master Interface Data Burst Size Register */
+ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */
+ u8 res6[8];
+ u32 ulpiview; /* ULPI register access */
+ u8 res7[12];
+ u32 configflag; /* Configure Flag Register */
+ u32 portsc; /* Port 1 Status and Control Register */
+ u8 res8[28];
+ u32 otgsc; /* On-The-Go Status and Control */
+ u32 usbmode; /* USB Mode Register */
+ u32 endptsetupstat; /* Endpoint Setup Status Register */
+ u32 endpointprime; /* Endpoint Initialization Register */
+ u32 endptflush; /* Endpoint Flush Register */
+ u32 endptstatus; /* Endpoint Status Register */
+ u32 endptcomplete; /* Endpoint Complete Register */
+ u32 endptctrl[6]; /* Endpoint Control Registers */
+ u8 res9[552];
+ u32 snoop1;
+ u32 snoop2;
+ u32 age_cnt_thresh; /* Age Count Threshold Register */
+ u32 pri_ctrl; /* Priority Control Register */
+ u32 si_ctrl; /* System Interface Control Register */
+ u8 res10[236];
+#ifdef CONFIG_ARCH_MX51
+ u32 res11[128];
+#endif
+ u32 control; /* General Purpose Control Register */
+};
+
+
+struct fsl_otg_timer {
+ unsigned long expires; /* Number of count increase to timeout */
+ unsigned long count; /* Tick counter */
+ void (*function)(unsigned long); /* Timeout function */
+ unsigned long data; /* Data passed to function */
+ struct list_head list;
+};
+
+struct fsl_otg_timer inline *otg_timer_initializer
+(void (*function)(unsigned long), unsigned long expires, unsigned long data)
+{
+ struct fsl_otg_timer *timer;
+ timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL);
+ if (timer == NULL)
+ return NULL;
+ timer->function = function;
+ timer->expires = expires;
+ timer->data = data;
+ return timer;
+}
+
+struct fsl_otg {
+ struct otg_transceiver otg;
+ struct otg_fsm fsm;
+ struct usb_dr_mmap *dr_mem_map;
+ struct delayed_work otg_event;
+
+ /*used for usb host */
+ struct work_struct work_wq;
+ u8 host_working;
+
+ int irq;
+};
+
+struct fsl_otg_config {
+ u8 otg_port;
+};
+
+/*For SRP and HNP handle*/
+#define FSL_OTG_MAJOR 66
+#define FSL_OTG_NAME "fsl-usb2-otg"
+/*Command to OTG driver(ioctl)*/
+#define OTG_IOCTL_MAGIC FSL_OTG_MAJOR
+/*if otg work as host,it should return 1,otherwise it return 0*/
+#define GET_OTG_STATUS _IOR(OTG_IOCTL_MAGIC, 1, int)
+#define SET_A_SUSPEND_REQ _IOW(OTG_IOCTL_MAGIC, 2, int)
+#define SET_A_BUS_DROP _IOW(OTG_IOCTL_MAGIC, 3, int)
+#define SET_A_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 4, int)
+#define SET_B_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 5, int)
+#define GET_A_SUSPEND_REQ _IOR(OTG_IOCTL_MAGIC, 6, int)
+#define GET_A_BUS_DROP _IOR(OTG_IOCTL_MAGIC, 7, int)
+#define GET_A_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 8, int)
+#define GET_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int)
+
+extern const char *state_string(enum usb_otg_state state);
+extern int otg_set_resources(struct resource *resources);
diff --git a/drivers/usb/otg/otg_fsm.c b/drivers/usb/otg/otg_fsm.c
new file mode 100644
index 000000000000..955b21cdd609
--- /dev/null
+++ b/drivers/usb/otg/otg_fsm.c
@@ -0,0 +1,371 @@
+/* OTG Finite State Machine from OTG spec
+ *
+ * Copyright (C) 2006-2008 Freescale Semiconductor, Inc.
+ *
+ * Author: Li Yang <LeoLi@freescale.com>
+ * Jerry Huang <Chang-Ming.Huang@freescale.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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/usb/otg.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+
+#include <asm/types.h>
+#include "otg_fsm.h"
+
+
+/* Defined by device specific driver, for different timer implementation */
+extern void *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr,
+ *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, *a_wait_enum_tmr;
+
+const char *state_string(enum usb_otg_state state)
+{
+ switch (state) {
+ case OTG_STATE_A_IDLE: return "a_idle";
+ case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise";
+ case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon";
+ case OTG_STATE_A_HOST: return "a_host";
+ case OTG_STATE_A_SUSPEND: return "a_suspend";
+ case OTG_STATE_A_PERIPHERAL: return "a_peripheral";
+ case OTG_STATE_A_WAIT_VFALL: return "a_wait_vfall";
+ case OTG_STATE_A_VBUS_ERR: return "a_vbus_err";
+ case OTG_STATE_B_IDLE: return "b_idle";
+ case OTG_STATE_B_SRP_INIT: return "b_srp_init";
+ case OTG_STATE_B_PERIPHERAL: return "b_peripheral";
+ case OTG_STATE_B_WAIT_ACON: return "b_wait_acon";
+ case OTG_STATE_B_HOST: return "b_host";
+ default: return "UNDEFINED";
+ }
+}
+
+/* Change USB protocol when there is a protocol change */
+static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
+{
+ int ret = 0;
+
+ if (fsm->protocol != protocol) {
+ VDBG("Changing role fsm->protocol= %d; new protocol= %d\n",
+ fsm->protocol, protocol);
+ /* stop old protocol */
+ if (fsm->protocol == PROTO_HOST)
+ ret = fsm->ops->start_host(fsm, 0);
+ else if (fsm->protocol == PROTO_GADGET)
+ ret = fsm->ops->start_gadget(fsm, 0);
+ if (ret)
+ return ret;
+
+ /* start new protocol */
+ if (protocol == PROTO_HOST)
+ ret = fsm->ops->start_host(fsm, 1);
+ else if (protocol == PROTO_GADGET)
+ ret = fsm->ops->start_gadget(fsm, 1);
+ if (ret)
+ return ret;
+
+ fsm->protocol = protocol;
+ return 0;
+ }
+
+ return 0;
+}
+
+static int state_changed;
+
+/* Called when leaving a state. Do state clean up jobs here */
+void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
+{
+ switch (old_state) {
+ case OTG_STATE_B_IDLE:
+ otg_del_timer(fsm, b_se0_srp_tmr);
+ fsm->b_se0_srp = 0;
+ break;
+ case OTG_STATE_B_SRP_INIT:
+ fsm->b_srp_done = 0;
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ otg_del_timer(fsm, b_ase0_brst_tmr);
+ fsm->b_ase0_brst_tmout = 0;
+ break;
+ case OTG_STATE_B_HOST:
+ break;
+ case OTG_STATE_A_IDLE:
+ break;
+ case OTG_STATE_A_WAIT_VRISE:
+ otg_del_timer(fsm, a_wait_vrise_tmr);
+ fsm->a_wait_vrise_tmout = 0;
+ break;
+ case OTG_STATE_A_WAIT_BCON:
+ otg_del_timer(fsm, a_wait_bcon_tmr);
+ fsm->a_wait_bcon_tmout = 0;
+ break;
+ case OTG_STATE_A_HOST:
+ otg_del_timer(fsm, a_wait_enum_tmr);
+ break;
+ case OTG_STATE_A_SUSPEND:
+ otg_del_timer(fsm, a_aidl_bdis_tmr);
+ fsm->a_aidl_bdis_tmout = 0;
+ fsm->a_suspend_req = 0;
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+ otg_del_timer(fsm, a_wait_vrise_tmr);
+ break;
+ case OTG_STATE_A_VBUS_ERR:
+ break;
+ default:
+ break;
+ }
+}
+
+/* Called when entering a state */
+int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
+{
+ state_changed = 1;
+ if (fsm->transceiver->state == new_state)
+ return 0;
+ VDBG("Set state: %s \n", state_string(new_state));
+ otg_leave_state(fsm, fsm->transceiver->state);
+ switch (new_state) {
+ case OTG_STATE_B_IDLE:
+ otg_drv_vbus(fsm, 0);
+ otg_chrg_vbus(fsm, 0);
+ otg_loc_conn(fsm, 0);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_UNDEF);
+ otg_add_timer(fsm, b_se0_srp_tmr);
+ break;
+ case OTG_STATE_B_SRP_INIT:
+ otg_start_pulse(fsm);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_UNDEF);
+ otg_add_timer(fsm, b_srp_fail_tmr);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ otg_chrg_vbus(fsm, 0);
+ otg_loc_conn(fsm, 1);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_GADGET);
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ otg_chrg_vbus(fsm, 0);
+ otg_loc_conn(fsm, 0);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_HOST);
+ otg_add_timer(fsm, b_ase0_brst_tmr);
+ fsm->a_bus_suspend = 0;
+ break;
+ case OTG_STATE_B_HOST:
+ otg_chrg_vbus(fsm, 0);
+ otg_loc_conn(fsm, 0);
+ otg_loc_sof(fsm, 1);
+ otg_set_protocol(fsm, PROTO_HOST);
+ usb_bus_start_enum(fsm->transceiver->host,
+ fsm->transceiver->host->otg_port);
+ break;
+ case OTG_STATE_A_IDLE:
+ otg_drv_vbus(fsm, 0);
+ otg_chrg_vbus(fsm, 0);
+ otg_loc_conn(fsm, 0);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_HOST);
+ break;
+ case OTG_STATE_A_WAIT_VRISE:
+ otg_drv_vbus(fsm, 1);
+ otg_loc_conn(fsm, 0);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_HOST);
+ otg_add_timer(fsm, a_wait_vrise_tmr);
+ break;
+ case OTG_STATE_A_WAIT_BCON:
+ otg_drv_vbus(fsm, 1);
+ otg_loc_conn(fsm, 0);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_HOST);
+ otg_add_timer(fsm, a_wait_bcon_tmr);
+ break;
+ case OTG_STATE_A_HOST:
+ otg_drv_vbus(fsm, 1);
+ otg_loc_conn(fsm, 0);
+ otg_loc_sof(fsm, 1);
+ otg_set_protocol(fsm, PROTO_HOST);
+ /* When HNP is triggered while a_bus_req = 0, a_host will
+ * suspend too fast to complete a_set_b_hnp_en */
+ if (!fsm->a_bus_req || fsm->a_suspend_req)
+ otg_add_timer(fsm, a_wait_enum_tmr);
+ break;
+ case OTG_STATE_A_SUSPEND:
+ otg_drv_vbus(fsm, 1);
+ otg_loc_conn(fsm, 0);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_HOST);
+ otg_add_timer(fsm, a_aidl_bdis_tmr);
+
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ otg_loc_conn(fsm, 1);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_GADGET);
+ otg_drv_vbus(fsm, 1);
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+ otg_drv_vbus(fsm, 0);
+ otg_loc_conn(fsm, 0);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_HOST);
+ break;
+ case OTG_STATE_A_VBUS_ERR:
+ otg_drv_vbus(fsm, 0);
+ otg_loc_conn(fsm, 0);
+ otg_loc_sof(fsm, 0);
+ otg_set_protocol(fsm, PROTO_UNDEF);
+ break;
+ default:
+ break;
+ }
+
+ fsm->transceiver->state = new_state;
+ return 0;
+}
+
+/* State change judgement */
+int otg_statemachine(struct otg_fsm *fsm)
+{
+ enum usb_otg_state state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fsm->lock, flags);
+
+ state = fsm->transceiver->state;
+ state_changed = 0;
+ /* State machine state change judgement */
+
+ switch (state) {
+ case OTG_STATE_UNDEFINED:
+ VDBG("fsm->id = %d \n", fsm->id);
+ if (fsm->id)
+ otg_set_state(fsm, OTG_STATE_B_IDLE);
+ else
+ otg_set_state(fsm, OTG_STATE_A_IDLE);
+ break;
+ case OTG_STATE_B_IDLE:
+ if (!fsm->id)
+ otg_set_state(fsm, OTG_STATE_A_IDLE);
+ else if (fsm->b_sess_vld && fsm->transceiver->gadget)
+ otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp)
+ otg_set_state(fsm, OTG_STATE_B_SRP_INIT);
+ break;
+ case OTG_STATE_B_SRP_INIT:
+ if (!fsm->id || fsm->b_srp_done)
+ otg_set_state(fsm, OTG_STATE_B_IDLE);
+ break;
+ case OTG_STATE_B_PERIPHERAL:
+ if (!fsm->id || !fsm->b_sess_vld)
+ otg_set_state(fsm, OTG_STATE_B_IDLE);
+ else if (fsm->b_bus_req && fsm->transceiver->
+ gadget->b_hnp_enable && fsm->a_bus_suspend)
+ otg_set_state(fsm, OTG_STATE_B_WAIT_ACON);
+ break;
+ case OTG_STATE_B_WAIT_ACON:
+ if (fsm->a_conn)
+ otg_set_state(fsm, OTG_STATE_B_HOST);
+ else if (!fsm->id || !fsm->b_sess_vld)
+ otg_set_state(fsm, OTG_STATE_B_IDLE);
+ else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) {
+ fsm->b_ase0_brst_tmout = 0;
+ otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ }
+ break;
+ case OTG_STATE_B_HOST:
+ if (!fsm->id || !fsm->b_sess_vld)
+ otg_set_state(fsm, OTG_STATE_B_IDLE);
+ else if (!fsm->b_bus_req || !fsm->a_conn)
+ otg_set_state(fsm, OTG_STATE_B_PERIPHERAL);
+ break;
+ case OTG_STATE_A_IDLE:
+ if (fsm->id)
+ otg_set_state(fsm, OTG_STATE_B_IDLE);
+ else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det))
+ otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE);
+ break;
+ case OTG_STATE_A_WAIT_VRISE:
+ if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld ||
+ fsm->a_wait_vrise_tmout) {
+ otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
+ }
+ break;
+ case OTG_STATE_A_WAIT_BCON:
+ if (!fsm->a_vbus_vld)
+ otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
+ else if (fsm->b_conn)
+ otg_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout)
+ otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
+ break;
+ case OTG_STATE_A_HOST:
+ if ((!fsm->a_bus_req || fsm->a_suspend_req) &&
+ fsm->transceiver->host->b_hnp_enable)
+ otg_set_state(fsm, OTG_STATE_A_SUSPEND);
+ else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop)
+ otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
+ else if (!fsm->a_vbus_vld)
+ otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
+ break;
+ case OTG_STATE_A_SUSPEND:
+ if (!fsm->b_conn && fsm->transceiver->host->b_hnp_enable)
+ otg_set_state(fsm, OTG_STATE_A_PERIPHERAL);
+ else if (!fsm->b_conn && !fsm->transceiver->host->b_hnp_enable)
+ otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
+ else if (fsm->a_bus_req || fsm->b_bus_resume)
+ otg_set_state(fsm, OTG_STATE_A_HOST);
+ else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout)
+ otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
+ else if (!fsm->a_vbus_vld)
+ otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
+ break;
+ case OTG_STATE_A_PERIPHERAL:
+ if (fsm->id || fsm->a_bus_drop)
+ otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
+ else if (fsm->b_bus_suspend)
+ otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
+ else if (!fsm->a_vbus_vld)
+ otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
+ break;
+ case OTG_STATE_A_WAIT_VFALL:
+ if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld &&
+ !fsm->b_conn))
+ otg_set_state(fsm, OTG_STATE_A_IDLE);
+ break;
+ case OTG_STATE_A_VBUS_ERR:
+ if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err)
+ otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&fsm->lock, flags);
+
+ /* VDBG("quit statemachine, changed = %d \n", state_changed); */
+ return state_changed;
+}
diff --git a/drivers/usb/otg/otg_fsm.h b/drivers/usb/otg/otg_fsm.h
new file mode 100644
index 000000000000..cd6894c80403
--- /dev/null
+++ b/drivers/usb/otg/otg_fsm.h
@@ -0,0 +1,151 @@
+/* Copyright (C) 2006-2008 Freescale Semiconductor, 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if 0
+#define DEBUG 1
+#define VERBOSE 1
+#endif
+
+#ifdef DEBUG
+#define DBG(fmt, args...) printk(KERN_DEBUG "j=%lu [%s] " fmt "\n", jiffies, \
+ __func__, ## args)
+#else
+#define DBG(fmt, args...) do {} while (0)
+#endif
+
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(stuff...) do {} while (0)
+#endif
+
+#ifdef VERBOSE
+#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__)
+#else
+#define MPC_LOC do {} while (0)
+#endif
+
+#define PROTO_UNDEF (0)
+#define PROTO_HOST (1)
+#define PROTO_GADGET (2)
+
+/* OTG state machine according to the OTG spec */
+struct otg_fsm {
+ /* Input */
+ int a_bus_resume;
+ int a_bus_suspend;
+ int a_conn;
+ int a_sess_vld;
+ int a_srp_det;
+ int a_vbus_vld;
+ int b_bus_resume;
+ int b_bus_suspend;
+ int b_conn;
+ int b_se0_srp;
+ int b_sess_end;
+ int b_sess_vld;
+ int id;
+
+ /* Internal variables */
+ int a_set_b_hnp_en;
+ int b_srp_done;
+ int b_hnp_enable;
+
+ /* Timeout indicator for timers */
+ int a_wait_vrise_tmout;
+ int a_wait_bcon_tmout;
+ int a_aidl_bdis_tmout;
+ int b_ase0_brst_tmout;
+
+ /* Informative variables */
+ int a_bus_drop;
+ int a_bus_req;
+ int a_clr_err;
+ int a_suspend_req;
+ int b_bus_req;
+
+ /* Output */
+ int drv_vbus;
+ int loc_conn;
+ int loc_sof;
+
+ struct otg_fsm_ops *ops;
+ struct otg_transceiver *transceiver;
+
+ /* Current usb protocol used: 0:undefine; 1:host; 2:client */
+ int protocol;
+ spinlock_t lock;
+};
+
+struct otg_fsm_ops {
+ void (*chrg_vbus)(int on);
+ void (*drv_vbus)(int on);
+ void (*loc_conn)(int on);
+ void (*loc_sof)(int on);
+ void (*start_pulse)(void);
+ void (*add_timer)(void *timer);
+ void (*del_timer)(void *timer);
+ int (*start_host)(struct otg_fsm *fsm, int on);
+ int (*start_gadget)(struct otg_fsm *fsm, int on);
+};
+
+
+static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on)
+{
+ fsm->ops->chrg_vbus(on);
+}
+
+static inline void otg_drv_vbus(struct otg_fsm *fsm, int on)
+{
+ if (fsm->drv_vbus != on) {
+ fsm->drv_vbus = on;
+ fsm->ops->drv_vbus(on);
+ }
+}
+
+static inline void otg_loc_conn(struct otg_fsm *fsm, int on)
+{
+ if (fsm->loc_conn != on) {
+ fsm->loc_conn = on;
+ fsm->ops->loc_conn(on);
+ }
+}
+
+static inline void otg_loc_sof(struct otg_fsm *fsm, int on)
+{
+ if (fsm->loc_sof != on) {
+ fsm->loc_sof = on;
+ fsm->ops->loc_sof(on);
+ }
+}
+
+static inline void otg_start_pulse(struct otg_fsm *fsm)
+{
+ fsm->ops->start_pulse();
+}
+
+static inline void otg_add_timer(struct otg_fsm *fsm, void *timer)
+{
+ fsm->ops->add_timer(timer);
+}
+
+static inline void otg_del_timer(struct otg_fsm *fsm, void *timer)
+{
+ fsm->ops->del_timer(timer);
+}
+
+int otg_statemachine(struct otg_fsm *fsm);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3f3ce13fef43..095263ba8bc7 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -381,6 +381,10 @@ config FB_CLPS711X
Say Y to enable the Framebuffer driver for the CLPS7111 and
EP7212 processors.
+if ARCH_MXC
+source "drivers/video/mxc/Kconfig"
+endif
+
config FB_SA1100
bool "SA-1100 LCD support"
depends on (FB = y) && ARM && ARCH_SA1100
@@ -1940,6 +1944,16 @@ config FB_S3C2410_DEBUG
help
Turn on debugging messages. Note that you can set/unset at run time
through sysfs
+
+config FB_S3C2443_TFT
+ tristate "S3C2443 TFT LCD framebuffer support"
+ depends on FB && CPU_S3C2443
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ default y
+ ---help---
+ Add the support for TFT-LCD displays on the Samsung S3C2443 platforms
config FB_SM501
tristate "Silicon Motion SM501 framebuffer support"
@@ -1974,6 +1988,16 @@ config FB_PNX4008_DUM_RGB
---help---
Say Y here to enable support for PNX4008 RGB Framebuffer
+config FB_STMP37XX
+ tristate "STMP 37XX LCD Framebuffer driver"
+ depends on FB && (ARCH_STMP37XX || ARCH_STMP378X)
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Say Y here to enable support for the framebuffer driver for the
+ Sigmatel STMP37XX board.
+
config FB_IBM_GXT4500
tristate "Framebuffer support for IBM GXT4500P adaptor"
depends on FB && PPC
@@ -2033,6 +2057,29 @@ config FB_SH7760
and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for
panels <= 320 pixel horizontal resolution.
+config FB_NS9360
+ tristate "NS9360 LCD framebuffer support"
+ depends on FB && PROCESSOR_NS9360
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This enables support for the display controller inside the
+ Digi ns9360 CPUs.
+
+config FB_HX8347
+ tristate "HX8347 LCD framebuffer support"
+ depends on FB
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
+ select FB_DEFERRED_IO
+
+ help
+ This enables support for the HX8347 display controller.
+ Only the register interface mode is supported.
+
config FB_VIRTUAL
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e39e33e797da..fb28506f761d 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -110,10 +110,13 @@ obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o
obj-$(CONFIG_FB_SH7760) += sh7760fb.o
obj-$(CONFIG_FB_IMX) += imxfb.o
obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o
+obj-$(CONFIG_FB_S3C2443_TFT) += s3c2443fb_tft.o
obj-$(CONFIG_FB_FSL_DIU) += fsl-diu-fb.o
obj-$(CONFIG_FB_COBALT) += cobalt_lcdfb.o
obj-$(CONFIG_FB_PNX4008_DUM) += pnx4008/
obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/
+obj-$(CONFIG_FB_MXC) += mxc/
+obj-$(CONFIG_FB_STMP37XX) += stmp37xxfb.o
obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
obj-$(CONFIG_FB_PS3) += ps3fb.o
obj-$(CONFIG_FB_SM501) += sm501fb.o
@@ -123,6 +126,8 @@ obj-$(CONFIG_FB_OMAP) += omap/
obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o
obj-$(CONFIG_FB_CARMINE) += carminefb.o
obj-$(CONFIG_FB_MB862XX) += mb862xx/
+obj-$(CONFIG_FB_NS9360) += ns9360fb.o
+obj-$(CONFIG_FB_HX8347) += hx8347fb.o
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 4a4dd9adc328..c24696c0f7a7 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -214,3 +214,44 @@ config BACKLIGHT_SAHARA
help
If you have a Tabletkiosk Sahara Touch-iT, say y to enable the
backlight driver.
+
+menuconfig BACKLIGHT_MXC
+ bool "Freescale MXC/i.MX Backlight Drivers"
+ depends on BACKLIGHT_CLASS_DEVICE && ARCH_MXC
+ default y
+ help
+ If you have a Freescale MC13783 PMIC, say y to enable the
+ backlight driver.
+
+config BACKLIGHT_MXC_IPU
+ tristate "IPU PWM Backlight Driver"
+ depends on BACKLIGHT_MXC && MXC_IPU_V1
+ default y
+
+config BACKLIGHT_MXC_LCDC
+ tristate "LCDC PWM Backlight Driver"
+ depends on BACKLIGHT_MXC && (ARCH_MX21 || ARCH_MX27 || ARCH_MX25)
+ default y
+
+config BACKLIGHT_MXC_PMIC
+ tristate "PMIC Backlight Driver"
+ depends on BACKLIGHT_MXC && MXC_MC13783_LIGHT && MXC_MC13783_POWER
+ default y
+
+config BACKLIGHT_MXC_MC13892
+ tristate "Mc13892 Backlight Driver"
+ depends on BACKLIGHT_MXC && MXC_MC13892_LIGHT
+ default y
+
+config BACKLIGHT_STMP37XX
+ tristate "SigmaTel STMP37xx Backlight Driver"
+ depends on BACKLIGHT_CLASS_DEVICE && (ARCH_STMP37XX || ARCH_STMP378X)
+ default y
+ help
+ If you have a STMP37xx, say y to enable the
+ backlight driver.
+
+config BACKLIGHT_WM8350
+ tristate "WM8350 Backlight Driver"
+ depends on BACKLIGHT_MXC && REGULATOR_WM8350
+ default y
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 103427de6703..4d1f076e49a6 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -23,3 +23,9 @@ obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o
obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o
obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o
+obj-$(CONFIG_BACKLIGHT_MXC_LCDC) += mxc_lcdc_bl.o
+obj-$(CONFIG_BACKLIGHT_MXC_IPU) += mxc_ipu_bl.o
+obj-$(CONFIG_BACKLIGHT_MXC_PMIC) += mxc_pmic_bl.o
+obj-$(CONFIG_BACKLIGHT_WM8350) += wm8350_bl.o
+obj-$(CONFIG_BACKLIGHT_MXC_MC13892) += mxc_mc13892_bl.o
+obj-$(CONFIG_BACKLIGHT_STMP37XX) += stmp37xx_bl.o
diff --git a/drivers/video/backlight/mxc_ipu_bl.c b/drivers/video/backlight/mxc_ipu_bl.c
new file mode 100644
index 000000000000..a11c2d57e25e
--- /dev/null
+++ b/drivers/video/backlight/mxc_ipu_bl.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @defgroup IPU_BL MXC IPU Backlight Driver
+ */
+/*!
+ * @file mxc_ipu_bl.c
+ *
+ * @brief Backlight Driver for IPU PWM on Freescale MXC/i.MX platforms.
+ *
+ * This file contains API defined in include/linux/clk.h for setting up and
+ * retrieving clocks.
+ *
+ * Based on Sharp's Corgi Backlight Driver
+ *
+ * @ingroup IPU_BL
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/ipu.h>
+
+#define MXC_MAX_INTENSITY 255
+#define MXC_DEFAULT_INTENSITY 127
+#define MXC_INTENSITY_OFF 0
+
+struct mxcbl_dev_data {
+ int intensity;
+};
+
+static int fb_id;
+
+static int mxcbl_send_intensity(struct backlight_device *bd)
+{
+ int intensity = bd->props.brightness;
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+
+ ipu_sdc_set_brightness(intensity);
+
+ devdata->intensity = intensity;
+ return 0;
+}
+
+static int mxcbl_get_intensity(struct backlight_device *bd)
+{
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+ return devdata->intensity;
+}
+
+static int mxcbl_check_fb(struct fb_info *info)
+{
+ int id = info->fix.id[4] - '0';
+ if (id == fb_id) {
+ if ((id == 3) && !strcmp(info->fix.id, "DISP3 FG")) {
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static struct backlight_ops mxcbl_ops = {
+ .get_brightness = mxcbl_get_intensity,
+ .update_status = mxcbl_send_intensity,
+ .check_fb = mxcbl_check_fb,
+};
+
+static int __init mxcbl_probe(struct platform_device *pdev)
+{
+ struct backlight_device *bd;
+ struct mxcbl_dev_data *devdata;
+ int ret = 0;
+
+ devdata = kzalloc(sizeof(struct mxcbl_dev_data), GFP_KERNEL);
+ if (!devdata)
+ return -ENOMEM;
+ fb_id = (int)pdev->dev.platform_data;
+
+ bd = backlight_device_register(pdev->dev.bus_id, &pdev->dev, devdata,
+ &mxcbl_ops);
+ if (IS_ERR(bd)) {
+ ret = PTR_ERR(bd);
+ goto err0;
+ }
+ platform_set_drvdata(pdev, bd);
+
+ bd->props.brightness = MXC_DEFAULT_INTENSITY;
+ bd->props.max_brightness = MXC_MAX_INTENSITY;
+ bd->props.power = FB_BLANK_UNBLANK;
+ bd->props.fb_blank = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+
+ printk("MXC Backlight Device %s Initialized.\n", pdev->dev.bus_id);
+ return 0;
+ err0:
+ kfree(devdata);
+ return ret;
+}
+
+static int mxcbl_remove(struct platform_device *pdev)
+{
+ struct backlight_device *bd = platform_get_drvdata(pdev);
+
+ bd->props.brightness = MXC_INTENSITY_OFF;
+ backlight_update_status(bd);
+
+ backlight_device_unregister(bd);
+
+ return 0;
+}
+
+static struct platform_driver mxcbl_driver = {
+ .probe = mxcbl_probe,
+ .remove = mxcbl_remove,
+ .driver = {
+ .name = "mxc_ipu_bl",
+ },
+};
+
+static int __init mxcbl_init(void)
+{
+ return platform_driver_register(&mxcbl_driver);
+}
+
+static void __exit mxcbl_exit(void)
+{
+ platform_driver_unregister(&mxcbl_driver);
+}
+
+late_initcall(mxcbl_init);
+module_exit(mxcbl_exit);
+
+MODULE_DESCRIPTION("Freescale MXC/i.MX IPU PWM Backlight Driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/mxc_lcdc_bl.c b/drivers/video/backlight/mxc_lcdc_bl.c
new file mode 100644
index 000000000000..25076e07acbe
--- /dev/null
+++ b/drivers/video/backlight/mxc_lcdc_bl.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @defgroup LCDC_BL MXC LCDC Backlight Driver
+ */
+/*!
+ * @file mxc_lcdc_bl.c
+ *
+ * @brief Backlight Driver for LCDC PWM on Freescale MXC/i.MX platforms.
+ *
+ * This file contains API defined in include/linux/clk.h for setting up and
+ * retrieving clocks.
+ *
+ * Based on Sharp's Corgi Backlight Driver
+ *
+ * @ingroup LCDC_BL
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/clk.h>
+
+#define MXC_MAX_INTENSITY 255
+#define MXC_DEFAULT_INTENSITY 127
+#define MXC_INTENSITY_OFF 0
+
+extern void mx2fb_set_brightness(uint8_t);
+
+struct mxcbl_dev_data {
+ struct clk *clk;
+ int intensity;
+};
+
+static int mxcbl_send_intensity(struct backlight_device *bd)
+{
+ int intensity = bd->props.brightness;
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+
+ if ((devdata->intensity == 0) && (intensity != 0))
+ clk_enable(devdata->clk);
+
+ /* PWM contrast control register */
+ mx2fb_set_brightness(intensity);
+
+ if ((devdata->intensity != 0) && (intensity == 0))
+ clk_disable(devdata->clk);
+
+ devdata->intensity = intensity;
+ return 0;
+}
+
+static int mxcbl_get_intensity(struct backlight_device *bd)
+{
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+ return devdata->intensity;
+}
+
+static int mxcbl_check_fb(struct fb_info *info)
+{
+ if (strcmp(info->fix.id, "DISP0 BG") == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+static struct backlight_ops mxcbl_ops = {
+ .get_brightness = mxcbl_get_intensity,
+ .update_status = mxcbl_send_intensity,
+ .check_fb = mxcbl_check_fb,
+};
+
+static int __init mxcbl_probe(struct platform_device *pdev)
+{
+ struct backlight_device *bd;
+ struct mxcbl_dev_data *devdata;
+ int ret = 0;
+
+ devdata = kzalloc(sizeof(struct mxcbl_dev_data), GFP_KERNEL);
+ if (!devdata)
+ return -ENOMEM;
+
+ devdata->clk = clk_get(NULL, "lcdc_clk");
+
+ bd = backlight_device_register(pdev->dev.bus_id, &pdev->dev, devdata,
+ &mxcbl_ops);
+ if (IS_ERR(bd)) {
+ ret = PTR_ERR(bd);
+ goto err0;
+ }
+ platform_set_drvdata(pdev, bd);
+
+ bd->props.brightness = MXC_DEFAULT_INTENSITY;
+ bd->props.max_brightness = MXC_MAX_INTENSITY;
+ bd->props.power = FB_BLANK_UNBLANK;
+ bd->props.fb_blank = FB_BLANK_UNBLANK;
+ mx2fb_set_brightness(MXC_DEFAULT_INTENSITY);
+
+ printk("MXC Backlight Device %s Initialized.\n", pdev->dev.bus_id);
+ return 0;
+ err0:
+ kfree(devdata);
+ return ret;
+}
+
+static int mxcbl_remove(struct platform_device *pdev)
+{
+ struct backlight_device *bd = platform_get_drvdata(pdev);
+
+ bd->props.brightness = MXC_INTENSITY_OFF;
+ backlight_update_status(bd);
+
+ backlight_device_unregister(bd);
+
+ return 0;
+}
+
+static struct platform_driver mxcbl_driver = {
+ .probe = mxcbl_probe,
+ .remove = mxcbl_remove,
+ .driver = {
+ .name = "mxc_lcdc_bl",
+ },
+};
+
+static int __init mxcbl_init(void)
+{
+ return platform_driver_register(&mxcbl_driver);
+}
+
+static void __exit mxcbl_exit(void)
+{
+ platform_driver_unregister(&mxcbl_driver);
+}
+
+module_init(mxcbl_init);
+module_exit(mxcbl_exit);
+
+MODULE_DESCRIPTION("Freescale MXC/i.MX LCDC PWM Backlight Driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/mxc_mc13892_bl.c b/drivers/video/backlight/mxc_mc13892_bl.c
new file mode 100644
index 000000000000..2038859243dc
--- /dev/null
+++ b/drivers/video/backlight/mxc_mc13892_bl.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
+#include <linux/pmic_light.h>
+#include <linux/pmic_external.h>
+
+/*
+#define MXC_MAX_INTENSITY 255
+#define MXC_DEFAULT_INTENSITY 127
+*/
+/* workaround for atlas hot issue */
+#define MXC_MAX_INTENSITY 128
+#define MXC_DEFAULT_INTENSITY 64
+
+#define MXC_INTENSITY_OFF 0
+
+struct mxcbl_dev_data {
+ int intensity;
+ int suspend;
+};
+
+static int mxcbl_set_intensity(struct backlight_device *bd)
+{
+ int brightness = bd->props.brightness;
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ brightness = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ brightness = 0;
+ if (devdata->suspend)
+ brightness = 0;
+
+ brightness = brightness / 4;
+ mc13892_bklit_set_dutycycle(LIT_MAIN, brightness);
+ devdata->intensity = brightness;
+
+ return 0;
+}
+
+static int mxcbl_get_intensity(struct backlight_device *bd)
+{
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+ return devdata->intensity;
+}
+
+static int mxcbl_check_fb(struct fb_info *info)
+{
+ char *id = info->fix.id;
+
+ if (!strcmp(id, "DISP3 BG"))
+ return 1;
+ else
+ return 0;
+}
+
+static struct backlight_ops bl_ops;
+
+static int __init mxcbl_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct backlight_device *bd;
+ struct mxcbl_dev_data *devdata;
+ pmic_version_t pmic_version;
+
+ pr_debug("mc13892 backlight start probe\n");
+
+ devdata = kzalloc(sizeof(struct mxcbl_dev_data), GFP_KERNEL);
+ if (!devdata)
+ return -ENOMEM;
+
+ bl_ops.check_fb = mxcbl_check_fb;
+ bl_ops.get_brightness = mxcbl_get_intensity;
+ bl_ops.update_status = mxcbl_set_intensity;
+ bd = backlight_device_register(pdev->dev.bus_id, &pdev->dev, devdata,
+ &bl_ops);
+ if (IS_ERR(bd)) {
+ ret = PTR_ERR(bd);
+ goto err0;
+ }
+
+ platform_set_drvdata(pdev, bd);
+
+ /* according to LCD spec, current should be 18mA */
+ /* workaround for MC13892 TO1.1 crash issue, set current 6mA */
+ pmic_version = pmic_get_version();
+ if (pmic_version.revision < 20)
+ mc13892_bklit_set_current(LIT_MAIN, LIT_CURR_6);
+ else
+ mc13892_bklit_set_current(LIT_MAIN, LIT_CURR_18);
+ bd->props.brightness = MXC_DEFAULT_INTENSITY;
+ bd->props.max_brightness = MXC_MAX_INTENSITY;
+ bd->props.power = FB_BLANK_UNBLANK;
+ bd->props.fb_blank = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+ pr_debug("mc13892 backlight probed successfully\n");
+ return 0;
+
+ err0:
+ kfree(devdata);
+ return ret;
+}
+
+static int mxcbl_remove(struct platform_device *pdev)
+{
+ struct backlight_device *bd = platform_get_drvdata(pdev);
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+
+ kfree(devdata);
+ backlight_device_unregister(bd);
+ return 0;
+}
+
+static int mxcbl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct backlight_device *bd = platform_get_drvdata(pdev);
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+
+ devdata->suspend = 1;
+ backlight_update_status(bd);
+ return 0;
+}
+
+static int mxcbl_resume(struct platform_device *pdev)
+{
+ struct backlight_device *bd = platform_get_drvdata(pdev);
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+
+ devdata->suspend = 0;
+ backlight_update_status(bd);
+ return 0;
+}
+
+static struct platform_driver mxcbl_driver = {
+ .probe = mxcbl_probe,
+ .remove = mxcbl_remove,
+ .suspend = mxcbl_suspend,
+ .resume = mxcbl_resume,
+ .driver = {
+ .name = "mxc_mc13892_bl",
+ },
+};
+
+static int __init mxcbl_init(void)
+{
+ return platform_driver_register(&mxcbl_driver);
+}
+
+static void __exit mxcbl_exit(void)
+{
+ platform_driver_unregister(&mxcbl_driver);
+}
+
+module_init(mxcbl_init);
+module_exit(mxcbl_exit);
+
+MODULE_DESCRIPTION("Freescale MXC/i.MX PMIC Backlight Driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/mxc_pmic_bl.c b/drivers/video/backlight/mxc_pmic_bl.c
new file mode 100644
index 000000000000..a3b0c7b44a01
--- /dev/null
+++ b/drivers/video/backlight/mxc_pmic_bl.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*!
+ * @defgroup PMIC_BL MXC PMIC Backlight Driver
+ */
+/*!
+ * @file mxc_pmic_bl.c
+ *
+ * @brief PMIC Backlight Driver for Freescale MXC/i.MX platforms.
+ *
+ * This file contains API defined in include/linux/clk.h for setting up and
+ * retrieving clocks.
+ *
+ * Based on Sharp's Corgi Backlight Driver
+ *
+ * @ingroup PMIC_BL
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/pmic_light.h>
+
+#include <mach/pmic_power.h>
+
+#define MXC_MAX_INTENSITY 255
+#define MXC_DEFAULT_INTENSITY 127
+#define MXC_INTENSITY_OFF 0
+
+struct mxcbl_dev_data {
+ int bl_id;
+ int intensity;
+ struct backlight_ops bl_ops;
+};
+
+static int pmic_bl_use_count;
+static int main_fb_id;
+static int sec_fb_id;
+
+static int mxcbl_send_intensity(struct backlight_device *bd)
+{
+ int intensity = bd->props.brightness;
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+
+ intensity = intensity / 16;
+ pmic_bklit_set_dutycycle(devdata->bl_id, intensity);
+
+ devdata->intensity = intensity;
+ return 0;
+}
+
+static int mxcbl_get_intensity(struct backlight_device *bd)
+{
+ struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
+ return devdata->intensity;
+}
+
+static int mxcbl_check_main_fb(struct fb_info *info)
+{
+ int id = info->fix.id[4] - '0';
+
+ if (id == main_fb_id) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int mxcbl_check_sec_fb(struct fb_info *info)
+{
+ int id = info->fix.id[4] - '0';
+
+ if (id == sec_fb_id) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int __init mxcbl_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct backlight_device *bd;
+ struct mxcbl_dev_data *devdata;
+
+ devdata = kzalloc(sizeof(struct mxcbl_dev_data), GFP_KERNEL);
+ if (!devdata)
+ return -ENOMEM;
+ devdata->bl_id = pdev->id;
+
+ if (pdev->id == 0) {
+ devdata->bl_ops.check_fb = mxcbl_check_main_fb;
+ main_fb_id = (int)pdev->dev.platform_data;
+ } else {
+ devdata->bl_ops.check_fb = mxcbl_check_sec_fb;
+ sec_fb_id = (int)pdev->dev.platform_data;
+ }
+
+ devdata->bl_ops.get_brightness = mxcbl_get_intensity;
+ devdata->bl_ops.update_status = mxcbl_send_intensity,
+ bd =
+ backlight_device_register(pdev->dev.bus_id, &pdev->dev, devdata,
+ &devdata->bl_ops);
+ if (IS_ERR(bd)) {
+ ret = PTR_ERR(bd);
+ goto err0;
+ }
+
+ platform_set_drvdata(pdev, bd);
+
+ if (pmic_bl_use_count++ == 0) {
+ pmic_power_regulator_on(SW_SW3);
+ pmic_power_regulator_set_lp_mode(SW_SW3, LOW_POWER_CTRL_BY_PIN);
+
+ pmic_bklit_tcled_master_enable();
+ pmic_bklit_enable_edge_slow();
+ pmic_bklit_set_cycle_time(0);
+ }
+
+ pmic_bklit_set_current(devdata->bl_id, 7);
+ bd->props.brightness = MXC_DEFAULT_INTENSITY;
+ bd->props.max_brightness = MXC_MAX_INTENSITY;
+ bd->props.power = FB_BLANK_UNBLANK;
+ bd->props.fb_blank = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+
+ printk("MXC Backlight Device %s Initialized.\n", pdev->dev.bus_id);
+ return 0;
+ err0:
+ kfree(devdata);
+ return ret;
+}
+
+static int mxcbl_remove(struct platform_device *pdev)
+{
+ struct backlight_device *bd = platform_get_drvdata(pdev);
+
+ bd->props.brightness = MXC_INTENSITY_OFF;
+ backlight_update_status(bd);
+
+ if (--pmic_bl_use_count == 0) {
+ pmic_bklit_tcled_master_disable();
+
+ pmic_power_regulator_off(SW_SW3);
+ pmic_power_regulator_set_lp_mode(SW_SW3, LOW_POWER_CTRL_BY_PIN);
+ }
+
+ backlight_device_unregister(bd);
+
+ printk("MXC Backlight Driver Unloaded\n");
+
+ return 0;
+}
+
+static struct platform_driver mxcbl_driver = {
+ .probe = mxcbl_probe,
+ .remove = mxcbl_remove,
+ .driver = {
+ .name = "mxc_pmic_bl",
+ },
+};
+
+static int __init mxcbl_init(void)
+{
+ return platform_driver_register(&mxcbl_driver);
+}
+
+static void __exit mxcbl_exit(void)
+{
+ platform_driver_unregister(&mxcbl_driver);
+}
+
+module_init(mxcbl_init);
+module_exit(mxcbl_exit);
+
+MODULE_DESCRIPTION("Freescale MXC/i.MX PMIC Backlight Driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index ea07258565f0..e641584e212e 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -3,7 +3,7 @@
*
* simple PWM based backlight control, board code has to setup
* 1) pin configuration so PWM waveforms can output
- * 2) platform_data casts to the PWM id (0/1/2/3 on PXA)
+ * 2) platform_data being correctly configured
*
* 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
@@ -97,7 +97,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
} else
dev_dbg(&pdev->dev, "got pwm for backlight\n");
- bl = backlight_device_register(pdev->name, &pdev->dev,
+ bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev,
pb, &pwm_backlight_ops);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
diff --git a/drivers/video/backlight/stmp37xx_bl.c b/drivers/video/backlight/stmp37xx_bl.c
new file mode 100644
index 000000000000..6b4d9a0ccbaa
--- /dev/null
+++ b/drivers/video/backlight/stmp37xx_bl.c
@@ -0,0 +1,378 @@
+/*
+ * Backlight Driver for Freescale STMP37XX/STMP378X
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/lcdif.h>
+#include <mach/regulator.h>
+
+struct stmp3xxx_bl_data {
+ struct notifier_block nb;
+ struct notifier_block reg_nb;
+ struct notifier_block reg_init_nb;
+ struct backlight_device *bd;
+ struct stmp3xxx_platform_bl_data *pdata;
+ int current_intensity;
+ int saved_intensity;
+ int stmp3xxxbl_suspended;
+ int stmp3xxxbl_constrained;
+};
+
+static int stmp3xxxbl_do_probe(struct stmp3xxx_bl_data *data,
+ struct stmp3xxx_platform_bl_data *pdata);
+static int stmp3xxxbl_set_intensity(struct backlight_device *bd);
+static inline void bl_register_reg(struct stmp3xxx_platform_bl_data *pdata,
+ struct stmp3xxx_bl_data *data);
+
+
+/*
+ * If we got here init is done
+ */
+static int bl_init_reg_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct stmp3xxx_bl_data *bdata;
+ struct stmp3xxx_platform_bl_data *pdata;
+ struct regulator *r = regulator_get(NULL, "stmp3xxx-bl-1");
+
+ bdata = container_of(self, struct stmp3xxx_bl_data, reg_init_nb);
+ pdata = bdata->pdata;
+
+ if (r && !IS_ERR(r))
+ regulator_put(r);
+ else
+ goto out;
+
+ bl_register_reg(pdata, bdata);
+
+ if (pdata->regulator) {
+
+ printk(KERN_NOTICE"%s: setting intensity\n", __func__);
+
+ bus_unregister_notifier(&platform_bus_type,
+ &bdata->reg_init_nb);
+ mutex_lock(&bdata->bd->ops_lock);
+ stmp3xxxbl_set_intensity(bdata->bd);
+ mutex_unlock(&bdata->bd->ops_lock);
+ }
+
+out:
+ return 0;
+}
+
+static int bl_reg_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct stmp3xxx_bl_data *bdata;
+ struct stmp3xxx_platform_bl_data *pdata;
+ bdata = container_of(self, struct stmp3xxx_bl_data, reg_nb);
+ pdata = bdata->pdata;
+
+ mutex_lock(&bdata->bd->ops_lock);
+
+ switch (event) {
+ case STMP3XXX_REG5V_IS_USB:
+ bdata->bd->props.max_brightness = pdata->bl_cons_intensity;
+ bdata->bd->props.brightness = pdata->bl_cons_intensity;
+ bdata->saved_intensity = bdata->current_intensity;
+ bdata->stmp3xxxbl_constrained = 1;
+ break;
+ case STMP3XXX_REG5V_NOT_USB:
+ bdata->bd->props.max_brightness = pdata->bl_max_intensity;
+ bdata->bd->props.brightness = bdata->saved_intensity;
+ bdata->stmp3xxxbl_constrained = 0;
+ break;
+ }
+
+ stmp3xxxbl_set_intensity(bdata->bd);
+ mutex_unlock(&bdata->bd->ops_lock);
+ return 0;
+}
+
+static inline void bl_unregister_reg(struct stmp3xxx_platform_bl_data *pdata,
+ struct stmp3xxx_bl_data *data)
+{
+ if (!pdata)
+ return;
+ if (pdata->regulator)
+ regulator_unregister_notifier(pdata->regulator,
+ &data->reg_nb);
+ if (pdata->regulator)
+ regulator_put(pdata->regulator);
+ pdata->regulator = NULL;
+}
+
+static inline void bl_register_reg(struct stmp3xxx_platform_bl_data *pdata,
+ struct stmp3xxx_bl_data *data)
+{
+ pdata->regulator = regulator_get(NULL, "stmp3xxx-bl-1");
+ if (pdata->regulator && !IS_ERR(pdata->regulator)) {
+ regulator_set_mode(pdata->regulator, REGULATOR_MODE_FAST);
+ if (pdata->regulator) {
+ data->reg_nb.notifier_call = bl_reg_callback;
+ regulator_register_notifier(pdata->regulator,
+ &data->reg_nb);
+ }
+ } else{
+ printk(KERN_ERR "%s: failed to get regulator\n", __func__);
+ pdata->regulator = NULL;
+ }
+
+}
+
+static int bl_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct stmp3xxx_platform_fb_entry *pentry = data;
+ struct stmp3xxx_bl_data *bdata;
+ struct stmp3xxx_platform_bl_data *pdata;
+
+ switch (event) {
+ case STMP3XXX_LCDIF_PANEL_INIT:
+ bdata = container_of(self, struct stmp3xxx_bl_data, nb);
+ pdata = pentry->bl_data;
+ bdata->pdata = pdata;
+ if (pdata) {
+ bl_register_reg(pdata, bdata);
+ if (!pdata->regulator) {
+ /* wait for regulator to appear */
+ bdata->reg_init_nb.notifier_call =
+ bl_init_reg_callback;
+ bus_register_notifier(&platform_bus_type,
+ &bdata->reg_init_nb);
+ }
+ return stmp3xxxbl_do_probe(bdata, pdata);
+ }
+ break;
+
+ case STMP3XXX_LCDIF_PANEL_RELEASE:
+ bdata = container_of(self, struct stmp3xxx_bl_data, nb);
+ pdata = pentry->bl_data;
+ if (pdata) {
+ bus_unregister_notifier(&platform_bus_type,
+ &bdata->reg_init_nb);
+ bl_unregister_reg(pdata, bdata);
+ pdata->free_bl(pdata);
+ }
+ bdata->pdata = NULL;
+ break;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stmp3xxxbl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct stmp3xxx_bl_data *data = platform_get_drvdata(pdev);
+ struct stmp3xxx_platform_bl_data *pdata = data->pdata;
+
+ data->stmp3xxxbl_suspended = 1;
+ if (pdata) {
+ dev_dbg(&pdev->dev, "real suspend\n");
+ stmp3xxxbl_set_intensity(data->bd);
+ }
+ return 0;
+}
+
+static int stmp3xxxbl_resume(struct platform_device *pdev)
+{
+ struct stmp3xxx_bl_data *data = platform_get_drvdata(pdev);
+ struct stmp3xxx_platform_bl_data *pdata = data->pdata;
+ int ret = 0;
+
+ data->stmp3xxxbl_suspended = 0;
+ if (pdata) {
+ dev_dbg(&pdev->dev, "real resume\n");
+ pdata->free_bl(pdata);
+ ret = pdata->init_bl(pdata);
+ if (ret)
+ goto out;
+ stmp3xxxbl_set_intensity(data->bd);
+ }
+out:
+ return ret;
+}
+#else
+#define stmp3xxxbl_suspend NULL
+#define stmp3xxxbl_resume NULL
+#endif
+/*
+ * This function should be called with bd->ops_lock held
+ * Suspend/resume ?
+ */
+static int stmp3xxxbl_set_intensity(struct backlight_device *bd)
+{
+ struct platform_device *pdev = dev_get_drvdata(&bd->dev);
+ struct stmp3xxx_bl_data *data = platform_get_drvdata(pdev);
+ struct stmp3xxx_platform_bl_data *pdata = data->pdata;
+
+ if (pdata) {
+ int ret;
+
+ ret = pdata->set_bl_intensity(pdata, bd,
+ data->stmp3xxxbl_suspended);
+ if (ret)
+ bd->props.brightness = data->current_intensity;
+ else
+ data->current_intensity = bd->props.brightness;
+ return ret;
+ } else
+ return -ENODEV;
+}
+
+static int stmp3xxxbl_get_intensity(struct backlight_device *bd)
+{
+ struct platform_device *pdev = dev_get_drvdata(&bd->dev);
+ struct stmp3xxx_bl_data *data = platform_get_drvdata(pdev);
+
+ return data->current_intensity;
+}
+
+static struct backlight_ops stmp3xxxbl_ops = {
+ .get_brightness = stmp3xxxbl_get_intensity,
+ .update_status = stmp3xxxbl_set_intensity,
+};
+
+static int stmp3xxxbl_do_probe(struct stmp3xxx_bl_data *data,
+ struct stmp3xxx_platform_bl_data *pdata)
+{
+ int ret = pdata->init_bl(pdata);
+
+ if (ret)
+ goto out;
+
+ data->bd->props.power = FB_BLANK_UNBLANK;
+ data->bd->props.fb_blank = FB_BLANK_UNBLANK;
+ if (data->stmp3xxxbl_constrained) {
+ data->bd->props.max_brightness = pdata->bl_cons_intensity;
+ data->bd->props.brightness = pdata->bl_cons_intensity;
+ } else {
+ data->bd->props.max_brightness = pdata->bl_max_intensity;
+ data->bd->props.brightness = pdata->bl_default_intensity;
+ }
+
+ data->pdata = pdata;
+ stmp3xxxbl_set_intensity(data->bd);
+
+out:
+ return ret;
+}
+
+static int __init stmp3xxxbl_probe(struct platform_device *pdev)
+{
+ struct stmp3xxx_bl_data *data;
+ struct stmp3xxx_platform_bl_data *pdata = pdev->dev.platform_data;
+ int ret = 0;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ data->bd = backlight_device_register(pdev->name, &pdev->dev, pdev,
+ &stmp3xxxbl_ops);
+ if (IS_ERR(data->bd)) {
+ ret = PTR_ERR(data->bd);
+ goto out_1;
+ }
+
+ get_device(&pdev->dev);
+
+ data->nb.notifier_call = bl_callback;
+ stmp3xxx_lcdif_register_client(&data->nb);
+ platform_set_drvdata(pdev, data);
+
+ if (pdata) {
+ ret = stmp3xxxbl_do_probe(data, pdata);
+ if (ret)
+ goto out_2;
+ }
+
+ goto out;
+
+out_2:
+ put_device(&pdev->dev);
+out_1:
+ kfree(data);
+out:
+ return ret;
+}
+
+static int stmp3xxxbl_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_platform_bl_data *pdata = pdev->dev.platform_data;
+ struct stmp3xxx_bl_data *data = platform_get_drvdata(pdev);
+ struct backlight_device *bd = data->bd;
+
+ bd->props.power = FB_BLANK_POWERDOWN;
+ bd->props.fb_blank = FB_BLANK_POWERDOWN;
+ bd->props.brightness = 0;
+ data->current_intensity = bd->props.brightness;
+
+ if (pdata) {
+ pdata->set_bl_intensity(pdata, bd, data->stmp3xxxbl_suspended);
+ if (pdata->free_bl)
+ pdata->free_bl(pdata);
+ }
+ backlight_device_unregister(bd);
+ if (pdata->regulator)
+ regulator_put(pdata->regulator);
+ put_device(&pdev->dev);
+ platform_set_drvdata(pdev, NULL);
+ stmp3xxx_lcdif_unregister_client(&data->nb);
+ kfree(data);
+
+ return 0;
+}
+
+static struct platform_driver stmp3xxxbl_driver = {
+ .probe = stmp3xxxbl_probe,
+ .remove = __devexit_p(stmp3xxxbl_remove),
+ .suspend = stmp3xxxbl_suspend,
+ .resume = stmp3xxxbl_resume,
+ .driver = {
+ .name = "stmp3xxx-bl",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmp3xxx_init(void)
+{
+ return platform_driver_register(&stmp3xxxbl_driver);
+}
+
+static void __exit stmp3xxx_exit(void)
+{
+ platform_driver_unregister(&stmp3xxxbl_driver);
+}
+
+module_init(stmp3xxx_init);
+module_exit(stmp3xxx_exit);
+
+MODULE_AUTHOR("Embedded Alley Solutions, Inc <sources@embeddedalley.com>");
+MODULE_DESCRIPTION("STMP3xxx Backlight Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/wm8350_bl.c b/drivers/video/backlight/wm8350_bl.c
new file mode 100644
index 000000000000..c724025f74dc
--- /dev/null
+++ b/drivers/video/backlight/wm8350_bl.c
@@ -0,0 +1,298 @@
+/*
+ * Backlight driver for DCDC2 on i.MX32ADS board
+ *
+ * Copyright(C) 2007 Wolfson Microelectronics PLC.
+ *
+ * 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>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/wm8350/pmic.h>
+#include <linux/mfd/wm8350/bl.h>
+
+struct wm8350_backlight {
+ struct backlight_properties props;
+ struct backlight_device *device;
+ struct regulator *dcdc;
+ struct regulator *isink;
+ struct notifier_block notifier;
+ struct work_struct work;
+ struct mutex mutex;
+ int intensity;
+ int suspend;
+ int retries;
+};
+
+/* hundredths of uA, 405 = 4.05 uA */
+static const int intensity_huA[] = {
+ 405, 482, 573, 681, 810, 963, 1146, 1362, 1620, 1927, 2291, 2725,
+ 3240, 3853, 4582, 5449, 6480, 7706, 9164, 10898, 12960, 15412, 18328,
+ 21796, 25920, 30824, 36656, 43592, 51840, 61648, 73313, 87184,
+ 103680, 123297, 146626, 174368, 207360, 246594, 293251, 348737,
+ 414720, 493188, 586503, 697473, 829440, 986376, 1173005, 1394946,
+ 1658880, 1972752, 2346011, 2789892, 3317760, 3945504, 4692021,
+ 5579785, 6635520, 7891008, 9384042, 11159570, 13271040, 15782015,
+ 18768085, 22319140,
+};
+
+static void bl_work(struct work_struct *work)
+{
+ struct wm8350_backlight *bl =
+ container_of(work, struct wm8350_backlight, work);
+ struct regulator *isink = bl->isink;
+
+ mutex_lock(&bl->mutex);
+ if (bl->intensity >= 0 &&
+ bl->intensity < ARRAY_SIZE(intensity_huA)) {
+ bl->retries = 0;
+ regulator_set_current_limit(isink,
+ 0, intensity_huA[bl->intensity] / 100);
+ } else
+ printk(KERN_ERR "wm8350: Backlight intensity error\n");
+ mutex_unlock(&bl->mutex);
+}
+
+static int wm8350_bl_notifier(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct wm8350_backlight *bl =
+ container_of(self, struct wm8350_backlight, notifier);
+ struct regulator *isink = bl->isink;
+
+ if (event & REGULATOR_EVENT_UNDER_VOLTAGE)
+ printk(KERN_ERR "wm8350: BL DCDC undervoltage\n");
+ if (event & REGULATOR_EVENT_REGULATION_OUT)
+ printk(KERN_ERR "wm8350: BL ISINK out of regulation\n");
+
+ mutex_lock(&bl->mutex);
+ if (bl->retries) {
+ bl->retries--;
+ regulator_disable(isink);
+ regulator_set_current_limit(isink, 0, bl->intensity);
+ regulator_enable(isink);
+ } else {
+ printk(KERN_ERR
+ "wm8350: BL regulation retry failure - disable\n");
+ bl->intensity = 0;
+ regulator_disable(isink);
+ }
+ mutex_unlock(&bl->mutex);
+ return 0;
+}
+
+static int wm8350_bl_send_intensity(struct backlight_device *bd)
+{
+ struct wm8350_backlight *bl =
+ (struct wm8350_backlight *)dev_get_drvdata(&bd->dev);
+ int intensity = bd->props.brightness;
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bl->suspend)
+ intensity = 0;
+
+ mutex_lock(&bl->mutex);
+ bl->intensity = intensity;
+ mutex_unlock(&bl->mutex);
+ schedule_work(&bl->work);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8350_bl_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct wm8350_backlight *bl =
+ (struct wm8350_backlight *)platform_get_drvdata(pdev);
+
+ bl->suspend = 1;
+ backlight_update_status(bl->device);
+ return 0;
+}
+
+static int wm8350_bl_resume(struct platform_device *pdev)
+{
+ struct wm8350_backlight *bl =
+ (struct wm8350_backlight *)platform_get_drvdata(pdev);
+
+ bl->suspend = 0;
+ backlight_update_status(bl->device);
+ return 0;
+}
+#else
+#define wm8350_bl_suspend NULL
+#define wm8350_bl_resume NULL
+#endif
+
+static int wm8350_bl_get_intensity(struct backlight_device *bd)
+{
+ struct wm8350_backlight *bl =
+ (struct wm8350_backlight *)dev_get_drvdata(&bd->dev);
+ return bl->intensity;
+}
+
+static struct backlight_ops wm8350_bl_ops = {
+ .get_brightness = wm8350_bl_get_intensity,
+ .update_status = wm8350_bl_send_intensity,
+};
+
+static int wm8350_bl_probe(struct platform_device *pdev)
+{
+ struct regulator *isink, *dcdc;
+ struct wm8350_backlight *bl;
+ struct wm8350_bl_platform_data *pdata = pdev->dev.platform_data;
+ struct wm8350 *pmic;
+ int ret;
+
+ if (pdata == NULL) {
+ printk(KERN_ERR "%s: no platform data\n", __func__);
+ return -ENODEV;
+ }
+
+ if (pdata->isink != WM8350_ISINK_A && pdata->isink != WM8350_ISINK_B) {
+ printk(KERN_ERR "%s: invalid ISINK\n", __func__);
+ return -EINVAL;
+ }
+ if (pdata->dcdc != WM8350_DCDC_2 && pdata->dcdc != WM8350_DCDC_5) {
+ printk(KERN_ERR "%s: invalid DCDC\n", __func__);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "wm8350: backlight using %s and %s\n",
+ pdata->isink == WM8350_ISINK_A ? "ISINKA" : "ISINKB",
+ pdata->dcdc == WM8350_DCDC_2 ? "DCDC2" : "DCDC5");
+
+ isink = regulator_get(&pdev->dev,
+ pdata->isink == WM8350_ISINK_A ? "ISINKA" : "ISINKB");
+ if (IS_ERR(isink) || isink == NULL) {
+ printk(KERN_ERR "%s: cant get ISINK\n", __func__);
+ return PTR_ERR(isink);
+ }
+
+ dcdc = regulator_get(&pdev->dev,
+ pdata->dcdc == WM8350_DCDC_2 ? "DCDC2" : "DCDC5");
+ if (IS_ERR(dcdc) || dcdc == NULL) {
+ printk(KERN_ERR "%s: cant get DCDC\n", __func__);
+ regulator_put(isink);
+ return PTR_ERR(dcdc);
+ }
+
+ bl = kzalloc(sizeof(*bl), GFP_KERNEL);
+ if (bl == NULL) {
+ regulator_put(isink);
+ regulator_put(dcdc);
+ return -ENOMEM;
+ }
+
+ mutex_init(&bl->mutex);
+ INIT_WORK(&bl->work, bl_work);
+ bl->props.max_brightness = pdata->max_brightness;
+ bl->props.power = pdata->power;
+ bl->props.brightness = pdata->brightness;
+ bl->retries = pdata->retries;
+ bl->dcdc = dcdc;
+ bl->isink = isink;
+ platform_set_drvdata(pdev, bl);
+ pmic = regulator_get_drvdata(bl->isink);
+
+ wm8350_bl_ops.check_fb = pdata->check_fb;
+
+ bl->device = backlight_device_register(pdev->dev.bus_id, &pdev->dev,
+ bl, &wm8350_bl_ops);
+ if (IS_ERR(bl->device)) {
+ ret = PTR_ERR(bl->device);
+ regulator_put(dcdc);
+ regulator_put(isink);
+ kfree(bl);
+ return ret;
+ }
+
+ bl->notifier.notifier_call = wm8350_bl_notifier;
+ regulator_register_notifier(dcdc, &bl->notifier);
+ regulator_register_notifier(isink, &bl->notifier);
+ bl->device->props = bl->props;
+
+ regulator_set_current_limit(isink, 0, 20000);
+
+ wm8350_isink_set_flash(pmic, pdata->isink,
+ WM8350_ISINK_FLASH_DISABLE,
+ WM8350_ISINK_FLASH_TRIG_BIT,
+ WM8350_ISINK_FLASH_DUR_32MS,
+ WM8350_ISINK_FLASH_ON_1_00S,
+ WM8350_ISINK_FLASH_OFF_1_00S,
+ WM8350_ISINK_FLASH_MODE_EN);
+
+ wm8350_dcdc25_set_mode(pmic, pdata->dcdc,
+ WM8350_ISINK_MODE_BOOST, WM8350_ISINK_ILIM_NORMAL,
+ pdata->voltage_ramp, pdata->isink == WM8350_ISINK_A ?
+ WM8350_DC5_FBSRC_ISINKA : WM8350_DC5_FBSRC_ISINKB);
+
+ wm8350_dcdc_set_slot(pmic, pdata->dcdc, 15, 0,
+ pdata->dcdc == WM8350_DCDC_2 ?
+ WM8350_DC2_ERRACT_SHUTDOWN_CONV : WM8350_DC5_ERRACT_NONE);
+
+ regulator_enable(isink);
+ backlight_update_status(bl->device);
+ return 0;
+}
+
+static int wm8350_bl_remove(struct platform_device *pdev)
+{
+ struct wm8350_backlight *bl =
+ (struct wm8350_backlight *)platform_get_drvdata(pdev);
+ struct regulator *isink = bl->isink, *dcdc = bl->dcdc;
+
+ bl->intensity = 0;
+ backlight_update_status(bl->device);
+ schedule_work(&bl->work);
+ flush_scheduled_work();
+ backlight_device_unregister(bl->device);
+
+ regulator_set_current_limit(isink, 0, 0);
+ regulator_disable(isink);
+ regulator_unregister_notifier(isink, &bl->notifier);
+ regulator_unregister_notifier(dcdc, &bl->notifier);
+ regulator_put(isink);
+ regulator_put(dcdc);
+ return 0;
+}
+
+struct platform_driver imx32ads_backlight_driver = {
+ .driver = {
+ .name = "wm8350-bl",
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8350_bl_probe,
+ .remove = wm8350_bl_remove,
+ .suspend = wm8350_bl_suspend,
+ .resume = wm8350_bl_resume,
+};
+
+static int __devinit imx32ads_backlight_init(void)
+{
+ return platform_driver_register(&imx32ads_backlight_driver);
+}
+
+static void imx32ads_backlight_exit(void)
+{
+ platform_driver_unregister(&imx32ads_backlight_driver);
+}
+
+device_initcall_sync(imx32ads_backlight_init);
+module_exit(imx32ads_backlight_exit);
+
+MODULE_AUTHOR("Liam Girdwood <lg@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM8350 Backlight driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/hx8347fb.c b/drivers/video/hx8347fb.c
new file mode 100644
index 000000000000..1b0e66e12921
--- /dev/null
+++ b/drivers/video/hx8347fb.c
@@ -0,0 +1,482 @@
+/*
+ * linux/drivers/video/hx8347fb.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/firmware.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/irq.h>
+
+#include <video/hx8347fb.h>
+
+#include <asm/unaligned.h>
+
+#define DRV_NAME "hx8347"
+
+
+#ifdef CONFIG_PM
+
+/*
+ * Command sequences, as follows:
+ * register, data, delay (ms)
+ * Taken from a Himax application note
+ */
+u8 hxstandby_in[][3] = {
+ {0x26, 0x38, 40}, /* Display off */
+ {0x26, 0x28, 40},
+ {0x26, 0, 40},
+ {0x43, 0, 10}, /* Power off */
+ {0x1b, 0, 10},
+ {0x1b, 0x08, 10},
+ {0x1c, 0, 10},
+ {0x90, 0, 10},
+ {0x1b, 0x09, 10}, /* Into standby mode */
+ {0x19, 0x48, 0} /* Stop oscillation */
+};
+
+u8 hxstandby_out[][3] = {
+ {0x19, 0x49, 10}, /* Start oscillation */
+ {0x1b, 0x08, 0}, /* Exit standby mode */
+ {0x20, 0x40, 0}, /* Power supply setting */
+ {0x1d, 0x07, 0},
+ {0x1e, 0, 0},
+ {0x1f, 0x03, 0},
+ {0x44, 0x20, 0},
+ {0x45, 0x0e, 10},
+ {0x1c, 0x04, 20},
+ {0x1b, 0x18, 40},
+ {0x1b, 0x10, 40},
+ {0x43, 0x80, 100},
+ {0x90, 0x7f, 40}, /* Display on setting */
+ {0x26, 0x04, 40},
+ {0x26, 0x24, 0},
+ {0x26, 0x2c, 40},
+ {0x26, 0x3c, 0},
+};
+
+static void hx8347_standby(struct hx8347fb_par *par, int standby)
+{
+ int i;
+
+ if (standby) {
+ for (i = 0; i < (sizeof(hxstandby_in) / 3); i++) {
+ par->pdata->wr_reg(par, hxstandby_in[i][0], (u16)hxstandby_in[i][1]);
+ mdelay(hxstandby_in[i][2]);
+ }
+ } else {
+ for (i = 0; i < (sizeof(hxstandby_out) / 3); i++) {
+ par->pdata->wr_reg(par, hxstandby_out[i][0], (u16)hxstandby_out[i][1]);
+ mdelay(hxstandby_out[i][2]);
+ }
+ }
+}
+#endif
+
+static void hx8347_dpy_update(struct hx8347fb_par *par)
+{
+ u16 *buf = (u16 __force *)par->info->screen_base;
+
+ par->pdata->set_idx(par, SRAM_WR_CTRL);
+ par->pdata->wr_data(par, buf, par->info->fix.smem_len);
+}
+
+/* this is called back from the deferred io workqueue */
+static void hx8347_dpy_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ hx8347_dpy_update(info->par);
+}
+
+static void hx8347_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect)
+{
+ struct hx8347fb_par *par = info->par;
+
+ sys_fillrect(info, rect);
+ hx8347_dpy_update(par);
+}
+
+static void hx8347_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ struct hx8347fb_par *par = info->par;
+
+ sys_copyarea(info, area);
+ hx8347_dpy_update(par);
+}
+
+static void hx8347_imageblit(struct fb_info *info,
+ const struct fb_image *image)
+{
+ struct hx8347fb_par *par = info->par;
+
+ sys_imageblit(info, image);
+ hx8347_dpy_update(par);
+}
+
+/*
+ * this is the slow path from userspace. they can seek and write to
+ * the fb. it is based on fb_sys_write
+ */
+static ssize_t hx8347_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hx8347fb_par *par = info->par;
+ unsigned long p = *ppos;
+ void *dst;
+ int err = 0;
+ unsigned long total_size;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return -EPERM;
+
+ total_size = info->fix.smem_len;
+
+ if (p > total_size)
+ return -EFBIG;
+
+ if (count > total_size) {
+ err = -EFBIG;
+ count = total_size;
+ }
+
+ if (count + p > total_size) {
+ if (!err)
+ err = -ENOSPC;
+
+ count = total_size - p;
+ }
+
+ dst = (void __force *)(info->screen_base + p);
+
+ if (copy_from_user(dst, buf, count))
+ err = -EFAULT;
+
+ if (!err)
+ *ppos += count;
+
+ hx8347_dpy_update(par);
+
+ return (err) ? err : count;
+}
+
+static int hx8347_set_par(struct fb_info *fb)
+{
+ fb->fix.visual = FB_VISUAL_TRUECOLOR;
+ fb->fix.line_length = fb->var.width * fb->var.bits_per_pixel / 8;
+
+ return 0;
+}
+
+static int hx8347_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *fb)
+{
+ struct hx8347fb_par *par = fb->par;
+
+ if (regno > fb->var.bits_per_pixel)
+ return -EINVAL;
+
+ par->pdata->pseudo_pal[regno] = (red & 0xf800) |
+ ((green & 0xfc00) >> 5) | ((green & 0xf800) >> 11);
+
+ return 0;
+}
+
+static struct fb_ops hx8347fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_set_par = hx8347_set_par,
+ .fb_setcolreg = hx8347_setcolreg,
+ .fb_read = fb_sys_read,
+ .fb_write = hx8347_write,
+ .fb_fillrect = hx8347_fillrect,
+ .fb_copyarea = hx8347_copyarea,
+ .fb_imageblit = hx8347_imageblit,
+};
+
+static struct fb_deferred_io hx8347fb_defio = {
+ .delay = HZ/20,
+ .deferred_io = hx8347_dpy_deferred_io,
+};
+
+static int __devinit hx8347_probe(struct platform_device *pdev)
+{
+ struct fb_info *info;
+ struct hx8347fb_pdata *pdata;
+ struct hx8347fb_par *par;
+ struct resource *cmdregs = NULL;
+ struct resource *dataregs = NULL;
+ unsigned char *videomemory;
+ int ret;
+
+ /* pick up board specific routines */
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -EINVAL;
+
+ /* try to count device specific driver, if can't, platform recalls */
+ if (!try_module_get(pdata->owner))
+ return -ENODEV;
+
+ cmdregs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!cmdregs) {
+ dev_err(&pdev->dev, "%s: unable to get iomem resources\n", __func__);
+ ret = -ENXIO;
+ goto err_resources;
+ }
+ dataregs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!dataregs) {
+ dev_err(&pdev->dev, "%s: unable to get iomem resources\n", __func__);
+ ret = -ENXIO;
+ goto err_resources;
+ }
+
+ pdata->irq = platform_get_irq(pdev, 0);
+ if (pdata->irq < 0) {
+ dev_err(&pdev->dev, "%s: unable to get irq\n", __func__);
+ ret = pdata->irq;
+ goto err_resources;
+ }
+
+ if (!request_mem_region(cmdregs[0].start, cmdregs[0].end - cmdregs[0].start + 1, "hx8347fb cmd")) {
+ dev_dbg(&pdev->dev, "%s: request_mem_region failed\n", __func__);
+ ret = -EBUSY;
+ goto err_resources;
+ }
+
+ if (!request_mem_region(dataregs[0].start, dataregs[0].end - dataregs[0].start + 1, "hx8347fb data")) {
+ dev_dbg(&pdev->dev, "%s:request_mem_region failed\n", __func__);
+ ret = -EBUSY;
+ goto err_request_mem;
+ }
+ info = framebuffer_alloc(sizeof(struct hx8347fb_par), &pdev->dev);
+ if (!info) {
+ dev_dbg(&pdev->dev, "%s: unable to allocate frambebuffer device\n", __func__);
+ ret = -ENOMEM;
+ goto err_fballoc;
+ }
+ par = info->par;
+ par->mmio_cmd = (void __iomem *)ioremap_nocache(cmdregs[0].start, cmdregs[0].end - cmdregs[0].start + 1);
+ if (!par->mmio_cmd) {
+ dev_err(&pdev->dev, "%s: error ioremap cmd\n", __func__);
+ ret = -EBUSY;
+ goto err_map_cmd;
+ }
+ par->mmio_data = (void __iomem *)ioremap_nocache(dataregs[0].start, dataregs[0].end - dataregs[0].start + 1);
+ if (!par->mmio_data) {
+ dev_dbg(&pdev->dev, "%s: error ioremap data\n", __func__);
+ ret = -EBUSY;
+ goto err_map_data;
+ }
+
+ info->fix.smem_len = pdata->xres * pdata->yres * pdata->bits_per_pixel / 8;
+ if (!pdata->usedma) {
+ if ((videomemory = vmalloc(info->fix.smem_len)) == NULL) {
+ dev_err(&pdev->dev, "%s: error vmalloc\n", __func__);
+ ret = -ENOMEM;
+ goto err_vmalloc;
+ }
+ } else {
+ pdev->dev.coherent_dma_mask = (u32)-1;
+ videomemory = dma_alloc_writecombine(&pdev->dev, info->fix.smem_len,
+ &par->fb_p, GFP_KERNEL);
+ if (!videomemory) {
+ dev_dbg(&pdev->dev, "%s: err_alloc_fb\n", __func__);
+ ret = -ENOMEM;
+ goto err_dma_alloc;
+ }
+ }
+
+ memset(videomemory, 0, info->fix.smem_len);
+ info->screen_base = (char __force __iomem *)videomemory;
+
+ info->fbops = &hx8347fb_ops;
+ info->pseudo_palette = &pdata->pseudo_pal[0];
+ strcpy(info->fix.id, DRV_NAME);
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fix.smem_start = dataregs[0].start;
+ info->fix.line_length = pdata->xres * pdata->bits_per_pixel / 8;
+
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.height = pdata->yres;
+ info->var.yres = pdata->yres;
+ info->var.yres_virtual = pdata->yres;
+ info->var.width = pdata->xres;
+ info->var.xres = pdata->xres;
+ info->var.xres_virtual = pdata->xres;
+ info->var.vmode = FB_VMODE_NONINTERLACED;
+ info->var.bits_per_pixel = pdata->bits_per_pixel;
+
+ info->var.red.offset = 11;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 0;
+ info->var.red.length = 5;
+ info->var.green.length = 6;
+ info->var.blue.length = 5;
+
+ par->info = info;
+ par->pdata = pdata;
+
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->fbdefio = &hx8347fb_defio;
+ fb_deferred_io_init(info);
+
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "%s: unable to register fb device\n", DRV_NAME);
+ goto err_fbreg;
+ }
+
+ /* Verify that the minimal hooks have been setup */
+ if (!par->pdata->init || !par->pdata->wr_reg ||
+ !par->pdata->set_idx || !par->pdata->wr_data)
+ goto err_init;
+
+ ret = par->pdata->init(par);
+ if (ret < 0)
+ goto err_init;
+
+ /* Enable backlight */
+ if (par->pdata->bl_enable)
+ par->pdata->bl_enable(par, 1);
+
+ platform_set_drvdata(pdev, info);
+ printk(KERN_INFO
+ "fb%d: HX8347 frame buffer device, using %dK of video memory\n",
+ info->node, info->fix.smem_len >> 10);
+
+ return 0;
+
+err_init:
+ unregister_framebuffer(info);
+err_fbreg:
+ if (!pdata->usedma)
+ vfree(videomemory);
+err_vmalloc:
+err_dma_alloc:
+ iounmap(par->mmio_data);
+err_map_data:
+ iounmap(par->mmio_cmd);
+err_map_cmd:
+ framebuffer_release(info);
+err_fballoc:
+ release_mem_region(dataregs[0].start, dataregs[0].end - dataregs[0].start + 1);
+err_request_mem:
+ release_mem_region(cmdregs[0].start, cmdregs[0].end - cmdregs[0].start + 1);
+err_resources:
+ module_put(pdata->owner);
+
+ return ret;
+}
+
+static int __devexit hx8347_remove(struct platform_device *dev)
+{
+ struct fb_info *info = platform_get_drvdata(dev);
+ struct hx8347fb_par *par = info->par;
+
+ if (info) {
+ if (par->pdata->bl_enable)
+ par->pdata->bl_enable(par, 0);
+ unregister_framebuffer(info);
+ fb_deferred_io_cleanup(info);
+ fb_dealloc_cmap(&info->cmap);
+ if (!par->pdata->usedma)
+ vfree((void __force *)info->screen_base);
+ iounmap(par->mmio_data);
+ iounmap(par->mmio_cmd);
+
+ if (par->pdata->cleanup)
+ par->pdata->cleanup(par);
+
+ module_put(par->pdata->owner);
+ dev_dbg(&dev->dev, "calling release\n");
+ framebuffer_release(info);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/* suspend and resume support for the lcd controller */
+static int hx8347_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct fb_info *info = platform_get_drvdata(dev);
+ struct hx8347fb_par *par = info->par;
+
+ if (state.event == PM_EVENT_SUSPEND) {
+ /* Disable the backlight set standby mode */
+ if (par->pdata->bl_enable)
+ par->pdata->bl_enable(par, 0);
+ mdelay(1);
+ hx8347_standby(par, 1);
+ }
+
+ return 0;
+}
+
+static int hx8347_resume(struct platform_device *dev)
+{
+ struct fb_info *info = platform_get_drvdata(dev);
+ struct hx8347fb_par *par = info->par;
+
+ /* Configure the display to resume and enable the backlight */
+ hx8347_standby(par, 0);
+ mdelay(10);
+ if (par->pdata->bl_enable)
+ par->pdata->bl_enable(par, 1);
+
+ return 0;
+}
+#else
+#define hx8347_suspend NULL
+#define hx8347_resume NULL
+#endif
+
+
+static struct platform_driver hx8347_driver = {
+ .probe = hx8347_probe,
+ .remove = hx8347_remove,
+ .suspend = hx8347_suspend,
+ .resume = hx8347_resume,
+
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "hx8347",
+ },
+};
+
+static int __init hx8347_init(void)
+{
+ return platform_driver_register(&hx8347_driver);
+}
+
+static void __exit hx8347_exit(void)
+{
+ platform_driver_unregister(&hx8347_driver);
+}
+
+module_init(hx8347_init);
+module_exit(hx8347_exit);
+
+MODULE_DESCRIPTION("Framebuffer driver for the HX8347 controller");
+MODULE_AUTHOR("Pedro Perez de Heredia");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index d3c3af53a290..4fc9bcd1b6e1 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -220,6 +220,10 @@ static const struct fb_videomode modedb[] = {
NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2,
0, FB_VMODE_DOUBLE
}, {
+ /* 480x272 @ 60 Hz, 31.5 kHz hsync */
+ NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1,
+ 0, FB_VMODE_DOUBLE
+ }, {
/* 400x300 @ 56 Hz, 35.2 kHz hsync, 4:3 aspect ratio */
NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1,
0, FB_VMODE_DOUBLE
diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig
new file mode 100644
index 000000000000..709cfcfc45f4
--- /dev/null
+++ b/drivers/video/mxc/Kconfig
@@ -0,0 +1,87 @@
+config FB_MXC
+ tristate "MXC Framebuffer support"
+ depends on FB && (MXC_IPU || ARCH_MX21 || ARCH_MX27 || ARCH_MX25)
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select FB_MODE_HELPERS
+ default y
+ help
+ This is a framebuffer device for the MXC LCD Controller.
+ See <http://www.linux-fbdev.org/> for information on framebuffer
+ devices.
+
+ If you plan to use the LCD display with your MXC system, say
+ Y here.
+
+config FB_MXC_SYNC_PANEL
+ depends on FB_MXC
+ tristate "Synchronous Panel Framebuffer"
+ default y
+
+config FB_MXC_EPSON_VGA_SYNC_PANEL
+ depends on FB_MXC_SYNC_PANEL
+ tristate "Epson VGA Panel"
+ default n
+
+config FB_MXC_TVOUT_TVE
+ tristate "MXC TVE TV Out Encoder"
+ depends on FB_MXC_SYNC_PANEL
+ depends on MXC_IPU_V3
+
+config FB_MXC_CLAA_WVGA_SYNC_PANEL
+ depends on FB_MXC_SYNC_PANEL
+ tristate "CLAA WVGA Panel"
+
+config FB_MXC_CH7026
+ depends on FB_MXC_SYNC_PANEL
+ tristate "Chrontel CH7026 VGA Interface Chip"
+
+config FB_MXC_TVOUT
+ bool "FS453 TV Out Encoder"
+ depends on FB_MXC_SYNC_PANEL
+
+config FB_MXC_TVOUT_CH7024
+ tristate "CH7024 TV Out Encoder"
+ depends on FB_MXC_SYNC_PANEL
+
+config FB_MXC_LOW_PWR_DISPLAY
+ bool "Low Power Display Refresh Mode"
+ depends on FB_MXC_SYNC_PANEL && MXC_FB_IRAM
+ default y
+
+config FB_MXC_INTERNAL_MEM
+ bool "Framebuffer in Internal RAM"
+ depends on FB_MXC_SYNC_PANEL && MXC_FB_IRAM
+ default y
+
+config FB_MXC_ASYNC_PANEL
+ depends on FB_MXC
+ bool "Asynchronous Panels"
+ default n
+
+menu "Asynchronous Panel Type"
+ depends on FB_MXC_ASYNC_PANEL && FB_MXC
+
+config FB_MXC_EPSON_PANEL
+ depends on FB_MXC_ASYNC_PANEL
+ default n
+ bool "Epson 176x220 Panel"
+
+endmenu
+
+choice
+ prompt "Async Panel Interface Type"
+ depends on FB_MXC_ASYNC_PANEL && FB_MXC
+ default FB_MXC_ASYNC_PANEL_IFC_16_BIT
+
+config FB_MXC_ASYNC_PANEL_IFC_8_BIT
+ bool "8-bit Parallel Bus Interface"
+
+config FB_MXC_ASYNC_PANEL_IFC_16_BIT
+ bool "16-bit Parallel Bus Interface"
+
+config FB_MXC_ASYNC_PANEL_IFC_SERIAL
+ bool "Serial Bus Interface"
+
+endchoice
diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile
new file mode 100644
index 000000000000..a1bd24b967a5
--- /dev/null
+++ b/drivers/video/mxc/Makefile
@@ -0,0 +1,22 @@
+ifeq ($(CONFIG_ARCH_MX21)$(CONFIG_ARCH_MX27)$(CONFIG_ARCH_MX25),y)
+ obj-$(CONFIG_FB_MXC_TVOUT) += fs453.o
+ obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mx2fb.o mxcfb_modedb.o
+ obj-$(CONFIG_FB_MXC_EPSON_PANEL) += mx2fb_epson.o
+else
+ifeq ($(CONFIG_MXC_IPU_V1),y)
+ obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxcfb.o mxcfb_modedb.o
+else
+ obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_ipuv3_fb.o
+endif
+ obj-$(CONFIG_FB_MXC_TVOUT) += fs453.o
+ obj-$(CONFIG_FB_MXC_EPSON_PANEL) += mxcfb_epson.o
+ obj-$(CONFIG_FB_MXC_EPSON_QVGA_PANEL) += mxcfb_epson_qvga.o
+ obj-$(CONFIG_FB_MXC_TOSHIBA_QVGA_PANEL) += mxcfb_toshiba_qvga.o
+ obj-$(CONFIG_FB_MXC_SHARP_128_PANEL) += mxcfb_sharp_128x128.o
+endif
+obj-$(CONFIG_FB_MXC_EPSON_VGA_SYNC_PANEL) += mxcfb_epson_vga.o
+obj-$(CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL) += mxcfb_claa_wvga.o
+obj-$(CONFIG_FB_MXC_TVOUT_CH7024) += ch7024.o
+obj-$(CONFIG_FB_MXC_TVOUT_TVE) += tve.o
+obj-$(CONFIG_FB_MXC_CH7026) += mxcfb_ch7026.o
+obj-$(CONFIG_FB_MODE_HELPERS) += mxc_edid.o
diff --git a/drivers/video/mxc/ch7024.c b/drivers/video/mxc/ch7024.c
new file mode 100644
index 000000000000..35ffb7570c2b
--- /dev/null
+++ b/drivers/video/mxc/ch7024.c
@@ -0,0 +1,866 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ch7024.c
+ * @brief Driver for CH7024 TV encoder
+ *
+ * @ingroup Framebuffer
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sysfs.h>
+#include <linux/mxcfb.h>
+#include <linux/regulator/consumer.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <mach/gpio.h>
+#include <mach/hw_events.h>
+
+/*!
+ * CH7024 registers
+ */
+#define CH7024_DEVID 0x00
+#define CH7024_REVID 0x01
+#define CH7024_PG 0x02
+
+#define CH7024_RESET 0x03
+#define CH7024_POWER 0x04
+#define CH7024_TVHUE 0x05
+#define CH7024_TVSAT 0x06
+#define CH7024_TVCTA 0x07
+#define CH7024_TVBRI 0x08
+#define CH7024_TVSHARP 0x09
+#define CH7024_OUT_FMT 0x0A
+#define CH7024_XTAL 0x0B
+#define CH7024_IDF1 0x0C
+#define CH7024_IDF2 0x0D
+#define CH7024_SYNC 0x0E
+#define CH7024_TVFILTER1 0x0F
+#define CH7024_TVFILTER2 0x10
+#define CH7024_IN_TIMING1 0x11
+#define CH7024_IN_TIMING2 0x12
+#define CH7024_IN_TIMING3 0x13
+#define CH7024_IN_TIMING4 0x14
+#define CH7024_IN_TIMING5 0x15
+#define CH7024_IN_TIMING6 0x16
+#define CH7024_IN_TIMING7 0x17
+#define CH7024_IN_TIMING8 0x18
+#define CH7024_IN_TIMING9 0x19
+#define CH7024_IN_TIMING10 0x1A
+#define CH7024_IN_TIMING11 0x1B
+#define CH7024_ACIV 0x1C
+#define CH7024_CLK_TREE 0x1D
+#define CH7024_OUT_TIMING1 0x1E
+#define CH7024_OUT_TIMING2 0x1F
+#define CH7024_V_POS1 0x20
+#define CH7024_V_POS2 0x21
+#define CH7024_H_POS1 0x22
+#define CH7024_H_POS2 0x23
+#define CH7024_PCLK_A1 0x24
+#define CH7024_PCLK_A2 0x25
+#define CH7024_PCLK_A3 0x26
+#define CH7024_PCLK_A4 0x27
+#define CH7024_CLK_P1 0x28
+#define CH7024_CLK_P2 0x29
+#define CH7024_CLK_P3 0x2A
+#define CH7024_CLK_N1 0x2B
+#define CH7024_CLK_N2 0x2C
+#define CH7024_CLK_N3 0x2D
+#define CH7024_CLK_T 0x2E
+#define CH7024_PLL1 0x2F
+#define CH7024_PLL2 0x30
+#define CH7024_PLL3 0x31
+#define CH7024_SC_FREQ1 0x34
+#define CH7024_SC_FREQ2 0x35
+#define CH7024_SC_FREQ3 0x36
+#define CH7024_SC_FREQ4 0x37
+#define CH7024_DAC_TRIM 0x62
+#define CH7024_DATA_IO 0x63
+#define CH7024_ATT_DISP 0x7E
+
+/*!
+ * CH7024 register values
+ */
+/* video output formats */
+#define CH7024_VOS_NTSC_M 0x0
+#define CH7024_VOS_NTSC_J 0x1
+#define CH7024_VOS_NTSC_443 0x2
+#define CH7024_VOS_PAL_BDGHKI 0x3
+#define CH7024_VOS_PAL_M 0x4
+#define CH7024_VOS_PAL_N 0x5
+#define CH7024_VOS_PAL_NC 0x6
+#define CH7024_VOS_PAL_60 0x7
+/* crystal predefined */
+#define CH7024_XTAL_13MHZ 0x4
+#define CH7024_XTAL_26MHZ 0xB
+
+/* chip ID */
+#define CH7024_DEVICE_ID 0x45
+
+/* clock source define */
+#define CLK_HIGH 0
+#define CLK_LOW 1
+
+/* CH7024 presets structs */
+struct ch7024_clock {
+ u32 A;
+ u32 P;
+ u32 N;
+ u32 T;
+ u8 PLLN1;
+ u8 PLLN2;
+ u8 PLLN3;
+};
+
+struct ch7024_input_timing {
+ u32 HTI;
+ u32 VTI;
+ u32 HAI;
+ u32 VAI;
+ u32 HW;
+ u32 HO;
+ u32 VW;
+ u32 VO;
+ u32 VOS;
+};
+
+#define TVOUT_FMT_OFF 0
+#define TVOUT_FMT_NTSC 1
+#define TVOUT_FMT_PAL 2
+
+static int enabled; /* enable power on or not */
+static int pm_status; /* status before suspend */
+
+static struct i2c_client *ch7024_client;
+static struct fb_info *ch7024_fbi;
+static int ch7024_cur_mode;
+static u32 detect_gpio;
+static struct regulator *io_reg;
+static struct regulator *core_reg;
+static struct regulator *analog_reg;
+
+static void hp_detect_wq_handler(struct work_struct *);
+DECLARE_DELAYED_WORK(ch7024_wq, hp_detect_wq_handler);
+
+static inline int ch7024_read_reg(u8 reg)
+{
+ return i2c_smbus_read_byte_data(ch7024_client, reg);
+}
+
+static inline int ch7024_write_reg(u8 reg, u8 word)
+{
+ return i2c_smbus_write_byte_data(ch7024_client, reg, word);
+}
+
+/**
+ * PAL B/D/G/H/K/I clock and timting structures
+ */
+static struct ch7024_clock ch7024_clk_pal = {
+ .A = 0x0,
+ .P = 0x36b00,
+ .N = 0x41eb00,
+ .T = 0x3f,
+ .PLLN1 = 0x0,
+ .PLLN2 = 0x1b,
+ .PLLN3 = 0x12,
+};
+
+static struct ch7024_input_timing ch7024_timing_pal = {
+ .HTI = 950,
+ .VTI = 560,
+ .HAI = 640,
+ .VAI = 480,
+ .HW = 60,
+ .HO = 250,
+ .VW = 40,
+ .VO = 40,
+ .VOS = CH7024_VOS_PAL_BDGHKI,
+};
+
+/**
+ * NTSC_M clock and timting structures
+ * TODO: change values to work well.
+ */
+static struct ch7024_clock ch7024_clk_ntsc = {
+ .A = 0x0,
+ .P = 0x2ac90,
+ .N = 0x36fc90,
+ .T = 0x3f,
+ .PLLN1 = 0x0,
+ .PLLN2 = 0x1b,
+ .PLLN3 = 0x12,
+};
+
+static struct ch7024_input_timing ch7024_timing_ntsc = {
+ .HTI = 801,
+ .VTI = 554,
+ .HAI = 640,
+ .VAI = 480,
+ .HW = 60,
+ .HO = 101,
+ .VW = 20,
+ .VO = 54,
+ .VOS = CH7024_VOS_NTSC_M,
+};
+
+static struct fb_videomode video_modes[] = {
+ {
+ /* NTSC TV output */
+ "TV-NTSC", 60, 640, 480, 37594,
+ 0, 101,
+ 0, 54,
+ 60, 20,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* PAL TV output */
+ "TV-PAL", 50, 640, 480, 37594,
+ 0, 250,
+ 0, 40,
+ 60, 40,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+};
+
+/**
+ * ch7024_setup
+ * initial the CH7024 chipset by setting register
+ * @param:
+ * vos: output video format
+ * @return:
+ * 0 successful
+ * otherwise failed
+ */
+static int ch7024_setup(int vos)
+{
+ struct ch7024_input_timing *ch_timing;
+ struct ch7024_clock *ch_clk;
+#ifdef DEBUG_CH7024
+ int i, val;
+#endif
+
+ /* select output video format */
+ if (vos == TVOUT_FMT_PAL) {
+ ch_timing = &ch7024_timing_pal;
+ ch_clk = &ch7024_clk_pal;
+ pr_debug("CH7024: change to PAL video\n");
+ } else if (vos == TVOUT_FMT_NTSC) {
+ ch_timing = &ch7024_timing_ntsc;
+ ch_clk = &ch7024_clk_ntsc;
+ pr_debug("CH7024: change to NTSC video\n");
+ } else {
+
+ pr_debug("CH7024: no such video format.\n");
+ return -EINVAL;
+ }
+ ch7024_write_reg(CH7024_RESET, 0x0);
+ ch7024_write_reg(CH7024_RESET, 0x3);
+
+ ch7024_write_reg(CH7024_POWER, 0x0C); /* power on, disable DAC */
+ ch7024_write_reg(CH7024_XTAL, CH7024_XTAL_26MHZ);
+ ch7024_write_reg(CH7024_SYNC, 0x0D); /* SLAVE mode, and TTL */
+ ch7024_write_reg(CH7024_IDF1, 0x00);
+ ch7024_write_reg(CH7024_TVFILTER1, 0x00); /* set XCH=0 */
+ ch7024_write_reg(CH7024_CLK_TREE, 0x9E); /* Invert input clk */
+
+ /* set input clock and divider */
+ /* set PLL */
+ ch7024_write_reg(CH7024_PLL1, ch_clk->PLLN1);
+ ch7024_write_reg(CH7024_PLL2, ch_clk->PLLN2);
+ ch7024_write_reg(CH7024_PLL3, ch_clk->PLLN3);
+ /* set A register */
+ ch7024_write_reg(CH7024_PCLK_A1, (ch_clk->A >> 24) & 0xFF);
+ ch7024_write_reg(CH7024_PCLK_A2, (ch_clk->A >> 16) & 0xFF);
+ ch7024_write_reg(CH7024_PCLK_A3, (ch_clk->A >> 8) & 0xFF);
+ ch7024_write_reg(CH7024_PCLK_A4, ch_clk->A & 0xFF);
+ /* set P register */
+ ch7024_write_reg(CH7024_CLK_P1, (ch_clk->P >> 16) & 0xFF);
+ ch7024_write_reg(CH7024_CLK_P2, (ch_clk->P >> 8) & 0xFF);
+ ch7024_write_reg(CH7024_CLK_P3, ch_clk->P & 0xFF);
+ /* set N register */
+ ch7024_write_reg(CH7024_CLK_N1, (ch_clk->N >> 16) & 0xFF);
+ ch7024_write_reg(CH7024_CLK_N2, (ch_clk->N >> 8) & 0xFF);
+ ch7024_write_reg(CH7024_CLK_N3, ch_clk->N & 0xFF);
+ /* set T register */
+ ch7024_write_reg(CH7024_CLK_T, ch_clk->T & 0xFF);
+
+ /* set sub-carrier frequency generation method */
+ ch7024_write_reg(CH7024_ACIV, 0x00); /* ACIV = 0, automatical SCF */
+ /* TV out pattern and DAC switch */
+ ch7024_write_reg(CH7024_OUT_FMT, (0x10 | ch_timing->VOS) & 0xFF);
+
+ /* input settings */
+ /* input format, RGB666 */
+ ch7024_write_reg(CH7024_IDF2, 0x02);
+ /* HAI/HTI VAI */
+ ch7024_write_reg(CH7024_IN_TIMING1, ((ch_timing->HTI >> 5) & 0x38) |
+ ((ch_timing->HAI >> 8) & 0x07));
+ ch7024_write_reg(CH7024_IN_TIMING2, ch_timing->HAI & 0xFF);
+ ch7024_write_reg(CH7024_IN_TIMING8, ch_timing->VAI & 0xFF);
+ /* HTI VTI */
+ ch7024_write_reg(CH7024_IN_TIMING3, ch_timing->HTI & 0xFF);
+ ch7024_write_reg(CH7024_IN_TIMING9, ch_timing->VTI & 0xFF);
+ /* HW/HO(h) VW */
+ ch7024_write_reg(CH7024_IN_TIMING4, ((ch_timing->HW >> 5) & 0x18) |
+ ((ch_timing->HO >> 8) & 0x7));
+ ch7024_write_reg(CH7024_IN_TIMING6, ch_timing->HW & 0xFF);
+ ch7024_write_reg(CH7024_IN_TIMING11, ch_timing->VW & 0x3F);
+ /* HO(l) VO/VAI/VTI */
+ ch7024_write_reg(CH7024_IN_TIMING5, ch_timing->HO & 0xFF);
+ ch7024_write_reg(CH7024_IN_TIMING7, ((ch_timing->VO >> 4) & 0x30) |
+ ((ch_timing->VTI >> 6) & 0x0C) |
+ ((ch_timing->VAI >> 8) & 0x03));
+ ch7024_write_reg(CH7024_IN_TIMING10, ch_timing->VO & 0xFF);
+
+ /* adjust the brightness */
+ ch7024_write_reg(CH7024_TVBRI, 0x90);
+
+ ch7024_write_reg(CH7024_OUT_TIMING1, 0x4);
+ ch7024_write_reg(CH7024_OUT_TIMING2, 0xe0);
+
+ if (vos == TVOUT_FMT_PAL) {
+ ch7024_write_reg(CH7024_V_POS1, 0x03);
+ ch7024_write_reg(CH7024_V_POS2, 0x7d);
+ } else {
+ ch7024_write_reg(CH7024_V_POS1, 0x02);
+ ch7024_write_reg(CH7024_V_POS2, 0x7b);
+ }
+
+ ch7024_write_reg(CH7024_POWER, 0x00);
+
+#ifdef DEBUG_CH7024
+ for (i = 0; i < CH7024_SC_FREQ4; i++) {
+
+ val = ch7024_read_reg(i);
+ pr_debug("CH7024, reg[0x%x] = %x\n", i, val);
+ }
+#endif
+ return 0;
+}
+
+/**
+ * ch7024_enable
+ * Enable the ch7024 Power to begin TV encoder
+ */
+static int ch7024_enable(void)
+{
+ int en = enabled;
+
+ if (!enabled) {
+ regulator_enable(core_reg);
+ regulator_enable(io_reg);
+ regulator_enable(analog_reg);
+ msleep(200);
+ enabled = 1;
+ ch7024_write_reg(CH7024_POWER, 0x00);
+ pr_debug("CH7024 power on.\n");
+ }
+ return en;
+}
+
+/**
+ * ch7024_disable
+ * Disable the ch7024 Power to stop TV encoder
+ */
+static void ch7024_disable(void)
+{
+ if (enabled) {
+ enabled = 0;
+ ch7024_write_reg(CH7024_POWER, 0x0D);
+ regulator_disable(analog_reg);
+ regulator_disable(io_reg);
+ regulator_disable(core_reg);
+ pr_debug("CH7024 power off.\n");
+ }
+}
+
+static int ch7024_detect(void)
+{
+ int en;
+ int detect = 0;
+
+ if (gpio_get_value(detect_gpio) == 1) {
+ set_irq_type(ch7024_client->irq, IRQF_TRIGGER_FALLING);
+
+ en = ch7024_enable();
+
+ ch7024_write_reg(CH7024_DAC_TRIM, 0xB4);
+ msleep(50);
+ detect = ch7024_read_reg(CH7024_ATT_DISP) & 0x3;
+ ch7024_write_reg(CH7024_DAC_TRIM, 0x34);
+
+ if (!en)
+ ch7024_disable();
+ } else {
+ set_irq_type(ch7024_client->irq, IRQF_TRIGGER_RISING);
+ }
+ dev_dbg(&ch7024_client->dev, "detect = %d\n", detect);
+ return (detect);
+}
+
+static irqreturn_t hp_detect_handler(int irq, void *data)
+{
+ disable_irq(irq);
+ schedule_delayed_work(&ch7024_wq, 50);
+
+ return IRQ_HANDLED;
+}
+
+static void hp_detect_wq_handler(struct work_struct *work)
+{
+ int detect;
+ struct mxc_hw_event event = { HWE_PHONEJACK_PLUG, 0 };
+
+ detect = ch7024_detect();
+
+ enable_irq(ch7024_client->irq);
+
+ sysfs_notify(&ch7024_client->dev.kobj, NULL, "headphone");
+
+ /* send hw event by netlink */
+ event.args = detect;
+ hw_event_send(1, &event);
+}
+
+int ch7024_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+ struct fb_info *fbi = event->info;
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ if ((ch7024_fbi != NULL) || strcmp(fbi->fix.id, "DISP3 BG"))
+ break;
+
+ ch7024_fbi = fbi;
+ fb_add_videomode(&video_modes[0], &ch7024_fbi->modelist);
+ fb_add_videomode(&video_modes[1], &ch7024_fbi->modelist);
+ break;
+ case FB_EVENT_MODE_CHANGE:
+ if (ch7024_fbi != fbi)
+ break;
+
+ if (!fbi->mode) {
+ ch7024_disable();
+ ch7024_cur_mode = TVOUT_FMT_OFF;
+ return 0;
+ }
+
+ if (fb_mode_is_equal(fbi->mode, &video_modes[0])) {
+ ch7024_cur_mode = TVOUT_FMT_NTSC;
+ ch7024_enable();
+ ch7024_setup(TVOUT_FMT_NTSC);
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes[1])) {
+ ch7024_cur_mode = TVOUT_FMT_PAL;
+ ch7024_enable();
+ ch7024_setup(TVOUT_FMT_PAL);
+ } else {
+ ch7024_disable();
+ ch7024_cur_mode = TVOUT_FMT_OFF;
+ return 0;
+ }
+ break;
+ case FB_EVENT_BLANK:
+ if ((ch7024_fbi != fbi) || (ch7024_cur_mode == TVOUT_FMT_OFF))
+ return 0;
+
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ ch7024_enable();
+ ch7024_setup(ch7024_cur_mode);
+ } else {
+ ch7024_disable();
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = ch7024_fb_event,
+};
+
+static ssize_t show_headphone(struct device_driver *dev, char *buf)
+{
+ int detect;
+
+ detect = ch7024_detect();
+
+ if (detect == 0) {
+ strcpy(buf, "none\n");
+ } else if (detect == 1) {
+ strcpy(buf, "cvbs\n");
+ } else {
+ strcpy(buf, "headset\n");
+ }
+
+ return strlen(buf);
+}
+
+DRIVER_ATTR(headphone, 0644, show_headphone, NULL);
+
+static ssize_t show_brightness(struct device_driver *dev, char *buf)
+{
+ u32 reg;
+ reg = ch7024_read_reg(CH7024_TVBRI);
+ return snprintf(buf, PAGE_SIZE, "%u", reg);
+}
+
+static ssize_t store_brightness(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ char *endp;
+ int brightness = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ if (brightness > 255)
+ brightness = 255;
+
+ ch7024_write_reg(CH7024_TVBRI, brightness);
+
+ return count;
+}
+
+DRIVER_ATTR(brightness, 0644, show_brightness, store_brightness);
+
+static ssize_t show_contrast(struct device_driver *dev, char *buf)
+{
+ u32 reg;
+ reg = ch7024_read_reg(CH7024_TVCTA);
+
+ reg *= 2; /* Scale to 0 - 255 */
+
+ return snprintf(buf, PAGE_SIZE, "%u", reg);
+}
+
+static ssize_t store_contrast(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ char *endp;
+ int contrast = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ contrast /= 2;
+ if (contrast > 127)
+ contrast = 127;
+
+ ch7024_write_reg(CH7024_TVCTA, contrast);
+
+ return count;
+}
+
+DRIVER_ATTR(contrast, 0644, show_contrast, store_contrast);
+
+static ssize_t show_hue(struct device_driver *dev, char *buf)
+{
+ u32 reg;
+ reg = ch7024_read_reg(CH7024_TVHUE);
+
+ reg *= 2; /* Scale to 0 - 255 */
+
+ return snprintf(buf, PAGE_SIZE, "%u", reg);
+}
+
+static ssize_t store_hue(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ char *endp;
+ int hue = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ hue /= 2;
+ if (hue > 127)
+ hue = 127;
+
+ ch7024_write_reg(CH7024_TVHUE, hue);
+
+ return count;
+}
+
+DRIVER_ATTR(hue, 0644, show_hue, store_hue);
+
+static ssize_t show_saturation(struct device_driver *dev, char *buf)
+{
+ u32 reg;
+ reg = ch7024_read_reg(CH7024_TVSAT);
+
+ reg *= 2; /* Scale to 0 - 255 */
+
+ return snprintf(buf, PAGE_SIZE, "%u", reg);
+}
+
+static ssize_t store_saturation(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ char *endp;
+ int saturation = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ saturation /= 2;
+ if (saturation > 127)
+ saturation = 127;
+
+ ch7024_write_reg(CH7024_TVSAT, saturation);
+
+ return count;
+}
+
+DRIVER_ATTR(saturation, 0644, show_saturation, store_saturation);
+
+static ssize_t show_sharpness(struct device_driver *dev, char *buf)
+{
+ u32 reg;
+ reg = ch7024_read_reg(CH7024_TVSHARP);
+
+ reg *= 32; /* Scale to 0 - 255 */
+
+ return snprintf(buf, PAGE_SIZE, "%u", reg);
+}
+
+static ssize_t store_sharpness(struct device_driver *dev, const char *buf,
+ size_t count)
+{
+ char *endp;
+ int sharpness = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (*endp && isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ sharpness /= 32; /* Scale to 0 - 7 */
+ if (sharpness > 7)
+ sharpness = 7;
+
+ ch7024_write_reg(CH7024_TVSHARP, sharpness);
+
+ return count;
+}
+
+DRIVER_ATTR(sharpness, 0644, show_sharpness, store_sharpness);
+
+static int ch7024_probe(struct i2c_client *client, const struct i2c_device_id *dev_id)
+{
+ int ret, i;
+ u32 id;
+ u32 irqtype;
+ struct mxc_tvout_platform_data *plat_data = client->dev.platform_data;
+
+ ch7024_client = client;
+
+ io_reg = regulator_get(&client->dev, plat_data->io_reg);
+ core_reg = regulator_get(&client->dev, plat_data->core_reg);
+ analog_reg = regulator_get(&client->dev, plat_data->analog_reg);
+
+ regulator_enable(io_reg);
+ regulator_enable(core_reg);
+ regulator_enable(analog_reg);
+ msleep(200);
+
+ id = ch7024_read_reg(CH7024_DEVID);
+
+ regulator_disable(core_reg);
+ regulator_disable(io_reg);
+ regulator_disable(analog_reg);
+
+ if (id < 0 || id != CH7024_DEVICE_ID) {
+ printk(KERN_ERR
+ "ch7024: TV encoder not present: id = %x\n", id);
+ return -ENODEV;
+ }
+ printk(KERN_ERR "ch7024: TV encoder present: id = %x\n", id);
+
+ detect_gpio = plat_data->detect_line;
+
+ if (client->irq > 0) {
+ if (ch7024_detect() == 0)
+ irqtype = IRQF_TRIGGER_RISING;
+ else
+ irqtype = IRQF_TRIGGER_FALLING;
+
+ ret = request_irq(client->irq, hp_detect_handler, irqtype,
+ client->name, client);
+ if (ret < 0)
+ goto err0;
+
+ ret = driver_create_file(&client->driver->driver,
+ &driver_attr_headphone);
+ if (ret < 0)
+ goto err1;
+ }
+
+ ret = driver_create_file(&client->driver->driver,
+ &driver_attr_brightness);
+ if (ret)
+ goto err2;
+
+ ret = driver_create_file(&client->driver->driver,
+ &driver_attr_contrast);
+ if (ret)
+ goto err3;
+ ret = driver_create_file(&client->driver->driver, &driver_attr_hue);
+ if (ret)
+ goto err4;
+ ret = driver_create_file(&client->driver->driver,
+ &driver_attr_saturation);
+ if (ret)
+ goto err5;
+ ret = driver_create_file(&client->driver->driver,
+ &driver_attr_sharpness);
+ if (ret)
+ goto err6;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ if (strcmp(registered_fb[i]->fix.id, "DISP3 BG") == 0) {
+ ch7024_fbi = registered_fb[i];
+ break;
+ }
+ }
+ if (ch7024_fbi != NULL) {
+ fb_add_videomode(&video_modes[0], &ch7024_fbi->modelist);
+ fb_add_videomode(&video_modes[1], &ch7024_fbi->modelist);
+ }
+ fb_register_client(&nb);
+
+ return 0;
+ err6:
+ driver_remove_file(&client->driver->driver, &driver_attr_saturation);
+ err5:
+ driver_remove_file(&client->driver->driver, &driver_attr_hue);
+ err4:
+ driver_remove_file(&client->driver->driver, &driver_attr_contrast);
+ err3:
+ driver_remove_file(&client->driver->driver, &driver_attr_brightness);
+ err2:
+ driver_remove_file(&client->driver->driver, &driver_attr_headphone);
+ err1:
+ free_irq(client->irq, client);
+ err0:
+ return ret;
+}
+
+static int ch7024_remove(struct i2c_client *client)
+{
+ free_irq(client->irq, client);
+
+ regulator_put(io_reg);
+ regulator_put(core_reg);
+ regulator_put(analog_reg);
+
+ driver_remove_file(&client->driver->driver, &driver_attr_headphone);
+ driver_remove_file(&client->driver->driver, &driver_attr_brightness);
+ driver_remove_file(&client->driver->driver, &driver_attr_contrast);
+ driver_remove_file(&client->driver->driver, &driver_attr_hue);
+ driver_remove_file(&client->driver->driver, &driver_attr_saturation);
+ driver_remove_file(&client->driver->driver, &driver_attr_sharpness);
+
+ fb_unregister_client(&nb);
+
+ ch7024_client = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * PM suspend/resume routing
+ */
+static int ch7024_suspend(struct i2c_client *client, pm_message_t state)
+{
+ pr_debug("Ch7024 suspend routing..\n");
+ if (enabled) {
+ ch7024_disable();
+ pm_status = 1;
+ } else {
+ pm_status = 0;
+ }
+ return 0;
+}
+
+static int ch7024_resume(struct i2c_client *client)
+{
+ pr_debug("Ch7024 resume routing..\n");
+ if (pm_status) {
+ ch7024_enable();
+ ch7024_setup(ch7024_cur_mode);
+ }
+ return 0;
+}
+#else
+#define ch7024_suspend NULL
+#define ch7024_resume NULL
+#endif
+
+static const struct i2c_device_id ch7024_id[] = {
+ { "ch7024", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, ch7024_id);
+
+static struct i2c_driver ch7024_driver = {
+ .driver = {
+ .name = "ch7024",
+ },
+ .probe = ch7024_probe,
+ .remove = ch7024_remove,
+ .suspend = ch7024_suspend,
+ .resume = ch7024_resume,
+ .id_table = ch7024_id,
+};
+
+static int __init ch7024_init(void)
+{
+ return i2c_add_driver(&ch7024_driver);
+}
+
+static void __exit ch7024_exit(void)
+{
+ i2c_del_driver(&ch7024_driver);
+}
+
+module_init(ch7024_init);
+module_exit(ch7024_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("CH7024 TV encoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/fs453.c b/drivers/video/mxc/fs453.c
new file mode 100644
index 000000000000..449675e83aef
--- /dev/null
+++ b/drivers/video/mxc/fs453.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup FS453 Focus FS453 TV Encoder Driver
+ */
+/*!
+ * @file fs453.c
+ * @brief Driver for FS453/4 TV encoder
+ *
+ * @ingroup FS453
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/ioctl.h>
+#include <linux/video_encoder.h>
+
+#include "fs453.h"
+
+/*
+ * FIXME: VGA mode is not defined by video_encoder.h
+ * while FS453 supports VGA output.
+ */
+#ifndef VIDEO_ENCODER_VGA
+#define VIDEO_ENCODER_VGA 32
+#endif
+
+/*!
+ * This stucture contains the status of FS453.
+ */
+struct fs453_data {
+ int norm;
+ int input;
+ int output;
+ int enable;
+};
+
+/*!
+ * This structure contains all the register values needed to program the
+ * TV encoder chip. This structure is instantiated and initialized for
+ * each supported output standard.
+ */
+struct fs453_presets {
+ u32 mode; /*! Video mode */
+ u16 qpr; /*! Quick Program Register */
+ u16 pwr_mgmt; /*! Power Management */
+ u16 iho; /*! Input Horizontal Offset */
+ u16 ivo; /*! Input Vertical Offset */
+ u16 ihw; /*! Input Horizontal Width */
+ u16 vsc; /*! Vertical Scaling Coefficient */
+ u16 hsc; /*! Horizontal Scaling Coefficient */
+ u16 bypass; /*! Bypass */
+ u16 misc; /*! Miscellaneous Bits Register */
+ u8 misc46; /*! Miscellaneous Bits Register 46 */
+ u8 misc47; /*! Miscellaneous Bits Register 47 */
+ u32 ncon; /*! Numerator of NCO Word */
+ u32 ncod; /*! Denominator of NCO Word */
+ u16 pllm; /*! PLL M and Pump Control */
+ u16 plln; /*! PLL N */
+ u16 pllpd; /*! PLL Post-Divider */
+ u16 vid_cntl0; /*! Video Control 0 */
+ u16 dac_cntl; /*! DAC Control */
+ u16 fifo_lat; /*! FIFO Latency */
+};
+
+static struct fs453_presets fs453_vga_presets = {
+ .mode = VIDEO_ENCODER_VGA,
+ .qpr = 0x9cb0,
+ .pwr_mgmt = 0x0408,
+ .misc = 0x0103,
+ .ncon = 0x00000000,
+ .ncod = 0x00000000,
+ .misc46 = 0xa9,
+ .misc47 = 0x00,
+ .pllm = 0x317f,
+ .plln = 0x008e,
+ .pllpd = 0x0202,
+ .vid_cntl0 = 0x4006,
+ .dac_cntl = 0x00e4,
+ .fifo_lat = 0x0082,
+};
+
+static struct fs453_presets fs453_ntsc_presets = {
+ .mode = VIDEO_ENCODER_NTSC,
+ .qpr = 0x9c48,
+ .pwr_mgmt = 0x0200,
+ .misc = 0x0103,
+ .ncon = 0x00000001,
+ .ncod = 0x00000001,
+ .misc46 = 0x01,
+ .misc47 = 0x00,
+ .pllm = 0x4000 | (296 - 17),
+ .plln = 30 - 1,
+ .pllpd = ((10 - 1) << 8) | (10 - 1),
+ .iho = 0,
+ .ivo = 40,
+ .ihw = 768,
+ .vsc = 789,
+ .hsc = 0x0000,
+ .bypass = 0x000a,
+ .vid_cntl0 = 0x0340,
+ .dac_cntl = 0x00e4,
+ .fifo_lat = 0x0082,
+};
+
+static struct fs453_presets fs453_pal_presets = {
+ .mode = VIDEO_ENCODER_PAL,
+ .qpr = 0x9c41,
+ .pwr_mgmt = 0x0200,
+ .misc = 0x0103,
+ .ncon = 0x00000001,
+ .ncod = 0x00000001,
+ .misc46 = 0x01,
+ .misc47 = 0x00,
+ .pllm = 0x4000 | (296 - 17),
+ .plln = 30 - 1,
+ .pllpd = ((10 - 1) << 8) | (10 - 1),
+ .iho = 0,
+ .ivo = 19,
+ .ihw = 768,
+ .vsc = 8200,
+ .hsc = 0x0000,
+ .bypass = 0x000a,
+ .vid_cntl0 = 0x0340,
+ .dac_cntl = 0x00e4,
+ .fifo_lat = 0x0082,
+};
+
+static int fs453_preset(struct i2c_client *client,
+ struct fs453_presets *presets);
+static int fs453_enable(struct i2c_client *client, int enable);
+
+static struct i2c_driver fs453_driver;
+/*
+ * FIXME: fs453_client will represent the first FS453 device found by
+ * the I2C subsystem, which means fs453_ioctl() always works on the
+ * first FS453 device.
+ */
+static struct i2c_client *fs453_client = 0;
+
+static int fs453_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ int val;
+ char *smode = 0;
+ struct video_encoder_capability *cap;
+ struct fs453_data *data = i2c_get_clientdata(client);
+ int ret = 0;
+
+ switch (cmd) {
+ case ENCODER_GET_CAPABILITIES:
+ cap = arg;
+ cap->flags =
+ VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC | VIDEO_ENCODER_VGA;
+ cap->inputs = 1;
+ cap->outputs = 1;
+ break;
+ case ENCODER_SET_NORM:
+ val = *(int *)arg;
+ switch (val) {
+ case VIDEO_ENCODER_PAL:
+ ret = fs453_preset(client, &fs453_pal_presets);
+ smode = "PAL";
+ break;
+ case VIDEO_ENCODER_NTSC:
+ ret = fs453_preset(client, &fs453_ntsc_presets);
+ smode = "NTSC";
+ break;
+ case VIDEO_ENCODER_VGA:
+ ret = fs453_preset(client, &fs453_vga_presets);
+ smode = "VGA";
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (!ret) {
+ data->norm = val;
+ data->enable = 1;
+ pr_debug("FS453: switched to %s\n", smode);
+ }
+ break;
+ case ENCODER_SET_INPUT:
+ val = *(int *)arg;
+ /* We have only one input */
+ if (val != 0)
+ return -EINVAL;
+ data->input = val;
+ break;
+ case ENCODER_SET_OUTPUT:
+ val = *(int *)arg;
+ /* We have only one output */
+ if (val != 0)
+ return -EINVAL;
+ data->output = val;
+ break;
+ case ENCODER_ENABLE_OUTPUT:
+ val = *(int *)arg;
+ if ((ret = fs453_enable(client, val)) == 0)
+ data->enable = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int i2c_fs453_detect_client(struct i2c_adapter *adapter, int address,
+ int kind)
+{
+ int chip_id;
+ struct i2c_client *client;
+ struct fs453_data *data;
+ const char *client_name = "FS453 I2C dev";
+
+ pr_debug("FS453: i2c-bus: %s; address: 0x%x\n", adapter->name, address);
+
+ /* Let's see whether this adapter can support what we need */
+ if (!i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ pr_debug("FS453: SMBUS word/byte operations not permited.\n");
+ return 0;
+ }
+
+ client =
+ kmalloc(sizeof(struct i2c_client) + sizeof(struct fs453_data),
+ GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+
+ data = (struct fs453_data *)(client + 1);
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &fs453_driver;
+ client->flags = 0;
+
+ /*
+ * The generic detection, that is skipped if any force
+ * parameter was used.
+ */
+ if (kind < 0) {
+ chip_id = i2c_smbus_read_word_data(client, FS453_ID);
+ if (chip_id != FS453_CHIP_ID) {
+ pr_info("FS453: TV encoder not present\n");
+ kfree(client);
+ return 0;
+ } else
+ pr_info("FS453: TV encoder present, ID=0x%04X\n",
+ chip_id);
+ }
+ strcpy(client->name, client_name);
+
+ /* FS453 default status */
+ data->input = 0;
+ data->output = 0;
+ data->norm = 0;
+ data->enable = 0;
+ i2c_set_clientdata(client, data);
+
+ if (i2c_attach_client(client)) {
+ pr_debug("FS453: i2c_attach_client() failed.\n");
+ kfree(client);
+ } else if (fs453_client == 0)
+ fs453_client = client;
+
+ return 0;
+}
+
+static unsigned short normal_i2c[] = { FS453_I2C_ADDR, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static int i2c_fs453_attach(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, &i2c_fs453_detect_client);
+}
+
+static int i2c_fs453_detach(struct i2c_client *client)
+{
+ int err;
+
+ if ((err = i2c_detach_client(client))) {
+ pr_debug("FS453: i2c_detach_client() failed\n");
+ return err;
+ }
+
+ if (fs453_client == client)
+ fs453_client = 0;
+
+ kfree(client);
+ return 0;
+}
+
+static struct i2c_driver fs453_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "FS453 driver",
+ },
+ .attach_adapter = &i2c_fs453_attach,
+ .detach_client = &i2c_fs453_detach,
+ .command = fs453_command,
+};
+
+/*!
+ * @brief Function to read TV encoder registers on the i2c bus
+ * @param client I2C client structure
+ * @param reg The register number
+ * @param value Pointer to buffer to receive the read data
+ * @param len Number of 16-bit register words to read
+ * @return 0 on success, others on failure
+ */
+static int fs453_read(struct i2c_client *client, u8 reg, u32 * value, u32 len)
+{
+ if (len == 1)
+ *value = i2c_smbus_read_byte_data(client, reg);
+ else if (len == 2)
+ *value = i2c_smbus_read_word_data(client, reg);
+ else if (len == 4) {
+ *(u16 *) value = i2c_smbus_read_word_data(client, reg);
+ *((u16 *) value + 1) =
+ i2c_smbus_read_word_data(client, reg + 2);
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+/*!
+ * @brief Function to write a TV encoder register on the i2c bus
+ * @param client I2C client structure
+ * @param reg The register number
+ * @param value The value to write
+ * @param len Number of words to write (must be 1)
+ * @return 0 on success, others on failure
+ */
+static int fs453_write(struct i2c_client *client, u8 reg, u32 value, u32 len)
+{
+ if (len == 1)
+ return i2c_smbus_write_byte_data(client, reg, (u8) value);
+ else if (len == 2)
+ return i2c_smbus_write_word_data(client, reg, (u16) value);
+ else if (len == 4)
+ return i2c_smbus_write_block_data(client, reg, len,
+ (u8 *) & value);
+ else
+ return -EINVAL;
+}
+
+/*!
+ * @brief Function to initialize the TV encoder
+ * @param client I2C client structure
+ * @param presets FS453 pre-defined register values
+ * @return 0 on success; ENODEV if the encoder wasn't found
+ */
+static int fs453_preset(struct i2c_client *client,
+ struct fs453_presets *presets)
+{
+ u32 data;
+
+ if (!client)
+ return -ENODEV;
+
+ /* set the clock level */
+ fs453_write(client, FS453_CR, CR_GCC_CK_LVL, 2);
+
+ /* soft reset the encoder */
+ fs453_read(client, FS453_CR, &data, 2);
+ fs453_write(client, FS453_CR, data | CR_SRESET, 2);
+ fs453_write(client, FS453_CR, data & ~CR_SRESET, 2);
+
+ fs453_write(client, FS453_BYPASS, presets->bypass, 2);
+
+ /* Write the QPR (Quick Programming Register). */
+ fs453_write(client, FS453_QPR, presets->qpr, 2);
+
+ if (presets->mode != VIDEO_ENCODER_VGA) {
+ /* set up the NCO and PLL */
+ fs453_write(client, FS453_NCON, presets->ncon, 4);
+ fs453_write(client, FS453_NCOD, presets->ncod, 4);
+ fs453_write(client, FS453_PLL_M_PUMP, presets->pllm, 2);
+ fs453_write(client, FS453_PLL_N, presets->plln, 2);
+ fs453_write(client, FS453_PLL_PDIV, presets->pllpd, 2);
+
+ /* latch the NCO and PLL settings */
+ fs453_read(client, FS453_CR, &data, 2);
+ fs453_write(client, FS453_CR, data | CR_NCO_EN, 2);
+ fs453_write(client, FS453_CR, data & ~CR_NCO_EN, 2);
+ }
+
+ /* customize */
+ fs453_write(client, FS453_PWR_MGNT, presets->pwr_mgmt, 2);
+
+ fs453_write(client, FS453_IHO, presets->iho, 2);
+ fs453_write(client, FS453_IVO, presets->ivo, 2);
+ fs453_write(client, FS453_IHW, presets->ihw, 2);
+ fs453_write(client, FS453_VSC, presets->vsc, 2);
+ fs453_write(client, FS453_HSC, presets->hsc, 2);
+
+ fs453_write(client, FS453_MISC, presets->misc, 2);
+
+ fs453_write(client, FS453_VID_CNTL0, presets->vid_cntl0, 2);
+ fs453_write(client, FS453_MISC_46, presets->misc46, 1);
+ fs453_write(client, FS453_MISC_47, presets->misc47, 1);
+
+ fs453_write(client, FS453_DAC_CNTL, presets->dac_cntl, 2);
+ fs453_write(client, FS453_FIFO_LAT, presets->fifo_lat, 2);
+
+ return 0;
+}
+
+/*!
+ * @brief Function to enable/disable the TV encoder
+ * @param client I2C client structure
+ * @param enable 0 to disable, others to enable
+ * @return 0 on success; ENODEV if the encoder wasn't found
+ */
+static int fs453_enable(struct i2c_client *client, int enable)
+{
+ struct fs453_data *data;
+
+ if (!client)
+ return -ENODEV;
+
+ data = i2c_get_clientdata(client);
+
+ if (enable)
+ return fs453_command(client, ENCODER_SET_NORM, &data->norm);
+ else
+ return fs453_write(client, FS453_PWR_MGNT, 0x3BFF, 2);
+}
+
+/*!
+ * @brief FS453 control routine
+ * @param cmd Control command
+ * @param arg Control argument
+ * @return 0 on success, others on failure
+ */
+int fs453_ioctl(unsigned int cmd, void *arg)
+{
+ if (!fs453_client)
+ return -ENODEV;
+
+ return fs453_command(fs453_client, cmd, arg);
+}
+
+/*!
+ * @brief Probe for the TV enocder and initialize the driver
+ * @return 0 on success, others on failure
+ */
+static int __init fs453_init(void)
+{
+ int err;
+
+ pr_info("FS453/4 driver, (c) 2005 Freescale Semiconductor, Inc.\n");
+
+ if ((err = i2c_add_driver(&fs453_driver))) {
+ pr_info("FS453: driver registration failed\n");
+ return err;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Module exit routine
+ */
+static void __exit fs453_exit(void)
+{
+ i2c_del_driver(&fs453_driver);
+}
+
+module_init(fs453_init);
+module_exit(fs453_exit);
+
+EXPORT_SYMBOL(fs453_ioctl);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("FS453/4 TV encoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/fs453.h b/drivers/video/mxc/fs453.h
new file mode 100644
index 000000000000..1edd11481b76
--- /dev/null
+++ b/drivers/video/mxc/fs453.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @file fs453.h
+ * @brief Driver for FS453/4 TV encoder
+ *
+ * @ingroup FS453
+ */
+
+#ifndef __FS453_H__
+#define __FS453_H__
+
+/* I2C address of the FS453 chip */
+
+#define I2C1_BUS 0
+#define FS453_I2C_ADDR 0x6A
+
+/*!
+ *
+ * FS453 register file
+ *
+ */
+#define FS453_IHO 0x00 /*! Input Horizontal Offset */
+#define FS453_IVO 0x02 /*! Input Vertical Offset */
+#define FS453_IHW 0x04 /*! Input Horizontal Width */
+#define FS453_VSC 0x06 /*! Vertical Scaling Coefficient */
+#define FS453_HSC 0x08 /*! Horizontal Scaling Coefficient */
+#define FS453_BYPASS 0x0A /*! BYPASS */
+#define FS453_CR 0x0C /*! Command Register */
+#define FS453_MISC 0x0E /*! Miscellaneous Bits Register */
+#define FS453_NCON 0x10 /*! Numerator of NCO Word */
+#define FS453_NCOD 0x14 /*! Denominator of NCO Word */
+#define FS453_PLL_M_PUMP 0x18 /*! PLL M and Pump Control */
+#define FS453_PLL_N 0x1A /*! PLL N */
+#define FS453_PLL_PDIV 0x1C /*! PLL Post-Divider */
+#define FS453_SHP 0x24 /*! Sharpness Filter */
+#define FS453_FLK 0x26 /*! Filcker Filter Coefficient */
+#define FS453_GPIO 0x28 /*! General Purpose I/O, Output Enab */
+#define FS453_ID 0x32 /*! Part Identification Number */
+#define FS453_STATUS 0x34 /*! Status Port */
+#define FS453_FIFO_SP 0x36 /*! FIFO Status Port Fill/Underrun */
+#define FS453_FIFO_LAT 0x38 /*! FIFO Latency */
+#define FS453_CHR_FREQ 0x40 /*! Chroma Subcarrier Frequency */
+#define FS453_CHR_PHASE 0x44 /*! Chroma Phase */
+#define FS453_MISC_45 0x45 /*! Miscellaneous Bits Register 45 */
+#define FS453_MISC_46 0x46 /*! Miscellaneous Bits Register 46 */
+#define FS453_MISC_47 0x47 /*! Miscellaneous Bits Register 47 */
+#define FS453_HSYNC_WID 0x48 /*! HSync Width */
+#define FS453_BURST_WID 0x49 /*! Burst Width */
+#define FS453_BPORCH 0x4A /*! Back Porch Width */
+#define FS453_CB_BURST 0x4B /*! Cb Burst Amplitude */
+#define FS453_CR_BURST 0x4C /*! Cr Burst Amplitude */
+#define FS453_MISC_4D 0x4D /*! Miscellaneous Bits Register 4D */
+#define FS453_BLACK_LVL 0x4E /*! Black Level */
+#define FS453_BLANK_LVL 0x50 /*! Blank Level */
+#define FS453_NUM_LINES 0x57 /*! Number of Lines */
+#define FS453_WHITE_LVL 0x5E /*! White Level */
+#define FS453_CB_GAIN 0x60 /*! Cb Color Saturation */
+#define FS453_CR_GAIN 0x62 /*! Cr Color Saturation */
+#define FS453_TINT 0x65 /*! Tint */
+#define FS453_BR_WAY 0x69 /*! Width of Breezeway */
+#define FS453_FR_PORCH 0x6C /*! Front Porch */
+#define FS453_NUM_PIXELS 0x71 /*! Total num. of luma/chroma Pixels */
+#define FS453_1ST_LINE 0x73 /*! First Video Line */
+#define FS453_MISC_74 0x74 /*! Miscellaneous Bits Register 74 */
+#define FS453_SYNC_LVL 0x75 /*! Sync Level */
+#define FS453_VBI_BL_LVL 0x7C /*! VBI Blank Level */
+#define FS453_SOFT_RST 0x7E /*! Encoder Soft Reset */
+#define FS453_ENC_VER 0x7F /*! Encoder Version */
+#define FS453_WSS_CONFIG 0x80 /*! WSS Configuration Register */
+#define FS453_WSS_CLK 0x81 /*! WSS Clock */
+#define FS453_WSS_DATAF1 0x83 /*! WSS Data Field 1 */
+#define FS453_WSS_DATAF0 0x86 /*! WSS Data Field 0 */
+#define FS453_WSS_LNF1 0x89 /*! WSS Line Number Field 1 */
+#define FS453_WSS_LNF0 0x8A /*! WSS Line Number Field 0 */
+#define FS453_WSS_LVL 0x8B /*! WSS Level */
+#define FS453_MISC_8D 0x8D /*! Miscellaneous Bits Register 8D */
+#define FS453_VID_CNTL0 0x92 /*! Video Control 0 */
+#define FS453_HD_FP_SYNC 0x94 /*! Horiz. Front Porch & HSync Width */
+#define FS453_HD_YOFF_BP 0x96 /*! HDTV Lum. Offset & Back Porch */
+#define FS453_SYNC_DL 0x98 /*! Sync Delay Value */
+#define FS453_LD_DET 0x9C /*! DAC Load Detect */
+#define FS453_DAC_CNTL 0x9E /*! DAC Control */
+#define FS453_PWR_MGNT 0xA0 /*! Power Management */
+#define FS453_RED_MTX 0xA2 /*! RGB to YCrCb Matrix Red Coeff. */
+#define FS453_GRN_MTX 0xA4 /*! RGB to YCrCb Matrix Green Coeff. */
+#define FS453_BLU_MTX 0xA6 /*! RGB to YCrCb Matrix Blue Coeff. */
+#define FS453_RED_SCL 0xA8 /*! RGB to YCrCb Scaling Red Coeff. */
+#define FS453_GRN_SCL 0xAA /*! RGB to YCrCb Scaling Green Coeff. */
+#define FS453_BLU_SCL 0xAC /*! RGB to YCrCb Scaling Blue Coeff. */
+#define FS453_CC_FIELD_1 0xAE /*! Closed Caption Field 1 Data */
+#define FS453_CC_FIELD_2 0xB0 /*! Closed Caption Field 2 Data */
+#define FS453_CC_CONTROL 0xB2 /*! Closed Caption Control */
+#define FS453_CC_BLANK_VALUE 0xB4 /*! Closed Caption Blanking Value */
+#define FS453_CC_BLANK_SAMPLE 0xB6 /*! Closed Caption Blanking Sample */
+#define FS453_HACT_ST 0xB8 /*! HDTV Horizontal Active Start */
+#define FS453_HACT_WD 0xBA /*! HDTV Horizontal Active Width */
+#define FS453_VACT_ST 0xBC /*! HDTV Veritical Active Width */
+#define FS453_VACT_HT 0xBE /*! HDTV Veritical Active Height */
+#define FS453_PR_PB_SCALING 0xC0 /*! Pr and Pb Relative Scaling */
+#define FS453_LUMA_BANDWIDTH 0xC2 /*! Luminance Frequency Response */
+#define FS453_QPR 0xC4 /*! Quick Program Register */
+
+/*! Command register bits */
+
+#define CR_GCC_CK_LVL 0x2000 /*! Graphics Controller switching lev */
+#define CR_P656_LVL 0x1000 /*! Pixel Port Output switching level */
+#define CR_P656_IN 0x0800 /*! Pixel Port In */
+#define CR_P656_OUT 0x0400 /*! Pixel Port Out */
+#define CR_CBAR_480P 0x0200 /*! 480P Color Bars */
+#define CR_PAL_NTSCIN 0x0100 /*! PAL or NTSC input */
+#define CR_SYNC_MS 0x0080 /*! Sync Master or Slave */
+#define CR_FIFO_CLR 0x0040 /*! FIFO Clear */
+#define CR_CACQ_CLR 0x0020 /*! CACQ Clear */
+#define CR_CDEC_BP 0x0010 /*! Chroma Decimator Bypass */
+#define CR_NCO_EN 0x0002 /*! Enable NCO Latch */
+#define CR_SRESET 0x0001 /*! Soft Reset */
+
+/*! Chip ID register bits */
+
+#define FS453_CHIP_ID 0xFE05 /*! Chip ID register expected value */
+
+#endif /* __FS453_H__ */
diff --git a/drivers/video/mxc/mx2fb.c b/drivers/video/mxc/mx2fb.c
new file mode 100644
index 000000000000..bdf7d19e19f1
--- /dev/null
+++ b/drivers/video/mxc/mx2fb.c
@@ -0,0 +1,1346 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup Framebuffer_MX27 Framebuffer Driver for MX27.
+ */
+
+/*!
+ * @file mx2fb.c
+ *
+ * @brief Frame buffer driver for MX27 ADS.
+ *
+ * @ingroup Framebuffer_MX27
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/mxcfb.h>
+#include <asm/uaccess.h>
+
+#include "mx2fb.h"
+
+#define MX2FB_TYPE_BG 0
+#define MX2FB_TYPE_GW 1
+
+extern void gpio_lcdc_active(void);
+extern void gpio_lcdc_inactive(void);
+extern void board_power_lcd(int on);
+
+static char *fb_mode = 0;
+static int fb_enabled = 0;
+static unsigned long default_bpp = 16;
+static ATOMIC_NOTIFIER_HEAD(mx2fb_notifier_list);
+static struct clk *lcdc_clk;
+/*!
+ * @brief Structure containing the MX2 specific framebuffer information.
+ */
+struct mx2fb_info {
+ int type;
+ char *id;
+ int registered;
+ int blank;
+ unsigned long pseudo_palette[16];
+};
+
+/* Framebuffer APIs */
+static int mx2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info);
+static int mx2fb_set_par(struct fb_info *info);
+static int mx2fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info);
+static int mx2fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int mx2fb_blank(int blank_mode, struct fb_info *info);
+static int mx2fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg);
+
+/* Driver entries */
+int __init mx2fb_init(void);
+void __exit mx2fb_exit(void);
+#ifndef MODULE
+static int __init mx2fb_setup(char *);
+#endif
+
+/* Internal functions */
+static int __init _init_fbinfo(struct fb_info *info,
+ struct platform_device *pdev);
+static int __init _install_fb(struct fb_info *info,
+ struct platform_device *pdev);
+static void __exit _uninstall_fb(struct fb_info *info);
+static int _map_video_memory(struct fb_info *info);
+static void _unmap_video_memory(struct fb_info *info);
+static void _set_fix(struct fb_info *info);
+static void _enable_lcdc(struct fb_info *info);
+static void _disable_lcdc(struct fb_info *info);
+static void _enable_graphic_window(struct fb_info *info);
+static void _disable_graphic_window(struct fb_info *info);
+static void _update_lcdc(struct fb_info *info);
+static void _request_irq(void);
+static void _free_irq(void);
+
+#ifdef CONFIG_PM
+static int mx2fb_suspend(struct platform_device *pdev, pm_message_t state);
+static int mx2fb_resume(struct platform_device *pdev);
+#else
+#define mx2fb_suspend 0
+#define mx2fb_resume 0
+#endif
+
+static int mx2fb_probe(struct platform_device *pdev);
+
+#ifdef CONFIG_FB_MXC_TVOUT
+#include <linux/video_encoder.h>
+/*
+ * FIXME: VGA mode is not defined by video_encoder.h
+ * while FS453 supports VGA output.
+ */
+#ifndef VIDEO_ENCODER_VGA
+#define VIDEO_ENCODER_VGA 32
+#endif
+
+#define MODE_PAL "TV-PAL"
+#define MODE_NTSC "TV-NTSC"
+#define MODE_VGA "TV-VGA"
+
+extern int fs453_ioctl(unsigned int cmd, void *arg);
+#endif
+
+struct mx2fb_info mx2fbi_bg = {
+ .type = MX2FB_TYPE_BG,
+ .id = "DISP0 BG",
+ .registered = 0,
+};
+
+static struct mx2fb_info mx2fbi_gw = {
+ .type = MX2FB_TYPE_GW,
+ .id = "DISP0 FG",
+ .registered = 0,
+};
+
+/*! Current graphic window information */
+static struct fb_gwinfo g_gwinfo = {
+ .enabled = 0,
+ .alpha_value = 255,
+ .ck_enabled = 0,
+ .ck_red = 0,
+ .ck_green = 0,
+ .ck_blue = 0,
+ .xpos = 0,
+ .ypos = 0,
+};
+
+/*!
+ * @brief Framebuffer information structures.
+ * There are up to 3 framebuffers: background, TVout, and graphic window.
+ * If graphic window is configured, it must be the last framebuffer.
+ */
+static struct fb_info mx2fb_info[] = {
+ {.par = &mx2fbi_bg},
+ {.par = &mx2fbi_gw},
+};
+
+/*!
+ * @brief This structure contains pointers to the power management
+ * callback functions.
+ */
+static struct platform_driver mx2fb_driver = {
+ .driver = {
+ .name = "mxc_sdc_fb",
+ .owner = THIS_MODULE,
+ .bus = &platform_bus_type,
+ },
+ .probe = mx2fb_probe,
+ .suspend = mx2fb_suspend,
+ .resume = mx2fb_resume,
+};
+
+/*!
+ * @brief Framebuffer file operations
+ */
+static struct fb_ops mx2fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = mx2fb_check_var,
+ .fb_set_par = mx2fb_set_par,
+ .fb_setcolreg = mx2fb_setcolreg,
+ .fb_blank = mx2fb_blank,
+ .fb_pan_display = mx2fb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ //.fb_cursor = soft_cursor,
+ .fb_ioctl = mx2fb_ioctl,
+};
+
+/*!
+ * @brief Validates a var passed in.
+ *
+ * @param var Frame buffer variable screen structure
+ * @param info Frame buffer structure that represents a single frame buffer
+ *
+ * @return Negative errno on error, or zero on success.
+ *
+ * Checks to see if the hardware supports the state requested by var passed
+ * in. This function does not alter the hardware state! If the var passed in
+ * is slightly off by what the hardware can support then we alter the var
+ * PASSED in to what we can do. If the hardware doesn't support mode change
+ * a -EINVAL will be returned by the upper layers.
+ *
+ */
+static int mx2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ unsigned long htotal, vtotal;
+
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if (var->xoffset < 0)
+ var->xoffset = 0;
+
+ if (var->yoffset < 0)
+ var->yoffset = 0;
+
+ if (var->xoffset + info->var.xres > info->var.xres_virtual)
+ var->xoffset = info->var.xres_virtual - info->var.xres;
+
+ if (var->yoffset + info->var.yres > info->var.yres_virtual)
+ var->yoffset = info->var.yres_virtual - info->var.yres;
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16)) {
+ var->bits_per_pixel = default_bpp;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ if (var->pixclock < 1000) {
+ htotal = var->xres + var->right_margin + var->hsync_len +
+ var->left_margin;
+ vtotal = var->yres + var->lower_margin + var->vsync_len +
+ var->upper_margin;
+ var->pixclock = (vtotal * htotal * 6UL) / 100UL;
+ var->pixclock = KHZ2PICOS(var->pixclock);
+ dev_dbg(info->device,
+ "pixclock set for 60Hz refresh = %u ps\n",
+ var->pixclock);
+ }
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+
+ /* Copy nonstd field to/from sync for fbset usage */
+ var->sync |= var->nonstd;
+ var->nonstd |= var->sync;
+
+ return 0;
+}
+
+/*!
+ * @brief Alters the hardware state.
+ *
+ * @param info Frame buffer structure that represents a single frame buffer
+ *
+ * @return Zero on success others on failure
+ *
+ * Using the fb_var_screeninfo in fb_info we set the resolution of this
+ * particular framebuffer. This function alters the fb_fix_screeninfo stored
+ * in fb_info. It doesn't not alter var in fb_info since we are using that
+ * data. This means we depend on the data in var inside fb_info to be
+ * supported by the hardware. mx2fb_check_var is always called before
+ * mx2fb_set_par to ensure this.
+ */
+static int mx2fb_set_par(struct fb_info *info)
+{
+ unsigned long len;
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ _set_fix(info);
+
+ len = info->var.yres_virtual * info->fix.line_length;
+ if (len > info->fix.smem_len) {
+ if (info->fix.smem_start)
+ _unmap_video_memory(info);
+
+ /* Memory allocation for framebuffer */
+ if (_map_video_memory(info)) {
+ dev_err(info->device, "Unable to allocate fb memory\n");
+ return -ENOMEM;
+ }
+ }
+
+ _update_lcdc(info);
+ if (info->fbops->fb_blank)
+ info->fbops->fb_blank(mx2fbi->blank, info);
+
+ return 0;
+}
+
+/*!
+ * @brief Sets a color register.
+ *
+ * @param regno Which register in the CLUT we are programming
+ * @param red The red value which can be up to 16 bits wide
+ * @param green The green value which can be up to 16 bits wide
+ * @param blue The blue value which can be up to 16 bits wide.
+ * @param transp If supported the alpha value which can be up to
+ * 16 bits wide.
+ * @param info Frame buffer info structure
+ *
+ * @return Negative errno on error, or zero on success.
+ *
+ * Set a single color register. The values supplied have a 16 bit magnitude
+ * which needs to be scaled in this function for the hardware. Things to take
+ * into consideration are how many color registers, if any, are supported with
+ * the current color visual. With truecolor mode no color palettes are
+ * supported. Here a psuedo palette is created which we store the value in
+ * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited
+ * color palette.
+ */
+static int mx2fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info)
+{
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (info->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = info->pseudo_palette;
+ u32 v;
+
+#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
+ red = CNVT_TOHW(red, info->var.red.length);
+ green = CNVT_TOHW(green, info->var.green.length);
+ blue = CNVT_TOHW(blue, info->var.blue.length);
+ transp = CNVT_TOHW(transp, info->var.transp.length);
+#undef CNVT_TOHW
+
+ v = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+
+ pal[regno] = v;
+ ret = 0;
+ }
+ break;
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+
+ return ret;
+}
+
+/*!
+ * @brief Pans the display.
+ *
+ * @param var Frame buffer variable screen structure
+ * @param info Frame buffer structure that represents a single frame buffer
+ *
+ * @return Negative errno on error, or zero on success.
+ *
+ * Pan (or wrap, depending on the `vmode' field) the display using the
+ * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values
+ * don't fit, return -EINVAL.
+ */
+static int mx2fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ if ((info->var.xoffset == var->xoffset) &&
+ (info->var.yoffset == var->yoffset)) {
+ return 0; /* No change, do nothing */
+ }
+
+ if (var->xoffset < 0 || var->yoffset < 0
+ || var->xoffset + info->var.xres > info->var.xres_virtual
+ || var->yoffset + info->var.yres > info->var.yres_virtual)
+ return -EINVAL;
+
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+
+ _update_lcdc(info);
+
+ if (var->vmode & FB_VMODE_YWRAP) {
+ info->var.vmode |= FB_VMODE_YWRAP;
+ } else {
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Blanks the display.
+ *
+ * @param blank_mode The blank mode we want.
+ * @param info Frame buffer structure that represents a single frame buffer
+ *
+ * @return Negative errno on error, or zero on success.
+ *
+ * Blank the screen if blank_mode != 0, else unblank. Return 0 if blanking
+ * succeeded, != 0 if un-/blanking failed.
+ * blank_mode == 2: suspend vsync
+ * blank_mode == 3: suspend hsync
+ * blank_mode == 4: powerdown
+ */
+static int mx2fb_blank(int blank_mode, struct fb_info *info)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ dev_dbg(info->device, "blank mode = %d\n", blank_mode);
+
+ mx2fbi->blank = blank_mode;
+
+ switch (blank_mode) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ _disable_lcdc(info);
+ break;
+ case FB_BLANK_UNBLANK:
+ _enable_lcdc(info);
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Ioctl function to support customized ioctl operations.
+ *
+ * @param info Framebuffer structure that represents a single frame buffer
+ * @param cmd The command number
+ * @param arg Argument which depends on cmd
+ *
+ * @return Negative errno on error, or zero on success.
+ */
+static int mx2fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+ struct mx2fb_gbl_alpha ga;
+ struct mx2fb_color_key ck;
+
+ switch (cmd) {
+ case MX2FB_SET_GBL_ALPHA:
+ if (mx2fbi->type != MX2FB_TYPE_GW)
+ return -ENODEV;
+
+ if (!arg)
+ return -EINVAL;
+
+ /* set graphic window information */
+ if (copy_from_user((void *)&ga, (void *)arg, sizeof(ga)))
+ return -EFAULT;
+
+ g_gwinfo.alpha_value = ga.alpha;
+
+ if (g_gwinfo.enabled)
+ _enable_graphic_window(info);
+ else
+ _disable_graphic_window(info);
+ break;
+ case MX2FB_SET_CLR_KEY:
+ if (mx2fbi->type != MX2FB_TYPE_GW)
+ return -ENODEV;
+
+ if (!arg)
+ return -EINVAL;
+
+ /* set graphic window information */
+ if (copy_from_user((void *)&ck, (void *)arg, sizeof(ck)))
+ return -EFAULT;
+
+ g_gwinfo.ck_enabled = ck.enable;
+ g_gwinfo.ck_red = (ck.color_key & 0x003F0000) >> 16;
+ g_gwinfo.ck_green = (ck.color_key & 0x00003F00) >> 8;
+ g_gwinfo.ck_blue = ck.color_key & 0x0000003F;
+
+ if (g_gwinfo.enabled)
+ _enable_graphic_window(info);
+ else
+ _disable_graphic_window(info);
+ break;
+ case FBIOGET_GWINFO:
+ if (mx2fbi->type != MX2FB_TYPE_GW)
+ return -ENODEV;
+
+ if (!arg)
+ return -EINVAL;
+
+ /* get graphic window information */
+ if (copy_to_user((void *)arg, (void *)&g_gwinfo,
+ sizeof(g_gwinfo)))
+ return -EFAULT;
+ break;
+ case FBIOPUT_GWINFO:
+ if (mx2fbi->type != MX2FB_TYPE_GW)
+ return -ENODEV;
+
+ if (!arg)
+ return -EINVAL;
+
+ /* set graphic window information */
+ if (copy_from_user((void *)&g_gwinfo, (void *)arg,
+ sizeof(g_gwinfo)))
+ return -EFAULT;
+
+ if (g_gwinfo.enabled)
+ _enable_graphic_window(info);
+ else
+ _disable_graphic_window(info);
+ break;
+#ifdef CONFIG_FB_MXC_TVOUT
+ case ENCODER_GET_CAPABILITIES:{
+ int ret;
+ struct video_encoder_capability cap;
+
+ if (mx2fbi->type != MX2FB_TYPE_BG)
+ return -ENODEV;
+
+ ret = fs453_ioctl(cmd, &cap);
+ if (ret)
+ return ret;
+
+ if (copy_to_user((void *)arg, &cap, sizeof(cap)))
+ return -EFAULT;
+ break;
+ }
+ case ENCODER_SET_NORM:{
+ int ret;
+ unsigned long mode;
+ char *smode;
+ struct fb_var_screeninfo var;
+
+ if (mx2fbi->type != MX2FB_TYPE_BG)
+ return -ENODEV;
+
+ if (copy_from_user(&mode, (void *)arg, sizeof(mode)))
+ return -EFAULT;
+ if ((ret = fs453_ioctl(cmd, &mode)))
+ return ret;
+
+ if (mode == VIDEO_ENCODER_PAL)
+ smode = MODE_PAL;
+ else if (mode == VIDEO_ENCODER_NTSC)
+ smode = MODE_NTSC;
+ else
+ smode = MODE_VGA;
+
+ var = info->var;
+ var.nonstd = 0;
+ ret = fb_find_mode(&var, info, smode, mxcfb_modedb,
+ mxcfb_modedb_sz, NULL, default_bpp);
+ if ((ret != 1) && (ret != 2)) /* specified mode not found */
+ return -ENODEV;
+
+ info->var = var;
+ fb_mode = smode;
+ return mx2fb_set_par(info);
+ }
+ case ENCODER_SET_INPUT:
+ case ENCODER_SET_OUTPUT:
+ case ENCODER_ENABLE_OUTPUT:{
+ unsigned long varg;
+
+ if (mx2fbi->type != MX2FB_TYPE_BG)
+ return -ENODEV;
+
+ if (copy_from_user(&varg, (void *)arg, sizeof(varg)))
+ return -EFAULT;
+ return fs453_ioctl(cmd, &varg);
+ }
+#endif
+ default:
+ dev_dbg(info->device, "Unknown ioctl command (0x%08X)\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Set fixed framebuffer parameters based on variable settings.
+ *
+ * @param info framebuffer information pointer
+ * @return Negative errno on error, or zero on success.
+ */
+static void _set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ strncpy(fix->id, mx2fbi->id, strlen(mx2fbi->id));
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+}
+
+/*!
+ * @brief Initialize framebuffer information structure.
+ *
+ * @param info framebuffer information pointer
+ * @param pdev pointer to struct device
+ * @return Negative errno on error, or zero on success.
+ */
+static int __init _init_fbinfo(struct fb_info *info,
+ struct platform_device *pdev)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ info->device = &pdev->dev;
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->fbops = &mx2fb_ops;
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->pseudo_palette = &mx2fbi->pseudo_palette;
+
+ /* Allocate colormap */
+ fb_alloc_cmap(&info->cmap, 16, 0);
+
+ return 0;
+}
+
+/*!
+ * @brief Install framebuffer into the system.
+ *
+ * @param info framebuffer information pointer
+ * @param pdev pointer to struct device
+ * @return Negative errno on error, or zero on success.
+ */
+static int __init _install_fb(struct fb_info *info,
+ struct platform_device *pdev)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ if (_init_fbinfo(info, pdev))
+ return -EINVAL;
+
+ if (fb_mode == 0)
+ fb_mode = pdev->dev.platform_data;
+
+ if (!fb_find_mode(&info->var, info, fb_mode, mxcfb_modedb,
+ mxcfb_modedb_sz, NULL, default_bpp)) {
+ fb_dealloc_cmap(&info->cmap);
+ return -EBUSY;
+ }
+
+ /* Default Y virtual size is 2x panel size */
+ /* info->var.yres_virtual = info->var.yres << 1; */
+
+ if (mx2fbi->type == MX2FB_TYPE_GW)
+ mx2fbi->blank = FB_BLANK_NORMAL;
+ else
+ mx2fbi->blank = FB_BLANK_UNBLANK;
+
+ if (mx2fb_set_par(info)) {
+ fb_dealloc_cmap(&info->cmap);
+ return -EINVAL;
+ }
+
+ if (register_framebuffer(info) < 0) {
+ _unmap_video_memory(info);
+ fb_dealloc_cmap(&info->cmap);
+ return -EINVAL;
+ }
+
+ mx2fbi->registered = 1;
+ dev_info(info->device, "fb%d: %s fb device registered successfully.\n",
+ info->node, info->fix.id);
+
+ return 0;
+}
+
+/*!
+ * @brief Uninstall framebuffer from the system.
+ *
+ * @param info framebuffer information pointer
+ */
+static void __exit _uninstall_fb(struct fb_info *info)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ if (!mx2fbi->registered)
+ return;
+
+ unregister_framebuffer(info);
+ _unmap_video_memory(info);
+ if (&info->cmap)
+ fb_dealloc_cmap(&info->cmap);
+
+ mx2fbi->registered = 0;
+}
+
+/*!
+ * @brief Allocate memory for framebuffer.
+ *
+ * @param info framebuffer information pointer
+ * @return Negative errno on error, or zero on success.
+ */
+static int _map_video_memory(struct fb_info *info)
+{
+ info->fix.smem_len = info->fix.line_length * info->var.yres_virtual;
+ info->screen_base = dma_alloc_coherent(0,
+ info->fix.smem_len,
+ (dma_addr_t *) & info->fix.
+ smem_start,
+ GFP_DMA | GFP_KERNEL);
+
+ if (info->screen_base == 0) {
+ dev_err(info->device, "Unable to allocate fb memory\n");
+ return -EBUSY;
+ }
+ dev_dbg(info->device, "Allocated fb @ paddr=0x%08lX, size=%d.\n",
+ info->fix.smem_start, info->fix.smem_len);
+
+ info->screen_size = info->fix.smem_len;
+
+ /* Clear the screen */
+ memset((char *)info->screen_base, 0, info->fix.smem_len);
+
+ return 0;
+}
+
+/*!
+ * @brief Release memory for framebuffer.
+ * @param info framebuffer information pointer
+ */
+static void _unmap_video_memory(struct fb_info *info)
+{
+ dma_free_coherent(0, info->fix.smem_len, info->screen_base,
+ (dma_addr_t) info->fix.smem_start);
+
+ info->screen_base = 0;
+ info->fix.smem_start = 0;
+ info->fix.smem_len = 0;
+}
+
+/*!
+ * @brief Enable LCD controller.
+ * @param info framebuffer information pointer
+ */
+static void _enable_lcdc(struct fb_info *info)
+{
+ static int first_enable = 1;
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ /*
+ * Graphic window can only be enabled while the HCLK to the LCDC
+ * is disabled. Once enabled it can subsequently be disabled and
+ * enabled without turning off the HCLK.
+ * The graphic window is enabled and then disabled here. So next
+ * time to enable graphic window the HCLK to LCDC does not need
+ * to be disabled, and the flicker (due to disabling of HCLK to
+ * LCDC) is avoided.
+ */
+ if (first_enable) {
+ _enable_graphic_window(info);
+ _disable_graphic_window(info);
+ first_enable = 0;
+ }
+
+ if (mx2fbi->type == MX2FB_TYPE_GW)
+ _enable_graphic_window(info);
+ else if (!fb_enabled) {
+ clk_enable(lcdc_clk);
+ gpio_lcdc_active();
+ board_power_lcd(1);
+ fb_enabled++;
+#ifdef CONFIG_FB_MXC_TVOUT
+ if (fb_mode) {
+ unsigned long mode = 0;
+
+ if (strcmp(fb_mode, MODE_VGA) == 0)
+ mode = VIDEO_ENCODER_VGA;
+ else if (strcmp(fb_mode, MODE_NTSC) == 0)
+ mode = VIDEO_ENCODER_NTSC;
+ else if (strcmp(fb_mode, MODE_PAL) == 0)
+ mode = VIDEO_ENCODER_PAL;
+ if (mode)
+ fs453_ioctl(ENCODER_SET_NORM, &mode);
+ }
+#endif
+ }
+}
+
+/*!
+ * @brief Disable LCD controller.
+ * @param info framebuffer information pointer
+ */
+static void _disable_lcdc(struct fb_info *info)
+{
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ if (mx2fbi->type == MX2FB_TYPE_GW)
+ _disable_graphic_window(info);
+ else {
+ if (fb_enabled) {
+ gpio_lcdc_inactive();
+ board_power_lcd(0);
+ clk_disable(lcdc_clk);
+ fb_enabled = 0;
+ }
+#ifdef CONFIG_FB_MXC_TVOUT
+ if (fb_mode) {
+ int enable = 0;
+
+ if ((strcmp(fb_mode, MODE_VGA) == 0)
+ || (strcmp(fb_mode, MODE_NTSC) == 0)
+ || (strcmp(fb_mode, MODE_PAL) == 0))
+ fs453_ioctl(ENCODER_ENABLE_OUTPUT, &enable);
+ }
+#endif
+ }
+}
+
+/*!
+ * @brief Enable graphic window.
+ * @param info framebuffer information pointer
+ */
+static void _enable_graphic_window(struct fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->var;
+
+ g_gwinfo.enabled = 1;
+
+ g_gwinfo.base = (var->yoffset * var->xres_virtual + var->xoffset);
+ g_gwinfo.base *= (var->bits_per_pixel) / 8;
+ g_gwinfo.base += info->fix.smem_start;
+
+ g_gwinfo.xres = var->xres;
+ g_gwinfo.yres = var->yres;
+ g_gwinfo.xres_virtual = var->xres_virtual;
+
+ mx2_gw_set(&g_gwinfo);
+}
+
+/*!
+ * @brief Disable graphic window.
+ * @param info framebuffer information pointer
+ */
+static void _disable_graphic_window(struct fb_info *info)
+{
+ unsigned long i = 0;
+
+ g_gwinfo.enabled = 0;
+
+ /*
+ * Set alpha value to zero and reduce gw size, otherwise the graphic
+ * window will not be able to be enabled again.
+ */
+ __raw_writel(__raw_readl(LCDC_REG(LCDC_LGWCR)) & 0x00FFFFFF,
+ LCDC_REG(LCDC_LGWCR));
+ __raw_writel(((16 >> 4) << 20) + 16, LCDC_REG(LCDC_LGWSR));
+ while (i < 1000)
+ i++;
+
+ /* Now disable graphic window */
+ __raw_writel(__raw_readl(LCDC_REG(LCDC_LGWCR)) & ~0x00400000,
+ LCDC_REG(LCDC_LGWCR));
+
+ dev_dbg(info->device, "Graphic window disabled.\n");
+}
+
+/*!
+ * @brief Setup graphic window properties.
+ * @param gwinfo graphic window information pointer
+ */
+void mx2_gw_set(struct fb_gwinfo *gwinfo)
+{
+ int width, height, xpos, ypos;
+ int width_bg, height_bg;
+ unsigned long lgwcr = 0x00400000; /* Graphic window control register */
+
+ if (!gwinfo->enabled) {
+ _disable_graphic_window(0);
+ return;
+ }
+
+ /* Graphic window start address register */
+ __raw_writel(gwinfo->base, LCDC_REG(LCDC_LGWSAR));
+
+ /*
+ * The graphic window width, height, x position and y position
+ * must be synced up width the background window, otherwise there
+ * may be flickering.
+ */
+ width_bg = (__raw_readl(LCDC_REG(LCDC_LSR)) & 0x03F00000) >> 16;
+ height_bg = __raw_readl(LCDC_REG(LCDC_LSR)) & 0x000003FF;
+
+ width = (gwinfo->xres > width_bg) ? width_bg : gwinfo->xres;
+ height = (gwinfo->yres > height_bg) ? height_bg : gwinfo->yres;
+
+ xpos = gwinfo->xpos;
+ ypos = gwinfo->ypos;
+
+ if (xpos + width > width_bg)
+ xpos = width_bg - width;
+ if (ypos + height > height_bg)
+ ypos = height_bg - height;
+
+ /* Graphic window size register */
+ __raw_writel(((width >> 4) << 20) + height, LCDC_REG(LCDC_LGWSR));
+
+ /* Graphic window virtual page width register */
+ __raw_writel(gwinfo->xres_virtual >> 1, LCDC_REG(LCDC_LGWVPWR));
+
+ /* Graphic window position register */
+ __raw_writel(((xpos & 0x000003FF) << 16) | (ypos & 0x000003FF),
+ LCDC_REG(LCDC_LGWPR));
+
+ /* Graphic window panning offset register */
+ __raw_writel(0, LCDC_REG(LCDC_LGWPOR));
+
+ /* Graphic window DMA control register */
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0)
+ __raw_writel(0x00040060, LCDC_REG(LCDC_LGWDCR));
+ else
+ __raw_writel(0x00020010, LCDC_REG(LCDC_LGWDCR));
+
+ /* Graphic window control register */
+ lgwcr |= (gwinfo->alpha_value & 0x000000FF) << 24;
+ lgwcr |= gwinfo->ck_enabled ? 0x00800000 : 0;
+ lgwcr |= gwinfo->vs_reversed ? 0x00200000 : 0;
+
+ /*
+ * Color keying value
+ * Todo: assume always use RGB565
+ */
+ lgwcr |= (gwinfo->ck_red & 0x0000003F) << 12;
+ lgwcr |= (gwinfo->ck_green & 0x0000003F) << 6;
+ lgwcr |= gwinfo->ck_blue & 0x0000003F;
+
+ __raw_writel(lgwcr, LCDC_REG(LCDC_LGWCR));
+
+ pr_debug("Graphic window enabled.\n");
+}
+
+/*!
+ * @brief Update LCDC registers
+ * @param info framebuffer information pointer
+ */
+static void _update_lcdc(struct fb_info *info)
+{
+ unsigned long base;
+ unsigned long perclk, pcd, pcr;
+ struct fb_var_screeninfo *var = &info->var;
+ struct mx2fb_info *mx2fbi = (struct mx2fb_info *)info->par;
+
+ if (mx2fbi->type == MX2FB_TYPE_GW) {
+ _enable_graphic_window(info);
+ return;
+ }
+
+ base = (var->yoffset * var->xres_virtual + var->xoffset);
+ base *= (var->bits_per_pixel) / 8;
+ base += info->fix.smem_start;
+
+ /* Screen start address register */
+ __raw_writel(base, LCDC_REG(LCDC_LSSAR));
+
+ /* Size register */
+ dev_dbg(info->device, "xres = %d, yres = %d\n",
+ info->var.xres, info->var.yres);
+ __raw_writel(((info->var.xres >> 4) << 20) + info->var.yres,
+ LCDC_REG(LCDC_LSR));
+
+ /* Virtual page width register */
+ __raw_writel(info->var.xres_virtual >> 1, LCDC_REG(LCDC_LVPWR));
+
+ /* To setup LCDC pixel clock */
+ perclk = clk_round_rate(lcdc_clk, 134000000);
+ if (clk_set_rate(lcdc_clk, perclk)) {
+ printk(KERN_INFO "mx2fb: Unable to set clock to %lu\n", perclk);
+ perclk = clk_get_rate(lcdc_clk);
+ }
+
+ /* Calculate pixel clock divider, and round to the nearest integer */
+ pcd = (perclk * 8 / (PICOS2KHZ(var->pixclock) * 1000UL) + 4) / 8;
+ if (--pcd > 0x3F)
+ pcd = 0x3F;
+
+ /* Panel configuration register */
+ pcr = 0xFA008B80 | pcd;
+ pcr |= (var->sync & FB_SYNC_CLK_LAT_FALL) ? 0x00200000 : 0;
+ pcr |= (var->sync & FB_SYNC_DATA_INVERT) ? 0x01000000 : 0;
+ pcr |= (var->sync & FB_SYNC_SHARP_MODE) ? 0x00000040 : 0;
+ pcr |= (var->sync & FB_SYNC_OE_LOW_ACT) ? 0x00100000 : 0;
+ __raw_writel(pcr, LCDC_REG(LCDC_LPCR));
+
+ /* Horizontal and vertical configuration register */
+ __raw_writel(((var->hsync_len - 1) << 26)
+ + ((var->right_margin - 1) << 8)
+ + (var->left_margin - 3), LCDC_REG(LCDC_LHCR));
+ __raw_writel((var->vsync_len << 26)
+ + (var->lower_margin << 8)
+ + var->upper_margin, LCDC_REG(LCDC_LVCR));
+
+ /* Sharp configuration register */
+ __raw_writel(0x00120300, LCDC_REG(LCDC_LSCR));
+
+ /* Refresh mode control reigster */
+ __raw_writel(0x00000000, LCDC_REG(LCDC_LRMCR));
+
+ /* DMA control register */
+ if (cpu_is_mx27_rev(CHIP_REV_2_0) > 0)
+ __raw_writel(0x00040060, LCDC_REG(LCDC_LDCR));
+ else
+ __raw_writel(0x00020010, LCDC_REG(LCDC_LDCR));
+}
+
+/*!
+ * @brief Set LCD brightness
+ * @param level brightness level
+ */
+void mx2fb_set_brightness(uint8_t level)
+{
+ /* Set LCDC PWM contract control register */
+ __raw_writel(0x00A90300 | level, LCDC_REG(LCDC_LPCCR));
+}
+
+EXPORT_SYMBOL(mx2fb_set_brightness);
+
+/*
+ * @brief LCDC interrupt handler
+ */
+static irqreturn_t mx2fb_isr(int irq, void *dev_id)
+{
+ struct fb_event event;
+ unsigned long status = __raw_readl(LCDC_REG(LCDC_LISR));
+
+ if (status & MX2FB_INT_EOF) {
+ event.info = &mx2fb_info[0];
+ atomic_notifier_call_chain(&mx2fb_notifier_list,
+ FB_EVENT_MXC_EOF, &event);
+ }
+
+ if (status & MX2FB_INT_GW_EOF) {
+ event.info = &mx2fb_info[1];
+ atomic_notifier_call_chain(&mx2fb_notifier_list,
+ FB_EVENT_MXC_EOF, &event);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * @brief Config and request LCDC interrupt
+ */
+static void _request_irq(void)
+{
+ unsigned long status;
+ unsigned long flags;
+
+ /* Read to clear the status */
+ status = __raw_readl(LCDC_REG(LCDC_LISR));
+
+ if (request_irq(MXC_INT_LCDC, mx2fb_isr, 0, "LCDC", 0))
+ pr_info("Request LCDC IRQ failed.\n");
+ else {
+ spin_lock_irqsave(&mx2fb_notifier_list.lock, flags);
+
+ /* Enable interrupt in case client has registered */
+ if (mx2fb_notifier_list.head != NULL) {
+ unsigned long status;
+ unsigned long ints = MX2FB_INT_EOF;
+
+ ints |= MX2FB_INT_GW_EOF;
+
+ /* Read to clear the status */
+ status = __raw_readl(LCDC_REG(LCDC_LISR));
+
+ /* Configure interrupt condition for EOF */
+ __raw_writel(0x0, LCDC_REG(LCDC_LICR));
+
+ /* Enable EOF and graphic window EOF interrupt */
+ __raw_writel(ints, LCDC_REG(LCDC_LIER));
+ }
+
+ spin_unlock_irqrestore(&mx2fb_notifier_list.lock, flags);
+ }
+}
+
+/*!
+ * @brief Free LCDC interrupt handler
+ */
+static void _free_irq(void)
+{
+ /* Disable all LCDC interrupt */
+ __raw_writel(0x0, LCDC_REG(LCDC_LIER));
+
+ free_irq(MXC_INT_LCDC, 0);
+}
+
+/*!
+ * @brief Register a client notifier
+ * @param nb notifier block to callback on events
+ */
+int mx2fb_register_client(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret;
+
+ ret = atomic_notifier_chain_register(&mx2fb_notifier_list, nb);
+
+ spin_lock_irqsave(&mx2fb_notifier_list.lock, flags);
+
+ /* Enable interrupt in case client has registered */
+ if (mx2fb_notifier_list.head != NULL) {
+ unsigned long status;
+ unsigned long ints = MX2FB_INT_EOF;
+
+ ints |= MX2FB_INT_GW_EOF;
+
+ /* Read to clear the status */
+ status = __raw_readl(LCDC_REG(LCDC_LISR));
+
+ /* Configure interrupt condition for EOF */
+ __raw_writel(0x0, LCDC_REG(LCDC_LICR));
+
+ /* Enable EOF and graphic window EOF interrupt */
+ __raw_writel(ints, LCDC_REG(LCDC_LIER));
+ }
+
+ spin_unlock_irqrestore(&mx2fb_notifier_list.lock, flags);
+
+ return ret;
+}
+
+/*!
+ * @brief Unregister a client notifier
+ * @param nb notifier block to callback on events
+ */
+int mx2fb_unregister_client(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret;
+
+ ret = atomic_notifier_chain_unregister(&mx2fb_notifier_list, nb);
+
+ spin_lock_irqsave(&mx2fb_notifier_list.lock, flags);
+
+ /* Mask interrupt in case no client registered */
+ if (mx2fb_notifier_list.head == NULL)
+ __raw_writel(0x0, LCDC_REG(LCDC_LIER));
+
+ spin_unlock_irqrestore(&mx2fb_notifier_list.lock, flags);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Power management hooks. Note that we won't be called from IRQ context,
+ * unlike the blank functions above, so we may sleep.
+ */
+
+/*!
+ * @brief Suspends the framebuffer and blanks the screen.
+ * Power management support
+ */
+static int mx2fb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ _disable_lcdc(&mx2fb_info[0]);
+
+ return 0;
+}
+
+/*!
+ * @brief Resumes the framebuffer and unblanks the screen.
+ * Power management support
+ */
+static int mx2fb_resume(struct platform_device *pdev)
+{
+ _enable_lcdc(&mx2fb_info[0]);
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+/*!
+ * @brief Probe routine for the framebuffer driver. It is called during the
+ * driver binding process.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mx2fb_probe(struct platform_device *pdev)
+{
+ int ret, i;
+
+ lcdc_clk = clk_get(&pdev->dev, "lcdc_clk");
+
+ for (i = 0; i < sizeof(mx2fb_info) / sizeof(struct fb_info); i++) {
+ if ((ret = _install_fb(&mx2fb_info[i], pdev))) {
+ dev_err(&pdev->dev,
+ "Failed to register framebuffer %d\n", i);
+ return ret;
+ }
+ }
+ _request_irq();
+
+ return 0;
+}
+
+/*!
+ * @brief Initialization
+ */
+int __init mx2fb_init(void)
+{
+ /*
+ * For kernel boot options (in 'video=xxxfb:<options>' format)
+ */
+#ifndef MODULE
+ {
+ char *option;
+
+ if (fb_get_options("mxcfb", &option))
+ return -ENODEV;
+ mx2fb_setup(option);
+ }
+#endif
+ return platform_driver_register(&mx2fb_driver);
+}
+
+/*!
+ * @brief Cleanup
+ */
+void __exit mx2fb_exit(void)
+{
+ int i;
+
+ _free_irq();
+ for (i = sizeof(mx2fb_info) / sizeof(struct fb_info); i > 0; i--)
+ _uninstall_fb(&mx2fb_info[i - 1]);
+
+ platform_driver_unregister(&mx2fb_driver);
+}
+
+#ifndef MODULE
+/*!
+ * @brief Setup
+ * Parse user specified options
+ * Example: video=mxcfb:240x320,bpp=16,Sharp-QVGA
+ */
+static int __init mx2fb_setup(char *options)
+{
+ char *opt;
+
+ if (!options || !*options)
+ return 0;
+
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+
+ if (!strncmp(opt, "bpp=", 4))
+ default_bpp = simple_strtoul(opt + 4, NULL, 0);
+ else
+ fb_mode = opt;
+ }
+
+ return 0;
+}
+#endif
+
+/* Modularization */
+module_init(mx2fb_init);
+module_exit(mx2fb_exit);
+
+EXPORT_SYMBOL(mx2_gw_set);
+EXPORT_SYMBOL(mx2fb_register_client);
+EXPORT_SYMBOL(mx2fb_unregister_client);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MX2 framebuffer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mx2fb.h b/drivers/video/mxc/mx2fb.h
new file mode 100644
index 000000000000..ed20d78289ce
--- /dev/null
+++ b/drivers/video/mxc/mx2fb.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx2fb.h
+ *
+ * @brief Header file for the MX27 Frame buffer
+ *
+ * @ingroup Framebuffer
+ */
+#ifndef __MX2FB_H__
+#define __MX2FB_H__
+
+/*! @brief MX27 LCDC graphic window information */
+struct fb_gwinfo {
+ /*! Non-zero if graphic window is enabled */
+ __u32 enabled;
+
+ /* The fields below are valid only when graphic window is enabled */
+
+ /*! Graphic window alpha value from 0 to 255 */
+ __u32 alpha_value;
+
+ /*! Non-zero if graphic window color keying is enabled. */
+ __u32 ck_enabled;
+
+ /*
+ * The fields ck_red, ck_green and ck_blue are valid only when
+ * graphic window and the color keying are enabled. They are the
+ * color component of graphic window color keying.
+ */
+
+ /*! Color keying red component */
+ __u32 ck_red;
+
+ /*! Color keying green component */
+ __u32 ck_green;
+
+ /*! Color keying blue component */
+ __u32 ck_blue;
+
+ /*! Graphic window x position */
+ __u32 xpos;
+
+ /*! Graphic window y position */
+ __u32 ypos;
+
+ /*! Non-zero if graphic window vertical scan in reverse direction. */
+ __u32 vs_reversed;
+
+ /*
+ * The following fields are valid for FBIOGET_GWINFO and
+ * mx2_gw_set(). FBIOPUT_GWINFO ignores these fields.
+ */
+ __u32 base; /* Graphic window start address */
+ __u32 xres; /* Visible x resolution */
+ __u32 yres; /* Visible y resolution */
+ __u32 xres_virtual; /* Virtual x resolution */
+};
+
+/* 0x46E0-0x46FF are reserved for MX27 */
+#define FBIOGET_GWINFO 0x46E0 /*!< Get graphic window information */
+#define FBIOPUT_GWINFO 0x46E1 /*!< Set graphic window information */
+
+struct mx2fb_gbl_alpha {
+ int enable;
+ int alpha;
+};
+
+struct mx2fb_color_key {
+ int enable;
+ __u32 color_key;
+};
+
+#define MX2FB_SET_GBL_ALPHA _IOW('M', 0, struct mx2fb_gbl_alpha)
+#define MX2FB_SET_CLR_KEY _IOW('M', 1, struct mx2fb_color_key)
+#define MX2FB_WAIT_FOR_VSYNC _IOW('F', 0x20, u_int32_t)
+
+#ifdef __KERNEL__
+
+/*
+ * LCDC register definitions
+ */
+#define LCDC_LSSAR 0x00
+#define LCDC_LSR 0x04
+#define LCDC_LVPWR 0x08
+#define LCDC_LCPR 0x0C
+#define LCDC_LCWHBR 0x10
+#define LCDC_LCCMR 0x14
+#define LCDC_LPCR 0x18
+#define LCDC_LHCR 0x1C
+#define LCDC_LVCR 0x20
+#define LCDC_LPOR 0x24
+#define LCDC_LSCR 0x28
+#define LCDC_LPCCR 0x2C
+#define LCDC_LDCR 0x30
+#define LCDC_LRMCR 0x34
+#define LCDC_LICR 0x38
+#define LCDC_LIER 0x3C
+#define LCDC_LISR 0x40
+#define LCDC_LGWSAR 0x50
+#define LCDC_LGWSR 0x54
+#define LCDC_LGWVPWR 0x58
+#define LCDC_LGWPOR 0x5C
+#define LCDC_LGWPR 0x60
+#define LCDC_LGWCR 0x64
+#define LCDC_LGWDCR 0x68
+#define LCDC_LAUSCR 0x80
+#define LCDC_LAUSCCR 0x84
+
+#define LCDC_REG(reg) (IO_ADDRESS(LCDC_BASE_ADDR) + reg)
+
+#define MX2FB_INT_BOF 0x0001 /* Beginning of Frame */
+#define MX2FB_INT_EOF 0x0002 /* End of Frame */
+#define MX2FB_INT_ERR_RES 0x0004 /* Error Response */
+#define MX2FB_INT_UDR_ERR 0x0008 /* Under Run Error */
+#define MX2FB_INT_GW_BOF 0x0010 /* Graphic Window BOF */
+#define MX2FB_INT_GW_EOF 0x0020 /* Graphic Window EOF */
+#define MX2FB_INT_GW_ERR_RES 0x0040 /* Graphic Window ERR_RES */
+#define MX2FB_INT_GW_UDR_ERR 0x0080 /* Graphic Window UDR_ERR */
+
+#define FB_EVENT_MXC_EOF 0x8001 /* End of Frame event */
+
+int mx2fb_register_client(struct notifier_block *nb);
+int mx2fb_unregister_client(struct notifier_block *nb);
+
+void mx2_gw_set(struct fb_gwinfo *gwinfo);
+
+#endif /* __KERNEL__ */
+
+#endif /* __MX2FB_H__ */
diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c
new file mode 100644
index 000000000000..a6a87bd86f65
--- /dev/null
+++ b/drivers/video/mxc/mxc_edid.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxc_edid.c
+ *
+ * @brief MXC EDID tools
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/fb.h>
+
+#define EDID_LENGTH 128
+
+static u8 edid[EDID_LENGTH];
+
+int read_edid(struct i2c_adapter *adp,
+ struct fb_var_screeninfo *einfo,
+ int *dvi)
+{
+ u8 buf0[2] = {0, 0};
+ int dat = 0;
+ u16 addr = 0x50;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buf0,
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ .buf = edid,
+ },
+ };
+
+ if (adp == NULL || einfo == NULL)
+ return -EINVAL;
+
+ buf0[0] = 0x00;
+ memset(&edid, 0, sizeof(edid));
+ memset(einfo, 0, sizeof(struct fb_var_screeninfo));
+ dat = i2c_transfer(adp, msg, 2);
+
+ /* If 0x50 fails, try 0x37. */
+ if (edid[1] == 0x00) {
+ msg[0].addr = msg[1].addr = 0x37;
+ dat = i2c_transfer(adp, msg, 2);
+ }
+
+ if (edid[1] == 0x00)
+ return -ENOENT;
+
+ *dvi = 0;
+ if ((edid[20] == 0x80) || (edid[20] == 0x88) || (edid[20] == 0))
+ *dvi = 1;
+
+ dat = fb_parse_edid(edid, einfo);
+ if (dat)
+ return -dat;
+
+ /* This is valid for version 1.3 of the EDID */
+ if ((edid[18] == 1) && (edid[19] == 3)) {
+ einfo->height = edid[21] * 10;
+ einfo->width = edid[22] * 10;
+ }
+
+ return 0;
+}
+
+
diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c
new file mode 100644
index 000000000000..279764d9a3a4
--- /dev/null
+++ b/drivers/video/mxc/mxc_ipuv3_fb.c
@@ -0,0 +1,1599 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <mach/hardware.h>
+
+/*
+ * Driver name
+ */
+#define MXCFB_NAME "mxc_sdc_fb"
+/*!
+ * Structure containing the MXC specific framebuffer information.
+ */
+struct mxcfb_info {
+ int blank;
+ ipu_channel_t ipu_ch;
+ int ipu_di;
+ u32 ipu_di_pix_fmt;
+ bool overlay;
+ bool alpha_chan_en;
+ dma_addr_t alpha_phy_addr0;
+ dma_addr_t alpha_phy_addr1;
+ void *alpha_virt_addr0;
+ void *alpha_virt_addr1;
+ uint32_t alpha_mem_len;
+ uint32_t ipu_ch_irq;
+ uint32_t cur_ipu_buf;
+ uint32_t cur_ipu_alpha_buf;
+
+ u32 pseudo_palette[16];
+
+ struct semaphore flip_sem;
+ struct semaphore alpha_flip_sem;
+ struct completion vsync_complete;
+};
+
+struct mxcfb_alloc_list {
+ struct list_head list;
+ dma_addr_t phy_addr;
+ void *cpu_addr;
+ u32 size;
+};
+
+enum {
+ BOTH_ON,
+ SRC_ON,
+ TGT_ON,
+ BOTH_OFF
+};
+
+static char *fb_mode;
+static unsigned long default_bpp = 16;
+static bool g_dp_in_use;
+LIST_HEAD(fb_alloc_list);
+static struct fb_info *mxcfb_info[3];
+
+static uint32_t bpp_to_pixfmt(struct fb_info *fbi)
+{
+ uint32_t pixfmt = 0;
+
+ if (fbi->var.nonstd)
+ return fbi->var.nonstd;
+
+ switch (fbi->var.bits_per_pixel) {
+ case 24:
+ pixfmt = IPU_PIX_FMT_BGR24;
+ break;
+ case 32:
+ pixfmt = IPU_PIX_FMT_BGR32;
+ break;
+ case 16:
+ pixfmt = IPU_PIX_FMT_RGB565;
+ break;
+ }
+ return pixfmt;
+}
+
+static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id);
+static int mxcfb_blank(int blank, struct fb_info *info);
+static int mxcfb_map_video_memory(struct fb_info *fbi);
+static int mxcfb_unmap_video_memory(struct fb_info *fbi);
+
+/*
+ * Set fixed framebuffer parameters based on variable settings.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+
+ return 0;
+}
+
+static int _setup_disp_channel1(struct fb_info *fbi)
+{
+ ipu_channel_params_t params;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ memset(&params, 0, sizeof(params));
+ params.mem_dp_bg_sync.di = mxc_fbi->ipu_di;
+
+ /*
+ * Assuming interlaced means yuv output, below setting also
+ * valid for mem_dc_sync. FG should have the same vmode as BG.
+ */
+ if (mxc_fbi->ipu_ch == MEM_FG_SYNC) {
+ struct mxcfb_info *mxc_fbi_tmp;
+ int i;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ mxc_fbi_tmp = (struct mxcfb_info *)
+ (registered_fb[i]->par);
+ if (mxc_fbi_tmp->ipu_ch == MEM_BG_SYNC) {
+ fbi->var.vmode =
+ registered_fb[i]->var.vmode;
+ break;
+ }
+ }
+ }
+ if (fbi->var.vmode & FB_VMODE_INTERLACED) {
+ params.mem_dp_bg_sync.interlaced = true;
+ params.mem_dp_bg_sync.out_pixel_fmt =
+ IPU_PIX_FMT_YUV444;
+ } else {
+ if (mxc_fbi->ipu_di_pix_fmt)
+ params.mem_dp_bg_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+ else
+ params.mem_dp_bg_sync.out_pixel_fmt = IPU_PIX_FMT_RGB666;
+ }
+ params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi);
+ if (mxc_fbi->alpha_chan_en)
+ params.mem_dp_bg_sync.alpha_chan_en = true;
+
+ ipu_init_channel(mxc_fbi->ipu_ch, &params);
+
+ return 0;
+}
+
+static int _setup_disp_channel2(struct fb_info *fbi)
+{
+ int retval = 0;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ mxc_fbi->cur_ipu_buf = 1;
+ sema_init(&mxc_fbi->flip_sem, 1);
+ if (mxc_fbi->alpha_chan_en) {
+ mxc_fbi->cur_ipu_alpha_buf = 1;
+ sema_init(&mxc_fbi->alpha_flip_sem, 1);
+ }
+ fbi->var.xoffset = fbi->var.yoffset = 0;
+
+ retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi),
+ fbi->var.xres, fbi->var.yres,
+ fbi->fix.line_length,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start +
+ (fbi->fix.line_length * fbi->var.yres),
+ fbi->fix.smem_start,
+ 0, 0);
+ if (retval) {
+ dev_err(fbi->device,
+ "ipu_init_channel_buffer error %d\n", retval);
+ }
+
+ if (mxc_fbi->alpha_chan_en) {
+ retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch,
+ IPU_ALPHA_IN_BUFFER,
+ IPU_PIX_FMT_GENERIC,
+ fbi->var.xres, fbi->var.yres,
+ fbi->var.xres,
+ IPU_ROTATE_NONE,
+ mxc_fbi->alpha_phy_addr0,
+ mxc_fbi->alpha_phy_addr1,
+ 0, 0);
+ if (retval) {
+ dev_err(fbi->device,
+ "ipu_init_channel_buffer error %d\n", retval);
+ return retval;
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Set framebuffer parameters and change the operating mode.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_par(struct fb_info *fbi)
+{
+ int retval = 0;
+ u32 mem_len, alpha_mem_len;
+ ipu_di_signal_cfg_t sig_cfg;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ dev_dbg(fbi->device, "Reconfiguring framebuffer\n");
+
+ ipu_disable_irq(mxc_fbi->ipu_ch_irq);
+ ipu_disable_channel(mxc_fbi->ipu_ch, true);
+ ipu_uninit_channel(mxc_fbi->ipu_ch);
+ ipu_clear_irq(mxc_fbi->ipu_ch_irq);
+ mxcfb_set_fix(fbi);
+
+ mem_len = fbi->var.yres_virtual * fbi->fix.line_length;
+ if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) {
+ if (fbi->fix.smem_start)
+ mxcfb_unmap_video_memory(fbi);
+
+ if (mxcfb_map_video_memory(fbi) < 0)
+ return -ENOMEM;
+ }
+ if (mxc_fbi->alpha_chan_en) {
+ alpha_mem_len = fbi->var.xres * fbi->var.yres;
+ if ((!mxc_fbi->alpha_phy_addr0 && !mxc_fbi->alpha_phy_addr1) ||
+ (alpha_mem_len > mxc_fbi->alpha_mem_len)) {
+ if (mxc_fbi->alpha_phy_addr0)
+ dma_free_coherent(fbi->device,
+ mxc_fbi->alpha_mem_len,
+ mxc_fbi->alpha_virt_addr0,
+ mxc_fbi->alpha_phy_addr0);
+ if (mxc_fbi->alpha_phy_addr1)
+ dma_free_coherent(fbi->device,
+ mxc_fbi->alpha_mem_len,
+ mxc_fbi->alpha_virt_addr1,
+ mxc_fbi->alpha_phy_addr1);
+
+ mxc_fbi->alpha_virt_addr0 =
+ dma_alloc_coherent(fbi->device,
+ alpha_mem_len,
+ &mxc_fbi->alpha_phy_addr0,
+ GFP_DMA | GFP_KERNEL);
+
+ mxc_fbi->alpha_virt_addr1 =
+ dma_alloc_coherent(fbi->device,
+ alpha_mem_len,
+ &mxc_fbi->alpha_phy_addr1,
+ GFP_DMA | GFP_KERNEL);
+ if (mxc_fbi->alpha_virt_addr0 == NULL ||
+ mxc_fbi->alpha_virt_addr1 == NULL) {
+ dev_err(fbi->device, "mxcfb: dma alloc for"
+ " alpha buffer failed.\n");
+ if (mxc_fbi->alpha_virt_addr0)
+ dma_free_coherent(fbi->device,
+ mxc_fbi->alpha_mem_len,
+ mxc_fbi->alpha_virt_addr0,
+ mxc_fbi->alpha_phy_addr0);
+ if (mxc_fbi->alpha_virt_addr1)
+ dma_free_coherent(fbi->device,
+ mxc_fbi->alpha_mem_len,
+ mxc_fbi->alpha_virt_addr1,
+ mxc_fbi->alpha_phy_addr1);
+ return -ENOMEM;
+ }
+ mxc_fbi->alpha_mem_len = alpha_mem_len;
+ }
+ }
+
+ _setup_disp_channel1(fbi);
+
+ if (!mxc_fbi->overlay) {
+ uint32_t out_pixel_fmt;
+
+ memset(&sig_cfg, 0, sizeof(sig_cfg));
+ if (fbi->var.vmode & FB_VMODE_INTERLACED) {
+ sig_cfg.interlaced = true;
+ out_pixel_fmt = IPU_PIX_FMT_YUV444;
+ } else {
+ if (mxc_fbi->ipu_di_pix_fmt)
+ out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+ else
+ out_pixel_fmt = IPU_PIX_FMT_RGB666;
+ }
+ if (fbi->var.vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
+ sig_cfg.odd_field_first = true;
+ if (fbi->var.sync & FB_SYNC_EXT)
+ sig_cfg.ext_clk = true;
+ if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
+ sig_cfg.Hsync_pol = true;
+ if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
+ sig_cfg.Vsync_pol = true;
+ if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL))
+ sig_cfg.clk_pol = true;
+ if (fbi->var.sync & FB_SYNC_DATA_INVERT)
+ sig_cfg.data_pol = true;
+ if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT))
+ sig_cfg.enable_pol = true;
+ if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN)
+ sig_cfg.clkidle_en = true;
+
+ dev_dbg(fbi->device, "pixclock = %ul Hz\n",
+ (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
+
+ if (ipu_init_sync_panel(mxc_fbi->ipu_di,
+ (PICOS2KHZ(fbi->var.pixclock)) * 1000UL,
+ fbi->var.xres, fbi->var.yres,
+ out_pixel_fmt,
+ fbi->var.left_margin,
+ fbi->var.hsync_len,
+ fbi->var.right_margin,
+ fbi->var.upper_margin,
+ fbi->var.vsync_len,
+ fbi->var.lower_margin,
+ 0, sig_cfg) != 0) {
+ dev_err(fbi->device,
+ "mxcfb: Error initializing panel.\n");
+ return -EINVAL;
+ }
+
+ fbi->mode =
+ (struct fb_videomode *)fb_match_mode(&fbi->var,
+ &fbi->modelist);
+ ipu_disp_set_window_pos(mxc_fbi->ipu_ch, 0, 0);
+ }
+
+ retval = _setup_disp_channel2(fbi);
+ if (retval)
+ return retval;
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK) {
+ ipu_enable_channel(mxc_fbi->ipu_ch);
+ }
+
+ return retval;
+}
+
+static int _swap_channels(struct fb_info *fbi,
+ struct fb_info *fbi_to, bool both_on)
+{
+ int retval, tmp;
+ ipu_channel_t old_ch;
+ struct mxcfb_info *mxc_fbi_from = (struct mxcfb_info *)fbi->par;
+ struct mxcfb_info *mxc_fbi_to = (struct mxcfb_info *)fbi_to->par;
+
+ if (both_on) {
+ ipu_disable_channel(mxc_fbi_to->ipu_ch, true);
+ ipu_uninit_channel(mxc_fbi_to->ipu_ch);
+ }
+
+ /* switch the mxc fbi parameters */
+ old_ch = mxc_fbi_from->ipu_ch;
+ mxc_fbi_from->ipu_ch = mxc_fbi_to->ipu_ch;
+ mxc_fbi_to->ipu_ch = old_ch;
+ tmp = mxc_fbi_from->ipu_ch_irq;
+ mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq;
+ mxc_fbi_to->ipu_ch_irq = tmp;
+
+ _setup_disp_channel1(fbi);
+ retval = _setup_disp_channel2(fbi);
+ if (retval)
+ return retval;
+
+ /* switch between dp and dc, disable old idmac, enable new idmac */
+ retval = ipu_swap_channel(old_ch, mxc_fbi_from->ipu_ch);
+ ipu_uninit_channel(old_ch);
+
+ if (both_on) {
+ _setup_disp_channel1(fbi_to);
+ retval = _setup_disp_channel2(fbi_to);
+ if (retval)
+ return retval;
+ ipu_enable_channel(mxc_fbi_to->ipu_ch);
+ }
+
+ return retval;
+}
+
+static int swap_channels(struct fb_info *fbi)
+{
+ int i;
+ int swap_mode;
+ ipu_channel_t ch_to;
+ struct mxcfb_info *mxc_fbi_from = (struct mxcfb_info *)fbi->par;
+ struct fb_info *fbi_to = NULL;
+ struct mxcfb_info *mxc_fbi_to;
+
+ /* what's the target channel? */
+ if (mxc_fbi_from->ipu_ch == MEM_BG_SYNC)
+ ch_to = MEM_DC_SYNC;
+ else
+ ch_to = MEM_BG_SYNC;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ mxc_fbi_to =
+ (struct mxcfb_info *)mxcfb_info[i]->par;
+ if (mxc_fbi_to->ipu_ch == ch_to) {
+ fbi_to = mxcfb_info[i];
+ break;
+ }
+ }
+ if (fbi_to == NULL)
+ return -1;
+
+ if (mxc_fbi_from->blank == FB_BLANK_UNBLANK) {
+ if (mxc_fbi_to->blank == FB_BLANK_UNBLANK)
+ swap_mode = BOTH_ON;
+ else
+ swap_mode = SRC_ON;
+ } else {
+ if (mxc_fbi_to->blank == FB_BLANK_UNBLANK)
+ swap_mode = TGT_ON;
+ else
+ swap_mode = BOTH_OFF;
+ }
+
+ /* tvout di-1: for DC use UYVY, for DP use RGB */
+ if (mxc_fbi_from->ipu_di == 1 && ch_to == MEM_DC_SYNC) {
+ fbi->var.bits_per_pixel = 16;
+ fbi->var.nonstd = IPU_PIX_FMT_UYVY;
+ } else if (mxc_fbi_from->ipu_di == 1 && ch_to == MEM_BG_SYNC) {
+ fbi->var.nonstd = 0;
+ } else if (mxc_fbi_from->ipu_di == 0 && ch_to == MEM_DC_SYNC) {
+ fbi_to->var.nonstd = 0;
+ } else if (mxc_fbi_from->ipu_di == 0 && ch_to == MEM_BG_SYNC) {
+ fbi->var.bits_per_pixel = 16;
+ fbi->var.nonstd = IPU_PIX_FMT_UYVY;
+ }
+
+ switch (swap_mode) {
+ case BOTH_ON:
+ /* disable target->switch src->enable target */
+ _swap_channels(fbi, fbi_to, true);
+ break;
+ case SRC_ON:
+ /* just switch src */
+ _swap_channels(fbi, fbi_to, false);
+ break;
+ case TGT_ON:
+ /* just switch target */
+ _swap_channels(fbi_to, fbi, false);
+ break;
+ case BOTH_OFF:
+ /* switch directly, no more need to do */
+ mxc_fbi_to->ipu_ch = mxc_fbi_from->ipu_ch;
+ mxc_fbi_from->ipu_ch = ch_to;
+ i = mxc_fbi_from->ipu_ch_irq;
+ mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq;
+ mxc_fbi_to->ipu_ch_irq = i;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Check framebuffer variable parameters and adjust to valid values.
+ *
+ * @param var framebuffer variable parameters
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ u32 vtotal;
+ u32 htotal;
+
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8))
+ var->bits_per_pixel = default_bpp;
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ var->red.length = 3;
+ var->red.offset = 5;
+ var->red.msb_right = 0;
+
+ var->green.length = 3;
+ var->green.offset = 2;
+ var->green.msb_right = 0;
+
+ var->blue.length = 2;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ if (var->pixclock < 1000) {
+ htotal = var->xres + var->right_margin + var->hsync_len +
+ var->left_margin;
+ vtotal = var->yres + var->lower_margin + var->vsync_len +
+ var->upper_margin;
+ var->pixclock = (vtotal * htotal * 6UL) / 100UL;
+ var->pixclock = KHZ2PICOS(var->pixclock);
+ dev_dbg(info->device,
+ "pixclock set for 60Hz refresh = %u ps\n",
+ var->pixclock);
+ }
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+
+ return 0;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int mxcfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int trans, struct fb_info *fbi)
+{
+ unsigned int val;
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (fbi->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (fbi->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = fbi->pseudo_palette;
+
+ val = _chan_to_field(red, &fbi->var.red);
+ val |= _chan_to_field(green, &fbi->var.green);
+ val |= _chan_to_field(blue, &fbi->var.blue);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Function to handle custom ioctls for MXC framebuffer.
+ *
+ * @param inode inode struct
+ *
+ * @param file file struct
+ *
+ * @param cmd Ioctl command to handle
+ *
+ * @param arg User pointer to command arguments
+ *
+ * @param fbi framebuffer information pointer
+ */
+static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ int __user *argp = (void __user *)arg;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ switch (cmd) {
+ case MXCFB_SET_GBL_ALPHA:
+ {
+ struct mxcfb_gbl_alpha ga;
+
+ if (copy_from_user(&ga, (void *)arg, sizeof(ga))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (ipu_disp_set_global_alpha(mxc_fbi->ipu_ch,
+ (bool)ga.enable,
+ ga.alpha)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (ga.enable)
+ mxc_fbi->alpha_chan_en = false;
+
+ if (ga.enable)
+ dev_dbg(fbi->device,
+ "Set global alpha of %s to %d\n",
+ fbi->fix.id, ga.alpha);
+ break;
+ }
+ case MXCFB_SET_LOC_ALPHA:
+ {
+ struct mxcfb_loc_alpha la;
+ int i;
+ char *video_plane_idstr = "";
+
+ if (copy_from_user(&la, (void *)arg, sizeof(la))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (ipu_disp_set_global_alpha(mxc_fbi->ipu_ch,
+ !(bool)la.enable, 0)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ if (la.enable) {
+ mxc_fbi->alpha_chan_en = true;
+
+ if (mxc_fbi->ipu_ch == MEM_FG_SYNC)
+ video_plane_idstr = "DISP3 BG";
+ else if (mxc_fbi->ipu_ch == MEM_BG_SYNC)
+ video_plane_idstr = "DISP3 FG";
+
+ for (i = 0; i < num_registered_fb; i++) {
+ char *idstr = registered_fb[i]->fix.id;
+ if (strcmp(idstr, video_plane_idstr) == 0) {
+ ((struct mxcfb_info *)(registered_fb[i]->par))->alpha_chan_en = false;
+ break;
+ }
+ }
+ } else
+ mxc_fbi->alpha_chan_en = false;
+
+ mxcfb_set_par(fbi);
+
+ la.alpha_phy_addr0 = mxc_fbi->alpha_phy_addr0;
+ la.alpha_phy_addr1 = mxc_fbi->alpha_phy_addr1;
+ if (copy_to_user((void *)arg, &la, sizeof(la))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (la.enable)
+ dev_dbg(fbi->device,
+ "Enable DP local alpha for %s\n",
+ fbi->fix.id);
+ break;
+ }
+ case MXCFB_SET_LOC_ALP_BUF:
+ {
+ unsigned long base;
+ uint32_t ipu_alp_ch_irq;
+
+ if (!(((mxc_fbi->ipu_ch == MEM_FG_SYNC) ||
+ (mxc_fbi->ipu_ch == MEM_BG_SYNC)) &&
+ (mxc_fbi->alpha_chan_en))) {
+ dev_err(fbi->device,
+ "Should use background or overlay "
+ "framebuffer to set the alpha buffer "
+ "number\n");
+ return -EINVAL;
+ }
+
+ if (get_user(base, argp))
+ return -EFAULT;
+
+ if (base != mxc_fbi->alpha_phy_addr0 &&
+ base != mxc_fbi->alpha_phy_addr1) {
+ dev_err(fbi->device,
+ "Wrong alpha buffer physical address "
+ "%lu\n", base);
+ return -EINVAL;
+ }
+
+ if (mxc_fbi->ipu_ch == MEM_FG_SYNC)
+ ipu_alp_ch_irq = IPU_IRQ_FG_ALPHA_SYNC_EOF;
+ else
+ ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF;
+
+ down(&mxc_fbi->alpha_flip_sem);
+
+ mxc_fbi->cur_ipu_alpha_buf =
+ !mxc_fbi->cur_ipu_alpha_buf;
+ if (ipu_update_channel_buffer(mxc_fbi->ipu_ch,
+ IPU_ALPHA_IN_BUFFER,
+ mxc_fbi->
+ cur_ipu_alpha_buf,
+ base) == 0) {
+ ipu_select_buffer(mxc_fbi->ipu_ch,
+ IPU_ALPHA_IN_BUFFER,
+ mxc_fbi->cur_ipu_alpha_buf);
+ ipu_clear_irq(ipu_alp_ch_irq);
+ ipu_enable_irq(ipu_alp_ch_irq);
+ } else {
+ dev_err(fbi->device,
+ "Error updating %s SDC alpha buf %d "
+ "to address=0x%08lX\n",
+ fbi->fix.id,
+ mxc_fbi->cur_ipu_alpha_buf, base);
+ }
+ break;
+ }
+ case MXCFB_SET_CLR_KEY:
+ {
+ struct mxcfb_color_key key;
+ if (copy_from_user(&key, (void *)arg, sizeof(key))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = ipu_disp_set_color_key(mxc_fbi->ipu_ch,
+ key.enable,
+ key.color_key);
+ dev_dbg(fbi->device, "Set color key to 0x%08X\n",
+ key.color_key);
+ break;
+ }
+ case MXCFB_WAIT_FOR_VSYNC:
+ {
+ if (mxc_fbi->blank != FB_BLANK_UNBLANK)
+ break;
+
+ down(&mxc_fbi->flip_sem);
+ init_completion(&mxc_fbi->vsync_complete);
+
+ ipu_clear_irq(mxc_fbi->ipu_ch_irq);
+ ipu_enable_irq(mxc_fbi->ipu_ch_irq);
+ retval = wait_for_completion_interruptible_timeout(
+ &mxc_fbi->vsync_complete, 1 * HZ);
+ if (retval == 0) {
+ dev_err(fbi->device,
+ "MXCFB_WAIT_FOR_VSYNC: timeout %d\n",
+ retval);
+ retval = -ETIME;
+ } else if (retval > 0) {
+ retval = 0;
+ }
+ break;
+ }
+ case FBIO_ALLOC:
+ {
+ int size;
+ struct mxcfb_alloc_list *mem;
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (mem == NULL)
+ return -ENOMEM;
+
+ if (get_user(size, argp))
+ return -EFAULT;
+
+ mem->size = PAGE_ALIGN(size);
+
+ mem->cpu_addr = dma_alloc_coherent(fbi->device, size,
+ &mem->phy_addr,
+ GFP_DMA);
+ if (mem->cpu_addr == NULL) {
+ kfree(mem);
+ return -ENOMEM;
+ }
+
+ list_add(&mem->list, &fb_alloc_list);
+
+ dev_dbg(fbi->device, "allocated %d bytes @ 0x%08X\n",
+ mem->size, mem->phy_addr);
+
+ if (put_user(mem->phy_addr, argp))
+ return -EFAULT;
+
+ break;
+ }
+ case FBIO_FREE:
+ {
+ unsigned long offset;
+ struct mxcfb_alloc_list *mem;
+
+ if (get_user(offset, argp))
+ return -EFAULT;
+
+ retval = -EINVAL;
+ list_for_each_entry(mem, &fb_alloc_list, list) {
+ if (mem->phy_addr == offset) {
+ list_del(&mem->list);
+ dma_free_coherent(fbi->device,
+ mem->size,
+ mem->cpu_addr,
+ mem->phy_addr);
+ kfree(mem);
+ retval = 0;
+ break;
+ }
+ }
+
+ break;
+ }
+ case MXCFB_SET_OVERLAY_POS:
+ {
+ struct mxcfb_pos pos;
+ struct fb_info *bg_fbi = NULL;
+ struct mxcfb_info *bg_mxcfbi = NULL;
+ int i;
+
+ if (mxc_fbi->ipu_ch != MEM_FG_SYNC) {
+ dev_err(fbi->device, "Should use the overlay "
+ "framebuffer to set the position of "
+ "the overlay window\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ if (copy_from_user(&pos, (void *)arg, sizeof(pos))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ for (i = 0; i < num_registered_fb; i++) {
+ bg_mxcfbi =
+ ((struct mxcfb_info *)(registered_fb[i]->par));
+
+ if (bg_mxcfbi->ipu_ch == MEM_BG_SYNC) {
+ bg_fbi = registered_fb[i];
+ break;
+ }
+ }
+
+ if (bg_fbi == NULL) {
+ dev_err(fbi->device, "Cannot find the "
+ "background framebuffer\n");
+ retval = -ENOENT;
+ break;
+ }
+
+ if (fbi->var.xres + pos.x > bg_fbi->var.xres) {
+ if (bg_fbi->var.xres < fbi->var.xres)
+ pos.x = 0;
+ else
+ pos.x = bg_fbi->var.xres - fbi->var.xres;
+ }
+ if (fbi->var.yres + pos.y > bg_fbi->var.yres) {
+ if (bg_fbi->var.yres < fbi->var.yres)
+ pos.y = 0;
+ else
+ pos.y = bg_fbi->var.yres - fbi->var.yres;
+ }
+
+ retval = ipu_disp_set_window_pos(mxc_fbi->ipu_ch,
+ pos.x, pos.y);
+
+ if (copy_to_user((void *)arg, &pos, sizeof(pos))) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case MXCFB_GET_FB_IPU_CHAN:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->ipu_ch, argp))
+ return -EFAULT;
+ break;
+ }
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/*
+ * mxcfb_blank():
+ * Blank the display.
+ */
+static int mxcfb_blank(int blank, struct fb_info *info)
+{
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+
+ dev_dbg(info->device, "blank = %d\n", blank);
+
+ if (mxc_fbi->blank == blank)
+ return 0;
+
+ mxc_fbi->blank = blank;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ ipu_disable_channel(mxc_fbi->ipu_ch, true);
+ ipu_uninit_channel(mxc_fbi->ipu_ch);
+ break;
+ case FB_BLANK_UNBLANK:
+ mxcfb_set_par(info);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ *
+ * @param var Variable screen buffer information
+ * @param info Framebuffer information pointer
+ */
+static int
+mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+ u_int y_bottom;
+ unsigned long base;
+
+ if (var->xoffset > 0) {
+ dev_dbg(info->device, "x panning not supported\n");
+ return -EINVAL;
+ }
+
+ if ((info->var.xoffset == var->xoffset) &&
+ (info->var.yoffset == var->yoffset))
+ return 0; /* No change, do nothing */
+
+ y_bottom = var->yoffset;
+
+ if (!(var->vmode & FB_VMODE_YWRAP))
+ y_bottom += var->yres;
+
+ if (y_bottom > info->var.yres_virtual)
+ return -EINVAL;
+
+ base = (var->yoffset * var->xres_virtual + var->xoffset);
+ base *= (var->bits_per_pixel) / 8;
+ base += info->fix.smem_start;
+
+ dev_dbg(info->device, "Updating SDC BG buf %d address=0x%08lX\n",
+ mxc_fbi->cur_ipu_buf, base);
+
+ down(&mxc_fbi->flip_sem);
+ init_completion(&mxc_fbi->vsync_complete);
+
+ mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_buf;
+ if (ipu_update_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf, base) == 0) {
+ ipu_select_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf);
+ ipu_clear_irq(mxc_fbi->ipu_ch_irq);
+ ipu_enable_irq(mxc_fbi->ipu_ch_irq);
+ } else {
+ dev_err(info->device,
+ "Error updating SDC buf %d to address=0x%08lX\n",
+ mxc_fbi->cur_ipu_buf, base);
+ }
+
+ dev_dbg(info->device, "Update complete\n");
+
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+
+ if (var->vmode & FB_VMODE_YWRAP)
+ info->var.vmode |= FB_VMODE_YWRAP;
+ else
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+
+ return 0;
+}
+
+/*
+ * Function to handle custom mmap for MXC framebuffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @param vma Pointer to vm_area_struct
+ */
+static int mxcfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
+{
+ bool found = false;
+ u32 len;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ struct mxcfb_alloc_list *mem;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ if (offset < fbi->fix.smem_len) {
+ /* mapping framebuffer memory */
+ len = fbi->fix.smem_len - offset;
+ vma->vm_pgoff = (fbi->fix.smem_start + offset) >> PAGE_SHIFT;
+ } else if ((vma->vm_pgoff ==
+ (mxc_fbi->alpha_phy_addr0 >> PAGE_SHIFT)) ||
+ (vma->vm_pgoff ==
+ (mxc_fbi->alpha_phy_addr1 >> PAGE_SHIFT))) {
+ len = mxc_fbi->alpha_mem_len;
+ } else {
+ list_for_each_entry(mem, &fb_alloc_list, list) {
+ if (offset == mem->phy_addr) {
+ found = true;
+ len = mem->size;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+ }
+
+ len = PAGE_ALIGN(len);
+ if (vma->vm_end - vma->vm_start > len)
+ return -EINVAL;
+
+ /* make buffers bufferable */
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
+ dev_dbg(fbi->device, "mmap remap_pfn_range failed\n");
+ return -ENOBUFS;
+ }
+
+ return 0;
+}
+
+/*!
+ * This structure contains the pointers to the control functions that are
+ * invoked by the core framebuffer driver to perform operations like
+ * blitting, rectangle filling, copy regions and cursor definition.
+ */
+static struct fb_ops mxcfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_set_par = mxcfb_set_par,
+ .fb_check_var = mxcfb_check_var,
+ .fb_setcolreg = mxcfb_setcolreg,
+ .fb_pan_display = mxcfb_pan_display,
+ .fb_ioctl = mxcfb_ioctl,
+ .fb_mmap = mxcfb_mmap,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = mxcfb_blank,
+};
+
+static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ complete(&mxc_fbi->vsync_complete);
+ up(&mxc_fbi->flip_sem);
+ ipu_disable_irq(irq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_alpha_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ up(&mxc_fbi->alpha_flip_sem);
+ ipu_disable_irq(irq);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Suspends the framebuffer and blanks the screen. Power management support
+ */
+static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+ int saved_blank;
+#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
+ void *fbmem;
+#endif
+
+ acquire_console_sem();
+ fb_set_suspend(fbi, 1);
+ saved_blank = mxc_fbi->blank;
+ mxcfb_blank(FB_BLANK_POWERDOWN, fbi);
+ mxc_fbi->blank = saved_blank;
+ release_console_sem();
+
+ return 0;
+}
+
+/*
+ * Resumes the framebuffer and unblanks the screen. Power management support
+ */
+static int mxcfb_resume(struct platform_device *pdev)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+ int saved_blank;
+
+ acquire_console_sem();
+ saved_blank = mxc_fbi->blank;
+ mxc_fbi->blank = FB_BLANK_POWERDOWN;
+ mxcfb_blank(saved_blank, fbi);
+ fb_set_suspend(fbi, 0);
+ release_console_sem();
+
+ return 0;
+}
+
+/*
+ * Main framebuffer functions
+ */
+
+/*!
+ * Allocates the DRAM memory for the frame buffer. This buffer is remapped
+ * into a non-cached, non-buffered, memory region to allow palette and pixel
+ * writes to occur without flushing the cache. Once this area is remapped,
+ * all virtual memory access to the video memory should occur at the new region.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_map_video_memory(struct fb_info *fbi)
+{
+ if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length)
+ fbi->fix.smem_len = fbi->var.yres_virtual *
+ fbi->fix.line_length;
+
+ fbi->screen_base = dma_alloc_writecombine(fbi->device,
+ fbi->fix.smem_len,
+ (dma_addr_t *)&fbi->fix.smem_start,
+ GFP_DMA);
+ if (fbi->screen_base == 0) {
+ dev_err(fbi->device, "Unable to allocate framebuffer memory\n");
+ fbi->fix.smem_len = 0;
+ fbi->fix.smem_start = 0;
+ return -EBUSY;
+ }
+
+ dev_dbg(fbi->device, "allocated fb @ paddr=0x%08X, size=%d.\n",
+ (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len);
+
+ fbi->screen_size = fbi->fix.smem_len;
+
+ /* Clear the screen */
+ memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+ return 0;
+}
+
+/*!
+ * De-allocates the DRAM memory for the frame buffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_unmap_video_memory(struct fb_info *fbi)
+{
+ dma_free_writecombine(fbi->device, fbi->fix.smem_len,
+ fbi->screen_base, fbi->fix.smem_start);
+ fbi->screen_base = 0;
+ fbi->fix.smem_start = 0;
+ fbi->fix.smem_len = 0;
+ return 0;
+}
+
+/*!
+ * Initializes the framebuffer information pointer. After allocating
+ * sufficient memory for the framebuffer structure, the fields are
+ * filled with custom information passed in from the configurable
+ * structures. This includes information such as bits per pixel,
+ * color maps, screen width/height and RGBA offsets.
+ *
+ * @return Framebuffer structure initialized with our information
+ */
+static struct fb_info *mxcfb_init_fbinfo(struct device *dev, struct fb_ops *ops)
+{
+ struct fb_info *fbi;
+ struct mxcfb_info *mxcfbi;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ fbi = framebuffer_alloc(sizeof(struct mxcfb_info), dev);
+ if (!fbi)
+ return NULL;
+
+ mxcfbi = (struct mxcfb_info *)fbi->par;
+
+ fbi->var.activate = FB_ACTIVATE_NOW;
+
+ fbi->fbops = ops;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->pseudo_palette = mxcfbi->pseudo_palette;
+
+ /*
+ * Allocate colormap
+ */
+ fb_alloc_cmap(&fbi->cmap, 16, 0);
+
+ return fbi;
+}
+
+static ssize_t show_disp_chan(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par;
+
+ if (mxcfbi->ipu_ch == MEM_BG_SYNC)
+ return sprintf(buf, "2-layer-fb-bg\n");
+ else if (mxcfbi->ipu_ch == MEM_FG_SYNC)
+ return sprintf(buf, "2-layer-fb-fg\n");
+ else if (mxcfbi->ipu_ch == MEM_DC_SYNC)
+ return sprintf(buf, "1-layer-fb\n");
+ else
+ return sprintf(buf, "err: no display chan\n");
+}
+
+static ssize_t swap_disp_chan(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par;
+ struct mxcfb_info *fg_mxcfbi = NULL;
+
+ /* swap only happen between DP-BG and DC, while DP-FG disable */
+ if (((mxcfbi->ipu_ch == MEM_BG_SYNC) &&
+ (strstr(buf, "1-layer-fb") != NULL)) ||
+ ((mxcfbi->ipu_ch == MEM_DC_SYNC) &&
+ (strstr(buf, "2-layer-fb-bg") != NULL))) {
+ int i;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ fg_mxcfbi =
+ (struct mxcfb_info *)mxcfb_info[i]->par;
+ if (fg_mxcfbi->ipu_ch == MEM_FG_SYNC)
+ break;
+ else
+ fg_mxcfbi = NULL;
+ }
+ if (!fg_mxcfbi ||
+ fg_mxcfbi->blank == FB_BLANK_UNBLANK) {
+ dev_err(dev,
+ "Can not switch while fb2(fb-fg) is on.\n");
+ return count;
+ }
+
+ if (swap_channels(info) < 0)
+ dev_err(dev, "Swap display channel failed.\n");
+ }
+
+ return count;
+}
+DEVICE_ATTR(fsl_disp_property, 644, show_disp_chan, swap_disp_chan);
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxcfb_probe(struct platform_device *pdev)
+{
+ struct fb_info *fbi;
+ struct mxcfb_info *mxcfbi;
+ struct mxc_fb_platform_data *plat_data = pdev->dev.platform_data;
+ struct resource *res;
+ int ret = 0;
+
+ /*
+ * Initialize FB structures
+ */
+ fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);
+ if (!fbi) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+ mxcfbi = (struct mxcfb_info *)fbi->par;
+
+ if (!g_dp_in_use) {
+ mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF;
+ mxcfbi->ipu_ch = MEM_BG_SYNC;
+ mxcfbi->blank = FB_BLANK_UNBLANK;
+ } else {
+ mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF;
+ mxcfbi->ipu_ch = MEM_DC_SYNC;
+ mxcfbi->blank = FB_BLANK_POWERDOWN;
+ }
+
+ mxcfbi->ipu_di = pdev->id;
+
+ if (pdev->id == 0) {
+ ipu_disp_set_global_alpha(mxcfbi->ipu_ch, true, 0x80);
+ ipu_disp_set_color_key(mxcfbi->ipu_ch, false, 0);
+ strcpy(fbi->fix.id, "DISP3 BG");
+
+ if (!g_dp_in_use)
+ if (ipu_request_irq(IPU_IRQ_BG_ALPHA_SYNC_EOF,
+ mxcfb_alpha_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(&pdev->dev, "Error registering BG "
+ "alpha irq handler.\n");
+ ret = -EBUSY;
+ goto err1;
+ }
+ g_dp_in_use = true;
+ } else if (pdev->id == 1) {
+ strcpy(fbi->fix.id, "DISP3 BG - DI1");
+
+ if (!g_dp_in_use)
+ if (ipu_request_irq(IPU_IRQ_BG_ALPHA_SYNC_EOF,
+ mxcfb_alpha_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(&pdev->dev, "Error registering BG "
+ "alpha irq handler.\n");
+ ret = -EBUSY;
+ goto err1;
+ }
+ g_dp_in_use = true;
+ } else if (pdev->id == 2) { /* Overlay */
+ mxcfbi->ipu_ch_irq = IPU_IRQ_FG_SYNC_EOF;
+ mxcfbi->ipu_ch = MEM_FG_SYNC;
+ mxcfbi->ipu_di = -1;
+ mxcfbi->overlay = true;
+ mxcfbi->blank = FB_BLANK_POWERDOWN;
+
+ strcpy(fbi->fix.id, "DISP3 FG");
+
+ if (ipu_request_irq(IPU_IRQ_FG_ALPHA_SYNC_EOF,
+ mxcfb_alpha_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(&pdev->dev, "Error registering FG alpha irq "
+ "handler.\n");
+ ret = -EBUSY;
+ goto err1;
+ }
+ }
+
+ mxcfb_info[pdev->id] = fbi;
+
+ if (ipu_request_irq(mxcfbi->ipu_ch_irq, mxcfb_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(&pdev->dev, "Error registering BG irq handler.\n");
+ ret = -EBUSY;
+ goto err1;
+ }
+ ipu_disable_irq(mxcfbi->ipu_ch_irq);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res) {
+ fbi->fix.smem_len = res->end - res->start + 1;
+ fbi->fix.smem_start = res->start;
+ fbi->screen_base = ioremap(fbi->fix.smem_start, fbi->fix.smem_len);
+ }
+
+ /* Need dummy values until real panel is configured */
+ fbi->var.xres = 240;
+ fbi->var.yres = 320;
+
+ if (!fb_mode && plat_data && plat_data->mode_str)
+ fb_find_mode(&fbi->var, fbi, plat_data->mode_str, NULL, 0, NULL,
+ default_bpp);
+
+ if (fb_mode)
+ fb_find_mode(&fbi->var, fbi, fb_mode, NULL, 0, NULL,
+ default_bpp);
+
+ if (plat_data) {
+ mxcfbi->ipu_di_pix_fmt = plat_data->interface_pix_fmt;
+ if (!fb_mode && plat_data->mode)
+ fb_videomode_to_var(&fbi->var, plat_data->mode);
+ }
+
+ mxcfb_check_var(&fbi->var, fbi);
+
+ /* Default Y virtual size is 2x panel size */
+ fbi->var.yres_virtual = fbi->var.yres * 2;
+
+ mxcfb_set_fix(fbi);
+
+ /* alocate fb first */
+ if (!res)
+ if (mxcfb_map_video_memory(fbi) < 0)
+ return -ENOMEM;
+
+ ret = register_framebuffer(fbi);
+ if (ret < 0)
+ goto err2;
+
+ platform_set_drvdata(pdev, fbi);
+
+ ret = device_create_file(fbi->dev, &dev_attr_fsl_disp_property);
+ if (ret)
+ dev_err(&pdev->dev, "Error %d on creating file\n", ret);
+
+ return 0;
+
+err2:
+ ipu_free_irq(mxcfbi->ipu_ch_irq, fbi);
+err1:
+ fb_dealloc_cmap(&fbi->cmap);
+ framebuffer_release(fbi);
+err0:
+ return ret;
+}
+
+static int mxcfb_remove(struct platform_device *pdev)
+{
+ struct fb_info *fbi = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ if (!fbi)
+ return 0;
+
+ mxcfb_blank(FB_BLANK_POWERDOWN, fbi);
+ ipu_free_irq(mxc_fbi->ipu_ch_irq, fbi);
+ mxcfb_unmap_video_memory(fbi);
+
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+
+ unregister_framebuffer(fbi);
+ framebuffer_release(fbi);
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcfb_driver = {
+ .driver = {
+ .name = MXCFB_NAME,
+ },
+ .probe = mxcfb_probe,
+ .remove = mxcfb_remove,
+ .suspend = mxcfb_suspend,
+ .resume = mxcfb_resume,
+};
+
+/*
+ * Parse user specified options (`video=trident:')
+ * example:
+ * video=trident:800x600,bpp=16,noaccel
+ */
+int mxcfb_setup(char *options)
+{
+ char *opt;
+ if (!options || !*options)
+ return 0;
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+ if (!strncmp(opt, "bpp=", 4))
+ default_bpp = simple_strtoul(opt + 4, NULL, 0);
+ else
+ fb_mode = opt;
+ }
+ return 0;
+}
+
+/*!
+ * Main entry function for the framebuffer. The function registers the power
+ * management callback functions with the kernel and also registers the MXCFB
+ * callback functions with the core Linux framebuffer driver \b fbmem.c
+ *
+ * @return Error code indicating success or failure
+ */
+int __init mxcfb_init(void)
+{
+ int ret = 0;
+#ifndef MODULE
+ char *option = NULL;
+#endif
+
+#ifndef MODULE
+ if (fb_get_options("mxcfb", &option))
+ return -ENODEV;
+ mxcfb_setup(option);
+#endif
+
+ ret = platform_driver_register(&mxcfb_driver);
+ return ret;
+}
+
+void mxcfb_exit(void)
+{
+ platform_driver_unregister(&mxcfb_driver);
+}
+
+module_init(mxcfb_init);
+module_exit(mxcfb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxcfb.c b/drivers/video/mxc/mxcfb.c
new file mode 100644
index 000000000000..668f4017471f
--- /dev/null
+++ b/drivers/video/mxc/mxcfb.c
@@ -0,0 +1,1502 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <mach/hardware.h>
+
+/*
+ * Driver name
+ */
+#define MXCFB_NAME "mxc_sdc_fb"
+/*!
+ * Structure containing the MXC specific framebuffer information.
+ */
+struct mxcfb_info {
+ int blank;
+ ipu_channel_t ipu_ch;
+ uint32_t ipu_ch_irq;
+ uint32_t cur_ipu_buf;
+
+ u32 pseudo_palette[16];
+
+ struct semaphore flip_sem;
+ spinlock_t fb_lock;
+};
+
+struct mxcfb_data {
+ struct fb_info *fbi;
+ struct fb_info *fbi_ovl;
+ volatile int32_t vsync_flag;
+ wait_queue_head_t vsync_wq;
+ wait_queue_head_t suspend_wq;
+ bool suspended;
+ int backlight_level;
+};
+
+struct mxcfb_alloc_list {
+ struct list_head list;
+ dma_addr_t phy_addr;
+ void *cpu_addr;
+ u32 size;
+};
+
+static struct mxcfb_data mxcfb_drv_data;
+
+static char *fb_mode = NULL;
+static unsigned long default_bpp = 16;
+#ifdef CONFIG_FB_MXC_INTERNAL_MEM
+static struct clk *iram_clk;
+#endif
+LIST_HEAD(fb_alloc_list);
+
+static uint32_t bpp_to_pixfmt(int bpp)
+{
+ uint32_t pixfmt = 0;
+ switch (bpp) {
+ case 24:
+ pixfmt = IPU_PIX_FMT_BGR24;
+ break;
+ case 32:
+ pixfmt = IPU_PIX_FMT_BGR32;
+ break;
+ case 16:
+ pixfmt = IPU_PIX_FMT_RGB565;
+ break;
+ }
+ return pixfmt;
+}
+
+extern void gpio_lcd_active(void);
+extern void gpio_lcd_inactive(void);
+#ifdef CONFIG_FB_MXC_TVOUT
+#include <linux/video_encoder.h>
+/*
+ * FIXME: VGA mode is not defined by video_encoder.h
+ * while FS453 supports VGA output.
+ */
+#ifndef VIDEO_ENCODER_VGA
+#define VIDEO_ENCODER_VGA 32
+#endif
+
+#define MODE_PAL "TV-PAL"
+#define MODE_NTSC "TV-NTSC"
+#define MODE_VGA "TV-VGA"
+
+extern int fs453_ioctl(unsigned int cmd, void *arg);
+#endif
+static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id);
+static int mxcfb_blank(int blank, struct fb_info *info);
+static int mxcfb_map_video_memory(struct fb_info *fbi, bool use_internal_ram);
+static int mxcfb_unmap_video_memory(struct fb_info *fbi);
+
+/*
+ * Set fixed framebuffer parameters based on variable settings.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+
+ if (mxc_fbi->ipu_ch == MEM_SDC_FG)
+ strncpy(fix->id, "DISP3 FG", 8);
+ else
+ strncpy(fix->id, "DISP3 BG", 8);
+
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+
+ return 0;
+}
+
+/*
+ * Set framebuffer parameters and change the operating mode.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_par(struct fb_info *fbi)
+{
+ int retval;
+ bool use_iram = false;
+ u32 mem_len;
+ ipu_di_signal_cfg_t sig_cfg;
+ ipu_panel_t mode = IPU_PANEL_TFT;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false))) < 0) {
+ return retval;
+ }
+
+ ipu_disable_irq(mxc_fbi->ipu_ch_irq);
+ ipu_disable_channel(mxc_fbi->ipu_ch, true);
+ ipu_uninit_channel(mxc_fbi->ipu_ch);
+ ipu_clear_irq(mxc_fbi->ipu_ch_irq);
+ mxcfb_set_fix(fbi);
+
+ mem_len = fbi->var.yres_virtual * fbi->fix.line_length;
+ if (mem_len > fbi->fix.smem_len) {
+ if (fbi->fix.smem_start)
+ mxcfb_unmap_video_memory(fbi);
+
+#ifdef CONFIG_FB_MXC_INTERNAL_MEM
+ if (mxc_fbi->ipu_ch == MEM_SDC_BG) {
+ use_iram = true;
+ }
+#endif
+ if (mxcfb_map_video_memory(fbi, use_iram) < 0)
+ return -ENOMEM;
+ }
+
+ ipu_init_channel(mxc_fbi->ipu_ch, NULL);
+
+ /* Clear the screen */
+ memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+ if (mxc_fbi->ipu_ch == MEM_SDC_BG) {
+ memset(&sig_cfg, 0, sizeof(sig_cfg));
+ if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
+ sig_cfg.Hsync_pol = true;
+ if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
+ sig_cfg.Vsync_pol = true;
+ if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL))
+ sig_cfg.clk_pol = true;
+ if (fbi->var.sync & FB_SYNC_DATA_INVERT)
+ sig_cfg.data_pol = true;
+ if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT))
+ sig_cfg.enable_pol = true;
+ if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN)
+ sig_cfg.clkidle_en = true;
+ if (fbi->var.sync & FB_SYNC_SHARP_MODE)
+ mode = IPU_PANEL_SHARP_TFT;
+
+ dev_dbg(fbi->device, "pixclock = %ul Hz\n",
+ (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
+
+ if (ipu_sdc_init_panel(mode,
+ (PICOS2KHZ(fbi->var.pixclock)) * 1000UL,
+ fbi->var.xres, fbi->var.yres,
+ (fbi->var.sync & FB_SYNC_SWAP_RGB) ?
+ IPU_PIX_FMT_BGR666 : IPU_PIX_FMT_RGB666,
+ fbi->var.left_margin,
+ fbi->var.hsync_len,
+ fbi->var.right_margin,
+ fbi->var.upper_margin,
+ fbi->var.vsync_len,
+ fbi->var.lower_margin, sig_cfg) != 0) {
+ dev_err(fbi->device,
+ "mxcfb: Error initializing panel.\n");
+ return -EINVAL;
+ }
+
+ fbi->mode =
+ (struct fb_videomode *)fb_match_mode(&fbi->var,
+ &fbi->modelist);
+ }
+
+ ipu_disp_set_window_pos(mxc_fbi->ipu_ch, 0, 0);
+
+ mxc_fbi->cur_ipu_buf = 1;
+ sema_init(&mxc_fbi->flip_sem, 1);
+ fbi->var.xoffset = fbi->var.yoffset = 0;
+
+ retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ fbi->var.xres, fbi->var.yres,
+ fbi->var.xres_virtual,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start +
+ (fbi->fix.line_length * fbi->var.yres),
+ fbi->fix.smem_start,
+ 0, 0);
+ if (retval) {
+ dev_err(fbi->device,
+ "ipu_init_channel_buffer error %d\n", retval);
+ return retval;
+ }
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK) {
+ ipu_enable_channel(mxc_fbi->ipu_ch);
+ }
+
+ return 0;
+}
+
+/*
+ * Check framebuffer variable parameters and adjust to valid values.
+ *
+ * @param var framebuffer variable parameters
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ u32 vtotal;
+ u32 htotal;
+
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+#ifdef CONFIG_FB_MXC_INTERNAL_MEM
+ if ((info->fix.smem_start == FB_RAM_BASE_ADDR) &&
+ ((var->yres_virtual * var->xres_virtual * var->bits_per_pixel / 8) >
+ FB_RAM_SIZE)) {
+ return -EINVAL;
+ }
+#endif
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16)) {
+ var->bits_per_pixel = default_bpp;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ if (var->pixclock < 1000) {
+ htotal = var->xres + var->right_margin + var->hsync_len +
+ var->left_margin;
+ vtotal = var->yres + var->lower_margin + var->vsync_len +
+ var->upper_margin;
+ var->pixclock = (vtotal * htotal * 6UL) / 100UL;
+ var->pixclock = KHZ2PICOS(var->pixclock);
+ dev_dbg(info->device,
+ "pixclock set for 60Hz refresh = %u ps\n",
+ var->pixclock);
+ }
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+
+ /* nonstd used for YUV formats, but only RGB supported */
+ var->nonstd = 0;
+
+ return 0;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+static int
+mxcfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int trans, struct fb_info *fbi)
+{
+ unsigned int val;
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (fbi->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (fbi->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = fbi->pseudo_palette;
+
+ val = _chan_to_field(red, &fbi->var.red);
+ val |= _chan_to_field(green, &fbi->var.green);
+ val |= _chan_to_field(blue, &fbi->var.blue);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Function to handle custom ioctls for MXC framebuffer.
+ *
+ * @param inode inode struct
+ *
+ * @param file file struct
+ *
+ * @param cmd Ioctl command to handle
+ *
+ * @param arg User pointer to command arguments
+ *
+ * @param fbi framebuffer information pointer
+ */
+static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ int __user *argp = (void __user *)arg;
+
+ if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false))) < 0) {
+ return retval;
+ }
+
+ switch (cmd) {
+ case MXCFB_SET_GBL_ALPHA:
+ {
+ struct mxcfb_gbl_alpha ga;
+ if (copy_from_user(&ga, (void *)arg, sizeof(ga))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval =
+ ipu_sdc_set_global_alpha((bool) ga.enable,
+ ga.alpha);
+ dev_dbg(fbi->device, "Set global alpha to %d\n",
+ ga.alpha);
+ break;
+ }
+ case MXCFB_SET_CLR_KEY:
+ {
+ struct mxcfb_color_key key;
+ if (copy_from_user(&key, (void *)arg, sizeof(key))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = ipu_sdc_set_color_key(MEM_SDC_BG, key.enable,
+ key.color_key);
+ dev_dbg(fbi->device, "Set color key to 0x%08X\n",
+ key.color_key);
+ break;
+ }
+ case MXCFB_WAIT_FOR_VSYNC:
+ {
+#ifndef CONFIG_ARCH_MX3
+ mxcfb_drv_data.vsync_flag = 0;
+ ipu_enable_irq(IPU_IRQ_SDC_DISP3_VSYNC);
+ if (!wait_event_interruptible_timeout
+ (mxcfb_drv_data.vsync_wq,
+ mxcfb_drv_data.vsync_flag != 0, 1 * HZ)) {
+ dev_err(fbi->device,
+ "MXCFB_WAIT_FOR_VSYNC: timeout\n");
+ retval = -ETIME;
+ break;
+ } else if (signal_pending(current)) {
+ dev_err(fbi->device,
+ "MXCFB_WAIT_FOR_VSYNC: interrupt received\n");
+ retval = -ERESTARTSYS;
+ break;
+ }
+#endif
+ break;
+ }
+ case MXCFB_GET_FB_IPU_CHAN:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->ipu_ch, argp))
+ return -EFAULT;
+
+ break;
+ }
+#ifdef CONFIG_FB_MXC_TVOUT
+ case ENCODER_GET_CAPABILITIES:
+ {
+ struct video_encoder_capability cap;
+
+ if ((retval = fs453_ioctl(cmd, &cap)))
+ break;
+
+ if (copy_to_user((void *)arg, &cap, sizeof(cap)))
+ retval = -EFAULT;
+ break;
+ }
+ case ENCODER_SET_NORM:
+ {
+ unsigned long mode;
+ char *smode;
+ struct fb_var_screeninfo var;
+
+ if (copy_from_user(&mode, (void *)arg, sizeof(mode))) {
+ retval = -EFAULT;
+ break;
+ }
+ if ((retval = fs453_ioctl(cmd, &mode)))
+ break;
+
+ if (mode == VIDEO_ENCODER_PAL)
+ smode = MODE_PAL;
+ else if (mode == VIDEO_ENCODER_NTSC)
+ smode = MODE_NTSC;
+ else
+ smode = MODE_VGA;
+
+ var = fbi->var;
+ var.nonstd = 0;
+ retval = fb_find_mode(&var, fbi, smode, mxcfb_modedb,
+ mxcfb_modedb_sz, NULL,
+ default_bpp);
+ if ((retval != 1) && (retval != 2)) { /* specified mode not found */
+ retval = -ENODEV;
+ break;
+ }
+
+ fbi->var = var;
+ fb_mode = smode;
+ retval = mxcfb_set_par(fbi);
+ break;
+ }
+ case ENCODER_SET_INPUT:
+ case ENCODER_SET_OUTPUT:
+ case ENCODER_ENABLE_OUTPUT:
+ {
+ unsigned long varg;
+
+ if (copy_from_user(&varg, (void *)arg, sizeof(varg))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = fs453_ioctl(cmd, &varg);
+ break;
+ }
+#endif
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/*
+ * Function to handle custom ioctls for MXC framebuffer.
+ *
+ * @param inode inode struct
+ *
+ * @param file file struct
+ *
+ * @param cmd Ioctl command to handle
+ *
+ * @param arg User pointer to command arguments
+ *
+ * @param fbi framebuffer information pointer
+ */
+static int mxcfb_ioctl_ovl(struct fb_info *fbi, unsigned int cmd,
+ unsigned long arg)
+{
+ int retval = 0;
+ int __user *argp = (void __user *)arg;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false))) < 0) {
+ return retval;
+ }
+
+ switch (cmd) {
+ case FBIO_ALLOC:
+ {
+ int size;
+ struct mxcfb_alloc_list *mem;
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (mem == NULL)
+ return -ENOMEM;
+
+ if (get_user(size, argp))
+ return -EFAULT;
+
+ mem->size = PAGE_ALIGN(size);
+
+ mem->cpu_addr = dma_alloc_coherent(fbi->device, size,
+ &mem->phy_addr,
+ GFP_DMA);
+ if (mem->cpu_addr == NULL) {
+ kfree(mem);
+ return -ENOMEM;
+ }
+
+ list_add(&mem->list, &fb_alloc_list);
+
+ dev_dbg(fbi->device, "allocated %d bytes @ 0x%08X\n",
+ mem->size, mem->phy_addr);
+
+ if (put_user(mem->phy_addr, argp))
+ return -EFAULT;
+
+ break;
+ }
+ case FBIO_FREE:
+ {
+ unsigned long offset;
+ struct mxcfb_alloc_list *mem;
+
+ if (get_user(offset, argp))
+ return -EFAULT;
+
+ retval = -EINVAL;
+ list_for_each_entry(mem, &fb_alloc_list, list) {
+ if (mem->phy_addr == offset) {
+ list_del(&mem->list);
+ dma_free_coherent(fbi->device,
+ mem->size,
+ mem->cpu_addr,
+ mem->phy_addr);
+ kfree(mem);
+ retval = 0;
+ break;
+ }
+ }
+
+ break;
+ }
+ case MXCFB_SET_OVERLAY_POS:
+ {
+ struct mxcfb_pos pos;
+ if (copy_from_user(&pos, (void *)arg, sizeof(pos))) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = ipu_disp_set_window_pos(mxc_fbi->ipu_ch,
+ pos.x, pos.y);
+ break;
+ }
+ case MXCFB_GET_FB_IPU_CHAN:
+ {
+ struct mxcfb_info *mxc_fbi =
+ (struct mxcfb_info *)fbi->par;
+
+ if (put_user(mxc_fbi->ipu_ch, argp))
+ return -EFAULT;
+
+ break;
+ }
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/*
+ * mxcfb_blank():
+ * Blank the display.
+ */
+static int mxcfb_blank(int blank, struct fb_info *info)
+{
+ int retval;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+
+ dev_dbg(info->device, "blank = %d\n", blank);
+
+ if (mxc_fbi->blank == blank)
+ return 0;
+
+ if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false))) < 0) {
+ return retval;
+ }
+
+ mxc_fbi->blank = blank;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ ipu_disable_channel(MEM_SDC_BG, true);
+ gpio_lcd_inactive();
+#ifdef CONFIG_FB_MXC_TVOUT
+ if (fb_mode) {
+ int enable = 0;
+
+ if ((strcmp(fb_mode, MODE_VGA) == 0)
+ || (strcmp(fb_mode, MODE_NTSC) == 0)
+ || (strcmp(fb_mode, MODE_PAL) == 0))
+ fs453_ioctl(ENCODER_ENABLE_OUTPUT, &enable);
+ }
+#endif
+ break;
+ case FB_BLANK_UNBLANK:
+ gpio_lcd_active();
+ ipu_enable_channel(MEM_SDC_BG);
+#ifdef CONFIG_FB_MXC_TVOUT
+ if (fb_mode) {
+ unsigned long mode = 0;
+
+ if (strcmp(fb_mode, MODE_VGA) == 0)
+ mode = VIDEO_ENCODER_VGA;
+ else if (strcmp(fb_mode, MODE_NTSC) == 0)
+ mode = VIDEO_ENCODER_NTSC;
+ else if (strcmp(fb_mode, MODE_PAL) == 0)
+ mode = VIDEO_ENCODER_PAL;
+ if (mode)
+ fs453_ioctl(ENCODER_SET_NORM, &mode);
+ }
+#endif
+ break;
+ }
+ return 0;
+}
+
+/*
+ * mxcfb_blank_ovl():
+ * Blank the display.
+ */
+static int mxcfb_blank_ovl(int blank, struct fb_info *info)
+{
+ int retval;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+
+ dev_dbg(info->device, "ovl blank = %d\n", blank);
+
+ if (mxc_fbi->blank == blank)
+ return 0;
+
+ if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false))) < 0) {
+ return retval;
+ }
+
+ mxc_fbi->blank = blank;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ ipu_disable_channel(MEM_SDC_FG, true);
+ break;
+ case FB_BLANK_UNBLANK:
+ ipu_enable_channel(MEM_SDC_FG);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ *
+ * @param var Variable screen buffer information
+ * @param info Framebuffer information pointer
+ */
+static int
+mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+ unsigned long lock_flags = 0;
+ int retval;
+ u_int y_bottom;
+ unsigned long base;
+
+ if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false))) < 0) {
+ return retval;
+ }
+
+ if (var->xoffset > 0) {
+ dev_dbg(info->device, "x panning not supported\n");
+ return -EINVAL;
+ }
+
+ if ((info->var.xoffset == var->xoffset) &&
+ (info->var.yoffset == var->yoffset)) {
+ return 0; // No change, do nothing
+ }
+
+ y_bottom = var->yoffset;
+
+ if (!(var->vmode & FB_VMODE_YWRAP)) {
+ y_bottom += var->yres;
+ }
+
+ if (y_bottom > info->var.yres_virtual) {
+ return -EINVAL;
+ }
+
+ base = (var->yoffset * var->xres_virtual + var->xoffset);
+ base *= (var->bits_per_pixel) / 8;
+ base += info->fix.smem_start;
+
+ down(&mxc_fbi->flip_sem);
+
+ spin_lock_irqsave(&mxc_fbi->fb_lock, lock_flags);
+
+ dev_dbg(info->device, "Updating SDC BG buf %d address=0x%08lX\n",
+ mxc_fbi->cur_ipu_buf, base);
+
+ mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_buf;
+ if (ipu_update_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf, base) == 0) {
+ ipu_select_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf);
+ ipu_clear_irq(mxc_fbi->ipu_ch_irq);
+ ipu_enable_irq(mxc_fbi->ipu_ch_irq);
+ } else {
+ dev_err(info->device,
+ "Error updating SDC buf %d to address=0x%08lX\n",
+ mxc_fbi->cur_ipu_buf, base);
+ }
+
+ spin_unlock_irqrestore(&mxc_fbi->fb_lock, lock_flags);
+
+ dev_dbg(info->device, "Update complete\n");
+
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+
+ if (var->vmode & FB_VMODE_YWRAP) {
+ info->var.vmode |= FB_VMODE_YWRAP;
+ } else {
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+ }
+
+ return 0;
+}
+
+/*
+ * Function to handle custom mmap for MXC framebuffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @param vma Pointer to vm_area_struct
+ */
+static int mxcfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
+{
+ bool found = false;
+ u32 len;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ struct mxcfb_alloc_list *mem;
+
+ if (offset < fbi->fix.smem_len) {
+ /* mapping framebuffer memory */
+ len = fbi->fix.smem_len - offset;
+ vma->vm_pgoff = (fbi->fix.smem_start + offset) >> PAGE_SHIFT;
+ } else {
+ list_for_each_entry(mem, &fb_alloc_list, list) {
+ if (offset == mem->phy_addr) {
+ found = true;
+ len = mem->size;
+ break;
+ }
+ }
+ if (!found) {
+ return -EINVAL;
+ }
+ }
+
+ len = PAGE_ALIGN(len);
+ if (vma->vm_end - vma->vm_start > len) {
+ return -EINVAL;
+ }
+
+ /* make buffers write-thru cacheable */
+ vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) &
+ ~L_PTE_BUFFERABLE);
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
+ dev_dbg(fbi->device, "mmap remap_pfn_range failed\n");
+ return -ENOBUFS;
+
+ }
+
+ return 0;
+}
+
+/*!
+ * This structure contains the pointers to the control functions that are
+ * invoked by the core framebuffer driver to perform operations like
+ * blitting, rectangle filling, copy regions and cursor definition.
+ */
+static struct fb_ops mxcfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_set_par = mxcfb_set_par,
+ .fb_check_var = mxcfb_check_var,
+ .fb_setcolreg = mxcfb_setcolreg,
+ .fb_pan_display = mxcfb_pan_display,
+ .fb_ioctl = mxcfb_ioctl,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = mxcfb_blank,
+};
+
+static struct fb_ops mxcfb_ovl_ops = {
+ .owner = THIS_MODULE,
+ .fb_set_par = mxcfb_set_par,
+ .fb_check_var = mxcfb_check_var,
+ .fb_setcolreg = mxcfb_setcolreg,
+ .fb_pan_display = mxcfb_pan_display,
+ .fb_ioctl = mxcfb_ioctl_ovl,
+ .fb_mmap = mxcfb_mmap,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = mxcfb_blank_ovl,
+};
+
+static irqreturn_t mxcfb_vsync_irq_handler(int irq, void *dev_id)
+{
+ struct mxcfb_data *fb_data = dev_id;
+
+ ipu_disable_irq(irq);
+
+ fb_data->vsync_flag = 1;
+ wake_up_interruptible(&fb_data->vsync_wq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ up(&mxc_fbi->flip_sem);
+ ipu_disable_irq(irq);
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Power management hooks. Note that we won't be called from IRQ context,
+ * unlike the blank functions above, so we may sleep.
+ */
+
+/*
+ * Suspends the framebuffer and blanks the screen. Power management support
+ */
+static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mxcfb_data *drv_data = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)drv_data->fbi->par;
+ struct mxcfb_info *mxc_fbi_ovl =
+ (struct mxcfb_info *)drv_data->fbi_ovl->par;
+#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
+ void *fbmem;
+#endif
+
+ drv_data->suspended = true;
+
+ acquire_console_sem();
+ fb_set_suspend(drv_data->fbi, 1);
+ fb_set_suspend(drv_data->fbi_ovl, 1);
+ release_console_sem();
+
+ if (mxc_fbi_ovl->blank == FB_BLANK_UNBLANK) {
+ ipu_disable_channel(MEM_SDC_FG, true);
+ }
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK) {
+#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
+ if (drv_data->fbi->fix.smem_start != FB_RAM_BASE_ADDR) {
+ fbmem = ioremap(FB_RAM_BASE_ADDR, FB_RAM_SIZE);
+ memcpy(fbmem, drv_data->fbi->screen_base, FB_RAM_SIZE);
+ iounmap(fbmem);
+ mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_buf;
+ ipu_update_channel_buffer(MEM_SDC_BG, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf,
+ FB_RAM_BASE_ADDR);
+ ipu_select_buffer(MEM_SDC_BG, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf);
+ }
+ ipu_lowpwr_display_enable();
+#else
+ ipu_disable_channel(MEM_SDC_BG, true);
+ gpio_lcd_inactive();
+#endif
+#ifdef CONFIG_FB_MXC_TVOUT
+ if (fb_mode) {
+ int enable = 0;
+
+ if ((strcmp(fb_mode, MODE_VGA) == 0)
+ || (strcmp(fb_mode, MODE_NTSC) == 0)
+ || (strcmp(fb_mode, MODE_PAL) == 0))
+ fs453_ioctl(ENCODER_ENABLE_OUTPUT, &enable);
+ }
+#endif
+ }
+ return 0;
+}
+
+/*
+ * Resumes the framebuffer and unblanks the screen. Power management support
+ */
+static int mxcfb_resume(struct platform_device *pdev)
+{
+ struct mxcfb_data *drv_data = platform_get_drvdata(pdev);
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)drv_data->fbi->par;
+ struct mxcfb_info *mxc_fbi_ovl =
+ (struct mxcfb_info *)drv_data->fbi_ovl->par;
+
+ drv_data->suspended = false;
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK) {
+#ifdef CONFIG_FB_MXC_LOW_PWR_DISPLAY
+ ipu_lowpwr_display_disable();
+ if (drv_data->fbi->fix.smem_start != FB_RAM_BASE_ADDR) {
+ mxc_fbi->cur_ipu_buf = !mxc_fbi->cur_ipu_buf;
+ ipu_update_channel_buffer(MEM_SDC_BG, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf,
+ drv_data->fbi->fix.
+ smem_start);
+ ipu_select_buffer(MEM_SDC_BG, IPU_INPUT_BUFFER,
+ mxc_fbi->cur_ipu_buf);
+ }
+#else
+ gpio_lcd_active();
+ ipu_enable_channel(MEM_SDC_BG);
+#endif
+#ifdef CONFIG_FB_MXC_TVOUT
+ if (fb_mode) {
+ u32 mode = 0;
+
+ if (strcmp(fb_mode, MODE_VGA) == 0)
+ mode = VIDEO_ENCODER_VGA;
+ else if (strcmp(fb_mode, MODE_NTSC) == 0)
+ mode = VIDEO_ENCODER_NTSC;
+ else if (strcmp(fb_mode, MODE_PAL) == 0)
+ mode = VIDEO_ENCODER_PAL;
+ if (mode)
+ fs453_ioctl(ENCODER_SET_NORM, &mode);
+ }
+#endif
+ }
+
+ if (mxc_fbi_ovl->blank == FB_BLANK_UNBLANK) {
+ ipu_enable_channel(MEM_SDC_FG);
+ }
+
+ acquire_console_sem();
+ fb_set_suspend(drv_data->fbi, 0);
+ fb_set_suspend(drv_data->fbi_ovl, 0);
+ release_console_sem();
+
+ wake_up_interruptible(&drv_data->suspend_wq);
+ return 0;
+}
+#else
+#define mxcfb_suspend NULL
+#define mxcfb_resume NULL
+#endif
+
+/*
+ * Main framebuffer functions
+ */
+
+/*!
+ * Allocates the DRAM memory for the frame buffer. This buffer is remapped
+ * into a non-cached, non-buffered, memory region to allow palette and pixel
+ * writes to occur without flushing the cache. Once this area is remapped,
+ * all virtual memory access to the video memory should occur at the new region.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @param use_internal_ram flag on whether to use internal RAM for memory
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_map_video_memory(struct fb_info *fbi, bool use_internal_ram)
+{
+ int retval = 0;
+
+#ifdef CONFIG_FB_MXC_INTERNAL_MEM
+ if (use_internal_ram) {
+ fbi->fix.smem_len = FB_RAM_SIZE;
+ fbi->fix.smem_start = FB_RAM_BASE_ADDR;
+ if (fbi->fix.smem_len <
+ (fbi->var.yres_virtual * fbi->fix.line_length)) {
+ dev_err(fbi->device,
+ "Not enough internal RAM for framebuffer configuration\n");
+ retval = -EINVAL;
+ goto err0;
+ }
+
+ if (request_mem_region(fbi->fix.smem_start, fbi->fix.smem_len,
+ fbi->device->driver->name) == NULL) {
+ dev_err(fbi->device,
+ "Unable to request internal RAM\n");
+ retval = -ENOMEM;
+ goto err0;
+ }
+
+ if (!(fbi->screen_base = ioremap(fbi->fix.smem_start,
+ fbi->fix.smem_len))) {
+ dev_err(fbi->device,
+ "Unable to map fb memory to virtual address\n");
+ release_mem_region(fbi->fix.smem_start,
+ fbi->fix.smem_len);
+ retval = -EIO;
+ goto err0;
+ }
+
+ iram_clk = clk_get(NULL, "iram_clk");
+ clk_enable(iram_clk);
+ } else
+#endif
+ {
+ fbi->fix.smem_len = fbi->var.yres_virtual *
+ fbi->fix.line_length;
+ fbi->screen_base =
+ dma_alloc_writecombine(fbi->device,
+ fbi->fix.smem_len,
+ (dma_addr_t *) & fbi->fix.smem_start,
+ GFP_DMA);
+
+ if (fbi->screen_base == 0) {
+ dev_err(fbi->device,
+ "Unable to allocate framebuffer memory\n");
+ retval = -EBUSY;
+ goto err0;
+ }
+ }
+
+ dev_dbg(fbi->device, "allocated fb @ paddr=0x%08X, size=%d.\n",
+ (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len);
+
+ fbi->screen_size = fbi->fix.smem_len;
+
+ /* Clear the screen */
+ memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+ return 0;
+
+ err0:
+ fbi->fix.smem_len = 0;
+ fbi->fix.smem_start = 0;
+ fbi->screen_base = NULL;
+ return retval;
+}
+
+/*!
+ * De-allocates the DRAM memory for the frame buffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_unmap_video_memory(struct fb_info *fbi)
+{
+#ifdef CONFIG_FB_MXC_INTERNAL_MEM
+ if (fbi->fix.smem_start == FB_RAM_BASE_ADDR) {
+ iounmap(fbi->screen_base);
+ release_mem_region(fbi->fix.smem_start, fbi->fix.smem_len);
+ fbi->fix.smem_start = 0;
+ fbi->fix.smem_len = 0;
+ clk_disable(iram_clk);
+ } else
+#endif
+ {
+ dma_free_writecombine(fbi->device, fbi->fix.smem_len,
+ fbi->screen_base, fbi->fix.smem_start);
+ }
+ fbi->screen_base = 0;
+ fbi->fix.smem_start = 0;
+ fbi->fix.smem_len = 0;
+ return 0;
+}
+
+/*!
+ * Initializes the framebuffer information pointer. After allocating
+ * sufficient memory for the framebuffer structure, the fields are
+ * filled with custom information passed in from the configurable
+ * structures. This includes information such as bits per pixel,
+ * color maps, screen width/height and RGBA offsets.
+ *
+ * @return Framebuffer structure initialized with our information
+ */
+static struct fb_info *mxcfb_init_fbinfo(struct device *dev, struct fb_ops *ops)
+{
+ struct fb_info *fbi;
+ struct mxcfb_info *mxcfbi;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ fbi = framebuffer_alloc(sizeof(struct mxcfb_info), dev);
+ if (!fbi)
+ return NULL;
+
+ mxcfbi = (struct mxcfb_info *)fbi->par;
+
+ fbi->var.activate = FB_ACTIVATE_NOW;
+
+ fbi->fbops = ops;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->pseudo_palette = mxcfbi->pseudo_palette;
+
+ spin_lock_init(&mxcfbi->fb_lock);
+
+ /*
+ * Allocate colormap
+ */
+ fb_alloc_cmap(&fbi->cmap, 16, 0);
+
+ return fbi;
+}
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxcfb_probe(struct platform_device *pdev)
+{
+ char *mode = pdev->dev.platform_data;
+ struct fb_info *fbi;
+ struct mxcfb_info *mxcfbi;
+ struct fb_info *fbi_ovl;
+ int ret = 0;
+
+ /*
+ * Initialize FB structures
+ */
+ fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);
+ if (!fbi) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+ mxcfbi = (struct mxcfb_info *)fbi->par;
+
+ mxcfbi->ipu_ch_irq = IPU_IRQ_SDC_BG_EOF;
+ mxcfbi->cur_ipu_buf = 0;
+ mxcfbi->ipu_ch = MEM_SDC_BG;
+
+ ipu_sdc_set_global_alpha(true, 0xFF);
+ ipu_sdc_set_color_key(MEM_SDC_BG, false, 0);
+
+ if (ipu_request_irq(IPU_IRQ_SDC_BG_EOF, mxcfb_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(&pdev->dev, "Error registering BG irq handler.\n");
+ ret = -EBUSY;
+ goto err1;
+ }
+ ipu_disable_irq(IPU_IRQ_SDC_BG_EOF);
+
+ if (fb_mode == NULL) {
+ fb_mode = mode;
+ }
+
+ if (!fb_find_mode(&fbi->var, fbi, fb_mode, mxcfb_modedb,
+ mxcfb_modedb_sz, NULL, default_bpp)) {
+ ret = -EBUSY;
+ goto err2;
+ }
+ fb_videomode_to_modelist(mxcfb_modedb, mxcfb_modedb_sz, &fbi->modelist);
+
+ /* Default Y virtual size is 2x panel size */
+#ifndef CONFIG_FB_MXC_INTERNAL_MEM
+ fbi->var.yres_virtual = fbi->var.yres * 2;
+#endif
+
+ mxcfb_drv_data.fbi = fbi;
+ mxcfb_drv_data.backlight_level = 255;
+ mxcfb_drv_data.suspended = false;
+ init_waitqueue_head(&mxcfb_drv_data.suspend_wq);
+
+ mxcfbi->blank = FB_BLANK_NORMAL;
+ ret = mxcfb_set_par(fbi);
+ if (ret < 0) {
+ goto err2;
+ }
+ mxcfb_blank(FB_BLANK_UNBLANK, fbi);
+
+ /*
+ * Register framebuffer
+ */
+ ret = register_framebuffer(fbi);
+ if (ret < 0) {
+ goto err2;
+ }
+
+ /*
+ * Initialize Overlay FB structures
+ */
+ fbi_ovl = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ovl_ops);
+ if (!fbi_ovl) {
+ ret = -ENOMEM;
+ goto err3;
+ }
+ mxcfb_drv_data.fbi_ovl = fbi_ovl;
+ mxcfbi = (struct mxcfb_info *)fbi_ovl->par;
+
+ mxcfbi->ipu_ch_irq = IPU_IRQ_SDC_FG_EOF;
+ mxcfbi->cur_ipu_buf = 0;
+ mxcfbi->ipu_ch = MEM_SDC_FG;
+
+ if (ipu_request_irq(IPU_IRQ_SDC_FG_EOF, mxcfb_irq_handler, 0,
+ MXCFB_NAME, fbi_ovl) != 0) {
+ dev_err(fbi->device, "Error registering FG irq handler.\n");
+ ret = -EBUSY;
+ goto err4;
+ }
+ ipu_disable_irq(mxcfbi->ipu_ch_irq);
+
+ /* Default Y virtual size is 2x panel size */
+ fbi_ovl->var = fbi->var;
+ fbi_ovl->var.yres_virtual = fbi->var.yres * 2;
+
+ /* Overlay is blanked by default */
+ mxcfbi->blank = FB_BLANK_NORMAL;
+
+ ret = mxcfb_set_par(fbi_ovl);
+ if (ret < 0) {
+ goto err5;
+ }
+
+ /*
+ * Register overlay framebuffer
+ */
+ ret = register_framebuffer(fbi_ovl);
+ if (ret < 0) {
+ goto err5;
+ }
+
+ platform_set_drvdata(pdev, &mxcfb_drv_data);
+
+ init_waitqueue_head(&mxcfb_drv_data.vsync_wq);
+ if (!cpu_is_mx31() && !cpu_is_mx32()) {
+ if ((ret = ipu_request_irq(IPU_IRQ_SDC_DISP3_VSYNC,
+ mxcfb_vsync_irq_handler,
+ 0, MXCFB_NAME,
+ &mxcfb_drv_data)) < 0) {
+ goto err6;
+ }
+ ipu_disable_irq(IPU_IRQ_SDC_DISP3_VSYNC);
+ }
+
+ printk(KERN_INFO "mxcfb: fb registered, using mode %s\n", fb_mode);
+ return 0;
+
+ err6:
+ unregister_framebuffer(fbi_ovl);
+ err5:
+ ipu_free_irq(IPU_IRQ_SDC_FG_EOF, fbi_ovl);
+ err4:
+ fb_dealloc_cmap(&fbi_ovl->cmap);
+ framebuffer_release(fbi_ovl);
+ err3:
+ unregister_framebuffer(fbi);
+ err2:
+ ipu_free_irq(IPU_IRQ_SDC_BG_EOF, fbi);
+ err1:
+ fb_dealloc_cmap(&fbi->cmap);
+ framebuffer_release(fbi);
+ err0:
+ printk(KERN_ERR "mxcfb: failed to register fb\n");
+ return ret;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcfb_driver = {
+ .driver = {
+ .name = MXCFB_NAME,
+ },
+ .probe = mxcfb_probe,
+ .suspend = mxcfb_suspend,
+ .resume = mxcfb_resume,
+};
+
+/*
+ * Parse user specified options (`video=trident:')
+ * example:
+ * video=trident:800x600,bpp=16,noaccel
+ */
+int mxcfb_setup(char *options)
+{
+ char *opt;
+ if (!options || !*options)
+ return 0;
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+ if (!strncmp(opt, "bpp=", 4))
+ default_bpp = simple_strtoul(opt + 4, NULL, 0);
+ else
+ fb_mode = opt;
+ }
+ return 0;
+}
+
+/*!
+ * Main entry function for the framebuffer. The function registers the power
+ * management callback functions with the kernel and also registers the MXCFB
+ * callback functions with the core Linux framebuffer driver \b fbmem.c
+ *
+ * @return Error code indicating success or failure
+ */
+int __init mxcfb_init(void)
+{
+ int ret = 0;
+#ifndef MODULE
+ char *option = NULL;
+#endif
+
+#ifndef MODULE
+ if (fb_get_options("mxcfb", &option))
+ return -ENODEV;
+ mxcfb_setup(option);
+#endif
+
+ ret = platform_driver_register(&mxcfb_driver);
+ return ret;
+}
+
+void mxcfb_exit(void)
+{
+ struct fb_info *fbi = mxcfb_drv_data.fbi;
+
+ if (fbi) {
+ mxcfb_unmap_video_memory(fbi);
+
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+
+ unregister_framebuffer(fbi);
+ framebuffer_release(fbi);
+ }
+
+ fbi = mxcfb_drv_data.fbi_ovl;
+ if (fbi) {
+ mxcfb_unmap_video_memory(fbi);
+
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+
+ unregister_framebuffer(fbi);
+ framebuffer_release(fbi);
+ }
+#ifndef CONFIG_ARCH_MX3
+ ipu_free_irq(IPU_IRQ_SDC_DISP3_VSYNC, &mxcfb_drv_data);
+#endif
+
+ platform_driver_unregister(&mxcfb_driver);
+}
+
+module_init(mxcfb_init);
+module_exit(mxcfb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxcfb_ch7026.c b/drivers/video/mxc/mxcfb_ch7026.c
new file mode 100644
index 000000000000..c781e1c26086
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_ch7026.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb_epson_vga.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <linux/mxcfb.h>
+#include <linux/ipu.h>
+
+static struct i2c_client *ch7026_client;
+
+static int lcd_init(void);
+static void lcd_poweron(struct fb_info *info);
+static void lcd_poweroff(void);
+
+static void (*lcd_reset) (void);
+static struct regulator *io_reg;
+static struct regulator *core_reg;
+static struct regulator *analog_reg;
+
+ /* 8 800x600-60 VESA */
+static struct fb_videomode mode = {
+ NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
+};
+
+static void lcd_init_fb(struct fb_info *info)
+{
+ struct fb_var_screeninfo var;
+
+ memset(&var, 0, sizeof(var));
+
+ fb_videomode_to_var(&var, &mode);
+
+ var.activate = FB_ACTIVATE_ALL;
+
+ acquire_console_sem();
+ info->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(info, &var);
+ fb_blank(info, FB_BLANK_UNBLANK);
+ info->flags &= ~FBINFO_MISC_USEREVENT;
+ release_console_sem();
+}
+
+static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+
+ if (strcmp(event->info->fix.id, "DISP3 BG - DI1"))
+ return 0;
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ lcd_init_fb(event->info);
+ lcd_poweron(event->info);
+ break;
+ case FB_EVENT_BLANK:
+ if (*((int *)event->data) == FB_BLANK_UNBLANK)
+ lcd_poweron(event->info);
+ else
+ lcd_poweroff();
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = lcd_fb_event,
+};
+
+/*!
+ * This function is called whenever the SPI slave device is detected.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devinit lcd_probe(struct device *dev)
+{
+ int ret = 0;
+ int i;
+ struct mxc_lcd_platform_data *plat = dev->platform_data;
+
+ if (plat) {
+
+ io_reg = regulator_get(dev, plat->io_reg);
+ if (!IS_ERR(io_reg)) {
+ regulator_set_voltage(io_reg, 1800000, 1800000);
+ regulator_enable(io_reg);
+ } else {
+ io_reg = NULL;
+ }
+
+ core_reg = regulator_get(dev, plat->core_reg);
+ if (!IS_ERR(core_reg)) {
+ regulator_set_voltage(core_reg, 2500000, 2500000);
+ regulator_enable(core_reg);
+ } else {
+ core_reg = NULL;
+ }
+ analog_reg = regulator_get(dev, plat->analog_reg);
+ if (!IS_ERR(analog_reg)) {
+ regulator_set_voltage(analog_reg, 2775000, 2775000);
+ regulator_enable(analog_reg);
+ } else {
+ analog_reg = NULL;
+ }
+ msleep(100);
+
+ lcd_reset = plat->reset;
+ if (lcd_reset)
+ lcd_reset();
+ }
+
+ for (i = 0; i < num_registered_fb; i++) {
+ if (strcmp(registered_fb[i]->fix.id, "DISP3 BG - DI1") == 0) {
+ ret = lcd_init();
+ if (ret < 0)
+ goto err;
+
+ lcd_init_fb(registered_fb[i]);
+ fb_show_logo(registered_fb[i], 0);
+ lcd_poweron(registered_fb[i]);
+ }
+ }
+
+ fb_register_client(&nb);
+ return 0;
+err:
+ if (io_reg)
+ regulator_disable(io_reg);
+ if (core_reg)
+ regulator_disable(core_reg);
+ if (analog_reg)
+ regulator_disable(analog_reg);
+
+ return ret;
+}
+
+static int __devinit ch7026_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int id_reg;
+ ch7026_client = client;
+
+ return lcd_probe(&client->dev);
+}
+
+static int __devexit ch7026_remove(struct i2c_client *client)
+{
+ fb_unregister_client(&nb);
+ lcd_poweroff();
+ regulator_put(io_reg);
+ regulator_put(core_reg);
+ regulator_put(analog_reg);
+
+ return 0;
+}
+
+static int ch7026_suspend(struct i2c_client *client, pm_message_t message)
+{
+ return 0;
+}
+
+static int ch7026_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+u8 reg_init[][2] = {
+ { 0x02, 0x01 },
+ { 0x02, 0x03 },
+ { 0x03, 0x00 },
+ { 0x06, 0x6B },
+ { 0x08, 0x08 },
+ { 0x09, 0x80 },
+ { 0x0C, 0x0A },
+ { 0x0D, 0x89 },
+ { 0x0F, 0x23 },
+ { 0x10, 0x20 },
+ { 0x11, 0x20 },
+ { 0x12, 0x40 },
+ { 0x13, 0x28 },
+ { 0x14, 0x80 },
+ { 0x15, 0x52 },
+ { 0x16, 0x58 },
+ { 0x17, 0x74 },
+ { 0x19, 0x01 },
+ { 0x1A, 0x04 },
+ { 0x1B, 0x23 },
+ { 0x1C, 0x20 },
+ { 0x1D, 0x20 },
+ { 0x1F, 0x28 },
+ { 0x20, 0x80 },
+ { 0x21, 0x12 },
+ { 0x22, 0x58 },
+ { 0x23, 0x74 },
+ { 0x25, 0x01 },
+ { 0x26, 0x04 },
+ { 0x37, 0x20 },
+ { 0x39, 0x20 },
+ { 0x3B, 0x20 },
+ { 0x41, 0xA2 },
+ { 0x4D, 0x03 },
+ { 0x4E, 0x13 },
+ { 0x4F, 0xB1 },
+ { 0x50, 0x3B },
+ { 0x51, 0x54 },
+ { 0x52, 0x12 },
+ { 0x53, 0x13 },
+ { 0x55, 0xE5 },
+ { 0x5E, 0x80 },
+ { 0x69, 0x64 },
+ { 0x7D, 0x62 },
+ { 0x04, 0x00 },
+ { 0x06, 0x69 },
+
+ /*
+ NOTE: The following five repeated sentences are used here to wait memory initial complete, please don't remove...(you could refer to Appendix A of programming guide document (CH7025(26)B Programming Guide Rev2.03.pdf) for detailed information about memory initialization!
+ */
+ { 0x03, 0x00 },
+ { 0x03, 0x00 },
+ { 0x03, 0x00 },
+ { 0x03, 0x00 },
+ { 0x03, 0x00 },
+
+ { 0x06, 0x68 },
+ { 0x02, 0x02 },
+ { 0x02, 0x03 },
+};
+
+#define REGMAP_LENGTH (sizeof(reg_init) / (2*sizeof(u8)))
+
+/*
+ * Send init commands to L4F00242T03
+ *
+ */
+static int lcd_init(void)
+{
+ int i;
+ int dat;
+
+ dev_dbg(&ch7026_client->dev, "initializing CH7026\n");
+
+ /* read device ID */
+ msleep(100);
+ dat = i2c_smbus_read_byte_data(ch7026_client, 0x00);
+ dev_dbg(&ch7026_client->dev, "read id = 0x%02X\n", dat);
+ if (dat != 0x54)
+ return -ENODEV;
+
+ for (i = 0; i < REGMAP_LENGTH; ++i) {
+ if (i2c_smbus_write_byte_data
+ (ch7026_client, reg_init[i][0], reg_init[i][1]) < 0)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int lcd_on;
+/*
+ * Send Power On commands to L4F00242T03
+ *
+ */
+static void lcd_poweron(struct fb_info *info)
+{
+ u16 data[4];
+ u32 refresh;
+
+ if (lcd_on)
+ return;
+
+ dev_dbg(&ch7026_client->dev, "turning on LCD\n");
+
+ data[0] = PICOS2KHZ(info->var.pixclock) / 10;
+ data[2] = info->var.hsync_len + info->var.left_margin +
+ info->var.xres + info->var.right_margin;
+ data[3] = info->var.vsync_len + info->var.upper_margin +
+ info->var.yres + info->var.lower_margin;
+
+ refresh = data[2] * data[3];
+ refresh = (PICOS2KHZ(info->var.pixclock) * 1000) / refresh;
+ data[1] = refresh * 100;
+
+ lcd_on = 1;
+}
+
+/*
+ * Send Power Off commands to L4F00242T03
+ *
+ */
+static void lcd_poweroff(void)
+{
+ if (!lcd_on)
+ return;
+
+ dev_dbg(&ch7026_client->dev, "turning off LCD\n");
+
+ lcd_on = 0;
+}
+
+static const struct i2c_device_id ch7026_id[] = {
+ {"ch7026", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ch7026_id);
+
+static struct i2c_driver ch7026_driver = {
+ .driver = {
+ .name = "ch7026",
+ },
+ .probe = ch7026_probe,
+ .remove = ch7026_remove,
+ .suspend = ch7026_suspend,
+ .resume = ch7026_resume,
+ .id_table = ch7026_id,
+};
+
+static int __init ch7026_init(void)
+{
+ return i2c_add_driver(&ch7026_driver);
+}
+
+static void __exit ch7026_exit(void)
+{
+ i2c_del_driver(&ch7026_driver);
+}
+
+module_init(ch7026_init);
+module_exit(ch7026_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("CH7026 VGA driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxcfb_claa_wvga.c b/drivers/video/mxc/mxcfb_claa_wvga.c
new file mode 100644
index 000000000000..2e032317ab7d
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_claa_wvga.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb_claa_wvga.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mxcfb.h>
+#include <linux/regulator/consumer.h>
+
+static void lcd_poweron(void);
+static void lcd_poweroff(void);
+
+static struct platform_device *plcd_dev;
+static struct regulator *io_reg;
+static struct regulator *core_reg;
+static int lcd_on;
+
+static struct fb_videomode video_modes[] = {
+ {
+ /* 800x480 @ 55 Hz , pixel clk @ 25MHz */
+ "CLAA-WVGA", 55, 800, 480, 40000, 40, 40, 5, 5, 20, 10,
+ FB_SYNC_CLK_LAT_FALL,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+};
+
+static void lcd_init_fb(struct fb_info *info)
+{
+ struct fb_var_screeninfo var;
+
+ memset(&var, 0, sizeof(var));
+
+ fb_videomode_to_var(&var, &video_modes[0]);
+
+ var.activate = FB_ACTIVATE_ALL;
+ var.yres_virtual = var.yres * 2;
+
+ acquire_console_sem();
+ info->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(info, &var);
+ info->flags &= ~FBINFO_MISC_USEREVENT;
+ release_console_sem();
+}
+
+static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+
+ if (strcmp(event->info->fix.id, "DISP3 BG")) {
+ return 0;
+ }
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ lcd_init_fb(event->info);
+ lcd_poweron();
+ break;
+ case FB_EVENT_BLANK:
+ if ((event->info->var.xres != 800) ||
+ (event->info->var.yres != 480)) {
+ break;
+ }
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ lcd_poweron();
+ } else {
+ lcd_poweroff();
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = lcd_fb_event,
+};
+
+/*!
+ * This function is called whenever the SPI slave device is detected.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devinit lcd_probe(struct platform_device *pdev)
+{
+ int i;
+ struct mxc_lcd_platform_data *plat = pdev->dev.platform_data;
+
+ if (plat) {
+ if (plat->reset)
+ plat->reset();
+
+ io_reg = regulator_get(&pdev->dev, plat->io_reg);
+ if (IS_ERR(io_reg))
+ io_reg = NULL;
+ core_reg = regulator_get(&pdev->dev, plat->core_reg);
+ if (!IS_ERR(core_reg)) {
+ regulator_set_voltage(io_reg, 1800000, 1800000);
+ } else {
+ core_reg = NULL;
+ }
+ }
+
+ for (i = 0; i < num_registered_fb; i++) {
+ if (strcmp(registered_fb[i]->fix.id, "DISP3 BG") == 0) {
+ lcd_init_fb(registered_fb[i]);
+ fb_show_logo(registered_fb[i], 0);
+ lcd_poweron();
+ } else if (strcmp(registered_fb[i]->fix.id, "DISP3 FG") == 0) {
+ lcd_init_fb(registered_fb[i]);
+ }
+ }
+
+ fb_register_client(&nb);
+
+ plcd_dev = pdev;
+
+ return 0;
+}
+
+static int __devexit lcd_remove(struct platform_device *pdev)
+{
+ fb_unregister_client(&nb);
+ lcd_poweroff();
+ if (io_reg)
+ regulator_put(io_reg);
+ if (core_reg)
+ regulator_put(core_reg);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int lcd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+static int lcd_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define lcd_suspend NULL
+#define lcd_resume NULL
+#endif
+
+/*!
+ * platform driver structure for CLAA WVGA
+ */
+static struct platform_driver lcd_driver = {
+ .driver = {
+ .name = "lcd_claa"},
+ .probe = lcd_probe,
+ .remove = __devexit_p(lcd_remove),
+ .suspend = lcd_suspend,
+ .resume = lcd_resume,
+};
+
+/*
+ * Send Power On commands to L4F00242T03
+ *
+ */
+static void lcd_poweron(void)
+{
+ if (lcd_on)
+ return;
+
+ dev_dbg(&plcd_dev->dev, "turning on LCD\n");
+ if (core_reg)
+ regulator_enable(core_reg);
+ if (io_reg)
+ regulator_enable(io_reg);
+ lcd_on = 1;
+}
+
+/*
+ * Send Power Off commands to L4F00242T03
+ *
+ */
+static void lcd_poweroff(void)
+{
+ lcd_on = 0;
+ dev_dbg(&plcd_dev->dev, "turning off LCD\n");
+ if (io_reg)
+ regulator_disable(io_reg);
+ if (core_reg)
+ regulator_disable(core_reg);
+}
+
+static int __init claa_lcd_init(void)
+{
+ return platform_driver_register(&lcd_driver);
+}
+
+static void __exit claa_lcd_exit(void)
+{
+ platform_driver_unregister(&lcd_driver);
+}
+
+module_init(claa_lcd_init);
+module_exit(claa_lcd_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("CLAA WVGA LCD init driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxcfb_epson.c b/drivers/video/mxc/mxcfb_epson.c
new file mode 100644
index 000000000000..25b05e4b1a0e
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_epson.c
@@ -0,0 +1,1158 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxcfb_epson.c
+ *
+ * @brief MXC Frame buffer driver for ADC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <mach/ipu.h>
+#include <mach/mxcfb.h>
+
+#define PARTIAL_REFRESH
+#define MXCFB_REFRESH_DEFAULT MXCFB_REFRESH_PARTIAL
+/*
+ * Driver name
+ */
+#define MXCFB_NAME "MXCFB_EPSON"
+
+#define MXCFB_SCREEN_TOP_OFFSET 0
+#define MXCFB_SCREEN_LEFT_OFFSET 2
+#define MXCFB_SCREEN_WIDTH 176
+#define MXCFB_SCREEN_HEIGHT 220
+
+/*!
+ * Enum defining Epson panel commands.
+ */
+enum {
+ DISON = 0xAF,
+ DISOFF = 0xAE,
+ DISCTL = 0xCA,
+ SD_CSET = 0x15,
+ SD_PSET = 0x75,
+ DATCTL = 0xBC,
+ SLPIN = 0x95,
+ SLPOUT = 0x94,
+ DISNOR = 0xA6,
+ RAMWR = 0x5C,
+ VOLCTR = 0xC6,
+ GCP16 = 0xCC,
+ GCP64 = 0xCB,
+};
+
+struct mxcfb_info {
+ int open_count;
+ int blank;
+ uint32_t disp_num;
+
+ u32 pseudo_palette[16];
+
+ int32_t cur_update_mode;
+ dma_addr_t alloc_start_paddr;
+ void *alloc_start_vaddr;
+ u32 alloc_size;
+ uint32_t snoop_window_size;
+};
+
+struct mxcfb_data {
+ struct fb_info *fbi;
+ volatile int32_t vsync_flag;
+ wait_queue_head_t vsync_wq;
+ wait_queue_head_t suspend_wq;
+ bool suspended;
+};
+
+static struct mxcfb_data mxcfb_drv_data;
+static unsigned long default_bpp = 16;
+
+void slcd_gpio_config(void);
+extern void gpio_lcd_active(void);
+static int mxcfb_blank(int blank, struct fb_info *fbi);
+
+static uint32_t bpp_to_pixfmt(int bpp)
+{
+ uint32_t pixfmt = 0;
+ switch (bpp) {
+ case 24:
+ pixfmt = IPU_PIX_FMT_BGR24;
+ break;
+ case 32:
+ pixfmt = IPU_PIX_FMT_BGR32;
+ break;
+ case 16:
+ pixfmt = IPU_PIX_FMT_RGB565;
+ break;
+ }
+ return pixfmt;
+}
+
+/*!
+ * This function sets display region in the Epson panel
+ *
+ * @param disp display panel to config
+ * @param x1 x-coordinate of one vertex.
+ * @param x2 x-coordinate of second vertex.
+ * @param y1 y-coordinate of one vertex.
+ * @param y2 y-coordinate of second vertex.
+ */
+void set_panel_region(int disp, uint32_t x1, uint32_t x2,
+ uint32_t y1, uint32_t y2)
+{
+ uint32_t param[8];
+
+ memset(param, 0, sizeof(uint32_t) * 8);
+ param[0] = x1;
+ param[2] = x2;
+ param[4] = y1;
+ param[6] = y2;
+
+ // SD_CSET
+ ipu_adc_write_cmd(disp, CMD, SD_CSET, param, 4);
+ // SD_PSET
+
+ ipu_adc_write_cmd(disp, CMD, SD_PSET, &(param[4]), 4);
+}
+
+/*!
+ * Function to create and initiate template command buffer for ADC. This
+ * template will be written to Panel memory.
+ */
+static void init_channel_template(int disp)
+{
+ /* template command buffer for ADC is 32 */
+ uint32_t tempCmd[TEMPLATE_BUF_SIZE];
+ uint32_t i = 0;
+
+ memset(tempCmd, 0, sizeof(uint32_t) * TEMPLATE_BUF_SIZE);
+ /* setup update display region */
+ /* whole the screen during init */
+ /*WRITE Y COORDINATE CMND */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 0, SINGLE_STEP, SD_PSET);
+ /*WRITE Y START ADDRESS CMND LSB[22:8] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_YADDR, 1, SINGLE_STEP, 0x01);
+ /*WRITE Y START ADDRESS CMND MSB[22:16] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_YADDR, 1, SINGLE_STEP, 0x09);
+ /*WRITE Y STOP ADDRESS CMND LSB */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP,
+ MXCFB_SCREEN_HEIGHT - 1);
+ /*WRITE Y STOP ADDRESS CMND MSB */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP, 0);
+ /*WRITE X COORDINATE CMND */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 0, SINGLE_STEP, SD_CSET);
+ /*WRITE X ADDRESS CMND LSB[7:0] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_XADDR, 1, SINGLE_STEP, 0x01);
+ /*WRITE X ADDRESS CMND MSB[22:8] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP, 0);
+ /*WRITE X STOP ADDRESS CMND LSB */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP,
+ MXCFB_SCREEN_WIDTH + 1);
+ /*WRITE X STOP ADDRESS CMND MSB */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP, 0);
+ /*WRITE MEMORY CMND MSB */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 0, SINGLE_STEP, RAMWR);
+ /*WRITE DATA CMND and STP */
+ tempCmd[i++] = ipu_adc_template_gen(WR_DATA, 1, STOP, 0);
+
+ ipu_adc_write_template(disp, tempCmd, true);
+}
+
+/*!
+ * Function to initialize the panel. First it resets the panel and then
+ * initilizes panel.
+ */
+static void _init_panel(int disp)
+{
+ uint32_t cmd_param;
+ uint32_t i;
+
+ gpio_lcd_active();
+ slcd_gpio_config();
+
+ // DATCTL
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL_IFC_16_BIT
+ // 16-bit 565 mode
+ cmd_param = 0x28;
+#else
+ // 8-bit 666 mode
+ cmd_param = 0x08;
+#endif
+ ipu_adc_write_cmd(disp, CMD, DATCTL, &cmd_param, 1);
+
+ // Sleep OUT
+ ipu_adc_write_cmd(disp, CMD, SLPOUT, 0, 0);
+
+ // Set display to white
+ // Setup page and column addresses
+ set_panel_region(disp, MXCFB_SCREEN_LEFT_OFFSET,
+ MXCFB_SCREEN_WIDTH + MXCFB_SCREEN_LEFT_OFFSET - 1,
+ 0, MXCFB_SCREEN_HEIGHT - 1);
+ // Do RAM write cmd
+ ipu_adc_write_cmd(disp, CMD, RAMWR, 0, 0);
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL_IFC_16_BIT
+ for (i = 0; i < (MXCFB_SCREEN_WIDTH * MXCFB_SCREEN_HEIGHT); i++)
+#else
+ for (i = 0; i < (MXCFB_SCREEN_WIDTH * MXCFB_SCREEN_HEIGHT * 3); i++)
+#endif
+ ipu_adc_write_cmd(disp, DAT, 0xFFFF, 0, 0);
+
+ // Pause 80 ms
+ mdelay(80);
+
+ // Display ON
+ ipu_adc_write_cmd(disp, CMD, DISON, 0, 0);
+ // Pause 200 ms
+ mdelay(200);
+
+ pr_debug("initialized panel\n");
+}
+
+#ifdef PARTIAL_REFRESH
+static irqreturn_t mxcfb_sys2_eof_irq_handler(int irq, void *dev_id)
+{
+ ipu_channel_params_t params;
+ struct fb_info *fbi = dev_id;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+ uint32_t stat[2], seg_size;
+ uint32_t lsb, msb;
+ uint32_t update_height, start_line, start_addr, end_line, end_addr;
+ uint32_t stride_pixels = (fbi->fix.line_length * 8) /
+ fbi->var.bits_per_pixel;
+
+ ipu_adc_get_snooping_status(&stat[0], &stat[1]);
+ //DPRINTK("snoop status = 0x%08X%08X\n", stat[1], stat[0]);
+
+ if (!stat[0] && !stat[1]) {
+ dev_err(fbi->device, "error no bus snooping bits set\n");
+ return IRQ_HANDLED;
+ }
+ ipu_disable_irq(IPU_IRQ_ADC_SYS2_EOF);
+
+ lsb = ffs(stat[0]);
+ if (lsb) {
+ lsb--;
+ } else {
+ lsb = ffs(stat[1]);
+ lsb += 32 - 1;
+ }
+ msb = fls(stat[1]);
+ if (msb) {
+ msb += 32;
+ } else {
+ msb = fls(stat[0]);
+ }
+
+ seg_size = mxc_fbi->snoop_window_size / 64;
+
+ start_addr = lsb * seg_size; // starting address offset
+ start_line = start_addr / fbi->fix.line_length;
+ start_addr = start_line * fbi->fix.line_length; // Addr aligned to line
+ start_addr += fbi->fix.smem_start;
+
+ end_addr = msb * seg_size; // ending address offset
+ end_line = end_addr / fbi->fix.line_length;
+ end_line++;
+
+ if (end_line > fbi->var.yres) {
+ end_line = fbi->var.yres;
+ }
+
+ update_height = end_line - start_line;
+ dev_dbg(fbi->device, "updating rows %d to %d, start addr = 0x%08X\n",
+ start_line, end_line, start_addr);
+
+ ipu_uninit_channel(ADC_SYS1);
+ params.adc_sys1.disp = mxc_fbi->disp_num;
+ params.adc_sys1.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET;
+ params.adc_sys1.out_top = start_line;
+ ipu_init_channel(ADC_SYS1, &params);
+
+ ipu_init_channel_buffer(ADC_SYS1, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ MXCFB_SCREEN_WIDTH,
+ update_height,
+ stride_pixels,
+ IPU_ROTATE_NONE, (dma_addr_t) start_addr, 0,
+ 0, 0);
+ ipu_enable_channel(ADC_SYS1);
+ ipu_select_buffer(ADC_SYS1, IPU_INPUT_BUFFER, 0);
+ ipu_enable_irq(IPU_IRQ_ADC_SYS1_EOF);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_sys1_eof_irq_handler(int irq, void *dev_id)
+{
+ ipu_disable_irq(IPU_IRQ_ADC_SYS1_EOF);
+ ipu_disable_channel(ADC_SYS1, false);
+
+ ipu_enable_channel(ADC_SYS2);
+ ipu_enable_irq(IPU_IRQ_ADC_SYS2_EOF);
+
+ return IRQ_HANDLED;
+}
+#endif
+
+/*!
+ * Function to initialize Asynchronous Display Controller. It also initilizes
+ * the ADC System 1 channel. Configure ADC display 0 parallel interface for
+ * the panel.
+ *
+ * @param fbi framebuffer information pointer
+ */
+static void mxcfb_init_panel(struct fb_info *fbi)
+{
+ int msb;
+ int panel_stride;
+ ipu_channel_params_t params;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL_IFC_16_BIT
+ uint32_t pix_fmt = IPU_PIX_FMT_RGB565;
+ ipu_adc_sig_cfg_t sig = { 0, 0, 0, 0, 0, 0, 0, 0,
+ IPU_ADC_BURST_WCS,
+ IPU_ADC_IFC_MODE_SYS80_TYPE2,
+ 16, 0, 0, IPU_ADC_SER_NO_RW
+ };
+ mxc_fbi->disp_num = DISP0;
+#elif defined(CONFIG_FB_MXC_ASYNC_PANEL_IFC_8_BIT)
+ uint32_t pix_fmt = IPU_PIX_FMT_RGB666;
+ ipu_adc_sig_cfg_t sig = { 0, 0, 0, 0, 0, 0, 0, 0,
+ IPU_ADC_BURST_WCS,
+ IPU_ADC_IFC_MODE_SYS80_TYPE2,
+ 8, 0, 0, IPU_ADC_SER_NO_RW
+ };
+ mxc_fbi->disp_num = DISP0;
+#else
+ uint32_t pix_fmt = IPU_PIX_FMT_RGB565;
+ ipu_adc_sig_cfg_t sig = { 0, 1, 0, 0, 0, 0, 0, 0,
+ IPU_ADC_BURST_SERIAL,
+ IPU_ADC_IFC_MODE_5WIRE_SERIAL_CLK,
+ 16, 0, 0, IPU_ADC_SER_NO_RW
+ };
+ fbi->disp_num = DISP1;
+#endif
+
+#ifdef PARTIAL_REFRESH
+ if (ipu_request_irq(IPU_IRQ_ADC_SYS2_EOF, mxcfb_sys2_eof_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(fbi->device, "Error registering SYS2 irq handler.\n");
+ return;
+ }
+
+ if (ipu_request_irq(IPU_IRQ_ADC_SYS1_EOF, mxcfb_sys1_eof_irq_handler, 0,
+ MXCFB_NAME, fbi) != 0) {
+ dev_err(fbi->device, "Error registering SYS1 irq handler.\n");
+ return;
+ }
+ ipu_disable_irq(IPU_IRQ_ADC_SYS1_EOF);
+ ipu_disable_irq(IPU_IRQ_ADC_SYS2_EOF);
+#endif
+ // Init DI interface
+ msb = fls(MXCFB_SCREEN_WIDTH);
+ if (!(MXCFB_SCREEN_WIDTH & ((1UL << msb) - 1)))
+ msb--; // Already aligned to power 2
+ panel_stride = 1UL << msb;
+ ipu_adc_init_panel(mxc_fbi->disp_num,
+ MXCFB_SCREEN_WIDTH + MXCFB_SCREEN_LEFT_OFFSET,
+ MXCFB_SCREEN_HEIGHT,
+ pix_fmt, panel_stride, sig, XY, 0, VsyncInternal);
+
+ ipu_adc_init_ifc_timing(mxc_fbi->disp_num, true,
+ 190, 17, 104, 190, 5000000);
+ ipu_adc_init_ifc_timing(mxc_fbi->disp_num, false, 123, 17, 68, 0, 0);
+
+ // Needed to turn on ADC clock for panel init
+ memset(&params, 0, sizeof(params));
+ params.adc_sys1.disp = mxc_fbi->disp_num;
+ params.adc_sys1.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET;
+ params.adc_sys1.out_top = MXCFB_SCREEN_TOP_OFFSET;
+ ipu_init_channel(ADC_SYS1, &params);
+
+ _init_panel(mxc_fbi->disp_num);
+ init_channel_template(mxc_fbi->disp_num);
+}
+
+int mxcfb_set_refresh_mode(struct fb_info *fbi, int mode,
+ struct mxcfb_rect *update_region)
+{
+ unsigned long start_addr;
+ int ret_mode;
+ uint32_t dummy;
+ ipu_channel_params_t params;
+ struct mxcfb_rect rect;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+ uint32_t stride_pixels = (fbi->fix.line_length * 8) /
+ fbi->var.bits_per_pixel;
+ uint32_t memsize = fbi->fix.smem_len;
+
+ if (mxc_fbi->cur_update_mode == mode)
+ return mode;
+
+ ret_mode = mxc_fbi->cur_update_mode;
+
+ ipu_disable_irq(IPU_IRQ_ADC_SYS1_EOF);
+ ipu_adc_set_update_mode(ADC_SYS1, IPU_ADC_REFRESH_NONE, 0, 0, 0);
+#ifdef PARTIAL_REFRESH
+ ipu_disable_irq(IPU_IRQ_ADC_SYS2_EOF);
+ ipu_adc_set_update_mode(ADC_SYS2, IPU_ADC_REFRESH_NONE, 0, 0, 0);
+#endif
+
+ ipu_disable_channel(ADC_SYS1, true);
+ ipu_clear_irq(IPU_IRQ_ADC_SYS1_EOF);
+#ifdef PARTIAL_REFRESH
+ ipu_disable_channel(ADC_SYS2, true);
+ ipu_clear_irq(IPU_IRQ_ADC_SYS2_EOF);
+#endif
+ ipu_adc_get_snooping_status(&dummy, &dummy);
+
+ mxc_fbi->cur_update_mode = mode;
+
+ switch (mode) {
+ case MXCFB_REFRESH_OFF:
+ if (ipu_adc_set_update_mode(ADC_SYS1, IPU_ADC_REFRESH_NONE,
+ 0, 0, 0) < 0)
+ dev_err(fbi->device, "Error enabling auto refesh.\n");
+ if (ipu_adc_set_update_mode(ADC_SYS2, IPU_ADC_REFRESH_NONE,
+ 0, 0, 0) < 0)
+ dev_err(fbi->device, "Error enabling auto refesh.\n");
+#if 0
+ ipu_init_channel_buffer(ADC_SYS2, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ 1, 1, 4,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start,
+ fbi->fix.smem_start, 0, 0);
+ ipu_enable_channel(ADC_SYS2);
+ ipu_select_buffer(ADC_SYS2, IPU_INPUT_BUFFER, 0);
+ ipu_select_buffer(ADC_SYS2, IPU_INPUT_BUFFER, 1);
+ msleep(10);
+#endif
+ ipu_uninit_channel(ADC_SYS1);
+#ifdef PARTIAL_REFRESH
+ ipu_uninit_channel(ADC_SYS2);
+#endif
+ break;
+ case MXCFB_REFRESH_PARTIAL:
+#ifdef PARTIAL_REFRESH
+ ipu_adc_get_snooping_status(&dummy, &dummy);
+
+ params.adc_sys2.disp = DISP0;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys2.out_left = 0;
+ params.adc_sys2.out_top = 0;
+ ipu_init_channel(ADC_SYS2, &params);
+
+ if (ipu_adc_set_update_mode(ADC_SYS1, IPU_ADC_REFRESH_NONE,
+ 0, 0, 0) < 0) {
+ dev_err(fbi->device, "Error enabling auto refesh.\n");
+ }
+ if (ipu_adc_set_update_mode
+ (ADC_SYS2, IPU_ADC_AUTO_REFRESH_SNOOP, 30,
+ fbi->fix.smem_start, &memsize) < 0) {
+ dev_err(fbi->device, "Error enabling auto refesh.\n");
+ }
+ mxc_fbi->snoop_window_size = memsize;
+
+ ipu_init_channel_buffer(ADC_SYS2, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ 1, 1, 4,
+ IPU_ROTATE_NONE,
+ fbi->fix.smem_start, 0, 0, 0);
+
+ params.adc_sys1.disp = mxc_fbi->disp_num;
+ params.adc_sys1.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET;
+ params.adc_sys1.out_top = MXCFB_SCREEN_TOP_OFFSET;
+ ipu_init_channel(ADC_SYS1, &params);
+
+ ipu_init_channel_buffer(ADC_SYS1, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ MXCFB_SCREEN_WIDTH, MXCFB_SCREEN_HEIGHT,
+ stride_pixels, IPU_ROTATE_NONE,
+ fbi->fix.smem_start, 0, 0, 0);
+ ipu_enable_channel(ADC_SYS1);
+ ipu_select_buffer(ADC_SYS1, IPU_INPUT_BUFFER, 0);
+ ipu_enable_irq(IPU_IRQ_ADC_SYS1_EOF);
+ break;
+#endif
+ case MXCFB_REFRESH_AUTO:
+ if (update_region == NULL) {
+ update_region = &rect;
+ rect.top = 0;
+ rect.left = 0;
+ rect.height = MXCFB_SCREEN_HEIGHT;
+ rect.width = MXCFB_SCREEN_WIDTH;
+ }
+ params.adc_sys1.disp = mxc_fbi->disp_num;
+ params.adc_sys1.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET +
+ update_region->left;
+ params.adc_sys1.out_top = MXCFB_SCREEN_TOP_OFFSET +
+ update_region->top;
+ ipu_init_channel(ADC_SYS1, &params);
+
+ // Address aligned to line
+ start_addr = update_region->top * fbi->fix.line_length;
+ start_addr += fbi->fix.smem_start;
+ start_addr += update_region->left * fbi->var.bits_per_pixel / 8;
+
+ ipu_init_channel_buffer(ADC_SYS1, IPU_INPUT_BUFFER,
+ bpp_to_pixfmt(fbi->var.bits_per_pixel),
+ update_region->width,
+ update_region->height, stride_pixels,
+ IPU_ROTATE_NONE, start_addr, 0, 0, 0);
+ ipu_enable_channel(ADC_SYS1);
+ ipu_select_buffer(ADC_SYS1, IPU_INPUT_BUFFER, 0);
+
+ if (ipu_adc_set_update_mode
+ (ADC_SYS1, IPU_ADC_AUTO_REFRESH_SNOOP, 30,
+ fbi->fix.smem_start, &memsize) < 0)
+ dev_err(fbi->device, "Error enabling auto refesh.\n");
+
+ mxc_fbi->snoop_window_size = memsize;
+
+ break;
+ }
+ return ret_mode;
+}
+
+/*
+ * Open the main framebuffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @param user Set if opened by user or clear if opened by kernel
+ */
+static int mxcfb_open(struct fb_info *fbi, int user)
+{
+ int retval = 0;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false))) < 0) {
+ return retval;
+ }
+
+ mxc_fbi->open_count++;
+
+ retval = mxcfb_blank(FB_BLANK_UNBLANK, fbi);
+ return retval;
+}
+
+/*
+ * Close the main framebuffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @param user Set if opened by user or clear if opened by kernel
+ */
+static int mxcfb_release(struct fb_info *fbi, int user)
+{
+ int retval = 0;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false))) < 0) {
+ return retval;
+ }
+
+ --mxc_fbi->open_count;
+ if (mxc_fbi->open_count == 0) {
+ retval = mxcfb_blank(FB_BLANK_POWERDOWN, fbi);
+ }
+ return retval;
+}
+
+/*
+ * Set fixed framebuffer parameters based on variable settings.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_fix(struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct fb_var_screeninfo *var = &info->var;
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par;
+
+ // Set framebuffer id to IPU display number.
+ strcpy(fix->id, "DISP0 FB");
+ fix->id[4] = '0' + mxc_fbi->disp_num;
+
+ // Init settings based on the panel size
+ fix->line_length = MXCFB_SCREEN_WIDTH * var->bits_per_pixel / 8;
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->accel = FB_ACCEL_NONE;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ fix->xpanstep = 0;
+ fix->ypanstep = 0;
+
+ return 0;
+}
+
+/*
+ * Set framebuffer parameters and change the operating mode.
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_set_par(struct fb_info *fbi)
+{
+ int retval = 0;
+ int mode;
+
+ if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false))) < 0) {
+ return retval;
+ }
+
+ mode = mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, NULL);
+
+ mxcfb_set_fix(fbi);
+
+ if (mode != MXCFB_REFRESH_OFF) {
+#ifdef PARTIAL_REFRESH
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_PARTIAL, NULL);
+#else
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_AUTO, NULL);
+#endif
+ }
+ return 0;
+}
+
+/*
+ * Check framebuffer variable parameters and adjust to valid values.
+ *
+ * @param var framebuffer variable parameters
+ *
+ * @param info framebuffer information pointer
+ */
+static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
+{
+ if (var->xres > MXCFB_SCREEN_WIDTH)
+ var->xres = MXCFB_SCREEN_WIDTH;
+ if (var->yres > MXCFB_SCREEN_HEIGHT)
+ var->yres = MXCFB_SCREEN_HEIGHT;
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
+ (var->bits_per_pixel != 16)) {
+ var->bits_per_pixel = default_bpp;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ var->red.length = 5;
+ var->red.offset = 11;
+ var->red.msb_right = 0;
+
+ var->green.length = 6;
+ var->green.offset = 5;
+ var->green.msb_right = 0;
+
+ var->blue.length = 5;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 24:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ var->transp.msb_right = 0;
+ break;
+ case 32:
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->red.msb_right = 0;
+
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->green.msb_right = 0;
+
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ var->transp.msb_right = 0;
+ break;
+ }
+
+ var->height = -1;
+ var->width = -1;
+ var->grayscale = 0;
+ var->nonstd = 0;
+
+ var->pixclock = -1;
+ var->left_margin = -1;
+ var->right_margin = -1;
+ var->upper_margin = -1;
+ var->lower_margin = -1;
+ var->hsync_len = -1;
+ var->vsync_len = -1;
+
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->sync = 0;
+
+ return 0;
+}
+
+static inline u_int _chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int
+mxcfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int trans, struct fb_info *fbi)
+{
+ unsigned int val;
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (fbi->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green +
+ 7471 * blue) >> 16;
+ switch (fbi->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < 16) {
+ u32 *pal = fbi->pseudo_palette;
+
+ val = _chan_to_field(red, &fbi->var.red);
+ val |= _chan_to_field(green, &fbi->var.green);
+ val |= _chan_to_field(blue, &fbi->var.blue);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+
+ case FB_VISUAL_STATIC_PSEUDOCOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * mxcfb_blank():
+ * Blank the display.
+ */
+static int mxcfb_blank(int blank, struct fb_info *fbi)
+{
+ int retval = 0;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ dev_dbg(fbi->device, "blank = %d\n", blank);
+
+ if ((retval = wait_event_interruptible(mxcfb_drv_data.suspend_wq,
+ (mxcfb_drv_data.suspended ==
+ false))) < 0) {
+ return retval;
+ }
+
+ mxc_fbi->blank = blank;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, NULL);
+ break;
+ case FB_BLANK_UNBLANK:
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_DEFAULT, NULL);
+ break;
+ }
+ return 0;
+}
+
+/*!
+ * This structure contains the pointers to the control functions that are
+ * invoked by the core framebuffer driver to perform operations like
+ * blitting, rectangle filling, copy regions and cursor definition.
+ */
+static struct fb_ops mxcfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = mxcfb_open,
+ .fb_release = mxcfb_release,
+ .fb_set_par = mxcfb_set_par,
+ .fb_check_var = mxcfb_check_var,
+ .fb_setcolreg = mxcfb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_blank = mxcfb_blank,
+};
+
+/*!
+ * Allocates the DRAM memory for the frame buffer. This buffer is remapped
+ * into a non-cached, non-buffered, memory region to allow palette and pixel
+ * writes to occur without flushing the cache. Once this area is remapped,
+ * all virtual memory access to the video memory should occur at the new region.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_map_video_memory(struct fb_info *fbi)
+{
+ u32 msb;
+ u32 offset;
+ struct mxcfb_info *mxcfbi = fbi->par;
+
+ fbi->fix.smem_len = fbi->var.xres_virtual * fbi->var.yres_virtual * 4;
+
+ // Set size to power of 2.
+ msb = fls(fbi->fix.smem_len);
+ if (!(fbi->fix.smem_len & ((1UL << msb) - 1)))
+ msb--; // Already aligned to power 2
+ if (msb < 11)
+ msb = 11;
+ mxcfbi->alloc_size = (1UL << msb) * 2;
+
+ mxcfbi->alloc_start_vaddr = dma_alloc_coherent(fbi->device,
+ mxcfbi->alloc_size,
+ &mxcfbi->
+ alloc_start_paddr,
+ GFP_KERNEL | GFP_DMA);
+
+ if (mxcfbi->alloc_start_vaddr == 0) {
+ dev_err(fbi->device, "Unable to allocate framebuffer memory\n");
+ return -ENOMEM;
+ }
+ dev_dbg(fbi->device, "allocated fb memory @ paddr=0x%08X, size=%d.\n",
+ (uint32_t) mxcfbi->alloc_start_paddr, mxcfbi->alloc_size);
+
+ offset =
+ ((mxcfbi->alloc_size / 2) - 1) & ~((mxcfbi->alloc_size / 2) - 1);
+ fbi->fix.smem_start = mxcfbi->alloc_start_paddr + offset;
+ dev_dbg(fbi->device, "aligned fb start @ paddr=0x%08lX, size=%u.\n",
+ fbi->fix.smem_start, fbi->fix.smem_len);
+
+ fbi->screen_base = mxcfbi->alloc_start_vaddr + offset;
+
+ /* Clear the screen */
+ memset(fbi->screen_base, 0, fbi->fix.smem_len);
+ return 0;
+}
+
+/*!
+ * De-allocates the DRAM memory for the frame buffer.
+ *
+ * @param fbi framebuffer information pointer
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_unmap_video_memory(struct fb_info *fbi)
+{
+ struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+
+ dma_free_coherent(fbi->device, mxc_fbi->alloc_size,
+ mxc_fbi->alloc_start_vaddr,
+ mxc_fbi->alloc_start_paddr);
+ return 0;
+}
+
+/*!
+ * Initializes the framebuffer information pointer. After allocating
+ * sufficient memory for the framebuffer structure, the fields are
+ * filled with custom information passed in from the configurable
+ * structures. This includes information such as bits per pixel,
+ * color maps, screen width/height and RGBA offsets.
+ *
+ * @return Framebuffer structure initialized with our information
+ */
+static struct fb_info *mxcfb_init_fbinfo(struct device *dev, struct fb_ops *ops)
+{
+ struct fb_info *fbi;
+ struct mxcfb_info *mxcfbi;
+
+ /*
+ * Allocate sufficient memory for the fb structure
+ */
+ fbi = framebuffer_alloc(sizeof(struct mxcfb_info), dev);
+ if (!fbi)
+ return NULL;
+
+ mxcfbi = (struct mxcfb_info *)fbi->par;
+
+ /*
+ * Fill in fb_info structure information
+ */
+ fbi->var.xres = fbi->var.xres_virtual = MXCFB_SCREEN_WIDTH;
+ fbi->var.yres = fbi->var.yres_virtual = MXCFB_SCREEN_HEIGHT;
+ fbi->var.activate = FB_ACTIVATE_NOW;
+ mxcfb_check_var(&fbi->var, fbi);
+
+ mxcfb_set_fix(fbi);
+
+ fbi->fbops = ops;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->pseudo_palette = mxcfbi->pseudo_palette;
+
+ /*
+ * Allocate colormap
+ */
+ fb_alloc_cmap(&fbi->cmap, 16, 0);
+
+ return fbi;
+}
+
+/*!
+ * Probe routine for the framebuffer driver. It is called during the
+ * driver binding process. The following functions are performed in
+ * this routine: Framebuffer initialization, Memory allocation and
+ * mapping, Framebuffer registration, IPU initialization.
+ *
+ * @return Appropriate error code to the kernel common code
+ */
+static int mxcfb_probe(struct platform_device *pdev)
+{
+ struct fb_info *fbi;
+ struct mxcfb_info *mxc_fbi;
+ int ret;
+
+ platform_set_drvdata(pdev, &mxcfb_drv_data);
+
+ /*
+ * Initialize FB structures
+ */
+ fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops);
+ if (!fbi) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+ mxcfb_drv_data.fbi = fbi;
+ mxc_fbi = fbi->par;
+
+ mxcfb_drv_data.suspended = false;
+ init_waitqueue_head(&mxcfb_drv_data.suspend_wq);
+
+ /*
+ * Allocate memory
+ */
+ ret = mxcfb_map_video_memory(fbi);
+ if (ret < 0) {
+ goto err1;
+ }
+
+ mxcfb_init_panel(fbi);
+
+ /*
+ * Register framebuffer
+ */
+ ret = register_framebuffer(fbi);
+ if (ret < 0) {
+ goto err2;
+ }
+
+ dev_info(&pdev->dev, "%s registered\n", MXCFB_NAME);
+
+ return 0;
+
+ err2:
+ mxcfb_unmap_video_memory(fbi);
+ err1:
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+ framebuffer_release(fbi);
+ err0:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * Power management hooks. Note that we won't be called from IRQ context,
+ * unlike the blank functions above, so we may sleep.
+ */
+
+/*!
+ * Suspends the framebuffer and blanks the screen. Power management support
+ *
+ * @param pdev pointer to device structure.
+ * @param state state of the device.
+ *
+ * @return success
+ */
+static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mxcfb_data *drv_data = platform_get_drvdata(pdev);
+ struct fb_info *fbi = drv_data->fbi;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ drv_data->suspended = true;
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK)
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, NULL);
+ /* Display OFF */
+ ipu_adc_write_cmd(mxc_fbi->disp_num, CMD, DISOFF, 0, 0);
+
+ return 0;
+}
+
+/*!
+ * Resumes the framebuffer and unblanks the screen. Power management support
+ *
+ * @param pdev pointer to device structure.
+ *
+ * @return success
+ */
+static int mxcfb_resume(struct platform_device *pdev)
+{
+ struct mxcfb_data *drv_data = platform_get_drvdata(pdev);
+ struct fb_info *fbi = drv_data->fbi;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+
+ // Display ON
+ ipu_adc_write_cmd(mxc_fbi->disp_num, CMD, DISON, 0, 0);
+ drv_data->suspended = false;
+
+ if (mxc_fbi->blank == FB_BLANK_UNBLANK)
+ mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_DEFAULT, NULL);
+ wake_up_interruptible(&drv_data->suspend_wq);
+
+ return 0;
+}
+#else
+#define mxcfb_suspend NULL
+#define mxcfb_resume NULL
+#endif
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxcfb_driver = {
+ .driver = {
+ .name = MXCFB_NAME,
+ },
+ .probe = mxcfb_probe,
+ .suspend = mxcfb_suspend,
+ .resume = mxcfb_resume,
+};
+
+/*!
+ * Device definition for the Framebuffer
+ */
+static struct platform_device mxcfb_device = {
+ .name = MXCFB_NAME,
+ .id = 0,
+ .dev = {
+ .coherent_dma_mask = 0xFFFFFFFF,
+ }
+};
+
+/*!
+ * Main entry function for the framebuffer. The function registers the power
+ * management callback functions with the kernel and also registers the MXCFB
+ * callback functions with the core Linux framebuffer driver \b fbmem.c
+ *
+ * @return Error code indicating success or failure
+ */
+static int mxcfb_init(void)
+{
+ int ret = 0;
+
+ ret = platform_driver_register(&mxcfb_driver);
+ if (ret == 0) {
+ ret = platform_device_register(&mxcfb_device);
+ if (ret != 0) {
+ platform_driver_unregister(&mxcfb_driver);
+ }
+ }
+ return ret;
+}
+
+static void mxcfb_exit(void)
+{
+ struct fb_info *fbi = dev_get_drvdata(&mxcfb_device.dev);
+
+ if (fbi) {
+ mxcfb_unmap_video_memory(fbi);
+
+ if (&fbi->cmap)
+ fb_dealloc_cmap(&fbi->cmap);
+
+ unregister_framebuffer(fbi);
+ framebuffer_release(fbi);
+ }
+
+ platform_device_unregister(&mxcfb_device);
+ platform_driver_unregister(&mxcfb_driver);
+}
+
+module_init(mxcfb_init);
+module_exit(mxcfb_exit);
+
+EXPORT_SYMBOL(mxcfb_set_refresh_mode);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Epson framebuffer driver");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxcfb_epson_vga.c b/drivers/video/mxc/mxcfb_epson_vga.c
new file mode 100644
index 000000000000..28856b6158ac
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_epson_vga.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxcfb_epson_vga.c
+ *
+ * @brief MXC Frame buffer driver for SDC
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/mxcfb.h>
+#include <linux/ipu.h>
+#include <asm/mach-types.h>
+
+static struct spi_device *lcd_spi;
+static struct device *lcd_dev;
+
+static void lcd_init(void);
+static void lcd_poweron(void);
+static void lcd_poweroff(void);
+
+static void (*lcd_reset) (void);
+static struct regulator *io_reg;
+static struct regulator *core_reg;
+
+static struct fb_videomode video_modes[] = {
+ {
+ /* 480x640 @ 60 Hz */
+ "Epson-VGA", 60, 480, 640, 41701, 60, 41, 10, 5, 20, 10,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+};
+
+static void lcd_init_fb(struct fb_info *info)
+{
+ struct fb_var_screeninfo var;
+
+ memset(&var, 0, sizeof(var));
+
+ fb_videomode_to_var(&var, &video_modes[0]);
+
+ if (machine_is_mx31_3ds()) {
+ var.upper_margin = 0;
+ var.left_margin = 0;
+ }
+
+ var.activate = FB_ACTIVATE_ALL;
+ var.yres_virtual = var.yres * 2;
+
+ acquire_console_sem();
+ info->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(info, &var);
+ info->flags &= ~FBINFO_MISC_USEREVENT;
+ release_console_sem();
+}
+
+static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+
+ if (strcmp(event->info->fix.id, "DISP3 BG")) {
+ return 0;
+ }
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ lcd_init_fb(event->info);
+ lcd_poweron();
+ break;
+ case FB_EVENT_BLANK:
+ if ((event->info->var.xres != 480) ||
+ (event->info->var.yres != 640)) {
+ break;
+ }
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ lcd_poweron();
+ } else {
+ lcd_poweroff();
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = lcd_fb_event,
+};
+
+/*!
+ * This function is called whenever the SPI slave device is detected.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devinit lcd_probe(struct device *dev)
+{
+ int i;
+ struct mxc_lcd_platform_data *plat = dev->platform_data;
+
+ lcd_dev = dev;
+
+ if (plat) {
+ io_reg = regulator_get(dev, plat->io_reg);
+ if (!IS_ERR(io_reg)) {
+ regulator_set_voltage(io_reg, 1800000, 1800000);
+ regulator_enable(io_reg);
+ }
+ core_reg = regulator_get(dev, plat->core_reg);
+ if (!IS_ERR(core_reg)) {
+ regulator_set_voltage(core_reg, 2800000, 2800000);
+ regulator_enable(core_reg);
+ }
+
+ lcd_reset = plat->reset;
+ if (lcd_reset)
+ lcd_reset();
+ }
+
+ lcd_init();
+
+ for (i = 0; i < num_registered_fb; i++) {
+ if (strcmp(registered_fb[i]->fix.id, "DISP3 BG") == 0) {
+ lcd_init_fb(registered_fb[i]);
+ fb_show_logo(registered_fb[i], 0);
+ lcd_poweron();
+ }
+ }
+
+ fb_register_client(&nb);
+
+ return 0;
+}
+
+static int __devinit lcd_plat_probe(struct platform_device *pdev)
+{
+ ipu_adc_sig_cfg_t sig;
+ ipu_channel_params_t param;
+
+ memset(&sig, 0, sizeof(sig));
+ sig.ifc_width = 9;
+ sig.clk_pol = 1;
+ ipu_init_async_panel(0, IPU_PANEL_SERIAL, 90, IPU_PIX_FMT_GENERIC, sig);
+
+ memset(&param, 0, sizeof(param));
+ ipu_init_channel(DIRECT_ASYNC1, &param);
+
+ return lcd_probe(&pdev->dev);
+}
+
+static int __devinit lcd_spi_probe(struct spi_device *spi)
+{
+ lcd_spi = spi;
+
+ spi->bits_per_word = 9;
+ spi_setup(spi);
+
+ return lcd_probe(&spi->dev);
+}
+
+static int __devexit lcd_remove(struct device *dev)
+{
+ fb_unregister_client(&nb);
+ lcd_poweroff();
+ regulator_put(io_reg);
+ regulator_put(core_reg);
+
+ return 0;
+}
+
+static int __devexit lcd_spi_remove(struct spi_device *spi)
+{
+ int ret = lcd_remove(&spi->dev);
+ lcd_spi = NULL;
+ return ret;
+}
+
+static int __devexit lcd_plat_remove(struct platform_device *pdev)
+{
+ return lcd_remove(&pdev->dev);
+}
+
+static int lcd_suspend(struct spi_device *spi, pm_message_t message)
+{
+ lcd_poweroff();
+ return 0;
+}
+
+static int lcd_resume(struct spi_device *spi)
+{
+ if (lcd_reset)
+ lcd_reset();
+
+ lcd_init();
+ lcd_poweron();
+ return 0;
+}
+
+/*!
+ * spi driver structure for LTV350QV
+ */
+static struct spi_driver lcd_spi_dev_driver = {
+
+ .driver = {
+ .name = "lcd_spi",
+ .owner = THIS_MODULE,
+ },
+ .probe = lcd_spi_probe,
+ .remove = __devexit_p(lcd_spi_remove),
+ .suspend = lcd_suspend,
+ .resume = lcd_resume,
+};
+
+static struct platform_driver lcd_plat_driver = {
+ .driver = {
+ .name = "lcd_spi",
+ .owner = THIS_MODULE,
+ },
+ .probe = lcd_plat_probe,
+ .remove = __devexit_p(lcd_plat_remove),
+};
+
+#define param(x) ((x) | 0x100)
+
+/*
+ * Send init commands to L4F00242T03
+ *
+ */
+static void lcd_init(void)
+{
+ const u16 cmd[] = { 0x36, param(0), 0x3A, param(0x60) };
+
+ dev_dbg(lcd_dev, "initializing LCD\n");
+ if (lcd_spi) {
+ spi_write(lcd_spi, (const u8 *)cmd, ARRAY_SIZE(cmd));
+ } else {
+ ipu_disp_direct_write(DIRECT_ASYNC1, 0x36, 0);
+ ipu_disp_direct_write(DIRECT_ASYNC1, 0x100, 0);
+ ipu_disp_direct_write(DIRECT_ASYNC1, 0x3A, 0);
+ ipu_disp_direct_write(DIRECT_ASYNC1, 0x160, 0);
+ msleep(1);
+ ipu_uninit_channel(DIRECT_ASYNC1);
+ }
+}
+
+static int lcd_on;
+/*
+ * Send Power On commands to L4F00242T03
+ *
+ */
+static void lcd_poweron(void)
+{
+ const u16 slpout = 0x11;
+ const u16 dison = 0x29;
+ ipu_channel_params_t param;
+ if (lcd_on)
+ return;
+
+ dev_dbg(lcd_dev, "turning on LCD\n");
+
+ if (lcd_spi) {
+ msleep(60);
+ spi_write(lcd_spi, (const u8 *)&slpout, 1);
+ msleep(60);
+ spi_write(lcd_spi, (const u8 *)&dison, 1);
+ } else {
+ memset(&param, 0, sizeof(param));
+ ipu_init_channel(DIRECT_ASYNC1, &param);
+ ipu_disp_direct_write(DIRECT_ASYNC1, slpout, 0);
+ msleep(60);
+ ipu_disp_direct_write(DIRECT_ASYNC1, dison, 0);
+ msleep(1);
+ ipu_uninit_channel(DIRECT_ASYNC1);
+ }
+ lcd_on = 1;
+}
+
+/*
+ * Send Power Off commands to L4F00242T03
+ *
+ */
+static void lcd_poweroff(void)
+{
+ const u16 slpin = 0x10;
+ const u16 disoff = 0x28;
+ ipu_channel_params_t param;
+ if (!lcd_on)
+ return;
+
+ dev_dbg(lcd_dev, "turning off LCD\n");
+
+ if (lcd_spi) {
+ msleep(60);
+ spi_write(lcd_spi, (const u8 *)&disoff, 1);
+ msleep(60);
+ spi_write(lcd_spi, (const u8 *)&slpin, 1);
+ } else {
+ memset(&param, 0, sizeof(param));
+ ipu_init_channel(DIRECT_ASYNC1, &param);
+ ipu_disp_direct_write(DIRECT_ASYNC1, disoff, 0);
+ msleep(60);
+ ipu_disp_direct_write(DIRECT_ASYNC1, slpin, 0);
+ msleep(1);
+ ipu_uninit_channel(DIRECT_ASYNC1);
+ }
+ lcd_on = 0;
+}
+
+static int __init epson_lcd_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&lcd_plat_driver);
+ if (ret)
+ return ret;
+
+ return spi_register_driver(&lcd_spi_dev_driver);
+
+}
+
+static void __exit epson_lcd_exit(void)
+{
+ spi_unregister_driver(&lcd_spi_dev_driver);
+}
+
+module_init(epson_lcd_init);
+module_exit(epson_lcd_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Epson VGA LCD init driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxcfb_modedb.c b/drivers/video/mxc/mxcfb_modedb.c
new file mode 100644
index 000000000000..ad31c6b4f856
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_modedb.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/mxcfb.h>
+
+struct fb_videomode mxcfb_modedb[] = {
+ {
+ /* 240x320 @ 60 Hz */
+ "Sharp-QVGA", 60, 240, 320, 185925, 9, 16, 7, 9, 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE |
+ FB_SYNC_DATA_INVERT | FB_SYNC_CLK_IDLE_EN,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* 240x33 @ 60 Hz */
+ "Sharp-CLI", 60, 240, 33, 185925, 9, 16, 7, 9 + 287, 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE |
+ FB_SYNC_DATA_INVERT | FB_SYNC_CLK_IDLE_EN,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* 640x480 @ 60 Hz */
+ "NEC-VGA", 60, 640, 480, 38255, 144, 0, 34, 40, 1, 1,
+ FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* 640x480 @ 60 Hz */
+ "CPT-VGA", 60, 640, 480, 39683, 45, 114, 33, 11, 1, 1,
+ FB_SYNC_CLK_LAT_FALL,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* NTSC TV output */
+ "TV-NTSC", 60, 640, 480, 37538,
+ 38, 858 - 640 - 38 - 3,
+ 36, 518 - 480 - 36 - 1,
+ 3, 1,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* PAL TV output */
+ "TV-PAL", 50, 640, 480, 37538,
+ 38, 960 - 640 - 38 - 32,
+ 32, 555 - 480 - 32 - 3,
+ 32, 3,
+ 0,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+ {
+ /* TV output VGA mode, 640x480 @ 65 Hz */
+ "TV-VGA", 60, 640, 480, 40574, 35, 45, 9, 1, 46, 5,
+ 0, FB_VMODE_NONINTERLACED, 0,
+ },
+};
+
+int mxcfb_modedb_sz = ARRAY_SIZE(mxcfb_modedb);
diff --git a/drivers/video/mxc/tve.c b/drivers/video/mxc/tve.c
new file mode 100644
index 000000000000..6a37ce39592b
--- /dev/null
+++ b/drivers/video/mxc/tve.c
@@ -0,0 +1,803 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file tve.c
+ * @brief Driver for i.MX TV encoder
+ *
+ * @ingroup Framebuffer
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/clk.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/irq.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/mxcfb.h>
+#include <linux/regulator/consumer.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+
+#define TVE_ENABLE (1UL)
+#define TVE_DAC_FULL_RATE (0UL<<1)
+#define TVE_DAC_DIV2_RATE (1UL<<1)
+#define TVE_DAC_DIV4_RATE (2UL<<1)
+#define TVE_IPU_CLK_ENABLE (1UL<<3)
+
+#define CD_LM_INT 0x00000001
+#define CD_SM_INT 0x00000002
+#define CD_MON_END_INT 0x00000004
+#define CD_CH_0_LM_ST 0x00000001
+#define CD_CH_0_SM_ST 0x00000010
+#define CD_CH_1_LM_ST 0x00000002
+#define CD_CH_1_SM_ST 0x00000020
+#define CD_CH_2_LM_ST 0x00000004
+#define CD_CH_2_SM_ST 0x00000040
+#define CD_MAN_TRIG 0x00000100
+
+#define TVE_STAND_MASK (0x0F<<8)
+#define TVE_NTSC_STAND (0UL<<8)
+#define TVE_PAL_STAND (3UL<<8)
+#define TVE_HD720P60_STAND (4UL<<8)
+
+#define TVOUT_FMT_OFF 0
+#define TVOUT_FMT_NTSC 1
+#define TVOUT_FMT_PAL 2
+#define TVOUT_FMT_720P60 3
+
+static int enabled; /* enable power on or not */
+
+static struct fb_info *tve_fbi;
+
+struct tve_data {
+ struct platform_device *pdev;
+ int revision;
+ int cur_mode;
+ int output_mode;
+ int detect;
+ void *base;
+ int irq;
+ struct clk *clk;
+ struct regulator *dac_reg;
+ struct regulator *dig_reg;
+ struct delayed_work cd_work;
+} tve;
+
+struct tve_reg_mapping {
+ u32 tve_com_conf_reg;
+ u32 tve_cd_cont_reg;
+ u32 tve_int_cont_reg;
+ u32 tve_stat_reg;
+ u32 tve_mv_cont_reg;
+};
+
+struct tve_reg_fields_mapping {
+ u32 cd_en;
+ u32 cd_trig_mode;
+ u32 cd_lm_int;
+ u32 cd_sm_int;
+ u32 cd_mon_end_int;
+ u32 cd_man_trig;
+ u32 sync_ch_mask;
+ u32 tvout_mode_mask;
+ u32 sync_ch_offset;
+ u32 tvout_mode_offset;
+ u32 cd_ch_stat_offset;
+};
+
+static struct tve_reg_mapping tve_regs_v1 = {
+ 0, 0x14, 0x28, 0x2C, 0x48
+};
+
+static struct tve_reg_fields_mapping tve_reg_fields_v1 = {
+ 1, 2, 1, 2, 4, 0x00010000, 0x7000, 0x70, 12, 4, 8
+};
+
+static struct tve_reg_mapping tve_regs_v2 = {
+ 0, 0x34, 0x64, 0x68, 0xDC
+};
+
+static struct tve_reg_fields_mapping tve_reg_fields_v2 = {
+ 1, 2, 1, 2, 4, 0x01000000, 0x700000, 0x7000, 20, 12, 16
+};
+
+
+struct tve_reg_mapping *tve_regs;
+struct tve_reg_fields_mapping *tve_reg_fields;
+
+/* For MX37 need modify some fields in tve_probe */
+static struct fb_videomode video_modes[] = {
+ {
+ /* NTSC TV output */
+ "TV-NTSC", 60, 720, 480, 74074,
+ 122, 15,
+ 18, 26,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_EXT,
+ FB_VMODE_INTERLACED,
+ 0,},
+ {
+ /* PAL TV output */
+ "TV-PAL", 50, 720, 576, 74074,
+ 132, 11,
+ 22, 26,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_EXT,
+ FB_VMODE_INTERLACED | FB_VMODE_ODD_FLD_FIRST,
+ 0,},
+ {
+ /* 720p60 TV output */
+ "720P60", 60, 1280, 720, 13468,
+ 260, 109,
+ 25, 4,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT |
+ FB_SYNC_EXT,
+ FB_VMODE_NONINTERLACED,
+ 0,},
+};
+
+enum tvout_mode {
+ TV_OFF,
+ CVBS0,
+ CVBS2,
+ CVBS02,
+ SVIDEO,
+ SVIDEO_CVBS,
+ YPBPR,
+ RGB
+};
+
+static unsigned short tvout_mode_to_channel_map[8] = {
+ 0, /* TV_OFF */
+ 1, /* CVBS0 */
+ 4, /* CVBS2 */
+ 5, /* CVBS02 */
+ 1, /* SVIDEO */
+ 5, /* SVIDEO_CVBS */
+ 1, /* YPBPR */
+ 7 /* RGB */
+};
+
+
+static void tve_set_tvout_mode(int mode)
+{
+ u32 conf_reg;
+
+ conf_reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+ conf_reg &= ~(tve_reg_fields->sync_ch_mask |
+ tve_reg_fields->tvout_mode_mask);
+ /* clear sync_ch and tvout_mode fields */
+ conf_reg |=
+ mode << tve_reg_fields->
+ tvout_mode_offset | tvout_mode_to_channel_map[mode] <<
+ tve_reg_fields->sync_ch_offset;
+ __raw_writel(conf_reg, tve.base + tve_regs->tve_com_conf_reg);
+}
+
+static int _is_tvout_mode_hd_compatible(void)
+{
+ u32 conf_reg, mode;
+
+ conf_reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+ mode = (conf_reg >> tve_reg_fields->tvout_mode_offset) & 7;
+ if (mode == YPBPR || mode == RGB) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+/**
+ * tve_setup
+ * initial the CH7024 chipset by setting register
+ * @param:
+ * vos: output video format
+ * @return:
+ * 0 successful
+ * otherwise failed
+ */
+static int tve_setup(int mode)
+{
+ u32 reg;
+ struct clk *pll3_clk;
+ unsigned long pll3_clock_rate = 216000000;
+
+ if (tve.cur_mode == mode)
+ return 0;
+
+ tve.cur_mode = mode;
+
+ switch (mode) {
+ case TVOUT_FMT_PAL:
+ case TVOUT_FMT_NTSC:
+ pll3_clock_rate = 216000000;
+ break;
+ case TVOUT_FMT_720P60:
+ pll3_clock_rate = 297000000;
+ break;
+ }
+ if (enabled)
+ clk_disable(tve.clk);
+
+ pll3_clk = clk_get(NULL, "pll3");
+ clk_disable(pll3_clk);
+ clk_set_rate(pll3_clk, pll3_clock_rate);
+ clk_enable(pll3_clk);
+
+ clk_enable(tve.clk);
+
+ /* select output video format */
+ if (mode == TVOUT_FMT_PAL) {
+ reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_PAL_STAND;
+ __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+ pr_debug("TVE: change to PAL video\n");
+ } else if (mode == TVOUT_FMT_NTSC) {
+ reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_NTSC_STAND;
+ __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+ pr_debug("TVE: change to NTSC video\n");
+ } else if (mode == TVOUT_FMT_720P60) {
+ if (!_is_tvout_mode_hd_compatible()) {
+ tve_set_tvout_mode(YPBPR);
+ pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
+ }
+ reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_HD720P60_STAND;
+ __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+ pr_debug("TVE: change to 720P60 video\n");
+ } else if (mode == TVOUT_FMT_OFF) {
+ __raw_writel(0x0, tve.base + tve_regs->tve_com_conf_reg);
+ pr_debug("TVE: change to OFF video\n");
+ } else {
+ pr_debug("TVE: no such video format.\n");
+ if (!enabled)
+ clk_disable(tve.clk);
+ return -EINVAL;
+ }
+
+ if (!enabled)
+ clk_disable(tve.clk);
+
+ return 0;
+}
+
+/**
+ * tve_enable
+ * Enable the tve Power to begin TV encoder
+ */
+static void tve_enable(void)
+{
+ u32 reg;
+
+ if (!enabled) {
+ enabled = 1;
+ clk_enable(tve.clk);
+ reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+ __raw_writel(reg | TVE_IPU_CLK_ENABLE | TVE_ENABLE,
+ tve.base + tve_regs->tve_com_conf_reg);
+ pr_debug("TVE power on.\n");
+ }
+
+ /* enable interrupt */
+ __raw_writel(CD_SM_INT | CD_LM_INT | CD_MON_END_INT,
+ tve.base + tve_regs->tve_stat_reg);
+ __raw_writel(CD_SM_INT | CD_LM_INT | CD_MON_END_INT,
+ tve.base + tve_regs->tve_int_cont_reg);
+}
+
+/**
+ * tve_disable
+ * Disable the tve Power to stop TV encoder
+ */
+static void tve_disable(void)
+{
+ u32 reg;
+
+ if (enabled) {
+ enabled = 0;
+ reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+ __raw_writel(reg & ~TVE_ENABLE & ~TVE_IPU_CLK_ENABLE,
+ tve.base + tve_regs->tve_com_conf_reg);
+ clk_disable(tve.clk);
+ pr_debug("TVE power off.\n");
+ }
+}
+
+static int tve_update_detect_status(void)
+{
+ int old_detect = tve.detect;
+ u32 stat_lm, stat_sm, stat;
+ u32 int_ctl = __raw_readl(tve.base + tve_regs->tve_int_cont_reg);
+ u32 cd_cont_reg =
+ __raw_readl(tve.base + tve_regs->tve_cd_cont_reg);
+ u32 timeout = 40;
+
+ if ((cd_cont_reg & 0x1) == 0) {
+ pr_warning("Warning: pls enable TVE CD first!\n");
+ return tve.detect;
+ }
+
+ stat = __raw_readl(tve.base + tve_regs->tve_stat_reg);
+ while (((stat & CD_MON_END_INT) == 0) && (timeout > 0)) {
+ msleep(2);
+ timeout -= 2;
+ stat = __raw_readl(tve.base + tve_regs->tve_stat_reg);
+ }
+ if (((stat & CD_MON_END_INT) == 0) && (timeout <= 0)) {
+ pr_warning("Warning: get detect resultwithout CD_MON_END_INT!\n");
+ return tve.detect;
+ }
+
+ stat = stat >> tve_reg_fields->cd_ch_stat_offset;
+ stat_lm = stat & (CD_CH_0_LM_ST | CD_CH_1_LM_ST | CD_CH_2_LM_ST);
+ if ((stat_lm == (CD_CH_0_LM_ST | CD_CH_1_LM_ST | CD_CH_2_LM_ST)) &&
+ ((stat & (CD_CH_0_SM_ST | CD_CH_1_SM_ST | CD_CH_2_SM_ST)) == 0)
+ ) {
+ tve.detect = 3;
+ tve.output_mode = YPBPR;
+ } else if ((stat_lm == (CD_CH_0_LM_ST | CD_CH_1_LM_ST)) &&
+ ((stat & (CD_CH_0_SM_ST | CD_CH_1_SM_ST)) == 0)) {
+ tve.detect = 4;
+ tve.output_mode = SVIDEO;
+ } else if (stat_lm == CD_CH_0_LM_ST) {
+ stat_sm = stat & CD_CH_0_SM_ST;
+ if (stat_sm != 0) {
+ /* headset */
+ tve.detect = 2;
+ tve.output_mode = TV_OFF;
+ } else {
+ tve.detect = 1;
+ tve.output_mode = CVBS0;
+ }
+ } else if (stat_lm == CD_CH_2_LM_ST) {
+ stat_sm = stat & CD_CH_2_SM_ST;
+ if (stat_sm != 0) {
+ /* headset */
+ tve.detect = 2;
+ tve.output_mode = TV_OFF;
+ } else {
+ tve.detect = 1;
+ tve.output_mode = CVBS2;
+ }
+ } else {
+ /* none */
+ tve.detect = 0;
+ tve.output_mode = TV_OFF;
+ }
+
+ tve_set_tvout_mode(tve.output_mode);
+
+ /* clear interrupt */
+ __raw_writel(CD_MON_END_INT | CD_LM_INT | CD_SM_INT,
+ tve.base + tve_regs->tve_stat_reg);
+
+ __raw_writel(int_ctl | CD_SM_INT | CD_LM_INT,
+ tve.base + tve_regs->tve_int_cont_reg);
+
+ if (old_detect != tve.detect)
+ sysfs_notify(&tve.pdev->dev.kobj, NULL, "headphone");
+
+ dev_dbg(&tve.pdev->dev, "detect = %d mode = %d\n",
+ tve.detect, tve.output_mode);
+ return tve.detect;
+}
+
+static void cd_work_func(struct work_struct *work)
+{
+ tve_update_detect_status();
+}
+
+static int tve_man_detect(void)
+{
+ u32 cd_cont;
+ u32 int_cont;
+
+ if (!enabled)
+ return -1;
+
+ int_cont = __raw_readl(tve.base + tve_regs->tve_int_cont_reg);
+ __raw_writel(int_cont &
+ ~(tve_reg_fields->cd_sm_int | tve_reg_fields->cd_lm_int),
+ tve.base + tve_regs->tve_int_cont_reg);
+
+ cd_cont = __raw_readl(tve.base + tve_regs->tve_cd_cont_reg);
+ __raw_writel(cd_cont | tve_reg_fields->cd_trig_mode,
+ tve.base + tve_regs->tve_cd_cont_reg);
+
+ __raw_writel(tve_reg_fields->cd_sm_int | tve_reg_fields->
+ cd_lm_int | tve_reg_fields->
+ cd_mon_end_int | tve_reg_fields->cd_man_trig,
+ tve.base + tve_regs->tve_stat_reg);
+
+ while ((__raw_readl(tve.base + tve_regs->tve_stat_reg)
+ & tve_reg_fields->cd_mon_end_int) == 0)
+ msleep(5);
+
+ tve_update_detect_status();
+
+ __raw_writel(cd_cont, tve.base + tve_regs->tve_cd_cont_reg);
+ __raw_writel(int_cont, tve.base + tve_regs->tve_int_cont_reg);
+
+ return tve.detect;
+}
+
+static irqreturn_t tve_detect_handler(int irq, void *data)
+{
+ u32 int_ctl = __raw_readl(tve.base + tve_regs->tve_int_cont_reg);
+
+ /* disable INT first */
+ int_ctl &= ~(CD_SM_INT | CD_LM_INT | CD_MON_END_INT);
+ __raw_writel(int_ctl, tve.base + tve_regs->tve_int_cont_reg);
+
+ __raw_writel(CD_MON_END_INT | CD_LM_INT | CD_SM_INT,
+ tve.base + tve_regs->tve_stat_reg);
+
+ schedule_delayed_work(&tve.cd_work, msecs_to_jiffies(1000));
+
+ return IRQ_HANDLED;
+}
+
+int tve_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+ struct fb_info *fbi = event->info;
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ pr_debug("fb registered event\n");
+ if ((tve_fbi != NULL) || strcmp(fbi->fix.id, "DISP3 BG - DI1"))
+ break;
+
+ tve_fbi = fbi;
+ fb_add_videomode(&video_modes[0], &tve_fbi->modelist);
+ fb_add_videomode(&video_modes[1], &tve_fbi->modelist);
+ fb_add_videomode(&video_modes[2], &tve_fbi->modelist);
+ break;
+ case FB_EVENT_MODE_CHANGE:
+ {
+ struct fb_videomode cur_mode;
+ struct fb_videomode *mode;
+ struct list_head *pos;
+ struct fb_modelist *modelist;
+
+ if (tve_fbi != fbi)
+ break;
+
+ fb_var_to_videomode(&cur_mode, &fbi->var);
+
+ list_for_each(pos, &tve_fbi->modelist) {
+ modelist = list_entry(pos, struct fb_modelist, list);
+ mode = &modelist->mode;
+ if (fb_mode_is_equal(&cur_mode, mode)) {
+ fbi->mode = mode;
+ break;
+ }
+ }
+
+ if (!fbi->mode) {
+ tve_disable();
+ tve.cur_mode = TVOUT_FMT_OFF;
+ return 0;
+ }
+
+ pr_debug("fb mode change event: xres=%d, yres=%d\n",
+ fbi->mode->xres, fbi->mode->yres);
+
+ tve_disable();
+
+ if (fb_mode_is_equal(fbi->mode, &video_modes[0])) {
+ tve_setup(TVOUT_FMT_NTSC);
+ tve_enable();
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes[1])) {
+ tve_setup(TVOUT_FMT_PAL);
+ tve_enable();
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes[2])) {
+ tve_setup(TVOUT_FMT_720P60);
+ tve_enable();
+ } else {
+ tve_setup(TVOUT_FMT_OFF);
+ }
+ break;
+ }
+ case FB_EVENT_BLANK:
+ if ((tve_fbi != fbi) || (fbi->mode == NULL))
+ return 0;
+
+ if (*((int *)event->data) == FB_BLANK_UNBLANK) {
+ if (fb_mode_is_equal(fbi->mode, &video_modes[0])) {
+ if (tve.cur_mode != TVOUT_FMT_NTSC) {
+ tve_disable();
+ tve_setup(TVOUT_FMT_NTSC);
+ }
+ tve_enable();
+ } else if (fb_mode_is_equal(fbi->mode,
+ &video_modes[1])) {
+ if (tve.cur_mode != TVOUT_FMT_PAL) {
+ tve_disable();
+ tve_setup(TVOUT_FMT_PAL);
+ }
+ tve_enable();
+ } else if (fb_mode_is_equal(fbi->mode,
+ &video_modes[2])) {
+ if (tve.cur_mode != TVOUT_FMT_720P60) {
+ tve_disable();
+ tve_setup(TVOUT_FMT_720P60);
+ }
+ tve_enable();
+ } else {
+ tve_setup(TVOUT_FMT_OFF);
+ }
+ } else
+ tve_disable();
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = tve_fb_event,
+};
+
+static ssize_t show_headphone(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int detect;
+
+ if (!enabled) {
+ strcpy(buf, "tve power off\n");
+ return strlen(buf);
+ }
+
+ detect = tve_update_detect_status();
+
+ if (detect == 0)
+ strcpy(buf, "none\n");
+ else if (detect == 1)
+ strcpy(buf, "cvbs\n");
+ else if (detect == 2)
+ strcpy(buf, "headset\n");
+ else if (detect == 3)
+ strcpy(buf, "component\n");
+ else
+ strcpy(buf, "svideo\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+
+static int _tve_get_revision(void)
+{
+ u32 conf_reg;
+ u32 rev = 0;
+
+ /* find out TVE rev based on the base addr default value
+ * can be used at the init/probe ONLY */
+ conf_reg = __raw_readl(tve.base);
+ switch (conf_reg) {
+ case 0x00842000:
+ rev = 1;
+ break;
+ case 0x00100000:
+ rev = 2;
+ break;
+ }
+ return rev;
+}
+
+static int tve_probe(struct platform_device *pdev)
+{
+ int ret, i;
+ struct resource *res;
+ struct tve_platform_data *plat_data = pdev->dev.platform_data;
+ u32 conf_reg;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL)
+ return -ENOMEM;
+
+ tve.pdev = pdev;
+ tve.base = ioremap(res->start, res->end - res->start);
+
+ tve.irq = platform_get_irq(pdev, 0);
+ if (tve.irq < 0) {
+ ret = tve.irq;
+ goto err0;
+ }
+
+ ret = request_irq(tve.irq, tve_detect_handler, 0, pdev->name, pdev);
+ if (ret < 0)
+ goto err0;
+
+ ret = device_create_file(&pdev->dev, &dev_attr_headphone);
+ if (ret < 0)
+ goto err1;
+
+ for (i = 0; i < num_registered_fb; i++) {
+ if (strcmp(registered_fb[i]->fix.id, "DISP3 BG - DI1") == 0) {
+ tve_fbi = registered_fb[i];
+ break;
+ }
+ }
+
+ /* adjust video mode for mx37 */
+ if (cpu_is_mx37()) {
+ video_modes[0].left_margin = 121;
+ video_modes[0].right_margin = 16;
+ video_modes[0].upper_margin = 17;
+ video_modes[0].lower_margin = 5;
+ video_modes[1].left_margin = 131;
+ video_modes[1].right_margin = 12;
+ video_modes[1].upper_margin = 21;
+ video_modes[1].lower_margin = 3;
+ }
+
+ if (tve_fbi != NULL) {
+ fb_add_videomode(&video_modes[0], &tve_fbi->modelist);
+ fb_add_videomode(&video_modes[1], &tve_fbi->modelist);
+ fb_add_videomode(&video_modes[2], &tve_fbi->modelist);
+ }
+
+ tve.dac_reg = regulator_get(&pdev->dev, plat_data->dac_reg);
+ if (!IS_ERR(tve.dac_reg)) {
+ regulator_set_voltage(tve.dac_reg, 2500000, 2500000);
+ regulator_enable(tve.dac_reg);
+ }
+
+ tve.dig_reg = regulator_get(&pdev->dev, plat_data->dig_reg);
+ if (!IS_ERR(tve.dig_reg)) {
+ regulator_set_voltage(tve.dig_reg, 1250000, 1250000);
+ regulator_enable(tve.dig_reg);
+ }
+
+ tve.clk = clk_get(&pdev->dev, "tve_clk");
+ clk_set_rate(tve.clk, 216000000);
+ clk_enable(tve.clk);
+
+ tve.revision = _tve_get_revision();
+ if (tve.revision == 1) {
+ tve_regs = &tve_regs_v1;
+ tve_reg_fields = &tve_reg_fields_v1;
+ } else {
+ tve_regs = &tve_regs_v2;
+ tve_reg_fields = &tve_reg_fields_v2;
+ }
+
+ /* Setup cable detect, for YPrPb mode, default use channel#0 for Y */
+ INIT_DELAYED_WORK(&tve.cd_work, cd_work_func);
+ if (tve.revision == 1)
+ __raw_writel(0x01067701, tve.base + tve_regs->tve_cd_cont_reg);
+ else
+ __raw_writel(0x00770601, tve.base + tve_regs->tve_cd_cont_reg);
+
+ conf_reg = 0;
+ __raw_writel(conf_reg, tve.base + tve_regs->tve_com_conf_reg);
+
+ __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg - 4 * 5);
+ __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg - 4 * 4);
+ __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg - 4 * 3);
+ __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg - 4 * 2);
+ __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg - 4);
+ __raw_writel(0x00000000, tve.base + tve_regs->tve_mv_cont_reg);
+
+ clk_disable(tve.clk);
+
+ ret = fb_register_client(&nb);
+ if (ret < 0)
+ goto err2;
+
+ return 0;
+err2:
+ device_remove_file(&pdev->dev, &dev_attr_headphone);
+err1:
+ free_irq(tve.irq, pdev);
+err0:
+ iounmap(tve.base);
+ return ret;
+}
+
+static int tve_remove(struct platform_device *pdev)
+{
+ if (enabled) {
+ clk_disable(tve.clk);
+ enabled = 0;
+ }
+ free_irq(tve.irq, pdev);
+ device_remove_file(&pdev->dev, &dev_attr_headphone);
+ fb_unregister_client(&nb);
+ return 0;
+}
+
+/*!
+ * PM suspend/resume routing
+ */
+static int tve_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (enabled) {
+ __raw_writel(0, tve.base + tve_regs->tve_int_cont_reg);
+ __raw_writel(0, tve.base + tve_regs->tve_cd_cont_reg);
+ __raw_writel(0, tve.base + tve_regs->tve_com_conf_reg);
+ clk_disable(tve.clk);
+ }
+ return 0;
+}
+
+static int tve_resume(struct platform_device *pdev)
+{
+ if (enabled) {
+ clk_enable(tve.clk);
+
+ /* Setup cable detect */
+ if (tve.revision == 1)
+ __raw_writel(0x01067701,
+ tve.base + tve_regs->tve_cd_cont_reg);
+ else
+ __raw_writel(0x00770601,
+ tve.base + tve_regs->tve_cd_cont_reg);
+
+ if (tve.cur_mode == TVOUT_FMT_NTSC) {
+ tve_disable();
+ tve.cur_mode = TVOUT_FMT_OFF;
+ tve_setup(TVOUT_FMT_NTSC);
+ } else if (tve.cur_mode == TVOUT_FMT_PAL) {
+ tve_disable();
+ tve.cur_mode = TVOUT_FMT_OFF;
+ tve_setup(TVOUT_FMT_PAL);
+ } else if (tve.cur_mode == TVOUT_FMT_720P60) {
+ tve_disable();
+ tve.cur_mode = TVOUT_FMT_OFF;
+ tve_setup(TVOUT_FMT_720P60);
+ }
+ tve_enable();
+ }
+
+ return 0;
+}
+
+static struct platform_driver tve_driver = {
+ .driver = {
+ .name = "tve",
+ },
+ .probe = tve_probe,
+ .remove = tve_remove,
+ .suspend = tve_suspend,
+ .resume = tve_resume,
+};
+
+static int __init tve_init(void)
+{
+ return platform_driver_register(&tve_driver);
+}
+
+static void __exit tve_exit(void)
+{
+ platform_driver_unregister(&tve_driver);
+}
+
+module_init(tve_init);
+module_exit(tve_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX TV encoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/ns9360fb.c b/drivers/video/ns9360fb.c
new file mode 100644
index 000000000000..c6c31c458186
--- /dev/null
+++ b/drivers/video/ns9360fb.c
@@ -0,0 +1,336 @@
+/*
+ * drivers/video/ns9360fb.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <mach/regs-sys-ns9360.h>
+#include <mach/ns9360fb.h>
+#include <mach/gpio.h>
+
+#define LCD_TIMING(x) ((x) * 4)
+#define LCD_TIMING2_BCD (1 << 26)
+#define LCD_TIMING2_PCD(x) (((x) & 0x1F))
+#define LCD_UPBASE 0x10
+#define LCD_CONTROL 0x1c
+
+#define DRIVER_NAME "ns9360fb"
+
+#define fb2nfb(fbi) container_of(fbi, struct ns9360fb_info, fb)
+struct ns9360fb_info {
+ struct fb_info fb;
+
+ struct resource *mem;
+ struct clk *clk;
+ struct ns9360fb_pdata *pdata;
+
+ void __iomem *ioaddr;
+
+ dma_addr_t fb_p;
+ unsigned char *fb_v;
+ unsigned int fb_size;
+
+ u32 pseudo_pal[16];
+};
+
+static void ns9360fb_select_video_clk_src(int source)
+{
+ u32 reset, clock;
+
+ reset = __raw_readl(SYS_RESET) & ~SYS_RESET_LCDC;
+ __raw_writel(reset, SYS_RESET);
+
+ clock = __raw_readl(SYS_CLOCK) & ~SYS_CLOCK_LPCSEXT;
+ if (!source) {
+ /* Use external clock */
+ if (!gpio_request(15, "ns9360fb-extclk")) {
+ gpio_configure_ns9360(15, 0, 0, 2);
+ clock |= SYS_CLOCK_LPCSEXT;
+ } else {
+ printk(KERN_ERR DRIVER_NAME
+ ": unable to request gpio for external video clock\n");
+ }
+ }
+ __raw_writel(clock, SYS_CLOCK);
+
+ reset = __raw_readl(SYS_RESET) | SYS_RESET_LCDC;
+ __raw_writel(reset, SYS_RESET);
+}
+
+static int ns9360fb_set_par(struct fb_info *fb)
+{
+ fb->fix.visual = FB_VISUAL_TRUECOLOR;
+ fb->fix.line_length = fb->var.width * fb->var.bits_per_pixel / 8;
+
+ return 0;
+}
+
+static int ns9360fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *fb)
+{
+ struct ns9360fb_info *info = fb2nfb(fb);
+
+ if (regno > info->fb.var.bits_per_pixel)
+ return -EINVAL;
+
+ info->pseudo_pal[regno] = (red & 0xF800) |
+ ((green & 0xF800) >> 5) | ((green & 0xF800) >> 10);
+
+ return 0;
+}
+
+static struct fb_ops ns9360fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_set_par = ns9360fb_set_par,
+ .fb_setcolreg = ns9360fb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static int __init ns9360fb_probe(struct platform_device *pdev)
+{
+ struct ns9360fb_info *info;
+ struct ns9360fb_display *display;
+ char *option = NULL;
+ int ret, i;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_dbg(&pdev->dev, "%s: err_alloc_info\n", __func__);
+ ret = -ENOMEM;
+ goto err_alloc_info;
+ }
+ platform_set_drvdata(pdev, info);
+
+ info->pdata = pdev->dev.platform_data;
+ if (!info->pdata) {
+ dev_dbg(&pdev->dev, "%s: err_pdata\n", __func__);
+ ret = -ENOENT;
+ goto err_pdata;
+ }
+
+ /* Get the display information */
+ if (!info->pdata->num_displays) {
+ dev_err(&pdev->dev, "no display information available\n");
+ ret = -EINVAL;
+ goto err_pdata;
+ }
+
+ if (info->pdata->num_displays > 1) {
+ /*
+ * If there are multiple displays, use the one specified
+ * through the command line parameter vith following
+ * format video=displayfb:<display_name>
+ */
+ if (fb_get_options("displayfb", &option)) {
+ dev_err(&pdev->dev,
+ "no display information available in commnad line\n");
+ ret = -ENODEV;
+ goto err_pdata;
+ }
+ if (!option) {
+ ret = -ENODEV;
+ goto err_pdata;
+ }
+ dev_info(&pdev->dev, "display options: %s\n", option);
+
+ for (i = 0; i < info->pdata->num_displays; i++) {
+ if (!strcmp(option, info->pdata->displays[i].display_name))
+ info->pdata->display = &info->pdata->displays[i];
+ }
+ } else {
+ /* If there is only one, that is what we use */
+ info->pdata->display = info->pdata->displays;
+ }
+
+ if ((display = info->pdata->display) == NULL) {
+ dev_err(&pdev->dev, "no display information available\n");
+ ret = -ENODEV;
+ goto err_pdata;
+ }
+
+ info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!info->mem) {
+ dev_dbg(&pdev->dev, "%s: err_mem\n", __func__);
+ ret = -ENOENT;
+ goto err_mem;
+ }
+
+ if (!request_mem_region(info->mem->start,
+ info->mem->end - info->mem->start + 1,
+ DRIVER_NAME)) {
+ dev_dbg(&pdev->dev, "%s: err_req_mem\n", __func__);
+ ret = -EBUSY;
+ goto err_req_mem;
+ }
+
+ info->ioaddr = ioremap(info->mem->start,
+ info->mem->end - info->mem->start + 1);
+ if (info->ioaddr <= 0) {
+ dev_dbg(&pdev->dev, "%s: err_map_mem\n", __func__);
+ ret = -EBUSY;
+ goto err_map_mem;
+ }
+
+ strcpy(info->fb.fix.id, DRIVER_NAME);
+ info->fb.fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fb.fix.accel = FB_ACCEL_NONE;
+
+ info->fb.flags = FBINFO_FLAG_DEFAULT;
+ info->fb.fbops = &ns9360fb_ops;
+ info->fb.pseudo_palette = &info->pseudo_pal;
+
+ info->fb.var.activate = FB_ACTIVATE_NOW;
+ info->fb.var.height = display->height;
+ info->fb.var.yres = display->height;
+ info->fb.var.yres_virtual = display->height;
+ info->fb.var.width = display->width;
+ info->fb.var.xres = display->width;
+ info->fb.var.xres_virtual = display->width;
+ info->fb.var.vmode = FB_VMODE_NONINTERLACED;
+ info->fb.var.bits_per_pixel = 16;
+
+ info->fb.var.red.offset = 10;
+ info->fb.var.green.offset = 5;
+ info->fb.var.blue.offset = 0;
+ info->fb.var.red.length = 5;
+ info->fb.var.green.length = 5;
+ info->fb.var.blue.length = 5;
+
+ info->fb.fix.smem_len = info->fb.var.xres * info->fb.var.yres *
+ info->fb.var.bits_per_pixel / 8;
+
+ pdev->dev.coherent_dma_mask = (u32)-1;
+
+ info->fb_size = PAGE_ALIGN(info->fb.fix.smem_len + PAGE_SIZE);
+ info->fb_v = dma_alloc_writecombine(&pdev->dev, info->fb_size,
+ &info->fb_p, GFP_KERNEL);
+ if (!info->fb_v) {
+ dev_dbg(&pdev->dev, "%s: err_alloc_fb\n", __func__);
+ ret = -ENOMEM;
+ goto err_alloc_fb;
+ }
+
+ info->fb.screen_base = info->fb_v + PAGE_SIZE;
+ info->fb.fix.smem_start = info->fb_p + PAGE_SIZE;
+
+ info->clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(info->clk)) {
+ dev_dbg(&pdev->dev, "%s: err_clk_get\n", __func__);
+ ret = PTR_ERR(info->clk);
+ goto err_clk_get;
+ }
+
+ memset(info->fb.screen_base, 0, info->fb.fix.smem_len);
+
+ ret = clk_enable(info->clk);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_clk_enable\n", __func__);
+ goto err_clk_enable;
+ }
+
+ /* display->clock = 0 means use external source */
+ ns9360fb_select_video_clk_src(display->clock);
+
+ if (!(display->timing[2] & LCD_TIMING2_BCD)) {
+ display->timing[2] |=
+ LCD_TIMING2_PCD((clk_get_rate(info->clk) /
+ display->clock) - 1);
+ }
+
+ /* write configuration to video controller */
+ for (i = 0; i < 4; i++)
+ iowrite32(display->timing[i],
+ info->ioaddr + LCD_TIMING(i));
+
+ iowrite32(info->fb.fix.smem_start, info->ioaddr + LCD_UPBASE);
+ iowrite32(display->control, info->ioaddr + LCD_CONTROL);
+
+ ret = register_framebuffer(&info->fb);
+ if (ret) {
+ dev_dbg(&pdev->dev, "%s: err_reg_fb\n", __func__);
+ goto err_reg_fb;
+ }
+
+ dev_info(&pdev->dev, "fb mapped to 0x%p (0x%p), display config %s\n",
+ (void *)info->fb.fix.smem_start, info->fb.screen_base,
+ display->display_name);
+
+ return 0;
+
+err_reg_fb:
+ clk_disable(info->clk);
+err_clk_enable:
+ clk_put(info->clk);
+err_clk_get:
+ dma_free_writecombine(&pdev->dev, info->fb_size,
+ info->fb_v, info->fb_p);
+err_alloc_fb:
+ iounmap(info->ioaddr);
+err_map_mem:
+ release_mem_region(info->mem->start,
+ info->mem->end - info->mem->start + 1);
+err_req_mem:
+err_mem:
+err_pdata:
+ kfree(info);
+err_alloc_info:
+
+ return ret;
+}
+
+static int ns9360fb_remove(struct platform_device *pdev)
+{
+ struct ns9360fb_info *info = platform_get_drvdata(pdev);
+
+ clk_disable(info->clk);
+ clk_put(info->clk);
+
+ dma_free_writecombine(&pdev->dev, info->fb_size,
+ info->fb_v, info->fb_p);
+
+ iounmap(info->ioaddr);
+ release_mem_region(info->mem->start,
+ info->mem->end - info->mem->start + 1);
+
+ kfree(info);
+ return 0;
+}
+
+static struct platform_driver ns9360fb_driver = {
+ .probe = ns9360fb_probe,
+ .remove = ns9360fb_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit ns9360fb_init(void)
+{
+ return platform_driver_register(&ns9360fb_driver);
+}
+
+static void __exit ns9360fb_cleanup(void)
+{
+ platform_driver_unregister(&ns9360fb_driver);
+}
+
+module_init(ns9360fb_init);
+module_exit(ns9360fb_cleanup);
+
+MODULE_AUTHOR("Matthias Ludwig");
+MODULE_DESCRIPTION("Driver for Digi ns9360 SoC framebuffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/s3c2410fb_tft.c b/drivers/video/s3c2410fb_tft.c
new file mode 100644
index 000000000000..8632beb68a7e
--- /dev/null
+++ b/drivers/video/s3c2410fb_tft.c
@@ -0,0 +1,1008 @@
+/* -*- linux-c -*-
+ *
+ * linux/drivers/video/s3cfb.c
+ *
+ * $Id: s3cfb.c,v 1.63 2007/07/12 05:26:04 yreom Exp $
+ *
+ * Revision 1.16 2006/09/14 04:45:15 ihlee215
+ * OSD support added
+ *
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * S3C LCD Controller Frame Buffer Driver
+ * based on skeletonfb.c, sa1100fb.c
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/div64.h>
+#include <linux/math64.h>
+
+#include <asm/mach/map.h>
+/* #include <asm/arch/registers.h> */
+#include <mach/idle.h>
+#include <mach/fb.h>
+#include <mach/regs-gpio.h>
+
+#include "s3c2410fb.h"
+
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+
+
+
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] s3cfb: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "[ INFO ] s3cfb: " fmt, ## args)
+
+
+#if 1
+#define S3CFB_DEBUG
+#endif
+
+#ifdef S3CFB_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "s3cfb: " fmt, ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+
+
+#define S3CFB_DRIVER_NAME "s3c2410fb-tft"
+
+
+static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct s3c2410fb_info *fbi = info->par;
+ struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
+ struct s3c2410fb_display *display = NULL;
+ struct s3c2410fb_display *default_display = mach_info->displays +
+ mach_info->default_display;
+ int type = default_display->type;
+ unsigned i;
+
+ printk_debug("Calling %s: var=%p, info=%p\n", __func__, var, info);
+
+ /* validate x/y resolution */
+ /* choose default mode if possible */
+ if (var->yres == default_display->yres &&
+ var->xres == default_display->xres &&
+ var->bits_per_pixel == default_display->bpp)
+ display = default_display;
+ else
+ for (i = 0; i < mach_info->num_displays; i++)
+ if (type == mach_info->displays[i].type &&
+ var->yres == mach_info->displays[i].yres &&
+ var->xres == mach_info->displays[i].xres &&
+ var->bits_per_pixel == mach_info->displays[i].bpp) {
+ display = mach_info->displays + i;
+ break;
+ }
+
+ if (!display) {
+ printk_err("wrong resolution or depth %dx%d at %d bpp\n",
+ var->xres, var->yres, var->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ /* it is always the size as the display */
+ var->xres_virtual = display->xres;
+ var->yres_virtual = display->yres;
+ var->height = display->height;
+ var->width = display->width;
+
+ /* copy lcd settings */
+ var->pixclock = display->pixclock;
+ var->left_margin = display->left_margin;
+ var->right_margin = display->right_margin;
+ var->upper_margin = display->upper_margin;
+ var->lower_margin = display->lower_margin;
+ var->vsync_len = display->vsync_len;
+ var->hsync_len = display->hsync_len;
+
+ /*
+ * If using the Power Signal then request the GPIO
+ * (@XXX: Use the correct request and configuration function for the GPIO)
+ * Luis Galdos
+ */
+ if (display->lcdcon5 & S3C2410_LCDCON5_PWREN) {
+ printk_info("Configuring the power LED GPIO\n");
+ s3c2410_gpio_cfgpin(S3C2410_GPG4, S3C2410_GPG4_LCDPWREN);
+ }
+
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ /* set r/g/b positions */
+ switch (var->bits_per_pixel) {
+ case 1:
+ case 2:
+ case 4:
+ var->red.offset = 0;
+ var->red.length = var->bits_per_pixel;
+ var->green = var->red;
+ var->blue = var->red;
+ break;
+ case 8:
+ /* 8 bpp 332 */
+ var->red.length = 3;
+ var->red.offset = 5;
+ var->green.length = 3;
+ var->green.offset = 2;
+ var->blue.length = 2;
+ var->blue.offset = 0;
+ break;
+ case 12:
+ /* 12 bpp 444 */
+ var->red.length = 4;
+ var->red.offset = 8;
+ var->green.length = 4;
+ var->green.offset = 4;
+ var->blue.length = 4;
+ var->blue.offset = 0;
+ break;
+ case 16:
+ /* 16 bpp, 565 format */
+ if (S3C24XX_LCD_WINCON_BPP(display->wincon0) ==
+ S3C24XX_LCD_WINCON_16BPP_565) {
+ /* 16 bpp, 565 format */
+ var->red.offset = 11;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ } else {
+ /* 16 bpp, 1555 format */
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ }
+ break;
+ case 18:
+ /* 18bpp only supports 666 */
+ var->red.offset = 12;
+ var->green.offset = 6;
+ var->blue.offset = 0;
+ var->red.length = 6;
+ var->green.length = 6;
+ var->blue.length = 6;
+ break;
+ case 32:
+ /* 24 bpp 888 and 8 dummy */
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ break;
+ }
+ return 0;
+}
+
+
+#if 0
+static void s3cfb_dummy_values(struct s3c2410fb_info *fbi)
+{
+
+ writel(0x10415, fbi->io + S3C24XX_LCD_WINCON0);
+ writel(0x00, fbi->io + S3C24XX_LCD_WINCON1);
+
+ writel(0x173, fbi->io + S3C24XX_LCD_VIDCON0);
+ writel(0x195D060, fbi->io + S3C24XX_LCD_VIDCON1);
+
+ writel(0x1F0203, fbi->io + S3C24XX_LCD_VIDTCON0);
+ writel(0x40618, fbi->io + S3C24XX_LCD_VIDTCON1);
+ writel(0xEFA7F, fbi->io + S3C24XX_LCD_VIDTCON2);
+
+ writel(0x00, fbi->io + S3C24XX_LCD_VIDOSD0A);
+ writel(0x13F9DF, fbi->io + S3C24XX_LCD_VIDOSD0B);
+}
+#endif /* 0: Only for testing */
+
+
+/* Write into the register the passed display configuration */
+static void s3c2410fb_activate_var(struct fb_info *info)
+{
+ struct s3c2410fb_info *fbi;
+ unsigned short hsync_cnt, vclk_cnt;
+ unsigned char clkval;
+ struct s3c2410fb_display *display;
+ struct s3c2410fb_mach_info *mach_info;
+ unsigned long pixel_clk, lcd_clk;
+ unsigned long vidcon0, vidcon1, vidtcon0, vidtcon1, vidtcon2;
+ unsigned long wincon0, wincon1;
+ unsigned long vidosd0a, vidosd0b;
+
+ printk_debug("Calling %s\n", __func__);
+
+ fbi = info->par;
+ mach_info = fbi->dev->platform_data;
+ display = mach_info->displays + mach_info->default_display;
+
+ /* Write the configuration into the register VIDCON0 */
+ hsync_cnt = display->lower_margin + /* VBPD */
+ display->upper_margin + /* VFPD */
+ display->vsync_len + /* VSPW */
+ display->height; /* LINEVAL */
+
+ vclk_cnt = display->right_margin + /* HBPD */
+ display->left_margin + /* HFPD */
+ display->hsync_len + /* HSPW */
+ display->width; /* HOZVAL */
+
+ pixel_clk = (display->frame_rate * vclk_cnt * hsync_cnt);
+ lcd_clk = clk_get_rate(fbi->clk);
+
+ /*
+ * @FIXME: The U-Boot has another clock calculation. See under:
+ * cpu/s3c24xx/s3c2443/fb.c
+ */
+ clkval = (lcd_clk / pixel_clk) - 1;
+ printk_debug("Calculated CLKVAL is 0x%x (pixel clk: %lu | lcd clk %lu)\n",
+ clkval, pixel_clk, lcd_clk);
+
+ vidcon1 = readl(fbi->io + S3C24XX_LCD_VIDCON1);
+ vidcon0 = readl(fbi->io + S3C24XX_LCD_VIDCON0);
+ wincon1 = readl(fbi->io + S3C24XX_LCD_WINCON1);
+
+ /* Configure the clock */
+ vidcon0 |= (display->vidcon0 | S3C24XX_LCD_VIDCON0_CLKVAL(clkval));
+
+ vidcon1 |= display->vidcon1;
+
+ vidtcon0 = S3C24XX_LCD_VIDTCON0_VSPW(display->vsync_len - 1) |
+ S3C24XX_LCD_VIDTCON0_VFPD(display->upper_margin - 1) |
+ S3C24XX_LCD_VIDTCON0_VBPD(display->lower_margin - 1);
+
+ vidtcon1 = S3C24XX_LCD_VIDTCON1_HSPW(display->hsync_len - 1) |
+ S3C24XX_LCD_VIDTCON1_HFPD(display->left_margin - 1) |
+ S3C24XX_LCD_VIDTCON1_HBPD(display->right_margin - 1);
+
+
+ vidtcon2 = S3C24XX_LCD_VIDTCON2_HOZVAL(display->width - 1) |
+ S3C24XX_LCD_VIDTCON2_LINEVAL(display->height - 1);
+
+ /* Write the user configuration too */
+ wincon0 = (display->wincon0 |
+ S3C24XX_LCD_WINCON0_ENWIN_F |
+ S3C24XX_LCD_WINCON0_HAWSWP |
+ S3C24XX_LCD_WINCON0_4WBURST);
+
+
+ vidosd0a = 0x00;
+ vidosd0b = S3C24XX_LCD_VIDOSD0B_RIGHT_X(display->width - 1) |
+ S3C24XX_LCD_VIDOSD0B_RIGHT_Y(display->height - 1);
+
+
+ /* And now write the configuration into the corresponding registers */
+ writel(vidcon0, fbi->io + S3C24XX_LCD_VIDCON0);
+ writel(vidcon1, fbi->io + S3C24XX_LCD_VIDCON1);
+ writel(vidtcon0, fbi->io + S3C24XX_LCD_VIDTCON0);
+ writel(vidtcon1, fbi->io + S3C24XX_LCD_VIDTCON1);
+ writel(vidtcon2, fbi->io + S3C24XX_LCD_VIDTCON2);
+ writel(wincon0, fbi->io + S3C24XX_LCD_WINCON0);
+ writel(vidosd0a, fbi->io + S3C24XX_LCD_VIDOSD0A);
+ writel(vidosd0b, fbi->io + S3C24XX_LCD_VIDOSD0B);
+
+ /* These are the configuration addresses for the frame buffer */
+ writel(info->fix.smem_start, fbi->io + S3C24XX_LCD_VIDW00ADD0B0);
+ writel(info->fix.smem_start + info->fix.smem_len,
+ fbi->io + S3C24XX_LCD_VIDW00ADD1B0);
+
+ printk_debug("vidcon0 0x%08x | vidcon1 0x%08x\n",
+ (unsigned int)vidcon0,
+ (unsigned int)vidcon1);
+ printk_debug("vidtcon0 0x%08x | vidtcon1 0x%08x | vidtcon2 0x%08x\n",
+ (unsigned int)vidtcon0,
+ (unsigned int)vidtcon1,
+ (unsigned int)vidtcon2);
+}
+
+
+/* Write the display configuration to the hardware */
+static int s3c2410fb_set_par(struct fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->var;
+
+ printk_debug("Calling %s\n", __func__);
+
+ switch (var->bits_per_pixel) {
+ case 32:
+ case 16:
+ case 12:
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ break;
+ case 1:
+ info->fix.visual = FB_VISUAL_MONO01;
+ break;
+ default:
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+ }
+
+
+ info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
+ printk_debug("Line length is %i\n", info->fix.line_length);
+
+
+ /* activate this new configuration */
+ s3c2410fb_activate_var(info);
+ return 0;
+}
+
+
+static void s3c2410fb_lcd_enable(struct s3c2410fb_info *fbi, int enable)
+{
+ unsigned long flags;
+ unsigned long vidcon0;
+
+ local_irq_save(flags);
+
+ printk_debug("%s the TFT LCD\n", enable ? "Enabling" : "Disabling");
+
+ vidcon0 = readl(fbi->io + S3C24XX_LCD_VIDCON0);
+ if (enable)
+ vidcon0 |= (S3C24XX_LCD_VIDCON0_ENVID | S3C24XX_LCD_VIDCON0_ENVID_F);
+ else
+ vidcon0 &= ~(S3C24XX_LCD_VIDCON0_ENVID | S3C24XX_LCD_VIDCON0_ENVID_F);
+
+ writel(vidcon0, fbi->io + S3C24XX_LCD_VIDCON0);
+ local_irq_restore(flags);
+}
+
+
+
+static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
+{
+ struct s3c2410fb_info *fbi = info->par;
+
+ printk_debug("Blank (mode=%d, info=%p, power down=%i)\n",
+ blank_mode, info, FB_BLANK_POWERDOWN);
+
+ if (blank_mode == FB_BLANK_POWERDOWN) {
+ s3c2410fb_lcd_enable(fbi, 0);
+ } else {
+ s3c2410fb_lcd_enable(fbi, 1);
+ }
+
+ return 0;
+}
+
+static void schedule_palette_update(struct s3c2410fb_info *fbi,
+ unsigned int regno, unsigned int val)
+{
+ unsigned long flags;
+ unsigned long irqen;
+ void __iomem *irq_base = fbi->irq_base;
+
+ printk_debug("Calling %s\n", __func__);
+
+ local_irq_save(flags);
+
+ fbi->palette_buffer[regno] = val;
+
+ if (!fbi->palette_ready) {
+ fbi->palette_ready = 1;
+ /* enable IRQ */
+ irqen = readl(irq_base + S3C24XX_LCDINTMSK);
+ irqen &= ~S3C2410_LCDINT_FRSYNC;
+ writel(irqen, irq_base + S3C24XX_LCDINTMSK);
+ }
+
+ local_irq_restore(flags);
+}
+
+
+/* from pxafb.c */
+static inline unsigned int chan_to_field(unsigned int chan,
+ struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+
+
+static int s3c2410fb_setcolreg(unsigned regno,
+ unsigned red, unsigned green, unsigned blue,
+ unsigned transp, struct fb_info *info)
+{
+ struct s3c2410fb_info *fbi = info->par;
+ void __iomem *regs = fbi->io;
+ unsigned int val;
+
+
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /* true-colour, use pseudo-palette */
+ if (regno < 16) {
+ u32 *pal = info->pseudo_palette;
+
+ val = chan_to_field(red, &info->var.red);
+ val |= chan_to_field(green, &info->var.green);
+ val |= chan_to_field(blue, &info->var.blue);
+
+ pal[regno] = val;
+ }
+ break;
+
+ case FB_VISUAL_PSEUDOCOLOR:
+ if (regno < 256) {
+ /* currently assume RGB 5-6-5 mode */
+
+ val = (red >> 0) & 0xf800;
+ val |= (green >> 5) & 0x07e0;
+ val |= (blue >> 11) & 0x001f;
+
+ writel(val, regs + S3C2410_TFTPAL(regno));
+ schedule_palette_update(fbi, regno, val);
+ }
+
+ break;
+
+ default:
+ return 1; /* unknown type */
+ }
+
+ return 0;
+}
+
+
+
+/* Function called when the frame buffer device (/dev/fbX) is being opened */
+static int s3c2410fb_tft_open(struct fb_info *info, int user)
+{
+ struct s3c2410fb_info *fbi;
+
+ fbi = info->par;
+
+ printk_debug("Calling %s\n", __func__);
+ s3c2410fb_lcd_enable(fbi, 1);
+ return 0;
+}
+
+
+static int s3c2410fb_tft_release(struct fb_info *info, int user)
+{
+ struct s3c2410fb_info *fbi;
+
+ fbi = info->par;
+ printk_debug("Calling %s\n", __func__);
+ s3c2410fb_lcd_enable(fbi, 0);
+ return 0;
+}
+
+
+static int s3c2410fb_tft_pan(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+
+
+
+ return 0;
+}
+
+/* These are the available device operations */
+static struct fb_ops s3cfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = s3c2410fb_check_var,
+ .fb_set_par = s3c2410fb_set_par,
+ .fb_blank = s3c2410fb_blank,
+ .fb_setcolreg = s3c2410fb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_open = s3c2410fb_tft_open,
+ .fb_release = s3c2410fb_tft_release,
+ .fb_pan_display = s3c2410fb_tft_pan,
+};
+
+
+
+static irqreturn_t s3cfb_irq(int irq, void *dev_id)
+{
+ printk_info("IRQ of the TFT LCD enabled?\n");
+/* struct s3c2410fb_info *fbi = dev_id; */
+/* void __iomem *irq_base = fbi->irq_base; */
+/* unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND); */
+
+/* if (lcdirq & S3C2410_LCDINT_FRSYNC) { */
+/* if (fbi->palette_ready) */
+/* s3c2410fb_write_palette(fbi); */
+
+/* writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND); */
+/* writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND); */
+/* } */
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * s3c2410fb_map_video_memory():
+ * Allocates the DRAM memory for the frame buffer. This buffer is
+ * remapped into a non-cached, non-buffered, memory region to
+ * allow palette and pixel writes to occur without flushing the
+ * cache. Once this area is remapped, all virtual memory
+ * access to the video memory should occur at the new region.
+ */
+static int __init s3c2410fb_map_video_memory(struct fb_info *info)
+{
+ struct s3c2410fb_info *fbi = info->par;
+ dma_addr_t map_dma;
+ unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
+
+ printk_debug("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
+
+ info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
+ &map_dma, GFP_KERNEL);
+
+ if (info->screen_base) {
+ /* prevent initial garbage on screen */
+ printk_debug("map_video_memory: clear %p:%08x\n",
+ info->screen_base, map_size);
+ memset(info->screen_base, 0x00, map_size);
+
+ info->fix.smem_start = map_dma;
+ info->fix.smem_len = map_size;
+
+ printk_debug("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
+ info->fix.smem_start, info->screen_base, map_size);
+ }
+
+ return info->screen_base ? 0 : -ENOMEM;
+}
+
+
+
+static inline void modify_gpio(void __iomem *reg,
+ unsigned long set, unsigned long mask)
+{
+ unsigned long tmp;
+
+ tmp = readl(reg) & ~mask;
+ writel(tmp | set, reg);
+}
+
+
+
+static int s3c2410fb_init_registers(struct fb_info *info)
+{
+ struct s3c2410fb_info *fbi = info->par;
+ struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
+ unsigned long flags;
+
+ /* Initialise LCD with values from haret */
+
+ local_irq_save(flags);
+
+ /* modify the gpio(s) with interrupts set (bjd) */
+
+ modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
+ modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
+ modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
+ modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
+
+ local_irq_restore(flags);
+
+/* printk_debug("LPCSEL = 0x%08lx\n", mach_info->lpcsel); */
+/* writel(mach_info->lpcsel, lpcsel); */
+
+/* printk_debug("replacing TPAL %08x\n", readl(tpal)); */
+
+ /* ensure temporary palette disabled */
+/* writel(0x00, tpal); */
+
+ return 0;
+}
+
+
+
+static inline void s3c2410fb_unmap_video_memory(struct fb_info *info)
+{
+ struct s3c2410fb_info *fbi = info->par;
+
+ dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
+ info->screen_base, info->fix.smem_start);
+}
+
+
+
+int __init s3c_fb_probe(struct platform_device *pdev)
+{
+ struct s3c2410fb_info *info;
+ struct s3c2410fb_display *display;
+ struct fb_info *fbinfo;
+ struct s3c2410fb_mach_info *mach_info;
+ struct resource *res;
+ int ret;
+ int irq;
+ int i;
+ int size;
+ u32 vidcon0;
+
+ /* @XXX: Use a higher probe for setting the type */
+ enum s3c_drv_type drv_type = DRV_S3C2410;
+
+ printk_debug("Probing a new device ID %i\n", pdev->id);
+
+
+ /* Get the user defined LCD controller configuration */
+ mach_info = pdev->dev.platform_data;
+ if (mach_info == NULL) {
+ printk_err("No platform data for lcd, cannot attach\n");
+ return -EINVAL;
+ }
+
+ if (mach_info->default_display >= mach_info->num_displays) {
+ printk_err("Default is %d but only %d displays\n",
+ mach_info->default_display, mach_info->num_displays);
+ return -EINVAL;
+ }
+
+ /* Get the user defined display configuration */
+ display = mach_info->displays + mach_info->default_display;
+
+ /* Get the IRQ for the display controller */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ printk_err("No irq for device.\n");
+ return -ENOENT;
+ }
+
+ /* Allocate the frame buffer for this device */
+ fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
+ if (!fbinfo)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, fbinfo);
+
+ /* Use the private data for our internal settings */
+ info = fbinfo->par;
+ info->dev = &pdev->dev;
+ info->drv_type = drv_type;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory registers\n");
+ ret = -ENXIO;
+ goto dealloc_fb;
+ }
+
+ size = (res->end - res->start) + 1;
+ info->mem = request_mem_region(res->start, size, pdev->name);
+ if (info->mem == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region\n");
+ ret = -ENOENT;
+ goto dealloc_fb;
+ }
+
+ info->io = ioremap(res->start, size);
+ if (info->io == NULL) {
+ dev_err(&pdev->dev, "ioremap() of registers failed\n");
+ ret = -ENXIO;
+ goto release_mem;
+ }
+
+ info->irq_base = info->io +
+ ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);
+
+ /* @XXX: Do we really need the below code? */
+ strcpy(fbinfo->fix.id, S3CFB_DRIVER_NAME);
+
+ /* Stop the video first */
+ vidcon0 = readl(info->io + S3C24XX_LCD_VIDCON0);
+ vidcon0 &= (S3C24XX_LCD_VIDCON0_ENVID | S3C24XX_LCD_VIDCON0_ENVID_F);
+ writel(vidcon0, info->io + S3C24XX_LCD_VIDCON0);
+
+ /* Start the initial fix configuration */
+ fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
+ fbinfo->fix.type_aux = 0;
+ fbinfo->fix.xpanstep = 0;
+ fbinfo->fix.ypanstep = 0;
+ fbinfo->fix.ywrapstep = 0;
+ fbinfo->fix.accel = FB_ACCEL_NONE;
+
+ fbinfo->var.nonstd = 0;
+ fbinfo->var.activate = FB_ACTIVATE_NOW;
+ fbinfo->var.accel_flags = 0;
+ fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
+
+ fbinfo->fbops = &s3cfb_ops;
+ fbinfo->flags = FBINFO_FLAG_DEFAULT;
+ fbinfo->pseudo_palette = &info->pseudo_pal;
+
+ for (i = 0; i < 256; i++)
+ info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
+
+ /* Request our IRQ */
+ ret = request_irq(irq, s3cfb_irq, IRQF_DISABLED, pdev->name, info);
+ if (ret) {
+ printk_err("Cannot get irq %d - err %d\n", irq, ret);
+ ret = -EBUSY;
+ goto release_regs;
+ }
+
+ /*
+ * We are using the HCLK, then the U-Boot is using this clock too
+ */
+ if (display->vidcon0 & S3C24XX_LCD_VIDCON0_CLKSEL_LCD) {
+ printk_err("Invalid clock (only tested with the HCLK).\n");
+ ret = -EINVAL;
+ goto release_irq;
+ }
+
+ /* @FIXME: Select the clock depending on the passed display configuration */
+ info->clk = clk_get(NULL, "hclk");
+ if (!info->clk || IS_ERR(info->clk)) {
+ printk_err("Failed to get lcd clock source\n");
+ ret = -ENOENT;
+ goto release_irq;
+ }
+
+ clk_enable(info->clk);
+ printk_debug("Got and enabled the HCLK clock\n");
+
+ msleep(1);
+
+ /* find maximum required memory size for display */
+ for (i = 0; i < mach_info->num_displays; i++) {
+ unsigned long smem_len = mach_info->displays[i].xres;
+
+ smem_len *= mach_info->displays[i].yres;
+ smem_len *= mach_info->displays[i].bpp;
+ smem_len >>= 3;
+ if (fbinfo->fix.smem_len < smem_len)
+ fbinfo->fix.smem_len = smem_len;
+ }
+
+ /* Initialize video memory */
+ ret = s3c2410fb_map_video_memory(fbinfo);
+ if (ret) {
+ printk_err("Failed to allocate video RAM: %d\n", ret);
+ ret = -ENOMEM;
+ goto release_clock;
+ }
+
+
+ fbinfo->var.xres = display->xres;
+ fbinfo->var.yres = display->yres;
+ fbinfo->var.bits_per_pixel = display->bpp;
+
+
+ s3c2410fb_init_registers(fbinfo);
+
+
+ s3c2410fb_check_var(&fbinfo->var, fbinfo);
+
+
+ ret = register_framebuffer(fbinfo);
+ if (ret) {
+ printk_err("Failed to register the frambuffer device, %i\n", ret);
+ goto free_video_memory;
+ }
+
+
+ printk_info("New frame buffer fb%d: %s frame buffer device\n",
+ fbinfo->node, fbinfo->fix.id);
+ return 0;
+
+ free_video_memory:
+ s3c2410fb_unmap_video_memory(fbinfo);
+
+ release_clock:
+ clk_disable(info->clk);
+ clk_put(info->clk);
+
+ release_irq:
+ free_irq(irq, info);
+
+ release_regs:
+ iounmap(info->io);
+
+ release_mem:
+ release_resource(info->mem);
+ kfree(info->mem);
+
+ dealloc_fb:
+ platform_set_drvdata(pdev, NULL);
+ framebuffer_release(fbinfo);
+
+ return ret;
+}
+
+/* s3c_fb_stop_lcd
+ *
+ * shutdown the lcd controller
+ */
+void s3c_fb_stop_lcd(void)
+{
+/* unsigned long flags; */
+/* unsigned long tmp; */
+/* printk("s3c_fb_stop_lcd() called.\n"); */
+
+/* local_irq_save(flags); */
+
+/* #if defined (CONFIG_FB_LTV350QV ) || defined (CONFIG_FB_LMS430WQ ) || defined(CONFIG_ARCH_S3C64XX) */
+/* tmp = __raw_readl(S3C_VIDCON0); */
+/* __raw_writel(tmp & ~(ENVID|ENVID_F), S3C_VIDCON0); */
+/* #else */
+/* tmp = __raw_readl(S3C_LCDCON1); */
+/* __raw_writel(tmp & ~(ENVID|ENVID_F), S3C_LCDCON1); */
+/* #endif */
+/* local_irq_restore(flags); */
+}
+
+
+void s3c_fb_start_lcd(void) {
+/* unsigned long flags; */
+/* unsigned long tmp; */
+/* printk("s3c_fb_start_lcd() called.\n"); */
+
+/* local_irq_save(flags); */
+
+/* #if defined (CONFIG_FB_LTV350QV ) || defined (CONFIG_FB_LMS430WQ ) || defined(CONFIG_ARCH_S3C64XX) */
+/* tmp = __raw_readl(S3C_VIDCON0); */
+/* __raw_writel(tmp | ENVID | ENVID_F, S3C_VIDCON0); */
+/* #else */
+/* tmp = __raw_readl(S3C_LCDCON1); */
+/* __raw_writel(tmp | ENVID | ENVID_F, S3C_LCDCON1); */
+/* #endif */
+/* local_irq_restore(flags); */
+}
+
+/*
+ * Cleanup
+ */
+static int s3c_fb_remove(struct platform_device *pdev)
+{
+ struct fb_info *fbinfo;
+ struct s3c2410fb_info *info;
+ int irq;
+
+ fbinfo = platform_get_drvdata(pdev);
+ info = fbinfo->par;
+
+ unregister_framebuffer(fbinfo);
+
+ /* Free the requested clock */
+ if (info->clk) {
+ clk_disable(info->clk);
+ clk_put(info->clk);
+ info->clk = NULL;
+ }
+
+ /* Free the requested IRQ */
+ irq = platform_get_irq(pdev, 0);
+ free_irq(irq, info);
+
+ /* Unamp que requested memory mapped registers */
+ iounmap(info->io);
+
+
+ release_resource(info->mem);
+ kfree(info->mem);
+
+ platform_set_drvdata(pdev, NULL);
+
+ framebuffer_release(fbinfo);
+
+/* struct s3c_fb_info *info = fbinfo->par; */
+/* int irq; */
+/* int index=0; */
+
+/* s3c_fb_stop_lcd(); */
+/* msleep(1); */
+
+/* for(index=0; index<S3C_FB_NUM; index++){ */
+/* s3c_fb_unmap_video_memory((struct s3c_fb_info *)&info[index]); */
+
+/* if (lcd_clock) { */
+/* clk_disable(lcd_clock); */
+/* clk_put(lcd_clock); */
+/* lcd_clock = NULL; */
+/* } */
+
+/* irq = platform_get_irq(pdev, 0); */
+/* free_irq(irq,&info[index]); */
+/* release_mem_region((unsigned long)S3C_VA_LCD, S3C_SZ_LCD); */
+/* unregister_framebuffer(&(info[index].fb)); */
+/* } // for(index=0; index<CONFIG_FB_NUM; index++) */
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/* suspend and resume support for the lcd controller */
+static int s3c_fb_suspend(struct platform_device *dev, pm_message_t state)
+{
+/* s3c_fb_stop_lcd(); */
+
+/* /\* sleep before disabling the clock, we need to ensure */
+/* * the LCD DMA engine is not going to get back on the bus */
+/* * before the clock goes off again (bjd) *\/ */
+
+/* msleep(1); */
+/* clk_disable(lcd_clock); */
+
+ return 0;
+}
+
+static int s3c_fb_resume(struct platform_device *dev)
+{
+/* clk_enable(lcd_clock); */
+/* msleep(1); */
+
+/* Init_LDI(); */
+/* #if 0 */
+/* for(int index=0; index<S3C_FB_NUM; index++) */
+/* s3c_fb_init_registers(&info[index]); */
+/* #endif */
+
+ return 0;
+}
+
+#else
+#define s3c_fb_suspend NULL
+#define s3c_fb_resume NULL
+#endif
+
+static struct platform_driver s3c_fb_driver = {
+ .probe = s3c_fb_probe,
+ .remove = s3c_fb_remove,
+ .suspend = s3c_fb_suspend,
+ .resume = s3c_fb_resume,
+ .driver = {
+ .name = S3CFB_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+
+int __devinit s3c_fb_init(void)
+{
+ return platform_driver_register(&s3c_fb_driver);
+}
+
+
+static void __exit s3c_fb_cleanup(void)
+{
+ platform_driver_unregister(&s3c_fb_driver);
+}
+
+EXPORT_SYMBOL(s3c_fb_stop_lcd);
+EXPORT_SYMBOL(s3c_fb_start_lcd);
+
+module_init(s3c_fb_init);
+module_exit(s3c_fb_cleanup);
+
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("Framebuffer driver for the S3C");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/s3c2443fb_tft.c b/drivers/video/s3c2443fb_tft.c
new file mode 100644
index 000000000000..ed81f138966c
--- /dev/null
+++ b/drivers/video/s3c2443fb_tft.c
@@ -0,0 +1,737 @@
+/*
+ * linux/drivers/video/s3c2443fb_tft.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Derived from s3c2410fb.c from Arnaud Patard and Ben Dooks
+ * It could be merged in future
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+
+#include <asm/mach/map.h>
+#include <mach/idle.h>
+#include <mach/s3c2443-fb.h>
+#include <mach/regs-gpio.h>
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] " DRV_NAME ": " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "[ INFO ] " DRV_NAME ": " fmt, ## args)
+
+#if 0
+#define S3C2443_FB_DEBUG
+#endif
+
+#ifdef S3C2443_FB_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG DRV_NAME ": " fmt, ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+#define DRV_NAME "s3c2443fb-tft"
+#define PALETTE_BUFF_CLEAR (0x80000000)
+
+struct s3c2443fb_info {
+ struct device *dev;
+ struct clk *clk;
+ struct resource *mem;
+ void __iomem *io;
+ void __iomem *irq_base;
+ unsigned int palette_ready;
+ u32 palette_buffer[256];
+ u32 pseudo_pal[16];
+};
+
+static int s3c2443_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct s3c2443fb_info *fbi = info->par;
+ struct s3c2443fb_mach_info *mach_info = fbi->dev->platform_data;
+ struct s3c2443fb_display *display = NULL;
+
+ display = mach_info->display;
+ if (!display) {
+ dev_err(fbi->dev, "unable to access display info(null)\n");
+ return -EINVAL;
+ }
+
+ /* it is always the size as the display */
+ var->xres_virtual = display->xres;
+ var->yres_virtual = display->yres;
+ var->height = display->height;
+ var->width = display->width;
+
+ /* copy lcd settings */
+ var->pixclock = display->pixclock;
+ var->left_margin = display->left_margin;
+ var->right_margin = display->right_margin;
+ var->upper_margin = display->upper_margin;
+ var->lower_margin = display->lower_margin;
+ var->vsync_len = display->vsync_len;
+ var->hsync_len = display->hsync_len;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+
+ /* set r/g/b positions */
+ switch (var->bits_per_pixel) {
+ case 1:
+ case 2:
+ case 4:
+ var->red.offset = 0;
+ var->red.length = var->bits_per_pixel;
+ var->green = var->red;
+ var->blue = var->red;
+ break;
+ case 8:
+ /* 8 bpp 332 */
+ var->red.length = 3;
+ var->red.offset = 5;
+ var->green.length = 3;
+ var->green.offset = 2;
+ var->blue.length = 2;
+ var->blue.offset = 0;
+ break;
+ case 12:
+ /* 12 bpp 444 */
+ var->red.length = 4;
+ var->red.offset = 8;
+ var->green.length = 4;
+ var->green.offset = 4;
+ var->blue.length = 4;
+ var->blue.offset = 0;
+ break;
+ case 16:
+ /* 16 bpp, 565 format */
+ if (display->bpp_mode == S3C24XX_LCD_WINCON_16BPP_565) {
+ /* 16 bpp, 565 format */
+ var->red.offset = 11;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ } else {
+ /* 16 bpp, 1555 format */
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ }
+ break;
+ case 18:
+ /* 18bpp only supports 666 */
+ var->red.offset = 12;
+ var->green.offset = 6;
+ var->blue.offset = 0;
+ var->red.length = 6;
+ var->green.length = 6;
+ var->blue.length = 6;
+ break;
+ case 32:
+ /* 24 bpp 888 and 8 dummy */
+ var->red.length = 8;
+ var->red.offset = 16;
+ var->green.length = 8;
+ var->green.offset = 8;
+ var->blue.length = 8;
+ var->blue.offset = 0;
+ break;
+ }
+
+ return 0;
+}
+
+/* Write into the register the passed display configuration */
+static void s3c2443fb_activate_var(struct fb_info *info)
+{
+ struct s3c2443fb_info *fbi = info->par;
+ struct s3c2443fb_mach_info *mach_info = fbi->dev->platform_data;
+ struct s3c2443fb_display *display = mach_info->display;
+ unsigned short hsync_cnt, vclk_cnt;
+ unsigned char clkval;
+ unsigned long pixel_clk, lcd_clk;
+ unsigned long vidcon0, vidcon1, vidtcon0, vidtcon1, vidtcon2;
+ unsigned long wincon0, wincon1;
+ unsigned long vidosd0a, vidosd0b;
+
+ /* Compute number of clocks per frame */
+ hsync_cnt = display->lower_margin + /* VBPD */
+ display->upper_margin + /* VFPD */
+ display->vsync_len + /* VSPW */
+ display->height + /* LINEVAL */
+ 4;
+
+ vclk_cnt = display->right_margin + /* HBPD */
+ display->left_margin + /* HFPD */
+ display->hsync_len + /* HSPW */
+ display->width + /* HOZVAL */
+ 4;
+
+ pixel_clk = (display->frame_rate * (vclk_cnt+1) * hsync_cnt);
+ lcd_clk = clk_get_rate(fbi->clk);
+ clkval = (lcd_clk / pixel_clk) - 1;
+
+ if (abs((lcd_clk / clkval) - pixel_clk) >
+ abs((lcd_clk / (clkval + 1)) - pixel_clk))
+ clkval++;
+
+ printk_debug("Calculated CLKVAL is 0x%x (pixel clk: %lu | lcd clk %lu)\n",
+ clkval, pixel_clk, lcd_clk);
+
+ vidcon1 = readl(fbi->io + S3C24XX_LCD_VIDCON1);
+ vidcon0 = readl(fbi->io + S3C24XX_LCD_VIDCON0);
+ wincon1 = readl(fbi->io + S3C24XX_LCD_WINCON1);
+
+ /* Configure the clock */
+ vidcon0 &= ~(S3C24XX_LCD_VIDCON0_CLKSEL_MASK |
+ S3C24XX_LCD_VIDCON0_CLKVAL_MASK);
+ if (!strcmp(display->clock_source, "display-if"))
+ vidcon0 |= S3C24XX_LCD_VIDCON0_CLKSEL_LCD;
+
+ vidcon0 |= (display->vidcon0 | S3C24XX_LCD_VIDCON0_CLKVAL(clkval));
+ vidcon1 |= display->vidcon1;
+
+ vidtcon0 = S3C24XX_LCD_VIDTCON0_VSPW(display->vsync_len - 1) |
+ S3C24XX_LCD_VIDTCON0_VFPD(display->upper_margin - 1) |
+ S3C24XX_LCD_VIDTCON0_VBPD(display->lower_margin - 1);
+
+ vidtcon1 = S3C24XX_LCD_VIDTCON1_HSPW(display->hsync_len - 1) |
+ S3C24XX_LCD_VIDTCON1_HFPD(display->left_margin - 1) |
+ S3C24XX_LCD_VIDTCON1_HBPD(display->right_margin - 1);
+
+ vidtcon2 = S3C24XX_LCD_VIDTCON2_HOZVAL(display->width - 1) |
+ S3C24XX_LCD_VIDTCON2_LINEVAL(display->height - 1);
+
+ /* Write the user configuration too */
+ wincon0 = display->bpp_mode |
+ S3C24XX_LCD_WINCON0_ENWIN_F |
+ S3C24XX_LCD_WINCON0_HAWSWP |
+ S3C24XX_LCD_WINCON0_16WBURST;
+
+ vidosd0a = 0x00;
+ vidosd0b = S3C24XX_LCD_VIDOSD0B_RIGHT_X(display->width - 1) |
+ S3C24XX_LCD_VIDOSD0B_RIGHT_Y(display->height - 1);
+
+ /* And now write the configuration into the corresponding registers */
+ writel(vidcon0, fbi->io + S3C24XX_LCD_VIDCON0);
+ writel(vidcon1, fbi->io + S3C24XX_LCD_VIDCON1);
+ writel(vidtcon0, fbi->io + S3C24XX_LCD_VIDTCON0);
+ writel(vidtcon1, fbi->io + S3C24XX_LCD_VIDTCON1);
+ writel(vidtcon2, fbi->io + S3C24XX_LCD_VIDTCON2);
+ writel(wincon0, fbi->io + S3C24XX_LCD_WINCON0);
+ writel(vidosd0a, fbi->io + S3C24XX_LCD_VIDOSD0A);
+ writel(vidosd0b, fbi->io + S3C24XX_LCD_VIDOSD0B);
+
+ /* These are the configuration addresses for the frame buffer */
+ writel(info->fix.smem_start, fbi->io + S3C24XX_LCD_VIDW00ADD0B0);
+ writel(info->fix.smem_start + info->fix.smem_len,
+ fbi->io + S3C24XX_LCD_VIDW00ADD1B0);
+
+ printk_debug("vidcon0 0x%08x | vidcon1 0x%08x\n",
+ (unsigned int)vidcon0,
+ (unsigned int)vidcon1);
+ printk_debug("vidtcon0 0x%08x | vidtcon1 0x%08x | vidtcon2 0x%08x\n",
+ (unsigned int)vidtcon0,
+ (unsigned int)vidtcon1,
+ (unsigned int)vidtcon2);
+}
+
+/* Write the display configuration to the hardware */
+static int s3c2443fb_set_par(struct fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->var;
+
+ switch (var->bits_per_pixel) {
+ case 32:
+ case 16:
+ case 12:
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ break;
+ case 1:
+ info->fix.visual = FB_VISUAL_MONO01;
+ break;
+ default:
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+ }
+
+
+ info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
+
+ /* activate this new configuration */
+ s3c2443fb_activate_var(info);
+ return 0;
+}
+
+static void s3c2443fb_lcd_enable(struct s3c2443fb_info *fbi, int enable)
+{
+ struct s3c2443fb_mach_info *mach_info = fbi->dev->platform_data;
+ struct s3c2443fb_display *display = mach_info->display;
+ unsigned long flags;
+ unsigned long vidcon0;
+
+ local_irq_save(flags);
+ vidcon0 = readl(fbi->io + S3C24XX_LCD_VIDCON0);
+ if (enable)
+ vidcon0 |= S3C24XX_LCD_VIDCON0_ENVID | S3C24XX_LCD_VIDCON0_ENVID_F;
+ else
+ vidcon0 &= ~(S3C24XX_LCD_VIDCON0_ENVID | S3C24XX_LCD_VIDCON0_ENVID_F);
+
+ writel(vidcon0, fbi->io + S3C24XX_LCD_VIDCON0);
+
+ if (display->display_power_enable)
+ display->display_power_enable(enable);
+
+ local_irq_restore(flags);
+}
+
+static int s3c2443fb_blank(int blank_mode, struct fb_info *info)
+{
+ struct s3c2443fb_info *fbi = info->par;
+
+ printk_debug("Blank (mode=%d, info=%p)\n", blank_mode, info);
+
+ if (blank_mode == FB_BLANK_POWERDOWN) {
+ s3c2443fb_lcd_enable(fbi, 0);
+ } else {
+ s3c2443fb_lcd_enable(fbi, 1);
+ }
+
+ return 0;
+}
+
+static void schedule_palette_update(struct s3c2443fb_info *fbi,
+ unsigned int regno, unsigned int val)
+{
+ unsigned long flags;
+ unsigned long irqen;
+ void __iomem *irq_base = fbi->irq_base;
+
+ printk_debug("Calling %s\n", __func__);
+
+ local_irq_save(flags);
+
+ fbi->palette_buffer[regno] = val;
+
+ if (!fbi->palette_ready) {
+ fbi->palette_ready = 1;
+ /* enable IRQ */
+ irqen = readl(irq_base + S3C24XX_LCDINTMSK);
+ irqen &= ~S3C2410_LCDINT_FRSYNC;
+ writel(irqen, irq_base + S3C24XX_LCDINTMSK);
+ }
+
+ local_irq_restore(flags);
+}
+
+
+/* from pxafb.c */
+static inline unsigned int chan_to_field(unsigned int chan,
+ struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int s3c2443fb_setcolreg(unsigned regno,
+ unsigned red, unsigned green, unsigned blue,
+ unsigned transp, struct fb_info *info)
+{
+ struct s3c2443fb_info *fbi = info->par;
+ void __iomem *regs = fbi->io;
+ unsigned int val;
+
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ /* true-colour, use pseudo-palette */
+ if (regno < 16) {
+ u32 *pal = info->pseudo_palette;
+
+ val = chan_to_field(red, &info->var.red);
+ val |= chan_to_field(green, &info->var.green);
+ val |= chan_to_field(blue, &info->var.blue);
+
+ pal[regno] = val;
+ }
+ break;
+
+ case FB_VISUAL_PSEUDOCOLOR:
+ if (regno < 256) {
+ /* currently assume RGB 5-6-5 mode */
+
+ val = (red >> 0) & 0xf800;
+ val |= (green >> 5) & 0x07e0;
+ val |= (blue >> 11) & 0x001f;
+
+ writel(val, regs + S3C2410_TFTPAL(regno));
+ schedule_palette_update(fbi, regno, val);
+ }
+
+ break;
+
+ default:
+ return 1; /* unknown type */
+ }
+
+ return 0;
+}
+
+/* These are the available device operations */
+static struct fb_ops s3c2443fb_tft_fbops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = s3c2443_check_var,
+ .fb_set_par = s3c2443fb_set_par,
+ .fb_blank = s3c2443fb_blank,
+ .fb_setcolreg = s3c2443fb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+/*
+ * Allocates the DRAM memory for the frame buffer. This buffer is
+ * remapped into a non-cached, non-buffered, memory region to
+ * allow palette and pixel writes to occur without flushing the
+ * cache. Once this area is remapped, all virtual memory
+ * access to the video memory should occur at the new region.
+ */
+static int __init s3c2443fb_map_video_memory(struct fb_info *info)
+{
+ struct s3c2443fb_info *fbi = info->par;
+ dma_addr_t map_dma;
+ unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
+
+ info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
+ &map_dma, GFP_KERNEL);
+
+ if (info->screen_base) {
+ /* clear screen */
+ memset(info->screen_base, 0x00, map_size);
+ info->fix.smem_start = map_dma;
+ }
+
+ return info->screen_base ? 0 : -ENOMEM;
+}
+
+static inline void modify_gpio(void __iomem *reg,
+ unsigned long set, unsigned long mask)
+{
+ unsigned long tmp;
+
+ tmp = readl(reg) & ~mask;
+ writel(tmp | set, reg);
+}
+
+static int s3c2443fb_init_registers(struct fb_info *info)
+{
+ struct s3c2443fb_info *fbi = info->par;
+ struct s3c2443fb_mach_info *mach_info = fbi->dev->platform_data;
+ unsigned long flags;
+
+ /* modify the gpio(s) with interrupts set (bjd) */
+ local_irq_save(flags);
+ /* Select display type and config the gpios */
+ writel(readl(S3C24XX_MISCCR) | (1 << 28), S3C24XX_MISCCR);
+ modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
+ modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
+ modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
+ modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static inline void s3c2443fb_unmap_video_memory(struct fb_info *info)
+{
+ struct s3c2443fb_info *fbi = info->par;
+
+ dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
+ info->screen_base, info->fix.smem_start);
+}
+
+int __init s3c2443_fb_probe(struct platform_device *pdev)
+{
+ struct s3c2443fb_info *info;
+ struct s3c2443fb_display *display;
+ struct s3c2443fb_mach_info *mach_info;
+ struct fb_info *fbinfo;
+ struct resource *res;
+ int ret, i;
+ char *option = NULL;
+
+ /* Get the user defined LCD controller configuration */
+ mach_info = pdev->dev.platform_data;
+ if (mach_info == NULL) {
+ dev_err(&pdev->dev, "no platform data for lcd driver\n");
+ return -EINVAL;
+ }
+
+ /* Get the display information */
+ if (!mach_info->num_displays) {
+ dev_err(&pdev->dev, "no display information available\n");
+ return -EINVAL;
+ }
+
+ if (mach_info->num_displays > 1) {
+ /*
+ * If there are multiple displays, use the one specified
+ * through the command line parameter vith following
+ * format video=displayfb:<display_name>
+ */
+ if (fb_get_options("displayfb", &option)) {
+ dev_err(&pdev->dev, "no display information available in commnad line\n");
+ return -ENODEV;
+ }
+ if (!option)
+ return -ENODEV;
+
+ printk_debug("display options: %s\n", option);
+ for (i = 0; i < mach_info->num_displays; i++) {
+ if (!strcmp(option, mach_info->displays[i].display_name))
+ mach_info->display = &mach_info->displays[i];
+ }
+ } else {
+ /* If there is only one, that is what we use */
+ mach_info->display = mach_info->displays;
+ }
+
+ if ((display = mach_info->display) == NULL) {
+ dev_err(&pdev->dev, "no display information available\n");
+ return -EINVAL;
+ }
+
+ /* Allocate the frame buffer for this device */
+ fbinfo = framebuffer_alloc(sizeof(struct s3c2443fb_info), &pdev->dev);
+ if (!fbinfo)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, fbinfo);
+
+ /* Use the private data for our internal settings */
+ info = fbinfo->par;
+ info->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory registers\n");
+ ret = -ENXIO;
+ goto dealloc_fb;
+ }
+ info->mem = request_mem_region(res->start,
+ res->end - res->start + 1, pdev->name);
+ if (info->mem == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region\n");
+ ret = -ENOENT;
+ goto dealloc_fb;
+ }
+
+ info->io = ioremap(res->start, res->end - res->start + 1);
+ if (info->io == NULL) {
+ dev_err(&pdev->dev, "ioremap of registers failed\n");
+ ret = -ENXIO;
+ goto release_mem;
+ }
+
+ strcpy(fbinfo->fix.id, DRV_NAME);
+
+ /* Start the initial fix configuration */
+ fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
+ fbinfo->fix.type_aux = 0;
+ fbinfo->fix.xpanstep = 0;
+ fbinfo->fix.ypanstep = 0;
+ fbinfo->fix.ywrapstep = 0;
+ fbinfo->fix.accel = FB_ACCEL_NONE;
+
+ fbinfo->var.nonstd = 0;
+ fbinfo->var.activate = FB_ACTIVATE_NOW;
+ fbinfo->var.accel_flags = 0;
+ fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
+
+ fbinfo->fbops = &s3c2443fb_tft_fbops;
+ fbinfo->flags = FBINFO_FLAG_DEFAULT;
+ fbinfo->pseudo_palette = &info->pseudo_pal;
+
+ for (i = 0; i < 256; i++)
+ info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
+
+ info->clk = clk_get(NULL, mach_info->display->clock_source);
+ if (!info->clk || IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "failed to get lcd clock source %s\n",
+ mach_info->display->clock_source);
+ ret = -ENOENT;
+ goto release_regs;
+ }
+
+ clk_enable(info->clk);
+ msleep(1);
+
+ /* Initialize video memory */
+ fbinfo->var.xres = display->xres;
+ fbinfo->var.yres = display->yres;
+ fbinfo->var.bits_per_pixel = display->bpp;
+ fbinfo->fix.smem_len = display->xres * display->yres * (display->bpp >> 3);
+
+ ret = s3c2443fb_map_video_memory(fbinfo);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to map video memory\n");
+ ret = -ENOMEM;
+ goto release_clock;
+ }
+
+ s3c2443fb_init_registers(fbinfo);
+
+ s3c2443_check_var(&fbinfo->var, fbinfo);
+
+ ret = register_framebuffer(fbinfo);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register framebuffer device\n");
+ goto free_video_memory;
+ }
+
+ s3c2443fb_lcd_enable(info, 1);
+
+ printk(KERN_INFO DRV_NAME ": frame buffer fb%d: %s, display config %s\n",
+ fbinfo->node, fbinfo->fix.id, mach_info->display->display_name);
+ return 0;
+
+free_video_memory:
+ s3c2443fb_unmap_video_memory(fbinfo);
+
+release_clock:
+ clk_disable(info->clk);
+ clk_put(info->clk);
+
+release_regs:
+ iounmap(info->io);
+
+release_mem:
+ release_resource(info->mem);
+ kfree(info->mem);
+
+dealloc_fb:
+ platform_set_drvdata(pdev, NULL);
+ framebuffer_release(fbinfo);
+
+ return ret;
+}
+
+static int s3c2443_fb_remove(struct platform_device *pdev)
+{
+ struct fb_info *fbinfo = platform_get_drvdata(pdev);
+ struct s3c2443fb_info *info = fbinfo->par;
+
+ fbinfo = platform_get_drvdata(pdev);
+ info = fbinfo->par;
+
+ s3c2443fb_lcd_enable(info, 0);
+ unregister_framebuffer(fbinfo);
+ s3c2443fb_unmap_video_memory(fbinfo);
+
+ if (info->clk) {
+ clk_disable(info->clk);
+ clk_put(info->clk);
+ info->clk = NULL;
+ }
+
+ iounmap(info->io);
+ release_resource(info->mem);
+ kfree(info->mem);
+ platform_set_drvdata(pdev, NULL);
+ framebuffer_release(fbinfo);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/* suspend and resume support for the lcd controller */
+static int s3c2443_fb_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct fb_info *fbinfo = platform_get_drvdata(dev);
+ struct s3c2443fb_info *info = fbinfo->par;
+
+ if (state.event == PM_EVENT_SUSPEND) {
+ s3c2443fb_lcd_enable(info, 0);
+ /* sleep before disabling the clock, we need to ensure
+ * the LCD DMA engine is not going to get back on the bus
+ * before the clock goes off again (bjd) */
+ msleep(1);
+ clk_disable(info->clk);
+ }
+
+ return 0;
+}
+
+static int s3c2443_fb_resume(struct platform_device *dev)
+{
+ struct fb_info *fbinfo = platform_get_drvdata(dev);
+ struct s3c2443fb_info *info = fbinfo->par;
+
+ clk_enable(info->clk);
+ msleep(1);
+ s3c2443fb_init_registers(fbinfo);
+ s3c2443_check_var(&fbinfo->var, fbinfo);
+ s3c2443fb_activate_var(fbinfo);
+ s3c2443fb_lcd_enable(info, 1);
+
+ return 0;
+}
+#else
+#define s3c2443_fb_suspend NULL
+#define s3c2443_fb_resume NULL
+#endif
+
+static struct platform_driver s3c2443_fb_driver = {
+ .probe = s3c2443_fb_probe,
+ .remove = s3c2443_fb_remove,
+ .suspend = s3c2443_fb_suspend,
+ .resume = s3c2443_fb_resume,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+int __devinit s3c2443_fb_init(void)
+{
+ return platform_driver_register(&s3c2443_fb_driver);
+}
+
+static void __exit s3c2443_fb_cleanup(void)
+{
+ platform_driver_unregister(&s3c2443_fb_driver);
+}
+
+module_init(s3c2443_fb_init);
+module_exit(s3c2443_fb_cleanup);
+
+MODULE_AUTHOR("Luis Galdos & Pedro Perez de Heredia");
+MODULE_DESCRIPTION("Framebuffer driver for the S3C2443");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/video/stmp37xxfb.c b/drivers/video/stmp37xxfb.c
new file mode 100644
index 000000000000..bf058ae3e097
--- /dev/null
+++ b/drivers/video/stmp37xxfb.c
@@ -0,0 +1,844 @@
+/*
+ * Freescale STMP37XX/STMP378X framebuffer driver
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <linux/cpufreq.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-pinctrl.h>
+#include <mach/regs-lcdif.h>
+#include <mach/regs-clkctrl.h>
+#include <mach/regs-apbh.h>
+#include <mach/lcdif.h>
+
+#include <mach/stmp3xxx.h>
+
+#define NUM_SCREENS 1
+
+struct stmp3xxx_fb_data {
+ struct fb_info info;
+ struct stmp3xxx_platform_fb_data *pdata;
+ int is_blank;
+ ssize_t mem_size;
+ ssize_t map_size;
+ dma_addr_t phys_start;
+ dma_addr_t cur_phys;
+ int dma_irq;
+ int err_irq;
+ void *virt_start;
+ struct device *dev;
+ wait_queue_head_t vsync_wait_q;
+ u32 vsync_count;
+ void *par;
+};
+
+/* forward declaration */
+static int stmp3xxxfb_blank(int blank, struct fb_info *info);
+static unsigned char *default_panel_name;
+static struct stmp3xxx_fb_data *cdata;
+
+static irqreturn_t lcd_irq_handler(int irq, void *dev_id)
+{
+ struct stmp3xxx_fb_data *data = dev_id;
+ u32 status_lcd = HW_LCDIF_CTRL1_RD();
+ u32 status_apbh = HW_APBH_CTRL1_RD();
+ pr_debug("%s: irq %d\n", __func__, irq);
+
+ if (status_apbh & BM_APBH_CTRL1_CH0_CMDCMPLT_IRQ)
+ HW_APBH_CTRL1_CLR(BM_APBH_CTRL1_CH0_CMDCMPLT_IRQ);
+
+ if (status_lcd & BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ) {
+ pr_debug("%s: VSYNC irq\n", __func__);
+ data->vsync_count++;
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ);
+ wake_up_interruptible(&data->vsync_wait_q);
+ }
+ if (status_lcd & BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ) {
+ pr_debug("%s: frame done irq\n", __func__);
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ);
+ data->vsync_count++;
+ }
+ if (status_lcd & BM_LCDIF_CTRL1_UNDERFLOW_IRQ) {
+ pr_debug("%s: underflow irq\n", __func__);
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_UNDERFLOW_IRQ);
+ }
+ if (status_lcd & BM_LCDIF_CTRL1_OVERFLOW_IRQ) {
+ pr_debug("%s: overflow irq\n", __func__);
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_OVERFLOW_IRQ);
+ }
+ return IRQ_HANDLED;
+}
+
+static struct fb_var_screeninfo stmp3xxxfb_default __devinitdata = {
+ .activate = FB_ACTIVATE_TEST,
+ .height = -1,
+ .width = -1,
+ .pixclock = 20000,
+ .left_margin = 64,
+ .right_margin = 64,
+ .upper_margin = 32,
+ .lower_margin = 32,
+ .hsync_len = 64,
+ .vsync_len = 2,
+ .vmode = FB_VMODE_NONINTERLACED,
+};
+
+static struct fb_fix_screeninfo stmp3xxxfb_fix __devinitdata = {
+ .id = "stmp3xxxfb",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+ .accel = FB_ACCEL_NONE,
+};
+
+int stmp3xxxfb_get_info(struct fb_var_screeninfo *var,
+ struct fb_fix_screeninfo *fix)
+{
+ if (!cdata)
+ return -ENODEV;
+
+ *var = cdata->info.var;
+ *fix = cdata->info.fix;
+ return 0;
+}
+
+void stmp3xxxfb_cfg_pxp(int enable, dma_addr_t pxp_phys)
+{
+ if (enable)
+ cdata->pdata->cur->pan_display(pxp_phys);
+ else
+ cdata->pdata->cur->pan_display(cdata->cur_phys);
+}
+
+static int stmp3xxxfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct stmp3xxx_fb_data *data = (struct stmp3xxx_fb_data *)info;
+
+ unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+
+ if (off < info->fix.smem_len)
+ return dma_mmap_writecombine(data->dev, vma,
+ data->virt_start,
+ data->phys_start,
+ info->fix.smem_len);
+ else
+ return -EINVAL;
+}
+
+static int stmp3xxxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+{
+ if (regno >= 256) /* no. of hw registers */
+ return 1;
+ /*
+ * Program hardware... do anything you want with transp
+ */
+
+ /* grayscale works only partially under directcolor */
+ if (info->var.grayscale) {
+ /* grayscale = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue =
+ (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+
+ /* Directcolor:
+ * var->{color}.offset contains start of bitfield
+ * var->{color}.length contains length of bitfield
+ * {hardwarespecific} contains width of RAMDAC
+ * cmap[X] is programmed to
+ * (X << red.offset) | (X << green.offset) | (X << blue.offset)
+ * RAMDAC[X] is programmed to (red, green, blue)
+ *
+ * Pseudocolor:
+ * uses offset = 0 && length = RAMDAC register width.
+ * var->{color}.offset is 0
+ * var->{color}.length contains widht of DAC
+ * cmap is not used
+ * RAMDAC[X] is programmed to (red, green, blue)
+ * Truecolor:
+ * does not use DAC. Usually 3 are present.
+ * var->{color}.offset contains start of bitfield
+ * var->{color}.length contains length of bitfield
+ * cmap is programmed to
+ * (red << red.offset) | (green << green.offset) |
+ * (blue << blue.offset) | (transp << transp.offset)
+ * RAMDAC does not exist
+ */
+#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ red = CNVT_TOHW(red, info->var.red.length);
+ green = CNVT_TOHW(green, info->var.green.length);
+ blue = CNVT_TOHW(blue, info->var.blue.length);
+ transp = CNVT_TOHW(transp, info->var.transp.length);
+ break;
+ case FB_VISUAL_DIRECTCOLOR:
+ red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */
+ green = CNVT_TOHW(green, 8);
+ blue = CNVT_TOHW(blue, 8);
+ /* hey, there is bug in transp handling... */
+ transp = CNVT_TOHW(transp, 8);
+ break;
+ }
+#undef CNVT_TOHW
+ /* Truecolor has hardware independent palette */
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+
+ if (regno >= 16)
+ return 1;
+
+ ((u32 *) (info->pseudo_palette))[regno] =
+ (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+ }
+ return 0;
+}
+
+static inline u_long get_line_length(int xres_virtual, int bpp)
+{
+ u_long length;
+
+ length = xres_virtual * bpp;
+ length = (length + 31) & ~31;
+ length >>= 3;
+ return length;
+}
+
+static int get_matching_pentry(struct stmp3xxx_platform_fb_entry *pentry,
+ void *data, int ret_prev)
+{
+ struct fb_var_screeninfo *info = data;
+ pr_debug("%s: %d:%d:%d vs %d:%d:%d\n", __func__,
+ pentry->x_res, pentry->y_res, pentry->bpp,
+ info->yres, info->xres, info->bits_per_pixel);
+ if (pentry->x_res == info->yres && pentry->y_res == info->xres &&
+ pentry->bpp == info->bits_per_pixel)
+ ret_prev = (int)pentry;
+ return ret_prev;
+}
+
+static int get_matching_pentry_by_name(
+ struct stmp3xxx_platform_fb_entry *pentry,
+ void *data,
+ int ret_prev)
+{
+ unsigned char *name = data;
+ if (!strcmp(pentry->name, name))
+ ret_prev = (int)pentry;
+ return ret_prev;
+}
+
+/*
+ * This routine actually sets the video mode. It's in here where we
+ * the hardware state info->par and fix which can be affected by the
+ * change in par. For this driver it doesn't do much.
+ *
+ * XXX: REVISIT
+ */
+static int stmp3xxxfb_set_par(struct fb_info *info)
+{
+ struct stmp3xxx_fb_data *data = (struct stmp3xxx_fb_data *)info;
+ struct stmp3xxx_platform_fb_data *pdata = data->pdata;
+ struct stmp3xxx_platform_fb_entry *pentry;
+ pentry = (void *)stmp3xxx_lcd_iterate_pdata(pdata,
+ get_matching_pentry,
+ &info->var);
+
+ dev_dbg(data->dev, "%s: xres %d, yres %d, bpp %d\n",
+ __func__,
+ info->var.xres,
+ info->var.yres,
+ info->var.bits_per_pixel);
+ if (!pentry)
+ return -EINVAL;
+
+ info->fix.line_length = get_line_length(info->var.xres_virtual,
+ info->var.bits_per_pixel);
+
+ if (pentry == pdata->cur || !pdata->cur)
+ return 0;
+
+ /* release prev panel */
+ stmp3xxxfb_blank(FB_BLANK_POWERDOWN, &data->info);
+ if (pdata->cur->stop_panel)
+ pdata->cur->stop_panel();
+ pdata->cur->release_panel(data->dev, pdata->cur);
+
+ info->fix.smem_len = pentry->y_res * pentry->x_res * pentry->bpp / 8;
+ info->screen_size = info->fix.smem_len;
+ memset((void *)info->screen_base, 0, info->screen_size);
+
+ /* init next panel */
+ pdata->cur = pentry;
+ stmp3xxx_init_lcdif();
+ pentry->init_panel(data->dev, data->phys_start, info->fix.smem_len,
+ pentry);
+ pentry->run_panel();
+ stmp3xxxfb_blank(FB_BLANK_UNBLANK, &data->info);
+
+ return 0;
+}
+
+static int stmp3xxxfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ u32 line_length;
+ struct stmp3xxx_fb_data *data = (struct stmp3xxx_fb_data *)info;
+ struct stmp3xxx_platform_fb_data *pdata = data->pdata;
+
+ /*
+ * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
+ * as FB_VMODE_SMOOTH_XPAN is only used internally
+ */
+
+ if (var->vmode & FB_VMODE_CONUPDATE) {
+ var->vmode |= FB_VMODE_YWRAP;
+ var->xoffset = info->var.xoffset;
+ var->yoffset = info->var.yoffset;
+ }
+
+ pr_debug("%s: xres %d, yres %d, bpp %d\n", __func__,
+ var->xres, var->yres, var->bits_per_pixel);
+ /*
+ * Some very basic checks
+ */
+ if (!var->xres)
+ var->xres = 1;
+ if (!var->yres)
+ var->yres = 1;
+ if (var->xres > var->xres_virtual)
+ var->xres_virtual = var->xres;
+ if (var->yres > var->yres_virtual)
+ var->yres_virtual = var->yres;
+
+ if (var->xres_virtual < var->xoffset + var->xres)
+ var->xres_virtual = var->xoffset + var->xres;
+ if (var->yres_virtual < var->yoffset + var->yres)
+ var->yres_virtual = var->yoffset + var->yres;
+
+ line_length = get_line_length(var->xres_virtual, var->bits_per_pixel);
+ dev_dbg(data->dev,
+ "line_length %d, var->yres_virtual %d, data->mem_size %d\n",
+ line_length, var->yres_virtual, data->mem_size);
+ if (line_length * var->yres_virtual > data->map_size)
+ return -ENOMEM;
+
+ if (!stmp3xxx_lcd_iterate_pdata(pdata, get_matching_pentry, var))
+ return -EINVAL;
+
+ if (var->bits_per_pixel == 16) {
+ /* RGBA 5551 */
+ if (var->transp.length) {
+ var->red.offset = 0;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->blue.offset = 10;
+ var->blue.length = 5;
+ var->transp.offset = 15;
+ var->transp.length = 1;
+ } else { /* RGB 565 */
+ var->red.offset = 0;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 11;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ }
+ } else {
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ }
+
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+
+ return 0;
+}
+
+
+static int stmp3xxxfb_wait_for_vsync(u32 channel, struct fb_info *info)
+{
+ struct stmp3xxx_fb_data *data = (struct stmp3xxx_fb_data *)info;
+ u32 count = data->vsync_count;
+ int ret = 0;
+
+ HW_LCDIF_CTRL1_SET(BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ_EN);
+ ret = wait_event_interruptible_timeout(data->vsync_wait_q,
+ count != data->vsync_count, HZ / 10);
+ HW_LCDIF_CTRL1_CLR(BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ_EN);
+ if (!ret) {
+ dev_err(data->dev, "wait for vsync timed out\n");
+ ret = -ETIMEDOUT;
+ }
+ return ret;
+}
+
+static int stmp3xxxfb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ u32 channel = 0;
+ int ret = -EINVAL;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ if (!get_user(channel, (__u32 __user *) arg))
+ ret = stmp3xxxfb_wait_for_vsync(channel, info);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int stmp3xxxfb_blank(int blank, struct fb_info *info)
+{
+ struct stmp3xxx_fb_data *data = (struct stmp3xxx_fb_data *)info;
+ int ret = data->pdata->cur->blank_panel ?
+ data->pdata->cur->blank_panel(blank) :
+ -ENOTSUPP;
+ if (ret == 0)
+ data->is_blank = (blank != FB_BLANK_UNBLANK);
+ return ret;
+}
+
+static int stmp3xxxfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct stmp3xxx_fb_data *data = (struct stmp3xxx_fb_data *)info;
+ int ret = 0;
+
+ pr_debug("%s: var->xoffset %d, info->var.xoffset %d\n",
+ __func__, var->xoffset, info->var.xoffset);
+ /* check if var is valid; also, xpan is not supported */
+ if (!var || (var->xoffset != info->var.xoffset) ||
+ (var->yoffset + var->yres > var->yres_virtual)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!data->pdata->cur->pan_display) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* update framebuffer visual */
+ data->cur_phys = data->phys_start +
+ info->fix.line_length * var->yoffset;
+ data->pdata->cur->pan_display(data->cur_phys);
+out:
+ return ret;
+}
+
+static struct fb_ops stmp3xxxfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = stmp3xxxfb_check_var,
+ .fb_set_par = stmp3xxxfb_set_par,
+ .fb_mmap = stmp3xxxfb_mmap,
+ .fb_setcolreg = stmp3xxxfb_setcolreg,
+ .fb_ioctl = stmp3xxxfb_ioctl,
+ .fb_blank = stmp3xxxfb_blank,
+ .fb_pan_display = stmp3xxxfb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static void init_timings(struct stmp3xxx_fb_data *data)
+{
+ unsigned phase_time;
+ unsigned timings;
+
+ /* Just use a phase_time of 1. As optimal as it gets, now. */
+ phase_time = 1;
+
+ /* Program all 4 timings the same */
+ timings = phase_time;
+ timings |= timings << 8;
+ timings |= timings << 16;
+ HW_LCDIF_TIMING_WR(timings);
+}
+
+#ifdef CONFIG_CPU_FREQ
+
+struct stmp3xxxfb_notifier_block {
+ struct stmp3xxx_fb_data *fb_data;
+ struct notifier_block nb;
+};
+
+static int stmp3xxxfb_notifier(struct notifier_block *self,
+ unsigned long phase, void *p)
+{
+ struct stmp3xxxfb_notifier_block *block =
+ container_of(self, struct stmp3xxxfb_notifier_block, nb);
+ struct stmp3xxx_fb_data *data = block->fb_data;
+
+ switch (phase) {
+ case CPUFREQ_POSTCHANGE:
+ stmp3xxxfb_blank(FB_BLANK_UNBLANK, &data->info);
+ break;
+
+ case CPUFREQ_PRECHANGE:
+ stmp3xxxfb_blank(FB_BLANK_POWERDOWN, &data->info);
+ break;
+
+ default:
+ dev_dbg(data->dev, "didn't handle notify %ld\n", phase);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct stmp3xxxfb_notifier_block stmp3xxxfb_nb = {
+ .nb = {
+ .notifier_call = stmp3xxxfb_notifier,
+ },
+};
+#endif /* CONFIG_CPU_FREQ */
+
+static int get_max_memsize(struct stmp3xxx_platform_fb_entry *pentry,
+ void *data, int ret_prev)
+{
+ struct stmp3xxx_fb_data *fbdata = data;
+ int sz = pentry->x_res * pentry->y_res * pentry->bpp / 8;
+ fbdata->mem_size = sz < ret_prev ? ret_prev : sz;
+ pr_debug("%s: mem_size now %d\n", __func__, fbdata->mem_size);
+ return fbdata->mem_size;
+}
+
+static int __devinit stmp3xxxfb_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct stmp3xxx_fb_data *data;
+ struct resource *res;
+ struct fb_info *info;
+ struct stmp3xxx_platform_fb_data *pdata = pdev->dev.platform_data;
+ struct stmp3xxx_platform_fb_entry *pentry;
+
+ if (pdata == NULL) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (default_panel_name) {
+ pentry = (void *)stmp3xxx_lcd_iterate_pdata(pdata,
+ get_matching_pentry_by_name,
+ default_panel_name);
+ if (pentry) {
+ stmp3xxx_lcd_move_pentry_up(pentry, pdata);
+ pdata->cur = pentry;
+ }
+ }
+ if (!default_panel_name || !pentry)
+ pentry = pdata->cur;
+ if (!pentry || !pentry->init_panel || !pentry->run_panel ||
+ !pentry->release_panel) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ data = (struct stmp3xxx_fb_data *)framebuffer_alloc(
+ sizeof(struct stmp3xxx_fb_data) +
+ sizeof(u32) * 256 -
+ sizeof(struct fb_info), &pdev->dev);
+ if (data == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cdata = data;
+ data->dev = &pdev->dev;
+ data->pdata = pdata;
+ platform_set_drvdata(pdev, data);
+ info = &data->info;
+
+ dev_dbg(&pdev->dev, "resolution %dx%d, bpp %d\n", pentry->x_res,
+ pentry->y_res, pentry->bpp);
+
+ stmp3xxx_lcd_iterate_pdata(pdata, get_max_memsize, data);
+
+ data->map_size = PAGE_ALIGN(data->mem_size) * NUM_SCREENS;
+ dev_dbg(&pdev->dev, "memory to allocate: %d\n", data->map_size);
+
+ data->virt_start = dma_alloc_writecombine(&pdev->dev,
+ data->map_size,
+ &data->phys_start,
+ GFP_KERNEL);
+
+ if (data->virt_start == NULL) {
+ ret = -ENOMEM;
+ goto out_dma;
+ }
+ dev_dbg(&pdev->dev, "allocated at %p:0x%x\n", data->virt_start,
+ data->phys_start);
+
+ stmp3xxxfb_default.bits_per_pixel = pentry->bpp;
+ /* NB: rotated */
+ stmp3xxxfb_default.xres = pentry->y_res;
+ stmp3xxxfb_default.yres = pentry->x_res;
+ stmp3xxxfb_default.xres_virtual = pentry->y_res;
+ stmp3xxxfb_default.yres_virtual = data->map_size /
+ (pentry->y_res * pentry->bpp / 8);
+ if (stmp3xxxfb_default.yres_virtual >= stmp3xxxfb_default.yres * 2)
+ stmp3xxxfb_default.yres_virtual = stmp3xxxfb_default.yres * 2;
+ else
+ stmp3xxxfb_default.yres_virtual = stmp3xxxfb_default.yres;
+
+ stmp3xxxfb_fix.smem_start = data->phys_start;
+ stmp3xxxfb_fix.smem_len = pentry->y_res * pentry->x_res * pentry->bpp /
+ 8;
+ stmp3xxxfb_fix.ypanstep = 1;
+
+ switch (pentry->bpp) {
+ case 32:
+ case 24:
+ stmp3xxxfb_default.red.offset = 16;
+ stmp3xxxfb_default.red.length = 8;
+ stmp3xxxfb_default.green.offset = 8;
+ stmp3xxxfb_default.green.length = 8;
+ stmp3xxxfb_default.blue.offset = 0;
+ stmp3xxxfb_default.blue.length = 8;
+ break;
+
+ case 16:
+ stmp3xxxfb_default.red.offset = 11;
+ stmp3xxxfb_default.red.length = 5;
+ stmp3xxxfb_default.green.offset = 5;
+ stmp3xxxfb_default.green.length = 6;
+ stmp3xxxfb_default.blue.offset = 0;
+ stmp3xxxfb_default.blue.length = 5;
+ break;
+
+ default:
+ dev_err(&pdev->dev, "unsupported bitwidth %d\n", pentry->bpp);
+ ret = -EINVAL;
+ goto out_dma;
+ }
+
+ info->screen_base = data->virt_start;
+ info->fbops = &stmp3xxxfb_ops;
+
+ ret = fb_find_mode(&info->var, info, NULL, NULL, 0, NULL, 8);
+
+ if (!ret || (ret == 4))
+ info->var = stmp3xxxfb_default;
+ info->fix = stmp3xxxfb_fix;
+ info->pseudo_palette = &data->par;
+ data->par = NULL;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ init_waitqueue_head(&data->vsync_wait_q);
+ data->vsync_count = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "cannot get IRQ resource\n");
+ ret = -ENODEV;
+ goto out_dma;
+ }
+ data->dma_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "cannot get IRQ resource\n");
+ ret = -ENODEV;
+ goto out_dma;
+ }
+ data->err_irq = res->start;
+
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret)
+ goto out_cmap;
+
+ stmp3xxx_init_lcdif();
+ ret = pentry->init_panel(data->dev, data->phys_start,
+ stmp3xxxfb_fix.smem_len, pentry);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot initialize LCD panel\n");
+ goto out_panel;
+ }
+ dev_dbg(&pdev->dev, "LCD panel initialized\n");
+ init_timings(data);
+
+ ret = request_irq(data->dma_irq, lcd_irq_handler, 0, "fb_dma", data);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",
+ data->dma_irq, ret);
+ goto out_panel;
+ }
+ ret = request_irq(data->err_irq, lcd_irq_handler, 0, "fb_error", data);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",
+ data->err_irq, ret);
+ goto out_irq;
+ }
+ ret = register_framebuffer(info);
+ if (ret)
+ goto out_register;
+
+ pentry->run_panel();
+ dev_dbg(&pdev->dev, "LCD DMA channel has been started\n");
+ data->cur_phys = data->phys_start;
+ dev_dbg(&pdev->dev, "LCD running now\n");
+
+#ifdef CONFIG_CPU_FREQ
+ stmp3xxxfb_nb.fb_data = data;
+ cpufreq_register_notifier(&stmp3xxxfb_nb.nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+#endif /* CONFIG_CPU_FREQ */
+
+ goto out;
+
+out_register:
+ free_irq(data->err_irq, data);
+out_irq:
+ free_irq(data->dma_irq, data);
+out_panel:
+ fb_dealloc_cmap(&info->cmap);
+out_cmap:
+ dma_free_writecombine(&pdev->dev, data->map_size, data->virt_start,
+ data->phys_start);
+out_dma:
+ kfree(data);
+out:
+ return ret;
+}
+
+static int stmp3xxxfb_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_fb_data *data = platform_get_drvdata(pdev);
+ struct stmp3xxx_platform_fb_data *pdata = pdev->dev.platform_data;
+ struct stmp3xxx_platform_fb_entry *pentry = pdata->cur;
+
+ stmp3xxxfb_blank(FB_BLANK_POWERDOWN, &data->info);
+ if (pentry->stop_panel)
+ pentry->stop_panel();
+ pentry->release_panel(&pdev->dev, pentry);
+
+ unregister_framebuffer(&data->info);
+ framebuffer_release(&data->info);
+ fb_dealloc_cmap(&data->info.cmap);
+ free_irq(data->dma_irq, data);
+ free_irq(data->err_irq, data);
+ dma_free_writecombine(&pdev->dev, data->map_size, data->virt_start,
+ data->phys_start);
+ kfree(data);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stmp3xxxfb_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct stmp3xxx_fb_data *data = platform_get_drvdata(pdev);
+ struct stmp3xxx_platform_fb_data *pdata = pdev->dev.platform_data;
+ struct stmp3xxx_platform_fb_entry *pentry = pdata->cur;
+ int ret;
+
+ ret = stmp3xxxfb_blank(FB_BLANK_POWERDOWN, &data->info);
+ if (ret)
+ goto out;
+ if (pentry->stop_panel)
+ pentry->stop_panel();
+ pentry->release_panel(data->dev, pentry);
+
+out:
+ return ret;
+}
+
+static int stmp3xxxfb_resume(struct platform_device *pdev)
+{
+ struct stmp3xxx_fb_data *data = platform_get_drvdata(pdev);
+ struct stmp3xxx_platform_fb_data *pdata = pdev->dev.platform_data;
+ struct stmp3xxx_platform_fb_entry *pentry = pdata->cur;
+
+ stmp3xxx_init_lcdif();
+ init_timings(data);
+ pentry->init_panel(data->dev, data->phys_start, data->info.fix.smem_len,
+ pentry);
+ pentry->run_panel();
+ stmp3xxxfb_blank(FB_BLANK_UNBLANK, &data->info);
+ return 0;
+}
+#else
+#define stmp3xxxfb_suspend NULL
+#define stmp3xxxfb_resume NULL
+#endif
+
+static struct platform_driver stmp3xxxfb_driver = {
+ .probe = stmp3xxxfb_probe,
+ .remove = stmp3xxxfb_remove,
+ .suspend = stmp3xxxfb_suspend,
+ .resume = stmp3xxxfb_resume,
+ .driver = {
+ .name = "stmp3xxx-fb",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmp3xxxfb_init(void)
+{
+ return platform_driver_register(&stmp3xxxfb_driver);
+}
+
+static void __exit stmp3xxxfb_exit(void)
+{
+ platform_driver_unregister(&stmp3xxxfb_driver);
+}
+
+module_init(stmp3xxxfb_init);
+module_exit(stmp3xxxfb_exit);
+
+/*
+ * LCD panel select
+ */
+static int __init default_panel_select(char *str)
+{
+ default_panel_name = str;
+ return 0;
+}
+__setup("lcd_panel=", default_panel_select);
+
+MODULE_AUTHOR("Vitaly Wool <vital@embeddedalley.com>");
+MODULE_DESCRIPTION("STMP3xxx Framebuffer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig
index 90616822cd20..f75d0d10c21f 100644
--- a/drivers/w1/masters/Kconfig
+++ b/drivers/w1/masters/Kconfig
@@ -34,6 +34,12 @@ config W1_MASTER_DS2482
This driver can also be built as a module. If so, the module
will be called ds2482.
+config W1_MASTER_MXC
+ tristate "Freescale MXC driver for 1-wire"
+ depends on W1 && ARCH_MXC
+ help
+ Say Y here to enable MXC 1-wire host
+
config W1_MASTER_DS1WM
tristate "Maxim DS1WM 1-wire busmaster"
depends on W1 && ARM && HAVE_CLK
diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile
index bc4714a75f3a..c5a3e96fcbab 100644
--- a/drivers/w1/masters/Makefile
+++ b/drivers/w1/masters/Makefile
@@ -5,6 +5,8 @@
obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o
obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o
obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o
+obj-$(CONFIG_W1_MASTER_MXC) += mxc_w1.o
+
obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o
obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o
obj-$(CONFIG_HDQ_MASTER_OMAP) += omap_hdq.o
diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c
new file mode 100644
index 000000000000..7c0e4a10d23d
--- /dev/null
+++ b/drivers/w1/masters/mxc_w1.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup MXC_OWIRE MXC Driver for owire interface
+ */
+
+/*!
+ * @file mxc_w1.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC owire interface.
+ *
+ *
+ * @ingroup MXC_OWIRE
+ */
+
+/*!
+ * Include Files
+ */
+
+#include <asm/atomic.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <mach/hardware.h>
+#include <asm/setup.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_log.h"
+
+/*
+ * mxc function declarations
+ */
+
+static int __devinit mxc_w1_probe(struct platform_device *pdev);
+static int __devexit mxc_w1_remove(struct platform_device *pdev);
+static DECLARE_COMPLETION(transmit_done);
+extern void gpio_owire_active(void);
+extern void gpio_owire_inactive(void);
+
+/*
+ * MXC W1 Register offsets
+ */
+#define MXC_W1_CONTROL 0x00
+#define MXC_W1_TIME_DIVIDER 0x02
+#define MXC_W1_RESET 0x04
+#define MXC_W1_COMMAND 0x06
+#define MXC_W1_TXRX 0x08
+#define MXC_W1_INTERRUPT 0x0A
+#define MXC_W1_INTERRUPT_EN 0x0C
+DEFINE_SPINLOCK(w1_lock);
+
+/*!
+ * This structure contains pointers to callback functions.
+ */
+static struct platform_driver mxc_w1_driver = {
+ .driver = {
+ .name = "mxc_w1",
+ },
+ .probe = mxc_w1_probe,
+ .remove = mxc_w1_remove,
+};
+
+/*!
+ * This structure is used to store
+ * information specific to w1 module.
+ */
+
+struct mxc_w1_device {
+ char *base_address;
+ unsigned long found;
+ unsigned int clkdiv;
+ struct clk *clk;
+ struct w1_bus_master *bus_master;
+};
+
+/*
+ * this is the low level routine to
+ * reset the device on the One Wire interface
+ * on the hardware
+ * @param data the data field of the w1 device structure
+ * @return the function returns 0 when the reset pulse has
+ * been generated
+ */
+static u8 mxc_w1_ds2_reset_bus(void *data)
+{
+ volatile u8 reg_val;
+ u8 ret;
+ struct mxc_w1_device *dev = (struct mxc_w1_device *)data;
+
+ __raw_writeb(0x80, (dev->base_address + MXC_W1_CONTROL));
+
+ do {
+ reg_val = __raw_readb(dev->base_address + MXC_W1_CONTROL);
+ } while (((reg_val >> 7) & 0x1) != 0);
+ ret = ((reg_val >> 7) & 0x1);
+ return ret;
+}
+
+/*!
+ * this is the low level routine to read/write a bit on the One Wire
+ * interface on the hardware
+ * @param data the data field of the w1 device structure
+ * @param bit 0 = write-0 cycle, 1 = write-1/read cycle
+ * @return the function returns the bit read (0 or 1)
+ */
+static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit)
+{
+
+ volatile u8 reg_val;
+ struct mxc_w1_device *dev = (struct mxc_w1_device *)data;
+ u8 ret = 0;
+
+ if (0 == bit) {
+ __raw_writeb((1 << 5), (dev->base_address + MXC_W1_CONTROL));
+
+ do {
+ reg_val =
+ __raw_readb(dev->base_address + MXC_W1_CONTROL);
+ } while (0 != ((reg_val >> 5) & 0x1));
+ }
+
+ else {
+ __raw_writeb((1 << 4), dev->base_address + MXC_W1_CONTROL);
+ do {
+ reg_val =
+ __raw_readb(dev->base_address + MXC_W1_CONTROL);
+ } while (0 != ((reg_val >> 4) & 0x1));
+
+ reg_val =
+ (((__raw_readb(dev->base_address + MXC_W1_CONTROL)) >> 3) &
+ 0x1);
+ ret = (u8) (reg_val);
+ }
+
+ return ret;
+}
+
+static void mxc_w1_ds2_write_byte(void *data, u8 byte)
+{
+ struct mxc_w1_device *dev = (struct mxc_w1_device *)data;
+ INIT_COMPLETION(transmit_done);
+ __raw_writeb(byte, (dev->base_address + MXC_W1_TXRX));
+ __raw_writeb(0x10, (dev->base_address + MXC_W1_INTERRUPT_EN));
+ wait_for_completion(&transmit_done);
+}
+static u8 mxc_w1_ds2_read_byte(void *data)
+{
+ volatile u8 reg_val;
+ struct mxc_w1_device *dev = (struct mxc_w1_device *)data;
+ mxc_w1_ds2_write_byte(data, 0xFF);
+ reg_val = __raw_readb((dev->base_address + MXC_W1_TXRX));
+ return reg_val;
+}
+static u8 mxc_w1_read_byte(void *data)
+{
+ volatile u8 reg_val;
+ struct mxc_w1_device *dev = (struct mxc_w1_device *)data;
+ reg_val = __raw_readb((dev->base_address + MXC_W1_TXRX));
+ return reg_val;
+}
+static irqreturn_t w1_interrupt_handler(int irq, void *data)
+{
+ u8 reg_val;
+ irqreturn_t ret = IRQ_NONE;
+ struct mxc_w1_device *dev = (struct mxc_w1_device *)data;
+ reg_val = __raw_readb((dev->base_address + MXC_W1_INTERRUPT));
+ if ((reg_val & 0x10)) {
+ complete(&transmit_done);
+ reg_val = __raw_readb((dev->base_address + MXC_W1_TXRX));
+ ret = IRQ_HANDLED;
+ }
+ return ret;
+}
+void search_ROM_accelerator(void *data, struct w1_master *master, u8 search_type,
+ w1_slave_found_callback cb)
+{
+ u64 rn[2], last_rn[2], rn2[2];
+ u64 rn1, rom_id, temp, temp1;
+ int i, j, z, w, last_zero, loop;
+ u8 bit, reg_val, bit2;
+ u8 byte, byte1;
+ int disc, prev_disc, last_disc;
+ struct mxc_w1_device *dev = (struct mxc_w1_device *)data;
+ last_rn[0] = 0;
+ last_rn[1] = 0;
+ rom_id = 0;
+ prev_disc = 0;
+ loop = 0;
+ disc = -1;
+ last_disc = 0;
+ last_zero = 0;
+ while (!last_zero) {
+ /*
+ * Reset bus and all 1-wire device state machines
+ * so they can respond to our requests.
+ *
+ * Return 0 - device(s) present, 1 - no devices present.
+ */
+ if (mxc_w1_ds2_reset_bus(data)) {
+ pr_debug("No devices present on the wire.\n");
+ break;
+ }
+ rn[0] = 0;
+ rn[1] = 0;
+ __raw_writeb(0x80, (dev->base_address + MXC_W1_CONTROL));
+ mdelay(1);
+ mxc_w1_ds2_write_byte(data, 0xF0);
+ __raw_writeb(0x02, (dev->base_address + MXC_W1_COMMAND));
+ memcpy(rn2, last_rn, 16);
+ z = 0;
+ w = 0;
+ for (i = 0; i < 16; i++) {
+ reg_val = rn2[z] >> (8 * w);
+ mxc_w1_ds2_write_byte(data, reg_val);
+ reg_val = mxc_w1_read_byte(data);
+ if ((reg_val & 0x3) == 0x3) {
+ pr_debug("Device is Not Responding\n");
+ break;
+ }
+ for (j = 0; j < 8; j += 2) {
+ byte = 0xFF;
+ byte1 = 1;
+ byte ^= byte1 << j;
+ bit = (reg_val >> j) & 0x1;
+ bit2 = (reg_val >> j);
+ if (bit) {
+ prev_disc = disc;
+ disc = 8 * i + j;
+ reg_val &= byte;
+ }
+ }
+ rn1 = 0;
+ rn1 = reg_val;
+ rn[z] |= rn1 << (8 * w);
+ w++;
+ if (i == 7) {
+ z++;
+ w = 0;
+ }
+ }
+ if ((disc == -1) || (disc == prev_disc))
+ last_zero = 1;
+ if (disc == last_disc)
+ disc = prev_disc;
+ z = 0;
+ rom_id = 0;
+ for (i = 0, j = 1; i < 64; i++) {
+ temp = 0;
+ temp = (rn[z] >> j) & 0x1;
+ rom_id |= (temp << i);
+ j += 2;
+ if (i == 31) {
+ z++;
+ j = 1;
+ }
+
+ }
+ if (disc > 63) {
+ last_rn[0] = rn[0];
+ temp1 = rn[1];
+ loop = disc % 64;
+ temp = 1;
+ temp1 |= (temp << (loop + 1)) - 1;
+ temp1 |= (temp << (loop + 1));
+ last_rn[1] = temp1;
+
+ } else {
+ last_rn[1] = 0;
+ temp1 = rn[0];
+ temp = 1;
+ temp1 |= (temp << (loop + 1)) - 1;
+ temp1 |= (temp << (loop + 1));
+ last_rn[0] = temp1;
+ }
+ last_disc = disc;
+ cb(master, rom_id);
+ }
+}
+
+/*!
+ * this routine sets the One Wire clock
+ * to a value of 1 Mhz, as required by
+ * hardware.
+ * @param dev the device structure for w1
+ * @return The function returns void
+ */
+static void mxc_w1_hw_init(struct mxc_w1_device *dev)
+{
+ clk_enable(dev->clk);
+
+ /* set the timer divider clock to divide by 65 */
+ /* as the clock to the One Wire is at 66.5MHz */
+ __raw_writeb(dev->clkdiv, dev->base_address + MXC_W1_TIME_DIVIDER);
+
+ return;
+}
+
+/*!
+ * this is the probe routine for the One Wire driver.
+ * It is called during the driver initilaization.
+ * @param pdev the platform device structure for w1
+ * @return The function returns 0 on success
+ * and a non-zero value on failure
+ *
+ */
+static int __devinit mxc_w1_probe(struct platform_device *pdev)
+{
+ struct mxc_w1_device *dev;
+ struct mxc_w1_config *data =
+ (struct mxc_w1_config *)pdev->dev.platform_data;
+ int flag, ret_val, irq = -ENXIO;
+ int err = 0;
+ ret_val = 0;
+ flag = data->search_rom_accelerator;
+ dev = kzalloc(sizeof(struct mxc_w1_device) +
+ sizeof(struct w1_bus_master), GFP_KERNEL);
+ if (!dev) {
+ return -ENOMEM;
+ }
+ dev->clk = clk_get(&pdev->dev, "owire_clk");
+ dev->bus_master = (struct w1_bus_master *)(dev + 1);
+ dev->found = 1;
+ dev->clkdiv = (clk_get_rate(dev->clk) / 1000000) - 1;
+ dev->base_address = (void *)IO_ADDRESS(OWIRE_BASE_ADDR);
+
+ mxc_w1_hw_init(dev);
+ dev->bus_master->data = dev;
+ dev->bus_master->reset_bus = &mxc_w1_ds2_reset_bus;
+ dev->bus_master->touch_bit = &mxc_w1_ds2_touch_bit;
+ if (flag) {
+ dev->bus_master->write_byte = &mxc_w1_ds2_write_byte;
+ dev->bus_master->read_byte = &mxc_w1_ds2_read_byte;
+ dev->bus_master->search = &search_ROM_accelerator;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ err = -ENOENT;
+ goto err_out_free_device;
+ }
+ ret_val =
+ request_irq(irq, w1_interrupt_handler, 0, "mxc_w1", dev);
+ if (ret_val) {
+ pr_debug("OWire:request_irq(%d) returned error %d\n",
+ irq, ret_val);
+ err = -1;
+ goto err_out_free_device;
+ }
+ }
+ err = w1_add_master_device(dev->bus_master);
+ if (err)
+ goto err_irq;
+
+ platform_set_drvdata(pdev, dev);
+ return 0;
+
+err_irq:
+ if (irq >= 0)
+ free_irq(irq, dev);
+err_out_free_device:
+ kfree(dev);
+ return err;
+}
+
+/*
+ * disassociate the w1 device from the driver
+ * @param dev the device structure for w1
+ * @return The function returns void
+ */
+static int mxc_w1_remove(struct platform_device *pdev)
+{
+ struct mxc_w1_device *dev = platform_get_drvdata(pdev);
+ struct mxc_w1_config *data =
+ (struct mxc_w1_config *)pdev->dev.platform_data;
+ int irq;
+
+ if (dev->found) {
+ w1_remove_master_device(dev->bus_master);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if ((irq >= 0) && (data->search_rom_accelerator))
+ free_irq(irq, dev);
+
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+
+ kfree(dev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+/*
+ * initialize the driver
+ * @return The function returns 0 on success
+ * and a non-zero value on failure
+ */
+
+static int __init mxc_w1_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Serial: MXC OWire driver\n");
+
+ gpio_owire_active();
+
+ ret = platform_driver_register(&mxc_w1_driver);
+
+ return ret;
+}
+
+/*
+ * cleanup before the driver exits
+ */
+static void mxc_w1_exit(void)
+{
+ gpio_owire_inactive();
+ platform_driver_unregister(&mxc_w1_driver);
+}
+
+module_init(mxc_w1_init);
+module_exit(mxc_w1_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductors Inc");
+MODULE_DESCRIPTION("Driver for One-Wire on MXC");
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index 8d0b1fb1e52e..ad77126801f5 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -16,12 +16,40 @@ config W1_SLAVE_SMEM
Say Y here if you want to connect 1-wire
simple 64bit memory rom(ds2401/ds2411/ds1990*) to your wire.
+config W1_SLAVE_DS2431
+ tristate "1kb EEPROM family support (DS2431)"
+ help
+ Say Y here if you want to use a 1-wire
+ 1kb EEPROM family device (DS2431)
+
+config W1_SLAVE_DS2751
+ tristate "Battery Level sensing support (DS2751)"
+ depends on W1
+ help
+ Say Y here if you want to use a 1-wire
+ battery level sensing device (DS2751).
+
+config W1_SLAVE_DS2751_CRC
+ bool "Protect DS2751 data with a CRC16"
+ depends on W1_SLAVE_DS2751
+ select CRC16
+ help
+ Say Y here to protect DS2751 data with a CRC16.
+ Each block has 30 bytes of data and a two byte CRC16.
+ Full block writes are only allowed if the CRC is valid.
+
config W1_SLAVE_DS2433
tristate "4kb EEPROM family support (DS2433)"
help
Say Y here if you want to use a 1-wire
4kb EEPROM family device (DS2433).
+config W1_SLAVE_DS2438
+ tristate "Smart Battery Monitor (DS2438)"
+ help
+ Say Y here if you want to use a 1-wire
+ Smart Battery Monitor family device (DS2438).
+
config W1_SLAVE_DS2433_CRC
bool "Protect DS2433 data with a CRC16"
depends on W1_SLAVE_DS2433
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index 990f400b6d22..fd53f53d8681 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -4,6 +4,9 @@
obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o
obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o
+obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o
obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o
+obj-$(CONFIG_W1_SLAVE_DS2751) += w1_ds2751.o
+obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o
diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c
new file mode 100644
index 000000000000..a3434ad5994c
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2438.c
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+/*
+ * 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
+ */
+
+#include <asm/types.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/idr.h>
+#include <linux/power_supply.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+#include "w1_ds2438.h"
+
+struct ds2438_device_info {
+ /* DS2438 data, valid after calling ds2438_battery_read_status() */
+ unsigned long update_time; /* jiffies when data read */
+ char raw[DS2438_PAGE_SIZE]; /* raw DS2438 data */
+ int voltage_uV;
+ int current_uA;
+ int accum_current_uAh;
+ int temp_C;
+ int charge_status;
+ u8 init:1;
+ u8 setup:1;
+ u8 calibrate:1;
+ u8 input_src:1;
+ u8 ee_flg:1;
+ u8 resv_bit:3;
+ u8 threshold:8;
+ u16 resv_bytes;
+ u32 senser;
+
+ struct power_supply bat;
+ struct device *w1_dev;
+ struct ds2438_ops ops;
+ struct workqueue_struct *monitor_wqueue;
+ struct delayed_work monitor_work;
+};
+
+#define DS2438_SENSER 25
+#define to_ds2438_device_info(x) container_of((x), struct ds2438_device_info, \
+ bat);
+
+
+static enum power_supply_property ds2438_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+};
+
+static char ds2438_sensers_title[] = "DS2438 senserin thousands of resister:";
+static unsigned int cache_time = 1000;
+module_param(cache_time, uint, 0644);
+MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+
+static ssize_t ds2438_show_input(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ return sprintf(buf, "%s\n", di->input_src ? "1:VDD" : "0:VAD");
+}
+
+static ssize_t ds2438_show_senser(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int len;
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ len = sprintf(buf, "%s\n", ds2438_sensers_title);
+ len += sprintf(buf + len, "%d\n", di->senser);
+ return len;
+}
+
+static ssize_t ds2438_show_ee(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ return sprintf(buf, "%d\n", di->ee_flg);
+}
+
+static ssize_t ds2438_show_threshold(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ return sprintf(buf, "%d\n", di->threshold);
+}
+
+static ssize_t ds2438_set_input(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+ di->input_src = !!simple_strtoul(buf, NULL, 0);
+ return count;
+}
+
+static ssize_t ds2438_set_senser(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ u32 resister;
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+ resister = simple_strtoul(buf, NULL, 0);
+ if (resister)
+ di->senser = resister;
+ return count;
+}
+
+static ssize_t ds2438_set_ee(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ di->ee_flg = !!simple_strtoul(buf, NULL, 0);
+ di->setup = 1;
+ return count;
+}
+
+static ssize_t ds2438_set_threshold(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int threshold;
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ threshold = simple_strtoul(buf, NULL, 0);
+ if (threshold < 256) {
+ di->threshold = threshold;
+ di->setup = 1;
+ return count;
+ }
+ return -EINVAL;
+}
+
+static ssize_t ds2438_set_calibrate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ di->calibrate = !!simple_strtoul(buf, NULL, 0);
+ return count;
+}
+
+static struct device_attribute ds2438_dev_attr[] = {
+ __ATTR(input_src, 0664, ds2438_show_input, ds2438_set_input),
+ __ATTR(senser, 0664, ds2438_show_senser, ds2438_set_senser),
+ __ATTR(ee_flg, 0664, ds2438_show_ee, ds2438_set_ee),
+ __ATTR(threshold, 0664, ds2438_show_threshold, ds2438_set_threshold),
+ __ATTR(calibrate, 0220, NULL, ds2438_set_calibrate),
+};
+
+static void ds2438_setup(struct ds2438_device_info *di)
+{
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ if (di->init && di->setup) {
+ if (di->ee_flg)
+ di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_EE;
+ else
+ di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_EE;
+ if (di->input_src)
+ di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_AD;
+ else
+ di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_AD;
+ di->raw[PAGE0_THRESHOLD] = di->threshold;
+ } else {
+ di->ee_flg = !!(di->raw[PAGE0_STAT_CTRL] & DS2438_CTRL_EE);
+ di->input_src = !!(di->raw[PAGE0_STAT_CTRL] & DS2438_CTRL_AD);
+ di->threshold = di->raw[PAGE0_THRESHOLD];
+ di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_IAD | DS2438_CTRL_CA;
+ }
+ di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL);
+ if (!di->init) {
+ di->calibrate = 1;
+ di->init = 1;
+ }
+ di->setup = 0;
+}
+
+static void ds2438_calibrate(struct ds2438_device_info *di)
+{
+ int current_raw;
+ /* disable ICA */
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_IAD;
+ di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL);
+
+ /* Zero offset */
+ di->ops.load_sram(di->w1_dev, PAGE1_ETM);
+ di->ops.read_page(di->w1_dev, PAGE1_ETM, di->raw);
+ ds2438_writew(di->raw + PAGE1_OFFSET_LSB, 0);
+ di->ops.drain_sram(di->w1_dev, PAGE1_ETM_BYTE0);
+
+ /* enable ICA & read current */
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_IAD;
+ di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL);
+ /*wait current convert about 36HZ */
+ mdelay(30);
+ /* disable ICA */
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->raw[PAGE0_STAT_CTRL] &= ~DS2438_CTRL_IAD;
+ di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL);
+ /* read current value */
+ current_raw = ds2438_readw(di->raw + PAGE0_CURRENT_LSB);
+ /* write offset by current value */
+ di->ops.load_sram(di->w1_dev, PAGE1_ETM);
+ di->ops.read_page(di->w1_dev, PAGE1_ETM, di->raw);
+ ds2438_writew(di->raw + PAGE1_OFFSET_LSB, current_raw << 8);
+ di->ops.write_page(di->w1_dev, PAGE1_ETM, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE1_ETM);
+
+ /*enable ICA again */
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->raw[PAGE0_STAT_CTRL] |= DS2438_CTRL_IAD;
+ di->ops.write_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ di->ops.drain_sram(di->w1_dev, PAGE0_CONTROL);
+ di->calibrate = 0;
+}
+
+/*
+ * power supply temperture is in tenths of degree.
+ */
+static inline int ds2438_get_temp(u16 raw)
+{
+ int degree, s;
+ s = !!(raw & 0x8000);
+
+ if (s)
+ raw = ((~raw & 0x7FFF) + 1);
+ degree = ((raw >> 8) * 10) + (((raw & 0xFF) * 5) + 63) / 128;
+ return s ? -degree : degree;
+}
+
+/*
+ * power supply current is in uA.
+ */
+static inline int ds2438_get_current(u32 senser, u16 raw)
+{
+ int s, current_uA;
+ s = !!(raw & 0xFC00);
+ /* (x * 1000 * 1000)uA / (4096 * (Rsens / 1000)) */
+ raw &= 0x3FF;
+ current_uA = raw * 125 * 125 * 125;
+ current_uA /= (senser << 3);
+ return s ? -current_uA : current_uA;
+}
+
+/*
+ * power supply current is in uAh.
+ */
+static inline int ds2438_get_ica(u32 senser, u8 raw)
+{
+ int charge_uAh;
+ /* (x * 1000 * 1000)uA / (2048 * (Rsens / 1000)) */
+ charge_uAh = (raw * 125 * 125 * 125) >> 4;
+ charge_uAh /= (senser << 4);
+ return charge_uAh;
+}
+
+static int ds2438_battery_update_page1(struct ds2438_device_info *di)
+{
+ int ica_raw;
+ di->ops.load_sram(di->w1_dev, PAGE1_ETM);
+ di->ops.read_page(di->w1_dev, PAGE1_ETM, di->raw);
+ ica_raw = di->raw[PAGE1_ICA];
+ di->accum_current_uAh = ds2438_get_ica(di->senser, ica_raw);
+ return 0;
+}
+
+static int ds2438_battery_read_status(struct ds2438_device_info *di)
+{
+ u8 status;
+ int temp_raw, voltage_raw, current_raw;
+
+ if (!(di->init) || di->setup)
+ ds2438_setup(di);
+
+ if (di->calibrate)
+ ds2438_calibrate(di);
+
+ if (di->update_time && time_before(jiffies, di->update_time +
+ msecs_to_jiffies(cache_time)))
+ return 0;
+
+ di->ops.load_sram(di->w1_dev, PAGE0_CONTROL);
+ di->ops.read_page(di->w1_dev, PAGE0_CONTROL, di->raw);
+ status = di->raw[PAGE0_STAT_CTRL];
+ temp_raw = ds2438_readw(di->raw + PAGE0_TEMP_LSB);
+ voltage_raw = ds2438_readw(di->raw + PAGE0_VOLTAGE_LSB);
+ current_raw = ds2438_readw(di->raw + PAGE0_CURRENT_LSB);
+ di->temp_C = ds2438_get_temp(temp_raw);
+ di->voltage_uV = voltage_raw * 10000;
+ di->current_uA = ds2438_get_current(di->senser, current_raw);
+
+ ds2438_battery_update_page1(di);
+
+ if (!(status & DS2438_STAT_TB))
+ di->ops.command(di->w1_dev, DS2438_CONVERT_TEMP, 0);
+ if (!(status & DS2438_STAT_ADB))
+ di->ops.command(di->w1_dev, DS2438_CONVERT_VOLT, 0);
+ di->update_time = jiffies;
+ return 0;
+}
+
+static void ds2438_battery_update_status(struct ds2438_device_info *di)
+{
+ int old_charge_status = di->charge_status;
+
+ ds2438_battery_read_status(di);
+
+ if (di->charge_status != old_charge_status)
+ power_supply_changed(&di->bat);
+}
+
+static void ds2438_battery_work(struct work_struct *work)
+{
+ struct ds2438_device_info *di = container_of(work,
+ struct ds2438_device_info,
+ monitor_work.work);
+ const int interval = HZ * 60;
+
+ dev_dbg(di->w1_dev, "%s\n", __func__);
+
+ ds2438_battery_update_status(di);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
+}
+
+static void ds2438_battery_external_power_changed(struct power_supply *psy)
+{
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ dev_dbg(di->w1_dev, "%s\n", __func__);
+
+ cancel_delayed_work(&di->monitor_work);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ / 10);
+}
+
+static int ds2438_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ds2438_device_info *di = to_ds2438_device_info(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = di->charge_status;
+ return 0;
+ default:
+ break;
+ }
+
+ ds2438_battery_read_status(di);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = di->voltage_uV;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = di->current_uA;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = di->temp_C;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = di->accum_current_uAh;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* W1 slave DS2438 famliy operations */
+static int ds2438_read_page(struct device *dev, u8 page, u8 *buf)
+{
+ struct w1_slave *slave = container_of(dev, struct w1_slave, dev);
+ if ((page >= DS2438_PAGE_NUM) || (buf == NULL))
+ return -EINVAL;
+
+ mutex_lock(&slave->master->mutex);
+ if (!w1_reset_select_slave(slave)) {
+ w1_write_8(slave->master, W1_READ_SCRATCHPAD);
+ w1_write_8(slave->master, page);
+ w1_read_block(slave->master, buf, DS2438_PAGE_SIZE);
+ }
+ mutex_unlock(&slave->master->mutex);
+ return 0;
+}
+
+static int ds2438_write_page(struct device *dev, u8 page, u8 *buf)
+{
+ struct w1_slave *slave = container_of(dev, struct w1_slave, dev);
+ if ((page >= DS2438_PAGE_NUM) || (buf == NULL))
+ return -EINVAL;
+
+ mutex_lock(&slave->master->mutex);
+ if (!w1_reset_select_slave(slave)) {
+ w1_write_8(slave->master, DS2438_WRITE_SCRATCHPAD);
+ w1_write_8(slave->master, page);
+ w1_write_block(slave->master, buf, DS2438_PAGE_SIZE);
+ }
+ mutex_unlock(&slave->master->mutex);
+ return 0;
+}
+
+static int ds2438_command(struct device *dev, u8 command, u8 data)
+{
+ struct w1_slave *slave = container_of(dev, struct w1_slave, dev);
+
+ mutex_lock(&slave->master->mutex);
+ if (!w1_reset_select_slave(slave)) {
+ w1_write_8(slave->master, command);
+ switch (command) {
+ case DS2438_COPY_SCRATCHPAD:
+ case DS2438_RECALL_MEMORY:
+ w1_write_8(slave->master, data);
+ }
+ }
+ mutex_unlock(&slave->master->mutex);
+ return 0;
+}
+
+static int ds2438_drain_sram(struct device *dev, u8 page)
+{
+ return ds2438_command(dev, DS2438_COPY_SCRATCHPAD, page);
+}
+
+static int ds2438_load_sram(struct device *dev, u8 page)
+{
+ return ds2438_command(dev, DS2438_RECALL_MEMORY, page);
+}
+
+static inline void ds2438_defaut_ops(struct ds2438_ops *ops)
+{
+ ops->read_page = ds2438_read_page;
+ ops->write_page = ds2438_write_page;
+ ops->drain_sram = ds2438_drain_sram;
+ ops->load_sram = ds2438_load_sram;
+ ops->command = ds2438_command;
+}
+
+static int ds2438_add_slave(struct w1_slave *slave)
+{
+ int i, retval = 0;
+ struct ds2438_device_info *di;
+
+ di = kzalloc(sizeof(*di), GFP_KERNEL);
+ if (!di) {
+ retval = -ENOMEM;
+ goto di_alloc_failed;
+ }
+
+ di->w1_dev = &slave->dev;
+ di->bat.name = slave->dev.bus_id;
+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->bat.properties = ds2438_battery_props;
+ di->bat.num_properties = ARRAY_SIZE(ds2438_battery_props);
+ di->bat.get_property = ds2438_battery_get_property;
+ di->bat.external_power_changed = ds2438_battery_external_power_changed;
+ ds2438_defaut_ops(&di->ops);
+ di->senser = DS2438_SENSER;
+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+ retval = power_supply_register(&slave->dev, &di->bat);
+ if (retval) {
+ dev_err(&slave->dev, "failed to register battery\n");
+ goto batt_failed;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ds2438_dev_attr); i++) {
+ if (device_create_file(di->bat.dev, ds2438_dev_attr + i)) {
+ printk(KERN_ERR "Customize attribute file fail!\n");
+ break;
+ }
+ }
+
+ if (i != ARRAY_SIZE(ds2438_dev_attr)) {
+ for (; i >= 0; i++)
+ device_remove_file(di->bat.dev, ds2438_dev_attr + i);
+ goto workqueue_failed;
+ }
+ INIT_DELAYED_WORK(&di->monitor_work, ds2438_battery_work);
+ di->monitor_wqueue = create_singlethread_workqueue(slave->dev.bus_id);
+ if (!di->monitor_wqueue) {
+ retval = -ESRCH;
+ goto workqueue_failed;
+ }
+ dev_set_drvdata(&slave->dev, di);
+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ / 2);
+
+ goto success;
+
+ workqueue_failed:
+ power_supply_unregister(&di->bat);
+ batt_failed:
+ kfree(di);
+ di_alloc_failed:
+ success:
+ return retval;
+}
+
+static void ds2438_remove_slave(struct w1_slave *slave)
+{
+ struct ds2438_device_info *di = dev_get_drvdata(&slave->dev);
+
+ cancel_rearming_delayed_workqueue(di->monitor_wqueue,
+ &di->monitor_work);
+ destroy_workqueue(di->monitor_wqueue);
+ power_supply_unregister(&di->bat);
+}
+
+static struct w1_family_ops w1_ds2438_fops = {
+ .add_slave = ds2438_add_slave,
+ .remove_slave = ds2438_remove_slave,
+};
+
+static struct w1_family w1_family_ds2438 = {
+ .fid = W1_FAMILY_DS2438,
+ .fops = &w1_ds2438_fops,
+};
+
+static int __init w1_ds2438_init(void)
+{
+ pr_info("1-wire driver for the DS2438 smart battery monitor\n");
+ return w1_register_family(&w1_family_ds2438);
+}
+
+static void __exit w1_ds2438_fini(void)
+{
+ w1_unregister_family(&w1_family_ds2438);
+}
+
+module_init(w1_ds2438_init);
+module_exit(w1_ds2438_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductors Inc");
+MODULE_DESCRIPTION("1-wire DS2438 family, smart battery monitor.");
diff --git a/drivers/w1/slaves/w1_ds2438.h b/drivers/w1/slaves/w1_ds2438.h
new file mode 100644
index 000000000000..fe22b6ec253b
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2438.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+/*
+ * 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 __W1_DS2438_H__
+#define __W1_DS2438_H__
+
+#include <asm/types.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+
+#define DS2438_DEV_NAME "ds2438-battery"
+
+#define DS2438_PAGE_SIZE 8
+#define DS2438_PAGE_NUM 8
+
+#define DS2438_CONVERT_TEMP 0x44
+#define DS2438_CONVERT_VOLT 0xB4
+#define DS2438_WRITE_SCRATCHPAD 0x4E
+#define DS2438_COPY_SCRATCHPAD 0x48
+#define DS2438_RECALL_MEMORY 0xB8
+
+enum DS2438_PAGE {
+ PAGE0_CONTROL = 0,
+ PAGE1_ETM,
+ PAGE2_STAMP,
+ PAGE3_RESV3,
+ PAGE4_RESV4,
+ PAGE5_RESV5,
+ PAGE6_RESV6,
+ PAGE7_CCA,
+};
+
+enum DS2438_REG {
+ /* PAGE 0 */
+ PAGE0_STAT_CTRL = 0,
+ PAGE0_TEMP_LSB = 1,
+ PAGE0_TEMP_MSB = 2,
+ PAGE0_VOLTAGE_LSB = 3,
+ PAGE0_VOLTAGE_MSB = 4,
+ PAGE0_CURRENT_LSB = 5,
+ PAGE0_CURRENT_MSB = 6,
+ PAGE0_THRESHOLD = 7,
+
+ /* PAGE 1 */
+ PAGE1_ETM_BYTE0 = 0,
+ PAGE1_ETM_BYTE1 = 1,
+ PAGE1_ETM_BYTE2 = 2,
+ PAGE1_ETM_BYTE3 = 3,
+ PAGE1_ICA = 4,
+ PAGE1_OFFSET_LSB = 5,
+ PAGE1_OFFSET_MSB = 6,
+
+ /* PAGE 2 */
+ PAGE2_DISCONNECT_BYTE0 = 0,
+ PAGE2_DISCONNECT_BYTE1 = 1,
+ PAGE2_DISCONNECT_BYTE2 = 2,
+ PAGE2_DISCONNECT_BYTE3 = 3,
+ PAGE2_END_CHARGE_BYTE0 = 4,
+ PAGE2_END_CHARGE_BYTE1 = 5,
+ PAGE2_END_CHARGE_BYTE2 = 6,
+ PAGE2_END_CHARGE_BYTE3 = 7,
+
+ /* PAGE 3 */
+ /* PAGE 4 */
+ /* PAGE 5 */
+ /* PAGE 6 */
+ /* PAGE 7 */
+ PAGE7_CCA_LSB = 4,
+ PAGE7_CCA_MSB = 5,
+ PAGE7_DCA_LSB = 6,
+ PAGE7_DCA_MSB = 7,
+};
+
+#define DS2438_CTRL_IAD (1 << 0)
+#define DS2438_CTRL_CA (1 << 1)
+#define DS2438_CTRL_EE (1 << 2)
+#define DS2438_CTRL_AD (1 << 3)
+#define DS2438_STAT_TB (1 << 4)
+#define DS2438_STAT_NVB (1 << 5)
+#define DS2438_STAT_ADB (1 << 6)
+
+struct ds2438_ops {
+ int (*read_page) (struct device *, u8, u8 *);
+ int (*read_byte) (struct device *, u8, u8, u8 *);
+ int (*read_halfword) (struct device *, u8, u8, u16 *);
+ int (*read_word) (struct device *, u8, u8, u32 *);
+ int (*write_page) (struct device *, u8, u8 *);
+ int (*write_byte) (struct device *, u8, u8, u8);
+ int (*write_halfword) (struct device *, u8, u8, u16);
+ int (*write_word) (struct device *, u8, u8, u32);
+ int (*drain_sram) (struct device *, u8);
+ int (*load_sram) (struct device *, u8);
+ int (*command) (struct device *, u8, u8);
+};
+
+static inline u16 ds2438_readw(u8 *raw)
+{
+ return ((*(raw + 1)) << 8) | (*raw);
+}
+
+static inline void ds2438_writew(u8 *raw, u16 data)
+{
+ *raw++ = data & 0xFF;
+ *raw = (data >> 8) & 0xFF;
+}
+#endif /* __W1_DS2438_H__ */
diff --git a/drivers/w1/slaves/w1_ds2751.c b/drivers/w1/slaves/w1_ds2751.c
new file mode 100644
index 000000000000..9346a21bdc70
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2751.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+/*
+ * Implementation based on w1_ds2433.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#ifdef CONFIG_W1_F51_CRC
+#include <linux/crc16.h>
+
+#define CRC16_INIT 0
+#define CRC16_VALID 0xb001
+
+#endif
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+
+#define W1_EEPROM_SIZE 32
+#define W1_PAGE_SIZE 32
+#define W1_PAGE_BITS 5
+#define W1_PAGE_MASK 0x1F
+
+#define W1_F51_TIME 300
+
+#define W1_F51_READ_EEPROM 0xB8
+#define W1_F51_WRITE_SCRATCH 0x6C
+#define W1_F51_READ_SCRATCH 0x69
+#define W1_F51_COPY_SCRATCH 0x48
+#define W1_STATUS_OFFSET 0x0001
+#define W1_EEPROM_OFFSET 0x0007
+#define W1_SPECIAL_OFFSET 0x0008
+#define W1_EEPROM_BLOCK_0 0x0020
+#define W1_EEPROM_BLOCK_1 0x0030
+#define W1_SRAM 0x0080
+struct w1_f51_data {
+ u8 memory[W1_EEPROM_SIZE];
+ u32 validcrc;
+};
+
+/**
+ * Check the file size bounds and adjusts count as needed.
+ * This would not be needed if the file size didn't reset to 0 after a write.
+ */
+static inline size_t w1_f51_fix_count(loff_t off, size_t count, size_t size)
+{
+ if (off > size)
+ return 0;
+
+ if ((off + count) > size)
+ return (size - off);
+
+ return count;
+}
+
+#ifdef CONFIG_W1_F51_CRC
+static int w1_f51_refresh_block(struct w1_slave *sl, struct w1_f51_data *data,
+ int block)
+{
+ u8 wrbuf[3];
+ int off = block * W1_PAGE_SIZE;
+ if (data->validcrc & (1 << block))
+ return 0;
+
+ if (w1_reset_select_slave(sl)) {
+ data->validcrc = 0;
+ return -EIO;
+ }
+ wrbuf[0] = W1_F51_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE);
+
+ /* cache the block if the CRC is valid */
+ if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID)
+ data->validcrc |= (1 << block);
+
+ return 0;
+}
+#endif /* CONFIG_W1_F51_CRC */
+
+static ssize_t w1_f51_read_bin(struct kobject *kobj, char *buf, loff_t off,
+ size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+#ifdef CONFIG_W1_F51_CRC
+ struct w1_f51_data *data = sl->family_data;
+ int i, min_page, max_page;
+#else
+ u8 wrbuf[3];
+#endif
+
+ if ((count = w1_f51_fix_count(off, count, W1_EEPROM_SIZE)) == 0) {
+ return 0;
+ }
+ mutex_lock(&sl->master->mutex);
+#ifdef CONFIG_W1_F51_CRC
+ min_page = (off >> W1_PAGE_BITS);
+ max_page = (off + count - 1) >> W1_PAGE_BITS;
+ for (i = min_page; i <= max_page; i++) {
+ if (w1_f51_refresh_block(sl, data, i)) {
+ count = -EIO;
+ goto out_up;
+ }
+ }
+ memcpy(buf, &data->memory[off], count);
+
+#else /* CONFIG_W1_F51_CRC */
+
+ /* read directly from the EEPROM */
+ if (w1_reset_select_slave(sl)) {
+ count = -EIO;
+ goto out_up;
+ }
+ off = (loff_t) W1_EEPROM_BLOCK_0;
+ wrbuf[0] = W1_F51_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+ w1_write_block(sl->master, wrbuf, 3);
+ if (w1_reset_select_slave(sl)) {
+ count = -EIO;
+ goto out_up;
+ }
+
+ wrbuf[0] = W1_F51_READ_SCRATCH;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, buf, count);
+
+#endif /* CONFIG_W1_F51_CRC */
+
+ out_up:
+ mutex_unlock(&sl->master->mutex);
+ return count;
+}
+
+/**
+ * Writes to the scratchpad and reads it back for verification.
+ * Then copies the scratchpad to EEPROM.
+ * The data must be on one page.
+ * The master must be locked.
+ *
+ * @param sl The slave structure
+ * @param addr Address for the write
+ * @param len length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
+ * @param data The data to write
+ * @return 0=Success -1=failure
+ */
+static int w1_f51_write(struct w1_slave *sl, int addr, int len, const u8 * data)
+{
+ u8 wrbuf[4];
+ u8 rdbuf[W1_EEPROM_SIZE + 3];
+ u8 es = (addr + len - 1) & 0x1f;
+ /* Write the data to the scratchpad */
+ if (w1_reset_select_slave(sl))
+ return -1;
+ wrbuf[0] = W1_F51_WRITE_SCRATCH;
+ wrbuf[1] = addr & 0xff;
+ wrbuf[2] = addr >> 8;
+
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_write_block(sl->master, data, len);
+ /* Read the scratchpad and verify */
+ if (w1_reset_select_slave(sl))
+ return -1;
+ wrbuf[0] = W1_F51_READ_SCRATCH;
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, rdbuf, len + 3);
+ /* Compare what was read against the data written */
+ if (memcmp(data, &rdbuf[0], len) != 0) {
+ printk("Error reading the scratch Pad\n");
+ return -1;
+ }
+ /* Copy the scratchpad to EEPROM */
+ if (w1_reset_select_slave(sl))
+ return -1;
+ wrbuf[0] = W1_F51_COPY_SCRATCH;
+ wrbuf[3] = es;
+ w1_write_block(sl->master, wrbuf, 4);
+ /* Sleep for 5 ms to wait for the write to complete */
+ msleep(5);
+
+ /* Reset the bus to wake up the EEPROM (this may not be needed) */
+ w1_reset_bus(sl->master);
+
+ return 0;
+}
+
+static ssize_t w1_f51_write_bin(struct kobject *kobj, char *buf, loff_t off,
+ size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int addr;
+
+ if ((count = w1_f51_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
+ return 0;
+ off = (loff_t) 0x0020;
+#ifdef CONFIG_W1_F51_CRC
+ /* can only write full blocks in cached mode */
+ if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) {
+ dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n",
+ (int)off, count);
+ return -EINVAL;
+ }
+
+ /* make sure the block CRCs are valid */
+ for (idx = 0; idx < count; idx += W1_PAGE_SIZE) {
+ if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE) != CRC16_VALID) {
+ dev_err(&sl->dev, "bad CRC at offset %d\n", (int)off);
+ return -EINVAL;
+ }
+ }
+#endif /* CONFIG_W1_F51_CRC */
+
+ mutex_lock(&sl->master->mutex);
+
+ /* Can only write data to one page at a time */
+ addr = off;
+ if (w1_f51_write(sl, addr, count, buf) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+
+ out_up:
+ mutex_unlock(&sl->master->mutex);
+
+ return count;
+}
+
+static struct bin_attribute w1_f51_bin_attr = {
+ .attr = {
+ .name = "eeprom",
+ .mode = S_IRUGO | S_IWUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = W1_EEPROM_SIZE,
+ .read = w1_f51_read_bin,
+ .write = w1_f51_write_bin,
+};
+
+static int w1_f51_add_slave(struct w1_slave *sl)
+{
+ int err;
+#ifdef CONFIG_W1_F51_CRC
+ struct w1_f51_data *data;
+ data = kmalloc(sizeof(struct w1_f51_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ memset(data, 0, sizeof(struct w1_f51_data));
+ sl->family_data = data;
+
+#endif /* CONFIG_W1_F51_CRC */
+
+ err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f51_bin_attr);
+
+#ifdef CONFIG_W1_F51_CRC
+ if (err)
+ kfree(data);
+#endif /* CONFIG_W1_F51_CRC */
+
+ return err;
+}
+
+static void w1_f51_remove_slave(struct w1_slave *sl)
+{
+#ifdef CONFIG_W1_F51_CRC
+ kfree(sl->family_data);
+ sl->family_data = NULL;
+#endif /* CONFIG_W1_F51_CRC */
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_f51_bin_attr);
+}
+
+static struct w1_family_ops w1_f51_fops = {
+ .add_slave = w1_f51_add_slave,
+ .remove_slave = w1_f51_remove_slave,
+};
+
+static struct w1_family w1_family_51 = {
+ .fid = W1_EEPROM_DS2751,
+ .fops = &w1_f51_fops,
+};
+
+static int __init w1_f51_init(void)
+{
+ return w1_register_family(&w1_family_51);
+}
+
+static void __exit w1_f51_fini(void)
+{
+ w1_unregister_family(&w1_family_51);
+}
+
+module_init(w1_f51_init);
+module_exit(w1_f51_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductors Inc");
+MODULE_DESCRIPTION
+ ("w1 family 51 driver for DS2751, Battery Level Sensing Device");
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index 3ca1b9298f21..c541f9215765 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -32,8 +32,10 @@
#define W1_THERM_DS18S20 0x10
#define W1_THERM_DS1822 0x22
#define W1_EEPROM_DS2433 0x23
+#define W1_EEPROM_DS2751 0x51
#define W1_THERM_DS18B20 0x28
#define W1_EEPROM_DS2431 0x2D
+#define W1_FAMILY_DS2438 0x26
#define W1_FAMILY_DS2760 0x30
#define MAXNAMELEN 32
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 4fd3fa5546b1..edc87ff76d7c 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -196,6 +196,27 @@ config PNX4008_WATCHDOG
Say N if you are unsure.
+config MXC_WATCHDOG
+ tristate "MXC watchdog"
+ depends on WATCHDOG && WATCHDOG_NOWAYOUT
+ depends on ARCH_MXC
+ help
+ Watchdog timer embedded into MXC chips. This will
+ reboot your system when timeout is reached.
+
+ NOTE: once enabled, this timer cannot be disabled.
+ To compile this driver as a module, choose M here: the
+ module will be called mxc_wdt.
+
+config STMP3XXX_WATCHDOG
+ tristate "Sigmatel STMP3XXX watchdog"
+ depends on ARCH_STMP3XXX
+ help
+ Say Y here if to include support for the watchdog timer
+ for the Sigmatel STMP37XX/378X SoC.
+ To compile this driver as a module, choose M here: the
+ module will be called stmp3xxx_wdt.
+
config IOP_WATCHDOG
tristate "IOP Watchdog"
depends on PLAT_IOP
@@ -233,6 +254,15 @@ config ORION5X_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called orion5x_wdt.
+config NS9XXX_WATCHDOG
+ tristate "ns9xxx watchdog"
+ depends on PROCESSOR_NS921X || PROCESSOR_NS9360
+ help
+ Say Y here if to include support for the watchdog timer
+ in the ns9xxx processor.
+ This driver can be built as a module by choosing M. The module
+ will be called ns9xxx_wdt.
+
# ARM26 Architecture
# AVR32 Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e352bbb7630b..181c0b79304a 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -37,10 +37,13 @@ obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
+obj-$(CONFIG_MXC_WATCHDOG) += mxc_wdt.o
+obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
obj-$(CONFIG_ORION5X_WATCHDOG) += orion5x_wdt.o
+obj-$(CONFIG_NS9XXX_WATCHDOG) += ns9xxx_wdt.o
# ARM26 Architecture
diff --git a/drivers/watchdog/mxc_wdt.c b/drivers/watchdog/mxc_wdt.c
new file mode 100644
index 000000000000..833fa4cff224
--- /dev/null
+++ b/drivers/watchdog/mxc_wdt.c
@@ -0,0 +1,385 @@
+/*
+ * linux/drivers/char/watchdog/mxc_wdt.c
+ *
+ * Watchdog driver for FSL MXC. It is based on omap1610_wdt.c
+ *
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * 2005 (c) MontaVista Software, Inc. All Rights Reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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
+ *
+ * History:
+ *
+ * 20051207: <AKuster@mvista.com>
+ * Full rewrite based on
+ * linux-2.6.15-rc5/drivers/char/watchdog/omap_wdt.c
+ * Add platform resource support
+ *
+ */
+
+/*!
+ * @defgroup WDOG Watchdog Timer (WDOG) Driver
+ */
+/*!
+ * @file mxc_wdt.c
+ *
+ * @brief Watchdog timer driver
+ *
+ * @ingroup WDOG
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#include <mach/hardware.h>
+#include "mxc_wdt.h"
+#define DVR_VER "2.0"
+
+#define WDOG_SEC_TO_COUNT(s) ((s * 2) << 8)
+#define WDOG_COUNT_TO_SEC(c) ((c >> 8) / 2)
+
+static u32 wdt_base_reg;
+static int mxc_wdt_users;
+static struct clk *mxc_wdt_clk;
+
+static unsigned timer_margin = TIMER_MARGIN_DEFAULT;
+module_param(timer_margin, uint, 0);
+MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
+
+static unsigned dev_num = 0;
+
+static void mxc_wdt_ping(u32 base)
+{
+ /* issue the service sequence instructions */
+ __raw_writew(WDT_MAGIC_1, base + MXC_WDT_WSR);
+ __raw_writew(WDT_MAGIC_2, base + MXC_WDT_WSR);
+}
+
+static void mxc_wdt_config(u32 base)
+{
+ u16 val;
+
+ val = __raw_readw(base + MXC_WDT_WCR);
+ val |= 0xFF00 | WCR_WOE_BIT | WCR_WDA_BIT | WCR_SRS_BIT;
+ /* enable suspend WDT */
+ val |= WCR_WDZST_BIT | WCR_WDBG_BIT;
+ /* generate reset if wdog times out */
+ val &= ~WCR_WRE_BIT;
+
+ __raw_writew(val, base + MXC_WDT_WCR);
+}
+
+static void mxc_wdt_enable(u32 base)
+{
+ u16 val;
+
+ val = __raw_readw(base + MXC_WDT_WCR);
+ val |= WCR_WDE_BIT;
+ __raw_writew(val, base + MXC_WDT_WCR);
+}
+
+static void mxc_wdt_disable(u32 base)
+{
+ /* disable not supported by this chip */
+}
+
+static void mxc_wdt_adjust_timeout(unsigned new_timeout)
+{
+ if (new_timeout < TIMER_MARGIN_MIN)
+ new_timeout = TIMER_MARGIN_DEFAULT;
+ if (new_timeout > TIMER_MARGIN_MAX)
+ new_timeout = TIMER_MARGIN_MAX;
+ timer_margin = new_timeout;
+}
+
+static u16 mxc_wdt_get_timeout(u32 base)
+{
+ u16 val;
+
+ val = __raw_readw(base + MXC_WDT_WCR);
+ return WDOG_COUNT_TO_SEC(val);
+}
+
+static u16 mxc_wdt_get_bootreason(u32 base)
+{
+ u16 val;
+
+ val = __raw_readw(base + MXC_WDT_WRSR);
+ return val;
+}
+
+static void mxc_wdt_set_timeout(u32 base)
+{
+ u16 val;
+ val = __raw_readw(base + MXC_WDT_WCR);
+ val = (val & 0x00FF) | WDOG_SEC_TO_COUNT(timer_margin);
+ __raw_writew(val, base + MXC_WDT_WCR);
+ val = __raw_readw(base + MXC_WDT_WCR);
+ timer_margin = WDOG_COUNT_TO_SEC(val);
+}
+
+/*
+ * Allow only one task to hold it open
+ */
+
+static int mxc_wdt_open(struct inode *inode, struct file *file)
+{
+
+ if (test_and_set_bit(1, (unsigned long *)&mxc_wdt_users))
+ return -EBUSY;
+
+ mxc_wdt_config(wdt_base_reg);
+ mxc_wdt_set_timeout(wdt_base_reg);
+ mxc_wdt_enable(wdt_base_reg);
+ mxc_wdt_ping(wdt_base_reg);
+
+ return 0;
+}
+
+static int mxc_wdt_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer unless NOWAYOUT is defined.
+ */
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ mxc_wdt_disable(wdt_base_reg);
+
+#else
+ printk(KERN_CRIT "mxc_wdt: Unexpected close, not stopping!\n");
+#endif
+ mxc_wdt_users = 0;
+ return 0;
+}
+
+static ssize_t
+mxc_wdt_write(struct file *file, const char __user * data,
+ size_t len, loff_t * ppos)
+{
+ /* Refresh LOAD_TIME. */
+ if (len)
+ mxc_wdt_ping(wdt_base_reg);
+ return len;
+}
+
+static int
+mxc_wdt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int new_margin;
+ int bootr;
+
+ static struct watchdog_info ident = {
+ .identity = "MXC Watchdog",
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 0,
+ };
+
+ switch (cmd) {
+ default:
+ return -ENOIOCTLCMD;
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident));
+ case WDIOC_GETSTATUS:
+ return put_user(0, (int __user *)arg);
+ case WDIOC_GETBOOTSTATUS:
+ bootr = mxc_wdt_get_bootreason(wdt_base_reg);
+ return put_user(bootr, (int __user *)arg);
+ case WDIOC_KEEPALIVE:
+ mxc_wdt_ping(wdt_base_reg);
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int __user *)arg))
+ return -EFAULT;
+
+ mxc_wdt_adjust_timeout(new_margin);
+ mxc_wdt_disable(wdt_base_reg);
+ mxc_wdt_set_timeout(wdt_base_reg);
+ mxc_wdt_enable(wdt_base_reg);
+ mxc_wdt_ping(wdt_base_reg);
+ return 0;
+
+ case WDIOC_GETTIMEOUT:
+ mxc_wdt_ping(wdt_base_reg);
+ new_margin = mxc_wdt_get_timeout(wdt_base_reg);
+ return put_user(new_margin, (int __user *)arg);
+ }
+}
+
+static struct file_operations mxc_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = mxc_wdt_write,
+ .ioctl = mxc_wdt_ioctl,
+ .open = mxc_wdt_open,
+ .release = mxc_wdt_release,
+};
+
+static struct miscdevice mxc_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &mxc_wdt_fops
+};
+
+static int __init mxc_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res, *mem;
+ int ret;
+
+ /* reserve static register mappings */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, dev_num);
+ if (!res)
+ return -ENOENT;
+
+ mem = request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name);
+ if (mem == NULL)
+ return -EBUSY;
+
+ platform_set_drvdata(pdev, mem);
+
+ wdt_base_reg = IO_ADDRESS(res->start);
+ mxc_wdt_disable(wdt_base_reg);
+ mxc_wdt_adjust_timeout(timer_margin);
+
+ mxc_wdt_users = 0;
+
+ mxc_wdt_miscdev.this_device = &pdev->dev;
+
+ mxc_wdt_clk = clk_get(NULL, "wdog_clk");
+ clk_enable(mxc_wdt_clk);
+
+ ret = misc_register(&mxc_wdt_miscdev);
+ if (ret)
+ goto fail;
+
+ pr_info("MXC Watchdog # %d Timer: initial timeout %d sec\n", dev_num,
+ timer_margin);
+
+ return 0;
+
+ fail:
+ release_resource(mem);
+ pr_info("MXC Watchdog Probe failed\n");
+ return ret;
+}
+
+static void mxc_wdt_shutdown(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_drvdata(pdev);
+ mxc_wdt_disable(wdt_base_reg);
+ pr_info("MXC Watchdog # %d shutdown\n", dev_num);
+}
+
+static int __exit mxc_wdt_remove(struct platform_device *pdev)
+{
+ struct resource *mem = platform_get_drvdata(pdev);
+ misc_deregister(&mxc_wdt_miscdev);
+ release_resource(mem);
+ pr_info("MXC Watchdog # %d removed\n", dev_num);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/* REVISIT ... not clear this is the best way to handle system suspend; and
+ * it's very inappropriate for selective device suspend (e.g. suspending this
+ * through sysfs rather than by stopping the watchdog daemon). Also, this
+ * may not play well enough with NOWAYOUT...
+ */
+
+static int mxc_wdt_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct resource *res = platform_get_drvdata(pdev);
+
+ if (mxc_wdt_users) {
+ mxc_wdt_disable(wdt_base_reg);
+ }
+ return 0;
+}
+
+static int mxc_wdt_resume(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_drvdata(pdev);
+ if (mxc_wdt_users) {
+ mxc_wdt_enable(wdt_base_reg);
+ mxc_wdt_ping(wdt_base_reg);
+ }
+ return 0;
+}
+
+#else
+#define mxc_wdt_suspend NULL
+#define mxc_wdt_resume NULL
+#endif
+
+static struct platform_driver mxc_wdt_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mxc_wdt",
+ },
+ .probe = mxc_wdt_probe,
+ .shutdown = mxc_wdt_shutdown,
+ .remove = __exit_p(mxc_wdt_remove),
+ .suspend = mxc_wdt_suspend,
+ .resume = mxc_wdt_resume,
+};
+
+static int __init mxc_wdt_init(void)
+{
+ pr_info("MXC WatchDog Driver %s\n", DVR_VER);
+
+ if ((timer_margin < TIMER_MARGIN_MIN) ||
+ (timer_margin > TIMER_MARGIN_MAX)) {
+ pr_info("MXC watchdog error. wrong timer_margin %d\n",
+ timer_margin);
+ pr_info(" Range: %d to %d seconds\n", TIMER_MARGIN_MIN,
+ TIMER_MARGIN_MAX);
+ return -EINVAL;
+ }
+
+ return platform_driver_register(&mxc_wdt_driver);
+}
+
+static void __exit mxc_wdt_exit(void)
+{
+ platform_driver_unregister(&mxc_wdt_driver);
+ pr_info("MXC WatchDog Driver removed\n");
+}
+
+module_init(mxc_wdt_init);
+module_exit(mxc_wdt_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/mxc_wdt.h b/drivers/watchdog/mxc_wdt.h
new file mode 100644
index 000000000000..cd09b9acf99f
--- /dev/null
+++ b/drivers/watchdog/mxc_wdt.h
@@ -0,0 +1,37 @@
+/*
+ * linux/drivers/char/watchdog/mxc_wdt.h
+ *
+ * BRIEF MODULE DESCRIPTION
+ * MXC Watchdog timer register definitions
+ *
+ * Author: MontaVista Software, Inc.
+ * <AKuster@mvista.com> or <source@mvista.com>
+ *
+ * 2005 (c) MontaVista Software, Inc.
+ * Copyright 2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * 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.
+ */
+
+#ifndef __MXC_WDT_H__
+#define __MXC_WDT_H__
+
+#define MXC_WDT_WCR 0x00
+#define MXC_WDT_WSR 0x02
+#define MXC_WDT_WRSR 0x04
+#define WCR_WOE_BIT (1 << 6)
+#define WCR_WDA_BIT (1 << 5)
+#define WCR_SRS_BIT (1 << 4)
+#define WCR_WRE_BIT (1 << 3)
+#define WCR_WDE_BIT (1 << 2)
+#define WCR_WDBG_BIT (1 << 1)
+#define WCR_WDZST_BIT (1 << 0)
+#define WDT_MAGIC_1 0x5555
+#define WDT_MAGIC_2 0xAAAA
+
+#define TIMER_MARGIN_MAX 127
+#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */
+#define TIMER_MARGIN_MIN 1
+
+#endif /* __MXC_WDT_H__ */
diff --git a/drivers/watchdog/ns9xxx_wdt.c b/drivers/watchdog/ns9xxx_wdt.c
new file mode 100644
index 000000000000..6234fbaa1609
--- /dev/null
+++ b/drivers/watchdog/ns9xxx_wdt.c
@@ -0,0 +1,330 @@
+/*
+ * drivers/watchdog/ns9xxx.c
+ *
+ * based on at91rm9200_wdt.c by Andrew Victor
+ *
+ * Copyright (C) 2009 Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+#define WDT_CONFIG (0)
+#define WDT_TIMER (4)
+
+#define WDT_CONFIG_ENABLE (1 << 7)
+#define WDT_CONFIG_IRQCLEAR (1 << 5)
+#define WDT_CONFIG_RESPONSE (1 << 4)
+#define WDT_CONFIG_DIV64 (0x5)
+
+#define DRIVER_NAME "ns9xxx-wdt"
+#define DEFAULT_TIME 10
+
+#define ns9xxx_wdt_pat() \
+{ \
+ iowrite32(pdata.multiplier * timeout, \
+ pdata.ioaddr + WDT_TIMER); \
+}
+
+struct ns9xxx_wdt_pdata {
+ void __iomem *ioaddr;
+ struct resource *mem;
+ struct clk *clk;
+
+ unsigned int multiplier;
+ unsigned int timeout_max;
+
+ unsigned long busy;
+};
+
+static struct ns9xxx_wdt_pdata pdata;
+
+static unsigned int timeout = DEFAULT_TIME;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog Timeout in seconds. "
+ "(default=" __MODULE_STRING(DEFAULT_TIME) ")");
+
+static struct watchdog_info ns9xxx_wdt_info = {
+ .identity = "ns9xxx watchdog",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+/* disable watchdog */
+static inline void ns9xxx_wdt_stop(void)
+{
+ unsigned long cfg;
+
+ /* watchdog cannot be disabled,
+ * but stopped by clearing WDT_CONFIG_IRQCLEAR */
+ cfg = ioread32(pdata.ioaddr + WDT_CONFIG) & ~WDT_CONFIG_IRQCLEAR;
+ iowrite32(cfg, pdata.ioaddr + WDT_CONFIG);
+}
+
+/* enable/reset watchdog */
+static inline void ns9xxx_wdt_start(void)
+{
+ ns9xxx_wdt_pat();
+
+ /* enable watchdog
+ * reset interrupt
+ * action = reset device
+ * divisor = 64 (slowest)
+ */
+ iowrite32(WDT_CONFIG_ENABLE | WDT_CONFIG_IRQCLEAR
+ | WDT_CONFIG_RESPONSE | WDT_CONFIG_DIV64,
+ pdata.ioaddr + WDT_CONFIG);
+}
+
+/* device is opened, start watchdog */
+static int ns9xxx_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &pdata.busy))
+ return -EBUSY;
+
+ ns9xxx_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+/* device is closed, watchdog will not(!) be disabled */
+static int ns9xxx_wdt_close(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &pdata.busy);
+ return 0;
+}
+
+/* update the time interval */
+static int ns9xxx_wdt_settimeout(int new_time)
+{
+ /* skip check if we do not have a clock speed yet */
+ if ((new_time <= 0) || (new_time > pdata.timeout_max))
+ return -EINVAL;
+
+ timeout = new_time;
+ return 0;
+}
+
+/* handle ioctl */
+static int ns9xxx_wdt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value;
+ int ret;
+
+ switch (cmd) {
+ case WDIOC_KEEPALIVE:
+ ns9xxx_wdt_pat();
+ return 0;
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ns9xxx_wdt_info,
+ sizeof(ns9xxx_wdt_info)) ? -EFAULT : 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ ret = ns9xxx_wdt_settimeout(new_value);
+ if (ret)
+ return ret;
+ ns9xxx_wdt_start();
+ return put_user(timeout, p);
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_value, p))
+ return -EFAULT;
+ if (new_value & WDIOS_ENABLECARD)
+ ns9xxx_wdt_start();
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
+
+/* pat watchdog on every write to the device */
+static ssize_t ns9xxx_wdt_write(struct file *file,
+ const char __user *data, size_t len, loff_t *ppos)
+{
+ ns9xxx_wdt_pat();
+ return len;
+}
+
+static const struct file_operations ns9xxx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .ioctl = ns9xxx_wdt_ioctl,
+ .open = ns9xxx_wdt_open,
+ .release = ns9xxx_wdt_close,
+ .write = ns9xxx_wdt_write,
+};
+
+static struct miscdevice ns9xxx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = DRIVER_NAME,
+ .fops = &ns9xxx_wdt_fops,
+};
+
+static int __devinit ns9xxx_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (ns9xxx_wdt_miscdev.parent)
+ return -EBUSY;
+
+ ns9xxx_wdt_miscdev.parent = &pdev->dev;
+
+ pdata.mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pdata.mem) {
+ pr_err(DRIVER_NAME ": memory not available\n");
+ return -ENOENT;
+ }
+
+ if (!request_mem_region(pdata.mem->start,
+ pdata.mem->end - pdata.mem->start + 1,
+ DRIVER_NAME)) {
+ pr_err(DRIVER_NAME ": memory already mapped\n");
+ ret = -EIO;
+ goto err_mem;
+ }
+
+ pdata.ioaddr = ioremap(pdata.mem->start,
+ pdata.mem->end - pdata.mem->start + 1);
+ if (!pdata.ioaddr) {
+ pr_err(DRIVER_NAME ": unable to remap IO memory\n");
+ ret = -EIO;
+ goto err_remap;
+ }
+
+ pdata.clk = clk_get(&pdev->dev, DRIVER_NAME);
+ if (IS_ERR(pdata.clk)) {
+ pr_err(DRIVER_NAME ": clock not available\n");
+ ret = PTR_ERR(pdata.clk);
+ goto err_clk_get;
+ }
+
+ if (!clk_get_rate(pdata.clk)) {
+ pr_err(DRIVER_NAME ": cannot get clock rate\n");
+ ret = -EIO;
+ goto err_clk_rate;
+ }
+
+ /* calculate counter speed and maximum time
+ * clock speed is cpu speed divided by 64 (slowest mode)
+ * counter uses full 32bit register
+ */
+
+ pdata.multiplier = clk_get_rate(pdata.clk) / 64;
+ pdata.timeout_max = 0;
+ pdata.timeout_max = ~pdata.timeout_max / pdata.multiplier;
+
+ /* recheck timeout for overflow */
+ ns9xxx_wdt_settimeout(timeout);
+
+ ret = misc_register(&ns9xxx_wdt_miscdev);
+ if (ret) {
+ pr_err(DRIVER_NAME ": cannot register misc device\n");
+ goto err_register;
+ }
+
+ dev_info(&pdev->dev, "NS9xxx watchdog timer at 0x%p\n",
+ pdata.ioaddr);
+ return 0;
+
+err_register:
+err_clk_rate:
+ clk_put(pdata.clk);
+err_clk_get:
+ iounmap(pdata.ioaddr);
+err_remap:
+ release_mem_region(pdata.mem->start,
+ pdata.mem->end - pdata.mem->start + 1);
+err_mem:
+ release_resource(pdata.mem);
+
+ return ret;
+}
+
+static int __devexit ns9xxx_wdt_remove(struct platform_device *pdev)
+{
+ int res;
+
+ res = misc_deregister(&ns9xxx_wdt_miscdev);
+ if (!res)
+ ns9xxx_wdt_miscdev.parent = NULL;
+
+ return res;
+}
+
+static void ns9xxx_wdt_shutdown(struct platform_device *pdev)
+{
+ ns9xxx_wdt_stop();
+}
+
+#ifdef CONFIG_PM
+
+static int ns9xxx_wdt_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ ns9xxx_wdt_stop();
+ return 0;
+}
+
+static int ns9xxx_wdt_resume(struct platform_device *pdev)
+{
+ if (pdata.busy)
+ ns9xxx_wdt_start();
+ return 0;
+}
+
+#else
+# define ns9xxx_wdt_suspend NULL
+# define ns9xxx_wdt_resume NULL
+#endif
+
+
+static struct platform_driver ns9xxx_wdt_driver = {
+ .probe = ns9xxx_wdt_probe,
+ .remove = __devexit_p(ns9xxx_wdt_remove),
+ .shutdown = ns9xxx_wdt_shutdown,
+ .suspend = ns9xxx_wdt_suspend,
+ .resume = ns9xxx_wdt_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ns9xxx_wdt_init(void)
+{
+ return platform_driver_register(&ns9xxx_wdt_driver);
+}
+
+static void __exit ns9xxx_wdt_exit(void)
+{
+ return platform_driver_unregister(&ns9xxx_wdt_driver);
+}
+
+module_init(ns9xxx_wdt_init);
+module_exit(ns9xxx_wdt_exit);
+
+MODULE_AUTHOR("Digi International Inc.");
+MODULE_DESCRIPTION("Digi NS9xxx Watchdog Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/watchdog/stmp3xxx_wdt.c b/drivers/watchdog/stmp3xxx_wdt.c
new file mode 100644
index 000000000000..72b0e0d01c00
--- /dev/null
+++ b/drivers/watchdog/stmp3xxx_wdt.c
@@ -0,0 +1,301 @@
+/*
+ * Watchdog driver for Freescale STMP37XX/STMP378X
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+
+#include <mach/regs-rtc.h>
+
+#define DEFAULT_HEARTBEAT 19
+#define MAX_HEARTBEAT (0x10000000 >> 6) /* actually 0x100000000 >> 10 */
+
+/* missing bitmask in headers */
+#define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER 0x80000000
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+#define WDT_REGION_INITED 2
+#define WDT_DEVICE_INITED 3
+
+#define WDOG_COUNTER_RATE 1000 /* 1 kHz clock */
+
+DEFINE_SPINLOCK(io_lock);
+static unsigned long wdt_status;
+static const int nowayout = WATCHDOG_NOWAYOUT;
+static int heartbeat = DEFAULT_HEARTBEAT;
+static unsigned long boot_status;
+
+
+static void wdt_enable(u32 value)
+{
+ spin_lock(&io_lock);
+ HW_RTC_WATCHDOG_WR(value);
+ HW_RTC_CTRL_SET(BM_RTC_CTRL_WATCHDOGEN);
+ HW_RTC_PERSISTENT1_SET(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER);
+ spin_unlock(&io_lock);
+}
+
+static void wdt_disable(void)
+{
+ spin_lock(&io_lock);
+ HW_RTC_PERSISTENT1_CLR(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER);
+ HW_RTC_CTRL_CLR(BM_RTC_CTRL_WATCHDOGEN);
+ spin_unlock(&io_lock);
+}
+
+static int stmp3xxx_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ wdt_enable(heartbeat * WDOG_COUNTER_RATE);
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t
+stmp3xxx_wdt_write(struct file *file, const char *data, size_t len,
+ loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_enable(heartbeat * WDOG_COUNTER_RATE);
+ }
+
+ return len;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET |
+ WDIOF_MAGICCLOSE |
+ WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = "STMP37XX Watchdog",
+};
+
+static int
+stmp3xxx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_heartbeat, opts;
+ int ret = -ENOTTY;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int __user *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(boot_status, (int __user *)arg);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ get_user(opts, (int __user *)arg);
+ if (opts & WDIOS_DISABLECARD)
+ wdt_disable();
+ else if (opts & WDIOS_ENABLECARD)
+ wdt_enable(heartbeat * WDOG_COUNTER_RATE);
+ else {
+ pr_debug("%s: unknown option 0x%x\n", __func__, opts);
+ ret = -EINVAL;
+ }
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ get_user(new_heartbeat, (int __user *)arg);
+ if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) {
+ ret = -EINVAL;
+ break;
+ }
+
+ heartbeat = new_heartbeat;
+ wdt_enable(heartbeat * WDOG_COUNTER_RATE);
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int __user *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable(heartbeat * WDOG_COUNTER_RATE);
+ ret = 0;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int stmp3xxx_wdt_release(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+
+ if (!nowayout) {
+ if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
+ pr_debug("%s: Device closed unexpectdly\n", __func__);
+ ret = -EINVAL;
+ } else {
+ wdt_disable();
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ clear_bit(WDT_IN_USE, &wdt_status);
+
+ return ret;
+}
+
+static const struct file_operations stmp3xxx_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = stmp3xxx_wdt_write,
+ .ioctl = stmp3xxx_wdt_ioctl,
+ .open = stmp3xxx_wdt_open,
+ .release = stmp3xxx_wdt_release,
+};
+
+static struct miscdevice stmp3xxx_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &stmp3xxx_wdt_fops,
+};
+
+static int stmp3xxx_wdt_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+
+ if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
+ heartbeat = DEFAULT_HEARTBEAT;
+
+ boot_status = HW_RTC_PERSISTENT1_RD() &
+ BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER;
+ boot_status = !!boot_status;
+ HW_RTC_PERSISTENT1_CLR(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER);
+
+ dev_dbg(&pdev->dev, "STMP37XX Watchdog Timer: heartbeat %d sec\n",
+ heartbeat);
+
+ ret = misc_register(&stmp3xxx_wdt_miscdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot register misc device\n");
+ } else {
+ wdt_disable(); /* disable for now */
+ set_bit(WDT_DEVICE_INITED, &wdt_status);
+ }
+
+ return ret;
+}
+
+static int stmp3xxx_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&stmp3xxx_wdt_miscdev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wdt_suspended;
+static u32 wdt_saved_time;
+
+static int stmp3xxx_wdt_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ if (HW_RTC_CTRL_RD() & BM_RTC_CTRL_WATCHDOGEN) {
+ wdt_suspended = 1;
+ wdt_saved_time = HW_RTC_WATCHDOG_RD();
+ wdt_disable();
+ }
+ return 0;
+}
+
+static int stmp3xxx_wdt_resume(struct platform_device *pdev)
+{
+ if (wdt_suspended) {
+ wdt_enable(wdt_saved_time);
+ wdt_suspended = 0;
+ }
+ return 0;
+}
+#else
+#define stmp3xxx_wdt_suspend NULL
+#define stmp3xxx_wdt_resume NULL
+#endif
+
+static struct platform_driver platform_wdt_driver = {
+ .driver = {
+ .name = "stmp3xxx_wdt",
+ },
+ .probe = stmp3xxx_wdt_probe,
+ .remove = __devexit_p(stmp3xxx_wdt_remove),
+ .suspend = stmp3xxx_wdt_suspend,
+ .resume = stmp3xxx_wdt_resume,
+};
+
+static int __init stmp3xxx_wdt_init(void)
+{
+ return platform_driver_register(&platform_wdt_driver);
+}
+
+static void __exit stmp3xxx_wdt_exit(void)
+{
+ return platform_driver_unregister(&platform_wdt_driver);
+}
+
+module_init(stmp3xxx_wdt_init);
+module_exit(stmp3xxx_wdt_exit);
+
+MODULE_DESCRIPTION("STMP37XX Watchdog Driver");
+MODULE_LICENSE("GPL");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat period in seconds from 1 to "
+ __MODULE_STRING(MAX_HEARTBEAT) ", default "
+ __MODULE_STRING(DEFAULT_HEARTBEAT));
+
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/fs/Kconfig b/fs/Kconfig
index 522469a7eca3..b3ecd032c77e 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -931,6 +931,56 @@ config CRAMFS
If unsure, say N.
+config SQUASHFS
+ tristate "SquashFS 3.4 - Squashed file system support"
+ select ZLIB_INFLATE
+ help
+ Saying Y here includes support for SquashFS 3.4 (a Compressed
+ Read-Only File System). Squashfs is a highly compressed read-only
+ filesystem for Linux. It uses zlib compression to compress both
+ files, inodes and directories. Inodes in the system are very small
+ and all blocks are packed to minimise data overhead. Block sizes
+ greater than 4K are supported up to a maximum of 1 Mbytes (default
+ block size 128K). SquashFS 3.3 supports 64 bit filesystems and files
+ (larger than 4GB), full uid/gid information, hard links and timestamps.
+
+ Squashfs is intended for general read-only filesystem use, for
+ archival use (i.e. in cases where a .tar.gz file may be used), and in
+ embedded systems where low overhead is needed. Further information
+ and filesystem tools are available from http://squashfs.sourceforge.net.
+
+ If you want to compile this as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read <file:Documentation/modules.txt>. The module
+ will be called squashfs. Note that the root file system (the one
+ containing the directory /) cannot be compiled as a module.
+
+ If unsure, say N.
+
+config SQUASHFS_EMBEDDED
+
+ bool "Additional option for memory-constrained systems"
+ depends on SQUASHFS
+ default n
+ help
+ Saying Y here allows you to specify cache size.
+
+ If unsure, say N.
+
+config SQUASHFS_FRAGMENT_CACHE_SIZE
+ int "Number of fragments cached" if SQUASHFS_EMBEDDED
+ depends on SQUASHFS
+ default "3"
+ help
+ By default SquashFS caches the last 3 fragments read from
+ the filesystem. Increasing this amount may mean SquashFS
+ has to re-read fragments less often from disk, at the expense
+ of extra system memory. Decreasing this amount will mean
+ SquashFS uses less memory at the expense of extra reads from disk.
+
+ Note there must be at least one cached fragment. Anything
+ much more than three will probably not make much difference.
+
config VXFS_FS
tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)"
depends on BLOCK
diff --git a/fs/Makefile b/fs/Makefile
index d9f8afe6f0c4..c56718026f03 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_JBD) += jbd/
obj-$(CONFIG_JBD2) += jbd2/
obj-$(CONFIG_EXT2_FS) += ext2/
obj-$(CONFIG_CRAMFS) += cramfs/
+obj-$(CONFIG_SQUASHFS) += squashfs/
obj-y += ramfs/
obj-$(CONFIG_HUGETLBFS) += hugetlbfs/
obj-$(CONFIG_CODA_FS) += coda/
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 549daf8005fb..d0ac382da6e9 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -140,6 +140,7 @@ asmlinkage long sys_dup(unsigned int fildes)
}
return ret;
}
+EXPORT_SYMBOL(sys_dup);
#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT | O_NOATIME)
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index 249305d65d5b..cf677ad82e3e 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -680,7 +680,9 @@ void jffs2_gc_release_page(struct jffs2_sb_info *c,
static int jffs2_flash_setup(struct jffs2_sb_info *c) {
int ret = 0;
- if (jffs2_cleanmarker_oob(c)) {
+ if (c->mtd->type == MTD_NANDFLASH) {
+ if (!(c->mtd->flags & MTD_OOB_WRITEABLE))
+ printk(KERN_INFO "JFFS2 doesn't use OOB.\n");
/* NAND flash... do setup accordingly */
ret = jffs2_nand_flash_setup(c);
if (ret)
@@ -713,7 +715,7 @@ static int jffs2_flash_setup(struct jffs2_sb_info *c) {
void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
- if (jffs2_cleanmarker_oob(c)) {
+ if (c->mtd->type == MTD_NANDFLASH) {
jffs2_nand_flash_cleanup(c);
}
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
index 5e194a5c8e29..a0f7af6aa69b 100644
--- a/fs/jffs2/os-linux.h
+++ b/fs/jffs2/os-linux.h
@@ -110,7 +110,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
#define jffs2_can_mark_obsolete(c) (c->mtd->flags & (MTD_BIT_WRITEABLE))
#endif
-#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
+#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH && (c->mtd->flags & MTD_OOB_WRITEABLE))
#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf))
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 1d437de1e9a8..b07934bcf2fd 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -112,7 +112,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
if (!flashbuf) {
/* For NAND it's quicker to read a whole eraseblock at a time,
apparently */
- if (jffs2_cleanmarker_oob(c))
+ if (c->mtd->type == MTD_NANDFLASH)
buf_size = c->sector_size;
else
buf_size = PAGE_SIZE;
@@ -449,22 +449,24 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
- if (jffs2_cleanmarker_oob(c)) {
+ if (c->mtd->type == MTD_NANDFLASH) {
int ret;
if (c->mtd->block_isbad(c->mtd, jeb->offset))
return BLK_STATE_BADBLOCK;
- ret = jffs2_check_nand_cleanmarker(c, jeb);
- D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
+ if (jffs2_cleanmarker_oob(c)) {
+ ret = jffs2_check_nand_cleanmarker(c, jeb);
+ D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n", ret));
- /* Even if it's not found, we still scan to see
- if the block is empty. We use this information
- to decide whether to erase it or not. */
- switch (ret) {
- case 0: cleanmarkerfound = 1; break;
- case 1: break;
- default: return ret;
+ /* Even if it's not found, we still scan to see
+ if the block is empty. We use this information
+ to decide whether to erase it or not. */
+ switch (ret) {
+ case 0: cleanmarkerfound = 1; break;
+ case 1: break;
+ default: return ret;
+ }
}
}
#endif
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index 6063a8e4b9f3..c4a08e266b72 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -716,7 +716,9 @@ struct lock_manager_operations nlmsvc_lock_operations = {
static void
nlmsvc_grant_blocked(struct nlm_block *block)
{
+#ifdef CONFIG_FILE_LOCKING
struct nlm_file *file = block->b_file;
+#endif
struct nlm_lock *lock = &block->b_call->a_args.lock;
int error;
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
index afd08e2c90a5..70219c9a4659 100644
--- a/fs/nfsd/nfsxdr.c
+++ b/fs/nfsd/nfsxdr.c
@@ -151,7 +151,9 @@ static __be32 *
encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
struct kstat *stat)
{
+#ifdef CONFIG_FILE_LOCKING
struct dentry *dentry = fhp->fh_dentry;
+#endif
int type;
struct timespec time;
u32 f;
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
new file mode 100644
index 000000000000..1bc7b0609e23
--- /dev/null
+++ b/fs/squashfs/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the linux squashfs routines.
+#
+
+obj-$(CONFIG_SQUASHFS) += squashfs.o
+squashfs-y += inode.o
+squashfs-y += squashfs2_0.o
diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c
new file mode 100644
index 000000000000..a9b9d8c7dfad
--- /dev/null
+++ b/fs/squashfs/inode.c
@@ -0,0 +1,2157 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * 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,
+ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * inode.c
+ */
+
+#include <linux/squashfs_fs.h>
+#include <linux/module.h>
+#include <linux/zlib.h>
+#include <linux/fs.h>
+#include <linux/squashfs_fs_sb.h>
+#include <linux/squashfs_fs_i.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <linux/vmalloc.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/exportfs.h>
+
+#include "squashfs.h"
+
+static struct dentry *squashfs_fh_to_dentry(struct super_block *s,
+ struct fid *fid, int fh_len, int fh_type);
+static struct dentry *squashfs_fh_to_parent(struct super_block *s,
+ struct fid *fid, int fh_len, int fh_type);
+static struct dentry *squashfs_get_parent(struct dentry *child);
+static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode);
+static int squashfs_statfs(struct dentry *, struct kstatfs *);
+static int squashfs_symlink_readpage(struct file *file, struct page *page);
+static long long read_blocklist(struct inode *inode, int index,
+ int readahead_blks, char *block_list,
+ unsigned short **block_p, unsigned int *bsize);
+static int squashfs_readpage(struct file *file, struct page *page);
+static int squashfs_readdir(struct file *, void *, filldir_t);
+static struct dentry *squashfs_lookup(struct inode *, struct dentry *,
+ struct nameidata *);
+static int squashfs_remount(struct super_block *s, int *flags, char *data);
+static void squashfs_put_super(struct super_block *);
+static int squashfs_get_sb(struct file_system_type *,int, const char *, void *,
+ struct vfsmount *);
+static struct inode *squashfs_alloc_inode(struct super_block *sb);
+static void squashfs_destroy_inode(struct inode *inode);
+static int init_inodecache(void);
+static void destroy_inodecache(void);
+
+static struct file_system_type squashfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "squashfs",
+ .get_sb = squashfs_get_sb,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV
+};
+
+static const unsigned char squashfs_filetype_table[] = {
+ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
+};
+
+static struct super_operations squashfs_super_ops = {
+ .alloc_inode = squashfs_alloc_inode,
+ .destroy_inode = squashfs_destroy_inode,
+ .statfs = squashfs_statfs,
+ .put_super = squashfs_put_super,
+ .remount_fs = squashfs_remount
+};
+
+static struct export_operations squashfs_export_ops = {
+ .fh_to_dentry = squashfs_fh_to_dentry,
+ .fh_to_parent = squashfs_fh_to_parent,
+ .get_parent = squashfs_get_parent
+};
+
+SQSH_EXTERN const struct address_space_operations squashfs_symlink_aops = {
+ .readpage = squashfs_symlink_readpage
+};
+
+SQSH_EXTERN const struct address_space_operations squashfs_aops = {
+ .readpage = squashfs_readpage
+};
+
+static const struct file_operations squashfs_dir_ops = {
+ .read = generic_read_dir,
+ .readdir = squashfs_readdir
+};
+
+SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = {
+ .lookup = squashfs_lookup
+};
+
+
+static struct buffer_head *get_block_length(struct super_block *s,
+ int *cur_index, int *offset, int *c_byte)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ unsigned short temp;
+ struct buffer_head *bh;
+
+ if (!(bh = sb_bread(s, *cur_index)))
+ goto out;
+
+ if (msblk->devblksize - *offset == 1) {
+ if (msblk->swap)
+ ((unsigned char *) &temp)[1] = *((unsigned char *)
+ (bh->b_data + *offset));
+ else
+ ((unsigned char *) &temp)[0] = *((unsigned char *)
+ (bh->b_data + *offset));
+ brelse(bh);
+ if (!(bh = sb_bread(s, ++(*cur_index))))
+ goto out;
+ if (msblk->swap)
+ ((unsigned char *) &temp)[0] = *((unsigned char *)
+ bh->b_data);
+ else
+ ((unsigned char *) &temp)[1] = *((unsigned char *)
+ bh->b_data);
+ *c_byte = temp;
+ *offset = 1;
+ } else {
+ if (msblk->swap) {
+ ((unsigned char *) &temp)[1] = *((unsigned char *)
+ (bh->b_data + *offset));
+ ((unsigned char *) &temp)[0] = *((unsigned char *)
+ (bh->b_data + *offset + 1));
+ } else {
+ ((unsigned char *) &temp)[0] = *((unsigned char *)
+ (bh->b_data + *offset));
+ ((unsigned char *) &temp)[1] = *((unsigned char *)
+ (bh->b_data + *offset + 1));
+ }
+ *c_byte = temp;
+ *offset += 2;
+ }
+
+ if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) {
+ if (*offset == msblk->devblksize) {
+ brelse(bh);
+ if (!(bh = sb_bread(s, ++(*cur_index))))
+ goto out;
+ *offset = 0;
+ }
+ if (*((unsigned char *) (bh->b_data + *offset)) !=
+ SQUASHFS_MARKER_BYTE) {
+ ERROR("Metadata block marker corrupt @ %x\n",
+ *cur_index);
+ brelse(bh);
+ goto out;
+ }
+ (*offset)++;
+ }
+ return bh;
+
+out:
+ return NULL;
+}
+
+
+SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
+ long long index, unsigned int length,
+ long long *next_index, int srclength)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ struct buffer_head **bh;
+ unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1);
+ unsigned int cur_index = index >> msblk->devblksize_log2;
+ int bytes, avail_bytes, b = 0, k = 0;
+ unsigned int compressed;
+ unsigned int c_byte = length;
+
+ bh = kmalloc(((sblk->block_size >> msblk->devblksize_log2) + 1) *
+ sizeof(struct buffer_head *), GFP_KERNEL);
+ if (bh == NULL)
+ goto read_failure;
+
+ if (c_byte) {
+ bytes = -offset;
+ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte);
+ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
+
+ TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index,
+ compressed ? "" : "un", (unsigned int) c_byte, srclength);
+
+ if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used)
+ goto read_failure;
+
+ for (b = 0; bytes < (int) c_byte; b++, cur_index++) {
+ bh[b] = sb_getblk(s, cur_index);
+ if (bh[b] == NULL)
+ goto block_release;
+ bytes += msblk->devblksize;
+ }
+ ll_rw_block(READ, b, bh);
+ } else {
+ if (index < 0 || (index + 2) > sblk->bytes_used)
+ goto read_failure;
+
+ bh[0] = get_block_length(s, &cur_index, &offset, &c_byte);
+ if (bh[0] == NULL)
+ goto read_failure;
+ b = 1;
+
+ bytes = msblk->devblksize - offset;
+ compressed = SQUASHFS_COMPRESSED(c_byte);
+ c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
+
+ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
+ ? "" : "un", (unsigned int) c_byte);
+
+ if (c_byte > srclength || (index + c_byte) > sblk->bytes_used)
+ goto block_release;
+
+ for (; bytes < c_byte; b++) {
+ bh[b] = sb_getblk(s, ++cur_index);
+ if (bh[b] == NULL)
+ goto block_release;
+ bytes += msblk->devblksize;
+ }
+ ll_rw_block(READ, b - 1, bh + 1);
+ }
+
+ if (compressed) {
+ int zlib_err = 0;
+
+ /*
+ * uncompress block
+ */
+
+ mutex_lock(&msblk->read_data_mutex);
+
+ msblk->stream.next_out = buffer;
+ msblk->stream.avail_out = srclength;
+
+ for (bytes = 0; k < b; k++) {
+ avail_bytes = min(c_byte - bytes, msblk->devblksize - offset);
+
+ wait_on_buffer(bh[k]);
+ if (!buffer_uptodate(bh[k]))
+ goto release_mutex;
+
+ msblk->stream.next_in = bh[k]->b_data + offset;
+ msblk->stream.avail_in = avail_bytes;
+
+ if (k == 0) {
+ zlib_err = zlib_inflateInit(&msblk->stream);
+ if (zlib_err != Z_OK) {
+ ERROR("zlib_inflateInit returned unexpected result 0x%x,"
+ " srclength %d\n", zlib_err, srclength);
+ goto release_mutex;
+ }
+
+ if (avail_bytes == 0) {
+ offset = 0;
+ brelse(bh[k]);
+ continue;
+ }
+ }
+
+ zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH);
+ if (zlib_err != Z_OK && zlib_err != Z_STREAM_END) {
+ ERROR("zlib_inflate returned unexpected result 0x%x,"
+ " srclength %d, avail_in %d, avail_out %d\n", zlib_err,
+ srclength, msblk->stream.avail_in, msblk->stream.avail_out);
+ goto release_mutex;
+ }
+
+ bytes += avail_bytes;
+ offset = 0;
+ brelse(bh[k]);
+ }
+
+ if (zlib_err != Z_STREAM_END)
+ goto release_mutex;
+
+ zlib_err = zlib_inflateEnd(&msblk->stream);
+ if (zlib_err != Z_OK) {
+ ERROR("zlib_inflateEnd returned unexpected result 0x%x,"
+ " srclength %d\n", zlib_err, srclength);
+ goto release_mutex;
+ }
+ bytes = msblk->stream.total_out;
+ mutex_unlock(&msblk->read_data_mutex);
+ } else {
+ int i;
+
+ for(i = 0; i < b; i++) {
+ wait_on_buffer(bh[i]);
+ if (!buffer_uptodate(bh[i]))
+ goto block_release;
+ }
+
+ for (bytes = 0; k < b; k++) {
+ avail_bytes = min(c_byte - bytes, msblk->devblksize - offset);
+
+ memcpy(buffer + bytes, bh[k]->b_data + offset, avail_bytes);
+ bytes += avail_bytes;
+ offset = 0;
+ brelse(bh[k]);
+ }
+ }
+
+ if (next_index)
+ *next_index = index + c_byte + (length ? 0 :
+ (SQUASHFS_CHECK_DATA(msblk->sblk.flags) ? 3 : 2));
+
+ kfree(bh);
+ return bytes;
+
+release_mutex:
+ mutex_unlock(&msblk->read_data_mutex);
+
+block_release:
+ for (; k < b; k++)
+ brelse(bh[k]);
+
+read_failure:
+ ERROR("sb_bread failed reading block 0x%x\n", cur_index);
+ kfree(bh);
+ return 0;
+}
+
+
+static struct squashfs_cache_entry *squashfs_cache_get(struct super_block *s,
+ struct squashfs_cache *cache, long long block, int length)
+{
+ int i, n;
+ struct squashfs_cache_entry *entry;
+
+ spin_lock(&cache->lock);
+
+ while (1) {
+ for (i = 0; i < cache->entries && cache->entry[i].block != block; i++);
+
+ if (i == cache->entries) {
+ if (cache->unused_blks == 0) {
+ cache->waiting ++;
+ spin_unlock(&cache->lock);
+ wait_event(cache->wait_queue, cache->unused_blks);
+ spin_lock(&cache->lock);
+ cache->waiting --;
+ continue;
+ }
+
+ i = cache->next_blk;
+ for (n = 0; n < cache->entries; n++) {
+ if (cache->entry[i].locked == 0)
+ break;
+ i = (i + 1) % cache->entries;
+ }
+
+ cache->next_blk = (i + 1) % cache->entries;
+ entry = &cache->entry[i];
+
+ cache->unused_blks --;
+ entry->block = block;
+ entry->locked = 1;
+ entry->pending = 1;
+ entry->waiting = 0;
+ entry->error = 0;
+ spin_unlock(&cache->lock);
+
+ entry->length = squashfs_read_data(s, entry->data,
+ block, length, &entry->next_index, cache->block_size);
+
+ spin_lock(&cache->lock);
+
+ if (entry->length == 0)
+ entry->error = 1;
+
+ entry->pending = 0;
+ spin_unlock(&cache->lock);
+ if (entry->waiting)
+ wake_up_all(&entry->wait_queue);
+ goto out;
+ }
+
+ entry = &cache->entry[i];
+ if (entry->locked == 0)
+ cache->unused_blks --;
+ entry->locked++;
+
+ if (entry->pending) {
+ entry->waiting ++;
+ spin_unlock(&cache->lock);
+ wait_event(entry->wait_queue, !entry->pending);
+ goto out;
+ }
+
+ spin_unlock(&cache->lock);
+ goto out;
+ }
+
+out:
+ TRACE("Got %s %d, start block %lld, locked %d, error %d\n", i,
+ cache->name, entry->block, entry->locked, entry->error);
+ if (entry->error)
+ ERROR("Unable to read %s cache entry [%llx]\n", cache->name, block);
+ return entry;
+}
+
+
+static void squashfs_cache_put(struct squashfs_cache *cache,
+ struct squashfs_cache_entry *entry)
+{
+ spin_lock(&cache->lock);
+ entry->locked --;
+ if (entry->locked == 0) {
+ cache->unused_blks ++;
+ spin_unlock(&cache->lock);
+ if (cache->waiting)
+ wake_up(&cache->wait_queue);
+ } else
+ spin_unlock(&cache->lock);
+}
+
+
+static void squashfs_cache_delete(struct squashfs_cache *cache)
+{
+ int i;
+
+ if (cache == NULL)
+ return;
+
+ for (i = 0; i < cache->entries; i++)
+ if (cache->entry[i].data) {
+ if (cache->use_vmalloc)
+ vfree(cache->entry[i].data);
+ else
+ kfree(cache->entry[i].data);
+ }
+
+ kfree(cache);
+}
+
+
+static struct squashfs_cache *squashfs_cache_init(char *name, int entries,
+ int block_size, int use_vmalloc)
+{
+ int i;
+ struct squashfs_cache *cache = kzalloc(sizeof(struct squashfs_cache) +
+ entries * sizeof(struct squashfs_cache_entry), GFP_KERNEL);
+ if (cache == NULL) {
+ ERROR("Failed to allocate %s cache\n", name);
+ goto failed;
+ }
+
+ cache->next_blk = 0;
+ cache->unused_blks = entries;
+ cache->entries = entries;
+ cache->block_size = block_size;
+ cache->use_vmalloc = use_vmalloc;
+ cache->name = name;
+ cache->waiting = 0;
+ spin_lock_init(&cache->lock);
+ init_waitqueue_head(&cache->wait_queue);
+
+ for (i = 0; i < entries; i++) {
+ init_waitqueue_head(&cache->entry[i].wait_queue);
+ cache->entry[i].block = SQUASHFS_INVALID_BLK;
+ cache->entry[i].data = use_vmalloc ? vmalloc(block_size) :
+ kmalloc(block_size, GFP_KERNEL);
+ if (cache->entry[i].data == NULL) {
+ ERROR("Failed to allocate %s cache entry\n", name);
+ goto cleanup;
+ }
+ }
+
+ return cache;
+
+cleanup:
+ squashfs_cache_delete(cache);
+failed:
+ return NULL;
+}
+
+
+SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, void *buffer,
+ long long block, unsigned int offset,
+ int length, long long *next_block,
+ unsigned int *next_offset)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ int bytes, return_length = length;
+ struct squashfs_cache_entry *entry;
+
+ TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset);
+
+ while (1) {
+ entry = squashfs_cache_get(s, msblk->block_cache, block, 0);
+ bytes = entry->length - offset;
+
+ if (entry->error || bytes < 1) {
+ return_length = 0;
+ goto finish;
+ } else if (bytes >= length) {
+ if (buffer)
+ memcpy(buffer, entry->data + offset, length);
+ if (entry->length - offset == length) {
+ *next_block = entry->next_index;
+ *next_offset = 0;
+ } else {
+ *next_block = block;
+ *next_offset = offset + length;
+ }
+ goto finish;
+ } else {
+ if (buffer) {
+ memcpy(buffer, entry->data + offset, bytes);
+ buffer = (char *) buffer + bytes;
+ }
+ block = entry->next_index;
+ squashfs_cache_put(msblk->block_cache, entry);
+ length -= bytes;
+ offset = 0;
+ }
+ }
+
+finish:
+ squashfs_cache_put(msblk->block_cache, entry);
+ return return_length;
+}
+
+
+static int get_fragment_location(struct super_block *s, unsigned int fragment,
+ long long *fragment_start_block,
+ unsigned int *fragment_size)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ long long start_block =
+ msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)];
+ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
+ struct squashfs_fragment_entry fragment_entry;
+
+ if (msblk->swap) {
+ struct squashfs_fragment_entry sfragment_entry;
+
+ if (!squashfs_get_cached_block(s, &sfragment_entry, start_block, offset,
+ sizeof(sfragment_entry), &start_block, &offset))
+ goto out;
+ SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry);
+ } else
+ if (!squashfs_get_cached_block(s, &fragment_entry, start_block, offset,
+ sizeof(fragment_entry), &start_block, &offset))
+ goto out;
+
+ *fragment_start_block = fragment_entry.start_block;
+ *fragment_size = fragment_entry.size;
+
+ return 1;
+
+out:
+ return 0;
+}
+
+
+SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk,
+ struct squashfs_cache_entry *fragment)
+{
+ squashfs_cache_put(msblk->fragment_cache, fragment);
+}
+
+
+SQSH_EXTERN
+struct squashfs_cache_entry *get_cached_fragment(struct super_block *s,
+ long long start_block, int length)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+
+ return squashfs_cache_get(s, msblk->fragment_cache, start_block, length);
+}
+
+
+static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
+ struct squashfs_base_inode_header *inodeb)
+{
+ i->i_ino = inodeb->inode_number;
+ i->i_mtime.tv_sec = inodeb->mtime;
+ i->i_atime.tv_sec = inodeb->mtime;
+ i->i_ctime.tv_sec = inodeb->mtime;
+ i->i_uid = msblk->uid[inodeb->uid];
+ i->i_mode = inodeb->mode;
+ i->i_size = 0;
+
+ if (inodeb->guid == SQUASHFS_GUIDS)
+ i->i_gid = i->i_uid;
+ else
+ i->i_gid = msblk->guid[inodeb->guid];
+}
+
+
+static squashfs_inode_t squashfs_inode_lookup(struct super_block *s, int ino)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ long long start = msblk->inode_lookup_table[SQUASHFS_LOOKUP_BLOCK(ino - 1)];
+ int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino - 1);
+ squashfs_inode_t inode;
+
+ TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino);
+
+ if (msblk->swap) {
+ squashfs_inode_t sinode;
+
+ if (!squashfs_get_cached_block(s, &sinode, start, offset,
+ sizeof(sinode), &start, &offset))
+ goto out;
+ SQUASHFS_SWAP_INODE_T((&inode), &sinode);
+ } else if (!squashfs_get_cached_block(s, &inode, start, offset,
+ sizeof(inode), &start, &offset))
+ goto out;
+
+ TRACE("squashfs_inode_lookup, inode = 0x%llx\n", inode);
+
+ return inode;
+
+out:
+ return SQUASHFS_INVALID_BLK;
+}
+
+
+
+static struct dentry *squashfs_export_iget(struct super_block *s,
+ unsigned int inode_number)
+{
+ squashfs_inode_t inode;
+ struct dentry *dentry = ERR_PTR(-ENOENT);
+
+ TRACE("Entered squashfs_export_iget\n");
+
+ inode = squashfs_inode_lookup(s, inode_number);
+ if(inode != SQUASHFS_INVALID_BLK)
+ dentry = d_obtain_alias(squashfs_iget(s, inode, inode_number));
+
+ return dentry;
+}
+
+
+static struct dentry *squashfs_fh_to_dentry(struct super_block *s,
+ struct fid *fid, int fh_len, int fh_type)
+{
+ if((fh_type != FILEID_INO32_GEN && fh_type != FILEID_INO32_GEN_PARENT) ||
+ fh_len < 2)
+ return NULL;
+
+ return squashfs_export_iget(s, fid->i32.ino);
+}
+
+
+static struct dentry *squashfs_fh_to_parent(struct super_block *s,
+ struct fid *fid, int fh_len, int fh_type)
+{
+ if(fh_type != FILEID_INO32_GEN_PARENT || fh_len < 4)
+ return NULL;
+
+ return squashfs_export_iget(s, fid->i32.parent_ino);
+}
+
+
+static struct dentry *squashfs_get_parent(struct dentry *child)
+{
+ struct inode *i = child->d_inode;
+
+ TRACE("Entered squashfs_get_parent\n");
+
+ return squashfs_export_iget(i->i_sb, SQUASHFS_I(i)->u.s2.parent_inode);
+}
+
+
+SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s,
+ squashfs_inode_t inode, unsigned int inode_number)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct inode *i = iget_locked(s, inode_number);
+
+ TRACE("Entered squashfs_iget\n");
+
+ if(i && (i->i_state & I_NEW)) {
+ (msblk->read_inode)(i, inode);
+ unlock_new_inode(i);
+ }
+
+ return i;
+}
+
+
+static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode)
+{
+ struct super_block *s = i->i_sb;
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ long long block = SQUASHFS_INODE_BLK(inode) + sblk->inode_table_start;
+ unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
+ long long next_block;
+ unsigned int next_offset;
+ union squashfs_inode_header id, sid;
+ struct squashfs_base_inode_header *inodeb = &id.base, *sinodeb = &sid.base;
+
+ TRACE("Entered squashfs_read_inode\n");
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, sinodeb, block, offset,
+ sizeof(*sinodeb), &next_block, &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, sizeof(*sinodeb));
+ } else
+ if (!squashfs_get_cached_block(s, inodeb, block, offset,
+ sizeof(*inodeb), &next_block, &next_offset))
+ goto failed_read;
+
+ squashfs_new_inode(msblk, i, inodeb);
+
+ switch(inodeb->inode_type) {
+ case SQUASHFS_FILE_TYPE: {
+ unsigned int frag_size;
+ long long frag_blk;
+ struct squashfs_reg_inode_header *inodep = &id.reg;
+ struct squashfs_reg_inode_header *sinodep = &sid.reg;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, sinodep, block, offset,
+ sizeof(*sinodep), &next_block, &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, inodep, block, offset,
+ sizeof(*inodep), &next_block, &next_offset))
+ goto failed_read;
+
+ frag_blk = SQUASHFS_INVALID_BLK;
+
+ if (inodep->fragment != SQUASHFS_INVALID_FRAG)
+ if(!get_fragment_location(s, inodep->fragment, &frag_blk,
+ &frag_size))
+ goto failed_read;
+
+ i->i_nlink = 1;
+ i->i_size = inodep->file_size;
+ i->i_fop = &generic_ro_fops;
+ i->i_mode |= S_IFREG;
+ i->i_blocks = ((i->i_size - 1) >> 9) + 1;
+ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
+ SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
+ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
+ SQUASHFS_I(i)->start_block = inodep->start_block;
+ SQUASHFS_I(i)->u.s1.block_list_start = next_block;
+ SQUASHFS_I(i)->offset = next_offset;
+ i->i_data.a_ops = &squashfs_aops;
+
+ TRACE("File inode %x:%x, start_block %llx, "
+ "block_list_start %llx, offset %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ inodep->start_block, next_block,
+ next_offset);
+ break;
+ }
+ case SQUASHFS_LREG_TYPE: {
+ unsigned int frag_size;
+ long long frag_blk;
+ struct squashfs_lreg_inode_header *inodep = &id.lreg;
+ struct squashfs_lreg_inode_header *sinodep = &sid.lreg;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, sinodep, block, offset,
+ sizeof(*sinodep), &next_block, &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, inodep, block, offset,
+ sizeof(*inodep), &next_block, &next_offset))
+ goto failed_read;
+
+ frag_blk = SQUASHFS_INVALID_BLK;
+
+ if (inodep->fragment != SQUASHFS_INVALID_FRAG)
+ if (!get_fragment_location(s, inodep->fragment, &frag_blk,
+ &frag_size))
+ goto failed_read;
+
+ i->i_nlink = inodep->nlink;
+ i->i_size = inodep->file_size;
+ i->i_fop = &generic_ro_fops;
+ i->i_mode |= S_IFREG;
+ i->i_blocks = ((i->i_size - 1) >> 9) + 1;
+ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
+ SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
+ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
+ SQUASHFS_I(i)->start_block = inodep->start_block;
+ SQUASHFS_I(i)->u.s1.block_list_start = next_block;
+ SQUASHFS_I(i)->offset = next_offset;
+ i->i_data.a_ops = &squashfs_aops;
+
+ TRACE("File inode %x:%x, start_block %llx, "
+ "block_list_start %llx, offset %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ inodep->start_block, next_block,
+ next_offset);
+ break;
+ }
+ case SQUASHFS_DIR_TYPE: {
+ struct squashfs_dir_inode_header *inodep = &id.dir;
+ struct squashfs_dir_inode_header *sinodep = &sid.dir;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, sinodep, block, offset,
+ sizeof(*sinodep), &next_block, &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, inodep, block, offset,
+ sizeof(*inodep), &next_block, &next_offset))
+ goto failed_read;
+
+ i->i_nlink = inodep->nlink;
+ i->i_size = inodep->file_size;
+ i->i_op = &squashfs_dir_inode_ops;
+ i->i_fop = &squashfs_dir_ops;
+ i->i_mode |= S_IFDIR;
+ SQUASHFS_I(i)->start_block = inodep->start_block;
+ SQUASHFS_I(i)->offset = inodep->offset;
+ SQUASHFS_I(i)->u.s2.directory_index_count = 0;
+ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
+
+ TRACE("Directory inode %x:%x, start_block %x, offset "
+ "%x\n", SQUASHFS_INODE_BLK(inode),
+ offset, inodep->start_block,
+ inodep->offset);
+ break;
+ }
+ case SQUASHFS_LDIR_TYPE: {
+ struct squashfs_ldir_inode_header *inodep = &id.ldir;
+ struct squashfs_ldir_inode_header *sinodep = &sid.ldir;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, sinodep, block, offset,
+ sizeof(*sinodep), &next_block, &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, inodep, block, offset,
+ sizeof(*inodep), &next_block, &next_offset))
+ goto failed_read;
+
+ i->i_nlink = inodep->nlink;
+ i->i_size = inodep->file_size;
+ i->i_op = &squashfs_dir_inode_ops;
+ i->i_fop = &squashfs_dir_ops;
+ i->i_mode |= S_IFDIR;
+ SQUASHFS_I(i)->start_block = inodep->start_block;
+ SQUASHFS_I(i)->offset = inodep->offset;
+ SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
+ SQUASHFS_I(i)->u.s2.directory_index_offset = next_offset;
+ SQUASHFS_I(i)->u.s2.directory_index_count = inodep->i_count;
+ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
+
+ TRACE("Long directory inode %x:%x, start_block %x, offset %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ inodep->start_block, inodep->offset);
+ break;
+ }
+ case SQUASHFS_SYMLINK_TYPE: {
+ struct squashfs_symlink_inode_header *inodep = &id.symlink;
+ struct squashfs_symlink_inode_header *sinodep = &sid.symlink;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, sinodep, block, offset,
+ sizeof(*sinodep), &next_block, &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, inodep, block, offset,
+ sizeof(*inodep), &next_block, &next_offset))
+ goto failed_read;
+
+ i->i_nlink = inodep->nlink;
+ i->i_size = inodep->symlink_size;
+ i->i_op = &page_symlink_inode_operations;
+ i->i_data.a_ops = &squashfs_symlink_aops;
+ i->i_mode |= S_IFLNK;
+ SQUASHFS_I(i)->start_block = next_block;
+ SQUASHFS_I(i)->offset = next_offset;
+
+ TRACE("Symbolic link inode %x:%x, start_block %llx, offset %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ next_block, next_offset);
+ break;
+ }
+ case SQUASHFS_BLKDEV_TYPE:
+ case SQUASHFS_CHRDEV_TYPE: {
+ struct squashfs_dev_inode_header *inodep = &id.dev;
+ struct squashfs_dev_inode_header *sinodep = &sid.dev;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, sinodep, block, offset,
+ sizeof(*sinodep), &next_block, &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, inodep, block, offset,
+ sizeof(*inodep), &next_block, &next_offset))
+ goto failed_read;
+
+ i->i_nlink = inodep->nlink;
+ i->i_mode |= (inodeb->inode_type == SQUASHFS_CHRDEV_TYPE) ?
+ S_IFCHR : S_IFBLK;
+ init_special_inode(i, i->i_mode, old_decode_dev(inodep->rdev));
+
+ TRACE("Device inode %x:%x, rdev %x\n",
+ SQUASHFS_INODE_BLK(inode), offset, inodep->rdev);
+ break;
+ }
+ case SQUASHFS_FIFO_TYPE:
+ case SQUASHFS_SOCKET_TYPE: {
+ struct squashfs_ipc_inode_header *inodep = &id.ipc;
+ struct squashfs_ipc_inode_header *sinodep = &sid.ipc;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, sinodep, block, offset,
+ sizeof(*sinodep), &next_block, &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, inodep, block, offset,
+ sizeof(*inodep), &next_block, &next_offset))
+ goto failed_read;
+
+ i->i_nlink = inodep->nlink;
+ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
+ ? S_IFIFO : S_IFSOCK;
+ init_special_inode(i, i->i_mode, 0);
+ break;
+ }
+ default:
+ ERROR("Unknown inode type %d in squashfs_iget!\n",
+ inodeb->inode_type);
+ goto failed_read1;
+ }
+
+ return 1;
+
+failed_read:
+ ERROR("Unable to read inode [%llx:%x]\n", block, offset);
+
+failed_read1:
+ make_bad_inode(i);
+ return 0;
+}
+
+
+static int read_inode_lookup_table(struct super_block *s)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(sblk->inodes);
+
+ TRACE("In read_inode_lookup_table, length %d\n", length);
+
+ /* Allocate inode lookup table */
+ msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL);
+ if (msblk->inode_lookup_table == NULL) {
+ ERROR("Failed to allocate inode lookup table\n");
+ return 0;
+ }
+
+ if (!squashfs_read_data(s, (char *) msblk->inode_lookup_table,
+ sblk->lookup_table_start, length |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
+ ERROR("unable to read inode lookup table\n");
+ return 0;
+ }
+
+ if (msblk->swap) {
+ int i;
+ long long block;
+
+ for (i = 0; i < SQUASHFS_LOOKUP_BLOCKS(sblk->inodes); i++) {
+ /* XXX */
+ SQUASHFS_SWAP_LOOKUP_BLOCKS((&block),
+ &msblk->inode_lookup_table[i], 1);
+ msblk->inode_lookup_table[i] = block;
+ }
+ }
+
+ return 1;
+}
+
+
+static int read_fragment_index_table(struct super_block *s)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments);
+
+ if(length == 0)
+ return 1;
+
+ /* Allocate fragment index table */
+ msblk->fragment_index = kmalloc(length, GFP_KERNEL);
+ if (msblk->fragment_index == NULL) {
+ ERROR("Failed to allocate fragment index table\n");
+ return 0;
+ }
+
+ if (!squashfs_read_data(s, (char *) msblk->fragment_index,
+ sblk->fragment_table_start, length |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
+ ERROR("unable to read fragment index table\n");
+ return 0;
+ }
+
+ if (msblk->swap) {
+ int i;
+ long long fragment;
+
+ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); i++) {
+ /* XXX */
+ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment),
+ &msblk->fragment_index[i], 1);
+ msblk->fragment_index[i] = fragment;
+ }
+ }
+
+ return 1;
+}
+
+
+static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent)
+{
+ struct squashfs_super_block *sblk = &msblk->sblk;
+
+ msblk->read_inode = squashfs_read_inode;
+ msblk->read_blocklist = read_blocklist;
+ msblk->read_fragment_index_table = read_fragment_index_table;
+
+ if (sblk->s_major == 1) {
+ if (!squashfs_1_0_supported(msblk)) {
+ SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems "
+ "are unsupported\n");
+ SERROR("Please recompile with Squashfs 1.0 support enabled\n");
+ return 0;
+ }
+ } else if (sblk->s_major == 2) {
+ if (!squashfs_2_0_supported(msblk)) {
+ SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems "
+ "are unsupported\n");
+ SERROR("Please recompile with Squashfs 2.0 support enabled\n");
+ return 0;
+ }
+ } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor >
+ SQUASHFS_MINOR) {
+ SERROR("Major/Minor mismatch, trying to mount newer %d.%d "
+ "filesystem\n", sblk->s_major, sblk->s_minor);
+ SERROR("Please update your kernel\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int squashfs_fill_super(struct super_block *s, void *data, int silent)
+{
+ struct squashfs_sb_info *msblk;
+ struct squashfs_super_block *sblk;
+ char b[BDEVNAME_SIZE];
+ struct inode *root;
+
+ TRACE("Entered squashfs_fill_superblock\n");
+
+ s->s_fs_info = kzalloc(sizeof(struct squashfs_sb_info), GFP_KERNEL);
+ if (s->s_fs_info == NULL) {
+ ERROR("Failed to allocate superblock\n");
+ goto failure;
+ }
+ msblk = s->s_fs_info;
+
+ msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize());
+ if (msblk->stream.workspace == NULL) {
+ ERROR("Failed to allocate zlib workspace\n");
+ goto failure;
+ }
+ sblk = &msblk->sblk;
+
+ msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE);
+ msblk->devblksize_log2 = ffz(~msblk->devblksize);
+
+ mutex_init(&msblk->read_data_mutex);
+ mutex_init(&msblk->read_page_mutex);
+ mutex_init(&msblk->meta_index_mutex);
+
+ /* sblk->bytes_used is checked in squashfs_read_data to ensure reads are not
+ * beyond filesystem end. As we're using squashfs_read_data to read sblk here,
+ * first set sblk->bytes_used to a useful value */
+ sblk->bytes_used = sizeof(struct squashfs_super_block);
+ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
+ sizeof(struct squashfs_super_block) |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, sizeof(struct squashfs_super_block))) {
+ SERROR("unable to read superblock\n");
+ goto failed_mount;
+ }
+
+ /* Check it is a SQUASHFS superblock */
+ if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) {
+ if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) {
+ struct squashfs_super_block ssblk;
+
+ WARNING("Mounting a different endian SQUASHFS filesystem on %s\n",
+ bdevname(s->s_bdev, b));
+
+ SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk);
+ memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block));
+ msblk->swap = 1;
+ } else {
+ SERROR("Can't find a SQUASHFS superblock on %s\n",
+ bdevname(s->s_bdev, b));
+ goto failed_mount;
+ }
+ }
+
+ /* Check the MAJOR & MINOR versions */
+ if(!supported_squashfs_filesystem(msblk, silent))
+ goto failed_mount;
+
+ /* Check the filesystem does not extend beyond the end of the
+ block device */
+ if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode))
+ goto failed_mount;
+
+ /* Check the root inode for sanity */
+ if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE)
+ goto failed_mount;
+
+ TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b));
+ TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sblk->flags)
+ ? "un" : "");
+ TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sblk->flags)
+ ? "un" : "");
+ TRACE("Check data is %spresent in the filesystem\n",
+ SQUASHFS_CHECK_DATA(sblk->flags) ? "" : "not ");
+ TRACE("Filesystem size %lld bytes\n", sblk->bytes_used);
+ TRACE("Block size %d\n", sblk->block_size);
+ TRACE("Number of inodes %d\n", sblk->inodes);
+ if (sblk->s_major > 1)
+ TRACE("Number of fragments %d\n", sblk->fragments);
+ TRACE("Number of uids %d\n", sblk->no_uids);
+ TRACE("Number of gids %d\n", sblk->no_guids);
+ TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start);
+ TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start);
+ if (sblk->s_major > 1)
+ TRACE("sblk->fragment_table_start %llx\n", sblk->fragment_table_start);
+ TRACE("sblk->uid_start %llx\n", sblk->uid_start);
+
+ s->s_maxbytes = MAX_LFS_FILESIZE;
+ s->s_flags |= MS_RDONLY;
+ s->s_op = &squashfs_super_ops;
+
+ msblk->block_cache = squashfs_cache_init("metadata", SQUASHFS_CACHED_BLKS,
+ SQUASHFS_METADATA_SIZE, 0);
+ if (msblk->block_cache == NULL)
+ goto failed_mount;
+
+ /* Allocate read_page block */
+ msblk->read_page = vmalloc(sblk->block_size);
+ if (msblk->read_page == NULL) {
+ ERROR("Failed to allocate read_page block\n");
+ goto failed_mount;
+ }
+
+ /* Allocate uid and gid tables */
+ msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) *
+ sizeof(unsigned int), GFP_KERNEL);
+ if (msblk->uid == NULL) {
+ ERROR("Failed to allocate uid/gid table\n");
+ goto failed_mount;
+ }
+ msblk->guid = msblk->uid + sblk->no_uids;
+
+ if (msblk->swap) {
+ unsigned int suid[sblk->no_uids + sblk->no_guids];
+
+ if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
+ ((sblk->no_uids + sblk->no_guids) *
+ sizeof(unsigned int)) |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
+ ERROR("unable to read uid/gid table\n");
+ goto failed_mount;
+ }
+
+ SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids +
+ sblk->no_guids), (sizeof(unsigned int) * 8));
+ } else
+ if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start,
+ ((sblk->no_uids + sblk->no_guids) *
+ sizeof(unsigned int)) |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
+ ERROR("unable to read uid/gid table\n");
+ goto failed_mount;
+ }
+
+
+ if (sblk->s_major == 1 && squashfs_1_0_supported(msblk))
+ goto allocate_root;
+
+ msblk->fragment_cache = squashfs_cache_init("fragment",
+ SQUASHFS_CACHED_FRAGMENTS, sblk->block_size, 1);
+ if (msblk->fragment_cache == NULL)
+ goto failed_mount;
+
+ /* Allocate and read fragment index table */
+ if (msblk->read_fragment_index_table(s) == 0)
+ goto failed_mount;
+
+ if(sblk->s_major < 3 || sblk->lookup_table_start == SQUASHFS_INVALID_BLK)
+ goto allocate_root;
+
+ /* Allocate and read inode lookup table */
+ if (read_inode_lookup_table(s) == 0)
+ goto failed_mount;
+
+ s->s_export_op = &squashfs_export_ops;
+
+allocate_root:
+ root = new_inode(s);
+ if ((msblk->read_inode)(root, sblk->root_inode) == 0)
+ goto failed_mount;
+ insert_inode_hash(root);
+
+ s->s_root = d_alloc_root(root);
+ if (s->s_root == NULL) {
+ ERROR("Root inode create failed\n");
+ iput(root);
+ goto failed_mount;
+ }
+
+ TRACE("Leaving squashfs_fill_super\n");
+ return 0;
+
+failed_mount:
+ kfree(msblk->inode_lookup_table);
+ kfree(msblk->fragment_index);
+ squashfs_cache_delete(msblk->fragment_cache);
+ kfree(msblk->uid);
+ vfree(msblk->read_page);
+ squashfs_cache_delete(msblk->block_cache);
+ kfree(msblk->fragment_index_2);
+ vfree(msblk->stream.workspace);
+ kfree(s->s_fs_info);
+ s->s_fs_info = NULL;
+ return -EINVAL;
+
+failure:
+ return -ENOMEM;
+}
+
+
+static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+
+ TRACE("Entered squashfs_statfs\n");
+
+ buf->f_type = SQUASHFS_MAGIC;
+ buf->f_bsize = sblk->block_size;
+ buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1;
+ buf->f_bfree = buf->f_bavail = 0;
+ buf->f_files = sblk->inodes;
+ buf->f_ffree = 0;
+ buf->f_namelen = SQUASHFS_NAME_LEN;
+
+ return 0;
+}
+
+
+static int squashfs_symlink_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ int index = page->index << PAGE_CACHE_SHIFT, length, bytes, avail_bytes;
+ long long block = SQUASHFS_I(inode)->start_block;
+ int offset = SQUASHFS_I(inode)->offset;
+ void *pageaddr = kmap(page);
+
+ TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
+ "%llx, offset %x\n", page->index,
+ SQUASHFS_I(inode)->start_block,
+ SQUASHFS_I(inode)->offset);
+
+ for (length = 0; length < index; length += bytes) {
+ bytes = squashfs_get_cached_block(inode->i_sb, NULL, block,
+ offset, PAGE_CACHE_SIZE, &block, &offset);
+ if (bytes == 0) {
+ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset);
+ goto skip_read;
+ }
+ }
+
+ if (length != index) {
+ ERROR("(squashfs_symlink_readpage) length != index\n");
+ bytes = 0;
+ goto skip_read;
+ }
+
+ avail_bytes = min_t(int, i_size_read(inode) - length, PAGE_CACHE_SIZE);
+
+ bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, offset,
+ avail_bytes, &block, &offset);
+ if (bytes == 0)
+ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset);
+
+skip_read:
+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+ kunmap(page);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ unlock_page(page);
+
+ return 0;
+}
+
+
+static struct meta_index *locate_meta_index(struct inode *inode, int index, int offset)
+{
+ struct meta_index *meta = NULL;
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+ int i;
+
+ mutex_lock(&msblk->meta_index_mutex);
+
+ TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
+
+ if (msblk->meta_index == NULL)
+ goto not_allocated;
+
+ for (i = 0; i < SQUASHFS_META_NUMBER; i ++) {
+ if (msblk->meta_index[i].inode_number == inode->i_ino &&
+ msblk->meta_index[i].offset >= offset &&
+ msblk->meta_index[i].offset <= index &&
+ msblk->meta_index[i].locked == 0) {
+ TRACE("locate_meta_index: entry %d, offset %d\n", i,
+ msblk->meta_index[i].offset);
+ meta = &msblk->meta_index[i];
+ offset = meta->offset;
+ }
+ }
+
+ if (meta)
+ meta->locked = 1;
+
+not_allocated:
+ mutex_unlock(&msblk->meta_index_mutex);
+
+ return meta;
+}
+
+
+static struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip)
+{
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+ struct meta_index *meta = NULL;
+ int i;
+
+ mutex_lock(&msblk->meta_index_mutex);
+
+ TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
+
+ if (msblk->meta_index == NULL) {
+ msblk->meta_index = kmalloc(sizeof(struct meta_index) *
+ SQUASHFS_META_NUMBER, GFP_KERNEL);
+ if (msblk->meta_index == NULL) {
+ ERROR("Failed to allocate meta_index\n");
+ goto failed;
+ }
+ for (i = 0; i < SQUASHFS_META_NUMBER; i++) {
+ msblk->meta_index[i].inode_number = 0;
+ msblk->meta_index[i].locked = 0;
+ }
+ msblk->next_meta_index = 0;
+ }
+
+ for (i = SQUASHFS_META_NUMBER; i &&
+ msblk->meta_index[msblk->next_meta_index].locked; i --)
+ msblk->next_meta_index = (msblk->next_meta_index + 1) %
+ SQUASHFS_META_NUMBER;
+
+ if (i == 0) {
+ TRACE("empty_meta_index: failed!\n");
+ goto failed;
+ }
+
+ TRACE("empty_meta_index: returned meta entry %d, %p\n",
+ msblk->next_meta_index,
+ &msblk->meta_index[msblk->next_meta_index]);
+
+ meta = &msblk->meta_index[msblk->next_meta_index];
+ msblk->next_meta_index = (msblk->next_meta_index + 1) %
+ SQUASHFS_META_NUMBER;
+
+ meta->inode_number = inode->i_ino;
+ meta->offset = offset;
+ meta->skip = skip;
+ meta->entries = 0;
+ meta->locked = 1;
+
+failed:
+ mutex_unlock(&msblk->meta_index_mutex);
+ return meta;
+}
+
+
+static void release_meta_index(struct inode *inode, struct meta_index *meta)
+{
+ meta->locked = 0;
+ smp_mb();
+}
+
+
+static int read_block_index(struct super_block *s, int blocks, char *block_list,
+ long long *start_block, int *offset)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ unsigned int *block_listp;
+ int block = 0;
+
+ if (msblk->swap) {
+ char sblock_list[blocks << 2];
+
+ if (!squashfs_get_cached_block(s, sblock_list, *start_block,
+ *offset, blocks << 2, start_block, offset)) {
+ ERROR("Fail reading block list [%llx:%x]\n", *start_block, *offset);
+ goto failure;
+ }
+ SQUASHFS_SWAP_INTS(((unsigned int *)block_list),
+ ((unsigned int *)sblock_list), blocks);
+ } else {
+ if (!squashfs_get_cached_block(s, block_list, *start_block,
+ *offset, blocks << 2, start_block, offset)) {
+ ERROR("Fail reading block list [%llx:%x]\n", *start_block, *offset);
+ goto failure;
+ }
+ }
+
+ for (block_listp = (unsigned int *) block_list; blocks;
+ block_listp++, blocks --)
+ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp);
+
+ return block;
+
+failure:
+ return -1;
+}
+
+
+#define SIZE 256
+
+static inline int calculate_skip(int blocks) {
+ int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES);
+ return skip >= 7 ? 7 : skip + 1;
+}
+
+
+static int get_meta_index(struct inode *inode, int index,
+ long long *index_block, int *index_offset,
+ long long *data_block, char *block_list)
+{
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ int skip = calculate_skip(i_size_read(inode) >> sblk->block_log);
+ int offset = 0;
+ struct meta_index *meta;
+ struct meta_entry *meta_entry;
+ long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start;
+ int cur_offset = SQUASHFS_I(inode)->offset;
+ long long cur_data_block = SQUASHFS_I(inode)->start_block;
+ int i;
+
+ index /= SQUASHFS_META_INDEXES * skip;
+
+ while (offset < index) {
+ meta = locate_meta_index(inode, index, offset + 1);
+
+ if (meta == NULL) {
+ meta = empty_meta_index(inode, offset + 1, skip);
+ if (meta == NULL)
+ goto all_done;
+ } else {
+ if(meta->entries == 0)
+ goto failed;
+ /* XXX */
+ offset = index < meta->offset + meta->entries ? index :
+ meta->offset + meta->entries - 1;
+ /* XXX */
+ meta_entry = &meta->meta_entry[offset - meta->offset];
+ cur_index_block = meta_entry->index_block + sblk->inode_table_start;
+ cur_offset = meta_entry->offset;
+ cur_data_block = meta_entry->data_block;
+ TRACE("get_meta_index: offset %d, meta->offset %d, "
+ "meta->entries %d\n", offset, meta->offset, meta->entries);
+ TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
+ " data_block 0x%llx\n", cur_index_block,
+ cur_offset, cur_data_block);
+ }
+
+ for (i = meta->offset + meta->entries; i <= index &&
+ i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
+ int blocks = skip * SQUASHFS_META_INDEXES;
+
+ while (blocks) {
+ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : blocks;
+ int res = read_block_index(inode->i_sb, block, block_list,
+ &cur_index_block, &cur_offset);
+
+ if (res == -1)
+ goto failed;
+
+ cur_data_block += res;
+ blocks -= block;
+ }
+
+ meta_entry = &meta->meta_entry[i - meta->offset];
+ meta_entry->index_block = cur_index_block - sblk->inode_table_start;
+ meta_entry->offset = cur_offset;
+ meta_entry->data_block = cur_data_block;
+ meta->entries ++;
+ offset ++;
+ }
+
+ TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
+ meta->offset, meta->entries);
+
+ release_meta_index(inode, meta);
+ }
+
+all_done:
+ *index_block = cur_index_block;
+ *index_offset = cur_offset;
+ *data_block = cur_data_block;
+
+ return offset * SQUASHFS_META_INDEXES * skip;
+
+failed:
+ release_meta_index(inode, meta);
+ return -1;
+}
+
+
+static long long read_blocklist(struct inode *inode, int index,
+ int readahead_blks, char *block_list,
+ unsigned short **block_p, unsigned int *bsize)
+{
+ long long block_ptr;
+ int offset;
+ long long block;
+ int res = get_meta_index(inode, index, &block_ptr, &offset, &block,
+ block_list);
+
+ TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset"
+ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, block);
+
+ if(res == -1)
+ goto failure;
+
+ index -= res;
+
+ while (index) {
+ int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index;
+ int res = read_block_index(inode->i_sb, blocks, block_list,
+ &block_ptr, &offset);
+ if (res == -1)
+ goto failure;
+ block += res;
+ index -= blocks;
+ }
+
+ if (read_block_index(inode->i_sb, 1, block_list, &block_ptr, &offset) == -1)
+ goto failure;
+ *bsize = *((unsigned int *) block_list);
+
+ return block;
+
+failure:
+ return 0;
+}
+
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ unsigned char *block_list = NULL;
+ long long block;
+ unsigned int bsize, i;
+ int bytes;
+ int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT);
+ void *pageaddr;
+ struct squashfs_cache_entry *fragment = NULL;
+ char *data_ptr = msblk->read_page;
+
+ int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1;
+ int start_index = page->index & ~mask;
+ int end_index = start_index | mask;
+ int file_end = i_size_read(inode) >> sblk->block_log;
+ int sparse = 0;
+
+ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
+ page->index, SQUASHFS_I(inode)->start_block);
+
+ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT))
+ goto out;
+
+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+ || index < file_end) {
+ block_list = kmalloc(SIZE, GFP_KERNEL);
+ if (block_list == NULL) {
+ ERROR("Failed to allocate block_list\n");
+ goto error_out;
+ }
+
+ block = (msblk->read_blocklist)(inode, index, 1, block_list, NULL, &bsize);
+ if (block == 0)
+ goto error_out;
+
+ if (bsize == 0) { /* hole */
+ bytes = index == file_end ?
+ (i_size_read(inode) & (sblk->block_size - 1)) : sblk->block_size;
+ sparse = 1;
+ } else {
+ mutex_lock(&msblk->read_page_mutex);
+
+ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
+ bsize, NULL, sblk->block_size);
+
+ if (bytes == 0) {
+ ERROR("Unable to read page, block %llx, size %x\n", block, bsize);
+ mutex_unlock(&msblk->read_page_mutex);
+ goto error_out;
+ }
+ }
+ } else {
+ fragment = get_cached_fragment(inode->i_sb,
+ SQUASHFS_I(inode)-> u.s1.fragment_start_block,
+ SQUASHFS_I(inode)->u.s1.fragment_size);
+
+ if (fragment->error) {
+ ERROR("Unable to read page, block %llx, size %x\n",
+ SQUASHFS_I(inode)->u.s1.fragment_start_block,
+ (int) SQUASHFS_I(inode)->u.s1.fragment_size);
+ release_cached_fragment(msblk, fragment);
+ goto error_out;
+ }
+ bytes = i_size_read(inode) & (sblk->block_size - 1);
+ data_ptr = fragment->data + SQUASHFS_I(inode)->u.s1.fragment_offset;
+ }
+
+ for (i = start_index; i <= end_index && bytes > 0; i++,
+ bytes -= PAGE_CACHE_SIZE, data_ptr += PAGE_CACHE_SIZE) {
+ struct page *push_page;
+ int avail = sparse ? 0 : min_t(unsigned int, bytes, PAGE_CACHE_SIZE);
+
+ TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
+
+ push_page = (i == page->index) ? page :
+ grab_cache_page_nowait(page->mapping, i);
+
+ if (!push_page)
+ continue;
+
+ if (PageUptodate(push_page))
+ goto skip_page;
+
+ pageaddr = kmap_atomic(push_page, KM_USER0);
+ memcpy(pageaddr, data_ptr, avail);
+ memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
+ kunmap_atomic(pageaddr, KM_USER0);
+ flush_dcache_page(push_page);
+ SetPageUptodate(push_page);
+skip_page:
+ unlock_page(push_page);
+ if(i != page->index)
+ page_cache_release(push_page);
+ }
+
+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+ || index < file_end) {
+ if (!sparse)
+ mutex_unlock(&msblk->read_page_mutex);
+ kfree(block_list);
+ } else
+ release_cached_fragment(msblk, fragment);
+
+ return 0;
+
+error_out:
+ SetPageError(page);
+out:
+ pageaddr = kmap_atomic(page, KM_USER0);
+ memset(pageaddr, 0, PAGE_CACHE_SIZE);
+ kunmap_atomic(pageaddr, KM_USER0);
+ flush_dcache_page(page);
+ if (!PageError(page))
+ SetPageUptodate(page);
+ unlock_page(page);
+
+ kfree(block_list);
+ return 0;
+}
+
+
+static int get_dir_index_using_offset(struct super_block *s,
+ long long *next_block, unsigned int *next_offset,
+ long long index_start, unsigned int index_offset, int i_count,
+ long long f_pos)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ int i, length = 0;
+ struct squashfs_dir_index index;
+
+ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
+ i_count, (unsigned int) f_pos);
+
+ f_pos -= 3;
+ if (f_pos == 0)
+ goto finish;
+
+ for (i = 0; i < i_count; i++) {
+ if (msblk->swap) {
+ struct squashfs_dir_index sindex;
+ squashfs_get_cached_block(s, &sindex, index_start, index_offset,
+ sizeof(sindex), &index_start, &index_offset);
+ SQUASHFS_SWAP_DIR_INDEX(&index, &sindex);
+ } else
+ squashfs_get_cached_block(s, &index, index_start, index_offset,
+ sizeof(index), &index_start, &index_offset);
+
+ if (index.index > f_pos)
+ break;
+
+ squashfs_get_cached_block(s, NULL, index_start, index_offset,
+ index.size + 1, &index_start, &index_offset);
+
+ length = index.index;
+ *next_block = index.start_block + sblk->directory_table_start;
+ }
+
+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+
+finish:
+ return length + 3;
+}
+
+
+static int get_dir_index_using_name(struct super_block *s,
+ long long *next_block, unsigned int *next_offset,
+ long long index_start, unsigned int index_offset, int i_count,
+ const char *name, int size)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ int i, length = 0;
+ struct squashfs_dir_index *index;
+ char *str;
+
+ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
+
+ str = kmalloc(sizeof(struct squashfs_dir_index) +
+ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL);
+ if (str == NULL) {
+ ERROR("Failed to allocate squashfs_dir_index\n");
+ goto failure;
+ }
+
+ index = (struct squashfs_dir_index *) (str + SQUASHFS_NAME_LEN + 1);
+ strncpy(str, name, size);
+ str[size] = '\0';
+
+ for (i = 0; i < i_count; i++) {
+ if (msblk->swap) {
+ struct squashfs_dir_index sindex;
+ squashfs_get_cached_block(s, &sindex, index_start, index_offset,
+ sizeof(sindex), &index_start, &index_offset);
+ SQUASHFS_SWAP_DIR_INDEX(index, &sindex);
+ } else
+ squashfs_get_cached_block(s, index, index_start, index_offset,
+ sizeof(struct squashfs_dir_index), &index_start, &index_offset);
+
+ squashfs_get_cached_block(s, index->name, index_start, index_offset,
+ index->size + 1, &index_start, &index_offset);
+
+ index->name[index->size + 1] = '\0';
+
+ if (strcmp(index->name, str) > 0)
+ break;
+
+ length = index->index;
+ *next_block = index->start_block + sblk->directory_table_start;
+ }
+
+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+ kfree(str);
+
+failure:
+ return length + 3;
+}
+
+
+static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+ struct inode *i = file->f_dentry->d_inode;
+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ long long next_block = SQUASHFS_I(i)->start_block +
+ sblk->directory_table_start;
+ int next_offset = SQUASHFS_I(i)->offset, length = 0, dir_count;
+ struct squashfs_dir_header dirh;
+ struct squashfs_dir_entry *dire;
+
+ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset);
+
+ dire = kmalloc(sizeof(struct squashfs_dir_entry) +
+ SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
+ if (dire == NULL) {
+ ERROR("Failed to allocate squashfs_dir_entry\n");
+ goto finish;
+ }
+
+ while(file->f_pos < 3) {
+ char *name;
+ int size, i_ino;
+
+ if(file->f_pos == 0) {
+ name = ".";
+ size = 1;
+ i_ino = i->i_ino;
+ } else {
+ name = "..";
+ size = 2;
+ i_ino = SQUASHFS_I(i)->u.s2.parent_inode;
+ }
+ TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n",
+ (unsigned int) dirent, name, size, (int)
+ file->f_pos, i_ino, squashfs_filetype_table[1]);
+
+ if (filldir(dirent, name, size, file->f_pos, i_ino,
+ squashfs_filetype_table[1]) < 0) {
+ TRACE("Filldir returned less than 0\n");
+ goto finish;
+ }
+ file->f_pos += size;
+ }
+
+ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
+ SQUASHFS_I(i)->u.s2.directory_index_start,
+ SQUASHFS_I(i)->u.s2.directory_index_offset,
+ SQUASHFS_I(i)->u.s2.directory_index_count, file->f_pos);
+
+ while (length < i_size_read(i)) {
+ /* read directory header */
+ if (msblk->swap) {
+ struct squashfs_dir_header sdirh;
+
+ if (!squashfs_get_cached_block(i->i_sb, &sdirh, next_block,
+ next_offset, sizeof(sdirh), &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(sdirh);
+ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, &dirh, next_block,
+ next_offset, sizeof(dirh), &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(dirh);
+ }
+
+ dir_count = dirh.count + 1;
+ while (dir_count--) {
+ if (msblk->swap) {
+ struct squashfs_dir_entry sdire;
+ if (!squashfs_get_cached_block(i->i_sb, &sdire, next_block,
+ next_offset, sizeof(sdire), &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(sdire);
+ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, dire, next_block,
+ next_offset, sizeof(*dire), &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(*dire);
+ }
+
+ if (!squashfs_get_cached_block(i->i_sb, dire->name, next_block,
+ next_offset, dire->size + 1, &next_block, &next_offset))
+ goto failed_read;
+
+ length += dire->size + 1;
+
+ if (file->f_pos >= length)
+ continue;
+
+ dire->name[dire->size + 1] = '\0';
+
+ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n",
+ (unsigned int) dirent, dire->name, dire->size + 1,
+ (int) file->f_pos, dirh.start_block, dire->offset,
+ dirh.inode_number + dire->inode_number,
+ squashfs_filetype_table[dire->type]);
+
+ if (filldir(dirent, dire->name, dire->size + 1, file->f_pos,
+ dirh.inode_number + dire->inode_number,
+ squashfs_filetype_table[dire->type]) < 0) {
+ TRACE("Filldir returned less than 0\n");
+ goto finish;
+ }
+ file->f_pos = length;
+ }
+ }
+
+finish:
+ kfree(dire);
+ return 0;
+
+failed_read:
+ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+ next_offset);
+ kfree(dire);
+ return 0;
+}
+
+
+static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ const unsigned char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+ struct inode *inode = NULL;
+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ long long next_block = SQUASHFS_I(i)->start_block +
+ sblk->directory_table_start;
+ int next_offset = SQUASHFS_I(i)->offset, length = 0, dir_count;
+ struct squashfs_dir_header dirh;
+ struct squashfs_dir_entry *dire;
+
+ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
+
+ dire = kmalloc(sizeof(struct squashfs_dir_entry) +
+ SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
+ if (dire == NULL) {
+ ERROR("Failed to allocate squashfs_dir_entry\n");
+ goto exit_lookup;
+ }
+
+ if (len > SQUASHFS_NAME_LEN)
+ goto exit_lookup;
+
+ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
+ SQUASHFS_I(i)->u.s2.directory_index_start,
+ SQUASHFS_I(i)->u.s2.directory_index_offset,
+ SQUASHFS_I(i)->u.s2.directory_index_count, name, len);
+
+ while (length < i_size_read(i)) {
+ /* read directory header */
+ if (msblk->swap) {
+ struct squashfs_dir_header sdirh;
+ if (!squashfs_get_cached_block(i->i_sb, &sdirh, next_block,
+ next_offset, sizeof(sdirh), &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(sdirh);
+ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, &dirh, next_block,
+ next_offset, sizeof(dirh), &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(dirh);
+ }
+
+ dir_count = dirh.count + 1;
+ while (dir_count--) {
+ if (msblk->swap) {
+ struct squashfs_dir_entry sdire;
+ if (!squashfs_get_cached_block(i->i_sb, &sdire, next_block,
+ next_offset, sizeof(sdire), &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(sdire);
+ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, dire, next_block,
+ next_offset, sizeof(*dire), &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(*dire);
+ }
+
+ if (!squashfs_get_cached_block(i->i_sb, dire->name, next_block,
+ next_offset, dire->size + 1, &next_block, &next_offset))
+ goto failed_read;
+
+ length += dire->size + 1;
+
+ if (name[0] < dire->name[0])
+ goto exit_lookup;
+
+ if ((len == dire->size + 1) && !strncmp(name, dire->name, len)) {
+ squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block,
+ dire->offset);
+
+ TRACE("calling squashfs_iget for directory entry %s, inode"
+ " %x:%x, %d\n", name, dirh.start_block, dire->offset,
+ dirh.inode_number + dire->inode_number);
+
+ inode = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number);
+
+ goto exit_lookup;
+ }
+ }
+ }
+
+exit_lookup:
+ kfree(dire);
+ if (inode)
+ return d_splice_alias(inode, dentry);
+ d_add(dentry, inode);
+ return ERR_PTR(0);
+
+failed_read:
+ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+ next_offset);
+ goto exit_lookup;
+}
+
+
+static int squashfs_remount(struct super_block *s, int *flags, char *data)
+{
+ *flags |= MS_RDONLY;
+ return 0;
+}
+
+
+static void squashfs_put_super(struct super_block *s)
+{
+ if (s->s_fs_info) {
+ struct squashfs_sb_info *sbi = s->s_fs_info;
+ squashfs_cache_delete(sbi->block_cache);
+ squashfs_cache_delete(sbi->fragment_cache);
+ vfree(sbi->read_page);
+ kfree(sbi->uid);
+ kfree(sbi->fragment_index);
+ kfree(sbi->fragment_index_2);
+ kfree(sbi->meta_index);
+ vfree(sbi->stream.workspace);
+ kfree(s->s_fs_info);
+ s->s_fs_info = NULL;
+ }
+}
+
+
+static int squashfs_get_sb(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data, struct vfsmount *mnt)
+{
+ return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
+ mnt);
+}
+
+
+static int __init init_squashfs_fs(void)
+{
+ int err = init_inodecache();
+ if (err)
+ goto out;
+
+ printk(KERN_INFO "squashfs: version 3.4 (2008/08/26) "
+ "Phillip Lougher\n");
+
+ err = register_filesystem(&squashfs_fs_type);
+ if (err)
+ destroy_inodecache();
+
+out:
+ return err;
+}
+
+
+static void __exit exit_squashfs_fs(void)
+{
+ unregister_filesystem(&squashfs_fs_type);
+ destroy_inodecache();
+}
+
+
+static struct kmem_cache * squashfs_inode_cachep;
+
+
+static struct inode *squashfs_alloc_inode(struct super_block *sb)
+{
+ struct squashfs_inode_info *ei;
+ ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
+ return ei ? &ei->vfs_inode : NULL;
+}
+
+
+static void squashfs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode));
+}
+
+
+static void init_once(void *foo)
+{
+ struct squashfs_inode_info *ei = foo;
+
+ inode_init_once(&ei->vfs_inode);
+}
+
+
+static int __init init_inodecache(void)
+{
+ squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
+ sizeof(struct squashfs_inode_info), 0,
+ SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, init_once);
+ if (squashfs_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+
+static void destroy_inodecache(void)
+{
+ kmem_cache_destroy(squashfs_inode_cachep);
+}
+
+
+module_init(init_squashfs_fs);
+module_exit(exit_squashfs_fs);
+MODULE_DESCRIPTION("squashfs 3.4, a compressed read-only filesystem");
+MODULE_AUTHOR("Phillip Lougher <phillip@lougher.demon.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
new file mode 100644
index 000000000000..4e117425bc43
--- /dev/null
+++ b/fs/squashfs/squashfs.h
@@ -0,0 +1,86 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * 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,
+ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs.h
+ */
+
+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+#endif
+
+#ifdef SQUASHFS_TRACE
+#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args)
+#else
+#define TRACE(s, args...) {}
+#endif
+
+#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args)
+
+#define SERROR(s, args...) do { \
+ if (!silent) \
+ printk(KERN_ERR "SQUASHFS error: "s, ## args);\
+ } while(0)
+
+#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args)
+
+static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode)
+{
+ return list_entry(inode, struct squashfs_inode_info, vfs_inode);
+}
+
+#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY)
+#define SQSH_EXTERN
+extern unsigned int squashfs_read_data(struct super_block *s, char *buffer,
+ long long index, unsigned int length,
+ long long *next_index, int srclength);
+extern int squashfs_get_cached_block(struct super_block *s, void *buffer,
+ long long block, unsigned int offset,
+ int length, long long *next_block,
+ unsigned int *next_offset);
+extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct
+ squashfs_cache_entry *fragment);
+extern struct squashfs_cache_entry *get_cached_fragment(struct super_block
+ *s, long long start_block,
+ int length);
+extern struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number);
+extern const struct address_space_operations squashfs_symlink_aops;
+extern const struct address_space_operations squashfs_aops;
+extern struct inode_operations squashfs_dir_inode_ops;
+#else
+#define SQSH_EXTERN static
+#endif
+
+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk);
+#else
+static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
+extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk);
+#else
+static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
+{
+ return 0;
+}
+#endif
diff --git a/fs/squashfs/squashfs2_0.c b/fs/squashfs/squashfs2_0.c
new file mode 100644
index 000000000000..30ebdb9418a9
--- /dev/null
+++ b/fs/squashfs/squashfs2_0.c
@@ -0,0 +1,740 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * 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,
+ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs2_0.c
+ */
+
+#include <linux/squashfs_fs.h>
+#include <linux/module.h>
+#include <linux/zlib.h>
+#include <linux/fs.h>
+#include <linux/squashfs_fs_sb.h>
+#include <linux/squashfs_fs_i.h>
+
+#include "squashfs.h"
+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
+static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *,
+ struct nameidata *);
+
+static struct file_operations squashfs_dir_ops_2 = {
+ .read = generic_read_dir,
+ .readdir = squashfs_readdir_2
+};
+
+static struct inode_operations squashfs_dir_inode_ops_2 = {
+ .lookup = squashfs_lookup_2
+};
+
+static unsigned char squashfs_filetype_table[] = {
+ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
+};
+
+static int read_fragment_index_table_2(struct super_block *s)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+
+ if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
+ (sblk->fragments), GFP_KERNEL))) {
+ ERROR("Failed to allocate uid/gid table\n");
+ return 0;
+ }
+
+ if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
+ !squashfs_read_data(s, (char *)
+ msblk->fragment_index_2,
+ sblk->fragment_table_start,
+ SQUASHFS_FRAGMENT_INDEX_BYTES_2
+ (sblk->fragments) |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) {
+ ERROR("unable to read fragment index table\n");
+ return 0;
+ }
+
+ if (msblk->swap) {
+ int i;
+ unsigned int fragment;
+
+ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
+ i++) {
+ SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
+ &msblk->fragment_index_2[i], 1);
+ msblk->fragment_index_2[i] = fragment;
+ }
+ }
+
+ return 1;
+}
+
+
+static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
+ long long *fragment_start_block,
+ unsigned int *fragment_size)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ long long start_block =
+ msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
+ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
+ struct squashfs_fragment_entry_2 fragment_entry;
+
+ if (msblk->swap) {
+ struct squashfs_fragment_entry_2 sfragment_entry;
+
+ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
+ start_block, offset,
+ sizeof(sfragment_entry), &start_block,
+ &offset))
+ goto out;
+ SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
+ } else
+ if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
+ start_block, offset,
+ sizeof(fragment_entry), &start_block,
+ &offset))
+ goto out;
+
+ *fragment_start_block = fragment_entry.start_block;
+ *fragment_size = fragment_entry.size;
+
+ return 1;
+
+out:
+ return 0;
+}
+
+
+static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
+ struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
+{
+ struct squashfs_super_block *sblk = &msblk->sblk;
+
+ i->i_ino = ino;
+ i->i_mtime.tv_sec = sblk->mkfs_time;
+ i->i_atime.tv_sec = sblk->mkfs_time;
+ i->i_ctime.tv_sec = sblk->mkfs_time;
+ i->i_uid = msblk->uid[inodeb->uid];
+ i->i_mode = inodeb->mode;
+ i->i_nlink = 1;
+ i->i_size = 0;
+ if (inodeb->guid == SQUASHFS_GUIDS)
+ i->i_gid = i->i_uid;
+ else
+ i->i_gid = msblk->guid[inodeb->guid];
+}
+
+
+static int squashfs_read_inode_2(struct inode *i, squashfs_inode_t inode)
+{
+ struct super_block *s = i->i_sb;
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ unsigned int block = SQUASHFS_INODE_BLK(inode) +
+ sblk->inode_table_start;
+ unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
+ unsigned int ino = SQUASHFS_MK_VFS_INODE(block -
+ sblk->inode_table_start, offset);
+ long long next_block;
+ unsigned int next_offset;
+ union squashfs_inode_header_2 id, sid;
+ struct squashfs_base_inode_header_2 *inodeb = &id.base,
+ *sinodeb = &sid.base;
+
+ TRACE("Entered squashfs_read_inode_2\n");
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
+ offset, sizeof(*sinodeb), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
+ sizeof(*sinodeb));
+ } else
+ if (!squashfs_get_cached_block(s, (char *) inodeb, block,
+ offset, sizeof(*inodeb), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ squashfs_new_inode(msblk, i, inodeb, ino);
+
+ switch(inodeb->inode_type) {
+ case SQUASHFS_FILE_TYPE: {
+ struct squashfs_reg_inode_header_2 *inodep = &id.reg;
+ struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
+ long long frag_blk;
+ unsigned int frag_size = 0;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, (char *)
+ sinodep, block, offset,
+ sizeof(*sinodep), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, (char *)
+ inodep, block, offset,
+ sizeof(*inodep), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ frag_blk = SQUASHFS_INVALID_BLK;
+ if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
+ !get_fragment_location_2(s,
+ inodep->fragment, &frag_blk, &frag_size))
+ goto failed_read;
+
+ i->i_size = inodep->file_size;
+ i->i_fop = &generic_ro_fops;
+ i->i_mode |= S_IFREG;
+ i->i_mtime.tv_sec = inodep->mtime;
+ i->i_atime.tv_sec = inodep->mtime;
+ i->i_ctime.tv_sec = inodep->mtime;
+ i->i_blocks = ((i->i_size - 1) >> 9) + 1;
+ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
+ SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
+ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
+ SQUASHFS_I(i)->start_block = inodep->start_block;
+ SQUASHFS_I(i)->u.s1.block_list_start = next_block;
+ SQUASHFS_I(i)->offset = next_offset;
+ i->i_data.a_ops = &squashfs_aops;
+
+ TRACE("File inode %x:%x, start_block %x, "
+ "block_list_start %llx, offset %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ inodep->start_block, next_block,
+ next_offset);
+ break;
+ }
+ case SQUASHFS_DIR_TYPE: {
+ struct squashfs_dir_inode_header_2 *inodep = &id.dir;
+ struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, (char *)
+ sinodep, block, offset,
+ sizeof(*sinodep), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, (char *)
+ inodep, block, offset,
+ sizeof(*inodep), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ i->i_size = inodep->file_size;
+ i->i_op = &squashfs_dir_inode_ops_2;
+ i->i_fop = &squashfs_dir_ops_2;
+ i->i_mode |= S_IFDIR;
+ i->i_mtime.tv_sec = inodep->mtime;
+ i->i_atime.tv_sec = inodep->mtime;
+ i->i_ctime.tv_sec = inodep->mtime;
+ SQUASHFS_I(i)->start_block = inodep->start_block;
+ SQUASHFS_I(i)->offset = inodep->offset;
+ SQUASHFS_I(i)->u.s2.directory_index_count = 0;
+ SQUASHFS_I(i)->u.s2.parent_inode = 0;
+
+ TRACE("Directory inode %x:%x, start_block %x, offset "
+ "%x\n", SQUASHFS_INODE_BLK(inode),
+ offset, inodep->start_block,
+ inodep->offset);
+ break;
+ }
+ case SQUASHFS_LDIR_TYPE: {
+ struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
+ struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, (char *)
+ sinodep, block, offset,
+ sizeof(*sinodep), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
+ sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, (char *)
+ inodep, block, offset,
+ sizeof(*inodep), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ i->i_size = inodep->file_size;
+ i->i_op = &squashfs_dir_inode_ops_2;
+ i->i_fop = &squashfs_dir_ops_2;
+ i->i_mode |= S_IFDIR;
+ i->i_mtime.tv_sec = inodep->mtime;
+ i->i_atime.tv_sec = inodep->mtime;
+ i->i_ctime.tv_sec = inodep->mtime;
+ SQUASHFS_I(i)->start_block = inodep->start_block;
+ SQUASHFS_I(i)->offset = inodep->offset;
+ SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
+ SQUASHFS_I(i)->u.s2.directory_index_offset =
+ next_offset;
+ SQUASHFS_I(i)->u.s2.directory_index_count =
+ inodep->i_count;
+ SQUASHFS_I(i)->u.s2.parent_inode = 0;
+
+ TRACE("Long directory inode %x:%x, start_block %x, "
+ "offset %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ inodep->start_block, inodep->offset);
+ break;
+ }
+ case SQUASHFS_SYMLINK_TYPE: {
+ struct squashfs_symlink_inode_header_2 *inodep =
+ &id.symlink;
+ struct squashfs_symlink_inode_header_2 *sinodep =
+ &sid.symlink;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, (char *)
+ sinodep, block, offset,
+ sizeof(*sinodep), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
+ sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, (char *)
+ inodep, block, offset,
+ sizeof(*inodep), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ i->i_size = inodep->symlink_size;
+ i->i_op = &page_symlink_inode_operations;
+ i->i_data.a_ops = &squashfs_symlink_aops;
+ i->i_mode |= S_IFLNK;
+ SQUASHFS_I(i)->start_block = next_block;
+ SQUASHFS_I(i)->offset = next_offset;
+
+ TRACE("Symbolic link inode %x:%x, start_block %llx, "
+ "offset %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ next_block, next_offset);
+ break;
+ }
+ case SQUASHFS_BLKDEV_TYPE:
+ case SQUASHFS_CHRDEV_TYPE: {
+ struct squashfs_dev_inode_header_2 *inodep = &id.dev;
+ struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
+
+ if (msblk->swap) {
+ if (!squashfs_get_cached_block(s, (char *)
+ sinodep, block, offset,
+ sizeof(*sinodep), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, (char *)
+ inodep, block, offset,
+ sizeof(*inodep), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ i->i_mode |= (inodeb->inode_type ==
+ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR :
+ S_IFBLK;
+ init_special_inode(i, i->i_mode,
+ old_decode_dev(inodep->rdev));
+
+ TRACE("Device inode %x:%x, rdev %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ inodep->rdev);
+ break;
+ }
+ case SQUASHFS_FIFO_TYPE:
+ case SQUASHFS_SOCKET_TYPE: {
+
+ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
+ ? S_IFIFO : S_IFSOCK;
+ init_special_inode(i, i->i_mode, 0);
+ break;
+ }
+ default:
+ ERROR("Unknown inode type %d in squashfs_iget!\n",
+ inodeb->inode_type);
+ goto failed_read1;
+ }
+
+ return 1;
+
+failed_read:
+ ERROR("Unable to read inode [%x:%x]\n", block, offset);
+
+failed_read1:
+ return 0;
+}
+
+
+static int get_dir_index_using_offset(struct super_block *s, long long
+ *next_block, unsigned int *next_offset,
+ long long index_start,
+ unsigned int index_offset, int i_count,
+ long long f_pos)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ int i, length = 0;
+ struct squashfs_dir_index_2 index;
+
+ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
+ i_count, (unsigned int) f_pos);
+
+ if (f_pos == 0)
+ goto finish;
+
+ for (i = 0; i < i_count; i++) {
+ if (msblk->swap) {
+ struct squashfs_dir_index_2 sindex;
+ squashfs_get_cached_block(s, (char *) &sindex,
+ index_start, index_offset,
+ sizeof(sindex), &index_start,
+ &index_offset);
+ SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex);
+ } else
+ squashfs_get_cached_block(s, (char *) &index,
+ index_start, index_offset,
+ sizeof(index), &index_start,
+ &index_offset);
+
+ if (index.index > f_pos)
+ break;
+
+ squashfs_get_cached_block(s, NULL, index_start, index_offset,
+ index.size + 1, &index_start,
+ &index_offset);
+
+ length = index.index;
+ *next_block = index.start_block + sblk->directory_table_start;
+ }
+
+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+
+finish:
+ return length;
+}
+
+
+static int get_dir_index_using_name(struct super_block *s, long long
+ *next_block, unsigned int *next_offset,
+ long long index_start,
+ unsigned int index_offset, int i_count,
+ const char *name, int size)
+{
+ struct squashfs_sb_info *msblk = s->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ int i, length = 0;
+ struct squashfs_dir_index_2 *index;
+ char *str;
+
+ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
+
+ if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
+ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
+ ERROR("Failed to allocate squashfs_dir_index\n");
+ goto failure;
+ }
+
+ index = (struct squashfs_dir_index_2 *) (str + SQUASHFS_NAME_LEN + 1);
+ strncpy(str, name, size);
+ str[size] = '\0';
+
+ for (i = 0; i < i_count; i++) {
+ if (msblk->swap) {
+ struct squashfs_dir_index_2 sindex;
+ squashfs_get_cached_block(s, (char *) &sindex,
+ index_start, index_offset,
+ sizeof(sindex), &index_start,
+ &index_offset);
+ SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
+ } else
+ squashfs_get_cached_block(s, (char *) index,
+ index_start, index_offset,
+ sizeof(struct squashfs_dir_index_2),
+ &index_start, &index_offset);
+
+ squashfs_get_cached_block(s, index->name, index_start,
+ index_offset, index->size + 1,
+ &index_start, &index_offset);
+
+ index->name[index->size + 1] = '\0';
+
+ if (strcmp(index->name, str) > 0)
+ break;
+
+ length = index->index;
+ *next_block = index->start_block + sblk->directory_table_start;
+ }
+
+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+ kfree(str);
+failure:
+ return length;
+}
+
+
+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir)
+{
+ struct inode *i = file->f_dentry->d_inode;
+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ long long next_block = SQUASHFS_I(i)->start_block +
+ sblk->directory_table_start;
+ int next_offset = SQUASHFS_I(i)->offset, length = 0,
+ dir_count;
+ struct squashfs_dir_header_2 dirh;
+ struct squashfs_dir_entry_2 *dire;
+
+ TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
+
+ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
+ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
+ ERROR("Failed to allocate squashfs_dir_entry\n");
+ goto finish;
+ }
+
+ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
+ SQUASHFS_I(i)->u.s2.directory_index_start,
+ SQUASHFS_I(i)->u.s2.directory_index_offset,
+ SQUASHFS_I(i)->u.s2.directory_index_count,
+ file->f_pos);
+
+ while (length < i_size_read(i)) {
+ /* read directory header */
+ if (msblk->swap) {
+ struct squashfs_dir_header_2 sdirh;
+
+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
+ next_block, next_offset, sizeof(sdirh),
+ &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(sdirh);
+ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+ next_block, next_offset, sizeof(dirh),
+ &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(dirh);
+ }
+
+ dir_count = dirh.count + 1;
+ while (dir_count--) {
+ if (msblk->swap) {
+ struct squashfs_dir_entry_2 sdire;
+ if (!squashfs_get_cached_block(i->i_sb, (char *)
+ &sdire, next_block, next_offset,
+ sizeof(sdire), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ length += sizeof(sdire);
+ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, (char *)
+ dire, next_block, next_offset,
+ sizeof(*dire), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ length += sizeof(*dire);
+ }
+
+ if (!squashfs_get_cached_block(i->i_sb, dire->name,
+ next_block, next_offset,
+ dire->size + 1, &next_block,
+ &next_offset))
+ goto failed_read;
+
+ length += dire->size + 1;
+
+ if (file->f_pos >= length)
+ continue;
+
+ dire->name[dire->size + 1] = '\0';
+
+ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
+ (unsigned int) dirent, dire->name,
+ dire->size + 1, (int) file->f_pos,
+ dirh.start_block, dire->offset,
+ squashfs_filetype_table[dire->type]);
+
+ if (filldir(dirent, dire->name, dire->size + 1,
+ file->f_pos, SQUASHFS_MK_VFS_INODE(
+ dirh.start_block, dire->offset),
+ squashfs_filetype_table[dire->type])
+ < 0) {
+ TRACE("Filldir returned less than 0\n");
+ goto finish;
+ }
+ file->f_pos = length;
+ }
+ }
+
+finish:
+ kfree(dire);
+ return 0;
+
+failed_read:
+ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+ next_offset);
+ kfree(dire);
+ return 0;
+}
+
+
+static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ const unsigned char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+ struct inode *inode = NULL;
+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
+ struct squashfs_super_block *sblk = &msblk->sblk;
+ long long next_block = SQUASHFS_I(i)->start_block +
+ sblk->directory_table_start;
+ int next_offset = SQUASHFS_I(i)->offset, length = 0,
+ dir_count;
+ struct squashfs_dir_header_2 dirh;
+ struct squashfs_dir_entry_2 *dire;
+ int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
+
+ TRACE("Entered squashfs_lookup_2 [%llx:%x]\n", next_block, next_offset);
+
+ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
+ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
+ ERROR("Failed to allocate squashfs_dir_entry\n");
+ goto exit_loop;
+ }
+
+ if (len > SQUASHFS_NAME_LEN)
+ goto exit_loop;
+
+ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
+ SQUASHFS_I(i)->u.s2.directory_index_start,
+ SQUASHFS_I(i)->u.s2.directory_index_offset,
+ SQUASHFS_I(i)->u.s2.directory_index_count, name,
+ len);
+
+ while (length < i_size_read(i)) {
+ /* read directory header */
+ if (msblk->swap) {
+ struct squashfs_dir_header_2 sdirh;
+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
+ next_block, next_offset, sizeof(sdirh),
+ &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(sdirh);
+ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+ next_block, next_offset, sizeof(dirh),
+ &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(dirh);
+ }
+
+ dir_count = dirh.count + 1;
+ while (dir_count--) {
+ if (msblk->swap) {
+ struct squashfs_dir_entry_2 sdire;
+ if (!squashfs_get_cached_block(i->i_sb, (char *)
+ &sdire, next_block,next_offset,
+ sizeof(sdire), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ length += sizeof(sdire);
+ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, (char *)
+ dire, next_block,next_offset,
+ sizeof(*dire), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ length += sizeof(*dire);
+ }
+
+ if (!squashfs_get_cached_block(i->i_sb, dire->name,
+ next_block, next_offset, dire->size + 1,
+ &next_block, &next_offset))
+ goto failed_read;
+
+ length += dire->size + 1;
+
+ if (sorted && name[0] < dire->name[0])
+ goto exit_loop;
+
+ if ((len == dire->size + 1) && !strncmp(name,
+ dire->name, len)) {
+ squashfs_inode_t ino =
+ SQUASHFS_MKINODE(dirh.start_block,
+ dire->offset);
+ unsigned int inode_number = SQUASHFS_MK_VFS_INODE(dirh.start_block,
+ dire->offset);
+
+ TRACE("calling squashfs_iget for directory "
+ "entry %s, inode %x:%x, %lld\n", name,
+ dirh.start_block, dire->offset, ino);
+
+ inode = squashfs_iget(i->i_sb, ino, inode_number);
+
+ goto exit_loop;
+ }
+ }
+ }
+
+exit_loop:
+ kfree(dire);
+ d_add(dentry, inode);
+ return ERR_PTR(0);
+
+failed_read:
+ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+ next_offset);
+ goto exit_loop;
+}
+
+
+int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
+{
+ struct squashfs_super_block *sblk = &msblk->sblk;
+
+ msblk->read_inode = squashfs_read_inode_2;
+ msblk->read_fragment_index_table = read_fragment_index_table_2;
+
+ sblk->bytes_used = sblk->bytes_used_2;
+ sblk->uid_start = sblk->uid_start_2;
+ sblk->guid_start = sblk->guid_start_2;
+ sblk->inode_table_start = sblk->inode_table_start_2;
+ sblk->directory_table_start = sblk->directory_table_start_2;
+ sblk->fragment_table_start = sblk->fragment_table_start_2;
+
+ return 1;
+}
diff --git a/git_revision.txt b/git_revision.txt
new file mode 100644
index 000000000000..a83679700d7b
--- /dev/null
+++ b/git_revision.txt
@@ -0,0 +1 @@
+5bf775c85eb7d4921cc32f11ea7afbc1dae7efe6
diff --git a/include/asm-arm/plat-s3c24xx/mci.h b/include/asm-arm/plat-s3c24xx/mci.h
index 2d0852ac3b27..3f333e83cb3b 100644
--- a/include/asm-arm/plat-s3c24xx/mci.h
+++ b/include/asm-arm/plat-s3c24xx/mci.h
@@ -10,6 +10,9 @@ struct s3c24xx_mci_pdata {
unsigned long ocr_avail;
void (*set_power)(unsigned char power_mode,
unsigned short vdd);
+
+ /* Enable/Disable the DMA-support (Luis Galdos) */
+ int dma_enable;
};
#endif /* _ARCH_NCI_H */
diff --git a/include/asm-arm/plat-s3c24xx/regs-spi.h b/include/asm-arm/plat-s3c24xx/regs-spi.h
index 2b35479ee35c..9fcb1f245097 100644
--- a/include/asm-arm/plat-s3c24xx/regs-spi.h
+++ b/include/asm-arm/plat-s3c24xx/regs-spi.h
@@ -78,5 +78,101 @@
#define S3C2412_RXFIFO (0x18)
#define S3C2412_SPFIC (0x24)
+/*
+ * High Speed SPI registers and control bits
+ * Coming from the SMDK (Luis Galdos)
+ */
+#define S3C2443_SPI0_CH_CFG (0x00) /* Configuration */
+#define S3C2443_SPI0_CLK_CFG (0x04) /* Clock configuration */
+#define S3C2443_SPI0_MODE_CFG (0x08) /* FIFO control */
+#define S3C2443_SPI0_SLAVE_SEL (0x0C) /* Slave selection */
+#define S3C2443_SPI0_INT_EN (0x10) /* interrupt enable */
+#define S3C2443_SPI0_STATUS (0x14) /* status */
+#define S3C2443_SPI0_TX_DATA (0x18) /* TX data */
+#define S3C2443_SPI0_RX_DATA (0x1C) /* RX data */
+#define S3C2443_SPI0_PACKET_CNT (0x20) /* Count how many data master gets */
+#define S3C2443_SPI0_PENDING_CLR (0x24) /* Pending clear */
+
+#define S3C2443_SPI0_TX_DATA_PA (0x52000018)
+#define S3C2443_SPI0_RX_DATA_PA (0x5200001C)
+
+#define S3C2443_SPI0_CH_SW_RST (1<<5)
+#define S3C2443_SPI0_CH_MASTER (0<<4)
+#define S3C2443_SPI0_CH_SLAVE (1<<4)
+#define S3C2443_SPI0_CH_RISING (0<<3)
+#define S3C2443_SPI0_CH_FALLING (1<<3)
+#define S3C2443_SPI0_CH_FORMAT_A (0<<2)
+#define S3C2443_SPI0_CH_FORMAT_B (1<<2)
+#define S3C2443_SPI0_CH_RXCH_OFF (0<<1)
+#define S3C2443_SPI0_CH_RXCH_ON (1<<1)
+#define S3C2443_SPI0_CH_TXCH_OFF (0<<0)
+#define S3C2443_SPI0_CH_TXCH_ON (1<<0)
+
+#define S3C2443_SPI0_CLKSEL_PCLK (0<<9)
+#define S3C2443_SPI0_CLKSEL_HCLK (1<<9)
+#define S3C2443_SPI0_CLKSEL_ECLK (2<<9)
+#define S3C2443_SPI0_CLKSEL_EPLL (3<<9)
+#define S3C2443_SPI0_ENCLK_DISABLE (0<<8)
+#define S3C2443_SPI0_ENCLK_ENABLE (1<<8)
+#define S3C2443_SPI0_CLK_PRE_MASK (0xff)
+
+#define S3C2443_SPI0_MODE_CH_TSZ_BYTE (0<<18)
+#define S3C2443_SPI0_MODE_CH_TSZ_WORD (1<<18)
+#define S3C2443_SPI0_FEED_BACK_DELAY (1<<17)
+#define S3C2443_SPI0_MODE_RXDMA_OFF (0<<2)
+#define S3C2443_SPI0_MODE_RXDMA_ON (1<<2)
+#define S3C2443_SPI0_MODE_TXDMA_OFF (0<<1)
+#define S3C2443_SPI0_MODE_TXDMA_ON (1<<1)
+#define S3C2443_SPI0_MODE_SINGLE (0<<0)
+#define S3C2443_SPI0_MODE_4BURST (1<<0)
+
+#define S3C2443_SPI0_STUS_TX_DONE (1<<21)
+#define S3C2443_SPI0_STUS_TRAILCNT_ZERO (1<<20)
+
+#define S3C2443_SPI0_STUS_TX_LEVEL(x) ((x >> 6) & 0x7F)
+#define S3C2443_SPI0_STUS_RX_LEVEL(x) ((x >> 13) & 0x7F)
+
+#define S3C2443_SPI0_STUS_RX_OVERRUN_ERR (1<<5)
+#define S3C2443_SPI0_STUS_RX_UNDERRUN_ERR (1<<4)
+#define S3C2443_SPI0_STUS_TX_OVERRUN_ERR (1<<3)
+#define S3C2443_SPI0_STUS_TX_UNDERRUN_ERR (1<<2)
+#define S3C2443_SPI0_STUS_RX_FIFORDY (1<<1)
+#define S3C2443_SPI0_STUS_TX_FIFORDY (1<<0)
+
+#define S3C2443_SPI0_SLAVE_SIG_ACT (0<<0)
+#define S3C2443_SPI0_SLAVE_SIG_INACT (1<<0)
+
+#define S3C2443_SPI0_STUS_TX_DONE (1<<21)
+#define S3C2443_SPI0_STUS_TRAILCNT_ZERO (1<<20)
+#define S3C2443_SPI0_STUS_RX_OVERRUN (1<<5)
+#define S3C2443_SPI0_STUS_RX_UNDERRUN (1<<4)
+#define S3C2443_SPI0_STUS_TX_OVERRUN (1<<3)
+#define S3C2443_SPI0_STUS_TX_UNDERRUN (1<<2)
+#define S3C2443_SPI0_STUS_RX_FIFORDY (1<<1)
+#define S3C2443_SPI0_STUS_TX_FIFORDY (1<<0)
+
+#define S3C2443_SPI0_INT_TRAILING_DIS (0<<6)
+#define S3C2443_SPI0_INT_TRAILING_EN (1<<6)
+#define S3C2443_SPI0_INT_RX_OVERRUN_DIS (0<<5)
+#define S3C2443_SPI0_INT_RX_OVERRUN_EN (1<<5)
+#define S3C2443_SPI0_INT_RX_UNDERRUN_DIS (0<<4)
+#define S3C2443_SPI0_INT_RX_UNDERRUN_EN (1<<4)
+#define S3C2443_SPI0_INT_TX_OVERRUN_DIS (0<<3)
+#define S3C2443_SPI0_INT_TX_OVERRUN_EN (1<<3)
+#define S3C2443_SPI0_INT_TX_UNDERRUN_DIS (0<<2)
+#define S3C2443_SPI0_INT_TX_UNDERRUN_EN (1<<2)
+#define S3C2443_SPI0_INT_RX_FIFORDY_DIS (0<<1)
+#define S3C2443_SPI0_INT_RX_FIFORDY_EN (1<<1)
+#define S3C2443_SPI0_INT_TX_FIFORDY_DIS (0<<0)
+#define S3C2443_SPI0_INT_TX_FIFORDY_EN (1<<0)
+
+#define S3C2443_SPI0_PACKET_CNT_DIS (0<<16)
+#define S3C2443_SPI0_PACKET_CNT_EN (1<<16)
+
+#define S3C2443_SPI0_PND_TX_UNDERRUN_CLR (1<<4)
+#define S3C2443_SPI0_PND_TX_OVERRUN_CLR (1<<3)
+#define S3C2443_SPI0_PND_RX_UNDERRUN_CLR (1<<2)
+#define S3C2443_SPI0_PND_RX_OVERRUN_CLR (1<<1)
+#define S3C2443_SPI0_PND_TRAILING_CLR (1<<0)
#endif /* __ASM_ARCH_REGS_SPI_H */
diff --git a/include/asm-arm/plat-s3c24xx/regs-udc.h b/include/asm-arm/plat-s3c24xx/regs-udc.h
index f0dd4a41b37b..966dd96fb245 100644
--- a/include/asm-arm/plat-s3c24xx/regs-udc.h
+++ b/include/asm-arm/plat-s3c24xx/regs-udc.h
@@ -1,4 +1,4 @@
-/* arch/arm/mach-s3c2410/include/mach/regs-udc.h
+/* linux/include/asm-arm/arch-s3c2410/regs-udc.h
*
* Copyright (C) 2004 Herbert Poetzl <herbert@13thfloor.at>
*
@@ -150,4 +150,186 @@
#define S3C2410_UDC_MAXP_64 (1<<3)
+
+#define S3C24XX_USBDREG(x) (x)
+
+/* Registers for the S3C2443 */
+/* Non-Indexed Registers */
+#if defined(CONFIG_CPU_S3C2443)
+
+#define S3C24XX_UDC_IR_REG S3C24XX_USBDREG(0x00) /* Index register */
+#define S3C24XX_UDC_EIR_REG S3C24XX_USBDREG(0x04) /* EP IRQ pending */
+#define S3C24XX_UDC_EIER_REG S3C24XX_USBDREG(0x08) /* EP IRQ enable */
+#define S3C24XX_UDC_FAR_REG S3C24XX_USBDREG(0x0c) /* Function address */
+#define S3C24XX_UDC_FNR_REG S3C24XX_USBDREG(0x10) /* Frame number */
+#define S3C24XX_UDC_EDR_REG S3C24XX_USBDREG(0x14) /* Endpoint direction */
+#define S3C24XX_UDC_TR_REG S3C24XX_USBDREG(0x18) /* Test register */
+#define S3C24XX_UDC_SSR_REG S3C24XX_USBDREG(0x1c) /* System status */
+#define S3C24XX_UDC_SCR_REG S3C24XX_USBDREG(0x20) /* System control */
+#define S3C24XX_UDC_EP0SR_REG S3C24XX_USBDREG(0x24) /* Endpoint 0 status */
+#define S3C24XX_UDC_EP0CR_REG S3C24XX_USBDREG(0x28) /* Endpoint 0 control */
+#define S3C24XX_UDC_EP0BR_REG S3C24XX_USBDREG(0x60) /* Endpoint 0 Buffer */
+#define S3C24XX_UDC_EP1BR_REG S3C24XX_USBDREG(0x64) /* Endpoint 1 Buffer */
+#define S3C24XX_UDC_EP2BR_REG S3C24XX_USBDREG(0x68) /* Endpoint 2 Buffer */
+#define S3C24XX_UDC_EP3BR_REG S3C24XX_USBDREG(0x6C) /* Endpoint 3 Buffer */
+#define S3C24XX_UDC_EP4BR_REG S3C24XX_USBDREG(0x70) /* Endpoint 4 Buffer */
+#define S3C24XX_UDC_EP5BR_REG S3C24XX_USBDREG(0x74) /* Endpoint 5 Buffer */
+#define S3C24XX_UDC_EP6BR_REG S3C24XX_USBDREG(0x78) /* Endpoint 6 Buffer */
+#define S3C24XX_UDC_EP7BR_REG S3C24XX_USBDREG(0x7C) /* Endpoint 7 Buffer */
+#define S3C24XX_UDC_EP8BR_REG S3C24XX_USBDREG(0x80) /* Endpoint 8 Buffer */
+
+#define S3C24XX_UDC_FIFO_CON_REG S3C24XX_USBDREG(0x100) /* Burst FIFO-DMA Control */
+#define S3C24XX_UDC_FIFO_STATUS_REG S3C24XX_USBDREG(0x104) /* Burst FIFO Status */
+
+/* Indexed Registers */
+#define S3C24XX_UDC_EP_STATUS_REG S3C24XX_USBDREG(0x2c) /* Endpoints status */
+#define S3C24XX_UDC_EP_CON_REG S3C24XX_USBDREG(0x30) /* Endpoints control */
+#define S3C24XX_UDC_BYTE_READ_CNT_REG S3C24XX_USBDREG(0x34) /* Byte read count */
+#define S3C24XX_UDC_BYTE_WRITE_CNT_REG S3C24XX_USBDREG(0x38) /* Byte write count */
+#define S3C24XX_UDC_MAXP_REG S3C24XX_USBDREG(0x3c) /* Max packet size */
+#define S3C24XX_UDC_DMA_CON_REG S3C24XX_USBDREG(0x40) /* DMA control */
+#define S3C24XX_UDC_DMA_CNT_REG S3C24XX_USBDREG(0x44) /* DMA count */
+#define S3C24XX_UDC_DMA_FIFO_CNT_REG S3C24XX_USBDREG(0x48) /* DMA FIFO count */
+#define S3C24XX_UDC_DMA_TOTAL_CNT1_REG S3C24XX_USBDREG(0x4c) /* DMA Total Transfer count1 */
+#define S3C24XX_UDC_DMA_TOTAL_CNT2_REG S3C24XX_USBDREG(0x50) /* DMA Total Transfer count2 */
+#define S3C24XX_UDC_DMA_IF_CON_REG S3C24XX_USBDREG(0x84) /* DMA interface Control */
+#define S3C24XX_UDC_DMA_MEM_BASE_ADDR_REG S3C24XX_USBDREG(0x88) /* Mem Base Addr */
+#define S3C24XX_UDC_DMA_MEM_CURRENT_ADDR_REG S3C24XX_USBDREG(0x8c) /* Mem current Addr */
+
+/* @TODO: Indexed registers according to the data sheet */
+#define S3C24XX_UDC_ESR_REG S3C24XX_USBDREG(0x2C) /* EP status */
+#define S3C24XX_UDC_ECR_REG S3C24XX_USBDREG(0x30) /* EP control */
+#define S3C24XX_UDC_BRCR_REG S3C24XX_USBDREG(0x34) /* Byte read count */
+#define S3C24XX_UDC_BWCR_REG S3C24XX_USBDREG(0x38) /* Byte write count */
+
+#else
+#error "Unsupported platform for the UDC-S3C24XX?"
#endif
+
+
+/* EP interrupt register Bits */
+#define S3C24XX_UDC_INT_EP3 (1<<3) // R/C
+#define S3C24XX_UDC_INT_EP2 (1<<2) // R/C
+#define S3C24XX_UDC_INT_EP1 (1<<1) // R/C
+#define S3C24XX_UDC_INT_EP0 (1<<0) // R/C
+
+/* System status register Bits */
+#define S3C24XX_UDC_INT_CHECK (0xff8f)
+#define S3C24XX_UDC_INT_ERR (0xff80) // R/C
+#define S3C24XX_UDC_SSR_DCERR (1 << 11) /* Data CRC error */
+#define S3C24XX_UDC_SSR_EOERR (1 << 10) /* EB overrun error */
+#define S3C24XX_UDC_SSR_VBUSOFF (1 << 9) /* Vbus OFF */
+#define S3C24XX_UDC_INT_VBUSON (1 << 8) /* Vbus ON */
+#define S3C24XX_UDC_SSR_TBM (1 << 7) /* Toggle bit mismatch */
+#define S3C24XX_UDC_INT_HSP (1 << 4) // R
+#define S3C24XX_UDC_INT_SDE (1 << 3) // R/C
+#define S3C24XX_UDC_INT_RESUME (1 << 2) // R/C
+#define S3C24XX_UDC_INT_SUSPEND (1 << 1) // R/C
+#define S3C24XX_UDC_INT_RESET (1 << 0) // R/C
+
+/* system control register Bits */
+#define S3C24XX_UDC_DTZIEN_EN (1 << 14)
+#define S3C24XX_UDC_DI_EN (1 << 12)
+#define S3C24XX_UDC_VBUSOFF_EN (1 << 11)
+#define S3C24XX_UDC_VBUSON_EN (1 << 10)
+#define S3C24XX_UDC_RWDE_EN (1 << 9)
+#define S3C24XX_UDC_EIE_EN (1 << 8)
+#define S3C24XX_UDC_BIS_EN (1 << 7)
+#define S3C24XX_UDC_SPDEN_EN (1 << 6)
+#define S3C24XX_UDC_RRD_EN (1 << 5)
+#define S3C24XX_UDC_IPS_EN (1 << 4)
+#define S3C24XX_UDC_MFRM_EN (1 << 2)
+#define S3C24XX_UDC_SUS_EN (1 << 1)
+#define S3C24XX_UDC_RST_EN (1 << 0)
+
+/* EP0 status register Bits */
+#define S3C24XX_UDC_EP0_LWO (1<<6)
+#define S3C24XX_UDC_EP0_STALL (1<<4)
+#define S3C24XX_UDC_EP0_TX_SUCCESS (1<<1)
+#define S3C24XX_UDC_EP0_RX_SUCCESS (1<<0)
+
+/* EP status register Bits */
+#define S3C24XX_UDC_EP_FPID (1<<11)
+#define S3C24XX_UDC_EP_OSD (1<<10)
+#define S3C24XX_UDC_EP_DTCZ (1<<9)
+#define S3C24XX_UDC_EP_SPT (1<<8)
+
+/* EP0 status register bits */
+#define S3C24XX_UDC_EP0SR_RSR (1 << 0)
+#define S3C24XX_UDC_EP0SR_TST (1 << 1)
+#define S3C24XX_UDC_EP0SR_SHT (1 << 4)
+#define S3C24XX_UDC_EP0SR_LWO (1 << 6)
+
+/* EP0 control register bits */
+#define S3C24XX_UDC_EP0CR_TTE (1 << 3)
+#define S3C24XX_UDC_EP0CR_TSS (1 << 2)
+#define S3C24XX_UDC_EP0CR_ESS (1 << 1)
+#define S3C24XX_UDC_EP0CR_TZLS (1 << 0)
+
+/* Indexed EP control register bits */
+#define S3C24XX_UDC_ECR_INPKTHLD (1 << 12)
+#define S3C24XX_UDC_ECR_OUTPKTHLD (1 << 11)
+#define S3C24XX_UDC_ECR_TNPMF (1 << 10)
+#define S3C24XX_UDC_ECR_IME (1 << 9)
+#define S3C24XX_UDC_ECR_DUEN (1 << 7)
+#define S3C24XX_UDC_ECR_FLUSH (1 << 6)
+#define S3C24XX_UDC_ECR_TTE (1 << 5)
+#define S3C24XX_UDC_ECR_CDP (1 << 2)
+#define S3C24XX_UDC_ECR_ESS (1 << 1)
+#define S3C24XX_UDC_ECR_TZLS (1 << 0)
+
+/* Index EP status register bits */
+#define S3C24XX_UDC_ESR_RPS (1 << 0) /* Rx packet success */
+#define S3C24XX_UDC_ESR_TPS (1 << 1) /* Tx packet success */
+#define S3C24XX_UDC_ESR_PSIF (3 << 2) /* Packet status in FIFO */
+#define S3C24XX_UDC_ESR_PSIFNR(x) (((x) >> 2) & (0x3)) /* Number of packets in FIFO */
+#define S3C24XX_UDC_ESR_PSIF_ONE (1 << 2) /* TRUE if one packet in the FIFO*/
+#define S3C24XX_UDC_ESR_PSIF_TWO (2 << 2) /* TRUE if two packets in the FIFO */
+#define S3C24XX_UDC_ESR_LWO (1 << 4) /* Last word odd */
+#define S3C24XX_UDC_ESR_FSC (1 << 5) /* Function stall condition */
+#define S3C24XX_UDC_ESR_FFS (1 << 6) /* FIFO flushed */
+#define S3C24XX_UDC_ESR_SPT (1 << 8) /* Short packet received */
+#define S3C24XX_UDC_ESR_DTCZ (1 << 9) /* DMA total count zero */
+#define S3C24XX_UDC_ESR_OSD (1 << 10) /* OUT start DMA operation */
+#define S3C24XX_UDC_ESR_FPID (1 << 11) /* First OUT packet interrupt disable */
+#define S3C24XX_UDC_ESR_FOVF (1 << 14) /* FIFO overflow */
+#define S3C24XX_UDC_ESR_FUDR (1 << 15) /* FIFO underflow */
+
+/* Test register bits */
+#define S3C24XX_UDC_TR_TSNS (1 << 0) /* Test SE0 NAK select */
+#define S3C24XX_UDC_TR_TJS (1 << 1) /* Test J select */
+#define S3C24XX_UDC_TR_TKS (1 << 2) /* Test K select */
+#define S3C24XX_UDC_TR_TPS (1 << 3) /* Test packets */
+#define S3C24XX_UDC_TR_TMD (1 << 4) /* Test mode */
+#define S3C24XX_UDC_TR_PERR (1 << 12) /* PID error */
+#define S3C24XX_UDC_TR_EUERR (1 << 13) /* EB underrun error */
+#define S3C24XX_UDC_TR_VBUS (1 << 15) /* Vbus */
+
+/* System control register bits */
+#define S3C24XX_UDC_SCR_HRESE (1 << 0) /* Reset enable */
+#define S3C24XX_UDC_SCR_HSUSPE (1 << 1) /* Suspend enable */
+#define S3C24XX_UDC_SCR_MFRM (1 << 2) /* Resume by MCU */
+#define S3C24XX_UDC_SCR_IPS (1 << 4) /* Interrupt polarity */
+#define S3C24XX_UDC_SCR_RRDE (1 << 5) /* Reverse read data enable */
+#define S3C24XX_UDC_SCR_SPDEN (1 << 6) /* Speed detect end interrupt enable */
+#define S3C24XX_UDC_SCR_BIS (1 << 7) /* Bus interface select */
+#define S3C24XX_UDC_SCR_EIE (1 << 8) /* Error interrupt enable */
+#define S3C24XX_UDC_SCR_RWDE (1 << 9) /* Reverse write data enable */
+#define S3C24XX_UDC_SCR_VBUSONEN (1 << 10) /* VBUS on enable */
+#define S3C24XX_UDC_SCR_VBUSOFFEN (1 << 11) /* VBUS off enable */
+#define S3C24XX_UDC_SCR_DIEN (1 << 12) /* DUAL interrupt enable */
+#define S3C24XX_UDC_SCR_DTZIEN (1 << 13) /* DMA total counter zero interrupt enable */
+
+/* @IMPORTANT: Dont use the below control bits */
+#define S3C24XX_UDC_EP_DOM (1<<7)
+#define S3C24XX_UDC_EP_FIFO_FLUSH (1<<6)
+#define S3C24XX_UDC_EP_STALL (1<<5)
+#define S3C24XX_UDC_EP_LWO (1<<4)
+#define S3C24XX_UDC_EP_PSIF_ONE (1<<2)
+#define S3C24XX_UDC_EP_PSIF_TWO (2<<2)
+#define S3C24XX_UDC_EP_TX_SUCCESS (1<<1)
+#define S3C24XX_UDC_EP_RX_SUCCESS (1<<0)
+
+#endif /* __ASM_ARCH_REGS_UDC_H */
+
+
diff --git a/include/asm-arm/plat-s3c24xx/spi.h b/include/asm-arm/plat-s3c24xx/spi.h
new file mode 100644
index 000000000000..c420b4ab2b1c
--- /dev/null
+++ b/include/asm-arm/plat-s3c24xx/spi.h
@@ -0,0 +1,55 @@
+/* -*- linux-c -*-
+ *
+ * include/asm-arm/plat-s3c24xx/spi.h
+ *
+ * Copyright (c) 2008 Digi International Spain
+ *
+ *
+ * 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.
+ *
+ * Author : Luis Galdos
+ *
+ */
+
+#ifndef __ASM_ARM_S3C24XX_SPI_H
+#define __ASM_ARM_S3C24XX_SPI_H
+
+/* Macros for the configuration of the GPIOs */
+#define SPI_MOSI_GPIO_OFFSET 0
+#define SPI_MISO_GPIO_OFFSET 1
+#define SPI_CLK_GPIO_OFFSET 2
+#define SPI_EN_GPIO_OFFSET 3
+
+#define MAX_SPI_GPIOS 10
+#define S3C2443_SPI_MAX_CS 1
+
+struct s3c24xx_spi_gpio {
+ unsigned long nr;
+ unsigned long cfg;
+};
+
+/*
+ * The HS SPI controller supports two different input clocks:
+ * PCLK : 33MHz
+ * EPLL : 48MHz
+ */
+enum s3c2443_hsspi_clk {
+ S3C2443_HSSPI_INCLK_EPLL,
+ S3C2443_HSSPI_INCLK_PCLK,
+};
+
+/* Platform data for the SPI-controller */
+struct s3c2443_spi_info {
+ signed short bus_num;
+ struct s3c24xx_spi_gpio mosi;
+ struct s3c24xx_spi_gpio miso;
+ struct s3c24xx_spi_gpio clk;
+ struct s3c24xx_spi_gpio cs[S3C2443_SPI_MAX_CS];
+ unsigned int num_chipselect;
+ enum s3c2443_hsspi_clk input_clk;
+};
+
+
+#endif /* __ASM_ARM_S3C24XX_SPI_H */
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 81797ec9ab29..ec42422c6036 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -88,6 +88,12 @@ struct gpio_chip {
int (*to_irq)(struct gpio_chip *chip,
unsigned offset);
+ void (*pullupdown)(struct gpio_chip *chip,
+ unsigned offset, int value);
+
+ int (*wakeup_configure)(struct gpio_chip *chip,
+ unsigned offset, int value);
+
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
int base;
@@ -113,6 +119,7 @@ extern void gpio_free(unsigned gpio);
extern int gpio_direction_input(unsigned gpio);
extern int gpio_direction_output(unsigned gpio, int value);
+extern int gpio_wakeup_configure(unsigned gpio, int value);
extern int gpio_get_value_cansleep(unsigned gpio);
extern void gpio_set_value_cansleep(unsigned gpio, int value);
@@ -128,6 +135,7 @@ extern void __gpio_set_value(unsigned gpio, int value);
extern int __gpio_cansleep(unsigned gpio);
extern int __gpio_to_irq(unsigned gpio);
+extern void __gpio_set_pullupdown(unsigned gpio, int value);
#ifdef CONFIG_GPIO_SYSFS
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index e531783e5d78..f99ebb9ae347 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -50,6 +50,7 @@ header-y += comstats.h
header-y += const.h
header-y += cgroupstats.h
header-y += cramfs_fs.h
+header-y += cryptodev.h
header-y += cycx_cfm.h
header-y += dlmconstants.h
header-y += dlm_device.h
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
new file mode 100644
index 000000000000..46f2bf517ac1
--- /dev/null
+++ b/include/linux/can/dev.h
@@ -0,0 +1,151 @@
+/*
+ * linux/can/dev.h
+ *
+ * Definitions for the CAN network device driver interface
+ *
+ * Copyright (C) 2006 Andrey Volkov <avolkov@varma-el.com>
+ * Varma Electronics Oy
+ *
+ * Copyright (C) 2008 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ */
+
+#ifndef CAN_DEV_H
+#define CAN_DEV_H
+
+#include <linux/can/error.h>
+
+#if 1
+/* XXX: Under test */
+#define CAN_BITRATE_UNCONFIGURED ((__u32) 0xFFFFFFFFU)
+#define CAN_BITRATE_UNKNOWN 0
+#define CAN_BITRATE_DEFAULT 500000
+
+/*
+ * CAN custom bit time
+ */
+enum can_bittimes {
+ CAN_BITTIME_STD,
+ CAN_BITTIME_BTR
+};
+#endif
+
+/*
+ * CAN bitrate and bit-timing
+ */
+struct can_bittiming {
+ u32 bitrate;
+ u32 sample_point;
+ u32 tq;
+ u32 prop_seg;
+ u32 phase_seg1;
+ u32 phase_seg2;
+ u32 sjw;
+ u32 clock;
+ u32 brp;
+};
+
+struct can_bittiming_const {
+ u32 tseg1_min;
+ u32 tseg1_max;
+ u32 tseg2_min;
+ u32 tseg2_max;
+ u32 sjw_max;
+ u32 brp_min;
+ u32 brp_max;
+ u32 brp_inc;
+};
+
+/*
+ * CAN mode
+ */
+enum can_mode {
+ CAN_MODE_STOP = 0,
+ CAN_MODE_START,
+ CAN_MODE_SLEEP
+};
+
+/*
+ * CAN controller mode
+ */
+#define CAN_CTRLMODE_LOOPBACK 0x1
+#define CAN_CTRLMODE_LISTENONLY 0x2
+#define CAN_CTRLMODE_3_SAMPLES 0x4 /* Triple sampling mode */
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+ CAN_STATE_ACTIVE = 0,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF,
+ CAN_STATE_STOPPED,
+ CAN_STATE_SLEEPING
+};
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+ unsigned long error_warning;
+ unsigned long data_overrun;
+ unsigned long wakeup;
+ unsigned long bus_error;
+ unsigned long error_passive;
+ unsigned long arbitration_lost;
+ unsigned long restarts;
+ unsigned long bus_error_at_init;
+};
+
+/*
+ * CAN common private data
+ */
+#define CAN_ECHO_SKB_MAX 4
+
+struct can_priv {
+ struct can_device_stats can_stats;
+
+ struct can_bittiming bittiming;
+ struct can_bittiming_const *bittiming_const;
+
+ spinlock_t irq_lock;
+
+ enum can_state state;
+ u32 ctrlmode;
+
+ int restart_ms;
+ struct timer_list timer;
+
+ struct sk_buff *echo_skb[CAN_ECHO_SKB_MAX];
+
+ int (*do_set_bittiming)(struct net_device *dev);
+ int (*do_get_state)(struct net_device *dev, enum can_state *state);
+ int (*do_set_mode)(struct net_device *dev, enum can_mode mode);
+ int (*do_set_ctrlmode)(struct net_device *dev, u32 ctrlmode);
+ int (*do_get_ctrlmode)(struct net_device *dev, u32 *ctrlmode);
+};
+
+#define ND2D(_ndev) (_ndev->dev.parent)
+
+
+struct net_device *alloc_candev(int sizeof_priv);
+void free_candev(struct net_device *dev);
+int register_candev(struct net_device *dev);
+void unregister_candev(struct net_device *dev);
+
+int can_set_bittiming(struct net_device *dev);
+
+int can_restart_now(struct net_device *dev);
+
+void can_bus_off(struct net_device *dev);
+
+void can_close_cleanup(struct net_device *dev);
+
+void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, int idx);
+void can_get_echo_skb(struct net_device *dev, int idx);
+
+int can_sample_point(struct can_bittiming *bt);
+
+#endif /* CAN_DEV_H */
diff --git a/include/linux/can/ioctl.h b/include/linux/can/ioctl.h
new file mode 100644
index 000000000000..5a95326ce8b5
--- /dev/null
+++ b/include/linux/can/ioctl.h
@@ -0,0 +1,97 @@
+
+/*
+ * linux/can/ioctl.h
+ *
+ * Definitions for CAN controller setup (work in progress)
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_IOCTL_H
+#define CAN_IOCTL_H
+
+#include <linux/sockios.h>
+
+/*
+ * CAN bitrate
+ */
+#define CAN_BITRATE_UNCONFIGURED ((__u32) 0xFFFFFFFFU)
+#define CAN_BITRATE_UNKNOWN 0
+#define CAN_BITRATE_DEFAULT 500000
+
+/*
+ * CAN custom bit time
+ */
+enum can_bittimes {
+ CAN_BITTIME_STD,
+ CAN_BITTIME_BTR
+};
+
+/* TSEG1 of controllers usually is a sum of synch_seg (always 1),
+ * prop_seg and phase_seg1, TSEG2 = phase_seg2 */
+
+struct can_bittime_std {
+ __u32 brp; /* baud rate prescaler */
+ __u8 prop_seg; /* from 1 to 8 */
+ __u8 phase_seg1; /* from 1 to 8 */
+ __u8 phase_seg2; /* from 1 to 8 */
+ __u8 sjw:7; /* from 1 to 4 */
+ __u8 sam:1; /* 1 - enable triple sampling */
+};
+
+struct can_bittime_btr {
+ __u8 btr0;
+ __u8 btr1;
+};
+
+struct can_bittime {
+ enum can_bittimes type;
+ union {
+ struct can_bittime_std std;
+ struct can_bittime_btr btr;
+ };
+};
+
+/*
+ * CAN mode
+ */
+enum can_mode {
+ CAN_MODE_STOP = 0,
+ CAN_MODE_START,
+ CAN_MODE_SLEEP
+};
+
+/*
+ * CAN controller mode
+ */
+#define CAN_CTRLMODE_LOOPBACK 0x1
+#define CAN_CTRLMODE_LISTENONLY 0x2
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+ CAN_STATE_ACTIVE = 0,
+ CAN_STATE_BUS_WARNING,
+ CAN_STATE_BUS_PASSIVE,
+ CAN_STATE_BUS_OFF,
+ CAN_STATE_STOPPED,
+ CAN_STATE_SLEEPING
+};
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+ int error_warning;
+ int data_overrun;
+ int wakeup;
+ int bus_error;
+ int error_passive;
+ int arbitration_lost;
+ int restarts;
+ int bus_error_at_init;
+};
+
+#endif /* CAN_IOCTL_H */
diff --git a/include/linux/can/platform/mcp251x.h b/include/linux/can/platform/mcp251x.h
new file mode 100644
index 000000000000..d217ffabf77d
--- /dev/null
+++ b/include/linux/can/platform/mcp251x.h
@@ -0,0 +1,34 @@
+#ifndef __CAN_PLATFORM_MCP251X_H__
+#define __CAN_PLATFORM_MCP251X_H__
+
+/*
+ *
+ * CAN bus driver for Microchip 251x CAN Controller with SPI Interface
+ *
+ */
+
+/**
+ * struct mcp251x_platform_data - MCP251X SPI CAN controller platform data
+ * @oscillator_frequency: - oscillator frequency in Hz
+ * @model: - actual type of chip
+ * @board_specific_setup: - called before probing the chip (power,reset)
+ * @transceiver_enable: - called to power on/off the transceiver
+ * @power_enable: - called to power on/off the mcp *and* the
+ * transceiver
+ *
+ * Please note that you should define power_enable or transceiver_enable or
+ * none of them. Defining both of them is no use.
+ *
+ */
+
+struct mcp251x_platform_data {
+ unsigned long oscillator_frequency;
+ int model;
+#define CAN_MCP251X_MCP2510 0
+#define CAN_MCP251X_MCP2515 1
+ int (*board_specific_setup)(struct spi_device *spi);
+ int (*transceiver_enable)(int enable);
+ int (*power_enable) (int enable);
+};
+
+#endif /* __CAN_PLATFORM_MCP251X_H__ */
diff --git a/include/linux/can/platform/sja1000.h b/include/linux/can/platform/sja1000.h
new file mode 100644
index 000000000000..37966e630ff5
--- /dev/null
+++ b/include/linux/can/platform/sja1000.h
@@ -0,0 +1,32 @@
+#ifndef _CAN_PLATFORM_SJA1000_H_
+#define _CAN_PLATFORM_SJA1000_H_
+
+/* clock divider register */
+#define CDR_CLKOUT_MASK 0x07
+#define CDR_CLK_OFF 0x08 /* Clock off (CLKOUT pin) */
+#define CDR_RXINPEN 0x20 /* TX1 output is RX irq output */
+#define CDR_CBP 0x40 /* CAN input comparator bypass */
+#define CDR_PELICAN 0x80 /* PeliCAN mode */
+
+/* output control register */
+#define OCR_MODE_BIPHASE 0x00
+#define OCR_MODE_TEST 0x01
+#define OCR_MODE_NORMAL 0x02
+#define OCR_MODE_CLOCK 0x03
+#define OCR_TX0_INVERT 0x04
+#define OCR_TX0_PULLDOWN 0x08
+#define OCR_TX0_PULLUP 0x10
+#define OCR_TX0_PUSHPULL 0x18
+#define OCR_TX1_INVERT 0x20
+#define OCR_TX1_PULLDOWN 0x40
+#define OCR_TX1_PULLUP 0x80
+#define OCR_TX1_PUSHPULL 0xc0
+
+struct sja1000_platform_data {
+ u32 clock; /* CAN bus oscillator frequency in Hz */
+
+ u8 ocr; /* output control register */
+ u8 cdr; /* clock divider register */
+};
+
+#endif /* !_CAN_PLATFORM_SJA1000_H_ */
diff --git a/include/linux/cryptodev.h b/include/linux/cryptodev.h
new file mode 100644
index 000000000000..0f8f6705cdc9
--- /dev/null
+++ b/include/linux/cryptodev.h
@@ -0,0 +1,82 @@
+/*
+ * Driver for /dev/crypto device (aka CryptoDev)
+ *
+ * Copyright (c) 2004 Michal Ludvig <mludvig@suse.cz>, SuSE Labs
+ *
+ * Structures and ioctl command names were taken from
+ * OpenBSD to preserve compatibility with their API.
+ *
+ */
+
+#ifndef _CRYPTODEV_H
+#define _CRYPTODEV_H
+
+#ifndef __KERNEL__
+#include <inttypes.h>
+#endif
+
+#define CRYPTODEV_MINOR MISC_DYNAMIC_MINOR
+
+#define CRYPTO_FLAG_ECB 0x0000
+#define CRYPTO_FLAG_CBC 0x0001
+#define CRYPTO_FLAG_CFB 0x0002
+#define CRYPTO_FLAG_OFB 0x0003
+#define CRYPTO_FLAG_CTR 0x0004
+#define CRYPTO_FLAG_HMAC 0x0010
+#define CRYPTO_FLAG_MASK 0x00FF
+
+#define CRYPTO_CIPHER_NAME 0x0100
+#define CRYPTO_CIPHER_NAME_CBC (CRYPTO_CIPHER_NAME | CRYPTO_FLAG_CBC)
+#define CRYPTO_HASH_NAME 0x0200
+#define CRYPTO_HASH_NAME_HMAC (CRYPTO_HASH_NAME | CRYPTO_FLAG_HMAC)
+
+/* ioctl parameter to create a session */
+struct session_op {
+ unsigned int cipher; /* e.g. CRYPTO_DES_CBC */
+ unsigned int mac; /* e.g. CRYPTO_MD5_HMAC */
+ char *alg_name; /* set cipher=CRYPTO_CIPHER_NAME
+ or mac=CRYPTO_HASH_NAME */
+ #define MAX_ALG_NAME_LEN 128
+ size_t alg_namelen;
+
+ size_t keylen; /* cipher key */
+ char *key;
+ int mackeylen; /* mac key */
+ char *mackey;
+
+ /* Return values */
+ unsigned int blocksize; /* selected algorithm's block size */
+ uint32_t ses; /* session ID */
+};
+
+/* ioctl parameter to request a crypt/decrypt operation against a session */
+struct crypt_op {
+ uint32_t ses; /* from session_op->ses */
+ #define COP_DECRYPT 0
+ #define COP_ENCRYPT 1
+ uint32_t op; /* ie. COP_ENCRYPT */
+ uint32_t flags; /* unused */
+
+ size_t len;
+ char *src, *dst;
+ char *mac;
+ char *iv;
+};
+
+/* clone original filedescriptor */
+#define CRIOGET _IOWR('c', 100, uint32_t)
+
+/* create crypto session */
+#define CIOCGSESSION _IOWR('c', 101, struct session_op)
+
+/* finish crypto session */
+#define CIOCFSESSION _IOW('c', 102, uint32_t)
+
+/* request encryption/decryptions of a given buffer */
+#define CIOCCRYPT _IOWR('c', 103, struct crypt_op)
+
+/* ioctl()s for asym-crypto. Not yet supported. */
+#define CIOCKEY _IOWR('c', 104, void *)
+#define CIOCASYMFEAT _IOR('c', 105, uint32_t)
+
+#endif /* _CRYPTODEV_H */
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 708bab58d8d0..6fac52cff1c9 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -6,7 +6,7 @@
*
* Maintainer: Kumar Gala <galak@kernel.crashing.org>
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright 2004, 2009 Freescale Semiconductor, 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
@@ -92,6 +92,15 @@ enum fsl_usb2_operating_modes {
FSL_USB2_DR_OTG,
};
+/* this used for usb port type */
+enum fsl_usb2_modes {
+ FSL_USB_DR_HOST,
+ FSL_USB_DR_DEVICE,
+ FSL_USB_MPH_HOST1,
+ FSL_USB_MPH_HOST2,
+ FSL_USB_UNKNOWN, /* unkonwn status */
+};
+
enum fsl_usb2_phy_modes {
FSL_USB2_PHY_NONE,
FSL_USB2_PHY_ULPI,
@@ -100,11 +109,49 @@ enum fsl_usb2_phy_modes {
FSL_USB2_PHY_SERIAL,
};
+struct platform_device;
struct fsl_usb2_platform_data {
/* board specific information */
enum fsl_usb2_operating_modes operating_mode;
enum fsl_usb2_phy_modes phy_mode;
unsigned int port_enables;
+
+ char *name; /* pretty print */
+ int (*platform_init) (struct platform_device *);
+ void (*platform_uninit) (struct fsl_usb2_platform_data *);
+ void __iomem *regs; /* ioremap'd register base */
+ u32 xcvr_type; /* PORTSC_PTS_* */
+ char *transceiver; /* transceiver name */
+ unsigned power_budget; /* for hcd->power_budget */
+ struct platform_device *pdev;
+ struct fsl_xcvr_ops *xcvr_ops;
+ struct fsl_xcvr_power *xcvr_pwr;
+ int (*gpio_usb_active) (void);
+ void (*gpio_usb_inactive) (void);
+ void (*usb_clock_for_pm) (bool);
+ void (*platform_suspend)(struct fsl_usb2_platform_data *);
+ void (*platform_resume)(struct fsl_usb2_platform_data *);
+ void (*wake_up_enable)(struct fsl_usb2_platform_data *pdata, bool on);
+ unsigned big_endian_mmio : 1;
+ unsigned big_endian_desc : 1;
+ unsigned es : 1; /* need USBMODE:ES */
+ unsigned have_sysif_regs : 1;
+ unsigned le_setup_buf : 1;
+ unsigned change_ahb_burst:1;
+ unsigned ahb_burst_mode:3;
+ unsigned suspended : 1;
+ unsigned already_suspended : 1;
+
+ /* register save area for suspend/resume */
+ u32 pm_command;
+ u32 pm_status;
+ u32 pm_intr_enable;
+ u32 pm_frame_index;
+ u32 pm_segment;
+ u32 pm_frame_list;
+ u32 pm_async_next;
+ u32 pm_configured_flag;
+ u32 pm_portsc;
};
/* Flags in fsl_usb2_mph_platform_data */
@@ -122,6 +169,20 @@ struct fsl_spi_platform_data {
u32 sysclk;
};
+struct fsl_ata_platform_data {
+ int adma_flag; /* AMDA mode is used or not, 1:used.*/
+ int udma_mask; /* UDMA modes h/w can handle */
+ int mwdma_mask; /* MDMA modes h/w can handle */
+ int pio_mask; /* PIO modes h/w can handle */
+ int fifo_alarm; /* value for fifo_alarm reg */
+ int max_sg; /* longest sglist h/w can handle */
+ int (*init)(struct platform_device *pdev);
+ void (*exit)(void);
+ char *io_reg;
+ char *core_reg;
+};
+
+
struct mpc8xx_pcmcia_ops {
void(*hw_ctrl)(int slot, int enable);
int(*voltage_set)(int slot, int vcc, int vpp);
diff --git a/include/linux/i2c-ns9xxx.h b/include/linux/i2c-ns9xxx.h
new file mode 100644
index 000000000000..0e9e1797f51a
--- /dev/null
+++ b/include/linux/i2c-ns9xxx.h
@@ -0,0 +1,22 @@
+/*
+ * include/linux/i2c-ns9xxx.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef _LINUX_I2C_NS9XXX_H
+#define _LINUX_I2C_NS9XXX_H
+
+struct plat_ns9xxx_i2c {
+ unsigned int gpio_scl;
+ unsigned int gpio_sda;
+ unsigned int speed;
+ void (*gpio_configuration_func)(void);
+};
+
+#endif /* ifndef _LINUX_I2C_NS9XXX_H */
+
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 14126bc36641..c4e6ca1a6306 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -12,8 +12,8 @@
* published by the Free Software Foundation.
*/
-#ifndef IEEE80211_H
-#define IEEE80211_H
+#ifndef LINUX_IEEE80211_H
+#define LINUX_IEEE80211_H
#include <linux/types.h>
#include <asm/byteorder.h>
@@ -97,7 +97,10 @@
#define IEEE80211_MAX_FRAME_LEN 2352
#define IEEE80211_MAX_SSID_LEN 32
+
#define IEEE80211_MAX_MESH_ID_LEN 32
+#define IEEE80211_MESH_CONFIG_LEN 19
+
#define IEEE80211_QOS_CTL_LEN 2
#define IEEE80211_QOS_CTL_TID_MASK 0x000F
#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007
@@ -666,6 +669,13 @@ struct ieee80211_cts {
u8 ra[6];
} __attribute__ ((packed));
+struct ieee80211_pspoll {
+ __le16 frame_control;
+ __le16 aid;
+ u8 bssid[6];
+ u8 ta[6];
+} __attribute__ ((packed));
+
/**
* struct ieee80211_bar - HT Block Ack Request
*
@@ -685,28 +695,88 @@ struct ieee80211_bar {
#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000
#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004
+
+#define IEEE80211_HT_MCS_MASK_LEN 10
+
+/**
+ * struct ieee80211_mcs_info - MCS information
+ * @rx_mask: RX mask
+ * @rx_highest: highest supported RX rate
+ * @tx_params: TX parameters
+ */
+struct ieee80211_mcs_info {
+ u8 rx_mask[IEEE80211_HT_MCS_MASK_LEN];
+ __le16 rx_highest;
+ u8 tx_params;
+ u8 reserved[3];
+} __attribute__((packed));
+
+/* 802.11n HT capability MSC set */
+#define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff
+#define IEEE80211_HT_MCS_TX_DEFINED 0x01
+#define IEEE80211_HT_MCS_TX_RX_DIFF 0x02
+/* value 0 == 1 stream etc */
+#define IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK 0x0C
+#define IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT 2
+#define IEEE80211_HT_MCS_TX_MAX_STREAMS 4
+#define IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION 0x10
+
+/*
+ * 802.11n D5.0 20.3.5 / 20.6 says:
+ * - indices 0 to 7 and 32 are single spatial stream
+ * - 8 to 31 are multiple spatial streams using equal modulation
+ * [8..15 for two streams, 16..23 for three and 24..31 for four]
+ * - remainder are multiple spatial streams using unequal modulation
+ */
+#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START 33
+#define IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE \
+ (IEEE80211_HT_MCS_UNEQUAL_MODULATION_START / 8)
+
/**
* struct ieee80211_ht_cap - HT capabilities
*
- * This structure refers to "HT capabilities element" as
- * described in 802.11n draft section 7.3.2.52
+ * This structure is the "HT capabilities element" as
+ * described in 802.11n D5.0 7.3.2.57
*/
struct ieee80211_ht_cap {
__le16 cap_info;
u8 ampdu_params_info;
- u8 supp_mcs_set[16];
+
+ /* 16 bytes MCS information */
+ struct ieee80211_mcs_info mcs;
+
__le16 extended_ht_cap_info;
__le32 tx_BF_cap_info;
u8 antenna_selection_info;
} __attribute__ ((packed));
+/* 802.11n HT capabilities masks (for cap_info) */
+#define IEEE80211_HT_CAP_LDPC_CODING 0x0001
+#define IEEE80211_HT_CAP_SUP_WIDTH_20_40 0x0002
+#define IEEE80211_HT_CAP_SM_PS 0x000C
+#define IEEE80211_HT_CAP_GRN_FLD 0x0010
+#define IEEE80211_HT_CAP_SGI_20 0x0020
+#define IEEE80211_HT_CAP_SGI_40 0x0040
+#define IEEE80211_HT_CAP_TX_STBC 0x0080
+#define IEEE80211_HT_CAP_RX_STBC 0x0300
+#define IEEE80211_HT_CAP_DELAY_BA 0x0400
+#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
+#define IEEE80211_HT_CAP_DSSSCCK40 0x1000
+#define IEEE80211_HT_CAP_PSMP_SUPPORT 0x2000
+#define IEEE80211_HT_CAP_40MHZ_INTOLERANT 0x4000
+#define IEEE80211_HT_CAP_LSIG_TXOP_PROT 0x8000
+
+/* 802.11n HT capability AMPDU settings (for ampdu_params_info) */
+#define IEEE80211_HT_AMPDU_PARM_FACTOR 0x03
+#define IEEE80211_HT_AMPDU_PARM_DENSITY 0x1C
+
/**
- * struct ieee80211_ht_cap - HT additional information
+ * struct ieee80211_ht_info - HT information
*
- * This structure refers to "HT information element" as
- * described in 802.11n draft section 7.3.2.53
+ * This structure is the "HT information element" as
+ * described in 802.11n D5.0 7.3.2.58
*/
-struct ieee80211_ht_addt_info {
+struct ieee80211_ht_info {
u8 control_chan;
u8 ht_param;
__le16 operation_mode;
@@ -714,36 +784,33 @@ struct ieee80211_ht_addt_info {
u8 basic_set[16];
} __attribute__ ((packed));
-/* 802.11n HT capabilities masks */
-#define IEEE80211_HT_CAP_SUP_WIDTH 0x0002
-#define IEEE80211_HT_CAP_SM_PS 0x000C
-#define IEEE80211_HT_CAP_GRN_FLD 0x0010
-#define IEEE80211_HT_CAP_SGI_20 0x0020
-#define IEEE80211_HT_CAP_SGI_40 0x0040
-#define IEEE80211_HT_CAP_DELAY_BA 0x0400
-#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
-#define IEEE80211_HT_CAP_DSSSCCK40 0x1000
-/* 802.11n HT capability AMPDU settings */
-#define IEEE80211_HT_CAP_AMPDU_FACTOR 0x03
-#define IEEE80211_HT_CAP_AMPDU_DENSITY 0x1C
-/* 802.11n HT capability MSC set */
-#define IEEE80211_SUPP_MCS_SET_UEQM 4
-#define IEEE80211_HT_CAP_MAX_STREAMS 4
-#define IEEE80211_SUPP_MCS_SET_LEN 10
-/* maximum streams the spec allows */
-#define IEEE80211_HT_CAP_MCS_TX_DEFINED 0x01
-#define IEEE80211_HT_CAP_MCS_TX_RX_DIFF 0x02
-#define IEEE80211_HT_CAP_MCS_TX_STREAMS 0x0C
-#define IEEE80211_HT_CAP_MCS_TX_UEQM 0x10
-/* 802.11n HT IE masks */
-#define IEEE80211_HT_IE_CHA_SEC_OFFSET 0x03
-#define IEEE80211_HT_IE_CHA_SEC_NONE 0x00
-#define IEEE80211_HT_IE_CHA_SEC_ABOVE 0x01
-#define IEEE80211_HT_IE_CHA_SEC_BELOW 0x03
-#define IEEE80211_HT_IE_CHA_WIDTH 0x04
-#define IEEE80211_HT_IE_HT_PROTECTION 0x0003
-#define IEEE80211_HT_IE_NON_GF_STA_PRSNT 0x0004
-#define IEEE80211_HT_IE_NON_HT_STA_PRSNT 0x0010
+/* for ht_param */
+#define IEEE80211_HT_PARAM_CHA_SEC_OFFSET 0x03
+#define IEEE80211_HT_PARAM_CHA_SEC_NONE 0x00
+#define IEEE80211_HT_PARAM_CHA_SEC_ABOVE 0x01
+#define IEEE80211_HT_PARAM_CHA_SEC_BELOW 0x03
+#define IEEE80211_HT_PARAM_CHAN_WIDTH_ANY 0x04
+#define IEEE80211_HT_PARAM_RIFS_MODE 0x08
+#define IEEE80211_HT_PARAM_SPSMP_SUPPORT 0x10
+#define IEEE80211_HT_PARAM_SERV_INTERVAL_GRAN 0xE0
+
+/* for operation_mode */
+#define IEEE80211_HT_OP_MODE_PROTECTION 0x0003
+#define IEEE80211_HT_OP_MODE_PROTECTION_NONE 0
+#define IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER 1
+#define IEEE80211_HT_OP_MODE_PROTECTION_20MHZ 2
+#define IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED 3
+#define IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT 0x0004
+#define IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT 0x0010
+
+/* for stbc_param */
+#define IEEE80211_HT_STBC_PARAM_DUAL_BEACON 0x0040
+#define IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT 0x0080
+#define IEEE80211_HT_STBC_PARAM_STBC_BEACON 0x0100
+#define IEEE80211_HT_STBC_PARAM_LSIG_TXOP_FULLPROT 0x0200
+#define IEEE80211_HT_STBC_PARAM_PCO_ACTIVE 0x0400
+#define IEEE80211_HT_STBC_PARAM_PCO_PHASE 0x0800
+
/* block-ack parameters */
#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
@@ -769,7 +836,6 @@ struct ieee80211_ht_addt_info {
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
-#define WLAN_AUTH_FAST_BSS_TRANSITION 2
#define WLAN_AUTH_LEAP 128
#define WLAN_AUTH_CHALLENGE_LEN 128
@@ -949,7 +1015,7 @@ enum ieee80211_eid {
WLAN_EID_EXT_SUPP_RATES = 50,
/* 802.11n */
WLAN_EID_HT_CAPABILITY = 45,
- WLAN_EID_HT_EXTRA_INFO = 61,
+ WLAN_EID_HT_INFORMATION = 61,
/* 802.11i */
WLAN_EID_RSN = 48,
WLAN_EID_WPA = 221,
@@ -976,6 +1042,68 @@ enum ieee80211_spectrum_mgmt_actioncode {
WLAN_ACTION_SPCT_CHL_SWITCH = 4,
};
+/*
+ * IEEE 802.11-2007 7.3.2.9 Country information element
+ *
+ * Minimum length is 8 octets, ie len must be evenly
+ * divisible by 2
+ */
+
+/* Although the spec says 8 I'm seeing 6 in practice */
+#define IEEE80211_COUNTRY_IE_MIN_LEN 6
+
+/*
+ * For regulatory extension stuff see IEEE 802.11-2007
+ * Annex I (page 1141) and Annex J (page 1147). Also
+ * review 7.3.2.9.
+ *
+ * When dot11RegulatoryClassesRequired is true and the
+ * first_channel/reg_extension_id is >= 201 then the IE
+ * compromises of the 'ext' struct represented below:
+ *
+ * - Regulatory extension ID - when generating IE this just needs
+ * to be monotonically increasing for each triplet passed in
+ * the IE
+ * - Regulatory class - index into set of rules
+ * - Coverage class - index into air propagation time (Table 7-27),
+ * in microseconds, you can compute the air propagation time from
+ * the index by multiplying by 3, so index 10 yields a propagation
+ * of 10 us. Valid values are 0-31, values 32-255 are not defined
+ * yet. A value of 0 inicates air propagation of <= 1 us.
+ *
+ * See also Table I.2 for Emission limit sets and table
+ * I.3 for Behavior limit sets. Table J.1 indicates how to map
+ * a reg_class to an emission limit set and behavior limit set.
+ */
+#define IEEE80211_COUNTRY_EXTENSION_ID 201
+
+/*
+ * Channels numbers in the IE must be monotonically increasing
+ * if dot11RegulatoryClassesRequired is not true.
+ *
+ * If dot11RegulatoryClassesRequired is true consecutive
+ * subband triplets following a regulatory triplet shall
+ * have monotonically increasing first_channel number fields.
+ *
+ * Channel numbers shall not overlap.
+ *
+ * Note that max_power is signed.
+ */
+struct ieee80211_country_ie_triplet {
+ union {
+ struct {
+ u8 first_channel;
+ u8 num_channels;
+ s8 max_power;
+ } __attribute__ ((packed)) chans;
+ struct {
+ u8 reg_extension_id;
+ u8 reg_class;
+ u8 coverage_class;
+ } __attribute__ ((packed)) ext;
+ };
+} __attribute__ ((packed));
+
/* BACK action code */
enum ieee80211_back_actioncode {
WLAN_ACTION_ADDBA_REQ = 0,
@@ -1057,4 +1185,4 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr)
return hdr->addr1;
}
-#endif /* IEEE80211_H */
+#endif /* LINUX_IEEE80211_H */
diff --git a/include/linux/imx_adc.h b/include/linux/imx_adc.h
new file mode 100644
index 000000000000..5c0e2462b1ef
--- /dev/null
+++ b/include/linux/imx_adc.h
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __ASM_ARCH_IMX_ADC_H__
+#define __ASM_ARCH_IMX_ADC_H__
+
+/*!
+ * @defgroup IMX_ADC Digitizer Driver
+ * @ingroup IMX_DRVRS
+ */
+
+/*!
+ * @file arch-mxc/imx_adc.h
+ * @brief This is the header of IMX ADC driver.
+ *
+ * @ingroup IMX_ADC
+ */
+
+#include <linux/ioctl.h>
+
+/*!
+ * @enum IMX_ADC_STATUS
+ * @brief Define return values for all IMX_ADC APIs.
+ *
+ * These return values are used by all of the IMX_ADC APIs.
+ *
+ * @ingroup IMX_ADC
+ */
+enum IMX_ADC_STATUS {
+ /*! The requested operation was successfully completed. */
+ IMX_ADC_SUCCESS = 0,
+ /*! The requested operation could not be completed due to an error. */
+ IMX_ADC_ERROR = -1,
+ /*!
+ * The requested operation failed because one or more of the
+ * parameters was invalid.
+ */
+ IMX_ADC_PARAMETER_ERROR = -2,
+ /*!
+ * The requested operation could not be completed because the ADC
+ * hardware does not support it.
+ */
+ IMX_ADC_NOT_SUPPORTED = -3,
+ /*! Error in malloc function */
+ IMX_ADC_MALLOC_ERROR = -5,
+ /*! Error in un-subscribe event */
+ IMX_ADC_UNSUBSCRIBE_ERROR = -6,
+ /*! Event occur and not subscribed */
+ IMX_ADC_EVENT_NOT_SUBSCRIBED = -7,
+ /*! Error - bad call back */
+ IMX_ADC_EVENT_CALL_BACK = -8,
+ /*!
+ * The requested operation could not be completed because there
+ * are too many ADC client requests
+ */
+ IMX_ADC_CLIENT_NBOVERFLOW = -9,
+};
+
+/*
+ * Macros implementing error handling
+ */
+#define CHECK_ERROR(a) \
+do { \
+ int ret = (a); \
+ if (ret != IMX_ADC_SUCCESS) \
+ return ret; \
+} while (0)
+
+#define CHECK_ERROR_KFREE(func, freeptrs) \
+do { \
+ int ret = (func); \
+ if (ret != IMX_ADC_SUCCESS) { \
+ freeptrs; \
+ return ret; \
+ } \
+} while (0)
+
+#define MOD_NAME "mxcadc"
+
+/*!
+ * @name IOCTL user space interface
+ */
+
+/*!
+ * Initialize ADC.
+ * Argument type: none.
+ */
+#define IMX_ADC_INIT _IO('p', 0xb0)
+/*!
+ * De-initialize ADC.
+ * Argument type: none.
+ */
+#define IMX_ADC_DEINIT _IO('p', 0xb1)
+/*!
+ * Convert one channel.
+ * Argument type: pointer to t_adc_convert_param.
+ */
+#define IMX_ADC_CONVERT _IOWR('p', 0xb2, int)
+/*!
+ * Convert multiple channels.
+ * Argument type: pointer to t_adc_convert_param.
+ */
+#define IMX_ADC_CONVERT_MULTICHANNEL _IOWR('p', 0xb4, int)
+
+/*! @{ */
+/*!
+ * @name Touch Screen minimum and maximum values
+ */
+#define IMX_ADC_DEVICE "/dev/imx_adc"
+
+/*
+ * Maximun allowed variation in the three X/Y co-ordinates acquired from
+ * touch screen
+ */
+#define DELTA_Y_MAX 100
+#define DELTA_X_MAX 100
+
+/* Upon clearing the filter, this is the delay in restarting the filter */
+#define FILTER_MIN_DELAY 4
+
+/* Length of X and Y touch screen filters */
+#define FILTLEN 3
+
+#define TS_X_MAX 1000
+#define TS_Y_MAX 1000
+
+#define TS_X_MIN 80
+#define TS_Y_MIN 80
+
+/*! @} */
+/*!
+ * This enumeration defines input channels for IMX ADC
+ */
+
+enum t_channel {
+ TS_X_POS,
+ TS_Y_POS,
+ GER_PURPOSE_ADC0,
+ GER_PURPOSE_ADC1,
+ GER_PURPOSE_ADC2,
+ GER_PURPOSE_MULTICHNNEL,
+};
+
+/*!
+ * This structure is used to report touch screen value.
+ */
+struct t_touch_screen {
+ /* Touch Screen X position */
+ unsigned int x_position;
+ /* Touch Screen X position1 */
+ unsigned int x_position1;
+ /* Touch Screen X position2 */
+ unsigned int x_position2;
+ /* Touch Screen X position3 */
+ unsigned int x_position3;
+ /* Touch Screen Y position */
+ unsigned int y_position;
+ /* Touch Screen Y position1 */
+ unsigned int y_position1;
+ /* Touch Screen Y position2 */
+ unsigned int y_position2;
+ /* Touch Screen Y position3 */
+ unsigned int y_position3;
+ /* Touch Screen contact value */
+ unsigned int contact_resistance;
+ /* Flag indicate the data usability */
+ unsigned int valid_flag;
+};
+
+/*!
+ * This structure is used with IOCTL code \a IMX_ADC_CONVERT,
+ * \a IMX_ADC_CONVERT_8X and \a IMX_ADC_CONVERT_MULTICHANNEL.
+ */
+
+struct t_adc_convert_param {
+ /* channel or channels to be sampled. */
+ enum t_channel channel;
+ /* holds up to 16 sampling results */
+ unsigned short result[16];
+};
+
+/* EXPORTED FUNCTIONS */
+
+#ifdef __KERNEL__
+/* Driver data */
+struct imx_adc_data {
+ u32 irq;
+ struct clk *adc_clk;
+};
+
+/*!
+ * This function initializes all ADC registers with default values. This
+ * function also registers the interrupt events.
+ *
+ * @return This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_init(void);
+
+/*!
+ * This function disables the ADC, de-registers the interrupt events.
+ *
+ * @return This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_deinit(void);
+
+/*!
+ * This function triggers a conversion and returns one sampling result of one
+ * channel.
+ *
+ * @param channel The channel to be sampled
+ * @param result The pointer to the conversion result. The memory
+ * should be allocated by the caller of this function.
+ *
+ * @return This function returns IMX_ADC_SUCCESS if successful.
+ */
+
+enum IMX_ADC_STATUS imx_adc_convert(enum t_channel channel,
+ unsigned short *result);
+
+/*!
+ * This function triggers a conversion and returns sampling results of each
+ * specified channel.
+ *
+ * @param channels This input parameter is bitmap to specify channels
+ * to be sampled.
+ * @param result The pointer to array to store sampling result.
+ * The order of the result in the array is from lowest
+ * channel number to highest channel number of the
+ * sampled channels.
+ * The memory should be allocated by the caller of this
+ * function.
+ * Note that the behavior of this function might differ
+ * from one platform to another regarding especially
+ * channels order.
+ *
+ * @return This function returns IMX_ADC_SUCCESS if successful.
+ */
+
+enum IMX_ADC_STATUS imx_adc_convert_multichnnel(enum t_channel channels,
+ unsigned short *result);
+
+/*!
+ * This function retrieves the current touch screen operation mode.
+ *
+ * @param touch_sample Pointer to touch sample.
+ * @param wait_tsi if true, we wait until interrupt occurs
+ * @return This function returns IMX_ADC_SUCCESS if successful.
+ */
+enum IMX_ADC_STATUS imx_adc_get_touch_sample(struct t_touch_screen *ts_value,
+ int wait_tsi);
+
+/*!
+ * This function read the touch screen value.
+ *
+ * @param touch_sample return value of touch screen
+ * @param wait_tsi if true, we need wait until interrupt occurs
+ * @return This function returns 0.
+ */
+enum IMX_ADC_STATUS imx_adc_read_ts(struct t_touch_screen *touch_sample,
+ int wait_tsi);
+
+int is_imx_adc_ready(void);
+
+#endif /* _KERNEL */
+#endif /* __ASM_ARCH_IMX_ADC_H__ */
diff --git a/include/linux/ipu.h b/include/linux/ipu.h
new file mode 100644
index 000000000000..167abf1b900a
--- /dev/null
+++ b/include/linux/ipu.h
@@ -0,0 +1,1235 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+/*!
+ * @defgroup IPU MXC Image Processing Unit (IPU) Driver
+ */
+/*!
+ * @file arch-mxc/ipu.h
+ *
+ * @brief This file contains the IPU driver API declarations.
+ *
+ * @ingroup IPU
+ */
+
+#ifndef __ASM_ARCH_IPU_H__
+#define __ASM_ARCH_IPU_H__
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#ifdef __KERNEL__
+#include <linux/interrupt.h>
+#else
+#ifndef __cplusplus
+typedef unsigned char bool;
+#endif
+#define irqreturn_t int
+#define dma_addr_t int
+#define u32 unsigned int
+#define __u32 u32
+#endif
+
+/*!
+ * Enumeration of IPU rotation modes
+ */
+typedef enum {
+ /* Note the enum values correspond to BAM value */
+ IPU_ROTATE_NONE = 0,
+ IPU_ROTATE_VERT_FLIP = 1,
+ IPU_ROTATE_HORIZ_FLIP = 2,
+ IPU_ROTATE_180 = 3,
+ IPU_ROTATE_90_RIGHT = 4,
+ IPU_ROTATE_90_RIGHT_VFLIP = 5,
+ IPU_ROTATE_90_RIGHT_HFLIP = 6,
+ IPU_ROTATE_90_LEFT = 7,
+} ipu_rotate_mode_t;
+
+/*!
+ * Enumeration of Post Filter modes
+ */
+typedef enum {
+ PF_DISABLE_ALL = 0,
+ PF_MPEG4_DEBLOCK = 1,
+ PF_MPEG4_DERING = 2,
+ PF_MPEG4_DEBLOCK_DERING = 3,
+ PF_H264_DEBLOCK = 4,
+} pf_operation_t;
+
+/*!
+ * Enumeration of Synchronous (Memory-less) panel types
+ */
+typedef enum {
+ IPU_PANEL_SHARP_TFT,
+ IPU_PANEL_TFT,
+} ipu_panel_t;
+
+/*!
+ * Enumeration of VDI MOTION select
+ */
+typedef enum {
+ MED_MOTION = 0,
+ LOW_MOTION = 1,
+ HIGH_MOTION = 2,
+} ipu_motion_sel;
+
+/* IPU Pixel format definitions */
+/* Four-character-code (FOURCC) */
+#define fourcc(a, b, c, d)\
+ (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24))
+
+/*!
+ * @name IPU Pixel Formats
+ *
+ * Pixel formats are defined with ASCII FOURCC code. The pixel format codes are
+ * the same used by V4L2 API.
+ */
+
+/*! @{ */
+/*! @name Generic or Raw Data Formats */
+/*! @{ */
+#define IPU_PIX_FMT_GENERIC fourcc('I', 'P', 'U', '0') /*!< IPU Generic Data */
+#define IPU_PIX_FMT_GENERIC_32 fourcc('I', 'P', 'U', '1') /*!< IPU Generic Data */
+#define IPU_PIX_FMT_LVDS666 fourcc('L', 'V', 'D', '6') /*!< IPU Generic Data */
+#define IPU_PIX_FMT_LVDS888 fourcc('L', 'V', 'D', '8') /*!< IPU Generic Data */
+/*! @} */
+/*! @name RGB Formats */
+/*! @{ */
+#define IPU_PIX_FMT_RGB332 fourcc('R', 'G', 'B', '1') /*!< 8 RGB-3-3-2 */
+#define IPU_PIX_FMT_RGB555 fourcc('R', 'G', 'B', 'O') /*!< 16 RGB-5-5-5 */
+#define IPU_PIX_FMT_RGB565 fourcc('R', 'G', 'B', 'P') /*!< 1 6 RGB-5-6-5 */
+#define IPU_PIX_FMT_RGB666 fourcc('R', 'G', 'B', '6') /*!< 18 RGB-6-6-6 */
+#define IPU_PIX_FMT_BGR666 fourcc('B', 'G', 'R', '6') /*!< 18 BGR-6-6-6 */
+#define IPU_PIX_FMT_BGR24 fourcc('B', 'G', 'R', '3') /*!< 24 BGR-8-8-8 */
+#define IPU_PIX_FMT_RGB24 fourcc('R', 'G', 'B', '3') /*!< 24 RGB-8-8-8 */
+#define IPU_PIX_FMT_BGR32 fourcc('B', 'G', 'R', '4') /*!< 32 BGR-8-8-8-8 */
+#define IPU_PIX_FMT_BGRA32 fourcc('B', 'G', 'R', 'A') /*!< 32 BGR-8-8-8-8 */
+#define IPU_PIX_FMT_RGB32 fourcc('R', 'G', 'B', '4') /*!< 32 RGB-8-8-8-8 */
+#define IPU_PIX_FMT_RGBA32 fourcc('R', 'G', 'B', 'A') /*!< 32 RGB-8-8-8-8 */
+#define IPU_PIX_FMT_ABGR32 fourcc('A', 'B', 'G', 'R') /*!< 32 ABGR-8-8-8-8 */
+/*! @} */
+/*! @name YUV Interleaved Formats */
+/*! @{ */
+#define IPU_PIX_FMT_YUYV fourcc('Y', 'U', 'Y', 'V') /*!< 16 YUV 4:2:2 */
+#define IPU_PIX_FMT_UYVY fourcc('U', 'Y', 'V', 'Y') /*!< 16 YUV 4:2:2 */
+#define IPU_PIX_FMT_Y41P fourcc('Y', '4', '1', 'P') /*!< 12 YUV 4:1:1 */
+#define IPU_PIX_FMT_YUV444 fourcc('Y', '4', '4', '4') /*!< 24 YUV 4:4:4 */
+/* two planes -- one Y, one Cb + Cr interleaved */
+#define IPU_PIX_FMT_NV12 fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */
+/*! @} */
+/*! @name YUV Planar Formats */
+/*! @{ */
+#define IPU_PIX_FMT_GREY fourcc('G', 'R', 'E', 'Y') /*!< 8 Greyscale */
+#define IPU_PIX_FMT_YVU410P fourcc('Y', 'V', 'U', '9') /*!< 9 YVU 4:1:0 */
+#define IPU_PIX_FMT_YUV410P fourcc('Y', 'U', 'V', '9') /*!< 9 YUV 4:1:0 */
+#define IPU_PIX_FMT_YVU420P fourcc('Y', 'V', '1', '2') /*!< 12 YVU 4:2:0 */
+#define IPU_PIX_FMT_YUV420P fourcc('I', '4', '2', '0') /*!< 12 YUV 4:2:0 */
+#define IPU_PIX_FMT_YUV420P2 fourcc('Y', 'U', '1', '2') /*!< 12 YUV 4:2:0 */
+#define IPU_PIX_FMT_YVU422P fourcc('Y', 'V', '1', '6') /*!< 16 YVU 4:2:2 */
+#define IPU_PIX_FMT_YUV422P fourcc('4', '2', '2', 'P') /*!< 16 YUV 4:2:2 */
+/*! @} */
+
+/* IPU Driver channels definitions. */
+/* Note these are different from IDMA channels */
+#ifdef CONFIG_MXC_IPU_V1
+#define _MAKE_CHAN(num, in, out, sec) ((num << 24) | (sec << 16) | (out << 8) | in)
+#define IPU_CHAN_ID(ch) (ch >> 24)
+#define IPU_CHAN_SEC_DMA(ch) ((uint32_t) (ch >> 16) & 0xFF)
+#define IPU_CHAN_OUT_DMA(ch) ((uint32_t) (ch >> 8) & 0xFF)
+#define IPU_CHAN_IN_DMA(ch) ((uint32_t) (ch & 0xFF))
+
+#else
+#define IPU_MAX_CH 32
+#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \
+ ((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out)
+#define _MAKE_ALT_CHAN(ch) (ch | (IPU_MAX_CH << 24))
+#define IPU_CHAN_ID(ch) (ch >> 24)
+#define IPU_CHAN_ALT(ch) (ch & 0x02000000)
+#define IPU_CHAN_ALPHA_IN_DMA(ch) ((uint32_t) (ch >> 6) & 0x3F)
+#define IPU_CHAN_GRAPH_IN_DMA(ch) ((uint32_t) (ch >> 12) & 0x3F)
+#define IPU_CHAN_VIDEO_IN_DMA(ch) ((uint32_t) (ch >> 18) & 0x3F)
+#define IPU_CHAN_OUT_DMA(ch) ((uint32_t) (ch & 0x3F))
+#define NO_DMA 0x3F
+#define ALT 1
+#endif
+/*!
+ * Enumeration of IPU logical channels. An IPU logical channel is defined as a
+ * combination of an input (memory to IPU), output (IPU to memory), and/or
+ * secondary input IDMA channels and in some cases an Image Converter task.
+ * Some channels consist of only an input or output.
+ */
+typedef enum {
+ CHAN_NONE = -1,
+#ifdef CONFIG_MXC_IPU_V1
+ CSI_MEM = _MAKE_CHAN(1, 0xFF, 7, 0xFF), /*!< CSI raw sensor data to memory */
+
+ CSI_PRP_ENC_MEM = _MAKE_CHAN(2, 0xFF, 0, 0xFF), /*!< CSI to IC Encoder PreProcessing to Memory */
+ MEM_PRP_ENC_MEM = _MAKE_CHAN(3, 6, 0, 0xFF), /*!< Memory to IC Encoder PreProcessing to Memory */
+ MEM_ROT_ENC_MEM = _MAKE_CHAN(4, 10, 8, 0xFF), /*!< Memory to IC Encoder Rotation to Memory */
+
+ CSI_PRP_VF_MEM = _MAKE_CHAN(5, 0xFF, 1, 0xFF), /*!< CSI to IC Viewfinder PreProcessing to Memory */
+ CSI_PRP_VF_ADC = _MAKE_CHAN(6, 0xFF, 1, 0xFF), /*!< CSI to IC Viewfinder PreProcessing to ADC */
+ MEM_PRP_VF_MEM = _MAKE_CHAN(7, 6, 1, 3), /*!< Memory to IC Viewfinder PreProcessing to Memory */
+ MEM_PRP_VF_ADC = _MAKE_CHAN(8, 6, 1, 3), /*!< Memory to IC Viewfinder PreProcessing to ADC */
+ MEM_ROT_VF_MEM = _MAKE_CHAN(9, 11, 9, 0xFF), /*!< Memory to IC Viewfinder Rotation to Memory */
+
+ MEM_PP_MEM = _MAKE_CHAN(10, 5, 2, 4), /*!< Memory to IC PostProcessing to Memory */
+ MEM_ROT_PP_MEM = _MAKE_CHAN(11, 13, 12, 0xFF), /*!< Memory to IC PostProcessing Rotation to Memory */
+ MEM_PP_ADC = _MAKE_CHAN(12, 5, 2, 4), /*!< Memory to IC PostProcessing to ADC */
+
+ MEM_SDC_BG = _MAKE_CHAN(14, 14, 0xFF, 0xFF), /*!< Memory to SDC Background plane */
+ MEM_SDC_FG = _MAKE_CHAN(15, 15, 0xFF, 0xFF), /*!< Memory to SDC Foreground plane */
+ MEM_SDC_MASK = _MAKE_CHAN(16, 16, 0xFF, 0xFF), /*!< Memory to SDC Mask */
+
+ MEM_BG_SYNC = MEM_SDC_BG,
+ MEM_FG_SYNC = MEM_SDC_FG,
+
+ ADC_SYS1 = _MAKE_CHAN(17, 18, 22, 20), /*!< Memory to ADC System Channel 1 */
+ ADC_SYS2 = _MAKE_CHAN(18, 19, 23, 21), /*!< Memory to ADC System Channel 2 */
+
+ MEM_PF_Y_MEM = _MAKE_CHAN(19, 26, 29, 24), /*!< Y and PF Memory to Post-filter to Y Memory */
+ MEM_PF_U_MEM = _MAKE_CHAN(20, 27, 30, 25), /*!< U and PF Memory to Post-filter to U Memory */
+ MEM_PF_V_MEM = _MAKE_CHAN(21, 28, 31, 0xFF), /*!< V Memory to Post-filter to V Memory */
+
+ MEM_DC_SYNC = CHAN_NONE,
+ DIRECT_ASYNC0 = CHAN_NONE,
+ DIRECT_ASYNC1 = CHAN_NONE,
+ MEM_VDI_PRP_VF_MEM_P = CHAN_NONE,
+ MEM_VDI_PRP_VF_MEM = CHAN_NONE,
+ MEM_VDI_PRP_VF_MEM_N = CHAN_NONE,
+#else
+ MEM_ROT_ENC_MEM = _MAKE_CHAN(1, 45, NO_DMA, NO_DMA, 48),
+ MEM_ROT_VF_MEM = _MAKE_CHAN(2, 46, NO_DMA, NO_DMA, 49),
+ MEM_ROT_PP_MEM = _MAKE_CHAN(3, 47, NO_DMA, NO_DMA, 50),
+
+ MEM_PRP_ENC_MEM = _MAKE_CHAN(4, 12, 14, 17, 20),
+ MEM_PRP_VF_MEM = _MAKE_CHAN(5, 12, 14, 17, 21),
+ MEM_PP_MEM = _MAKE_CHAN(6, 11, 15, 18, 22),
+
+ MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA),
+ MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA),
+ MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA),
+ MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA),
+
+ MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA),
+ MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA),
+ MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0),
+ MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0),
+
+ DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
+ DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA),
+
+ CSI_MEM0 = _MAKE_CHAN(15, NO_DMA, NO_DMA, NO_DMA, 0),
+ CSI_MEM1 = _MAKE_CHAN(16, NO_DMA, NO_DMA, NO_DMA, 1),
+ CSI_MEM2 = _MAKE_CHAN(17, NO_DMA, NO_DMA, NO_DMA, 2),
+ CSI_MEM3 = _MAKE_CHAN(18, NO_DMA, NO_DMA, NO_DMA, 3),
+
+ CSI_MEM = CSI_MEM0,
+
+ CSI_PRP_ENC_MEM = _MAKE_CHAN(19, NO_DMA, NO_DMA, NO_DMA, 20),
+ CSI_PRP_VF_MEM = _MAKE_CHAN(20, NO_DMA, NO_DMA, NO_DMA, 21),
+
+ MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN(21, 8, 14, 17, 21),
+ MEM_VDI_PRP_VF_MEM = _MAKE_CHAN(22, 9, 14, 17, 21),
+ MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN(23, 10, 14, 17, 21),
+
+ MEM_PP_ADC = CHAN_NONE,
+ ADC_SYS2 = CHAN_NONE,
+#endif
+
+} ipu_channel_t;
+
+/*!
+ * Enumeration of types of buffers for a logical channel.
+ */
+typedef enum {
+ IPU_OUTPUT_BUFFER = 0, /*!< Buffer for output from IPU */
+ IPU_ALPHA_IN_BUFFER = 1, /*!< Buffer for input to IPU */
+ IPU_GRAPH_IN_BUFFER = 2, /*!< Buffer for input to IPU */
+ IPU_VIDEO_IN_BUFFER = 3, /*!< Buffer for input to IPU */
+ IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER,
+ IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER,
+} ipu_buffer_t;
+
+#define IPU_PANEL_SERIAL 1
+#define IPU_PANEL_PARALLEL 2
+
+/*!
+ * Enumeration of DI ports for ADC.
+ */
+typedef enum {
+ DISP0,
+ DISP1,
+ DISP2,
+ DISP3
+} display_port_t;
+
+/*!
+ * Enumeration of ADC channel operation mode.
+ */
+typedef enum {
+ Disable,
+ WriteTemplateNonSeq,
+ ReadTemplateNonSeq,
+ WriteTemplateUnCon,
+ ReadTemplateUnCon,
+ WriteDataWithRS,
+ WriteDataWoRS,
+ WriteCmd
+} mcu_mode_t;
+
+/*!
+ * Enumeration of ADC channel addressing mode.
+ */
+typedef enum {
+ FullWoBE,
+ FullWithBE,
+ XY
+} display_addressing_t;
+
+/*!
+ * Union of initialization parameters for a logical channel.
+ */
+typedef union {
+ struct {
+ uint32_t csi;
+ bool mipi_en;
+ uint32_t mipi_id;
+ } csi_mem;
+ struct {
+ uint32_t in_width;
+ uint32_t in_height;
+ uint32_t in_pixel_fmt;
+ uint32_t out_width;
+ uint32_t out_height;
+ uint32_t out_pixel_fmt;
+ uint32_t csi;
+ } csi_prp_enc_mem;
+ struct {
+ uint32_t in_width;
+ uint32_t in_height;
+ uint32_t in_pixel_fmt;
+ uint32_t out_width;
+ uint32_t out_height;
+ uint32_t out_pixel_fmt;
+ } mem_prp_enc_mem;
+ struct {
+ uint32_t in_width;
+ uint32_t in_height;
+ uint32_t in_pixel_fmt;
+ uint32_t out_width;
+ uint32_t out_height;
+ uint32_t out_pixel_fmt;
+ } mem_rot_enc_mem;
+ struct {
+ uint32_t in_width;
+ uint32_t in_height;
+ uint32_t in_pixel_fmt;
+ uint32_t out_width;
+ uint32_t out_height;
+ uint32_t out_pixel_fmt;
+ bool graphics_combine_en;
+ bool global_alpha_en;
+ bool key_color_en;
+ uint32_t csi;
+ } csi_prp_vf_mem;
+ struct {
+ uint32_t in_width;
+ uint32_t in_height;
+ uint32_t in_pixel_fmt;
+ uint32_t out_width;
+ uint32_t out_height;
+ uint32_t out_pixel_fmt;
+ bool graphics_combine_en;
+ bool global_alpha_en;
+ bool key_color_en;
+ display_port_t disp;
+ uint32_t out_left;
+ uint32_t out_top;
+ } csi_prp_vf_adc;
+ struct {
+ uint32_t in_width;
+ uint32_t in_height;
+ uint32_t in_pixel_fmt;
+ uint32_t out_width;
+ uint32_t out_height;
+ uint32_t out_pixel_fmt;
+ bool graphics_combine_en;
+ bool global_alpha_en;
+ bool key_color_en;
+ uint32_t in_g_pixel_fmt;
+ uint8_t alpha;
+ uint32_t key_color;
+ bool alpha_chan_en;
+ ipu_motion_sel motion_sel;
+ enum v4l2_field field_fmt;
+ } mem_prp_vf_mem;
+ struct {
+ uint32_t temp;
+ } mem_prp_vf_adc;
+ struct {
+ uint32_t temp;
+ } mem_rot_vf_mem;
+ struct {
+ uint32_t in_width;
+ uint32_t in_height;
+ uint32_t in_pixel_fmt;
+ uint32_t out_width;
+ uint32_t out_height;
+ uint32_t out_pixel_fmt;
+ bool graphics_combine_en;
+ bool global_alpha_en;
+ bool key_color_en;
+ uint32_t in_g_pixel_fmt;
+ uint8_t alpha;
+ uint32_t key_color;
+ bool alpha_chan_en;
+ uint32_t out_resize_ratio;
+ } mem_pp_mem;
+ struct {
+ uint32_t temp;
+ } mem_rot_mem;
+ struct {
+ uint32_t in_width;
+ uint32_t in_height;
+ uint32_t in_pixel_fmt;
+ uint32_t out_width;
+ uint32_t out_height;
+ uint32_t out_pixel_fmt;
+ bool graphics_combine_en;
+ bool global_alpha_en;
+ bool key_color_en;
+ display_port_t disp;
+ uint32_t out_left;
+ uint32_t out_top;
+ } mem_pp_adc;
+ struct {
+ pf_operation_t operation;
+ } mem_pf_mem;
+ struct {
+ uint32_t di;
+ bool interlaced;
+ } mem_dc_sync;
+ struct {
+ uint32_t temp;
+ } mem_sdc_fg;
+ struct {
+ uint32_t di;
+ bool interlaced;
+ uint32_t in_pixel_fmt;
+ uint32_t out_pixel_fmt;
+ bool alpha_chan_en;
+ } mem_dp_bg_sync;
+ struct {
+ uint32_t temp;
+ } mem_sdc_bg;
+ struct {
+ uint32_t di;
+ bool interlaced;
+ uint32_t in_pixel_fmt;
+ uint32_t out_pixel_fmt;
+ bool alpha_chan_en;
+ } mem_dp_fg_sync;
+ struct {
+ uint32_t di;
+ } direct_async;
+ struct {
+ display_port_t disp;
+ mcu_mode_t ch_mode;
+ uint32_t out_left;
+ uint32_t out_top;
+ } adc_sys1;
+ struct {
+ display_port_t disp;
+ mcu_mode_t ch_mode;
+ uint32_t out_left;
+ uint32_t out_top;
+ } adc_sys2;
+} ipu_channel_params_t;
+
+/*!
+ * Enumeration of IPU interrupt sources.
+ */
+enum ipu_irq_line {
+#ifdef CONFIG_MXC_IPU_V1
+ IPU_IRQ_DC_FC_1 = -1,
+
+ IPU_IRQ_PRP_ENC_OUT_EOF = 0,
+ IPU_IRQ_PRP_VF_OUT_EOF = 1,
+ IPU_IRQ_PP_OUT_EOF = 2,
+ IPU_IRQ_PRP_GRAPH_IN_EOF = 3,
+ IPU_IRQ_PP_GRAPH_IN_EOF = 4,
+ IPU_IRQ_PP_IN_EOF = 5,
+ IPU_IRQ_PRP_IN_EOF = 6,
+ IPU_IRQ_SENSOR_OUT_EOF = 7,
+ IPU_IRQ_CSI0_OUT_EOF = IPU_IRQ_SENSOR_OUT_EOF,
+ IPU_IRQ_PRP_ENC_ROT_OUT_EOF = 8,
+ IPU_IRQ_PRP_VF_ROT_OUT_EOF = 9,
+ IPU_IRQ_PRP_ENC_ROT_IN_EOF = 10,
+ IPU_IRQ_PRP_VF_ROT_IN_EOF = 11,
+ IPU_IRQ_PP_ROT_OUT_EOF = 12,
+ IPU_IRQ_PP_ROT_IN_EOF = 13,
+ IPU_IRQ_BG_SYNC_EOF = 14,
+ IPU_IRQ_SDC_BG_EOF = IPU_IRQ_BG_SYNC_EOF,
+ IPU_IRQ_FG_SYNC_EOF = 15,
+ IPU_IRQ_SDC_FG_EOF = IPU_IRQ_FG_SYNC_EOF,
+ IPU_IRQ_SDC_MASK_EOF = 16,
+ IPU_IRQ_SDC_BG_PART_EOF = 17,
+ IPU_IRQ_ADC_SYS1_WR_EOF = 18,
+ IPU_IRQ_ADC_SYS2_WR_EOF = 19,
+ IPU_IRQ_ADC_SYS1_CMD_EOF = 20,
+ IPU_IRQ_ADC_SYS2_CMD_EOF = 21,
+ IPU_IRQ_ADC_SYS1_RD_EOF = 22,
+ IPU_IRQ_ADC_SYS2_RD_EOF = 23,
+ IPU_IRQ_PF_QP_IN_EOF = 24,
+ IPU_IRQ_PF_BSP_IN_EOF = 25,
+ IPU_IRQ_PF_Y_IN_EOF = 26,
+ IPU_IRQ_PF_U_IN_EOF = 27,
+ IPU_IRQ_PF_V_IN_EOF = 28,
+ IPU_IRQ_PF_Y_OUT_EOF = 29,
+ IPU_IRQ_PF_U_OUT_EOF = 30,
+ IPU_IRQ_PF_V_OUT_EOF = 31,
+
+ IPU_IRQ_PRP_ENC_OUT_NF = 32,
+ IPU_IRQ_PRP_VF_OUT_NF = 33,
+ IPU_IRQ_PP_OUT_NF = 34,
+ IPU_IRQ_PRP_GRAPH_IN_NF = 35,
+ IPU_IRQ_PP_GRAPH_IN_NF = 36,
+ IPU_IRQ_PP_IN_NF = 37,
+ IPU_IRQ_PRP_IN_NF = 38,
+ IPU_IRQ_SENSOR_OUT_NF = 39,
+ IPU_IRQ_PRP_ENC_ROT_OUT_NF = 40,
+ IPU_IRQ_PRP_VF_ROT_OUT_NF = 41,
+ IPU_IRQ_PRP_ENC_ROT_IN_NF = 42,
+ IPU_IRQ_PRP_VF_ROT_IN_NF = 43,
+ IPU_IRQ_PP_ROT_OUT_NF = 44,
+ IPU_IRQ_PP_ROT_IN_NF = 45,
+ IPU_IRQ_SDC_FG_NF = 46,
+ IPU_IRQ_SDC_BG_NF = 47,
+ IPU_IRQ_SDC_MASK_NF = 48,
+ IPU_IRQ_SDC_BG_PART_NF = 49,
+ IPU_IRQ_ADC_SYS1_WR_NF = 50,
+ IPU_IRQ_ADC_SYS2_WR_NF = 51,
+ IPU_IRQ_ADC_SYS1_CMD_NF = 52,
+ IPU_IRQ_ADC_SYS2_CMD_NF = 53,
+ IPU_IRQ_ADC_SYS1_RD_NF = 54,
+ IPU_IRQ_ADC_SYS2_RD_NF = 55,
+ IPU_IRQ_PF_QP_IN_NF = 56,
+ IPU_IRQ_PF_BSP_IN_NF = 57,
+ IPU_IRQ_PF_Y_IN_NF = 58,
+ IPU_IRQ_PF_U_IN_NF = 59,
+ IPU_IRQ_PF_V_IN_NF = 60,
+ IPU_IRQ_PF_Y_OUT_NF = 61,
+ IPU_IRQ_PF_U_OUT_NF = 62,
+ IPU_IRQ_PF_V_OUT_NF = 63,
+
+ IPU_IRQ_BREAKRQ = 64,
+ IPU_IRQ_SDC_BG_OUT_EOF = 65,
+ IPU_IRQ_BG_SF_END = IPU_IRQ_SDC_BG_OUT_EOF,
+ IPU_IRQ_SDC_FG_OUT_EOF = 66,
+ IPU_IRQ_SDC_MASK_OUT_EOF = 67,
+ IPU_IRQ_ADC_SERIAL_DATA_OUT = 68,
+ IPU_IRQ_SENSOR_NF = 69,
+ IPU_IRQ_SENSOR_EOF = 70,
+ IPU_IRQ_SDC_DISP3_VSYNC = 80,
+ IPU_IRQ_ADC_DISP0_VSYNC = 81,
+ IPU_IRQ_ADC_DISP12_VSYNC = 82,
+ IPU_IRQ_ADC_PRP_EOF = 83,
+ IPU_IRQ_ADC_PP_EOF = 84,
+ IPU_IRQ_ADC_SYS1_EOF = 85,
+ IPU_IRQ_ADC_SYS2_EOF = 86,
+
+ IPU_IRQ_PRP_ENC_OUT_NFB4EOF_ERR = 96,
+ IPU_IRQ_PRP_VF_OUT_NFB4EOF_ERR = 97,
+ IPU_IRQ_PP_OUT_NFB4EOF_ERR = 98,
+ IPU_IRQ_PRP_GRAPH_IN_NFB4EOF_ERR = 99,
+ IPU_IRQ_PP_GRAPH_IN_NFB4EOF_ERR = 100,
+ IPU_IRQ_PP_IN_NFB4EOF_ERR = 101,
+ IPU_IRQ_PRP_IN_NFB4EOF_ERR = 102,
+ IPU_IRQ_SENSOR_OUT_NFB4EOF_ERR = 103,
+ IPU_IRQ_PRP_ENC_ROT_OUT_NFB4EOF_ERR = 104,
+ IPU_IRQ_PRP_VF_ROT_OUT_NFB4EOF_ERR = 105,
+ IPU_IRQ_PRP_ENC_ROT_IN_NFB4EOF_ERR = 106,
+ IPU_IRQ_PRP_VF_ROT_IN_NFB4EOF_ERR = 107,
+ IPU_IRQ_PP_ROT_OUT_NFB4EOF_ERR = 108,
+ IPU_IRQ_PP_ROT_IN_NFB4EOF_ERR = 109,
+ IPU_IRQ_SDC_FG_NFB4EOF_ERR = 110,
+ IPU_IRQ_SDC_BG_NFB4EOF_ERR = 111,
+ IPU_IRQ_SDC_MASK_NFB4EOF_ERR = 112,
+ IPU_IRQ_SDC_BG_PART_NFB4EOF_ERR = 113,
+ IPU_IRQ_ADC_SYS1_WR_NFB4EOF_ERR = 114,
+ IPU_IRQ_ADC_SYS2_WR_NFB4EOF_ERR = 115,
+ IPU_IRQ_ADC_SYS1_CMD_NFB4EOF_ERR = 116,
+ IPU_IRQ_ADC_SYS2_CMD_NFB4EOF_ERR = 117,
+ IPU_IRQ_ADC_SYS1_RD_NFB4EOF_ERR = 118,
+ IPU_IRQ_ADC_SYS2_RD_NFB4EOF_ERR = 119,
+ IPU_IRQ_PF_QP_IN_NFB4EOF_ERR = 120,
+ IPU_IRQ_PF_BSP_IN_NFB4EOF_ERR = 121,
+ IPU_IRQ_PF_Y_IN_NFB4EOF_ERR = 122,
+ IPU_IRQ_PF_U_IN_NFB4EOF_ERR = 123,
+ IPU_IRQ_PF_V_IN_NFB4EOF_ERR = 124,
+ IPU_IRQ_PF_Y_OUT_NFB4EOF_ERR = 125,
+ IPU_IRQ_PF_U_OUT_NFB4EOF_ERR = 126,
+ IPU_IRQ_PF_V_OUT_NFB4EOF_ERR = 127,
+
+ IPU_IRQ_BAYER_BUFOVF_ERR = 128,
+ IPU_IRQ_ENC_BUFOVF_ERR = 129,
+ IPU_IRQ_VF_BUFOVF_ERR = 130,
+ IPU_IRQ_ADC_PP_TEAR_ERR = 131,
+ IPU_IRQ_ADC_SYS1_TEAR_ERR = 132,
+ IPU_IRQ_ADC_SYS2_TEAR_ERR = 133,
+ IPU_IRQ_SDC_BGD_ERR = 134,
+ IPU_IRQ_SDC_FGD_ERR = 135,
+ IPU_IRQ_SDC_MASKD_ERR = 136,
+ IPU_IRQ_BAYER_FRM_LOST_ERR = 137,
+ IPU_IRQ_ENC_FRM_LOST_ERR = 138,
+ IPU_IRQ_VF_FRM_LOST_ERR = 139,
+ IPU_IRQ_ADC_LOCK_ERR = 140,
+ IPU_IRQ_DI_LLA_LOCK_ERR = 141,
+ IPU_IRQ_AHB_M1_ERR = 142,
+ IPU_IRQ_AHB_M12_ERR = 143,
+#else
+ IPU_IRQ_CSI0_OUT_EOF = 0,
+ IPU_IRQ_CSI1_OUT_EOF = 1,
+ IPU_IRQ_CSI2_OUT_EOF = 2,
+ IPU_IRQ_CSI3_OUT_EOF = 3,
+ IPU_IRQ_PP_IN_EOF = 11,
+ IPU_IRQ_PRP_IN_EOF = 12,
+ IPU_IRQ_PRP_GRAPH_IN_EOF = 14,
+ IPU_IRQ_PP_GRAPH_IN_EOF = 15,
+ IPU_IRQ_PRP_ALPHA_IN_EOF = 17,
+ IPU_IRQ_PP_ALPHA_IN_EOF = 18,
+ IPU_IRQ_PRP_ENC_OUT_EOF = 20,
+ IPU_IRQ_PRP_VF_OUT_EOF = 21,
+ IPU_IRQ_PP_OUT_EOF = 22,
+ IPU_IRQ_BG_SYNC_EOF = 23,
+ IPU_IRQ_BG_ASYNC_EOF = 24,
+ IPU_IRQ_FG_SYNC_EOF = 27,
+ IPU_IRQ_DC_SYNC_EOF = 28,
+ IPU_IRQ_FG_ASYNC_EOF = 29,
+ IPU_IRQ_FG_ALPHA_SYNC_EOF = 31,
+
+ IPU_IRQ_FG_ALPHA_ASYNC_EOF = 33,
+ IPU_IRQ_DC_READ_EOF = 40,
+ IPU_IRQ_DC_ASYNC_EOF = 41,
+ IPU_IRQ_DC_CMD1_EOF = 42,
+ IPU_IRQ_DC_CMD2_EOF = 43,
+ IPU_IRQ_DC_MASK_EOF = 44,
+ IPU_IRQ_PRP_ENC_ROT_IN_EOF = 45,
+ IPU_IRQ_PRP_VF_ROT_IN_EOF = 46,
+ IPU_IRQ_PP_ROT_IN_EOF = 47,
+ IPU_IRQ_PRP_ENC_ROT_OUT_EOF = 48,
+ IPU_IRQ_PRP_VF_ROT_OUT_EOF = 49,
+ IPU_IRQ_PP_ROT_OUT_EOF = 50,
+ IPU_IRQ_BG_ALPHA_SYNC_EOF = 51,
+ IPU_IRQ_BG_ALPHA_ASYNC_EOF = 52,
+
+ IPU_IRQ_DP_SF_START = 448 + 2,
+ IPU_IRQ_DP_SF_END = 448 + 3,
+ IPU_IRQ_BG_SF_END = IPU_IRQ_DP_SF_END,
+ IPU_IRQ_DC_FC_0 = 448 + 8,
+ IPU_IRQ_DC_FC_1 = 448 + 9,
+ IPU_IRQ_DC_FC_2 = 448 + 10,
+ IPU_IRQ_DC_FC_3 = 448 + 11,
+ IPU_IRQ_DC_FC_4 = 448 + 12,
+ IPU_IRQ_DC_FC_6 = 448 + 13,
+ IPU_IRQ_VSYNC_PRE_0 = 448 + 14,
+ IPU_IRQ_VSYNC_PRE_1 = 448 + 15,
+#endif
+
+ IPU_IRQ_COUNT
+};
+
+/*!
+ * Bitfield of Display Interface signal polarities.
+ */
+typedef struct {
+ unsigned datamask_en:1;
+ unsigned ext_clk:1;
+ unsigned interlaced:1;
+ unsigned odd_field_first:1;
+ unsigned clksel_en:1;
+ unsigned clkidle_en:1;
+ unsigned data_pol:1; /* true = inverted */
+ unsigned clk_pol:1; /* true = rising edge */
+ unsigned enable_pol:1;
+ unsigned Hsync_pol:1; /* true = active high */
+ unsigned Vsync_pol:1;
+} ipu_di_signal_cfg_t;
+
+/*!
+ * Bitfield of CSI signal polarities and modes.
+ */
+
+typedef struct {
+ unsigned data_width:4;
+ unsigned clk_mode:3;
+ unsigned ext_vsync:1;
+ unsigned Vsync_pol:1;
+ unsigned Hsync_pol:1;
+ unsigned pixclk_pol:1;
+ unsigned data_pol:1;
+ unsigned sens_clksrc:1;
+ unsigned pack_tight:1;
+ unsigned force_eof:1;
+ unsigned data_en_pol:1;
+ unsigned data_fmt;
+ unsigned csi;
+ unsigned mclk;
+} ipu_csi_signal_cfg_t;
+
+/*!
+ * Enumeration of CSI data bus widths.
+ */
+enum {
+ IPU_CSI_DATA_WIDTH_4,
+ IPU_CSI_DATA_WIDTH_8,
+ IPU_CSI_DATA_WIDTH_10,
+ IPU_CSI_DATA_WIDTH_16,
+};
+
+/*!
+ * Enumeration of CSI clock modes.
+ */
+enum {
+ IPU_CSI_CLK_MODE_GATED_CLK,
+ IPU_CSI_CLK_MODE_NONGATED_CLK,
+ IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE,
+ IPU_CSI_CLK_MODE_CCIR656_INTERLACED,
+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR,
+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR,
+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR,
+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR,
+};
+
+enum {
+ IPU_CSI_MIPI_DI0,
+ IPU_CSI_MIPI_DI1,
+ IPU_CSI_MIPI_DI2,
+ IPU_CSI_MIPI_DI3,
+};
+
+typedef enum {
+ RGB,
+ YCbCr,
+ YUV
+} ipu_color_space_t;
+
+/*!
+ * Enumeration of ADC vertical sync mode.
+ */
+typedef enum {
+ VsyncNone,
+ VsyncInternal,
+ VsyncCSI,
+ VsyncExternal
+} vsync_t;
+
+typedef enum {
+ DAT,
+ CMD
+} cmddata_t;
+
+/*!
+ * Enumeration of ADC display update mode.
+ */
+typedef enum {
+ IPU_ADC_REFRESH_NONE,
+ IPU_ADC_AUTO_REFRESH,
+ IPU_ADC_AUTO_REFRESH_SNOOP,
+ IPU_ADC_SNOOPING,
+} ipu_adc_update_mode_t;
+
+/*!
+ * Enumeration of ADC display interface types (serial or parallel).
+ */
+enum {
+ IPU_ADC_IFC_MODE_SYS80_TYPE1,
+ IPU_ADC_IFC_MODE_SYS80_TYPE2,
+ IPU_ADC_IFC_MODE_SYS68K_TYPE1,
+ IPU_ADC_IFC_MODE_SYS68K_TYPE2,
+ IPU_ADC_IFC_MODE_3WIRE_SERIAL,
+ IPU_ADC_IFC_MODE_4WIRE_SERIAL,
+ IPU_ADC_IFC_MODE_5WIRE_SERIAL_CLK,
+ IPU_ADC_IFC_MODE_5WIRE_SERIAL_CS,
+};
+
+enum {
+ IPU_ADC_IFC_WIDTH_8,
+ IPU_ADC_IFC_WIDTH_16,
+};
+
+/*!
+ * Enumeration of ADC display interface burst mode.
+ */
+enum {
+ IPU_ADC_BURST_WCS,
+ IPU_ADC_BURST_WBLCK,
+ IPU_ADC_BURST_NONE,
+ IPU_ADC_BURST_SERIAL,
+};
+
+/*!
+ * Enumeration of ADC display interface RW signal timing modes.
+ */
+enum {
+ IPU_ADC_SER_NO_RW,
+ IPU_ADC_SER_RW_BEFORE_RS,
+ IPU_ADC_SER_RW_AFTER_RS,
+};
+
+/*!
+ * Bitfield of ADC signal polarities and modes.
+ */
+typedef struct {
+ unsigned data_pol:1;
+ unsigned clk_pol:1;
+ unsigned cs_pol:1;
+ unsigned rs_pol:1;
+ unsigned addr_pol:1;
+ unsigned read_pol:1;
+ unsigned write_pol:1;
+ unsigned Vsync_pol:1;
+ unsigned burst_pol:1;
+ unsigned burst_mode:2;
+ unsigned ifc_mode:3;
+ unsigned ifc_width:5;
+ unsigned ser_preamble_len:4;
+ unsigned ser_preamble:8;
+ unsigned ser_rw_mode:2;
+} ipu_adc_sig_cfg_t;
+
+/*!
+ * Enumeration of ADC template commands.
+ */
+enum {
+ RD_DATA,
+ RD_ACK,
+ RD_WAIT,
+ WR_XADDR,
+ WR_YADDR,
+ WR_ADDR,
+ WR_CMND,
+ WR_DATA,
+};
+
+/*!
+ * Enumeration of ADC template command flow control.
+ */
+enum {
+ SINGLE_STEP,
+ PAUSE,
+ STOP,
+};
+
+
+/*Define template constants*/
+#define ATM_ADDR_RANGE 0x20 /*offset address of DISP */
+#define TEMPLATE_BUF_SIZE 0x20 /*size of template */
+
+/*!
+ * Define to create ADC template command entry.
+ */
+#define ipu_adc_template_gen(oc, rs, fc, dat) ( ((rs) << 29) | ((fc) << 27) | \
+ ((oc) << 24) | (dat) )
+
+typedef struct {
+ u32 reg;
+ u32 value;
+} ipu_lpmc_reg_t;
+
+#define IPU_LPMC_REG_READ 0x80000000L
+
+#define CSI_MCLK_VF 1
+#define CSI_MCLK_ENC 2
+#define CSI_MCLK_RAW 4
+#define CSI_MCLK_I2C 8
+
+/* Common IPU API */
+int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t * params);
+void ipu_uninit_channel(ipu_channel_t channel);
+
+static inline bool ipu_can_rotate_in_place(ipu_rotate_mode_t rot)
+{
+#ifdef CONFIG_MXC_IPU_V3D
+ return (rot < IPU_ROTATE_HORIZ_FLIP);
+#else
+ return (rot < IPU_ROTATE_90_RIGHT);
+#endif
+}
+
+int32_t ipu_init_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t pixel_fmt,
+ uint16_t width, uint16_t height,
+ uint32_t stride,
+ ipu_rotate_mode_t rot_mode,
+ dma_addr_t phyaddr_0, dma_addr_t phyaddr_1,
+ uint32_t u_offset, uint32_t v_offset);
+
+int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t bufNum, dma_addr_t phyaddr);
+
+int32_t ipu_update_channel_offset(ipu_channel_t channel, ipu_buffer_t type,
+ uint32_t pixel_fmt,
+ uint16_t width, uint16_t height,
+ uint32_t stride,
+ uint32_t u, uint32_t v,
+ uint32_t vertical_offset, uint32_t horizontal_offset);
+
+int32_t ipu_select_buffer(ipu_channel_t channel,
+ ipu_buffer_t type, uint32_t bufNum);
+int32_t ipu_select_multi_vdi_buffer(uint32_t bufNum);
+
+int32_t ipu_link_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch);
+int32_t ipu_unlink_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch);
+
+int32_t ipu_is_channel_busy(ipu_channel_t channel);
+int32_t ipu_enable_channel(ipu_channel_t channel);
+int32_t ipu_disable_channel(ipu_channel_t channel, bool wait_for_stop);
+int32_t ipu_swap_channel(ipu_channel_t from_ch, ipu_channel_t to_ch);
+
+int ipu_lowpwr_display_enable(void);
+int ipu_lowpwr_display_disable(void);
+
+void ipu_enable_irq(uint32_t irq);
+void ipu_disable_irq(uint32_t irq);
+void ipu_clear_irq(uint32_t irq);
+int ipu_request_irq(uint32_t irq,
+ irqreturn_t(*handler) (int, void *),
+ uint32_t irq_flags, const char *devname, void *dev_id);
+void ipu_free_irq(uint32_t irq, void *dev_id);
+bool ipu_get_irq_status(uint32_t irq);
+void ipu_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3]);
+
+/* SDC API */
+int32_t ipu_sdc_init_panel(ipu_panel_t panel,
+ uint32_t pixel_clk,
+ uint16_t width, uint16_t height,
+ uint32_t pixel_fmt,
+ uint16_t hStartWidth, uint16_t hSyncWidth,
+ uint16_t hEndWidth, uint16_t vStartWidth,
+ uint16_t vSyncWidth, uint16_t vEndWidth,
+ ipu_di_signal_cfg_t sig);
+
+int32_t ipu_sdc_set_global_alpha(bool enable, uint8_t alpha);
+int32_t ipu_sdc_set_color_key(ipu_channel_t channel, bool enable,
+ uint32_t colorKey);
+int32_t ipu_sdc_set_brightness(uint8_t value);
+
+int32_t ipu_init_sync_panel(int disp,
+ uint32_t pixel_clk,
+ uint16_t width, uint16_t height,
+ uint32_t pixel_fmt,
+ uint16_t h_start_width, uint16_t h_sync_width,
+ uint16_t h_end_width, uint16_t v_start_width,
+ uint16_t v_sync_width, uint16_t v_end_width,
+ uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig);
+
+int32_t ipu_disp_set_window_pos(ipu_channel_t channel, int16_t x_pos,
+ int16_t y_pos);
+int32_t ipu_disp_set_global_alpha(ipu_channel_t channel, bool enable,
+ uint8_t alpha);
+int32_t ipu_disp_set_color_key(ipu_channel_t channel, bool enable,
+ uint32_t colorKey);
+
+int ipu_init_async_panel(int disp, int type, uint32_t cycle_time,
+ uint32_t pixel_fmt, ipu_adc_sig_cfg_t sig);
+void ipu_disp_direct_write(ipu_channel_t channel, u32 value, u32 offset);
+void ipu_reset_disp_panel(void);
+
+/* ADC API */
+int32_t ipu_adc_write_template(display_port_t disp, uint32_t * pCmd,
+ bool write);
+
+int32_t ipu_adc_set_update_mode(ipu_channel_t channel,
+ ipu_adc_update_mode_t mode,
+ uint32_t refresh_rate, unsigned long addr,
+ uint32_t * size);
+
+int32_t ipu_adc_get_snooping_status(uint32_t * statl, uint32_t * stath);
+
+int32_t ipu_adc_write_cmd(display_port_t disp, cmddata_t type,
+ uint32_t cmd, const uint32_t * params,
+ uint16_t numParams);
+
+int32_t ipu_adc_init_panel(display_port_t disp,
+ uint16_t width, uint16_t height,
+ uint32_t pixel_fmt,
+ uint32_t stride,
+ ipu_adc_sig_cfg_t sig,
+ display_addressing_t addr,
+ uint32_t vsync_width, vsync_t mode);
+
+int32_t ipu_adc_init_ifc_timing(display_port_t disp, bool read,
+ uint32_t cycle_time,
+ uint32_t up_time,
+ uint32_t down_time,
+ uint32_t read_latch_time, uint32_t pixel_clk);
+
+/* CMOS Sensor Interface API */
+int32_t ipu_csi_init_interface(uint16_t width, uint16_t height,
+ uint32_t pixel_fmt, ipu_csi_signal_cfg_t sig);
+
+int32_t ipu_csi_enable_mclk(int src, bool flag, bool wait);
+
+static inline int32_t ipu_csi_enable_mclk_if(int src, uint32_t csi,
+ bool flag, bool wait)
+{
+#ifdef CONFIG_MXC_IPU_V1
+ return ipu_csi_enable_mclk(src, flag, wait);
+#else
+ return ipu_csi_enable_mclk(csi, flag, wait);
+#endif
+}
+
+int ipu_csi_read_mclk_flag(void);
+
+void ipu_csi_flash_strobe(bool flag);
+
+void ipu_csi_get_window_size(uint32_t *width, uint32_t *height, uint32_t csi);
+
+void ipu_csi_set_window_size(uint32_t width, uint32_t height, uint32_t csi);
+
+void ipu_csi_set_window_pos(uint32_t left, uint32_t top, uint32_t csi);
+
+/* Post Filter functions */
+int32_t ipu_pf_set_pause_row(uint32_t pause_row);
+
+uint32_t bytes_per_pixel(uint32_t fmt);
+
+/* New added for IPU-lib functionality*/
+int ipu_open(void);
+int ipu_register_generic_isr(int irq, void *dev);
+void ipu_close(void);
+
+typedef struct _ipu_channel_parm {
+ ipu_channel_t channel;
+ ipu_channel_params_t params;
+ bool flag;
+} ipu_channel_parm;
+
+typedef struct _ipu_channel_buf_parm {
+ ipu_channel_t channel;
+ ipu_buffer_t type;
+ uint32_t pixel_fmt;
+ uint16_t width;
+ uint16_t height;
+ uint16_t stride;
+ ipu_rotate_mode_t rot_mode;
+ dma_addr_t phyaddr_0;
+ dma_addr_t phyaddr_1;
+ uint32_t u_offset;
+ uint32_t v_offset;
+ uint32_t bufNum;
+} ipu_channel_buf_parm;
+
+typedef struct _ipu_channel_link {
+ ipu_channel_t src_ch;
+ ipu_channel_t dest_ch;
+} ipu_channel_link;
+
+typedef struct _ipu_channel_info {
+ ipu_channel_t channel;
+ bool stop;
+} ipu_channel_info;
+
+typedef struct ipu_irq_info {
+ uint32_t irq;
+ irqreturn_t(*handler) (int, void *);
+ uint32_t irq_flags;
+ char *devname;
+ void *dev_id;
+} ipu_irq_info;
+
+typedef struct _ipu_sdc_panel_info {
+ ipu_panel_t panel;
+ uint32_t pixel_clk;
+ uint16_t width;
+ uint16_t height;
+ uint32_t pixel_fmt;
+ uint16_t hStartWidth;
+ uint16_t hSyncWidth;
+ uint16_t hEndWidth;
+ uint16_t vStartWidth;
+ uint16_t vSyncWidth;
+ uint16_t vEndWidth;
+ ipu_di_signal_cfg_t signal;
+} ipu_sdc_panel_info;
+
+typedef struct _ipu_sdc_window_pos {
+ ipu_channel_t channel;
+ int16_t x_pos;
+ int16_t y_pos;
+} ipu_sdc_window_pos;
+
+typedef struct _ipu_sdc_global_alpha {
+ bool enable;
+ uint8_t alpha;
+} ipu_sdc_global_alpha;
+
+typedef struct _ipu_sdc_color_key {
+ ipu_channel_t channel;
+ bool enable;
+ uint32_t colorKey;
+} ipu_sdc_color_key;
+
+typedef struct _ipu_adc_template {
+ display_port_t disp;
+ uint32_t *pCmd;
+ bool write;
+} ipu_adc_template;
+
+typedef struct _ipu_adc_update {
+ ipu_channel_t channel;
+ ipu_adc_update_mode_t mode;
+ uint32_t refresh_rate;
+ unsigned long addr;
+ uint32_t *size;
+} ipu_adc_update;
+
+typedef struct _ipu_adc_snoop {
+ uint32_t *statl;
+ uint32_t *stath;
+} ipu_adc_snoop;
+
+typedef struct _ipu_adc_cmd {
+ display_port_t disp;
+ cmddata_t type;
+ uint32_t cmd;
+ uint32_t *params;
+ uint16_t numParams;
+} ipu_adc_cmd;
+
+typedef struct _ipu_adc_panel {
+ display_port_t disp;
+ uint16_t width;
+ uint16_t height;
+ uint32_t pixel_fmt;
+ uint32_t stride;
+ ipu_adc_sig_cfg_t signal;
+ display_addressing_t addr;
+ uint32_t vsync_width;
+ vsync_t mode;
+} ipu_adc_panel;
+
+typedef struct _ipu_adc_ifc_timing {
+ display_port_t disp;
+ bool read;
+ uint32_t cycle_time;
+ uint32_t up_time;
+ uint32_t down_time;
+ uint32_t read_latch_time;
+ uint32_t pixel_clk;
+} ipu_adc_ifc_timing;
+
+typedef struct _ipu_csi_interface {
+ uint16_t width;
+ uint16_t height;
+ uint16_t pixel_fmt;
+ ipu_csi_signal_cfg_t signal;
+} ipu_csi_interface;
+
+typedef struct _ipu_csi_mclk {
+ int src;
+ bool flag;
+ bool wait;
+} ipu_csi_mclk;
+
+typedef struct _ipu_csi_window {
+ uint32_t left;
+ uint32_t top;
+} ipu_csi_window;
+
+typedef struct _ipu_csi_window_size {
+ uint32_t width;
+ uint32_t height;
+} ipu_csi_window_size;
+
+typedef struct _ipu_event_info {
+ int irq;
+ void *dev;
+} ipu_event_info;
+
+typedef struct _ipu_mem_info {
+ dma_addr_t paddr;
+ void *vaddr;
+ int size;
+} ipu_mem_info;
+
+/* IOCTL commands */
+
+#define IPU_INIT_CHANNEL _IOW('I',0x1,ipu_channel_parm)
+#define IPU_UNINIT_CHANNEL _IOW('I',0x2,ipu_channel_t)
+#define IPU_INIT_CHANNEL_BUFFER _IOW('I',0x3,ipu_channel_buf_parm)
+#define IPU_UPDATE_CHANNEL_BUFFER _IOW('I',0x4,ipu_channel_buf_parm)
+#define IPU_SELECT_CHANNEL_BUFFER _IOW('I',0x5,ipu_channel_buf_parm)
+#define IPU_LINK_CHANNELS _IOW('I',0x6,ipu_channel_link)
+#define IPU_UNLINK_CHANNELS _IOW('I',0x7,ipu_channel_link)
+#define IPU_ENABLE_CHANNEL _IOW('I',0x8,ipu_channel_t)
+#define IPU_DISABLE_CHANNEL _IOW('I',0x9,ipu_channel_info)
+#define IPU_ENABLE_IRQ _IOW('I',0xA,int)
+#define IPU_DISABLE_IRQ _IOW('I',0xB,int)
+#define IPU_CLEAR_IRQ _IOW('I',0xC,int)
+#define IPU_FREE_IRQ _IOW('I',0xD,ipu_irq_info)
+#define IPU_REQUEST_IRQ_STATUS _IOW('I',0xE,int)
+#define IPU_SDC_INIT_PANEL _IOW('I',0xF,ipu_sdc_panel_info)
+#define IPU_SDC_SET_WIN_POS _IOW('I',0x10,ipu_sdc_window_pos)
+#define IPU_SDC_SET_GLOBAL_ALPHA _IOW('I',0x11,ipu_sdc_global_alpha)
+#define IPU_SDC_SET_COLOR_KEY _IOW('I',0x12,ipu_sdc_color_key)
+#define IPU_SDC_SET_BRIGHTNESS _IOW('I',0x13,int)
+#define IPU_ADC_WRITE_TEMPLATE _IOW('I',0x14,ipu_adc_template)
+#define IPU_ADC_UPDATE _IOW('I',0x15,ipu_adc_update)
+#define IPU_ADC_SNOOP _IOW('I',0x16,ipu_adc_snoop)
+#define IPU_ADC_CMD _IOW('I',0x17,ipu_adc_cmd)
+#define IPU_ADC_INIT_PANEL _IOW('I',0x18,ipu_adc_panel)
+#define IPU_ADC_IFC_TIMING _IOW('I',0x19,ipu_adc_ifc_timing)
+#define IPU_CSI_INIT_INTERFACE _IOW('I',0x1A,ipu_csi_interface)
+#define IPU_CSI_ENABLE_MCLK _IOW('I',0x1B,ipu_csi_mclk)
+#define IPU_CSI_READ_MCLK_FLAG _IOR('I',0x1C,ipu_csi_mclk)
+#define IPU_CSI_FLASH_STROBE _IOW('I',0x1D,ipu_csi_mclk)
+#define IPU_CSI_GET_WIN_SIZE _IOR('I',0x1E,ipu_csi_window_size)
+#define IPU_CSI_SET_WIN_SIZE _IOW('I',0x1F,ipu_csi_window_size)
+#define IPU_CSI_SET_WINDOW _IOW('I',0x20,ipu_csi_window)
+#define IPU_PF_SET_PAUSE_ROW _IOW('I',0x21, uint32_t)
+#define IPU_REGISTER_GENERIC_ISR _IOW('I', 0x22, ipu_event_info)
+#define IPU_GET_EVENT _IOWR('I', 0x23, ipu_event_info)
+#define IPU_ALOC_MEM _IOWR('I', 0x24, ipu_mem_info)
+#define IPU_FREE_MEM _IOW('I', 0x25, ipu_mem_info)
+#define IPU_IS_CHAN_BUSY _IOW('I', 0x26, ipu_channel_t)
+
+
+/* two stripe calculations */
+struct stripe_param{
+ unsigned int input_width; /* width of the input stripe */
+ unsigned int output_width; /* width of the output stripe */
+ unsigned int input_column; /* the first column on the input stripe */
+ unsigned int output_column; /* the first column on the output stripe */
+ unsigned int idr;
+ /* inverse downisizing ratio parameter; expressed as a power of 2 */
+ unsigned int irr;
+ /* inverse resizing ratio parameter; expressed as a multiple of 2^-13 */
+};
+
+
+
+
+int ipu_calc_stripes_sizes(const unsigned int input_frame_width,
+ unsigned int output_frame_width,
+ const unsigned int maximal_stripe_width,
+ const unsigned long long cirr,
+ const unsigned int equal_stripes,
+ u32 input_pixelformat,
+ u32 output_pixelformat,
+ struct stripe_param *left,
+ struct stripe_param *right);
+
+#endif
diff --git a/include/linux/major.h b/include/linux/major.h
index 88249452b935..9c9d170d191f 100644
--- a/include/linux/major.h
+++ b/include/linux/major.h
@@ -170,6 +170,8 @@
#define VIOTAPE_MAJOR 230
+#define LBA_MAJOR 240
+
#define BLOCK_EXT_MAJOR 259
#endif
diff --git a/include/linux/mfd/mc13783/core.h b/include/linux/mfd/mc13783/core.h
new file mode 100644
index 000000000000..0b01d3e1241a
--- /dev/null
+++ b/include/linux/mfd/mc13783/core.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __LINUX_MFD_MC13783_CORE_H_
+#define __LINUX_MFD_MC13783_CORE_H_
+
+#include <linux/kernel.h>
+
+/*!
+ * brief PMIC regulators.
+ */
+#define MC13783_SW1A 0
+#define MC13783_SW1B 1
+#define MC13783_SW2A 2
+#define MC13783_SW2B 3
+#define MC13783_SW3 4
+#define MC13783_VAUDIO 5
+#define MC13783_VIOHI 6
+#define MC13783_VIOLO 7
+#define MC13783_VDIG 8
+#define MC13783_VGEN 9
+#define MC13783_VRFDIG 10
+#define MC13783_VRFREF 11
+#define MC13783_VRFCP 12
+#define MC13783_VSIM 13
+#define MC13783_VESIM 14
+#define MC13783_VCAM 15
+#define MC13783_VRFBG 16
+#define MC13783_VVIB 17
+#define MC13783_VRF1 18
+#define MC13783_VRF2 19
+#define MC13783_VMMC1 20
+#define MC13783_VMMC2 21
+#define MC13783_GPO1 22
+#define MC13783_GPO2 23
+#define MC13783_GPO3 24
+#define MC13783_GPO4 25
+#define MC13783_REG_NUM 26
+
+struct mc13783;
+struct regulator_init_data;
+
+struct mc13783_pmic {
+ /* regulator devices */
+ struct platform_device *pdev[MC13783_REG_NUM];
+};
+
+struct mc13783 {
+ int rev; /* chip revision */
+
+ struct device *dev;
+
+ u16 *reg_cache;
+
+ /* Client devices */
+ struct mc13783_pmic pmic;
+};
+
+int mc13783_register_regulator(struct mc13783 *mc13783, int reg,
+ struct regulator_init_data *initdata);
+
+#endif
diff --git a/include/linux/mfd/mc13892/core.h b/include/linux/mfd/mc13892/core.h
new file mode 100644
index 000000000000..6e4ef85f0231
--- /dev/null
+++ b/include/linux/mfd/mc13892/core.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __LINUX_MFD_MC13892_CORE_H_
+#define __LINUX_MFD_MC13892_CORE_H_
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#define MC13892_SW1 0
+#define MC13892_SW2 1
+#define MC13892_SW3 2
+#define MC13892_SW4 3
+#define MC13892_SWBST 4
+#define MC13892_VIOHI 5
+#define MC13892_VPLL 6
+#define MC13892_VDIG 7
+#define MC13892_VSD 8
+#define MC13892_VUSB2 9
+#define MC13892_VVIDEO 10
+#define MC13892_VAUDIO 11
+#define MC13892_VCAM 12
+#define MC13892_VGEN1 13
+#define MC13892_VGEN2 14
+#define MC13892_VGEN3 15
+#define MC13892_VUSB 16
+#define MC13892_GPO1 17
+#define MC13892_GPO2 18
+#define MC13892_GPO3 19
+#define MC13892_GPO4 20
+#define MC13892_PWGT1 21
+#define MC13892_PWGT2 22
+#define MC13892_REG_NUM 23
+
+struct mc13892;
+struct regulator_init_data;
+
+struct mc13892_platform_data {
+ int (*init)(struct mc13892 *);
+};
+
+struct mc13892_pmic {
+ /* regulator devices */
+ struct platform_device *pdev[MC13892_REG_NUM];
+};
+
+struct mc13892 {
+ int rev; /* chip revision */
+
+ struct device *dev;
+
+ /* device IO */
+ union {
+ struct i2c_client *i2c_client;
+ struct spi_device *spi_device;
+ };
+ u16 *reg_cache;
+
+ /* Client devices */
+ struct mc13892_pmic pmic;
+};
+
+int mc13892_register_regulator(struct mc13892 *mc13892, int reg,
+ struct regulator_init_data *initdata);
+
+#endif
diff --git a/include/linux/mfd/mc34704/core.h b/include/linux/mfd/mc34704/core.h
new file mode 100644
index 000000000000..e352e2b6c3d8
--- /dev/null
+++ b/include/linux/mfd/mc34704/core.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __LINUX_MFD_MC34704_CORE_H_
+#define __LINUX_MFD_MC34704_CORE_H_
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#define MC34704_BKLT 0 /* REG1 5V for Backlight */
+#define MC34704_CPU 1 /* REG2 3.3V for CPU & CPU board peripherals */
+#define MC34704_CORE 2 /* REG3 1.45V for CPU Core */
+#define MC34704_DDR 3 /* REG4 1.8V for DDR */
+#define MC34704_PERS 4 /* REG5 3.3V for Personality board */
+#define MC34704_REG6 5 /* REG6 not used */
+#define MC34704_REG7 6 /* REG7 not used */
+#define MC34704_REG8 7 /* REG8 not used */
+#define MC34704_REG_NUM 8
+
+/* Regulator voltages in mV and dynamic voltage scaling limits in percent */
+
+#define REG1_V_MV 5000
+#define REG1_DVS_MIN_PCT (-10)
+#define REG1_DVS_MAX_PCT 10
+
+#define REG2_V_MV 3300
+#define REG2_DVS_MIN_PCT (-20)
+#define REG2_DVS_MAX_PCT 17.5
+
+#define REG3_V_MV 1450
+#define REG3_DVS_MIN_PCT (-20)
+#define REG3_DVS_MAX_PCT 17.5
+
+#define REG4_V_MV 1800
+#define REG4_DVS_MIN_PCT (-20)
+#define REG4_DVS_MAX_PCT 17.5
+
+#define REG5_V_MV 3300
+#define REG5_DVS_MIN_PCT (-20)
+#define REG5_DVS_MAX_PCT 17.5
+
+struct mc34704;
+struct regulator_init_data;
+
+struct mc34704_platform_data {
+ int (*init) (struct mc34704 *);
+};
+
+struct mc34704_pmic {
+ /* regulator devices */
+ struct platform_device *pdev[MC34704_REG_NUM];
+};
+
+struct mc34704 {
+ int rev; /* chip revision */
+
+ struct device *dev;
+
+ /* device IO */
+ union {
+ struct i2c_client *i2c_client;
+ struct spi_device *spi_device;
+ };
+ u16 *reg_cache;
+
+ /* Client devices */
+ struct mc34704_pmic pmic;
+};
+
+int mc34704_register_regulator(struct mc34704 *mc34704, int reg,
+ struct regulator_init_data *initdata);
+
+#endif
diff --git a/include/linux/mfd/mc9s08dz60/core.h b/include/linux/mfd/mc9s08dz60/core.h
new file mode 100644
index 000000000000..ecc1806c0f90
--- /dev/null
+++ b/include/linux/mfd/mc9s08dz60/core.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __LINUX_MFD_MC13892_CORE_H_
+#define __LINUX_MFD_MC13892_CORE_H_
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#define MC9S08DZ60_LCD 0
+#define MC9S08DZ60_WIFI 1
+#define MC9S08DZ60_HDD 2
+#define MC9S08DZ60_GPS 3
+#define MC9S08DZ60_SPKR 4
+#define MC9S08DZ60_REG_NUM 5
+
+struct mc9s08dz60;
+struct regulator_init_data;
+
+struct mc9s08dz60_platform_data {
+ int (*init)(struct mc9s08dz60 *);
+};
+
+struct mc9s08dz60_pmic {
+ /* regulator devices */
+ struct platform_device *pdev[MC9S08DZ60_REG_NUM];
+};
+
+struct mc9s08dz60 {
+ int rev; /* chip revision */
+
+ struct device *dev;
+
+ /* device IO */
+ union {
+ struct i2c_client *i2c_client;
+ struct spi_device *spi_device;
+ };
+ u16 *reg_cache;
+
+ /* Client devices */
+ struct mc9s08dz60_pmic pmic;
+};
+
+int mc9s08dz60_register_regulator(struct mc9s08dz60 *mc9s08dz60, int reg,
+ struct regulator_init_data *initdata);
+
+#endif
diff --git a/include/linux/mfd/mc9s08dz60/pmic.h b/include/linux/mfd/mc9s08dz60/pmic.h
new file mode 100644
index 000000000000..c1218ad7ebc0
--- /dev/null
+++ b/include/linux/mfd/mc9s08dz60/pmic.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __LINUX_MCU_PMIC_H_
+#define __LINUX_MCU_PMIC_H_
+
+enum {
+
+ /*reg names for mcu */
+ REG_MCU_VERSION = 0,
+ REG_MCU_SECS,
+ REG_MCU_MINS,
+ REG_MCU_HRS,
+ REG_MCU_DAY,
+ REG_MCU_DATE,
+ REG_MCU_MONTH,
+ REG_MCU_YEAR,
+ REG_MCU_ALARM_SECS,
+ REG_MCU_ALARM_MINS,
+ REG_MCU_ALARM_HRS,
+ REG_MCU_TS_CONTROL,
+ REG_MCU_X_LOW,
+ REG_MCU_Y_LOW,
+ REG_MCU_XY_HIGH,
+ REG_MCU_X_LEFT_LOW,
+ REG_MCU_X_LEFT_HIGH,
+ REG_MCU_X_RIGHT,
+ REG_MCU_Y_TOP_LOW,
+ REG_MCU_Y_TOP_HIGH,
+ REG_MCU_Y_BOTTOM,
+ REG_MCU_RESET_1,
+ REG_MCU_RESET_2,
+ REG_MCU_POWER_CTL,
+ REG_MCU_DELAY_CONFIG,
+ REG_MCU_GPIO_1,
+ REG_MCU_GPIO_2,
+ REG_MCU_KPD_1,
+ REG_MCU_KPD_2,
+ REG_MCU_KPD_CONTROL,
+ REG_MCU_INT_ENABLE_1,
+ REG_MCU_INT_ENABLE_2,
+ REG_MCU_INT_FLAG_1,
+ REG_MCU_INT_FLAG_2,
+ REG_MCU_DES_FLAG,
+};
+
+enum {
+
+ MCU_GPIO_REG_RESET_1,
+ MCU_GPIO_REG_RESET_2,
+ MCU_GPIO_REG_POWER_CONTROL,
+ MCU_GPIO_REG_GPIO_CONTROL_1,
+ MCU_GPIO_REG_GPIO_CONTROL_2,
+};
+
+int pmic_gpio_set_bit_val(int reg, unsigned int bit,
+ unsigned int val);
+
+int pmic_gpio_get_bit_val(int reg, unsigned int bit,
+ unsigned int *val);
+
+int pmic_gpio_get_designation_bit_val(unsigned int bit,
+ unsigned int *val);
+
+
+int mcu_pmic_read_reg(int reg, unsigned int *reg_value,
+ unsigned int reg_mask);
+
+int mcu_pmic_write_reg(int reg, unsigned int reg_value,
+ unsigned int reg_mask);
+#endif
diff --git a/include/linux/mfd/wm8350/audio.h b/include/linux/mfd/wm8350/audio.h
index 217bb22ebb8e..7489d55e1e92 100644
--- a/include/linux/mfd/wm8350/audio.h
+++ b/include/linux/mfd/wm8350/audio.h
@@ -1,7 +1,7 @@
/*
* audio.h -- Audio Driver for Wolfson WM8350 PMIC
*
- * Copyright 2007 Wolfson Microelectronics PLC
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC
*
* 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
@@ -70,9 +70,9 @@
#define WM8350_CODEC_ISEL_0_5 3 /* x0.5 */
#define WM8350_VMID_OFF 0
-#define WM8350_VMID_500K 1
-#define WM8350_VMID_100K 2
-#define WM8350_VMID_10K 3
+#define WM8350_VMID_300K 1
+#define WM8350_VMID_50K 2
+#define WM8350_VMID_5K 3
/*
* R40 (0x28) - Clock Control 1
@@ -591,8 +591,37 @@
#define WM8350_IRQ_CODEC_MICSCD 41
#define WM8350_IRQ_CODEC_MICD 42
+/*
+ * WM8350 Platform data.
+ *
+ * This must be initialised per platform for best audio performance.
+ * Please see WM8350 datasheet for information.
+ */
+struct wm8350_audio_platform_data {
+ int vmid_discharge_msecs; /* VMID --> OFF discharge time */
+ int drain_msecs; /* OFF drain time */
+ int cap_discharge_msecs; /* Cap ON (from OFF) discharge time */
+ int vmid_charge_msecs; /* vmid power up time */
+ char *regulator1;
+ char *regulator2;
+ u32 vmid_s_curve:2; /* vmid enable s curve speed */
+ u32 dis_out4:2; /* out4 discharge speed */
+ u32 dis_out3:2; /* out3 discharge speed */
+ u32 dis_out2:2; /* out2 discharge speed */
+ u32 dis_out1:2; /* out1 discharge speed */
+ u32 vroi_out4:1; /* out4 tie off */
+ u32 vroi_out3:1; /* out3 tie off */
+ u32 vroi_out2:1; /* out2 tie off */
+ u32 vroi_out1:1; /* out1 tie off */
+ u32 vroi_enable:1; /* enable tie off */
+ u32 codec_current_on:2; /* current level ON */
+ u32 codec_current_standby:2; /* current level STANDBY */
+ u32 codec_current_charge:2; /* codec current @ vmid charge */
+};
+
struct wm8350_codec {
struct platform_device *pdev;
+ struct wm8350_audio_platform_data *platform_data;
};
#endif
diff --git a/include/linux/mfd/wm8350/bl.h b/include/linux/mfd/wm8350/bl.h
new file mode 100644
index 000000000000..ded2d22db737
--- /dev/null
+++ b/include/linux/mfd/wm8350/bl.h
@@ -0,0 +1,32 @@
+/*
+ * bl.h -- Backlight Driver for Wolfson WM8350 PMIC
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC
+ *
+ * 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 __LINUX_MFD_WM8350_BL_H_
+#define __LINUX_MFD_WM8350_BL_H_
+
+#include <linux/fb.h>
+
+/*
+ * WM8350 Backlight platform data
+ */
+struct wm8350_bl_platform_data {
+ int isink;
+ int dcdc;
+ int voltage_ramp;
+ int retries;
+ int max_brightness;
+ int power;
+ int brightness;
+ int (*check_fb)(struct fb_info *);
+};
+
+#endif
diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h
index 6ebf97f2a475..9490ec175d5a 100644
--- a/include/linux/mfd/wm8350/core.h
+++ b/include/linux/mfd/wm8350/core.h
@@ -536,6 +536,7 @@
#define WM8350_REV_E 0x4
#define WM8350_REV_F 0x5
#define WM8350_REV_G 0x6
+#define WM8350_REV_H 0x7
#define WM8350_NUM_IRQ 63
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index 26433ec520b3..279c0bfd2c4c 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -12,6 +12,7 @@
#define APOLLO_MOUSE_MINOR 7
#define PC110PAD_MINOR 9
/*#define ADB_MOUSE_MINOR 10 FIXME OBSOLETE */
+#define CRYPTODEV_MINOR 70 /* /dev/crypto */
#define WATCHDOG_MINOR 130 /* Watchdog timer */
#define TEMP_MINOR 131 /* Temperature Sensor */
#define RTC_MINOR 135
diff --git a/include/linux/mma7455l.h b/include/linux/mma7455l.h
new file mode 100644
index 000000000000..12ab50affd84
--- /dev/null
+++ b/include/linux/mma7455l.h
@@ -0,0 +1,11 @@
+#ifndef _LINUX_MMA7455L_H
+#define _LINUX_MMA7455L_H
+
+struct mma7455l_platform_data {
+ /* Calibration offsets */
+ s16 calibration_x;
+ s16 calibration_y;
+ s16 calibration_z;
+};
+
+#endif /* _LINUX_MMA7455L_H */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 143cebf0586f..5e3d87b913fe 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -54,6 +54,8 @@ struct mmc_command {
#define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
+#define MMC_KEEP_CLK_RUN (1 << 31) /* Keep card clock on after request */
+
/*
* These are the SPI response types for MMC, SD, and SDIO cards.
* Commands return R1, with maybe more info. Zero is an error type;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f842f234e44f..4e457256bd33 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -41,6 +41,7 @@ struct mmc_ios {
#define MMC_BUS_WIDTH_1 0
#define MMC_BUS_WIDTH_4 2
+#define MMC_BUS_WIDTH_8 3
unsigned char timing; /* timing specification used */
@@ -116,6 +117,7 @@ struct mmc_host {
#define MMC_CAP_SDIO_IRQ (1 << 3) /* Can signal pending SDIO IRQs */
#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
+#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
diff --git a/include/linux/mtd/ccx9x_nand.h b/include/linux/mtd/ccx9x_nand.h
new file mode 100644
index 000000000000..ac6b32848955
--- /dev/null
+++ b/include/linux/mtd/ccx9x_nand.h
@@ -0,0 +1,11 @@
+#ifndef __CCX9X_NAND_H__
+#define __CCX9X_NAND_H__
+
+struct ccx9x_nand_info {
+ unsigned int addr_offset;
+ unsigned int cmd_offset;
+ unsigned int delay;
+ unsigned int busy_pin;
+};
+
+#endif
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 733d3f3b4eb8..0e6a4d5c1318 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -43,8 +43,8 @@ extern void nand_wait_ready(struct mtd_info *mtd);
* is supported now. If you add a chip with bigger oobsize/page
* adjust this accordingly.
*/
-#define NAND_MAX_OOBSIZE 64
-#define NAND_MAX_PAGESIZE 2048
+#define NAND_MAX_OOBSIZE (218 * NAND_MAX_CHIPS)
+#define NAND_MAX_PAGESIZE (4096 * NAND_MAX_CHIPS)
/*
* Constants for hardware specific CLE/ALE/NCE function
@@ -551,7 +551,10 @@ extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
int allowbbt);
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, uint8_t * buf);
-
+extern int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops);
+extern int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops);
/*
* Constants for oob configuration
*/
diff --git a/include/linux/mxc_asrc.h b/include/linux/mxc_asrc.h
new file mode 100644
index 000000000000..17e97bef003b
--- /dev/null
+++ b/include/linux/mxc_asrc.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mx35_asrc.h
+ *
+ * @brief MX35 Asynchronous Sample Rate Converter
+ *
+ * @ingroup ??
+ */
+
+#ifndef __MXC_ASRC_H__
+#define __MXC_ASRC_H__
+
+#define ASRC_IOC_MAGIC 'C'
+
+#define ASRC_REQ_PAIR _IOWR(ASRC_IOC_MAGIC, 0, struct asrc_req)
+#define ASRC_CONFIG_PAIR _IOWR(ASRC_IOC_MAGIC, 1, struct asrc_config)
+#define ASRC_RELEASE_PAIR _IOW(ASRC_IOC_MAGIC, 2, enum asrc_pair_index)
+#define ASRC_QUERYBUF _IOWR(ASRC_IOC_MAGIC, 3, struct asrc_buffer)
+#define ASRC_Q_INBUF _IOW(ASRC_IOC_MAGIC, 4, struct asrc_buffer)
+#define ASRC_DQ_INBUF _IOW(ASRC_IOC_MAGIC, 5, struct asrc_buffer)
+#define ASRC_Q_OUTBUF _IOW(ASRC_IOC_MAGIC, 6, struct asrc_buffer)
+#define ASRC_DQ_OUTBUF _IOW(ASRC_IOC_MAGIC, 7, struct asrc_buffer)
+#define ASRC_START_CONV _IOW(ASRC_IOC_MAGIC, 8, enum asrc_pair_index)
+#define ASRC_STOP_CONV _IOW(ASRC_IOC_MAGIC, 9, enum asrc_pair_index)
+#define ASRC_STATUS _IOW(ASRC_IOC_MAGIC, 10, struct asrc_status_flags)
+#define ASRC_FLUSH _IOW(ASRC_IOC_MAGIC, 11, enum asrc_pair_index)
+
+enum asrc_pair_index {
+ ASRC_PAIR_A,
+ ASRC_PAIR_B,
+ ASRC_PAIR_C
+};
+
+enum asrc_inclk {
+ INCLK_NONE = 0x03,
+ INCLK_ESAI_RX = 0x00,
+ INCLK_SSI1_RX = 0x01,
+ INCLK_SSI2_RX = 0x02,
+ INCLK_SPDIF_RX = 0x04,
+ INCLK_MLB_CLK = 0x05,
+ INCLK_ESAI_TX = 0x08,
+ INCLK_SSI1_TX = 0x09,
+ INCLK_SSI2_TX = 0x0a,
+ INCLK_SPDIF_TX = 0x0c,
+ INCLK_ASRCK1_CLK = 0x0f,
+};
+
+enum asrc_outclk {
+ OUTCLK_NONE = 0x03,
+ OUTCLK_ESAI_TX = 0x00,
+ OUTCLK_SSI1_TX = 0x01,
+ OUTCLK_SSI2_TX = 0x02,
+ OUTCLK_SPDIF_TX = 0x04,
+ OUTCLK_MLB_CLK = 0x05,
+ OUTCLK_ESAI_RX = 0x08,
+ OUTCLK_SSI1_RX = 0x09,
+ OUTCLK_SSI2_RX = 0x0a,
+ OUTCLK_SPDIF_RX = 0x0c,
+ OUTCLK_ASRCK1_CLK = 0x0f,
+};
+
+struct asrc_config {
+ enum asrc_pair_index pair;
+ unsigned int channel_num;
+ unsigned int buffer_num;
+ unsigned int dma_buffer_size;
+ unsigned int input_sample_rate;
+ unsigned int output_sample_rate;
+ unsigned int word_width;
+ enum asrc_inclk inclk;
+ enum asrc_outclk outclk;
+};
+
+struct asrc_pair {
+ unsigned int start_channel;
+ unsigned int chn_num;
+ unsigned int chn_max;
+ unsigned int active;
+ unsigned int overload_error;
+};
+
+struct asrc_req {
+ unsigned int chn_num;
+ enum asrc_pair_index index;
+};
+
+struct asrc_querybuf {
+ unsigned int buffer_index;
+ unsigned int input_length;
+ unsigned int output_length;
+ unsigned long input_offset;
+ unsigned long output_offset;
+};
+
+struct asrc_buffer {
+ unsigned int index;
+ unsigned int length;
+ int buf_valid;
+};
+
+struct asrc_status_flags {
+ enum asrc_pair_index index;
+ unsigned int overload_error;
+};
+
+#define ASRC_BUF_NA -35 /* ASRC DQ's buffer is NOT available */
+#define ASRC_BUF_AV 35 /* ASRC DQ's buffer is available */
+enum asrc_error_status {
+ ASRC_TASK_Q_OVERLOAD = 0x01,
+ ASRC_OUTPUT_TASK_OVERLOAD = 0x02,
+ ASRC_INPUT_TASK_OVERLOAD = 0x04,
+ ASRC_OUTPUT_BUFFER_OVERFLOW = 0x08,
+ ASRC_INPUT_BUFFER_UNDERRUN = 0x10,
+};
+
+#ifdef __KERNEL__
+
+#define ASRC_DMA_BUFFER_NUM 8
+
+#define ASRC_ASRCTR_REG 0x00
+#define ASRC_ASRIER_REG 0x04
+#define ASRC_ASRCNCR_REG 0x0C
+#define ASRC_ASRCFG_REG 0x10
+#define ASRC_ASRCSR_REG 0x14
+#define ASRC_ASRCDR1_REG 0x18
+#define ASRC_ASRCDR2_REG 0x1C
+#define ASRC_ASRSTR_REG 0x20
+#define ASRC_ASRRA_REG 0x24
+#define ASRC_ASRRB_REG 0x28
+#define ASRC_ASRRC_REG 0x2C
+#define ASRC_ASRPM1_REG 0x40
+#define ASRC_ASRPM2_REG 0x44
+#define ASRC_ASRPM3_REG 0x48
+#define ASRC_ASRPM4_REG 0x4C
+#define ASRC_ASRPM5_REG 0x50
+#define ASRC_ASRTFR1 0x54
+#define ASRC_ASRCCR_REG 0x5C
+#define ASRC_ASRDIA_REG 0x60
+#define ASRC_ASRDOA_REG 0x64
+#define ASRC_ASRDIB_REG 0x68
+#define ASRC_ASRDOB_REG 0x6C
+#define ASRC_ASRDIC_REG 0x70
+#define ASRC_ASRDOC_REG 0x74
+#define ASRC_ASRIDRHA_REG 0x80
+#define ASRC_ASRIDRLA_REG 0x84
+#define ASRC_ASRIDRHB_REG 0x88
+#define ASRC_ASRIDRLB_REG 0x8C
+#define ASRC_ASRIDRHC_REG 0x90
+#define ASRC_ASRIDRLC_REG 0x94
+#define ASRC_ASR76K_REG 0x98
+#define ASRC_ASR56K_REG 0x9C
+
+struct dma_block {
+ unsigned int index;
+ unsigned int length;
+ unsigned char *dma_vaddr;
+ dma_addr_t dma_paddr;
+ struct list_head queue;
+};
+
+struct asrc_pair_params {
+ enum asrc_pair_index index;
+ struct list_head input_queue;
+ struct list_head input_done_queue;
+ struct list_head output_queue;
+ struct list_head output_done_queue;
+ wait_queue_head_t input_wait_queue;
+ wait_queue_head_t output_wait_queue;
+ unsigned int input_counter;
+ unsigned int output_counter;
+ unsigned int input_queue_empty;
+ unsigned int output_queue_empty;
+ unsigned int input_dma_channel;
+ unsigned int output_dma_channel;
+ unsigned int input_buffer_size;
+ unsigned int output_buffer_size;
+ unsigned int buffer_num;
+ unsigned int pair_hold;
+ unsigned int asrc_active;
+ struct dma_block input_dma[ASRC_DMA_BUFFER_NUM];
+ struct dma_block output_dma[ASRC_DMA_BUFFER_NUM];
+ struct semaphore busy_lock;
+};
+
+struct asrc_data {
+ struct asrc_pair asrc_pair[3];
+};
+
+extern int asrc_req_pair(int chn_num, enum asrc_pair_index *index);
+extern void asrc_release_pair(enum asrc_pair_index index);
+extern int asrc_config_pair(struct asrc_config *config);
+extern void asrc_get_status(struct asrc_status_flags *flags);
+extern void asrc_start_conv(enum asrc_pair_index index);
+extern void asrc_stop_conv(enum asrc_pair_index index);
+
+#endif /* __kERNEL__ */
+
+#endif /* __MXC_ASRC_H__ */
diff --git a/include/linux/mxc_mlb.h b/include/linux/mxc_mlb.h
new file mode 100644
index 000000000000..b38ad8c16702
--- /dev/null
+++ b/include/linux/mxc_mlb.h
@@ -0,0 +1,51 @@
+/*
+ * mxc_mlb.h
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 _MXC_MLB_H
+#define _MXC_MLB_H
+
+/* define IOCTL command */
+#define MLB_SET_FPS _IOW('S', 0x10, unsigned int)
+#define MLB_GET_VER _IOR('S', 0x11, unsigned long)
+#define MLB_SET_DEVADDR _IOR('S', 0x12, unsigned char)
+/*!
+ * set channel address for each logical channel
+ * the MSB 16bits is for tx channel, the left LSB is for rx channel
+ */
+#define MLB_CHAN_SETADDR _IOW('S', 0x13, unsigned int)
+#define MLB_CHAN_STARTUP _IO('S', 0x14)
+#define MLB_CHAN_SHUTDOWN _IO('S', 0x15)
+#define MLB_CHAN_GETEVENT _IOR('S', 0x16, unsigned long)
+
+/*!
+ * MLB event define
+ */
+enum {
+ MLB_EVT_TX_PROTO_ERR_CUR = 1 << 0,
+ MLB_EVT_TX_BRK_DETECT_CUR = 1 << 1,
+ MLB_EVT_TX_PROTO_ERR_PREV = 1 << 8,
+ MLB_EVT_TX_BRK_DETECT_PREV = 1 << 9,
+ MLB_EVT_RX_PROTO_ERR_CUR = 1 << 16,
+ MLB_EVT_RX_BRK_DETECT_CUR = 1 << 17,
+ MLB_EVT_RX_PROTO_ERR_PREV = 1 << 24,
+ MLB_EVT_RX_BRK_DETECT_PREV = 1 << 25,
+};
+
+#ifdef __KERNEL__
+extern void gpio_mlb_active(void);
+extern void gpio_mlb_inactive(void);
+#endif
+
+#endif /* _MXC_MLB_H */
diff --git a/include/linux/mxc_pf.h b/include/linux/mxc_pf.h
new file mode 100644
index 000000000000..6d38642eef12
--- /dev/null
+++ b/include/linux/mxc_pf.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+/*!
+ * @defgroup MXC_PF MPEG4/H.264 Post Filter Driver
+ */
+/*!
+ * @file arch-mxc/mxc_pf.h
+ *
+ * @brief MXC IPU MPEG4/H.264 Post-filtering driver
+ *
+ * User-level API for IPU Hardware MPEG4/H.264 Post-filtering.
+ *
+ * @ingroup MXC_PF
+ */
+#ifndef __INCLUDED_MXC_PF_H__
+#define __INCLUDED_MXC_PF_H__
+
+#define PF_MAX_BUFFER_CNT 17
+
+#define PF_WAIT_Y 0x0001
+#define PF_WAIT_U 0x0002
+#define PF_WAIT_V 0x0004
+#define PF_WAIT_ALL (PF_WAIT_Y|PF_WAIT_U|PF_WAIT_V)
+
+/*!
+ * Structure for Post Filter initialization parameters.
+ */
+typedef struct {
+ uint16_t pf_mode; /*!< Post filter operation mode */
+ uint16_t width; /*!< Width of frame in pixels */
+ uint16_t height; /*!< Height of frame in pixels */
+ uint16_t stride; /*!< Stride of Y plane in pixels. Stride for U and V planes is half Y stride */
+ uint32_t qp_size;
+ unsigned long qp_paddr;
+} pf_init_params;
+
+/*!
+ * Structure for Post Filter buffer request parameters.
+ */
+typedef struct {
+ int count; /*!< Number of buffers requested */
+ __u32 req_size;
+} pf_reqbufs_params;
+
+/*!
+ * Structure for Post Filter buffer request parameters.
+ */
+typedef struct {
+ int index;
+ int size; /*!< Size of buffer allocated */
+ __u32 offset; /*!< Buffer offset in driver memory. Set by QUERYBUF */
+ __u32 y_offset; /*!< Optional starting relative offset of Y data
+ from beginning of buffer. Set to 0 to use default
+ calculated based on height and stride */
+ __u32 u_offset; /*!< Optional starting relative offset of U data
+ from beginning of buffer. Set to 0 to use default
+ calculated based on height and stride */
+ __u32 v_offset; /*!< Optional starting relative offset of V data
+ from beginning of buffer. Set to 0 to use default
+ calculated based on height and stride */
+} pf_buf;
+
+/*!
+ * Structure for Post Filter start parameters.
+ */
+typedef struct {
+ pf_buf in; /*!< Input buffer address and offsets */
+ pf_buf out; /*!< Output buffer address and offsets */
+ int qp_buf;
+ int wait;
+ uint32_t h264_pause_row; /*!< Row to pause at for H.264 mode. 0 to disable pause */
+} pf_start_params;
+
+/*! @name User Client Ioctl Interface */
+/*! @{ */
+
+/*!
+ * IOCTL to Initialize the Post Filter.
+ */
+#define PF_IOCTL_INIT _IOW('F',0x0, pf_init_params)
+
+/*!
+ * IOCTL to Uninitialize the Post Filter.
+ */
+#define PF_IOCTL_UNINIT _IO('F',0x1)
+
+/*!
+ * IOCTL to set the buffer mode and allocate buffers if driver allocated.
+ */
+#define PF_IOCTL_REQBUFS _IOWR('F',0x2, pf_reqbufs_params)
+
+/*!
+ * IOCTL to set the buffer mode and allocate buffers if driver allocated.
+ */
+#define PF_IOCTL_QUERYBUF _IOR('F',0x2, pf_buf)
+
+/*!
+ * IOCTL to start post filtering on a frame of data. This ioctl may block until
+ * processing is done or return immediately.
+ */
+#define PF_IOCTL_START _IOWR('F',0x3, pf_start_params)
+
+/*!
+ * IOCTL to resume post-filtering after an intra frame pause in H.264 mode.
+ */
+#define PF_IOCTL_RESUME _IOW('F',0x4, int)
+
+/*!
+ * IOCTL to wait for post-filtering to complete.
+ */
+#define PF_IOCTL_WAIT _IOW('F',0x5, int)
+/*! @} */
+
+#endif /* __INCLUDED_MXC_PF_H__ */
diff --git a/include/linux/mxc_scc2_driver.h b/include/linux/mxc_scc2_driver.h
new file mode 100644
index 000000000000..5f84545bc0d0
--- /dev/null
+++ b/include/linux/mxc_scc2_driver.h
@@ -0,0 +1,975 @@
+
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 SCC_DRIVER_H
+#define SCC_DRIVER_H
+
+/*
+ * NAMING CONVENTIONS
+ * ==================
+ * (A note to maintainers and other interested parties)
+ *
+ * Use scc_ or SCC_ prefix for 'high-level' interface routines and the types
+ * passed to those routines. Try to avoid #defines in these interfaces.
+ *
+ * Use SMN_ or SCM_ prefix for the #defines used with scc_read_register() and
+ * scc_write_register, or values passed/retrieved from those routines.
+ */
+
+/*! @file mxc_scc2_driver.h
+ *
+ * @brief (Header file to use the SCC2 driver.)
+ *
+ * The SCC2 driver is available to other kernel modules directly. Secure
+ * Partition functionality is extended to users through the SHW API. Other
+ * functionality of the SCC2 is limited to kernel-space users.
+ *
+ * With the exception of #scc_monitor_security_failure(), all routines are
+ * 'synchronous', i.e. they will not return to their caller until the requested
+ * action is complete, or fails to complete. Some of these functions could
+ * take quite a while to perform, depending upon the request.
+ *
+ * Routines are provided to:
+ * @li trigger a security-violation alarm - #scc_set_sw_alarm()
+ * @li get configuration and version information - #scc_get_configuration()
+ * @li zeroize memory - #scc_zeroize_memories()
+ * @li Work with secure partitions: #scc_allocate_partition()
+ * #scc_engage_partition() #scc_diminish_permissions()
+ * #scc_release_partition()
+ * @li Encrypt or decrypt regions of data: #scc_encrypt_region()
+ * #scc_decrypt_region()
+ * @li monitor the Security Failure alarm - #scc_monitor_security_failure()
+ * @li stop monitoring Security Failure alarm -
+ * #scc_stop_monitoring_security_failure()
+ * @li write registers of the SCC - #scc_write_register()
+ * @li read registers of the SCC - #scc_read_register()
+ *
+ * The SCC2 encrypts and decrypts using Triple DES with an internally stored
+ * key. When the SCC2 is in Secure mode, it uses its secret, unique-per-chip
+ * key. When it is in Non-Secure mode, it uses a default key. This ensures
+ * that secrets stay secret if the SCC2 is not in Secure mode.
+ *
+ * Not all functions that could be provided in a 'high level' manner have been
+ * implemented. Among the missing are interfaces to the ASC/AIC components and
+ * the timer functions. These and other features must be accessed through
+ * #scc_read_register() and #scc_write_register(), using the @c \#define values
+ * provided.
+ *
+ * Here is a glossary of acronyms used in the SCC2 driver documentation:
+ * - CBC - Cipher Block Chaining. A method of performing a block cipher.
+ * Each block is encrypted using some part of the result of the previous
+ * block's encryption. It needs an 'initialization vector' to seed the
+ * operation.
+ * - ECB - Electronic Code Book. A method of performing a block cipher.
+ * With a given key, a given block will always encrypt to the same value.
+ * - DES - Data Encryption Standard. (8-byte) Block cipher algorithm which
+ * uses 56-bit keys. In SCC2, this key is constant and unique to the device.
+ * SCC uses the "triple DES" form of this algorithm.
+ * - AIC - Algorithm Integrity Checker.
+ * - ASC - Algorithm Sequence Checker.
+ * - SMN - Security Monitor. The part of the SCC2 responsible for monitoring
+ * for security problems and notifying the CPU and other PISA components.
+ * - SCM - Secure Memory. The part of the SCC2 which handles the cryptography.
+ * - SCC - Security Controller. Central security mechanism for PISA.
+ * - PISA - Platform-Independent Security Architecture.
+ */
+
+/* Temporarily define compile-time flags to make Doxygen happy. */
+#ifdef DOXYGEN_HACK
+/** @defgroup scccompileflags SCC Driver compile-time flags
+ *
+ * These preprocessor flags should be set, if desired, in a makefile so
+ * that they show up on the compiler command line.
+ */
+/** @addtogroup scccompileflags */
+
+/** @{ */
+/**
+ * Compile-time flag to change @ref smnregs and @ref scmregs
+ * offset values for the SCC's implementation on the MX.21 board.
+ *
+ * This must also be set properly for any code which calls the
+ * scc_read_register() or scc_write_register() functions or references the
+ * register offsets.
+ */
+#define TAHITI
+/** @} */
+#undef TAHITI
+
+#endif /* DOXYGEN_HACK */
+
+/*! Major Version of the driver. Used for
+ scc_configuration->driver_major_version */
+#define SCC_DRIVER_MAJOR_VERSION 2
+/*! Old Minor Version of the driver. */
+#define SCC_DRIVER_MINOR_VERSION_0 0
+/*! Minor Version of the driver. Used for
+ scc_configuration->driver_minor_version */
+#define SCC_DRIVER_MINOR_VERSION_2 2
+
+
+/*!
+ * Interrupt line number of SCM interrupt.
+ */
+#define INT_SCC_SCM MXC_INT_SCC_SCM
+
+/*!
+ * Interrupt line number of the SMN interrupt.
+ */
+#define INT_SCC_SMN MXC_INT_SCC_SMN
+
+/**
+ * @typedef scc_return_t
+ */
+/** Common status return values from SCC driver functions. */
+ typedef enum scc_return_t {
+ SCC_RET_OK = 0, /**< Function succeeded */
+ SCC_RET_FAIL, /**< Non-specific failure */
+ SCC_RET_VERIFICATION_FAILED,
+ /**< Decrypt validation failed */
+ SCC_RET_TOO_MANY_FUNCTIONS,
+ /**< At maximum registered functions */
+ SCC_RET_BUSY, /**< SCC is busy and cannot handle request */
+ /**< Encryption or decryption failed because@c count_out_bytes
+ says that @c data_out is too small to hold the value. */
+ SCC_RET_INSUFFICIENT_SPACE,
+ } scc_return_t;
+
+/**
+ * @typedef scc_partition_status_t
+ */
+/** Partition status information. */
+ typedef enum scc_partition_status_t {
+ SCC_PART_S_UNUSABLE,
+ /**< Partition not implemented */
+ SCC_PART_S_UNAVAILABLE,
+ /**< Partition owned by other host */
+ SCC_PART_S_AVAILABLE,
+ /**< Partition available */
+ SCC_PART_S_ALLOCATED,
+ /**< Partition owned by host but not engaged*/
+ SCC_PART_S_ENGAGED,
+ /**< Partition owned by host and engaged */
+ } scc_partition_status_t;
+
+/**
+ * Configuration information about SCC and the driver.
+ *
+ * This struct/typedef contains information from the SCC and the driver to
+ * allow the user of the driver to determine the size of the SCC's memories and
+ * the version of the SCC and the driver.
+ */
+ typedef struct scc_config_t {
+ int driver_major_version;
+ /**< Major version of the SCC driver code */
+ int driver_minor_version;
+ /**< Minor version of the SCC driver code */
+ int scm_version; /**< Version from SCM Configuration register */
+ int smn_version; /**< Version from SMN Status register */
+ /**< Number of bytes per block of RAM; also
+ block size of the crypto algorithm. */
+ int block_size_bytes;
+ int partition_size_bytes;
+ /**< Number of bytes in each partition */
+ int partition_count;
+ /**< Number of partitions on this platform */
+ } scc_config_t;
+
+/**
+ * @typedef scc_enc_dec_t
+ */
+/**
+ * Determine whether SCC will run its cryptographic
+ * function as an encryption or decryption.
+ */
+ typedef enum scc_enc_dec_t {
+ SCC_ENCRYPT, /**< Encrypt (from Red to Black) */
+ SCC_DECRYPT /**< Decrypt (from Black to Red) */
+ } scc_enc_dec_t;
+
+/**
+ * @typedef scc_verify_t
+ */
+/**
+ * Tell the driver whether it is responsible for verifying the integrity of a
+ * secret. During an encryption, using other than #SCC_VERIFY_MODE_NONE will
+ * cause a check value to be generated and appended to the plaintext before
+ * encryption. During decryption, the check value will be verified after
+ * decryption, and then stripped from the message.
+ */
+ typedef enum scc_verify_t {
+ /** No verification value added or checked. Input plaintext data must be
+ * be a multiple of the blocksize (#scc_get_configuration()). */
+ SCC_VERIFY_MODE_NONE,
+ /** Driver will generate/validate a 2-byte CCITT CRC. Input plaintext
+ will be padded to a multiple of the blocksize, adding 3-10 bytes
+ to the resulting output ciphertext. Upon decryption, this padding
+ will be stripped, and the CRC will be verified. */
+ SCC_VERIFY_MODE_CCITT_CRC
+ } scc_verify_t;
+
+/**
+ * @typedef scc_cypher_mode_t
+ */
+/**
+ * Select the cypher mode to use for partition cover/uncover operations.
+ */
+
+ typedef enum scc_cypher_mode_t {
+ SCC_CYPHER_MODE_ECB = 1,
+ /**< ECB mode */
+ SCC_CYPHER_MODE_CBC = 2,
+ /**< CBC mode */
+ } scc_cypher_mode_t;
+
+/**
+ * Allocate a partition of secure memory
+ *
+ * @param smid_value Value to use for the SMID register. Must be 0 for
+ * kernel mode ownership.
+ * @param[out] part_no (If successful) Assigned partition number.
+ * @param[out] part_base Kernel virtual address of the partition.
+ * @param[out] part_phys Physical address of the partition.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+ extern scc_return_t
+ scc_allocate_partition(uint32_t smid_value,
+ int *part_no,
+ void **part_base, uint32_t *part_phys);
+
+/* Note: This function has to be run in the same context (userspace or kernel
+ * mode) as the process that will be using the partition. Because the SCC2 API
+ * is not accessible in user mode, this function is also provided as a macro in
+ * in fsl_shw.h. Kernel-mode users that include this file are able to use this
+ * version of the function without having to include the whole SHW API. If the
+ * macro definition was defined before we got here, un-define it so this
+ * version will be used instead.
+ */
+
+#ifdef scc_engage_partition
+#undef scc_engage_partition
+#endif
+
+/**
+ * Engage partition of secure memory
+ *
+ * @param part_base (kernel) Virtual
+ * @param UMID NULL, or 16-byte UMID for partition security
+ * @param permissions ORed values of the type SCM_PERM_* which will be used as
+ * initial partition permissions. SHW API users should use
+ * the FSL_PERM_* definitions instead.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+ extern scc_return_t
+ scc_engage_partition(void *part_base,
+ const uint8_t *UMID, uint32_t permissions);
+
+/**
+ * Release a partition of secure memory
+ *
+ * @param part_base Kernel virtual address of the partition to be released.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+ extern scc_return_t scc_release_partition(void *part_base);
+
+/**
+ * Diminish the permissions on a partition of secure memory
+ *
+ * @param part_base Kernel virtual address of the partition.
+ *
+ * @param permissions ORed values of the type SCM_PERM_* which will be used as
+ * initial partition permissions. SHW API users should use
+ * the FSL_PERM_* definitions instead.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+ extern scc_return_t
+ scc_diminish_permissions(void *part_base, uint32_t permissions);
+
+/**
+ * Query the status of a partition of secure memory
+ *
+ * @param part_base Kernel virtual address of the partition.
+ *
+ * @return SCC_RET_OK if successful.
+ */
+ extern scc_partition_status_t scc_partition_status(void *part_base);
+
+/**
+ * Calculate the physical address from the kernel virtual address.
+ */
+ extern uint32_t scc_virt_to_phys(void *address);
+/*scc_return_t
+scc_verify_slot_access(uint64_t owner_id, uint32_t slot, uint32_t access_len);*/
+
+
+/**
+ * Encrypt a region of secure memory.
+ *
+ * @param part_base Kernel virtual address of the partition.
+ * @param offset_bytes Offset from the start of the partition to the plaintext
+ * data.
+ * @param byte_count Length of the region (octets).
+ * @param black_data Physical location to store the encrypted data.
+ * @param IV Value to use for the Initialization Vector.
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #scc_cypher_mode_t
+ *
+ * @return SCC_RET_OK if successful.
+ */
+ extern scc_return_t
+ scc_encrypt_region(uint32_t part_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t *black_data,
+ uint32_t *IV, scc_cypher_mode_t cypher_mode);
+
+/**
+ * Decrypt a region into secure memory
+ *
+ * @param part_base Kernel virtual address of the partition.
+ * @param offset_bytes Offset from the start of the partition to store the
+ * plaintext data.
+ * @param byte_count Length of the region (octets).
+ * @param black_data Physical location of the encrypted data.
+ * @param IV Value to use for the Initialization Vector.
+ * @param cypher_mode Cyphering mode to use, specified by type
+ * #scc_cypher_mode_t
+ *
+ * @return SCC_RET_OK if successful.
+ */
+ extern scc_return_t
+ scc_decrypt_region(uint32_t part_base, uint32_t offset_bytes,
+ uint32_t byte_count, uint8_t *black_data,
+ uint32_t *IV, scc_cypher_mode_t cypher_mode);
+
+/**
+ * Retrieve configuration information from the SCC.
+ *
+ * This function always succeeds.
+ *
+ * @return A pointer to the configuration information. This is a pointer to
+ * static memory and must not be freed. The values never change, and
+ * the return value will never be null.
+ */
+ extern scc_config_t *scc_get_configuration(void);
+
+/**
+ * Zeroize Red and Black memories of the SCC. This will start the Zeroizing
+ * process. The routine will return when the memories have zeroized or failed
+ * to do so. The driver will poll waiting for this to occur, so this
+ * routine must not be called from interrupt level. Some future version of
+ * driver may elect instead to sleep.
+ *
+ * @return 0 or error if initialization fails.
+ */
+ extern scc_return_t scc_zeroize_memories(void);
+
+/**
+ * Signal a software alarm to the SCC. This will take the SCC and other PISA
+ * parts out of Secure mode and into Security Failure mode. The SCC will stay
+ * in failed mode until a reboot.
+ *
+ * @internal
+ * If the SCC is not already in fail state, simply write the
+ * #SMN_COMMAND_SET_SOFTWARE_ALARM bit in #SMN_COMMAND_REG. Since there is no
+ * reason to wait for the interrupt to bounce back, simply act as though
+ * one did.
+ */
+ extern void scc_set_sw_alarm(void);
+
+/**
+ * This routine will register a function to be called should a Security Failure
+ * be signalled by the SCC (Security Monitor).
+ *
+ * The callback function may be called from interrupt level, it may be called
+ * from some process' task. It should therefore not take a long time to
+ * perform its operation, and it may not sleep.
+ *
+ * @param callback_func Function pointer to routine which will receive
+ * notification of the security failure.
+ * @return 0 if function was successfully registered, non-zero on
+ * failure. See #scc_return_t.
+ *
+ * @internal
+ * There is a fixed global static array which keeps track of the requests to
+ * monitor the failure.
+ *
+ * Add @c callback_func to the first empty slot in #scc_callbacks[]. If there
+ * is no room, return #SCC_RET_TOO_MANY_FUNCTIONS.
+ */
+ extern scc_return_t scc_monitor_security_failure(void
+ callback_func(void));
+
+/**
+ * This routine will deregister a function previously registered with
+ * #scc_monitor_security_failure().
+ *
+ * @param callback_func Function pointer to routine previously registered with
+ * #scc_stop_monitoring_security_failure().
+ */
+ extern void scc_stop_monitoring_security_failure(void
+ callback_func(void));
+
+/**
+ * Read value from an SCC register.
+ * The offset will be checked for validity (range) as well as whether it is
+ * accessible (e.g. not busy, not in failed state) at the time of the call.
+ *
+ * @param[in] register_offset The (byte) offset within the SCC block
+ * of the register to be queried. See
+ * @ref scmregs and @ref smnregs.
+ * @param[out] value Pointer to where value from the register
+ * should be placed.
+ * @return 0 if OK, non-zero on error. See #scc_return_t.
+ *
+ * @internal
+ * Verify that the register_offset is a) valid, b) refers to a readable
+ * register, and c) the SCC is in a state which would allow a read of this
+ * register.
+ */
+ extern scc_return_t scc_read_register(int register_offset,
+ uint32_t * value);
+
+/**
+ * Write a new value into an SCC register.
+ * The offset will be checked for validity (range) as well as whether it is
+ * accessible (e.g. not busy, not in failed state) at the time of the call.
+ *
+ * @param[in] register_offset The (byte) offset within the SCC block
+ * of the register to be modified. See
+ * @ref scmregs and @ref smnregs
+ * @param[in] value The value to store into the register.
+ * @return 0 if OK, non-zero on error. See #scc_return_t.
+ *
+ * @internal
+ * Verify that the register_offset is a) valid, b) refers to a writeable
+ * register, and c) the SCC is in a state which would allow a write to this
+ * register.
+ */
+ extern scc_return_t scc_write_register(int register_offset,
+ uint32_t value);
+
+/**
+ * @defgroup scmregs SCM Registers
+ *
+ * These values are offsets into the SCC for the Secure Memory
+ * (SCM) registers. They are used in the @c register_offset parameter of
+ * #scc_read_register() and #scc_write_register().
+ */
+/** @addtogroup scmregs */
+/** @{ */
+/** Offset of SCM Version ID Register */
+#define SCM_VERSION_REG 0x000
+/** Offset of SCM Interrupt Control Register */
+#define SCM_INT_CTL_REG 0x008
+/** Offset of SCM Status Register */
+#define SCM_STATUS_REG 0x00c
+/** Offset of SCM Error Status Register */
+#define SCM_ERR_STATUS_REG 0x010
+/** Offset of SCM Fault Address Register */
+#define SCM_FAULT_ADR_REG 0x014
+/** Offset of SCM Partition Owners Register */
+#define SCM_PART_OWNERS_REG 0x018
+/** Offset of SCM Partitions Engaged Register */
+#define SCM_PART_ENGAGED_REG 0x01c
+/** Offset of SCM Unique Number 0 Register */
+#define SCM_UNIQUE_ID0_REG 0x020
+/** Offset of SCM Unique Number 1 Register */
+#define SCM_UNIQUE_ID1_REG 0x024
+/** Offset of SCM Unique Number 2 Register */
+#define SCM_UNIQUE_ID2_REG 0x028
+/** Offset of SCM Unique Number 3 Register */
+#define SCM_UNIQUE_ID3_REG 0x02c
+/** Offset of SCM Zeroize Command Register */
+#define SCM_ZCMD_REG 0x050
+/** Offset of SCM Cipher Command Register */
+#define SCM_CCMD_REG 0x054
+/** Offset of SCM Cipher Black RAM Start Address Register */
+#define SCM_C_BLACK_ST_REG 0x058
+/** Offset of SCM Internal Debug Register */
+#define SCM_DBG_STATUS_REG 0x05c
+/** Offset of SCM Cipher IV 0 Register */
+#define SCM_AES_CBC_IV0_REG 0x060
+/** Offset of SCM Cipher IV 1 Register */
+#define SCM_AES_CBC_IV1_REG 0x064
+/** Offset of SCM Cipher IV 2 Register */
+#define SCM_AES_CBC_IV2_REG 0x068
+/** Offset of SCM Cipher IV 3 Register */
+#define SCM_AES_CBC_IV3_REG 0x06c
+/** Offset of SCM SMID Partition 0 Register */
+#define SCM_SMID0_REG 0x080
+/** Offset of SCM Partition 0 Access Permissions Register */
+#define SCM_ACC0_REG 0x084
+/** Offset of SCM SMID Partition 1 Register */
+#define SCM_SMID1_REG 0x088
+/** Offset of SCM Partition 1 Access Permissions Register */
+#define SCM_ACC1_REG 0x08c
+/** Offset of SCM SMID Partition 2 Register */
+#define SCM_SMID2_REG 0x090
+/** Offset of SCM Partition 2 Access Permissions Register */
+#define SCM_ACC2_REG 0x094
+/** Offset of SCM SMID Partition 3 Register */
+#define SCM_SMID3_REG 0x098
+/** Offset of SCM Partition 3 Access Permissions Register */
+#define SCM_ACC3_REG 0x09c
+/** Offset of SCM SMID Partition 4 Register */
+#define SCM_SMID4_REG 0x0a0
+/** Offset of SCM Partition 4 Access Permissions Register */
+#define SCM_ACC4_REG 0x0a4
+/** Offset of SCM SMID Partition 5 Register */
+#define SCM_SMID5_REG 0x0a8
+/** Offset of SCM Partition 5 Access Permissions Register */
+#define SCM_ACC5_REG 0x0ac
+/** Offset of SCM SMID Partition 6 Register */
+#define SCM_SMID6_REG 0x0b0
+/** Offset of SCM Partition 6 Access Permissions Register */
+#define SCM_ACC6_REG 0x0b4
+/** Offset of SCM SMID Partition 7 Register */
+#define SCM_SMID7_REG 0x0b8
+/** Offset of SCM Partition 7 Access Permissions Register */
+#define SCM_ACC7_REG 0x0bc
+/** Offset of SCM SMID Partition 8 Register */
+#define SCM_SMID8_REG 0x0c0
+/** Offset of SCM Partition 8 Access Permissions Register */
+#define SCM_ACC8_REG 0x0c4
+/** Offset of SCM SMID Partition 9 Register */
+#define SCM_SMID9_REG 0x0c8
+/** Offset of SCM Partition 9 Access Permissions Register */
+#define SCM_ACC9_REG 0x0cc
+/** Offset of SCM SMID Partition 10 Register */
+#define SCM_SMID10_REG 0x0d0
+/** Offset of SCM Partition 10 Access Permissions Register */
+#define SCM_ACC10_REG 0x0d4
+/** Offset of SCM SMID Partition 11 Register */
+#define SCM_SMID11_REG 0x0d8
+/** Offset of SCM Partition 11 Access Permissions Register */
+#define SCM_ACC11_REG 0x0dc
+/** Offset of SCM SMID Partition 12 Register */
+#define SCM_SMID12_REG 0x0e0
+/** Offset of SCM Partition 12 Access Permissions Register */
+#define SCM_ACC12_REG 0x0e4
+/** Offset of SCM SMID Partition 13 Register */
+#define SCM_SMID13_REG 0x0e8
+/** Offset of SCM Partition 13 Access Permissions Register */
+#define SCM_ACC13_REG 0x0ec
+/** Offset of SCM SMID Partition 14 Register */
+#define SCM_SMID14_REG 0x0f0
+/** Offset of SCM Partition 14 Access Permissions Register */
+#define SCM_ACC14_REG 0x0f4
+/** Offset of SCM SMID Partition 15 Register */
+#define SCM_SMID15_REG 0x0f8
+/** Offset of SCM Partition 15 Access Permissions Register */
+#define SCM_ACC15_REG 0x0fc
+/** @} */
+
+/** Number of bytes of register space for the SCM. */
+#define SCM_REG_BANK_SIZE 0x100
+
+/** Number of bytes of register space for the SCM. */
+#define SCM_REG_BANK_SIZE 0x100
+
+/** Offset of the SMN registers */
+#define SMN_ADDR_OFFSET 0x100
+
+/**
+ * @defgroup smnregs SMN Registers
+ *
+ * These values are offsets into the SCC for the Security Monitor
+ * (SMN) registers. They are used in the @c register_offset parameter of the
+ * #scc_read_register() and #scc_write_register().
+ */
+/** @addtogroup smnregs */
+/** @{ */
+/** Offset of SMN Status Register */
+#define SMN_STATUS_REG (SMN_ADDR_OFFSET+0x00000000)
+/** Offset of SMH Command Register */
+#define SMN_COMMAND_REG (SMN_ADDR_OFFSET+0x00000004)
+/** Offset of SMH Sequence Start Register */
+#define SMN_SEQ_START_REG (SMN_ADDR_OFFSET+0x00000008)
+/** Offset of SMH Sequence End Register */
+#define SMN_SEQ_END_REG (SMN_ADDR_OFFSET+0x0000000c)
+/** Offset of SMH Sequence Check Register */
+#define SMN_SEQ_CHECK_REG (SMN_ADDR_OFFSET+0x00000010)
+/** Offset of SMH BitBank Count Register */
+#define SMN_BB_CNT_REG (SMN_ADDR_OFFSET+0x00000014)
+/** Offset of SMH BitBank Increment Register */
+#define SMN_BB_INC_REG (SMN_ADDR_OFFSET+0x00000018)
+/** Offset of SMH BitBank Decrement Register */
+#define SMN_BB_DEC_REG (SMN_ADDR_OFFSET+0x0000001c)
+/** Offset of SMH Compare Register */
+#define SMN_COMPARE_REG (SMN_ADDR_OFFSET+0x00000020)
+/** Offset of SMH Plaintext Check Register */
+#define SMN_PT_CHK_REG (SMN_ADDR_OFFSET+0x00000024)
+/** Offset of SMH Ciphertext Check Register */
+#define SMN_CT_CHK_REG (SMN_ADDR_OFFSET+0x00000028)
+/** Offset of SMH Timer Initial Value Register */
+#define SMN_TIMER_IV_REG (SMN_ADDR_OFFSET+0x0000002c)
+/** Offset of SMH Timer Control Register */
+#define SMN_TIMER_CTL_REG (SMN_ADDR_OFFSET+0x00000030)
+/** Offset of SMH Security Violation Register */
+#define SMN_SEC_VIO_REG (SMN_ADDR_OFFSET+0x00000034)
+/** Offset of SMH Timer Register */
+#define SMN_TIMER_REG (SMN_ADDR_OFFSET+0x00000038)
+/** Offset of SMH High-Assurance Control Register */
+#define SMN_HAC_REG (SMN_ADDR_OFFSET+0x0000003c)
+/** Number of bytes allocated to the SMN registers */
+#define SMN_REG_BANK_SIZE 0x40
+/** @} */
+
+/** Number of bytes of total register space for the SCC. */
+#define SCC_ADDRESS_RANGE (SMN_ADDR_OFFSET + SMN_REG_BANK_SIZE)
+
+/**
+ * @defgroup smnstatusregdefs SMN Status Register definitions (SMN_STATUS)
+ */
+/** @addtogroup smnstatusregdefs */
+/** @{ */
+/** SMN version id. */
+#define SMN_STATUS_VERSION_ID_MASK 0xfc000000
+/** number of bits to shift #SMN_STATUS_VERSION_ID_MASK to get it to LSB */
+#define SMN_STATUS_VERSION_ID_SHIFT 28
+/** Illegal bus master access attempted. */
+#define SMN_STATUS_ILLEGAL_MASTER 0x01000000
+/** Scan mode entered/exited since last reset. */
+#define SMN_STATUS_SCAN_EXIT 0x00800000
+/** Some security peripheral is initializing */
+#define SMN_STATUS_PERIP_INIT 0x00010000
+/** Internal error detected in SMN. */
+#define SMN_STATUS_SMN_ERROR 0x00008000
+/** SMN has an outstanding interrupt. */
+#define SMN_STATUS_SMN_STATUS_IRQ 0x00004000
+/** Software Alarm was triggered. */
+#define SMN_STATUS_SOFTWARE_ALARM 0x00002000
+/** Timer has expired. */
+#define SMN_STATUS_TIMER_ERROR 0x00001000
+/** Plaintext/Ciphertext compare failed. */
+#define SMN_STATUS_PC_ERROR 0x00000800
+/** Bit Bank detected overflow or underflow */
+#define SMN_STATUS_BITBANK_ERROR 0x00000400
+/** Algorithm Sequence Check failed. */
+#define SMN_STATUS_ASC_ERROR 0x00000200
+/** Security Policy Block detected error. */
+#define SMN_STATUS_SECURITY_POLICY_ERROR 0x00000100
+/** Security Violation Active error. */
+#define SMN_STATUS_SEC_VIO_ACTIVE_ERROR 0x00000080
+/** Processor booted from internal ROM. */
+#define SMN_STATUS_INTERNAL_BOOT 0x00000020
+/** SMN's internal state. */
+#define SMN_STATUS_STATE_MASK 0x0000001F
+/** Number of bits to shift #SMN_STATUS_STATE_MASK to get it to LSB. */
+#define SMN_STATUS_STATE_SHIFT 0
+/** @} */
+
+/**
+ * @defgroup sccscmstates SMN Model Secure State Controller States (SMN_STATE_MASK)
+ */
+/** @addtogroup sccscmstates */
+/** @{ */
+/** This is the first state of the SMN after power-on reset */
+#define SMN_STATE_START 0x0
+/** The SMN is zeroizing its RAM during reset */
+#define SMN_STATE_ZEROIZE_RAM 0x5
+/** SMN has passed internal checks, and is waiting for Software check-in */
+#define SMN_STATE_HEALTH_CHECK 0x6
+/** Fatal Security Violation. SMN is locked, SCM is inoperative. */
+#define SMN_STATE_FAIL 0x9
+/** SCC is in secure state. SCM is using secret key. */
+#define SMN_STATE_SECURE 0xA
+/** Due to non-fatal error, device is not secure. SCM is using default key. */
+#define SMN_STATE_NON_SECURE 0xC
+/** @} */
+
+/** @{ */
+/** SCM Status bit: Key Status is Default Key in Use */
+#define SCM_STATUS_KST_DEFAULT_KEY 0x80000000
+/** SCM Status bit: Key Status is (reserved) */
+#define SCM_STATUS_KST_RESERVED1 0x40000000
+/** SCM Status bit: Key status is Wrong Key */
+#define SCM_STATUS_KST_WRONG_KEY 0x20000000
+/** SCM Status bit: Bad Key detected */
+#define SCM_STATUS_KST_BAD_KEY 0x10000000
+/** SCM Status bit: Error has occurred */
+#define SCM_STATUS_ERR 0x00008000
+/** SCM Status bit: Monitor State is Failed */
+#define SCM_STATUS_MSS_FAIL 0x00004000
+/** SCM Status bit: Monitor State is Secure */
+#define SCM_STATUS_MSS_SEC 0x00002000
+/** SCM Status bit: Secure Storage is Failed */
+#define SCM_STATUS_RSS_FAIL 0x00000400
+/** SCM Status bit: Secure Storage is Secure */
+#define SCM_STATUS_RSS_SEC 0x00000200
+/** SCM Status bit: Secure Storage is Initializing */
+#define SCM_STATUS_RSS_INIT 0x00000100
+/** SCM Status bit: Unique Number Valid */
+#define SCM_STATUS_UNV 0x00000080
+/** SCM Status bit: Big Endian mode */
+#define SCM_STATUS_BIG 0x00000040
+/** SCM Status bit: Using Secret Key */
+#define SCM_STATUS_USK 0x00000020
+/** SCM Status bit: Ram is being blocked */
+#define SCM_STATUS_BAR 0x00000010
+/** Bit mask of SRS */
+#define SCM_STATUS_SRS_MASK 0x0000000F
+/** Number of bits to shift SRS to/from MSb */
+#define SCM_STATUS_SRS_SHIFT 0
+/** @} */
+
+#define SCM_STATUS_SRS_RESET 0x0 /**< Reset, Zeroise All */
+#define SCM_STATUS_SRS_READY 0x1 /**< All Ready */
+#define SCM_STATUS_SRS_ZBUSY 0x2 /**< Zeroize Busy (Partition Only) */
+#define SCM_STATUS_SRS_CBUSY 0x3 /**< Cipher Busy */
+#define SCM_STATUS_SRS_ABUSY 0x4 /**< All Busy */
+#define SCM_STATUS_SRS_ZDONE 0x5 /**< Zeroize Done, Cipher Ready */
+#define SCM_STATUS_SRS_CDONE 0x6 /**< Cipher Done, Zeroize Ready */
+#define SCM_STATUS_SRS_ZDONE2 0x7 /**< Zeroize Done, Cipher Busy */
+#define SCM_STATUS_SRS_CDONE2 0x8 /**< Cipher Done, Zeroize Busy */
+#define SCM_STATUS_SRS_ADONE 0xD /**< All Done */
+#define SCM_STATUS_SRS_FAIL 0xF /**< Fail State */
+
+
+/* Format of the SCM VERSION ID REGISTER */
+#define SCM_VER_BPP_MASK 0xFF000000 /**< Bytes Per Partition Mask */
+#define SCM_VER_BPP_SHIFT 24 /**< Bytes Per Partition Shift */
+#define SCM_VER_BPCB_MASK 0x001F0000 /**< Bytes Per Cipher Block Mask */
+#define SCM_VER_BPCB_SHIFT 16 /**< Bytes Per Cipher Block Shift */
+#define SCM_VER_NP_MASK 0x0000F000 /**< Number of Partitions Mask */
+#define SCM_VER_NP_SHIFT 12 /**< Number of Partitions Shift */
+#define SCM_VER_MAJ_MASK 0x00000F00 /**< Major Version Mask */
+#define SCM_VER_MAJ_SHIFT 8 /**< Major Version Shift */
+#define SCM_VER_MIN_MASK 0x000000FF /**< Minor Version Mask */
+#define SCM_VER_MIN_SHIFT 0 /**< Minor Version Shift */
+
+/**< SCC Hardware version supported by this driver */
+#define SCM_MAJOR_VERSION_2 2
+
+/* Format of the SCM ERROR STATUS REGISTER */
+#define SCM_ERRSTAT_MID_MASK 0x00F00000 /**< Master ID Mask */
+#define SCM_ERRSTAT_MID_SHIFT 20 /**< Master ID Shift */
+#define SCM_ERRSTAT_ILM 0x00080000 /**< Illegal Master */
+#define SCM_ERRSTAT_SUP 0x00008000 /**< Supervisor Access */
+#define SCM_ERRSTAT_ERC_MASK 0x00000F00 /**< Error Code Mask */
+#define SCM_ERRSTAT_ERC_SHIFT 8 /**< Error Code Shift */
+#define SCM_ERRSTAT_SMS_MASK 0x000000F0 /**< Secure Monitor State Mask */
+#define SCM_ERRSTAT_SMS_SHIFT 4 /**< Secure Monitor State Shift */
+#define SCM_ERRSTAT_SRS_MASK 0x0000000F /**< Secure Ram State Mask */
+#define SCM_ERRSTAT_SRS_SHIFT 0 /**< Secure Ram State Shift */
+
+/* SCM ERROR STATUS REGISTER ERROR CODES */
+#define SCM_ERCD_UNK_ADDR 0x1 /**< Unknown Address */
+#define SCM_ERCD_UNK_CMD 0x2 /**< Unknown Command */
+#define SCM_ERCD_READ_PERM 0x3 /**< Read Permission Error */
+#define SCM_ERCD_WRITE_PERM 0x4 /**< Write Permission Error */
+#define SCM_ERCD_DMA_ERROR 0x5 /**< DMA Error */
+#define SCM_ERCD_BLK_OVFL 0x6 /**< Encryption Block Length Overflow */
+#define SCM_ERCD_NO_KEY 0x7 /**< Key Not Engaged */
+#define SCM_ERCD_ZRZ_OVFL 0x8 /**< Zeroize Command Queue Overflow */
+#define SCM_ERCD_CPHR_OVFL 0x9 /**< Cipher Command Queue Overflow */
+#define SCM_ERCD_PROC_INTR 0xA /**< Process Interrupted */
+#define SCM_ERCD_WRNG_KEY 0xB /**< Wrong Key */
+#define SCM_ERCD_DEVICE_BUSY 0xC /**< Device Busy */
+#define SCM_ERCD_UNALGN_ADDR 0xD /**< DMA Unaligned Address */
+
+/* Format of the CIPHER COMMAND REGISTER */
+#define SCM_CCMD_LENGTH_MASK 0xFFF00000 /**< Cipher Length Mask */
+#define SCM_CCMD_LENGTH_SHIFT 20 /**< Cipher Length Shift */
+#define SCM_CCMD_OFFSET_MASK 0x000FFF00 /**< Block Offset Mask */
+#define SCM_CCMD_OFFSET_SHIFT 8 /**< Block Offset Shift */
+#define SCM_CCMD_PART_MASK 0x000000F0 /**< Partition Number Mask */
+#define SCM_CCMD_PART_SHIFT 4 /**< Partition Number Shift */
+#define SCM_CCMD_CCMD_MASK 0x0000000F /**< Cipher Command Mask */
+#define SCM_CCMD_CCMD_SHIFT 0 /**< Cipher Command Shift */
+
+/* Values for SCM_CCMD_CCMD field */
+#define SCM_CCMD_AES_DEC_ECB 1 /**< Decrypt without Chaining (ECB) */
+#define SCM_CCMD_AES_ENC_ECB 3 /**< Encrypt without Chaining (ECB) */
+#define SCM_CCMD_AES_DEC_CBC 5 /**< Decrypt with Chaining (CBC) */
+#define SCM_CCMD_AES_ENC_CBC 7 /**< Encrypt with Chaining (CBC) */
+
+#define SCM_CCMD_AES 1 /**< Use AES Mode */
+#define SCM_CCMD_DEC 0 /**< Decrypt */
+#define SCM_CCMD_ENC 2 /**< Encrypt */
+#define SCM_CCMD_ECB 0 /**< Perform operation without chaining (ECB) */
+#define SCM_CCMD_CBC 4 /**< Perform operation with chaining (CBC) */
+
+/* Format of the ZEROIZE COMMAND REGISTER */
+#define SCM_ZCMD_PART_MASK 0x000000F0 /**< Target Partition Mask */
+#define SCM_ZCMD_PART_SHIFT 4 /**< Target Partition Shift */
+#define SCM_ZCMD_CCMD_MASK 0x0000000F /**< Zeroize Command Mask */
+#define SCM_ZCMD_CCMD_SHIFT 0 /**< Zeroize Command Shift */
+
+/* MASTER ACCESS PERMISSIONS REGISTER */
+/* Note that API users should use the FSL_PERM_ defines instead of these */
+/** SCM Access Permission: Do not zeroize/deallocate partition
+ on SMN Fail state */
+#define SCM_PERM_NO_ZEROIZE 0x10000000
+/** SCM Access Permission: Ignore Supervisor/User mode
+ in permission determination */
+#define SCM_PERM_HD_SUP_DISABLE 0x00000800
+/** SCM Access Permission: Allow Read Access to Host Domain */
+#define SCM_PERM_HD_READ 0x00000400
+/** SCM Access Permission: Allow Write Access to Host Domain */
+#define SCM_PERM_HD_WRITE 0x00000200
+/** SCM Access Permission: Allow Execute Access to Host Domain */
+#define SCM_PERM_HD_EXECUTE 0x00000100
+/** SCM Access Permission: Allow Read Access to Trusted Host Domain */
+#define SCM_PERM_TH_READ 0x00000040
+/** SCM Access Permission: Allow Write Access to Trusted Host Domain */
+#define SCM_PERM_TH_WRITE 0x00000020
+/** SCM Access Permission: Allow Read Access to Other/World Domain */
+#define SCM_PERM_OT_READ 0x00000004
+/** SCM Access Permission: Allow Write Access to Other/World Domain */
+#define SCM_PERM_OT_WRITE 0x00000002
+/** SCM Access Permission: Allow Execute Access to Other/World Domain */
+#define SCM_PERM_OT_EXECUTE 0x00000001
+/**< Valid bits that can be set in the Permissions register */
+#define SCM_PERM_MASK 0xC0000F67
+
+/* Zeroize Command register definitions */
+#define ZCMD_DEALLOC_PART 3 /**< Deallocate Partition */
+#define Z_INT_EN 0x00000002 /**< Zero Interrupt Enable */
+
+/**
+ * @defgroup scmpartitionownersregdefs SCM Partition Owners Register
+ */
+/** @addtogroup scmpartitionownersregdefs */
+/** @{ */
+/** Number of bits to shift partition number to get to its field. */
+#define SCM_POWN_SHIFT 2
+/** Mask for a field once the register has been shifted. */
+#define SCM_POWN_MASK 3
+/** Partition is free */
+#define SCM_POWN_PART_FREE 0
+/** Partition is unable to be allocated */
+#define SCM_POWN_PART_UNUSABLE 1
+/** Partition is owned by another master */
+#define SCM_POWN_PART_OTHER 2
+/** Partition is owned by this master */
+#define SCM_POWN_PART_OWNED 3
+/** @} */
+
+/**
+ * @defgroup smnpartitionsengagedregdefs SCM Partitions Engaged Register
+ */
+/** @addtogroup smnpartitionsengagedregdefs */
+/** @{ */
+/** Number of bits to shift partition number to get to its field. */
+#define SCM_PENG_SHIFT 1
+/** Engaged value for a field once the register has been shifted. */
+#define SCM_PENG_ENGAGED 1
+/** @} */
+
+/** Number of bytes between each subsequent SMID register */
+#define SCM_SMID_WIDTH 8
+
+/**
+ * @defgroup smncommandregdefs SMN Command Register Definitions (SMN_COMMAND_REG)
+ */
+/** @addtogroup smncommandregdefs */
+/** @{ */
+
+/** These bits are unimplemented or reserved */
+#define SMN_COMMAND_ZEROS_MASK 0xfffffff0
+#define SMN_COMMAND_CLEAR_INTERRUPT 0x8 /**< Clear SMN Interrupt */
+#define SMN_COMMAND_CLEAR_BIT_BANK 0x4 /**< Clear SMN Bit Bank */
+#define SMN_COMMAND_ENABLE_INTERRUPT 0x2 /**< Enable SMN Interrupts */
+#define SMN_COMMAND_SET_SOFTWARE_ALARM 0x1 /**< Set Software Alarm */
+/** @} */
+
+/**
+ * @defgroup smntimercontroldefs SMN Timer Control Register definitions (SMN_TIMER_CONTROL)
+ */
+/** @addtogroup smntimercontroldefs */
+/** @{ */
+/** These bits are reserved or zero */
+#define SMN_TIMER_CTRL_ZEROS_MASK 0xfffffffc
+/** Load the timer from #SMN_TIMER_IV_REG */
+#define SMN_TIMER_LOAD_TIMER 0x2
+/** Setting to zero stops the Timer */
+#define SMN_TIMER_STOP_MASK 0x1
+/** Setting this value starts the timer */
+#define SMN_TIMER_START_TIMER 0x1
+/** @} */
+
+/**
+ * @defgroup scmchainmodedefs SCM_CHAINING_MODE_MASK - Bit definitions
+ */
+/** @addtogroup scmchainmodedefs */
+/** @{ */
+#define SCM_CBC_MODE 0x2 /**< Cipher block chaining */
+#define SCM_ECB_MODE 0x0 /**< Electronic codebook. */
+/** @} */
+
+/* Bit definitions in the SCM_CIPHER_MODE_MASK */
+/**
+ * @defgroup scmciphermodedefs SCM_CIPHER_MODE_MASK - Bit definitions
+ */
+/** @addtogroup scmciphermodedefs */
+/** @{ */
+#define SCM_DECRYPT_MODE 0x1 /**< decrypt from black to red memory */
+#define SCM_ENCRYPT_MODE 0x0 /**< encrypt from red to black memory */
+/** @} */
+
+/**
+ * @defgroup smndbgdetdefs SMN Debug Detector Status Register (SCM_DEBUG_DETECT_STAT)
+ */
+/** @addtogroup smndbgdetdefs */
+/** @{ */
+#define SMN_DBG_ZEROS_MASK 0xfffff000 /**< These bits are zero or reserved */
+#define SMN_DBG_D12 0x0800 /**< Error detected on Debug Port D12 */
+#define SMN_DBG_D11 0x0400 /**< Error detected on Debug Port D11 */
+#define SMN_DBG_D10 0x0200 /**< Error detected on Debug Port D10 */
+#define SMN_DBG_D9 0x0100 /**< Error detected on Debug Port D9 */
+#define SMN_DBG_D8 0x0080 /**< Error detected on Debug Port D8 */
+#define SMN_DBG_D7 0x0040 /**< Error detected on Debug Port D7 */
+#define SMN_DBG_D6 0x0020 /**< Error detected on Debug Port D6 */
+#define SMN_DBG_D5 0x0010 /**< Error detected on Debug Port D5 */
+#define SMN_DBG_D4 0x0008 /**< Error detected on Debug Port D4 */
+#define SMN_DBG_D3 0x0004 /**< Error detected on Debug Port D3 */
+#define SMN_DBG_D2 0x0002 /**< Error detected on Debug Port D2 */
+#define SMN_DBG_D1 0x0001 /**< Error detected on Debug Port D1 */
+/** @} */
+
+/** Mask for the usable bits of the Sequence Start Register
+ (#SMN_SEQ_START_REG) */
+#define SMN_SEQUENCE_START_MASK 0x0000ffff
+
+/** Mask for the usable bits of the Sequence End Register
+ (#SMN_SEQ_END_REG) */
+#define SMN_SEQUENCE_END_MASK 0x0000ffff
+
+/** Mask for the usable bits of the Sequence Check Register
+ (#SMN_SEQ_CHECK_REG) */
+#define SMN_SEQUENCE_CHECK_MASK 0x0000ffff
+
+/** Mask for the usable bits of the Bit Counter Register
+ (#SMN_BB_CNT_REG) */
+#define SMN_BIT_COUNT_MASK 0x000007ff
+
+/** Mask for the usable bits of the Bit Bank Increment Size Register
+ (#SMN_BB_INC_REG) */
+#define SMN_BITBANK_INC_SIZE_MASK 0x000007ff
+
+/** Mask for the usable bits of the Bit Bank Decrement Register
+ (#SMN_BB_DEC_REG) */
+#define SMN_BITBANK_DECREMENT_MASK 0x000007ff
+
+/** Mask for the usable bits of the Compare Size Register
+ (#SMN_COMPARE_REG) */
+#define SMN_COMPARE_SIZE_MASK 0x0000003f
+
+/*! @} */
+
+#endif /* SCC_DRIVER_H */
diff --git a/include/linux/mxc_scc_driver.h b/include/linux/mxc_scc_driver.h
new file mode 100644
index 000000000000..5f1e1bbe8117
--- /dev/null
+++ b/include/linux/mxc_scc_driver.h
@@ -0,0 +1,1031 @@
+
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_SCC_DRIVER_H__
+#define __ASM_ARCH_MXC_SCC_DRIVER_H__
+
+/* Start marker for C++ compilers */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * NAMING CONVENTIONS
+ * ==================
+ * (A note to maintainers and other interested parties)
+ *
+ * Use scc_ or SCC_ prefix for 'high-level' interface routines and the types
+ * passed to those routines. Try to avoid #defines in these interfaces.
+ *
+ * Use SMN_ or SCM_ prefix for the #defines used with scc_read_register() and
+ * scc_write_register, or values passed/retrieved from those routines.
+ */
+
+/*!
+ * @defgroup MXCSCC SCC Driver
+ *
+ * @ingroup MXCSECDRVRS
+ */
+
+/*!
+ * @file arch-mxc/mxc_scc_driver.h
+ *
+ * @brief (Header file to use the SCC driver.)
+ *
+ * The SCC driver will only be available to other kernel modules. That is,
+ * there will be no node file in /dev, no way for a user-mode program to access
+ * the driver, no way for a user program to access the device directly.
+ *
+ * With the exception of #scc_monitor_security_failure(), all routines are
+ * 'synchronous', i.e. they will not return to their caller until the requested
+ * action is complete, or fails to complete. Some of these functions could
+ * take quite a while to perform, depending upon the request.
+ *
+ * Routines are provided to:
+ * @li encrypt or decrypt secrets - #scc_crypt()
+ * @li trigger a security-violation alarm - #scc_set_sw_alarm()
+ * @li get configuration and version information - #scc_get_configuration()
+ * @li zeroize memory - #scc_zeroize_memories()
+ * @li Work on wrapped and stored secret values: #scc_alloc_slot(),
+ * #scc_dealloc_slot(), scc_load_slot(), #scc_decrypt_slot(),
+ * #scc_encrypt_slot(), #scc_get_slot_info()
+
+ * @li monitor the Security Failure alarm - #scc_monitor_security_failure()
+ * @li stop monitoring Security Failure alarm -
+ * #scc_stop_monitoring_security_failure()
+ * @li write registers of the SCC - #scc_write_register()
+ * @li read registers of the SCC - #scc_read_register()
+ *
+ * The driver does not allow "storage" of data in either the Red or Black
+ * memories. Any decrypted secret is returned to the user, and if the user
+ * wants to use it at a later point, the encrypted form must again be passed
+ * to the driver, and it must be decrypted again.
+ *
+ * The SCC encrypts and decrypts using Triple DES with an internally stored
+ * key. When the SCC is in Secure mode, it uses its secret, unique-per-chip
+ * key. When it is in Non-Secure mode, it uses a default key. This ensures
+ * that secrets stay secret if the SCC is not in Secure mode.
+ *
+ * Not all functions that could be provided in a 'high level' manner have been
+ * implemented. Among the missing are interfaces to the ASC/AIC components and
+ * the timer functions. These and other features must be accessed through
+ * #scc_read_register() and #scc_write_register(), using the @c \#define values
+ * provided.
+ *
+ * Here is a glossary of acronyms used in the SCC driver documentation:
+ * - CBC - Cipher Block Chaining. A method of performing a block cipher.
+ * Each block is encrypted using some part of the result of the previous
+ * block's encryption. It needs an 'initialization vector' to seed the
+ * operation.
+ * - ECB - Electronic Code Book. A method of performing a block cipher.
+ * With a given key, a given block will always encrypt to the same value.
+ * - DES - Data Encryption Standard. (8-byte) Block cipher algorithm which
+ * uses 56-bit keys. In SCC, this key is constant and unique to the device.
+ * SCC uses the "triple DES" form of this algorithm.
+ * - AIC - Algorithm Integrity Checker.
+ * - ASC - Algorithm Sequence Checker.
+ * - SMN - Security Monitor. The part of the SCC responsible for monitoring
+ * for security problems and notifying the CPU and other PISA components.
+ * - SCM - Secure Memory. The part of the SCC which handles the cryptography.
+ * - SCC - Security Controller. Central security mechanism for PISA.
+ * - PISA - Platform-Independent Security Architecture.
+ */
+
+/* Temporarily define compile-time flags to make Doxygen happy. */
+#ifdef DOXYGEN_HACK
+/*! @defgroup scccompileflags SCC Driver compile-time flags
+ *
+ * These preprocessor flags should be set, if desired, in a makefile so
+ * that they show up on the compiler command line.
+ */
+/*! @addtogroup scccompileflags */
+
+/*! @{ */
+/*!
+ * Compile-time flag to change @ref smnregs and @ref scmregs
+ * offset values for the SCC's implementation on the MX.21 board.
+ *
+ * This must also be set properly for any code which calls the
+ * scc_read_register() or scc_write_register() functions or references the
+ * register offsets.
+ */
+#define TAHITI
+/*! @} */
+#undef TAHITI
+
+#endif /* DOXYGEN_HACK */
+
+/*! Major Version of the driver. Used for
+ scc_configuration->driver_major_version */
+#define SCC_DRIVER_MAJOR_VERSION_1 1
+/*! Old Minor Version of the driver. */
+#define SCC_DRIVER_MINOR_VERSION_0 0
+/*! Old Minor Version of the driver. */
+#define SCC_DRIVER_MINOR_VERSION_4 4
+/*! Old Minor Version of the driver. */
+#define SCC_DRIVER_MINOR_VERSION_5 5
+/*! Old Minor Version of the driver. */
+#define SCC_DRIVER_MINOR_VERSION_6 6
+/*! Minor Version of the driver. Used for
+ scc_configuration->driver_minor_version */
+#define SCC_DRIVER_MINOR_VERSION_8 8
+
+
+/*!
+ * @typedef scc_return_t
+ */
+/*! Common status return values from SCC driver functions. */
+ typedef enum scc_return_t {
+ SCC_RET_OK = 0, /*!< Function succeeded */
+ SCC_RET_FAIL, /*!< Non-specific failure */
+ SCC_RET_VERIFICATION_FAILED, /*!< Decrypt validation failed */
+ SCC_RET_TOO_MANY_FUNCTIONS, /*!< At maximum registered functions */
+ SCC_RET_BUSY, /*!< SCC is busy and cannot handle request */
+ SCC_RET_INSUFFICIENT_SPACE, /*!< Encryption or decryption failed because
+ @c count_out_bytes says that @c data_out is
+ too small to hold the value. */
+ } scc_return_t;
+
+/*!
+ * Configuration information about SCC and the driver.
+ *
+ * This struct/typedef contains information from the SCC and the driver to
+ * allow the user of the driver to determine the size of the SCC's memories and
+ * the version of the SCC and the driver.
+ */
+ typedef struct scc_config_t {
+ int driver_major_version; /*!< Major version of the SCC driver code */
+ int driver_minor_version; /*!< Minor version of the SCC driver code */
+ int scm_version; /*!< Version from SCM Configuration register */
+ int smn_version; /*!< Version from SMN Status register */
+ int block_size_bytes; /*!< Number of bytes per block of RAM; also
+ block size of the crypto algorithm. */
+ int black_ram_size_blocks; /*!< Number of blocks of Black RAM */
+ int red_ram_size_blocks; /*!< Number of blocks of Red RAM */
+ } scc_config_t;
+
+/*!
+ * @typedef scc_enc_dec_t
+ */
+/*!
+ * Determine whether SCC will run its cryptographic
+ * function as an encryption or decryption. Used as an argument to
+ * #scc_crypt().
+ */
+ typedef enum scc_enc_dec_t {
+ SCC_ENCRYPT, /*!< Encrypt (from Red to Black) */
+ SCC_DECRYPT /*!< Decrypt (from Black to Red) */
+ } scc_enc_dec_t;
+
+/*
+ * @typedef scc_crypto_mode_t
+ */
+/*!
+ * Determine whether SCC will run its cryptographic function in ECB (electronic
+ * codebook) or CBC (cipher-block chaining) mode. Used as an argument to
+ * #scc_crypt().
+ */
+ typedef enum scc_crypto_mode_t {
+ SCC_ECB_MODE, /*!< Electronic Codebook Mode */
+ SCC_CBC_MODE /*!< Cipher Block Chaining Mode */
+ } scc_crypto_mode_t;
+
+/*!
+ * @typedef scc_verify_t
+ */
+/*!
+ * Tell the driver whether it is responsible for verifying the integrity of a
+ * secret. During an encryption, using other than #SCC_VERIFY_MODE_NONE will
+ * cause a check value to be generated and appended to the plaintext before
+ * encryption. During decryption, the check value will be verified after
+ * decryption, and then stripped from the message.
+ */
+ typedef enum scc_verify_t {
+ /*! No verification value added or checked. Input plaintext data must be
+ * be a multiple of the blocksize (#scc_get_configuration()). */
+ SCC_VERIFY_MODE_NONE,
+ /*! Driver will generate/validate a 2-byte CCITT CRC. Input plaintext will
+ be padded to a multiple of the blocksize, adding 3-10 bytes to the
+ resulting output ciphertext. Upon decryption, this padding will be
+ stripped, and the CRC will be verified. */
+ SCC_VERIFY_MODE_CCITT_CRC
+ } scc_verify_t;
+
+/*!
+ * Determine if the given credentials match that of the key slot.
+ *
+ * @param[in] owner_id A value which will control access to the slot.
+ * @param[in] slot Key Slot to query
+ * @param[in] access_len Length of the key
+ *
+ * @return 0 on success, non-zero on failure. See #scc_return_t.
+ */
+ scc_return_t
+ scc_verify_slot_access(uint64_t owner_id, uint32_t slot,
+ uint32_t access_len);
+
+/*!
+ * Retrieve configuration information from the SCC.
+ *
+ * This function always succeeds.
+ *
+ * @return A pointer to the configuration information. This is a pointer to
+ * static memory and must not be freed. The values never change, and
+ * the return value will never be null.
+ */
+ extern scc_config_t *scc_get_configuration(void);
+
+/*!
+ * Zeroize Red and Black memories of the SCC. This will start the Zeroizing
+ * process. The routine will return when the memories have zeroized or failed
+ * to do so. The driver will poll waiting for this to occur, so this
+ * routine must not be called from interrupt level. Some future version of
+ * driver may elect instead to sleep.
+ *
+ * @return 0 or error if initialization fails.
+ */
+ extern scc_return_t scc_zeroize_memories(void);
+
+/*!
+ * Perform a Triple DES encryption or decryption operation.
+ *
+ * This routine will cause the SCM to perform an encryption or decryption with
+ * its internal key. If the SCC's #SMN_STATUS register shows that the SCC is
+ * in #SMN_STATE_SECURE, then the Secret Key will be used. If it is
+ * #SMN_STATE_NON_SECURE (or health check), then the Default Key will be used.
+ *
+ * This function will perform in a variety of ways, depending upon the values
+ * of @c direction, @c crypto_mode, and @c check_mode. If
+ * #SCC_VERIFY_MODE_CCITT_CRC mode is requested, upon successful completion,
+ * the @c count_in_bytes will be different from the returned value of @c
+ * count_out_bytes. This is because the two-byte CRC and some amount of
+ * padding (at least one byte) will either be added or stripped.
+ *
+ * This function will not return until the SCC has performed the operation (or
+ * reported failure to do so). It must therefore not be called from interrupt
+ * level. In the current version, it will poll the SCC for completion. In
+ * future versions, it may sleep.
+ *
+ * @param[in] count_in_bytes The number of bytes to move through the crypto
+ * function. Must be greater than zero.
+ *
+ * @param[in] data_in Pointer to the array of bytes to be used as input
+ * to the crypto function.
+ *
+ * @param[in] init_vector Pointer to the block-sized (8 byte) array of
+ * bytes which form the initialization vector for
+ * this operation. A non-null value is required
+ * when @c crypto_mode has the value #SCC_CBC_MODE;
+ * the value is ignored in #SCC_ECB_MODE.
+ *
+ * @param[in] direction Direct the driver to perform encryption or
+ * decryption.
+ *
+ * @param[in] crypto_mode Run the crypto function in ECB or CBC mode.
+ *
+ * @param[in] check_mode During encryption, generate and append a check
+ * value to the plaintext and pad the resulting
+ * data. During decryption, validate the plaintext
+ * with that check value and remove the padding.
+ *
+ * @param[in,out] count_out_bytes On input, the number of bytes available for
+ * copying to @c data_out. On return, the number of
+ * bytes copied to @c data_out.
+ *
+ * @param[out] data_out Pointer to the array of bytes that are where the
+ * output of the crypto function are to be placed.
+ * For encryption, this must be able to hold a
+ * longer ciphertext than the plaintext message at
+ * @c data_in. The driver will append a 'pad' of
+ * 1-8 bytes to the message, and if @c check_mode is
+ * used, additional bytes may be added, the number
+ * depending upon the type of check being requested.
+ *
+ * @return 0 on success, non-zero on failure. See #scc_return_t.
+ *
+ * @internal
+ * This function will verify SCC state and the functions parameters. It will
+ * acquire the crypto lock, and set up some SCC registers and variables common
+ * to encryption and decryption. A rough check will be made to verify that
+ * enough space is available in @c count_out_bytes. Upon success, either the
+ * #scc_encrypt or #scc_decrypt routine will be called to do the actual work.
+ * The crypto lock will then be released.
+ */
+extern scc_return_t scc_crypt(unsigned long count_in_bytes,
+ const uint8_t * data_in,
+ const uint8_t * init_vector,
+ scc_enc_dec_t direction,
+ scc_crypto_mode_t crypto_mode,
+ scc_verify_t check_mode,
+ uint8_t * data_out,
+ unsigned long *count_out_bytes);
+
+
+/*!
+ * Allocate a key slot for a stored key (or other stored value).
+ *
+ * This feature is to allow decrypted secret values to be kept in RED RAM.
+ * This can all visibility of the data only by Sahara.
+ *
+ * @param value_size_bytes Size, in bytes, of RED key/value. Currently only
+ * a size up to 32 bytes is supported.
+ *
+ * @param owner_id A value which will control access to the slot.
+ * It must be passed into to any subsequent calls to
+ * use the assigned slot.
+ *
+ * @param[out] slot The slot number for the key.
+ *
+ * @return 0 on success, non-zero on failure. See #scc_return_t.
+ */
+ extern scc_return_t scc_alloc_slot(uint32_t value_size_bytes,
+ uint64_t owner_id, uint32_t * slot);
+
+/*!
+ * Deallocate the key slot of a stored key or secret value.
+ *
+ * @param owner_id The id which owns the @c slot.
+ *
+ * @param slot The slot number for the key.
+
+ * @return 0 on success, non-zero on failure. See #scc_return_t.
+ */
+ extern scc_return_t scc_dealloc_slot(uint64_t owner_id, uint32_t slot);
+
+/*!
+ * Load a value into a slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param key_data Data to load into the slot
+ * @param key_length Length, in bytes, of @c key_data to copy to SCC.
+ *
+ * @return SCC_RET_OK on success. SCC_RET_FAIL will be returned if slot
+ * specified cannot be accessed for any reason, or SCC_RET_INSUFFICIENT_SPACE
+ * if @c key_length exceeds the size of the slot.
+ */
+ extern scc_return_t scc_load_slot(uint64_t owner_id, uint32_t slot,
+ const uint8_t * key_data,
+ uint32_t key_length);
+/*!
+ * Read a value from a slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param key_length Length, in bytes, of @c key_data to copy from SCC.
+ * @param key_data Location to write the key
+ *
+ * @return SCC_RET_OK on success. SCC_RET_FAIL will be returned if slot
+ * specified cannot be accessed for any reason, or SCC_RET_INSUFFICIENT_SPACE
+ * if @c key_length exceeds the size of the slot.
+ */
+ extern scc_return_t scc_read_slot(uint64_t owner_id, uint32_t slot,
+ uint32_t key_length,
+ uint8_t * key_data);
+
+/*!
+ * Allocate a key slot to fit the requested size.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param length Length, in bytes, of @c black_data
+ * @param black_data Location to store result of encrypting RED data in slot
+ *
+ * @return SCC_RET_OK on success, SCC_RET_FAIL if slot specified cannot be
+ * accessed for any reason.
+ */
+ extern scc_return_t scc_encrypt_slot(uint64_t owner_id, uint32_t slot,
+ uint32_t length,
+ uint8_t * black_data);
+
+/*!
+ * Decrypt some black data and leave result in the slot.
+ *
+ * @param owner_id Value of owner of slot
+ * @param slot Handle of slot
+ * @param length Length, in bytes, of @c black_data
+ * @param black_data Location of data to dencrypt and store in slot
+ *
+ * @return SCC_RET_OK on success, SCC_RET_FAIL if slot specified cannot be
+ * accessed for any reason.
+ */
+ extern scc_return_t scc_decrypt_slot(uint64_t owner_id, uint32_t slot,
+ uint32_t length,
+ const uint8_t * black_data);
+
+/*!
+ * Get attributes of data in RED slot.
+ *
+ * @param owner_id The id which owns the @c slot.
+ *
+ * @param slot The slot number for the key.
+ *
+ * @param[out] address Physical address of RED value.
+ *
+ * @param[out] value_size_bytes Length, in bytes, of RED value,
+ * or NULL if unneeded..
+ *
+ * @param[out] slot_size_bytes Length, in bytes, of slot size,
+ * or NULL if unneeded..
+ *
+ * @return 0 on success, non-zero on failure. See #scc_return_t.
+ */
+ extern scc_return_t scc_get_slot_info(uint64_t owner_id, uint32_t slot,
+ uint32_t *address,
+ uint32_t *value_size_bytes,
+ uint32_t *slot_size_bytes);
+
+/*!
+ * Signal a software alarm to the SCC. This will take the SCC and other PISA
+ * parts out of Secure mode and into Security Failure mode. The SCC will stay
+ * in failed mode until a reboot.
+ *
+ * @internal
+ * If the SCC is not already in fail state, simply write the
+ * #SMN_COMMAND_SET_SOFTWARE_ALARM bit in #SMN_COMMAND. Since there is no
+ * reason to wait for the interrupt to bounce back, simply act as though
+ * one did.
+ */
+ extern void scc_set_sw_alarm(void);
+
+/*!
+ * This routine will register a function to be called should a Security Failure
+ * be signalled by the SCC (Security Monitor).
+ *
+ * The callback function may be called from interrupt level, it may be called
+ * from some process' task. It should therefore not take a long time to
+ * perform its operation, and it may not sleep.
+ *
+ * @param callback_func Function pointer to routine which will receive
+ * notification of the security failure.
+ * @return 0 if function was successfully registered, non-zero on
+ * failure. See #scc_return_t.
+ *
+ * @internal
+ * There is a fixed global static array which keeps track of the requests to
+ * monitor the failure.
+ *
+ * Add @c callback_func to the first empty slot in #scc_callbacks[]. If there
+ * is no room, return #SCC_RET_TOO_MANY_FUNCTIONS.
+ */
+ extern scc_return_t scc_monitor_security_failure(void
+ callback_func(void));
+
+/*!
+ * This routine will deregister a function previously registered with
+ * #scc_monitor_security_failure().
+ *
+ * @param callback_func Function pointer to routine previously registered with
+ * #scc_stop_monitoring_security_failure().
+ */
+ extern void scc_stop_monitoring_security_failure(void
+ callback_func(void));
+
+/*!
+ * Read value from an SCC register.
+ * The offset will be checked for validity (range) as well as whether it is
+ * accessible (e.g. not busy, not in failed state) at the time of the call.
+ *
+ * @param[in] register_offset The (byte) offset within the SCC block
+ * of the register to be queried. See
+ * @ref scmregs and @ref smnregs.
+ * @param[out] value Pointer to where value from the register
+ * should be placed.
+ * @return 0 if OK, non-zero on error. See #scc_return_t.
+ *
+ * @internal
+ * Verify that the register_offset is a) valid, b) refers to a readable
+ * register, and c) the SCC is in a state which would allow a read of this
+ * register.
+ */
+ extern scc_return_t scc_read_register(int register_offset,
+ uint32_t * value);
+
+/*!
+ * Write a new value into an SCC register.
+ * The offset will be checked for validity (range) as well as whether it is
+ * accessible (e.g. not busy, not in failed state) at the time of the call.
+ *
+ * @param[in] register_offset The (byte) offset within the SCC block
+ * of the register to be modified. See
+ * @ref scmregs and @ref smnregs.
+ * @param[in] value The value to store into the register.
+ * @return 0 if OK, non-zero on error. See #scc_return_t.
+ *
+ * @internal
+ * Verify that the register_offset is a) valid, b) refers to a writeable
+ * register, and c) the SCC is in a state which would allow a write to this
+ * register.
+ */
+ extern scc_return_t scc_write_register(int register_offset,
+ uint32_t value);
+
+/*
+ * NOTE TO MAINTAINERS
+ *
+ * All of the doxygen comments for the register offset values are in this the
+ * following comment section. Any changes to register names or definitions
+ * must be reflected in this section and in both the TAHITI and non-TAHITI
+ *version of the memory map.
+ */
+
+/*!
+ * @defgroup scmregs SCM Registers
+ *
+ * These values are offsets into the SCC for the Secure Memory
+ * (SCM) registers. They are used in the @c register_offset parameter of
+ * #scc_read_register() and #scc_write_register().
+ */
+/*! @addtogroup scmregs */
+/*! @{ */
+/*! @def SCM_RED_START
+ * Starting block offset in red memory for cipher function. */
+
+/*! @def SCM_BLACK_START
+ * Starting block offset in black memory for cipher function. */
+
+/*! @def SCM_LENGTH
+ * Number of blocks to process during cipher function */
+
+/*! @def SCM_CONTROL
+ * SCM Control register.
+ * See @ref scmcontrolregdefs "SCM Control Register definitions" for details.
+ */
+
+/*! @def SCM_STATUS
+ * SCM Status register.
+ * See @ref scmstatusregdefs "SCM Status Register Definitions" for details.
+ */
+
+/*! @def SCM_ERROR_STATUS
+ * SCM Error Status Register.
+ * See @ref scmerrstatdefs "SCM Error Status Register definitions" for
+ * details. */
+
+/*! @def SCM_INTERRUPT_CTRL
+ * SCM Interrupt Control Register.
+ * See @ref scminterruptcontroldefs "SCM Interrupt Control Register definitions"
+ * for details.
+ */
+
+/*! @def SCM_CONFIGURATION
+ * SCM Configuration Register.
+ * See @ref scmconfigdefs "SCM Configuration Register Definitions" for
+ * details.
+ */
+
+/*! @def SCM_INIT_VECTOR_0
+ * Upper Half of the Initialization Vector */
+
+/*! @def SCM_INIT_VECTOR_1
+ * Lower Half of the Initialization Vector */
+
+/*! @def SCM_RED_MEMORY
+ * Starting location of first block of Red memory */
+
+/*! @def SCM_BLACK_MEMORY
+ * Starting location of first block of Black memory */
+
+ /*! @} *//* end of SCM group */
+
+/*!
+ * @defgroup smnregs SMN Registers
+ *
+ * These values are offsets into the SCC for the Security Monitor
+ * (SMN) registers. They are used in the @c register_offset parameter of the
+ * #scc_read_register() and #scc_write_register().
+ */
+/*! @addtogroup smnregs */
+/*! @{ */
+/*! @def SMN_STATUS
+ * Status register for SMN.
+ * See @ref smnstatusregdefs "SMN Status Register definitions" for further
+ * information.
+ */
+
+/*! @def SMN_COMMAND
+ * Command register for SMN. See
+ * @ref smncommandregdefs "Command Register Definitions" for further
+ * information.
+ */
+
+/*! @def SMN_SEQUENCE_START
+ * Sequence Start register for ASC. See #SMN_SEQUENCE_START_MASK
+ */
+
+/*! @def SMN_SEQUENCE_END
+ * Sequence End register for ASC. See #SMN_SEQUENCE_CHECK_MASK
+ */
+
+/*! @def SMN_SEQUENCE_CHECK
+ * Sequence Check register for ASC. See #SMN_SEQUENCE_END_MASK
+ */
+
+/*! @def SMN_BIT_COUNT
+ * Bit Bank Repository for AIC. See #SMN_BIT_COUNT_MASK
+ */
+
+/*! @def SMN_BITBANK_INC_SIZE
+ * Bit Bank Increment Size for AIC. See #SMN_BITBANK_INC_SIZE_MASK
+ */
+
+/*! @def SMN_BITBANK_DECREMENT
+ * Bit Bank Decrement for AIC. See #SMN_BITBANK_DECREMENT_MASK
+ */
+
+/*! @def SMN_COMPARE_SIZE
+ * Compare Size register for Plaintext/Ciphertext checker. See
+ * #SMN_COMPARE_SIZE_MASK */
+
+/*! @def SMN_PLAINTEXT_CHECK
+ * Plaintext Check register for Plaintext/Ciphertext checker.
+ */
+
+/*! @def SMN_CIPHERTEXT_CHECK
+ * Ciphertext Check register for Plaintext/Ciphertext checker.
+ */
+
+/*! @def SMN_TIMER_IV
+ * Timer Initial Value register
+ */
+
+/*! @def SMN_TIMER_CONTROL
+ * Timer Control register.
+ * See @ref smntimercontroldefs "SMN Timer Control Register definitions".
+ */
+
+/*! @def SMN_DEBUG_DETECT_STAT
+ * Debug Detector Status Register
+ * See @ref smndbgdetdefs "SMN Debug Detector Status Register"for definitions.
+ */
+
+/*! @def SMN_TIMER
+ * Current value of the Timer Register
+ */
+
+ /*! @} *//* end of SMN group */
+
+/*
+ * SCC MEMORY MAP
+ *
+ */
+
+/* SCM registers */
+#define SCM_RED_START 0x00000000 /* read/write */
+#define SCM_BLACK_START 0x00000004 /* read/write */
+#define SCM_LENGTH 0x00000008 /* read/write */
+#define SCM_CONTROL 0x0000000C /* read/write */
+#define SCM_STATUS 0x00000010 /* read only */
+#define SCM_ERROR_STATUS 0x00000014 /* read/write */
+#define SCM_INTERRUPT_CTRL 0x00000018 /* read/write */
+#define SCM_CONFIGURATION 0x0000001C /* read only */
+#define SCM_INIT_VECTOR_0 0x00000020 /* read/write */
+#define SCM_INIT_VECTOR_1 0x00000024 /* read/write */
+#define SCM_RED_MEMORY 0x00000400 /* read/write */
+#define SCM_BLACK_MEMORY 0x00000800 /* read/write */
+
+/* SMN Registers */
+#define SMN_STATUS 0x00001000 /* read/write */
+#define SMN_COMMAND 0x00001004 /* read/write */
+#define SMN_SEQUENCE_START 0x00001008 /* read/write */
+#define SMN_SEQUENCE_END 0x0000100C /* read/write */
+#define SMN_SEQUENCE_CHECK 0x00001010 /* read/write */
+#define SMN_BIT_COUNT 0x00001014 /* read only */
+#define SMN_BITBANK_INC_SIZE 0x00001018 /* read/write */
+#define SMN_BITBANK_DECREMENT 0x0000101C /* write only */
+#define SMN_COMPARE_SIZE 0x00001020 /* read/write */
+#define SMN_PLAINTEXT_CHECK 0x00001024 /* read/write */
+#define SMN_CIPHERTEXT_CHECK 0x00001028 /* read/write */
+#define SMN_TIMER_IV 0x0000102C /* read/write */
+#define SMN_TIMER_CONTROL 0x00001030 /* read/write */
+#define SMN_DEBUG_DETECT_STAT 0x00001034 /* read/write */
+#define SMN_TIMER 0x00001038 /* read only */
+
+/*! Total address space of the SCC, in bytes */
+#define SCC_ADDRESS_RANGE 0x103c
+
+/*!
+ * @defgroup smnstatusregdefs SMN Status Register definitions (SMN_STATUS)
+ */
+/*! @addtogroup smnstatusregdefs */
+/*! @{ */
+/*! SMN version id. */
+#define SMN_STATUS_VERSION_ID_MASK 0xfc000000
+/*! number of bits to shift #SMN_STATUS_VERSION_ID_MASK to get it to LSB */
+#define SMN_STATUS_VERSION_ID_SHIFT 26
+/*! Cacheable access to SMN attempted. */
+#define SMN_STATUS_CACHEABLE_ACCESS 0x02000000
+/*! Illegal bus master access attempted. */
+#define SMN_STATUS_ILLEGAL_MASTER 0x01000000
+/*! Scan mode entered/exited since last reset. */
+#define SMN_STATUS_SCAN_EXIT 0x00800000
+/*! Unaligned access attempted. */
+#define SMN_STATUS_UNALIGNED_ACCESS 0x00400000
+/*! Bad byte offset access attempted. */
+#define SMN_STATUS_BYTE_ACCESS 0x00200000
+/*! Illegal address access attempted. */
+#define SMN_STATUS_ILLEGAL_ADDRESS 0x00100000
+/*! User access attempted. */
+#define SMN_STATUS_USER_ACCESS 0x00080000
+/*! SCM is using DEFAULT key. */
+#define SMN_STATUS_DEFAULT_KEY 0x00040000
+/*! SCM detects weak or bad key. */
+#define SMN_STATUS_BAD_KEY 0x00020000
+/*! Illegal access to SCM detected. */
+#define SMN_STATUS_ILLEGAL_ACCESS 0x00010000
+/*! Internal error detected in SCM. */
+#define SMN_STATUS_SCM_ERROR 0x00008000
+/*! SMN has an outstanding interrupt. */
+#define SMN_STATUS_SMN_STATUS_IRQ 0x00004000
+/*! Software Alarm was triggered. */
+#define SMN_STATUS_SOFTWARE_ALARM 0x00002000
+/*! Timer has expired. */
+#define SMN_STATUS_TIMER_ERROR 0x00001000
+/*! Plaintext/Ciphertext compare failed. */
+#define SMN_STATUS_PC_ERROR 0x00000800
+/*! Bit Bank detected overflow or underflow */
+#define SMN_STATUS_BITBANK_ERROR 0x00000400
+/*! Algorithm Sequence Check failed. */
+#define SMN_STATUS_ASC_ERROR 0x00000200
+/*! Security Policy Block detected error. */
+#define SMN_STATUS_SECURITY_POLICY_ERROR 0x00000100
+/*! At least one Debug signal is active. */
+#define SMN_STATUS_DEBUG_ACTIVE 0x00000080
+/*! SCM failed to zeroize its memory. */
+#define SMN_STATUS_ZEROIZE_FAIL 0x00000040
+/*! Processor booted from internal ROM. */
+#define SMN_STATUS_INTERNAL_BOOT 0x00000020
+/*! SMN's internal state. */
+#define SMN_STATUS_STATE_MASK 0x0000001F
+/*! Number of bits to shift #SMN_STATUS_STATE_MASK to get it to LSB. */
+#define SMN_STATUS_STATE_SHIFT 0
+/*! @} */
+
+/*!
+ * @defgroup sccscmstates SMN Model Secure State Controller States (SMN_STATE_MASK)
+ */
+/*! @addtogroup sccscmstates */
+/*! @{ */
+/*! This is the first state of the SMN after power-on reset */
+#define SMN_STATE_START 0x0
+/*! The SMN is zeroizing its RAM during reset */
+#define SMN_STATE_ZEROIZE_RAM 0x5
+/*! SMN has passed internal checks, and is waiting for Software check-in */
+#define SMN_STATE_HEALTH_CHECK 0x6
+/*! Fatal Security Violation. SMN is locked, SCM is inoperative. */
+#define SMN_STATE_FAIL 0x9
+/*! SCC is in secure state. SCM is using secret key. */
+#define SMN_STATE_SECURE 0xA
+/*! Due to non-fatal error, device is not secure. SCM is using default key. */
+#define SMN_STATE_NON_SECURE 0xC
+/*! @} */
+
+/*!
+ * @defgroup scmconfigdefs SCM Configuration Register definitions (SCM_CONFIGURATION)
+ **/
+/*! @addtogroup scmconfigdefs */
+/*! @{ */
+/*! Version number of the Secure Memory. */
+#define SCM_CFG_VERSION_ID_MASK 0xf8000000
+/*! Number of bits to shift #SCM_CFG_VERSION_ID_MASK to get it to LSB. */
+#define SCM_CFG_VERSION_ID_SHIFT 27
+/*! Version one value for SCC configuration */
+#define SCM_VERSION_1 1
+/*! Size, in blocks, of Red memory. */
+#define SCM_CFG_BLACK_SIZE_MASK 0x07fe0000
+/*! Number of bits to shift #SCM_CFG_BLACK_SIZE_MASK to get it to LSB. */
+#define SCM_CFG_BLACK_SIZE_SHIFT 17
+/*! Size, in blocks, of Black memory. */
+#define SCM_CFG_RED_SIZE_MASK 0x0001ff80
+/*! Number of bits to shift #SCM_CFG_RED_SIZE_MASK to get it to LSB. */
+#define SCM_CFG_RED_SIZE_SHIFT 7
+/*! Number of bytes per block. */
+#define SCM_CFG_BLOCK_SIZE_MASK 0x0000007f
+/*! Number of bits to shift #SCM_CFG_BLOCK_SIZE_MASK to get it to LSB. */
+#define SCM_CFG_BLOCK_SIZE_SHIFT 0
+/*! @} */
+
+/*!
+ * @defgroup smncommandregdefs SMN Command Register Definitions (SMN_COMMAND)
+ */
+/*! @addtogroup smncommandregdefs */
+/*! @{ */
+#define SMN_COMMAND_ZEROS_MASK 0xffffff70 /*!< These bits are unimplemented
+ or reserved */
+#define SMN_COMMAND_TAMPER_LOCK 0x10 /*!< Lock Tamper Detect Bit */
+#define SMN_COMMAND_CLEAR_INTERRUPT 0x8 /*!< Clear SMN Interrupt */
+#define SMN_COMMAND_CLEAR_BIT_BANK 0x4 /*!< Clear SMN Bit Bank */
+#define SMN_COMMAND_ENABLE_INTERRUPT 0x2 /*!< Enable SMN Interrupts */
+#define SMN_COMMAND_SET_SOFTWARE_ALARM 0x1 /*!< Set Software Alarm */
+/*! @} */
+
+/*!
+ * @defgroup smntimercontroldefs SMN Timer Control Register definitions (SMN_TIMER_CONTROL)
+ */
+/*! @addtogroup smntimercontroldefs */
+/*! @{ */
+/*! These bits are reserved or zero */
+#define SMN_TIMER_CTRL_ZEROS_MASK 0xfffffffc
+/*! Load the timer from #SMN_TIMER_IV */
+#define SMN_TIMER_LOAD_TIMER 0x2
+/*! Setting to zero stops the Timer */
+#define SMN_TIMER_STOP_MASK 0x1
+/*! Setting this value starts the timer */
+#define SMN_TIMER_START_TIMER 0x1
+/*! @} */
+
+/*!
+ * @defgroup scminterruptcontroldefs SCM Interrupt Control Register definitions (SCM_INTERRUPT_CTRL)
+ *
+ * These are the bit definitions for the #SCM_INTERRUPT_CTRL register.
+ */
+/*! @addtogroup scminterruptcontroldefs */
+/*! @{ */
+/*! Clear SCM memory */
+#define SCM_INTERRUPT_CTRL_ZEROIZE_MEMORY 0x4
+/*! Clear outstanding SCM interrupt */
+#define SCM_INTERRUPT_CTRL_CLEAR_INTERRUPT 0x2
+/*! Inhibit SCM interrupts */
+#define SCM_INTERRUPT_CTRL_MASK_INTERRUPTS 0x1
+/*! @} */
+
+/*!
+ * @defgroup scmcontrolregdefs SCM Control Register definitions (SCM_CONTROL).
+ * These values are used with the #SCM_CONTROL register.
+ */
+/*! @addtogroup scmcontrolregdefs */
+/*! @{ */
+/*! These bits are zero or reserved */
+#define SCM_CONTROL_ZEROS_MASK 0xfffffff8
+/*! Setting this will start encrypt/decrypt */
+#define SCM_CONTROL_START_CIPHER 0x04
+/*! CBC/ECB flag.
+ * See @ref scmchainmodedefs "Chaining Mode bit definitions."
+ */
+#define SCM_CONTROL_CHAINING_MODE_MASK 0x02
+/*! Encrypt/decrypt choice.
+ * See @ref scmciphermodedefs "Cipher Mode bit definitions." */
+#define SCM_CONTROL_CIPHER_MODE_MASK 0x01
+/*! @} */
+
+/*!
+ * @defgroup scmchainmodedefs SCM_CHAINING_MODE_MASK - Bit definitions
+ */
+/*! @addtogroup scmchainmodedefs */
+/*! @{ */
+#define SCM_CBC_MODE 0x2 /*!< Cipher block chaining */
+#define SCM_ECB_MODE 0x0 /*!< Electronic codebook. */
+/*! @} */
+
+/* Bit definitions in the SCM_CIPHER_MODE_MASK */
+/*!
+ * @defgroup scmciphermodedefs SCM_CIPHER_MODE_MASK - Bit definitions
+ */
+/*! @{ */
+#define SCM_DECRYPT_MODE 0x1 /*!< decrypt from black to red memory */
+#define SCM_ENCRYPT_MODE 0x0 /*!< encrypt from red to black memory */
+/*! @} */
+
+/*!
+ * @defgroup scmstatusregdefs SCM Status Register (SCM_STATUS).
+ * Bit and field definitions of the SCM_STATUS register.
+ */
+/*! @addtogroup scmstatusregdefs */
+/*! @{ */
+/*! These bits are zero or reserved */
+#define SCM_STATUS_ZEROS_MASK 0xffffe000
+/*! Ciphering failed due to length error. */
+#define SCM_STATUS_LENGTH_ERROR 0x1000
+/*! SMN has stopped blocking access to the SCM */
+#define SCM_STATUS_BLOCK_ACCESS_REMOVED 0x0800
+/*! Ciphering done. */
+#define SCM_STATUS_CIPHERING_DONE 0x0400
+/*! Zeroizing done. */
+#define SCM_STATUS_ZEROIZING_DONE 0x0200
+/*! SCM wants attention. Interrupt status is available. */
+#define SCM_STATUS_INTERRUPT_STATUS 0x0100
+/*! Secret Key is in use. */
+#define SCM_STATUS_SECRET_KEY 0x0080
+/*! Secret Key is in use. Deprecated. Use #SCM_STATUS_SECRET_KEY. */
+#define SCM_STATUS_DEFAULT_KEY 0x0080
+/*! Internal error to SCM. */
+#define SCM_STATUS_INTERNAL_ERROR 0x0040
+/*! Secret key is not valid. */
+#define SCM_STATUS_BAD_SECRET_KEY 0x0020
+/*! Failed to zeroize memory. */
+#define SCM_STATUS_ZEROIZE_FAILED 0x0010
+/*! SMN is blocking access to Secure Memory. */
+#define SCM_STATUS_SMN_BLOCKING_ACCESS 0x0008
+/*! SCM is current encrypting or decrypting data. */
+#define SCM_STATUS_CIPHERING 0x0004
+/*! SCM is currently zeroizing data. */
+#define SCM_STATUS_ZEROIZING 0x0002
+/*! SCM is busy and access to memory is blocked. */
+#define SCM_STATUS_BUSY 0x0001
+/*! @} */
+
+/*!
+ * @defgroup scmerrstatdefs SCM Error Status Register (SCM_ERROR_STATUS)
+ *
+ * These definitions are associated with the SCM Error Status Register
+ * (SCM_ERROR_STATUS).
+ */
+/*! @addtogroup scmerrstatdefs */
+/*! @{ */
+/*! These bits are zero or reserved */
+#define SCM_ERR_ZEROS_MASK 0xffffc000
+/*! Cacheable access to SCM was attempted */
+#define SCM_ERR_CACHEABLE_ACCESS 0x2000
+/*! Access attempted by illegal bus master */
+#define SCM_ERR_ILLEGAL_MASTER 0x1000
+/*! Unaligned access attempted */
+#define SCM_ERR_UNALIGNED_ACCESS 0x0800
+/*! Byte or half-word access attempted */
+#define SCM_ERR_BYTE_ACCESS 0x0400
+/*! Illegal address attempted */
+#define SCM_ERR_ILLEGAL_ADDRESS 0x0200
+/*! User access attempted */
+#define SCM_ERR_USER_ACCESS 0x0100
+/*! Access attempted while SCM was using default key */
+#define SCM_ERR_SECRET_KEY_IN_USE 0x0080
+/*! Access attempted while SCM had internal error */
+#define SCM_ERR_INTERNAL_ERROR 0x0040
+/*! Access attempted while SCM was detecting Bad Key */
+#define SCM_ERR_BAD_SECRET_KEY 0x0020
+/*! The SCM failed to Zeroize memory */
+#define SCM_ERR_ZEROIZE_FAILED 0x0010
+/*! Access attempted while SMN was Blocking Access */
+#define SCM_ERR_SMN_BLOCKING_ACCESS 0x0008
+/*! Access attempted while SCM was CIPHERING */
+#define SCM_ERR_CIPHERING 0x0004
+/*! Access attempted while SCM was ZEROIZING */
+#define SCM_ERR_ZEROIZING 0x0002
+/*! Access attempted while SCM was BUSY */
+#define SCM_ERR_BUSY 0x0001
+/*! @} */
+
+/*!
+ * @defgroup smndbgdetdefs SMN Debug Detector Status Register
+ * (SCM_DEBUG_DETECT_STAT)
+ */
+/*! @addtogroup smndbgdetdefs */
+/*! @{ */
+#define SMN_DBG_ZEROS_MASK 0xfffff000 /*!< These bits are zero or reserved */
+#define SMN_DBG_D12 0x0800 /*!< Error detected on Debug Port D12 */
+#define SMN_DBG_D11 0x0400 /*!< Error detected on Debug Port D11 */
+#define SMN_DBG_D10 0x0200 /*!< Error detected on Debug Port D10 */
+#define SMN_DBG_D9 0x0100 /*!< Error detected on Debug Port D9 */
+#define SMN_DBG_D8 0x0080 /*!< Error detected on Debug Port D8 */
+#define SMN_DBG_D7 0x0040 /*!< Error detected on Debug Port D7 */
+#define SMN_DBG_D6 0x0020 /*!< Error detected on Debug Port D6 */
+#define SMN_DBG_D5 0x0010 /*!< Error detected on Debug Port D5 */
+#define SMN_DBG_D4 0x0008 /*!< Error detected on Debug Port D4 */
+#define SMN_DBG_D3 0x0004 /*!< Error detected on Debug Port D3 */
+#define SMN_DBG_D2 0x0002 /*!< Error detected on Debug Port D2 */
+#define SMN_DBG_D1 0x0001 /*!< Error detected on Debug Port D1 */
+/*! @} */
+
+/*! Mask for the usable bits of the Sequence Start Register
+ (#SMN_SEQUENCE_START) */
+#define SMN_SEQUENCE_START_MASK 0x0000ffff
+
+/*! Mask for the usable bits of the Sequence End Register
+ (#SMN_SEQUENCE_END) */
+#define SMN_SEQUENCE_END_MASK 0x0000ffff
+
+/*! Mask for the usable bits of the Sequence Check Register
+ (#SMN_SEQUENCE_CHECK) */
+#define SMN_SEQUENCE_CHECK_MASK 0x0000ffff
+
+/*! Mask for the usable bits of the Bit Counter Register
+ (#SMN_BIT_COUNT) */
+#define SMN_BIT_COUNT_MASK 0x000007ff
+
+/*! Mask for the usable bits of the Bit Bank Increment Size Register
+ (#SMN_BITBANK_INC_SIZE) */
+#define SMN_BITBANK_INC_SIZE_MASK 0x000007ff
+
+/*! Mask for the usable bits of the Bit Bank Decrement Register
+ (#SMN_BITBANK_DECREMENT) */
+#define SMN_BITBANK_DECREMENT_MASK 0x000007ff
+
+/*! Mask for the usable bits of the Compare Size Register
+ (#SMN_COMPARE_SIZE) */
+#define SMN_COMPARE_SIZE_MASK 0x0000003f
+
+/* Close out marker for C++ compilers */
+#ifdef __cplusplus
+}
+#endif
+#endif /* __ASM_ARCH_MXC_SCC_DRIVER_H__ */
diff --git a/include/linux/mxc_si4702.h b/include/linux/mxc_si4702.h
new file mode 100644
index 000000000000..55d7fd08df47
--- /dev/null
+++ b/include/linux/mxc_si4702.h
@@ -0,0 +1,39 @@
+/*
+ * linux/drivers/char/mxc_si4702.h
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup Character device driver for SI4702 FM radio
+ */
+
+/*
+ * @file mxc_si4702.h
+ *
+ * @brief SI4702 Radio FM driver
+ *
+ * @ingroup Character
+ */
+
+#ifndef _MXC_SI4702_FM_H
+#define _MXC_SI4702_FM_H
+
+/* define IOCTL command */
+#define SI4702_GETVOLUME _IOR('S', 0x10, unsigned int)
+#define SI4702_SETVOLUME _IOW('S', 0x11, unsigned int)
+#define SI4702_MUTEON _IO('S', 0x12)
+#define SI4702_MUTEOFF _IO('S', 0x13)
+#define SI4702_SELECT _IOW('S', 0x14, unsigned int)
+#define SI4702_SEEK _IOWR('S', 0x15, unsigned int)
+
+#endif /* _MXC_SI4702_FM_H */
diff --git a/include/linux/mxc_sim_interface.h b/include/linux/mxc_sim_interface.h
new file mode 100644
index 000000000000..ee6e50c95675
--- /dev/null
+++ b/include/linux/mxc_sim_interface.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file mxc_sim_interface.h
+ *
+ * @brief Driver for Freescale IMX SIM interface
+ *
+ */
+
+#ifndef MXC_SIM_INTERFACE_H
+#define MXC_SIM_INTERFACE_H
+
+#define SIM_ATR_LENGTH_MAX 32
+
+/* Raw ATR SIM_IOCTL_GET_ATR */
+typedef struct {
+ uint32_t size; /* length of ATR received */
+ uint8_t t[SIM_ATR_LENGTH_MAX]; /* raw ATR string received */
+} sim_atr_t;
+
+/* Communication parameters for SIM_IOCTL_[GET|SET]_PARAM */
+typedef struct {
+ uint8_t convention; /* direct = 0, indirect = 1 */
+ uint8_t FI, DI; /* frequency multiplier and devider indices */
+ uint8_t PI1, II; /* programming voltage and current indices */
+ uint8_t N; /* extra guard time */
+ uint8_t T; /* protocol type: T0 = 0, T1 = 1 */
+ uint8_t PI2; /* programming voltage 2 value */
+ uint8_t WWT; /* working wait time */
+} sim_param_t;
+
+/* ISO7816-3 protocols */
+#define SIM_PROTOCOL_T0 0
+#define SIM_PROTOCOL_T1 1
+
+/* Transfer data for SIM_IOCTL_XFER */
+typedef struct {
+ uint8_t *xmt_buffer; /* transmit buffer pointer */
+ int32_t xmt_length; /* transmit buffer length */
+ uint8_t *rcv_buffer; /* receive buffer pointer */
+ int32_t rcv_length; /* receive buffer length */
+ int type; /* transfer type: TPDU = 0, PTS = 1 */
+ int timeout; /* transfer timeout in milliseconds */
+ uint8_t sw1; /* status word 1 */
+ uint8_t sw2; /* status word 2 */
+} sim_xfer_t;
+
+/* Transfer types for SIM_IOCTL_XFER */
+#define SIM_XFER_TYPE_TPDU 0
+#define SIM_XFER_TYPE_PTS 1
+
+/* Interface power states */
+#define SIM_POWER_OFF 0
+#define SIM_POWER_ON 1
+
+/* Return values for SIM_IOCTL_GET_PRESENSE */
+#define SIM_PRESENT_REMOVED 0
+#define SIM_PRESENT_DETECTED 1
+#define SIM_PRESENT_OPERATIONAL 2
+
+/* Return values for SIM_IOCTL_GET_ERROR */
+#define SIM_OK 0
+#define SIM_E_ACCESS 1
+#define SIM_E_TPDUSHORT 2
+#define SIM_E_PTSEMPTY 3
+#define SIM_E_INVALIDXFERTYPE 4
+#define SIM_E_INVALIDXMTLENGTH 5
+#define SIM_E_INVALIDRCVLENGTH 6
+#define SIM_E_NACK 7
+#define SIM_E_TIMEOUT 8
+#define SIM_E_NOCARD 9
+#define SIM_E_PARAM_FI_INVALID 10
+#define SIM_E_PARAM_DI_INVALID 11
+#define SIM_E_PARAM_FBYD_WITHFRACTION 12
+#define SIM_E_PARAM_FBYD_NOTDIVBY8OR12 13
+#define SIM_E_PARAM_DIVISOR_RANGE 14
+#define SIM_E_MALLOC 15
+#define SIM_E_IRQ 16
+#define SIM_E_POWERED_ON 17
+#define SIM_E_POWERED_OFF 18
+
+/* ioctl encodings */
+#define SIM_IOCTL_BASE 0xc0
+#define SIM_IOCTL_GET_PRESENSE _IOR(SIM_IOCTL_BASE, 1, int)
+#define SIM_IOCTL_GET_ATR _IOR(SIM_IOCTL_BASE, 2, sim_atr_t)
+#define SIM_IOCTL_GET_PARAM_ATR _IOR(SIM_IOCTL_BASE, 3, sim_param_t)
+#define SIM_IOCTL_GET_PARAM _IOR(SIM_IOCTL_BASE, 4, sim_param_t)
+#define SIM_IOCTL_SET_PARAM _IOW(SIM_IOCTL_BASE, 5, sim_param_t)
+#define SIM_IOCTL_XFER _IOR(SIM_IOCTL_BASE, 6, sim_xfer_t)
+#define SIM_IOCTL_POWER_ON _IO(SIM_IOCTL_BASE, 7)
+#define SIM_IOCTL_POWER_OFF _IO(SIM_IOCTL_BASE, 8)
+#define SIM_IOCTL_WARM_RESET _IO(SIM_IOCTL_BASE, 9)
+#define SIM_IOCTL_COLD_RESET _IO(SIM_IOCTL_BASE, 10)
+#define SIM_IOCTL_CARD_LOCK _IO(SIM_IOCTL_BASE, 11)
+#define SIM_IOCTL_CARD_EJECT _IO(SIM_IOCTL_BASE, 12)
+
+#endif
diff --git a/include/linux/mxc_v4l2.h b/include/linux/mxc_v4l2.h
new file mode 100644
index 000000000000..d474aa2f4c86
--- /dev/null
+++ b/include/linux/mxc_v4l2.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+/*!
+ * @file arch-mxc/mxc_v4l2.h
+ *
+ * @brief mxc V4L2 private structures
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+
+#ifndef __ASM_ARCH_MXC_V4L2_H__
+#define __ASM_ARCH_MXC_V4L2_H__
+
+/*
+ * For IPUv1 and IPUv3, V4L2_CID_MXC_ROT means encoder ioctl ID.
+ * And V4L2_CID_MXC_VF_ROT is viewfinder ioctl ID only for IPUv1 and IPUv3.
+ */
+#define V4L2_CID_MXC_ROT (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_MXC_FLASH (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_MXC_VF_ROT (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_MXC_MOTION (V4L2_CID_PRIVATE_BASE + 3)
+
+#define V4L2_MXC_ROTATE_NONE 0
+#define V4L2_MXC_ROTATE_VERT_FLIP 1
+#define V4L2_MXC_ROTATE_HORIZ_FLIP 2
+#define V4L2_MXC_ROTATE_180 3
+#define V4L2_MXC_ROTATE_90_RIGHT 4
+#define V4L2_MXC_ROTATE_90_RIGHT_VFLIP 5
+#define V4L2_MXC_ROTATE_90_RIGHT_HFLIP 6
+#define V4L2_MXC_ROTATE_90_LEFT 7
+
+struct v4l2_mxc_offset {
+ uint32_t u_offset;
+ uint32_t v_offset;
+};
+
+#endif
diff --git a/include/linux/mxcfb.h b/include/linux/mxcfb.h
new file mode 100644
index 000000000000..79c23c344dfa
--- /dev/null
+++ b/include/linux/mxcfb.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+/*
+ * @file arch-mxc/ mxcfb.h
+ *
+ * @brief Global header file for the MXC Frame buffer
+ *
+ * @ingroup Framebuffer
+ */
+#ifndef __ASM_ARCH_MXCFB_H__
+#define __ASM_ARCH_MXCFB_H__
+
+#include <linux/fb.h>
+
+#define FB_SYNC_OE_LOW_ACT 0x80000000
+#define FB_SYNC_CLK_LAT_FALL 0x40000000
+#define FB_SYNC_DATA_INVERT 0x20000000
+#define FB_SYNC_CLK_IDLE_EN 0x10000000
+#define FB_SYNC_SHARP_MODE 0x08000000
+#define FB_SYNC_SWAP_RGB 0x04000000
+
+struct mxcfb_gbl_alpha {
+ int enable;
+ int alpha;
+};
+
+struct mxcfb_loc_alpha {
+ int enable;
+ unsigned long alpha_phy_addr0;
+ unsigned long alpha_phy_addr1;
+};
+
+struct mxcfb_color_key {
+ int enable;
+ __u32 color_key;
+};
+
+struct mxcfb_pos {
+ __u16 x;
+ __u16 y;
+};
+
+#define MXCFB_WAIT_FOR_VSYNC _IOW('F', 0x20, u_int32_t)
+#define MXCFB_SET_GBL_ALPHA _IOW('F', 0x21, struct mxcfb_gbl_alpha)
+#define MXCFB_SET_CLR_KEY _IOW('F', 0x22, struct mxcfb_color_key)
+#define MXCFB_SET_OVERLAY_POS _IOWR('F', 0x24, struct mxcfb_pos)
+#define MXCFB_GET_FB_IPU_CHAN _IOR('F', 0x25, u_int32_t)
+#define MXCFB_SET_LOC_ALPHA _IOWR('F', 0x26, struct mxcfb_loc_alpha)
+#define MXCFB_SET_LOC_ALP_BUF _IOW('F', 0x27, unsigned long)
+
+#ifdef __KERNEL__
+
+extern struct fb_videomode mxcfb_modedb[];
+extern int mxcfb_modedb_sz;
+
+enum {
+ MXCFB_REFRESH_OFF,
+ MXCFB_REFRESH_AUTO,
+ MXCFB_REFRESH_PARTIAL,
+};
+
+struct mxcfb_rect {
+ u32 top;
+ u32 left;
+ u32 width;
+ u32 height;
+};
+
+int mxcfb_set_refresh_mode(struct fb_info *fbi, int mode,
+ struct mxcfb_rect *update_region);
+
+#endif /* __KERNEL__ */
+#endif
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 9bad65400fba..e86ed59f9ad5 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -3,7 +3,26 @@
/*
* 802.11 netlink interface public header
*
- * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006, 2007, 2008 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2008 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com>
+ * Copyright 2008 Michael Buesch <mb@bu3sch.de>
+ * Copyright 2008 Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+ * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
*/
/**
@@ -25,8 +44,10 @@
*
* @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request
* to get a list of all present wiphys.
- * @NL80211_CMD_SET_WIPHY: set wiphy name, needs %NL80211_ATTR_WIPHY and
- * %NL80211_ATTR_WIPHY_NAME.
+ * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
+ * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
+ * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, and/or
+ * %NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET.
* @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
* or rename notification. Has attributes %NL80211_ATTR_WIPHY and
* %NL80211_ATTR_WIPHY_NAME.
@@ -106,6 +127,12 @@
* to the the specified ISO/IEC 3166-1 alpha2 country code. The core will
* store this as a valid request and then query userspace for it.
*
+ * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the
+ * interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the
+ * interface identified by %NL80211_ATTR_IFINDEX
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -148,6 +175,9 @@ enum nl80211_commands {
NL80211_CMD_SET_REG,
NL80211_CMD_REQ_SET_REG,
+ NL80211_CMD_GET_MESH_PARAMS,
+ NL80211_CMD_SET_MESH_PARAMS,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -169,6 +199,15 @@ enum nl80211_commands {
* @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf.
* /sys/class/ieee80211/<phyname>/index
* @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
+ * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters
+ * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz
+ * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
+ * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included):
+ * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including
+ * this attribute)
+ * NL80211_CHAN_HT20 = HT20 only
+ * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
+ * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
*
* @NL80211_ATTR_IFINDEX: network interface index of the device to operate on
* @NL80211_ATTR_IFNAME: network interface name
@@ -234,6 +273,9 @@ enum nl80211_commands {
* (u8, 0 or 1)
* @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled
* (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic
+ * rates in format defined by IEEE 802.11 7.3.2.2 but without the length
+ * restriction (at most %NL80211_MAX_SUPP_RATES).
*
* @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from
* association request when used with NL80211_CMD_NEW_STATION)
@@ -296,6 +338,14 @@ enum nl80211_attrs {
NL80211_ATTR_REG_ALPHA2,
NL80211_ATTR_REG_RULES,
+ NL80211_ATTR_MESH_PARAMS,
+
+ NL80211_ATTR_BSS_BASIC_RATES,
+
+ NL80211_ATTR_WIPHY_TXQ_PARAMS,
+ NL80211_ATTR_WIPHY_FREQ,
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -307,6 +357,10 @@ enum nl80211_attrs {
* here
*/
#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY
+#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES
+#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS
+#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ
+#define NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET
#define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_REG_RULES 32
@@ -371,6 +425,32 @@ enum nl80211_sta_flags {
};
/**
+ * enum nl80211_rate_info - bitrate information
+ *
+ * These attribute types are used with %NL80211_STA_INFO_TXRATE
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
+ * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
+ * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate
+ * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
+ * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @__NL80211_RATE_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_rate_info {
+ __NL80211_RATE_INFO_INVALID,
+ NL80211_RATE_INFO_BITRATE,
+ NL80211_RATE_INFO_MCS,
+ NL80211_RATE_INFO_40_MHZ_WIDTH,
+ NL80211_RATE_INFO_SHORT_GI,
+
+ /* keep last */
+ __NL80211_RATE_INFO_AFTER_LAST,
+ NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_sta_info - station information
*
* These attribute types are used with %NL80211_ATTR_STA_INFO
@@ -382,6 +462,9 @@ enum nl80211_sta_flags {
* @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
+ * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+ * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+ * containing info as possible, see &enum nl80211_sta_info_txrate.
*/
enum nl80211_sta_info {
__NL80211_STA_INFO_INVALID,
@@ -391,6 +474,8 @@ enum nl80211_sta_info {
NL80211_STA_INFO_LLID,
NL80211_STA_INFO_PLID,
NL80211_STA_INFO_PLINK_STATE,
+ NL80211_STA_INFO_SIGNAL,
+ NL80211_STA_INFO_TX_BITRATE,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
@@ -452,17 +537,29 @@ enum nl80211_mpath_info {
* an array of nested frequency attributes
* @NL80211_BAND_ATTR_RATES: supported bitrates in this band,
* an array of nested bitrate attributes
+ * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as
+ * defined in 802.11n
+ * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n
+ * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n
*/
enum nl80211_band_attr {
__NL80211_BAND_ATTR_INVALID,
NL80211_BAND_ATTR_FREQS,
NL80211_BAND_ATTR_RATES,
+ NL80211_BAND_ATTR_HT_MCS_SET,
+ NL80211_BAND_ATTR_HT_CAPA,
+ NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+ NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+
/* keep last */
__NL80211_BAND_ATTR_AFTER_LAST,
NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
};
+#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA
+
/**
* enum nl80211_frequency_attr - frequency attributes
* @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
@@ -474,6 +571,8 @@ enum nl80211_band_attr {
* on this channel in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
* on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
+ * (100 * dBm).
*/
enum nl80211_frequency_attr {
__NL80211_FREQUENCY_ATTR_INVALID,
@@ -482,12 +581,15 @@ enum nl80211_frequency_attr {
NL80211_FREQUENCY_ATTR_PASSIVE_SCAN,
NL80211_FREQUENCY_ATTR_NO_IBSS,
NL80211_FREQUENCY_ATTR_RADAR,
+ NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1
};
+#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER
+
/**
* enum nl80211_bitrate_attr - bitrate attributes
* @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps
@@ -594,4 +696,119 @@ enum nl80211_mntr_flags {
NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1
};
+/**
+ * enum nl80211_meshconf_params - mesh configuration parameters
+ *
+ * Mesh configuration parameters
+ *
+ * @__NL80211_MESHCONF_INVALID: internal use
+ *
+ * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
+ * millisecond units, used by the Peer Link Open message
+ *
+ * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in
+ * millisecond units, used by the peer link management to close a peer link
+ *
+ * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
+ * millisecond units
+ *
+ * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed
+ * on this mesh interface
+ *
+ * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link
+ * open retries that can be sent to establish a new peer link instance in a
+ * mesh
+ *
+ * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
+ * point.
+ *
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
+ * open peer links when we detect compatible mesh peers.
+ *
+ * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
+ * containing a PREQ that an MP can send to a particular destination (path
+ * target)
+ *
+ * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths
+ * (in milliseconds)
+ *
+ * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait
+ * until giving up on a path discovery (in milliseconds)
+ *
+ * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh
+ * points receiving a PREQ shall consider the forwarding information from the
+ * root to be valid. (TU = time unit)
+ *
+ * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in
+ * TUs) during which an MP can send only one action frame containing a PREQ
+ * reference element
+ *
+ * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
+ * that it takes for an HWMP information element to propagate across the mesh
+ *
+ * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
+ *
+ * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_meshconf_params {
+ __NL80211_MESHCONF_INVALID,
+ NL80211_MESHCONF_RETRY_TIMEOUT,
+ NL80211_MESHCONF_CONFIRM_TIMEOUT,
+ NL80211_MESHCONF_HOLDING_TIMEOUT,
+ NL80211_MESHCONF_MAX_PEER_LINKS,
+ NL80211_MESHCONF_MAX_RETRIES,
+ NL80211_MESHCONF_TTL,
+ NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ NL80211_MESHCONF_PATH_REFRESH_TIME,
+ NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+
+ /* keep last */
+ __NL80211_MESHCONF_ATTR_AFTER_LAST,
+ NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_txq_attr - TX queue parameter attributes
+ * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
+ * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*)
+ * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning
+ * disabled
+ * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form
+ * 2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form
+ * 2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255]
+ * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal
+ * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number
+ */
+enum nl80211_txq_attr {
+ __NL80211_TXQ_ATTR_INVALID,
+ NL80211_TXQ_ATTR_QUEUE,
+ NL80211_TXQ_ATTR_TXOP,
+ NL80211_TXQ_ATTR_CWMIN,
+ NL80211_TXQ_ATTR_CWMAX,
+ NL80211_TXQ_ATTR_AIFS,
+
+ /* keep last */
+ __NL80211_TXQ_ATTR_AFTER_LAST,
+ NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
+};
+
+enum nl80211_txq_q {
+ NL80211_TXQ_Q_VO,
+ NL80211_TXQ_Q_VI,
+ NL80211_TXQ_Q_BE,
+ NL80211_TXQ_Q_BK
+};
+
+enum nl80211_channel_type {
+ NL80211_CHAN_NO_HT,
+ NL80211_CHAN_HT20,
+ NL80211_CHAN_HT40MINUS,
+ NL80211_CHAN_HT40PLUS
+};
#endif /* __LINUX_NL80211_H */
diff --git a/include/linux/ns9xxx-eth.h b/include/linux/ns9xxx-eth.h
new file mode 100644
index 000000000000..c83d8cd98122
--- /dev/null
+++ b/include/linux/ns9xxx-eth.h
@@ -0,0 +1,21 @@
+/*
+ * include/linux/ns9xxx-eth.h
+ *
+ * Copyright (C) 2007 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef _LINUX_NS9XXX_ETH_H
+#define _LINUX_NS9XXX_ETH_H
+
+struct plat_ns9xxx_eth {
+ unsigned int irqrx;
+ unsigned int irqtx;
+ u32 phy_mask;
+ unsigned int activityled;
+};
+
+#endif /* ifndef _LINUX_NS9XXX_ETH_H */
diff --git a/include/linux/pmic_adc.h b/include/linux/pmic_adc.h
new file mode 100644
index 000000000000..8cc87293fb3e
--- /dev/null
+++ b/include/linux/pmic_adc.h
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __ASM_ARCH_MXC_PMIC_ADC_H__
+#define __ASM_ARCH_MXC_PMIC_ADC_H__
+
+/*!
+ * @defgroup PMIC_ADC PMIC Digitizer Driver
+ * @ingroup PMIC_DRVRS
+ */
+
+/*!
+ * @file arch-mxc/pmic_adc.h
+ * @brief This is the header of PMIC ADC driver.
+ *
+ * @ingroup PMIC_ADC
+ */
+
+#include <linux/ioctl.h>
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+/*!
+ * @name IOCTL user space interface
+ */
+
+/*! @{ */
+/*!
+ * Initialize ADC.
+ * Argument type: none.
+ */
+#define PMIC_ADC_INIT _IO('p', 0xb0)
+/*!
+ * De-initialize ADC.
+ * Argument type: none.
+ */
+#define PMIC_ADC_DEINIT _IO('p', 0xb1)
+/*!
+ * Convert one channel.
+ * Argument type: pointer to t_adc_convert_param.
+ */
+#define PMIC_ADC_CONVERT _IOWR('p', 0xb2, int)
+/*!
+ * Convert one channel eight samples.
+ * Argument type: pointer to t_adc_convert_param.
+ */
+#define PMIC_ADC_CONVERT_8X _IOWR('p', 0xb3, int)
+/*!
+ * Convert multiple channels.
+ * Argument type: pointer to t_adc_convert_param.
+ */
+#define PMIC_ADC_CONVERT_MULTICHANNEL _IOWR('p', 0xb4, int)
+/*!
+ * Set touch screen operation mode.
+ * Argument type: t_touch_mode.
+ */
+#define PMIC_ADC_SET_TOUCH_MODE _IOW('p', 0xb5, int)
+/*!
+ * Get touch screen operation mode.
+ * Argument type: pointer to t_touch_mode.
+ */
+#define PMIC_ADC_GET_TOUCH_MODE _IOR('p', 0xb6, int)
+/*!
+ * Get touch screen sample.
+ * Argument type: pointer to t_touch_sample.
+ */
+#define PMIC_ADC_GET_TOUCH_SAMPLE _IOWR('p', 0xb7, int)
+/*!
+ * Get battery current.
+ * Argument type: pointer to unsigned short.
+ */
+#define PMIC_ADC_GET_BATTERY_CURRENT _IOR('p', 0xb8, int)
+/*!
+ * Activate comparator.
+ * Argument type: pointer to t_adc_comp_param.
+ */
+#define PMIC_ADC_ACTIVATE_COMPARATOR _IOW('p', 0xb9, int)
+/*!
+ * De-active comparator.
+ * Argument type: none.
+ */
+#define PMIC_ADC_DEACTIVE_COMPARATOR _IOW('p', 0xba, int)
+
+/*!
+ * Install touch screen read interface.
+ */
+#define TOUCH_SCREEN_READ_INSTALL _IOWR('D',4, int)
+/*!
+ * Remove touch screen read interface.
+ */
+#define TOUCH_SCREEN_READ_UNINSTALL _IOWR('D',5, int)
+
+/*! @{ */
+/*!
+ * @name Touch Screen minimum and maximum values
+ */
+#define TS_X_MIN 80 /*! < Minimum X */
+#define TS_Y_MIN 80 /*! < Minimum Y */
+
+#define TS_X_MAX 1000 /*! < Maximum X */
+#define TS_Y_MAX 1000 /*! < Maximum Y */
+/*! @} */
+/*!
+ * This enumeration defines input channels for PMIC ADC
+ */
+
+typedef enum {
+ BATTERY_VOLTAGE,
+ BATTERY_CURRENT,
+ CHARGE_VOLTAGE,
+ CHARGE_CURRENT,
+ APPLICATION_SUPPLY,
+ TS_X_POS1,
+ TS_X_POS2,
+ TS_Y_POS1,
+ TS_Y_POS2,
+ GEN_PURPOSE_AD4,
+ GEN_PURPOSE_AD5,
+ GEN_PURPOSE_AD6,
+ GEN_PURPOSE_AD7,
+ GEN_PURPOSE_AD8,
+ GEN_PURPOSE_AD9,
+ GEN_PURPOSE_AD10,
+ GEN_PURPOSE_AD11,
+ USB_ID,
+ LICELL,
+ RAWEXTBPLUSSENSE,
+ MPBSENSE,
+ BATSENSE,
+ GND,
+ THERMISTOR,
+ DIE_TEMP
+} t_channel;
+
+/*!
+ * This enumeration defines reason of ADC Comparator interrupt.
+ */
+typedef enum {
+ /*!
+ * Greater than WHIGH
+ */
+ GTWHIGH,
+ /*!
+ * Less than WLOW
+ */
+ LTWLOW,
+} t_comp_exception;
+
+/*!
+ * ADC comparator callback function type
+ */
+typedef void (*t_comparator_cb) (t_comp_exception reason);
+
+/*!
+ * This enumeration defines the touch screen operation modes.
+ */
+typedef enum {
+ /*!
+ * Touch Screen X position
+ */
+ TS_X_POSITION = 0,
+ /*!
+ * Touch Screen Y position
+ */
+ TS_Y_POSITION = 1,
+ /*!
+ * Pressure
+ */
+ TS_PRESSURE = 2,
+ /*!
+ * Plate X
+ */
+ TS_PLATE_X = 3,
+ /*!
+ * Plate Y
+ */
+ TS_PLATE_Y = 4,
+ /*!
+ * Standby
+ */
+ TS_STANDBY = 5,
+ /*!
+ * No touch screen, TSX1, TSX2, TSY1 and TSY2 are used as general
+ * purpose A/D inputs.
+ */
+ TS_NONE = 6,
+} t_touch_mode;
+/*!
+ * This structure is used to report touch screen value.
+ */
+typedef struct {
+/*!
+ * Touch Screen X position
+ */
+ unsigned int x_position;
+ /*!
+ * Touch Screen X position1
+ */
+ unsigned int x_position1;
+ /*!
+ * Touch Screen X position2
+ */
+ unsigned int x_position2;
+ /*!
+ * Touch Screen X position3
+ */
+ unsigned int x_position3;
+ /*!
+ * Touch Screen Y position
+ */
+ unsigned int y_position;
+ /*!
+ * Touch Screen Y position1
+ */
+ unsigned int y_position1;
+ /*!
+ * Touch Screen Y position2
+ */
+ unsigned int y_position2;
+ /*!
+ * Touch Screen Y position3
+ */
+ unsigned int y_position3;
+ /*!
+ * Touch Screen contact value
+ */
+ unsigned int contact_resistance;
+} t_touch_screen;
+
+/*!
+ * This enumeration defines ADC conversion modes.
+ */
+typedef enum {
+ /*!
+ * Sample 8 channels, 1 sample per channel
+ */
+ ADC_8CHAN_1X = 0,
+ /*!
+ * Sample 1 channel 8 times
+ */
+ ADC_1CHAN_8X,
+} t_conversion_mode;
+
+/*!
+ * This structure is used with IOCTL code \a PMIC_ADC_CONVERT,
+ * \a PMIC_ADC_CONVERT_8X and \a PMIC_ADC_CONVERT_MULTICHANNEL.
+ */
+
+typedef struct {
+ /*!
+ * channel or channels to be sampled.
+ */
+ t_channel channel;
+ /*!
+ * holds up to 16 sampling results
+ */
+ unsigned short result[16];
+} t_adc_convert_param;
+
+/*!
+ * This structure is used to activate/deactivate ADC comparator.
+ */
+typedef struct {
+ /*!
+ * wlow.
+ */
+ unsigned char wlow;
+ /*!
+ * whigh.
+ */
+ unsigned char whigh;
+ /*!
+ * channel to monitor
+ */
+ t_channel channel;
+ /*!
+ * callback function.
+ */
+ t_comparator_cb callback;
+} t_adc_comp_param;
+
+/* EXPORTED FUNCTIONS */
+
+#ifdef __KERNEL__
+/*!
+ * This function initializes all ADC registers with default values. This
+ * function also registers the interrupt events.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_init(void);
+
+/*!
+ * This function disables the ADC, de-registers the interrupt events.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_deinit(void);
+
+/*!
+ * This function triggers a conversion and returns one sampling result of one
+ * channel.
+ *
+ * @param channel The channel to be sampled
+ * @param result The pointer to the conversion result. The memory
+ * should be allocated by the caller of this function.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+
+PMIC_STATUS pmic_adc_convert(t_channel channel, unsigned short *result);
+
+/*!
+ * This function triggers a conversion and returns eight sampling results of
+ * one channel.
+ *
+ * @param channel The channel to be sampled
+ * @param result The pointer to array to store eight sampling results.
+ * The memory should be allocated by the caller of this
+ * function.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+
+PMIC_STATUS pmic_adc_convert_8x(t_channel channel, unsigned short *result);
+
+/*!
+ * This function triggers a conversion and returns sampling results of each
+ * specified channel.
+ *
+ * @param channels This input parameter is bitmap to specify channels
+ * to be sampled.
+ * @param result The pointer to array to store sampling result.
+ * The order of the result in the array is from lowest
+ * channel number to highest channel number of the
+ * sampled channels.
+ * The memory should be allocated by the caller of this
+ * function.
+ * Note that the behavior of this function might differ
+ * from one platform to another regarding especially
+ * channels order.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+
+PMIC_STATUS pmic_adc_convert_multichnnel(t_channel channels,
+ unsigned short *result);
+
+/*!
+ * This function sets touch screen operation mode.
+ *
+ * @param touch_mode Touch screen operation mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_set_touch_mode(t_touch_mode touch_mode);
+
+/*!
+ * This function retrieves the current touch screen operation mode.
+ *
+ * @param touch_mode Pointer to the retrieved touch screen operation
+ * mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_get_touch_mode(t_touch_mode * touch_mode);
+
+/*!
+ * This function retrieves the current touch screen operation mode.
+ *
+ * @param touch_sample Pointer to touch sample.
+ * @param wait Indicates if this function needs to block or not.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_get_touch_sample(t_touch_screen * ts_value, int wait);
+
+/*!
+ * This function starts a Battery Current mode conversion.
+ *
+ * @param mode Conversion mode.
+ * @param result Battery Current measurement result.
+ * if \a mode = ADC_8CHAN_1X, the result is \n
+ * result[0] = (BATTP - BATT_I) \n
+ * if \a mode = ADC_1CHAN_8X, the result is \n
+ * result[0] = BATTP \n
+ * result[1] = BATT_I \n
+ * result[2] = BATTP \n
+ * result[3] = BATT_I \n
+ * result[4] = BATTP \n
+ * result[5] = BATT_I \n
+ * result[6] = BATTP \n
+ * result[7] = BATT_I
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_get_battery_current(t_conversion_mode mode,
+ unsigned short *result);
+
+/*!
+ * This function actives the comparator. When comparator is activated and ADC
+ * is enabled, the 8th converted value will be digitally compared against the
+ * window defined by WLOW and WHIGH registers.
+ *
+ * @param low Comparison window low threshold (WLOW).
+ * @param high Comparison window high threshold (WHIGH).
+ * @param callback Callback function to be called when the converted
+ * value is beyond the comparison window. The callback
+ * function will pass a parameter of type
+ * \b t_comp_expection to indicate the reason of
+ * comparator exception.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+
+PMIC_STATUS pmic_adc_active_comparator(unsigned char low,
+ unsigned char high,
+ t_channel channel,
+ t_comparator_cb callback);
+
+/*!
+ * This function de-actives the comparator.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_adc_deactive_comparator(void);
+
+/*!
+ * This function enables the touch screen read interface.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_adc_install_ts(void);
+
+/*!
+ * This function disables the touch screen read interface.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_adc_remove_ts(void);
+
+int is_pmic_adc_ready(void);
+
+#endif /* _KERNEL */
+#endif /* __ASM_ARCH_MXC_PMIC_ADC_H__ */
diff --git a/include/linux/pmic_battery.h b/include/linux/pmic_battery.h
new file mode 100644
index 000000000000..b32136dab186
--- /dev/null
+++ b/include/linux/pmic_battery.h
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+#ifndef __ASM_ARCH_MXC_PMIC_BATTERY_H__
+#define __ASM_ARCH_MXC_PMIC_BATTERY_H__
+
+/*!
+ * @defgroup PMIC_BATTERY PMIC Battery Driver
+ * @ingroup PMIC_DRVRS
+ */
+
+/*!
+ * @file arch-mxc/pmic_battery.h
+ * @brief This is the header of PMIC Battery driver.
+ *
+ * @ingroup PMIC_BATTERY
+ */
+
+#include <linux/ioctl.h>
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+/*!
+ * @name IOCTL user space interface
+ */
+
+/*! @{ */
+/*!
+ * Enable and disable charger.
+ * Argument type: pointer to t_charger_setting
+ */
+#define PMIC_BATT_CHARGER_CONTROL _IOW('p', 0xc0, int)
+/*!
+ * Set charger configuration.
+ * Argument type: pointer to t_charger_setting
+ */
+#define PMIC_BATT_SET_CHARGER _IOW('p', 0xc1, int)
+/*!
+ * Get charger configuration.
+ * Argument type: pointer to t_charger_setting
+ */
+#define PMIC_BATT_GET_CHARGER _IOR('p', 0xc2, int)
+/*!
+ * Get charger current.
+ * Argument type: pointer to t_charger_setting
+ */
+#define PMIC_BATT_GET_CHARGER_CURRENT _IOR('p', 0xc3, int)
+/*!
+ * Set EOL control
+ * Argument type: pointer to t_eol_setting
+ */
+#define PMIC_BATT_EOL_CONTROL _IOW('p', 0xc4, int)
+/*!
+ * Enable and disable charging LED.
+ * Argument type: bool
+ */
+#define PMIC_BATT_LED_CONTROL _IOW('p', 0xc5, int)
+/*!
+ * Enable and disable reverse supply.
+ * Argument type: bool
+ */
+#define PMIC_BATT_REV_SUPP_CONTROL _IOW('p', 0xc6, int)
+/*!
+ * Enable and disable unregulated charging mode.
+ * Argument type: bool
+ */
+#define PMIC_BATT_UNREG_CONTROL _IOW('p', 0xc7, int)
+
+/*!
+ * Set the output controls.
+ * Argument type: t_control
+ */
+#define PMIC_BATT_SET_OUT_CONTROL _IOW('p', 0xc8, int)
+/*!
+ * Set the over voltage threshold.
+ * Argument type: int
+ */
+#define PMIC_BATT_SET_THRESHOLD _IOW('p', 0xc9, int)
+
+/*!
+ * Get the charger voltage.
+ * Argument type: int
+ */
+#define PMIC_BATT_GET_CHARGER_VOLTAGE _IOR('p', 0xca, int)
+/*!
+ * Get the battery voltage.
+ * Argument type: int
+ */
+#define PMIC_BATT_GET_BATTERY_VOLTAGE _IOR('p', 0xcb, int)
+/*!
+ * Get the battery current.
+ * Argument type: int
+ */
+#define PMIC_BATT_GET_BATTERY_CURRENT _IOR('p', 0xcc, int)
+/*!
+ * Get the charger sensor.
+ * Argument type: int
+ */
+#define PMIC_BATT_GET_CHARGER_SENSOR _IOR('p', 0xcd, int)
+/*!
+ * Get the battery temperature.
+ * Argument type: int
+ */
+#define PMIC_BATT_GET_BATTERY_TEMPERATURE _IOR('p', 0xce, int)
+/*! @} */
+
+/*!
+ * This enumeration defines battery chargers.
+ */
+typedef enum {
+ BATT_MAIN_CHGR = 0, /*!< Main battery charger */
+ BATT_CELL_CHGR, /*!< Cell battery charger */
+ BATT_TRCKLE_CHGR /*!< Trickle charger (only available on mc13783) */
+} t_batt_charger;
+
+/*!
+ * This enumeration defines the bp threshold.
+ */
+typedef enum {
+ BATT_BP_0 = 0, /*!< LOBATL UVDET + 0.2 */
+ BATT_BP_1, /*!< LOBATL UVDET + 0.3 */
+ BATT_BP_2, /*!< LOBATL UVDET + 0.4 */
+ BATT_BP_3 /*!< LOBATL UVDET + 0.5 */
+} t_bp_threshold;
+
+/*!
+ * This enumeration of all types of output controls
+ */
+typedef enum {
+ /*!
+ * controlled hardware
+ */
+ CONTROL_HARDWARE = 0,
+ /*!
+ * BPFET is driven low, BATTFET is driven high
+ */
+ CONTROL_BPFET_LOW,
+ /*!
+ * BPFET is driven high, BATTFET is driven low
+ */
+ CONTROL_BPFET_HIGH,
+} t_control;
+
+/*!
+ * This enumeration define all battery interrupt
+ */
+typedef enum {
+ /*!
+ * Charge detection interrupt
+ */
+ BAT_IT_CHG_DET,
+ /*!
+ * Charge over voltage detection it
+ */
+ BAT_IT_CHG_OVERVOLT,
+ /*!
+ * Charge path reverse current it
+ */
+ BAT_IT_CHG_REVERSE,
+ /*!
+ * Charge path short circuitin revers supply mode it
+ */
+ BAT_IT_CHG_SHORT_CIRCUIT,
+ /*!
+ * Charger has switched its mode (CC to CV or CV to CC)
+ */
+ BAT_IT_CCCV,
+ /*!
+ * Charge current has dropped below its threshold
+ */
+ BAT_IT_BELOW_THRESHOLD,
+} t_batt_event;
+
+/*!
+ * This structure is used for the following battery changer control
+ * IOCTLs:
+ * - PMIC_BATT_CHARGER_CONTROL
+ * - PMIC_BATT_SET_CHARGER
+ * - PMIC_BATT_GET_CHARGER
+ */
+typedef struct {
+ /*!
+ * Charger
+ */
+ t_batt_charger chgr;
+ /*!
+ * Turn on charger
+ */
+ bool on;
+ /*!
+ * Charging voltage
+ */
+ unsigned char c_voltage;
+ /*!
+ * Charging current
+ */
+ unsigned char c_current;
+} t_charger_setting;
+
+/*!
+ * This structure is used for EOL setting IOCTL PMIC_BATT_EOL_CONTROL
+ */
+typedef struct {
+ /*!
+ * Enable EOL comparator
+ */
+ bool enable;
+ /*!
+ * c_voltage threshold - Used on SC55112
+ */
+ unsigned char threshold;
+ /*!
+ * bp threshold - Used on mc13783
+ */
+ t_bp_threshold typical;
+} t_eol_setting;
+
+/* EXPORTED FUNCTIONS */
+
+#ifdef __KERNEL__
+
+/*START: for 3ds hw event*/
+/*!
+ * Battery event type enum
+ */
+enum {
+ BAT_EVENT_CHARGER_PLUG = 0x01,
+ BAT_EVENT_CHARGER_UNPLUG = 0x02,
+ BAT_EVENT_CHARGER_OVERVOLTAGE = 0x04,
+ BAT_EVENT_BATTERY_LOW = 0x08,
+ BAT_EVENT_POWER_FAILED = 0x10,
+ BAT_EVENT_CHARGER_FULL = 0x20,
+} t_bat_event;
+/*END: for 3ds hw event*/
+
+/*!
+ * This function is used to start charging a battery. For different charger,
+ * different c_voltage and current range are supported. \n
+ *
+ *
+ * @param chgr Charger as defined in \b t_batt_charger.
+ * @param c_voltage Charging voltage.
+ * @param c_current Charging current.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_enable_charger(t_batt_charger chgr,
+ unsigned char c_voltage,
+ unsigned char c_current);
+
+/*!
+ * This function turns off a charger.
+ *
+ * @param chgr Charger as defined in \b t_batt_charger.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_disable_charger(t_batt_charger chgr);
+
+/*!
+ * This function is used to change the charger setting.
+ *
+ * @param chgr Charger as defined in \b t_batt_charger.
+ * @param c_voltage Charging voltage.
+ * @param c_current Charging current.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_charger(t_batt_charger chgr,
+ unsigned char c_voltage,
+ unsigned char c_current);
+
+/*!
+ * This function is used to retrieve the charger setting.
+ *
+ * @param chgr Charger as defined in \b t_batt_charger.
+ * @param c_voltage Output parameter for charging c_voltage setting.
+ * @param c_current Output parameter for charging current setting.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_get_charger_setting(t_batt_charger chgr,
+ unsigned char *c_voltage,
+ unsigned char *c_current);
+
+/*!
+ * This function is retrieves the main battery charging current.
+ *
+ * @param c_current Output parameter for charging current setting.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+
+PMIC_STATUS pmic_batt_get_charge_current(unsigned short *c_current);
+
+/*!
+ * This function enables End-of-Life comparator.
+ *
+ * @param threshold End-of-Life threshold.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_enable_eol(unsigned char threshold);
+
+/*!
+ * This function enables End-of-Life comparator.
+ *
+ * @param typical Falling Edge Threshold threshold.
+ * @verbatim
+ BPDET UVDET LOBATL
+ ____ _____ ___________
+ 0 2.6 UVDET + 0.2
+ 1 2.6 UVDET + 0.3
+ 2 2.6 UVDET + 0.4
+ 3 2.6 UVDET + 0.5
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_bp_enable_eol(t_bp_threshold typical);
+
+/*!
+ * This function disables End-of-Life comparator.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_disable_eol(void);
+
+/*!
+ * This function sets the output controls.
+ * It sets the FETOVRD and FETCTRL bits of mc13783
+ *
+ * @param control type of control.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_set_out_control(t_control control);
+
+/*!
+ * This function sets over voltage threshold.
+ *
+ * @param threshold value of over voltage threshold.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_set_threshold(int threshold);
+
+/*!
+ * This function controls charge LED.
+ *
+ * @param on If on is true, LED will be turned on,
+ * or otherwise, LED will be turned off.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_led_control(bool on);
+
+/*!
+ * This function sets reverse supply mode.
+ *
+ * @param enable If enable is true, reverse supply mode is enable,
+ * or otherwise, reverse supply mode is disabled.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_reverse_supply(bool enable);
+
+/*!
+ * This function sets unregulated charging mode on main battery.
+ *
+ * @param enable If enable is true, unregulated charging mode is
+ * enable, or otherwise, disabled.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_unregulated(bool enable);
+
+/*!
+ * This function sets a 5K pull down at CHRGRAW.
+ * To be used in the dual path charging configuration.
+ *
+ * @param enable If enable is true, 5k pull down is
+ * enable, or otherwise, disabled.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_batt_set_5k_pull(bool enable);
+
+/*!
+ * This function is used to subscribe on battery event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_event_subscribe(t_batt_event event, void *callback);
+
+/*!
+ * This function is used to un subscribe on battery event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_batt_event_unsubscribe(t_batt_event event, void *callback);
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_ARCH_MXC_PMIC_BATTERY_H__ */
diff --git a/include/linux/pmic_external.h b/include/linux/pmic_external.h
new file mode 100644
index 000000000000..96d61b740666
--- /dev/null
+++ b/include/linux/pmic_external.h
@@ -0,0 +1,1108 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 __ASM_ARCH_MXC_PMIC_EXTERNAL_H__
+#define __ASM_ARCH_MXC_PMIC_EXTERNAL_H__
+
+#ifdef __KERNEL__
+#include <linux/list.h>
+#endif
+
+/*!
+ * @defgroup PMIC_DRVRS PMIC Drivers
+ */
+
+/*!
+ * @defgroup PMIC_CORE PMIC Protocol Drivers
+ * @ingroup PMIC_DRVRS
+ */
+
+/*!
+ * @file arch-mxc/pmic_external.h
+ * @brief This file contains interface of PMIC protocol driver.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+#include <linux/ioctl.h>
+#include <linux/pmic_status.h>
+
+/*!
+ * This is the enumeration of versions of PMIC
+ */
+typedef enum {
+ PMIC_MC13783 = 1, /*!< MC13783 */
+ PMIC_SC55112 = 2, /*!< SC55112 */
+ PMIC_MC13892 = 3,
+ PMIC_MC34704 = 4
+} pmic_id_t;
+
+/*!
+ * @struct pmic_version_t
+ * @brief PMIC version and revision
+ */
+typedef struct {
+ /*!
+ * PMIC version identifier.
+ */
+ pmic_id_t id;
+ /*!
+ * Revision of the PMIC.
+ */
+ int revision;
+} pmic_version_t;
+
+/*!
+ * struct pmic_event_callback_t
+ * @brief This structure contains callback function pointer and its
+ * parameter to be used when un/registering and launching a callback
+ * for an event.
+ */
+typedef struct {
+ /*!
+ * call back function
+ */
+ void (*func) (void *);
+
+ /*!
+ * call back function parameter
+ */
+ void *param;
+} pmic_event_callback_t;
+
+/*!
+ * This structure is used with IOCTL.
+ * It defines register, register value, register mask and event number
+ */
+typedef struct {
+ /*!
+ * register number
+ */
+ int reg;
+ /*!
+ * value of register
+ */
+ unsigned int reg_value;
+ /*!
+ * mask of bits, only used with PMIC_WRITE_REG
+ */
+ unsigned int reg_mask;
+} register_info;
+
+/*!
+ * @name IOCTL definitions for sc55112 core driver
+ */
+/*! @{ */
+/*! Read a PMIC register */
+#define PMIC_READ_REG _IOWR('P', 0xa0, register_info*)
+/*! Write a PMIC register */
+#define PMIC_WRITE_REG _IOWR('P', 0xa1, register_info*)
+/*! Subscribe a PMIC interrupt event */
+#define PMIC_SUBSCRIBE _IOR('P', 0xa2, int)
+/*! Unsubscribe a PMIC interrupt event */
+#define PMIC_UNSUBSCRIBE _IOR('P', 0xa3, int)
+/*! Subscribe a PMIC event for user notification*/
+#define PMIC_NOTIFY_USER _IOR('P', 0xa4, int)
+/*! Get the PMIC event occured for which user recieved notification */
+#define PMIC_GET_NOTIFY _IOW('P', 0xa5, int)
+/*! @} */
+
+/*!
+ * This is PMIC registers valid bits
+ */
+#define PMIC_ALL_BITS 0xFFFFFF
+#define PMIC_MAX_EVENTS 48
+
+#define PMIC_ARBITRATION "NULL"
+
+#ifdef CONFIG_MXC_PMIC_MC13783
+/*!
+ * This is the enumeration of register names of MC13783
+ */
+typedef enum {
+ /*!
+ * REG_INTERRUPT_STATUS_0
+ */
+ REG_INTERRUPT_STATUS_0 = 0,
+ /*!
+ * REG_INTERRUPT_MASK_0
+ */
+ REG_INTERRUPT_MASK_0,
+ /*!
+ * REG_INTERRUPT_SENSE_0
+ */
+ REG_INTERRUPT_SENSE_0,
+ /*!
+ * REG_INTERRUPT_STATUS_1
+ */
+ REG_INTERRUPT_STATUS_1,
+ /*!
+ * REG_INTERRUPT_MASK_1
+ */
+ REG_INTERRUPT_MASK_1,
+ /*!
+ * REG_INTERRUPT_SENSE_1
+ */
+ REG_INTERRUPT_SENSE_1,
+ /*!
+ * REG_POWER_UP_MODE_SENSE
+ */
+ REG_POWER_UP_MODE_SENSE,
+ /*!
+ * REG_REVISION
+ */
+ REG_REVISION,
+ /*!
+ * REG_SEMAPHORE
+ */
+ REG_SEMAPHORE,
+ /*!
+ * REG_ARBITRATION_PERIPHERAL_AUDIO
+ */
+ REG_ARBITRATION_PERIPHERAL_AUDIO,
+ /*!
+ * REG_ARBITRATION_SWITCHERS
+ */
+ REG_ARBITRATION_SWITCHERS,
+ /*!
+ * REG_ARBITRATION_REGULATORS_0
+ */
+ REG_ARBITRATION_REGULATORS_0,
+ /*!
+ * REG_ARBITRATION_REGULATORS_1
+ */
+ REG_ARBITRATION_REGULATORS_1,
+ /*!
+ * REG_POWER_CONTROL_0
+ */
+ REG_POWER_CONTROL_0,
+ /*!
+ * REG_POWER_CONTROL_1
+ */
+ REG_POWER_CONTROL_1,
+ /*!
+ * REG_POWER_CONTROL_2
+ */
+ REG_POWER_CONTROL_2,
+ /*!
+ * REG_REGEN_ASSIGNMENT
+ */
+ REG_REGEN_ASSIGNMENT,
+ /*!
+ * REG_CONTROL_SPARE
+ */
+ REG_CONTROL_SPARE,
+ /*!
+ * REG_MEMORY_A
+ */
+ REG_MEMORY_A,
+ /*!
+ * REG_MEMORY_B
+ */
+ REG_MEMORY_B,
+ /*!
+ * REG_RTC_TIME
+ */
+ REG_RTC_TIME,
+ /*!
+ * REG_RTC_ALARM
+ */
+ REG_RTC_ALARM,
+ /*!
+ * REG_RTC_DAY
+ */
+ REG_RTC_DAY,
+ /*!
+ * REG_RTC_DAY_ALARM
+ */
+ REG_RTC_DAY_ALARM,
+ /*!
+ * REG_SWITCHERS_0
+ */
+ REG_SWITCHERS_0,
+ /*!
+ * REG_SWITCHERS_1
+ */
+ REG_SWITCHERS_1,
+ /*!
+ * REG_SWITCHERS_2
+ */
+ REG_SWITCHERS_2,
+ /*!
+ * REG_SWITCHERS_3
+ */
+ REG_SWITCHERS_3,
+ /*!
+ * REG_SWITCHERS_4
+ */
+ REG_SWITCHERS_4,
+ /*!
+ * REG_SWITCHERS_5
+ */
+ REG_SWITCHERS_5,
+ /*!
+ * REG_REGULATOR_SETTING_0
+ */
+ REG_REGULATOR_SETTING_0,
+ /*!
+ * REG_REGULATOR_SETTING_1
+ */
+ REG_REGULATOR_SETTING_1,
+ /*!
+ * REG_REGULATOR_MODE_0
+ */
+ REG_REGULATOR_MODE_0,
+ /*!
+ * REG_REGULATOR_MODE_1
+ */
+ REG_REGULATOR_MODE_1,
+ /*!
+ * REG_POWER_MISCELLANEOUS
+ */
+ REG_POWER_MISCELLANEOUS,
+ /*!
+ * REG_POWER_SPARE
+ */
+ REG_POWER_SPARE,
+ /*!
+ * REG_AUDIO_RX_0
+ */
+ REG_AUDIO_RX_0,
+ /*!
+ * REG_AUDIO_RX_1
+ */
+ REG_AUDIO_RX_1,
+ /*!
+ * REG_AUDIO_TX
+ */
+ REG_AUDIO_TX,
+ /*!
+ * REG_AUDIO_SSI_NETWORK
+ */
+ REG_AUDIO_SSI_NETWORK,
+ /*!
+ * REG_AUDIO_CODEC
+ */
+ REG_AUDIO_CODEC,
+ /*!
+ * REG_AUDIO_STEREO_DAC
+ */
+ REG_AUDIO_STEREO_DAC,
+ /*!
+ * REG_AUDIO_SPARE
+ */
+ REG_AUDIO_SPARE,
+ /*!
+ * REG_ADC_0
+ */
+ REG_ADC_0,
+ /*!
+ * REG_ADC_1
+ */
+ REG_ADC_1,
+ /*!
+ * REG_ADC_2
+ */
+ REG_ADC_2,
+ /*!
+ * REG_ADC_3
+ */
+ REG_ADC_3,
+ /*!
+ * REG_ADC_4
+ */
+ REG_ADC_4,
+ /*!
+ * REG_CHARGER
+ */
+ REG_CHARGER,
+ /*!
+ * REG_USB
+ */
+ REG_USB,
+ /*!
+ * REG_CHARGE_USB_SPARE
+ */
+ REG_CHARGE_USB_SPARE,
+ /*!
+ * REG_LED_CONTROL_0
+ */
+ REG_LED_CONTROL_0,
+ /*!
+ * REG_LED_CONTROL_1
+ */
+ REG_LED_CONTROL_1,
+ /*!
+ * REG_LED_CONTROL_2
+ */
+ REG_LED_CONTROL_2,
+ /*!
+ * REG_LED_CONTROL_3
+ */
+ REG_LED_CONTROL_3,
+ /*!
+ * REG_LED_CONTROL_4
+ */
+ REG_LED_CONTROL_4,
+ /*!
+ * REG_LED_CONTROL_5
+ */
+ REG_LED_CONTROL_5,
+ /*!
+ * REG_SPARE
+ */
+ REG_SPARE,
+ /*!
+ * REG_TRIM_0
+ */
+ REG_TRIM_0,
+ /*!
+ * REG_TRIM_1
+ */
+ REG_TRIM_1,
+ /*!
+ * REG_TEST_0
+ */
+ REG_TEST_0,
+ /*!
+ * REG_TEST_1
+ */
+ REG_TEST_1,
+ /*!
+ * REG_TEST_2
+ */
+ REG_TEST_2,
+ /*!
+ * REG_TEST_3
+ */
+ REG_TEST_3,
+ /*!
+ * REG_NB
+ */
+ REG_NB,
+} pmic_reg;
+
+/*!
+ * This is event list of mc13783 interrupt
+ */
+
+typedef enum {
+ /*!
+ * ADC has finished requested conversions
+ */
+ EVENT_ADCDONEI = 0,
+ /*!
+ * ADCBIS has finished requested conversions
+ */
+ EVENT_ADCBISDONEI = 1,
+ /*!
+ * Touchscreen wakeup
+ */
+ EVENT_TSI = 2,
+ /*!
+ * ADC reading above high limit
+ */
+ EVENT_WHIGHI = 3,
+ /*!
+ * ADC reading below low limit
+ */
+ EVENT_WLOWI = 4,
+ /*!
+ * Charger attach and removal
+ */
+ EVENT_CHGDETI = 6,
+ /*!
+ * Charger over-voltage detection
+ */
+ EVENT_CHGOVI = 7,
+ /*!
+ * Charger path reverse current
+ */
+ EVENT_CHGREVI = 8,
+ /*!
+ * Charger path short circuit
+ */
+ EVENT_CHGSHORTI = 9,
+ /*!
+ * BP regulator current or voltage regulation
+ */
+ EVENT_CCCVI = 10,
+ /*!
+ * Charge current below threshold
+ */
+ EVENT_CHRGCURRI = 11,
+ /*!
+ * BP turn on threshold detection
+ */
+ EVENT_BPONI = 12,
+ /*!
+ * End of life / low battery detect
+ */
+ EVENT_LOBATLI = 13,
+ /*!
+ * Low battery warning
+ */
+ EVENT_LOBATHI = 14,
+ /*!
+ * USB detect
+ */
+ EVENT_USBI = 16,
+ /*!
+ * USB ID Line detect
+ */
+ EVENT_IDI = 19,
+ /*!
+ * Single ended 1 detect
+ */
+ EVENT_SE1I = 21,
+ /*!
+ * Car-kit detect
+ */
+ EVENT_CKDETI = 22,
+ /*!
+ * 1 Hz time-tick
+ */
+ EVENT_E1HZI = 24,
+ /*!
+ * Time of day alarm
+ */
+ EVENT_TODAI = 25,
+ /*!
+ * ON1B event
+ */
+ EVENT_ONOFD1I = 27,
+ /*!
+ * ON2B event
+ */
+ EVENT_ONOFD2I = 28,
+ /*!
+ * ON3B event
+ */
+ EVENT_ONOFD3I = 29,
+ /*!
+ * System reset
+ */
+ EVENT_SYSRSTI = 30,
+ /*!
+ * RTC reset occurred
+ */
+ EVENT_RTCRSTI = 31,
+ /*!
+ * Power cut event
+ */
+ EVENT_PCI = 32,
+ /*!
+ * Warm start event
+ */
+ EVENT_WARMI = 33,
+ /*!
+ * Memory hold event
+ */
+ EVENT_MEMHLDI = 34,
+ /*!
+ * Power ready
+ */
+ EVENT_PWRRDYI = 35,
+ /*!
+ * Thermal warning lower threshold
+ */
+ EVENT_THWARNLI = 36,
+ /*!
+ * Thermal warning higher threshold
+ */
+ EVENT_THWARNHI = 37,
+ /*!
+ * Clock source change
+ */
+ EVENT_CLKI = 38,
+ /*!
+ * Semaphore
+ */
+ EVENT_SEMAFI = 39,
+ /*!
+ * Microphone bias 2 detect
+ */
+ EVENT_MC2BI = 41,
+ /*!
+ * Headset attach
+ */
+ EVENT_HSDETI = 42,
+ /*!
+ * Stereo headset detect
+ */
+ EVENT_HSLI = 43,
+ /*!
+ * Thermal shutdown ALSP
+ */
+ EVENT_ALSPTHI = 44,
+ /*!
+ * Short circuit on AHS outputs
+ */
+ EVENT_AHSSHORTI = 45,
+ /*!
+ * number of event
+ */
+ EVENT_NB,
+} type_event;
+
+/*!
+ * This enumeration all senses of MC13783.
+ */
+typedef enum {
+ /*!
+ * Charger attach sense
+ */
+ SENSE_CHGDETS = 6,
+ /*!
+ * Charger over-voltage sense
+ */
+ SENSE_CHGOVS,
+ /*!
+ * Charger reverse current
+ * If 1 current flows into phone
+ */
+ SENSE_CHGREVS,
+ /*!
+ * Charger short circuit
+ */
+ SENSE_CHGSHORTS,
+ /*!
+ * Charger regulator operating mode
+ */
+ SENSE_CCCVS,
+ /*!
+ * Charger current below threshold
+ */
+ SENSE_CHGCURRS,
+ /*!
+ * BP turn on
+ */
+ SENSE_BPONS,
+ /*!
+ * Low bat detect
+ */
+ SENSE_LOBATLS,
+ /*!
+ * Low bat warning
+ */
+ SENSE_LOBATHS,
+ /*!
+ * UDPS
+ */
+ SENSE_UDPS,
+ /*!
+ * USB 4V4
+ */
+ SENSE_USB4V4S,
+ /*!
+ * USB 2V0
+ */
+ SENSE_USB2V0S,
+ /*!
+ * USB 0V8
+ */
+ SENSE_USB0V8S,
+ /*!
+ * ID Floats
+ */
+ SENSE_ID_FLOATS,
+ /*!
+ * ID Gnds
+ */
+ SENSE_ID_GNDS,
+ /*!
+ * Single ended
+ */
+ SENSE_SE1S,
+ /*!
+ * Car-kit detect
+ */
+ SENSE_CKDETS,
+ /*!
+ * UDMS
+ */
+ SENSE_UDMS,
+ /*!
+ * mic bias detect
+ */
+ SENSE_MC2BS,
+ /*!
+ * headset attached
+ */
+ SENSE_HSDETS,
+ /*!
+ * ST headset attached
+ */
+ SENSE_HSLS,
+ /*!
+ * Thermal shutdown ALSP
+ */
+ SENSE_ALSPTHS,
+ /*!
+ * short circuit on AHS
+ */
+ SENSE_AHSSHORTS,
+ /*!
+ * ON1B pin is hight
+ */
+ SENSE_ONOFD1S,
+ /*!
+ * ON2B pin is hight
+ */
+ SENSE_ONOFD2S,
+ /*!
+ * ON3B pin is hight
+ */
+ SENSE_ONOFD3S,
+ /*!
+ * System reset power ready
+ */
+ SENSE_PWRRDYS,
+ /*!
+ * Thermal warning higher threshold
+ */
+ SENSE_THWARNHS,
+ /*!
+ * Thermal warning lower threshold
+ */
+ SENSE_THWARNLS,
+ /*!
+ * Clock source is XTAL
+ */
+ SENSE_CLKS,
+} t_sensor;
+
+/*!
+ * This structure is used to read all sense bits of MC13783.
+ */
+typedef struct {
+ /*!
+ * Charger attach sense
+ */
+ bool sense_chgdets;
+ /*!
+ * Charger over-voltage sense
+ */
+ bool sense_chgovs;
+ /*!
+ * Charger reverse current
+ * If 1 current flows into phone
+ */
+ bool sense_chgrevs;
+ /*!
+ * Charger short circuit
+ */
+ bool sense_chgshorts;
+ /*!
+ * Charger regulator operating mode
+ */
+ bool sense_cccvs;
+ /*!
+ * Charger current below threshold
+ */
+ bool sense_chgcurrs;
+ /*!
+ * BP turn on
+ */
+ bool sense_bpons;
+ /*!
+ * Low bat detect
+ */
+ bool sense_lobatls;
+ /*!
+ * Low bat warning
+ */
+ bool sense_lobaths;
+ /*!
+ * USB 4V4
+ */
+ bool sense_usb4v4s;
+ /*!
+ * USB 2V0
+ */
+ bool sense_usb2v0s;
+ /*!
+ * USB 0V8
+ */
+ bool sense_usb0v8s;
+ /*!
+ * ID Floats
+ */
+ bool sense_id_floats;
+ /*!
+ * ID Gnds
+ */
+ bool sense_id_gnds;
+ /*!
+ * Single ended
+ */
+ bool sense_se1s;
+ /*!
+ * Car-kit detect
+ */
+ bool sense_ckdets;
+ /*!
+ * mic bias detect
+ */
+ bool sense_mc2bs;
+ /*!
+ * headset attached
+ */
+ bool sense_hsdets;
+ /*!
+ * ST headset attached
+ */
+ bool sense_hsls;
+ /*!
+ * Thermal shutdown ALSP
+ */
+ bool sense_alspths;
+ /*!
+ * short circuit on AHS
+ */
+ bool sense_ahsshorts;
+ /*!
+ * ON1B pin is hight
+ */
+ bool sense_onofd1s;
+ /*!
+ * ON2B pin is hight
+ */
+ bool sense_onofd2s;
+ /*!
+ * ON3B pin is hight
+ */
+ bool sense_onofd3s;
+ /*!
+ * System reset power ready
+ */
+ bool sense_pwrrdys;
+ /*!
+ * Thermal warning higher threshold
+ */
+ bool sense_thwarnhs;
+ /*!
+ * Thermal warning lower threshold
+ */
+ bool sense_thwarnls;
+ /*!
+ * Clock source is XTAL
+ */
+ bool sense_clks;
+} t_sensor_bits;
+
+#endif /*CONFIG_MXC_PMIC_MC13783 */
+
+#if defined(CONFIG_MXC_PMIC_MC13892_MODULE) || defined(CONFIG_MXC_PMIC_MC13892)
+enum {
+ REG_INT_STATUS0 = 0,
+ REG_INT_MASK0,
+ REG_INT_SENSE0,
+ REG_INT_STATUS1,
+ REG_INT_MASK1,
+ REG_INT_SENSE1,
+ REG_PU_MODE_S,
+ REG_IDENTIFICATION,
+ REG_UNUSED0,
+ REG_ACC0,
+ REG_ACC1, /*10 */
+ REG_UNUSED1,
+ REG_UNUSED2,
+ REG_POWER_CTL0,
+ REG_POWER_CTL1,
+ REG_POWER_CTL2,
+ REG_REGEN_ASSIGN,
+ REG_UNUSED3,
+ REG_MEM_A,
+ REG_MEM_B,
+ REG_RTC_TIME, /*20 */
+ REG_RTC_ALARM,
+ REG_RTC_DAY,
+ REG_RTC_DAY_ALARM,
+ REG_SW_0,
+ REG_SW_1,
+ REG_SW_2,
+ REG_SW_3,
+ REG_SW_4,
+ REG_SW_5,
+ REG_SETTING_0, /*30 */
+ REG_SETTING_1,
+ REG_MODE_0,
+ REG_MODE_1,
+ REG_POWER_MISC,
+ REG_UNUSED4,
+ REG_UNUSED5,
+ REG_UNUSED6,
+ REG_UNUSED7,
+ REG_UNUSED8,
+ REG_UNUSED9, /*40 */
+ REG_UNUSED10,
+ REG_UNUSED11,
+ REG_ADC0,
+ REG_ADC1,
+ REG_ADC2,
+ REG_ADC3,
+ REG_ADC4,
+ REG_CHARGE,
+ REG_USB0,
+ REG_USB1, /*50 */
+ REG_LED_CTL0,
+ REG_LED_CTL1,
+ REG_LED_CTL2,
+ REG_LED_CTL3,
+ REG_UNUSED12,
+ REG_UNUSED13,
+ REG_TRIM0,
+ REG_TRIM1,
+ REG_TEST0,
+ REG_TEST1, /*60 */
+ REG_TEST2,
+ REG_TEST3,
+ REG_TEST4,
+};
+
+typedef enum {
+ EVENT_ADCDONEI = 0,
+ EVENT_ADCBISDONEI = 1,
+ EVENT_TSI = 2,
+ EVENT_VBUSVI = 3,
+ EVENT_IDFACI = 4,
+ EVENT_USBOVI = 5,
+ EVENT_CHGDETI = 6,
+ EVENT_CHGFAULTI = 7,
+ EVENT_CHGREVI = 8,
+ EVENT_CHGRSHORTI = 9,
+ EVENT_CCCVI = 10,
+ EVENT_CHGCURRI = 11,
+ EVENT_BPONI = 12,
+ EVENT_LOBATLI = 13,
+ EVENT_LOBATHI = 14,
+ EVENT_IDFLOATI = 19,
+ EVENT_IDGNDI = 20,
+ EVENT_SE1I = 21,
+ EVENT_CKDETI = 22,
+ EVENT_1HZI = 24,
+ EVENT_TODAI = 25,
+ EVENT_PWRONI = 27,
+ EVENT_WDIRESETI = 29,
+ EVENT_SYSRSTI = 30,
+ EVENT_RTCRSTI = 31,
+ EVENT_PCI = 32,
+ EVENT_WARMI = 33,
+ EVENT_MEMHLDI = 34,
+ EVENT_THWARNLI = 36,
+ EVENT_THWARNHI = 37,
+ EVENT_CLKI = 38,
+ EVENT_SCPI = 40,
+ EVENT_LBPI = 44,
+ EVENT_NB,
+} type_event;
+
+typedef enum {
+ SENSE_VBUSVS = 3,
+ SENSE_IDFACS = 4,
+ SENSE_USBOVS = 5,
+ SENSE_CHGDETS = 6,
+ SENSE_CHGREVS = 8,
+ SENSE_CHGRSHORTS = 9,
+ SENSE_CCCVS = 10,
+ SENSE_CHGCURRS = 11,
+ SENSE_BPONS = 12,
+ SENSE_LOBATLS = 13,
+ SENSE_LOBATHS = 14,
+ SENSE_IDFLOATS = 19,
+ SENSE_IDGNDS = 20,
+ SENSE_SE1S = 21,
+ SENSE_PWRONS = 27,
+ SENSE_THWARNLS = 36,
+ SENSE_THWARNHS = 37,
+ SENSE_CLKS = 38,
+ SENSE_LBPS = 44,
+ SENSE_NB,
+} t_sensor;
+
+typedef struct {
+ bool sense_vbusvs;
+ bool sense_idfacs;
+ bool sense_usbovs;
+ bool sense_chgdets;
+ bool sense_chgrevs;
+ bool sense_chgrshorts;
+ bool sense_cccvs;
+ bool sense_chgcurrs;
+ bool sense_bpons;
+ bool sense_lobatls;
+ bool sense_lobaths;
+ bool sense_idfloats;
+ bool sense_idgnds;
+ bool sense_se1s;
+ bool sense_pwrons;
+ bool sense_thwarnls;
+ bool sense_thwarnhs;
+ bool sense_clks;
+ bool sense_lbps;
+} t_sensor_bits;
+
+extern struct i2c_client *mc13892_client;
+int pmic_i2c_24bit_read(struct i2c_client *client, unsigned int reg_num,
+ unsigned int *value);
+int pmic_read(int reg_num, unsigned int *reg_val);
+int pmic_write(int reg_num, const unsigned int reg_val);
+void gpio_pmic_active(void);
+void pmic_event_list_init(void);
+void mc13892_power_off(void);
+
+#endif
+
+#if defined(CONFIG_MXC_PMIC_MC34704_MODULE) || defined(CONFIG_MXC_PMIC_MC34704)
+
+typedef enum {
+ /* register names for mc34704 */
+ REG_MC34704_GENERAL1 = 0x01,
+ REG_MC34704_GENERAL2 = 0x02,
+ REG_MC34704_GENERAL3 = 0x03,
+ REG_MC34704_VGSET1 = 0x04,
+ REG_MC34704_VGSET2 = 0x05,
+ REG_MC34704_REG2SET1 = 0x06,
+ REG_MC34704_REG2SET2 = 0x07,
+ REG_MC34704_REG3SET1 = 0x08,
+ REG_MC34704_REG3SET2 = 0x09,
+ REG_MC34704_REG4SET1 = 0x0A,
+ REG_MC34704_REG4SET2 = 0x0B,
+ REG_MC34704_REG5SET1 = 0x0C,
+ REG_MC34704_REG5SET2 = 0x0D,
+ REG_MC34704_REG5SET3 = 0x0E,
+ REG_MC34704_REG6SET1 = 0x0F,
+ REG_MC34704_REG6SET2 = 0x10,
+ REG_MC34704_REG6SET3 = 0x11,
+ REG_MC34704_REG7SET1 = 0x12,
+ REG_MC34704_REG7SET2 = 0x13,
+ REG_MC34704_REG7SET3 = 0x14,
+ REG_MC34704_REG8SET1 = 0x15,
+ REG_MC34704_REG8SET2 = 0x16,
+ REG_MC34704_REG8SET3 = 0x17,
+ REG_MC34704_FAULTS = 0x18,
+ REG_MC34704_I2CSET1 = 0x19,
+ REG_MC34704_REG3DAC = 0x49,
+ REG_MC34704_REG7CR0 = 0x58,
+ REG_MC34704_REG7DAC = 0x59,
+ REG_NB = 0x60,
+} pmic_reg;
+
+typedef enum {
+ /* events for mc34704 */
+ EVENT_FLT1 = 0,
+ EVENT_FLT2,
+ EVENT_FLT3,
+ EVENT_FLT4,
+ EVENT_FLT5,
+ EVENT_FLT6,
+ EVENT_FLT7,
+ EVENT_FLT8,
+ EVENT_NB,
+} type_event;
+
+typedef enum {
+ MCU_SENSOR_NOT_SUPPORT
+} t_sensor;
+
+typedef enum {
+ MCU_SENSOR_BIT_NOT_SUPPORT
+} t_sensor_bits;
+
+#endif /* MXC_PMIC_MC34704 */
+
+/* EXPORTED FUNCTIONS */
+#ifdef __KERNEL__
+
+#if defined(CONFIG_MXC_PMIC)
+/*!
+ * This function is used to determine the PMIC type and its revision.
+ *
+ * @return Returns the PMIC type and its revision.
+ */
+pmic_version_t pmic_get_version(void);
+
+/*!
+ * This function is called by PMIC clients to read a register on PMIC.
+ *
+ * @param priority priority of access
+ * @param reg number of register
+ * @param reg_value return value of register
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_read_reg(int reg, unsigned int *reg_value,
+ unsigned int reg_mask);
+/*!
+ * This function is called by PMIC clients to write a register on MC13783.
+ *
+ * @param priority priority of access
+ * @param reg number of register
+ * @param reg_value New value of register
+ * @param reg_mask Bitmap mask indicating which bits to modify
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_write_reg(int reg, unsigned int reg_value,
+ unsigned int reg_mask);
+
+/*!
+ * This function is called by PMIC clients to subscribe on an event.
+ *
+ * @param event_sub structure of event, it contains type of event and callback
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_event_subscribe(type_event event,
+ pmic_event_callback_t callback);
+/*!
+* This function is called by PMIC clients to un-subscribe on an event.
+*
+* @param event_unsub structure of event, it contains type of event and callback
+*
+* @return This function returns PMIC_SUCCESS if successful.
+*/
+PMIC_STATUS pmic_event_unsubscribe(type_event event,
+ pmic_event_callback_t callback);
+/*!
+* This function is called to read all sensor bits of PMIC.
+*
+* @param sensor Sensor to be checked.
+*
+* @return This function returns true if the sensor bit is high;
+* or returns false if the sensor bit is low.
+*/
+bool pmic_check_sensor(t_sensor sensor);
+
+/*!
+* This function checks one sensor of PMIC.
+*
+* @param sensor_bits structure of all sensor bits.
+*
+* @return This function returns PMIC_SUCCESS if successful.
+*/
+PMIC_STATUS pmic_get_sensors(t_sensor_bits * sensor_bits);
+
+void pmic_event_callback(type_event event);
+void pmic_event_list_init(void);
+
+#endif /*CONFIG_MXC_PMIC*/
+#endif /* __KERNEL__ */
+/* CONFIG_MXC_PMIC_MC13783 || CONFIG_MXC_PMIC_MC9SDZ60 */
+
+struct pmic_platform_data {
+ int (*init)(void *);
+ int power_key_irq;
+};
+
+#endif /* __ASM_ARCH_MXC_PMIC_EXTERNAL_H__ */
diff --git a/include/linux/pmic_light.h b/include/linux/pmic_light.h
new file mode 100644
index 000000000000..8490dc133ccf
--- /dev/null
+++ b/include/linux/pmic_light.h
@@ -0,0 +1,1082 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+#ifndef __ASM_ARCH_MXC_PMIC_LIGHT_H__
+#define __ASM_ARCH_MXC_PMIC_LIGHT_H__
+
+/*!
+ * @defgroup PMIC_LIGHT PMIC Light Driver
+ * @ingroup PMIC_DRVRS
+ */
+
+/*!
+ * @file arch-mxc/pmic_light.h
+ * @brief This is the header of PMIC Light driver.
+ *
+ * @ingroup PMIC_LIGHT
+ */
+
+#include <linux/ioctl.h>
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+/*!
+ * @name IOCTL user space interface
+ */
+
+/*! @{ */
+/*!
+ * Enable Backlight.
+ * Argument type: none.
+ */
+#define PMIC_BKLIT_ENABLE _IO('p', 0xe0)
+/*!
+ * Disable Backlight.
+ * Argument type: none.
+ */
+#define PMIC_BKLIT_DISABLE _IO('p', 0xe1)
+/*!
+ * Set backlight configuration.
+ * Argument type: pointer to t_bklit_setting_param
+ */
+#define PMIC_SET_BKLIT _IOW('p', 0xe2, int)
+/*!
+ * Get backlight configuration.
+ * Argument type: pointer to t_bklit_setting_param
+ */
+#define PMIC_GET_BKLIT _IOWR('p', 0xe3, int)
+/*!
+ * Ramp up configuration.
+ * Argument type: t_bklit_channel
+ */
+#define PMIC_RAMPUP_BKLIT _IOW('p', 0xe4, int)
+/*!
+ * Ramp down configuration.
+ * Argument type: t_bklit_channel
+ */
+#define PMIC_RAMPDOWN_BKLIT _IOW('p', 0xe5, int)
+/*!
+ * Enable Tri-color LED.
+ * Argument type: t_tcled_enable_param
+ */
+#define PMIC_TCLED_ENABLE _IOW('p', 0xe6, int)
+/*!
+ * Disable Tri-color LED.
+ * Argument type: t_funlight_bank
+ */
+#define PMIC_TCLED_DISABLE _IOW('p', 0xe7, int)
+/*!
+ * Start Tri-color LED pattern.
+ * Argument type: t_fun_param
+ */
+#define PMIC_TCLED_PATTERN _IOW('p', 0xe8, int)
+/*!
+ * Enable Backlight & tcled.
+ * Argument type: none.
+ */
+#define PMIC_BKLIT_TCLED_ENABLE _IO('p', 0xe9)
+/*!
+ * Disable Backlight & tcled.
+ * Argument type: none.
+ */
+#define PMIC_BKLIT_TCLED_DISABLE _IO('p', 0xea)
+/*!
+ * Reset ramp up configuration.
+ * Argument type: t_bklit_channel
+ */
+#define PMIC_OFF_RAMPUP_BKLIT _IOW('p', 0xeb, int)
+/*!
+ * Reset ramp down configuration.
+ * Argument type: t_bklit_channel
+ */
+#define PMIC_OFF_RAMPDOWN_BKLIT _IOW('p', 0xec, int)
+/*!
+ * Set tcled ind configuration.
+ * Argument type: t_tcled_ind_param
+ */
+#define PMIC_SET_TCLED _IOW('p', 0xed, int)
+/*!
+ * Get tcled ind configuration.
+ * Argument type: t_tcled_ind_param
+ */
+#define PMIC_GET_TCLED _IOWR('p', 0xee, int)
+/*! @} */
+/*!
+ * @enum t_bklit_mode
+ * @brief Backlight modes.
+ */
+typedef enum {
+ BACKLIGHT_CURRENT_CTRL_MODE, /*! < Current control mode */
+ BACKLIGHT_TRIODE_MODE /*! < Triode mode */
+} t_bklit_mode;
+
+/*!
+ * @enum t_bklit_channel
+ * @brief Backlight channels.
+ */
+typedef enum {
+ BACKLIGHT_LED1, /*! < Backlight channel 1 */
+ BACKLIGHT_LED2, /*! < Backlight channel 2 */
+ BACKLIGHT_LED3 /*! < Backlight channel 3 */
+} t_bklit_channel;
+
+/*!
+ * @enum t_bklit_strobe_mode
+ * @brief Backlight Strobe Light Pulsing modes.
+ */
+typedef enum {
+ /*!
+ * No Strobe Light Pulsing
+ */
+ BACKLIGHT_STROBE_NONE,
+ /*!
+ * Strobe Light Pulsing at 3.3% duty cycle over 300msec (Driver goes
+ * into Triode Mode with pulses constrained to 10msec.)
+ */
+ BACKLIGHT_STROBE_FAST,
+ /*!
+ * Strobe Light Pulsing at 10% duty cycle over 100msec (Driver goes
+ * into Triode Mode with pulses constrained to 10msec.)
+ */
+ BACKLIGHT_STROBE_SLOW
+} t_bklit_strobe_mode;
+
+/*!
+ * @struct t_bklit_setting_param
+ * @brief Backlight setting.
+ */
+
+typedef struct {
+ t_bklit_channel channel; /*!< Channel */
+ t_bklit_mode mode; /*!< Mode */
+ t_bklit_strobe_mode strobe; /*!< Strobe mode */
+ unsigned char current_level; /*!< Current level */
+ unsigned char duty_cycle; /*!< Duty cycle */
+ unsigned char cycle_time; /*!< Cycle time */
+ bool edge_slow; /*!< Edge Slow */
+ bool en_dis; /*!< Enable disable boost mode */
+ unsigned int abms; /*!< Adaptive boost
+ * mode selection */
+ unsigned int abr; /*!< Adaptive
+ * boost reference */
+} t_bklit_setting_param;
+
+/*!
+ * @enum t_funlight_bank
+ * @brief Tri-color LED fun light banks.
+ */
+typedef enum {
+ TCLED_FUN_BANK1 = 0, /*! < Fun light bank 1 */
+ TCLED_FUN_BANK2, /*! < Fun light bank 2 */
+ TCLED_FUN_BANK3 /*! < Fun light bank 3 */
+} t_funlight_bank;
+
+/*!
+ * @enum t_tcled_mode
+ * @brief Tri-color LED operation modes.
+ *
+ * The Tri-Color LED Driver circuitry includes 2 modes of operation. In LED
+ * Indicator Mode, this circuitry operates as Red and Green LED Drivers with
+ * flasher timing to indicate GSM network status. In Fun Light Mode, this
+ * circuitry provides expanded capability for current control and distribution
+ * that supplements the three channels.
+ */
+typedef enum {
+ TCLED_IND_MODE = 0, /*! < LED Indicator Mode */
+ TCLED_FUN_MODE /*! < Fun Light Mode */
+} t_tcled_mode;
+
+/*!
+ * @struct t_tcled_enable_param
+ * @brief enable setting.
+ */
+typedef struct {
+ t_funlight_bank bank; /*!< Bank */
+ t_tcled_mode mode; /*!< Mode */
+} t_tcled_enable_param;
+
+/*!
+ * @enum t_ind_channel
+ * @brief Tri-color LED indicator mode channels.
+ *
+ */
+
+typedef enum {
+ TCLED_IND_RED = 0, /*! < Red LED */
+ TCLED_IND_GREEN, /*! < Green LED */
+ TCLED_IND_BLUE /*! < Blue LED */
+} t_ind_channel;
+
+/*!
+ * @enum t_funlight_channel
+ * @brief Tri-color LED fun light mode channels.
+ *
+ */
+typedef enum {
+ TCLED_FUN_CHANNEL1 = 0, /*! < Fun light channel 1 (Red) */
+ TCLED_FUN_CHANNEL2, /*! < Fun light channel 2 (Green) */
+ TCLED_FUN_CHANNEL3 /*! < Fun light channel 3 (Blue) */
+} t_funlight_channel;
+
+/*!
+ * @enum t_tcled_ind_blink_pattern
+ * @brief Tri-color LED Indicator Mode blinking mode.
+ */
+typedef enum {
+ TCLED_IND_OFF = 0, /*! < Continuous off */
+ TCLED_IND_BLINK_1, /*! < 1 / 31 */
+ TCLED_IND_BLINK_2, /*! < 2 / 31 */
+ TCLED_IND_BLINK_3, /*! < 3 / 31 */
+ TCLED_IND_BLINK_4, /*! < 4 / 31 */
+ TCLED_IND_BLINK_5, /*! < 5 / 31 */
+ TCLED_IND_BLINK_6, /*! < 6 / 31 */
+ TCLED_IND_BLINK_7, /*! < 7 / 31 */
+ TCLED_IND_BLINK_8, /*! < 8 / 31 */
+ TCLED_IND_BLINK_9, /*! < 9 / 31 */
+ TCLED_IND_BLINK_10, /*! < 10 / 31 */
+ TCLED_IND_BLINK_11, /*! < 11 / 31 */
+ TCLED_IND_BLINK_12, /*! < 12 / 31 */
+ TCLED_IND_BLINK_13, /*! < 13 / 31 */
+ TCLED_IND_BLINK_14, /*! < 14 / 31 */
+ TCLED_IND_BLINK_15, /*! < 15 / 31 */
+ TCLED_IND_BLINK_16, /*! < 16 / 31 */
+ TCLED_IND_BLINK_17, /*! < 17 / 31 */
+ TCLED_IND_BLINK_18, /*! < 18 / 31 */
+ TCLED_IND_BLINK_19, /*! < 19 / 31 */
+ TCLED_IND_BLINK_20, /*! < 20 / 31 */
+ TCLED_IND_BLINK_21, /*! < 21 / 31 */
+ TCLED_IND_BLINK_22, /*! < 22 / 31 */
+ TCLED_IND_BLINK_23, /*! < 23 / 31 */
+ TCLED_IND_BLINK_24, /*! < 24 / 31 */
+ TCLED_IND_BLINK_25, /*! < 25 / 31 */
+ TCLED_IND_BLINK_26, /*! < 26 / 31 */
+ TCLED_IND_BLINK_27, /*! < 27 / 31 */
+ TCLED_IND_BLINK_28, /*! < 28 / 31 */
+ TCLED_IND_BLINK_29, /*! < 29 / 31 */
+ TCLED_IND_BLINK_30, /*! < 30 / 31 */
+ TCLED_IND_ON /*! < Continuous on */
+} t_tcled_ind_blink_pattern;
+
+/*!
+ * @enum t_tcled_cur_level
+ * @brief Tri-color LED current levels.
+ */
+typedef enum {
+ TCLED_CUR_LEVEL_1 = 0, /*! < Tri-Color LED current level 1 */
+ TCLED_CUR_LEVEL_2, /*! < Tri-Color LED current level 2 */
+ TCLED_CUR_LEVEL_3, /*! < Tri-Color LED current level 3 */
+ TCLED_CUR_LEVEL_4 /*! < Tri-Color LED current level 4 */
+} t_tcled_cur_level;
+
+/*!
+ * @enum t_tcled_fun_cycle_time
+ * @brief Tri-color LED fun light mode cycle time.
+ */
+typedef enum {
+ TC_CYCLE_TIME_1 = 0, /*! < Tri-Color LED cycle time 1 */
+ TC_CYCLE_TIME_2, /*! < Tri-Color LED cycle time 2 */
+ TC_CYCLE_TIME_3, /*! < Tri-Color LED cycle time 3 */
+ TC_CYCLE_TIME_4 /*! < Tri-Color LED cycle time 4 */
+} t_tcled_fun_cycle_time;
+
+/*!
+ * @enum t_tcled_fun_speed
+ * @brief Tri-color LED fun light mode pattern speed.
+ */
+typedef enum {
+ TC_OFF = 0, /*! < Tri-Color pattern off */
+ TC_SLOW, /*! < Tri-Color slow pattern */
+ TC_FAST /*! < Tri-Color fast pattern */
+} t_tcled_fun_speed;
+
+/*!
+ * @enum t_tcled_fun_speed
+ * @brief Tri-color LED fun light mode pattern speed.
+ */
+typedef enum {
+ TC_STROBE_OFF = 0, /*! < No strobe */
+ TC_STROBE_SLOW, /*! < Slow strobe pattern */
+ TC_STROBE_FAST /*! < fast strobe pattern */
+} t_tcled_fun_strobe_speed;
+
+/*!
+ * @enum t_chaselight_pattern
+ * @brief Tri-color LED fun light mode chasing light patterns.
+ */
+typedef enum {
+ PMIC_RGB = 0, /*!< R -> G -> B */
+ BGR /*!< B -> G -> R */
+} t_chaselight_pattern;
+
+/*!
+ * This enumeration of Fun Light Pattern.
+ */
+typedef enum {
+ /*!
+ * Blended ramps slow
+ */
+ BLENDED_RAMPS_SLOW,
+ /*!
+ * Blended ramps fast
+ */
+ BLENDED_RAMPS_FAST,
+ /*!
+ * Saw ramps slow
+ */
+ SAW_RAMPS_SLOW,
+ /*!
+ * Saw ramps fast
+ */
+ SAW_RAMPS_FAST,
+ /*!
+ * Blended bowtie slow
+ */
+ BLENDED_BOWTIE_SLOW,
+ /*!
+ * Blended bowtie fast
+ */
+ BLENDED_BOWTIE_FAST,
+ /*!
+ * Strobe slow
+ */
+ STROBE_SLOW,
+ /*!
+ * Strobe fast
+ */
+ STROBE_FAST,
+ /*!
+ * Chasing Light RGB Slow
+ */
+ CHASING_LIGHT_RGB_SLOW,
+ /*!
+ * Chasing Light RGB fast
+ */
+ CHASING_LIGHT_RGB_FAST,
+ /*!
+ * Chasing Light BGR Slow
+ */
+ CHASING_LIGHT_BGR_SLOW,
+ /*!
+ * Chasing Light BGR fast
+ */
+ CHASING_LIGHT_BGR_FAST,
+} t_fun_pattern;
+
+/*!
+ * @struct t_fun_param
+ * @brief LED fun pattern IOCTL parameter
+ */
+typedef struct {
+ t_funlight_bank bank; /*!< TCLED bank */
+ t_funlight_channel channel; /*!< TCLED channel */
+ t_fun_pattern pattern; /*!< Fun pattern */
+} t_fun_param;
+
+/*!
+ * @enum t_led_channel
+ * @brief LED channels including backlight and tri-color LEDs.
+ */
+typedef enum {
+ AUDIO_LED1, /*! < Backlight channel 1 */
+ AUDIO_LED2, /*! < Backlight channel 2 */
+ AUDIO_LEDR, /*! < Fun light channel 1 (Red) */
+ AUDIO_LEDG, /*! < Fun light channel 2 (Green) */
+ AUDIO_LEDB /*! < Fun light channel 3 (Blue) */
+} t_led_channel;
+
+/*!
+ * @enum t_aud_path
+ * @brief LED audio modulation in-out audio channels
+ */
+typedef enum {
+ MIXED_RX = 0, /*!< Mixed L & R Channel RX audio */
+ TX /*!< TX path */
+} t_aud_path;
+
+/*!
+ * @enum t_aud_gain
+ * @brief LED audio modulation in-out audio channels
+ */
+typedef enum {
+ GAIN_MINUS6DB = 0, /*!< -6 dB */
+ GAIN_0DB, /*!< 0 dB */
+ GAIN_6DB, /*!< 6 dB */
+ GAIN_12DB /*!< 12 dB */
+} t_aud_gain;
+
+/*!
+ * @struct t_tcled_ind_param
+ * @brief LED parameter
+ */
+typedef struct {
+ t_funlight_bank bank; /*! < tcled bank */
+ t_ind_channel channel; /*! < tcled channel */
+ t_tcled_cur_level level; /*! < tcled current level */
+ t_tcled_ind_blink_pattern pattern; /*! < tcled dutty cycle */
+ bool skip; /*! < tcled skip */
+ bool rampup; /*! < tcled rampup */
+ bool rampdown; /*! < tcled rampdown */
+ bool half_current; /*! < tcled half current */
+} t_tcled_ind_param;
+
+#if defined(CONFIG_MXC_PMIC_MC13892)
+
+enum curr_level {
+ LIT_CURR_0 = 0,
+ LIT_CURR_3,
+ LIT_CURR_6,
+ LIT_CURR_9,
+ LIT_CURR_12,
+ LIT_CURR_15,
+ LIT_CURR_18,
+ LIT_CURR_21,
+ /* below setting only used for main/aux/keypad */
+ LIT_CURR_HI_0,
+ LIT_CURR_HI_6,
+ LIT_CURR_HI_12,
+ LIT_CURR_HI_18,
+ LIT_CURR_HI_24,
+ LIT_CURR_HI_30,
+ LIT_CURR_HI_36,
+ LIT_CURR_HI_42,
+};
+
+enum lit_channel {
+ LIT_MAIN = 0,
+ LIT_AUX,
+ LIT_KEY,
+ LIT_RED,
+ LIT_GREEN,
+ LIT_BLUE,
+};
+
+#endif
+
+/* EXPORTED FUNCTIONS */
+#ifdef __KERNEL__
+/*!
+ * This function enables backlight & tcled.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_tcled_master_enable(void);
+
+/*!
+ * This function disables backlight & tcled.
+ *
+ * @return This function returns PMIC_SUCCESS if successful
+ */
+PMIC_STATUS pmic_bklit_tcled_master_disable(void);
+
+/*!
+ * This function enables backlight.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_master_enable(void);
+
+/*!
+ * This function disables backlight.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_master_disable(void);
+
+/*!
+ * This function sets backlight current level.
+ *
+ * @param channel Backlight channel
+ * @param level Backlight current level, as the following table.
+ * @verbatim
+ level current
+ ------ -----------
+ 0 0 mA
+ 1 12 mA
+ 2 24 mA
+ 3 36 mA
+ 4 48 mA
+ 5 60 mA
+ 6 72 mA
+ 7 84 mA
+ @endverbatim
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_current(t_bklit_channel channel,
+ unsigned char level);
+
+/*!
+ * This function retrives backlight current level.
+ *
+ * @param channel Backlight channel
+ * @param level Pointer to store backlight current level result.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_current(t_bklit_channel channel,
+ unsigned char *level);
+
+/*!
+ * This function sets a backlight channel duty cycle.
+ * LED perceived brightness for each zone may be individually set by setting
+ * duty cycle. The default setting is for 0% duty cycle; this keeps all zone
+ * drivers turned off even after the master enable command. Each LED current
+ * sink can be turned on and adjusted for brightness with an independent 4 bit
+ * word for a duty cycle ranging from 0% to 100% in approximately 6.7% steps.
+ *
+ * @param channel Backlight channel.
+ * @param dc Backlight duty cycle, as the following table.
+ * @verbatim
+ dc Duty Cycle (% On-time over Cycle Time)
+ ------ ---------------------------------------
+ 0 0%
+ 1 6.7%
+ 2 13.3%
+ 3 20%
+ 4 26.7%
+ 5 33.3%
+ 6 40%
+ 7 46.7%
+ 8 53.3%
+ 9 60%
+ 10 66.7%
+ 11 73.3%
+ 12 80%
+ 13 86.7%
+ 14 93.3%
+ 15 100%
+ @endverbatim
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_dutycycle(t_bklit_channel channel, unsigned char dc);
+
+/*!
+ * This function retrives a backlight channel duty cycle.
+ *
+ * @param channel Backlight channel.
+ * @param cycle Pointer to backlight duty cycle.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_dutycycle(t_bklit_channel channel,
+ unsigned char *dc);
+
+/*!
+ * This function sets a backlight channel cycle time.
+ * Cycle Time is defined as the period of a complete cycle of
+ * Time_on + Time_off. The default Cycle Time is set to 0.01 seconds such that
+ * the 100 Hz on-off cycling is averaged out by the eye to eliminate
+ * flickering. Additionally, the Cycle Time can be programmed to intentionally
+ * extend the period of on-off cycles for a visual pulsating or blinking effect.
+ *
+ * @param period Backlight cycle time, as the following table.
+ * @verbatim
+ period Cycle Time
+ -------- ------------
+ 0 0.01 seconds
+ 1 0.1 seconds
+ 2 0.5 seconds
+ 3 2 seconds
+ @endverbatim
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_cycle_time(unsigned char period);
+
+/*!
+ * This function retrives a backlight channel cycle time setting.
+ *
+ * @param period Pointer to save backlight cycle time setting result.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_cycle_time(unsigned char *period);
+
+/*!
+ * This function sets backlight operation mode. There are two modes of
+ * operations: current control and triode mode.
+ * The Duty Cycle/Cycle Time control is retained in Triode Mode. Audio
+ * coupling is not available in Triode Mode.
+ *
+ * @param channel Backlight channel.
+ * @param mode Backlight operation mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_mode(t_bklit_channel channel, t_bklit_mode mode);
+/*!
+ * This function gets backlight operation mode. There are two modes of
+ * operations: current control and triode mode.
+ * The Duty Cycle/Cycle Time control is retained in Triode Mode. Audio
+ * coupling is not available in Triode Mode.
+ *
+ * @param channel Backlight channel.
+ * @param mode Backlight operation mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_mode(t_bklit_channel channel, t_bklit_mode * mode);
+/*!
+ * This function starts backlight brightness ramp up function; ramp time is
+ * fixed at 0.5 seconds.
+ *
+ * @param channel Backlight channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_rampup(t_bklit_channel channel);
+/*!
+ * This function stops backlight brightness ramp up function;
+ *
+ * @param channel Backlight channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_off_rampup(t_bklit_channel channel);
+/*!
+ * This function starts backlight brightness ramp down function; ramp time is
+ * fixed at 0.5 seconds.
+ *
+ * @param channel Backlight channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_rampdown(t_bklit_channel channel);
+/*!
+ * This function stops backlight brightness ramp down function.
+ *
+ * @param channel Backlight channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_off_rampdown(t_bklit_channel channel);
+/*!
+ * This function enables backlight analog edge slowing mode. Analog Edge
+ * Slowing slows down the transient edges to reduce the chance of coupling LED
+ * modulation activity into other circuits. Rise and fall times will be targeted
+ * for approximately 50usec.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_enable_edge_slow(void);
+
+/*!
+ * This function disables backlight analog edge slowing mode. The backlight
+ * drivers will default to an “Instant On” mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_disable_edge_slow(void);
+/*!
+ * This function gets backlight analog edge slowing mode. DThe backlight
+ *
+ * @param edge Edge slowing mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_get_edge_slow(bool * edge);
+/*!
+ * This function sets backlight Strobe Light Pulsing mode.
+ *
+ * @param channel Backlight channel.
+ * @param mode Strobe Light Pulsing mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_bklit_set_strobemode(t_bklit_channel channel,
+ t_bklit_strobe_mode mode);
+
+/*!
+ * This function enables tri-color LED.
+ *
+ * @param mode Tri-color LED operation mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_enable(t_tcled_mode mode, t_funlight_bank bank);
+/*!
+ * This function disables tri-color LED.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_disable(t_funlight_bank bank);
+/*!
+ * This function retrives tri-color LED operation mode.
+ *
+ * @param mode Pointer to Tri-color LED operation mode.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_get_mode(t_tcled_mode * mode, t_funlight_bank bank);
+/*!
+ * This function sets a tri-color LED channel current level in indicator mode.
+ *
+ * @param channel Tri-color LED channel.
+ * @param level Current level.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_set_current(t_ind_channel channel,
+ t_tcled_cur_level level,
+ t_funlight_bank bank);
+/*!
+ * This function retrives a tri-color LED channel current level in indicator mode.
+ *
+ * @param channel Tri-color LED channel.
+ * @param level Pointer to current level.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_get_current(t_ind_channel channel,
+ t_tcled_cur_level * level,
+ t_funlight_bank bank);
+/*!
+ * This function sets a tri-color LED channel blinking pattern in indication
+ * mode.
+ *
+ * @param channel Tri-color LED channel.
+ * @param pattern Blinking pattern.
+ * @param skip If true, skip a cycle after each cycle.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+
+PMIC_STATUS pmic_tcled_ind_set_blink_pattern(t_ind_channel channel,
+ t_tcled_ind_blink_pattern pattern,
+ bool skip, t_funlight_bank bank);
+/*!
+ * This function retrives a tri-color LED channel blinking pattern in
+ * indication mode.
+ *
+ * @param channel Tri-color LED channel.
+ * @param pattern Pointer to Blinking pattern.
+ * @param skip Pointer to a boolean variable indicating if skip
+ * a cycle after each cycle.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_ind_get_blink_pattern(t_ind_channel channel,
+ t_tcled_ind_blink_pattern *
+ pattern, bool * skip,
+ t_funlight_bank bank);
+/*!
+ * This function sets a tri-color LED channel current level in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param level Current level.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_set_current(t_funlight_bank bank,
+ t_funlight_channel channel,
+ t_tcled_cur_level level);
+
+/*!
+ * This function retrives a tri-color LED channel current level
+ * in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param level Pointer to current level.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_get_current(t_funlight_bank bank,
+ t_funlight_channel channel,
+ t_tcled_cur_level * level);
+
+/*!
+ * This function sets tri-color LED cycle time in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param ct Cycle time.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_set_cycletime(t_funlight_bank bank,
+ t_tcled_fun_cycle_time ct);
+
+/*!
+ * This function retrives tri-color LED cycle time in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param ct Pointer to cycle time.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_get_cycletime(t_funlight_bank bank,
+ t_tcled_fun_cycle_time * ct);
+
+/*!
+ * This function sets a tri-color LED channel duty cycle in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param dc Duty cycle.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_set_dutycycle(t_funlight_bank bank,
+ t_funlight_channel channel,
+ unsigned char dc);
+
+/*!
+ * This function retrives a tri-color LED channel duty cycle in Fun Light mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param dc Pointer to duty cycle.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_get_dutycycle(t_funlight_bank bank,
+ t_funlight_channel channel,
+ unsigned char *dc);
+
+/*!
+ * This function initiates Blended Ramp fun light pattern.
+ *
+ * @param bank Tri-color LED bank
+ * @param speed Speed of pattern.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_blendedramps(t_funlight_bank bank,
+ t_tcled_fun_speed speed);
+
+/*!
+ * This function initiates Saw Ramp fun light pattern.
+ *
+ * @param bank Tri-color LED bank
+ * @param speed Speed of pattern.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_sawramps(t_funlight_bank bank,
+ t_tcled_fun_speed speed);
+
+/*!
+ * This function initiates Blended Bowtie fun light pattern.
+ *
+ * @param bank Tri-color LED bank
+ * @param speed Speed of pattern.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_blendedbowtie(t_funlight_bank bank,
+ t_tcled_fun_speed speed);
+
+/*!
+ * This function initiates Chasing Lights fun light pattern.
+ *
+ * @param bank Tri-color LED bank
+ * @param pattern Chasing light pattern mode.
+ * @param speed Speed of pattern.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_chasinglightspattern(t_funlight_bank bank,
+ t_chaselight_pattern pattern,
+ t_tcled_fun_speed speed);
+
+/*!
+ * This function initiates Strobe Mode fun light pattern.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param speed Speed of pattern.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_strobe(t_funlight_bank bank,
+ t_funlight_channel channel,
+ t_tcled_fun_strobe_speed speed);
+
+/*!
+ * This function initiates Tri-color LED brightness Ramp Up function; Ramp time
+ * is fixed at 1 second.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param rampup Ramp-up configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_rampup(t_funlight_bank bank,
+ t_funlight_channel channel, bool rampup);
+/*!
+ * This function gets Tri-color LED brightness Ramp Up function; Ramp time
+ * is fixed at 1 second.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param rampup Ramp-up configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_get_fun_rampup(t_funlight_bank bank,
+ t_funlight_channel channel,
+ bool * rampup);
+
+/*!
+ * This function initiates Tri-color LED brightness Ramp Down function; Ramp
+ * time is fixed at 1 second.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param rampdown Ramp-down configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_rampdown(t_funlight_bank bank,
+ t_funlight_channel channel, bool rampdown);
+/*!
+ * This function initiates Tri-color LED brightness Ramp Down function; Ramp
+ * time is fixed at 1 second.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ * @param rampdown Ramp-down configuration.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_get_fun_rampdown(t_funlight_bank bank,
+ t_funlight_channel channel,
+ bool * rampdown);
+
+/*!
+ * This function enables a Tri-color channel triode mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_triode_on(t_funlight_bank bank,
+ t_funlight_channel channel);
+
+/*!
+ * This function disables a Tri-color LED channel triode mode.
+ *
+ * @param bank Tri-color LED bank
+ * @param channel Tri-color LED channel.
+ *
+ * @return This function returns PMIC_SUCCESS if successful.
+ */
+PMIC_STATUS pmic_tcled_fun_triode_off(t_funlight_bank bank,
+ t_funlight_channel channel);
+
+/*!
+ * This function enables Tri-color LED edge slowing.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_tcled_enable_edge_slow(void);
+
+/*!
+ * This function disables Tri-color LED edge slowing.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_tcled_disable_edge_slow(void);
+
+/*!
+ * This function enables Tri-color LED half current mode.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_tcled_enable_half_current(void);
+
+/*!
+ * This function disables Tri-color LED half current mode.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_tcled_disable_half_current(void);
+
+/*!
+ * This function enables backlight or Tri-color LED audio modulation.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_tcled_enable_audio_modulation(t_led_channel channel,
+ t_aud_path path,
+ t_aud_gain gain,
+ bool lpf_bypass);
+
+/*!
+ * This function disables backlight or Tri-color LED audio modulation.
+ *
+ * @return This function returns PMIC_NOT_SUPPORTED.
+ */
+PMIC_STATUS pmic_tcled_disable_audio_modulation(void);
+/*!
+ * This function enables the boost mode.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param en_dis Enable or disable the boost mode
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_set_boost_mode(bool en_dis);
+
+/*!
+ * This function gets the boost mode.
+ * Only on mc13783 2.0 or higher
+ *
+ * @param en_dis Enable or disable the boost mode
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_get_boost_mode(bool * en_dis);
+
+/*!
+ * This function sets boost mode configuration
+ * Only on mc13783 2.0 or higher
+ *
+ * @param abms Define adaptive boost mode selection
+ * @param abr Define adaptive boost reference
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_config_boost_mode(unsigned int abms, unsigned int abr);
+
+/*!
+ * This function gets boost mode configuration
+ * Only on mc13783 2.0 or higher
+ *
+ * @param abms Define adaptive boost mode selection
+ * @param abr Define adaptive boost reference
+ *
+ * @return This function returns 0 if successful.
+ */
+PMIC_STATUS pmic_bklit_gets_boost_mode(unsigned int *abms, unsigned int *abr);
+
+#if defined(CONFIG_MXC_PMIC_MC13892)
+
+PMIC_STATUS mc13892_bklit_set_current(enum lit_channel channel,
+ unsigned char level);
+PMIC_STATUS mc13892_bklit_get_current(enum lit_channel channel,
+ unsigned char *level);
+PMIC_STATUS mc13892_bklit_set_dutycycle(enum lit_channel channel,
+ unsigned char dc);
+PMIC_STATUS mc13892_bklit_get_dutycycle(enum lit_channel channel,
+ unsigned char *dc);
+PMIC_STATUS mc13892_bklit_set_ramp(enum lit_channel channel, int flag);
+PMIC_STATUS mc13892_bklit_get_ramp(enum lit_channel channel, int *flag);
+PMIC_STATUS mc13892_bklit_set_blink_p(enum lit_channel channel, int period);
+PMIC_STATUS mc13892_bklit_get_blink_p(enum lit_channel channel, int *period);
+
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_ARCH_MXC_PMIC_LIGHT_H__ */
diff --git a/include/linux/pmic_rtc.h b/include/linux/pmic_rtc.h
new file mode 100644
index 000000000000..2f7aeda4d3d2
--- /dev/null
+++ b/include/linux/pmic_rtc.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __ASM_ARCH_MXC_PMIC_RTC_H__
+#define __ASM_ARCH_MXC_PMIC_RTC_H__
+
+/*!
+ * @defgroup PMIC_RTC PMIC RTC Driver
+ * @ingroup PMIC_DRVRS
+ */
+
+/*!
+ * @file arch-mxc/pmic_rtc.h
+ * @brief This is the header of PMIC RTC driver.
+ *
+ * @ingroup PMIC_RTC
+ */
+
+/*
+ * Includes
+ */
+#include <linux/ioctl.h>
+#include <linux/pmic_status.h>
+#include <linux/pmic_external.h>
+
+#define PMIC_RTC_SET_TIME _IOWR('p',0xd1, int)
+#define PMIC_RTC_GET_TIME _IOWR('p',0xd2, int)
+#define PMIC_RTC_SET_ALARM _IOWR('p',0xd3, int)
+#define PMIC_RTC_GET_ALARM _IOWR('p',0xd4, int)
+#define PMIC_RTC_WAIT_ALARM _IOWR('p',0xd5, int)
+#define PMIC_RTC_ALARM_REGISTER _IOWR('p',0xd6, int)
+#define PMIC_RTC_ALARM_UNREGISTER _IOWR('p',0xd7, int)
+
+/*!
+ * This enumeration define all RTC interrupt
+ */
+typedef enum {
+ /*!
+ * Time of day alarm
+ */
+ RTC_IT_ALARM,
+ /*!
+ * 1 Hz timetick
+ */
+ RTC_IT_1HZ,
+ /*!
+ * RTC reset occurred
+ */
+ RTC_IT_RST,
+} t_rtc_int;
+
+/*
+ * RTC PMIC API
+ */
+
+/* EXPORTED FUNCTIONS */
+#ifdef __KERNEL__
+
+/*!
+ * This function set the real time clock of PMIC
+ *
+ * @param pmic_time value of date and time
+ *
+ * @return This function returns PMIC_STATUS if successful.
+ */
+PMIC_STATUS pmic_rtc_set_time(struct timeval *pmic_time);
+
+/*!
+ * This function get the real time clock of PMIC
+ *
+ * @param pmic_time return value of date and time
+ *
+ * @return This function returns PMIC_STATUS if successful.
+ */
+PMIC_STATUS pmic_rtc_get_time(struct timeval *pmic_time);
+
+/*!
+ * This function set the real time clock alarm of PMIC
+ *
+ * @param pmic_time value of date and time
+ *
+ * @return This function returns PMIC_STATUS if successful.
+ */
+PMIC_STATUS pmic_rtc_set_time_alarm(struct timeval *pmic_time);
+
+/*!
+ * This function get the real time clock alarm of PMIC
+ *
+ * @param pmic_time return value of date and time
+ *
+ * @return This function returns PMIC_STATUS if successful.
+ */
+PMIC_STATUS pmic_rtc_get_time_alarm(struct timeval *pmic_time);
+
+/*!
+ * This function wait the Alarm event
+ *
+ * @return This function returns PMIC_STATUS if successful.
+ */
+PMIC_STATUS pmic_rtc_wait_alarm(void);
+
+/*!
+ * This function is used to un/subscribe on RTC event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ * @param sub define if Un/subscribe event.
+ *
+ * @return This function returns PMIC_STATUS if successful.
+ */
+PMIC_STATUS pmic_rtc_event(t_rtc_int event, void *callback, bool sub);
+
+/*!
+ * This function is used to subscribe on RTC event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns PMIC_STATUS if successful.
+ */
+PMIC_STATUS pmic_rtc_event_sub(t_rtc_int event, void *callback);
+
+/*!
+ * This function is used to un-subscribe on RTC event IT.
+ *
+ * @param event type of event.
+ * @param callback event callback function.
+ *
+ * @return This function returns PMIC_STATUS if successful.
+ */
+PMIC_STATUS pmic_rtc_event_unsub(t_rtc_int event, void *callback);
+
+/*!
+ * This function is used to tell if PMIC RTC has been correctly loaded.
+ *
+ * @return This function returns 1 if RTC was successfully loaded
+ * 0 otherwise.
+ */
+int pmic_rtc_loaded(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_ARCH_MXC_PMIC_RTC_H__ */
diff --git a/include/linux/pmic_status.h b/include/linux/pmic_status.h
new file mode 100644
index 000000000000..19410fa03f7b
--- /dev/null
+++ b/include/linux/pmic_status.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+#ifndef __ASM_ARCH_MXC_PMIC_STATUS_H__
+#define __ASM_ARCH_MXC_PMIC_STATUS_H__
+#include <asm-generic/errno-base.h>
+#ifdef __KERNEL__
+#include <asm/uaccess.h> /* copy_{from,to}_user() */
+#endif
+/*!
+ * @file arch-mxc/pmic_status.h
+ * @brief PMIC APIs return code definition.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*!
+ * @enum PMIC_STATUS
+ * @brief Define return values for all PMIC APIs.
+ *
+ * These return values are used by all of the PMIC APIs.
+ *
+ * @ingroup PMIC
+ */
+typedef enum {
+ PMIC_SUCCESS = 0, /*!< The requested operation was successfully
+ completed. */
+ PMIC_ERROR = -1, /*!< The requested operation could not be completed
+ due to an error. */
+ PMIC_PARAMETER_ERROR = -2, /*!< The requested operation failed because
+ one or more of the parameters was
+ invalid. */
+ PMIC_NOT_SUPPORTED = -3, /*!< The requested operation could not be
+ completed because the PMIC hardware
+ does not support it. */
+ PMIC_SYSTEM_ERROR_EINTR = -EINTR,
+
+ PMIC_MALLOC_ERROR = -5, /*!< Error in malloc function */
+ PMIC_UNSUBSCRIBE_ERROR = -6, /*!< Error in un-subscribe event */
+ PMIC_EVENT_NOT_SUBSCRIBED = -7, /*!< Event occur and not subscribed */
+ PMIC_EVENT_CALL_BACK = -8, /*!< Error - bad call back */
+ PMIC_CLIENT_NBOVERFLOW = -9, /*!< The requested operation could not be
+ completed because there are too many
+ PMIC client requests */
+} PMIC_STATUS;
+
+/*
+ * Bitfield macros that use rely on bitfield width/shift information.
+ */
+#define BITFMASK(field) (((1U << (field ## _WID)) - 1) << (field ## _LSH))
+#define BITFVAL(field, val) ((val) << (field ## _LSH))
+#define BITFEXT(var, bit) ((var & BITFMASK(bit)) >> (bit ## _LSH))
+
+/*
+ * Macros implementing error handling
+ */
+#define CHECK_ERROR(a) \
+do { \
+ int ret = (a); \
+ if (ret != PMIC_SUCCESS) \
+ return ret; \
+} while (0)
+
+#define CHECK_ERROR_KFREE(func, freeptrs) \
+do { \
+ int ret = (func); \
+ if (ret != PMIC_SUCCESS) { \
+ freeptrs; \
+ return ret; \
+ } \
+} while (0);
+
+#endif /* __ASM_ARCH_MXC_PMIC_STATUS_H__ */
diff --git a/include/linux/pwm-led.h b/include/linux/pwm-led.h
new file mode 100644
index 000000000000..9c84766ca52d
--- /dev/null
+++ b/include/linux/pwm-led.h
@@ -0,0 +1,35 @@
+#ifndef __LINUX_PWM_LED_H
+#define __LINUX_PWM_LED_H
+
+/*
+ * include/linux/pwm-led.h
+ *
+ * Copyright (C) 2008 Bill Gatliff
+ *
+ * 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
+ */
+
+struct led_info;
+struct pwm_channel_config;
+
+struct pwm_led_platform_data {
+ const char *bus_id;
+ int chan;
+ struct pwm_channel_config *config;
+ struct led_info *led_info;
+};
+
+#endif /* __LINUX_PWM_LED_H */
+
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 3945f803d514..bf33edec0761 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -1,31 +1,162 @@
#ifndef __LINUX_PWM_H
#define __LINUX_PWM_H
+#include <linux/workqueue.h>
+
struct pwm_device;
/*
- * pwm_request - request a PWM device
+ * include/linux/pwm.h
+ *
+ * Copyright (C) 2008 Bill Gatliff
+ *
+ * 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
*/
-struct pwm_device *pwm_request(int pwm_id, const char *label);
-/*
- * pwm_free - free a PWM device
- */
-void pwm_free(struct pwm_device *pwm);
+enum {
+ PWM_CONFIG_DUTY_TICKS = BIT(0),
+ PWM_CONFIG_PERIOD_TICKS = BIT(1),
+ PWM_CONFIG_POLARITY = BIT(2),
+ PWM_CONFIG_START = BIT(3),
+ PWM_CONFIG_STOP = BIT(4),
+ PWM_CONFIG_HANDLER = BIT(5),
+ PWM_CONFIG_DUTY_NS = BIT(6),
+ PWM_CONFIG_DUTY_PERCENT = BIT(7),
+ PWM_CONFIG_PERIOD_NS = BIT(8),
+};
-/*
- * pwm_config - change a PWM device configuration
- */
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
+struct pwm_channel;
+struct work_struct;
-/*
- * pwm_enable - start a PWM output toggling
- */
-int pwm_enable(struct pwm_device *pwm);
+typedef int (*pwm_handler_t)(struct pwm_channel *p, void *data);
+typedef void (*pwm_callback_t)(struct pwm_channel *p);
-/*
- * pwm_disable - stop a PWM output toggling
- */
-void pwm_disable(struct pwm_device *pwm);
+struct pwm_channel_config {
+ int config_mask;
+ unsigned long duty_ticks;
+ unsigned long period_ticks;
+ int polarity;
+
+ pwm_handler_t handler;
+
+ unsigned long duty_ns;
+ unsigned long period_ns;
+ int duty_percent;
+};
+
+struct pwm_device {
+ struct list_head list;
+ spinlock_t list_lock;
+ struct device *dev;
+ struct module *owner;
+ struct pwm_channel *channels;
+
+ const char *bus_id;
+ int nchan;
+
+ int (*request) (struct pwm_channel *p);
+ void (*free) (struct pwm_channel *p);
+ int (*config) (struct pwm_channel *p,
+ struct pwm_channel_config *c);
+ int (*config_nosleep)(struct pwm_channel *p,
+ struct pwm_channel_config *c);
+ int (*synchronize) (struct pwm_channel *p,
+ struct pwm_channel *to_p);
+ int (*unsynchronize)(struct pwm_channel *p,
+ struct pwm_channel *from_p);
+ int (*set_callback) (struct pwm_channel *p,
+ pwm_callback_t callback);
+};
+
+int pwm_register(struct pwm_device *pwm);
+int pwm_unregister(struct pwm_device *pwm);
+
+enum {
+ FLAG_REQUESTED = 0,
+ FLAG_STOP = 1,
+};
+
+struct pwm_channel {
+ struct list_head list;
+ struct pwm_device *pwm;
+ const char *requester;
+ int chan;
+ unsigned long flags;
+ unsigned long tick_hz;
+
+ spinlock_t lock;
+ struct completion complete;
+
+ pwm_callback_t callback;
+
+ struct work_struct handler_work;
+ pwm_handler_t handler;
+ void *handler_data;
+
+ int active_low;
+ unsigned long period_ticks;
+ unsigned long duty_ticks;
+};
+
+struct pwm_channel *
+pwm_request(const char *bus_id, int chan,
+ const char *requester);
+
+void pwm_free(struct pwm_channel *pwm);
+
+int pwm_config_nosleep(struct pwm_channel *pwm,
+ struct pwm_channel_config *c);
+
+int pwm_config(struct pwm_channel *pwm,
+ struct pwm_channel_config *c);
+
+unsigned long pwm_ns_to_ticks(struct pwm_channel *pwm,
+ unsigned long nsecs);
+
+unsigned long pwm_ticks_to_ns(struct pwm_channel *pwm,
+ unsigned long ticks);
+
+int pwm_set_period_ns(struct pwm_channel *pwm,
+ unsigned long period_ns);
+
+unsigned long int pwm_get_period_ns(struct pwm_channel *pwm);
+
+int pwm_set_duty_ns(struct pwm_channel *pwm,
+ unsigned long duty_ns);
+
+int pwm_set_duty_percent(struct pwm_channel *pwm,
+ int percent);
+
+unsigned long pwm_get_duty_ns(struct pwm_channel *pwm);
+
+int pwm_set_polarity(struct pwm_channel *pwm,
+ int active_high);
+
+int pwm_start(struct pwm_channel *pwm);
+
+int pwm_stop(struct pwm_channel *pwm);
+
+int pwm_set_handler(struct pwm_channel *pwm,
+ pwm_handler_t handler,
+ void *data);
+
+int pwm_synchronize(struct pwm_channel *p,
+ struct pwm_channel *to_p);
+
+
+int pwm_unsynchronize(struct pwm_channel *p,
+ struct pwm_channel *from_p);
-#endif /* __ASM_ARCH_PWM_H */
+#endif /* __LINUX_PWM_H */
diff --git a/include/linux/random.h b/include/linux/random.h
index 36f125c0c603..22804d9a99cb 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -8,6 +8,7 @@
#define _LINUX_RANDOM_H
#include <linux/ioctl.h>
+#include <linux/types.h> /* for __u32 in user space */
/* ioctl()'s for the random number generator */
@@ -32,6 +33,30 @@
/* Clear the entropy pool and associated counters. (Superuser only.) */
#define RNDCLEARPOOL _IO( 'R', 0x06 )
+#ifdef CONFIG_FIPS_RNG
+
+/* Size of seed value - equal to AES blocksize */
+#define AES_BLOCK_SIZE_BYTES 16
+#define SEED_SIZE_BYTES AES_BLOCK_SIZE_BYTES
+/* Size of AES key */
+#define KEY_SIZE_BYTES 16
+
+/* ioctl() structure used by FIPS 140-2 Tests */
+struct rand_fips_test {
+ unsigned char key[KEY_SIZE_BYTES]; /* Input */
+ unsigned char datetime[SEED_SIZE_BYTES]; /* Input */
+ unsigned char seed[SEED_SIZE_BYTES]; /* Input */
+ unsigned char result[SEED_SIZE_BYTES]; /* Output */
+};
+
+/* FIPS 140-2 RNG Variable Seed Test. (Superuser only.) */
+#define RNDFIPSVST _IOWR('R', 0x10, struct rand_fips_test)
+
+/* FIPS 140-2 RNG Monte Carlo Test. (Superuser only.) */
+#define RNDFIPSMCT _IOWR('R', 0x11, struct rand_fips_test)
+
+#endif /* #ifdef CONFIG_FIPS_RNG */
+
struct rand_pool_info {
int entropy_count;
int buf_size;
@@ -48,6 +73,10 @@ extern void add_input_randomness(unsigned int type, unsigned int code,
unsigned int value);
extern void add_interrupt_randomness(int irq);
+extern void random_input_words(__u32 *buf, size_t wordcount, int ent_count);
+extern int random_input_wait(void);
+#define HAS_RANDOM_INPUT_WAIT 1
+
extern void get_random_bytes(void *buf, int nbytes);
void generate_random_uuid(unsigned char uuid_out[16]);
diff --git a/include/linux/serial.h b/include/linux/serial.h
index 1ea8d9265bf6..9136cc5608c3 100644
--- a/include/linux/serial.h
+++ b/include/linux/serial.h
@@ -10,8 +10,9 @@
#ifndef _LINUX_SERIAL_H
#define _LINUX_SERIAL_H
-#ifdef __KERNEL__
#include <linux/types.h>
+
+#ifdef __KERNEL__
#include <asm/page.h>
/*
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 4e4f1277f3bf..74ddc941e640 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -155,9 +155,21 @@
#define PORT_SC26XX 82
+/* NetSilicon 921x */
+#define PORT_NS921X 83
+
+/* Digi ns9360 */
+#define PORT_NS9360 84
+
/* SH-SCI */
#define PORT_SCIFA 83
+/* Freescale Semiconductor MXC fmaily */
+#define PORT_MXC 84
+
+/* STMP37xx ports */
+#define PORT_STMP37xx 85
+
#ifdef __KERNEL__
#include <linux/compiler.h>
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 2725f4e5a9bf..0b92223af697 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -250,6 +250,9 @@ typedef unsigned char *sk_buff_data_t;
* @tc_verd: traffic control verdict
* @ndisc_nodetype: router type (from link layer)
* @do_not_encrypt: set to prevent encryption of this frame
+ * @requeue: set to indicate that the wireless core should attempt
+ * a software retry on this frame if we failed to
+ * receive an ACK for it
* @dma_cookie: a cookie to one of several possible DMA operations
* done by skb DMA functions
* @secmark: security marking
@@ -325,6 +328,7 @@ struct sk_buff {
#endif
#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
__u8 do_not_encrypt:1;
+ __u8 requeue:1;
#endif
/* 0/13/14 bit hole */
diff --git a/include/linux/soundcard.h b/include/linux/soundcard.h
index 523d069c862c..5510ec6626e1 100644
--- a/include/linux/soundcard.h
+++ b/include/linux/soundcard.h
@@ -873,12 +873,16 @@ typedef struct copr_msg {
#define SOUND_MIXER_READ_LINE1 MIXER_READ(SOUND_MIXER_LINE1)
#define SOUND_MIXER_READ_LINE2 MIXER_READ(SOUND_MIXER_LINE2)
#define SOUND_MIXER_READ_LINE3 MIXER_READ(SOUND_MIXER_LINE3)
+#define SOUND_MIXER_READ_PHONEIN MIXER_READ(SOUND_MIXER_PHONEIN)
+#define SOUND_MIXER_READ_PHONEOUT MIXER_READ(SOUND_MIXER_PHONEOUT)
/* Obsolete macros */
#define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE)
#define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE)
#define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD)
+#define SOUND_MIXER_READ_OUTSRC MIXER_READ(SOUND_MIXER_OUTSRC)
+#define SOUND_MIXER_READ_OUTMASK MIXER_READ(SOUND_MIXER_OUTMASK)
#define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC)
#define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK)
#define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK)
@@ -903,6 +907,8 @@ typedef struct copr_msg {
#define SOUND_MIXER_WRITE_LINE1 MIXER_WRITE(SOUND_MIXER_LINE1)
#define SOUND_MIXER_WRITE_LINE2 MIXER_WRITE(SOUND_MIXER_LINE2)
#define SOUND_MIXER_WRITE_LINE3 MIXER_WRITE(SOUND_MIXER_LINE3)
+#define SOUND_MIXER_WRITE_PHONEIN MIXER_WRITE(SOUND_MIXER_PHONEIN)
+#define SOUND_MIXER_WRITE_PHONEOUT MIXER_WRITE(SOUND_MIXER_PHONEOUT)
/* Obsolete macros */
#define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE)
@@ -910,6 +916,7 @@ typedef struct copr_msg {
#define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD)
#define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC)
+#define SOUND_MIXER_WRITE_OUTSRC MIXER_WRITE(SOUND_MIXER_OUTSRC)
typedef struct mixer_info
{
diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h
index 05eab2f11e63..c87aa2680947 100644
--- a/include/linux/spi/ads7846.h
+++ b/include/linux/spi/ads7846.h
@@ -5,35 +5,17 @@
*
* It's OK if the min/max values are zero.
*/
-enum ads7846_filter {
- ADS7846_FILTER_OK,
- ADS7846_FILTER_REPEAT,
- ADS7846_FILTER_IGNORE,
-};
struct ads7846_platform_data {
u16 model; /* 7843, 7845, 7846. */
u16 vref_delay_usecs; /* 0 for external vref; etc */
- u16 vref_mv; /* external vref value, milliVolts */
- bool keep_vref_on; /* set to keep vref on for differential
- * measurements as well */
-
- /* Settling time of the analog signals; a function of Vcc and the
- * capacitance on the X/Y drivers. If set to non-zero, two samples
- * are taken with settle_delay us apart, and the second one is used.
- * ~150 uSec with 0.01uF caps.
- */
- u16 settle_delay_usecs;
-
- /* If set to non-zero, after samples are taken this delay is applied
- * and penirq is rechecked, to help avoid false events. This value
- * is affected by the material used to build the touch layer.
- */
- u16 penirq_recheck_delay_usecs;
-
u16 x_plate_ohms;
u16 y_plate_ohms;
+ u8 buflen;
+ u8 skip_samples;
+ u16 rotate;
+
u16 x_min, x_max;
u16 y_min, y_max;
u16 pressure_min, pressure_max;
@@ -43,13 +25,5 @@ struct ads7846_platform_data {
u16 debounce_tol; /* tolerance used for filtering */
u16 debounce_rep; /* additional consecutive good readings
* required after the first two */
- int gpio_pendown; /* the GPIO used to decide the pendown
- * state if get_pendown_state == NULL
- */
int (*get_pendown_state)(void);
- int (*filter_init) (struct ads7846_platform_data *pdata,
- void **filter_data);
- int (*filter) (void *filter_data, int data_idx, int *val);
- void (*filter_cleanup)(void *filter_data);
};
-
diff --git a/include/linux/spi_ns9360.h b/include/linux/spi_ns9360.h
new file mode 100644
index 000000000000..1bc7901efa8c
--- /dev/null
+++ b/include/linux/spi_ns9360.h
@@ -0,0 +1,22 @@
+/*
+ * include/linux/ns9360.h
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __SPI_NS9360_H__
+#define __SPI_NS9360_H__
+
+#define DRIVER_NAME "spi_ns9360"
+
+struct ns9360_data {
+ unsigned int *gpios;
+ unsigned int nr_gpios;
+};
+
+#endif
diff --git a/include/linux/squashfs_fs.h b/include/linux/squashfs_fs.h
new file mode 100644
index 000000000000..4cdd11f12bd7
--- /dev/null
+++ b/include/linux/squashfs_fs.h
@@ -0,0 +1,935 @@
+#ifndef SQUASHFS_FS
+#define SQUASHFS_FS
+
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * 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,
+ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs.h
+ */
+
+#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY
+#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
+#endif
+
+#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
+#define SQUASHFS_MAJOR 3
+#define SQUASHFS_MINOR 1
+#define SQUASHFS_MAGIC 0x73717368
+#define SQUASHFS_MAGIC_SWAP 0x68737173
+#define SQUASHFS_START 0
+
+/* size of metadata (inode and directory) blocks */
+#define SQUASHFS_METADATA_SIZE 8192
+#define SQUASHFS_METADATA_LOG 13
+
+/* default size of data blocks */
+#define SQUASHFS_FILE_SIZE 131072
+#define SQUASHFS_FILE_LOG 17
+
+#define SQUASHFS_FILE_MAX_SIZE 1048576
+
+/* Max number of uids and gids */
+#define SQUASHFS_UIDS 256
+#define SQUASHFS_GUIDS 255
+
+/* Max length of filename (not 255) */
+#define SQUASHFS_NAME_LEN 256
+
+#define SQUASHFS_INVALID ((long long) 0xffffffffffff)
+#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff)
+#define SQUASHFS_INVALID_BLK ((long long) -1)
+#define SQUASHFS_USED_BLK ((long long) -2)
+
+/* Filesystem flags */
+#define SQUASHFS_NOI 0
+#define SQUASHFS_NOD 1
+#define SQUASHFS_CHECK 2
+#define SQUASHFS_NOF 3
+#define SQUASHFS_NO_FRAG 4
+#define SQUASHFS_ALWAYS_FRAG 5
+#define SQUASHFS_DUPLICATE 6
+#define SQUASHFS_EXPORT 7
+
+#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
+
+#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
+ SQUASHFS_NOI)
+
+#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
+ SQUASHFS_NOD)
+
+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
+ SQUASHFS_NOF)
+
+#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
+ SQUASHFS_NO_FRAG)
+
+#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
+ SQUASHFS_ALWAYS_FRAG)
+
+#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
+ SQUASHFS_DUPLICATE)
+
+#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
+ SQUASHFS_EXPORT)
+
+#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \
+ SQUASHFS_CHECK)
+
+#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
+ duplicate_checking, exportable) (noi | (nod << 1) | (check_data << 2) \
+ | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
+ (duplicate_checking << 6) | (exportable << 7))
+
+/* Max number of types and file types */
+#define SQUASHFS_DIR_TYPE 1
+#define SQUASHFS_FILE_TYPE 2
+#define SQUASHFS_SYMLINK_TYPE 3
+#define SQUASHFS_BLKDEV_TYPE 4
+#define SQUASHFS_CHRDEV_TYPE 5
+#define SQUASHFS_FIFO_TYPE 6
+#define SQUASHFS_SOCKET_TYPE 7
+#define SQUASHFS_LDIR_TYPE 8
+#define SQUASHFS_LREG_TYPE 9
+
+/* 1.0 filesystem type definitions */
+#define SQUASHFS_TYPES 5
+#define SQUASHFS_IPC_TYPE 0
+
+/* Flag whether block is compressed or uncompressed, bit is set if block is
+ * uncompressed */
+#define SQUASHFS_COMPRESSED_BIT (1 << 15)
+
+#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
+ (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
+
+#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
+
+#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
+
+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \
+ ~SQUASHFS_COMPRESSED_BIT_BLOCK)
+
+#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
+
+/*
+ * Inode number ops. Inodes consist of a compressed block number, and an
+ * uncompressed offset within that block
+ */
+#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16))
+
+#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff))
+
+#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\
+ << 16) + (B)))
+
+/* Compute 32 bit VFS inode number from squashfs inode number */
+#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \
+ ((b) >> 2) + 1))
+/* XXX */
+
+/* Translate between VFS mode and squashfs mode */
+#define SQUASHFS_MODE(a) ((a) & 0xfff)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES(A) ((A) * sizeof(struct squashfs_fragment_entry))
+
+#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
+ SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
+ SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
+ SQUASHFS_METADATA_SIZE - 1) / \
+ SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
+ sizeof(long long))
+
+/* inode lookup table defines */
+#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(squashfs_inode_t))
+
+#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
+ SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
+ SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
+ SQUASHFS_METADATA_SIZE - 1) / \
+ SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
+ sizeof(long long))
+
+/* cached data constants for filesystem */
+#define SQUASHFS_CACHED_BLKS 8
+
+#define SQUASHFS_MAX_FILE_SIZE_LOG 64
+
+#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \
+ (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
+
+#define SQUASHFS_MARKER_BYTE 0xff
+
+/* meta index cache */
+#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
+#define SQUASHFS_META_ENTRIES 31
+#define SQUASHFS_META_NUMBER 8
+#define SQUASHFS_SLOTS 4
+
+struct meta_entry {
+ long long data_block;
+ unsigned int index_block;
+ unsigned short offset;
+ unsigned short pad;
+};
+
+struct meta_index {
+ unsigned int inode_number;
+ unsigned int offset;
+ unsigned short entries;
+ unsigned short skip;
+ unsigned short locked;
+ unsigned short pad;
+ struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
+};
+
+
+/*
+ * definitions for structures on disk
+ */
+
+typedef long long squashfs_block_t;
+typedef long long squashfs_inode_t;
+
+struct squashfs_super_block {
+ unsigned int s_magic;
+ unsigned int inodes;
+ unsigned int bytes_used_2;
+ unsigned int uid_start_2;
+ unsigned int guid_start_2;
+ unsigned int inode_table_start_2;
+ unsigned int directory_table_start_2;
+ unsigned int s_major:16;
+ unsigned int s_minor:16;
+ unsigned int block_size_1:16;
+ unsigned int block_log:16;
+ unsigned int flags:8;
+ unsigned int no_uids:8;
+ unsigned int no_guids:8;
+ unsigned int mkfs_time /* time of filesystem creation */;
+ squashfs_inode_t root_inode;
+ unsigned int block_size;
+ unsigned int fragments;
+ unsigned int fragment_table_start_2;
+ long long bytes_used;
+ long long uid_start;
+ long long guid_start;
+ long long inode_table_start;
+ long long directory_table_start;
+ long long fragment_table_start;
+ long long lookup_table_start;
+} __attribute__ ((packed));
+
+struct squashfs_dir_index {
+ unsigned int index;
+ unsigned int start_block;
+ unsigned char size;
+ unsigned char name[0];
+} __attribute__ ((packed));
+
+#define SQUASHFS_BASE_INODE_HEADER \
+ unsigned int inode_type:4; \
+ unsigned int mode:12; \
+ unsigned int uid:8; \
+ unsigned int guid:8; \
+ unsigned int mtime; \
+ unsigned int inode_number;
+
+struct squashfs_base_inode_header {
+ SQUASHFS_BASE_INODE_HEADER;
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header {
+ SQUASHFS_BASE_INODE_HEADER;
+ unsigned int nlink;
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header {
+ SQUASHFS_BASE_INODE_HEADER;
+ unsigned int nlink;
+ unsigned short rdev;
+} __attribute__ ((packed));
+
+struct squashfs_symlink_inode_header {
+ SQUASHFS_BASE_INODE_HEADER;
+ unsigned int nlink;
+ unsigned short symlink_size;
+ char symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header {
+ SQUASHFS_BASE_INODE_HEADER;
+ squashfs_block_t start_block;
+ unsigned int fragment;
+ unsigned int offset;
+ unsigned int file_size;
+ unsigned short block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_lreg_inode_header {
+ SQUASHFS_BASE_INODE_HEADER;
+ unsigned int nlink;
+ squashfs_block_t start_block;
+ unsigned int fragment;
+ unsigned int offset;
+ long long file_size;
+ unsigned short block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header {
+ SQUASHFS_BASE_INODE_HEADER;
+ unsigned int nlink;
+ unsigned int file_size:19;
+ unsigned int offset:13;
+ unsigned int start_block;
+ unsigned int parent_inode;
+} __attribute__ ((packed));
+
+struct squashfs_ldir_inode_header {
+ SQUASHFS_BASE_INODE_HEADER;
+ unsigned int nlink;
+ unsigned int file_size:27;
+ unsigned int offset:13;
+ unsigned int start_block;
+ unsigned int i_count:16;
+ unsigned int parent_inode;
+ struct squashfs_dir_index index[0];
+} __attribute__ ((packed));
+
+union squashfs_inode_header {
+ struct squashfs_base_inode_header base;
+ struct squashfs_dev_inode_header dev;
+ struct squashfs_symlink_inode_header symlink;
+ struct squashfs_reg_inode_header reg;
+ struct squashfs_lreg_inode_header lreg;
+ struct squashfs_dir_inode_header dir;
+ struct squashfs_ldir_inode_header ldir;
+ struct squashfs_ipc_inode_header ipc;
+};
+
+struct squashfs_dir_entry {
+ unsigned int offset:13;
+ unsigned int type:3;
+ unsigned int size:8;
+ int inode_number:16;
+ char name[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_header {
+ unsigned int count:8;
+ unsigned int start_block;
+ unsigned int inode_number;
+} __attribute__ ((packed));
+
+struct squashfs_fragment_entry {
+ long long start_block;
+ unsigned int size;
+ unsigned int unused;
+} __attribute__ ((packed));
+
+extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
+extern int squashfs_uncompress_init(void);
+extern int squashfs_uncompress_exit(void);
+
+/*
+ * macros to convert each packed bitfield structure from little endian to big
+ * endian and vice versa. These are needed when creating or using a filesystem
+ * on a machine with different byte ordering to the target architecture.
+ *
+ */
+
+#define SQUASHFS_SWAP_START \
+ int bits;\
+ int b_pos;\
+ unsigned long long val;\
+ unsigned char *s;\
+ unsigned char *d;
+
+#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
+ SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
+ SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
+ SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
+ SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
+ SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
+ SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
+ SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
+ SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
+ SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
+ SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
+ SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
+ SQUASHFS_SWAP((s)->flags, d, 288, 8);\
+ SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
+ SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
+ SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
+ SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
+ SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
+ SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
+ SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
+ SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
+ SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
+ SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
+ SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
+ SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
+ SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
+ SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\
+}
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
+ SQUASHFS_MEMSET(s, d, n);\
+ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+ SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+ SQUASHFS_SWAP((s)->uid, d, 16, 8);\
+ SQUASHFS_SWAP((s)->guid, d, 24, 8);\
+ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
+ SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+ sizeof(struct squashfs_ipc_inode_header))\
+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+}
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+ sizeof(struct squashfs_dev_inode_header)); \
+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+ SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+ sizeof(struct squashfs_symlink_inode_header));\
+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+ SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+ sizeof(struct squashfs_reg_inode_header));\
+ SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
+ SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
+ SQUASHFS_SWAP((s)->offset, d, 192, 32);\
+ SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
+}
+
+#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+ sizeof(struct squashfs_lreg_inode_header));\
+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+ SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
+ SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
+ SQUASHFS_SWAP((s)->offset, d, 224, 32);\
+ SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+ sizeof(struct squashfs_dir_inode_header));\
+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+ SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
+ SQUASHFS_SWAP((s)->offset, d, 147, 13);\
+ SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
+ SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
+}
+
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+ sizeof(struct squashfs_ldir_inode_header));\
+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+ SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
+ SQUASHFS_SWAP((s)->offset, d, 155, 13);\
+ SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
+ SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
+ SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
+ SQUASHFS_SWAP((s)->index, d, 0, 32);\
+ SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
+ SQUASHFS_SWAP((s)->size, d, 64, 8);\
+}
+
+#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
+ SQUASHFS_SWAP((s)->count, d, 0, 8);\
+ SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
+ SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
+ SQUASHFS_SWAP((s)->offset, d, 0, 13);\
+ SQUASHFS_SWAP((s)->type, d, 13, 3);\
+ SQUASHFS_SWAP((s)->size, d, 16, 8);\
+ SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
+ SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
+ SQUASHFS_SWAP((s)->size, d, 64, 32);\
+}
+
+#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
+
+#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
+ int entry;\
+ int bit_position;\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, n * 2);\
+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+ 16)\
+ SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
+}
+
+#define SQUASHFS_SWAP_INTS(s, d, n) {\
+ int entry;\
+ int bit_position;\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, n * 4);\
+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+ 32)\
+ SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
+}
+
+#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
+ int entry;\
+ int bit_position;\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, n * 8);\
+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+ 64)\
+ SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
+}
+
+#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
+ int entry;\
+ int bit_position;\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, n * bits / 8);\
+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+ bits)\
+ SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+
+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+
+struct squashfs_base_inode_header_1 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:4; /* index into uid table */
+ unsigned int guid:4; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_1 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:4; /* index into uid table */
+ unsigned int guid:4; /* index into guid table */
+ unsigned int type:4;
+ unsigned int offset:4;
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_1 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:4; /* index into uid table */
+ unsigned int guid:4; /* index into guid table */
+ unsigned short rdev;
+} __attribute__ ((packed));
+
+struct squashfs_symlink_inode_header_1 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:4; /* index into uid table */
+ unsigned int guid:4; /* index into guid table */
+ unsigned short symlink_size;
+ char symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_1 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:4; /* index into uid table */
+ unsigned int guid:4; /* index into guid table */
+ unsigned int mtime;
+ unsigned int start_block;
+ unsigned int file_size:32;
+ unsigned short block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_1 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:4; /* index into uid table */
+ unsigned int guid:4; /* index into guid table */
+ unsigned int file_size:19;
+ unsigned int offset:13;
+ unsigned int mtime;
+ unsigned int start_block:24;
+} __attribute__ ((packed));
+
+union squashfs_inode_header_1 {
+ struct squashfs_base_inode_header_1 base;
+ struct squashfs_dev_inode_header_1 dev;
+ struct squashfs_symlink_inode_header_1 symlink;
+ struct squashfs_reg_inode_header_1 reg;
+ struct squashfs_dir_inode_header_1 dir;
+ struct squashfs_ipc_inode_header_1 ipc;
+};
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
+ SQUASHFS_MEMSET(s, d, n);\
+ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+ SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+ SQUASHFS_SWAP((s)->uid, d, 16, 4);\
+ SQUASHFS_SWAP((s)->guid, d, 20, 4);
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+ sizeof(struct squashfs_ipc_inode_header_1));\
+ SQUASHFS_SWAP((s)->type, d, 24, 4);\
+ SQUASHFS_SWAP((s)->offset, d, 28, 4);\
+}
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+ sizeof(struct squashfs_dev_inode_header_1));\
+ SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+ sizeof(struct squashfs_symlink_inode_header_1));\
+ SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+ sizeof(struct squashfs_reg_inode_header_1));\
+ SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
+ SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
+ SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+ sizeof(struct squashfs_dir_inode_header_1));\
+ SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
+ SQUASHFS_SWAP((s)->offset, d, 43, 13);\
+ SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
+ SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
+}
+
+#endif
+
+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
+
+struct squashfs_dir_index_2 {
+ unsigned int index:27;
+ unsigned int start_block:29;
+ unsigned char size;
+ unsigned char name[0];
+} __attribute__ ((packed));
+
+struct squashfs_base_inode_header_2 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:8; /* index into uid table */
+ unsigned int guid:8; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_2 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:8; /* index into uid table */
+ unsigned int guid:8; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_2 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:8; /* index into uid table */
+ unsigned int guid:8; /* index into guid table */
+ unsigned short rdev;
+} __attribute__ ((packed));
+
+struct squashfs_symlink_inode_header_2 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:8; /* index into uid table */
+ unsigned int guid:8; /* index into guid table */
+ unsigned short symlink_size;
+ char symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_2 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:8; /* index into uid table */
+ unsigned int guid:8; /* index into guid table */
+ unsigned int mtime;
+ unsigned int start_block;
+ unsigned int fragment;
+ unsigned int offset;
+ unsigned int file_size:32;
+ unsigned short block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_2 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:8; /* index into uid table */
+ unsigned int guid:8; /* index into guid table */
+ unsigned int file_size:19;
+ unsigned int offset:13;
+ unsigned int mtime;
+ unsigned int start_block:24;
+} __attribute__ ((packed));
+
+struct squashfs_ldir_inode_header_2 {
+ unsigned int inode_type:4;
+ unsigned int mode:12; /* protection */
+ unsigned int uid:8; /* index into uid table */
+ unsigned int guid:8; /* index into guid table */
+ unsigned int file_size:27;
+ unsigned int offset:13;
+ unsigned int mtime;
+ unsigned int start_block:24;
+ unsigned int i_count:16;
+ struct squashfs_dir_index_2 index[0];
+} __attribute__ ((packed));
+
+union squashfs_inode_header_2 {
+ struct squashfs_base_inode_header_2 base;
+ struct squashfs_dev_inode_header_2 dev;
+ struct squashfs_symlink_inode_header_2 symlink;
+ struct squashfs_reg_inode_header_2 reg;
+ struct squashfs_dir_inode_header_2 dir;
+ struct squashfs_ldir_inode_header_2 ldir;
+ struct squashfs_ipc_inode_header_2 ipc;
+};
+
+struct squashfs_dir_header_2 {
+ unsigned int count:8;
+ unsigned int start_block:24;
+} __attribute__ ((packed));
+
+struct squashfs_dir_entry_2 {
+ unsigned int offset:13;
+ unsigned int type:3;
+ unsigned int size:8;
+ char name[0];
+} __attribute__ ((packed));
+
+struct squashfs_fragment_entry_2 {
+ unsigned int start_block;
+ unsigned int size;
+} __attribute__ ((packed));
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
+ SQUASHFS_MEMSET(s, d, n);\
+ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+ SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+ SQUASHFS_SWAP((s)->uid, d, 16, 8);\
+ SQUASHFS_SWAP((s)->guid, d, 24, 8);\
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
+ SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+ sizeof(struct squashfs_dev_inode_header_2)); \
+ SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+ sizeof(struct squashfs_symlink_inode_header_2));\
+ SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+ sizeof(struct squashfs_reg_inode_header_2));\
+ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
+ SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
+ SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
+ SQUASHFS_SWAP((s)->offset, d, 128, 32);\
+ SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+ sizeof(struct squashfs_dir_inode_header_2));\
+ SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
+ SQUASHFS_SWAP((s)->offset, d, 51, 13);\
+ SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
+ SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
+}
+
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+ sizeof(struct squashfs_ldir_inode_header_2));\
+ SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
+ SQUASHFS_SWAP((s)->offset, d, 59, 13);\
+ SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
+ SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
+ SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
+ SQUASHFS_SWAP((s)->index, d, 0, 27);\
+ SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
+ SQUASHFS_SWAP((s)->size, d, 56, 8);\
+}
+#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
+ SQUASHFS_SWAP((s)->count, d, 0, 8);\
+ SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
+}
+
+#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
+ SQUASHFS_SWAP((s)->offset, d, 0, 13);\
+ SQUASHFS_SWAP((s)->type, d, 13, 3);\
+ SQUASHFS_SWAP((s)->size, d, 16, 8);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
+ SQUASHFS_SWAP_START\
+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
+ SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
+ SQUASHFS_SWAP((s)->size, d, 32, 32);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2))
+
+#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \
+ SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \
+ SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
+ SQUASHFS_METADATA_SIZE - 1) / \
+ SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
+ sizeof(int))
+
+#endif
+
+#ifdef __KERNEL__
+
+/*
+ * macros used to swap each structure entry, taking into account
+ * bitfields and different bitfield placing conventions on differing
+ * architectures
+ */
+
+#include <asm/byteorder.h>
+
+#ifdef __BIG_ENDIAN
+ /* convert from little endian to big endian */
+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
+ tbits, b_pos)
+#else
+ /* convert from big endian to little endian */
+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
+ tbits, 64 - tbits - b_pos)
+#endif
+
+#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
+ b_pos = pos % 8;\
+ val = 0;\
+ s = (unsigned char *)p + (pos / 8);\
+ d = ((unsigned char *) &val) + 7;\
+ for(bits = 0; bits < (tbits + b_pos); bits += 8) \
+ *d-- = *s++;\
+ value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
+}
+
+#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n);
+
+#endif
+#endif
diff --git a/include/linux/squashfs_fs_i.h b/include/linux/squashfs_fs_i.h
new file mode 100644
index 000000000000..b41c13ccb1d4
--- /dev/null
+++ b/include/linux/squashfs_fs_i.h
@@ -0,0 +1,45 @@
+#ifndef SQUASHFS_FS_I
+#define SQUASHFS_FS_I
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * 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,
+ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs_i.h
+ */
+
+struct squashfs_inode_info {
+ long long start_block;
+ unsigned int offset;
+ union {
+ struct {
+ long long fragment_start_block;
+ unsigned int fragment_size;
+ unsigned int fragment_offset;
+ long long block_list_start;
+ } s1;
+ struct {
+ long long directory_index_start;
+ unsigned int directory_index_offset;
+ unsigned int directory_index_count;
+ unsigned int parent_inode;
+ } s2;
+ } u;
+ struct inode vfs_inode;
+};
+#endif
diff --git a/include/linux/squashfs_fs_sb.h b/include/linux/squashfs_fs_sb.h
new file mode 100644
index 000000000000..75c371695751
--- /dev/null
+++ b/include/linux/squashfs_fs_sb.h
@@ -0,0 +1,79 @@
+#ifndef SQUASHFS_FS_SB
+#define SQUASHFS_FS_SB
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * 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,
+ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs_sb.h
+ */
+
+#include <linux/squashfs_fs.h>
+
+struct squashfs_cache_entry {
+ long long block;
+ int length;
+ int locked;
+ long long next_index;
+ char pending;
+ char error;
+ int waiting;
+ wait_queue_head_t wait_queue;
+ char *data;
+};
+
+struct squashfs_cache {
+ char *name;
+ int entries;
+ int block_size;
+ int next_blk;
+ int waiting;
+ int unused_blks;
+ int use_vmalloc;
+ spinlock_t lock;
+ wait_queue_head_t wait_queue;
+ struct squashfs_cache_entry entry[0];
+};
+
+struct squashfs_sb_info {
+ struct squashfs_super_block sblk;
+ int devblksize;
+ int devblksize_log2;
+ int swap;
+ struct squashfs_cache *block_cache;
+ struct squashfs_cache *fragment_cache;
+ int next_meta_index;
+ unsigned int *uid;
+ unsigned int *guid;
+ long long *fragment_index;
+ unsigned int *fragment_index_2;
+ char *read_page;
+ struct mutex read_data_mutex;
+ struct mutex read_page_mutex;
+ struct mutex meta_index_mutex;
+ struct meta_index *meta_index;
+ z_stream stream;
+ long long *inode_lookup_table;
+ int (*read_inode)(struct inode *i, squashfs_inode_t \
+ inode);
+ long long (*read_blocklist)(struct inode *inode, int \
+ index, int readahead_blks, char *block_list, \
+ unsigned short **block_p, unsigned int *bsize);
+ int (*read_fragment_index_table)(struct super_block *s);
+};
+#endif
diff --git a/include/linux/usb.h b/include/linux/usb.h
index f72aa51f7bcd..2c1622706800 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1349,6 +1349,9 @@ struct urb {
usb_complete_t complete; /* (in) completion routine */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/* (in) ISO ONLY */
+#ifdef CONFIG_USB_STATIC_IRAM
+ int use_iram;
+#endif
};
/* ----------------------------------------------------------------------- */
diff --git a/include/linux/usb/fsl_xcvr.h b/include/linux/usb/fsl_xcvr.h
new file mode 100644
index 000000000000..9a48bb61834c
--- /dev/null
+++ b/include/linux/usb/fsl_xcvr.h
@@ -0,0 +1,44 @@
+#ifndef __LINUX_USB_FSL_XCVR_H
+#define __LINUX_USB_FSL_XCVR_H
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+struct fsl_usb2_platform_data;
+
+enum usb_test_mode{
+ USB_TEST_J = 1,
+ USB_TEST_K = 2,
+};
+
+/**
+ * @name: transceiver name
+ * @xcvr_type: one of PORTSC_PTS_{UTMI,SERIAL,ULPI}
+ * @init: transceiver- and board-specific initialization function
+ * @uninit: transceiver- and board-specific uninitialization function
+ * @set_host:
+ * @set_device:
+ * @pullup: enable or disable D+ pullup
+ *
+ */
+struct fsl_xcvr_ops {
+ char *name;
+ u32 xcvr_type;
+
+ void (*init)(struct fsl_xcvr_ops *ops);
+ void (*uninit)(struct fsl_xcvr_ops *ops);
+ void (*suspend)(struct fsl_xcvr_ops *ops);
+ void (*set_host)(void);
+ void (*set_device)(void);
+ void (*set_vbus_power)(struct fsl_xcvr_ops *ops,
+ struct fsl_usb2_platform_data *pdata, int on);
+ void (*set_remote_wakeup)(u32 *view);
+ void (*pullup)(int on);
+ void(*set_test_mode)(u32 *view, enum usb_test_mode mode);
+};
+
+struct fsl_xcvr_power {
+ struct platform_device *usb_pdev;
+ struct regulator *regu1;
+ struct regulator *regu2;
+};
+#endif
diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h
index c6c61cd5a254..6539b66489a9 100644
--- a/include/mtd/mtd-abi.h
+++ b/include/mtd/mtd-abi.h
@@ -28,12 +28,13 @@ struct mtd_oob_buf {
#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */
#define MTD_NO_ERASE 0x1000 /* No erase necessary */
#define MTD_POWERUP_LOCK 0x2000 /* Always locked after reset */
+#define MTD_OOB_WRITEABLE 0x4000 /* Use Out-Of-Band area */
// Some common devices / combinations of capabilities
#define MTD_CAP_ROM 0
#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
-#define MTD_CAP_NANDFLASH (MTD_WRITEABLE)
+#define MTD_CAP_NANDFLASH (MTD_WRITEABLE | MTD_OOB_WRITEABLE)
/* ECC byte placement */
#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 0e85ec39b638..23c0ab74ded6 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5,6 +5,8 @@
#include <linux/skbuff.h>
#include <linux/nl80211.h>
#include <net/genetlink.h>
+/* remove once we remove the wext stuff */
+#include <net/iw_handler.h>
/*
* 802.11 configuration in-kernel interface
@@ -167,6 +169,9 @@ struct station_parameters {
* @STATION_INFO_LLID: @llid filled
* @STATION_INFO_PLID: @plid filled
* @STATION_INFO_PLINK_STATE: @plink_state filled
+ * @STATION_INFO_SIGNAL: @signal filled
+ * @STATION_INFO_TX_BITRATE: @tx_bitrate fields are filled
+ * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
*/
enum station_info_flags {
STATION_INFO_INACTIVE_TIME = 1<<0,
@@ -175,6 +180,39 @@ enum station_info_flags {
STATION_INFO_LLID = 1<<3,
STATION_INFO_PLID = 1<<4,
STATION_INFO_PLINK_STATE = 1<<5,
+ STATION_INFO_SIGNAL = 1<<6,
+ STATION_INFO_TX_BITRATE = 1<<7,
+};
+
+/**
+ * enum station_info_rate_flags - bitrate info flags
+ *
+ * Used by the driver to indicate the specific rate transmission
+ * type for 802.11n transmissions.
+ *
+ * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled
+ * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission
+ * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval
+ */
+enum rate_info_flags {
+ RATE_INFO_FLAGS_MCS = 1<<0,
+ RATE_INFO_FLAGS_40_MHZ_WIDTH = 1<<1,
+ RATE_INFO_FLAGS_SHORT_GI = 1<<2,
+};
+
+/**
+ * struct rate_info - bitrate information
+ *
+ * Information about a receiving or transmitting bitrate
+ *
+ * @flags: bitflag of flags from &enum rate_info_flags
+ * @mcs: mcs index if struct describes a 802.11n bitrate
+ * @legacy: bitrate in 100kbit/s for 802.11abg
+ */
+struct rate_info {
+ u8 flags;
+ u8 mcs;
+ u16 legacy;
};
/**
@@ -189,6 +227,8 @@ enum station_info_flags {
* @llid: mesh local link id
* @plid: mesh peer link id
* @plink_state: mesh peer link state
+ * @signal: signal strength of last received packet in dBm
+ * @txrate: current unicast bitrate to this station
*/
struct station_info {
u32 filled;
@@ -198,6 +238,8 @@ struct station_info {
u16 llid;
u16 plid;
u8 plink_state;
+ s8 signal;
+ struct rate_info txrate;
};
/**
@@ -280,11 +322,16 @@ struct mpath_info {
* (0 = no, 1 = yes, -1 = do not change)
* @use_short_slot_time: Whether the use of short slot time is allowed
* (0 = no, 1 = yes, -1 = do not change)
+ * @basic_rates: basic rates in IEEE 802.11 format
+ * (or NULL for no change)
+ * @basic_rates_len: number of basic rates
*/
struct bss_parameters {
int use_cts_prot;
int use_short_preamble;
int use_short_slot_time;
+ u8 *basic_rates;
+ u8 basic_rates_len;
};
/**
@@ -331,25 +378,65 @@ struct ieee80211_regdomain {
struct ieee80211_reg_rule reg_rules[];
};
-#define MHZ_TO_KHZ(freq) (freq * 1000)
-#define KHZ_TO_MHZ(freq) (freq / 1000)
-#define DBI_TO_MBI(gain) (gain * 100)
-#define MBI_TO_DBI(gain) (gain / 100)
-#define DBM_TO_MBM(gain) (gain * 100)
-#define MBM_TO_DBM(gain) (gain / 100)
+#define MHZ_TO_KHZ(freq) ((freq) * 1000)
+#define KHZ_TO_MHZ(freq) ((freq) / 1000)
+#define DBI_TO_MBI(gain) ((gain) * 100)
+#define MBI_TO_DBI(gain) ((gain) / 100)
+#define DBM_TO_MBM(gain) ((gain) * 100)
+#define MBM_TO_DBM(gain) ((gain) / 100)
#define REG_RULE(start, end, bw, gain, eirp, reg_flags) { \
- .freq_range.start_freq_khz = (start) * 1000, \
- .freq_range.end_freq_khz = (end) * 1000, \
- .freq_range.max_bandwidth_khz = (bw) * 1000, \
- .power_rule.max_antenna_gain = (gain) * 100, \
- .power_rule.max_eirp = (eirp) * 100, \
+ .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \
+ .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \
+ .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \
+ .power_rule.max_antenna_gain = DBI_TO_MBI(gain), \
+ .power_rule.max_eirp = DBM_TO_MBM(eirp), \
.flags = reg_flags, \
}
+struct mesh_config {
+ /* Timeouts in ms */
+ /* Mesh plink management parameters */
+ u16 dot11MeshRetryTimeout;
+ u16 dot11MeshConfirmTimeout;
+ u16 dot11MeshHoldingTimeout;
+ u16 dot11MeshMaxPeerLinks;
+ u8 dot11MeshMaxRetries;
+ u8 dot11MeshTTL;
+ bool auto_open_plinks;
+ /* HWMP parameters */
+ u8 dot11MeshHWMPmaxPREQretries;
+ u32 path_refresh_time;
+ u16 min_discovery_timeout;
+ u32 dot11MeshHWMPactivePathTimeout;
+ u16 dot11MeshHWMPpreqMinInterval;
+ u16 dot11MeshHWMPnetDiameterTraversalTime;
+};
+
+/**
+ * struct ieee80211_txq_params - TX queue parameters
+ * @queue: TX queue identifier (NL80211_TXQ_Q_*)
+ * @txop: Maximum burst time in units of 32 usecs, 0 meaning disabled
+ * @cwmin: Minimum contention window [a value of the form 2^n-1 in the range
+ * 1..32767]
+ * @cwmax: Maximum contention window [a value of the form 2^n-1 in the range
+ * 1..32767]
+ * @aifs: Arbitration interframe space [0..255]
+ */
+struct ieee80211_txq_params {
+ enum nl80211_txq_q queue;
+ u16 txop;
+ u16 cwmin;
+ u16 cwmax;
+ u8 aifs;
+};
+
/* from net/wireless.h */
struct wiphy;
+/* from net/ieee80211.h */
+struct ieee80211_channel;
+
/**
* struct cfg80211_ops - backend description for wireless configuration
*
@@ -397,9 +484,19 @@ struct wiphy;
*
* @change_station: Modify a given station.
*
+ * @get_mesh_params: Put the current mesh parameters into *params
+ *
+ * @set_mesh_params: Set mesh parameters.
+ * The mask is a bitfield which tells us which parameters to
+ * set, and which to leave alone.
+ *
* @set_mesh_cfg: set mesh parameters (by now, just mesh id)
*
* @change_bss: Modify parameters for a given BSS.
+ *
+ * @set_txq_params: Set TX queue parameters
+ *
+ * @set_channel: Set channel
*/
struct cfg80211_ops {
int (*add_virtual_intf)(struct wiphy *wiphy, char *name,
@@ -452,9 +549,30 @@ struct cfg80211_ops {
int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *dst, u8 *next_hop,
struct mpath_info *pinfo);
-
+ int (*get_mesh_params)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct mesh_config *conf);
+ int (*set_mesh_params)(struct wiphy *wiphy,
+ struct net_device *dev,
+ const struct mesh_config *nconf, u32 mask);
int (*change_bss)(struct wiphy *wiphy, struct net_device *dev,
struct bss_parameters *params);
+
+ int (*set_txq_params)(struct wiphy *wiphy,
+ struct ieee80211_txq_params *params);
+
+ int (*set_channel)(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type);
};
+/* temporary wext handlers */
+int cfg80211_wext_giwname(struct net_device *dev,
+ struct iw_request_info *info,
+ char *name, char *extra);
+int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra);
+int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra);
+
#endif /* __NET_CFG80211_H */
diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h
index 93a56de3594b..06e1493fab14 100644
--- a/include/net/ieee80211.h
+++ b/include/net/ieee80211.h
@@ -1258,6 +1258,9 @@ extern int ieee80211_set_encryption(struct ieee80211_device *ieee);
/* ieee80211_tx.c */
extern int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev);
extern void ieee80211_txb_free(struct ieee80211_txb *);
+extern int ieee80211_tx_frame(struct ieee80211_device *ieee,
+ struct ieee80211_hdr *frame, int hdr_len,
+ int total_len, int encrypt_mpdu);
/* ieee80211_rx.c */
extern void ieee80211_rx_any(struct ieee80211_device *ieee,
@@ -1305,6 +1308,14 @@ extern int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee,
extern int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra);
+extern int ieee80211_wx_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra);
+extern int ieee80211_wx_get_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra);
static inline void ieee80211_increment_scans(struct ieee80211_device *ieee)
{
diff --git a/include/net/ieee80211softmac.h b/include/net/ieee80211softmac.h
new file mode 100644
index 000000000000..fa76ecdf4d61
--- /dev/null
+++ b/include/net/ieee80211softmac.h
@@ -0,0 +1,394 @@
+/*
+ * ieee80211softmac.h - public interface to the softmac
+ *
+ * Copyright (c) 2005 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * 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, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR 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
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#ifndef IEEE80211SOFTMAC_H_
+#define IEEE80211SOFTMAC_H_
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/list.h>
+#include <net/ieee80211.h>
+
+/* Once the API is considered more or less stable,
+ * this should be incremented on API incompatible changes.
+ */
+#define IEEE80211SOFTMAC_API 0
+
+#define IEEE80211SOFTMAC_MAX_RATES_LEN 8
+#define IEEE80211SOFTMAC_MAX_EX_RATES_LEN 255
+
+struct ieee80211softmac_ratesinfo {
+ u8 count;
+ u8 rates[IEEE80211SOFTMAC_MAX_RATES_LEN + IEEE80211SOFTMAC_MAX_EX_RATES_LEN];
+};
+
+/* internal structures */
+struct ieee80211softmac_network;
+struct ieee80211softmac_scaninfo;
+
+struct ieee80211softmac_essid {
+ u8 len;
+ char data[IW_ESSID_MAX_SIZE+1];
+};
+
+struct ieee80211softmac_wpa {
+ char *IE;
+ int IElen;
+ int IEbuflen;
+};
+
+/*
+ * Information about association
+ */
+struct ieee80211softmac_assoc_info {
+
+ struct mutex mutex;
+
+ /*
+ * This is the requested ESSID. It is written
+ * only by the WX handlers.
+ *
+ */
+ struct ieee80211softmac_essid req_essid;
+ /*
+ * the ESSID of the network we're currently
+ * associated (or trying) to. This is
+ * updated to the network's actual ESSID
+ * even if the requested ESSID was 'ANY'
+ */
+ struct ieee80211softmac_essid associate_essid;
+
+ /* BSSID we're trying to associate to */
+ char bssid[ETH_ALEN];
+
+ /* some flags.
+ * static_essid is valid if the essid is constant,
+ * this is for use by the wx handlers only.
+ *
+ * associating is true, if the network has been
+ * auth'ed on and we are in the process of associating.
+ *
+ * bssvalid is true if we found a matching network
+ * and saved it's BSSID into the bssid above.
+ *
+ * bssfixed is used for SIOCSIWAP.
+ */
+ u8 static_essid;
+ u8 short_preamble_available;
+ u8 associating;
+ u8 associated;
+ u8 assoc_wait;
+ u8 bssvalid;
+ u8 bssfixed;
+
+ u8 channel; /* channel the association was sent on */
+
+ /* Scan retries remaining */
+ int scan_retry;
+
+ // PPH struct work_struct work;
+ // PPH struct work_struct timeout;
+ struct delayed_work work;
+ struct delayed_work timeout;
+
+};
+
+struct ieee80211softmac_bss_info {
+ /* Rates supported by the network */
+ struct ieee80211softmac_ratesinfo supported_rates;
+
+ /* This indicates whether frames can currently be transmitted with
+ * short preamble (only use this variable during TX at CCK rates) */
+ u8 short_preamble:1;
+
+ /* This indicates whether protection (e.g. self-CTS) should be used
+ * when transmitting with OFDM modulation */
+ u8 use_protection:1;
+};
+
+enum {
+ IEEE80211SOFTMAC_AUTH_OPEN_REQUEST = 1,
+ IEEE80211SOFTMAC_AUTH_OPEN_RESPONSE = 2,
+};
+
+enum {
+ IEEE80211SOFTMAC_AUTH_SHARED_REQUEST = 1,
+ IEEE80211SOFTMAC_AUTH_SHARED_CHALLENGE = 2,
+ IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE = 3,
+ IEEE80211SOFTMAC_AUTH_SHARED_PASS = 4,
+};
+
+/* We should make these tunable
+ * AUTH_TIMEOUT seems really long, but that's what it is in BSD */
+#define IEEE80211SOFTMAC_AUTH_TIMEOUT (12 * HZ)
+#define IEEE80211SOFTMAC_AUTH_RETRY_LIMIT 5
+#define IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT 3
+
+struct ieee80211softmac_txrates {
+ /* The Bit-Rate to be used for multicast frames. */
+ u8 mcast_rate;
+
+ /* The Bit-Rate to be used for multicast management frames. */
+ u8 mgt_mcast_rate;
+
+ /* The Bit-Rate to be used for any other (normal) data packet. */
+ u8 default_rate;
+ /* The Bit-Rate to be used for default fallback
+ * (If the device supports fallback and hardware-retry)
+ */
+ u8 default_fallback;
+
+ /* This is the rate that the user asked for */
+ u8 user_rate;
+};
+
+/* Bits for txrates_change callback. */
+#define IEEE80211SOFTMAC_TXRATECHG_DEFAULT (1 << 0) /* default_rate */
+#define IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK (1 << 1) /* default_fallback */
+#define IEEE80211SOFTMAC_TXRATECHG_MCAST (1 << 2) /* mcast_rate */
+#define IEEE80211SOFTMAC_TXRATECHG_MGT_MCAST (1 << 3) /* mgt_mcast_rate */
+
+#define IEEE80211SOFTMAC_BSSINFOCHG_RATES (1 << 0) /* supported_rates */
+#define IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE (1 << 1) /* short_preamble */
+#define IEEE80211SOFTMAC_BSSINFOCHG_PROTECTION (1 << 2) /* use_protection */
+
+struct ieee80211softmac_device {
+ /* 802.11 structure for data stuff */
+ struct ieee80211_device *ieee;
+ struct net_device *dev;
+
+ /* only valid if associated, then holds the Association ID */
+ u16 association_id;
+
+ /* the following methods are callbacks that the driver
+ * using this framework has to assign
+ */
+
+ /* always assign these */
+ void (*set_bssid_filter)(struct net_device *dev, const u8 *bssid);
+ void (*set_channel)(struct net_device *dev, u8 channel);
+
+ /* assign if you need it, informational only */
+ void (*link_change)(struct net_device *dev);
+
+ /* If the hardware can do scanning, assign _all_ three of these callbacks.
+ * When the scan finishes, call ieee80211softmac_scan_finished().
+ */
+
+ /* when called, start_scan is guaranteed to not be called again
+ * until you call ieee80211softmac_scan_finished.
+ * Return 0 if scanning could start, error otherwise.
+ * SOFTMAC AUTHORS: don't call this, use ieee80211softmac_start_scan */
+ int (*start_scan)(struct net_device *dev);
+ /* this should block until after ieee80211softmac_scan_finished was called
+ * SOFTMAC AUTHORS: don't call this, use ieee80211softmac_wait_for_scan */
+ void (*wait_for_scan)(struct net_device *dev);
+ /* stop_scan aborts a scan, but is asynchronous.
+ * if you want to wait for it too, use wait_for_scan
+ * SOFTMAC AUTHORS: don't call this, use ieee80211softmac_stop_scan */
+ void (*stop_scan)(struct net_device *dev);
+
+ /* we'll need something about beacons here too, for AP or ad-hoc modes */
+
+ /* Transmission rates to be used by the driver.
+ * The SoftMAC figures out the best possible rates.
+ * The driver just needs to read them.
+ */
+ struct ieee80211softmac_txrates txrates;
+
+ /* If the driver needs to do stuff on TX rate changes, assign this
+ * callback. See IEEE80211SOFTMAC_TXRATECHG for change flags. */
+ void (*txrates_change)(struct net_device *dev,
+ u32 changes);
+
+ /* If the driver needs to do stuff when BSS properties change, assign
+ * this callback. see IEEE80211SOFTMAC_BSSINFOCHG for change flags. */
+ void (*bssinfo_change)(struct net_device *dev,
+ u32 changes);
+
+ /* private stuff follows */
+ /* this lock protects this structure */
+ spinlock_t lock;
+
+//PPH
+ struct workqueue_struct *wq;
+
+
+ u8 running; /* SoftMAC started? */
+ u8 scanning;
+
+ struct ieee80211softmac_scaninfo *scaninfo;
+ struct ieee80211softmac_assoc_info associnfo;
+ struct ieee80211softmac_bss_info bssinfo;
+
+ struct list_head auth_queue;
+ struct list_head events;
+
+ struct ieee80211softmac_ratesinfo ratesinfo;
+ int txrate_badness;
+
+ /* WPA stuff */
+ struct ieee80211softmac_wpa wpa;
+
+ /* we need to keep a list of network structs we copied */
+ struct list_head network_list;
+
+ struct {
+ volatile int had_probe_resp;
+ volatile int wait_cycles_left;
+ } scan;
+
+
+ /* This must be the last item so that it points to the data
+ * allocated beyond this structure by alloc_ieee80211 */
+ u8 priv[0];
+};
+
+extern void ieee80211softmac_scan_finished(struct ieee80211softmac_device *sm);
+
+static inline void * ieee80211softmac_priv(struct net_device *dev)
+{
+ return ((struct ieee80211softmac_device *)ieee80211_priv(dev))->priv;
+}
+
+extern struct net_device * alloc_ieee80211softmac(int sizeof_priv);
+extern void free_ieee80211softmac(struct net_device *dev);
+
+/* Call this function if you detect a lost TX fragment.
+ * (If the device indicates failure of ACK RX, for example.)
+ * It is wise to call this function if you are able to detect lost packets,
+ * because it contributes to the TX Rates auto adjustment.
+ */
+extern void ieee80211softmac_fragment_lost(struct net_device *dev,
+ u16 wireless_sequence_number);
+/* Call this function before _start to tell the softmac what rates
+ * the hw supports. The rates parameter is copied, so you can
+ * free it right after calling this function.
+ * Note that the rates need to be sorted. */
+extern void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates);
+
+/* Finds the highest rate which is:
+ * 1. Present in ri (optionally a basic rate)
+ * 2. Supported by the device
+ * 3. Less than or equal to the user-defined rate
+ */
+extern u8 ieee80211softmac_highest_supported_rate(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_ratesinfo *ri, int basic_only);
+
+/* Helper function which advises you the rate at which a frame should be
+ * transmitted at. */
+static inline u8 ieee80211softmac_suggest_txrate(struct ieee80211softmac_device *mac,
+ int is_multicast,
+ int is_mgt)
+{
+ struct ieee80211softmac_txrates *txrates = &mac->txrates;
+
+ if (!mac->associnfo.associated)
+ return txrates->mgt_mcast_rate;
+
+ /* We are associated, sending unicast frame */
+ if (!is_multicast)
+ return txrates->default_rate;
+
+ /* We are associated, sending multicast frame */
+ if (is_mgt)
+ return txrates->mgt_mcast_rate;
+ else
+ return txrates->mcast_rate;
+}
+
+/* Helper function which advises you when it is safe to transmit with short
+ * preamble.
+ * You should only call this function when transmitting at CCK rates. */
+static inline int ieee80211softmac_short_preamble_ok(struct ieee80211softmac_device *mac,
+ int is_multicast,
+ int is_mgt)
+{
+ return (is_multicast && is_mgt) ? 0 : mac->bssinfo.short_preamble;
+}
+
+/* Helper function which advises you whether protection (e.g. self-CTS) is
+ * needed. 1 = protection needed, 0 = no protection needed
+ * Only use this function when transmitting with OFDM modulation. */
+static inline int ieee80211softmac_protection_needed(struct ieee80211softmac_device *mac)
+{
+ return mac->bssinfo.use_protection;
+}
+
+/* Start the SoftMAC. Call this after you initialized the device
+ * and it is ready to run.
+ */
+extern void ieee80211softmac_start(struct net_device *dev);
+/* Stop the SoftMAC. Call this before you shutdown the device. */
+extern void ieee80211softmac_stop(struct net_device *dev);
+
+/*
+ * Event system
+ */
+
+/* valid event types */
+#define IEEE80211SOFTMAC_EVENT_ANY -1 /*private use only*/
+#define IEEE80211SOFTMAC_EVENT_SCAN_FINISHED 0
+#define IEEE80211SOFTMAC_EVENT_ASSOCIATED 1
+#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED 2
+#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT 3
+#define IEEE80211SOFTMAC_EVENT_AUTHENTICATED 4
+#define IEEE80211SOFTMAC_EVENT_AUTH_FAILED 5
+#define IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT 6
+#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_NET_NOT_FOUND 7
+#define IEEE80211SOFTMAC_EVENT_DISASSOCIATED 8
+/* keep this updated! */
+#define IEEE80211SOFTMAC_EVENT_LAST 8
+/*
+ * If you want to be notified of certain events, you can call
+ * ieee80211softmac_notify[_atomic] with
+ * - event set to one of the constants below
+ * - fun set to a function pointer of the appropriate type
+ * - context set to the context data you want passed
+ * The return value is 0, or an error.
+ */
+typedef void (*notify_function_ptr)(struct net_device *dev, int event_type, void *context);
+
+#define ieee80211softmac_notify(dev, event, fun, context) ieee80211softmac_notify_gfp(dev, event, fun, context, GFP_KERNEL);
+#define ieee80211softmac_notify_atomic(dev, event, fun, context) ieee80211softmac_notify_gfp(dev, event, fun, context, GFP_ATOMIC);
+
+extern int ieee80211softmac_notify_gfp(struct net_device *dev,
+ int event, notify_function_ptr fun, void *context, gfp_t gfp_mask);
+
+/* To clear pending work (for ifconfig down, etc.) */
+extern void
+ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm);
+
+extern struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_essid *essid);
+extern void
+ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *del_net);
+void ieee80211softmac_disassoc(struct ieee80211softmac_device *mac);
+
+#endif /* IEEE80211SOFTMAC_H_ */
diff --git a/include/net/ieee80211softmac_wx.h b/include/net/ieee80211softmac_wx.h
new file mode 100644
index 000000000000..4ee3ad57283f
--- /dev/null
+++ b/include/net/ieee80211softmac_wx.h
@@ -0,0 +1,99 @@
+/*
+ * This file contains the prototypes for the wireless extension
+ * handlers that the softmac API provides. Include this file to
+ * use the wx handlers, you can assign these directly.
+ *
+ * Copyright (c) 2005 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * 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, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR 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
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#ifndef _IEEE80211SOFTMAC_WX_H
+#define _IEEE80211SOFTMAC_WX_H
+
+#include <net/ieee80211softmac.h>
+#include <net/iw_handler.h>
+
+extern int
+ieee80211softmac_wx_trigger_scan(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra);
+
+extern int
+ieee80211softmac_wx_get_scan_results(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra);
+
+extern int
+ieee80211softmac_wx_set_essid(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra);
+
+extern int
+ieee80211softmac_wx_get_essid(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra);
+
+extern int
+ieee80211softmac_wx_set_rate(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra);
+
+extern int
+ieee80211softmac_wx_get_rate(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra);
+
+extern int
+ieee80211softmac_wx_get_wap(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra);
+
+extern int
+ieee80211softmac_wx_set_wap(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra);
+
+extern int
+ieee80211softmac_wx_set_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra);
+
+extern int
+ieee80211softmac_wx_get_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra);
+extern int
+ieee80211softmac_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra);
+#endif /* _IEEE80211SOFTMAC_WX */
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 73d81bc6aa75..559422fc0943 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -3,7 +3,7 @@
*
* Copyright 2002-2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
- * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net>
*
* 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
@@ -107,7 +107,7 @@ enum ieee80211_max_queues {
* The information provided in this structure is required for QoS
* transmit queue configuration. Cf. IEEE 802.11 7.3.2.29.
*
- * @aifs: arbitration interface space [0..255]
+ * @aifs: arbitration interframe space [0..255]
* @cw_min: minimum contention window [a value of the form
* 2^n-1 in the range 1..32767]
* @cw_max: maximum contention window [like @cw_min]
@@ -164,6 +164,14 @@ enum ieee80211_bss_change {
};
/**
+ * struct ieee80211_bss_ht_conf - BSS's changing HT configuration
+ * @operation_mode: HT operation mode (like in &struct ieee80211_ht_info)
+ */
+struct ieee80211_bss_ht_conf {
+ u16 operation_mode;
+};
+
+/**
* struct ieee80211_bss_conf - holds the BSS's changing parameters
*
* This structure keeps information about a BSS (and an association
@@ -172,15 +180,17 @@ enum ieee80211_bss_change {
* @assoc: association status
* @aid: association ID number, valid only when @assoc is true
* @use_cts_prot: use CTS protection
- * @use_short_preamble: use 802.11b short preamble
- * @use_short_slot: use short slot time (only relevant for ERP)
+ * @use_short_preamble: use 802.11b short preamble;
+ * if the hardware cannot handle this it must set the
+ * IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE hardware flag
+ * @use_short_slot: use short slot time (only relevant for ERP);
+ * if the hardware cannot handle this it must set the
+ * IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag
* @dtim_period: num of beacons before the next DTIM, for PSM
* @timestamp: beacon timestamp
* @beacon_int: beacon interval
* @assoc_capability: capabilities taken from assoc resp
- * @assoc_ht: association in HT mode
- * @ht_conf: ht capabilities
- * @ht_bss_conf: ht extended capabilities
+ * @ht: BSS's HT configuration
* @basic_rates: bitmap of basic rates, each bit stands for an
* index into the rate table configured by the driver in
* the current band.
@@ -198,10 +208,7 @@ struct ieee80211_bss_conf {
u16 assoc_capability;
u64 timestamp;
u64 basic_rates;
- /* ht related data */
- bool assoc_ht;
- struct ieee80211_ht_info *ht_conf;
- struct ieee80211_ht_bss_info *ht_bss_conf;
+ struct ieee80211_bss_ht_conf ht;
};
/**
@@ -210,29 +217,24 @@ struct ieee80211_bss_conf {
* These flags are used with the @flags member of &ieee80211_tx_info.
*
* @IEEE80211_TX_CTL_REQ_TX_STATUS: request TX status callback for this frame.
- * @IEEE80211_TX_CTL_USE_RTS_CTS: use RTS-CTS before sending frame
- * @IEEE80211_TX_CTL_USE_CTS_PROTECT: use CTS protection for the frame (e.g.,
- * for combined 802.11g / 802.11b networks)
+ * @IEEE80211_TX_CTL_ASSIGN_SEQ: The driver has to assign a sequence
+ * number to this frame, taking care of not overwriting the fragment
+ * number and increasing the sequence number only when the
+ * IEEE80211_TX_CTL_FIRST_FRAGMENT flag is set. mac80211 will properly
+ * assign sequence numbers to QoS-data frames but cannot do so correctly
+ * for non-QoS-data and management frames because beacons need them from
+ * that counter as well and mac80211 cannot guarantee proper sequencing.
+ * If this flag is set, the driver should instruct the hardware to
+ * assign a sequence number to the frame or assign one itself. Cf. IEEE
+ * 802.11-2007 7.1.3.4.1 paragraph 3. This flag will always be set for
+ * beacons and always be clear for frames without a sequence number field.
* @IEEE80211_TX_CTL_NO_ACK: tell the low level not to wait for an ack
- * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: TBD
* @IEEE80211_TX_CTL_CLEAR_PS_FILT: clear powersave filter for destination
* station
- * @IEEE80211_TX_CTL_REQUEUE: TBD
* @IEEE80211_TX_CTL_FIRST_FRAGMENT: this is a first fragment of the frame
- * @IEEE80211_TX_CTL_SHORT_PREAMBLE: TBD
- * @IEEE80211_TX_CTL_LONG_RETRY_LIMIT: this frame should be send using the
- * through set_retry_limit configured long retry value
* @IEEE80211_TX_CTL_SEND_AFTER_DTIM: send this frame after DTIM beacon
* @IEEE80211_TX_CTL_AMPDU: this frame should be sent as part of an A-MPDU
- * @IEEE80211_TX_CTL_OFDM_HT: this frame can be sent in HT OFDM rates. number
- * of streams when this flag is on can be extracted from antenna_sel_tx,
- * so if 1 antenna is marked use SISO, 2 antennas marked use MIMO, n
- * antennas marked use MIMO_n.
- * @IEEE80211_TX_CTL_GREEN_FIELD: use green field protection for this frame
- * @IEEE80211_TX_CTL_40_MHZ_WIDTH: send this frame using 40 Mhz channel width
- * @IEEE80211_TX_CTL_DUP_DATA: duplicate data frame on both 20 Mhz channels
- * @IEEE80211_TX_CTL_SHORT_GI: send this frame using short guard interval
- * @IEEE80211_TX_CTL_INJECTED: TBD
+ * @IEEE80211_TX_CTL_INJECTED: Frame was injected, internal to mac80211.
* @IEEE80211_TX_STAT_TX_FILTERED: The frame was not transmitted
* because the destination STA was in powersave mode.
* @IEEE80211_TX_STAT_ACK: Frame was acknowledged
@@ -240,63 +242,67 @@ struct ieee80211_bss_conf {
* is for the whole aggregation.
* @IEEE80211_TX_STAT_AMPDU_NO_BACK: no block ack was returned,
* so consider using block ack request (BAR).
- * @IEEE80211_TX_CTL_ASSIGN_SEQ: The driver has to assign a sequence
- * number to this frame, taking care of not overwriting the fragment
- * number and increasing the sequence number only when the
- * IEEE80211_TX_CTL_FIRST_FRAGMENT flags is set. mac80211 will properly
- * assign sequence numbers to QoS-data frames but cannot do so correctly
- * for non-QoS-data and management frames because beacons need them from
- * that counter as well and mac80211 cannot guarantee proper sequencing.
- * If this flag is set, the driver should instruct the hardware to
- * assign a sequence number to the frame or assign one itself. Cf. IEEE
- * 802.11-2007 7.1.3.4.1 paragraph 3. This flag will always be set for
- * beacons always be clear for frames without a sequence number field.
+ * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be
+ * set by rate control algorithms to indicate probe rate, will
+ * be cleared for fragmented frames (except on the last fragment)
*/
enum mac80211_tx_control_flags {
IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0),
- IEEE80211_TX_CTL_USE_RTS_CTS = BIT(2),
- IEEE80211_TX_CTL_USE_CTS_PROTECT = BIT(3),
- IEEE80211_TX_CTL_NO_ACK = BIT(4),
- IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(5),
- IEEE80211_TX_CTL_CLEAR_PS_FILT = BIT(6),
- IEEE80211_TX_CTL_REQUEUE = BIT(7),
- IEEE80211_TX_CTL_FIRST_FRAGMENT = BIT(8),
- IEEE80211_TX_CTL_SHORT_PREAMBLE = BIT(9),
- IEEE80211_TX_CTL_LONG_RETRY_LIMIT = BIT(10),
- IEEE80211_TX_CTL_SEND_AFTER_DTIM = BIT(12),
- IEEE80211_TX_CTL_AMPDU = BIT(13),
- IEEE80211_TX_CTL_OFDM_HT = BIT(14),
- IEEE80211_TX_CTL_GREEN_FIELD = BIT(15),
- IEEE80211_TX_CTL_40_MHZ_WIDTH = BIT(16),
- IEEE80211_TX_CTL_DUP_DATA = BIT(17),
- IEEE80211_TX_CTL_SHORT_GI = BIT(18),
- IEEE80211_TX_CTL_INJECTED = BIT(19),
- IEEE80211_TX_STAT_TX_FILTERED = BIT(20),
- IEEE80211_TX_STAT_ACK = BIT(21),
- IEEE80211_TX_STAT_AMPDU = BIT(22),
- IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(23),
- IEEE80211_TX_CTL_ASSIGN_SEQ = BIT(24),
+ IEEE80211_TX_CTL_ASSIGN_SEQ = BIT(1),
+ IEEE80211_TX_CTL_NO_ACK = BIT(2),
+ IEEE80211_TX_CTL_CLEAR_PS_FILT = BIT(3),
+ IEEE80211_TX_CTL_FIRST_FRAGMENT = BIT(4),
+ IEEE80211_TX_CTL_SEND_AFTER_DTIM = BIT(5),
+ IEEE80211_TX_CTL_AMPDU = BIT(6),
+ IEEE80211_TX_CTL_INJECTED = BIT(7),
+ IEEE80211_TX_STAT_TX_FILTERED = BIT(8),
+ IEEE80211_TX_STAT_ACK = BIT(9),
+ IEEE80211_TX_STAT_AMPDU = BIT(10),
+ IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11),
+ IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12),
+};
+
+enum mac80211_rate_control_flags {
+ IEEE80211_TX_RC_USE_RTS_CTS = BIT(0),
+ IEEE80211_TX_RC_USE_CTS_PROTECT = BIT(1),
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(2),
+
+ /* rate index is an MCS rate number instead of an index */
+ IEEE80211_TX_RC_MCS = BIT(3),
+ IEEE80211_TX_RC_GREEN_FIELD = BIT(4),
+ IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(5),
+ IEEE80211_TX_RC_DUP_DATA = BIT(6),
+ IEEE80211_TX_RC_SHORT_GI = BIT(7),
};
-#define IEEE80211_TX_INFO_DRIVER_DATA_SIZE \
- (sizeof(((struct sk_buff *)0)->cb) - 8)
-#define IEEE80211_TX_INFO_DRIVER_DATA_PTRS \
- (IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *))
+/* there are 40 bytes if you don't need the rateset to be kept */
+#define IEEE80211_TX_INFO_DRIVER_DATA_SIZE 40
-/* maximum number of alternate rate retry stages */
-#define IEEE80211_TX_MAX_ALTRATE 3
+/* if you do need the rateset, then you have less space */
+#define IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE 24
+
+/* maximum number of rate stages */
+#define IEEE80211_TX_MAX_RATES 5
/**
- * struct ieee80211_tx_altrate - alternate rate selection/status
+ * struct ieee80211_tx_rate - rate selection/status
+ *
+ * @idx: rate index to attempt to send with
+ * @flags: rate control flags (&enum mac80211_rate_control_flags)
+ * @count: number of tries in this rate before going to the next rate
*
- * @rate_idx: rate index to attempt to send with
- * @limit: number of retries before fallback
+ * A value of -1 for @idx indicates an invalid rate and, if used
+ * in an array of retry rates, that no more rates should be tried.
+ *
+ * When used for transmit status reporting, the driver should
+ * always report the rate along with the flags it used.
*/
-struct ieee80211_tx_altrate {
- s8 rate_idx;
- u8 limit;
-};
+struct ieee80211_tx_rate {
+ s8 idx;
+ u8 count;
+ u8 flags;
+} __attribute__((packed));
/**
* struct ieee80211_tx_info - skb transmit information
@@ -310,15 +316,12 @@ struct ieee80211_tx_altrate {
* it may be NULL.
*
* @flags: transmit info flags, defined above
- * @band: TBD
- * @tx_rate_idx: TBD
- * @antenna_sel_tx: TBD
+ * @band: the band to transmit on (use for checking for races)
+ * @antenna_sel_tx: antenna to use, 0 for automatic diversity
+ * @pad: padding, ignore
* @control: union for control data
* @status: union for status data
* @driver_data: array of driver_data pointers
- * @retry_count: number of retries
- * @excessive_retries: set to 1 if the frame was retried many times
- * but not acknowledged
* @ampdu_ack_len: number of aggregated frames.
* relevant only if IEEE80211_TX_STATUS_AMPDU was set.
* @ampdu_ack_map: block ack bit map for the aggregation.
@@ -329,31 +332,44 @@ struct ieee80211_tx_info {
/* common information */
u32 flags;
u8 band;
- s8 tx_rate_idx;
+
u8 antenna_sel_tx;
- /* 1 byte hole */
+ /* 2 byte hole */
+ u8 pad[2];
union {
struct {
+ union {
+ /* rate control */
+ struct {
+ struct ieee80211_tx_rate rates[
+ IEEE80211_TX_MAX_RATES];
+ s8 rts_cts_rate_idx;
+ };
+ /* only needed before rate control */
+ unsigned long jiffies;
+ };
/* NB: vif can be NULL for injected frames */
struct ieee80211_vif *vif;
struct ieee80211_key_conf *hw_key;
struct ieee80211_sta *sta;
- unsigned long jiffies;
- s8 rts_cts_rate_idx;
- u8 retry_limit;
- struct ieee80211_tx_altrate retries[IEEE80211_TX_MAX_ALTRATE];
} control;
struct {
+ struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
+ u8 ampdu_ack_len;
u64 ampdu_ack_map;
int ack_signal;
- struct ieee80211_tx_altrate retries[IEEE80211_TX_MAX_ALTRATE + 1];
- u8 retry_count;
- bool excessive_retries;
- u8 ampdu_ack_len;
+ /* 8 bytes free */
} status;
- void *driver_data[IEEE80211_TX_INFO_DRIVER_DATA_PTRS];
+ struct {
+ struct ieee80211_tx_rate driver_rates[
+ IEEE80211_TX_MAX_RATES];
+ void *rate_driver_data[
+ IEEE80211_TX_INFO_RATE_DRIVER_DATA_SIZE / sizeof(void *)];
+ };
+ void *driver_data[
+ IEEE80211_TX_INFO_DRIVER_DATA_SIZE / sizeof(void *)];
};
};
@@ -362,6 +378,41 @@ static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb)
return (struct ieee80211_tx_info *)skb->cb;
}
+/**
+ * ieee80211_tx_info_clear_status - clear TX status
+ *
+ * @info: The &struct ieee80211_tx_info to be cleared.
+ *
+ * When the driver passes an skb back to mac80211, it must report
+ * a number of things in TX status. This function clears everything
+ * in the TX status but the rate control information (it does clear
+ * the count since you need to fill that in anyway).
+ *
+ * NOTE: You can only use this function if you do NOT use
+ * info->driver_data! Use info->rate_driver_data
+ * instead if you need only the less space that allows.
+ */
+static inline void
+ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
+{
+ int i;
+
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) !=
+ offsetof(struct ieee80211_tx_info, control.rates));
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) !=
+ offsetof(struct ieee80211_tx_info, driver_rates));
+ BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, status.rates) != 8);
+ /* clear the rate counts */
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++)
+ info->status.rates[i].count = 0;
+
+ BUILD_BUG_ON(
+ offsetof(struct ieee80211_tx_info, status.ampdu_ack_len) != 23);
+ memset(&info->status.ampdu_ack_len, 0,
+ sizeof(struct ieee80211_tx_info) -
+ offsetof(struct ieee80211_tx_info, status.ampdu_ack_len));
+}
+
/**
* enum mac80211_rx_flags - receive flags
@@ -384,6 +435,9 @@ static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb)
* is valid. This is useful in monitor mode and necessary for beacon frames
* to enable IBSS merging.
* @RX_FLAG_SHORTPRE: Short preamble was used for this frame
+ * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index
+ * @RX_FLAG_40MHZ: HT40 (40 MHz) was used
+ * @RX_FLAG_SHORT_GI: Short guard interval was used
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = 1<<0,
@@ -394,7 +448,10 @@ enum mac80211_rx_flags {
RX_FLAG_FAILED_FCS_CRC = 1<<5,
RX_FLAG_FAILED_PLCP_CRC = 1<<6,
RX_FLAG_TSFT = 1<<7,
- RX_FLAG_SHORTPRE = 1<<8
+ RX_FLAG_SHORTPRE = 1<<8,
+ RX_FLAG_HT = 1<<9,
+ RX_FLAG_40MHZ = 1<<10,
+ RX_FLAG_SHORT_GI = 1<<11,
};
/**
@@ -414,7 +471,8 @@ enum mac80211_rx_flags {
* @noise: noise when receiving this frame, in dBm.
* @qual: overall signal quality indication, in percent (0-100).
* @antenna: antenna used
- * @rate_idx: index of data rate into band's supported rates
+ * @rate_idx: index of data rate into band's supported rates or MCS index if
+ * HT rates are use (RX_FLAG_HT)
* @flag: %RX_FLAG_*
*/
struct ieee80211_rx_status {
@@ -434,21 +492,49 @@ struct ieee80211_rx_status {
*
* Flags to define PHY configuration options
*
- * @IEEE80211_CONF_SHORT_SLOT_TIME: use 802.11g short slot time
* @IEEE80211_CONF_RADIOTAP: add radiotap header at receive time (if supported)
- * @IEEE80211_CONF_SUPPORT_HT_MODE: use 802.11n HT capabilities (if supported)
* @IEEE80211_CONF_PS: Enable 802.11 power save mode
*/
enum ieee80211_conf_flags {
- /*
- * TODO: IEEE80211_CONF_SHORT_SLOT_TIME will be removed once drivers
- * have been converted to use bss_info_changed() for slot time
- * configuration
- */
- IEEE80211_CONF_SHORT_SLOT_TIME = (1<<0),
- IEEE80211_CONF_RADIOTAP = (1<<1),
- IEEE80211_CONF_SUPPORT_HT_MODE = (1<<2),
- IEEE80211_CONF_PS = (1<<3),
+ IEEE80211_CONF_RADIOTAP = (1<<0),
+ IEEE80211_CONF_PS = (1<<1),
+};
+
+/* XXX: remove all this once drivers stop trying to use it */
+static inline int __deprecated __IEEE80211_CONF_SHORT_SLOT_TIME(void)
+{
+ return 0;
+}
+#define IEEE80211_CONF_SHORT_SLOT_TIME (__IEEE80211_CONF_SHORT_SLOT_TIME())
+
+struct ieee80211_ht_conf {
+ bool enabled;
+ enum nl80211_channel_type channel_type;
+};
+
+/**
+ * enum ieee80211_conf_changed - denotes which configuration changed
+ *
+ * @IEEE80211_CONF_CHANGE_RADIO_ENABLED: the value of radio_enabled changed
+ * @IEEE80211_CONF_CHANGE_BEACON_INTERVAL: the beacon interval changed
+ * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed
+ * @IEEE80211_CONF_CHANGE_RADIOTAP: the radiotap flag changed
+ * @IEEE80211_CONF_CHANGE_PS: the PS flag changed
+ * @IEEE80211_CONF_CHANGE_POWER: the TX power changed
+ * @IEEE80211_CONF_CHANGE_CHANNEL: the channel changed
+ * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
+ * @IEEE80211_CONF_CHANGE_HT: HT configuration changed
+ */
+enum ieee80211_conf_changed {
+ IEEE80211_CONF_CHANGE_RADIO_ENABLED = BIT(0),
+ IEEE80211_CONF_CHANGE_BEACON_INTERVAL = BIT(1),
+ IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2),
+ IEEE80211_CONF_CHANGE_RADIOTAP = BIT(3),
+ IEEE80211_CONF_CHANGE_PS = BIT(4),
+ IEEE80211_CONF_CHANGE_POWER = BIT(5),
+ IEEE80211_CONF_CHANGE_CHANNEL = BIT(6),
+ IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7),
+ IEEE80211_CONF_CHANGE_HT = BIT(8),
};
/**
@@ -457,34 +543,31 @@ enum ieee80211_conf_flags {
* This struct indicates how the driver shall configure the hardware.
*
* @radio_enabled: when zero, driver is required to switch off the radio.
- * TODO make a flag
* @beacon_int: beacon interval (TODO make interface config)
* @listen_interval: listen interval in units of beacon interval
* @flags: configuration flags defined above
* @power_level: requested transmit power (in dBm)
- * @max_antenna_gain: maximum antenna gain (in dBi)
- * @antenna_sel_tx: transmit antenna selection, 0: default/diversity,
- * 1/2: antenna 0/1
- * @antenna_sel_rx: receive antenna selection, like @antenna_sel_tx
- * @ht_conf: describes current self configuration of 802.11n HT capabilies
- * @ht_bss_conf: describes current BSS configuration of 802.11n HT parameters
* @channel: the channel to tune to
+ * @ht: the HT configuration for the device
+ * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame
+ * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
+ * but actually means the number of transmissions not the number of retries
+ * @short_frame_max_tx_count: Maximum number of transmissions for a "short"
+ * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
+ * number of transmissions not the number of retries
*/
struct ieee80211_conf {
- int radio_enabled;
-
int beacon_int;
- u16 listen_interval;
u32 flags;
int power_level;
- int max_antenna_gain;
- u8 antenna_sel_tx;
- u8 antenna_sel_rx;
- struct ieee80211_channel *channel;
+ u16 listen_interval;
+ bool radio_enabled;
+
+ u8 long_frame_max_tx_count, short_frame_max_tx_count;
- struct ieee80211_ht_info ht_conf;
- struct ieee80211_ht_bss_info ht_bss_conf;
+ struct ieee80211_channel *channel;
+ struct ieee80211_ht_conf ht;
};
/**
@@ -494,11 +577,14 @@ struct ieee80211_conf {
* use during the life of a virtual interface.
*
* @type: type of this virtual interface
+ * @bss_conf: BSS configuration for this interface, either our own
+ * or the BSS we're associated to
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *).
*/
struct ieee80211_vif {
enum nl80211_iftype type;
+ struct ieee80211_bss_conf bss_conf;
/* must be last */
u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
};
@@ -542,14 +628,12 @@ struct ieee80211_if_init_conf {
* enum ieee80211_if_conf_change - interface config change flags
*
* @IEEE80211_IFCC_BSSID: The BSSID changed.
- * @IEEE80211_IFCC_SSID: The SSID changed.
* @IEEE80211_IFCC_BEACON: The beacon for this interface changed
* (currently AP and MESH only), use ieee80211_beacon_get().
*/
enum ieee80211_if_conf_change {
IEEE80211_IFCC_BSSID = BIT(0),
- IEEE80211_IFCC_SSID = BIT(1),
- IEEE80211_IFCC_BEACON = BIT(2),
+ IEEE80211_IFCC_BEACON = BIT(1),
};
/**
@@ -557,11 +641,6 @@ enum ieee80211_if_conf_change {
*
* @changed: parameters that have changed, see &enum ieee80211_if_conf_change.
* @bssid: BSSID of the network we are associated to/creating.
- * @ssid: used (together with @ssid_len) by drivers for hardware that
- * generate beacons independently. The pointer is valid only during the
- * config_interface() call, so copy the value somewhere if you need
- * it.
- * @ssid_len: length of the @ssid field.
*
* This structure is passed to the config_interface() callback of
* &struct ieee80211_hw.
@@ -569,8 +648,6 @@ enum ieee80211_if_conf_change {
struct ieee80211_if_conf {
u32 changed;
u8 *bssid;
- u8 *ssid;
- size_t ssid_len;
};
/**
@@ -677,7 +754,7 @@ enum set_key_cmd {
* @addr: MAC address
* @aid: AID we assigned to the station if we're an AP
* @supp_rates: Bitmap of supported rates (per band)
- * @ht_info: HT capabilities of this STA
+ * @ht_cap: HT capabilities of this STA; restricted to our own TX capabilities
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *), size is determined in hw information.
*/
@@ -685,7 +762,7 @@ struct ieee80211_sta {
u64 supp_rates[IEEE80211_NUM_BANDS];
u8 addr[ETH_ALEN];
u16 aid;
- struct ieee80211_ht_info ht_info;
+ struct ieee80211_sta_ht_cap ht_cap;
/* must be last */
u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
@@ -695,13 +772,17 @@ struct ieee80211_sta {
* enum sta_notify_cmd - sta notify command
*
* Used with the sta_notify() callback in &struct ieee80211_ops, this
- * indicates addition and removal of a station to station table.
+ * indicates addition and removal of a station to station table,
+ * or if a associated station made a power state transition.
*
* @STA_NOTIFY_ADD: a station was added to the station table
* @STA_NOTIFY_REMOVE: a station being removed from the station table
+ * @STA_NOTIFY_SLEEP: a station is now sleeping
+ * @STA_NOTIFY_AWAKE: a sleeping station woke up
*/
enum sta_notify_cmd {
- STA_NOTIFY_ADD, STA_NOTIFY_REMOVE
+ STA_NOTIFY_ADD, STA_NOTIFY_REMOVE,
+ STA_NOTIFY_SLEEP, STA_NOTIFY_AWAKE,
};
/**
@@ -769,6 +850,14 @@ enum ieee80211_tkip_key_type {
* @IEEE80211_HW_SPECTRUM_MGMT:
* Hardware supports spectrum management defined in 802.11h
* Measurement, Channel Switch, Quieting, TPC
+ *
+ * @IEEE80211_HW_AMPDU_AGGREGATION:
+ * Hardware supports 11n A-MPDU aggregation.
+ *
+ * @IEEE80211_HW_NO_STACK_DYNAMIC_PS:
+ * Hardware which has dynamic power save support, meaning
+ * that power save is enabled in idle periods, and don't need support
+ * from stack.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_RX_INCLUDES_FCS = 1<<1,
@@ -780,6 +869,8 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SIGNAL_DBM = 1<<7,
IEEE80211_HW_NOISE_DBM = 1<<8,
IEEE80211_HW_SPECTRUM_MGMT = 1<<9,
+ IEEE80211_HW_AMPDU_AGGREGATION = 1<<10,
+ IEEE80211_HW_NO_STACK_DYNAMIC_PS = 1<<11,
};
/**
@@ -838,8 +929,8 @@ enum ieee80211_hw_flags {
* @sta_data_size: size (in bytes) of the drv_priv data area
* within &struct ieee80211_sta.
*
- * @max_altrates: maximum number of alternate rate retry stages
- * @max_altrate_tries: maximum number of tries for each stage
+ * @max_rates: maximum number of alternate rate retry stages
+ * @max_rate_tries: maximum number of tries for each stage
*/
struct ieee80211_hw {
struct ieee80211_conf conf;
@@ -856,12 +947,10 @@ struct ieee80211_hw {
u16 ampdu_queues;
u16 max_listen_interval;
s8 max_signal;
- u8 max_altrates;
- u8 max_altrate_tries;
+ u8 max_rates;
+ u8 max_rate_tries;
};
-struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy);
-
/**
* SET_IEEE80211_DEV - set device for 802.11 hardware
*
@@ -874,7 +963,7 @@ static inline void SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev
}
/**
- * SET_IEEE80211_PERM_ADDR - set the permanenet MAC address for 802.11 hardware
+ * SET_IEEE80211_PERM_ADDR - set the permanent MAC address for 802.11 hardware
*
* @hw: the &struct ieee80211_hw to set the MAC address for
* @addr: the address to set
@@ -898,9 +987,9 @@ static inline struct ieee80211_rate *
ieee80211_get_tx_rate(const struct ieee80211_hw *hw,
const struct ieee80211_tx_info *c)
{
- if (WARN_ON(c->tx_rate_idx < 0))
+ if (WARN_ON(c->control.rates[0].idx < 0))
return NULL;
- return &hw->wiphy->bands[c->band]->bitrates[c->tx_rate_idx];
+ return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[0].idx];
}
static inline struct ieee80211_rate *
@@ -916,9 +1005,9 @@ static inline struct ieee80211_rate *
ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
const struct ieee80211_tx_info *c, int idx)
{
- if (c->control.retries[idx].rate_idx < 0)
+ if (c->control.rates[idx + 1].idx < 0)
return NULL;
- return &hw->wiphy->bands[c->band]->bitrates[c->control.retries[idx].rate_idx];
+ return &hw->wiphy->bands[c->band]->bitrates[c->control.rates[idx + 1].idx];
}
/**
@@ -967,7 +1056,7 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
* This happens everytime the iv16 wraps around (every 65536 packets). The
* set_key() call will happen only once for each key (unless the AP did
* rekeying), it will not include a valid phase 1 key. The valid phase 1 key is
- * provided by udpate_tkip_key only. The trigger that makes mac80211 call this
+ * provided by update_tkip_key only. The trigger that makes mac80211 call this
* handler is software decryption with wrap around of iv16.
*/
@@ -1060,12 +1149,14 @@ enum ieee80211_filter_flags {
* @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation
* @IEEE80211_AMPDU_TX_START: start Tx aggregation
* @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation
+ * @IEEE80211_AMPDU_TX_RESUME: resume TX aggregation
*/
enum ieee80211_ampdu_mlme_action {
IEEE80211_AMPDU_RX_START,
IEEE80211_AMPDU_RX_STOP,
IEEE80211_AMPDU_TX_START,
IEEE80211_AMPDU_TX_STOP,
+ IEEE80211_AMPDU_TX_RESUME,
};
/**
@@ -1101,7 +1192,7 @@ enum ieee80211_ampdu_mlme_action {
* Must be implemented.
*
* @add_interface: Called when a netdevice attached to the hardware is
- * enabled. Because it is not called for monitor mode devices, @open
+ * enabled. Because it is not called for monitor mode devices, @start
* and @stop must be implemented.
* The driver should perform any initialization it needs before
* the device can be enabled. The initial configuration for the
@@ -1163,14 +1254,9 @@ enum ieee80211_ampdu_mlme_action {
*
* @set_rts_threshold: Configuration of RTS threshold (if device needs it)
*
- * @set_frag_threshold: Configuration of fragmentation threshold. Assign this if
- * the device does fragmentation by itself; if this method is assigned then
- * the stack will not do fragmentation.
- *
- * @set_retry_limit: Configuration of retry limits (if device needs it)
- *
- * @sta_notify: Notifies low level driver about addition or removal
- * of assocaited station or AP.
+ * @sta_notify: Notifies low level driver about addition, removal or power
+ * state transition of an associated station, AP, IBSS/WDS/mesh peer etc.
+ * Must be atomic.
*
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
* bursting) for a hardware TX queue.
@@ -1194,8 +1280,6 @@ enum ieee80211_ampdu_mlme_action {
* This is needed only for IBSS mode and the result of this function is
* used to determine whether to reply to Probe Requests.
*
- * @conf_ht: Configures low level driver with 802.11n HT data. Must be atomic.
- *
* @ampdu_action: Perform a certain A-MPDU action
* The RA/TID combination determines the destination and TID we want
* the ampdu action to be performed for. The action is defined through
@@ -1211,7 +1295,7 @@ struct ieee80211_ops {
struct ieee80211_if_init_conf *conf);
void (*remove_interface)(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
- int (*config)(struct ieee80211_hw *hw, struct ieee80211_conf *conf);
+ int (*config)(struct ieee80211_hw *hw, u32 changed);
int (*config_interface)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_if_conf *conf);
@@ -1237,9 +1321,6 @@ struct ieee80211_ops {
void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
u32 *iv32, u16 *iv16);
int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value);
- int (*set_frag_threshold)(struct ieee80211_hw *hw, u32 value);
- int (*set_retry_limit)(struct ieee80211_hw *hw,
- u32 short_retry, u32 long_retr);
void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum sta_notify_cmd, struct ieee80211_sta *sta);
int (*conf_tx)(struct ieee80211_hw *hw, u16 queue,
@@ -1472,7 +1553,7 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
* the next beacon frame from the 802.11 code. The low-level is responsible
* for calling this function before beacon data is needed (e.g., based on
* hardware interrupt). Returned skb is used only once and low-level driver
- * is responsible of freeing it.
+ * is responsible for freeing it.
*/
struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
@@ -1803,24 +1884,38 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
/* Rate control API */
+
/**
- * struct rate_selection - rate information for/from rate control algorithms
- *
- * @rate_idx: selected transmission rate index
- * @nonerp_idx: Non-ERP rate to use instead if ERP cannot be used
- * @probe_idx: rate for probing (or -1)
- * @max_rate_idx: maximum rate index that can be used, this is
- * input to the algorithm and will be enforced
- */
-struct rate_selection {
- s8 rate_idx, nonerp_idx, probe_idx, max_rate_idx;
+ * struct ieee80211_tx_rate_control - rate control information for/from RC algo
+ *
+ * @hw: The hardware the algorithm is invoked for.
+ * @sband: The band this frame is being transmitted on.
+ * @bss_conf: the current BSS configuration
+ * @reported_rate: The rate control algorithm can fill this in to indicate
+ * which rate should be reported to userspace as the current rate and
+ * used for rate calculations in the mesh network.
+ * @rts: whether RTS will be used for this frame because it is longer than the
+ * RTS threshold
+ * @short_preamble: whether mac80211 will request short-preamble transmission
+ * if the selected rate supports it
+ * @max_rate_idx: user-requested maximum rate (not MCS for now)
+ * @skb: the skb that will be transmitted, the control information in it needs
+ * to be filled in
+ */
+struct ieee80211_tx_rate_control {
+ struct ieee80211_hw *hw;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_bss_conf *bss_conf;
+ struct sk_buff *skb;
+ struct ieee80211_tx_rate reported_rate;
+ bool rts, short_preamble;
+ u8 max_rate_idx;
};
struct rate_control_ops {
struct module *module;
const char *name;
void *(*alloc)(struct ieee80211_hw *hw, struct dentry *debugfsdir);
- void (*clear)(void *priv);
void (*free)(void *priv);
void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp);
@@ -1832,10 +1927,8 @@ struct rate_control_ops {
void (*tx_status)(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb);
- void (*get_rate)(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb,
- struct rate_selection *sel);
+ void (*get_rate)(void *priv, struct ieee80211_sta *sta, void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc);
void (*add_sta_debugfs)(void *priv, void *priv_sta,
struct dentry *dir);
diff --git a/include/net/wireless.h b/include/net/wireless.h
index 721efb363db7..21c5d966142d 100644
--- a/include/net/wireless.h
+++ b/include/net/wireless.h
@@ -10,6 +10,7 @@
#include <linux/netdevice.h>
#include <linux/debugfs.h>
#include <linux/list.h>
+#include <linux/ieee80211.h>
#include <net/cfg80211.h>
/**
@@ -133,23 +134,23 @@ struct ieee80211_rate {
};
/**
- * struct ieee80211_ht_info - describing STA's HT capabilities
+ * struct ieee80211_sta_ht_cap - STA's HT capabilities
*
* This structure describes most essential parameters needed
* to describe 802.11n HT capabilities for an STA.
*
- * @ht_supported: is HT supported by STA, 0: no, 1: yes
+ * @ht_supported: is HT supported by the STA
* @cap: HT capabilities map as described in 802.11n spec
* @ampdu_factor: Maximum A-MPDU length factor
* @ampdu_density: Minimum A-MPDU spacing
- * @supp_mcs_set: Supported MCS set as described in 802.11n spec
+ * @mcs: Supported MCS rates
*/
-struct ieee80211_ht_info {
+struct ieee80211_sta_ht_cap {
u16 cap; /* use IEEE80211_HT_CAP_ */
- u8 ht_supported;
+ bool ht_supported;
u8 ampdu_factor;
u8 ampdu_density;
- u8 supp_mcs_set[16];
+ struct ieee80211_mcs_info mcs;
};
/**
@@ -173,13 +174,18 @@ struct ieee80211_supported_band {
enum ieee80211_band band;
int n_channels;
int n_bitrates;
- struct ieee80211_ht_info ht_info;
+ struct ieee80211_sta_ht_cap ht_cap;
};
/**
* struct wiphy - wireless hardware description
* @idx: the wiphy index assigned to this item
* @class_dev: the class device representing /sys/class/ieee80211/<wiphy-name>
+ * @fw_handles_regulatory: tells us the firmware for this device
+ * has its own regulatory solution and cannot identify the
+ * ISO / IEC 3166 alpha2 it belongs to. When this is enabled
+ * we will disregard the first regulatory hint (when the
+ * initiator is %REGDOM_SET_BY_CORE).
* @reg_notifier: the driver's regulatory notification callback
*/
struct wiphy {
@@ -191,6 +197,8 @@ struct wiphy {
/* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */
u16 interface_modes;
+ bool fw_handles_regulatory;
+
/* If multiple wiphys are registered and you're handed e.g.
* a regular netdev with assigned ieee80211_ptr, you won't
* know whether it points to a wiphy your driver has registered
@@ -262,9 +270,9 @@ static inline struct device *wiphy_dev(struct wiphy *wiphy)
/**
* wiphy_name - get wiphy name
*/
-static inline char *wiphy_name(struct wiphy *wiphy)
+static inline const char *wiphy_name(struct wiphy *wiphy)
{
- return wiphy->dev.bus_id;
+ return dev_name(&wiphy->dev);
}
/**
@@ -340,55 +348,51 @@ ieee80211_get_channel(struct wiphy *wiphy, int freq)
}
/**
- * __regulatory_hint - hint to the wireless core a regulatory domain
- * @wiphy: if a driver is providing the hint this is the driver's very
- * own &struct wiphy
- * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain
- * should be in. If @rd is set this should be NULL
- * @rd: a complete regulatory domain, if passed the caller need not worry
- * about freeing it
- *
- * The Wireless subsystem can use this function to hint to the wireless core
- * what it believes should be the current regulatory domain by
- * giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory
- * domain should be in or by providing a completely build regulatory domain.
+ * ieee80211_get_response_rate - get basic rate for a given rate
*
- * Returns -EALREADY if *a regulatory domain* has already been set. Note that
- * this could be by another driver. It is safe for drivers to continue if
- * -EALREADY is returned, if drivers are not capable of world roaming they
- * should not register more channels than they support. Right now we only
- * support listening to the first driver hint. If the driver is capable
- * of world roaming but wants to respect its own EEPROM mappings for
- * specific regulatory domains it should register the @reg_notifier callback
- * on the &struct wiphy. Returns 0 if the hint went through fine or through an
- * intersection operation. Otherwise a standard error code is returned.
+ * @sband: the band to look for rates in
+ * @basic_rates: bitmap of basic rates
+ * @bitrate: the bitrate for which to find the basic rate
*
+ * This function returns the basic rate corresponding to a given
+ * bitrate, that is the next lower bitrate contained in the basic
+ * rate map, which is, for this function, given as a bitmap of
+ * indices of rates in the band's bitrate table.
*/
-extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
- const char *alpha2, struct ieee80211_regdomain *rd);
+struct ieee80211_rate *
+ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
+ u64 basic_rates, int bitrate);
+
/**
* regulatory_hint - driver hint to the wireless core a regulatory domain
- * @wiphy: the driver's very own &struct wiphy
+ * @wiphy: the wireless device giving the hint (used only for reporting
+ * conflicts)
* @alpha2: the ISO/IEC 3166 alpha2 the driver claims its regulatory domain
* should be in. If @rd is set this should be NULL. Note that if you
* set this to NULL you should still set rd->alpha2 to some accepted
* alpha2.
- * @rd: a complete regulatory domain provided by the driver. If passed
- * the driver does not need to worry about freeing it.
*
* Wireless drivers can use this function to hint to the wireless core
* what it believes should be the current regulatory domain by
* giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory
* domain should be in or by providing a completely build regulatory domain.
* If the driver provides an ISO/IEC 3166 alpha2 userspace will be queried
- * for a regulatory domain structure for the respective country. If
- * a regulatory domain is build and passed you should set the alpha2
- * if possible, otherwise set it to the special value of "99" which tells
- * the wireless core it is unknown. If you pass a built regulatory domain
- * and we return non zero you are in charge of kfree()'ing the structure.
+ * for a regulatory domain structure for the respective country.
+ */
+extern void regulatory_hint(struct wiphy *wiphy, const char *alpha2);
+
+/**
+ * regulatory_hint_11d - hints a country IE as a regulatory domain
+ * @wiphy: the wireless device giving the hint (used only for reporting
+ * conflicts)
+ * @country_ie: pointer to the country IE
+ * @country_ie_len: length of the country IE
*
- * See __regulatory_hint() documentation for possible return values.
+ * We will intersect the rd with the what CRDA tells us should apply
+ * for the alpha2 this country IE belongs to, this prevents APs from
+ * sending us incorrect or outdated information against a country.
*/
-extern int regulatory_hint(struct wiphy *wiphy,
- const char *alpha2, struct ieee80211_regdomain *rd);
+extern void regulatory_hint_11d(struct wiphy *wiphy,
+ u8 *country_ie,
+ u8 country_ie_len);
#endif /* __NET_WIRELESS_H */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 5e0189876afd..24815b675cc3 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -394,6 +394,9 @@ struct snd_soc_dai {
/* DAI private data */
void *private_data;
+
+ /* For checking the start of the delayed work inside the soc-core.c */
+ int prepared;
};
/* SoC Audio Codec */
diff --git a/include/video/hx8347fb.h b/include/video/hx8347fb.h
new file mode 100644
index 000000000000..8c989211f6fb
--- /dev/null
+++ b/include/video/hx8347fb.h
@@ -0,0 +1,95 @@
+/*
+ * hx8347fb.h - definitions for the HX8347 framebuffer driver
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+
+#ifndef __HX8347FB_H_
+#define __HX8347FB_H_
+
+/* Index Register values */
+#define DISP_MODE_CTRL 0x01
+#define COL_ADDR_START_2 0x02
+#define COL_ADDR_START_1 0x03
+#define COL_ADDR_END_2 0x04
+#define COL_ADDR_END_1 0x05
+#define ROW_ADDR_START_2 0x06
+#define ROW_ADDR_START_1 0x07
+#define ROW_ADDR_END_2 0x08
+#define ROW_ADDR_END_1 0x09
+
+/* Cont her*/
+#define PARTIAL_AREA_START_R2 0x0a
+/* End Cont her*/
+
+#define MEM_ACCESS_CTRL 0x16
+/* Cont her*/
+/* End Cont her*/
+
+#define SRAM_WR_CTRL 0x22
+/* Cont her*/
+/* End Cont her*/
+
+
+#define DISP_CTRL_1 0x26
+#define DISP_CTRL_2 0x27
+#define DISP_CTRL_3 0x28
+#define DISP_CTRL_4 0x29
+#define DISP_CTRL_5 0x2a
+#define POWER_CTRL_11 0x2b
+#define DISP_CTRL_6 0x2c
+/* Cont her*/
+/* End Cont her*/
+
+
+
+#define HIMAX_ID_CODE 0x67
+/* Cont her*/
+/* End Cont her*/
+
+
+/* Display Mode Control Register Masks */
+#define DMC_PTLO (0x01 << 0)
+#define DMC_NORON (0x01 << 1)
+#define DMC_INVON (0x01 << 2)
+#define DMC_IDMON (0x01 << 3)
+
+struct hx8347fb_par {
+ spinlock_t lock;
+ struct fb_info *info;
+ void __iomem *mmio_cmd;
+ void __iomem *mmio_data;
+ dma_addr_t fb_p;
+ struct hx8347fb_pdata *pdata;
+};
+
+ /* Platform data structure */
+struct hx8347fb_pdata {
+ struct module *owner;
+ int irq;
+ struct platform_device *pdev;
+ struct clk *bus_clk;
+ struct clk *lcdc_clk;
+ int rst_gpio;
+ int enable_gpio;
+ int usedma;
+ unsigned int xres;
+ unsigned int yres;
+ unsigned int bits_per_pixel;
+ u32 pseudo_pal[16];
+ void (*reset)(struct hx8347fb_par *);
+ void (*bl_enable)(struct hx8347fb_par *, int);
+ void (*cleanup)(struct hx8347fb_par *);
+ int (*init)(struct hx8347fb_par *);
+ void (*set_idx)(struct hx8347fb_par *, u8);
+ void (*wr_reg)(struct hx8347fb_par *, u8, u16);
+ u16 (*rd_reg)(struct hx8347fb_par *, u8);
+ void (*wr_data)(struct hx8347fb_par *, u16 *, int);
+};
+
+#endif /* __HX8347FB_H_ */
diff --git a/init/do_mounts_rd.c b/init/do_mounts_rd.c
index a7c748fa977a..a343342f1656 100644
--- a/init/do_mounts_rd.c
+++ b/init/do_mounts_rd.c
@@ -5,6 +5,7 @@
#include <linux/ext2_fs.h>
#include <linux/romfs_fs.h>
#include <linux/cramfs_fs.h>
+#include <linux/squashfs_fs.h>
#include <linux/initrd.h>
#include <linux/string.h>
@@ -37,6 +38,7 @@ static int __init crd_load(int in_fd, int out_fd);
* numbers could not be found.
*
* We currently check for the following magic numbers:
+ * squashfs
* minix
* ext2
* romfs
@@ -51,6 +53,7 @@ identify_ramdisk_image(int fd, int start_block)
struct ext2_super_block *ext2sb;
struct romfs_super_block *romfsb;
struct cramfs_super *cramfsb;
+ struct squashfs_super_block *squashfsb;
int nblocks = -1;
unsigned char *buf;
@@ -62,6 +65,7 @@ identify_ramdisk_image(int fd, int start_block)
ext2sb = (struct ext2_super_block *) buf;
romfsb = (struct romfs_super_block *) buf;
cramfsb = (struct cramfs_super *) buf;
+ squashfsb = (struct squashfs_super_block *) buf;
memset(buf, 0xe5, size);
/*
@@ -99,6 +103,18 @@ identify_ramdisk_image(int fd, int start_block)
goto done;
}
+ /* squashfs is at block zero too */
+ if (squashfsb->s_magic == SQUASHFS_MAGIC) {
+ printk(KERN_NOTICE
+ "RAMDISK: squashfs filesystem found at block %d\n",
+ start_block);
+ if (squashfsb->s_major < 3)
+ nblocks = (squashfsb->bytes_used_2+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
+ else
+ nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
+ goto done;
+ }
+
/*
* Read block 1 to test for minix and ext2 superblock
*/
diff --git a/init/main.c b/init/main.c
index 7e117a231af1..d4d1de02ac66 100644
--- a/init/main.c
+++ b/init/main.c
@@ -800,6 +800,12 @@ static int noinline init_post(void)
system_state = SYSTEM_RUNNING;
numa_default_policy();
+#ifdef CONFIG_TMPFSDEV
+ sys_mount("tmpfsdev", "/dev", "tmpfs", 0, "size=64k");
+ sys_mknod("/dev/console", S_IFCHR | 0600, new_encode_dev(MKDEV(5, 1)));
+ sys_mknod("/dev/null", S_IFCHR | 0600, new_encode_dev(MKDEV(1, 3)));
+#endif
+
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index b0f239e443bc..8fa69624cf2c 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -867,6 +867,19 @@ config DYNAMIC_PRINTK_DEBUG
debugging for all modules. This mode can be turned off via the above
disable command.
+config TMPFSDEV
+ bool "Create some device nodes in tmpfs"
+ depends on TMPFS
+ default n
+ help
+ This hack allows the kernel to create some needed device nodes itself
+ on booting. It mounts a tmpfs in /dev and creates the nodes inside.
+
+ This supports customization of root filesystems without root
+ privileges.
+
+ Currently created nodes: console, null.
+
source "samples/Kconfig"
source "lib/Kconfig.kgdb"
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 65f7757465bd..08c4f10ade69 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -556,6 +556,7 @@ static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
C(truesize);
#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
C(do_not_encrypt);
+ C(requeue);
#endif
atomic_set(&n->users, 1);
diff --git a/net/ieee80211/Kconfig b/net/ieee80211/Kconfig
index 94ed7d3cd9da..bd501046c9c0 100644
--- a/net/ieee80211/Kconfig
+++ b/net/ieee80211/Kconfig
@@ -71,3 +71,4 @@ config IEEE80211_CRYPT_TKIP
This can be compiled as a module and it will be called
"ieee80211_crypt_tkip".
+source "net/ieee80211/softmac/Kconfig"
diff --git a/net/ieee80211/Makefile b/net/ieee80211/Makefile
index f988417121da..796a7c76ee48 100644
--- a/net/ieee80211/Makefile
+++ b/net/ieee80211/Makefile
@@ -10,3 +10,4 @@ ieee80211-objs := \
ieee80211_wx.o \
ieee80211_geo.o
+obj-$(CONFIG_IEEE80211_SOFTMAC) += softmac/
diff --git a/net/ieee80211/compat.h b/net/ieee80211/compat.h
new file mode 100644
index 000000000000..eedd84a98c0f
--- /dev/null
+++ b/net/ieee80211/compat.h
@@ -0,0 +1,104 @@
+/*
+ * Header file to maintain compatibility among different kernel versions.
+ *
+ * Copyright (c) 2004-2006 Zhu Yi <yi.zhu@intel.com>, Intel Corporation
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/version.h>
+#include <linux/if_ether.h> /* ETH_ALEN */
+#include <linux/wireless.h>
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,8)
+#define __iomem
+#define __le32 u32
+#endif
+
+#ifndef NETDEV_TX_OK
+#define NETDEV_TX_OK 0
+#endif
+
+#ifndef ARPHRD_IEEE80211_RADIOTAP
+#define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */
+#endif
+
+#ifndef __bitwise /* if __leXX is not defined */
+typedef __u16 __le16;
+typedef __u64 __le64;
+#endif
+
+#ifndef DEFINE_SPINLOCK
+#define DEFINE_SPINLOCK(s) spinlock_t s = SPIN_LOCK_UNLOCKED
+#endif
+
+#ifndef WIRELESS_SPY
+#define WIRELESS_SPY /* enable iwspy support */
+#endif
+
+#ifndef __nocast
+#define __nocast
+#endif
+
+#ifndef NETDEV_TX_BUSY
+#define NETDEV_TX_BUSY 1
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+typedef unsigned gfp_t;
+#endif
+
+/* WE compatibility macros */
+#if WIRELESS_EXT < 17
+#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */
+#define IW_QUAL_LEVEL_UPDATED 0x02
+#define IW_QUAL_NOISE_UPDATED 0x04
+#define IW_QUAL_ALL_UPDATED 0x07
+#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */
+#define IW_QUAL_LEVEL_INVALID 0x20
+#define IW_QUAL_NOISE_INVALID 0x40
+#define IW_QUAL_ALL_INVALID 0x70
+#endif
+
+#if WIRELESS_EXT < 19
+#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */
+#endif
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
+static inline int is_multicast_ether_addr(const u8 *addr)
+{
+ return addr[0] & 0x01;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+static inline int is_broadcast_ether_addr(const u8 *addr)
+{
+ return (addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) == 0xff;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+static inline void *kzalloc(size_t size, unsigned __nocast flags)
+{
+ void *ret = kmalloc(size, flags);
+ if (ret)
+ memset(ret, 0, size);
+ return ret;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
+static inline unsigned compare_ether_addr(const u8 *_a, const u8 *_b)
+{
+ const u16 *a = (const u16 *) _a;
+ const u16 *b = (const u16 *) _b;
+
+ BUILD_BUG_ON(ETH_ALEN != 6);
+ return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0;
+}
+#endif
diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c
index d996547f7a62..d44c96b73d51 100644
--- a/net/ieee80211/ieee80211_tx.c
+++ b/net/ieee80211/ieee80211_tx.c
@@ -41,6 +41,7 @@
#include <linux/etherdevice.h>
#include <asm/uaccess.h>
+#include "compat.h"
#include <net/ieee80211.h>
/*
@@ -54,7 +55,7 @@ Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs |
| | tion | (BSSID) | | | ence | data | |
`--------------------------------------------------| |------'
Total: 28 non-data bytes `----.----'
- |
+ |
.- 'Frame data' expands, if WEP enabled, to <----------'
|
V
@@ -64,8 +65,8 @@ Bytes | 4 | 0-2296 | 4 |
Desc. | IV | Encrypted | ICV |
| | Packet | |
`-----| |-----'
- `-----.-----'
- |
+ `-----.-----'
+ |
.- 'Encrypted Packet' expands to
|
V
@@ -126,7 +127,7 @@ payload of each frame is reduced to 492 bytes.
static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
-static int ieee80211_copy_snap(u8 * data, __be16 h_proto)
+static int ieee80211_copy_snap(u8 * data, u16 h_proto)
{
struct ieee80211_snap_hdr *snap;
u8 *oui;
@@ -136,7 +137,7 @@ static int ieee80211_copy_snap(u8 * data, __be16 h_proto)
snap->ssap = 0xaa;
snap->ctrl = 0x03;
- if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX))
+ if (h_proto == 0x8137 || h_proto == 0x80f3)
oui = P802_1H_OUI;
else
oui = RFC1042_OUI;
@@ -144,7 +145,7 @@ static int ieee80211_copy_snap(u8 * data, __be16 h_proto)
snap->oui[1] = oui[1];
snap->oui[2] = oui[2];
- memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16));
+ *(u16 *) (data + SNAP_SIZE) = htons(h_proto);
return SNAP_SIZE + sizeof(u16);
}
@@ -167,9 +168,13 @@ static int ieee80211_encrypt_fragment(struct ieee80211_device *ieee,
atomic_dec(&crypt->refcnt);
if (res < 0) {
+ unsigned long flags;
+
+ spin_lock_irqsave( &ieee->lock, flags );
printk(KERN_INFO "%s: Encryption failed: len=%d.\n",
ieee->dev->name, frag->len);
ieee->ieee_stats.tx_discards++;
+ spin_unlock_irqrestore( &ieee->lock, flags );
return -1;
}
@@ -225,10 +230,10 @@ static int ieee80211_classify(struct sk_buff *skb)
struct iphdr *ip;
eth = (struct ethhdr *)skb->data;
- if (eth->h_proto != htons(ETH_P_IP))
+ if (eth->h_proto != __constant_htons(ETH_P_IP))
return 0;
- ip = ip_hdr(skb);
+ ip = ip_hdr(skb);
switch (ip->tos & 0xfc) {
case 0x20:
return 2;
@@ -256,12 +261,11 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
struct ieee80211_device *ieee = netdev_priv(dev);
struct ieee80211_txb *txb = NULL;
struct ieee80211_hdr_3addrqos *frag_hdr;
- int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
+ int i, i_unlocked, bytes_per_frag, nr_frags, bytes_last_frag, frag_size,
rts_required;
unsigned long flags;
struct net_device_stats *stats = &ieee->stats;
- int encrypt, host_encrypt, host_encrypt_msdu, host_build_iv;
- __be16 ether_type;
+ int ether_type, encrypt, host_encrypt, host_encrypt_msdu, host_build_iv;
int bytes, fc, hdr_len;
struct sk_buff *skb_frag;
struct ieee80211_hdr_3addrqos header = {/* Ensure zero initialized */
@@ -273,6 +277,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
struct ieee80211_crypt_data *crypt;
int priority = skb->priority;
int snapped = 0;
+ u16 frag = 0;
if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority))
return NETDEV_TX_BUSY;
@@ -283,20 +288,22 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
* creating it... */
if (!ieee->hard_start_xmit) {
printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name);
+ spin_unlock_irqrestore(&ieee->lock, flags);
goto success;
}
if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) {
printk(KERN_WARNING "%s: skb too small (%d).\n",
ieee->dev->name, skb->len);
+ spin_unlock_irqrestore(&ieee->lock, flags);
goto success;
}
- ether_type = ((struct ethhdr *)skb->data)->h_proto;
+ ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto);
crypt = ieee->crypt[ieee->tx_keyidx];
- encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) &&
+ encrypt = !(ether_type == ETH_P_PAE && ieee->ieee802_1x) &&
ieee->sec.encrypt;
host_encrypt = ieee->host_encrypt && encrypt && crypt;
@@ -304,14 +311,15 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
host_build_iv = ieee->host_build_iv && encrypt && crypt;
if (!encrypt && ieee->ieee802_1x &&
- ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) {
+ ieee->drop_unencrypted && ether_type != ETH_P_PAE) {
stats->tx_dropped++;
+ spin_unlock_irqrestore(&ieee->lock, flags);
goto success;
}
/* Save source and destination addresses */
- skb_copy_from_linear_data(skb, dest, ETH_ALEN);
- skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN);
+ memcpy(dest, skb->data, ETH_ALEN);
+ memcpy(src, skb->data + ETH_ALEN, ETH_ALEN);
if (host_encrypt || host_build_iv)
fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA |
@@ -364,7 +372,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
snapped = 1;
ieee80211_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)),
ether_type);
- skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len);
+ memcpy(skb_put(skb_new, skb->len), skb->data, skb->len);
res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv);
if (res < 0) {
IEEE80211_ERROR("msdu encryption failed\n");
@@ -387,14 +395,14 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
else
frag_size = ieee->fts;
+ /* the FCS must be included always in fragmentation calculation */
+ frag_size -= IEEE80211_FCS_LEN;
+
/* Determine amount of payload per fragment. Regardless of if
* this stack is providing the full 802.11 header, one will
* eventually be affixed to this fragment -- so we must account
* for it when determining the amount of payload space. */
- bytes_per_frag = frag_size - hdr_len;
- if (ieee->config &
- (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
- bytes_per_frag -= IEEE80211_FCS_LEN;
+ bytes_per_frag = frag_size - IEEE80211_3ADDR_LEN;
/* Each fragment may need to have room for encryptiong
* pre/postfix */
@@ -413,11 +421,15 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
} else {
nr_frags = 1;
bytes_per_frag = bytes_last_frag = bytes;
- frag_size = bytes + hdr_len;
+ frag_size = bytes + IEEE80211_3ADDR_LEN - IEEE80211_FCS_LEN;
}
- rts_required = (frag_size > ieee->rts
- && ieee->config & CFG_IEEE80211_RTS);
+ /* threshold is on transmitted data */
+ rts_required = ( ieee->config & CFG_IEEE80211_RTS ) &&
+ ( ieee->rts < ( ( ( nr_frags > 1 ) ?
+ bytes_per_frag :
+ bytes_last_frag ) +
+ IEEE80211_FCS_LEN + IEEE80211_3ADDR_LEN));
if (rts_required)
nr_frags++;
@@ -433,7 +445,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
}
txb->encrypted = encrypt;
if (host_encrypt)
- txb->payload_size = frag_size * (nr_frags - 1) +
+ txb->payload_size = ( frag_size + IEEE80211_FCS_LEN ) * (nr_frags - 1) +
bytes_last_frag;
else
txb->payload_size = bytes;
@@ -464,7 +476,9 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
} else
i = 0;
+ i_unlocked = i;
for (; i < nr_frags; i++) {
+ u16 sc;
skb_frag = txb->fragments[i];
if (host_encrypt || host_build_iv)
@@ -475,6 +489,10 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
(struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
memcpy(frag_hdr, &header, hdr_len);
+ /* add frag number to frag_hdr */
+ sc = le16_to_cpu(frag_hdr->seq_ctl);
+ sc |= (frag++ & 0x0F);
+ frag_hdr->seq_ctl = cpu_to_le16(sc);
/* If this is not the last fragment, then add the MOREFRAGS
* bit to the frame control */
if (i != nr_frags - 1) {
@@ -486,23 +504,37 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
bytes = bytes_last_frag;
}
- if (i == 0 && !snapped) {
+ if ((((i == 0) && !txb->rts_included) ||
+ ((i == 1) && txb->rts_included)) && !snapped) {
ieee80211_copy_snap(skb_put
(skb_frag, SNAP_SIZE + sizeof(u16)),
ether_type);
bytes -= SNAP_SIZE + sizeof(u16);
}
- skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes);
+ memcpy(skb_put(skb_frag, bytes), skb->data, bytes);
/* Advance the SKB... */
skb_pull(skb, bytes);
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ /* this may take some time. We really don't wonna do this with irqs
+ * disabled. */
+ i = i_unlocked;
+ for (; i < nr_frags; i++) {
+ skb_frag = txb->fragments[i];
/* Encryption routine will move the header forward in order
* to insert the IV between the header and the payload */
if (host_encrypt)
ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
else if (host_build_iv) {
+ struct ieee80211_crypt_data *crypt;
+
+ spin_lock_irqsave( &ieee->lock, flags );
+ crypt = ieee->crypt[ieee->tx_keyidx];
atomic_inc(&crypt->refcnt);
if (crypt->ops->build_iv)
crypt->ops->build_iv(skb_frag, hdr_len,
@@ -510,6 +542,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
ieee->sec.key_sizes[ieee->sec.active_key],
crypt->priv);
atomic_dec(&crypt->refcnt);
+ spin_unlock_irqrestore( &ieee->lock, flags );
}
if (ieee->config &
@@ -518,7 +551,6 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
}
success:
- spin_unlock_irqrestore(&ieee->lock, flags);
dev_kfree_skb_any(skb);
@@ -542,4 +574,92 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
return 1;
}
+/* Incoming 802.11 strucure is converted to a TXB
+ * a block of 802.11 fragment packets (stored as skbs) */
+int ieee80211_tx_frame(struct ieee80211_device *ieee,
+ struct ieee80211_hdr *frame, int hdr_len, int total_len,
+ int encrypt_mpdu)
+{
+ struct ieee80211_txb *txb = NULL;
+ unsigned long flags;
+ struct net_device_stats *stats = &ieee->stats;
+ struct sk_buff *skb_frag;
+ int priority = -1;
+ int fraglen = total_len;
+ int headroom = ieee->tx_headroom;
+ struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx];
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (encrypt_mpdu && (!ieee->sec.encrypt || !crypt))
+ encrypt_mpdu = 0;
+
+ /* If there is no driver handler to take the TXB, dont' bother
+ * creating it... */
+ if (!ieee->hard_start_xmit) {
+ printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name);
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ goto success;
+ }
+
+ if (unlikely(total_len < 24)) {
+ printk(KERN_WARNING "%s: skb too small (%d).\n",
+ ieee->dev->name, total_len);
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ goto success;
+ }
+
+ if (encrypt_mpdu) {
+ frame->frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ fraglen += crypt->ops->extra_mpdu_prefix_len +
+ crypt->ops->extra_mpdu_postfix_len;
+ headroom += crypt->ops->extra_mpdu_prefix_len;
+ }
+
+ /* When we allocate the TXB we allocate enough space for the reserve
+ * and full fragment bytes (bytes_per_frag doesn't include prefix,
+ * postfix, header, FCS, etc.) */
+ txb = ieee80211_alloc_txb(1, fraglen, headroom, GFP_ATOMIC);
+ if (unlikely(!txb)) {
+ printk(KERN_WARNING "%s: Could not allocate TXB\n",
+ ieee->dev->name);
+ goto failed;
+ }
+ txb->encrypted = 0;
+ txb->payload_size = fraglen;
+
+ skb_frag = txb->fragments[0];
+
+ memcpy(skb_put(skb_frag, total_len), frame, total_len);
+
+ if (ieee->config &
+ (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
+ skb_put(skb_frag, 4);
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ /* To avoid overcomplicating things, we do the corner-case frame
+ * encryption in software. The only real situation where encryption is
+ * needed here is during software-based shared key authentication. */
+ if (encrypt_mpdu)
+ ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
+
+ success:
+ if (txb) {
+ if ((*ieee->hard_start_xmit) (txb, ieee->dev, priority) == 0) {
+ stats->tx_packets++;
+ stats->tx_bytes += txb->payload_size;
+ return 0;
+ }
+ ieee80211_txb_free(txb);
+ }
+ return 0;
+
+ failed:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ stats->tx_errors++;
+ return 1;
+}
+
+EXPORT_SYMBOL(ieee80211_tx_frame);
EXPORT_SYMBOL(ieee80211_txb_free);
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c
index 973832dd7faf..689e11b602d3 100644
--- a/net/ieee80211/ieee80211_wx.c
+++ b/net/ieee80211/ieee80211_wx.c
@@ -752,9 +752,98 @@ int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee,
return 0;
}
+int ieee80211_wx_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211_device *ieee = netdev_priv(dev);
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ switch (wrqu->param.flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * Host AP driver does not use these parameters and allows
+ * wpa_supplicant to control them internally.
+ */
+ break;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ break; /* FIXME */
+ case IW_AUTH_DROP_UNENCRYPTED:
+ ieee->drop_unencrypted = !!wrqu->param.value;
+ break;
+ case IW_AUTH_80211_AUTH_ALG:
+ break; /* FIXME */
+ case IW_AUTH_WPA_ENABLED:
+ ieee->privacy_invoked = ieee->wpa_enabled = !!wrqu->param.value;
+ break;
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ ieee->ieee802_1x = !!wrqu->param.value;
+ break;
+ case IW_AUTH_PRIVACY_INVOKED:
+ ieee->privacy_invoked = !!wrqu->param.value;
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ return err;
+}
+
+int ieee80211_wx_get_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211_device *ieee = netdev_priv(dev);
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ switch (wrqu->param.flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ case IW_AUTH_TKIP_COUNTERMEASURES: /* FIXME */
+ case IW_AUTH_80211_AUTH_ALG: /* FIXME */
+ /*
+ * Host AP driver does not use these parameters and allows
+ * wpa_supplicant to control them internally.
+ */
+ err = -EOPNOTSUPP;
+ break;
+ case IW_AUTH_DROP_UNENCRYPTED:
+ wrqu->param.value = ieee->drop_unencrypted;
+ break;
+ case IW_AUTH_WPA_ENABLED:
+ wrqu->param.value = ieee->wpa_enabled;
+ break;
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ wrqu->param.value = ieee->ieee802_1x;
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ return err;
+}
+
EXPORT_SYMBOL(ieee80211_wx_set_encodeext);
EXPORT_SYMBOL(ieee80211_wx_get_encodeext);
EXPORT_SYMBOL(ieee80211_wx_get_scan);
EXPORT_SYMBOL(ieee80211_wx_set_encode);
EXPORT_SYMBOL(ieee80211_wx_get_encode);
+
+EXPORT_SYMBOL_GPL(ieee80211_wx_set_auth);
+EXPORT_SYMBOL_GPL(ieee80211_wx_get_auth);
diff --git a/net/ieee80211/softmac/Kconfig b/net/ieee80211/softmac/Kconfig
new file mode 100644
index 000000000000..2811651cb134
--- /dev/null
+++ b/net/ieee80211/softmac/Kconfig
@@ -0,0 +1,12 @@
+config IEEE80211_SOFTMAC
+ tristate "Software MAC add-on to the IEEE 802.11 networking stack"
+ depends on IEEE80211 && EXPERIMENTAL
+ select WIRELESS_EXT
+ select IEEE80211_CRYPT_WEP
+ ---help---
+ This option enables the hardware independent software MAC addon
+ for the IEEE 802.11 networking stack.
+
+config IEEE80211_SOFTMAC_DEBUG
+ bool "Enable full debugging output"
+ depends on IEEE80211_SOFTMAC
diff --git a/net/ieee80211/softmac/Makefile b/net/ieee80211/softmac/Makefile
new file mode 100644
index 000000000000..bfcb391bb2c7
--- /dev/null
+++ b/net/ieee80211/softmac/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_IEEE80211_SOFTMAC) += ieee80211softmac.o
+ieee80211softmac-objs := \
+ ieee80211softmac_io.o \
+ ieee80211softmac_auth.o \
+ ieee80211softmac_module.o \
+ ieee80211softmac_scan.o \
+ ieee80211softmac_wx.o \
+ ieee80211softmac_assoc.o \
+ ieee80211softmac_event.o
diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c
new file mode 100644
index 000000000000..47b35c160b41
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c
@@ -0,0 +1,542 @@
+/*
+ * This file contains the softmac's association logic.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * 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, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR 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
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+/*
+ * Overview
+ *
+ * Before you can associate, you have to authenticate.
+ *
+ */
+
+static void ieee80211softmac_associated(struct ieee80211softmac_device *mac,
+ struct ieee80211_assoc_response *resp, struct ieee80211softmac_network *net);
+
+/* Sends out an association request to the desired AP */
+static void
+ieee80211softmac_assoc(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ unsigned long flags;
+
+ /* Switch to correct channel for this network */
+ mac->set_channel(mac->dev, net->channel);
+
+ /* Send association request */
+ ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_ASSOC_REQ, 0);
+
+ spin_lock_irqsave(&mac->lock, flags);
+ dprintk(KERN_INFO PFX "sent association request!\n");
+
+ mac->associnfo.associated = 0; /* just to make sure */
+
+ /* Set a timer for timeout */
+ /* FIXME: make timeout configurable */
+ if (likely(mac->running))
+ queue_delayed_work(mac->wq, &mac->associnfo.timeout, 5 * HZ);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+static void
+ieee80211softmac_assoc_adhoc(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ unsigned long flags;
+
+ dprintk(KERN_INFO PFX "Associating to ad-hoc bssid " MAC_FMT "\n", MAC_ARG(net->bssid)); /* P3 */
+
+ mac->set_channel(mac->dev, net->channel);
+
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_associated(mac, NULL, net);
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATED, net);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+void
+ieee80211softmac_assoc_timeout(struct work_struct *work)
+{
+ struct ieee80211softmac_device *mac =
+ container_of(work, struct ieee80211softmac_device,
+ associnfo.timeout.work);
+
+ struct ieee80211softmac_network *n = NULL;
+
+ mutex_lock(&mac->associnfo.mutex);
+ /* we might race against ieee80211softmac_handle_assoc_response,
+ * so make sure only one of us does something */
+ if (!mac->associnfo.associating)
+ goto out;
+ mac->associnfo.associating = 0;
+ mac->associnfo.bssvalid = 0;
+ mac->associnfo.associated = 0;
+
+ n = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid);
+
+ dprintk(KERN_INFO PFX "assoc request timed out!\n");
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, n);
+out:
+ mutex_unlock(&mac->associnfo.mutex);
+}
+
+void
+ieee80211softmac_disassoc(struct ieee80211softmac_device *mac)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ if (mac->associnfo.associating)
+ cancel_delayed_work(&mac->associnfo.timeout);
+
+ netif_carrier_off(mac->dev);
+
+ mac->associnfo.associated = 0;
+ mac->associnfo.bssvalid = 0;
+ mac->associnfo.associating = 0;
+ mac->associnfo.channel = -1;
+ ieee80211softmac_init_bss(mac);
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_DISASSOCIATED, NULL);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+/* Sends out a disassociation request to the desired AP */
+void
+ieee80211softmac_send_disassoc_req(struct ieee80211softmac_device *mac, u16 reason)
+{
+ struct ieee80211softmac_network *found;
+
+ if (mac->associnfo.bssvalid && mac->associnfo.associated) {
+ found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid);
+ if (found)
+ ieee80211softmac_send_mgt_frame(mac, found, IEEE80211_STYPE_DISASSOC, reason);
+ }
+
+ ieee80211softmac_disassoc(mac);
+}
+
+static inline int
+we_support_all_basic_rates(struct ieee80211softmac_device *mac, u8 *from, u8 from_len)
+{
+ int idx;
+ u8 rate;
+
+ for (idx = 0; idx < (from_len); idx++) {
+ rate = (from)[idx];
+ if (!(rate & IEEE80211_BASIC_RATE_MASK))
+ continue;
+ rate &= ~IEEE80211_BASIC_RATE_MASK;
+ if (!ieee80211softmac_ratesinfo_rate_supported(&mac->ratesinfo, rate))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+network_matches_request(struct ieee80211softmac_device *mac, struct ieee80211_network *net)
+{
+ /* we cannot associate to networks whose name we don't know */
+ if (ieee80211_is_empty_essid(net->ssid, net->ssid_len))
+ return 0;
+ /* do not associate to a network whose BSSBasicRateSet we cannot support */
+ if (!we_support_all_basic_rates(mac, net->rates, net->rates_len))
+ return 0;
+ /* do we really need to check the ex rates? */
+ if (!we_support_all_basic_rates(mac, net->rates_ex, net->rates_ex_len))
+ return 0;
+ /* assume that users know what they're doing ...
+ * (note we don't let them select a net we're incompatible with) */
+ if (mac->associnfo.bssfixed) {
+ return !memcmp(mac->associnfo.bssid, net->bssid, ETH_ALEN);
+ }
+ /* if 'ANY' network requested, take any that doesn't have privacy enabled */
+ if (mac->associnfo.req_essid.len == 0
+ && !(net->capability & WLAN_CAPABILITY_PRIVACY))
+ return 1;
+ if (net->ssid_len != mac->associnfo.req_essid.len)
+ return 0;
+ if (!memcmp(net->ssid, mac->associnfo.req_essid.data, mac->associnfo.req_essid.len))
+ return 1;
+
+ return 0;
+}
+
+static void
+ieee80211softmac_assoc_notify_scan(struct net_device *dev, int event_type, void *context)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ ieee80211softmac_assoc_work(&mac->associnfo.work.work);
+}
+
+static void
+ieee80211softmac_assoc_notify_auth(struct net_device *dev, int event_type, void *context)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ switch (event_type) {
+ case IEEE80211SOFTMAC_EVENT_AUTHENTICATED:
+ ieee80211softmac_assoc_work(&mac->associnfo.work.work);
+ break;
+ case IEEE80211SOFTMAC_EVENT_AUTH_FAILED:
+ case IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT:
+ ieee80211softmac_disassoc(mac);
+ break;
+ }
+}
+
+/* This function is called to handle userspace requests (asynchronously) */
+void
+ieee80211softmac_assoc_work(struct work_struct *work)
+{
+ struct ieee80211softmac_device *mac =
+ container_of(work, struct ieee80211softmac_device,
+ associnfo.work.work);
+
+ struct ieee80211softmac_network *found = NULL;
+ struct ieee80211_network *net = NULL, *best = NULL;
+ int bssvalid;
+ unsigned long flags;
+
+ mutex_lock(&mac->associnfo.mutex);
+
+ if (!mac->associnfo.associating)
+ goto out;
+
+ /* ieee80211_disassoc might clear this */
+ bssvalid = mac->associnfo.bssvalid;
+
+ /* meh */
+ if (mac->associnfo.associated)
+ ieee80211softmac_send_disassoc_req(mac, WLAN_REASON_DISASSOC_STA_HAS_LEFT);
+
+ /* try to find the requested network in our list, if we found one already */
+ if (bssvalid || mac->associnfo.bssfixed) {
+ found = ieee80211softmac_get_network_by_bssid_with_channel(mac, mac->associnfo.bssid, mac->associnfo.channel);
+ }
+
+ /* Search the ieee80211 networks for this network if we didn't find it by bssid,
+ * but only if we've scanned at least once (to get a better list of networks to
+ * select from). If we have not scanned before, the !found logic below will be
+ * invoked and will scan. */
+ if (!found && (mac->associnfo.scan_retry < IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT))
+ {
+ s8 rssi = -128; /* if I don't initialise, gcc emits an invalid warning
+ because it cannot follow the best pointer logic. */
+ spin_lock_irqsave(&mac->ieee->lock, flags);
+ list_for_each_entry(net, &mac->ieee->network_list, list) {
+ /* we're supposed to find the network with
+ * the best signal here, as we're asked to join
+ * any network with a specific ESSID, and many
+ * different ones could have that.
+ *
+ * I'll for now just go with the reported rssi.
+ *
+ * We also should take into account the rateset
+ * here to find the best BSSID to try.
+ */
+ if (network_matches_request(mac, net)) {
+ if (!best) {
+ best = net;
+ rssi = best->stats.rssi;
+ continue;
+ }
+ /* we already had a matching network, so
+ * compare their properties to get the
+ * better of the two ... (see above)
+ */
+ if (rssi < net->stats.rssi) {
+ best = net;
+ rssi = best->stats.rssi;
+ }
+ }
+ }
+ /* if we unlock here, we might get interrupted and the `best'
+ * pointer could go stale */
+ if (best) {
+ found = ieee80211softmac_create_network(mac, best);
+ /* if found is still NULL, then we got -ENOMEM somewhere */
+ if (found)
+ ieee80211softmac_add_network(mac, found);
+ }
+ spin_unlock_irqrestore(&mac->ieee->lock, flags);
+ }
+
+ if (!found) {
+ if (mac->associnfo.scan_retry > 0) {
+ mac->associnfo.scan_retry--;
+
+ /* We know of no such network. Let's scan.
+ * NB: this also happens if we had no memory to copy the network info...
+ * Maybe we can hope to have more memory after scanning finishes ;)
+ */
+ dprintk(KERN_INFO PFX "Associate: Scanning for networks first.\n");
+ ieee80211softmac_notify(mac->dev, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, ieee80211softmac_assoc_notify_scan, NULL);
+ if (ieee80211softmac_start_scan(mac))
+ dprintk(KERN_INFO PFX "Associate: failed to initiate scan. Is device up?\n");
+ goto out;
+ } else {
+ mac->associnfo.associating = 0;
+ mac->associnfo.associated = 0;
+
+ dprintk(KERN_INFO PFX "Unable to find matching network after scan!\n");
+ /* reset the retry counter for the next user request since we
+ * break out and don't reschedule ourselves after this point. */
+ mac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_NET_NOT_FOUND, NULL);
+ goto out;
+ }
+ }
+
+ /* reset the retry counter for the next user request since we
+ * now found a net and will try to associate to it, but not
+ * schedule this function again. */
+ mac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
+ mac->associnfo.bssvalid = 1;
+ mac->associnfo.channel = found->channel;
+ memcpy(mac->associnfo.bssid, found->bssid, ETH_ALEN);
+ /* copy the ESSID for displaying it */
+ mac->associnfo.associate_essid.len = found->essid.len;
+ memcpy(mac->associnfo.associate_essid.data, found->essid.data, IW_ESSID_MAX_SIZE + 1);
+
+ /* we found a network! authenticate (if necessary) and associate to it. */
+ if (found->authenticating) {
+ dprintk(KERN_INFO PFX "Already requested authentication, waiting...\n");
+ if(!mac->associnfo.assoc_wait) {
+ mac->associnfo.assoc_wait = 1;
+ ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL);
+ }
+ goto out;
+ }
+ if (found->mode == IW_MODE_ADHOC) {
+ ieee80211softmac_assoc_adhoc(mac, found);
+ mac->associnfo.assoc_wait = 0;
+ goto out;
+ }
+ if (!found->authenticated && !found->authenticating) {
+ /* This relies on the fact that _auth_req only queues the work,
+ * otherwise adding the notification would be racy. */
+ if (!ieee80211softmac_auth_req(mac, found)) {
+ if(!mac->associnfo.assoc_wait) {
+ dprintk(KERN_INFO PFX "Cannot associate without being authenticated, requested authentication\n");
+ mac->associnfo.assoc_wait = 1;
+ ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL);
+ }
+ } else {
+ printkl(KERN_WARNING PFX "Not authenticated, but requesting authentication failed. Giving up to associate\n");
+ mac->associnfo.assoc_wait = 0;
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, found);
+ }
+ goto out;
+ }
+ /* finally! now we can start associating */
+ mac->associnfo.assoc_wait = 0;
+ ieee80211softmac_assoc(mac, found);
+
+out:
+ mutex_unlock(&mac->associnfo.mutex);
+}
+
+/* call this to do whatever is necessary when we're associated */
+static void
+ieee80211softmac_associated(struct ieee80211softmac_device *mac,
+ struct ieee80211_assoc_response * resp,
+ struct ieee80211softmac_network *net)
+{
+ u16 cap = 0;
+ u8 erp_value = net->erp_value;
+
+ if (resp != NULL)
+ cap = le16_to_cpu(resp->capability);
+ mac->associnfo.associating = 0;
+ mac->bssinfo.supported_rates = net->supported_rates;
+ ieee80211softmac_recalc_txrates(mac);
+
+ mac->associnfo.associated = 1;
+
+ if (resp != NULL)
+ mac->associnfo.short_preamble_available =
+ (cap & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0;
+ ieee80211softmac_process_erp(mac, erp_value);
+
+ if (mac->set_bssid_filter)
+ mac->set_bssid_filter(mac->dev, net->bssid);
+ memcpy(mac->ieee->bssid, net->bssid, ETH_ALEN);
+ netif_carrier_on(mac->dev);
+
+ if (resp != NULL)
+ mac->association_id = le16_to_cpup(&resp->aid);
+}
+
+/* received frame handling functions */
+int
+ieee80211softmac_handle_assoc_response(struct net_device * dev,
+ struct ieee80211_assoc_response * resp,
+ struct ieee80211_network * _ieee80211_network)
+{
+ /* NOTE: the network parameter has to be mostly ignored by
+ * this code because it is the ieee80211's pointer
+ * to the struct, not ours (we made a copy)
+ */
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ u16 status = le16_to_cpup(&resp->status);
+ struct ieee80211softmac_network *network = NULL;
+ unsigned long flags;
+
+ if (unlikely(!mac->running))
+ return -ENODEV;
+
+ spin_lock_irqsave(&mac->lock, flags);
+
+ if (!mac->associnfo.associating) {
+ /* we race against the timeout function, so make sure
+ * only one of us can do work */
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+ }
+ network = ieee80211softmac_get_network_by_bssid_locked(mac, resp->header.addr3);
+
+ /* someone sending us things without us knowing him? Ignore. */
+ if (!network) {
+ dprintk(KERN_INFO PFX "Received unrequested assocation response from " MAC_FMT "\n", MAC_ARG(resp->header.addr3));
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+ }
+
+ /* now that we know it was for us, we can cancel the timeout */
+ cancel_delayed_work(&mac->associnfo.timeout);
+
+ /* if the association response included an ERP IE, update our saved
+ * copy */
+ if (_ieee80211_network->flags & NETWORK_HAS_ERP_VALUE)
+ network->erp_value = _ieee80211_network->erp_value;
+
+ switch (status) {
+ case 0:
+ dprintk(KERN_INFO PFX "associated!\n");
+ ieee80211softmac_associated(mac, resp, network);
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATED, network);
+ break;
+ case WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH:
+ if (!network->auth_desynced_once) {
+ /* there seem to be a few rare cases where our view of
+ * the world is obscured, or buggy APs that don't DEAUTH
+ * us properly. So we handle that, but allow it only once.
+ */
+ printkl(KERN_INFO PFX "We were not authenticated during association, retrying...\n");
+ network->authenticated = 0;
+ /* we don't want to do this more than once ... */
+ network->auth_desynced_once = 1;
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+ break;
+ }
+ default:
+ dprintk(KERN_INFO PFX "associating failed (reason: 0x%x)!\n", status);
+ mac->associnfo.associating = 0;
+ mac->associnfo.bssvalid = 0;
+ mac->associnfo.associated = 0;
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, network);
+ }
+
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+}
+
+void
+ieee80211softmac_try_reassoc(struct ieee80211softmac_device *mac)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ mac->associnfo.associating = 1;
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+int
+ieee80211softmac_handle_disassoc(struct net_device * dev,
+ struct ieee80211_disassoc *disassoc)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct ieee80211softmac_network *network = NULL;
+ struct ieee80211_network *net = NULL;
+ struct ieee80211_network *nettmp = NULL;
+ unsigned long flags;
+
+ if (unlikely(!mac->running))
+ return -ENODEV;
+
+ if (memcmp(disassoc->header.addr2, mac->associnfo.bssid, ETH_ALEN))
+ return 0;
+
+ if (memcmp(disassoc->header.addr1, mac->dev->dev_addr, ETH_ALEN))
+ return 0;
+
+ dprintk(KERN_INFO PFX "got disassoc frame\n");
+
+ /* Delete this network, so a rescan is triggered when again trying to
+ * reassociate. This is necessary because of channel switches and we
+ * don't want logic to find this channel again. */
+ network = ieee80211softmac_get_network_by_bssid_with_channel( mac, mac->associnfo.bssid, mac->associnfo.channel );
+ ieee80211softmac_disassoc( mac ); /* associnfo no longer valid */
+
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_del_network_locked(mac, network);
+ if(network->challenge != NULL)
+ kfree(network->challenge);
+ kfree(network);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* delete all ieee802.11 networks */
+ spin_lock_irqsave(&mac->ieee->lock, flags);
+ list_for_each_entry_safe(net, nettmp, &mac->ieee->network_list, list) {
+ struct ieee80211_network* net80211 = list_entry( &net->list, struct ieee80211_network, list);
+ if( !memcmp(net80211->bssid, mac->associnfo.bssid, ETH_ALEN))
+ list_del(&net80211->list);
+ }
+ spin_unlock_irqrestore(&mac->ieee->lock, flags);
+
+ ieee80211softmac_try_reassoc(mac);
+
+ return 0;
+}
+
+int
+ieee80211softmac_handle_reassoc_req(struct net_device * dev,
+ struct ieee80211_reassoc_request * resp)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct ieee80211softmac_network *network;
+
+ if (unlikely(!mac->running))
+ return -ENODEV;
+
+ network = ieee80211softmac_get_network_by_bssid(mac, resp->header.addr3);
+ if (!network) {
+ dprintkl(KERN_INFO PFX "reassoc request from unknown network\n");
+ return 0;
+ }
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+
+ return 0;
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_auth.c b/net/ieee80211/softmac/ieee80211softmac_auth.c
new file mode 100644
index 000000000000..3441ca53f533
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_auth.c
@@ -0,0 +1,413 @@
+/*
+ * This file contains the softmac's authentication logic.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * 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, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR 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
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+
+static void ieee80211softmac_auth_queue(struct work_struct *work);
+/* Queues an auth request to the desired AP */
+int
+ieee80211softmac_auth_req(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net)
+{
+ struct ieee80211softmac_auth_queue_item *auth;
+ unsigned long flags;
+
+ if (net->authenticating || net->authenticated)
+ return 0;
+ net->authenticating = 1;
+
+ /* Add the network if it's not already added */
+ ieee80211softmac_add_network(mac, net);
+
+ dprintk(KERN_NOTICE PFX "Queueing Authentication Request to "MAC_FMT"\n", MAC_ARG(net->bssid));
+ /* Queue the auth request */
+ auth = (struct ieee80211softmac_auth_queue_item *)
+ kmalloc(sizeof(struct ieee80211softmac_auth_queue_item), GFP_KERNEL);
+ if(auth == NULL)
+ return -ENOMEM;
+
+ auth->net = net;
+ auth->mac = mac;
+ auth->retry = IEEE80211SOFTMAC_AUTH_RETRY_LIMIT;
+ auth->state = IEEE80211SOFTMAC_AUTH_OPEN_REQUEST;
+ INIT_DELAYED_WORK(&auth->work, ieee80211softmac_auth_queue);
+
+ /* Lock (for list) */
+ spin_lock_irqsave(&mac->lock, flags);
+
+ /* add to list */
+ list_add_tail(&auth->list, &mac->auth_queue);
+ queue_delayed_work(mac->wq, &auth->work, 0);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return 0;
+}
+
+
+/* Sends an auth request to the desired AP and handles timeouts */
+static void
+ieee80211softmac_auth_queue(struct work_struct *work)
+{
+ struct ieee80211softmac_device *mac;
+ struct ieee80211softmac_auth_queue_item *auth;
+ struct ieee80211softmac_network *net;
+ unsigned long flags;
+
+ auth = container_of(work, struct ieee80211softmac_auth_queue_item,
+ work.work);
+
+ net = auth->net;
+ mac = auth->mac;
+
+ if(auth->retry > 0) {
+ /* Switch to correct channel for this network */
+ mac->set_channel(mac->dev, net->channel);
+
+ /* Lock and set flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ if (unlikely(!mac->running)) {
+ /* Prevent reschedule on workqueue flush */
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return;
+ }
+ net->authenticated = 0;
+ /* add a timeout call so we eventually give up waiting for an auth reply */
+ queue_delayed_work(mac->wq, &auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT);
+ auth->retry--;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ if (ieee80211softmac_send_mgt_frame(mac, auth->net, IEEE80211_STYPE_AUTH, auth->state))
+ dprintk(KERN_NOTICE PFX "Sending Authentication Request to "MAC_FMT" failed (this shouldn't happen, wait for the timeout).\n", MAC_ARG(net->bssid));
+ else
+ dprintk(KERN_NOTICE PFX "Sent Authentication Request to "MAC_FMT".\n", MAC_ARG(net->bssid));
+ return;
+ }
+
+ printkl(KERN_WARNING PFX "Authentication timed out with "MAC_FMT"\n", MAC_ARG(net->bssid));
+ /* Remove this item from the queue */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT, net);
+ cancel_delayed_work(&auth->work); /* just to make sure... */
+ list_del(&auth->list);
+ spin_unlock_irqrestore(&mac->lock, flags);
+ /* Free it */
+ kfree(auth);
+}
+
+/* Sends a response to an auth challenge (for shared key auth). */
+static void
+ieee80211softmac_auth_challenge_response(struct work_struct *work)
+{
+ struct ieee80211softmac_auth_queue_item *aq =
+ container_of(work, struct ieee80211softmac_auth_queue_item,
+ work.work);
+
+
+ /* Send our response */
+ ieee80211softmac_send_mgt_frame(aq->mac, aq->net, IEEE80211_STYPE_AUTH, aq->state);
+}
+
+/* Handle the auth response from the AP
+ * This should be registered with ieee80211 as handle_auth
+ */
+int
+ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth)
+{
+
+ struct list_head *list_ptr;
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct ieee80211softmac_auth_queue_item *aq = NULL;
+ struct ieee80211softmac_network *net = NULL;
+ unsigned long flags;
+ u8 * data;
+
+ if (unlikely(!mac->running))
+ return -ENODEV;
+
+ /* Find correct auth queue item */
+ spin_lock_irqsave(&mac->lock, flags);
+ list_for_each(list_ptr, &mac->auth_queue) {
+ aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list);
+ net = aq->net;
+ if (!memcmp(net->bssid, auth->header.addr2, ETH_ALEN))
+ break;
+ else
+ aq = NULL;
+ }
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Make sure that we've got an auth queue item for this request */
+ if(aq == NULL)
+ {
+ dprintkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but no queue item exists.\n", MAC_ARG(auth->header.addr2));
+ /* Error #? */
+ return -1;
+ }
+
+ /* Check for out of order authentication */
+ if(!net->authenticating)
+ {
+ dprintkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but did not request authentication.\n",MAC_ARG(auth->header.addr2));
+ return -1;
+ }
+
+ /* Parse the auth packet */
+ switch(auth->algorithm) {
+ case WLAN_AUTH_OPEN:
+ /* Check the status code of the response */
+
+ switch(auth->status) {
+ case WLAN_STATUS_SUCCESS:
+ /* Update the status to Authenticated */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 1;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Send event */
+ dprintkl(KERN_NOTICE PFX "Open Authentication completed with "MAC_FMT"\n", MAC_ARG(net->bssid));
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_AUTHENTICATED, net);
+ break;
+ default:
+ /* Lock and reset flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticated = 0;
+ net->authenticating = 0;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ dprintkl(KERN_NOTICE PFX "Open Authentication with "MAC_FMT" failed, error code: %i\n",
+ MAC_ARG(net->bssid), le16_to_cpup(&auth->status));
+ /* Count the error? */
+ break;
+ }
+ goto free_aq;
+ break;
+ case WLAN_AUTH_SHARED_KEY:
+ /* Figure out where we are in the process */
+ switch(auth->transaction) {
+ case IEEE80211SOFTMAC_AUTH_SHARED_CHALLENGE:
+ /* Check to make sure we have a challenge IE */
+ data = (u8 *)auth->info_element;
+ if (*data++ != MFIE_TYPE_CHALLENGE) {
+ dprintkl(KERN_NOTICE PFX "Shared Key Authentication failed due to a missing challenge.\n");
+ break;
+ }
+ /* Save the challenge */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->challenge_len = *data++;
+ if (net->challenge_len > WLAN_AUTH_CHALLENGE_LEN)
+ net->challenge_len = WLAN_AUTH_CHALLENGE_LEN;
+ kfree(net->challenge);
+
+
+ net->challenge = kmalloc(net->challenge_len, GFP_ATOMIC);
+ if (net->challenge == NULL) {
+ printkl(KERN_NOTICE PFX "Shared Key "
+ "Authentication failed due to "
+ "memory shortage.\n");
+ spin_unlock_irqrestore(&mac->lock, flags);
+ break;
+ }
+ memcpy(net->challenge, data, net->challenge_len);
+ aq->state = IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE;
+
+ /* We reuse the work struct from the auth request here.
+ * It is safe to do so as each one is per-request, and
+ * at this point (dealing with authentication response)
+ * we have obviously already sent the initial auth
+ * request. */
+ cancel_delayed_work(&aq->work);
+ INIT_DELAYED_WORK(&aq->work, &ieee80211softmac_auth_challenge_response);
+ queue_delayed_work(mac->wq, &aq->work, 0);
+
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+ case IEEE80211SOFTMAC_AUTH_SHARED_PASS:
+ kfree(net->challenge);
+ net->challenge = NULL;
+ net->challenge_len = 0;
+ /* Check the status code of the response */
+ switch(auth->status) {
+ case WLAN_STATUS_SUCCESS:
+ /* Update the status to Authenticated */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 1;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ dprintkl(KERN_NOTICE PFX "Shared Key Authentication completed with "MAC_FMT"\n",
+ MAC_ARG(net->bssid));
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_AUTHENTICATED, net);
+ break;
+ default:
+ dprintkl(KERN_NOTICE PFX "Shared Key Authentication with "MAC_FMT" failed, error code: %i\n",
+ MAC_ARG(net->bssid), le16_to_cpup(&auth->status));
+ /* Lock and reset flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 0;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ /* Count the error? */
+ break;
+ }
+ goto free_aq;
+ break;
+ default:
+ printkl(KERN_WARNING PFX "Unhandled Authentication Step: %i\n", auth->transaction);
+ break;
+ }
+ goto free_aq;
+ break;
+ default:
+ /* ERROR */
+ goto free_aq;
+ break;
+ }
+ return 0;
+free_aq:
+ /* Cancel the timeout */
+ spin_lock_irqsave(&mac->lock, flags);
+ cancel_delayed_work(&aq->work);
+ /* Remove this item from the queue */
+ list_del(&aq->list);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Free it */
+ kfree(aq);
+ return 0;
+}
+
+/*
+ * Handle deauthorization
+ */
+static void
+ieee80211softmac_deauth_from_net(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net)
+{
+ struct ieee80211softmac_auth_queue_item *aq = NULL;
+ struct list_head *list_ptr;
+ unsigned long flags;
+
+ /* deauthentication implies disassociation */
+ ieee80211softmac_disassoc(mac);
+
+ /* Lock and reset status flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 0;
+
+ /* Find correct auth queue item, if it exists */
+ list_for_each(list_ptr, &mac->auth_queue) {
+ aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list);
+ if (!memcmp(net->bssid, aq->net->bssid, ETH_ALEN))
+ break;
+ else
+ aq = NULL;
+ }
+
+ /* Cancel pending work */
+ if(aq != NULL)
+ /* Not entirely safe? What about running work? */
+ cancel_delayed_work(&aq->work);
+
+ /* Free our network ref */
+ ieee80211softmac_del_network_locked(mac, net);
+ if(net->challenge != NULL)
+ kfree(net->challenge);
+ kfree(net);
+
+ /* can't transmit data right now... */
+ netif_carrier_off(mac->dev);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ ieee80211softmac_try_reassoc(mac);
+}
+
+/*
+ * Sends a deauth request to the desired AP
+ */
+int
+ieee80211softmac_deauth_req(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net, int reason)
+{
+ int ret;
+
+ /* Make sure the network is authenticated */
+ if (!net->authenticated)
+ {
+ dprintkl(KERN_DEBUG PFX "Can't send deauthentication packet, network is not authenticated.\n");
+ /* Error okay? */
+ return -EPERM;
+ }
+
+ /* Send the de-auth packet */
+ if((ret = ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_DEAUTH, reason)))
+ return ret;
+
+ ieee80211softmac_deauth_from_net(mac, net);
+ return 0;
+}
+
+/*
+ * This should be registered with ieee80211 as handle_deauth
+ */
+int
+ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_deauth *deauth)
+{
+
+ struct ieee80211softmac_network *net = NULL;
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ if (unlikely(!mac->running))
+ return -ENODEV;
+
+ if (!deauth) {
+ dprintk("deauth without deauth packet. eek!\n");
+ return 0;
+ }
+
+ net = ieee80211softmac_get_network_by_bssid(mac, deauth->header.addr2);
+
+ if (net == NULL) {
+ dprintkl(KERN_DEBUG PFX "Received deauthentication packet from "MAC_FMT", but that network is unknown.\n",
+ MAC_ARG(deauth->header.addr2));
+ return 0;
+ }
+
+ /* Make sure the network is authenticated */
+ if(!net->authenticated)
+ {
+ dprintkl(KERN_DEBUG PFX "Can't perform deauthentication, network is not authenticated.\n");
+ /* Error okay? */
+ return -EPERM;
+ }
+
+ ieee80211softmac_deauth_from_net(mac, net);
+
+ /* let's try to re-associate */
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+ return 0;
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_event.c b/net/ieee80211/softmac/ieee80211softmac_event.c
new file mode 100644
index 000000000000..f1d1790e179c
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_event.c
@@ -0,0 +1,190 @@
+/*
+ * Event system
+ * Also see comments in public header file and longer explanation below.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * 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, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR 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
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+/*
+ * Each event has associated to it
+ * - an event type (see constants in public header)
+ * - an event context (see below)
+ * - the function to be called
+ * - a context (extra parameter to call the function with)
+ * - and the softmac struct
+ *
+ * The event context is private and can only be used from
+ * within this module. Its meaning varies with the event
+ * type:
+ * SCAN_FINISHED,
+ * DISASSOCIATED: NULL
+ * ASSOCIATED,
+ * ASSOCIATE_FAILED,
+ * ASSOCIATE_TIMEOUT,
+ * AUTHENTICATED,
+ * AUTH_FAILED,
+ * AUTH_TIMEOUT: a pointer to the network struct
+ * ...
+ * Code within this module can use the event context to be only
+ * called when the event is true for that specific context
+ * as per above table.
+ * If the event context is NULL, then the notification is always called,
+ * regardless of the event context. The event context is not passed to
+ * the callback, it is assumed that the context suffices.
+ *
+ * You can also use the event context only by setting the event type
+ * to -1 (private use only), in which case you'll be notified
+ * whenever the event context matches.
+ */
+
+static char *event_descriptions[IEEE80211SOFTMAC_EVENT_LAST+1] = {
+ NULL, /* scan finished */
+ NULL, /* associated */
+ "associating failed",
+ "associating timed out",
+ "authenticated",
+ "authenticating failed",
+ "authenticating timed out",
+ "associating failed because no suitable network was found",
+ NULL, /* disassociated */
+};
+
+
+static void
+ieee80211softmac_notify_callback(struct work_struct *work)
+{
+ struct ieee80211softmac_event *pevent =
+ container_of(work, struct ieee80211softmac_event, work.work);
+ struct ieee80211softmac_event event = *pevent;
+ kfree(pevent);
+
+ event.fun(event.mac->dev, event.event_type, event.context);
+}
+
+
+int
+ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac,
+ int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask)
+{
+ struct ieee80211softmac_event *eventptr;
+ unsigned long flags;
+
+ if (event < -1 || event > IEEE80211SOFTMAC_EVENT_LAST)
+ return -ENOSYS;
+
+ if (!fun)
+ return -EINVAL;
+
+ eventptr = kmalloc(sizeof(struct ieee80211softmac_event), gfp_mask);
+ if (!eventptr)
+ return -ENOMEM;
+
+ eventptr->event_type = event;
+ INIT_DELAYED_WORK(&eventptr->work, ieee80211softmac_notify_callback);
+ eventptr->fun = fun;
+ eventptr->context = context;
+ eventptr->mac = mac;
+ eventptr->event_context = event_context;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ list_add(&eventptr->list, &mac->events);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return 0;
+}
+
+int
+ieee80211softmac_notify_gfp(struct net_device *dev,
+ int event, notify_function_ptr fun, void *context, gfp_t gfp_mask)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ if (event < 0 || event > IEEE80211SOFTMAC_EVENT_LAST)
+ return -ENOSYS;
+
+ return ieee80211softmac_notify_internal(mac, event, NULL, fun, context, gfp_mask);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_notify_gfp);
+
+/* private -- calling all callbacks that were specified */
+void
+ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_ctx)
+{
+ struct ieee80211softmac_event *eventptr, *tmp;
+ struct ieee80211softmac_network *network;
+
+ if (event >= 0) {
+ union iwreq_data wrqu;
+ int we_event;
+ char *msg = NULL;
+
+ memset(&wrqu, '\0', sizeof (union iwreq_data));
+
+ switch(event) {
+ case IEEE80211SOFTMAC_EVENT_ASSOCIATED:
+ network = (struct ieee80211softmac_network *)event_ctx;
+ memcpy(wrqu.ap_addr.sa_data, &network->bssid[0], ETH_ALEN);
+ /* fall through */
+ case IEEE80211SOFTMAC_EVENT_DISASSOCIATED:
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ we_event = SIOCGIWAP;
+ break;
+ case IEEE80211SOFTMAC_EVENT_SCAN_FINISHED:
+ we_event = SIOCGIWSCAN;
+ break;
+ default:
+ msg = event_descriptions[event];
+ if (!msg)
+ msg = "SOFTMAC EVENT BUG";
+ wrqu.data.length = strlen(msg);
+ we_event = IWEVCUSTOM;
+ break;
+ }
+ wireless_send_event(mac->dev, we_event, &wrqu, msg);
+ }
+
+ if (!list_empty(&mac->events))
+ list_for_each_entry_safe(eventptr, tmp, &mac->events, list) {
+ if ((eventptr->event_type == event || eventptr->event_type == -1)
+ && (eventptr->event_context == NULL || eventptr->event_context == event_ctx)) {
+ list_del(&eventptr->list);
+ /* User may have subscribed to ANY event, so
+ * we tell them which event triggered it. */
+ eventptr->event_type = event;
+ queue_delayed_work(mac->wq, &eventptr->work, 0);
+ }
+ }
+}
+
+void
+ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_call_events_locked(mac, event, event_ctx);
+
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_io.c b/net/ieee80211/softmac/ieee80211softmac_io.c
new file mode 100644
index 000000000000..19a690046c72
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_io.c
@@ -0,0 +1,488 @@
+/*
+ * Some parts based on code from net80211
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * 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.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "ieee80211softmac_priv.h"
+
+/* Helper functions for inserting data into the frames */
+
+/*
+ * Adds an ESSID element to the frame
+ *
+ */
+static u8 *
+ieee80211softmac_add_essid(u8 *dst, struct ieee80211softmac_essid *essid)
+{
+ if (essid) {
+ *dst++ = MFIE_TYPE_SSID;
+ *dst++ = essid->len;
+ memcpy(dst, essid->data, essid->len);
+ return dst+essid->len;
+ } else {
+ *dst++ = MFIE_TYPE_SSID;
+ *dst++ = 0;
+ return dst;
+ }
+}
+
+/* Adds Supported Rates and if required Extended Rates Information Element
+ * to the frame, ASSUMES WE HAVE A SORTED LIST OF RATES */
+static u8 *
+ieee80211softmac_frame_add_rates(u8 *dst, const struct ieee80211softmac_ratesinfo *r)
+{
+ int cck_len, ofdm_len;
+ *dst++ = MFIE_TYPE_RATES;
+
+ for(cck_len=0; ieee80211_is_cck_rate(r->rates[cck_len]) && (cck_len < r->count);cck_len++);
+
+ if(cck_len > IEEE80211SOFTMAC_MAX_RATES_LEN)
+ cck_len = IEEE80211SOFTMAC_MAX_RATES_LEN;
+ *dst++ = cck_len;
+ memcpy(dst, r->rates, cck_len);
+ dst += cck_len;
+
+ if(cck_len < r->count){
+ for (ofdm_len=0; ieee80211_is_ofdm_rate(r->rates[ofdm_len + cck_len]) && (ofdm_len + cck_len < r->count); ofdm_len++);
+ if (ofdm_len > 0) {
+ if (ofdm_len > IEEE80211SOFTMAC_MAX_EX_RATES_LEN)
+ ofdm_len = IEEE80211SOFTMAC_MAX_EX_RATES_LEN;
+ *dst++ = MFIE_TYPE_RATES_EX;
+ *dst++ = ofdm_len;
+ memcpy(dst, r->rates + cck_len, ofdm_len);
+ dst += ofdm_len;
+ }
+ }
+ return dst;
+}
+
+/* Allocate a management frame */
+static u8 *
+ieee80211softmac_alloc_mgt(u32 size)
+{
+ u8 * data;
+
+ /* Add the header and FCS to the size */
+ size = size + IEEE80211_3ADDR_LEN;
+ if(size > IEEE80211_DATA_LEN)
+ return NULL;
+ /* Allocate the frame */
+ data = kzalloc(size, GFP_ATOMIC);
+ return data;
+}
+
+/*
+ * Add a 2 Address Header
+ */
+static void
+ieee80211softmac_hdr_2addr(struct ieee80211softmac_device *mac,
+ struct ieee80211_hdr_2addr *header, u32 type, u8 *dest)
+{
+ /* Fill in the frame control flags */
+ header->frame_ctl = cpu_to_le16(type);
+ /* Control packets always have WEP turned off */
+ if(type > IEEE80211_STYPE_CFENDACK && type < IEEE80211_STYPE_PSPOLL)
+ header->frame_ctl |= mac->ieee->sec.level ? cpu_to_le16(IEEE80211_FCTL_PROTECTED) : 0;
+
+ /* Fill in the duration */
+ header->duration_id = 0;
+ /* FIXME: How do I find this?
+ * calculate. But most drivers just fill in 0 (except if it's a station id of course) */
+
+ /* Fill in the Destination Address */
+ if(dest == NULL)
+ memset(header->addr1, 0xFF, ETH_ALEN);
+ else
+ memcpy(header->addr1, dest, ETH_ALEN);
+ /* Fill in the Source Address */
+ memcpy(header->addr2, mac->ieee->dev->dev_addr, ETH_ALEN);
+
+}
+
+
+/* Add a 3 Address Header */
+static void
+ieee80211softmac_hdr_3addr(struct ieee80211softmac_device *mac,
+ struct ieee80211_hdr_3addr *header, u32 type, u8 *dest, u8 *bssid)
+{
+ /* This is common with 2addr, so use that instead */
+ ieee80211softmac_hdr_2addr(mac, (struct ieee80211_hdr_2addr *)header, type, dest);
+
+ /* Fill in the BSS ID */
+ if(bssid == NULL)
+ memset(header->addr3, 0xFF, ETH_ALEN);
+ else
+ memcpy(header->addr3, bssid, ETH_ALEN);
+
+ /* Fill in the sequence # */
+ /* FIXME: I need to add this to the softmac struct
+ * shouldn't the sequence number be in ieee80211? */
+}
+
+static u16
+ieee80211softmac_capabilities(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net)
+{
+ u16 capability = 0;
+
+ /* ESS and IBSS bits are set according to the current mode */
+ switch (mac->ieee->iw_mode) {
+ case IW_MODE_INFRA:
+ capability = cpu_to_le16(WLAN_CAPABILITY_ESS);
+ break;
+ case IW_MODE_ADHOC:
+ capability = cpu_to_le16(WLAN_CAPABILITY_IBSS);
+ break;
+ case IW_MODE_AUTO:
+ capability = net->capabilities &
+ (WLAN_CAPABILITY_ESS|WLAN_CAPABILITY_IBSS);
+ break;
+ default:
+ /* bleh. we don't ever go to these modes */
+ printk(KERN_ERR PFX "invalid iw_mode!\n");
+ break;
+ }
+
+ /* CF Pollable / CF Poll Request */
+ /* Needs to be implemented, for now, the 0's == not supported */
+
+ /* Privacy Bit */
+ capability |= mac->ieee->sec.level ?
+ cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0;
+
+ /* Short Preamble */
+ /* Always supported: we probably won't ever be powering devices which
+ * dont support this... */
+ capability |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+ /* PBCC */
+ /* Not widely used */
+
+ /* Channel Agility */
+ /* Not widely used */
+
+ /* Short Slot */
+ /* Will be implemented later */
+
+ /* DSSS-OFDM */
+ /* Not widely used */
+
+ return capability;
+}
+
+/*****************************************************************************
+ * Create Management packets
+ *****************************************************************************/
+
+/* Creates an association request packet */
+static u32
+ieee80211softmac_assoc_req(struct ieee80211_assoc_request **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ u8 *data;
+ (*pkt) = (struct ieee80211_assoc_request *)ieee80211softmac_alloc_mgt(
+ 2 + /* Capability Info */
+ 2 + /* Listen Interval */
+ /* SSID IE */
+ 1 + 1 + IW_ESSID_MAX_SIZE +
+ /* Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
+ /* Extended Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN +
+ /* WPA IE if present */
+ mac->wpa.IElen
+ /* Other IE's? Optional?
+ * Yeah, probably need an extra IE parameter -- lots of vendors like to
+ * fill in their own IEs */
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_ASSOC_REQ, net->bssid, net->bssid);
+
+ /* Fill in the capabilities */
+ (*pkt)->capability = ieee80211softmac_capabilities(mac, net);
+
+ /* Fill in Listen Interval (?) */
+ (*pkt)->listen_interval = cpu_to_le16(10);
+
+ data = (u8 *)(*pkt)->info_element;
+ /* Add SSID */
+ data = ieee80211softmac_add_essid(data, &net->essid);
+ /* Add Rates */
+ data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
+ /* Add WPA IE */
+ if (mac->wpa.IElen && mac->wpa.IE) {
+ memcpy(data, mac->wpa.IE, mac->wpa.IElen);
+ data += mac->wpa.IElen;
+ }
+ /* Return the number of used bytes */
+ return (data - (u8*)(*pkt));
+}
+
+/* Create a reassociation request packet */
+static u32
+ieee80211softmac_reassoc_req(struct ieee80211_reassoc_request **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ u8 *data;
+ (*pkt) = (struct ieee80211_reassoc_request *)ieee80211softmac_alloc_mgt(
+ 2 + /* Capability Info */
+ 2 + /* Listen Interval */
+ ETH_ALEN + /* AP MAC */
+ /* SSID IE */
+ 1 + 1 + IW_ESSID_MAX_SIZE +
+ /* Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
+ /* Extended Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN
+ /* Other IE's? */
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_REASSOC_REQ, net->bssid, net->bssid);
+
+ /* Fill in the capabilities */
+ (*pkt)->capability = ieee80211softmac_capabilities(mac, net);
+
+ /* Fill in Listen Interval (?) */
+ (*pkt)->listen_interval = cpu_to_le16(10);
+ /* Fill in the current AP MAC */
+ memcpy((*pkt)->current_ap, mac->ieee->bssid, ETH_ALEN);
+
+ data = (u8 *)(*pkt)->info_element;
+ /* Add SSID */
+ data = ieee80211softmac_add_essid(data, &net->essid);
+ /* Add Rates */
+ data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
+ /* Return packet size */
+ return (data - (u8 *)(*pkt));
+}
+
+/* Create an authentication packet */
+static u32
+ieee80211softmac_auth(struct ieee80211_auth **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net,
+ u16 transaction, u16 status, int *encrypt_mpdu)
+{
+ u8 *data;
+ int auth_mode = mac->ieee->sec.auth_mode;
+ int is_shared_response = (auth_mode == WLAN_AUTH_SHARED_KEY
+ && transaction == IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE);
+
+ /* Allocate Packet */
+ (*pkt) = (struct ieee80211_auth *)ieee80211softmac_alloc_mgt(
+ 2 + /* Auth Algorithm */
+ 2 + /* Auth Transaction Seq */
+ 2 + /* Status Code */
+ /* Challenge Text IE */
+ (is_shared_response ? 1 + 1 + net->challenge_len : 0)
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_AUTH, net->bssid, net->bssid);
+
+ /* Algorithm */
+ (*pkt)->algorithm = cpu_to_le16(auth_mode);
+ /* Transaction */
+ (*pkt)->transaction = cpu_to_le16(transaction);
+ /* Status */
+ (*pkt)->status = cpu_to_le16(status);
+
+ data = (u8 *)(*pkt)->info_element;
+ /* Challenge Text */
+ if (is_shared_response) {
+ *data = MFIE_TYPE_CHALLENGE;
+ data++;
+
+ /* Copy the challenge in */
+ *data = net->challenge_len;
+ data++;
+ memcpy(data, net->challenge, net->challenge_len);
+ data += net->challenge_len;
+
+ /* Make sure this frame gets encrypted with the shared key */
+ *encrypt_mpdu = 1;
+ } else
+ *encrypt_mpdu = 0;
+
+ /* Return the packet size */
+ return (data - (u8 *)(*pkt));
+}
+
+/* Create a disassocation or deauthentication packet */
+static u32
+ieee80211softmac_disassoc_deauth(struct ieee80211_disassoc **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net,
+ u16 type, u16 reason)
+{
+ /* Allocate Packet */
+ (*pkt) = (struct ieee80211_disassoc *)ieee80211softmac_alloc_mgt(2);
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), type, net->bssid, net->bssid);
+ /* Reason */
+ (*pkt)->reason = cpu_to_le16(reason);
+ /* Return the packet size */
+ return (2 + IEEE80211_3ADDR_LEN);
+}
+
+/* Create a probe request packet */
+static u32
+ieee80211softmac_probe_req(struct ieee80211_probe_request **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_essid *essid)
+{
+ u8 *data;
+ /* Allocate Packet */
+ (*pkt) = (struct ieee80211_probe_request *)ieee80211softmac_alloc_mgt(
+ /* SSID of requested network */
+ 1 + 1 + IW_ESSID_MAX_SIZE +
+ /* Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
+ /* Extended Rates IE */
+ 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_PROBE_REQ, NULL, NULL);
+
+ data = (u8 *)(*pkt)->info_element;
+ /* Add ESSID (can be NULL) */
+ data = ieee80211softmac_add_essid(data, essid);
+ /* Add Rates */
+ data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
+ /* Return packet size */
+ return (data - (u8 *)(*pkt));
+}
+
+/* Create a probe response packet */
+/* FIXME: Not complete */
+static u32
+ieee80211softmac_probe_resp(struct ieee80211_probe_response **pkt,
+ struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ u8 *data;
+ /* Allocate Packet */
+ (*pkt) = (struct ieee80211_probe_response *)ieee80211softmac_alloc_mgt(
+ 8 + /* Timestamp */
+ 2 + /* Beacon Interval */
+ 2 + /* Capability Info */
+ /* SSID IE */
+ 1 + 1 + IW_ESSID_MAX_SIZE +
+ 7 + /* FH Parameter Set */
+ 2 + /* DS Parameter Set */
+ 8 + /* CF Parameter Set */
+ 4 /* IBSS Parameter Set */
+ );
+ if (unlikely((*pkt) == NULL))
+ return 0;
+ ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_PROBE_RESP, net->bssid, net->bssid);
+ data = (u8 *)(*pkt)->info_element;
+
+ /* Return the packet size */
+ return (data - (u8 *)(*pkt));
+}
+
+
+/* Sends a manangement packet
+ * FIXME: document the use of the arg parameter
+ * for _AUTH: (transaction #) | (status << 16)
+ */
+int
+ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac,
+ void *ptrarg, u32 type, u32 arg)
+{
+ void *pkt = NULL;
+ u32 pkt_size = 0;
+ int encrypt_mpdu = 0;
+
+ switch(type) {
+ case IEEE80211_STYPE_ASSOC_REQ:
+ pkt_size = ieee80211softmac_assoc_req((struct ieee80211_assoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
+ break;
+ case IEEE80211_STYPE_REASSOC_REQ:
+ pkt_size = ieee80211softmac_reassoc_req((struct ieee80211_reassoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
+ break;
+ case IEEE80211_STYPE_AUTH:
+ pkt_size = ieee80211softmac_auth((struct ieee80211_auth **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, (u16)(arg & 0xFFFF), (u16) (arg >> 16), &encrypt_mpdu);
+ break;
+ case IEEE80211_STYPE_DISASSOC:
+ case IEEE80211_STYPE_DEAUTH:
+ pkt_size = ieee80211softmac_disassoc_deauth((struct ieee80211_disassoc **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, type, (u16)(arg & 0xFFFF));
+ break;
+ case IEEE80211_STYPE_PROBE_REQ:
+ pkt_size = ieee80211softmac_probe_req((struct ieee80211_probe_request **)(&pkt), mac, (struct ieee80211softmac_essid *)ptrarg);
+ break;
+ case IEEE80211_STYPE_PROBE_RESP:
+ pkt_size = ieee80211softmac_probe_resp((struct ieee80211_probe_response **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
+ break;
+ default:
+ printkl(KERN_DEBUG PFX "Unsupported Management Frame type: %i\n", type);
+ return -EINVAL;
+ };
+
+ if(pkt_size == 0 || pkt == NULL) {
+ printkl(KERN_DEBUG PFX "Error, packet is nonexistant or 0 length\n");
+ return -ENOMEM;
+ }
+
+ /* Send the packet to the ieee80211 layer for tx */
+ /* we defined softmac->mgmt_xmit for this. Should we keep it
+ * as it is (that means we'd need to wrap this into a txb),
+ * modify the prototype (so it matches this function),
+ * or get rid of it alltogether?
+ * Does this work for you now?
+ */
+ ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *)pkt,
+ IEEE80211_3ADDR_LEN, pkt_size, encrypt_mpdu);
+
+ kfree(pkt);
+ return 0;
+}
+
+/* Beacon handling */
+int ieee80211softmac_handle_beacon(struct net_device *dev,
+ struct ieee80211_beacon *beacon,
+ struct ieee80211_network *network)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ /* This might race, but we don't really care and it's not worth
+ * adding heavyweight locking in this fastpath.
+ */
+ if (mac->associnfo.associated) {
+ if (memcmp(network->bssid, mac->associnfo.bssid, ETH_ALEN) == 0)
+ ieee80211softmac_process_erp(mac, network->erp_value);
+ }
+
+ return 0;
+}
+
diff --git a/net/ieee80211/softmac/ieee80211softmac_module.c b/net/ieee80211/softmac/ieee80211softmac_module.c
new file mode 100644
index 000000000000..fcf6a28295e8
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_module.c
@@ -0,0 +1,621 @@
+/*
+ * Contains some basic softmac functions along with module registration code etc.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * 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, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR 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
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+#include <linux/sort.h>
+#include <linux/etherdevice.h>
+
+struct net_device *alloc_ieee80211softmac(int sizeof_priv)
+{
+ struct ieee80211softmac_device *softmac;
+ struct net_device *dev;
+
+ dev = alloc_ieee80211(sizeof(struct ieee80211softmac_device) + sizeof_priv);
+ if (!dev)
+ return NULL;
+
+ softmac = ieee80211_priv(dev);
+// softmac->wq = create_freezeable_workqueue("softmac");
+ softmac->wq = create_workqueue("softmac");
+ if (!softmac->wq) {
+ free_ieee80211(dev);
+ return NULL;
+ }
+
+ softmac->dev = dev;
+ softmac->ieee = netdev_priv(dev);
+ spin_lock_init(&softmac->lock);
+
+ softmac->ieee->handle_auth = ieee80211softmac_auth_resp;
+ softmac->ieee->handle_deauth = ieee80211softmac_deauth_resp;
+ softmac->ieee->handle_assoc_response = ieee80211softmac_handle_assoc_response;
+ softmac->ieee->handle_reassoc_request = ieee80211softmac_handle_reassoc_req;
+ softmac->ieee->handle_disassoc = ieee80211softmac_handle_disassoc;
+ softmac->ieee->handle_beacon = ieee80211softmac_handle_beacon;
+ softmac->ieee->handle_probe_response = ieee80211softmac_handle_probe_resp;
+ softmac->scaninfo = NULL;
+
+ softmac->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
+ softmac->associnfo.channel = -1;
+ /* TODO: initialise all the other callbacks in the ieee struct
+ * (once they're written)
+ */
+
+ INIT_LIST_HEAD(&softmac->auth_queue);
+ INIT_LIST_HEAD(&softmac->network_list);
+ INIT_LIST_HEAD(&softmac->events);
+
+ mutex_init(&softmac->associnfo.mutex);
+
+ INIT_DELAYED_WORK(&softmac->associnfo.work, ieee80211softmac_assoc_work);
+ INIT_DELAYED_WORK(&softmac->associnfo.timeout, ieee80211softmac_assoc_timeout);
+
+ softmac->start_scan = ieee80211softmac_start_scan_implementation;
+ softmac->wait_for_scan = ieee80211softmac_wait_for_scan_implementation;
+ softmac->stop_scan = ieee80211softmac_stop_scan_implementation;
+
+ /* to start with, we can't send anything ... */
+ netif_carrier_off(dev);
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_ieee80211softmac);
+
+/* Clears the pending work queue items, stops all scans, etc. */
+void
+ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm)
+{
+ unsigned long flags;
+ struct ieee80211softmac_event *eventptr, *eventtmp;
+ struct ieee80211softmac_auth_queue_item *authptr, *authtmp;
+ struct ieee80211softmac_network *netptr, *nettmp;
+
+ ieee80211softmac_stop_scan(sm);
+ ieee80211softmac_wait_for_scan(sm);
+
+ spin_lock_irqsave(&sm->lock, flags);
+ sm->running = 0;
+
+ /* Free all pending assoc work items */
+ cancel_delayed_work(&sm->associnfo.work);
+
+ /* Free all pending scan work items */
+ if(sm->scaninfo != NULL)
+ cancel_delayed_work(&sm->scaninfo->softmac_scan);
+
+ /* Free all pending auth work items */
+ list_for_each_entry(authptr, &sm->auth_queue, list)
+ cancel_delayed_work(&authptr->work);
+
+ /* delete all pending event calls and work items */
+ list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list)
+ cancel_delayed_work(&eventptr->work);
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+ flush_scheduled_work();
+
+ /* now we should be save and no longer need locking... */
+ spin_lock_irqsave(&sm->lock, flags);
+ /* Free all pending auth work items */
+ list_for_each_entry_safe(authptr, authtmp, &sm->auth_queue, list) {
+ list_del(&authptr->list);
+ kfree(authptr);
+ }
+
+ /* delete all pending event calls and work items */
+ list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list) {
+ list_del(&eventptr->list);
+ kfree(eventptr);
+ }
+
+ /* Free all networks */
+ list_for_each_entry_safe(netptr, nettmp, &sm->network_list, list) {
+ ieee80211softmac_del_network_locked(sm, netptr);
+ if(netptr->challenge != NULL)
+ kfree(netptr->challenge);
+ kfree(netptr);
+ }
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_clear_pending_work);
+
+void free_ieee80211softmac(struct net_device *dev)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(dev);
+ ieee80211softmac_clear_pending_work(sm);
+ kfree(sm->scaninfo);
+ kfree(sm->wpa.IE);
+ free_ieee80211(dev);
+}
+EXPORT_SYMBOL_GPL(free_ieee80211softmac);
+
+static void ieee80211softmac_start_check_rates(struct ieee80211softmac_device *mac)
+{
+ struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
+ /* I took out the sorting check, we're seperating by modulation now. */
+ if (ri->count)
+ return;
+ /* otherwise assume we hav'em all! */
+ if (mac->ieee->modulation & IEEE80211_CCK_MODULATION) {
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK;
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK;
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK;
+ ri->rates[ri->count++] = IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK;
+ }
+ if (mac->ieee->modulation & IEEE80211_OFDM_MODULATION) {
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_6MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_9MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_12MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_18MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_24MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_36MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_48MB;
+ ri->rates[ri->count++] = IEEE80211_OFDM_RATE_54MB;
+ }
+}
+
+int ieee80211softmac_ratesinfo_rate_supported(struct ieee80211softmac_ratesinfo *ri, u8 rate)
+{
+ int search;
+ u8 search_rate;
+
+ for (search = 0; search < ri->count; search++) {
+ search_rate = ri->rates[search];
+ search_rate &= ~IEEE80211_BASIC_RATE_MASK;
+ if (rate == search_rate)
+ return 1;
+ }
+
+ return 0;
+}
+
+u8 ieee80211softmac_highest_supported_rate(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_ratesinfo *ri, int basic_only)
+{
+ u8 user_rate = mac->txrates.user_rate;
+ int i;
+
+ if (ri->count == 0)
+ return IEEE80211_CCK_RATE_1MB;
+
+ for (i = ri->count - 1; i >= 0; i--) {
+ u8 rate = ri->rates[i];
+ if (basic_only && !(rate & IEEE80211_BASIC_RATE_MASK))
+ continue;
+ rate &= ~IEEE80211_BASIC_RATE_MASK;
+ if (rate > user_rate)
+ continue;
+ if (ieee80211softmac_ratesinfo_rate_supported(&mac->ratesinfo, rate))
+ return rate;
+ }
+
+ /* If we haven't found a suitable rate by now, just trust the user */
+ return user_rate;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_highest_supported_rate);
+
+void ieee80211softmac_process_erp(struct ieee80211softmac_device *mac,
+ u8 erp_value)
+{
+ int use_protection;
+ int short_preamble;
+ u32 changes = 0;
+
+ /* Barker preamble mode */
+ short_preamble = ((erp_value & WLAN_ERP_BARKER_PREAMBLE) == 0
+ && mac->associnfo.short_preamble_available) ? 1 : 0;
+
+ /* Protection needed? */
+ use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
+
+ if (mac->bssinfo.short_preamble != short_preamble) {
+ changes |= IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE;
+ mac->bssinfo.short_preamble = short_preamble;
+ }
+
+ if (mac->bssinfo.use_protection != use_protection) {
+ changes |= IEEE80211SOFTMAC_BSSINFOCHG_PROTECTION;
+ mac->bssinfo.use_protection = use_protection;
+ }
+
+ if (mac->bssinfo_change && changes)
+ mac->bssinfo_change(mac->dev, changes);
+}
+
+void ieee80211softmac_recalc_txrates(struct ieee80211softmac_device *mac)
+{
+ struct ieee80211softmac_txrates *txrates = &mac->txrates;
+ u32 change = 0;
+
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
+ txrates->default_rate = ieee80211softmac_highest_supported_rate(mac, &mac->bssinfo.supported_rates, 0);
+
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
+ txrates->default_fallback = lower_rate(mac, txrates->default_rate);
+
+ change |= IEEE80211SOFTMAC_TXRATECHG_MCAST;
+ txrates->mcast_rate = ieee80211softmac_highest_supported_rate(mac, &mac->bssinfo.supported_rates, 1);
+
+ if (mac->txrates_change)
+ mac->txrates_change(mac->dev, change);
+
+}
+
+void ieee80211softmac_init_bss(struct ieee80211softmac_device *mac)
+{
+ struct ieee80211_device *ieee = mac->ieee;
+ u32 change = 0;
+ struct ieee80211softmac_txrates *txrates = &mac->txrates;
+ struct ieee80211softmac_bss_info *bssinfo = &mac->bssinfo;
+
+ /* TODO: We need some kind of state machine to lower the default rates
+ * if we loose too many packets.
+ */
+ /* Change the default txrate to the highest possible value.
+ * The txrate machine will lower it, if it is too high.
+ */
+ if (ieee->modulation & IEEE80211_OFDM_MODULATION)
+ txrates->user_rate = IEEE80211_OFDM_RATE_24MB;
+ else
+ txrates->user_rate = IEEE80211_CCK_RATE_11MB;
+
+ txrates->default_rate = IEEE80211_CCK_RATE_1MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
+
+ txrates->default_fallback = IEEE80211_CCK_RATE_1MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
+
+ txrates->mcast_rate = IEEE80211_CCK_RATE_1MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_MCAST;
+
+ txrates->mgt_mcast_rate = IEEE80211_CCK_RATE_1MB;
+ change |= IEEE80211SOFTMAC_TXRATECHG_MGT_MCAST;
+
+ if (mac->txrates_change)
+ mac->txrates_change(mac->dev, change);
+
+ change = 0;
+
+ bssinfo->supported_rates.count = 0;
+ memset(bssinfo->supported_rates.rates, 0,
+ sizeof(bssinfo->supported_rates.rates));
+ change |= IEEE80211SOFTMAC_BSSINFOCHG_RATES;
+
+ bssinfo->short_preamble = 0;
+ change |= IEEE80211SOFTMAC_BSSINFOCHG_SHORT_PREAMBLE;
+
+ bssinfo->use_protection = 0;
+ change |= IEEE80211SOFTMAC_BSSINFOCHG_PROTECTION;
+
+ if (mac->bssinfo_change)
+ mac->bssinfo_change(mac->dev, change);
+
+ mac->running = 1;
+}
+
+void ieee80211softmac_start(struct net_device *dev)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ ieee80211softmac_start_check_rates(mac);
+ ieee80211softmac_init_bss(mac);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_start);
+
+void ieee80211softmac_stop(struct net_device *dev)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ ieee80211softmac_clear_pending_work(mac);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_stop);
+
+void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ memcpy(mac->ratesinfo.rates, rates, count);
+ mac->ratesinfo.count = count;
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_set_rates);
+
+static u8 raise_rate(struct ieee80211softmac_device *mac, u8 rate)
+{
+ int i;
+ struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
+
+ for (i=0; i<ri->count-1; i++) {
+ if (ri->rates[i] == rate)
+ return ri->rates[i+1];
+ }
+ /* I guess we can't go any higher... */
+ return ri->rates[ri->count];
+}
+
+u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta)
+{
+ int i;
+ struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo;
+
+ for (i=delta; i<ri->count; i++) {
+ if (ri->rates[i] == rate)
+ return ri->rates[i-delta];
+ }
+ /* I guess we can't go any lower... */
+ return ri->rates[0];
+}
+
+static void ieee80211softmac_add_txrates_badness(struct ieee80211softmac_device *mac,
+ int amount)
+{
+ u8 default_rate = mac->txrates.default_rate;
+ u8 default_fallback = mac->txrates.default_fallback;
+ u32 changes = 0;
+
+ //TODO: This is highly experimental code.
+ // Maybe the dynamic rate selection does not work
+ // and it has to be removed again.
+
+printk("badness %d\n", mac->txrate_badness);
+ mac->txrate_badness += amount;
+ if (mac->txrate_badness <= -1000) {
+ /* Very small badness. Try a faster bitrate. */
+ default_rate = raise_rate(mac, default_rate);
+ changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
+ default_fallback = get_fallback_rate(mac, default_rate);
+ changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
+ mac->txrate_badness = 0;
+printk("Bitrate raised to %u\n", default_rate);
+ } else if (mac->txrate_badness >= 10000) {
+ /* Very high badness. Try a slower bitrate. */
+ default_rate = lower_rate(mac, default_rate);
+ changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT;
+ default_fallback = get_fallback_rate(mac, default_rate);
+ changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK;
+ mac->txrate_badness = 0;
+printk("Bitrate lowered to %u\n", default_rate);
+ }
+
+ mac->txrates.default_rate = default_rate;
+ mac->txrates.default_fallback = default_fallback;
+
+ if (changes && mac->txrates_change)
+ mac->txrates_change(mac->dev, changes);
+}
+
+void ieee80211softmac_fragment_lost(struct net_device *dev,
+ u16 wl_seq)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_add_txrates_badness(mac, 1000);
+ //TODO
+
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_fragment_lost);
+
+static int rate_cmp(const void *a_, const void *b_) {
+ u8 *a, *b;
+ a = (u8*)a_;
+ b = (u8*)b_;
+ return ((*a & ~IEEE80211_BASIC_RATE_MASK) - (*b & ~IEEE80211_BASIC_RATE_MASK));
+}
+
+/* Allocate a softmac network struct and fill it from a network */
+struct ieee80211softmac_network *
+ieee80211softmac_create_network(struct ieee80211softmac_device *mac,
+ struct ieee80211_network *net)
+{
+ struct ieee80211softmac_network *softnet;
+ softnet = kzalloc(sizeof(struct ieee80211softmac_network), GFP_ATOMIC);
+ if(softnet == NULL)
+ return NULL;
+ memcpy(softnet->bssid, net->bssid, ETH_ALEN);
+ softnet->mode = (net->capability & WLAN_CAPABILITY_ESS)? IW_MODE_INFRA: IW_MODE_ADHOC;
+ softnet->channel = net->channel;
+ softnet->essid.len = net->ssid_len;
+ memcpy(softnet->essid.data, net->ssid, softnet->essid.len);
+
+ /* copy rates over */
+ softnet->supported_rates.count = net->rates_len;
+ memcpy(&softnet->supported_rates.rates[0], net->rates, net->rates_len);
+ memcpy(&softnet->supported_rates.rates[softnet->supported_rates.count], net->rates_ex, net->rates_ex_len);
+ softnet->supported_rates.count += net->rates_ex_len;
+ sort(softnet->supported_rates.rates, softnet->supported_rates.count, sizeof(softnet->supported_rates.rates[0]), rate_cmp, NULL);
+
+ /* we save the ERP value because it is needed at association time, and
+ * many AP's do not include an ERP IE in the association response. */
+ softnet->erp_value = net->erp_value;
+
+ softnet->capabilities = net->capability;
+ return softnet;
+}
+
+
+/* Add a network to the list, while locked */
+void
+ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *add_net)
+{
+ struct list_head *list_ptr;
+ struct ieee80211softmac_network *softmac_net = NULL;
+
+ list_for_each(list_ptr, &mac->network_list) {
+ softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
+ if(!memcmp(softmac_net->bssid, add_net->bssid, ETH_ALEN))
+ break;
+ else
+ softmac_net = NULL;
+ }
+ if(softmac_net == NULL)
+ list_add(&(add_net->list), &mac->network_list);
+}
+
+/* Add a network to the list, with locking */
+void
+ieee80211softmac_add_network(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *add_net)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_add_network_locked(mac, add_net);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+
+/* Delete a network from the list, while locked*/
+void
+ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *del_net)
+{
+ list_del(&(del_net->list));
+}
+
+/* Delete a network from the list with locking */
+void
+ieee80211softmac_del_network(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *del_net)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_del_network_locked(mac, del_net);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+/* Get a network from the list by MAC while locked */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_bssid_locked(struct ieee80211softmac_device *mac,
+ u8 *bssid)
+{
+ struct list_head *list_ptr;
+ struct ieee80211softmac_network *softmac_net = NULL;
+ list_for_each(list_ptr, &mac->network_list) {
+ softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
+ if(!memcmp(softmac_net->bssid, bssid, ETH_ALEN))
+ break;
+ else
+ softmac_net = NULL;
+ }
+ return softmac_net;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_get_network_by_bssid_locked);
+
+/* Get a network from the list by BSSID with locking */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_bssid(struct ieee80211softmac_device *mac,
+ u8 *bssid)
+{
+ unsigned long flags;
+ struct ieee80211softmac_network *softmac_net;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ softmac_net = ieee80211softmac_get_network_by_bssid_locked(mac, bssid);
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return softmac_net;
+}
+
+/* Get a network from the list by MAC and channel while locked */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_bssid_with_channel_locked(
+ struct ieee80211softmac_device *mac,
+ u8 *bssid,
+ u8 channel )
+{
+ struct list_head *list_ptr;
+ struct ieee80211softmac_network *softmac_net = NULL;
+ list_for_each(list_ptr, &mac->network_list) {
+ softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
+ if(!memcmp(softmac_net->bssid, bssid, ETH_ALEN) &&
+ (channel == softmac_net->channel))
+ break;
+ else
+ softmac_net = NULL;
+ }
+ return softmac_net;
+}
+
+/* Get a network from the list by BSSID and channel with locking */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_bssid_with_channel(
+ struct ieee80211softmac_device *mac,
+ u8 *bssid,
+ u8 channel )
+{
+ unsigned long flags;
+ struct ieee80211softmac_network *softmac_net;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ softmac_net = ieee80211softmac_get_network_by_bssid_with_channel_locked(mac, bssid,channel);
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return softmac_net;
+}
+
+/* Get a network from the list by ESSID while locked */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_essid *essid)
+{
+ struct list_head *list_ptr;
+ struct ieee80211softmac_network *softmac_net = NULL;
+
+ list_for_each(list_ptr, &mac->network_list) {
+ softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list);
+ if (softmac_net->essid.len == essid->len &&
+ !memcmp(softmac_net->essid.data, essid->data, essid->len))
+ return softmac_net;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_get_network_by_essid_locked);
+
+/* Get a network from the list by ESSID with locking */
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_essid *essid)
+{
+ unsigned long flags;
+ struct ieee80211softmac_network *softmac_net = NULL;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ softmac_net = ieee80211softmac_get_network_by_essid_locked(mac, essid);
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return softmac_net;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Johannes Berg");
+MODULE_AUTHOR("Joseph Jezak");
+MODULE_AUTHOR("Larry Finger");
+MODULE_AUTHOR("Danny van Dyk");
+MODULE_AUTHOR("Michael Buesch");
+MODULE_DESCRIPTION("802.11 software MAC");
diff --git a/net/ieee80211/softmac/ieee80211softmac_priv.h b/net/ieee80211/softmac/ieee80211softmac_priv.h
new file mode 100644
index 000000000000..c94d19929a8a
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_priv.h
@@ -0,0 +1,256 @@
+/*
+ * Internal softmac API definitions.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * 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, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR 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
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#ifndef IEEE80211SOFTMAC_PRIV_H_
+#define IEEE80211SOFTMAC_PRIV_H_
+
+#include <net/ieee80211softmac.h>
+#include <net/ieee80211softmac_wx.h>
+#include <linux/kernel.h>
+#include <linux/stringify.h>
+
+
+#define PFX "SoftMAC: "
+//#define CONFIG_IEEE80211_SOFTMAC_DEBUG 1
+
+#ifdef assert
+# undef assert
+#endif
+#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG
+#define assert(expr) \
+ do { \
+ if (unlikely(!(expr))) { \
+ printkl(KERN_ERR PFX "ASSERTION FAILED (%s) at: %s:%d:%s()\n", #expr, \
+ __FILE__, __LINE__, __FUNCTION__); \
+ } \
+ } while (0)
+#else
+#define assert(expr) do {} while (0)
+#endif
+
+/* rate limited printk(). */
+#ifdef printkl
+# undef printkl
+#endif
+#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0)
+/* rate limited printk() for debugging */
+#ifdef dprintkl
+# undef dprintkl
+#endif
+#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG
+# define dprintkl printkl
+#else
+# define dprintkl(f, x...) do { /* nothing */ } while (0)
+#endif
+
+/* debugging printk() */
+#ifdef dprintk
+# undef dprintk
+#endif
+#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG
+# define dprintk(f, x...) do { printk(f ,##x); } while (0)
+#else
+# define dprintk(f, x...) do { /* nothing */ } while (0)
+#endif
+
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5]
+
+
+/* private definitions and prototypes */
+
+/*** prototypes from _scan.c */
+void ieee80211softmac_scan(struct work_struct *work);
+/* for internal use if scanning is needed */
+int ieee80211softmac_start_scan(struct ieee80211softmac_device *mac);
+void ieee80211softmac_stop_scan(struct ieee80211softmac_device *mac);
+void ieee80211softmac_wait_for_scan(struct ieee80211softmac_device *mac);
+
+/* for use by _module.c to assign to the callbacks */
+int ieee80211softmac_start_scan_implementation(struct net_device *dev);
+void ieee80211softmac_stop_scan_implementation(struct net_device *dev);
+void ieee80211softmac_wait_for_scan_implementation(struct net_device *dev);
+int ieee80211softmac_handle_probe_resp(struct net_device *dev,
+ struct ieee80211_beacon *beacon,
+ struct ieee80211_network *network);
+
+/*** Network prototypes from _module.c */
+struct ieee80211softmac_network * ieee80211softmac_create_network(
+ struct ieee80211softmac_device *mac, struct ieee80211_network *net);
+void ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net);
+void ieee80211softmac_add_network(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net);
+void ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net);
+void ieee80211softmac_del_network(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net);
+struct ieee80211softmac_network * ieee80211softmac_get_network_by_bssid_locked(
+ struct ieee80211softmac_device *mac, u8 *ea);
+struct ieee80211softmac_network * ieee80211softmac_get_network_by_bssid(
+ struct ieee80211softmac_device *mac, u8 *ea);
+struct ieee80211softmac_network * ieee80211softmac_get_network_by_bssid_with_channel(
+ struct ieee80211softmac_device *mac, u8 *ea, u8 channel );
+struct ieee80211softmac_network * ieee80211softmac_get_network_by_ssid_locked(
+ struct ieee80211softmac_device *mac, u8 *ssid, u8 ssid_len);
+struct ieee80211softmac_network * ieee80211softmac_get_network_by_ssid(
+ struct ieee80211softmac_device *mac, u8 *ssid, u8 ssid_len);
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_essid *essid);
+struct ieee80211softmac_network *
+ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_essid *essid);
+
+/* Rates related */
+void ieee80211softmac_process_erp(struct ieee80211softmac_device *mac,
+ u8 erp_value);
+int ieee80211softmac_ratesinfo_rate_supported(struct ieee80211softmac_ratesinfo *ri, u8 rate);
+u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta);
+void ieee80211softmac_init_bss(struct ieee80211softmac_device *mac);
+void ieee80211softmac_recalc_txrates(struct ieee80211softmac_device *mac);
+static inline u8 lower_rate(struct ieee80211softmac_device *mac, u8 rate) {
+ return ieee80211softmac_lower_rate_delta(mac, rate, 1);
+}
+
+static inline u8 get_fallback_rate(struct ieee80211softmac_device *mac, u8 rate)
+{
+ return ieee80211softmac_lower_rate_delta(mac, rate, 2);
+}
+
+
+/*** prototypes from _io.c */
+int ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac,
+ void* ptrarg, u32 type, u32 arg);
+int ieee80211softmac_handle_beacon(struct net_device *dev,
+ struct ieee80211_beacon *beacon,
+ struct ieee80211_network *network);
+
+/*** prototypes from _auth.c */
+/* do these have to go into the public header? */
+int ieee80211softmac_auth_req(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net);
+int ieee80211softmac_deauth_req(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, int reason);
+
+/* for use by _module.c to assign to the callbacks */
+int ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth);
+int ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_deauth *deauth);
+
+/*** prototypes from _assoc.c */
+void ieee80211softmac_assoc_work(struct work_struct *work);
+int ieee80211softmac_handle_assoc_response(struct net_device * dev,
+ struct ieee80211_assoc_response * resp,
+ struct ieee80211_network * network);
+int ieee80211softmac_handle_disassoc(struct net_device * dev,
+ struct ieee80211_disassoc * disassoc);
+int ieee80211softmac_handle_reassoc_req(struct net_device * dev,
+ struct ieee80211_reassoc_request * reassoc);
+void ieee80211softmac_assoc_timeout(struct work_struct *work);
+void ieee80211softmac_send_disassoc_req(struct ieee80211softmac_device *mac, u16 reason);
+void ieee80211softmac_disassoc(struct ieee80211softmac_device *mac);
+
+/* some helper functions */
+static inline int ieee80211softmac_scan_handlers_check_self(struct ieee80211softmac_device *sm)
+{
+ return (sm->start_scan == ieee80211softmac_start_scan_implementation) &&
+ (sm->stop_scan == ieee80211softmac_stop_scan_implementation) &&
+ (sm->wait_for_scan == ieee80211softmac_wait_for_scan_implementation);
+}
+
+static inline int ieee80211softmac_scan_sanity_check(struct ieee80211softmac_device *sm)
+{
+ return ((sm->start_scan != ieee80211softmac_start_scan_implementation) &&
+ (sm->stop_scan != ieee80211softmac_stop_scan_implementation) &&
+ (sm->wait_for_scan != ieee80211softmac_wait_for_scan_implementation)
+ ) || ieee80211softmac_scan_handlers_check_self(sm);
+}
+
+#define IEEE80211SOFTMAC_PROBE_DELAY HZ/50
+#define IEEE80211SOFTMAC_WORKQUEUE_NAME_LEN (17 + IFNAMSIZ)
+
+struct ieee80211softmac_network {
+ struct list_head list; /* List */
+ /* Network information copied from ieee80211_network */
+ u8 bssid[ETH_ALEN];
+ u8 mode;
+ u8 channel;
+ struct ieee80211softmac_essid essid;
+
+ struct ieee80211softmac_ratesinfo supported_rates;
+
+ /* SoftMAC specific */
+ u16 authenticating:1, /* Status Flags */
+ authenticated:1,
+ auth_desynced_once:1;
+
+ u8 erp_value; /* Saved ERP value */
+ u16 capabilities; /* Capabilities bitfield */
+ u8 challenge_len; /* Auth Challenge length */
+ char *challenge; /* Challenge Text */
+};
+
+/* structure used to keep track of networks we're auth'ing to */
+struct ieee80211softmac_auth_queue_item {
+ struct list_head list; /* List head */
+ struct ieee80211softmac_network *net; /* Network to auth */
+ struct ieee80211softmac_device *mac; /* SoftMAC device */
+ u8 retry; /* Retry limit */
+ u8 state; /* Auth State */
+ struct delayed_work work; /* Work queue */
+
+};
+
+/* scanning information */
+struct ieee80211softmac_scaninfo {
+ u8 current_channel_idx,
+ number_channels;
+ struct ieee80211_channel *channels;
+ u8 started:1,
+ stop:1;
+ u8 skip_flags;
+ struct completion finished;
+ struct delayed_work softmac_scan;
+ struct ieee80211softmac_device *mac;
+};
+
+/* private event struct */
+struct ieee80211softmac_event {
+ struct list_head list;
+ int event_type;
+ void *event_context;
+ struct delayed_work work;
+ notify_function_ptr fun;
+ void *context;
+ struct ieee80211softmac_device *mac;
+};
+
+void ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_context);
+void ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_context);
+int ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac,
+ int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask);
+
+void ieee80211softmac_try_reassoc(struct ieee80211softmac_device *mac);
+
+#endif /* IEEE80211SOFTMAC_PRIV_H_ */
diff --git a/net/ieee80211/softmac/ieee80211softmac_scan.c b/net/ieee80211/softmac/ieee80211softmac_scan.c
new file mode 100644
index 000000000000..dd43abee552d
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_scan.c
@@ -0,0 +1,331 @@
+/*
+ * Scanning routines.
+ *
+ * These are not exported because they're assigned to the function pointers.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * 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, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR 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
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include <linux/completion.h>
+#include "ieee80211softmac_priv.h"
+
+/* internal, use to trigger scanning if needed.
+ * Returns -EBUSY if already scanning,
+ * result of start_scan otherwise */
+int
+ieee80211softmac_start_scan(struct ieee80211softmac_device *sm)
+{
+ struct ieee80211_network *net = NULL;
+ struct ieee80211_network *nettmp = NULL;
+ struct ieee80211softmac_network *netptr, *nettmp_sm;
+ unsigned long flags;
+ unsigned long flags2;
+ int ret;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ if (sm->scanning)
+ {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return -EINPROGRESS;
+ }
+ sm->scanning = 1;
+ sm->scan.had_probe_resp = 0;
+ sm->scan.wait_cycles_left = 0;
+
+ if( !sm->associnfo.associated ) {
+ /* Delete all existing networks. This is necessary to overcome
+ * stale entries that are not active any longer but are still in
+ * list with good values. The association logic based on RSSI
+ * might decide to switch to that AP when a new but worse
+ * active entry is present. all ieee802.11 networks */
+ spin_lock_irqsave(&sm->ieee->lock, flags2);
+ list_for_each_entry_safe(net, nettmp, &sm->ieee->network_list, list) {
+ list_move(&net->list, &sm->ieee->network_free_list);
+ }
+ spin_unlock_irqrestore(&sm->ieee->lock, flags2);
+
+ /* all softmac networks */
+ list_for_each_entry_safe(netptr, nettmp_sm, &sm->network_list, list) {
+ ieee80211softmac_del_network_locked(sm, netptr);
+ if(netptr->challenge != NULL)
+ kfree(netptr->challenge);
+ kfree(netptr);
+ }
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ ret = sm->start_scan(sm->dev);
+ if (ret) {
+ spin_lock_irqsave(&sm->lock, flags);
+ sm->scanning = 0;
+ spin_unlock_irqrestore(&sm->lock, flags);
+ }
+ return ret;
+}
+
+void
+ieee80211softmac_stop_scan(struct ieee80211softmac_device *sm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sm->lock, flags);
+
+ if (!sm->scanning) {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+ sm->stop_scan(sm->dev);
+}
+
+void
+ieee80211softmac_wait_for_scan(struct ieee80211softmac_device *sm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sm->lock, flags);
+
+ if (!sm->scanning) {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&sm->lock, flags);
+ sm->wait_for_scan(sm->dev);
+}
+
+
+/* internal scanning implementation follows */
+void ieee80211softmac_scan(struct work_struct *work)
+{
+ int invalid_channel;
+ u8 current_channel_idx;
+ struct ieee80211softmac_scaninfo *si =
+ container_of(work, struct ieee80211softmac_scaninfo,
+ softmac_scan.work);
+
+ struct ieee80211softmac_device *sm = si->mac;
+ unsigned long flags;
+
+ /* waiting a fixed time for a probe response is maybe not the best
+ * solution. We probably won't catch all if there are many. Therefore,
+ * if we had a probe response, we wait a tick longer. Maybe another AP
+ * responds as well, but was too slow for that time slice. */
+ spin_lock_irqsave(&sm->lock, flags);
+ if( sm->scan.had_probe_resp && sm->scan.wait_cycles_left ) {
+ sm->scan.had_probe_resp = 0;
+ sm->scan.wait_cycles_left--;
+ queue_delayed_work(si->mac->wq, &si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY);
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ while (!(si->stop) && (si->current_channel_idx < si->number_channels)) {
+ current_channel_idx = si->current_channel_idx;
+ si->current_channel_idx++; /* go to the next channel */
+
+ invalid_channel = (si->skip_flags & si->channels[current_channel_idx].flags);
+
+ if (!invalid_channel) {
+ spin_lock_irqsave(&sm->lock, flags);
+ /* limit cycles we wait for probe responses so that
+ we never hang. */
+ sm->scan.wait_cycles_left = 3;
+ sm->scan.had_probe_resp = 0;
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ sm->set_channel(sm->dev, si->channels[current_channel_idx].channel);
+ // FIXME make this user configurable (active/passive)
+ if(ieee80211softmac_send_mgt_frame(sm, NULL, IEEE80211_STYPE_PROBE_REQ, 0))
+ printkl(KERN_DEBUG PFX "Sending Probe Request Failed\n");
+
+ /* also send directed management frame for the network we're looking for */
+ // TODO: is this if correct, or should we do this only if scanning from assoc request?
+ if (sm->associnfo.req_essid.len)
+ ieee80211softmac_send_mgt_frame(sm, &sm->associnfo.req_essid, IEEE80211_STYPE_PROBE_REQ, 0);
+
+ spin_lock_irqsave(&sm->lock, flags);
+ if (unlikely(!sm->running)) {
+ /* Prevent reschedule on workqueue flush */
+ spin_unlock_irqrestore(&sm->lock, flags);
+ break;
+ }
+ queue_delayed_work(si->mac->wq, &si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY);
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return;
+ } else {
+ dprintk(PFX "Not probing Channel %d (not allowed here)\n", si->channels[current_channel_idx].channel);
+ }
+ }
+
+ spin_lock_irqsave(&sm->lock, flags);
+ cancel_delayed_work(&si->softmac_scan);
+ si->started = 0;
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ dprintk(PFX "Scanning finished: scanned %d channels starting with channel %d\n",
+ sm->scaninfo->number_channels, sm->scaninfo->channels[0].channel);
+ ieee80211softmac_scan_finished(sm);
+ complete_all(&sm->scaninfo->finished);
+}
+
+static inline struct ieee80211softmac_scaninfo *allocate_scaninfo(struct ieee80211softmac_device *mac)
+{
+ /* ugh. can we call this without having the spinlock held? */
+ struct ieee80211softmac_scaninfo *info = kmalloc(sizeof(struct ieee80211softmac_scaninfo), GFP_ATOMIC);
+ if (unlikely(!info))
+ return NULL;
+
+ INIT_DELAYED_WORK(&info->softmac_scan, ieee80211softmac_scan);
+
+ info->mac = mac;
+ init_completion(&info->finished);
+ return info;
+}
+
+int ieee80211softmac_start_scan_implementation(struct net_device *dev)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(dev);
+ unsigned long flags;
+
+ if (!(dev->flags & IFF_UP))
+ return -ENODEV;
+
+ assert(ieee80211softmac_scan_handlers_check_self(sm));
+ if (!ieee80211softmac_scan_handlers_check_self(sm))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ /* it looks like we need to hold the lock here
+ * to make sure we don't allocate two of these... */
+ if (unlikely(!sm->scaninfo))
+ sm->scaninfo = allocate_scaninfo(sm);
+ if (unlikely(!sm->scaninfo)) {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return -ENOMEM;
+ }
+
+ sm->scaninfo->skip_flags = IEEE80211_CH_INVALID;
+ if (0 /* not scanning in IEEE802.11b */)//TODO
+ sm->scaninfo->skip_flags |= IEEE80211_CH_B_ONLY;
+ if (0 /* IEEE802.11a */) {//TODO
+ sm->scaninfo->channels = sm->ieee->geo.a;
+ sm->scaninfo->number_channels = sm->ieee->geo.a_channels;
+ } else {
+ sm->scaninfo->channels = sm->ieee->geo.bg;
+ sm->scaninfo->number_channels = sm->ieee->geo.bg_channels;
+ }
+ sm->scaninfo->current_channel_idx = 0;
+ sm->scaninfo->started = 1;
+ sm->scaninfo->stop = 0;
+ INIT_COMPLETION(sm->scaninfo->finished);
+ if (sm->set_bssid_filter) {
+ u8 bc_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ sm->set_bssid_filter(sm->dev, bc_addr);
+ }
+ queue_delayed_work(sm->wq, &sm->scaninfo->softmac_scan, 0);
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return 0;
+}
+
+void ieee80211softmac_stop_scan_implementation(struct net_device *dev)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(dev);
+ unsigned long flags;
+
+ assert(ieee80211softmac_scan_handlers_check_self(sm));
+ if (!ieee80211softmac_scan_handlers_check_self(sm))
+ return;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ assert(sm->scaninfo != NULL);
+ if (sm->scaninfo) {
+ if (sm->scaninfo->started)
+ sm->scaninfo->stop = 1;
+ else
+ complete_all(&sm->scaninfo->finished);
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+}
+
+void ieee80211softmac_wait_for_scan_implementation(struct net_device *dev)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(dev);
+ unsigned long flags;
+
+ assert(ieee80211softmac_scan_handlers_check_self(sm));
+ if (!ieee80211softmac_scan_handlers_check_self(sm))
+ return;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ if (!sm->scaninfo->started) {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+ wait_for_completion(&sm->scaninfo->finished);
+}
+
+/* this is what drivers (that do scanning) call when they're done */
+void ieee80211softmac_scan_finished(struct ieee80211softmac_device *sm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ sm->scanning = 0;
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ if (sm->associnfo.bssvalid) {
+ struct ieee80211softmac_network *net;
+
+ net = ieee80211softmac_get_network_by_bssid_with_channel(sm, sm->associnfo.bssid, sm->associnfo.channel);
+ if (net) {
+ sm->set_channel(sm->dev, net->channel);
+ if (sm->set_bssid_filter)
+ sm->set_bssid_filter(sm->dev, net->bssid);
+ }
+ }
+ ieee80211softmac_call_events(sm, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, NULL);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_scan_finished);
+
+/* Probe Response handling */
+int ieee80211softmac_handle_probe_resp(
+ struct net_device *dev,
+ struct ieee80211_beacon *beacon,
+ struct ieee80211_network *network )
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(dev);
+
+ unsigned long flags;
+
+ spin_lock_irqsave(&sm->lock, flags);
+ /* it's unlikely that we overrun it while scanning */
+ sm->scan.had_probe_resp++;
+ spin_unlock_irqrestore(&sm->lock, flags);
+
+ return 0;
+}
+
diff --git a/net/ieee80211/softmac/ieee80211softmac_wx.c b/net/ieee80211/softmac/ieee80211softmac_wx.c
new file mode 100644
index 000000000000..b8e0f60541db
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_wx.c
@@ -0,0 +1,514 @@
+/*
+ * This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * 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, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR 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
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+#include <net/iw_handler.h>
+/* for is_broadcast_ether_addr and is_zero_ether_addr */
+#include <linux/etherdevice.h>
+
+int
+ieee80211softmac_wx_trigger_scan(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+ return ieee80211softmac_start_scan(sm);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_trigger_scan);
+
+
+/* if we're still scanning, return -EAGAIN so that userspace tools
+ * can get the complete scan results, otherwise return 0. */
+int
+ieee80211softmac_wx_get_scan_results(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ unsigned long flags;
+ struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+
+ spin_lock_irqsave(&sm->lock, flags);
+ if (sm->scanning) {
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return -EAGAIN;
+ }
+ spin_unlock_irqrestore(&sm->lock, flags);
+ return ieee80211_wx_get_scan(sm->ieee, info, data, extra);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_scan_results);
+
+int
+ieee80211softmac_wx_set_essid(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+ struct ieee80211softmac_auth_queue_item *authptr;
+ int length = 0;
+
+ mutex_lock(&sm->associnfo.mutex);
+
+ /* Doe not cancel resetting of essid if new essid is equal to the old
+ essid.
+ Because if using wpa_supplicant and setting the same network with
+ "select x" or reassociating with "reassociate", wpa_supplicant removes
+ key information and tries to reassociate. But we did not do a rescan and
+ assoc_work. Now wireless system is stalled until the AP is sending a
+ disconnect_event. */
+ if((sm->associnfo.associating || sm->associnfo.associated) &&
+ (data->essid.flags && data->essid.length)) {
+ /* Get the associating network */
+ dprintk(KERN_INFO PFX "Canceling existing associate request!\n");
+ /* Cancel assoc work */
+ cancel_delayed_work(&sm->associnfo.work);
+ /* We don't have to do this, but it's a little cleaner */
+ list_for_each_entry(authptr, &sm->auth_queue, list)
+ cancel_delayed_work(&authptr->work);
+ sm->associnfo.bssvalid = 0;
+ sm->associnfo.bssfixed = 0;
+ flush_scheduled_work();
+ sm->associnfo.associating = 0;
+ sm->associnfo.associated = 0;
+ }
+
+
+ sm->associnfo.static_essid = 0;
+ sm->associnfo.assoc_wait = 0;
+
+ if (data->essid.flags && data->essid.length) {
+ length = min((int)data->essid.length, IW_ESSID_MAX_SIZE);
+ if (length) {
+ memcpy(sm->associnfo.req_essid.data, extra, length);
+ sm->associnfo.static_essid = 1;
+ }
+ }
+
+ /* set our requested ESSID length.
+ * If applicable, we have already copied the data in */
+ sm->associnfo.req_essid.len = length;
+
+ sm->associnfo.associating = 1;
+ /* queue lower level code to do work (if necessary) */
+ queue_delayed_work(sm->wq, &sm->associnfo.work, 0);
+ mutex_unlock(&sm->associnfo.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid);
+
+int
+ieee80211softmac_wx_get_essid(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
+
+ mutex_lock(&sm->associnfo.mutex);
+ /* If all fails, return ANY (empty) */
+ data->essid.length = 0;
+ data->essid.flags = 0; /* active */
+
+ /* If we have a statically configured ESSID then return it */
+ if (sm->associnfo.static_essid) {
+ data->essid.length = sm->associnfo.req_essid.len;
+ data->essid.flags = 1; /* active */
+ memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len);
+ }
+
+ /* If we're associating/associated, return that */
+ if (sm->associnfo.associated || sm->associnfo.associating) {
+ data->essid.length = sm->associnfo.associate_essid.len;
+ data->essid.flags = 1; /* active */
+ memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len);
+ }
+ mutex_unlock(&sm->associnfo.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid);
+
+int
+ieee80211softmac_wx_set_rate(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
+ struct ieee80211_device *ieee = mac->ieee;
+ unsigned long flags;
+ s32 in_rate = data->bitrate.value;
+ u8 rate;
+ int is_ofdm = 0;
+ int err = -EINVAL;
+
+ if (in_rate == -1) {
+ /* FIXME: We don't correctly handle backing down to lower
+ rates, so 801.11g devices start off at 11M for now. People
+ can manually change it if they really need to, but 11M is
+ more reliable. Note similar logic in
+ ieee80211softmac_wx_set_rate() */
+ if (ieee->modulation & IEEE80211_CCK_MODULATION)
+ in_rate = 11000000;
+ else
+ in_rate = 54000000;
+ }
+
+ switch (in_rate) {
+ case 1000000:
+ rate = IEEE80211_CCK_RATE_1MB;
+ break;
+ case 2000000:
+ rate = IEEE80211_CCK_RATE_2MB;
+ break;
+ case 5500000:
+ rate = IEEE80211_CCK_RATE_5MB;
+ break;
+ case 11000000:
+ rate = IEEE80211_CCK_RATE_11MB;
+ break;
+ case 6000000:
+ rate = IEEE80211_OFDM_RATE_6MB;
+ is_ofdm = 1;
+ break;
+ case 9000000:
+ rate = IEEE80211_OFDM_RATE_9MB;
+ is_ofdm = 1;
+ break;
+ case 12000000:
+ rate = IEEE80211_OFDM_RATE_12MB;
+ is_ofdm = 1;
+ break;
+ case 18000000:
+ rate = IEEE80211_OFDM_RATE_18MB;
+ is_ofdm = 1;
+ break;
+ case 24000000:
+ rate = IEEE80211_OFDM_RATE_24MB;
+ is_ofdm = 1;
+ break;
+ case 36000000:
+ rate = IEEE80211_OFDM_RATE_36MB;
+ is_ofdm = 1;
+ break;
+ case 48000000:
+ rate = IEEE80211_OFDM_RATE_48MB;
+ is_ofdm = 1;
+ break;
+ case 54000000:
+ rate = IEEE80211_OFDM_RATE_54MB;
+ is_ofdm = 1;
+ break;
+ default:
+ goto out;
+ }
+
+ spin_lock_irqsave(&mac->lock, flags);
+
+ /* Check if correct modulation for this PHY. */
+ if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION))
+ goto out_unlock;
+
+ mac->txrates.user_rate = rate;
+ ieee80211softmac_recalc_txrates(mac);
+ err = 0;
+
+out_unlock:
+ spin_unlock_irqrestore(&mac->lock, flags);
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate);
+
+int
+ieee80211softmac_wx_get_rate(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
+ unsigned long flags;
+ int err = -EINVAL;
+
+ spin_lock_irqsave(&mac->lock, flags);
+
+ if (unlikely(!mac->running)) {
+ err = -ENODEV;
+ goto out_unlock;
+ }
+
+ switch (mac->txrates.default_rate) {
+ case IEEE80211_CCK_RATE_1MB:
+ data->bitrate.value = 1000000;
+ break;
+ case IEEE80211_CCK_RATE_2MB:
+ data->bitrate.value = 2000000;
+ break;
+ case IEEE80211_CCK_RATE_5MB:
+ data->bitrate.value = 5500000;
+ break;
+ case IEEE80211_CCK_RATE_11MB:
+ data->bitrate.value = 11000000;
+ break;
+ case IEEE80211_OFDM_RATE_6MB:
+ data->bitrate.value = 6000000;
+ break;
+ case IEEE80211_OFDM_RATE_9MB:
+ data->bitrate.value = 9000000;
+ break;
+ case IEEE80211_OFDM_RATE_12MB:
+ data->bitrate.value = 12000000;
+ break;
+ case IEEE80211_OFDM_RATE_18MB:
+ data->bitrate.value = 18000000;
+ break;
+ case IEEE80211_OFDM_RATE_24MB:
+ data->bitrate.value = 24000000;
+ break;
+ case IEEE80211_OFDM_RATE_36MB:
+ data->bitrate.value = 36000000;
+ break;
+ case IEEE80211_OFDM_RATE_48MB:
+ data->bitrate.value = 48000000;
+ break;
+ case IEEE80211_OFDM_RATE_54MB:
+ data->bitrate.value = 54000000;
+ break;
+ default:
+ assert(0);
+ goto out_unlock;
+ }
+ err = 0;
+out_unlock:
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate);
+
+int
+ieee80211softmac_wx_get_wap(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
+ int err = 0;
+
+ mutex_lock(&mac->associnfo.mutex);
+ if (mac->associnfo.bssvalid)
+ memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
+ else
+ memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
+ data->ap_addr.sa_family = ARPHRD_ETHER;
+ mutex_unlock(&mac->associnfo.mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
+
+int
+ieee80211softmac_wx_set_wap(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
+
+ /* sanity check */
+ if (data->ap_addr.sa_family != ARPHRD_ETHER) {
+ return -EINVAL;
+ }
+
+ mutex_lock(&mac->associnfo.mutex);
+ if (is_broadcast_ether_addr(data->ap_addr.sa_data)) {
+ /* the bssid we have is not to be fixed any longer,
+ * and we should reassociate to the best AP. */
+ mac->associnfo.bssfixed = 0;
+ /* force reassociation */
+ mac->associnfo.bssvalid = 0;
+ if (mac->associnfo.associated)
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+ } else if (is_zero_ether_addr(data->ap_addr.sa_data)) {
+ /* the bssid we have is no longer fixed */
+ mac->associnfo.bssfixed = 0;
+ } else {
+ if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
+ if (mac->associnfo.associating || mac->associnfo.associated) {
+ /* bssid unchanged and associated or associating - just return */
+ goto out;
+ }
+ } else {
+ /* copy new value in data->ap_addr.sa_data to bssid */
+ memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
+ }
+ /* tell the other code that this bssid should be used no matter what */
+ mac->associnfo.bssfixed = 1;
+ /* queue associate if new bssid or (old one again and not associated) */
+ queue_delayed_work(mac->wq, &mac->associnfo.work, 0);
+ }
+
+ out:
+ mutex_unlock(&mac->associnfo.mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
+
+int
+ieee80211softmac_wx_set_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+ int err = 0;
+ char *buf;
+ int i;
+
+ mutex_lock(&mac->associnfo.mutex);
+ spin_lock_irqsave(&mac->lock, flags);
+ /* bleh. shouldn't be locked for that kmalloc... */
+
+ if (wrqu->data.length) {
+ if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) {
+ /* this is an IE, so the length must be
+ * correct. Is it possible though that
+ * more than one IE is passed in?
+ */
+ err = -EINVAL;
+ goto out;
+ }
+ if (mac->wpa.IEbuflen <= wrqu->data.length) {
+ buf = kmalloc(wrqu->data.length, GFP_ATOMIC);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+ kfree(mac->wpa.IE);
+ mac->wpa.IE = buf;
+ mac->wpa.IEbuflen = wrqu->data.length;
+ }
+ memcpy(mac->wpa.IE, extra, wrqu->data.length);
+ dprintk(KERN_INFO PFX "generic IE set to ");
+ for (i=0;i<wrqu->data.length;i++)
+ dprintk("%.2x", (u8)mac->wpa.IE[i]);
+ dprintk("\n");
+ mac->wpa.IElen = wrqu->data.length;
+ } else {
+ kfree(mac->wpa.IE);
+ mac->wpa.IE = NULL;
+ mac->wpa.IElen = 0;
+ mac->wpa.IEbuflen = 0;
+ }
+
+ out:
+ spin_unlock_irqrestore(&mac->lock, flags);
+ mutex_unlock(&mac->associnfo.mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
+
+int
+ieee80211softmac_wx_get_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+ int err = 0;
+ int space = wrqu->data.length;
+
+ mutex_lock(&mac->associnfo.mutex);
+ spin_lock_irqsave(&mac->lock, flags);
+
+ wrqu->data.length = 0;
+
+ if (mac->wpa.IE && mac->wpa.IElen) {
+ wrqu->data.length = mac->wpa.IElen;
+ if (mac->wpa.IElen <= space)
+ memcpy(extra, mac->wpa.IE, mac->wpa.IElen);
+ else
+ err = -E2BIG;
+ }
+ spin_unlock_irqrestore(&mac->lock, flags);
+ mutex_unlock(&mac->associnfo.mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
+
+int
+ieee80211softmac_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ u16 reason = cpu_to_le16(mlme->reason_code);
+ struct ieee80211softmac_network *net;
+ int err = -EINVAL;
+
+ mutex_lock(&mac->associnfo.mutex);
+
+ if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) {
+ printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n");
+ goto out;
+ }
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data);
+ if (!net) {
+ printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n");
+ goto out;
+ }
+ err = ieee80211softmac_deauth_req(mac, net, reason);
+ goto out;
+ case IW_MLME_DISASSOC:
+ ieee80211softmac_send_disassoc_req(mac, reason);
+ mac->associnfo.associated = 0;
+ mac->associnfo.associating = 0;
+ err = 0;
+ goto out;
+ default:
+ err = -EOPNOTSUPP;
+ }
+
+out:
+ mutex_unlock(&mac->associnfo.mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 691268f3a359..dbdf6d198524 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -145,6 +145,14 @@ config IP_PNP
on NFS" as well), because all other machines configure the network
in their startup scripts.
+config IP_PNP_DELAY
+ bool "Insert delay for initialization of network"
+ depends on IP_PNP
+ default y
+ help
+ Some network devices need a delay before setting the IP address for
+ them. On embedded systems that might not be necessary.
+
config IP_PNP_DHCP
bool "IP: DHCP support"
depends on IP_PNP
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index 42065fff46c4..695be5573424 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -86,8 +86,13 @@
#endif
/* Define the friendly delay before and after opening net devices */
-#define CONF_PRE_OPEN 500 /* Before opening: 1/2 second */
-#define CONF_POST_OPEN 1 /* After opening: 1 second */
+#ifdef CONFIG_IP_PNP_DELAY
+# define CONF_PRE_OPEN 500 /* Before opening: 1/2 second */
+# define CONF_POST_OPEN 1 /* After opening: 1 second */
+#else
+# define CONF_PRE_OPEN 0
+# define CONF_POST_OPEN 0
+#endif /* CONFIG_IP_PNP_DELAY */
/* Define the timeout for waiting for a DHCP/BOOTP/RARP reply */
#define CONF_OPEN_RETRIES 2 /* (Re)open devices twice */
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 7f710a27e91c..78d59a2e94e8 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -16,20 +16,30 @@ menu "Rate control algorithm selection"
config MAC80211_RC_PID
bool "PID controller based rate control algorithm" if EMBEDDED
- default y
---help---
This option enables a TX rate control algorithm for
mac80211 that uses a PID controller to select the TX
rate.
+config MAC80211_RC_MULTIRATE_PID
+ bool "Multirate PID controller based rate control algorithm" if EMBEDDED
+ ---help---
+ This option enables a TX rate control algorithm for
+ mac80211 that uses a PID controller to select the TX
+ rate. This version of the PID algorithm has been enhanced
+ to perform retries of packets at different rates.
+ This algorithm is recommended for the
+ ConnectCore Wi-9P 9215.
+
config MAC80211_RC_MINSTREL
- bool "Minstrel"
+ bool "Minstrel" if EMBEDDED
+ default y
---help---
This option enables the 'minstrel' TX rate control algorithm
choice
prompt "Default rate control algorithm"
- default MAC80211_RC_DEFAULT_PID
+ default MAC80211_RC_MULTIRATE_DEFAULT_PID
---help---
This option selects the default rate control algorithm
mac80211 will use. Note that this default can still be
@@ -41,8 +51,15 @@ config MAC80211_RC_DEFAULT_PID
depends on MAC80211_RC_PID
---help---
Select the PID controller based rate control as the
- default rate control algorithm. You should choose
- this unless you know what you are doing.
+ default rate control algorithm.
+
+config MAC80211_RC_MULTIRATE_DEFAULT_PID
+ bool "Multirate PID controller based rate control algorithm"
+ depends on MAC80211_RC_MULTIRATE_PID
+ ---help---
+ Select the multirate PID controller based rate control as the
+ default rate control algorithm. You should choose this
+ unless you know what you are doing.
config MAC80211_RC_DEFAULT_MINSTREL
bool "Minstrel"
@@ -55,8 +72,9 @@ endchoice
config MAC80211_RC_DEFAULT
string
- default "pid" if MAC80211_RC_DEFAULT_PID
default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL
+ default "pid" if MAC80211_RC_DEFAULT_PID
+ default "multirate-pid" if MAC80211_RC_MULTIRATE_DEFAULT_PID
default ""
endmenu
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 31cfd1f89a72..87e6dcb2d096 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -41,8 +41,14 @@ mac80211-$(CONFIG_MAC80211_MESH) += \
rc80211_pid-y := rc80211_pid_algo.o
rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o
+rc80211_multirate_pid-y := rc80211_multirate_pid_algo.o
+rc80211_multirate_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_multirate_pid_debugfs.o
+
rc80211_minstrel-y := rc80211_minstrel.o
rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o
mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y)
+mac80211-$(CONFIG_MAC80211_RC_MULTIRATE_PID) += $(rc80211_multirate_pid-y)
mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 855126a3039d..9d4e4d846ec1 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -17,13 +17,6 @@
#include "rate.h"
#include "mesh.h"
-struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy)
-{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- return &local->hw;
-}
-EXPORT_SYMBOL(wiphy_to_hw);
-
static bool nl80211_type_check(enum nl80211_iftype type)
{
switch (type) {
@@ -33,6 +26,8 @@ static bool nl80211_type_check(enum nl80211_iftype type)
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
#endif
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_WDS:
return true;
default:
@@ -315,12 +310,35 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->filled = STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_BYTES |
- STATION_INFO_TX_BYTES;
+ STATION_INFO_TX_BYTES |
+ STATION_INFO_TX_BITRATE;
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
sinfo->rx_bytes = sta->rx_bytes;
sinfo->tx_bytes = sta->tx_bytes;
+ if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
+ sinfo->filled |= STATION_INFO_SIGNAL;
+ sinfo->signal = (s8)sta->last_signal;
+ }
+
+ sinfo->txrate.flags = 0;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_SHORT_GI)
+ sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+
+ if (!(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) {
+ struct ieee80211_supported_band *sband;
+ sband = sta->local->hw.wiphy->bands[
+ sta->local->hw.conf.channel->band];
+ sinfo->txrate.legacy =
+ sband->bitrates[sta->last_tx_rate.idx].bitrate;
+ } else
+ sinfo->txrate.mcs = sta->last_tx_rate.idx;
+
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
sinfo->filled |= STATION_INFO_LLID |
@@ -401,8 +419,10 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
*/
if (params->interval) {
sdata->local->hw.conf.beacon_int = params->interval;
- if (ieee80211_hw_config(sdata->local))
- return -EINVAL;
+ err = ieee80211_hw_config(sdata->local,
+ IEEE80211_CONF_CHANGE_BEACON_INTERVAL);
+ if (err < 0)
+ return err;
/*
* We updated some parameter so if below bails out
* it's not an error.
@@ -589,6 +609,8 @@ static void sta_apply_parameters(struct ieee80211_local *local,
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ sband = local->hw.wiphy->bands[local->oper_channel->band];
+
/*
* FIXME: updating the flags is racy when this function is
* called from ieee80211_change_station(), this will
@@ -629,7 +651,6 @@ static void sta_apply_parameters(struct ieee80211_local *local,
if (params->supported_rates) {
rates = 0;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
for (i = 0; i < params->supported_rates_len; i++) {
int rate = (params->supported_rates[i] & 0x7f) * 5;
@@ -641,10 +662,10 @@ static void sta_apply_parameters(struct ieee80211_local *local,
sta->sta.supp_rates[local->oper_channel->band] = rates;
}
- if (params->ht_capa) {
- ieee80211_ht_cap_ie_to_ht_info(params->ht_capa,
- &sta->sta.ht_info);
- }
+ if (params->ht_capa)
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ params->ht_capa,
+ &sta->sta.ht_cap);
if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
switch (params->plink_action) {
@@ -665,6 +686,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
int err;
+ int layer2_update;
/* Prevent a race with changing the rate control algorithm */
if (!netif_running(dev))
@@ -695,17 +717,25 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
rate_control_rate_init(sta);
+ layer2_update = sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_AP;
+
rcu_read_lock();
err = sta_info_insert(sta);
if (err) {
/* STA has been freed */
+ if (err == -EEXIST && layer2_update) {
+ /* Need to update layer 2 devices on reassociation */
+ sta = sta_info_get(local, mac);
+ if (sta)
+ ieee80211_send_layer2_update(sta);
+ }
rcu_read_unlock();
return err;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
- sdata->vif.type == NL80211_IFTYPE_AP)
+ if (layer2_update)
ieee80211_send_layer2_update(sta);
rcu_read_unlock();
@@ -957,6 +987,72 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
rcu_read_unlock();
return 0;
}
+
+static int ieee80211_get_mesh_params(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct mesh_config *conf)
+{
+ struct ieee80211_sub_if_data *sdata;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+ memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config));
+ return 0;
+}
+
+static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask)
+{
+ return (mask >> (parm-1)) & 0x1;
+}
+
+static int ieee80211_set_mesh_params(struct wiphy *wiphy,
+ struct net_device *dev,
+ const struct mesh_config *nconf, u32 mask)
+{
+ struct mesh_config *conf;
+ struct ieee80211_sub_if_data *sdata;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
+ return -ENOTSUPP;
+
+ /* Set the config options which we are interested in setting */
+ conf = &(sdata->u.mesh.mshcfg);
+ if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask))
+ conf->dot11MeshRetryTimeout = nconf->dot11MeshRetryTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_CONFIRM_TIMEOUT, mask))
+ conf->dot11MeshConfirmTimeout = nconf->dot11MeshConfirmTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HOLDING_TIMEOUT, mask))
+ conf->dot11MeshHoldingTimeout = nconf->dot11MeshHoldingTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_MAX_PEER_LINKS, mask))
+ conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks;
+ if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask))
+ conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries;
+ if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask))
+ conf->dot11MeshTTL = nconf->dot11MeshTTL;
+ if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask))
+ conf->auto_open_plinks = nconf->auto_open_plinks;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask))
+ conf->dot11MeshHWMPmaxPREQretries =
+ nconf->dot11MeshHWMPmaxPREQretries;
+ if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask))
+ conf->path_refresh_time = nconf->path_refresh_time;
+ if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask))
+ conf->min_discovery_timeout = nconf->min_discovery_timeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask))
+ conf->dot11MeshHWMPactivePathTimeout =
+ nconf->dot11MeshHWMPactivePathTimeout;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask))
+ conf->dot11MeshHWMPpreqMinInterval =
+ nconf->dot11MeshHWMPpreqMinInterval;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ mask))
+ conf->dot11MeshHWMPnetDiameterTraversalTime =
+ nconf->dot11MeshHWMPnetDiameterTraversalTime;
+ return 0;
+}
+
#endif
static int ieee80211_change_bss(struct wiphy *wiphy,
@@ -972,25 +1068,79 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
return -EINVAL;
if (params->use_cts_prot >= 0) {
- sdata->bss_conf.use_cts_prot = params->use_cts_prot;
+ sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
changed |= BSS_CHANGED_ERP_CTS_PROT;
}
if (params->use_short_preamble >= 0) {
- sdata->bss_conf.use_short_preamble =
+ sdata->vif.bss_conf.use_short_preamble =
params->use_short_preamble;
changed |= BSS_CHANGED_ERP_PREAMBLE;
}
if (params->use_short_slot_time >= 0) {
- sdata->bss_conf.use_short_slot =
+ sdata->vif.bss_conf.use_short_slot =
params->use_short_slot_time;
changed |= BSS_CHANGED_ERP_SLOT;
}
+ if (params->basic_rates) {
+ int i, j;
+ u32 rates = 0;
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_supported_band *sband =
+ wiphy->bands[local->oper_channel->band];
+
+ for (i = 0; i < params->basic_rates_len; i++) {
+ int rate = (params->basic_rates[i] & 0x7f) * 5;
+ for (j = 0; j < sband->n_bitrates; j++) {
+ if (sband->bitrates[j].bitrate == rate)
+ rates |= BIT(j);
+ }
+ }
+ sdata->vif.bss_conf.basic_rates = rates;
+ changed |= BSS_CHANGED_BASIC_RATES;
+ }
+
ieee80211_bss_info_change_notify(sdata, changed);
return 0;
}
+static int ieee80211_set_txq_params(struct wiphy *wiphy,
+ struct ieee80211_txq_params *params)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_tx_queue_params p;
+
+ if (!local->ops->conf_tx)
+ return -EOPNOTSUPP;
+
+ memset(&p, 0, sizeof(p));
+ p.aifs = params->aifs;
+ p.cw_max = params->cwmax;
+ p.cw_min = params->cwmin;
+ p.txop = params->txop;
+ if (local->ops->conf_tx(local_to_hw(local), params->queue, &p)) {
+ printk(KERN_DEBUG "%s: failed to set TX queue "
+ "parameters for queue %d\n", local->mdev->name,
+ params->queue);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ieee80211_set_channel(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ local->oper_channel = chan;
+ local->oper_channel_type = channel_type;
+
+ return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -1013,6 +1163,10 @@ struct cfg80211_ops mac80211_config_ops = {
.change_mpath = ieee80211_change_mpath,
.get_mpath = ieee80211_get_mpath,
.dump_mpath = ieee80211_dump_mpath,
+ .set_mesh_params = ieee80211_set_mesh_params,
+ .get_mesh_params = ieee80211_get_mesh_params,
#endif
.change_bss = ieee80211_change_bss,
+ .set_txq_params = ieee80211_set_txq_params,
+ .set_channel = ieee80211_set_channel,
};
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 24ce54463310..2697a2fe608f 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -47,18 +47,14 @@ static const struct file_operations name## _ops = { \
DEBUGFS_READONLY_FILE(frequency, 20, "%d",
local->hw.conf.channel->center_freq);
-DEBUGFS_READONLY_FILE(antenna_sel_tx, 20, "%d",
- local->hw.conf.antenna_sel_tx);
-DEBUGFS_READONLY_FILE(antenna_sel_rx, 20, "%d",
- local->hw.conf.antenna_sel_rx);
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
local->rts_threshold);
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
local->fragmentation_threshold);
DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
- local->short_retry_limit);
+ local->hw.conf.short_frame_max_tx_count);
DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
- local->long_retry_limit);
+ local->hw.conf.long_frame_max_tx_count);
DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d",
local->total_ps_buffered);
DEBUGFS_READONLY_FILE(wep_iv, 20, "%#06x",
@@ -202,8 +198,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
local->debugfs.keys = debugfs_create_dir("keys", phyd);
DEBUGFS_ADD(frequency);
- DEBUGFS_ADD(antenna_sel_tx);
- DEBUGFS_ADD(antenna_sel_rx);
DEBUGFS_ADD(rts_threshold);
DEBUGFS_ADD(fragmentation_threshold);
DEBUGFS_ADD(short_retry_limit);
@@ -258,8 +252,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
void debugfs_hw_del(struct ieee80211_local *local)
{
DEBUGFS_DEL(frequency);
- DEBUGFS_DEL(antenna_sel_tx);
- DEBUGFS_DEL(antenna_sel_rx);
DEBUGFS_DEL(rts_threshold);
DEBUGFS_DEL(fragmentation_threshold);
DEBUGFS_DEL(short_retry_limit);
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index a3294d109322..6424ac565ae0 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -188,7 +188,6 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
{
static int keycount;
char buf[50];
- DECLARE_MAC_BUF(mac);
struct sta_info *sta;
if (!key->local->debugfs.keys)
@@ -206,8 +205,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
rcu_read_lock();
sta = rcu_dereference(key->sta);
if (sta)
- sprintf(buf, "../../stations/%s",
- print_mac(mac, sta->sta.addr));
+ sprintf(buf, "../../stations/%pM", sta->sta.addr);
rcu_read_unlock();
/* using sta as a boolean is fine outside RCU lock */
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 2ad504fc3414..c54219301724 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -41,29 +41,6 @@ static ssize_t ieee80211_if_read(
return ret;
}
-#ifdef CONFIG_MAC80211_MESH
-static ssize_t ieee80211_if_write(
- struct ieee80211_sub_if_data *sdata,
- char const __user *userbuf,
- size_t count, loff_t *ppos,
- int (*format)(struct ieee80211_sub_if_data *, char *))
-{
- char buf[10];
- int buf_size;
-
- memset(buf, 0x00, sizeof(buf));
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, userbuf, buf_size))
- return count;
- read_lock(&dev_base_lock);
- if (sdata->dev->reg_state == NETREG_REGISTERED)
- (*format)(sdata, buf);
- read_unlock(&dev_base_lock);
-
- return count;
-}
-#endif
-
#define IEEE80211_IF_FMT(name, field, format_string) \
static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, char *buf, \
@@ -71,19 +48,6 @@ static ssize_t ieee80211_if_fmt_##name( \
{ \
return scnprintf(buf, buflen, format_string, sdata->field); \
}
-#define IEEE80211_IF_WFMT(name, field, type) \
-static int ieee80211_if_wfmt_##name( \
- struct ieee80211_sub_if_data *sdata, char *buf) \
-{ \
- unsigned long tmp; \
- char *endp; \
- \
- tmp = simple_strtoul(buf, &endp, 0); \
- if ((endp == buf) || ((type)tmp != tmp)) \
- return -EINVAL; \
- sdata->field = tmp; \
- return 0; \
-}
#define IEEE80211_IF_FMT_DEC(name, field) \
IEEE80211_IF_FMT(name, field, "%d\n")
#define IEEE80211_IF_FMT_HEX(name, field) \
@@ -104,8 +68,7 @@ static ssize_t ieee80211_if_fmt_##name( \
const struct ieee80211_sub_if_data *sdata, char *buf, \
int buflen) \
{ \
- DECLARE_MAC_BUF(mac); \
- return scnprintf(buf, buflen, "%s\n", print_mac(mac, sdata->field));\
+ return scnprintf(buf, buflen, "%pM\n", sdata->field); \
}
#define __IEEE80211_IF_FILE(name) \
@@ -126,34 +89,6 @@ static const struct file_operations name##_ops = { \
IEEE80211_IF_FMT_##format(name, field) \
__IEEE80211_IF_FILE(name)
-#define __IEEE80211_IF_WFILE(name) \
-static ssize_t ieee80211_if_read_##name(struct file *file, \
- char __user *userbuf, \
- size_t count, loff_t *ppos) \
-{ \
- return ieee80211_if_read(file->private_data, \
- userbuf, count, ppos, \
- ieee80211_if_fmt_##name); \
-} \
-static ssize_t ieee80211_if_write_##name(struct file *file, \
- const char __user *userbuf, \
- size_t count, loff_t *ppos) \
-{ \
- return ieee80211_if_write(file->private_data, \
- userbuf, count, ppos, \
- ieee80211_if_wfmt_##name); \
-} \
-static const struct file_operations name##_ops = { \
- .read = ieee80211_if_read_##name, \
- .write = ieee80211_if_write_##name, \
- .open = mac80211_open_file_generic, \
-}
-
-#define IEEE80211_IF_WFILE(name, field, format, type) \
- IEEE80211_IF_FMT_##format(name, field) \
- IEEE80211_IF_WFMT(name, field, type) \
- __IEEE80211_IF_WFILE(name)
-
/* common attributes */
IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
IEEE80211_IF_FILE(force_unicast_rateidx, force_unicast_rateidx, DEC);
@@ -184,7 +119,7 @@ static ssize_t ieee80211_if_fmt_flags(
sdata->u.sta.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "",
sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "",
sdata->u.sta.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "",
- sdata->bss_conf.use_cts_prot ? "CTS prot\n" : "");
+ sdata->vif.bss_conf.use_cts_prot ? "CTS prot\n" : "");
}
__IEEE80211_IF_FILE(flags);
@@ -212,30 +147,30 @@ IEEE80211_IF_FILE(dropped_frames_no_route,
IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC);
/* Mesh parameters */
-IEEE80211_IF_WFILE(dot11MeshMaxRetries,
- u.mesh.mshcfg.dot11MeshMaxRetries, DEC, u8);
-IEEE80211_IF_WFILE(dot11MeshRetryTimeout,
- u.mesh.mshcfg.dot11MeshRetryTimeout, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshConfirmTimeout,
- u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHoldingTimeout,
- u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC, u8);
-IEEE80211_IF_WFILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC, u8);
-IEEE80211_IF_WFILE(dot11MeshMaxPeerLinks,
- u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHWMPactivePathTimeout,
- u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC, u32);
-IEEE80211_IF_WFILE(dot11MeshHWMPpreqMinInterval,
- u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHWMPnetDiameterTraversalTime,
- u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC, u16);
-IEEE80211_IF_WFILE(dot11MeshHWMPmaxPREQretries,
- u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC, u8);
-IEEE80211_IF_WFILE(path_refresh_time,
- u.mesh.mshcfg.path_refresh_time, DEC, u32);
-IEEE80211_IF_WFILE(min_discovery_timeout,
- u.mesh.mshcfg.min_discovery_timeout, DEC, u16);
+IEEE80211_IF_FILE(dot11MeshMaxRetries,
+ u.mesh.mshcfg.dot11MeshMaxRetries, DEC);
+IEEE80211_IF_FILE(dot11MeshRetryTimeout,
+ u.mesh.mshcfg.dot11MeshRetryTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshConfirmTimeout,
+ u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshHoldingTimeout,
+ u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC);
+IEEE80211_IF_FILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC);
+IEEE80211_IF_FILE(dot11MeshMaxPeerLinks,
+ u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout,
+ u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval,
+ u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime,
+ u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries,
+ u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC);
+IEEE80211_IF_FILE(path_refresh_time,
+ u.mesh.mshcfg.path_refresh_time, DEC);
+IEEE80211_IF_FILE(min_discovery_timeout,
+ u.mesh.mshcfg.min_discovery_timeout, DEC);
#endif
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index b85c4f27b361..a2fbe0131312 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -39,13 +39,6 @@ static const struct file_operations sta_ ##name## _ops = { \
.open = mac80211_open_file_generic, \
}
-#define STA_OPS_WR(name) \
-static const struct file_operations sta_ ##name## _ops = { \
- .read = sta_##name##_read, \
- .write = sta_##name##_write, \
- .open = mac80211_open_file_generic, \
-}
-
#define STA_FILE(name, field, format) \
STA_READ_##format(name, field) \
STA_OPS(name)
@@ -144,7 +137,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
for (i = 0; i < STA_TID_NUM; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_rx[i]?
+ sta->ampdu_mlme.tid_state_rx[i] ?
sta->ampdu_mlme.tid_rx[i]->dialog_token : 0);
p += scnprintf(p, sizeof(buf)+buf-p, "\n TX :");
@@ -155,84 +148,20 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
for (i = 0; i < STA_TID_NUM; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_tx[i]?
+ sta->ampdu_mlme.tid_state_tx[i] ?
sta->ampdu_mlme.tid_tx[i]->dialog_token : 0);
p += scnprintf(p, sizeof(buf)+buf-p, "\n SSN :");
for (i = 0; i < STA_TID_NUM; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_tx[i]?
+ sta->ampdu_mlme.tid_state_tx[i] ?
sta->ampdu_mlme.tid_tx[i]->ssn : 0);
p += scnprintf(p, sizeof(buf)+buf-p, "\n");
return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
}
-
-static ssize_t sta_agg_status_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- struct sta_info *sta = file->private_data;
- struct ieee80211_local *local = sta->sdata->local;
- struct ieee80211_hw *hw = &local->hw;
- u8 *da = sta->sta.addr;
- static int tid_static_tx[16] = {0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0};
- static int tid_static_rx[16] = {1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1};
- char *endp;
- char buf[32];
- int buf_size, rs;
- unsigned int tid_num;
- char state[4];
-
- memset(buf, 0x00, sizeof(buf));
- buf_size = min(count, (sizeof(buf)-1));
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
-
- tid_num = simple_strtoul(buf, &endp, 0);
- if (endp == buf)
- return -EINVAL;
-
- if ((tid_num >= 100) && (tid_num <= 115)) {
- /* toggle Rx aggregation command */
- tid_num = tid_num - 100;
- if (tid_static_rx[tid_num] == 1) {
- strcpy(state, "off");
- ieee80211_sta_stop_rx_ba_session(sta->sdata, da, tid_num, 0,
- WLAN_REASON_QSTA_REQUIRE_SETUP);
- sta->ampdu_mlme.tid_state_rx[tid_num] |=
- HT_AGG_STATE_DEBUGFS_CTL;
- tid_static_rx[tid_num] = 0;
- } else {
- strcpy(state, "on ");
- sta->ampdu_mlme.tid_state_rx[tid_num] &=
- ~HT_AGG_STATE_DEBUGFS_CTL;
- tid_static_rx[tid_num] = 1;
- }
- printk(KERN_DEBUG "debugfs - try switching tid %u %s\n",
- tid_num, state);
- } else if ((tid_num >= 0) && (tid_num <= 15)) {
- /* toggle Tx aggregation command */
- if (tid_static_tx[tid_num] == 0) {
- strcpy(state, "on ");
- rs = ieee80211_start_tx_ba_session(hw, da, tid_num);
- if (rs == 0)
- tid_static_tx[tid_num] = 1;
- } else {
- strcpy(state, "off");
- rs = ieee80211_stop_tx_ba_session(hw, da, tid_num, 1);
- if (rs == 0)
- tid_static_tx[tid_num] = 0;
- }
- printk(KERN_DEBUG "debugfs - switching tid %u %s, return=%d\n",
- tid_num, state, rs);
- }
-
- return count;
-}
-STA_OPS_WR(agg_status);
+STA_OPS(agg_status);
#define DEBUGFS_ADD(name) \
sta->debugfs.name = debugfs_create_file(#name, 0400, \
@@ -246,15 +175,14 @@ STA_OPS_WR(agg_status);
void ieee80211_sta_debugfs_add(struct sta_info *sta)
{
struct dentry *stations_dir = sta->local->debugfs.stations;
- DECLARE_MAC_BUF(mbuf);
- u8 *mac;
+ u8 mac[3*ETH_ALEN];
sta->debugfs.add_has_run = true;
if (!stations_dir)
return;
- mac = print_mac(mbuf, sta->sta.addr);
+ snprintf(mac, sizeof(mac), "%pM", sta->sta.addr);
/*
* This might fail due to a race condition:
diff --git a/net/mac80211/event.c b/net/mac80211/event.c
index 8de60de70bc9..0d95561c0ee0 100644
--- a/net/mac80211/event.c
+++ b/net/mac80211/event.c
@@ -21,14 +21,13 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
{
union iwreq_data wrqu;
char *buf = kmalloc(128, GFP_ATOMIC);
- DECLARE_MAC_BUF(mac);
if (buf) {
/* TODO: needed parameters: count, key type, TSC */
sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
- "keyid=%d %scast addr=%s)",
+ "keyid=%d %scast addr=%pM)",
keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
- print_mac(mac, hdr->addr2));
+ hdr->addr2);
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = strlen(buf);
wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf);
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index dc7d9a3d70d5..c5c0c5271096 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -20,50 +20,138 @@
#include "sta_info.h"
#include "wme.h"
-int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
- struct ieee80211_ht_info *ht_info)
+void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
+ struct ieee80211_ht_cap *ht_cap_ie,
+ struct ieee80211_sta_ht_cap *ht_cap)
{
+ u8 ampdu_info, tx_mcs_set_cap;
+ int i, max_tx_streams;
- if (ht_info == NULL)
- return -EINVAL;
+ BUG_ON(!ht_cap);
+
+ memset(ht_cap, 0, sizeof(*ht_cap));
+
+ if (!ht_cap_ie)
+ return;
+
+ ht_cap->ht_supported = true;
- memset(ht_info, 0, sizeof(*ht_info));
+ ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & sband->ht_cap.cap;
+ ht_cap->cap &= ~IEEE80211_HT_CAP_SM_PS;
+ ht_cap->cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS;
- if (ht_cap_ie) {
- u8 ampdu_info = ht_cap_ie->ampdu_params_info;
+ ampdu_info = ht_cap_ie->ampdu_params_info;
+ ht_cap->ampdu_factor =
+ ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
+ ht_cap->ampdu_density =
+ (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
- ht_info->ht_supported = 1;
- ht_info->cap = le16_to_cpu(ht_cap_ie->cap_info);
- ht_info->ampdu_factor =
- ampdu_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
- ht_info->ampdu_density =
- (ampdu_info & IEEE80211_HT_CAP_AMPDU_DENSITY) >> 2;
- memcpy(ht_info->supp_mcs_set, ht_cap_ie->supp_mcs_set, 16);
- } else
- ht_info->ht_supported = 0;
+ /* own MCS TX capabilities */
+ tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;
- return 0;
+ /* can we TX with MCS rates? */
+ if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
+ return;
+
+ /* Counting from 0, therefore +1 */
+ if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
+ max_tx_streams =
+ ((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
+ >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
+ else
+ max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS;
+
+ /*
+ * 802.11n D5.0 20.3.5 / 20.6 says:
+ * - indices 0 to 7 and 32 are single spatial stream
+ * - 8 to 31 are multiple spatial streams using equal modulation
+ * [8..15 for two streams, 16..23 for three and 24..31 for four]
+ * - remainder are multiple spatial streams using unequal modulation
+ */
+ for (i = 0; i < max_tx_streams; i++)
+ ht_cap->mcs.rx_mask[i] =
+ sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
+
+ if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
+ for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
+ i < IEEE80211_HT_MCS_MASK_LEN; i++)
+ ht_cap->mcs.rx_mask[i] =
+ sband->ht_cap.mcs.rx_mask[i] &
+ ht_cap_ie->mcs.rx_mask[i];
+
+ /* handle MCS rate 32 too */
+ if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
+ ht_cap->mcs.rx_mask[32/8] |= 1;
}
-int ieee80211_ht_addt_info_ie_to_ht_bss_info(
- struct ieee80211_ht_addt_info *ht_add_info_ie,
- struct ieee80211_ht_bss_info *bss_info)
+/*
+ * ieee80211_enable_ht should be called only after the operating band
+ * has been determined as ht configuration depends on the hw's
+ * HT abilities for a specific band.
+ */
+u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_ht_info *hti,
+ u16 ap_ht_cap_flags)
{
- if (bss_info == NULL)
- return -EINVAL;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_bss_ht_conf ht;
+ u32 changed = 0;
+ bool enable_ht = true, ht_changed;
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ memset(&ht, 0, sizeof(ht));
+
+ /* HT is not supported */
+ if (!sband->ht_cap.ht_supported)
+ enable_ht = false;
+
+ /* check that channel matches the right operating channel */
+ if (local->hw.conf.channel->center_freq !=
+ ieee80211_channel_to_frequency(hti->control_chan))
+ enable_ht = false;
+
+ if (enable_ht) {
+ channel_type = NL80211_CHAN_HT20;
+
+ if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
+ (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
+ (hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
+ switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ channel_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ channel_type = NL80211_CHAN_HT40MINUS;
+ break;
+ }
+ }
+ }
+
+ ht_changed = local->hw.conf.ht.enabled != enable_ht ||
+ channel_type != local->hw.conf.ht.channel_type;
+
+ local->oper_channel_type = channel_type;
+ local->hw.conf.ht.enabled = enable_ht;
- memset(bss_info, 0, sizeof(*bss_info));
+ if (ht_changed)
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_HT);
- if (ht_add_info_ie) {
- u16 op_mode;
- op_mode = le16_to_cpu(ht_add_info_ie->operation_mode);
+ /* disable HT */
+ if (!enable_ht)
+ return 0;
- bss_info->primary_channel = ht_add_info_ie->control_chan;
- bss_info->bss_cap = ht_add_info_ie->ht_param;
- bss_info->bss_op_mode = (u8)(op_mode & 0xff);
+ ht.operation_mode = le16_to_cpu(hti->operation_mode);
+
+ /* if bss configuration changed store the new one */
+ if (memcmp(&sdata->vif.bss_conf.ht, &ht, sizeof(ht))) {
+ changed |= BSS_CHANGED_HT;
+ sdata->vif.bss_conf.ht = ht;
}
- return 0;
+ return changed;
}
static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
@@ -241,7 +329,6 @@ void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *r
struct ieee80211_hw *hw = &local->hw;
struct sta_info *sta;
int ret, i;
- DECLARE_MAC_BUF(mac);
rcu_read_lock();
@@ -269,8 +356,8 @@ void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *r
BUG_ON(!local->ops->ampdu_action);
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Rx BA session stop requested for %s tid %u\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_RX_STOP,
@@ -382,15 +469,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
struct ieee80211_sub_if_data *sdata;
u16 start_seq_num;
u8 *state;
- int ret;
- DECLARE_MAC_BUF(mac);
+ int ret = 0;
- if (tid >= STA_TID_NUM)
+ if ((tid >= STA_TID_NUM) || !(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION))
return -EINVAL;
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Open BA session requested for %s tid %u\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Open BA session requested for %pM tid %u\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
@@ -442,17 +528,19 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
(unsigned long)&sta->timer_to_tid[tid];
init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
- /* create a new queue for this aggregation */
- ret = ieee80211_ht_agg_queue_add(local, sta, tid);
+ if (hw->ampdu_queues) {
+ /* create a new queue for this aggregation */
+ ret = ieee80211_ht_agg_queue_add(local, sta, tid);
- /* case no queue is available to aggregation
- * don't switch to aggregation */
- if (ret) {
+ /* case no queue is available to aggregation
+ * don't switch to aggregation */
+ if (ret) {
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "BA request denied - queue unavailable for"
- " tid %d\n", tid);
+ printk(KERN_DEBUG "BA request denied - "
+ "queue unavailable for tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- goto err_unlock_queue;
+ goto err_unlock_queue;
+ }
}
sdata = sta->sdata;
@@ -471,7 +559,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
/* No need to requeue the packets in the agg queue, since we
* held the tx lock: no packet could be enqueued to the newly
* allocated queue */
- ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
+ if (hw->ampdu_queues)
+ ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - HW unavailable for"
" tid %d\n", tid);
@@ -481,7 +570,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
}
/* Will put all the packets in the new SW queue */
- ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
spin_unlock_bh(&sta->lock);
/* send an addBA request */
@@ -524,7 +614,6 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
struct sta_info *sta;
u8 *state;
int ret = 0;
- DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM)
return -EINVAL;
@@ -546,11 +635,12 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
}
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Tx BA session stop requested for %s tid %u\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
*state = HT_AGG_STATE_REQ_STOP_BA_MSK |
(initiator << HT_AGG_STATE_INITIATOR_SHIFT);
@@ -563,7 +653,8 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
if (ret) {
WARN_ON(ret != -EBUSY);
*state = HT_AGG_STATE_OPERATIONAL;
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
goto stop_BA_exit;
}
@@ -579,7 +670,6 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
u8 *state;
- DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -594,8 +684,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
if (!sta) {
rcu_read_unlock();
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Could not find station: %s\n",
- print_mac(mac, ra));
+ printk(KERN_DEBUG "Could not find station: %pM\n", ra);
#endif
return;
}
@@ -621,7 +710,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
#endif
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues)
+ ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
}
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
@@ -634,7 +724,6 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
struct sta_info *sta;
u8 *state;
int agg_queue;
- DECLARE_MAC_BUF(mac);
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -645,16 +734,15 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
}
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Stopping Tx BA session for %s tid %d\n",
- print_mac(mac, ra), tid);
+ printk(KERN_DEBUG "Stopping Tx BA session for %pM tid %d\n",
+ ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Could not find station: %s\n",
- print_mac(mac, ra));
+ printk(KERN_DEBUG "Could not find station: %pM\n", ra);
#endif
rcu_read_unlock();
return;
@@ -677,16 +765,18 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
ieee80211_send_delba(sta->sdata, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
- agg_queue = sta->tid_to_tx_q[tid];
-
- ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
-
- /* We just requeued the all the frames that were in the
- * removed queue, and since we might miss a softirq we do
- * netif_schedule_queue. ieee80211_wake_queue is not used
- * here as this queue is not necessarily stopped
- */
- netif_schedule_queue(netdev_get_tx_queue(local->mdev, agg_queue));
+ if (hw->ampdu_queues) {
+ agg_queue = sta->tid_to_tx_q[tid];
+ ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
+
+ /* We just requeued the all the frames that were in the
+ * removed queue, and since we might miss a softirq we do
+ * netif_schedule_queue. ieee80211_wake_queue is not used
+ * here as this queue is not necessarily stopped
+ */
+ netif_schedule_queue(netdev_get_tx_queue(local->mdev,
+ agg_queue));
+ }
spin_lock_bh(&sta->lock);
*state = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.addba_req_num[tid] = 0;
@@ -783,7 +873,6 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
u8 dialog_token;
int ret = -EOPNOTSUPP;
- DECLARE_MAC_BUF(mac);
/* extract session parameters from addba request frame */
dialog_token = mgmt->u.action.u.addba_req.dialog_token;
@@ -801,15 +890,16 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
/* sanity check for incoming parameters:
* check if configuration can support the BA policy
* and if buffer size does not exceeds max value */
+ /* XXX: check own ht delayed BA capability?? */
if (((ba_policy != 1)
- && (!(conf->ht_conf.cap & IEEE80211_HT_CAP_DELAY_BA)))
+ && (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA)))
|| (buf_size > IEEE80211_MAX_AMPDU_BUF)) {
status = WLAN_STATUS_INVALID_QOS_PARAM;
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "AddBA Req with bad params from "
- "%s on tid %u. policy %d, buffer size %d\n",
- print_mac(mac, mgmt->sa), tid, ba_policy,
+ "%pM on tid %u. policy %d, buffer size %d\n",
+ mgmt->sa, tid, ba_policy,
buf_size);
#endif /* CONFIG_MAC80211_HT_DEBUG */
goto end_no_lock;
@@ -820,7 +910,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
sband = local->hw.wiphy->bands[conf->channel->band];
buf_size = IEEE80211_MIN_AMPDU_BUF;
- buf_size = buf_size << sband->ht_info.ampdu_factor;
+ buf_size = buf_size << sband->ht_cap.ampdu_factor;
}
@@ -831,8 +921,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "unexpected AddBA Req from "
- "%s on tid %u\n",
- print_mac(mac, mgmt->sa), tid);
+ "%pM on tid %u\n",
+ mgmt->sa, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
goto end;
}
@@ -910,7 +1000,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
{
struct ieee80211_hw *hw = &local->hw;
u16 capab;
- u16 tid;
+ u16 tid, start_seq_num;
u8 *state;
capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
@@ -943,9 +1033,18 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
*state |= HT_ADDBA_RECEIVED_MSK;
sta->ampdu_mlme.addba_req_num[tid] = 0;
- if (*state == HT_AGG_STATE_OPERATIONAL)
+ if (*state == HT_AGG_STATE_OPERATIONAL &&
+ local->hw.ampdu_queues)
ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (local->ops->ampdu_action) {
+ (void)local->ops->ampdu_action(hw,
+ IEEE80211_AMPDU_TX_RESUME,
+ &sta->sta, tid, &start_seq_num);
+ }
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Resuming TX aggregation for tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
spin_unlock_bh(&sta->lock);
} else {
sta->ampdu_mlme.addba_req_num[tid]++;
@@ -964,7 +1063,6 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
u16 tid, params;
u16 initiator;
- DECLARE_MAC_BUF(mac);
params = le16_to_cpu(mgmt->u.action.u.delba.params);
tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
@@ -972,9 +1070,8 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
- printk(KERN_DEBUG "delba from %s (%s) tid %d reason code %d\n",
- print_mac(mac, mgmt->sa),
- initiator ? "initiator" : "recipient", tid,
+ printk(KERN_DEBUG "delba from %pM (%s) tid %d reason code %d\n",
+ mgmt->sa, initiator ? "initiator" : "recipient", tid,
mgmt->u.action.u.delba.reason_code);
#endif /* CONFIG_MAC80211_HT_DEBUG */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 156e42a003ae..e036c7e748d8 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -23,6 +23,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/etherdevice.h>
+#include <net/cfg80211.h>
#include <net/wireless.h>
#include <net/iw_handler.h>
#include <net/mac80211.h>
@@ -142,7 +143,6 @@ typedef unsigned __bitwise__ ieee80211_tx_result;
#define IEEE80211_TX_FRAGMENTED BIT(0)
#define IEEE80211_TX_UNICAST BIT(1)
#define IEEE80211_TX_PS_BUFFERED BIT(2)
-#define IEEE80211_TX_PROBE_LAST_FRAG BIT(3)
struct ieee80211_tx_data {
struct sk_buff *skb;
@@ -153,11 +153,6 @@ struct ieee80211_tx_data {
struct ieee80211_key *key;
struct ieee80211_channel *channel;
- s8 rate_idx;
- /* use this rate (if set) for last fragment; rate can
- * be set to lower rate for the first fragments, e.g.,
- * when using CTS protection with IEEE 802.11g. */
- s8 last_frag_rate_idx;
/* Extra fragments (in addition to the first fragment
* in skb) */
@@ -192,7 +187,6 @@ struct ieee80211_rx_data {
struct ieee80211_rx_status *status;
struct ieee80211_rate *rate;
- u16 ethertype;
unsigned int flags;
int sent_ps_buffered;
int queue;
@@ -203,9 +197,7 @@ struct ieee80211_rx_data {
struct ieee80211_tx_stored_packet {
struct sk_buff *skb;
struct sk_buff **extra_frag;
- s8 last_frag_rate_idx;
int num_extra_frag;
- bool last_frag_rate_ctrl_probe;
};
struct beacon_data {
@@ -219,9 +211,6 @@ struct ieee80211_if_ap {
struct list_head vlans;
- u8 ssid[IEEE80211_MAX_SSID_LEN];
- size_t ssid_len;
-
/* yes, this looks ugly, but guarantees that we can later use
* bitmap_empty :)
* NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */
@@ -255,26 +244,6 @@ struct mesh_preq_queue {
u8 flags;
};
-struct mesh_config {
- /* Timeouts in ms */
- /* Mesh plink management parameters */
- u16 dot11MeshRetryTimeout;
- u16 dot11MeshConfirmTimeout;
- u16 dot11MeshHoldingTimeout;
- u16 dot11MeshMaxPeerLinks;
- u8 dot11MeshMaxRetries;
- u8 dot11MeshTTL;
- bool auto_open_plinks;
- /* HWMP parameters */
- u8 dot11MeshHWMPmaxPREQretries;
- u32 path_refresh_time;
- u16 min_discovery_timeout;
- u32 dot11MeshHWMPactivePathTimeout;
- u16 dot11MeshHWMPpreqMinInterval;
- u16 dot11MeshHWMPnetDiameterTraversalTime;
-};
-
-
/* flags used in struct ieee80211_if_sta.flags */
#define IEEE80211_STA_SSID_SET BIT(0)
#define IEEE80211_STA_BSSID_SET BIT(1)
@@ -438,8 +407,7 @@ struct ieee80211_sub_if_data {
struct ieee80211_key *keys[NUM_DEFAULT_KEYS];
struct ieee80211_key *default_key;
- /* BSS configuration for this interface. */
- struct ieee80211_bss_conf bss_conf;
+ u16 sequence_number;
/*
* AP this belongs to: self in AP mode and
@@ -570,6 +538,11 @@ enum {
IEEE80211_ADDBA_MSG = 4,
};
+enum queue_stop_reason {
+ IEEE80211_QUEUE_STOP_REASON_DRIVER,
+ IEEE80211_QUEUE_STOP_REASON_PS,
+};
+
/* maximum number of hardware queues we support. */
#define QD_MAX_QUEUES (IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_QUEUES)
@@ -586,7 +559,8 @@ struct ieee80211_local {
const struct ieee80211_ops *ops;
unsigned long queue_pool[BITS_TO_LONGS(QD_MAX_QUEUES)];
-
+ unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
+ spinlock_t queue_stop_reason_lock;
struct net_device *mdev; /* wmaster# - "master" 802.11 device */
int open_count;
int monitors, cooked_mntrs;
@@ -633,8 +607,6 @@ struct ieee80211_local {
int rts_threshold;
int fragmentation_threshold;
- int short_retry_limit; /* dot11ShortRetryLimit */
- int long_retry_limit; /* dot11LongRetryLimit */
struct crypto_blkcipher *wep_tx_tfm;
struct crypto_blkcipher *wep_rx_tfm;
@@ -659,6 +631,7 @@ struct ieee80211_local {
struct delayed_work scan_work;
struct ieee80211_sub_if_data *scan_sdata;
struct ieee80211_channel *oper_channel, *scan_channel;
+ enum nl80211_channel_type oper_channel_type;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
size_t scan_ssid_len;
struct list_head bss_list;
@@ -722,13 +695,17 @@ struct ieee80211_local {
int wifi_wme_noack_test;
unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
+ bool powersave;
+ int dynamic_ps_timeout;
+ struct work_struct dynamic_ps_enable_work;
+ struct work_struct dynamic_ps_disable_work;
+ struct timer_list dynamic_ps_timer;
+
#ifdef CONFIG_MAC80211_DEBUGFS
struct local_debugfsdentries {
struct dentry *rcdir;
struct dentry *rcname;
struct dentry *frequency;
- struct dentry *antenna_sel_tx;
- struct dentry *antenna_sel_rx;
struct dentry *rts_threshold;
struct dentry *fragmentation_threshold;
struct dentry *short_retry_limit;
@@ -817,7 +794,7 @@ struct ieee802_11_elems {
u8 *wmm_info;
u8 *wmm_param;
struct ieee80211_ht_cap *ht_cap_elem;
- struct ieee80211_ht_addt_info *ht_info_elem;
+ struct ieee80211_ht_info *ht_info_elem;
u8 *mesh_config;
u8 *mesh_id;
u8 *peer_link;
@@ -869,11 +846,6 @@ static inline struct ieee80211_hw *local_to_hw(
return &local->hw;
}
-struct sta_attribute {
- struct attribute attr;
- ssize_t (*show)(const struct sta_info *, char *buf);
- ssize_t (*store)(struct sta_info *, const char *buf, size_t count);
-};
static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
{
@@ -882,12 +854,9 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
}
-int ieee80211_hw_config(struct ieee80211_local *local);
+int ieee80211_hw_config(struct ieee80211_local *local, u32 changed);
int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
-u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
- struct ieee80211_ht_info *req_ht_cap,
- struct ieee80211_ht_bss_info *req_bss_cap);
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed);
void ieee80211_configure_filter(struct ieee80211_local *local);
@@ -906,8 +875,7 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid);
void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta);
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, u8 *bssid,
- u8 *addr, u64 supp_rates);
+ u8 *bssid, u8 *addr, u64 supp_rates);
int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason);
int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
@@ -968,11 +936,12 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
/* HT */
-int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie,
- struct ieee80211_ht_info *ht_info);
-int ieee80211_ht_addt_info_ie_to_ht_bss_info(
- struct ieee80211_ht_addt_info *ht_add_info_ie,
- struct ieee80211_ht_bss_info *bss_info);
+void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
+ struct ieee80211_ht_cap *ht_cap_ie,
+ struct ieee80211_sta_ht_cap *ht_cap);
+u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_ht_info *hti,
+ u16 ap_ht_cap_flags);
void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn);
void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
@@ -1014,6 +983,17 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freq);
u64 ieee80211_mandatory_rates(struct ieee80211_local *local,
enum ieee80211_band band);
+void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
+void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
+void ieee80211_dynamic_ps_timer(unsigned long data);
+
+void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason);
+void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason);
+void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+ u16 stype, u16 reason);
+
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
#else
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 8336fee68d3e..b9074824862a 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -65,7 +65,7 @@ static int ieee80211_open(struct net_device *dev)
struct ieee80211_if_init_conf conf;
u32 changed = 0;
int res;
- bool need_hw_reconfig = 0;
+ u32 hw_reconf_flags = 0;
u8 null_addr[ETH_ALEN] = {0};
/* fail early if user set an invalid address */
@@ -152,7 +152,8 @@ static int ieee80211_open(struct net_device *dev)
res = local->ops->start(local_to_hw(local));
if (res)
goto err_del_bss;
- need_hw_reconfig = 1;
+ /* we're brought up, everything changes */
+ hw_reconf_flags = ~0;
ieee80211_led_radio(local, local->hw.conf.radio_enabled);
}
@@ -198,8 +199,10 @@ static int ieee80211_open(struct net_device *dev)
/* must be before the call to ieee80211_configure_filter */
local->monitors++;
- if (local->monitors == 1)
+ if (local->monitors == 1) {
local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_RADIOTAP;
+ }
if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
local->fif_fcsfail++;
@@ -226,8 +229,14 @@ static int ieee80211_open(struct net_device *dev)
if (res)
goto err_stop;
- if (ieee80211_vif_is_mesh(&sdata->vif))
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ local->fif_other_bss++;
+ netif_addr_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_addr_unlock_bh(local->mdev);
+
ieee80211_start_mesh(sdata);
+ }
changed |= ieee80211_reset_erp_info(sdata);
ieee80211_bss_info_change_notify(sdata, changed);
ieee80211_enable_keys(sdata);
@@ -279,8 +288,8 @@ static int ieee80211_open(struct net_device *dev)
atomic_inc(&local->iff_promiscs);
local->open_count++;
- if (need_hw_reconfig) {
- ieee80211_hw_config(local);
+ if (hw_reconf_flags) {
+ ieee80211_hw_config(local, hw_reconf_flags);
/*
* set default queue parameters so drivers don't
* need to initialise the hardware if the hardware
@@ -322,6 +331,7 @@ static int ieee80211_stop(struct net_device *dev)
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_init_conf conf;
struct sta_info *sta;
+ u32 hw_reconf_flags = 0;
/*
* Stop TX on this interface first.
@@ -405,8 +415,10 @@ static int ieee80211_stop(struct net_device *dev)
}
local->monitors--;
- if (local->monitors == 0)
+ if (local->monitors == 0) {
local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_RADIOTAP;
+ }
if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
local->fif_fcsfail--;
@@ -423,7 +435,11 @@ static int ieee80211_stop(struct net_device *dev)
break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
- sdata->u.sta.state = IEEE80211_STA_MLME_DISABLED;
+ /* Announce that we are leaving the network. */
+ if (sdata->u.sta.state != IEEE80211_STA_MLME_DISABLED)
+ ieee80211_sta_deauthenticate(sdata,
+ WLAN_REASON_DEAUTH_LEAVING);
+
memset(sdata->u.sta.bssid, 0, ETH_ALEN);
del_timer_sync(&sdata->u.sta.timer);
/*
@@ -450,8 +466,15 @@ static int ieee80211_stop(struct net_device *dev)
/* fall through */
case NL80211_IFTYPE_MESH_POINT:
if (ieee80211_vif_is_mesh(&sdata->vif)) {
- /* allmulti is always set on mesh ifaces */
+ /* other_bss and allmulti are always set on mesh
+ * ifaces */
+ local->fif_other_bss--;
atomic_dec(&local->iff_allmultis);
+
+ netif_addr_lock_bh(local->mdev);
+ ieee80211_configure_filter(local);
+ netif_addr_unlock_bh(local->mdev);
+
ieee80211_stop_mesh(sdata);
}
/* fall through */
@@ -504,8 +527,15 @@ static int ieee80211_stop(struct net_device *dev)
tasklet_disable(&local->tx_pending_tasklet);
tasklet_disable(&local->tasklet);
+
+ /* no reconfiguring after stop! */
+ hw_reconf_flags = 0;
}
+ /* do after stop to avoid reconfiguring when we stop anyway */
+ if (hw_reconf_flags)
+ ieee80211_hw_config(local, hw_reconf_flags);
+
return 0;
}
@@ -668,6 +698,11 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
if (type == sdata->vif.type)
return 0;
+ /* Setting ad-hoc mode on non-IBSS channel is not supported. */
+ if (sdata->local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS &&
+ type == NL80211_IFTYPE_ADHOC)
+ return -EOPNOTSUPP;
+
/*
* We could, here, on changes between IBSS/STA/MESH modes,
* invoke an MLME function instead that disassociates etc.
@@ -682,7 +717,7 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
ieee80211_setup_sdata(sdata, type);
/* reset some values that shouldn't be kept across type changes */
- sdata->bss_conf.basic_rates =
+ sdata->vif.bss_conf.basic_rates =
ieee80211_mandatory_rates(sdata->local,
sdata->local->hw.conf.channel->band);
sdata->drop_unencrypted = 0;
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index a5b06fe71980..999f7aa42326 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -132,7 +132,6 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
{
const u8 *addr;
int ret;
- DECLARE_MAC_BUF(mac);
assert_key_lock();
might_sleep();
@@ -154,16 +153,15 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
printk(KERN_ERR "mac80211-%s: failed to set key "
- "(%d, %s) to hardware (%d)\n",
+ "(%d, %pM) to hardware (%d)\n",
wiphy_name(key->local->hw.wiphy),
- key->conf.keyidx, print_mac(mac, addr), ret);
+ key->conf.keyidx, addr, ret);
}
static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
{
const u8 *addr;
int ret;
- DECLARE_MAC_BUF(mac);
assert_key_lock();
might_sleep();
@@ -186,9 +184,9 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
if (ret)
printk(KERN_ERR "mac80211-%s: failed to remove key "
- "(%d, %s) from hardware (%d)\n",
+ "(%d, %pM) from hardware (%d)\n",
wiphy_name(key->local->hw.wiphy),
- key->conf.keyidx, print_mac(mac, addr), ret);
+ key->conf.keyidx, addr, ret);
spin_lock(&todo_lock);
key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index ae62ad40ad63..4a61fd0b8d79 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -41,6 +41,8 @@
*/
struct ieee80211_tx_status_rtap_hdr {
struct ieee80211_radiotap_header hdr;
+ u8 rate;
+ u8 padding_for_rate;
__le16 tx_flags;
u8 data_retries;
} __attribute__ ((packed));
@@ -169,19 +171,13 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
conf.changed = changed;
if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ sdata->vif.type == NL80211_IFTYPE_ADHOC)
conf.bssid = sdata->u.sta.bssid;
- conf.ssid = sdata->u.sta.ssid;
- conf.ssid_len = sdata->u.sta.ssid_len;
- } else if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ else if (sdata->vif.type == NL80211_IFTYPE_AP)
conf.bssid = sdata->dev->dev_addr;
- conf.ssid = sdata->u.ap.ssid;
- conf.ssid_len = sdata->u.ap.ssid_len;
- } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ else if (ieee80211_vif_is_mesh(&sdata->vif)) {
u8 zero[ETH_ALEN] = { 0 };
conf.bssid = zero;
- conf.ssid = zero;
- conf.ssid_len = 0;
} else {
WARN_ON(1);
return -EINVAL;
@@ -190,136 +186,73 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID)))
return -EINVAL;
- if (WARN_ON(!conf.ssid && (changed & IEEE80211_IFCC_SSID)))
- return -EINVAL;
-
return local->ops->config_interface(local_to_hw(local),
&sdata->vif, &conf);
}
-int ieee80211_hw_config(struct ieee80211_local *local)
+int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
{
struct ieee80211_channel *chan;
int ret = 0;
+ int power;
+ enum nl80211_channel_type channel_type;
+
+ might_sleep();
- if (local->sw_scanning)
+ if (local->sw_scanning) {
chan = local->scan_channel;
- else
+ channel_type = NL80211_CHAN_NO_HT;
+ } else {
chan = local->oper_channel;
+ channel_type = local->oper_channel_type;
+ }
- local->hw.conf.channel = chan;
+ if (chan != local->hw.conf.channel ||
+ channel_type != local->hw.conf.ht.channel_type) {
+ local->hw.conf.channel = chan;
+ local->hw.conf.ht.channel_type = channel_type;
+ switch (channel_type) {
+ case NL80211_CHAN_NO_HT:
+ local->hw.conf.ht.enabled = false;
+ break;
+ case NL80211_CHAN_HT20:
+ case NL80211_CHAN_HT40MINUS:
+ case NL80211_CHAN_HT40PLUS:
+ local->hw.conf.ht.enabled = true;
+ break;
+ }
+ changed |= IEEE80211_CONF_CHANGE_CHANNEL;
+ }
if (!local->hw.conf.power_level)
- local->hw.conf.power_level = chan->max_power;
+ power = chan->max_power;
else
- local->hw.conf.power_level = min(chan->max_power,
- local->hw.conf.power_level);
-
- local->hw.conf.max_antenna_gain = chan->max_antenna_gain;
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: HW CONFIG: freq=%d\n",
- wiphy_name(local->hw.wiphy), chan->center_freq);
-#endif
-
- if (local->open_count)
- ret = local->ops->config(local_to_hw(local), &local->hw.conf);
-
- return ret;
-}
-
-/**
- * ieee80211_handle_ht should be used only after legacy configuration
- * has been determined namely band, as ht configuration depends upon
- * the hardware's HT abilities for a _specific_ band.
- */
-u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
- struct ieee80211_ht_info *req_ht_cap,
- struct ieee80211_ht_bss_info *req_bss_cap)
-{
- struct ieee80211_conf *conf = &local->hw.conf;
- struct ieee80211_supported_band *sband;
- struct ieee80211_ht_info ht_conf;
- struct ieee80211_ht_bss_info ht_bss_conf;
- u32 changed = 0;
- int i;
- u8 max_tx_streams = IEEE80211_HT_CAP_MAX_STREAMS;
- u8 tx_mcs_set_cap;
-
- sband = local->hw.wiphy->bands[conf->channel->band];
-
- memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info));
- memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info));
-
- /* HT is not supported */
- if (!sband->ht_info.ht_supported) {
- conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
- goto out;
+ power = min(chan->max_power, local->hw.conf.power_level);
+ if (local->hw.conf.power_level != power) {
+ changed |= IEEE80211_CONF_CHANGE_POWER;
+ local->hw.conf.power_level = power;
}
- /* disable HT */
- if (!enable_ht) {
- if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)
- changed |= BSS_CHANGED_HT;
- conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
- conf->ht_conf.ht_supported = 0;
- goto out;
+ if (changed && local->open_count) {
+ ret = local->ops->config(local_to_hw(local), changed);
+ /*
+ * Goal:
+ * HW reconfiguration should never fail, the driver has told
+ * us what it can support so it should live up to that promise.
+ *
+ * Current status:
+ * rfkill is not integrated with mac80211 and a
+ * configuration command can thus fail if hardware rfkill
+ * is enabled
+ *
+ * FIXME: integrate rfkill with mac80211 and then add this
+ * WARN_ON() back
+ *
+ */
+ /* WARN_ON(ret); */
}
-
- if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE))
- changed |= BSS_CHANGED_HT;
-
- conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE;
- ht_conf.ht_supported = 1;
-
- ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap;
- ht_conf.cap &= ~(IEEE80211_HT_CAP_SM_PS);
- ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_SM_PS;
- ht_bss_conf.primary_channel = req_bss_cap->primary_channel;
- ht_bss_conf.bss_cap = req_bss_cap->bss_cap;
- ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode;
-
- ht_conf.ampdu_factor = req_ht_cap->ampdu_factor;
- ht_conf.ampdu_density = req_ht_cap->ampdu_density;
-
- /* Bits 96-100 */
- tx_mcs_set_cap = sband->ht_info.supp_mcs_set[12];
-
- /* configure suppoerted Tx MCS according to requested MCS
- * (based in most cases on Rx capabilities of peer) and self
- * Tx MCS capabilities (as defined by low level driver HW
- * Tx capabilities) */
- if (!(tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_DEFINED))
- goto check_changed;
-
- /* Counting from 0 therfore + 1 */
- if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_RX_DIFF)
- max_tx_streams = ((tx_mcs_set_cap &
- IEEE80211_HT_CAP_MCS_TX_STREAMS) >> 2) + 1;
-
- for (i = 0; i < max_tx_streams; i++)
- ht_conf.supp_mcs_set[i] =
- sband->ht_info.supp_mcs_set[i] &
- req_ht_cap->supp_mcs_set[i];
-
- if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_UEQM)
- for (i = IEEE80211_SUPP_MCS_SET_UEQM;
- i < IEEE80211_SUPP_MCS_SET_LEN; i++)
- ht_conf.supp_mcs_set[i] =
- sband->ht_info.supp_mcs_set[i] &
- req_ht_cap->supp_mcs_set[i];
-
-check_changed:
- /* if bss configuration changed store the new one */
- if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) ||
- memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) {
- changed |= BSS_CHANGED_HT;
- memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf));
- memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf));
- }
-out:
- return changed;
+ return ret;
}
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
@@ -336,15 +269,18 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
if (local->ops->bss_info_changed)
local->ops->bss_info_changed(local_to_hw(local),
&sdata->vif,
- &sdata->bss_conf,
+ &sdata->vif.bss_conf,
changed);
}
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
{
- sdata->bss_conf.use_cts_prot = 0;
- sdata->bss_conf.use_short_preamble = 0;
- return BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE;
+ sdata->vif.bss_conf.use_cts_prot = false;
+ sdata->vif.bss_conf.use_short_preamble = false;
+ sdata->vif.bss_conf.use_short_slot = false;
+ return BSS_CHANGED_ERP_CTS_PROT |
+ BSS_CHANGED_ERP_PREAMBLE |
+ BSS_CHANGED_ERP_SLOT;
}
void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
@@ -405,7 +341,8 @@ static void ieee80211_tasklet_handler(unsigned long data)
dev_kfree_skb(skb);
break ;
default:
- WARN_ON(1);
+ WARN(1, "mac80211: Packet is of unknown type %d\n",
+ skb->pkt_type);
dev_kfree_skb(skb);
break;
}
@@ -466,8 +403,6 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
struct sta_info *sta,
struct sk_buff *skb)
{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
sta->tx_filtered_count++;
/*
@@ -514,10 +449,9 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
return;
}
- if (!test_sta_flags(sta, WLAN_STA_PS) &&
- !(info->flags & IEEE80211_TX_CTL_REQUEUE)) {
+ if (!test_sta_flags(sta, WLAN_STA_PS) && !skb->requeue) {
/* Software retry the packet once */
- info->flags |= IEEE80211_TX_CTL_REQUEUE;
+ skb->requeue = 1;
ieee80211_remove_tx_extra(local, sta->key, skb);
dev_queue_xmit(skb);
return;
@@ -547,13 +481,28 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
struct ieee80211_sub_if_data *sdata;
struct net_device *prev_dev = NULL;
struct sta_info *sta;
+ int retry_count = -1, i;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ /* the HW cannot have attempted that rate */
+ if (i >= hw->max_rates) {
+ info->status.rates[i].idx = -1;
+ info->status.rates[i].count = 0;
+ }
+
+ retry_count += info->status.rates[i].count;
+ }
+ if (retry_count < 0)
+ retry_count = 0;
rcu_read_lock();
+ sband = local->hw.wiphy->bands[info->band];
+
sta = sta_info_get(local, hdr->addr1);
if (sta) {
- if (info->status.excessive_retries &&
+ if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
test_sta_flags(sta, WLAN_STA_PS)) {
/*
* The STA is in power save mode, so assume
@@ -584,12 +533,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
rcu_read_unlock();
return;
} else {
- if (info->status.excessive_retries)
+ if (!(info->flags & IEEE80211_TX_STAT_ACK))
sta->tx_retry_failed++;
- sta->tx_retry_count += info->status.retry_count;
+ sta->tx_retry_count += retry_count;
}
- sband = local->hw.wiphy->bands[info->band];
rate_control_tx_status(local, sband, sta, skb);
}
@@ -610,9 +558,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
local->dot11TransmittedFrameCount++;
if (is_multicast_ether_addr(hdr->addr1))
local->dot11MulticastTransmittedFrameCount++;
- if (info->status.retry_count > 0)
+ if (retry_count > 0)
local->dot11RetryCount++;
- if (info->status.retry_count > 1)
+ if (retry_count > 1)
local->dot11MultipleRetryCount++;
}
@@ -656,19 +604,30 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
rthdr->hdr.it_present =
cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) |
- (1 << IEEE80211_RADIOTAP_DATA_RETRIES));
+ (1 << IEEE80211_RADIOTAP_DATA_RETRIES) |
+ (1 << IEEE80211_RADIOTAP_RATE));
if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
!is_multicast_ether_addr(hdr->addr1))
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL);
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) &&
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT))
+ /*
+ * XXX: Once radiotap gets the bitmap reset thing the vendor
+ * extensions proposal contains, we can actually report
+ * the whole set of tries we did.
+ */
+ if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
+ (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT))
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS);
- else if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
+ else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS);
+ if (info->status.rates[0].idx >= 0 &&
+ !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS))
+ rthdr->rate = sband->bitrates[
+ info->status.rates[0].idx].bitrate / 5;
- rthdr->data_retries = info->status.retry_count;
+ /* for now report the total retry_count */
+ rthdr->data_retries = retry_count;
/* XXX: is this sufficient for BPF? */
skb_set_mac_header(skb, 0);
@@ -753,20 +712,30 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
BUG_ON(!ops->configure_filter);
local->ops = ops;
- local->hw.queues = 1; /* default */
-
+ /* set up some defaults */
+ local->hw.queues = 1;
+ local->hw.max_rates = 1;
local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
- local->short_retry_limit = 7;
- local->long_retry_limit = 4;
- local->hw.conf.radio_enabled = 1;
+ local->hw.conf.long_frame_max_tx_count = 4;
+ local->hw.conf.short_frame_max_tx_count = 7;
+ local->hw.conf.radio_enabled = true;
INIT_LIST_HEAD(&local->interfaces);
spin_lock_init(&local->key_lock);
+ spin_lock_init(&local->queue_stop_reason_lock);
+
INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
+ INIT_WORK(&local->dynamic_ps_enable_work,
+ ieee80211_dynamic_ps_enable_work);
+ INIT_WORK(&local->dynamic_ps_disable_work,
+ ieee80211_dynamic_ps_disable_work);
+ setup_timer(&local->dynamic_ps_timer,
+ ieee80211_dynamic_ps_timer, (unsigned long) local);
+
sta_info_init(local);
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
@@ -788,7 +757,6 @@ EXPORT_SYMBOL(ieee80211_alloc_hw);
int ieee80211_register_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
- const char *name;
int result;
enum ieee80211_band band;
struct net_device *mdev;
@@ -853,8 +821,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
mdev->header_ops = &ieee80211_header_ops;
mdev->set_multicast_list = ieee80211_master_set_multicast_list;
- name = wiphy_dev(local->hw.wiphy)->driver->name;
- local->hw.workqueue = create_freezeable_workqueue(name);
+ local->hw.workqueue =
+ create_freezeable_workqueue(wiphy_name(local->hw.wiphy));
if (!local->hw.workqueue) {
result = -ENOMEM;
goto fail_workqueue;
@@ -921,12 +889,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->mdev->select_queue = ieee80211_select_queue;
- /* add one default STA interface */
- result = ieee80211_if_add(local, "wlan%d", NULL,
- NL80211_IFTYPE_STATION, NULL);
- if (result)
- printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
- wiphy_name(local->hw.wiphy));
+ /* add one default STA interface if supported */
+ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
+ result = ieee80211_if_add(local, "wlan%d", NULL,
+ NL80211_IFTYPE_STATION, NULL);
+ if (result)
+ printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
+ wiphy_name(local->hw.wiphy));
+ }
rtnl_unlock();
@@ -1013,7 +983,7 @@ static int __init ieee80211_init(void)
BUILD_BUG_ON(sizeof(struct ieee80211_tx_info) > sizeof(skb->cb));
BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, driver_data) +
- IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb));
+ IEEE80211_TX_INFO_DRIVER_DATA_SIZE > sizeof(skb->cb));
ret = rc80211_minstrel_init();
if (ret)
@@ -1023,6 +993,10 @@ static int __init ieee80211_init(void)
if (ret)
return ret;
+ ret = rc80211_multirate_pid_init();
+ if (ret)
+ return ret;
+
ieee80211_debugfs_netdev_init();
return 0;
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 8013277924f2..82f568e94365 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -238,7 +238,7 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
pos = skb_put(skb, 21);
*pos++ = WLAN_EID_MESH_CONFIG;
- *pos++ = MESH_CFG_LEN;
+ *pos++ = IEEE80211_MESH_CONFIG_LEN;
/* Version */
*pos++ = 1;
@@ -473,7 +473,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
size_t len,
struct ieee80211_rx_status *rx_status)
{
- struct ieee80211_local *local= sdata->local;
+ struct ieee80211_local *local = sdata->local;
struct ieee802_11_elems elems;
struct ieee80211_channel *channel;
u64 supp_rates = 0;
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index e10471c6ba42..c197ab545e54 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -145,9 +145,6 @@ struct mesh_rmc {
};
-/* Mesh IEs constants */
-#define MESH_CFG_LEN 19
-
/*
* MESH_CFG_COMP_LEN Includes:
* - Active path selection protocol ID.
@@ -157,7 +154,7 @@ struct mesh_rmc {
* Does not include mesh capabilities, which may vary across nodes in the same
* mesh
*/
-#define MESH_CFG_CMP_LEN 17
+#define MESH_CFG_CMP_LEN (IEEE80211_MESH_CONFIG_LEN - 2)
/* Default values, timeouts in ms */
#define MESH_TTL 5
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 501c7831adb4..71fe60961230 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -218,12 +218,16 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
if (sta->fail_avg >= 100)
return MAX_METRIC;
+
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)
+ return MAX_METRIC;
+
err = (sta->fail_avg << ARITH_SHIFT) / 100;
/* bitrate is in units of 100 Kbps, while we need rate in units of
* 1Mbps. This will be corrected on tx_time computation.
*/
- rate = sband->bitrates[sta->last_txrate_idx].bitrate;
+ rate = sband->bitrates[sta->last_tx_rate.idx].bitrate;
tx_time = (device_constant + 10 * test_frame_len / rate);
estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err));
result = (tx_time * estimated_retx) >> (2 * ARITH_SHIFT) ;
@@ -759,7 +763,6 @@ enddiscovery:
*
* @skb: 802.11 frame to be sent
* @sdata: network subif the frame will be sent through
- * @fwd_frame: true if this frame was originally from a different host
*
* Returns: 0 if the next hop was found. Nonzero otherwise. If no next hop is
* found, the function will start a path discovery and queue the frame so it is
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index faac101c0f85..1159bdb4119c 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -107,6 +107,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
sta->flags = WLAN_STA_AUTHORIZED;
sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
+ rate_control_rate_init(sta);
return sta;
}
@@ -257,9 +258,6 @@ static void mesh_plink_timer(unsigned long data)
struct sta_info *sta;
__le16 llid, plid, reason;
struct ieee80211_sub_if_data *sdata;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
/*
* This STA is valid because sta_info_destroy() will
@@ -274,8 +272,8 @@ static void mesh_plink_timer(unsigned long data)
spin_unlock_bh(&sta->lock);
return;
}
- mpl_dbg("Mesh plink timer for %s fired on state %d\n",
- print_mac(mac, sta->sta.addr), sta->plink_state);
+ mpl_dbg("Mesh plink timer for %pM fired on state %d\n",
+ sta->sta.addr, sta->plink_state);
reason = 0;
llid = sta->llid;
plid = sta->plid;
@@ -287,9 +285,9 @@ static void mesh_plink_timer(unsigned long data)
/* retry timer */
if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
u32 rand;
- mpl_dbg("Mesh plink for %s (retry, timeout): %d %d\n",
- print_mac(mac, sta->sta.addr),
- sta->plink_retries, sta->plink_timeout);
+ mpl_dbg("Mesh plink for %pM (retry, timeout): %d %d\n",
+ sta->sta.addr, sta->plink_retries,
+ sta->plink_timeout);
get_random_bytes(&rand, sizeof(u32));
sta->plink_timeout = sta->plink_timeout +
rand % sta->plink_timeout;
@@ -337,9 +335,6 @@ int mesh_plink_open(struct sta_info *sta)
{
__le16 llid;
struct ieee80211_sub_if_data *sdata = sta->sdata;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
spin_lock_bh(&sta->lock);
get_random_bytes(&llid, 2);
@@ -351,8 +346,8 @@ int mesh_plink_open(struct sta_info *sta)
sta->plink_state = PLINK_OPN_SNT;
mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
spin_unlock_bh(&sta->lock);
- mpl_dbg("Mesh plink: starting establishment with %s\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink: starting establishment with %pM\n",
+ sta->sta.addr);
return mesh_plink_frame_tx(sdata, PLINK_OPEN,
sta->sta.addr, llid, 0, 0);
@@ -360,10 +355,6 @@ int mesh_plink_open(struct sta_info *sta)
void mesh_plink_block(struct sta_info *sta)
{
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
-
spin_lock_bh(&sta->lock);
__mesh_plink_deactivate(sta);
sta->plink_state = PLINK_BLOCKED;
@@ -374,12 +365,8 @@ int mesh_plink_close(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
__le16 llid, plid, reason;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
- mpl_dbg("Mesh plink: closing link with %s\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink: closing link with %pM\n", sta->sta.addr);
spin_lock_bh(&sta->lock);
sta->reason = cpu_to_le16(MESH_LINK_CANCELLED);
reason = sta->reason;
@@ -417,9 +404,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
u8 ie_len;
u8 *baseaddr;
__le16 plid, llid, reason;
-#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
- DECLARE_MAC_BUF(mac);
-#endif
/* need action_code, aux */
if (len < IEEE80211_MIN_ACTION_SIZE + 3)
@@ -557,10 +541,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
}
}
- mpl_dbg("Mesh plink (peer, state, llid, plid, event): %s %d %d %d %d\n",
- print_mac(mac, mgmt->sa), sta->plink_state,
- le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
- event);
+ mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %d %d %d %d\n",
+ mgmt->sa, sta->plink_state,
+ le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
+ event);
reason = 0;
switch (sta->plink_state) {
/* spin_unlock as soon as state is updated at each case */
@@ -660,8 +644,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
spin_unlock_bh(&sta->lock);
- mpl_dbg("Mesh plink with %s ESTABLISHED\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
+ sta->sta.addr);
break;
default:
spin_unlock_bh(&sta->lock);
@@ -693,8 +677,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->plink_state = PLINK_ESTAB;
mesh_plink_inc_estab_count(sdata);
spin_unlock_bh(&sta->lock);
- mpl_dbg("Mesh plink with %s ESTABLISHED\n",
- print_mac(mac, sta->sta.addr));
+ mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
+ sta->sta.addr);
mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
plid, 0);
break;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 409bb7716236..88a26198747f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -14,7 +14,6 @@
#include <linux/delay.h>
#include <linux/if_ether.h>
#include <linux/skbuff.h>
-#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <linux/random.h>
@@ -236,7 +235,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies, *ht_add_ie;
+ u8 *pos, *ies, *ht_ie;
int i, len, count, rates_len, supp_rates_len;
u16 capab;
struct ieee80211_bss *bss;
@@ -310,7 +309,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ASSOC_REQ);
mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
- mgmt->u.reassoc_req.listen_interval =
+ mgmt->u.assoc_req.listen_interval =
cpu_to_le16(local->hw.conf.listen_interval);
}
@@ -393,24 +392,25 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
/* wmm support is a must to HT */
if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED) &&
- sband->ht_info.ht_supported &&
- (ht_add_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_EXTRA_INFO))) {
- struct ieee80211_ht_addt_info *ht_add_info =
- (struct ieee80211_ht_addt_info *)ht_add_ie;
- u16 cap = sband->ht_info.cap;
+ sband->ht_cap.ht_supported &&
+ (ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_INFORMATION)) &&
+ ht_ie[1] >= sizeof(struct ieee80211_ht_info)) {
+ struct ieee80211_ht_info *ht_info =
+ (struct ieee80211_ht_info *)(ht_ie + 2);
+ u16 cap = sband->ht_cap.cap;
__le16 tmp;
u32 flags = local->hw.conf.channel->flags;
- switch (ht_add_info->ht_param & IEEE80211_HT_IE_CHA_SEC_OFFSET) {
- case IEEE80211_HT_IE_CHA_SEC_ABOVE:
+ switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) {
- cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
cap &= ~IEEE80211_HT_CAP_SGI_40;
}
break;
- case IEEE80211_HT_IE_CHA_SEC_BELOW:
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
if (flags & IEEE80211_CHAN_NO_FAT_BELOW) {
- cap &= ~IEEE80211_HT_CAP_SUP_WIDTH;
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
cap &= ~IEEE80211_HT_CAP_SGI_40;
}
break;
@@ -424,9 +424,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
memcpy(pos, &tmp, sizeof(u16));
pos += sizeof(u16);
/* TODO: needs a define here for << 2 */
- *pos++ = sband->ht_info.ampdu_factor |
- (sband->ht_info.ampdu_density << 2);
- memcpy(pos, sband->ht_info.supp_mcs_set, 16);
+ *pos++ = sband->ht_cap.ampdu_factor |
+ (sband->ht_cap.ampdu_density << 2);
+ memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
}
kfree(ifsta->assocreq_ies);
@@ -439,7 +439,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
}
-static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
u16 stype, u16 reason)
{
struct ieee80211_local *local = sdata->local;
@@ -467,6 +467,7 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_tx_skb(sdata, skb, 0);
}
+EXPORT_SYMBOL(ieee80211_send_deauth_disassoc);
/* MLME */
static void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
@@ -568,25 +569,35 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
}
}
-static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata,
- bool use_protection,
- bool use_short_preamble)
+static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
+ u16 capab, bool erp_valid, u8 erp)
{
- struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf;
+ struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- DECLARE_MAC_BUF(mac);
#endif
u32 changed = 0;
+ bool use_protection;
+ bool use_short_preamble;
+ bool use_short_slot;
+
+ if (erp_valid) {
+ use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0;
+ use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0;
+ } else {
+ use_protection = false;
+ use_short_preamble = !!(capab & WLAN_CAPABILITY_SHORT_PREAMBLE);
+ }
+
+ use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
if (use_protection != bss_conf->use_cts_prot) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: CTS protection %s (BSSID="
- "%s)\n",
+ printk(KERN_DEBUG "%s: CTS protection %s (BSSID=%pM)\n",
sdata->dev->name,
use_protection ? "enabled" : "disabled",
- print_mac(mac, ifsta->bssid));
+ ifsta->bssid);
}
#endif
bss_conf->use_cts_prot = use_protection;
@@ -597,40 +608,28 @@ static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: switched to %s barker preamble"
- " (BSSID=%s)\n",
+ " (BSSID=%pM)\n",
sdata->dev->name,
use_short_preamble ? "short" : "long",
- print_mac(mac, ifsta->bssid));
+ ifsta->bssid);
}
#endif
bss_conf->use_short_preamble = use_short_preamble;
changed |= BSS_CHANGED_ERP_PREAMBLE;
}
- return changed;
-}
-
-static u32 ieee80211_handle_erp_ie(struct ieee80211_sub_if_data *sdata,
- u8 erp_value)
-{
- bool use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0;
- bool use_short_preamble = (erp_value & WLAN_ERP_BARKER_PREAMBLE) == 0;
-
- return ieee80211_handle_protect_preamb(sdata,
- use_protection, use_short_preamble);
-}
-
-static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_bss *bss)
-{
- u32 changed = 0;
-
- if (bss->has_erp_value)
- changed |= ieee80211_handle_erp_ie(sdata, bss->erp_value);
- else {
- u16 capab = bss->capability;
- changed |= ieee80211_handle_protect_preamb(sdata, false,
- (capab & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0);
+ if (use_short_slot != bss_conf->use_short_slot) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: switched to %s slot time"
+ " (BSSID=%pM)\n",
+ sdata->dev->name,
+ use_short_slot ? "short" : "long",
+ ifsta->bssid);
+ }
+#endif
+ bss_conf->use_short_slot = use_short_slot;
+ changed |= BSS_CHANGED_ERP_SLOT;
}
return changed;
@@ -701,14 +700,15 @@ static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata,
static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+ struct ieee80211_if_sta *ifsta,
+ u32 bss_info_changed)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_conf *conf = &local_to_hw(local)->conf;
- u32 changed = BSS_CHANGED_ASSOC;
struct ieee80211_bss *bss;
+ bss_info_changed |= BSS_CHANGED_ASSOC;
ifsta->flags |= IEEE80211_STA_ASSOCIATED;
if (sdata->vif.type != NL80211_IFTYPE_STATION)
@@ -719,22 +719,16 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ifsta->ssid, ifsta->ssid_len);
if (bss) {
/* set timing information */
- sdata->bss_conf.beacon_int = bss->beacon_int;
- sdata->bss_conf.timestamp = bss->timestamp;
- sdata->bss_conf.dtim_period = bss->dtim_period;
+ sdata->vif.bss_conf.beacon_int = bss->beacon_int;
+ sdata->vif.bss_conf.timestamp = bss->timestamp;
+ sdata->vif.bss_conf.dtim_period = bss->dtim_period;
- changed |= ieee80211_handle_bss_capability(sdata, bss);
+ bss_info_changed |= ieee80211_handle_bss_capability(sdata,
+ bss->capability, bss->has_erp_value, bss->erp_value);
ieee80211_rx_bss_put(local, bss);
}
- if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
- changed |= BSS_CHANGED_HT;
- sdata->bss_conf.assoc_ht = 1;
- sdata->bss_conf.ht_conf = &conf->ht_conf;
- sdata->bss_conf.ht_bss_conf = &conf->ht_bss_conf;
- }
-
ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN);
ieee80211_sta_send_associnfo(sdata, ifsta);
@@ -742,14 +736,25 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ifsta->last_probe = jiffies;
ieee80211_led_assoc(local, 1);
- sdata->bss_conf.assoc = 1;
+ sdata->vif.bss_conf.assoc = 1;
/*
* For now just always ask the driver to update the basic rateset
* when we have associated, we aren't checking whether it actually
* changed or not.
*/
- changed |= BSS_CHANGED_BASIC_RATES;
- ieee80211_bss_info_change_notify(sdata, changed);
+ bss_info_changed |= BSS_CHANGED_BASIC_RATES;
+ ieee80211_bss_info_change_notify(sdata, bss_info_changed);
+
+ if (local->powersave) {
+ if (local->dynamic_ps_timeout > 0)
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ else {
+ conf->flags |= IEEE80211_CONF_PS;
+ ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_PS);
+ }
+ }
netif_tx_start_all_queues(sdata->dev);
netif_carrier_on(sdata->dev);
@@ -760,18 +765,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- DECLARE_MAC_BUF(mac);
-
ifsta->direct_probe_tries++;
if (ifsta->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
- printk(KERN_DEBUG "%s: direct probe to AP %s timed out\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
+ sdata->dev->name, ifsta->bssid);
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata, ifsta);
return;
}
- printk(KERN_DEBUG "%s: direct probe to AP %s try %d\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid),
+ printk(KERN_DEBUG "%s: direct probe to AP %pM try %d\n",
+ sdata->dev->name, ifsta->bssid,
ifsta->direct_probe_tries);
ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
@@ -791,33 +795,36 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- DECLARE_MAC_BUF(mac);
-
ifsta->auth_tries++;
if (ifsta->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
- printk(KERN_DEBUG "%s: authentication with AP %s"
+ printk(KERN_DEBUG "%s: authentication with AP %pM"
" timed out\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, ifsta->bssid);
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata, ifsta);
return;
}
ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
- printk(KERN_DEBUG "%s: authenticate with AP %s\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: authenticate with AP %pM\n",
+ sdata->dev->name, ifsta->bssid);
ieee80211_send_auth(sdata, ifsta, 1, NULL, 0, 0);
mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
}
+/*
+ * The disassoc 'reason' argument can be either our own reason
+ * if self disconnected or a reason code from the AP.
+ */
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta, bool deauth,
bool self_disconnected, u16 reason)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- u32 changed = BSS_CHANGED_ASSOC;
+ u32 changed = 0, config_changed = 0;
rcu_read_lock();
@@ -851,21 +858,40 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
changed |= ieee80211_reset_erp_info(sdata);
- if (sdata->bss_conf.assoc_ht)
- changed |= BSS_CHANGED_HT;
-
- sdata->bss_conf.assoc_ht = 0;
- sdata->bss_conf.ht_conf = NULL;
- sdata->bss_conf.ht_bss_conf = NULL;
-
ieee80211_led_assoc(local, 0);
- sdata->bss_conf.assoc = 0;
+ changed |= BSS_CHANGED_ASSOC;
+ sdata->vif.bss_conf.assoc = false;
ieee80211_sta_send_apinfo(sdata, ifsta);
- if (self_disconnected)
+ if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT)
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ rcu_read_unlock();
+
+ local->hw.conf.ht.enabled = false;
+ local->oper_channel_type = NL80211_CHAN_NO_HT;
+ config_changed |= IEEE80211_CONF_CHANGE_HT;
+
+ del_timer_sync(&local->dynamic_ps_timer);
+ cancel_work_sync(&local->dynamic_ps_enable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ config_changed |= IEEE80211_CONF_CHANGE_PS;
+ }
+
+ ieee80211_hw_config(local, config_changed);
+ ieee80211_bss_info_change_notify(sdata, changed);
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, ifsta->bssid);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+
sta_info_unlink(&sta);
rcu_read_unlock();
@@ -914,20 +940,19 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata,
static void ieee80211_associate(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta)
{
- DECLARE_MAC_BUF(mac);
-
ifsta->assoc_tries++;
if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
- printk(KERN_DEBUG "%s: association with AP %s"
+ printk(KERN_DEBUG "%s: association with AP %pM"
" timed out\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, ifsta->bssid);
ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata, ifsta);
return;
}
ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
- printk(KERN_DEBUG "%s: associate with AP %s\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: associate with AP %pM\n",
+ sdata->dev->name, ifsta->bssid);
if (ieee80211_privacy_mismatch(sdata, ifsta)) {
printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
"mixed-cell disabled - abort association\n", sdata->dev->name);
@@ -947,7 +972,6 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int disassoc;
- DECLARE_MAC_BUF(mac);
/* TODO: start monitoring current AP signal quality and number of
* missed beacons. Scan other channels every now and then and search
@@ -960,8 +984,8 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
- printk(KERN_DEBUG "%s: No STA entry for own AP %s\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n",
+ sdata->dev->name, ifsta->bssid);
disassoc = 1;
} else {
disassoc = 0;
@@ -969,9 +993,9 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
if (ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) {
printk(KERN_DEBUG "%s: No ProbeResp from "
- "current AP %s - assume out of "
+ "current AP %pM - assume out of "
"range\n",
- sdata->dev->name, print_mac(mac, ifsta->bssid));
+ sdata->dev->name, ifsta->bssid);
disassoc = 1;
} else
ieee80211_send_probe_req(sdata, ifsta->bssid,
@@ -1032,7 +1056,6 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
size_t len)
{
u16 auth_alg, auth_transaction, status_code;
- DECLARE_MAC_BUF(mac);
if (ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE &&
sdata->vif.type != NL80211_IFTYPE_ADHOC)
@@ -1125,7 +1148,6 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
size_t len)
{
u16 reason_code;
- DECLARE_MAC_BUF(mac);
if (len < 24 + 2)
return;
@@ -1136,7 +1158,8 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
if (ifsta->flags & IEEE80211_STA_AUTHENTICATED)
- printk(KERN_DEBUG "%s: deauthenticated\n", sdata->dev->name);
+ printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n",
+ sdata->dev->name, reason_code);
if (ifsta->state == IEEE80211_STA_MLME_AUTHENTICATE ||
ifsta->state == IEEE80211_STA_MLME_ASSOCIATE ||
@@ -1157,7 +1180,6 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
size_t len)
{
u16 reason_code;
- DECLARE_MAC_BUF(mac);
if (len < 24 + 2)
return;
@@ -1168,7 +1190,8 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
if (ifsta->flags & IEEE80211_STA_ASSOCIATED)
- printk(KERN_DEBUG "%s: disassociated\n", sdata->dev->name);
+ printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
+ sdata->dev->name, reason_code);
if (ifsta->state == IEEE80211_STA_MLME_ASSOCIATED) {
ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
@@ -1176,7 +1199,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
IEEE80211_RETRY_AUTH_INTERVAL);
}
- ieee80211_set_disassoc(sdata, ifsta, false, false, 0);
+ ieee80211_set_disassoc(sdata, ifsta, false, false, reason_code);
}
@@ -1192,11 +1215,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
u64 rates, basic_rates;
u16 capab_info, status_code, aid;
struct ieee802_11_elems elems;
- struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf;
+ struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
u8 *pos;
+ u32 changed = 0;
int i, j;
- DECLARE_MAC_BUF(mac);
- bool have_higher_than_11mbit = false;
+ bool have_higher_than_11mbit = false, newsta = false;
+ u16 ap_ht_cap_flags;
/* AssocResp and ReassocResp have identical structure, so process both
* of them in this function. */
@@ -1214,9 +1238,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
- printk(KERN_DEBUG "%s: RX %sssocResp from %s (capab=0x%x "
+ printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
"status=%d aid=%d)\n",
- sdata->dev->name, reassoc ? "Rea" : "A", print_mac(mac, mgmt->sa),
+ sdata->dev->name, reassoc ? "Rea" : "A", mgmt->sa,
capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
if (status_code != WLAN_STATUS_SUCCESS) {
@@ -1259,7 +1283,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
sta = sta_info_get(local, ifsta->bssid);
if (!sta) {
struct ieee80211_bss *bss;
- int err;
+
+ newsta = true;
sta = sta_info_alloc(sdata, ifsta->bssid, GFP_ATOMIC);
if (!sta) {
@@ -1278,13 +1303,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
ieee80211_rx_bss_put(local, bss);
}
- err = sta_info_insert(sta);
- if (err) {
- printk(KERN_DEBUG "%s: failed to insert STA entry for"
- " the AP (error %d)\n", sdata->dev->name, err);
- rcu_read_unlock();
- return;
- }
/* update new sta with its last rx activity */
sta->last_rx = jiffies;
}
@@ -1308,34 +1326,40 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
for (i = 0; i < elems.supp_rates_len; i++) {
int rate = (elems.supp_rates[i] & 0x7f) * 5;
+ bool is_basic = !!(elems.supp_rates[i] & 0x80);
if (rate > 110)
have_higher_than_11mbit = true;
for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate)
+ if (sband->bitrates[j].bitrate == rate) {
rates |= BIT(j);
- if (elems.supp_rates[i] & 0x80)
- basic_rates |= BIT(j);
+ if (is_basic)
+ basic_rates |= BIT(j);
+ break;
+ }
}
}
for (i = 0; i < elems.ext_supp_rates_len; i++) {
int rate = (elems.ext_supp_rates[i] & 0x7f) * 5;
+ bool is_basic = !!(elems.supp_rates[i] & 0x80);
if (rate > 110)
have_higher_than_11mbit = true;
for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate)
+ if (sband->bitrates[j].bitrate == rate) {
rates |= BIT(j);
- if (elems.ext_supp_rates[i] & 0x80)
- basic_rates |= BIT(j);
+ if (is_basic)
+ basic_rates |= BIT(j);
+ break;
+ }
}
}
sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
- sdata->bss_conf.basic_rates = basic_rates;
+ sdata->vif.bss_conf.basic_rates = basic_rates;
/* cf. IEEE 802.11 9.2.12 */
if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
@@ -1344,31 +1368,43 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
- if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
- (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
- struct ieee80211_ht_bss_info bss_info;
- ieee80211_ht_cap_ie_to_ht_info(
- elems.ht_cap_elem, &sta->sta.ht_info);
- ieee80211_ht_addt_info_ie_to_ht_bss_info(
- elems.ht_info_elem, &bss_info);
- ieee80211_handle_ht(local, 1, &sta->sta.ht_info, &bss_info);
- }
+ if (elems.ht_cap_elem)
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ elems.ht_cap_elem, &sta->sta.ht_cap);
+
+ ap_ht_cap_flags = sta->sta.ht_cap.cap;
rate_control_rate_init(sta);
- if (elems.wmm_param) {
+ if (elems.wmm_param)
set_sta_flags(sta, WLAN_STA_WME);
- rcu_read_unlock();
+
+ if (newsta) {
+ int err = sta_info_insert(sta);
+ if (err) {
+ printk(KERN_DEBUG "%s: failed to insert STA entry for"
+ " the AP (error %d)\n", sdata->dev->name, err);
+ rcu_read_unlock();
+ return;
+ }
+ }
+
+ rcu_read_unlock();
+
+ if (elems.wmm_param)
ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
elems.wmm_param_len);
- } else
- rcu_read_unlock();
+
+ if (elems.ht_info_elem && elems.wmm_param &&
+ (ifsta->flags & IEEE80211_STA_WMM_ENABLED))
+ changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
+ ap_ht_cap_flags);
/* set AID and assoc capability,
* ieee80211_set_associated() will tell the driver */
bss_conf->aid = aid;
bss_conf->assoc_capability = capab_info;
- ieee80211_set_associated(sdata, ifsta);
+ ieee80211_set_associated(sdata, ifsta, changed);
ieee80211_associated(sdata, ifsta);
}
@@ -1386,6 +1422,13 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband;
union iwreq_data wrqu;
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
+ "response\n", sdata->dev->name);
+ return -ENOMEM;
+ }
+
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
/* Remove possible STA entries from other IBSS networks. */
@@ -1411,63 +1454,62 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
return res;
/* Build IBSS probe response */
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
- if (skb) {
- skb_reserve(skb, local->hw.extra_tx_headroom);
- mgmt = (struct ieee80211_mgmt *)
- skb_put(skb, 24 + sizeof(mgmt->u.beacon));
- memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
- mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
- IEEE80211_STYPE_PROBE_RESP);
- memset(mgmt->da, 0xff, ETH_ALEN);
- memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- mgmt->u.beacon.beacon_int =
- cpu_to_le16(local->hw.conf.beacon_int);
- mgmt->u.beacon.timestamp = cpu_to_le64(bss->timestamp);
- mgmt->u.beacon.capab_info = cpu_to_le16(bss->capability);
-
- pos = skb_put(skb, 2 + ifsta->ssid_len);
- *pos++ = WLAN_EID_SSID;
- *pos++ = ifsta->ssid_len;
- memcpy(pos, ifsta->ssid, ifsta->ssid_len);
-
- rates = bss->supp_rates_len;
- if (rates > 8)
- rates = 8;
- pos = skb_put(skb, 2 + rates);
- *pos++ = WLAN_EID_SUPP_RATES;
- *pos++ = rates;
- memcpy(pos, bss->supp_rates, rates);
+ skb_reserve(skb, local->hw.extra_tx_headroom);
- if (bss->band == IEEE80211_BAND_2GHZ) {
- pos = skb_put(skb, 2 + 1);
- *pos++ = WLAN_EID_DS_PARAMS;
- *pos++ = 1;
- *pos++ = ieee80211_frequency_to_channel(bss->freq);
- }
+ mgmt = (struct ieee80211_mgmt *)
+ skb_put(skb, 24 + sizeof(mgmt->u.beacon));
+ memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+ memset(mgmt->da, 0xff, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->u.beacon.beacon_int =
+ cpu_to_le16(local->hw.conf.beacon_int);
+ mgmt->u.beacon.timestamp = cpu_to_le64(bss->timestamp);
+ mgmt->u.beacon.capab_info = cpu_to_le16(bss->capability);
- pos = skb_put(skb, 2 + 2);
- *pos++ = WLAN_EID_IBSS_PARAMS;
- *pos++ = 2;
- /* FIX: set ATIM window based on scan results */
- *pos++ = 0;
- *pos++ = 0;
+ pos = skb_put(skb, 2 + ifsta->ssid_len);
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ifsta->ssid_len;
+ memcpy(pos, ifsta->ssid, ifsta->ssid_len);
- if (bss->supp_rates_len > 8) {
- rates = bss->supp_rates_len - 8;
- pos = skb_put(skb, 2 + rates);
- *pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = rates;
- memcpy(pos, &bss->supp_rates[8], rates);
- }
+ rates = bss->supp_rates_len;
+ if (rates > 8)
+ rates = 8;
+ pos = skb_put(skb, 2 + rates);
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = rates;
+ memcpy(pos, bss->supp_rates, rates);
+
+ if (bss->band == IEEE80211_BAND_2GHZ) {
+ pos = skb_put(skb, 2 + 1);
+ *pos++ = WLAN_EID_DS_PARAMS;
+ *pos++ = 1;
+ *pos++ = ieee80211_frequency_to_channel(bss->freq);
+ }
- ifsta->probe_resp = skb;
+ pos = skb_put(skb, 2 + 2);
+ *pos++ = WLAN_EID_IBSS_PARAMS;
+ *pos++ = 2;
+ /* FIX: set ATIM window based on scan results */
+ *pos++ = 0;
+ *pos++ = 0;
- ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+ if (bss->supp_rates_len > 8) {
+ rates = bss->supp_rates_len - 8;
+ pos = skb_put(skb, 2 + rates);
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
+ *pos++ = rates;
+ memcpy(pos, &bss->supp_rates[8], rates);
}
+ ifsta->probe_resp = skb;
+
+ ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+
+
rates = 0;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
for (i = 0; i < bss->supp_rates_len; i++) {
@@ -1507,8 +1549,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
u64 beacon_timestamp, rx_timestamp;
u64 supp_rates = 0;
enum ieee80211_band band = rx_status->band;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
if (elems->ds_params && elems->ds_params_len == 1)
freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
@@ -1538,17 +1578,16 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_IBSS_DEBUG
if (sta->sta.supp_rates[band] != prev_rates)
printk(KERN_DEBUG "%s: updated supp_rates set "
- "for %s based on beacon info (0x%llx | "
+ "for %pM based on beacon info (0x%llx | "
"0x%llx -> 0x%llx)\n",
sdata->dev->name,
- print_mac(mac, sta->sta.addr),
+ sta->sta.addr,
(unsigned long long) prev_rates,
(unsigned long long) supp_rates,
(unsigned long long) sta->sta.supp_rates[band]);
#endif
} else {
- ieee80211_ibss_add_sta(sdata, NULL, mgmt->bssid,
- mgmt->sa, supp_rates);
+ ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
}
rcu_read_unlock();
@@ -1595,8 +1634,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* e.g: at 1 MBit that means mactime is 192 usec earlier
* (=24 bytes * 8 usecs/byte) than the beacon timestamp.
*/
- int rate = local->hw.wiphy->bands[band]->
+ int rate;
+ if (rx_status->flag & RX_FLAG_HT) {
+ rate = 65; /* TODO: HT rates */
+ } else {
+ rate = local->hw.wiphy->bands[band]->
bitrates[rx_status->rate_idx].bitrate;
+ }
rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate);
} else if (local && local->ops && local->ops->get_tsf)
/* second best option: get current TSF */
@@ -1605,10 +1649,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
/* can't merge without knowing the TSF */
rx_timestamp = -1LLU;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "RX beacon SA=%s BSSID="
- "%s TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n",
- print_mac(mac, mgmt->sa),
- print_mac(mac2, mgmt->bssid),
+ printk(KERN_DEBUG "RX beacon SA=%pM BSSID="
+ "%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n",
+ mgmt->sa, mgmt->bssid,
(unsigned long long)rx_timestamp,
(unsigned long long)beacon_timestamp,
(unsigned long long)(rx_timestamp - beacon_timestamp),
@@ -1617,13 +1660,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (beacon_timestamp > rx_timestamp) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: beacon TSF higher than "
- "local TSF - IBSS merge with BSSID %s\n",
- sdata->dev->name, print_mac(mac, mgmt->bssid));
+ "local TSF - IBSS merge with BSSID %pM\n",
+ sdata->dev->name, mgmt->bssid);
#endif
ieee80211_sta_join_ibss(sdata, &sdata->u.sta, bss);
- ieee80211_ibss_add_sta(sdata, NULL,
- mgmt->bssid, mgmt->sa,
- supp_rates);
+ ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
}
}
@@ -1671,8 +1712,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
size_t baselen;
struct ieee802_11_elems elems;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_conf *conf = &local->hw.conf;
u32 changed = 0;
+ bool erp_valid;
+ u8 erp_value = 0;
/* Process beacon from the current BSS */
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
@@ -1694,22 +1736,49 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
elems.wmm_param_len);
- if (elems.erp_info && elems.erp_info_len >= 1)
- changed |= ieee80211_handle_erp_ie(sdata, elems.erp_info[0]);
- else {
- u16 capab = le16_to_cpu(mgmt->u.beacon.capab_info);
- changed |= ieee80211_handle_protect_preamb(sdata, false,
- (capab & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0);
+
+ if (elems.erp_info && elems.erp_info_len >= 1) {
+ erp_valid = true;
+ erp_value = elems.erp_info[0];
+ } else {
+ erp_valid = false;
}
+ changed |= ieee80211_handle_bss_capability(sdata,
+ le16_to_cpu(mgmt->u.beacon.capab_info),
+ erp_valid, erp_value);
- if (elems.ht_cap_elem && elems.ht_info_elem &&
- elems.wmm_param && conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
- struct ieee80211_ht_bss_info bss_info;
- ieee80211_ht_addt_info_ie_to_ht_bss_info(
- elems.ht_info_elem, &bss_info);
- changed |= ieee80211_handle_ht(local, 1, &conf->ht_conf,
- &bss_info);
+ if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param) {
+ struct sta_info *sta;
+ struct ieee80211_supported_band *sband;
+ u16 ap_ht_cap_flags;
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, ifsta->bssid);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
+ elems.ht_cap_elem, &sta->sta.ht_cap);
+
+ ap_ht_cap_flags = sta->sta.ht_cap.cap;
+
+ rcu_read_unlock();
+
+ changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
+ ap_ht_cap_flags);
+ }
+
+ if (elems.country_elem) {
+ /* Note we are only reviewing this on beacons
+ * for the BSSID we are associated to */
+ regulatory_hint_11d(local->hw.wiphy,
+ elems.country_elem, elems.country_elem_len);
}
ieee80211_bss_info_change_notify(sdata, changed);
@@ -1727,11 +1796,6 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb;
struct ieee80211_mgmt *resp;
u8 *pos, *end;
- DECLARE_MAC_BUF(mac);
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
-#endif
if (sdata->vif.type != NL80211_IFTYPE_ADHOC ||
ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED ||
@@ -1744,10 +1808,10 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
tx_last_beacon = 1;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: RX ProbeReq SA=%s DA=%s BSSID="
- "%s (tx_last_beacon=%d)\n",
- sdata->dev->name, print_mac(mac, mgmt->sa), print_mac(mac2, mgmt->da),
- print_mac(mac3, mgmt->bssid), tx_last_beacon);
+ printk(KERN_DEBUG "%s: RX ProbeReq SA=%pM DA=%pM BSSID=%pM"
+ " (tx_last_beacon=%d)\n",
+ sdata->dev->name, mgmt->sa, mgmt->da,
+ mgmt->bssid, tx_last_beacon);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
if (!tx_last_beacon)
@@ -1763,8 +1827,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
pos + 2 + pos[1] > end) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq "
- "from %s\n",
- sdata->dev->name, print_mac(mac, mgmt->sa));
+ "from %pM\n",
+ sdata->dev->name, mgmt->sa);
#endif
return;
}
@@ -1783,8 +1847,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
resp = (struct ieee80211_mgmt *) skb->data;
memcpy(resp->da, mgmt->sa, ETH_ALEN);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: Sending ProbeResp to %s\n",
- sdata->dev->name, print_mac(mac, resp->da));
+ printk(KERN_DEBUG "%s: Sending ProbeResp to %pM\n",
+ sdata->dev->name, resp->da);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
ieee80211_tx_skb(sdata, skb, 0);
}
@@ -1972,7 +2036,7 @@ static int ieee80211_sta_match_ssid(struct ieee80211_if_sta *ifsta,
}
}
- if (hidden_ssid && ifsta->ssid_len == ssid_len)
+ if (hidden_ssid && (ifsta->ssid_len == ssid_len || ssid_len == 0))
return 1;
if (ssid_len == 1 && ssid[0] == ' ')
@@ -1990,7 +2054,6 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
u8 bssid[ETH_ALEN], *pos;
int i;
int ret;
- DECLARE_MAC_BUF(mac);
#if 0
/* Easier testing, use fixed BSSID. */
@@ -2006,8 +2069,8 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
bssid[0] |= 0x02;
#endif
- printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %s\n",
- sdata->dev->name, print_mac(mac, bssid));
+ printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n",
+ sdata->dev->name, bssid);
bss = ieee80211_rx_bss_add(local, bssid,
local->hw.conf.channel->center_freq,
@@ -2050,8 +2113,6 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
int found = 0;
u8 bssid[ETH_ALEN];
int active_ibss;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
if (ifsta->ssid_len == 0)
return -EINVAL;
@@ -2068,8 +2129,7 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
|| !(bss->capability & WLAN_CAPABILITY_IBSS))
continue;
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG " bssid=%s found\n",
- print_mac(mac, bss->bssid));
+ printk(KERN_DEBUG " bssid=%pM found\n", bss->bssid);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
memcpy(bssid, bss->bssid, ETH_ALEN);
found = 1;
@@ -2080,9 +2140,8 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_IBSS_DEBUG
if (found)
- printk(KERN_DEBUG " sta_find_ibss: selected %s current "
- "%s\n", print_mac(mac, bssid),
- print_mac(mac2, ifsta->bssid));
+ printk(KERN_DEBUG " sta_find_ibss: selected %pM current "
+ "%pM\n", bssid, ifsta->bssid);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
if (found && memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) {
@@ -2099,9 +2158,9 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
if (!bss)
goto dont_join;
- printk(KERN_DEBUG "%s: Selected IBSS BSSID %s"
+ printk(KERN_DEBUG "%s: Selected IBSS BSSID %pM"
" based on configured SSID\n",
- sdata->dev->name, print_mac(mac, bssid));
+ sdata->dev->name, bssid);
ret = ieee80211_sta_join_ibss(sdata, ifsta, bss);
ieee80211_rx_bss_put(local, bss);
return ret;
@@ -2338,12 +2397,10 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
* must be callable in atomic context.
*/
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, u8 *bssid,
- u8 *addr, u64 supp_rates)
+ u8 *bssid,u8 *addr, u64 supp_rates)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- DECLARE_MAC_BUF(mac);
int band = local->hw.conf.channel->band;
/* TODO: Could consider removing the least recently used entry and
@@ -2351,7 +2408,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: No room for a new IBSS STA "
- "entry %s\n", sdata->dev->name, print_mac(mac, addr));
+ "entry %pM\n", sdata->dev->name, addr);
}
return NULL;
}
@@ -2360,8 +2417,8 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
return NULL;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Adding new IBSS station %s (dev=%s)\n",
- wiphy_name(local->hw.wiphy), print_mac(mac, addr), sdata->dev->name);
+ printk(KERN_DEBUG "%s: Adding new IBSS station %pM (dev=%s)\n",
+ wiphy_name(local->hw.wiphy), addr, sdata->dev->name);
#endif
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
@@ -2408,7 +2465,6 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
{
struct ieee80211_if_sta *ifsta;
- int res;
if (len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
@@ -2420,19 +2476,6 @@ int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size
memcpy(ifsta->ssid, ssid, len);
ifsta->ssid_len = len;
ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
-
- res = 0;
- /*
- * Hack! MLME code needs to be cleaned up to have different
- * entry points for configuration and internal selection change
- */
- if (netif_running(sdata->dev))
- res = ieee80211_if_config(sdata, IEEE80211_IFCC_SSID);
- if (res) {
- printk(KERN_DEBUG "%s: Failed to config new SSID to "
- "the low-level driver\n", sdata->dev->name);
- return res;
- }
}
if (len)
@@ -2560,3 +2603,39 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
ieee80211_restart_sta_timer(sdata);
rcu_read_unlock();
}
+
+void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local,
+ dynamic_ps_disable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ }
+
+ ieee80211_wake_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_PS);
+}
+
+void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local,
+ dynamic_ps_enable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS)
+ return;
+
+ local->hw.conf.flags |= IEEE80211_CONF_PS;
+
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+}
+
+void ieee80211_dynamic_ps_timer(unsigned long data)
+{
+ struct ieee80211_local *local = (void *) data;
+
+ queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
+}
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 5d786720d935..3fa7ab285066 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -199,48 +199,44 @@ static void rate_control_release(struct kref *kref)
}
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
- struct sta_info *sta, struct sk_buff *skb,
- struct rate_selection *sel)
+ struct sta_info *sta,
+ struct ieee80211_tx_rate_control *txrc)
{
struct rate_control_ref *ref = sdata->local->rate_ctrl;
void *priv_sta = NULL;
struct ieee80211_sta *ista = NULL;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
int i;
- sel->rate_idx = -1;
- sel->nonerp_idx = -1;
- sel->probe_idx = -1;
- sel->max_rate_idx = sdata->max_ratectrl_rateidx;
-
if (sta) {
ista = &sta->sta;
priv_sta = sta->rate_ctrl_priv;
}
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ info->control.rates[i].idx = -1;
+ info->control.rates[i].flags = 0;
+ info->control.rates[i].count = 1;
+ }
+
if (sta && sdata->force_unicast_rateidx > -1)
- sel->rate_idx = sdata->force_unicast_rateidx;
+ info->control.rates[0].idx = sdata->force_unicast_rateidx;
else
- ref->ops->get_rate(ref->priv, sband, ista, priv_sta, skb, sel);
-
- if (sdata->max_ratectrl_rateidx > -1 &&
- sel->rate_idx > sdata->max_ratectrl_rateidx)
- sel->rate_idx = sdata->max_ratectrl_rateidx;
-
- BUG_ON(sel->rate_idx < 0);
-
- /* Select a non-ERP backup rate. */
- if (sel->nonerp_idx < 0) {
- for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *rate = &sband->bitrates[i];
- if (sband->bitrates[sel->rate_idx].bitrate < rate->bitrate)
- break;
-
- if (rate_supported(ista, sband->band, i) &&
- !(rate->flags & IEEE80211_RATE_ERP_G))
- sel->nonerp_idx = i;
- }
+ ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
+
+ /*
+ * try to enforce the maximum rate the user wanted
+ */
+ if (sdata->max_ratectrl_rateidx > -1)
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS)
+ continue;
+ info->control.rates[i].idx =
+ min_t(s8, info->control.rates[i].idx,
+ sdata->max_ratectrl_rateidx);
}
+
+ BUG_ON(info->control.rates[0].idx < 0);
}
struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index d0092f847f82..9b632372e688 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -31,9 +31,8 @@ struct rate_control_ref {
struct rate_control_ref *rate_control_alloc(const char *name,
struct ieee80211_local *local);
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_supported_band *sband,
- struct sta_info *sta, struct sk_buff *skb,
- struct rate_selection *sel);
+ struct sta_info *sta,
+ struct ieee80211_tx_rate_control *txrc);
struct rate_control_ref *rate_control_get(struct rate_control_ref *ref);
void rate_control_put(struct rate_control_ref *ref);
@@ -64,12 +63,6 @@ static inline void rate_control_rate_init(struct sta_info *sta)
}
-static inline void rate_control_clear(struct ieee80211_local *local)
-{
- struct rate_control_ref *ref = local->rate_ctrl;
- ref->ops->clear(ref->priv);
-}
-
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
struct ieee80211_sta *sta,
gfp_t gfp)
@@ -125,6 +118,19 @@ static inline void rc80211_pid_exit(void)
}
#endif
+#ifdef CONFIG_MAC80211_RC_MULTIRATE_PID
+extern int rc80211_multirate_pid_init(void);
+extern void rc80211_multirate_pid_exit(void);
+#else
+static inline int rc80211_multirate_pid_init(void)
+{
+ return 0;
+}
+static inline void rc80211_multirate_pid_exit(void)
+{
+}
+#endif
+
#ifdef CONFIG_MAC80211_RC_MINSTREL
extern int rc80211_minstrel_init(void);
extern void rc80211_minstrel_exit(void);
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index f6d69dab07a3..3824990d340b 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -126,7 +126,9 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
mr->adjusted_retry_count = mr->retry_count >> 1;
if (mr->adjusted_retry_count > 2)
mr->adjusted_retry_count = 2;
+ mr->sample_limit = 4;
} else {
+ mr->sample_limit = -1;
mr->adjusted_retry_count = mr->retry_count;
}
if (!mr->adjusted_retry_count)
@@ -169,30 +171,20 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
{
struct minstrel_sta_info *mi = priv_sta;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_tx_altrate *ar = info->status.retries;
- struct minstrel_priv *mp = priv;
- int i, ndx, tries;
- int success = 0;
-
- if (!info->status.excessive_retries)
- success = 1;
+ struct ieee80211_tx_rate *ar = info->status.rates;
+ int i, ndx;
+ int success;
- if (!mp->has_mrr || (ar[0].rate_idx < 0)) {
- ndx = rix_to_ndx(mi, info->tx_rate_idx);
- tries = info->status.retry_count + 1;
- mi->r[ndx].success += success;
- mi->r[ndx].attempts += tries;
- return;
- }
+ success = !!(info->flags & IEEE80211_TX_STAT_ACK);
- for (i = 0; i < 4; i++) {
- if (ar[i].rate_idx < 0)
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ if (ar[i].idx < 0)
break;
- ndx = rix_to_ndx(mi, ar[i].rate_idx);
- mi->r[ndx].attempts += ar[i].limit + 1;
+ ndx = rix_to_ndx(mi, ar[i].idx);
+ mi->r[ndx].attempts += ar[i].count;
- if ((i != 3) && (ar[i + 1].rate_idx < 0))
+ if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0))
mi->r[ndx].success += success;
}
@@ -210,9 +202,9 @@ minstrel_get_retry_count(struct minstrel_rate *mr,
{
unsigned int retry = mr->adjusted_retry_count;
- if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
retry = max(2U, min(mr->retry_count_rtscts, retry));
- else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)
+ else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
retry = max(2U, min(mr->retry_count_cts, retry));
return retry;
}
@@ -233,15 +225,16 @@ minstrel_get_next_sample(struct minstrel_sta_info *mi)
return sample_ndx;
}
-void
-minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb, struct rate_selection *sel)
+static void
+minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
+ void *priv_sta, struct ieee80211_tx_rate_control *txrc)
{
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_supported_band *sband = txrc->sband;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct minstrel_sta_info *mi = priv_sta;
struct minstrel_priv *mp = priv;
- struct ieee80211_tx_altrate *ar = info->control.retries;
+ struct ieee80211_tx_rate *ar = info->control.rates;
unsigned int ndx, sample_ndx = 0;
bool mrr;
bool sample_slower = false;
@@ -251,16 +244,12 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
int sample_rate;
if (!sta || !mi || use_low_rate(skb)) {
- sel->rate_idx = rate_lowest_index(sband, sta);
+ ar[0].idx = rate_lowest_index(sband, sta);
+ ar[0].count = mp->max_retry;
return;
}
- mrr = mp->has_mrr;
-
- /* mac80211 does not allow mrr for RTS/CTS */
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT))
- mrr = false;
+ mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot;
if (time_after(jiffies, mi->stats_update + (mp->update_interval *
HZ) / 1000))
@@ -278,7 +267,8 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
(mi->sample_count + mi->sample_deferred / 2);
/* delta > 0: sampling required */
- if (delta > 0) {
+ if ((delta > 0) && (mrr || !mi->prev_sample)) {
+ struct minstrel_rate *msr;
if (mi->packet_count >= 10000) {
mi->sample_deferred = 0;
mi->sample_count = 0;
@@ -297,13 +287,20 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
}
sample_ndx = minstrel_get_next_sample(mi);
+ msr = &mi->r[sample_ndx];
sample = true;
- sample_slower = mrr && (mi->r[sample_ndx].perfect_tx_time >
+ sample_slower = mrr && (msr->perfect_tx_time >
mi->r[ndx].perfect_tx_time);
if (!sample_slower) {
- ndx = sample_ndx;
- mi->sample_count++;
+ if (msr->sample_limit != 0) {
+ ndx = sample_ndx;
+ mi->sample_count++;
+ if (msr->sample_limit > 0)
+ msr->sample_limit--;
+ } else {
+ sample = false;
+ }
} else {
/* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark
* packets that have the sampling rate deferred to the
@@ -315,13 +312,22 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
mi->sample_deferred++;
}
}
- sel->rate_idx = mi->r[ndx].rix;
- info->control.retry_limit = minstrel_get_retry_count(&mi->r[ndx], info);
+ mi->prev_sample = sample;
+
+ /* If we're not using MRR and the sampling rate already
+ * has a probability of >95%, we shouldn't be attempting
+ * to use it, as this only wastes precious airtime */
+ if (!mrr && sample && (mi->r[ndx].probability > 17100))
+ ndx = mi->max_tp_rate;
+
+ ar[0].idx = mi->r[ndx].rix;
+ ar[0].count = minstrel_get_retry_count(&mi->r[ndx], info);
if (!mrr) {
- ar[0].rate_idx = mi->lowest_rix;
- ar[0].limit = mp->max_retry;
- ar[1].rate_idx = -1;
+ if (!sample)
+ ar[0].count = mp->max_retry;
+ ar[1].idx = mi->lowest_rix;
+ ar[1].count = mp->max_retry;
return;
}
@@ -336,9 +342,9 @@ minstrel_get_rate(void *priv, struct ieee80211_supported_band *sband,
}
mrr_ndx[1] = mi->max_prob_rate;
mrr_ndx[2] = 0;
- for (i = 0; i < 3; i++) {
- ar[i].rate_idx = mi->r[mrr_ndx[i]].rix;
- ar[i].limit = mi->r[mrr_ndx[i]].adjusted_retry_count;
+ for (i = 1; i < 4; i++) {
+ ar[i].idx = mi->r[mrr_ndx[i - 1]].rix;
+ ar[i].count = mi->r[mrr_ndx[i - 1]].adjusted_retry_count;
}
}
@@ -389,13 +395,15 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
{
struct minstrel_sta_info *mi = priv_sta;
struct minstrel_priv *mp = priv;
- struct minstrel_rate *mr_ctl;
+ struct ieee80211_local *local = hw_to_local(mp->hw);
+ struct ieee80211_rate *ctl_rate;
unsigned int i, n = 0;
unsigned int t_slot = 9; /* FIXME: get real slot time */
mi->lowest_rix = rate_lowest_index(sband, sta);
- mr_ctl = &mi->r[rix_to_ndx(mi, mi->lowest_rix)];
- mi->sp_ack_dur = mr_ctl->ack_time;
+ ctl_rate = &sband->bitrates[mi->lowest_rix];
+ mi->sp_ack_dur = ieee80211_frame_duration(local, 10, ctl_rate->bitrate,
+ !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1);
for (i = 0; i < sband->n_bitrates; i++) {
struct minstrel_rate *mr = &mi->r[n];
@@ -410,11 +418,12 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
mr->rix = i;
mr->bitrate = sband->bitrates[i].bitrate / 5;
- calc_rate_durations(mi, hw_to_local(mp->hw), mr,
+ calc_rate_durations(mi, local, mr,
&sband->bitrates[i]);
/* calculate maximum number of retransmissions before
* fallback (based on maximum segment size) */
+ mr->sample_limit = -1;
mr->retry_count = 1;
mr->retry_count_cts = 1;
mr->retry_count_rtscts = 1;
@@ -500,11 +509,6 @@ minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
kfree(mi);
}
-static void
-minstrel_clear(void *priv)
-{
-}
-
static void *
minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
{
@@ -532,13 +536,13 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
/* maximum time that the hw is allowed to stay in one MRR segment */
mp->segment_size = 6000;
- if (hw->max_altrate_tries > 0)
- mp->max_retry = hw->max_altrate_tries;
+ if (hw->max_rate_tries > 0)
+ mp->max_retry = hw->max_rate_tries;
else
/* safe default, does not necessarily have to match hw properties */
mp->max_retry = 7;
- if (hw->max_altrates >= 3)
+ if (hw->max_rates >= 4)
mp->has_mrr = true;
mp->hw = hw;
@@ -558,7 +562,6 @@ static struct rate_control_ops mac80211_minstrel = {
.tx_status = minstrel_tx_status,
.get_rate = minstrel_get_rate,
.rate_init = minstrel_rate_init,
- .clear = minstrel_clear,
.alloc = minstrel_alloc,
.free = minstrel_free,
.alloc_sta = minstrel_alloc_sta,
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
index 9a90a6aee043..869fe0ef951d 100644
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -16,6 +16,7 @@ struct minstrel_rate {
unsigned int perfect_tx_time;
unsigned int ack_time;
+ int sample_limit;
unsigned int retry_count;
unsigned int retry_count_cts;
unsigned int retry_count_rtscts;
@@ -57,6 +58,7 @@ struct minstrel_sta_info {
int n_rates;
struct minstrel_rate *r;
+ bool prev_sample;
/* sampling table */
u8 *sample_table;
diff --git a/net/mac80211/rc80211_multirate_pid.h b/net/mac80211/rc80211_multirate_pid.h
new file mode 100644
index 000000000000..9f6b473f8821
--- /dev/null
+++ b/net/mac80211/rc80211_multirate_pid.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
+ * Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it>
+ * Copyright 2009, Digi International <support@digi.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.
+ */
+
+/*
+ * This is a modified version of the 802.11 PID algorithm. The standard
+ * version uses the same rate for all the retries of the same frame. The code
+ * was modified to perform the first few retries at the same rate and then
+ * reduce the data rate for each successive retry until we either run out of retries
+ * or successfully transmit the frame. This seems to provide better performance
+ * on error prone networks.
+ */
+
+#ifndef RC80211_MULTIRATE_PID_H
+#define RC80211_MULTIRATE_PID_H
+
+/* Sampling period for measuring percentage of failed frames in ms. */
+#define RC80211_MULTIRATE_PID_INTERVAL 125
+
+/* Exponential averaging smoothness (used for I part of PID controller) */
+#define RC80211_MULTIRATE_PID_SMOOTHING_SHIFT 3
+#define RC80211_MULTIRATE_PID_SMOOTHING (1 << RC80211_MULTIRATE_PID_SMOOTHING_SHIFT)
+
+/* Sharpening factor (used for D part of PID controller) */
+#define RC80211_MULTIRATE_PID_SHARPENING_FACTOR 0
+#define RC80211_MULTIRATE_PID_SHARPENING_DURATION 0
+
+/* Fixed point arithmetic shifting amount. */
+#define RC80211_MULTIRATE_PID_ARITH_SHIFT 8
+
+/* Fixed point arithmetic factor. */
+#define RC80211_MULTIRATE_PID_ARITH_FACTOR (1 << RC80211_MULTIRATE_PID_ARITH_SHIFT)
+
+/* Proportional PID component coefficient. */
+#define RC80211_MULTIRATE_PID_COEFF_P 15
+/* Integral PID component coefficient. */
+#define RC80211_MULTIRATE_PID_COEFF_I 9
+/* Derivative PID component coefficient. */
+#define RC80211_MULTIRATE_PID_COEFF_D 15
+
+/* Target failed frames rate for the PID controller. NB: This effectively gives
+ * maximum failed frames percentage we're willing to accept. If the wireless
+ * link quality is good, the controller will fail to adjust failed frames
+ * percentage to the target. This is intentional.
+ */
+#define RC80211_MULTIRATE_PID_TARGET_PF 14
+
+/* Rate behaviour normalization quantity over time. */
+#define RC80211_MULTIRATE_PID_NORM_OFFSET 3
+
+/* Push high rates right after loading. */
+#define RC80211_MULTIRATE_PID_FAST_START 0
+
+/* Arithmetic right shift for positive and negative values for ISO C. */
+#define RC80211_MULTIRATE_PID_DO_ARITH_RIGHT_SHIFT(x, y) \
+ ((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y))
+
+enum rc_multirate_pid_event_type {
+ RC80211_MULTIRATE_PID_EVENT_TYPE_TX_STATUS,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_RATE_CHANGE,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_TX_RATE,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_PF_SAMPLE,
+};
+
+union rc_multirate_pid_event_data {
+ /* RC80211_MULTIRATE_PID_EVENT_TX_STATUS */
+ struct {
+ u32 flags;
+ struct ieee80211_tx_info tx_status;
+ };
+ /* RC80211_MULTIRATE_PID_EVENT_TYPE_RATE_CHANGE */
+ /* RC80211_MULTIRATE_PID_EVENT_TYPE_TX_RATE */
+ struct {
+ int index;
+ int rate;
+ };
+ /* RC80211_MULTIRATE_PID_EVENT_TYPE_PF_SAMPLE */
+ struct {
+ s32 pf_sample;
+ s32 prop_err;
+ s32 int_err;
+ s32 der_err;
+ };
+};
+
+struct rc_multirate_pid_event {
+ /* The time when the event occured */
+ unsigned long timestamp;
+
+ /* Event ID number */
+ unsigned int id;
+
+ /* Type of event */
+ enum rc_multirate_pid_event_type type;
+
+ /* type specific data */
+ union rc_multirate_pid_event_data data;
+};
+
+/* Size of the event ring buffer. */
+#define RC80211_MULTIRATE_PID_EVENT_RING_SIZE 32
+
+struct rc_multirate_pid_event_buffer {
+ /* Counter that generates event IDs */
+ unsigned int ev_count;
+
+ /* Ring buffer of events */
+ struct rc_multirate_pid_event
+ ring[RC80211_MULTIRATE_PID_EVENT_RING_SIZE];
+
+ /* Index to the entry in events_buf to be reused */
+ unsigned int next_entry;
+
+ /* Lock that guards against concurrent access to this buffer struct */
+ spinlock_t lock;
+
+ /* Wait queue for poll/select and blocking I/O */
+ wait_queue_head_t waitqueue;
+};
+
+struct rc_multirate_pid_events_file_info {
+ /* The event buffer we read */
+ struct rc_multirate_pid_event_buffer *events;
+
+ /* The entry we have should read next */
+ unsigned int next_entry;
+};
+
+/**
+ * struct rc_multirate_pid_debugfs_entries - tunable parameters
+ *
+ * Algorithm parameters, tunable via debugfs.
+ * @target: target percentage for failed frames
+ * @sampling_period: error sampling interval in milliseconds
+ * @coeff_p: absolute value of the proportional coefficient
+ * @coeff_i: absolute value of the integral coefficient
+ * @coeff_d: absolute value of the derivative coefficient
+ * @smoothing_shift: absolute value of the integral smoothing factor (i.e.
+ * amount of smoothing introduced by the exponential moving average)
+ * @sharpen_factor: absolute value of the derivative sharpening factor (i.e.
+ * amount of emphasis given to the derivative term after low activity
+ * events)
+ * @sharpen_duration: duration of the sharpening effect after the detected low
+ * activity event, relative to sampling_period
+ * @norm_offset: amount of normalization periodically performed on the learnt
+ * rate behaviour values (lower means we should trust more what we learnt
+ * about behaviour of rates, higher means we should trust more the natural
+ * ordering of rates)
+ */
+struct rc_multirate_pid_debugfs_entries {
+ struct dentry *target;
+ struct dentry *sampling_period;
+ struct dentry *coeff_p;
+ struct dentry *coeff_i;
+ struct dentry *coeff_d;
+ struct dentry *smoothing_shift;
+ struct dentry *sharpen_factor;
+ struct dentry *sharpen_duration;
+ struct dentry *norm_offset;
+};
+
+void rate_control_multirate_pid_event_tx_status(struct
+ rc_multirate_pid_event_buffer
+ *buf,
+ struct ieee80211_tx_info
+ *stat);
+
+void rate_control_multirate_pid_event_rate_change(struct
+ rc_multirate_pid_event_buffer
+ *buf, int index,
+ int rate);
+
+void rate_control_multirate_pid_event_tx_rate(struct
+ rc_multirate_pid_event_buffer
+ *buf, int index, int rate);
+
+void rate_control_multirate_pid_event_pf_sample(struct
+ rc_multirate_pid_event_buffer
+ *buf, s32 pf_sample,
+ s32 prop_err, s32 int_err,
+ s32 der_err);
+
+void rate_control_multirate_pid_add_sta_debugfs(void *priv, void *priv_sta,
+ struct dentry *dir);
+
+void rate_control_multirate_pid_remove_sta_debugfs(void *priv,
+ void *priv_sta);
+
+struct rc_multirate_pid_sta_info {
+ unsigned long last_change;
+ unsigned long last_sample;
+
+ u32 tx_num_failed;
+ u32 tx_num_xmit;
+
+ int txrate_idx;
+
+ /* Average failed frames percentage error (i.e. actual vs. target
+ * percentage), scaled by RC80211_MULTIRATE_PID_SMOOTHING. This value is computed
+ * using using an exponential weighted average technique:
+ *
+ * (RC80211_MULTIRATE_PID_SMOOTHING - 1) * err_avg_old + err
+ * err_avg = ------------------------------------------
+ * RC80211_MULTIRATE_PID_SMOOTHING
+ *
+ * where err_avg is the new approximation, err_avg_old the previous one
+ * and err is the error w.r.t. to the current failed frames percentage
+ * sample. Note that the bigger RC80211_MULTIRATE_PID_SMOOTHING the more weight is
+ * given to the previous estimate, resulting in smoother behavior (i.e.
+ * corresponding to a longer integration window).
+ *
+ * For computation, we actually don't use the above formula, but this
+ * one:
+ *
+ * err_avg_scaled = err_avg_old_scaled - err_avg_old + err
+ *
+ * where:
+ * err_avg_scaled = err * RC80211_MULTIRATE_PID_SMOOTHING
+ * err_avg_old_scaled = err_avg_old * RC80211_MULTIRATE_PID_SMOOTHING
+ *
+ * This avoids floating point numbers and the per_failed_old value can
+ * easily be obtained by shifting per_failed_old_scaled right by
+ * RC80211_MULTIRATE_PID_SMOOTHING_SHIFT.
+ */
+ s32 err_avg_sc;
+
+ /* Last framed failes percentage sample. */
+ u32 last_pf;
+
+ /* Sharpening needed. */
+ u8 sharp_cnt;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ /* Event buffer */
+ struct rc_multirate_pid_event_buffer events;
+
+ /* Events debugfs file entry */
+ struct dentry *events_entry;
+#endif
+};
+
+/* Algorithm parameters. We keep them on a per-algorithm approach, so they can
+ * be tuned individually for each interface.
+ */
+struct rc_multirate_pid_rateinfo {
+
+ /* Map sorted rates to rates in ieee80211_hw_mode. */
+ int index;
+
+ /* Map rates in ieee80211_hw_mode to sorted rates. */
+ int rev_index;
+
+ /* Did we do any measurement on this rate? */
+ bool valid;
+
+ /* Comparison with the lowest rate. */
+ int diff;
+};
+
+struct rc_multirate_pid_info {
+
+ /* The failed frames percentage target. */
+ unsigned int target;
+
+ /* Rate at which failed frames percentage is sampled in 0.001s. */
+ unsigned int sampling_period;
+
+ /* P, I and D coefficients. */
+ int coeff_p;
+ int coeff_i;
+ int coeff_d;
+
+ /* Exponential averaging shift. */
+ unsigned int smoothing_shift;
+
+ /* Sharpening factor and duration. */
+ unsigned int sharpen_factor;
+ unsigned int sharpen_duration;
+
+ /* Normalization offset. */
+ unsigned int norm_offset;
+
+ /* Rates information. */
+ struct rc_multirate_pid_rateinfo *rinfo;
+
+ /* Index of the last used rate. */
+ int oldrate;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ /* Debugfs entries created for the parameters above. */
+ struct rc_multirate_pid_debugfs_entries dentries;
+#endif
+};
+
+#endif /* RC80211_MULTIRATE_PID_H */
diff --git a/net/mac80211/rc80211_multirate_pid_algo.c b/net/mac80211/rc80211_multirate_pid_algo.c
new file mode 100644
index 000000000000..aae2c44e572a
--- /dev/null
+++ b/net/mac80211/rc80211_multirate_pid_algo.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
+ * Copyright 2007-2008, Stefano Brivio <stefano.brivio@polimi.it>
+ * Copyright 2009, Digi International <support@multirate.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/netdevice.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/debugfs.h>
+#include <net/mac80211.h>
+#include "rate.h"
+#include "mesh.h"
+#include "rc80211_multirate_pid.h"
+
+/*
+ * This is a modified version of the 802.11 PID algorithm. The standard
+ * version uses the same rate for all the retries of the same frame. The code
+ * was modified to perform the first few retries at the same rate and then
+ * reduce the data rate for each successive retry until we either run out of retries
+ * or successfully transmit the frame. This seems to provide better performance
+ * on error prone networks.
+ */
+
+/* This is an implementation of a TX rate control algorithm that uses a PID
+ * controller. Given a target failed frames rate, the controller decides about
+ * TX rate changes to meet the target failed frames rate.
+ *
+ * The controller basically computes the following:
+ *
+ * adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening)
+ *
+ * where
+ * adj adjustment value that is used to switch TX rate (see below)
+ * err current error: target vs. current failed frames percentage
+ * last_err last error
+ * err_avg average (i.e. poor man's integral) of recent errors
+ * sharpening non-zero when fast response is needed (i.e. right after
+ * association or no frames sent for a long time), heading
+ * to zero over time
+ * CP Proportional coefficient
+ * CI Integral coefficient
+ * CD Derivative coefficient
+ *
+ * CP, CI, CD are subject to careful tuning.
+ *
+ * The integral component uses a exponential moving average approach instead of
+ * an actual sliding window. The advantage is that we don't need to keep an
+ * array of the last N error values and computation is easier.
+ *
+ * Once we have the adj value, we map it to a rate by means of a learning
+ * algorithm. This algorithm keeps the state of the percentual failed frames
+ * difference between rates. The behaviour of the lowest available rate is kept
+ * as a reference value, and every time we switch between two rates, we compute
+ * the difference between the failed frames each rate exhibited. By doing so,
+ * we compare behaviours which different rates exhibited in adjacent timeslices,
+ * thus the comparison is minimally affected by external conditions. This
+ * difference gets propagated to the whole set of measurements, so that the
+ * reference is always the same. Periodically, we normalize this set so that
+ * recent events weigh the most. By comparing the adj value with this set, we
+ * avoid pejorative switches to lower rates and allow for switches to higher
+ * rates if they behaved well.
+ *
+ * Note that for the computations we use a fixed-point representation to avoid
+ * floating point arithmetic. Hence, all values are shifted left by
+ * RC80211_MULTIRATE_PID_ARITH_SHIFT.
+ */
+
+
+/* Adjust the rate while ensuring that we won't switch to a lower rate if it
+ * exhibited a worse failed frames behaviour and we'll choose the highest rate
+ * whose failed frames behaviour is not worse than the one of the original rate
+ * target. While at it, check that the new rate is valid. */
+static void rate_control_multirate_pid_adjust_rate(struct
+ ieee80211_supported_band
+ *sband,
+ struct ieee80211_sta
+ *sta,
+ struct
+ rc_multirate_pid_sta_info
+ *spinfo, int adj,
+ struct
+ rc_multirate_pid_rateinfo
+ *rinfo)
+{
+ int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
+ int cur = spinfo->txrate_idx;
+
+ band = sband->band;
+ n_bitrates = sband->n_bitrates;
+
+ /* Map passed arguments to sorted values. */
+ cur_sorted = rinfo[cur].rev_index;
+ new_sorted = cur_sorted + adj;
+
+ /* Check limits. */
+ if (new_sorted < 0)
+ new_sorted = rinfo[0].rev_index;
+ else if (new_sorted >= n_bitrates)
+ new_sorted = rinfo[n_bitrates - 1].rev_index;
+
+ tmp = new_sorted;
+
+ if (adj < 0) {
+ /* Ensure that the rate decrease isn't disadvantageous. */
+ for (probe = cur_sorted; probe >= new_sorted; probe--)
+ if (rinfo[probe].diff <= rinfo[cur_sorted].diff &&
+ rate_supported(sta, band, rinfo[probe].index))
+ tmp = probe;
+ } else {
+ /* Look for rate increase with zero (or below) cost. */
+ for (probe = new_sorted + 1; probe < n_bitrates; probe++)
+ if (rinfo[probe].diff <= rinfo[new_sorted].diff &&
+ rate_supported(sta, band, rinfo[probe].index))
+ tmp = probe;
+ }
+
+ /* Fit the rate found to the nearest supported rate. */
+ do {
+ if (rate_supported(sta, band, rinfo[tmp].index)) {
+ spinfo->txrate_idx = rinfo[tmp].index;
+ break;
+ }
+ if (adj < 0)
+ tmp--;
+ else
+ tmp++;
+ } while (tmp < n_bitrates && tmp >= 0);
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ rate_control_multirate_pid_event_rate_change(&spinfo->events,
+ spinfo->txrate_idx,
+ sband->
+ bitrates[spinfo->
+ txrate_idx].
+ bitrate);
+#endif
+}
+
+/* Normalize the failed frames per-rate differences. */
+static void rate_control_multirate_pid_normalize(struct
+ rc_multirate_pid_info
+ *pinfo, int l)
+{
+ int i, norm_offset = pinfo->norm_offset;
+ struct rc_multirate_pid_rateinfo *r = pinfo->rinfo;
+
+ if (r[0].diff > norm_offset)
+ r[0].diff -= norm_offset;
+ else if (r[0].diff < -norm_offset)
+ r[0].diff += norm_offset;
+ for (i = 0; i < l - 1; i++)
+ if (r[i + 1].diff > r[i].diff + norm_offset)
+ r[i + 1].diff -= norm_offset;
+ else if (r[i + 1].diff <= r[i].diff)
+ r[i + 1].diff += norm_offset;
+}
+
+static void rate_control_multirate_pid_sample(struct rc_multirate_pid_info
+ *pinfo,
+ struct
+ ieee80211_supported_band
+ *sband,
+ struct ieee80211_sta *sta,
+ struct
+ rc_multirate_pid_sta_info
+ *spinfo)
+{
+ struct rc_multirate_pid_rateinfo *rinfo = pinfo->rinfo;
+ u32 pf;
+ s32 err_avg;
+ u32 err_prop;
+ u32 err_int;
+ u32 err_der;
+ int adj, i, j, tmp;
+ unsigned long period;
+
+ /* In case nothing happened during the previous control interval, turn
+ * the sharpening factor on. */
+ period = (HZ * pinfo->sampling_period + 500) / 1000;
+ if (!period)
+ period = 1;
+ if (jiffies - spinfo->last_sample > 2 * period)
+ spinfo->sharp_cnt = pinfo->sharpen_duration;
+
+ spinfo->last_sample = jiffies;
+
+ /* This should never happen, but in case, we assume the old sample is
+ * still a good measurement and copy it. */
+ if (unlikely(spinfo->tx_num_xmit == 0))
+ pf = spinfo->last_pf;
+ else {
+ /* XXX: BAD HACK!!! */
+ struct sta_info *si =
+ container_of(sta, struct sta_info, sta);
+
+ pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
+
+ if (ieee80211_vif_is_mesh(&si->sdata->vif) && pf == 100)
+ mesh_plink_broken(si);
+ pf <<= RC80211_MULTIRATE_PID_ARITH_SHIFT;
+ si->fail_avg = ((pf + (spinfo->last_pf << 3)) / 9)
+ >> RC80211_MULTIRATE_PID_ARITH_SHIFT;
+ }
+
+ spinfo->tx_num_xmit = 0;
+ spinfo->tx_num_failed = 0;
+
+ /* If we just switched rate, update the rate behaviour info. */
+ if (pinfo->oldrate != spinfo->txrate_idx) {
+
+ i = rinfo[pinfo->oldrate].rev_index;
+ j = rinfo[spinfo->txrate_idx].rev_index;
+
+ tmp = (pf - spinfo->last_pf);
+ tmp =
+ RC80211_MULTIRATE_PID_DO_ARITH_RIGHT_SHIFT(tmp,
+ RC80211_MULTIRATE_PID_ARITH_SHIFT);
+
+ rinfo[j].diff = rinfo[i].diff + tmp;
+ pinfo->oldrate = spinfo->txrate_idx;
+ }
+ rate_control_multirate_pid_normalize(pinfo, sband->n_bitrates);
+
+ /* Compute the proportional, integral and derivative errors. */
+ err_prop =
+ (pinfo->target << RC80211_MULTIRATE_PID_ARITH_SHIFT) - pf;
+
+ err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift;
+ spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop;
+ err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift;
+
+ err_der = (pf - spinfo->last_pf) *
+ (1 + pinfo->sharpen_factor * spinfo->sharp_cnt);
+ spinfo->last_pf = pf;
+ if (spinfo->sharp_cnt)
+ spinfo->sharp_cnt--;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ rate_control_multirate_pid_event_pf_sample(&spinfo->events, pf,
+ err_prop, err_int,
+ err_der);
+#endif
+
+ /* Compute the controller output. */
+ adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i
+ + err_der * pinfo->coeff_d);
+ adj =
+ RC80211_MULTIRATE_PID_DO_ARITH_RIGHT_SHIFT(adj,
+ 2 *
+ RC80211_MULTIRATE_PID_ARITH_SHIFT);
+
+ /* Change rate. */
+ if (adj)
+ rate_control_multirate_pid_adjust_rate(sband, sta, spinfo,
+ adj, rinfo);
+}
+
+static void rate_control_multirate_pid_tx_status(void *priv,
+ struct
+ ieee80211_supported_band
+ *sband,
+ struct ieee80211_sta *sta,
+ void *priv_sta,
+ struct sk_buff *skb)
+{
+ struct rc_multirate_pid_info *pinfo = priv;
+ struct rc_multirate_pid_sta_info *spinfo = priv_sta;
+ unsigned long period;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (!spinfo)
+ return;
+
+ /* Ignore all frames that were sent with a different rate than the rate
+ * we currently advise mac80211 to use. */
+ if (info->status.rates[0].idx != spinfo->txrate_idx)
+ return;
+
+ spinfo->tx_num_xmit++;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ rate_control_multirate_pid_event_tx_status(&spinfo->events, info);
+#endif
+
+ /* We count frames that totally failed to be transmitted as two bad
+ * frames, those that made it out but had some retries as one good and
+ * one bad frame. */
+ if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
+ spinfo->tx_num_failed += 2;
+ spinfo->tx_num_xmit++;
+ } else if (info->status.rates[0].count > 1) {
+ spinfo->tx_num_failed++;
+ spinfo->tx_num_xmit++;
+ }
+
+ /* Update PID controller state. */
+ period = (HZ * pinfo->sampling_period + 500) / 1000;
+ if (!period)
+ period = 1;
+ if (time_after(jiffies, spinfo->last_sample + period))
+ rate_control_multirate_pid_sample(pinfo, sband, sta,
+ spinfo);
+}
+
+static void
+rate_control_multirate_pid_get_rate(void *priv, struct ieee80211_sta *sta,
+ void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc)
+{
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_supported_band *sband = txrc->sband;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct rc_multirate_pid_sta_info *spinfo = priv_sta;
+ int rateidx, savedidx;
+ u16 fc;
+ int max_tries, i;
+
+ if (txrc->rts)
+ max_tries = txrc->hw->conf.long_frame_max_tx_count;
+ else
+ max_tries = txrc->hw->conf.short_frame_max_tx_count;
+
+ /* Send management frames and broadcast/multicast data using lowest
+ * rate. */
+ fc = le16_to_cpu(hdr->frame_control);
+ if (!sta || !spinfo ||
+ (fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
+ is_multicast_ether_addr(hdr->addr1)) {
+ info->control.rates[0].idx = rate_lowest_index(sband, sta);
+ return;
+ }
+
+ rateidx = spinfo->txrate_idx;
+
+ if (rateidx >= sband->n_bitrates)
+ rateidx = sband->n_bitrates - 1;
+
+ savedidx = rateidx;
+
+ if (max_tries >= 2) {
+ info->control.rates[0].count = 2;
+ max_tries -= 2;
+ } else {
+ info->control.rates[0].count = max_tries;
+ max_tries = 0;
+ }
+ info->control.rates[0].idx = rateidx;
+ rateidx--;
+ for (i = 1; (i < IEEE80211_TX_MAX_RATES)
+ && (max_tries > 0) && (rateidx >= 0); i++) {
+ info->control.rates[i].idx = rateidx;
+ info->control.rates[i].count = 1;
+ rateidx--;
+ max_tries--;
+ }
+ info->control.rates[0].count += max_tries;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ rate_control_multirate_pid_event_tx_rate(&spinfo->events,
+ savedidx,
+ sband->bitrates[savedidx].
+ bitrate);
+#endif
+}
+
+static void
+rate_control_multirate_pid_rate_init(void *priv,
+ struct ieee80211_supported_band
+ *sband, struct ieee80211_sta *sta,
+ void *priv_sta)
+{
+ struct rc_multirate_pid_sta_info *spinfo = priv_sta;
+ struct sta_info *si;
+
+ /* TODO: This routine should consider using RSSI from previous packets
+ * as we need to have IEEE 802.1X auth succeed immediately after assoc..
+ * Until that method is implemented, we will use the lowest supported
+ * rate as a workaround. */
+
+ spinfo->txrate_idx = rate_lowest_index(sband, sta);
+ /* HACK */
+ si = container_of(sta, struct sta_info, sta);
+ si->fail_avg = 0;
+}
+
+static void *rate_control_multirate_pid_alloc(struct ieee80211_hw *hw,
+ struct dentry *debugfsdir)
+{
+ struct rc_multirate_pid_info *pinfo;
+ struct rc_multirate_pid_rateinfo *rinfo;
+ struct ieee80211_supported_band *sband;
+ int i, j, tmp;
+ bool s;
+#ifdef CONFIG_MAC80211_DEBUGFS
+ struct rc_multirate_pid_debugfs_entries *de;
+#endif
+
+ sband = hw->wiphy->bands[hw->conf.channel->band];
+
+ pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC);
+ if (!pinfo)
+ return NULL;
+
+ /* We can safely assume that sband won't change unless we get
+ * reinitialized. */
+ rinfo = kmalloc(sizeof(*rinfo) * sband->n_bitrates, GFP_ATOMIC);
+ if (!rinfo) {
+ kfree(pinfo);
+ return NULL;
+ }
+
+ pinfo->target = RC80211_MULTIRATE_PID_TARGET_PF;
+ pinfo->sampling_period = RC80211_MULTIRATE_PID_INTERVAL;
+ pinfo->coeff_p = RC80211_MULTIRATE_PID_COEFF_P;
+ pinfo->coeff_i = RC80211_MULTIRATE_PID_COEFF_I;
+ pinfo->coeff_d = RC80211_MULTIRATE_PID_COEFF_D;
+ pinfo->smoothing_shift = RC80211_MULTIRATE_PID_SMOOTHING_SHIFT;
+ pinfo->sharpen_factor = RC80211_MULTIRATE_PID_SHARPENING_FACTOR;
+ pinfo->sharpen_duration =
+ RC80211_MULTIRATE_PID_SHARPENING_DURATION;
+ pinfo->norm_offset = RC80211_MULTIRATE_PID_NORM_OFFSET;
+ pinfo->rinfo = rinfo;
+ pinfo->oldrate = 0;
+
+ /* Sort the rates. This is optimized for the most common case (i.e.
+ * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed
+ * mapping too. */
+ for (i = 0; i < sband->n_bitrates; i++) {
+ rinfo[i].index = i;
+ rinfo[i].rev_index = i;
+ if (RC80211_MULTIRATE_PID_FAST_START)
+ rinfo[i].diff = 0;
+ else
+ rinfo[i].diff = i * pinfo->norm_offset;
+ }
+ for (i = 1; i < sband->n_bitrates; i++) {
+ s = 0;
+ for (j = 0; j < sband->n_bitrates - i; j++)
+ if (unlikely
+ (sband->bitrates[rinfo[j].index].bitrate >
+ sband->bitrates[rinfo[j + 1].index].
+ bitrate)) {
+ tmp = rinfo[j].index;
+ rinfo[j].index = rinfo[j + 1].index;
+ rinfo[j + 1].index = tmp;
+ rinfo[rinfo[j].index].rev_index = j;
+ rinfo[rinfo[j + 1].index].rev_index =
+ j + 1;
+ s = 1;
+ }
+ if (!s)
+ break;
+ }
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ de = &pinfo->dentries;
+ de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR,
+ debugfsdir, &pinfo->target);
+ de->sampling_period = debugfs_create_u32("sampling_period",
+ S_IRUSR | S_IWUSR,
+ debugfsdir,
+ &pinfo->sampling_period);
+ de->coeff_p =
+ debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR, debugfsdir,
+ (u32 *) & pinfo->coeff_p);
+ de->coeff_i =
+ debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR, debugfsdir,
+ (u32 *) & pinfo->coeff_i);
+ de->coeff_d =
+ debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR, debugfsdir,
+ (u32 *) & pinfo->coeff_d);
+ de->smoothing_shift =
+ debugfs_create_u32("smoothing_shift", S_IRUSR | S_IWUSR,
+ debugfsdir, &pinfo->smoothing_shift);
+ de->sharpen_factor =
+ debugfs_create_u32("sharpen_factor", S_IRUSR | S_IWUSR,
+ debugfsdir, &pinfo->sharpen_factor);
+ de->sharpen_duration =
+ debugfs_create_u32("sharpen_duration", S_IRUSR | S_IWUSR,
+ debugfsdir, &pinfo->sharpen_duration);
+ de->norm_offset =
+ debugfs_create_u32("norm_offset", S_IRUSR | S_IWUSR,
+ debugfsdir, &pinfo->norm_offset);
+#endif
+
+ return pinfo;
+}
+
+static void rate_control_multirate_pid_free(void *priv)
+{
+ struct rc_multirate_pid_info *pinfo = priv;
+#ifdef CONFIG_MAC80211_DEBUGFS
+ struct rc_multirate_pid_debugfs_entries *de = &pinfo->dentries;
+
+ debugfs_remove(de->norm_offset);
+ debugfs_remove(de->sharpen_duration);
+ debugfs_remove(de->sharpen_factor);
+ debugfs_remove(de->smoothing_shift);
+ debugfs_remove(de->coeff_d);
+ debugfs_remove(de->coeff_i);
+ debugfs_remove(de->coeff_p);
+ debugfs_remove(de->sampling_period);
+ debugfs_remove(de->target);
+#endif
+
+ kfree(pinfo->rinfo);
+ kfree(pinfo);
+}
+
+static void *rate_control_multirate_pid_alloc_sta(void *priv,
+ struct ieee80211_sta
+ *sta, gfp_t gfp)
+{
+ struct rc_multirate_pid_sta_info *spinfo;
+
+ spinfo = kzalloc(sizeof(*spinfo), gfp);
+ if (spinfo == NULL)
+ return NULL;
+
+ spinfo->last_sample = jiffies;
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+ spin_lock_init(&spinfo->events.lock);
+ init_waitqueue_head(&spinfo->events.waitqueue);
+#endif
+
+ return spinfo;
+}
+
+static void rate_control_multirate_pid_free_sta(void *priv,
+ struct ieee80211_sta *sta,
+ void *priv_sta)
+{
+ kfree(priv_sta);
+}
+
+static struct rate_control_ops mac80211_rcpid = {
+ .name = "multirate-pid",
+ .tx_status = rate_control_multirate_pid_tx_status,
+ .get_rate = rate_control_multirate_pid_get_rate,
+ .rate_init = rate_control_multirate_pid_rate_init,
+ .alloc = rate_control_multirate_pid_alloc,
+ .free = rate_control_multirate_pid_free,
+ .alloc_sta = rate_control_multirate_pid_alloc_sta,
+ .free_sta = rate_control_multirate_pid_free_sta,
+#ifdef CONFIG_MAC80211_DEBUGFS
+ .add_sta_debugfs = rate_control_multirate_pid_add_sta_debugfs,
+ .remove_sta_debugfs =
+ rate_control_multirate_pid_remove_sta_debugfs,
+#endif
+};
+
+int __init rc80211_multirate_pid_init(void)
+{
+ return ieee80211_rate_control_register(&mac80211_rcpid);
+}
+
+void rc80211_multirate_pid_exit(void)
+{
+ ieee80211_rate_control_unregister(&mac80211_rcpid);
+}
diff --git a/net/mac80211/rc80211_multirate_pid_debugfs.c b/net/mac80211/rc80211_multirate_pid_debugfs.c
new file mode 100644
index 000000000000..1d98a3062793
--- /dev/null
+++ b/net/mac80211/rc80211_multirate_pid_debugfs.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
+ *
+ * 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/spinlock.h>
+#include <linux/poll.h>
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+
+#include <net/mac80211.h>
+#include "rate.h"
+
+#include "rc80211_multirate_pid.h"
+
+static void rate_control_multirate_pid_event(struct
+ rc_multirate_pid_event_buffer
+ *buf,
+ enum
+ rc_multirate_pid_event_type
+ type,
+ union
+ rc_multirate_pid_event_data
+ *data)
+{
+ struct rc_multirate_pid_event *ev;
+ unsigned long status;
+
+ spin_lock_irqsave(&buf->lock, status);
+ ev = &(buf->ring[buf->next_entry]);
+ buf->next_entry =
+ (buf->next_entry + 1) % RC80211_MULTIRATE_PID_EVENT_RING_SIZE;
+
+ ev->timestamp = jiffies;
+ ev->id = buf->ev_count++;
+ ev->type = type;
+ ev->data = *data;
+
+ spin_unlock_irqrestore(&buf->lock, status);
+
+ wake_up_all(&buf->waitqueue);
+}
+
+void rate_control_multirate_pid_event_tx_status(struct
+ rc_multirate_pid_event_buffer
+ *buf,
+ struct ieee80211_tx_info
+ *stat)
+{
+ union rc_multirate_pid_event_data evd;
+
+ evd.flags = stat->flags;
+ memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info));
+ rate_control_multirate_pid_event(buf,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_TX_STATUS,
+ &evd);
+}
+
+void rate_control_multirate_pid_event_rate_change(struct
+ rc_multirate_pid_event_buffer
+ *buf, int index,
+ int rate)
+{
+ union rc_multirate_pid_event_data evd;
+
+ evd.index = index;
+ evd.rate = rate;
+ rate_control_multirate_pid_event(buf,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_RATE_CHANGE,
+ &evd);
+}
+
+void rate_control_multirate_pid_event_tx_rate(struct
+ rc_multirate_pid_event_buffer
+ *buf, int index, int rate)
+{
+ union rc_multirate_pid_event_data evd;
+
+ evd.index = index;
+ evd.rate = rate;
+ rate_control_multirate_pid_event(buf,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_TX_RATE,
+ &evd);
+}
+
+void rate_control_multirate_pid_event_pf_sample(struct
+ rc_multirate_pid_event_buffer
+ *buf, s32 pf_sample,
+ s32 prop_err, s32 int_err,
+ s32 der_err)
+{
+ union rc_multirate_pid_event_data evd;
+
+ evd.pf_sample = pf_sample;
+ evd.prop_err = prop_err;
+ evd.int_err = int_err;
+ evd.der_err = der_err;
+ rate_control_multirate_pid_event(buf,
+ RC80211_MULTIRATE_PID_EVENT_TYPE_PF_SAMPLE,
+ &evd);
+}
+
+static int rate_control_multirate_pid_events_open(struct inode *inode,
+ struct file *file)
+{
+ struct rc_multirate_pid_sta_info *sinfo = inode->i_private;
+ struct rc_multirate_pid_event_buffer *events = &sinfo->events;
+ struct rc_multirate_pid_events_file_info *file_info;
+ unsigned long status;
+
+ /* Allocate a state struct */
+ file_info = kmalloc(sizeof(*file_info), GFP_KERNEL);
+ if (file_info == NULL)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&events->lock, status);
+
+ file_info->next_entry = events->next_entry;
+ file_info->events = events;
+
+ spin_unlock_irqrestore(&events->lock, status);
+
+ file->private_data = file_info;
+
+ return 0;
+}
+
+static int rate_control_multirate_pid_events_release(struct inode *inode,
+ struct file *file)
+{
+ struct rc_multirate_pid_events_file_info *file_info =
+ file->private_data;
+
+ kfree(file_info);
+
+ return 0;
+}
+
+static unsigned int rate_control_multirate_pid_events_poll(struct file
+ *file,
+ poll_table *
+ wait)
+{
+ struct rc_multirate_pid_events_file_info *file_info =
+ file->private_data;
+
+ poll_wait(file, &file_info->events->waitqueue, wait);
+
+ return POLLIN | POLLRDNORM;
+}
+
+#define RC80211_MULTIRATE_PID_PRINT_BUF_SIZE 64
+
+static ssize_t rate_control_multirate_pid_events_read(struct file *file,
+ char __user * buf,
+ size_t length,
+ loff_t * offset)
+{
+ struct rc_multirate_pid_events_file_info *file_info =
+ file->private_data;
+ struct rc_multirate_pid_event_buffer *events = file_info->events;
+ struct rc_multirate_pid_event *ev;
+ char pb[RC80211_MULTIRATE_PID_PRINT_BUF_SIZE];
+ int ret;
+ int p;
+ unsigned long status;
+
+ /* Check if there is something to read. */
+ if (events->next_entry == file_info->next_entry) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ /* Wait */
+ ret = wait_event_interruptible(events->waitqueue,
+ events->next_entry !=
+ file_info->next_entry);
+
+ if (ret)
+ return ret;
+ }
+
+ /* Write out one event per call. I don't care whether it's a little
+ * inefficient, this is debugging code anyway. */
+ spin_lock_irqsave(&events->lock, status);
+
+ /* Get an event */
+ ev = &(events->ring[file_info->next_entry]);
+ file_info->next_entry = (file_info->next_entry + 1) %
+ RC80211_MULTIRATE_PID_EVENT_RING_SIZE;
+
+ /* Print information about the event. Note that userpace needs to
+ * provide large enough buffers. */
+ length = length < RC80211_MULTIRATE_PID_PRINT_BUF_SIZE ?
+ length : RC80211_MULTIRATE_PID_PRINT_BUF_SIZE;
+ p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
+ switch (ev->type) {
+ case RC80211_MULTIRATE_PID_EVENT_TYPE_TX_STATUS:
+ p += snprintf(pb + p, length - p, "tx_status %u %u",
+ !(ev->data.flags & IEEE80211_TX_STAT_ACK),
+ ev->data.tx_status.status.rates[0].idx);
+ break;
+ case RC80211_MULTIRATE_PID_EVENT_TYPE_RATE_CHANGE:
+ p += snprintf(pb + p, length - p, "rate_change %d %d",
+ ev->data.index, ev->data.rate);
+ break;
+ case RC80211_MULTIRATE_PID_EVENT_TYPE_TX_RATE:
+ p += snprintf(pb + p, length - p, "tx_rate %d %d",
+ ev->data.index, ev->data.rate);
+ break;
+ case RC80211_MULTIRATE_PID_EVENT_TYPE_PF_SAMPLE:
+ p += snprintf(pb + p, length - p,
+ "pf_sample %d %d %d %d",
+ ev->data.pf_sample, ev->data.prop_err,
+ ev->data.int_err, ev->data.der_err);
+ break;
+ }
+ p += snprintf(pb + p, length - p, "\n");
+
+ spin_unlock_irqrestore(&events->lock, status);
+
+ if (copy_to_user(buf, pb, p))
+ return -EFAULT;
+
+ return p;
+}
+
+#undef RC80211_MULTIRATE_PID_PRINT_BUF_SIZE
+
+static struct file_operations rc_multirate_pid_fop_events = {
+ .owner = THIS_MODULE,
+ .read = rate_control_multirate_pid_events_read,
+ .poll = rate_control_multirate_pid_events_poll,
+ .open = rate_control_multirate_pid_events_open,
+ .release = rate_control_multirate_pid_events_release,
+};
+
+void rate_control_multirate_pid_add_sta_debugfs(void *priv, void *priv_sta,
+ struct dentry *dir)
+{
+ struct rc_multirate_pid_sta_info *spinfo = priv_sta;
+
+ spinfo->events_entry =
+ debugfs_create_file("rc_multirate_pid_events", S_IRUGO, dir,
+ spinfo, &rc_multirate_pid_fop_events);
+}
+
+void rate_control_multirate_pid_remove_sta_debugfs(void *priv,
+ void *priv_sta)
+{
+ struct rc_multirate_pid_sta_info *spinfo = priv_sta;
+
+ debugfs_remove(spinfo->events_entry);
+}
diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h
index 01d64d53f3b9..1a873f00691a 100644
--- a/net/mac80211/rc80211_pid.h
+++ b/net/mac80211/rc80211_pid.h
@@ -49,7 +49,7 @@
/* Arithmetic right shift for positive and negative values for ISO C. */
#define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \
- (x) < 0 ? -((-(x)) >> (y)) : (x) >> (y)
+ ((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y))
enum rc_pid_event_type {
RC_PID_EVENT_TYPE_TX_STATUS,
@@ -61,6 +61,7 @@ enum rc_pid_event_type {
union rc_pid_event_data {
/* RC_PID_EVENT_TX_STATUS */
struct {
+ u32 flags;
struct ieee80211_tx_info tx_status;
};
/* RC_PID_EVENT_TYPE_RATE_CHANGE */
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
index 86eb374e3b87..5b1c9f1abf10 100644
--- a/net/mac80211/rc80211_pid_algo.c
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -241,7 +241,7 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba
/* Ignore all frames that were sent with a different rate than the rate
* we currently advise mac80211 to use. */
- if (info->tx_rate_idx != spinfo->txrate_idx)
+ if (info->status.rates[0].idx != spinfo->txrate_idx)
return;
spinfo->tx_num_xmit++;
@@ -253,10 +253,10 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba
/* We count frames that totally failed to be transmitted as two bad
* frames, those that made it out but had some retries as one good and
* one bad frame. */
- if (info->status.excessive_retries) {
+ if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
spinfo->tx_num_failed += 2;
spinfo->tx_num_xmit++;
- } else if (info->status.retry_count) {
+ } else if (info->status.rates[0].count > 1) {
spinfo->tx_num_failed++;
spinfo->tx_num_xmit++;
}
@@ -270,23 +270,31 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba
}
static void
-rate_control_pid_get_rate(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb,
- struct rate_selection *sel)
+rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
+ void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc)
{
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_supported_band *sband = txrc->sband;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct rc_pid_sta_info *spinfo = priv_sta;
int rateidx;
u16 fc;
+ if (txrc->rts)
+ info->control.rates[0].count =
+ txrc->hw->conf.long_frame_max_tx_count;
+ else
+ info->control.rates[0].count =
+ txrc->hw->conf.short_frame_max_tx_count;
/* Send management frames and broadcast/multicast data using lowest
* rate. */
fc = le16_to_cpu(hdr->frame_control);
if (!sta || !spinfo ||
(fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
is_multicast_ether_addr(hdr->addr1)) {
- sel->rate_idx = rate_lowest_index(sband, sta);
+ info->control.rates[0].idx = rate_lowest_index(sband, sta);
return;
}
@@ -295,7 +303,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_supported_band *sband,
if (rateidx >= sband->n_bitrates)
rateidx = sband->n_bitrates - 1;
- sel->rate_idx = rateidx;
+ info->control.rates[0].idx = rateidx;
#ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_tx_rate(&spinfo->events,
@@ -394,11 +402,11 @@ static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->sampling_period);
de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR,
- debugfsdir, &pinfo->coeff_p);
+ debugfsdir, (u32 *)&pinfo->coeff_p);
de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR,
- debugfsdir, &pinfo->coeff_i);
+ debugfsdir, (u32 *)&pinfo->coeff_i);
de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR,
- debugfsdir, &pinfo->coeff_d);
+ debugfsdir, (u32 *)&pinfo->coeff_d);
de->smoothing_shift = debugfs_create_u32("smoothing_shift",
S_IRUSR | S_IWUSR, debugfsdir,
&pinfo->smoothing_shift);
@@ -437,10 +445,6 @@ static void rate_control_pid_free(void *priv)
kfree(pinfo);
}
-static void rate_control_pid_clear(void *priv)
-{
-}
-
static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta,
gfp_t gfp)
{
@@ -471,7 +475,6 @@ static struct rate_control_ops mac80211_rcpid = {
.tx_status = rate_control_pid_tx_status,
.get_rate = rate_control_pid_get_rate,
.rate_init = rate_control_pid_rate_init,
- .clear = rate_control_pid_clear,
.alloc = rate_control_pid_alloc,
.free = rate_control_pid_free,
.alloc_sta = rate_control_pid_alloc_sta,
diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c
index 8121d3bc6835..a08a9b530347 100644
--- a/net/mac80211/rc80211_pid_debugfs.c
+++ b/net/mac80211/rc80211_pid_debugfs.c
@@ -43,6 +43,7 @@ void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
{
union rc_pid_event_data evd;
+ evd.flags = stat->flags;
memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info));
rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd);
}
@@ -167,8 +168,8 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
switch (ev->type) {
case RC_PID_EVENT_TYPE_TX_STATUS:
p += snprintf(pb + p, length - p, "tx_status %u %u",
- ev->data.tx_status.status.excessive_retries,
- ev->data.tx_status.status.retry_count);
+ !(ev->data.flags & IEEE80211_TX_STAT_ACK),
+ ev->data.tx_status.status.rates[0].idx);
break;
case RC_PID_EVENT_TYPE_RATE_CHANGE:
p += snprintf(pb + p, length - p, "rate_change %d %d",
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index cf6b121e1bbf..6cc9c174d071 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -26,10 +26,11 @@
#include "tkip.h"
#include "wme.h"
-u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
- struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff *skb, u16 mpdu_seq_num,
- int bar_req);
+static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ struct sk_buff *skb,
+ u16 mpdu_seq_num,
+ int bar_req);
/*
* monitor mode reception
*
@@ -122,7 +123,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* radiotap header, set always present flags */
rthdr->it_present =
cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_ANTENNA) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
@@ -148,7 +148,19 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos++;
/* IEEE80211_RADIOTAP_RATE */
- *pos = rate->bitrate / 5;
+ if (status->flag & RX_FLAG_HT) {
+ /*
+ * TODO: add following information into radiotap header once
+ * suitable fields are defined for it:
+ * - MCS index (status->rate_idx)
+ * - HT40 (status->flag & RX_FLAG_40MHZ)
+ * - short-GI (status->flag & RX_FLAG_SHORT_GI)
+ */
+ *pos = 0;
+ } else {
+ rthdr->it_present |= (1 << IEEE80211_RADIOTAP_RATE);
+ *pos = rate->bitrate / 5;
+ }
pos++;
/* IEEE80211_RADIOTAP_CHANNEL */
@@ -474,6 +486,79 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
}
+#define FRAME_CLASS_1 (1)
+#define FRAME_CLASS_2 (2)
+#define FRAME_CLASS_3 (3)
+
+static unsigned int debug_noinline frame_class_type(struct ieee80211_hdr *hdr, bool is_ibss)
+{
+ unsigned result = FRAME_CLASS_3;
+
+ if (ieee80211_is_mgmt(hdr->frame_control)) {
+ if ((ieee80211_is_assoc_req(hdr->frame_control))
+ || (ieee80211_is_assoc_resp(hdr->frame_control))
+ || (ieee80211_is_reassoc_req(hdr->frame_control))
+ || (ieee80211_is_reassoc_resp(hdr->frame_control))
+ || (ieee80211_is_disassoc(hdr->frame_control))) {
+ result = FRAME_CLASS_2;
+ } else if ((ieee80211_is_probe_req(hdr->frame_control))
+ || (ieee80211_is_probe_resp(hdr->frame_control))
+ || (ieee80211_is_beacon(hdr->frame_control))
+ || (ieee80211_is_auth(hdr->frame_control))
+ || (ieee80211_is_deauth(hdr->frame_control))
+ || (ieee80211_is_atim(hdr->frame_control))
+ || (ieee80211_is_cfend(hdr->frame_control))
+ || ((ieee80211_is_action(hdr->frame_control)) && (is_ibss))) {
+ result = FRAME_CLASS_1;
+ }
+ } else if (ieee80211_is_ctl(hdr->frame_control)) {
+ if ((ieee80211_is_rts(hdr->frame_control))
+ || (ieee80211_is_cts(hdr->frame_control))
+ || (ieee80211_is_ack(hdr->frame_control))
+ || (ieee80211_is_cfend(hdr->frame_control))
+ || (ieee80211_is_cfendack(hdr->frame_control))) {
+ result = FRAME_CLASS_1;
+ }
+ } else if (ieee80211_is_data(hdr->frame_control)) {
+ if ((!ieee80211_has_fromds(hdr->frame_control))
+ && (!ieee80211_has_tods(hdr->frame_control))) {
+ result = FRAME_CLASS_1;
+ }
+ }
+
+ return result;
+}
+
+static void private_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+ u16 stype, u16 reason, u8 *destination)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for "
+ "deauth/disassoc frame\n", sdata->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
+ skb_put(skb, 2);
+ /* u.deauth.reason_code == u.disassoc.reason_code */
+ mgmt->u.deauth.reason_code = cpu_to_le16(reason);
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
{
@@ -510,6 +595,37 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
if (ieee80211_vif_is_mesh(&rx->sdata->vif))
return ieee80211_rx_mesh_check(rx);
+ if ((rx->sdata)
+ && !is_multicast_ether_addr(hdr->addr1)
+ && (rx->sdata->vif.type != NL80211_IFTYPE_ADHOC)
+ && (rx->flags & IEEE80211_RX_RA_MATCH)) {
+ unsigned class_type = frame_class_type(hdr, false);
+
+ switch(class_type) {
+ case FRAME_CLASS_1:
+ default:
+ break;
+ case FRAME_CLASS_2:
+ if ((rx->sta != NULL) && (!test_sta_flags(rx->sta, WLAN_STA_AUTH))) {
+ private_send_deauth_disassoc(rx->sdata,
+ IEEE80211_STYPE_DEAUTH,
+ WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA,
+ hdr->addr1);
+ }
+ break;
+ case FRAME_CLASS_3:
+ if ((rx->sta == NULL) || (!test_sta_flags(rx->sta, WLAN_STA_ASSOC))) {
+ if ((rx->sta != NULL) && (test_sta_flags(rx->sta, WLAN_STA_AUTH))) {
+ private_send_deauth_disassoc(rx->sdata,
+ IEEE80211_STYPE_DISASSOC,
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA,
+ hdr->addr1);
+ }
+ }
+ break;
+ }
+ }
+
if (unlikely((ieee80211_is_data(hdr->frame_control) ||
ieee80211_is_pspoll(hdr->frame_control)) &&
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
@@ -653,13 +769,16 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
static void ap_sta_ps_start(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
- DECLARE_MAC_BUF(mac);
+ struct ieee80211_local *local = sdata->local;
atomic_inc(&sdata->bss->num_sta_ps);
set_and_clear_sta_flags(sta, WLAN_STA_PS, WLAN_STA_PSPOLL);
+ if (local->ops->sta_notify)
+ local->ops->sta_notify(local_to_hw(local), &sdata->vif,
+ STA_NOTIFY_SLEEP, &sta->sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA %s aid %d enters power save mode\n",
- sdata->dev->name, print_mac(mac, sta->sta.addr), sta->sta.aid);
+ printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n",
+ sdata->dev->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
@@ -669,38 +788,37 @@ static int ap_sta_ps_end(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
int sent = 0;
- struct ieee80211_tx_info *info;
- DECLARE_MAC_BUF(mac);
atomic_dec(&sdata->bss->num_sta_ps);
clear_sta_flags(sta, WLAN_STA_PS | WLAN_STA_PSPOLL);
+ if (local->ops->sta_notify)
+ local->ops->sta_notify(local_to_hw(local), &sdata->vif,
+ STA_NOTIFY_AWAKE, &sta->sta);
if (!skb_queue_empty(&sta->ps_tx_buf))
sta_info_clear_tim_bit(sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA %s aid %d exits power save mode\n",
- sdata->dev->name, print_mac(mac, sta->sta.addr), sta->sta.aid);
+ printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n",
+ sdata->dev->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
/* Send all buffered frames to the station */
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
- info = IEEE80211_SKB_CB(skb);
sent++;
- info->flags |= IEEE80211_TX_CTL_REQUEUE;
+ skb->requeue = 1;
dev_queue_xmit(skb);
}
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
- info = IEEE80211_SKB_CB(skb);
local->total_ps_buffered--;
sent++;
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA %s aid %d send PS frame "
+ printk(KERN_DEBUG "%s: STA %pM aid %d send PS frame "
"since STA not sleeping anymore\n", sdata->dev->name,
- print_mac(mac, sta->sta.addr), sta->sta.aid);
+ sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- info->flags |= IEEE80211_TX_CTL_REQUEUE;
+ skb->requeue = 1;
dev_queue_xmit(skb);
}
@@ -745,17 +863,29 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
sta->last_qual = rx->status->qual;
sta->last_noise = rx->status->noise;
+ /*
+ * Change STA power saving mode only at the end of a frame
+ * exchange sequence.
+ */
if (!ieee80211_has_morefrags(hdr->frame_control) &&
(rx->sdata->vif.type == NL80211_IFTYPE_AP ||
rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
- /* Change STA power saving mode only in the end of a frame
- * exchange sequence */
- if (test_sta_flags(sta, WLAN_STA_PS) &&
- !ieee80211_has_pm(hdr->frame_control))
- rx->sent_ps_buffered += ap_sta_ps_end(sta);
- else if (!test_sta_flags(sta, WLAN_STA_PS) &&
- ieee80211_has_pm(hdr->frame_control))
- ap_sta_ps_start(sta);
+ if (test_sta_flags(sta, WLAN_STA_PS)) {
+ /*
+ * Ignore doze->wake transitions that are
+ * indicated by non-data frames, the standard
+ * is unclear here, but for example going to
+ * PS mode and then scanning would cause a
+ * doze->wake transition for the probe request,
+ * and that is clearly undesirable.
+ */
+ if (ieee80211_is_data(hdr->frame_control) &&
+ !ieee80211_has_pm(hdr->frame_control))
+ rx->sent_ps_buffered += ap_sta_ps_end(sta);
+ } else {
+ if (ieee80211_has_pm(hdr->frame_control))
+ ap_sta_ps_start(sta);
+ }
}
/* Drop data::nullfunc frames silently, since they are used only to
@@ -789,15 +919,12 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *) entry->skb_list.next->data;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
printk(KERN_DEBUG "%s: RX reassembly removed oldest "
"fragment entry (idx=%d age=%lu seq=%d last_frag=%d "
- "addr1=%s addr2=%s\n",
+ "addr1=%pM addr2=%pM\n",
sdata->dev->name, idx,
jiffies - entry->first_frag_time, entry->seq,
- entry->last_frag, print_mac(mac, hdr->addr1),
- print_mac(mac2, hdr->addr2));
+ entry->last_frag, hdr->addr1, hdr->addr2);
#endif
__skb_queue_purge(&entry->skb_list);
}
@@ -866,7 +993,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
unsigned int frag, seq;
struct ieee80211_fragment_entry *entry;
struct sk_buff *skb;
- DECLARE_MAC_BUF(mac);
hdr = (struct ieee80211_hdr *)rx->skb->data;
fc = hdr->frame_control;
@@ -970,7 +1096,6 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
struct sk_buff *skb;
int no_pending_pkts;
- DECLARE_MAC_BUF(mac);
__le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control;
if (likely(!rx->sta || !ieee80211_is_pspoll(fc) ||
@@ -1001,8 +1126,8 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "STA %s aid %d: PS Poll (entries after %d)\n",
- print_mac(mac, rx->sta->sta.addr), rx->sta->sta.aid,
+ printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
+ rx->sta->sta.addr, rx->sta->sta.aid,
skb_queue_len(&rx->sta->ps_tx_buf));
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
@@ -1025,9 +1150,9 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
* Should we send it a null-func frame indicating we
* have nothing buffered for it?
*/
- printk(KERN_DEBUG "%s: STA %s sent PS Poll even "
+ printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
"though there are no buffered frames for it\n",
- rx->dev->name, print_mac(mac, rx->sta->sta.addr));
+ rx->dev->name, rx->sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
@@ -1097,10 +1222,6 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
u8 src[ETH_ALEN] __aligned(2);
struct sk_buff *skb = rx->skb;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
- DECLARE_MAC_BUF(mac3);
- DECLARE_MAC_BUF(mac4);
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return -1;
@@ -1279,7 +1400,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
int remaining, err;
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN];
- DECLARE_MAC_BUF(mac);
if (unlikely(!ieee80211_is_data(fc)))
return RX_CONTINUE;
@@ -1552,14 +1672,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
return RX_DROP_MONITOR;
- /*
- * FIXME: revisit this, I'm sure we should handle most
- * of these frames in other modes as well!
- */
- if (sdata->vif.type != NL80211_IFTYPE_STATION &&
- sdata->vif.type != NL80211_IFTYPE_ADHOC)
- return RX_CONTINUE;
-
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_BACK:
switch (mgmt->u.action.u.addba_req.action_code) {
@@ -1632,8 +1744,6 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
{
int keyidx;
unsigned int hdrlen;
- DECLARE_MAC_BUF(mac);
- DECLARE_MAC_BUF(mac2);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (rx->skb->len >= hdrlen + 4)
@@ -1854,10 +1964,15 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
if (!(sdata->dev->flags & IFF_PROMISC))
return 0;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
- } else if (!rx->sta)
- rx->sta = ieee80211_ibss_add_sta(sdata, rx->skb,
- bssid, hdr->addr2,
- BIT(rx->status->rate_idx));
+ } else if (!rx->sta) {
+ int rate_idx;
+ if (rx->status->flag & RX_FLAG_HT)
+ rate_idx = 0; /* TODO: HT rates */
+ else
+ rate_idx = rx->status->rate_idx;
+ rx->sta = ieee80211_ibss_add_sta(sdata, bssid, hdr->addr2,
+ BIT(rate_idx));
+ }
break;
case NL80211_IFTYPE_MESH_POINT:
if (!multicast &&
@@ -2002,17 +2117,17 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
static inline int seq_less(u16 sq1, u16 sq2)
{
- return (((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1));
+ return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
}
static inline u16 seq_inc(u16 sq)
{
- return ((sq + 1) & SEQ_MASK);
+ return (sq + 1) & SEQ_MASK;
}
static inline u16 seq_sub(u16 sq1, u16 sq2)
{
- return ((sq1 - sq2) & SEQ_MASK);
+ return (sq1 - sq2) & SEQ_MASK;
}
@@ -2020,10 +2135,11 @@ static inline u16 seq_sub(u16 sq1, u16 sq2)
* As it function blongs to Rx path it must be called with
* the proper rcu_read_lock protection for its flow.
*/
-u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
- struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff *skb, u16 mpdu_seq_num,
- int bar_req)
+static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ struct sk_buff *skb,
+ u16 mpdu_seq_num,
+ int bar_req)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_rx_status status;
@@ -2062,7 +2178,13 @@ u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
tid_agg_rx->reorder_buf[index]->cb,
sizeof(status));
sband = local->hw.wiphy->bands[status.band];
- rate = &sband->bitrates[status.rate_idx];
+ if (status.flag & RX_FLAG_HT) {
+ /* TODO: HT rates */
+ rate = sband->bitrates;
+ } else {
+ rate = &sband->bitrates
+ [status.rate_idx];
+ }
__ieee80211_rx_handle_packet(hw,
tid_agg_rx->reorder_buf[index],
&status, rate);
@@ -2106,7 +2228,10 @@ u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
memcpy(&status, tid_agg_rx->reorder_buf[index]->cb,
sizeof(status));
sband = local->hw.wiphy->bands[status.band];
- rate = &sband->bitrates[status.rate_idx];
+ if (status.flag & RX_FLAG_HT)
+ rate = sband->bitrates; /* TODO: HT rates */
+ else
+ rate = &sband->bitrates[status.rate_idx];
__ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index],
&status, rate);
tid_agg_rx->stored_mpdu_num--;
@@ -2194,15 +2319,26 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
}
sband = local->hw.wiphy->bands[status->band];
-
- if (!sband ||
- status->rate_idx < 0 ||
- status->rate_idx >= sband->n_bitrates) {
+ if (!sband) {
WARN_ON(1);
return;
}
- rate = &sband->bitrates[status->rate_idx];
+ if (status->flag & RX_FLAG_HT) {
+ /* rate_idx is MCS index */
+ if (WARN_ON(status->rate_idx < 0 ||
+ status->rate_idx >= 76))
+ return;
+ /* HT rates are not in the table - use the highest legacy rate
+ * for now since other parts of mac80211 may not yet be fully
+ * MCS aware. */
+ rate = &sband->bitrates[sband->n_bitrates - 1];
+ } else {
+ if (WARN_ON(status->rate_idx < 0 ||
+ status->rate_idx >= sband->n_bitrates))
+ return;
+ rate = &sband->bitrates[status->rate_idx];
+ }
/*
* key references and virtual interfaces are protected using RCU
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 416bb41099f3..f5c7c3371929 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -159,7 +159,7 @@ ieee80211_rx_mesh_bss_add(struct ieee80211_local *local, u8 *mesh_id, int mesh_i
{
struct ieee80211_bss *bss;
- if (mesh_config_len != MESH_CFG_LEN)
+ if (mesh_config_len != IEEE80211_MESH_CONFIG_LEN)
return NULL;
bss = kzalloc(sizeof(*bss), GFP_ATOMIC);
@@ -448,18 +448,17 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
if (local->hw_scanning) {
local->hw_scanning = false;
- if (ieee80211_hw_config(local))
- printk(KERN_DEBUG "%s: failed to restore operational "
- "channel after scan\n", wiphy_name(local->hw.wiphy));
-
+ /*
+ * Somebody might have requested channel change during scan
+ * that we won't have acted upon, try now. ieee80211_hw_config
+ * will set the flag based on actual changes.
+ */
+ ieee80211_hw_config(local, 0);
goto done;
}
local->sw_scanning = false;
- if (ieee80211_hw_config(local))
- printk(KERN_DEBUG "%s: failed to restore operational "
- "channel after scan\n", wiphy_name(local->hw.wiphy));
-
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
netif_tx_lock_bh(local->mdev);
netif_addr_lock(local->mdev);
@@ -546,12 +545,9 @@ void ieee80211_scan_work(struct work_struct *work)
if (!skip) {
local->scan_channel = chan;
- if (ieee80211_hw_config(local)) {
- printk(KERN_DEBUG "%s: failed to set freq to "
- "%d MHz for scan\n", wiphy_name(local->hw.wiphy),
- chan->center_freq);
+ if (ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_CHANNEL))
skip = 1;
- }
}
/* advance state machine to next channel/band */
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index d254446b85b5..10c5539c20ab 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -137,14 +137,12 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
static void __sta_info_free(struct ieee80211_local *local,
struct sta_info *sta)
{
- DECLARE_MAC_BUF(mbuf);
-
rate_control_free_sta(sta);
rate_control_put(sta->rate_ctrl);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Destroyed STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->sta.addr));
+ printk(KERN_DEBUG "%s: Destroyed STA %pM\n",
+ wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
kfree(sta);
@@ -222,7 +220,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int i;
- DECLARE_MAC_BUF(mbuf);
sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
if (!sta)
@@ -263,8 +260,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sta->tx_filtered);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Allocated STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->sta.addr));
+ printk(KERN_DEBUG "%s: Allocated STA %pM\n",
+ wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
#ifdef CONFIG_MAC80211_MESH
@@ -281,7 +278,6 @@ int sta_info_insert(struct sta_info *sta)
struct ieee80211_sub_if_data *sdata = sta->sdata;
unsigned long flags;
int err = 0;
- DECLARE_MAC_BUF(mac);
/*
* Can't be a WARN_ON because it can be triggered through a race:
@@ -294,7 +290,7 @@ int sta_info_insert(struct sta_info *sta)
}
if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->dev->dev_addr) == 0 ||
- is_multicast_ether_addr(sta->sta.addr))) {
+ is_multicast_ether_addr(sta->sta.addr))) {
err = -EINVAL;
goto out_free;
}
@@ -322,8 +318,8 @@ int sta_info_insert(struct sta_info *sta)
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Inserted STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mac, sta->sta.addr));
+ printk(KERN_DEBUG "%s: Inserted STA %pM\n",
+ wiphy_name(local->hw.wiphy), sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
spin_unlock_irqrestore(&local->sta_lock, flags);
@@ -423,9 +419,6 @@ static void __sta_info_unlink(struct sta_info **sta)
{
struct ieee80211_local *local = (*sta)->local;
struct ieee80211_sub_if_data *sdata = (*sta)->sdata;
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- DECLARE_MAC_BUF(mbuf);
-#endif
/*
* pull caller's reference if we're already gone.
*/
@@ -468,8 +461,8 @@ static void __sta_info_unlink(struct sta_info **sta)
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Removed STA %s\n",
- wiphy_name(local->hw.wiphy), print_mac(mbuf, (*sta)->sta.addr));
+ printk(KERN_DEBUG "%s: Removed STA %pM\n",
+ wiphy_name(local->hw.wiphy), (*sta)->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
/*
@@ -544,7 +537,6 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
unsigned long flags;
struct sk_buff *skb;
struct ieee80211_sub_if_data *sdata;
- DECLARE_MAC_BUF(mac);
if (skb_queue_empty(&sta->ps_tx_buf))
return;
@@ -564,8 +556,8 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
sdata = sta->sdata;
local->total_ps_buffered--;
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "Buffered frame expired (STA "
- "%s)\n", print_mac(mac, sta->sta.addr));
+ printk(KERN_DEBUG "Buffered frame expired (STA %pM)\n",
+ sta->sta.addr);
#endif
dev_kfree_skb(skb);
@@ -809,15 +801,14 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta, *tmp;
LIST_HEAD(tmp_list);
- DECLARE_MAC_BUF(mac);
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
if (time_after(jiffies, sta->last_rx + exp_time)) {
#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: expiring inactive STA %s\n",
- sdata->dev->name, print_mac(mac, sta->sta.addr));
+ printk(KERN_DEBUG "%s: expiring inactive STA %pM\n",
+ sdata->dev->name, sta->sta.addr);
#endif
__sta_info_unlink(&sta);
if (sta)
@@ -830,7 +821,7 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
}
struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
- const u8 *addr)
+ const u8 *addr)
{
struct sta_info *sta = sta_info_get(hw_to_local(hw), addr);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 168a39a298bd..e49a5b99cf10 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -160,18 +160,17 @@ struct sta_ampdu_mlme {
* @list: global linked list entry
* @hnext: hash table linked list pointer
* @local: pointer to the global information
- * @sdata: TBD
- * @key: TBD
- * @rate_ctrl: TBD
- * @rate_ctrl_priv: TBD
+ * @sdata: virtual interface this station belongs to
+ * @key: peer key negotiated with this station, if any
+ * @rate_ctrl: rate control algorithm reference
+ * @rate_ctrl_priv: rate control private per-STA pointer
+ * @last_tx_rate: rate used for last transmit, to report to userspace as
+ * "the" transmit rate
* @lock: used for locking all fields that require locking, see comments
* in the header file.
* @flaglock: spinlock for flags accesses
- * @addr: MAC address of this STA
- * @aid: STA's unique AID (1..2007, 0 = not assigned yet),
- * only used in AP (and IBSS?) mode
- * @listen_interval: TBD
- * @pin_status: TBD
+ * @listen_interval: listen interval of this station, when we're acting as AP
+ * @pin_status: used internally for pinning a STA struct into memory
* @flags: STA flags, see &enum ieee80211_sta_info_flags
* @ps_tx_buf: buffer of frames to transmit to this station
* when it leaves power saving state
@@ -180,8 +179,8 @@ struct sta_ampdu_mlme {
* power saving state
* @rx_packets: Number of MSDUs received from this STA
* @rx_bytes: Number of bytes received from this STA
- * @wep_weak_iv_count: TBD
- * @last_rx: TBD
+ * @wep_weak_iv_count: number of weak WEP IVs received from this station
+ * @last_rx: time (in jiffies) when last frame was received from this STA
* @num_duplicates: number of duplicate frames received from this STA
* @rx_fragments: number of received MPDUs
* @rx_dropped: number of dropped MPDUs from this STA
@@ -189,26 +188,25 @@ struct sta_ampdu_mlme {
* @last_qual: qual of last received frame from this STA
* @last_noise: noise of last received frame from this STA
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
- * @tx_filtered_count: TBD
- * @tx_retry_failed: TBD
- * @tx_retry_count: TBD
+ * @tx_filtered_count: number of frames the hardware filtered for this STA
+ * @tx_retry_failed: number of frames that failed retry
+ * @tx_retry_count: total number of retries for frames to this STA
* @fail_avg: moving percentage of failed MSDUs
* @tx_packets: number of RX/TX MSDUs
- * @tx_bytes: TBD
+ * @tx_bytes: number of bytes transmitted to this STA
* @tx_fragments: number of transmitted MPDUs
- * @last_txrate_idx: Index of the last used transmit rate
- * @tid_seq: TBD
- * @ampdu_mlme: TBD
+ * @tid_seq: per-TID sequence numbers for sending to this STA
+ * @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers
* @tid_to_tx_q: map tid to tx queue
* @llid: Local link ID
* @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state
* @plink_retries: Retries in establishment
- * @ignore_plink_timer: TBD
- * @plink_state plink_state: TBD
- * @plink_timeout: TBD
- * @plink_timer: TBD
+ * @ignore_plink_timer: ignore the peer-link timer (used internally)
+ * @plink_state: peer link state
+ * @plink_timeout: timeout of peer link
+ * @plink_timer: peer link watch timer
* @debugfs: debug filesystem info
* @sta: station information we share with the driver
*/
@@ -267,7 +265,7 @@ struct sta_info {
unsigned long tx_packets;
unsigned long tx_bytes;
unsigned long tx_fragments;
- unsigned int last_txrate_idx;
+ struct ieee80211_tx_rate last_tx_rate;
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
/*
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index 34b32bc8f609..38fa111d2dc6 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -263,10 +263,9 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
(iv32 == key->u.tkip.rx[queue].iv32 &&
iv16 <= key->u.tkip.rx[queue].iv16))) {
#ifdef CONFIG_MAC80211_TKIP_DEBUG
- DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "TKIP replay detected for RX frame from "
- "%s (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n",
- print_mac(mac, ta),
+ "%pM (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n",
+ ta,
iv32, iv16, key->u.tkip.rx[queue].iv32,
key->u.tkip.rx[queue].iv16);
#endif
@@ -287,9 +286,8 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
{
int i;
u8 key_offset = NL80211_TKIP_DATA_OFFSET_ENCR_KEY;
- DECLARE_MAC_BUF(mac);
- printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%s"
- " TK=", print_mac(mac, ta));
+ printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%pM"
+ " TK=", ta);
for (i = 0; i < 16; i++)
printk("%02x ",
key->conf.key[key_offset + i]);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1460537faf33..37e3d5ef7e3f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -46,13 +46,20 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
struct ieee80211_local *local = tx->local;
struct ieee80211_supported_band *sband;
struct ieee80211_hdr *hdr;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
+ /* assume HW handles this */
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
+ return 0;
+
+ /* uh huh? */
+ if (WARN_ON_ONCE(info->control.rates[0].idx < 0))
+ return 0;
sband = local->hw.wiphy->bands[tx->channel->band];
- txrate = &sband->bitrates[tx->rate_idx];
+ txrate = &sband->bitrates[info->control.rates[0].idx];
- erp = 0;
- if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
- erp = txrate->flags & IEEE80211_RATE_ERP_G;
+ erp = txrate->flags & IEEE80211_RATE_ERP_G;
/*
* data and mgmt (except PS Poll):
@@ -116,7 +123,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
if (r->bitrate > txrate->bitrate)
break;
- if (tx->sdata->bss_conf.basic_rates & BIT(i))
+ if (tx->sdata->vif.bss_conf.basic_rates & BIT(i))
rate = r->bitrate;
switch (sband->band) {
@@ -150,7 +157,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
* to closest integer */
dur = ieee80211_frame_duration(local, 10, rate, erp,
- tx->sdata->bss_conf.use_short_preamble);
+ tx->sdata->vif.bss_conf.use_short_preamble);
if (next_frag_len) {
/* Frame is fragmented: duration increases with time needed to
@@ -159,7 +166,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
/* next fragment */
dur += ieee80211_frame_duration(local, next_frag_len,
txrate->bitrate, erp,
- tx->sdata->bss_conf.use_short_preamble);
+ tx->sdata->vif.bss_conf.use_short_preamble);
}
return cpu_to_le16(dur);
@@ -201,10 +208,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
tx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
ieee80211_is_data(hdr->frame_control))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- DECLARE_MAC_BUF(mac);
printk(KERN_DEBUG "%s: dropped data frame to not "
- "associated station %s\n",
- tx->dev->name, print_mac(mac, hdr->addr1));
+ "associated station %pM\n",
+ tx->dev->name, hdr->addr1);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);
return TX_DROP;
@@ -331,7 +337,6 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
u32 staflags;
- DECLARE_MAC_BUF(mac);
if (unlikely(!sta || ieee80211_is_probe_resp(hdr->frame_control)))
return TX_CONTINUE;
@@ -341,9 +346,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
if (unlikely((staflags & WLAN_STA_PS) &&
!(staflags & WLAN_STA_PSPOLL))) {
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "STA %s aid %d: PS buffer (entries "
+ printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries "
"before %d)\n",
- print_mac(mac, sta->sta.addr), sta->sta.aid,
+ sta->sta.addr, sta->sta.aid,
skb_queue_len(&sta->ps_tx_buf));
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
@@ -352,9 +357,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: STA %s TX "
+ printk(KERN_DEBUG "%s: STA %pM TX "
"buffer full - dropping oldest frame\n",
- tx->dev->name, print_mac(mac, sta->sta.addr));
+ tx->dev->name, sta->sta.addr);
}
#endif
dev_kfree_skb(old);
@@ -371,9 +376,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
}
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
else if (unlikely(test_sta_flags(sta, WLAN_STA_PS))) {
- printk(KERN_DEBUG "%s: STA %s in PS mode, but pspoll "
+ printk(KERN_DEBUG "%s: STA %pM in PS mode, but pspoll "
"set -> send frame\n", tx->dev->name,
- print_mac(mac, sta->sta.addr));
+ sta->sta.addr);
}
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
clear_sta_flags(sta, WLAN_STA_PSPOLL);
@@ -439,140 +444,154 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
{
- struct rate_selection rsel;
- struct ieee80211_supported_band *sband;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+ struct ieee80211_hdr *hdr = (void *)tx->skb->data;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *rate;
+ int i, len;
+ bool inval = false, rts = false, short_preamble = false;
+ struct ieee80211_tx_rate_control txrc;
- sband = tx->local->hw.wiphy->bands[tx->channel->band];
+ memset(&txrc, 0, sizeof(txrc));
- if (likely(tx->rate_idx < 0)) {
- rate_control_get_rate(tx->sdata, sband, tx->sta,
- tx->skb, &rsel);
- if (tx->sta)
- tx->sta->last_txrate_idx = rsel.rate_idx;
- tx->rate_idx = rsel.rate_idx;
- if (unlikely(rsel.probe_idx >= 0)) {
- info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
- info->control.retries[0].rate_idx = tx->rate_idx;
- info->control.retries[0].limit = tx->local->hw.max_altrate_tries;
- tx->rate_idx = rsel.probe_idx;
- } else if (info->control.retries[0].limit == 0)
- info->control.retries[0].rate_idx = -1;
-
- if (unlikely(tx->rate_idx < 0))
- return TX_DROP;
- } else
- info->control.retries[0].rate_idx = -1;
+ sband = tx->local->hw.wiphy->bands[tx->channel->band];
- if (tx->sdata->bss_conf.use_cts_prot &&
- (tx->flags & IEEE80211_TX_FRAGMENTED) && (rsel.nonerp_idx >= 0)) {
- tx->last_frag_rate_idx = tx->rate_idx;
- if (rsel.probe_idx >= 0)
- tx->flags &= ~IEEE80211_TX_PROBE_LAST_FRAG;
- else
- tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
- tx->rate_idx = rsel.nonerp_idx;
- info->tx_rate_idx = rsel.nonerp_idx;
- info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- } else {
- tx->last_frag_rate_idx = tx->rate_idx;
- info->tx_rate_idx = tx->rate_idx;
+ len = min_t(int, tx->skb->len + FCS_LEN,
+ tx->local->fragmentation_threshold);
+
+ /* set up the tx rate control struct we give the RC algo */
+ txrc.hw = local_to_hw(tx->local);
+ txrc.sband = sband;
+ txrc.bss_conf = &tx->sdata->vif.bss_conf;
+ txrc.skb = tx->skb;
+ txrc.reported_rate.idx = -1;
+ txrc.max_rate_idx = tx->sdata->max_ratectrl_rateidx;
+
+ /* set up RTS protection if desired */
+ if (tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD &&
+ len > tx->local->rts_threshold) {
+ txrc.rts = rts = true;
}
- info->tx_rate_idx = tx->rate_idx;
- return TX_CONTINUE;
-}
+ /*
+ * Use short preamble if the BSS can handle it, but not for
+ * management frames unless we know the receiver can handle
+ * that -- the management frame might be to a station that
+ * just wants a probe response.
+ */
+ if (tx->sdata->vif.bss_conf.use_short_preamble &&
+ (ieee80211_is_data(hdr->frame_control) ||
+ (tx->sta && test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE))))
+ txrc.short_preamble = short_preamble = true;
-static ieee80211_tx_result debug_noinline
-ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
- struct ieee80211_supported_band *sband;
- sband = tx->local->hw.wiphy->bands[tx->channel->band];
+ rate_control_get_rate(tx->sdata, tx->sta, &txrc);
+
+ if (unlikely(info->control.rates[0].idx < 0))
+ return TX_DROP;
+
+ if (txrc.reported_rate.idx < 0)
+ txrc.reported_rate = info->control.rates[0];
if (tx->sta)
- info->control.sta = &tx->sta->sta;
+ tx->sta->last_tx_rate = txrc.reported_rate;
- if (!info->control.retry_limit) {
- if (!is_multicast_ether_addr(hdr->addr1)) {
- int len = min_t(int, tx->skb->len + FCS_LEN,
- tx->local->fragmentation_threshold);
- if (len > tx->local->rts_threshold
- && tx->local->rts_threshold <
- IEEE80211_MAX_RTS_THRESHOLD) {
- info->flags |= IEEE80211_TX_CTL_USE_RTS_CTS;
- info->flags |=
- IEEE80211_TX_CTL_LONG_RETRY_LIMIT;
- info->control.retry_limit =
- tx->local->long_retry_limit;
- } else {
- info->control.retry_limit =
- tx->local->short_retry_limit;
- }
- } else {
- info->control.retry_limit = 1;
- }
- }
+ if (unlikely(!info->control.rates[0].count))
+ info->control.rates[0].count = 1;
- if (tx->flags & IEEE80211_TX_FRAGMENTED) {
- /* Do not use multiple retry rates when sending fragmented
- * frames.
- * TODO: The last fragment could still use multiple retry
- * rates. */
- info->control.retries[0].rate_idx = -1;
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ /*
+ * XXX: verify the rate is in the basic rateset
+ */
+ return TX_CONTINUE;
}
- /* Use CTS protection for unicast frames sent using extended rates if
- * there are associated non-ERP stations and RTS/CTS is not configured
- * for the frame. */
- if ((tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) &&
- (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_ERP_G) &&
- (tx->flags & IEEE80211_TX_UNICAST) &&
- tx->sdata->bss_conf.use_cts_prot &&
- !(info->flags & IEEE80211_TX_CTL_USE_RTS_CTS))
- info->flags |= IEEE80211_TX_CTL_USE_CTS_PROTECT;
-
- /* Transmit data frames using short preambles if the driver supports
- * short preambles at the selected rate and short preambles are
- * available on the network at the current point in time. */
- if (ieee80211_is_data(hdr->frame_control) &&
- (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
- tx->sdata->bss_conf.use_short_preamble &&
- (!tx->sta || test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE))) {
- info->flags |= IEEE80211_TX_CTL_SHORT_PREAMBLE;
+ /*
+ * set up the RTS/CTS rate as the fastest basic rate
+ * that is not faster than the data rate
+ *
+ * XXX: Should this check all retry rates?
+ */
+ if (!(info->control.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+ s8 baserate = 0;
+
+ rate = &sband->bitrates[info->control.rates[0].idx];
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ /* must be a basic rate */
+ if (!(tx->sdata->vif.bss_conf.basic_rates & BIT(i)))
+ continue;
+ /* must not be faster than the data rate */
+ if (sband->bitrates[i].bitrate > rate->bitrate)
+ continue;
+ /* maximum */
+ if (sband->bitrates[baserate].bitrate <
+ sband->bitrates[i].bitrate)
+ baserate = i;
+ }
+
+ info->control.rts_cts_rate_idx = baserate;
}
- if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
- (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)) {
- struct ieee80211_rate *rate;
- s8 baserate = -1;
- int idx;
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ /*
+ * make sure there's no valid rate following
+ * an invalid one, just in case drivers don't
+ * take the API seriously to stop at -1.
+ */
+ if (inval) {
+ info->control.rates[i].idx = -1;
+ continue;
+ }
+ if (info->control.rates[i].idx < 0) {
+ inval = true;
+ continue;
+ }
- /* Do not use multiple retry rates when using RTS/CTS */
- info->control.retries[0].rate_idx = -1;
+ /*
+ * For now assume MCS is already set up correctly, this
+ * needs to be fixed.
+ */
+ if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) {
+ WARN_ON(info->control.rates[i].idx > 76);
+ continue;
+ }
- /* Use min(data rate, max base rate) as CTS/RTS rate */
- rate = &sband->bitrates[tx->rate_idx];
+ /* set up RTS protection if desired */
+ if (rts)
+ info->control.rates[i].flags |=
+ IEEE80211_TX_RC_USE_RTS_CTS;
- for (idx = 0; idx < sband->n_bitrates; idx++) {
- if (sband->bitrates[idx].bitrate > rate->bitrate)
- continue;
- if (tx->sdata->bss_conf.basic_rates & BIT(idx) &&
- (baserate < 0 ||
- (sband->bitrates[baserate].bitrate
- < sband->bitrates[idx].bitrate)))
- baserate = idx;
+ /* RC is busted */
+ if (WARN_ON_ONCE(info->control.rates[i].idx >=
+ sband->n_bitrates)) {
+ info->control.rates[i].idx = -1;
+ continue;
}
- if (baserate >= 0)
- info->control.rts_cts_rate_idx = baserate;
- else
- info->control.rts_cts_rate_idx = 0;
+ rate = &sband->bitrates[info->control.rates[i].idx];
+
+ /* set up short preamble */
+ if (short_preamble &&
+ rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
+ info->control.rates[i].flags |=
+ IEEE80211_TX_RC_USE_SHORT_PREAMBLE;
+
+ /* set up G protection */
+ if (!rts && tx->sdata->vif.bss_conf.use_cts_prot &&
+ rate->flags & IEEE80211_RATE_ERP_G)
+ info->control.rates[i].flags |=
+ IEEE80211_TX_RC_USE_CTS_PROTECT;
}
+ return TX_CONTINUE;
+}
+
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
if (tx->sta)
info->control.sta = &tx->sta->sta;
@@ -602,8 +621,18 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
if (ieee80211_hdrlen(hdr->frame_control) < 24)
return TX_CONTINUE;
+ /*
+ * Anything but QoS data that has a sequence number field
+ * (is long enough) gets a sequence number from the global
+ * counter.
+ */
if (!ieee80211_is_data_qos(hdr->frame_control)) {
+ /* driver should assign sequence number */
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
+ /* for pure STA mode without beacons, we can do it */
+ hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number);
+ tx->sdata->sequence_number += 0x10;
+ tx->sdata->sequence_number &= IEEE80211_SCTL_SEQ;
return TX_CONTINUE;
}
@@ -632,6 +661,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
size_t hdrlen, per_fragm, num_fragm, payload_len, left;
struct sk_buff **frags, *first, *frag;
@@ -648,9 +678,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
* This scenario is handled in __ieee80211_tx_prepare but extra
* caution taken here as fragmented ampdu may cause Tx stop.
*/
- if (WARN_ON(tx->flags & IEEE80211_TX_CTL_AMPDU ||
- skb_get_queue_mapping(tx->skb) >=
- ieee80211_num_regular_queues(&tx->local->hw)))
+ if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
return TX_DROP;
first = tx->skb;
@@ -684,21 +712,48 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
IEEE80211_ENCRYPT_TAILROOM);
if (!frag)
goto fail;
+
/* Make sure that all fragments use the same priority so
* that they end up using the same TX queue */
frag->priority = first->priority;
+
skb_reserve(frag, tx->local->tx_headroom +
IEEE80211_ENCRYPT_HEADROOM);
+
+ /* copy TX information */
+ info = IEEE80211_SKB_CB(frag);
+ memcpy(info, first->cb, sizeof(frag->cb));
+
+ /* copy/fill in 802.11 header */
fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
memcpy(fhdr, first->data, hdrlen);
- if (i == num_fragm - 2)
- fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
+
+ if (i == num_fragm - 2) {
+ /* clear MOREFRAGS bit for the last fragment */
+ fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
+ } else {
+ /*
+ * No multi-rate retries for fragmented frames, that
+ * would completely throw off the NAV at other STAs.
+ */
+ info->control.rates[1].idx = -1;
+ info->control.rates[2].idx = -1;
+ info->control.rates[3].idx = -1;
+ info->control.rates[4].idx = -1;
+ BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5);
+ info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
+ }
+
+ /* copy data */
copylen = left > per_fragm ? per_fragm : left;
memcpy(skb_put(frag, copylen), pos, copylen);
- memcpy(frag->cb, first->cb, sizeof(frag->cb));
+
skb_copy_queue_mapping(frag, first);
+
frag->do_not_encrypt = first->do_not_encrypt;
+ frag->dev = first->dev;
+ frag->iif = first->iif;
pos += copylen;
left -= copylen;
@@ -757,12 +812,10 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
tx->extra_frag[0]->len);
for (i = 0; i < tx->num_extra_frag; i++) {
- if (i + 1 < tx->num_extra_frag) {
+ if (i + 1 < tx->num_extra_frag)
next_len = tx->extra_frag[i + 1]->len;
- } else {
+ else
next_len = 0;
- tx->rate_idx = tx->last_frag_rate_idx;
- }
hdr = (struct ieee80211_hdr *)tx->extra_frag[i]->data;
hdr->duration_id = ieee80211_duration(tx, 0, next_len);
@@ -815,7 +868,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
(struct ieee80211_radiotap_header *) skb->data;
struct ieee80211_supported_band *sband;
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
sband = tx->local->hw.wiphy->bands[tx->channel->band];
@@ -829,8 +881,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
*/
while (!ret) {
- int i, target_rate;
-
ret = ieee80211_radiotap_iterator_next(&iterator);
if (ret)
@@ -844,38 +894,6 @@ __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
* get_unaligned((type *)iterator.this_arg) to dereference
* iterator.this_arg for type "type" safely on all arches.
*/
- case IEEE80211_RADIOTAP_RATE:
- /*
- * radiotap rate u8 is in 500kbps units eg, 0x02=1Mbps
- * ieee80211 rate int is in 100kbps units eg, 0x0a=1Mbps
- */
- target_rate = (*iterator.this_arg) * 5;
- for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *r;
-
- r = &sband->bitrates[i];
-
- if (r->bitrate == target_rate) {
- tx->rate_idx = i;
- break;
- }
- }
- break;
-
- case IEEE80211_RADIOTAP_ANTENNA:
- /*
- * radiotap uses 0 for 1st ant, mac80211 is 1 for
- * 1st ant
- */
- info->antenna_sel_tx = (*iterator.this_arg) + 1;
- break;
-
-#if 0
- case IEEE80211_RADIOTAP_DBM_TX_POWER:
- control->power_level = *iterator.this_arg;
- break;
-#endif
-
case IEEE80211_RADIOTAP_FLAGS:
if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FCS) {
/*
@@ -933,7 +951,8 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
struct ieee80211_sub_if_data *sdata;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- int hdrlen;
+ int hdrlen, tid;
+ u8 *qc, *state;
memset(tx, 0, sizeof(*tx));
tx->skb = skb;
@@ -941,8 +960,6 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
tx->local = local;
tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
tx->channel = local->hw.conf.channel;
- tx->rate_idx = -1;
- tx->last_frag_rate_idx = -1;
/*
* Set this flag (used below to indicate "automatic fragmentation"),
* it will be cleared/left by radiotap as desired.
@@ -966,6 +983,15 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
tx->sta = sta_info_get(local, hdr->addr1);
+ if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
+
+ state = &tx->sta->ampdu_mlme.tid_state_tx[tid];
+ if (*state == HT_AGG_STATE_OPERATIONAL)
+ info->flags |= IEEE80211_TX_CTL_AMPDU;
+ }
+
if (is_multicast_ether_addr(hdr->addr1)) {
tx->flags &= ~IEEE80211_TX_UNICAST;
info->flags |= IEEE80211_TX_CTL_NO_ACK;
@@ -977,7 +1003,6 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
if (tx->flags & IEEE80211_TX_FRAGMENTED) {
if ((tx->flags & IEEE80211_TX_UNICAST) &&
skb->len + FCS_LEN > local->fragmentation_threshold &&
- !local->ops->set_frag_threshold &&
!(info->flags & IEEE80211_TX_CTL_AMPDU))
tx->flags |= IEEE80211_TX_FRAGMENTED;
else
@@ -1043,23 +1068,11 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
if (!tx->extra_frag[i])
continue;
info = IEEE80211_SKB_CB(tx->extra_frag[i]);
- info->flags &= ~(IEEE80211_TX_CTL_USE_RTS_CTS |
- IEEE80211_TX_CTL_USE_CTS_PROTECT |
- IEEE80211_TX_CTL_CLEAR_PS_FILT |
+ info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
IEEE80211_TX_CTL_FIRST_FRAGMENT);
if (netif_subqueue_stopped(local->mdev,
tx->extra_frag[i]))
return IEEE80211_TX_FRAG_AGAIN;
- if (i == tx->num_extra_frag) {
- info->tx_rate_idx = tx->last_frag_rate_idx;
-
- if (tx->flags & IEEE80211_TX_PROBE_LAST_FRAG)
- info->flags |=
- IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- else
- info->flags &=
- ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
- }
ret = local->ops->tx(local_to_hw(local),
tx->extra_frag[i]);
@@ -1168,7 +1181,7 @@ retry:
* queues, there's no reason for a driver to reject
* a frame there, warn and drop it.
*/
- if (WARN_ON(queue >= ieee80211_num_regular_queues(&local->hw)))
+ if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
goto drop;
store = &local->pending_packet[queue];
@@ -1196,9 +1209,6 @@ retry:
store->skb = skb;
store->extra_frag = tx.extra_frag;
store->num_extra_frag = tx.num_extra_frag;
- store->last_frag_rate_idx = tx.last_frag_rate_idx;
- store->last_frag_rate_ctrl_probe =
- !!(tx.flags & IEEE80211_TX_PROBE_LAST_FRAG);
}
out:
rcu_read_unlock();
@@ -1299,8 +1309,10 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (is_multicast_ether_addr(hdr->addr3))
memcpy(hdr->addr1, hdr->addr3, ETH_ALEN);
else
- if (mesh_nexthop_lookup(skb, osdata))
- return 0;
+ if (mesh_nexthop_lookup(skb, osdata)) {
+ dev_put(odev);
+ return 0;
+ }
if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0)
IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh,
fwded_frames);
@@ -1333,6 +1345,8 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
list) {
if (!netif_running(sdata->dev))
continue;
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
+ continue;
if (compare_ether_addr(sdata->dev->dev_addr,
hdr->addr2)) {
dev_hold(sdata->dev);
@@ -1465,6 +1479,19 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
goto fail;
}
+ if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
+ local->dynamic_ps_timeout > 0) {
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ ieee80211_stop_queues_by_reason(&local->hw,
+ IEEE80211_QUEUE_STOP_REASON_PS);
+ queue_work(local->hw.workqueue,
+ &local->dynamic_ps_disable_work);
+ }
+
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ }
+
nh_pos = skb_network_header(skb) - skb->data;
h_pos = skb_transport_header(skb) - skb->data;
@@ -1593,12 +1620,10 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
compare_ether_addr(dev->dev_addr,
skb->data + ETH_ALEN) == 0))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- DECLARE_MAC_BUF(mac);
-
if (net_ratelimit())
- printk(KERN_DEBUG "%s: dropped frame to %s"
+ printk(KERN_DEBUG "%s: dropped frame to %pM"
" (unauthorized port)\n", dev->name,
- print_mac(mac, hdr.addr1));
+ hdr.addr1);
#endif
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
@@ -1757,10 +1782,7 @@ void ieee80211_tx_pending(unsigned long data)
store = &local->pending_packet[i];
tx.extra_frag = store->extra_frag;
tx.num_extra_frag = store->num_extra_frag;
- tx.last_frag_rate_idx = store->last_frag_rate_idx;
tx.flags = 0;
- if (store->last_frag_rate_ctrl_probe)
- tx.flags |= IEEE80211_TX_PROBE_LAST_FRAG;
ret = __ieee80211_tx(local, store->skb, &tx);
if (ret) {
if (ret == IEEE80211_TX_FRAG_AGAIN)
@@ -1775,8 +1797,7 @@ void ieee80211_tx_pending(unsigned long data)
/* functions for drivers to get certain frames */
-static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
- struct ieee80211_if_ap *bss,
+static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss,
struct sk_buff *skb,
struct beacon_data *beacon)
{
@@ -1844,11 +1865,9 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
struct ieee80211_local *local = hw_to_local(hw);
struct sk_buff *skb = NULL;
struct ieee80211_tx_info *info;
- struct net_device *bdev;
struct ieee80211_sub_if_data *sdata = NULL;
struct ieee80211_if_ap *ap = NULL;
struct ieee80211_if_sta *ifsta = NULL;
- struct rate_selection rsel;
struct beacon_data *beacon;
struct ieee80211_supported_band *sband;
enum ieee80211_band band = local->hw.conf.channel->band;
@@ -1858,7 +1877,6 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
rcu_read_lock();
sdata = vif_to_sdata(vif);
- bdev = sdata->dev;
if (sdata->vif.type == NL80211_IFTYPE_AP) {
ap = &sdata->u.ap;
@@ -1886,12 +1904,12 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
* of the tim bitmap in mac80211 and the driver.
*/
if (local->tim_in_locked_section) {
- ieee80211_beacon_add_tim(local, ap, skb, beacon);
+ ieee80211_beacon_add_tim(ap, skb, beacon);
} else {
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
- ieee80211_beacon_add_tim(local, ap, skb, beacon);
+ ieee80211_beacon_add_tim(ap, skb, beacon);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
@@ -1952,33 +1970,23 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
skb->do_not_encrypt = 1;
info->band = band;
- rate_control_get_rate(sdata, sband, NULL, skb, &rsel);
-
- if (unlikely(rsel.rate_idx < 0)) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: ieee80211_beacon_get: "
- "no rate found\n",
- wiphy_name(local->hw.wiphy));
- }
- dev_kfree_skb_any(skb);
- skb = NULL;
- goto out;
- }
+ /*
+ * XXX: For now, always use the lowest rate
+ */
+ info->control.rates[0].idx = 0;
+ info->control.rates[0].count = 1;
+ info->control.rates[1].idx = -1;
+ info->control.rates[2].idx = -1;
+ info->control.rates[3].idx = -1;
+ info->control.rates[4].idx = -1;
+ BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5);
info->control.vif = vif;
- info->tx_rate_idx = rsel.rate_idx;
info->flags |= IEEE80211_TX_CTL_NO_ACK;
info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
- if (sdata->bss_conf.use_short_preamble &&
- sband->bitrates[rsel.rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE)
- info->flags |= IEEE80211_TX_CTL_SHORT_PREAMBLE;
-
- info->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
- info->control.retry_limit = 1;
-
-out:
+ out:
rcu_read_unlock();
return skb;
}
@@ -2023,14 +2031,12 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
struct sk_buff *skb = NULL;
struct sta_info *sta;
struct ieee80211_tx_data tx;
- struct net_device *bdev;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_ap *bss = NULL;
struct beacon_data *beacon;
struct ieee80211_tx_info *info;
sdata = vif_to_sdata(vif);
- bdev = sdata->dev;
bss = &sdata->u.ap;
if (!bss)
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index cee4884b9d06..fb89e1d0aa03 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -239,7 +239,7 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
erp = 0;
if (vif) {
sdata = vif_to_sdata(vif);
- short_preamble = sdata->bss_conf.use_short_preamble;
+ short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
}
@@ -272,7 +272,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
erp = 0;
if (vif) {
sdata = vif_to_sdata(vif);
- short_preamble = sdata->bss_conf.use_short_preamble;
+ short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
}
@@ -312,7 +312,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
erp = 0;
if (vif) {
sdata = vif_to_sdata(vif);
- short_preamble = sdata->bss_conf.use_short_preamble;
+ short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
}
@@ -330,10 +330,20 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_ctstoself_duration);
-void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
+ /* we don't need to track ampdu queues */
+ if (queue < ieee80211_num_regular_queues(hw)) {
+ __clear_bit(reason, &local->queue_stop_reasons[queue]);
+
+ if (local->queue_stop_reasons[queue] != 0)
+ /* someone still has this queue stopped */
+ return;
+ }
+
if (test_bit(queue, local->queues_pending)) {
set_bit(queue, local->queues_pending_run);
tasklet_schedule(&local->tx_pending_tasklet);
@@ -341,22 +351,74 @@ void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
netif_wake_subqueue(local->mdev, queue);
}
}
+
+void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ __ieee80211_wake_queue(hw, queue, reason);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
+{
+ ieee80211_wake_queue_by_reason(hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
+}
EXPORT_SYMBOL(ieee80211_wake_queue);
-void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
+ /* we don't need to track ampdu queues */
+ if (queue < ieee80211_num_regular_queues(hw))
+ __set_bit(reason, &local->queue_stop_reasons[queue]);
+
netif_stop_subqueue(local->mdev, queue);
}
+
+void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ __ieee80211_stop_queue(hw, queue, reason);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
+{
+ ieee80211_stop_queue_by_reason(hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
+}
EXPORT_SYMBOL(ieee80211_stop_queue);
-void ieee80211_stop_queues(struct ieee80211_hw *hw)
+void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason)
{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
int i;
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
for (i = 0; i < ieee80211_num_queues(hw); i++)
- ieee80211_stop_queue(hw, i);
+ __ieee80211_stop_queue(hw, i, reason);
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_stop_queues(struct ieee80211_hw *hw)
+{
+ ieee80211_stop_queues_by_reason(hw,
+ IEEE80211_QUEUE_STOP_REASON_DRIVER);
}
EXPORT_SYMBOL(ieee80211_stop_queues);
@@ -367,12 +429,24 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
}
EXPORT_SYMBOL(ieee80211_queue_stopped);
-void ieee80211_wake_queues(struct ieee80211_hw *hw)
+void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+ enum queue_stop_reason reason)
{
+ struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
int i;
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
- ieee80211_wake_queue(hw, i);
+ __ieee80211_wake_queue(hw, i, reason);
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+}
+
+void ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+ ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_DRIVER);
}
EXPORT_SYMBOL(ieee80211_wake_queues);
@@ -532,8 +606,8 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
if (elen >= sizeof(struct ieee80211_ht_cap))
elems->ht_cap_elem = (void *)pos;
break;
- case WLAN_EID_HT_EXTRA_INFO:
- if (elen >= sizeof(struct ieee80211_ht_addt_info))
+ case WLAN_EID_HT_INFORMATION:
+ if (elen >= sizeof(struct ieee80211_ht_info))
elems->ht_info_elem = (void *)pos;
break;
case WLAN_EID_MESH_ID:
@@ -638,19 +712,16 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freqMHz)
if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
- chan->flags & IEEE80211_CHAN_NO_IBSS) {
- printk(KERN_DEBUG "%s: IBSS not allowed on frequency "
- "%d MHz\n", sdata->dev->name, chan->center_freq);
+ chan->flags & IEEE80211_CHAN_NO_IBSS)
return ret;
- }
local->oper_channel = chan;
+ local->oper_channel_type = NL80211_CHAN_NO_HT;
if (local->sw_scanning || local->hw_scanning)
ret = 0;
else
- ret = ieee80211_hw_config(local);
-
- rate_control_clear(local);
+ ret = ieee80211_hw_config(
+ local, IEEE80211_CONF_CHANGE_CHANNEL);
}
return ret;
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index f0e2d3ecb5c4..7043ddc75498 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -17,6 +17,7 @@
#include <linux/err.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
+#include <asm/unaligned.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
@@ -49,17 +50,19 @@ void ieee80211_wep_free(struct ieee80211_local *local)
crypto_free_blkcipher(local->wep_rx_tfm);
}
-static inline int ieee80211_wep_weak_iv(u32 iv, int keylen)
+static inline bool ieee80211_wep_weak_iv(u32 iv, int keylen)
{
- /* Fluhrer, Mantin, and Shamir have reported weaknesses in the
+ /*
+ * Fluhrer, Mantin, and Shamir have reported weaknesses in the
* key scheduling algorithm of RC4. At least IVs (KeyByte + 3,
- * 0xff, N) can be used to speedup attacks, so avoid using them. */
+ * 0xff, N) can be used to speedup attacks, so avoid using them.
+ */
if ((iv & 0xff00) == 0xff00) {
u8 B = (iv >> 16) & 0xff;
if (B >= 3 && B < 3 + keylen)
- return 1;
+ return true;
}
- return 0;
+ return false;
}
@@ -123,10 +126,10 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
{
struct blkcipher_desc desc = { .tfm = tfm };
struct scatterlist sg;
- __le32 *icv;
+ __le32 icv;
- icv = (__le32 *)(data + data_len);
- *icv = cpu_to_le32(~crc32_le(~0, data, data_len));
+ icv = cpu_to_le32(~crc32_le(~0, data, data_len));
+ put_unaligned(icv, (__le32 *)(data + data_len));
crypto_blkcipher_setkey(tfm, rc4key, klen);
sg_init_one(&sg, data, data_len + WEP_ICV_LEN);
@@ -268,7 +271,7 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
}
-u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
+bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
unsigned int hdrlen;
@@ -276,16 +279,13 @@ u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
u32 iv;
if (!ieee80211_has_protected(hdr->frame_control))
- return NULL;
+ return false;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
ivpos = skb->data + hdrlen;
iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2];
- if (ieee80211_wep_weak_iv(iv, key->conf.keylen))
- return ivpos;
-
- return NULL;
+ return ieee80211_wep_weak_iv(iv, key->conf.keylen);
}
ieee80211_rx_result
@@ -329,6 +329,8 @@ static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
ieee80211_tx_result
ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
{
+ int i;
+
ieee80211_tx_set_protected(tx);
if (wep_encrypt_skb(tx, tx->skb) < 0) {
@@ -337,9 +339,8 @@ ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
}
if (tx->extra_frag) {
- int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (wep_encrypt_skb(tx, tx->extra_frag[i]) < 0) {
+ if (wep_encrypt_skb(tx, tx->extra_frag[i])) {
I802_DEBUG_INC(tx->local->
tx_handlers_drop_wep);
return TX_DROP;
diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h
index e587172115b8..d3f0db48314e 100644
--- a/net/mac80211/wep.h
+++ b/net/mac80211/wep.h
@@ -26,7 +26,7 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_key *key);
int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb,
struct ieee80211_key *key);
-u8 *ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
+bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
ieee80211_rx_result
ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx);
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index ab4ddba874be..bfe4a950225a 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -135,48 +135,6 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
return -EOPNOTSUPP;
}
-static int ieee80211_ioctl_giwname(struct net_device *dev,
- struct iw_request_info *info,
- char *name, char *extra)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_supported_band *sband;
- u8 is_ht = 0, is_a = 0, is_b = 0, is_g = 0;
-
-
- sband = local->hw.wiphy->bands[IEEE80211_BAND_5GHZ];
- if (sband) {
- is_a = 1;
- is_ht |= sband->ht_info.ht_supported;
- }
-
- sband = local->hw.wiphy->bands[IEEE80211_BAND_2GHZ];
- if (sband) {
- int i;
- /* Check for mandatory rates */
- for (i = 0; i < sband->n_bitrates; i++) {
- if (sband->bitrates[i].bitrate == 10)
- is_b = 1;
- if (sband->bitrates[i].bitrate == 60)
- is_g = 1;
- }
- is_ht |= sband->ht_info.ht_supported;
- }
-
- strcpy(name, "IEEE 802.11");
- if (is_a)
- strcat(name, "a");
- if (is_b)
- strcat(name, "b");
- if (is_g)
- strcat(name, "g");
- if (is_ht)
- strcat(name, "n");
-
- return 0;
-}
-
-
static int ieee80211_ioctl_giwrange(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *extra)
@@ -185,6 +143,8 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
struct iw_range *range = (struct iw_range *) extra;
enum ieee80211_band band;
int c = 0;
+ int max_power = 0;
+ int i;
data->length = sizeof(struct iw_range);
memset(range, 0, sizeof(struct iw_range));
@@ -230,9 +190,7 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
-
for (band = 0; band < IEEE80211_NUM_BANDS; band ++) {
- int i;
struct ieee80211_supported_band *sband;
sband = local->hw.wiphy->bands[band];
@@ -250,12 +208,44 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
range->freq[c].m = chan->center_freq;
range->freq[c].e = 6;
c++;
+ if (chan->max_power > max_power)
+ max_power = chan->max_power;
+ }
+ }
+
+ for (i=0; i< sband->n_bitrates; i++) {
+ struct ieee80211_rate *brate = &sband->bitrates[i];
+ int j;
+
+ /* avoid duplicating rates from previous bands */
+ for (j = 0; j < range->num_bitrates; j++)
+ if (range->bitrate[j] == brate->bitrate * 100000)
+ break;
+
+ if (j == range->num_bitrates) {
+ range->bitrate[range->num_bitrates] =
+ brate->bitrate * 100000;
+ range->num_bitrates++;
}
}
}
+
range->num_channels = c;
range->num_frequency = c;
+ if (max_power >= IW_MAX_TXPOWER) {
+ range->num_txpower = IW_MAX_TXPOWER;
+ range->txpower[0] = 0;
+ for (i = 1; i < IW_MAX_TXPOWER; i++) {
+ range->txpower[i] = max_power - (IW_MAX_TXPOWER - (i + 1));
+ }
+ } else {
+ range->num_txpower = max_power + 1;
+ for (i = 0; i < max_power; i++) {
+ range->txpower[i] = i;
+ }
+ }
+
IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
@@ -266,78 +256,6 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev,
}
-static int ieee80211_ioctl_siwmode(struct net_device *dev,
- struct iw_request_info *info,
- __u32 *mode, char *extra)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
- int type;
-
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- return -EOPNOTSUPP;
-
- switch (*mode) {
- case IW_MODE_INFRA:
- type = NL80211_IFTYPE_STATION;
- break;
- case IW_MODE_ADHOC:
- /* Setting ad-hoc mode on non ibss channel is not
- * supported.
- */
- if (local->oper_channel &&
- (local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS))
- return -EOPNOTSUPP;
-
- type = NL80211_IFTYPE_ADHOC;
- break;
- case IW_MODE_REPEAT:
- type = NL80211_IFTYPE_WDS;
- break;
- case IW_MODE_MONITOR:
- type = NL80211_IFTYPE_MONITOR;
- break;
- default:
- return -EINVAL;
- }
-
- return ieee80211_if_change_type(sdata, type);
-}
-
-
-static int ieee80211_ioctl_giwmode(struct net_device *dev,
- struct iw_request_info *info,
- __u32 *mode, char *extra)
-{
- struct ieee80211_sub_if_data *sdata;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_AP:
- *mode = IW_MODE_MASTER;
- break;
- case NL80211_IFTYPE_STATION:
- *mode = IW_MODE_INFRA;
- break;
- case NL80211_IFTYPE_ADHOC:
- *mode = IW_MODE_ADHOC;
- break;
- case NL80211_IFTYPE_MONITOR:
- *mode = IW_MODE_MONITOR;
- break;
- case NL80211_IFTYPE_WDS:
- *mode = IW_MODE_REPEAT;
- break;
- case NL80211_IFTYPE_AP_VLAN:
- *mode = IW_MODE_SECOND; /* FIXME */
- break;
- default:
- *mode = IW_MODE_AUTO;
- break;
- }
- return 0;
-}
-
static int ieee80211_ioctl_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
@@ -415,13 +333,6 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
return 0;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- memcpy(sdata->u.ap.ssid, ssid, len);
- memset(sdata->u.ap.ssid + len, 0,
- IEEE80211_MAX_SSID_LEN - len);
- sdata->u.ap.ssid_len = len;
- return ieee80211_if_config(sdata, IEEE80211_IFCC_SSID);
- }
return -EOPNOTSUPP;
}
@@ -445,15 +356,6 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
return res;
}
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
- len = sdata->u.ap.ssid_len;
- if (len > IW_ESSID_MAX_SIZE)
- len = IW_ESSID_MAX_SIZE;
- memcpy(ssid, sdata->u.ap.ssid, len);
- data->length = len;
- data->flags = 1;
- return 0;
- }
return -EOPNOTSUPP;
}
@@ -548,8 +450,7 @@ static int ieee80211_ioctl_siwscan(struct net_device *dev,
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
- sdata->vif.type != NL80211_IFTYPE_AP)
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
/* if SSID was specified explicitly then use that */
@@ -644,8 +545,8 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
sta = sta_info_get(local, sdata->u.sta.bssid);
- if (sta && sta->last_txrate_idx < sband->n_bitrates)
- rate->value = sband->bitrates[sta->last_txrate_idx].bitrate;
+ if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS))
+ rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate;
else
rate->value = 0;
@@ -664,45 +565,35 @@ static int ieee80211_ioctl_siwtxpower(struct net_device *dev,
union iwreq_data *data, char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- bool need_reconfig = 0;
+ struct ieee80211_channel* chan = local->hw.conf.channel;
+ u32 reconf_flags = 0;
int new_power_level;
if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
return -EINVAL;
if (data->txpower.flags & IW_TXPOW_RANGE)
return -EINVAL;
+ if (!chan)
+ return -EINVAL;
- if (data->txpower.fixed) {
- new_power_level = data->txpower.value;
- } else {
- /*
- * Automatic power level. Use maximum power for the current
- * channel. Should be part of rate control.
- */
- struct ieee80211_channel* chan = local->hw.conf.channel;
- if (!chan)
- return -EINVAL;
-
+ if (data->txpower.fixed)
+ new_power_level = min(data->txpower.value, chan->max_power);
+ else /* Automatic power level setting */
new_power_level = chan->max_power;
- }
if (local->hw.conf.power_level != new_power_level) {
local->hw.conf.power_level = new_power_level;
- need_reconfig = 1;
+ reconf_flags |= IEEE80211_CONF_CHANGE_POWER;
}
if (local->hw.conf.radio_enabled != !(data->txpower.disabled)) {
local->hw.conf.radio_enabled = !(data->txpower.disabled);
- need_reconfig = 1;
+ reconf_flags |= IEEE80211_CONF_CHANGE_RADIO_ENABLED;
ieee80211_led_radio(local, local->hw.conf.radio_enabled);
}
- if (need_reconfig) {
- ieee80211_hw_config(local);
- /* The return value of hw_config is not of big interest here,
- * as it doesn't say that it failed because of _this_ config
- * change or something else. Ignore it. */
- }
+ if (reconf_flags)
+ ieee80211_hw_config(local, reconf_flags);
return 0;
}
@@ -779,14 +670,6 @@ static int ieee80211_ioctl_siwfrag(struct net_device *dev,
local->fragmentation_threshold = frag->value & ~0x1;
}
- /* If the wlan card performs fragmentation in hardware/firmware,
- * configure it here */
-
- if (local->ops->set_frag_threshold)
- return local->ops->set_frag_threshold(
- local_to_hw(local),
- local->fragmentation_threshold);
-
return 0;
}
@@ -814,21 +697,16 @@ static int ieee80211_ioctl_siwretry(struct net_device *dev,
(retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
return -EINVAL;
- if (retry->flags & IW_RETRY_MAX)
- local->long_retry_limit = retry->value;
- else if (retry->flags & IW_RETRY_MIN)
- local->short_retry_limit = retry->value;
- else {
- local->long_retry_limit = retry->value;
- local->short_retry_limit = retry->value;
+ if (retry->flags & IW_RETRY_MAX) {
+ local->hw.conf.long_frame_max_tx_count = retry->value;
+ } else if (retry->flags & IW_RETRY_MIN) {
+ local->hw.conf.short_frame_max_tx_count = retry->value;
+ } else {
+ local->hw.conf.long_frame_max_tx_count = retry->value;
+ local->hw.conf.short_frame_max_tx_count = retry->value;
}
- if (local->ops->set_retry_limit) {
- return local->ops->set_retry_limit(
- local_to_hw(local),
- local->short_retry_limit,
- local->long_retry_limit);
- }
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
return 0;
}
@@ -845,14 +723,15 @@ static int ieee80211_ioctl_giwretry(struct net_device *dev,
/* first return min value, iwconfig will ask max value
* later if needed */
retry->flags |= IW_RETRY_LIMIT;
- retry->value = local->short_retry_limit;
- if (local->long_retry_limit != local->short_retry_limit)
+ retry->value = local->hw.conf.short_frame_max_tx_count;
+ if (local->hw.conf.long_frame_max_tx_count !=
+ local->hw.conf.short_frame_max_tx_count)
retry->flags |= IW_RETRY_MIN;
return 0;
}
if (retry->flags & IW_RETRY_MAX) {
retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
- retry->value = local->long_retry_limit;
+ retry->value = local->hw.conf.long_frame_max_tx_count;
}
return 0;
@@ -983,25 +862,56 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
struct iw_param *wrq,
char *extra)
{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_conf *conf = &local->hw.conf;
+ int ret = 0, timeout = 0;
+ bool ps;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EINVAL;
if (wrq->disabled) {
- conf->flags &= ~IEEE80211_CONF_PS;
- return ieee80211_hw_config(local);
+ ps = false;
+ timeout = 0;
+ goto set;
}
switch (wrq->flags & IW_POWER_MODE) {
case IW_POWER_ON: /* If not specified */
case IW_POWER_MODE: /* If set all mask */
case IW_POWER_ALL_R: /* If explicitely state all */
- conf->flags |= IEEE80211_CONF_PS;
+ ps = true;
break;
- default: /* Otherwise we don't support it */
- return -EINVAL;
+ default: /* Otherwise we ignore */
+ break;
+ }
+
+ if (wrq->flags & IW_POWER_TIMEOUT)
+ timeout = wrq->value / 1000;
+
+set:
+ if (ps == local->powersave && timeout == local->dynamic_ps_timeout)
+ return ret;
+
+ local->powersave = ps;
+ local->dynamic_ps_timeout = timeout;
+
+ if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
+ if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS) &&
+ local->dynamic_ps_timeout > 0)
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->dynamic_ps_timeout));
+ else {
+ if (local->powersave)
+ conf->flags |= IEEE80211_CONF_PS;
+ else
+ conf->flags &= ~IEEE80211_CONF_PS;
+ }
+ ret = ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
- return ieee80211_hw_config(local);
+ return ret;
}
static int ieee80211_ioctl_giwpower(struct net_device *dev,
@@ -1010,9 +920,8 @@ static int ieee80211_ioctl_giwpower(struct net_device *dev,
char *extra)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_conf *conf = &local->hw.conf;
- wrqu->power.disabled = !(conf->flags & IEEE80211_CONF_PS);
+ wrqu->power.disabled = !local->powersave;
return 0;
}
@@ -1176,13 +1085,13 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev,
static const iw_handler ieee80211_handler[] =
{
(iw_handler) NULL, /* SIOCSIWCOMMIT */
- (iw_handler) ieee80211_ioctl_giwname, /* SIOCGIWNAME */
+ (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
(iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */
(iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */
- (iw_handler) ieee80211_ioctl_siwmode, /* SIOCSIWMODE */
- (iw_handler) ieee80211_ioctl_giwmode, /* SIOCGIWMODE */
+ (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */
+ (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */
(iw_handler) NULL, /* SIOCSIWSENS */
(iw_handler) NULL, /* SIOCGIWSENS */
(iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 139b5f267b34..ac71b38f7cb5 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -114,8 +114,8 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
{
struct ieee80211_master_priv *mpriv = netdev_priv(dev);
struct ieee80211_local *local = mpriv->local;
+ struct ieee80211_hw *hw = &local->hw;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct sta_info *sta;
u16 queue;
u8 tid;
@@ -124,21 +124,19 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
if (unlikely(queue >= local->hw.queues))
queue = local->hw.queues - 1;
- if (info->flags & IEEE80211_TX_CTL_REQUEUE) {
+ if (skb->requeue) {
+ if (!hw->ampdu_queues)
+ return queue;
+
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
if (sta) {
- struct ieee80211_hw *hw = &local->hw;
int ampdu_queue = sta->tid_to_tx_q[tid];
if ((ampdu_queue < ieee80211_num_queues(hw)) &&
- test_bit(ampdu_queue, local->queue_pool)) {
+ test_bit(ampdu_queue, local->queue_pool))
queue = ampdu_queue;
- info->flags |= IEEE80211_TX_CTL_AMPDU;
- } else {
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- }
}
rcu_read_unlock();
@@ -159,20 +157,18 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
*p++ = ack_policy | tid;
*p = 0;
+ if (!hw->ampdu_queues)
+ return queue;
+
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
if (sta) {
int ampdu_queue = sta->tid_to_tx_q[tid];
- struct ieee80211_hw *hw = &local->hw;
if ((ampdu_queue < ieee80211_num_queues(hw)) &&
- test_bit(ampdu_queue, local->queue_pool)) {
+ test_bit(ampdu_queue, local->queue_pool))
queue = ampdu_queue;
- info->flags |= IEEE80211_TX_CTL_AMPDU;
- } else {
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
- }
}
rcu_read_unlock();
@@ -206,13 +202,11 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
* on the previous queue
* since HT is strict in order */
#ifdef CONFIG_MAC80211_HT_DEBUG
- if (net_ratelimit()) {
- DECLARE_MAC_BUF(mac);
+ if (net_ratelimit())
printk(KERN_DEBUG "allocated aggregation queue"
- " %d tid %d addr %s pool=0x%lX\n",
- i, tid, print_mac(mac, sta->sta.addr),
+ " %d tid %d addr %pM pool=0x%lX\n",
+ i, tid, sta->sta.addr,
local->queue_pool[0]);
- }
#endif /* CONFIG_MAC80211_HT_DEBUG */
return 0;
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 6db649480e8f..7aa63caf8d50 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -49,8 +49,7 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
!(tx->flags & IEEE80211_TX_FRAGMENTED) &&
!(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) &&
!wpa_test) {
- /* hwaccel - with no need for preallocated room for Michael MIC
- */
+ /* hwaccel - with no need for preallocated room for MMIC */
return TX_CONTINUE;
}
@@ -67,8 +66,6 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
#else
authenticator = 1;
#endif
- /* At this point we know we're using ALG_TKIP. To get the MIC key
- * we now will rely on the offset from the ieee80211_key_conf::key */
key_offset = authenticator ?
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY :
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
@@ -90,11 +87,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
u8 mic[MICHAEL_MIC_LEN];
struct sk_buff *skb = rx->skb;
int authenticator = 1, wpa_test = 0;
- DECLARE_MAC_BUF(mac);
- /*
- * No way to verify the MIC if the hardware stripped it
- */
+ /* No way to verify the MIC if the hardware stripped it */
if (rx->status->flag & RX_FLAG_MMIC_STRIPPED)
return RX_CONTINUE;
@@ -116,8 +110,6 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
#else
authenticator = 1;
#endif
- /* At this point we know we're using ALG_TKIP. To get the MIC key
- * we now will rely on the offset from the ieee80211_key_conf::key */
key_offset = authenticator ?
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY :
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
@@ -202,6 +194,7 @@ ieee80211_tx_result
ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
+ int i;
ieee80211_tx_set_protected(tx);
@@ -209,9 +202,8 @@ ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
return TX_DROP;
if (tx->extra_frag) {
- int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (tkip_encrypt_skb(tx, tx->extra_frag[i]) < 0)
+ if (tkip_encrypt_skb(tx, tx->extra_frag[i]))
return TX_DROP;
}
}
@@ -227,7 +219,6 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
int hdrlen, res, hwaccel = 0, wpa_test = 0;
struct ieee80211_key *key = rx->key;
struct sk_buff *skb = rx->skb;
- DECLARE_MAC_BUF(mac);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -350,7 +341,7 @@ static inline void ccmp_pn2hdr(u8 *hdr, u8 *pn, int key_id)
}
-static inline int ccmp_hdr2pn(u8 *pn, u8 *hdr)
+static inline void ccmp_hdr2pn(u8 *pn, u8 *hdr)
{
pn[0] = hdr[7];
pn[1] = hdr[6];
@@ -358,7 +349,6 @@ static inline int ccmp_hdr2pn(u8 *pn, u8 *hdr)
pn[3] = hdr[4];
pn[4] = hdr[1];
pn[5] = hdr[0];
- return (hdr[3] >> 6) & 0x03;
}
@@ -373,7 +363,7 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
!(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
- /* hwaccel - with no need for preallocated room for CCMP "
+ /* hwaccel - with no need for preallocated room for CCMP
* header or MIC fields */
info->control.hw_key = &tx->key->conf;
return 0;
@@ -426,6 +416,7 @@ ieee80211_tx_result
ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
+ int i;
ieee80211_tx_set_protected(tx);
@@ -433,9 +424,8 @@ ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
return TX_DROP;
if (tx->extra_frag) {
- int i;
for (i = 0; i < tx->num_extra_frag; i++) {
- if (ccmp_encrypt_skb(tx, tx->extra_frag[i]) < 0)
+ if (ccmp_encrypt_skb(tx, tx->extra_frag[i]))
return TX_DROP;
}
}
@@ -453,7 +443,6 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
struct sk_buff *skb = rx->skb;
u8 pn[CCMP_PN_LEN];
int data_len;
- DECLARE_MAC_BUF(mac);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -468,7 +457,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
(rx->status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
- (void) ccmp_hdr2pn(pn, skb->data + hdrlen);
+ ccmp_hdr2pn(pn, skb->data + hdrlen);
if (memcmp(pn, key->u.ccmp.rx_pn[rx->queue], CCMP_PN_LEN) <= 0) {
key->u.ccmp.replays++;
@@ -483,9 +472,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
key->u.ccmp.tfm, key->u.ccmp.rx_crypto_buf,
skb->data + hdrlen + CCMP_HDR_LEN, data_len,
skb->data + skb->len - CCMP_MIC_LEN,
- skb->data + hdrlen + CCMP_HDR_LEN)) {
+ skb->data + hdrlen + CCMP_HDR_LEN))
return RX_DROP_UNUSABLE;
- }
}
memcpy(key->u.ccmp.rx_pn[rx->queue], pn, CCMP_PN_LEN);
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 646c7121dbc0..092ae6faccca 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -1,6 +1,15 @@
config CFG80211
tristate "Improved wireless configuration API"
+config CFG80211_REG_DEBUG
+ bool "cfg80211 regulatory debugging"
+ depends on CFG80211
+ default n
+ ---help---
+ You can enable this if you want to debug regulatory changes.
+
+ If unsure, say N.
+
config NL80211
bool "nl80211 new netlink interface support"
depends on CFG80211
@@ -40,6 +49,8 @@ config WIRELESS_OLD_REGULATORY
ieee80211_regdom module parameter. This is being phased out and you
should stop using them ASAP.
+ Note: You will need CRDA if you want 802.11d support
+
Say Y unless you have installed a new userspace application.
Also say Y if have one currently depending on the ieee80211_regdom
module parameter and cannot port it to use the new userspace
@@ -72,3 +83,32 @@ config WIRELESS_EXT_SYSFS
Say Y if you have programs using it, like old versions of
hal.
+
+config LIB80211
+ tristate "Common routines for IEEE802.11 drivers"
+ default n
+ help
+ This options enables a library of common routines used
+ by IEEE802.11 wireless LAN drivers.
+
+ Drivers should select this themselves if needed. Say Y if
+ you want this built into your kernel.
+
+config LIB80211_CRYPT_WEP
+ tristate
+
+config LIB80211_CRYPT_CCMP
+ tristate
+
+config LIB80211_CRYPT_TKIP
+ tristate
+
+config LIB80211_DEBUG
+ bool "lib80211 debugging messages"
+ depends on LIB80211
+ default n
+ ---help---
+ You can enable this if you want verbose debugging messages
+ from lib80211.
+
+ If unsure, say N.
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index b9f943c45f3b..938a334c8dbc 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -1,5 +1,12 @@
obj-$(CONFIG_WIRELESS_EXT) += wext.o
obj-$(CONFIG_CFG80211) += cfg80211.o
+obj-$(CONFIG_LIB80211) += lib80211.o
+obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
+obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
+obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o
+cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
cfg80211-$(CONFIG_NL80211) += nl80211.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 5031db7b275b..b96fc0c3f1c4 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -19,7 +19,6 @@
#include "nl80211.h"
#include "core.h"
#include "sysfs.h"
-#include "reg.h"
/* name for sysfs, %d is appended */
#define PHY_NAME "phy"
@@ -236,8 +235,7 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
mutex_unlock(&cfg80211_drv_mutex);
/* give it a proper name */
- snprintf(drv->wiphy.dev.bus_id, BUS_ID_SIZE,
- PHY_NAME "%d", drv->idx);
+ dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->idx);
mutex_init(&drv->mtx);
mutex_init(&drv->devlist_mtx);
@@ -301,12 +299,10 @@ int wiphy_register(struct wiphy *wiphy)
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
+ mutex_lock(&cfg80211_drv_mutex);
+
/* set up regulatory info */
- mutex_lock(&cfg80211_reg_mutex);
wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE);
- mutex_unlock(&cfg80211_reg_mutex);
-
- mutex_lock(&cfg80211_drv_mutex);
res = device_add(&drv->wiphy.dev);
if (res)
@@ -351,6 +347,10 @@ void wiphy_unregister(struct wiphy *wiphy)
/* unlock again before freeing */
mutex_unlock(&drv->mtx);
+ /* If this device got a regulatory hint tell core its
+ * free to listen now to a new shiny device regulatory hint */
+ reg_device_remove(wiphy);
+
list_del(&drv->list);
device_del(&drv->wiphy.dev);
debugfs_remove(drv->wiphy.debugfsdir);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 771cc5cc7658..f7fb9f413028 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -11,6 +11,7 @@
#include <net/genetlink.h>
#include <net/wireless.h>
#include <net/cfg80211.h>
+#include "reg.h"
struct cfg80211_registered_device {
struct cfg80211_ops *ops;
@@ -21,6 +22,18 @@ struct cfg80211_registered_device {
* any call is in progress */
struct mutex mtx;
+ /* ISO / IEC 3166 alpha2 for which this device is receiving
+ * country IEs on, this can help disregard country IEs from APs
+ * on the same alpha2 quickly. The alpha2 may differ from
+ * cfg80211_regdomain's alpha2 when an intersection has occurred.
+ * If the AP is reconfigured this can also be used to tell us if
+ * the country on the country IE changed. */
+ char country_ie_alpha2[2];
+
+ /* If a Country IE has been received this tells us the environment
+ * which its telling us its in. This defaults to ENVIRON_ANY */
+ enum environment_cap env;
+
/* wiphy index, internal only */
int idx;
diff --git a/net/wireless/lib80211.c b/net/wireless/lib80211.c
new file mode 100644
index 000000000000..97d411f74507
--- /dev/null
+++ b/net/wireless/lib80211.c
@@ -0,0 +1,284 @@
+/*
+ * lib80211 -- common bits for IEEE802.11 drivers
+ *
+ * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com>
+ *
+ * Portions copied from old ieee80211 component, w/ original copyright
+ * notices below:
+ *
+ * Host AP crypto routines
+ *
+ * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
+ * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/ieee80211.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <net/lib80211.h>
+
+#define DRV_NAME "lib80211"
+
+#define DRV_DESCRIPTION "common routines for IEEE802.11 drivers"
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>");
+MODULE_LICENSE("GPL");
+
+struct lib80211_crypto_alg {
+ struct list_head list;
+ struct lib80211_crypto_ops *ops;
+};
+
+static LIST_HEAD(lib80211_crypto_algs);
+static DEFINE_SPINLOCK(lib80211_crypto_lock);
+
+const char *print_ssid(char *buf, const char *ssid, u8 ssid_len)
+{
+ const char *s = ssid;
+ char *d = buf;
+
+ ssid_len = min_t(u8, ssid_len, IEEE80211_MAX_SSID_LEN);
+ while (ssid_len--) {
+ if (isprint(*s)) {
+ *d++ = *s++;
+ continue;
+ }
+
+ *d++ = '\\';
+ if (*s == '\0')
+ *d++ = '0';
+ else if (*s == '\n')
+ *d++ = 'n';
+ else if (*s == '\r')
+ *d++ = 'r';
+ else if (*s == '\t')
+ *d++ = 't';
+ else if (*s == '\\')
+ *d++ = '\\';
+ else
+ d += snprintf(d, 3, "%03o", *s);
+ s++;
+ }
+ *d = '\0';
+ return buf;
+}
+EXPORT_SYMBOL(print_ssid);
+
+int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
+ spinlock_t *lock)
+{
+ memset(info, 0, sizeof(*info));
+
+ info->name = name;
+ info->lock = lock;
+
+ INIT_LIST_HEAD(&info->crypt_deinit_list);
+ setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler,
+ (unsigned long)info);
+
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_crypt_info_init);
+
+void lib80211_crypt_info_free(struct lib80211_crypt_info *info)
+{
+ int i;
+
+ lib80211_crypt_quiescing(info);
+ del_timer_sync(&info->crypt_deinit_timer);
+ lib80211_crypt_deinit_entries(info, 1);
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ struct lib80211_crypt_data *crypt = info->crypt[i];
+ if (crypt) {
+ if (crypt->ops) {
+ crypt->ops->deinit(crypt->priv);
+ module_put(crypt->ops->owner);
+ }
+ kfree(crypt);
+ info->crypt[i] = NULL;
+ }
+ }
+}
+EXPORT_SYMBOL(lib80211_crypt_info_free);
+
+void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, int force)
+{
+ struct lib80211_crypt_data *entry, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(info->lock, flags);
+ list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) {
+ if (atomic_read(&entry->refcnt) != 0 && !force)
+ continue;
+
+ list_del(&entry->list);
+
+ if (entry->ops) {
+ entry->ops->deinit(entry->priv);
+ module_put(entry->ops->owner);
+ }
+ kfree(entry);
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_deinit_entries);
+
+/* After this, crypt_deinit_list won't accept new members */
+void lib80211_crypt_quiescing(struct lib80211_crypt_info *info)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(info->lock, flags);
+ info->crypt_quiesced = 1;
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_quiescing);
+
+void lib80211_crypt_deinit_handler(unsigned long data)
+{
+ struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data;
+ unsigned long flags;
+
+ lib80211_crypt_deinit_entries(info, 0);
+
+ spin_lock_irqsave(info->lock, flags);
+ if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) {
+ printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
+ "deletion list\n", info->name);
+ info->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&info->crypt_deinit_timer);
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_deinit_handler);
+
+void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info,
+ struct lib80211_crypt_data **crypt)
+{
+ struct lib80211_crypt_data *tmp;
+ unsigned long flags;
+
+ if (*crypt == NULL)
+ return;
+
+ tmp = *crypt;
+ *crypt = NULL;
+
+ /* must not run ops->deinit() while there may be pending encrypt or
+ * decrypt operations. Use a list of delayed deinits to avoid needing
+ * locking. */
+
+ spin_lock_irqsave(info->lock, flags);
+ if (!info->crypt_quiesced) {
+ list_add(&tmp->list, &info->crypt_deinit_list);
+ if (!timer_pending(&info->crypt_deinit_timer)) {
+ info->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&info->crypt_deinit_timer);
+ }
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(lib80211_crypt_delayed_deinit);
+
+int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops)
+{
+ unsigned long flags;
+ struct lib80211_crypto_alg *alg;
+
+ alg = kzalloc(sizeof(*alg), GFP_KERNEL);
+ if (alg == NULL)
+ return -ENOMEM;
+
+ alg->ops = ops;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_add(&alg->list, &lib80211_crypto_algs);
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+
+ printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n",
+ ops->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_register_crypto_ops);
+
+int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops)
+{
+ struct lib80211_crypto_alg *alg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_for_each_entry(alg, &lib80211_crypto_algs, list) {
+ if (alg->ops == ops)
+ goto found;
+ }
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return -EINVAL;
+
+ found:
+ printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm "
+ "'%s'\n", ops->name);
+ list_del(&alg->list);
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ kfree(alg);
+ return 0;
+}
+EXPORT_SYMBOL(lib80211_unregister_crypto_ops);
+
+struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name)
+{
+ struct lib80211_crypto_alg *alg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&lib80211_crypto_lock, flags);
+ list_for_each_entry(alg, &lib80211_crypto_algs, list) {
+ if (strcmp(alg->ops->name, name) == 0)
+ goto found;
+ }
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return NULL;
+
+ found:
+ spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
+ return alg->ops;
+}
+EXPORT_SYMBOL(lib80211_get_crypto_ops);
+
+static void *lib80211_crypt_null_init(int keyidx)
+{
+ return (void *)1;
+}
+
+static void lib80211_crypt_null_deinit(void *priv)
+{
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_null = {
+ .name = "NULL",
+ .init = lib80211_crypt_null_init,
+ .deinit = lib80211_crypt_null_deinit,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_init(void)
+{
+ printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION "\n");
+ return lib80211_register_crypto_ops(&lib80211_crypt_null);
+}
+
+static void __exit lib80211_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_null);
+ BUG_ON(!list_empty(&lib80211_crypto_algs));
+}
+
+module_init(lib80211_init);
+module_exit(lib80211_exit);
diff --git a/net/wireless/lib80211_crypt_ccmp.c b/net/wireless/lib80211_crypt_ccmp.c
new file mode 100644
index 000000000000..2301dc1edc4c
--- /dev/null
+++ b/net/wireless/lib80211_crypt_ccmp.c
@@ -0,0 +1,494 @@
+/*
+ * lib80211 crypt: host-based CCMP encryption implementation for lib80211
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <asm/string.h>
+#include <linux/wireless.h>
+
+#include <linux/ieee80211.h>
+
+#include <linux/crypto.h>
+
+#include <net/lib80211.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Host AP crypt: CCMP");
+MODULE_LICENSE("GPL");
+
+#define AES_BLOCK_LEN 16
+#define CCMP_HDR_LEN 8
+#define CCMP_MIC_LEN 8
+#define CCMP_TK_LEN 16
+#define CCMP_PN_LEN 6
+
+struct lib80211_ccmp_data {
+ u8 key[CCMP_TK_LEN];
+ int key_set;
+
+ u8 tx_pn[CCMP_PN_LEN];
+ u8 rx_pn[CCMP_PN_LEN];
+
+ u32 dot11RSNAStatsCCMPFormatErrors;
+ u32 dot11RSNAStatsCCMPReplays;
+ u32 dot11RSNAStatsCCMPDecryptErrors;
+
+ int key_idx;
+
+ struct crypto_cipher *tfm;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
+ tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
+ u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
+};
+
+static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm,
+ const u8 pt[16], u8 ct[16])
+{
+ crypto_cipher_encrypt_one(tfm, ct, pt);
+}
+
+static void *lib80211_ccmp_init(int key_idx)
+{
+ struct lib80211_ccmp_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = key_idx;
+
+ priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tfm)) {
+ printk(KERN_DEBUG "lib80211_crypt_ccmp: could not allocate "
+ "crypto API aes\n");
+ priv->tfm = NULL;
+ goto fail;
+ }
+
+ return priv;
+
+ fail:
+ if (priv) {
+ if (priv->tfm)
+ crypto_free_cipher(priv->tfm);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+static void lib80211_ccmp_deinit(void *priv)
+{
+ struct lib80211_ccmp_data *_priv = priv;
+ if (_priv && _priv->tfm)
+ crypto_free_cipher(_priv->tfm);
+ kfree(priv);
+}
+
+static inline void xor_block(u8 * b, u8 * a, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ b[i] ^= a[i];
+}
+
+static void ccmp_init_blocks(struct crypto_cipher *tfm,
+ struct ieee80211_hdr *hdr,
+ u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0)
+{
+ u8 *pos, qc = 0;
+ size_t aad_len;
+ int a4_included, qc_included;
+ u8 aad[2 * AES_BLOCK_LEN];
+
+ a4_included = ieee80211_has_a4(hdr->frame_control);
+ qc_included = ieee80211_is_data_qos(hdr->frame_control);
+
+ aad_len = 22;
+ if (a4_included)
+ aad_len += 6;
+ if (qc_included) {
+ pos = (u8 *) & hdr->addr4;
+ if (a4_included)
+ pos += 6;
+ qc = *pos & 0x0f;
+ aad_len += 2;
+ }
+
+ /* CCM Initial Block:
+ * Flag (Include authentication header, M=3 (8-octet MIC),
+ * L=1 (2-octet Dlen))
+ * Nonce: 0x00 | A2 | PN
+ * Dlen */
+ b0[0] = 0x59;
+ b0[1] = qc;
+ memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
+ memcpy(b0 + 8, pn, CCMP_PN_LEN);
+ b0[14] = (dlen >> 8) & 0xff;
+ b0[15] = dlen & 0xff;
+
+ /* AAD:
+ * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
+ * A1 | A2 | A3
+ * SC with bits 4..15 (seq#) masked to zero
+ * A4 (if present)
+ * QC (if present)
+ */
+ pos = (u8 *) hdr;
+ aad[0] = 0; /* aad_len >> 8 */
+ aad[1] = aad_len & 0xff;
+ aad[2] = pos[0] & 0x8f;
+ aad[3] = pos[1] & 0xc7;
+ memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
+ pos = (u8 *) & hdr->seq_ctrl;
+ aad[22] = pos[0] & 0x0f;
+ aad[23] = 0; /* all bits masked */
+ memset(aad + 24, 0, 8);
+ if (a4_included)
+ memcpy(aad + 24, hdr->addr4, ETH_ALEN);
+ if (qc_included) {
+ aad[a4_included ? 30 : 24] = qc;
+ /* rest of QC masked */
+ }
+
+ /* Start with the first block and AAD */
+ lib80211_ccmp_aes_encrypt(tfm, b0, auth);
+ xor_block(auth, aad, AES_BLOCK_LEN);
+ lib80211_ccmp_aes_encrypt(tfm, auth, auth);
+ xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
+ lib80211_ccmp_aes_encrypt(tfm, auth, auth);
+ b0[0] &= 0x07;
+ b0[14] = b0[15] = 0;
+ lib80211_ccmp_aes_encrypt(tfm, b0, s0);
+}
+
+static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
+ u8 *aeskey, int keylen, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ int i;
+ u8 *pos;
+
+ if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len)
+ return -1;
+
+ if (aeskey != NULL && keylen >= CCMP_TK_LEN)
+ memcpy(aeskey, key->key, CCMP_TK_LEN);
+
+ pos = skb_push(skb, CCMP_HDR_LEN);
+ memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
+ pos += hdr_len;
+
+ i = CCMP_PN_LEN - 1;
+ while (i >= 0) {
+ key->tx_pn[i]++;
+ if (key->tx_pn[i] != 0)
+ break;
+ i--;
+ }
+
+ *pos++ = key->tx_pn[5];
+ *pos++ = key->tx_pn[4];
+ *pos++ = 0;
+ *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
+ *pos++ = key->tx_pn[3];
+ *pos++ = key->tx_pn[2];
+ *pos++ = key->tx_pn[1];
+ *pos++ = key->tx_pn[0];
+
+ return CCMP_HDR_LEN;
+}
+
+static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ int data_len, i, blocks, last, len;
+ u8 *pos, *mic;
+ struct ieee80211_hdr *hdr;
+ u8 *b0 = key->tx_b0;
+ u8 *b = key->tx_b;
+ u8 *e = key->tx_e;
+ u8 *s0 = key->tx_s0;
+
+ if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len)
+ return -1;
+
+ data_len = skb->len - hdr_len;
+ len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv);
+ if (len < 0)
+ return -1;
+
+ pos = skb->data + hdr_len + CCMP_HDR_LEN;
+ mic = skb_put(skb, CCMP_MIC_LEN);
+ hdr = (struct ieee80211_hdr *)skb->data;
+ ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
+
+ blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Authentication */
+ xor_block(b, pos, len);
+ lib80211_ccmp_aes_encrypt(key->tfm, b, b);
+ /* Encryption, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ lib80211_ccmp_aes_encrypt(key->tfm, b0, e);
+ xor_block(pos, e, len);
+ pos += len;
+ }
+
+ for (i = 0; i < CCMP_MIC_LEN; i++)
+ mic[i] = b[i] ^ s0[i];
+
+ return 0;
+}
+
+/*
+ * deal with seq counter wrapping correctly.
+ * refer to timer_after() for jiffies wrapping handling
+ */
+static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o)
+{
+ u32 iv32_n, iv16_n;
+ u32 iv32_o, iv16_o;
+
+ iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3];
+ iv16_n = (pn_n[4] << 8) | pn_n[5];
+
+ iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3];
+ iv16_o = (pn_o[4] << 8) | pn_o[5];
+
+ if ((s32)iv32_n - (s32)iv32_o < 0 ||
+ (iv32_n == iv32_o && iv16_n <= iv16_o))
+ return 1;
+ return 0;
+}
+
+static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_ccmp_data *key = priv;
+ u8 keyidx, *pos;
+ struct ieee80211_hdr *hdr;
+ u8 *b0 = key->rx_b0;
+ u8 *b = key->rx_b;
+ u8 *a = key->rx_a;
+ u8 pn[6];
+ int i, blocks, last, len;
+ size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
+ u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
+
+ if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -1;
+ }
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: received packet without ExtIV"
+ " flag from %pM\n", hdr->addr2);
+ }
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -2;
+ }
+ keyidx >>= 6;
+ if (key->key_idx != keyidx) {
+ printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
+ "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
+ return -6;
+ }
+ if (!key->key_set) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: received packet from %pM"
+ " with keyid=%d that does not have a configured"
+ " key\n", hdr->addr2, keyidx);
+ }
+ return -3;
+ }
+
+ pn[0] = pos[7];
+ pn[1] = pos[6];
+ pn[2] = pos[5];
+ pn[3] = pos[4];
+ pn[4] = pos[1];
+ pn[5] = pos[0];
+ pos += 8;
+
+ if (ccmp_replay_check(pn, key->rx_pn)) {
+#ifdef CONFIG_LIB80211_DEBUG
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: replay detected: STA=%pM "
+ "previous PN %02x%02x%02x%02x%02x%02x "
+ "received PN %02x%02x%02x%02x%02x%02x\n",
+ hdr->addr2,
+ key->rx_pn[0], key->rx_pn[1], key->rx_pn[2],
+ key->rx_pn[3], key->rx_pn[4], key->rx_pn[5],
+ pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
+ }
+#endif
+ key->dot11RSNAStatsCCMPReplays++;
+ return -4;
+ }
+
+ ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
+ xor_block(mic, b, CCMP_MIC_LEN);
+
+ blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Decrypt, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ lib80211_ccmp_aes_encrypt(key->tfm, b0, b);
+ xor_block(pos, b, len);
+ /* Authentication */
+ xor_block(a, pos, len);
+ lib80211_ccmp_aes_encrypt(key->tfm, a, a);
+ pos += len;
+ }
+
+ if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: decrypt failed: STA="
+ "%pM\n", hdr->addr2);
+ }
+ key->dot11RSNAStatsCCMPDecryptErrors++;
+ return -5;
+ }
+
+ memcpy(key->rx_pn, pn, CCMP_PN_LEN);
+
+ /* Remove hdr and MIC */
+ memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
+ skb_pull(skb, CCMP_HDR_LEN);
+ skb_trim(skb, skb->len - CCMP_MIC_LEN);
+
+ return keyidx;
+}
+
+static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_ccmp_data *data = priv;
+ int keyidx;
+ struct crypto_cipher *tfm = data->tfm;
+
+ keyidx = data->key_idx;
+ memset(data, 0, sizeof(*data));
+ data->key_idx = keyidx;
+ data->tfm = tfm;
+ if (len == CCMP_TK_LEN) {
+ memcpy(data->key, key, CCMP_TK_LEN);
+ data->key_set = 1;
+ if (seq) {
+ data->rx_pn[0] = seq[5];
+ data->rx_pn[1] = seq[4];
+ data->rx_pn[2] = seq[3];
+ data->rx_pn[3] = seq[2];
+ data->rx_pn[4] = seq[1];
+ data->rx_pn[5] = seq[0];
+ }
+ crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN);
+ } else if (len == 0)
+ data->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_ccmp_data *data = priv;
+
+ if (len < CCMP_TK_LEN)
+ return -1;
+
+ if (!data->key_set)
+ return 0;
+ memcpy(key, data->key, CCMP_TK_LEN);
+
+ if (seq) {
+ seq[0] = data->tx_pn[5];
+ seq[1] = data->tx_pn[4];
+ seq[2] = data->tx_pn[3];
+ seq[3] = data->tx_pn[2];
+ seq[4] = data->tx_pn[1];
+ seq[5] = data->tx_pn[0];
+ }
+
+ return CCMP_TK_LEN;
+}
+
+static char *lib80211_ccmp_print_stats(char *p, void *priv)
+{
+ struct lib80211_ccmp_data *ccmp = priv;
+
+ p += sprintf(p, "key[%d] alg=CCMP key_set=%d "
+ "tx_pn=%02x%02x%02x%02x%02x%02x "
+ "rx_pn=%02x%02x%02x%02x%02x%02x "
+ "format_errors=%d replays=%d decrypt_errors=%d\n",
+ ccmp->key_idx, ccmp->key_set,
+ ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2],
+ ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5],
+ ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2],
+ ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5],
+ ccmp->dot11RSNAStatsCCMPFormatErrors,
+ ccmp->dot11RSNAStatsCCMPReplays,
+ ccmp->dot11RSNAStatsCCMPDecryptErrors);
+
+ return p;
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_ccmp = {
+ .name = "CCMP",
+ .init = lib80211_ccmp_init,
+ .deinit = lib80211_ccmp_deinit,
+ .build_iv = lib80211_ccmp_hdr,
+ .encrypt_mpdu = lib80211_ccmp_encrypt,
+ .decrypt_mpdu = lib80211_ccmp_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = lib80211_ccmp_set_key,
+ .get_key = lib80211_ccmp_get_key,
+ .print_stats = lib80211_ccmp_print_stats,
+ .extra_mpdu_prefix_len = CCMP_HDR_LEN,
+ .extra_mpdu_postfix_len = CCMP_MIC_LEN,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_ccmp_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_ccmp);
+}
+
+static void __exit lib80211_crypto_ccmp_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp);
+}
+
+module_init(lib80211_crypto_ccmp_init);
+module_exit(lib80211_crypto_ccmp_exit);
diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c
new file mode 100644
index 000000000000..c36287399d7e
--- /dev/null
+++ b/net/wireless/lib80211_crypt_tkip.c
@@ -0,0 +1,788 @@
+/*
+ * lib80211 crypt: host-based TKIP encryption implementation for lib80211
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/mm.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <asm/string.h>
+
+#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include <linux/crypto.h>
+#include <linux/crc32.h>
+
+#include <net/lib80211.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("lib80211 crypt: TKIP");
+MODULE_LICENSE("GPL");
+
+struct lib80211_tkip_data {
+#define TKIP_KEY_LEN 32
+ u8 key[TKIP_KEY_LEN];
+ int key_set;
+
+ u32 tx_iv32;
+ u16 tx_iv16;
+ u16 tx_ttak[5];
+ int tx_phase1_done;
+
+ u32 rx_iv32;
+ u16 rx_iv16;
+ u16 rx_ttak[5];
+ int rx_phase1_done;
+ u32 rx_iv32_new;
+ u16 rx_iv16_new;
+
+ u32 dot11RSNAStatsTKIPReplays;
+ u32 dot11RSNAStatsTKIPICVErrors;
+ u32 dot11RSNAStatsTKIPLocalMICFailures;
+
+ int key_idx;
+
+ struct crypto_blkcipher *rx_tfm_arc4;
+ struct crypto_hash *rx_tfm_michael;
+ struct crypto_blkcipher *tx_tfm_arc4;
+ struct crypto_hash *tx_tfm_michael;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 rx_hdr[16], tx_hdr[16];
+
+ unsigned long flags;
+};
+
+static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ unsigned long old_flags = _priv->flags;
+ _priv->flags = flags;
+ return old_flags;
+}
+
+static unsigned long lib80211_tkip_get_flags(void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ return _priv->flags;
+}
+
+static void *lib80211_tkip_init(int key_idx)
+{
+ struct lib80211_tkip_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+
+ priv->key_idx = key_idx;
+
+ priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_arc4)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API arc4\n");
+ priv->tx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_michael)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API michael_mic\n");
+ priv->tx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_arc4)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API arc4\n");
+ priv->rx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_michael)) {
+ printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate "
+ "crypto API michael_mic\n");
+ priv->rx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ return priv;
+
+ fail:
+ if (priv) {
+ if (priv->tx_tfm_michael)
+ crypto_free_hash(priv->tx_tfm_michael);
+ if (priv->tx_tfm_arc4)
+ crypto_free_blkcipher(priv->tx_tfm_arc4);
+ if (priv->rx_tfm_michael)
+ crypto_free_hash(priv->rx_tfm_michael);
+ if (priv->rx_tfm_arc4)
+ crypto_free_blkcipher(priv->rx_tfm_arc4);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+static void lib80211_tkip_deinit(void *priv)
+{
+ struct lib80211_tkip_data *_priv = priv;
+ if (_priv) {
+ if (_priv->tx_tfm_michael)
+ crypto_free_hash(_priv->tx_tfm_michael);
+ if (_priv->tx_tfm_arc4)
+ crypto_free_blkcipher(_priv->tx_tfm_arc4);
+ if (_priv->rx_tfm_michael)
+ crypto_free_hash(_priv->rx_tfm_michael);
+ if (_priv->rx_tfm_arc4)
+ crypto_free_blkcipher(_priv->rx_tfm_arc4);
+ }
+ kfree(priv);
+}
+
+static inline u16 RotR1(u16 val)
+{
+ return (val >> 1) | (val << 15);
+}
+
+static inline u8 Lo8(u16 val)
+{
+ return val & 0xff;
+}
+
+static inline u8 Hi8(u16 val)
+{
+ return val >> 8;
+}
+
+static inline u16 Lo16(u32 val)
+{
+ return val & 0xffff;
+}
+
+static inline u16 Hi16(u32 val)
+{
+ return val >> 16;
+}
+
+static inline u16 Mk16(u8 hi, u8 lo)
+{
+ return lo | (((u16) hi) << 8);
+}
+
+static inline u16 Mk16_le(__le16 * v)
+{
+ return le16_to_cpu(*v);
+}
+
+static const u16 Sbox[256] = {
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+static inline u16 _S_(u16 v)
+{
+ u16 t = Sbox[Hi8(v)];
+ return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+#define PHASE1_LOOP_COUNT 8
+
+static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA,
+ u32 IV32)
+{
+ int i, j;
+
+ /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+ TTAK[0] = Lo16(IV32);
+ TTAK[1] = Hi16(IV32);
+ TTAK[2] = Mk16(TA[1], TA[0]);
+ TTAK[3] = Mk16(TA[3], TA[2]);
+ TTAK[4] = Mk16(TA[5], TA[4]);
+
+ for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+ j = 2 * (i & 1);
+ TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+ TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+ TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+ TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+ TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+ }
+}
+
+static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK,
+ u16 IV16)
+{
+ /* Make temporary area overlap WEP seed so that the final copy can be
+ * avoided on little endian hosts. */
+ u16 *PPK = (u16 *) & WEPSeed[4];
+
+ /* Step 1 - make copy of TTAK and bring in TSC */
+ PPK[0] = TTAK[0];
+ PPK[1] = TTAK[1];
+ PPK[2] = TTAK[2];
+ PPK[3] = TTAK[3];
+ PPK[4] = TTAK[4];
+ PPK[5] = TTAK[4] + IV16;
+
+ /* Step 2 - 96-bit bijective mixing using S-box */
+ PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0]));
+ PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2]));
+ PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4]));
+ PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6]));
+ PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8]));
+ PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10]));
+
+ PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12]));
+ PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14]));
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+
+ /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+ * WEPSeed[0..2] is transmitted as WEP IV */
+ WEPSeed[0] = Hi8(IV16);
+ WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+ WEPSeed[2] = Lo8(IV16);
+ WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1);
+
+#ifdef __BIG_ENDIAN
+ {
+ int i;
+ for (i = 0; i < 6; i++)
+ PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
+ }
+#endif
+}
+
+static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
+ u8 * rc4key, int keylen, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ int len;
+ u8 *pos;
+ struct ieee80211_hdr *hdr;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (skb_headroom(skb) < 8 || skb->len < hdr_len)
+ return -1;
+
+ if (rc4key == NULL || keylen < 16)
+ return -1;
+
+ if (!tkey->tx_phase1_done) {
+ tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
+ tkey->tx_iv32);
+ tkey->tx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
+
+ len = skb->len - hdr_len;
+ pos = skb_push(skb, 8);
+ memmove(pos, pos + 8, hdr_len);
+ pos += hdr_len;
+
+ *pos++ = *rc4key;
+ *pos++ = *(rc4key + 1);
+ *pos++ = *(rc4key + 2);
+ *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
+ *pos++ = tkey->tx_iv32 & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 8) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 16) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 24) & 0xff;
+
+ tkey->tx_iv16++;
+ if (tkey->tx_iv16 == 0) {
+ tkey->tx_phase1_done = 0;
+ tkey->tx_iv32++;
+ }
+
+ return 8;
+}
+
+static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 };
+ int len;
+ u8 rc4key[16], *pos, *icv;
+ u32 crc;
+ struct scatterlist sg;
+
+ if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
+ if (net_ratelimit()) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)skb->data;
+ printk(KERN_DEBUG ": TKIP countermeasures: dropped "
+ "TX packet to %pM\n", hdr->addr1);
+ }
+ return -1;
+ }
+
+ if (skb_tailroom(skb) < 4 || skb->len < hdr_len)
+ return -1;
+
+ len = skb->len - hdr_len;
+ pos = skb->data + hdr_len;
+
+ if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
+ return -1;
+
+ icv = skb_put(skb, 4);
+
+ crc = ~crc32_le(~0, pos, len);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16);
+ sg_init_one(&sg, pos, len + 4);
+ return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
+}
+
+/*
+ * deal with seq counter wrapping correctly.
+ * refer to timer_after() for jiffies wrapping handling
+ */
+static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n,
+ u32 iv32_o, u16 iv16_o)
+{
+ if ((s32)iv32_n - (s32)iv32_o < 0 ||
+ (iv32_n == iv32_o && iv16_n <= iv16_o))
+ return 1;
+ return 0;
+}
+
+static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 };
+ u8 rc4key[16];
+ u8 keyidx, *pos;
+ u32 iv32;
+ u16 iv16;
+ struct ieee80211_hdr *hdr;
+ u8 icv[4];
+ u32 crc;
+ struct scatterlist sg;
+ int plen;
+
+ hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG ": TKIP countermeasures: dropped "
+ "received packet from %pM\n", hdr->addr2);
+ }
+ return -1;
+ }
+
+ if (skb->len < hdr_len + 8 + 4)
+ return -1;
+
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: received packet without ExtIV"
+ " flag from %pM\n", hdr->addr2);
+ }
+ return -2;
+ }
+ keyidx >>= 6;
+ if (tkey->key_idx != keyidx) {
+ printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
+ "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
+ return -6;
+ }
+ if (!tkey->key_set) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: received packet from %pM"
+ " with keyid=%d that does not have a configured"
+ " key\n", hdr->addr2, keyidx);
+ }
+ return -3;
+ }
+ iv16 = (pos[0] << 8) | pos[2];
+ iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
+ pos += 8;
+
+ if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
+#ifdef CONFIG_LIB80211_DEBUG
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: replay detected: STA=%pM"
+ " previous TSC %08x%04x received TSC "
+ "%08x%04x\n", hdr->addr2,
+ tkey->rx_iv32, tkey->rx_iv16, iv32, iv16);
+ }
+#endif
+ tkey->dot11RSNAStatsTKIPReplays++;
+ return -4;
+ }
+
+ if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
+ tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
+ tkey->rx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
+
+ plen = skb->len - hdr_len - 12;
+
+ crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16);
+ sg_init_one(&sg, pos, plen + 4);
+ if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG ": TKIP: failed to decrypt "
+ "received packet from %pM\n",
+ hdr->addr2);
+ }
+ return -7;
+ }
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ if (iv32 != tkey->rx_iv32) {
+ /* Previously cached Phase1 result was already lost, so
+ * it needs to be recalculated for the next packet. */
+ tkey->rx_phase1_done = 0;
+ }
+#ifdef CONFIG_LIB80211_DEBUG
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: ICV error detected: STA="
+ "%pM\n", hdr->addr2);
+ }
+#endif
+ tkey->dot11RSNAStatsTKIPICVErrors++;
+ return -5;
+ }
+
+ /* Update real counters only after Michael MIC verification has
+ * completed */
+ tkey->rx_iv32_new = iv32;
+ tkey->rx_iv16_new = iv16;
+
+ /* Remove IV and ICV */
+ memmove(skb->data + 8, skb->data, hdr_len);
+ skb_pull(skb, 8);
+ skb_trim(skb, skb->len - 4);
+
+ return keyidx;
+}
+
+static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr,
+ u8 * data, size_t data_len, u8 * mic)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[2];
+
+ if (tfm_michael == NULL) {
+ printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
+ return -1;
+ }
+ sg_init_table(sg, 2);
+ sg_set_buf(&sg[0], hdr, 16);
+ sg_set_buf(&sg[1], data, data_len);
+
+ if (crypto_hash_setkey(tfm_michael, key, 8))
+ return -1;
+
+ desc.tfm = tfm_michael;
+ desc.flags = 0;
+ return crypto_hash_digest(&desc, sg, data_len + 16, mic);
+}
+
+static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
+{
+ struct ieee80211_hdr *hdr11;
+
+ hdr11 = (struct ieee80211_hdr *)skb->data;
+
+ switch (le16_to_cpu(hdr11->frame_control) &
+ (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+ case IEEE80211_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ case IEEE80211_FCTL_FROMDS:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
+ break;
+ case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
+ break;
+ case 0:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ }
+
+ if (ieee80211_is_data_qos(hdr11->frame_control)) {
+ hdr[12] = le16_to_cpu(*ieee80211_get_qos_ctl(hdr11))
+ & IEEE80211_QOS_CTL_TID_MASK;
+ } else
+ hdr[12] = 0; /* priority */
+
+ hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len,
+ void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ u8 *pos;
+
+ if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
+ printk(KERN_DEBUG "Invalid packet for Michael MIC add "
+ "(tailroom=%d hdr_len=%d skb->len=%d)\n",
+ skb_tailroom(skb), hdr_len, skb->len);
+ return -1;
+ }
+
+ michael_mic_hdr(skb, tkey->tx_hdr);
+ pos = skb_put(skb, 8);
+ if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
+ return -1;
+
+ return 0;
+}
+
+static void lib80211_michael_mic_failure(struct net_device *dev,
+ struct ieee80211_hdr *hdr,
+ int keyidx)
+{
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure ev;
+
+ /* TODO: needed parameters: count, keyid, key type, TSC */
+ memset(&ev, 0, sizeof(ev));
+ ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
+ if (hdr->addr1[0] & 0x01)
+ ev.flags |= IW_MICFAILURE_GROUP;
+ else
+ ev.flags |= IW_MICFAILURE_PAIRWISE;
+ ev.src_addr.sa_family = ARPHRD_ETHER;
+ memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = sizeof(ev);
+ wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev);
+}
+
+static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
+ int hdr_len, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ u8 mic[8];
+
+ if (!tkey->key_set)
+ return -1;
+
+ michael_mic_hdr(skb, tkey->rx_hdr);
+ if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
+ return -1;
+ if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
+ struct ieee80211_hdr *hdr;
+ hdr = (struct ieee80211_hdr *)skb->data;
+ printk(KERN_DEBUG "%s: Michael MIC verification failed for "
+ "MSDU from %pM keyidx=%d\n",
+ skb->dev ? skb->dev->name : "N/A", hdr->addr2,
+ keyidx);
+ if (skb->dev)
+ lib80211_michael_mic_failure(skb->dev, hdr, keyidx);
+ tkey->dot11RSNAStatsTKIPLocalMICFailures++;
+ return -1;
+ }
+
+ /* Update TSC counters for RX now that the packet verification has
+ * completed. */
+ tkey->rx_iv32 = tkey->rx_iv32_new;
+ tkey->rx_iv16 = tkey->rx_iv16_new;
+
+ skb_trim(skb, skb->len - 8);
+
+ return 0;
+}
+
+static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+ int keyidx;
+ struct crypto_hash *tfm = tkey->tx_tfm_michael;
+ struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4;
+ struct crypto_hash *tfm3 = tkey->rx_tfm_michael;
+ struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4;
+
+ keyidx = tkey->key_idx;
+ memset(tkey, 0, sizeof(*tkey));
+ tkey->key_idx = keyidx;
+ tkey->tx_tfm_michael = tfm;
+ tkey->tx_tfm_arc4 = tfm2;
+ tkey->rx_tfm_michael = tfm3;
+ tkey->rx_tfm_arc4 = tfm4;
+ if (len == TKIP_KEY_LEN) {
+ memcpy(tkey->key, key, TKIP_KEY_LEN);
+ tkey->key_set = 1;
+ tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
+ if (seq) {
+ tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
+ (seq[3] << 8) | seq[2];
+ tkey->rx_iv16 = (seq[1] << 8) | seq[0];
+ }
+ } else if (len == 0)
+ tkey->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_tkip_data *tkey = priv;
+
+ if (len < TKIP_KEY_LEN)
+ return -1;
+
+ if (!tkey->key_set)
+ return 0;
+ memcpy(key, tkey->key, TKIP_KEY_LEN);
+
+ if (seq) {
+ /* Return the sequence number of the last transmitted frame. */
+ u16 iv16 = tkey->tx_iv16;
+ u32 iv32 = tkey->tx_iv32;
+ if (iv16 == 0)
+ iv32--;
+ iv16--;
+ seq[0] = tkey->tx_iv16;
+ seq[1] = tkey->tx_iv16 >> 8;
+ seq[2] = tkey->tx_iv32;
+ seq[3] = tkey->tx_iv32 >> 8;
+ seq[4] = tkey->tx_iv32 >> 16;
+ seq[5] = tkey->tx_iv32 >> 24;
+ }
+
+ return TKIP_KEY_LEN;
+}
+
+static char *lib80211_tkip_print_stats(char *p, void *priv)
+{
+ struct lib80211_tkip_data *tkip = priv;
+ p += sprintf(p, "key[%d] alg=TKIP key_set=%d "
+ "tx_pn=%02x%02x%02x%02x%02x%02x "
+ "rx_pn=%02x%02x%02x%02x%02x%02x "
+ "replays=%d icv_errors=%d local_mic_failures=%d\n",
+ tkip->key_idx, tkip->key_set,
+ (tkip->tx_iv32 >> 24) & 0xff,
+ (tkip->tx_iv32 >> 16) & 0xff,
+ (tkip->tx_iv32 >> 8) & 0xff,
+ tkip->tx_iv32 & 0xff,
+ (tkip->tx_iv16 >> 8) & 0xff,
+ tkip->tx_iv16 & 0xff,
+ (tkip->rx_iv32 >> 24) & 0xff,
+ (tkip->rx_iv32 >> 16) & 0xff,
+ (tkip->rx_iv32 >> 8) & 0xff,
+ tkip->rx_iv32 & 0xff,
+ (tkip->rx_iv16 >> 8) & 0xff,
+ tkip->rx_iv16 & 0xff,
+ tkip->dot11RSNAStatsTKIPReplays,
+ tkip->dot11RSNAStatsTKIPICVErrors,
+ tkip->dot11RSNAStatsTKIPLocalMICFailures);
+ return p;
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_tkip = {
+ .name = "TKIP",
+ .init = lib80211_tkip_init,
+ .deinit = lib80211_tkip_deinit,
+ .build_iv = lib80211_tkip_hdr,
+ .encrypt_mpdu = lib80211_tkip_encrypt,
+ .decrypt_mpdu = lib80211_tkip_decrypt,
+ .encrypt_msdu = lib80211_michael_mic_add,
+ .decrypt_msdu = lib80211_michael_mic_verify,
+ .set_key = lib80211_tkip_set_key,
+ .get_key = lib80211_tkip_get_key,
+ .print_stats = lib80211_tkip_print_stats,
+ .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
+ .extra_mpdu_postfix_len = 4, /* ICV */
+ .extra_msdu_postfix_len = 8, /* MIC */
+ .get_flags = lib80211_tkip_get_flags,
+ .set_flags = lib80211_tkip_set_flags,
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_tkip_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_tkip);
+}
+
+static void __exit lib80211_crypto_tkip_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_tkip);
+}
+
+module_init(lib80211_crypto_tkip_init);
+module_exit(lib80211_crypto_tkip_exit);
diff --git a/net/wireless/lib80211_crypt_wep.c b/net/wireless/lib80211_crypt_wep.c
new file mode 100644
index 000000000000..6d41e05ca33b
--- /dev/null
+++ b/net/wireless/lib80211_crypt_wep.c
@@ -0,0 +1,296 @@
+/*
+ * lib80211 crypt: host-based WEP encryption implementation for lib80211
+ *
+ * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008, John W. Linville <linville@tuxdriver.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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <linux/mm.h>
+#include <asm/string.h>
+
+#include <net/lib80211.h>
+
+#include <linux/crypto.h>
+#include <linux/crc32.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("lib80211 crypt: WEP");
+MODULE_LICENSE("GPL");
+
+struct lib80211_wep_data {
+ u32 iv;
+#define WEP_KEY_LEN 13
+ u8 key[WEP_KEY_LEN + 1];
+ u8 key_len;
+ u8 key_idx;
+ struct crypto_blkcipher *tx_tfm;
+ struct crypto_blkcipher *rx_tfm;
+};
+
+static void *lib80211_wep_init(int keyidx)
+{
+ struct lib80211_wep_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = keyidx;
+
+ priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm)) {
+ printk(KERN_DEBUG "lib80211_crypt_wep: could not allocate "
+ "crypto API arc4\n");
+ priv->tx_tfm = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm)) {
+ printk(KERN_DEBUG "lib80211_crypt_wep: could not allocate "
+ "crypto API arc4\n");
+ priv->rx_tfm = NULL;
+ goto fail;
+ }
+ /* start WEP IV from a random value */
+ get_random_bytes(&priv->iv, 4);
+
+ return priv;
+
+ fail:
+ if (priv) {
+ if (priv->tx_tfm)
+ crypto_free_blkcipher(priv->tx_tfm);
+ if (priv->rx_tfm)
+ crypto_free_blkcipher(priv->rx_tfm);
+ kfree(priv);
+ }
+ return NULL;
+}
+
+static void lib80211_wep_deinit(void *priv)
+{
+ struct lib80211_wep_data *_priv = priv;
+ if (_priv) {
+ if (_priv->tx_tfm)
+ crypto_free_blkcipher(_priv->tx_tfm);
+ if (_priv->rx_tfm)
+ crypto_free_blkcipher(_priv->rx_tfm);
+ }
+ kfree(priv);
+}
+
+/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */
+static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len,
+ u8 *key, int keylen, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ u32 klen, len;
+ u8 *pos;
+
+ if (skb_headroom(skb) < 4 || skb->len < hdr_len)
+ return -1;
+
+ len = skb->len - hdr_len;
+ pos = skb_push(skb, 4);
+ memmove(pos, pos + 4, hdr_len);
+ pos += hdr_len;
+
+ klen = 3 + wep->key_len;
+
+ wep->iv++;
+
+ /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
+ * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
+ * can be used to speedup attacks, so avoid using them. */
+ if ((wep->iv & 0xff00) == 0xff00) {
+ u8 B = (wep->iv >> 16) & 0xff;
+ if (B >= 3 && B < klen)
+ wep->iv += 0x0100;
+ }
+
+ /* Prepend 24-bit IV to RC4 key and TX frame */
+ *pos++ = (wep->iv >> 16) & 0xff;
+ *pos++ = (wep->iv >> 8) & 0xff;
+ *pos++ = wep->iv & 0xff;
+ *pos++ = wep->key_idx << 6;
+
+ return 0;
+}
+
+/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
+ * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
+ * so the payload length increases with 8 bytes.
+ *
+ * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
+ */
+static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ struct blkcipher_desc desc = { .tfm = wep->tx_tfm };
+ u32 crc, klen, len;
+ u8 *pos, *icv;
+ struct scatterlist sg;
+ u8 key[WEP_KEY_LEN + 3];
+
+ /* other checks are in lib80211_wep_build_iv */
+ if (skb_tailroom(skb) < 4)
+ return -1;
+
+ /* add the IV to the frame */
+ if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv))
+ return -1;
+
+ /* Copy the IV into the first 3 bytes of the key */
+ skb_copy_from_linear_data_offset(skb, hdr_len, key, 3);
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ len = skb->len - hdr_len - 4;
+ pos = skb->data + hdr_len + 4;
+ klen = 3 + wep->key_len;
+
+ /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */
+ crc = ~crc32_le(~0, pos, len);
+ icv = skb_put(skb, 4);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ crypto_blkcipher_setkey(wep->tx_tfm, key, klen);
+ sg_init_one(&sg, pos, len + 4);
+ return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
+}
+
+/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
+ * the frame: IV (4 bytes), encrypted payload (including SNAP header),
+ * ICV (4 bytes). len includes both IV and ICV.
+ *
+ * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
+ * failure. If frame is OK, IV and ICV will be removed.
+ */
+static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ struct blkcipher_desc desc = { .tfm = wep->rx_tfm };
+ u32 crc, klen, plen;
+ u8 key[WEP_KEY_LEN + 3];
+ u8 keyidx, *pos, icv[4];
+ struct scatterlist sg;
+
+ if (skb->len < hdr_len + 8)
+ return -1;
+
+ pos = skb->data + hdr_len;
+ key[0] = *pos++;
+ key[1] = *pos++;
+ key[2] = *pos++;
+ keyidx = *pos++ >> 6;
+ if (keyidx != wep->key_idx)
+ return -1;
+
+ klen = 3 + wep->key_len;
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ /* Apply RC4 to data and compute CRC32 over decrypted data */
+ plen = skb->len - hdr_len - 8;
+
+ crypto_blkcipher_setkey(wep->rx_tfm, key, klen);
+ sg_init_one(&sg, pos, plen + 4);
+ if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4))
+ return -7;
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ /* ICV mismatch - drop frame */
+ return -2;
+ }
+
+ /* Remove IV and ICV */
+ memmove(skb->data + 4, skb->data, hdr_len);
+ skb_pull(skb, 4);
+ skb_trim(skb, skb->len - 4);
+
+ return 0;
+}
+
+static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+
+ if (len < 0 || len > WEP_KEY_LEN)
+ return -1;
+
+ memcpy(wep->key, key, len);
+ wep->key_len = len;
+
+ return 0;
+}
+
+static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+
+ if (len < wep->key_len)
+ return -1;
+
+ memcpy(key, wep->key, wep->key_len);
+
+ return wep->key_len;
+}
+
+static char *lib80211_wep_print_stats(char *p, void *priv)
+{
+ struct lib80211_wep_data *wep = priv;
+ p += sprintf(p, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len);
+ return p;
+}
+
+static struct lib80211_crypto_ops lib80211_crypt_wep = {
+ .name = "WEP",
+ .init = lib80211_wep_init,
+ .deinit = lib80211_wep_deinit,
+ .build_iv = lib80211_wep_build_iv,
+ .encrypt_mpdu = lib80211_wep_encrypt,
+ .decrypt_mpdu = lib80211_wep_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = lib80211_wep_set_key,
+ .get_key = lib80211_wep_get_key,
+ .print_stats = lib80211_wep_print_stats,
+ .extra_mpdu_prefix_len = 4, /* IV */
+ .extra_mpdu_postfix_len = 4, /* ICV */
+ .owner = THIS_MODULE,
+};
+
+static int __init lib80211_crypto_wep_init(void)
+{
+ return lib80211_register_crypto_ops(&lib80211_crypt_wep);
+}
+
+static void __exit lib80211_crypto_wep_exit(void)
+{
+ lib80211_unregister_crypto_ops(&lib80211_crypt_wep);
+}
+
+module_init(lib80211_crypto_wep_init);
+module_exit(lib80211_crypto_wep_exit);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 572793c8c7ab..31b807af3235 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -58,6 +58,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = BUS_ID_SIZE-1 },
+ [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
@@ -84,7 +87,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
.len = NL80211_MAX_SUPP_RATES },
[NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
[NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
- [NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
[NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_MESH_ID_LEN },
[NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
@@ -95,6 +98,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
+ [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_RATES },
+
+ [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
[NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
.len = NL80211_HT_CAPABILITY_LEN },
@@ -157,6 +164,19 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (!nl_band)
goto nla_put_failure;
+ /* add HT info */
+ if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
+ NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
+ sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
+ &dev->wiphy.bands[band]->ht_cap.mcs);
+ NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
+ dev->wiphy.bands[band]->ht_cap.cap);
+ NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+ dev->wiphy.bands[band]->ht_cap.ampdu_factor);
+ NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+ dev->wiphy.bands[band]->ht_cap.ampdu_density);
+ }
+
/* add frequencies */
nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
if (!nl_freqs)
@@ -180,6 +200,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (chan->flags & IEEE80211_CHAN_RADAR)
NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
+ NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+ DBM_TO_MBM(chan->max_power));
+
nla_nest_end(msg, nl_freq);
}
@@ -269,20 +292,142 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
return -ENOBUFS;
}
+static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
+ [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
+ [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
+ [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
+};
+
+static int parse_txq_params(struct nlattr *tb[],
+ struct ieee80211_txq_params *txq_params)
+{
+ if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
+ !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
+ !tb[NL80211_TXQ_ATTR_AIFS])
+ return -EINVAL;
+
+ txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
+ txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
+ txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
+ txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
+ txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
+
+ return 0;
+}
+
static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev;
- int result;
-
- if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
- return -EINVAL;
+ int result = 0, rem_txq_params = 0;
+ struct nlattr *nl_txq_params;
rdev = cfg80211_get_dev_from_info(info);
if (IS_ERR(rdev))
return PTR_ERR(rdev);
- result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
+ if (info->attrs[NL80211_ATTR_WIPHY_NAME]) {
+ result = cfg80211_dev_rename(
+ rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
+ if (result)
+ goto bad_res;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
+ struct ieee80211_txq_params txq_params;
+ struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
+
+ if (!rdev->ops->set_txq_params) {
+ result = -EOPNOTSUPP;
+ goto bad_res;
+ }
+
+ nla_for_each_nested(nl_txq_params,
+ info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
+ rem_txq_params) {
+ nla_parse(tb, NL80211_TXQ_ATTR_MAX,
+ nla_data(nl_txq_params),
+ nla_len(nl_txq_params),
+ txq_params_policy);
+ result = parse_txq_params(tb, &txq_params);
+ if (result)
+ goto bad_res;
+
+ result = rdev->ops->set_txq_params(&rdev->wiphy,
+ &txq_params);
+ if (result)
+ goto bad_res;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+ struct ieee80211_channel *chan;
+ struct ieee80211_sta_ht_cap *ht_cap;
+ u32 freq, sec_freq;
+
+ if (!rdev->ops->set_channel) {
+ result = -EOPNOTSUPP;
+ goto bad_res;
+ }
+
+ result = -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ channel_type = nla_get_u32(info->attrs[
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+ if (channel_type != NL80211_CHAN_NO_HT &&
+ channel_type != NL80211_CHAN_HT20 &&
+ channel_type != NL80211_CHAN_HT40PLUS &&
+ channel_type != NL80211_CHAN_HT40MINUS)
+ goto bad_res;
+ }
+
+ freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ chan = ieee80211_get_channel(&rdev->wiphy, freq);
+
+ /* Primary channel not allowed */
+ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+ goto bad_res;
+
+ if (channel_type == NL80211_CHAN_HT40MINUS)
+ sec_freq = freq - 20;
+ else if (channel_type == NL80211_CHAN_HT40PLUS)
+ sec_freq = freq + 20;
+ else
+ sec_freq = 0;
+
+ ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
+
+ /* no HT capabilities */
+ if (channel_type != NL80211_CHAN_NO_HT &&
+ !ht_cap->ht_supported)
+ goto bad_res;
+
+ if (sec_freq) {
+ struct ieee80211_channel *schan;
+
+ /* no 40 MHz capabilities */
+ if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
+ (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
+ goto bad_res;
+ schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
+
+ /* Secondary channel not allowed */
+ if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
+ goto bad_res;
+ }
+
+ result = rdev->ops->set_channel(&rdev->wiphy, chan,
+ channel_type);
+ if (result)
+ goto bad_res;
+ }
+
+
+ bad_res:
cfg80211_put_dev(rdev);
return result;
}
@@ -945,12 +1090,46 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags)
return 0;
}
+static u16 nl80211_calculate_bitrate(struct rate_info *rate)
+{
+ int modulation, streams, bitrate;
+
+ if (!(rate->flags & RATE_INFO_FLAGS_MCS))
+ return rate->legacy;
+
+ /* the formula below does only work for MCS values smaller than 32 */
+ if (rate->mcs >= 32)
+ return 0;
+
+ modulation = rate->mcs & 7;
+ streams = (rate->mcs >> 3) + 1;
+
+ bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
+ 13500000 : 6500000;
+
+ if (modulation < 4)
+ bitrate *= (modulation + 1);
+ else if (modulation == 4)
+ bitrate *= (modulation + 2);
+ else
+ bitrate *= (modulation + 3);
+
+ bitrate *= streams;
+
+ if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+ bitrate = (bitrate / 9) * 10;
+
+ /* do NOT round down here */
+ return (bitrate + 50000) / 100000;
+}
+
static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
int flags, struct net_device *dev,
u8 *mac_addr, struct station_info *sinfo)
{
void *hdr;
- struct nlattr *sinfoattr;
+ struct nlattr *sinfoattr, *txrate;
+ u16 bitrate;
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
if (!hdr)
@@ -980,7 +1159,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
if (sinfo->filled & STATION_INFO_PLINK_STATE)
NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
sinfo->plink_state);
+ if (sinfo->filled & STATION_INFO_SIGNAL)
+ NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
+ sinfo->signal);
+ if (sinfo->filled & STATION_INFO_TX_BITRATE) {
+ txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
+ if (!txrate)
+ goto nla_put_failure;
+
+ /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
+ bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
+ if (bitrate > 0)
+ NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
+ NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
+ sinfo->txrate.mcs);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
+ NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
+ if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
+ NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
+
+ nla_nest_end(msg, txrate);
+ }
nla_nest_end(msg, sinfoattr);
return genlmsg_end(msg, hdr);
@@ -1598,6 +1799,12 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
params.use_short_slot_time =
nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
+ if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+ params.basic_rates =
+ nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ params.basic_rates_len =
+ nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+ }
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
@@ -1680,11 +1887,199 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
#endif
mutex_lock(&cfg80211_drv_mutex);
- r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL);
+ r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY);
mutex_unlock(&cfg80211_drv_mutex);
return r;
}
+static int nl80211_get_mesh_params(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct mesh_config cur_params;
+ int err;
+ struct net_device *dev;
+ void *hdr;
+ struct nlattr *pinfoattr;
+ struct sk_buff *msg;
+
+ /* Look up our device */
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->get_mesh_params) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Get the mesh params */
+ rtnl_lock();
+ err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
+ rtnl_unlock();
+ if (err)
+ goto out;
+
+ /* Draw up a netlink message to send back */
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg) {
+ err = -ENOBUFS;
+ goto out;
+ }
+ hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_GET_MESH_PARAMS);
+ if (!hdr)
+ goto nla_put_failure;
+ pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
+ if (!pinfoattr)
+ goto nla_put_failure;
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
+ cur_params.dot11MeshRetryTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
+ cur_params.dot11MeshConfirmTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
+ cur_params.dot11MeshHoldingTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+ cur_params.dot11MeshMaxPeerLinks);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
+ cur_params.dot11MeshMaxRetries);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
+ cur_params.dot11MeshTTL);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+ cur_params.auto_open_plinks);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ cur_params.dot11MeshHWMPmaxPREQretries);
+ NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
+ cur_params.path_refresh_time);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ cur_params.min_discovery_timeout);
+ NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ cur_params.dot11MeshHWMPactivePathTimeout);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ cur_params.dot11MeshHWMPpreqMinInterval);
+ NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ cur_params.dot11MeshHWMPnetDiameterTraversalTime);
+ nla_nest_end(msg, pinfoattr);
+ genlmsg_end(msg, hdr);
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto out;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ err = -EMSGSIZE;
+out:
+ /* Cleanup */
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ return err;
+}
+
+#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
+do {\
+ if (table[attr_num]) {\
+ cfg.param = nla_fn(table[attr_num]); \
+ mask |= (1 << (attr_num - 1)); \
+ } \
+} while (0);\
+
+static struct nla_policy
+nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
+ [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
+
+ [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
+ [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
+ [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
+};
+
+static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ u32 mask;
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct mesh_config cfg;
+ struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
+ struct nlattr *parent_attr;
+
+ parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
+ if (!parent_attr)
+ return -EINVAL;
+ if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
+ parent_attr, nl80211_meshconf_params_policy))
+ return -EINVAL;
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->set_mesh_params) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* This makes sure that there aren't more than 32 mesh config
+ * parameters (otherwise our bitfield scheme would not work.) */
+ BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
+
+ /* Fill in the params struct */
+ mask = 0;
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
+ mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
+ mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
+ mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
+ mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
+ mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
+ mask, NL80211_MESHCONF_TTL, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
+ mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
+ mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+ nla_get_u8);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
+ mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
+ mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+ nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
+ mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+ nla_get_u32);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
+ mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+ nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
+ dot11MeshHWMPnetDiameterTraversalTime,
+ mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ nla_get_u16);
+
+ /* Apply changes */
+ rtnl_lock();
+ err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
+ rtnl_unlock();
+
+ out:
+ /* cleanup */
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ return err;
+}
+
+#undef FILL_IN_MESH_PARAM_IF_SET
+
static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
@@ -1743,12 +2138,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
mutex_lock(&cfg80211_drv_mutex);
r = set_regdom(rd);
mutex_unlock(&cfg80211_drv_mutex);
- if (r)
- goto bad_reg;
-
return r;
-bad_reg:
+ bad_reg:
kfree(rd);
return -EINVAL;
}
@@ -1902,6 +2294,18 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NL80211_CMD_GET_MESH_PARAMS,
+ .doit = nl80211_get_mesh_params,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_SET_MESH_PARAMS,
+ .doit = nl80211_set_mesh_params,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
/* multicast groups */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index eb3b1a9f9b12..22713aa4ea40 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -42,17 +42,40 @@
#include "core.h"
#include "reg.h"
-/* wiphy is set if this request's initiator is REGDOM_SET_BY_DRIVER */
+/**
+ * struct regulatory_request - receipt of last regulatory request
+ *
+ * @wiphy: this is set if this request's initiator is
+ * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This
+ * can be used by the wireless core to deal with conflicts
+ * and potentially inform users of which devices specifically
+ * cased the conflicts.
+ * @initiator: indicates who sent this request, could be any of
+ * of those set in reg_set_by, %REGDOM_SET_BY_*
+ * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested
+ * regulatory domain. We have a few special codes:
+ * 00 - World regulatory domain
+ * 99 - built by driver but a specific alpha2 cannot be determined
+ * 98 - result of an intersection between two regulatory domains
+ * @intersect: indicates whether the wireless core should intersect
+ * the requested regulatory domain with the presently set regulatory
+ * domain.
+ * @country_ie_checksum: checksum of the last processed and accepted
+ * country IE
+ * @country_ie_env: lets us know if the AP is telling us we are outdoor,
+ * indoor, or if it doesn't matter
+ */
struct regulatory_request {
- struct list_head list;
struct wiphy *wiphy;
- int granted;
enum reg_set_by initiator;
char alpha2[2];
+ bool intersect;
+ u32 country_ie_checksum;
+ enum environment_cap country_ie_env;
};
-static LIST_HEAD(regulatory_requests);
-DEFINE_MUTEX(cfg80211_reg_mutex);
+/* Receipt of information from last regulatory request */
+static struct regulatory_request *last_request;
/* To trigger userspace events */
static struct platform_device *reg_pdev;
@@ -63,13 +86,16 @@ static u32 supported_bandwidths[] = {
MHZ_TO_KHZ(20),
};
-static struct list_head regulatory_requests;
-
/* Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
* information to give us an alpha2 */
static const struct ieee80211_regdomain *cfg80211_regdomain;
+/* We use this as a place for the rd structure built from the
+ * last parsed country IE to rest until CRDA gets back to us with
+ * what it thinks should apply for the same country */
+static const struct ieee80211_regdomain *country_ie_regdomain;
+
/* We keep a static world regulatory domain in case of the absence of CRDA */
static const struct ieee80211_regdomain world_regdom = {
.n_reg_rules = 1,
@@ -85,7 +111,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
&world_regdom;
#ifdef CONFIG_WIRELESS_OLD_REGULATORY
-static char *ieee80211_regdom = "US";
+static char *ieee80211_regdom = "";
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
@@ -164,8 +190,14 @@ static const struct ieee80211_regdomain *static_regdom(char *alpha2)
return &jp_regdom;
if (alpha2[0] == 'E' && alpha2[1] == 'U')
return &eu_regdom;
+
+#if 1
+ /* PPH Default to europe now... */
+ return &eu_regdom;
+#else
/* Default, as per the old rules */
return &us_regdom;
+#endif
}
static bool is_old_static_regdom(const struct ieee80211_regdomain *rd)
@@ -204,7 +236,7 @@ static void reset_regdomains(void)
* core upon initialization */
static void update_world_regdomain(const struct ieee80211_regdomain *rd)
{
- BUG_ON(list_empty(&regulatory_requests));
+ BUG_ON(!last_request);
reset_regdomains();
@@ -249,6 +281,18 @@ static bool is_unknown_alpha2(const char *alpha2)
return false;
}
+static bool is_intersected_alpha2(const char *alpha2)
+{
+ if (!alpha2)
+ return false;
+ /* Special case where regulatory domain is the
+ * result of an intersection between two regulatory domain
+ * structures */
+ if (alpha2[0] == '9' && alpha2[1] == '8')
+ return true;
+ return false;
+}
+
static bool is_an_alpha2(const char *alpha2)
{
if (!alpha2)
@@ -277,6 +321,25 @@ static bool regdom_changed(const char *alpha2)
return true;
}
+/**
+ * country_ie_integrity_changes - tells us if the country IE has changed
+ * @checksum: checksum of country IE of fields we are interested in
+ *
+ * If the country IE has not changed you can ignore it safely. This is
+ * useful to determine if two devices are seeing two different country IEs
+ * even on the same alpha2. Note that this will return false if no IE has
+ * been set on the wireless core yet.
+ */
+static bool country_ie_integrity_changes(u32 checksum)
+{
+ /* If no IE has been set then the checksum doesn't change */
+ if (unlikely(!last_request->country_ie_checksum))
+ return false;
+ if (unlikely(last_request->country_ie_checksum != checksum))
+ return true;
+ return false;
+}
+
/* This lets us keep regulatory code which is updated on a regulatory
* basis in userspace. */
static int call_crda(const char *alpha2)
@@ -300,121 +363,13 @@ static int call_crda(const char *alpha2)
return kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, envp);
}
-/* This has the logic which determines when a new request
- * should be ignored. */
-static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
- char *alpha2, struct ieee80211_regdomain *rd)
-{
- struct regulatory_request *last_request = NULL;
-
- /* All initial requests are respected */
- if (list_empty(&regulatory_requests))
- return 0;
-
- last_request = list_first_entry(&regulatory_requests,
- struct regulatory_request, list);
-
- switch (set_by) {
- case REGDOM_SET_BY_INIT:
- return -EINVAL;
- case REGDOM_SET_BY_CORE:
- /* Always respect new wireless core hints, should only
- * come in for updating the world regulatory domain at init
- * anyway */
- return 0;
- case REGDOM_SET_BY_COUNTRY_IE:
- if (last_request->initiator == set_by) {
- if (last_request->wiphy != wiphy) {
- /* Two cards with two APs claiming different
- * different Country IE alpha2s!
- * You're special!! */
- if (!alpha2_equal(last_request->alpha2,
- cfg80211_regdomain->alpha2)) {
- /* XXX: Deal with conflict, consider
- * building a new one out of the
- * intersection */
- WARN_ON(1);
- return -EOPNOTSUPP;
- }
- return -EALREADY;
- }
- /* Two consecutive Country IE hints on the same wiphy */
- if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
- return 0;
- return -EALREADY;
- }
- if (WARN(!is_alpha2_set(alpha2) || !is_an_alpha2(alpha2),
- "Invalid Country IE regulatory hint passed "
- "to the wireless core\n"))
- return -EINVAL;
- /* We ignore Country IE hints for now, as we haven't yet
- * added the dot11MultiDomainCapabilityEnabled flag
- * for wiphys */
- return 1;
- case REGDOM_SET_BY_DRIVER:
- BUG_ON(!wiphy);
- if (last_request->initiator == set_by) {
- /* Two separate drivers hinting different things,
- * this is possible if you have two devices present
- * on a system with different EEPROM regulatory
- * readings. XXX: Do intersection, we support only
- * the first regulatory hint for now */
- if (last_request->wiphy != wiphy)
- return -EALREADY;
- if (rd)
- return -EALREADY;
- /* Driver should not be trying to hint different
- * regulatory domains! */
- BUG_ON(!alpha2_equal(alpha2,
- cfg80211_regdomain->alpha2));
- return -EALREADY;
- }
- if (last_request->initiator == REGDOM_SET_BY_CORE)
- return 0;
- /* XXX: Handle intersection, and add the
- * dot11MultiDomainCapabilityEnabled flag to wiphy. For now
- * we assume the driver has this set to false, following the
- * 802.11d dot11MultiDomainCapabilityEnabled documentation */
- if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
- return 0;
- return 0;
- case REGDOM_SET_BY_USER:
- if (last_request->initiator == set_by ||
- last_request->initiator == REGDOM_SET_BY_CORE)
- return 0;
- /* Drivers can use their wiphy's reg_notifier()
- * to override any information */
- if (last_request->initiator == REGDOM_SET_BY_DRIVER)
- return 0;
- /* XXX: Handle intersection */
- if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
- return -EOPNOTSUPP;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static bool __reg_is_valid_request(const char *alpha2,
- struct regulatory_request **request)
-{
- struct regulatory_request *req;
- if (list_empty(&regulatory_requests))
- return false;
- list_for_each_entry(req, &regulatory_requests, list) {
- if (alpha2_equal(req->alpha2, alpha2)) {
- *request = req;
- return true;
- }
- }
- return false;
-}
-
/* Used by nl80211 before kmalloc'ing our regulatory domain */
bool reg_is_valid_request(const char *alpha2)
{
- struct regulatory_request *request = NULL;
- return __reg_is_valid_request(alpha2, &request);
+ if (!last_request)
+ return false;
+
+ return alpha2_equal(last_request->alpha2, alpha2);
}
/* Sanity check on a regulatory rule */
@@ -423,7 +378,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
const struct ieee80211_freq_range *freq_range = &rule->freq_range;
u32 freq_diff;
- if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0)
+ if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0)
return false;
if (freq_range->start_freq_khz > freq_range->end_freq_khz)
@@ -431,7 +386,8 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
- if (freq_range->max_bandwidth_khz > freq_diff)
+ if (freq_range->end_freq_khz <= freq_range->start_freq_khz ||
+ freq_range->max_bandwidth_khz > freq_diff)
return false;
return true;
@@ -445,6 +401,9 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)
if (!rd->n_reg_rules)
return false;
+ if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
+ return false;
+
for (i = 0; i < rd->n_reg_rules; i++) {
reg_rule = &rd->reg_rules[i];
if (!is_valid_reg_rule(reg_rule))
@@ -469,6 +428,361 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range,
return 0;
}
+/**
+ * freq_in_rule_band - tells us if a frequency is in a frequency band
+ * @freq_range: frequency rule we want to query
+ * @freq_khz: frequency we are inquiring about
+ *
+ * This lets us know if a specific frequency rule is or is not relevant to
+ * a specific frequency's band. Bands are device specific and artificial
+ * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is
+ * safe for now to assume that a frequency rule should not be part of a
+ * frequency's band if the start freq or end freq are off by more than 2 GHz.
+ * This resolution can be lowered and should be considered as we add
+ * regulatory rule support for other "bands".
+ **/
+static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
+ u32 freq_khz)
+{
+#define ONE_GHZ_IN_KHZ 1000000
+ if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+ return true;
+ if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+ return true;
+ return false;
+#undef ONE_GHZ_IN_KHZ
+}
+
+/* Converts a country IE to a regulatory domain. A regulatory domain
+ * structure has a lot of information which the IE doesn't yet have,
+ * so for the other values we use upper max values as we will intersect
+ * with our userspace regulatory agent to get lower bounds. */
+static struct ieee80211_regdomain *country_ie_2_rd(
+ u8 *country_ie,
+ u8 country_ie_len,
+ u32 *checksum)
+{
+ struct ieee80211_regdomain *rd = NULL;
+ unsigned int i = 0;
+ char alpha2[2];
+ u32 flags = 0;
+ u32 num_rules = 0, size_of_regd = 0;
+ u8 *triplets_start = NULL;
+ u8 len_at_triplet = 0;
+ /* the last channel we have registered in a subband (triplet) */
+ int last_sub_max_channel = 0;
+
+ *checksum = 0xDEADBEEF;
+
+ /* Country IE requirements */
+ BUG_ON(country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN ||
+ country_ie_len & 0x01);
+
+ alpha2[0] = country_ie[0];
+ alpha2[1] = country_ie[1];
+
+ /*
+ * Third octet can be:
+ * 'I' - Indoor
+ * 'O' - Outdoor
+ *
+ * anything else we assume is no restrictions
+ */
+ if (country_ie[2] == 'I')
+ flags = NL80211_RRF_NO_OUTDOOR;
+ else if (country_ie[2] == 'O')
+ flags = NL80211_RRF_NO_INDOOR;
+
+ country_ie += 3;
+ country_ie_len -= 3;
+
+ triplets_start = country_ie;
+ len_at_triplet = country_ie_len;
+
+ *checksum ^= ((flags ^ alpha2[0] ^ alpha2[1]) << 8);
+
+ /* We need to build a reg rule for each triplet, but first we must
+ * calculate the number of reg rules we will need. We will need one
+ * for each channel subband */
+ while (country_ie_len >= 3) {
+ int end_channel = 0;
+ struct ieee80211_country_ie_triplet *triplet =
+ (struct ieee80211_country_ie_triplet *) country_ie;
+ int cur_sub_max_channel = 0, cur_channel = 0;
+
+ if (triplet->ext.reg_extension_id >=
+ IEEE80211_COUNTRY_EXTENSION_ID) {
+ country_ie += 3;
+ country_ie_len -= 3;
+ continue;
+ }
+
+ /* 2 GHz */
+ if (triplet->chans.first_channel <= 14)
+ end_channel = triplet->chans.first_channel +
+ triplet->chans.num_channels;
+ else
+ /*
+ * 5 GHz -- For example in country IEs if the first
+ * channel given is 36 and the number of channels is 4
+ * then the individual channel numbers defined for the
+ * 5 GHz PHY by these parameters are: 36, 40, 44, and 48
+ * and not 36, 37, 38, 39.
+ *
+ * See: http://tinyurl.com/11d-clarification
+ */
+ end_channel = triplet->chans.first_channel +
+ (4 * (triplet->chans.num_channels - 1));
+
+ cur_channel = triplet->chans.first_channel;
+ cur_sub_max_channel = end_channel;
+
+ /* Basic sanity check */
+ if (cur_sub_max_channel < cur_channel)
+ return NULL;
+
+ /* Do not allow overlapping channels. Also channels
+ * passed in each subband must be monotonically
+ * increasing */
+ if (last_sub_max_channel) {
+ if (cur_channel <= last_sub_max_channel)
+ return NULL;
+ if (cur_sub_max_channel <= last_sub_max_channel)
+ return NULL;
+ }
+
+ /* When dot11RegulatoryClassesRequired is supported
+ * we can throw ext triplets as part of this soup,
+ * for now we don't care when those change as we
+ * don't support them */
+ *checksum ^= ((cur_channel ^ cur_sub_max_channel) << 8) |
+ ((cur_sub_max_channel ^ cur_sub_max_channel) << 16) |
+ ((triplet->chans.max_power ^ cur_sub_max_channel) << 24);
+
+ last_sub_max_channel = cur_sub_max_channel;
+
+ country_ie += 3;
+ country_ie_len -= 3;
+ num_rules++;
+
+ /* Note: this is not a IEEE requirement but
+ * simply a memory requirement */
+ if (num_rules > NL80211_MAX_SUPP_REG_RULES)
+ return NULL;
+ }
+
+ country_ie = triplets_start;
+ country_ie_len = len_at_triplet;
+
+ size_of_regd = sizeof(struct ieee80211_regdomain) +
+ (num_rules * sizeof(struct ieee80211_reg_rule));
+
+ rd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ rd->n_reg_rules = num_rules;
+ rd->alpha2[0] = alpha2[0];
+ rd->alpha2[1] = alpha2[1];
+
+ /* This time around we fill in the rd */
+ while (country_ie_len >= 3) {
+ int end_channel = 0;
+ struct ieee80211_country_ie_triplet *triplet =
+ (struct ieee80211_country_ie_triplet *) country_ie;
+ struct ieee80211_reg_rule *reg_rule = NULL;
+ struct ieee80211_freq_range *freq_range = NULL;
+ struct ieee80211_power_rule *power_rule = NULL;
+
+ /* Must parse if dot11RegulatoryClassesRequired is true,
+ * we don't support this yet */
+ if (triplet->ext.reg_extension_id >=
+ IEEE80211_COUNTRY_EXTENSION_ID) {
+ country_ie += 3;
+ country_ie_len -= 3;
+ continue;
+ }
+
+ reg_rule = &rd->reg_rules[i];
+ freq_range = &reg_rule->freq_range;
+ power_rule = &reg_rule->power_rule;
+
+ reg_rule->flags = flags;
+
+ /* 2 GHz */
+ if (triplet->chans.first_channel <= 14)
+ end_channel = triplet->chans.first_channel +
+ triplet->chans.num_channels;
+ else
+ end_channel = triplet->chans.first_channel +
+ (4 * (triplet->chans.num_channels - 1));
+
+ /* The +10 is since the regulatory domain expects
+ * the actual band edge, not the center of freq for
+ * its start and end freqs, assuming 20 MHz bandwidth on
+ * the channels passed */
+ freq_range->start_freq_khz =
+ MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+ triplet->chans.first_channel) - 10);
+ freq_range->end_freq_khz =
+ MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+ end_channel) + 10);
+
+ /* Large arbitrary values, we intersect later */
+ /* Increment this if we ever support >= 40 MHz channels
+ * in IEEE 802.11 */
+ freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
+ power_rule->max_antenna_gain = DBI_TO_MBI(100);
+ power_rule->max_eirp = DBM_TO_MBM(100);
+
+ country_ie += 3;
+ country_ie_len -= 3;
+ i++;
+
+ BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
+ }
+
+ return rd;
+}
+
+
+/* Helper for regdom_intersect(), this does the real
+ * mathematical intersection fun */
+static int reg_rules_intersect(
+ const struct ieee80211_reg_rule *rule1,
+ const struct ieee80211_reg_rule *rule2,
+ struct ieee80211_reg_rule *intersected_rule)
+{
+ const struct ieee80211_freq_range *freq_range1, *freq_range2;
+ struct ieee80211_freq_range *freq_range;
+ const struct ieee80211_power_rule *power_rule1, *power_rule2;
+ struct ieee80211_power_rule *power_rule;
+ u32 freq_diff;
+
+ freq_range1 = &rule1->freq_range;
+ freq_range2 = &rule2->freq_range;
+ freq_range = &intersected_rule->freq_range;
+
+ power_rule1 = &rule1->power_rule;
+ power_rule2 = &rule2->power_rule;
+ power_rule = &intersected_rule->power_rule;
+
+ freq_range->start_freq_khz = max(freq_range1->start_freq_khz,
+ freq_range2->start_freq_khz);
+ freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
+ freq_range2->end_freq_khz);
+ freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz,
+ freq_range2->max_bandwidth_khz);
+
+ freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
+ if (freq_range->max_bandwidth_khz > freq_diff)
+ freq_range->max_bandwidth_khz = freq_diff;
+
+ power_rule->max_eirp = min(power_rule1->max_eirp,
+ power_rule2->max_eirp);
+ power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,
+ power_rule2->max_antenna_gain);
+
+ intersected_rule->flags = (rule1->flags | rule2->flags);
+
+ if (!is_valid_reg_rule(intersected_rule))
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * regdom_intersect - do the intersection between two regulatory domains
+ * @rd1: first regulatory domain
+ * @rd2: second regulatory domain
+ *
+ * Use this function to get the intersection between two regulatory domains.
+ * Once completed we will mark the alpha2 for the rd as intersected, "98",
+ * as no one single alpha2 can represent this regulatory domain.
+ *
+ * Returns a pointer to the regulatory domain structure which will hold the
+ * resulting intersection of rules between rd1 and rd2. We will
+ * kzalloc() this structure for you.
+ */
+static struct ieee80211_regdomain *regdom_intersect(
+ const struct ieee80211_regdomain *rd1,
+ const struct ieee80211_regdomain *rd2)
+{
+ int r, size_of_regd;
+ unsigned int x, y;
+ unsigned int num_rules = 0, rule_idx = 0;
+ const struct ieee80211_reg_rule *rule1, *rule2;
+ struct ieee80211_reg_rule *intersected_rule;
+ struct ieee80211_regdomain *rd;
+ /* This is just a dummy holder to help us count */
+ struct ieee80211_reg_rule irule;
+
+ /* Uses the stack temporarily for counter arithmetic */
+ intersected_rule = &irule;
+
+ memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule));
+
+ if (!rd1 || !rd2)
+ return NULL;
+
+ /* First we get a count of the rules we'll need, then we actually
+ * build them. This is to so we can malloc() and free() a
+ * regdomain once. The reason we use reg_rules_intersect() here
+ * is it will return -EINVAL if the rule computed makes no sense.
+ * All rules that do check out OK are valid. */
+
+ for (x = 0; x < rd1->n_reg_rules; x++) {
+ rule1 = &rd1->reg_rules[x];
+ for (y = 0; y < rd2->n_reg_rules; y++) {
+ rule2 = &rd2->reg_rules[y];
+ if (!reg_rules_intersect(rule1, rule2,
+ intersected_rule))
+ num_rules++;
+ memset(intersected_rule, 0,
+ sizeof(struct ieee80211_reg_rule));
+ }
+ }
+
+ if (!num_rules)
+ return NULL;
+
+ size_of_regd = sizeof(struct ieee80211_regdomain) +
+ ((num_rules + 1) * sizeof(struct ieee80211_reg_rule));
+
+ rd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!rd)
+ return NULL;
+
+ for (x = 0; x < rd1->n_reg_rules; x++) {
+ rule1 = &rd1->reg_rules[x];
+ for (y = 0; y < rd2->n_reg_rules; y++) {
+ rule2 = &rd2->reg_rules[y];
+ /* This time around instead of using the stack lets
+ * write to the target rule directly saving ourselves
+ * a memcpy() */
+ intersected_rule = &rd->reg_rules[rule_idx];
+ r = reg_rules_intersect(rule1, rule2,
+ intersected_rule);
+ /* No need to memset here the intersected rule here as
+ * we're not using the stack anymore */
+ if (r)
+ continue;
+ rule_idx++;
+ }
+ }
+
+ if (rule_idx != num_rules) {
+ kfree(rd);
+ return NULL;
+ }
+
+ rd->n_reg_rules = num_rules;
+ rd->alpha2[0] = '9';
+ rd->alpha2[1] = '8';
+
+ return rd;
+}
+
/* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may
* want to just have the channel structure use these */
static u32 map_regdom_flags(u32 rd_flags)
@@ -491,12 +805,23 @@ static u32 map_regdom_flags(u32 rd_flags)
* this value to the maximum allowed bandwidth.
* @reg_rule: the regulatory rule which we have for this frequency
*
- * Use this function to get the regulatory rule for a specific frequency.
+ * Use this function to get the regulatory rule for a specific frequency on
+ * a given wireless device. If the device has a specific regulatory domain
+ * it wants to follow we respect that unless a country IE has been received
+ * and processed already.
+ *
+ * Returns 0 if it was able to find a valid regulatory rule which does
+ * apply to the given center_freq otherwise it returns non-zero. It will
+ * also return -ERANGE if we determine the given center_freq does not even have
+ * a regulatory rule for a frequency range in the center_freq's band. See
+ * freq_in_rule_band() for our current definition of a band -- this is purely
+ * subjective and right now its 802.11 specific.
*/
static int freq_reg_info(u32 center_freq, u32 *bandwidth,
const struct ieee80211_reg_rule **reg_rule)
{
int i;
+ bool band_rule_found = false;
u32 max_bandwidth = 0;
if (!cfg80211_regdomain)
@@ -510,7 +835,15 @@ static int freq_reg_info(u32 center_freq, u32 *bandwidth,
rr = &cfg80211_regdomain->reg_rules[i];
fr = &rr->freq_range;
pr = &rr->power_rule;
+
+ /* We only need to know if one frequency rule was
+ * was in center_freq's band, that's enough, so lets
+ * not overwrite it once found */
+ if (!band_rule_found)
+ band_rule_found = freq_in_rule_band(fr, center_freq);
+
max_bandwidth = freq_max_bandwidth(fr, center_freq);
+
if (max_bandwidth && *bandwidth <= max_bandwidth) {
*reg_rule = rr;
*bandwidth = max_bandwidth;
@@ -518,23 +851,64 @@ static int freq_reg_info(u32 center_freq, u32 *bandwidth,
}
}
+ if (!band_rule_found)
+ return -ERANGE;
+
return !max_bandwidth;
}
-static void handle_channel(struct ieee80211_channel *chan)
+static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
+ unsigned int chan_idx)
{
int r;
- u32 flags = chan->orig_flags;
+ u32 flags;
u32 max_bandwidth = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+
+ sband = wiphy->bands[band];
+ BUG_ON(chan_idx >= sband->n_channels);
+ chan = &sband->channels[chan_idx];
+
+ flags = chan->orig_flags;
r = freq_reg_info(MHZ_TO_KHZ(chan->center_freq),
&max_bandwidth, &reg_rule);
if (r) {
- flags |= IEEE80211_CHAN_DISABLED;
- chan->flags = flags;
+ /* This means no regulatory rule was found in the country IE
+ * with a frequency range on the center_freq's band, since
+ * IEEE-802.11 allows for a country IE to have a subset of the
+ * regulatory information provided in a country we ignore
+ * disabling the channel unless at least one reg rule was
+ * found on the center_freq's band. For details see this
+ * clarification:
+ *
+ * http://tinyurl.com/11d-clarification
+ */
+ if (r == -ERANGE &&
+ last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
+#ifdef CONFIG_CFG80211_REG_DEBUG
+ printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz "
+ "intact on %s - no rule found in band on "
+ "Country IE\n",
+ chan->center_freq, wiphy_name(wiphy));
+#endif
+ } else {
+ /* In this case we know the country IE has at least one reg rule
+ * for the band so we respect its band definitions */
+#ifdef CONFIG_CFG80211_REG_DEBUG
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
+ printk(KERN_DEBUG "cfg80211: Disabling "
+ "channel %d MHz on %s due to "
+ "Country IE\n",
+ chan->center_freq, wiphy_name(wiphy));
+#endif
+ flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = flags;
+ }
return;
}
@@ -551,12 +925,26 @@ static void handle_channel(struct ieee80211_channel *chan)
chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
}
-static void handle_band(struct ieee80211_supported_band *sband)
+static void handle_band(struct wiphy *wiphy, enum ieee80211_band band)
{
- int i;
+ unsigned int i;
+ struct ieee80211_supported_band *sband;
+
+ BUG_ON(!wiphy->bands[band]);
+ sband = wiphy->bands[band];
for (i = 0; i < sband->n_channels; i++)
- handle_channel(&sband->channels[i]);
+ handle_channel(wiphy, band, i);
+}
+
+static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby)
+{
+ if (!last_request)
+ return true;
+ if (setby == REGDOM_SET_BY_CORE &&
+ wiphy->fw_handles_regulatory)
+ return true;
+ return false;
}
static void update_all_wiphy_regulatory(enum reg_set_by setby)
@@ -564,7 +952,8 @@ static void update_all_wiphy_regulatory(enum reg_set_by setby)
struct cfg80211_registered_device *drv;
list_for_each_entry(drv, &cfg80211_drv_list, list)
- wiphy_update_regulatory(&drv->wiphy, setby);
+ if (!ignore_reg_update(&drv->wiphy, setby))
+ wiphy_update_regulatory(&drv->wiphy, setby);
}
void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby)
@@ -572,84 +961,243 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby)
enum ieee80211_band band;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (wiphy->bands[band])
- handle_band(wiphy->bands[band]);
+ handle_band(wiphy, band);
if (wiphy->reg_notifier)
wiphy->reg_notifier(wiphy, setby);
}
}
-/* Caller must hold &cfg80211_drv_mutex */
-int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
- const char *alpha2, struct ieee80211_regdomain *rd)
-{
- struct regulatory_request *request;
- char *rd_alpha2;
- int r = 0;
-
- r = ignore_request(wiphy, set_by, (char *) alpha2, rd);
- if (r)
- return r;
+/* Return value which can be used by ignore_request() to indicate
+ * it has been determined we should intersect two regulatory domains */
+#define REG_INTERSECT 1
- if (rd)
- rd_alpha2 = rd->alpha2;
- else
- rd_alpha2 = (char *) alpha2;
+/* This has the logic which determines when a new request
+ * should be ignored. */
+static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
+ const char *alpha2)
+{
+ /* All initial requests are respected */
+ if (!last_request)
+ return 0;
switch (set_by) {
+ case REGDOM_SET_BY_INIT:
+ return -EINVAL;
case REGDOM_SET_BY_CORE:
+ /*
+ * Always respect new wireless core hints, should only happen
+ * when updating the world regulatory domain at init.
+ */
+ return 0;
case REGDOM_SET_BY_COUNTRY_IE:
+ if (unlikely(!is_an_alpha2(alpha2)))
+ return -EINVAL;
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
+ if (last_request->wiphy != wiphy) {
+ /*
+ * Two cards with two APs claiming different
+ * different Country IE alpha2s. We could
+ * intersect them, but that seems unlikely
+ * to be correct. Reject second one for now.
+ */
+ if (!alpha2_equal(alpha2,
+ cfg80211_regdomain->alpha2))
+ return -EOPNOTSUPP;
+ return -EALREADY;
+ }
+ /* Two consecutive Country IE hints on the same wiphy.
+ * This should be picked up early by the driver/stack */
+ if (WARN_ON(!alpha2_equal(cfg80211_regdomain->alpha2,
+ alpha2)))
+ return 0;
+ return -EALREADY;
+ }
+ return REG_INTERSECT;
case REGDOM_SET_BY_DRIVER:
+ if (last_request->initiator == REGDOM_SET_BY_DRIVER)
+ return -EALREADY;
+ return 0;
case REGDOM_SET_BY_USER:
- request = kzalloc(sizeof(struct regulatory_request),
- GFP_KERNEL);
- if (!request)
- return -ENOMEM;
-
- request->alpha2[0] = rd_alpha2[0];
- request->alpha2[1] = rd_alpha2[1];
- request->initiator = set_by;
- request->wiphy = wiphy;
-
- list_add_tail(&request->list, &regulatory_requests);
- if (rd)
- break;
- r = call_crda(alpha2);
-#ifndef CONFIG_WIRELESS_OLD_REGULATORY
- if (r)
- printk(KERN_ERR "cfg80211: Failed calling CRDA\n");
-#endif
- break;
- default:
- r = -ENOTSUPP;
- break;
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
+ return REG_INTERSECT;
+ /* If the user knows better the user should set the regdom
+ * to their country before the IE is picked up */
+ if (last_request->initiator == REGDOM_SET_BY_USER &&
+ last_request->intersect)
+ return -EOPNOTSUPP;
+ return 0;
}
- return r;
+ return -EINVAL;
}
-/* If rd is not NULL and if this call fails the caller must free it */
-int regulatory_hint(struct wiphy *wiphy, const char *alpha2,
- struct ieee80211_regdomain *rd)
+/* Caller must hold &cfg80211_drv_mutex */
+int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
+ const char *alpha2,
+ u32 country_ie_checksum,
+ enum environment_cap env)
{
- int r;
- BUG_ON(!rd && !alpha2);
+ struct regulatory_request *request;
+ bool intersect = false;
+ int r = 0;
- mutex_lock(&cfg80211_drv_mutex);
+ r = ignore_request(wiphy, set_by, alpha2);
- r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, rd);
- if (r || !rd)
- goto unlock_and_exit;
+ if (r == REG_INTERSECT)
+ intersect = true;
+ else if (r)
+ return r;
- /* If the driver passed a regulatory domain we skipped asking
- * userspace for one so we can now go ahead and set it */
- r = set_regdom(rd);
+ request = kzalloc(sizeof(struct regulatory_request),
+ GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->alpha2[0] = alpha2[0];
+ request->alpha2[1] = alpha2[1];
+ request->initiator = set_by;
+ request->wiphy = wiphy;
+ request->intersect = intersect;
+ request->country_ie_checksum = country_ie_checksum;
+ request->country_ie_env = env;
+
+ kfree(last_request);
+ last_request = request;
+ /*
+ * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled
+ * AND if CRDA is NOT present nothing will happen, if someone
+ * wants to bother with 11d with OLD_REG you can add a timer.
+ * If after x amount of time nothing happens you can call:
+ *
+ * return set_regdom(country_ie_regdomain);
+ *
+ * to intersect with the static rd
+ */
+ return call_crda(alpha2);
+}
-unlock_and_exit:
+void regulatory_hint(struct wiphy *wiphy, const char *alpha2)
+{
+ BUG_ON(!alpha2);
+
+ mutex_lock(&cfg80211_drv_mutex);
+ __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY);
mutex_unlock(&cfg80211_drv_mutex);
- return r;
}
EXPORT_SYMBOL(regulatory_hint);
+static bool reg_same_country_ie_hint(struct wiphy *wiphy,
+ u32 country_ie_checksum)
+{
+ if (!last_request->wiphy)
+ return false;
+ if (likely(last_request->wiphy != wiphy))
+ return !country_ie_integrity_changes(country_ie_checksum);
+ /* We should not have let these through at this point, they
+ * should have been picked up earlier by the first alpha2 check
+ * on the device */
+ if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum)))
+ return true;
+ return false;
+}
+
+void regulatory_hint_11d(struct wiphy *wiphy,
+ u8 *country_ie,
+ u8 country_ie_len)
+{
+ struct ieee80211_regdomain *rd = NULL;
+ char alpha2[2];
+ u32 checksum = 0;
+ enum environment_cap env = ENVIRON_ANY;
+
+ if (!last_request)
+ return;
+
+ mutex_lock(&cfg80211_drv_mutex);
+
+ /* IE len must be evenly divisible by 2 */
+ if (country_ie_len & 0x01)
+ goto out;
+
+ if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
+ goto out;
+
+ /* Pending country IE processing, this can happen after we
+ * call CRDA and wait for a response if a beacon was received before
+ * we were able to process the last regulatory_hint_11d() call */
+ if (country_ie_regdomain)
+ goto out;
+
+ alpha2[0] = country_ie[0];
+ alpha2[1] = country_ie[1];
+
+ if (country_ie[2] == 'I')
+ env = ENVIRON_INDOOR;
+ else if (country_ie[2] == 'O')
+ env = ENVIRON_OUTDOOR;
+
+ /* We will run this for *every* beacon processed for the BSSID, so
+ * we optimize an early check to exit out early if we don't have to
+ * do anything */
+ if (likely(last_request->wiphy)) {
+ struct cfg80211_registered_device *drv_last_ie;
+
+ drv_last_ie = wiphy_to_dev(last_request->wiphy);
+
+ /* Lets keep this simple -- we trust the first AP
+ * after we intersect with CRDA */
+ if (likely(last_request->wiphy == wiphy)) {
+ /* Ignore IEs coming in on this wiphy with
+ * the same alpha2 and environment cap */
+ if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
+ alpha2) &&
+ env == drv_last_ie->env)) {
+ goto out;
+ }
+ /* the wiphy moved on to another BSSID or the AP
+ * was reconfigured. XXX: We need to deal with the
+ * case where the user suspends and goes to goes
+ * to another country, and then gets IEs from an
+ * AP with different settings */
+ goto out;
+ } else {
+ /* Ignore IEs coming in on two separate wiphys with
+ * the same alpha2 and environment cap */
+ if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
+ alpha2) &&
+ env == drv_last_ie->env)) {
+ goto out;
+ }
+ /* We could potentially intersect though */
+ goto out;
+ }
+ }
+
+ rd = country_ie_2_rd(country_ie, country_ie_len, &checksum);
+ if (!rd)
+ goto out;
+
+ /* This will not happen right now but we leave it here for the
+ * the future when we want to add suspend/resume support and having
+ * the user move to another country after doing so, or having the user
+ * move to another AP. Right now we just trust the first AP. This is why
+ * this is marked as likley(). If we hit this before we add this support
+ * we want to be informed of it as it would indicate a mistake in the
+ * current design */
+ if (likely(WARN_ON(reg_same_country_ie_hint(wiphy, checksum))))
+ goto out;
+
+ /* We keep this around for when CRDA comes back with a response so
+ * we can intersect with that */
+ country_ie_regdomain = rd;
+
+ __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE,
+ country_ie_regdomain->alpha2, checksum, env);
+
+out:
+ mutex_unlock(&cfg80211_drv_mutex);
+}
+EXPORT_SYMBOL(regulatory_hint_11d);
static void print_rd_rules(const struct ieee80211_regdomain *rd)
{
@@ -689,7 +1237,25 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
static void print_regdomain(const struct ieee80211_regdomain *rd)
{
- if (is_world_regdom(rd->alpha2))
+ if (is_intersected_alpha2(rd->alpha2)) {
+ struct wiphy *wiphy = NULL;
+ struct cfg80211_registered_device *drv;
+
+ if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
+ if (last_request->wiphy) {
+ wiphy = last_request->wiphy;
+ drv = wiphy_to_dev(wiphy);
+ printk(KERN_INFO "cfg80211: Current regulatory "
+ "domain updated by AP to: %c%c\n",
+ drv->country_ie_alpha2[0],
+ drv->country_ie_alpha2[1]);
+ } else
+ printk(KERN_INFO "cfg80211: Current regulatory "
+ "domain intersected: \n");
+ } else
+ printk(KERN_INFO "cfg80211: Current regulatory "
+ "intersected: \n");
+ } else if (is_world_regdom(rd->alpha2))
printk(KERN_INFO "cfg80211: World regulatory "
"domain updated:\n");
else {
@@ -705,21 +1271,50 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
print_rd_rules(rd);
}
-void print_regdomain_info(const struct ieee80211_regdomain *rd)
+static void print_regdomain_info(const struct ieee80211_regdomain *rd)
{
printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n",
rd->alpha2[0], rd->alpha2[1]);
print_rd_rules(rd);
}
-static int __set_regdom(const struct ieee80211_regdomain *rd)
+#ifdef CONFIG_CFG80211_REG_DEBUG
+static void reg_country_ie_process_debug(
+ const struct ieee80211_regdomain *rd,
+ const struct ieee80211_regdomain *country_ie_regdomain,
+ const struct ieee80211_regdomain *intersected_rd)
+{
+ printk(KERN_DEBUG "cfg80211: Received country IE:\n");
+ print_regdomain_info(country_ie_regdomain);
+ printk(KERN_DEBUG "cfg80211: CRDA thinks this should applied:\n");
+ print_regdomain_info(rd);
+ if (intersected_rd) {
+ printk(KERN_DEBUG "cfg80211: We intersect both of these "
+ "and get:\n");
+ print_regdomain_info(intersected_rd);
+ return;
+ }
+ printk(KERN_DEBUG "cfg80211: Intersection between both failed\n");
+}
+#else
+static inline void reg_country_ie_process_debug(
+ const struct ieee80211_regdomain *rd,
+ const struct ieee80211_regdomain *country_ie_regdomain,
+ const struct ieee80211_regdomain *intersected_rd)
{
- struct regulatory_request *request = NULL;
+}
+#endif
+/* Takes ownership of rd only if it doesn't fail */
+static int __set_regdom(const struct ieee80211_regdomain *rd)
+{
+ const struct ieee80211_regdomain *intersected_rd = NULL;
+ struct cfg80211_registered_device *drv = NULL;
+ struct wiphy *wiphy = NULL;
/* Some basic sanity checks first */
if (is_world_regdom(rd->alpha2)) {
- if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request)))
+ if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
return -EINVAL;
update_world_regdomain(rd);
return 0;
@@ -729,45 +1324,102 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
!is_unknown_alpha2(rd->alpha2))
return -EINVAL;
- if (list_empty(&regulatory_requests))
+ if (!last_request)
return -EINVAL;
- /* allow overriding the static definitions if CRDA is present */
- if (!is_old_static_regdom(cfg80211_regdomain) &&
- !regdom_changed(rd->alpha2))
- return -EINVAL;
+ /* Lets only bother proceeding on the same alpha2 if the current
+ * rd is non static (it means CRDA was present and was used last)
+ * and the pending request came in from a country IE */
+ if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) {
+ /* If someone else asked us to change the rd lets only bother
+ * checking if the alpha2 changes if CRDA was already called */
+ if (!is_old_static_regdom(cfg80211_regdomain) &&
+ !regdom_changed(rd->alpha2))
+ return -EINVAL;
+ }
+
+ wiphy = last_request->wiphy;
/* Now lets set the regulatory domain, update all driver channels
* and finally inform them of what we have done, in case they want
* to review or adjust their own settings based on their own
* internal EEPROM data */
- if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request)))
+ if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
return -EINVAL;
- reset_regdomains();
+ if (!is_valid_rd(rd)) {
+ printk(KERN_ERR "cfg80211: Invalid "
+ "regulatory domain detected:\n");
+ print_regdomain_info(rd);
+ return -EINVAL;
+ }
- /* Country IE parsing coming soon */
- switch (request->initiator) {
- case REGDOM_SET_BY_CORE:
- case REGDOM_SET_BY_DRIVER:
- case REGDOM_SET_BY_USER:
- if (!is_valid_rd(rd)) {
- printk(KERN_ERR "cfg80211: Invalid "
- "regulatory domain detected:\n");
- print_regdomain_info(rd);
+ if (!last_request->intersect) {
+ reset_regdomains();
+ cfg80211_regdomain = rd;
+ return 0;
+ }
+
+ /* Intersection requires a bit more work */
+
+ if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) {
+
+ intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
+ if (!intersected_rd)
return -EINVAL;
- }
- break;
- case REGDOM_SET_BY_COUNTRY_IE: /* Not yet */
- WARN_ON(1);
- default:
- return -EOPNOTSUPP;
+
+ /* We can trash what CRDA provided now */
+ kfree(rd);
+ rd = NULL;
+
+ reset_regdomains();
+ cfg80211_regdomain = intersected_rd;
+
+ return 0;
}
- /* Tada! */
- cfg80211_regdomain = rd;
- request->granted = 1;
+ /*
+ * Country IE requests are handled a bit differently, we intersect
+ * the country IE rd with what CRDA believes that country should have
+ */
+
+ BUG_ON(!country_ie_regdomain);
+
+ if (rd != country_ie_regdomain) {
+ /* Intersect what CRDA returned and our what we
+ * had built from the Country IE received */
+
+ intersected_rd = regdom_intersect(rd, country_ie_regdomain);
+
+ reg_country_ie_process_debug(rd, country_ie_regdomain,
+ intersected_rd);
+
+ kfree(country_ie_regdomain);
+ country_ie_regdomain = NULL;
+ } else {
+ /* This would happen when CRDA was not present and
+ * OLD_REGULATORY was enabled. We intersect our Country
+ * IE rd and what was set on cfg80211 originally */
+ intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
+ }
+
+ if (!intersected_rd)
+ return -EINVAL;
+
+ drv = wiphy_to_dev(wiphy);
+
+ drv->country_ie_alpha2[0] = rd->alpha2[0];
+ drv->country_ie_alpha2[1] = rd->alpha2[1];
+ drv->env = last_request->country_ie_env;
+
+ BUG_ON(intersected_rd == rd);
+
+ kfree(rd);
+ rd = NULL;
+
+ reset_regdomains();
+ cfg80211_regdomain = intersected_rd;
return 0;
}
@@ -775,52 +1427,41 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
/* Use this call to set the current regulatory domain. Conflicts with
* multiple drivers can be ironed out later. Caller must've already
- * kmalloc'd the rd structure. If this calls fails you should kfree()
- * the passed rd. Caller must hold cfg80211_drv_mutex */
+ * kmalloc'd the rd structure. Caller must hold cfg80211_drv_mutex */
int set_regdom(const struct ieee80211_regdomain *rd)
{
- struct regulatory_request *this_request = NULL, *prev_request = NULL;
int r;
- if (!list_empty(&regulatory_requests))
- prev_request = list_first_entry(&regulatory_requests,
- struct regulatory_request, list);
-
/* Note that this doesn't update the wiphys, this is done below */
r = __set_regdom(rd);
- if (r)
+ if (r) {
+ kfree(rd);
return r;
-
- BUG_ON((!__reg_is_valid_request(rd->alpha2, &this_request)));
-
- /* The initial standard core update of the world regulatory domain, no
- * need to keep that request info around if it didn't fail. */
- if (is_world_regdom(rd->alpha2) &&
- this_request->initiator == REGDOM_SET_BY_CORE &&
- this_request->granted) {
- list_del(&this_request->list);
- kfree(this_request);
- this_request = NULL;
- }
-
- /* Remove old requests, we only leave behind the last one */
- if (prev_request) {
- list_del(&prev_request->list);
- kfree(prev_request);
- prev_request = NULL;
}
/* This would make this whole thing pointless */
- BUG_ON(rd != cfg80211_regdomain);
+ if (!last_request->intersect)
+ BUG_ON(rd != cfg80211_regdomain);
/* update all wiphys now with the new established regulatory domain */
- update_all_wiphy_regulatory(this_request->initiator);
+ update_all_wiphy_regulatory(last_request->initiator);
- print_regdomain(rd);
+ print_regdomain(cfg80211_regdomain);
return r;
}
+/* Caller must hold cfg80211_drv_mutex */
+void reg_device_remove(struct wiphy *wiphy)
+{
+ if (!last_request || !last_request->wiphy)
+ return;
+ if (last_request->wiphy != wiphy)
+ return;
+ last_request->wiphy = NULL;
+ last_request->country_ie_env = ENVIRON_ANY;
+}
+
int regulatory_init(void)
{
int err;
@@ -838,13 +1479,13 @@ int regulatory_init(void)
* you have CRDA you get it updated, otherwise you get
* stuck with the static values. We ignore "EU" code as
* that is not a valid ISO / IEC 3166 alpha2 */
- if (ieee80211_regdom[0] != 'E' && ieee80211_regdom[1] != 'U')
+ if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U')
err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE,
- ieee80211_regdom, NULL);
+ ieee80211_regdom, 0, ENVIRON_ANY);
#else
cfg80211_regdomain = cfg80211_world_regdom;
- err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", NULL);
+ err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", 0, ENVIRON_ANY);
if (err)
printk(KERN_ERR "cfg80211: calling CRDA failed - "
"unable to update world regulatory domain, "
@@ -856,16 +1497,15 @@ int regulatory_init(void)
void regulatory_exit(void)
{
- struct regulatory_request *req, *req_tmp;
-
mutex_lock(&cfg80211_drv_mutex);
reset_regdomains();
- list_for_each_entry_safe(req, req_tmp, &regulatory_requests, list) {
- list_del(&req->list);
- kfree(req);
- }
+ kfree(country_ie_regdomain);
+ country_ie_regdomain = NULL;
+
+ kfree(last_request);
+
platform_device_unregister(reg_pdev);
mutex_unlock(&cfg80211_drv_mutex);
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index a33362872f3c..a76ea3ff7cd6 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -1,13 +1,44 @@
#ifndef __NET_WIRELESS_REG_H
#define __NET_WIRELESS_REG_H
-extern struct mutex cfg80211_reg_mutex;
bool is_world_regdom(const char *alpha2);
bool reg_is_valid_request(const char *alpha2);
+void reg_device_remove(struct wiphy *wiphy);
+
int regulatory_init(void);
void regulatory_exit(void);
int set_regdom(const struct ieee80211_regdomain *rd);
+enum environment_cap {
+ ENVIRON_ANY,
+ ENVIRON_INDOOR,
+ ENVIRON_OUTDOOR,
+};
+
+
+/**
+ * __regulatory_hint - hint to the wireless core a regulatory domain
+ * @wiphy: if the hint comes from country information from an AP, this
+ * is required to be set to the wiphy that received the information
+ * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain
+ * should be in.
+ * @country_ie_checksum: checksum of processed country IE, set this to 0
+ * if the hint did not come from a country IE
+ * @country_ie_env: the environment the IE told us we are in, %ENVIRON_*
+ *
+ * The Wireless subsystem can use this function to hint to the wireless core
+ * what it believes should be the current regulatory domain by giving it an
+ * ISO/IEC 3166 alpha2 country code it knows its regulatory domain should be
+ * in.
+ *
+ * Returns zero if all went fine, %-EALREADY if a regulatory domain had
+ * already been set or other standard error codes.
+ *
+ */
+extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
+ const char *alpha2, u32 country_ie_checksum,
+ enum environment_cap country_ie_env);
+
#endif /* __NET_WIRELESS_REG_H */
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 29f820e18251..79a382877641 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -23,25 +23,20 @@ static inline struct cfg80211_registered_device *dev_to_rdev(
return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
}
-static ssize_t _show_index(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%d\n", dev_to_rdev(dev)->idx);
+#define SHOW_FMT(name, fmt, member) \
+static ssize_t name ## _show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
}
-static ssize_t _show_permaddr(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- unsigned char *addr = dev_to_rdev(dev)->wiphy.perm_addr;
-
- return sprintf(buf, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
- addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
-}
+SHOW_FMT(index, "%d", idx);
+SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
static struct device_attribute ieee80211_dev_attrs[] = {
- __ATTR(index, S_IRUGO, _show_index, NULL),
- __ATTR(macaddress, S_IRUGO, _show_permaddr, NULL),
+ __ATTR_RO(index),
+ __ATTR_RO(macaddress),
{}
};
diff --git a/net/wireless/util.c b/net/wireless/util.c
index f54424693a38..e76cc28b0345 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -7,6 +7,25 @@
#include <asm/bitops.h>
#include "core.h"
+struct ieee80211_rate *
+ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
+ u64 basic_rates, int bitrate)
+{
+ struct ieee80211_rate *result = &sband->bitrates[0];
+ int i;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (!(basic_rates & BIT(i)))
+ continue;
+ if (sband->bitrates[i].bitrate > bitrate)
+ continue;
+ result = &sband->bitrates[i];
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(ieee80211_get_response_rate);
+
int ieee80211_channel_to_frequency(int chan)
{
if (chan < 14)
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
new file mode 100644
index 000000000000..58e489fd4aed
--- /dev/null
+++ b/net/wireless/wext-compat.c
@@ -0,0 +1,139 @@
+/*
+ * cfg80211 - wext compat code
+ *
+ * This is temporary code until all wireless functionality is migrated
+ * into cfg80211, when that happens all the exports here go away and
+ * we directly assign the wireless handlers of wireless interfaces.
+ *
+ * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/wireless.h>
+#include <linux/nl80211.h>
+#include <net/iw_handler.h>
+#include <net/wireless.h>
+#include <net/cfg80211.h>
+#include "core.h"
+
+int cfg80211_wext_giwname(struct net_device *dev,
+ struct iw_request_info *info,
+ char *name, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct ieee80211_supported_band *sband;
+ bool is_ht = false, is_a = false, is_b = false, is_g = false;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ sband = wdev->wiphy->bands[IEEE80211_BAND_5GHZ];
+ if (sband) {
+ is_a = true;
+ is_ht |= sband->ht_cap.ht_supported;
+ }
+
+ sband = wdev->wiphy->bands[IEEE80211_BAND_2GHZ];
+ if (sband) {
+ int i;
+ /* Check for mandatory rates */
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if (sband->bitrates[i].bitrate == 10)
+ is_b = true;
+ if (sband->bitrates[i].bitrate == 60)
+ is_g = true;
+ }
+ is_ht |= sband->ht_cap.ht_supported;
+ }
+
+ strcpy(name, "IEEE 802.11");
+ if (is_a)
+ strcat(name, "a");
+ if (is_b)
+ strcat(name, "b");
+ if (is_g)
+ strcat(name, "g");
+ if (is_ht)
+ strcat(name, "n");
+
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_wext_giwname);
+
+int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev;
+ struct vif_params vifparams;
+ enum nl80211_iftype type;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ rdev = wiphy_to_dev(wdev->wiphy);
+
+ if (!rdev->ops->change_virtual_intf)
+ return -EOPNOTSUPP;
+
+ /* don't support changing VLANs, you just re-create them */
+ if (wdev->iftype == NL80211_IFTYPE_AP_VLAN)
+ return -EOPNOTSUPP;
+
+ switch (*mode) {
+ case IW_MODE_INFRA:
+ type = NL80211_IFTYPE_STATION;
+ break;
+ case IW_MODE_ADHOC:
+ type = NL80211_IFTYPE_ADHOC;
+ break;
+ case IW_MODE_REPEAT:
+ type = NL80211_IFTYPE_WDS;
+ break;
+ case IW_MODE_MONITOR:
+ type = NL80211_IFTYPE_MONITOR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ memset(&vifparams, 0, sizeof(vifparams));
+
+ return rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type,
+ NULL, &vifparams);
+}
+EXPORT_SYMBOL(cfg80211_wext_siwmode);
+
+int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
+ u32 *mode, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ *mode = IW_MODE_MASTER;
+ break;
+ case NL80211_IFTYPE_STATION:
+ *mode = IW_MODE_INFRA;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ *mode = IW_MODE_ADHOC;
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ *mode = IW_MODE_MONITOR;
+ break;
+ case NL80211_IFTYPE_WDS:
+ *mode = IW_MODE_REPEAT;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ *mode = IW_MODE_SECOND; /* FIXME */
+ break;
+ default:
+ *mode = IW_MODE_AUTO;
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_wext_giwmode);
diff --git a/net/wireless/wext.c b/net/wireless/wext.c
index d98ffb75119a..e49a2d1ef1e4 100644
--- a/net/wireless/wext.c
+++ b/net/wireless/wext.c
@@ -64,7 +64,7 @@
* o Remove spy_offset from struct iw_handler_def
* o Start deprecating dev->get_wireless_stats, output a warning
* o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
- * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
+ * o Don't lose INVALID/DBM flags when clearing UPDATED flags (iwstats)
*
* v8 - 17.02.06 - Jean II
* o RtNetlink requests support (SET/GET)
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c
index 8912c0f5460b..72c15205bb2b 100644
--- a/scripts/basic/fixdep.c
+++ b/scripts/basic/fixdep.c
@@ -373,10 +373,11 @@ void print_deps(void)
void traps(void)
{
static char test[] __attribute__((aligned(sizeof(int)))) = "CONF";
+ int *p = (int *)test;
- if (*(int *)test != INT_CONF) {
+ if (*p != INT_CONF) {
fprintf(stderr, "fixdep: sizeof(int) != 4 or wrong endianess? %#x\n",
- *(int *)test);
+ *p);
exit(2);
}
}
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index ad2434b26970..0037a1a4339a 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -71,7 +71,8 @@ static int read_symbol(FILE *in, struct sym_entry *s)
if (rc != 3) {
if (rc != EOF) {
/* skip line */
- fgets(str, 500, in);
+ /* JVS: check return value to avoid compiler warning */
+ if (fgets(str, 500, in) == NULL) ;
}
return -1;
}
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 3e1057f885c6..b28d74c323fa 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -101,7 +101,8 @@ static int conf_askvalue(struct symbol *sym, const char *def)
check_stdin();
case ask_all:
fflush(stdout);
- fgets(line, 128, stdin);
+ /* JVS: check return value to avoid compiler warning */
+ if (fgets(line, 128, stdin) == NULL) ;
return 1;
default:
break;
@@ -303,7 +304,8 @@ static int conf_choice(struct menu *menu)
check_stdin();
case ask_all:
fflush(stdout);
- fgets(line, 128, stdin);
+ /* JVS: check return value to avoid compiler warning */
+ if (fgets(line, 128, stdin) == NULL) ;
strip(line);
if (line[0] == '?') {
printf("\n%s\n", get_help(menu));
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 88921611b22e..ae89a17e60e0 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -1539,7 +1539,8 @@ static void get_markers(struct elf_info *info, struct module *mod)
const char *name = strings + sym->st_value;
const char *fmt = strchr(name, '\0') + 1;
char *line = NULL;
- asprintf(&line, "%s\t%s\t%s\n", name, mod->name, fmt);
+ /* JVS: check return value to avoid compiler warning */
+ if (asprintf(&line, "%s\t%s\t%s\n", name, mod->name, fmt) == -1) ;
NOFAIL(line);
mod->markers[n++] = line;
}
@@ -1959,7 +1960,8 @@ static void write_dump(const char *fname)
static void add_marker(struct module *mod, const char *name, const char *fmt)
{
char *line = NULL;
- asprintf(&line, "%s\t%s\t%s\n", name, mod->name, fmt);
+ /* JVS: check return value to avoid compiler warning */
+ if (asprintf(&line, "%s\t%s\t%s\n", name, mod->name, fmt) == -1) ;
NOFAIL(line);
mod->markers = NOFAIL(realloc(mod->markers, ((mod->nmarkers + 1) *
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 72d233528ade..4f9eece3ae3f 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -19,6 +19,18 @@ if head=`git rev-parse --verify --short HEAD 2>/dev/null`; then
fi
fi
+ # Do we have a DEL-tag?
+ if git name-rev --tags HEAD | grep -i "del-" > /dev/null; then
+
+ # -- The option 'exact-match' is available first by v1.5.5.1
+ ver=$(git --version | cut -d " " -f 3 | sed 's#\.##g')
+ if [ $ver -gt 1551 ]; then
+ printf '%s' - $(git describe --tags --exact-match HEAD)
+ else
+ printf '%s' - $(git describe --tags)
+ fi
+ fi
+
# Are there uncommitted changes?
git update-index --refresh --unmerged > /dev/null
if git diff-index --name-only HEAD | grep -v "^scripts/package" \
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index f8e6de48d816..25b346110e49 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -50,5 +50,54 @@ config SND_PXA2XX_AC97
Say Y or M if you want to support any AC97 codec attached to
the PXA2xx AC97 interface.
+config SND_MXC_SPDIF
+ tristate "MXC SPDIF sound card spport"
+ select SND_PCM
+ help
+ Say Y here to enable SPDIF sound card
+
+config SND_MXC_PMIC
+ tristate "MXC PMIC sound system"
+ depends on ARCH_MXC && MXC_DAM && MXC_SSI && \
+ (MXC_MC13783_AUDIO || MXC_PMIC_SC55112_AUDIO)
+ default y
+ select SND_PCM
+ help
+ Say Y here to include support for soundcards based on the
+ MC13783 chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-mc13783.
+
+config SND_MXC_PLAYBACK_MIXING
+ bool "Playback Stream Mixing"
+ depends on (!ARCH_MX27) && (!ARCH_MXC91131) && ARCH_MXC && MXC_DAM && MXC_SSI && \
+ (MXC_MC13783_AUDIO)
+ default n
+ select SND_PCM
+ help
+ Say Y here to include support mixing for soundcards based on the
+ MC13783 chip. This supports audio stream mixing on VCODEC for mc13783 based platforms.
+ Analog mixng as well as Digital mixing can be tested on these platforms.
+ As of now , mixing of mono files only are supported in Digital Mixing since it is done on VCODEC.
+ SSI 2 channel mode is used to mix 2 streams on a single SSI. This is supported on all platforms except imx27ads(imx27ads - Analog mixing only).
+
+config HEADSET_DETECT_ENABLE
+ bool "Headset Detect Enable"
+ depends on (!ARCH_MXC91131) && ARCH_MXC && MXC_DAM && MXC_SSI && \
+ (MXC_MC13783_AUDIO)
+ default n
+ select SND_PCM
+ help
+ Say Y here to enable Headset Detect Feature.
+
+config SND_MXC_PMIC_IRAM
+ bool "MXC PMIC sound system supports IRAM"
+ depends on SND_MXC_PMIC && SDMA_IRAM
+ default n
+ select SND_PCM
+ help
+ It will use IRAM as the DMA buffer of ALSA playback.
+
endif # SND_ARM
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 2054de11de8a..307e2db108db 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -17,3 +17,14 @@ snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o
obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
+
+#
+# Define the header file locations for PMIC drivers.
+#
+CFLAGS_mxc-alsa-pmic.o = -I$(TOPDIR)/drivers/mxc
+obj-$(CONFIG_SND_MXC_PMIC) += snd-mxc-alsa.o
+snd-mxc-alsa-objs := mxc-alsa-pmic.o mxc-alsa-mixer.o
+
+CFLGS_mxc_alsa_spdif.o = -I$(TOPDIR)/drivers/mxc
+obj-$(CONFIG_SND_MXC_SPDIF) += snd-spdif.o
+snd-spdif-objs := mxc-alsa-spdif.o
diff --git a/sound/arm/mxc-alsa-common.h b/sound/arm/mxc-alsa-common.h
new file mode 100644
index 000000000000..863c0432510b
--- /dev/null
+++ b/sound/arm/mxc-alsa-common.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @file mxc-alsa-common.h
+ * @brief
+ * @ingroup SOUND_DRV
+ */
+
+#ifndef __MXC_ALSA_COMMON_H__
+#define __MXC_ALSA_COMMON_H__
+
+/* Enums typically used by the Mixer support APIs
+ * Emunerates IP, OP and mixer sources.
+ */
+
+typedef enum {
+ CODEC_DIR_OUT,
+ MIXER_OUT
+} OUTPUT_SOURCE;
+
+typedef enum {
+ OP_NODEV = -1,
+ OP_EARPIECE,
+ OP_HANDSFREE,
+ OP_HEADSET,
+ OP_LINEOUT,
+ OP_MAXDEV,
+ OP_MONO
+} OUTPUT_DEVICES;
+
+typedef enum {
+ IP_NODEV = -1,
+ IP_HANDSET,
+ IP_HEADSET,
+ IP_LINEIN,
+ IP_MAXDEV
+} INPUT_DEVICES;
+
+extern int mxc_alsa_create_ctl(struct snd_card *card, void *p_value);
+
+extern int set_mixer_output_device(PMIC_AUDIO_HANDLE handle, OUTPUT_SOURCE src,
+ OUTPUT_DEVICES dev, bool enable);
+extern int set_mixer_output_volume(PMIC_AUDIO_HANDLE handle, int volume,
+ OUTPUT_DEVICES dev);
+extern int set_mixer_input_device(PMIC_AUDIO_HANDLE handle, INPUT_DEVICES dev,
+ bool enable);
+extern int set_mixer_output_mono_adder(PMIC_AUDIO_MONO_ADDER_MODE mode);
+extern int set_mixer_input_gain(PMIC_AUDIO_HANDLE handle, int val);
+extern int set_mixer_output_balance(int bal);
+
+extern int get_mixer_output_device(void);
+extern int get_mixer_output_volume(void);
+extern int get_mixer_output_mono_adder(void);
+extern int get_mixer_output_balance(void);
+extern int get_mixer_input_gain(void);
+extern int get_mixer_input_device(void);
+#endif /* __MXC_ALSA_COMMON_H__ */
diff --git a/sound/arm/mxc-alsa-mixer.c b/sound/arm/mxc-alsa-mixer.c
new file mode 100644
index 000000000000..c77120cf48eb
--- /dev/null
+++ b/sound/arm/mxc-alsa-mixer.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @file mxc-alsa-mixer.c
+ * @brief this file implements the mxc sound driver mixer interface for ALSA.
+ * The mxc sound driver supports mono/stereo recording (there are
+ * some limitations due to hardware), mono/stereo playback and
+ * audio mixing. This file implements output switching, volume/balance controls
+ * mono adder config, I/P dev switching and gain on the PCM streams.
+ * Recording supports 8000 khz and 16000 khz sample rate.
+ * Playback supports 8000, 11025, 16000, 22050, 24000, 32000,
+ * 44100 and 48000 khz for mono and stereo.
+ *
+ * @ingroup SOUND_DRV
+ */
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <linux/soundcard.h>
+#include <mach/pmic_audio.h>
+#include "mxc-alsa-common.h"
+/*!
+ * These are the functions implemented in the ALSA PCM driver that
+ * are used for mixer operations
+ *
+ */
+
+/*!
+ * These are the callback functions for mixer controls
+ *
+ */
+/* Output device control*/
+static int pmic_mixer_output_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 15;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+static int pmic_mixer_output_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ int dev, i;
+ dev = uvalue->value.integer.value[0];
+ for (i = OP_EARPIECE; i < OP_MAXDEV; i++) {
+ if (dev & (1 << i)) {
+ set_mixer_output_device(NULL, MIXER_OUT, i, 1);
+ } else {
+ set_mixer_output_device(NULL, MIXER_OUT, i, 0);
+ }
+ }
+ return 0;
+}
+static int pmic_mixer_output_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ int val, ret = 0, i = 0;
+ for (i = OP_EARPIECE; i < OP_MAXDEV; i++) {
+ val = get_mixer_output_device();
+ if (val & SOUND_MASK_PHONEOUT)
+ ret = ret | 1;
+ if (val & SOUND_MASK_SPEAKER)
+ ret = ret | 2;
+ if (val & SOUND_MASK_VOLUME)
+ ret = ret | 4;
+ if (val & SOUND_MASK_PCM)
+ ret = ret | 8;
+ uvalue->value.integer.value[0] = ret;
+ }
+ return 0;
+
+}
+
+/* Input gain control*/
+static int pmic_cap_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+static int pmic_cap_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ int val;
+ val = get_mixer_input_gain();
+ val = val & 0xFF;
+ uvalue->value.integer.value[0] = val;
+ return 0;
+}
+
+static int pmic_cap_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+
+ int vol;
+ vol = uvalue->value.integer.value[0];
+ vol = vol | (vol << 8);
+ set_mixer_input_gain(NULL, vol);
+ return 0;
+}
+
+/* Mono adder control*/
+static int pmic_pb_monoconfig_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 3;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+static int pmic_pb_monoconfig_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ int mono;
+ mono = uvalue->value.integer.value[0];
+ set_mixer_output_mono_adder(mono);
+ return 0;
+}
+static int pmic_pb_monoconfig_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ uvalue->value.integer.value[0] = get_mixer_output_mono_adder();
+ return 0;
+}
+
+/*!
+ * These are the ALSA control structures with init values
+ *
+ */
+
+/* Input device control*/
+static int pmic_cap_input_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 7;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+static int pmic_cap_input_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ int dev, i;
+ dev = uvalue->value.integer.value[0];
+ for (i = IP_HANDSET; i < IP_MAXDEV; i++) {
+ if (dev & (1 << i)) {
+ set_mixer_input_device(NULL, i, 1);
+ } else {
+ set_mixer_input_device(NULL, i, 0);
+ }
+ }
+ return 0;
+}
+static int pmic_cap_input_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ int val, ret = 0, i = 0;
+ for (i = IP_HANDSET; i < IP_MAXDEV; i++) {
+ val = get_mixer_input_device();
+ if (val & SOUND_MASK_PHONEIN)
+ ret = ret | 1;
+ if (val & SOUND_MASK_MIC)
+ ret = ret | 2;
+ if (val & SOUND_MASK_LINE)
+ ret = ret | 4;
+ uvalue->value.integer.value[0] = ret;
+ }
+ return 0;
+}
+
+/* Volume control*/
+static int pmic_pb_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ int volume;
+ volume = uvalue->value.integer.value[0];
+ volume = volume | (volume << 8);
+ set_mixer_output_volume(NULL, volume, OP_NODEV);
+ return 0;
+}
+static int pmic_pb_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int pmic_pb_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ int val;
+ val = get_mixer_output_volume();
+ val = val & 0xFF;
+ uvalue->value.integer.value[0] = val;
+ return 0;
+}
+
+/* Balance control start */
+static int pmic_pb_balance_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int pmic_pb_balance_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ uvalue->value.integer.value[0] = get_mixer_output_balance();
+ return 0;
+
+}
+static int pmic_pb_balance_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ int bal;
+ bal = uvalue->value.integer.value[0];
+ set_mixer_output_balance(bal);
+ return 0;
+}
+
+/* Balance control end */
+
+/* loopback control start */
+static int pmic_loopback_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int pmic_loopback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ uvalue->value.integer.value[0] = kcontrol->private_value;
+ return 0;
+
+}
+static int pmic_loopback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ int changed;
+ long flag = uvalue->value.integer.value[0];
+ changed =
+ (uvalue->value.integer.value[0] == kcontrol->private_value) ? 0 : 1;
+ kcontrol->private_value = uvalue->value.integer.value[0];
+ if (flag)
+ pmic_audio_fm_output_enable(true);
+ else
+ pmic_audio_fm_output_enable(false);
+
+ return changed;
+}
+
+/* Loopback control end */
+
+/* Kcontrol structure definitions */
+struct snd_kcontrol_new pmic_control_pb_vol __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .index = 0x00,
+ .info = pmic_pb_volume_info,
+ .get = pmic_pb_volume_get,
+ .put = pmic_pb_volume_put,
+ .private_value = 0xffab1,
+};
+
+struct snd_kcontrol_new pmic_control_pb_bal __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Balance Playback Volume",
+ .index = 0x00,
+ .info = pmic_pb_balance_info,
+ .get = pmic_pb_balance_get,
+ .put = pmic_pb_balance_put,
+ .private_value = 0xffab2,
+};
+struct snd_kcontrol_new pmic_control_pb_monoconfig __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Monoconfig Playback Volume",
+ .index = 0x00,
+ .info = pmic_pb_monoconfig_info,
+ .get = pmic_pb_monoconfig_get,
+ .put = pmic_pb_monoconfig_put,
+ .private_value = 0xffab2,
+};
+struct snd_kcontrol_new pmic_control_op_sw __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Output Playback Volume",
+ .index = 0x00,
+ .info = pmic_mixer_output_info,
+ .get = pmic_mixer_output_get,
+ .put = pmic_mixer_output_put,
+ .private_value = 0xffab4,
+};
+
+struct snd_kcontrol_new pmic_control_cap_vol __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Capture Volume",
+ .index = 0x00,
+ .info = pmic_cap_volume_info,
+ .get = pmic_cap_volume_get,
+ .put = pmic_cap_volume_put,
+ .private_value = 0xffab5,
+};
+struct snd_kcontrol_new pmic_control_ip_sw __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Input Capture Volume",
+ .index = 0x00,
+ .info = pmic_cap_input_info,
+ .get = pmic_cap_input_get,
+ .put = pmic_cap_input_put,
+ .private_value = 0xffab5,
+};
+
+struct snd_kcontrol_new pmic_control_loop_out __devinitdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Loopback Line-in",
+ .index = 0x00,
+ .info = pmic_loopback_info,
+ .get = pmic_loopback_get,
+ .put = pmic_loopback_put,
+ .private_value = 0,
+};
+
+/*!
+ * This function registers the control components of ALSA Mixer
+ * It is called by ALSA PCM init.
+ *
+ * @param card pointer to the ALSA sound card structure.
+ *
+ * @return 0 on success, -ve otherwise.
+ */
+int __devinit mxc_alsa_create_ctl(struct snd_card *card, void *p_value)
+{
+ int err = 0;
+
+ if ((err =
+ snd_ctl_add(card, snd_ctl_new1(&pmic_control_op_sw, p_value))) < 0)
+ return err;
+
+ if ((err =
+ snd_ctl_add(card,
+ snd_ctl_new1(&pmic_control_pb_vol, p_value))) < 0)
+ return err;
+ if ((err =
+ snd_ctl_add(card,
+ snd_ctl_new1(&pmic_control_pb_monoconfig,
+ p_value))) < 0)
+ return err;
+ if ((err =
+ snd_ctl_add(card,
+ snd_ctl_new1(&pmic_control_pb_bal, p_value))) < 0)
+ return err;
+ if ((err =
+ snd_ctl_add(card,
+ snd_ctl_new1(&pmic_control_cap_vol, p_value))) < 0)
+ return err;
+ if ((err =
+ snd_ctl_add(card, snd_ctl_new1(&pmic_control_ip_sw, p_value))) < 0)
+ return err;
+ err = snd_ctl_add(card, snd_ctl_new1(&pmic_control_loop_out, p_value));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(mxc_alsa_create_ctl);
diff --git a/sound/arm/mxc-alsa-pmic.c b/sound/arm/mxc-alsa-pmic.c
new file mode 100644
index 000000000000..be824ef0f4c8
--- /dev/null
+++ b/sound/arm/mxc-alsa-pmic.c
@@ -0,0 +1,3790 @@
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @defgroup SOUND_DRV MXC Sound Driver for ALSA
+ */
+
+ /*!
+ * @file mxc-alsa-pmic.c
+ * @brief this fle mxc-alsa-pmic.c
+ * @brief this file implements the mxc sound driver interface for ALSA.
+ * The mxc sound driver supports mono/stereo recording (there are
+ * some limitations due to hardware), mono/stereo playback and
+ * audio mixing.
+ * Recording supports 8000 khz and 16000 khz sample rate.
+ * Playback supports 8000, 11025, 16000, 22050, 24000, 32000,
+ * 44100, 48000 and 96000 Hz for mono and stereo.
+ * This file also handles the software mixer and abstraction APIs
+ * that control the volume,balance,mono-adder,input and output
+ * devices for PMIC.
+ * These mixer controls shall be accessible thru alsa as well as
+ * OSS emulation modes
+ *
+ * @ingroup SOUND_DRV
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/soundcard.h>
+#include <linux/pmic_external.h>
+#include <linux/pm.h>
+#include <linux/fs.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+
+#include <mach/pmic_audio.h>
+#include <mach/dma.h>
+#include <asm/mach-types.h>
+
+#include <ssi/ssi.h>
+#include <ssi/registers.h>
+#include <dam/dam.h>
+
+#include "mxc-alsa-pmic.h"
+#include "mxc-alsa-common.h"
+
+/*
+ * PMIC driver buffer policy.
+ * Customize here if the sound is not correct
+ */
+#define MAX_BUFFER_SIZE (32*1024)
+#define DMA_BUF_SIZE (8*1024)
+#define MIN_PERIOD_SIZE 64
+#define MIN_PERIOD 2
+#define MAX_PERIOD 255
+
+#define AUD_MUX_CONF 0x0031010
+#define MASK_2_TS 0xfffffffc
+#define MASK_1_TS 0xfffffffd
+#define MASK_1_TS_MIX 0xfffffffc
+#define MASK_1_TS_STDAC 0xfffffffe
+#define MASK_1_TS_REC 0xfffffffe
+#define SOUND_CARD_NAME "MXC"
+
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+#define MAX_IRAM_SIZE (IRAM_SIZE - CONFIG_SDMA_IRAM_SIZE)
+#define DMA_IRAM_SIZE (4*1024)
+#define ADMA_BASE_PADDR (IRAM_BASE_ADDR + CONFIG_SDMA_IRAM_SIZE)
+#define ADMA_BASE_VADDR (IRAM_BASE_ADDR_VIRT + CONFIG_SDMA_IRAM_SIZE)
+
+#if (MAX_IRAM_SIZE + CONFIG_SDMA_IRAM_SIZE) > IRAM_SIZE
+#error "The IRAM size required has beyond the limitation of IC spec"
+#endif
+
+#if (MAX_IRAM_SIZE&(DMA_IRAM_SIZE-1))
+#error "The IRAM size for DMA ring buffer should be multiples of dma buffer size"
+#endif
+
+#endif /* CONFIG_SND_MXC_PMIC_IRAM */
+
+/*!
+ * These defines enable DMA chaining for playback
+ * and capture respectively.
+ */
+#define MXC_SOUND_PLAYBACK_CHAIN_DMA_EN 1
+#define MXC_SOUND_CAPTURE_CHAIN_DMA_EN 1
+
+/*!
+ * ID for this card
+ */
+static char *id = NULL;
+
+#define MXC_ALSA_MAX_PCM_DEV 3
+#define MXC_ALSA_MAX_PLAYBACK 3
+#define MXC_ALSA_MAX_CAPTURE 1
+
+struct mxc_audio_platform_data *audio_data;
+/*!
+ * This structure is the global configuration of the soundcard
+ * that are accessed by the mixer as well as by the playback/recording
+ * stream. This contains various volume, balance, mono adder settings
+ *
+ */
+typedef struct audio_mixer_control {
+
+ /*!
+ * This variable holds the current active output device(s)
+ */
+ int output_device;
+
+ /*!
+ * This variable holds the current active input device.
+ */
+ int input_device;
+
+ /* Used only for playback/recording on codec .. Use 1 for playback
+ * and 0 for recording*/
+ int direction;
+
+ /*!
+ * This variable holds the current source for active ouput device(s)
+ */
+ OUTPUT_SOURCE source_for_output[OP_MAXDEV];
+
+ /*!
+ * This variable says if a given output device is part of an ongoing
+ * playback. This variable will be set and reset by the playback stream
+ * when stream is activated and when stream is closed. This shall also
+ * be set and reset my mixer functions for enabling/disabling output devs
+ */
+ int output_active[OP_MAXDEV];
+
+ /*!
+ * This variable holds the current volume for active input device.
+ * This maps to the input gain of recording device
+ */
+ int input_volume;
+
+ /*!
+ * This variable holds the current volume for playback devices.
+ */
+ //int output_volume[OP_MAXDEV];
+ int master_volume_out;
+
+ /*!
+ * This variable holds the balance setting for the mixer out.
+ * The range is 0 to 100. 50 means both L and R equal.
+ * < 50 attenuates left side and > 50 attenualtes right side
+ */
+ int mixer_balance;
+
+ /*!
+ * This variable holds the current mono adder config.
+ */
+ PMIC_AUDIO_MONO_ADDER_MODE mixer_mono_adder;
+
+ /*!
+ * Semaphore used to control the access to this structure.
+ */
+ struct semaphore sem;
+
+ /*!
+ * These variables are set by PCM stream and mixer when the voice codec's / ST dac's outputs are
+ * connected to the analog mixer of PMIC audio chip
+ */
+ int codec_out_to_mixer;
+ int stdac_out_to_mixer;
+
+ int codec_playback_active;
+ int codec_capture_active;
+ int stdac_playback_active;
+ int mixing_active;
+
+ /*!
+ * This variable holds the configuration of the headset which was previously enabled.
+ */
+ int old_prof;
+
+ PMIC_AUDIO_HANDLE stdac_handle;
+ PMIC_AUDIO_HANDLE voice_codec_handle;
+
+} audio_mixer_control_t;
+
+/*!
+ * This structure stores current state of audio configuration
+ * soundcard wrt a specific stream (playback on different DACs, recording on the codec etc).
+ * It is used to set/get current values and are NOT accessed by the Mixer. This structure shall
+ * be retrieved thru pcm substream pointer and hence the mixer component will have no access
+ * to it. There will be as many structures as the number of streams. In our case it's 3. Codec playback
+ * STDAC playback and voice codec recording.
+ * This structure will be used at the beginning of activating a stream to configure audio chip.
+ *
+ */
+typedef struct pmic_audio_device {
+
+ PMIC_AUDIO_HANDLE handle;
+ /*!
+ * This variable holds the sample rate currently being used.
+ */
+ int sample_rate;
+
+ /*!
+ * This variable holds the current protocol PMIC is using.
+ * PMIC can use one of three protocols at any given time:
+ * normal, network and I2S.
+ */
+ int protocol;
+
+ /*!
+ * This variables tells us whether PMIC runs in
+ * master mode (PMIC generates audio clocks)or slave mode (AP side
+ * generates audio clocks)
+ *
+ * Currently the default mode is master mode because PMIC clocks have
+ * higher precision.
+ */
+ int mode;
+
+ /* This variable holds the value representing the
+ * base clock PMIC will use to generate internal
+ * clocks (BCL clock and FrameSync clock)
+ */
+ int pll;
+
+ /*!
+ * This variable holds the SSI to which PMIC is currently connected.
+ */
+ int ssi;
+
+ /*!
+ * This variable tell us whether bit clock is inverted or not.
+ */
+ int bcl_inverted;
+
+ /*!
+ * This variable tell us whether frame clock is inverted or not.
+ */
+ int fs_inverted;
+
+ /*!
+ * This variable holds the pll used for PMIC audio operations.
+ */
+ int pll_rate;
+
+ /*!
+ * This variable holds the filter that PMIC is applying to
+ * CODEC operations.
+ */
+ int codec_filter;
+
+} pmic_audio_device_t;
+
+/*!
+ * This structure represents an audio stream in term of
+ * channel DMA, HW configuration on PMIC and on AudioMux/SSI
+ */
+typedef struct audio_stream {
+ /*!
+ * identification string
+ */
+ char *id;
+
+ /*!
+ * numeric identification
+ */
+ int stream_id;
+
+ /*!
+ * SSI ID on the ARM side
+ */
+ int ssi;
+
+ /*!
+ * DAM port on the ARM side
+ */
+ int dam_port;
+
+ /*!
+ * device identifier for DMA
+ */
+ int dma_wchannel;
+
+ /*!
+ * we are using this stream for transfer now
+ */
+ int active:1;
+
+ /*!
+ * current transfer period
+ */
+ int period;
+
+ /*!
+ * current count of transfered periods
+ */
+ int periods;
+
+ /*!
+ * are we recording - flag used to do DMA trans. for sync
+ */
+ int tx_spin;
+
+ /*!
+ * Previous offset value for resume
+ */
+ unsigned int old_offset;
+#if 0
+ /*!
+ * Path for this stream
+ */
+ device_data_t stream_device;
+#endif
+
+ /*!
+ * pmic audio chip stream specific configuration
+ */
+ pmic_audio_device_t pmic_audio_device;
+
+ /*!
+ * for locking in DMA operations
+ */
+ spinlock_t dma_lock;
+
+ /*!
+ * Alsa substream pointer
+ */
+ struct snd_pcm_substream *stream;
+} audio_stream_t;
+
+/*!
+ * This structure represents the PMIC sound card with its
+ * 2 streams (StDac and Codecs) and its shared parameters
+ */
+typedef struct snd_card_mxc_pmic_audio {
+ /*!
+ * ALSA sound card handle
+ */
+ struct snd_card *card;
+
+ /*!
+ * ALSA pcm driver type handle
+ */
+ struct snd_pcm *pcm[MXC_ALSA_MAX_PCM_DEV];
+
+ /*!
+ * playback & capture streams handle
+ * We can support a maximum of two playback streams (voice-codec
+ * and ST-DAC) and 1 recording stream
+ */
+ audio_stream_t s[MXC_ALSA_MAX_CAPTURE + MXC_ALSA_MAX_PLAYBACK];
+
+} mxc_pmic_audio_t;
+
+/*!
+ * pmic audio chip parameters for IP/OP and volume controls
+ */
+audio_mixer_control_t audio_mixer_control;
+
+/*!
+ * Global variable that represents the PMIC soundcard
+ * with its 2 availables stream devices: stdac and codec
+ */
+mxc_pmic_audio_t *mxc_audio = NULL;
+
+/*!
+ * Supported playback rates array
+ */
+static unsigned int playback_rates_stereo[] = {
+ 8000,
+ 11025,
+ 12000,
+ 16000,
+ 22050,
+ 24000,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 96000,
+};
+
+static unsigned int playback_rates_mono[] = {
+ 8000,
+ 16000,
+};
+
+/*!
+ * Supported capture rates array
+ */
+static unsigned int capture_rates[] = {
+ 8000,
+ 16000,
+};
+
+/*!
+ * this structure represents the sample rates supported
+ * by PMIC for playback operations on StDac.
+ */
+static struct snd_pcm_hw_constraint_list hw_playback_rates_stereo = {
+ .count = ARRAY_SIZE(playback_rates_stereo),
+ .list = playback_rates_stereo,
+ .mask = 0,
+};
+
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+static spinlock_t g_audio_iram_lock = SPIN_LOCK_UNLOCKED;
+static int g_audio_iram_en = 1;
+static int g_device_opened = 0;
+extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end);
+
+static inline int mxc_snd_enable_iram(int enable)
+{
+ int ret = -EBUSY;
+ unsigned long flags;
+ spin_lock_irqsave(&g_audio_iram_lock, flags);
+ if (!g_device_opened) {
+ g_audio_iram_en = (enable != 0);
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&g_audio_iram_lock, flags);
+ return ret;
+}
+
+static inline void mxc_snd_pcm_iram_get(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&g_audio_iram_lock, flags);
+ g_audio_iram_en++;
+ spin_unlock_irqrestore(&g_audio_iram_lock, flags);
+}
+
+static inline void mxc_snd_pcm_iram_put(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&g_audio_iram_lock, flags);
+ g_audio_iram_en--;
+ spin_unlock_irqrestore(&g_audio_iram_lock, flags);
+}
+
+struct snd_dma_buffer g_iram_dmab;
+
+#endif /* CONFIG_SND_MXC_PMIC_IRAM */
+
+/*!
+ * this structure represents the sample rates supported
+ * by PMIC for playback operations on Voice codec.
+ */
+static struct snd_pcm_hw_constraint_list hw_playback_rates_mono = {
+ .count = ARRAY_SIZE(playback_rates_mono),
+ .list = playback_rates_mono,
+ .mask = 0,
+};
+
+/*!
+ * this structure represents the sample rates supported
+ * by PMIC for capture operations on Codec.
+ */
+static struct snd_pcm_hw_constraint_list hw_capture_rates = {
+ .count = ARRAY_SIZE(capture_rates),
+ .list = capture_rates,
+ .mask = 0,
+};
+
+#ifdef CONFIG_HEADSET_DETECT_ENABLE
+static PMIC_HS_STATE hs_state;
+
+/*!
+ *This is used to maintain the state of the Headset*/
+static int headset_state = 0;
+
+/*Callback for headset event. */
+static void HSCallback(const PMIC_HS_STATE hs_st)
+{
+
+ msleep(10);
+
+ if (headset_state == 1) {
+ pmic_audio_output_disable_phantom_ground();
+ headset_state = 0;
+ if (audio_mixer_control.stdac_playback_active)
+ pmic_audio_output_clear_port(audio_mixer_control.
+ stdac_handle,
+ STEREO_HEADSET_LEFT |
+ STEREO_HEADSET_RIGHT);
+ else if (audio_mixer_control.voice_codec_handle)
+ pmic_audio_output_clear_port(audio_mixer_control.
+ voice_codec_handle,
+ STEREO_HEADSET_LEFT |
+ STEREO_HEADSET_RIGHT);
+
+ if (audio_mixer_control.old_prof & (SOUND_MASK_PHONEOUT)) {
+ set_mixer_output_device(NULL, MIXER_OUT, OP_EARPIECE,
+ 1);
+ }
+ if (audio_mixer_control.old_prof & (SOUND_MASK_VOLUME)) {
+ set_mixer_output_device(NULL, MIXER_OUT, OP_HANDSFREE,
+ 1);
+ }
+ if (audio_mixer_control.old_prof & (SOUND_MASK_SPEAKER)) {
+#ifdef CONFIG_HEADSET_DETECT_ENABLE
+ /*This is a temporary workaround which should be removed later */
+ set_mixer_output_device(NULL, MIXER_OUT, OP_MONO, 1);
+#else
+ set_mixer_output_device(NULL, MIXER_OUT, OP_HEADSET, 1);
+#endif
+ }
+ if (audio_mixer_control.old_prof & (SOUND_MASK_PCM)) {
+ set_mixer_output_device(NULL, MIXER_OUT, OP_LINEOUT, 1);
+ }
+
+ } else {
+ headset_state = 1;
+
+ pmic_audio_output_enable_phantom_ground();
+
+ audio_mixer_control.old_prof =
+ audio_mixer_control.output_device;
+
+ if (audio_mixer_control.old_prof & (SOUND_MASK_PHONEOUT)) {
+ set_mixer_output_device(NULL, MIXER_OUT, OP_EARPIECE,
+ 0);
+ }
+ if (audio_mixer_control.old_prof & (SOUND_MASK_VOLUME)) {
+ set_mixer_output_device(NULL, MIXER_OUT, OP_HANDSFREE,
+ 0);
+ }
+ if (audio_mixer_control.old_prof & (SOUND_MASK_SPEAKER)) {
+ set_mixer_output_device(NULL, MIXER_OUT, OP_HEADSET, 0);
+ }
+ if (audio_mixer_control.old_prof & (SOUND_MASK_PCM)) {
+ set_mixer_output_device(NULL, MIXER_OUT, OP_LINEOUT, 0);
+ }
+ /*This is a temporary workaround which should be removed later */
+ set_mixer_output_device(NULL, MIXER_OUT, OP_MONO, 1);
+
+ }
+}
+
+#endif
+/*!
+ * This function configures audio multiplexer to support
+ * audio data routing in PMIC master mode.
+ *
+ * @param ssi SSI of the ARM to connect to the DAM.
+ */
+void configure_dam_pmic_master(int ssi)
+{
+ int source_port;
+ int target_port;
+
+ if (ssi == SSI1) {
+ pr_debug("DAM: port 1 -> port 4\n");
+ source_port = audio_data->src_port;
+
+ target_port = port_4;
+ } else {
+ pr_debug("DAM: port 2 -> port 5\n");
+ source_port = port_2;
+ target_port = port_5;
+ }
+
+ dam_reset_register(source_port);
+ dam_reset_register(target_port);
+
+ dam_select_mode(source_port, normal_mode);
+ dam_select_mode(target_port, internal_network_mode);
+
+ dam_set_synchronous(source_port, true);
+ dam_set_synchronous(target_port, true);
+
+ dam_select_RxD_source(source_port, target_port);
+ dam_select_RxD_source(target_port, source_port);
+
+ dam_select_TxFS_direction(source_port, signal_out);
+ dam_select_TxFS_source(source_port, false, target_port);
+
+ dam_select_TxClk_direction(source_port, signal_out);
+ dam_select_TxClk_source(source_port, false, target_port);
+
+ dam_select_RxFS_direction(source_port, signal_out);
+ dam_select_RxFS_source(source_port, false, target_port);
+
+ dam_select_RxClk_direction(source_port, signal_out);
+ dam_select_RxClk_source(source_port, false, target_port);
+
+ dam_set_internal_network_mode_mask(target_port, 0xfc);
+
+ writel(AUD_MUX_CONF, IO_ADDRESS(AUDMUX_BASE_ADDR) + 0x38);
+}
+
+/*!
+ * This function configures the SSI in order to receive audio
+ * from PMIC (recording). Configuration of SSI consists mainly in
+ * setting the following:
+ *
+ * 1) SSI to use (SSI1 or SSI2)
+ * 2) SSI mode (normal or network. We use always network mode)
+ * 3) SSI STCCR register settings, which control the sample rate (BCL and
+ * FS clocks)
+ * 4) Watermarks for SSI FIFOs as well as timeslots to be used.
+ * 5) Enable SSI.
+ *
+ * @param substream pointer to the structure of the current stream.
+ */
+void configure_ssi_rx(struct snd_pcm_substream *substream)
+{
+ mxc_pmic_audio_t *chip;
+ audio_stream_t *s;
+ int ssi;
+
+ chip = snd_pcm_substream_chip(substream);
+ s = &chip->s[substream->pstr->stream];
+ ssi = s->ssi;
+
+ pr_debug("configure_ssi_rx: SSI %d\n", ssi + 1);
+
+ ssi_enable(ssi, false);
+ ssi_synchronous_mode(ssi, true);
+ ssi_network_mode(ssi, true);
+
+ if (machine_is_mx27ads()) {
+ ssi_tx_clock_divide_by_two(ssi, 0);
+ ssi_tx_clock_prescaler(ssi, 0);
+ ssi_tx_frame_rate(ssi, 2);
+ }
+ /* OJO */
+ ssi_tx_frame_rate(ssi, 1);
+
+ ssi_tx_early_frame_sync(ssi, ssi_frame_sync_one_bit_before);
+ ssi_tx_frame_sync_length(ssi, ssi_frame_sync_one_bit);
+ ssi_tx_word_length(ssi, ssi_16_bits);
+
+ ssi_rx_early_frame_sync(ssi, ssi_frame_sync_one_bit_before);
+ ssi_rx_frame_sync_length(ssi, ssi_frame_sync_one_bit);
+ ssi_rx_fifo_enable(ssi, ssi_fifo_0, true);
+ ssi_rx_bit0(ssi, true);
+
+ ssi_rx_fifo_full_watermark(ssi, ssi_fifo_0, RX_WATERMARK);
+
+ /* We never use the divider by 2 implemented in SSI */
+ ssi_rx_clock_divide_by_two(ssi, 0);
+
+ /* Set prescaler range (a fixed divide-by-eight prescaler
+ * in series with the variable prescaler) to 0 as we don't
+ * need it.
+ */
+ ssi_rx_clock_prescaler(ssi, 0);
+
+ /* Currently, only supported sample length is 16 bits */
+ ssi_rx_word_length(ssi, ssi_16_bits);
+
+ /* set direction of clocks ("externally" means that clocks come
+ * from PMIC to MCU)
+ */
+ ssi_rx_frame_direction(ssi, ssi_tx_rx_externally);
+ ssi_rx_clock_direction(ssi, ssi_tx_rx_externally);
+
+ /* Frame Rate Divider Control.
+ * In Normal mode, this ratio determines the word
+ * transfer rate. In Network mode, this ration sets
+ * the number of words per frame.
+ */
+ ssi_tx_frame_rate(ssi, 4);
+ ssi_rx_frame_rate(ssi, 4);
+
+ ssi_enable(ssi, true);
+}
+
+/*!
+ * This function configures the SSI in order to
+ * send data to PMIC. Configuration of SSI consists
+ * mainly in setting the following:
+ *
+ * 1) SSI to use (SSI1 or SSI2)
+ * 2) SSI mode (normal for normal use e.g. playback, network for mixing)
+ * 3) SSI STCCR register settings, which control the sample rate (BCL and
+ * FS clocks)
+ * 4) Watermarks for SSI FIFOs as well as timeslots to be used.
+ * 5) Enable SSI.
+ *
+ * @param substream pointer to the structure of the current stream.
+ */
+void configure_ssi_tx(struct snd_pcm_substream *substream)
+{
+ mxc_pmic_audio_t *chip;
+ audio_stream_t *s;
+ struct snd_pcm_runtime *runtime;
+ int ssi;
+ int device, stream_id = -1;
+ device = substream->pcm->device;
+ if (device == 0)
+ stream_id = 0;
+ else if (device == 1)
+ stream_id = 2;
+ else
+ stream_id = 3;
+
+ chip = snd_pcm_substream_chip(substream);
+ s = &chip->s[stream_id];
+ runtime = substream->runtime;
+ ssi = s->ssi;
+
+ pr_debug("configure_ssi_tx: SSI %d\n", ssi + 1);
+
+ ssi_enable(ssi, false);
+ ssi_synchronous_mode(ssi, true);
+ if (runtime->channels == 1) {
+ if (stream_id == 2) {
+ ssi_network_mode(ssi, true);
+ } else {
+#ifndef CONFIG_SND_MXC_PLAYBACK_MIXING
+ ssi_network_mode(ssi, false);
+#endif
+ }
+ } else {
+ ssi_network_mode(ssi, true);
+ }
+
+#ifdef CONFIG_SND_MXC_PLAYBACK_MIXING
+ ssi_two_channel_mode(ssi, true);
+ ssi_tx_fifo_enable(ssi, ssi_fifo_1, true);
+ ssi_tx_fifo_empty_watermark(ssi, ssi_fifo_1, TX_WATERMARK);
+#endif
+
+ ssi_tx_early_frame_sync(ssi, ssi_frame_sync_one_bit_before);
+ ssi_tx_frame_sync_length(ssi, ssi_frame_sync_one_bit);
+ ssi_tx_fifo_enable(ssi, ssi_fifo_0, true);
+ ssi_tx_bit0(ssi, true);
+
+ ssi_tx_fifo_empty_watermark(ssi, ssi_fifo_0, TX_WATERMARK);
+
+ /* We never use the divider by 2 implemented in SSI */
+ ssi_tx_clock_divide_by_two(ssi, 0);
+
+ ssi_tx_clock_prescaler(ssi, 0);
+
+ /*Currently, only supported sample length is 16 bits */
+ ssi_tx_word_length(ssi, ssi_16_bits);
+
+ /* clocks are being provided by PMIC */
+ ssi_tx_frame_direction(ssi, ssi_tx_rx_externally);
+ ssi_tx_clock_direction(ssi, ssi_tx_rx_externally);
+
+ if (runtime->channels == 1) {
+#ifndef CONFIG_SND_MXC_PLAYBACK_MIXING
+ if (stream_id == 2) {
+ ssi_tx_frame_rate(ssi, 4);
+ } else {
+ ssi_tx_frame_rate(ssi, 1);
+ }
+#else
+ if (stream_id == 2) {
+ ssi_tx_frame_rate(ssi, 2);
+ }
+#endif
+
+ } else {
+ ssi_tx_frame_rate(ssi, 2);
+ }
+
+ ssi_enable(ssi, true);
+}
+
+/*!
+ * This function normalizes speed given by the user
+ * if speed is not supported, the function will
+ * calculate the nearest one.
+ *
+ * @param speed speed requested by the user.
+ *
+ * @return The normalized speed.
+ */
+int adapt_speed(int speed)
+{
+
+ /* speeds from 8k to 96k */
+ if (speed >= (64000 + 96000) / 2) {
+ speed = 96000;
+ } else if (speed >= (48000 + 64000) / 2) {
+ speed = 64000;
+ } else if (speed >= (44100 + 48000) / 2) {
+ speed = 48000;
+ } else if (speed >= (32000 + 44100) / 2) {
+ speed = 44100;
+ } else if (speed >= (24000 + 32000) / 2) {
+ speed = 32000;
+ } else if (speed >= (22050 + 24000) / 2) {
+ speed = 24000;
+ } else if (speed >= (16000 + 22050) / 2) {
+ speed = 22050;
+ } else if (speed >= (12000 + 16000) / 2) {
+ speed = 16000;
+ } else if (speed >= (11025 + 12000) / 2) {
+ speed = 12000;
+ } else if (speed >= (8000 + 11025) / 2) {
+ speed = 11025;
+ } else {
+ speed = 8000;
+ }
+ return speed;
+}
+
+/*!
+ * This function get values to be put in PMIC registers.
+ * This values represents the sample rate that PMIC
+ * should use for current playback or recording.
+ *
+ * @param substream pointer to the structure of the current stream.
+ */
+void normalize_speed_for_pmic(struct snd_pcm_substream *substream)
+{
+ mxc_pmic_audio_t *chip;
+ audio_stream_t *s;
+ pmic_audio_device_t *pmic_device;
+ struct snd_pcm_runtime *runtime;
+ int device, stream_id = -1;
+ device = substream->pcm->device;
+ if (device == 0) {
+ if ((audio_mixer_control.codec_capture_active == 1)
+ && (substream->stream == 1)) {
+ stream_id = 1;
+ } else
+ stream_id = 0;
+ } else {
+ stream_id = 2;
+ }
+
+ chip = snd_pcm_substream_chip(substream);
+ s = &chip->s[stream_id];
+ pmic_device = &s->pmic_audio_device;
+ runtime = substream->runtime;
+
+ /* As the driver allows continuous sample rate, we must adapt the rate */
+ runtime->rate = adapt_speed(runtime->rate);
+
+ if (pmic_device->handle == audio_mixer_control.voice_codec_handle) {
+ switch (runtime->rate) {
+ case 8000:
+ pmic_device->sample_rate = VCODEC_RATE_8_KHZ;
+ break;
+ case 16000:
+ pmic_device->sample_rate = VCODEC_RATE_16_KHZ;
+ break;
+ default:
+ pmic_device->sample_rate = VCODEC_RATE_8_KHZ;
+ break;
+ }
+
+ } else if (pmic_device->handle == audio_mixer_control.stdac_handle) {
+ switch (runtime->rate) {
+ case 8000:
+ pmic_device->sample_rate = STDAC_RATE_8_KHZ;
+ break;
+
+ case 11025:
+ pmic_device->sample_rate = STDAC_RATE_11_025_KHZ;
+ break;
+
+ case 12000:
+ pmic_device->sample_rate = STDAC_RATE_12_KHZ;
+ break;
+
+ case 16000:
+ pmic_device->sample_rate = STDAC_RATE_16_KHZ;
+ break;
+
+ case 22050:
+ pmic_device->sample_rate = STDAC_RATE_22_050_KHZ;
+ break;
+
+ case 24000:
+ pmic_device->sample_rate = STDAC_RATE_24_KHZ;
+ break;
+
+ case 32000:
+ pmic_device->sample_rate = STDAC_RATE_32_KHZ;
+ break;
+
+ case 44100:
+ pmic_device->sample_rate = STDAC_RATE_44_1_KHZ;
+ break;
+
+ case 48000:
+ pmic_device->sample_rate = STDAC_RATE_48_KHZ;
+ break;
+
+ case 64000:
+ pmic_device->sample_rate = STDAC_RATE_64_KHZ;
+ break;
+
+ case 96000:
+ pmic_device->sample_rate = STDAC_RATE_96_KHZ;
+ break;
+
+ default:
+ pmic_device->sample_rate = STDAC_RATE_8_KHZ;
+ }
+ }
+
+}
+
+/*!
+ * This function configures number of channels for next audio operation
+ * (recording/playback) Number of channels define if sound is stereo
+ * or mono.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+void set_pmic_channels(struct snd_pcm_substream *substream)
+{
+ mxc_pmic_audio_t *chip;
+ audio_stream_t *s;
+ struct snd_pcm_runtime *runtime;
+ int device = -1, stream_id = -1;
+
+ chip = snd_pcm_substream_chip(substream);
+ device = substream->pcm->device;
+
+ if (device == 0) {
+ if (substream->pstr->stream == 1) {
+ stream_id = 1;
+ } else {
+ stream_id = 0;
+ }
+ } else {
+ stream_id = 2;
+ }
+ s = &chip->s[stream_id];
+ runtime = substream->runtime;
+
+ if (runtime->channels == 2) {
+ ssi_tx_mask_time_slot(s->ssi, MASK_2_TS);
+ ssi_rx_mask_time_slot(s->ssi, MASK_1_TS_REC);
+ } else {
+ if (stream_id == 2) {
+#ifdef CONFIG_MXC_PMIC_SC55112
+ ssi_tx_mask_time_slot(s->ssi, MASK_1_TS_REC);
+#else
+
+ if (audio_mixer_control.mixing_active == 1)
+ ssi_tx_mask_time_slot(s->ssi, MASK_1_TS_MIX);
+ else
+ ssi_tx_mask_time_slot(s->ssi, MASK_1_TS);
+
+#endif
+ } else {
+
+ ssi_tx_mask_time_slot(s->ssi, MASK_1_TS_STDAC);
+ }
+#ifndef CONFIG_SND_MXC_PLAYBACK_MIXING
+ ssi_rx_mask_time_slot(s->ssi, MASK_1_TS_REC);
+#endif
+
+ }
+
+}
+
+/*!
+ * This function sets the input device in PMIC. It takes an
+ * ALSA value and modifies registers using pmic-specific values.
+ *
+ * @param handle Handle to the PMIC device opened
+ * @param val ALSA value. This value defines the input device that
+ * PMIC should activate to get audio signal (recording)
+ * @param enable Whether to enable or diable the input
+ */
+int set_mixer_input_device(PMIC_AUDIO_HANDLE handle, INPUT_DEVICES dev,
+ bool enable)
+{
+
+ if (down_interruptible(&audio_mixer_control.sem))
+ return -EINTR;
+ if (handle != NULL) {
+ if (audio_mixer_control.input_device & SOUND_MASK_PHONEIN) {
+ pmic_audio_vcodec_set_mic(handle, MIC1_LEFT,
+ MIC1_RIGHT_MIC_MONO);
+ pmic_audio_vcodec_enable_micbias(handle, MIC_BIAS1);
+ } else {
+ pmic_audio_vcodec_set_mic_on_off(handle,
+ MIC1_LEFT,
+ MIC1_RIGHT_MIC_MONO);
+ pmic_audio_vcodec_disable_micbias(handle, MIC_BIAS1);
+ }
+ if (audio_mixer_control.input_device & SOUND_MASK_MIC) {
+ pmic_audio_vcodec_set_mic(handle, NO_MIC, MIC2_AUX);
+ pmic_audio_vcodec_enable_micbias(handle, MIC_BIAS2);
+ } else {
+ pmic_audio_vcodec_set_mic_on_off(handle, NO_MIC,
+ MIC2_AUX);
+ pmic_audio_vcodec_disable_micbias(handle, MIC_BIAS2);
+ }
+ if (audio_mixer_control.input_device & SOUND_MASK_LINE) {
+ pmic_audio_vcodec_set_mic(handle, NO_MIC, TXIN_EXT);
+ } else {
+ pmic_audio_vcodec_set_mic_on_off(handle, NO_MIC,
+ TXIN_EXT);
+ }
+ up(&audio_mixer_control.sem);
+ return 0;
+
+ }
+ switch (dev) {
+ case IP_HANDSET:
+ pr_debug("Input: SOUND_MASK_PHONEIN \n");
+ if (handle == NULL) {
+ if (enable) {
+ if (audio_mixer_control.codec_capture_active) {
+ handle =
+ audio_mixer_control.
+ voice_codec_handle;
+ pmic_audio_vcodec_set_mic(handle,
+ MIC1_LEFT,
+ MIC1_RIGHT_MIC_MONO);
+ pmic_audio_vcodec_enable_micbias(handle,
+ MIC_BIAS1);
+ }
+ audio_mixer_control.input_device |=
+ SOUND_MASK_PHONEIN;
+ } else {
+ if (audio_mixer_control.codec_capture_active) {
+ handle =
+ audio_mixer_control.
+ voice_codec_handle;
+ pmic_audio_vcodec_set_mic_on_off(handle,
+ MIC1_LEFT,
+ MIC1_RIGHT_MIC_MONO);
+ pmic_audio_vcodec_disable_micbias
+ (handle, MIC_BIAS1);
+ }
+ audio_mixer_control.input_device &=
+ ~SOUND_MASK_PHONEIN;
+ }
+ }
+ break;
+
+ case IP_HEADSET:
+ if (handle == NULL) {
+ if (enable) {
+ if (audio_mixer_control.codec_capture_active) {
+ handle =
+ audio_mixer_control.
+ voice_codec_handle;
+ pmic_audio_vcodec_set_mic(handle,
+ NO_MIC,
+ MIC2_AUX);
+ pmic_audio_vcodec_enable_micbias(handle,
+ MIC_BIAS2);
+ }
+ audio_mixer_control.input_device |=
+ SOUND_MASK_MIC;
+ } else {
+ if (audio_mixer_control.codec_capture_active) {
+ handle =
+ audio_mixer_control.
+ voice_codec_handle;
+ pmic_audio_vcodec_set_mic_on_off(handle,
+ NO_MIC,
+ MIC2_AUX);
+ pmic_audio_vcodec_disable_micbias
+ (handle, MIC_BIAS2);
+ }
+ audio_mixer_control.input_device &=
+ ~SOUND_MASK_MIC;
+ }
+ // Enable Mic with MIC2_AUX
+ }
+ break;
+
+ case IP_LINEIN:
+ if (handle == NULL) {
+ if (enable) {
+ if (audio_mixer_control.codec_capture_active) {
+ handle =
+ audio_mixer_control.
+ voice_codec_handle;
+ pmic_audio_vcodec_set_mic(handle,
+ NO_MIC,
+ TXIN_EXT);
+ }
+ audio_mixer_control.input_device |=
+ SOUND_MASK_LINE;
+ } else {
+ if (audio_mixer_control.codec_capture_active) {
+ handle =
+ audio_mixer_control.
+ voice_codec_handle;
+ pmic_audio_vcodec_set_mic_on_off(handle,
+ NO_MIC,
+ TXIN_EXT);
+ }
+ audio_mixer_control.input_device &=
+ ~SOUND_MASK_LINE;
+ }
+ }
+ break;
+
+ default:
+ up(&audio_mixer_control.sem);
+ return -1;
+ break;
+ }
+ up(&audio_mixer_control.sem);
+ return 0;
+}
+
+EXPORT_SYMBOL(set_mixer_input_device);
+
+int get_mixer_input_device()
+{
+ int val;
+ val = audio_mixer_control.input_device;
+ return val;
+}
+
+EXPORT_SYMBOL(get_mixer_input_device);
+
+/*!
+ * This function sets the PMIC input device's gain.
+ * Note that the gain is the input volume
+ *
+ * @param handle Handle to the opened PMIC device
+ * @param val gain to be applied. This value can go
+ * from 0 (mute) to 100 (max gain)
+ */
+int set_mixer_input_gain(PMIC_AUDIO_HANDLE handle, int val)
+{
+ int leftdb, rightdb;
+ int left, right;
+
+ left = (val & 0x00ff);
+ right = ((val & 0xff00) >> 8);
+ if (down_interruptible(&audio_mixer_control.sem))
+ return -EINTR;
+ leftdb = (left * PMIC_INPUT_VOLUME_MAX) / INPUT_VOLUME_MAX;
+ rightdb = (right * PMIC_INPUT_VOLUME_MAX) / INPUT_VOLUME_MAX;
+ audio_mixer_control.input_volume = val;
+ if (audio_mixer_control.voice_codec_handle == handle) {
+ pmic_audio_vcodec_set_record_gain(handle, VOLTAGE_TO_VOLTAGE,
+ leftdb, VOLTAGE_TO_VOLTAGE,
+ rightdb);
+ } else if ((handle == NULL)
+ && (audio_mixer_control.codec_capture_active)) {
+ pmic_audio_vcodec_set_record_gain(audio_mixer_control.
+ voice_codec_handle,
+ VOLTAGE_TO_VOLTAGE, leftdb,
+ VOLTAGE_TO_VOLTAGE, rightdb);
+ }
+ up(&audio_mixer_control.sem);
+ return 0;
+}
+
+EXPORT_SYMBOL(set_mixer_input_gain);
+
+int get_mixer_input_gain()
+{
+ int val;
+ val = audio_mixer_control.input_volume;
+ return val;
+}
+
+EXPORT_SYMBOL(get_mixer_input_gain);
+
+/*!
+ * This function sets the PMIC output device's volume.
+ *
+ * @param handle Handle to the PMIC device opened
+ * @param volume ALSA value. This value defines the playback volume
+ * @param dev which output device gets affected by this volume
+ *
+ */
+
+int set_mixer_output_volume(PMIC_AUDIO_HANDLE handle, int volume,
+ OUTPUT_DEVICES dev)
+{
+ int leftdb, rightdb;
+ int right, left;
+
+ if (down_interruptible(&audio_mixer_control.sem))
+ return -EINTR;
+ left = (volume & 0x00ff);
+ right = ((volume & 0xff00) >> 8);
+
+ leftdb = (left * PMIC_OUTPUT_VOLUME_MAX) / OUTPUT_VOLUME_MAX;
+ rightdb = (right * PMIC_OUTPUT_VOLUME_MAX) / OUTPUT_VOLUME_MAX;
+ if (handle == NULL) {
+ /* Invoked by mixer */
+ audio_mixer_control.master_volume_out = volume;
+ if (audio_mixer_control.codec_playback_active)
+ pmic_audio_output_set_pgaGain(audio_mixer_control.
+ voice_codec_handle,
+ rightdb);
+ if (audio_mixer_control.stdac_playback_active)
+ pmic_audio_output_set_pgaGain(audio_mixer_control.
+ stdac_handle, rightdb);
+
+ } else {
+ /* change the required volume */
+ audio_mixer_control.master_volume_out = volume;
+ pmic_audio_output_set_pgaGain(handle, rightdb);
+ }
+ up(&audio_mixer_control.sem);
+ return 0;
+}
+
+EXPORT_SYMBOL(set_mixer_output_volume);
+
+int get_mixer_output_volume()
+{
+ int val;
+ val = audio_mixer_control.master_volume_out;
+ return val;
+}
+
+EXPORT_SYMBOL(get_mixer_output_volume);
+
+/*!
+ * This function sets the PMIC output device's balance.
+ *
+ * @param bal Balance to be applied. This value can go
+ * from 0 (Left atten) to 100 (Right atten)
+ * 50 is both equal
+ */
+int set_mixer_output_balance(int bal)
+{
+ int channel = 0;
+ PMIC_AUDIO_OUTPUT_BALANCE_GAIN b_gain;
+ PMIC_AUDIO_HANDLE handle;
+ if (down_interruptible(&audio_mixer_control.sem))
+ return -EINTR;
+ // Convert ALSA value to PMIC value i.e. atten and channel value
+ if (bal < 0)
+ bal = 0;
+ if (bal > 100)
+ bal = 100;
+ if (bal < 50) {
+ channel = 1;
+ } else {
+ bal = 100 - bal;
+ channel = 0;
+ }
+
+ b_gain = bal / 8;
+
+ audio_mixer_control.mixer_balance = bal;
+ if (audio_mixer_control.codec_playback_active) {
+ handle = audio_mixer_control.voice_codec_handle;
+ // Use codec's handle to set balance
+ } else if (audio_mixer_control.stdac_playback_active) {
+ handle = audio_mixer_control.stdac_handle;
+ // Use STDac's handle to set balance
+ } else {
+ up(&audio_mixer_control.sem);
+ return 0;
+ }
+ if (channel == 0)
+ pmic_audio_output_set_balance(handle, BAL_GAIN_0DB, b_gain);
+ else
+ pmic_audio_output_set_balance(handle, b_gain, BAL_GAIN_0DB);
+ up(&audio_mixer_control.sem);
+ return 0;
+}
+
+EXPORT_SYMBOL(set_mixer_output_balance);
+
+int get_mixer_output_balance()
+{
+ int val;
+ val = audio_mixer_control.mixer_balance;
+ return val;
+}
+
+EXPORT_SYMBOL(get_mixer_output_balance);
+
+/*!
+ * This function sets the PMIC output device's mono adder config.
+ *
+ * @param mode Mono adder mode to be set
+ */
+int set_mixer_output_mono_adder(PMIC_AUDIO_MONO_ADDER_MODE mode)
+{
+ PMIC_AUDIO_HANDLE handle;
+ if (down_interruptible(&audio_mixer_control.sem))
+ return -EINTR;
+ audio_mixer_control.mixer_mono_adder = mode;
+ if (audio_mixer_control.codec_playback_active) {
+ handle = audio_mixer_control.voice_codec_handle;
+ // Use codec's handle to set balance
+ pmic_audio_output_enable_mono_adder(audio_mixer_control.
+ voice_codec_handle, mode);
+ } else if (audio_mixer_control.stdac_playback_active) {
+ handle = audio_mixer_control.stdac_handle;
+ pmic_audio_output_enable_mono_adder(audio_mixer_control.
+ stdac_handle, mode);
+ // Use STDac's handle to set balance
+ }
+ up(&audio_mixer_control.sem);
+ return 0;
+}
+
+EXPORT_SYMBOL(set_mixer_output_mono_adder);
+
+int get_mixer_output_mono_adder()
+{
+ int val;
+ val = audio_mixer_control.mixer_mono_adder;
+ return val;
+}
+
+EXPORT_SYMBOL(get_mixer_output_mono_adder);
+
+/*!
+ * This function sets the output device(s) in PMIC. It takes an
+ * ALSA value and modifies registers using PMIC-specific values.
+ *
+ * @param handle handle to the device already opened
+ * @param src Source connected to o/p device
+ * @param dev Output device to be enabled
+ * @param enable Enable or disable the device
+ *
+ */
+int set_mixer_output_device(PMIC_AUDIO_HANDLE handle, OUTPUT_SOURCE src,
+ OUTPUT_DEVICES dev, bool enable)
+{
+ PMIC_AUDIO_OUTPUT_PORT port;
+ if (down_interruptible(&audio_mixer_control.sem))
+ return -EINTR;
+ if (!((src == CODEC_DIR_OUT) || (src == MIXER_OUT))) {
+ up(&audio_mixer_control.sem);
+ return -1;
+ }
+ if (handle != (PMIC_AUDIO_HANDLE) NULL) {
+ /* Invoked by playback stream */
+ if (audio_mixer_control.output_device & SOUND_MASK_PHONEOUT) {
+ audio_mixer_control.output_active[OP_EARPIECE] = 1;
+ pmic_audio_output_set_port(handle, MONO_SPEAKER);
+ } else {
+ audio_mixer_control.output_active[OP_EARPIECE] = 0;
+ pmic_audio_output_clear_port(handle, MONO_SPEAKER);
+ }
+ if (audio_mixer_control.output_device & SOUND_MASK_SPEAKER) {
+ audio_mixer_control.output_active[OP_HANDSFREE] = 1;
+ pmic_audio_output_set_port(handle, MONO_LOUDSPEAKER);
+ } else {
+ audio_mixer_control.output_active[OP_HANDSFREE] = 0;
+ pmic_audio_output_clear_port(handle, MONO_LOUDSPEAKER);
+ }
+ if (audio_mixer_control.output_device & SOUND_MASK_VOLUME) {
+ audio_mixer_control.output_active[OP_HEADSET] = 1;
+ if (dev != OP_MONO) {
+ pmic_audio_output_set_port(handle,
+ STEREO_HEADSET_LEFT |
+ STEREO_HEADSET_RIGHT);
+ } else {
+ pmic_audio_output_set_port(handle,
+ STEREO_HEADSET_LEFT);
+ /*This is a temporary workaround which should be removed later */
+ }
+
+ } else {
+ audio_mixer_control.output_active[OP_HEADSET] = 0;
+ pmic_audio_output_clear_port(handle,
+ STEREO_HEADSET_LEFT |
+ STEREO_HEADSET_RIGHT);
+ }
+ if (audio_mixer_control.output_device & SOUND_MASK_PCM) {
+ audio_mixer_control.output_active[OP_LINEOUT] = 1;
+ pmic_audio_output_set_port(handle,
+ STEREO_OUT_LEFT |
+ STEREO_OUT_RIGHT);
+ } else {
+ audio_mixer_control.output_active[OP_LINEOUT] = 0;
+ pmic_audio_output_clear_port(handle,
+ STEREO_OUT_LEFT |
+ STEREO_OUT_RIGHT);
+ }
+ } else {
+ switch (dev) {
+ case OP_EARPIECE:
+ if (enable) {
+ audio_mixer_control.output_device |=
+ SOUND_MASK_PHONEOUT;
+ audio_mixer_control.source_for_output[dev] =
+ src;
+ } else {
+ audio_mixer_control.output_device &=
+ ~SOUND_MASK_PHONEOUT;
+ }
+ port = MONO_SPEAKER;
+ break;
+ case OP_HANDSFREE:
+ if (enable) {
+ audio_mixer_control.output_device |=
+ SOUND_MASK_SPEAKER;
+ audio_mixer_control.source_for_output[dev] =
+ src;
+ } else {
+ audio_mixer_control.output_device &=
+ ~SOUND_MASK_SPEAKER;
+ }
+ port = MONO_LOUDSPEAKER;
+ break;
+ case OP_HEADSET:
+ if (enable) {
+ audio_mixer_control.output_device |=
+ SOUND_MASK_VOLUME;
+ audio_mixer_control.source_for_output[dev] =
+ src;
+ } else {
+ audio_mixer_control.output_device &=
+ ~SOUND_MASK_VOLUME;
+ }
+ port = STEREO_HEADSET_LEFT | STEREO_HEADSET_RIGHT;
+ break;
+ case OP_MONO:
+ /*This is a temporary workaround which should be removed later */
+ if (enable) {
+ audio_mixer_control.output_device |=
+ SOUND_MASK_VOLUME;
+ audio_mixer_control.source_for_output[dev] =
+ src;
+ } else {
+ audio_mixer_control.output_device &=
+ ~SOUND_MASK_VOLUME;
+ }
+ port = STEREO_HEADSET_LEFT;
+ break;
+ case OP_LINEOUT:
+ if (enable) {
+ audio_mixer_control.output_device |=
+ SOUND_MASK_PCM;
+ audio_mixer_control.source_for_output[dev] =
+ src;
+ } else {
+ audio_mixer_control.output_device &=
+ ~SOUND_MASK_PCM;
+ }
+ port = STEREO_OUT_LEFT | STEREO_OUT_RIGHT;
+ break;
+ default:
+ up(&audio_mixer_control.sem);
+ return -1;
+ break;
+ }
+ /* Invoked by mixer .. little tricky to handle over here */
+ if (audio_mixer_control.codec_playback_active) {
+ if (enable) {
+ audio_mixer_control.output_active[dev] = 1;
+ pmic_audio_output_set_port(audio_mixer_control.
+ voice_codec_handle,
+ port);
+ } else {
+ audio_mixer_control.output_active[dev] = 0;
+ pmic_audio_output_clear_port
+ (audio_mixer_control.voice_codec_handle,
+ port);
+ }
+ }
+ if (audio_mixer_control.stdac_playback_active) {
+ if (enable) {
+ audio_mixer_control.output_active[dev] = 1;
+ pmic_audio_output_set_port(audio_mixer_control.
+ stdac_handle, port);
+ } else {
+ audio_mixer_control.output_active[dev] = 0;
+ pmic_audio_output_clear_port
+ (audio_mixer_control.stdac_handle, port);
+ }
+ }
+
+ }
+ up(&audio_mixer_control.sem);
+ return 0;
+ // Set O/P device with handle and port
+
+}
+
+EXPORT_SYMBOL(set_mixer_output_device);
+
+int get_mixer_output_device()
+{
+ int val;
+ val = audio_mixer_control.output_device;
+ return val;
+}
+
+EXPORT_SYMBOL(get_mixer_output_device);
+
+/*!
+ * This function configures the CODEC for playback/recording.
+ *
+ * main configured elements are:
+ * - audio path on PMIC
+ * - external clock to generate BC and FS clocks
+ * - PMIC mode (master or slave)
+ * - protocol
+ * - sample rate
+ *
+ * @param substream pointer to the structure of the current stream.
+ * @param stream_id index into the audio_stream array.
+ */
+void configure_codec(struct snd_pcm_substream *substream, int stream_id)
+{
+ mxc_pmic_audio_t *chip;
+ audio_stream_t *s;
+ pmic_audio_device_t *pmic;
+ PMIC_AUDIO_HANDLE handle;
+ int ssi_bus;
+
+ chip = snd_pcm_substream_chip(substream);
+ s = &chip->s[stream_id];
+ pmic = &s->pmic_audio_device;
+ handle = audio_mixer_control.voice_codec_handle;
+
+ ssi_bus = (pmic->ssi == SSI1) ? AUDIO_DATA_BUS_1 : AUDIO_DATA_BUS_2;
+
+ pmic_audio_vcodec_set_rxtx_timeslot(handle, USE_TS0);
+ pmic_audio_vcodec_enable_mixer(handle, USE_TS1, VCODEC_MIX_IN_0DB,
+ VCODEC_MIX_OUT_0DB);
+ pmic_audio_set_protocol(handle, ssi_bus, pmic->protocol, pmic->mode,
+ USE_4_TIMESLOTS);
+
+ msleep(20);
+ pmic_audio_vcodec_set_clock(handle, pmic->pll, pmic->pll_rate,
+ pmic->sample_rate, NO_INVERT);
+ msleep(20);
+ pmic_audio_vcodec_set_config(handle, VCODEC_MASTER_CLOCK_OUTPUTS);
+ pmic_audio_digital_filter_reset(handle);
+ msleep(15);
+ if (stream_id == 2) {
+ pmic_audio_output_enable_mixer(handle);
+ set_mixer_output_device(handle, MIXER_OUT, OP_NODEV, 1);
+ set_mixer_output_volume(handle,
+ audio_mixer_control.master_volume_out,
+ OP_HEADSET);
+ } else {
+ set_mixer_input_device(handle, IP_NODEV, 1);
+ set_mixer_input_gain(handle, audio_mixer_control.input_volume);
+ }
+ pmic_audio_enable(handle);
+}
+
+/*!
+ * This function configures the STEREODAC for playback/recording.
+ *
+ * main configured elements are:
+ * - audio path on PMIC
+ * - external clock to generate BC and FS clocks
+ * - PMIC mode (master or slave)
+ * - protocol
+ * - sample rate
+ *
+ * @param substream pointer to the structure of the current stream.
+ */
+void configure_stereodac(struct snd_pcm_substream *substream)
+{
+ mxc_pmic_audio_t *chip;
+ int stream_id;
+ audio_stream_t *s;
+ pmic_audio_device_t *pmic;
+ int ssi_bus;
+ PMIC_AUDIO_HANDLE handle;
+ struct snd_pcm_runtime *runtime;
+
+ chip = snd_pcm_substream_chip(substream);
+ stream_id = substream->pstr->stream;
+ s = &chip->s[stream_id];
+ pmic = &s->pmic_audio_device;
+ handle = pmic->handle;
+ runtime = substream->runtime;
+
+ if (runtime->channels == 1) {
+ audio_mixer_control.mixer_mono_adder = MONO_ADD_LEFT_RIGHT;
+ } else {
+ audio_mixer_control.mixer_mono_adder = MONO_ADDER_OFF;
+ }
+
+ ssi_bus = (pmic->ssi == SSI1) ? AUDIO_DATA_BUS_1 : AUDIO_DATA_BUS_2;
+ pmic_audio_stdac_set_rxtx_timeslot(handle, USE_TS0_TS1);
+ pmic_audio_stdac_enable_mixer(handle, USE_TS2_TS3, STDAC_NO_MIX,
+ STDAC_MIX_OUT_0DB);
+#ifdef CONFIG_MXC_PMIC_SC55112
+ pmic_audio_set_protocol(handle, ssi_bus, pmic->protocol, pmic->mode,
+ USE_4_TIMESLOTS);
+#else
+ pmic_audio_set_protocol(handle, ssi_bus, pmic->protocol, pmic->mode,
+ USE_2_TIMESLOTS);
+#endif
+ pmic_audio_stdac_set_clock(handle, pmic->pll, pmic->pll_rate,
+ pmic->sample_rate, NO_INVERT);
+
+ pmic_audio_stdac_set_config(handle, STDAC_MASTER_CLOCK_OUTPUTS);
+ pmic_audio_output_enable_mixer(handle);
+ audio_mixer_control.stdac_out_to_mixer = 1;
+ pmic_audio_digital_filter_reset(handle);
+ msleep(10);
+ pmic_audio_output_enable_phantom_ground();
+ set_mixer_output_volume(handle, audio_mixer_control.master_volume_out,
+ OP_HEADSET);
+ pmic_audio_output_enable_mono_adder(handle,
+ audio_mixer_control.
+ mixer_mono_adder);
+#ifdef CONFIG_HEADSET_DETECT_ENABLE
+ set_mixer_output_device(handle, MIXER_OUT, OP_MONO, 1);
+#else
+ set_mixer_output_device(handle, MIXER_OUT, OP_NODEV, 1);
+#endif
+ pmic_audio_enable(handle);
+
+}
+
+/*!
+ * This function disables CODEC's amplifiers, volume and clock.
+ * @param handle Handle of voice codec
+ */
+
+void disable_codec(PMIC_AUDIO_HANDLE handle)
+{
+ pmic_audio_disable(handle);
+ pmic_audio_vcodec_clear_config(handle, VCODEC_MASTER_CLOCK_OUTPUTS);
+}
+
+/*!
+ * This function disables STEREODAC's amplifiers, volume and clock.
+ * @param handle Handle of STdac
+ * @param
+ */
+
+void disable_stereodac(void)
+{
+
+ audio_mixer_control.stdac_out_to_mixer = 0;
+}
+
+/*!
+ * This function configures PMIC for recording.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+int configure_pmic_recording(struct snd_pcm_substream *substream)
+{
+
+ configure_codec(substream, 1);
+ return 0;
+}
+
+/*!
+ * This function configures PMIC for playing back.
+ *
+ * @param substream pointer to the structure of the current stream.
+ * @param stream_id Index into the audio_stream array .
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+
+int configure_pmic_playback(struct snd_pcm_substream *substream, int stream_id)
+{
+ if (stream_id == 0) {
+ configure_stereodac(substream);
+ } else if (stream_id == 2 || stream_id == 3) {
+ configure_codec(substream, stream_id);
+ }
+ return 0;
+}
+
+/*!
+ * This function shutsdown the PMIC soundcard.
+ * Nothing to be done here
+ *
+ * @param mxc_audio pointer to the sound card structure.
+ *
+ * @return
+ */
+/*
+static void mxc_pmic_audio_shutdown(mxc_pmic_audio_t * mxc_audio)
+{
+
+}
+*/
+
+/*!
+ * This function configures the DMA channel used to transfer
+ * audio from MCU to PMIC
+ *
+ * @param substream pointer to the structure of the current stream.
+ * @param callback pointer to function that will be
+ * called when a SDMA TX transfer finishes.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int
+configure_write_channel(audio_stream_t * s, mxc_dma_callback_t callback,
+ int stream_id)
+{
+ int ret = -1;
+ int channel = -1;
+#ifdef CONFIG_SND_MXC_PLAYBACK_MIXING
+
+ int channel1 = -1;
+#endif
+
+ if (stream_id == 0) {
+ if (audio_data->ssi_num == 2) {
+ channel =
+ mxc_dma_request(MXC_DMA_SSI2_16BIT_TX0,
+ "ALSA TX DMA");
+ ret =
+ mxc_dma_callback_set(channel,
+ (mxc_dma_callback_t) callback,
+ (void *)s);
+ if (ret != 0) {
+ mxc_dma_free(channel);
+ return -1;
+ }
+ s->dma_wchannel = channel;
+
+ } else {
+ channel =
+ mxc_dma_request(MXC_DMA_SSI1_16BIT_TX0,
+ "ALSA TX DMA");
+ ret =
+ mxc_dma_callback_set(channel,
+ (mxc_dma_callback_t) callback,
+ (void *)s);
+ if (ret != 0) {
+ mxc_dma_free(channel);
+ return -1;
+ }
+ s->dma_wchannel = channel;
+ }
+
+ }
+ if (stream_id == 3) {
+ channel =
+ mxc_dma_request(MXC_DMA_SSI1_16BIT_TX0, "ALSA TX DMA");
+ if (channel < 0) {
+ pr_debug("error requesting a write dma channel\n");
+ return -1;
+ }
+ ret =
+ mxc_dma_callback_set(channel, (mxc_dma_callback_t) callback,
+ (void *)s);
+ if (ret != 0) {
+ mxc_dma_free(channel);
+
+ return -1;
+ }
+ s->dma_wchannel = channel;
+ }
+#ifdef CONFIG_SND_MXC_PLAYBACK_MIXING
+ else if (stream_id == 2) {
+ channel1 =
+ mxc_dma_request(MXC_DMA_SSI1_16BIT_TX1, "ALSA TX DMA");
+ ret =
+ mxc_dma_callback_set(channel1,
+ (mxc_dma_callback_t) callback,
+ (void *)s);
+ if (ret != 0) {
+ mxc_dma_free(channel1);
+ return -1;
+ }
+ s->dma_wchannel = channel1;
+ }
+#else
+
+ if (stream_id == 2) {
+ channel =
+ mxc_dma_request(MXC_DMA_SSI1_16BIT_TX0, "ALSA TX DMA");
+ ret =
+ mxc_dma_callback_set(channel, (mxc_dma_callback_t) callback,
+ (void *)s);
+ if (ret != 0) {
+ mxc_dma_free(channel);
+ return -1;
+ }
+ s->dma_wchannel = channel;
+ }
+#endif
+ /*if (channel < 0) {
+ pr_debug("error requesting a write dma channel\n");
+ return -1;
+ } */
+
+ return 0;
+}
+
+/*!
+ * This function configures the DMA channel used to transfer
+ * audio from PMIC to MCU
+ *
+ * @param substream pointer to the structure of the current stream.
+ * @param callback pointer to function that will be
+ * called when a SDMA RX transfer finishes.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int configure_read_channel(audio_stream_t * s,
+ mxc_dma_callback_t callback)
+{
+ int ret = -1;
+ int channel = -1;
+
+ channel = mxc_dma_request(MXC_DMA_SSI1_16BIT_RX0, "ALSA RX DMA");
+ if (channel < 0) {
+ pr_debug("error requesting a read dma channel\n");
+ return -1;
+ }
+
+ ret =
+ mxc_dma_callback_set(channel, (mxc_dma_callback_t) callback,
+ (void *)s);
+ if (ret != 0) {
+ mxc_dma_free(channel);
+ return -1;
+ }
+ s->dma_wchannel = channel;
+
+ return 0;
+}
+
+/*!
+ * This function frees the stream structure
+ *
+ * @param s pointer to the structure of the current stream.
+ */
+static void audio_dma_free(audio_stream_t * s)
+{
+ /*
+ * There is nothing to be done here since the dma channel has been
+ * freed either in the callback or in the stop method
+ */
+
+}
+
+/*!
+ * This function gets the dma pointer position during record.
+ * Our DMA implementation does not allow to retrieve this position
+ * when a transfert is active, so, it answers the middle of
+ * the current period beeing transfered
+ *
+ * @param s pointer to the structure of the current stream.
+ *
+ */
+static u_int audio_get_capture_dma_pos(audio_stream_t * s)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int offset;
+
+ substream = s->stream;
+ runtime = substream->runtime;
+ offset = 0;
+
+ /* tx_spin value is used here to check if a transfert is active */
+ if (s->tx_spin) {
+ offset = (runtime->period_size * (s->periods)) + 0;
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+ pr_debug("MXC: audio_get_dma_pos offset %d\n", offset);
+ } else {
+ offset = (runtime->period_size * (s->periods));
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+ pr_debug("MXC: audio_get_dma_pos BIS offset %d\n", offset);
+ }
+
+ return offset;
+}
+
+/*!
+ * This function gets the dma pointer position during playback.
+ * Our DMA implementation does not allow to retrieve this position
+ * when a transfert is active, so, it answers the middle of
+ * the current period beeing transfered
+ *
+ * @param s pointer to the structure of the current stream.
+ *
+ */
+static u_int audio_get_playback_dma_pos(audio_stream_t * s)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int offset;
+
+ substream = s->stream;
+ runtime = substream->runtime;
+ offset = 0;
+
+ /* tx_spin value is used here to check if a transfert is active */
+ if (s->tx_spin) {
+ offset = (runtime->period_size * (s->periods)) + 0;
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+ pr_debug("MXC: audio_get_dma_pos offset %d\n", offset);
+ } else {
+ offset = (runtime->period_size * (s->periods));
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+ pr_debug("MXC: audio_get_dma_pos BIS offset %d\n", offset);
+ }
+
+ return offset;
+}
+
+/*!
+ * This function is called whenever a new audio block needs to be
+ * transferred to PMIC. The function receives the address and the size
+ * of the new block and start a new DMA transfer.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static void audio_playback_dma(audio_stream_t * s)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size = 0;
+ unsigned int offset;
+ int ret = 0;
+ mxc_dma_requestbuf_t dma_request;
+#ifdef CONFIG_SND_MXC_PLAYBACK_MIXING
+ unsigned int dma_size_mix = 0, offset_mix;
+ mxc_dma_requestbuf_t dma_request_mix;
+ int ret1 = 0;
+#endif
+ int device;
+ int stream_id;
+
+ substream = s->stream;
+ runtime = substream->runtime;
+ device = substream->pcm->device;
+ if (device == 0) {
+ stream_id = 0;
+ } else if (device == 1) {
+ stream_id = 2;
+ } else {
+ stream_id = 3;
+ }
+
+ pr_debug("\nDMA direction %d\(0 is playback 1 is capture)\n",
+ s->stream_id);
+
+#ifndef CONFIG_SND_MXC_PLAYBACK_MIXING
+ memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));
+
+#else
+ if (stream_id == 2) {
+ memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));
+ } else if (stream_id == 3) {
+ memset(&dma_request_mix, 0, sizeof(mxc_dma_requestbuf_t));
+ }
+#endif
+ if (s->active) {
+ if (ssi_get_status(s->ssi) & (ssi_transmitter_underrun_0)) {
+ ssi_enable(s->ssi, false);
+ ssi_transmit_enable(s->ssi, false);
+ ssi_enable(s->ssi, true);
+ }
+#ifndef CONFIG_SND_MXC_PLAYBACK_MIXING
+
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ pr_debug("s->period (%x) runtime->periods (%d)\n",
+ s->period, runtime->periods);
+ pr_debug("runtime->period_size (%d) dma_size (%d)\n",
+ (unsigned int)runtime->period_size,
+ runtime->dma_bytes);
+
+ offset = dma_size * s->period;
+ if (snd_BUG_ON(dma_size > DMA_BUF_SIZE))
+ return;
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+
+ if (g_audio_iram_en && stream_id == 0) {
+ dma_request.src_addr = ADMA_BASE_PADDR + offset;
+ } else
+#endif /*CONFIG_SND_MXC_PMIC_IRAM */
+ {
+
+ dma_request.src_addr =
+ (dma_addr_t) (dma_map_single
+ (NULL, runtime->dma_area + offset,
+ dma_size, DMA_TO_DEVICE));
+ }
+ if (stream_id == 0) {
+ dma_request.dst_addr =
+ (dma_addr_t) get_ssi_fifo_addr(s->ssi, 1);
+ } else if (stream_id == 2) {
+ dma_request.dst_addr =
+ (dma_addr_t) get_ssi_fifo_addr(s->ssi, 1);
+ }
+ dma_request.num_of_bytes = dma_size;
+ pr_debug("MXC: Start DMA offset (%d) size (%d)\n",
+ offset, runtime->dma_bytes);
+
+ mxc_dma_config(s->dma_wchannel, &dma_request, 1,
+ MXC_DMA_MODE_WRITE);
+ ret = mxc_dma_enable(s->dma_wchannel);
+ ssi_transmit_enable(s->ssi, true);
+ s->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
+
+ if (ret) {
+ pr_debug("audio_process_dma: cannot queue DMA buffer\
+ (%i)\n", ret);
+ return;
+ }
+ s->period++;
+ s->period %= runtime->periods;
+#else
+
+ if (stream_id == 2) {
+ dma_size =
+ frames_to_bytes(runtime, runtime->period_size);
+ pr_debug("s->period (%x) runtime->periods (%d)\n",
+ s->period, runtime->periods);
+ pr_debug("runtime->period_size (%d) dma_size (%d)\n",
+ (unsigned int)runtime->period_size,
+ runtime->dma_bytes);
+
+ offset = dma_size * s->period;
+ if (snd_BUG_ON(dma_size > DMA_BUF_SIZE))
+ return;
+
+ dma_request.src_addr =
+ (dma_addr_t) (dma_map_single
+ (NULL, runtime->dma_area + offset,
+ dma_size, DMA_TO_DEVICE));
+ dma_request.dst_addr =
+ (dma_addr_t) get_ssi_fifo_addr(s->ssi, 1);
+ dma_request.num_of_bytes = dma_size;
+ pr_debug("MXC: Start DMA offset (%d) size (%d)\n",
+ offset, runtime->dma_bytes);
+
+ mxc_dma_config(s->dma_wchannel, &dma_request, 1,
+ MXC_DMA_MODE_WRITE);
+ ret = mxc_dma_enable(s->dma_wchannel);
+ ssi_transmit_enable(s->ssi, true);
+ s->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
+
+ if (ret) {
+ pr_debug("audio_process_dma: cannot queue DMA buffer\
+ (%i)\n",
+ ret);
+ return;
+ }
+ s->period++;
+ s->period %= runtime->periods;
+
+ } else if (stream_id == 3) {
+
+ dma_size_mix =
+ frames_to_bytes(runtime, runtime->period_size);
+ pr_debug("s->period (%x) runtime->periods (%d)\n",
+ s->period, runtime->periods);
+ pr_debug("runtime->period_size (%d) dma_size (%d)\n",
+ (unsigned int)runtime->period_size,
+ runtime->dma_bytes);
+
+ offset_mix = dma_size_mix * s->period;
+ if (snd_BUG_ON(dma_size_mix > DMA_BUF_SIZE))
+ return;
+ dma_request_mix.src_addr =
+ (dma_addr_t) (dma_map_single
+ (NULL, runtime->dma_area + offset_mix,
+ dma_size_mix, DMA_TO_DEVICE));
+ dma_request_mix.dst_addr =
+ (dma_addr_t) get_ssi_fifo_addr(s->ssi, 1);
+ dma_request_mix.num_of_bytes = dma_size_mix;
+
+ pr_debug("MXC: Start DMA offset (%d) size (%d)\n",
+ offset_mix, runtime->dma_bytes);
+
+ mxc_dma_config(s->dma_wchannel, &dma_request_mix, 1,
+ MXC_DMA_MODE_WRITE);
+ ret1 = mxc_dma_enable(s->dma_wchannel);
+ ssi_transmit_enable(s->ssi, true);
+ s->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
+
+ if (ret1) {
+ pr_debug("audio_process_dma: cannot queue DMA buffer\
+ (%i)\n",
+ ret1);
+ return;
+ }
+ s->period++;
+ s->period %= runtime->periods;
+
+ }
+#endif
+#ifdef MXC_SOUND_PLAYBACK_CHAIN_DMA_EN
+
+ if ((s->period > s->periods) && ((s->period - s->periods) > 1)) {
+ pr_debug
+ ("audio playback chain dma: already double buffered\n");
+ return;
+ }
+
+ if ((s->period < s->periods)
+ && ((s->period + runtime->periods - s->periods) > 1)) {
+ pr_debug
+ ("audio playback chain dma: already double buffered\n");
+ return;
+ }
+
+ if (s->period == s->periods) {
+ pr_debug
+ ("audio playback chain dma: s->period == s->periods\n");
+ return;
+ }
+
+ if (snd_pcm_playback_hw_avail(runtime) <
+ 2 * runtime->period_size) {
+ pr_debug
+ ("audio playback chain dma: available data is not enough\n");
+ return;
+ }
+
+ pr_debug
+ ("audio playback chain dma:to set up the 2nd dma buffer\n");
+
+#ifndef CONFIG_SND_MXC_PLAYBACK_MIXING
+ offset = dma_size * s->period;
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+ if (g_audio_iram_en && stream_id == 0) {
+ dma_request.src_addr = ADMA_BASE_PADDR + offset;
+ } else
+#endif /*CONFIG_SND_MXC_PMIC_IRAM */
+ {
+
+ dma_request.src_addr =
+ (dma_addr_t) (dma_map_single
+ (NULL, runtime->dma_area + offset,
+ dma_size, DMA_TO_DEVICE));
+ }
+ mxc_dma_config(s->dma_wchannel, &dma_request, 1,
+ MXC_DMA_MODE_WRITE);
+#else
+ if (stream_id == 3) {
+ offset_mix = dma_size_mix * s->period;
+ dma_request.src_addr =
+ (dma_addr_t) (dma_map_single
+ (NULL,
+ runtime->dma_area + offset_mix,
+ dma_size, DMA_TO_DEVICE));
+
+ mxc_dma_config(s->dma_wchannel, &dma_request_mix, 1,
+ MXC_DMA_MODE_WRITE);
+ } else {
+ offset = dma_size * s->period;
+ dma_request.src_addr =
+ (dma_addr_t) (dma_map_single
+ (NULL,
+ runtime->dma_area + offset,
+ dma_size, DMA_TO_DEVICE));
+
+ mxc_dma_config(s->dma_wchannel, &dma_request, 1,
+ MXC_DMA_MODE_WRITE);
+ }
+
+#endif
+ ret = mxc_dma_enable(s->dma_wchannel);
+
+ s->period++;
+ s->period %= runtime->periods;
+#endif /* MXC_SOUND_PLAYBACK_CHAIN_DMA_EN */
+ }
+}
+
+/*!
+ * This function is called whenever a new audio block needs to be
+ * transferred from PMIC. The function receives the address and the size
+ * of the block that will store the audio samples and start a new DMA transfer.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static void audio_capture_dma(audio_stream_t * s)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int offset;
+ int ret = 0;
+ mxc_dma_requestbuf_t dma_request;
+
+ substream = s->stream;
+ runtime = substream->runtime;
+
+ pr_debug("\nDMA direction %d\
+ (0 is playback 1 is capture)\n", s->stream_id);
+
+ memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));
+
+ if (s->active) {
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ pr_debug("s->period (%x) runtime->periods (%d)\n",
+ s->period, runtime->periods);
+ pr_debug("runtime->period_size (%d) dma_size (%d)\n",
+ (unsigned int)runtime->period_size,
+ runtime->dma_bytes);
+
+ offset = dma_size * s->period;
+ snd_BUG_ON(dma_size > DMA_BUF_SIZE);
+
+ dma_request.dst_addr = (dma_addr_t) (dma_map_single(NULL,
+ runtime->
+ dma_area +
+ offset,
+ dma_size,
+ DMA_FROM_DEVICE));
+ dma_request.src_addr =
+ (dma_addr_t) get_ssi_fifo_addr(s->ssi, 0);
+ dma_request.num_of_bytes = dma_size;
+
+ pr_debug("MXC: Start DMA offset (%d) size (%d)\n", offset,
+ runtime->dma_bytes);
+
+ mxc_dma_config(s->dma_wchannel, &dma_request, 1,
+ MXC_DMA_MODE_READ);
+ ret = mxc_dma_enable(s->dma_wchannel);
+
+ s->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
+
+ if (ret) {
+ pr_debug("audio_process_dma: cannot queue DMA buffer\
+ (%i)\n", ret);
+ return;
+ }
+ s->period++;
+ s->period %= runtime->periods;
+
+#ifdef MXC_SOUND_CAPTURE_CHAIN_DMA_EN
+ if ((s->period > s->periods) && ((s->period - s->periods) > 1)) {
+ pr_debug
+ ("audio capture chain dma: already double buffered\n");
+ return;
+ }
+
+ if ((s->period < s->periods)
+ && ((s->period + runtime->periods - s->periods) > 1)) {
+ pr_debug
+ ("audio capture chain dma: already double buffered\n");
+ return;
+ }
+
+ if (s->period == s->periods) {
+ pr_debug
+ ("audio capture chain dma: s->period == s->periods\n");
+ return;
+ }
+
+ if (snd_pcm_capture_hw_avail(runtime) <
+ 2 * runtime->period_size) {
+ pr_debug
+ ("audio capture chain dma: available data is not enough\n");
+ return;
+ }
+
+ pr_debug
+ ("audio capture chain dma:to set up the 2nd dma buffer\n");
+ offset = dma_size * s->period;
+ dma_request.dst_addr = (dma_addr_t) (dma_map_single(NULL,
+ runtime->
+ dma_area +
+ offset,
+ dma_size,
+ DMA_FROM_DEVICE));
+ mxc_dma_config(s->dma_wchannel, &dma_request, 1,
+ MXC_DMA_MODE_READ);
+ ret = mxc_dma_enable(s->dma_wchannel);
+
+ s->period++;
+ s->period %= runtime->periods;
+#endif /* MXC_SOUND_CAPTURE_CHAIN_DMA_EN */
+ }
+}
+
+/*!
+ * This is a callback which will be called
+ * when a TX transfer finishes. The call occurs
+ * in interrupt context.
+ *
+ * @param dat pointer to the structure of the current stream.
+ *
+ */
+static void audio_playback_dma_callback(void *data, int error,
+ unsigned int count)
+{
+ audio_stream_t *s;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int previous_period;
+ unsigned int offset;
+
+ s = data;
+ substream = s->stream;
+ runtime = substream->runtime;
+ previous_period = s->periods;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * previous_period;
+
+ s->tx_spin = 0;
+ s->periods++;
+ s->periods %= runtime->periods;
+
+ /*
+ * Give back to the CPU the access to the non cached memory
+ */
+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
+ DMA_TO_DEVICE);
+
+ /*
+ * If we are getting a callback for an active stream then we inform
+ * the PCM middle layer we've finished a period
+ */
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+ spin_lock(&s->dma_lock);
+
+ /*
+ * Trig next DMA transfer
+ */
+ audio_playback_dma(s);
+
+ spin_unlock(&s->dma_lock);
+
+}
+
+/*!
+ * This is a callback which will be called
+ * when a RX transfer finishes. The call occurs
+ * in interrupt context.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static void audio_capture_dma_callback(void *data, int error,
+ unsigned int count)
+{
+ audio_stream_t *s;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int previous_period;
+ unsigned int offset;
+
+ s = data;
+ substream = s->stream;
+ runtime = substream->runtime;
+ previous_period = s->periods;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * previous_period;
+
+ s->tx_spin = 0;
+ s->periods++;
+ s->periods %= runtime->periods;
+
+ /*
+ * Give back to the CPU the access to the non cached memory
+ */
+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
+ DMA_FROM_DEVICE);
+
+ /*
+ * If we are getting a callback for an active stream then we inform
+ * the PCM middle layer we've finished a period
+ */
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+
+ spin_lock(&s->dma_lock);
+
+ /*
+ * Trig next DMA transfer
+ */
+ audio_capture_dma(s);
+
+ spin_unlock(&s->dma_lock);
+
+}
+
+/*!
+ * This function is a dispatcher of command to be executed
+ * by the driver for playback.
+ *
+ * @param substream pointer to the structure of the current stream.
+ * @param cmd command to be executed
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int
+snd_mxc_audio_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ mxc_pmic_audio_t *chip;
+ int stream_id;
+ audio_stream_t *s;
+ int err;
+ int device;
+ device = substream->pcm->device;
+ if (device == 0) {
+ stream_id = 0;
+ } else if (device == 1) {
+ stream_id = 2;
+ } else {
+ stream_id = 3;
+ }
+
+ chip = snd_pcm_substream_chip(substream);
+ //stream_id = substream->pstr->stream;
+ s = &chip->s[stream_id];
+ err = 0;
+
+ /* note local interrupts are already disabled in the midlevel code */
+ spin_lock(&s->dma_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ s->tx_spin = 0;
+ s->active = 1;
+ audio_playback_dma(s);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ s->active = 0;
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ spin_unlock(&s->dma_lock);
+ return err;
+}
+
+/*!
+ * This function is a dispatcher of command to be executed
+ * by the driver for capture.
+ *
+ * @param substream pointer to the structure of the current stream.
+ * @param cmd command to be executed
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int
+snd_mxc_audio_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ mxc_pmic_audio_t *chip;
+ int stream_id;
+ audio_stream_t *s;
+ int err;
+
+ chip = snd_pcm_substream_chip(substream);
+ stream_id = substream->pstr->stream;
+ s = &chip->s[stream_id];
+ err = 0;
+
+ /* note local interrupts are already disabled in the midlevel code */
+ spin_lock(&s->dma_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ s->tx_spin = 0;
+ s->active = 1;
+ audio_capture_dma(s);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ s->active = 0;
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ spin_unlock(&s->dma_lock);
+ return err;
+}
+
+/*!
+ * This function configures the hardware to allow audio
+ * playback operations. It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_mxc_audio_playback_prepare(struct snd_pcm_substream *substream)
+{
+ mxc_pmic_audio_t *chip;
+ audio_stream_t *s;
+ int ssi;
+ int device = -1, stream_id = -1;
+
+ device = substream->pcm->device;
+ if (device == 0)
+ stream_id = 0;
+ else if (device == 1)
+ stream_id = 2;
+ else if (device == 2)
+ stream_id = 3;
+
+ chip = snd_pcm_substream_chip(substream);
+ s = &chip->s[stream_id];
+ ssi = s->ssi;
+
+ normalize_speed_for_pmic(substream);
+
+ configure_dam_pmic_master(ssi);
+
+ configure_ssi_tx(substream);
+
+ ssi_interrupt_enable(ssi, ssi_tx_dma_interrupt_enable);
+ ssi_interrupt_enable(ssi, ssi_tx_interrupt_enable);
+
+ if (configure_pmic_playback(substream, stream_id) == -1)
+ pr_debug(KERN_ERR "MXC: PMIC Playback Config FAILED\n");
+ ssi_interrupt_enable(ssi, ssi_tx_fifo_0_empty);
+ ssi_interrupt_enable(ssi, ssi_tx_fifo_1_empty);
+ /*
+ ssi_transmit_enable(ssi, true);
+ */
+ msleep(20);
+ set_pmic_channels(substream);
+ s->period = 0;
+ s->periods = 0;
+
+ msleep(100);
+
+ return 0;
+}
+
+/*!
+ * This function gets the current capture pointer position.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static
+snd_pcm_uframes_t snd_mxc_audio_capture_pointer(struct snd_pcm_substream
+ *substream)
+{
+ mxc_pmic_audio_t *chip;
+
+ chip = snd_pcm_substream_chip(substream);
+ return audio_get_capture_dma_pos(&chip->s[substream->pstr->stream]);
+}
+
+/*!
+ * This function gets the current playback pointer position.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static snd_pcm_uframes_t
+snd_mxc_audio_playback_pointer(struct snd_pcm_substream *substream)
+{
+ mxc_pmic_audio_t *chip;
+ int device;
+ int stream_id;
+ device = substream->pcm->device;
+ if (device == 0)
+ stream_id = 0;
+ else if (device == 1)
+ stream_id = 2;
+ else
+ stream_id = 3;
+ chip = snd_pcm_substream_chip(substream);
+ return audio_get_playback_dma_pos(&chip->s[stream_id]);
+}
+
+/*!
+ * This structure reprensents the capabilities of the driver
+ * in capture mode.
+ * It is used by ALSA framework.
+ */
+static struct snd_pcm_hardware snd_mxc_pmic_capture = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = MAX_BUFFER_SIZE,
+ .period_bytes_min = MIN_PERIOD_SIZE,
+ .period_bytes_max = DMA_BUF_SIZE,
+ .periods_min = MIN_PERIOD,
+ .periods_max = MAX_PERIOD,
+ .fifo_size = 0,
+
+};
+
+/*!
+ * This structure reprensents the capabilities of the driver
+ * in playback mode for ST-Dac.
+ * It is used by ALSA framework.
+ */
+static struct snd_pcm_hardware snd_mxc_pmic_playback_stereo = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS),
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = MAX_BUFFER_SIZE,
+ .period_bytes_min = MIN_PERIOD_SIZE,
+ .period_bytes_max = DMA_BUF_SIZE,
+ .periods_min = MIN_PERIOD,
+ .periods_max = MAX_PERIOD,
+ .fifo_size = 0,
+
+};
+
+/*!
+ * This structure reprensents the capabilities of the driver
+ * in playback mode for Voice-codec.
+ * It is used by ALSA framework.
+ */
+static struct snd_pcm_hardware snd_mxc_pmic_playback_mono = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+ .rate_min = 8000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = MAX_BUFFER_SIZE,
+ .period_bytes_min = MIN_PERIOD_SIZE,
+ .period_bytes_max = DMA_BUF_SIZE,
+ .periods_min = MIN_PERIOD,
+ .periods_max = MAX_PERIOD,
+ .fifo_size = 0,
+
+};
+
+/*!
+ * This function opens a PMIC audio device in playback mode
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_card_mxc_audio_playback_open(struct snd_pcm_substream *substream)
+{
+ mxc_pmic_audio_t *chip;
+ struct snd_pcm_runtime *runtime;
+ int stream_id = -1;
+ int err;
+ audio_stream_t *s;
+ PMIC_AUDIO_HANDLE temp_handle;
+ int device = -1;
+
+ device = substream->pcm->device;
+
+ chip = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+ if (device == 0)
+ stream_id = 0;
+ else if (device == 1)
+ stream_id = 2;
+ else if (device == 2) {
+ stream_id = 3;
+ }
+
+ s = &chip->s[stream_id];
+ err = -1;
+
+ if (stream_id == 0) {
+ if ((audio_data->ssi_num == 1)
+ && (audio_mixer_control.codec_playback_active
+ || audio_mixer_control.codec_capture_active)) {
+ return -EBUSY;
+ }
+
+ if (PMIC_SUCCESS == pmic_audio_open(&temp_handle, STEREO_DAC)) {
+ audio_mixer_control.stdac_handle = temp_handle;
+ audio_mixer_control.stdac_playback_active = 1;
+ chip->s[stream_id].pmic_audio_device.handle =
+ temp_handle;
+ } else {
+ return -EBUSY;
+ }
+ } else if (stream_id == 2) {
+ if ((audio_data->ssi_num == 1)
+ && (audio_mixer_control.stdac_playback_active)) {
+ return -EBUSY;
+ }
+
+ audio_mixer_control.codec_playback_active = 1;
+ if (PMIC_SUCCESS == pmic_audio_open(&temp_handle, VOICE_CODEC)) {
+ audio_mixer_control.voice_codec_handle = temp_handle;
+ chip->s[stream_id].pmic_audio_device.handle =
+ temp_handle;
+ } else {
+ if (audio_mixer_control.codec_capture_active) {
+ temp_handle =
+ audio_mixer_control.voice_codec_handle;
+ } else {
+ return -EBUSY;
+ }
+ }
+
+ } else if (stream_id == 3) {
+ audio_mixer_control.mixing_active = 1;
+
+ }
+
+ pmic_audio_antipop_enable(ANTI_POP_RAMP_SLOW);
+ msleep(250);
+
+ chip->s[stream_id].stream = substream;
+
+ if (stream_id == 0)
+ runtime->hw = snd_mxc_pmic_playback_stereo;
+ else if (stream_id == 2)
+ runtime->hw = snd_mxc_pmic_playback_mono;
+
+ else if (stream_id == 3) {
+ runtime->hw = snd_mxc_pmic_playback_mono;
+
+ if ((err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &hw_playback_rates_mono))
+ < 0)
+ return err;
+ }
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+ if (g_audio_iram_en && stream_id == 0) {
+ runtime->hw.buffer_bytes_max = MAX_IRAM_SIZE;
+ runtime->hw.period_bytes_max = DMA_IRAM_SIZE;
+ }
+#endif /*CONFIG_SND_MXC_PMIC_IRAM */
+
+ if ((err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS)) <
+ 0)
+ goto exit_err;
+ if (stream_id == 0) {
+ if ((err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &hw_playback_rates_stereo))
+ < 0)
+ goto exit_err;
+
+ } else if (stream_id == 2) {
+ if ((err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &hw_playback_rates_mono))
+ < 0)
+ goto exit_err;
+ }
+ msleep(10);
+
+ /* setup DMA controller for playback */
+ if ((err =
+ configure_write_channel(&mxc_audio->s[stream_id],
+ audio_playback_dma_callback,
+ stream_id)) < 0)
+ goto exit_err;
+
+ /* enable ssi clock */
+ clk_enable(audio_data->ssi_clk[s->ssi]);
+
+ return 0;
+ exit_err:
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+ if (stream_id == 0)
+ mxc_snd_pcm_iram_put();
+#endif /*CONFIG_SND_MXC_PMIC_IRAM */
+ return err;
+
+}
+
+/*!
+ * This function closes an PMIC audio device for playback.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_card_mxc_audio_playback_close(struct snd_pcm_substream
+ *substream)
+{
+ mxc_pmic_audio_t *chip;
+ audio_stream_t *s;
+ PMIC_AUDIO_HANDLE handle;
+ int ssi;
+ int device, stream_id = -1;
+ handle = (PMIC_AUDIO_HANDLE) NULL;
+ device = substream->pcm->device;
+ if (device == 0) {
+ stream_id = 0;
+ } else if (device == 1) {
+ stream_id = 2;
+ } else
+ stream_id = 3;
+
+ chip = snd_pcm_substream_chip(substream);
+ s = &chip->s[stream_id];
+ ssi = s->ssi;
+
+ if (audio_mixer_control.mixing_active == 1) {
+ goto End;
+
+ } else {
+
+ if (stream_id == 0) {
+ disable_stereodac();
+ audio_mixer_control.stdac_playback_active = 0;
+ handle = audio_mixer_control.stdac_handle;
+ audio_mixer_control.stdac_handle = NULL;
+ chip->s[stream_id].pmic_audio_device.handle = NULL;
+ } else if ((stream_id == 2) || (stream_id == 3)) {
+
+ audio_mixer_control.codec_playback_active = 0;
+ handle = audio_mixer_control.voice_codec_handle;
+ disable_codec(handle);
+ audio_mixer_control.voice_codec_handle = NULL;
+ chip->s[stream_id].pmic_audio_device.handle = NULL;
+ }
+
+ }
+ pmic_audio_close(handle);
+
+ ssi_transmit_enable(ssi, false);
+ ssi_interrupt_disable(ssi, ssi_tx_dma_interrupt_enable);
+ ssi_tx_fifo_enable(ssi, ssi_fifo_0, false);
+ ssi_enable(ssi, false);
+ mxc_dma_free((mxc_audio->s[stream_id]).dma_wchannel);
+
+ chip->s[stream_id].stream = NULL;
+ End:
+ audio_mixer_control.mixing_active = 0;
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+ if (stream_id == 0)
+ mxc_snd_pcm_iram_put();
+#endif /*CONFIG_SND_MXC_PMIC_IRAM */
+ /* disable ssi clock */
+ clk_disable(audio_data->ssi_clk[ssi]);
+
+ return 0;
+}
+
+/*!
+ * This function closes a PMIC audio device for capture.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_card_mxc_audio_capture_close(struct snd_pcm_substream *substream)
+{
+ PMIC_AUDIO_HANDLE handle;
+ mxc_pmic_audio_t *chip;
+ audio_stream_t *s;
+ int ssi;
+
+ chip = snd_pcm_substream_chip(substream);
+ s = &chip->s[substream->pstr->stream];
+ ssi = s->ssi;
+
+ audio_mixer_control.codec_capture_active = 0;
+ handle = audio_mixer_control.voice_codec_handle;
+ disable_codec(handle);
+ audio_mixer_control.voice_codec_handle = NULL;
+ chip->s[SNDRV_PCM_STREAM_CAPTURE].pmic_audio_device.handle = NULL;
+
+ pmic_audio_close(handle);
+
+ ssi_receive_enable(ssi, false);
+ ssi_interrupt_disable(ssi, ssi_rx_dma_interrupt_enable);
+ ssi_rx_fifo_enable(ssi, ssi_fifo_0, false);
+ ssi_enable(ssi, false);
+ mxc_dma_free((mxc_audio->s[1]).dma_wchannel);
+
+ chip->s[substream->pstr->stream].stream = NULL;
+
+ /* disable ssi clock */
+ clk_disable(audio_data->ssi_clk[ssi]);
+
+ return 0;
+}
+
+/*!
+ * This function configure the Audio HW in terms of memory allocation.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_mxc_audio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_pcm_runtime *runtime;
+ int ret = 0, size;
+ int device, stream_id = -1;
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+ struct snd_dma_buffer *dmab;
+#endif /*CONFIG_SND_MXC_PMIC_IRAM */
+
+ runtime = substream->runtime;
+ ret =
+ snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (ret < 0)
+ return ret;
+ size = params_buffer_bytes(hw_params);
+ device = substream->pcm->device;
+ if (device == 0)
+ stream_id = 0;
+ else if (device == 1)
+ stream_id = 2;
+
+ runtime->dma_addr = virt_to_phys(runtime->dma_area);
+
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && g_audio_iram_en
+ && stream_id == 0) {
+ if (runtime->dma_buffer_p
+ && (runtime->dma_buffer_p != &g_iram_dmab)) {
+ snd_pcm_lib_free_pages(substream);
+ }
+ dmab = &g_iram_dmab;
+ dmab->dev = substream->dma_buffer.dev;
+ dmab->area = (char *)ADMA_BASE_VADDR;
+ dmab->addr = ADMA_BASE_PADDR;
+ dmab->bytes = size;
+ snd_pcm_set_runtime_buffer(substream, dmab);
+ runtime->dma_bytes = size;
+ } else
+#endif /* CONFIG_SND_MXC_PMIC_IRAM */
+ {
+ ret = snd_pcm_lib_malloc_pages(substream, size);
+ if (ret < 0)
+ return ret;
+
+ runtime->dma_addr = virt_to_phys(runtime->dma_area);
+ }
+
+ pr_debug("MXC: snd_mxc_audio_hw_params runtime->dma_addr 0x(%x)\n",
+ (unsigned int)runtime->dma_addr);
+ pr_debug("MXC: snd_mxc_audio_hw_params runtime->dma_area 0x(%x)\n",
+ (unsigned int)runtime->dma_area);
+ pr_debug("MXC: snd_mxc_audio_hw_params runtime->dma_bytes 0x(%x)\n",
+ (unsigned int)runtime->dma_bytes);
+
+ return ret;
+}
+
+/*!
+ * This function frees the audio hardware at the end of playback/capture.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_mxc_audio_hw_free(struct snd_pcm_substream *substream)
+{
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+ if (substream->runtime->dma_buffer_p == &g_iram_dmab) {
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+ } else
+#endif /* CONFIG_SND_MXC_PMIC_IRAM */
+ {
+ return snd_pcm_lib_free_pages(substream);
+ }
+ return 0;
+}
+
+/*!
+ * This function configures the hardware to allow audio
+ * capture operations. It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_mxc_audio_capture_prepare(struct snd_pcm_substream *substream)
+{
+ mxc_pmic_audio_t *chip;
+ audio_stream_t *s;
+ int ssi;
+
+ chip = snd_pcm_substream_chip(substream);
+ s = &chip->s[substream->pstr->stream];
+ ssi = s->ssi;
+
+ normalize_speed_for_pmic(substream);
+
+ pr_debug("substream->pstr->stream %d\n", substream->pstr->stream);
+ pr_debug("SSI %d\n", ssi + 1);
+ configure_dam_pmic_master(ssi);
+
+ configure_ssi_rx(substream);
+
+ ssi_interrupt_enable(ssi, ssi_rx_dma_interrupt_enable);
+
+ if (configure_pmic_recording(substream) == -1)
+ pr_debug(KERN_ERR "MXC: PMIC Record Config FAILED\n");
+
+ ssi_interrupt_enable(ssi, ssi_rx_fifo_0_full);
+ ssi_receive_enable(ssi, true);
+
+ msleep(20);
+ set_pmic_channels(substream);
+
+ s->period = 0;
+ s->periods = 0;
+
+ return 0;
+}
+
+/*!
+ * This function opens an PMIC audio device in capture mode
+ * on Codec.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_card_mxc_audio_capture_open(struct snd_pcm_substream *substream)
+{
+ mxc_pmic_audio_t *chip;
+ struct snd_pcm_runtime *runtime;
+ int stream_id;
+ int err;
+ PMIC_AUDIO_HANDLE temp_handle;
+ audio_stream_t *s;
+
+ chip = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+ stream_id = substream->pstr->stream;
+ s = &chip->s[stream_id];
+ err = -1;
+
+ if ((audio_data->ssi_num == 1)
+ && (audio_mixer_control.stdac_playback_active)) {
+ return -EBUSY;
+ }
+ if (PMIC_SUCCESS == pmic_audio_open(&temp_handle, VOICE_CODEC)) {
+ audio_mixer_control.voice_codec_handle = temp_handle;
+ audio_mixer_control.codec_capture_active = 1;
+ chip->s[SNDRV_PCM_STREAM_CAPTURE].pmic_audio_device.handle =
+ temp_handle;
+ } else {
+ if (audio_mixer_control.codec_playback_active) {
+ temp_handle = audio_mixer_control.voice_codec_handle;
+ } else {
+ return -EBUSY;
+ }
+ }
+ pmic_audio_antipop_enable(ANTI_POP_RAMP_SLOW);
+
+ chip->s[stream_id].stream = substream;
+
+ if (stream_id == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw = snd_mxc_pmic_capture;
+ } else {
+ return err;
+ }
+
+ if ((err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS)) <
+ 0) {
+ return err;
+ }
+
+ if ((err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &hw_capture_rates)) < 0) {
+ return err;
+ }
+
+ /* setup DMA controller for Record */
+ err = configure_read_channel(&mxc_audio->s[SNDRV_PCM_STREAM_CAPTURE],
+ audio_capture_dma_callback);
+ if (err < 0) {
+ return err;
+ }
+
+ /* enable ssi clock */
+ clk_enable(audio_data->ssi_clk[s->ssi]);
+
+ msleep(50);
+
+ return 0;
+}
+
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+static struct page *snd_mxc_audio_playback_nopage(struct vm_area_struct *area,
+ unsigned long address,
+ int *type)
+{
+ struct snd_pcm_substream *substream = area->vm_private_data;
+ struct snd_pcm_runtime *runtime;
+ unsigned long offset;
+ struct page *page;
+ void *vaddr;
+ size_t dma_bytes;
+
+ if (substream == NULL)
+ return NOPAGE_OOM;
+ runtime = substream->runtime;
+ if (g_audio_iram_en) {
+ return NOPAGE_SIGBUS;
+ }
+ offset = area->vm_pgoff << PAGE_SHIFT;
+ offset += address - area->vm_start;
+ if (snd_BUG_ON(offset % PAGE_SIZE) != 0)
+ return NOPAGE_OOM;
+ dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
+ if (offset > dma_bytes - PAGE_SIZE)
+ return NOPAGE_SIGBUS;
+ if (substream->ops->page) {
+ page = substream->ops->page(substream, offset);
+ if (!page)
+ return NOPAGE_OOM;
+ } else {
+ vaddr = runtime->dma_area + offset;
+ page = virt_to_page(vaddr);
+ }
+ get_page(page);
+ if (type)
+ *type = VM_FAULT_MINOR;
+ return page;
+}
+
+static struct vm_operations_struct snd_mxc_audio_playback_vm_ops = {
+ .open = snd_pcm_mmap_data_open,
+ .close = snd_pcm_mmap_data_close,
+ .nopage = snd_mxc_audio_playback_nopage,
+};
+
+#ifdef CONFIG_ARCH_MX3
+static inline int snd_mxc_set_pte_attr(struct mm_struct *mm,
+ pmd_t * pmd,
+ unsigned long addr, unsigned long end)
+{
+
+ pte_t *pte;
+ spinlock_t *ptl;
+ pte = pte_alloc_map_lock(mm, pmd, addr, &ptl);
+
+ if (!pte)
+ return -ENOMEM;
+ do {
+ BUG_ON(pte_none(*pte)); //The mapping is created. It should not be none.
+ *(pte - 512) |= 0x83; //Directly modify to non-shared device
+ } while (pte++, addr += PAGE_SIZE, addr != end);
+
+ pte_unmap_unlock(pte - 1, ptl);
+
+ return 0;
+
+}
+
+static int snd_mxc_set_pmd_attr(struct mm_struct *mm,
+ pud_t * pud,
+ unsigned long addr, unsigned long end)
+{
+
+ pmd_t *pmd;
+ unsigned long next;
+ pmd = pmd_alloc(mm, pud, addr);
+
+ if (!pmd)
+ return -ENOMEM;
+ do {
+
+ next = pmd_addr_end(addr, end);
+ if (snd_mxc_set_pte_attr(mm, pmd, addr, next))
+ return -ENOMEM;
+
+ } while (pmd++, addr = next, addr != end);
+
+ return 0;
+
+}
+
+static int snd_mxc_set_pud_attr(struct mm_struct *mm,
+ pgd_t * pgd,
+ unsigned long addr, unsigned long end)
+{
+
+ pud_t *pud;
+ unsigned long next;
+ pud = pud_alloc(mm, pgd, addr);
+
+ if (!pud)
+ return -ENOMEM;
+ do {
+
+ next = pud_addr_end(addr, end);
+ if (snd_mxc_set_pmd_attr(mm, pud, addr, next))
+ return -ENOMEM;
+
+ } while (pud++, addr = next, addr != end);
+
+ return 0;
+
+}
+
+static inline int snd_mxc_set_pgd_attr(struct vm_area_struct *area)
+{
+
+ int ret = 0;
+ pgd_t *pgd;
+ struct mm_struct *mm = current->mm;
+ unsigned long next, addr = area->vm_start;
+
+ pgd = pgd_offset(mm, addr);
+ flush_cache_range(area, addr, area->vm_end);
+
+ do {
+ if (!pgd_present(*pgd))
+ return -1;
+
+ next = pgd_addr_end(addr, area->vm_end);
+ if ((ret = snd_mxc_set_pud_attr(mm, pgd, addr, next)))
+ break;
+
+ } while (pgd++, addr = next, addr != area->vm_end);
+
+ return ret;
+
+}
+
+#else
+#define snd_mxc_set_page_attr() (0)
+#endif
+
+static int snd_mxc_audio_playback_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
+{
+ int ret = 0;
+ area->vm_ops = &snd_mxc_audio_playback_vm_ops;
+ area->vm_private_data = substream;
+ if (g_audio_iram_en) {
+ unsigned long off = area->vm_pgoff << PAGE_SHIFT;
+ unsigned long phys = ADMA_BASE_PADDR + off;
+ unsigned long size = area->vm_end - area->vm_start;
+ if (off + size > MAX_IRAM_SIZE) {
+ return -EINVAL;
+ }
+ area->vm_page_prot = pgprot_nonshareddev(area->vm_page_prot);
+ area->vm_flags |= VM_IO;
+ ret =
+ remap_pfn_range(area, area->vm_start, phys >> PAGE_SHIFT,
+ size, area->vm_page_prot);
+ if (ret == 0) {
+ ret = snd_mxc_set_pgd_attr(area);
+ }
+
+ } else {
+ area->vm_flags |= VM_RESERVED;
+ }
+ if (ret == 0)
+ area->vm_ops->open(area);
+ return ret;
+}
+
+#endif /*CONFIG_SND_MXC_PMIC_IRAM */
+
+/*!
+ * This structure is the list of operation that the driver
+ * must provide for the capture interface
+ */
+static struct snd_pcm_ops snd_card_mxc_audio_capture_ops = {
+ .open = snd_card_mxc_audio_capture_open,
+ .close = snd_card_mxc_audio_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_mxc_audio_hw_params,
+ .hw_free = snd_mxc_audio_hw_free,
+ .prepare = snd_mxc_audio_capture_prepare,
+ .trigger = snd_mxc_audio_capture_trigger,
+ .pointer = snd_mxc_audio_capture_pointer,
+};
+
+/*!
+ * This structure is the list of operation that the driver
+ * must provide for the playback interface
+ */
+static struct snd_pcm_ops snd_card_mxc_audio_playback_ops = {
+ .open = snd_card_mxc_audio_playback_open,
+ .close = snd_card_mxc_audio_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_mxc_audio_hw_params,
+ .hw_free = snd_mxc_audio_hw_free,
+ .prepare = snd_mxc_audio_playback_prepare,
+ .trigger = snd_mxc_audio_playback_trigger,
+ .pointer = snd_mxc_audio_playback_pointer,
+#ifdef CONFIG_SND_MXC_PMIC_IRAM
+ .mmap = snd_mxc_audio_playback_mmap,
+#endif /*CONFIG_SND_MXC_PMIC_IRAM */
+};
+
+/*!
+ * This functions initializes the capture audio device supported by
+ * PMIC IC.
+ *
+ * @param mxc_audio pointer to the sound card structure
+ *
+ */
+void init_device_capture(mxc_pmic_audio_t * mxc_audio)
+{
+ audio_stream_t *audio_stream;
+ pmic_audio_device_t *pmic_device;
+
+ audio_stream = &mxc_audio->s[SNDRV_PCM_STREAM_CAPTURE];
+ pmic_device = &audio_stream->pmic_audio_device;
+
+ /* These parameters defines the identity of
+ * the device (codec or stereodac)
+ */
+ audio_stream->ssi = SSI1;
+ audio_stream->dam_port = DAM_PORT_4;
+ pmic_device->ssi = SSI1;
+
+ pmic_device->mode = BUS_MASTER_MODE;
+ pmic_device->protocol = NETWORK_MODE;
+
+#ifdef CONFIG_MXC_PMIC_SC55112
+ pmic_device->pll = CLOCK_IN_CLKIN;
+ pmic_device->pll_rate = VCODEC_CLI_33_6MHZ;
+#else
+ if (machine_is_mx31ads()) {
+ pmic_device->pll = CLOCK_IN_CLIB;
+ } else {
+ pmic_device->pll = CLOCK_IN_CLIA;
+ }
+ pmic_device->pll_rate = VCODEC_CLI_26MHZ;
+#endif
+ pmic_device->bcl_inverted = 0;
+ pmic_device->fs_inverted = 0;
+
+}
+
+/*!
+ * This functions initializes the playback audio device supported by
+ * PMIC IC.
+ *
+ * @param mxc_audio pointer to the sound card structure.
+ * @param device device ID of PCM instance.
+ *
+ */
+void init_device_playback(mxc_pmic_audio_t * mxc_audio, int device)
+{
+ audio_stream_t *audio_stream;
+ pmic_audio_device_t *pmic_device;
+ if (device == 0)
+ audio_stream = &mxc_audio->s[0];
+ else
+ audio_stream = &mxc_audio->s[2];
+ pmic_device = &audio_stream->pmic_audio_device;
+
+ /* These parameters defines the identity of
+ * the device (codec or stereodac)
+ */
+ if (device == 0) {
+ if (audio_data->ssi_num == 2) {
+ audio_stream->ssi = SSI2;
+ audio_stream->dam_port = DAM_PORT_5;
+ pmic_device->ssi = SSI2;
+ } else {
+ audio_stream->ssi = SSI1;
+ audio_stream->dam_port = DAM_PORT_4;
+ pmic_device->ssi = SSI1;
+ }
+
+ pmic_device->mode = BUS_MASTER_MODE;
+ pmic_device->protocol = NETWORK_MODE;
+
+#ifdef CONFIG_MXC_PMIC_SC55112
+ pmic_device->pll = CLOCK_IN_CLKIN;
+ pmic_device->pll_rate = STDAC_CLI_33_6MHZ;
+#else
+ if (machine_is_mx31ads()) {
+ pmic_device->pll = CLOCK_IN_CLIB;
+ } else {
+ pmic_device->pll = CLOCK_IN_CLIA;
+ }
+ pmic_device->pll_rate = STDAC_CLI_26MHZ;
+#endif
+
+ pmic_device->bcl_inverted = 0;
+ pmic_device->fs_inverted = 0;
+
+ } else if ((device == 1) || (device == 2)) {
+ audio_stream->ssi = SSI1;
+ audio_stream->dam_port = DAM_PORT_4;
+ pmic_device->ssi = SSI1;
+
+ pmic_device->mode = BUS_MASTER_MODE;
+ pmic_device->protocol = NETWORK_MODE;
+
+#ifdef CONFIG_MXC_PMIC_SC55112
+ pmic_device->pll = CLOCK_IN_CLKIN;
+ pmic_device->pll_rate = VCODEC_CLI_33_6MHZ;
+#else
+ if (machine_is_mx31ads()) {
+ pmic_device->pll = CLOCK_IN_CLIB;
+ } else {
+ pmic_device->pll = CLOCK_IN_CLIA;
+ }
+ pmic_device->pll_rate = VCODEC_CLI_26MHZ;
+#endif
+ pmic_device->bcl_inverted = 0;
+ pmic_device->fs_inverted = 0;
+ }
+
+}
+
+/*!
+ * This functions initializes the mixer related information
+ *
+ * @param mxc_audio pointer to the sound card structure.
+ *
+ */
+void mxc_pmic_mixer_controls_init(mxc_pmic_audio_t * mxc_audio)
+{
+ audio_mixer_control_t *audio_control;
+ int i = 0;
+
+ audio_control = &audio_mixer_control;
+
+ memset(audio_control, 0, sizeof(audio_mixer_control_t));
+ sema_init(&audio_control->sem, 1);
+
+ audio_control->input_device = SOUND_MASK_MIC;
+ audio_control->output_device = SOUND_MASK_VOLUME | SOUND_MASK_PCM;
+
+ /* PMIC has to internal sources that can be routed to output
+ One is codec direct out and the other is mixer out
+ Initially we configure all outputs to have no source and
+ will be later configured either by PCM stream handler or mixer */
+ for (i = 0; i < OP_MAXDEV && i != OP_HEADSET; i++) {
+ audio_control->source_for_output[i] = MIXER_OUT;
+ }
+
+ /* These bits are initially reset and set when playback begins */
+ audio_control->codec_out_to_mixer = 0;
+ audio_control->stdac_out_to_mixer = 0;
+
+ audio_control->mixer_balance = 50;
+ if (machine_is_mx31ads())
+ audio_control->mixer_mono_adder = STEREO_OPPOSITE_PHASE;
+ else
+ audio_control->mixer_mono_adder = MONO_ADDER_OFF;
+ /* Default values for input and output */
+ audio_control->input_volume = ((40 << 8) & 0xff00) | (40 & 0x00ff);
+ audio_control->master_volume_out = ((50 << 8) & 0xff00) | (50 & 0x00ff);
+
+ if (PMIC_SUCCESS != pmic_audio_set_autodetect(1))
+ msleep(30);
+}
+
+/*!
+ * This functions initializes the 2 audio devices supported by
+ * PMIC IC. The parameters define the type of device (CODEC or STEREODAC)
+ *
+ * @param mxc_audio pointer to the sound card structure.
+ * @param device device id of the PCM stream.
+ *
+ */
+void mxc_pmic_audio_init(mxc_pmic_audio_t * mxc_audio, int device)
+{
+ if (device == 0) {
+ mxc_audio->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Audio out";
+ mxc_audio->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id =
+ SNDRV_PCM_STREAM_PLAYBACK;
+ mxc_audio->s[SNDRV_PCM_STREAM_CAPTURE].id = "Audio in";
+ mxc_audio->s[SNDRV_PCM_STREAM_CAPTURE].stream_id =
+ SNDRV_PCM_STREAM_CAPTURE;
+ } else if (device == 1) {
+ mxc_audio->s[2].id = "Audio out";
+ mxc_audio->s[2].stream_id = 2;
+ } else if (device == 2) {
+ mxc_audio->s[2].id = "Audio out";
+ mxc_audio->s[2].stream_id = 3;
+ }
+
+ init_device_playback(mxc_audio, device);
+ if (!device) {
+ init_device_capture(mxc_audio);
+ }
+}
+
+/*!
+ * This function the soundcard structure.
+ *
+ * @param mxc_audio pointer to the sound card structure.
+ * @param device the device index (zero based)
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int __devinit snd_card_mxc_audio_pcm(mxc_pmic_audio_t *mxc_audio,
+ int device)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ /*
+ * Create a new PCM instance with 1 capture stream and 1 playback substream
+ */
+ if ((err = snd_pcm_new(mxc_audio->card, "MXC", device, 1, 1, &pcm)) < 0) {
+ return err;
+ }
+
+ /*
+ * this sets up our initial buffers and sets the dma_type to isa.
+ * isa works but I'm not sure why (or if) it's the right choice
+ * this may be too large, trying it for now
+ */
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data
+ (GFP_KERNEL), MAX_BUFFER_SIZE * 2,
+ MAX_BUFFER_SIZE * 2);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_card_mxc_audio_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_card_mxc_audio_capture_ops);
+
+ pcm->private_data = mxc_audio;
+ pcm->info_flags = 0;
+ strncpy(pcm->name, SOUND_CARD_NAME, sizeof(pcm->name));
+ mxc_audio->pcm[device] = pcm;
+ mxc_pmic_audio_init(mxc_audio, device);
+
+ /* Allocating a second device for PCM playback on voice codec */
+ device = 1;
+ if ((err = snd_pcm_new(mxc_audio->card, "MXC", device, 1, 0, &pcm)) < 0) {
+ return err;
+ }
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data
+ (GFP_KERNEL), MAX_BUFFER_SIZE * 2,
+ MAX_BUFFER_SIZE * 2);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_card_mxc_audio_playback_ops);
+ pcm->private_data = mxc_audio;
+ pcm->info_flags = 0;
+ strncpy(pcm->name, SOUND_CARD_NAME, sizeof(pcm->name));
+ mxc_audio->pcm[device] = pcm;
+ mxc_pmic_audio_init(mxc_audio, device);
+
+ device = 2;
+ if ((err = snd_pcm_new(mxc_audio->card, "MXC", device, 1, 0, &pcm)) < 0) {
+ return err;
+ }
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data
+ (GFP_KERNEL), MAX_BUFFER_SIZE * 2,
+ MAX_BUFFER_SIZE * 2);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_card_mxc_audio_playback_ops);
+ pcm->private_data = mxc_audio;
+ pcm->info_flags = 0;
+ strncpy(pcm->name, SOUND_CARD_NAME, sizeof(pcm->name));
+ mxc_audio->pcm[device] = pcm;
+
+ /* End of allocation */
+ /* FGA for record and not hard coded playback */
+ mxc_pmic_mixer_controls_init(mxc_audio);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function suspends all active streams.
+ *
+ * TBD
+ *
+ * @param card pointer to the sound card structure.
+ * @param state requested state
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_mxc_audio_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ struct snd_card *card = platform_get_drvdata(dev);
+ mxc_pmic_audio_t *chip = card->private_data;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ snd_pcm_suspend_all(chip->pcm[0]);
+ //mxc_alsa_audio_shutdown(chip);
+
+ return 0;
+}
+
+/*!
+ * This function resumes all suspended streams.
+ *
+ * TBD
+ *
+ * @param card pointer to the sound card structure.
+ * @param state requested state
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_mxc_audio_resume(struct platform_device *dev)
+{
+ struct snd_card *card = platform_get_drvdata(dev);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+#endif /* COMFIG_PM */
+
+/*!
+ * This function frees the sound card structure
+ *
+ * @param card pointer to the sound card structure.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+void snd_mxc_audio_free(struct snd_card *card)
+{
+ mxc_pmic_audio_t *chip;
+
+ chip = card->private_data;
+ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
+ audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
+ mxc_audio = NULL;
+ card->private_data = NULL;
+ kfree(chip);
+
+}
+
+/*!
+ * This function initializes the driver in terms of memory of the soundcard
+ * and some basic HW clock settings.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int __devinit mxc_alsa_audio_probe(struct platform_device *pdev)
+{
+ int err;
+ struct snd_card *card;
+
+ audio_data = (struct mxc_audio_platform_data *)pdev->dev.platform_data;
+ if (!audio_data) {
+ dev_err(&pdev->dev, "Can't get the platform data for ALSA\n");
+ return -EINVAL;
+ }
+ /* register the soundcard */
+ card = snd_card_new(-1, id, THIS_MODULE, sizeof(mxc_pmic_audio_t));
+ if (card == NULL) {
+ return -ENOMEM;
+ }
+
+ mxc_audio = kcalloc(1, sizeof(*mxc_audio), GFP_KERNEL);
+ if (mxc_audio == NULL) {
+ return -ENOMEM;
+ }
+
+ card->private_data = (void *)mxc_audio;
+ card->private_free = snd_mxc_audio_free;
+
+ mxc_audio->card = card;
+ card->dev = &pdev->dev;
+ if ((err = snd_card_mxc_audio_pcm(mxc_audio, 0)) < 0) {
+ goto nodev;
+ }
+
+ if (0 == mxc_alsa_create_ctl(card, (void *)&audio_mixer_control))
+ printk(KERN_INFO "Control ALSA component registered\n");
+
+#ifdef CONFIG_HEADSET_DETECT_ENABLE
+ pmic_audio_antipop_enable(ANTI_POP_RAMP_SLOW);
+ pmic_audio_set_callback((PMIC_AUDIO_CALLBACK) HSCallback,
+ HEADSET_DETECTED | HEADSET_REMOVED, &hs_state);
+#endif
+ /* Set autodetect feature in order to allow audio operations */
+
+ spin_lock_init(&(mxc_audio->s[0].dma_lock));
+ spin_lock_init(&(mxc_audio->s[1].dma_lock));
+ spin_lock_init(&(mxc_audio->s[2].dma_lock));
+
+ strcpy(card->driver, "MXC");
+ strcpy(card->shortname, "PMIC-audio");
+ sprintf(card->longname, "MXC Freescale with PMIC");
+
+ if ((err = snd_card_register(card)) == 0) {
+ pr_debug(KERN_INFO "MXC audio support initialized\n");
+ platform_set_drvdata(pdev, card);
+ return 0;
+ }
+
+ nodev:
+ snd_card_free(card);
+ return err;
+}
+
+static int __devexit mxc_alsa_audio_remove(struct platform_device *dev)
+{
+ snd_card_free(mxc_audio->card);
+ kfree(mxc_audio);
+ platform_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver mxc_alsa_audio_driver = {
+ .probe = mxc_alsa_audio_probe,
+ .remove = __devexit_p(mxc_alsa_audio_remove),
+#ifdef CONFIG_PM
+ .suspend = snd_mxc_audio_suspend,
+ .resume = snd_mxc_audio_resume,
+#endif
+ .driver = {
+ .name = "mxc_alsa",
+ },
+};
+
+static int __init mxc_alsa_audio_init(void)
+{
+ return platform_driver_register(&mxc_alsa_audio_driver);
+}
+
+/*!
+ * This function frees the sound driver structure.
+ *
+ */
+
+static void __exit mxc_alsa_audio_exit(void)
+{
+ platform_driver_unregister(&mxc_alsa_audio_driver);
+}
+
+module_init(mxc_alsa_audio_init);
+module_exit(mxc_alsa_audio_exit);
+
+MODULE_AUTHOR("FREESCALE SEMICONDUCTOR");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MXC driver for ALSA");
+MODULE_SUPPORTED_DEVICE("{{PMIC}}");
+
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for MXC + PMIC soundcard.");
diff --git a/sound/arm/mxc-alsa-pmic.h b/sound/arm/mxc-alsa-pmic.h
new file mode 100644
index 000000000000..46c5ca4ae1ab
--- /dev/null
+++ b/sound/arm/mxc-alsa-pmic.h
@@ -0,0 +1,110 @@
+/*
+ * 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
+ *
+ * Copyright 2007-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+ /*!
+ * @file mxc-alsa-pmic.h
+ * @brief
+ * @ingroup SOUND_DRV
+ */
+
+#ifndef __MXC_ALSA_PMIC_H__
+#define __MXC_ALSA_PMIC_H__
+
+#ifdef __KERNEL__
+
+ /**/
+#define PMIC_MASTER 0x1
+#define PMIC_SLAVE 0x2
+ /**/
+#define DAM_PORT_4 port_4
+#define DAM_PORT_5 port_5
+ /**/
+#define PMIC_STEREODAC 0x1
+#define PMIC_CODEC 0x2
+ /**/
+#define PMIC_AUDIO_ADDER_STEREO 0x1
+#define PMIC_AUDIO_ADDER_STEREO_OPPOSITE 0x2
+#define PMIC_AUDIO_ADDER_MONO 0x4
+#define PMIC_AUDIO_ADDER_MONO_OPPOSITE 0x8
+#define TX_WATERMARK 0x4
+#define RX_WATERMARK 0x6
+ /**/
+#define SSI_DEFAULT_FIFO 0x0
+#define DEFAULT_MASTER_CLOCK 0x1
+ /**/
+#define SDMA_TXFIFO_WATERMARK 0x4
+#define SDMA_RXFIFO_WATERMARK 0x6
+ /**/
+#define TIMESLOTS_2 0x3
+#define TIMESLOTS_4 0x2
+#define SAMPLE_RATE_MAX 0x9
+ /**/
+#define CLK_SELECT_SLAVE_BCL 0x7
+#define CLK_SELECT_SLAVE_CLI 0x5
+#define CLK_SELECT_MASTER_CLI 0x4
+/* Volume to balance ratio */
+#define VOLUME_BALANCE_RATIO ((6 + 39) / (21 + 21))
+/* -21dB */
+#define PMIC_BALANCE_MIN 0x0
+/* 0dB*/
+#define PMIC_BALANCE_MAX 0x7
+/* -21dB left */
+#define BALANCE_MIN 0x0
+/* 0db, no balance */
+#define NO_BALANCE 0x32
+/* -21dB right*/
+#define BALANCE_MAX 0x64
+/* -8dB */
+#define PMIC_INPUT_VOLUME_MIN 0x0
+/* +23dB */
+#define PMIC_INPUT_VOLUME_MAX 0x1f
+/* -39dB */
+#define PMIC_OUTPUT_VOLUME_MIN PMIC_INPUT_VOLUME_MIN
+/* +6dB */
+#define PMIC_OUTPUT_VOLUME_MAX 0xd
+/* -8dB */
+#define INPUT_VOLUME_MIN 0x0
+/* +23dB */
+#define INPUT_VOLUME_MAX 0x64
+/* -39dB */
+#define OUTPUT_VOLUME_MIN 0x0
+/* +6dB */
+#define OUTPUT_VOLUME_MAX 0x64
+/* 96 Khz */
+#define SAMPLE_FREQ_MAX 96000
+/* 8 Khz */
+#define SAMPLE_FREQ_MIN 8000
+/*!
+ * Define channels available on MC13783. This is mainly used
+ * in mixer interface to control different input/output
+ * devices
+ */
+#define MXC_STEREO_OUTPUT ( SOUND_MASK_VOLUME | SOUND_MASK_PCM )
+#define MXC_STEREO_INPUT ( SOUND_MASK_PHONEIN )
+#define MXC_MONO_OUTPUT ( SOUND_MASK_PHONEOUT | SOUND_MASK_SPEAKER )
+#define MXC_MONO_INPUT ( SOUND_MASK_LINE | SOUND_MASK_MIC )
+#define SNDCTL_CLK_SET_MASTER _SIOR('Z', 30, int)
+#define SNDCTL_PMIC_READ_OUT_BALANCE _SIOR('Z', 8, int)
+//#define SNDCTL_MC13783_WRITE_OUT_BALANCE _SIOWR('Z', 9, int)
+#define SNDCTL_PMIC_WRITE_OUT_ADDER _SIOWR('Z', 10, int)
+#define SNDCTL_PMIC_READ_OUT_ADDER _SIOR('Z', 11, int)
+#define SNDCTL_PMIC_WRITE_CODEC_FILTER _SIOWR('Z', 12, int)
+#define SNDCTL_PMIC_READ_CODEC_FILTER _SIOR('Z', 13, int)
+#define SNDCTL_DAM_SET_OUT_PORT _SIOWR('Z', 14, int)
+#endif /* __KERNEL__ */
+#endif /* __MXC_ALSA_PMIC_H__ */
diff --git a/sound/arm/mxc-alsa-spdif.c b/sound/arm/mxc-alsa-spdif.c
new file mode 100644
index 000000000000..21c632365a85
--- /dev/null
+++ b/sound/arm/mxc-alsa-spdif.c
@@ -0,0 +1,2266 @@
+/*
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @defgroup SOUND_DRV MXC Sound Driver for ALSA
+ */
+
+/*!
+ * @file mxc-alsa-spdif.c
+ * @brief this fle mxc-alsa-spdif.c
+ * @brief this file implements mxc alsa driver for spdif.
+ * The spdif tx supports consumer channel for linear PCM and
+ * compressed audio data. The supported sample rates are
+ * 48KHz, 44.1KHz and 32KHz.
+ *
+ * @ingroup SOUND_DRV
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/soundcard.h>
+#include <linux/clk.h>
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#include <mach/dma.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+
+#define MXC_SPDIF_NAME "MXC_SPDIF"
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for spdif sound card.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for spdif sound card.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable spdif sound card.");
+
+#define SPDIF_MAX_BUF_SIZE (32*1024)
+#define SPDIF_DMA_BUF_SIZE (8*1024)
+#define SPDIF_MIN_PERIOD_SIZE 64
+#define SPDIF_MIN_PERIOD 2
+#define SPDIF_MAX_PERIOD 255
+
+#define SPDIF_REG_SCR 0x00
+#define SPDIF_REG_SRCD 0x04
+#define SPDIF_REG_SRPC 0x08
+#define SPDIF_REG_SIE 0x0C
+#define SPDIF_REG_SIS 0x10
+#define SPDIF_REG_SIC 0x10
+#define SPDIF_REG_SRL 0x14
+#define SPDIF_REG_SRR 0x18
+#define SPDIF_REG_SRCSLH 0x1C
+#define SPDIF_REG_SRCSLL 0x20
+#define SPDIF_REG_SQU 0x24
+#define SPDIF_REG_SRQ 0x28
+#define SPDIF_REG_STL 0x2C
+#define SPDIF_REG_STR 0x30
+#define SPDIF_REG_STCSCH 0x34
+#define SPDIF_REG_STCSCL 0x38
+#define SPDIF_REG_SRFM 0x44
+#define SPDIF_REG_STC 0x50
+
+/* SPDIF Configuration register */
+#define SCR_RXFIFO_CTL_ZERO (1 << 23)
+#define SCR_RXFIFO_OFF (1 << 22)
+#define SCR_RXFIFO_RST (1 << 21)
+#define SCR_RXFIFO_FSEL_BIT (19)
+#define SCR_RXFIFO_FSEL_MASK (0x3 << SCR_RXFIFO_FSEL_BIT)
+#define SCR_RXFIFO_AUTOSYNC (1 << 18)
+#define SCR_TXFIFO_AUTOSYNC (1 << 17)
+#define SCR_TXFIFO_ESEL_BIT (15)
+#define SCR_TXFIFO_ESEL_MASK (0x3 << SCR_TXFIFO_FSEL_BIT)
+#define SCR_LOW_POWER (1 << 13)
+#define SCR_SOFT_RESET (1 << 12)
+#define SCR_TXFIFO_ZERO (0 << 10)
+#define SCR_TXFIFO_NORMAL (1 << 10)
+#define SCR_TXFIFO_ONESAMPLE (1 << 11)
+#define SCR_DMA_RX_EN (1 << 9)
+#define SCR_DMA_TX_EN (1 << 8)
+#define SCR_VAL_CLEAR (1 << 5)
+#define SCR_TXSEL_OFF (0 << 2)
+#define SCR_TXSEL_RX (1 << 2)
+#define SCR_TXSEL_NORMAL (0x5 << 2)
+#define SCR_USRC_SEL_NONE (0x0)
+#define SCR_USRC_SEL_RECV (0x1)
+#define SCR_USRC_SEL_CHIP (0x3)
+
+/* SPDIF CDText control */
+#define SRCD_CD_USER_OFFSET 1
+#define SRCD_CD_USER (1 << SRCD_CD_USER_OFFSET)
+
+/* SPDIF Phase Configuration register */
+#define SRPC_DPLL_LOCKED (1 << 6)
+#define SRPC_CLKSRC_SEL_OFFSET 7
+#define SRPC_CLKSRC_SEL_LOCKED 5
+/* gain sel */
+#define SRPC_GAINSEL_OFFSET 3
+
+enum spdif_gainsel {
+ GAINSEL_MULTI_24 = 0,
+ GAINSEL_MULTI_16,
+ GAINSEL_MULTI_12,
+ GAINSEL_MULTI_8,
+ GAINSEL_MULTI_6,
+ GAINSEL_MULTI_4,
+ GAINSEL_MULTI_3,
+ GAINSEL_MULTI_MAX,
+};
+
+#define SPDIF_DEFAULT_GAINSEL GAINSEL_MULTI_8
+
+/* SPDIF interrupt mask define */
+#define INT_DPLL_LOCKED (1 << 20)
+#define INT_TXFIFO_UNOV (1 << 19)
+#define INT_TXFIFO_RESYNC (1 << 18)
+#define INT_CNEW (1 << 17)
+#define INT_VAL_NOGOOD (1 << 16)
+#define INT_SYM_ERR (1 << 15)
+#define INT_BIT_ERR (1 << 14)
+#define INT_URX_FUL (1 << 10)
+#define INT_URX_OV (1 << 9)
+#define INT_QRX_FUL (1 << 8)
+#define INT_QRX_OV (1 << 7)
+#define INT_UQ_SYNC (1 << 6)
+#define INT_UQ_ERR (1 << 5)
+#define INT_RX_UNOV (1 << 4)
+#define INT_RX_RESYNC (1 << 3)
+#define INT_LOSS_LOCK (1 << 2)
+#define INT_TX_EMPTY (1 << 1)
+#define INT_RXFIFO_FUL (1 << 0)
+
+/* SPDIF Clock register */
+#define STC_SYSCLK_DIV_OFFSET 11
+#define STC_TXCLK_SRC_OFFSET 8
+#define STC_TXCLK_DIV_OFFSET 0
+
+#define SPDIF_CSTATUS_BYTE 6
+#define SPDIF_UBITS_SIZE 96
+#define SPDIF_QSUB_SIZE (SPDIF_UBITS_SIZE/8)
+
+enum spdif_clk_accuracy {
+ SPDIF_CLK_ACCURACY_LEV2 = 0,
+ SPDIF_CLK_ACCURACY_LEV1 = 2,
+ SPDIF_CLK_ACCURACY_LEV3 = 1,
+ SPDIF_CLK_ACCURACY_RESV = 3
+};
+
+/* SPDIF clock source */
+enum spdif_clk_src {
+ SPDIF_CLK_SRC1 = 0,
+ SPDIF_CLK_SRC2,
+ SPDIF_CLK_SRC3,
+ SPDIF_CLK_SRC4,
+ SPDIF_CLK_SRC5,
+};
+
+enum spdif_max_wdl {
+ SPDIF_MAX_WDL_20,
+ SPDIF_MAX_WDL_24
+};
+
+enum spdif_wdl {
+ SPDIF_WDL_DEFAULT = 0,
+ SPDIF_WDL_FIFTH = 4,
+ SPDIF_WDL_FOURTH = 3,
+ SPDIF_WDL_THIRD = 2,
+ SPDIF_WDL_SECOND = 1,
+ SPDIF_WDL_MAX = 5
+};
+
+static unsigned int gainsel_multi[GAINSEL_MULTI_MAX] = {
+ 24 * 1024, 16 * 1024, 12 * 1024,
+ 8 * 1024, 6 * 1024, 4 * 1024,
+ 3 * 1024,
+};
+
+/*!
+ * SPDIF control structure
+ * Defines channel status, subcode and Q sub
+ */
+struct spdif_mixer_control {
+
+ /* spinlock to access control data */
+ spinlock_t ctl_lock;
+ /* IEC958 channel tx status bit */
+ unsigned char ch_status[4];
+ /* User bits */
+ unsigned char subcode[2 * SPDIF_UBITS_SIZE];
+ /* Q subcode part of user bits */
+ unsigned char qsub[2 * SPDIF_QSUB_SIZE];
+ /* buffer ptrs for writer */
+ unsigned int upos;
+ unsigned int qpos;
+ /* ready buffer index of the two buffers */
+ unsigned int ready_buf;
+};
+
+/*!
+ * This structure represents an audio stream in term of
+ * channel DMA, HW configuration on spdif controller.
+ */
+struct mxc_spdif_stream {
+
+ /*!
+ * identification string
+ */
+ char *id;
+ /*!
+ * device identifier for DMA
+ */
+ int dma_wchannel;
+ /*!
+ * we are using this stream for transfer now
+ */
+ int active:1;
+ /*!
+ * current transfer period
+ */
+ int period;
+ /*!
+ * current count of transfered periods
+ */
+ int periods;
+ /*!
+ * for locking in DMA operations
+ */
+ spinlock_t dma_lock;
+ /*!
+ * Alsa substream pointer
+ */
+ struct snd_pcm_substream *stream;
+};
+
+struct mxc_spdif_device {
+ /*!
+ * SPDIF module register base address
+ */
+ unsigned long __iomem *reg_base;
+
+ /*!
+ * spdif tx available or not
+ */
+ int mxc_spdif_tx;
+
+ /*!
+ * spdif rx available or not
+ */
+ int mxc_spdif_rx;
+
+ /*!
+ * spdif 44100 clock src
+ */
+ int spdif_txclk_44100;
+
+ /*!
+ * spdif 48000 clock src
+ */
+ int spdif_txclk_48000;
+
+ /*!
+ * ALSA SPDIF sound card handle
+ */
+ struct snd_card *card;
+
+ /*!
+ * ALSA spdif driver type handle
+ */
+ struct snd_pcm *pcm;
+
+ /*!
+ * DPLL locked status
+ */
+ atomic_t dpll_locked;
+
+ /*!
+ * Playback/Capture substream
+ */
+ struct mxc_spdif_stream s[2];
+};
+
+static struct spdif_mixer_control mxc_spdif_control;
+
+static unsigned long spdif_base_addr;
+
+/* define each spdif interrupt handlers */
+typedef void (*spdif_irq_func_t) (unsigned int bit, void *desc);
+
+/* spdif irq functions declare */
+static void spdif_irq_fifo(unsigned int bit, void *devid);
+static void spdif_irq_dpll_lock(unsigned int bit, void *devid);
+static void spdif_irq_uq(unsigned int bit, void *devid);
+static void spdif_irq_bit_error(unsigned int bit, void *devid);
+static void spdif_irq_sym_error(unsigned int bit, void *devid);
+static void spdif_irq_valnogood(unsigned int bit, void *devid);
+static void spdif_irq_cnew(unsigned int bit, void *devid);
+
+/* irq function list */
+static spdif_irq_func_t spdif_irq_handlers[] = {
+ spdif_irq_fifo,
+ spdif_irq_fifo,
+ spdif_irq_dpll_lock,
+ NULL,
+ spdif_irq_fifo,
+ spdif_irq_uq,
+ spdif_irq_uq,
+ spdif_irq_uq,
+ spdif_irq_uq,
+ spdif_irq_uq,
+ spdif_irq_uq,
+ NULL,
+ NULL,
+ NULL,
+ spdif_irq_bit_error,
+ spdif_irq_sym_error,
+ spdif_irq_valnogood,
+ spdif_irq_cnew,
+ NULL,
+ spdif_irq_fifo,
+ spdif_irq_dpll_lock,
+};
+
+/*!
+ * @brief Enable/Disable spdif DMA request
+ *
+ * This function is called to enable or disable the dma transfer
+ */
+static void spdif_dma_enable(int txrx, int enable)
+{
+ unsigned long value;
+
+ value = __raw_readl(SPDIF_REG_SCR + spdif_base_addr) & 0xfffeff;
+ if (enable)
+ value |= txrx;
+
+ __raw_writel(value, SPDIF_REG_SCR + spdif_base_addr);
+
+}
+
+/*!
+ * @brief Enable spdif interrupt
+ *
+ * This function is called to enable relevant interrupts.
+ */
+static void spdif_intr_enable(unsigned long intr, int enable)
+{
+ unsigned long value;
+
+ value = __raw_readl(spdif_base_addr + SPDIF_REG_SIE) & 0xffffff;
+ if (enable)
+ value |= intr;
+ else
+ value &= ~intr;
+
+ __raw_writel(value, spdif_base_addr + SPDIF_REG_SIE);
+}
+
+/*!
+ * @brief Set the clock accuracy level in the channel bit
+ *
+ * This function is called to set the clock accuracy level
+ */
+static int spdif_set_clk_accuracy(enum spdif_clk_accuracy level)
+{
+ unsigned long value;
+
+ value = __raw_readl(SPDIF_REG_STCSCL + spdif_base_addr) & 0xffffcf;
+ value |= (level << 4);
+ __raw_writel(value, SPDIF_REG_STCSCL + spdif_base_addr);
+
+ return 0;
+}
+
+/*!
+ * set SPDIF PhaseConfig register for rx clock
+ */
+static int spdif_set_rx_clksrc(enum spdif_clk_src clksrc,
+ enum spdif_gainsel gainsel, int dpll_locked)
+{
+ unsigned long value;
+ if (clksrc > SPDIF_CLK_SRC5 || gainsel > GAINSEL_MULTI_3)
+ return 1;
+
+ value = (dpll_locked ? (clksrc) :
+ (clksrc + SRPC_CLKSRC_SEL_LOCKED)) << SRPC_CLKSRC_SEL_OFFSET |
+ (gainsel << SRPC_GAINSEL_OFFSET);
+ __raw_writel(value, spdif_base_addr + SPDIF_REG_SRPC);
+
+ return 0;
+}
+
+/*!
+ * Get RX data clock rate
+ * given the SPDIF bus_clk
+ */
+static int spdif_get_rxclk_rate(struct clk *bus_clk, enum spdif_gainsel gainsel)
+{
+ unsigned long freqmeas, phaseconf, busclk_freq = 0;
+ u64 tmpval64;
+ enum spdif_clk_src clksrc;
+
+ freqmeas = __raw_readl(spdif_base_addr + SPDIF_REG_SRFM);
+ phaseconf = __raw_readl(spdif_base_addr + SPDIF_REG_SRPC);
+
+ clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0x0F;
+ if (clksrc < 5 && (phaseconf & SRPC_DPLL_LOCKED)) {
+ /* get bus clock from system */
+ busclk_freq = clk_get_rate(bus_clk);
+ }
+
+ /* FreqMeas_CLK = (BUS_CLK*FreqMeas[23:0])/2^10/GAINSEL/128 */
+ tmpval64 = (u64) busclk_freq * freqmeas;
+ do_div(tmpval64, gainsel_multi[gainsel]);
+ do_div(tmpval64, 128 * 1024);
+ return (int)tmpval64;
+}
+
+/*!
+ * @brief Set the audio sample rate in the channel status bit
+ *
+ * This function is called to set the audio sample rate to be transfered.
+ */
+static int spdif_set_sample_rate(int src_44100, int src_48000, int sample_rate)
+{
+ unsigned long cstatus, stc;
+
+ cstatus = __raw_readl(SPDIF_REG_STCSCL + spdif_base_addr) & 0xfffff0;
+ stc = __raw_readl(SPDIF_REG_STC + spdif_base_addr) & ~0x7FF;
+
+ switch (sample_rate) {
+ case 44100:
+ __raw_writel(cstatus, SPDIF_REG_STCSCL + spdif_base_addr);
+ stc |= (src_44100 << 8) | 0x07;
+ __raw_writel(stc, SPDIF_REG_STC + spdif_base_addr);
+ pr_debug("set sample rate to 44100\n");
+ break;
+ case 48000:
+ cstatus |= 0x04;
+ __raw_writel(cstatus, SPDIF_REG_STCSCL + spdif_base_addr);
+ stc |= (src_48000 << 8) | 0x07;
+ __raw_writel(stc, SPDIF_REG_STC + spdif_base_addr);
+ pr_debug("set sample rate to 48000\n");
+ break;
+ case 32000:
+ cstatus |= 0x0c;
+ __raw_writel(cstatus, SPDIF_REG_STCSCL + spdif_base_addr);
+ stc |= (src_48000 << 8) | 0x0b;
+ __raw_writel(stc, SPDIF_REG_STC + spdif_base_addr);
+ pr_debug("set sample rate to 32000\n");
+ break;
+ }
+
+ return 0;
+}
+
+/*!
+ * @brief Set the lchannel status bit
+ *
+ * This function is called to set the channel status
+ */
+static int spdif_set_channel_status(int value, unsigned long reg)
+{
+ __raw_writel(value & 0xffffff, reg + spdif_base_addr);
+
+ return 0;
+}
+
+/*!
+ * @brief Get spdif interrupt status and clear the interrupt
+ *
+ * This function is called to check relevant interrupt status
+ */
+static int spdif_intr_status(void)
+{
+ unsigned long value;
+
+ value = __raw_readl(SPDIF_REG_SIS + spdif_base_addr) & 0xffffff;
+ value &= __raw_readl(spdif_base_addr + SPDIF_REG_SIE);
+ __raw_writel(value, SPDIF_REG_SIC + spdif_base_addr);
+
+ return value;
+}
+
+/*!
+ * @brief spdif interrupt handler
+ */
+static irqreturn_t spdif_isr(int irq, void *dev_id)
+{
+ unsigned long int_stat;
+ int line;
+
+ int_stat = spdif_intr_status();
+
+ while ((line = ffs(int_stat)) != 0) {
+ int_stat &= ~(1UL << (line - 1));
+ if (spdif_irq_handlers[line - 1] != NULL)
+ spdif_irq_handlers[line - 1] (line - 1, dev_id);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * Interrupt handlers
+ *
+ */
+/*!
+ * FIFO related interrupts handler
+ *
+ * Rx FIFO Full, Underrun/Overrun interrupts
+ * Tx FIFO Empty, Underrun/Overrun interrupts
+ */
+static void spdif_irq_fifo(unsigned int bit, void *devid)
+{
+
+}
+
+/*!
+ * DPLL lock related interrupts handler
+ *
+ * DPLL locked and lock loss interrupts
+ */
+static void spdif_irq_dpll_lock(unsigned int bit, void *devid)
+{
+ struct mxc_spdif_device *chip = (struct mxc_spdif_device *)devid;
+ unsigned int locked = __raw_readl(spdif_base_addr + SPDIF_REG_SRPC);
+
+ if (locked & SRPC_DPLL_LOCKED) {
+ pr_debug("SPDIF Rx dpll locked\n");
+ atomic_set(&chip->dpll_locked, 1);
+ } else {
+ /* INT_LOSS_LOCK */
+ pr_debug("SPDIF Rx dpll loss lock\n");
+ atomic_set(&chip->dpll_locked, 0);
+ }
+}
+
+/*!
+ * U/Q channel related interrupts handler
+ *
+ * U/QChannel full, overrun interrupts
+ * U/QChannel sync error and frame error interrupts
+ */
+static void spdif_irq_uq(unsigned int bit, void *devid)
+{
+ unsigned long val;
+ int index;
+ struct spdif_mixer_control *ctrl = &mxc_spdif_control;
+
+ bit = 1 << bit;
+ /* get U/Q channel datas */
+ switch (bit) {
+
+ case INT_URX_OV: /* read U data */
+ pr_debug("User bit receive overrun\n");
+ case INT_URX_FUL:
+ pr_debug("U bit receive full\n");
+
+ if (ctrl->upos >= SPDIF_UBITS_SIZE * 2) {
+ ctrl->upos = 0;
+ } else if (unlikely((ctrl->upos % SPDIF_UBITS_SIZE) + 3
+ > SPDIF_UBITS_SIZE)) {
+ pr_err("User bit receivce buffer overflow\n");
+ break;
+ }
+ val = __raw_readl(spdif_base_addr + SPDIF_REG_SQU);
+ ctrl->subcode[ctrl->upos++] = val >> 16;
+ ctrl->subcode[ctrl->upos++] = val >> 8;
+ ctrl->subcode[ctrl->upos++] = val;
+
+ break;
+
+ case INT_QRX_OV: /* read Q data */
+ pr_debug("Q bit receive overrun\n");
+ case INT_QRX_FUL:
+ pr_debug("Q bit receive full\n");
+
+ if (ctrl->qpos >= SPDIF_QSUB_SIZE * 2) {
+ ctrl->qpos = 0;
+ } else if (unlikely((ctrl->qpos % SPDIF_QSUB_SIZE) + 3
+ > SPDIF_QSUB_SIZE)) {
+ pr_err("Q bit receivce buffer overflow\n");
+ break;
+ }
+ val = __raw_readl(spdif_base_addr + SPDIF_REG_SRQ);
+ ctrl->qsub[ctrl->qpos++] = val >> 16;
+ ctrl->qsub[ctrl->qpos++] = val >> 8;
+ ctrl->qsub[ctrl->qpos++] = val;
+
+ break;
+
+ case INT_UQ_ERR: /* read U/Q data and do buffer reset */
+ pr_debug("U/Q bit receive error\n");
+ val = __raw_readl(spdif_base_addr + SPDIF_REG_SQU);
+ val = __raw_readl(spdif_base_addr + SPDIF_REG_SRQ);
+ /* drop this U/Q buffer */
+ ctrl->ready_buf = ctrl->upos = ctrl->qpos = 0;
+ break;
+
+ case INT_UQ_SYNC: /* U/Q buffer reset */
+ pr_debug("U/Q sync receive\n");
+
+ if (ctrl->qpos == 0)
+ break;
+ index = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE;
+ /* set ready to this buffer */
+ ctrl->ready_buf = index + 1;
+ break;
+ }
+}
+
+/*!
+ * SPDIF receiver found parity bit error interrupt handler
+ */
+static void spdif_irq_bit_error(unsigned int bit, void *devid)
+{
+ pr_debug("SPDIF interrupt parity bit error\n");
+}
+
+/*!
+ * SPDIF receiver found illegal symbol interrupt handler
+ */
+static void spdif_irq_sym_error(unsigned int bit, void *devid)
+{
+ pr_debug("SPDIF interrupt symbol error\n");
+}
+
+/*!
+ * SPDIF validity flag no good interrupt handler
+ */
+static void spdif_irq_valnogood(unsigned int bit, void *devid)
+{
+ pr_debug("SPDIF interrupt validate is not good\n");
+}
+
+/*!
+ * SPDIF receive change in value of control channel
+ */
+static void spdif_irq_cnew(unsigned int bit, void *devid)
+{
+ pr_debug("SPDIF interrupt cstatus new\n");
+}
+
+/*!
+ * Do software reset to SPDIF
+ */
+static void spdif_softreset(void)
+{
+ unsigned long value = 1;
+ int cycle = 0;
+ __raw_writel(SCR_SOFT_RESET, spdif_base_addr + SPDIF_REG_SCR);
+ while (value && (cycle++ < 10)) {
+ value = __raw_readl(spdif_base_addr + SPDIF_REG_SCR) & 0x1000;
+ }
+
+}
+
+/*!
+ * SPDIF RX initial function
+ */
+static void spdif_rx_init(void)
+{
+ unsigned long regval;
+
+ regval = __raw_readl(spdif_base_addr + SPDIF_REG_SCR);
+ /**
+ * initial and reset SPDIF configuration:
+ * RxFIFO off
+ * RxFIFO sel to 8 sample
+ * Autosync
+ * Valid bit set
+ */
+ regval &= ~(SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO | SCR_LOW_POWER);
+ regval |= (2 << SCR_RXFIFO_FSEL_BIT) | SCR_RXFIFO_AUTOSYNC;
+ __raw_writel(regval, spdif_base_addr + SPDIF_REG_SCR);
+}
+
+/*!
+ * SPDIF RX un-initial function
+ */
+static void spdif_rx_uninit(void)
+{
+ unsigned long regval;
+
+ /* turn off RX fifo, disable dma and autosync */
+ regval = __raw_readl(spdif_base_addr + SPDIF_REG_SCR);
+ regval |= SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
+ regval &= ~(SCR_DMA_RX_EN | SCR_RXFIFO_AUTOSYNC);
+ __raw_writel(regval, spdif_base_addr + SPDIF_REG_SCR);
+}
+
+/*!
+ * @brief Initialize spdif module
+ *
+ * This function is called to set the spdif to initial state.
+ */
+static void spdif_tx_init(void)
+{
+ unsigned long regval;
+
+ regval = __raw_readl(spdif_base_addr + SPDIF_REG_SCR);
+
+ regval &= 0xfc32e3;
+ regval |= SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_NORMAL |
+ SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP | (2 << SCR_TXFIFO_ESEL_BIT);
+ __raw_writel(regval, SPDIF_REG_SCR + spdif_base_addr);
+
+ /* Default clock source from EXTAL, divider by 8, generate 44.1kHz
+ sample rate */
+ regval = 0x07;
+ __raw_writel(regval, SPDIF_REG_STC + spdif_base_addr);
+
+}
+
+/*!
+ * @brief deinitialize spdif module
+ *
+ * This function is called to stop the spdif
+ */
+static void spdif_tx_uninit(void)
+{
+ unsigned long regval;
+
+ regval = __raw_readl(SPDIF_REG_SCR + spdif_base_addr) & 0xffffe3;
+ regval |= SCR_TXSEL_OFF;
+ __raw_writel(regval, SPDIF_REG_SCR + spdif_base_addr);
+ regval = __raw_readl(SPDIF_REG_STC + spdif_base_addr) & ~0x7FF;
+ regval |= (0x7 << STC_TXCLK_SRC_OFFSET);
+ __raw_writel(regval, SPDIF_REG_STC + spdif_base_addr);
+
+}
+
+static unsigned int spdif_playback_rates[] = { 32000, 44100, 48000 };
+static unsigned int spdif_capture_rates[] = {
+ 16000, 32000, 44100, 48000, 64000, 96000
+};
+
+/*!
+ * this structure represents the sample rates supported
+ * by SPDIF
+ */
+static struct snd_pcm_hw_constraint_list hw_playback_rates_stereo = {
+ .count = ARRAY_SIZE(spdif_playback_rates),
+ .list = spdif_playback_rates,
+ .mask = 0,
+};
+
+static struct snd_pcm_hw_constraint_list hw_capture_rates_stereo = {
+ .count = ARRAY_SIZE(spdif_capture_rates),
+ .list = spdif_capture_rates,
+ .mask = 0,
+};
+
+/*!
+ * This structure reprensents the capabilities of the driver
+ * in playback mode.
+ */
+static struct snd_pcm_hardware snd_spdif_playback_hw = {
+ .info =
+ (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_HALF_DUPLEX |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .rates =
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+ .rate_min = 32000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = SPDIF_MAX_BUF_SIZE,
+ .period_bytes_min = SPDIF_MIN_PERIOD_SIZE,
+ .period_bytes_max = SPDIF_DMA_BUF_SIZE,
+ .periods_min = SPDIF_MIN_PERIOD,
+ .periods_max = SPDIF_MAX_PERIOD,
+ .fifo_size = 0,
+};
+
+/*!
+ * This structure reprensents the capabilities of the driver
+ * in capture mode.
+ */
+static struct snd_pcm_hardware snd_spdif_capture_hw = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ .rates =
+ (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100
+ | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 |
+ SNDRV_PCM_RATE_96000),
+ .rate_min = 16000,
+ .rate_max = 96000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = SPDIF_MAX_BUF_SIZE,
+ .period_bytes_min = SPDIF_MIN_PERIOD_SIZE,
+ .period_bytes_max = SPDIF_DMA_BUF_SIZE,
+ .periods_min = SPDIF_MIN_PERIOD,
+ .periods_max = SPDIF_MAX_PERIOD,
+ .fifo_size = 0,
+
+};
+
+/*!
+ * This function configures the DMA channel used to transfer
+ * audio from MCU to SPDIF or from SPDIF to MCU
+ *
+ * @param s pointer to the structure of the current stream.
+ * @param callback pointer to function that will be
+ * called when a SDMA TX transfer finishes.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int
+spdif_configure_dma_channel(struct mxc_spdif_stream *s,
+ mxc_dma_callback_t callback)
+{
+ int ret = -1;
+ int channel = -1;
+
+ if (s->dma_wchannel != 0)
+ mxc_dma_free(s->dma_wchannel);
+
+ if (s->stream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+
+ if (s->stream->runtime->sample_bits > 16) {
+ channel =
+ mxc_dma_request(MXC_DMA_SPDIF_32BIT_TX,
+ "SPDIF TX DMA");
+ } else {
+ channel =
+ mxc_dma_request(MXC_DMA_SPDIF_16BIT_TX,
+ "SPDIF TX DMA");
+ }
+
+ } else if (s->stream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+
+ channel = mxc_dma_request(MXC_DMA_SPDIF_32BIT_RX,
+ "SPDIF RX DMA");
+
+ }
+
+ pr_debug("spdif_configure_dma_channel: %d\n", channel);
+
+ ret = mxc_dma_callback_set(channel,
+ (mxc_dma_callback_t) callback, (void *)s);
+ if (ret != 0) {
+ pr_info("spdif_configure_dma_channel - err\n");
+ mxc_dma_free(channel);
+ return -1;
+ }
+ s->dma_wchannel = channel;
+ return 0;
+}
+
+/*!
+ * This function gets the dma pointer position during playback/capture.
+ * Our DMA implementation does not allow to retrieve this position
+ * when a transfert is active, so, it answers the middle of
+ * the current period beeing transfered
+ *
+ * @param s pointer to the structure of the current stream.
+ *
+ */
+static u_int spdif_get_dma_pos(struct mxc_spdif_stream *s)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int offset = 0;
+ substream = s->stream;
+ runtime = substream->runtime;
+
+ offset = (runtime->period_size * (s->periods));
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+ pr_debug
+ ("MXC: spdif_get_dma_pos BIS offset %d, buffer_size %d\n",
+ offset, (int)runtime->buffer_size);
+ return offset;
+}
+
+/*!
+ * This function stops the current dma transfert for playback
+ * and clears the dma pointers.
+ *
+ * @param s pointer to the structure of the current stream.
+ *
+ */
+static void spdif_stop_tx(struct mxc_spdif_stream *s)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int offset;
+
+ substream = s->stream;
+ runtime = substream->runtime;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * s->periods;
+
+ s->active = 0;
+ s->period = 0;
+ s->periods = 0;
+
+ /* this stops the dma channel and clears the buffer ptrs */
+ mxc_dma_disable(s->dma_wchannel);
+ spdif_dma_enable(SCR_DMA_TX_EN, 0);
+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
+ DMA_TO_DEVICE);
+}
+
+/*!
+ * This function is called whenever a new audio block needs to be
+ * transferred to SPDIF. The function receives the address and the size
+ * of the new block and start a new DMA transfer.
+ *
+ * @param s pointer to the structure of the current stream.
+ *
+ */
+static void spdif_start_tx(struct mxc_spdif_stream *s)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size = 0;
+ unsigned int offset;
+ int ret = 0;
+ mxc_dma_requestbuf_t dma_request;
+ substream = s->stream;
+ runtime = substream->runtime;
+ memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));
+ if (s->active) {
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * s->period;
+ dma_request.src_addr =
+ (dma_addr_t) (dma_map_single
+ (NULL, runtime->dma_area + offset, dma_size,
+ DMA_TO_DEVICE));
+
+ dma_request.dst_addr = (dma_addr_t) (SPDIF_BASE_ADDR + 0x2c);
+
+ dma_request.num_of_bytes = dma_size;
+ mxc_dma_config(s->dma_wchannel, &dma_request, 1,
+ MXC_DMA_MODE_WRITE);
+ ret = mxc_dma_enable(s->dma_wchannel);
+ spdif_dma_enable(SCR_DMA_TX_EN, 1);
+ if (ret) {
+ pr_info("audio_process_dma: cannot queue DMA \
+ buffer\n");
+ return;
+ }
+ s->period++;
+ s->period %= runtime->periods;
+
+ if ((s->period > s->periods)
+ && ((s->period - s->periods) > 1)) {
+ pr_debug("audio playback chain dma: already double \
+ buffered\n");
+ return;
+ }
+
+ if ((s->period < s->periods)
+ && ((s->period + runtime->periods - s->periods) > 1)) {
+ pr_debug("audio playback chain dma: already double \
+ buffered\n");
+ return;
+ }
+
+ if (s->period == s->periods) {
+ pr_debug("audio playback chain dma: s->period == \
+ s->periods\n");
+ return;
+ }
+
+ if (snd_pcm_playback_hw_avail(runtime) <
+ 2 * runtime->period_size) {
+ pr_debug("audio playback chain dma: available data \
+ is not enough\n");
+ return;
+ }
+
+ pr_debug
+ ("audio playback chain dma:to set up the 2nd dma buffer\n");
+ pr_debug("SCR: 0x%08x\n",
+ __raw_readl(spdif_base_addr + SPDIF_REG_SCR));
+
+ offset = dma_size * s->period;
+ dma_request.src_addr =
+ (dma_addr_t) (dma_map_single
+ (NULL, runtime->dma_area + offset, dma_size,
+ DMA_TO_DEVICE));
+ mxc_dma_config(s->dma_wchannel, &dma_request, 1,
+ MXC_DMA_MODE_WRITE);
+ ret = mxc_dma_enable(s->dma_wchannel);
+ s->period++;
+ s->period %= runtime->periods;
+
+ }
+ return;
+}
+
+/*!
+ * This is a callback which will be called
+ * when a TX transfer finishes. The call occurs
+ * in interrupt context.
+ *
+ * @param data pointer to the structure of the current stream
+ * @param error DMA error flag
+ * @param count number of bytes transfered by the DMA
+ */
+static void spdif_tx_callback(void *data, int error, unsigned int count)
+{
+ struct mxc_spdif_stream *s;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int previous_period;
+ unsigned int offset;
+ s = data;
+ substream = s->stream;
+ runtime = substream->runtime;
+ previous_period = s->periods;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * previous_period;
+
+ spin_lock(&s->dma_lock);
+ s->periods++;
+ s->periods %= runtime->periods;
+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
+ DMA_TO_DEVICE);
+ spin_unlock(&s->dma_lock);
+
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+
+ spin_lock(&s->dma_lock);
+ spdif_start_tx(s);
+ spin_unlock(&s->dma_lock);
+}
+
+/*!
+ * This function is a dispatcher of command to be executed
+ * by the driver for playback.
+ *
+ * @param substream pointer to the structure of the current stream.
+ * @param cmd command to be executed
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int
+snd_mxc_spdif_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct mxc_spdif_device *chip;
+ struct mxc_spdif_stream *s;
+ int err = 0;
+ unsigned long flags;
+ chip = snd_pcm_substream_chip(substream);
+ s = &chip->s[SNDRV_PCM_STREAM_PLAYBACK];
+
+ spin_lock_irqsave(&s->dma_lock, flags);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ s->active = 1;
+ spdif_start_tx(s);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ spdif_stop_tx(s);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ s->active = 0;
+ s->periods = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ s->active = 1;
+ spdif_start_tx(s);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ s->active = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ s->active = 1;
+ spdif_start_tx(s);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ spin_unlock_irqrestore(&s->dma_lock, flags);
+ return err;
+}
+
+/*!
+ * This function configures the hardware to allow audio
+ * playback operations. It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_mxc_spdif_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct mxc_spdif_device *chip;
+ struct snd_pcm_runtime *runtime;
+ int err;
+ unsigned int ch_status;
+
+ chip = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+
+ spdif_tx_init();
+
+ ch_status =
+ ((mxc_spdif_control.ch_status[2] << 16) | (mxc_spdif_control.
+ ch_status[1] << 8) |
+ mxc_spdif_control.ch_status[0]);
+ spdif_set_channel_status(ch_status, SPDIF_REG_STCSCH);
+ ch_status = mxc_spdif_control.ch_status[3];
+ spdif_set_channel_status(ch_status, SPDIF_REG_STCSCL);
+ spdif_intr_enable(INT_TXFIFO_RESYNC, 1);
+ spdif_set_sample_rate(chip->spdif_txclk_44100, chip->spdif_txclk_48000,
+ runtime->rate);
+ spdif_set_clk_accuracy(SPDIF_CLK_ACCURACY_LEV2);
+ /* setup DMA controller for spdif tx */
+ err = spdif_configure_dma_channel(&chip->
+ s[SNDRV_PCM_STREAM_PLAYBACK],
+ spdif_tx_callback);
+ if (err < 0) {
+ pr_info("snd_mxc_spdif_playback_prepare - err < 0\n");
+ return err;
+ }
+
+ /**
+ * FIXME: dump registers
+ */
+ pr_debug("SCR: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SCR));
+ pr_debug("SIE: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SIE));
+ pr_debug("STC: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_STC));
+ return 0;
+}
+
+/*!
+ * This function gets the current playback pointer position.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static snd_pcm_uframes_t
+snd_mxc_spdif_playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct mxc_spdif_device *chip;
+ chip = snd_pcm_substream_chip(substream);
+ return spdif_get_dma_pos(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]);
+}
+
+static int snd_card_mxc_spdif_playback_open(struct snd_pcm_substream *substream)
+{
+ struct mxc_spdif_device *chip;
+ struct snd_pcm_runtime *runtime;
+ int err;
+ struct mxc_spdif_platform_data *spdif_data;
+
+ chip = snd_pcm_substream_chip(substream);
+
+ spdif_data = chip->card->dev->platform_data;
+ /* enable tx clock */
+ clk_enable(spdif_data->spdif_clk);
+ clk_enable(spdif_data->spdif_audio_clk);
+
+ runtime = substream->runtime;
+ chip->s[SNDRV_PCM_STREAM_PLAYBACK].stream = substream;
+ runtime->hw = snd_spdif_playback_hw;
+ err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &hw_playback_rates_stereo);
+ if (err < 0)
+ goto failed;
+ err =
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto failed;
+
+ return 0;
+ failed:
+ clk_disable(spdif_data->spdif_clk);
+ return err;
+}
+
+/*!
+ * This function closes an spdif device for playback.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_card_mxc_spdif_playback_close(struct snd_pcm_substream
+ *substream)
+{
+ struct mxc_spdif_device *chip;
+ struct mxc_spdif_platform_data *spdif_data;
+
+ chip = snd_pcm_substream_chip(substream);
+ spdif_data = chip->card->dev->platform_data;
+
+ pr_debug("SIS: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SIS));
+
+ spdif_intr_status();
+ spdif_intr_enable(INT_TXFIFO_RESYNC, 0);
+ spdif_tx_uninit();
+ clk_disable(spdif_data->spdif_audio_clk);
+ clk_disable(spdif_data->spdif_clk);
+ mxc_dma_free(chip->s[SNDRV_PCM_STREAM_PLAYBACK].dma_wchannel);
+ chip->s[SNDRV_PCM_STREAM_PLAYBACK].dma_wchannel = 0;
+
+ return 0;
+}
+
+/*! TODO: update the dma start/stop callback routine
+ * This function stops the current dma transfert for capture
+ * and clears the dma pointers.
+ *
+ * @param s pointer to the structure of the current stream.
+ *
+ */
+static void spdif_stop_rx(struct mxc_spdif_stream *s)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int offset;
+
+ substream = s->stream;
+ runtime = substream->runtime;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * s->periods;
+
+ s->active = 0;
+ s->period = 0;
+ s->periods = 0;
+
+ /* this stops the dma channel and clears the buffer ptrs */
+ mxc_dma_disable(s->dma_wchannel);
+ spdif_dma_enable(SCR_DMA_RX_EN, 0);
+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
+ DMA_FROM_DEVICE);
+}
+
+/*!
+ * This function is called whenever a new audio block needs to be
+ * received from SPDIF. The function receives the address and the size
+ * of the new block and start a new DMA transfer.
+ *
+ * @param s pointer to the structure of the current stream.
+ *
+ */
+static void spdif_start_rx(struct mxc_spdif_stream *s)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size = 0;
+ unsigned int offset;
+ int ret = 0;
+ mxc_dma_requestbuf_t dma_request;
+
+ substream = s->stream;
+ runtime = substream->runtime;
+ memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));
+
+ if (s->active) {
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ pr_debug("s->period (%x) runtime->periods (%d)\n",
+ s->period, runtime->periods);
+ pr_debug("runtime->period_size (%d) dma_size (%d)\n",
+ (unsigned int)runtime->period_size,
+ runtime->dma_bytes);
+
+ offset = dma_size * s->period;
+ dma_request.dst_addr =
+ (dma_addr_t) (dma_map_single
+ (NULL, runtime->dma_area + offset, dma_size,
+ DMA_FROM_DEVICE));
+
+ dma_request.src_addr =
+ (dma_addr_t) (SPDIF_BASE_ADDR + SPDIF_REG_SRL);
+ dma_request.num_of_bytes = dma_size;
+ /* config and enable sdma for RX */
+ mxc_dma_config(s->dma_wchannel, &dma_request, 1,
+ MXC_DMA_MODE_READ);
+ ret = mxc_dma_enable(s->dma_wchannel);
+ /* enable SPDIF dma */
+ spdif_dma_enable(SCR_DMA_RX_EN, 1);
+
+ if (ret) {
+ pr_info("audio_process_dma: cannot queue DMA \
+ buffer\n");
+ return;
+ }
+ s->period++;
+ s->period %= runtime->periods;
+
+ if ((s->period > s->periods)
+ && ((s->period - s->periods) > 1)) {
+ pr_debug("audio capture chain dma: already double \
+ buffered\n");
+ return;
+ }
+
+ if ((s->period < s->periods)
+ && ((s->period + runtime->periods - s->periods) > 1)) {
+ pr_debug("audio capture chain dma: already double \
+ buffered\n");
+ return;
+ }
+
+ if (s->period == s->periods) {
+ pr_debug("audio capture chain dma: s->period == \
+ s->periods\n");
+ return;
+ }
+
+ if (snd_pcm_capture_hw_avail(runtime) <
+ 2 * runtime->period_size) {
+ pr_debug("audio capture chain dma: available data \
+ is not enough\n");
+ return;
+ }
+
+ pr_debug
+ ("audio playback chain dma:to set up the 2nd dma buffer\n");
+
+ offset = dma_size * s->period;
+ dma_request.dst_addr =
+ (dma_addr_t) (dma_map_single
+ (NULL, runtime->dma_area + offset, dma_size,
+ DMA_FROM_DEVICE));
+ mxc_dma_config(s->dma_wchannel, &dma_request, 1,
+ MXC_DMA_MODE_READ);
+ ret = mxc_dma_enable(s->dma_wchannel);
+ s->period++;
+ s->period %= runtime->periods;
+
+ }
+ return;
+}
+
+/*!
+ * This is a callback which will be called
+ * when a RX transfer finishes. The call occurs
+ * in interrupt context.
+ *
+ * @param data pointer to the structure of the current stream
+ * @param error DMA error flag
+ * @param count number of bytes transfered by the DMA
+ */
+static void spdif_rx_callback(void *data, int error, unsigned int count)
+{
+ struct mxc_spdif_stream *s;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int dma_size;
+ unsigned int previous_period;
+ unsigned int offset;
+
+ s = data;
+ substream = s->stream;
+ runtime = substream->runtime;
+ previous_period = s->periods;
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * previous_period;
+
+ spin_lock(&s->dma_lock);
+ s->periods++;
+ s->periods %= runtime->periods;
+
+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
+ DMA_FROM_DEVICE);
+ spin_unlock(&s->dma_lock);
+
+ if (s->active)
+ snd_pcm_period_elapsed(s->stream);
+ spin_lock(&s->dma_lock);
+ spdif_start_rx(s);
+ spin_unlock(&s->dma_lock);
+}
+
+/*!
+ * This function is a dispatcher of command to be executed
+ * by the driver for capture.
+ *
+ * @param substream pointer to the structure of the current stream.
+ * @param cmd command to be executed
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int
+snd_mxc_spdif_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct mxc_spdif_device *chip;
+ struct mxc_spdif_stream *s;
+ int err = 0;
+ unsigned long flags;
+ chip = snd_pcm_substream_chip(substream);
+ s = &chip->s[SNDRV_PCM_STREAM_CAPTURE];
+
+ spin_lock_irqsave(&s->dma_lock, flags);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ s->active = 1;
+ spdif_start_rx(s);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ spdif_stop_rx(s);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ s->active = 0;
+ s->periods = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ s->active = 1;
+ spdif_start_rx(s);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ s->active = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ s->active = 1;
+ spdif_start_rx(s);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ spin_unlock_irqrestore(&s->dma_lock, flags);
+ return err;
+}
+
+/*!
+ * This function configures the hardware to allow audio
+ * capture operations. It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_mxc_spdif_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct mxc_spdif_device *chip;
+ struct mxc_spdif_platform_data *spdif_data;
+ struct snd_pcm_runtime *runtime;
+ int err;
+
+ chip = snd_pcm_substream_chip(substream);
+ runtime = substream->runtime;
+ spdif_data = chip->card->dev->platform_data;
+
+ spdif_rx_init();
+ /* enable interrupts, include DPLL lock */
+ spdif_intr_enable(INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL |
+ INT_URX_OV | INT_QRX_FUL | INT_QRX_OV |
+ INT_UQ_SYNC | INT_UQ_ERR | INT_RX_RESYNC |
+ INT_LOSS_LOCK, 1);
+
+ /* setup rx clock source */
+ spdif_set_rx_clksrc(spdif_data->spdif_clkid, SPDIF_DEFAULT_GAINSEL, 1);
+
+ /* setup DMA controller for spdif rx */
+ err = spdif_configure_dma_channel(&chip->
+ s[SNDRV_PCM_STREAM_CAPTURE],
+ spdif_rx_callback);
+ if (err < 0) {
+ pr_info("snd_mxc_spdif_playback_prepare - err < 0\n");
+ return err;
+ }
+
+ /* Debug: dump registers */
+ pr_debug("SCR: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SCR));
+ pr_debug("SIE: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SIE));
+ pr_debug("SRPC: 0x%08x\n",
+ __raw_readl(spdif_base_addr + SPDIF_REG_SRPC));
+ pr_debug("FreqMeas: 0x%08x\n",
+ __raw_readl(spdif_base_addr + SPDIF_REG_SRFM));
+
+ return 0;
+}
+
+/*!
+ * This function gets the current capture pointer position.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ */
+static snd_pcm_uframes_t
+snd_mxc_spdif_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct mxc_spdif_device *chip;
+ chip = snd_pcm_substream_chip(substream);
+ return spdif_get_dma_pos(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);
+}
+
+/*!
+ * This function opens a spdif device in capture mode
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_card_mxc_spdif_capture_open(struct snd_pcm_substream *substream)
+{
+ struct mxc_spdif_device *chip;
+ struct snd_pcm_runtime *runtime;
+ int err = 0;
+ struct mxc_spdif_platform_data *spdif_data;
+
+ chip = snd_pcm_substream_chip(substream);
+
+ spdif_data = chip->card->dev->platform_data;
+ /* enable rx bus clock */
+ clk_enable(spdif_data->spdif_clk);
+
+ runtime = substream->runtime;
+ chip->s[SNDRV_PCM_STREAM_CAPTURE].stream = substream;
+ runtime->hw = snd_spdif_capture_hw;
+
+ /* set hw param constraints */
+ err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &hw_capture_rates_stereo);
+ if (err < 0)
+ goto failed;
+ err =
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto failed;
+
+ /* enable spdif dpll lock interrupt */
+ spdif_intr_enable(INT_DPLL_LOCKED, 1);
+
+ return 0;
+
+ failed:
+ clk_disable(spdif_data->spdif_clk);
+ return err;
+}
+
+/*!
+ * This function closes an spdif device for capture.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_card_mxc_spdif_capture_close(struct snd_pcm_substream
+ *substream)
+{
+ struct mxc_spdif_device *chip;
+ struct mxc_spdif_platform_data *spdif_data;
+
+ chip = snd_pcm_substream_chip(substream);
+ spdif_data = chip->card->dev->platform_data;
+
+ pr_debug("SIS: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SIS));
+ pr_debug("SRPC: 0x%08x\n",
+ __raw_readl(spdif_base_addr + SPDIF_REG_SRPC));
+ pr_debug("FreqMeas: 0x%08x\n",
+ __raw_readl(spdif_base_addr + SPDIF_REG_SRFM));
+
+ spdif_intr_enable(INT_DPLL_LOCKED | INT_SYM_ERR | INT_BIT_ERR |
+ INT_URX_FUL | INT_URX_OV | INT_QRX_FUL | INT_QRX_OV |
+ INT_UQ_SYNC | INT_UQ_ERR | INT_RX_RESYNC |
+ INT_LOSS_LOCK, 0);
+ spdif_rx_uninit();
+ clk_disable(spdif_data->spdif_clk);
+ mxc_dma_free(chip->s[SNDRV_PCM_STREAM_CAPTURE].dma_wchannel);
+ chip->s[SNDRV_PCM_STREAM_CAPTURE].dma_wchannel = 0;
+ return 0;
+}
+
+/*!
+ * This function configure the Audio HW in terms of memory allocation.
+ * It is called by ALSA framework.
+ *
+ * @param substream pointer to the structure of the current stream.
+ * @param hw_params Pointer to hardware paramters structure
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_mxc_spdif_hw_params(struct snd_pcm_substream
+ *substream, struct snd_pcm_hw_params
+ *hw_params)
+{
+ struct snd_pcm_runtime *runtime;
+ int ret = 0;
+ runtime = substream->runtime;
+ ret =
+ snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (ret < 0) {
+ pr_info("snd_mxc_spdif_hw_params - ret: %d\n", ret);
+ return ret;
+ }
+ runtime->dma_addr = virt_to_phys(runtime->dma_area);
+ return ret;
+}
+
+/*!
+ * This function frees the spdif hardware at the end of playback.
+ *
+ * @param substream pointer to the structure of the current stream.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_mxc_spdif_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+/*!
+ * This structure is the list of operation that the driver
+ * must provide for the playback interface
+ */
+static struct snd_pcm_ops snd_card_mxc_spdif_playback_ops = {
+ .open = snd_card_mxc_spdif_playback_open,
+ .close = snd_card_mxc_spdif_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_mxc_spdif_hw_params,
+ .hw_free = snd_mxc_spdif_hw_free,
+ .prepare = snd_mxc_spdif_playback_prepare,
+ .trigger = snd_mxc_spdif_playback_trigger,
+ .pointer = snd_mxc_spdif_playback_pointer,
+};
+
+static struct snd_pcm_ops snd_card_mxc_spdif_capture_ops = {
+ .open = snd_card_mxc_spdif_capture_open,
+ .close = snd_card_mxc_spdif_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_mxc_spdif_hw_params,
+ .hw_free = snd_mxc_spdif_hw_free,
+ .prepare = snd_mxc_spdif_capture_prepare,
+ .trigger = snd_mxc_spdif_capture_trigger,
+ .pointer = snd_mxc_spdif_capture_pointer,
+};
+
+/*!
+ * This functions initializes the playback audio device supported by
+ * spdif
+ *
+ * @param mxc_spdif pointer to the sound card structure.
+ *
+ */
+void mxc_init_spdif_device(struct mxc_spdif_device *mxc_spdif)
+{
+
+ /* initial spinlock for control data */
+ spin_lock_init(&mxc_spdif_control.ctl_lock);
+
+ if (mxc_spdif->mxc_spdif_tx) {
+
+ mxc_spdif->s[SNDRV_PCM_STREAM_PLAYBACK].id = "spdif tx";
+ /* init tx channel status default value */
+ mxc_spdif_control.ch_status[0] =
+ IEC958_AES0_CON_NOT_COPYRIGHT |
+ IEC958_AES0_CON_EMPHASIS_5015;
+ mxc_spdif_control.ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID;
+ mxc_spdif_control.ch_status[2] = 0x00;
+ mxc_spdif_control.ch_status[3] =
+ IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM;
+ }
+ if (mxc_spdif->mxc_spdif_rx) {
+
+ /* TODO: Add code here if capture is available */
+ mxc_spdif->s[SNDRV_PCM_STREAM_CAPTURE].id = "spdif rx";
+ }
+
+}
+
+/*!
+ * MXC SPDIF IEC958 controller(mixer) functions
+ *
+ * Channel status get/put control
+ * User bit value get/put control
+ * Valid bit value get control
+ * DPLL lock status get control
+ * User bit sync mode selection control
+ *
+ */
+static int mxc_pb_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int mxc_pb_spdif_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ uvalue->value.iec958.status[0] = mxc_spdif_control.ch_status[0];
+ uvalue->value.iec958.status[1] = mxc_spdif_control.ch_status[1];
+ uvalue->value.iec958.status[2] = mxc_spdif_control.ch_status[2];
+ uvalue->value.iec958.status[3] = mxc_spdif_control.ch_status[3];
+ return 0;
+}
+
+static int mxc_pb_spdif_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ unsigned int ch_status;
+ mxc_spdif_control.ch_status[0] = uvalue->value.iec958.status[0];
+ mxc_spdif_control.ch_status[1] = uvalue->value.iec958.status[1];
+ mxc_spdif_control.ch_status[2] = uvalue->value.iec958.status[2];
+ mxc_spdif_control.ch_status[3] = uvalue->value.iec958.status[3];
+ ch_status =
+ ((mxc_spdif_control.ch_status[2] << 16) | (mxc_spdif_control.
+ ch_status[1] << 8) |
+ mxc_spdif_control.ch_status[0]);
+ spdif_set_channel_status(ch_status, SPDIF_REG_STCSCH);
+ ch_status = mxc_spdif_control.ch_status[3];
+ spdif_set_channel_status(ch_status, SPDIF_REG_STCSCL);
+ return 0;
+}
+
+static int snd_mxc_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+/*!
+ * Get channel status from SPDIF_RX_CCHAN register
+ */
+static int snd_mxc_spdif_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int cstatus;
+
+ if (!(__raw_readl(spdif_base_addr + SPDIF_REG_SIS) & INT_CNEW))
+ return -EAGAIN;
+
+ cstatus = __raw_readl(spdif_base_addr + SPDIF_REG_SRCSLH);
+ ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF;
+ ucontrol->value.iec958.status[1] = (cstatus >> 8) & 0xFF;
+ ucontrol->value.iec958.status[2] = cstatus & 0xFF;
+ cstatus = __raw_readl(spdif_base_addr + SPDIF_REG_SRCSLL);
+ ucontrol->value.iec958.status[3] = (cstatus >> 16) & 0xFF;
+ ucontrol->value.iec958.status[4] = (cstatus >> 8) & 0xFF;
+ ucontrol->value.iec958.status[5] = cstatus & 0xFF;
+
+ /* clear intr */
+ __raw_writel(INT_CNEW, spdif_base_addr + SPDIF_REG_SIC);
+
+ return 0;
+}
+
+/*!
+ * Get User bits (subcode) from chip value which readed out
+ * in UChannel register.
+ */
+static int snd_mxc_spdif_subcode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&mxc_spdif_control.ctl_lock, flags);
+ if (mxc_spdif_control.ready_buf) {
+ memcpy(&ucontrol->value.iec958.subcode[0],
+ &mxc_spdif_control.
+ subcode[(mxc_spdif_control.ready_buf -
+ 1) * SPDIF_UBITS_SIZE], SPDIF_UBITS_SIZE);
+ } else {
+ ret = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&mxc_spdif_control.ctl_lock, flags);
+
+ return ret;
+}
+
+/*!
+ * Q-subcode infomation.
+ * the byte size is SPDIF_UBITS_SIZE/8
+ */
+static int snd_mxc_spdif_qinfo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = SPDIF_QSUB_SIZE;
+ return 0;
+}
+
+/*!
+ * Get Q subcode from chip value which readed out
+ * in QChannel register.
+ */
+static int snd_mxc_spdif_qget(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&mxc_spdif_control.ctl_lock, flags);
+ if (mxc_spdif_control.ready_buf) {
+ memcpy(&ucontrol->value.bytes.data[0],
+ &mxc_spdif_control.
+ qsub[(mxc_spdif_control.ready_buf -
+ 1) * SPDIF_QSUB_SIZE], SPDIF_QSUB_SIZE);
+ } else {
+ ret = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&mxc_spdif_control.ctl_lock, flags);
+
+ return ret;
+}
+
+/*!
+ * Valid bit infomation.
+ */
+static int snd_mxc_spdif_vbit_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+/*!
+ * Get valid good bit from interrupt status register.
+ */
+static int snd_mxc_spdif_vbit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int int_val;
+
+ int_val = __raw_readl(spdif_base_addr + SPDIF_REG_SIS);
+ ucontrol->value.integer.value[0] = (int_val & INT_VAL_NOGOOD) != 0;
+ __raw_writel(INT_VAL_NOGOOD, spdif_base_addr + SPDIF_REG_SIC);
+
+ return 0;
+}
+
+/*!
+ * DPLL lock infomation.
+ */
+static int snd_mxc_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 16000;
+ uinfo->value.integer.max = 96000;
+ return 0;
+}
+
+/*!
+ * Get DPLL lock or not info from stable interrupt status register.
+ * User application must use this control to get locked,
+ * then can do next PCM operation
+ */
+static int snd_mxc_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct mxc_spdif_device *chip = snd_kcontrol_chip(kcontrol);
+ struct mxc_spdif_platform_data *spdif_data;
+
+ spdif_data = chip->card->dev->platform_data;
+
+ if (atomic_read(&chip->dpll_locked)) {
+ ucontrol->value.integer.value[0] =
+ spdif_get_rxclk_rate(spdif_data->spdif_clk,
+ SPDIF_DEFAULT_GAINSEL);
+ } else {
+ ucontrol->value.integer.value[0] = 0;
+ }
+ return 0;
+}
+
+/*!
+ * User bit sync mode info
+ */
+static int snd_mxc_spdif_usync_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+/*!
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int snd_mxc_spdif_usync_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int int_val;
+
+ int_val = __raw_readl(spdif_base_addr + SPDIF_REG_SRCD);
+ ucontrol->value.integer.value[0] = (int_val & SRCD_CD_USER) != 0;
+ return 0;
+}
+
+/*!
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int snd_mxc_spdif_usync_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int int_val;
+
+ int_val = ucontrol->value.integer.value[0] << SRCD_CD_USER_OFFSET;
+ __raw_writel(int_val, spdif_base_addr + SPDIF_REG_SRCD);
+ return 0;
+}
+
+/*!
+ * MXC SPDIF IEC958 controller defines
+ */
+static struct snd_kcontrol_new snd_mxc_spdif_ctrls[] = {
+ /* status cchanel controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = mxc_pb_spdif_info,
+ .get = mxc_pb_spdif_get,
+ .put = mxc_pb_spdif_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_mxc_spdif_info,
+ .get = snd_mxc_spdif_capture_get,
+ },
+ /* user bits controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_mxc_spdif_info,
+ .get = snd_mxc_spdif_subcode_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Q-subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_mxc_spdif_qinfo,
+ .get = snd_mxc_spdif_qget,
+ },
+ /* valid bit error controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 V-Bit Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_mxc_spdif_vbit_info,
+ .get = snd_mxc_spdif_vbit_get,
+ },
+ /* DPLL lock info get controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "RX Sample Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_mxc_spdif_rxrate_info,
+ .get = snd_mxc_spdif_rxrate_get,
+ },
+ /* User bit sync mode set/get controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 USyncMode CDText",
+ .access =
+ SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_mxc_spdif_usync_info,
+ .get = snd_mxc_spdif_usync_get,
+ .put = snd_mxc_spdif_usync_put,
+ },
+};
+
+/*!
+ * This function the soundcard structure.
+ *
+ * @param mxc_spdif pointer to the sound card structure.
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int snd_card_mxc_spdif_pcm(struct mxc_spdif_device *mxc_spdif)
+{
+ struct snd_pcm *pcm;
+ int err;
+ err = snd_pcm_new(mxc_spdif->card, MXC_SPDIF_NAME, 0,
+ mxc_spdif->mxc_spdif_tx,
+ mxc_spdif->mxc_spdif_rx, &pcm);
+ if (err < 0)
+ return err;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data
+ (GFP_KERNEL),
+ SPDIF_MAX_BUF_SIZE * 2,
+ SPDIF_MAX_BUF_SIZE * 2);
+ if (mxc_spdif->mxc_spdif_tx)
+ snd_pcm_set_ops(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_card_mxc_spdif_playback_ops);
+ if (mxc_spdif->mxc_spdif_rx)
+ snd_pcm_set_ops(pcm,
+ SNDRV_PCM_STREAM_CAPTURE,
+ &snd_card_mxc_spdif_capture_ops);
+ pcm->private_data = mxc_spdif;
+ pcm->info_flags = 0;
+ strncpy(pcm->name, MXC_SPDIF_NAME, sizeof(pcm->name));
+ mxc_spdif->pcm = pcm;
+ mxc_init_spdif_device(mxc_spdif);
+ return 0;
+}
+
+extern void gpio_spdif_active(void);
+
+/*!
+ * This function initializes the driver in terms of memory of the soundcard
+ * and some basic HW clock settings.
+ *
+ * @param pdev Pointer to the platform device
+ * @return 0 on success, -1 otherwise.
+ */
+static int mxc_alsa_spdif_probe(struct platform_device
+ *pdev)
+{
+ int err, idx;
+ static int dev;
+ struct snd_card *card;
+ struct mxc_spdif_device *chip;
+ struct resource *res;
+ struct snd_kcontrol *kctl;
+ struct mxc_spdif_platform_data *plat_data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ /* register the soundcard */
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct mxc_spdif_device));
+ if (card == NULL)
+ return -ENOMEM;
+ chip = card->private_data;
+ chip->card = card;
+ card->dev = &pdev->dev;
+ chip->reg_base = ioremap(res->start, res->end - res->start + 1);
+ spdif_base_addr = (unsigned long)chip->reg_base;
+ plat_data = (struct mxc_spdif_platform_data *)pdev->dev.platform_data;
+ chip->mxc_spdif_tx = plat_data->spdif_tx;
+ chip->mxc_spdif_rx = plat_data->spdif_rx;
+ chip->spdif_txclk_44100 = plat_data->spdif_clk_44100;
+ chip->spdif_txclk_48000 = plat_data->spdif_clk_48000;
+ atomic_set(&chip->dpll_locked, 0);
+
+ err = snd_card_mxc_spdif_pcm(chip);
+ if (err < 0)
+ goto nodev;
+
+ /*!
+ * Add controls to the card
+ */
+ for (idx = 0; idx < ARRAY_SIZE(snd_mxc_spdif_ctrls); idx++) {
+
+ kctl = snd_ctl_new1(&snd_mxc_spdif_ctrls[idx], chip);
+ if (kctl == NULL) {
+ err = -ENOMEM;
+ goto nodev;
+ }
+ /* check to add control to corresponding substream */
+ if (strstr(kctl->id.name, "Playback"))
+ kctl->id.device = 0;
+ else
+ kctl->id.device = 1;
+
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ goto nodev;
+ }
+
+ clk_enable(plat_data->spdif_core_clk);
+ /*!
+ * SPDIF interrupt initialization
+ * software reset to SPDIF
+ */
+ spdif_softreset();
+ /* disable all the interrupts */
+ spdif_intr_enable(0xffffff, 0);
+ /* spdif interrupt register and disable */
+ if (request_irq(MXC_INT_SPDIF, spdif_isr, 0, "spdif", chip)) {
+ pr_err("MXC spdif: failed to request irq\n");
+ err = -EBUSY;
+ goto nodev;
+ }
+
+ if (chip->mxc_spdif_tx)
+ spin_lock_init(&chip->s[SNDRV_PCM_STREAM_PLAYBACK].dma_lock);
+ if (chip->mxc_spdif_rx)
+ spin_lock_init(&chip->s[SNDRV_PCM_STREAM_CAPTURE].dma_lock);
+ strcpy(card->driver, MXC_SPDIF_NAME);
+ strcpy(card->shortname, "MXC SPDIF TX/RX");
+ sprintf(card->longname, "MXC Freescale with SPDIF");
+
+ err = snd_card_register(card);
+ if (err == 0) {
+ pr_info("MXC spdif support initialized\n");
+ platform_set_drvdata(pdev, card);
+ gpio_spdif_active();
+ return 0;
+ }
+
+ nodev:
+ snd_card_free(card);
+ return err;
+}
+
+extern void gpio_spdif_inactive(void);
+
+/*!
+ * This function releases the sound card and unmap the io address
+ *
+ * @param pdev Pointer to the platform device
+ * @return 0 on success, -1 otherwise.
+ */
+
+static int mxc_alsa_spdif_remove(struct platform_device *pdev)
+{
+ struct mxc_spdif_device *chip;
+ struct snd_card *card;
+ struct mxc_spdif_platform_data *plat_data;
+
+ card = platform_get_drvdata(pdev);
+ plat_data = pdev->dev.platform_data;
+ chip = card->private_data;
+ free_irq(MXC_INT_SPDIF, chip);
+ iounmap(chip->reg_base);
+
+ snd_card_free(card);
+ platform_set_drvdata(pdev, NULL);
+
+ clk_disable(plat_data->spdif_core_clk);
+ gpio_spdif_inactive();
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function suspends all active streams.
+ *
+ * TBD
+ *
+ * @param card pointer to the sound card structure.
+ * @param state requested state
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int mxc_alsa_spdif_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+/*!
+ * This function resumes all suspended streams.
+ *
+ * TBD
+ *
+ * @param card pointer to the sound card structure.
+ * @param state requested state
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+static int mxc_alsa_spdif_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
+static struct platform_driver mxc_alsa_spdif_driver = {
+ .probe = mxc_alsa_spdif_probe,
+ .remove = mxc_alsa_spdif_remove,
+#ifdef CONFIG_PM
+ .suspend = mxc_alsa_spdif_suspend,
+ .resume = mxc_alsa_spdif_resume,
+#endif
+ .driver = {
+ .name = "mxc_alsa_spdif",
+ },
+};
+
+/*!
+ * This function registers the sound driver structure.
+ *
+ */
+static int __init mxc_alsa_spdif_init(void)
+{
+ return platform_driver_register(&mxc_alsa_spdif_driver);
+}
+
+/*!
+ * This function frees the sound driver structure.
+ *
+ */
+static void __exit mxc_alsa_spdif_exit(void)
+{
+ platform_driver_unregister(&mxc_alsa_spdif_driver);
+}
+
+module_init(mxc_alsa_spdif_init);
+module_exit(mxc_alsa_spdif_exit);
+MODULE_AUTHOR("FREESCALE SEMICONDUCTOR");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MXC ALSA driver for SPDIF");
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 4dfda6674bec..1e48ea18e5f8 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -10,7 +10,7 @@ menuconfig SND_SOC
If you want ASoC support, you should say Y here and also to the
specific driver for your SoC platform below.
-
+
ASoC provides power efficient ALSA support for embedded battery powered
SoC based systems like PDA's, Phones and Personal Media Players.
@@ -29,6 +29,8 @@ source "sound/soc/au1x/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/sh/Kconfig"
+source "sound/soc/imx/Kconfig"
+source "sound/soc/stmp3xxx/Kconfig"
source "sound/soc/fsl/Kconfig"
source "sound/soc/davinci/Kconfig"
source "sound/soc/omap/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index d849349f2c66..d997c29d2d8a 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,5 +1,5 @@
snd-soc-core-objs := soc-core.o soc-dapm.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
-obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
+obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ imx/ stmp3xxx/
obj-$(CONFIG_SND_SOC) += omap/ au1x/ blackfin/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 38a0e3b620a7..adf30811bb78 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -5,12 +5,14 @@ config SND_SOC_ALL_CODECS
select SPI_MASTER
select SND_SOC_AD73311
select SND_SOC_AK4535
+ select SND_SOC_AK5702
select SND_SOC_CS4270
select SND_SOC_SSM2602
select SND_SOC_TLV320AIC23
select SND_SOC_TLV320AIC26
select SND_SOC_TLV320AIC3X
select SND_SOC_UDA1380
+ select SND_SOC_WM8350
select SND_SOC_WM8510
select SND_SOC_WM8580
select SND_SOC_WM8731
@@ -42,6 +44,10 @@ config SND_SOC_AD73311
config SND_SOC_AK4535
tristate
+config SND_SOC_AK5702
+ tristate
+ depends on I2C
+
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate
@@ -78,6 +84,9 @@ config SND_SOC_TLV320AIC3X
config SND_SOC_UDA1380
tristate
+config SND_SOC_WM8350
+ tristate
+
config SND_SOC_WM8510
tristate
@@ -110,3 +119,22 @@ config SND_SOC_WM9712
config SND_SOC_WM9713
tristate
+
+config SND_SOC_SGTL5000
+ tristate
+ depends on I2C
+
+config SND_SOC_AK4647
+ tristate
+ depends on I2C
+
+config SND_SOC_STMP378X_CODEC
+ tristate
+ depends on SND_SOC
+
+config SND_SOC_STMP3XXX_SPDIF
+ tristate
+ depends on SND_SOC
+
+config SND_SOC_BLUETOOTH
+ tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 90f0a585fc70..b6eb70ba7533 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -2,12 +2,14 @@ snd-soc-ac97-objs := ac97.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-ak4535-objs := ak4535.o
+snd-soc-ak5702-objs := ak5702.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-ssm2602-objs := ssm2602.o
snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
snd-soc-uda1380-objs := uda1380.o
+snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8580-objs := wm8580.o
snd-soc-wm8731-objs := wm8731.o
@@ -19,17 +21,24 @@ snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
+snd-soc-sgtl5000-objs := sgtl5000.o
+snd-soc-ak4647-objs := ak4647.o
+snd-soc-stmp378x-codec-objs := stmp378x_codec.o
+snd-soc-stmp3xxx-spdif-objs := stmp3xxx_spdif.o
+snd-soc-bluetooth-objs := bluetooth.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+obj-$(CONFIG_SND_SOC_AK5702) += snd-soc-ak5702.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
@@ -41,3 +50,8 @@ obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
+obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
+obj-$(CONFIG_SND_SOC_AK4647) += snd-soc-ak4647.o
+obj-$(CONFIG_SND_SOC_STMP378X_CODEC) += snd-soc-stmp378x-codec.o
+obj-$(CONFIG_SND_SOC_STMP3XXX_SPDIF) += snd-soc-stmp3xxx-spdif.o
+obj-$(CONFIG_SND_SOC_BLUETOOTH) += snd-soc-bluetooth.o
diff --git a/sound/soc/codecs/ak4647.c b/sound/soc/codecs/ak4647.c
new file mode 100644
index 000000000000..20d74d185276
--- /dev/null
+++ b/sound/soc/codecs/ak4647.c
@@ -0,0 +1,789 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ak4647.c
+ * @brief Driver for AK4647
+ *
+ * @ingroup Sound
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "ak4647.h"
+
+#define SET_BIT_IN_BYTE(byte, pos) (byte |= (0x01 << pos))
+#define CLEAR_BIT_IN_BYTE(byte, pos) (byte &= ~(0x01 << pos))
+
+static struct i2c_client *ak4647_i2c_client;
+
+int ak4647_read_reg(unsigned int reg, u8 *value)
+{
+ s32 retval;
+ retval = i2c_smbus_read_byte_data(ak4647_i2c_client, reg);
+ if (-1 == retval) {
+ pr_err("%s:read reg errorr:reg=%x,val=%x\n",
+ __func__, reg, *value);
+ return -1;
+ } else {
+ *value = (u8) retval;
+ return 0;
+ }
+}
+
+int ak4647_write_reg(unsigned int reg, u8 value)
+{
+ if (i2c_smbus_write_byte_data(ak4647_i2c_client, reg, value) < 0) {
+ pr_err("%s:write reg errorr:reg=%x,val=%x\n",
+ __func__, reg, value);
+ return -1;
+ }
+ return 0;
+}
+
+static unsigned int ak4647_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 value;
+ ak4647_read_reg(reg, &value);
+ return value;
+}
+
+static int ak4647_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ return ak4647_write_reg(reg, value);
+}
+
+#define DEBUG_AK4647 0
+
+#if DEBUG_AK4647
+
+static char *ak4647_reg_names[] = {
+ "AK4647_PM1",
+ "AK4647_PM2",
+ "AK4647_SIG1",
+ "AK4647_SIG2",
+ "AK4647_MODE1",
+ "AK4647_MODE2",
+ "AK4647_TIMER",
+ "AK4647_ALC1",
+ "AK4647_ALC2",
+ "AK4647_LEFT_INPUT_VOLUME",
+ "AK4647_LEFT_DGT_VOLUME",
+ "AK4647_ALC3",
+ "AK4647_RIGHT_INPUT_VOLUME",
+ "AK4647_RIGHT_DGT_VOLUME",
+ "AK4647_MODE3",
+ "AK4647_MODE4",
+ "AK4647_PM3",
+ "AK4647_DGT_FIL_SEL",
+ "AK4647_FIL3_COEF0",
+ "AK4647_FIL3_COEF1",
+ "AK4647_FIL3_COEF2",
+ "AK4647_FIL3_COEF3",
+ "AK4647_EQ_COEF0",
+ "AK4647_EQ_COEF1",
+ "AK4647_EQ_COEF2",
+ "AK4647_EQ_COEF3",
+ "AK4647_EQ_COEF4",
+ "AK4647_EQ_COEF5",
+ "AK4647_FIL1_COEF0",
+ "AK4647_FIL1_COEF1",
+ "AK4647_FIL1_COEF2",
+ "AK4647_FIL1_COEF3",
+};
+
+static void dump_all_regs(void)
+{
+ int i;
+ u8 value;
+
+ for (i = AK4647_REG_START; i < AK4647_REG_NUMBER; i++) {
+ ak4647_read_reg(i, &value);
+ pr_info("%s = 0x%x\n", ak4647_reg_names[i], value);
+ }
+}
+#endif
+
+static int loopback_xhandle_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int loopback_xhandle_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static const char *ak4647_hp_out[] = { "Stereo", "Mono" };
+
+static const char *ak4647_left_in[] = { "LIN1", "LIN2" };
+
+static const char *ak4647_right_in[] = { "RIN1", "RIN2" };
+
+static const char *ak4647_deemp[] = { "44.1kHz", "Off", "48kHz", "32kHz" };
+
+static const struct soc_enum ak4647_enum[] = {
+ SOC_ENUM_SINGLE(AK4647_MODE4, 2, 2, ak4647_hp_out),
+ SOC_ENUM_SINGLE(AK4647_MODE3, 0, 4, ak4647_deemp),
+ SOC_ENUM_SINGLE(AK4647_PM3, 1, 2, ak4647_left_in),
+ SOC_ENUM_SINGLE(AK4647_PM3, 2, 2, ak4647_right_in),
+};
+
+#undef snd_soc_info_bool_ext
+#define snd_soc_info_bool_ext snd_ctl_boolean_mono_info
+static const struct snd_kcontrol_new ak4647_snd_controls[] = {
+ SOC_ENUM("Headphone Output", ak4647_enum[0]),
+ SOC_ENUM("Playback Deemphasis", ak4647_enum[1]),
+ SOC_ENUM("Left Capture Select", ak4647_enum[2]),
+ SOC_ENUM("Right Capture Select", ak4647_enum[3]),
+ SOC_SINGLE("Bass Volume", AK4647_MODE3, 2, 3, 0),
+ SOC_SINGLE("Mic Boost (+20dB) Switch", AK4647_SIG1, 0, 1, 0),
+ SOC_SINGLE("Mic Bias", AK4647_SIG1, 2, 1, 0),
+ SOC_SINGLE("ALC Switch", AK4647_ALC1, 5, 1, 0),
+ SOC_SINGLE("ALC Recovery Time", AK4647_TIMER, 2, 3, 0),
+ SOC_SINGLE("ALC ZC Time", AK4647_TIMER, 4, 3, 0),
+ SOC_SINGLE("ALC Volume", AK4647_ALC2, 0, 127, 0),
+ SOC_SINGLE("Left Capture Volume", AK4647_LEFT_INPUT_VOLUME, 0, 242, 0),
+ SOC_SINGLE("Right Capture Volume",
+ AK4647_RIGHT_INPUT_VOLUME, 0, 242, 0),
+ SOC_SINGLE("Left Playback Volume", AK4647_LEFT_DGT_VOLUME, 0, 255, 1),
+ SOC_SINGLE("Right Playback Volume", AK4647_RIGHT_DGT_VOLUME, 0, 255, 1),
+ SOC_SINGLE_BOOL_EXT("Loopback Line-in", 0,
+ loopback_xhandle_get, loopback_xhandle_put),
+};
+
+/* add non dapm controls */
+static int ak4647_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(ak4647_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&ak4647_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* Stereo Mixer for HP*/
+static const struct snd_kcontrol_new ak4647_hp_stereo_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch", AK4647_MODE4, 0, 1, 0),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4647_MODE4, 1, 1, 0),
+};
+
+/* Stereo Mixer for Line out*/
+static const struct snd_kcontrol_new ak4647_line_stereo_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch", AK4647_SIG1, 4, 1, 0),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4647_SIG2, 2, 1, 0),
+};
+
+/* ak4647 dapm widgets */
+static const struct snd_soc_dapm_widget ak4647_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Headphone Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4647_hp_stereo_mixer_controls[0],
+ ARRAY_SIZE(ak4647_hp_stereo_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Lineout Mixer", SND_SOC_NOPM, 0, 0,
+ &ak4647_line_stereo_mixer_controls[0],
+ ARRAY_SIZE(ak4647_line_stereo_mixer_controls)),
+ SND_SOC_DAPM_DAC("DAC", "Playback", AK4647_PM1, 2, 0),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+
+ SND_SOC_DAPM_ADC("Left ADC", "Capture", AK4647_PM1, 0, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Capture", AK4647_PM3, 0, 0),
+ SND_SOC_DAPM_PGA("HP R Amp", AK4647_PM2, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP L Amp", AK4647_PM2, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic", AK4647_PM1, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Line Out Amp", AK4647_PM1, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", AK4647_SIG1, 2, 0),
+ SND_SOC_DAPM_INPUT("Left Input"),
+ SND_SOC_DAPM_INPUT("Right Input"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /*headphone stereo mixer */
+ {"Headphone Mixer", "Playback Switch", "DAC"},
+ {"Headphone Mixer", "Mic Sidetone Switch", "Mic"},
+
+ /*lineout stereo mixer */
+ {"Lineout Mixer", "Playback Switch", "DAC"},
+ {"Lineout Mixer", "Mic Sidetone Switch", "Mic"},
+
+ /* headphone amp */
+ {"HP R Amp", NULL, "Headphone Mixer"},
+ {"HP L Amp", NULL, "Headphone Mixer"},
+
+ /* headphone */
+ {"HPR", NULL, "HP R Amp"},
+ {"HPL", NULL, "HP L Amp"},
+
+ /* line out */
+ {"Line Out Amp", NULL, "Lineout Mixer"},
+ {"LOUT", NULL, "Line Out Amp"},
+
+ /* ADC */
+ {"Left ADC", NULL, "Left Input"},
+ {"Right ADC", NULL, "Right Input"},
+
+};
+
+static int ak4647_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, ak4647_dapm_widgets,
+ ARRAY_SIZE(ak4647_dapm_widgets));
+
+ /* set up audio path audio_mapnects */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+#define AK4647_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_48000)
+
+#define AK4647_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+static int ak4647_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ u8 value;
+ u8 fs = 0;
+
+ /* FS3 is on D5 */
+ switch (freq) {
+ case 8000:
+ fs = 0x0;
+ break;
+ case 11025:
+ fs = 0x5;
+ break;
+ case 16000:
+ fs = 0x2;
+ break;
+ case 22050:
+ fs = 0x7;
+ break;
+ case 32000:
+ fs = 0x22;
+ break;
+ case 44100:
+ fs = 0x27;
+ break;
+ case 48000:
+ fs = 0x23;
+ break;
+ default:
+ pr_err("unsupported sample rate");
+ return -1;
+ }
+
+ ak4647_read_reg(AK4647_MODE2, &value);
+ value &= 0xC0;
+ value |= fs;
+ ak4647_write_reg(AK4647_MODE2, value);
+ return 0;
+}
+
+static int ak4647_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ u8 reg_mode1, reg_pm2;
+
+ /* get current values */
+ ak4647_read_reg(AK4647_MODE1, &reg_mode1);
+ ak4647_read_reg(AK4647_PM2, &reg_pm2);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ SET_BIT_IN_BYTE(reg_pm2, 3);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ CLEAR_BIT_IN_BYTE(reg_pm2, 3);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ SET_BIT_IN_BYTE(reg_mode1, 0);
+ SET_BIT_IN_BYTE(reg_mode1, 1);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ SET_BIT_IN_BYTE(reg_mode1, 0);
+ CLEAR_BIT_IN_BYTE(reg_mode1, 1);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ CLEAR_BIT_IN_BYTE(reg_mode1, 0);
+ SET_BIT_IN_BYTE(reg_mode1, 1);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ default:
+ pr_err("dai format %d not supported", fmt);
+ return -EINVAL;
+ }
+
+ ak4647_write_reg(AK4647_MODE1, reg_mode1);
+ ak4647_write_reg(AK4647_PM2, reg_pm2);
+ return 0;
+}
+
+static int ak4647_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
+{
+ int retval = 0;
+ u8 value;
+ if (AK4647_BCLK_CLKDIV == div_id) {
+ ak4647_read_reg(AK4647_MODE1, &value);
+ switch (div) {
+ case AK4647_BCLK_DIV_32:
+ CLEAR_BIT_IN_BYTE(value, 3);
+ ak4647_write_reg(AK4647_MODE1, value);
+ break;
+ case AK4647_BCLK_DIV_64:
+ SET_BIT_IN_BYTE(value, 3);
+ ak4647_write_reg(AK4647_MODE1, value);
+ break;
+ default:
+ retval = -1;
+ pr_err("wrong div value for divid %d", div_id);
+ break;
+ }
+ } else if (AK4647_MCLK_CLKDIV == div_id) {
+ ak4647_read_reg(AK4647_MODE2, &value);
+ switch (div) {
+ case AK4647_MCLK_DIV_32:
+ SET_BIT_IN_BYTE(value, 7);
+ SET_BIT_IN_BYTE(value, 6);
+ ak4647_write_reg(AK4647_MODE2, value);
+ break;
+ case AK4647_MCLK_DIV_64:
+ SET_BIT_IN_BYTE(value, 7);
+ CLEAR_BIT_IN_BYTE(value, 6);
+ ak4647_write_reg(AK4647_MODE2, value);
+ break;
+ case AK4647_MCLK_DIV_128:
+ CLEAR_BIT_IN_BYTE(value, 7);
+ SET_BIT_IN_BYTE(value, 6);
+ ak4647_write_reg(AK4647_MODE2, value);
+ break;
+ case AK4647_MCLK_DIV_256:
+ CLEAR_BIT_IN_BYTE(value, 7);
+ CLEAR_BIT_IN_BYTE(value, 6);
+ ak4647_write_reg(AK4647_MODE2, value);
+ break;
+ default:
+ retval = -1;
+ pr_err("wrong div value for div id %d", div_id);
+ break;
+ }
+ } else {
+ retval = -1;
+ pr_err("wrong div id");
+ }
+
+ return retval;
+}
+
+static int ak4647_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ u8 value;
+
+ ak4647_read_reg(AK4647_MODE3, &value);
+
+ if (mute)
+ SET_BIT_IN_BYTE(value, 5);
+ else
+ CLEAR_BIT_IN_BYTE(value, 5);
+ ak4647_write_reg(AK4647_MODE3, value);
+ return 0;
+}
+
+struct ak4647_state_data {
+ u8 left_capture_vol;
+ u8 right_capture_vol;
+};
+static struct ak4647_state_data ak4647_state;
+
+static int ak4647_pcm_startup(struct snd_pcm_substream *substream)
+{
+ u8 value;
+ /* for playback, save down capture volume */
+ if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
+ ak4647_read_reg(AK4647_LEFT_INPUT_VOLUME, &value);
+ ak4647_state.left_capture_vol = value;
+ ak4647_read_reg(AK4647_RIGHT_INPUT_VOLUME, &value);
+ ak4647_state.right_capture_vol = value;
+
+ /* when PMADL=PMADR=0 set IVL &IVR to be 0x91 (0db) */
+ ak4647_write_reg(AK4647_LEFT_INPUT_VOLUME, 0x91);
+ ak4647_write_reg(AK4647_RIGHT_INPUT_VOLUME, 0x91);
+ }
+
+ /* output digital volume independent */
+ ak4647_read_reg(AK4647_MODE3, &value);
+ CLEAR_BIT_IN_BYTE(value, 4);
+ ak4647_write_reg(AK4647_MODE3, value);
+ return 0;
+}
+
+static void ak4647_pcm_shutdown(struct snd_pcm_substream *substream)
+{
+ u8 value;
+ ak4647_read_reg(AK4647_PM2, &value);
+ /* mute */
+ CLEAR_BIT_IN_BYTE(value, 6);
+ ak4647_write_reg(AK4647_PM2, value);
+
+ /* after playback, restore capture volume */
+ if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
+ ak4647_write_reg(AK4647_LEFT_INPUT_VOLUME,
+ ak4647_state.left_capture_vol);
+ ak4647_write_reg(AK4647_RIGHT_INPUT_VOLUME,
+ ak4647_state.right_capture_vol);
+ }
+}
+
+static int ak4647_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ u8 value;
+
+ /* VCOM power on */
+ ak4647_write_reg(AK4647_PM1, 0x44);
+ msleep(30);
+
+ ak4647_read_reg(AK4647_PM2, &value);
+ /* PLL enabled */
+ SET_BIT_IN_BYTE(value, 0);
+
+ ak4647_write_reg(AK4647_PM2, value);
+ /* wait for PLL locked */
+ msleep(40);
+
+ /* don't mute */
+ SET_BIT_IN_BYTE(value, 6);
+ ak4647_write_reg(AK4647_PM2, value);
+ return 0;
+}
+
+struct snd_soc_dai ak4647_hifi_dai = {
+ .name = "ak4647",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AK4647_RATES,
+ .formats = AK4647_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4647_RATES,
+ .formats = AK4647_FORMATS,
+ },
+ .ops = {
+ .startup = ak4647_pcm_startup,
+ .shutdown = ak4647_pcm_shutdown,
+ .prepare = ak4647_pcm_prepare,
+ },
+ .dai_ops = {
+ .digital_mute = ak4647_digital_mute,
+ .set_fmt = ak4647_set_dai_fmt,
+ .set_sysclk = ak4647_set_dai_sysclk,
+ .set_clkdiv = ak4647_set_clkdiv,
+ },
+};
+
+EXPORT_SYMBOL_GPL(ak4647_hifi_dai);
+
+static int ak4647_init(struct snd_soc_device *socdev)
+{
+
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+ unsigned char val;
+
+ ret = ak4647_read_reg(AK4647_REG_START, &val);
+ if (ret < 0) {
+ pr_err("Device with ID register %x is not a AK4647\n", val);
+ return -ENODEV;
+ }
+
+ codec->name = "ak4647", codec->owner = THIS_MODULE;
+ codec->read = ak4647_codec_read;
+ codec->write = ak4647_codec_write;
+ codec->dai = &ak4647_hifi_dai;
+ codec->num_dai = 1;
+
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&ak4647_i2c_client->dev, "failed to create pcms\n");
+ return ret;
+ }
+
+ /* setup init value for audio path controls here */
+ /* enable DAC to headphone */
+ ak4647_write_reg(AK4647_MODE4, 0x9);
+ /* capture source to be LIN2 and RIN2 */
+ ak4647_write_reg(AK4647_PM3, 0x6);
+ /* MPWR pin up */
+ ak4647_write_reg(AK4647_SIG1, 0x5);
+
+ ak4647_add_controls(codec);
+ ak4647_add_widgets(codec);
+
+ ret = snd_soc_register_card(socdev);
+
+ if (ret < 0) {
+ pr_err("ak4647: failed to register card\n");
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ return ret;
+ }
+
+ /* MCKI = 12M, default audio interface format as "left-justified" */
+ ak4647_write_reg(AK4647_MODE1, 0x62);
+
+ /* ALC disabled */
+ ak4647_write_reg(AK4647_ALC1, 0x0);
+
+ ak4647_state.left_capture_vol = 0x91;
+ ak4647_state.right_capture_vol = 0x91;
+
+ return 0;
+}
+
+static struct snd_soc_device *ak4647_socdev;
+
+static int ak4647_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = ak4647_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(client, codec);
+ ak4647_i2c_client = client;
+ codec->control_data = client;
+ ret = ak4647_init(socdev);
+ if (ret < 0)
+ dev_err(&client->dev, "Device initialisation failed\n");
+
+ return ret;
+
+}
+
+static const struct i2c_device_id ak4647_id[] = {
+ {"ak4647-i2c", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ak4647_id);
+
+static struct i2c_driver ak4647_i2c_driver = {
+ .driver = {.owner = THIS_MODULE,
+ .name = "ak4647-i2c",
+ },
+ .probe = ak4647_i2c_probe,
+ .id_table = ak4647_id,
+};
+
+int pmic_audio_fm_output_enable(bool enable)
+{
+ u8 val;
+ if (enable) {
+ ak4647_set_dai_fmt(NULL,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_SYNC);
+ ak4647_set_dai_sysclk(NULL, 0, 44100, 0);
+ ak4647_set_clkdiv(NULL, 0, 0);
+ /* VCOM power on */
+ ak4647_write_reg(AK4647_PM1, 0x44);
+ msleep(30);
+
+ ak4647_read_reg(AK4647_PM2, &val);
+ /* PLL enabled */
+ SET_BIT_IN_BYTE(val, 0);
+
+ ak4647_write_reg(AK4647_PM2, val);
+ /* wait for PLL locked */
+ msleep(40);
+
+ /* don't mute */
+ SET_BIT_IN_BYTE(val, 6);
+ ak4647_write_reg(AK4647_PM2, val);
+
+ /* loopback STDO to DAC */
+ ak4647_read_reg(AK4647_MODE3, &val);
+ SET_BIT_IN_BYTE(val, 6);
+ ak4647_write_reg(AK4647_MODE3, val);
+
+ /* switch to R/L 1 */
+ ak4647_read_reg(AK4647_PM3, &val);
+ CLEAR_BIT_IN_BYTE(val, 1);
+ CLEAR_BIT_IN_BYTE(val, 2);
+ ak4647_write_reg(AK4647_PM3, val);
+
+ /* power up ADC */
+ ak4647_read_reg(AK4647_PM1, &val);
+ SET_BIT_IN_BYTE(val, 0);
+ ak4647_write_reg(AK4647_PM1, val);
+ ak4647_read_reg(AK4647_PM3, &val);
+ SET_BIT_IN_BYTE(val, 0);
+ ak4647_write_reg(AK4647_PM3, val);
+
+ /* power up DAC */
+ ak4647_read_reg(AK4647_PM1, &val);
+ SET_BIT_IN_BYTE(val, 2);
+ ak4647_write_reg(AK4647_PM1, val);
+
+ msleep(30);
+
+ /* headphone output switch on */
+ ak4647_read_reg(AK4647_MODE4, &val);
+ SET_BIT_IN_BYTE(val, 0);
+ ak4647_write_reg(AK4647_MODE4, val);
+
+ /* power on headphone amp */
+ ak4647_read_reg(AK4647_PM2, &val);
+ SET_BIT_IN_BYTE(val, 4);
+ SET_BIT_IN_BYTE(val, 5);
+ ak4647_write_reg(AK4647_PM2, val);
+
+ ak4647_digital_mute(NULL, 0);
+ } else {
+ ak4647_digital_mute(NULL, 1);
+
+ /* disbale loopback */
+ ak4647_read_reg(AK4647_MODE3, &val);
+ CLEAR_BIT_IN_BYTE(val, 6);
+ SET_BIT_IN_BYTE(val, 5);
+ ak4647_write_reg(AK4647_MODE3, val);
+
+ /* switch to R/L 2 */
+ ak4647_read_reg(AK4647_PM3, &val);
+ SET_BIT_IN_BYTE(val, 1);
+ SET_BIT_IN_BYTE(val, 2);
+ ak4647_write_reg(AK4647_PM3, val);
+ }
+ return 0;
+}
+
+static int loopback_xhandle_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = kcontrol->private_value;
+ return 0;
+}
+
+static int loopback_xhandle_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int changed;
+ long flag = ucontrol->value.integer.value[0];
+ changed =
+ (ucontrol->value.integer.value[0] ==
+ kcontrol->private_value) ? 0 : 1;
+ kcontrol->private_value = flag;
+ if (flag)
+ pmic_audio_fm_output_enable(true);
+ else
+ pmic_audio_fm_output_enable(false);
+
+ return changed;
+}
+
+static int ak4647_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ ak4647_socdev = socdev;
+
+ ret = i2c_add_driver(&ak4647_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ kfree(codec);
+ }
+
+ return ret;
+}
+
+/* power down chip */
+static int ak4647_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ i2c_del_driver(&ak4647_i2c_driver);
+ kfree(codec);
+
+ return 0;
+}
+
+static int ak4647_suspend(struct platform_device *pdev, pm_message_t state)
+{
+
+ return 0;
+}
+
+static int ak4647_resume(struct platform_device *pdev)
+{
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4647 = {
+ .probe = ak4647_probe,
+ .remove = ak4647_remove,
+ .suspend = ak4647_suspend,
+ .resume = ak4647_resume,
+};
+
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4647);
+
+MODULE_DESCRIPTION("ASoC ak4647 driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4647.h b/sound/soc/codecs/ak4647.h
new file mode 100644
index 000000000000..d88832b1dd09
--- /dev/null
+++ b/sound/soc/codecs/ak4647.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file ak4647.h
+ * @brief Driver for AK4647
+ *
+ * @ingroup Sound
+ */
+#ifndef _AK4647_H_
+#define _AK4647_H_
+
+#ifdef __KERNEL__
+
+/*!
+ * AK4647 registers
+ */
+
+#define AK4647_PM1 0x00
+#define AK4647_PM2 0x01
+#define AK4647_SIG1 0x02
+#define AK4647_SIG2 0x03
+#define AK4647_MODE1 0x04
+#define AK4647_MODE2 0x05
+#define AK4647_TIMER 0x06
+#define AK4647_ALC1 0x07
+#define AK4647_ALC2 0x08
+
+#define AK4647_LEFT_INPUT_VOLUME 0x09
+#define AK4647_LEFT_DGT_VOLUME 0x0A
+#define AK4647_ALC3 0x0B
+#define AK4647_RIGHT_INPUT_VOLUME 0x0C
+#define AK4647_RIGHT_DGT_VOLUME 0x0D
+#define AK4647_MODE3 0x0E
+#define AK4647_MODE4 0x0F
+#define AK4647_PM3 0x10
+#define AK4647_DGT_FIL_SEL 0x11
+
+/* filter 3 coeffecient*/
+
+#define AK4647_FIL3_COEF0 0x12
+#define AK4647_FIL3_COEF1 0x13
+#define AK4647_FIL3_COEF2 0x14
+#define AK4647_FIL3_COEF3 0x15
+
+/* eq coeffecient*/
+
+#define AK4647_EQ_COEF0 0x16
+#define AK4647_EQ_COEF1 0x17
+#define AK4647_EQ_COEF2 0x18
+#define AK4647_EQ_COEF3 0x19
+#define AK4647_EQ_COEF4 0x1A
+#define AK4647_EQ_COEF5 0x1B
+
+/* filter 3 coeffecient*/
+
+#define AK4647_FIL1_COEF0 0x1C
+#define AK4647_FIL1_COEF1 0x1D
+#define AK4647_FIL1_COEF2 0x1E
+#define AK4647_FIL1_COEF3 0x1F
+
+#define AK4647_REG_START 0x00
+#define AK4647_REG_END 0x1F
+#define AK4647_REG_NUMBER 0x20
+
+/* clock divider id's */
+#define AK4647_BCLK_CLKDIV 0
+#define AK4647_MCLK_CLKDIV 1
+
+/* bit clock div values (AK4647_BCLK_CLKDIV)*/
+#define AK4647_BCLK_DIV_32 0
+#define AK4647_BCLK_DIV_64 1
+
+/* m clock div values (AK4647_MCLK_CLKDIV)*/
+#define AK4647_MCLK_DIV_32 0
+#define AK4647_MCLK_DIV_64 1
+#define AK4647_MCLK_DIV_128 2
+#define AK4647_MCLK_DIV_256 3
+
+#endif /* __KERNEL__ */
+
+#endif /* _AK4647_H_ */
diff --git a/sound/soc/codecs/ak5702.c b/sound/soc/codecs/ak5702.c
new file mode 100644
index 000000000000..a4a046faf5cc
--- /dev/null
+++ b/sound/soc/codecs/ak5702.c
@@ -0,0 +1,592 @@
+/*
+ * ak5702.c -- AK5702 Soc Audio driver
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "ak5702.h"
+
+#define AK5702_VERSION "0.1"
+
+/* codec private data */
+struct ak5702_priv {
+ unsigned int sysclk;
+};
+
+/*
+ * ak5702 register cache
+ */
+static const u16 ak5702_reg[AK5702_CACHEREGNUM] = {
+ 0x0000, 0x0024, 0x0000, 0x0001, 0x0023, 0x001f,
+ 0x0000, 0x0001, 0x0091, 0x0000, 0x00e1, 0x0000,
+ 0x00a0, 0x0000, 0x0000, 0x0000, 0x0001, 0x0020,
+ 0x0000, 0x0000, 0x0001, 0x0091, 0x0000, 0x00e1,
+ 0x0000,
+};
+
+/*
+ * read ak5702 register cache
+ */
+static inline unsigned int ak5702_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+
+ if (reg >= AK5702_CACHEREGNUM)
+ return -1;
+ return cache[reg];
+}
+
+static inline unsigned int ak5702_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u8 data;
+ data = reg;
+
+ if (codec->hw_write(codec->control_data, &data, 1) != 1)
+ return -EIO;
+ if (codec->hw_read(codec->control_data, &data, 1) != 1)
+ return -EIO;
+
+ return data;
+};
+
+/*
+ * write ak5702 register cache
+ */
+static inline void ak5702_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+
+ if (reg >= AK5702_CACHEREGNUM)
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the AK5702 register space
+ */
+static int ak5702_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ u8 data[2];
+
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ ak5702_write_reg_cache(codec, reg, value);
+ if (codec->hw_write(codec->control_data, data, 2) == 2)
+ return 0;
+ else
+ return -EIO;
+}
+
+static const char *ak5702_mic_gain[] = { "0dB", "+15dB", "+30dB", "+36dB" };
+static const char *ak5702_adca_left_type[] =
+ { "Single-ended", "Full-differential" };
+static const char *ak5702_adca_right_type[] =
+ { "Single-ended", "Full-differential" };
+static const char *ak5702_adcb_left_type[] =
+ { "Single-ended", "Full-differential" };
+static const char *ak5702_adcb_right_type[] =
+ { "Single-ended", "Full-differential" };
+static const char *ak5702_adca_left_input[] = { "LIN1", "LIN2" };
+static const char *ak5702_adca_right_input[] = { "RIN1", "RIN2" };
+static const char *ak5702_adcb_left_input[] = { "LIN3", "LIN4" };
+static const char *ak5702_adcb_right_input[] = { "RIN3", "RIN4" };
+
+static const struct soc_enum ak5702_enum[] = {
+ SOC_ENUM_SINGLE(AK5702_MICG1, 0, 4, ak5702_mic_gain),
+ SOC_ENUM_SINGLE(AK5702_MICG2, 0, 4, ak5702_mic_gain),
+
+ SOC_ENUM_SINGLE(AK5702_SIG1, 0, 2, ak5702_adca_left_input),
+ SOC_ENUM_SINGLE(AK5702_SIG1, 1, 2, ak5702_adca_right_input),
+ SOC_ENUM_SINGLE(AK5702_SIG2, 0, 2, ak5702_adcb_left_input),
+ SOC_ENUM_SINGLE(AK5702_SIG1, 1, 2, ak5702_adcb_right_input),
+
+ SOC_ENUM_SINGLE(AK5702_SIG1, 2, 2, ak5702_adca_left_type),
+ SOC_ENUM_SINGLE(AK5702_SIG1, 3, 2, ak5702_adca_right_type),
+ SOC_ENUM_SINGLE(AK5702_SIG2, 2, 2, ak5702_adcb_left_type),
+ SOC_ENUM_SINGLE(AK5702_SIG2, 3, 2, ak5702_adcb_right_type),
+};
+
+static const struct snd_kcontrol_new ak5702_snd_controls[] = {
+ SOC_SINGLE("ADCA Left Vol", AK5702_LVOL1, 0, 242, 0),
+ SOC_SINGLE("ADCA Right Vol", AK5702_RVOL1, 0, 242, 0),
+ SOC_SINGLE("ADCB Left Vol", AK5702_LVOL2, 0, 242, 0),
+ SOC_SINGLE("ADCB Right Vol", AK5702_RVOL2, 0, 242, 0),
+
+ SOC_ENUM("MIC-AmpA Gain", ak5702_enum[0]),
+ SOC_ENUM("MIC-AmpB Gain", ak5702_enum[1]),
+
+ SOC_ENUM("ADCA Left Source", ak5702_enum[2]),
+ SOC_ENUM("ADCA Right Source", ak5702_enum[3]),
+ SOC_ENUM("ADCB Left Source", ak5702_enum[4]),
+ SOC_ENUM("ADCB Right Source", ak5702_enum[5]),
+
+ SOC_ENUM("ADCA Left Type", ak5702_enum[6]),
+ SOC_ENUM("ADCA Right Type", ak5702_enum[7]),
+ SOC_ENUM("ADCB Left Type", ak5702_enum[8]),
+ SOC_ENUM("ADCB Right Type", ak5702_enum[9]),
+};
+
+/* add non dapm controls */
+static int ak5702_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(ak5702_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&ak5702_snd_controls[i], codec,
+ NULL));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* ak5702 dapm widgets */
+static const struct snd_soc_dapm_widget ak5702_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC("ADCA Left", "Capture", AK5702_PM1, 0, 0),
+ SND_SOC_DAPM_ADC("ADCA Right", "Capture", AK5702_PM1, 1, 0),
+ SND_SOC_DAPM_ADC("ADCB Left", "Capture", AK5702_PM2, 0, 0),
+ SND_SOC_DAPM_ADC("ADCB Right", "Capture", AK5702_PM2, 1, 0),
+
+ SND_SOC_DAPM_INPUT("ADCA Left Input"),
+ SND_SOC_DAPM_INPUT("ADCA Right Input"),
+ SND_SOC_DAPM_INPUT("ADCB Left Input"),
+ SND_SOC_DAPM_INPUT("ADCB Right Input"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"ADCA Left", NULL, "ADCA Left Input"},
+ {"ADCA Right", NULL, "ADCA Right Input"},
+ {"ADCB Left", NULL, "ADCB Left Input"},
+ {"ADCB Right", NULL, "ADCB Right Input"},
+};
+
+static int ak5702_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, ak5702_dapm_widgets,
+ ARRAY_SIZE(ak5702_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static int ak5702_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 fs = 0;
+ u8 value;
+
+ switch (freq) {
+ case 8000:
+ fs = 0x0;
+ break;
+ case 11025:
+ fs = 0x05;
+ break;
+ case 12000:
+ fs = 0x01;
+ break;
+ case 16000:
+ fs = 0x02;
+ break;
+ case 22050:
+ fs = 0x07;
+ break;
+ case 24000:
+ fs = 0x03;
+ break;
+ case 32000:
+ fs = 0x0a;
+ break;
+ case 44100:
+ fs = 0x0f;
+ break;
+ case 48000:
+ fs = 0x0b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ value = ak5702_read_reg_cache(codec, AK5702_FS1);
+ value &= (~AK5702_FS1_FS_MASK);
+ value |= fs;
+ ak5702_write(codec, AK5702_FS1, value);
+ return 0;
+}
+
+static int ak5702_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 fmt1 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ fmt1 = AK5702_FMT1_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fmt1 = AK5702_FMT1_MSB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ak5702_write(codec, AK5702_FMT1, fmt1);
+ ak5702_write(codec, AK5702_FMT2, AK5702_FMT2_STEREO);
+ return 0;
+}
+
+static int ak5702_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 reg = 0;
+
+ reg = ak5702_read_reg_cache(codec, AK5702_PLL1);
+ switch (pll_id) {
+ case AK5702_PLL_POWERDOWN:
+ reg &= (~AK5702_PLL1_PM_MASK);
+ reg |= AK5702_PLL1_POWERDOWN;
+ break;
+ case AK5702_PLL_MASTER:
+ reg &= (~AK5702_PLL1_MODE_MASK);
+ reg |= AK5702_PLL1_MASTER;
+ reg |= AK5702_PLL1_POWERUP;
+ break;
+ case AK5702_PLL_SLAVE:
+ reg &= (~AK5702_PLL1_MODE_MASK);
+ reg |= AK5702_PLL1_SLAVE;
+ reg |= AK5702_PLL1_POWERUP;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ switch (freq_in) {
+ case 11289600:
+ reg &= (~AK5702_PLL1_PLL_MASK);
+ reg |= AK5702_PLL1_11289600;
+ break;
+ case 12000000:
+ reg &= (~AK5702_PLL1_PLL_MASK);
+ reg |= AK5702_PLL1_12000000;
+ break;
+ case 12288000:
+ reg &= (~AK5702_PLL1_PLL_MASK);
+ reg |= AK5702_PLL1_12288000;
+ break;
+ case 19200000:
+ reg &= (~AK5702_PLL1_PLL_MASK);
+ reg |= AK5702_PLL1_19200000;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ ak5702_write(codec, AK5702_PLL1, reg);
+ return 0;
+}
+
+static int ak5702_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 reg = 0;
+
+ if (div_id == AK5702_BCLK_CLKDIV) {
+ reg = ak5702_read_reg_cache(codec, AK5702_FS1);
+ switch (div) {
+ case AK5702_BCLK_DIV_32:
+ reg &= (~AK5702_FS1_BCKO_MASK);
+ reg |= AK5702_FS1_BCKO_32FS;
+ ak5702_write(codec, AK5702_FS1, reg);
+ break;
+ case AK5702_BCLK_DIV_64:
+ reg &= (~AK5702_FS1_BCKO_MASK);
+ reg |= AK5702_FS1_BCKO_64FS;
+ ak5702_write(codec, AK5702_FS1, reg);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int ak5702_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u8 reg = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ reg = ak5702_read_reg_cache(codec, AK5702_PM1);
+ ak5702_write(codec, AK5702_PM1, reg | AK5702_PM1_PMVCM);
+ reg = ak5702_read_reg_cache(codec, AK5702_PLL1);
+ reg = reg | AK5702_PLL1_POWERUP | AK5702_PLL1_MASTER;
+ ak5702_write(codec, AK5702_PLL1, reg);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ reg = ak5702_read_reg_cache(codec, AK5702_PM1);
+ ak5702_write(codec, AK5702_PM1, reg | AK5702_PM1_PMVCM);
+ reg = ak5702_read_reg_cache(codec, AK5702_PLL1);
+ ak5702_write(codec, AK5702_PLL1, reg & (~AK5702_PLL1_POWERUP));
+ break;
+ case SND_SOC_BIAS_OFF:
+ reg = ak5702_read_reg_cache(codec, AK5702_PM1);
+ ak5702_write(codec, AK5702_PM1, reg & (~AK5702_PM1_PMVCM));
+ break;
+ }
+
+ codec->bias_level = level;
+ return 0;
+}
+
+static int ak5702_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ ak5702_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ak5702_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+
+ /* Bring the codec back up to standby first to minimise pop/clicks */
+ ak5702_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ ak5702_set_bias_level(codec, codec->suspend_bias_level);
+
+ /* Sync back everything else */
+ for (i = 0; i < ARRAY_SIZE(ak5702_reg); i++)
+ ak5702_write(codec, i, ak5702_reg[i]);
+
+ return 0;
+}
+
+#define AK5702_RATES SNDRV_PCM_RATE_8000_48000
+#define AK5702_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+struct snd_soc_dai ak5702_dai = {
+ .name = "AK5702",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = AK5702_RATES,
+ .formats = AK5702_FORMATS,
+ },
+ .dai_ops = {
+ .set_fmt = ak5702_set_dai_fmt,
+ .set_sysclk = ak5702_set_dai_sysclk,
+ .set_clkdiv = ak5702_set_dai_clkdiv,
+ .set_pll = ak5702_set_dai_pll,
+ },
+};
+EXPORT_SYMBOL_GPL(ak5702_dai);
+
+static int ak5702_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+ u8 reg = 0;
+
+ codec->name = "AK5702";
+ codec->owner = THIS_MODULE;
+ codec->read = ak5702_read_reg_cache;
+ codec->write = ak5702_write;
+ codec->set_bias_level = ak5702_set_bias_level;
+ codec->dai = &ak5702_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(ak5702_reg);
+ codec->reg_cache = (void *)&ak5702_reg;
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "ak5702: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ reg = ak5702_read_reg_cache(codec, AK5702_PM1);
+ reg |= AK5702_PM1_PMVCM;
+ ak5702_write(codec, AK5702_PM1, reg);
+
+ /* initialize ADC */
+ reg = AK5702_SIG1_L_LIN1 | AK5702_SIG1_R_RIN2;
+ ak5702_write(codec, AK5702_SIG1, reg);
+ reg = AK5702_SIG2_L_LIN3 | AK5702_SIG2_R_RIN4;
+ ak5702_write(codec, AK5702_SIG2, reg);
+
+ reg = ak5702_read_reg_cache(codec, AK5702_PM1);
+ reg = reg | AK5702_PM1_PMADAL | AK5702_PM1_PMADAR;
+ ak5702_write(codec, AK5702_PM1, reg);
+ reg = ak5702_read_reg_cache(codec, AK5702_PM2);
+ reg = reg | AK5702_PM2_PMADBL | AK5702_PM2_PMADBR;
+ ak5702_write(codec, AK5702_PM2, reg);
+
+ /* initialize volume */
+ ak5702_write(codec, AK5702_MICG1, AK5702_MICG1_INIT);
+ ak5702_write(codec, AK5702_MICG2, AK5702_MICG2_INIT);
+ ak5702_write(codec, AK5702_VOL1, AK5702_VOL1_IVOLAC);
+ ak5702_write(codec, AK5702_VOL2, AK5702_VOL2_IVOLBC);
+ ak5702_write(codec, AK5702_LVOL1, AK5702_LVOL1_INIT);
+ ak5702_write(codec, AK5702_RVOL1, AK5702_RVOL1_INIT);
+ ak5702_write(codec, AK5702_LVOL2, AK5702_LVOL2_INIT);
+ ak5702_write(codec, AK5702_RVOL2, AK5702_RVOL2_INIT);
+
+ ak5702_add_controls(codec);
+ ak5702_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "ak5702: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+
+static struct snd_soc_device *ak5702_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int ak5702_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = ak5702_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = ak5702_init(socdev);
+ if (ret < 0)
+ printk(KERN_ERR "failed to initialise AK5702\n");
+
+ return ret;
+}
+
+static const struct i2c_device_id ak5702_i2c_id[] = {
+ {"ak5702-i2c", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ak5702_i2c_id);
+
+static struct i2c_driver ak5702_i2c_driver = {
+ .driver = {
+ .name = "ak5702-i2c",
+ .owner = THIS_MODULE,
+ },
+ .probe = ak5702_i2c_probe,
+ .id_table = ak5702_i2c_id,
+};
+#endif
+
+static int ak5702_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct ak5702_setup_data *setup;
+ struct snd_soc_codec *codec;
+ struct ak5702_priv *ak5702;
+ int ret;
+
+ setup = socdev->codec_data;
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ ak5702 = kzalloc(sizeof(struct ak5702_priv), GFP_KERNEL);
+ if (ak5702 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = ak5702;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ ak5702_socdev = socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ codec->hw_write = (hw_write_t) i2c_master_send;
+ codec->hw_read = (hw_read_t) i2c_master_recv;
+ ret = i2c_add_driver(&ak5702_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ kfree(codec->private_data);
+ kfree(codec);
+ }
+#endif
+ return ret;
+}
+
+static int ak5702_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ ak5702_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&ak5702_i2c_driver);
+#endif
+ kfree(codec->private_data);
+ kfree(codec);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak5702 = {
+ .probe = ak5702_probe,
+ .remove = ak5702_remove,
+ .suspend = ak5702_suspend,
+ .resume = ak5702_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak5702);
+
+MODULE_DESCRIPTION("Soc AK5702 driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak5702.h b/sound/soc/codecs/ak5702.h
new file mode 100644
index 000000000000..97af6e45e498
--- /dev/null
+++ b/sound/soc/codecs/ak5702.h
@@ -0,0 +1,130 @@
+/*
+ * ak5702.h -- AK5702 Soc Audio driver
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _AK5702_H
+#define _AK5702_H
+
+/* AK5702 register space */
+
+#define AK5702_PM1 0x00
+#define AK5702_PLL1 0x01
+#define AK5702_SIG1 0x02
+#define AK5702_MICG1 0x03
+#define AK5702_FMT1 0x04
+#define AK5702_FS1 0x05
+#define AK5702_CLK1 0x06
+#define AK5702_VOL1 0x07
+#define AK5702_LVOL1 0x08
+#define AK5702_RVOL1 0x09
+#define AK5702_TIMER1 0x0a
+#define AK5702_ALC11 0x0b
+#define AK5702_ALC12 0x0c
+#define AK5702_MODE11 0x0d
+#define AK5702_MODE12 0x0e
+#define AK5702_MODE13 0x0f
+
+#define AK5702_PM2 0x10
+#define AK5702_PLL2 0x11
+#define AK5702_SIG2 0x12
+#define AK5702_MICG2 0x13
+#define AK5702_FMT2 0x14
+#define AK5702_FS2 0x15
+#define AK5702_CLK2 0x16
+#define AK5702_VOL2 0x17
+#define AK5702_LVOL2 0x18
+#define AK5702_RVOL2 0x19
+#define AK5702_TIMER2 0x1a
+#define AK5702_ALC21 0x1b
+#define AK5702_ALC22 0x1c
+#define AK5702_MODE21 0x1d
+#define AK5702_MODE22 0x1e
+
+#define AK5702_CACHEREGNUM 0x1F
+
+#define AK5702_PM1_PMADAL 0x01
+#define AK5702_PM1_PMADAR 0x02
+#define AK5702_PM1_PMVCM 0x04
+#define AK5702_PM2_PMADBL 0x01
+#define AK5702_PM2_PMADBR 0x02
+
+#define AK5702_PLL1_POWERDOWN 0x0
+#define AK5702_PLL1_POWERUP 0x01
+#define AK5702_PLL1_MASTER 0x02
+#define AK5702_PLL1_SLAVE 0x0
+#define AK5702_PLL1_11289600 0x10
+#define AK5702_PLL1_12000000 0x24
+#define AK5702_PLL1_12288000 0x14
+#define AK5702_PLL1_19200000 0x20
+
+#define AK5702_SIG1_L_LIN1 0x0
+#define AK5702_SIG1_L_LIN2 0x01
+#define AK5702_SIG1_R_RIN1 0x0
+#define AK5702_SIG1_R_RIN2 0x02
+#define AK5702_SIG1_PMMPA 0x10
+#define AK5702_SIG2_L_LIN3 0x0
+#define AK5702_SIG2_L_LIN4 0x01
+#define AK5702_SIG2_R_RIN3 0x0
+#define AK5702_SIG2_R_RIN4 0x02
+#define AK5702_SIG2_PMMPB 0x10
+
+#define AK5702_MICG1_INIT 0x0
+#define AK5702_MICG2_INIT 0x0
+
+#define AK5702_FMT1_I2S 0x23
+#define AK5702_FMT1_MSB 0x22
+#define AK5702_FMT2_STEREO 0x20
+#define AK5702_FS1_BCKO_32FS 0x10
+#define AK5702_FS1_BCKO_64FS 0x20
+#define AK5702_CLK1_PS_256FS 0x0
+#define AK5702_CLK1_PS_128FS 0x01
+#define AK5702_CLK1_PS_64FS 0x02
+#define AK5702_CLK1_PS_32FS 0x03
+#define AK5702_VOL1_IVOLAC 0x01
+#define AK5702_VOL2_IVOLBC 0x01
+#define AK5702_LVOL1_INIT 0x91
+#define AK5702_RVOL1_INIT 0x91
+#define AK5702_LVOL2_INIT 0x91
+#define AK5702_RVOL2_INIT 0x91
+
+#define AK5702_PLL1_PM_MASK 0x01
+#define AK5702_PLL1_MODE_MASK 0x02
+#define AK5702_PLL1_PLL_MASK 0x3c
+#define AK5702_FS1_BCKO_MASK 0x30
+#define AK5702_FS1_FS_MASK 0x0f
+#define AK5702_CLK1_PS_MASK 0x03
+
+/* clock divider id */
+#define AK5702_BCLK_CLKDIV 0
+#define AK5702_MCLK_CLKDIV 1
+
+/* bit clock div values */
+#define AK5702_BCLK_DIV_32 0
+#define AK5702_BCLK_DIV_64 1
+
+/* m clock div values */
+#define AK5702_MCLK_DIV_32 0
+#define AK5702_MCLK_DIV_64 1
+#define AK5702_MCLK_DIV_128 2
+#define AK5702_MCLK_DIV_256 3
+
+/* PLL master and slave modes */
+#define AK5702_PLL_POWERDOWN 0
+#define AK5702_PLL_MASTER 1
+#define AK5702_PLL_SLAVE 2
+
+struct ak5702_setup_data {
+ int i2c_bus;
+ unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai ak5702_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak5702;
+
+#endif
diff --git a/sound/soc/codecs/bluetooth.c b/sound/soc/codecs/bluetooth.c
new file mode 100644
index 000000000000..f77e951120d3
--- /dev/null
+++ b/sound/soc/codecs/bluetooth.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+/*!
+ * @file bluetooth.c
+ * @brief Driver for bluetooth PCM interface
+ *
+ * @ingroup Sound
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#define BLUETOOTH_RATES SNDRV_PCM_RATE_8000
+
+#define BLUETOOTH_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+struct snd_soc_dai bt_dai = {
+ .name = "bluetooth",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = BLUETOOTH_RATES,
+ .formats = BLUETOOTH_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = BLUETOOTH_RATES,
+ .formats = BLUETOOTH_FORMATS,
+ },
+};
+EXPORT_SYMBOL_GPL(bt_dai);
+
+static int bt_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec->name = "bluetooth";
+ codec->owner = THIS_MODULE;
+ codec->dai = &bt_dai;
+ codec->num_dai = 1;
+
+ snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ pr_err("failed to create bluetooth pcms\n");
+ return ret;
+ }
+
+ ret = snd_soc_register_card(socdev);
+ strcpy(codec->card->id, "bluetooth");
+
+ if (ret < 0) {
+ pr_err("bluetooth: failed to register card\n");
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_device *bt_socdev;
+
+static int bt_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ bt_socdev = socdev;
+
+ ret = bt_init(socdev);
+ if (ret < 0) {
+ pr_err("Bluetooth codec initialisation failed\n");
+ kfree(codec);
+ }
+
+ return ret;
+}
+
+/* power down chip */
+static int bt_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ kfree(codec);
+
+ return 0;
+}
+
+static int bt_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+static int bt_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_bt = {
+ .probe = bt_probe,
+ .remove = bt_remove,
+ .suspend = bt_suspend,
+ .resume = bt_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_bt);
+
+MODULE_DESCRIPTION("ASoC bluetooth codec driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
new file mode 100644
index 000000000000..6448129cd239
--- /dev/null
+++ b/sound/soc/codecs/sgtl5000.c
@@ -0,0 +1,1219 @@
+/*
+ * sgtl5000.c -- SGTL5000 ALSA SoC Audio driver
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "sgtl5000.h"
+
+struct sgtl5000_priv {
+ int sysclk;
+ int master;
+ int fmt;
+ int rev;
+ int lrclk;
+ int capture_channels;
+ int playback_active;
+ int capture_active;
+ struct snd_pcm_substream *master_substream;
+ struct snd_pcm_substream *slave_substream;
+};
+
+static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level);
+
+#define SGTL5000_MAX_CACHED_REG SGTL5000_CHIP_SHORT_CTRL
+static u16 sgtl5000_regs[(SGTL5000_MAX_CACHED_REG >> 1) + 1];
+
+static unsigned int sgtl5000_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ unsigned int offset = reg >> 1;
+ if (offset >= ARRAY_SIZE(sgtl5000_regs))
+ return -EINVAL;
+ pr_debug("r r:%02x,v:%04x\n", reg, cache[offset]);
+ return cache[offset];
+}
+
+static unsigned int sgtl5000_hw_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct i2c_client *client = codec->control_data;
+ int i2c_ret;
+ u16 value;
+ u8 buf0[2], buf1[2];
+ u16 addr = client->addr;
+ u16 flags = client->flags;
+ struct i2c_msg msg[2] = {
+ {addr, flags, 2, buf0},
+ {addr, flags | I2C_M_RD, 2, buf1},
+ };
+
+ buf0[0] = (reg & 0xff00) >> 8;
+ buf0[1] = reg & 0xff;
+ i2c_ret = i2c_transfer(client->adapter, msg, 2);
+ if (i2c_ret < 0) {
+ pr_err("%s: read reg error : Reg 0x%02x\n", __func__, reg);
+ return 0;
+ }
+
+ value = buf1[0] << 8 | buf1[1];
+
+ pr_debug("r r:%02x,v:%04x\n", reg, value);
+ return value;
+}
+
+static unsigned int sgtl5000_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+ if ((reg == SGTL5000_CHIP_ID) ||
+ (reg == SGTL5000_CHIP_ADCDAC_CTRL) ||
+ (reg == SGTL5000_CHIP_ANA_STATUS) ||
+ (reg > SGTL5000_MAX_CACHED_REG))
+ return sgtl5000_hw_read(codec, reg);
+ else
+ return sgtl5000_read_reg_cache(codec, reg);
+}
+
+static inline void sgtl5000_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ unsigned int offset = reg >> 1;
+ if (offset < ARRAY_SIZE(sgtl5000_regs))
+ cache[offset] = value;
+}
+
+static int sgtl5000_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct i2c_client *client = codec->control_data;
+ u16 addr = client->addr;
+ u16 flags = client->flags;
+ u8 buf[4];
+ int i2c_ret;
+ struct i2c_msg msg = { addr, flags, 4, buf };
+
+ sgtl5000_write_reg_cache(codec, reg, value);
+ pr_debug("w r:%02x,v:%04x\n", reg, value);
+ buf[0] = (reg & 0xff00) >> 8;
+ buf[1] = reg & 0xff;
+ buf[2] = (value & 0xff00) >> 8;
+ buf[3] = value & 0xff;
+
+ i2c_ret = i2c_transfer(client->adapter, &msg, 1);
+ if (i2c_ret < 0) {
+ pr_err("%s: write reg error : Reg 0x%02x = 0x%04x\n",
+ __func__, reg, value);
+ return -EIO;
+ }
+
+ return i2c_ret;
+}
+
+static void sgtl5000_sync_reg_cache(struct snd_soc_codec *codec)
+{
+ int reg;
+ for (reg = 0; reg <= SGTL5000_MAX_CACHED_REG; reg += 2)
+ sgtl5000_write_reg_cache(codec, reg,
+ sgtl5000_hw_read(codec, reg));
+}
+
+static int sgtl5000_restore_reg(struct snd_soc_codec *codec, unsigned int reg)
+{
+ unsigned int cached_val, hw_val;
+
+ cached_val = sgtl5000_read_reg_cache(codec, reg);
+ hw_val = sgtl5000_hw_read(codec, reg);
+
+ if (hw_val != cached_val)
+ return sgtl5000_write(codec, reg, cached_val);
+
+ return 0;
+}
+
+static int all_reg[] = {
+ SGTL5000_CHIP_ID,
+ SGTL5000_CHIP_DIG_POWER,
+ SGTL5000_CHIP_CLK_CTRL,
+ SGTL5000_CHIP_I2S_CTRL,
+ SGTL5000_CHIP_SSS_CTRL,
+ SGTL5000_CHIP_ADCDAC_CTRL,
+ SGTL5000_CHIP_DAC_VOL,
+ SGTL5000_CHIP_PAD_STRENGTH,
+ SGTL5000_CHIP_ANA_ADC_CTRL,
+ SGTL5000_CHIP_ANA_HP_CTRL,
+ SGTL5000_CHIP_ANA_CTRL,
+ SGTL5000_CHIP_LINREG_CTRL,
+ SGTL5000_CHIP_REF_CTRL,
+ SGTL5000_CHIP_MIC_CTRL,
+ SGTL5000_CHIP_LINE_OUT_CTRL,
+ SGTL5000_CHIP_LINE_OUT_VOL,
+ SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_CHIP_PLL_CTRL,
+ SGTL5000_CHIP_CLK_TOP_CTRL,
+ SGTL5000_CHIP_ANA_STATUS,
+ SGTL5000_CHIP_SHORT_CTRL,
+};
+
+#ifdef DEBUG
+static void dump_reg(struct snd_soc_codec *codec)
+{
+ int i, reg;
+ printk(KERN_DEBUG "dump begin\n");
+ for (i = 0; i < 21; i++) {
+ reg = sgtl5000_read(codec, all_reg[i]);
+ printk(KERN_DEBUG "d r %04x, v %04x\n", all_reg[i], reg);
+ }
+ printk(KERN_DEBUG "dump end\n");
+}
+#else
+static void dump_reg(struct snd_soc_codec *codec)
+{
+}
+#endif
+
+static int dac_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = widget->codec;
+ unsigned int reg;
+
+ if (ucontrol->value.enumerated.item[0]) {
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_CLK_TOP_CTRL);
+ reg |= SGTL5000_INT_OSC_EN;
+ sgtl5000_write(codec, SGTL5000_CHIP_CLK_TOP_CTRL, reg);
+
+ if (codec->bias_level != SND_SOC_BIAS_ON) {
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_ON);
+ } else
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_ANA_CTRL);
+ reg &= ~(SGTL5000_LINE_OUT_MUTE | SGTL5000_HP_MUTE);
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_CTRL, reg);
+ } else {
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_CLK_TOP_CTRL);
+ reg &= ~SGTL5000_INT_OSC_EN;
+ sgtl5000_write(codec, SGTL5000_CHIP_CLK_TOP_CTRL, reg);
+
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ }
+ return 0;
+}
+
+static const char *adc_mux_text[] = {
+ "MIC_IN", "LINE_IN"
+};
+
+static const char *dac_mux_text[] = {
+ "DAC", "LINE_IN"
+};
+
+static const struct soc_enum adc_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 2, 2, adc_mux_text);
+
+static const struct soc_enum dac_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, dac_mux_text);
+
+static const struct snd_kcontrol_new adc_mux =
+SOC_DAPM_ENUM("ADC Mux", adc_enum);
+
+static const struct snd_kcontrol_new dac_mux = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Mux",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE
+ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_soc_info_enum_double,
+ .get = snd_soc_dapm_get_enum_double,
+ .put = dac_mux_put,
+ .private_value = (unsigned long)&dac_enum,
+};
+
+static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("LINE_IN"),
+ SND_SOC_DAPM_INPUT("MIC_IN"),
+
+ SND_SOC_DAPM_OUTPUT("HP_OUT"),
+ SND_SOC_DAPM_OUTPUT("LINE_OUT"),
+
+ SND_SOC_DAPM_PGA("HP", SGTL5000_CHIP_ANA_CTRL, 4, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("LO", SGTL5000_CHIP_ANA_CTRL, 8, 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("ADC Mux", SND_SOC_NOPM, 0, 0, &adc_mux),
+ SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, &dac_mux),
+
+ SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_DIG_POWER, 6, 0),
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"ADC Mux", "LINE_IN", "LINE_IN"},
+ {"ADC Mux", "MIC_IN", "MIC_IN"},
+ {"ADC", NULL, "ADC Mux"},
+ {"DAC Mux", "DAC", "DAC"},
+ {"DAC Mux", "LINE_IN", "LINE_IN"},
+ {"LO", NULL, "DAC"},
+ {"HP", NULL, "DAC Mux"},
+ {"LINE_OUT", NULL, "LO"},
+ {"HP_OUT", NULL, "HP"},
+};
+
+static int sgtl5000_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, sgtl5000_dapm_widgets,
+ ARRAY_SIZE(sgtl5000_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static int dac_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xfc - 0x3c;
+ return 0;
+}
+
+static int dac_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg, l, r;
+
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_DAC_VOL);
+ l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) << SGTL5000_DAC_VOL_LEFT_SHIFT;
+ r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) << SGTL5000_DAC_VOL_RIGHT_SHIFT;
+ l = l < 0x3c ? 0x3c : l;
+ l = l > 0xfc ? 0xfc : l;
+ r = r < 0x3c ? 0x3c : r;
+ r = r > 0xfc ? 0xfc : r;
+ l = 0xfc - l;
+ r = 0xfc - r;
+
+ ucontrol->value.integer.value[0] = l;
+ ucontrol->value.integer.value[1] = l;
+
+ return 0;
+}
+
+static int dac_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg, l, r;
+
+ l = ucontrol->value.integer.value[0];
+ r = ucontrol->value.integer.value[1];
+
+ l = l < 0 ? 0 : l;
+ l = l > 0xfc - 0x3c ? 0xfc - 0x3c : l;
+ r = r < 0 ? 0 : r;
+ r = r > 0xfc - 0x3c ? 0xfc - 0x3c : r;
+ l = 0xfc - l;
+ r = 0xfc - r;
+
+ reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT |
+ r << SGTL5000_DAC_VOL_RIGHT_SHIFT;
+
+ sgtl5000_write(codec, SGTL5000_CHIP_DAC_VOL, reg);
+
+ return 0;
+}
+
+static const char *mic_gain_text[] = {
+ "0dB", "20dB", "30dB", "40dB"
+};
+
+static const char *adc_m6db_text[] = {
+ "No Change", "Reduced by 6dB"
+};
+
+static const struct soc_enum mic_gain =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_MIC_CTRL, 0, 4, mic_gain_text);
+
+static const struct soc_enum adc_m6db =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_ADC_CTRL, 8, 2, adc_m6db_text);
+
+static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
+ SOC_ENUM("MIC GAIN", mic_gain),
+ SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0),
+ SOC_ENUM("Capture Vol Reduction", adc_m6db),
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = dac_info_volsw,
+ .get = dac_get_volsw,
+ .put = dac_put_volsw,
+ },
+ SOC_DOUBLE("Headphone Volume", SGTL5000_CHIP_ANA_HP_CTRL, 0, 8, 0x7f,
+ 1),
+};
+
+static int sgtl5000_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(sgtl5000_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&sgtl5000_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 adcdac_ctrl;
+
+ adcdac_ctrl = sgtl5000_read(codec, SGTL5000_CHIP_ADCDAC_CTRL);
+
+ if (mute) {
+ adcdac_ctrl |= SGTL5000_DAC_MUTE_LEFT;
+ adcdac_ctrl |= SGTL5000_DAC_MUTE_RIGHT;
+ } else {
+ adcdac_ctrl &= ~SGTL5000_DAC_MUTE_LEFT;
+ adcdac_ctrl &= ~SGTL5000_DAC_MUTE_RIGHT;
+ }
+
+ sgtl5000_write(codec, SGTL5000_CHIP_ADCDAC_CTRL, adcdac_ctrl);
+ if (!mute)
+ dump_reg(codec);
+ return 0;
+}
+
+static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct sgtl5000_priv *sgtl5000 = codec->private_data;
+ u16 i2sctl = 0;
+ pr_debug("%s:fmt=%08x\n", __func__, fmt);
+ sgtl5000->master = 0;
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ i2sctl |= SGTL5000_I2S_MASTER;
+ sgtl5000->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ i2sctl |= SGTL5000_I2S_MODE_PCM;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ i2sctl |= SGTL5000_I2S_MODE_PCM;
+ i2sctl |= SGTL5000_I2S_LRALIGN;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ i2sctl |= SGTL5000_I2S_MODE_I2S_LJ;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ i2sctl |= SGTL5000_I2S_MODE_RJ;
+ i2sctl |= SGTL5000_I2S_LRPOL;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ i2sctl |= SGTL5000_I2S_MODE_I2S_LJ;
+ i2sctl |= SGTL5000_I2S_LRALIGN;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sgtl5000->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ /* Clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_NB_IF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ case SND_SOC_DAIFMT_IB_NF:
+ i2sctl |= SGTL5000_I2S_SCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+ sgtl5000_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl);
+
+ return 0;
+}
+
+static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct sgtl5000_priv *sgtl5000 = codec->private_data;
+
+ switch (clk_id) {
+ case SGTL5000_SYSCLK:
+ sgtl5000->sysclk = freq;
+ break;
+ case SGTL5000_LRCLK:
+ sgtl5000->lrclk = freq;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sgtl5000_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct sgtl5000_priv *sgtl5000 = codec->private_data;
+ int reg;
+
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_DIG_POWER);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg |= SGTL5000_I2S_IN_POWERUP;
+ else
+ reg |= SGTL5000_I2S_OUT_POWERUP;
+ sgtl5000_write(codec, SGTL5000_CHIP_DIG_POWER, reg);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_ANA_POWER);
+ reg |= SGTL5000_ADC_POWERUP;
+ if (sgtl5000->capture_channels == 1)
+ reg &= ~SGTL5000_ADC_STEREO;
+ else
+ reg |= SGTL5000_ADC_STEREO;
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, reg);
+ }
+
+ return 0;
+}
+
+static int sgtl5000_pcm_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct sgtl5000_priv *sgtl5000 = codec->private_data;
+ struct snd_pcm_runtime *master_runtime;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sgtl5000->playback_active++;
+ else
+ sgtl5000->capture_active++;
+
+ /* The DAI has shared clocks so if we already have a playback or
+ * capture going then constrain this substream to match it.
+ */
+ if (sgtl5000->master_substream) {
+ master_runtime = sgtl5000->master_substream->runtime;
+
+ pr_debug("Constraining to %d bits at %dHz\n",
+ master_runtime->sample_bits, master_runtime->rate);
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ master_runtime->rate,
+ master_runtime->rate);
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ master_runtime->sample_bits,
+ master_runtime->sample_bits);
+
+ sgtl5000->slave_substream = substream;
+ } else
+ sgtl5000->master_substream = substream;
+
+ return 0;
+}
+
+static void sgtl5000_pcm_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct sgtl5000_priv *sgtl5000 = codec->private_data;
+ int reg, dig_pwr, ana_pwr;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sgtl5000->playback_active--;
+ else
+ sgtl5000->capture_active--;
+
+ if (sgtl5000->master_substream == substream)
+ sgtl5000->master_substream = sgtl5000->slave_substream;
+
+ sgtl5000->slave_substream = NULL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ana_pwr = sgtl5000_read(codec, SGTL5000_CHIP_ANA_POWER);
+ ana_pwr &= ~(SGTL5000_ADC_POWERUP | SGTL5000_ADC_STEREO);
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+ }
+
+ dig_pwr = sgtl5000_read(codec, SGTL5000_CHIP_DIG_POWER);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dig_pwr &= ~SGTL5000_I2S_IN_POWERUP;
+ else
+ dig_pwr &= ~SGTL5000_I2S_OUT_POWERUP;
+ sgtl5000_write(codec, SGTL5000_CHIP_DIG_POWER, dig_pwr);
+
+ if (!sgtl5000->playback_active && !sgtl5000->capture_active) {
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_I2S_CTRL);
+ reg &= ~SGTL5000_I2S_MASTER;
+ sgtl5000_write(codec, SGTL5000_CHIP_I2S_CTRL, reg);
+ }
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ * input: params_rate, params_fmt
+ */
+static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct sgtl5000_priv *sgtl5000 = codec->private_data;
+ int channels = params_channels(params);
+ int clk_ctl = 0;
+ int pll_ctl = 0;
+ int i2s_ctl;
+ int div2 = 0;
+ int reg;
+
+ pr_debug("%s channels=%d\n", __func__, channels);
+
+ if (!sgtl5000->sysclk) {
+ pr_err("%s: set sysclk first!\n", __func__);
+ return -EFAULT;
+ }
+
+ if (substream == sgtl5000->slave_substream) {
+ pr_debug("Ignoring hw_params for slave substream\n");
+ return 0;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ sgtl5000->capture_channels = channels;
+
+ switch (sgtl5000->lrclk) {
+ case 32000:
+ clk_ctl |= SGTL5000_SYS_FS_32k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ case 44100:
+ clk_ctl |= SGTL5000_SYS_FS_44_1k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ case 48000:
+ clk_ctl |= SGTL5000_SYS_FS_48k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ case 96000:
+ clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT;
+ break;
+ default:
+ pr_err("%s: sample rate %d not supported\n", __func__,
+ sgtl5000->lrclk);
+ return -EFAULT;
+ }
+
+#if 0 /* SGTL5000 rev1 has a IC bug to prevent switching to MCLK from PLL. */
+ if (fs * 256 == sgtl5000->sysclk)
+ clk_ctl |= SGTL5000_MCLK_FREQ_256FS << SGTL5000_MCLK_FREQ_SHIFT;
+ else if (fs * 384 == sgtl5000->sysclk && fs != 96000)
+ clk_ctl |= SGTL5000_MCLK_FREQ_384FS << SGTL5000_MCLK_FREQ_SHIFT;
+ else if (fs * 512 == sgtl5000->sysclk && fs != 96000)
+ clk_ctl |= SGTL5000_MCLK_FREQ_512FS << SGTL5000_MCLK_FREQ_SHIFT;
+ else
+#endif
+ {
+ if (!sgtl5000->master) {
+ pr_err("%s: PLL not supported in slave mode\n",
+ __func__);
+ return -EINVAL;
+ }
+ clk_ctl |= SGTL5000_MCLK_FREQ_PLL << SGTL5000_MCLK_FREQ_SHIFT;
+ }
+
+ if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) {
+ u64 out, t;
+ unsigned int in, int_div, frac_div;
+ if (sgtl5000->sysclk > 17000000) {
+ div2 = 1;
+ in = sgtl5000->sysclk / 2;
+ } else {
+ div2 = 0;
+ in = sgtl5000->sysclk;
+ }
+ if (sgtl5000->lrclk == 44100)
+ out = 180633600;
+ else
+ out = 196608000;
+ t = do_div(out, in);
+ int_div = out;
+ t *= 2048;
+ do_div(t, in);
+ frac_div = t;
+ pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT |
+ frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT;
+ }
+
+ i2s_ctl = sgtl5000_read(codec, SGTL5000_CHIP_I2S_CTRL);
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
+ return -EINVAL;
+ i2s_ctl |= SGTL5000_I2S_DLEN_16 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_32FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ i2s_ctl |= SGTL5000_I2S_DLEN_20 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ i2s_ctl |= SGTL5000_I2S_DLEN_24 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
+ return -EINVAL;
+ i2s_ctl |= SGTL5000_I2S_DLEN_32 << SGTL5000_I2S_DLEN_SHIFT;
+ i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+ SGTL5000_I2S_SCLKFREQ_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pr_debug("fs=%d,clk_ctl=%d,pll_ctl=%d,i2s_ctl=%d,div2=%d\n",
+ sgtl5000->lrclk, clk_ctl, pll_ctl, i2s_ctl, div2);
+
+ if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) {
+ sgtl5000_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_CLK_TOP_CTRL);
+ if (div2)
+ reg |= SGTL5000_INPUT_FREQ_DIV2;
+ else
+ reg &= ~SGTL5000_INPUT_FREQ_DIV2;
+ sgtl5000_write(codec, SGTL5000_CHIP_CLK_TOP_CTRL, reg);
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_ANA_POWER);
+ reg |= SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP;
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, reg);
+ }
+ sgtl5000_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+ sgtl5000_write(codec, SGTL5000_CHIP_I2S_CTRL, i2s_ctl);
+
+ return 0;
+}
+
+static void sgtl5000_mic_bias(struct snd_soc_codec *codec, int enable)
+{
+ int reg, bias_r = 0;
+ if (enable)
+ bias_r = SGTL5000_BIAS_R_4k << SGTL5000_BIAS_R_SHIFT;
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_MIC_CTRL);
+ if ((reg & SGTL5000_BIAS_R_MASK) != bias_r) {
+ reg &= ~SGTL5000_BIAS_R_MASK;
+ reg |= bias_r;
+ sgtl5000_write(codec, SGTL5000_CHIP_MIC_CTRL, reg);
+ }
+}
+
+static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg, ana_pwr;
+ int delay = 0;
+ pr_debug("dapm level %d\n", level);
+ switch (level) {
+ case SND_SOC_BIAS_ON: /* full On */
+ if (codec->bias_level == SND_SOC_BIAS_ON)
+ break;
+
+ sgtl5000_mic_bias(codec, 1);
+
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_ANA_POWER);
+ reg |= SGTL5000_VAG_POWERUP;
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, reg);
+ msleep(400);
+
+ break;
+
+ case SND_SOC_BIAS_PREPARE: /* partial On */
+ if (codec->bias_level == SND_SOC_BIAS_PREPARE)
+ break;
+
+ sgtl5000_mic_bias(codec, 0);
+
+ /* must power up hp/line out before vag & dac to
+ avoid pops. */
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_ANA_POWER);
+ if (reg & SGTL5000_VAG_POWERUP)
+ delay = 400;
+ reg &= ~SGTL5000_VAG_POWERUP;
+ reg |= SGTL5000_DAC_POWERUP;
+ reg |= SGTL5000_HP_POWERUP;
+ reg |= SGTL5000_LINE_OUT_POWERUP;
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, reg);
+ if (delay)
+ msleep(delay);
+
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_DIG_POWER);
+ reg |= SGTL5000_DAC_EN;
+ sgtl5000_write(codec, SGTL5000_CHIP_DIG_POWER, reg);
+
+ break;
+
+ case SND_SOC_BIAS_STANDBY: /* Off, with power */
+ /* soc doesn't do PREPARE state after record so make sure
+ that anything that needs to be turned OFF gets turned off. */
+ if (codec->bias_level == SND_SOC_BIAS_STANDBY)
+ break;
+
+ /* soc calls digital_mute to unmute before record but doesn't
+ call digital_mute to mute after record. */
+ sgtl5000_digital_mute(&sgtl5000_dai, 1);
+
+ sgtl5000_mic_bias(codec, 0);
+
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_ANA_POWER);
+ if (reg & SGTL5000_VAG_POWERUP) {
+ reg &= ~SGTL5000_VAG_POWERUP;
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, reg);
+ msleep(400);
+ }
+ reg &= ~SGTL5000_DAC_POWERUP;
+ reg &= ~SGTL5000_HP_POWERUP;
+ reg &= ~SGTL5000_LINE_OUT_POWERUP;
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, reg);
+
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_DIG_POWER);
+ reg &= ~SGTL5000_DAC_EN;
+ sgtl5000_write(codec, SGTL5000_CHIP_DIG_POWER, reg);
+
+ break;
+
+ case SND_SOC_BIAS_OFF: /* Off, without power */
+ /* must power down hp/line out after vag & dac to
+ avoid pops. */
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_ANA_POWER);
+ ana_pwr = reg;
+ reg &= ~SGTL5000_VAG_POWERUP;
+
+ /* Workaround for sgtl5000 rev 0x11 chip audio suspend failure
+ issue on mx25 */
+ /* reg &= ~SGTL5000_REFTOP_POWERUP; */
+
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, reg);
+ msleep(600);
+
+ reg &= ~SGTL5000_HP_POWERUP;
+ reg &= ~SGTL5000_LINE_OUT_POWERUP;
+ reg &= ~SGTL5000_DAC_POWERUP;
+ reg &= ~SGTL5000_ADC_POWERUP;
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, reg);
+
+ /* save ANA POWER register value for resume */
+ sgtl5000_write_reg_cache(codec, SGTL5000_CHIP_ANA_POWER,
+ ana_pwr);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define SGTL5000_RATES (SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000)
+
+#define SGTL5000_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai sgtl5000_dai = {
+ .name = "SGTL5000",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SGTL5000_RATES,
+ .formats = SGTL5000_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SGTL5000_RATES,
+ .formats = SGTL5000_FORMATS,
+ },
+ .ops = {
+ .prepare = sgtl5000_pcm_prepare,
+ .startup = sgtl5000_pcm_startup,
+ .shutdown = sgtl5000_pcm_shutdown,
+ .hw_params = sgtl5000_pcm_hw_params,
+ },
+ .dai_ops = {
+ .digital_mute = sgtl5000_digital_mute,
+ .set_fmt = sgtl5000_set_dai_fmt,
+ .set_sysclk = sgtl5000_set_dai_sysclk}
+};
+EXPORT_SYMBOL_GPL(sgtl5000_dai);
+
+static int sgtl5000_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int sgtl5000_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ unsigned int i;
+
+ /* Restore refs first in same order as in sgtl5000_init */
+ sgtl5000_restore_reg(codec, SGTL5000_CHIP_LINREG_CTRL);
+ sgtl5000_restore_reg(codec, SGTL5000_CHIP_ANA_POWER);
+ msleep(10);
+ sgtl5000_restore_reg(codec, SGTL5000_CHIP_REF_CTRL);
+ sgtl5000_restore_reg(codec, SGTL5000_CHIP_LINE_OUT_CTRL);
+
+ /* Restore everythine else */
+ for (i = 1; i < sizeof(all_reg) / sizeof(int); i++)
+ sgtl5000_restore_reg(codec, all_reg[i]);
+
+ sgtl5000_write(codec, SGTL5000_DAP_CTRL, 0);
+
+ /* Bring the codec back up to standby first to minimise pop/clicks */
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ sgtl5000_set_bias_level(codec, codec->suspend_bias_level);
+
+ return 0;
+}
+
+/*
+ * initialise the SGTL5000 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int sgtl5000_init(struct snd_soc_device *socdev)
+{
+ struct sgtl5000_platform_data *plat = socdev->codec_data;
+ struct snd_soc_codec *codec = socdev->codec;
+ struct i2c_client *client = codec->control_data;
+ struct sgtl5000_priv *sgtl5000 = codec->private_data;
+ u16 reg, ana_pwr, lreg_ctrl, ref_ctrl, lo_ctrl, short_ctrl, sss;
+ int vag;
+ unsigned int val;
+ int ret = 0;
+
+ val = sgtl5000_read(codec, SGTL5000_CHIP_ID);
+ if (((val & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
+ SGTL5000_PARTID_PART_ID) {
+ pr_err("Device with ID register %x is not a SGTL5000\n", val);
+ return -ENODEV;
+ }
+
+ sgtl5000->rev = (val & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+ dev_info(&client->dev, "SGTL5000 revision %d\n", sgtl5000->rev);
+
+ codec->name = "SGTL5000";
+ codec->owner = THIS_MODULE;
+ codec->read = sgtl5000_read_reg_cache;
+ codec->write = sgtl5000_write;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = sgtl5000_set_bias_level;
+ codec->dai = &sgtl5000_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = sizeof(sgtl5000_regs);
+ codec->reg_cache_step = 2;
+ codec->reg_cache = (void *)&sgtl5000_regs;
+ if (codec->reg_cache == NULL) {
+ dev_err(&client->dev, "Failed to allocate register cache\n");
+ return -ENOMEM;
+ }
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to create pcms\n");
+ return ret;
+ }
+
+ sgtl5000_sync_reg_cache(codec);
+
+ /* reset value */
+ ana_pwr = SGTL5000_DAC_STEREO |
+ SGTL5000_LINREG_SIMPLE_POWERUP |
+ SGTL5000_STARTUP_POWERUP |
+ SGTL5000_ADC_STEREO | SGTL5000_REFTOP_POWERUP;
+ lreg_ctrl = 0;
+ ref_ctrl = 0;
+ lo_ctrl = 0;
+ short_ctrl = 0;
+ sss = SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT;
+
+ /* workaround for rev 0x11: use vddd linear regulator */
+ if (!plat->vddd || (sgtl5000->rev >= 0x11)) {
+ /* set VDDD to 1.2v */
+ lreg_ctrl |= 0x8 << SGTL5000_LINREG_VDDD_SHIFT;
+ /* power internal linear regulator */
+ ana_pwr |= SGTL5000_LINEREG_D_POWERUP;
+ } else {
+ /* turn of startup power */
+ ana_pwr &= ~SGTL5000_STARTUP_POWERUP;
+ ana_pwr &= ~SGTL5000_LINREG_SIMPLE_POWERUP;
+ }
+ if (plat->vddio < 3100 && plat->vdda < 3100) {
+ /* Enable VDDC charge pump */
+ ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP;
+ }
+ if (plat->vddio >= 3100 && plat->vdda >= 3100) {
+ /* VDDC use VDDIO rail */
+ lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
+ if (plat->vddio >= 3100)
+ lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
+ SGTL5000_VDDC_MAN_ASSN_SHIFT;
+ }
+ /* If PLL is powered up (such as on power cycle) leave it on. */
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_ANA_POWER);
+ ana_pwr |= reg & (SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
+
+ /* set ADC/DAC ref voltage to vdda/2 */
+ vag = plat->vdda / 2;
+ if (vag <= SGTL5000_ANA_GND_BASE)
+ vag = 0;
+ else if (vag >= SGTL5000_ANA_GND_BASE + SGTL5000_ANA_GND_STP *
+ (SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT))
+ vag = SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT;
+ else
+ vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP;
+ ref_ctrl |= vag << SGTL5000_ANA_GND_SHIFT;
+
+ /* set line out ref voltage to vddio/2 */
+ vag = plat->vddio / 2;
+ if (vag <= SGTL5000_LINE_OUT_GND_BASE)
+ vag = 0;
+ else if (vag >= SGTL5000_LINE_OUT_GND_BASE + SGTL5000_LINE_OUT_GND_STP *
+ SGTL5000_LINE_OUT_GND_MAX)
+ vag = SGTL5000_LINE_OUT_GND_MAX;
+ else
+ vag = (vag - SGTL5000_LINE_OUT_GND_BASE) /
+ SGTL5000_LINE_OUT_GND_STP;
+ lo_ctrl |= vag << SGTL5000_LINE_OUT_GND_SHIFT;
+
+ /* enable small pop */
+ ref_ctrl |= SGTL5000_SMALL_POP;
+
+ /* Controls the output bias current for the lineout */
+ lo_ctrl |= (SGTL5000_LINE_OUT_CURRENT_360u << SGTL5000_LINE_OUT_CURRENT_SHIFT);
+
+ /* set short detect */
+ /* keep default */
+
+ /* set routing */
+ /* keep default, bypass DAP */
+
+ sgtl5000_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl);
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+ msleep(10);
+
+ /* For rev 0x11, if vddd linear reg has been enabled, we have
+ to disable simple reg to get proper VDDD voltage. */
+ if ((ana_pwr & SGTL5000_LINEREG_D_POWERUP) && (sgtl5000->rev >= 0x11)) {
+ ana_pwr &= ~SGTL5000_LINREG_SIMPLE_POWERUP;
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+ msleep(10);
+ }
+
+ sgtl5000_write(codec, SGTL5000_CHIP_REF_CTRL, ref_ctrl);
+ sgtl5000_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL, lo_ctrl);
+ sgtl5000_write(codec, SGTL5000_CHIP_SHORT_CTRL, short_ctrl);
+ sgtl5000_write(codec, SGTL5000_CHIP_SSS_CTRL, sss);
+ sgtl5000_write(codec, SGTL5000_CHIP_DIG_POWER, 0);
+
+ reg = SGTL5000_DAC_VOL_RAMP_EN |
+ SGTL5000_DAC_MUTE_RIGHT | SGTL5000_DAC_MUTE_LEFT;
+ sgtl5000_write(codec, SGTL5000_CHIP_ADCDAC_CTRL, reg);
+
+ if (cpu_is_mx25())
+ sgtl5000_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x01df);
+ else
+ sgtl5000_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f);
+
+ reg = sgtl5000_read(codec, SGTL5000_CHIP_ANA_ADC_CTRL);
+ reg &= ~SGTL5000_ADC_VOL_M6DB;
+ reg &= ~(SGTL5000_ADC_VOL_LEFT_MASK | SGTL5000_ADC_VOL_RIGHT_MASK);
+ reg |= (0xf << SGTL5000_ADC_VOL_LEFT_SHIFT)
+ | (0xf << SGTL5000_ADC_VOL_RIGHT_SHIFT);
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_ADC_CTRL, reg);
+
+ reg = SGTL5000_LINE_OUT_MUTE | SGTL5000_HP_MUTE |
+ SGTL5000_HP_ZCD_EN | SGTL5000_ADC_ZCD_EN;
+ sgtl5000_write(codec, SGTL5000_CHIP_ANA_CTRL, reg);
+
+ sgtl5000_write(codec, SGTL5000_CHIP_MIC_CTRL, 0);
+ sgtl5000_write(codec, SGTL5000_CHIP_CLK_TOP_CTRL, 0);
+ /* disable DAP */
+ sgtl5000_write(codec, SGTL5000_DAP_CTRL, 0);
+ /* TODO: initialize DAP */
+
+ sgtl5000_add_controls(codec);
+ sgtl5000_add_widgets(codec);
+
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "sgtl5000: failed to register card\n");
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_device *sgtl5000_socdev;
+
+static int sgtl5000_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct snd_soc_device *socdev = sgtl5000_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = sgtl5000_init(socdev);
+ if (ret < 0)
+ dev_err(&i2c->dev, "Device initialisation failed\n");
+
+ return ret;
+}
+
+static const struct i2c_device_id sgtl5000_id[] = {
+ {"sgtl5000-i2c", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, sgtl5000_id);
+
+static struct i2c_driver sgtl5000_i2c_driver = {
+ .driver = {
+ .name = "sgtl5000-i2c",
+ .owner = THIS_MODULE,
+ },
+ .probe = sgtl5000_i2c_probe,
+ .id_table = sgtl5000_id,
+};
+
+static int sgtl5000_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct sgtl5000_priv *sgtl5000;
+ int ret = 0;
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ sgtl5000 = kzalloc(sizeof(struct sgtl5000_priv), GFP_KERNEL);
+ if (sgtl5000 == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->private_data = sgtl5000;
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ sgtl5000_socdev = socdev;
+
+ ret = i2c_add_driver(&sgtl5000_i2c_driver);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "can't add i2c driver\n");
+ kfree(codec->private_data);
+ kfree(codec);
+ }
+
+ return ret;
+}
+
+/* power down chip */
+static int sgtl5000_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ i2c_del_driver(&sgtl5000_i2c_driver);
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_sgtl5000 = {
+ .probe = sgtl5000_probe,
+ .remove = sgtl5000_remove,
+ .suspend = sgtl5000_suspend,
+ .resume = sgtl5000_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_sgtl5000);
+
+MODULE_DESCRIPTION("ASoC SGTL5000 driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
new file mode 100644
index 000000000000..177be05b486c
--- /dev/null
+++ b/sound/soc/codecs/sgtl5000.h
@@ -0,0 +1,405 @@
+/*
+ * sgtl5000.h - SGTL5000 audio codec interface
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, 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.
+ */
+
+#ifndef _SGTL5000_H
+#define _SGTL5000_H
+
+#include <linux/i2c.h>
+
+extern struct snd_soc_dai sgtl5000_dai;
+extern struct snd_soc_codec_device soc_codec_dev_sgtl5000;
+
+/*
+ * Register values.
+ */
+#define SGTL5000_CHIP_ID 0x0000
+#define SGTL5000_CHIP_DIG_POWER 0x0002
+#define SGTL5000_CHIP_CLK_CTRL 0x0004
+#define SGTL5000_CHIP_I2S_CTRL 0x0006
+#define SGTL5000_CHIP_SSS_CTRL 0x000a
+#define SGTL5000_CHIP_ADCDAC_CTRL 0x000e
+#define SGTL5000_CHIP_DAC_VOL 0x0010
+#define SGTL5000_CHIP_PAD_STRENGTH 0x0014
+#define SGTL5000_CHIP_ANA_ADC_CTRL 0x0020
+#define SGTL5000_CHIP_ANA_HP_CTRL 0x0022
+#define SGTL5000_CHIP_ANA_CTRL 0x0024
+#define SGTL5000_CHIP_LINREG_CTRL 0x0026
+#define SGTL5000_CHIP_REF_CTRL 0x0028
+#define SGTL5000_CHIP_MIC_CTRL 0x002a
+#define SGTL5000_CHIP_LINE_OUT_CTRL 0x002c
+#define SGTL5000_CHIP_LINE_OUT_VOL 0x002e
+#define SGTL5000_CHIP_ANA_POWER 0x0030
+#define SGTL5000_CHIP_PLL_CTRL 0x0032
+#define SGTL5000_CHIP_CLK_TOP_CTRL 0x0034
+#define SGTL5000_CHIP_ANA_STATUS 0x0036
+#define SGTL5000_CHIP_SHORT_CTRL 0x003c
+#define SGTL5000_CHIP_ANA_TEST2 0x003a
+#define SGTL5000_DAP_CTRL 0x0100
+#define SGTL5000_DAP_PEQ 0x0102
+#define SGTL5000_DAP_BASS_ENHANCE 0x0104
+#define SGTL5000_DAP_BASS_ENHANCE_CTRL 0x0106
+#define SGTL5000_DAP_AUDIO_EQ 0x0108
+#define SGTL5000_DAP_SURROUND 0x010a
+#define SGTL5000_DAP_FLT_COEF_ACCESS 0x010c
+#define SGTL5000_DAP_COEF_WR_B0_MSB 0x010e
+#define SGTL5000_DAP_COEF_WR_B0_LSB 0x0110
+#define SGTL5000_DAP_EQ_BASS_BAND0 0x0116
+#define SGTL5000_DAP_EQ_BASS_BAND1 0x0118
+#define SGTL5000_DAP_EQ_BASS_BAND2 0x011a
+#define SGTL5000_DAP_EQ_BASS_BAND3 0x011c
+#define SGTL5000_DAP_EQ_BASS_BAND4 0x011e
+#define SGTL5000_DAP_MAIN_CHAN 0x0120
+#define SGTL5000_DAP_MIX_CHAN 0x0122
+#define SGTL5000_DAP_AVC_CTRL 0x0124
+#define SGTL5000_DAP_AVC_THRESHOLD 0x0126
+#define SGTL5000_DAP_AVC_ATTACK 0x0128
+#define SGTL5000_DAP_AVC_DECAY 0x012a
+#define SGTL5000_DAP_COEF_WR_B1_MSB 0x012c
+#define SGTL5000_DAP_COEF_WR_B1_LSB 0x012e
+#define SGTL5000_DAP_COEF_WR_B2_MSB 0x0130
+#define SGTL5000_DAP_COEF_WR_B2_LSB 0x0132
+#define SGTL5000_DAP_COEF_WR_A1_MSB 0x0134
+#define SGTL5000_DAP_COEF_WR_A1_LSB 0x0136
+#define SGTL5000_DAP_COEF_WR_A2_MSB 0x0138
+#define SGTL5000_DAP_COEF_WR_A2_LSB 0x013a
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * SGTL5000_CHIP_ID
+ */
+#define SGTL5000_PARTID_MASK 0xff00
+#define SGTL5000_PARTID_SHIFT 8
+#define SGTL5000_PARTID_WIDTH 8
+#define SGTL5000_PARTID_PART_ID 0xa0
+#define SGTL5000_REVID_MASK 0x00ff
+#define SGTL5000_REVID_SHIFT 0
+#define SGTL5000_REVID_WIDTH 8
+
+/*
+ * SGTL5000_CHIP_DIG_POWER
+ */
+#define SGTL5000_ADC_EN 0x0040
+#define SGTL5000_DAC_EN 0x0020
+#define SGTL5000_DAP_POWERUP 0x0010
+#define SGTL5000_I2S_OUT_POWERUP 0x0002
+#define SGTL5000_I2S_IN_POWERUP 0x0001
+
+/*
+ * SGTL5000_CHIP_CLK_CTRL
+ */
+#define SGTL5000_SYS_FS_MASK 0x00c0
+#define SGTL5000_SYS_FS_SHIFT 2
+#define SGTL5000_SYS_FS_WIDTH 2
+#define SGTL5000_SYS_FS_32k 0x0
+#define SGTL5000_SYS_FS_44_1k 0x1
+#define SGTL5000_SYS_FS_48k 0x2
+#define SGTL5000_SYS_FS_96k 0x3
+#define SGTL5000_MCLK_FREQ_MASK 0x0003
+#define SGTL5000_MCLK_FREQ_SHIFT 0
+#define SGTL5000_MCLK_FREQ_WIDTH 2
+#define SGTL5000_MCLK_FREQ_256FS 0x0
+#define SGTL5000_MCLK_FREQ_384FS 0x1
+#define SGTL5000_MCLK_FREQ_512FS 0x2
+#define SGTL5000_MCLK_FREQ_PLL 0x3
+
+/*
+ * SGTL5000_CHIP_I2S_CTRL
+ */
+#define SGTL5000_I2S_SCLKFREQ_MASK 0x0100
+#define SGTL5000_I2S_SCLKFREQ_SHIFT 8
+#define SGTL5000_I2S_SCLKFREQ_WIDTH 1
+#define SGTL5000_I2S_SCLKFREQ_64FS 0x0
+#define SGTL5000_I2S_SCLKFREQ_32FS 0x1 /* Not for RJ mode */
+#define SGTL5000_I2S_MASTER 0x0080
+#define SGTL5000_I2S_SCLK_INV 0x0040
+#define SGTL5000_I2S_DLEN_MASK 0x0030
+#define SGTL5000_I2S_DLEN_SHIFT 4
+#define SGTL5000_I2S_DLEN_WIDTH 2
+#define SGTL5000_I2S_DLEN_32 0x0
+#define SGTL5000_I2S_DLEN_24 0x1
+#define SGTL5000_I2S_DLEN_20 0x2
+#define SGTL5000_I2S_DLEN_16 0x3
+#define SGTL5000_I2S_MODE_MASK 0x000c
+#define SGTL5000_I2S_MODE_SHIFT 2
+#define SGTL5000_I2S_MODE_WIDTH 2
+#define SGTL5000_I2S_MODE_I2S_LJ 0x0
+#define SGTL5000_I2S_MODE_RJ 0x1
+#define SGTL5000_I2S_MODE_PCM 0x2
+#define SGTL5000_I2S_LRALIGN 0x0002
+#define SGTL5000_I2S_LRPOL 0x0001 /* set for which mode */
+
+/*
+ * SGTL5000_CHIP_SSS_CTRL
+ */
+#define SGTL5000_DAP_MIX_LRSWAP 0x4000
+#define SGTL5000_DAP_LRSWAP 0x2000
+#define SGTL5000_DAC_LRSWAP 0x1000
+#define SGTL5000_I2S_OUT_LRSWAP 0x0400
+#define SGTL5000_DAP_MIX_SEL_MASK 0x0300
+#define SGTL5000_DAP_MIX_SEL_SHIFT 8
+#define SGTL5000_DAP_MIX_SEL_WIDTH 2
+#define SGTL5000_DAP_MIX_SEL_ADC 0x0
+#define SGTL5000_DAP_MIX_SEL_I2S_IN 0x1
+#define SGTL5000_DAP_SEL_MASK 0x00c0
+#define SGTL5000_DAP_SEL_SHIFT 6
+#define SGTL5000_DAP_SEL_WIDTH 2
+#define SGTL5000_DAP_SEL_ADC 0x0
+#define SGTL5000_DAP_SEL_I2S_IN 0x1
+#define SGTL5000_DAC_SEL_MASK 0x0030
+#define SGTL5000_DAC_SEL_SHIFT 4
+#define SGTL5000_DAC_SEL_WIDTH 2
+#define SGTL5000_DAC_SEL_ADC 0x0
+#define SGTL5000_DAC_SEL_I2S_IN 0x1
+#define SGTL5000_DAC_SEL_DAP 0x3
+#define SGTL5000_I2S_OUT_SEL_MASK 0x0003
+#define SGTL5000_I2S_OUT_SEL_SHIFT 0
+#define SGTL5000_I2S_OUT_SEL_WIDTH 2
+#define SGTL5000_I2S_OUT_SEL_ADC 0x0
+#define SGTL5000_I2S_OUT_SEL_I2S_IN 0x1
+#define SGTL5000_I2S_OUT_SEL_DAP 0x3
+
+/*
+ * SGTL5000_CHIP_ADCDAC_CTRL
+ */
+#define SGTL5000_VOL_BUSY_DAC_RIGHT 0x2000
+#define SGTL5000_VOL_BUSY_DAC_LEFT 0x1000
+#define SGTL5000_DAC_VOL_RAMP_EN 0x0200
+#define SGTL5000_DAC_VOL_RAMP_EXPO 0x0100
+#define SGTL5000_DAC_MUTE_RIGHT 0x0008
+#define SGTL5000_DAC_MUTE_LEFT 0x0004
+#define SGTL5000_ADC_HPF_FREEZE 0x0002
+#define SGTL5000_ADC_HPF_BYPASS 0x0001
+
+/*
+ * SGTL5000_CHIP_DAC_VOL
+ */
+#define SGTL5000_DAC_VOL_RIGHT_MASK 0xff00
+#define SGTL5000_DAC_VOL_RIGHT_SHIFT 8
+#define SGTL5000_DAC_VOL_RIGHT_WIDTH 8
+#define SGTL5000_DAC_VOL_LEFT_MASK 0x00ff
+#define SGTL5000_DAC_VOL_LEFT_SHIFT 0
+#define SGTL5000_DAC_VOL_LEFT_WIDTH 8
+
+/*
+ * SGTL5000_CHIP_PAD_STRENGTH
+ */
+#define SGTL5000_PAD_I2S_LRCLK_MASK 0x0300
+#define SGTL5000_PAD_I2S_LRCLK_SHIFT 8
+#define SGTL5000_PAD_I2S_LRCLK_WIDTH 2
+#define SGTL5000_PAD_I2S_SCLK_MASK 0x00c0
+#define SGTL5000_PAD_I2S_SCLK_SHIFT 6
+#define SGTL5000_PAD_I2S_SCLK_WIDTH 2
+#define SGTL5000_PAD_I2S_DOUT_MASK 0x0030
+#define SGTL5000_PAD_I2S_DOUT_SHIFT 4
+#define SGTL5000_PAD_I2S_DOUT_WIDTH 2
+#define SGTL5000_PAD_I2C_SDA_MASK 0x000c
+#define SGTL5000_PAD_I2C_SDA_SHIFT 2
+#define SGTL5000_PAD_I2C_SDA_WIDTH 2
+#define SGTL5000_PAD_I2C_SCL_MASK 0x0003
+#define SGTL5000_PAD_I2C_SCL_SHIFT 0
+#define SGTL5000_PAD_I2C_SCL_WIDTH 2
+
+/*
+ * SGTL5000_CHIP_ANA_ADC_CTRL
+ */
+#define SGTL5000_ADC_VOL_M6DB 0x0100
+#define SGTL5000_ADC_VOL_RIGHT_MASK 0x00f0
+#define SGTL5000_ADC_VOL_RIGHT_SHIFT 4
+#define SGTL5000_ADC_VOL_RIGHT_WIDTH 4
+#define SGTL5000_ADC_VOL_LEFT_MASK 0x000f
+#define SGTL5000_ADC_VOL_LEFT_SHIFT 0
+#define SGTL5000_ADC_VOL_LEFT_WIDTH 4
+
+/*
+ * SGTL5000_CHIP_ANA_HP_CTRL
+ */
+#define SGTL5000_HP_VOL_RIGHT_MASK 0x7f00
+#define SGTL5000_HP_VOL_RIGHT_SHIFT 8
+#define SGTL5000_HP_VOL_RIGHT_WIDTH 7
+#define SGTL5000_HP_VOL_LEFT_MASK 0x007f
+#define SGTL5000_HP_VOL_LEFT_SHIFT 0
+#define SGTL5000_HP_VOL_LEFT_WIDTH 7
+
+/*
+ * SGTL5000_CHIP_ANA_CTRL
+ */
+#define SGTL5000_LINE_OUT_MUTE 0x0100
+#define SGTL5000_HP_SEL_MASK 0x0040
+#define SGTL5000_HP_SEL_SHIFT 6
+#define SGTL5000_HP_SEL_WIDTH 1
+#define SGTL5000_HP_SEL_DAC 0x0
+#define SGTL5000_HP_SEL_LINE_IN 0x1
+#define SGTL5000_HP_ZCD_EN 0x0020
+#define SGTL5000_HP_MUTE 0x0010
+#define SGTL5000_ADC_SEL_MASK 0x0004
+#define SGTL5000_ADC_SEL_SHIFT 2
+#define SGTL5000_ADC_SEL_WIDTH 1
+#define SGTL5000_ADC_SEL_MIC 0x0
+#define SGTL5000_ADC_SEL_LINE_IN 0x1
+#define SGTL5000_ADC_ZCD_EN 0x0002
+#define SGTL5000_ADC_MUTE 0x0001
+
+/*
+ * SGTL5000_CHIP_LINREG_CTRL
+ */
+#define SGTL5000_VDDC_MAN_ASSN_MASK 0x0040
+#define SGTL5000_VDDC_MAN_ASSN_SHIFT 6
+#define SGTL5000_VDDC_MAN_ASSN_WIDTH 1
+#define SGTL5000_VDDC_MAN_ASSN_VDDA 0x0
+#define SGTL5000_VDDC_MAN_ASSN_VDDIO 0x1
+#define SGTL5000_VDDC_ASSN_OVRD 0x0020
+#define SGTL5000_LINREG_VDDD_MASK 0x000f
+#define SGTL5000_LINREG_VDDD_SHIFT 0
+#define SGTL5000_LINREG_VDDD_WIDTH 4
+
+/*
+ * SGTL5000_CHIP_REF_CTRL
+ */
+#define SGTL5000_ANA_GND_MASK 0x01f0
+#define SGTL5000_ANA_GND_SHIFT 4
+#define SGTL5000_ANA_GND_WIDTH 5
+#define SGTL5000_ANA_GND_BASE 800 /* mv */
+#define SGTL5000_ANA_GND_STP 25 /*mv */
+#define SGTL5000_BIAS_CTRL_MASK 0x000e
+#define SGTL5000_BIAS_CTRL_SHIFT 1
+#define SGTL5000_BIAS_CTRL_WIDTH 3
+#define SGTL5000_SMALL_POP 0x0001
+
+/*
+ * SGTL5000_CHIP_MIC_CTRL
+ */
+#define SGTL5000_BIAS_R_MASK 0x0200
+#define SGTL5000_BIAS_R_SHIFT 8
+#define SGTL5000_BIAS_R_WIDTH 2
+#define SGTL5000_BIAS_R_off 0x0
+#define SGTL5000_BIAS_R_2K 0x1
+#define SGTL5000_BIAS_R_4k 0x2
+#define SGTL5000_BIAS_R_8k 0x3
+#define SGTL5000_BIAS_VOLT_MASK 0x0070
+#define SGTL5000_BIAS_VOLT_SHIFT 4
+#define SGTL5000_BIAS_VOLT_WIDTH 3
+#define SGTL5000_MIC_GAIN_MASK 0x0003
+#define SGTL5000_MIC_GAIN_SHIFT 0
+#define SGTL5000_MIC_GAIN_WIDTH 2
+
+/*
+ * SGTL5000_CHIP_LINE_OUT_CTRL
+ */
+#define SGTL5000_LINE_OUT_CURRENT_MASK 0x0f00
+#define SGTL5000_LINE_OUT_CURRENT_SHIFT 8
+#define SGTL5000_LINE_OUT_CURRENT_WIDTH 4
+#define SGTL5000_LINE_OUT_CURRENT_180u 0x0
+#define SGTL5000_LINE_OUT_CURRENT_270u 0x1
+#define SGTL5000_LINE_OUT_CURRENT_360u 0x3
+#define SGTL5000_LINE_OUT_CURRENT_450u 0x7
+#define SGTL5000_LINE_OUT_CURRENT_540u 0xf
+#define SGTL5000_LINE_OUT_GND_MASK 0x003f
+#define SGTL5000_LINE_OUT_GND_SHIFT 0
+#define SGTL5000_LINE_OUT_GND_WIDTH 6
+#define SGTL5000_LINE_OUT_GND_BASE 800 /* mv */
+#define SGTL5000_LINE_OUT_GND_STP 25
+#define SGTL5000_LINE_OUT_GND_MAX 0x23
+
+/*
+ * SGTL5000_CHIP_LINE_OUT_VOL
+ */
+#define SGTL5000_LINE_OUT_VOL_RIGHT_MASK 0x1f00
+#define SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT 8
+#define SGTL5000_LINE_OUT_VOL_RIGHT_WIDTH 5
+#define SGTL5000_LINE_OUT_VOL_LEFT_MASK 0x001f
+#define SGTL5000_LINE_OUT_VOL_LEFT_SHIFT 0
+#define SGTL5000_LINE_OUT_VOL_LEFT_WIDTH 5
+
+/*
+ * SGTL5000_CHIP_ANA_POWER
+ */
+#define SGTL5000_DAC_STEREO 0x4000
+#define SGTL5000_LINREG_SIMPLE_POWERUP 0x2000
+#define SGTL5000_STARTUP_POWERUP 0x1000
+#define SGTL5000_VDDC_CHRGPMP_POWERUP 0x0800
+#define SGTL5000_PLL_POWERUP 0x0400
+#define SGTL5000_LINEREG_D_POWERUP 0x0200
+#define SGTL5000_VCOAMP_POWERUP 0x0100
+#define SGTL5000_VAG_POWERUP 0x0080
+#define SGTL5000_ADC_STEREO 0x0040
+#define SGTL5000_REFTOP_POWERUP 0x0020
+#define SGTL5000_HP_POWERUP 0x0010
+#define SGTL5000_DAC_POWERUP 0x0008
+#define SGTL5000_CAPLESS_HP_POWERUP 0x0004
+#define SGTL5000_ADC_POWERUP 0x0002
+#define SGTL5000_LINE_OUT_POWERUP 0x0001
+
+/*
+ * SGTL5000_CHIP_PLL_CTRL
+ */
+#define SGTL5000_PLL_INT_DIV_MASK 0xf800
+#define SGTL5000_PLL_INT_DIV_SHIFT 11
+#define SGTL5000_PLL_INT_DIV_WIDTH 5
+#define SGTL5000_PLL_FRAC_DIV_MASK 0x0700
+#define SGTL5000_PLL_FRAC_DIV_SHIFT 0
+#define SGTL5000_PLL_FRAC_DIV_WIDTH 11
+
+/*
+ * SGTL5000_CHIP_CLK_TOP_CTRL
+ */
+#define SGTL5000_INT_OSC_EN 0x0800
+#define SGTL5000_INPUT_FREQ_DIV2 0x0008
+
+/*
+ * SGTL5000_CHIP_ANA_STATUS
+ */
+#define SGTL5000_HP_LRSHORT 0x0200
+#define SGTL5000_CAPLESS_SHORT 0x0100
+#define SGTL5000_PLL_LOCKED 0x0010
+
+/*
+ * SGTL5000_CHIP_SHORT_CTRL
+ */
+#define SGTL5000_LVLADJR_MASK 0x7000
+#define SGTL5000_LVLADJR_SHIFT 12
+#define SGTL5000_LVLADJR_WIDTH 3
+#define SGTL5000_LVLADJL_MASK 0x0700
+#define SGTL5000_LVLADJL_SHIFT 8
+#define SGTL5000_LVLADJL_WIDTH 3
+#define SGTL5000_LVLADJC_MASK 0x0070
+#define SGTL5000_LVLADJC_SHIFT 4
+#define SGTL5000_LVLADJC_WIDTH 3
+#define SGTL5000_LR_SHORT_MOD_MASK 0x000c
+#define SGTL5000_LR_SHORT_MOD_SHIFT 2
+#define SGTL5000_LR_SHORT_MOD_WIDTH 2
+#define SGTL5000_CM_SHORT_MOD_MASK 0x0003
+#define SGTL5000_CM_SHORT_MOD_SHIFT 0
+#define SGTL5000_CM_SHORT_MOD_WIDTH 2
+
+/*
+ *SGTL5000_CHIP_ANA_TEST2
+ */
+#define SGTL5000_MONO_DAC 0x1000
+
+/*
+ * SGTL5000_DAP_CTRL
+ */
+#define SGTL5000_DAP_MIX_EN 0x0010
+#define SGTL5000_DAP_EN 0x0001
+
+#define SGTL5000_SYSCLK 0x00
+#define SGTL5000_LRCLK 0x01
+
+struct sgtl5000_platform_data {
+ int vddio; /* voltage of VDDIO (mv) */
+ int vdda; /* voltage of vdda (mv) */
+ int vddd; /* voltage of vddd (mv), 0 if not connected */
+};
+
+#endif
diff --git a/sound/soc/codecs/stmp378x_codec.c b/sound/soc/codecs/stmp378x_codec.c
new file mode 100644
index 000000000000..d5a4d0656fe9
--- /dev/null
+++ b/sound/soc/codecs/stmp378x_codec.c
@@ -0,0 +1,929 @@
+/*
+ * ALSA codec for Freescale STMP378X ADC/DAC
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <asm/dma.h>
+
+#include <mach/regs-apbx.h>
+#include <mach/regs-audioin.h>
+#include <mach/regs-audioout.h>
+#include <mach/regs-rtc.h>
+
+#include "stmp378x_codec.h"
+
+#define BV_AUDIOIN_ADCVOL_SELECT__MIC 0x00 /* missing define */
+
+#define STMP378X_VERSION "0.1"
+struct stmp378x_codec_priv {
+ struct device *dev;
+ struct clk *clk;
+};
+
+/*
+ * ALSA API
+ */
+static u32 adc_regmap[] = {
+ HW_AUDIOOUT_CTRL_ADDR,
+ HW_AUDIOOUT_STAT_ADDR,
+ HW_AUDIOOUT_DACSRR_ADDR,
+ HW_AUDIOOUT_DACVOLUME_ADDR,
+ HW_AUDIOOUT_DACDEBUG_ADDR,
+ HW_AUDIOOUT_HPVOL_ADDR,
+ HW_AUDIOOUT_PWRDN_ADDR,
+ HW_AUDIOOUT_REFCTRL_ADDR,
+ HW_AUDIOOUT_ANACTRL_ADDR,
+ HW_AUDIOOUT_TEST_ADDR,
+ HW_AUDIOOUT_BISTCTRL_ADDR,
+ HW_AUDIOOUT_BISTSTAT0_ADDR,
+ HW_AUDIOOUT_BISTSTAT1_ADDR,
+ HW_AUDIOOUT_ANACLKCTRL_ADDR,
+ HW_AUDIOOUT_DATA_ADDR,
+ HW_AUDIOOUT_SPEAKERCTRL_ADDR,
+ HW_AUDIOOUT_VERSION_ADDR,
+ HW_AUDIOIN_CTRL_ADDR,
+ HW_AUDIOIN_STAT_ADDR,
+ HW_AUDIOIN_ADCSRR_ADDR,
+ HW_AUDIOIN_ADCVOLUME_ADDR,
+ HW_AUDIOIN_ADCDEBUG_ADDR,
+ HW_AUDIOIN_ADCVOL_ADDR,
+ HW_AUDIOIN_MICLINE_ADDR,
+ HW_AUDIOIN_ANACLKCTRL_ADDR,
+ HW_AUDIOIN_DATA_ADDR,
+};
+
+static u16 stmp378x_audio_regs[ADC_REGNUM];
+
+static u8 dac_volumn_control_word[] = {
+ 0x37, 0x5e, 0x7e, 0x8e,
+ 0x9e, 0xae, 0xb6, 0xbe,
+ 0xc6, 0xce, 0xd6, 0xde,
+ 0xe6, 0xee, 0xf6, 0xfe,
+};
+
+/*
+ * ALSA core supports only 16 bit registers. It means we have to simulate it
+ * by virtually splitting a 32bit ADC/DAC registers into two halves
+ * high (bits 31:16) and low (bits 15:0). The routins abow detects which part
+ * of 32bit register is accessed.
+ */
+static void stmp378x_codec_write_cache(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg < ADC_REGNUM)
+ cache[reg] = value;
+}
+
+static int stmp378x_codec_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ unsigned int reg_val;
+ unsigned int mask = 0xffff;
+
+ if (reg >= ADC_REGNUM)
+ return -EIO;
+
+ stmp378x_codec_write_cache(codec, reg, value);
+
+ if (reg & 0x1) {
+ mask <<= 16;
+ value <<= 16;
+ }
+
+ reg_val = __raw_readl(adc_regmap[reg >> 1]);
+ reg_val = (reg_val & ~mask) | value;
+ __raw_writel(reg_val, adc_regmap[reg >> 1]);
+
+ return 0;
+}
+
+static unsigned int stmp378x_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ unsigned int reg_val;
+
+ if (reg >= ADC_REGNUM)
+ return -1;
+
+ reg_val = __raw_readl(adc_regmap[reg >> 1]);
+ if (reg & 1)
+ reg_val >>= 16;
+
+ return reg_val & 0xffff;
+}
+
+static unsigned int stmp378x_codec_read_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= ADC_REGNUM)
+ return -EINVAL;
+ return cache[reg];
+}
+
+static void stmp378x_codec_sync_reg_cache(struct snd_soc_codec *codec)
+{
+ int reg;
+ for (reg = 0; reg < ADC_REGNUM; reg += 1)
+ stmp378x_codec_write_cache(codec, reg,
+ stmp378x_codec_read(codec, reg));
+}
+
+
+static int stmp378x_codec_restore_reg(struct snd_soc_codec *codec, unsigned int reg)
+{
+ unsigned int cached_val, hw_val;
+
+ cached_val = stmp378x_codec_read_cache(codec, reg);
+ hw_val = stmp378x_codec_read(codec, reg);
+
+ if (hw_val != cached_val)
+ return stmp378x_codec_write(codec, reg, cached_val);
+
+ return 0;
+}
+
+static int dac_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xf;
+ return 0;
+}
+
+static int dac_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg, l, r;
+ int i;
+
+ reg = HW_AUDIOOUT_DACVOLUME_RD();
+
+ l = (reg & BM_AUDIOOUT_DACVOLUME_VOLUME_LEFT) >>
+ BP_AUDIOOUT_DACVOLUME_VOLUME_LEFT;
+ r = (reg & BM_AUDIOOUT_DACVOLUME_VOLUME_RIGHT) >>
+ BP_AUDIOOUT_DACVOLUME_VOLUME_RIGHT;
+ /*Left channel*/
+ i = 0;
+ while (i < 16) {
+ if (l == dac_volumn_control_word[i]) {
+ ucontrol->value.integer.value[0] = i;
+ break;
+ }
+ i++;
+ }
+ if (i == 16)
+ ucontrol->value.integer.value[0] = i;
+ /*Right channel*/
+ i = 0;
+ while (i < 16) {
+ if (r == dac_volumn_control_word[i]) {
+ ucontrol->value.integer.value[1] = i;
+ break;
+ }
+ i++;
+ }
+ if (i == 16)
+ ucontrol->value.integer.value[1] = i;
+
+ return 0;
+}
+
+static int dac_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int reg, l, r;
+ int i;
+
+ i = ucontrol->value.integer.value[0];
+ l = dac_volumn_control_word[i];
+ /*Get dac volume for left channel*/
+ reg = BF_AUDIOOUT_DACVOLUME_VOLUME_LEFT(l);
+
+ i = ucontrol->value.integer.value[1];
+ r = dac_volumn_control_word[i];
+ /*Get dac volume for right channel*/
+ reg = reg | BF_AUDIOOUT_DACVOLUME_VOLUME_RIGHT(r);
+
+ /*Clear left/right dac volume*/
+ HW_AUDIOOUT_DACVOLUME_CLR(BM_AUDIOOUT_DACVOLUME_VOLUME_LEFT |
+ BM_AUDIOOUT_DACVOLUME_VOLUME_RIGHT);
+ HW_AUDIOOUT_DACVOLUME_SET(reg);
+}
+
+static const char *stmp378x_codec_adc_input_sel[] =
+ {"Mic", "Line In 1", "Head Phone", "Line In 2"};
+
+static const char *stmp378x_codec_hp_output_sel[] =
+ {"DAC Out", "Line In 1"};
+
+static const char *stmp378x_codec_adc_3d_sel[] =
+ {"Off", "Low", "Medium", "High"};
+
+static const struct soc_enum stmp378x_codec_enum[] = {
+ SOC_ENUM_SINGLE(ADC_ADCVOL_L, 12, 4, stmp378x_codec_adc_input_sel),
+ SOC_ENUM_SINGLE(ADC_ADCVOL_L, 4, 4, stmp378x_codec_adc_input_sel),
+ SOC_ENUM_SINGLE(DAC_HPVOL_H, 0, 2, stmp378x_codec_hp_output_sel),
+ SOC_ENUM_SINGLE(DAC_CTRL_L, 8, 4, stmp378x_codec_adc_3d_sel),
+};
+
+/* Codec controls */
+static const struct snd_kcontrol_new stmp378x_snd_controls[] = {
+ /* Playback Volume */
+ {.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DAC Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = dac_info_volsw,
+ .get = dac_get_volsw,
+ .put = dac_put_volsw,
+ },
+
+ SOC_DOUBLE_R("DAC Playback Switch",
+ DAC_VOLUME_H, DAC_VOLUME_L, 8, 0x01, 1),
+ SOC_DOUBLE("HP Playback Volume", DAC_HPVOL_L, 8, 0, 0x7F, 1),
+ SOC_SINGLE("HP Playback Switch", DAC_HPVOL_H, 8, 0x1, 1),
+ SOC_SINGLE("Speaker Playback Switch", DAC_SPEAKERCTRL_H, 8, 0x1, 1),
+
+ /* Capture Volume */
+ SOC_DOUBLE_R("ADC Capture Volume",
+ ADC_VOLUME_H, ADC_VOLUME_L, 0, 0xFF, 0),
+ SOC_DOUBLE("ADC PGA Capture Volume", ADC_ADCVOL_L, 8, 0, 0x0F, 0),
+ SOC_SINGLE("ADC PGA Capture Switch", ADC_ADCVOL_H, 8, 0x1, 1),
+ SOC_SINGLE("Mic PGA Capture Volume", ADC_MICLINE_L, 0, 0x03, 0),
+
+ /* Virtual 3D effect */
+ SOC_ENUM("3D effect", stmp378x_codec_enum[3]),
+};
+
+/* add non dapm controls */
+static int stmp378x_codec_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(stmp378x_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&stmp378x_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Left ADC Mux */
+static const struct snd_kcontrol_new stmp378x_left_adc_controls =
+SOC_DAPM_ENUM("Route", stmp378x_codec_enum[0]);
+
+/* Right ADC Mux */
+static const struct snd_kcontrol_new stmp378x_right_adc_controls =
+SOC_DAPM_ENUM("Route", stmp378x_codec_enum[1]);
+
+/* Head Phone Mux */
+static const struct snd_kcontrol_new stmp378x_hp_controls =
+SOC_DAPM_ENUM("Route", stmp378x_codec_enum[2]);
+
+static const struct snd_soc_dapm_widget stmp378x_codec_widgets[] = {
+
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", DAC_PWRDN_L, 8, 1),
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", DAC_PWRDN_H, 0, 1),
+
+ SND_SOC_DAPM_DAC("DAC", "Playback", DAC_PWRDN_L, 12, 1),
+
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+ &stmp378x_left_adc_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+ &stmp378x_right_adc_controls),
+ SND_SOC_DAPM_MUX("HP Mux", SND_SOC_NOPM, 0, 0,
+ &stmp378x_hp_controls),
+
+ SND_SOC_DAPM_PGA("HP_AMP", DAC_PWRDN_L, 0, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("HP_CAPLESS", DAC_PWRDN_L, 4, 1, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPK_AMP", DAC_PWRDN_H, 8, 1, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("LINE1L"),
+ SND_SOC_DAPM_INPUT("LINE1R"),
+ SND_SOC_DAPM_INPUT("LINE2L"),
+ SND_SOC_DAPM_INPUT("LINE2R"),
+ SND_SOC_DAPM_INPUT("MIC"),
+
+ SND_SOC_DAPM_OUTPUT("SPEAKER"),
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+};
+static const struct snd_soc_dapm_route intercon[] = {
+
+ /* Left ADC Mux */
+ {"Left ADC Mux", "Mic", "MIC"},
+ {"Left ADC Mux", "Line In 1", "LINE1L"},
+ {"Left ADC Mux", "Line In 2", "LINE2L"},
+ {"Left ADC Mux", "Head Phone", "HPL"},
+
+ /* Right ADC Mux */
+ {"Right ADC Mux", "Mic", "MIC"},
+ {"Right ADC Mux", "Line In 1", "LINE1R"},
+ {"Right ADC Mux", "Line In 2", "LINE2R"},
+ {"Right ADC Mux", "Head Phone", "HPR"},
+
+ /* ADC */
+ {"Left ADC", NULL, "Left ADC Mux"},
+ {"Right ADC", NULL, "Right ADC Mux"},
+
+ /* HP Mux */
+ {"HP Mux", "DAC Out", "DAC"},
+ {"HP Mux", "Line In 1", "LINE1L"},
+ {"HP Mux", "Line In 1", "LINE1R"},
+
+ /* HP output */
+ {"HP_CAPLESS", NULL, "HP Mux"},
+ {"HP_AMP", NULL, "HP_CAPLESS"},
+ {"HPR", NULL, "HP_AMP"},
+ {"HPL", NULL, "HP_AMP"},
+
+ /* Speaker amp */
+ {"SPK_AMP", NULL, "DAC"},
+ {"SPEAKER", NULL, "SPK_AMP"},
+};
+
+static int stmp378x_codec_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, stmp378x_codec_widgets,
+ ARRAY_SIZE(stmp378x_codec_widgets));
+
+ /* set up audio path interconnects */
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+struct dac_srr {
+ u32 rate;
+ u32 basemult;
+ u32 src_hold;
+ u32 src_int;
+ u32 src_frac;
+};
+
+static struct dac_srr srr_values[] = {
+ {192000, 0x4, 0x0, 0x0F, 0x13FF},
+ {176400, 0x4, 0x0, 0x11, 0x0037},
+ {128000, 0x4, 0x0, 0x17, 0x0E00},
+ {96000, 0x2, 0x0, 0x0F, 0x13FF},
+ {88200, 0x2, 0x0, 0x11, 0x0037},
+ {64000, 0x2, 0x0, 0x17, 0x0E00},
+ {48000, 0x1, 0x0, 0x0F, 0x13FF},
+ {44100, 0x1, 0x0, 0x11, 0x0037},
+ {32000, 0x1, 0x0, 0x17, 0x0E00},
+ {24000, 0x1, 0x1, 0x0F, 0x13FF},
+ {22050, 0x1, 0x1, 0x11, 0x0037},
+ {16000, 0x1, 0x1, 0x17, 0x0E00},
+ {12000, 0x1, 0x3, 0x0F, 0x13FF},
+ {11025, 0x1, 0x3, 0x11, 0x0037},
+ {8000, 0x1, 0x3, 0x17, 0x0E00}
+};
+
+static inline int get_srr_values(int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(srr_values); i++)
+ if (srr_values[i].rate == rate)
+ return i;
+
+ return -1;
+}
+
+static int stmp378x_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+ int i;
+ u32 srr_value = 0;
+ u32 src_hold = 0;
+
+ i = get_srr_values(params_rate(params));
+ if (i < 0)
+ printk(KERN_WARNING "%s doesn't support rate %d\n",
+ codec->name, params_rate(params));
+ else {
+ src_hold = srr_values[i].src_hold;
+
+ srr_value =
+ BF_AUDIOOUT_DACSRR_BASEMULT(srr_values[i].basemult) |
+ BF_AUDIOOUT_DACSRR_SRC_INT(srr_values[i].src_int) |
+ BF_AUDIOOUT_DACSRR_SRC_FRAC(srr_values[i].src_frac) |
+ BF_AUDIOOUT_DACSRR_SRC_HOLD(src_hold);
+
+ if (playback)
+ HW_AUDIOOUT_DACSRR_WR(srr_value);
+ else
+ HW_AUDIOIN_ADCSRR_WR(srr_value);
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ if (playback)
+ HW_AUDIOOUT_CTRL_SET(BM_AUDIOOUT_CTRL_WORD_LENGTH);
+ else
+ HW_AUDIOIN_CTRL_SET(BM_AUDIOIN_CTRL_WORD_LENGTH);
+
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ if (playback)
+ HW_AUDIOOUT_CTRL_CLR(BM_AUDIOOUT_CTRL_WORD_LENGTH);
+ else
+ HW_AUDIOIN_CTRL_CLR(BM_AUDIOIN_CTRL_WORD_LENGTH);
+
+ break;
+
+ default:
+ printk(KERN_WARNING "%s doesn't support format %d\n",
+ codec->name, params_format(params));
+
+ }
+
+ return 0;
+}
+
+static int stmp378x_codec_dig_mute(struct snd_soc_dai *dai, int mute)
+{
+ u32 dac_mask = BM_AUDIOOUT_DACVOLUME_MUTE_LEFT |
+ BM_AUDIOOUT_DACVOLUME_MUTE_RIGHT;
+
+ if (mute) {
+ HW_AUDIOOUT_DACVOLUME_SET(dac_mask);
+ HW_AUDIOOUT_HPVOL_SET(BM_AUDIOOUT_HPVOL_MUTE);
+ HW_AUDIOOUT_SPEAKERCTRL_SET(BM_AUDIOOUT_SPEAKERCTRL_MUTE);
+ } else {
+ HW_AUDIOOUT_DACVOLUME_CLR(dac_mask);
+ HW_AUDIOOUT_HPVOL_CLR(BM_AUDIOOUT_HPVOL_MUTE);
+ HW_AUDIOOUT_SPEAKERCTRL_CLR(BM_AUDIOOUT_SPEAKERCTRL_MUTE);
+ }
+ return 0;
+}
+
+/*
+ * Codec initialization
+ */
+#define VAG_BASE_VALUE ((1400/2 - 625)/25)
+static void stmp378x_codec_dac_set_vag(void)
+{
+ u32 refctrl_val = HW_AUDIOOUT_REFCTRL_RD();
+
+ refctrl_val &= ~(BM_AUDIOOUT_REFCTRL_VAG_VAL);
+ refctrl_val &= ~(BM_AUDIOOUT_REFCTRL_VBG_ADJ);
+ refctrl_val |= BF_AUDIOOUT_REFCTRL_VAG_VAL(VAG_BASE_VALUE) |
+ BM_AUDIOOUT_REFCTRL_ADJ_VAG |
+ BF_AUDIOOUT_REFCTRL_ADC_REFVAL(0xF) |
+ BM_AUDIOOUT_REFCTRL_ADJ_ADC |
+ BF_AUDIOOUT_REFCTRL_VBG_ADJ(0x3) |
+ BM_AUDIOOUT_REFCTRL_RAISE_REF;
+
+ HW_AUDIOOUT_REFCTRL_WR(refctrl_val);
+}
+
+static void
+stmp378x_codec_dac_power_on(struct stmp378x_codec_priv *stmp378x_adc)
+{
+ /* Ungate DAC clocks */
+ HW_AUDIOOUT_CTRL_CLR(BM_AUDIOOUT_CTRL_CLKGATE);
+ HW_AUDIOOUT_ANACLKCTRL_CLR(BM_AUDIOOUT_ANACLKCTRL_CLKGATE);
+
+ /* 16 bit word length */
+ HW_AUDIOOUT_CTRL_SET(BM_AUDIOOUT_CTRL_WORD_LENGTH);
+
+ /* Update DAC volume over zero crossings */
+ HW_AUDIOOUT_DACVOLUME_SET(BM_AUDIOOUT_DACVOLUME_EN_ZCD);
+ /* Mute DAC */
+ HW_AUDIOOUT_DACVOLUME_SET(BM_AUDIOOUT_DACVOLUME_MUTE_LEFT |
+ BM_AUDIOOUT_DACVOLUME_MUTE_RIGHT);
+
+ /* Update HP volume over zero crossings */
+ HW_AUDIOOUT_HPVOL_SET(BM_AUDIOOUT_HPVOL_EN_MSTR_ZCD);
+
+ /* Prepare powering up HP output */
+ HW_AUDIOOUT_ANACTRL_SET(BM_AUDIOOUT_ANACTRL_HP_HOLD_GND);
+ HW_RTC_PERSISTENT0_SET(BF_RTC_PERSISTENT0_SPARE_ANALOG(0x2));
+ HW_AUDIOOUT_ANACTRL_SET(BM_AUDIOOUT_ANACTRL_HP_CLASSAB);
+ HW_AUDIOOUT_ANACTRL_CLR(BM_AUDIOOUT_ANACTRL_HP_HOLD_GND);
+ /* Mute HP output */
+ HW_AUDIOOUT_HPVOL_SET(BM_AUDIOOUT_HPVOL_MUTE);
+
+ /* Mute speaker amp */
+ HW_AUDIOOUT_SPEAKERCTRL_SET(BM_AUDIOOUT_SPEAKERCTRL_MUTE);
+}
+
+static void
+stmp378x_codec_dac_power_down(struct stmp378x_codec_priv *stmp378x_adc)
+{
+ /* Disable class AB */
+ HW_AUDIOOUT_ANACTRL_CLR(BM_AUDIOOUT_ANACTRL_HP_CLASSAB);
+
+ /* Set hold to ground */
+ HW_AUDIOOUT_ANACTRL_SET(BM_AUDIOOUT_ANACTRL_HP_HOLD_GND);
+
+ /* Mute HP output */
+ HW_AUDIOOUT_HPVOL_SET(BM_AUDIOOUT_HPVOL_MUTE);
+ /* Power down HP output */
+ HW_AUDIOOUT_PWRDN_SET(BM_AUDIOOUT_PWRDN_HEADPHONE);
+
+ /* Mute speaker amp */
+ HW_AUDIOOUT_SPEAKERCTRL_SET(BM_AUDIOOUT_SPEAKERCTRL_MUTE);
+ /* Power down speaker amp */
+ HW_AUDIOOUT_PWRDN_SET(BM_AUDIOOUT_PWRDN_SPEAKER);
+
+ /* Mute DAC */
+ HW_AUDIOOUT_DACVOLUME_SET(BM_AUDIOOUT_DACVOLUME_MUTE_LEFT |
+ BM_AUDIOOUT_DACVOLUME_MUTE_RIGHT);
+ /* Power down DAC */
+ HW_AUDIOOUT_PWRDN_SET(BM_AUDIOOUT_PWRDN_DAC);
+
+ /* Gate DAC clocks */
+ HW_AUDIOOUT_ANACLKCTRL_SET(BM_AUDIOOUT_ANACLKCTRL_CLKGATE);
+ HW_AUDIOOUT_CTRL_SET(BM_AUDIOOUT_CTRL_CLKGATE);
+}
+
+static void
+stmp378x_codec_adc_power_on(struct stmp378x_codec_priv *stmp378x_adc)
+{
+ u32 reg;
+
+ /* Ungate ADC clocks */
+ HW_AUDIOIN_CTRL_CLR(BM_AUDIOIN_CTRL_CLKGATE);
+ HW_AUDIOIN_ANACLKCTRL_CLR(BM_AUDIOIN_ANACLKCTRL_CLKGATE);
+
+ /* 16 bit word length */
+ HW_AUDIOIN_CTRL_SET(BM_AUDIOIN_CTRL_WORD_LENGTH);
+
+ /* Unmute ADC channels */
+ HW_AUDIOIN_ADCVOL_CLR(BM_AUDIOIN_ADCVOL_MUTE);
+
+ /*
+ * The MUTE_LEFT and MUTE_RIGHT fields need to be cleared.
+ * They aren't presented in the datasheet, so this is hardcode.
+ */
+ HW_AUDIOIN_ADCVOLUME_CLR(0x01000100);
+
+ /* Set the Input channel gain 3dB */
+ HW_AUDIOIN_ADCVOL_CLR(BM_AUDIOIN_ADCVOL_GAIN_LEFT);
+ HW_AUDIOIN_ADCVOL_CLR(BM_AUDIOIN_ADCVOL_GAIN_RIGHT);
+ HW_AUDIOIN_ADCVOL_SET(BF_AUDIOIN_ADCVOL_GAIN_LEFT(2));
+ HW_AUDIOIN_ADCVOL_SET(BF_AUDIOIN_ADCVOL_GAIN_RIGHT(2));
+
+ /* Select default input - Microphone */
+ HW_AUDIOIN_ADCVOL_CLR(BM_AUDIOIN_ADCVOL_SELECT_LEFT);
+ HW_AUDIOIN_ADCVOL_CLR(BM_AUDIOIN_ADCVOL_SELECT_RIGHT);
+ HW_AUDIOIN_ADCVOL_SET(
+ BF_AUDIOIN_ADCVOL_SELECT_LEFT(BV_AUDIOIN_ADCVOL_SELECT__MIC));
+ HW_AUDIOIN_ADCVOL_SET(
+ BF_AUDIOIN_ADCVOL_SELECT_RIGHT(BV_AUDIOIN_ADCVOL_SELECT__MIC));
+
+ /* Supply bias voltage to microphone */
+ HW_AUDIOIN_MICLINE_SET(BF_AUDIOIN_MICLINE_MIC_RESISTOR(2));
+ HW_AUDIOIN_MICLINE_SET(BM_AUDIOIN_MICLINE_MIC_SELECT);
+
+ /* Set max ADC volume */
+ reg = HW_AUDIOIN_ADCVOLUME_RD();
+ reg &= ~BM_AUDIOIN_ADCVOLUME_VOLUME_LEFT;
+ reg &= ~BM_AUDIOIN_ADCVOLUME_VOLUME_RIGHT;
+ reg |= BF_AUDIOIN_ADCVOLUME_VOLUME_LEFT(ADC_VOLUME_MAX);
+ reg |= BF_AUDIOIN_ADCVOLUME_VOLUME_RIGHT(ADC_VOLUME_MAX);
+ HW_AUDIOIN_ADCVOLUME_WR(reg);
+}
+
+static void
+stmp378x_codec_adc_power_down(struct stmp378x_codec_priv *stmp378x_adc)
+{
+ /* Mute ADC channels */
+ HW_AUDIOIN_ADCVOL_SET(BM_AUDIOIN_ADCVOL_MUTE);
+
+ /* Power Down ADC */
+ HW_AUDIOOUT_PWRDN_SET(
+ BM_AUDIOOUT_PWRDN_ADC | BM_AUDIOOUT_PWRDN_RIGHT_ADC);
+
+ /* Gate ADC clocks */
+ HW_AUDIOIN_CTRL_SET(BM_AUDIOIN_CTRL_CLKGATE);
+ HW_AUDIOIN_ANACLKCTRL_SET(BM_AUDIOIN_ANACLKCTRL_CLKGATE);
+
+ /* Disable bias voltage to microphone*/
+ HW_AUDIOIN_MICLINE_SET(BF_AUDIOIN_MICLINE_MIC_RESISTOR(0));
+}
+
+static void
+stmp378x_codec_dac_enable(struct stmp378x_codec_priv *stmp378x_adc)
+{
+ /* Move DAC codec out of reset */
+ HW_AUDIOOUT_CTRL_CLR(BM_AUDIOOUT_CTRL_SFTRST);
+
+ /* Reduce analog power */
+ HW_AUDIOOUT_TEST_CLR(BM_AUDIOOUT_TEST_HP_I1_ADJ);
+ HW_AUDIOOUT_TEST_SET(BF_AUDIOOUT_TEST_HP_I1_ADJ(0x1));
+ HW_AUDIOOUT_REFCTRL_SET(BM_AUDIOOUT_REFCTRL_LOW_PWR);
+ HW_AUDIOOUT_REFCTRL_SET(BM_AUDIOOUT_REFCTRL_XTAL_BGR_BIAS);
+ HW_AUDIOOUT_REFCTRL_CLR(BM_AUDIOOUT_REFCTRL_BIAS_CTRL);
+ HW_AUDIOOUT_REFCTRL_CLR(BF_AUDIOOUT_REFCTRL_BIAS_CTRL(0x1));
+
+ /* Set Vag value */
+ stmp378x_codec_dac_set_vag();
+
+ /* Power on DAC codec */
+ stmp378x_codec_dac_power_on(stmp378x_adc);
+}
+
+static void stmp378x_codec_dac_disable(struct stmp378x_codec_priv *stmp378x_adc)
+{
+ stmp378x_codec_dac_power_down(stmp378x_adc);
+}
+
+static void
+stmp378x_codec_adc_enable(struct stmp378x_codec_priv *stmp378x_adc)
+{
+ /* Move ADC codec out of reset */
+ HW_AUDIOIN_CTRL_CLR(BM_AUDIOIN_CTRL_SFTRST);
+
+ /* Power on ADC codec */
+ stmp378x_codec_adc_power_on(stmp378x_adc);
+}
+
+static void stmp378x_codec_adc_disable(struct stmp378x_codec_priv *stmp378x_adc)
+{
+ stmp378x_codec_adc_power_down(stmp378x_adc);
+}
+
+static void
+stmp378x_codec_init(struct snd_soc_codec *codec)
+{
+ struct stmp378x_codec_priv *stmp378x_adc = codec->private_data;
+
+ /* Soft reset DAC block */
+ HW_AUDIOOUT_CTRL_SET(BM_AUDIOOUT_CTRL_SFTRST);
+ while (!(HW_AUDIOOUT_CTRL_RD() & BM_AUDIOOUT_CTRL_CLKGATE));
+
+ /* Soft reset ADC block */
+ HW_AUDIOIN_CTRL_SET(BM_AUDIOIN_CTRL_SFTRST);
+ while (!(HW_AUDIOIN_CTRL_RD() & BM_AUDIOIN_CTRL_CLKGATE));
+
+ stmp378x_codec_dac_enable(stmp378x_adc);
+ stmp378x_codec_adc_enable(stmp378x_adc);
+
+ /*Sync regs and cache*/
+ stmp378x_codec_sync_reg_cache(codec);
+
+ stmp378x_codec_add_controls(codec);
+ stmp378x_codec_add_widgets(codec);
+}
+
+static void
+stmp378x_codec_exit(struct snd_soc_codec *codec)
+{
+ struct stmp378x_codec_priv *stmp378x_adc = codec->private_data;
+ stmp378x_codec_dac_disable(stmp378x_adc);
+ stmp378x_codec_adc_disable(stmp378x_adc);
+}
+
+#define STMP378X_ADC_RATES SNDRV_PCM_RATE_8000_192000
+#define STMP378X_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+struct snd_soc_dai stmp378x_codec_dai = {
+ .name = "stmp378x adc/dac",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STMP378X_ADC_RATES,
+ .formats = STMP378X_ADC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STMP378X_ADC_RATES,
+ .formats = STMP378X_ADC_FORMATS,
+ },
+ .ops = {
+ .hw_params = stmp378x_codec_hw_params,
+ },
+ .dai_ops = {
+ .digital_mute = stmp378x_codec_dig_mute,
+ }
+};
+EXPORT_SYMBOL_GPL(stmp378x_codec_dai);
+
+static int stmp378x_codec_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct stmp378x_codec_priv *stmp378x_adc;
+ int ret = 0;
+
+ printk(KERN_INFO "STMP378X ADC/DAC Audio Codec %s\n", STMP378X_VERSION);
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ stmp378x_adc = kzalloc(sizeof(struct stmp378x_codec_priv), GFP_KERNEL);
+ if (stmp378x_adc == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->name = "stmp378x adc/dac";
+ codec->owner = THIS_MODULE;
+ codec->private_data = stmp378x_adc;
+ codec->read = stmp378x_codec_read;
+ codec->write = stmp378x_codec_write;
+ codec->dai = &stmp378x_codec_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = sizeof(stmp378x_audio_regs) >> 1;
+ codec->reg_cache_step = 1;
+ codec->reg_cache = (void *)&stmp378x_audio_regs;
+ socdev->codec = codec;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: failed to create pcms\n", __func__);
+ goto pcm_err;
+ }
+
+ /* Turn on audio clock */
+ stmp378x_adc->dev = &pdev->dev;
+ stmp378x_adc->clk = clk_get(stmp378x_adc->dev, "audio");
+ if (IS_ERR(stmp378x_adc->clk)) {
+ ret = PTR_ERR(stmp378x_adc->clk);
+ printk(KERN_ERR "%s: Clocks initialization failed\n", __func__);
+ goto clk_err;
+ }
+ clk_enable(stmp378x_adc->clk);
+
+ stmp378x_codec_init(codec);
+
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: failed to register card\n", __func__);
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ clk_disable(stmp378x_adc->clk);
+ clk_put(stmp378x_adc->clk);
+clk_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(socdev->codec);
+ return ret;
+}
+
+static int stmp378x_codec_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct stmp378x_codec_priv *stmp378x_adc;
+
+ if (codec == NULL)
+ return 0;
+
+ stmp378x_adc = codec->private_data;
+
+ clk_disable(stmp378x_adc->clk);
+ clk_put(stmp378x_adc->clk);
+
+ stmp378x_codec_exit(codec);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ kfree(socdev->codec);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stmp378x_codec_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct stmp378x_codec_priv *stmp378x_adc;
+ int ret = -EINVAL;
+
+ if (codec == NULL)
+ goto out;
+
+ stmp378x_adc = codec->private_data;
+
+ stmp378x_codec_dac_disable(stmp378x_adc);
+ stmp378x_codec_adc_disable(stmp378x_adc);
+ clk_disable(stmp378x_adc->clk);
+ ret = 0;
+
+out:
+ return ret;
+}
+
+static int stmp378x_codec_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct stmp378x_codec_priv *stmp378x_adc;
+ int ret = -EINVAL;
+
+ if (codec == NULL)
+ goto out;
+
+ stmp378x_adc = codec->private_data;
+ clk_enable(stmp378x_adc->clk);
+
+ /* Soft reset DAC block */
+ HW_AUDIOOUT_CTRL_SET(BM_AUDIOOUT_CTRL_SFTRST);
+ while (!(HW_AUDIOOUT_CTRL_RD() & BM_AUDIOOUT_CTRL_CLKGATE));
+
+ /* Soft reset ADC block */
+ HW_AUDIOIN_CTRL_SET(BM_AUDIOIN_CTRL_SFTRST);
+ while (!(HW_AUDIOIN_CTRL_RD() & BM_AUDIOIN_CTRL_CLKGATE));
+
+ stmp378x_codec_dac_enable(stmp378x_adc);
+ stmp378x_codec_adc_enable(stmp378x_adc);
+
+ /*restore registers relevant to amixer controls*/
+ stmp378x_codec_restore_reg(codec, DAC_CTRL_L);
+ stmp378x_codec_restore_reg(codec, DAC_VOLUME_L);
+ stmp378x_codec_restore_reg(codec, DAC_VOLUME_H);
+ stmp378x_codec_restore_reg(codec, DAC_HPVOL_L);
+ stmp378x_codec_restore_reg(codec, DAC_HPVOL_H);
+ stmp378x_codec_restore_reg(codec, DAC_SPEAKERCTRL_H);
+ stmp378x_codec_restore_reg(codec, ADC_VOLUME_L);
+ stmp378x_codec_restore_reg(codec, ADC_VOLUME_H);
+ stmp378x_codec_restore_reg(codec, ADC_ADCVOL_L);
+ stmp378x_codec_restore_reg(codec, ADC_ADCVOL_H);
+ stmp378x_codec_restore_reg(codec, ADC_MICLINE_L);
+
+ ret = 0;
+
+out:
+ return ret;
+}
+#else
+#define stmp378x_codec_suspend NULL
+#define stmp378x_codec_resume NULL
+#endif /* CONFIG_PM */
+
+struct snd_soc_codec_device soc_codec_dev_stmp378x = {
+ .probe = stmp378x_codec_probe,
+ .remove = stmp378x_codec_remove,
+ .suspend = stmp378x_codec_suspend,
+ .resume = stmp378x_codec_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_stmp378x);
+
+MODULE_DESCRIPTION("STMP378X ADC/DAC codec");
+MODULE_AUTHOR("Vladislav Buzov");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/stmp378x_codec.h b/sound/soc/codecs/stmp378x_codec.h
new file mode 100644
index 000000000000..80fce7273126
--- /dev/null
+++ b/sound/soc/codecs/stmp378x_codec.h
@@ -0,0 +1,87 @@
+/*
+ * ALSA codec for Freescale STMP378X
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 __STMP378X_CODEC_H
+#define __STMP378X_CODEC_H
+
+#define DAC_CTRL_L 0
+#define DAC_CTRL_H 1
+#define DAC_STAT_L 2
+#define DAC_STAT_H 3
+#define DAC_SRR_L 4
+#define DAC_VOLUME_L 6
+#define DAC_VOLUME_H 7
+#define DAC_DEBUG_L 8
+#define DAC_DEBUG_H 9
+#define DAC_HPVOL_L 10
+#define DAC_HPVOL_H 11
+#define DAC_PWRDN_L 12
+#define DAC_PWRDN_H 13
+#define DAC_REFCTRL_L 14
+#define DAC_REFCTRL_H 15
+#define DAC_ANACTRL_L 16
+#define DAC_ANACTRL_H 17
+#define DAC_TEST_L 18
+#define DAC_TEST_H 19
+#define DAC_BISTCTRL_L 20
+#define DAC_BISTCTRL_H 21
+#define DAC_BISTSTAT0_L 22
+#define DAC_BISTSTAT0_H 23
+#define DAC_BISTSTAT1_L 24
+#define DAC_BISTSTAT1_H 25
+#define DAC_ANACLKCTRL_L 26
+#define DAC_ANACLKCTRL_H 27
+#define DAC_DATA_L 28
+#define DAC_DATA_H 29
+#define DAC_SPEAKERCTRL_L 30
+#define DAC_SPEAKERCTRL_H 31
+#define DAC_VERSION_L 32
+#define DAC_VERSION_H 33
+#define ADC_CTRL_L 34
+#define ADC_CTRL_H 35
+#define ADC_STAT_L 36
+#define ADC_STAT_H 37
+#define ADC_SRR_L 38
+#define ADC_SRR_H 39
+#define ADC_VOLUME_L 40
+#define ADC_VOLUME_H 41
+#define ADC_DEBUG_L 42
+#define ADC_DEBUG_H 43
+#define ADC_ADCVOL_L 44
+#define ADC_ADCVOL_H 45
+#define ADC_MICLINE_L 46
+#define ADC_MICLINE_H 47
+#define ADC_ANACLKCTRL_L 48
+#define ADC_ANACLKCTRL_H 49
+#define ADC_DATA_L 50
+#define ADC_DATA_H 51
+
+#define ADC_REGNUM 52
+
+#define DAC_VOLUME_MIN 0x37
+#define DAC_VOLUME_MAX 0xFE
+#define ADC_VOLUME_MIN 0x37
+#define ADC_VOLUME_MAX 0xFE
+#define HP_VOLUME_MAX 0x0
+#define HP_VOLUME_MIN 0x7F
+#define LO_VOLUME_MAX 0x0
+#define LO_VOLUME_MIN 0x1F
+
+extern struct snd_soc_dai stmp378x_codec_dai;
+extern struct snd_soc_codec_device soc_codec_dev_stmp378x;
+
+#endif /* __STMP378X_CODEC_H */
diff --git a/sound/soc/codecs/stmp3xxx_spdif.c b/sound/soc/codecs/stmp3xxx_spdif.c
new file mode 100644
index 000000000000..9f51ec4ffbe4
--- /dev/null
+++ b/sound/soc/codecs/stmp3xxx_spdif.c
@@ -0,0 +1,412 @@
+/*
+ * ALSA SoC STMP3xxx SPDIF transmitter driver
+ *
+ * Vladimir Barinov <vbarinov@embeddedalley.com>
+ *
+ * Copyright 2008 SigmaTel, Inc
+ * Copyright 2008 Embedded Alley Solutions, Inc
+ * Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <asm/dma.h>
+
+#include <mach/regs-spdif.h>
+
+#include "stmp3xxx_spdif.h"
+
+#define STMP3XXX_VERSION "0.1"
+struct stmp3xxx_codec_priv {
+ struct device *dev;
+ struct clk *clk;
+};
+
+/*
+ * ALSA API
+ */
+static u32 spdif_regmap[] = {
+ HW_SPDIF_CTRL_ADDR,
+ HW_SPDIF_STAT_ADDR,
+ HW_SPDIF_FRAMECTRL_ADDR,
+ HW_SPDIF_SRR_ADDR,
+ HW_SPDIF_DEBUG_ADDR,
+ HW_SPDIF_DATA_ADDR,
+ HW_SPDIF_VERSION_ADDR,
+};
+
+/*
+ * ALSA core supports only 16 bit registers. It means we have to simulate it
+ * by virtually splitting a 32bit SPDIF registers into two halves
+ * high (bits 31:16) and low (bits 15:0). The routins abow detects which part
+ * of 32bit register is accessed.
+ */
+static int stmp3xxx_codec_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
+{
+ unsigned int reg_val;
+ unsigned int mask = 0xffff;
+
+ if (reg >= SPDIF_REGNUM)
+ return -EIO;
+
+ if (reg & 0x1) {
+ mask <<= 16;
+ value <<= 16;
+ }
+
+ reg_val = __raw_readl(spdif_regmap[reg >> 1]);
+ reg_val = (reg_val & ~mask) | value;
+ __raw_writel(reg_val, spdif_regmap[reg >> 1]);
+
+ return 0;
+}
+
+static unsigned int stmp3xxx_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ unsigned int reg_val;
+
+ if (reg >= SPDIF_REGNUM)
+ return -1;
+
+ reg_val = __raw_readl(spdif_regmap[reg >> 1]);
+ if (reg & 1)
+ reg_val >>= 16;
+
+ return reg_val & 0xffff;
+}
+
+/* Codec controls */
+static const struct snd_kcontrol_new stmp3xxx_snd_controls[] = {
+ SOC_SINGLE("PRO", SPDIF_FRAMECTRL_L, 0, 0x1, 0),
+ SOC_SINGLE("AUDIO", SPDIF_FRAMECTRL_L, 1, 0x1, 0),
+ SOC_SINGLE("COPY", SPDIF_FRAMECTRL_L, 2, 0x1, 0),
+ SOC_SINGLE("PRE", SPDIF_FRAMECTRL_L, 3, 0x1, 0),
+ SOC_SINGLE("CC", SPDIF_FRAMECTRL_L, 4, 0x7F, 0),
+ SOC_SINGLE("L", SPDIF_FRAMECTRL_L, 12, 0x1, 0),
+ SOC_SINGLE("V", SPDIF_FRAMECTRL_L, 13, 0x1, 0),
+ SOC_SINGLE("USER DATA", SPDIF_FRAMECTRL_L, 14, 0x1, 0),
+ SOC_SINGLE("AUTO MUTE", SPDIF_FRAMECTRL_H, 16, 0x1, 0),
+ SOC_SINGLE("V CONFIG", SPDIF_FRAMECTRL_H, 17, 0x1, 0),
+};
+
+/* add non dapm controls */
+static int stmp3xxx_codec_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(stmp3xxx_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&stmp3xxx_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+struct spdif_srr {
+ u32 rate;
+ u32 basemult;
+ u32 rate_factor;
+};
+
+static struct spdif_srr srr_values[] = {
+ {96000, 0x2, 0x0BB80},
+ {88200, 0x2, 0x0AC44},
+ {64000, 0x2, 0x07D00},
+ {48000, 0x1, 0x0BB80},
+ {44100, 0x1, 0x0AC44},
+ {32000, 0x1, 0x07D00},
+};
+
+static inline int get_srr_values(int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(srr_values); i++)
+ if (srr_values[i].rate == rate)
+ return i;
+
+ return -1;
+}
+
+static int stmp3xxx_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+ int i;
+ u32 srr_value = 0;
+ u32 basemult;
+
+ i = get_srr_values(params_rate(params));
+ if (i < 0)
+ printk(KERN_WARNING "%s doesn't support rate %d\n",
+ codec->name, params_rate(params));
+ else {
+ basemult = srr_values[i].basemult;
+
+ srr_value = BF_SPDIF_SRR_BASEMULT(basemult) |
+ BF_SPDIF_SRR_RATE(srr_values[i].rate_factor);
+
+ if (playback)
+ HW_SPDIF_SRR_WR(srr_value);
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ if (playback)
+ HW_SPDIF_CTRL_SET(BM_SPDIF_CTRL_WORD_LENGTH);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ if (playback)
+ HW_SPDIF_CTRL_CLR(BM_SPDIF_CTRL_WORD_LENGTH);
+ break;
+ default:
+ printk(KERN_WARNING "%s doesn't support format %d\n",
+ codec->name, params_format(params));
+ }
+
+ return 0;
+}
+
+static void
+stmp3xxx_codec_spdif_enable(struct stmp3xxx_codec_priv *stmp3xxx_spdif)
+{
+ /* Move SPDIF codec out of reset */
+ HW_SPDIF_CTRL_CLR(BM_SPDIF_CTRL_SFTRST);
+
+ /* Ungate SPDIF clocks */
+ HW_SPDIF_CTRL_CLR(BM_SPDIF_CTRL_CLKGATE);
+
+ /* 16 bit word length */
+ HW_SPDIF_CTRL_SET(BM_SPDIF_CTRL_WORD_LENGTH);
+}
+
+static void
+stmp3xxx_codec_spdif_disable(struct stmp3xxx_codec_priv *stmp3xxx_spdif)
+{
+ /* Gate SPDIF clocks */
+ HW_SPDIF_CTRL_SET(BM_SPDIF_CTRL_CLKGATE);
+}
+
+static void stmp3xxx_codec_init(struct snd_soc_codec *codec)
+{
+ struct stmp3xxx_codec_priv *stmp3xxx_spdif = codec->private_data;
+
+ /* Soft reset SPDIF block */
+ HW_SPDIF_CTRL_SET(BM_SPDIF_CTRL_SFTRST);
+ while (!(HW_SPDIF_CTRL_RD() & BM_SPDIF_CTRL_CLKGATE));
+
+ stmp3xxx_codec_spdif_enable(stmp3xxx_spdif);
+
+ stmp3xxx_codec_add_controls(codec);
+}
+
+static void stmp3xxx_codec_exit(struct snd_soc_codec *codec)
+{
+ struct stmp3xxx_codec_priv *stmp3xxx_spdif = codec->private_data;
+
+ stmp3xxx_codec_spdif_disable(stmp3xxx_spdif);
+}
+
+#define STMP3XXX_SPDIF_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+#define STMP3XXX_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai stmp3xxx_spdif_codec_dai = {
+ .name = "stmp3xxx spdif",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STMP3XXX_SPDIF_RATES,
+ .formats = STMP3XXX_SPDIF_FORMATS,
+ },
+ .ops = {
+ .hw_params = stmp3xxx_codec_hw_params,
+ },
+};
+EXPORT_SYMBOL_GPL(stmp3xxx_spdif_codec_dai);
+
+static int stmp3xxx_codec_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ struct stmp3xxx_codec_priv *stmp3xxx_spdif;
+ int ret = 0;
+
+ printk(KERN_INFO
+ "STMP3XXX SPDIF Audio Transmitter %s\n", STMP3XXX_VERSION);
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ stmp3xxx_spdif =
+ kzalloc(sizeof(struct stmp3xxx_codec_priv), GFP_KERNEL);
+ if (stmp3xxx_spdif == NULL) {
+ kfree(codec);
+ return -ENOMEM;
+ }
+
+ codec->name = "stmp3xxx spdif";
+ codec->owner = THIS_MODULE;
+ codec->private_data = stmp3xxx_spdif;
+ codec->read = stmp3xxx_codec_read;
+ codec->write = stmp3xxx_codec_write;
+ codec->dai = &stmp3xxx_spdif_codec_dai;
+ codec->num_dai = 1;
+ socdev->codec = codec;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: failed to create pcms\n", __func__);
+ goto pcm_err;
+ }
+
+ /* Turn on audio clock */
+ stmp3xxx_spdif->dev = &pdev->dev;
+ stmp3xxx_spdif->clk = clk_get(stmp3xxx_spdif->dev, "spdif");
+ if (IS_ERR(stmp3xxx_spdif->clk)) {
+ ret = PTR_ERR(stmp3xxx_spdif->clk);
+ printk(KERN_ERR "%s: Clocks initialization failed\n", __func__);
+ goto clk_err;
+ }
+ clk_enable(stmp3xxx_spdif->clk);
+
+ stmp3xxx_codec_init(codec);
+
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: failed to register card\n", __func__);
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ clk_disable(stmp3xxx_spdif->clk);
+ clk_put(stmp3xxx_spdif->clk);
+clk_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(socdev->codec);
+ return ret;
+}
+
+static int stmp3xxx_codec_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct stmp3xxx_codec_priv *stmp3xxx_spdif;
+
+ if (codec == NULL)
+ return 0;
+
+ stmp3xxx_spdif = codec->private_data;
+
+ clk_disable(stmp3xxx_spdif->clk);
+ clk_put(stmp3xxx_spdif->clk);
+
+ stmp3xxx_codec_exit(codec);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ kfree(socdev->codec);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stmp3xxx_codec_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct stmp3xxx_codec_priv *stmp3xxx_spdif;
+ int ret = -EINVAL;
+
+ if (codec == NULL)
+ goto out;
+
+ stmp3xxx_spdif = codec->private_data;
+
+ stmp3xxx_codec_spdif_disable(stmp3xxx_spdif);
+ clk_disable(stmp3xxx_spdif->clk);
+ ret = 0;
+
+out:
+ return ret;
+}
+
+static int stmp3xxx_codec_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct stmp3xxx_codec_priv *stmp3xxx_spdif;
+ int ret = -EINVAL;
+
+ if (codec == NULL)
+ goto out;
+
+ stmp3xxx_spdif = codec->private_data;
+ clk_enable(stmp3xxx_spdif->clk);
+
+ /* Soft reset SPDIF block */
+ HW_SPDIF_CTRL_SET(BM_SPDIF_CTRL_SFTRST);
+ while (!(HW_SPDIF_CTRL_RD() & BM_SPDIF_CTRL_CLKGATE));
+
+ stmp3xxx_codec_spdif_enable(stmp3xxx_spdif);
+
+ ret = 0;
+
+out:
+ return ret;
+}
+#else
+#define stmp3xxx_codec_suspend NULL
+#define stmp3xxx_codec_resume NULL
+#endif /* CONFIG_PM */
+
+struct snd_soc_codec_device soc_spdif_codec_dev_stmp3xxx = {
+ .probe = stmp3xxx_codec_probe,
+ .remove = stmp3xxx_codec_remove,
+ .suspend = stmp3xxx_codec_suspend,
+ .resume = stmp3xxx_codec_resume,
+};
+EXPORT_SYMBOL_GPL(soc_spdif_codec_dev_stmp3xxx);
+
+MODULE_DESCRIPTION("STMP3XXX SPDIF transmitter");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/stmp3xxx_spdif.h b/sound/soc/codecs/stmp3xxx_spdif.h
new file mode 100644
index 000000000000..0ae20a7e8cc6
--- /dev/null
+++ b/sound/soc/codecs/stmp3xxx_spdif.h
@@ -0,0 +1,37 @@
+/*
+ * ALSA SoC STMP378x SPDIF codec driver
+ *
+ * Vladimir Barinov <vbarinov@embeddedalley.com>
+ *
+ * Copyright 2008 SigmaTel, Inc
+ * Copyright 2008 Embedded Alley Solutions, Inc
+ * Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+#ifndef __STMP3XXX_SPDIF_CODEC_H
+#define __STMP3XXX_SPDIF_CODEC_H
+
+#define SPDIF_CTRL_L 0
+#define SPDIF_CTRL_H 1
+#define SPDIF_STAT_L 2
+#define SPDIF_STAT_H 3
+#define SPDIF_FRAMECTRL_L 4
+#define SPDIF_FRAMECTRL_H 5
+#define SPDIF_SRR_L 6
+#define SPDIF_SRR_H 7
+#define SPDIF_DEBUG_L 8
+#define SPDIF_DEBUG_H 9
+#define SPDIF_DATA_L 10
+#define SPDIF_DATA_H 11
+#define SPDIF_VERSION_L 12
+#define SPDIF_VERSION_H 13
+
+#define SPDIF_REGNUM 14
+
+extern struct snd_soc_dai stmp3xxx_spdif_codec_dai;
+extern struct snd_soc_codec_device soc_spdif_codec_dev_stmp3xxx;
+
+#endif /* __STMP3XXX_SPDIF_CODEC_H */
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
new file mode 100644
index 000000000000..5657b4d9a870
--- /dev/null
+++ b/sound/soc/codecs/wm8350.c
@@ -0,0 +1,1546 @@
+/*
+ * wm8350.c -- WM8350 ALSA SoC audio driver
+ *
+ * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <lg@opensource.wolfsonmicro.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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8350.h"
+
+#define WM8350_OUTn_0dB 0x39
+
+#define WM8350_RAMP_NONE 0
+#define WM8350_RAMP_UP 1
+#define WM8350_RAMP_DOWN 2
+
+struct wm8350_output {
+ u16 active;
+ u16 left_vol;
+ u16 right_vol;
+ u16 ramp;
+ u16 mute;
+};
+
+struct wm8350_data {
+ struct wm8350_output out1;
+ struct wm8350_output out2;
+ struct regulator_bulk_data supplies[2];
+};
+
+static int wm8350_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level);
+
+static unsigned int wm8350_codec_cache_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct wm8350 *wm8350 = codec->control_data;
+ return wm8350->reg_cache[reg];
+}
+
+static unsigned int wm8350_codec_read(struct snd_soc_codec *codec,
+ unsigned int reg)
+{
+ struct wm8350 *wm8350 = codec->control_data;
+ return wm8350_reg_read(wm8350, reg);
+}
+
+static int wm8350_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+ struct wm8350 *wm8350 = codec->control_data;
+ return wm8350_reg_write(wm8350, reg, value);
+}
+
+/*
+ * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown.
+ */
+static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec)
+{
+ struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_output *out1 = &wm8350_data->out1;
+ struct wm8350 *wm8350 = codec->control_data;
+ int left_complete = 0, right_complete = 0;
+ u16 reg, val;
+
+ /* left channel */
+ reg = wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME);
+ val = (reg & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+
+ if (out1->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out1->left_vol) {
+ val++;
+ reg &= ~WM8350_OUT1L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else if (out1->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT1L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else
+ return 1;
+
+ /* right channel */
+ reg = wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME);
+ val = (reg & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ if (out1->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out1->right_vol) {
+ val++;
+ reg &= ~WM8350_OUT1R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ } else if (out1->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT1R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ }
+
+ /* only hit the update bit if either volume has changed this step */
+ if (!left_complete || !right_complete)
+ wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU);
+
+ return left_complete & right_complete;
+}
+
+/*
+ * Ramp OUT2 PGA volume to minimise pops at stream startup and shutdown.
+ */
+static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec)
+{
+ struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_output *out2 = &wm8350_data->out2;
+ struct wm8350 *wm8350 = codec->control_data;
+ int left_complete = 0, right_complete = 0;
+ u16 reg, val;
+
+ /* left channel */
+ reg = wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME);
+ val = (reg & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+ if (out2->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out2->left_vol) {
+ val++;
+ reg &= ~WM8350_OUT2L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else if (out2->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT2L_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
+ reg | (val << WM8350_OUT1L_VOL_SHIFT));
+ } else
+ left_complete = 1;
+ } else
+ return 1;
+
+ /* right channel */
+ reg = wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME);
+ val = (reg & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ if (out2->ramp == WM8350_RAMP_UP) {
+ /* ramp step up */
+ if (val < out2->right_vol) {
+ val++;
+ reg &= ~WM8350_OUT2R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ } else if (out2->ramp == WM8350_RAMP_DOWN) {
+ /* ramp step down */
+ if (val > 0) {
+ val--;
+ reg &= ~WM8350_OUT2R_VOL_MASK;
+ wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
+ reg | (val << WM8350_OUT1R_VOL_SHIFT));
+ } else
+ right_complete = 1;
+ }
+
+ /* only hit the update bit if either volume has changed this step */
+ if (!left_complete || !right_complete)
+ wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU);
+
+ return left_complete & right_complete;
+}
+
+/*
+ * This work ramps both output PGAs at stream start/stop time to
+ * minimise pop associated with DAPM power switching.
+ * It's best to enable Zero Cross when ramping occurs to minimise any
+ * zipper noises.
+ */
+static void wm8350_pga_work(struct work_struct *work)
+{
+ struct snd_soc_codec *codec =
+ container_of(work, struct snd_soc_codec, delayed_work.work);
+ struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_output *out1 = &wm8350_data->out1,
+ *out2 = &wm8350_data->out2;
+ int i, out1_complete, out2_complete;
+
+ /* do we need to ramp at all ? */
+ if (out1->ramp == WM8350_RAMP_NONE && out2->ramp == WM8350_RAMP_NONE)
+ return;
+
+ /* PGA volumes have 6 bits of resolution to ramp */
+ for (i = 0; i <= 63; i++) {
+ out1_complete = 1, out2_complete = 1;
+ if (out1->ramp != WM8350_RAMP_NONE)
+ out1_complete = wm8350_out1_ramp_step(codec);
+ if (out2->ramp != WM8350_RAMP_NONE)
+ out2_complete = wm8350_out2_ramp_step(codec);
+
+ /* ramp finished ? */
+ if (out1_complete && out2_complete)
+ break;
+
+ /* we need to delay longer on the up ramp */
+ if (out1->ramp == WM8350_RAMP_UP ||
+ out2->ramp == WM8350_RAMP_UP) {
+ /* delay is longer over 0dB as increases are larger */
+ if (i >= WM8350_OUTn_0dB)
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (2));
+ else
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (1));
+ } else
+ udelay(50); /* doesn't matter if we delay longer */
+ }
+
+ out1->ramp = WM8350_RAMP_NONE;
+ out2->ramp = WM8350_RAMP_NONE;
+}
+
+/*
+ * WM8350 Controls
+ */
+
+static int pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm8350_data *wm8350_data = codec->private_data;
+ struct wm8350_output *out;
+
+ switch (w->shift) {
+ case 0:
+ case 1:
+ out = &wm8350_data->out1;
+ break;
+ case 2:
+ case 3:
+ out = &wm8350_data->out2;
+ break;
+
+ default:
+ BUG();
+ return -1;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ out->ramp = WM8350_RAMP_UP;
+ out->active = 1;
+
+ if (!delayed_work_pending(&codec->delayed_work))
+ schedule_delayed_work(&codec->delayed_work,
+ msecs_to_jiffies(1));
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ out->ramp = WM8350_RAMP_DOWN;
+ out->active = 0;
+
+ if (!delayed_work_pending(&codec->delayed_work))
+ schedule_delayed_work(&codec->delayed_work,
+ msecs_to_jiffies(1));
+ break;
+ }
+
+ return 0;
+}
+
+static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8350_data *wm8350_priv = codec->private_data;
+ struct wm8350_output *out = NULL;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int ret;
+ unsigned int reg = mc->reg;
+ u16 val;
+
+ /* For OUT1 and OUT2 we shadow the values and only actually write
+ * them out when active in order to ensure the amplifier comes on
+ * as quietly as possible. */
+ switch (reg) {
+ case WM8350_LOUT1_VOLUME:
+ out = &wm8350_priv->out1;
+ break;
+ case WM8350_LOUT2_VOLUME:
+ out = &wm8350_priv->out2;
+ break;
+ default:
+ break;
+ }
+
+ if (out) {
+ out->left_vol = ucontrol->value.integer.value[0];
+ out->right_vol = ucontrol->value.integer.value[1];
+ if (!out->active)
+ return 1;
+ }
+
+ ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /* now hit the volume update bits (always bit 8) */
+ val = wm8350_codec_read(codec, reg);
+ wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU);
+ return 1;
+}
+
+static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8350_data *wm8350_priv = codec->private_data;
+ struct wm8350_output *out1 = &wm8350_priv->out1;
+ struct wm8350_output *out2 = &wm8350_priv->out2;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+
+ /* If these are cached registers use the cache */
+ switch (reg) {
+ case WM8350_LOUT1_VOLUME:
+ ucontrol->value.integer.value[0] = out1->left_vol;
+ ucontrol->value.integer.value[1] = out1->right_vol;
+ return 0;
+
+ case WM8350_LOUT2_VOLUME:
+ ucontrol->value.integer.value[0] = out2->left_vol;
+ ucontrol->value.integer.value[1] = out2->right_vol;
+ return 0;
+
+ default:
+ break;
+ }
+
+ return snd_soc_get_volsw_2r(kcontrol, ucontrol);
+}
+
+/* double control with volume update */
+#define SOC_WM8350_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, \
+ xinvert, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw_2r, \
+ .get = wm8350_get_volsw_2r, .put = wm8350_put_volsw_2r_vu, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) \
+ {.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+ .rshift = xshift, .max = xmax, .invert = xinvert}, }
+
+static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" };
+static const char *wm8350_dacmutem[] = { "Normal", "Soft" };
+static const char *wm8350_dacmutes[] = { "Fast", "Slow" };
+static const char *wm8350_dacfilter[] = { "Normal", "Sloping" };
+static const char *wm8350_adcfilter[] = { "None", "High Pass" };
+static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" };
+static const char *wm8350_lr[] = { "Left", "Right" };
+
+static const struct soc_enum wm8350_enum[] = {
+ SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 4, 4, wm8350_deemp),
+ SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol),
+ SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem),
+ SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes),
+ SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 12, 2, wm8350_dacfilter),
+ SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter),
+ SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp),
+ SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol),
+ SOC_ENUM_SINGLE(WM8350_INPUT_MIXER_VOLUME, 15, 2, wm8350_lr),
+};
+
+static DECLARE_TLV_DB_LINEAR(pre_amp_tlv, -1200, 3525);
+static DECLARE_TLV_DB_LINEAR(out_pga_tlv, -5700, 600);
+static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1);
+static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1);
+static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1);
+
+static const unsigned int capture_sd_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 12, TLV_DB_SCALE_ITEM(-3600, 300, 1),
+ 13, 15, TLV_DB_SCALE_ITEM(0, 0, 0),
+};
+
+static const struct snd_kcontrol_new wm8350_snd_controls[] = {
+ SOC_ENUM("Playback Deemphasis", wm8350_enum[0]),
+ SOC_ENUM("Playback DAC Inversion", wm8350_enum[1]),
+ SOC_WM8350_DOUBLE_R_TLV("Playback PCM Volume",
+ WM8350_DAC_DIGITAL_VOLUME_L,
+ WM8350_DAC_DIGITAL_VOLUME_R,
+ 0, 255, 0, dac_pcm_tlv),
+ SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]),
+ SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]),
+ SOC_ENUM("Playback PCM Filter", wm8350_enum[4]),
+ SOC_ENUM("Capture PCM Filter", wm8350_enum[5]),
+ SOC_ENUM("Capture PCM HP Filter", wm8350_enum[6]),
+ SOC_ENUM("Capture ADC Inversion", wm8350_enum[7]),
+ SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume",
+ WM8350_ADC_DIGITAL_VOLUME_L,
+ WM8350_ADC_DIGITAL_VOLUME_R,
+ 0, 255, 0, adc_pcm_tlv),
+ SOC_DOUBLE_TLV("Capture Sidetone Volume",
+ WM8350_ADC_DIVIDER,
+ 8, 4, 15, 1, capture_sd_tlv),
+ SOC_WM8350_DOUBLE_R_TLV("Capture Volume",
+ WM8350_LEFT_INPUT_VOLUME,
+ WM8350_RIGHT_INPUT_VOLUME,
+ 2, 63, 0, pre_amp_tlv),
+ SOC_DOUBLE_R("Capture ZC Switch",
+ WM8350_LEFT_INPUT_VOLUME,
+ WM8350_RIGHT_INPUT_VOLUME, 13, 1, 0),
+ SOC_SINGLE_TLV("Left Input Left Sidetone Volume",
+ WM8350_OUTPUT_LEFT_MIXER_VOLUME, 1, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Left Input Right Sidetone Volume",
+ WM8350_OUTPUT_LEFT_MIXER_VOLUME,
+ 5, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Left Input Bypass Volume",
+ WM8350_OUTPUT_LEFT_MIXER_VOLUME,
+ 9, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Right Input Left Sidetone Volume",
+ WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+ 1, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Right Input Right Sidetone Volume",
+ WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+ 5, 7, 0, out_mix_tlv),
+ SOC_SINGLE_TLV("Right Input Bypass Volume",
+ WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+ 13, 7, 0, out_mix_tlv),
+ SOC_SINGLE("Left Input Mixer +20dB Switch",
+ WM8350_INPUT_MIXER_VOLUME_L, 0, 1, 0),
+ SOC_SINGLE("Right Input Mixer +20dB Switch",
+ WM8350_INPUT_MIXER_VOLUME_R, 0, 1, 0),
+ SOC_SINGLE_TLV("Out4 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME,
+ 1, 7, 0, out_mix_tlv),
+ SOC_WM8350_DOUBLE_R_TLV("Out1 Playback Volume",
+ WM8350_LOUT1_VOLUME,
+ WM8350_ROUT1_VOLUME,
+ 2, 63, 0, out_pga_tlv),
+ SOC_DOUBLE_R("Out1 Playback ZC Switch",
+ WM8350_LOUT1_VOLUME,
+ WM8350_ROUT1_VOLUME, 13, 1, 0),
+ SOC_WM8350_DOUBLE_R_TLV("Out2 Playback Volume",
+ WM8350_LOUT2_VOLUME,
+ WM8350_ROUT2_VOLUME,
+ 2, 63, 0, out_pga_tlv),
+ SOC_DOUBLE_R("Out2 Playback ZC Switch", WM8350_LOUT2_VOLUME,
+ WM8350_ROUT2_VOLUME, 13, 1, 0),
+ SOC_SINGLE("Out2 Right Invert Switch", WM8350_ROUT2_VOLUME, 10, 1, 0),
+ SOC_SINGLE_TLV("Out2 Beep Volume", WM8350_BEEP_VOLUME,
+ 5, 7, 0, out_mix_tlv),
+
+ SOC_DOUBLE_R("Out1 Playback Switch",
+ WM8350_LOUT1_VOLUME,
+ WM8350_ROUT1_VOLUME,
+ 14, 1, 1),
+ SOC_DOUBLE_R("Out2 Playback Switch",
+ WM8350_LOUT2_VOLUME,
+ WM8350_ROUT2_VOLUME,
+ 14, 1, 1),
+};
+
+/* Same as snd_soc_dapm_put_volsw, but change power state */
+static int wm8350_dapm_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_codec *codec = widget->codec;
+ if (ucontrol->value.integer.value[0]) {
+ if (codec->bias_level != SND_SOC_BIAS_ON) {
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_ON);
+ } else
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+ } else {
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ }
+ return 0;
+}
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Playback Mixer */
+static const struct snd_kcontrol_new wm8350_left_play_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch",
+ WM8350_LEFT_MIXER_CONTROL, 11, 1, 0),
+ SOC_SINGLE_EXT("Left Bypass Switch",
+ WM8350_LEFT_MIXER_CONTROL, 2, 1, 0,
+ snd_soc_dapm_get_volsw, wm8350_dapm_put_volsw),
+ SOC_DAPM_SINGLE("Right Playback Switch",
+ WM8350_LEFT_MIXER_CONTROL, 12, 1, 0),
+ SOC_DAPM_SINGLE("Left Sidetone Switch",
+ WM8350_LEFT_MIXER_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right Sidetone Switch",
+ WM8350_LEFT_MIXER_CONTROL, 1, 1, 0),
+};
+
+/* Right Playback Mixer */
+static const struct snd_kcontrol_new wm8350_right_play_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Playback Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 12, 1, 0),
+ SOC_SINGLE_EXT("Right Bypass Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 3, 1, 0,
+ snd_soc_dapm_get_volsw, wm8350_dapm_put_volsw),
+ SOC_DAPM_SINGLE("Left Playback Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Left Sidetone Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right Sidetone Switch",
+ WM8350_RIGHT_MIXER_CONTROL, 1, 1, 0),
+};
+
+/* Out4 Mixer */
+static const struct snd_kcontrol_new wm8350_out4_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right Playback Switch",
+ WM8350_OUT4_MIXER_CONTROL, 12, 1, 0),
+ SOC_DAPM_SINGLE("Left Playback Switch",
+ WM8350_OUT4_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Right Capture Switch",
+ WM8350_OUT4_MIXER_CONTROL, 9, 1, 0),
+ SOC_DAPM_SINGLE("Out3 Playback Switch",
+ WM8350_OUT4_MIXER_CONTROL, 2, 1, 0),
+ SOC_DAPM_SINGLE("Right Mixer Switch",
+ WM8350_OUT4_MIXER_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("Left Mixer Switch",
+ WM8350_OUT4_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* Out3 Mixer */
+static const struct snd_kcontrol_new wm8350_out3_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left Playback Switch",
+ WM8350_OUT3_MIXER_CONTROL, 11, 1, 0),
+ SOC_DAPM_SINGLE("Left Capture Switch",
+ WM8350_OUT3_MIXER_CONTROL, 8, 1, 0),
+ SOC_DAPM_SINGLE("Out4 Playback Switch",
+ WM8350_OUT3_MIXER_CONTROL, 3, 1, 0),
+ SOC_DAPM_SINGLE("Left Mixer Switch",
+ WM8350_OUT3_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* Left Input Mixer */
+static const struct snd_kcontrol_new wm8350_left_capt_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_L, 1, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_L, 9, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE("PGA Capture Switch",
+ WM8350_LEFT_INPUT_VOLUME, 14, 1, 1),
+};
+
+/* Right Input Mixer */
+static const struct snd_kcontrol_new wm8350_right_capt_mixer_controls[] = {
+ SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_R, 5, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
+ WM8350_INPUT_MIXER_VOLUME_R, 13, 7, 0, out_mix_tlv),
+ SOC_DAPM_SINGLE("PGA Capture Switch",
+ WM8350_RIGHT_INPUT_VOLUME, 14, 1, 1),
+};
+
+/* Left Mic Mixer */
+static const struct snd_kcontrol_new wm8350_left_mic_mixer_controls[] = {
+ SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 1, 1, 0),
+ SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 0, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 2, 1, 0),
+};
+
+/* Right Mic Mixer */
+static const struct snd_kcontrol_new wm8350_right_mic_mixer_controls[] = {
+ SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 9, 1, 0),
+ SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 8, 1, 0),
+ SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 10, 1, 0),
+};
+
+/* Beep Switch */
+static const struct snd_kcontrol_new wm8350_beep_switch_controls =
+SOC_DAPM_SINGLE("Switch", WM8350_BEEP_VOLUME, 15, 1, 1);
+
+/* Out4 Capture Mux */
+static const struct snd_kcontrol_new wm8350_out4_capture_controls =
+SOC_DAPM_ENUM("Route", wm8350_enum[8]);
+
+static const struct snd_soc_dapm_widget wm8350_dapm_widgets[] = {
+
+ SND_SOC_DAPM_PGA("IN3R PGA", WM8350_POWER_MGMT_2, 11, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IN3L PGA", WM8350_POWER_MGMT_2, 10, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("Right Out2 PGA", WM8350_POWER_MGMT_3, 3, 0, NULL,
+ 0, pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Left Out2 PGA", WM8350_POWER_MGMT_3, 2, 0, NULL, 0,
+ pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Right Out1 PGA", WM8350_POWER_MGMT_3, 1, 0, NULL,
+ 0, pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Left Out1 PGA", WM8350_POWER_MGMT_3, 0, 0, NULL, 0,
+ pga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MIXER("Right Capture Mixer", WM8350_POWER_MGMT_2,
+ 7, 0, &wm8350_right_capt_mixer_controls[0],
+ ARRAY_SIZE(wm8350_right_capt_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Capture Mixer", WM8350_POWER_MGMT_2,
+ 6, 0, &wm8350_left_capt_mixer_controls[0],
+ ARRAY_SIZE(wm8350_left_capt_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Out4 Mixer", WM8350_POWER_MGMT_2, 5, 0,
+ &wm8350_out4_mixer_controls[0],
+ ARRAY_SIZE(wm8350_out4_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Out3 Mixer", WM8350_POWER_MGMT_2, 4, 0,
+ &wm8350_out3_mixer_controls[0],
+ ARRAY_SIZE(wm8350_out3_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Playback Mixer", WM8350_POWER_MGMT_2, 1, 0,
+ &wm8350_right_play_mixer_controls[0],
+ ARRAY_SIZE(wm8350_right_play_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Playback Mixer", WM8350_POWER_MGMT_2, 0, 0,
+ &wm8350_left_play_mixer_controls[0],
+ ARRAY_SIZE(wm8350_left_play_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left Mic Mixer", WM8350_POWER_MGMT_2, 8, 0,
+ &wm8350_left_mic_mixer_controls[0],
+ ARRAY_SIZE(wm8350_left_mic_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right Mic Mixer", WM8350_POWER_MGMT_2, 9, 0,
+ &wm8350_right_mic_mixer_controls[0],
+ ARRAY_SIZE(wm8350_right_mic_mixer_controls)),
+
+ /* virtual mixer for Beep and Out2R */
+ SND_SOC_DAPM_MIXER("Out2 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("Beep", WM8350_POWER_MGMT_3, 7, 0,
+ &wm8350_beep_switch_controls),
+
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
+ WM8350_POWER_MGMT_4, 3, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture",
+ WM8350_POWER_MGMT_4, 2, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback",
+ WM8350_POWER_MGMT_4, 5, 0),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback",
+ WM8350_POWER_MGMT_4, 4, 0),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8350_POWER_MGMT_1, 4, 0),
+
+ SND_SOC_DAPM_MUX("Out4 Capture Channel", SND_SOC_NOPM, 0, 0,
+ &wm8350_out4_capture_controls),
+
+ SND_SOC_DAPM_OUTPUT("OUT1R"),
+ SND_SOC_DAPM_OUTPUT("OUT1L"),
+ SND_SOC_DAPM_OUTPUT("OUT2R"),
+ SND_SOC_DAPM_OUTPUT("OUT2L"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+
+ SND_SOC_DAPM_INPUT("IN1RN"),
+ SND_SOC_DAPM_INPUT("IN1RP"),
+ SND_SOC_DAPM_INPUT("IN2R"),
+ SND_SOC_DAPM_INPUT("IN1LP"),
+ SND_SOC_DAPM_INPUT("IN1LN"),
+ SND_SOC_DAPM_INPUT("IN2L"),
+ SND_SOC_DAPM_INPUT("IN3R"),
+ SND_SOC_DAPM_INPUT("IN3L"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* left playback mixer */
+ {"Left Playback Mixer", "Playback Switch", "Left DAC"},
+ {"Left Playback Mixer", "Left Bypass Switch", "IN3L PGA"},
+ {"Left Playback Mixer", "Right Playback Switch", "Right DAC"},
+ {"Left Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
+ {"Left Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
+
+ /* right playback mixer */
+ {"Right Playback Mixer", "Playback Switch", "Right DAC"},
+ {"Right Playback Mixer", "Right Bypass Switch", "IN3R PGA"},
+ {"Right Playback Mixer", "Left Playback Switch", "Left DAC"},
+ {"Right Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
+ {"Right Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
+
+ /* out4 playback mixer */
+ {"Out4 Mixer", "Right Playback Switch", "Right DAC"},
+ {"Out4 Mixer", "Left Playback Switch", "Left DAC"},
+ {"Out4 Mixer", "Right Capture Switch", "Right Capture Mixer"},
+ {"Out4 Mixer", "Out3 Playback Switch", "Out3 Mixer"},
+ {"Out4 Mixer", "Right Mixer Switch", "Right Playback Mixer"},
+ {"Out4 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
+ {"OUT4", NULL, "Out4 Mixer"},
+
+ /* out3 playback mixer */
+ {"Out3 Mixer", "Left Playback Switch", "Left DAC"},
+ {"Out3 Mixer", "Left Capture Switch", "Left Capture Mixer"},
+ {"Out3 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
+ {"Out3 Mixer", "Out4 Playback Switch", "Out4 Mixer"},
+ {"OUT3", NULL, "Out3 Mixer"},
+
+ /* out2 */
+ {"Right Out2 PGA", NULL, "Right Playback Mixer"},
+ {"Left Out2 PGA", NULL, "Left Playback Mixer"},
+ {"OUT2L", NULL, "Left Out2 PGA"},
+ {"OUT2R", NULL, "Right Out2 PGA"},
+
+ /* out1 */
+ {"Right Out1 PGA", NULL, "Right Playback Mixer"},
+ {"Left Out1 PGA", NULL, "Left Playback Mixer"},
+ {"OUT1L", NULL, "Left Out1 PGA"},
+ {"OUT1R", NULL, "Right Out1 PGA"},
+
+ /* ADCs */
+ {"Left ADC", NULL, "Left Capture Mixer"},
+ {"Right ADC", NULL, "Right Capture Mixer"},
+
+ /* Left capture mixer */
+ {"Left Capture Mixer", "L2 Capture Volume", "IN2L"},
+ {"Left Capture Mixer", "L3 Capture Volume", "IN3L PGA"},
+ {"Left Capture Mixer", "PGA Capture Switch", "Left Mic Mixer"},
+ {"Left Capture Mixer", NULL, "Out4 Capture Channel"},
+
+ /* Right capture mixer */
+ {"Right Capture Mixer", "L2 Capture Volume", "IN2R"},
+ {"Right Capture Mixer", "L3 Capture Volume", "IN3R PGA"},
+ {"Right Capture Mixer", "PGA Capture Switch", "Right Mic Mixer"},
+ {"Right Capture Mixer", NULL, "Out4 Capture Channel"},
+
+ /* L3 Inputs */
+ {"IN3L PGA", NULL, "IN3L"},
+ {"IN3R PGA", NULL, "IN3R"},
+
+ /* Left Mic mixer */
+ {"Left Mic Mixer", "INN Capture Switch", "IN1LN"},
+ {"Left Mic Mixer", "INP Capture Switch", "IN1LP"},
+ {"Left Mic Mixer", "IN2 Capture Switch", "IN2L"},
+
+ /* Right Mic mixer */
+ {"Right Mic Mixer", "INN Capture Switch", "IN1RN"},
+ {"Right Mic Mixer", "INP Capture Switch", "IN1RP"},
+ {"Right Mic Mixer", "IN2 Capture Switch", "IN2R"},
+
+ /* out 4 capture */
+ {"Out4 Capture Channel", NULL, "Out4 Mixer"},
+
+ /* Beep */
+ {"Beep", NULL, "IN3R PGA"},
+};
+
+static int wm8350_add_controls(struct snd_soc_codec *codec)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8350_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8350_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int wm8350_add_widgets(struct snd_soc_codec *codec)
+{
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(codec,
+ wm8350_dapm_widgets,
+ ARRAY_SIZE(wm8350_dapm_widgets));
+ if (ret != 0) {
+ pr_err("wm8350: dapm control register failed\n");
+ return ret;
+ }
+
+ /* set up audio paths */
+ ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ if (ret != 0) {
+ pr_err("wm8350: DAPM route register failed\n");
+ return ret;
+ }
+
+ return snd_soc_dapm_new_widgets(codec);
+}
+
+static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8350 *wm8350 = codec->control_data;
+ u16 fll_4;
+
+ switch (clk_id) {
+ case WM8350_MCLK_SEL_MCLK:
+ wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_1,
+ WM8350_MCLK_SEL);
+ break;
+ case WM8350_MCLK_SEL_PLL_MCLK:
+ case WM8350_MCLK_SEL_PLL_DAC:
+ case WM8350_MCLK_SEL_PLL_ADC:
+ case WM8350_MCLK_SEL_PLL_32K:
+ wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1,
+ WM8350_MCLK_SEL);
+ fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+ ~WM8350_FLL_CLK_SRC_MASK;
+ wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
+ break;
+ }
+
+ /* MCLK direction */
+ if (dir == WM8350_MCLK_DIR_OUT)
+ wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_MCLK_DIR);
+ else
+ wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_MCLK_DIR);
+
+ return 0;
+}
+
+static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 val;
+
+ switch (div_id) {
+ case WM8350_ADC_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_ADC_DIVIDER) &
+ ~WM8350_ADC_CLKDIV_MASK;
+ wm8350_codec_write(codec, WM8350_ADC_DIVIDER, val | div);
+ break;
+ case WM8350_DAC_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_DAC_CLOCK_CONTROL) &
+ ~WM8350_DAC_CLKDIV_MASK;
+ wm8350_codec_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
+ break;
+ case WM8350_BCLK_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+ ~WM8350_BCLK_DIV_MASK;
+ wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ break;
+ case WM8350_OPCLK_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+ ~WM8350_OPCLK_DIV_MASK;
+ wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ break;
+ case WM8350_SYS_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+ ~WM8350_MCLK_DIV_MASK;
+ wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+ break;
+ case WM8350_DACLR_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+ ~WM8350_DACLRC_RATE_MASK;
+ wm8350_codec_write(codec, WM8350_DAC_LR_RATE, val | div);
+ break;
+ case WM8350_ADCLR_CLKDIV:
+ val = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+ ~WM8350_ADCLRC_RATE_MASK;
+ wm8350_codec_write(codec, WM8350_ADC_LR_RATE, val | div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+ ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK);
+ u16 master = wm8350_codec_read(codec, WM8350_AI_DAC_CONTROL) &
+ ~WM8350_BCLK_MSTR;
+ u16 dac_lrc = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+ ~WM8350_DACLRC_ENA;
+ u16 adc_lrc = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+ ~WM8350_ADCLRC_ENA;
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master |= WM8350_BCLK_MSTR;
+ dac_lrc |= WM8350_DACLRC_ENA;
+ adc_lrc |= WM8350_ADCLRC_ENA;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= 0x2 << 8;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= 0x1 << 8;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface |= 0x3 << 8;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ iface |= 0x3 << 8; /* lg not sure which mode */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= WM8350_AIF_LRCLK_INV | WM8350_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= WM8350_AIF_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface |= WM8350_AIF_LRCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+ wm8350_codec_write(codec, WM8350_AI_DAC_CONTROL, master);
+ wm8350_codec_write(codec, WM8350_DAC_LR_RATE, dac_lrc);
+ wm8350_codec_write(codec, WM8350_ADC_LR_RATE, adc_lrc);
+ return 0;
+}
+
+static int wm8350_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int master = wm8350_codec_cache_read(codec, WM8350_AI_DAC_CONTROL) &
+ WM8350_BCLK_MSTR;
+ int enabled = 0;
+
+ /* Check that the DACs or ADCs are enabled since they are
+ * required for LRC in master mode. The DACs or ADCs need a
+ * valid audio path i.e. pin -> ADC or DAC -> pin before
+ * the LRC will be enabled in master mode. */
+ if (!master && cmd != SNDRV_PCM_TRIGGER_START)
+ return 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
+ (WM8350_ADCR_ENA | WM8350_ADCL_ENA);
+ } else {
+ enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
+ (WM8350_DACR_ENA | WM8350_DACL_ENA);
+ }
+
+ if (!enabled) {
+ pr_err("wm8350: %s: invalid audio path - no clocks available\n",
+ __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+ ~WM8350_AIF_WL_MASK;
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x1 << 10;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface |= 0x2 << 10;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x3 << 10;
+ break;
+ }
+
+ wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+ return 0;
+}
+
+static int wm8350_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8350 *wm8350 = codec->control_data;
+
+ if (mute)
+ wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+ else
+ wm8350_clear_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+ return 0;
+}
+
+/* FLL divisors */
+struct _fll_div {
+ int div; /* FLL_OUTDIV */
+ int n;
+ int k;
+ int ratio; /* FLL_FRATIO */
+};
+
+/* The size in bits of the fll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
+ unsigned int output)
+{
+ u64 Kpart;
+ unsigned int t1, t2, K, Nmod;
+
+ if (output >= 2815250 && output <= 3125000)
+ fll_div->div = 0x4;
+ else if (output >= 5625000 && output <= 6250000)
+ fll_div->div = 0x3;
+ else if (output >= 11250000 && output <= 12500000)
+ fll_div->div = 0x2;
+ else if (output >= 22500000 && output <= 25000000)
+ fll_div->div = 0x1;
+ else {
+ printk(KERN_ERR "wm8350: fll freq %d out of range\n", output);
+ return -EINVAL;
+ }
+
+ if (input > 48000)
+ fll_div->ratio = 1;
+ else
+ fll_div->ratio = 8;
+
+ t1 = output * (1 << (fll_div->div + 1));
+ t2 = input * fll_div->ratio;
+
+ fll_div->n = t1 / t2;
+ Nmod = t1 % t2;
+
+ if (Nmod) {
+ Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+ do_div(Kpart, t2);
+ K = Kpart & 0xFFFFFFFF;
+
+ /* Check if we need to round */
+ if ((K % 10) >= 5)
+ K += 5;
+
+ /* Move down to proper range now rounding is done */
+ K /= 10;
+ fll_div->k = K;
+ } else
+ fll_div->k = 0;
+
+ return 0;
+}
+
+static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
+ int pll_id, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct wm8350 *wm8350 = codec->control_data;
+ struct _fll_div fll_div;
+ int ret = 0;
+ u16 fll_1, fll_4;
+
+ /* power down FLL - we need to do this for reconfiguration */
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
+ WM8350_FLL_ENA | WM8350_FLL_OSC_ENA);
+
+ if (freq_out == 0 || freq_in == 0)
+ return ret;
+
+ ret = fll_factors(&fll_div, freq_in, freq_out);
+ if (ret < 0)
+ return ret;
+ dev_dbg(wm8350->dev,
+ "FLL in %d FLL out %d N 0x%x K 0x%x div %d ratio %d",
+ freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div,
+ fll_div.ratio);
+
+ /* set up N.K & dividers */
+ fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) &
+ ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000);
+ wm8350_codec_write(codec, WM8350_FLL_CONTROL_1,
+ fll_1 | (fll_div.div << 8));
+ wm8350_codec_write(codec, WM8350_FLL_CONTROL_2,
+ (fll_div.ratio << 11) |
+ (fll_div.n & WM8350_FLL_N_MASK));
+ wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
+ fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+ ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
+ wm8350_codec_write(codec, WM8350_FLL_CONTROL_4,
+ fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) |
+ (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));
+
+ /* power FLL on */
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA);
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA);
+
+ return 0;
+}
+
+static int wm8350_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct wm8350 *wm8350 = codec->control_data;
+ struct wm8350_data *priv = codec->private_data;
+ struct wm8350_audio_platform_data *platform =
+ wm8350->codec.platform_data;
+ u16 pm1;
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_VMID_50K |
+ platform->codec_current_on << 14);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1);
+ pm1 &= ~WM8350_VMID_MASK;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_VMID_50K);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret != 0)
+ return ret;
+
+ /* Enable the system clock */
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4,
+ WM8350_SYSCLK_ENA);
+
+ /* mute DAC & outputs */
+ wm8350_set_bits(wm8350, WM8350_DAC_MUTE,
+ WM8350_DAC_MUTE_ENA);
+
+ /* discharge cap memory */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ platform->dis_out1 |
+ (platform->dis_out2 << 2) |
+ (platform->dis_out3 << 4) |
+ (platform->dis_out4 << 6));
+
+ /* wait for discharge */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->
+ cap_discharge_msecs));
+
+ /* enable antipop */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ (platform->vmid_s_curve << 8));
+
+ /* ramp up vmid */
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ (platform->
+ codec_current_charge << 14) |
+ WM8350_VMID_5K | WM8350_VMIDEN |
+ WM8350_VBUFEN);
+
+ /* wait for vmid */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->
+ vmid_charge_msecs));
+
+ /* turn on vmid 300k */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+ pm1 |= WM8350_VMID_300K |
+ (platform->codec_current_standby << 14);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1);
+
+
+ /* enable analogue bias */
+ pm1 |= WM8350_BIASEN;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+ /* disable antipop */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
+
+ } else {
+ /* turn on vmid 300k and reduce current */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_VMID_300K |
+ (platform->
+ codec_current_standby << 14));
+
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+
+ /* mute DAC & enable outputs */
+ wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_3,
+ WM8350_OUT1L_ENA | WM8350_OUT1R_ENA |
+ WM8350_OUT2L_ENA | WM8350_OUT2R_ENA);
+
+ /* enable anti pop S curve */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ (platform->vmid_s_curve << 8));
+
+ /* turn off vmid */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~WM8350_VMIDEN;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+ /* wait */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->
+ vmid_discharge_msecs));
+
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+ (platform->vmid_s_curve << 8) |
+ platform->dis_out1 |
+ (platform->dis_out2 << 2) |
+ (platform->dis_out3 << 4) |
+ (platform->dis_out4 << 6));
+
+ /* turn off VBuf and drain */
+ pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+ ~(WM8350_VBUFEN | WM8350_VMID_MASK);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+ pm1 | WM8350_OUTPUT_DRAIN_EN);
+
+ /* wait */
+ schedule_timeout_interruptible(msecs_to_jiffies
+ (platform->drain_msecs));
+
+ pm1 &= ~WM8350_BIASEN;
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+ /* disable anti-pop */
+ wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
+
+ wm8350_clear_bits(wm8350, WM8350_LOUT1_VOLUME,
+ WM8350_OUT1L_ENA);
+ wm8350_clear_bits(wm8350, WM8350_ROUT1_VOLUME,
+ WM8350_OUT1R_ENA);
+ wm8350_clear_bits(wm8350, WM8350_LOUT2_VOLUME,
+ WM8350_OUT2L_ENA);
+ wm8350_clear_bits(wm8350, WM8350_ROUT2_VOLUME,
+ WM8350_OUT2R_ENA);
+
+ /* disable clock gen */
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
+ WM8350_SYSCLK_ENA);
+
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+static int wm8350_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int wm8350_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+ return 0;
+}
+
+static int wm8350_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ struct wm8350 *wm8350 = codec->control_data;
+ struct wm8350_data *priv = codec->private_data;
+ int ret;
+ struct wm8350_output *out1;
+ struct wm8350_output *out2;
+
+ /* Put the codec into reset if it wasn't already */
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+ /* Enable the codec */
+ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+ /* Enable robust clocking mode in ADC */
+ wm8350_codec_write(codec, WM8350_SECURITY, 0xa7);
+ wm8350_codec_write(codec, 0xde, 0x13);
+ wm8350_codec_write(codec, WM8350_SECURITY, 0);
+
+ /* read OUT1 & OUT2 volumes */
+ out1 = &priv->out1;
+ out2 = &priv->out2;
+ out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) &
+ WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+ out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) &
+ WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) &
+ WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+ out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) &
+ WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+ wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0);
+ wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0);
+ wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU);
+ wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0);
+ wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0);
+ wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU);
+
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ pr_err("wm8350: failed to create pcms\n");
+ return ret;
+ }
+
+ wm8350_add_controls(codec);
+ wm8350_add_widgets(codec);
+
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ pr_err("wm8350: failed to register card\n");
+ goto card_err;
+ }
+
+ return 0;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ return ret;
+}
+
+static int wm8350_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ struct wm8350_data *priv = codec->private_data;
+ struct wm8350 *wm8350 = codec->control_data;
+ int ret;
+
+ /* cancel any work waiting to be queued. */
+ ret = cancel_delayed_work(&codec->delayed_work);
+
+ /* if there was any work waiting then we run it now and
+ * wait for its completion */
+ if (ret) {
+ schedule_delayed_work(&codec->delayed_work, 0);
+ flush_scheduled_work();
+ }
+
+ wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+ regulator_bulk_free(ARRAY_SIZE(priv->supplies), priv->supplies);
+ kfree(codec->private_data);
+ kfree(codec);
+
+ return 0;
+}
+
+#define WM8350_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define WM8350_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8350_dai = {
+ .name = "WM8350",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8350_RATES,
+ .formats = WM8350_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = WM8350_RATES,
+ .formats = WM8350_FORMATS,
+ },
+ .ops = {
+ .hw_params = wm8350_pcm_hw_params,
+ .trigger = wm8350_pcm_trigger,
+ },
+ .dai_ops = {
+ .digital_mute = wm8350_mute,
+ .set_sysclk = wm8350_set_dai_sysclk,
+ .set_fmt = wm8350_set_dai_fmt,
+ .set_pll = wm8350_set_fll,
+ .set_clkdiv = wm8350_set_clkdiv,
+ },
+};
+EXPORT_SYMBOL_GPL(wm8350_dai);
+
+static int wm8350_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct wm8350 *wm8350 = socdev->codec_data;
+ struct wm8350_audio_platform_data *platform =
+ wm8350->codec.platform_data;
+ struct snd_soc_codec *codec;
+ struct wm8350_data *priv;
+ int ret;
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+
+ ret = -ENODEV;
+ priv = kzalloc(sizeof(struct wm8350_data), GFP_KERNEL);
+ if (priv == NULL)
+ goto err_codec;
+
+ priv->supplies[0].supply = platform->regulator1;
+ priv->supplies[1].supply = platform->regulator2;
+
+ ret = regulator_bulk_get(&pdev->dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret != 0)
+ goto err_priv;
+
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+ codec->name = "WM8350";
+ codec->owner = THIS_MODULE;
+ codec->read = wm8350_codec_read;
+ codec->write = wm8350_codec_write;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = wm8350_set_bias_level;
+ codec->dai = &wm8350_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = WM8350_MAX_REGISTER;
+ codec->private_data = priv;
+ codec->control_data = wm8350;
+
+ INIT_DELAYED_WORK(&codec->delayed_work, wm8350_pga_work);
+
+ wm8350_init(socdev);
+
+ return 0;
+
+err_priv:
+ kfree(codec->private_data);
+err_codec:
+ kfree(codec);
+ return ret;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8350 = {
+ .probe = wm8350_probe,
+ .remove = wm8350_remove,
+ .suspend = wm8350_suspend,
+ .resume = wm8350_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8350);
+
+MODULE_DESCRIPTION("ASoC WM8350 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-codec");
diff --git a/sound/soc/codecs/wm8350.h b/sound/soc/codecs/wm8350.h
new file mode 100644
index 000000000000..cc2887aa6c38
--- /dev/null
+++ b/sound/soc/codecs/wm8350.h
@@ -0,0 +1,20 @@
+/*
+ * wm8350.h - WM8903 audio codec interface
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ * 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 _WM8350_H
+#define _WM8350_H
+
+#include <sound/soc.h>
+
+extern struct snd_soc_dai wm8350_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8350;
+
+#endif
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 627ebfb4209b..5b77a51d264d 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -171,6 +172,8 @@ struct wm8580_priv {
#define WM8580_DAC_CONTROL5_MUTEALL 0x10
+#define WM8580_REG_DATA_LENGTH 9
+
/*
* wm8580 register cache
* We can't read the WM8580 register space when we
@@ -215,13 +218,18 @@ static inline void wm8580_write_reg_cache(struct snd_soc_codec *codec,
cache[reg] = value;
}
+static struct snd_soc_device *wm8580_socdev;
+
/*
* write to the WM8580 register space
*/
static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
+ struct snd_soc_device *socdev = wm8580_socdev;
+ struct wm8580_setup_data *setup = socdev->codec_data;
u8 data[2];
+ int ret = 0;
BUG_ON(reg > ARRAY_SIZE(wm8580_reg));
@@ -237,18 +245,20 @@ static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg,
return 0;
}
- /* data is
- * D15..D9 WM8580 register offset
- * D8...D0 register data
- */
- data[0] = (reg << 1) | ((value >> 8) & 0x0001);
- data[1] = value & 0x00ff;
-
wm8580_write_reg_cache(codec, reg, value);
- if (codec->hw_write(codec->control_data, data, 2) == 2)
- return 0;
- else
- return -EIO;
+
+ if (setup->spi) {
+ data[1] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[0] = value & 0x00ff;
+ ret = codec->hw_write(codec->control_data, data, 1);
+ } else if (setup->i2c_address) {
+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+ data[1] = value & 0x00ff;
+ ret = codec->hw_write(codec->control_data, data, 2);
+ } else
+ ret = -EIO;
+
+ return ret;
}
static inline unsigned int wm8580_read(struct snd_soc_codec *codec,
@@ -265,9 +275,11 @@ static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- int reg = kcontrol->private_value & 0xff;
- int reg2 = (kcontrol->private_value >> 24) & 0xff;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
int ret;
u16 val;
@@ -289,15 +301,16 @@ static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
return 0;
}
-#define SOC_WM8580_OUT_DOUBLE_R_TLV(xname, reg_left, reg_right, shift, max, invert, tlv_array) \
+#define SOC_WM8580_OUT_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
SNDRV_CTL_ELEM_ACCESS_READWRITE, \
.tlv.p = (tlv_array), \
.info = snd_soc_info_volsw_2r, \
.get = snd_soc_get_volsw_2r, .put = wm8580_out_vu, \
- .private_value = (reg_left) | ((shift) << 8) | \
- ((max) << 12) | ((invert) << 20) | ((reg_right) << 24) }
+ .private_value = (unsigned long)&(struct soc_mixer_control) \
+ {.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+ .max = xmax, .invert = xinvert}, }
static const struct snd_kcontrol_new wm8580_snd_controls[] = {
SOC_WM8580_OUT_DOUBLE_R_TLV("DAC1 Playback Volume",
@@ -312,7 +325,6 @@ SOC_WM8580_OUT_DOUBLE_R_TLV("DAC3 Playback Volume",
WM8580_DIGITAL_ATTENUATION_DACL3,
WM8580_DIGITAL_ATTENUATION_DACR3,
0, 0xff, 0, dac_tlv),
-
SOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0),
SOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0),
SOC_SINGLE("DAC3 Deemphasis Switch", WM8580_DAC_CONTROL3, 2, 1, 0),
@@ -531,9 +543,9 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
(pll_div.k >> 18 & 0xf) | (pll_div.n << 4));
reg = wm8580_read(codec, WM8580_PLLA4 + offset);
- reg &= ~0x3f;
+ reg &= ~0x3b;
reg |= pll_div.prescale | pll_div.postscale << 1 |
- pll_div.freqmode << 4;
+ pll_div.freqmode << 3;
wm8580_write(codec, WM8580_PLLA4 + offset, reg);
@@ -707,7 +719,6 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
case WM8580_CLKSRC_PLLB:
reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB;
break;
-
default:
return -EINVAL;
}
@@ -738,6 +749,33 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
wm8580_write(codec, WM8580_PLLB4, reg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int wm8580_set_paif_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int reg;
+
+ switch (clk_id) {
+ case WM8580_BCLK_CLKDIV:
+ reg = wm8580_read(codec, WM8580_PAIF1) &
+ ~WM8580_AIF_BCLKSEL_MASK;
+ wm8580_write(codec, WM8580_PAIF1, reg | freq);
+ reg = wm8580_read(codec, WM8580_PAIF2) &
+ ~WM8580_AIF_BCLKSEL_MASK;
+ wm8580_write(codec, WM8580_PAIF2, reg | freq);
+ break;
+ case WM8580_LRCLK_CLKDIV:
+ reg = wm8580_read(codec, WM8580_PAIF1) & ~0x07;
+ wm8580_write(codec, WM8580_PAIF1, reg | freq);
break;
default:
@@ -801,6 +839,7 @@ struct snd_soc_dai wm8580_dai[] = {
},
.dai_ops = {
.set_fmt = wm8580_set_paif_dai_fmt,
+ .set_sysclk = wm8580_set_paif_dai_sysclk,
.set_clkdiv = wm8580_set_dai_clkdiv,
.set_pll = wm8580_set_dai_pll,
.digital_mute = wm8580_digital_mute,
@@ -836,6 +875,7 @@ static int wm8580_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
int ret = 0;
+ u16 data;
codec->name = "WM8580";
codec->owner = THIS_MODULE;
@@ -851,6 +891,11 @@ static int wm8580_init(struct snd_soc_device *socdev)
if (codec->reg_cache == NULL)
return -ENOMEM;
+ wm8580_write(codec, WM8580_READBACK, 0x10);
+ data = 2 << WM8580_REG_DATA_LENGTH;
+ codec->hw_read(codec->control_data, (char *)&data, 1);
+ pr_info("wm8580device rev:%d\n", data & 0xff);
+
/* Get the codec into a known state */
wm8580_write(codec, WM8580_RESET, 0);
@@ -888,10 +933,6 @@ pcm_err:
return ret;
}
-/* If the i2c layer weren't so broken, we could pass this kind of data
- around */
-static struct snd_soc_device *wm8580_socdev;
-
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
@@ -981,6 +1022,75 @@ static struct i2c_client client_template = {
};
#endif
+#if defined(CONFIG_SPI) || defined(CONFIG_SPI_MODULE)
+
+ /*!
+ * This function is called to transfer data to WM8580 on SPI.
+ *
+ * @param spi the SPI slave device(WM8580)
+ * @param buf the pointer to the data buffer
+ * @param len the length of the data to be transferred
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+static inline int spi_rw(void *control_data, char *data, int length)
+{
+ struct spi_transfer t = {
+ .tx_buf = (const void *)data,
+ .rx_buf = (void *)data,
+ .len = length,
+ .cs_change = 0,
+ .delay_usecs = 0,
+ };
+ struct spi_message m;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ return spi_sync((struct spi_device *)control_data, &m);
+
+}
+
+/*!
+ * This function is called whenever the SPI slave device is detected.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devinit wm8580_spi_probe(struct spi_device *spi)
+{
+ int ret;
+ struct snd_soc_device *socdev = wm8580_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ spi->bits_per_word = 16;
+ spi->mode = SPI_MODE_2;
+
+ spi_setup(spi);
+
+ codec->control_data = spi;
+ ret = wm8580_init(socdev);
+ if (ret < 0)
+ pr_err("Failed to initialise WM8580\n");
+
+ return ret;
+}
+
+static int __devinit wm8580_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver wm8580_spi_driver = {
+ .driver = {
+ .name = "wm8580_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8580_spi_probe,
+ .remove = __devexit_p(wm8580_spi_remove),
+};
+#endif
+
static int wm8580_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -1017,7 +1127,14 @@ static int wm8580_probe(struct platform_device *pdev)
if (ret != 0)
printk(KERN_ERR "can't add i2c driver");
}
-#else
+#endif
+
+#if defined(CONFIG_SPI) || defined(CONFIG_SPI_MODULE)
+ if (setup->spi) {
+ codec->hw_write = (hw_write_t) spi_rw;
+ codec->hw_read = (hw_read_t) spi_rw;
+ spi_register_driver(&wm8580_spi_driver);
+ }
/* Add other interfaces here */
#endif
return ret;
@@ -1028,13 +1145,22 @@ static int wm8580_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
+ struct wm8580_setup_data *setup;
if (codec->control_data)
wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
+
+ setup = socdev->codec_data;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_del_driver(&wm8580_i2c_driver);
+ if (setup->i2c_address)
+ i2c_del_driver(&wm8580_i2c_driver);
+#endif
+
+#if defined(CONFIG_SPI) || defined(CONFIG_SPI_MODULE)
+ if (setup->spi)
+ spi_unregister_driver(&wm8580_spi_driver);
#endif
kfree(codec->private_data);
kfree(codec);
diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h
index 589ddaba21d7..0c51ede6b13b 100644
--- a/sound/soc/codecs/wm8580.h
+++ b/sound/soc/codecs/wm8580.h
@@ -28,7 +28,12 @@
#define WM8580_CLKSRC_OSC 4
#define WM8580_CLKSRC_NONE 5
+/*clock divider id's */
+#define WM8580_BCLK_CLKDIV 0
+#define WM8580_LRCLK_CLKDIV 1
+
struct wm8580_setup_data {
+ unsigned short spi;
unsigned short i2c_address;
};
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index d426eaa22185..c668d094fffd 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1277,7 +1277,13 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
/* set vmid to 50k and unmute dac */
- wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
+ /* wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0); */
+ /*
+ * Force the enable of the MICBIAS, otherwise the microphone will not
+ * work.
+ * (Luis Galdos)
+ */
+ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00e0);
break;
case SND_SOC_BIAS_PREPARE:
/* set vmid to 5k for quick power up */
@@ -1783,7 +1789,7 @@ static int wm8753_probe(struct platform_device *pdev)
struct wm8753_priv *wm8753;
int ret = 0;
- pr_info("WM8753 Audio Codec %s", WM8753_VERSION);
+ pr_info("WM8753 Audio Codec %s\n", WM8753_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
@@ -1850,19 +1856,30 @@ static int wm8753_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
+ struct wm8753_setup_data *setup;
if (codec->control_data)
wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
run_delayed_work(&codec->delayed_work);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
+
+ /*
+ * Unregister the devices depending on the passed setup configuration
+ * (Luis Galdos)
+ */
+ setup = socdev->codec_data;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
- i2c_unregister_device(codec->control_data);
- i2c_del_driver(&wm8753_i2c_driver);
+ if (setup->i2c_address) {
+ i2c_unregister_device(codec->control_data);
+ i2c_del_driver(&wm8753_i2c_driver);
+ }
#endif
#if defined(CONFIG_SPI_MASTER)
- spi_unregister_driver(&wm8753_spi_driver);
+ if (setup->spi)
+ spi_unregister_driver(&wm8753_spi_driver);
#endif
+
kfree(codec->private_data);
kfree(codec);
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
new file mode 100644
index 000000000000..c81f943a2e10
--- /dev/null
+++ b/sound/soc/imx/Kconfig
@@ -0,0 +1,72 @@
+config SND_MXC_SOC
+ tristate "SoC Audio for the Freescale i.MX CPU"
+ depends on ARCH_MXC && SND
+ select SND_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the MXC I2S or SSP interface. You will also need
+ to select the audio interfaces to support below.
+
+if SND_MXC_SOC
+
+config SND_MXC_SOC_SSI
+ tristate
+
+config SND_MXC_SOC_ESAI
+ tristate
+
+config SND_MXC_SOC_IRAM
+ bool "Locate Audio DMA playback buffers in IRAM"
+ help
+ Say Y if you don't want Audio playback buffers in external ram
+
+config SND_SOC_IMX_3STACK_WM8350
+ tristate "SoC Audio support for IMX - WM8350"
+ depends on MFD_WM8350
+ select SND_MXC_SOC_SSI
+ select SND_SOC_WM8350
+ help
+ Say Y if you want to add support for SoC audio on IMX 3STACK
+ with the WM8350.
+
+config SND_SOC_IMX_3STACK_SGTL5000
+ tristate "SoC Audio support for IMX - SGTL5000"
+ select SND_MXC_SOC_SSI
+ select SND_SOC_SGTL5000
+ help
+ Say Y if you want to add support for SoC audio on IMX 3STACK
+ with the SGTL5000.
+
+config SND_SOC_IMX_3STACK_AK4647
+ tristate "SoC Audio support for IMX - AK4647"
+ select SND_MXC_SOC_SSI
+ select SND_SOC_AK4647
+ help
+ Say Y if you want to add support for SoC audio on IMX 3STACK
+ with the AK4647.
+
+config SND_SOC_IMX_3STACK_WM8580
+ tristate "SoC Audio support for IMX - WM8580"
+ select SND_MXC_SOC_ESAI
+ select SND_SOC_WM8580
+ help
+ Say Y if you want to add support for Soc audio on IMX 3STACK
+ with the WM8580
+
+config SND_SOC_IMX_3STACK_AK5702
+ tristate "SoC Audio support for IMX - AK5702"
+ select SND_MXC_SOC_ESAI
+ select SND_SOC_AK5702
+ help
+ Say Y if you want to add support for Soc audio on IMX 3STACK
+ with the AK5702
+
+config SND_SOC_IMX_3STACK_BLUETOOTH
+ tristate "SoC Audio support for IMX - BLUETOOTH"
+ select SND_MXC_SOC_SSI
+ select SND_SOC_BLUETOOTH
+ help
+ Say Y if you want to add support for Soc audio on IMX 3STACK
+ with the BLUETOOTH
+endif
+
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
new file mode 100644
index 000000000000..0460f965a85b
--- /dev/null
+++ b/sound/soc/imx/Makefile
@@ -0,0 +1,23 @@
+# i.MX Platform Support
+snd-soc-imx-objs := imx-pcm.o
+snd-soc-imx-ssi-objs := imx-ssi.o
+snd-soc-imx-esai-objs := imx-esai.o
+
+obj-$(CONFIG_SND_MXC_SOC) += snd-soc-imx.o
+obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-imx-ssi.o
+obj-$(CONFIG_SND_MXC_SOC_ESAI) += snd-soc-imx-esai.o
+
+# i.MX Machine Support
+snd-soc-imx-3stack-wm8350-objs := imx-3stack-wm8350.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_WM8350) += snd-soc-imx-3stack-wm8350.o
+snd-soc-imx-3stack-sgtl5000-objs := imx-3stack-sgtl5000.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_SGTL5000) += snd-soc-imx-3stack-sgtl5000.o
+snd-soc-imx-3stack-ak4647-objs := imx-3stack-ak4647.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_AK4647) += snd-soc-imx-3stack-ak4647.o
+snd-soc-imx-3stack-wm8580-objs := imx-3stack-wm8580.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_WM8580) += snd-soc-imx-3stack-wm8580.o
+snd-soc-imx-3stack-ak5702-objs := imx-3stack-ak5702.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_AK5702) += snd-soc-imx-3stack-ak5702.o
+snd-soc-imx-3stack-bt-objs := imx-3stack-bt.o
+obj-$(CONFIG_SND_SOC_IMX_3STACK_BLUETOOTH) += snd-soc-imx-3stack-bt.o
+
diff --git a/sound/soc/imx/imx-3stack-ak4647.c b/sound/soc/imx/imx-3stack-ak4647.c
new file mode 100644
index 000000000000..aaab351487c8
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-ak4647.c
@@ -0,0 +1,441 @@
+/*
+ * imx-3stack-ak4647.c -- SoC audio for imx_3stack
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <mach/clock.h>
+#include <mach/mxc.h>
+#include <linux/regulator/consumer.h>
+
+#include "imx-pcm.h"
+#include "imx-ssi.h"
+
+#define AK4647_SSI_MASTER 1
+
+extern struct snd_soc_dai ak4647_hifi_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4647;
+
+static void headphone_detect_handler(struct work_struct *work);
+static DECLARE_WORK(hp_event, headphone_detect_handler);
+static int ak4647_jack_func;
+static int ak4647_spk_func;
+
+struct imx_3stack_priv {
+ struct platform_device *pdev;
+};
+
+static struct imx_3stack_priv machine_priv;
+
+static void imx_3stack_init_dam(int ssi_port, int dai_port)
+{
+ /* AK4647 uses SSI1 or SSI2 via AUDMUX port dai_port for audio */
+ unsigned int ssi_ptcr = 0;
+ unsigned int dai_ptcr = 0;
+ unsigned int ssi_pdcr = 0;
+ unsigned int dai_pdcr = 0;
+
+ /* reset port ssi_port & dai_port */
+ __raw_writel(0, DAM_PTCR(ssi_port));
+ __raw_writel(0, DAM_PTCR(dai_port));
+ __raw_writel(0, DAM_PDCR(ssi_port));
+ __raw_writel(0, DAM_PDCR(dai_port));
+
+ /* set to synchronous */
+ ssi_ptcr |= AUDMUX_PTCR_SYN;
+ dai_ptcr |= AUDMUX_PTCR_SYN;
+
+#if AK4647_SSI_MASTER
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TFSDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, dai_port);
+
+ /* set Tx Clock direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, dai_port);
+#else
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source ssi_port --> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TFSDIR;
+ dai_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, ssi_port);
+
+ /* set Tx Clock direction and source ssi_port--> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ dai_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, ssi_port);
+#endif
+
+ __raw_writel(ssi_ptcr, DAM_PTCR(ssi_port));
+ __raw_writel(dai_ptcr, DAM_PTCR(dai_port));
+ __raw_writel(ssi_pdcr, DAM_PDCR(ssi_port));
+ __raw_writel(dai_pdcr, DAM_PDCR(dai_port));
+}
+
+static int imx_3stack_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ int ret = 0;
+ u32 dai_format;
+
+#if AK4647_SSI_MASTER
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_SYNC;
+#else
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_SYNC;
+#endif
+ if (channels == 2)
+ dai_format |= SND_SOC_DAIFMT_TDM;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channels == 1 ? 0xfffffffe : 0xfffffffc, 2);
+
+ /* set the SSI system clock as input (unused) */
+ snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN);
+
+ snd_soc_dai_set_sysclk(codec_dai, 0, rate, 0);
+
+ /* set codec BCLK division for sample rate */
+ snd_soc_dai_set_clkdiv(codec_dai, 0, 0);
+
+ return 0;
+}
+
+/*
+ * imx_3stack ak4647 HiFi DAI operations.
+ */
+static struct snd_soc_ops imx_3stack_hifi_ops = {
+ .hw_params = imx_3stack_hifi_hw_params,
+};
+
+static int ak4647_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = ak4647_jack_func;
+ return 0;
+}
+
+static int ak4647_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ if (ak4647_jack_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ ak4647_jack_func = ucontrol->value.integer.value[0];
+
+ if (ak4647_jack_func)
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int ak4647_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = ak4647_spk_func;
+ return 0;
+}
+
+static int ak4647_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ if (ak4647_spk_func == ucontrol->value.integer.value[0])
+ return 0;
+
+ ak4647_spk_func = ucontrol->value.integer.value[0];
+ if (ak4647_spk_func)
+ snd_soc_dapm_enable_pin(codec, "Line Out Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Line Out Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+ if (plat->amp_enable == NULL)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ plat->amp_enable(1);
+ else
+ plat->amp_enable(0);
+ return 0;
+}
+
+/* imx_3stack machine dapm widgets */
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", spk_amp_event),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+/* example machine audio_mapnections */
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* mic is connected to mic1 - with bias */
+ {"Left Input", NULL, "Mic1 Jack"},
+
+ /* Line in jack */
+ {"Left Input", NULL, "Line In Jack"},
+ {"Right Input", NULL, "Line In Jack"},
+
+ /* Headphone jack */
+ {"Headphone Jack", NULL, "HPL"},
+ {"Headphone Jack", NULL, "HPR"},
+
+ /* Line out jack */
+ {"Line Out Jack", NULL, "LOUT"},
+
+ /* Ext Spk */
+ {"Ext Spk", NULL, "LOUT"},
+
+};
+
+static const char *jack_function[] = { "off", "on" };
+
+static const char *spk_function[] = { "off", "on" };
+
+static const struct soc_enum ak4647_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new ak4647_machine_controls[] = {
+ SOC_ENUM_EXT("Jack Function", ak4647_enum[0], ak4647_get_jack,
+ ak4647_set_jack),
+ SOC_ENUM_EXT("Speaker Function", ak4647_enum[1], ak4647_get_spk,
+ ak4647_set_spk),
+};
+
+static void headphone_detect_handler(struct work_struct *work)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct platform_device *pdev = priv->pdev;
+
+ sysfs_notify(&pdev->dev.kobj, NULL, "headphone");
+}
+
+static irqreturn_t imx_headphone_detect_handler(int irq, void *dev_id)
+{
+ schedule_work(&hp_event);
+ return IRQ_HANDLED;
+
+}
+
+static ssize_t show_headphone(struct device_driver *dev, char *buf)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ unsigned int value;
+
+ value = plat->hp_status();
+
+ if (value == 0)
+ strcpy(buf, "speaker\n");
+ else
+ strcpy(buf, "headphone\n");
+
+ return strlen(buf);
+}
+
+DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+
+static int imx_3stack_ak4647_init(struct snd_soc_codec *codec)
+{
+ int i, ret;
+ for (i = 0; i < ARRAY_SIZE(ak4647_machine_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&ak4647_machine_controls[i],
+ codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai imx_3stack_cpu_dai;
+
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "ak4647",
+ .stream_name = "ak4647",
+ .cpu_dai = &imx_3stack_cpu_dai,
+ .codec_dai = &ak4647_hifi_dai,
+ .init = imx_3stack_ak4647_init,
+ .ops = &imx_3stack_hifi_ops,
+};
+
+static struct snd_soc_machine snd_soc_machine_imx_3stack = {
+ .name = "imx-3stack",
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .machine = &snd_soc_machine_imx_3stack,
+ .platform = &imx_soc_platform,
+ .codec_dev = &soc_codec_dev_ak4647,
+};
+
+/*
+ * This function will register the snd_soc_pcm_link drivers.
+ * It also registers devices for platform DMA, I2S, SSP and registers an
+ * I2C driver to probe the codec.
+ */
+static int __init imx_3stack_ak4647_probe(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *dev_data = pdev->dev.platform_data;
+ struct imx_3stack_priv *priv = &machine_priv;
+ int ret = 0;
+
+ dev_data->init();
+
+ /* imx_3stack ak4647 hifi interface */
+ imx_ssi_dai_init(&imx_3stack_cpu_dai);
+ if (dev_data->src_port == 1)
+ imx_3stack_cpu_dai.name = "imx-ssi-1";
+ else
+ imx_3stack_cpu_dai.name = "imx-ssi-3";
+
+ /* Configure audio port 3 */
+ gpio_activate_audio_ports();
+ imx_3stack_init_dam(dev_data->src_port, dev_data->ext_port);
+
+ if (request_irq
+ (dev_data->intr_id_hp, imx_headphone_detect_handler, 0,
+ "headphone", NULL))
+ goto err;
+
+ ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);
+ if (ret < 0)
+ goto sysfs_err;
+
+ priv->pdev = pdev;
+ return ret;
+
+sysfs_err:
+ free_irq(dev_data->intr_id_hp, NULL);
+err:
+ return ret;
+}
+
+static int __devexit imx_3stack_ak4647_remove(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *dev_data = pdev->dev.platform_data;
+ gpio_inactivate_audio_ports();
+ free_irq(dev_data->intr_id_hp, NULL);
+ driver_remove_file(pdev->dev.driver, &driver_attr_headphone);
+ return 0;
+}
+
+static struct platform_driver imx_3stack_ak4647_driver = {
+ .probe = imx_3stack_ak4647_probe,
+ .remove = __devexit_p(imx_3stack_ak4647_remove),
+ .driver = {
+ .name = "imx-3stack-ak4647",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_asoc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_ak4647_driver);
+ if (ret < 0)
+ goto exit;
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", 3);
+ if (!imx_3stack_snd_device)
+ goto err_device_alloc;
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+ if (0 == ret)
+ goto exit;
+
+ platform_device_put(imx_3stack_snd_device);
+err_device_alloc:
+ platform_driver_unregister(&imx_3stack_ak4647_driver);
+exit:
+ return ret;
+}
+
+static void __exit imx_3stack_asoc_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_ak4647_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_asoc_init);
+module_exit(imx_3stack_asoc_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC ak4647 imx_3stack");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-ak5702.c b/sound/soc/imx/imx-3stack-ak5702.c
new file mode 100644
index 000000000000..9d7b3575109b
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-ak5702.c
@@ -0,0 +1,229 @@
+/*
+ * imx-3stack-ak5702.c -- SoC audio for imx_3stack
+ *
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/mxc.h>
+
+#include "imx-pcm.h"
+#include "imx-esai.h"
+#include "../codecs/ak5702.h"
+
+struct imx_3stack_pcm_state {
+ int lr_clk_active;
+};
+
+static struct imx_3stack_pcm_state clk_state;
+
+static int imx_3stack_startup(struct snd_pcm_substream *substream)
+{
+ clk_state.lr_clk_active++;
+ return 0;
+}
+
+static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+
+ /* disable the PLL if there are no active Rx channels */
+ if (!codec_dai->active)
+ snd_soc_dai_set_pll(codec_dai, 0, 0, 0);
+ clk_state.lr_clk_active--;
+}
+
+static int imx_3stack_surround_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ unsigned int rate = params_rate(params);
+ u32 dai_format;
+
+ if (clk_state.lr_clk_active > 1)
+ return 0;
+
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_ASYNC;
+ dai_format |= SND_SOC_DAIFMT_TDM;
+
+ /* set codec DAI configuration */
+ snd_soc_dai_set_fmt(codec_dai, dai_format);
+
+ /* set cpu DAI configuration */
+ snd_soc_dai_set_fmt(cpu_dai, dai_format);
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffff, 2);
+
+ /* set the ESAI system clock as input */
+ snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_IN);
+
+ /* set codec BCLK division */
+ snd_soc_dai_set_clkdiv(codec_dai, AK5702_BCLK_CLKDIV,
+ AK5702_BCLK_DIV_32);
+
+ snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_OUT);
+
+ snd_soc_dai_set_pll(codec_dai, 1, 12000000, 0);
+ return 0;
+}
+
+/*
+ * imx_3stack ak5702 DAI opserations.
+ */
+static struct snd_soc_ops imx_3stack_surround_ops = {
+ .startup = imx_3stack_startup,
+ .shutdown = imx_3stack_shutdown,
+ .hw_params = imx_3stack_surround_hw_params,
+};
+
+/* imx_3stack machine dapm widgets */
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+/* example machine audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Line in jack */
+ {"ADCA Left Input", NULL, "Line In Jack"},
+ {"ADCA Right Input", NULL, "Line In Jack"},
+ {"ADCB Left Input", NULL, "Line In Jack"},
+ {"ADCB Right Input", NULL, "Line In Jack"},
+};
+
+static int imx_3stack_ak5702_init(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+ snd_soc_dapm_sync(codec);
+ return 0;
+}
+
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "ak5702",
+ .stream_name = "ak5702",
+ .cpu_dai = &imx_esai_dai,
+ .codec_dai = &ak5702_dai,
+ .init = imx_3stack_ak5702_init,
+ .ops = &imx_3stack_surround_ops,
+};
+
+static int imx_3stack_machine_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ kfree(socdev->codec_data);
+ return 0;
+}
+
+static struct snd_soc_machine snd_soc_machine_imx_3stack = {
+ .name = "imx-3stack",
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+ .remove = imx_3stack_machine_remove,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .machine = &snd_soc_machine_imx_3stack,
+ .platform = &imx_soc_platform,
+ .codec_dev = &soc_codec_dev_ak5702,
+};
+
+/*
+ * This function will register the snd_soc_pcm_link drivers.
+ */
+static int __devinit imx_3stack_ak5702_probe(struct platform_device *pdev)
+{
+ struct ak5702_setup_data *setup;
+
+ imx_esai_dai.name = "imx-esai-txrx";
+
+ setup = kzalloc(sizeof(struct ak5702_setup_data), GFP_KERNEL);
+ setup->i2c_bus = 1;
+ setup->i2c_address = 0x13;
+ imx_3stack_snd_devdata.codec_data = setup;
+
+ gpio_activate_esai_ports();
+ return 0;
+}
+
+static int imx_3stack_ak5702_remove(struct platform_device *pdev)
+{
+ gpio_deactivate_esai_ports();
+ return 0;
+}
+
+static struct platform_driver imx_3stack_ak5702_driver = {
+ .probe = imx_3stack_ak5702_probe,
+ .remove = imx_3stack_ak5702_remove,
+ .driver = {
+ .name = "imx-3stack-ak5702",
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_asoc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_ak5702_driver);
+ if (ret)
+ return -ENOMEM;
+
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!imx_3stack_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+ if (ret)
+ platform_device_put(imx_3stack_snd_device);
+
+ return ret;
+}
+
+static void __exit imx_3stack_asoc_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_ak5702_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_asoc_init);
+module_exit(imx_3stack_asoc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("ALSA SoC ak5702 imx_3stack");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-bt.c b/sound/soc/imx/imx-3stack-bt.c
new file mode 100644
index 000000000000..365f17d9f9c6
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-bt.c
@@ -0,0 +1,256 @@
+/*
+ * imx-3stack-bt.c -- SoC bluetooth audio for imx_3stack
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <mach/mxc.h>
+
+#include "imx-pcm.h"
+#include "imx-ssi.h"
+#include "imx-3stack-bt.h"
+
+#define BT_SSI_MASTER 1
+
+struct imx_3stack_priv {
+ struct platform_device *pdev;
+ int active;
+};
+
+static struct imx_3stack_priv machine_priv;
+
+static void imx_3stack_init_dam(int ssi_port, int dai_port)
+{
+ /* bt uses SSI1 or SSI2 via AUDMUX port dai_port for audio */
+ unsigned int ssi_ptcr = 0;
+ unsigned int dai_ptcr = 0;
+ unsigned int ssi_pdcr = 0;
+ unsigned int dai_pdcr = 0;
+
+ /* reset port ssi_port & dai_port */
+ __raw_writel(0, DAM_PTCR(ssi_port));
+ __raw_writel(0, DAM_PTCR(dai_port));
+ __raw_writel(0, DAM_PDCR(ssi_port));
+ __raw_writel(0, DAM_PDCR(dai_port));
+
+ /* set to synchronous */
+ ssi_ptcr |= AUDMUX_PTCR_SYN;
+ dai_ptcr |= AUDMUX_PTCR_SYN;
+
+#if BT_SSI_MASTER
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TFSDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, dai_port);
+
+ /* set Tx Clock direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, dai_port);
+#else
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source ssi_port --> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TFSDIR;
+ dai_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, ssi_port);
+
+ /* set Tx Clock direction and source ssi_port--> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ dai_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, ssi_port);
+#endif
+
+ __raw_writel(ssi_ptcr, DAM_PTCR(ssi_port));
+ __raw_writel(dai_ptcr, DAM_PTCR(dai_port));
+ __raw_writel(ssi_pdcr, DAM_PDCR(ssi_port));
+ __raw_writel(dai_pdcr, DAM_PDCR(dai_port));
+}
+
+static int imx_3stack_bt_startup(struct snd_pcm_substream *substream)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+
+ if (!priv->active)
+ gpio_activate_bt_audio_port();
+ priv->active++;
+ return 0;
+}
+
+static int imx_3stack_bt_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ unsigned int channels = params_channels(params);
+ int ret = 0;
+ u32 dai_format;
+
+#if BT_SSI_MASTER
+ dai_format = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_IB_IF |
+ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_SYNC;
+#else
+ dai_format = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_IB_IF |
+ SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_SYNC;
+#endif
+ if (channels == 2)
+ dai_format |= SND_SOC_DAIFMT_TDM;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channels == 1 ? 0xfffffffe : 0xfffffffc, 2);
+
+ /* set the SSI system clock as input (unused) */
+ snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
+static void imx_3stack_bt_shutdown(struct snd_pcm_substream *substream)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+
+ priv->active--;
+ if (!priv->active)
+ gpio_inactivate_bt_audio_port();
+}
+
+/*
+ * imx_3stack bt DAI opserations.
+ */
+static struct snd_soc_ops imx_3stack_bt_ops = {
+ .startup = imx_3stack_bt_startup,
+ .hw_params = imx_3stack_bt_hw_params,
+ .shutdown = imx_3stack_bt_shutdown,
+};
+
+static struct snd_soc_dai imx_3stack_cpu_dai;
+
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "bluetooth",
+ .stream_name = "bluetooth",
+ .cpu_dai = &imx_3stack_cpu_dai,
+ .codec_dai = &bt_dai,
+ .ops = &imx_3stack_bt_ops,
+};
+
+static struct snd_soc_machine snd_soc_machine_imx_3stack = {
+ .name = "imx-3stack",
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .machine = &snd_soc_machine_imx_3stack,
+ .platform = &imx_soc_platform,
+ .codec_dev = &soc_codec_dev_bt,
+};
+
+/*
+ * This function will register the snd_soc_pcm_link drivers.
+ * It also registers devices for platform DMA, I2S, SSP and registers an
+ * I2C driver to probe the codec.
+ */
+static int __init imx_3stack_bt_probe(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *dev_data = pdev->dev.platform_data;
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct snd_soc_dai *cpu_dai;
+
+ /* imx_3stack bt interface */
+ imx_ssi_dai_init(&imx_3stack_cpu_dai);
+ imx_3stack_cpu_dai.private_data = dev_data;
+
+ if (dev_data->src_port == 1)
+ imx_3stack_cpu_dai.name = "imx-ssi-1";
+ else
+ imx_3stack_cpu_dai.name = "imx-ssi-3";
+
+ /* Configure audio port */
+ imx_3stack_init_dam(dev_data->src_port, dev_data->ext_port);
+
+ priv->pdev = pdev;
+ priv->active = 0;
+ return 0;
+
+}
+
+static int __devexit imx_3stack_bt_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver imx_3stack_bt_driver = {
+ .probe = imx_3stack_bt_probe,
+ .remove = __devexit_p(imx_3stack_bt_remove),
+ .driver = {
+ .name = "imx-3stack-bt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_asoc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_bt_driver);
+ if (ret < 0)
+ goto exit;
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", 4);
+ if (!imx_3stack_snd_device)
+ goto err_device_alloc;
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+ if (0 == ret)
+ goto exit;
+
+ platform_device_put(imx_3stack_snd_device);
+err_device_alloc:
+ platform_driver_unregister(&imx_3stack_bt_driver);
+exit:
+ return ret;
+}
+
+static void __exit imx_3stack_asoc_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_bt_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_asoc_init);
+module_exit(imx_3stack_asoc_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC bluetooth imx_3stack");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-bt.h b/sound/soc/imx/imx-3stack-bt.h
new file mode 100644
index 000000000000..4e1d3547dd56
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-bt.h
@@ -0,0 +1,21 @@
+/*
+ * imx-3stack-bt.h -- Bluetooth PCM driver header file for Freescale IMX
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 _MXC_BTPCM_H
+#define _MXC_BTPCM_H
+
+extern struct snd_soc_dai bt_dai;
+extern struct snd_soc_codec_device soc_codec_dev_bt;
+#endif
diff --git a/sound/soc/imx/imx-3stack-sgtl5000.c b/sound/soc/imx/imx-3stack-sgtl5000.c
new file mode 100644
index 000000000000..0ab4c2c2231c
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-sgtl5000.c
@@ -0,0 +1,741 @@
+/*
+ * imx-3stack-sgtl5000.c -- i.MX 3Stack Driver for Freescale SGTL5000 Codec
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Revision history
+ * 21th Oct 2008 Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <mach/dma.h>
+#include <mach/spba.h>
+#include <mach/clock.h>
+#include <mach/mxc.h>
+
+#include "../codecs/sgtl5000.h"
+#include "imx-ssi.h"
+#include "imx-pcm.h"
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+#include <linux/mxc_asrc.h>
+
+static unsigned int sgtl5000_rates[] = {
+ 0,
+ 32000,
+ 44100,
+ 48000,
+ 96000,
+};
+
+struct asrc_esai {
+ unsigned int cpu_dai_rates;
+ unsigned int codec_dai_rates;
+ enum asrc_pair_index asrc_index;
+ unsigned int output_sample_rate;
+};
+
+static struct asrc_esai asrc_ssi_data;
+#endif
+
+/* SSI BCLK and LRC master */
+#define SGTL5000_SSI_MASTER 1
+
+struct imx_3stack_priv {
+ int sysclk;
+ int hw;
+ struct platform_device *pdev;
+ struct regulator *reg_vddio;
+ struct regulator *reg_vdda;
+ struct regulator *reg_vddd;
+};
+
+static struct imx_3stack_priv machine_priv;
+
+static int imx_3stack_audio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
+ struct imx_3stack_priv *priv = &machine_priv;
+ unsigned int rate = params_rate(params);
+ int ret = 0;
+
+ unsigned int channels = params_channels(params);
+ u32 dai_format;
+
+ /* only need to do this once as capture and playback are sync */
+ if (priv->hw)
+ return 0;
+ priv->hw = 1;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if ((asrc_ssi_data.output_sample_rate != 0)
+ && (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) {
+ unsigned int asrc_input_rate = rate;
+ unsigned int channel = params_channels(params);
+ struct mxc_runtime_data *pcm_data =
+ substream->runtime->private_data;
+ struct asrc_config config;
+ struct mxc_audio_platform_data *plat;
+ struct imx_3stack_priv *priv = &machine_priv;
+ int retVal = 0;
+ retVal = asrc_req_pair(channel, &asrc_ssi_data.asrc_index);
+ if (retVal < 0) {
+ pr_err("asrc_req_pair fail\n");
+ return -1;
+ }
+ config.pair = asrc_ssi_data.asrc_index;
+ config.channel_num = channel;
+ config.input_sample_rate = asrc_input_rate;
+ config.output_sample_rate = asrc_ssi_data.output_sample_rate;
+ config.inclk = INCLK_NONE;
+ config.word_width = 32;
+ plat = priv->pdev->dev.platform_data;
+ if (plat->src_port == 1)
+ config.outclk = OUTCLK_SSI1_TX;
+ else
+ config.outclk = OUTCLK_SSI2_TX;
+ retVal = asrc_config_pair(&config);
+ if (retVal < 0) {
+ pr_err("Fail to config asrc\n");
+ asrc_release_pair(asrc_ssi_data.asrc_index);
+ return retVal;
+ }
+ rate = asrc_ssi_data.output_sample_rate;
+ pcm_data->asrc_index = asrc_ssi_data.asrc_index;
+ pcm_data->asrc_enable = 1;
+ }
+#endif
+
+ snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, priv->sysclk, 0);
+ snd_soc_dai_set_sysclk(codec_dai, SGTL5000_LRCLK, rate, 0);
+
+#if SGTL5000_SSI_MASTER
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_SYNC;
+ if (channels == 2)
+ dai_format |= SND_SOC_DAIFMT_TDM;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+#else
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_SYNC;
+ if (channels == 2)
+ dai_format |= SND_SOC_DAIFMT_TDM;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+#endif
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channels == 1 ? 0xfffffffe : 0xfffffffc, 2);
+
+ /* set the SSI system clock as input (unused) */
+ snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
+static int imx_3stack_startup(struct snd_pcm_substream *substream)
+{
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (asrc_ssi_data.output_sample_rate != 0) {
+ struct snd_soc_pcm_runtime *rtd =
+ substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ asrc_ssi_data.cpu_dai_rates = cpu_dai->playback.rates;
+ asrc_ssi_data.codec_dai_rates =
+ codec_dai->playback.rates;
+ cpu_dai->playback.rates =
+ SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT;
+ codec_dai->playback.rates =
+ SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT;
+ }
+ }
+#endif
+ return 0;
+}
+
+static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (asrc_ssi_data.output_sample_rate != 0) {
+ struct snd_soc_pcm_runtime *rtd =
+ substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ codec_dai->playback.rates =
+ asrc_ssi_data.codec_dai_rates;
+ cpu_dai->playback.rates = asrc_ssi_data.cpu_dai_rates;
+ asrc_release_pair(asrc_ssi_data.asrc_index);
+ }
+ }
+#endif
+
+ priv->hw = 0;
+}
+
+/*
+ * imx_3stack SGTL5000 audio DAI opserations.
+ */
+static struct snd_soc_ops imx_3stack_ops = {
+ .startup = imx_3stack_startup,
+ .shutdown = imx_3stack_shutdown,
+ .hw_params = imx_3stack_audio_hw_params,
+};
+
+static void imx_3stack_init_dam(int ssi_port, int dai_port)
+{
+ unsigned int ssi_ptcr = 0;
+ unsigned int dai_ptcr = 0;
+ unsigned int ssi_pdcr = 0;
+ unsigned int dai_pdcr = 0;
+ /* SGTL5000 uses SSI1 or SSI2 via AUDMUX port dai_port for audio */
+
+ /* reset port ssi_port & dai_port */
+ __raw_writel(0, DAM_PTCR(ssi_port));
+ __raw_writel(0, DAM_PTCR(dai_port));
+ __raw_writel(0, DAM_PDCR(ssi_port));
+ __raw_writel(0, DAM_PDCR(dai_port));
+
+ /* set to synchronous */
+ ssi_ptcr |= AUDMUX_PTCR_SYN;
+ dai_ptcr |= AUDMUX_PTCR_SYN;
+
+#if SGTL5000_SSI_MASTER
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TFSDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, dai_port);
+
+ /* set Tx Clock direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, dai_port);
+#else
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source ssi_port --> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TFSDIR;
+ dai_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, ssi_port);
+
+ /* set Tx Clock direction and source ssi_port--> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ dai_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, ssi_port);
+#endif
+
+ __raw_writel(ssi_ptcr, DAM_PTCR(ssi_port));
+ __raw_writel(dai_ptcr, DAM_PTCR(dai_port));
+ __raw_writel(ssi_pdcr, DAM_PDCR(ssi_port));
+ __raw_writel(dai_pdcr, DAM_PDCR(dai_port));
+}
+
+/* imx_3stack machine connections to the codec pins */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* Mic Jack --> MIC_IN (with automatic bias) */
+ {"MIC_IN", NULL, "Mic Jack"},
+
+ /* Line in Jack --> LINE_IN */
+ {"LINE_IN", NULL, "Line In Jack"},
+
+ /* HP_OUT --> Headphone Jack */
+ {"Headphone Jack", NULL, "HP_OUT"},
+
+ /* LINE_OUT --> Ext Speaker */
+ {"Ext Spk", NULL, "LINE_OUT"},
+};
+
+static int sgtl5000_jack_func;
+static int sgtl5000_spk_func;
+
+static void headphone_detect_handler(struct work_struct *work)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ int hp_status;
+
+ sysfs_notify(&pdev->dev.kobj, NULL, "headphone");
+ hp_status = plat->hp_status();
+ if (hp_status)
+ set_irq_type(plat->hp_irq, IRQ_TYPE_EDGE_FALLING);
+ else
+ set_irq_type(plat->hp_irq, IRQ_TYPE_EDGE_RISING);
+ enable_irq(plat->hp_irq);
+}
+
+static DECLARE_DELAYED_WORK(hp_event, headphone_detect_handler);
+
+static irqreturn_t imx_headphone_detect_handler(int irq, void *data)
+{
+ disable_irq(irq);
+ schedule_delayed_work(&hp_event, msecs_to_jiffies(200));
+ return IRQ_HANDLED;
+}
+
+static ssize_t show_headphone(struct device_driver *dev, char *buf)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ u16 hp_status;
+
+ /* determine whether hp is plugged in */
+ hp_status = plat->hp_status();
+
+ if (hp_status == 0)
+ strcpy(buf, "speaker\n");
+ else
+ strcpy(buf, "headphone\n");
+
+ return strlen(buf);
+}
+
+static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+
+static const char *jack_function[] = { "off", "on"
+};
+
+static const char *spk_function[] = { "off", "on" };
+
+static const struct soc_enum sgtl5000_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static int sgtl5000_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = sgtl5000_jack_func;
+ return 0;
+}
+
+static int sgtl5000_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (sgtl5000_jack_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ sgtl5000_jack_func = ucontrol->value.enumerated.item[0];
+ if (sgtl5000_jack_func)
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int sgtl5000_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = sgtl5000_spk_func;
+ return 0;
+}
+
+static int sgtl5000_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (sgtl5000_spk_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ sgtl5000_spk_func = ucontrol->value.enumerated.item[0];
+ if (sgtl5000_spk_func)
+ snd_soc_dapm_enable_pin(codec, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin(codec, "Ext Spk");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+ if (plat->amp_enable == NULL)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ plat->amp_enable(1);
+ else
+ plat->amp_enable(0);
+
+ return 0;
+}
+
+/* imx_3stack machine dapm widgets */
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", spk_amp_event),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_kcontrol_new sgtl5000_machine_controls[] = {
+ SOC_ENUM_EXT("Jack Function", sgtl5000_enum[0], sgtl5000_get_jack,
+ sgtl5000_set_jack),
+ SOC_ENUM_EXT("Speaker Function", sgtl5000_enum[1], sgtl5000_get_spk,
+ sgtl5000_set_spk),
+};
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+static int asrc_func;
+
+static const char *asrc_function[] =
+ { "disable", "32KHz", "44.1KHz", "48KHz", "96KHz" };
+
+static const struct soc_enum asrc_enum[] = {
+ SOC_ENUM_SINGLE_EXT(5, asrc_function),
+};
+
+static int asrc_get_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = asrc_func;
+ return 0;
+}
+
+static int asrc_set_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (asrc_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ asrc_func = ucontrol->value.enumerated.item[0];
+ asrc_ssi_data.output_sample_rate = sgtl5000_rates[asrc_func];
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new asrc_controls[] = {
+ SOC_ENUM_EXT("ASRC", asrc_enum[0], asrc_get_rate,
+ asrc_set_rate),
+};
+#endif
+
+static int imx_3stack_sgtl5000_init(struct snd_soc_codec *codec)
+{
+ int i, ret;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ for (i = 0; i < ARRAY_SIZE(asrc_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&asrc_controls[i], codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+ asrc_ssi_data.output_sample_rate = sgtl5000_rates[asrc_func];
+
+#endif
+
+ /* Add imx_3stack specific controls */
+ for (i = 0; i < ARRAY_SIZE(sgtl5000_machine_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&sgtl5000_machine_controls[i],
+ codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Add imx_3stack specific widgets */
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+
+ /* Set up imx_3stack specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai imx_3stack_cpu_dai;
+
+/* imx_3stack digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "SGTL5000",
+ .stream_name = "SGTL5000",
+ .cpu_dai = &imx_3stack_cpu_dai,
+ .codec_dai = &sgtl5000_dai,
+ .init = imx_3stack_sgtl5000_init,
+ .ops = &imx_3stack_ops,
+};
+
+static int imx_3stack_machine_remove(struct platform_device *pdev)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct mxc_audio_platform_data *plat;
+ if (priv->reg_vddio)
+ regulator_disable(priv->reg_vddio);
+ if (priv->reg_vddd)
+ regulator_disable(priv->reg_vddd);
+ if (priv->reg_vdda)
+ regulator_disable(priv->reg_vdda);
+ if (priv->reg_vdda)
+ regulator_put(priv->reg_vdda);
+ if (priv->reg_vddio)
+ regulator_put(priv->reg_vddio);
+ if (priv->reg_vddd)
+ regulator_put(priv->reg_vddd);
+ if (priv->pdev) {
+ plat = priv->pdev->dev.platform_data;
+ if (plat->finit)
+ plat->finit();
+ }
+
+ return 0;
+}
+
+/* imx_3stack audio machine driver */
+static struct snd_soc_machine snd_soc_machine_imx_3stack = {
+ .name = "imx-3stack",
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+ .remove = imx_3stack_machine_remove,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .machine = &snd_soc_machine_imx_3stack,
+ .platform = &imx_soc_platform,
+ .codec_dev = &soc_codec_dev_sgtl5000,
+};
+
+static int __devinit imx_3stack_sgtl5000_probe(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ struct regulator *reg;
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct sgtl5000_platform_data *codec_data;
+ int ret = 0;
+
+ priv->sysclk = plat->sysclk;
+ priv->pdev = pdev;
+
+ imx_ssi_dai_init(&imx_3stack_cpu_dai);
+ imx_3stack_cpu_dai.private_data = plat;
+
+ codec_data = kzalloc(sizeof(struct sgtl5000_platform_data), GFP_KERNEL);
+ if (!codec_data) {
+ ret = -ENOMEM;
+ goto err_codec_data;
+ }
+ codec_data->vddio = plat->vddio / 1000; /* uV to mV */
+ codec_data->vdda = plat->vdda / 1000;
+ codec_data->vddd = plat->vddd / 1000;
+ imx_3stack_snd_devdata.codec_data = codec_data;
+
+ gpio_activate_audio_ports();
+ imx_3stack_init_dam(plat->src_port, plat->ext_port);
+
+ if (plat->src_port == 2)
+ imx_3stack_cpu_dai.name = "imx-ssi-3";
+ else
+ imx_3stack_cpu_dai.name = "imx-ssi-1";
+
+ ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);
+ if (ret < 0) {
+ pr_err("%s:failed to create driver_attr_headphone\n", __func__);
+ goto sysfs_err;
+ }
+
+ ret = -EINVAL;
+ if (plat->init && plat->init())
+ goto err_plat_init;
+ if (plat->vddio_reg) {
+ reg = regulator_get(&pdev->dev, plat->vddio_reg);
+ if (IS_ERR(reg))
+ goto err_reg_vddio;
+ priv->reg_vddio = reg;
+ }
+ if (plat->vdda_reg) {
+ reg = regulator_get(&pdev->dev, plat->vdda_reg);
+ if (IS_ERR(reg))
+ goto err_reg_vdda;
+ priv->reg_vdda = reg;
+ }
+ if (plat->vddd_reg) {
+ reg = regulator_get(&pdev->dev, plat->vddd_reg);
+ if (IS_ERR(reg))
+ goto err_reg_vddd;
+ priv->reg_vddd = reg;
+ }
+
+ if (priv->reg_vdda) {
+ ret = regulator_set_voltage(priv->reg_vdda,
+ plat->vdda, plat->vdda);
+ regulator_enable(priv->reg_vdda);
+ }
+ if (priv->reg_vddio) {
+ regulator_set_voltage(priv->reg_vddio,
+ plat->vddio, plat->vddio);
+ regulator_enable(priv->reg_vddio);
+ }
+ if (priv->reg_vddd) {
+ regulator_set_voltage(priv->reg_vddd, plat->vddd, plat->vddd);
+ regulator_enable(priv->reg_vddd);
+ }
+
+ /* The SGTL5000 has an internal reset that is deasserted 8 SYS_MCLK
+ cycles after all power rails have been brought up. After this time
+ communication can start */
+ msleep(1);
+
+ if (plat->hp_status())
+ ret = request_irq(plat->hp_irq,
+ imx_headphone_detect_handler,
+ IRQ_TYPE_EDGE_FALLING, pdev->name, priv);
+ else
+ ret = request_irq(plat->hp_irq,
+ imx_headphone_detect_handler,
+ IRQ_TYPE_EDGE_RISING, pdev->name, priv);
+ if (ret < 0) {
+ pr_err("%s: request irq failed\n", __func__);
+ goto err_card_reg;
+ }
+
+ sgtl5000_jack_func = 1;
+ sgtl5000_spk_func = 1;
+
+ return 0;
+
+err_card_reg:
+ if (priv->reg_vddd)
+ regulator_put(priv->reg_vddd);
+err_reg_vddd:
+ if (priv->reg_vdda)
+ regulator_put(priv->reg_vdda);
+err_reg_vdda:
+ if (priv->reg_vddio)
+ regulator_put(priv->reg_vddio);
+err_reg_vddio:
+ if (plat->finit)
+ plat->finit();
+err_plat_init:
+ driver_remove_file(pdev->dev.driver, &driver_attr_headphone);
+sysfs_err:
+ kfree(codec_data);
+err_codec_data:
+ return ret;
+}
+
+static int imx_3stack_sgtl5000_remove(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ struct imx_3stack_priv *priv = &machine_priv;
+
+ free_irq(plat->hp_irq, priv);
+
+ driver_remove_file(pdev->dev.driver, &driver_attr_headphone);
+
+ kfree(imx_3stack_snd_devdata.codec_data);
+
+ return 0;
+}
+
+static struct platform_driver imx_3stack_sgtl5000_audio_driver = {
+ .probe = imx_3stack_sgtl5000_probe,
+ .remove = imx_3stack_sgtl5000_remove,
+ .driver = {
+ .name = "imx-3stack-sgtl5000",
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_sgtl5000_audio_driver);
+ if (ret)
+ return -ENOMEM;
+
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", 2);
+ if (!imx_3stack_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+
+ if (ret)
+ platform_device_put(imx_3stack_snd_device);
+
+ return ret;
+}
+
+static void __exit imx_3stack_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_sgtl5000_audio_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_init);
+module_exit(imx_3stack_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SGTL5000 Driver for i.MX 3STACK");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-wm8350.c b/sound/soc/imx/imx-3stack-wm8350.c
new file mode 100644
index 000000000000..3e79794abe64
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-wm8350.c
@@ -0,0 +1,717 @@
+/*
+ * imx-3stack-wm8350.c -- i.MX 3Stack Driver for Wolfson WM8350 Codec
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ * Revision history
+ * 19th Jun 2007 Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <mach/dma.h>
+#include <mach/spba.h>
+#include <mach/clock.h>
+#include <mach/mxc.h>
+
+#include "../codecs/wm8350.h"
+#include "imx-ssi.h"
+#include "imx-pcm.h"
+
+void gpio_activate_audio_ports(void);
+
+/* SSI BCLK and LRC master */
+#define WM8350_SSI_MASTER 1
+
+struct imx_3stack_priv {
+ int lr_clk_active;
+ int playback_active;
+ int capture_active;
+ struct platform_device *pdev;
+ struct wm8350 *wm8350;
+};
+
+static struct imx_3stack_priv machine_priv;
+
+struct _wm8350_audio {
+ unsigned int channels;
+ snd_pcm_format_t format;
+ unsigned int rate;
+ unsigned int sysclk;
+ unsigned int bclkdiv;
+ unsigned int clkdiv;
+ unsigned int lr_rate;
+};
+
+/* in order of power consumption per rate (lowest first) */
+static const struct _wm8350_audio wm8350_audio[] = {
+ /* 16bit mono modes */
+
+ {1, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000,
+ WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000,
+ WM8350_BCLK_DIV_24, WM8350_DACDIV_6, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000,
+ WM8350_BCLK_DIV_12, WM8350_DACDIV_3, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_2, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_2, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600,
+ WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600,
+ WM8350_BCLK_DIV_16, WM8350_DACDIV_4, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_2, 32,},
+ {1, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_2, 32,},
+
+ /* 16 bit stereo modes */
+ {2, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000,
+ WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000,
+ WM8350_BCLK_DIV_24, WM8350_DACDIV_3, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000,
+ WM8350_BCLK_DIV_12, WM8350_DACDIV_1_5, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600,
+ WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600,
+ WM8350_BCLK_DIV_16, WM8350_DACDIV_2, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+ {2, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200,
+ WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+
+ /* 24bit stereo modes */
+ {2, SNDRV_PCM_FORMAT_S24_LE, 48000, 12288000,
+ WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+ {2, SNDRV_PCM_FORMAT_S24_LE, 96000, 24576000,
+ WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+ {2, SNDRV_PCM_FORMAT_S24_LE, 44100, 11289600,
+ WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+ {2, SNDRV_PCM_FORMAT_S24_LE, 88200, 22579200,
+ WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+};
+
+#if WM8350_SSI_MASTER
+static int imx_3stack_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->codec;
+ struct wm8350 *wm8350 = codec->control_data;
+ struct imx_3stack_priv *priv = &machine_priv;
+
+ /* In master mode the LR clock can come from either the DAC or ADC.
+ * We use the LR clock from whatever stream is enabled first.
+ */
+
+ if (!priv->lr_clk_active) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_LRC_ADC_SEL);
+ else
+ wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_LRC_ADC_SEL);
+ }
+ priv->lr_clk_active++;
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ priv->capture_active = 1;
+ else
+ priv->playback_active = 1;
+ return 0;
+}
+#else
+#define imx_3stack_startup NULL
+#endif
+
+static int imx_3stack_audio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
+ struct imx_3stack_priv *priv = &machine_priv;
+ int ret = 0;
+ int i, found = 0;
+ snd_pcm_format_t format = params_format(params);
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ u32 dai_format;
+
+ /* only need to do this once as capture and playback are sync */
+ if (priv->lr_clk_active > 1)
+ return 0;
+
+ /* find the correct audio parameters */
+ for (i = 0; i < ARRAY_SIZE(wm8350_audio); i++) {
+ if (rate == wm8350_audio[i].rate &&
+ format == wm8350_audio[i].format &&
+ channels == wm8350_audio[i].channels) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printk(KERN_ERR "%s: invalid params\n", __func__);
+ return -EINVAL;
+ }
+
+#if WM8350_SSI_MASTER
+ /* codec FLL input is 32768 kHz from MCLK */
+ codec_dai->dai_ops.set_pll(codec_dai, 0, 32768, wm8350_audio[i].sysclk);
+#else
+ /* codec FLL input is rate from DAC LRC */
+ codec_dai->dai_ops.set_pll(codec_dai, 0, rate, wm8350_audio[i].sysclk);
+#endif
+
+#if WM8350_SSI_MASTER
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_SYNC;
+ if (channels == 2)
+ dai_format |= SND_SOC_DAIFMT_TDM;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ dai_format &= ~SND_SOC_DAIFMT_INV_MASK;
+ /* Invert frame to switch mic from right channel to left */
+ dai_format |= SND_SOC_DAIFMT_NB_IF;
+ }
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set 32KHZ as the codec system clock for DAC and ADC */
+ snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_32K,
+ wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN);
+#else
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_SYNC;
+ if (channels == 2)
+ dai_format |= SND_SOC_DAIFMT_TDM;
+
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+ if (ret < 0)
+ return ret;
+
+ /* set DAC LRC as the codec system clock for DAC and ADC */
+ snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_DAC,
+ wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN);
+#endif
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai,
+ channels == 1 ? 0xfffffffe : 0xfffffffc,
+ channels);
+
+ /* set the SSI system clock as input (unused) */
+ snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN);
+
+ /* set codec BCLK division for sample rate */
+ snd_soc_dai_set_clkdiv(codec_dai, WM8350_BCLK_CLKDIV,
+ wm8350_audio[i].bclkdiv);
+
+ /* DAI is synchronous and clocked with DAC LRCLK & ADC LRC */
+ snd_soc_dai_set_clkdiv(codec_dai,
+ WM8350_DACLR_CLKDIV,
+ wm8350_audio[i].lr_rate);
+ snd_soc_dai_set_clkdiv(codec_dai,
+ WM8350_ADCLR_CLKDIV,
+ wm8350_audio[i].lr_rate);
+
+ /* now configure DAC and ADC clocks */
+ snd_soc_dai_set_clkdiv(codec_dai,
+ WM8350_DAC_CLKDIV, wm8350_audio[i].clkdiv);
+
+ snd_soc_dai_set_clkdiv(codec_dai,
+ WM8350_ADC_CLKDIV, wm8350_audio[i].clkdiv);
+
+ return 0;
+}
+
+static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->socdev->codec;
+ struct snd_soc_dai_link *machine = rtd->dai;
+ struct snd_soc_dai *codec_dai = machine->codec_dai;
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct wm8350 *wm8350 = codec->control_data;
+
+ /* disable the PLL if there are no active Tx or Rx channels */
+ if (!codec_dai->active)
+ snd_soc_dai_set_pll(codec_dai, 0, 0, 0);
+ priv->lr_clk_active--;
+
+ /*
+ * We need to keep track of active streams in master mode and
+ * switch LRC source if necessary.
+ */
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ priv->capture_active = 0;
+ else
+ priv->playback_active = 0;
+
+ if (priv->capture_active)
+ wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_LRC_ADC_SEL);
+ else if (priv->playback_active)
+ wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+ WM8350_LRC_ADC_SEL);
+}
+
+/*
+ * imx_3stack WM8350 HiFi DAI operations.
+ */
+static struct snd_soc_ops imx_3stack_ops = {
+ .startup = imx_3stack_startup,
+ .shutdown = imx_3stack_shutdown,
+ .hw_params = imx_3stack_audio_hw_params,
+};
+
+/* need to refine these */
+static struct wm8350_audio_platform_data imx_3stack_wm8350_setup = {
+ .vmid_discharge_msecs = 1000,
+ .drain_msecs = 30,
+ .cap_discharge_msecs = 700,
+ .vmid_charge_msecs = 700,
+ .vmid_s_curve = WM8350_S_CURVE_SLOW,
+ .dis_out4 = WM8350_DISCHARGE_SLOW,
+ .dis_out3 = WM8350_DISCHARGE_SLOW,
+ .dis_out2 = WM8350_DISCHARGE_SLOW,
+ .dis_out1 = WM8350_DISCHARGE_SLOW,
+ .vroi_out4 = WM8350_TIE_OFF_500R,
+ .vroi_out3 = WM8350_TIE_OFF_500R,
+ .vroi_out2 = WM8350_TIE_OFF_500R,
+ .vroi_out1 = WM8350_TIE_OFF_500R,
+ .vroi_enable = 0,
+ .codec_current_on = WM8350_CODEC_ISEL_1_0,
+ .codec_current_standby = WM8350_CODEC_ISEL_0_5,
+ .codec_current_charge = WM8350_CODEC_ISEL_1_5,
+};
+
+static void imx_3stack_init_dam(int ssi_port, int dai_port)
+{
+ unsigned int ssi_ptcr = 0;
+ unsigned int dai_ptcr = 0;
+ unsigned int ssi_pdcr = 0;
+ unsigned int dai_pdcr = 0;
+ /* WM8350 uses SSI1 or SSI2 via AUDMUX port dai_port for audio */
+
+ /* reset port ssi_port & dai_port */
+ __raw_writel(0, DAM_PTCR(ssi_port));
+ __raw_writel(0, DAM_PTCR(dai_port));
+ __raw_writel(0, DAM_PDCR(ssi_port));
+ __raw_writel(0, DAM_PDCR(dai_port));
+
+ /* set to synchronous */
+ ssi_ptcr |= AUDMUX_PTCR_SYN;
+ dai_ptcr |= AUDMUX_PTCR_SYN;
+
+#if WM8350_SSI_MASTER
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TFSDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, dai_port);
+
+ /* set Tx Clock direction and source dai_port--> ssi_port output */
+ ssi_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ ssi_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, dai_port);
+#else
+ /* set Rx sources ssi_port <--> dai_port */
+ ssi_pdcr |= AUDMUX_PDCR_RXDSEL(dai_port);
+ dai_pdcr |= AUDMUX_PDCR_RXDSEL(ssi_port);
+
+ /* set Tx frame direction and source ssi_port --> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TFSDIR;
+ dai_ptcr |= AUDMUX_PTCR_TFSSEL(AUDMUX_FROM_TXFS, ssi_port);
+
+ /* set Tx Clock direction and source ssi_port--> dai_port output */
+ dai_ptcr |= AUDMUX_PTCR_TCLKDIR;
+ dai_ptcr |= AUDMUX_PTCR_TCSEL(AUDMUX_FROM_TXFS, ssi_port);
+#endif
+
+ __raw_writel(ssi_ptcr, DAM_PTCR(ssi_port));
+ __raw_writel(dai_ptcr, DAM_PTCR(dai_port));
+ __raw_writel(ssi_pdcr, DAM_PDCR(ssi_port));
+ __raw_writel(dai_pdcr, DAM_PDCR(dai_port));
+}
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* SiMIC --> IN1LN (with automatic bias) via SP1 */
+ {"IN1RP", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "SiMIC"},
+
+ /* Mic 1 Jack --> IN1LN and IN1LP (with automatic bias) */
+ {"IN1LN", NULL, "Mic Bias"},
+ {"IN1LP", NULL, "Mic1 Jack"},
+ {"Mic Bias", NULL, "Mic1 Jack"},
+
+ /* Mic 2 Jack --> IN1RN and IN1RP (with automatic bias) */
+ {"IN1RN", NULL, "Mic2 Jack"},
+ {"IN1RP", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Mic2 Jack"},
+
+ /* Line in Jack --> AUX (L+R) */
+ {"IN3R", NULL, "Line In Jack"},
+ {"IN3L", NULL, "Line In Jack"},
+
+ /* Out1 --> Headphone Jack */
+ {"Headphone Jack", NULL, "OUT1R"},
+ {"Headphone Jack", NULL, "OUT1L"},
+
+ /* Out1 --> Line Out Jack */
+ {"Line Out Jack", NULL, "OUT2R"},
+ {"Line Out Jack", NULL, "OUT2L"},
+};
+
+static int wm8350_jack_func;
+static int wm8350_spk_func;
+
+static void headphone_detect_handler(struct work_struct *work)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct wm8350 *wm8350 = priv->wm8350;
+
+ sysfs_notify(&pdev->dev.kobj, NULL, "headphone");
+ wm8350_unmask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+}
+
+static DECLARE_DELAYED_WORK(hp_event, headphone_detect_handler);
+
+static void imx_3stack_jack_handler(struct wm8350 *wm8350, int irq, void *data)
+{
+ wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+ schedule_delayed_work(&hp_event, msecs_to_jiffies(200));
+}
+
+static ssize_t show_headphone(struct device_driver *dev, char *buf)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ u16 reg;
+
+ reg = wm8350_reg_read(priv->wm8350, WM8350_JACK_PIN_STATUS);
+
+ if (reg & WM8350_JACK_R_LVL)
+ strcpy(buf, "speaker\n");
+ else
+ strcpy(buf, "headphone\n");
+
+ return strlen(buf);
+}
+
+static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+
+static const char *jack_function[] = { "off", "on"
+};
+
+static const char *spk_function[] = { "off", "on" };
+
+static const struct soc_enum wm8350_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, jack_function),
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static int wm8350_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = wm8350_jack_func;
+ return 0;
+}
+
+static int wm8350_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (wm8350_jack_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wm8350_jack_func = ucontrol->value.enumerated.item[0];
+ if (wm8350_jack_func)
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int wm8350_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = wm8350_spk_func;
+ return 0;
+}
+
+static int wm8350_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (wm8350_spk_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ wm8350_spk_func = ucontrol->value.enumerated.item[0];
+ if (wm8350_spk_func)
+ snd_soc_dapm_enable_pin(codec, "Line Out Jack");
+ else
+ snd_soc_dapm_disable_pin(codec, "Line Out Jack");
+
+ snd_soc_dapm_sync(codec);
+ return 1;
+}
+
+static int spk_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+ if (plat->amp_enable == NULL)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ plat->amp_enable(1);
+ else
+ plat->amp_enable(0);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_MIC("SiMIC", NULL),
+ SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic2 Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", spk_amp_event),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+static const struct snd_kcontrol_new wm8350_machine_controls[] = {
+ SOC_ENUM_EXT("Jack Function", wm8350_enum[0], wm8350_get_jack,
+ wm8350_set_jack),
+ SOC_ENUM_EXT("Speaker Function", wm8350_enum[1], wm8350_get_spk,
+ wm8350_set_spk),
+};
+
+static int imx_3stack_wm8350_init(struct snd_soc_codec *codec)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct wm8350 *wm8350 = priv->wm8350;
+ int i, ret;
+
+ codec->control_data = wm8350;
+
+ /* Add imx_3stack specific controls */
+ for (i = 0; i < ARRAY_SIZE(wm8350_machine_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&wm8350_machine_controls[i],
+ codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Add imx_3stack specific widgets */
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+
+ /* Set up imx_3stack specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+
+}
+
+static struct snd_soc_dai imx_3stack_cpu_dai;
+/* imx_3stack digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "WM8350",
+ .stream_name = "WM8350",
+ .cpu_dai = &imx_3stack_cpu_dai,
+ .codec_dai = &wm8350_dai,
+ .init = imx_3stack_wm8350_init,
+ .ops = &imx_3stack_ops,
+};
+
+static int imx_3stack_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct wm8350 *wm8350 = priv->wm8350;
+
+ socdev->codec_data = wm8350;
+ wm8350->codec.platform_data = &imx_3stack_wm8350_setup;
+
+ return 0;
+}
+
+/* imx_3stack audio machine driver */
+static struct snd_soc_machine snd_soc_machine_imx_3stack = {
+ .name = "imx-3stack",
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+ .probe = imx_3stack_machine_probe,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .machine = &snd_soc_machine_imx_3stack,
+ .platform = &imx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8350,
+};
+
+static int __devinit imx_3stack_wm8350_probe(struct platform_device *pdev)
+{
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct wm8350 *wm8350 = plat->priv;
+ int ret = 0;
+ u16 reg;
+
+ priv->pdev = pdev;
+ priv->wm8350 = wm8350;
+
+ imx_ssi_dai_init(&imx_3stack_cpu_dai);
+ imx_3stack_cpu_dai.private_data = plat;
+
+ imx_3stack_wm8350_setup.regulator1 = plat->regulator1;
+ imx_3stack_wm8350_setup.regulator2 = plat->regulator2;
+
+ gpio_activate_audio_ports();
+ imx_3stack_init_dam(plat->src_port, plat->ext_port);
+
+ if (plat->src_port == 2)
+ imx_3stack_cpu_dai.name = "imx-ssi-3";
+ else
+ imx_3stack_cpu_dai.name = "imx-ssi-1";
+
+ ret = driver_create_file(pdev->dev.driver, &driver_attr_headphone);
+ if (ret < 0) {
+ pr_err("%s:failed to create driver_attr_headphone\n", __func__);
+ return ret;
+ }
+
+ /* enable slow clock gen for jack detect */
+ reg = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_4);
+ wm8350_reg_write(wm8350, WM8350_POWER_MGMT_4, reg | WM8350_TOCLK_ENA);
+ /* enable jack detect */
+ reg = wm8350_reg_read(wm8350, WM8350_JACK_DETECT);
+ wm8350_reg_write(wm8350, WM8350_JACK_DETECT, reg | WM8350_JDR_ENA);
+ wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
+ imx_3stack_jack_handler, NULL);
+ wm8350_unmask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+
+ wm8350_jack_func = 1;
+ wm8350_spk_func = 1;
+
+ return 0;
+}
+
+static int imx_3stack_wm8350_remove(struct platform_device *pdev)
+{
+ struct imx_3stack_priv *priv = &machine_priv;
+ struct wm8350 *wm8350 = priv->wm8350;
+
+ wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+ wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
+
+ return 0;
+}
+
+static struct platform_driver imx_3stack_wm8350_audio_driver = {
+ .probe = imx_3stack_wm8350_probe,
+ .remove = __devexit_p(imx_3stack_wm8350_remove),
+ .driver = {
+ .name = "wm8350-imx-3stack-audio",
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_wm8350_audio_driver);
+ if (ret)
+ return -ENOMEM;
+
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!imx_3stack_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+
+ if (ret)
+ platform_device_put(imx_3stack_snd_device);
+
+ return ret;
+}
+
+static void __exit imx_3stack_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_wm8350_audio_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_init);
+module_exit(imx_3stack_exit);
+
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_DESCRIPTION("PMIC WM8350 Driver for i.MX 3STACK");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-3stack-wm8580.c b/sound/soc/imx/imx-3stack-wm8580.c
new file mode 100644
index 000000000000..e12478e59d3f
--- /dev/null
+++ b/sound/soc/imx/imx-3stack-wm8580.c
@@ -0,0 +1,447 @@
+/*
+ * imx-3stack-wm8580.c -- SoC 5.1 audio for imx_3stack
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <mach/mxc.h>
+
+#include "imx-pcm.h"
+#include "imx-esai.h"
+#include "../codecs/wm8580.h"
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+#include <linux/mxc_asrc.h>
+#endif
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+static unsigned int asrc_rates[] = {
+ 0,
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+};
+
+struct asrc_esai {
+ unsigned int cpu_dai_rates;
+ unsigned int codec_dai_rates;
+ enum asrc_pair_index asrc_index;
+ unsigned int output_sample_rate;
+};
+
+static struct asrc_esai asrc_esai_data;
+
+#endif
+
+struct imx_3stack_pcm_state {
+ int lr_clk_active;
+};
+
+extern void gpio_activate_esai_ports(void);
+extern void gpio_deactivate_esai_ports(void);
+
+static struct imx_3stack_pcm_state clk_state;
+
+static int imx_3stack_startup(struct snd_pcm_substream *substream)
+{
+ clk_state.lr_clk_active++;
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (asrc_esai_data.output_sample_rate != 0) {
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ asrc_esai_data.cpu_dai_rates = cpu_dai->playback.rates;
+ asrc_esai_data.codec_dai_rates = codec_dai->playback.rates;
+ cpu_dai->playback.rates =
+ SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT;
+ codec_dai->playback.rates =
+ SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT;
+ }
+#endif
+
+ return 0;
+}
+
+static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (asrc_esai_data.output_sample_rate != 0) {
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ codec_dai->playback.rates = asrc_esai_data.codec_dai_rates;
+ cpu_dai->playback.rates = asrc_esai_data.cpu_dai_rates;
+ asrc_release_pair(asrc_esai_data.asrc_index);
+ }
+#endif
+
+ /* disable the PLL if there are no active Tx or Rx channels */
+ if (!codec_dai->active)
+ snd_soc_dai_set_pll(codec_dai, 0, 0, 0);
+ clk_state.lr_clk_active--;
+}
+
+static int imx_3stack_surround_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_link *pcm_link = rtd->dai;
+ struct snd_soc_dai *cpu_dai = pcm_link->cpu_dai;
+ struct snd_soc_dai *codec_dai = pcm_link->codec_dai;
+ unsigned int rate = params_rate(params);
+ u32 dai_format;
+ unsigned int pll_out = 0, lrclk_ratio = 0;
+ unsigned int channel = params_channels(params);
+
+ if (clk_state.lr_clk_active > 1)
+ return 0;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (asrc_esai_data.output_sample_rate != 0) {
+ unsigned int asrc_input_rate = rate;
+ struct mxc_runtime_data *pcm_data =
+ substream->runtime->private_data;
+ struct asrc_config config;
+ int retVal = 0;;
+
+ retVal = asrc_req_pair(channel, &asrc_esai_data.asrc_index);
+ if (retVal < 0) {
+ pr_err("Fail to request asrc pair\n");
+ return -1;
+ }
+
+ config.pair = asrc_esai_data.asrc_index;
+ config.channel_num = channel;
+ config.input_sample_rate = asrc_input_rate;
+ config.output_sample_rate = asrc_esai_data.output_sample_rate;
+ config.inclk = INCLK_NONE;
+ config.word_width = 32;
+ config.outclk = OUTCLK_ESAI_TX;
+ retVal = asrc_config_pair(&config);
+ if (retVal < 0) {
+ pr_err("Fail to config asrc\n");
+ asrc_release_pair(asrc_esai_data.asrc_index);
+ return retVal;
+ }
+ rate = asrc_esai_data.output_sample_rate;
+ pcm_data->asrc_index = asrc_esai_data.asrc_index;
+ pcm_data->asrc_enable = 1;
+ }
+#endif
+
+ switch (rate) {
+ case 8000:
+ lrclk_ratio = 5;
+ pll_out = 6144000;
+ break;
+ case 11025:
+ lrclk_ratio = 4;
+ pll_out = 5644800;
+ break;
+ case 16000:
+ lrclk_ratio = 3;
+ pll_out = 6144000;
+ break;
+ case 32000:
+ lrclk_ratio = 3;
+ pll_out = 12288000;
+ break;
+ case 48000:
+ lrclk_ratio = 2;
+ pll_out = 12288000;
+ break;
+ case 64000:
+ lrclk_ratio = 1;
+ pll_out = 12288000;
+ break;
+ case 96000:
+ lrclk_ratio = 2;
+ pll_out = 24576000;
+ break;
+ case 128000:
+ lrclk_ratio = 1;
+ pll_out = 24576000;
+ break;
+ case 22050:
+ lrclk_ratio = 4;
+ pll_out = 11289600;
+ break;
+ case 44100:
+ lrclk_ratio = 2;
+ pll_out = 11289600;
+ break;
+ case 88200:
+ lrclk_ratio = 0;
+ pll_out = 11289600;
+ break;
+ case 176400:
+ lrclk_ratio = 0;
+ pll_out = 22579200;
+ break;
+ case 192000:
+ lrclk_ratio = 0;
+ pll_out = 24576000;
+ break;
+ default:
+ pr_info("Rate not support.\n");
+ return -EINVAL;;
+ }
+
+ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_ASYNC;
+
+ dai_format |= SND_SOC_DAIFMT_TDM;
+
+ /* set codec DAI configuration */
+ snd_soc_dai_set_fmt(codec_dai, dai_format);
+
+ /* set cpu DAI configuration */
+ snd_soc_dai_set_fmt(cpu_dai, dai_format);
+
+ /* set i.MX active slot mask */
+ snd_soc_dai_set_tdm_slot(cpu_dai, channel == 1 ? 0x1 : 0x3, 2);
+
+ /* set the ESAI system clock as input (unused) */
+ snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_IN);
+
+ snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, WM8580_CLKSRC_PLLA);
+ snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL,
+ WM8580_CLKSRC_PLLA);
+
+ /* set codec LRCLK and BCLK */
+ snd_soc_dai_set_sysclk(codec_dai, WM8580_BCLK_CLKDIV, 0,
+ SND_SOC_CLOCK_OUT);
+ snd_soc_dai_set_sysclk(codec_dai, WM8580_LRCLK_CLKDIV, lrclk_ratio,
+ SND_SOC_CLOCK_OUT);
+
+ snd_soc_dai_set_pll(codec_dai, 1, 12000000, pll_out);
+ return 0;
+}
+
+/*
+ * imx_3stack wm8580 HiFi DAI opserations.
+ */
+static struct snd_soc_ops imx_3stack_surround_ops = {
+ .startup = imx_3stack_startup,
+ .shutdown = imx_3stack_shutdown,
+ .hw_params = imx_3stack_surround_hw_params,
+};
+
+/* imx_3stack machine dapm widgets */
+static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+};
+
+/* example machine audio_mapnections */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* Line out jack */
+ {"Line Out Jack", NULL, "VOUT1L"},
+ {"Line Out Jack", NULL, "VOUT1R"},
+ {"Line Out Jack", NULL, "VOUT2L"},
+ {"Line Out Jack", NULL, "VOUT2R"},
+ {"Line Out Jack", NULL, "VOUT3L"},
+ {"Line Out Jack", NULL, "VOUT3R"},
+
+};
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+static int asrc_func;
+
+static const char *asrc_function[] =
+ { "disable", "8KHz", "11.025KHz", "16KHz", "22.05KHz", "32KHz", "44.1KHz",
+ "48KHz", "64KHz", "88.2KHz", "96KHz", "176.4KHz", "192KHz"
+};
+
+static const struct soc_enum asrc_enum[] = {
+ SOC_ENUM_SINGLE_EXT(13, asrc_function),
+};
+
+static int asrc_get_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = asrc_func;
+ return 0;
+}
+
+static int asrc_set_rate(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ if (asrc_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ asrc_func = ucontrol->value.enumerated.item[0];
+ asrc_esai_data.output_sample_rate = asrc_rates[asrc_func];
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new asrc_controls[] = {
+ SOC_ENUM_EXT("ASRC", asrc_enum[0], asrc_get_rate,
+ asrc_set_rate),
+};
+
+#endif
+
+static int imx_3stack_wm8580_init(struct snd_soc_codec *codec)
+{
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ int i;
+ int ret;
+ for (i = 0; i < ARRAY_SIZE(asrc_controls); i++) {
+ ret = snd_ctl_add(codec->card,
+ snd_soc_cnew(&asrc_controls[i], codec, NULL));
+ if (ret < 0)
+ return ret;
+ }
+ asrc_esai_data.output_sample_rate = asrc_rates[asrc_func];
+#endif
+
+ snd_soc_dapm_new_controls(codec, imx_3stack_dapm_widgets,
+ ARRAY_SIZE(imx_3stack_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link imx_3stack_dai = {
+ .name = "wm8580",
+ .stream_name = "wm8580",
+ .cpu_dai = &imx_esai_dai,
+ .codec_dai = wm8580_dai,
+ .init = imx_3stack_wm8580_init,
+ .ops = &imx_3stack_surround_ops,
+};
+
+static int imx_3stack_machine_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+ kfree(socdev->codec_data);
+ return 0;
+}
+
+static struct snd_soc_machine snd_soc_machine_imx_3stack = {
+ .name = "imx-3stack",
+ .dai_link = &imx_3stack_dai,
+ .num_links = 1,
+ .remove = imx_3stack_machine_remove,
+};
+
+static struct snd_soc_device imx_3stack_snd_devdata = {
+ .machine = &snd_soc_machine_imx_3stack,
+ .platform = &imx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8580,
+};
+
+/*
+ * This function will register the snd_soc_pcm_link drivers.
+ */
+static int __devinit imx_3stack_wm8580_probe(struct platform_device *pdev)
+{
+ struct wm8580_setup_data *setup;
+
+ imx_esai_dai.name = "imx-esai-txrx";
+
+ setup = kzalloc(sizeof(struct wm8580_setup_data), GFP_KERNEL);
+ setup->spi = 1;
+ imx_3stack_snd_devdata.codec_data = setup;
+
+ /* Configure audio port 3 */
+ gpio_activate_esai_ports();
+
+ return 0;
+}
+
+static int __devexit imx_3stack_wm8580_remove(struct platform_device *pdev)
+{
+ gpio_deactivate_esai_ports();
+ return 0;
+}
+
+static struct platform_driver imx_3stack_wm8580_driver = {
+ .probe = imx_3stack_wm8580_probe,
+ .remove = __devexit_p(imx_3stack_wm8580_remove),
+ .driver = {
+ .name = "imx-3stack-wm8580",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_device *imx_3stack_snd_device;
+
+static int __init imx_3stack_asoc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&imx_3stack_wm8580_driver);
+ if (ret < 0)
+ goto exit;
+ imx_3stack_snd_device = platform_device_alloc("soc-audio", 1);
+ if (!imx_3stack_snd_device)
+ goto err_device_alloc;
+ platform_set_drvdata(imx_3stack_snd_device, &imx_3stack_snd_devdata);
+ imx_3stack_snd_devdata.dev = &imx_3stack_snd_device->dev;
+ ret = platform_device_add(imx_3stack_snd_device);
+ if (0 == ret)
+ goto exit;
+
+ platform_device_put(imx_3stack_snd_device);
+ err_device_alloc:
+ platform_driver_unregister(&imx_3stack_wm8580_driver);
+ exit:
+ return ret;
+}
+
+static void __exit imx_3stack_asoc_exit(void)
+{
+ platform_driver_unregister(&imx_3stack_wm8580_driver);
+ platform_device_unregister(imx_3stack_snd_device);
+}
+
+module_init(imx_3stack_asoc_init);
+module_exit(imx_3stack_asoc_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC wm8580 imx_3stack");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-esai.c b/sound/soc/imx/imx-esai.c
new file mode 100644
index 000000000000..2f85e6ed4c11
--- /dev/null
+++ b/sound/soc/imx/imx-esai.c
@@ -0,0 +1,904 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+
+ /*!
+ * @file imx-esai.c
+ * @brief this file implements the esai interface
+ * in according to ASoC architeture
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/dma.h>
+#include <mach/clock.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "imx-esai.h"
+#include "imx-pcm.h"
+
+/*#define IMX_ESAI_DUMP 1*/
+
+#ifdef IMX_ESAI_DUMP
+#define ESAI_DUMP() \
+ do {pr_info("dump @ %s\n", __func__); \
+ pr_info("ecr %x\n", __raw_readl(ESAI_ECR)); \
+ pr_info("esr %x\n", __raw_readl(ESAI_ESR)); \
+ pr_info("tfcr %x\n", __raw_readl(ESAI_TFCR)); \
+ pr_info("tfsr %x\n", __raw_readl(ESAI_TFSR)); \
+ pr_info("rfcr %x\n", __raw_readl(ESAI_RFCR)); \
+ pr_info("rfsr %x\n", __raw_readl(ESAI_RFSR)); \
+ pr_info("tsr %x\n", __raw_readl(ESAI_TSR)); \
+ pr_info("saisr %x\n", __raw_readl(ESAI_SAISR)); \
+ pr_info("saicr %x\n", __raw_readl(ESAI_SAICR)); \
+ pr_info("tcr %x\n", __raw_readl(ESAI_TCR)); \
+ pr_info("tccr %x\n", __raw_readl(ESAI_TCCR)); \
+ pr_info("rcr %x\n", __raw_readl(ESAI_RCR)); \
+ pr_info("rccr %x\n", __raw_readl(ESAI_RCCR)); \
+ pr_info("tsma %x\n", __raw_readl(ESAI_TSMA)); \
+ pr_info("tsmb %x\n", __raw_readl(ESAI_TSMB)); \
+ pr_info("rsma %x\n", __raw_readl(ESAI_RSMA)); \
+ pr_info("rsmb %x\n", __raw_readl(ESAI_RSMB)); \
+ pr_info("prrc %x\n", __raw_readl(ESAI_PRRC)); \
+ pr_info("pcrc %x\n", __raw_readl(ESAI_PCRC)); } while (0);
+#else
+#define ESAI_DUMP()
+#endif
+
+#define ESAI_IO_BASE_ADDR IO_ADDRESS(ESAI_BASE_ADDR)
+
+#define ESAI_ETDR (ESAI_IO_BASE_ADDR + 0x00)
+#define ESAI_ERDR (ESAI_IO_BASE_ADDR + 0x04)
+#define ESAI_ECR (ESAI_IO_BASE_ADDR + 0x08)
+#define ESAI_ESR (ESAI_IO_BASE_ADDR + 0x0C)
+#define ESAI_TFCR (ESAI_IO_BASE_ADDR + 0x10)
+#define ESAI_TFSR (ESAI_IO_BASE_ADDR + 0x14)
+#define ESAI_RFCR (ESAI_IO_BASE_ADDR + 0x18)
+#define ESAI_RFSR (ESAI_IO_BASE_ADDR + 0x1C)
+#define ESAI_TX0 (ESAI_IO_BASE_ADDR + 0x80)
+#define ESAI_TX1 (ESAI_IO_BASE_ADDR + 0x84)
+#define ESAI_TX2 (ESAI_IO_BASE_ADDR + 0x88)
+#define ESAI_TX3 (ESAI_IO_BASE_ADDR + 0x8C)
+#define ESAI_TX4 (ESAI_IO_BASE_ADDR + 0x90)
+#define ESAI_TX5 (ESAI_IO_BASE_ADDR + 0x94)
+#define ESAI_TSR (ESAI_IO_BASE_ADDR + 0x98)
+#define ESAI_RX0 (ESAI_IO_BASE_ADDR + 0xA0)
+#define ESAI_RX1 (ESAI_IO_BASE_ADDR + 0xA4)
+#define ESAI_RX2 (ESAI_IO_BASE_ADDR + 0xA8)
+#define ESAI_RX3 (ESAI_IO_BASE_ADDR + 0xAC)
+#define ESAI_SAISR (ESAI_IO_BASE_ADDR + 0xCC)
+#define ESAI_SAICR (ESAI_IO_BASE_ADDR + 0xD0)
+#define ESAI_TCR (ESAI_IO_BASE_ADDR + 0xD4)
+#define ESAI_TCCR (ESAI_IO_BASE_ADDR + 0xD8)
+#define ESAI_RCR (ESAI_IO_BASE_ADDR + 0xDC)
+#define ESAI_RCCR (ESAI_IO_BASE_ADDR + 0xE0)
+#define ESAI_TSMA (ESAI_IO_BASE_ADDR + 0xE4)
+#define ESAI_TSMB (ESAI_IO_BASE_ADDR + 0xE8)
+#define ESAI_RSMA (ESAI_IO_BASE_ADDR + 0xEC)
+#define ESAI_RSMB (ESAI_IO_BASE_ADDR + 0xF0)
+#define ESAI_PRRC (ESAI_IO_BASE_ADDR + 0xF8)
+#define ESAI_PCRC (ESAI_IO_BASE_ADDR + 0xFC)
+
+#define ESAI_ECR_ETI (1 << 19)
+#define ESAI_ECR_ETO (1 << 18)
+#define ESAI_ECR_ERI (1 << 17)
+#define ESAI_ECR_ERO (1 << 16)
+#define ESAI_ECR_ERST (1 << 1)
+#define ESAI_ECR_ESAIEN (1 << 0)
+
+#define ESAI_ESR_TINIT (1 << 10)
+#define ESAI_ESR_RFF (1 << 9)
+#define ESAI_ESR_TFE (1 << 8)
+#define ESAI_ESR_TLS (1 << 7)
+#define ESAI_ESR_TDE (1 << 6)
+#define ESAI_ESR_TED (1 << 5)
+#define ESAI_ESR_TD (1 << 4)
+#define ESAI_ESR_RLS (1 << 3)
+#define ESAI_ESR_RDE (1 << 2)
+#define ESAI_ESR_RED (1 << 1)
+#define ESAI_ESR_RD (1 << 0)
+
+#define ESAI_TFCR_TIEN (1 << 19)
+#define ESAI_TFCR_TE5 (1 << 7)
+#define ESAI_TFCR_TE4 (1 << 6)
+#define ESAI_TFCR_TE3 (1 << 5)
+#define ESAI_TFCR_TE2 (1 << 4)
+#define ESAI_TFCR_TE1 (1 << 3)
+#define ESAI_TFCR_TE0 (1 << 2)
+#define ESAI_TFCR_TFR (1 << 1)
+#define ESAI_TFCR_TFEN (1 << 0)
+#define ESAI_TFCR_TE(x) ((0x3f >> (6 - ((x + 1) >> 1))) << 2)
+#define ESAI_TFCR_TE_MASK 0xfff03
+#define ESAI_TFCR_TFWM(x) ((x - 1) << 8)
+#define ESAI_TFCR_TWA_MASK 0xf8ffff
+
+#define ESAI_RFCR_REXT (1 << 19)
+#define ESAI_RFCR_RE3 (1 << 5)
+#define ESAI_RFCR_RE2 (1 << 4)
+#define ESAI_RFCR_RE1 (1 << 3)
+#define ESAI_RFCR_RE0 (1 << 2)
+#define ESAI_RFCR_RFR (1 << 1)
+#define ESAI_RFCR_RFEN (1 << 0)
+#define ESAI_RFCR_RE(x) ((0xf >> (4 - ((x + 1) >> 1))) << 3)
+#define ESAI_RFCR_RE_MASK 0xfffc3
+#define ESAI_RFCR_RFWM(x) ((x-1) << 8)
+#define ESAI_RFCR_RWA_MASK 0xf8ffff
+
+#define ESAI_WORD_LEN_32 (0x00 << 16)
+#define ESAI_WORD_LEN_28 (0x01 << 16)
+#define ESAI_WORD_LEN_24 (0x02 << 16)
+#define ESAI_WORD_LEN_20 (0x03 << 16)
+#define ESAI_WORD_LEN_16 (0x04 << 16)
+#define ESAI_WORD_LEN_12 (0x05 << 16)
+#define ESAI_WORD_LEN_8 (0x06 << 16)
+#define ESAI_WORD_LEN_4 (0x07 << 16)
+
+#define ESAI_SAISR_TODFE (1 << 17)
+#define ESAI_SAISR_TEDE (1 << 16)
+#define ESAI_SAISR_TDE (1 << 15)
+#define ESAI_SAISR_TUE (1 << 14)
+#define ESAI_SAISR_TFS (1 << 13)
+#define ESAI_SAISR_RODF (1 << 10)
+#define ESAI_SAISR_REDF (1 << 9)
+#define ESAI_SAISR_RDF (1 << 8)
+#define ESAI_SAISR_ROE (1 << 7)
+#define ESAI_SAISR_RFS (1 << 6)
+#define ESAI_SAISR_IF2 (1 << 2)
+#define ESAI_SAISR_IF1 (1 << 1)
+#define ESAI_SAISR_IF0 (1 << 0)
+
+#define ESAI_SAICR_ALC (1 << 8)
+#define ESAI_SAICR_TEBE (1 << 7)
+#define ESAI_SAICR_SYNC (1 << 6)
+#define ESAI_SAICR_OF2 (1 << 2)
+#define ESAI_SAICR_OF1 (1 << 1)
+#define ESAI_SAICR_OF0 (1 << 0)
+
+#define ESAI_TCR_TLIE (1 << 23)
+#define ESAI_TCR_TIE (1 << 22)
+#define ESAI_TCR_TEDIE (1 << 21)
+#define ESAI_TCR_TEIE (1 << 20)
+#define ESAI_TCR_TPR (1 << 19)
+#define ESAI_TCR_PADC (1 << 17)
+#define ESAI_TCR_TFSR (1 << 16)
+#define ESAI_TCR_TFSL (1 << 15)
+#define ESAI_TCR_TWA (1 << 7)
+#define ESAI_TCR_TSHFD_MSB (0 << 6)
+#define ESAI_TCR_TSHFD_LSB (1 << 6)
+#define ESAI_TCR_TE5 (1 << 5)
+#define ESAI_TCR_TE4 (1 << 4)
+#define ESAI_TCR_TE3 (1 << 3)
+#define ESAI_TCR_TE2 (1 << 2)
+#define ESAI_TCR_TE1 (1 << 1)
+#define ESAI_TCR_TE0 (1 << 0)
+#define ESAI_TCR_TE(x) (0x3f >> (6 - ((x + 1) >> 1)))
+
+#define ESAI_TCR_TSWS_MASK 0xff83ff
+#define ESAI_TCR_TSWS_STL8_WDL8 (0x00 << 10)
+#define ESAI_TCR_TSWS_STL12_WDL8 (0x04 << 10)
+#define ESAI_TCR_TSWS_STL12_WDL12 (0x01 << 10)
+#define ESAI_TCR_TSWS_STL16_WDL8 (0x08 << 10)
+#define ESAI_TCR_TSWS_STL16_WDL12 (0x05 << 10)
+#define ESAI_TCR_TSWS_STL16_WDL16 (0x02 << 10)
+#define ESAI_TCR_TSWS_STL20_WDL8 (0x0c << 10)
+#define ESAI_TCR_TSWS_STL20_WDL12 (0x09 << 10)
+#define ESAI_TCR_TSWS_STL20_WDL16 (0x06 << 10)
+#define ESAI_TCR_TSWS_STL20_WDL20 (0x03 << 10)
+#define ESAI_TCR_TSWS_STL24_WDL8 (0x10 << 10)
+#define ESAI_TCR_TSWS_STL24_WDL12 (0x0d << 10)
+#define ESAI_TCR_TSWS_STL24_WDL16 (0x0a << 10)
+#define ESAI_TCR_TSWS_STL24_WDL20 (0x07 << 10)
+#define ESAI_TCR_TSWS_STL24_WDL24 (0x1e << 10)
+#define ESAI_TCR_TSWS_STL32_WDL8 (0x18 << 10)
+#define ESAI_TCR_TSWS_STL32_WDL12 (0x15 << 10)
+#define ESAI_TCR_TSWS_STL32_WDL16 (0x12 << 10)
+#define ESAI_TCR_TSWS_STL32_WDL20 (0x0f << 10)
+#define ESAI_TCR_TSWS_STL32_WDL24 (0x1f << 10)
+
+#define ESAI_TCR_TMOD_MASK 0xfffcff
+#define ESAI_TCR_TMOD_NORMAL (0x00 << 8)
+#define ESAI_TCR_TMOD_ONDEMAND (0x01 << 8)
+#define ESAI_TCR_TMOD_NETWORK (0x01 << 8)
+#define ESAI_TCR_TMOD_RESERVED (0x02 << 8)
+#define ESAI_TCR_TMOD_AC97 (0x03 << 8)
+
+#define ESAI_TCCR_THCKD (1 << 23)
+#define ESAI_TCCR_TFSD (1 << 22)
+#define ESAI_TCCR_TCKD (1 << 21)
+#define ESAI_TCCR_THCKP (1 << 20)
+#define ESAI_TCCR_TFSP (1 << 19)
+#define ESAI_TCCR_TCKP (1 << 18)
+
+#define ESAI_TCCR_TPSR_MASK 0xfffeff
+#define ESAI_TCCR_TPSR_BYPASS (1 << 8)
+#define ESAI_TCCR_TPSR_DIV8 (0 << 8)
+
+#define ESAI_TCCR_TFP_MASK 0xfc3fff
+#define ESAI_TCCR_TFP(x) ((x & 0xf) << 14)
+
+#define ESAI_TCCR_TDC_MASK 0xffc1ff
+#define ESAI_TCCR_TDC(x) (((x) & 0x1f) << 9)
+
+#define ESAI_TCCR_TPM_MASK 0xffff00
+#define ESAI_TCCR_TPM(x) (x & 0xff)
+
+#define ESAI_RCR_RLIE (1 << 23)
+#define ESAI_RCR_RIE (1 << 22)
+#define ESAI_RCR_REDIE (1 << 21)
+#define ESAI_RCR_REIE (1 << 20)
+#define ESAI_RCR_RPR (1 << 19)
+#define ESAI_RCR_RFSR (1 << 16)
+#define ESAI_RCR_RFSL (1 << 15)
+#define ESAI_RCR_RWA (1 << 7)
+#define ESAI_RCR_RSHFD_MSB (0 << 6)
+#define ESAI_RCR_RSHFD_LSB (1 << 6)
+#define ESAI_RCR_RE3 (1 << 3)
+#define ESAI_RCR_RE2 (1 << 2)
+#define ESAI_RCR_RE1 (1 << 1)
+#define ESAI_RCR_RE0 (1 << 0)
+#define ESAI_RCR_RE(x) ((0xf >> (4 - ((x + 1) >> 1))) << 1)
+
+#define ESAI_RCR_RSWS_MASK 0xff83ff
+#define ESAI_RCR_RSWS_STL8_WDL8 (0x00 << 10)
+#define ESAI_RCR_RSWS_STL12_WDL8 (0x04 << 10)
+#define ESAI_RCR_RSWS_STL12_WDL12 (0x01 << 10)
+#define ESAI_RCR_RSWS_STL16_WDL8 (0x08 << 10)
+#define ESAI_RCR_RSWS_STL16_WDL12 (0x05 << 10)
+#define ESAI_RCR_RSWS_STL16_WDL16 (0x02 << 10)
+#define ESAI_RCR_RSWS_STL20_WDL8 (0x0c << 10)
+#define ESAI_RCR_RSWS_STL20_WDL12 (0x09 << 10)
+#define ESAI_RCR_RSWS_STL20_WDL16 (0x06 << 10)
+#define ESAI_RCR_RSWS_STL20_WDL20 (0x03 << 10)
+#define ESAI_RCR_RSWS_STL24_WDL8 (0x10 << 10)
+#define ESAI_RCR_RSWS_STL24_WDL12 (0x0d << 10)
+#define ESAI_RCR_RSWS_STL24_WDL16 (0x0a << 10)
+#define ESAI_RCR_RSWS_STL24_WDL20 (0x07 << 10)
+#define ESAI_RCR_RSWS_STL24_WDL24 (0x1e << 10)
+#define ESAI_RCR_RSWS_STL32_WDL8 (0x18 << 10)
+#define ESAI_RCR_RSWS_STL32_WDL12 (0x15 << 10)
+#define ESAI_RCR_RSWS_STL32_WDL16 (0x12 << 10)
+#define ESAI_RCR_RSWS_STL32_WDL20 (0x0f << 10)
+#define ESAI_RCR_RSWS_STL32_WDL24 (0x1f << 10)
+
+#define ESAI_RCR_RMOD_MASK 0xfffcff
+#define ESAI_RCR_RMOD_NORMAL (0x00 << 8)
+#define ESAI_RCR_RMOD_ONDEMAND (0x01 << 8)
+#define ESAI_RCR_RMOD_NETWORK (0x01 << 8)
+#define ESAI_RCR_RMOD_RESERVED (0x02 << 8)
+#define ESAI_RCR_RMOD_AC97 (0x03 << 8)
+
+#define ESAI_RCCR_RHCKD (1 << 23)
+#define ESAI_RCCR_RFSD (1 << 22)
+#define ESAI_RCCR_RCKD (1 << 21)
+#define ESAI_RCCR_RHCKP (1 << 20)
+#define ESAI_RCCR_RFSP (1 << 19)
+#define ESAI_RCCR_RCKP (1 << 18)
+
+#define ESAI_RCCR_RPSR_MASK 0xfffeff
+#define ESAI_RCCR_RPSR_BYPASS (1 << 8)
+#define ESAI_RCCR_RPSR_DIV8 (0 << 8)
+
+#define ESAI_RCCR_RFP_MASK 0xfc3fff
+#define ESAI_RCCR_RFP(x) ((x & 0xf) << 14)
+
+#define ESAI_RCCR_RDC_MASK 0xffc1ff
+#define ESAI_RCCR_RDC(x) (((x) & 0x1f) << 9)
+
+#define ESAI_RCCR_RPM_MASK 0xffff00
+#define ESAI_RCCR_RPM(x) (x & 0xff)
+
+#define ESAI_GPIO_ESAI 0xfff
+
+/* ESAI clock source */
+#define ESAI_CLK_FSYS 0
+#define ESAI_CLK_EXTAL 1
+
+/* ESAI clock divider */
+#define ESAI_TX_DIV_PSR 0
+#define ESAI_TX_DIV_PM 1
+#define ESAI_TX_DIV_FP 2
+#define ESAI_RX_DIV_PSR 3
+#define ESAI_RX_DIV_PM 4
+#define ESAI_RX_DIV_FP 5
+
+static int imx_esai_txrx_state;
+
+static int imx_esai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ u32 ecr, tccr, rccr;
+
+ ecr = __raw_readl(ESAI_ECR);
+ tccr = __raw_readl(ESAI_TCCR);
+ rccr = __raw_readl(ESAI_RCCR);
+
+ if (dir == SND_SOC_CLOCK_IN) {
+ if (cpu_dai->id & IMX_DAI_ESAI_TX)
+ tccr &=
+ ~(ESAI_TCCR_THCKD | ESAI_TCCR_TCKD |
+ ESAI_TCCR_TFSD);
+ if (cpu_dai->id & IMX_DAI_ESAI_RX)
+ rccr &=
+ ~(ESAI_RCCR_RHCKD | ESAI_RCCR_RCKD |
+ ESAI_RCCR_RFSD);
+ } else {
+ if (cpu_dai->id & IMX_DAI_ESAI_TX)
+ tccr |=
+ ESAI_TCCR_THCKD | ESAI_TCCR_TCKD | ESAI_TCCR_TFSD;
+ if (cpu_dai->id & IMX_DAI_ESAI_RX)
+ rccr |=
+ ESAI_RCCR_RHCKD | ESAI_RCCR_RCKD | ESAI_RCCR_RFSD;
+ if (clk_id == ESAI_CLK_FSYS) {
+ if (cpu_dai->id & IMX_DAI_ESAI_TX)
+ ecr &= ~(ESAI_ECR_ETI | ESAI_ECR_ETO);
+ if (cpu_dai->id & IMX_DAI_ESAI_RX)
+ ecr &= ~(ESAI_ECR_ERI | ESAI_ECR_ERO);
+ } else if (clk_id == ESAI_CLK_EXTAL) {
+ if (cpu_dai->id & IMX_DAI_ESAI_TX) {
+ ecr |= ESAI_ECR_ETI;
+ ecr &= ~ESAI_ECR_ETO;
+ }
+ if (cpu_dai->id & IMX_DAI_ESAI_RX) {
+ ecr |= ESAI_ECR_ERI;
+ ecr &= ~ESAI_ECR_ERO;
+ }
+ }
+ }
+
+ __raw_writel(ecr, ESAI_ECR);
+ if (cpu_dai->id & IMX_DAI_ESAI_TX)
+ __raw_writel(tccr, ESAI_TCCR);
+ if (cpu_dai->id & IMX_DAI_ESAI_RX)
+ __raw_writel(rccr, ESAI_RCCR);
+
+ ESAI_DUMP();
+
+ return 0;
+}
+
+static int imx_esai_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ u32 tccr, rccr;
+
+ tccr = __raw_readl(ESAI_TCCR);
+ rccr = __raw_readl(ESAI_RCCR);
+
+ switch (div_id) {
+ case ESAI_TX_DIV_PSR:
+ tccr &= ESAI_TCCR_TPSR_MASK;
+ tccr |= div;
+ break;
+ case ESAI_TX_DIV_PM:
+ tccr &= ESAI_TCCR_TPM_MASK;
+ tccr |= ESAI_TCCR_TPM(div);
+ break;
+ case ESAI_TX_DIV_FP:
+ tccr &= ESAI_TCCR_TFP_MASK;
+ tccr |= ESAI_TCCR_TFP(div);
+ break;
+ case ESAI_RX_DIV_PSR:
+ rccr &= ESAI_RCCR_RPSR_MASK;
+ rccr |= div;
+ break;
+ case ESAI_RX_DIV_PM:
+ rccr &= ESAI_RCCR_RPM_MASK;
+ rccr |= ESAI_RCCR_RPM(div);
+ break;
+ case ESAI_RX_DIV_FP:
+ rccr &= ESAI_RCCR_RFP_MASK;
+ rccr |= ESAI_RCCR_RFP(div);
+ break;
+ return -EINVAL;
+ }
+ if (cpu_dai->id & IMX_DAI_ESAI_TX)
+ __raw_writel(tccr, ESAI_TCCR);
+ if (cpu_dai->id & IMX_DAI_ESAI_RX)
+ __raw_writel(rccr, ESAI_RCCR);
+ return 0;
+}
+
+/*
+ * ESAI Network Mode or TDM slots configuration.
+ */
+static int imx_esai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int mask, int slots)
+{
+ u32 tccr, rccr;
+
+ if (cpu_dai->id & IMX_DAI_ESAI_TX) {
+ tccr = __raw_readl(ESAI_TCCR);
+
+ tccr &= ESAI_TCCR_TDC_MASK;
+ tccr |= ESAI_TCCR_TDC(slots - 1);
+
+ __raw_writel(tccr, ESAI_TCCR);
+ __raw_writel((mask & 0xffff), ESAI_TSMA);
+ __raw_writel(((mask >> 16) & 0xffff), ESAI_TSMB);
+ }
+
+ if (cpu_dai->id & IMX_DAI_ESAI_RX) {
+ rccr = __raw_readl(ESAI_RCCR);
+
+ rccr &= ESAI_RCCR_RDC_MASK;
+ rccr |= ESAI_RCCR_RDC(slots - 1);
+
+ __raw_writel(rccr, ESAI_RCCR);
+ __raw_writel((mask & 0xffff), ESAI_RSMA);
+ __raw_writel(((mask >> 16) & 0xffff), ESAI_RSMB);
+ }
+
+ ESAI_DUMP();
+
+ return 0;
+}
+
+/*
+ * ESAI DAI format configuration.
+ */
+static int imx_esai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ u32 tcr, tccr, rcr, rccr, saicr;
+
+ tcr = __raw_readl(ESAI_TCR);
+ tccr = __raw_readl(ESAI_TCCR);
+ rcr = __raw_readl(ESAI_RCR);
+ rccr = __raw_readl(ESAI_RCCR);
+ saicr = __raw_readl(ESAI_SAICR);
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* data on rising edge of bclk, frame low 1clk before data */
+ tcr &= ~ESAI_TCR_TFSL;
+ tcr |= ESAI_TCR_TFSR;
+ rcr &= ~ESAI_RCR_RFSL;
+ rcr |= ESAI_RCR_RFSR;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* data on rising edge of bclk, frame high with data */
+ tcr &= ~(ESAI_TCR_TFSL | ESAI_TCR_TFSR);
+ rcr &= ~(ESAI_RCR_RFSL | ESAI_RCR_RFSR);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /* data on rising edge of bclk, frame high with data */
+ tcr |= ESAI_TCR_TFSL;
+ rcr |= ESAI_RCR_RFSL;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* data on rising edge of bclk, frame high 1clk before data */
+ tcr |= ESAI_TCR_TFSL;
+ rcr |= ESAI_RCR_RFSL;
+ break;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ tccr |= ESAI_TCCR_TFSP;
+ tccr &= ~(ESAI_TCCR_TCKP | ESAI_TCCR_THCKP);
+ rccr &= ~(ESAI_RCCR_RCKP | ESAI_RCCR_RHCKP);
+ rccr |= ESAI_RCCR_RFSP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ tccr &= ~(ESAI_TCCR_TCKP | ESAI_TCCR_THCKP | ESAI_TCCR_TFSP);
+ rccr &= ~(ESAI_RCCR_RCKP | ESAI_RCCR_RHCKP | ESAI_RCCR_RFSP);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ tccr |= ESAI_TCCR_TCKP | ESAI_TCCR_THCKP | ESAI_TCCR_TFSP;
+ rccr |= ESAI_RCCR_RCKP | ESAI_RCCR_RHCKP | ESAI_RCCR_RFSP;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ tccr &= ~ESAI_TCCR_TFSP;
+ tccr |= ESAI_TCCR_TCKP | ESAI_TCCR_THCKP;
+ rccr &= ~ESAI_RCCR_RFSP;
+ rccr |= ESAI_RCCR_RCKP | ESAI_RCCR_RHCKP;
+ break;
+ }
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ tccr &= ~(ESAI_TCCR_TFSD | ESAI_TCCR_TCKD);
+ rccr &= ~(ESAI_RCCR_RFSD | ESAI_RCCR_RCKD);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ tccr &= ~ESAI_TCCR_TFSD;
+ tccr |= ESAI_TCCR_TCKD;
+ rccr &= ~ESAI_RCCR_RFSD;
+ rccr |= ESAI_RCCR_RCKD;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ tccr &= ~ESAI_TCCR_TCKD;
+ tccr |= ESAI_TCCR_TFSD;
+ rccr &= ~ESAI_RCCR_RCKD;
+ rccr |= ESAI_RCCR_RFSD;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ tccr |= (ESAI_TCCR_TFSD | ESAI_TCCR_TCKD);
+ rccr |= (ESAI_RCCR_RFSD | ESAI_RCCR_RCKD);
+ }
+
+ /* sync */
+ if (fmt & SND_SOC_DAIFMT_ASYNC)
+ saicr &= ~ESAI_SAICR_SYNC;
+ else
+ saicr |= ESAI_SAICR_SYNC;
+
+ tcr &= ESAI_TCR_TMOD_MASK;
+ rcr &= ESAI_RCR_RMOD_MASK;
+ /* tdm - only for stereo atm */
+ if (fmt & SND_SOC_DAIFMT_TDM) {
+ tcr |= ESAI_TCR_TMOD_NETWORK;
+ rcr |= ESAI_RCR_RMOD_NETWORK;
+ } else {
+ tcr |= ESAI_TCR_TMOD_NORMAL;
+ rcr |= ESAI_RCR_RMOD_NORMAL;
+ }
+
+ if (cpu_dai->id & IMX_DAI_ESAI_TX) {
+ __raw_writel(tcr, ESAI_TCR);
+ __raw_writel(tccr, ESAI_TCCR);
+ }
+ if (cpu_dai->id & IMX_DAI_ESAI_RX) {
+ __raw_writel(rcr, ESAI_RCR);
+ __raw_writel(rccr, ESAI_RCCR);
+ }
+
+ __raw_writel(saicr, ESAI_SAICR);
+
+ ESAI_DUMP();
+ return 0;
+}
+
+static struct clk *esai_clk;
+
+static int fifo_err_counter;
+
+static irqreturn_t esai_irq(int irq, void *dev_id)
+{
+ if (fifo_err_counter++ % 1000 == 0)
+ printk(KERN_ERR
+ "esai_irq SAISR %x fifo_errs=%d\n",
+ __raw_readl(ESAI_SAISR), fifo_err_counter);
+ return IRQ_HANDLED;
+}
+
+static int imx_esai_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ if (cpu_dai->playback.active && (cpu_dai->id & IMX_DAI_ESAI_TX))
+ return 0;
+ if (cpu_dai->capture.active && (cpu_dai->id & IMX_DAI_ESAI_RX))
+ return 0;
+
+ if (!(imx_esai_txrx_state & IMX_DAI_ESAI_TXRX)) {
+ if (request_irq(MXC_INT_ESAI, esai_irq, 0, "esai", NULL)) {
+ pr_err("%s: failure requesting esai irq\n", __func__);
+ return -EBUSY;
+ }
+ clk_enable(esai_clk);
+ __raw_writel(ESAI_ECR_ERST, ESAI_ECR);
+ __raw_writel(ESAI_ECR_ESAIEN, ESAI_ECR);
+
+ __raw_writel(ESAI_GPIO_ESAI, ESAI_PRRC);
+ __raw_writel(ESAI_GPIO_ESAI, ESAI_PCRC);
+ }
+
+ if (cpu_dai->id & IMX_DAI_ESAI_TX) {
+ imx_esai_txrx_state |= IMX_DAI_ESAI_TX;
+ __raw_writel(ESAI_TCR_TPR, ESAI_TCR);
+ }
+ if (cpu_dai->id & IMX_DAI_ESAI_RX) {
+ imx_esai_txrx_state |= IMX_DAI_ESAI_RX;
+ __raw_writel(ESAI_RCR_RPR, ESAI_RCR);
+ }
+
+ ESAI_DUMP();
+ return 0;
+}
+
+/*
+ * This function is called to initialize the TX port before enable
+ * the tx port.
+ */
+static int imx_esai_hw_tx_params(struct snd_pcm_substream
+ *substream, struct snd_pcm_hw_params
+ *params)
+{
+ u32 tcr, tfcr;
+ unsigned int channels;
+
+ tcr = __raw_readl(ESAI_TCR);
+ tfcr = __raw_readl(ESAI_TFCR);
+
+ tfcr |= ESAI_TFCR_TFR;
+ __raw_writel(tfcr, ESAI_TFCR);
+ tfcr &= ~ESAI_TFCR_TFR;
+ /* DAI data (word) size */
+ tfcr &= ESAI_TFCR_TWA_MASK;
+ tcr &= ESAI_TCR_TSWS_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ tfcr |= ESAI_WORD_LEN_16;
+ tcr |= ESAI_TCR_TSHFD_MSB | ESAI_TCR_TSWS_STL32_WDL16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ tfcr |= ESAI_WORD_LEN_20;
+ tcr |= ESAI_TCR_TSHFD_MSB | ESAI_TCR_TSWS_STL32_WDL20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ tfcr |= ESAI_WORD_LEN_24;
+ tcr |= ESAI_TCR_TSHFD_MSB | ESAI_TCR_TSWS_STL32_WDL24;
+ break;
+ }
+
+ channels = params_channels(params);
+ tfcr &= ESAI_TFCR_TE_MASK;
+ tfcr |= ESAI_TFCR_TE(channels);
+
+ tfcr |= ESAI_TFCR_TFWM(64);
+
+ /* Left aligned, Zero padding */
+ tcr |= ESAI_TCR_PADC;
+
+ __raw_writel(tcr, ESAI_TCR);
+ __raw_writel(tfcr, ESAI_TFCR);
+
+ ESAI_DUMP();
+ return 0;
+}
+
+/*
+ * This function is called to initialize the RX port before enable
+ * the rx port.
+ */
+static int imx_esai_hw_rx_params(struct snd_pcm_substream
+ *substream, struct snd_pcm_hw_params
+ *params)
+{
+ u32 rcr, rfcr;
+ unsigned int channels;
+
+ rcr = __raw_readl(ESAI_RCR);
+ rfcr = __raw_readl(ESAI_RFCR);
+
+ rfcr |= ESAI_RFCR_RFR;
+ __raw_writel(rfcr, ESAI_RFCR);
+ rfcr &= ~ESAI_RFCR_RFR;
+
+ rfcr &= ESAI_RFCR_RWA_MASK;
+ rcr &= ESAI_RCR_RSWS_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ rfcr |= ESAI_WORD_LEN_16;
+ rcr |= ESAI_RCR_RSHFD_MSB | ESAI_RCR_RSWS_STL16_WDL16;
+ break;
+ }
+
+ channels = params_channels(params);
+ rfcr &= ESAI_RFCR_RE_MASK;
+ rfcr |= ESAI_RFCR_RE(channels);
+
+ rfcr |= ESAI_RFCR_RFWM(64);
+
+ __raw_writel(rcr, ESAI_RCR);
+ __raw_writel(rfcr, ESAI_RFCR);
+ return 0;
+}
+
+/*
+ * This function is called to initialize the TX or RX port,
+ */
+static int imx_esai_hw_params(struct snd_pcm_substream
+ *substream, struct snd_pcm_hw_params *params)
+{
+ /* Tx/Rx config */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (__raw_readl(ESAI_TCR) & ESAI_TCR_TE0)
+ return 0;
+ return imx_esai_hw_tx_params(substream, params);
+ } else {
+ if (__raw_readl(ESAI_RCR) & ESAI_RCR_RE1)
+ return 0;
+ return imx_esai_hw_rx_params(substream, params);
+ }
+}
+
+static int imx_esai_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ u32 reg, tfcr = 0, rfcr = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tfcr = __raw_readl(ESAI_TFCR);
+ reg = __raw_readl(ESAI_TCR);
+ } else {
+ rfcr = __raw_readl(ESAI_RFCR);
+ reg = __raw_readl(ESAI_RCR);
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tfcr |= ESAI_TFCR_TFEN;
+ __raw_writel(tfcr, ESAI_TFCR);
+ reg &= ~ESAI_TCR_TPR;
+ reg |= ESAI_TCR_TE(substream->runtime->channels);
+ __raw_writel(reg, ESAI_TCR);
+ } else {
+ rfcr |= ESAI_RFCR_RFEN;
+ __raw_writel(rfcr, ESAI_RFCR);
+ reg &= ~ESAI_RCR_RPR;
+ reg |= ESAI_RCR_RE(substream->runtime->channels);
+ __raw_writel(reg, ESAI_RCR);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg &= ~ESAI_TCR_TE(substream->runtime->channels);
+ __raw_writel(reg, ESAI_TCR);
+ reg |= ESAI_TCR_TPR;
+ __raw_writel(reg, ESAI_TCR);
+ tfcr |= ESAI_TFCR_TFR;
+ tfcr &= ~ESAI_TFCR_TFEN;
+ __raw_writel(tfcr, ESAI_TFCR);
+ tfcr &= ~ESAI_TFCR_TFR;
+ __raw_writel(tfcr, ESAI_TFCR);
+ } else {
+ reg &= ~ESAI_RCR_RE(substream->runtime->channels);
+ __raw_writel(reg, ESAI_RCR);
+ reg |= ESAI_RCR_RPR;
+ __raw_writel(reg, ESAI_RCR);
+ rfcr |= ESAI_RFCR_RFR;
+ rfcr &= ~ESAI_RFCR_RFEN;
+ __raw_writel(rfcr, ESAI_RFCR);
+ rfcr &= ~ESAI_RFCR_RFR;
+ __raw_writel(rfcr, ESAI_RFCR);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ESAI_DUMP();
+ return 0;
+}
+
+static void imx_esai_shutdown(struct snd_pcm_substream
+ *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ if (cpu_dai->id & IMX_DAI_ESAI_TX)
+ imx_esai_txrx_state &= ~IMX_DAI_ESAI_TX;
+ if (cpu_dai->id & IMX_DAI_ESAI_RX)
+ imx_esai_txrx_state &= ~IMX_DAI_ESAI_RX;
+
+ /* shutdown ESAI if neither Tx or Rx is active */
+ if (!(imx_esai_txrx_state & IMX_DAI_ESAI_TXRX)) {
+ free_irq(MXC_INT_ESAI, NULL);
+ clk_disable(esai_clk);
+ }
+}
+
+#ifdef CONFIG_PM
+static int imx_esai_suspend(struct platform_device *dev,
+ struct snd_soc_dai *dai)
+{
+ if (!dai->active)
+ return 0;
+
+ /*do we need to disable any clocks */
+ return 0;
+}
+
+static int imx_esai_resume(struct platform_device *dev, struct snd_soc_dai *dai)
+{
+ if (!dai->active)
+ return 0;
+
+ /* do we need to enable any clocks */
+ return 0;
+}
+
+#else
+#define imx_esai_suspend NULL
+#define imx_esai_resume NULL
+#endif
+
+static int imx_esai_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+ if (!strcmp("imx-esai-tx", dai->name))
+ dai->id = IMX_DAI_ESAI_TX;
+ else if (!strcmp("imx-esai-rx", dai->name))
+ dai->id = IMX_DAI_ESAI_RX;
+ else if (!strcmp("imx-esai-txrx", dai->name))
+ dai->id = IMX_DAI_ESAI_TXRX;
+ else {
+ pr_err("%s: invalid device %s\n", __func__, dai->name);
+ return -ENODEV;
+ }
+
+ imx_esai_txrx_state = 0;
+
+ esai_clk = clk_get(NULL, "esai_clk");
+
+ return 0;
+}
+
+static void imx_esai_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+
+ clk_put(esai_clk);
+}
+
+#define IMX_ESAI_RATES SNDRV_PCM_RATE_8000_192000
+
+#define IMX_ESAI_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai imx_esai_dai = {
+ .name = "imx-esai",
+ .id = 0,
+ .type = SND_SOC_DAI_PCM,
+ .probe = imx_esai_probe,
+ .remove = imx_esai_remove,
+ .suspend = imx_esai_suspend,
+ .resume = imx_esai_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = IMX_ESAI_RATES,
+ .formats = IMX_ESAI_FORMATS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = IMX_ESAI_RATES,
+ .formats = IMX_ESAI_FORMATS,
+ },
+ .ops = {
+ .startup = imx_esai_startup,
+ .shutdown = imx_esai_shutdown,
+ .trigger = imx_esai_trigger,
+ .hw_params = imx_esai_hw_params,
+ },
+ .dai_ops = {
+ .set_sysclk = imx_esai_set_dai_sysclk,
+ .set_clkdiv = imx_esai_set_dai_clkdiv,
+ .set_fmt = imx_esai_set_dai_fmt,
+ .set_tdm_slot = imx_esai_set_dai_tdm_slot,
+ },
+};
+
+EXPORT_SYMBOL_GPL(imx_esai_dai);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX ASoC ESAI driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-esai.h b/sound/soc/imx/imx-esai.h
new file mode 100644
index 000000000000..8ac2e3fbe6d3
--- /dev/null
+++ b/sound/soc/imx/imx-esai.h
@@ -0,0 +1,25 @@
+/*
+ * imx-esai.h -- ESAI driver header file for Freescale IMX
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * 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 _MXC_ESAI_H
+#define _MXC_ESAI_H
+
+#define IMX_DAI_ESAI_TX 0x04
+#define IMX_DAI_ESAI_RX 0x08
+#define IMX_DAI_ESAI_TXRX (IMX_DAI_ESAI_TX | IMX_DAI_ESAI_RX)
+
+extern struct snd_soc_dai imx_esai_dai;
+
+#endif
diff --git a/sound/soc/imx/imx-pcm.c b/sound/soc/imx/imx-pcm.c
new file mode 100644
index 000000000000..6836523a60ed
--- /dev/null
+++ b/sound/soc/imx/imx-pcm.c
@@ -0,0 +1,689 @@
+/*
+ * imx-pcm.c -- ALSA SoC interface for the Freescale i.MX3 CPU's
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * Based on imx31-pcm.c by Nicolas Pitre, (C) 2004 MontaVista Software, Inc.
+ * and on mxc-alsa-mc13783 (C) 2006-2009 Freescale.
+ *
+ * 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>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/dma.h>
+#include <mach/spba.h>
+#include <mach/clock.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "imx-pcm.h"
+#include "imx-ssi.h"
+#include "imx-esai.h"
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+#include <linux/delay.h>
+#include <linux/mxc_asrc.h>
+#endif
+
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+static bool UseIram = 1;
+#else
+static bool UseIram;
+#endif
+
+/* debug */
+#define IMX_PCM_DEBUG 0
+#if IMX_PCM_DEBUG
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+
+static const struct snd_pcm_hardware imx_pcm_hardware = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+#ifdef CONFIG_SND_MXC_SOC_IRAM
+ .buffer_bytes_max = SND_RAM_SIZE,
+ .period_bytes_max = SND_RAM_SIZE / 4,
+#else
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_max = 16 * 1024,
+#endif
+ .period_bytes_min = 2 * SZ_1K,
+ .periods_min = 2,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static uint32_t audio_iram_phys_base_addr;
+static void *audio_iram_virt_base_addr;
+
+static struct vm_operations_struct snd_mxc_audio_playback_vm_ops = {
+ .open = snd_pcm_mmap_data_open,
+ .close = snd_pcm_mmap_data_close,
+};
+
+/*
+ enable user space access to iram buffer
+*/
+static int imx_iram_audio_playback_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
+{
+ unsigned long off;
+ unsigned long phys;
+ unsigned long size;
+ int ret = 0;
+
+ area->vm_ops = &snd_mxc_audio_playback_vm_ops;
+ area->vm_private_data = substream;
+
+ off = area->vm_pgoff << PAGE_SHIFT;
+ phys = audio_iram_phys_base_addr + off;
+ size = area->vm_end - area->vm_start;
+
+ if (off + size > SND_RAM_SIZE)
+ return -EINVAL;
+
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ area->vm_flags |= VM_IO;
+ ret =
+ remap_pfn_range(area, area->vm_start, phys >> PAGE_SHIFT,
+ size, area->vm_page_prot);
+ if (ret == 0)
+ area->vm_ops->open(area);
+
+ return ret;
+}
+
+/*
+ Map nbytes in virtual space
+ bytes -audio iram iram partition size
+ phys_addr - physical address of iram buffer
+ returns - virtual address of the iram buffer or NULL if fail
+*/
+static void *imx_iram_init(dma_addr_t *phys_addr, size_t bytes)
+{
+ void *iram_base;
+
+ iram_base = (void *)ioremap((uint32_t) SND_RAM_BASE_ADDR, bytes);
+
+ audio_iram_virt_base_addr = iram_base;
+ audio_iram_phys_base_addr = (uint32_t) SND_RAM_BASE_ADDR;
+ *phys_addr = (dma_addr_t) SND_RAM_BASE_ADDR;
+
+ return audio_iram_virt_base_addr;
+
+}
+
+/*
+ destroy the virtual mapping of the iram buffer
+*/
+
+static void imx_iram_free(void)
+{
+ iounmap(audio_iram_virt_base_addr);
+}
+
+static int imx_get_sdma_transfer(int format, int dai_port,
+ struct snd_pcm_substream *substream)
+{
+ int transfer = -1;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+ if (prtd->asrc_enable == 1) {
+ if (dai_port == IMX_DAI_SSI0) {
+ if (prtd->asrc_index == 0)
+ transfer = MXC_DMA_ASRCA_SSI1_TX0;
+ else if (prtd->asrc_index == 1)
+ transfer = MXC_DMA_ASRCB_SSI1_TX0;
+ } else if (dai_port == IMX_DAI_SSI1) {
+ if (prtd->asrc_index == 0)
+ transfer = MXC_DMA_ASRCA_SSI1_TX1;
+ else if (prtd->asrc_index == 1)
+ transfer = MXC_DMA_ASRCB_SSI1_TX1;
+ } else if (dai_port == IMX_DAI_SSI2) {
+ if (prtd->asrc_index == 0)
+ transfer = MXC_DMA_ASRCA_SSI2_TX0;
+ else if (prtd->asrc_index == 1)
+ transfer = MXC_DMA_ASRCB_SSI2_TX0;
+ } else if (dai_port == IMX_DAI_SSI3) {
+ if (prtd->asrc_index == 0)
+ transfer = MXC_DMA_ASRCA_SSI2_TX1;
+ else if (prtd->asrc_index == 1)
+ transfer = MXC_DMA_ASRCB_SSI2_TX1;
+ } else if (dai_port & IMX_DAI_ESAI_TX) {
+ if (prtd->asrc_index == 0)
+ transfer = MXC_DMA_ASRCA_ESAI;
+ else if (prtd->asrc_index == 1)
+ transfer = MXC_DMA_ASRCB_ESAI;
+ else
+ transfer = MXC_DMA_ASRCC_ESAI;
+ }
+ } else {
+#endif
+
+ if (dai_port == IMX_DAI_SSI0) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI1_16BIT_TX0;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI1_24BIT_TX0;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI1_24BIT_TX0;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI1_16BIT_RX0;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI1_24BIT_RX0;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI1_24BIT_RX0;
+ }
+ } else if (dai_port == IMX_DAI_SSI1) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI1_16BIT_TX1;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI1_24BIT_TX1;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI1_24BIT_TX1;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI1_16BIT_RX1;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI1_24BIT_RX1;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI1_24BIT_RX1;
+ }
+ } else if (dai_port == IMX_DAI_SSI2) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI2_16BIT_TX0;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI2_24BIT_TX0;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI2_24BIT_TX0;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI2_16BIT_RX0;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI2_24BIT_RX0;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI2_24BIT_RX0;
+ }
+ } else if (dai_port == IMX_DAI_SSI3) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI2_16BIT_TX1;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI2_24BIT_TX1;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI2_24BIT_TX1;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_SSI2_16BIT_RX1;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_SSI2_24BIT_RX1;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_SSI2_24BIT_RX1;
+ }
+ } else if ((dai_port & IMX_DAI_ESAI_TX)
+ || (dai_port & IMX_DAI_ESAI_RX)) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_ESAI_16BIT_TX;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_ESAI_24BIT_TX;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_ESAI_24BIT_TX;
+ } else {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ transfer = MXC_DMA_ESAI_16BIT_RX;
+ else if (format == SNDRV_PCM_FORMAT_S24_LE)
+ transfer = MXC_DMA_ESAI_24BIT_RX;
+ else if (format == SNDRV_PCM_FORMAT_S20_3LE)
+ transfer = MXC_DMA_ESAI_24BIT_RX;
+ }
+ }
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ }
+#endif
+ return transfer;
+}
+
+static int dma_new_period(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+ unsigned int dma_size = frames_to_bytes(runtime, runtime->period_size);
+ unsigned int offset = dma_size * prtd->period;
+ int ret = 0;
+ mxc_dma_requestbuf_t sdma_request;
+
+ if (!prtd->active)
+ return 0;
+
+ memset(&sdma_request, 0, sizeof(mxc_dma_requestbuf_t));
+
+ dbg("period pos ALSA %x DMA %x\n", runtime->periods, prtd->period);
+ dbg("period size ALSA %x DMA %x Offset %x dmasize %x\n",
+ (unsigned int)runtime->period_size,
+ runtime->dma_bytes, offset, dma_size);
+ dbg("DMA addr %x\n", runtime->dma_addr + offset);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sdma_request.src_addr =
+ (dma_addr_t) (runtime->dma_addr + offset);
+ else
+ sdma_request.dst_addr =
+ (dma_addr_t) (runtime->dma_addr + offset);
+
+ sdma_request.num_of_bytes = dma_size;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mxc_dma_config(prtd->dma_wchannel,
+ &sdma_request, 1, MXC_DMA_MODE_WRITE);
+ ret = mxc_dma_enable(prtd->dma_wchannel);
+ } else {
+
+ mxc_dma_config(prtd->dma_wchannel,
+ &sdma_request, 1, MXC_DMA_MODE_READ);
+ ret = mxc_dma_enable(prtd->dma_wchannel);
+ }
+ prtd->dma_active = 1;
+ prtd->period++;
+ prtd->period %= runtime->periods;
+
+ return ret;
+}
+
+static void audio_dma_irq(void *data)
+{
+ struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+
+ prtd->dma_active = 0;
+ prtd->periods++;
+ prtd->periods %= runtime->periods;
+
+ dbg("irq per %d offset %x\n", prtd->periods,
+ frames_to_bytes(runtime, runtime->period_size) * prtd->periods);
+
+ if (prtd->active)
+ snd_pcm_period_elapsed(substream);
+ dma_new_period(substream);
+}
+
+static int imx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+ int ret = 0, channel = 0;
+
+ if (prtd->dma_alloc) {
+ mxc_dma_free(prtd->dma_wchannel);
+ prtd->dma_alloc = 0;
+ }
+
+ /* only allocate the DMA chn once */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (prtd->asrc_enable == 1) {
+ struct dma_channel_info info;
+ mxc_dma_requestbuf_t sdma_request;
+ info.asrc.channs = runtime->channels;
+ if (prtd->dma_asrc) {
+ mxc_dma_free(prtd->dma_asrc);
+ prtd->dma_asrc = 0;
+ }
+ memset(&sdma_request, 0, sizeof(mxc_dma_requestbuf_t));
+ /* num_of_bytes can be set any value except for zero */
+ sdma_request.num_of_bytes = 0x40;
+ channel =
+ mxc_dma_request_ext(prtd->dma_ch,
+ "ALSA TX SDMA", &info);
+
+ mxc_dma_config(channel, &sdma_request,
+ 1, MXC_DMA_MODE_WRITE);
+ prtd->dma_asrc = channel;
+ if (prtd->asrc_index == 0)
+ prtd->dma_ch = MXC_DMA_ASRC_A_RX;
+ else if (prtd->asrc_index == 1)
+ prtd->dma_ch = MXC_DMA_ASRC_B_RX;
+ else
+ prtd->dma_ch = MXC_DMA_ASRC_C_RX;
+
+ channel =
+ mxc_dma_request(MXC_DMA_ASRC_A_RX, "ALSA ASRC RX");
+ } else
+ channel = mxc_dma_request(prtd->dma_ch, "ALSA TX SDMA");
+#else
+ channel = mxc_dma_request(prtd->dma_ch, "ALSA TX SDMA");
+#endif
+ if (channel < 0) {
+ pr_err("imx-pcm: error requesting \
+ a write dma channel\n");
+ return channel;
+ }
+ ret = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+ audio_dma_irq, (void *)substream);
+
+ } else {
+ channel = mxc_dma_request(prtd->dma_ch, "ALSA RX SDMA");
+ if (channel < 0) {
+ pr_err("imx-pcm: error requesting \
+ a read dma channel\n");
+ return channel;
+ }
+ ret = mxc_dma_callback_set(channel, (mxc_dma_callback_t)
+ audio_dma_irq, (void *)substream);
+ }
+ prtd->dma_wchannel = channel;
+ prtd->dma_alloc = 1;
+
+ prtd->period = 0;
+ prtd->periods = 0;
+ return 0;
+}
+
+static int imx_pcm_hw_params(struct snd_pcm_substream
+ *substream, struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ prtd->dma_ch =
+ imx_get_sdma_transfer(params_format(params),
+ rtd->dai->cpu_dai->id, substream);
+
+ if (prtd->dma_ch < 0) {
+ printk(KERN_ERR "imx-pcm: invaild sdma transfer type");
+ return -1;
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int imx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+
+ if (prtd->dma_wchannel) {
+ mxc_dma_free(prtd->dma_wchannel);
+ prtd->dma_wchannel = 0;
+ prtd->dma_alloc = 0;
+ }
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if ((prtd->asrc_enable == 1) && prtd->dma_asrc) {
+ mxc_dma_free(prtd->dma_asrc);
+ prtd->dma_asrc = 0;
+ }
+#endif
+
+ return 0;
+}
+
+static int imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct mxc_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ prtd->dma_active = 0;
+ prtd->active = 1;
+ ret = dma_new_period(substream);
+ ret = dma_new_period(substream);
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (prtd->asrc_enable == 1) {
+ ret = mxc_dma_enable(prtd->dma_asrc);
+ asrc_start_conv(prtd->asrc_index);
+ /* There is underrun, if immediately enable SSI after
+ start ASRC */
+ mdelay(1);
+ }
+#endif
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ prtd->active = 0;
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ if (prtd->asrc_enable == 1) {
+ mxc_dma_disable(prtd->dma_asrc);
+ asrc_stop_conv(prtd->asrc_index);
+ }
+#endif
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t imx_pcm_pointer(struct
+ snd_pcm_substream
+ *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+ unsigned int offset = 0;
+
+ offset = (runtime->period_size * (prtd->periods));
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+ dbg("pointer offset %x\n", offset);
+
+ return offset;
+}
+
+static int imx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd;
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &imx_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ prtd = kzalloc(sizeof(struct mxc_runtime_data), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ runtime->private_data = prtd;
+ return 0;
+}
+
+static int imx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mxc_runtime_data *prtd = runtime->private_data;
+
+ kfree(prtd);
+ return 0;
+}
+
+static int
+imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai *cpu_dai = socdev->machine->dai_link->cpu_dai;
+ struct mxc_audio_platform_data *dev_data = cpu_dai->private_data;
+ int ext_ram = 0;
+ int ret = 0;
+
+ dbg("+imx_pcm_mmap:"
+ "UseIram=%d dma_addr=%x dma_area=%x dma_bytes=%d\n",
+ UseIram, (unsigned int)runtime->dma_addr,
+ runtime->dma_area, runtime->dma_bytes);
+
+ if (dev_data)
+ ext_ram = dev_data->ext_ram;
+
+ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ || ext_ram || !UseIram) {
+ ret =
+ dma_mmap_writecombine(substream->pcm->card->
+ dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+ return ret;
+ } else
+ return imx_iram_audio_playback_mmap(substream, vma);
+}
+
+struct snd_pcm_ops imx_pcm_ops = {
+ .open = imx_pcm_open,
+ .close = imx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = imx_pcm_hw_params,
+ .hw_free = imx_pcm_hw_free,
+ .prepare = imx_pcm_prepare,
+ .trigger = imx_pcm_trigger,
+ .pointer = imx_pcm_pointer,
+ .mmap = imx_pcm_mmap,
+};
+
+static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai *cpu_dai = socdev->machine->dai_link->cpu_dai;
+ struct mxc_audio_platform_data *dev_data = cpu_dai->private_data;
+ int ext_ram = 0;
+ size_t size = imx_pcm_hardware.buffer_bytes_max;
+
+ if (dev_data)
+ ext_ram = dev_data->ext_ram;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+
+ if ((stream == SNDRV_PCM_STREAM_CAPTURE) || ext_ram || !UseIram)
+ buf->area =
+ dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ else
+ buf->area = imx_iram_init(&buf->addr, size);
+
+ if (!buf->area)
+ return -ENOMEM;
+ buf->bytes = size;
+ printk(KERN_INFO "DMA Sound Buffers Allocated:"
+ "UseIram=%d buf->addr=%x buf->area=%p size=%d\n",
+ UseIram, buf->addr, buf->area, size);
+ return 0;
+}
+
+static void imx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_dai *cpu_dai = socdev->machine->dai_link->cpu_dai;
+ struct mxc_audio_platform_data *dev_data = cpu_dai->private_data;
+ int ext_ram = 0;
+ int stream;
+
+ if (dev_data)
+ ext_ram = dev_data->ext_ram;
+
+ for (stream = 0; stream < 2; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ if ((stream == SNDRV_PCM_STREAM_CAPTURE)
+ || ext_ram || !UseIram)
+ dma_free_writecombine(pcm->card->dev,
+ buf->bytes, buf->area, buf->addr);
+ else
+ imx_iram_free();
+ buf->area = NULL;
+ }
+}
+
+static u64 imx_pcm_dmamask = 0xffffffff;
+
+static int imx_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &imx_pcm_dmamask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = 0xffffffff;
+
+ if (dai->playback.channels_min) {
+ ret = imx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ goto out;
+ }
+
+ if (dai->capture.channels_min) {
+ ret = imx_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret)
+ goto out;
+ }
+ out:
+ return ret;
+}
+
+struct snd_soc_platform imx_soc_platform = {
+ .name = "imx-audio",
+ .pcm_ops = &imx_pcm_ops,
+ .pcm_new = imx_pcm_new,
+ .pcm_free = imx_pcm_free_dma_buffers,
+};
+
+EXPORT_SYMBOL_GPL(imx_soc_platform);
+
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_DESCRIPTION("Freescale i.MX3x PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-pcm.h b/sound/soc/imx/imx-pcm.h
new file mode 100644
index 000000000000..c22a42b70364
--- /dev/null
+++ b/sound/soc/imx/imx-pcm.h
@@ -0,0 +1,83 @@
+/*
+ * imx-pcm.h :- ASoC platform header for Freescale i.MX
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Copyright 2006, 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MXC_PCM_H
+#define _MXC_PCM_H
+
+#include <mach/dma.h>
+
+/* AUDMUX regs definition */
+#define AUDMUX_IO_BASE_ADDR IO_ADDRESS(AUDMUX_BASE_ADDR)
+
+#define DAM_PTCR1 ((AUDMUX_IO_BASE_ADDR) + 0x00)
+#define DAM_PDCR1 ((AUDMUX_IO_BASE_ADDR) + 0x04)
+#define DAM_PTCR2 ((AUDMUX_IO_BASE_ADDR) + 0x08)
+#define DAM_PDCR2 ((AUDMUX_IO_BASE_ADDR) + 0x0C)
+#define DAM_PTCR3 ((AUDMUX_IO_BASE_ADDR) + 0x10)
+#define DAM_PDCR3 ((AUDMUX_IO_BASE_ADDR) + 0x14)
+#define DAM_PTCR4 ((AUDMUX_IO_BASE_ADDR) + 0x18)
+#define DAM_PDCR4 ((AUDMUX_IO_BASE_ADDR) + 0x1C)
+#define DAM_PTCR5 ((AUDMUX_IO_BASE_ADDR) + 0x20)
+#define DAM_PDCR5 ((AUDMUX_IO_BASE_ADDR) + 0x24)
+#define DAM_PTCR6 ((AUDMUX_IO_BASE_ADDR) + 0x28)
+#define DAM_PDCR6 ((AUDMUX_IO_BASE_ADDR) + 0x2C)
+#define DAM_PTCR7 ((AUDMUX_IO_BASE_ADDR) + 0x30)
+#define DAM_PDCR7 ((AUDMUX_IO_BASE_ADDR) + 0x34)
+#define DAM_CNMCR ((AUDMUX_IO_BASE_ADDR) + 0x38)
+#define DAM_PTCR(a) ((AUDMUX_IO_BASE_ADDR) + (a-1)*8)
+#define DAM_PDCR(a) ((AUDMUX_IO_BASE_ADDR) + 4 + (a-1)*8)
+
+#define AUDMUX_PTCR_TFSDIR (1 << 31)
+#define AUDMUX_PTCR_TFSSEL(x, y) \
+ ((x << 30) | (((y - 1) & 0x7) << 27))
+#define AUDMUX_PTCR_TCLKDIR (1 << 26)
+#define AUDMUX_PTCR_TCSEL(x, y) \
+ ((x << 25) | (((y - 1) & 0x7) << 22))
+#define AUDMUX_PTCR_RFSDIR (1 << 21)
+#define AUDMUX_PTCR_RFSSEL(x, y) \
+ ((x << 20) | (((y - 1) & 0x7) << 17))
+#define AUDMUX_PTCR_RCLKDIR (1 << 16)
+#define AUDMUX_PTCR_RCSEL(x, y) \
+ ((x << 15) | (((y - 1) & 0x7) << 12))
+#define AUDMUX_PTCR_SYN (1 << 11)
+
+#define AUDMUX_FROM_TXFS 0
+#define AUDMUX_FROM_RXFS 1
+
+#define AUDMUX_PDCR_RXDSEL(x) (((x - 1) & 0x7) << 13)
+#define AUDMUX_PDCR_TXDXEN (1 << 12)
+#define AUDMUX_PDCR_MODE(x) (((x) & 0x3) << 8)
+#define AUDMUX_PDCR_INNMASK(x) (((x) & 0xff) << 0)
+
+#define AUDMUX_CNMCR_CEN (1 << 18)
+#define AUDMUX_CNMCR_FSPOL (1 << 17)
+#define AUDMUX_CNMCR_CLKPOL (1 << 16)
+#define AUDMUX_CNMCR_CNTHI(x) (((x) & 0xff) << 8)
+#define AUDMUX_CNMCR_CNTLOW(x) (((x) & 0xff) << 0)
+
+
+struct mxc_runtime_data {
+ int dma_ch;
+ spinlock_t dma_lock;
+ int active, period, periods;
+ int dma_wchannel;
+ int dma_active;
+ int dma_alloc;
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_MXC_ASRC_MODULE)
+ int dma_asrc;
+ int asrc_index;
+ int asrc_enable;
+#endif
+};
+
+extern struct snd_soc_platform imx_soc_platform;
+
+#endif
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c
new file mode 100644
index 000000000000..5845dd41046c
--- /dev/null
+++ b/sound/soc/imx/imx-ssi.c
@@ -0,0 +1,793 @@
+/*
+ * imx-ssi.c -- SSI driver for Freescale IMX
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * Based on mxc-alsa-mc13783 (C) 2006-2009 Freescale Semiconductor, 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.
+ *
+ * Revision history
+ * 29th Aug 2006 Initial version.
+ *
+ * TODO:
+ * Need to rework SSI register defs when new defs go into mainline.
+ * Add support for TDM and FIFO 1.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/dma.h>
+#include <mach/clock.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+
+#include "imx-ssi.h"
+#include "imx-pcm.h"
+
+/* debug */
+#define IMX_SSI_DEBUG 0
+#if IMX_SSI_DEBUG
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+
+#define IMX_SSI_DUMP 0
+#if IMX_SSI_DUMP
+#define SSI_DUMP() \
+ do { \
+ printk(KERN_INFO "dump @ %s\n", __func__); \
+ printk(KERN_INFO "scr %x\t, %x\n", \
+ __raw_readl(SSI1_SCR), __raw_readl(SSI2_SCR)); \
+ printk(KERN_INFO "sisr %x\t, %x\n", \
+ __raw_readl(SSI1_SISR), __raw_readl(SSI2_SISR)); \
+ printk(KERN_INFO "stcr %x\t, %x\n", \
+ __raw_readl(SSI1_STCR), __raw_readl(SSI2_STCR)); \
+ printk(KERN_INFO "srcr %x\t, %x\n", \
+ __raw_readl(SSI1_SRCR), __raw_readl(SSI2_SRCR)); \
+ printk(KERN_INFO "stccr %x\t, %x\n", \
+ __raw_readl(SSI1_STCCR), __raw_readl(SSI2_STCCR)); \
+ printk(KERN_INFO "srccr %x\t, %x\n", \
+ __raw_readl(SSI1_SRCCR), __raw_readl(SSI2_SRCCR)); \
+ printk(KERN_INFO "sfcsr %x\t, %x\n", \
+ __raw_readl(SSI1_SFCSR), __raw_readl(SSI2_SFCSR)); \
+ printk(KERN_INFO "stmsk %x\t, %x\n", \
+ __raw_readl(SSI1_STMSK), __raw_readl(SSI2_STMSK)); \
+ printk(KERN_INFO "srmsk %x\t, %x\n", \
+ __raw_readl(SSI1_SRMSK), __raw_readl(SSI2_SRMSK)); \
+ printk(KERN_INFO "sier %x\t, %x\n", \
+ __raw_readl(SSI1_SIER), __raw_readl(SSI2_SIER)); \
+ } while (0);
+#else
+#define SSI_DUMP()
+#endif
+
+#define SSI1_PORT 0
+#define SSI2_PORT 1
+
+static int ssi_active[2] = { 0, 0 };
+
+/*
+ * SSI system clock configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ u32 scr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1)
+ scr = __raw_readl(SSI1_SCR);
+ else
+ scr = __raw_readl(SSI2_SCR);
+
+ if (scr & SSI_SCR_SSIEN)
+ return 0;
+
+ switch (clk_id) {
+ case IMX_SSP_SYS_CLK:
+ if (dir == SND_SOC_CLOCK_OUT)
+ scr |= SSI_SCR_SYS_CLK_EN;
+ else
+ scr &= ~SSI_SCR_SYS_CLK_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1)
+ __raw_writel(scr, SSI1_SCR);
+ else
+ __raw_writel(scr, SSI2_SCR);
+
+ return 0;
+}
+
+/*
+ * SSI Clock dividers
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ u32 stccr, srccr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ if (__raw_readl(SSI1_SCR) & SSI_SCR_SSIEN)
+ return 0;
+
+ srccr = __raw_readl(SSI1_SRCCR);
+ stccr = __raw_readl(SSI1_STCCR);
+ } else {
+ if (__raw_readl(SSI2_SCR) & SSI_SCR_SSIEN)
+ return 0;
+
+ srccr = __raw_readl(SSI2_SRCCR);
+ stccr = __raw_readl(SSI2_STCCR);
+ }
+
+ switch (div_id) {
+ case IMX_SSI_TX_DIV_2:
+ stccr &= ~SSI_STCCR_DIV2;
+ stccr |= div;
+ break;
+ case IMX_SSI_TX_DIV_PSR:
+ stccr &= ~SSI_STCCR_PSR;
+ stccr |= div;
+ break;
+ case IMX_SSI_TX_DIV_PM:
+ stccr &= ~0xff;
+ stccr |= SSI_STCCR_PM(div);
+ break;
+ case IMX_SSI_RX_DIV_2:
+ stccr &= ~SSI_STCCR_DIV2;
+ stccr |= div;
+ break;
+ case IMX_SSI_RX_DIV_PSR:
+ stccr &= ~SSI_STCCR_PSR;
+ stccr |= div;
+ break;
+ case IMX_SSI_RX_DIV_PM:
+ stccr &= ~0xff;
+ stccr |= SSI_STCCR_PM(div);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ __raw_writel(stccr, SSI1_STCCR);
+ __raw_writel(srccr, SSI1_SRCCR);
+ } else {
+ __raw_writel(stccr, SSI2_STCCR);
+ __raw_writel(srccr, SSI2_SRCCR);
+ }
+ return 0;
+}
+
+/*
+ * SSI Network Mode or TDM slots configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int mask, int slots)
+{
+ u32 stmsk, srmsk, stccr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ if (__raw_readl(SSI1_SCR) & SSI_SCR_SSIEN)
+ return 0;
+ stccr = __raw_readl(SSI1_STCCR);
+ } else {
+ if (__raw_readl(SSI2_SCR) & SSI_SCR_SSIEN)
+ return 0;
+ stccr = __raw_readl(SSI2_STCCR);
+ }
+
+ stmsk = srmsk = mask;
+ stccr &= ~SSI_STCCR_DC_MASK;
+ stccr |= SSI_STCCR_DC(slots - 1);
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ __raw_writel(stmsk, SSI1_STMSK);
+ __raw_writel(srmsk, SSI1_SRMSK);
+ __raw_writel(stccr, SSI1_STCCR);
+ __raw_writel(stccr, SSI1_SRCCR);
+ } else {
+ __raw_writel(stmsk, SSI2_STMSK);
+ __raw_writel(srmsk, SSI2_SRMSK);
+ __raw_writel(stccr, SSI2_STCCR);
+ __raw_writel(stccr, SSI2_SRCCR);
+ }
+
+ return 0;
+}
+
+/*
+ * SSI DAI format configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ * Note: We don't use the I2S modes but instead manually configure the
+ * SSI for I2S.
+ */
+static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ u32 stcr = 0, srcr = 0, scr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1)
+ scr = __raw_readl(SSI1_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET);
+ else
+ scr = __raw_readl(SSI2_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET);
+
+ if (scr & SSI_SCR_SSIEN)
+ return 0;
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* data on rising edge of bclk, frame low 1clk before data */
+ stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
+ srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* data on rising edge of bclk, frame high with data */
+ stcr |= SSI_STCR_TXBIT0;
+ srcr |= SSI_SRCR_RXBIT0;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /* data on rising edge of bclk, frame high with data */
+ stcr |= SSI_STCR_TFSL;
+ srcr |= SSI_SRCR_RFSL;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* data on rising edge of bclk, frame high 1clk before data */
+ stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS;
+ srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS;
+ break;
+ }
+
+ /* DAI clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
+ srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ stcr |= SSI_STCR_TFSI;
+ stcr &= ~SSI_STCR_TSCKP;
+ srcr |= SSI_SRCR_RFSI;
+ srcr &= ~SSI_SRCR_RSCKP;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ stcr &= ~SSI_STCR_TFSI;
+ stcr |= SSI_STCR_TSCKP;
+ srcr &= ~SSI_SRCR_RFSI;
+ srcr |= SSI_SRCR_RSCKP;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
+ srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP;
+ break;
+ }
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
+ if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+ && (fmt & SND_SOC_DAIFMT_TDM)) {
+ scr &= ~SSI_SCR_I2S_MODE_MASK;
+ scr |= SSI_SCR_I2S_MODE_MSTR;
+ }
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ stcr |= SSI_STCR_TFDIR;
+ srcr |= SSI_SRCR_RFDIR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ stcr |= SSI_STCR_TXDIR;
+ srcr |= SSI_SRCR_RXDIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+ && (fmt & SND_SOC_DAIFMT_TDM)) {
+ scr &= ~SSI_SCR_I2S_MODE_MASK;
+ scr |= SSI_SCR_I2S_MODE_SLAVE;
+ }
+ break;
+ }
+
+ /* sync */
+ if (!(fmt & SND_SOC_DAIFMT_ASYNC))
+ scr |= SSI_SCR_SYN;
+
+ /* tdm - only for stereo atm */
+ if (fmt & SND_SOC_DAIFMT_TDM)
+ scr |= SSI_SCR_NET;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ __raw_writel(stcr, SSI1_STCR);
+ __raw_writel(srcr, SSI1_SRCR);
+ __raw_writel(scr, SSI1_SCR);
+ } else {
+ __raw_writel(stcr, SSI2_STCR);
+ __raw_writel(srcr, SSI2_SRCR);
+ __raw_writel(scr, SSI2_SCR);
+ }
+ SSI_DUMP();
+ return 0;
+}
+
+static struct clk *ssi1_clk;
+static struct clk *ssi2_clk;
+
+static int imx_ssi_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ /* we cant really change any SSI values after SSI is enabled
+ * need to fix in software for max flexibility - lrg */
+ if (cpu_dai->playback.active || cpu_dai->capture.active)
+ return 0;
+
+ /* reset the SSI port - Sect 45.4.4 */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+
+ if (ssi_active[SSI1_PORT]++)
+ return 0;
+
+ __raw_writel(0, SSI1_SCR);
+ ssi1_clk = clk_get(NULL, "ssi_clk.0");
+ clk_enable(ssi1_clk);
+
+ /* BIG FAT WARNING
+ * SDMA FIFO watermark must == SSI FIFO watermark for
+ * best results.
+ */
+ __raw_writel((SSI_SFCSR_RFWM1(SSI_RXFIFO_WATERMARK) |
+ SSI_SFCSR_RFWM0(SSI_RXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM1(SSI_TXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM0(SSI_TXFIFO_WATERMARK)),
+ SSI1_SFCSR);
+ __raw_writel(0, SSI1_SIER);
+ } else {
+
+ if (ssi_active[SSI2_PORT]++)
+ return 0;
+
+ __raw_writel(0, SSI2_SCR);
+ ssi2_clk = clk_get(NULL, "ssi_clk.1");
+ clk_enable(ssi2_clk);
+ /* above warning applies here too */
+ __raw_writel((SSI_SFCSR_RFWM1(SSI_RXFIFO_WATERMARK) |
+ SSI_SFCSR_RFWM0(SSI_RXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM1(SSI_TXFIFO_WATERMARK) |
+ SSI_SFCSR_TFWM0(SSI_TXFIFO_WATERMARK)),
+ SSI2_SFCSR);
+ __raw_writel(0, SSI2_SIER);
+ }
+
+ SSI_DUMP();
+ return 0;
+}
+
+static int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 stccr, stcr, sier;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ stccr = __raw_readl(SSI1_STCCR) & ~SSI_STCCR_WL_MASK;
+ stcr = __raw_readl(SSI1_STCR);
+ sier = __raw_readl(SSI1_SIER);
+ } else {
+ stccr = __raw_readl(SSI2_STCCR) & ~SSI_STCCR_WL_MASK;
+ stcr = __raw_readl(SSI2_STCR);
+ sier = __raw_readl(SSI2_SIER);
+ }
+
+ /* DAI data (word) size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ stccr |= SSI_STCCR_WL(16);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ stccr |= SSI_STCCR_WL(20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ stccr |= SSI_STCCR_WL(24);
+ break;
+ }
+
+ /* enable interrupts */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ stcr |= SSI_STCR_TFEN0;
+ else
+ stcr |= SSI_STCR_TFEN1;
+ sier |= SSI_SIER_TDMAE | SSI_SIER_TIE | SSI_SIER_TUE0_EN;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ __raw_writel(stcr, SSI1_STCR);
+ __raw_writel(stccr, SSI1_STCCR);
+ __raw_writel(sier, SSI1_SIER);
+ } else {
+ __raw_writel(stcr, SSI2_STCR);
+ __raw_writel(stccr, SSI2_STCCR);
+ __raw_writel(sier, SSI2_SIER);
+ }
+
+ return 0;
+}
+
+static int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 srccr, srcr, sier;
+ bool sync_mode;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ sync_mode =
+ (__raw_readl(SSI1_SCR) & SSI_SCR_SYN) ? true : false;
+ srccr =
+ sync_mode ? __raw_readl(SSI1_STCCR) :
+ __raw_readl(SSI1_SRCCR);
+ srcr = __raw_readl(SSI1_SRCR);
+ sier = __raw_readl(SSI1_SIER);
+ } else {
+ sync_mode =
+ (__raw_readl(SSI2_SCR) & SSI_SCR_SYN) ? true : false;
+ srccr =
+ sync_mode ? __raw_readl(SSI2_STCCR) :
+ __raw_readl(SSI2_SRCCR);
+ srcr = __raw_readl(SSI2_SRCR);
+ sier = __raw_readl(SSI2_SIER);
+ }
+ srccr &= ~SSI_SRCCR_WL_MASK;
+
+ /* DAI data (word) size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ srccr |= SSI_SRCCR_WL(16);
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ srccr |= SSI_SRCCR_WL(20);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ srccr |= SSI_SRCCR_WL(24);
+ break;
+ }
+
+ /* enable interrupts */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+ srcr |= SSI_SRCR_RFEN0;
+ else
+ srcr |= SSI_SRCR_RFEN1;
+ sier |= SSI_SIER_RDMAE | SSI_SIER_RIE | SSI_SIER_ROE0_EN;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ __raw_writel(srcr, SSI1_SRCR);
+ if (sync_mode)
+ __raw_writel(srccr, SSI1_STCCR);
+ else
+ __raw_writel(srccr, SSI1_SRCCR);
+ __raw_writel(sier, SSI1_SIER);
+ } else {
+ __raw_writel(srcr, SSI2_SRCR);
+ if (sync_mode)
+ __raw_writel(srccr, SSI2_STCCR);
+ else
+ __raw_writel(srccr, SSI2_SRCCR);
+ __raw_writel(sier, SSI2_SIER);
+ }
+ return 0;
+}
+
+/*
+ * Should only be called when port is inactive (i.e. SSIEN = 0),
+ * although can be called multiple times by upper layers.
+ */
+static int imx_ssi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int id;
+
+ id = cpu_dai->id;
+
+ /* Tx/Rx config */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* cant change any parameters when SSI is running */
+ if (id == IMX_DAI_SSI0 || id == IMX_DAI_SSI1) {
+ if ((__raw_readl(SSI1_SCR) & SSI_SCR_SSIEN) &&
+ (__raw_readl(SSI1_SCR) & SSI_SCR_TE))
+ return 0;
+ } else {
+ if ((__raw_readl(SSI2_SCR) & SSI_SCR_SSIEN) &&
+ (__raw_readl(SSI2_SCR) & SSI_SCR_TE))
+ return 0;
+ }
+ return imx_ssi_hw_tx_params(substream, params);
+ } else {
+ /* cant change any parameters when SSI is running */
+ if (id == IMX_DAI_SSI0 || id == IMX_DAI_SSI1) {
+ if ((__raw_readl(SSI1_SCR) & SSI_SCR_SSIEN) &&
+ (__raw_readl(SSI1_SCR) & SSI_SCR_RE))
+ return 0;
+ } else {
+ if ((__raw_readl(SSI2_SCR) & SSI_SCR_SSIEN) &&
+ (__raw_readl(SSI2_SCR) & SSI_SCR_RE))
+ return 0;
+ }
+ return imx_ssi_hw_rx_params(substream, params);
+ }
+}
+
+static int imx_ssi_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 scr;
+
+ /* enable the SSI port, note that no other port config
+ * should happen after SSIEN is set */
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1) {
+ scr = __raw_readl(SSI1_SCR);
+ __raw_writel((scr | SSI_SCR_SSIEN), SSI1_SCR);
+ } else {
+ scr = __raw_readl(SSI2_SCR);
+ __raw_writel((scr | SSI_SCR_SSIEN), SSI2_SCR);
+ }
+ SSI_DUMP();
+ return 0;
+}
+
+static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ u32 scr;
+
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1)
+ scr = __raw_readl(SSI1_SCR);
+ else
+ scr = __raw_readl(SSI2_SCR);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (scr & SSI_SCR_RE) {
+ if (cpu_dai->id == IMX_DAI_SSI0
+ || cpu_dai->id == IMX_DAI_SSI1)
+ __raw_writel(0, SSI1_SCR);
+ else
+ __raw_writel(0, SSI2_SCR);
+ }
+ scr |= SSI_SCR_TE;
+ } else
+ scr |= SSI_SCR_RE;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ scr &= ~SSI_SCR_TE;
+ else
+ scr &= ~SSI_SCR_RE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI1)
+ __raw_writel(scr, SSI1_SCR);
+ else
+ __raw_writel(scr, SSI2_SCR);
+
+ SSI_DUMP();
+ return 0;
+}
+
+static void imx_ssi_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int id;
+
+ id = cpu_dai->id;
+
+ /* shutdown SSI if neither Tx or Rx is active */
+ if (cpu_dai->playback.active || cpu_dai->capture.active)
+ return;
+
+ if (id == IMX_DAI_SSI0 || id == IMX_DAI_SSI1) {
+
+ if (--ssi_active[SSI1_PORT] > 1)
+ return;
+
+ __raw_writel(0, SSI1_SCR);
+
+ clk_disable(ssi1_clk);
+ clk_put(ssi1_clk);
+
+ } else {
+ if (--ssi_active[SSI2_PORT])
+ return;
+ __raw_writel(0, SSI2_SCR);
+ clk_disable(ssi2_clk);
+ clk_put(ssi2_clk);
+ }
+}
+
+#ifdef CONFIG_PM
+static int imx_ssi_suspend(struct platform_device *dev, struct snd_soc_dai *dai)
+{
+ if (!dai->active)
+ return 0;
+
+ /* do we need to disable any clocks? */
+
+ return 0;
+}
+
+static int imx_ssi_resume(struct platform_device *dev, struct snd_soc_dai *dai)
+{
+ if (!dai->active)
+ return 0;
+
+ /* do we need to enable any clocks? */
+
+ return 0;
+}
+#else
+#define imx_ssi_suspend NULL
+#define imx_ssi_resume NULL
+#endif
+
+static int fifo_err_counter;
+
+static irqreturn_t ssi1_irq(int irq, void *dev_id)
+{
+ if (fifo_err_counter++ % 1000 == 0)
+ printk(KERN_ERR "ssi1_irq SISR %x SIER %x fifo_errs=%d\n",
+ __raw_readl(SSI1_SISR), __raw_readl(SSI1_SIER),
+ fifo_err_counter);
+ __raw_writel((SSI_SIER_TUE0_EN | SSI_SIER_ROE0_EN), SSI1_SISR);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ssi2_irq(int irq, void *dev_id)
+{
+ if (fifo_err_counter++ % 1000 == 0)
+ printk(KERN_ERR "ssi2_irq SISR %x SIER %x fifo_errs=%d\n",
+ __raw_readl(SSI2_SISR), __raw_readl(SSI2_SIER),
+ fifo_err_counter);
+ __raw_writel((SSI_SIER_TUE0_EN | SSI_SIER_ROE0_EN), SSI2_SISR);
+ return IRQ_HANDLED;
+}
+
+static int imx_ssi_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+ if (!strcmp(dai->name, "imx-ssi-1"))
+ dai->id = IMX_DAI_SSI0;
+ else if (!strcmp(dai->name, "imx-ssi-2"))
+ dai->id = IMX_DAI_SSI1;
+ else if (!strcmp(dai->name, "imx-ssi-3"))
+ dai->id = IMX_DAI_SSI2;
+ else if (!strcmp(dai->name, "imx-ssi-4"))
+ dai->id = IMX_DAI_SSI3;
+ else {
+ printk(KERN_ERR "%s: invalid device %s\n", __func__, dai->name);
+ return -ENODEV;
+ }
+
+ if ((!strcmp(dai->name, "imx-ssi-1")) ||
+ (!strcmp(dai->name, "imx-ssi-2")))
+ if (request_irq(MXC_INT_SSI1, ssi1_irq, 0, "ssi1", dai)) {
+ printk(KERN_ERR "%s: failure requesting irq %s\n",
+ __func__, "ssi1");
+ return -EBUSY;
+ }
+
+ if ((!strcmp(dai->name, "imx-ssi-3")) ||
+ (!strcmp(dai->name, "imx-ssi-4")))
+ if (request_irq(MXC_INT_SSI2, ssi2_irq, 0, "ssi2", dai)) {
+ printk(KERN_ERR "%s: failure requesting irq %s\n",
+ __func__, "ssi2");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void imx_ssi_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ if ((!strcmp(dai->name, "imx-ssi-1")) ||
+ (!strcmp(dai->name, "imx-ssi-2")))
+ free_irq(MXC_INT_SSI1, dai);
+
+ if ((!strcmp(dai->name, "imx-ssi-3")) ||
+ (!strcmp(dai->name, "imx-ssi-4")))
+ free_irq(MXC_INT_SSI2, dai);
+}
+
+#define IMX_SSI_RATES \
+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+
+#define IMX_SSI_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_pcm_stream imx_ssi_playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = IMX_SSI_RATES,
+ .formats = IMX_SSI_FORMATS,
+};
+
+static struct snd_soc_pcm_stream imx_ssi_capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = IMX_SSI_RATES,
+ .formats = IMX_SSI_FORMATS,
+};
+
+static struct snd_soc_ops imx_ssi_ops = {
+ .startup = imx_ssi_startup,
+ .shutdown = imx_ssi_shutdown,
+ .trigger = imx_ssi_trigger,
+ .prepare = imx_ssi_prepare,
+ .hw_params = imx_ssi_hw_params,
+};
+
+static struct snd_soc_dai_ops imx_ssi_dai_ops = {
+ .set_sysclk = imx_ssi_set_dai_sysclk,
+ .set_clkdiv = imx_ssi_set_dai_clkdiv,
+ .set_fmt = imx_ssi_set_dai_fmt,
+ .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
+};
+
+void imx_ssi_dai_init(struct snd_soc_dai *imx_ssi_dai)
+{
+ imx_ssi_dai->id = 0;
+ imx_ssi_dai->type = SND_SOC_DAI_PCM;
+ imx_ssi_dai->probe = imx_ssi_probe;
+ imx_ssi_dai->suspend = imx_ssi_suspend;
+ imx_ssi_dai->remove = imx_ssi_remove;
+ imx_ssi_dai->resume = imx_ssi_resume;
+ imx_ssi_dai->playback = imx_ssi_playback;
+ imx_ssi_dai->capture = imx_ssi_capture;
+ imx_ssi_dai->ops = imx_ssi_ops;
+ imx_ssi_dai->dai_ops = imx_ssi_dai_ops;
+};
+EXPORT_SYMBOL_GPL(imx_ssi_dai_init);
+
+MODULE_AUTHOR
+ ("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("i.MX ASoC I2S driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h
new file mode 100644
index 000000000000..593eb6ff5b70
--- /dev/null
+++ b/sound/soc/imx/imx-ssi.h
@@ -0,0 +1,218 @@
+/*
+ * 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 _IMX_SSI_H
+#define _IMX_SSI_H
+
+#include <mach/hardware.h>
+
+/* SSI regs definition */
+#define SSI1_IO_BASE_ADDR IO_ADDRESS(SSI1_BASE_ADDR)
+#define SSI2_IO_BASE_ADDR IO_ADDRESS(SSI2_BASE_ADDR)
+
+#define SSI1_STX0 ((SSI1_IO_BASE_ADDR) + 0x00)
+#define SSI1_STX1 ((SSI1_IO_BASE_ADDR) + 0x04)
+#define SSI1_SRX0 ((SSI1_IO_BASE_ADDR) + 0x08)
+#define SSI1_SRX1 ((SSI1_IO_BASE_ADDR) + 0x0c)
+#define SSI1_SCR ((SSI1_IO_BASE_ADDR) + 0x10)
+#define SSI1_SISR ((SSI1_IO_BASE_ADDR) + 0x14)
+#define SSI1_SIER ((SSI1_IO_BASE_ADDR) + 0x18)
+#define SSI1_STCR ((SSI1_IO_BASE_ADDR) + 0x1c)
+#define SSI1_SRCR ((SSI1_IO_BASE_ADDR) + 0x20)
+#define SSI1_STCCR ((SSI1_IO_BASE_ADDR) + 0x24)
+#define SSI1_SRCCR ((SSI1_IO_BASE_ADDR) + 0x28)
+#define SSI1_SFCSR ((SSI1_IO_BASE_ADDR) + 0x2c)
+#define SSI1_STR ((SSI1_IO_BASE_ADDR) + 0x30)
+#define SSI1_SOR ((SSI1_IO_BASE_ADDR) + 0x34)
+#define SSI1_SACNT ((SSI1_IO_BASE_ADDR) + 0x38)
+#define SSI1_SACADD ((SSI1_IO_BASE_ADDR) + 0x3c)
+#define SSI1_SACDAT ((SSI1_IO_BASE_ADDR) + 0x40)
+#define SSI1_SATAG ((SSI1_IO_BASE_ADDR) + 0x44)
+#define SSI1_STMSK ((SSI1_IO_BASE_ADDR) + 0x48)
+#define SSI1_SRMSK ((SSI1_IO_BASE_ADDR) + 0x4c)
+
+#define SSI2_STX0 ((SSI2_IO_BASE_ADDR) + 0x00)
+#define SSI2_STX1 ((SSI2_IO_BASE_ADDR) + 0x04)
+#define SSI2_SRX0 ((SSI2_IO_BASE_ADDR) + 0x08)
+#define SSI2_SRX1 ((SSI2_IO_BASE_ADDR) + 0x0c)
+#define SSI2_SCR ((SSI2_IO_BASE_ADDR) + 0x10)
+#define SSI2_SISR ((SSI2_IO_BASE_ADDR) + 0x14)
+#define SSI2_SIER ((SSI2_IO_BASE_ADDR) + 0x18)
+#define SSI2_STCR ((SSI2_IO_BASE_ADDR) + 0x1c)
+#define SSI2_SRCR ((SSI2_IO_BASE_ADDR) + 0x20)
+#define SSI2_STCCR ((SSI2_IO_BASE_ADDR) + 0x24)
+#define SSI2_SRCCR ((SSI2_IO_BASE_ADDR) + 0x28)
+#define SSI2_SFCSR ((SSI2_IO_BASE_ADDR) + 0x2c)
+#define SSI2_STR ((SSI2_IO_BASE_ADDR) + 0x30)
+#define SSI2_SOR ((SSI2_IO_BASE_ADDR) + 0x34)
+#define SSI2_SACNT ((SSI2_IO_BASE_ADDR) + 0x38)
+#define SSI2_SACADD ((SSI2_IO_BASE_ADDR) + 0x3c)
+#define SSI2_SACDAT ((SSI2_IO_BASE_ADDR) + 0x40)
+#define SSI2_SATAG ((SSI2_IO_BASE_ADDR) + 0x44)
+#define SSI2_STMSK ((SSI2_IO_BASE_ADDR) + 0x48)
+#define SSI2_SRMSK ((SSI2_IO_BASE_ADDR) + 0x4c)
+
+#define SSI_SCR_CLK_IST (1 << 9)
+#define SSI_SCR_TCH_EN (1 << 8)
+#define SSI_SCR_SYS_CLK_EN (1 << 7)
+#define SSI_SCR_I2S_MODE_NORM (0 << 5)
+#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
+#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
+#define SSI_SCR_SYN (1 << 4)
+#define SSI_SCR_NET (1 << 3)
+#define SSI_SCR_RE (1 << 2)
+#define SSI_SCR_TE (1 << 1)
+#define SSI_SCR_SSIEN (1 << 0)
+#define SSI_SCR_I2S_MODE_MASK (3 << 5)
+
+#define SSI_SISR_CMDAU (1 << 18)
+#define SSI_SISR_CMDDU (1 << 17)
+#define SSI_SISR_RXT (1 << 16)
+#define SSI_SISR_RDR1 (1 << 15)
+#define SSI_SISR_RDR0 (1 << 14)
+#define SSI_SISR_TDE1 (1 << 13)
+#define SSI_SISR_TDE0 (1 << 12)
+#define SSI_SISR_ROE1 (1 << 11)
+#define SSI_SISR_ROE0 (1 << 10)
+#define SSI_SISR_TUE1 (1 << 9)
+#define SSI_SISR_TUE0 (1 << 8)
+#define SSI_SISR_TFS (1 << 7)
+#define SSI_SISR_RFS (1 << 6)
+#define SSI_SISR_TLS (1 << 5)
+#define SSI_SISR_RLS (1 << 4)
+#define SSI_SISR_RFF1 (1 << 3)
+#define SSI_SISR_RFF0 (1 << 2)
+#define SSI_SISR_TFE1 (1 << 1)
+#define SSI_SISR_TFE0 (1 << 0)
+
+#define SSI_SIER_RDMAE (1 << 22)
+#define SSI_SIER_RIE (1 << 21)
+#define SSI_SIER_TDMAE (1 << 20)
+#define SSI_SIER_TIE (1 << 19)
+#define SSI_SIER_CMDAU_EN (1 << 18)
+#define SSI_SIER_CMDDU_EN (1 << 17)
+#define SSI_SIER_RXT_EN (1 << 16)
+#define SSI_SIER_RDR1_EN (1 << 15)
+#define SSI_SIER_RDR0_EN (1 << 14)
+#define SSI_SIER_TDE1_EN (1 << 13)
+#define SSI_SIER_TDE0_EN (1 << 12)
+#define SSI_SIER_ROE1_EN (1 << 11)
+#define SSI_SIER_ROE0_EN (1 << 10)
+#define SSI_SIER_TUE1_EN (1 << 9)
+#define SSI_SIER_TUE0_EN (1 << 8)
+#define SSI_SIER_TFS_EN (1 << 7)
+#define SSI_SIER_RFS_EN (1 << 6)
+#define SSI_SIER_TLS_EN (1 << 5)
+#define SSI_SIER_RLS_EN (1 << 4)
+#define SSI_SIER_RFF1_EN (1 << 3)
+#define SSI_SIER_RFF0_EN (1 << 2)
+#define SSI_SIER_TFE1_EN (1 << 1)
+#define SSI_SIER_TFE0_EN (1 << 0)
+
+#define SSI_STCR_TXBIT0 (1 << 9)
+#define SSI_STCR_TFEN1 (1 << 8)
+#define SSI_STCR_TFEN0 (1 << 7)
+#define SSI_STCR_TFDIR (1 << 6)
+#define SSI_STCR_TXDIR (1 << 5)
+#define SSI_STCR_TSHFD (1 << 4)
+#define SSI_STCR_TSCKP (1 << 3)
+#define SSI_STCR_TFSI (1 << 2)
+#define SSI_STCR_TFSL (1 << 1)
+#define SSI_STCR_TEFS (1 << 0)
+
+#define SSI_SRCR_RXBIT0 (1 << 9)
+#define SSI_SRCR_RFEN1 (1 << 8)
+#define SSI_SRCR_RFEN0 (1 << 7)
+#define SSI_SRCR_RFDIR (1 << 6)
+#define SSI_SRCR_RXDIR (1 << 5)
+#define SSI_SRCR_RSHFD (1 << 4)
+#define SSI_SRCR_RSCKP (1 << 3)
+#define SSI_SRCR_RFSI (1 << 2)
+#define SSI_SRCR_RFSL (1 << 1)
+#define SSI_SRCR_REFS (1 << 0)
+
+#define SSI_STCCR_DIV2 (1 << 18)
+#define SSI_STCCR_PSR (1 << 15)
+#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
+#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
+#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
+#define SSI_STCCR_WL_MASK (0xf << 13)
+#define SSI_STCCR_DC_MASK (0x1f << 8)
+#define SSI_STCCR_PM_MASK (0xff << 0)
+
+#define SSI_SRCCR_DIV2 (1 << 18)
+#define SSI_SRCCR_PSR (1 << 15)
+#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
+#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
+#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
+#define SSI_SRCCR_WL_MASK (0xf << 13)
+#define SSI_SRCCR_DC_MASK (0x1f << 8)
+#define SSI_SRCCR_PM_MASK (0xff << 0)
+
+
+#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
+#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
+#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
+#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
+#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
+#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
+#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
+#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
+
+#define SSI_STR_TEST (1 << 15)
+#define SSI_STR_RCK2TCK (1 << 14)
+#define SSI_STR_RFS2TFS (1 << 13)
+#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
+#define SSI_STR_TXD2RXD (1 << 7)
+#define SSI_STR_TCK2RCK (1 << 6)
+#define SSI_STR_TFS2RFS (1 << 5)
+#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
+
+#define SSI_SOR_CLKOFF (1 << 6)
+#define SSI_SOR_RX_CLR (1 << 5)
+#define SSI_SOR_TX_CLR (1 << 4)
+#define SSI_SOR_INIT (1 << 3)
+#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
+#define SSI_SOR_SYNRST (1 << 0)
+
+#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
+#define SSI_SACNT_WR (x << 4)
+#define SSI_SACNT_RD (x << 3)
+#define SSI_SACNT_TIF (x << 2)
+#define SSI_SACNT_FV (x << 1)
+#define SSI_SACNT_AC97EN (x << 0)
+
+/* SDMA & SSI watermarks for FIFO's */
+#define SDMA_TXFIFO_WATERMARK 0x4
+#define SDMA_RXFIFO_WATERMARK 0x6
+#define SSI_TXFIFO_WATERMARK 0x4
+#define SSI_RXFIFO_WATERMARK 0x6
+
+/* i.MX DAI SSP ID's */
+#define IMX_DAI_SSI0 0 /* SSI1 FIFO 0 */
+#define IMX_DAI_SSI1 1 /* SSI1 FIFO 1 */
+#define IMX_DAI_SSI2 2 /* SSI2 FIFO 0 */
+#define IMX_DAI_SSI3 3 /* SSI2 FIFO 1 */
+
+/* SSI clock sources */
+#define IMX_SSP_SYS_CLK 0
+
+/* SSI audio dividers */
+#define IMX_SSI_TX_DIV_2 0
+#define IMX_SSI_TX_DIV_PSR 1
+#define IMX_SSI_TX_DIV_PM 2
+#define IMX_SSI_RX_DIV_2 3
+#define IMX_SSI_RX_DIV_PSR 4
+#define IMX_SSI_RX_DIV_PM 5
+
+
+/* SSI Div 2 */
+#define IMX_SSI_DIV_2_OFF (~SSI_STCCR_DIV2)
+#define IMX_SSI_DIV_2_ON SSI_STCCR_DIV2
+
+extern void imx_ssi_dai_init(struct snd_soc_dai *imx_ssi_dai);
+
+#endif
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index b9f2353effeb..8a70e56f2b4e 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -26,6 +26,24 @@ config SND_S3C24XX_SOC_NEO1973_WM8753
Say Y if you want to add support for SoC audio on smdk2440
with the WM8753.
+config SND_S3C24XX_SOC_CC9M2443JS_WM8753
+ tristate "SoC I2S Audio support for Digi CC9M2443JS - WM8753"
+ depends on SND_S3C24XX_SOC && (MACH_CC9M2443JS || MACH_CCW9M2443JS)
+ select I2C_S3C2410
+ select SND_S3C2412_SOC_I2S
+ select SND_SOC_WM8753
+ help
+ Say Y if you want to add support for SoC audio interface on the Digi
+ ConnectCore 9M 2443 / ConnectCore Wi-9M 2443 in a Jump Start board
+ with the audio codec WM8753.
+
+config CC9M2443JS_WM8753_I2C_ADAPTER
+ int "I2C adapter connected with the WM8753"
+ depends on SND_S3C24XX_SOC_CC9M2443JS_WM8753
+ default 1
+ help
+ This is the adapter/bus number where the WM8753 is connected to.
+
config SND_S3C24XX_SOC_SMDK2443_WM9710
tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
depends on SND_S3C24XX_SOC && MACH_SMDK2443
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 0aa5fb0b9700..1419a82a9c8e 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -13,7 +13,9 @@ obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
+snd-soc-cc9m2443js-wm8753-objs := cc9m2443js_wm8753.o
obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
+obj-$(CONFIG_SND_S3C24XX_SOC_CC9M2443JS_WM8753) += snd-soc-cc9m2443js-wm8753.o
diff --git a/sound/soc/s3c24xx/cc9m2443js_wm8753.c b/sound/soc/s3c24xx/cc9m2443js_wm8753.c
new file mode 100644
index 000000000000..7004fa621cf7
--- /dev/null
+++ b/sound/soc/s3c24xx/cc9m2443js_wm8753.c
@@ -0,0 +1,298 @@
+/* -*- linux-c -*-
+ *
+ * cc9m2443js_wm8753.c -- SoC audio for the Digi CC9M2443JS
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ * Based on the driver of the Neo1973
+ *
+ * IMPORTANT: In difference to the Neo1973, we are using the S3C2443 as master
+ * and not as slave. That means, the configuration of the codec is quite different
+ * (Luis Galdos)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/math64.h>
+#include <linux/clk.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/scoop.h>
+#include <mach/regs-clock.h>
+#include <mach/regs-gpio.h>
+#include <mach/hardware.h>
+#include <mach/audio.h>
+#include <linux/io.h>
+#include <mach/spi-gpio.h>
+#include <sound/pcm_params.h>
+
+#include <asm/plat-s3c24xx/regs-iis.h>
+
+#include "../codecs/wm8753.h"
+#include "lm4857.h"
+#include "s3c24xx-pcm.h"
+#include "s3c2412-i2s.h"
+
+/* Additional info and error messages */
+#define printk_err(fmt, args...) printk(KERN_ERR "[ ERROR ] cc9m2443-snd: " fmt, ## args)
+#define printk_info(fmt, args...) printk(KERN_INFO "cc9m2443-snd: " fmt, ## args)
+
+#if 0
+#define CC9M2443JS_SND_DEBUG
+#endif
+
+#ifdef CC9M2443JS_SND_DEBUG
+# define printk_debug(fmt, args...) printk(KERN_DEBUG "cc9m2443-snd: %s() " fmt, __func__ , ## args)
+#else
+# define printk_debug(fmt, args...)
+#endif
+
+static int cc9m2443js_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ unsigned int pll_out = 0, bclk = 0;
+ int ret = 0;
+ unsigned long bitfs, is16bit;
+ struct s3c2412_rate_calc info;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ pll_out = 12288000;
+ break;
+ case 48000:
+ bclk = WM8753_BCLK_DIV_4;
+ pll_out = 12288000;
+ break;
+ case 96000:
+ bclk = WM8753_BCLK_DIV_2;
+ pll_out = 12288000;
+ break;
+ case 11025:
+ bclk = WM8753_BCLK_DIV_16;
+ pll_out = 11289600;
+ break;
+ case 22050:
+ bclk = WM8753_BCLK_DIV_8;
+ pll_out = 11289600;
+ break;
+ case 44100:
+ bclk = WM8753_BCLK_DIV_4;
+ pll_out = 11289600;
+ break;
+ case 88200:
+ bclk = WM8753_BCLK_DIV_2;
+ pll_out = 11289600;
+ break;
+ }
+
+ /* cpu_iis_mclk = iis_clkrate / params_rate(params); */
+ s3c2412_iis_calc_rate(&info, NULL, params_rate(params), s3c2412_get_iisclk());
+
+ printk_debug("Sample rate %i | Master Clk %lu Hz | FS %u | Clk divisor %u\n",
+ params_rate(params), clk_get_rate(s3c2412_get_iisclk()),
+ info.fs_div, info.clk_div);
+
+ /* REQUIRED: Set codec DAI configuration (normal bclk and frm ) and as slave */
+ ret = codec_dai->dai_ops.set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* REQUIRED: Set cpu DAI configuration (normal bclk and frm) and CPU as master */
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ /* REQUIRED: Set the codec system clock for DAC and ADC */
+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ printk_err("set_sysclk() failed (%i)\n", ret);
+ return ret;
+ }
+
+ /* REQUIRED: Set MCLK division for sample rate (256, 384, 512, 768) */
+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C2412_DIV_RCLK, info.fs_div);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Set the BCLK configuration for the CPU
+ * The required value depends on the configured root clock (RFS) according
+ * to the table 25-2 of the data sheet of the S3C2443
+ * @XXX: Define the corresponding macros for the Bit FS selection
+ */
+ is16bit = (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) ? 1 : 0;
+ switch (info.fs_div) {
+ case 256:
+ bitfs = (is16bit) ? 0x0 : 0x2;
+ break;
+ case 512:
+ bitfs = (is16bit) ? 0x0 : 0x2;
+ break;
+ case 384:
+ bitfs = (is16bit) ? 0x0 : 0x2;
+ break;
+ case 768:
+ bitfs = (is16bit) ? 0x0 : 0x2;
+ break;
+ default:
+ printk_err("Unsupported root clock FS %i divisor found\n", info.fs_div);
+ ret = -EINVAL;
+ goto exit_all;
+ break;
+ }
+
+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C2412_DIV_BCLK, bitfs);
+ if (ret) {
+ printk_err("Couldn't set the BLCK, %i\n", ret);
+ return ret;
+ }
+
+ /* Set the prescaler divisor for the master clock */
+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C2412_DIV_PRESCALER,
+ info.clk_div);
+ if (ret < 0) {
+ printk_err("Prescaler setting failed, %i\n", ret);
+ return ret;
+ }
+
+ exit_all:
+ return ret;
+}
+
+static int cc9m2443js_hifi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+ /* disable the PLL */
+ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
+}
+
+static int cc9m2443js_hifi_startup(struct snd_pcm_substream *substream)
+{
+ printk_debug("Going to startup the codec\n");
+
+ return 0;
+}
+
+/* We don't need this function, but we know that they are available */
+static void cc9m2443js_hifi_shutdown(struct snd_pcm_substream *substream)
+{
+ printk_debug("Going to shutdown the codec\n");
+}
+
+/*
+ * WM8753 HiFi DAI operations.
+ */
+static struct snd_soc_ops cc9m2443js_hifi_ops = {
+ .hw_params = cc9m2443js_hifi_hw_params,
+ .hw_free = cc9m2443js_hifi_hw_free,
+ .startup = cc9m2443js_hifi_startup,
+ .shutdown = cc9m2443js_hifi_shutdown,
+};
+
+/*
+ * Structure for the interface with the DAIs of the codec and CPU
+ * The two below elements will be probed by the same I2S-driver
+ */
+static struct snd_soc_dai_link cc9m2443js_dai[] = {
+ {
+ /*
+ * Hifi Playback
+ * Infos under: proc/asound/card0/pcm0p/info
+ */
+ .name = "WM8753",
+ .stream_name = "WM8753 HiFi",
+ .cpu_dai = &s3c2443_i2s_dai,
+ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
+ /* .init = cc9m2443js_wm8753_snd_init, */
+ .ops = &cc9m2443js_hifi_ops,
+ },{
+ /*
+ * Voice
+ * Infos under: proc/asound/card0/pcm1c/info
+ */
+ .name = "Inline",
+ .stream_name = "WM8753 Inline",
+ .cpu_dai = &s3c2443_i2s_dai,
+ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
+ .ops = &cc9m2443js_hifi_ops,
+ }
+};
+
+/* This is the machine-structure */
+static struct snd_soc_machine cc9m2443js_snd = {
+ .name = "cc9m2443js",
+ .dai_link = cc9m2443js_dai,
+ .num_links = ARRAY_SIZE(cc9m2443js_dai),
+};
+
+static struct wm8753_setup_data cc9m2443js_wm8753_setup = {
+ .i2c_address = 0x1a,
+ .i2c_bus = CONFIG_CC9M2443JS_WM8753_I2C_ADAPTER,
+};
+
+static struct snd_soc_device cc9m2443js_snd_devdata = {
+ .machine = &cc9m2443js_snd,
+ .platform = &s3c24xx_soc_platform,
+ .codec_dev = &soc_codec_dev_wm8753,
+ .codec_data = &cc9m2443js_wm8753_setup,
+};
+
+static struct platform_device *cc9m2443js_snd_device;
+
+static int __init cc9m2443js_wm8753_init(void)
+{
+ int ret;
+
+ cc9m2443js_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!cc9m2443js_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(cc9m2443js_snd_device, &cc9m2443js_snd_devdata);
+ cc9m2443js_snd_devdata.dev = &cc9m2443js_snd_device->dev;
+ ret = platform_device_add(cc9m2443js_snd_device);
+ if (ret)
+ platform_device_put(cc9m2443js_snd_device);
+
+ return ret;
+}
+
+static void __exit cc9m2443js_wm8753_exit(void)
+{
+ platform_device_unregister(cc9m2443js_snd_device);
+}
+
+module_init(cc9m2443js_wm8753_init);
+module_exit(cc9m2443js_wm8753_exit);
+
+/* Module information */
+MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org");
+MODULE_AUTHOR("Luis Galdos, luis.galdos[at]digi.com");
+MODULE_DESCRIPTION("ALSA SoC WM8753 Digi CC9M2443JS");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index ded7d995a922..a5aa9af0997f 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -83,6 +83,9 @@ struct s3c2412_i2s_info {
u32 suspend_iismod;
u32 suspend_iiscon;
u32 suspend_iispsr;
+
+ u32 cpu_is_s3c2443;
+ int counts;
};
static struct s3c2412_i2s_info s3c2412_i2s;
@@ -540,6 +543,13 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
case S3C2412_DIV_BCLK:
reg = readl(i2s->regs + S3C2412_IISMOD);
reg &= ~S3C2412_IISMOD_BCLK_MASK;
+
+ /*
+ * The bitfield for the S3C2443 has another offset
+ * (Luis Galdos)
+ */
+ div = (i2s->cpu_is_s3c2443) ? (div << 1) : (div);
+
writel(reg | div, i2s->regs + S3C2412_IISMOD);
DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
@@ -579,8 +589,9 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
case S3C2412_DIV_PRESCALER:
if (div >= 0) {
- writel((div << 8) | S3C2412_IISPSR_PSREN,
- i2s->regs + S3C2412_IISPSR);
+ /* The S3C2443 has different bit fields (Luis Galdos) */
+ div = (i2s->cpu_is_s3c2443) ? (div - 1) : (div << 8);
+ writel(div | S3C2412_IISPSR_PSREN, i2s->regs + S3C2412_IISPSR);
} else {
writel(0x0, i2s->regs + S3C2412_IISPSR);
}
@@ -604,26 +615,36 @@ EXPORT_SYMBOL_GPL(s3c2412_get_iisclk);
static int s3c2412_i2s_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
+ unsigned long regval;
+ int retval;
+
DBG("Entered %s\n", __func__);
+ /* Check if we have already probed the interface */
+ s3c2412_i2s.counts++;
+ if (s3c2412_i2s.counts > 1)
+ return 0;
+
s3c2412_i2s.dev = &pdev->dev;
s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
- if (s3c2412_i2s.regs == NULL)
- return -ENXIO;
+ if (s3c2412_i2s.regs == NULL) {
+ retval = -ENXIO;
+ goto exit_err;
+ }
s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis");
if (s3c2412_i2s.iis_pclk == NULL) {
DBG("failed to get iis_clock\n");
- iounmap(s3c2412_i2s.regs);
- return -ENODEV;
+ retval = -ENODEV;
+ goto exit_iounmap;
}
s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk");
if (s3c2412_i2s.iis_cclk == NULL) {
DBG("failed to get i2sclk clock\n");
- iounmap(s3c2412_i2s.regs);
- return -ENODEV;
+ retval = -ENODEV;
+ goto exit_put_pclk;
}
clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
@@ -640,10 +661,67 @@ static int s3c2412_i2s_probe(struct platform_device *pdev,
s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
+ /* Stop the DMA-channel if it's already running! */
+ regval = readl(s3c2412_i2s.regs + S3C2412_IISCON);
+ if (regval & S3C2412_IISCON_IIS_ACTIVE) {
+ printk(KERN_ERR "DMA channel seems to be already active.\n");
+ regval &= ~S3C2412_IISCON_IIS_ACTIVE;
+ writel(regval, s3c2412_i2s.regs + S3C2412_IISCON);
+ }
+
+ /*
+ * Set the mode to transmit and receive, otherwise the below commands
+ * for stopping the RX and TX will fail
+ */
+ regval = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
+ regval &= ~S3C2412_IISMOD_MODE_MASK;
+ regval |= (S3C2412_IISMOD_MODE_TXRX);
+ writel(regval, s3c2412_i2s.regs + S3C2412_IISMOD);
+
s3c2412_snd_txctrl(0);
s3c2412_snd_rxctrl(0);
+ printk("I2S %s successfully probed\n",
+ s3c2412_i2s.cpu_is_s3c2443 ? "S3C2443" : "S3C2412");
+
return 0;
+
+exit_put_pclk:
+ clk_put(s3c2412_i2s.iis_pclk);
+
+exit_iounmap:
+ iounmap(s3c2412_i2s.regs);
+
+exit_err:
+ s3c2412_i2s.counts -= 1;
+ return retval;
+}
+
+static int s3c2443_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+ s3c2412_i2s.cpu_is_s3c2443 = 1;
+ return s3c2412_i2s_probe(pdev, dai);
+}
+
+static void s3c2443_i2s_remove(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+ /*
+ * If there is still one device using the interface, skip the complete
+ * remove of the driver
+ */
+ s3c2412_i2s.counts -= 1;
+ if (s3c2412_i2s.counts)
+ return;
+
+ clk_disable(s3c2412_i2s.iis_cclk);
+ clk_disable(s3c2412_i2s.iis_pclk);
+
+ /* @FIXME: We can't put the clock at this place! (Luis Galdos) */
+ /* clk_put(s3c2412_i2s.iis_cclk); */
+ clk_put(s3c2412_i2s.iis_pclk);
+
+ iounmap(s3c2412_i2s.regs);
+ s3c2412_i2s.regs = NULL;
}
#ifdef CONFIG_PM
@@ -739,6 +817,41 @@ struct snd_soc_dai s3c2412_i2s_dai = {
};
EXPORT_SYMBOL_GPL(s3c2412_i2s_dai);
+/* This is the DAI for the S3C2443 */
+#define S3C2443_I2S_RATES (SNDRV_PCM_RATE_44100)
+
+struct snd_soc_dai s3c2443_i2s_dai = {
+ .name = "s3c2443-i2s",
+ .id = 0,
+ .type = SND_SOC_DAI_I2S,
+ .probe = s3c2443_i2s_probe,
+ .remove = s3c2443_i2s_remove,
+ .suspend = s3c2412_i2s_suspend,
+ .resume = s3c2412_i2s_resume,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = S3C2443_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = S3C2443_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = {
+ .trigger = s3c2412_i2s_trigger,
+ .hw_params = s3c2412_i2s_hw_params,
+ },
+ .dai_ops = {
+ .set_fmt = s3c2412_i2s_set_fmt,
+ .set_clkdiv = s3c2412_i2s_set_clkdiv,
+ .set_sysclk = s3c2412_i2s_set_sysclk,
+ },
+};
+EXPORT_SYMBOL_GPL(s3c2443_i2s_dai);
+
/* Module information */
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h
index aac08a25e541..f0428fc887d7 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.h
+++ b/sound/soc/s3c24xx/s3c2412-i2s.h
@@ -25,6 +25,7 @@
extern struct clk *s3c2412_get_iisclk(void);
extern struct snd_soc_dai s3c2412_i2s_dai;
+extern struct snd_soc_dai s3c2443_i2s_dai;
struct s3c2412_rate_calc {
unsigned int clk_div; /* for prescaler */
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index e13e614bada9..48b0b2a833bc 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -71,6 +71,8 @@ struct s3c24xx_runtime_data {
dma_addr_t dma_pos;
dma_addr_t dma_end;
struct s3c24xx_pcm_dma_params *params;
+
+ int resumed;
};
/* s3c24xx_pcm_enqueue
@@ -457,11 +459,77 @@ static int s3c24xx_pcm_new(struct snd_card *card,
return ret;
}
+/* Enables the support of the Power Management */
+#if defined(CONFIG_PM)
+static int s3c24xx_pcm_suspend(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime;
+ struct s3c24xx_runtime_data *prtd;
+ int retval;
+
+ retval = 0;
+ runtime = dai->runtime;
+ if (!runtime)
+ goto exit_suspend;
+
+ prtd = runtime->private_data;
+ if (!prtd)
+ goto exit_suspend;
+
+ prtd->resumed = 0;
+
+exit_suspend:
+ return retval;
+}
+
+/* We need to reenable the DMA-channel when coming out from the suspend */
+static int s3c24xx_pcm_resume(struct platform_device *pdev, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime;
+ struct s3c24xx_runtime_data *prtd;
+ int retval;
+
+ retval = 0;
+ runtime = dai->runtime;
+ if (!runtime)
+ goto exit_resume;
+
+ prtd = runtime->private_data;
+ if (!prtd)
+ goto exit_resume;
+
+ /* Check if a DMA-channel is available and was not already resumed */
+ if (prtd->params != NULL && !prtd->resumed) {
+
+ /*
+ * We must reenable the DMA-channels otherwise they will not work
+ * after the wakeup-reset
+ */
+ s3c2410_dma_free(prtd->params->channel, prtd->params->client);
+ retval = s3c2410_dma_request(prtd->params->channel,
+ prtd->params->client, NULL);
+ if (retval < 0)
+ printk(KERN_ERR "s3c24xx-pcm: DMA %u request failed (%i)\n",
+ prtd->params->channel, retval);
+ else
+ prtd->resumed = 1;
+ }
+
+exit_resume:
+ return retval;
+}
+#else
+#define s3c24xx_pcm_suspend NULL
+#define s3c24xx_pcm_resume NULL
+#endif /* defined(CONFIG_PM) */
+
struct snd_soc_platform s3c24xx_soc_platform = {
.name = "s3c24xx-audio",
.pcm_ops = &s3c24xx_pcm_ops,
.pcm_new = s3c24xx_pcm_new,
.pcm_free = s3c24xx_pcm_free_dma_buffers,
+ .suspend = s3c24xx_pcm_suspend,
+ .resume = s3c24xx_pcm_resume,
};
EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 16c7453f4946..6b83792e94f4 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -360,9 +360,11 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* start delayed pop wq here for playback streams */
- codec_dai->pop_wait = 1;
- schedule_delayed_work(&socdev->delayed_work,
- msecs_to_jiffies(pmdown_time));
+ if (codec_dai->prepared) {
+ codec_dai->pop_wait = 1;
+ schedule_delayed_work(&socdev->delayed_work,
+ msecs_to_jiffies(pmdown_time));
+ }
} else {
/* capture streams can be powered down now */
snd_soc_dapm_stream_event(codec,
@@ -374,6 +376,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
SND_SOC_BIAS_STANDBY);
}
+ codec_dai->prepared = 0;
mutex_unlock(&pcm_mutex);
return 0;
}
@@ -395,7 +398,13 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
int ret = 0;
mutex_lock(&pcm_mutex);
-
+ /*
+ * This is for informing the close-function that we really need to
+ * start the delayed work for stopping the codecs
+ * (Luis Galdos)
+ */
+ codec_dai->prepared = 1;
+
if (machine->ops && machine->ops->prepare) {
ret = machine->ops->prepare(substream);
if (ret < 0) {
diff --git a/sound/soc/stmp3xxx/Kconfig b/sound/soc/stmp3xxx/Kconfig
new file mode 100644
index 000000000000..5a3ee6067f90
--- /dev/null
+++ b/sound/soc/stmp3xxx/Kconfig
@@ -0,0 +1,31 @@
+config SND_STMP3XXX_SOC
+ tristate "SoC Audio for the SigmaTel STMP3XXX chips"
+ depends on ARCH_STMP3XXX && SND_SOC
+ select SND_PCM
+ help
+ Say Y or M if you want to add support for codecs embedded into
+ the STMP3XXX chips.
+
+config SND_STMP3XXX_SOC_DAI
+ tristate
+
+config SND_STMP3XXX_SOC_SPDIF_DAI
+ tristate
+
+config SND_STMP3XXX_SOC_STMP3780_DEVB
+ tristate "SoC Audio support for STMP3780 Development Board"
+ depends on SND_STMP3XXX_SOC && ARCH_STMP378X
+ select SND_STMP3XXX_SOC_DAI
+ select SND_SOC_STMP378X_CODEC
+ help
+ Say Y if you want to add support for SoC audio on stmp3780 development
+ board with the stmp378x codec.
+
+config SND_STMP3XXX_SOC_STMP3780_DEVB_SPDIF
+ tristate "SoC SPDIF support for STMP3780 Development Board"
+ depends on SND_STMP3XXX_SOC && ARCH_STMP378X
+ select SND_STMP3XXX_SOC_SPDIF_DAI
+ select SND_SOC_STMP3XXX_SPDIF
+ help
+ Say Y if you want to add support for SoC audio on stmp3780 development
+ board with the SPDIF transmitter.
diff --git a/sound/soc/stmp3xxx/Makefile b/sound/soc/stmp3xxx/Makefile
new file mode 100644
index 000000000000..082e7aaf365c
--- /dev/null
+++ b/sound/soc/stmp3xxx/Makefile
@@ -0,0 +1,15 @@
+# STMP3XXX platfrom support
+snd-soc-stmp3xxx-objs := stmp3xxx_pcm.o
+snd-soc-stmp3xxx-dai-objs := stmp3xxx_dai.o
+snd-soc-stmp3xxx-spdif-dai-objs := stmp3xxx_spdif_dai.o
+
+obj-$(CONFIG_SND_STMP3XXX_SOC) += snd-soc-stmp3xxx.o
+obj-$(CONFIG_SND_STMP3XXX_SOC_DAI) += snd-soc-stmp3xxx-dai.o
+obj-$(CONFIG_SND_STMP3XXX_SOC_SPDIF_DAI) += snd-soc-stmp3xxx-spdif-dai.o
+
+# Machine Support
+snd-soc-stmp3780-devb-objs := stmp3780_devb.o
+snd-soc-stmp3780-devb-spdif-objs := stmp3780_devb_spdif.o
+
+obj-$(CONFIG_SND_STMP3XXX_SOC_STMP3780_DEVB) += snd-soc-stmp3780-devb.o
+obj-$(CONFIG_SND_STMP3XXX_SOC_STMP3780_DEVB_SPDIF) += snd-soc-stmp3780-devb-spdif.o
diff --git a/sound/soc/stmp3xxx/stmp3780_devb.c b/sound/soc/stmp3xxx/stmp3780_devb.c
new file mode 100644
index 000000000000..acb6414cc52f
--- /dev/null
+++ b/sound/soc/stmp3xxx/stmp3780_devb.c
@@ -0,0 +1,97 @@
+/*
+ * ASoC driver for Freescale STMP3780 development board
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+#include <mach/regs-apbx.h>
+
+#include "../codecs/stmp378x_codec.h"
+#include "stmp3xxx_dai.h"
+#include "stmp3xxx_pcm.h"
+
+/* stmp3780 devb digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link stmp3780_devb_dai = {
+ .name = "STMP378X ADC/DAC",
+ .stream_name = "STMP378X ADC/DAC",
+ .cpu_dai = &stmp3xxx_adc_dai,
+ .codec_dai = &stmp378x_codec_dai,
+};
+
+/* stmp3780 devb audio machine driver */
+static struct snd_soc_machine snd_soc_machine_stmp3780_devb = {
+ .name = "STMP3780 Devb",
+ .dai_link = &stmp3780_devb_dai,
+ .num_links = 1,
+};
+
+/* stmp3780 devb audio subsystem */
+static struct snd_soc_device stmp3780_devb_snd_devdata = {
+ .machine = &snd_soc_machine_stmp3780_devb,
+ .platform = &stmp3xxx_soc_platform,
+ .codec_dev = &soc_codec_dev_stmp378x,
+};
+
+static struct platform_device *stmp3780_devb_snd_device;
+
+static int __init stmp3780_devb_init(void)
+{
+ int ret = 0;
+
+ stmp3780_devb_snd_device = platform_device_alloc("soc-audio", 0);
+ if (!stmp3780_devb_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(stmp3780_devb_snd_device,
+ &stmp3780_devb_snd_devdata);
+ stmp3780_devb_snd_devdata.dev = &stmp3780_devb_snd_device->dev;
+ stmp3780_devb_snd_device->dev.platform_data =
+ &stmp3780_devb_snd_devdata;
+
+ if (ret) {
+ platform_device_put(stmp3780_devb_snd_device);
+ return ret;
+ }
+
+ ret = platform_device_add(stmp3780_devb_snd_device);
+ if (ret)
+ platform_device_put(stmp3780_devb_snd_device);
+
+ return ret;
+}
+
+static void __exit stmp3780_devb_exit(void)
+{
+ platform_device_unregister(stmp3780_devb_snd_device);
+}
+
+module_init(stmp3780_devb_init);
+module_exit(stmp3780_devb_exit);
+
+MODULE_AUTHOR("Vladislav Buzov");
+MODULE_DESCRIPTION("STMP3780 development board ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/stmp3xxx/stmp3780_devb_spdif.c b/sound/soc/stmp3xxx/stmp3780_devb_spdif.c
new file mode 100644
index 000000000000..c8dbe2cf6958
--- /dev/null
+++ b/sound/soc/stmp3xxx/stmp3780_devb_spdif.c
@@ -0,0 +1,95 @@
+/*
+ * ASoC driver for STMP3780 development board
+ *
+ * Vladimir Barinov <vbarinov@embeddedalley.com>
+ *
+ * Copyright 2008 SigmaTel, Inc
+ * Copyright 2008 Embedded Alley Solutions, Inc
+ * Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+#include <mach/regs-apbx.h>
+
+#include <mach/stmp3xxx.h>
+
+#include "../codecs/stmp3xxx_spdif.h"
+#include "stmp3xxx_spdif_dai.h"
+#include "stmp3xxx_pcm.h"
+
+/* stmp3780 devb digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link stmp3780_devb_dai = {
+ .name = "STMP3XXX SPDIF",
+ .stream_name = "STMP3XXX SPDIF",
+ .cpu_dai = &stmp3xxx_spdif_dai,
+ .codec_dai = &stmp3xxx_spdif_codec_dai,
+};
+
+/* stmp3780 devb audio machine driver */
+static struct snd_soc_machine snd_soc_machine_stmp3780_devb = {
+ .name = "STMP3780 Devb",
+ .dai_link = &stmp3780_devb_dai,
+ .num_links = 1,
+};
+
+/* stmp3780 devb audio subsystem */
+static struct snd_soc_device stmp3780_devb_snd_devdata = {
+ .machine = &snd_soc_machine_stmp3780_devb,
+ .platform = &stmp3xxx_soc_platform,
+ .codec_dev = &soc_spdif_codec_dev_stmp3xxx,
+};
+
+static struct platform_device *stmp3780_devb_snd_device;
+
+static int __init stmp3780_devb_init(void)
+{
+ int ret = 0;
+
+ stmp3780_devb_snd_device = platform_device_alloc("soc-audio", 1);
+ if (!stmp3780_devb_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(stmp3780_devb_snd_device,
+ &stmp3780_devb_snd_devdata);
+ stmp3780_devb_snd_devdata.dev = &stmp3780_devb_snd_device->dev;
+ stmp3780_devb_snd_device->dev.platform_data =
+ &stmp3780_devb_snd_devdata;
+
+ ret = platform_device_add(stmp3780_devb_snd_device);
+ if (ret)
+ goto out;
+
+ ret = spdif_pinmux_request();
+out:
+ if (ret)
+ platform_device_put(stmp3780_devb_snd_device);
+ return ret;
+}
+
+static void __exit stmp3780_devb_exit(void)
+{
+ spdif_pinmux_release();
+ platform_device_unregister(stmp3780_devb_snd_device);
+}
+
+module_init(stmp3780_devb_init);
+module_exit(stmp3780_devb_exit);
+
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("STMP3780 development board ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/stmp3xxx/stmp3xxx_dai.c b/sound/soc/stmp3xxx/stmp3xxx_dai.c
new file mode 100644
index 000000000000..0fa7e1d89bf1
--- /dev/null
+++ b/sound/soc/stmp3xxx/stmp3xxx_dai.c
@@ -0,0 +1,224 @@
+/*
+ * ASoC Audio Layer for Freescale STMP37XX/STMP378X ADC/DAC
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+#include <mach/regs-audioin.h>
+#include <mach/regs-audioout.h>
+#include "stmp3xxx_pcm.h"
+
+#define STMP3XXX_ADC_RATES SNDRV_PCM_RATE_8000_192000
+#define STMP3XXX_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct stmp3xxx_pcm_dma_params stmp3xxx_audio_in = {
+ .name = "stmp3xxx adc",
+ .dma_bus = STMP3XXX_BUS_APBX,
+ .dma_ch = 0,
+ .irq = IRQ_ADC_DMA,
+};
+
+struct stmp3xxx_pcm_dma_params stmp3xxx_audio_out = {
+ .name = "stmp3xxx dac",
+ .dma_bus = STMP3XXX_BUS_APBX,
+ .dma_ch = 1,
+ .irq = IRQ_DAC_DMA,
+};
+
+static irqreturn_t stmp3xxx_err_irq(int irq, void *dev_id)
+{
+ struct snd_pcm_substream *substream = dev_id;
+ int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+ u32 ctrl_reg;
+ u32 overflow_mask;
+ u32 underflow_mask;
+
+ if (playback) {
+ ctrl_reg = HW_AUDIOOUT_CTRL_RD();
+ underflow_mask = BM_AUDIOOUT_CTRL_FIFO_UNDERFLOW_IRQ;
+ overflow_mask = BM_AUDIOOUT_CTRL_FIFO_OVERFLOW_IRQ;
+ } else {
+ ctrl_reg = HW_AUDIOIN_CTRL_RD();
+ underflow_mask = BM_AUDIOIN_CTRL_FIFO_UNDERFLOW_IRQ;
+ overflow_mask = BM_AUDIOIN_CTRL_FIFO_OVERFLOW_IRQ;
+ }
+
+ if (ctrl_reg & underflow_mask) {
+ printk(KERN_DEBUG "%s underflow detected\n",
+ playback ? "DAC" : "ADC");
+
+ if (playback)
+ HW_AUDIOOUT_CTRL_CLR(
+ BM_AUDIOOUT_CTRL_FIFO_UNDERFLOW_IRQ);
+ else
+ HW_AUDIOIN_CTRL_CLR(
+ BM_AUDIOIN_CTRL_FIFO_UNDERFLOW_IRQ);
+
+ } else if (ctrl_reg & overflow_mask) {
+ printk(KERN_DEBUG "%s overflow detected\n",
+ playback ? "DAC" : "ADC");
+
+ if (playback)
+ HW_AUDIOOUT_CTRL_CLR(
+ BM_AUDIOOUT_CTRL_FIFO_OVERFLOW_IRQ);
+ else
+ HW_AUDIOIN_CTRL_CLR(BM_AUDIOIN_CTRL_FIFO_OVERFLOW_IRQ);
+ } else
+ printk(KERN_WARNING "Unknown DAC error interrupt\n");
+
+ return IRQ_HANDLED;
+}
+
+static int stmp3xxx_adc_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (playback)
+ HW_AUDIOOUT_CTRL_SET(BM_AUDIOOUT_CTRL_RUN);
+ else
+ HW_AUDIOIN_CTRL_SET(BM_AUDIOIN_CTRL_RUN);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (playback)
+ HW_AUDIOOUT_CTRL_CLR(BM_AUDIOOUT_CTRL_RUN);
+ else
+ HW_AUDIOIN_CTRL_CLR(BM_AUDIOIN_CTRL_RUN);
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int stmp3xxx_adc_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+ int irq;
+ int ret;
+
+ if (playback) {
+ irq = IRQ_DAC_ERROR;
+ cpu_dai->dma_data = &stmp3xxx_audio_out;
+ } else {
+ irq = IRQ_ADC_ERROR;
+ cpu_dai->dma_data = &stmp3xxx_audio_in;
+ }
+
+ ret = request_irq(irq, stmp3xxx_err_irq, 0, "STMP3xxx DAC/ADC Error",
+ substream);
+ if (ret) {
+ printk(KERN_ERR "%s: Unable to request ADC/DAC error irq %d\n",
+ __func__, IRQ_DAC_ERROR);
+ return ret;
+ }
+
+ /* Enable error interrupt */
+ if (playback) {
+ HW_AUDIOOUT_CTRL_CLR(BM_AUDIOOUT_CTRL_FIFO_OVERFLOW_IRQ);
+ HW_AUDIOOUT_CTRL_CLR(BM_AUDIOOUT_CTRL_FIFO_UNDERFLOW_IRQ);
+ HW_AUDIOOUT_CTRL_SET(BM_AUDIOOUT_CTRL_FIFO_ERROR_IRQ_EN);
+ } else {
+ HW_AUDIOIN_CTRL_CLR(BM_AUDIOIN_CTRL_FIFO_OVERFLOW_IRQ);
+ HW_AUDIOIN_CTRL_CLR(BM_AUDIOIN_CTRL_FIFO_UNDERFLOW_IRQ);
+ HW_AUDIOIN_CTRL_SET(BM_AUDIOIN_CTRL_FIFO_ERROR_IRQ_EN);
+ }
+
+ return 0;
+}
+
+static void stmp3xxx_adc_shutdown(struct snd_pcm_substream *substream)
+{
+ int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+
+ /* Disable error interrupt */
+ if (playback) {
+ HW_AUDIOOUT_CTRL_CLR(BM_AUDIOOUT_CTRL_FIFO_ERROR_IRQ_EN);
+ free_irq(IRQ_DAC_ERROR, substream);
+ } else {
+ HW_AUDIOIN_CTRL_CLR(BM_AUDIOIN_CTRL_FIFO_ERROR_IRQ_EN);
+ free_irq(IRQ_ADC_ERROR, substream);
+ }
+}
+
+#ifdef CONFIG_PM
+static int stmp3xxx_adc_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+
+static int stmp3xxx_adc_resume(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+#else
+#define stmp3xxx_adc_suspend NULL
+#define stmp3xxx_adc_resume NULL
+#endif /* CONFIG_PM */
+
+struct snd_soc_dai stmp3xxx_adc_dai = {
+ .name = "stmp3xxx adc/dac",
+ .id = 0,
+ .type = SND_SOC_DAI_PCM,
+ .suspend = stmp3xxx_adc_suspend,
+ .resume = stmp3xxx_adc_resume,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STMP3XXX_ADC_RATES,
+ .formats = STMP3XXX_ADC_FORMATS,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STMP3XXX_ADC_RATES,
+ .formats = STMP3XXX_ADC_FORMATS,
+ },
+ .ops = {
+ .startup = stmp3xxx_adc_startup,
+ .shutdown = stmp3xxx_adc_shutdown,
+ .trigger = stmp3xxx_adc_trigger,
+ },
+};
+EXPORT_SYMBOL_GPL(stmp3xxx_adc_dai);
+
+MODULE_AUTHOR("Vladislav Buzov");
+MODULE_DESCRIPTION("stmp3xxx dac/adc DAI");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/stmp3xxx/stmp3xxx_dai.h b/sound/soc/stmp3xxx/stmp3xxx_dai.h
new file mode 100644
index 000000000000..409256a86d15
--- /dev/null
+++ b/sound/soc/stmp3xxx/stmp3xxx_dai.h
@@ -0,0 +1,21 @@
+/*
+ * ASoC Audio Layer for Freescale STMP37XX/STMP378X ADC/DAC
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 _STMP3XXX_DEV_H
+#define _STMP3XXX_DEV_H
+extern struct snd_soc_dai stmp3xxx_adc_dai;
+#endif
diff --git a/sound/soc/stmp3xxx/stmp3xxx_pcm.c b/sound/soc/stmp3xxx/stmp3xxx_pcm.c
new file mode 100644
index 000000000000..bbd6d753c409
--- /dev/null
+++ b/sound/soc/stmp3xxx/stmp3xxx_pcm.c
@@ -0,0 +1,446 @@
+/*
+ * ASoC PCM interface for Freescale STMP37XX/STMP378X ADC/DAC
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+#include <mach/hardware.h>
+
+#include <mach/regs-apbx.h>
+
+#include "stmp3xxx_pcm.h"
+
+static const struct snd_pcm_hardware stmp3xxx_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 255,
+ .buffer_bytes_max = 64 * 1024,
+ .fifo_size = 32,
+};
+
+/*
+ * Required to request DMA channels
+ */
+struct device *stmp3xxx_pcm_dev;
+
+struct stmp3xxx_runtime_data {
+ u32 dma_ch;
+ u32 dma_period;
+ u32 dma_totsize;
+
+ struct stmp3xxx_pcm_dma_params *params;
+ struct stmp3xxx_dma_descriptor *dma_desc_array;
+};
+
+static irqreturn_t stmp3xxx_pcm_dma_irq(int irq, void *dev_id)
+{
+ struct snd_pcm_substream *substream = dev_id;
+ struct stmp3xxx_runtime_data *prtd = substream->runtime->private_data;
+
+#ifdef CONFIG_ARCH_STMP37XX
+ u32 err_mask = 1 << (16 + prtd->params->dma_ch);
+#endif
+#ifdef CONFIG_ARCH_STMP378X
+ u32 err_mask = 1 << prtd->params->dma_ch;
+#endif
+ u32 irq_mask = 1 << prtd->params->dma_ch;
+
+#ifdef CONFIG_ARCH_STMP37XX
+ if (HW_APBX_CTRL1_RD() & err_mask) {
+#endif
+#ifdef CONFIG_ARCH_STMP378X
+ if (HW_APBX_CTRL2_RD() & err_mask) {
+#endif
+ printk(KERN_WARNING "%s: DMA audio channel %d (%s) error\n",
+ __func__, prtd->params->dma_ch, prtd->params->name);
+#ifdef CONFIG_ARCH_STMP37XX
+ HW_APBX_CTRL1_CLR(err_mask);
+#endif
+#ifdef CONFIG_ARCH_STMP378X
+ HW_APBX_CTRL2_CLR(err_mask);
+#endif
+ } else if (HW_APBX_CTRL1_RD() & irq_mask) {
+ stmp3xxx_dma_clear_interrupt(prtd->dma_ch);
+ snd_pcm_period_elapsed(substream);
+ } else
+ printk(KERN_WARNING "%s: Unknown interrupt\n", __func__);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Make a circular DMA descriptor list
+ */
+static int stmp3xxx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stmp3xxx_runtime_data *prtd = runtime->private_data;
+ dma_addr_t dma_buffer_phys;
+ int periods_num, playback, i;
+
+ playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+ periods_num = prtd->dma_totsize / prtd->dma_period;
+ dma_buffer_phys = runtime->dma_addr;
+
+ /* Reset DMA channel, enable interrupt */
+ stmp3xxx_dma_reset_channel(prtd->dma_ch);
+
+ /* Set up a DMA chain to sent DMA buffer */
+ for (i = 0; i < periods_num; i++) {
+ int next = (i + 1) % periods_num;
+ u32 cmd = 0;
+
+ /* Link with previous command */
+ prtd->dma_desc_array[i].command->next =
+ prtd->dma_desc_array[next].handle;
+
+ prtd->dma_desc_array[i].next_descr =
+ &prtd->dma_desc_array[next];
+
+ cmd = BF_APBX_CHn_CMD_XFER_COUNT(prtd->dma_period) |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BM_APBX_CHn_CMD_CHAIN;
+
+ /* Set DMA direction */
+ if (playback)
+ cmd |= BF_APBX_CHn_CMD_COMMAND(
+ BV_APBX_CHn_CMD_COMMAND__DMA_READ);
+ else
+ cmd |= BF_APBX_CHn_CMD_COMMAND(
+ BV_APBX_CHn_CMD_COMMAND__DMA_WRITE);
+
+ prtd->dma_desc_array[i].command->cmd = cmd;
+ prtd->dma_desc_array[i].command->buf_ptr = dma_buffer_phys;
+
+ /* Next data chunk */
+ dma_buffer_phys += prtd->dma_period;
+ }
+
+ return 0;
+}
+
+/*
+ * Stop circular DMA descriptor list
+ * We should not stop DMA in a middle of current transaction once we receive
+ * stop request from ALSA core. This function finds the next DMA descriptor
+ * and set it up to decrement DMA channel semaphore. So the current transaction
+ * is the last data transfer.
+ */
+static void stmp3xxx_pcm_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stmp3xxx_runtime_data *prtd = runtime->private_data;
+ dma_addr_t pos;
+ int desc;
+
+ /* Freez DMA channel for a moment */
+ stmp3xxx_dma_freeze(prtd->dma_ch);
+
+ /* Find current DMA descriptor */
+ pos = HW_APBX_CHn_BAR_RD(prtd->params->dma_ch);
+ desc = (pos - runtime->dma_addr) / prtd->dma_period;
+
+ /* Set up the next descriptor to decrement DMA channel sempahore */
+ prtd->dma_desc_array[desc].next_descr->command->cmd
+ = BM_APBX_CHn_CMD_SEMAPHORE;
+
+ /* Let the current DMA transaction finish */
+ stmp3xxx_dma_unfreeze(prtd->dma_ch);
+}
+
+static int stmp3xxx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct stmp3xxx_runtime_data *prtd = substream->runtime->private_data;
+ int ret = 0;
+
+ switch (cmd) {
+
+ case SNDRV_PCM_TRIGGER_START:
+ stmp3xxx_dma_go(prtd->dma_ch, prtd->dma_desc_array, 1);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ stmp3xxx_pcm_stop(substream);
+ break;
+
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ stmp3xxx_dma_unfreeze(prtd->dma_ch);
+ break;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ stmp3xxx_dma_freeze(prtd->dma_ch);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t
+stmp3xxx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stmp3xxx_runtime_data *prtd = runtime->private_data;
+ unsigned int offset;
+ dma_addr_t pos;
+
+ pos = HW_APBX_CHn_BAR_RD(prtd->params->dma_ch);
+ offset = bytes_to_frames(runtime, pos - runtime->dma_addr);
+
+ if (offset >= runtime->buffer_size)
+ offset = 0;
+
+ return offset;
+}
+
+static int stmp3xxx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct stmp3xxx_runtime_data *prtd = substream->runtime->private_data;
+
+ prtd->dma_period = params_period_bytes(hw_params);
+ prtd->dma_totsize = params_buffer_bytes(hw_params);
+
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int stmp3xxx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int stmp3xxx_pcm_dma_request(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct stmp3xxx_runtime_data *prtd = runtime->private_data;
+ struct stmp3xxx_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data;
+ int desc_num = stmp3xxx_pcm_hardware.periods_max;
+ int desc;
+ int ret;
+
+ if (!dma_data)
+ return -ENODEV;
+
+ prtd->params = dma_data;
+ prtd->dma_ch = STMP3xxx_DMA(dma_data->dma_ch, dma_data->dma_bus);
+
+ ret = stmp3xxx_dma_request(prtd->dma_ch, stmp3xxx_pcm_dev,
+ prtd->params->name);
+ if (ret) {
+ printk(KERN_ERR "%s: Failed to request DMA channel (%d:%d)\n",
+ __func__, dma_data->dma_bus, dma_data->dma_ch);
+ return ret;
+ }
+
+ /* Allocate memory for data and pio DMA descriptors */
+ prtd->dma_desc_array =
+ kzalloc(sizeof(struct stmp3xxx_dma_descriptor) * desc_num,
+ GFP_KERNEL);
+ if (prtd->dma_desc_array == NULL) {
+ printk(KERN_ERR "%s: Unable to allocate memory\n", __func__);
+ stmp3xxx_dma_release(prtd->dma_ch);
+ return -ENOMEM;
+ }
+
+ for (desc = 0; desc < desc_num; desc++) {
+ ret = stmp3xxx_dma_allocate_command(prtd->dma_ch,
+ &prtd->dma_desc_array[desc]);
+ if (ret) {
+ printk(KERN_ERR"%s Unable to allocate DMA command %d\n",
+ __func__, desc);
+ goto err;
+ }
+ }
+
+ ret = request_irq(prtd->params->irq, stmp3xxx_pcm_dma_irq, 0,
+ "STMP3xxx PCM DMA", substream);
+ if (ret) {
+ printk(KERN_ERR "%s: Unable to request DMA irq %d\n", __func__,
+ prtd->params->irq);
+ goto err;
+ }
+
+
+ /* Enable completion interrupt */
+ stmp3xxx_dma_clear_interrupt(prtd->dma_ch);
+ stmp3xxx_dma_enable_interrupt(prtd->dma_ch);
+
+ return 0;
+
+err:
+ while (--desc >= 0)
+ stmp3xxx_dma_free_command(prtd->dma_ch,
+ &prtd->dma_desc_array[desc]);
+ kfree(prtd->dma_desc_array);
+ stmp3xxx_dma_release(prtd->dma_ch);
+
+ return ret;
+}
+
+static int stmp3xxx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stmp3xxx_runtime_data *prtd;
+ int ret;
+
+ /* Ensure that buffer size is a multiple of the period size */
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ snd_soc_set_runtime_hwparams(substream, &stmp3xxx_pcm_hardware);
+
+ prtd = kzalloc(sizeof(struct stmp3xxx_runtime_data), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ runtime->private_data = prtd;
+
+ ret = stmp3xxx_pcm_dma_request(substream);
+ if (ret) {
+ printk(KERN_ERR "stmp3xxx_pcm: Failed to request channels\n");
+ kfree(prtd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stmp3xxx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct stmp3xxx_runtime_data *prtd = runtime->private_data;
+ int desc_num = stmp3xxx_pcm_hardware.periods_max;
+ int desc;
+
+ /* Free DMA irq */
+ free_irq(prtd->params->irq, substream);
+
+ /* Free DMA channel */
+ for (desc = 0; desc < desc_num; desc++)
+ stmp3xxx_dma_free_command(prtd->dma_ch,
+ &prtd->dma_desc_array[desc]);
+ kfree(prtd->dma_desc_array);
+ stmp3xxx_dma_release(prtd->dma_ch);
+
+ /* Free private runtime data */
+ kfree(prtd);
+
+ return 0;
+}
+
+static int stmp3xxx_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ return dma_mmap_coherent(NULL, vma, runtime->dma_area,
+ runtime->dma_addr, runtime->dma_bytes);
+}
+
+struct snd_pcm_ops stmp3xxx_pcm_ops = {
+ .open = stmp3xxx_pcm_open,
+ .close = stmp3xxx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = stmp3xxx_pcm_hw_params,
+ .hw_free = stmp3xxx_pcm_hw_free,
+ .prepare = stmp3xxx_pcm_prepare,
+ .trigger = stmp3xxx_pcm_trigger,
+ .pointer = stmp3xxx_pcm_pointer,
+ .mmap = stmp3xxx_pcm_mmap,
+};
+
+static u64 stmp3xxx_pcm_dma_mask = DMA_32BIT_MASK;
+
+static int stmp3xxx_pcm_new(struct snd_card *card,
+ struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+ size_t size = stmp3xxx_pcm_hardware.buffer_bytes_max;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &stmp3xxx_pcm_dma_mask;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_32BIT_MASK;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, NULL,
+ size, size);
+
+ return 0;
+}
+
+static void stmp3xxx_pcm_free(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+/*
+ * We need probe/remove callbacks to setup stmp3xxx_pcm_dev
+ */
+static int stmp3xxx_pcm_probe(struct platform_device *pdev)
+{
+ stmp3xxx_pcm_dev = &pdev->dev;
+ return 0;
+}
+
+static int stmp3xxx_pcm_remove(struct platform_device *pdev)
+{
+ stmp3xxx_pcm_dev = NULL;
+ return 0;
+}
+
+struct snd_soc_platform stmp3xxx_soc_platform = {
+ .name = "STMP3xxx Audio",
+ .pcm_ops = &stmp3xxx_pcm_ops,
+ .probe = stmp3xxx_pcm_probe,
+ .remove = stmp3xxx_pcm_remove,
+ .pcm_new = stmp3xxx_pcm_new,
+ .pcm_free = stmp3xxx_pcm_free,
+};
+EXPORT_SYMBOL_GPL(stmp3xxx_soc_platform);
+
+MODULE_AUTHOR("Vladislav Buzov");
+MODULE_DESCRIPTION("STMP3xxx DMA Module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/stmp3xxx/stmp3xxx_pcm.h b/sound/soc/stmp3xxx/stmp3xxx_pcm.h
new file mode 100644
index 000000000000..78ac9487353f
--- /dev/null
+++ b/sound/soc/stmp3xxx/stmp3xxx_pcm.h
@@ -0,0 +1,30 @@
+/*
+ * ASoC PCM interface for Freescale STMP37XX/STMP378X ADC/DAC
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 _STMP3XXX_PCM_H
+#define _STMP3XXX_PCM_H
+
+struct stmp3xxx_pcm_dma_params {
+ char *name;
+ int dma_bus; /* DMA bus */
+ int dma_ch; /* DMA channel number */
+ int irq; /* DMA interrupt number */
+};
+
+extern struct snd_soc_platform stmp3xxx_soc_platform;
+
+#endif
diff --git a/sound/soc/stmp3xxx/stmp3xxx_spdif_dai.c b/sound/soc/stmp3xxx/stmp3xxx_spdif_dai.c
new file mode 100644
index 000000000000..4529b3fcaf42
--- /dev/null
+++ b/sound/soc/stmp3xxx/stmp3xxx_spdif_dai.c
@@ -0,0 +1,184 @@
+/*
+ * ALSA SoC SPDIF Audio Layer for STMP3xxx processor familiy
+ *
+ * Vladimir Barinov <vbarinov@embeddedalley.com>
+ *
+ * Copyright 2008 SigmaTel, Inc
+ * Copyright 2008 Embedded Alley Solutions, Inc
+ * Copyright 2008-2009 Freescale Semiconductor, Inc.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+#include <mach/regs-spdif.h>
+#include "stmp3xxx_pcm.h"
+
+#define STMP3XXX_SPDIF_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+#define STMP3XXX_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct stmp3xxx_pcm_dma_params stmp3xxx_spdif = {
+ .name = "stmp3xxx spdif",
+ .dma_bus = STMP3XXX_BUS_APBX,
+ .dma_ch = 2,
+ .irq = IRQ_SPDIF_DMA,
+};
+
+static irqreturn_t stmp3xxx_err_irq(int irq, void *dev_id)
+{
+ struct snd_pcm_substream *substream = dev_id;
+ int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+ u32 ctrl_reg = 0;
+ u32 overflow_mask;
+ u32 underflow_mask;
+
+ if (playback) {
+ ctrl_reg = HW_SPDIF_CTRL_RD();
+ underflow_mask = BM_SPDIF_CTRL_FIFO_UNDERFLOW_IRQ;
+ overflow_mask = BM_SPDIF_CTRL_FIFO_OVERFLOW_IRQ;
+ }
+
+ if (ctrl_reg & underflow_mask) {
+ printk(KERN_DEBUG "underflow detected SPDIF\n");
+
+ if (playback)
+ HW_SPDIF_CTRL_CLR(BM_SPDIF_CTRL_FIFO_UNDERFLOW_IRQ);
+ } else if (ctrl_reg & overflow_mask) {
+ printk(KERN_DEBUG "overflow detected SPDIF\n");
+
+ if (playback)
+ HW_SPDIF_CTRL_CLR(BM_SPDIF_CTRL_FIFO_OVERFLOW_IRQ);
+ } else
+ printk(KERN_WARNING "Unknown SPDIF error interrupt\n");
+
+ return IRQ_HANDLED;
+}
+
+static int stmp3xxx_spdif_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (playback)
+ HW_SPDIF_CTRL_SET(BM_SPDIF_CTRL_RUN);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (playback)
+ HW_SPDIF_CTRL_CLR(BM_SPDIF_CTRL_RUN);
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int stmp3xxx_spdif_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+ int irq;
+ int ret;
+
+ if (playback) {
+ irq = IRQ_SPDIF_ERROR;
+ cpu_dai->dma_data = &stmp3xxx_spdif;
+ }
+
+ ret = request_irq(irq, stmp3xxx_err_irq, 0, "STMP3xxx SPDIF Error",
+ substream);
+ if (ret) {
+ printk(KERN_ERR "%s: Unable to request SPDIF error irq %d\n",
+ __func__, IRQ_SPDIF_ERROR);
+ return ret;
+ }
+
+ /* Enable error interrupt */
+ if (playback) {
+ HW_SPDIF_CTRL_CLR(BM_SPDIF_CTRL_FIFO_OVERFLOW_IRQ);
+ HW_SPDIF_CTRL_CLR(BM_SPDIF_CTRL_FIFO_UNDERFLOW_IRQ);
+ HW_SPDIF_CTRL_SET(BM_SPDIF_CTRL_FIFO_ERROR_IRQ_EN);
+ }
+
+ return 0;
+}
+
+static void stmp3xxx_spdif_shutdown(struct snd_pcm_substream *substream)
+{
+ int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0;
+
+ /* Disable error interrupt */
+ if (playback) {
+ HW_SPDIF_CTRL_CLR(BM_SPDIF_CTRL_FIFO_ERROR_IRQ_EN);
+ free_irq(IRQ_SPDIF_ERROR, substream);
+ }
+}
+
+#ifdef CONFIG_PM
+static int stmp3xxx_spdif_suspend(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+
+static int stmp3xxx_spdif_resume(struct platform_device *pdev,
+ struct snd_soc_dai *cpu_dai)
+{
+ return 0;
+}
+#else
+#define stmp3xxx_spdif_suspend NULL
+#define stmp3xxx_spdif_resume NULL
+#endif /* CONFIG_PM */
+
+struct snd_soc_dai stmp3xxx_spdif_dai = {
+ .name = "stmp3xxx spdif",
+ .id = 0,
+ .type = SND_SOC_DAI_PCM,
+ .suspend = stmp3xxx_spdif_suspend,
+ .resume = stmp3xxx_spdif_resume,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STMP3XXX_SPDIF_RATES,
+ .formats = STMP3XXX_SPDIF_FORMATS,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = STMP3XXX_SPDIF_RATES,
+ .formats = STMP3XXX_SPDIF_FORMATS,
+ },
+ .ops = {
+ .startup = stmp3xxx_spdif_startup,
+ .shutdown = stmp3xxx_spdif_shutdown,
+ .trigger = stmp3xxx_spdif_trigger,
+ },
+};
+EXPORT_SYMBOL_GPL(stmp3xxx_spdif_dai);
+
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("stmp3xxx SPDIF DAI");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/stmp3xxx/stmp3xxx_spdif_dai.h b/sound/soc/stmp3xxx/stmp3xxx_spdif_dai.h
new file mode 100644
index 000000000000..bec3d6fad7c9
--- /dev/null
+++ b/sound/soc/stmp3xxx/stmp3xxx_spdif_dai.h
@@ -0,0 +1,21 @@
+/*
+ * ASoC Audio Layer for Freescale STMP3XXX SPDIF transmitter
+ *
+ * Author: Vladimir Barinov <vbarinov@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * 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 _STMP3XXX_SPDIF_H
+#define _STMP3XXX_SPDIF_H
+extern struct snd_soc_dai stmp3xxx_spdif_dai;
+#endif